xref: /onnv-gate/usr/src/uts/common/io/fdc.c (revision 940:44d4ab1dbd0c)
10Sstevel@tonic-gate /*
20Sstevel@tonic-gate  * CDDL HEADER START
30Sstevel@tonic-gate  *
40Sstevel@tonic-gate  * The contents of this file are subject to the terms of the
50Sstevel@tonic-gate  * Common Development and Distribution License, Version 1.0 only
60Sstevel@tonic-gate  * (the "License").  You may not use this file except in compliance
70Sstevel@tonic-gate  * with the License.
80Sstevel@tonic-gate  *
90Sstevel@tonic-gate  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
100Sstevel@tonic-gate  * or http://www.opensolaris.org/os/licensing.
110Sstevel@tonic-gate  * See the License for the specific language governing permissions
120Sstevel@tonic-gate  * and limitations under the License.
130Sstevel@tonic-gate  *
140Sstevel@tonic-gate  * When distributing Covered Code, include this CDDL HEADER in each
150Sstevel@tonic-gate  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
160Sstevel@tonic-gate  * If applicable, add the following below this CDDL HEADER, with the
170Sstevel@tonic-gate  * fields enclosed by brackets "[]" replaced with your own identifying
180Sstevel@tonic-gate  * information: Portions Copyright [yyyy] [name of copyright owner]
190Sstevel@tonic-gate  *
200Sstevel@tonic-gate  * CDDL HEADER END
210Sstevel@tonic-gate  */
220Sstevel@tonic-gate /*
23*940Smrj  * Copyright 2005 Sun Microsystems, Inc.  All rights reserved.
240Sstevel@tonic-gate  * Use is subject to license terms.
250Sstevel@tonic-gate  */
260Sstevel@tonic-gate 
270Sstevel@tonic-gate #pragma ident	"%Z%%M%	%I%	%E% SMI"
280Sstevel@tonic-gate 
290Sstevel@tonic-gate /*
300Sstevel@tonic-gate  * Floppy Disk Controller Driver
310Sstevel@tonic-gate  *
320Sstevel@tonic-gate  *   for the standard PC architecture using the Intel 8272A fdc.
330Sstevel@tonic-gate  *   Note that motor control and drive select use a latch external
340Sstevel@tonic-gate  *   to the fdc.
350Sstevel@tonic-gate  *
360Sstevel@tonic-gate  *   This driver is EISA capable, and uses DMA buffer chaining if available.
370Sstevel@tonic-gate  *   If this driver is attached to the ISA bus nexus (or if the EISA bus driver
380Sstevel@tonic-gate  *   does not support DMA buffer chaining), then the bus driver must ensure
390Sstevel@tonic-gate  *   that dma mapping (breakup) and dma engine requests are properly degraded.
400Sstevel@tonic-gate  */
410Sstevel@tonic-gate 
420Sstevel@tonic-gate /*
430Sstevel@tonic-gate  * hack for bugid 1160621:
440Sstevel@tonic-gate  * workaround compiler optimization bug by turning on DEBUG
450Sstevel@tonic-gate  */
460Sstevel@tonic-gate #ifndef DEBUG
470Sstevel@tonic-gate #define	DEBUG	1
480Sstevel@tonic-gate #endif
490Sstevel@tonic-gate 
500Sstevel@tonic-gate #include <sys/param.h>
510Sstevel@tonic-gate #include <sys/buf.h>
520Sstevel@tonic-gate #include <sys/ioctl.h>
530Sstevel@tonic-gate #include <sys/uio.h>
540Sstevel@tonic-gate #include <sys/open.h>
550Sstevel@tonic-gate #include <sys/conf.h>
560Sstevel@tonic-gate #include <sys/file.h>
570Sstevel@tonic-gate #include <sys/cmn_err.h>
580Sstevel@tonic-gate #include <sys/debug.h>
590Sstevel@tonic-gate #include <sys/kmem.h>
600Sstevel@tonic-gate #include <sys/stat.h>
610Sstevel@tonic-gate 
620Sstevel@tonic-gate #include <sys/autoconf.h>
630Sstevel@tonic-gate #include <sys/dkio.h>
640Sstevel@tonic-gate #include <sys/vtoc.h>
650Sstevel@tonic-gate #include <sys/kstat.h>
660Sstevel@tonic-gate 
670Sstevel@tonic-gate #include <sys/fdio.h>
680Sstevel@tonic-gate #include <sys/fdc.h>
690Sstevel@tonic-gate #include <sys/i8272A.h>
700Sstevel@tonic-gate #include <sys/fd_debug.h>
710Sstevel@tonic-gate #include <sys/promif.h>
720Sstevel@tonic-gate #include <sys/ddi.h>
730Sstevel@tonic-gate #include <sys/sunddi.h>
740Sstevel@tonic-gate 
750Sstevel@tonic-gate /*
760Sstevel@tonic-gate  * bss (uninitialized data)
770Sstevel@tonic-gate  */
780Sstevel@tonic-gate static void *fdc_state_head;		/* opaque handle top of state structs */
790Sstevel@tonic-gate static ddi_dma_attr_t fdc_dma_attr;
80*940Smrj static ddi_device_acc_attr_t fdc_accattr = {DDI_DEVICE_ATTR_V0,
81*940Smrj 	DDI_STRUCTURE_LE_ACC, DDI_STRICTORDER_ACC};
820Sstevel@tonic-gate 
830Sstevel@tonic-gate /*
840Sstevel@tonic-gate  * Local static data
850Sstevel@tonic-gate  */
860Sstevel@tonic-gate #define	OURUN_TRIES	12
870Sstevel@tonic-gate static uchar_t rwretry = 4;
880Sstevel@tonic-gate static uchar_t skretry = 3;
890Sstevel@tonic-gate static uchar_t configurecmd[4] = {FO_CNFG, 0, 0x0F, 0};
900Sstevel@tonic-gate static uchar_t recalcmd[2] = {FO_RECAL, 0};
910Sstevel@tonic-gate static uchar_t senseintcmd = FO_SINT;
920Sstevel@tonic-gate 
930Sstevel@tonic-gate /*
940Sstevel@tonic-gate  * error handling
950Sstevel@tonic-gate  *
960Sstevel@tonic-gate  * for debugging, set rwretry and skretry = 1
970Sstevel@tonic-gate  *		set fcerrlevel to 1
980Sstevel@tonic-gate  *		set fcerrmask  to 224  or 644
990Sstevel@tonic-gate  *
1000Sstevel@tonic-gate  * after debug, set rwretry to 4, skretry to 3, and fcerrlevel to 5
1010Sstevel@tonic-gate  * set fcerrmask to FDEM_ALL
1020Sstevel@tonic-gate  * or remove the define DEBUG
1030Sstevel@tonic-gate  */
1040Sstevel@tonic-gate static uint_t fcerrmask = FDEM_ALL;
1050Sstevel@tonic-gate static int fcerrlevel = 6;
1060Sstevel@tonic-gate 
1070Sstevel@tonic-gate #define	KIOIP	KSTAT_INTR_PTR(fcp->c_intrstat)
1080Sstevel@tonic-gate 
1090Sstevel@tonic-gate 
1100Sstevel@tonic-gate static xlate_tbl_t drate_mfm[] = {
1110Sstevel@tonic-gate 	{  250, 2},
1120Sstevel@tonic-gate 	{  300, 1},
1130Sstevel@tonic-gate 	{  417, 0},
1140Sstevel@tonic-gate 	{  500, 0},
1150Sstevel@tonic-gate 	{ 1000, 3},
1160Sstevel@tonic-gate 	{    0, 0}
1170Sstevel@tonic-gate };
1180Sstevel@tonic-gate 
1190Sstevel@tonic-gate static xlate_tbl_t sector_size[] = {
1200Sstevel@tonic-gate 	{  256, 1},
1210Sstevel@tonic-gate 	{  512, 2},
1220Sstevel@tonic-gate 	{ 1024, 3},
1230Sstevel@tonic-gate 	{    0, 2}
1240Sstevel@tonic-gate };
1250Sstevel@tonic-gate 
1260Sstevel@tonic-gate static xlate_tbl_t motor_onbits[] = {
1270Sstevel@tonic-gate 	{  0, 0x10},
1280Sstevel@tonic-gate 	{  1, 0x20},
1290Sstevel@tonic-gate 	{  2, 0x40},
1300Sstevel@tonic-gate 	{  3, 0x80},
1310Sstevel@tonic-gate 	{  0, 0x80}
1320Sstevel@tonic-gate };
1330Sstevel@tonic-gate 
1340Sstevel@tonic-gate static xlate_tbl_t step_rate[] = {
1350Sstevel@tonic-gate 	{  10, 0xF0},		/* for 500K data rate */
1360Sstevel@tonic-gate 	{  20, 0xE0},
1370Sstevel@tonic-gate 	{  30, 0xD0},
1380Sstevel@tonic-gate 	{  40, 0xC0},
1390Sstevel@tonic-gate 	{  50, 0xB0},
1400Sstevel@tonic-gate 	{  60, 0xA0},
1410Sstevel@tonic-gate 	{  70, 0x90},
1420Sstevel@tonic-gate 	{  80, 0x80},
1430Sstevel@tonic-gate 	{  90, 0x70},
1440Sstevel@tonic-gate 	{ 100, 0x60},
1450Sstevel@tonic-gate 	{ 110, 0x50},
1460Sstevel@tonic-gate 	{ 120, 0x40},
1470Sstevel@tonic-gate 	{ 130, 0x30},
1480Sstevel@tonic-gate 	{ 140, 0x20},
1490Sstevel@tonic-gate 	{ 150, 0x10},
1500Sstevel@tonic-gate 	{ 160, 0x00},
1510Sstevel@tonic-gate 	{   0, 0x00}
1520Sstevel@tonic-gate };
1530Sstevel@tonic-gate 
1540Sstevel@tonic-gate #ifdef notdef
1550Sstevel@tonic-gate static xlate_tbl_t head_unld[] = {
1560Sstevel@tonic-gate 	{  16, 0x1},		/* for 500K data rate */
1570Sstevel@tonic-gate 	{  32, 0x2},
1580Sstevel@tonic-gate 	{  48, 0x3},
1590Sstevel@tonic-gate 	{  64, 0x4},
1600Sstevel@tonic-gate 	{  80, 0x5},
1610Sstevel@tonic-gate 	{  96, 0x6},
1620Sstevel@tonic-gate 	{ 112, 0x7},
1630Sstevel@tonic-gate 	{ 128, 0x8},
1640Sstevel@tonic-gate 	{ 144, 0x9},
1650Sstevel@tonic-gate 	{ 160, 0xA},
1660Sstevel@tonic-gate 	{ 176, 0xB},
1670Sstevel@tonic-gate 	{ 192, 0xC},
1680Sstevel@tonic-gate 	{ 208, 0xD},
1690Sstevel@tonic-gate 	{ 224, 0xE},
1700Sstevel@tonic-gate 	{ 240, 0xF},
1710Sstevel@tonic-gate 	{ 256, 0x0},
1720Sstevel@tonic-gate 	{   0, 0x0}
1730Sstevel@tonic-gate };
1740Sstevel@tonic-gate #endif
1750Sstevel@tonic-gate 
1760Sstevel@tonic-gate static struct fdcmdinfo {
1770Sstevel@tonic-gate 	char *cmdname;		/* command name */
1780Sstevel@tonic-gate 	uchar_t ncmdbytes;	/* number of bytes of command */
1790Sstevel@tonic-gate 	uchar_t nrsltbytes;	/* number of bytes in result */
1800Sstevel@tonic-gate 	uchar_t cmdtype;		/* characteristics */
1810Sstevel@tonic-gate } fdcmds[] = {
1820Sstevel@tonic-gate 	"", 0, 0, 0,			/* - */
1830Sstevel@tonic-gate 	"", 0, 0, 0,			/* - */
1840Sstevel@tonic-gate 	"read_track", 9, 7, 1,		/* 2 */
1850Sstevel@tonic-gate 	"specify", 3, 0, 3,		/* 3 */
1860Sstevel@tonic-gate 	"sense_drv_status", 2, 1, 3,	/* 4 */
1870Sstevel@tonic-gate 	"write", 9, 7, 1,		/* 5 */
1880Sstevel@tonic-gate 	"read", 9, 7, 1,		/* 6 */
1890Sstevel@tonic-gate 	"recalibrate", 2, 0, 2,		/* 7 */
1900Sstevel@tonic-gate 	"sense_int_status", 1, 2, 3,	/* 8 */
1910Sstevel@tonic-gate 	"write_del", 9, 7, 1,		/* 9 */
1920Sstevel@tonic-gate 	"read_id", 2, 7, 2,		/* A */
1930Sstevel@tonic-gate 	"", 0, 0, 0,			/* - */
1940Sstevel@tonic-gate 	"read_del", 9, 7, 1,		/* C */
1950Sstevel@tonic-gate 	"format_track", 10, 7, 1,	/* D */
1960Sstevel@tonic-gate 	"dump_reg", 1, 10, 4,		/* E */
1970Sstevel@tonic-gate 	"seek", 3, 0, 2,		/* F */
1980Sstevel@tonic-gate 	"version", 1, 1, 3,		/* 10 */
1990Sstevel@tonic-gate 	"", 0, 0, 0,			/* - */
2000Sstevel@tonic-gate 	"perp_mode", 2, 0, 3,		/* 12 */
2010Sstevel@tonic-gate 	"configure", 4, 0, 4,		/* 13 */
2020Sstevel@tonic-gate 	/* relative seek */
2030Sstevel@tonic-gate };
2040Sstevel@tonic-gate 
2050Sstevel@tonic-gate 
2060Sstevel@tonic-gate static int
2070Sstevel@tonic-gate fdc_bus_ctl(dev_info_t *, dev_info_t *, ddi_ctl_enum_t, void *, void *);
2080Sstevel@tonic-gate static int get_ioaddr(dev_info_t *dip, int *ioaddr);
2090Sstevel@tonic-gate static int get_unit(dev_info_t *dip, int *cntrl_num);
2100Sstevel@tonic-gate 
2110Sstevel@tonic-gate struct bus_ops fdc_bus_ops = {
2120Sstevel@tonic-gate 	BUSO_REV,
2130Sstevel@tonic-gate 	nullbusmap,
2140Sstevel@tonic-gate 	0,	/* ddi_intrspec_t (*bus_get_intrspec)(); */
2150Sstevel@tonic-gate 	0,	/* int 	(*bus_add_intrspec)(); */
2160Sstevel@tonic-gate 	0,	/* void (*bus_remove_intrspec)(); */
2170Sstevel@tonic-gate 	i_ddi_map_fault,
2180Sstevel@tonic-gate 	ddi_dma_map,
2190Sstevel@tonic-gate 	ddi_dma_allochdl,
2200Sstevel@tonic-gate 	ddi_dma_freehdl,
2210Sstevel@tonic-gate 	ddi_dma_bindhdl,
2220Sstevel@tonic-gate 	ddi_dma_unbindhdl,
2230Sstevel@tonic-gate 	ddi_dma_flush,
2240Sstevel@tonic-gate 	ddi_dma_win,
2250Sstevel@tonic-gate 	ddi_dma_mctl,
2260Sstevel@tonic-gate 	fdc_bus_ctl,
2270Sstevel@tonic-gate 	ddi_bus_prop_op,
2280Sstevel@tonic-gate };
2290Sstevel@tonic-gate 
2300Sstevel@tonic-gate static int fdc_getinfo(dev_info_t *, ddi_info_cmd_t, void *, void **);
2310Sstevel@tonic-gate static int fdc_probe(dev_info_t *);
2320Sstevel@tonic-gate static int fdc_attach(dev_info_t *, ddi_attach_cmd_t);
2330Sstevel@tonic-gate static int fdc_detach(dev_info_t *, ddi_detach_cmd_t);
2340Sstevel@tonic-gate static int fdc_enhance_probe(struct fdcntlr *fcp);
2350Sstevel@tonic-gate 
2360Sstevel@tonic-gate struct dev_ops	fdc_ops = {
2370Sstevel@tonic-gate 	DEVO_REV,		/* devo_rev, */
2380Sstevel@tonic-gate 	0,			/* refcnt  */
2390Sstevel@tonic-gate 	fdc_getinfo,		/* getinfo */
2400Sstevel@tonic-gate 	nulldev,		/* identify */
2410Sstevel@tonic-gate 	fdc_probe,		/* probe */
2420Sstevel@tonic-gate 	fdc_attach,		/* attach */
2430Sstevel@tonic-gate 	fdc_detach,		/* detach */
2440Sstevel@tonic-gate 	nodev,			/* reset */
2450Sstevel@tonic-gate 	(struct cb_ops *)0,	/* driver operations */
2460Sstevel@tonic-gate 	&fdc_bus_ops		/* bus operations */
2470Sstevel@tonic-gate };
2480Sstevel@tonic-gate 
2490Sstevel@tonic-gate /*
2500Sstevel@tonic-gate  * This is the loadable module wrapper.
2510Sstevel@tonic-gate  */
2520Sstevel@tonic-gate #include <sys/modctl.h>
2530Sstevel@tonic-gate 
2540Sstevel@tonic-gate extern struct mod_ops mod_driverops;
2550Sstevel@tonic-gate 
2560Sstevel@tonic-gate static struct modldrv modldrv = {
2570Sstevel@tonic-gate 	&mod_driverops,		/* Type of module. This one is a driver */
2580Sstevel@tonic-gate 	"Floppy Controller %I%",	/* Name of the module. */
2590Sstevel@tonic-gate 	&fdc_ops,		/* Driver ops vector */
2600Sstevel@tonic-gate };
2610Sstevel@tonic-gate 
2620Sstevel@tonic-gate static struct modlinkage modlinkage = {
2630Sstevel@tonic-gate 	MODREV_1, (void *)&modldrv, NULL
2640Sstevel@tonic-gate };
2650Sstevel@tonic-gate 
2660Sstevel@tonic-gate int
2670Sstevel@tonic-gate _init(void)
2680Sstevel@tonic-gate {
2690Sstevel@tonic-gate 	int retval;
2700Sstevel@tonic-gate 
2710Sstevel@tonic-gate 	if ((retval = ddi_soft_state_init(&fdc_state_head,
2720Sstevel@tonic-gate 	    sizeof (struct fdcntlr) + NFDUN * sizeof (struct fcu_obj), 0)) != 0)
2730Sstevel@tonic-gate 		return (retval);
2740Sstevel@tonic-gate 
2750Sstevel@tonic-gate 	if ((retval = mod_install(&modlinkage)) != 0)
2760Sstevel@tonic-gate 		ddi_soft_state_fini(&fdc_state_head);
2770Sstevel@tonic-gate 	return (retval);
2780Sstevel@tonic-gate }
2790Sstevel@tonic-gate 
2800Sstevel@tonic-gate int
2810Sstevel@tonic-gate _fini(void)
2820Sstevel@tonic-gate {
2830Sstevel@tonic-gate 	int retval;
2840Sstevel@tonic-gate 
2850Sstevel@tonic-gate 	if ((retval = mod_remove(&modlinkage)) != 0)
2860Sstevel@tonic-gate 		return (retval);
2870Sstevel@tonic-gate 	ddi_soft_state_fini(&fdc_state_head);
2880Sstevel@tonic-gate 	return (retval);
2890Sstevel@tonic-gate }
2900Sstevel@tonic-gate 
2910Sstevel@tonic-gate int
2920Sstevel@tonic-gate _info(struct modinfo *modinfop)
2930Sstevel@tonic-gate {
2940Sstevel@tonic-gate 	return (mod_info(&modlinkage, modinfop));
2950Sstevel@tonic-gate }
2960Sstevel@tonic-gate 
2970Sstevel@tonic-gate 
2980Sstevel@tonic-gate int fdc_start(struct fcu_obj *);
2990Sstevel@tonic-gate int fdc_abort(struct fcu_obj *);
3000Sstevel@tonic-gate int fdc_getcap(struct fcu_obj *, char *, int);
3010Sstevel@tonic-gate int fdc_setcap(struct fcu_obj *, char *, int, int);
3020Sstevel@tonic-gate int fdc_dkinfo(struct fcu_obj *, struct dk_cinfo *);
3030Sstevel@tonic-gate int fdc_select(struct fcu_obj *, int, int);
3040Sstevel@tonic-gate int fdgetchng(struct fcu_obj *, int);
3050Sstevel@tonic-gate int fdresetchng(struct fcu_obj *, int);
3060Sstevel@tonic-gate int fdrecalseek(struct fcu_obj *, int, int, int);
3070Sstevel@tonic-gate int fdrw(struct fcu_obj *, int, int, int, int, int, caddr_t, uint_t);
3080Sstevel@tonic-gate int fdtrkformat(struct fcu_obj *, int, int, int, int);
3090Sstevel@tonic-gate int fdrawioctl(struct fcu_obj *, int, caddr_t);
3100Sstevel@tonic-gate 
3110Sstevel@tonic-gate static struct fcobjops fdc_iops = {
3120Sstevel@tonic-gate 		fdc_start,	/* controller start */
3130Sstevel@tonic-gate 		fdc_abort,	/* controller abort */
3140Sstevel@tonic-gate 		fdc_getcap,	/* capability retrieval */
3150Sstevel@tonic-gate 		fdc_setcap,	/* capability establishment */
3160Sstevel@tonic-gate 		fdc_dkinfo,	/* get disk controller info */
3170Sstevel@tonic-gate 
3180Sstevel@tonic-gate 		fdc_select,	/* select / deselect unit */
3190Sstevel@tonic-gate 		fdgetchng,	/* get media change */
3200Sstevel@tonic-gate 		fdresetchng,	/* reset media change */
3210Sstevel@tonic-gate 		fdrecalseek,	/* recal / seek */
322*940Smrj 		NULL,		/* read /write request (UNUSED) */
3230Sstevel@tonic-gate 		fdrw,		/* read /write sector */
3240Sstevel@tonic-gate 		fdtrkformat,	/* format track */
3250Sstevel@tonic-gate 		fdrawioctl	/* raw ioctl */
3260Sstevel@tonic-gate };
3270Sstevel@tonic-gate 
3280Sstevel@tonic-gate 
3290Sstevel@tonic-gate /*
3300Sstevel@tonic-gate  * Function prototypes
3310Sstevel@tonic-gate  */
3320Sstevel@tonic-gate void encode(xlate_tbl_t *tablep, int val, uchar_t *rcode);
3330Sstevel@tonic-gate int decode(xlate_tbl_t *, int, int *);
3340Sstevel@tonic-gate static int fdc_propinit1(struct fdcntlr *, int);
3350Sstevel@tonic-gate static void fdc_propinit2(struct fdcntlr *, int);
3360Sstevel@tonic-gate void fdcquiesce(struct fdcntlr *);
3370Sstevel@tonic-gate int fdcsense_chng(struct fdcntlr *, int);
3380Sstevel@tonic-gate int fdcsense_drv(struct fdcntlr *, int);
3390Sstevel@tonic-gate int fdcsense_int(struct fdcntlr *, int *, int *);
3400Sstevel@tonic-gate int fdcspecify(struct fdcntlr *, int, int, int);
3410Sstevel@tonic-gate int fdcspdchange(struct fdcntlr *, struct fcu_obj *, int);
3420Sstevel@tonic-gate static int fdc_exec(struct fdcntlr *, int, int);
3430Sstevel@tonic-gate int fdcheckdisk(struct fdcntlr *, int);
3440Sstevel@tonic-gate static uint_t fdc_intr(caddr_t arg);
3450Sstevel@tonic-gate static void fdwatch(void *arg);
3460Sstevel@tonic-gate static void fdmotort(void *arg);
3470Sstevel@tonic-gate static int fdrecover(struct fdcntlr *);
3480Sstevel@tonic-gate static int fdc_motorsm(struct fcu_obj *, int, int);
3490Sstevel@tonic-gate static int fdc_statemach(struct fdcntlr *);
3500Sstevel@tonic-gate int fdc_docmd(struct fdcntlr *, uchar_t *, uchar_t);
3510Sstevel@tonic-gate int fdc_result(struct fdcntlr *, uchar_t *, uchar_t);
3520Sstevel@tonic-gate 
3530Sstevel@tonic-gate 
3540Sstevel@tonic-gate /* ARGSUSED */
3550Sstevel@tonic-gate static int
3560Sstevel@tonic-gate fdc_bus_ctl(dev_info_t *dip, dev_info_t *rdip, ddi_ctl_enum_t ctlop,
3570Sstevel@tonic-gate     void *arg, void *result)
3580Sstevel@tonic-gate {
3590Sstevel@tonic-gate 	struct 	fdcntlr *fcp;
3600Sstevel@tonic-gate 	struct	fcu_obj *fjp;
3610Sstevel@tonic-gate 
3620Sstevel@tonic-gate 	FCERRPRINT(FDEP_L0, FDEM_ATTA,
3630Sstevel@tonic-gate 	    (CE_CONT, "fdc_bus_ctl: cmd= %x\n", ctlop));
3640Sstevel@tonic-gate 
3650Sstevel@tonic-gate 	if ((fcp = ddi_get_driver_private(dip)) == NULL)
3660Sstevel@tonic-gate 		return (DDI_FAILURE);
3670Sstevel@tonic-gate 
3680Sstevel@tonic-gate 	switch (ctlop) {
3690Sstevel@tonic-gate 
3700Sstevel@tonic-gate 	case DDI_CTLOPS_REPORTDEV:
3710Sstevel@tonic-gate 		cmn_err(CE_CONT, "?%s%d at %s%d\n",
3720Sstevel@tonic-gate 		    ddi_get_name(rdip), ddi_get_instance(rdip),
3730Sstevel@tonic-gate 		    ddi_get_name(dip), ddi_get_instance(dip));
3740Sstevel@tonic-gate 		FCERRPRINT(FDEP_L3, FDEM_ATTA,
3750Sstevel@tonic-gate 		    (CE_WARN, "fdc_bus_ctl: report %s%d at %s%d",
3760Sstevel@tonic-gate 		    ddi_get_name(rdip), ddi_get_instance(rdip),
3770Sstevel@tonic-gate 		    ddi_get_name(dip), ddi_get_instance(dip)));
3780Sstevel@tonic-gate 		return (DDI_SUCCESS);
3790Sstevel@tonic-gate 
3800Sstevel@tonic-gate 	case DDI_CTLOPS_INITCHILD:
3810Sstevel@tonic-gate 	{
3820Sstevel@tonic-gate 		dev_info_t *udip = (dev_info_t *)arg;
3830Sstevel@tonic-gate 		int cntlr;
3840Sstevel@tonic-gate 		int len;
3850Sstevel@tonic-gate 		int unit;
3860Sstevel@tonic-gate 		char name[MAXNAMELEN];
3870Sstevel@tonic-gate 
3880Sstevel@tonic-gate 		FCERRPRINT(FDEP_L3, FDEM_ATTA,
3890Sstevel@tonic-gate 		    (CE_WARN, "fdc_bus_ctl: init child 0x%p", (void*)udip));
3900Sstevel@tonic-gate 		cntlr = fcp->c_number;
3910Sstevel@tonic-gate 
3920Sstevel@tonic-gate 		len = sizeof (unit);
3930Sstevel@tonic-gate 		if (ddi_prop_op(DDI_DEV_T_ANY, udip, PROP_LEN_AND_VAL_BUF,
3940Sstevel@tonic-gate 		    DDI_PROP_DONTPASS, "unit", (caddr_t)&unit, &len)
3950Sstevel@tonic-gate 		    != DDI_PROP_SUCCESS ||
3960Sstevel@tonic-gate 		    cntlr != FDCTLR(unit) ||
3970Sstevel@tonic-gate 		    (fcp->c_unit[FDUNIT(unit)])->fj_dip)
3980Sstevel@tonic-gate 			return (DDI_NOT_WELL_FORMED);
3990Sstevel@tonic-gate 
4000Sstevel@tonic-gate 		(void) sprintf(name, "%d,%d", cntlr, FDUNIT(unit));
4010Sstevel@tonic-gate 		ddi_set_name_addr(udip, name);
4020Sstevel@tonic-gate 
4030Sstevel@tonic-gate 		fjp = fcp->c_unit[FDUNIT(unit)];
4040Sstevel@tonic-gate 		fjp->fj_unit = unit;
4050Sstevel@tonic-gate 		fjp->fj_dip = udip;
4060Sstevel@tonic-gate 		fjp->fj_ops = &fdc_iops;
4070Sstevel@tonic-gate 		fjp->fj_fdc = fcp;
4080Sstevel@tonic-gate 		fjp->fj_iblock = &fcp->c_iblock;
4090Sstevel@tonic-gate 
4100Sstevel@tonic-gate 		ddi_set_driver_private(udip, fjp);
4110Sstevel@tonic-gate 
4120Sstevel@tonic-gate 		return (DDI_SUCCESS);
4130Sstevel@tonic-gate 	}
4140Sstevel@tonic-gate 	case DDI_CTLOPS_UNINITCHILD:
4150Sstevel@tonic-gate 	{
4160Sstevel@tonic-gate 		dev_info_t *udip = (dev_info_t *)arg;
4170Sstevel@tonic-gate 
4180Sstevel@tonic-gate 		FCERRPRINT(FDEP_L3, FDEM_ATTA,
4190Sstevel@tonic-gate 		    (CE_WARN, "fdc_bus_ctl: uninit child 0x%p", (void *)udip));
4200Sstevel@tonic-gate 		fjp = ddi_get_driver_private(udip);
4210Sstevel@tonic-gate 		ddi_set_driver_private(udip, NULL);
4220Sstevel@tonic-gate 		fjp->fj_dip = NULL;
4230Sstevel@tonic-gate 		ddi_set_name_addr(udip, NULL);
4240Sstevel@tonic-gate 		return (DDI_SUCCESS);
4250Sstevel@tonic-gate 	}
4260Sstevel@tonic-gate 	default:
4270Sstevel@tonic-gate 		return (DDI_FAILURE);
4280Sstevel@tonic-gate 	}
4290Sstevel@tonic-gate }
4300Sstevel@tonic-gate 
4310Sstevel@tonic-gate /* ARGSUSED */
4320Sstevel@tonic-gate static int
4330Sstevel@tonic-gate fdc_getinfo(dev_info_t *dip, ddi_info_cmd_t cmd, void *arg, void **result)
4340Sstevel@tonic-gate {
4350Sstevel@tonic-gate 	struct fdcntlr *fcp;
4360Sstevel@tonic-gate 	int rval;
4370Sstevel@tonic-gate 
4380Sstevel@tonic-gate 	switch (cmd) {
4390Sstevel@tonic-gate 	case DDI_INFO_DEVT2DEVINFO:
4400Sstevel@tonic-gate 		if (fcp = ddi_get_soft_state(fdc_state_head, (dev_t)arg)) {
4410Sstevel@tonic-gate 			*result = fcp->c_dip;
4420Sstevel@tonic-gate 			rval = DDI_SUCCESS;
4430Sstevel@tonic-gate 			break;
4440Sstevel@tonic-gate 		} else {
4450Sstevel@tonic-gate 			rval = DDI_FAILURE;
4460Sstevel@tonic-gate 			break;
4470Sstevel@tonic-gate 		}
4480Sstevel@tonic-gate 	case DDI_INFO_DEVT2INSTANCE:
4490Sstevel@tonic-gate 		*result = (void *)(uintptr_t)getminor((dev_t)arg);
4500Sstevel@tonic-gate 		rval = DDI_SUCCESS;
4510Sstevel@tonic-gate 		break;
4520Sstevel@tonic-gate 	default:
4530Sstevel@tonic-gate 		rval = DDI_FAILURE;
4540Sstevel@tonic-gate 	}
4550Sstevel@tonic-gate 	return (rval);
4560Sstevel@tonic-gate }
4570Sstevel@tonic-gate 
4580Sstevel@tonic-gate static int
4590Sstevel@tonic-gate fdc_probe(dev_info_t *dip)
4600Sstevel@tonic-gate {
4610Sstevel@tonic-gate 	int	debug[2];
4620Sstevel@tonic-gate 	int ioaddr;
4630Sstevel@tonic-gate 	int	len;
4640Sstevel@tonic-gate 	uchar_t	stat;
4650Sstevel@tonic-gate 
4660Sstevel@tonic-gate 	len = sizeof (debug);
4670Sstevel@tonic-gate 	if (ddi_prop_op(DDI_DEV_T_ANY, dip, PROP_LEN_AND_VAL_BUF,
4680Sstevel@tonic-gate 	    DDI_PROP_DONTPASS, "debug", (caddr_t)debug, &len) ==
4690Sstevel@tonic-gate 	    DDI_PROP_SUCCESS) {
4700Sstevel@tonic-gate 		fcerrlevel = debug[0];
4710Sstevel@tonic-gate 		fcerrmask = (uint_t)debug[1];
4720Sstevel@tonic-gate 	}
4730Sstevel@tonic-gate 
4740Sstevel@tonic-gate 	FCERRPRINT(FDEP_L3, FDEM_ATTA, (CE_WARN, "fdc_probe: dip %p",
4750Sstevel@tonic-gate 		(void*)dip));
4760Sstevel@tonic-gate 
4770Sstevel@tonic-gate 	if (get_ioaddr(dip, &ioaddr) != DDI_SUCCESS)
4780Sstevel@tonic-gate 		return (DDI_PROBE_FAILURE);
4790Sstevel@tonic-gate 
4800Sstevel@tonic-gate 	stat = inb(ioaddr + FCR_MSR);
4810Sstevel@tonic-gate 	if ((stat & (MS_RQM | MS_DIO | MS_CB)) != MS_RQM &&
4820Sstevel@tonic-gate 	    (stat & ~MS_DIO) != MS_CB)
4830Sstevel@tonic-gate 		return (DDI_PROBE_FAILURE);
4840Sstevel@tonic-gate 
4850Sstevel@tonic-gate 	return (DDI_PROBE_SUCCESS);
4860Sstevel@tonic-gate }
4870Sstevel@tonic-gate 
4880Sstevel@tonic-gate /* ARGSUSED */
4890Sstevel@tonic-gate static int
4900Sstevel@tonic-gate fdc_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
4910Sstevel@tonic-gate {
4920Sstevel@tonic-gate 	struct fdcntlr *fcp;
4930Sstevel@tonic-gate 	struct fcu_obj *fjp;
4940Sstevel@tonic-gate 	int cntlr_num, ctlr, unit;
4950Sstevel@tonic-gate 	int intr_set = 0;
4960Sstevel@tonic-gate 	int len;
4970Sstevel@tonic-gate 	char name[MAXNAMELEN];
4980Sstevel@tonic-gate 
4990Sstevel@tonic-gate 	FCERRPRINT(FDEP_L3, FDEM_ATTA, (CE_WARN, "fdc_attach: dip %p",
5000Sstevel@tonic-gate 		(void*)dip));
5010Sstevel@tonic-gate 
5020Sstevel@tonic-gate 	switch (cmd) {
5030Sstevel@tonic-gate 	case DDI_ATTACH:
5040Sstevel@tonic-gate 		if (ddi_getprop
5050Sstevel@tonic-gate 		    (DDI_DEV_T_ANY, dip, 0, "ignore-hardware-nodes", 0)) {
5060Sstevel@tonic-gate 			len = sizeof (cntlr_num);
5070Sstevel@tonic-gate 			if (ddi_prop_op(DDI_DEV_T_ANY, dip,
5080Sstevel@tonic-gate 			    PROP_LEN_AND_VAL_BUF, DDI_PROP_DONTPASS, "unit",
5090Sstevel@tonic-gate 			    (caddr_t)&cntlr_num, &len) != DDI_PROP_SUCCESS) {
5100Sstevel@tonic-gate 				FCERRPRINT(FDEP_L3, FDEM_ATTA, (CE_WARN,
5110Sstevel@tonic-gate 				    "fdc_attach failed: dip %p", (void*)dip));
5120Sstevel@tonic-gate 				return (DDI_FAILURE);
5130Sstevel@tonic-gate 			}
5140Sstevel@tonic-gate 		} else {
5150Sstevel@tonic-gate 			if (get_unit(dip, &cntlr_num) != DDI_SUCCESS)
5160Sstevel@tonic-gate 				return (DDI_FAILURE);
5170Sstevel@tonic-gate 		}
5180Sstevel@tonic-gate 
5190Sstevel@tonic-gate 		ctlr = ddi_get_instance(dip);
5200Sstevel@tonic-gate 		if (ddi_soft_state_zalloc(fdc_state_head, ctlr) != 0)
5210Sstevel@tonic-gate 			return (DDI_FAILURE);
5220Sstevel@tonic-gate 		fcp = ddi_get_soft_state(fdc_state_head, ctlr);
5230Sstevel@tonic-gate 
5240Sstevel@tonic-gate 		for (unit = 0, fjp = (struct fcu_obj *)(fcp+1);
5250Sstevel@tonic-gate 		    unit < NFDUN; unit++) {
5260Sstevel@tonic-gate 			fcp->c_unit[unit] = fjp++;
5270Sstevel@tonic-gate 		}
5280Sstevel@tonic-gate 		fcp->c_dip = dip;
5290Sstevel@tonic-gate 
5300Sstevel@tonic-gate 		if (fdc_propinit1(fcp, cntlr_num) != DDI_SUCCESS)
5310Sstevel@tonic-gate 			goto no_attach;
5320Sstevel@tonic-gate 
5330Sstevel@tonic-gate 		/* get iblock cookie to initialize mutex used in the ISR */
5340Sstevel@tonic-gate 		if (ddi_get_iblock_cookie(dip, (uint_t)0, &fcp->c_iblock) !=
5350Sstevel@tonic-gate 		    DDI_SUCCESS) {
5360Sstevel@tonic-gate 			cmn_err(CE_WARN,
5370Sstevel@tonic-gate 			    "fdc_attach: cannot get iblock cookie");
5380Sstevel@tonic-gate 			goto no_attach;
5390Sstevel@tonic-gate 		}
5400Sstevel@tonic-gate 		mutex_init(&fcp->c_lock, NULL, MUTEX_DRIVER, fcp->c_iblock);
5410Sstevel@tonic-gate 		intr_set = 1;
5420Sstevel@tonic-gate 
5430Sstevel@tonic-gate 		/* setup interrupt handler */
5440Sstevel@tonic-gate 		if (ddi_add_intr(dip, (uint_t)0, NULL,
5450Sstevel@tonic-gate 		    (ddi_idevice_cookie_t *)0, fdc_intr, (caddr_t)fcp) !=
5460Sstevel@tonic-gate 		    DDI_SUCCESS) {
5470Sstevel@tonic-gate 			cmn_err(CE_WARN, "fdc: cannot add intr\n");
5480Sstevel@tonic-gate 			goto no_attach;
5490Sstevel@tonic-gate 		}
5500Sstevel@tonic-gate 		intr_set++;
5510Sstevel@tonic-gate 
5520Sstevel@tonic-gate 		/*
5530Sstevel@tonic-gate 		 * acquire the DMA channel
5540Sstevel@tonic-gate 		 * this assumes that the chnl is not shared; else allocate
5550Sstevel@tonic-gate 		 * and free the chnl with each fdc request
5560Sstevel@tonic-gate 		 */
5570Sstevel@tonic-gate 		if (ddi_dmae_alloc(dip, fcp->c_dmachan, DDI_DMA_DONTWAIT, NULL)
5580Sstevel@tonic-gate 		    != DDI_SUCCESS) {
5590Sstevel@tonic-gate 			cmn_err(CE_WARN, "fdc: cannot acquire dma%d\n",
5600Sstevel@tonic-gate 			    fcp->c_dmachan);
5610Sstevel@tonic-gate 			goto no_attach;
5620Sstevel@tonic-gate 		}
5630Sstevel@tonic-gate 		(void) ddi_dmae_getattr(dip, &fdc_dma_attr);
564*940Smrj 		fdc_dma_attr.dma_attr_align = MMU_PAGESIZE;
5650Sstevel@tonic-gate 
5660Sstevel@tonic-gate 		mutex_init(&fcp->c_dorlock, NULL, MUTEX_DRIVER, fcp->c_iblock);
5670Sstevel@tonic-gate 		cv_init(&fcp->c_iocv, NULL, CV_DRIVER, fcp->c_iblock);
5680Sstevel@tonic-gate 		sema_init(&fcp->c_selsem, 1, NULL, SEMA_DRIVER, NULL);
5690Sstevel@tonic-gate 
5700Sstevel@tonic-gate 		(void) sprintf(name, "fdc%d", ctlr);
5710Sstevel@tonic-gate 		fcp->c_intrstat = kstat_create("fdc", ctlr, name,
5720Sstevel@tonic-gate 		    "controller", KSTAT_TYPE_INTR, 1, KSTAT_FLAG_PERSISTENT);
5730Sstevel@tonic-gate 		if (fcp->c_intrstat) {
5740Sstevel@tonic-gate 			kstat_install(fcp->c_intrstat);
5750Sstevel@tonic-gate 		}
5760Sstevel@tonic-gate 
5770Sstevel@tonic-gate 		ddi_set_driver_private(dip, fcp);
5780Sstevel@tonic-gate 
5790Sstevel@tonic-gate 		/*
5800Sstevel@tonic-gate 		 * reset the controller
5810Sstevel@tonic-gate 		 */
5820Sstevel@tonic-gate 		sema_p(&fcp->c_selsem);
5830Sstevel@tonic-gate 		mutex_enter(&fcp->c_lock);
5840Sstevel@tonic-gate 		fcp->c_csb.csb_xstate = FXS_RESET;
5850Sstevel@tonic-gate 		fcp->c_flags |= FCFLG_WAITING;
5860Sstevel@tonic-gate 		fdcquiesce(fcp);
5870Sstevel@tonic-gate 
5880Sstevel@tonic-gate 		/* first test for mode == Model 30 */
5890Sstevel@tonic-gate 		fcp->c_mode = (inb(fcp->c_regbase + FCR_SRB) & 0x1c) ?
5900Sstevel@tonic-gate 		    FDCMODE_AT : FDCMODE_30;
5910Sstevel@tonic-gate 
5920Sstevel@tonic-gate 		while (fcp->c_flags & FCFLG_WAITING) {
5930Sstevel@tonic-gate 			cv_wait(&fcp->c_iocv, &fcp->c_lock);
5940Sstevel@tonic-gate 		}
5950Sstevel@tonic-gate 		mutex_exit(&fcp->c_lock);
5960Sstevel@tonic-gate 		sema_v(&fcp->c_selsem);
5970Sstevel@tonic-gate 
5980Sstevel@tonic-gate 		fdc_propinit2(fcp, cntlr_num);
5990Sstevel@tonic-gate 
6000Sstevel@tonic-gate 		ddi_report_dev(dip);
6010Sstevel@tonic-gate 		return (DDI_SUCCESS);
6020Sstevel@tonic-gate 
6030Sstevel@tonic-gate 	default:
6040Sstevel@tonic-gate 		return (DDI_FAILURE);
6050Sstevel@tonic-gate 	}
6060Sstevel@tonic-gate 
6070Sstevel@tonic-gate no_attach:
6080Sstevel@tonic-gate 	if (intr_set) {
6090Sstevel@tonic-gate 		if (intr_set > 1)
6100Sstevel@tonic-gate 			ddi_remove_intr(dip, 0, fcp->c_iblock);
6110Sstevel@tonic-gate 		mutex_destroy(&fcp->c_lock);
6120Sstevel@tonic-gate 	}
6130Sstevel@tonic-gate 	ddi_soft_state_free(fdc_state_head, cntlr_num);
6140Sstevel@tonic-gate 	return (DDI_FAILURE);
6150Sstevel@tonic-gate }
6160Sstevel@tonic-gate 
6170Sstevel@tonic-gate static int
6180Sstevel@tonic-gate fdc_propinit1(struct fdcntlr *fcp, int cntlr)
6190Sstevel@tonic-gate {
6200Sstevel@tonic-gate 	dev_info_t *dip;
6210Sstevel@tonic-gate 	int len;
6220Sstevel@tonic-gate 	int value;
6230Sstevel@tonic-gate 
6240Sstevel@tonic-gate 	dip = fcp->c_dip;
6250Sstevel@tonic-gate 	len = sizeof (value);
6260Sstevel@tonic-gate 
6270Sstevel@tonic-gate 	if (get_ioaddr(dip, &value) != DDI_SUCCESS)
6280Sstevel@tonic-gate 		return (DDI_FAILURE);
6290Sstevel@tonic-gate 
6300Sstevel@tonic-gate 	fcp->c_regbase = (ushort_t)value;
6310Sstevel@tonic-gate 
6320Sstevel@tonic-gate 	if (ddi_prop_op(DDI_DEV_T_ANY, dip, PROP_LEN_AND_VAL_BUF,
6330Sstevel@tonic-gate 	    DDI_PROP_DONTPASS, "dma-channels", (caddr_t)&value, &len)
6340Sstevel@tonic-gate 	    != DDI_PROP_SUCCESS) {
6350Sstevel@tonic-gate 			cmn_err(CE_WARN,
6360Sstevel@tonic-gate 			    "fdc_attach: Error, could not find a dma channel");
6370Sstevel@tonic-gate 			return (DDI_FAILURE);
6380Sstevel@tonic-gate 	}
6390Sstevel@tonic-gate 	fcp->c_dmachan = (ushort_t)value;
6400Sstevel@tonic-gate 	fcp->c_number = cntlr;
6410Sstevel@tonic-gate 	return (DDI_SUCCESS);
6420Sstevel@tonic-gate }
6430Sstevel@tonic-gate 
6440Sstevel@tonic-gate /* ARGSUSED */
6450Sstevel@tonic-gate static void
6460Sstevel@tonic-gate fdc_propinit2(struct fdcntlr *fcp, int cntlr)
6470Sstevel@tonic-gate {
6480Sstevel@tonic-gate 	dev_info_t *dip;
6490Sstevel@tonic-gate 	int ccr;
6500Sstevel@tonic-gate 	int len;
6510Sstevel@tonic-gate 	int value;
6520Sstevel@tonic-gate 
6530Sstevel@tonic-gate 	dip = fcp->c_dip;
6540Sstevel@tonic-gate 	len = sizeof (value);
6550Sstevel@tonic-gate 
6560Sstevel@tonic-gate 	if (ddi_prop_op(DDI_DEV_T_ANY, dip, PROP_LEN_AND_VAL_BUF,
6570Sstevel@tonic-gate 	    DDI_PROP_DONTPASS, "chip", (caddr_t)&value, &len)
6580Sstevel@tonic-gate 	    == DDI_PROP_SUCCESS)
6590Sstevel@tonic-gate 		fcp->c_chip = value;
6600Sstevel@tonic-gate 	else {
6610Sstevel@tonic-gate 		static uchar_t perpindcmd[2] = {FO_PERP, 0};
6620Sstevel@tonic-gate 		static uchar_t versioncmd = FO_VRSN;
6630Sstevel@tonic-gate 		uchar_t result;
6640Sstevel@tonic-gate 
6650Sstevel@tonic-gate 		fcp->c_chip = i8272A;
6660Sstevel@tonic-gate 		(void) fdc_docmd(fcp, &versioncmd, 1);
6670Sstevel@tonic-gate 		/*
6680Sstevel@tonic-gate 		 * Ignored return. If failed, warning was issued by fdc_docmd.
6690Sstevel@tonic-gate 		 * fdc_results retrieves the controller/drive status
6700Sstevel@tonic-gate 		 */
6710Sstevel@tonic-gate 		if (!fdc_result(fcp, &result, 1) && result == 0x90) {
6720Sstevel@tonic-gate 			/*
6730Sstevel@tonic-gate 			 * try a perpendicular_mode cmd to ensure
6740Sstevel@tonic-gate 			 * that we really have an enhanced controller
6750Sstevel@tonic-gate 			 */
6760Sstevel@tonic-gate 			if (fdc_docmd(fcp, perpindcmd, 2) ||
6770Sstevel@tonic-gate 			    fdc_docmd(fcp, configurecmd, 4))
6780Sstevel@tonic-gate 				/*
6790Sstevel@tonic-gate 				 * perpindicular_mode will be rejected by
6800Sstevel@tonic-gate 				 * older controllers; make sure we don't hang.
6810Sstevel@tonic-gate 				 */
6820Sstevel@tonic-gate 				(void) fdc_result(fcp, &result, 1);
6830Sstevel@tonic-gate 				/*
6840Sstevel@tonic-gate 				 * Ignored return. If failed, warning was
6850Sstevel@tonic-gate 				 * issued by fdc_result.
6860Sstevel@tonic-gate 				 */
6870Sstevel@tonic-gate 			else
6880Sstevel@tonic-gate 				/* enhanced type controller */
6890Sstevel@tonic-gate 
6900Sstevel@tonic-gate 				if ((fcp->c_chip = fdc_enhance_probe(fcp)) == 0)
6910Sstevel@tonic-gate 					/* default enhanced cntlr */
6920Sstevel@tonic-gate 					fcp->c_chip = i82077;
6930Sstevel@tonic-gate 		}
6940Sstevel@tonic-gate 		(void) ddi_prop_update_int(DDI_DEV_T_NONE, dip,
6950Sstevel@tonic-gate 		    "chip", fcp->c_chip);
6960Sstevel@tonic-gate 		/*
6970Sstevel@tonic-gate 		 * Ignoring return value because, for passed arguments, only
6980Sstevel@tonic-gate 		 * DDI_SUCCESS is returned.
6990Sstevel@tonic-gate 		 */
7000Sstevel@tonic-gate 	}
7010Sstevel@tonic-gate 	if (fcp->c_chip >= i82077 && fcp->c_mode == FDCMODE_30 &&
7020Sstevel@tonic-gate 	    (inb(fcp->c_regbase + FCR_DIR) & 0x70) == 0)
7030Sstevel@tonic-gate 		for (ccr = 0; ccr <= (FCC_NOPREC | FCC_DRATE); ccr++) {
7040Sstevel@tonic-gate 			/*
7050Sstevel@tonic-gate 			 * run through all the combinations of NOPREC and
7060Sstevel@tonic-gate 			 * datarate selection, and see if they show up in the
7070Sstevel@tonic-gate 			 * Model 30 DIR
7080Sstevel@tonic-gate 			 */
7090Sstevel@tonic-gate 			outb(fcp->c_regbase + FCR_CCR, ccr);
7100Sstevel@tonic-gate 			drv_usecwait(5);
7110Sstevel@tonic-gate 			if ((inb(fcp->c_regbase + FCR_DIR) &
7120Sstevel@tonic-gate 			    (FCC_NOPREC | FCC_DRATE)) != ccr) {
7130Sstevel@tonic-gate 				fcp->c_mode = FDCMODE_AT;
7140Sstevel@tonic-gate 				break;
7150Sstevel@tonic-gate 			}
7160Sstevel@tonic-gate 		}
7170Sstevel@tonic-gate 	else
7180Sstevel@tonic-gate 		fcp->c_mode = FDCMODE_AT;
7190Sstevel@tonic-gate 	outb(fcp->c_regbase + FCR_CCR, 0);
7200Sstevel@tonic-gate }
7210Sstevel@tonic-gate 
7220Sstevel@tonic-gate /* ARGSUSED */
7230Sstevel@tonic-gate static int
7240Sstevel@tonic-gate fdc_enhance_probe(struct fdcntlr *fcp)
7250Sstevel@tonic-gate {
7260Sstevel@tonic-gate 	static uchar_t nsccmd = FO_NSC;
7270Sstevel@tonic-gate 	uint_t	ddic;
7280Sstevel@tonic-gate 	int	retcode = 0;
7290Sstevel@tonic-gate 	uchar_t	result;
7300Sstevel@tonic-gate 	uchar_t	save;
7310Sstevel@tonic-gate 
7320Sstevel@tonic-gate 	/*
7330Sstevel@tonic-gate 	 * Try to identify the enhanced floppy controller.
7340Sstevel@tonic-gate 	 * This is required so that we can program the DENSEL output to
7350Sstevel@tonic-gate 	 * control 3D mode (1.0 MB, 1.6 MB and 2.0 MB unformatted capacity,
7360Sstevel@tonic-gate 	 * 720 KB, 1.2 MB, and 1.44 MB formatted capacity) 3.5" dual-speed
7370Sstevel@tonic-gate 	 * floppy drives.  Refer to bugid 1195155.
7380Sstevel@tonic-gate 	 */
7390Sstevel@tonic-gate 
7400Sstevel@tonic-gate 	(void) fdc_docmd(fcp, &nsccmd, 1);
7410Sstevel@tonic-gate 	/*
7420Sstevel@tonic-gate 	 * Ignored return. If failed, warning was issued by fdc_docmd.
7430Sstevel@tonic-gate 	 * fdc_results retrieves the controller/drive status
7440Sstevel@tonic-gate 	 */
7450Sstevel@tonic-gate 	if (!fdc_result(fcp, &result, 1) && result != S0_IVCMD) {
7460Sstevel@tonic-gate 		/*
7470Sstevel@tonic-gate 		 * only enhanced National Semi PC8477 core
7480Sstevel@tonic-gate 		 * should respond to this command
7490Sstevel@tonic-gate 		 */
7500Sstevel@tonic-gate 		if ((result & 0xf0) == 0x70) {
7510Sstevel@tonic-gate 			/* low 4 bits may change */
7520Sstevel@tonic-gate 			fcp->c_flags |= FCFLG_3DMODE;
7530Sstevel@tonic-gate 			retcode = PC87322;
7540Sstevel@tonic-gate 		} else
7550Sstevel@tonic-gate 			cmn_err(CE_CONT,
7560Sstevel@tonic-gate "?fdc: unidentified, enhanced, National Semiconductor cntlr %x\n", result);
7570Sstevel@tonic-gate 	} else {
7580Sstevel@tonic-gate 		save = inb(fcp->c_regbase + FCR_SRA);
7590Sstevel@tonic-gate 
7600Sstevel@tonic-gate 		do {
7610Sstevel@tonic-gate 			/* probe for motherboard version of SMC cntlr */
7620Sstevel@tonic-gate 
7630Sstevel@tonic-gate 			/* try to enable configuration mode */
7640Sstevel@tonic-gate 			ddic = ddi_enter_critical();
7650Sstevel@tonic-gate 			outb(fcp->c_regbase + FCR_SRA, FSA_ENA5);
7660Sstevel@tonic-gate 			outb(fcp->c_regbase + FCR_SRA, FSA_ENA5);
7670Sstevel@tonic-gate 			ddi_exit_critical(ddic);
7680Sstevel@tonic-gate 
7690Sstevel@tonic-gate 			outb(fcp->c_regbase + FCR_SRA, 0x0F);
7700Sstevel@tonic-gate 			if (inb(fcp->c_regbase + FCR_SRB) != 0x00)
7710Sstevel@tonic-gate 				/* always expect 0 from config reg F */
7720Sstevel@tonic-gate 				break;
7730Sstevel@tonic-gate 			outb(fcp->c_regbase + FCR_SRA, 0x0D);
7740Sstevel@tonic-gate 			if (inb(fcp->c_regbase + FCR_SRB) != 0x65)
7750Sstevel@tonic-gate 				/* expect 0x65 from config reg D */
7760Sstevel@tonic-gate 				break;
7770Sstevel@tonic-gate 			outb(fcp->c_regbase + FCR_SRA, 0x0E);
7780Sstevel@tonic-gate 			result = inb(fcp->c_regbase + FCR_SRB);
7790Sstevel@tonic-gate 			if (result != 0x02) {
7800Sstevel@tonic-gate 				/* expect revision level 2 from config reg E */
7810Sstevel@tonic-gate 				cmn_err(CE_CONT,
7820Sstevel@tonic-gate "?fdc: unidentified, enhanced, SMC cntlr revision %x\n", result);
7830Sstevel@tonic-gate 				/* break;	*/
7840Sstevel@tonic-gate 			}
7850Sstevel@tonic-gate 			fcp->c_flags |= FCFLG_3DMODE;
7860Sstevel@tonic-gate 			retcode = FDC37C665;
7870Sstevel@tonic-gate 		} while (retcode == 0);
7880Sstevel@tonic-gate 		outb(fcp->c_regbase + FCR_SRA, FSA_DISB);
7890Sstevel@tonic-gate 
7900Sstevel@tonic-gate 		while (retcode == 0) {
7910Sstevel@tonic-gate 			/* probe for adapter version of SMC cntlr */
7920Sstevel@tonic-gate 			ddic = ddi_enter_critical();
7930Sstevel@tonic-gate 			outb(fcp->c_regbase + FCR_SRA, FSA_ENA6);
7940Sstevel@tonic-gate 			outb(fcp->c_regbase + FCR_SRA, FSA_ENA6);
7950Sstevel@tonic-gate 			ddi_exit_critical(ddic);
7960Sstevel@tonic-gate 
7970Sstevel@tonic-gate 			outb(fcp->c_regbase + FCR_SRA, 0x0F);
7980Sstevel@tonic-gate 			if (inb(fcp->c_regbase + FCR_SRB) != 0x00)
7990Sstevel@tonic-gate 				/* always expect 0 from config reg F */
8000Sstevel@tonic-gate 				break;
8010Sstevel@tonic-gate 			outb(fcp->c_regbase + FCR_SRA, 0x0D);
8020Sstevel@tonic-gate 			if (inb(fcp->c_regbase + FCR_SRB) != 0x66)
8030Sstevel@tonic-gate 				/* expect 0x66 from config reg D */
8040Sstevel@tonic-gate 				break;
8050Sstevel@tonic-gate 			outb(fcp->c_regbase + FCR_SRA, 0x0E);
8060Sstevel@tonic-gate 			result = inb(fcp->c_regbase + FCR_SRB);
8070Sstevel@tonic-gate 			if (result != 0x02) {
8080Sstevel@tonic-gate 				/* expect revision level 2 from config reg E */
8090Sstevel@tonic-gate 				cmn_err(CE_CONT,
8100Sstevel@tonic-gate "?fdc: unidentified, enhanced, SMC cntlr revision %x\n", result);
8110Sstevel@tonic-gate 				/* break;	*/
8120Sstevel@tonic-gate 			}
8130Sstevel@tonic-gate 			fcp->c_flags |= FCFLG_3DMODE;
8140Sstevel@tonic-gate 			retcode = FDC37C666;
8150Sstevel@tonic-gate 		}
8160Sstevel@tonic-gate 		outb(fcp->c_regbase + FCR_SRA, FSA_DISB);
8170Sstevel@tonic-gate 
8180Sstevel@tonic-gate 		drv_usecwait(10);
8190Sstevel@tonic-gate 		outb(fcp->c_regbase + FCR_SRA, save);
8200Sstevel@tonic-gate 	}
8210Sstevel@tonic-gate 	return (retcode);
8220Sstevel@tonic-gate }
8230Sstevel@tonic-gate 
8240Sstevel@tonic-gate /* ARGSUSED */
8250Sstevel@tonic-gate static int
8260Sstevel@tonic-gate fdc_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
8270Sstevel@tonic-gate {
8280Sstevel@tonic-gate 	struct fdcntlr *fcp;
8290Sstevel@tonic-gate 	int unit;
8300Sstevel@tonic-gate 	int rval = 0;
8310Sstevel@tonic-gate 
8320Sstevel@tonic-gate 	FCERRPRINT(FDEP_L3, FDEM_ATTA, (CE_WARN, "fdc_detach: dip %p",
8330Sstevel@tonic-gate 		(void*)dip));
8340Sstevel@tonic-gate 
8350Sstevel@tonic-gate 	fcp = ddi_get_driver_private(dip);
8360Sstevel@tonic-gate 
8370Sstevel@tonic-gate 	switch (cmd) {
8380Sstevel@tonic-gate 	case DDI_DETACH:
8390Sstevel@tonic-gate 		for (unit = 0; unit < NFDUN; unit++)
8400Sstevel@tonic-gate 			if ((fcp->c_unit[unit])->fj_dip) {
8410Sstevel@tonic-gate 				rval = EBUSY;
8420Sstevel@tonic-gate 				break;
8430Sstevel@tonic-gate 			}
8440Sstevel@tonic-gate 		kstat_delete(fcp->c_intrstat);
8450Sstevel@tonic-gate 		fcp->c_intrstat = NULL;
8460Sstevel@tonic-gate 		ddi_remove_intr(fcp->c_dip, 0, fcp->c_iblock);
8470Sstevel@tonic-gate 		if (ddi_dmae_release(fcp->c_dip, fcp->c_dmachan) !=
8480Sstevel@tonic-gate 		    DDI_SUCCESS)
8490Sstevel@tonic-gate 			cmn_err(CE_WARN, "fdc_detach: dma release failed, "
8500Sstevel@tonic-gate 				"dip %p, dmachan %x\n",
8510Sstevel@tonic-gate 				(void*)fcp->c_dip, fcp->c_dmachan);
8520Sstevel@tonic-gate 		ddi_prop_remove_all(fcp->c_dip);
8530Sstevel@tonic-gate 		ddi_set_driver_private(fcp->c_dip, NULL);
8540Sstevel@tonic-gate 
8550Sstevel@tonic-gate 		mutex_destroy(&fcp->c_lock);
8560Sstevel@tonic-gate 		mutex_destroy(&fcp->c_dorlock);
8570Sstevel@tonic-gate 		cv_destroy(&fcp->c_iocv);
8580Sstevel@tonic-gate 		sema_destroy(&fcp->c_selsem);
8590Sstevel@tonic-gate 		ddi_soft_state_free(fdc_state_head, ddi_get_instance(dip));
8600Sstevel@tonic-gate 		break;
8610Sstevel@tonic-gate 	default:
8620Sstevel@tonic-gate 		rval = EINVAL;
8630Sstevel@tonic-gate 		break;
8640Sstevel@tonic-gate 	}
8650Sstevel@tonic-gate 	return (rval);
8660Sstevel@tonic-gate }
8670Sstevel@tonic-gate 
8680Sstevel@tonic-gate 
8690Sstevel@tonic-gate /* ARGSUSED */
8700Sstevel@tonic-gate int
8710Sstevel@tonic-gate fdc_start(struct fcu_obj *fjp)
8720Sstevel@tonic-gate {
8730Sstevel@tonic-gate 	return (ENOSYS);
8740Sstevel@tonic-gate }
8750Sstevel@tonic-gate 
8760Sstevel@tonic-gate int
8770Sstevel@tonic-gate fdc_abort(struct fcu_obj *fjp)
8780Sstevel@tonic-gate {
8790Sstevel@tonic-gate 	struct fdcntlr *fcp = fjp->fj_fdc;
8800Sstevel@tonic-gate 	int unit = fjp->fj_unit & 3;
8810Sstevel@tonic-gate 
8820Sstevel@tonic-gate 	FCERRPRINT(FDEP_L3, FDEM_RESE, (CE_WARN, "fdc_abort"));
8830Sstevel@tonic-gate 	if (fcp->c_curunit == unit) {
8840Sstevel@tonic-gate 		mutex_enter(&fcp->c_lock);
8850Sstevel@tonic-gate 		if (fcp->c_flags & FCFLG_WAITING) {
8860Sstevel@tonic-gate 			/*
8870Sstevel@tonic-gate 			 * this can cause data corruption !
8880Sstevel@tonic-gate 			 */
8890Sstevel@tonic-gate 			fdcquiesce(fcp);
8900Sstevel@tonic-gate 			fcp->c_csb.csb_xstate = FXS_RESET;
8910Sstevel@tonic-gate 			fcp->c_flags |= FCFLG_TIMEOUT;
8920Sstevel@tonic-gate 			if (ddi_dmae_stop(fcp->c_dip, fcp->c_dmachan) !=
8930Sstevel@tonic-gate 			    DDI_SUCCESS)
8940Sstevel@tonic-gate 				cmn_err(CE_WARN,
8950Sstevel@tonic-gate 					"fdc_detach: dma release failed, "
8960Sstevel@tonic-gate 					"dip %p, dmachan %x\n",
8970Sstevel@tonic-gate 					(void*)fcp->c_dip, fcp->c_dmachan);
8980Sstevel@tonic-gate 		}
8990Sstevel@tonic-gate 		mutex_exit(&fcp->c_lock);
9000Sstevel@tonic-gate 		drv_usecwait(500);
9010Sstevel@tonic-gate 		return (DDI_SUCCESS);
9020Sstevel@tonic-gate 	}
9030Sstevel@tonic-gate 	return (DDI_FAILURE);
9040Sstevel@tonic-gate }
9050Sstevel@tonic-gate 
9060Sstevel@tonic-gate /* ARGSUSED */
9070Sstevel@tonic-gate int
9080Sstevel@tonic-gate fdc_getcap(struct fcu_obj *fjp, char *a, int i)
9090Sstevel@tonic-gate {
9100Sstevel@tonic-gate 	return (ENOSYS);
9110Sstevel@tonic-gate }
9120Sstevel@tonic-gate 
9130Sstevel@tonic-gate /* ARGSUSED */
9140Sstevel@tonic-gate int
9150Sstevel@tonic-gate fdc_setcap(struct fcu_obj *fjp, char *a, int i, int j)
9160Sstevel@tonic-gate {
9170Sstevel@tonic-gate 	return (ENOSYS);
9180Sstevel@tonic-gate }
9190Sstevel@tonic-gate 
9200Sstevel@tonic-gate int
9210Sstevel@tonic-gate fdc_dkinfo(struct fcu_obj *fjp, struct dk_cinfo *dcp)
9220Sstevel@tonic-gate {
9230Sstevel@tonic-gate 	struct fdcntlr *fcp = fjp->fj_fdc;
9240Sstevel@tonic-gate 
9250Sstevel@tonic-gate 	(void) strncpy((char *)&dcp->dki_cname, ddi_get_name(fcp->c_dip),
9260Sstevel@tonic-gate 		DK_DEVLEN);
9270Sstevel@tonic-gate 	dcp->dki_ctype = DKC_UNKNOWN; /* no code for generic PC/AT fdc */
9280Sstevel@tonic-gate 	dcp->dki_flags = DKI_FMTTRK;
9290Sstevel@tonic-gate 	dcp->dki_addr = fcp->c_regbase;
9300Sstevel@tonic-gate 	dcp->dki_space = 0;
9310Sstevel@tonic-gate 	dcp->dki_prio = fcp->c_intprio;
9320Sstevel@tonic-gate 	dcp->dki_vec = fcp->c_intvec;
9330Sstevel@tonic-gate 	(void) strncpy((char *)&dcp->dki_dname, ddi_driver_name(fjp->fj_dip),
9340Sstevel@tonic-gate 		DK_DEVLEN);
9350Sstevel@tonic-gate 	dcp->dki_slave = fjp->fj_unit & 3;
9360Sstevel@tonic-gate 	dcp->dki_maxtransfer = maxphys / DEV_BSIZE;
9370Sstevel@tonic-gate 	return (DDI_SUCCESS);
9380Sstevel@tonic-gate }
9390Sstevel@tonic-gate 
9400Sstevel@tonic-gate /*
9410Sstevel@tonic-gate  * on=> non-zero = select, 0 = de-select
9420Sstevel@tonic-gate  */
9430Sstevel@tonic-gate /* ARGSUSED */
9440Sstevel@tonic-gate int
9450Sstevel@tonic-gate fdc_select(struct fcu_obj *fjp, int funit, int on)
9460Sstevel@tonic-gate {
9470Sstevel@tonic-gate 	struct fdcntlr *fcp = fjp->fj_fdc;
9480Sstevel@tonic-gate 	int unit = funit & 3;
9490Sstevel@tonic-gate 
9500Sstevel@tonic-gate 	if (on) {
9510Sstevel@tonic-gate 		/* possess controller */
9520Sstevel@tonic-gate 		sema_p(&fcp->c_selsem);
9530Sstevel@tonic-gate 		FCERRPRINT(FDEP_L2, FDEM_DSEL,
9540Sstevel@tonic-gate 		    (CE_NOTE, "fdc_select unit %d: on", funit));
9550Sstevel@tonic-gate 
9560Sstevel@tonic-gate 		if (fcp->c_curunit != unit || !(fjp->fj_flags & FUNIT_CHAROK)) {
9570Sstevel@tonic-gate 			fcp->c_curunit = unit;
9580Sstevel@tonic-gate 			fjp->fj_flags |= FUNIT_CHAROK;
9590Sstevel@tonic-gate 			if (fdcspecify(fcp,
9600Sstevel@tonic-gate 			    fjp->fj_chars->fdc_transfer_rate,
9610Sstevel@tonic-gate 			    fjp->fj_drive->fdd_steprate, 40))
9620Sstevel@tonic-gate 				cmn_err(CE_WARN,
9630Sstevel@tonic-gate 				    "fdc_select: controller setup rejected "
9640Sstevel@tonic-gate 				    "fdcntrl %p transfer rate %x step rate %x"
9650Sstevel@tonic-gate 				    " head load time 40\n", (void*)fcp,
9660Sstevel@tonic-gate 				    fjp->fj_chars->fdc_transfer_rate,
9670Sstevel@tonic-gate 				    fjp->fj_drive->fdd_steprate);
9680Sstevel@tonic-gate 		}
9690Sstevel@tonic-gate 
9700Sstevel@tonic-gate 		mutex_enter(&fcp->c_dorlock);
9710Sstevel@tonic-gate 
9720Sstevel@tonic-gate 		/* make sure drive is not selected in case we change speed */
9730Sstevel@tonic-gate 		fcp->c_digout = (fcp->c_digout & ~FD_DRSEL) |
9740Sstevel@tonic-gate 		    (~unit & FD_DRSEL);
9750Sstevel@tonic-gate 		outb(fcp->c_regbase + FCR_DOR, fcp->c_digout);
9760Sstevel@tonic-gate 
9770Sstevel@tonic-gate 		(void) fdc_motorsm(fjp, FMI_STARTCMD,
9780Sstevel@tonic-gate 		    fjp->fj_drive->fdd_motoron);
9790Sstevel@tonic-gate 		/*
9800Sstevel@tonic-gate 		 * Return value ignored - fdcmotort deals with failure.
9810Sstevel@tonic-gate 		 */
9820Sstevel@tonic-gate 		if (fdcspdchange(fcp, fjp, fjp->fj_attr->fda_rotatespd)) {
9830Sstevel@tonic-gate 			/* 3D drive requires 500 ms for speed change */
9840Sstevel@tonic-gate 			(void) fdc_motorsm(fjp, FMI_RSTARTCMD, 5);
9850Sstevel@tonic-gate 			/*
9860Sstevel@tonic-gate 			 * Return value ignored - fdcmotort deals with failure.
9870Sstevel@tonic-gate 			 */
9880Sstevel@tonic-gate 		}
9890Sstevel@tonic-gate 
9900Sstevel@tonic-gate 		fcp->c_digout = (fcp->c_digout & ~FD_DRSEL) | (unit & FD_DRSEL);
9910Sstevel@tonic-gate 		outb(fcp->c_regbase + FCR_DOR, fcp->c_digout);
9920Sstevel@tonic-gate 
9930Sstevel@tonic-gate 		mutex_exit(&fcp->c_dorlock);
9940Sstevel@tonic-gate 		fcp->c_csb.csb_drive = (uchar_t)unit;
9950Sstevel@tonic-gate 	} else {
9960Sstevel@tonic-gate 		FCERRPRINT(FDEP_L2, FDEM_DSEL,
9970Sstevel@tonic-gate 		    (CE_NOTE, "fdc_select unit %d: off", funit));
9980Sstevel@tonic-gate 
9990Sstevel@tonic-gate 		mutex_enter(&fcp->c_dorlock);
10000Sstevel@tonic-gate 
10010Sstevel@tonic-gate 		fcp->c_digout |= FD_DRSEL;
10020Sstevel@tonic-gate 		outb(fcp->c_regbase + FCR_DOR, fcp->c_digout);
10030Sstevel@tonic-gate 		(void) fdc_motorsm(fjp, FMI_IDLECMD,
10040Sstevel@tonic-gate 		    fjp->fj_drive->fdd_motoroff);
10050Sstevel@tonic-gate 		/*
10060Sstevel@tonic-gate 		 * Return value ignored - fdcmotort deals with failure.
10070Sstevel@tonic-gate 		 */
10080Sstevel@tonic-gate 
10090Sstevel@tonic-gate 		mutex_exit(&fcp->c_dorlock);
10100Sstevel@tonic-gate 
10110Sstevel@tonic-gate 		/* give up controller */
10120Sstevel@tonic-gate 		sema_v(&fcp->c_selsem);
10130Sstevel@tonic-gate 	}
10140Sstevel@tonic-gate 	return (0);
10150Sstevel@tonic-gate }
10160Sstevel@tonic-gate 
10170Sstevel@tonic-gate 
10180Sstevel@tonic-gate int
10190Sstevel@tonic-gate fdgetchng(struct fcu_obj *fjp, int funit)
10200Sstevel@tonic-gate {
10210Sstevel@tonic-gate 	if (fdcsense_drv(fjp->fj_fdc, funit & 3))
10220Sstevel@tonic-gate 		cmn_err(CE_WARN, "fdgetchng: write protect check failed\n");
10230Sstevel@tonic-gate 	return (fdcsense_chng(fjp->fj_fdc, funit & 3));
10240Sstevel@tonic-gate }
10250Sstevel@tonic-gate 
10260Sstevel@tonic-gate 
10270Sstevel@tonic-gate int
10280Sstevel@tonic-gate fdresetchng(struct fcu_obj *fjp, int funit)
10290Sstevel@tonic-gate {
10300Sstevel@tonic-gate 	struct fdcntlr *fcp = fjp->fj_fdc;
10310Sstevel@tonic-gate 	int unit = funit & 3;
10320Sstevel@tonic-gate 	int newcyl;			/* where to seek for reset of DSKCHG */
10330Sstevel@tonic-gate 
10340Sstevel@tonic-gate 	FCERRPRINT(FDEP_L2, FDEM_CHEK, (CE_NOTE, "fdmediachng unit %d", funit));
10350Sstevel@tonic-gate 
10360Sstevel@tonic-gate 	if (fcp->c_curpcyl[unit])
10370Sstevel@tonic-gate 		newcyl = fcp->c_curpcyl[unit] - 1;
10380Sstevel@tonic-gate 	else
10390Sstevel@tonic-gate 		newcyl = 1;
10400Sstevel@tonic-gate 	return (fdrecalseek(fjp, funit, newcyl, 0));
10410Sstevel@tonic-gate }
10420Sstevel@tonic-gate 
10430Sstevel@tonic-gate 
10440Sstevel@tonic-gate /*
10450Sstevel@tonic-gate  * fdrecalseek
10460Sstevel@tonic-gate  */
10470Sstevel@tonic-gate int
10480Sstevel@tonic-gate fdrecalseek(struct fcu_obj *fjp, int funit, int arg, int execflg)
10490Sstevel@tonic-gate {
10500Sstevel@tonic-gate 	struct fdcntlr *fcp = fjp->fj_fdc;
10510Sstevel@tonic-gate 	struct fdcsb *csb;
10520Sstevel@tonic-gate 	int unit = funit & 3;
10530Sstevel@tonic-gate 	int rval;
10540Sstevel@tonic-gate 
10550Sstevel@tonic-gate 	FCERRPRINT(FDEP_L2, FDEM_RECA, (CE_NOTE, "fdrecalseek unit %d to %d",
10560Sstevel@tonic-gate 	    funit, arg));
10570Sstevel@tonic-gate 
10580Sstevel@tonic-gate 	csb = &fcp->c_csb;
10590Sstevel@tonic-gate 	csb->csb_cmd[1] = (uchar_t)unit;
10600Sstevel@tonic-gate 	if (arg < 0) {			/* is recal... */
10610Sstevel@tonic-gate 		*csb->csb_cmd = FO_RECAL;
10620Sstevel@tonic-gate 		csb->csb_ncmds = 2;
10630Sstevel@tonic-gate 		csb->csb_timer = 28;
10640Sstevel@tonic-gate 	} else {
10650Sstevel@tonic-gate 		*csb->csb_cmd = FO_SEEK;
10660Sstevel@tonic-gate 		csb->csb_cmd[2] = (uchar_t)arg;
10670Sstevel@tonic-gate 		csb->csb_ncmds = 3;
10680Sstevel@tonic-gate 		csb->csb_timer = 10;
10690Sstevel@tonic-gate 	}
10700Sstevel@tonic-gate 	csb->csb_nrslts = 2;	/* 2 for SENSE INTERRUPTS */
10710Sstevel@tonic-gate 	csb->csb_opflags = CSB_OFINRPT;
10720Sstevel@tonic-gate 	csb->csb_maxretry = skretry;
10730Sstevel@tonic-gate 	csb->csb_dmahandle = NULL;
10740Sstevel@tonic-gate 	csb->csb_handle_bound = 0;
10750Sstevel@tonic-gate 	csb->csb_dmacookiecnt = 0;
10760Sstevel@tonic-gate 	csb->csb_dmacurrcookie = 0;
10770Sstevel@tonic-gate 	csb->csb_dmawincnt = 0;
10780Sstevel@tonic-gate 	csb->csb_dmacurrwin = 0;
10790Sstevel@tonic-gate 
10800Sstevel@tonic-gate 	/* send cmd off to fdc_exec */
10810Sstevel@tonic-gate 	if (rval = fdc_exec(fcp, 1, execflg))
10820Sstevel@tonic-gate 		goto out;
10830Sstevel@tonic-gate 
10840Sstevel@tonic-gate 	if (!(*csb->csb_rslt & S0_SEKEND) ||
10850Sstevel@tonic-gate 	    (*csb->csb_rslt & S0_ICMASK) ||
10860Sstevel@tonic-gate 	    ((*csb->csb_rslt & S0_ECHK) && arg < 0) ||
10870Sstevel@tonic-gate 	    csb->csb_cmdstat)
10880Sstevel@tonic-gate 		rval = ENODEV;
10890Sstevel@tonic-gate 
10900Sstevel@tonic-gate 	if (fdcsense_drv(fcp, unit))
10910Sstevel@tonic-gate 		cmn_err(CE_WARN, "fdgetchng: write protect check failed\n");
10920Sstevel@tonic-gate out:
10930Sstevel@tonic-gate 	return (rval);
10940Sstevel@tonic-gate }
10950Sstevel@tonic-gate 
10960Sstevel@tonic-gate 
10970Sstevel@tonic-gate /*
10980Sstevel@tonic-gate  * fdrw- used only for read/writing sectors into/from kernel buffers.
10990Sstevel@tonic-gate  */
11000Sstevel@tonic-gate int
11010Sstevel@tonic-gate fdrw(struct fcu_obj *fjp, int funit, int rw, int cyl, int head,
11020Sstevel@tonic-gate     int sector, caddr_t bufp, uint_t len)
11030Sstevel@tonic-gate {
11040Sstevel@tonic-gate 	struct fdcntlr *fcp = fjp->fj_fdc;
11050Sstevel@tonic-gate 	struct fdcsb *csb;
11060Sstevel@tonic-gate 	uint_t dmar_flags = 0;
11070Sstevel@tonic-gate 	int unit = funit & 3;
11080Sstevel@tonic-gate 	int rval;
1109*940Smrj 	ddi_acc_handle_t mem_handle = NULL;
1110*940Smrj 	caddr_t aligned_buf;
1111*940Smrj 	size_t real_size;
11120Sstevel@tonic-gate 
11130Sstevel@tonic-gate 	FCERRPRINT(FDEP_L1, FDEM_RW, (CE_CONT, "fdrw unit %d\n", funit));
11140Sstevel@tonic-gate 
11150Sstevel@tonic-gate 	csb = &fcp->c_csb;
11160Sstevel@tonic-gate 	if (rw) {
11170Sstevel@tonic-gate 		dmar_flags = DDI_DMA_READ;
11180Sstevel@tonic-gate 		csb->csb_opflags = CSB_OFDMARD | CSB_OFINRPT;
11190Sstevel@tonic-gate 		*csb->csb_cmd = FO_MT | FO_MFM | FO_SK | FO_RDDAT;
11200Sstevel@tonic-gate 	} else { /* write */
11210Sstevel@tonic-gate 		dmar_flags = DDI_DMA_WRITE;
11220Sstevel@tonic-gate 		csb->csb_opflags = CSB_OFDMAWT | CSB_OFINRPT;
11230Sstevel@tonic-gate 		*csb->csb_cmd = FO_MT | FO_MFM | FO_WRDAT;
11240Sstevel@tonic-gate 	}
11250Sstevel@tonic-gate 	csb->csb_cmd[1] = (uchar_t)(unit | ((head & 0x1) << 2));
11260Sstevel@tonic-gate 	csb->csb_cmd[2] = (uchar_t)cyl;
11270Sstevel@tonic-gate 	csb->csb_cmd[3] = (uchar_t)head;
11280Sstevel@tonic-gate 	csb->csb_cmd[4] = (uchar_t)sector;
11290Sstevel@tonic-gate 	encode(sector_size, fjp->fj_chars->fdc_sec_size,
11300Sstevel@tonic-gate 	    &csb->csb_cmd[5]);
11310Sstevel@tonic-gate 	csb->csb_cmd[6] = (uchar_t)max(fjp->fj_chars->fdc_secptrack, sector);
11320Sstevel@tonic-gate 	csb->csb_cmd[7] = fjp->fj_attr->fda_gapl;
11330Sstevel@tonic-gate 	csb->csb_cmd[8] = 0xFF;
11340Sstevel@tonic-gate 
11350Sstevel@tonic-gate 	csb->csb_ncmds = 9;
11360Sstevel@tonic-gate 	csb->csb_nrslts = 7;
11370Sstevel@tonic-gate 	csb->csb_timer = 36;
11380Sstevel@tonic-gate 	if (rw == FDRDONE)
11390Sstevel@tonic-gate 		csb->csb_maxretry = 1;
11400Sstevel@tonic-gate 	else
11410Sstevel@tonic-gate 		csb->csb_maxretry = rwretry;
11420Sstevel@tonic-gate 
11430Sstevel@tonic-gate 	csb->csb_dmahandle = NULL;
11440Sstevel@tonic-gate 	csb->csb_handle_bound = 0;
11450Sstevel@tonic-gate 	csb->csb_dmacookiecnt = 0;
11460Sstevel@tonic-gate 	csb->csb_dmacurrcookie = 0;
11470Sstevel@tonic-gate 	csb->csb_dmawincnt = 0;
11480Sstevel@tonic-gate 	csb->csb_dmacurrwin = 0;
11490Sstevel@tonic-gate 	dmar_flags |= (DDI_DMA_STREAMING | DDI_DMA_PARTIAL);
11500Sstevel@tonic-gate 
1151*940Smrj 	if (ddi_dma_alloc_handle(fcp->c_dip, &fdc_dma_attr, DDI_DMA_SLEEP,
11520Sstevel@tonic-gate 			0, &csb->csb_dmahandle) != DDI_SUCCESS) {
11530Sstevel@tonic-gate 		rval = EINVAL;
11540Sstevel@tonic-gate 		goto out;
11550Sstevel@tonic-gate 	}
11560Sstevel@tonic-gate 
1157*940Smrj 	/*
1158*940Smrj 	 * allocate a page aligned buffer to dma to/from. This way we can
1159*940Smrj 	 * ensure the cookie is a whole multiple of granularity and avoids
1160*940Smrj 	 * any alignment issues.
1161*940Smrj 	 */
1162*940Smrj 	rval = ddi_dma_mem_alloc(csb->csb_dmahandle, len, &fdc_accattr,
1163*940Smrj 	    DDI_DMA_CONSISTENT, DDI_DMA_SLEEP, NULL, &aligned_buf,
1164*940Smrj 	    &real_size, &mem_handle);
1165*940Smrj 	if (rval != DDI_SUCCESS) {
1166*940Smrj 		rval = EINVAL;
1167*940Smrj 		goto out;
1168*940Smrj 	}
1169*940Smrj 
1170*940Smrj 	if (dmar_flags & DDI_DMA_WRITE) {
1171*940Smrj 		bcopy(bufp, aligned_buf, len);
1172*940Smrj 	}
1173*940Smrj 
1174*940Smrj 	rval = ddi_dma_addr_bind_handle(csb->csb_dmahandle, NULL, aligned_buf,
1175*940Smrj 	    len, dmar_flags, DDI_DMA_SLEEP, 0, &csb->csb_dmacookie,
1176*940Smrj 	    &csb->csb_dmacookiecnt);
11770Sstevel@tonic-gate 
11780Sstevel@tonic-gate 	if (rval == DDI_DMA_MAPPED) {
11790Sstevel@tonic-gate 		csb->csb_dmawincnt = 1;
11800Sstevel@tonic-gate 		csb->csb_handle_bound = 1;
11810Sstevel@tonic-gate 	} else if (rval == DDI_DMA_PARTIAL_MAP) {
11820Sstevel@tonic-gate 		csb->csb_handle_bound = 1;
11830Sstevel@tonic-gate 		if (ddi_dma_numwin(csb->csb_dmahandle, &csb->csb_dmawincnt) !=
11840Sstevel@tonic-gate 					DDI_SUCCESS) {
11850Sstevel@tonic-gate 			cmn_err(CE_WARN, "fdrw: dma numwin failed\n");
11860Sstevel@tonic-gate 			rval = EINVAL;
11870Sstevel@tonic-gate 			goto out;
11880Sstevel@tonic-gate 		}
11890Sstevel@tonic-gate 	} else {
11900Sstevel@tonic-gate 		cmn_err(CE_WARN,
11910Sstevel@tonic-gate 			"fdrw: dma addr bind handle failed, rval = %d\n",
11920Sstevel@tonic-gate 			rval);
11930Sstevel@tonic-gate 		rval = EINVAL;
11940Sstevel@tonic-gate 		goto out;
11950Sstevel@tonic-gate 	}
11960Sstevel@tonic-gate 	rval = fdc_exec(fcp, 1, 1);
11970Sstevel@tonic-gate 
1198*940Smrj 	if (dmar_flags & DDI_DMA_READ) {
1199*940Smrj 		bcopy(aligned_buf, bufp, len);
1200*940Smrj 	}
1201*940Smrj 
12020Sstevel@tonic-gate out:
12030Sstevel@tonic-gate 	if (csb->csb_dmahandle) {
12040Sstevel@tonic-gate 		if (csb->csb_handle_bound) {
12050Sstevel@tonic-gate 			if (ddi_dma_unbind_handle(csb->csb_dmahandle) !=
12060Sstevel@tonic-gate 			    DDI_SUCCESS)
12070Sstevel@tonic-gate 				cmn_err(CE_WARN, "fdrw: "
12080Sstevel@tonic-gate 				    "dma unbind handle failed\n");
12090Sstevel@tonic-gate 			csb->csb_handle_bound = 0;
12100Sstevel@tonic-gate 		}
1211*940Smrj 		if (mem_handle != NULL) {
1212*940Smrj 			ddi_dma_mem_free(&mem_handle);
1213*940Smrj 		}
12140Sstevel@tonic-gate 		ddi_dma_free_handle(&csb->csb_dmahandle);
12150Sstevel@tonic-gate 		csb->csb_dmahandle = NULL;
12160Sstevel@tonic-gate 	}
12170Sstevel@tonic-gate 	return (rval);
12180Sstevel@tonic-gate }
12190Sstevel@tonic-gate 
12200Sstevel@tonic-gate 
12210Sstevel@tonic-gate int
12220Sstevel@tonic-gate fdtrkformat(struct fcu_obj *fjp, int funit, int cyl, int head, int filldata)
12230Sstevel@tonic-gate {
12240Sstevel@tonic-gate 	struct fdcntlr *fcp = fjp->fj_fdc;
12250Sstevel@tonic-gate 	struct fdcsb *csb;
12260Sstevel@tonic-gate 	int unit = funit & 3;
12270Sstevel@tonic-gate 	int fmdatlen, lsector, lstart;
12280Sstevel@tonic-gate 	int interleave, numsctr, offset, psector;
12290Sstevel@tonic-gate 	uchar_t *dp;
12300Sstevel@tonic-gate 	int rval;
1231*940Smrj 	ddi_acc_handle_t mem_handle = NULL;
1232*940Smrj 	caddr_t aligned_buf;
1233*940Smrj 	size_t real_size;
12340Sstevel@tonic-gate 
12350Sstevel@tonic-gate 	FCERRPRINT(FDEP_L2, FDEM_FORM,
12360Sstevel@tonic-gate 	    (CE_NOTE, "fdformattrk unit %d cyl=%d, hd=%d", funit, cyl, head));
12370Sstevel@tonic-gate 
12380Sstevel@tonic-gate 	csb = &fcp->c_csb;
12390Sstevel@tonic-gate 
12400Sstevel@tonic-gate 	csb->csb_opflags = CSB_OFDMAWT | CSB_OFINRPT;
12410Sstevel@tonic-gate 
12420Sstevel@tonic-gate 	*csb->csb_cmd = FO_FRMT | FO_MFM;
12430Sstevel@tonic-gate 	csb->csb_cmd[1] = (head << 2) | unit;
12440Sstevel@tonic-gate 	encode(sector_size, fjp->fj_chars->fdc_sec_size,
12450Sstevel@tonic-gate 	    &csb->csb_cmd[2]);
12460Sstevel@tonic-gate 	csb->csb_cmd[3] = numsctr = fjp->fj_chars->fdc_secptrack;
12470Sstevel@tonic-gate 	csb->csb_cmd[4] = fjp->fj_attr->fda_gapf;
12480Sstevel@tonic-gate 	csb->csb_cmd[5] = (uchar_t)filldata;
12490Sstevel@tonic-gate 
12500Sstevel@tonic-gate 	csb->csb_npcyl = (uchar_t)(cyl * fjp->fj_chars->fdc_steps);
12510Sstevel@tonic-gate 
12520Sstevel@tonic-gate 	csb->csb_dmahandle = NULL;
12530Sstevel@tonic-gate 	csb->csb_handle_bound = 0;
12540Sstevel@tonic-gate 	csb->csb_dmacookiecnt = 0;
12550Sstevel@tonic-gate 	csb->csb_dmacurrcookie = 0;
12560Sstevel@tonic-gate 	csb->csb_dmawincnt = 0;
12570Sstevel@tonic-gate 	csb->csb_dmacurrwin = 0;
12580Sstevel@tonic-gate 	csb->csb_ncmds = 6;
12590Sstevel@tonic-gate 	csb->csb_nrslts = 7;
12600Sstevel@tonic-gate 	csb->csb_timer = 32;
12610Sstevel@tonic-gate 	csb->csb_maxretry = rwretry;
12620Sstevel@tonic-gate 
12630Sstevel@tonic-gate 	/*
1264*940Smrj 	 * alloc space for format track cmd
12650Sstevel@tonic-gate 	 */
12660Sstevel@tonic-gate 	/*
12670Sstevel@tonic-gate 	 * NOTE: have to add size of fifo also - for dummy format action
12680Sstevel@tonic-gate 	 */
12690Sstevel@tonic-gate 	fmdatlen = 4 * numsctr;
1270*940Smrj 
1271*940Smrj 	if (ddi_dma_alloc_handle(fcp->c_dip, &fdc_dma_attr, DDI_DMA_SLEEP,
1272*940Smrj 			0, &csb->csb_dmahandle) != DDI_SUCCESS) {
1273*940Smrj 		rval = EINVAL;
1274*940Smrj 		goto out;
1275*940Smrj 	}
1276*940Smrj 
1277*940Smrj 	/*
1278*940Smrj 	 * allocate a page aligned buffer to dma to/from. This way we can
1279*940Smrj 	 * ensure the cookie is a whole multiple of granularity and avoids
1280*940Smrj 	 * any alignment issues.
1281*940Smrj 	 */
1282*940Smrj 	rval = ddi_dma_mem_alloc(csb->csb_dmahandle, fmdatlen, &fdc_accattr,
1283*940Smrj 	    DDI_DMA_CONSISTENT, DDI_DMA_SLEEP, NULL, &aligned_buf,
1284*940Smrj 	    &real_size, &mem_handle);
1285*940Smrj 	if (rval != DDI_SUCCESS) {
1286*940Smrj 		rval = EINVAL;
1287*940Smrj 		goto out;
1288*940Smrj 	}
1289*940Smrj 	dp = (uchar_t *)aligned_buf;
12900Sstevel@tonic-gate 
12910Sstevel@tonic-gate 	interleave = fjp->fj_attr->fda_intrlv;
12920Sstevel@tonic-gate 	offset = (numsctr + interleave - 1) / interleave;
12930Sstevel@tonic-gate 	for (psector = lstart = 1;
12940Sstevel@tonic-gate 	    psector <= numsctr; psector += interleave, lstart++) {
12950Sstevel@tonic-gate 		for (lsector = lstart; lsector <= numsctr; lsector += offset) {
12960Sstevel@tonic-gate 			*dp++ = (uchar_t)cyl;
12970Sstevel@tonic-gate 			*dp++ = (uchar_t)head;
12980Sstevel@tonic-gate 			*dp++ = (uchar_t)lsector;
12990Sstevel@tonic-gate 			*dp++ = csb->csb_cmd[2];
13000Sstevel@tonic-gate 		}
13010Sstevel@tonic-gate 	}
13020Sstevel@tonic-gate 
1303*940Smrj 	rval = ddi_dma_addr_bind_handle(csb->csb_dmahandle, NULL, aligned_buf,
1304*940Smrj 	    fmdatlen, DDI_DMA_WRITE | DDI_DMA_STREAMING | DDI_DMA_PARTIAL,
1305*940Smrj 	    DDI_DMA_SLEEP, 0, &csb->csb_dmacookie, &csb->csb_dmacookiecnt);
13060Sstevel@tonic-gate 
13070Sstevel@tonic-gate 	if (rval == DDI_DMA_MAPPED) {
13080Sstevel@tonic-gate 		csb->csb_dmawincnt = 1;
13090Sstevel@tonic-gate 		csb->csb_handle_bound = 1;
13100Sstevel@tonic-gate 	} else if (rval == DDI_DMA_PARTIAL_MAP) {
13110Sstevel@tonic-gate 		csb->csb_handle_bound = 1;
13120Sstevel@tonic-gate 		if (ddi_dma_numwin(csb->csb_dmahandle, &csb->csb_dmawincnt) !=
13130Sstevel@tonic-gate 					DDI_SUCCESS) {
13140Sstevel@tonic-gate 			cmn_err(CE_WARN, "fdtrkformat: dma numwin failed\n");
13150Sstevel@tonic-gate 			rval = EINVAL;
13160Sstevel@tonic-gate 			goto out;
13170Sstevel@tonic-gate 		}
13180Sstevel@tonic-gate 	} else {
13190Sstevel@tonic-gate 		cmn_err(CE_WARN,
13200Sstevel@tonic-gate 			"fdtrkformat: dma buf bind handle failed, rval = %d\n",
13210Sstevel@tonic-gate 			rval);
13220Sstevel@tonic-gate 		rval = EINVAL;
13230Sstevel@tonic-gate 		goto out;
13240Sstevel@tonic-gate 	}
13250Sstevel@tonic-gate 
13260Sstevel@tonic-gate 	rval = fdc_exec(fcp, 1, 1);
13270Sstevel@tonic-gate out:
13280Sstevel@tonic-gate 	if (csb->csb_dmahandle) {
13290Sstevel@tonic-gate 		if (csb->csb_handle_bound) {
13300Sstevel@tonic-gate 			if (ddi_dma_unbind_handle(csb->csb_dmahandle) !=
13310Sstevel@tonic-gate 			    DDI_SUCCESS)
13320Sstevel@tonic-gate 				cmn_err(CE_WARN, "fdtrkformat: "
13330Sstevel@tonic-gate 				    "dma unbind handle failed\n");
13340Sstevel@tonic-gate 			csb->csb_handle_bound = 0;
13350Sstevel@tonic-gate 		}
1336*940Smrj 		if (mem_handle != NULL) {
1337*940Smrj 			ddi_dma_mem_free(&mem_handle);
1338*940Smrj 		}
13390Sstevel@tonic-gate 		ddi_dma_free_handle(&csb->csb_dmahandle);
13400Sstevel@tonic-gate 		csb->csb_dmahandle = NULL;
13410Sstevel@tonic-gate 	}
13420Sstevel@tonic-gate 	return (rval);
13430Sstevel@tonic-gate }
13440Sstevel@tonic-gate 
13450Sstevel@tonic-gate /* ARGSUSED */
13460Sstevel@tonic-gate int
13470Sstevel@tonic-gate fdrawioctl(struct fcu_obj *fjp, int funit, caddr_t arg)
13480Sstevel@tonic-gate {
13490Sstevel@tonic-gate 	struct fdcntlr *fcp = fjp->fj_fdc;
13500Sstevel@tonic-gate 	struct fd_raw *fdrp = (struct fd_raw *)arg;
13510Sstevel@tonic-gate 	struct fdcsb *csb;
13520Sstevel@tonic-gate 	uint_t dmar_flags = 0;
13530Sstevel@tonic-gate 	int i;
13540Sstevel@tonic-gate 	int change = 1;
13550Sstevel@tonic-gate 	int sleep = 1;
13560Sstevel@tonic-gate 	int rval = 0;
13570Sstevel@tonic-gate 	int rval_exec = 0;
1358*940Smrj 	ddi_acc_handle_t mem_handle = NULL;
1359*940Smrj 	caddr_t aligned_buf;
1360*940Smrj 	size_t real_size;
13610Sstevel@tonic-gate 
13620Sstevel@tonic-gate 	FCERRPRINT(FDEP_L2, FDEM_RAWI,
13630Sstevel@tonic-gate 	    (CE_NOTE, "fdrawioctl: cmd[0]=0x%x", fdrp->fdr_cmd[0]));
13640Sstevel@tonic-gate 
13650Sstevel@tonic-gate 	csb = &fcp->c_csb;
13660Sstevel@tonic-gate 
13670Sstevel@tonic-gate 	/* copy cmd bytes into csb */
13680Sstevel@tonic-gate 	for (i = 0; i <= fdrp->fdr_cnum; i++)
13690Sstevel@tonic-gate 		csb->csb_cmd[i] = fdrp->fdr_cmd[i];
13700Sstevel@tonic-gate 	csb->csb_ncmds = (uchar_t)fdrp->fdr_cnum;
13710Sstevel@tonic-gate 
13720Sstevel@tonic-gate 	csb->csb_maxretry = 0;	/* let the application deal with errors */
13730Sstevel@tonic-gate 	csb->csb_opflags = CSB_OFRAWIOCTL;
13740Sstevel@tonic-gate 	csb->csb_nrslts = 0;
13750Sstevel@tonic-gate 	csb->csb_timer = 50;
13760Sstevel@tonic-gate 
13770Sstevel@tonic-gate 	switch (fdrp->fdr_cmd[0] & 0x0f) {
13780Sstevel@tonic-gate 
13790Sstevel@tonic-gate 	case FO_SEEK:
13800Sstevel@tonic-gate 		change = 0;
13810Sstevel@tonic-gate 		/* FALLTHROUGH */
13820Sstevel@tonic-gate 	case FO_RECAL:
13830Sstevel@tonic-gate 		csb->csb_opflags |= CSB_OFINRPT;
13840Sstevel@tonic-gate 		break;
13850Sstevel@tonic-gate 
13860Sstevel@tonic-gate 	case FO_FRMT:
13870Sstevel@tonic-gate 		csb->csb_npcyl = *(uchar_t *)(fdrp->fdr_addr) *
13880Sstevel@tonic-gate 		    fjp->fj_chars->fdc_steps;
13890Sstevel@tonic-gate 		/* FALLTHROUGH */
13900Sstevel@tonic-gate 	case FO_WRDAT:
13910Sstevel@tonic-gate 	case FO_WRDEL:
13920Sstevel@tonic-gate 		csb->csb_opflags |= CSB_OFDMAWT | CSB_OFRESLT | CSB_OFINRPT;
13930Sstevel@tonic-gate 		csb->csb_nrslts = 7;
13940Sstevel@tonic-gate 		if (fdrp->fdr_nbytes == 0)
13950Sstevel@tonic-gate 			return (EINVAL);
13960Sstevel@tonic-gate 		dmar_flags = DDI_DMA_WRITE;
13970Sstevel@tonic-gate 		break;
13980Sstevel@tonic-gate 
13990Sstevel@tonic-gate 	case FO_RDDAT:
14000Sstevel@tonic-gate 	case FO_RDDEL:
14010Sstevel@tonic-gate 	case FO_RDTRK:
14020Sstevel@tonic-gate 		csb->csb_opflags |= CSB_OFDMARD | CSB_OFRESLT | CSB_OFINRPT;
14030Sstevel@tonic-gate 		csb->csb_nrslts = 7;
14040Sstevel@tonic-gate 		dmar_flags = DDI_DMA_READ;
14050Sstevel@tonic-gate 		break;
14060Sstevel@tonic-gate 
14070Sstevel@tonic-gate 	case FO_RDID:
14080Sstevel@tonic-gate 		csb->csb_opflags |= CSB_OFRESLT | CSB_OFINRPT;
14090Sstevel@tonic-gate 		csb->csb_nrslts = 7;
14100Sstevel@tonic-gate 		break;
14110Sstevel@tonic-gate 
14120Sstevel@tonic-gate 	case FO_SDRV:
14130Sstevel@tonic-gate 		sleep = 0;
14140Sstevel@tonic-gate 		csb->csb_nrslts = 1;
14150Sstevel@tonic-gate 		break;
14160Sstevel@tonic-gate 
14170Sstevel@tonic-gate 	case FO_SINT:
14180Sstevel@tonic-gate 		sleep = 0;
14190Sstevel@tonic-gate 		change = 0;
14200Sstevel@tonic-gate 		csb->csb_nrslts = 2;
14210Sstevel@tonic-gate 		break;
14220Sstevel@tonic-gate 
14230Sstevel@tonic-gate 	case FO_SPEC:
14240Sstevel@tonic-gate 		sleep = 0;
14250Sstevel@tonic-gate 		change = 0;
14260Sstevel@tonic-gate 		break;
14270Sstevel@tonic-gate 
14280Sstevel@tonic-gate 	default:
14290Sstevel@tonic-gate 		return (EINVAL);
14300Sstevel@tonic-gate 	}
14310Sstevel@tonic-gate 
14320Sstevel@tonic-gate 	csb->csb_dmahandle = NULL;
14330Sstevel@tonic-gate 	csb->csb_handle_bound = 0;
14340Sstevel@tonic-gate 	csb->csb_dmacookiecnt = 0;
14350Sstevel@tonic-gate 	csb->csb_dmacurrcookie = 0;
14360Sstevel@tonic-gate 	csb->csb_dmawincnt = 0;
14370Sstevel@tonic-gate 	csb->csb_dmacurrwin = 0;
14380Sstevel@tonic-gate 
14390Sstevel@tonic-gate 	if (csb->csb_opflags & (CSB_OFDMARD | CSB_OFDMAWT)) {
1440*940Smrj 		if (ddi_dma_alloc_handle(fcp->c_dip, &fdc_dma_attr,
1441*940Smrj 		    DDI_DMA_SLEEP, 0, &csb->csb_dmahandle) != DDI_SUCCESS) {
14420Sstevel@tonic-gate 			rval = EINVAL;
14430Sstevel@tonic-gate 			goto out;
14440Sstevel@tonic-gate 		}
14450Sstevel@tonic-gate 
1446*940Smrj 		/*
1447*940Smrj 		 * allocate a page aligned buffer to dma to/from. This way we
1448*940Smrj 		 * can ensure the cookie is a whole multiple of granularity and
1449*940Smrj 		 * avoids any alignment issues.
1450*940Smrj 		 */
1451*940Smrj 		rval = ddi_dma_mem_alloc(csb->csb_dmahandle,
1452*940Smrj 		    (uint_t)fdrp->fdr_nbytes, &fdc_accattr, DDI_DMA_CONSISTENT,
1453*940Smrj 		    DDI_DMA_SLEEP, NULL, &aligned_buf, &real_size, &mem_handle);
1454*940Smrj 		if (rval != DDI_SUCCESS) {
1455*940Smrj 			rval = EINVAL;
1456*940Smrj 			goto out;
1457*940Smrj 		}
1458*940Smrj 
1459*940Smrj 		if (dmar_flags & DDI_DMA_WRITE) {
1460*940Smrj 			bcopy(fdrp->fdr_addr, aligned_buf,
1461*940Smrj 			    (uint_t)fdrp->fdr_nbytes);
1462*940Smrj 		}
1463*940Smrj 
14640Sstevel@tonic-gate 		dmar_flags |= (DDI_DMA_STREAMING | DDI_DMA_PARTIAL);
14650Sstevel@tonic-gate 		rval = ddi_dma_addr_bind_handle(csb->csb_dmahandle, NULL,
1466*940Smrj 		    aligned_buf, (uint_t)fdrp->fdr_nbytes, dmar_flags,
1467*940Smrj 		    DDI_DMA_SLEEP, 0, &csb->csb_dmacookie,
1468*940Smrj 		    &csb->csb_dmacookiecnt);
14690Sstevel@tonic-gate 
14700Sstevel@tonic-gate 		if (rval == DDI_DMA_MAPPED) {
14710Sstevel@tonic-gate 			csb->csb_dmawincnt = 1;
14720Sstevel@tonic-gate 			csb->csb_handle_bound = 1;
14730Sstevel@tonic-gate 		} else if (rval == DDI_DMA_PARTIAL_MAP) {
14740Sstevel@tonic-gate 			csb->csb_handle_bound = 1;
14750Sstevel@tonic-gate 			if (ddi_dma_numwin(csb->csb_dmahandle,
14760Sstevel@tonic-gate 			    &csb->csb_dmawincnt) != DDI_SUCCESS) {
14770Sstevel@tonic-gate 				cmn_err(CE_WARN,
14780Sstevel@tonic-gate 				    "fdrawioctl: dma numwin failed\n");
14790Sstevel@tonic-gate 				rval = EINVAL;
14800Sstevel@tonic-gate 				goto out;
14810Sstevel@tonic-gate 			}
14820Sstevel@tonic-gate 		} else {
14830Sstevel@tonic-gate 			cmn_err(CE_WARN, "fdrawioctl: "
14840Sstevel@tonic-gate 			    "dma buf bind handle failed, rval = %d\n", rval);
14850Sstevel@tonic-gate 			rval = EINVAL;
14860Sstevel@tonic-gate 			goto out;
14870Sstevel@tonic-gate 		}
14880Sstevel@tonic-gate 	}
14890Sstevel@tonic-gate 
14900Sstevel@tonic-gate 	FCERRPRINT(FDEP_L1, FDEM_RAWI,
14910Sstevel@tonic-gate 	    (CE_CONT, "cmd: %x %x %x %x %x %x %x %x %x %x\n", csb->csb_cmd[0],
14920Sstevel@tonic-gate 	    csb->csb_cmd[1], csb->csb_cmd[2], csb->csb_cmd[3],
14930Sstevel@tonic-gate 	    csb->csb_cmd[4], csb->csb_cmd[5], csb->csb_cmd[6],
14940Sstevel@tonic-gate 	    csb->csb_cmd[7], csb->csb_cmd[8], csb->csb_cmd[9]));
14950Sstevel@tonic-gate 	FCERRPRINT(FDEP_L1, FDEM_RAWI,
14960Sstevel@tonic-gate 	    (CE_CONT, "nbytes: %x, opflags: %x, addr: %p, len: %x\n",
14970Sstevel@tonic-gate 	    csb->csb_ncmds, csb->csb_opflags, (void *)fdrp->fdr_addr,
14980Sstevel@tonic-gate 	    fdrp->fdr_nbytes));
14990Sstevel@tonic-gate 
15000Sstevel@tonic-gate 	/*
15010Sstevel@tonic-gate 	 * Note that we ignore any error returns from fdexec.
15020Sstevel@tonic-gate 	 * This is the way the driver has been, and it may be
15030Sstevel@tonic-gate 	 * that the raw ioctl senders simply don't want to
15040Sstevel@tonic-gate 	 * see any errors returned in this fashion.
15050Sstevel@tonic-gate 	 */
15060Sstevel@tonic-gate 
15070Sstevel@tonic-gate 	/*
15080Sstevel@tonic-gate 	 * VP/ix sense drive ioctl call checks for the error return.
15090Sstevel@tonic-gate 	 */
15100Sstevel@tonic-gate 
15110Sstevel@tonic-gate 	rval_exec = fdc_exec(fcp, sleep, change);
15120Sstevel@tonic-gate 
1513*940Smrj 	if (dmar_flags & DDI_DMA_READ) {
1514*940Smrj 		bcopy(aligned_buf, fdrp->fdr_addr, (uint_t)fdrp->fdr_nbytes);
1515*940Smrj 	}
1516*940Smrj 
15170Sstevel@tonic-gate 	FCERRPRINT(FDEP_L1, FDEM_RAWI,
15180Sstevel@tonic-gate 	    (CE_CONT, "rslt: %x %x %x %x %x %x %x %x %x %x\n", csb->csb_rslt[0],
15190Sstevel@tonic-gate 	    csb->csb_rslt[1], csb->csb_rslt[2], csb->csb_rslt[3],
15200Sstevel@tonic-gate 	    csb->csb_rslt[4], csb->csb_rslt[5], csb->csb_rslt[6],
15210Sstevel@tonic-gate 	    csb->csb_rslt[7], csb->csb_rslt[8], csb->csb_rslt[9]));
15220Sstevel@tonic-gate 
15230Sstevel@tonic-gate 	/* copy results into fdr */
15240Sstevel@tonic-gate 	for (i = 0; i <= (int)csb->csb_nrslts; i++)
15250Sstevel@tonic-gate 		fdrp->fdr_result[i] = csb->csb_rslt[i];
15260Sstevel@tonic-gate /*	fdrp->fdr_nbytes = fdc->c_csb.csb_rlen;  return resid */
15270Sstevel@tonic-gate 
15280Sstevel@tonic-gate out:
15290Sstevel@tonic-gate 	if (csb->csb_dmahandle) {
15300Sstevel@tonic-gate 		if (csb->csb_handle_bound) {
15310Sstevel@tonic-gate 			if (ddi_dma_unbind_handle(csb->csb_dmahandle) !=
15320Sstevel@tonic-gate 			    DDI_SUCCESS)
15330Sstevel@tonic-gate 				cmn_err(CE_WARN, "fdrawioctl: "
15340Sstevel@tonic-gate 				    "dma unbind handle failed\n");
15350Sstevel@tonic-gate 			csb->csb_handle_bound = 0;
15360Sstevel@tonic-gate 		}
1537*940Smrj 		if (mem_handle != NULL) {
1538*940Smrj 			ddi_dma_mem_free(&mem_handle);
1539*940Smrj 		}
15400Sstevel@tonic-gate 		ddi_dma_free_handle(&csb->csb_dmahandle);
15410Sstevel@tonic-gate 		csb->csb_dmahandle = NULL;
15420Sstevel@tonic-gate 	}
15430Sstevel@tonic-gate 	if ((fdrp->fdr_cmd[0] & 0x0f) == FO_SDRV) {
15440Sstevel@tonic-gate 		return (rval_exec);
15450Sstevel@tonic-gate 	}
15460Sstevel@tonic-gate 	return (rval);
15470Sstevel@tonic-gate }
15480Sstevel@tonic-gate 
15490Sstevel@tonic-gate void
15500Sstevel@tonic-gate encode(xlate_tbl_t *tablep, int val, uchar_t *rcode)
15510Sstevel@tonic-gate {
15520Sstevel@tonic-gate 	do {
15530Sstevel@tonic-gate 		if (tablep->value >= val) {
15540Sstevel@tonic-gate 			*rcode = tablep->code;
15550Sstevel@tonic-gate 			return;
15560Sstevel@tonic-gate 		}
15570Sstevel@tonic-gate 	} while ((++tablep)->value);
15580Sstevel@tonic-gate 	*rcode = tablep->code;
15590Sstevel@tonic-gate 	cmn_err(CE_WARN, "fdc encode failed, table %p val %x code %x\n",
15600Sstevel@tonic-gate 	    (void *)tablep, val, (uint_t)*rcode);
15610Sstevel@tonic-gate }
15620Sstevel@tonic-gate 
15630Sstevel@tonic-gate int
15640Sstevel@tonic-gate decode(xlate_tbl_t *tablep, int kode, int *rvalue)
15650Sstevel@tonic-gate {
15660Sstevel@tonic-gate 	do  {
15670Sstevel@tonic-gate 		if (tablep->code == kode) {
15680Sstevel@tonic-gate 			*rvalue = tablep->value;
15690Sstevel@tonic-gate 			return (0);
15700Sstevel@tonic-gate 		}
15710Sstevel@tonic-gate 	} while ((++tablep)->value);
15720Sstevel@tonic-gate 	return (-1);
15730Sstevel@tonic-gate }
15740Sstevel@tonic-gate 
15750Sstevel@tonic-gate 
15760Sstevel@tonic-gate void
15770Sstevel@tonic-gate fdcquiesce(struct fdcntlr *fcp)
15780Sstevel@tonic-gate {
15790Sstevel@tonic-gate 	int unit;
15800Sstevel@tonic-gate 
15810Sstevel@tonic-gate 	FCERRPRINT(FDEP_L2, FDEM_RESE, (CE_NOTE, "fdcquiesce fcp %p",
15820Sstevel@tonic-gate 		(void*)fcp));
15830Sstevel@tonic-gate 	ASSERT(MUTEX_HELD(&fcp->c_lock));
15840Sstevel@tonic-gate 
15850Sstevel@tonic-gate 	mutex_enter(&fcp->c_dorlock);
15860Sstevel@tonic-gate 
15870Sstevel@tonic-gate 	if (ddi_dmae_stop(fcp->c_dip, fcp->c_dmachan) != DDI_SUCCESS)
15880Sstevel@tonic-gate 		cmn_err(CE_WARN, "fdcquiesce: dmae stop failed, "
15890Sstevel@tonic-gate 			"dip %p, dmachan %x\n",
15900Sstevel@tonic-gate 			(void*)fcp->c_dip, fcp->c_dmachan);
15910Sstevel@tonic-gate 
15920Sstevel@tonic-gate 	fcp->c_digout = (fcp->c_digout & (FD_DMTREN | FD_DRSEL)) | FD_ENABLE;
15930Sstevel@tonic-gate 	outb(fcp->c_regbase + FCR_DOR, fcp->c_digout);
15940Sstevel@tonic-gate 	drv_usecwait(20);
15950Sstevel@tonic-gate 	fcp->c_digout |= FD_RSETZ;
15960Sstevel@tonic-gate 	outb(fcp->c_regbase + FCR_DOR, fcp->c_digout);
15970Sstevel@tonic-gate 
15980Sstevel@tonic-gate 	mutex_exit(&fcp->c_dorlock);
15990Sstevel@tonic-gate 
16000Sstevel@tonic-gate 	/* count resets */
16010Sstevel@tonic-gate 	fcp->fdstats.reset++;
16020Sstevel@tonic-gate 	fcp->c_curunit = -1;
16030Sstevel@tonic-gate 	for (unit = 0; unit < NFDUN; unit++)
16040Sstevel@tonic-gate 		fcp->c_curpcyl[unit] = -1;
16050Sstevel@tonic-gate 
16060Sstevel@tonic-gate 	if (fcp->c_chip >= i82077) {
16070Sstevel@tonic-gate 		(void) fdc_docmd(fcp, configurecmd, 4);
16080Sstevel@tonic-gate 		/*
16090Sstevel@tonic-gate 		 * Ignored return. If failed, warning was issued by fdc_docmd.
16100Sstevel@tonic-gate 		 */
16110Sstevel@tonic-gate 	}
16120Sstevel@tonic-gate }
16130Sstevel@tonic-gate 
16140Sstevel@tonic-gate void
16150Sstevel@tonic-gate fdcreadid(struct fdcntlr *fcp, struct fdcsb *csb)
16160Sstevel@tonic-gate {
16170Sstevel@tonic-gate 	static uchar_t readidcmd[2] = {FO_RDID | FO_MFM, 0};
16180Sstevel@tonic-gate 
16190Sstevel@tonic-gate 	readidcmd[1] = csb->csb_cmd[1];
16200Sstevel@tonic-gate 	(void) fdc_docmd(fcp, readidcmd, 2);
16210Sstevel@tonic-gate }
16220Sstevel@tonic-gate 
16230Sstevel@tonic-gate int
16240Sstevel@tonic-gate fdcseek(struct fdcntlr *fcp, int unit, int cyl)
16250Sstevel@tonic-gate {
16260Sstevel@tonic-gate 	static uchar_t seekabscmd[3] = {FO_SEEK, 0, 0};
16270Sstevel@tonic-gate 
16280Sstevel@tonic-gate 	FCERRPRINT(FDEP_L0, FDEM_RECA, (CE_CONT, "fdcseek unit %d to cyl %d\n",
16290Sstevel@tonic-gate 	    unit, cyl));
16300Sstevel@tonic-gate 	seekabscmd[1] = (uchar_t)unit;
16310Sstevel@tonic-gate 	seekabscmd[2] = (uchar_t)cyl;
16320Sstevel@tonic-gate 	return (fdc_docmd(fcp, seekabscmd, 3));
16330Sstevel@tonic-gate }
16340Sstevel@tonic-gate 
16350Sstevel@tonic-gate /*
16360Sstevel@tonic-gate  * Returns status of disk change line of selected drive.
16370Sstevel@tonic-gate  *	= 0 means diskette is present
16380Sstevel@tonic-gate  *	!= 0 means diskette was removed and current state is unknown
16390Sstevel@tonic-gate  */
16400Sstevel@tonic-gate int
16410Sstevel@tonic-gate fdcsense_chng(struct fdcntlr *fcp, int unit)
16420Sstevel@tonic-gate {
16430Sstevel@tonic-gate 	int digital_input;
16440Sstevel@tonic-gate 
16450Sstevel@tonic-gate 	FCERRPRINT(FDEP_L0, FDEM_SCHG,
16460Sstevel@tonic-gate 	    (CE_CONT, "fdcsense_chng unit %d\n", unit));
16470Sstevel@tonic-gate 	digital_input = inb(fcp->c_regbase + FCR_DIR);
16480Sstevel@tonic-gate 	if (fcp->c_mode == FDCMODE_30)
16490Sstevel@tonic-gate 		digital_input ^= FDI_DKCHG;
16500Sstevel@tonic-gate 	return (digital_input & FDI_DKCHG);
16510Sstevel@tonic-gate }
16520Sstevel@tonic-gate 
16530Sstevel@tonic-gate int
16540Sstevel@tonic-gate fdcsense_drv(struct fdcntlr *fcp, int unit)
16550Sstevel@tonic-gate {
16560Sstevel@tonic-gate 	static uchar_t sensedrvcmd[2] = {FO_SDRV, 0};
16570Sstevel@tonic-gate 	uchar_t senser;
16580Sstevel@tonic-gate 	int rval;
16590Sstevel@tonic-gate 
16600Sstevel@tonic-gate 	sensedrvcmd[1] = (uchar_t)unit;
16610Sstevel@tonic-gate 	(void) fdc_docmd(fcp, sensedrvcmd, 2);
16620Sstevel@tonic-gate 	/*
16630Sstevel@tonic-gate 	 * Ignored return. If failed, warning was issued by fdc_docmd.
16640Sstevel@tonic-gate 	 * fdc_results retrieves the controller/drive status
16650Sstevel@tonic-gate 	 */
16660Sstevel@tonic-gate 	if (rval = fdc_result(fcp, &senser, 1))
16670Sstevel@tonic-gate 		goto done;
16680Sstevel@tonic-gate 	if (senser & S3_WPROT)
16690Sstevel@tonic-gate 		fcp->c_unit[unit]->fj_flags |= FUNIT_WPROT;
16700Sstevel@tonic-gate 	else
16710Sstevel@tonic-gate 		fcp->c_unit[unit]->fj_flags &= ~FUNIT_WPROT;
16720Sstevel@tonic-gate done:
16730Sstevel@tonic-gate 	return (rval);
16740Sstevel@tonic-gate }
16750Sstevel@tonic-gate 
16760Sstevel@tonic-gate int
16770Sstevel@tonic-gate fdcsense_int(struct fdcntlr *fcp, int *unitp, int *cylp)
16780Sstevel@tonic-gate {
16790Sstevel@tonic-gate 	uchar_t senser[2];
16800Sstevel@tonic-gate 	int rval;
16810Sstevel@tonic-gate 
16820Sstevel@tonic-gate 	(void) fdc_docmd(fcp, &senseintcmd, 1);
16830Sstevel@tonic-gate 	/*
16840Sstevel@tonic-gate 	 * Ignored return. If failed, warning was issued by fdc_docmd.
16850Sstevel@tonic-gate 	 * fdc_results retrieves the controller/drive status
16860Sstevel@tonic-gate 	 */
16870Sstevel@tonic-gate 
16880Sstevel@tonic-gate 	if (!(rval = fdc_result(fcp, senser, 2))) {
16890Sstevel@tonic-gate 		if ((*senser & (S0_IVCMD | S0_SEKEND | S0_ECHK)) != S0_SEKEND)
16900Sstevel@tonic-gate 			rval = 1;
16910Sstevel@tonic-gate 		if (unitp)
16920Sstevel@tonic-gate 			*unitp = *senser & 3;
16930Sstevel@tonic-gate 		if (cylp)
16940Sstevel@tonic-gate 			*cylp = senser[1];
16950Sstevel@tonic-gate 	}
16960Sstevel@tonic-gate 	return (rval);
16970Sstevel@tonic-gate }
16980Sstevel@tonic-gate 
16990Sstevel@tonic-gate int
17000Sstevel@tonic-gate fdcspecify(struct fdcntlr *fcp, int xferrate, int steprate, int hlt)
17010Sstevel@tonic-gate {
17020Sstevel@tonic-gate 	static uchar_t perpindcmd[2] = {FO_PERP, 0};
17030Sstevel@tonic-gate 	static uchar_t specifycmd[3] = {FO_SPEC, 0, 0};
17040Sstevel@tonic-gate 
17050Sstevel@tonic-gate 	encode(drate_mfm, xferrate, &fcp->c_config);
17060Sstevel@tonic-gate 	outb(fcp->c_regbase + FCR_CCR, fcp->c_config);
17070Sstevel@tonic-gate 
17080Sstevel@tonic-gate 	if (fcp->c_chip >= i82077) {
17090Sstevel@tonic-gate 		/*
17100Sstevel@tonic-gate 		 * Use old style perpendicular mode command of 82077.
17110Sstevel@tonic-gate 		 */
17120Sstevel@tonic-gate 		if (xferrate == 1000) {
17130Sstevel@tonic-gate 			/* Set GAP and WGATE */
17140Sstevel@tonic-gate 			perpindcmd[1] = 3;
17150Sstevel@tonic-gate 			/* double step rate because xlate table is for 500Kb */
17160Sstevel@tonic-gate 			steprate <<= 1;
17170Sstevel@tonic-gate 			hlt <<= 1;
17180Sstevel@tonic-gate 		} else
17190Sstevel@tonic-gate 			perpindcmd[1] = 0;
17200Sstevel@tonic-gate 		(void) fdc_docmd(fcp, perpindcmd, 2);
17210Sstevel@tonic-gate 		/*
17220Sstevel@tonic-gate 		 * Ignored return. If failed, warning was issued by fdc_docmd.
17230Sstevel@tonic-gate 		 */
17240Sstevel@tonic-gate 	}
17250Sstevel@tonic-gate 	encode(step_rate, steprate, &fcp->c_hutsrt);
17260Sstevel@tonic-gate 	specifycmd[1] = fcp->c_hutsrt |= 0x0F;	/* use max head unload time */
17270Sstevel@tonic-gate 	hlt = (hlt >= 256) ? 0 : (hlt >> 1);	/* encode head load time */
17280Sstevel@tonic-gate 	specifycmd[2] = fcp->c_hlt = hlt << 1;	/* make room for DMA bit */
17290Sstevel@tonic-gate 	return (fdc_docmd(fcp, specifycmd, 3));
17300Sstevel@tonic-gate }
17310Sstevel@tonic-gate 
17320Sstevel@tonic-gate int
17330Sstevel@tonic-gate fdcspdchange(struct fdcntlr *fcp, struct fcu_obj *fjp, int rpm)
17340Sstevel@tonic-gate {
17350Sstevel@tonic-gate 	int	retcode = 0;
17360Sstevel@tonic-gate 	uint_t	ddic;
17370Sstevel@tonic-gate 	uchar_t	deselect = 0;
17380Sstevel@tonic-gate 	uchar_t	ds_code;
17390Sstevel@tonic-gate 	uchar_t	enable_code;
17400Sstevel@tonic-gate 	uchar_t	save;
17410Sstevel@tonic-gate 
17420Sstevel@tonic-gate 	if (((fcp->c_flags & FCFLG_DSOUT) == 0 && rpm <= fjp->fj_rotspd) ||
17430Sstevel@tonic-gate 	    ((fcp->c_flags & FCFLG_DSOUT) && (fjp->fj_flags & FUNIT_3DMODE) &&
17440Sstevel@tonic-gate 	    rpm > fjp->fj_rotspd)) {
17450Sstevel@tonic-gate 		return (0);
17460Sstevel@tonic-gate 	}
17470Sstevel@tonic-gate 
17480Sstevel@tonic-gate 	FCERRPRINT(FDEP_L1, FDEM_SCHG,
17490Sstevel@tonic-gate 	    (CE_CONT, "fdcspdchange: %d rpm\n", rpm));
17500Sstevel@tonic-gate 	ASSERT(MUTEX_HELD(&fcp->c_dorlock));
17510Sstevel@tonic-gate 
17520Sstevel@tonic-gate 	switch (fcp->c_chip) {
17530Sstevel@tonic-gate 	default:
17540Sstevel@tonic-gate 		break;
17550Sstevel@tonic-gate 	case i82077:
17560Sstevel@tonic-gate 		break;
17570Sstevel@tonic-gate 
17580Sstevel@tonic-gate 	case PC87322:
17590Sstevel@tonic-gate 		{
17600Sstevel@tonic-gate 		uchar_t nscmodecmd[5] = {FO_MODE, 0x02, 0x00, 0xC8, 0x00};
17610Sstevel@tonic-gate 
17620Sstevel@tonic-gate 		if (rpm > fjp->fj_rotspd) {
17630Sstevel@tonic-gate 			nscmodecmd[3] ^= 0xC0;
17640Sstevel@tonic-gate 			retcode = (fcp->c_flags ^ FCFLG_DSOUT) ||
17650Sstevel@tonic-gate 			    (fjp->fj_flags ^ FUNIT_3DMODE);
17660Sstevel@tonic-gate 			fcp->c_flags |= FCFLG_DSOUT;
17670Sstevel@tonic-gate 			fjp->fj_flags |= FUNIT_3DMODE;
17680Sstevel@tonic-gate 		} else {
17690Sstevel@tonic-gate 			/* program DENSEL to default output */
17700Sstevel@tonic-gate 			fcp->c_flags &= ~FCFLG_DSOUT;
17710Sstevel@tonic-gate 			retcode = fjp->fj_flags & FUNIT_3DMODE;
17720Sstevel@tonic-gate 			fjp->fj_flags &= ~FUNIT_3DMODE;
17730Sstevel@tonic-gate 		}
17740Sstevel@tonic-gate 		if (retcode && (fcp->c_digout & FD_DRSEL) == fcp->c_curunit) {
17750Sstevel@tonic-gate 			/* de-select drive while changing speed */
17760Sstevel@tonic-gate 			deselect = fcp->c_digout ^ FD_DRSEL;
17770Sstevel@tonic-gate 			outb(fcp->c_regbase + FCR_DOR, deselect);
17780Sstevel@tonic-gate 		}
17790Sstevel@tonic-gate 
17800Sstevel@tonic-gate 		(void) fdc_docmd(fcp, nscmodecmd, 5);
17810Sstevel@tonic-gate 		/*
17820Sstevel@tonic-gate 		 * Ignored return. If failed, warning was issued by fdc_docmd.
17830Sstevel@tonic-gate 		 */
17840Sstevel@tonic-gate 		break;
17850Sstevel@tonic-gate 		}
17860Sstevel@tonic-gate 
17870Sstevel@tonic-gate 	case FDC37C665:
17880Sstevel@tonic-gate 		enable_code = FSA_ENA5;
17890Sstevel@tonic-gate 		goto SMC_config;
17900Sstevel@tonic-gate 
17910Sstevel@tonic-gate 	case FDC37C666:
17920Sstevel@tonic-gate 		enable_code = FSA_ENA6;
17930Sstevel@tonic-gate SMC_config:
17940Sstevel@tonic-gate 		if (rpm > fjp->fj_rotspd) {
17950Sstevel@tonic-gate 			/* force DENSEL output to active LOW */
17960Sstevel@tonic-gate 			ds_code = FSB_DSHI;
17970Sstevel@tonic-gate 			retcode = (fcp->c_flags ^ FCFLG_DSOUT) ||
17980Sstevel@tonic-gate 			    (fjp->fj_flags ^ FUNIT_3DMODE);
17990Sstevel@tonic-gate 			fcp->c_flags |= FCFLG_DSOUT;
18000Sstevel@tonic-gate 			fjp->fj_flags |= FUNIT_3DMODE;
18010Sstevel@tonic-gate 		} else {
18020Sstevel@tonic-gate 			/* program DENSEL to default output */
18030Sstevel@tonic-gate 			ds_code = 0;
18040Sstevel@tonic-gate 			fcp->c_flags &= ~FCFLG_DSOUT;
18050Sstevel@tonic-gate 			retcode = fjp->fj_flags & FUNIT_3DMODE;
18060Sstevel@tonic-gate 			fjp->fj_flags &= ~FUNIT_3DMODE;
18070Sstevel@tonic-gate 		}
18080Sstevel@tonic-gate 		if (retcode && (fcp->c_digout & FD_DRSEL) == fcp->c_curunit) {
18090Sstevel@tonic-gate 			/* de-select drive while changing speed */
18100Sstevel@tonic-gate 			deselect = fcp->c_digout ^ FD_DRSEL;
18110Sstevel@tonic-gate 			outb(fcp->c_regbase + FCR_DOR, deselect);
18120Sstevel@tonic-gate 		}
18130Sstevel@tonic-gate 		save = inb(fcp->c_regbase + FCR_SRA);
18140Sstevel@tonic-gate 
18150Sstevel@tonic-gate 		/* enter configuration mode */
18160Sstevel@tonic-gate 		ddic = ddi_enter_critical();
18170Sstevel@tonic-gate 		outb(fcp->c_regbase + FCR_SRA, enable_code);
18180Sstevel@tonic-gate 		outb(fcp->c_regbase + FCR_SRA, enable_code);
18190Sstevel@tonic-gate 		ddi_exit_critical(ddic);
18200Sstevel@tonic-gate 
18210Sstevel@tonic-gate 		outb(fcp->c_regbase + FCR_SRA, FSA_CR5);
18220Sstevel@tonic-gate 		enable_code = inb(fcp->c_regbase + FCR_SRB) & FSB_DSDEF;
18230Sstevel@tonic-gate 		/* update DENSEL mode bits */
18240Sstevel@tonic-gate 		outb(fcp->c_regbase + FCR_SRB, enable_code | ds_code);
18250Sstevel@tonic-gate 
18260Sstevel@tonic-gate 		/* exit configuration mode */
18270Sstevel@tonic-gate 		outb(fcp->c_regbase + FCR_SRA, FSA_DISB);
18280Sstevel@tonic-gate 		drv_usecwait(10);
18290Sstevel@tonic-gate 		outb(fcp->c_regbase + FCR_SRA, save);
18300Sstevel@tonic-gate 		break;
18310Sstevel@tonic-gate 	}
18320Sstevel@tonic-gate 	if (deselect)
18330Sstevel@tonic-gate 		/* reselect drive */
18340Sstevel@tonic-gate 		outb(fcp->c_regbase + FCR_DOR, fcp->c_digout);
18350Sstevel@tonic-gate 	return (retcode);
18360Sstevel@tonic-gate }
18370Sstevel@tonic-gate 
18380Sstevel@tonic-gate static int
18390Sstevel@tonic-gate fdc_motorsm(struct fcu_obj *fjp, int input, int timeval)
18400Sstevel@tonic-gate {
18410Sstevel@tonic-gate 	struct fdcntlr *fcp = fjp->fj_fdc;
18420Sstevel@tonic-gate 	int unit = fjp->fj_unit & 3;
18430Sstevel@tonic-gate 	int old_mstate;
18440Sstevel@tonic-gate 	int rval = 0;
18450Sstevel@tonic-gate 	uchar_t motorbit;
18460Sstevel@tonic-gate 
18470Sstevel@tonic-gate 	ASSERT(MUTEX_HELD(&fcp->c_dorlock));
18480Sstevel@tonic-gate 	old_mstate = fcp->c_mtrstate[unit];
18490Sstevel@tonic-gate 	encode(motor_onbits, unit, &motorbit);
18500Sstevel@tonic-gate 
18510Sstevel@tonic-gate 	switch (input) {
18520Sstevel@tonic-gate 	case FMI_TIMER:		/* timer expired */
18530Sstevel@tonic-gate 		fcp->c_motort[unit] = 0;
18540Sstevel@tonic-gate 		switch (old_mstate) {
18550Sstevel@tonic-gate 		case FMS_START:
18560Sstevel@tonic-gate 		case FMS_DELAY:
18570Sstevel@tonic-gate 			fcp->c_mtrstate[unit] = FMS_ON;
18580Sstevel@tonic-gate 			break;
18590Sstevel@tonic-gate 		case FMS_KILLST:
18600Sstevel@tonic-gate 			fcp->c_motort[unit] = timeout(fdmotort, (void *)fjp,
18610Sstevel@tonic-gate 			    drv_usectohz(1000000));
18620Sstevel@tonic-gate 			fcp->c_mtrstate[unit] = FMS_IDLE;
18630Sstevel@tonic-gate 			break;
18640Sstevel@tonic-gate 		case FMS_IDLE:
18650Sstevel@tonic-gate 			fcp->c_digout &= ~motorbit;
18660Sstevel@tonic-gate 			outb(fcp->c_regbase + FCR_DOR, fcp->c_digout);
18670Sstevel@tonic-gate 			fcp->c_mtrstate[unit] = FMS_OFF;
18680Sstevel@tonic-gate 			fjp->fj_flags &= ~FUNIT_3DMODE;
18690Sstevel@tonic-gate 			break;
18700Sstevel@tonic-gate 		case 86:
18710Sstevel@tonic-gate 			rval = -1;
18720Sstevel@tonic-gate 			break;
18730Sstevel@tonic-gate 		case FMS_OFF:
18740Sstevel@tonic-gate 		case FMS_ON:
18750Sstevel@tonic-gate 		default:
18760Sstevel@tonic-gate 			rval = -2;
18770Sstevel@tonic-gate 		}
18780Sstevel@tonic-gate 		break;
18790Sstevel@tonic-gate 
18800Sstevel@tonic-gate 	case FMI_STARTCMD:	/* start command */
18810Sstevel@tonic-gate 		switch (old_mstate) {
18820Sstevel@tonic-gate 		case FMS_IDLE:
18830Sstevel@tonic-gate 			fcp->c_mtrstate[unit] = 86;
18840Sstevel@tonic-gate 			mutex_exit(&fcp->c_dorlock);
18850Sstevel@tonic-gate 			(void) untimeout(fcp->c_motort[unit]);
18860Sstevel@tonic-gate 			mutex_enter(&fcp->c_dorlock);
18870Sstevel@tonic-gate 			fcp->c_motort[unit] = 0;
18880Sstevel@tonic-gate 			fcp->c_mtrstate[unit] = FMS_ON;
18890Sstevel@tonic-gate 			break;
18900Sstevel@tonic-gate 		case FMS_OFF:
18910Sstevel@tonic-gate 			fcp->c_digout |= motorbit;
18920Sstevel@tonic-gate 			outb(fcp->c_regbase + FCR_DOR, fcp->c_digout);
18930Sstevel@tonic-gate 
18940Sstevel@tonic-gate 			/* start motor_spinup_timer */
18950Sstevel@tonic-gate 			ASSERT(timeval > 0);
18960Sstevel@tonic-gate 			fcp->c_motort[unit] = timeout(fdmotort,  (void *)fjp,
18970Sstevel@tonic-gate 			    drv_usectohz(100000 * timeval));
18980Sstevel@tonic-gate 			/* FALLTHROUGH */
18990Sstevel@tonic-gate 		case FMS_KILLST:
19000Sstevel@tonic-gate 			fcp->c_mtrstate[unit] = FMS_START;
19010Sstevel@tonic-gate 			break;
19020Sstevel@tonic-gate 		default:
19030Sstevel@tonic-gate 			rval = -2;
19040Sstevel@tonic-gate 		}
19050Sstevel@tonic-gate 		break;
19060Sstevel@tonic-gate 
19070Sstevel@tonic-gate 	case FMI_RSTARTCMD:	/* restart command */
19080Sstevel@tonic-gate 		if (fcp->c_motort[unit] != 0) {
19090Sstevel@tonic-gate 			fcp->c_mtrstate[unit] = 86;
19100Sstevel@tonic-gate 			mutex_exit(&fcp->c_dorlock);
19110Sstevel@tonic-gate 			(void) untimeout(fcp->c_motort[unit]);
19120Sstevel@tonic-gate 			mutex_enter(&fcp->c_dorlock);
19130Sstevel@tonic-gate 		}
19140Sstevel@tonic-gate 		ASSERT(timeval > 0);
19150Sstevel@tonic-gate 		fcp->c_motort[unit] = timeout(fdmotort, (void *)fjp,
19160Sstevel@tonic-gate 		    drv_usectohz(100000 * timeval));
19170Sstevel@tonic-gate 		fcp->c_mtrstate[unit] = FMS_START;
19180Sstevel@tonic-gate 		break;
19190Sstevel@tonic-gate 
19200Sstevel@tonic-gate 	case FMI_DELAYCMD:	/* delay command */
19210Sstevel@tonic-gate 		if (fcp->c_motort[unit] == 0)
19220Sstevel@tonic-gate 			fcp->c_motort[unit] = timeout(fdmotort,  (void *)fjp,
19230Sstevel@tonic-gate 			    drv_usectohz(15000));
19240Sstevel@tonic-gate 		fcp->c_mtrstate[unit] = FMS_DELAY;
19250Sstevel@tonic-gate 		break;
19260Sstevel@tonic-gate 
19270Sstevel@tonic-gate 	case FMI_IDLECMD:	/* idle command */
19280Sstevel@tonic-gate 		switch (old_mstate) {
19290Sstevel@tonic-gate 		case FMS_DELAY:
19300Sstevel@tonic-gate 			fcp->c_mtrstate[unit] = 86;
19310Sstevel@tonic-gate 			mutex_exit(&fcp->c_dorlock);
19320Sstevel@tonic-gate 			(void) untimeout(fcp->c_motort[unit]);
19330Sstevel@tonic-gate 			mutex_enter(&fcp->c_dorlock);
19340Sstevel@tonic-gate 			/* FALLTHROUGH */
19350Sstevel@tonic-gate 		case FMS_ON:
19360Sstevel@tonic-gate 			ASSERT(timeval > 0);
19370Sstevel@tonic-gate 			fcp->c_motort[unit] = timeout(fdmotort, (void *)fjp,
19380Sstevel@tonic-gate 			    drv_usectohz(100000 * timeval));
19390Sstevel@tonic-gate 			fcp->c_mtrstate[unit] = FMS_IDLE;
19400Sstevel@tonic-gate 			break;
19410Sstevel@tonic-gate 		case FMS_START:
19420Sstevel@tonic-gate 			fcp->c_mtrstate[unit] = FMS_KILLST;
19430Sstevel@tonic-gate 			break;
19440Sstevel@tonic-gate 		default:
19450Sstevel@tonic-gate 			rval = -2;
19460Sstevel@tonic-gate 		}
19470Sstevel@tonic-gate 		break;
19480Sstevel@tonic-gate 
19490Sstevel@tonic-gate 	default:
19500Sstevel@tonic-gate 		rval = -3;
19510Sstevel@tonic-gate 	}
19520Sstevel@tonic-gate 	if (rval) {
19530Sstevel@tonic-gate 		FCERRPRINT(FDEP_L4, FDEM_EXEC, (CE_WARN,
19540Sstevel@tonic-gate 		    "fdc_motorsm: unit %d  bad input %d or bad state %d",
19550Sstevel@tonic-gate 		    (int)fjp->fj_unit, input, old_mstate));
19560Sstevel@tonic-gate #if 0
19570Sstevel@tonic-gate 		cmn_err(CE_WARN,
19580Sstevel@tonic-gate 		    "fdc_motorsm: unit %d  bad input %d or bad state %d\n",
19590Sstevel@tonic-gate 		    (int)fjp->fj_unit, input, old_mstate);
19600Sstevel@tonic-gate 		fcp->c_mtrstate[unit] = FMS_OFF;
19610Sstevel@tonic-gate 		if (fcp->c_motort[unit] != 0) {
19620Sstevel@tonic-gate 			mutex_exit(&fcp->c_dorlock);
19630Sstevel@tonic-gate 			(void) untimeout(fcp->c_motort[unit]);
19640Sstevel@tonic-gate 			mutex_enter(&fcp->c_dorlock);
19650Sstevel@tonic-gate 			fcp->c_motort[unit] = 0;
19660Sstevel@tonic-gate 		}
19670Sstevel@tonic-gate #endif
19680Sstevel@tonic-gate 	} else
19690Sstevel@tonic-gate 		FCERRPRINT(FDEP_L0, FDEM_EXEC,
19700Sstevel@tonic-gate 		    (CE_CONT, "fdc_motorsm unit %d: input %d,  %d -> %d\n",
19710Sstevel@tonic-gate 		    (int)fjp->fj_unit, input, old_mstate,
19720Sstevel@tonic-gate 		    fcp->c_mtrstate[unit]));
19730Sstevel@tonic-gate 	return (rval);
19740Sstevel@tonic-gate }
19750Sstevel@tonic-gate 
19760Sstevel@tonic-gate /*
19770Sstevel@tonic-gate  * fdmotort
19780Sstevel@tonic-gate  *	is called from timeout() when a motor timer has expired.
19790Sstevel@tonic-gate  */
19800Sstevel@tonic-gate static void
19810Sstevel@tonic-gate fdmotort(void *arg)
19820Sstevel@tonic-gate {
19830Sstevel@tonic-gate 	struct fcu_obj *fjp = (struct fcu_obj *)arg;
19840Sstevel@tonic-gate 	struct fdcntlr *fcp = fjp->fj_fdc;
19850Sstevel@tonic-gate 	struct fdcsb *csb = &fcp->c_csb;
19860Sstevel@tonic-gate 	int unit = fjp->fj_unit & 3;
19870Sstevel@tonic-gate 	int mval;
19880Sstevel@tonic-gate 	int newxstate = 0;
19890Sstevel@tonic-gate 
19900Sstevel@tonic-gate 	mutex_enter(&fcp->c_dorlock);
19910Sstevel@tonic-gate 	mval = fdc_motorsm(fjp, FMI_TIMER, 0);
19920Sstevel@tonic-gate 	mutex_exit(&fcp->c_dorlock);
19930Sstevel@tonic-gate 	if (mval < 0)
19940Sstevel@tonic-gate 		return;
19950Sstevel@tonic-gate 
19960Sstevel@tonic-gate 	mutex_enter(&fcp->c_lock);
19970Sstevel@tonic-gate 
19980Sstevel@tonic-gate 	if ((fcp->c_flags & FCFLG_WAITING) &&
19990Sstevel@tonic-gate 	    fcp->c_mtrstate[unit] == FMS_ON &&
20000Sstevel@tonic-gate 	    (csb->csb_xstate == FXS_MTRON || csb->csb_xstate == FXS_HDST ||
20010Sstevel@tonic-gate 	    csb->csb_xstate == FXS_DKCHGX))
20020Sstevel@tonic-gate 		newxstate = fdc_statemach(fcp);
20030Sstevel@tonic-gate 		if (newxstate == -1) {
20040Sstevel@tonic-gate 			FCERRPRINT(FDEP_L3, FDEM_EXEC,
20050Sstevel@tonic-gate 			    (CE_WARN,
20060Sstevel@tonic-gate 			    "fdc_motort unit %d: motor ready but bad xstate",
20070Sstevel@tonic-gate 			    (int)fjp->fj_unit));
20080Sstevel@tonic-gate 			fcp->c_csb.csb_cmdstat = EIO;
20090Sstevel@tonic-gate 		}
20100Sstevel@tonic-gate 		if (newxstate == -1 || newxstate == FXS_END) {
20110Sstevel@tonic-gate 			fcp->c_flags ^= FCFLG_WAITING;
20120Sstevel@tonic-gate 			cv_signal(&fcp->c_iocv);
20130Sstevel@tonic-gate 		}
20140Sstevel@tonic-gate 	mutex_exit(&fcp->c_lock);
20150Sstevel@tonic-gate }
20160Sstevel@tonic-gate 
20170Sstevel@tonic-gate /*
20180Sstevel@tonic-gate  * DMA interrupt service routine
20190Sstevel@tonic-gate  *
20200Sstevel@tonic-gate  *	Called by EISA dma interrupt service routine when buffer chaining
20210Sstevel@tonic-gate  *	is required.
20220Sstevel@tonic-gate  */
20230Sstevel@tonic-gate 
20240Sstevel@tonic-gate ddi_dma_cookie_t *
20250Sstevel@tonic-gate fdc_dmae_isr(struct fdcntlr *fcp)
20260Sstevel@tonic-gate {
20270Sstevel@tonic-gate 	struct fdcsb *csb = &fcp->c_csb;
20280Sstevel@tonic-gate 	off_t off;
20290Sstevel@tonic-gate 	size_t len;
20300Sstevel@tonic-gate 
20310Sstevel@tonic-gate 	if (csb->csb_dmahandle && !csb->csb_cmdstat) {
20320Sstevel@tonic-gate 		if (++csb->csb_dmacurrcookie < csb->csb_dmacookiecnt) {
20330Sstevel@tonic-gate 			ddi_dma_nextcookie(csb->csb_dmahandle,
20340Sstevel@tonic-gate 			    &csb->csb_dmacookie);
20350Sstevel@tonic-gate 			return (&csb->csb_dmacookie);
20360Sstevel@tonic-gate 		} else if (++csb->csb_dmacurrwin < csb->csb_dmawincnt) {
20370Sstevel@tonic-gate 			if (ddi_dma_getwin(csb->csb_dmahandle,
20380Sstevel@tonic-gate 			    csb->csb_dmacurrwin, &off, &len,
20390Sstevel@tonic-gate 			    &csb->csb_dmacookie,
20400Sstevel@tonic-gate 			    &csb->csb_dmacookiecnt) != DDI_SUCCESS) {
20410Sstevel@tonic-gate 				return (NULL);
20420Sstevel@tonic-gate 			}
20430Sstevel@tonic-gate 			csb->csb_dmacurrcookie = 0;
20440Sstevel@tonic-gate 			return (&csb->csb_dmacookie);
20450Sstevel@tonic-gate 		}
20460Sstevel@tonic-gate 	} else
20470Sstevel@tonic-gate 		cmn_err(CE_WARN, "fdc: unsolicited DMA interrupt\n");
20480Sstevel@tonic-gate 	return (NULL);
20490Sstevel@tonic-gate }
20500Sstevel@tonic-gate 
20510Sstevel@tonic-gate 
20520Sstevel@tonic-gate /*
20530Sstevel@tonic-gate  * returns:
20540Sstevel@tonic-gate  *	0 if all ok,
20550Sstevel@tonic-gate  *	ENXIO - diskette not in drive
20560Sstevel@tonic-gate  *	ETIMEDOUT - for immediate operations that timed out
20570Sstevel@tonic-gate  *	EBUSY - if stupid chip is locked busy???
20580Sstevel@tonic-gate  *	ENOEXEC - for timeout during sending cmds to chip
20590Sstevel@tonic-gate  *
20600Sstevel@tonic-gate  * to sleep: set sleep
20610Sstevel@tonic-gate  * to check for disk changed: set change
20620Sstevel@tonic-gate  */
20630Sstevel@tonic-gate static int
20640Sstevel@tonic-gate fdc_exec(struct fdcntlr *fcp, int sleep, int change)
20650Sstevel@tonic-gate {
20660Sstevel@tonic-gate 	struct ddi_dmae_req dmaereq;
20670Sstevel@tonic-gate 	struct fcu_obj *fjp;
20680Sstevel@tonic-gate 	struct fdcsb *csb;
20690Sstevel@tonic-gate 	off_t off;
20700Sstevel@tonic-gate 	size_t len;
20710Sstevel@tonic-gate 	int unit;
20720Sstevel@tonic-gate 
20730Sstevel@tonic-gate 	mutex_enter(&fcp->c_lock);
20740Sstevel@tonic-gate 	FCERRPRINT(FDEP_L0, FDEM_EXEC,
20750Sstevel@tonic-gate 	    (CE_CONT, "fdc_exec: sleep %x change %x\n", sleep, change));
20760Sstevel@tonic-gate 	csb = &fcp->c_csb;
20770Sstevel@tonic-gate 	unit = csb->csb_drive;
20780Sstevel@tonic-gate 	fjp = fcp->c_unit[unit];
20790Sstevel@tonic-gate 
20800Sstevel@tonic-gate 	if (csb->csb_opflags & CSB_OFINRPT) {
20810Sstevel@tonic-gate 		if (*csb->csb_cmd == FO_RECAL)
20820Sstevel@tonic-gate 			csb->csb_npcyl = 0;
20830Sstevel@tonic-gate 		else if ((*csb->csb_cmd & ~FO_MFM) != FO_FRMT)
20840Sstevel@tonic-gate 			csb->csb_npcyl =
20850Sstevel@tonic-gate 			    csb->csb_cmd[2] * fjp->fj_chars->fdc_steps;
20860Sstevel@tonic-gate 		csb->csb_xstate = FXS_START;
20870Sstevel@tonic-gate 	} else
20880Sstevel@tonic-gate 		csb->csb_xstate = FXS_DOIT;
20890Sstevel@tonic-gate 	csb->csb_retrys = 0;
20900Sstevel@tonic-gate 	csb->csb_ourtrys = 0;
20910Sstevel@tonic-gate 
20920Sstevel@tonic-gate 	if (csb->csb_dmahandle) {
20930Sstevel@tonic-gate 		/* ensure that entire format xfer is in one cookie */
20940Sstevel@tonic-gate 		/*
20950Sstevel@tonic-gate 		 * The change from  ddi_dma_buf/addr_setup() to
20960Sstevel@tonic-gate 		 * ddi_dma_buf/addr_bind_handle() has already loaded
20970Sstevel@tonic-gate 		 * the first DMA window and cookie.
20980Sstevel@tonic-gate 		 */
20990Sstevel@tonic-gate 		if ((*csb->csb_cmd & ~FO_MFM) == FO_FRMT &&
21000Sstevel@tonic-gate 		    (4 * csb->csb_cmd[3]) != csb->csb_dmacookie.dmac_size) {
21010Sstevel@tonic-gate 			mutex_exit(&fcp->c_lock);
21020Sstevel@tonic-gate 			return (EINVAL);
21030Sstevel@tonic-gate 		}
21040Sstevel@tonic-gate 	}
21050Sstevel@tonic-gate 
21060Sstevel@tonic-gate retry:
21070Sstevel@tonic-gate 	if (fcp->c_curunit != unit || !(fjp->fj_flags & FUNIT_CHAROK)) {
21080Sstevel@tonic-gate 		fcp->c_curunit = unit;
21090Sstevel@tonic-gate 		fjp->fj_flags |= FUNIT_CHAROK;
21100Sstevel@tonic-gate 		if (fjp->fj_chars->fdc_transfer_rate == 417) {
21110Sstevel@tonic-gate 			/* XXX hack for fdformat */
21120Sstevel@tonic-gate 			/* fjp->fj_chars->fdc_transfer_rate == 500;	*/
21130Sstevel@tonic-gate 			fjp->fj_attr->fda_rotatespd = 360;
21140Sstevel@tonic-gate 		}
21150Sstevel@tonic-gate 		if (fdcspecify(fcp, fjp->fj_chars->fdc_transfer_rate,
21160Sstevel@tonic-gate 		    fjp->fj_drive->fdd_steprate, 40))
21170Sstevel@tonic-gate 			cmn_err(CE_WARN,
21180Sstevel@tonic-gate 			    "fdc_select: controller setup rejected "
21190Sstevel@tonic-gate 			    "fdcntrl %p transfer rate %x step rate %x "
21200Sstevel@tonic-gate 			    "head load time 40\n", (void*)fcp,
21210Sstevel@tonic-gate 			    fjp->fj_chars->fdc_transfer_rate,
21220Sstevel@tonic-gate 			    fjp->fj_drive->fdd_steprate);
21230Sstevel@tonic-gate 
21240Sstevel@tonic-gate 		mutex_enter(&fcp->c_dorlock);
21250Sstevel@tonic-gate 		if (fdcspdchange(fcp, fjp, fjp->fj_attr->fda_rotatespd)) {
21260Sstevel@tonic-gate 			/* 3D drive requires 500 ms for speed change */
21270Sstevel@tonic-gate 			(void) fdc_motorsm(fjp, FMI_RSTARTCMD, 5);
21280Sstevel@tonic-gate 			/*
21290Sstevel@tonic-gate 			 * Return value ignored - fdcmotort deals with failure.
21300Sstevel@tonic-gate 			 */
21310Sstevel@tonic-gate 		}
21320Sstevel@tonic-gate 		mutex_exit(&fcp->c_dorlock);
21330Sstevel@tonic-gate 	}
21340Sstevel@tonic-gate 
21350Sstevel@tonic-gate 	/*
21360Sstevel@tonic-gate 	 * If checking for disk_change is enabled
21370Sstevel@tonic-gate 	 * (i.e. not seeking in fdresetchng),
21380Sstevel@tonic-gate 	 * we sample the DSKCHG line to see if the diskette has wandered away.
21390Sstevel@tonic-gate 	 */
21400Sstevel@tonic-gate 	if (change && fdcsense_chng(fcp, unit)) {
21410Sstevel@tonic-gate 		FCERRPRINT(FDEP_L3, FDEM_EXEC,
21420Sstevel@tonic-gate 		    (CE_WARN, "diskette %d changed!!!", csb->csb_drive));
21430Sstevel@tonic-gate 		fcp->c_unit[unit]->fj_flags |= FUNIT_CHANGED;
21440Sstevel@tonic-gate 		/*
21450Sstevel@tonic-gate 		 * If the diskette is still gone... so are we, adios!
21460Sstevel@tonic-gate 		 */
21470Sstevel@tonic-gate 		if (fdcheckdisk(fcp, unit)) {
21480Sstevel@tonic-gate 			mutex_exit(&fcp->c_lock);
21490Sstevel@tonic-gate 
21500Sstevel@tonic-gate 			/* VP/ix expects an EBUSY return here */
21510Sstevel@tonic-gate 			if (*csb->csb_cmd == FO_SDRV) {
21520Sstevel@tonic-gate 				return (EBUSY);
21530Sstevel@tonic-gate 			}
21540Sstevel@tonic-gate 			return (ENXIO);
21550Sstevel@tonic-gate 		}
21560Sstevel@tonic-gate 		/*
21570Sstevel@tonic-gate 		 * delay to ensure that new diskette is up to speed
21580Sstevel@tonic-gate 		 */
21590Sstevel@tonic-gate 		mutex_enter(&fcp->c_dorlock);
21600Sstevel@tonic-gate 		(void) fdc_motorsm(fjp, FMI_RSTARTCMD,
21610Sstevel@tonic-gate 			fjp->fj_drive->fdd_motoron);
21620Sstevel@tonic-gate 		/*
21630Sstevel@tonic-gate 		 * Return value ignored - fdcmotort deals with failure.
21640Sstevel@tonic-gate 		 */
21650Sstevel@tonic-gate 		mutex_exit(&fcp->c_dorlock);
21660Sstevel@tonic-gate 	}
21670Sstevel@tonic-gate 
21680Sstevel@tonic-gate 	/*
21690Sstevel@tonic-gate 	 * gather some statistics
21700Sstevel@tonic-gate 	 */
21710Sstevel@tonic-gate 	switch (csb->csb_cmd[0] & 0x1f) {
21720Sstevel@tonic-gate 	case FO_RDDAT:
21730Sstevel@tonic-gate 		fcp->fdstats.rd++;
21740Sstevel@tonic-gate 		break;
21750Sstevel@tonic-gate 	case FO_WRDAT:
21760Sstevel@tonic-gate 		fcp->fdstats.wr++;
21770Sstevel@tonic-gate 		break;
21780Sstevel@tonic-gate 	case FO_RECAL:
21790Sstevel@tonic-gate 		fcp->fdstats.recal++;
21800Sstevel@tonic-gate 		break;
21810Sstevel@tonic-gate 	case FO_FRMT:
21820Sstevel@tonic-gate 		fcp->fdstats.form++;
21830Sstevel@tonic-gate 		break;
21840Sstevel@tonic-gate 	default:
21850Sstevel@tonic-gate 		fcp->fdstats.other++;
21860Sstevel@tonic-gate 		break;
21870Sstevel@tonic-gate 	}
21880Sstevel@tonic-gate 
21890Sstevel@tonic-gate 	bzero(csb->csb_rslt, 10);
21900Sstevel@tonic-gate 	csb->csb_cmdstat = 0;
21910Sstevel@tonic-gate 
21920Sstevel@tonic-gate 	if (csb->csb_dmahandle) {
21930Sstevel@tonic-gate 		bzero(&dmaereq, sizeof (struct ddi_dmae_req));
21940Sstevel@tonic-gate 		dmaereq.der_command = (csb->csb_opflags & CSB_OFDMAWT) ?
21950Sstevel@tonic-gate 		    DMAE_CMD_WRITE : DMAE_CMD_READ;
21960Sstevel@tonic-gate 		/*
21970Sstevel@tonic-gate 		 * setup for dma buffer chaining regardless of bus capability
21980Sstevel@tonic-gate 		 */
21990Sstevel@tonic-gate 		dmaereq.der_bufprocess = DMAE_BUF_CHAIN;
22000Sstevel@tonic-gate 		dmaereq.proc = fdc_dmae_isr;
22010Sstevel@tonic-gate 		dmaereq.procparms = (void *)fcp;
22020Sstevel@tonic-gate 		if (ddi_dmae_prog(fcp->c_dip, &dmaereq, &csb->csb_dmacookie,
22030Sstevel@tonic-gate 		    fcp->c_dmachan) != DDI_SUCCESS)
22040Sstevel@tonic-gate 			cmn_err(CE_WARN, "fdc_exec: dmae prog failed, "
22050Sstevel@tonic-gate 				"dip %p, dmachan %x\n",
22060Sstevel@tonic-gate 				(void*)fcp->c_dip, fcp->c_dmachan);
22070Sstevel@tonic-gate 	}
22080Sstevel@tonic-gate 
22090Sstevel@tonic-gate 	if ((fdc_statemach(fcp) == FXS_DOWT) && !sleep) {
22100Sstevel@tonic-gate 		/*
22110Sstevel@tonic-gate 		 * If the operation has no results - then just return
22120Sstevel@tonic-gate 		 */
22130Sstevel@tonic-gate 		if (!csb->csb_nrslts) {
22140Sstevel@tonic-gate 			mutex_exit(&fcp->c_lock);
22150Sstevel@tonic-gate 			return (0);
22160Sstevel@tonic-gate 		}
22170Sstevel@tonic-gate 		/*
22180Sstevel@tonic-gate 		 * this operation has no interrupt and an immediate result
22190Sstevel@tonic-gate 		 * so wait for the results and stuff them into the csb
22200Sstevel@tonic-gate 		 */
22210Sstevel@tonic-gate 		if (fdc_statemach(fcp) == -1) {
22220Sstevel@tonic-gate 			mutex_exit(&fcp->c_lock);
22230Sstevel@tonic-gate 			return (EIO);
22240Sstevel@tonic-gate 		}
22250Sstevel@tonic-gate 	} else {
22260Sstevel@tonic-gate 		fcp->c_flags |= FCFLG_WAITING;
22270Sstevel@tonic-gate 		/*
22280Sstevel@tonic-gate 		 * wait for completion interrupt
22290Sstevel@tonic-gate 		 */
22300Sstevel@tonic-gate 		while (fcp->c_flags & FCFLG_WAITING) {
22310Sstevel@tonic-gate 			cv_wait(&fcp->c_iocv, &fcp->c_lock);
22320Sstevel@tonic-gate 		}
22330Sstevel@tonic-gate 	}
22340Sstevel@tonic-gate 
22350Sstevel@tonic-gate 	/*
22360Sstevel@tonic-gate 	 * See if there was an error detected, if so, fdrecover()
22370Sstevel@tonic-gate 	 * will check it out and say what to do.
22380Sstevel@tonic-gate 	 *
22390Sstevel@tonic-gate 	 * Don't do this, though, if this was the Sense Drive Status
22400Sstevel@tonic-gate 	 * or the Dump Registers command.
22410Sstevel@tonic-gate 	 */
22420Sstevel@tonic-gate 	if (csb->csb_cmdstat && *csb->csb_cmd != FO_SDRV) {
22430Sstevel@tonic-gate 		/* if it can restarted OK, then do so, else return error */
22440Sstevel@tonic-gate 		if (fdrecover(fcp)) {
22450Sstevel@tonic-gate 			mutex_exit(&fcp->c_lock);
22460Sstevel@tonic-gate 			return (EIO);
22470Sstevel@tonic-gate 		}
22480Sstevel@tonic-gate 		/* ASSUMES that cmd is still intact in csb */
22490Sstevel@tonic-gate 		if (csb->csb_xstate == FXS_END)
22500Sstevel@tonic-gate 			csb->csb_xstate = FXS_START;
22510Sstevel@tonic-gate 		if (fdc_dma_attr.dma_attr_sgllen > 1 && csb->csb_dmahandle) {
22520Sstevel@tonic-gate 			/*
22530Sstevel@tonic-gate 			 * restarted read/write operation requires
22540Sstevel@tonic-gate 			 * first DMA cookie of current window
22550Sstevel@tonic-gate 			 */
22560Sstevel@tonic-gate 			if (ddi_dma_getwin(csb->csb_dmahandle,
22570Sstevel@tonic-gate 			    csb->csb_dmacurrwin, &off, &len,
22580Sstevel@tonic-gate 			    &csb->csb_dmacookie,
22590Sstevel@tonic-gate 			    &csb->csb_dmacookiecnt) != DDI_SUCCESS) {
22600Sstevel@tonic-gate 
22610Sstevel@tonic-gate 				mutex_exit(&fcp->c_lock);
22620Sstevel@tonic-gate 				return (EIO);
22630Sstevel@tonic-gate 			}
22640Sstevel@tonic-gate 			csb->csb_dmacurrcookie = 0;
22650Sstevel@tonic-gate 		}
22660Sstevel@tonic-gate 		goto retry;
22670Sstevel@tonic-gate 	}
22680Sstevel@tonic-gate 	/* things went ok */
22690Sstevel@tonic-gate 	mutex_exit(&fcp->c_lock);
22700Sstevel@tonic-gate 	return (0);
22710Sstevel@tonic-gate }
22720Sstevel@tonic-gate 
22730Sstevel@tonic-gate /*
22740Sstevel@tonic-gate  * fdcheckdisk
22750Sstevel@tonic-gate  *	called by fdc_exec to check if the disk is still there - do a seek
22760Sstevel@tonic-gate  *	then see if DSKCHG line went away; if so, diskette is in; else
22770Sstevel@tonic-gate  *	it's (still) out.
22780Sstevel@tonic-gate  */
22790Sstevel@tonic-gate int
22800Sstevel@tonic-gate fdcheckdisk(struct fdcntlr *fcp, int unit)
22810Sstevel@tonic-gate {
22820Sstevel@tonic-gate 	struct fdcsb *csb = &fcp->c_csb;
22830Sstevel@tonic-gate 	int newcyl;			/* where to seek for reset of DSKCHG */
22840Sstevel@tonic-gate 	int rval;
22850Sstevel@tonic-gate 	enum fxstate save_xstate;
22860Sstevel@tonic-gate 	uchar_t save_cmd, save_cd1, save_npcyl;
22870Sstevel@tonic-gate 
22880Sstevel@tonic-gate 	ASSERT(MUTEX_HELD(&fcp->c_lock));
22890Sstevel@tonic-gate 	FCERRPRINT(FDEP_L1, FDEM_CHEK,
22900Sstevel@tonic-gate 	    (CE_CONT, "fdcheckdisk unit %d\n", unit));
22910Sstevel@tonic-gate 
22920Sstevel@tonic-gate 	if (fcp->c_curpcyl[unit])
22930Sstevel@tonic-gate 		newcyl = fcp->c_curpcyl[unit] - 1;
22940Sstevel@tonic-gate 	else
22950Sstevel@tonic-gate 		newcyl = 1;
22960Sstevel@tonic-gate 
22970Sstevel@tonic-gate 	save_cmd = *csb->csb_cmd;
22980Sstevel@tonic-gate 	save_cd1 = csb->csb_cmd[1];
22990Sstevel@tonic-gate 	save_npcyl = csb->csb_npcyl;
23000Sstevel@tonic-gate 	save_xstate = csb->csb_xstate;
23010Sstevel@tonic-gate 
23020Sstevel@tonic-gate 	*csb->csb_cmd = FO_SEEK;
23030Sstevel@tonic-gate 	csb->csb_cmd[1] = (uchar_t)unit;
23040Sstevel@tonic-gate 	csb->csb_npcyl = (uchar_t)newcyl;
23050Sstevel@tonic-gate 	fcp->c_flags |= FCFLG_WAITING;
23060Sstevel@tonic-gate 
23070Sstevel@tonic-gate 	if (fcp->c_mtrstate[unit] != FMS_ON && fcp->c_motort[unit] != 0)
23080Sstevel@tonic-gate 		/*
23090Sstevel@tonic-gate 		 * wait for motor to get up to speed,
23100Sstevel@tonic-gate 		 * and let motor_timer issue seek cmd
23110Sstevel@tonic-gate 		 */
23120Sstevel@tonic-gate 		csb->csb_xstate = FXS_DKCHGX;
23130Sstevel@tonic-gate 	else {
23140Sstevel@tonic-gate 		/*
23150Sstevel@tonic-gate 		 * motor is up to speed; issue seek cmd now
23160Sstevel@tonic-gate 		 */
23170Sstevel@tonic-gate 		csb->csb_xstate = FXS_SEEK;
23180Sstevel@tonic-gate 		if (rval = fdcseek(fcp, unit, newcyl)) {
23190Sstevel@tonic-gate 			/*
23200Sstevel@tonic-gate 			 * any recal/seek errors are too serious to attend to
23210Sstevel@tonic-gate 			 */
23220Sstevel@tonic-gate 			FCERRPRINT(FDEP_L3, FDEM_CHEK,
23230Sstevel@tonic-gate 			    (CE_WARN, "fdcheckdisk err %d", rval));
23240Sstevel@tonic-gate 			fcp->c_flags ^= FCFLG_WAITING;
23250Sstevel@tonic-gate 		}
23260Sstevel@tonic-gate 	}
23270Sstevel@tonic-gate 	/*
23280Sstevel@tonic-gate 	 * wait for completion interrupt
23290Sstevel@tonic-gate 	 * XXX This should be backed up with a watchdog timer!
23300Sstevel@tonic-gate 	 */
23310Sstevel@tonic-gate 	while (fcp->c_flags & FCFLG_WAITING) {
23320Sstevel@tonic-gate 		cv_wait(&fcp->c_iocv, &fcp->c_lock);
23330Sstevel@tonic-gate 	}
23340Sstevel@tonic-gate 
23350Sstevel@tonic-gate 	/*
23360Sstevel@tonic-gate 	 * if disk change still asserted, no diskette in drive!
23370Sstevel@tonic-gate 	 */
23380Sstevel@tonic-gate 	if (rval = fdcsense_chng(fcp, unit)) {
23390Sstevel@tonic-gate 		FCERRPRINT(FDEP_L3, FDEM_CHEK,
23400Sstevel@tonic-gate 		    (CE_WARN, "fdcheckdisk no disk %d", unit));
23410Sstevel@tonic-gate 	}
23420Sstevel@tonic-gate 
23430Sstevel@tonic-gate 	*csb->csb_cmd = save_cmd;
23440Sstevel@tonic-gate 	csb->csb_cmd[1] = save_cd1;
23450Sstevel@tonic-gate 	csb->csb_npcyl = save_npcyl;
23460Sstevel@tonic-gate 	csb->csb_xstate = save_xstate;
23470Sstevel@tonic-gate 	return (rval);
23480Sstevel@tonic-gate }
23490Sstevel@tonic-gate 
23500Sstevel@tonic-gate static int
23510Sstevel@tonic-gate fdrecover(struct fdcntlr *fcp)
23520Sstevel@tonic-gate {
23530Sstevel@tonic-gate 	struct fcu_obj *fjp;
23540Sstevel@tonic-gate 	struct fdcsb *csb = &fcp->c_csb;
23550Sstevel@tonic-gate 	int residual;
23560Sstevel@tonic-gate 	int unit;
23570Sstevel@tonic-gate 	char *failure;
23580Sstevel@tonic-gate 
23590Sstevel@tonic-gate 	FCERRPRINT(FDEP_L2, FDEM_RECO,
23600Sstevel@tonic-gate 	    (CE_NOTE, "fdrecover unit %d", csb->csb_drive));
23610Sstevel@tonic-gate 
23620Sstevel@tonic-gate 	unit = csb->csb_drive;
23630Sstevel@tonic-gate 	fjp = fcp->c_unit[unit];
23640Sstevel@tonic-gate 	if (fcp->c_flags & FCFLG_TIMEOUT) {
23650Sstevel@tonic-gate 		fcp->c_flags ^= FCFLG_TIMEOUT;
23660Sstevel@tonic-gate 		csb->csb_rslt[1] |= 0x08;
23670Sstevel@tonic-gate 		FCERRPRINT(FDEP_L3, FDEM_RECO,
23680Sstevel@tonic-gate 		    (CE_WARN, "fd unit %d: %s timed out", csb->csb_drive,
23690Sstevel@tonic-gate 		    fdcmds[*csb->csb_cmd & 0x1f].cmdname));
23700Sstevel@tonic-gate 	}
23710Sstevel@tonic-gate 
23720Sstevel@tonic-gate 	if (csb->csb_status & S0_SEKEND)
23730Sstevel@tonic-gate 		fcp->c_curpcyl[unit] = -1;
23740Sstevel@tonic-gate 
23750Sstevel@tonic-gate 	switch (csb->csb_oldxs) {
23760Sstevel@tonic-gate 	case FXS_RCAL:		/* recalibrate */
23770Sstevel@tonic-gate 	case FXS_SEEK:		/* seek */
23780Sstevel@tonic-gate 	case FXS_RESET:		/* cntlr reset */
23790Sstevel@tonic-gate 		FCERRPRINT(FDEP_L4, FDEM_RECO, (CE_WARN,
23800Sstevel@tonic-gate 		    "fd unit %d: %s error: st0=0x%x pcn=%d", csb->csb_drive,
23810Sstevel@tonic-gate 		    fdcmds[*csb->csb_cmd & 0x1f].cmdname,
23820Sstevel@tonic-gate 		    *csb->csb_rslt, csb->csb_rslt[1]));
23830Sstevel@tonic-gate 		if (csb->csb_retrys++ < skretry &&
23840Sstevel@tonic-gate 		    !(csb->csb_opflags & CSB_OFRAWIOCTL))
23850Sstevel@tonic-gate 			return (0);
23860Sstevel@tonic-gate 		break;
23870Sstevel@tonic-gate 
23880Sstevel@tonic-gate 	case FXS_RDID:		/* read ID */
23890Sstevel@tonic-gate 		if (!(csb->csb_status & S0_SEKEND))
23900Sstevel@tonic-gate 			csb->csb_xstate = FXS_HDST;
23910Sstevel@tonic-gate 		/* FALLTHROUGH */
23920Sstevel@tonic-gate 	case FXS_DOIT:		/* original operation */
23930Sstevel@tonic-gate 	case FXS_DOWT:		/* waiting on operation */
23940Sstevel@tonic-gate 		if (csb->csb_opflags & (CSB_OFDMARD | CSB_OFDMAWT)) {
23950Sstevel@tonic-gate 			if (ddi_dmae_getcnt(fcp->c_dip, fcp->c_dmachan,
23960Sstevel@tonic-gate 			    &residual) != DDI_SUCCESS)
23970Sstevel@tonic-gate 				cmn_err(CE_WARN,
23980Sstevel@tonic-gate 				    "fdc_recover: dmae getcnt failed, "
23990Sstevel@tonic-gate 				    "dip %p dmachan %x residual %x\n",
24000Sstevel@tonic-gate 				    (void*)fcp->c_dip, fcp->c_dmachan,
24010Sstevel@tonic-gate 				    residual);
24020Sstevel@tonic-gate 			FCERRPRINT(FDEP_L2, FDEM_RECO,
24030Sstevel@tonic-gate 	(CE_NOTE, "fd unit %d: %s error: dma count=0x%lx residual=0x%x",
24040Sstevel@tonic-gate 			    csb->csb_drive,
24050Sstevel@tonic-gate 			    fdcmds[*csb->csb_cmd & 0x1f].cmdname,
24060Sstevel@tonic-gate 			    csb->csb_dmacookie.dmac_size, residual));
24070Sstevel@tonic-gate 		}
24080Sstevel@tonic-gate 		if (csb->csb_rslt[1] == S1_OVRUN)
24090Sstevel@tonic-gate 			/*
24100Sstevel@tonic-gate 			 * handle retries of over/underrun
24110Sstevel@tonic-gate 			 * with a secondary retry counter
24120Sstevel@tonic-gate 			 */
24130Sstevel@tonic-gate 			if (++csb->csb_ourtrys <= OURUN_TRIES) {
24140Sstevel@tonic-gate 				FCERRPRINT(FDEP_L2, FDEM_RECO,
24150Sstevel@tonic-gate (CE_NOTE, "fd unit %d: %s error: over/under-run",
24160Sstevel@tonic-gate csb->csb_drive, fdcmds[*csb->csb_cmd & 0x1f].cmdname));
24170Sstevel@tonic-gate 				return (0);
24180Sstevel@tonic-gate 			} else
24190Sstevel@tonic-gate 				/*
24200Sstevel@tonic-gate 				 * count 1 set of over/underruns
24210Sstevel@tonic-gate 				 * as 1 primary retry effort
24220Sstevel@tonic-gate 				 */
24230Sstevel@tonic-gate 				csb->csb_ourtrys = 0;
24240Sstevel@tonic-gate 
24250Sstevel@tonic-gate 		if ((fjp->fj_flags & (FUNIT_UNLABELED | FUNIT_LABELOK)) &&
24260Sstevel@tonic-gate 		    !(csb->csb_opflags & CSB_OFRAWIOCTL)) {
24270Sstevel@tonic-gate 			/*
24280Sstevel@tonic-gate 			 * device is open so keep trying and
24290Sstevel@tonic-gate 			 * gather statistics on errors
24300Sstevel@tonic-gate 			 */
24310Sstevel@tonic-gate 			if (csb->csb_rslt[1] & S1_CRCER)
24320Sstevel@tonic-gate 				fcp->fdstats.de++;
24330Sstevel@tonic-gate 			if (csb->csb_rslt[1] & S1_OVRUN)
24340Sstevel@tonic-gate 				fcp->fdstats.run++;
24350Sstevel@tonic-gate 			if (csb->csb_rslt[1] & (S1_NODATA | S1_MADMK))
24360Sstevel@tonic-gate 				fcp->fdstats.bfmt++;
24370Sstevel@tonic-gate 			if (csb->csb_rslt[1] & 0x08)
24380Sstevel@tonic-gate 				fcp->fdstats.to++;
24390Sstevel@tonic-gate 
24400Sstevel@tonic-gate 			/*
24410Sstevel@tonic-gate 			 * if we have not run out of retries, return 0
24420Sstevel@tonic-gate 			 */
24430Sstevel@tonic-gate 			if (csb->csb_retrys++ < csb->csb_maxretry &&
24440Sstevel@tonic-gate 			    (*csb->csb_cmd & ~FO_MFM) != FO_FRMT) {
24450Sstevel@tonic-gate 				if (csb->csb_opflags &
24460Sstevel@tonic-gate 				    (CSB_OFDMARD | CSB_OFDMAWT)) {
24470Sstevel@tonic-gate 					FCERRPRINT(FDEP_L4, FDEM_RECO,
24480Sstevel@tonic-gate (CE_WARN, "fd unit %d: %s error: st0=0x%x st1=0x%x st2=0x%x",
24490Sstevel@tonic-gate 					    csb->csb_drive,
24500Sstevel@tonic-gate fdcmds[*csb->csb_cmd & 0x1f].cmdname,
24510Sstevel@tonic-gate 					    *csb->csb_rslt, csb->csb_rslt[1],
24520Sstevel@tonic-gate 					    csb->csb_rslt[2]));
24530Sstevel@tonic-gate 				}
24540Sstevel@tonic-gate 				if ((csb->csb_retrys & 1) &&
24550Sstevel@tonic-gate 				    csb->csb_xstate == FXS_END)
24560Sstevel@tonic-gate 					csb->csb_xstate = FXS_DOIT;
24570Sstevel@tonic-gate 				else if (csb->csb_retrys == 3)
24580Sstevel@tonic-gate 					csb->csb_xstate = FXS_RESTART;
24590Sstevel@tonic-gate 				return (0);
24600Sstevel@tonic-gate 			}
24610Sstevel@tonic-gate 			if (csb->csb_rslt[1] & S1_CRCER)
24620Sstevel@tonic-gate 				failure = "crc error";
24630Sstevel@tonic-gate 			else if (csb->csb_rslt[1] & S1_OVRUN)
24640Sstevel@tonic-gate 				failure = "over/under-run";
24650Sstevel@tonic-gate 			else if (csb->csb_rslt[1] & (S1_NODATA | S1_MADMK))
24660Sstevel@tonic-gate 				failure = "bad format";
24670Sstevel@tonic-gate 			else if (csb->csb_rslt[1] & 0x08)
24680Sstevel@tonic-gate 				failure = "timeout";
24690Sstevel@tonic-gate 			else
24700Sstevel@tonic-gate 				failure = "failed";
24710Sstevel@tonic-gate 			cmn_err(CE_NOTE, "!fd unit %d: %s %s (%x %x %x)",
24720Sstevel@tonic-gate 			    csb->csb_drive,
24730Sstevel@tonic-gate 			    fdcmds[*csb->csb_cmd & 0x1f].cmdname, failure,
24740Sstevel@tonic-gate 			    *csb->csb_rslt, csb->csb_rslt[1], csb->csb_rslt[2]);
24750Sstevel@tonic-gate 		} else {
24760Sstevel@tonic-gate 			FCERRPRINT(FDEP_L2, FDEM_RECO,
24770Sstevel@tonic-gate 			    (CE_NOTE, "fd unit %d: %s failed (%x %x %x)",
24780Sstevel@tonic-gate 			    csb->csb_drive,
24790Sstevel@tonic-gate 			    fdcmds[*csb->csb_cmd & 0x1f].cmdname,
24800Sstevel@tonic-gate 			    *csb->csb_rslt, csb->csb_rslt[1],
24810Sstevel@tonic-gate 			    csb->csb_rslt[2]));
24820Sstevel@tonic-gate 		}
24830Sstevel@tonic-gate 		break;
24840Sstevel@tonic-gate 
24850Sstevel@tonic-gate 	default:
24860Sstevel@tonic-gate 		FCERRPRINT(FDEP_L4, FDEM_RECO, (CE_WARN,
24870Sstevel@tonic-gate 		    "fd unit %d: %s failed: st0=0x%x st1=0x%x st2=0x%x",
24880Sstevel@tonic-gate 		    csb->csb_drive, fdcmds[*csb->csb_cmd & 0x1f].cmdname,
24890Sstevel@tonic-gate 		    *csb->csb_rslt, csb->csb_rslt[1], csb->csb_rslt[2]));
24900Sstevel@tonic-gate 		break;
24910Sstevel@tonic-gate 	}
24920Sstevel@tonic-gate 	return (1);
24930Sstevel@tonic-gate }
24940Sstevel@tonic-gate 
24950Sstevel@tonic-gate 
24960Sstevel@tonic-gate /*	Autovector Interrupt Entry Point	*/
24970Sstevel@tonic-gate /* ARGSUSED */
24980Sstevel@tonic-gate static uint_t
24990Sstevel@tonic-gate fdc_intr(caddr_t arg)
25000Sstevel@tonic-gate {
25010Sstevel@tonic-gate 	struct fdcntlr *fcp = (struct fdcntlr *)arg;
25020Sstevel@tonic-gate 	struct fdcsb *csb;
25030Sstevel@tonic-gate 	off_t off;
25040Sstevel@tonic-gate 	size_t blklen;
25050Sstevel@tonic-gate 	int drive;
25060Sstevel@tonic-gate 	int newstate;
25070Sstevel@tonic-gate 	int pendstate;
25080Sstevel@tonic-gate 	int rval = DDI_DMA_DONE;
25090Sstevel@tonic-gate 	int state;
25100Sstevel@tonic-gate 	int maxspin = 10;
25110Sstevel@tonic-gate 
25120Sstevel@tonic-gate 	csb = &fcp->c_csb;
25130Sstevel@tonic-gate 
25140Sstevel@tonic-gate 	mutex_enter(&fcp->c_lock);
25150Sstevel@tonic-gate 	/*
25160Sstevel@tonic-gate 	 * Wait for the RQM bit to be set, or until we've tested it
25170Sstevel@tonic-gate 	 * a bunch of times (which may imply this isn't our interrupt).
25180Sstevel@tonic-gate 	 */
25190Sstevel@tonic-gate 	state = inb(fcp->c_regbase + FCR_MSR);
25200Sstevel@tonic-gate 	pendstate = state & (MS_RQM | MS_DIO | MS_CB);
25210Sstevel@tonic-gate 	while (((pendstate & MS_RQM) == 0) && (maxspin-- > 0)) {
25220Sstevel@tonic-gate 		/* Small pause in between reading the status port */
25230Sstevel@tonic-gate 		drv_usecwait(10);
25240Sstevel@tonic-gate 		/* Reread the status port */
25250Sstevel@tonic-gate 		state = inb(fcp->c_regbase + FCR_MSR);
25260Sstevel@tonic-gate 		pendstate = state & (MS_RQM | MS_DIO | MS_CB);
25270Sstevel@tonic-gate 	}
25280Sstevel@tonic-gate 	FCERRPRINT(FDEP_L0, FDEM_INTR,
25290Sstevel@tonic-gate 	    (CE_CONT, "fdc_intr unit %d: xstate=%d MSR=0x%x\n",
25300Sstevel@tonic-gate 	    csb->csb_drive, csb->csb_xstate, state));
25310Sstevel@tonic-gate 
25320Sstevel@tonic-gate 	/*
25330Sstevel@tonic-gate 	 * If there is an operation outstanding AND the controller is ready
25340Sstevel@tonic-gate 	 * to receive a command or send us the result of a command (OR if the
25350Sstevel@tonic-gate 	 * controller is ready to accept a new command), AND if
25360Sstevel@tonic-gate 	 * someone has been waiting for a command to finish AND (if no unit
25370Sstevel@tonic-gate 	 * is BUSY OR if the unit that we're waiting for is BUSY (i.e. it's in
25380Sstevel@tonic-gate 	 * the middle of a seek/recalibrate)) then this interrupt is for us.
25390Sstevel@tonic-gate 	 */
25400Sstevel@tonic-gate 	if ((pendstate == (MS_RQM | MS_DIO | MS_CB) || pendstate == MS_RQM) &&
25410Sstevel@tonic-gate 	    (fcp->c_flags & FCFLG_WAITING) &&
25420Sstevel@tonic-gate 	    (!(state & 0x0f) || ((1 << csb->csb_drive) & state))) {
25430Sstevel@tonic-gate 		/*
25440Sstevel@tonic-gate 		 * Remove one of the conditions for entering this code.
25450Sstevel@tonic-gate 		 * The state_machine will release the c_lock if it
25460Sstevel@tonic-gate 		 * calls untimeout()
25470Sstevel@tonic-gate 		 */
25480Sstevel@tonic-gate 		fcp->c_flags ^= FCFLG_WAITING;
25490Sstevel@tonic-gate 
25500Sstevel@tonic-gate 		if ((newstate = fdc_statemach(fcp)) == -1) {
25510Sstevel@tonic-gate 			/* restore waiting flag */
25520Sstevel@tonic-gate 			fcp->c_flags |= FCFLG_WAITING;
25530Sstevel@tonic-gate 			mutex_exit(&fcp->c_lock);
25540Sstevel@tonic-gate 			return (DDI_INTR_CLAIMED);
25550Sstevel@tonic-gate 		}
25560Sstevel@tonic-gate 
25570Sstevel@tonic-gate 		if (fcp->c_intrstat)
25580Sstevel@tonic-gate 			KIOIP->intrs[KSTAT_INTR_HARD]++;
25590Sstevel@tonic-gate 		if (newstate == FXS_END) {
25600Sstevel@tonic-gate 
25610Sstevel@tonic-gate 			if (csb->csb_dmahandle && !csb->csb_cmdstat &&
25620Sstevel@tonic-gate 				/*
25630Sstevel@tonic-gate 				 * read/write operation may have multiple DMA
25640Sstevel@tonic-gate 				 * cookies: process next one
25650Sstevel@tonic-gate 				 */
25660Sstevel@tonic-gate 			    ((csb->csb_dmacurrcookie <
25670Sstevel@tonic-gate 			    (csb->csb_dmacookiecnt - 1)) ||
25680Sstevel@tonic-gate 			    (csb->csb_dmacurrwin) < (csb->csb_dmawincnt - 1))) {
25690Sstevel@tonic-gate 				/*
25700Sstevel@tonic-gate 				 * read/write operation requires another
25710Sstevel@tonic-gate 				 * DMA cookie: process next one
25720Sstevel@tonic-gate 				 */
25730Sstevel@tonic-gate 
25740Sstevel@tonic-gate 				if (++csb->csb_dmacurrcookie <
25750Sstevel@tonic-gate 				    csb->csb_dmacookiecnt) {
25760Sstevel@tonic-gate 					ddi_dma_nextcookie(csb->csb_dmahandle,
25770Sstevel@tonic-gate 					    &csb->csb_dmacookie);
25780Sstevel@tonic-gate 				} else if (++csb->csb_dmacurrwin <
25790Sstevel@tonic-gate 				    csb->csb_dmawincnt) {
25800Sstevel@tonic-gate 					if (ddi_dma_getwin(csb->csb_dmahandle,
25810Sstevel@tonic-gate 					    csb->csb_dmacurrwin, &off, &blklen,
25820Sstevel@tonic-gate 					    &csb->csb_dmacookie,
25830Sstevel@tonic-gate 					    &csb->csb_dmacookiecnt) !=
25840Sstevel@tonic-gate 					    DDI_SUCCESS) {
25850Sstevel@tonic-gate 						cmn_err(CE_WARN,
25860Sstevel@tonic-gate 						    "fdc_intr: "
25870Sstevel@tonic-gate 						    "dma getwin failed\n");
25880Sstevel@tonic-gate 					}
25890Sstevel@tonic-gate 					csb->csb_dmacurrcookie = 0;
25900Sstevel@tonic-gate 				}
25910Sstevel@tonic-gate 
25920Sstevel@tonic-gate 				if (ddi_dmae_prog(fcp->c_dip, NULL,
25930Sstevel@tonic-gate 				    &csb->csb_dmacookie, fcp->c_dmachan) !=
25940Sstevel@tonic-gate 				    DDI_SUCCESS)
25950Sstevel@tonic-gate 					cmn_err(CE_WARN,
25960Sstevel@tonic-gate 					    "fdc_intr: dmae prog failed, "
25970Sstevel@tonic-gate 					    "dip %p dmachannel %x\n",
25980Sstevel@tonic-gate 					    (void*)fcp->c_dip,
25990Sstevel@tonic-gate 					    fcp->c_dmachan);
26000Sstevel@tonic-gate 
26010Sstevel@tonic-gate 				/*
26020Sstevel@tonic-gate 				 * status of last operation has disk
26030Sstevel@tonic-gate 				 * address for continuation
26040Sstevel@tonic-gate 				 */
26050Sstevel@tonic-gate 				csb->csb_cmd[2] = csb->csb_rslt[3];
26060Sstevel@tonic-gate 				csb->csb_cmd[3] = csb->csb_rslt[4];
26070Sstevel@tonic-gate 				csb->csb_cmd[4] = csb->csb_rslt[5];
26080Sstevel@tonic-gate 				csb->csb_cmd[1] = (csb->csb_cmd[1] & ~0x04) |
26090Sstevel@tonic-gate 				    (csb->csb_cmd[3] << 2);
26100Sstevel@tonic-gate 
26110Sstevel@tonic-gate 				csb->csb_xstate = FXS_START;
26120Sstevel@tonic-gate 				(void) fdc_statemach(fcp);
26130Sstevel@tonic-gate 				/*
26140Sstevel@tonic-gate 				 * Ignored return.  If failed, warning already
26150Sstevel@tonic-gate 				 * posted.  Returned state irrelevant.
26160Sstevel@tonic-gate 				 */
26170Sstevel@tonic-gate 				/* restore waiting flag */
26180Sstevel@tonic-gate 				fcp->c_flags |= FCFLG_WAITING;
26190Sstevel@tonic-gate 				goto fi_exit;
26200Sstevel@tonic-gate 			}
26210Sstevel@tonic-gate 			if (rval != DDI_DMA_DONE)
26220Sstevel@tonic-gate 				csb->csb_cmdstat = EIO;
26230Sstevel@tonic-gate 			/*
26240Sstevel@tonic-gate 			 * somebody's waiting for completion of fdcntlr/csb,
26250Sstevel@tonic-gate 			 * wake them
26260Sstevel@tonic-gate 			 */
26270Sstevel@tonic-gate 			cv_signal(&fcp->c_iocv);
26280Sstevel@tonic-gate 		}
26290Sstevel@tonic-gate 		else
26300Sstevel@tonic-gate 			/* restore waiting flag */
26310Sstevel@tonic-gate 			fcp->c_flags |= FCFLG_WAITING;
26320Sstevel@tonic-gate fi_exit:
26330Sstevel@tonic-gate 		mutex_exit(&fcp->c_lock);
26340Sstevel@tonic-gate 		return (DDI_INTR_CLAIMED);
26350Sstevel@tonic-gate 	}
26360Sstevel@tonic-gate 
26370Sstevel@tonic-gate 	if (state & MS_RQM) {
26380Sstevel@tonic-gate 		(void) fdcsense_int(fcp, &drive, NULL);
26390Sstevel@tonic-gate 		/*
26400Sstevel@tonic-gate 		 * Ignored return - senser state already saved
26410Sstevel@tonic-gate 		 */
26420Sstevel@tonic-gate 		FCERRPRINT(FDEP_L4, FDEM_INTR,
26430Sstevel@tonic-gate 		    (CE_WARN, "fdc_intr unit %d: nobody sleeping 0x%x",
26440Sstevel@tonic-gate 		    drive, state));
26450Sstevel@tonic-gate 	} else {
26460Sstevel@tonic-gate 		FCERRPRINT(FDEP_L4, FDEM_INTR,
26470Sstevel@tonic-gate 		    (CE_WARN, "fdc_intr: nobody sleeping on %d 0x%x",
26480Sstevel@tonic-gate 		    csb->csb_drive, state));
26490Sstevel@tonic-gate 	}
26500Sstevel@tonic-gate 	/*
26510Sstevel@tonic-gate 	 * This should probably be protected, but, what the
26520Sstevel@tonic-gate 	 * heck...the cost isn't worth the accuracy for this
26530Sstevel@tonic-gate 	 * statistic.
26540Sstevel@tonic-gate 	 */
26550Sstevel@tonic-gate 	if (fcp->c_intrstat)
26560Sstevel@tonic-gate 		KIOIP->intrs[KSTAT_INTR_SPURIOUS]++;
26570Sstevel@tonic-gate 	mutex_exit(&fcp->c_lock);
26580Sstevel@tonic-gate 	return (DDI_INTR_UNCLAIMED);
26590Sstevel@tonic-gate }
26600Sstevel@tonic-gate 
26610Sstevel@tonic-gate /*
26620Sstevel@tonic-gate  * fdwatch
26630Sstevel@tonic-gate  *	is called from timeout() when a floppy operation timer has expired.
26640Sstevel@tonic-gate  */
26650Sstevel@tonic-gate static void
26660Sstevel@tonic-gate fdwatch(void *arg)
26670Sstevel@tonic-gate {
26680Sstevel@tonic-gate 	struct fdcntlr *fcp = (struct fdcntlr *)arg;
26690Sstevel@tonic-gate 	struct fdcsb *csb;
26700Sstevel@tonic-gate 
26710Sstevel@tonic-gate 	mutex_enter(&fcp->c_lock);
26720Sstevel@tonic-gate 
26730Sstevel@tonic-gate 	if (fcp->c_timeid == 0) {
26740Sstevel@tonic-gate 		/*
26750Sstevel@tonic-gate 		 * fdc_intr got here first, ergo, no timeout condition..
26760Sstevel@tonic-gate 		 */
26770Sstevel@tonic-gate 		mutex_exit(&fcp->c_lock);
26780Sstevel@tonic-gate 		return;
26790Sstevel@tonic-gate 	}
26800Sstevel@tonic-gate 
26810Sstevel@tonic-gate 	if (fcp->c_flags & FCFLG_WAITING) {
26820Sstevel@tonic-gate 		if (ddi_dmae_stop(fcp->c_dip, fcp->c_dmachan) != DDI_SUCCESS)
26830Sstevel@tonic-gate 			cmn_err(CE_WARN, "fdwatch: dmae stop failed, "
26840Sstevel@tonic-gate 				"dip %p, dmachan %x\n",
26850Sstevel@tonic-gate 				(void*)fcp->c_dip, fcp->c_dmachan);
26860Sstevel@tonic-gate 		csb = &fcp->c_csb;
26870Sstevel@tonic-gate 		FCERRPRINT(FDEP_L3, FDEM_WATC,
26880Sstevel@tonic-gate 		    (CE_WARN, "fdcwatch unit %d: xstate = %d",
26890Sstevel@tonic-gate 		    csb->csb_drive, csb->csb_xstate));
26900Sstevel@tonic-gate 		drv_usecwait(50);
26910Sstevel@tonic-gate 
26920Sstevel@tonic-gate 		if (inb(fcp->c_regbase + FCR_MSR) != MS_RQM) {
26930Sstevel@tonic-gate 			/*
26940Sstevel@tonic-gate 			 * cntlr is still busy, so reset it
26950Sstevel@tonic-gate 			 */
26960Sstevel@tonic-gate 			csb->csb_xstate = FXS_KILL;
26970Sstevel@tonic-gate 			(void) fdc_statemach(fcp);
26980Sstevel@tonic-gate 			/*
26990Sstevel@tonic-gate 			 * Ignored return.  If failed, warning already
27000Sstevel@tonic-gate 			 * posted.  Returned state irrelevant.
27010Sstevel@tonic-gate 			 */
27020Sstevel@tonic-gate 		} else {
27030Sstevel@tonic-gate 			csb->csb_xstate = FXS_END;
27040Sstevel@tonic-gate 			fcp->c_timeid = 0;
27050Sstevel@tonic-gate 			fcp->c_flags ^= FCFLG_WAITING;
27060Sstevel@tonic-gate 			cv_signal(&fcp->c_iocv);
27070Sstevel@tonic-gate 		}
27080Sstevel@tonic-gate 		csb->csb_cmdstat = EIO;
27090Sstevel@tonic-gate 		fcp->c_flags |= FCFLG_TIMEOUT;
27100Sstevel@tonic-gate 	} else {
27110Sstevel@tonic-gate 		FCERRPRINT(FDEP_L4, FDEM_INTR,
27120Sstevel@tonic-gate 		    (CE_WARN, "fdcwatch: not sleeping for unit %d",
27130Sstevel@tonic-gate 		    fcp->c_csb.csb_drive));
27140Sstevel@tonic-gate 	}
27150Sstevel@tonic-gate 	if (fcp->c_intrstat)
27160Sstevel@tonic-gate 		KIOIP->intrs[KSTAT_INTR_WATCHDOG]++;
27170Sstevel@tonic-gate 	mutex_exit(&fcp->c_lock);
27180Sstevel@tonic-gate }
27190Sstevel@tonic-gate 
27200Sstevel@tonic-gate 
27210Sstevel@tonic-gate static int
27220Sstevel@tonic-gate fdc_statemach(struct fdcntlr *fcp)
27230Sstevel@tonic-gate {
27240Sstevel@tonic-gate 	struct fcu_obj *fjp;
27250Sstevel@tonic-gate 	struct fdcsb *csb = &fcp->c_csb;
27260Sstevel@tonic-gate 	int backoff;
27270Sstevel@tonic-gate 	clock_t time;
27280Sstevel@tonic-gate 	int unit;
27290Sstevel@tonic-gate 
27300Sstevel@tonic-gate 	ASSERT(MUTEX_HELD(&fcp->c_lock));
27310Sstevel@tonic-gate 
27320Sstevel@tonic-gate 	unit = csb->csb_drive;
27330Sstevel@tonic-gate 	fjp = fcp->c_unit[unit];
27340Sstevel@tonic-gate 
27350Sstevel@tonic-gate 	csb->csb_oldxs = csb->csb_xstate;
27360Sstevel@tonic-gate 	switch (csb->csb_xstate) {
27370Sstevel@tonic-gate 
27380Sstevel@tonic-gate 	case FXS_START:		/* start of operation */
27390Sstevel@tonic-gate 		ASSERT(fcp->c_timeid == 0);
27400Sstevel@tonic-gate 		time = drv_usectohz(100000 * (unsigned int)csb->csb_timer);
27410Sstevel@tonic-gate 		if (time == 0)
27420Sstevel@tonic-gate 			time = drv_usectohz(2000000);
27430Sstevel@tonic-gate 		fcp->c_timeid = timeout(fdwatch, (void *)fcp, time);
27440Sstevel@tonic-gate 
27450Sstevel@tonic-gate 		if (fcp->c_mtrstate[unit] == FMS_START) {
27460Sstevel@tonic-gate 			/*
27470Sstevel@tonic-gate 			 * wait for motor to get up to speed
27480Sstevel@tonic-gate 			 */
27490Sstevel@tonic-gate 			csb->csb_xstate = FXS_MTRON;
27500Sstevel@tonic-gate 			break;
27510Sstevel@tonic-gate 		}
27520Sstevel@tonic-gate 		/* FALLTHROUGH */
27530Sstevel@tonic-gate 
27540Sstevel@tonic-gate 	case FXS_MTRON:		/* motor is at speed */
27550Sstevel@tonic-gate 		if (fcp->c_mtrstate[unit] != FMS_ON) {
27560Sstevel@tonic-gate 			/* how did we get here ?? */
27570Sstevel@tonic-gate 			cmn_err(CE_WARN, "fdc: selected but motor off");
27580Sstevel@tonic-gate 			return (-1);
27590Sstevel@tonic-gate 		}
27600Sstevel@tonic-gate 		if (fcp->c_curpcyl[unit] != -1 && *csb->csb_cmd != FO_RECAL)
27610Sstevel@tonic-gate 			goto nxs_seek;
27620Sstevel@tonic-gate 		recalcmd[1] = (uchar_t)unit;
27630Sstevel@tonic-gate 		if (fdc_docmd(fcp, recalcmd, 2) == -1) {
27640Sstevel@tonic-gate 			/* cntlr did not accept command bytes */
27650Sstevel@tonic-gate 			fdcquiesce(fcp);
27660Sstevel@tonic-gate 			csb->csb_cmdstat = EIO;
27670Sstevel@tonic-gate 			csb->csb_xstate = FXS_RESET;
27680Sstevel@tonic-gate 			break;
27690Sstevel@tonic-gate 		}
27700Sstevel@tonic-gate 		fcp->c_sekdir[unit] = 0;
27710Sstevel@tonic-gate 		csb->csb_xstate = FXS_RCAL;
27720Sstevel@tonic-gate 		break;
27730Sstevel@tonic-gate 
27740Sstevel@tonic-gate 	case FXS_RCAL:		/* forced recalibrate is complete */
27750Sstevel@tonic-gate #if 0	/* #ifdef _VPIX */
27760Sstevel@tonic-gate 	/* WARNING: this code breaks SPARC compatibility */
27770Sstevel@tonic-gate 		if (csb->csb_opflags & CSB_OFRAWIOCTL &&
27780Sstevel@tonic-gate 		    *csb->csb_cmd == FO_RECAL) {
27790Sstevel@tonic-gate 			fcp->c_curpcyl[unit] = 0;
27800Sstevel@tonic-gate 			csb->csb_status = 0;
27810Sstevel@tonic-gate 			goto nxs_cmpl;
27820Sstevel@tonic-gate 		}
27830Sstevel@tonic-gate #endif
27840Sstevel@tonic-gate 		(void) fdc_docmd(fcp, &senseintcmd, 1);
27850Sstevel@tonic-gate 		/*
27860Sstevel@tonic-gate 		 * Ignored return. If failed, warning was issued by fdc_docmd.
27870Sstevel@tonic-gate 		 * fdc_results retrieves the controller/drive status
27880Sstevel@tonic-gate 		 */
27890Sstevel@tonic-gate 		(void) fdc_result(fcp, csb->csb_rslt, 2);
27900Sstevel@tonic-gate 		/*
27910Sstevel@tonic-gate 		 * Ignored return. If failed, warning was issued by fdc_result.
27920Sstevel@tonic-gate 		 * Actual results checked below
27930Sstevel@tonic-gate 		 */
27940Sstevel@tonic-gate 		if ((csb->csb_status = ((*csb->csb_rslt ^ S0_SEKEND) &
27950Sstevel@tonic-gate 		    (S0_ICMASK | S0_SEKEND | S0_ECHK | S0_NOTRDY))) != 0) {
27960Sstevel@tonic-gate 			FCERRPRINT(FDEP_L3, FDEM_EXEC,
27970Sstevel@tonic-gate 			    (CE_WARN, "fdc_statemach unit %d: recal result %x",
27980Sstevel@tonic-gate 			    csb->csb_drive, *csb->csb_rslt));
27990Sstevel@tonic-gate 			fdcquiesce(fcp);
28000Sstevel@tonic-gate 			csb->csb_cmdstat = EIO;
28010Sstevel@tonic-gate 			csb->csb_xstate = FXS_RESET;
28020Sstevel@tonic-gate 			break;
28030Sstevel@tonic-gate 		}
28040Sstevel@tonic-gate 		if (unit != (*csb->csb_rslt & 3) || csb->csb_rslt[1]) {
28050Sstevel@tonic-gate 			csb->csb_status = S0_SEKEND;
28060Sstevel@tonic-gate 			goto nxs_cmpl;
28070Sstevel@tonic-gate 		}
28080Sstevel@tonic-gate 		fcp->c_curpcyl[unit] = csb->csb_rslt[1];
28090Sstevel@tonic-gate 		if (*csb->csb_cmd == FO_RECAL)
28100Sstevel@tonic-gate 			goto nxs_cmpl;
28110Sstevel@tonic-gate nxs_seek:
28120Sstevel@tonic-gate 		if (*csb->csb_cmd != FO_SEEK &&
28130Sstevel@tonic-gate 		    csb->csb_npcyl == fcp->c_curpcyl[unit])
28140Sstevel@tonic-gate 			goto nxs_doit;
28150Sstevel@tonic-gate 		fcp->c_sekdir[unit] = csb->csb_npcyl - fcp->c_curpcyl[unit];
28160Sstevel@tonic-gate 		/* FALLTHROUGH */
28170Sstevel@tonic-gate 
28180Sstevel@tonic-gate 	case FXS_DKCHGX:	/* reset Disk-Change latch */
28190Sstevel@tonic-gate 		(void) fdcseek(fcp, csb->csb_cmd[1], csb->csb_npcyl);
28200Sstevel@tonic-gate 		/*
28210Sstevel@tonic-gate 		 * Ignored return.  If command rejected, warnig already posted
28220Sstevel@tonic-gate 		 * by fdc_docmd().
28230Sstevel@tonic-gate 		 */
28240Sstevel@tonic-gate 		csb->csb_xstate = FXS_SEEK;
28250Sstevel@tonic-gate 		break;
28260Sstevel@tonic-gate 
28270Sstevel@tonic-gate 	case FXS_RESTART:	/* special restart of read/write operation */
28280Sstevel@tonic-gate 		ASSERT(fcp->c_timeid == 0);
28290Sstevel@tonic-gate 		time = drv_usectohz(100000 * csb->csb_timer);
28300Sstevel@tonic-gate 		if (time == 0)
28310Sstevel@tonic-gate 			time = drv_usectohz(2000000);
28320Sstevel@tonic-gate 		fcp->c_timeid = timeout(fdwatch, (void *)fcp, time);
28330Sstevel@tonic-gate 
28340Sstevel@tonic-gate 		if (fcp->c_mtrstate[unit] != FMS_ON) {
28350Sstevel@tonic-gate 			cmn_err(CE_WARN, "fdc: selected but motor off");
28360Sstevel@tonic-gate 			return (-1);
28370Sstevel@tonic-gate 		}
28380Sstevel@tonic-gate 		if ((csb->csb_npcyl == 0 || fcp->c_sekdir[unit] >= 0) &&
28390Sstevel@tonic-gate 		    (int)csb->csb_cmd[2] < (fjp->fj_chars->fdc_ncyl - 1))
28400Sstevel@tonic-gate 			backoff = csb->csb_npcyl + 1;
28410Sstevel@tonic-gate 		else
28420Sstevel@tonic-gate 			backoff = csb->csb_npcyl - 1;
28430Sstevel@tonic-gate 		(void) fdcseek(fcp, csb->csb_cmd[1], backoff);
28440Sstevel@tonic-gate 		/*
28450Sstevel@tonic-gate 		 * Ignored return.  If command rejected, warnig already posted
28460Sstevel@tonic-gate 		 * by fdc_docmd().
28470Sstevel@tonic-gate 		 */
28480Sstevel@tonic-gate 		csb->csb_xstate = FXS_RESEEK;
28490Sstevel@tonic-gate 		break;
28500Sstevel@tonic-gate 
28510Sstevel@tonic-gate 	case FXS_RESEEK:	/* seek to backoff-cyl complete */
28520Sstevel@tonic-gate 		(void) fdc_docmd(fcp, &senseintcmd, 1);
28530Sstevel@tonic-gate 		/*
28540Sstevel@tonic-gate 		 * Ignored return. If failed, warning was issued by fdc_docmd.
28550Sstevel@tonic-gate 		 * fdc_results retrieves the controller/drive status
28560Sstevel@tonic-gate 		 */
28570Sstevel@tonic-gate 		(void) fdc_result(fcp, csb->csb_rslt, 2);
28580Sstevel@tonic-gate 		/*
28590Sstevel@tonic-gate 		 * Ignored return. If failed, warning was issued by fdc_result.
28600Sstevel@tonic-gate 		 * Actual results checked below
28610Sstevel@tonic-gate 		 */
28620Sstevel@tonic-gate 		if ((csb->csb_status = ((*csb->csb_rslt ^ S0_SEKEND) &
28630Sstevel@tonic-gate 		    (S0_ICMASK | S0_SEKEND | S0_ECHK | S0_NOTRDY))) != 0)
28640Sstevel@tonic-gate 			goto nxs_cmpl;
28650Sstevel@tonic-gate 		(void) fdcseek(fcp, csb->csb_cmd[1], csb->csb_npcyl);
28660Sstevel@tonic-gate 		/*
28670Sstevel@tonic-gate 		 * Ignored return.  If command rejected, warnig already posted
28680Sstevel@tonic-gate 		 * by fdc_docmd().
28690Sstevel@tonic-gate 		 */
28700Sstevel@tonic-gate 		csb->csb_xstate = FXS_SEEK;
28710Sstevel@tonic-gate 		break;
28720Sstevel@tonic-gate 
28730Sstevel@tonic-gate 	case FXS_SEEK:		/* seek complete */
28740Sstevel@tonic-gate #if 0	/* #ifdef _VPIX */
28750Sstevel@tonic-gate 	/* WARNING: this code breaks SPARC compatibility and */
28760Sstevel@tonic-gate 	/* rawioctls in fdformat */
28770Sstevel@tonic-gate 		if (csb->csb_opflags & CSB_OFRAWIOCTL) {
28780Sstevel@tonic-gate 			fcp->c_curpcyl[unit] = csb->csb_npcyl;
28790Sstevel@tonic-gate 			csb->csb_status = 0;
28800Sstevel@tonic-gate 			goto nxs_cmpl;
28810Sstevel@tonic-gate 		}
28820Sstevel@tonic-gate #endif
28830Sstevel@tonic-gate 		(void) fdc_docmd(fcp, &senseintcmd, 1);
28840Sstevel@tonic-gate 		/*
28850Sstevel@tonic-gate 		 * Ignored return. If failed, warning was issued by fdc_docmd.
28860Sstevel@tonic-gate 		 * fdc_results retrieves the controller/drive status
28870Sstevel@tonic-gate 		 */
28880Sstevel@tonic-gate 		(void) fdc_result(fcp, csb->csb_rslt, 2);
28890Sstevel@tonic-gate 		/*
28900Sstevel@tonic-gate 		 * Ignored return. If failed, warning was issued by fdc_result.
28910Sstevel@tonic-gate 		 * Actual results checked below
28920Sstevel@tonic-gate 		 */
28930Sstevel@tonic-gate 		if ((csb->csb_status = ((*csb->csb_rslt ^ S0_SEKEND) &
28940Sstevel@tonic-gate 		    (S0_ICMASK | S0_SEKEND | S0_ECHK | S0_NOTRDY))) != 0)
28950Sstevel@tonic-gate 			goto nxs_cmpl;
28960Sstevel@tonic-gate 		if (unit != (*csb->csb_rslt & 3) ||
28970Sstevel@tonic-gate 		    csb->csb_rslt[1] != csb->csb_npcyl) {
28980Sstevel@tonic-gate 			csb->csb_status = S0_SEKEND;
28990Sstevel@tonic-gate 			goto nxs_cmpl;
29000Sstevel@tonic-gate 		};
29010Sstevel@tonic-gate 		fcp->c_curpcyl[unit] = csb->csb_rslt[1];
29020Sstevel@tonic-gate 		/* use motor_timer to delay for head settle */
29030Sstevel@tonic-gate 		mutex_enter(&fcp->c_dorlock);
29040Sstevel@tonic-gate 		(void) fdc_motorsm(fjp, FMI_DELAYCMD,
29050Sstevel@tonic-gate 		    fjp->fj_drive->fdd_headsettle / 1000);
29060Sstevel@tonic-gate 		/*
29070Sstevel@tonic-gate 		 * Return value ignored - fdcmotort deals with failure.
29080Sstevel@tonic-gate 		 */
29090Sstevel@tonic-gate 		mutex_exit(&fcp->c_dorlock);
29100Sstevel@tonic-gate 		csb->csb_xstate = FXS_HDST;
29110Sstevel@tonic-gate 		break;
29120Sstevel@tonic-gate 
29130Sstevel@tonic-gate 	case FXS_HDST:		/* head settle */
29140Sstevel@tonic-gate 		if (*csb->csb_cmd == FO_SEEK)
29150Sstevel@tonic-gate 			goto nxs_cmpl;
29160Sstevel@tonic-gate 		if ((*csb->csb_cmd & ~FO_MFM) == FO_FRMT)
29170Sstevel@tonic-gate 			goto nxs_doit;
29180Sstevel@tonic-gate 		fdcreadid(fcp, csb);
29190Sstevel@tonic-gate 		csb->csb_xstate = FXS_RDID;
29200Sstevel@tonic-gate 		break;
29210Sstevel@tonic-gate 
29220Sstevel@tonic-gate 	case FXS_RDID:		/* read ID complete */
29230Sstevel@tonic-gate 		(void) fdc_result(fcp, csb->csb_rslt, 7);
29240Sstevel@tonic-gate 		/*
29250Sstevel@tonic-gate 		 * Ignored return. If failed, warning was issued by fdc_result.
29260Sstevel@tonic-gate 		 * Actual results checked below
29270Sstevel@tonic-gate 		 */
29280Sstevel@tonic-gate 		if ((csb->csb_status = (*csb->csb_rslt &
29290Sstevel@tonic-gate 		    (S0_ICMASK | S0_ECHK | S0_NOTRDY))) != 0)
29300Sstevel@tonic-gate 			goto nxs_cmpl;
29310Sstevel@tonic-gate 		if (csb->csb_cmd[2] != csb->csb_rslt[3]) {
29320Sstevel@tonic-gate 			/* at wrong logical cylinder */
29330Sstevel@tonic-gate 			csb->csb_status = S0_SEKEND;
29340Sstevel@tonic-gate 			goto nxs_cmpl;
29350Sstevel@tonic-gate 		};
29360Sstevel@tonic-gate 		goto nxs_doit;
29370Sstevel@tonic-gate 
29380Sstevel@tonic-gate 	case FXS_DOIT:		/* do original operation */
29390Sstevel@tonic-gate 		ASSERT(fcp->c_timeid == 0);
29400Sstevel@tonic-gate 		time = drv_usectohz(100000 * csb->csb_timer);
29410Sstevel@tonic-gate 		if (time == 0)
29420Sstevel@tonic-gate 			time = drv_usectohz(2000000);
29430Sstevel@tonic-gate 		fcp->c_timeid = timeout(fdwatch, (void *)fcp, time);
29440Sstevel@tonic-gate nxs_doit:
29450Sstevel@tonic-gate 		if (fdc_docmd(fcp, csb->csb_cmd, csb->csb_ncmds) == -1) {
29460Sstevel@tonic-gate 			/* cntlr did not accept command bytes */
29470Sstevel@tonic-gate 			fdcquiesce(fcp);
29480Sstevel@tonic-gate 			csb->csb_xstate = FXS_RESET;
29490Sstevel@tonic-gate 			csb->csb_cmdstat = EIO;
29500Sstevel@tonic-gate 			break;
29510Sstevel@tonic-gate 		}
29520Sstevel@tonic-gate 		csb->csb_xstate = FXS_DOWT;
29530Sstevel@tonic-gate 		break;
29540Sstevel@tonic-gate 
29550Sstevel@tonic-gate 	case FXS_DOWT:		/* operation complete */
29560Sstevel@tonic-gate 		(void) fdc_result(fcp, csb->csb_rslt, csb->csb_nrslts);
29570Sstevel@tonic-gate 		/*
29580Sstevel@tonic-gate 		 * Ignored return. If failed, warning was issued by fdc_result.
29590Sstevel@tonic-gate 		 * Actual results checked below.
29600Sstevel@tonic-gate 		 */
29610Sstevel@tonic-gate 		if (*csb->csb_cmd == FO_SDRV) {
29620Sstevel@tonic-gate 			csb->csb_status =
29630Sstevel@tonic-gate 			    (*csb->csb_rslt ^ (S3_DRRDY | S3_2SIDE)) &
29640Sstevel@tonic-gate 			    ~(S3_HEAD | S3_UNIT);
29650Sstevel@tonic-gate 		} else {
29660Sstevel@tonic-gate 			csb->csb_status = *csb->csb_rslt &
29670Sstevel@tonic-gate 			    (S0_ICMASK | S0_ECHK | S0_NOTRDY);
29680Sstevel@tonic-gate 		}
29690Sstevel@tonic-gate nxs_cmpl:
29700Sstevel@tonic-gate 		if (csb->csb_status)
29710Sstevel@tonic-gate 			csb->csb_cmdstat = EIO;
29720Sstevel@tonic-gate 		csb->csb_xstate = FXS_END;
29730Sstevel@tonic-gate 
29740Sstevel@tonic-gate 		/*  remove watchdog timer if armed and not already triggered */
29750Sstevel@tonic-gate 		if (fcp->c_timeid != 0) {
29760Sstevel@tonic-gate 			timeout_id_t timeid;
29770Sstevel@tonic-gate 			timeid = fcp->c_timeid;
29780Sstevel@tonic-gate 			fcp->c_timeid = 0;
29790Sstevel@tonic-gate 			mutex_exit(&fcp->c_lock);
29800Sstevel@tonic-gate 			(void) untimeout(timeid);
29810Sstevel@tonic-gate 			mutex_enter(&fcp->c_lock);
29820Sstevel@tonic-gate 		}
29830Sstevel@tonic-gate 		break;
29840Sstevel@tonic-gate 
29850Sstevel@tonic-gate 	case FXS_KILL:		/* quiesce cntlr by reset */
29860Sstevel@tonic-gate 		fdcquiesce(fcp);
29870Sstevel@tonic-gate 		fcp->c_timeid = timeout(fdwatch, (void *)fcp,
29880Sstevel@tonic-gate 		    drv_usectohz(2000000));
29890Sstevel@tonic-gate 		csb->csb_xstate = FXS_RESET;
29900Sstevel@tonic-gate 		break;
29910Sstevel@tonic-gate 
29920Sstevel@tonic-gate 	case FXS_RESET:		/* int from reset */
29930Sstevel@tonic-gate 		for (unit = 0; unit < NFDUN; unit++) {
29940Sstevel@tonic-gate 			(void) fdcsense_int(fcp, NULL, NULL);
29950Sstevel@tonic-gate 			fcp->c_curpcyl[unit] = -1;
29960Sstevel@tonic-gate 		}
29970Sstevel@tonic-gate 		if (fcp->c_timeid != 0) {
29980Sstevel@tonic-gate 			timeout_id_t timeid;
29990Sstevel@tonic-gate 			timeid = fcp->c_timeid;
30000Sstevel@tonic-gate 			fcp->c_timeid = 0;
30010Sstevel@tonic-gate 			mutex_exit(&fcp->c_lock);
30020Sstevel@tonic-gate 			(void) untimeout(timeid);
30030Sstevel@tonic-gate 			mutex_enter(&fcp->c_lock);
30040Sstevel@tonic-gate 		}
30050Sstevel@tonic-gate 		csb->csb_xstate = FXS_END;
30060Sstevel@tonic-gate 		break;
30070Sstevel@tonic-gate 
30080Sstevel@tonic-gate 	default:
30090Sstevel@tonic-gate 		cmn_err(CE_WARN, "fdc: statemach, unknown state");
30100Sstevel@tonic-gate 		return (-1);
30110Sstevel@tonic-gate 	}
30120Sstevel@tonic-gate 	FCERRPRINT(FDEP_L1, FDEM_EXEC,
30130Sstevel@tonic-gate 	    (CE_CONT, "fdc_statemach unit %d: %d -> %d\n",
30140Sstevel@tonic-gate 	    csb->csb_drive, csb->csb_oldxs, csb->csb_xstate));
30150Sstevel@tonic-gate 	return (csb->csb_xstate);
30160Sstevel@tonic-gate }
30170Sstevel@tonic-gate 
30180Sstevel@tonic-gate 
30190Sstevel@tonic-gate /*
30200Sstevel@tonic-gate  * routine to program a command into the floppy disk controller.
30210Sstevel@tonic-gate  */
30220Sstevel@tonic-gate int
30230Sstevel@tonic-gate fdc_docmd(struct fdcntlr *fcp, uchar_t *oplistp, uchar_t count)
30240Sstevel@tonic-gate {
30250Sstevel@tonic-gate 	int ntries;
30260Sstevel@tonic-gate 
30270Sstevel@tonic-gate 	ASSERT(count >= 1);
30280Sstevel@tonic-gate 	FCERRPRINT(FDEP_L0, FDEM_EXEC,
30290Sstevel@tonic-gate 	    (CE_CONT, "fdc_docmd: %x %x %x %x %x %x %x %x %x\n",
30300Sstevel@tonic-gate 	    oplistp[0], oplistp[1], oplistp[2], oplistp[3], oplistp[4],
30310Sstevel@tonic-gate 	    oplistp[5], oplistp[6], oplistp[7], oplistp[8]));
30320Sstevel@tonic-gate 
30330Sstevel@tonic-gate 	do {
30340Sstevel@tonic-gate 		ntries = FDC_RQM_RETRY;
30350Sstevel@tonic-gate 		do {
30360Sstevel@tonic-gate 			if ((inb(fcp->c_regbase + FCR_MSR) & (MS_RQM|MS_DIO))
30370Sstevel@tonic-gate 			    == MS_RQM)
30380Sstevel@tonic-gate 				break;
30390Sstevel@tonic-gate 			else
30400Sstevel@tonic-gate 				drv_usecwait(1);
30410Sstevel@tonic-gate 		} while (--ntries);
30420Sstevel@tonic-gate 		if (ntries == 0) {
30430Sstevel@tonic-gate 			FCERRPRINT(FDEP_L3, FDEM_EXEC,
30440Sstevel@tonic-gate 			    (CE_WARN, "fdc_docmd: ctlr not ready"));
30450Sstevel@tonic-gate 			return (-1);
30460Sstevel@tonic-gate 		}
30470Sstevel@tonic-gate 		outb(fcp->c_regbase + FCR_DATA, *oplistp++);
30480Sstevel@tonic-gate 		drv_usecwait(16);	/* See comment in fdc_result() */
30490Sstevel@tonic-gate 	} while (--count);
30500Sstevel@tonic-gate 	return (0);
30510Sstevel@tonic-gate }
30520Sstevel@tonic-gate 
30530Sstevel@tonic-gate 
30540Sstevel@tonic-gate /*
30550Sstevel@tonic-gate  * Routine to return controller/drive status information.
30560Sstevel@tonic-gate  * The diskette-controller data-register is read the
30570Sstevel@tonic-gate  * requested number of times and the results are placed in
30580Sstevel@tonic-gate  * consecutive memory locations starting at the passed
30590Sstevel@tonic-gate  * address.
30600Sstevel@tonic-gate  */
30610Sstevel@tonic-gate int
30620Sstevel@tonic-gate fdc_result(struct fdcntlr *fcp, uchar_t *rsltp, uchar_t rcount)
30630Sstevel@tonic-gate {
30640Sstevel@tonic-gate 	int ntries;
30650Sstevel@tonic-gate 	uchar_t *abresultp = rsltp;
30660Sstevel@tonic-gate 	uchar_t stat;
30670Sstevel@tonic-gate 	int laxative = 7;
30680Sstevel@tonic-gate 
30690Sstevel@tonic-gate 	ntries = 10 * FDC_RQM_RETRY;
30700Sstevel@tonic-gate 	do {
30710Sstevel@tonic-gate 		do {
30720Sstevel@tonic-gate 			if ((inb(fcp->c_regbase + FCR_MSR) &
30730Sstevel@tonic-gate 			    (MS_RQM | MS_DIO)) == (MS_RQM | MS_DIO))
30740Sstevel@tonic-gate 				break;
30750Sstevel@tonic-gate 			else
30760Sstevel@tonic-gate 				drv_usecwait(10);
30770Sstevel@tonic-gate 		} while (--ntries);
30780Sstevel@tonic-gate 		if (!ntries) {
30790Sstevel@tonic-gate 			FCERRPRINT(FDEP_L3, FDEM_EXEC,
30800Sstevel@tonic-gate 			    (CE_WARN, "fdc_result: ctlr not ready"));
30810Sstevel@tonic-gate 			return (-2);
30820Sstevel@tonic-gate 		}
30830Sstevel@tonic-gate 		*rsltp++ = inb(fcp->c_regbase + FCR_DATA);
30840Sstevel@tonic-gate 
30850Sstevel@tonic-gate 		/*
30860Sstevel@tonic-gate 		 * The PRM suggests waiting for 14.5 us.
30870Sstevel@tonic-gate 		 * Adding a bit more to cover the case of bad calibration
30880Sstevel@tonic-gate 		 * of drv_usecwait().
30890Sstevel@tonic-gate 		 */
30900Sstevel@tonic-gate 		drv_usecwait(16);
30910Sstevel@tonic-gate 		ntries = FDC_RQM_RETRY;
30920Sstevel@tonic-gate 	} while (--rcount);
30930Sstevel@tonic-gate 	while ((inb(fcp->c_regbase + FCR_MSR) & MS_CB) && laxative--) {
30940Sstevel@tonic-gate 		FCERRPRINT(FDEP_L3, FDEM_EXEC,
30950Sstevel@tonic-gate 		    (CE_WARN, "fdc_result: ctlr still busy"));
30960Sstevel@tonic-gate 		/*
30970Sstevel@tonic-gate 		 * try to complete Result phase by purging
30980Sstevel@tonic-gate 		 * result bytes queued for reading
30990Sstevel@tonic-gate 		 */
31000Sstevel@tonic-gate 		*abresultp = S0_IVCMD;
31010Sstevel@tonic-gate 		do {
31020Sstevel@tonic-gate 			stat = inb(fcp->c_regbase + FCR_MSR) &
31030Sstevel@tonic-gate 			    (MS_RQM | MS_DIO);
31040Sstevel@tonic-gate 			if (stat == MS_RQM) {
31050Sstevel@tonic-gate 				/*
31060Sstevel@tonic-gate 				 * Result phase is complete
31070Sstevel@tonic-gate 				 * but did we get the results corresponding to
31080Sstevel@tonic-gate 				 * the command we think we executed?
31090Sstevel@tonic-gate 				 */
31100Sstevel@tonic-gate 				return (-1);
31110Sstevel@tonic-gate 			}
31120Sstevel@tonic-gate 			if (stat == (MS_RQM | MS_DIO))
31130Sstevel@tonic-gate 				break;
31140Sstevel@tonic-gate 			else
31150Sstevel@tonic-gate 				drv_usecwait(10);
31160Sstevel@tonic-gate 		} while (--ntries);
31170Sstevel@tonic-gate 		if (!ntries || !laxative) {
31180Sstevel@tonic-gate 			FCERRPRINT(FDEP_L3, FDEM_EXEC,
31190Sstevel@tonic-gate 			    (CE_WARN,
31200Sstevel@tonic-gate 			    "fdc_result: ctlr still busy and not ready"));
31210Sstevel@tonic-gate 			return (-3);
31220Sstevel@tonic-gate 		}
31230Sstevel@tonic-gate 		(void) inb(fcp->c_regbase + FCR_DATA);
31240Sstevel@tonic-gate 
31250Sstevel@tonic-gate 		drv_usecwait(16);	/* See comment above */
31260Sstevel@tonic-gate 		ntries = FDC_RQM_RETRY;
31270Sstevel@tonic-gate 	}
31280Sstevel@tonic-gate 	return (0);
31290Sstevel@tonic-gate }
31300Sstevel@tonic-gate 
31310Sstevel@tonic-gate /*
31320Sstevel@tonic-gate  *  Function: get_unit()
31330Sstevel@tonic-gate  *
31340Sstevel@tonic-gate  *  Assumptions:  ioaddr is either 0x3f0 or 0x370
31350Sstevel@tonic-gate  */
31360Sstevel@tonic-gate static int
31370Sstevel@tonic-gate get_unit(dev_info_t *dip, int *cntrl_num)
31380Sstevel@tonic-gate {
31390Sstevel@tonic-gate 	int ioaddr;
31400Sstevel@tonic-gate 
31410Sstevel@tonic-gate 	if (get_ioaddr(dip, &ioaddr) != DDI_SUCCESS)
31420Sstevel@tonic-gate 		return (DDI_FAILURE);
31430Sstevel@tonic-gate 
31440Sstevel@tonic-gate 	switch (ioaddr) {
31450Sstevel@tonic-gate 	case 0x3f0:
31460Sstevel@tonic-gate 		*cntrl_num = 0;
31470Sstevel@tonic-gate 		break;
31480Sstevel@tonic-gate 
31490Sstevel@tonic-gate 	case 0x370:
31500Sstevel@tonic-gate 		*cntrl_num = 1;
31510Sstevel@tonic-gate 		break;
31520Sstevel@tonic-gate 
31530Sstevel@tonic-gate 	default:
31540Sstevel@tonic-gate 		return (DDI_FAILURE);
31550Sstevel@tonic-gate 	}
31560Sstevel@tonic-gate 	return (DDI_SUCCESS);
31570Sstevel@tonic-gate }
31580Sstevel@tonic-gate 
31590Sstevel@tonic-gate static int
31600Sstevel@tonic-gate get_ioaddr(dev_info_t *dip, int *ioaddr)
31610Sstevel@tonic-gate {
31620Sstevel@tonic-gate 	int reglen, nregs, i;
31630Sstevel@tonic-gate 	int status = DDI_FAILURE;
31640Sstevel@tonic-gate 	struct {
31650Sstevel@tonic-gate 		int bustype;
31660Sstevel@tonic-gate 		int base;
31670Sstevel@tonic-gate 		int size;
31680Sstevel@tonic-gate 	} *reglist;
31690Sstevel@tonic-gate 
31700Sstevel@tonic-gate 	if (ddi_getlongprop(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS,
31710Sstevel@tonic-gate 		"reg", (caddr_t)&reglist, &reglen) != DDI_PROP_SUCCESS) {
31720Sstevel@tonic-gate 		cmn_err(CE_WARN, "fdc: reg property not found");
31730Sstevel@tonic-gate 		return (DDI_FAILURE);
31740Sstevel@tonic-gate 	}
31750Sstevel@tonic-gate 
31760Sstevel@tonic-gate 	nregs = reglen / sizeof (*reglist);
31770Sstevel@tonic-gate 	for (i = 0; i < nregs; i++) {
31780Sstevel@tonic-gate 		if (reglist[i].bustype == 1) {
31790Sstevel@tonic-gate 			*ioaddr = reglist[i].base;
31800Sstevel@tonic-gate 			status = DDI_SUCCESS;
31810Sstevel@tonic-gate 			break;
31820Sstevel@tonic-gate 		}
31830Sstevel@tonic-gate 	}
31840Sstevel@tonic-gate 	kmem_free(reglist, reglen);
31850Sstevel@tonic-gate 
31860Sstevel@tonic-gate 	if (status == DDI_SUCCESS) {
31870Sstevel@tonic-gate 		if (*ioaddr == 0x3f2 || *ioaddr == 0x372) {
31880Sstevel@tonic-gate 			/*
31890Sstevel@tonic-gate 			 * Some BIOS's (ASUS is one) don't include first
31900Sstevel@tonic-gate 			 * two IO ports in the floppy controller resources.
31910Sstevel@tonic-gate 			 */
31920Sstevel@tonic-gate 
31930Sstevel@tonic-gate 			*ioaddr -= 2; /* step back to 0x3f0 or 0x370 */
31940Sstevel@tonic-gate 
31950Sstevel@tonic-gate 			/*
31960Sstevel@tonic-gate 			 * It would be nice to update the regs property as well
31970Sstevel@tonic-gate 			 * so device pathname contains 3f0 instead of 3f2, but
31980Sstevel@tonic-gate 			 * updating the regs now won't have this effect as that
31990Sstevel@tonic-gate 			 * component of the device pathname has already been
32000Sstevel@tonic-gate 			 * constructed by the ISA nexus driver.
32010Sstevel@tonic-gate 			 *
32020Sstevel@tonic-gate 			 * reglist[i].base -= 2;
32030Sstevel@tonic-gate 			 * reglist[i].size += 2;
32040Sstevel@tonic-gate 			 * dev = makedevice(ddi_driver_major(dip), 0);
32050Sstevel@tonic-gate 			 * ddi_prop_update_int_array(dev, dip, "reg",
32060Sstevel@tonic-gate 			 *    (int *)reglist, reglen / sizeof (int));
32070Sstevel@tonic-gate 			 */
32080Sstevel@tonic-gate 		}
32090Sstevel@tonic-gate 	}
32100Sstevel@tonic-gate 
32110Sstevel@tonic-gate 	return (status);
32120Sstevel@tonic-gate }
3213