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