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