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/debug.h>
     57 #include <sys/kmem.h>
     58 #include <sys/stat.h>
     59 
     60 #include <sys/autoconf.h>
     61 #include <sys/dkio.h>
     62 #include <sys/vtoc.h>
     63 #include <sys/kstat.h>
     64 
     65 #include <sys/fdio.h>
     66 #include <sys/fdc.h>
     67 #include <sys/i8272A.h>
     68 #include <sys/fd_debug.h>
     69 #include <sys/promif.h>
     70 #include <sys/ddi.h>
     71 #include <sys/sunddi.h>
     72 
     73 /*
     74  * bss (uninitialized data)
     75  */
     76 static void *fdc_state_head;		/* opaque handle top of state structs */
     77 static ddi_dma_attr_t fdc_dma_attr;
     78 static ddi_device_acc_attr_t fdc_accattr = {DDI_DEVICE_ATTR_V0,
     79 	DDI_STRUCTURE_LE_ACC, DDI_STRICTORDER_ACC};
     80 
     81 /*
     82  * Local static data
     83  */
     84 #define	OURUN_TRIES	12
     85 static uchar_t rwretry = 4;
     86 static uchar_t skretry = 3;
     87 static uchar_t configurecmd[4] = {FO_CNFG, 0, 0x0F, 0};
     88 static uchar_t recalcmd[2] = {FO_RECAL, 0};
     89 static uchar_t senseintcmd = FO_SINT;
     90 
     91 /*
     92  * error handling
     93  *
     94  * for debugging, set rwretry and skretry = 1
     95  *		set fcerrlevel to 1
     96  *		set fcerrmask  to 224  or 644
     97  *
     98  * after debug, set rwretry to 4, skretry to 3, and fcerrlevel to 5
     99  * set fcerrmask to FDEM_ALL
    100  * or remove the define DEBUG
    101  */
    102 static uint_t fcerrmask = FDEM_ALL;
    103 static int fcerrlevel = 6;
    104 
    105 #define	KIOIP	KSTAT_INTR_PTR(fcp->c_intrstat)
    106 
    107 
    108 static xlate_tbl_t drate_mfm[] = {
    109 	{  250, 2},
    110 	{  300, 1},
    111 	{  417, 0},
    112 	{  500, 0},
    113 	{ 1000, 3},
    114 	{    0, 0}
    115 };
    116 
    117 static xlate_tbl_t sector_size[] = {
    118 	{  256, 1},
    119 	{  512, 2},
    120 	{ 1024, 3},
    121 	{    0, 2}
    122 };
    123 
    124 static xlate_tbl_t motor_onbits[] = {
    125 	{  0, 0x10},
    126 	{  1, 0x20},
    127 	{  2, 0x40},
    128 	{  3, 0x80},
    129 	{  0, 0x80}
    130 };
    131 
    132 static xlate_tbl_t step_rate[] = {
    133 	{  10, 0xF0},		/* for 500K data rate */
    134 	{  20, 0xE0},
    135 	{  30, 0xD0},
    136 	{  40, 0xC0},
    137 	{  50, 0xB0},
    138 	{  60, 0xA0},
    139 	{  70, 0x90},
    140 	{  80, 0x80},
    141 	{  90, 0x70},
    142 	{ 100, 0x60},
    143 	{ 110, 0x50},
    144 	{ 120, 0x40},
    145 	{ 130, 0x30},
    146 	{ 140, 0x20},
    147 	{ 150, 0x10},
    148 	{ 160, 0x00},
    149 	{   0, 0x00}
    150 };
    151 
    152 #ifdef notdef
    153 static xlate_tbl_t head_unld[] = {
    154 	{  16, 0x1},		/* for 500K data rate */
    155 	{  32, 0x2},
    156 	{  48, 0x3},
    157 	{  64, 0x4},
    158 	{  80, 0x5},
    159 	{  96, 0x6},
    160 	{ 112, 0x7},
    161 	{ 128, 0x8},
    162 	{ 144, 0x9},
    163 	{ 160, 0xA},
    164 	{ 176, 0xB},
    165 	{ 192, 0xC},
    166 	{ 208, 0xD},
    167 	{ 224, 0xE},
    168 	{ 240, 0xF},
    169 	{ 256, 0x0},
    170 	{   0, 0x0}
    171 };
    172 #endif
    173 
    174 static struct fdcmdinfo {
    175 	char *cmdname;		/* command name */
    176 	uchar_t ncmdbytes;	/* number of bytes of command */
    177 	uchar_t nrsltbytes;	/* number of bytes in result */
    178 	uchar_t cmdtype;		/* characteristics */
    179 } fdcmds[] = {
    180 	"", 0, 0, 0,			/* - */
    181 	"", 0, 0, 0,			/* - */
    182 	"read_track", 9, 7, 1,		/* 2 */
    183 	"specify", 3, 0, 3,		/* 3 */
    184 	"sense_drv_status", 2, 1, 3,	/* 4 */
    185 	"write", 9, 7, 1,		/* 5 */
    186 	"read", 9, 7, 1,		/* 6 */
    187 	"recalibrate", 2, 0, 2,		/* 7 */
    188 	"sense_int_status", 1, 2, 3,	/* 8 */
    189 	"write_del", 9, 7, 1,		/* 9 */
    190 	"read_id", 2, 7, 2,		/* A */
    191 	"", 0, 0, 0,			/* - */
    192 	"read_del", 9, 7, 1,		/* C */
    193 	"format_track", 10, 7, 1,	/* D */
    194 	"dump_reg", 1, 10, 4,		/* E */
    195 	"seek", 3, 0, 2,		/* F */
    196 	"version", 1, 1, 3,		/* 10 */
    197 	"", 0, 0, 0,			/* - */
    198 	"perp_mode", 2, 0, 3,		/* 12 */
    199 	"configure", 4, 0, 4,		/* 13 */
    200 	/* relative seek */
    201 };
    202 
    203 
    204 static int
    205 fdc_bus_ctl(dev_info_t *, dev_info_t *, ddi_ctl_enum_t, void *, void *);
    206 static int get_ioaddr(dev_info_t *dip, int *ioaddr);
    207 static int get_unit(dev_info_t *dip, int *cntrl_num);
    208 
    209 struct bus_ops fdc_bus_ops = {
    210 	BUSO_REV,
    211 	nullbusmap,
    212 	0,	/* ddi_intrspec_t (*bus_get_intrspec)(); */
    213 	0,	/* int 	(*bus_add_intrspec)(); */
    214 	0,	/* void (*bus_remove_intrspec)(); */
    215 	i_ddi_map_fault,
    216 	ddi_dma_map,
    217 	ddi_dma_allochdl,
    218 	ddi_dma_freehdl,
    219 	ddi_dma_bindhdl,
    220 	ddi_dma_unbindhdl,
    221 	ddi_dma_flush,
    222 	ddi_dma_win,
    223 	ddi_dma_mctl,
    224 	fdc_bus_ctl,
    225 	ddi_bus_prop_op,
    226 };
    227 
    228 static int fdc_getinfo(dev_info_t *, ddi_info_cmd_t, void *, void **);
    229 static int fdc_probe(dev_info_t *);
    230 static int fdc_attach(dev_info_t *, ddi_attach_cmd_t);
    231 static int fdc_detach(dev_info_t *, ddi_detach_cmd_t);
    232 static int fdc_quiesce(dev_info_t *);
    233 static int fdc_enhance_probe(struct fdcntlr *fcp);
    234 
    235 struct dev_ops	fdc_ops = {
    236 	DEVO_REV,		/* devo_rev, */
    237 	0,			/* refcnt  */
    238 	fdc_getinfo,		/* getinfo */
    239 	nulldev,		/* identify */
    240 	fdc_probe,		/* probe */
    241 	fdc_attach,		/* attach */
    242 	fdc_detach,		/* detach */
    243 	nodev,			/* reset */
    244 	(struct cb_ops *)0,	/* driver operations */
    245 	&fdc_bus_ops,		/* bus operations */
    246 	NULL,			/* power */
    247 	fdc_quiesce,		/* quiesce */
    248 };
    249 
    250 /*
    251  * This is the loadable module wrapper.
    252  */
    253 #include <sys/modctl.h>
    254 
    255 extern struct mod_ops mod_driverops;
    256 
    257 static struct modldrv modldrv = {
    258 	&mod_driverops,		/* Type of module. This one is a driver */
    259 	"Floppy Controller",	/* Name of the module. */
    260 	&fdc_ops,		/* Driver ops vector */
    261 };
    262 
    263 static struct modlinkage modlinkage = {
    264 	MODREV_1, (void *)&modldrv, NULL
    265 };
    266 
    267 int
    268 _init(void)
    269 {
    270 	int retval;
    271 
    272 	if ((retval = ddi_soft_state_init(&fdc_state_head,
    273 	    sizeof (struct fdcntlr) + NFDUN * sizeof (struct fcu_obj), 0)) != 0)
    274 		return (retval);
    275 
    276 	if ((retval = mod_install(&modlinkage)) != 0)
    277 		ddi_soft_state_fini(&fdc_state_head);
    278 	return (retval);
    279 }
    280 
    281 int
    282 _fini(void)
    283 {
    284 	int retval;
    285 
    286 	if ((retval = mod_remove(&modlinkage)) != 0)
    287 		return (retval);
    288 	ddi_soft_state_fini(&fdc_state_head);
    289 	return (retval);
    290 }
    291 
    292 int
    293 _info(struct modinfo *modinfop)
    294 {
    295 	return (mod_info(&modlinkage, modinfop));
    296 }
    297 
    298 
    299 int fdc_start(struct fcu_obj *);
    300 int fdc_abort(struct fcu_obj *);
    301 int fdc_getcap(struct fcu_obj *, char *, int);
    302 int fdc_setcap(struct fcu_obj *, char *, int, int);
    303 int fdc_dkinfo(struct fcu_obj *, struct dk_cinfo *);
    304 int fdc_select(struct fcu_obj *, int, int);
    305 int fdgetchng(struct fcu_obj *, int);
    306 int fdresetchng(struct fcu_obj *, int);
    307 int fdrecalseek(struct fcu_obj *, int, int, int);
    308 int fdrw(struct fcu_obj *, int, int, int, int, int, caddr_t, uint_t);
    309 int fdtrkformat(struct fcu_obj *, int, int, int, int);
    310 int fdrawioctl(struct fcu_obj *, int, caddr_t);
    311 
    312 static struct fcobjops fdc_iops = {
    313 		fdc_start,	/* controller start */
    314 		fdc_abort,	/* controller abort */
    315 		fdc_getcap,	/* capability retrieval */
    316 		fdc_setcap,	/* capability establishment */
    317 		fdc_dkinfo,	/* get disk controller info */
    318 
    319 		fdc_select,	/* select / deselect unit */
    320 		fdgetchng,	/* get media change */
    321 		fdresetchng,	/* reset media change */
    322 		fdrecalseek,	/* recal / seek */
    323 		NULL,		/* read /write request (UNUSED) */
    324 		fdrw,		/* read /write sector */
    325 		fdtrkformat,	/* format track */
    326 		fdrawioctl	/* raw ioctl */
    327 };
    328 
    329 
    330 /*
    331  * Function prototypes
    332  */
    333 void encode(xlate_tbl_t *tablep, int val, uchar_t *rcode);
    334 int decode(xlate_tbl_t *, int, int *);
    335 static int fdc_propinit1(struct fdcntlr *, int);
    336 static void fdc_propinit2(struct fdcntlr *, int);
    337 void fdcquiesce(struct fdcntlr *);
    338 int fdcsense_chng(struct fdcntlr *, int);
    339 int fdcsense_drv(struct fdcntlr *, int);
    340 int fdcsense_int(struct fdcntlr *, int *, int *);
    341 int fdcspecify(struct fdcntlr *, int, int, int);
    342 int fdcspdchange(struct fdcntlr *, struct fcu_obj *, int);
    343 static int fdc_exec(struct fdcntlr *, int, int);
    344 int fdcheckdisk(struct fdcntlr *, int);
    345 static uint_t fdc_intr(caddr_t arg);
    346 static void fdwatch(void *arg);
    347 static void fdmotort(void *arg);
    348 static int fdrecover(struct fdcntlr *);
    349 static int fdc_motorsm(struct fcu_obj *, int, int);
    350 static int fdc_statemach(struct fdcntlr *);
    351 int fdc_docmd(struct fdcntlr *, uchar_t *, uchar_t);
    352 int fdc_result(struct fdcntlr *, uchar_t *, uchar_t);
    353 
    354 
    355 /* ARGSUSED */
    356 static int
    357 fdc_bus_ctl(dev_info_t *dip, dev_info_t *rdip, ddi_ctl_enum_t ctlop,
    358     void *arg, void *result)
    359 {
    360 	struct 	fdcntlr *fcp;
    361 	struct	fcu_obj *fjp;
    362 
    363 	FCERRPRINT(FDEP_L0, FDEM_ATTA,
    364 	    (CE_CONT, "fdc_bus_ctl: cmd= %x\n", ctlop));
    365 
    366 	if ((fcp = ddi_get_driver_private(dip)) == NULL)
    367 		return (DDI_FAILURE);
    368 
    369 	switch (ctlop) {
    370 
    371 	case DDI_CTLOPS_REPORTDEV:
    372 		cmn_err(CE_CONT, "?%s%d at %s%d\n",
    373 		    ddi_get_name(rdip), ddi_get_instance(rdip),
    374 		    ddi_get_name(dip), ddi_get_instance(dip));
    375 		FCERRPRINT(FDEP_L3, FDEM_ATTA,
    376 		    (CE_WARN, "fdc_bus_ctl: report %s%d at %s%d",
    377 		    ddi_get_name(rdip), ddi_get_instance(rdip),
    378 		    ddi_get_name(dip), ddi_get_instance(dip)));
    379 		return (DDI_SUCCESS);
    380 
    381 	case DDI_CTLOPS_INITCHILD:
    382 	{
    383 		dev_info_t *udip = (dev_info_t *)arg;
    384 		int cntlr;
    385 		int len;
    386 		int unit;
    387 		char name[MAXNAMELEN];
    388 
    389 		FCERRPRINT(FDEP_L3, FDEM_ATTA,
    390 		    (CE_WARN, "fdc_bus_ctl: init child 0x%p", (void*)udip));
    391 		cntlr = fcp->c_number;
    392 
    393 		len = sizeof (unit);
    394 		if (ddi_prop_op(DDI_DEV_T_ANY, udip, PROP_LEN_AND_VAL_BUF,
    395 		    DDI_PROP_DONTPASS, "unit", (caddr_t)&unit, &len)
    396 		    != DDI_PROP_SUCCESS ||
    397 		    cntlr != FDCTLR(unit) ||
    398 		    (fcp->c_unit[FDUNIT(unit)])->fj_dip)
    399 			return (DDI_NOT_WELL_FORMED);
    400 
    401 		(void) sprintf(name, "%d,%d", cntlr, FDUNIT(unit));
    402 		ddi_set_name_addr(udip, name);
    403 
    404 		fjp = fcp->c_unit[FDUNIT(unit)];
    405 		fjp->fj_unit = unit;
    406 		fjp->fj_dip = udip;
    407 		fjp->fj_ops = &fdc_iops;
    408 		fjp->fj_fdc = fcp;
    409 		fjp->fj_iblock = &fcp->c_iblock;
    410 
    411 		ddi_set_driver_private(udip, fjp);
    412 
    413 		return (DDI_SUCCESS);
    414 	}
    415 	case DDI_CTLOPS_UNINITCHILD:
    416 	{
    417 		dev_info_t *udip = (dev_info_t *)arg;
    418 
    419 		FCERRPRINT(FDEP_L3, FDEM_ATTA,
    420 		    (CE_WARN, "fdc_bus_ctl: uninit child 0x%p", (void *)udip));
    421 		fjp = ddi_get_driver_private(udip);
    422 		ddi_set_driver_private(udip, NULL);
    423 		fjp->fj_dip = NULL;
    424 		ddi_set_name_addr(udip, NULL);
    425 		return (DDI_SUCCESS);
    426 	}
    427 	default:
    428 		return (DDI_FAILURE);
    429 	}
    430 }
    431 
    432 /* ARGSUSED */
    433 static int
    434 fdc_getinfo(dev_info_t *dip, ddi_info_cmd_t cmd, void *arg, void **result)
    435 {
    436 	struct fdcntlr *fcp;
    437 	int rval;
    438 
    439 	switch (cmd) {
    440 	case DDI_INFO_DEVT2DEVINFO:
    441 		if (fcp = ddi_get_soft_state(fdc_state_head, (dev_t)arg)) {
    442 			*result = fcp->c_dip;
    443 			rval = DDI_SUCCESS;
    444 			break;
    445 		} else {
    446 			rval = DDI_FAILURE;
    447 			break;
    448 		}
    449 	case DDI_INFO_DEVT2INSTANCE:
    450 		*result = (void *)(uintptr_t)getminor((dev_t)arg);
    451 		rval = DDI_SUCCESS;
    452 		break;
    453 	default:
    454 		rval = DDI_FAILURE;
    455 	}
    456 	return (rval);
    457 }
    458 
    459 static int
    460 fdc_probe(dev_info_t *dip)
    461 {
    462 	int	debug[2];
    463 	int ioaddr;
    464 	int	len;
    465 	uchar_t	stat;
    466 
    467 	len = sizeof (debug);
    468 	if (ddi_prop_op(DDI_DEV_T_ANY, dip, PROP_LEN_AND_VAL_BUF,
    469 	    DDI_PROP_DONTPASS, "debug", (caddr_t)debug, &len) ==
    470 	    DDI_PROP_SUCCESS) {
    471 		fcerrlevel = debug[0];
    472 		fcerrmask = (uint_t)debug[1];
    473 	}
    474 
    475 	FCERRPRINT(FDEP_L3, FDEM_ATTA, (CE_WARN, "fdc_probe: dip %p",
    476 	    (void*)dip));
    477 
    478 	if (get_ioaddr(dip, &ioaddr) != DDI_SUCCESS)
    479 		return (DDI_PROBE_FAILURE);
    480 
    481 	stat = inb(ioaddr + FCR_MSR);
    482 	if ((stat & (MS_RQM | MS_DIO | MS_CB)) != MS_RQM &&
    483 	    (stat & ~MS_DIO) != MS_CB)
    484 		return (DDI_PROBE_FAILURE);
    485 
    486 	return (DDI_PROBE_SUCCESS);
    487 }
    488 
    489 /* ARGSUSED */
    490 static int
    491 fdc_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
    492 {
    493 	struct fdcntlr *fcp;
    494 	struct fcu_obj *fjp;
    495 	int cntlr_num, ctlr, unit;
    496 	int intr_set = 0;
    497 	int len;
    498 	char name[MAXNAMELEN];
    499 
    500 	FCERRPRINT(FDEP_L3, FDEM_ATTA, (CE_WARN, "fdc_attach: dip %p",
    501 	    (void*)dip));
    502 
    503 	switch (cmd) {
    504 	case DDI_ATTACH:
    505 		if (ddi_getprop
    506 		    (DDI_DEV_T_ANY, dip, 0, "ignore-hardware-nodes", 0)) {
    507 			len = sizeof (cntlr_num);
    508 			if (ddi_prop_op(DDI_DEV_T_ANY, dip,
    509 			    PROP_LEN_AND_VAL_BUF, DDI_PROP_DONTPASS, "unit",
    510 			    (caddr_t)&cntlr_num, &len) != DDI_PROP_SUCCESS) {
    511 				FCERRPRINT(FDEP_L3, FDEM_ATTA, (CE_WARN,
    512 				    "fdc_attach failed: dip %p", (void*)dip));
    513 				return (DDI_FAILURE);
    514 			}
    515 		} else {
    516 			if (get_unit(dip, &cntlr_num) != DDI_SUCCESS)
    517 				return (DDI_FAILURE);
    518 		}
    519 
    520 		ctlr = ddi_get_instance(dip);
    521 		if (ddi_soft_state_zalloc(fdc_state_head, ctlr) != 0)
    522 			return (DDI_FAILURE);
    523 		fcp = ddi_get_soft_state(fdc_state_head, ctlr);
    524 
    525 		for (unit = 0, fjp = (struct fcu_obj *)(fcp+1);
    526 		    unit < NFDUN; unit++) {
    527 			fcp->c_unit[unit] = fjp++;
    528 		}
    529 		fcp->c_dip = dip;
    530 
    531 		if (fdc_propinit1(fcp, cntlr_num) != DDI_SUCCESS)
    532 			goto no_attach;
    533 
    534 		/* get iblock cookie to initialize mutex used in the ISR */
    535 		if (ddi_get_iblock_cookie(dip, (uint_t)0, &fcp->c_iblock) !=
    536 		    DDI_SUCCESS) {
    537 			cmn_err(CE_WARN,
    538 			    "fdc_attach: cannot get iblock cookie");
    539 			goto no_attach;
    540 		}
    541 		mutex_init(&fcp->c_lock, NULL, MUTEX_DRIVER, fcp->c_iblock);
    542 		intr_set = 1;
    543 
    544 		/* setup interrupt handler */
    545 		if (ddi_add_intr(dip, (uint_t)0, NULL,
    546 		    (ddi_idevice_cookie_t *)0, fdc_intr, (caddr_t)fcp) !=
    547 		    DDI_SUCCESS) {
    548 			cmn_err(CE_WARN, "fdc: cannot add intr\n");
    549 			goto no_attach;
    550 		}
    551 		intr_set++;
    552 
    553 		/*
    554 		 * acquire the DMA channel
    555 		 * this assumes that the chnl is not shared; else allocate
    556 		 * and free the chnl with each fdc request
    557 		 */
    558 		if (ddi_dmae_alloc(dip, fcp->c_dmachan, DDI_DMA_DONTWAIT, NULL)
    559 		    != DDI_SUCCESS) {
    560 			cmn_err(CE_WARN, "fdc: cannot acquire dma%d\n",
    561 			    fcp->c_dmachan);
    562 			goto no_attach;
    563 		}
    564 		(void) ddi_dmae_getattr(dip, &fdc_dma_attr);
    565 		fdc_dma_attr.dma_attr_align = MMU_PAGESIZE;
    566 
    567 		mutex_init(&fcp->c_dorlock, NULL, MUTEX_DRIVER, fcp->c_iblock);
    568 		cv_init(&fcp->c_iocv, NULL, CV_DRIVER, fcp->c_iblock);
    569 		sema_init(&fcp->c_selsem, 1, NULL, SEMA_DRIVER, NULL);
    570 
    571 		(void) sprintf(name, "fdc%d", ctlr);
    572 		fcp->c_intrstat = kstat_create("fdc", ctlr, name,
    573 		    "controller", KSTAT_TYPE_INTR, 1, KSTAT_FLAG_PERSISTENT);
    574 		if (fcp->c_intrstat) {
    575 			kstat_install(fcp->c_intrstat);
    576 		}
    577 
    578 		ddi_set_driver_private(dip, fcp);
    579 
    580 		/*
    581 		 * reset the controller
    582 		 */
    583 		sema_p(&fcp->c_selsem);
    584 		mutex_enter(&fcp->c_lock);
    585 		fcp->c_csb.csb_xstate = FXS_RESET;
    586 		fcp->c_flags |= FCFLG_WAITING;
    587 		fdcquiesce(fcp);
    588 
    589 		/* first test for mode == Model 30 */
    590 		fcp->c_mode = (inb(fcp->c_regbase + FCR_SRB) & 0x1c) ?
    591 		    FDCMODE_AT : FDCMODE_30;
    592 
    593 		while (fcp->c_flags & FCFLG_WAITING) {
    594 			cv_wait(&fcp->c_iocv, &fcp->c_lock);
    595 		}
    596 		mutex_exit(&fcp->c_lock);
    597 		sema_v(&fcp->c_selsem);
    598 
    599 		fdc_propinit2(fcp, cntlr_num);
    600 
    601 		ddi_report_dev(dip);
    602 		return (DDI_SUCCESS);
    603 
    604 	case DDI_RESUME:
    605 		return (DDI_SUCCESS);
    606 		/* break; */
    607 
    608 	default:
    609 		return (DDI_FAILURE);
    610 	}
    611 
    612 no_attach:
    613 	if (intr_set) {
    614 		if (intr_set > 1)
    615 			ddi_remove_intr(dip, 0, fcp->c_iblock);
    616 		mutex_destroy(&fcp->c_lock);
    617 	}
    618 	ddi_soft_state_free(fdc_state_head, cntlr_num);
    619 	return (DDI_FAILURE);
    620 }
    621 
    622 static int
    623 fdc_propinit1(struct fdcntlr *fcp, int cntlr)
    624 {
    625 	dev_info_t *dip;
    626 	int len;
    627 	int value;
    628 
    629 	dip = fcp->c_dip;
    630 	len = sizeof (value);
    631 
    632 	if (get_ioaddr(dip, &value) != DDI_SUCCESS)
    633 		return (DDI_FAILURE);
    634 
    635 	fcp->c_regbase = (ushort_t)value;
    636 
    637 	if (ddi_prop_op(DDI_DEV_T_ANY, dip, PROP_LEN_AND_VAL_BUF,
    638 	    DDI_PROP_DONTPASS, "dma-channels", (caddr_t)&value, &len)
    639 	    != DDI_PROP_SUCCESS) {
    640 			cmn_err(CE_WARN,
    641 			    "fdc_attach: Error, could not find a dma channel");
    642 			return (DDI_FAILURE);
    643 	}
    644 	fcp->c_dmachan = (ushort_t)value;
    645 	fcp->c_number = cntlr;
    646 	return (DDI_SUCCESS);
    647 }
    648 
    649 /* ARGSUSED */
    650 static void
    651 fdc_propinit2(struct fdcntlr *fcp, int cntlr)
    652 {
    653 	dev_info_t *dip;
    654 	int ccr;
    655 	int len;
    656 	int value;
    657 
    658 	dip = fcp->c_dip;
    659 	len = sizeof (value);
    660 
    661 	if (ddi_prop_op(DDI_DEV_T_ANY, dip, PROP_LEN_AND_VAL_BUF,
    662 	    DDI_PROP_DONTPASS, "chip", (caddr_t)&value, &len)
    663 	    == DDI_PROP_SUCCESS)
    664 		fcp->c_chip = value;
    665 	else {
    666 		static uchar_t perpindcmd[2] = {FO_PERP, 0};
    667 		static uchar_t versioncmd = FO_VRSN;
    668 		uchar_t result;
    669 
    670 		fcp->c_chip = i8272A;
    671 		(void) fdc_docmd(fcp, &versioncmd, 1);
    672 		/*
    673 		 * Ignored return. If failed, warning was issued by fdc_docmd.
    674 		 * fdc_results retrieves the controller/drive status
    675 		 */
    676 		if (!fdc_result(fcp, &result, 1) && result == 0x90) {
    677 			/*
    678 			 * try a perpendicular_mode cmd to ensure
    679 			 * that we really have an enhanced controller
    680 			 */
    681 			if (fdc_docmd(fcp, perpindcmd, 2) ||
    682 			    fdc_docmd(fcp, configurecmd, 4))
    683 				/*
    684 				 * perpindicular_mode will be rejected by
    685 				 * older controllers; make sure we don't hang.
    686 				 */
    687 				(void) fdc_result(fcp, &result, 1);
    688 				/*
    689 				 * Ignored return. If failed, warning was
    690 				 * issued by fdc_result.
    691 				 */
    692 			else
    693 				/* enhanced type controller */
    694 
    695 				if ((fcp->c_chip = fdc_enhance_probe(fcp)) == 0)
    696 					/* default enhanced cntlr */
    697 					fcp->c_chip = i82077;
    698 		}
    699 		(void) ddi_prop_update_int(DDI_DEV_T_NONE, dip,
    700 		    "chip", fcp->c_chip);
    701 		/*
    702 		 * Ignoring return value because, for passed arguments, only
    703 		 * DDI_SUCCESS is returned.
    704 		 */
    705 	}
    706 	if (fcp->c_chip >= i82077 && fcp->c_mode == FDCMODE_30 &&
    707 	    (inb(fcp->c_regbase + FCR_DIR) & 0x70) == 0)
    708 		for (ccr = 0; ccr <= (FCC_NOPREC | FCC_DRATE); ccr++) {
    709 			/*
    710 			 * run through all the combinations of NOPREC and
    711 			 * datarate selection, and see if they show up in the
    712 			 * Model 30 DIR
    713 			 */
    714 			outb(fcp->c_regbase + FCR_CCR, ccr);
    715 			drv_usecwait(5);
    716 			if ((inb(fcp->c_regbase + FCR_DIR) &
    717 			    (FCC_NOPREC | FCC_DRATE)) != ccr) {
    718 				fcp->c_mode = FDCMODE_AT;
    719 				break;
    720 			}
    721 		}
    722 	else
    723 		fcp->c_mode = FDCMODE_AT;
    724 	outb(fcp->c_regbase + FCR_CCR, 0);
    725 }
    726 
    727 /* ARGSUSED */
    728 static int
    729 fdc_enhance_probe(struct fdcntlr *fcp)
    730 {
    731 	static uchar_t nsccmd = FO_NSC;
    732 	uint_t	ddic;
    733 	int	retcode = 0;
    734 	uchar_t	result;
    735 	uchar_t	save;
    736 
    737 	/*
    738 	 * Try to identify the enhanced floppy controller.
    739 	 * This is required so that we can program the DENSEL output to
    740 	 * control 3D mode (1.0 MB, 1.6 MB and 2.0 MB unformatted capacity,
    741 	 * 720 KB, 1.2 MB, and 1.44 MB formatted capacity) 3.5" dual-speed
    742 	 * floppy drives.  Refer to bugid 1195155.
    743 	 */
    744 
    745 	(void) fdc_docmd(fcp, &nsccmd, 1);
    746 	/*
    747 	 * Ignored return. If failed, warning was issued by fdc_docmd.
    748 	 * fdc_results retrieves the controller/drive status
    749 	 */
    750 	if (!fdc_result(fcp, &result, 1) && result != S0_IVCMD) {
    751 		/*
    752 		 * only enhanced National Semi PC8477 core
    753 		 * should respond to this command
    754 		 */
    755 		if ((result & 0xf0) == 0x70) {
    756 			/* low 4 bits may change */
    757 			fcp->c_flags |= FCFLG_3DMODE;
    758 			retcode = PC87322;
    759 		} else
    760 			cmn_err(CE_CONT,
    761 "?fdc: unidentified, enhanced, National Semiconductor cntlr %x\n", result);
    762 	} else {
    763 		save = inb(fcp->c_regbase + FCR_SRA);
    764 
    765 		do {
    766 			/* probe for motherboard version of SMC cntlr */
    767 
    768 			/* try to enable configuration mode */
    769 			ddic = ddi_enter_critical();
    770 			outb(fcp->c_regbase + FCR_SRA, FSA_ENA5);
    771 			outb(fcp->c_regbase + FCR_SRA, FSA_ENA5);
    772 			ddi_exit_critical(ddic);
    773 
    774 			outb(fcp->c_regbase + FCR_SRA, 0x0F);
    775 			if (inb(fcp->c_regbase + FCR_SRB) != 0x00)
    776 				/* always expect 0 from config reg F */
    777 				break;
    778 			outb(fcp->c_regbase + FCR_SRA, 0x0D);
    779 			if (inb(fcp->c_regbase + FCR_SRB) != 0x65)
    780 				/* expect 0x65 from config reg D */
    781 				break;
    782 			outb(fcp->c_regbase + FCR_SRA, 0x0E);
    783 			result = inb(fcp->c_regbase + FCR_SRB);
    784 			if (result != 0x02) {
    785 				/* expect revision level 2 from config reg E */
    786 				cmn_err(CE_CONT,
    787 "?fdc: unidentified, enhanced, SMC cntlr revision %x\n", result);
    788 				/* break;	*/
    789 			}
    790 			fcp->c_flags |= FCFLG_3DMODE;
    791 			retcode = FDC37C665;
    792 		} while (retcode == 0);
    793 		outb(fcp->c_regbase + FCR_SRA, FSA_DISB);
    794 
    795 		while (retcode == 0) {
    796 			/* probe for adapter version of SMC cntlr */
    797 			ddic = ddi_enter_critical();
    798 			outb(fcp->c_regbase + FCR_SRA, FSA_ENA6);
    799 			outb(fcp->c_regbase + FCR_SRA, FSA_ENA6);
    800 			ddi_exit_critical(ddic);
    801 
    802 			outb(fcp->c_regbase + FCR_SRA, 0x0F);
    803 			if (inb(fcp->c_regbase + FCR_SRB) != 0x00)
    804 				/* always expect 0 from config reg F */
    805 				break;
    806 			outb(fcp->c_regbase + FCR_SRA, 0x0D);
    807 			if (inb(fcp->c_regbase + FCR_SRB) != 0x66)
    808 				/* expect 0x66 from config reg D */
    809 				break;
    810 			outb(fcp->c_regbase + FCR_SRA, 0x0E);
    811 			result = inb(fcp->c_regbase + FCR_SRB);
    812 			if (result != 0x02) {
    813 				/* expect revision level 2 from config reg E */
    814 				cmn_err(CE_CONT,
    815 "?fdc: unidentified, enhanced, SMC cntlr revision %x\n", result);
    816 				/* break;	*/
    817 			}
    818 			fcp->c_flags |= FCFLG_3DMODE;
    819 			retcode = FDC37C666;
    820 		}
    821 		outb(fcp->c_regbase + FCR_SRA, FSA_DISB);
    822 
    823 		drv_usecwait(10);
    824 		outb(fcp->c_regbase + FCR_SRA, save);
    825 	}
    826 	return (retcode);
    827 }
    828 
    829 /* ARGSUSED */
    830 static int
    831 fdc_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
    832 {
    833 	struct fdcntlr *fcp;
    834 	struct fcu_obj *fjp;
    835 	int unit;
    836 	int rval = 0;
    837 
    838 	FCERRPRINT(FDEP_L3, FDEM_ATTA, (CE_WARN, "fdc_detach: dip %p",
    839 	    (void*)dip));
    840 
    841 	fcp = ddi_get_driver_private(dip);
    842 
    843 	switch (cmd) {
    844 	case DDI_DETACH:
    845 		for (unit = 0; unit < NFDUN; unit++)
    846 			if ((fcp->c_unit[unit])->fj_dip) {
    847 				rval = EBUSY;
    848 				break;
    849 			}
    850 		kstat_delete(fcp->c_intrstat);
    851 		fcp->c_intrstat = NULL;
    852 		ddi_remove_intr(fcp->c_dip, 0, fcp->c_iblock);
    853 		if (ddi_dmae_release(fcp->c_dip, fcp->c_dmachan) !=
    854 		    DDI_SUCCESS)
    855 			cmn_err(CE_WARN, "fdc_detach: dma release failed, "
    856 			    "dip %p, dmachan %x\n",
    857 			    (void*)fcp->c_dip, fcp->c_dmachan);
    858 		ddi_prop_remove_all(fcp->c_dip);
    859 		ddi_set_driver_private(fcp->c_dip, NULL);
    860 
    861 		mutex_destroy(&fcp->c_lock);
    862 		mutex_destroy(&fcp->c_dorlock);
    863 		cv_destroy(&fcp->c_iocv);
    864 		sema_destroy(&fcp->c_selsem);
    865 		ddi_soft_state_free(fdc_state_head, ddi_get_instance(dip));
    866 		break;
    867 
    868 	case DDI_SUSPEND:
    869 		/*
    870 		 * Following code causes the fdc (floppy controller)
    871 		 * to suspend as long as there are no floppy drives
    872 		 * attached to it.
    873 		 * At present the floppy driver does not support
    874 		 * SUSPEND/RESUME.
    875 		 *
    876 		 * Check if any FD units are attached
    877 		 *
    878 		 * For now, SUSPEND/RESUME is not supported
    879 		 * if a floppy drive is present.
    880 		 * So if any FD unit is attached return DDI_FAILURE
    881 		 */
    882 		for (unit = 0; unit < NFDUN; unit++) {
    883 			fjp = fcp->c_unit[unit];
    884 			if (fjp->fj_flags & FUNIT_DRVATCH) {
    885 				cmn_err(CE_WARN,
    886 				    "fdc_detach: fd attached, failing SUSPEND");
    887 				return (DDI_FAILURE);
    888 			}
    889 		}
    890 
    891 		cmn_err(CE_NOTE, "fdc_detach: SUSPEND fdc");
    892 
    893 		rval = DDI_SUCCESS;
    894 		break;
    895 
    896 	default:
    897 		rval = EINVAL;
    898 		break;
    899 	}
    900 	return (rval);
    901 }
    902 
    903 
    904 /* ARGSUSED */
    905 int
    906 fdc_start(struct fcu_obj *fjp)
    907 {
    908 	return (ENOSYS);
    909 }
    910 
    911 int
    912 fdc_abort(struct fcu_obj *fjp)
    913 {
    914 	struct fdcntlr *fcp = fjp->fj_fdc;
    915 	int unit = fjp->fj_unit & 3;
    916 
    917 	FCERRPRINT(FDEP_L3, FDEM_RESE, (CE_WARN, "fdc_abort"));
    918 	if (fcp->c_curunit == unit) {
    919 		mutex_enter(&fcp->c_lock);
    920 		if (fcp->c_flags & FCFLG_WAITING) {
    921 			/*
    922 			 * this can cause data corruption !
    923 			 */
    924 			fdcquiesce(fcp);
    925 			fcp->c_csb.csb_xstate = FXS_RESET;
    926 			fcp->c_flags |= FCFLG_TIMEOUT;
    927 			if (ddi_dmae_stop(fcp->c_dip, fcp->c_dmachan) !=
    928 			    DDI_SUCCESS)
    929 				cmn_err(CE_WARN,
    930 				    "fdc_detach: dma release failed, "
    931 				    "dip %p, dmachan %x\n",
    932 				    (void*)fcp->c_dip, fcp->c_dmachan);
    933 		}
    934 		mutex_exit(&fcp->c_lock);
    935 		drv_usecwait(500);
    936 		return (DDI_SUCCESS);
    937 	}
    938 	return (DDI_FAILURE);
    939 }
    940 
    941 /* ARGSUSED */
    942 int
    943 fdc_getcap(struct fcu_obj *fjp, char *a, int i)
    944 {
    945 	return (ENOSYS);
    946 }
    947 
    948 /* ARGSUSED */
    949 int
    950 fdc_setcap(struct fcu_obj *fjp, char *a, int i, int j)
    951 {
    952 	return (ENOSYS);
    953 }
    954 
    955 int
    956 fdc_dkinfo(struct fcu_obj *fjp, struct dk_cinfo *dcp)
    957 {
    958 	struct fdcntlr *fcp = fjp->fj_fdc;
    959 
    960 	(void) strncpy((char *)&dcp->dki_cname, ddi_get_name(fcp->c_dip),
    961 	    DK_DEVLEN);
    962 	dcp->dki_ctype = DKC_UNKNOWN; /* no code for generic PC/AT fdc */
    963 	dcp->dki_flags = DKI_FMTTRK;
    964 	dcp->dki_addr = fcp->c_regbase;
    965 	dcp->dki_space = 0;
    966 	dcp->dki_prio = fcp->c_intprio;
    967 	dcp->dki_vec = fcp->c_intvec;
    968 	(void) strncpy((char *)&dcp->dki_dname, ddi_driver_name(fjp->fj_dip),
    969 	    DK_DEVLEN);
    970 	dcp->dki_slave = fjp->fj_unit & 3;
    971 	dcp->dki_maxtransfer = maxphys / DEV_BSIZE;
    972 	return (DDI_SUCCESS);
    973 }
    974 
    975 /*
    976  * on=> non-zero = select, 0 = de-select
    977  */
    978 /* ARGSUSED */
    979 int
    980 fdc_select(struct fcu_obj *fjp, int funit, int on)
    981 {
    982 	struct fdcntlr *fcp = fjp->fj_fdc;
    983 	int unit = funit & 3;
    984 
    985 	if (on) {
    986 		/* possess controller */
    987 		sema_p(&fcp->c_selsem);
    988 		FCERRPRINT(FDEP_L2, FDEM_DSEL,
    989 		    (CE_NOTE, "fdc_select unit %d: on", funit));
    990 
    991 		if (fcp->c_curunit != unit || !(fjp->fj_flags & FUNIT_CHAROK)) {
    992 			fcp->c_curunit = unit;
    993 			fjp->fj_flags |= FUNIT_CHAROK;
    994 			if (fdcspecify(fcp,
    995 			    fjp->fj_chars->fdc_transfer_rate,
    996 			    fjp->fj_drive->fdd_steprate, 40))
    997 				cmn_err(CE_WARN,
    998 				    "fdc_select: controller setup rejected "
    999 				    "fdcntrl %p transfer rate %x step rate %x"
   1000 				    " head load time 40\n", (void*)fcp,
   1001 				    fjp->fj_chars->fdc_transfer_rate,
   1002 				    fjp->fj_drive->fdd_steprate);
   1003 		}
   1004 
   1005 		mutex_enter(&fcp->c_dorlock);
   1006 
   1007 		/* make sure drive is not selected in case we change speed */
   1008 		fcp->c_digout = (fcp->c_digout & ~FD_DRSEL) |
   1009 		    (~unit & FD_DRSEL);
   1010 		outb(fcp->c_regbase + FCR_DOR, fcp->c_digout);
   1011 
   1012 		(void) fdc_motorsm(fjp, FMI_STARTCMD,
   1013 		    fjp->fj_drive->fdd_motoron);
   1014 		/*
   1015 		 * Return value ignored - fdcmotort deals with failure.
   1016 		 */
   1017 		if (fdcspdchange(fcp, fjp, fjp->fj_attr->fda_rotatespd)) {
   1018 			/* 3D drive requires 500 ms for speed change */
   1019 			(void) fdc_motorsm(fjp, FMI_RSTARTCMD, 5);
   1020 			/*
   1021 			 * Return value ignored - fdcmotort deals with failure.
   1022 			 */
   1023 		}
   1024 
   1025 		fcp->c_digout = (fcp->c_digout & ~FD_DRSEL) | (unit & FD_DRSEL);
   1026 		outb(fcp->c_regbase + FCR_DOR, fcp->c_digout);
   1027 
   1028 		mutex_exit(&fcp->c_dorlock);
   1029 		fcp->c_csb.csb_drive = (uchar_t)unit;
   1030 	} else {
   1031 		FCERRPRINT(FDEP_L2, FDEM_DSEL,
   1032 		    (