static void wdc_atapi_phase_complete(struct ata_xfer *xfer) { struct ata_channel *chp = xfer->c_chp; struct atac_softc *atac = chp->ch_atac; #if NATA_DMA || NATA_PIOBM struct wdc_softc *wdc = CHAN_TO_WDC(chp); #endif struct scsipi_xfer *sc_xfer = xfer->c_cmd; struct ata_drive_datas *drvp = &chp->ch_drive[xfer->c_drive]; /* wait for DSC if needed */ if (drvp->drive_flags & ATA_DRIVE_ATAPIDSCW) { ATADEBUG_PRINT(("wdc_atapi_phase_complete(%s:%d:%d) " "polldsc %d\n", device_xname(atac->atac_dev), chp->ch_channel, xfer->c_drive, xfer->c_dscpoll), DEBUG_XFERS); #if 1 if (cold) panic("wdc_atapi_phase_complete: cold"); #endif if (wdcwait(chp, WDCS_DSC, WDCS_DSC, 10, AT_POLL) == WDCWAIT_TOUT) { /* 10ms not enough, try again in 1 tick */ if (xfer->c_dscpoll++ > mstohz(sc_xfer->timeout)) { printf("%s:%d:%d: wait_for_dsc " "failed\n", device_xname(atac->atac_dev), chp->ch_channel, xfer->c_drive); sc_xfer->error = XS_TIMEOUT; wdc_atapi_reset(chp, xfer); return; } else callout_reset(&chp->ch_callout, 1, wdc_atapi_polldsc, xfer); return; } } /* * Some drive occasionally set WDCS_ERR with * "ATA illegal length indication" in the error * register. If we read some data the sense is valid * anyway, so don't report the error. */ if (chp->ch_status & WDCS_ERR && ((sc_xfer->xs_control & XS_CTL_REQSENSE) == 0 || sc_xfer->resid == sc_xfer->datalen)) { /* save the short sense */ sc_xfer->error = XS_SHORTSENSE; sc_xfer->sense.atapi_sense = chp->ch_error; if ((sc_xfer->xs_periph->periph_quirks & PQUIRK_NOSENSE) == 0) { /* ask scsipi to send a REQUEST_SENSE */ sc_xfer->error = XS_BUSY; sc_xfer->status = SCSI_CHECK; } #if NATA_DMA || NATA_PIOBM else if (wdc->dma_status & (WDC_DMAST_NOIRQ | WDC_DMAST_ERR)) { #if NATA_DMA ata_dmaerr(drvp, (xfer->c_flags & C_POLL) ? AT_POLL : 0); #endif sc_xfer->error = XS_RESET; wdc_atapi_reset(chp, xfer); return; } #endif } if (xfer->c_bcount != 0) { ATADEBUG_PRINT(("wdc_atapi_intr: bcount value is " "%d after io\n", xfer->c_bcount), DEBUG_XFERS); } #ifdef DIAGNOSTIC if (xfer->c_bcount < 0) { printf("wdc_atapi_intr warning: bcount value " "is %d after io\n", xfer->c_bcount); } #endif ATADEBUG_PRINT(("wdc_atapi_phase_complete: wdc_atapi_done(), " "error 0x%x sense 0x%x\n", sc_xfer->error, sc_xfer->sense.atapi_sense), DEBUG_INTR); wdc_atapi_done(chp, xfer); }
void wdc_atapi_intr_complete(struct channel_softc *chp, struct wdc_xfer *xfer, int timeout, struct atapi_return_args *ret) { 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(("PHASE_COMPLETED\n"), DEBUG_INTR); if (xfer->c_flags & C_DMA) { int retry; if (timeout) { sc_xfer->error = XS_TIMEOUT; ata_dmaerr(drvp); xfer->next = wdc_atapi_reset; return; } for (retry = 5; retry > 0; retry--) { wdc_atapi_update_status(chp); if ((chp->ch_status & (WDCS_BSY | WDCS_DRQ)) == 0) break; DELAY(5); } if (retry == 0) { ret->expect_irq = 1; return; } chp->wdc->dma_status = (*chp->wdc->dma_finish) (chp->wdc->dma_arg, chp->channel, xfer->drive, 1); if (chp->wdc->dma_status & WDC_DMAST_UNDER) xfer->c_bcount = 1; else xfer->c_bcount = 0; } as->protocol_phase = as_none; if (xfer->c_flags & C_SENSE) { if (chp->ch_status & WDCS_ERR) { if (chp->ch_error & WDCE_ABRT) { WDCDEBUG_PRINT(("wdc_atapi_intr: request_sense aborted, " "calling wdc_atapi_done()" ), DEBUG_INTR); xfer->next = wdc_atapi_done; return; } /* * request sense failed ! it's not supposed * to be possible */ sc_xfer->error = XS_SHORTSENSE; } else if (xfer->c_bcount < sizeof(sc_xfer->sense)) { /* use the sense we just read */ sc_xfer->error = XS_SENSE; } else { /* * command completed, but no data was read. * use the short sense we saved previously. */ sc_xfer->error = XS_SHORTSENSE; } } else { sc_xfer->resid = xfer->c_bcount; if (chp->ch_status & WDCS_ERR) { if (!atapi_to_scsi_sense(sc_xfer, chp->ch_error) && (sc_xfer->sc_link->quirks & ADEV_NOSENSE) == 0) { /* * let the driver issue a * 'request sense' */ xfer->databuf = &sc_xfer->sense; xfer->c_bcount = sizeof(sc_xfer->sense); xfer->c_skip = 0; xfer->c_done = NULL; xfer->c_flags |= C_SENSE; xfer->next = wdc_atapi_real_start; return; } } } if ((xfer->c_flags & C_DMA) && (chp->wdc->dma_status & ~WDC_DMAST_UNDER)) { ata_dmaerr(drvp); sc_xfer->error = XS_RESET; xfer->next = wdc_atapi_reset; return; } if (xfer->c_bcount != 0) { WDCDEBUG_PRINT(("wdc_atapi_intr: bcount value is " "%d after io\n", xfer->c_bcount), DEBUG_XFERS); } #ifdef DIAGNOSTIC if (xfer->c_bcount < 0) { printf("wdc_atapi_intr warning: bcount value " "is %d after io\n", xfer->c_bcount); } #endif WDCDEBUG_PRINT(("wdc_atapi_intr: wdc_atapi_done() (end), error 0x%x " "\n", sc_xfer->error), DEBUG_INTR); if (xfer->c_done) xfer->next = xfer->c_done; else xfer->next = wdc_atapi_done; return; }
static int wdc_atapi_intr(struct ata_channel *chp, struct ata_xfer *xfer, int irq) { struct atac_softc *atac = chp->ch_atac; struct wdc_softc *wdc = CHAN_TO_WDC(chp); struct wdc_regs *wdr = &wdc->regs[chp->ch_channel]; struct scsipi_xfer *sc_xfer = xfer->c_cmd; struct ata_drive_datas *drvp = &chp->ch_drive[xfer->c_drive]; int len, phase, i, retries=0; int ire; #if NATA_DMA int error; #endif #if NATA_DMA || NATA_PIOBM int dma_flags = 0; #endif void *cmd; ATADEBUG_PRINT(("wdc_atapi_intr %s:%d:%d\n", device_xname(atac->atac_dev), chp->ch_channel, drvp->drive), 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", device_xname(atac->atac_dev), chp->ch_channel, xfer->c_drive, drvp->state); panic("wdc_atapi_intr: bad state"); } /* * If we missed an interrupt in a PIO transfer, reset and restart. * Don't try to continue transfer, we may have missed cycles. */ if ((xfer->c_flags & (C_TIMEOU | C_DMA)) == C_TIMEOU) { sc_xfer->error = XS_TIMEOUT; wdc_atapi_reset(chp, xfer); return 1; } #if NATA_PIOBM /* Transfer-done interrupt for busmastering PIO operation */ if ((xfer->c_flags & C_PIOBM) && (chp->ch_flags & ATACH_PIOBM_WAIT)) { chp->ch_flags &= ~ATACH_PIOBM_WAIT; /* restore transfer length */ len = xfer->c_bcount; if (xfer->c_lenoff < 0) len += xfer->c_lenoff; if (sc_xfer->xs_control & XS_CTL_DATA_IN) goto end_piobm_datain; else goto end_piobm_dataout; } #endif /* Ack interrupt done in wdc_wait_for_unbusy */ if (wdc->select) wdc->select(chp, xfer->c_drive); bus_space_write_1(wdr->cmd_iot, wdr->cmd_iohs[wd_sdh], 0, WDSD_IBM | (xfer->c_drive << 4)); if (wdc_wait_for_unbusy(chp, (irq == 0) ? sc_xfer->timeout : 0, AT_POLL) == WDCWAIT_TOUT) { 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", device_xname(atac->atac_dev), chp->ch_channel, xfer->c_drive, xfer->c_bcount, xfer->c_skip); #if NATA_DMA if (xfer->c_flags & C_DMA) { ata_dmaerr(drvp, (xfer->c_flags & C_POLL) ? AT_POLL : 0); } #endif sc_xfer->error = XS_TIMEOUT; wdc_atapi_reset(chp, xfer); return 1; } if (wdc->irqack) wdc->irqack(chp); #if NATA_DMA /* * If we missed an IRQ and were using DMA, flag it as a DMA error * and reset device. */ if ((xfer->c_flags & C_TIMEOU) && (xfer->c_flags & C_DMA)) { ata_dmaerr(drvp, (xfer->c_flags & C_POLL) ? AT_POLL : 0); sc_xfer->error = XS_RESET; wdc_atapi_reset(chp, xfer); return (1); } #endif /* * if the request sense command was aborted, report the short sense * previously recorded, else continue normal processing */ #if NATA_DMA || NATA_PIOBM if (xfer->c_flags & (C_DMA | C_PIOBM)) dma_flags = (sc_xfer->xs_control & XS_CTL_DATA_IN) ? WDC_DMA_READ : 0; #endif again: len = bus_space_read_1(wdr->cmd_iot, wdr->cmd_iohs[wd_cyl_lo], 0) + 256 * bus_space_read_1(wdr->cmd_iot, wdr->cmd_iohs[wd_cyl_hi], 0); ire = bus_space_read_1(wdr->cmd_iot, wdr->cmd_iohs[wd_ireason], 0); phase = (ire & (WDCI_CMD | WDCI_IN)) | (chp->ch_status & WDCS_DRQ); ATADEBUG_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); switch (phase) { case PHASE_CMDOUT: cmd = sc_xfer->cmd; ATADEBUG_PRINT(("PHASE_CMDOUT\n"), DEBUG_INTR); #if NATA_DMA /* Init the DMA channel if necessary */ if (xfer->c_flags & C_DMA) { error = (*wdc->dma_init)(wdc->dma_arg, chp->ch_channel, xfer->c_drive, xfer->c_databuf, xfer->c_bcount, dma_flags); if (error) { if (error == EINVAL) { /* * We can't do DMA on this transfer * for some reason. Fall back to * PIO. */ xfer->c_flags &= ~C_DMA; error = 0; } else { sc_xfer->error = XS_DRIVER_STUFFUP; break; } } } #endif /* send packet command */ /* Commands are 12 or 16 bytes long. It's 32-bit aligned */ wdc->dataout_pio(chp, drvp->drive_flags, cmd, sc_xfer->cmdlen); #if NATA_DMA /* Start the DMA channel if necessary */ if (xfer->c_flags & C_DMA) { (*wdc->dma_start)(wdc->dma_arg, chp->ch_channel, xfer->c_drive); chp->ch_flags |= ATACH_DMA_WAIT; } #endif if ((sc_xfer->xs_control & XS_CTL_POLL) == 0) { chp->ch_flags |= ATACH_IRQ_WAIT; } return 1; case PHASE_DATAOUT: /* write data */ ATADEBUG_PRINT(("PHASE_DATAOUT\n"), DEBUG_INTR); #if NATA_DMA if ((sc_xfer->xs_control & XS_CTL_DATA_OUT) == 0 || (xfer->c_flags & C_DMA) != 0) { printf("wdc_atapi_intr: bad data phase DATAOUT\n"); if (xfer->c_flags & C_DMA) { ata_dmaerr(drvp, (xfer->c_flags & C_POLL) ? AT_POLL : 0); } sc_xfer->error = XS_TIMEOUT; wdc_atapi_reset(chp, xfer); return 1; } #endif xfer->c_lenoff = len - xfer->c_bcount; if (xfer->c_bcount < len) { printf("wdc_atapi_intr: warning: write only " "%d of %d requested bytes\n", xfer->c_bcount, len); len = xfer->c_bcount; } #if NATA_PIOBM if (xfer->c_flags & C_PIOBM) { /* start the busmastering PIO */ (*wdc->piobm_start)(wdc->dma_arg, chp->ch_channel, xfer->c_drive, xfer->c_skip, len, WDC_PIOBM_XFER_IRQ); chp->ch_flags |= ATACH_DMA_WAIT | ATACH_IRQ_WAIT | ATACH_PIOBM_WAIT; return 1; } #endif wdc->dataout_pio(chp, drvp->drive_flags, (char *)xfer->c_databuf + xfer->c_skip, len); #if NATA_PIOBM end_piobm_dataout: #endif for (i = xfer->c_lenoff; i > 0; i -= 2) bus_space_write_2(wdr->cmd_iot, wdr->cmd_iohs[wd_data], 0, 0); xfer->c_skip += len; xfer->c_bcount -= len; if ((sc_xfer->xs_control & XS_CTL_POLL) == 0) { chp->ch_flags |= ATACH_IRQ_WAIT; } return 1; case PHASE_DATAIN: /* Read data */ ATADEBUG_PRINT(("PHASE_DATAIN\n"), DEBUG_INTR); #if NATA_DMA if ((sc_xfer->xs_control & XS_CTL_DATA_IN) == 0 || (xfer->c_flags & C_DMA) != 0) { printf("wdc_atapi_intr: bad data phase DATAIN\n"); if (xfer->c_flags & C_DMA) { ata_dmaerr(drvp, (xfer->c_flags & C_POLL) ? AT_POLL : 0); } sc_xfer->error = XS_TIMEOUT; wdc_atapi_reset(chp, xfer); return 1; } #endif xfer->c_lenoff = len - xfer->c_bcount; if (xfer->c_bcount < len) { printf("wdc_atapi_intr: warning: reading only " "%d of %d bytes\n", xfer->c_bcount, len); len = xfer->c_bcount; } #if NATA_PIOBM if (xfer->c_flags & C_PIOBM) { /* start the busmastering PIO */ (*wdc->piobm_start)(wdc->dma_arg, chp->ch_channel, xfer->c_drive, xfer->c_skip, len, WDC_PIOBM_XFER_IRQ); chp->ch_flags |= ATACH_DMA_WAIT | ATACH_IRQ_WAIT | ATACH_PIOBM_WAIT; return 1; } #endif wdc->datain_pio(chp, drvp->drive_flags, (char *)xfer->c_databuf + xfer->c_skip, len); #if NATA_PIOBM end_piobm_datain: #endif if (xfer->c_lenoff > 0) wdcbit_bucket(chp, xfer->c_lenoff); xfer->c_skip += len; xfer->c_bcount -= len; if ((sc_xfer->xs_control & XS_CTL_POLL) == 0) { chp->ch_flags |= ATACH_IRQ_WAIT; } return 1; case PHASE_ABORTED: case PHASE_COMPLETED: ATADEBUG_PRINT(("PHASE_COMPLETED\n"), DEBUG_INTR); #if NATA_DMA if (xfer->c_flags & C_DMA) { xfer->c_bcount -= sc_xfer->datalen; } #endif sc_xfer->resid = xfer->c_bcount; wdc_atapi_phase_complete(xfer); return(1); default: if (++retries<500) { DELAY(100); chp->ch_status = bus_space_read_1(wdr->cmd_iot, wdr->cmd_iohs[wd_status], 0); chp->ch_error = bus_space_read_1(wdr->cmd_iot, wdr->cmd_iohs[wd_error], 0); goto again; } printf("wdc_atapi_intr: unknown phase 0x%x\n", phase); if (chp->ch_status & WDCS_ERR) { sc_xfer->error = XS_SHORTSENSE; sc_xfer->sense.atapi_sense = chp->ch_error; } else { #if NATA_DMA if (xfer->c_flags & C_DMA) { ata_dmaerr(drvp, (xfer->c_flags & C_POLL) ? AT_POLL : 0); } #endif sc_xfer->error = XS_RESET; wdc_atapi_reset(chp, xfer); return (1); } } ATADEBUG_PRINT(("wdc_atapi_intr: wdc_atapi_done() (end), error 0x%x " "sense 0x%x\n", sc_xfer->error, sc_xfer->sense.atapi_sense), DEBUG_INTR); wdc_atapi_done(chp, xfer); return (1); }