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