version 1.24.2.9, 1998/06/19 21:57:44 |
version 1.24.2.10, 1998/08/13 14:27:50 |
|
|
#include "wd.h" |
#include "wd.h" |
#include "atapibus.h" |
#include "atapibus.h" |
|
|
#define WDCDELAY 100 |
#define WDCDELAY 100 /* 100 microseconds */ |
#define WDCNDELAY 100000 /* so 10s for a controller state change */ |
#define WDCNDELAY_RST (WDC_RESET_WAIT * 1000 / WDCDELAY) |
#define WDCNDELAY_RST 310000 /* 31s for reset complete (specs) */ |
|
#if 0 |
#if 0 |
/* If you enable this, it will report any delays more than 100us * N long. */ |
/* If you enable this, it will report any delays more than WDCDELAY * N long. */ |
#define WDCNDELAY_DEBUG 50 |
#define WDCNDELAY_DEBUG 50 |
#endif |
#endif |
|
|
Line 136 int wdc_nxfer = 0; |
|
Line 135 int wdc_nxfer = 0; |
|
* Quick test to see if a controller with at last one attached drive |
* Quick test to see if a controller with at last one attached drive |
* is there. Doesn't wait for reset completion here, as it may take |
* is there. Doesn't wait for reset completion here, as it may take |
* up to 31 seconds, so we just test that at last one device asserts |
* up to 31 seconds, so we just test that at last one device asserts |
* busy after the reset. It's really unlikely that we'll find another device |
* busy after the reset, or for ATAPI signature. |
* that use ports adresses ranges separated by 0x200 and respond in the same |
* It's really unlikely that we'll find another device that use ports adresses |
* way. |
* ranges separated by 0x200 and respond in the same way. |
* Returns a bit for each possible drive found (0x01 for drive 0, |
* Returns a bit for each possible drive found (0x01 for drive 0, |
* 0x02 for drive 1). |
* 0x02 for drive 1). |
*/ |
*/ |
|
|
wdcprobe(chp) |
wdcprobe(chp) |
const struct channel_softc *chp; |
const struct channel_softc *chp; |
{ |
{ |
u_int8_t st0, st1; |
u_int8_t st0, st1, sc, sn, cl, ch; |
u_int8_t ret_value = 0x03; |
u_int8_t ret_value = 0x03; |
|
|
/* |
/* |
|
|
* Some controllers seems to put all 0 in the registers while SRST |
* Some controllers seems to put all 0 in the registers while SRST |
* is asserted. So we have to test BSY after SRST has been |
* is asserted. So we have to test BSY after SRST has been |
* deasserted. This assume that the drives will not reset within |
* deasserted. This assume that the drives will not reset within |
* 10-15ms. |
* 10-15ms. |
|
* If we don't see BSY asserted, the device may have reset very |
|
* quickly, so we test for ATA or ATAPI signature in registers. |
*/ |
*/ |
|
|
bus_space_write_1(chp->cmd_iot, chp->cmd_ioh, wd_sdh, |
bus_space_write_1(chp->cmd_iot, chp->cmd_ioh, wd_sdh, |
|
|
WDCDEBUG_PRINT(("%s:%d: after reset, st0=0x%x st1=0x%x\n", |
WDCDEBUG_PRINT(("%s:%d: after reset, st0=0x%x st1=0x%x\n", |
chp->wdc ? chp->wdc->sc_dev.dv_xname : "wdcprobe", chp->channel, |
chp->wdc ? chp->wdc->sc_dev.dv_xname : "wdcprobe", chp->channel, |
st0, st1), DEBUG_PROBE); |
st0, st1), DEBUG_PROBE); |
/* We should now have at last one device with BSY set */ |
/* |
if ((st0 & WDCS_BSY) == 0) |
* If drive 0 has BSY set, we can't say anything about device 1. |
|
* Else, look at registers signature for device 0 and look at |
|
* device 1. |
|
*/ |
|
if ((st0 & WDCS_BSY) != 0) |
|
return ret_value; |
|
|
|
/* test registers signature for device 0 */ |
|
bus_space_write_1(chp->cmd_iot, chp->cmd_ioh, wd_sdh, |
|
WDSD_IBM); |
|
sc = bus_space_read_1(chp->cmd_iot, chp->cmd_ioh, wd_seccnt); |
|
sn = bus_space_read_1(chp->cmd_iot, chp->cmd_ioh, wd_sector); |
|
cl = bus_space_read_1(chp->cmd_iot, chp->cmd_ioh, wd_cyl_lo); |
|
ch = bus_space_read_1(chp->cmd_iot, chp->cmd_ioh, wd_cyl_hi); |
|
WDCDEBUG_PRINT(("%s:%d: after reset, drive 0 sc=0x%x sn=0x%x cl=0x%x " |
|
"ch=0x%x\n", chp->wdc ? chp->wdc->sc_dev.dv_xname : "wdcprobe", |
|
chp->channel, sc, sn, cl, ch), DEBUG_PROBE); |
|
if (sc != 0x01 || sn != 0x01 || |
|
((cl != 0x00 || ch != 0x00) && /* ATA sig */ |
|
(cl != 0x14 || ch != 0xeb))) /* ATAPI sig */ |
ret_value &= ~0x01; |
ret_value &= ~0x01; |
if ((st1 & WDCS_BSY) == 0) |
/* Now look at device 1 */ |
ret_value &= ~0x02; |
if ((st1 & WDCS_BSY) == 0) { |
|
/* look at registers */ |
|
bus_space_write_1(chp->cmd_iot, chp->cmd_ioh, wd_sdh, |
|
WDSD_IBM | 0x10); |
|
sc = bus_space_read_1(chp->cmd_iot, chp->cmd_ioh, wd_seccnt); |
|
sn = bus_space_read_1(chp->cmd_iot, chp->cmd_ioh, wd_sector); |
|
cl = bus_space_read_1(chp->cmd_iot, chp->cmd_ioh, wd_cyl_lo); |
|
ch = bus_space_read_1(chp->cmd_iot, chp->cmd_ioh, wd_cyl_hi); |
|
WDCDEBUG_PRINT(("%s:%d: after reset, drive 1 sc=0x%x sn=0x%x " |
|
"cl=0x%x ch=0x%x\n", |
|
chp->wdc ? chp->wdc->sc_dev.dv_xname : "wdcprobe", |
|
chp->channel, sc, sn, cl, ch), DEBUG_PROBE); |
|
if (sc != 0x01 || sn != 0x01 || |
|
((cl != 0x00 || ch != 0x00) && /* ATA sig */ |
|
(cl != 0x14 || ch != 0xeb))) /* ATAPI sig */ |
|
ret_value &= ~0x02; |
|
} |
return (ret_value); |
return (ret_value); |
} |
} |
|
|
Line 252 __wdc_init_controller(chp) |
|
Line 288 __wdc_init_controller(chp) |
|
bus_space_write_1(chp->cmd_iot, chp->cmd_ioh, wd_sdh, |
bus_space_write_1(chp->cmd_iot, chp->cmd_ioh, wd_sdh, |
WDSD_IBM | (i << 4)); |
WDSD_IBM | (i << 4)); |
delay(1); |
delay(1); |
if (wait_for_unbusy(chp) != 0) |
if (wait_for_unbusy(chp, 1000) != 0) |
continue; |
continue; |
/* Test ATAPI signature */ |
/* Test ATAPI signature */ |
if (bus_space_read_1(chp->cmd_iot, chp->cmd_ioh, wd_cyl_lo) |
if (bus_space_read_1(chp->cmd_iot, chp->cmd_ioh, wd_cyl_lo) |
Line 263 __wdc_init_controller(chp) |
|
Line 299 __wdc_init_controller(chp) |
|
} else { |
} else { |
/* Try an ATA command */ |
/* Try an ATA command */ |
wdccommandshort(chp, i, WDCC_RECAL); |
wdccommandshort(chp, i, WDCC_RECAL); |
if (wait_for_ready(chp) == 0) |
if (wait_for_ready(chp, 10000) == 0) |
chp->ch_drive[i].drive_flags |= DRIVE_ATA; |
chp->ch_drive[i].drive_flags |= DRIVE_ATA; |
} |
} |
} |
} |
Line 284 __wdc_init_controller(chp) |
|
Line 320 __wdc_init_controller(chp) |
|
wdccommandshort(chp, i, WDCC_DIAGNOSE); |
wdccommandshort(chp, i, WDCC_DIAGNOSE); |
|
|
/* Wait for command to complete. */ |
/* Wait for command to complete. */ |
if (wait_for_unbusy(chp) < 0) |
if (wait_for_unbusy(chp, 10000) < 0) |
return (-1); |
return (-1); |
|
|
return 1; |
return 1; |
|
|
if (chp->ch_drive[i].drive_flags & DRIVE) { |
if (chp->ch_drive[i].drive_flags & DRIVE) { |
bus_space_write_1(chp->cmd_iot, chp->cmd_ioh, wd_sdh, |
bus_space_write_1(chp->cmd_iot, chp->cmd_ioh, wd_sdh, |
WDSD_IBM | (i << 4)); |
WDSD_IBM | (i << 4)); |
if (wait_for_unbusy(chp) < 0) |
if (wait_for_unbusy(chp, 10000) < 0) |
printf("%s:%d:%d: device busy\n", |
printf("%s:%d:%d: device busy\n", |
chp->wdc->sc_dev.dv_xname, chp->channel, i); |
chp->wdc->sc_dev.dv_xname, chp->channel, i); |
} |
} |
Line 507 wdcreset(chp, verb) |
|
Line 543 wdcreset(chp, verb) |
|
bus_space_write_1(chp->ctl_iot, chp->ctl_ioh, wd_aux_ctlr, |
bus_space_write_1(chp->ctl_iot, chp->ctl_ioh, wd_aux_ctlr, |
WDCTL_4BIT); |
WDCTL_4BIT); |
|
|
if ((st0 & WDCS_BSY) == 0 && (st1 & WDCS_BSY) == 0) { |
if (((chp->ch_drive[0].drive_flags & DRIVE_ATA) != 0 && |
|
(st0 & WDCS_BSY) == 0) || |
|
((chp->ch_drive[1].drive_flags & DRIVE_ATA) != 0 && |
|
(st1 & WDCS_BSY) == 0)) { |
if (verb) |
if (verb) |
printf("%s channel %d: device doesn't respond to " |
printf("%s channel %d: device doesn't respond to " |
"reset\n", chp->wdc->sc_dev.dv_xname, |
"reset\n", chp->wdc->sc_dev.dv_xname, |
Line 573 __wdcwait_reset(chp, drv_mask) |
|
Line 612 __wdcwait_reset(chp, drv_mask) |
|
|
|
/* |
/* |
* Wait for a drive to be !BSY, and have mask in its status register. |
* Wait for a drive to be !BSY, and have mask in its status register. |
* return -1 for a timeout |
* return -1 for a timeout after "timeout" ms. |
*/ |
*/ |
int |
int |
wdcwait(chp, mask) |
wdcwait(chp, mask, bits, timeout) |
struct channel_softc *chp; |
struct channel_softc *chp; |
int mask; |
int mask, bits, timeout; |
{ |
{ |
int timeout = 0; |
|
u_char status; |
u_char status; |
|
int time = 0; |
#ifdef WDCNDELAY_DEBUG |
#ifdef WDCNDELAY_DEBUG |
extern int cold; |
extern int cold; |
#endif |
#endif |
|
|
WDCDEBUG_PRINT(("wdcwait\n"), DEBUG_STATUS); |
WDCDEBUG_PRINT(("wdcwait\n"), DEBUG_STATUS); |
chp->ch_error = 0; |
chp->ch_error = 0; |
|
|
|
timeout = timeout * 1000 / WDCDELAY; /* delay uses microseconds */ |
|
|
for (;;) { |
for (;;) { |
chp->ch_status = status = |
chp->ch_status = status = |
bus_space_read_1(chp->cmd_iot, chp->cmd_ioh, wd_status); |
bus_space_read_1(chp->cmd_iot, chp->cmd_ioh, wd_status); |
if ((status & WDCS_BSY) == 0 && (status & mask) == mask) |
if ((status & WDCS_BSY) == 0 && (status & mask) == bits) |
break; |
break; |
if (++timeout > WDCNDELAY) { |
if (++time > timeout) { |
WDCDEBUG_PRINT(("wdcwait: timeout, status %x " |
WDCDEBUG_PRINT(("wdcwait: timeout, status %x " |
"error %x\n", status, |
"error %x\n", status, |
bus_space_read_1(chp->cmd_iot, chp->cmd_ioh, |
bus_space_read_1(chp->cmd_iot, chp->cmd_ioh, |
Line 609 wdcwait(chp, mask) |
|
Line 649 wdcwait(chp, mask) |
|
wd_error); |
wd_error); |
#ifdef WDCNDELAY_DEBUG |
#ifdef WDCNDELAY_DEBUG |
/* After autoconfig, there should be no long delays. */ |
/* After autoconfig, there should be no long delays. */ |
if (!cold && timeout > WDCNDELAY_DEBUG) { |
if (!cold && time > WDCNDELAY_DEBUG) { |
struct wdc_xfer *xfer = chp->ch_queue->sc_xfer.tqh_first; |
struct wdc_xfer *xfer = chp->ch_queue->sc_xfer.tqh_first; |
if (xfer == NULL) |
if (xfer == NULL) |
printf("%s channel %d: warning: busy-wait took %dus\n", |
printf("%s channel %d: warning: busy-wait took %dus\n", |
chp->wdc->sc_dev.dv_xname, chp->channel, |
chp->wdc->sc_dev.dv_xname, chp->channel, |
WDCDELAY * timeout); |
WDCDELAY * time); |
else |
else |
printf("%s:%d:%d: warning: busy-wait took %dus\n", |
printf("%s:%d:%d: warning: busy-wait took %dus\n", |
chp->wdc->sc_dev.dv_xname, xfer->channel, |
chp->wdc->sc_dev.dv_xname, xfer->channel, |
xfer->drive, |
xfer->drive, |
WDCDELAY * timeout); |
WDCDELAY * time); |
} |
} |
#endif |
#endif |
return 0; |
return 0; |
Line 797 wdc_exec_command(drvp, wdc_c) |
|
Line 837 wdc_exec_command(drvp, wdc_c) |
|
return WDC_TRY_AGAIN; |
return WDC_TRY_AGAIN; |
} |
} |
|
|
bzero(xfer, sizeof(struct wdc_xfer)); |
if (wdc_c->flags & AT_POLL) |
xfer->c_flags = C_INUSE; |
xfer->c_flags |= C_POLL; |
xfer->drive = drvp->drive; |
xfer->drive = drvp->drive; |
xfer->databuf = wdc_c->data; |
xfer->databuf = wdc_c->data; |
xfer->c_bcount = wdc_c->bcount; |
xfer->c_bcount = wdc_c->bcount; |
Line 839 __wdccommand_start(chp, xfer) |
|
Line 879 __wdccommand_start(chp, xfer) |
|
|
|
bus_space_write_1(chp->cmd_iot, chp->cmd_ioh, wd_sdh, |
bus_space_write_1(chp->cmd_iot, chp->cmd_ioh, wd_sdh, |
WDSD_IBM | (drive << 4)); |
WDSD_IBM | (drive << 4)); |
if (wdcwait(chp, wdc_c->r_st_bmask) != 0) { |
if (wdcwait(chp, wdc_c->r_st_bmask, wdc_c->r_st_bmask, |
|
wdc_c->timeout) != 0) { |
wdc_c->flags |= AT_TIMEOU; |
wdc_c->flags |= AT_TIMEOU; |
__wdccommand_done(chp, xfer); |
__wdccommand_done(chp, xfer); |
} |
} |
Line 847 __wdccommand_start(chp, xfer) |
|
Line 888 __wdccommand_start(chp, xfer) |
|
wdc_c->r_sector, wdc_c->r_count, wdc_c->r_precomp); |
wdc_c->r_sector, wdc_c->r_count, wdc_c->r_precomp); |
if ((wdc_c->flags & AT_POLL) == 0) { |
if ((wdc_c->flags & AT_POLL) == 0) { |
chp->ch_flags |= WDCF_IRQ_WAIT; /* wait for interrupt */ |
chp->ch_flags |= WDCF_IRQ_WAIT; /* wait for interrupt */ |
timeout(wdctimeout, chp, WAITTIME); |
timeout(wdctimeout, chp, wdc_c->timeout / 1000 * hz); |
return; |
return; |
} |
} |
/* |
/* |
Line 871 __wdccommand_intr(chp, xfer) |
|
Line 912 __wdccommand_intr(chp, xfer) |
|
char *data = wdc_c->data; |
char *data = wdc_c->data; |
|
|
WDCDEBUG_PRINT(("__wdccommand_intr\n"), DEBUG_INTR); |
WDCDEBUG_PRINT(("__wdccommand_intr\n"), DEBUG_INTR); |
if (wdcwait(chp, wdc_c->r_st_pmask)) { |
if (wdcwait(chp, wdc_c->r_st_pmask, wdc_c->r_st_pmask, |
|
wdc_c->timeout)) { |
wdc_c->flags |= AT_ERROR; |
wdc_c->flags |= AT_ERROR; |
__wdccommand_done(chp, xfer); |
__wdccommand_done(chp, xfer); |
return 1; |
return 1; |
Line 993 wdc_exec_xfer(chp, xfer) |
|
Line 1035 wdc_exec_xfer(chp, xfer) |
|
|
|
/* complete xfer setup */ |
/* complete xfer setup */ |
xfer->channel = chp->channel; |
xfer->channel = chp->channel; |
|
|
|
/* XXX if we are a polled cmd, and the list is not empty, clear it */ |
/* insert at the end of command list */ |
/* insert at the end of command list */ |
TAILQ_INSERT_TAIL(&chp->ch_queue->sc_xfer,xfer , c_xferchain); |
TAILQ_INSERT_TAIL(&chp->ch_queue->sc_xfer,xfer , c_xferchain); |
WDCDEBUG_PRINT(("wdcstart from wdc_exec_xfer, flags 0x%x\n", |
WDCDEBUG_PRINT(("wdcstart from wdc_exec_xfer, flags 0x%x\n", |
Line 1034 wdc_get_xfer(flags) |
|
Line 1078 wdc_get_xfer(flags) |
|
if ((xfer->c_flags & C_INUSE) != 0) |
if ((xfer->c_flags & C_INUSE) != 0) |
panic("wdc_get_xfer: xfer already in use\n"); |
panic("wdc_get_xfer: xfer already in use\n"); |
#endif |
#endif |
bzero(xfer,sizeof(struct wdc_xfer)); |
memset(xfer, 0, sizeof(struct wdc_xfer)); |
xfer->c_flags = C_INUSE; |
xfer->c_flags = C_INUSE; |
return xfer; |
return xfer; |
} |
} |