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