Source-Changes-HG archive
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index][Old Index]
[src/trunk]: src/sys/dev/mca edc_cmd_wait(): it is absolutely necessary to wa...
details: https://anonhg.NetBSD.org/src/rev/3103fd183eac
branches: trunk
changeset: 524823:3103fd183eac
user: jdolecek <jdolecek%NetBSD.org@localhost>
date: Sun Mar 31 10:01:26 2002 +0000
description:
edc_cmd_wait(): it is absolutely necessary to wait for the BSR_CMD_INPROGRESS
flag to clear, even when the COMMAND COMPLETE interrupt already did happen,
otherwise we get ATTENTION ERROR for next command if it happens soon
enough; this fixes the reliability problems introduced by previous change
some other cleanup & simlify of edc_cmd_wait()/edc_run_cmd(), the 'secs'
is just a hint used in !poll case only
add some comments
move status_block[] back to edc_mca_softc, to save stack memory
make #ifdef DEBUG #ifdef EDC_DEBUG and g/c some obsolete debug stuff
make some EAGAINs EIOs
edc_intr(): wakeup the waiter for any command, not just READ/WRITE DATA
diffstat:
sys/dev/mca/edc_mca.c | 202 +++++++++++++++++++++----------------------------
1 files changed, 88 insertions(+), 114 deletions(-)
diffs (truncated from 401 to 300 lines):
diff -r af64926972e3 -r 3103fd183eac sys/dev/mca/edc_mca.c
--- a/sys/dev/mca/edc_mca.c Sun Mar 31 08:02:08 2002 +0000
+++ b/sys/dev/mca/edc_mca.c Sun Mar 31 10:01:26 2002 +0000
@@ -1,4 +1,4 @@
-/* $NetBSD: edc_mca.c,v 1.15 2001/12/31 22:09:27 thorpej Exp $ */
+/* $NetBSD: edc_mca.c,v 1.16 2002/03/31 10:01:26 jdolecek Exp $ */
/*
* Copyright (c) 2001 The NetBSD Foundation, Inc.
@@ -50,7 +50,7 @@
*/
#include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: edc_mca.c,v 1.15 2001/12/31 22:09:27 thorpej Exp $");
+__KERNEL_RCSID(0, "$NetBSD: edc_mca.c,v 1.16 2002/03/31 10:01:26 jdolecek Exp $");
#include "rnd.h"
@@ -106,8 +106,14 @@
* controller */
/* I/O results variables */
- volatile int sc_error;
+ volatile int sc_stat;
+#define STAT_START 0
+#define STAT_ERROR 1
+#define STAT_DONE 2
volatile int sc_resblk; /* residual block count */
+
+ /* CMD status block - only set & used in edc_intr() */
+ u_int16_t status_block[EDC_MAX_CMD_RES_LEN];
};
int edc_mca_probe __P((struct device *, struct cfdata *, void *));
@@ -121,7 +127,7 @@
static void edc_dump_status_block __P((struct edc_mca_softc *,
u_int16_t *, int));
static int edc_do_attn __P((struct edc_mca_softc *, int, int, int));
-static int edc_cmd_wait __P((struct edc_mca_softc *, int, int));
+static void edc_cmd_wait __P((struct edc_mca_softc *, int, int));
static void edcworker __P((void *));
static void edc_spawn_worker __P((void *));
@@ -283,7 +289,7 @@
}
/*
- * Since interrupts are disabled ATM, it's necessary
+ * Since interrupts are disabled, it's necessary
* to detect the interrupt request and call edc_intr()
* explicitly. See also edc_run_cmd().
*/
@@ -350,8 +356,7 @@
struct edc_mca_softc *sc = arg;
u_int8_t isr, intr_id;
u_int16_t sifr;
- int cmd=-1, devno, error=0;
- u_int16_t status_block[EDC_MAX_CMD_RES_LEN]; /* CMD status block */
+ int cmd=-1, devno;
/*
* Check if the interrupt was for us.
@@ -367,7 +372,7 @@
isr = bus_space_read_1(sc->sc_iot, sc->sc_ioh, ISR);
intr_id = isr & ISR_INTR_ID_MASK;
-#ifdef DEBUG
+#ifdef EDC_DEBUG
if (intr_id == 0 || intr_id == 2 || intr_id == 4) {
printf("%s: bogus interrupt id %d\n", sc->sc_dev.dv_xname,
(int) intr_id);
@@ -401,16 +406,20 @@
cmd = sifr & SIFR_CMD_MASK;
/* Read whole status block */
- memset(status_block, 0, sizeof(status_block)); /* zero first */
- status_block[0] = sifr;
+ sc->status_block[0] = sifr;
for(i=1; i < len; i++) {
while((bus_space_read_1(sc->sc_iot, sc->sc_ioh, BSR)
& BSR_SIFR_FULL) == 0)
- delay(1);
+ ;
- status_block[i] = le16toh(
+ sc->status_block[i] = le16toh(
bus_space_read_2(sc->sc_iot, sc->sc_ioh, SIFR));
}
+ /* zero out rest */
+ if (i < EDC_MAX_CMD_RES_LEN) {
+ memset(&sc->status_block[i], 0,
+ (EDC_MAX_CMD_RES_LEN-i)*sizeof(u_int16_t));
+ }
}
switch (intr_id) {
@@ -422,31 +431,45 @@
bus_space_write_1(sc->sc_iot, sc->sc_ioh, BCR,
BCR_INT_ENABLE|BCR_DMA_ENABLE);
break;
+
case ISR_COMPLETED:
case ISR_COMPLETED_WITH_ECC:
case ISR_COMPLETED_RETRIES:
case ISR_COMPLETED_WARNING:
- error = 0;
-
/*
* Copy device config data if appropriate. sc->sc_ed[]
* entry might be NULL during probe.
*/
if (cmd == CMD_GET_DEV_CONF && sc->sc_ed[devno]) {
- memcpy(sc->sc_ed[devno]->sense_data, status_block,
+ memcpy(sc->sc_ed[devno]->sense_data, sc->status_block,
sizeof(sc->sc_ed[devno]->sense_data));
}
+ sc->sc_stat = STAT_DONE;
break;
+
case ISR_RESET_COMPLETED:
case ISR_ABORT_COMPLETED:
/* nothing to do */
break;
+
+ case ISR_ATTN_ERROR:
+ /*
+ * Basically, this means driver bug or something seriously
+ * hosed. panic rather than extending the lossage.
+ * No status block available, so no further info.
+ */
+ panic("%s: dev %d: attention error",
+ sc->sc_dev.dv_xname,
+ devno);
+ /* NOTREACHED */
+ break;
+
default:
if ((sc->sc_flags & DASD_QUIET) == 0)
- edc_dump_status_block(sc, status_block, intr_id);
+ edc_dump_status_block(sc, sc->status_block, intr_id);
- error = EIO;
+ sc->sc_stat = STAT_ERROR;
break;
}
@@ -460,10 +483,9 @@
edc_do_attn(sc, ATN_END_INT, devno, intr_id);
/* If Read or Write Data, wakeup worker thread to finish it */
- if (intr_id != ISR_DATA_TRANSFER_RDY
- && (cmd == CMD_READ_DATA || cmd == CMD_WRITE_DATA)) {
- if ((sc->sc_error = error) == 0)
- sc->sc_resblk = status_block[SB_RESBLKCNT_IDX];
+ if (intr_id != ISR_DATA_TRANSFER_RDY) {
+ if (cmd == CMD_READ_DATA || cmd == CMD_WRITE_DATA)
+ sc->sc_resblk = sc->status_block[SB_RESBLKCNT_IDX];
wakeup_one(sc);
}
@@ -489,30 +511,30 @@
* a RESET COMPLETED interrupt.
*/
if (intr_id != ISR_RESET_COMPLETED) {
+#ifdef EDC_DEBUG
+ if (attn_type == ATN_CMD_REQ
+ && (bus_space_read_1(sc->sc_iot, sc->sc_ioh, BSR)
+ & BSR_INT_PENDING))
+ panic("%s: edc int pending", sc->sc_dev.dv_xname);
+#endif
+
for(tries=1; tries < EDC_ATTN_MAXTRIES; tries++) {
if ((bus_space_read_1(sc->sc_iot, sc->sc_ioh, BSR)
- & BSR_BUSY) == 0) {
-#ifdef DEBUG
- if ((bus_space_read_1(sc->sc_iot, sc->sc_ioh,
- BSR) & BSR_INT_PENDING) && intr_id)
- panic("foobar");
-#endif
+ & BSR_BUSY) == 0)
break;
- }
}
if (tries == EDC_ATTN_MAXTRIES) {
printf("%s: edc_do_attn: timeout waiting for attachment to become available\n",
sc->sc_ed[devno]->sc_dev.dv_xname);
- return (EAGAIN);
+ return (EIO);
}
}
/*
* 3. Write proper DEVICE NUMBER and Attention number to ATN.
*/
- bus_space_write_1(sc->sc_iot, sc->sc_ioh, ATN,
- attn_type | (devno << 5));
+ bus_space_write_1(sc->sc_iot, sc->sc_ioh, ATN, attn_type | (devno<<5));
/*
* 4. Enable interrupts via BCR.
@@ -527,59 +549,36 @@
* We use mono_time, since we don't need actual RTC, just time
* interval.
*/
-static int
+static void
edc_cmd_wait(sc, secs, poll)
struct edc_mca_softc *sc;
int secs, poll;
{
- int val, delayed;
+ int val;
if (!poll) {
- int error;
+ int s;
/* Not polling, can sleep. Sleep until we are awakened,
* but maximum secs seconds.
*/
- error = tsleep(sc, PRIBIO, "edcwcmd", secs * hz);
- if (error)
- goto err;
- return (0);
+ s = splbio();
+ if (sc->sc_stat != STAT_DONE)
+ (void) tsleep(sc, PRIBIO, "edcwcmd", secs * hz);
+ splx(s);
}
- /* Poll the controller until command finishes */
- delayed = 0;
- do {
- val = bus_space_read_1(sc->sc_iot,sc->sc_ioh, BSR);
- if ((val & BSR_CMD_INPROGRESS) == 0)
- break;
-
- if (val & BSR_INTR)
- break;
-
- delay(1);
-
- /*
- * This is not as accurate as checking mono_time, but
- * it works with hardclock interrupts disabled too.
- */
- delayed++;
- if (delayed == 1000000) {
- delayed = 0;
- secs--;
- }
- } while(secs > 0);
-
- if (secs == 0 &&
- bus_space_read_1(sc->sc_iot, sc->sc_ioh, BSR) & BSR_CMD_INPROGRESS){
- err:
- printf("%s: timed out waiting for cmd to finish\n",
- sc->sc_dev.dv_xname);
- return (EAGAIN);
+ /* Wait until the command is completely finished */
+ while((val = bus_space_read_1(sc->sc_iot, sc->sc_ioh, BSR))
+ & BSR_CMD_INPROGRESS) {
+ if (poll && (val & BSR_INTR))
+ edc_intr(sc);
}
-
- return (0);
}
-
+
+/*
+ * Command controller to execute specified command on a device.
+ */
int
edc_run_cmd(sc, cmd, devno, cmd_args, cmd_len, poll)
struct edc_mca_softc *sc;
@@ -591,10 +590,7 @@
int i, error, tries;
u_int16_t cmd0;
- if (bus_space_read_1(sc->sc_iot, sc->sc_ioh, BSR) & BSR_BUSY) {
- printf("%s: device busy?\n", sc->sc_dev.dv_xname);
- return (EAGAIN);
- }
+ sc->sc_stat = STAT_START;
/* Do Attention Request for Command Request. */
if ((error = edc_do_attn(sc, ATN_CMD_REQ, devno, 0)))
@@ -622,53 +618,32 @@
* Write word of CMD to the CIFR. This sets "Command
* Interface Register Full (CMD IN)" in BSR. Once the attachment
* detects it, it reads the word and clears CMD IN. This all should
- * be quite fast, so don't bother with sleeps for !poll case.
+ * be quite fast, so don't sleep in !poll case neither.
*/
for(i=0; i < cmd_len; i++) {
bus_space_write_2(sc->sc_iot, sc->sc_ioh, CIFR,
htole16(cmd_args[i]));
Home |
Main Index |
Thread Index |
Old Index