diff options
-rw-r--r-- | sys/dev/atapiscsi/atapiscsi.c | 910 | ||||
-rw-r--r-- | sys/dev/ic/wdcvar.h | 17 |
2 files changed, 559 insertions, 368 deletions
diff --git a/sys/dev/atapiscsi/atapiscsi.c b/sys/dev/atapiscsi/atapiscsi.c index 5938166e8ee..98d2816ce1e 100644 --- a/sys/dev/atapiscsi/atapiscsi.c +++ b/sys/dev/atapiscsi/atapiscsi.c @@ -1,4 +1,4 @@ -/* $OpenBSD: atapiscsi.c,v 1.19 1999/11/17 01:22:55 csapuntz Exp $ */ +/* $OpenBSD: atapiscsi.c,v 1.20 1999/12/11 10:15:02 csapuntz Exp $ */ /* * This code is derived from code with the copyright below. @@ -77,6 +77,7 @@ #define DEBUG_POLL 0x40 #define DEBUG_ERRORS 0x80 /* Debug error handling code */ +#define WDCDEBUG #ifdef WDCDEBUG int wdcdebug_atapi_mask = 0; #define WDCDEBUG_PRINT(args, level) \ @@ -89,18 +90,46 @@ int wdcdebug_atapi_mask = 0; /* 10 ms, this is used only before sending a cmd. */ #define ATAPI_DELAY 10 #define ATAPI_RESET_WAIT 2000 +#define ATAPI_CTRL_WAIT 4000 /* When polling, let the exponential backoff max out at 1 second's interval. */ #define ATAPI_POLL_MAXTIC (hz) void wdc_atapi_minphys __P((struct buf *bp)); void wdc_atapi_start __P((struct channel_softc *,struct wdc_xfer *)); -int wdc_atapi_intr __P((struct channel_softc *, struct wdc_xfer *, int)); + +void timer_handler __P((void *)); + +int wdc_atapi_real_start __P((struct channel_softc *, struct wdc_xfer *, + int)); +int wdc_atapi_real_start_2 __P((struct channel_softc *, struct wdc_xfer *, + int)); +int wdc_atapi_intr_command __P((struct channel_softc *, struct wdc_xfer *, + int)); + +int wdc_atapi_intr_data __P((struct channel_softc *, struct wdc_xfer *, + int)); +int wdc_atapi_intr_complete __P((struct channel_softc *, struct wdc_xfer *, + int)); + +int wdc_atapi_intr_for_us __P((struct channel_softc *, struct wdc_xfer *, + int)); + +int wdc_atapi_send_packet __P((struct channel_softc *, struct wdc_xfer *, + int)); +int wdc_atapi_dma_flags __P((struct wdc_xfer *)); + int wdc_atapi_ctrl __P((struct channel_softc *, struct wdc_xfer *, int)); -void wdc_atapi_done __P((struct channel_softc *, struct wdc_xfer *)); -void wdc_atapi_reset __P((struct channel_softc *, struct wdc_xfer *)); +int wdc_atapi_in_data_phase __P((struct wdc_xfer *, int, int)); + +int wdc_atapi_intr __P((struct channel_softc *, struct wdc_xfer *, int)); + +int wdc_atapi_done __P((struct channel_softc *, struct wdc_xfer *, int)); +int wdc_atapi_reset __P((struct channel_softc *, struct wdc_xfer *, int)); +int wdc_atapi_reset_2 __P((struct channel_softc *, struct wdc_xfer *, int)); int wdc_atapi_send_cmd __P((struct scsi_xfer *sc_xfer)); +int wdc_atapi_tape_done __P((struct channel_softc *, struct wdc_xfer *, int)); #define MAX_SIZE MAXPHYS struct atapiscsi_softc; @@ -113,6 +142,7 @@ int wdc_atapi_get_params __P((struct channel_softc *, u_int8_t, struct ataparams int atapi_dsc_wait __P((struct ata_drive_datas *, int)); int atapi_dsc_ready __P((void *)); +void atapi_dsc_check __P((void *)); int atapi_dsc_semiready __P((void *)); int atapi_poll_wait __P((int (*) __P((void *)), void *, int, int, char *)); void atapi_to_scsi_sense __P((struct scsi_xfer *, u_int8_t)); @@ -359,11 +389,7 @@ wdc_atapi_send_cmd(sc_xfer) struct ata_drive_datas *drvp = &chp->ch_drive[drive]; struct wdc_xfer *xfer; int flags = sc_xfer->flags; - int s, ret, saved_datalen; - char saved_len_bytes[3]; - -restart: - saved_datalen = 0; + int s, ret; WDCDEBUG_PRINT(("wdc_atapi_send_cmd %s:%d:%d\n", chp->wdc->sc_dev.dv_xname, chp->channel, drive), DEBUG_XFERS); @@ -392,31 +418,32 @@ restart: if (drvp->atapi_cap & ACAP_DSC) { WDCDEBUG_PRINT(("about to send cmd %x ", sc_xfer->cmd->opcode), DEBUG_DSC); - xfer->c_flags |= C_NEEDDONE; switch (sc_xfer->cmd->opcode) { case READ: case WRITE: + xfer->c_flags |= C_MEDIA_ACCESS; + /* If we are not in buffer availability mode, we limit the first request to 0 bytes, which gets us into buffer availability mode without holding the bus. */ if (!(drvp->drive_flags & DRIVE_DSCBA)) { - saved_datalen = sc_xfer->datalen; - xfer->c_flags &= ~C_NEEDDONE; - sc_xfer->datalen = xfer->c_bcount = 0; - bcopy( - ((struct scsi_rw_tape *)sc_xfer->cmd)->len, - saved_len_bytes, 3); + xfer->c_bcount = 0; + xfer->transfer_len = + _3btol(((struct scsi_rw_tape *) + sc_xfer->cmd)->len); _lto3b(0, ((struct scsi_rw_tape *) sc_xfer->cmd)->len); + xfer->c_done = wdc_atapi_tape_done; WDCDEBUG_PRINT( ("R/W in completion mode, do 0 blocks\n"), DEBUG_DSC); } else WDCDEBUG_PRINT(("R/W %d blocks %d bytes\n", - _3btol(((struct scsi_rw_tape *) - sc_xfer->cmd)->len), sc_xfer->datalen), + _3btol(((struct scsi_rw_tape *) + sc_xfer->cmd)->len), + sc_xfer->datalen), DEBUG_DSC); /* DSC will change to buffer availability mode. @@ -427,15 +454,13 @@ restart: case LOAD: case REWIND: case SPACE: + case WRITE_FILEMARKS: #if 0 case LOCATE: case READ_POSITION: - case WRITE_FILEMARK: #endif - /* DSC will change to command completion mode. - We can reflect this early. */ - drvp->drive_flags &= ~DRIVE_DSCBA; - WDCDEBUG_PRINT(("clear DCSBA\n"), DEBUG_DSC); + + xfer->c_flags |= C_MEDIA_ACCESS; break; default: @@ -445,19 +470,10 @@ restart: wdc_exec_xfer(chp, xfer); #ifdef DIAGNOSTIC - if (((sc_xfer->flags & SCSI_POLL) != 0 || - (drvp->atapi_cap & ACAP_DSC) != 0) && + if ((xfer->c_flags & C_POLL) != 0 && (sc_xfer->flags & ITSDONE) == 0) panic("wdc_atapi_send_cmd: polled command not done"); #endif - if ((drvp->atapi_cap & ACAP_DSC) && saved_datalen != 0) { - sc_xfer->datalen = saved_datalen; - bcopy(saved_len_bytes, - ((struct scsi_rw_tape *)sc_xfer->cmd)->len, 3); - sc_xfer->flags &= ~ITSDONE; - splx(s); - goto restart; - } ret = (sc_xfer->flags & ITSDONE) ? COMPLETE : SUCCESSFULLY_QUEUED; splx(s); return (ret); @@ -500,14 +516,215 @@ atapi_to_scsi_sense(xfer, flags) } +enum atapi_context { + ctxt_process = 0, + ctxt_timer = 1, + ctxt_interrupt = 2 +}; + +int wdc_atapi_the_machine __P((struct channel_softc *, struct wdc_xfer *, + enum atapi_context)); + +int wdc_atapi_the_poll_machine __P((struct channel_softc *, struct wdc_xfer *)); + void wdc_atapi_start(chp, xfer) struct channel_softc *chp; struct wdc_xfer *xfer; { + xfer->next = wdc_atapi_real_start; + + wdc_atapi_the_machine(chp, xfer, ctxt_process); +} + + +void +timer_handler(arg) + void *arg; +{ + struct wdc_xfer *xfer = arg; + struct channel_softc *chp = xfer->chp; + int s; + + xfer->c_flags &= ~C_POLL_MACHINE; + + /* There is a race here between us and the interrupt */ + s = splbio(); + wdc_atapi_the_machine(chp, xfer, ctxt_timer); + splx(s); +} + + +int +wdc_atapi_intr(chp, xfer, irq) + struct channel_softc *chp; + struct wdc_xfer *xfer; + int irq; +{ + /* XXX we should consider an alternate signaling regime here */ + if (xfer->c_flags & C_TIMEOU) { + xfer->c_flags &= ~C_TIMEOU; + return (wdc_atapi_the_machine(chp, xfer, ctxt_timer)); + } + + return (wdc_atapi_the_machine(chp, xfer, ctxt_interrupt)); +} + +#define CONTINUE_POLL 0 +#define GOTO_NEXT 1 +#define DONE 2 + +int +wdc_atapi_the_poll_machine(chp, xfer) + struct channel_softc *chp; + struct wdc_xfer *xfer; +{ + int idx = 0, ret; + int current_timeout; + + xfer->timeout = -1; + + while (1) { + idx++; + + xfer->timeout = -1; + xfer->delay = 0; + xfer->expect_irq = 0; + + ret = (xfer->next)(chp, xfer, (current_timeout * 1000 <= idx)); + + if (xfer->timeout != -1) { + current_timeout = xfer->timeout; + idx = 0; + } + + if (xfer->delay != 0) { + while (xfer->delay--) + delay(1000); + } + + switch (ret) { + case GOTO_NEXT: + break; + + case CONTINUE_POLL: + DELAY(1); + + break; + + case DONE: + wdc_free_xfer(chp, xfer); + wdcstart(chp); + return (0); + } + } +} + +int +wdc_atapi_the_machine(chp, xfer, ctxt) + struct channel_softc *chp; + struct wdc_xfer *xfer; + enum atapi_context ctxt; +{ + int idx = 0, ret; + int claim_irq = 0; + u_int64_t now = (u_int64_t)time.tv_sec * 1000 + time.tv_usec / 1000; + int timeout_delay = hz / 10; + + if (xfer->c_flags & C_POLL) { + if (ctxt != ctxt_process) + return (0); + + wdc_atapi_the_poll_machine(chp, xfer); + return (0); + } + + do_op: + idx++; + + xfer->timeout = -1; + xfer->claim_irq = 0; + xfer->delay = 0; + + ret = (xfer->next)(chp, xfer, now >= xfer->endtime); + + if (xfer->timeout != -1) { + now = (u_int64_t)time.tv_sec * 1000 + + time.tv_usec / 1000; + + xfer->endtime = (u_int64_t)xfer->timeout + now; + + } + + if (xfer->claim_irq) claim_irq = xfer->claim_irq; + + if (xfer->delay) timeout_delay = max(xfer->delay * hz / 1000, 1); + + switch (ret) { + case GOTO_NEXT: + if (xfer->expect_irq) { + chp->ch_flags |= WDCF_IRQ_WAIT; + xfer->expect_irq = 0; + timeout(wdctimeout, chp, + (xfer->endtime - now) * hz / 1000); + + return (claim_irq); + } + + if (xfer->delay) + break; + + goto do_op; + + case CONTINUE_POLL: + if (xfer->delay) break; + if (idx >= 50) break; + + DELAY(1); + goto do_op; + + case DONE: + if (xfer->c_flags & C_POLL_MACHINE) + untimeout (timer_handler, xfer); + + wdc_free_xfer(chp, xfer); + wdcstart(chp); + + return (claim_irq); + } + + timeout(timer_handler, xfer, timeout_delay); + xfer->c_flags |= C_POLL_MACHINE; + return (claim_irq); +} + + +void wdc_atapi_update_status __P((struct channel_softc *)); + +void +wdc_atapi_update_status(chp) + struct channel_softc *chp; +{ + chp->ch_status = CHP_READ_REG(chp, wdr_status); + + if (chp->ch_status == 0xff && (chp->ch_flags & WDCF_ONESLAVE)) { + CHP_WRITE_REG(chp, wdr_sdh, WDSD_IBM | 0x10); + + chp->ch_status = CHP_READ_REG(chp, wdr_status); + } + + if ((chp->ch_status & (WDCS_BSY | WDCS_ERR)) == WDCS_ERR) + chp->ch_error = CHP_READ_REG(chp, wdr_error); +} + +int +wdc_atapi_real_start(chp, xfer, timeout) + struct channel_softc *chp; + struct wdc_xfer *xfer; + int timeout; +{ struct scsi_xfer *sc_xfer = xfer->cmd; struct ata_drive_datas *drvp = &chp->ch_drive[xfer->drive]; - struct atapiscsi_softc *as = sc_xfer->sc_link->adapter_softc; WDCDEBUG_PRINT(("wdc_atapi_start %s:%d:%d, scsi flags 0x%x\n", chp->wdc->sc_dev.dv_xname, chp->channel, drvp->drive, @@ -515,17 +732,44 @@ wdc_atapi_start(chp, xfer) /* Adjust C_DMA, it may have changed if we are requesting sense */ if (!(xfer->c_flags & C_POLL) && (drvp->drive_flags & (DRIVE_DMA | DRIVE_UDMA)) && - (sc_xfer->datalen > 0 || (xfer->c_flags & C_SENSE))) + (xfer->c_bcount > 0 || (xfer->c_flags & C_SENSE))) xfer->c_flags |= C_DMA; else xfer->c_flags &= ~C_DMA; - if (wdc_select_drive(chp, xfer->drive, ATAPI_DELAY) < 0) { + + CHP_WRITE_REG(chp, wdr_sdh, WDSD_IBM | (xfer->drive << 4)); + + DELAY(1); + + xfer->next = wdc_atapi_real_start_2; + xfer->timeout = ATAPI_DELAY; + + return (GOTO_NEXT); +} + + +int +wdc_atapi_real_start_2(chp, xfer, timeout) + struct channel_softc *chp; + struct wdc_xfer *xfer; + int timeout; +{ + struct scsi_xfer *sc_xfer = xfer->cmd; + struct ata_drive_datas *drvp = &chp->ch_drive[xfer->drive]; + + if (timeout) { printf("wdc_atapi_start: not ready, st = %02x\n", chp->ch_status); + sc_xfer->error = XS_TIMEOUT; - wdc_atapi_reset(chp, xfer); - return; + xfer->next = wdc_atapi_reset; + return (GOTO_NEXT); + } else { + wdc_atapi_update_status(chp); + + if (chp->ch_status & WDCS_BSY) + return (CONTINUE_POLL); } /* Do control operations specially. */ @@ -536,22 +780,26 @@ wdc_atapi_start(chp, xfer) xfer->drive, drvp->state); panic("wdc_atapi_start: bad state"); } - wdc_atapi_ctrl(chp, xfer, 0); - return; - } - - if (drvp->atapi_cap & ACAP_DSC) { - if (atapi_dsc_wait(drvp, sc_xfer->timeout)) { - sc_xfer->error = XS_TIMEOUT; - wdc_atapi_reset(chp, xfer); - return; - } - WDCDEBUG_PRINT(("wdc_atapi_start %s:%d:%d, DSC asserted\n", - chp->wdc->sc_dev.dv_xname, chp->channel, drvp->drive), - DEBUG_DSC); - drvp->drive_flags &= ~DRIVE_DSCWAIT; + + xfer->next = wdc_atapi_ctrl; + return (GOTO_NEXT); } + xfer->next = wdc_atapi_send_packet; + return (GOTO_NEXT); +} + + +int +wdc_atapi_send_packet(chp, xfer, timeout) + struct channel_softc *chp; + struct wdc_xfer *xfer; + int timeout; +{ + struct scsi_xfer *sc_xfer = xfer->cmd; + struct ata_drive_datas *drvp = &chp->ch_drive[xfer->drive]; + struct atapiscsi_softc *as = sc_xfer->sc_link->adapter_softc; + /* * Even with WDCS_ERR, the device should accept a command packet * Limit length to what can be stuffed into the cylinder register @@ -569,69 +817,37 @@ wdc_atapi_start(chp, xfer) as->protocol_phase = as_cmdout; as->retries = 0; - /* - * If there is no interrupt for CMD input, busy-wait for it (done in - * the interrupt routine. If it is a polled command, call the interrupt - * routine until command is done. - */ - if (((drvp->atapi_cap & ATAPI_CFG_DRQ_MASK) != ATAPI_CFG_IRQ_DRQ) || - (sc_xfer->flags & SCSI_POLL) || (drvp->atapi_cap & ACAP_DSC)) { - /* Wait for at last 400ns for status bit to be valid */ - DELAY(1); - wdc_atapi_intr(chp, xfer, 0); - } else { - chp->ch_flags |= WDCF_IRQ_WAIT; - timeout(wdctimeout, chp, hz); - return; - } + DELAY(1); - if ((sc_xfer->flags & SCSI_POLL) || (drvp->atapi_cap & ACAP_DSC)) { - while ((sc_xfer->flags & ITSDONE) == 0) { - if (drvp->atapi_cap & ACAP_DSC) { - if (atapi_poll_wait( - (drvp->drive_flags & DRIVE_DSCWAIT) ? - atapi_dsc_ready : atapi_dsc_semiready, - drvp, sc_xfer->timeout, PZERO + PCATCH, - "atapist")) { - sc_xfer->error = XS_TIMEOUT; - wdc_atapi_reset(chp, xfer); - return; - } - } else - /* Wait for at last 400ns for status bit to - be valid */ - DELAY(1); + xfer->next = wdc_atapi_intr_for_us; + xfer->timeout = sc_xfer->timeout; - wdc_atapi_intr(chp, xfer, 0); - } + if ((drvp->atapi_cap & ATAPI_CFG_DRQ_MASK) == ATAPI_CFG_IRQ_DRQ) { + /* We expect an IRQ to tell us of the next state */ + xfer->expect_irq = 1; } + return (GOTO_NEXT); } int -atapi_dsc_semiready(arg) - void *arg; +wdc_atapi_dma_flags(xfer) + struct wdc_xfer *xfer; { - struct ata_drive_datas *drvp = arg; - struct channel_softc *chp = drvp->chnl_softc; + struct scsi_xfer *sc_xfer = xfer->cmd; + int dma_flags; - /* We should really wait_for_unbusy here too before - switching drives. */ - CHP_WRITE_REG(chp, wdr_sdh, WDSD_IBM | (drvp->drive << 4)); + dma_flags = ((sc_xfer->flags & SCSI_DATA_IN) || + (xfer->c_flags & C_SENSE)) ? WDC_DMA_READ : 0; + dma_flags |= (xfer->c_flags & C_POLL) ? WDC_DMA_POLL : 0; - return (wait_for_unbusy(chp, 0) == 0); + return (dma_flags); } - -int wdc_atapi_intr_command __P((struct channel_softc *, struct wdc_xfer *, int)); -int wdc_atapi_intr_data __P((struct channel_softc *, struct wdc_xfer *, int)); -int wdc_atapi_intr_complete __P((struct channel_softc *, struct wdc_xfer *, int)); - - int -wdc_atapi_intr_command(chp, xfer, dma_flags) +wdc_atapi_intr_command(chp, xfer, timeout) struct channel_softc *chp; struct wdc_xfer *xfer; - int dma_flags; + int timeout; { struct scsi_xfer *sc_xfer = xfer->cmd; struct ata_drive_datas *drvp = &chp->ch_drive[xfer->drive]; @@ -640,6 +856,10 @@ wdc_atapi_intr_command(chp, xfer, dma_flags) u_int8_t cmd[16]; struct scsi_sense *cmd_reqsense; int cmdlen = (drvp->atapi_cap & ACAP_LEN) ? 16 : 12; + int dma_flags; + + xfer->claim_irq = 1; + dma_flags = wdc_atapi_dma_flags(xfer); bzero(cmd, sizeof(cmd)); @@ -660,8 +880,9 @@ wdc_atapi_intr_command(chp, xfer, dma_flags) chp->channel, xfer->drive, xfer->databuf, xfer->c_bcount, dma_flags) != 0) { sc_xfer->error = XS_DRIVER_STUFFUP; - wdc_atapi_done(chp, xfer); - return (1); + + xfer->next = wdc_atapi_done; + return (GOTO_NEXT); } } @@ -676,57 +897,38 @@ wdc_atapi_intr_command(chp, xfer, dma_flags) dma_flags); } - if ((sc_xfer->flags & SCSI_POLL) == 0 && - (drvp->atapi_cap & ACAP_DSC) == 0) { - chp->ch_flags |= WDCF_IRQ_WAIT; - timeout(wdctimeout, chp, sc_xfer->timeout * hz / 1000); - } + xfer->expect_irq = 1; /* If we read/write to a tape we will get into buffer availability mode. */ if (drvp->atapi_cap & ACAP_DSC) { - if (!(drvp->drive_flags & DRIVE_DSCBA) && - (sc_xfer->cmd->opcode == READ || - sc_xfer->cmd->opcode == WRITE)) { + if ((sc_xfer->cmd->opcode == READ || + sc_xfer->cmd->opcode == WRITE)) { drvp->drive_flags |= DRIVE_DSCBA; WDCDEBUG_PRINT(("set DSCBA\n"), DEBUG_DSC); + } else if ((xfer->c_flags & C_MEDIA_ACCESS) && + (drvp->drive_flags & DRIVE_DSCBA)) { + /* Clause 3.2.4 of QIC-157 D. + + Any media access command other than read or + write will switch DSC back to completion + mode */ + drvp->drive_flags &= ~DRIVE_DSCBA; + WDCDEBUG_PRINT(("clear DCSBA\n"), DEBUG_DSC); } - if (sc_xfer->cmd->opcode == READ) - drvp->drive_flags |= DRIVE_DSCWAIT; } - return (1); + return (GOTO_NEXT); } + int -wdc_atapi_intr_data(chp, xfer, dma_flags) - struct channel_softc *chp; +wdc_atapi_in_data_phase(xfer, len, ire) struct wdc_xfer *xfer; - int dma_flags; - + int len, ire; { struct scsi_xfer *sc_xfer = xfer->cmd; - struct ata_drive_datas *drvp = &chp->ch_drive[xfer->drive]; - struct atapiscsi_softc *as = sc_xfer->sc_link->adapter_softc; - int len, ire; - char *message = 0; - - len = (CHP_READ_REG(chp, wdr_cyl_hi) << 8) | - CHP_READ_REG(chp, wdr_cyl_lo); - ire = CHP_READ_REG(chp, wdr_ireason); - - WDCDEBUG_PRINT(("wdc_atapi_intr: c_bcount %d len %d st 0x%x err 0x%x " - "ire 0x%x :", xfer->c_bcount, - len, chp->ch_status, chp->ch_error, ire), DEBUG_INTR); - - /* Possibility to explore; what if we get an interrupt - during DMA ? */ - if ((xfer->c_flags & C_DMA) != 0) { - if ((xfer->c_flags & C_TIMEOU) == 0) - message = "unexpected interrupt during DMA mode"; - - goto unexpected_state; - } + char *message; if (ire & WDCI_CMD) { message = "unexpectedly in command phase"; @@ -738,7 +940,7 @@ wdc_atapi_intr_data(chp, xfer, dma_flags) message = "data phase where none expected"; goto unexpected_state; } - + /* Make sure polarities match */ if (((ire & WDCI_IN) == WDCI_IN) == ((sc_xfer->flags & SCSI_DATA_OUT) == SCSI_DATA_OUT)) { @@ -757,7 +959,36 @@ wdc_atapi_intr_data(chp, xfer, dma_flags) goto unexpected_state; } + + return (1); + + unexpected_state: + + return (0); +} + +int +wdc_atapi_intr_data(chp, xfer, timeout) + struct channel_softc *chp; + struct wdc_xfer *xfer; + int timeout; +{ + struct scsi_xfer *sc_xfer = xfer->cmd; + struct ata_drive_datas *drvp = &chp->ch_drive[xfer->drive]; + int len, ire; + + len = (CHP_READ_REG(chp, wdr_cyl_hi) << 8) | + CHP_READ_REG(chp, wdr_cyl_lo); + ire = CHP_READ_REG(chp, wdr_ireason); + + if (!wdc_atapi_in_data_phase(xfer, len, ire)) + return (CONTINUE_POLL); + if (xfer->c_bcount >= len) { + WDCDEBUG_PRINT(("wdc_atapi_intr: c_bcount %d len %d st 0x%x err 0x%x " + "ire 0x%x\n", xfer->c_bcount, + len, chp->ch_status, chp->ch_error, ire), DEBUG_INTR); + /* Common case */ if (sc_xfer->flags & SCSI_DATA_OUT) wdc_output_bytes(drvp, (u_int8_t *)xfer->databuf + @@ -793,60 +1024,25 @@ wdc_atapi_intr_data(chp, xfer, dma_flags) xfer->c_bcount = 0; } - if ((sc_xfer->flags & SCSI_POLL) == 0 && - (drvp->atapi_cap & ACAP_DSC) == 0) { - chp->ch_flags |= WDCF_IRQ_WAIT; - timeout(wdctimeout, chp, sc_xfer->timeout * hz / 1000); - } else if (drvp->atapi_cap & ACAP_DSC) - drvp->drive_flags |= DRIVE_DSCWAIT; - return (1); - - unexpected_state: + xfer->expect_irq = 1; + xfer->next = wdc_atapi_intr_for_us; - /* If we're in polling mode, then it's possible we caught - a drive in some awkward, intermediate state. Of course, - the drive should have BSY set while it's transitioning - through awkward states, but this may not always - be the case. */ - - /* Spurious interrupts can cause us pain too and we don't deal - with those nearly as well. We assume the spurious - interrupts are random and won't affect us on retry */ - if (xfer->c_flags & C_POLL) { - DELAY(1000); - as->retries++; - /* Give the drive up to 2 seconds to fix itself */ - if (as->retries <= 2000) - return (1); - } - - if (message) - printf ("wdc_atapi_intr_drq: %s\n", message); - - if (xfer->c_flags & C_DMA) { - (*chp->wdc->dma_finish)(chp->wdc->dma_arg, - chp->channel, xfer->drive, dma_flags); - drvp->n_dmaerrs++; - } - - sc_xfer->error = XS_RESET; - wdc_atapi_reset(chp, xfer); - - return (1); + return (GOTO_NEXT); } int -wdc_atapi_intr_complete(chp, xfer, dma_flags) +wdc_atapi_intr_complete(chp, xfer, timeout) struct channel_softc *chp; struct wdc_xfer *xfer; - int dma_flags; + int timeout; { struct scsi_xfer *sc_xfer = xfer->cmd; struct ata_drive_datas *drvp = &chp->ch_drive[xfer->drive]; struct atapiscsi_softc *as = sc_xfer->sc_link->adapter_softc; int dma_err = 0; + int dma_flags = wdc_atapi_dma_flags(xfer); WDCDEBUG_PRINT(("PHASE_COMPLETED\n"), DEBUG_INTR); @@ -856,12 +1052,8 @@ wdc_atapi_intr_complete(chp, xfer, dma_flags) dma_err = (*chp->wdc->dma_finish)(chp->wdc->dma_arg, chp->channel, xfer->drive, dma_flags); - /* Assume everything was transferred - XXX - maybe we want to check the error register here */ - if (xfer->c_flags & C_SENSE) - xfer->c_bcount -= sizeof(sc_xfer->sense); - else - xfer->c_bcount -= sc_xfer->datalen; + /* Assume everything was transferred */ + xfer->c_bcount = 0; } as->protocol_phase = as_none; @@ -872,8 +1064,8 @@ wdc_atapi_intr_complete(chp, xfer, dma_flags) WDCDEBUG_PRINT(("wdc_atapi_intr: request_sense aborted, " "calling wdc_atapi_done()" ), DEBUG_INTR); - wdc_atapi_done(chp, xfer); - return (1); + xfer->next = wdc_atapi_done; + return (GOTO_NEXT); } /* @@ -882,8 +1074,9 @@ wdc_atapi_intr_complete(chp, xfer, dma_flags) */ sc_xfer->error = XS_RESET; - wdc_atapi_reset(chp, xfer); - return (1); + xfer->next = wdc_atapi_reset; + return (GOTO_NEXT); + } else if (xfer->c_bcount < sizeof(sc_xfer->sense)) { /* use the sense we just read */ sc_xfer->error = XS_SENSE; @@ -907,21 +1100,22 @@ wdc_atapi_intr_complete(chp, xfer, dma_flags) * 'request sense' */ xfer->databuf = &sc_xfer->sense; - xfer->c_bcount = - sizeof(sc_xfer->sense); + xfer->c_bcount = sizeof(sc_xfer->sense); xfer->c_skip = 0; + xfer->c_done = NULL; xfer->c_flags |= C_SENSE; - wdc_atapi_start(chp, xfer); - return (1); + xfer->next = wdc_atapi_real_start; + return (GOTO_NEXT); } - } + } } if (dma_err < 0) { drvp->n_dmaerrs++; sc_xfer->error = XS_RESET; - wdc_atapi_reset(chp, xfer); - return (1); + + xfer->next = wdc_atapi_reset; + return (GOTO_NEXT); } @@ -939,98 +1133,113 @@ wdc_atapi_intr_complete(chp, xfer, dma_flags) WDCDEBUG_PRINT(("wdc_atapi_intr: wdc_atapi_done() (end), error 0x%x " "\n", sc_xfer->error), DEBUG_INTR); - wdc_atapi_done(chp, xfer); - return (1); + + + if (xfer->c_done) + xfer->next = xfer->c_done; + else + xfer->next = wdc_atapi_done; + + return (GOTO_NEXT); } int -wdc_atapi_intr(chp, xfer, irq) +wdc_atapi_intr_for_us(chp, xfer, timeout) struct channel_softc *chp; struct wdc_xfer *xfer; - int irq; + int timeout; + { struct scsi_xfer *sc_xfer = xfer->cmd; struct ata_drive_datas *drvp = &chp->ch_drive[xfer->drive]; struct atapiscsi_softc *as = sc_xfer->sc_link->adapter_softc; - int dma_flags = 0; - WDCDEBUG_PRINT(("wdc_atapi_intr %s:%d:%d\n", - chp->wdc->sc_dev.dv_xname, chp->channel, drvp->drive), - DEBUG_INTR); + WDCDEBUG_PRINT(("ATAPI_INTR\n"), DEBUG_INTR); - /* Is it not a transfer, but a control operation? */ - if (drvp->state < READY) { - printf("%s:%d:%d: bad state %d in wdc_atapi_intr\n", - chp->wdc->sc_dev.dv_xname, chp->channel, xfer->drive, - drvp->state); - panic("wdc_atapi_intr: bad state\n"); - } + /* If we missed an IRQ and were using DMA, flag it as a DMA error */ /* We should really wait_for_unbusy here too before switching drives. */ CHP_WRITE_REG(chp, wdr_sdh, WDSD_IBM | (xfer->drive << 4)); + DELAY (1); - /* Ack interrupt done in wait_for_unbusy */ - if (wait_for_unbusy(chp, - (irq == 0) ? sc_xfer->timeout : 0) != 0) { - if (irq && (xfer->c_flags & C_TIMEOU) == 0) - return (0); /* IRQ was not for us */ - printf("%s:%d:%d: device timeout, c_bcount=%d, c_skip=%d\n", - chp->wdc->sc_dev.dv_xname, chp->channel, xfer->drive, - xfer->c_bcount, xfer->c_skip); - if (xfer->c_flags & C_DMA) - drvp->n_dmaerrs++; - sc_xfer->error = XS_TIMEOUT; - wdc_atapi_reset(chp, xfer); - return (1); + wdc_atapi_update_status(chp); + + if (chp->ch_status & WDCS_BSY) { + if (timeout) { + printf("%s:%d:%d: device timeout, c_bcount=%d, c_skip=%d\n", + chp->wdc->sc_dev.dv_xname, chp->channel, xfer->drive, + xfer->c_bcount, xfer->c_skip); + + if (xfer->c_flags & C_DMA) + drvp->n_dmaerrs++; + + sc_xfer->error = XS_TIMEOUT; + xfer->next = wdc_atapi_reset; + return (GOTO_NEXT); + } else { + return (CONTINUE_POLL); + } } - /* If we missed an IRQ and were using DMA, flag it as a DMA error */ - if ((xfer->c_flags & C_TIMEOU) && (xfer->c_flags & C_DMA)) - drvp->n_dmaerrs++; - /* DRQ was dropped. This means the command is over. - Do cleanup, check for errors, etc. */ - if (xfer->c_flags & C_DMA) { - dma_flags = ((sc_xfer->flags & SCSI_DATA_IN) || - (xfer->c_flags & C_SENSE)) ? WDC_DMA_READ : 0; - dma_flags |= ((sc_xfer->flags & SCSI_POLL) || - (drvp->atapi_cap & ACAP_DSC)) ? WDC_DMA_POLL : 0; + if (as->protocol_phase != as_cmdout && + (xfer->c_flags & C_MEDIA_ACCESS) && + !(chp->ch_status & WDCS_DSC)) { + xfer->delay = 100; + return (CONTINUE_POLL); } + xfer->claim_irq = 1; + + if ((xfer->c_flags & C_TIMEOU) && (xfer->c_flags & C_DMA)) + drvp->n_dmaerrs++; + if (chp->ch_status & WDCS_DRQ) { if (as->protocol_phase == as_cmdout) - return (wdc_atapi_intr_command(chp, xfer, dma_flags)); + return (wdc_atapi_intr_command(chp, xfer, timeout)); + + if (!(xfer->c_flags & C_DMA)) + return (wdc_atapi_intr_data(chp, xfer, timeout)); else - return (wdc_atapi_intr_data(chp, xfer, dma_flags)); - } else - return (wdc_atapi_intr_complete(chp, xfer, dma_flags)); + return (CONTINUE_POLL); + } + + return (wdc_atapi_intr_complete(chp, xfer, timeout)); } int -wdc_atapi_ctrl(chp, xfer, irq) +wdc_atapi_ctrl(chp, xfer, timeout) struct channel_softc *chp; struct wdc_xfer *xfer; - int irq; + int timeout; { struct scsi_xfer *sc_xfer = xfer->cmd; struct ata_drive_datas *drvp = &chp->ch_drive[xfer->drive]; char *errstring = NULL; - int delay; /* Ack interrupt done in wait_for_unbusy */ -again: - delay = (xfer->c_flags & C_POLL) ? 4000 : ((irq == 0) ? ATAPI_DELAY : 1); + CHP_WRITE_REG(chp, wdr_sdh, WDSD_IBM | (xfer->drive << 4)); + delay (1); + + if (timeout) { + if (drvp->state == IDENTIFY_WAIT || + drvp->state == PIOMODE || + drvp->state == PIOMODE_WAIT || + drvp->state == DMAMODE_WAIT) + goto timeout; + } else { + wdc_atapi_update_status(chp); + + if (chp->ch_status & WDCS_BSY) + return (CONTINUE_POLL); + } + + xfer->claim_irq = 1; + WDCDEBUG_PRINT(("wdc_atapi_ctrl %s:%d:%d state %d\n", chp->wdc->sc_dev.dv_xname, chp->channel, drvp->drive, drvp->state), DEBUG_INTR | DEBUG_FUNCS); - /* We shouldn't have to select the drive in these states. - If we do, there are other, more serious problems */ - if (drvp->state != IDENTIFY_WAIT && - drvp->state != PIOMODE_WAIT && - drvp->state != DMAMODE_WAIT) - wdc_select_drive(chp, xfer->drive, delay); - /* Don't timeout during configuration */ xfer->c_flags &= ~C_TIMEOU; @@ -1042,17 +1251,13 @@ again: case IDENTIFY: wdccommandshort(chp, drvp->drive, ATAPI_IDENTIFY_DEVICE); drvp->state = IDENTIFY_WAIT; + xfer->timeout = ATAPI_CTRL_WAIT; + xfer->expect_irq = 1; break; case IDENTIFY_WAIT: errstring = "IDENTIFY"; - /* Some ATAPI devices need to try to read the media - before responding. Well, let's hope resets while - polling are few and far between */ - if (wdcwait(chp, 0, 0, delay)) - goto timeout; - /* We don't really care if this operation failed. It's just there to wake the drive from its stupor. */ if (!(chp->ch_status & WDCS_ERR)) { @@ -1060,11 +1265,13 @@ again: errstring = "Post IDENTIFY"; - if (wdcwait(chp, WDCS_DRQ, 0, 100)) - goto timeout; + xfer->timeout = 100; + drvp->state = PIOMODE; + break; } drvp->state = PIOMODE; + case PIOMODE: piomode: /* Don't try to set mode if controller can't be adjusted */ @@ -1076,11 +1283,11 @@ piomode: wdccommand(chp, drvp->drive, SET_FEATURES, 0, 0, 0, 0x08 | drvp->PIO_mode, WDSF_SET_MODE); drvp->state = PIOMODE_WAIT; + xfer->timeout = ATAPI_CTRL_WAIT; + xfer->expect_irq = 1; break; case PIOMODE_WAIT: errstring = "piomode"; - if (wait_for_unbusy(chp, delay)) - goto timeout; if (chp->ch_status & WDCS_ERR) { if (drvp->PIO_mode < 3) { drvp->PIO_mode = 3; @@ -1102,11 +1309,13 @@ piomode: goto ready; } drvp->state = DMAMODE_WAIT; + + xfer->timeout = ATAPI_CTRL_WAIT; + xfer->expect_irq = 1; break; + case DMAMODE_WAIT: errstring = "dmamode"; - if (wait_for_unbusy(chp, delay)) - goto timeout; if (chp->ch_status & WDCS_ERR) goto error; /* fall through */ @@ -1114,29 +1323,18 @@ piomode: case READY: ready: drvp->state = READY; - xfer->c_intr = wdc_atapi_intr; - wdc_atapi_start(chp, xfer); - return (1); - } - if ((sc_xfer->flags & SCSI_POLL) == 0 && - (drvp->atapi_cap & ACAP_DSC) == 0) { - chp->ch_flags |= WDCF_IRQ_WAIT; - xfer->c_intr = wdc_atapi_ctrl; - timeout(wdctimeout, chp, sc_xfer->timeout * hz / 1000); - } else { - goto again; + xfer->next = wdc_atapi_real_start; + break; } - return (1); + return (GOTO_NEXT); timeout: - if (irq && (xfer->c_flags & C_TIMEOU) == 0) { - return (0); /* IRQ was not for us */ - } printf("%s:%d:%d: %s timed out\n", chp->wdc->sc_dev.dv_xname, chp->channel, xfer->drive, errstring); sc_xfer->error = XS_TIMEOUT; - wdc_atapi_reset(chp, xfer); - return (1); + xfer->next = wdc_atapi_reset; + return (GOTO_NEXT); + error: printf("%s:%d:%d: %s ", chp->wdc->sc_dev.dv_xname, chp->channel, xfer->drive, @@ -1144,14 +1342,41 @@ error: printf("error (0x%x)\n", chp->ch_error); sc_xfer->error = XS_SHORTSENSE; atapi_to_scsi_sense(sc_xfer, chp->ch_error); - wdc_atapi_reset(chp, xfer); - return (1); + xfer->next = wdc_atapi_reset; + return (GOTO_NEXT); } -void -wdc_atapi_done(chp, xfer) +int +wdc_atapi_tape_done(chp, xfer, timeout) + struct channel_softc *chp; + struct wdc_xfer *xfer; + int timeout; +{ + struct scsi_xfer *sc_xfer = xfer->cmd; + + if (sc_xfer->error != XS_NOERROR) { + xfer->next = wdc_atapi_done; + return (GOTO_NEXT); + } + + _lto3b(xfer->transfer_len, + ((struct scsi_rw_tape *) + sc_xfer->cmd)->len); + + xfer->c_bcount = sc_xfer->datalen; + xfer->c_done = NULL; + xfer->c_skip = 0; + + xfer->next = wdc_atapi_real_start; + return (GOTO_NEXT); +} + + +int +wdc_atapi_done(chp, xfer, timeout) struct channel_softc *chp; struct wdc_xfer *xfer; + int timeout; { struct scsi_xfer *sc_xfer = xfer->cmd; int need_done = xfer->c_flags & C_NEEDDONE; @@ -1161,8 +1386,7 @@ wdc_atapi_done(chp, xfer) WDCDEBUG_PRINT(("wdc_atapi_done %s:%d:%d: flags 0x%x\n", chp->wdc->sc_dev.dv_xname, chp->channel, xfer->drive, (u_int)xfer->c_flags), DEBUG_XFERS); - /* remove this command from xfer queue */ - wdc_free_xfer(chp, xfer); + sc_xfer->flags |= ITSDONE; if (drvp->n_dmaerrs || (sc_xfer->error != XS_NOERROR && sc_xfer->error != XS_SENSE && @@ -1174,111 +1398,63 @@ wdc_atapi_done(chp, xfer) if (doing_dma) wdc_downgrade_mode(drvp); } - + if (need_done) { WDCDEBUG_PRINT(("wdc_atapi_done: scsi_done\n"), DEBUG_XFERS); scsi_done(sc_xfer); } - WDCDEBUG_PRINT(("wdcstart from wdc_atapi_done, flags 0x%x\n", - chp->ch_flags), DEBUG_XFERS); - wdcstart(chp); -} - -/* Wait until DSC gets asserted. */ -int -atapi_dsc_wait(drvp, timo) - struct ata_drive_datas *drvp; - int timo; -{ - struct channel_softc *chp = drvp->chnl_softc; - chp->ch_flags &= ~WDCF_ACTIVE; -#if 0 - /* XXX Something like this may be needed I have not investigated - close enough yet. If so we may need to put it back after - the poll wait. */ - TAILQ_REMOVE(&chp->ch_queue->sc_xfer, xfer, c_xferchain); -#endif - return (atapi_poll_wait(atapi_dsc_ready, drvp, timo, PZERO + PCATCH, - "atapidsc")); + return (DONE); } -int -atapi_dsc_ready(arg) - void *arg; -{ - struct ata_drive_datas *drvp = arg; - struct channel_softc *chp = drvp->chnl_softc; - - if (chp->ch_flags & WDCF_ACTIVE) - return (0); - wdc_select_drive(chp, drvp->drive, 0); - chp->ch_status = CHP_READ_REG(chp, wdr_status); - return ((chp->ch_status & (WDCS_BSY | WDCS_DSC)) == WDCS_DSC); -} int -atapi_poll_wait(ready, arg, timo, pri, msg) - int (*ready) __P((void *)); - void *arg; - int timo; - int pri; - char *msg; -{ - int maxtic, tic = 0, error; - u_int64_t starttime = time.tv_sec * 1000 + time.tv_usec / 1000; - u_int64_t endtime = starttime + timo; - - while (1) { - WDCDEBUG_PRINT(("atapi_poll_wait: msg=%s tic=%d\n", msg, tic), - DEBUG_POLL); - if (ready(arg)) - return (0); - -#if 0 - /* Exponential backoff. */ - tic = tic + tic + 1; -#else - tic = min(hz / 100, 1); -#endif - maxtic = (int) - (endtime - (time.tv_sec * 1000 + time.tv_usec / 1000)); - if (maxtic <= 0) - return (EWOULDBLOCK); - if (tic > maxtic) - tic = maxtic; - if (tic > ATAPI_POLL_MAXTIC) - tic = ATAPI_POLL_MAXTIC; - error = tsleep(arg, pri, msg, tic); - if (error != EWOULDBLOCK) - return (error); - } -} - -void -wdc_atapi_reset(chp, xfer) +wdc_atapi_reset(chp, xfer, timeout) struct channel_softc *chp; struct wdc_xfer *xfer; + int timeout; { struct ata_drive_datas *drvp = &chp->ch_drive[xfer->drive]; - struct scsi_xfer *sc_xfer = xfer->cmd; wdccommandshort(chp, xfer->drive, ATAPI_SOFT_RESET); + drvp->state = 0; /* Some ATAPI devices need extra time to find their brains after a reset */ - delay(5000); + xfer->next = wdc_atapi_reset_2; + xfer->delay = 10; + xfer->timeout = ATAPI_RESET_WAIT; + return (GOTO_NEXT); +} - drvp->state = 0; - if (wdcwait(chp, WDCS_DRQ, 0, ATAPI_RESET_WAIT) != 0) { +int +wdc_atapi_reset_2(chp, xfer, timeout) + struct channel_softc *chp; + struct wdc_xfer *xfer; + int timeout; +{ + struct ata_drive_datas *drvp = &chp->ch_drive[xfer->drive]; + struct scsi_xfer *sc_xfer = xfer->cmd; + + if (timeout) { printf("%s:%d:%d: reset failed\n", chp->wdc->sc_dev.dv_xname, chp->channel, xfer->drive); sc_xfer->error = XS_SELTIMEOUT; wdc_reset_channel(drvp); + + xfer->next = wdc_atapi_done; + return (GOTO_NEXT); } - wdc_atapi_done(chp, xfer); - return; + wdc_atapi_update_status(chp); + + if (chp->ch_status & (WDCS_BSY | WDCS_DRQ)) { + return (CONTINUE_POLL); + } + + xfer->next = wdc_atapi_done; + return (GOTO_NEXT); } + diff --git a/sys/dev/ic/wdcvar.h b/sys/dev/ic/wdcvar.h index cfe869793ea..c0b18b6574b 100644 --- a/sys/dev/ic/wdcvar.h +++ b/sys/dev/ic/wdcvar.h @@ -1,4 +1,4 @@ -/* $OpenBSD: wdcvar.h,v 1.5 1999/11/17 01:22:56 csapuntz Exp $ */ +/* $OpenBSD: wdcvar.h,v 1.6 1999/12/11 10:15:03 csapuntz Exp $ */ /* $NetBSD: wdcvar.h,v 1.17 1999/04/11 20:50:29 bouyer Exp $ */ /*- @@ -188,6 +188,8 @@ struct wdc_xfer { #define C_POLL 0x0020 /* cmd is polled */ #define C_DMA 0x0040 /* cmd uses DMA */ #define C_SENSE 0x0080 /* cmd is a internal command */ +#define C_MEDIA_ACCESS 0x0100 /* is a media access command */ +#define C_POLL_MACHINE 0x0200 /* machine has a poll hander */ /* Informations about our location */ struct channel_softc *chp; @@ -202,6 +204,19 @@ struct wdc_xfer { LIST_ENTRY(wdc_xfer) free_list; void (*c_start) __P((struct channel_softc *, struct wdc_xfer *)); int (*c_intr) __P((struct channel_softc *, struct wdc_xfer *, int)); + int (*c_done) __P((struct channel_softc *, struct wdc_xfer *, int)); + + /* Used by ATAPISCSI */ + int timeout; + u_int64_t endtime; + int delay; + unsigned int expect_irq:1; + unsigned int claim_irq:1; + + int (*next) __P((struct channel_softc *, struct wdc_xfer *, int)); + + /* Used for tape devices */ + int transfer_len; }; /* |