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