summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--sys/dev/isa/wdc.c1255
-rw-r--r--sys/dev/isa/wdlink.h13
2 files changed, 652 insertions, 616 deletions
diff --git a/sys/dev/isa/wdc.c b/sys/dev/isa/wdc.c
index 7349321522d..48094436200 100644
--- a/sys/dev/isa/wdc.c
+++ b/sys/dev/isa/wdc.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: wdc.c,v 1.18 1997/02/24 05:50:46 niklas Exp $ */
+/* $OpenBSD: wdc.c,v 1.19 1997/07/04 17:02:04 downsj Exp $ */
/* $NetBSD: wd.c,v 1.150 1996/05/12 23:54:03 mycroft Exp $ */
/*
@@ -66,7 +66,10 @@
#include <dev/isa/wdreg.h>
#include <dev/isa/wdlink.h>
+#include "atapibus.h"
+#if NATAPIBUS > 0
#include <dev/atapi/atapilink.h>
+#endif /* NATAPIBUS */
#define WAITTIME (10 * hz) /* time to wait for a completion */
/* this is a lot for hard drives, but not for cdroms */
@@ -97,23 +100,28 @@ struct cfdriver wdc_cd = {
NULL, "wdc", DV_DULL
};
+#if NWD > 0
int wdc_ata_intr __P((struct wdc_softc *,struct wdc_xfer *));
-void wdcstart __P((struct wdc_softc *));
void wdc_ata_start __P((struct wdc_softc *,struct wdc_xfer *));
+void wdc_ata_done __P((struct wdc_softc *, struct wdc_xfer *));
+__inline static void u_int16_to_string __P((u_int16_t *, char *, size_t));
+#endif /* NWD */
int wait_for_phase __P((struct wdc_softc *, int));
int wait_for_unphase __P((struct wdc_softc *, int));
-void wdc_atapi_start __P((struct wdc_softc *,struct wdc_xfer *));
+void wdcstart __P((struct wdc_softc *));
int wdcreset __P((struct wdc_softc *));
void wdcrestart __P((void *arg));
void wdcunwedge __P((struct wdc_softc *));
void wdctimeout __P((void *arg));
int wdccontrol __P((struct wd_link *));
-void wdc_ata_done __P((struct wdc_softc *, struct wdc_xfer *));
void wdc_free_xfer __P((struct wdc_xfer *));
void wdcerror __P((struct wdc_softc*, char *));
void wdcbit_bucket __P(( struct wdc_softc *, int));
+#if NATAPIBUS > 0
+void wdc_atapi_start __P((struct wdc_softc *,struct wdc_xfer *));
int wdc_atapi_intr __P((struct wdc_softc *, struct wdc_xfer *));
void wdc_atapi_done __P((struct wdc_softc *, struct wdc_xfer *));
+#endif /* NATAPIBUS */
#ifdef ATAPI_DEBUG
static int wdc_nxfer;
@@ -222,7 +230,9 @@ wdcattach(parent, self, aux)
{
struct wdc_softc *wdc = (void *)self;
struct isa_attach_args *ia = aux;
+#if NWD > 0
int drive;
+#endif /* NWD */
TAILQ_INIT(&wdc->sc_xfer);
wdc->sc_drq = ia->ia_drq;
@@ -237,6 +247,7 @@ wdcattach(parent, self, aux)
wdc_nxfer = 0;
#endif
+#if NATAPIBUS > 0
/*
* Attach an ATAPI bus, if configured.
*/
@@ -251,7 +262,8 @@ wdcattach(parent, self, aux)
wdc->ab_link->ctlr_link = &(wdc->ctlr_link);
wdc->ab_link->ctrl = self->dv_unit;
(void)config_found(self, (void *)wdc->ab_link, NULL);
-
+#endif /* NATAPIBUS */
+#if NWD > 0
/*
* Attach standard IDE/ESDI/etc. disks to the controller.
*/
@@ -281,13 +293,14 @@ wdcattach(parent, self, aux)
if (wdc->sc_drq != DRQUNK)
wdc->d_link[drive]->sc_mode = WDM_DMA;
else
-#endif
+#endif /* NISADMA */
wdc->d_link[drive]->sc_mode = 0;
(void)config_found(self, (void *)wdc->d_link[drive],
wdcprint);
}
}
+#endif /* NWD */
}
/*
@@ -331,17 +344,28 @@ wdcstart(wdc)
return;
}
wdc->sc_flags |= WDCF_ACTIVE;
+#if NATAPIBUS > 0 && NWD > 0
if (xfer->c_flags & C_ATAPI) {
#ifdef ATAPI_DEBUG_WDC
printf("wdcstart: atapi\n");
#endif
- wdc_atapi_start(wdc,xfer);
- } else {
- wdc_ata_start(wdc,xfer);
- }
+ wdc_atapi_start(wdc, xfer);
+ } else
+ wdc_ata_start(wdc, xfer);
+#else
+#if NATAPIBUS > 0
+#ifdef ATAPI_DEBUG_WDC
+ printf("wdcstart: atapi\n");
+#endif
+ wdc_atapi_start(wdc, xfer);
+#endif /* NATAPIBUS */
+#if NWD > 0
+ wdc_ata_start(wdc, xfer);
+#endif /* NWD */
+#endif /* NATAPIBUS && NWD */
}
-
+#if NWD > 0
void
wdc_ata_start(wdc, xfer)
struct wdc_softc *wdc;
@@ -568,172 +592,7 @@ wdc_ata_start(wdc, xfer)
}
int
-wait_for_phase(wdc, wphase)
- struct wdc_softc *wdc;
- int wphase;
-{
- bus_space_tag_t iot = wdc->sc_iot;
- bus_space_handle_t ioh = wdc->sc_ioh;
- int i, phase;
-
- for (i = 20000; i; i--) {
- phase = (bus_space_read_1(iot, ioh, wd_ireason) &
- (WDCI_CMD | WDCI_IN)) |
- (bus_space_read_1(iot, ioh, wd_status)
- & WDCS_DRQ);
- if (phase == wphase)
- break;
- delay(10);
- }
- return (phase);
-}
-
-int
-wait_for_unphase(wdc, wphase)
- struct wdc_softc *wdc;
- int wphase;
-{
- bus_space_tag_t iot = wdc->sc_iot;
- bus_space_handle_t ioh = wdc->sc_ioh;
- int i, phase;
-
- for (i = 20000; i; i--) {
- phase = (bus_space_read_1(iot, ioh, wd_ireason) &
- (WDCI_CMD | WDCI_IN)) |
- (bus_space_read_1(iot, ioh, wd_status)
- & WDCS_DRQ);
- if (phase != wphase)
- break;
- delay(10);
- }
- return (phase);
-}
-
-void
-wdc_atapi_start(wdc, xfer)
- struct wdc_softc *wdc;
- struct wdc_xfer *xfer;
-{
- bus_space_tag_t iot = wdc->sc_iot;
- bus_space_handle_t ioh = wdc->sc_ioh;
- struct atapi_command_packet *acp = xfer->atapi_cmd;
-
-#ifdef ATAPI_DEBUG_WDC
- printf("wdc_atapi_start, acp flags %lx\n",acp->flags);
-#endif
- if (wdc->sc_errors >= WDIORETRIES) {
- acp->status |= ERROR;
- acp->error = bus_space_read_1(iot, ioh, wd_error);
- wdc_atapi_done(wdc, xfer);
- return;
- }
- if (wait_for_unbusy(wdc) != 0) {
- if ((wdc->sc_status & WDCS_ERR) == 0) {
- printf("wdc_atapi_start: not ready, st = %02x\n",
- wdc->sc_status);
- acp->status = ERROR;
- return;
- }
- }
-
- if (wdccommand((struct wd_link*)xfer->d_link, ATAPI_PACKET_COMMAND,
- acp->drive, acp->data_size, 0, 0, 0) != 0) {
- printf("wdc_atapi_start: can't send atapi paket command\n");
- acp->status = ERROR;
- wdc->sc_flags |= WDCF_IRQ_WAIT;
- return;
- }
- if ((acp->flags & (ACAP_DRQ_INTR|ACAP_DRQ_ACCEL)) != ACAP_DRQ_INTR) {
- if (!(wdc->sc_flags & WDCF_BROKENPOLL)) {
- int phase = wait_for_phase(wdc, PHASE_CMDOUT);
-
- if (phase != PHASE_CMDOUT) {
- printf("wdc_atapi_start: timeout waiting "
- "PHASE_CMDOUT, got 0x%x\n", phase);
-
- /* NEC SUCKS. */
- wdc->sc_flags |= WDCF_BROKENPOLL;
- }
- } else
- DELAY(10); /* Simply pray for the data. */
-
- bus_space_write_raw_multi_2(iot, ioh, wd_data, acp->command,
- acp->command_size);
- }
- wdc->sc_flags |= WDCF_IRQ_WAIT;
-
-#ifdef ATAPI_DEBUG2
- printf("wdc_atapi_start: timeout\n");
-#endif
- timeout(wdctimeout, wdc, WAITTIME);
- return;
-}
-
-
-/*
- * Interrupt routine for the controller. Acknowledge the interrupt, check for
- * errors on the current operation, mark it done if necessary, and start the
- * next request. Also check for a partially done transfer, and continue with
- * the next chunk if so.
- */
-int
-wdcintr(arg)
- void *arg;
-{
- struct wdc_softc *wdc = arg;
- struct wdc_xfer *xfer;
-
- if ((wdc->sc_flags & WDCF_IRQ_WAIT) == 0) {
- bus_space_tag_t iot = wdc->sc_iot;
- bus_space_handle_t ioh = wdc->sc_ioh;
- u_char s;
-#ifdef ATAPI_DEBUG_WDC
- u_char e, i;
-#endif
- DELAY(100);
-
- /* Clear the pending interrupt and abort. */
- s = bus_space_read_1(iot, ioh, wd_status);
- if (s != (WDCS_DRDY|WDCS_DSC)) {
-#ifdef ATAPI_DEBUG_WDC
- e = bus_space_read_1(iot, ioh, wd_error);
- i = bus_space_read_1(iot, ioh, wd_seccnt);
-
- printf("wdcintr: inactive controller, "
- "punting st=%02x er=%02x irr=%02x\n", s, e, i);
-#else
- bus_space_read_1(iot, ioh, wd_error);
- bus_space_read_1(iot, ioh, wd_seccnt);
-#endif
-
- if (s & WDCS_DRQ) {
- int len = 256 * bus_space_read_1(iot, ioh,
- wd_cyl_hi) +
- bus_space_read_1(iot, ioh, wd_cyl_lo);
-#ifdef ATAPI_DEBUG_WDC
- printf("wdcintr: clearing up %d bytes\n", len);
-#endif
- wdcbit_bucket(wdc, len);
- }
- }
- return 0;
- }
-
- WDDEBUG_PRINT(("wdcintr\n"));
-
- wdc->sc_flags &= ~WDCF_IRQ_WAIT;
- xfer = wdc->sc_xfer.tqh_first;
- if (xfer->c_flags & C_ATAPI) {
- (void)wdc_atapi_intr(wdc, xfer);
- return 0;
- } else {
- return wdc_ata_intr(wdc, xfer);
- }
-}
-
-
-int
-wdc_ata_intr(wdc,xfer)
+wdc_ata_intr(wdc, xfer)
struct wdc_softc *wdc;
struct wdc_xfer *xfer;
{
@@ -851,245 +710,6 @@ restart:
return 1;
}
-int
-wdcreset(wdc)
- struct wdc_softc *wdc;
-{
- bus_space_tag_t iot = wdc->sc_iot;
- bus_space_handle_t ioh = wdc->sc_ioh;
-
- /* Reset the device. */
- bus_space_write_1(iot, ioh, wd_ctlr, WDCTL_RST|WDCTL_IDS);
- delay(1000);
- bus_space_write_1(iot, ioh, wd_ctlr, WDCTL_IDS);
- delay(1000);
- (void) bus_space_read_1(iot, ioh, wd_error);
- bus_space_write_1(iot, ioh, wd_ctlr, WDCTL_4BIT);
-
- if (wait_for_unbusy(wdc) < 0) {
- printf("%s: reset failed\n", wdc->sc_dev.dv_xname);
- return 1;
- }
-
- return 0;
-}
-
-void
-wdcrestart(arg)
- void *arg;
-{
- struct wdc_softc *wdc = arg;
- int s;
-
- s = splbio();
- wdcstart(wdc);
- splx(s);
-}
-
-/*
- * Unwedge the controller after an unexpected error. We do this by resetting
- * it, marking all drives for recalibration, and stalling the queue for a short
- * period to give the reset time to finish.
- * NOTE: We use a timeout here, so this routine must not be called during
- * autoconfig or dump.
- */
-void
-wdcunwedge(wdc)
- struct wdc_softc *wdc;
-{
- int unit;
-
-#ifdef ATAPI_DEBUG
- printf("wdcunwedge\n");
-#endif
-
- untimeout(wdctimeout, wdc);
- wdc->sc_flags &= ~WDCF_IRQ_WAIT;
- (void) wdcreset(wdc);
-
- /* Schedule recalibrate for all drives on this controller. */
- for (unit = 0; unit < 2; unit++) {
- if (!wdc->d_link[unit]) continue;
- if (wdc->d_link[unit]->sc_state > RECAL)
- wdc->d_link[unit]->sc_state = RECAL;
- }
-
- wdc->sc_flags |= WDCF_ERROR;
- ++wdc->sc_errors;
-
- /* Wake up in a little bit and restart the operation. */
- WDDEBUG_PRINT(("wdcrestart from wdcunwedge\n"));
- wdc->sc_flags &= ~WDCF_ACTIVE;
- timeout(wdcrestart, wdc, RECOVERYTIME);
-}
-
-int
-wdcwait(wdc, mask)
- struct wdc_softc *wdc;
- int mask;
-{
- bus_space_tag_t iot = wdc->sc_iot;
- bus_space_handle_t ioh = wdc->sc_ioh;
- int timeout = 0;
- u_char status;
-#ifdef WDCNDELAY_DEBUG
- extern int cold;
-#endif
-
- WDDEBUG_PRINT(("wdcwait\n"));
-
- for (;;) {
- wdc->sc_status = status = bus_space_read_1(iot, ioh,
- wd_status);
- /*
- * XXX
- * If a single slave ATAPI device is attached, it may
- * have released the bus. Select it and try again.
- */
- if (status == 0xff && wdc->sc_flags & WDCF_ONESLAVE) {
- bus_space_write_1(iot, ioh, wd_sdh,
- WDSD_IBM | 0x10);
- wdc->sc_status = status = bus_space_read_1(iot, ioh,
- wd_status);
- }
- if ((status & WDCS_BSY) == 0 && (status & mask) == mask)
- break;
- if (++timeout > WDCNDELAY) {
-#ifdef ATAPI_DEBUG2
- printf("wdcwait: timeout, status 0x%x\n", status);
-#endif
- return -1;
- }
- delay(WDCDELAY);
- }
- if (status & WDCS_ERR) {
- wdc->sc_error = bus_space_read_1(iot, ioh, wd_error);
- return WDCS_ERR;
- }
-#ifdef WDCNDELAY_DEBUG
- /* After autoconfig, there should be no long delays. */
- if (!cold && timeout > WDCNDELAY_DEBUG) {
- struct wdc_xfer *xfer = wdc->sc_xfer.tqh_first;
- if (xfer == NULL)
- printf("%s: warning: busy-wait took %dus\n",
- wdc->sc_dev.dv_xname, WDCDELAY * timeout);
- else
- printf("%s(%s): warning: busy-wait took %dus\n",
- wdc->sc_dev.dv_xname,
- ((struct device*)xfer->d_link->wd_softc)->dv_xname,
- WDCDELAY * timeout);
- }
-#endif
- return 0;
-}
-
-void
-wdctimeout(arg)
- void *arg;
-{
- struct wdc_softc *wdc = (struct wdc_softc *)arg;
- int s;
-
- WDDEBUG_PRINT(("wdctimeout\n"));
-
- s = splbio();
- if ((wdc->sc_flags & WDCF_IRQ_WAIT) != 0) {
- wdc->sc_flags &= ~WDCF_IRQ_WAIT;
- wdcerror(wdc, "lost interrupt");
- wdcunwedge(wdc);
- } else
- wdcerror(wdc, "missing untimeout");
- splx(s);
-}
-
-/*
- * Wait for the drive to become ready and send a command.
- * Return -1 if busy for too long or 0 otherwise.
- * Assumes interrupts are blocked.
- */
-int
-wdccommand(d_link, command, drive, cylin, head, sector, count)
- struct wd_link *d_link;
- int command;
- int drive, cylin, head, sector, count;
-{
- struct wdc_softc *wdc = (void*)d_link->wdc_softc;
- bus_space_tag_t iot = wdc->sc_iot;
- bus_space_handle_t ioh = wdc->sc_ioh;
- int stat;
-
- WDDEBUG_PRINT(("wdccommand drive %d\n", drive));
-
-#if defined(DIAGNOSTIC) && defined(WDCDEBUG)
- if ((wdc->sc_flags & WDCF_ACTIVE) == 0)
- printf("wdccommand: controler not active (drive %d)\n", drive);
-#endif
-
- /* Select drive, head, and addressing mode. */
- bus_space_write_1(iot, ioh, wd_sdh, WDSD_IBM | (drive << 4) | head);
-
- /* Wait for it to become ready to accept a command. */
- if (command == WDCC_IDP || d_link->type == BUS)
- stat = wait_for_unbusy(wdc);
- else
- stat = wdcwait(wdc, WDCS_DRDY);
-
- if (stat < 0) {
-#ifdef ATAPI_DEBUG
- printf("wdcommand: xfer failed (wait_for_unbusy) status %d\n",
- stat);
-#endif
- return -1;
- }
-
- /* Load parameters. */
- if (d_link->type == DRIVE && d_link->sc_lp->d_type == DTYPE_ST506)
- bus_space_write_1(iot, ioh, wd_precomp,
- d_link->sc_lp->d_precompcyl / 4);
- else
- bus_space_write_1(iot, ioh, wd_features, 0);
- bus_space_write_1(iot, ioh, wd_cyl_lo, cylin);
- bus_space_write_1(iot, ioh, wd_cyl_hi, cylin >> 8);
- bus_space_write_1(iot, ioh, wd_sector, sector);
- bus_space_write_1(iot, ioh, wd_seccnt, count);
-
- /* Send command. */
- bus_space_write_1(iot, ioh, wd_command, command);
-
- return 0;
-}
-
-/*
- * Simplified version of wdccommand().
- */
-int
-wdccommandshort(wdc, drive, command)
- struct wdc_softc *wdc;
- int drive;
- int command;
-{
- bus_space_tag_t iot = wdc->sc_iot;
- bus_space_handle_t ioh = wdc->sc_ioh;
-
- WDDEBUG_PRINT(("wdccommandshort\n"));
-
-#if defined(DIAGNOSTIC) && defined(WDCDEBUG)
- if ((wdc->sc_flags & WDCF_ACTIVE) == 0)
- printf("wdccommandshort: controller not active (drive %d)\n",
- drive);
-#endif
-
- /* Select drive. */
- bus_space_write_1(iot, ioh, wd_sdh, WDSD_IBM|(drive << 4));
-
- if (wdcwait(wdc, WDCS_DRDY) < 0)
- return -1;
-
- bus_space_write_1(iot, ioh, wd_command, command);
-
- return 0;
-}
-
void
wdc_ata_done(wdc, xfer)
struct wdc_softc *wdc;
@@ -1127,171 +747,6 @@ wdc_ata_done(wdc, xfer)
splx(s);
}
-void
-wdc_exec_xfer(d_link, xfer)
- struct wd_link *d_link;
- struct wdc_xfer *xfer;
-{
- struct wdc_softc *wdc = (struct wdc_softc *)d_link->wdc_softc;
- int s;
-
- WDDEBUG_PRINT(("wdc_exec_xfer\n"));
-
- s = splbio();
-
- /* insert at the end of command list */
- TAILQ_INSERT_TAIL(&wdc->sc_xfer,xfer , c_xferchain)
- WDDEBUG_PRINT(("wdcstart from wdc_exec_xfer, flags %d\n",
- wdc->sc_flags));
- wdcstart(wdc);
- splx(s);
-}
-
-struct wdc_xfer *
-wdc_get_xfer(c_link,flags)
- struct wdc_link *c_link;
- int flags;
-{
- struct wdc_xfer *xfer;
- int s;
-
- s = splbio();
- if ((xfer = xfer_free_list.lh_first) != NULL) {
- LIST_REMOVE(xfer, free_list);
- splx(s);
-#ifdef DIAGNOSTIC
- if ((xfer->c_flags & C_INUSE) != 0)
- panic("wdc_get_xfer: xfer already in use\n");
-#endif
- } else {
- splx(s);
-#ifdef ATAPI_DEBUG
- printf("wdc:making xfer %d\n",wdc_nxfer);
-#endif
- xfer = malloc(sizeof(*xfer), M_DEVBUF,
- ((flags & IDE_NOSLEEP) != 0 ? M_NOWAIT : M_WAITOK));
- if (xfer == NULL)
- return 0;
-
-#ifdef DIAGNOSTIC
- xfer->c_flags &= ~C_INUSE;
-#endif
-#ifdef ATAPI_DEBUG
- wdc_nxfer++;
-#endif
- }
-#ifdef DIAGNOSTIC
- if ((xfer->c_flags & C_INUSE) != 0)
- panic("wdc_get_xfer: xfer already in use\n");
-#endif
- bzero(xfer, sizeof(struct wdc_xfer));
- xfer->c_flags = C_INUSE;
- xfer->c_link = c_link;
- return xfer;
-}
-
-void
-wdc_free_xfer(xfer)
- struct wdc_xfer *xfer;
-{
- int s;
-
- s = splbio();
- xfer->c_flags &= ~C_INUSE;
- LIST_INSERT_HEAD(&xfer_free_list, xfer, free_list);
- splx(s);
-}
-
-/*
- * Implement operations needed before read/write.
- * Returns 0 if operation still in progress, 1 if completed.
- */
-int
-wdccontrol(d_link)
- struct wd_link *d_link;
-{
- struct wdc_softc *wdc = (void *)d_link->wdc_softc;
- bus_space_tag_t iot = wdc->sc_iot;
- bus_space_handle_t ioh = wdc->sc_ioh;
-
- WDDEBUG_PRINT(("wdccontrol\n"));
-
- switch (d_link->sc_state) {
- case RECAL: /* Set SDH, step rate, do recal. */
- if (wdccommandshort(wdc, d_link->sc_drive, WDCC_RECAL) != 0) {
- wderror(d_link, NULL, "wdccontrol: recal failed (1)");
- goto bad;
- }
- d_link->sc_state = RECAL_WAIT;
- break;
-
- case RECAL_WAIT:
- if (wdc->sc_status & WDCS_ERR) {
- wderror(d_link, NULL, "wdccontrol: recal failed (2)");
- goto bad;
- }
- /* fall through */
-
- case GEOMETRY:
- if ((d_link->sc_params.wdp_capabilities & WD_CAP_LBA) != 0)
- goto multimode;
- if (wdsetctlr(d_link) != 0) {
- /* Already printed a message. */
- goto bad;
- }
- d_link->sc_state = GEOMETRY_WAIT;
- break;
-
- case GEOMETRY_WAIT:
- if (wdc->sc_status & WDCS_ERR) {
- wderror(d_link, NULL, "wdccontrol: geometry failed");
- goto bad;
- }
- /* fall through */
-
- case MULTIMODE:
- multimode:
- if (d_link->sc_mode != WDM_PIOMULTI)
- goto ready;
- bus_space_write_1(iot, ioh, wd_seccnt, d_link->sc_multiple);
- if (wdccommandshort(wdc, d_link->sc_drive,
- WDCC_SETMULTI) != 0) {
- wderror(d_link, NULL,
- "wdccontrol: setmulti failed (1)");
- goto bad;
- }
- d_link->sc_state = MULTIMODE_WAIT;
- break;
-
- case MULTIMODE_WAIT:
- if (wdc->sc_status & WDCS_ERR) {
- wderror(d_link, NULL,
- "wdccontrol: setmulti failed (2)");
- goto bad;
- }
- /* fall through */
-
- case READY:
- ready:
- wdc->sc_errors = 0;
- d_link->sc_state = READY;
- /*
- * The rest of the initialization can be done by normal means.
- */
- return 1;
-
- bad:
- wdcunwedge(wdc);
- return 0;
- }
-
- wdc->sc_flags |= WDCF_IRQ_WAIT;
- timeout(wdctimeout, wdc, WAITTIME);
- return 0;
-}
-
-__inline static void u_int16_to_string __P((u_int16_t *, char *, size_t));
-
/* decode IDE strings, stored as if the words are big-endian. */
__inline static void
u_int16_to_string(from, to, cnt)
@@ -1427,17 +882,196 @@ wdc_get_parms(d_link)
return 0;
}
+/*
+ * Implement operations needed before read/write.
+ * Returns 0 if operation still in progress, 1 if completed.
+ */
+int
+wdccontrol(d_link)
+ struct wd_link *d_link;
+{
+ struct wdc_softc *wdc = (void *)d_link->wdc_softc;
+ bus_space_tag_t iot = wdc->sc_iot;
+ bus_space_handle_t ioh = wdc->sc_ioh;
+
+ WDDEBUG_PRINT(("wdccontrol\n"));
+
+ switch (d_link->sc_state) {
+ case RECAL: /* Set SDH, step rate, do recal. */
+ if (wdccommandshort(wdc, d_link->sc_drive, WDCC_RECAL) != 0) {
+ wderror(d_link, NULL, "wdccontrol: recal failed (1)");
+ goto bad;
+ }
+ d_link->sc_state = RECAL_WAIT;
+ break;
+
+ case RECAL_WAIT:
+ if (wdc->sc_status & WDCS_ERR) {
+ wderror(d_link, NULL, "wdccontrol: recal failed (2)");
+ goto bad;
+ }
+ /* fall through */
+
+ case GEOMETRY:
+ if ((d_link->sc_params.wdp_capabilities & WD_CAP_LBA) != 0)
+ goto multimode;
+ if (wdsetctlr(d_link) != 0) {
+ /* Already printed a message. */
+ goto bad;
+ }
+ d_link->sc_state = GEOMETRY_WAIT;
+ break;
+
+ case GEOMETRY_WAIT:
+ if (wdc->sc_status & WDCS_ERR) {
+ wderror(d_link, NULL, "wdccontrol: geometry failed");
+ goto bad;
+ }
+ /* fall through */
+
+ case MULTIMODE:
+ multimode:
+ if (d_link->sc_mode != WDM_PIOMULTI)
+ goto ready;
+ bus_space_write_1(iot, ioh, wd_seccnt, d_link->sc_multiple);
+ if (wdccommandshort(wdc, d_link->sc_drive,
+ WDCC_SETMULTI) != 0) {
+ wderror(d_link, NULL,
+ "wdccontrol: setmulti failed (1)");
+ goto bad;
+ }
+ d_link->sc_state = MULTIMODE_WAIT;
+ break;
+
+ case MULTIMODE_WAIT:
+ if (wdc->sc_status & WDCS_ERR) {
+ wderror(d_link, NULL,
+ "wdccontrol: setmulti failed (2)");
+ goto bad;
+ }
+ /* fall through */
+
+ case READY:
+ ready:
+ wdc->sc_errors = 0;
+ d_link->sc_state = READY;
+ /*
+ * The rest of the initialization can be done by normal means.
+ */
+ return 1;
+
+ bad:
+ wdcunwedge(wdc);
+ return 0;
+ }
+
+ wdc->sc_flags |= WDCF_IRQ_WAIT;
+ timeout(wdctimeout, wdc, WAITTIME);
+ return 0;
+}
+#endif /* NWD */
+
+int
+wait_for_phase(wdc, wphase)
+ struct wdc_softc *wdc;
+ int wphase;
+{
+ bus_space_tag_t iot = wdc->sc_iot;
+ bus_space_handle_t ioh = wdc->sc_ioh;
+ int i, phase;
+
+ for (i = 20000; i; i--) {
+ phase = (bus_space_read_1(iot, ioh, wd_ireason) &
+ (WDCI_CMD | WDCI_IN)) |
+ (bus_space_read_1(iot, ioh, wd_status)
+ & WDCS_DRQ);
+ if (phase == wphase)
+ break;
+ delay(10);
+ }
+ return (phase);
+}
+
+int
+wait_for_unphase(wdc, wphase)
+ struct wdc_softc *wdc;
+ int wphase;
+{
+ bus_space_tag_t iot = wdc->sc_iot;
+ bus_space_handle_t ioh = wdc->sc_ioh;
+ int i, phase;
+
+ for (i = 20000; i; i--) {
+ phase = (bus_space_read_1(iot, ioh, wd_ireason) &
+ (WDCI_CMD | WDCI_IN)) |
+ (bus_space_read_1(iot, ioh, wd_status)
+ & WDCS_DRQ);
+ if (phase != wphase)
+ break;
+ delay(10);
+ }
+ return (phase);
+}
+
+#if NATAPIBUS > 0
void
-wdcerror(wdc, msg)
+wdc_atapi_start(wdc, xfer)
struct wdc_softc *wdc;
- char *msg;
+ struct wdc_xfer *xfer;
{
- struct wdc_xfer *xfer = wdc->sc_xfer.tqh_first;
- if (xfer == NULL)
- printf("%s: %s\n", wdc->sc_dev.dv_xname, msg);
- else
- printf("%s(%s): %s\n", wdc->sc_dev.dv_xname,
- ((struct device*)xfer->d_link->wd_softc)->dv_xname, msg);
+ bus_space_tag_t iot = wdc->sc_iot;
+ bus_space_handle_t ioh = wdc->sc_ioh;
+ struct atapi_command_packet *acp = xfer->atapi_cmd;
+
+#ifdef ATAPI_DEBUG_WDC
+ printf("wdc_atapi_start, acp flags %lx\n",acp->flags);
+#endif
+ if (wdc->sc_errors >= WDIORETRIES) {
+ acp->status |= ERROR;
+ acp->error = bus_space_read_1(iot, ioh, wd_error);
+ wdc_atapi_done(wdc, xfer);
+ return;
+ }
+ if (wait_for_unbusy(wdc) != 0) {
+ if ((wdc->sc_status & WDCS_ERR) == 0) {
+ printf("wdc_atapi_start: not ready, st = %02x\n",
+ wdc->sc_status);
+ acp->status = ERROR;
+ return;
+ }
+ }
+
+ if (wdccommand((struct wd_link*)xfer->d_link, ATAPI_PACKET_COMMAND,
+ acp->drive, acp->data_size, 0, 0, 0) != 0) {
+ printf("wdc_atapi_start: can't send atapi paket command\n");
+ acp->status = ERROR;
+ wdc->sc_flags |= WDCF_IRQ_WAIT;
+ return;
+ }
+ if ((acp->flags & (ACAP_DRQ_INTR|ACAP_DRQ_ACCEL)) != ACAP_DRQ_INTR) {
+ if (!(wdc->sc_flags & WDCF_BROKENPOLL)) {
+ int phase = wait_for_phase(wdc, PHASE_CMDOUT);
+
+ if (phase != PHASE_CMDOUT) {
+ printf("wdc_atapi_start: timeout waiting "
+ "PHASE_CMDOUT, got 0x%x\n", phase);
+
+ /* NEC SUCKS. */
+ wdc->sc_flags |= WDCF_BROKENPOLL;
+ }
+ } else
+ DELAY(10); /* Simply pray for the data. */
+
+ bus_space_write_raw_multi_2(iot, ioh, wd_data, acp->command,
+ acp->command_size);
+ }
+ wdc->sc_flags |= WDCF_IRQ_WAIT;
+
+#ifdef ATAPI_DEBUG2
+ printf("wdc_atapi_start: timeout\n");
+#endif
+ timeout(wdctimeout, wdc, WAITTIME);
+ return;
}
int
@@ -1657,29 +1291,6 @@ wdc_atapi_send_command_packet(ab_link, acp)
}
}
-
-/*
- * the bit bucket
- */
-void
-wdcbit_bucket(wdc, size)
- struct wdc_softc *wdc;
- int size;
-{
- bus_space_tag_t iot = wdc->sc_iot;
- bus_space_handle_t ioh = wdc->sc_ioh;
- int i;
-
- for (i = 0 ; i < size / 2 ; i++) {
- u_int16_t null;
-
- bus_space_read_multi_2(iot, ioh, wd_data, &null, 1);
- }
-
- if (size % 2)
- bus_space_read_1(iot, ioh, wd_data);
-}
-
int
wdc_atapi_intr(wdc, xfer)
struct wdc_softc *wdc;
@@ -1859,3 +1470,423 @@ wdc_atapi_done(wdc, xfer)
splx(s);
}
+#endif /* NATAPIBUS */
+
+/*
+ * Interrupt routine for the controller. Acknowledge the interrupt, check for
+ * errors on the current operation, mark it done if necessary, and start the
+ * next request. Also check for a partially done transfer, and continue with
+ * the next chunk if so.
+ */
+int
+wdcintr(arg)
+ void *arg;
+{
+ struct wdc_softc *wdc = arg;
+ struct wdc_xfer *xfer;
+
+ if ((wdc->sc_flags & WDCF_IRQ_WAIT) == 0) {
+ bus_space_tag_t iot = wdc->sc_iot;
+ bus_space_handle_t ioh = wdc->sc_ioh;
+ u_char s;
+#ifdef ATAPI_DEBUG_WDC
+ u_char e, i;
+#endif
+ DELAY(100);
+
+ /* Clear the pending interrupt and abort. */
+ s = bus_space_read_1(iot, ioh, wd_status);
+ if (s != (WDCS_DRDY|WDCS_DSC)) {
+#ifdef ATAPI_DEBUG_WDC
+ e = bus_space_read_1(iot, ioh, wd_error);
+ i = bus_space_read_1(iot, ioh, wd_seccnt);
+
+ printf("wdcintr: inactive controller, "
+ "punting st=%02x er=%02x irr=%02x\n", s, e, i);
+#else
+ bus_space_read_1(iot, ioh, wd_error);
+ bus_space_read_1(iot, ioh, wd_seccnt);
+#endif
+
+ if (s & WDCS_DRQ) {
+ int len = 256 * bus_space_read_1(iot, ioh,
+ wd_cyl_hi) +
+ bus_space_read_1(iot, ioh, wd_cyl_lo);
+#ifdef ATAPI_DEBUG_WDC
+ printf("wdcintr: clearing up %d bytes\n", len);
+#endif
+ wdcbit_bucket(wdc, len);
+ }
+ }
+ return 0;
+ }
+
+ WDDEBUG_PRINT(("wdcintr\n"));
+
+ wdc->sc_flags &= ~WDCF_IRQ_WAIT;
+ xfer = wdc->sc_xfer.tqh_first;
+#if NATAPIBUS > 0 && NWD > 0
+ if (xfer->c_flags & C_ATAPI) {
+ (void)wdc_atapi_intr(wdc, xfer);
+ return 0;
+ } else
+ return wdc_ata_intr(wdc, xfer);
+#else
+#if NATAPIBUS > 0
+ (void)wdc_atapi_intr(wdc, xfer);
+ return 0;
+#endif /* NATAPIBUS */
+#if NWD > 0
+ return wdc_ata_intr(wdc, xfer);
+#endif /* NWD */
+#endif /* NATAPIBUS && NWD */
+}
+
+int
+wdcreset(wdc)
+ struct wdc_softc *wdc;
+{
+ bus_space_tag_t iot = wdc->sc_iot;
+ bus_space_handle_t ioh = wdc->sc_ioh;
+
+ /* Reset the device. */
+ bus_space_write_1(iot, ioh, wd_ctlr, WDCTL_RST|WDCTL_IDS);
+ delay(1000);
+ bus_space_write_1(iot, ioh, wd_ctlr, WDCTL_IDS);
+ delay(1000);
+ (void) bus_space_read_1(iot, ioh, wd_error);
+ bus_space_write_1(iot, ioh, wd_ctlr, WDCTL_4BIT);
+
+ if (wait_for_unbusy(wdc) < 0) {
+ printf("%s: reset failed\n", wdc->sc_dev.dv_xname);
+ return 1;
+ }
+
+ return 0;
+}
+
+void
+wdcrestart(arg)
+ void *arg;
+{
+ struct wdc_softc *wdc = arg;
+ int s;
+
+ s = splbio();
+ wdcstart(wdc);
+ splx(s);
+}
+
+/*
+ * Unwedge the controller after an unexpected error. We do this by resetting
+ * it, marking all drives for recalibration, and stalling the queue for a short
+ * period to give the reset time to finish.
+ * NOTE: We use a timeout here, so this routine must not be called during
+ * autoconfig or dump.
+ */
+void
+wdcunwedge(wdc)
+ struct wdc_softc *wdc;
+{
+ int unit;
+
+#ifdef ATAPI_DEBUG
+ printf("wdcunwedge\n");
+#endif
+
+ untimeout(wdctimeout, wdc);
+ wdc->sc_flags &= ~WDCF_IRQ_WAIT;
+ (void) wdcreset(wdc);
+
+ /* Schedule recalibrate for all drives on this controller. */
+ for (unit = 0; unit < 2; unit++) {
+ if (!wdc->d_link[unit]) continue;
+ if (wdc->d_link[unit]->sc_state > RECAL)
+ wdc->d_link[unit]->sc_state = RECAL;
+ }
+
+ wdc->sc_flags |= WDCF_ERROR;
+ ++wdc->sc_errors;
+
+ /* Wake up in a little bit and restart the operation. */
+ WDDEBUG_PRINT(("wdcrestart from wdcunwedge\n"));
+ wdc->sc_flags &= ~WDCF_ACTIVE;
+ timeout(wdcrestart, wdc, RECOVERYTIME);
+}
+
+int
+wdcwait(wdc, mask)
+ struct wdc_softc *wdc;
+ int mask;
+{
+ bus_space_tag_t iot = wdc->sc_iot;
+ bus_space_handle_t ioh = wdc->sc_ioh;
+ int timeout = 0;
+ u_char status;
+#ifdef WDCNDELAY_DEBUG
+ extern int cold;
+#endif
+
+ WDDEBUG_PRINT(("wdcwait\n"));
+
+ for (;;) {
+ wdc->sc_status = status = bus_space_read_1(iot, ioh,
+ wd_status);
+ /*
+ * XXX
+ * If a single slave ATAPI device is attached, it may
+ * have released the bus. Select it and try again.
+ */
+ if (status == 0xff && wdc->sc_flags & WDCF_ONESLAVE) {
+ bus_space_write_1(iot, ioh, wd_sdh,
+ WDSD_IBM | 0x10);
+ wdc->sc_status = status = bus_space_read_1(iot, ioh,
+ wd_status);
+ }
+ if ((status & WDCS_BSY) == 0 && (status & mask) == mask)
+ break;
+ if (++timeout > WDCNDELAY) {
+#ifdef ATAPI_DEBUG2
+ printf("wdcwait: timeout, status 0x%x\n", status);
+#endif
+ return -1;
+ }
+ delay(WDCDELAY);
+ }
+ if (status & WDCS_ERR) {
+ wdc->sc_error = bus_space_read_1(iot, ioh, wd_error);
+ return WDCS_ERR;
+ }
+#ifdef WDCNDELAY_DEBUG
+ /* After autoconfig, there should be no long delays. */
+ if (!cold && timeout > WDCNDELAY_DEBUG) {
+ struct wdc_xfer *xfer = wdc->sc_xfer.tqh_first;
+ if (xfer == NULL)
+ printf("%s: warning: busy-wait took %dus\n",
+ wdc->sc_dev.dv_xname, WDCDELAY * timeout);
+ else
+ printf("%s(%s): warning: busy-wait took %dus\n",
+ wdc->sc_dev.dv_xname,
+ ((struct device*)xfer->d_link->wd_softc)->dv_xname,
+ WDCDELAY * timeout);
+ }
+#endif
+ return 0;
+}
+
+void
+wdctimeout(arg)
+ void *arg;
+{
+ struct wdc_softc *wdc = (struct wdc_softc *)arg;
+ int s;
+
+ WDDEBUG_PRINT(("wdctimeout\n"));
+
+ s = splbio();
+ if ((wdc->sc_flags & WDCF_IRQ_WAIT) != 0) {
+ wdc->sc_flags &= ~WDCF_IRQ_WAIT;
+ wdcerror(wdc, "lost interrupt");
+ wdcunwedge(wdc);
+ } else
+ wdcerror(wdc, "missing untimeout");
+ splx(s);
+}
+
+/*
+ * Wait for the drive to become ready and send a command.
+ * Return -1 if busy for too long or 0 otherwise.
+ * Assumes interrupts are blocked.
+ */
+int
+wdccommand(d_link, command, drive, cylin, head, sector, count)
+ struct wd_link *d_link;
+ int command;
+ int drive, cylin, head, sector, count;
+{
+ struct wdc_softc *wdc = (void*)d_link->wdc_softc;
+ bus_space_tag_t iot = wdc->sc_iot;
+ bus_space_handle_t ioh = wdc->sc_ioh;
+ int stat;
+
+ WDDEBUG_PRINT(("wdccommand drive %d\n", drive));
+
+#if defined(DIAGNOSTIC) && defined(WDCDEBUG)
+ if ((wdc->sc_flags & WDCF_ACTIVE) == 0)
+ printf("wdccommand: controler not active (drive %d)\n", drive);
+#endif
+
+ /* Select drive, head, and addressing mode. */
+ bus_space_write_1(iot, ioh, wd_sdh, WDSD_IBM | (drive << 4) | head);
+
+ /* Wait for it to become ready to accept a command. */
+ if (command == WDCC_IDP || d_link->type == BUS)
+ stat = wait_for_unbusy(wdc);
+ else
+ stat = wdcwait(wdc, WDCS_DRDY);
+
+ if (stat < 0) {
+#ifdef ATAPI_DEBUG
+ printf("wdcommand: xfer failed (wait_for_unbusy) status %d\n",
+ stat);
+#endif
+ return -1;
+ }
+
+ /* Load parameters. */
+ if (d_link->type == DRIVE && d_link->sc_lp->d_type == DTYPE_ST506)
+ bus_space_write_1(iot, ioh, wd_precomp,
+ d_link->sc_lp->d_precompcyl / 4);
+ else
+ bus_space_write_1(iot, ioh, wd_features, 0);
+ bus_space_write_1(iot, ioh, wd_cyl_lo, cylin);
+ bus_space_write_1(iot, ioh, wd_cyl_hi, cylin >> 8);
+ bus_space_write_1(iot, ioh, wd_sector, sector);
+ bus_space_write_1(iot, ioh, wd_seccnt, count);
+
+ /* Send command. */
+ bus_space_write_1(iot, ioh, wd_command, command);
+
+ return 0;
+}
+
+/*
+ * Simplified version of wdccommand().
+ */
+int
+wdccommandshort(wdc, drive, command)
+ struct wdc_softc *wdc;
+ int drive;
+ int command;
+{
+ bus_space_tag_t iot = wdc->sc_iot;
+ bus_space_handle_t ioh = wdc->sc_ioh;
+
+ WDDEBUG_PRINT(("wdccommandshort\n"));
+
+#if defined(DIAGNOSTIC) && defined(WDCDEBUG)
+ if ((wdc->sc_flags & WDCF_ACTIVE) == 0)
+ printf("wdccommandshort: controller not active (drive %d)\n",
+ drive);
+#endif
+
+ /* Select drive. */
+ bus_space_write_1(iot, ioh, wd_sdh, WDSD_IBM|(drive << 4));
+
+ if (wdcwait(wdc, WDCS_DRDY) < 0)
+ return -1;
+
+ bus_space_write_1(iot, ioh, wd_command, command);
+
+ return 0;
+}
+
+void
+wdc_exec_xfer(d_link, xfer)
+ struct wd_link *d_link;
+ struct wdc_xfer *xfer;
+{
+ struct wdc_softc *wdc = (struct wdc_softc *)d_link->wdc_softc;
+ int s;
+
+ WDDEBUG_PRINT(("wdc_exec_xfer\n"));
+
+ s = splbio();
+
+ /* insert at the end of command list */
+ TAILQ_INSERT_TAIL(&wdc->sc_xfer,xfer , c_xferchain)
+ WDDEBUG_PRINT(("wdcstart from wdc_exec_xfer, flags %d\n",
+ wdc->sc_flags));
+ wdcstart(wdc);
+ splx(s);
+}
+
+struct wdc_xfer *
+wdc_get_xfer(c_link,flags)
+ struct wdc_link *c_link;
+ int flags;
+{
+ struct wdc_xfer *xfer;
+ int s;
+
+ s = splbio();
+ if ((xfer = xfer_free_list.lh_first) != NULL) {
+ LIST_REMOVE(xfer, free_list);
+ splx(s);
+#ifdef DIAGNOSTIC
+ if ((xfer->c_flags & C_INUSE) != 0)
+ panic("wdc_get_xfer: xfer already in use\n");
+#endif
+ } else {
+ splx(s);
+#ifdef ATAPI_DEBUG
+ printf("wdc:making xfer %d\n",wdc_nxfer);
+#endif
+ xfer = malloc(sizeof(*xfer), M_DEVBUF,
+ ((flags & IDE_NOSLEEP) != 0 ? M_NOWAIT : M_WAITOK));
+ if (xfer == NULL)
+ return 0;
+
+#ifdef DIAGNOSTIC
+ xfer->c_flags &= ~C_INUSE;
+#endif
+#ifdef ATAPI_DEBUG
+ wdc_nxfer++;
+#endif
+ }
+#ifdef DIAGNOSTIC
+ if ((xfer->c_flags & C_INUSE) != 0)
+ panic("wdc_get_xfer: xfer already in use\n");
+#endif
+ bzero(xfer, sizeof(struct wdc_xfer));
+ xfer->c_flags = C_INUSE;
+ xfer->c_link = c_link;
+ return xfer;
+}
+
+void
+wdc_free_xfer(xfer)
+ struct wdc_xfer *xfer;
+{
+ int s;
+
+ s = splbio();
+ xfer->c_flags &= ~C_INUSE;
+ LIST_INSERT_HEAD(&xfer_free_list, xfer, free_list);
+ splx(s);
+}
+
+void
+wdcerror(wdc, msg)
+ struct wdc_softc *wdc;
+ char *msg;
+{
+ struct wdc_xfer *xfer = wdc->sc_xfer.tqh_first;
+ if (xfer == NULL)
+ printf("%s: %s\n", wdc->sc_dev.dv_xname, msg);
+ else
+ printf("%s(%s): %s\n", wdc->sc_dev.dv_xname,
+ ((struct device*)xfer->d_link->wd_softc)->dv_xname, msg);
+}
+
+/*
+ * the bit bucket
+ */
+void
+wdcbit_bucket(wdc, size)
+ struct wdc_softc *wdc;
+ int size;
+{
+ bus_space_tag_t iot = wdc->sc_iot;
+ bus_space_handle_t ioh = wdc->sc_ioh;
+ int i;
+
+ for (i = 0 ; i < size / 2 ; i++) {
+ u_int16_t null;
+
+ bus_space_read_multi_2(iot, ioh, wd_data, &null, 1);
+ }
+
+ if (size % 2)
+ bus_space_read_1(iot, ioh, wd_data);
+}
diff --git a/sys/dev/isa/wdlink.h b/sys/dev/isa/wdlink.h
index 66ceb721b31..154902ca047 100644
--- a/sys/dev/isa/wdlink.h
+++ b/sys/dev/isa/wdlink.h
@@ -1,4 +1,4 @@
-/* $OpenBSD: wdlink.h,v 1.6 1996/11/29 22:55:10 niklas Exp $ */
+/* $OpenBSD: wdlink.h,v 1.7 1997/07/04 17:02:04 downsj Exp $ */
/*
* Copyright (c) 1994, 1995 Charles M. Hannum. All rights reserved.
@@ -37,6 +37,8 @@
/* #undef WDDEBUG */
/* #undef DIAGNOSTIC */
+#include "wd.h"
+
struct wdc_link {
int flags;
int openings;
@@ -134,13 +136,16 @@ struct wdc_xfer {
void wdc_exec_xfer __P((struct wd_link *, struct wdc_xfer *));
struct wdc_xfer *wdc_get_xfer __P((struct wdc_link *, int));
int wdc_get_parms __P((struct wd_link *));
+int wdccommandshort __P((struct wdc_softc *, int, int));
+int wdcwait __P((struct wdc_softc *, int));
+int wdccommand __P((struct wd_link *, int, int, int, int, int, int));
+
+#if NWD > 0
void wderror __P((struct wd_link* , struct buf *, char *));
int wdsetctlr __P((struct wd_link *));
void wdstart __P((void *));
void wddone __P((struct wd_link*, struct buf*));
-int wdccommand __P((struct wd_link *, int, int, int, int, int, int));
-int wdccommandshort __P((struct wdc_softc *, int, int));
-int wdcwait __P((struct wdc_softc *, int));
+#endif /* NWD */
/*
* ST506 spec says that if READY or SEEKCMPLT go off, then the read or write