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