Beispiel #1
0
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);
}
Beispiel #2
0
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;
}
Beispiel #3
0
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);
}