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