void wdc_atapi_ctrl(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]; char *errstring = NULL; wdc_atapi_update_status(chp); if (!timeout) { switch (drvp->state) { case ATAPI_IDENTIFY_WAIT_STATE: if (chp->ch_status & WDCS_BSY) return; break; default: if (chp->ch_status & (WDCS_BSY | WDCS_DRQ)) return; break; } } if (!wdc_atapi_drive_selected(chp, xfer->drive)) { wdc_set_drive(chp, xfer->drive); delay (1); } if (timeout) { int trigger_timeout = 1; switch (drvp->state) { case ATAPI_DEVICE_RESET_WAIT_STATE: errstring = "Device Reset Wait"; drvp->drive_flags &= ~DRIVE_DEVICE_RESET; break; case ATAPI_IDENTIFY_WAIT_STATE: errstring = "Identify"; if (!(chp->ch_status & WDCS_BSY) && (chp->ch_status & (WDCS_DRQ | WDCS_ERR))) trigger_timeout = 0; break; case ATAPI_PIOMODE_STATE: errstring = "Post-Identify"; if (!(chp->ch_status & (WDCS_BSY | WDCS_DRQ))) trigger_timeout = 0; break; case ATAPI_PIOMODE_WAIT_STATE: errstring = "PIOMODE"; if (chp->ch_status & (WDCS_BSY | WDCS_DRQ)) drvp->drive_flags &= ~DRIVE_MODE; else trigger_timeout = 0; break; case ATAPI_DMAMODE_WAIT_STATE: errstring = "dmamode"; if (chp->ch_status & (WDCS_BSY | WDCS_DRQ)) drvp->drive_flags &= ~(DRIVE_DMA | DRIVE_UDMA); else trigger_timeout = 0; break; default: errstring = "unknown state"; break; } if (trigger_timeout) goto timeout; } 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); switch (drvp->state) { /* My ATAPI slave device likes to assert DASP-/PDIAG- until it is DEVICE RESET. This causes the LED to stay on. There is a trade-off here. This drive will cause any play-back or seeks happening to be interrupted. Note that the bus reset that triggered this state (which may have been caused by the other drive on the chain) need not interrupt this playback. It happens to on my Smart & Friendly CD burner. - csapuntz@ */ case ATAPI_RESET_BASE_STATE: if ((drvp->drive_flags & DRIVE_DEVICE_RESET) == 0) { drvp->state = ATAPI_IDENTIFY_STATE; break; } wdccommandshort(chp, drvp->drive, ATAPI_DEVICE_RESET); drvp->state = ATAPI_DEVICE_RESET_WAIT_STATE; ret->delay = ATAPI_RESET_DELAY; ret->timeout = ATAPI_RESET_WAIT; break; case ATAPI_DEVICE_RESET_WAIT_STATE: /* FALLTHROUGH */ case ATAPI_IDENTIFY_STATE: wdccommandshort(chp, drvp->drive, ATAPI_IDENTIFY_DEVICE); drvp->state = ATAPI_IDENTIFY_WAIT_STATE; ret->delay = 10; ret->timeout = ATAPI_RESET_WAIT; break; case ATAPI_IDENTIFY_WAIT_STATE: { int idx = 0; while ((chp->ch_status & WDCS_DRQ) && idx++ < 20) { wdcbit_bucket(chp, 512); DELAY(1); wdc_atapi_update_status(chp); } drvp->state = ATAPI_PIOMODE_STATE; /* * Note, we can't go directly to set PIO mode * because the drive is free to assert BSY * after the transfer */ break; } case ATAPI_PIOMODE_STATE: /* Don't try to set mode if controller can't be adjusted */ if ((chp->wdc->cap & WDC_CAPABILITY_MODE) == 0) goto ready; /* Also don't try if the drive didn't report its mode */ if ((drvp->drive_flags & DRIVE_MODE) == 0) goto ready; /* SET FEATURES 0x08 is only for PIO mode > 2 */ if (drvp->PIO_mode <= 2) goto ready; wdccommand(chp, drvp->drive, SET_FEATURES, 0, 0, 0, 0x08 | drvp->PIO_mode, WDSF_SET_MODE); drvp->state = ATAPI_PIOMODE_WAIT_STATE; ret->timeout = ATAPI_CTRL_WAIT; ret->expect_irq = 1; break; case ATAPI_PIOMODE_WAIT_STATE: if (chp->wdc->cap & WDC_CAPABILITY_IRQACK) chp->wdc->irqack(chp); if (chp->ch_status & WDCS_ERR) { /* Downgrade straight to PIO mode 3 */ drvp->PIO_mode = 3; chp->wdc->set_modes(chp); } /* FALLTHROUGH */ case ATAPI_DMAMODE_STATE: if (drvp->drive_flags & DRIVE_UDMA) { wdccommand(chp, drvp->drive, SET_FEATURES, 0, 0, 0, 0x40 | drvp->UDMA_mode, WDSF_SET_MODE); } else if (drvp->drive_flags & DRIVE_DMA) { wdccommand(chp, drvp->drive, SET_FEATURES, 0, 0, 0, 0x20 | drvp->DMA_mode, WDSF_SET_MODE); } else { goto ready; } drvp->state = ATAPI_DMAMODE_WAIT_STATE; ret->timeout = ATAPI_CTRL_WAIT; ret->expect_irq = 1; break; case ATAPI_DMAMODE_WAIT_STATE: if (chp->wdc->cap & WDC_CAPABILITY_IRQACK) chp->wdc->irqack(chp); if (chp->ch_status & WDCS_ERR) drvp->drive_flags &= ~(DRIVE_DMA | DRIVE_UDMA); /* FALLTHROUGH */ case ATAPI_READY_STATE: ready: drvp->state = ATAPI_READY_STATE; xfer->next = wdc_atapi_real_start; break; } return; timeout: printf("%s:%d:%d: %s timed out\n", chp->wdc->sc_dev.dv_xname, chp->channel, xfer->drive, errstring); sc_xfer->error = XS_TIMEOUT; xfer->next = wdc_atapi_reset; return; }
void wdc_atapi_intr_data(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]; int len, ire; char *message; int tohost; len = (CHP_READ_REG(chp, wdr_cyl_hi) << 8) | CHP_READ_REG(chp, wdr_cyl_lo); WDC_LOG_REG(chp, wdr_cyl_lo, len); ire = CHP_READ_REG(chp, wdr_ireason); WDC_LOG_REG(chp, wdr_ireason, ire); if ((message = wdc_atapi_in_data_phase(xfer, len, ire))) { /* The drive has dropped BSY before setting up the registers correctly for DATA phase. This drive is not compliant with ATA/ATAPI-4. Give the drive 100ms to get its house in order before we try again. */ WDCDEBUG_PRINT(("wdc_atapi_intr: %s\n", message), DEBUG_ERRORS); if (!timeout) { ret->delay = 100; return; } } tohost = ((sc_xfer->flags & SCSI_DATA_IN) != 0 || (xfer->c_flags & C_SENSE) != 0); if (xfer->c_bcount >= len) { WDCDEBUG_PRINT(("wdc_atapi_intr: c_bcount %d len %d " "st 0x%b err 0x%x " "ire 0x%x\n", xfer->c_bcount, len, chp->ch_status, WDCS_BITS, chp->ch_error, ire), DEBUG_INTR); /* Common case */ if (!tohost) wdc_output_bytes(drvp, (u_int8_t *)xfer->databuf + xfer->c_skip, len); else wdc_input_bytes(drvp, (u_int8_t *)xfer->databuf + xfer->c_skip, len); xfer->c_skip += len; xfer->c_bcount -= len; } else { /* Exceptional case - drive want to transfer more data than we have buffer for */ if (!tohost) { /* Wouldn't it be better to just abort here rather than to write random stuff to drive? */ printf("wdc_atapi_intr: warning: device requesting " "%d bytes, only %d left in buffer\n", len, xfer->c_bcount); wdc_output_bytes(drvp, (u_int8_t *)xfer->databuf + xfer->c_skip, xfer->c_bcount); CHP_WRITE_RAW_MULTI_2(chp, NULL, len - xfer->c_bcount); } else { printf("wdc_atapi_intr: warning: reading only " "%d of %d bytes\n", xfer->c_bcount, len); wdc_input_bytes(drvp, (char *)xfer->databuf + xfer->c_skip, xfer->c_bcount); wdcbit_bucket(chp, len - xfer->c_bcount); } xfer->c_skip += xfer->c_bcount; xfer->c_bcount = 0; } ret->expect_irq = 1; xfer->next = wdc_atapi_pio_intr; 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); }