/* $NetBSD: ata_wdc.c,v 1.120 2021/10/05 08:01:05 rin Exp $ */
/*
* Copyright (c) 1998, 2001, 2003 Manuel Bouyer.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
/*-
* Copyright (c) 1998, 2004 The NetBSD Foundation, Inc.
* All rights reserved.
*
* This code is derived from software contributed to The NetBSD Foundation
* by Charles M. Hannum, by Onno van der Linden and by Manuel Bouyer.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
/* Do control operations specially. */
if (__predict_false(drvp->state < READY)) {
/*
* Actually, we want to be careful not to mess with the control
* state if the device is currently busy, but we can assume
* that we never get to this point if that's the case.
*/
/* If it's not a polled command, we need the kernel thread */
if ((xfer->c_flags & C_POLL) == 0 && !ata_is_thread_run(chp))
return ATASTART_TH;
/*
* disable interrupts, all commands here should be quick
* enough to be able to poll, and we don't go here that often
*/
if (! (wdc->cap & WDC_CAPABILITY_NO_AUXCTL))
bus_space_write_1(wdr->ctl_iot, wdr->ctl_ioh,
wd_aux_ctlr, WDCTL_4BIT | WDCTL_IDS);
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));
DELAY(10);
errstring = "wait";
if (wdcwait(chp, WDCS_DRDY, WDCS_DRDY, ATA_DELAY, wait_flags,
&tfd))
goto ctrltimeout;
wdccommandshort(chp, xfer->c_drive, WDCC_RECAL);
/* Wait for at last 400ns for status bit to be valid */
DELAY(1);
errstring = "recal";
if (wdcwait(chp, WDCS_DRDY, WDCS_DRDY, ATA_DELAY, wait_flags,
&tfd))
goto ctrltimeout;
if (ATACH_ST(tfd) & (WDCS_ERR | WDCS_DWF))
goto ctrlerror;
/* Don't try to set modes if controller can't be adjusted */
if (atac->atac_set_modes == NULL)
goto geometry;
/* Also don't try if the drive didn't report its mode */
if ((drvp->drive_flags & ATA_DRIVE_MODE) == 0)
goto geometry;
wdccommand(chp, drvp->drive, SET_FEATURES, 0, 0, 0,
0x08 | drvp->PIO_mode, WDSF_SET_MODE);
errstring = "piomode";
if (wdcwait(chp, WDCS_DRDY, WDCS_DRDY, ATA_DELAY, wait_flags,
&tfd))
goto ctrltimeout;
if (ATACH_ST(tfd) & (WDCS_ERR | WDCS_DWF))
goto ctrlerror;
#if NATA_DMA
#if NATA_UDMA
if (drvp->drive_flags & ATA_DRIVE_UDMA) {
wdccommand(chp, drvp->drive, SET_FEATURES, 0, 0, 0,
0x40 | drvp->UDMA_mode, WDSF_SET_MODE);
} else
#endif
if (drvp->drive_flags & ATA_DRIVE_DMA) {
wdccommand(chp, drvp->drive, SET_FEATURES, 0, 0, 0,
0x20 | drvp->DMA_mode, WDSF_SET_MODE);
} else {
goto geometry;
}
errstring = "dmamode";
if (wdcwait(chp, WDCS_DRDY, WDCS_DRDY, ATA_DELAY, wait_flags,
&tfd))
goto ctrltimeout;
if (ATACH_ST(tfd) & (WDCS_ERR | WDCS_DWF))
goto ctrlerror;
#endif /* NATA_DMA */
geometry:
if (ata_bio->flags & ATA_LBA)
goto multimode;
wdccommand(chp, xfer->c_drive, WDCC_IDP,
drvp->lp->d_ncylinders,
drvp->lp->d_ntracks - 1, 0, drvp->lp->d_nsectors,
(drvp->lp->d_type == DKTYPE_ST506) ?
drvp->lp->d_precompcyl / 4 : 0);
errstring = "geometry";
if (wdcwait(chp, WDCS_DRDY, WDCS_DRDY, ATA_DELAY, wait_flags,
&tfd))
goto ctrltimeout;
if (ATACH_ST(tfd) & (WDCS_ERR | WDCS_DWF))
goto ctrlerror;
multimode:
if (drvp->multi == 1)
goto ready;
wdccommand(chp, xfer->c_drive, WDCC_SETMULTI, 0, 0, 0,
drvp->multi, 0);
errstring = "setmulti";
if (wdcwait(chp, WDCS_DRDY, WDCS_DRDY, ATA_DELAY, wait_flags,
&tfd))
goto ctrltimeout;
if (ATACH_ST(tfd) & (WDCS_ERR | WDCS_DWF))
goto ctrlerror;
ready:
drvp->state = READY;
/*
* The drive is usable now
*/
if (! (wdc->cap & WDC_CAPABILITY_NO_AUXCTL))
bus_space_write_1(wdr->ctl_iot, wdr->ctl_ioh,
wd_aux_ctlr, WDCTL_4BIT);
delay(10); /* some drives need a little delay here */
}
static int
wdc_ata_bio_poll(struct ata_channel *chp, struct ata_xfer *xfer)
{
/* Wait for at last 400ns for status bit to be valid */
delay(1);
#if NATA_DMA
if (chp->ch_flags & ATACH_DMA_WAIT) {
wdc_dmawait(chp, xfer, ATA_DELAY);
chp->ch_flags &= ~ATACH_DMA_WAIT;
}
#endif
wdc_ata_bio_intr(chp, xfer, 0);
return (xfer->c_bio.flags & ATA_ITSDONE) ? ATAPOLL_DONE : ATAPOLL_AGAIN;
}
/* Is it not a transfer, but a control operation? */
if (drvp->state < READY) {
printf("%s:%d:%d: bad state %d in wdc_ata_bio_intr\n",
device_xname(atac->atac_dev), chp->ch_channel, xfer->c_drive,
drvp->state);
panic("wdc_ata_bio_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) {
ata_bio->error = TIMEOUT;
goto err;
}
#if NATA_PIOBM
/* Transfer-done interrupt for busmastering PIO read */
if ((xfer->c_flags & C_PIOBM) && (chp->ch_flags & ATACH_PIOBM_WAIT)) {
chp->ch_flags &= ~ATACH_PIOBM_WAIT;
goto end;
}
#endif
/* Ack interrupt done by wdc_wait_for_unbusy */
if (wdc_wait_for_unbusy(chp,
(irq == 0) ? ATA_DELAY : 0, AT_POLL, &tfd) < 0) {
if (irq && (xfer->c_flags & C_TIMEOU) == 0) {
ata_channel_unlock(chp);
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);
ata_bio->error = TIMEOUT;
goto err;
}
if (wdc->irqack)
wdc->irqack(chp);
drv_err = wdc_ata_err(drvp, ata_bio, tfd);
#if NATA_DMA
/* If we were using DMA, Turn off the DMA channel and check for error */
if (xfer->c_flags & C_DMA) {
if (ata_bio->flags & ATA_POLL) {
/*
* IDE drives deassert WDCS_BSY before transfer is
* complete when using DMA. Polling for DRQ to deassert
* is not enough DRQ is not required to be
* asserted for DMA transfers, so poll for DRDY.
*/
if (wdcwait(chp, WDCS_DRDY | WDCS_DRQ, WDCS_DRDY,
ATA_DELAY, ATA_POLL, &tfd) == WDCWAIT_TOUT) {
printf("%s:%d:%d: polled transfer timed out "
"(st=0x%x)\n",
device_xname(atac->atac_dev),
chp->ch_channel, xfer->c_drive,
ATACH_ST(tfd));
ata_bio->error = TIMEOUT;
drv_err = WDC_ATA_ERR;
}
}
if (wdc->dma_status != 0) {
if (drv_err != WDC_ATA_ERR) {
ata_bio->error = ERR_DMA;
drv_err = WDC_ATA_ERR;
}
}
if (ATACH_ST(tfd) & WDCS_DRQ) {
if (drv_err != WDC_ATA_ERR) {
printf("%s:%d:%d: intr with DRQ (st=0x%x)\n",
device_xname(atac->atac_dev),
chp->ch_channel,
xfer->c_drive, ATACH_ST(tfd));
ata_bio->error = TIMEOUT;
drv_err = WDC_ATA_ERR;
}
}
if (drv_err != WDC_ATA_ERR)
goto end;
if (ata_bio->r_error & WDCE_CRC || ata_bio->error == ERR_DMA) {
ata_dmaerr(drvp,
(xfer->c_flags & C_POLL) ? AT_POLL : 0);
goto err;
}
}
#endif /* NATA_DMA */
/* if we had an error, end */
if (drv_err == WDC_ATA_ERR)
goto err;
/* If this was a read and not using DMA, fetch the data. */
if ((ata_bio->flags & ATA_READ) != 0) {
if ((ATACH_ST(tfd) & WDCS_DRQ) != WDCS_DRQ) {
printf("%s:%d:%d: read intr before drq\n",
device_xname(atac->atac_dev), chp->ch_channel,
xfer->c_drive);
ata_bio->error = TIMEOUT;
goto err;
}
#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, ata_bio->nbytes,
WDC_PIOBM_XFER_IRQ);
chp->ch_flags |= ATACH_DMA_WAIT | ATACH_PIOBM_WAIT;
ata_channel_unlock(chp);
return 1;
}
#endif
wdc->datain_pio(chp, drvp->drive_flags,
(char *)xfer->c_databuf + xfer->c_skip, ata_bio->nbytes);
}
/* See if this transfer is complete. */
if (xfer->c_bcount > 0) {
if ((ata_bio->flags & ATA_POLL) == 0) {
/* Start the next operation */
KASSERT((chp->ch_flags & ATACH_IRQ_WAIT) == 0);
callout_stop(&chp->c_timo_callout);
ata_xfer_start(xfer);
} else {
/*
* Let ata_xfer_start() do the loop;
* see wdc_ata_bio_poll().
*/
}
ata_channel_unlock(chp);
return 1;
}
/* Done with this transfer */
ata_bio->error = NOERROR;
err: ata_channel_unlock(chp);
wdc_ata_bio_done(chp, xfer);
return 1;
}