fdc.c revision 2df1fe9ca32bb227b9158c67f5c00b54c20b10fd
/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (the "License").
* You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright 2007 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#pragma ident "%Z%%M% %I% %E% SMI"
/*
* Floppy Disk Controller Driver
*
* for the standard PC architecture using the Intel 8272A fdc.
* Note that motor control and drive select use a latch external
* to the fdc.
*
* This driver is EISA capable, and uses DMA buffer chaining if available.
* If this driver is attached to the ISA bus nexus (or if the EISA bus driver
* does not support DMA buffer chaining), then the bus driver must ensure
* that dma mapping (breakup) and dma engine requests are properly degraded.
*/
/*
* hack for bugid 1160621:
* workaround compiler optimization bug by turning on DEBUG
*/
#ifndef DEBUG
#define DEBUG 1
#endif
#include <sys/autoconf.h>
#include <sys/fd_debug.h>
/*
* bss (uninitialized data)
*/
static void *fdc_state_head; /* opaque handle top of state structs */
static ddi_dma_attr_t fdc_dma_attr;
/*
* Local static data
*/
#define OURUN_TRIES 12
/*
* error handling
*
* for debugging, set rwretry and skretry = 1
* set fcerrlevel to 1
* set fcerrmask to 224 or 644
*
* after debug, set rwretry to 4, skretry to 3, and fcerrlevel to 5
* set fcerrmask to FDEM_ALL
* or remove the define DEBUG
*/
static int fcerrlevel = 6;
static xlate_tbl_t drate_mfm[] = {
{ 250, 2},
{ 300, 1},
{ 417, 0},
{ 500, 0},
{ 1000, 3},
{ 0, 0}
};
static xlate_tbl_t sector_size[] = {
{ 256, 1},
{ 512, 2},
{ 1024, 3},
{ 0, 2}
};
static xlate_tbl_t motor_onbits[] = {
{ 0, 0x10},
{ 1, 0x20},
{ 2, 0x40},
{ 3, 0x80},
{ 0, 0x80}
};
static xlate_tbl_t step_rate[] = {
{ 10, 0xF0}, /* for 500K data rate */
{ 20, 0xE0},
{ 30, 0xD0},
{ 40, 0xC0},
{ 50, 0xB0},
{ 60, 0xA0},
{ 70, 0x90},
{ 80, 0x80},
{ 90, 0x70},
{ 100, 0x60},
{ 110, 0x50},
{ 120, 0x40},
{ 130, 0x30},
{ 140, 0x20},
{ 150, 0x10},
{ 160, 0x00},
{ 0, 0x00}
};
#ifdef notdef
static xlate_tbl_t head_unld[] = {
{ 16, 0x1}, /* for 500K data rate */
{ 32, 0x2},
{ 48, 0x3},
{ 64, 0x4},
{ 80, 0x5},
{ 96, 0x6},
{ 112, 0x7},
{ 128, 0x8},
{ 144, 0x9},
{ 160, 0xA},
{ 176, 0xB},
{ 192, 0xC},
{ 208, 0xD},
{ 224, 0xE},
{ 240, 0xF},
{ 256, 0x0},
{ 0, 0x0}
};
#endif
static struct fdcmdinfo {
char *cmdname; /* command name */
} fdcmds[] = {
"", 0, 0, 0, /* - */
"", 0, 0, 0, /* - */
"read_track", 9, 7, 1, /* 2 */
"specify", 3, 0, 3, /* 3 */
"sense_drv_status", 2, 1, 3, /* 4 */
"write", 9, 7, 1, /* 5 */
"read", 9, 7, 1, /* 6 */
"recalibrate", 2, 0, 2, /* 7 */
"sense_int_status", 1, 2, 3, /* 8 */
"write_del", 9, 7, 1, /* 9 */
"read_id", 2, 7, 2, /* A */
"", 0, 0, 0, /* - */
"read_del", 9, 7, 1, /* C */
"format_track", 10, 7, 1, /* D */
"dump_reg", 1, 10, 4, /* E */
"seek", 3, 0, 2, /* F */
"version", 1, 1, 3, /* 10 */
"", 0, 0, 0, /* - */
"perp_mode", 2, 0, 3, /* 12 */
"configure", 4, 0, 4, /* 13 */
/* relative seek */
};
static int
struct bus_ops fdc_bus_ops = {
0, /* ddi_intrspec_t (*bus_get_intrspec)(); */
0, /* int (*bus_add_intrspec)(); */
0, /* void (*bus_remove_intrspec)(); */
};
static int fdc_probe(dev_info_t *);
DEVO_REV, /* devo_rev, */
0, /* refcnt */
fdc_getinfo, /* getinfo */
nulldev, /* identify */
fdc_probe, /* probe */
fdc_attach, /* attach */
fdc_detach, /* detach */
nodev, /* reset */
(struct cb_ops *)0, /* driver operations */
&fdc_bus_ops /* bus operations */
};
/*
* This is the loadable module wrapper.
*/
extern struct mod_ops mod_driverops;
&mod_driverops, /* Type of module. This one is a driver */
"Floppy Controller %I%", /* Name of the module. */
&fdc_ops, /* Driver ops vector */
};
static struct modlinkage modlinkage = {
};
int
_init(void)
{
int retval;
return (retval);
return (retval);
}
int
_fini(void)
{
int retval;
return (retval);
return (retval);
}
int
{
}
int fdc_getcap(struct fcu_obj *, char *, int);
int fdc_setcap(struct fcu_obj *, char *, int, int);
int fdc_select(struct fcu_obj *, int, int);
int fdresetchng(struct fcu_obj *, int);
int fdrecalseek(struct fcu_obj *, int, int, int);
int fdtrkformat(struct fcu_obj *, int, int, int, int);
fdc_start, /* controller start */
fdc_abort, /* controller abort */
fdc_getcap, /* capability retrieval */
fdc_setcap, /* capability establishment */
fdc_dkinfo, /* get disk controller info */
fdc_select, /* select / deselect unit */
fdgetchng, /* get media change */
fdresetchng, /* reset media change */
fdrecalseek, /* recal / seek */
NULL, /* read /write request (UNUSED) */
fdrw, /* read /write sector */
fdtrkformat, /* format track */
fdrawioctl /* raw ioctl */
};
/*
* Function prototypes
*/
int decode(xlate_tbl_t *, int, int *);
static int fdc_propinit1(struct fdcntlr *, int);
static void fdc_propinit2(struct fdcntlr *, int);
void fdcquiesce(struct fdcntlr *);
int fdcsense_chng(struct fdcntlr *, int);
int fdcsense_drv(struct fdcntlr *, int);
int fdcsense_int(struct fdcntlr *, int *, int *);
int fdcspecify(struct fdcntlr *, int, int, int);
int fdcheckdisk(struct fdcntlr *, int);
static int fdc_motorsm(struct fcu_obj *, int, int);
static int fdc_statemach(struct fdcntlr *);
/* ARGSUSED */
static int
{
return (DDI_FAILURE);
switch (ctlop) {
case DDI_CTLOPS_REPORTDEV:
(CE_WARN, "fdc_bus_ctl: report %s%d at %s%d",
return (DDI_SUCCESS);
case DDI_CTLOPS_INITCHILD:
{
int cntlr;
int len;
int unit;
char name[MAXNAMELEN];
!= DDI_PROP_SUCCESS ||
return (DDI_NOT_WELL_FORMED);
return (DDI_SUCCESS);
}
case DDI_CTLOPS_UNINITCHILD:
{
return (DDI_SUCCESS);
}
default:
return (DDI_FAILURE);
}
}
/* ARGSUSED */
static int
{
int rval;
switch (cmd) {
case DDI_INFO_DEVT2DEVINFO:
rval = DDI_SUCCESS;
break;
} else {
rval = DDI_FAILURE;
break;
}
case DDI_INFO_DEVT2INSTANCE:
rval = DDI_SUCCESS;
break;
default:
rval = DDI_FAILURE;
}
return (rval);
}
static int
{
int debug[2];
int ioaddr;
int len;
fcerrlevel = debug[0];
}
(void*)dip));
return (DDI_PROBE_FAILURE);
return (DDI_PROBE_FAILURE);
return (DDI_PROBE_SUCCESS);
}
/* ARGSUSED */
static int
{
int intr_set = 0;
int len;
char name[MAXNAMELEN];
(void*)dip));
switch (cmd) {
case DDI_ATTACH:
if (ddi_getprop
"fdc_attach failed: dip %p", (void*)dip));
return (DDI_FAILURE);
}
} else {
return (DDI_FAILURE);
}
return (DDI_FAILURE);
}
goto no_attach;
/* get iblock cookie to initialize mutex used in the ISR */
DDI_SUCCESS) {
"fdc_attach: cannot get iblock cookie");
goto no_attach;
}
intr_set = 1;
/* setup interrupt handler */
DDI_SUCCESS) {
goto no_attach;
}
intr_set++;
/*
* acquire the DMA channel
* this assumes that the chnl is not shared; else allocate
* and free the chnl with each fdc request
*/
!= DDI_SUCCESS) {
goto no_attach;
}
if (fcp->c_intrstat) {
}
/*
* reset the controller
*/
/* first test for mode == Model 30 */
}
return (DDI_SUCCESS);
case DDI_RESUME:
return (DDI_SUCCESS);
/* break; */
default:
return (DDI_FAILURE);
}
if (intr_set) {
if (intr_set > 1)
}
return (DDI_FAILURE);
}
static int
{
int len;
int value;
return (DDI_FAILURE);
!= DDI_PROP_SUCCESS) {
"fdc_attach: Error, could not find a dma channel");
return (DDI_FAILURE);
}
return (DDI_SUCCESS);
}
/* ARGSUSED */
static void
{
int ccr;
int len;
int value;
== DDI_PROP_SUCCESS)
else {
/*
* Ignored return. If failed, warning was issued by fdc_docmd.
* fdc_results retrieves the controller/drive status
*/
/*
* try a perpendicular_mode cmd to ensure
* that we really have an enhanced controller
*/
/*
* perpindicular_mode will be rejected by
* older controllers; make sure we don't hang.
*/
/*
* Ignored return. If failed, warning was
* issued by fdc_result.
*/
else
/* enhanced type controller */
/* default enhanced cntlr */
}
/*
* Ignoring return value because, for passed arguments, only
* DDI_SUCCESS is returned.
*/
}
/*
* run through all the combinations of NOPREC and
* datarate selection, and see if they show up in the
* Model 30 DIR
*/
drv_usecwait(5);
break;
}
}
else
}
/* ARGSUSED */
static int
{
int retcode = 0;
/*
* Try to identify the enhanced floppy controller.
* This is required so that we can program the DENSEL output to
* control 3D mode (1.0 MB, 1.6 MB and 2.0 MB unformatted capacity,
* 720 KB, 1.2 MB, and 1.44 MB formatted capacity) 3.5" dual-speed
* floppy drives. Refer to bugid 1195155.
*/
/*
* Ignored return. If failed, warning was issued by fdc_docmd.
* fdc_results retrieves the controller/drive status
*/
/*
* only enhanced National Semi PC8477 core
* should respond to this command
*/
/* low 4 bits may change */
} else
"?fdc: unidentified, enhanced, National Semiconductor cntlr %x\n", result);
} else {
do {
/* probe for motherboard version of SMC cntlr */
/* try to enable configuration mode */
ddic = ddi_enter_critical();
/* always expect 0 from config reg F */
break;
/* expect 0x65 from config reg D */
break;
if (result != 0x02) {
/* expect revision level 2 from config reg E */
"?fdc: unidentified, enhanced, SMC cntlr revision %x\n", result);
/* break; */
}
} while (retcode == 0);
while (retcode == 0) {
/* probe for adapter version of SMC cntlr */
ddic = ddi_enter_critical();
/* always expect 0 from config reg F */
break;
/* expect 0x66 from config reg D */
break;
if (result != 0x02) {
/* expect revision level 2 from config reg E */
"?fdc: unidentified, enhanced, SMC cntlr revision %x\n", result);
/* break; */
}
}
drv_usecwait(10);
}
return (retcode);
}
/* ARGSUSED */
static int
{
int unit;
int rval = 0;
(void*)dip));
switch (cmd) {
case DDI_DETACH:
break;
}
"dip %p, dmachan %x\n",
break;
case DDI_SUSPEND:
/*
* Following code causes the fdc (floppy controller)
* to suspend as long as there are no floppy drives
* attached to it.
* At present the floppy driver does not support
*
* Check if any FD units are attached
*
* if a floppy drive is present.
* So if any FD unit is attached return DDI_FAILURE
*/
"fdc_detach: fd attached, failing SUSPEND");
return (DDI_FAILURE);
}
}
rval = DDI_SUCCESS;
break;
default:
break;
}
return (rval);
}
/* ARGSUSED */
int
{
return (ENOSYS);
}
int
{
/*
* this can cause data corruption !
*/
"fdc_detach: dma release failed, "
"dip %p, dmachan %x\n",
}
drv_usecwait(500);
return (DDI_SUCCESS);
}
return (DDI_FAILURE);
}
/* ARGSUSED */
int
{
return (ENOSYS);
}
/* ARGSUSED */
int
{
return (ENOSYS);
}
int
{
return (DDI_SUCCESS);
}
/*
* on=> non-zero = select, 0 = de-select
*/
/* ARGSUSED */
int
{
if (on) {
/* possess controller */
if (fdcspecify(fcp,
"fdc_select: controller setup rejected "
"fdcntrl %p transfer rate %x step rate %x"
" head load time 40\n", (void*)fcp,
}
/* make sure drive is not selected in case we change speed */
/*
* Return value ignored - fdcmotort deals with failure.
*/
/* 3D drive requires 500 ms for speed change */
/*
* Return value ignored - fdcmotort deals with failure.
*/
}
} else {
/*
* Return value ignored - fdcmotort deals with failure.
*/
/* give up controller */
}
return (0);
}
int
{
}
int
{
int newcyl; /* where to seek for reset of DSKCHG */
else
newcyl = 1;
}
/*
* fdrecalseek
*/
int
{
int rval;
if (arg < 0) { /* is recal... */
} else {
}
csb->csb_handle_bound = 0;
csb->csb_dmacookiecnt = 0;
csb->csb_dmacurrcookie = 0;
csb->csb_dmawincnt = 0;
csb->csb_dmacurrwin = 0;
/* send cmd off to fdc_exec */
goto out;
out:
return (rval);
}
/*
*/
int
{
uint_t dmar_flags = 0;
int rval;
if (rw) {
} else { /* write */
}
else
csb->csb_handle_bound = 0;
csb->csb_dmacookiecnt = 0;
csb->csb_dmacurrcookie = 0;
csb->csb_dmawincnt = 0;
csb->csb_dmacurrwin = 0;
goto out;
}
/*
* ensure the cookie is a whole multiple of granularity and avoids
* any alignment issues.
*/
&real_size, &mem_handle);
if (rval != DDI_SUCCESS) {
goto out;
}
if (dmar_flags & DDI_DMA_WRITE) {
}
&csb->csb_dmacookiecnt);
if (rval == DDI_DMA_MAPPED) {
} else if (rval == DDI_DMA_PARTIAL_MAP) {
DDI_SUCCESS) {
goto out;
}
} else {
"fdrw: dma addr bind handle failed, rval = %d\n", rval);
goto out;
}
if (dmar_flags & DDI_DMA_READ) {
}
out:
if (csb->csb_dmahandle) {
if (csb->csb_handle_bound) {
"dma unbind handle failed\n");
csb->csb_handle_bound = 0;
}
if (mem_handle != NULL) {
}
}
return (rval);
}
int
{
int rval;
csb->csb_handle_bound = 0;
csb->csb_dmacookiecnt = 0;
csb->csb_dmacurrcookie = 0;
csb->csb_dmawincnt = 0;
csb->csb_dmacurrwin = 0;
/*
* alloc space for format track cmd
*/
/*
* NOTE: have to add size of fifo also - for dummy format action
*/
goto out;
}
/*
* ensure the cookie is a whole multiple of granularity and avoids
* any alignment issues.
*/
&real_size, &mem_handle);
if (rval != DDI_SUCCESS) {
goto out;
}
}
}
if (rval == DDI_DMA_MAPPED) {
} else if (rval == DDI_DMA_PARTIAL_MAP) {
DDI_SUCCESS) {
goto out;
}
} else {
"fdtrkformat: dma buf bind handle failed, rval = %d\n",
rval);
goto out;
}
out:
if (csb->csb_dmahandle) {
if (csb->csb_handle_bound) {
"dma unbind handle failed\n");
csb->csb_handle_bound = 0;
}
if (mem_handle != NULL) {
}
}
return (rval);
}
/* ARGSUSED */
int
{
uint_t dmar_flags = 0;
int i;
int change = 1;
int sleep = 1;
int rval = 0;
int rval_exec = 0;
/* copy cmd bytes into csb */
csb->csb_nrslts = 0;
case FO_SEEK:
change = 0;
/* FALLTHROUGH */
case FO_RECAL:
break;
case FO_FRMT:
/* FALLTHROUGH */
case FO_WRDAT:
case FO_WRDEL:
if (fdrp->fdr_nbytes == 0)
return (EINVAL);
break;
case FO_RDDAT:
case FO_RDDEL:
case FO_RDTRK:
break;
case FO_RDID:
break;
case FO_SDRV:
sleep = 0;
break;
case FO_SINT:
sleep = 0;
change = 0;
break;
case FO_SPEC:
sleep = 0;
change = 0;
break;
default:
return (EINVAL);
}
csb->csb_handle_bound = 0;
csb->csb_dmacookiecnt = 0;
csb->csb_dmacurrcookie = 0;
csb->csb_dmawincnt = 0;
csb->csb_dmacurrwin = 0;
goto out;
}
/*
* can ensure the cookie is a whole multiple of granularity and
* avoids any alignment issues.
*/
if (rval != DDI_SUCCESS) {
goto out;
}
if (dmar_flags & DDI_DMA_WRITE) {
}
&csb->csb_dmacookiecnt);
if (rval == DDI_DMA_MAPPED) {
} else if (rval == DDI_DMA_PARTIAL_MAP) {
"fdrawioctl: dma numwin failed\n");
goto out;
}
} else {
"dma buf bind handle failed, rval = %d\n", rval);
goto out;
}
}
(CE_CONT, "nbytes: %x, opflags: %x, addr: %p, len: %x\n",
fdrp->fdr_nbytes));
/*
* Note that we ignore any error returns from fdexec.
* This is the way the driver has been, and it may be
* that the raw ioctl senders simply don't want to
* see any errors returned in this fashion.
*/
/*
*/
if (dmar_flags & DDI_DMA_READ) {
}
/* copy results into fdr */
for (i = 0; i <= (int)csb->csb_nrslts; i++)
/* fdrp->fdr_nbytes = fdc->c_csb.csb_rlen; return resid */
out:
if (csb->csb_dmahandle) {
if (csb->csb_handle_bound) {
"dma unbind handle failed\n");
csb->csb_handle_bound = 0;
}
if (mem_handle != NULL) {
}
}
return (rval_exec);
}
return (rval);
}
void
{
do {
return;
}
}
int
{
do {
return (0);
}
return (-1);
}
void
{
int unit;
(void*)fcp));
"dip %p, dmachan %x\n",
drv_usecwait(20);
/* count resets */
/*
* Ignored return. If failed, warning was issued by fdc_docmd.
*/
}
}
void
{
}
int
{
}
/*
* Returns status of disk change line of selected drive.
* = 0 means diskette is present
* != 0 means diskette was removed and current state is unknown
*/
int
{
int digital_input;
return (digital_input & FDI_DKCHG);
}
int
{
int rval;
/*
* Ignored return. If failed, warning was issued by fdc_docmd.
* fdc_results retrieves the controller/drive status
*/
goto done;
else
done:
return (rval);
}
int
{
int rval;
/*
* Ignored return. If failed, warning was issued by fdc_docmd.
* fdc_results retrieves the controller/drive status
*/
rval = 1;
if (unitp)
if (cylp)
}
return (rval);
}
int
{
/*
* Use old style perpendicular mode command of 82077.
*/
if (xferrate == 1000) {
/* Set GAP and WGATE */
/* double step rate because xlate table is for 500Kb */
steprate <<= 1;
hlt <<= 1;
} else
perpindcmd[1] = 0;
/*
* Ignored return. If failed, warning was issued by fdc_docmd.
*/
}
}
int
{
int retcode = 0;
return (0);
}
default:
break;
case i82077:
break;
case PC87322:
{
} else {
/* program DENSEL to default output */
}
/* de-select drive while changing speed */
}
/*
* Ignored return. If failed, warning was issued by fdc_docmd.
*/
break;
}
case FDC37C665:
goto SMC_config;
case FDC37C666:
/* force DENSEL output to active LOW */
} else {
/* program DENSEL to default output */
ds_code = 0;
}
/* de-select drive while changing speed */
}
/* enter configuration mode */
ddic = ddi_enter_critical();
/* update DENSEL mode bits */
/* exit configuration mode */
drv_usecwait(10);
break;
}
if (deselect)
/* reselect drive */
return (retcode);
}
static int
{
int old_mstate;
int rval = 0;
switch (input) {
case FMI_TIMER: /* timer expired */
switch (old_mstate) {
case FMS_START:
case FMS_DELAY:
break;
case FMS_KILLST:
drv_usectohz(1000000));
break;
case FMS_IDLE:
break;
case 86:
rval = -1;
break;
case FMS_OFF:
case FMS_ON:
default:
rval = -2;
}
break;
case FMI_STARTCMD: /* start command */
switch (old_mstate) {
case FMS_IDLE:
break;
case FMS_OFF:
/* start motor_spinup_timer */
/* FALLTHROUGH */
case FMS_KILLST:
break;
default:
rval = -2;
}
break;
case FMI_RSTARTCMD: /* restart command */
}
break;
case FMI_DELAYCMD: /* delay command */
drv_usectohz(15000));
break;
case FMI_IDLECMD: /* idle command */
switch (old_mstate) {
case FMS_DELAY:
/* FALLTHROUGH */
case FMS_ON:
break;
case FMS_START:
break;
default:
rval = -2;
}
break;
default:
rval = -3;
}
if (rval) {
"fdc_motorsm: unit %d bad input %d or bad state %d",
#if 0
"fdc_motorsm: unit %d bad input %d or bad state %d\n",
}
#endif
} else
(CE_CONT, "fdc_motorsm unit %d: input %d, %d -> %d\n",
return (rval);
}
/*
* fdmotort
* is called from timeout() when a motor timer has expired.
*/
static void
{
int mval;
int newxstate = 0;
if (mval < 0)
return;
if (newxstate == -1) {
(CE_WARN,
"fdc_motort unit %d: motor ready but bad xstate",
}
}
}
/*
* DMA interrupt service routine
*
* Called by EISA dma interrupt service routine when buffer chaining
* is required.
*/
{
&csb->csb_dmacookie);
return (&csb->csb_dmacookie);
&csb->csb_dmacookie,
return (NULL);
}
csb->csb_dmacurrcookie = 0;
return (&csb->csb_dmacookie);
}
} else
return (NULL);
}
/*
* returns:
* 0 if all ok,
* ENXIO - diskette not in drive
* ETIMEDOUT - for immediate operations that timed out
* EBUSY - if stupid chip is locked busy???
* ENOEXEC - for timeout during sending cmds to chip
*
* to sleep: set sleep
* to check for disk changed: set change
*/
static int
{
struct ddi_dmae_req dmaereq;
int unit;
} else
csb->csb_retrys = 0;
csb->csb_ourtrys = 0;
if (csb->csb_dmahandle) {
/* ensure that entire format xfer is in one cookie */
/*
* The change from ddi_dma_buf/addr_setup() to
* ddi_dma_buf/addr_bind_handle() has already loaded
* the first DMA window and cookie.
*/
return (EINVAL);
}
}
/* XXX hack for fdformat */
/* fjp->fj_chars->fdc_transfer_rate == 500; */
}
"fdc_select: controller setup rejected "
"fdcntrl %p transfer rate %x step rate %x "
"head load time 40\n", (void*)fcp,
/* 3D drive requires 500 ms for speed change */
/*
* Return value ignored - fdcmotort deals with failure.
*/
}
}
/*
* If checking for disk_change is enabled
* (i.e. not seeking in fdresetchng),
* we sample the DSKCHG line to see if the diskette has wandered away.
*/
/*
* If the diskette is still gone... so are we, adios!
*/
return (EBUSY);
}
return (ENXIO);
}
/*
* delay to ensure that new diskette is up to speed
*/
/*
* Return value ignored - fdcmotort deals with failure.
*/
}
/*
* gather some statistics
*/
case FO_RDDAT:
break;
case FO_WRDAT:
break;
case FO_RECAL:
break;
case FO_FRMT:
break;
default:
break;
}
csb->csb_cmdstat = 0;
if (csb->csb_dmahandle) {
/*
* setup for dma buffer chaining regardless of bus capability
*/
"dip %p, dmachan %x\n",
}
/*
* If the operation has no results - then just return
*/
if (!csb->csb_nrslts) {
return (0);
}
/*
* this operation has no interrupt and an immediate result
* so wait for the results and stuff them into the csb
*/
return (EIO);
}
} else {
/*
* wait for completion interrupt
*/
}
}
/*
* See if there was an error detected, if so, fdrecover()
* will check it out and say what to do.
*
* Don't do this, though, if this was the Sense Drive Status
* or the Dump Registers command.
*/
/* if it can restarted OK, then do so, else return error */
return (EIO);
}
/* ASSUMES that cmd is still intact in csb */
/*
* first DMA cookie of current window
*/
&csb->csb_dmacookie,
return (EIO);
}
csb->csb_dmacurrcookie = 0;
}
goto retry;
}
/* things went ok */
return (0);
}
/*
* fdcheckdisk
* called by fdc_exec to check if the disk is still there - do a seek
* then see if DSKCHG line went away; if so, diskette is in; else
* it's (still) out.
*/
int
{
int newcyl; /* where to seek for reset of DSKCHG */
int rval;
enum fxstate save_xstate;
else
newcyl = 1;
/*
* wait for motor to get up to speed,
* and let motor_timer issue seek cmd
*/
else {
/*
* motor is up to speed; issue seek cmd now
*/
/*
*/
}
}
/*
* wait for completion interrupt
* XXX This should be backed up with a watchdog timer!
*/
}
/*
* if disk change still asserted, no diskette in drive!
*/
}
return (rval);
}
static int
{
int residual;
int unit;
char *failure;
}
case FXS_RCAL: /* recalibrate */
case FXS_SEEK: /* seek */
case FXS_RESET: /* cntlr reset */
return (0);
break;
case FXS_RDID: /* read ID */
/* FALLTHROUGH */
case FXS_DOIT: /* original operation */
case FXS_DOWT: /* waiting on operation */
&residual) != DDI_SUCCESS)
"fdc_recover: dmae getcnt failed, "
"dip %p dmachan %x residual %x\n",
residual);
(CE_NOTE,
"fd unit %d: %s error: "
"dma count=0x%lx residual=0x%x",
}
/*
* with a secondary retry counter
*/
(CE_NOTE,
return (0);
} else
/*
* as 1 primary retry effort
*/
csb->csb_ourtrys = 0;
/*
* device is open so keep trying and
* gather statistics on errors
*/
/*
* if we have not run out of retries, return 0
*/
if (csb->csb_opflags &
(CSB_OFDMARD | CSB_OFDMAWT)) {
(CE_WARN,
"fd unit %d: %s error: "
"st0=0x%x st1=0x%x st2=0x%x",
0x1f].cmdname,
}
return (0);
}
failure = "crc error";
failure = "bad format";
failure = "timeout";
else
failure = "failed";
} else {
(CE_NOTE, "fd unit %d: %s failed (%x %x %x)",
}
break;
default:
"fd unit %d: %s failed: st0=0x%x st1=0x%x st2=0x%x",
break;
}
return (1);
}
/* Autovector Interrupt Entry Point */
/* ARGSUSED */
static uint_t
{
int drive;
int newstate;
int pendstate;
int rval = DDI_DMA_DONE;
int state;
int maxspin = 10;
/*
* Wait for the RQM bit to be set, or until we've tested it
* a bunch of times (which may imply this isn't our interrupt).
*/
/* Small pause in between reading the status port */
drv_usecwait(10);
/* Reread the status port */
}
(CE_CONT, "fdc_intr unit %d: xstate=%d MSR=0x%x\n",
/*
* If there is an operation outstanding AND the controller is ready
* to receive a command or send us the result of a command (OR if the
* controller is ready to accept a new command), AND if
* someone has been waiting for a command to finish AND (if no unit
* is BUSY OR if the unit that we're waiting for is BUSY (i.e. it's in
* the middle of a seek/recalibrate)) then this interrupt is for us.
*/
/*
* Remove one of the conditions for entering this code.
* The state_machine will release the c_lock if it
* calls untimeout()
*/
/* restore waiting flag */
return (DDI_INTR_CLAIMED);
}
if (fcp->c_intrstat)
/*
* cookies: process next one
*/
((csb->csb_dmacurrcookie <
/*
* DMA cookie: process next one
*/
if (++csb->csb_dmacurrcookie <
csb->csb_dmacookiecnt) {
&csb->csb_dmacookie);
} else if (++csb->csb_dmacurrwin <
csb->csb_dmawincnt) {
&csb->csb_dmacookie,
&csb->csb_dmacookiecnt) !=
DDI_SUCCESS) {
"fdc_intr: "
"dma getwin failed\n");
}
csb->csb_dmacurrcookie = 0;
}
"fdc_intr: dmae prog failed, "
"dip %p dmachannel %x\n",
/*
* status of last operation has disk
* address for continuation
*/
(void) fdc_statemach(fcp);
/*
* Ignored return. If failed, warning already
* posted. Returned state irrelevant.
*/
/* restore waiting flag */
goto fi_exit;
}
if (rval != DDI_DMA_DONE)
/*
* wake them
*/
}
else
/* restore waiting flag */
return (DDI_INTR_CLAIMED);
}
/*
* Ignored return - senser state already saved
*/
(CE_WARN, "fdc_intr unit %d: nobody sleeping 0x%x",
} else {
(CE_WARN, "fdc_intr: nobody sleeping on %d 0x%x",
}
/*
* This should probably be protected, but, what the
* heck...the cost isn't worth the accuracy for this
* statistic.
*/
if (fcp->c_intrstat)
return (DDI_INTR_UNCLAIMED);
}
/*
* fdwatch
* is called from timeout() when a floppy operation timer has expired.
*/
static void
{
/*
* fdc_intr got here first, ergo, no timeout condition..
*/
return;
}
"dip %p, dmachan %x\n",
(CE_WARN, "fdcwatch unit %d: xstate = %d",
drv_usecwait(50);
/*
* cntlr is still busy, so reset it
*/
(void) fdc_statemach(fcp);
/*
* Ignored return. If failed, warning already
* posted. Returned state irrelevant.
*/
} else {
}
} else {
(CE_WARN, "fdcwatch: not sleeping for unit %d",
}
if (fcp->c_intrstat)
}
static int
{
int backoff;
int unit;
switch (csb->csb_xstate) {
case FXS_START: /* start of operation */
if (time == 0)
/*
* wait for motor to get up to speed
*/
break;
}
/* FALLTHROUGH */
case FXS_MTRON: /* motor is at speed */
/* how did we get here ?? */
return (-1);
}
goto nxs_seek;
/* cntlr did not accept command bytes */
break;
}
break;
case FXS_RCAL: /* forced recalibrate is complete */
#if 0 /* #ifdef _VPIX */
/* WARNING: this code breaks SPARC compatibility */
csb->csb_status = 0;
goto nxs_cmpl;
}
#endif
/*
* Ignored return. If failed, warning was issued by fdc_docmd.
* fdc_results retrieves the controller/drive status
*/
/*
* Ignored return. If failed, warning was issued by fdc_result.
* Actual results checked below
*/
(CE_WARN, "fdc_statemach unit %d: recal result %x",
break;
}
goto nxs_cmpl;
}
goto nxs_cmpl;
goto nxs_doit;
/* FALLTHROUGH */
case FXS_DKCHGX: /* reset Disk-Change latch */
/*
* Ignored return. If command rejected, warnig already posted
* by fdc_docmd().
*/
break;
case FXS_RESTART: /* special restart of read/write operation */
if (time == 0)
return (-1);
}
else
/*
* Ignored return. If command rejected, warnig already posted
* by fdc_docmd().
*/
break;
case FXS_RESEEK: /* seek to backoff-cyl complete */
/*
* Ignored return. If failed, warning was issued by fdc_docmd.
* fdc_results retrieves the controller/drive status
*/
/*
* Ignored return. If failed, warning was issued by fdc_result.
* Actual results checked below
*/
goto nxs_cmpl;
/*
* Ignored return. If command rejected, warnig already posted
* by fdc_docmd().
*/
break;
case FXS_SEEK: /* seek complete */
#if 0 /* #ifdef _VPIX */
/* WARNING: this code breaks SPARC compatibility and */
/* rawioctls in fdformat */
csb->csb_status = 0;
goto nxs_cmpl;
}
#endif
/*
* Ignored return. If failed, warning was issued by fdc_docmd.
* fdc_results retrieves the controller/drive status
*/
/*
* Ignored return. If failed, warning was issued by fdc_result.
* Actual results checked below
*/
goto nxs_cmpl;
goto nxs_cmpl;
};
/* use motor_timer to delay for head settle */
/*
* Return value ignored - fdcmotort deals with failure.
*/
break;
case FXS_HDST: /* head settle */
goto nxs_cmpl;
goto nxs_doit;
break;
case FXS_RDID: /* read ID complete */
/*
* Ignored return. If failed, warning was issued by fdc_result.
* Actual results checked below
*/
goto nxs_cmpl;
/* at wrong logical cylinder */
goto nxs_cmpl;
};
goto nxs_doit;
case FXS_DOIT: /* do original operation */
if (time == 0)
/* cntlr did not accept command bytes */
break;
}
break;
case FXS_DOWT: /* operation complete */
/*
* Ignored return. If failed, warning was issued by fdc_result.
* Actual results checked below.
*/
csb->csb_status =
} else {
}
if (csb->csb_status)
/* remove watchdog timer if armed and not already triggered */
}
break;
case FXS_KILL: /* quiesce cntlr by reset */
drv_usectohz(2000000));
break;
case FXS_RESET: /* int from reset */
}
}
break;
default:
return (-1);
}
(CE_CONT, "fdc_statemach unit %d: %d -> %d\n",
return (csb->csb_xstate);
}
/*
* routine to program a command into the floppy disk controller.
*/
int
{
int ntries;
(CE_CONT, "fdc_docmd: %x %x %x %x %x %x %x %x %x\n",
do {
do {
== MS_RQM)
break;
else
drv_usecwait(1);
} while (--ntries);
if (ntries == 0) {
(CE_WARN, "fdc_docmd: ctlr not ready"));
return (-1);
}
} while (--count);
return (0);
}
/*
* Routine to return controller/drive status information.
* The diskette-controller data-register is read the
* requested number of times and the results are placed in
* consecutive memory locations starting at the passed
* address.
*/
int
{
int ntries;
int laxative = 7;
do {
do {
break;
else
drv_usecwait(10);
} while (--ntries);
if (!ntries) {
(CE_WARN, "fdc_result: ctlr not ready"));
return (-2);
}
/*
* The PRM suggests waiting for 14.5 us.
* Adding a bit more to cover the case of bad calibration
* of drv_usecwait().
*/
drv_usecwait(16);
} while (--rcount);
(CE_WARN, "fdc_result: ctlr still busy"));
/*
* try to complete Result phase by purging
* result bytes queued for reading
*/
do {
/*
* Result phase is complete
* but did we get the results corresponding to
* the command we think we executed?
*/
return (-1);
}
break;
else
drv_usecwait(10);
} while (--ntries);
(CE_WARN,
"fdc_result: ctlr still busy and not ready"));
return (-3);
}
}
return (0);
}
/*
* Function: get_unit()
*
* Assumptions: ioaddr is either 0x3f0 or 0x370
*/
static int
{
int ioaddr;
return (DDI_FAILURE);
switch (ioaddr) {
case 0x3f0:
*cntrl_num = 0;
break;
case 0x370:
*cntrl_num = 1;
break;
default:
return (DDI_FAILURE);
}
return (DDI_SUCCESS);
}
static int
{
int status = DDI_FAILURE;
struct {
int bustype;
int base;
int size;
} *reglist;
return (DDI_FAILURE);
}
for (i = 0; i < nregs; i++) {
break;
}
}
if (status == DDI_SUCCESS) {
/*
* Some BIOS's (ASUS is one) don't include first
* two IO ports in the floppy controller resources.
*/
/*
* It would be nice to update the regs property as well
* so device pathname contains 3f0 instead of 3f2, but
* updating the regs now won't have this effect as that
* component of the device pathname has already been
* constructed by the ISA nexus driver.
*
* reglist[i].base -= 2;
* reglist[i].size += 2;
* dev = makedevice(ddi_driver_major(dip), 0);
* ddi_prop_update_int_array(dev, dip, "reg",
* (int *)reglist, reglen / sizeof (int));
*/
}
}
return (status);
}