xref: /titanic_41/usr/src/uts/common/io/fdc.c (revision 5a59a8b3d86e67dbe75588879c46e3629f40efec)
1  /*
2   * CDDL HEADER START
3   *
4   * The contents of this file are subject to the terms of the
5   * Common Development and Distribution License, Version 1.0 only
6   * (the "License").  You may not use this file except in compliance
7   * with the License.
8   *
9   * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10   * or http://www.opensolaris.org/os/licensing.
11   * See the License for the specific language governing permissions
12   * and limitations under the License.
13   *
14   * When distributing Covered Code, include this CDDL HEADER in each
15   * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16   * If applicable, add the following below this CDDL HEADER, with the
17   * fields enclosed by brackets "[]" replaced with your own identifying
18   * information: Portions Copyright [yyyy] [name of copyright owner]
19   *
20   * CDDL HEADER END
21   */
22  /*
23   * Copyright 2005 Sun Microsystems, Inc.  All rights reserved.
24   * Use is subject to license terms.
25   */
26  
27  #pragma ident	"%Z%%M%	%I%	%E% SMI"
28  
29  /*
30   * Floppy Disk Controller Driver
31   *
32   *   for the standard PC architecture using the Intel 8272A fdc.
33   *   Note that motor control and drive select use a latch external
34   *   to the fdc.
35   *
36   *   This driver is EISA capable, and uses DMA buffer chaining if available.
37   *   If this driver is attached to the ISA bus nexus (or if the EISA bus driver
38   *   does not support DMA buffer chaining), then the bus driver must ensure
39   *   that dma mapping (breakup) and dma engine requests are properly degraded.
40   */
41  
42  /*
43   * hack for bugid 1160621:
44   * workaround compiler optimization bug by turning on DEBUG
45   */
46  #ifndef DEBUG
47  #define	DEBUG	1
48  #endif
49  
50  #include <sys/param.h>
51  #include <sys/buf.h>
52  #include <sys/ioctl.h>
53  #include <sys/uio.h>
54  #include <sys/open.h>
55  #include <sys/conf.h>
56  #include <sys/file.h>
57  #include <sys/cmn_err.h>
58  #include <sys/debug.h>
59  #include <sys/kmem.h>
60  #include <sys/stat.h>
61  
62  #include <sys/autoconf.h>
63  #include <sys/dkio.h>
64  #include <sys/vtoc.h>
65  #include <sys/kstat.h>
66  
67  #include <sys/fdio.h>
68  #include <sys/fdc.h>
69  #include <sys/i8272A.h>
70  #include <sys/fd_debug.h>
71  #include <sys/promif.h>
72  #include <sys/ddi.h>
73  #include <sys/sunddi.h>
74  
75  /*
76   * bss (uninitialized data)
77   */
78  static void *fdc_state_head;		/* opaque handle top of state structs */
79  static ddi_dma_attr_t fdc_dma_attr;
80  static ddi_device_acc_attr_t fdc_accattr = {DDI_DEVICE_ATTR_V0,
81  	DDI_STRUCTURE_LE_ACC, DDI_STRICTORDER_ACC};
82  
83  /*
84   * Local static data
85   */
86  #define	OURUN_TRIES	12
87  static uchar_t rwretry = 4;
88  static uchar_t skretry = 3;
89  static uchar_t configurecmd[4] = {FO_CNFG, 0, 0x0F, 0};
90  static uchar_t recalcmd[2] = {FO_RECAL, 0};
91  static uchar_t senseintcmd = FO_SINT;
92  
93  /*
94   * error handling
95   *
96   * for debugging, set rwretry and skretry = 1
97   *		set fcerrlevel to 1
98   *		set fcerrmask  to 224  or 644
99   *
100   * after debug, set rwretry to 4, skretry to 3, and fcerrlevel to 5
101   * set fcerrmask to FDEM_ALL
102   * or remove the define DEBUG
103   */
104  static uint_t fcerrmask = FDEM_ALL;
105  static int fcerrlevel = 6;
106  
107  #define	KIOIP	KSTAT_INTR_PTR(fcp->c_intrstat)
108  
109  
110  static xlate_tbl_t drate_mfm[] = {
111  	{  250, 2},
112  	{  300, 1},
113  	{  417, 0},
114  	{  500, 0},
115  	{ 1000, 3},
116  	{    0, 0}
117  };
118  
119  static xlate_tbl_t sector_size[] = {
120  	{  256, 1},
121  	{  512, 2},
122  	{ 1024, 3},
123  	{    0, 2}
124  };
125  
126  static xlate_tbl_t motor_onbits[] = {
127  	{  0, 0x10},
128  	{  1, 0x20},
129  	{  2, 0x40},
130  	{  3, 0x80},
131  	{  0, 0x80}
132  };
133  
134  static xlate_tbl_t step_rate[] = {
135  	{  10, 0xF0},		/* for 500K data rate */
136  	{  20, 0xE0},
137  	{  30, 0xD0},
138  	{  40, 0xC0},
139  	{  50, 0xB0},
140  	{  60, 0xA0},
141  	{  70, 0x90},
142  	{  80, 0x80},
143  	{  90, 0x70},
144  	{ 100, 0x60},
145  	{ 110, 0x50},
146  	{ 120, 0x40},
147  	{ 130, 0x30},
148  	{ 140, 0x20},
149  	{ 150, 0x10},
150  	{ 160, 0x00},
151  	{   0, 0x00}
152  };
153  
154  #ifdef notdef
155  static xlate_tbl_t head_unld[] = {
156  	{  16, 0x1},		/* for 500K data rate */
157  	{  32, 0x2},
158  	{  48, 0x3},
159  	{  64, 0x4},
160  	{  80, 0x5},
161  	{  96, 0x6},
162  	{ 112, 0x7},
163  	{ 128, 0x8},
164  	{ 144, 0x9},
165  	{ 160, 0xA},
166  	{ 176, 0xB},
167  	{ 192, 0xC},
168  	{ 208, 0xD},
169  	{ 224, 0xE},
170  	{ 240, 0xF},
171  	{ 256, 0x0},
172  	{   0, 0x0}
173  };
174  #endif
175  
176  static struct fdcmdinfo {
177  	char *cmdname;		/* command name */
178  	uchar_t ncmdbytes;	/* number of bytes of command */
179  	uchar_t nrsltbytes;	/* number of bytes in result */
180  	uchar_t cmdtype;		/* characteristics */
181  } fdcmds[] = {
182  	"", 0, 0, 0,			/* - */
183  	"", 0, 0, 0,			/* - */
184  	"read_track", 9, 7, 1,		/* 2 */
185  	"specify", 3, 0, 3,		/* 3 */
186  	"sense_drv_status", 2, 1, 3,	/* 4 */
187  	"write", 9, 7, 1,		/* 5 */
188  	"read", 9, 7, 1,		/* 6 */
189  	"recalibrate", 2, 0, 2,		/* 7 */
190  	"sense_int_status", 1, 2, 3,	/* 8 */
191  	"write_del", 9, 7, 1,		/* 9 */
192  	"read_id", 2, 7, 2,		/* A */
193  	"", 0, 0, 0,			/* - */
194  	"read_del", 9, 7, 1,		/* C */
195  	"format_track", 10, 7, 1,	/* D */
196  	"dump_reg", 1, 10, 4,		/* E */
197  	"seek", 3, 0, 2,		/* F */
198  	"version", 1, 1, 3,		/* 10 */
199  	"", 0, 0, 0,			/* - */
200  	"perp_mode", 2, 0, 3,		/* 12 */
201  	"configure", 4, 0, 4,		/* 13 */
202  	/* relative seek */
203  };
204  
205  
206  static int
207  fdc_bus_ctl(dev_info_t *, dev_info_t *, ddi_ctl_enum_t, void *, void *);
208  static int get_ioaddr(dev_info_t *dip, int *ioaddr);
209  static int get_unit(dev_info_t *dip, int *cntrl_num);
210  
211  struct bus_ops fdc_bus_ops = {
212  	BUSO_REV,
213  	nullbusmap,
214  	0,	/* ddi_intrspec_t (*bus_get_intrspec)(); */
215  	0,	/* int 	(*bus_add_intrspec)(); */
216  	0,	/* void (*bus_remove_intrspec)(); */
217  	i_ddi_map_fault,
218  	ddi_dma_map,
219  	ddi_dma_allochdl,
220  	ddi_dma_freehdl,
221  	ddi_dma_bindhdl,
222  	ddi_dma_unbindhdl,
223  	ddi_dma_flush,
224  	ddi_dma_win,
225  	ddi_dma_mctl,
226  	fdc_bus_ctl,
227  	ddi_bus_prop_op,
228  };
229  
230  static int fdc_getinfo(dev_info_t *, ddi_info_cmd_t, void *, void **);
231  static int fdc_probe(dev_info_t *);
232  static int fdc_attach(dev_info_t *, ddi_attach_cmd_t);
233  static int fdc_detach(dev_info_t *, ddi_detach_cmd_t);
234  static int fdc_enhance_probe(struct fdcntlr *fcp);
235  
236  struct dev_ops	fdc_ops = {
237  	DEVO_REV,		/* devo_rev, */
238  	0,			/* refcnt  */
239  	fdc_getinfo,		/* getinfo */
240  	nulldev,		/* identify */
241  	fdc_probe,		/* probe */
242  	fdc_attach,		/* attach */
243  	fdc_detach,		/* detach */
244  	nodev,			/* reset */
245  	(struct cb_ops *)0,	/* driver operations */
246  	&fdc_bus_ops		/* bus operations */
247  };
248  
249  /*
250   * This is the loadable module wrapper.
251   */
252  #include <sys/modctl.h>
253  
254  extern struct mod_ops mod_driverops;
255  
256  static struct modldrv modldrv = {
257  	&mod_driverops,		/* Type of module. This one is a driver */
258  	"Floppy Controller %I%",	/* Name of the module. */
259  	&fdc_ops,		/* Driver ops vector */
260  };
261  
262  static struct modlinkage modlinkage = {
263  	MODREV_1, (void *)&modldrv, NULL
264  };
265  
266  int
267  _init(void)
268  {
269  	int retval;
270  
271  	if ((retval = ddi_soft_state_init(&fdc_state_head,
272  	    sizeof (struct fdcntlr) + NFDUN * sizeof (struct fcu_obj), 0)) != 0)
273  		return (retval);
274  
275  	if ((retval = mod_install(&modlinkage)) != 0)
276  		ddi_soft_state_fini(&fdc_state_head);
277  	return (retval);
278  }
279  
280  int
281  _fini(void)
282  {
283  	int retval;
284  
285  	if ((retval = mod_remove(&modlinkage)) != 0)
286  		return (retval);
287  	ddi_soft_state_fini(&fdc_state_head);
288  	return (retval);
289  }
290  
291  int
292  _info(struct modinfo *modinfop)
293  {
294  	return (mod_info(&modlinkage, modinfop));
295  }
296  
297  
298  int fdc_start(struct fcu_obj *);
299  int fdc_abort(struct fcu_obj *);
300  int fdc_getcap(struct fcu_obj *, char *, int);
301  int fdc_setcap(struct fcu_obj *, char *, int, int);
302  int fdc_dkinfo(struct fcu_obj *, struct dk_cinfo *);
303  int fdc_select(struct fcu_obj *, int, int);
304  int fdgetchng(struct fcu_obj *, int);
305  int fdresetchng(struct fcu_obj *, int);
306  int fdrecalseek(struct fcu_obj *, int, int, int);
307  int fdrw(struct fcu_obj *, int, int, int, int, int, caddr_t, uint_t);
308  int fdtrkformat(struct fcu_obj *, int, int, int, int);
309  int fdrawioctl(struct fcu_obj *, int, caddr_t);
310  
311  static struct fcobjops fdc_iops = {
312  		fdc_start,	/* controller start */
313  		fdc_abort,	/* controller abort */
314  		fdc_getcap,	/* capability retrieval */
315  		fdc_setcap,	/* capability establishment */
316  		fdc_dkinfo,	/* get disk controller info */
317  
318  		fdc_select,	/* select / deselect unit */
319  		fdgetchng,	/* get media change */
320  		fdresetchng,	/* reset media change */
321  		fdrecalseek,	/* recal / seek */
322  		NULL,		/* read /write request (UNUSED) */
323  		fdrw,		/* read /write sector */
324  		fdtrkformat,	/* format track */
325  		fdrawioctl	/* raw ioctl */
326  };
327  
328  
329  /*
330   * Function prototypes
331   */
332  void encode(xlate_tbl_t *tablep, int val, uchar_t *rcode);
333  int decode(xlate_tbl_t *, int, int *);
334  static int fdc_propinit1(struct fdcntlr *, int);
335  static void fdc_propinit2(struct fdcntlr *, int);
336  void fdcquiesce(struct fdcntlr *);
337  int fdcsense_chng(struct fdcntlr *, int);
338  int fdcsense_drv(struct fdcntlr *, int);
339  int fdcsense_int(struct fdcntlr *, int *, int *);
340  int fdcspecify(struct fdcntlr *, int, int, int);
341  int fdcspdchange(struct fdcntlr *, struct fcu_obj *, int);
342  static int fdc_exec(struct fdcntlr *, int, int);
343  int fdcheckdisk(struct fdcntlr *, int);
344  static uint_t fdc_intr(caddr_t arg);
345  static void fdwatch(void *arg);
346  static void fdmotort(void *arg);
347  static int fdrecover(struct fdcntlr *);
348  static int fdc_motorsm(struct fcu_obj *, int, int);
349  static int fdc_statemach(struct fdcntlr *);
350  int fdc_docmd(struct fdcntlr *, uchar_t *, uchar_t);
351  int fdc_result(struct fdcntlr *, uchar_t *, uchar_t);
352  
353  
354  /* ARGSUSED */
355  static int
356  fdc_bus_ctl(dev_info_t *dip, dev_info_t *rdip, ddi_ctl_enum_t ctlop,
357      void *arg, void *result)
358  {
359  	struct 	fdcntlr *fcp;
360  	struct	fcu_obj *fjp;
361  
362  	FCERRPRINT(FDEP_L0, FDEM_ATTA,
363  	    (CE_CONT, "fdc_bus_ctl: cmd= %x\n", ctlop));
364  
365  	if ((fcp = ddi_get_driver_private(dip)) == NULL)
366  		return (DDI_FAILURE);
367  
368  	switch (ctlop) {
369  
370  	case DDI_CTLOPS_REPORTDEV:
371  		cmn_err(CE_CONT, "?%s%d at %s%d\n",
372  		    ddi_get_name(rdip), ddi_get_instance(rdip),
373  		    ddi_get_name(dip), ddi_get_instance(dip));
374  		FCERRPRINT(FDEP_L3, FDEM_ATTA,
375  		    (CE_WARN, "fdc_bus_ctl: report %s%d at %s%d",
376  		    ddi_get_name(rdip), ddi_get_instance(rdip),
377  		    ddi_get_name(dip), ddi_get_instance(dip)));
378  		return (DDI_SUCCESS);
379  
380  	case DDI_CTLOPS_INITCHILD:
381  	{
382  		dev_info_t *udip = (dev_info_t *)arg;
383  		int cntlr;
384  		int len;
385  		int unit;
386  		char name[MAXNAMELEN];
387  
388  		FCERRPRINT(FDEP_L3, FDEM_ATTA,
389  		    (CE_WARN, "fdc_bus_ctl: init child 0x%p", (void*)udip));
390  		cntlr = fcp->c_number;
391  
392  		len = sizeof (unit);
393  		if (ddi_prop_op(DDI_DEV_T_ANY, udip, PROP_LEN_AND_VAL_BUF,
394  		    DDI_PROP_DONTPASS, "unit", (caddr_t)&unit, &len)
395  		    != DDI_PROP_SUCCESS ||
396  		    cntlr != FDCTLR(unit) ||
397  		    (fcp->c_unit[FDUNIT(unit)])->fj_dip)
398  			return (DDI_NOT_WELL_FORMED);
399  
400  		(void) sprintf(name, "%d,%d", cntlr, FDUNIT(unit));
401  		ddi_set_name_addr(udip, name);
402  
403  		fjp = fcp->c_unit[FDUNIT(unit)];
404  		fjp->fj_unit = unit;
405  		fjp->fj_dip = udip;
406  		fjp->fj_ops = &fdc_iops;
407  		fjp->fj_fdc = fcp;
408  		fjp->fj_iblock = &fcp->c_iblock;
409  
410  		ddi_set_driver_private(udip, fjp);
411  
412  		return (DDI_SUCCESS);
413  	}
414  	case DDI_CTLOPS_UNINITCHILD:
415  	{
416  		dev_info_t *udip = (dev_info_t *)arg;
417  
418  		FCERRPRINT(FDEP_L3, FDEM_ATTA,
419  		    (CE_WARN, "fdc_bus_ctl: uninit child 0x%p", (void *)udip));
420  		fjp = ddi_get_driver_private(udip);
421  		ddi_set_driver_private(udip, NULL);
422  		fjp->fj_dip = NULL;
423  		ddi_set_name_addr(udip, NULL);
424  		return (DDI_SUCCESS);
425  	}
426  	default:
427  		return (DDI_FAILURE);
428  	}
429  }
430  
431  /* ARGSUSED */
432  static int
433  fdc_getinfo(dev_info_t *dip, ddi_info_cmd_t cmd, void *arg, void **result)
434  {
435  	struct fdcntlr *fcp;
436  	int rval;
437  
438  	switch (cmd) {
439  	case DDI_INFO_DEVT2DEVINFO:
440  		if (fcp = ddi_get_soft_state(fdc_state_head, (dev_t)arg)) {
441  			*result = fcp->c_dip;
442  			rval = DDI_SUCCESS;
443  			break;
444  		} else {
445  			rval = DDI_FAILURE;
446  			break;
447  		}
448  	case DDI_INFO_DEVT2INSTANCE:
449  		*result = (void *)(uintptr_t)getminor((dev_t)arg);
450  		rval = DDI_SUCCESS;
451  		break;
452  	default:
453  		rval = DDI_FAILURE;
454  	}
455  	return (rval);
456  }
457  
458  static int
459  fdc_probe(dev_info_t *dip)
460  {
461  	int	debug[2];
462  	int ioaddr;
463  	int	len;
464  	uchar_t	stat;
465  
466  	len = sizeof (debug);
467  	if (ddi_prop_op(DDI_DEV_T_ANY, dip, PROP_LEN_AND_VAL_BUF,
468  	    DDI_PROP_DONTPASS, "debug", (caddr_t)debug, &len) ==
469  	    DDI_PROP_SUCCESS) {
470  		fcerrlevel = debug[0];
471  		fcerrmask = (uint_t)debug[1];
472  	}
473  
474  	FCERRPRINT(FDEP_L3, FDEM_ATTA, (CE_WARN, "fdc_probe: dip %p",
475  		(void*)dip));
476  
477  	if (get_ioaddr(dip, &ioaddr) != DDI_SUCCESS)
478  		return (DDI_PROBE_FAILURE);
479  
480  	stat = inb(ioaddr + FCR_MSR);
481  	if ((stat & (MS_RQM | MS_DIO | MS_CB)) != MS_RQM &&
482  	    (stat & ~MS_DIO) != MS_CB)
483  		return (DDI_PROBE_FAILURE);
484  
485  	return (DDI_PROBE_SUCCESS);
486  }
487  
488  /* ARGSUSED */
489  static int
490  fdc_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
491  {
492  	struct fdcntlr *fcp;
493  	struct fcu_obj *fjp;
494  	int cntlr_num, ctlr, unit;
495  	int intr_set = 0;
496  	int len;
497  	char name[MAXNAMELEN];
498  
499  	FCERRPRINT(FDEP_L3, FDEM_ATTA, (CE_WARN, "fdc_attach: dip %p",
500  		(void*)dip));
501  
502  	switch (cmd) {
503  	case DDI_ATTACH:
504  		if (ddi_getprop
505  		    (DDI_DEV_T_ANY, dip, 0, "ignore-hardware-nodes", 0)) {
506  			len = sizeof (cntlr_num);
507  			if (ddi_prop_op(DDI_DEV_T_ANY, dip,
508  			    PROP_LEN_AND_VAL_BUF, DDI_PROP_DONTPASS, "unit",
509  			    (caddr_t)&cntlr_num, &len) != DDI_PROP_SUCCESS) {
510  				FCERRPRINT(FDEP_L3, FDEM_ATTA, (CE_WARN,
511  				    "fdc_attach failed: dip %p", (void*)dip));
512  				return (DDI_FAILURE);
513  			}
514  		} else {
515  			if (get_unit(dip, &cntlr_num) != DDI_SUCCESS)
516  				return (DDI_FAILURE);
517  		}
518  
519  		ctlr = ddi_get_instance(dip);
520  		if (ddi_soft_state_zalloc(fdc_state_head, ctlr) != 0)
521  			return (DDI_FAILURE);
522  		fcp = ddi_get_soft_state(fdc_state_head, ctlr);
523  
524  		for (unit = 0, fjp = (struct fcu_obj *)(fcp+1);
525  		    unit < NFDUN; unit++) {
526  			fcp->c_unit[unit] = fjp++;
527  		}
528  		fcp->c_dip = dip;
529  
530  		if (fdc_propinit1(fcp, cntlr_num) != DDI_SUCCESS)
531  			goto no_attach;
532  
533  		/* get iblock cookie to initialize mutex used in the ISR */
534  		if (ddi_get_iblock_cookie(dip, (uint_t)0, &fcp->c_iblock) !=
535  		    DDI_SUCCESS) {
536  			cmn_err(CE_WARN,
537  			    "fdc_attach: cannot get iblock cookie");
538  			goto no_attach;
539  		}
540  		mutex_init(&fcp->c_lock, NULL, MUTEX_DRIVER, fcp->c_iblock);
541  		intr_set = 1;
542  
543  		/* setup interrupt handler */
544  		if (ddi_add_intr(dip, (uint_t)0, NULL,
545  		    (ddi_idevice_cookie_t *)0, fdc_intr, (caddr_t)fcp) !=
546  		    DDI_SUCCESS) {
547  			cmn_err(CE_WARN, "fdc: cannot add intr\n");
548  			goto no_attach;
549  		}
550  		intr_set++;
551  
552  		/*
553  		 * acquire the DMA channel
554  		 * this assumes that the chnl is not shared; else allocate
555  		 * and free the chnl with each fdc request
556  		 */
557  		if (ddi_dmae_alloc(dip, fcp->c_dmachan, DDI_DMA_DONTWAIT, NULL)
558  		    != DDI_SUCCESS) {
559  			cmn_err(CE_WARN, "fdc: cannot acquire dma%d\n",
560  			    fcp->c_dmachan);
561  			goto no_attach;
562  		}
563  		(void) ddi_dmae_getattr(dip, &fdc_dma_attr);
564  		fdc_dma_attr.dma_attr_align = MMU_PAGESIZE;
565  
566  		mutex_init(&fcp->c_dorlock, NULL, MUTEX_DRIVER, fcp->c_iblock);
567  		cv_init(&fcp->c_iocv, NULL, CV_DRIVER, fcp->c_iblock);
568  		sema_init(&fcp->c_selsem, 1, NULL, SEMA_DRIVER, NULL);
569  
570  		(void) sprintf(name, "fdc%d", ctlr);
571  		fcp->c_intrstat = kstat_create("fdc", ctlr, name,
572  		    "controller", KSTAT_TYPE_INTR, 1, KSTAT_FLAG_PERSISTENT);
573  		if (fcp->c_intrstat) {
574  			kstat_install(fcp->c_intrstat);
575  		}
576  
577  		ddi_set_driver_private(dip, fcp);
578  
579  		/*
580  		 * reset the controller
581  		 */
582  		sema_p(&fcp->c_selsem);
583  		mutex_enter(&fcp->c_lock);
584  		fcp->c_csb.csb_xstate = FXS_RESET;
585  		fcp->c_flags |= FCFLG_WAITING;
586  		fdcquiesce(fcp);
587  
588  		/* first test for mode == Model 30 */
589  		fcp->c_mode = (inb(fcp->c_regbase + FCR_SRB) & 0x1c) ?
590  		    FDCMODE_AT : FDCMODE_30;
591  
592  		while (fcp->c_flags & FCFLG_WAITING) {
593  			cv_wait(&fcp->c_iocv, &fcp->c_lock);
594  		}
595  		mutex_exit(&fcp->c_lock);
596  		sema_v(&fcp->c_selsem);
597  
598  		fdc_propinit2(fcp, cntlr_num);
599  
600  		ddi_report_dev(dip);
601  		return (DDI_SUCCESS);
602  
603  	default:
604  		return (DDI_FAILURE);
605  	}
606  
607  no_attach:
608  	if (intr_set) {
609  		if (intr_set > 1)
610  			ddi_remove_intr(dip, 0, fcp->c_iblock);
611  		mutex_destroy(&fcp->c_lock);
612  	}
613  	ddi_soft_state_free(fdc_state_head, cntlr_num);
614  	return (DDI_FAILURE);
615  }
616  
617  static int
618  fdc_propinit1(struct fdcntlr *fcp, int cntlr)
619  {
620  	dev_info_t *dip;
621  	int len;
622  	int value;
623  
624  	dip = fcp->c_dip;
625  	len = sizeof (value);
626  
627  	if (get_ioaddr(dip, &value) != DDI_SUCCESS)
628  		return (DDI_FAILURE);
629  
630  	fcp->c_regbase = (ushort_t)value;
631  
632  	if (ddi_prop_op(DDI_DEV_T_ANY, dip, PROP_LEN_AND_VAL_BUF,
633  	    DDI_PROP_DONTPASS, "dma-channels", (caddr_t)&value, &len)
634  	    != DDI_PROP_SUCCESS) {
635  			cmn_err(CE_WARN,
636  			    "fdc_attach: Error, could not find a dma channel");
637  			return (DDI_FAILURE);
638  	}
639  	fcp->c_dmachan = (ushort_t)value;
640  	fcp->c_number = cntlr;
641  	return (DDI_SUCCESS);
642  }
643  
644  /* ARGSUSED */
645  static void
646  fdc_propinit2(struct fdcntlr *fcp, int cntlr)
647  {
648  	dev_info_t *dip;
649  	int ccr;
650  	int len;
651  	int value;
652  
653  	dip = fcp->c_dip;
654  	len = sizeof (value);
655  
656  	if (ddi_prop_op(DDI_DEV_T_ANY, dip, PROP_LEN_AND_VAL_BUF,
657  	    DDI_PROP_DONTPASS, "chip", (caddr_t)&value, &len)
658  	    == DDI_PROP_SUCCESS)
659  		fcp->c_chip = value;
660  	else {
661  		static uchar_t perpindcmd[2] = {FO_PERP, 0};
662  		static uchar_t versioncmd = FO_VRSN;
663  		uchar_t result;
664  
665  		fcp->c_chip = i8272A;
666  		(void) fdc_docmd(fcp, &versioncmd, 1);
667  		/*
668  		 * Ignored return. If failed, warning was issued by fdc_docmd.
669  		 * fdc_results retrieves the controller/drive status
670  		 */
671  		if (!fdc_result(fcp, &result, 1) && result == 0x90) {
672  			/*
673  			 * try a perpendicular_mode cmd to ensure
674  			 * that we really have an enhanced controller
675  			 */
676  			if (fdc_docmd(fcp, perpindcmd, 2) ||
677  			    fdc_docmd(fcp, configurecmd, 4))
678  				/*
679  				 * perpindicular_mode will be rejected by
680  				 * older controllers; make sure we don't hang.
681  				 */
682  				(void) fdc_result(fcp, &result, 1);
683  				/*
684  				 * Ignored return. If failed, warning was
685  				 * issued by fdc_result.
686  				 */
687  			else
688  				/* enhanced type controller */
689  
690  				if ((fcp->c_chip = fdc_enhance_probe(fcp)) == 0)
691  					/* default enhanced cntlr */
692  					fcp->c_chip = i82077;
693  		}
694  		(void) ddi_prop_update_int(DDI_DEV_T_NONE, dip,
695  		    "chip", fcp->c_chip);
696  		/*
697  		 * Ignoring return value because, for passed arguments, only
698  		 * DDI_SUCCESS is returned.
699  		 */
700  	}
701  	if (fcp->c_chip >= i82077 && fcp->c_mode == FDCMODE_30 &&
702  	    (inb(fcp->c_regbase + FCR_DIR) & 0x70) == 0)
703  		for (ccr = 0; ccr <= (FCC_NOPREC | FCC_DRATE); ccr++) {
704  			/*
705  			 * run through all the combinations of NOPREC and
706  			 * datarate selection, and see if they show up in the
707  			 * Model 30 DIR
708  			 */
709  			outb(fcp->c_regbase + FCR_CCR, ccr);
710  			drv_usecwait(5);
711  			if ((inb(fcp->c_regbase + FCR_DIR) &
712  			    (FCC_NOPREC | FCC_DRATE)) != ccr) {
713  				fcp->c_mode = FDCMODE_AT;
714  				break;
715  			}
716  		}
717  	else
718  		fcp->c_mode = FDCMODE_AT;
719  	outb(fcp->c_regbase + FCR_CCR, 0);
720  }
721  
722  /* ARGSUSED */
723  static int
724  fdc_enhance_probe(struct fdcntlr *fcp)
725  {
726  	static uchar_t nsccmd = FO_NSC;
727  	uint_t	ddic;
728  	int	retcode = 0;
729  	uchar_t	result;
730  	uchar_t	save;
731  
732  	/*
733  	 * Try to identify the enhanced floppy controller.
734  	 * This is required so that we can program the DENSEL output to
735  	 * control 3D mode (1.0 MB, 1.6 MB and 2.0 MB unformatted capacity,
736  	 * 720 KB, 1.2 MB, and 1.44 MB formatted capacity) 3.5" dual-speed
737  	 * floppy drives.  Refer to bugid 1195155.
738  	 */
739  
740  	(void) fdc_docmd(fcp, &nsccmd, 1);
741  	/*
742  	 * Ignored return. If failed, warning was issued by fdc_docmd.
743  	 * fdc_results retrieves the controller/drive status
744  	 */
745  	if (!fdc_result(fcp, &result, 1) && result != S0_IVCMD) {
746  		/*
747  		 * only enhanced National Semi PC8477 core
748  		 * should respond to this command
749  		 */
750  		if ((result & 0xf0) == 0x70) {
751  			/* low 4 bits may change */
752  			fcp->c_flags |= FCFLG_3DMODE;
753  			retcode = PC87322;
754  		} else
755  			cmn_err(CE_CONT,
756  "?fdc: unidentified, enhanced, National Semiconductor cntlr %x\n", result);
757  	} else {
758  		save = inb(fcp->c_regbase + FCR_SRA);
759  
760  		do {
761  			/* probe for motherboard version of SMC cntlr */
762  
763  			/* try to enable configuration mode */
764  			ddic = ddi_enter_critical();
765  			outb(fcp->c_regbase + FCR_SRA, FSA_ENA5);
766  			outb(fcp->c_regbase + FCR_SRA, FSA_ENA5);
767  			ddi_exit_critical(ddic);
768  
769  			outb(fcp->c_regbase + FCR_SRA, 0x0F);
770  			if (inb(fcp->c_regbase + FCR_SRB) != 0x00)
771  				/* always expect 0 from config reg F */
772  				break;
773  			outb(fcp->c_regbase + FCR_SRA, 0x0D);
774  			if (inb(fcp->c_regbase + FCR_SRB) != 0x65)
775  				/* expect 0x65 from config reg D */
776  				break;
777  			outb(fcp->c_regbase + FCR_SRA, 0x0E);
778  			result = inb(fcp->c_regbase + FCR_SRB);
779  			if (result != 0x02) {
780  				/* expect revision level 2 from config reg E */
781  				cmn_err(CE_CONT,
782  "?fdc: unidentified, enhanced, SMC cntlr revision %x\n", result);
783  				/* break;	*/
784  			}
785  			fcp->c_flags |= FCFLG_3DMODE;
786  			retcode = FDC37C665;
787  		} while (retcode == 0);
788  		outb(fcp->c_regbase + FCR_SRA, FSA_DISB);
789  
790  		while (retcode == 0) {
791  			/* probe for adapter version of SMC cntlr */
792  			ddic = ddi_enter_critical();
793  			outb(fcp->c_regbase + FCR_SRA, FSA_ENA6);
794  			outb(fcp->c_regbase + FCR_SRA, FSA_ENA6);
795  			ddi_exit_critical(ddic);
796  
797  			outb(fcp->c_regbase + FCR_SRA, 0x0F);
798  			if (inb(fcp->c_regbase + FCR_SRB) != 0x00)
799  				/* always expect 0 from config reg F */
800  				break;
801  			outb(fcp->c_regbase + FCR_SRA, 0x0D);
802  			if (inb(fcp->c_regbase + FCR_SRB) != 0x66)
803  				/* expect 0x66 from config reg D */
804  				break;
805  			outb(fcp->c_regbase + FCR_SRA, 0x0E);
806  			result = inb(fcp->c_regbase + FCR_SRB);
807  			if (result != 0x02) {
808  				/* expect revision level 2 from config reg E */
809  				cmn_err(CE_CONT,
810  "?fdc: unidentified, enhanced, SMC cntlr revision %x\n", result);
811  				/* break;	*/
812  			}
813  			fcp->c_flags |= FCFLG_3DMODE;
814  			retcode = FDC37C666;
815  		}
816  		outb(fcp->c_regbase + FCR_SRA, FSA_DISB);
817  
818  		drv_usecwait(10);
819  		outb(fcp->c_regbase + FCR_SRA, save);
820  	}
821  	return (retcode);
822  }
823  
824  /* ARGSUSED */
825  static int
826  fdc_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
827  {
828  	struct fdcntlr *fcp;
829  	int unit;
830  	int rval = 0;
831  
832  	FCERRPRINT(FDEP_L3, FDEM_ATTA, (CE_WARN, "fdc_detach: dip %p",
833  		(void*)dip));
834  
835  	fcp = ddi_get_driver_private(dip);
836  
837  	switch (cmd) {
838  	case DDI_DETACH:
839  		for (unit = 0; unit < NFDUN; unit++)
840  			if ((fcp->c_unit[unit])->fj_dip) {
841  				rval = EBUSY;
842  				break;
843  			}
844  		kstat_delete(fcp->c_intrstat);
845  		fcp->c_intrstat = NULL;
846  		ddi_remove_intr(fcp->c_dip, 0, fcp->c_iblock);
847  		if (ddi_dmae_release(fcp->c_dip, fcp->c_dmachan) !=
848  		    DDI_SUCCESS)
849  			cmn_err(CE_WARN, "fdc_detach: dma release failed, "
850  				"dip %p, dmachan %x\n",
851  				(void*)fcp->c_dip, fcp->c_dmachan);
852  		ddi_prop_remove_all(fcp->c_dip);
853  		ddi_set_driver_private(fcp->c_dip, NULL);
854  
855  		mutex_destroy(&fcp->c_lock);
856  		mutex_destroy(&fcp->c_dorlock);
857  		cv_destroy(&fcp->c_iocv);
858  		sema_destroy(&fcp->c_selsem);
859  		ddi_soft_state_free(fdc_state_head, ddi_get_instance(dip));
860  		break;
861  	default:
862  		rval = EINVAL;
863  		break;
864  	}
865  	return (rval);
866  }
867  
868  
869  /* ARGSUSED */
870  int
871  fdc_start(struct fcu_obj *fjp)
872  {
873  	return (ENOSYS);
874  }
875  
876  int
877  fdc_abort(struct fcu_obj *fjp)
878  {
879  	struct fdcntlr *fcp = fjp->fj_fdc;
880  	int unit = fjp->fj_unit & 3;
881  
882  	FCERRPRINT(FDEP_L3, FDEM_RESE, (CE_WARN, "fdc_abort"));
883  	if (fcp->c_curunit == unit) {
884  		mutex_enter(&fcp->c_lock);
885  		if (fcp->c_flags & FCFLG_WAITING) {
886  			/*
887  			 * this can cause data corruption !
888  			 */
889  			fdcquiesce(fcp);
890  			fcp->c_csb.csb_xstate = FXS_RESET;
891  			fcp->c_flags |= FCFLG_TIMEOUT;
892  			if (ddi_dmae_stop(fcp->c_dip, fcp->c_dmachan) !=
893  			    DDI_SUCCESS)
894  				cmn_err(CE_WARN,
895  					"fdc_detach: dma release failed, "
896  					"dip %p, dmachan %x\n",
897  					(void*)fcp->c_dip, fcp->c_dmachan);
898  		}
899  		mutex_exit(&fcp->c_lock);
900  		drv_usecwait(500);
901  		return (DDI_SUCCESS);
902  	}
903  	return (DDI_FAILURE);
904  }
905  
906  /* ARGSUSED */
907  int
908  fdc_getcap(struct fcu_obj *fjp, char *a, int i)
909  {
910  	return (ENOSYS);
911  }
912  
913  /* ARGSUSED */
914  int
915  fdc_setcap(struct fcu_obj *fjp, char *a, int i, int j)
916  {
917  	return (ENOSYS);
918  }
919  
920  int
921  fdc_dkinfo(struct fcu_obj *fjp, struct dk_cinfo *dcp)
922  {
923  	struct fdcntlr *fcp = fjp->fj_fdc;
924  
925  	(void) strncpy((char *)&dcp->dki_cname, ddi_get_name(fcp->c_dip),
926  		DK_DEVLEN);
927  	dcp->dki_ctype = DKC_UNKNOWN; /* no code for generic PC/AT fdc */
928  	dcp->dki_flags = DKI_FMTTRK;
929  	dcp->dki_addr = fcp->c_regbase;
930  	dcp->dki_space = 0;
931  	dcp->dki_prio = fcp->c_intprio;
932  	dcp->dki_vec = fcp->c_intvec;
933  	(void) strncpy((char *)&dcp->dki_dname, ddi_driver_name(fjp->fj_dip),
934  		DK_DEVLEN);
935  	dcp->dki_slave = fjp->fj_unit & 3;
936  	dcp->dki_maxtransfer = maxphys / DEV_BSIZE;
937  	return (DDI_SUCCESS);
938  }
939  
940  /*
941   * on=> non-zero = select, 0 = de-select
942   */
943  /* ARGSUSED */
944  int
945  fdc_select(struct fcu_obj *fjp, int funit, int on)
946  {
947  	struct fdcntlr *fcp = fjp->fj_fdc;
948  	int unit = funit & 3;
949  
950  	if (on) {
951  		/* possess controller */
952  		sema_p(&fcp->c_selsem);
953  		FCERRPRINT(FDEP_L2, FDEM_DSEL,
954  		    (CE_NOTE, "fdc_select unit %d: on", funit));
955  
956  		if (fcp->c_curunit != unit || !(fjp->fj_flags & FUNIT_CHAROK)) {
957  			fcp->c_curunit = unit;
958  			fjp->fj_flags |= FUNIT_CHAROK;
959  			if (fdcspecify(fcp,
960  			    fjp->fj_chars->fdc_transfer_rate,
961  			    fjp->fj_drive->fdd_steprate, 40))
962  				cmn_err(CE_WARN,
963  				    "fdc_select: controller setup rejected "
964  				    "fdcntrl %p transfer rate %x step rate %x"
965  				    " head load time 40\n", (void*)fcp,
966  				    fjp->fj_chars->fdc_transfer_rate,
967  				    fjp->fj_drive->fdd_steprate);
968  		}
969  
970  		mutex_enter(&fcp->c_dorlock);
971  
972  		/* make sure drive is not selected in case we change speed */
973  		fcp->c_digout = (fcp->c_digout & ~FD_DRSEL) |
974  		    (~unit & FD_DRSEL);
975  		outb(fcp->c_regbase + FCR_DOR, fcp->c_digout);
976  
977  		(void) fdc_motorsm(fjp, FMI_STARTCMD,
978  		    fjp->fj_drive->fdd_motoron);
979  		/*
980  		 * Return value ignored - fdcmotort deals with failure.
981  		 */
982  		if (fdcspdchange(fcp, fjp, fjp->fj_attr->fda_rotatespd)) {
983  			/* 3D drive requires 500 ms for speed change */
984  			(void) fdc_motorsm(fjp, FMI_RSTARTCMD, 5);
985  			/*
986  			 * Return value ignored - fdcmotort deals with failure.
987  			 */
988  		}
989  
990  		fcp->c_digout = (fcp->c_digout & ~FD_DRSEL) | (unit & FD_DRSEL);
991  		outb(fcp->c_regbase + FCR_DOR, fcp->c_digout);
992  
993  		mutex_exit(&fcp->c_dorlock);
994  		fcp->c_csb.csb_drive = (uchar_t)unit;
995  	} else {
996  		FCERRPRINT(FDEP_L2, FDEM_DSEL,
997  		    (CE_NOTE, "fdc_select unit %d: off", funit));
998  
999  		mutex_enter(&fcp->c_dorlock);
1000  
1001  		fcp->c_digout |= FD_DRSEL;
1002  		outb(fcp->c_regbase + FCR_DOR, fcp->c_digout);
1003  		(void) fdc_motorsm(fjp, FMI_IDLECMD,
1004  		    fjp->fj_drive->fdd_motoroff);
1005  		/*
1006  		 * Return value ignored - fdcmotort deals with failure.
1007  		 */
1008  
1009  		mutex_exit(&fcp->c_dorlock);
1010  
1011  		/* give up controller */
1012  		sema_v(&fcp->c_selsem);
1013  	}
1014  	return (0);
1015  }
1016  
1017  
1018  int
1019  fdgetchng(struct fcu_obj *fjp, int funit)
1020  {
1021  	if (fdcsense_drv(fjp->fj_fdc, funit & 3))
1022  		cmn_err(CE_WARN, "fdgetchng: write protect check failed\n");
1023  	return (fdcsense_chng(fjp->fj_fdc, funit & 3));
1024  }
1025  
1026  
1027  int
1028  fdresetchng(struct fcu_obj *fjp, int funit)
1029  {
1030  	struct fdcntlr *fcp = fjp->fj_fdc;
1031  	int unit = funit & 3;
1032  	int newcyl;			/* where to seek for reset of DSKCHG */
1033  
1034  	FCERRPRINT(FDEP_L2, FDEM_CHEK, (CE_NOTE, "fdmediachng unit %d", funit));
1035  
1036  	if (fcp->c_curpcyl[unit])
1037  		newcyl = fcp->c_curpcyl[unit] - 1;
1038  	else
1039  		newcyl = 1;
1040  	return (fdrecalseek(fjp, funit, newcyl, 0));
1041  }
1042  
1043  
1044  /*
1045   * fdrecalseek
1046   */
1047  int
1048  fdrecalseek(struct fcu_obj *fjp, int funit, int arg, int execflg)
1049  {
1050  	struct fdcntlr *fcp = fjp->fj_fdc;
1051  	struct fdcsb *csb;
1052  	int unit = funit & 3;
1053  	int rval;
1054  
1055  	FCERRPRINT(FDEP_L2, FDEM_RECA, (CE_NOTE, "fdrecalseek unit %d to %d",
1056  	    funit, arg));
1057  
1058  	csb = &fcp->c_csb;
1059  	csb->csb_cmd[1] = (uchar_t)unit;
1060  	if (arg < 0) {			/* is recal... */
1061  		*csb->csb_cmd = FO_RECAL;
1062  		csb->csb_ncmds = 2;
1063  		csb->csb_timer = 28;
1064  	} else {
1065  		*csb->csb_cmd = FO_SEEK;
1066  		csb->csb_cmd[2] = (uchar_t)arg;
1067  		csb->csb_ncmds = 3;
1068  		csb->csb_timer = 10;
1069  	}
1070  	csb->csb_nrslts = 2;	/* 2 for SENSE INTERRUPTS */
1071  	csb->csb_opflags = CSB_OFINRPT;
1072  	csb->csb_maxretry = skretry;
1073  	csb->csb_dmahandle = NULL;
1074  	csb->csb_handle_bound = 0;
1075  	csb->csb_dmacookiecnt = 0;
1076  	csb->csb_dmacurrcookie = 0;
1077  	csb->csb_dmawincnt = 0;
1078  	csb->csb_dmacurrwin = 0;
1079  
1080  	/* send cmd off to fdc_exec */
1081  	if (rval = fdc_exec(fcp, 1, execflg))
1082  		goto out;
1083  
1084  	if (!(*csb->csb_rslt & S0_SEKEND) ||
1085  	    (*csb->csb_rslt & S0_ICMASK) ||
1086  	    ((*csb->csb_rslt & S0_ECHK) && arg < 0) ||
1087  	    csb->csb_cmdstat)
1088  		rval = ENODEV;
1089  
1090  	if (fdcsense_drv(fcp, unit))
1091  		cmn_err(CE_WARN, "fdgetchng: write protect check failed\n");
1092  out:
1093  	return (rval);
1094  }
1095  
1096  
1097  /*
1098   * fdrw- used only for read/writing sectors into/from kernel buffers.
1099   */
1100  int
1101  fdrw(struct fcu_obj *fjp, int funit, int rw, int cyl, int head,
1102      int sector, caddr_t bufp, uint_t len)
1103  {
1104  	struct fdcntlr *fcp = fjp->fj_fdc;
1105  	struct fdcsb *csb;
1106  	uint_t dmar_flags = 0;
1107  	int unit = funit & 3;
1108  	int rval;
1109  	ddi_acc_handle_t mem_handle = NULL;
1110  	caddr_t aligned_buf;
1111  	size_t real_size;
1112  
1113  	FCERRPRINT(FDEP_L1, FDEM_RW, (CE_CONT, "fdrw unit %d\n", funit));
1114  
1115  	csb = &fcp->c_csb;
1116  	if (rw) {
1117  		dmar_flags = DDI_DMA_READ;
1118  		csb->csb_opflags = CSB_OFDMARD | CSB_OFINRPT;
1119  		*csb->csb_cmd = FO_MT | FO_MFM | FO_SK | FO_RDDAT;
1120  	} else { /* write */
1121  		dmar_flags = DDI_DMA_WRITE;
1122  		csb->csb_opflags = CSB_OFDMAWT | CSB_OFINRPT;
1123  		*csb->csb_cmd = FO_MT | FO_MFM | FO_WRDAT;
1124  	}
1125  	csb->csb_cmd[1] = (uchar_t)(unit | ((head & 0x1) << 2));
1126  	csb->csb_cmd[2] = (uchar_t)cyl;
1127  	csb->csb_cmd[3] = (uchar_t)head;
1128  	csb->csb_cmd[4] = (uchar_t)sector;
1129  	encode(sector_size, fjp->fj_chars->fdc_sec_size,
1130  	    &csb->csb_cmd[5]);
1131  	csb->csb_cmd[6] = (uchar_t)max(fjp->fj_chars->fdc_secptrack, sector);
1132  	csb->csb_cmd[7] = fjp->fj_attr->fda_gapl;
1133  	csb->csb_cmd[8] = 0xFF;
1134  
1135  	csb->csb_ncmds = 9;
1136  	csb->csb_nrslts = 7;
1137  	csb->csb_timer = 36;
1138  	if (rw == FDRDONE)
1139  		csb->csb_maxretry = 1;
1140  	else
1141  		csb->csb_maxretry = rwretry;
1142  
1143  	csb->csb_dmahandle = NULL;
1144  	csb->csb_handle_bound = 0;
1145  	csb->csb_dmacookiecnt = 0;
1146  	csb->csb_dmacurrcookie = 0;
1147  	csb->csb_dmawincnt = 0;
1148  	csb->csb_dmacurrwin = 0;
1149  	dmar_flags |= (DDI_DMA_STREAMING | DDI_DMA_PARTIAL);
1150  
1151  	if (ddi_dma_alloc_handle(fcp->c_dip, &fdc_dma_attr, DDI_DMA_SLEEP,
1152  			0, &csb->csb_dmahandle) != DDI_SUCCESS) {
1153  		rval = EINVAL;
1154  		goto out;
1155  	}
1156  
1157  	/*
1158  	 * allocate a page aligned buffer to dma to/from. This way we can
1159  	 * ensure the cookie is a whole multiple of granularity and avoids
1160  	 * any alignment issues.
1161  	 */
1162  	rval = ddi_dma_mem_alloc(csb->csb_dmahandle, len, &fdc_accattr,
1163  	    DDI_DMA_CONSISTENT, DDI_DMA_SLEEP, NULL, &aligned_buf,
1164  	    &real_size, &mem_handle);
1165  	if (rval != DDI_SUCCESS) {
1166  		rval = EINVAL;
1167  		goto out;
1168  	}
1169  
1170  	if (dmar_flags & DDI_DMA_WRITE) {
1171  		bcopy(bufp, aligned_buf, len);
1172  	}
1173  
1174  	rval = ddi_dma_addr_bind_handle(csb->csb_dmahandle, NULL, aligned_buf,
1175  	    len, dmar_flags, DDI_DMA_SLEEP, 0, &csb->csb_dmacookie,
1176  	    &csb->csb_dmacookiecnt);
1177  
1178  	if (rval == DDI_DMA_MAPPED) {
1179  		csb->csb_dmawincnt = 1;
1180  		csb->csb_handle_bound = 1;
1181  	} else if (rval == DDI_DMA_PARTIAL_MAP) {
1182  		csb->csb_handle_bound = 1;
1183  		if (ddi_dma_numwin(csb->csb_dmahandle, &csb->csb_dmawincnt) !=
1184  					DDI_SUCCESS) {
1185  			cmn_err(CE_WARN, "fdrw: dma numwin failed\n");
1186  			rval = EINVAL;
1187  			goto out;
1188  		}
1189  	} else {
1190  		cmn_err(CE_WARN,
1191  			"fdrw: dma addr bind handle failed, rval = %d\n",
1192  			rval);
1193  		rval = EINVAL;
1194  		goto out;
1195  	}
1196  	rval = fdc_exec(fcp, 1, 1);
1197  
1198  	if (dmar_flags & DDI_DMA_READ) {
1199  		bcopy(aligned_buf, bufp, len);
1200  	}
1201  
1202  out:
1203  	if (csb->csb_dmahandle) {
1204  		if (csb->csb_handle_bound) {
1205  			if (ddi_dma_unbind_handle(csb->csb_dmahandle) !=
1206  			    DDI_SUCCESS)
1207  				cmn_err(CE_WARN, "fdrw: "
1208  				    "dma unbind handle failed\n");
1209  			csb->csb_handle_bound = 0;
1210  		}
1211  		if (mem_handle != NULL) {
1212  			ddi_dma_mem_free(&mem_handle);
1213  		}
1214  		ddi_dma_free_handle(&csb->csb_dmahandle);
1215  		csb->csb_dmahandle = NULL;
1216  	}
1217  	return (rval);
1218  }
1219  
1220  
1221  int
1222  fdtrkformat(struct fcu_obj *fjp, int funit, int cyl, int head, int filldata)
1223  {
1224  	struct fdcntlr *fcp = fjp->fj_fdc;
1225  	struct fdcsb *csb;
1226  	int unit = funit & 3;
1227  	int fmdatlen, lsector, lstart;
1228  	int interleave, numsctr, offset, psector;
1229  	uchar_t *dp;
1230  	int rval;
1231  	ddi_acc_handle_t mem_handle = NULL;
1232  	caddr_t aligned_buf;
1233  	size_t real_size;
1234  
1235  	FCERRPRINT(FDEP_L2, FDEM_FORM,
1236  	    (CE_NOTE, "fdformattrk unit %d cyl=%d, hd=%d", funit, cyl, head));
1237  
1238  	csb = &fcp->c_csb;
1239  
1240  	csb->csb_opflags = CSB_OFDMAWT | CSB_OFINRPT;
1241  
1242  	*csb->csb_cmd = FO_FRMT | FO_MFM;
1243  	csb->csb_cmd[1] = (head << 2) | unit;
1244  	encode(sector_size, fjp->fj_chars->fdc_sec_size,
1245  	    &csb->csb_cmd[2]);
1246  	csb->csb_cmd[3] = numsctr = fjp->fj_chars->fdc_secptrack;
1247  	csb->csb_cmd[4] = fjp->fj_attr->fda_gapf;
1248  	csb->csb_cmd[5] = (uchar_t)filldata;
1249  
1250  	csb->csb_npcyl = (uchar_t)(cyl * fjp->fj_chars->fdc_steps);
1251  
1252  	csb->csb_dmahandle = NULL;
1253  	csb->csb_handle_bound = 0;
1254  	csb->csb_dmacookiecnt = 0;
1255  	csb->csb_dmacurrcookie = 0;
1256  	csb->csb_dmawincnt = 0;
1257  	csb->csb_dmacurrwin = 0;
1258  	csb->csb_ncmds = 6;
1259  	csb->csb_nrslts = 7;
1260  	csb->csb_timer = 32;
1261  	csb->csb_maxretry = rwretry;
1262  
1263  	/*
1264  	 * alloc space for format track cmd
1265  	 */
1266  	/*
1267  	 * NOTE: have to add size of fifo also - for dummy format action
1268  	 */
1269  	fmdatlen = 4 * numsctr;
1270  
1271  	if (ddi_dma_alloc_handle(fcp->c_dip, &fdc_dma_attr, DDI_DMA_SLEEP,
1272  			0, &csb->csb_dmahandle) != DDI_SUCCESS) {
1273  		rval = EINVAL;
1274  		goto out;
1275  	}
1276  
1277  	/*
1278  	 * allocate a page aligned buffer to dma to/from. This way we can
1279  	 * ensure the cookie is a whole multiple of granularity and avoids
1280  	 * any alignment issues.
1281  	 */
1282  	rval = ddi_dma_mem_alloc(csb->csb_dmahandle, fmdatlen, &fdc_accattr,
1283  	    DDI_DMA_CONSISTENT, DDI_DMA_SLEEP, NULL, &aligned_buf,
1284  	    &real_size, &mem_handle);
1285  	if (rval != DDI_SUCCESS) {
1286  		rval = EINVAL;
1287  		goto out;
1288  	}
1289  	dp = (uchar_t *)aligned_buf;
1290  
1291  	interleave = fjp->fj_attr->fda_intrlv;
1292  	offset = (numsctr + interleave - 1) / interleave;
1293  	for (psector = lstart = 1;
1294  	    psector <= numsctr; psector += interleave, lstart++) {
1295  		for (lsector = lstart; lsector <= numsctr; lsector += offset) {
1296  			*dp++ = (uchar_t)cyl;
1297  			*dp++ = (uchar_t)head;
1298  			*dp++ = (uchar_t)lsector;
1299  			*dp++ = csb->csb_cmd[2];
1300  		}
1301  	}
1302  
1303  	rval = ddi_dma_addr_bind_handle(csb->csb_dmahandle, NULL, aligned_buf,
1304  	    fmdatlen, DDI_DMA_WRITE | DDI_DMA_STREAMING | DDI_DMA_PARTIAL,
1305  	    DDI_DMA_SLEEP, 0, &csb->csb_dmacookie, &csb->csb_dmacookiecnt);
1306  
1307  	if (rval == DDI_DMA_MAPPED) {
1308  		csb->csb_dmawincnt = 1;
1309  		csb->csb_handle_bound = 1;
1310  	} else if (rval == DDI_DMA_PARTIAL_MAP) {
1311  		csb->csb_handle_bound = 1;
1312  		if (ddi_dma_numwin(csb->csb_dmahandle, &csb->csb_dmawincnt) !=
1313  					DDI_SUCCESS) {
1314  			cmn_err(CE_WARN, "fdtrkformat: dma numwin failed\n");
1315  			rval = EINVAL;
1316  			goto out;
1317  		}
1318  	} else {
1319  		cmn_err(CE_WARN,
1320  			"fdtrkformat: dma buf bind handle failed, rval = %d\n",
1321  			rval);
1322  		rval = EINVAL;
1323  		goto out;
1324  	}
1325  
1326  	rval = fdc_exec(fcp, 1, 1);
1327  out:
1328  	if (csb->csb_dmahandle) {
1329  		if (csb->csb_handle_bound) {
1330  			if (ddi_dma_unbind_handle(csb->csb_dmahandle) !=
1331  			    DDI_SUCCESS)
1332  				cmn_err(CE_WARN, "fdtrkformat: "
1333  				    "dma unbind handle failed\n");
1334  			csb->csb_handle_bound = 0;
1335  		}
1336  		if (mem_handle != NULL) {
1337  			ddi_dma_mem_free(&mem_handle);
1338  		}
1339  		ddi_dma_free_handle(&csb->csb_dmahandle);
1340  		csb->csb_dmahandle = NULL;
1341  	}
1342  	return (rval);
1343  }
1344  
1345  /* ARGSUSED */
1346  int
1347  fdrawioctl(struct fcu_obj *fjp, int funit, caddr_t arg)
1348  {
1349  	struct fdcntlr *fcp = fjp->fj_fdc;
1350  	struct fd_raw *fdrp = (struct fd_raw *)arg;
1351  	struct fdcsb *csb;
1352  	uint_t dmar_flags = 0;
1353  	int i;
1354  	int change = 1;
1355  	int sleep = 1;
1356  	int rval = 0;
1357  	int rval_exec = 0;
1358  	ddi_acc_handle_t mem_handle = NULL;
1359  	caddr_t aligned_buf;
1360  	size_t real_size;
1361  
1362  	FCERRPRINT(FDEP_L2, FDEM_RAWI,
1363  	    (CE_NOTE, "fdrawioctl: cmd[0]=0x%x", fdrp->fdr_cmd[0]));
1364  
1365  	csb = &fcp->c_csb;
1366  
1367  	/* copy cmd bytes into csb */
1368  	for (i = 0; i <= fdrp->fdr_cnum; i++)
1369  		csb->csb_cmd[i] = fdrp->fdr_cmd[i];
1370  	csb->csb_ncmds = (uchar_t)fdrp->fdr_cnum;
1371  
1372  	csb->csb_maxretry = 0;	/* let the application deal with errors */
1373  	csb->csb_opflags = CSB_OFRAWIOCTL;
1374  	csb->csb_nrslts = 0;
1375  	csb->csb_timer = 50;
1376  
1377  	switch (fdrp->fdr_cmd[0] & 0x0f) {
1378  
1379  	case FO_SEEK:
1380  		change = 0;
1381  		/* FALLTHROUGH */
1382  	case FO_RECAL:
1383  		csb->csb_opflags |= CSB_OFINRPT;
1384  		break;
1385  
1386  	case FO_FRMT:
1387  		csb->csb_npcyl = *(uchar_t *)(fdrp->fdr_addr) *
1388  		    fjp->fj_chars->fdc_steps;
1389  		/* FALLTHROUGH */
1390  	case FO_WRDAT:
1391  	case FO_WRDEL:
1392  		csb->csb_opflags |= CSB_OFDMAWT | CSB_OFRESLT | CSB_OFINRPT;
1393  		csb->csb_nrslts = 7;
1394  		if (fdrp->fdr_nbytes == 0)
1395  			return (EINVAL);
1396  		dmar_flags = DDI_DMA_WRITE;
1397  		break;
1398  
1399  	case FO_RDDAT:
1400  	case FO_RDDEL:
1401  	case FO_RDTRK:
1402  		csb->csb_opflags |= CSB_OFDMARD | CSB_OFRESLT | CSB_OFINRPT;
1403  		csb->csb_nrslts = 7;
1404  		dmar_flags = DDI_DMA_READ;
1405  		break;
1406  
1407  	case FO_RDID:
1408  		csb->csb_opflags |= CSB_OFRESLT | CSB_OFINRPT;
1409  		csb->csb_nrslts = 7;
1410  		break;
1411  
1412  	case FO_SDRV:
1413  		sleep = 0;
1414  		csb->csb_nrslts = 1;
1415  		break;
1416  
1417  	case FO_SINT:
1418  		sleep = 0;
1419  		change = 0;
1420  		csb->csb_nrslts = 2;
1421  		break;
1422  
1423  	case FO_SPEC:
1424  		sleep = 0;
1425  		change = 0;
1426  		break;
1427  
1428  	default:
1429  		return (EINVAL);
1430  	}
1431  
1432  	csb->csb_dmahandle = NULL;
1433  	csb->csb_handle_bound = 0;
1434  	csb->csb_dmacookiecnt = 0;
1435  	csb->csb_dmacurrcookie = 0;
1436  	csb->csb_dmawincnt = 0;
1437  	csb->csb_dmacurrwin = 0;
1438  
1439  	if (csb->csb_opflags & (CSB_OFDMARD | CSB_OFDMAWT)) {
1440  		if (ddi_dma_alloc_handle(fcp->c_dip, &fdc_dma_attr,
1441  		    DDI_DMA_SLEEP, 0, &csb->csb_dmahandle) != DDI_SUCCESS) {
1442  			rval = EINVAL;
1443  			goto out;
1444  		}
1445  
1446  		/*
1447  		 * allocate a page aligned buffer to dma to/from. This way we
1448  		 * can ensure the cookie is a whole multiple of granularity and
1449  		 * avoids any alignment issues.
1450  		 */
1451  		rval = ddi_dma_mem_alloc(csb->csb_dmahandle,
1452  		    (uint_t)fdrp->fdr_nbytes, &fdc_accattr, DDI_DMA_CONSISTENT,
1453  		    DDI_DMA_SLEEP, NULL, &aligned_buf, &real_size, &mem_handle);
1454  		if (rval != DDI_SUCCESS) {
1455  			rval = EINVAL;
1456  			goto out;
1457  		}
1458  
1459  		if (dmar_flags & DDI_DMA_WRITE) {
1460  			bcopy(fdrp->fdr_addr, aligned_buf,
1461  			    (uint_t)fdrp->fdr_nbytes);
1462  		}
1463  
1464  		dmar_flags |= (DDI_DMA_STREAMING | DDI_DMA_PARTIAL);
1465  		rval = ddi_dma_addr_bind_handle(csb->csb_dmahandle, NULL,
1466  		    aligned_buf, (uint_t)fdrp->fdr_nbytes, dmar_flags,
1467  		    DDI_DMA_SLEEP, 0, &csb->csb_dmacookie,
1468  		    &csb->csb_dmacookiecnt);
1469  
1470  		if (rval == DDI_DMA_MAPPED) {
1471  			csb->csb_dmawincnt = 1;
1472  			csb->csb_handle_bound = 1;
1473  		} else if (rval == DDI_DMA_PARTIAL_MAP) {
1474  			csb->csb_handle_bound = 1;
1475  			if (ddi_dma_numwin(csb->csb_dmahandle,
1476  			    &csb->csb_dmawincnt) != DDI_SUCCESS) {
1477  				cmn_err(CE_WARN,
1478  				    "fdrawioctl: dma numwin failed\n");
1479  				rval = EINVAL;
1480  				goto out;
1481  			}
1482  		} else {
1483  			cmn_err(CE_WARN, "fdrawioctl: "
1484  			    "dma buf bind handle failed, rval = %d\n", rval);
1485  			rval = EINVAL;
1486  			goto out;
1487  		}
1488  	}
1489  
1490  	FCERRPRINT(FDEP_L1, FDEM_RAWI,
1491  	    (CE_CONT, "cmd: %x %x %x %x %x %x %x %x %x %x\n", csb->csb_cmd[0],
1492  	    csb->csb_cmd[1], csb->csb_cmd[2], csb->csb_cmd[3],
1493  	    csb->csb_cmd[4], csb->csb_cmd[5], csb->csb_cmd[6],
1494  	    csb->csb_cmd[7], csb->csb_cmd[8], csb->csb_cmd[9]));
1495  	FCERRPRINT(FDEP_L1, FDEM_RAWI,
1496  	    (CE_CONT, "nbytes: %x, opflags: %x, addr: %p, len: %x\n",
1497  	    csb->csb_ncmds, csb->csb_opflags, (void *)fdrp->fdr_addr,
1498  	    fdrp->fdr_nbytes));
1499  
1500  	/*
1501  	 * Note that we ignore any error returns from fdexec.
1502  	 * This is the way the driver has been, and it may be
1503  	 * that the raw ioctl senders simply don't want to
1504  	 * see any errors returned in this fashion.
1505  	 */
1506  
1507  	/*
1508  	 * VP/ix sense drive ioctl call checks for the error return.
1509  	 */
1510  
1511  	rval_exec = fdc_exec(fcp, sleep, change);
1512  
1513  	if (dmar_flags & DDI_DMA_READ) {
1514  		bcopy(aligned_buf, fdrp->fdr_addr, (uint_t)fdrp->fdr_nbytes);
1515  	}
1516  
1517  	FCERRPRINT(FDEP_L1, FDEM_RAWI,
1518  	    (CE_CONT, "rslt: %x %x %x %x %x %x %x %x %x %x\n", csb->csb_rslt[0],
1519  	    csb->csb_rslt[1], csb->csb_rslt[2], csb->csb_rslt[3],
1520  	    csb->csb_rslt[4], csb->csb_rslt[5], csb->csb_rslt[6],
1521  	    csb->csb_rslt[7], csb->csb_rslt[8], csb->csb_rslt[9]));
1522  
1523  	/* copy results into fdr */
1524  	for (i = 0; i <= (int)csb->csb_nrslts; i++)
1525  		fdrp->fdr_result[i] = csb->csb_rslt[i];
1526  /*	fdrp->fdr_nbytes = fdc->c_csb.csb_rlen;  return resid */
1527  
1528  out:
1529  	if (csb->csb_dmahandle) {
1530  		if (csb->csb_handle_bound) {
1531  			if (ddi_dma_unbind_handle(csb->csb_dmahandle) !=
1532  			    DDI_SUCCESS)
1533  				cmn_err(CE_WARN, "fdrawioctl: "
1534  				    "dma unbind handle failed\n");
1535  			csb->csb_handle_bound = 0;
1536  		}
1537  		if (mem_handle != NULL) {
1538  			ddi_dma_mem_free(&mem_handle);
1539  		}
1540  		ddi_dma_free_handle(&csb->csb_dmahandle);
1541  		csb->csb_dmahandle = NULL;
1542  	}
1543  	if ((fdrp->fdr_cmd[0] & 0x0f) == FO_SDRV) {
1544  		return (rval_exec);
1545  	}
1546  	return (rval);
1547  }
1548  
1549  void
1550  encode(xlate_tbl_t *tablep, int val, uchar_t *rcode)
1551  {
1552  	do {
1553  		if (tablep->value >= val) {
1554  			*rcode = tablep->code;
1555  			return;
1556  		}
1557  	} while ((++tablep)->value);
1558  	*rcode = tablep->code;
1559  	cmn_err(CE_WARN, "fdc encode failed, table %p val %x code %x\n",
1560  	    (void *)tablep, val, (uint_t)*rcode);
1561  }
1562  
1563  int
1564  decode(xlate_tbl_t *tablep, int kode, int *rvalue)
1565  {
1566  	do  {
1567  		if (tablep->code == kode) {
1568  			*rvalue = tablep->value;
1569  			return (0);
1570  		}
1571  	} while ((++tablep)->value);
1572  	return (-1);
1573  }
1574  
1575  
1576  void
1577  fdcquiesce(struct fdcntlr *fcp)
1578  {
1579  	int unit;
1580  
1581  	FCERRPRINT(FDEP_L2, FDEM_RESE, (CE_NOTE, "fdcquiesce fcp %p",
1582  		(void*)fcp));
1583  	ASSERT(MUTEX_HELD(&fcp->c_lock));
1584  
1585  	mutex_enter(&fcp->c_dorlock);
1586  
1587  	if (ddi_dmae_stop(fcp->c_dip, fcp->c_dmachan) != DDI_SUCCESS)
1588  		cmn_err(CE_WARN, "fdcquiesce: dmae stop failed, "
1589  			"dip %p, dmachan %x\n",
1590  			(void*)fcp->c_dip, fcp->c_dmachan);
1591  
1592  	fcp->c_digout = (fcp->c_digout & (FD_DMTREN | FD_DRSEL)) | FD_ENABLE;
1593  	outb(fcp->c_regbase + FCR_DOR, fcp->c_digout);
1594  	drv_usecwait(20);
1595  	fcp->c_digout |= FD_RSETZ;
1596  	outb(fcp->c_regbase + FCR_DOR, fcp->c_digout);
1597  
1598  	mutex_exit(&fcp->c_dorlock);
1599  
1600  	/* count resets */
1601  	fcp->fdstats.reset++;
1602  	fcp->c_curunit = -1;
1603  	for (unit = 0; unit < NFDUN; unit++)
1604  		fcp->c_curpcyl[unit] = -1;
1605  
1606  	if (fcp->c_chip >= i82077) {
1607  		(void) fdc_docmd(fcp, configurecmd, 4);
1608  		/*
1609  		 * Ignored return. If failed, warning was issued by fdc_docmd.
1610  		 */
1611  	}
1612  }
1613  
1614  void
1615  fdcreadid(struct fdcntlr *fcp, struct fdcsb *csb)
1616  {
1617  	static uchar_t readidcmd[2] = {FO_RDID | FO_MFM, 0};
1618  
1619  	readidcmd[1] = csb->csb_cmd[1];
1620  	(void) fdc_docmd(fcp, readidcmd, 2);
1621  }
1622  
1623  int
1624  fdcseek(struct fdcntlr *fcp, int unit, int cyl)
1625  {
1626  	static uchar_t seekabscmd[3] = {FO_SEEK, 0, 0};
1627  
1628  	FCERRPRINT(FDEP_L0, FDEM_RECA, (CE_CONT, "fdcseek unit %d to cyl %d\n",
1629  	    unit, cyl));
1630  	seekabscmd[1] = (uchar_t)unit;
1631  	seekabscmd[2] = (uchar_t)cyl;
1632  	return (fdc_docmd(fcp, seekabscmd, 3));
1633  }
1634  
1635  /*
1636   * Returns status of disk change line of selected drive.
1637   *	= 0 means diskette is present
1638   *	!= 0 means diskette was removed and current state is unknown
1639   */
1640  int
1641  fdcsense_chng(struct fdcntlr *fcp, int unit)
1642  {
1643  	int digital_input;
1644  
1645  	FCERRPRINT(FDEP_L0, FDEM_SCHG,
1646  	    (CE_CONT, "fdcsense_chng unit %d\n", unit));
1647  	digital_input = inb(fcp->c_regbase + FCR_DIR);
1648  	if (fcp->c_mode == FDCMODE_30)
1649  		digital_input ^= FDI_DKCHG;
1650  	return (digital_input & FDI_DKCHG);
1651  }
1652  
1653  int
1654  fdcsense_drv(struct fdcntlr *fcp, int unit)
1655  {
1656  	static uchar_t sensedrvcmd[2] = {FO_SDRV, 0};
1657  	uchar_t senser;
1658  	int rval;
1659  
1660  	sensedrvcmd[1] = (uchar_t)unit;
1661  	(void) fdc_docmd(fcp, sensedrvcmd, 2);
1662  	/*
1663  	 * Ignored return. If failed, warning was issued by fdc_docmd.
1664  	 * fdc_results retrieves the controller/drive status
1665  	 */
1666  	if (rval = fdc_result(fcp, &senser, 1))
1667  		goto done;
1668  	if (senser & S3_WPROT)
1669  		fcp->c_unit[unit]->fj_flags |= FUNIT_WPROT;
1670  	else
1671  		fcp->c_unit[unit]->fj_flags &= ~FUNIT_WPROT;
1672  done:
1673  	return (rval);
1674  }
1675  
1676  int
1677  fdcsense_int(struct fdcntlr *fcp, int *unitp, int *cylp)
1678  {
1679  	uchar_t senser[2];
1680  	int rval;
1681  
1682  	(void) fdc_docmd(fcp, &senseintcmd, 1);
1683  	/*
1684  	 * Ignored return. If failed, warning was issued by fdc_docmd.
1685  	 * fdc_results retrieves the controller/drive status
1686  	 */
1687  
1688  	if (!(rval = fdc_result(fcp, senser, 2))) {
1689  		if ((*senser & (S0_IVCMD | S0_SEKEND | S0_ECHK)) != S0_SEKEND)
1690  			rval = 1;
1691  		if (unitp)
1692  			*unitp = *senser & 3;
1693  		if (cylp)
1694  			*cylp = senser[1];
1695  	}
1696  	return (rval);
1697  }
1698  
1699  int
1700  fdcspecify(struct fdcntlr *fcp, int xferrate, int steprate, int hlt)
1701  {
1702  	static uchar_t perpindcmd[2] = {FO_PERP, 0};
1703  	static uchar_t specifycmd[3] = {FO_SPEC, 0, 0};
1704  
1705  	encode(drate_mfm, xferrate, &fcp->c_config);
1706  	outb(fcp->c_regbase + FCR_CCR, fcp->c_config);
1707  
1708  	if (fcp->c_chip >= i82077) {
1709  		/*
1710  		 * Use old style perpendicular mode command of 82077.
1711  		 */
1712  		if (xferrate == 1000) {
1713  			/* Set GAP and WGATE */
1714  			perpindcmd[1] = 3;
1715  			/* double step rate because xlate table is for 500Kb */
1716  			steprate <<= 1;
1717  			hlt <<= 1;
1718  		} else
1719  			perpindcmd[1] = 0;
1720  		(void) fdc_docmd(fcp, perpindcmd, 2);
1721  		/*
1722  		 * Ignored return. If failed, warning was issued by fdc_docmd.
1723  		 */
1724  	}
1725  	encode(step_rate, steprate, &fcp->c_hutsrt);
1726  	specifycmd[1] = fcp->c_hutsrt |= 0x0F;	/* use max head unload time */
1727  	hlt = (hlt >= 256) ? 0 : (hlt >> 1);	/* encode head load time */
1728  	specifycmd[2] = fcp->c_hlt = hlt << 1;	/* make room for DMA bit */
1729  	return (fdc_docmd(fcp, specifycmd, 3));
1730  }
1731  
1732  int
1733  fdcspdchange(struct fdcntlr *fcp, struct fcu_obj *fjp, int rpm)
1734  {
1735  	int	retcode = 0;
1736  	uint_t	ddic;
1737  	uchar_t	deselect = 0;
1738  	uchar_t	ds_code;
1739  	uchar_t	enable_code;
1740  	uchar_t	save;
1741  
1742  	if (((fcp->c_flags & FCFLG_DSOUT) == 0 && rpm <= fjp->fj_rotspd) ||
1743  	    ((fcp->c_flags & FCFLG_DSOUT) && (fjp->fj_flags & FUNIT_3DMODE) &&
1744  	    rpm > fjp->fj_rotspd)) {
1745  		return (0);
1746  	}
1747  
1748  	FCERRPRINT(FDEP_L1, FDEM_SCHG,
1749  	    (CE_CONT, "fdcspdchange: %d rpm\n", rpm));
1750  	ASSERT(MUTEX_HELD(&fcp->c_dorlock));
1751  
1752  	switch (fcp->c_chip) {
1753  	default:
1754  		break;
1755  	case i82077:
1756  		break;
1757  
1758  	case PC87322:
1759  		{
1760  		uchar_t nscmodecmd[5] = {FO_MODE, 0x02, 0x00, 0xC8, 0x00};
1761  
1762  		if (rpm > fjp->fj_rotspd) {
1763  			nscmodecmd[3] ^= 0xC0;
1764  			retcode = (fcp->c_flags ^ FCFLG_DSOUT) ||
1765  			    (fjp->fj_flags ^ FUNIT_3DMODE);
1766  			fcp->c_flags |= FCFLG_DSOUT;
1767  			fjp->fj_flags |= FUNIT_3DMODE;
1768  		} else {
1769  			/* program DENSEL to default output */
1770  			fcp->c_flags &= ~FCFLG_DSOUT;
1771  			retcode = fjp->fj_flags & FUNIT_3DMODE;
1772  			fjp->fj_flags &= ~FUNIT_3DMODE;
1773  		}
1774  		if (retcode && (fcp->c_digout & FD_DRSEL) == fcp->c_curunit) {
1775  			/* de-select drive while changing speed */
1776  			deselect = fcp->c_digout ^ FD_DRSEL;
1777  			outb(fcp->c_regbase + FCR_DOR, deselect);
1778  		}
1779  
1780  		(void) fdc_docmd(fcp, nscmodecmd, 5);
1781  		/*
1782  		 * Ignored return. If failed, warning was issued by fdc_docmd.
1783  		 */
1784  		break;
1785  		}
1786  
1787  	case FDC37C665:
1788  		enable_code = FSA_ENA5;
1789  		goto SMC_config;
1790  
1791  	case FDC37C666:
1792  		enable_code = FSA_ENA6;
1793  SMC_config:
1794  		if (rpm > fjp->fj_rotspd) {
1795  			/* force DENSEL output to active LOW */
1796  			ds_code = FSB_DSHI;
1797  			retcode = (fcp->c_flags ^ FCFLG_DSOUT) ||
1798  			    (fjp->fj_flags ^ FUNIT_3DMODE);
1799  			fcp->c_flags |= FCFLG_DSOUT;
1800  			fjp->fj_flags |= FUNIT_3DMODE;
1801  		} else {
1802  			/* program DENSEL to default output */
1803  			ds_code = 0;
1804  			fcp->c_flags &= ~FCFLG_DSOUT;
1805  			retcode = fjp->fj_flags & FUNIT_3DMODE;
1806  			fjp->fj_flags &= ~FUNIT_3DMODE;
1807  		}
1808  		if (retcode && (fcp->c_digout & FD_DRSEL) == fcp->c_curunit) {
1809  			/* de-select drive while changing speed */
1810  			deselect = fcp->c_digout ^ FD_DRSEL;
1811  			outb(fcp->c_regbase + FCR_DOR, deselect);
1812  		}
1813  		save = inb(fcp->c_regbase + FCR_SRA);
1814  
1815  		/* enter configuration mode */
1816  		ddic = ddi_enter_critical();
1817  		outb(fcp->c_regbase + FCR_SRA, enable_code);
1818  		outb(fcp->c_regbase + FCR_SRA, enable_code);
1819  		ddi_exit_critical(ddic);
1820  
1821  		outb(fcp->c_regbase + FCR_SRA, FSA_CR5);
1822  		enable_code = inb(fcp->c_regbase + FCR_SRB) & FSB_DSDEF;
1823  		/* update DENSEL mode bits */
1824  		outb(fcp->c_regbase + FCR_SRB, enable_code | ds_code);
1825  
1826  		/* exit configuration mode */
1827  		outb(fcp->c_regbase + FCR_SRA, FSA_DISB);
1828  		drv_usecwait(10);
1829  		outb(fcp->c_regbase + FCR_SRA, save);
1830  		break;
1831  	}
1832  	if (deselect)
1833  		/* reselect drive */
1834  		outb(fcp->c_regbase + FCR_DOR, fcp->c_digout);
1835  	return (retcode);
1836  }
1837  
1838  static int
1839  fdc_motorsm(struct fcu_obj *fjp, int input, int timeval)
1840  {
1841  	struct fdcntlr *fcp = fjp->fj_fdc;
1842  	int unit = fjp->fj_unit & 3;
1843  	int old_mstate;
1844  	int rval = 0;
1845  	uchar_t motorbit;
1846  
1847  	ASSERT(MUTEX_HELD(&fcp->c_dorlock));
1848  	old_mstate = fcp->c_mtrstate[unit];
1849  	encode(motor_onbits, unit, &motorbit);
1850  
1851  	switch (input) {
1852  	case FMI_TIMER:		/* timer expired */
1853  		fcp->c_motort[unit] = 0;
1854  		switch (old_mstate) {
1855  		case FMS_START:
1856  		case FMS_DELAY:
1857  			fcp->c_mtrstate[unit] = FMS_ON;
1858  			break;
1859  		case FMS_KILLST:
1860  			fcp->c_motort[unit] = timeout(fdmotort, (void *)fjp,
1861  			    drv_usectohz(1000000));
1862  			fcp->c_mtrstate[unit] = FMS_IDLE;
1863  			break;
1864  		case FMS_IDLE:
1865  			fcp->c_digout &= ~motorbit;
1866  			outb(fcp->c_regbase + FCR_DOR, fcp->c_digout);
1867  			fcp->c_mtrstate[unit] = FMS_OFF;
1868  			fjp->fj_flags &= ~FUNIT_3DMODE;
1869  			break;
1870  		case 86:
1871  			rval = -1;
1872  			break;
1873  		case FMS_OFF:
1874  		case FMS_ON:
1875  		default:
1876  			rval = -2;
1877  		}
1878  		break;
1879  
1880  	case FMI_STARTCMD:	/* start command */
1881  		switch (old_mstate) {
1882  		case FMS_IDLE:
1883  			fcp->c_mtrstate[unit] = 86;
1884  			mutex_exit(&fcp->c_dorlock);
1885  			(void) untimeout(fcp->c_motort[unit]);
1886  			mutex_enter(&fcp->c_dorlock);
1887  			fcp->c_motort[unit] = 0;
1888  			fcp->c_mtrstate[unit] = FMS_ON;
1889  			break;
1890  		case FMS_OFF:
1891  			fcp->c_digout |= motorbit;
1892  			outb(fcp->c_regbase + FCR_DOR, fcp->c_digout);
1893  
1894  			/* start motor_spinup_timer */
1895  			ASSERT(timeval > 0);
1896  			fcp->c_motort[unit] = timeout(fdmotort,  (void *)fjp,
1897  			    drv_usectohz(100000 * timeval));
1898  			/* FALLTHROUGH */
1899  		case FMS_KILLST:
1900  			fcp->c_mtrstate[unit] = FMS_START;
1901  			break;
1902  		default:
1903  			rval = -2;
1904  		}
1905  		break;
1906  
1907  	case FMI_RSTARTCMD:	/* restart command */
1908  		if (fcp->c_motort[unit] != 0) {
1909  			fcp->c_mtrstate[unit] = 86;
1910  			mutex_exit(&fcp->c_dorlock);
1911  			(void) untimeout(fcp->c_motort[unit]);
1912  			mutex_enter(&fcp->c_dorlock);
1913  		}
1914  		ASSERT(timeval > 0);
1915  		fcp->c_motort[unit] = timeout(fdmotort, (void *)fjp,
1916  		    drv_usectohz(100000 * timeval));
1917  		fcp->c_mtrstate[unit] = FMS_START;
1918  		break;
1919  
1920  	case FMI_DELAYCMD:	/* delay command */
1921  		if (fcp->c_motort[unit] == 0)
1922  			fcp->c_motort[unit] = timeout(fdmotort,  (void *)fjp,
1923  			    drv_usectohz(15000));
1924  		fcp->c_mtrstate[unit] = FMS_DELAY;
1925  		break;
1926  
1927  	case FMI_IDLECMD:	/* idle command */
1928  		switch (old_mstate) {
1929  		case FMS_DELAY:
1930  			fcp->c_mtrstate[unit] = 86;
1931  			mutex_exit(&fcp->c_dorlock);
1932  			(void) untimeout(fcp->c_motort[unit]);
1933  			mutex_enter(&fcp->c_dorlock);
1934  			/* FALLTHROUGH */
1935  		case FMS_ON:
1936  			ASSERT(timeval > 0);
1937  			fcp->c_motort[unit] = timeout(fdmotort, (void *)fjp,
1938  			    drv_usectohz(100000 * timeval));
1939  			fcp->c_mtrstate[unit] = FMS_IDLE;
1940  			break;
1941  		case FMS_START:
1942  			fcp->c_mtrstate[unit] = FMS_KILLST;
1943  			break;
1944  		default:
1945  			rval = -2;
1946  		}
1947  		break;
1948  
1949  	default:
1950  		rval = -3;
1951  	}
1952  	if (rval) {
1953  		FCERRPRINT(FDEP_L4, FDEM_EXEC, (CE_WARN,
1954  		    "fdc_motorsm: unit %d  bad input %d or bad state %d",
1955  		    (int)fjp->fj_unit, input, old_mstate));
1956  #if 0
1957  		cmn_err(CE_WARN,
1958  		    "fdc_motorsm: unit %d  bad input %d or bad state %d\n",
1959  		    (int)fjp->fj_unit, input, old_mstate);
1960  		fcp->c_mtrstate[unit] = FMS_OFF;
1961  		if (fcp->c_motort[unit] != 0) {
1962  			mutex_exit(&fcp->c_dorlock);
1963  			(void) untimeout(fcp->c_motort[unit]);
1964  			mutex_enter(&fcp->c_dorlock);
1965  			fcp->c_motort[unit] = 0;
1966  		}
1967  #endif
1968  	} else
1969  		FCERRPRINT(FDEP_L0, FDEM_EXEC,
1970  		    (CE_CONT, "fdc_motorsm unit %d: input %d,  %d -> %d\n",
1971  		    (int)fjp->fj_unit, input, old_mstate,
1972  		    fcp->c_mtrstate[unit]));
1973  	return (rval);
1974  }
1975  
1976  /*
1977   * fdmotort
1978   *	is called from timeout() when a motor timer has expired.
1979   */
1980  static void
1981  fdmotort(void *arg)
1982  {
1983  	struct fcu_obj *fjp = (struct fcu_obj *)arg;
1984  	struct fdcntlr *fcp = fjp->fj_fdc;
1985  	struct fdcsb *csb = &fcp->c_csb;
1986  	int unit = fjp->fj_unit & 3;
1987  	int mval;
1988  	int newxstate = 0;
1989  
1990  	mutex_enter(&fcp->c_dorlock);
1991  	mval = fdc_motorsm(fjp, FMI_TIMER, 0);
1992  	mutex_exit(&fcp->c_dorlock);
1993  	if (mval < 0)
1994  		return;
1995  
1996  	mutex_enter(&fcp->c_lock);
1997  
1998  	if ((fcp->c_flags & FCFLG_WAITING) &&
1999  	    fcp->c_mtrstate[unit] == FMS_ON &&
2000  	    (csb->csb_xstate == FXS_MTRON || csb->csb_xstate == FXS_HDST ||
2001  	    csb->csb_xstate == FXS_DKCHGX))
2002  		newxstate = fdc_statemach(fcp);
2003  		if (newxstate == -1) {
2004  			FCERRPRINT(FDEP_L3, FDEM_EXEC,
2005  			    (CE_WARN,
2006  			    "fdc_motort unit %d: motor ready but bad xstate",
2007  			    (int)fjp->fj_unit));
2008  			fcp->c_csb.csb_cmdstat = EIO;
2009  		}
2010  		if (newxstate == -1 || newxstate == FXS_END) {
2011  			fcp->c_flags ^= FCFLG_WAITING;
2012  			cv_signal(&fcp->c_iocv);
2013  		}
2014  	mutex_exit(&fcp->c_lock);
2015  }
2016  
2017  /*
2018   * DMA interrupt service routine
2019   *
2020   *	Called by EISA dma interrupt service routine when buffer chaining
2021   *	is required.
2022   */
2023  
2024  ddi_dma_cookie_t *
2025  fdc_dmae_isr(struct fdcntlr *fcp)
2026  {
2027  	struct fdcsb *csb = &fcp->c_csb;
2028  	off_t off;
2029  	size_t len;
2030  
2031  	if (csb->csb_dmahandle && !csb->csb_cmdstat) {
2032  		if (++csb->csb_dmacurrcookie < csb->csb_dmacookiecnt) {
2033  			ddi_dma_nextcookie(csb->csb_dmahandle,
2034  			    &csb->csb_dmacookie);
2035  			return (&csb->csb_dmacookie);
2036  		} else if (++csb->csb_dmacurrwin < csb->csb_dmawincnt) {
2037  			if (ddi_dma_getwin(csb->csb_dmahandle,
2038  			    csb->csb_dmacurrwin, &off, &len,
2039  			    &csb->csb_dmacookie,
2040  			    &csb->csb_dmacookiecnt) != DDI_SUCCESS) {
2041  				return (NULL);
2042  			}
2043  			csb->csb_dmacurrcookie = 0;
2044  			return (&csb->csb_dmacookie);
2045  		}
2046  	} else
2047  		cmn_err(CE_WARN, "fdc: unsolicited DMA interrupt\n");
2048  	return (NULL);
2049  }
2050  
2051  
2052  /*
2053   * returns:
2054   *	0 if all ok,
2055   *	ENXIO - diskette not in drive
2056   *	ETIMEDOUT - for immediate operations that timed out
2057   *	EBUSY - if stupid chip is locked busy???
2058   *	ENOEXEC - for timeout during sending cmds to chip
2059   *
2060   * to sleep: set sleep
2061   * to check for disk changed: set change
2062   */
2063  static int
2064  fdc_exec(struct fdcntlr *fcp, int sleep, int change)
2065  {
2066  	struct ddi_dmae_req dmaereq;
2067  	struct fcu_obj *fjp;
2068  	struct fdcsb *csb;
2069  	off_t off;
2070  	size_t len;
2071  	int unit;
2072  
2073  	mutex_enter(&fcp->c_lock);
2074  	FCERRPRINT(FDEP_L0, FDEM_EXEC,
2075  	    (CE_CONT, "fdc_exec: sleep %x change %x\n", sleep, change));
2076  	csb = &fcp->c_csb;
2077  	unit = csb->csb_drive;
2078  	fjp = fcp->c_unit[unit];
2079  
2080  	if (csb->csb_opflags & CSB_OFINRPT) {
2081  		if (*csb->csb_cmd == FO_RECAL)
2082  			csb->csb_npcyl = 0;
2083  		else if ((*csb->csb_cmd & ~FO_MFM) != FO_FRMT)
2084  			csb->csb_npcyl =
2085  			    csb->csb_cmd[2] * fjp->fj_chars->fdc_steps;
2086  		csb->csb_xstate = FXS_START;
2087  	} else
2088  		csb->csb_xstate = FXS_DOIT;
2089  	csb->csb_retrys = 0;
2090  	csb->csb_ourtrys = 0;
2091  
2092  	if (csb->csb_dmahandle) {
2093  		/* ensure that entire format xfer is in one cookie */
2094  		/*
2095  		 * The change from  ddi_dma_buf/addr_setup() to
2096  		 * ddi_dma_buf/addr_bind_handle() has already loaded
2097  		 * the first DMA window and cookie.
2098  		 */
2099  		if ((*csb->csb_cmd & ~FO_MFM) == FO_FRMT &&
2100  		    (4 * csb->csb_cmd[3]) != csb->csb_dmacookie.dmac_size) {
2101  			mutex_exit(&fcp->c_lock);
2102  			return (EINVAL);
2103  		}
2104  	}
2105  
2106  retry:
2107  	if (fcp->c_curunit != unit || !(fjp->fj_flags & FUNIT_CHAROK)) {
2108  		fcp->c_curunit = unit;
2109  		fjp->fj_flags |= FUNIT_CHAROK;
2110  		if (fjp->fj_chars->fdc_transfer_rate == 417) {
2111  			/* XXX hack for fdformat */
2112  			/* fjp->fj_chars->fdc_transfer_rate == 500;	*/
2113  			fjp->fj_attr->fda_rotatespd = 360;
2114  		}
2115  		if (fdcspecify(fcp, fjp->fj_chars->fdc_transfer_rate,
2116  		    fjp->fj_drive->fdd_steprate, 40))
2117  			cmn_err(CE_WARN,
2118  			    "fdc_select: controller setup rejected "
2119  			    "fdcntrl %p transfer rate %x step rate %x "
2120  			    "head load time 40\n", (void*)fcp,
2121  			    fjp->fj_chars->fdc_transfer_rate,
2122  			    fjp->fj_drive->fdd_steprate);
2123  
2124  		mutex_enter(&fcp->c_dorlock);
2125  		if (fdcspdchange(fcp, fjp, fjp->fj_attr->fda_rotatespd)) {
2126  			/* 3D drive requires 500 ms for speed change */
2127  			(void) fdc_motorsm(fjp, FMI_RSTARTCMD, 5);
2128  			/*
2129  			 * Return value ignored - fdcmotort deals with failure.
2130  			 */
2131  		}
2132  		mutex_exit(&fcp->c_dorlock);
2133  	}
2134  
2135  	/*
2136  	 * If checking for disk_change is enabled
2137  	 * (i.e. not seeking in fdresetchng),
2138  	 * we sample the DSKCHG line to see if the diskette has wandered away.
2139  	 */
2140  	if (change && fdcsense_chng(fcp, unit)) {
2141  		FCERRPRINT(FDEP_L3, FDEM_EXEC,
2142  		    (CE_WARN, "diskette %d changed!!!", csb->csb_drive));
2143  		fcp->c_unit[unit]->fj_flags |= FUNIT_CHANGED;
2144  		/*
2145  		 * If the diskette is still gone... so are we, adios!
2146  		 */
2147  		if (fdcheckdisk(fcp, unit)) {
2148  			mutex_exit(&fcp->c_lock);
2149  
2150  			/* VP/ix expects an EBUSY return here */
2151  			if (*csb->csb_cmd == FO_SDRV) {
2152  				return (EBUSY);
2153  			}
2154  			return (ENXIO);
2155  		}
2156  		/*
2157  		 * delay to ensure that new diskette is up to speed
2158  		 */
2159  		mutex_enter(&fcp->c_dorlock);
2160  		(void) fdc_motorsm(fjp, FMI_RSTARTCMD,
2161  			fjp->fj_drive->fdd_motoron);
2162  		/*
2163  		 * Return value ignored - fdcmotort deals with failure.
2164  		 */
2165  		mutex_exit(&fcp->c_dorlock);
2166  	}
2167  
2168  	/*
2169  	 * gather some statistics
2170  	 */
2171  	switch (csb->csb_cmd[0] & 0x1f) {
2172  	case FO_RDDAT:
2173  		fcp->fdstats.rd++;
2174  		break;
2175  	case FO_WRDAT:
2176  		fcp->fdstats.wr++;
2177  		break;
2178  	case FO_RECAL:
2179  		fcp->fdstats.recal++;
2180  		break;
2181  	case FO_FRMT:
2182  		fcp->fdstats.form++;
2183  		break;
2184  	default:
2185  		fcp->fdstats.other++;
2186  		break;
2187  	}
2188  
2189  	bzero(csb->csb_rslt, 10);
2190  	csb->csb_cmdstat = 0;
2191  
2192  	if (csb->csb_dmahandle) {
2193  		bzero(&dmaereq, sizeof (struct ddi_dmae_req));
2194  		dmaereq.der_command = (csb->csb_opflags & CSB_OFDMAWT) ?
2195  		    DMAE_CMD_WRITE : DMAE_CMD_READ;
2196  		/*
2197  		 * setup for dma buffer chaining regardless of bus capability
2198  		 */
2199  		dmaereq.der_bufprocess = DMAE_BUF_CHAIN;
2200  		dmaereq.proc = fdc_dmae_isr;
2201  		dmaereq.procparms = (void *)fcp;
2202  		if (ddi_dmae_prog(fcp->c_dip, &dmaereq, &csb->csb_dmacookie,
2203  		    fcp->c_dmachan) != DDI_SUCCESS)
2204  			cmn_err(CE_WARN, "fdc_exec: dmae prog failed, "
2205  				"dip %p, dmachan %x\n",
2206  				(void*)fcp->c_dip, fcp->c_dmachan);
2207  	}
2208  
2209  	if ((fdc_statemach(fcp) == FXS_DOWT) && !sleep) {
2210  		/*
2211  		 * If the operation has no results - then just return
2212  		 */
2213  		if (!csb->csb_nrslts) {
2214  			mutex_exit(&fcp->c_lock);
2215  			return (0);
2216  		}
2217  		/*
2218  		 * this operation has no interrupt and an immediate result
2219  		 * so wait for the results and stuff them into the csb
2220  		 */
2221  		if (fdc_statemach(fcp) == -1) {
2222  			mutex_exit(&fcp->c_lock);
2223  			return (EIO);
2224  		}
2225  	} else {
2226  		fcp->c_flags |= FCFLG_WAITING;
2227  		/*
2228  		 * wait for completion interrupt
2229  		 */
2230  		while (fcp->c_flags & FCFLG_WAITING) {
2231  			cv_wait(&fcp->c_iocv, &fcp->c_lock);
2232  		}
2233  	}
2234  
2235  	/*
2236  	 * See if there was an error detected, if so, fdrecover()
2237  	 * will check it out and say what to do.
2238  	 *
2239  	 * Don't do this, though, if this was the Sense Drive Status
2240  	 * or the Dump Registers command.
2241  	 */
2242  	if (csb->csb_cmdstat && *csb->csb_cmd != FO_SDRV) {
2243  		/* if it can restarted OK, then do so, else return error */
2244  		if (fdrecover(fcp)) {
2245  			mutex_exit(&fcp->c_lock);
2246  			return (EIO);
2247  		}
2248  		/* ASSUMES that cmd is still intact in csb */
2249  		if (csb->csb_xstate == FXS_END)
2250  			csb->csb_xstate = FXS_START;
2251  		if (fdc_dma_attr.dma_attr_sgllen > 1 && csb->csb_dmahandle) {
2252  			/*
2253  			 * restarted read/write operation requires
2254  			 * first DMA cookie of current window
2255  			 */
2256  			if (ddi_dma_getwin(csb->csb_dmahandle,
2257  			    csb->csb_dmacurrwin, &off, &len,
2258  			    &csb->csb_dmacookie,
2259  			    &csb->csb_dmacookiecnt) != DDI_SUCCESS) {
2260  
2261  				mutex_exit(&fcp->c_lock);
2262  				return (EIO);
2263  			}
2264  			csb->csb_dmacurrcookie = 0;
2265  		}
2266  		goto retry;
2267  	}
2268  	/* things went ok */
2269  	mutex_exit(&fcp->c_lock);
2270  	return (0);
2271  }
2272  
2273  /*
2274   * fdcheckdisk
2275   *	called by fdc_exec to check if the disk is still there - do a seek
2276   *	then see if DSKCHG line went away; if so, diskette is in; else
2277   *	it's (still) out.
2278   */
2279  int
2280  fdcheckdisk(struct fdcntlr *fcp, int unit)
2281  {
2282  	struct fdcsb *csb = &fcp->c_csb;
2283  	int newcyl;			/* where to seek for reset of DSKCHG */
2284  	int rval;
2285  	enum fxstate save_xstate;
2286  	uchar_t save_cmd, save_cd1, save_npcyl;
2287  
2288  	ASSERT(MUTEX_HELD(&fcp->c_lock));
2289  	FCERRPRINT(FDEP_L1, FDEM_CHEK,
2290  	    (CE_CONT, "fdcheckdisk unit %d\n", unit));
2291  
2292  	if (fcp->c_curpcyl[unit])
2293  		newcyl = fcp->c_curpcyl[unit] - 1;
2294  	else
2295  		newcyl = 1;
2296  
2297  	save_cmd = *csb->csb_cmd;
2298  	save_cd1 = csb->csb_cmd[1];
2299  	save_npcyl = csb->csb_npcyl;
2300  	save_xstate = csb->csb_xstate;
2301  
2302  	*csb->csb_cmd = FO_SEEK;
2303  	csb->csb_cmd[1] = (uchar_t)unit;
2304  	csb->csb_npcyl = (uchar_t)newcyl;
2305  	fcp->c_flags |= FCFLG_WAITING;
2306  
2307  	if (fcp->c_mtrstate[unit] != FMS_ON && fcp->c_motort[unit] != 0)
2308  		/*
2309  		 * wait for motor to get up to speed,
2310  		 * and let motor_timer issue seek cmd
2311  		 */
2312  		csb->csb_xstate = FXS_DKCHGX;
2313  	else {
2314  		/*
2315  		 * motor is up to speed; issue seek cmd now
2316  		 */
2317  		csb->csb_xstate = FXS_SEEK;
2318  		if (rval = fdcseek(fcp, unit, newcyl)) {
2319  			/*
2320  			 * any recal/seek errors are too serious to attend to
2321  			 */
2322  			FCERRPRINT(FDEP_L3, FDEM_CHEK,
2323  			    (CE_WARN, "fdcheckdisk err %d", rval));
2324  			fcp->c_flags ^= FCFLG_WAITING;
2325  		}
2326  	}
2327  	/*
2328  	 * wait for completion interrupt
2329  	 * XXX This should be backed up with a watchdog timer!
2330  	 */
2331  	while (fcp->c_flags & FCFLG_WAITING) {
2332  		cv_wait(&fcp->c_iocv, &fcp->c_lock);
2333  	}
2334  
2335  	/*
2336  	 * if disk change still asserted, no diskette in drive!
2337  	 */
2338  	if (rval = fdcsense_chng(fcp, unit)) {
2339  		FCERRPRINT(FDEP_L3, FDEM_CHEK,
2340  		    (CE_WARN, "fdcheckdisk no disk %d", unit));
2341  	}
2342  
2343  	*csb->csb_cmd = save_cmd;
2344  	csb->csb_cmd[1] = save_cd1;
2345  	csb->csb_npcyl = save_npcyl;
2346  	csb->csb_xstate = save_xstate;
2347  	return (rval);
2348  }
2349  
2350  static int
2351  fdrecover(struct fdcntlr *fcp)
2352  {
2353  	struct fcu_obj *fjp;
2354  	struct fdcsb *csb = &fcp->c_csb;
2355  	int residual;
2356  	int unit;
2357  	char *failure;
2358  
2359  	FCERRPRINT(FDEP_L2, FDEM_RECO,
2360  	    (CE_NOTE, "fdrecover unit %d", csb->csb_drive));
2361  
2362  	unit = csb->csb_drive;
2363  	fjp = fcp->c_unit[unit];
2364  	if (fcp->c_flags & FCFLG_TIMEOUT) {
2365  		fcp->c_flags ^= FCFLG_TIMEOUT;
2366  		csb->csb_rslt[1] |= 0x08;
2367  		FCERRPRINT(FDEP_L3, FDEM_RECO,
2368  		    (CE_WARN, "fd unit %d: %s timed out", csb->csb_drive,
2369  		    fdcmds[*csb->csb_cmd & 0x1f].cmdname));
2370  	}
2371  
2372  	if (csb->csb_status & S0_SEKEND)
2373  		fcp->c_curpcyl[unit] = -1;
2374  
2375  	switch (csb->csb_oldxs) {
2376  	case FXS_RCAL:		/* recalibrate */
2377  	case FXS_SEEK:		/* seek */
2378  	case FXS_RESET:		/* cntlr reset */
2379  		FCERRPRINT(FDEP_L4, FDEM_RECO, (CE_WARN,
2380  		    "fd unit %d: %s error: st0=0x%x pcn=%d", csb->csb_drive,
2381  		    fdcmds[*csb->csb_cmd & 0x1f].cmdname,
2382  		    *csb->csb_rslt, csb->csb_rslt[1]));
2383  		if (csb->csb_retrys++ < skretry &&
2384  		    !(csb->csb_opflags & CSB_OFRAWIOCTL))
2385  			return (0);
2386  		break;
2387  
2388  	case FXS_RDID:		/* read ID */
2389  		if (!(csb->csb_status & S0_SEKEND))
2390  			csb->csb_xstate = FXS_HDST;
2391  		/* FALLTHROUGH */
2392  	case FXS_DOIT:		/* original operation */
2393  	case FXS_DOWT:		/* waiting on operation */
2394  		if (csb->csb_opflags & (CSB_OFDMARD | CSB_OFDMAWT)) {
2395  			if (ddi_dmae_getcnt(fcp->c_dip, fcp->c_dmachan,
2396  			    &residual) != DDI_SUCCESS)
2397  				cmn_err(CE_WARN,
2398  				    "fdc_recover: dmae getcnt failed, "
2399  				    "dip %p dmachan %x residual %x\n",
2400  				    (void*)fcp->c_dip, fcp->c_dmachan,
2401  				    residual);
2402  			FCERRPRINT(FDEP_L2, FDEM_RECO,
2403  	(CE_NOTE, "fd unit %d: %s error: dma count=0x%lx residual=0x%x",
2404  			    csb->csb_drive,
2405  			    fdcmds[*csb->csb_cmd & 0x1f].cmdname,
2406  			    csb->csb_dmacookie.dmac_size, residual));
2407  		}
2408  		if (csb->csb_rslt[1] == S1_OVRUN)
2409  			/*
2410  			 * handle retries of over/underrun
2411  			 * with a secondary retry counter
2412  			 */
2413  			if (++csb->csb_ourtrys <= OURUN_TRIES) {
2414  				FCERRPRINT(FDEP_L2, FDEM_RECO,
2415  (CE_NOTE, "fd unit %d: %s error: over/under-run",
2416  csb->csb_drive, fdcmds[*csb->csb_cmd & 0x1f].cmdname));
2417  				return (0);
2418  			} else
2419  				/*
2420  				 * count 1 set of over/underruns
2421  				 * as 1 primary retry effort
2422  				 */
2423  				csb->csb_ourtrys = 0;
2424  
2425  		if ((fjp->fj_flags & (FUNIT_UNLABELED | FUNIT_LABELOK)) &&
2426  		    !(csb->csb_opflags & CSB_OFRAWIOCTL)) {
2427  			/*
2428  			 * device is open so keep trying and
2429  			 * gather statistics on errors
2430  			 */
2431  			if (csb->csb_rslt[1] & S1_CRCER)
2432  				fcp->fdstats.de++;
2433  			if (csb->csb_rslt[1] & S1_OVRUN)
2434  				fcp->fdstats.run++;
2435  			if (csb->csb_rslt[1] & (S1_NODATA | S1_MADMK))
2436  				fcp->fdstats.bfmt++;
2437  			if (csb->csb_rslt[1] & 0x08)
2438  				fcp->fdstats.to++;
2439  
2440  			/*
2441  			 * if we have not run out of retries, return 0
2442  			 */
2443  			if (csb->csb_retrys++ < csb->csb_maxretry &&
2444  			    (*csb->csb_cmd & ~FO_MFM) != FO_FRMT) {
2445  				if (csb->csb_opflags &
2446  				    (CSB_OFDMARD | CSB_OFDMAWT)) {
2447  					FCERRPRINT(FDEP_L4, FDEM_RECO,
2448  (CE_WARN, "fd unit %d: %s error: st0=0x%x st1=0x%x st2=0x%x",
2449  					    csb->csb_drive,
2450  fdcmds[*csb->csb_cmd & 0x1f].cmdname,
2451  					    *csb->csb_rslt, csb->csb_rslt[1],
2452  					    csb->csb_rslt[2]));
2453  				}
2454  				if ((csb->csb_retrys & 1) &&
2455  				    csb->csb_xstate == FXS_END)
2456  					csb->csb_xstate = FXS_DOIT;
2457  				else if (csb->csb_retrys == 3)
2458  					csb->csb_xstate = FXS_RESTART;
2459  				return (0);
2460  			}
2461  			if (csb->csb_rslt[1] & S1_CRCER)
2462  				failure = "crc error";
2463  			else if (csb->csb_rslt[1] & S1_OVRUN)
2464  				failure = "over/under-run";
2465  			else if (csb->csb_rslt[1] & (S1_NODATA | S1_MADMK))
2466  				failure = "bad format";
2467  			else if (csb->csb_rslt[1] & 0x08)
2468  				failure = "timeout";
2469  			else
2470  				failure = "failed";
2471  			cmn_err(CE_NOTE, "!fd unit %d: %s %s (%x %x %x)",
2472  			    csb->csb_drive,
2473  			    fdcmds[*csb->csb_cmd & 0x1f].cmdname, failure,
2474  			    *csb->csb_rslt, csb->csb_rslt[1], csb->csb_rslt[2]);
2475  		} else {
2476  			FCERRPRINT(FDEP_L2, FDEM_RECO,
2477  			    (CE_NOTE, "fd unit %d: %s failed (%x %x %x)",
2478  			    csb->csb_drive,
2479  			    fdcmds[*csb->csb_cmd & 0x1f].cmdname,
2480  			    *csb->csb_rslt, csb->csb_rslt[1],
2481  			    csb->csb_rslt[2]));
2482  		}
2483  		break;
2484  
2485  	default:
2486  		FCERRPRINT(FDEP_L4, FDEM_RECO, (CE_WARN,
2487  		    "fd unit %d: %s failed: st0=0x%x st1=0x%x st2=0x%x",
2488  		    csb->csb_drive, fdcmds[*csb->csb_cmd & 0x1f].cmdname,
2489  		    *csb->csb_rslt, csb->csb_rslt[1], csb->csb_rslt[2]));
2490  		break;
2491  	}
2492  	return (1);
2493  }
2494  
2495  
2496  /*	Autovector Interrupt Entry Point	*/
2497  /* ARGSUSED */
2498  static uint_t
2499  fdc_intr(caddr_t arg)
2500  {
2501  	struct fdcntlr *fcp = (struct fdcntlr *)arg;
2502  	struct fdcsb *csb;
2503  	off_t off;
2504  	size_t blklen;
2505  	int drive;
2506  	int newstate;
2507  	int pendstate;
2508  	int rval = DDI_DMA_DONE;
2509  	int state;
2510  	int maxspin = 10;
2511  
2512  	csb = &fcp->c_csb;
2513  
2514  	mutex_enter(&fcp->c_lock);
2515  	/*
2516  	 * Wait for the RQM bit to be set, or until we've tested it
2517  	 * a bunch of times (which may imply this isn't our interrupt).
2518  	 */
2519  	state = inb(fcp->c_regbase + FCR_MSR);
2520  	pendstate = state & (MS_RQM | MS_DIO | MS_CB);
2521  	while (((pendstate & MS_RQM) == 0) && (maxspin-- > 0)) {
2522  		/* Small pause in between reading the status port */
2523  		drv_usecwait(10);
2524  		/* Reread the status port */
2525  		state = inb(fcp->c_regbase + FCR_MSR);
2526  		pendstate = state & (MS_RQM | MS_DIO | MS_CB);
2527  	}
2528  	FCERRPRINT(FDEP_L0, FDEM_INTR,
2529  	    (CE_CONT, "fdc_intr unit %d: xstate=%d MSR=0x%x\n",
2530  	    csb->csb_drive, csb->csb_xstate, state));
2531  
2532  	/*
2533  	 * If there is an operation outstanding AND the controller is ready
2534  	 * to receive a command or send us the result of a command (OR if the
2535  	 * controller is ready to accept a new command), AND if
2536  	 * someone has been waiting for a command to finish AND (if no unit
2537  	 * is BUSY OR if the unit that we're waiting for is BUSY (i.e. it's in
2538  	 * the middle of a seek/recalibrate)) then this interrupt is for us.
2539  	 */
2540  	if ((pendstate == (MS_RQM | MS_DIO | MS_CB) || pendstate == MS_RQM) &&
2541  	    (fcp->c_flags & FCFLG_WAITING) &&
2542  	    (!(state & 0x0f) || ((1 << csb->csb_drive) & state))) {
2543  		/*
2544  		 * Remove one of the conditions for entering this code.
2545  		 * The state_machine will release the c_lock if it
2546  		 * calls untimeout()
2547  		 */
2548  		fcp->c_flags ^= FCFLG_WAITING;
2549  
2550  		if ((newstate = fdc_statemach(fcp)) == -1) {
2551  			/* restore waiting flag */
2552  			fcp->c_flags |= FCFLG_WAITING;
2553  			mutex_exit(&fcp->c_lock);
2554  			return (DDI_INTR_CLAIMED);
2555  		}
2556  
2557  		if (fcp->c_intrstat)
2558  			KIOIP->intrs[KSTAT_INTR_HARD]++;
2559  		if (newstate == FXS_END) {
2560  
2561  			if (csb->csb_dmahandle && !csb->csb_cmdstat &&
2562  				/*
2563  				 * read/write operation may have multiple DMA
2564  				 * cookies: process next one
2565  				 */
2566  			    ((csb->csb_dmacurrcookie <
2567  			    (csb->csb_dmacookiecnt - 1)) ||
2568  			    (csb->csb_dmacurrwin) < (csb->csb_dmawincnt - 1))) {
2569  				/*
2570  				 * read/write operation requires another
2571  				 * DMA cookie: process next one
2572  				 */
2573  
2574  				if (++csb->csb_dmacurrcookie <
2575  				    csb->csb_dmacookiecnt) {
2576  					ddi_dma_nextcookie(csb->csb_dmahandle,
2577  					    &csb->csb_dmacookie);
2578  				} else if (++csb->csb_dmacurrwin <
2579  				    csb->csb_dmawincnt) {
2580  					if (ddi_dma_getwin(csb->csb_dmahandle,
2581  					    csb->csb_dmacurrwin, &off, &blklen,
2582  					    &csb->csb_dmacookie,
2583  					    &csb->csb_dmacookiecnt) !=
2584  					    DDI_SUCCESS) {
2585  						cmn_err(CE_WARN,
2586  						    "fdc_intr: "
2587  						    "dma getwin failed\n");
2588  					}
2589  					csb->csb_dmacurrcookie = 0;
2590  				}
2591  
2592  				if (ddi_dmae_prog(fcp->c_dip, NULL,
2593  				    &csb->csb_dmacookie, fcp->c_dmachan) !=
2594  				    DDI_SUCCESS)
2595  					cmn_err(CE_WARN,
2596  					    "fdc_intr: dmae prog failed, "
2597  					    "dip %p dmachannel %x\n",
2598  					    (void*)fcp->c_dip,
2599  					    fcp->c_dmachan);
2600  
2601  				/*
2602  				 * status of last operation has disk
2603  				 * address for continuation
2604  				 */
2605  				csb->csb_cmd[2] = csb->csb_rslt[3];
2606  				csb->csb_cmd[3] = csb->csb_rslt[4];
2607  				csb->csb_cmd[4] = csb->csb_rslt[5];
2608  				csb->csb_cmd[1] = (csb->csb_cmd[1] & ~0x04) |
2609  				    (csb->csb_cmd[3] << 2);
2610  
2611  				csb->csb_xstate = FXS_START;
2612  				(void) fdc_statemach(fcp);
2613  				/*
2614  				 * Ignored return.  If failed, warning already
2615  				 * posted.  Returned state irrelevant.
2616  				 */
2617  				/* restore waiting flag */
2618  				fcp->c_flags |= FCFLG_WAITING;
2619  				goto fi_exit;
2620  			}
2621  			if (rval != DDI_DMA_DONE)
2622  				csb->csb_cmdstat = EIO;
2623  			/*
2624  			 * somebody's waiting for completion of fdcntlr/csb,
2625  			 * wake them
2626  			 */
2627  			cv_signal(&fcp->c_iocv);
2628  		}
2629  		else
2630  			/* restore waiting flag */
2631  			fcp->c_flags |= FCFLG_WAITING;
2632  fi_exit:
2633  		mutex_exit(&fcp->c_lock);
2634  		return (DDI_INTR_CLAIMED);
2635  	}
2636  
2637  	if (state & MS_RQM) {
2638  		(void) fdcsense_int(fcp, &drive, NULL);
2639  		/*
2640  		 * Ignored return - senser state already saved
2641  		 */
2642  		FCERRPRINT(FDEP_L4, FDEM_INTR,
2643  		    (CE_WARN, "fdc_intr unit %d: nobody sleeping 0x%x",
2644  		    drive, state));
2645  	} else {
2646  		FCERRPRINT(FDEP_L4, FDEM_INTR,
2647  		    (CE_WARN, "fdc_intr: nobody sleeping on %d 0x%x",
2648  		    csb->csb_drive, state));
2649  	}
2650  	/*
2651  	 * This should probably be protected, but, what the
2652  	 * heck...the cost isn't worth the accuracy for this
2653  	 * statistic.
2654  	 */
2655  	if (fcp->c_intrstat)
2656  		KIOIP->intrs[KSTAT_INTR_SPURIOUS]++;
2657  	mutex_exit(&fcp->c_lock);
2658  	return (DDI_INTR_UNCLAIMED);
2659  }
2660  
2661  /*
2662   * fdwatch
2663   *	is called from timeout() when a floppy operation timer has expired.
2664   */
2665  static void
2666  fdwatch(void *arg)
2667  {
2668  	struct fdcntlr *fcp = (struct fdcntlr *)arg;
2669  	struct fdcsb *csb;
2670  
2671  	mutex_enter(&fcp->c_lock);
2672  
2673  	if (fcp->c_timeid == 0) {
2674  		/*
2675  		 * fdc_intr got here first, ergo, no timeout condition..
2676  		 */
2677  		mutex_exit(&fcp->c_lock);
2678  		return;
2679  	}
2680  
2681  	if (fcp->c_flags & FCFLG_WAITING) {
2682  		if (ddi_dmae_stop(fcp->c_dip, fcp->c_dmachan) != DDI_SUCCESS)
2683  			cmn_err(CE_WARN, "fdwatch: dmae stop failed, "
2684  				"dip %p, dmachan %x\n",
2685  				(void*)fcp->c_dip, fcp->c_dmachan);
2686  		csb = &fcp->c_csb;
2687  		FCERRPRINT(FDEP_L3, FDEM_WATC,
2688  		    (CE_WARN, "fdcwatch unit %d: xstate = %d",
2689  		    csb->csb_drive, csb->csb_xstate));
2690  		drv_usecwait(50);
2691  
2692  		if (inb(fcp->c_regbase + FCR_MSR) != MS_RQM) {
2693  			/*
2694  			 * cntlr is still busy, so reset it
2695  			 */
2696  			csb->csb_xstate = FXS_KILL;
2697  			(void) fdc_statemach(fcp);
2698  			/*
2699  			 * Ignored return.  If failed, warning already
2700  			 * posted.  Returned state irrelevant.
2701  			 */
2702  		} else {
2703  			csb->csb_xstate = FXS_END;
2704  			fcp->c_timeid = 0;
2705  			fcp->c_flags ^= FCFLG_WAITING;
2706  			cv_signal(&fcp->c_iocv);
2707  		}
2708  		csb->csb_cmdstat = EIO;
2709  		fcp->c_flags |= FCFLG_TIMEOUT;
2710  	} else {
2711  		FCERRPRINT(FDEP_L4, FDEM_INTR,
2712  		    (CE_WARN, "fdcwatch: not sleeping for unit %d",
2713  		    fcp->c_csb.csb_drive));
2714  	}
2715  	if (fcp->c_intrstat)
2716  		KIOIP->intrs[KSTAT_INTR_WATCHDOG]++;
2717  	mutex_exit(&fcp->c_lock);
2718  }
2719  
2720  
2721  static int
2722  fdc_statemach(struct fdcntlr *fcp)
2723  {
2724  	struct fcu_obj *fjp;
2725  	struct fdcsb *csb = &fcp->c_csb;
2726  	int backoff;
2727  	clock_t time;
2728  	int unit;
2729  
2730  	ASSERT(MUTEX_HELD(&fcp->c_lock));
2731  
2732  	unit = csb->csb_drive;
2733  	fjp = fcp->c_unit[unit];
2734  
2735  	csb->csb_oldxs = csb->csb_xstate;
2736  	switch (csb->csb_xstate) {
2737  
2738  	case FXS_START:		/* start of operation */
2739  		ASSERT(fcp->c_timeid == 0);
2740  		time = drv_usectohz(100000 * (unsigned int)csb->csb_timer);
2741  		if (time == 0)
2742  			time = drv_usectohz(2000000);
2743  		fcp->c_timeid = timeout(fdwatch, (void *)fcp, time);
2744  
2745  		if (fcp->c_mtrstate[unit] == FMS_START) {
2746  			/*
2747  			 * wait for motor to get up to speed
2748  			 */
2749  			csb->csb_xstate = FXS_MTRON;
2750  			break;
2751  		}
2752  		/* FALLTHROUGH */
2753  
2754  	case FXS_MTRON:		/* motor is at speed */
2755  		if (fcp->c_mtrstate[unit] != FMS_ON) {
2756  			/* how did we get here ?? */
2757  			cmn_err(CE_WARN, "fdc: selected but motor off");
2758  			return (-1);
2759  		}
2760  		if (fcp->c_curpcyl[unit] != -1 && *csb->csb_cmd != FO_RECAL)
2761  			goto nxs_seek;
2762  		recalcmd[1] = (uchar_t)unit;
2763  		if (fdc_docmd(fcp, recalcmd, 2) == -1) {
2764  			/* cntlr did not accept command bytes */
2765  			fdcquiesce(fcp);
2766  			csb->csb_cmdstat = EIO;
2767  			csb->csb_xstate = FXS_RESET;
2768  			break;
2769  		}
2770  		fcp->c_sekdir[unit] = 0;
2771  		csb->csb_xstate = FXS_RCAL;
2772  		break;
2773  
2774  	case FXS_RCAL:		/* forced recalibrate is complete */
2775  #if 0	/* #ifdef _VPIX */
2776  	/* WARNING: this code breaks SPARC compatibility */
2777  		if (csb->csb_opflags & CSB_OFRAWIOCTL &&
2778  		    *csb->csb_cmd == FO_RECAL) {
2779  			fcp->c_curpcyl[unit] = 0;
2780  			csb->csb_status = 0;
2781  			goto nxs_cmpl;
2782  		}
2783  #endif
2784  		(void) fdc_docmd(fcp, &senseintcmd, 1);
2785  		/*
2786  		 * Ignored return. If failed, warning was issued by fdc_docmd.
2787  		 * fdc_results retrieves the controller/drive status
2788  		 */
2789  		(void) fdc_result(fcp, csb->csb_rslt, 2);
2790  		/*
2791  		 * Ignored return. If failed, warning was issued by fdc_result.
2792  		 * Actual results checked below
2793  		 */
2794  		if ((csb->csb_status = ((*csb->csb_rslt ^ S0_SEKEND) &
2795  		    (S0_ICMASK | S0_SEKEND | S0_ECHK | S0_NOTRDY))) != 0) {
2796  			FCERRPRINT(FDEP_L3, FDEM_EXEC,
2797  			    (CE_WARN, "fdc_statemach unit %d: recal result %x",
2798  			    csb->csb_drive, *csb->csb_rslt));
2799  			fdcquiesce(fcp);
2800  			csb->csb_cmdstat = EIO;
2801  			csb->csb_xstate = FXS_RESET;
2802  			break;
2803  		}
2804  		if (unit != (*csb->csb_rslt & 3) || csb->csb_rslt[1]) {
2805  			csb->csb_status = S0_SEKEND;
2806  			goto nxs_cmpl;
2807  		}
2808  		fcp->c_curpcyl[unit] = csb->csb_rslt[1];
2809  		if (*csb->csb_cmd == FO_RECAL)
2810  			goto nxs_cmpl;
2811  nxs_seek:
2812  		if (*csb->csb_cmd != FO_SEEK &&
2813  		    csb->csb_npcyl == fcp->c_curpcyl[unit])
2814  			goto nxs_doit;
2815  		fcp->c_sekdir[unit] = csb->csb_npcyl - fcp->c_curpcyl[unit];
2816  		/* FALLTHROUGH */
2817  
2818  	case FXS_DKCHGX:	/* reset Disk-Change latch */
2819  		(void) fdcseek(fcp, csb->csb_cmd[1], csb->csb_npcyl);
2820  		/*
2821  		 * Ignored return.  If command rejected, warnig already posted
2822  		 * by fdc_docmd().
2823  		 */
2824  		csb->csb_xstate = FXS_SEEK;
2825  		break;
2826  
2827  	case FXS_RESTART:	/* special restart of read/write operation */
2828  		ASSERT(fcp->c_timeid == 0);
2829  		time = drv_usectohz(100000 * csb->csb_timer);
2830  		if (time == 0)
2831  			time = drv_usectohz(2000000);
2832  		fcp->c_timeid = timeout(fdwatch, (void *)fcp, time);
2833  
2834  		if (fcp->c_mtrstate[unit] != FMS_ON) {
2835  			cmn_err(CE_WARN, "fdc: selected but motor off");
2836  			return (-1);
2837  		}
2838  		if ((csb->csb_npcyl == 0 || fcp->c_sekdir[unit] >= 0) &&
2839  		    (int)csb->csb_cmd[2] < (fjp->fj_chars->fdc_ncyl - 1))
2840  			backoff = csb->csb_npcyl + 1;
2841  		else
2842  			backoff = csb->csb_npcyl - 1;
2843  		(void) fdcseek(fcp, csb->csb_cmd[1], backoff);
2844  		/*
2845  		 * Ignored return.  If command rejected, warnig already posted
2846  		 * by fdc_docmd().
2847  		 */
2848  		csb->csb_xstate = FXS_RESEEK;
2849  		break;
2850  
2851  	case FXS_RESEEK:	/* seek to backoff-cyl complete */
2852  		(void) fdc_docmd(fcp, &senseintcmd, 1);
2853  		/*
2854  		 * Ignored return. If failed, warning was issued by fdc_docmd.
2855  		 * fdc_results retrieves the controller/drive status
2856  		 */
2857  		(void) fdc_result(fcp, csb->csb_rslt, 2);
2858  		/*
2859  		 * Ignored return. If failed, warning was issued by fdc_result.
2860  		 * Actual results checked below
2861  		 */
2862  		if ((csb->csb_status = ((*csb->csb_rslt ^ S0_SEKEND) &
2863  		    (S0_ICMASK | S0_SEKEND | S0_ECHK | S0_NOTRDY))) != 0)
2864  			goto nxs_cmpl;
2865  		(void) fdcseek(fcp, csb->csb_cmd[1], csb->csb_npcyl);
2866  		/*
2867  		 * Ignored return.  If command rejected, warnig already posted
2868  		 * by fdc_docmd().
2869  		 */
2870  		csb->csb_xstate = FXS_SEEK;
2871  		break;
2872  
2873  	case FXS_SEEK:		/* seek complete */
2874  #if 0	/* #ifdef _VPIX */
2875  	/* WARNING: this code breaks SPARC compatibility and */
2876  	/* rawioctls in fdformat */
2877  		if (csb->csb_opflags & CSB_OFRAWIOCTL) {
2878  			fcp->c_curpcyl[unit] = csb->csb_npcyl;
2879  			csb->csb_status = 0;
2880  			goto nxs_cmpl;
2881  		}
2882  #endif
2883  		(void) fdc_docmd(fcp, &senseintcmd, 1);
2884  		/*
2885  		 * Ignored return. If failed, warning was issued by fdc_docmd.
2886  		 * fdc_results retrieves the controller/drive status
2887  		 */
2888  		(void) fdc_result(fcp, csb->csb_rslt, 2);
2889  		/*
2890  		 * Ignored return. If failed, warning was issued by fdc_result.
2891  		 * Actual results checked below
2892  		 */
2893  		if ((csb->csb_status = ((*csb->csb_rslt ^ S0_SEKEND) &
2894  		    (S0_ICMASK | S0_SEKEND | S0_ECHK | S0_NOTRDY))) != 0)
2895  			goto nxs_cmpl;
2896  		if (unit != (*csb->csb_rslt & 3) ||
2897  		    csb->csb_rslt[1] != csb->csb_npcyl) {
2898  			csb->csb_status = S0_SEKEND;
2899  			goto nxs_cmpl;
2900  		};
2901  		fcp->c_curpcyl[unit] = csb->csb_rslt[1];
2902  		/* use motor_timer to delay for head settle */
2903  		mutex_enter(&fcp->c_dorlock);
2904  		(void) fdc_motorsm(fjp, FMI_DELAYCMD,
2905  		    fjp->fj_drive->fdd_headsettle / 1000);
2906  		/*
2907  		 * Return value ignored - fdcmotort deals with failure.
2908  		 */
2909  		mutex_exit(&fcp->c_dorlock);
2910  		csb->csb_xstate = FXS_HDST;
2911  		break;
2912  
2913  	case FXS_HDST:		/* head settle */
2914  		if (*csb->csb_cmd == FO_SEEK)
2915  			goto nxs_cmpl;
2916  		if ((*csb->csb_cmd & ~FO_MFM) == FO_FRMT)
2917  			goto nxs_doit;
2918  		fdcreadid(fcp, csb);
2919  		csb->csb_xstate = FXS_RDID;
2920  		break;
2921  
2922  	case FXS_RDID:		/* read ID complete */
2923  		(void) fdc_result(fcp, csb->csb_rslt, 7);
2924  		/*
2925  		 * Ignored return. If failed, warning was issued by fdc_result.
2926  		 * Actual results checked below
2927  		 */
2928  		if ((csb->csb_status = (*csb->csb_rslt &
2929  		    (S0_ICMASK | S0_ECHK | S0_NOTRDY))) != 0)
2930  			goto nxs_cmpl;
2931  		if (csb->csb_cmd[2] != csb->csb_rslt[3]) {
2932  			/* at wrong logical cylinder */
2933  			csb->csb_status = S0_SEKEND;
2934  			goto nxs_cmpl;
2935  		};
2936  		goto nxs_doit;
2937  
2938  	case FXS_DOIT:		/* do original operation */
2939  		ASSERT(fcp->c_timeid == 0);
2940  		time = drv_usectohz(100000 * csb->csb_timer);
2941  		if (time == 0)
2942  			time = drv_usectohz(2000000);
2943  		fcp->c_timeid = timeout(fdwatch, (void *)fcp, time);
2944  nxs_doit:
2945  		if (fdc_docmd(fcp, csb->csb_cmd, csb->csb_ncmds) == -1) {
2946  			/* cntlr did not accept command bytes */
2947  			fdcquiesce(fcp);
2948  			csb->csb_xstate = FXS_RESET;
2949  			csb->csb_cmdstat = EIO;
2950  			break;
2951  		}
2952  		csb->csb_xstate = FXS_DOWT;
2953  		break;
2954  
2955  	case FXS_DOWT:		/* operation complete */
2956  		(void) fdc_result(fcp, csb->csb_rslt, csb->csb_nrslts);
2957  		/*
2958  		 * Ignored return. If failed, warning was issued by fdc_result.
2959  		 * Actual results checked below.
2960  		 */
2961  		if (*csb->csb_cmd == FO_SDRV) {
2962  			csb->csb_status =
2963  			    (*csb->csb_rslt ^ (S3_DRRDY | S3_2SIDE)) &
2964  			    ~(S3_HEAD | S3_UNIT);
2965  		} else {
2966  			csb->csb_status = *csb->csb_rslt &
2967  			    (S0_ICMASK | S0_ECHK | S0_NOTRDY);
2968  		}
2969  nxs_cmpl:
2970  		if (csb->csb_status)
2971  			csb->csb_cmdstat = EIO;
2972  		csb->csb_xstate = FXS_END;
2973  
2974  		/*  remove watchdog timer if armed and not already triggered */
2975  		if (fcp->c_timeid != 0) {
2976  			timeout_id_t timeid;
2977  			timeid = fcp->c_timeid;
2978  			fcp->c_timeid = 0;
2979  			mutex_exit(&fcp->c_lock);
2980  			(void) untimeout(timeid);
2981  			mutex_enter(&fcp->c_lock);
2982  		}
2983  		break;
2984  
2985  	case FXS_KILL:		/* quiesce cntlr by reset */
2986  		fdcquiesce(fcp);
2987  		fcp->c_timeid = timeout(fdwatch, (void *)fcp,
2988  		    drv_usectohz(2000000));
2989  		csb->csb_xstate = FXS_RESET;
2990  		break;
2991  
2992  	case FXS_RESET:		/* int from reset */
2993  		for (unit = 0; unit < NFDUN; unit++) {
2994  			(void) fdcsense_int(fcp, NULL, NULL);
2995  			fcp->c_curpcyl[unit] = -1;
2996  		}
2997  		if (fcp->c_timeid != 0) {
2998  			timeout_id_t timeid;
2999  			timeid = fcp->c_timeid;
3000  			fcp->c_timeid = 0;
3001  			mutex_exit(&fcp->c_lock);
3002  			(void) untimeout(timeid);
3003  			mutex_enter(&fcp->c_lock);
3004  		}
3005  		csb->csb_xstate = FXS_END;
3006  		break;
3007  
3008  	default:
3009  		cmn_err(CE_WARN, "fdc: statemach, unknown state");
3010  		return (-1);
3011  	}
3012  	FCERRPRINT(FDEP_L1, FDEM_EXEC,
3013  	    (CE_CONT, "fdc_statemach unit %d: %d -> %d\n",
3014  	    csb->csb_drive, csb->csb_oldxs, csb->csb_xstate));
3015  	return (csb->csb_xstate);
3016  }
3017  
3018  
3019  /*
3020   * routine to program a command into the floppy disk controller.
3021   */
3022  int
3023  fdc_docmd(struct fdcntlr *fcp, uchar_t *oplistp, uchar_t count)
3024  {
3025  	int ntries;
3026  
3027  	ASSERT(count >= 1);
3028  	FCERRPRINT(FDEP_L0, FDEM_EXEC,
3029  	    (CE_CONT, "fdc_docmd: %x %x %x %x %x %x %x %x %x\n",
3030  	    oplistp[0], oplistp[1], oplistp[2], oplistp[3], oplistp[4],
3031  	    oplistp[5], oplistp[6], oplistp[7], oplistp[8]));
3032  
3033  	do {
3034  		ntries = FDC_RQM_RETRY;
3035  		do {
3036  			if ((inb(fcp->c_regbase + FCR_MSR) & (MS_RQM|MS_DIO))
3037  			    == MS_RQM)
3038  				break;
3039  			else
3040  				drv_usecwait(1);
3041  		} while (--ntries);
3042  		if (ntries == 0) {
3043  			FCERRPRINT(FDEP_L3, FDEM_EXEC,
3044  			    (CE_WARN, "fdc_docmd: ctlr not ready"));
3045  			return (-1);
3046  		}
3047  		outb(fcp->c_regbase + FCR_DATA, *oplistp++);
3048  		drv_usecwait(16);	/* See comment in fdc_result() */
3049  	} while (--count);
3050  	return (0);
3051  }
3052  
3053  
3054  /*
3055   * Routine to return controller/drive status information.
3056   * The diskette-controller data-register is read the
3057   * requested number of times and the results are placed in
3058   * consecutive memory locations starting at the passed
3059   * address.
3060   */
3061  int
3062  fdc_result(struct fdcntlr *fcp, uchar_t *rsltp, uchar_t rcount)
3063  {
3064  	int ntries;
3065  	uchar_t *abresultp = rsltp;
3066  	uchar_t stat;
3067  	int laxative = 7;
3068  
3069  	ntries = 10 * FDC_RQM_RETRY;
3070  	do {
3071  		do {
3072  			if ((inb(fcp->c_regbase + FCR_MSR) &
3073  			    (MS_RQM | MS_DIO)) == (MS_RQM | MS_DIO))
3074  				break;
3075  			else
3076  				drv_usecwait(10);
3077  		} while (--ntries);
3078  		if (!ntries) {
3079  			FCERRPRINT(FDEP_L3, FDEM_EXEC,
3080  			    (CE_WARN, "fdc_result: ctlr not ready"));
3081  			return (-2);
3082  		}
3083  		*rsltp++ = inb(fcp->c_regbase + FCR_DATA);
3084  
3085  		/*
3086  		 * The PRM suggests waiting for 14.5 us.
3087  		 * Adding a bit more to cover the case of bad calibration
3088  		 * of drv_usecwait().
3089  		 */
3090  		drv_usecwait(16);
3091  		ntries = FDC_RQM_RETRY;
3092  	} while (--rcount);
3093  	while ((inb(fcp->c_regbase + FCR_MSR) & MS_CB) && laxative--) {
3094  		FCERRPRINT(FDEP_L3, FDEM_EXEC,
3095  		    (CE_WARN, "fdc_result: ctlr still busy"));
3096  		/*
3097  		 * try to complete Result phase by purging
3098  		 * result bytes queued for reading
3099  		 */
3100  		*abresultp = S0_IVCMD;
3101  		do {
3102  			stat = inb(fcp->c_regbase + FCR_MSR) &
3103  			    (MS_RQM | MS_DIO);
3104  			if (stat == MS_RQM) {
3105  				/*
3106  				 * Result phase is complete
3107  				 * but did we get the results corresponding to
3108  				 * the command we think we executed?
3109  				 */
3110  				return (-1);
3111  			}
3112  			if (stat == (MS_RQM | MS_DIO))
3113  				break;
3114  			else
3115  				drv_usecwait(10);
3116  		} while (--ntries);
3117  		if (!ntries || !laxative) {
3118  			FCERRPRINT(FDEP_L3, FDEM_EXEC,
3119  			    (CE_WARN,
3120  			    "fdc_result: ctlr still busy and not ready"));
3121  			return (-3);
3122  		}
3123  		(void) inb(fcp->c_regbase + FCR_DATA);
3124  
3125  		drv_usecwait(16);	/* See comment above */
3126  		ntries = FDC_RQM_RETRY;
3127  	}
3128  	return (0);
3129  }
3130  
3131  /*
3132   *  Function: get_unit()
3133   *
3134   *  Assumptions:  ioaddr is either 0x3f0 or 0x370
3135   */
3136  static int
3137  get_unit(dev_info_t *dip, int *cntrl_num)
3138  {
3139  	int ioaddr;
3140  
3141  	if (get_ioaddr(dip, &ioaddr) != DDI_SUCCESS)
3142  		return (DDI_FAILURE);
3143  
3144  	switch (ioaddr) {
3145  	case 0x3f0:
3146  		*cntrl_num = 0;
3147  		break;
3148  
3149  	case 0x370:
3150  		*cntrl_num = 1;
3151  		break;
3152  
3153  	default:
3154  		return (DDI_FAILURE);
3155  	}
3156  	return (DDI_SUCCESS);
3157  }
3158  
3159  static int
3160  get_ioaddr(dev_info_t *dip, int *ioaddr)
3161  {
3162  	int reglen, nregs, i;
3163  	int status = DDI_FAILURE;
3164  	struct {
3165  		int bustype;
3166  		int base;
3167  		int size;
3168  	} *reglist;
3169  
3170  	if (ddi_getlongprop(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS,
3171  		"reg", (caddr_t)&reglist, &reglen) != DDI_PROP_SUCCESS) {
3172  		cmn_err(CE_WARN, "fdc: reg property not found");
3173  		return (DDI_FAILURE);
3174  	}
3175  
3176  	nregs = reglen / sizeof (*reglist);
3177  	for (i = 0; i < nregs; i++) {
3178  		if (reglist[i].bustype == 1) {
3179  			*ioaddr = reglist[i].base;
3180  			status = DDI_SUCCESS;
3181  			break;
3182  		}
3183  	}
3184  	kmem_free(reglist, reglen);
3185  
3186  	if (status == DDI_SUCCESS) {
3187  		if (*ioaddr == 0x3f2 || *ioaddr == 0x372) {
3188  			/*
3189  			 * Some BIOS's (ASUS is one) don't include first
3190  			 * two IO ports in the floppy controller resources.
3191  			 */
3192  
3193  			*ioaddr -= 2; /* step back to 0x3f0 or 0x370 */
3194  
3195  			/*
3196  			 * It would be nice to update the regs property as well
3197  			 * so device pathname contains 3f0 instead of 3f2, but
3198  			 * updating the regs now won't have this effect as that
3199  			 * component of the device pathname has already been
3200  			 * constructed by the ISA nexus driver.
3201  			 *
3202  			 * reglist[i].base -= 2;
3203  			 * reglist[i].size += 2;
3204  			 * dev = makedevice(ddi_driver_major(dip), 0);
3205  			 * ddi_prop_update_int_array(dev, dip, "reg",
3206  			 *    (int *)reglist, reglen / sizeof (int));
3207  			 */
3208  		}
3209  	}
3210  
3211  	return (status);
3212  }
3213