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