version 1.55, 2002/09/27 15:37:33 |
version 1.55.6.7, 2005/11/10 14:07:47 |
|
|
* 3. All advertising materials mentioning features or use of this software |
* 3. All advertising materials mentioning features or use of this software |
* must display the following acknowledgement: |
* must display the following acknowledgement: |
* This product includes software developed by Manuel Bouyer. |
* This product includes software developed by Manuel Bouyer. |
* 4. Neither the name of the University nor the names of its contributors |
* 4. The name of the author may not be used to endorse or promote products |
* may be used to endorse or promote products derived from this software |
* derived from this software without specific prior written permission. |
* without specific prior written permission. |
|
* |
* |
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR |
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR |
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES |
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES |
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. |
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. |
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, |
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, |
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT |
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT |
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF |
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF |
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
* |
|
*/ |
*/ |
|
|
#include <sys/cdefs.h> |
#include <sys/cdefs.h> |
__KERNEL_RCSID(0, "$NetBSD$"); |
__KERNEL_RCSID(0, "$NetBSD$"); |
|
|
#ifndef WDCDEBUG |
#ifndef ATADEBUG |
#define WDCDEBUG |
#define ATADEBUG |
#endif /* WDCDEBUG */ |
#endif /* ATADEBUG */ |
|
|
#include <sys/param.h> |
#include <sys/param.h> |
#include <sys/systm.h> |
#include <sys/systm.h> |
Line 54 __KERNEL_RCSID(0, "$NetBSD$"); |
|
Line 52 __KERNEL_RCSID(0, "$NetBSD$"); |
|
#include <machine/bus.h> |
#include <machine/bus.h> |
|
|
#ifndef __BUS_SPACE_HAS_STREAM_METHODS |
#ifndef __BUS_SPACE_HAS_STREAM_METHODS |
#define bus_space_write_multi_stream_2 bus_space_write_multi_2 |
#define bus_space_write_multi_stream_2 bus_space_write_multi_2 |
#define bus_space_write_multi_stream_4 bus_space_write_multi_4 |
#define bus_space_write_multi_stream_4 bus_space_write_multi_4 |
#define bus_space_read_multi_stream_2 bus_space_read_multi_2 |
#define bus_space_read_multi_stream_2 bus_space_read_multi_2 |
#define bus_space_read_multi_stream_4 bus_space_read_multi_4 |
#define bus_space_read_multi_stream_4 bus_space_read_multi_4 |
#endif /* __BUS_SPACE_HAS_STREAM_METHODS */ |
#endif /* __BUS_SPACE_HAS_STREAM_METHODS */ |
|
|
#include <dev/ata/atareg.h> |
#include <dev/ata/atareg.h> |
Line 72 __KERNEL_RCSID(0, "$NetBSD$"); |
|
Line 70 __KERNEL_RCSID(0, "$NetBSD$"); |
|
#define DEBUG_STATUS 0x04 |
#define DEBUG_STATUS 0x04 |
#define DEBUG_FUNCS 0x08 |
#define DEBUG_FUNCS 0x08 |
#define DEBUG_PROBE 0x10 |
#define DEBUG_PROBE 0x10 |
#ifdef WDCDEBUG |
#ifdef ATADEBUG |
int wdcdebug_atapi_mask = 0; |
int wdcdebug_atapi_mask = 0; |
#define WDCDEBUG_PRINT(args, level) \ |
#define ATADEBUG_PRINT(args, level) \ |
if (wdcdebug_atapi_mask & (level)) \ |
if (wdcdebug_atapi_mask & (level)) \ |
printf args |
printf args |
#else |
#else |
#define WDCDEBUG_PRINT(args, level) |
#define ATADEBUG_PRINT(args, level) |
#endif |
#endif |
|
|
#define ATAPI_DELAY 10 /* 10 ms, this is used only before sending a cmd */ |
#define ATAPI_DELAY 10 /* 10 ms, this is used only before sending a cmd */ |
int wdc_atapi_get_params __P((struct scsipi_channel *, int, int, |
#define ATAPI_MODE_DELAY 1000 /* 1s, timeout for SET_FEATYRE cmds */ |
struct ataparams *)); |
|
void wdc_atapi_probe_device __P((struct atapibus_softc *, int)); |
static int wdc_atapi_get_params(struct scsipi_channel *, int, |
void wdc_atapi_minphys __P((struct buf *bp)); |
struct ataparams *); |
void wdc_atapi_start __P((struct channel_softc *,struct wdc_xfer *)); |
static void wdc_atapi_probe_device(struct atapibus_softc *, int); |
int wdc_atapi_intr __P((struct channel_softc *, struct wdc_xfer *, int)); |
static void wdc_atapi_minphys (struct buf *bp); |
void wdc_atapi_kill_xfer __P((struct channel_softc *, struct wdc_xfer *)); |
static void wdc_atapi_start(struct ata_channel *,struct ata_xfer *); |
int wdc_atapi_ctrl __P((struct channel_softc *, struct wdc_xfer *, int)); |
static int wdc_atapi_intr(struct ata_channel *, struct ata_xfer *, int); |
void wdc_atapi_phase_complete __P((struct wdc_xfer *)); |
static void wdc_atapi_kill_xfer(struct ata_channel *, |
void wdc_atapi_done __P((struct channel_softc *, struct wdc_xfer *)); |
struct ata_xfer *, int); |
void wdc_atapi_reset __P((struct channel_softc *, struct wdc_xfer *)); |
static void wdc_atapi_phase_complete(struct ata_xfer *); |
void wdc_atapi_scsipi_request __P((struct scsipi_channel *, |
static void wdc_atapi_done(struct ata_channel *, struct ata_xfer *); |
scsipi_adapter_req_t, void *)); |
static void wdc_atapi_reset(struct ata_channel *, struct ata_xfer *); |
void wdc_atapi_kill_pending __P((struct scsipi_periph *)); |
static void wdc_atapi_scsipi_request(struct scsipi_channel *, |
void wdc_atapi_polldsc __P((void *arg)); |
scsipi_adapter_req_t, void *); |
|
static void wdc_atapi_kill_pending(struct scsipi_periph *); |
|
static void wdc_atapi_polldsc(void *arg); |
|
|
#define MAX_SIZE MAXPHYS |
#define MAX_SIZE MAXPHYS |
|
|
const struct scsipi_bustype wdc_atapi_bustype = { |
static const struct scsipi_bustype wdc_atapi_bustype = { |
SCSIPI_BUSTYPE_ATAPI, |
SCSIPI_BUSTYPE_ATAPI, |
atapi_scsipi_cmd, |
atapi_scsipi_cmd, |
atapi_interpret_sense, |
atapi_interpret_sense, |
Line 109 const struct scsipi_bustype wdc_atapi_bu |
|
Line 109 const struct scsipi_bustype wdc_atapi_bu |
|
}; |
}; |
|
|
void |
void |
wdc_atapibus_attach(chp) |
wdc_atapibus_attach(struct atabus_softc *ata_sc) |
struct channel_softc *chp; |
|
{ |
{ |
struct wdc_softc *wdc = chp->wdc; |
struct ata_channel *chp = ata_sc->sc_chan; |
struct scsipi_adapter *adapt = &wdc->sc_atapi_adapter._generic; |
struct atac_softc *atac = chp->ch_atac; |
|
struct scsipi_adapter *adapt = &atac->atac_atapi_adapter._generic; |
struct scsipi_channel *chan = &chp->ch_atapi_channel; |
struct scsipi_channel *chan = &chp->ch_atapi_channel; |
|
|
/* |
/* |
* Fill in the scsipi_adapter. |
* Fill in the scsipi_adapter. |
*/ |
*/ |
adapt->adapt_dev = &wdc->sc_dev; |
adapt->adapt_dev = &atac->atac_dev; |
adapt->adapt_nchannels = wdc->nchannels; |
adapt->adapt_nchannels = atac->atac_nchannels; |
adapt->adapt_request = wdc_atapi_scsipi_request; |
adapt->adapt_request = wdc_atapi_scsipi_request; |
adapt->adapt_minphys = wdc_atapi_minphys; |
adapt->adapt_minphys = wdc_atapi_minphys; |
if (wdc->cap & WDC_CAPABILITY_NOIRQ) |
if (atac->atac_cap & ATAC_CAP_NOIRQ) |
adapt->adapt_flags |= SCSIPI_ADAPT_POLL_ONLY; |
adapt->adapt_flags |= SCSIPI_ADAPT_POLL_ONLY; |
wdc->sc_atapi_adapter.atapi_probe_device = wdc_atapi_probe_device; |
atac->atac_atapi_adapter.atapi_probe_device = wdc_atapi_probe_device; |
|
|
/* |
/* |
* Fill in the scsipi_channel. |
* Fill in the scsipi_channel. |
Line 133 wdc_atapibus_attach(chp) |
|
Line 133 wdc_atapibus_attach(chp) |
|
memset(chan, 0, sizeof(*chan)); |
memset(chan, 0, sizeof(*chan)); |
chan->chan_adapter = adapt; |
chan->chan_adapter = adapt; |
chan->chan_bustype = &wdc_atapi_bustype; |
chan->chan_bustype = &wdc_atapi_bustype; |
chan->chan_channel = chp->channel; |
chan->chan_channel = chp->ch_channel; |
chan->chan_flags = SCSIPI_CHAN_OPENINGS; |
chan->chan_flags = SCSIPI_CHAN_OPENINGS; |
chan->chan_openings = 1; |
chan->chan_openings = 1; |
chan->chan_max_periph = 1; |
chan->chan_max_periph = 1; |
chan->chan_ntargets = 2; |
chan->chan_ntargets = 2; |
chan->chan_nluns = 1; |
chan->chan_nluns = 1; |
|
|
chp->atapibus = config_found(&wdc->sc_dev, chan, atapiprint); |
chp->atapibus = config_found_ia(&ata_sc->sc_dev, "atapi", chan, |
|
atapiprint); |
} |
} |
|
|
void |
static void |
wdc_atapi_minphys(bp) |
wdc_atapi_minphys(struct buf *bp) |
struct buf *bp; |
|
{ |
{ |
|
|
if (bp->b_bcount > MAX_SIZE) |
if (bp->b_bcount > MAX_SIZE) |
Line 158 wdc_atapi_minphys(bp) |
|
Line 158 wdc_atapi_minphys(bp) |
|
* |
* |
* Must be called at splbio(). |
* Must be called at splbio(). |
*/ |
*/ |
void |
static void |
wdc_atapi_kill_pending(periph) |
wdc_atapi_kill_pending(struct scsipi_periph *periph) |
struct scsipi_periph *periph; |
|
{ |
{ |
struct wdc_softc *wdc = |
struct atac_softc *atac = |
(void *)periph->periph_channel->chan_adapter->adapt_dev; |
(void *)periph->periph_channel->chan_adapter->adapt_dev; |
struct channel_softc *chp = |
struct ata_channel *chp = |
wdc->channels[periph->periph_channel->chan_channel]; |
atac->atac_channels[periph->periph_channel->chan_channel]; |
|
|
wdc_kill_pending(chp); |
ata_kill_pending(&chp->ch_drive[periph->periph_target]); |
} |
} |
|
|
void |
static void |
wdc_atapi_kill_xfer(chp, xfer) |
wdc_atapi_kill_xfer(struct ata_channel *chp, struct ata_xfer *xfer, int reason) |
struct channel_softc *chp; |
|
struct wdc_xfer *xfer; |
|
{ |
{ |
struct scsipi_xfer *sc_xfer = xfer->cmd; |
struct scsipi_xfer *sc_xfer = xfer->c_cmd; |
|
|
callout_stop(&chp->ch_callout); |
|
/* remove this command from xfer queue */ |
/* remove this command from xfer queue */ |
wdc_free_xfer(chp, xfer); |
switch (reason) { |
sc_xfer->error = XS_DRIVER_STUFFUP; |
case KILL_GONE: |
|
sc_xfer->error = XS_DRIVER_STUFFUP; |
|
break; |
|
case KILL_RESET: |
|
sc_xfer->error = XS_RESET; |
|
break; |
|
default: |
|
printf("wdc_ata_bio_kill_xfer: unknown reason %d\n", |
|
reason); |
|
panic("wdc_ata_bio_kill_xfer"); |
|
} |
|
ata_free_xfer(chp, xfer); |
scsipi_done(sc_xfer); |
scsipi_done(sc_xfer); |
} |
} |
|
|
int |
static int |
wdc_atapi_get_params(chan, drive, flags, id) |
wdc_atapi_get_params(struct scsipi_channel *chan, int drive, |
struct scsipi_channel *chan; |
struct ataparams *id) |
int drive, flags; |
|
struct ataparams *id; |
|
{ |
{ |
struct wdc_softc *wdc = (void *)chan->chan_adapter->adapt_dev; |
struct wdc_softc *wdc = (void *)chan->chan_adapter->adapt_dev; |
struct channel_softc *chp = wdc->channels[chan->chan_channel]; |
struct atac_softc *atac = &wdc->sc_atac; |
struct wdc_command wdc_c; |
struct wdc_regs *wdr = &wdc->regs[chan->chan_channel]; |
|
struct ata_channel *chp = atac->atac_channels[chan->chan_channel]; |
|
struct ata_command ata_c; |
|
|
/* if no ATAPI device detected at wdc attach time, skip */ |
/* if no ATAPI device detected at wdc attach time, skip */ |
/* |
|
* XXX this will break scsireprobe if this is of any interest for |
|
* ATAPI devices one day. |
|
*/ |
|
if ((chp->ch_drive[drive].drive_flags & DRIVE_ATAPI) == 0) { |
if ((chp->ch_drive[drive].drive_flags & DRIVE_ATAPI) == 0) { |
WDCDEBUG_PRINT(("wdc_atapi_get_params: drive %d not present\n", |
ATADEBUG_PRINT(("wdc_atapi_get_params: drive %d not present\n", |
drive), DEBUG_PROBE); |
drive), DEBUG_PROBE); |
return -1; |
return -1; |
} |
} |
memset(&wdc_c, 0, sizeof(struct wdc_command)); |
|
wdc_c.r_command = ATAPI_SOFT_RESET; |
memset(&ata_c, 0, sizeof(struct ata_command)); |
wdc_c.r_st_bmask = 0; |
ata_c.r_command = ATAPI_SOFT_RESET; |
wdc_c.r_st_pmask = 0; |
ata_c.r_st_bmask = 0; |
wdc_c.flags = AT_POLL; |
ata_c.r_st_pmask = 0; |
wdc_c.timeout = WDC_RESET_WAIT; |
ata_c.flags = AT_WAIT | AT_POLL; |
if (wdc_exec_command(&chp->ch_drive[drive], &wdc_c) != WDC_COMPLETE) { |
ata_c.timeout = WDC_RESET_WAIT; |
|
if (wdc_exec_command(&chp->ch_drive[drive], &ata_c) != ATACMD_COMPLETE) { |
printf("wdc_atapi_get_params: ATAPI_SOFT_RESET failed for" |
printf("wdc_atapi_get_params: ATAPI_SOFT_RESET failed for" |
" drive %s:%d:%d: driver failed\n", |
" drive %s:%d:%d: driver failed\n", |
chp->wdc->sc_dev.dv_xname, chp->channel, drive); |
atac->atac_dev.dv_xname, chp->ch_channel, drive); |
panic("wdc_atapi_get_params"); |
panic("wdc_atapi_get_params"); |
} |
} |
if (wdc_c.flags & (AT_ERROR | AT_TIMEOU | AT_DF)) { |
if (ata_c.flags & (AT_ERROR | AT_TIMEOU | AT_DF)) { |
WDCDEBUG_PRINT(("wdc_atapi_get_params: ATAPI_SOFT_RESET " |
ATADEBUG_PRINT(("wdc_atapi_get_params: ATAPI_SOFT_RESET " |
"failed for drive %s:%d:%d: error 0x%x\n", |
"failed for drive %s:%d:%d: error 0x%x\n", |
chp->wdc->sc_dev.dv_xname, chp->channel, drive, |
atac->atac_dev.dv_xname, chp->ch_channel, drive, |
wdc_c.r_error), DEBUG_PROBE); |
ata_c.r_error), DEBUG_PROBE); |
return -1; |
return -1; |
} |
} |
chp->ch_drive[drive].state = 0; |
chp->ch_drive[drive].state = 0; |
|
|
bus_space_read_1(chp->cmd_iot, chp->cmd_ioh, wd_status); |
bus_space_read_1(wdr->cmd_iot, wdr->cmd_iohs[wd_status], 0); |
|
|
/* Some ATAPI devices need a bit more time after software reset. */ |
/* Some ATAPI devices need a bit more time after software reset. */ |
delay(5000); |
delay(5000); |
if (ata_get_params(&chp->ch_drive[drive], AT_POLL, id) != 0) { |
if (ata_get_params(&chp->ch_drive[drive], AT_WAIT, id) != 0) { |
WDCDEBUG_PRINT(("wdc_atapi_get_params: ATAPI_IDENTIFY_DEVICE " |
ATADEBUG_PRINT(("wdc_atapi_get_params: ATAPI_IDENTIFY_DEVICE " |
"failed for drive %s:%d:%d: error 0x%x\n", |
"failed for drive %s:%d:%d: error 0x%x\n", |
chp->wdc->sc_dev.dv_xname, chp->channel, drive, |
atac->atac_dev.dv_xname, chp->ch_channel, drive, |
wdc_c.r_error), DEBUG_PROBE); |
ata_c.r_error), DEBUG_PROBE); |
return -1; |
return -1; |
} |
} |
return 0; |
return 0; |
} |
} |
|
|
void |
static void |
wdc_atapi_probe_device(sc, target) |
wdc_atapi_probe_device(struct atapibus_softc *sc, int target) |
struct atapibus_softc *sc; |
|
int target; |
|
{ |
{ |
struct scsipi_channel *chan = sc->sc_channel; |
struct scsipi_channel *chan = sc->sc_channel; |
struct scsipi_periph *periph; |
struct scsipi_periph *periph; |
struct ataparams ids; |
struct ataparams ids; |
struct ataparams *id = &ids; |
struct ataparams *id = &ids; |
struct wdc_softc *wdc = (void *)chan->chan_adapter->adapt_dev; |
struct wdc_softc *wdc = (void *)chan->chan_adapter->adapt_dev; |
struct channel_softc *chp = wdc->channels[chan->chan_channel]; |
struct atac_softc *atac = &wdc->sc_atac; |
|
struct ata_channel *chp = atac->atac_channels[chan->chan_channel]; |
struct ata_drive_datas *drvp = &chp->ch_drive[target]; |
struct ata_drive_datas *drvp = &chp->ch_drive[target]; |
struct scsipibus_attach_args sa; |
struct scsipibus_attach_args sa; |
char serial_number[21], model[41], firmware_revision[9]; |
char serial_number[21], model[41], firmware_revision[9]; |
|
int s; |
|
|
/* skip if already attached */ |
/* skip if already attached */ |
if (scsipi_lookup_periph(chan, target, 0) != NULL) |
if (scsipi_lookup_periph(chan, target, 0) != NULL) |
return; |
return; |
|
|
if (wdc_atapi_get_params(chan, target, |
if (wdc_atapi_get_params(chan, target, id) == 0) { |
XS_CTL_POLL|XS_CTL_NOSLEEP, id) == 0) { |
|
#ifdef ATAPI_DEBUG_PROBE |
#ifdef ATAPI_DEBUG_PROBE |
printf("%s drive %d: cmdsz 0x%x drqtype 0x%x\n", |
printf("%s drive %d: cmdsz 0x%x drqtype 0x%x\n", |
sc->sc_dev.dv_xname, target, |
sc->sc_dev.dv_xname, target, |
Line 277 wdc_atapi_probe_device(sc, target) |
|
Line 280 wdc_atapi_probe_device(sc, target) |
|
periph->periph_switch = &atapi_probe_periphsw; |
periph->periph_switch = &atapi_probe_periphsw; |
periph->periph_target = target; |
periph->periph_target = target; |
periph->periph_lun = 0; |
periph->periph_lun = 0; |
|
periph->periph_quirks = PQUIRK_ONLYBIG; |
|
|
#ifdef SCSIPI_DEBUG |
#ifdef SCSIPI_DEBUG |
if (SCSIPI_DEBUG_TYPE == SCSIPI_BUSTYPE_ATAPI && |
if (SCSIPI_DEBUG_TYPE == SCSIPI_BUSTYPE_ATAPI && |
Line 286 wdc_atapi_probe_device(sc, target) |
|
Line 290 wdc_atapi_probe_device(sc, target) |
|
periph->periph_type = ATAPI_CFG_TYPE(id->atap_config); |
periph->periph_type = ATAPI_CFG_TYPE(id->atap_config); |
if (id->atap_config & ATAPI_CFG_REMOV) |
if (id->atap_config & ATAPI_CFG_REMOV) |
periph->periph_flags |= PERIPH_REMOVABLE; |
periph->periph_flags |= PERIPH_REMOVABLE; |
if (periph->periph_type == T_SEQUENTIAL) |
if (periph->periph_type == T_SEQUENTIAL) { |
|
s = splbio(); |
drvp->drive_flags |= DRIVE_ATAPIST; |
drvp->drive_flags |= DRIVE_ATAPIST; |
|
splx(s); |
|
} |
|
|
sa.sa_periph = periph; |
sa.sa_periph = periph; |
sa.sa_inqbuf.type = ATAPI_CFG_TYPE(id->atap_config); |
sa.sa_inqbuf.type = ATAPI_CFG_TYPE(id->atap_config); |
Line 311 wdc_atapi_probe_device(sc, target) |
|
Line 318 wdc_atapi_probe_device(sc, target) |
|
drvp->drv_softc = atapi_probe_device(sc, target, periph, &sa); |
drvp->drv_softc = atapi_probe_device(sc, target, periph, &sa); |
|
|
if (drvp->drv_softc) |
if (drvp->drv_softc) |
wdc_probe_caps(drvp); |
ata_probe_caps(drvp); |
|
else { |
|
s = splbio(); |
|
drvp->drive_flags &= ~DRIVE_ATAPI; |
|
splx(s); |
|
} |
|
} else { |
|
s = splbio(); |
|
drvp->drive_flags &= ~DRIVE_ATAPI; |
|
splx(s); |
} |
} |
} |
} |
|
|
void |
static void |
wdc_atapi_scsipi_request(chan, req, arg) |
wdc_atapi_scsipi_request(struct scsipi_channel *chan, scsipi_adapter_req_t req, |
struct scsipi_channel *chan; |
void *arg) |
scsipi_adapter_req_t req; |
|
void *arg; |
|
{ |
{ |
struct scsipi_adapter *adapt = chan->chan_adapter; |
struct scsipi_adapter *adapt = chan->chan_adapter; |
struct scsipi_periph *periph; |
struct scsipi_periph *periph; |
struct scsipi_xfer *sc_xfer; |
struct scsipi_xfer *sc_xfer; |
struct wdc_softc *wdc = (void *)adapt->adapt_dev; |
struct wdc_softc *wdc = (void *)adapt->adapt_dev; |
struct wdc_xfer *xfer; |
struct atac_softc *atac = &wdc->sc_atac; |
|
struct ata_xfer *xfer; |
int channel = chan->chan_channel; |
int channel = chan->chan_channel; |
int drive, s; |
int drive, s; |
|
|
Line 335 wdc_atapi_scsipi_request(chan, req, arg) |
|
Line 350 wdc_atapi_scsipi_request(chan, req, arg) |
|
periph = sc_xfer->xs_periph; |
periph = sc_xfer->xs_periph; |
drive = periph->periph_target; |
drive = periph->periph_target; |
|
|
WDCDEBUG_PRINT(("wdc_atapi_scsipi_request %s:%d:%d\n", |
ATADEBUG_PRINT(("wdc_atapi_scsipi_request %s:%d:%d\n", |
wdc->sc_dev.dv_xname, channel, drive), DEBUG_XFERS); |
atac->atac_dev.dv_xname, channel, drive), DEBUG_XFERS); |
if ((wdc->sc_dev.dv_flags & DVF_ACTIVE) == 0) { |
if ((atac->atac_dev.dv_flags & DVF_ACTIVE) == 0) { |
sc_xfer->error = XS_DRIVER_STUFFUP; |
sc_xfer->error = XS_DRIVER_STUFFUP; |
scsipi_done(sc_xfer); |
scsipi_done(sc_xfer); |
return; |
return; |
} |
} |
|
|
xfer = wdc_get_xfer(WDC_NOSLEEP); |
xfer = ata_get_xfer(ATAXF_NOSLEEP); |
if (xfer == NULL) { |
if (xfer == NULL) { |
sc_xfer->error = XS_RESOURCE_SHORTAGE; |
sc_xfer->error = XS_RESOURCE_SHORTAGE; |
scsipi_done(sc_xfer); |
scsipi_done(sc_xfer); |
Line 352 wdc_atapi_scsipi_request(chan, req, arg) |
|
Line 367 wdc_atapi_scsipi_request(chan, req, arg) |
|
|
|
if (sc_xfer->xs_control & XS_CTL_POLL) |
if (sc_xfer->xs_control & XS_CTL_POLL) |
xfer->c_flags |= C_POLL; |
xfer->c_flags |= C_POLL; |
xfer->drive = drive; |
if ((atac->atac_channels[channel]->ch_drive[drive].drive_flags & |
|
(DRIVE_DMA | DRIVE_UDMA)) && sc_xfer->datalen > 0) |
|
xfer->c_flags |= C_DMA; |
|
xfer->c_drive = drive; |
xfer->c_flags |= C_ATAPI; |
xfer->c_flags |= C_ATAPI; |
if (sc_xfer->cmd->opcode == GPCMD_REPORT_KEY || |
if (sc_xfer->cmd->opcode == GPCMD_REPORT_KEY || |
sc_xfer->cmd->opcode == GPCMD_SEND_KEY || |
sc_xfer->cmd->opcode == GPCMD_SEND_KEY || |
Line 361 wdc_atapi_scsipi_request(chan, req, arg) |
|
Line 379 wdc_atapi_scsipi_request(chan, req, arg) |
|
* DVD authentication commands must always be done in |
* DVD authentication commands must always be done in |
* PIO mode. |
* PIO mode. |
*/ |
*/ |
xfer->c_flags |= C_FORCEPIO; |
xfer->c_flags &= ~C_DMA; |
} |
} |
/* |
/* |
* DMA can't deal with transfers which are not a multiple of |
* DMA can't deal with transfers which are not a multiple of |
Line 372 wdc_atapi_scsipi_request(chan, req, arg) |
|
Line 390 wdc_atapi_scsipi_request(chan, req, arg) |
|
* 4 bytes. |
* 4 bytes. |
*/ |
*/ |
if (sc_xfer->datalen < 4 || (sc_xfer->datalen & 0x01)) |
if (sc_xfer->datalen < 4 || (sc_xfer->datalen & 0x01)) |
xfer->c_flags |= C_FORCEPIO; |
xfer->c_flags &= ~C_DMA; |
|
|
xfer->cmd = sc_xfer; |
xfer->c_cmd = sc_xfer; |
xfer->databuf = sc_xfer->data; |
xfer->c_databuf = sc_xfer->data; |
xfer->c_bcount = sc_xfer->datalen; |
xfer->c_bcount = sc_xfer->datalen; |
xfer->c_start = wdc_atapi_start; |
xfer->c_start = wdc_atapi_start; |
xfer->c_intr = wdc_atapi_intr; |
xfer->c_intr = wdc_atapi_intr; |
xfer->c_kill_xfer = wdc_atapi_kill_xfer; |
xfer->c_kill_xfer = wdc_atapi_kill_xfer; |
xfer->c_dscpoll = 0; |
xfer->c_dscpoll = 0; |
s = splbio(); |
s = splbio(); |
wdc_exec_xfer(wdc->channels[channel], xfer); |
ata_exec_xfer(atac->atac_channels[channel], xfer); |
#ifdef DIAGNOSTIC |
#ifdef DIAGNOSTIC |
if ((sc_xfer->xs_control & XS_CTL_POLL) != 0 && |
if ((sc_xfer->xs_control & XS_CTL_POLL) != 0 && |
(sc_xfer->xs_status & XS_STS_DONE) == 0) |
(sc_xfer->xs_status & XS_STS_DONE) == 0) |
Line 398 wdc_atapi_scsipi_request(chan, req, arg) |
|
Line 416 wdc_atapi_scsipi_request(chan, req, arg) |
|
} |
} |
} |
} |
|
|
void |
static void |
wdc_atapi_start(chp, xfer) |
wdc_atapi_start(struct ata_channel *chp, struct ata_xfer *xfer) |
struct channel_softc *chp; |
|
struct wdc_xfer *xfer; |
|
{ |
{ |
struct scsipi_xfer *sc_xfer = xfer->cmd; |
struct atac_softc *atac = chp->ch_atac; |
struct ata_drive_datas *drvp = &chp->ch_drive[xfer->drive]; |
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 wait_flags = (sc_xfer->xs_control & XS_CTL_POLL) ? AT_POLL : 0; |
|
const char *errstring; |
|
|
WDCDEBUG_PRINT(("wdc_atapi_start %s:%d:%d, scsi flags 0x%x \n", |
ATADEBUG_PRINT(("wdc_atapi_start %s:%d:%d, scsi flags 0x%x \n", |
chp->wdc->sc_dev.dv_xname, chp->channel, drvp->drive, |
atac->atac_dev.dv_xname, chp->ch_channel, drvp->drive, |
sc_xfer->xs_control), DEBUG_XFERS); |
sc_xfer->xs_control), DEBUG_XFERS); |
/* Adjust C_DMA, it may have changed if we are requesting sense */ |
if ((xfer->c_flags & C_DMA) && (drvp->n_xfers <= NXFER)) |
if ((drvp->drive_flags & (DRIVE_DMA | DRIVE_UDMA)) && |
drvp->n_xfers++; |
sc_xfer->datalen > 0 && !(xfer->c_flags & C_FORCEPIO)) { |
/* Do control operations specially. */ |
if (drvp->n_xfers <= NXFER) |
if (__predict_false(drvp->state < READY)) { |
drvp->n_xfers++; |
/* If it's not a polled command, we need the kernel thread */ |
xfer->c_flags |= C_DMA; |
if ((sc_xfer->xs_control & XS_CTL_POLL) == 0 && |
} else { |
(chp->ch_flags & ATACH_TH_RUN) == 0) { |
xfer->c_flags &= ~C_DMA; |
chp->ch_queue->queue_freeze++; |
|
wakeup(&chp->ch_thread); |
|
return; |
|
} |
|
/* |
|
* disable interrupts, all commands here should be quick |
|
* enouth to be able to poll, and we don't go here that often |
|
*/ |
|
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)); |
|
/* Don't try to set mode if controller can't be adjusted */ |
|
if (atac->atac_set_modes == NULL) |
|
goto ready; |
|
/* Also don't try if the drive didn't report its mode */ |
|
if ((drvp->drive_flags & DRIVE_MODE) == 0) |
|
goto ready; |
|
errstring = "unbusy"; |
|
if (wdc_wait_for_unbusy(chp, ATAPI_DELAY, wait_flags)) |
|
goto timeout; |
|
wdccommand(chp, drvp->drive, SET_FEATURES, 0, 0, 0, |
|
0x08 | drvp->PIO_mode, WDSF_SET_MODE); |
|
errstring = "piomode"; |
|
if (wdc_wait_for_unbusy(chp, ATAPI_MODE_DELAY, wait_flags)) |
|
goto timeout; |
|
if (chp->ch_status & WDCS_ERR) { |
|
if (chp->ch_error == WDCE_ABRT) { |
|
/* |
|
* Some ATAPI drives reject PIO settings. |
|
* Fall back to PIO mode 3 since that's the |
|
* minimum for ATAPI. |
|
*/ |
|
printf("%s:%d:%d: PIO mode %d rejected, " |
|
"falling back to PIO mode 3\n", |
|
atac->atac_dev.dv_xname, |
|
chp->ch_channel, xfer->c_drive, |
|
drvp->PIO_mode); |
|
if (drvp->PIO_mode > 3) |
|
drvp->PIO_mode = 3; |
|
} else |
|
goto error; |
|
} |
|
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; |
|
} |
|
errstring = "dmamode"; |
|
if (wdc_wait_for_unbusy(chp, ATAPI_MODE_DELAY, wait_flags)) |
|
goto timeout; |
|
if (chp->ch_status & WDCS_ERR) { |
|
if (chp->ch_error == WDCE_ABRT) { |
|
if (drvp->drive_flags & DRIVE_UDMA) |
|
goto error; |
|
else { |
|
/* |
|
* The drive rejected our DMA setting. |
|
* Fall back to mode 1. |
|
*/ |
|
printf("%s:%d:%d: DMA mode %d rejected, " |
|
"falling back to DMA mode 0\n", |
|
atac->atac_dev.dv_xname, |
|
chp->ch_channel, xfer->c_drive, |
|
drvp->DMA_mode); |
|
if (drvp->DMA_mode > 0) |
|
drvp->DMA_mode = 0; |
|
} |
|
} else |
|
goto error; |
|
} |
|
ready: |
|
drvp->state = READY; |
|
bus_space_write_1(wdr->ctl_iot, wdr->ctl_ioh, wd_aux_ctlr, |
|
WDCTL_4BIT); |
|
delay(10); /* some drives need a little delay here */ |
} |
} |
/* start timeout machinery */ |
/* start timeout machinery */ |
if ((sc_xfer->xs_control & XS_CTL_POLL) == 0) |
if ((sc_xfer->xs_control & XS_CTL_POLL) == 0) |
callout_reset(&chp->ch_callout, mstohz(sc_xfer->timeout), |
callout_reset(&chp->ch_callout, mstohz(sc_xfer->timeout), |
wdctimeout, chp); |
wdctimeout, chp); |
/* Do control operations specially. */ |
|
if (drvp->state < READY) { |
if (wdc->select) |
if (drvp->state != RESET) { |
wdc->select(chp, xfer->c_drive); |
printf("%s:%d:%d: bad state %d in wdc_atapi_start\n", |
bus_space_write_1(wdr->cmd_iot, wdr->cmd_iohs[wd_sdh], 0, |
chp->wdc->sc_dev.dv_xname, chp->channel, |
WDSD_IBM | (xfer->c_drive << 4)); |
xfer->drive, drvp->state); |
switch (wdc_wait_for_unbusy(chp, ATAPI_DELAY, wait_flags) < 0) { |
panic("wdc_atapi_start: bad state"); |
case WDCWAIT_OK: |
} |
break; |
drvp->state = PIOMODE; |
case WDCWAIT_TOUT: |
wdc_atapi_ctrl(chp, xfer, 0); |
|
return; |
|
} |
|
bus_space_write_1(chp->cmd_iot, chp->cmd_ioh, wd_sdh, |
|
WDSD_IBM | (xfer->drive << 4)); |
|
if (wait_for_unbusy(chp, ATAPI_DELAY) < 0) { |
|
printf("wdc_atapi_start: not ready, st = %02x\n", |
printf("wdc_atapi_start: not ready, st = %02x\n", |
chp->ch_status); |
chp->ch_status); |
sc_xfer->error = XS_TIMEOUT; |
sc_xfer->error = XS_TIMEOUT; |
wdc_atapi_reset(chp, xfer); |
wdc_atapi_reset(chp, xfer); |
return; |
return; |
|
case WDCWAIT_THR: |
|
return; |
} |
} |
|
|
/* |
/* |
Line 453 wdc_atapi_start(chp, xfer) |
|
Line 551 wdc_atapi_start(chp, xfer) |
|
* data is necessary, multiple data transfer phases will be done. |
* data is necessary, multiple data transfer phases will be done. |
*/ |
*/ |
|
|
wdccommand(chp, xfer->drive, ATAPI_PKT_CMD, |
wdccommand(chp, xfer->c_drive, ATAPI_PKT_CMD, |
xfer->c_bcount <= 0xffff ? xfer->c_bcount : 0xffff, |
xfer->c_bcount <= 0xffff ? xfer->c_bcount : 0xffff, |
0, 0, 0, |
0, 0, 0, |
(xfer->c_flags & C_DMA) ? ATAPI_PKT_CMD_FTRE_DMA : 0); |
(xfer->c_flags & C_DMA) ? ATAPI_PKT_CMD_FTRE_DMA : 0); |
|
|
/* |
/* |
* If there is no interrupt for CMD input, busy-wait for it (done in |
* 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 |
* the interrupt routine. If it is a polled command, call the interrupt |
* routine until command is done. |
* routine until command is done. |
*/ |
*/ |
Line 467 wdc_atapi_start(chp, xfer) |
|
Line 565 wdc_atapi_start(chp, xfer) |
|
ATAPI_CFG_IRQ_DRQ || (sc_xfer->xs_control & XS_CTL_POLL)) { |
ATAPI_CFG_IRQ_DRQ || (sc_xfer->xs_control & XS_CTL_POLL)) { |
/* Wait for at last 400ns for status bit to be valid */ |
/* Wait for at last 400ns for status bit to be valid */ |
DELAY(1); |
DELAY(1); |
if (chp->ch_flags & WDCF_DMA_WAIT) { |
|
wdc_dmawait(chp, xfer, sc_xfer->timeout); |
|
chp->ch_flags &= ~WDCF_DMA_WAIT; |
|
} |
|
wdc_atapi_intr(chp, xfer, 0); |
wdc_atapi_intr(chp, xfer, 0); |
} else { |
} else { |
chp->ch_flags |= WDCF_IRQ_WAIT; |
chp->ch_flags |= ATACH_IRQ_WAIT; |
} |
} |
if (sc_xfer->xs_control & XS_CTL_POLL) { |
if (sc_xfer->xs_control & XS_CTL_POLL) { |
|
if (chp->ch_flags & ATACH_DMA_WAIT) { |
|
wdc_dmawait(chp, xfer, sc_xfer->timeout); |
|
chp->ch_flags &= ~ATACH_DMA_WAIT; |
|
} |
while ((sc_xfer->xs_status & XS_STS_DONE) == 0) { |
while ((sc_xfer->xs_status & XS_STS_DONE) == 0) { |
/* Wait for at last 400ns for status bit to be valid */ |
/* Wait for at last 400ns for status bit to be valid */ |
DELAY(1); |
DELAY(1); |
wdc_atapi_intr(chp, xfer, 0); |
wdc_atapi_intr(chp, xfer, 0); |
} |
} |
} |
} |
|
return; |
|
timeout: |
|
printf("%s:%d:%d: %s timed out\n", |
|
atac->atac_dev.dv_xname, chp->ch_channel, xfer->c_drive, |
|
errstring); |
|
sc_xfer->error = XS_TIMEOUT; |
|
bus_space_write_1(wdr->ctl_iot, wdr->ctl_ioh, wd_aux_ctlr, WDCTL_4BIT); |
|
delay(10); /* some drives need a little delay here */ |
|
wdc_atapi_reset(chp, xfer); |
|
return; |
|
error: |
|
printf("%s:%d:%d: %s ", |
|
atac->atac_dev.dv_xname, chp->ch_channel, xfer->c_drive, |
|
errstring); |
|
printf("error (0x%x)\n", chp->ch_error); |
|
sc_xfer->error = XS_SHORTSENSE; |
|
sc_xfer->sense.atapi_sense = chp->ch_error; |
|
bus_space_write_1(wdr->ctl_iot, wdr->ctl_ioh, wd_aux_ctlr, WDCTL_4BIT); |
|
delay(10); /* some drives need a little delay here */ |
|
wdc_atapi_reset(chp, xfer); |
|
return; |
} |
} |
|
|
int |
static int |
wdc_atapi_intr(chp, xfer, irq) |
wdc_atapi_intr(struct ata_channel *chp, struct ata_xfer *xfer, int irq) |
struct channel_softc *chp; |
|
struct wdc_xfer *xfer; |
|
int irq; |
|
{ |
{ |
struct scsipi_xfer *sc_xfer = xfer->cmd; |
struct atac_softc *atac = chp->ch_atac; |
struct ata_drive_datas *drvp = &chp->ch_drive[xfer->drive]; |
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 len, phase, i, retries=0; |
int ire; |
int ire, error; |
int dma_flags = 0; |
int dma_flags = 0; |
void *cmd; |
void *cmd; |
|
|
WDCDEBUG_PRINT(("wdc_atapi_intr %s:%d:%d\n", |
ATADEBUG_PRINT(("wdc_atapi_intr %s:%d:%d\n", |
chp->wdc->sc_dev.dv_xname, chp->channel, drvp->drive), DEBUG_INTR); |
atac->atac_dev.dv_xname, chp->ch_channel, drvp->drive), |
|
DEBUG_INTR); |
|
|
/* Is it not a transfer, but a control operation? */ |
/* Is it not a transfer, but a control operation? */ |
if (drvp->state < READY) { |
if (drvp->state < READY) { |
printf("%s:%d:%d: bad state %d in wdc_atapi_intr\n", |
printf("%s:%d:%d: bad state %d in wdc_atapi_intr\n", |
chp->wdc->sc_dev.dv_xname, chp->channel, xfer->drive, |
atac->atac_dev.dv_xname, chp->ch_channel, xfer->c_drive, |
drvp->state); |
drvp->state); |
panic("wdc_atapi_intr: bad state"); |
panic("wdc_atapi_intr: bad state"); |
} |
} |
Line 515 wdc_atapi_intr(chp, xfer, irq) |
|
Line 635 wdc_atapi_intr(chp, xfer, irq) |
|
sc_xfer->error = XS_TIMEOUT; |
sc_xfer->error = XS_TIMEOUT; |
wdc_atapi_reset(chp, xfer); |
wdc_atapi_reset(chp, xfer); |
return 1; |
return 1; |
} |
} |
|
|
/* Ack interrupt done in wait_for_unbusy */ |
/* Ack interrupt done in wdc_wait_for_unbusy */ |
bus_space_write_1(chp->cmd_iot, chp->cmd_ioh, wd_sdh, |
if (wdc->select) |
WDSD_IBM | (xfer->drive << 4)); |
wdc->select(chp, xfer->c_drive); |
if (wait_for_unbusy(chp, |
bus_space_write_1(wdr->cmd_iot, wdr->cmd_iohs[wd_sdh], 0, |
(irq == 0) ? sc_xfer->timeout : 0) != 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) |
if (irq && (xfer->c_flags & C_TIMEOU) == 0) |
return 0; /* IRQ was not for us */ |
return 0; /* IRQ was not for us */ |
printf("%s:%d:%d: device timeout, c_bcount=%d, c_skip=%d\n", |
printf("%s:%d:%d: device timeout, c_bcount=%d, c_skip=%d\n", |
chp->wdc->sc_dev.dv_xname, chp->channel, xfer->drive, |
atac->atac_dev.dv_xname, chp->ch_channel, xfer->c_drive, |
xfer->c_bcount, xfer->c_skip); |
xfer->c_bcount, xfer->c_skip); |
if (xfer->c_flags & C_DMA) { |
if (xfer->c_flags & C_DMA) { |
ata_dmaerr(drvp); |
ata_dmaerr(drvp, |
|
(xfer->c_flags & C_POLL) ? AT_POLL : 0); |
} |
} |
sc_xfer->error = XS_TIMEOUT; |
sc_xfer->error = XS_TIMEOUT; |
wdc_atapi_reset(chp, xfer); |
wdc_atapi_reset(chp, xfer); |
return 1; |
return 1; |
} |
} |
if (chp->wdc->cap & WDC_CAPABILITY_IRQACK) |
if (wdc->irqack) |
chp->wdc->irqack(chp); |
wdc->irqack(chp); |
|
|
/* |
/* |
* If we missed an IRQ and were using DMA, flag it as a DMA error |
* If we missed an IRQ and were using DMA, flag it as a DMA error |
* and reset device. |
* and reset device. |
*/ |
*/ |
if ((xfer->c_flags & C_TIMEOU) && (xfer->c_flags & C_DMA)) { |
if ((xfer->c_flags & C_TIMEOU) && (xfer->c_flags & C_DMA)) { |
ata_dmaerr(drvp); |
ata_dmaerr(drvp, (xfer->c_flags & C_POLL) ? AT_POLL : 0); |
sc_xfer->error = XS_RESET; |
sc_xfer->error = XS_RESET; |
wdc_atapi_reset(chp, xfer); |
wdc_atapi_reset(chp, xfer); |
return (1); |
return (1); |
} |
} |
/* |
/* |
* if the request sense command was aborted, report the short sense |
* if the request sense command was aborted, report the short sense |
* previously recorded, else continue normal processing |
* previously recorded, else continue normal processing |
*/ |
*/ |
Line 556 wdc_atapi_intr(chp, xfer, irq) |
|
Line 679 wdc_atapi_intr(chp, xfer, irq) |
|
dma_flags = (sc_xfer->xs_control & XS_CTL_DATA_IN) |
dma_flags = (sc_xfer->xs_control & XS_CTL_DATA_IN) |
? WDC_DMA_READ : 0; |
? WDC_DMA_READ : 0; |
again: |
again: |
len = bus_space_read_1(chp->cmd_iot, chp->cmd_ioh, wd_cyl_lo) + |
len = bus_space_read_1(wdr->cmd_iot, wdr->cmd_iohs[wd_cyl_lo], 0) + |
256 * bus_space_read_1(chp->cmd_iot, chp->cmd_ioh, wd_cyl_hi); |
256 * bus_space_read_1(wdr->cmd_iot, wdr->cmd_iohs[wd_cyl_hi], 0); |
ire = bus_space_read_1(chp->cmd_iot, chp->cmd_ioh, wd_ireason); |
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); |
phase = (ire & (WDCI_CMD | WDCI_IN)) | (chp->ch_status & WDCS_DRQ); |
WDCDEBUG_PRINT(("wdc_atapi_intr: c_bcount %d len %d st 0x%x err 0x%x " |
ATADEBUG_PRINT(("wdc_atapi_intr: c_bcount %d len %d st 0x%x err 0x%x " |
"ire 0x%x :", xfer->c_bcount, |
"ire 0x%x :", xfer->c_bcount, |
len, chp->ch_status, chp->ch_error, ire), DEBUG_INTR); |
len, chp->ch_status, chp->ch_error, ire), DEBUG_INTR); |
|
|
switch (phase) { |
switch (phase) { |
case PHASE_CMDOUT: |
case PHASE_CMDOUT: |
cmd = sc_xfer->cmd; |
cmd = sc_xfer->cmd; |
WDCDEBUG_PRINT(("PHASE_CMDOUT\n"), DEBUG_INTR); |
ATADEBUG_PRINT(("PHASE_CMDOUT\n"), DEBUG_INTR); |
/* Init the DMA channel if necessary */ |
/* Init the DMA channel if necessary */ |
if (xfer->c_flags & C_DMA) { |
if (xfer->c_flags & C_DMA) { |
if ((*chp->wdc->dma_init)(chp->wdc->dma_arg, |
error = (*wdc->dma_init)(wdc->dma_arg, |
chp->channel, xfer->drive, |
chp->ch_channel, xfer->c_drive, |
xfer->databuf, xfer->c_bcount, dma_flags) != 0) { |
xfer->c_databuf, xfer->c_bcount, dma_flags); |
sc_xfer->error = XS_DRIVER_STUFFUP; |
if (error) { |
break; |
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; |
|
} |
} |
} |
} |
} |
|
|
/* send packet command */ |
/* send packet command */ |
/* Commands are 12 or 16 bytes long. It's 32-bit aligned */ |
/* Commands are 12 or 16 bytes long. It's 32-bit aligned */ |
if ((chp->wdc->cap & WDC_CAPABILITY_ATAPI_NOSTREAM)) { |
wdc->dataout_pio(chp, drvp->drive_flags, cmd, sc_xfer->cmdlen); |
if (drvp->drive_flags & DRIVE_CAP32) { |
|
bus_space_write_multi_4(chp->data32iot, |
|
chp->data32ioh, 0, |
|
(u_int32_t *)cmd, |
|
sc_xfer->cmdlen >> 2); |
|
} else { |
|
bus_space_write_multi_2(chp->cmd_iot, |
|
chp->cmd_ioh, wd_data, |
|
(u_int16_t *)cmd, |
|
sc_xfer->cmdlen >> 1); |
|
} |
|
} else { |
|
if (drvp->drive_flags & DRIVE_CAP32) { |
|
bus_space_write_multi_stream_4(chp->data32iot, |
|
chp->data32ioh, 0, |
|
(u_int32_t *)cmd, |
|
sc_xfer->cmdlen >> 2); |
|
} else { |
|
bus_space_write_multi_stream_2(chp->cmd_iot, |
|
chp->cmd_ioh, wd_data, |
|
(u_int16_t *)cmd, |
|
sc_xfer->cmdlen >> 1); |
|
} |
|
} |
|
/* Start the DMA channel if necessary */ |
/* Start the DMA channel if necessary */ |
if (xfer->c_flags & C_DMA) { |
if (xfer->c_flags & C_DMA) { |
(*chp->wdc->dma_start)(chp->wdc->dma_arg, |
(*wdc->dma_start)(wdc->dma_arg, |
chp->channel, xfer->drive); |
chp->ch_channel, xfer->c_drive); |
chp->ch_flags |= WDCF_DMA_WAIT; |
chp->ch_flags |= ATACH_DMA_WAIT; |
} |
} |
|
|
if ((sc_xfer->xs_control & XS_CTL_POLL) == 0) { |
if ((sc_xfer->xs_control & XS_CTL_POLL) == 0) { |
chp->ch_flags |= WDCF_IRQ_WAIT; |
chp->ch_flags |= ATACH_IRQ_WAIT; |
} |
} |
return 1; |
return 1; |
|
|
case PHASE_DATAOUT: |
case PHASE_DATAOUT: |
/* write data */ |
/* write data */ |
WDCDEBUG_PRINT(("PHASE_DATAOUT\n"), DEBUG_INTR); |
ATADEBUG_PRINT(("PHASE_DATAOUT\n"), DEBUG_INTR); |
if ((sc_xfer->xs_control & XS_CTL_DATA_OUT) == 0 || |
if ((sc_xfer->xs_control & XS_CTL_DATA_OUT) == 0 || |
(xfer->c_flags & C_DMA) != 0) { |
(xfer->c_flags & C_DMA) != 0) { |
printf("wdc_atapi_intr: bad data phase DATAOUT\n"); |
printf("wdc_atapi_intr: bad data phase DATAOUT\n"); |
if (xfer->c_flags & C_DMA) { |
if (xfer->c_flags & C_DMA) { |
ata_dmaerr(drvp); |
ata_dmaerr(drvp, |
|
(xfer->c_flags & C_POLL) ? AT_POLL : 0); |
} |
} |
sc_xfer->error = XS_TIMEOUT; |
sc_xfer->error = XS_TIMEOUT; |
wdc_atapi_reset(chp, xfer); |
wdc_atapi_reset(chp, xfer); |
|
|
if (xfer->c_bcount < len) { |
if (xfer->c_bcount < len) { |
printf("wdc_atapi_intr: warning: write only " |
printf("wdc_atapi_intr: warning: write only " |
"%d of %d requested bytes\n", xfer->c_bcount, len); |
"%d of %d requested bytes\n", xfer->c_bcount, len); |
if ((chp->wdc->cap & WDC_CAPABILITY_ATAPI_NOSTREAM)) { |
wdc->dataout_pio(chp, drvp->drive_flags, |
bus_space_write_multi_2(chp->cmd_iot, |
(char *)xfer->c_databuf + xfer->c_skip, |
chp->cmd_ioh, wd_data, |
xfer->c_bcount); |
(u_int16_t *)((char *)xfer->databuf + |
|
xfer->c_skip), |
|
xfer->c_bcount >> 1); |
|
} else { |
|
bus_space_write_multi_stream_2(chp->cmd_iot, |
|
chp->cmd_ioh, wd_data, |
|
(u_int16_t *)((char *)xfer->databuf + |
|
xfer->c_skip), |
|
xfer->c_bcount >> 1); |
|
} |
|
for (i = xfer->c_bcount; i < len; i += 2) |
for (i = xfer->c_bcount; i < len; i += 2) |
bus_space_write_2(chp->cmd_iot, chp->cmd_ioh, |
bus_space_write_2(wdr->cmd_iot, |
wd_data, 0); |
wdr->cmd_iohs[wd_data], 0, 0); |
xfer->c_skip += xfer->c_bcount; |
xfer->c_skip += xfer->c_bcount; |
xfer->c_bcount = 0; |
xfer->c_bcount = 0; |
} else { |
} else { |
if (drvp->drive_flags & DRIVE_CAP32) { |
wdc->dataout_pio(chp, drvp->drive_flags, |
if ((chp->wdc->cap & WDC_CAPABILITY_ATAPI_NOSTREAM)) |
(char *)xfer->c_databuf + xfer->c_skip, len); |
bus_space_write_multi_4(chp->data32iot, |
xfer->c_skip += len; |
chp->data32ioh, 0, |
xfer->c_bcount -= len; |
(u_int32_t *)((char *)xfer->databuf + |
|
xfer->c_skip), |
|
len >> 2); |
|
else |
|
bus_space_write_multi_stream_4(chp->data32iot, |
|
chp->data32ioh, wd_data, |
|
(u_int32_t *)((char *)xfer->databuf + |
|
xfer->c_skip), |
|
len >> 2); |
|
|
|
xfer->c_skip += len & 0xfffffffc; |
|
xfer->c_bcount -= len & 0xfffffffc; |
|
len = len & 0x03; |
|
} |
|
if (len > 0) { |
|
if ((chp->wdc->cap & WDC_CAPABILITY_ATAPI_NOSTREAM)) |
|
bus_space_write_multi_2(chp->cmd_iot, |
|
chp->cmd_ioh, wd_data, |
|
(u_int16_t *)((char *)xfer->databuf + |
|
xfer->c_skip), |
|
len >> 1); |
|
else |
|
bus_space_write_multi_stream_2(chp->cmd_iot, |
|
chp->cmd_ioh, wd_data, |
|
(u_int16_t *)((char *)xfer->databuf + |
|
xfer->c_skip), |
|
len >> 1); |
|
xfer->c_skip += len; |
|
xfer->c_bcount -= len; |
|
} |
|
} |
} |
if ((sc_xfer->xs_control & XS_CTL_POLL) == 0) { |
if ((sc_xfer->xs_control & XS_CTL_POLL) == 0) { |
chp->ch_flags |= WDCF_IRQ_WAIT; |
chp->ch_flags |= ATACH_IRQ_WAIT; |
} |
} |
return 1; |
return 1; |
|
|
case PHASE_DATAIN: |
case PHASE_DATAIN: |
/* Read data */ |
/* Read data */ |
WDCDEBUG_PRINT(("PHASE_DATAIN\n"), DEBUG_INTR); |
ATADEBUG_PRINT(("PHASE_DATAIN\n"), DEBUG_INTR); |
if ((sc_xfer->xs_control & XS_CTL_DATA_IN) == 0 || |
if ((sc_xfer->xs_control & XS_CTL_DATA_IN) == 0 || |
(xfer->c_flags & C_DMA) != 0) { |
(xfer->c_flags & C_DMA) != 0) { |
printf("wdc_atapi_intr: bad data phase DATAIN\n"); |
printf("wdc_atapi_intr: bad data phase DATAIN\n"); |
if (xfer->c_flags & C_DMA) { |
if (xfer->c_flags & C_DMA) { |
ata_dmaerr(drvp); |
ata_dmaerr(drvp, |
|
(xfer->c_flags & C_POLL) ? AT_POLL : 0); |
} |
} |
sc_xfer->error = XS_TIMEOUT; |
sc_xfer->error = XS_TIMEOUT; |
wdc_atapi_reset(chp, xfer); |
wdc_atapi_reset(chp, xfer); |
|
|
if (xfer->c_bcount < len) { |
if (xfer->c_bcount < len) { |
printf("wdc_atapi_intr: warning: reading only " |
printf("wdc_atapi_intr: warning: reading only " |
"%d of %d bytes\n", xfer->c_bcount, len); |
"%d of %d bytes\n", xfer->c_bcount, len); |
if ((chp->wdc->cap & WDC_CAPABILITY_ATAPI_NOSTREAM)) { |
wdc->datain_pio(chp, drvp->drive_flags, |
bus_space_read_multi_2(chp->cmd_iot, |
(char *)xfer->c_databuf + xfer->c_skip, |
chp->cmd_ioh, wd_data, |
xfer->c_bcount); |
(u_int16_t *)((char *)xfer->databuf + |
|
xfer->c_skip), |
|
xfer->c_bcount >> 1); |
|
} else { |
|
bus_space_read_multi_stream_2(chp->cmd_iot, |
|
chp->cmd_ioh, wd_data, |
|
(u_int16_t *)((char *)xfer->databuf + |
|
xfer->c_skip), |
|
xfer->c_bcount >> 1); |
|
} |
|
wdcbit_bucket(chp, len - xfer->c_bcount); |
wdcbit_bucket(chp, len - xfer->c_bcount); |
xfer->c_skip += xfer->c_bcount; |
xfer->c_skip += xfer->c_bcount; |
xfer->c_bcount = 0; |
xfer->c_bcount = 0; |
} else { |
} else { |
if (drvp->drive_flags & DRIVE_CAP32) { |
wdc->datain_pio(chp, drvp->drive_flags, |
if ((chp->wdc->cap & WDC_CAPABILITY_ATAPI_NOSTREAM)) |
(char *)xfer->c_databuf + xfer->c_skip, len); |
bus_space_read_multi_4(chp->data32iot, |
xfer->c_skip += len; |
chp->data32ioh, 0, |
xfer->c_bcount -=len; |
(u_int32_t *)((char *)xfer->databuf + |
|
xfer->c_skip), |
|
len >> 2); |
|
else |
|
bus_space_read_multi_stream_4(chp->data32iot, |
|
chp->data32ioh, wd_data, |
|
(u_int32_t *)((char *)xfer->databuf + |
|
xfer->c_skip), |
|
len >> 2); |
|
|
|
xfer->c_skip += len & 0xfffffffc; |
|
xfer->c_bcount -= len & 0xfffffffc; |
|
len = len & 0x03; |
|
} |
|
if (len > 0) { |
|
if ((chp->wdc->cap & WDC_CAPABILITY_ATAPI_NOSTREAM)) |
|
bus_space_read_multi_2(chp->cmd_iot, |
|
chp->cmd_ioh, wd_data, |
|
(u_int16_t *)((char *)xfer->databuf + |
|
xfer->c_skip), |
|
len >> 1); |
|
else |
|
bus_space_read_multi_stream_2(chp->cmd_iot, |
|
chp->cmd_ioh, wd_data, |
|
(u_int16_t *)((char *)xfer->databuf + |
|
xfer->c_skip), |
|
len >> 1); |
|
xfer->c_skip += len; |
|
xfer->c_bcount -=len; |
|
} |
|
} |
} |
if ((sc_xfer->xs_control & XS_CTL_POLL) == 0) { |
if ((sc_xfer->xs_control & XS_CTL_POLL) == 0) { |
chp->ch_flags |= WDCF_IRQ_WAIT; |
chp->ch_flags |= ATACH_IRQ_WAIT; |
} |
} |
return 1; |
return 1; |
|
|
case PHASE_ABORTED: |
case PHASE_ABORTED: |
case PHASE_COMPLETED: |
case PHASE_COMPLETED: |
WDCDEBUG_PRINT(("PHASE_COMPLETED\n"), DEBUG_INTR); |
ATADEBUG_PRINT(("PHASE_COMPLETED\n"), DEBUG_INTR); |
if (xfer->c_flags & C_DMA) { |
if (xfer->c_flags & C_DMA) { |
xfer->c_bcount -= sc_xfer->datalen; |
xfer->c_bcount -= sc_xfer->datalen; |
} |
} |
|
|
default: |
default: |
if (++retries<500) { |
if (++retries<500) { |
DELAY(100); |
DELAY(100); |
chp->ch_status = bus_space_read_1(chp->cmd_iot, |
chp->ch_status = bus_space_read_1(wdr->cmd_iot, |
chp->cmd_ioh, wd_status); |
wdr->cmd_iohs[wd_status], 0); |
chp->ch_error = bus_space_read_1(chp->cmd_iot, |
chp->ch_error = bus_space_read_1(wdr->cmd_iot, |
chp->cmd_ioh, wd_error); |
wdr->cmd_iohs[wd_error], 0); |
goto again; |
goto again; |
} |
} |
printf("wdc_atapi_intr: unknown phase 0x%x\n", phase); |
printf("wdc_atapi_intr: unknown phase 0x%x\n", phase); |
|
|
sc_xfer->sense.atapi_sense = chp->ch_error; |
sc_xfer->sense.atapi_sense = chp->ch_error; |
} else { |
} else { |
if (xfer->c_flags & C_DMA) { |
if (xfer->c_flags & C_DMA) { |
ata_dmaerr(drvp); |
ata_dmaerr(drvp, |
|
(xfer->c_flags & C_POLL) ? AT_POLL : 0); |
} |
} |
sc_xfer->error = XS_RESET; |
sc_xfer->error = XS_RESET; |
wdc_atapi_reset(chp, xfer); |
wdc_atapi_reset(chp, xfer); |
return (1); |
return (1); |
} |
} |
} |
} |
WDCDEBUG_PRINT(("wdc_atapi_intr: wdc_atapi_done() (end), error 0x%x " |
ATADEBUG_PRINT(("wdc_atapi_intr: wdc_atapi_done() (end), error 0x%x " |
"sense 0x%x\n", sc_xfer->error, sc_xfer->sense.atapi_sense), |
"sense 0x%x\n", sc_xfer->error, sc_xfer->sense.atapi_sense), |
DEBUG_INTR); |
DEBUG_INTR); |
wdc_atapi_done(chp, xfer); |
wdc_atapi_done(chp, xfer); |
return (1); |
return (1); |
} |
} |
|
|
int |
static void |
wdc_atapi_ctrl(chp, xfer, irq) |
wdc_atapi_phase_complete(struct ata_xfer *xfer) |
struct channel_softc *chp; |
|
struct wdc_xfer *xfer; |
|
int irq; |
|
{ |
{ |
struct scsipi_xfer *sc_xfer = xfer->cmd; |
struct ata_channel *chp = xfer->c_chp; |
struct ata_drive_datas *drvp = &chp->ch_drive[xfer->drive]; |
struct atac_softc *atac = chp->ch_atac; |
char *errstring = NULL; |
struct wdc_softc *wdc = CHAN_TO_WDC(chp); |
int delay = (irq == 0) ? ATAPI_DELAY : 0; |
struct scsipi_xfer *sc_xfer = xfer->c_cmd; |
|
struct ata_drive_datas *drvp = &chp->ch_drive[xfer->c_drive]; |
/* Ack interrupt done in wait_for_unbusy */ |
|
again: |
|
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); |
|
bus_space_write_1(chp->cmd_iot, chp->cmd_ioh, wd_sdh, |
|
WDSD_IBM | (xfer->drive << 4)); |
|
switch (drvp->state) { |
|
case PIOMODE: |
|
/* 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; |
|
wdccommand(chp, drvp->drive, SET_FEATURES, 0, 0, 0, |
|
0x08 | drvp->PIO_mode, WDSF_SET_MODE); |
|
drvp->state = PIOMODE_WAIT; |
|
break; |
|
case PIOMODE_WAIT: |
|
errstring = "piomode"; |
|
if (wait_for_unbusy(chp, delay)) |
|
goto timeout; |
|
if (chp->wdc->cap & WDC_CAPABILITY_IRQACK) |
|
chp->wdc->irqack(chp); |
|
if (chp->ch_status & WDCS_ERR) { |
|
if (chp->ch_error == WDCE_ABRT) { |
|
/* |
|
* some ATAPI drives rejects pio settings. |
|
* all we can do here is fall back to PIO 0 |
|
*/ |
|
drvp->drive_flags &= ~DRIVE_MODE; |
|
drvp->drive_flags &= ~(DRIVE_DMA|DRIVE_UDMA); |
|
drvp->PIO_mode = 0; |
|
drvp->DMA_mode = 0; |
|
printf("%s:%d:%d: pio setting rejected, " |
|
"falling back to PIO mode 0\n", |
|
chp->wdc->sc_dev.dv_xname, |
|
chp->channel, xfer->drive); |
|
chp->wdc->set_modes(chp); |
|
goto ready; |
|
} |
|
goto error; |
|
} |
|
/* fall through */ |
|
|
|
case DMAMODE: |
|
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 = DMAMODE_WAIT; |
|
break; |
|
case DMAMODE_WAIT: |
|
errstring = "dmamode"; |
|
if (wait_for_unbusy(chp, delay)) |
|
goto timeout; |
|
if (chp->wdc->cap & WDC_CAPABILITY_IRQACK) |
|
chp->wdc->irqack(chp); |
|
if (chp->ch_status & WDCS_ERR) |
|
goto error; |
|
/* fall through */ |
|
|
|
case READY: |
|
ready: |
|
drvp->state = READY; |
|
xfer->c_intr = wdc_atapi_intr; |
|
callout_stop(&chp->ch_callout); |
|
wdc_atapi_start(chp, xfer); |
|
return 1; |
|
} |
|
if ((sc_xfer->xs_control & XS_CTL_POLL) == 0) { |
|
chp->ch_flags |= WDCF_IRQ_WAIT; |
|
xfer->c_intr = wdc_atapi_ctrl; |
|
} else { |
|
goto again; |
|
} |
|
return 1; |
|
|
|
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; |
|
error: |
|
printf("%s:%d:%d: %s ", |
|
chp->wdc->sc_dev.dv_xname, chp->channel, xfer->drive, |
|
errstring); |
|
printf("error (0x%x)\n", chp->ch_error); |
|
sc_xfer->error = XS_SHORTSENSE; |
|
sc_xfer->sense.atapi_sense = chp->ch_error; |
|
wdc_atapi_reset(chp, xfer); |
|
return 1; |
|
} |
|
|
|
void |
|
wdc_atapi_phase_complete(xfer) |
|
struct wdc_xfer *xfer; |
|
{ |
|
struct channel_softc *chp = xfer->chp; |
|
struct scsipi_xfer *sc_xfer = xfer->cmd; |
|
struct ata_drive_datas *drvp = &chp->ch_drive[xfer->drive]; |
|
|
|
/* wait for DSC if needed */ |
/* wait for DSC if needed */ |
if (drvp->drive_flags & DRIVE_ATAPIST) { |
if (drvp->drive_flags & DRIVE_ATAPIST) { |
WDCDEBUG_PRINT(("wdc_atapi_phase_complete(%s:%d:%d) " |
ATADEBUG_PRINT(("wdc_atapi_phase_complete(%s:%d:%d) " |
"polldsc %d\n", chp->wdc->sc_dev.dv_xname, chp->channel, |
"polldsc %d\n", atac->atac_dev.dv_xname, chp->ch_channel, |
xfer->drive, xfer->c_dscpoll), DEBUG_XFERS); |
xfer->c_drive, xfer->c_dscpoll), DEBUG_XFERS); |
if (cold) { |
#if 1 |
if (wdcwait(chp, WDCS_DSC, WDCS_DSC, |
if (cold) |
sc_xfer->timeout)) { |
panic("wdc_atapi_phase_complete: cold"); |
printf("%s:%d:%d: wait_for_dsc failed\n", |
#endif |
chp->wdc->sc_dev.dv_xname, chp->channel, |
if (wdcwait(chp, WDCS_DSC, WDCS_DSC, 10, |
xfer->drive); |
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", |
|
atac->atac_dev.dv_xname, |
|
chp->ch_channel, xfer->c_drive); |
sc_xfer->error = XS_TIMEOUT; |
sc_xfer->error = XS_TIMEOUT; |
wdc_atapi_reset(chp, xfer); |
wdc_atapi_reset(chp, xfer); |
return; |
return; |
} |
} else |
} else { |
|
if (wdcwait(chp, WDCS_DSC, WDCS_DSC, 10)) { |
|
/* 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", |
|
chp->wdc->sc_dev.dv_xname, |
|
chp->channel, xfer->drive); |
|
sc_xfer->error = XS_TIMEOUT; |
|
wdc_atapi_reset(chp, xfer); |
|
return; |
|
} |
|
callout_reset(&chp->ch_callout, 1, |
callout_reset(&chp->ch_callout, 1, |
wdc_atapi_polldsc, xfer); |
wdc_atapi_polldsc, xfer); |
return; |
return; |
} |
|
} |
} |
} |
} |
|
|
/* |
/* |
* Some drive occasionally set WDCS_ERR with |
* Some drive occasionally set WDCS_ERR with |
* "ATA illegal length indication" in the error |
* "ATA illegal length indication" in the error |
* register. If we read some data the sense is valid |
* register. If we read some data the sense is valid |
* anyway, so don't report the error. |
* anyway, so don't report the error. |
Line 978 wdc_atapi_phase_complete(xfer) |
|
Line 892 wdc_atapi_phase_complete(xfer) |
|
/* ask scsipi to send a REQUEST_SENSE */ |
/* ask scsipi to send a REQUEST_SENSE */ |
sc_xfer->error = XS_BUSY; |
sc_xfer->error = XS_BUSY; |
sc_xfer->status = SCSI_CHECK; |
sc_xfer->status = SCSI_CHECK; |
} else if (chp->wdc->dma_status & |
} else if (wdc->dma_status & |
(WDC_DMAST_NOIRQ | WDC_DMAST_ERR)) { |
(WDC_DMAST_NOIRQ | WDC_DMAST_ERR)) { |
ata_dmaerr(drvp); |
ata_dmaerr(drvp, |
|
(xfer->c_flags & C_POLL) ? AT_POLL : 0); |
sc_xfer->error = XS_RESET; |
sc_xfer->error = XS_RESET; |
wdc_atapi_reset(chp, xfer); |
wdc_atapi_reset(chp, xfer); |
return; |
return; |
} |
} |
} |
} |
if (xfer->c_bcount != 0) { |
if (xfer->c_bcount != 0) { |
WDCDEBUG_PRINT(("wdc_atapi_intr: bcount value is " |
ATADEBUG_PRINT(("wdc_atapi_intr: bcount value is " |
"%d after io\n", xfer->c_bcount), DEBUG_XFERS); |
"%d after io\n", xfer->c_bcount), DEBUG_XFERS); |
} |
} |
#ifdef DIAGNOSTIC |
#ifdef DIAGNOSTIC |
Line 996 wdc_atapi_phase_complete(xfer) |
|
Line 911 wdc_atapi_phase_complete(xfer) |
|
"is %d after io\n", xfer->c_bcount); |
"is %d after io\n", xfer->c_bcount); |
} |
} |
#endif |
#endif |
WDCDEBUG_PRINT(("wdc_atapi_phase_complete: wdc_atapi_done(), " |
ATADEBUG_PRINT(("wdc_atapi_phase_complete: wdc_atapi_done(), " |
"error 0x%x sense 0x%x\n", sc_xfer->error, |
"error 0x%x sense 0x%x\n", sc_xfer->error, |
sc_xfer->sense.atapi_sense), DEBUG_INTR); |
sc_xfer->sense.atapi_sense), DEBUG_INTR); |
wdc_atapi_done(chp, xfer); |
wdc_atapi_done(chp, xfer); |
} |
} |
|
|
void |
static void |
wdc_atapi_done(chp, xfer) |
wdc_atapi_done(struct ata_channel *chp, struct ata_xfer *xfer) |
struct channel_softc *chp; |
|
struct wdc_xfer *xfer; |
|
{ |
{ |
struct scsipi_xfer *sc_xfer = xfer->cmd; |
struct atac_softc *atac = chp->ch_atac; |
|
struct scsipi_xfer *sc_xfer = xfer->c_cmd; |
|
int drive = xfer->c_drive; |
|
|
WDCDEBUG_PRINT(("wdc_atapi_done %s:%d:%d: flags 0x%x\n", |
ATADEBUG_PRINT(("wdc_atapi_done %s:%d:%d: flags 0x%x\n", |
chp->wdc->sc_dev.dv_xname, chp->channel, xfer->drive, |
atac->atac_dev.dv_xname, chp->ch_channel, xfer->c_drive, |
(u_int)xfer->c_flags), DEBUG_XFERS); |
(u_int)xfer->c_flags), DEBUG_XFERS); |
callout_stop(&chp->ch_callout); |
callout_stop(&chp->ch_callout); |
/* remove this command from xfer queue */ |
/* mark controller inactive and free the command */ |
wdc_free_xfer(chp, xfer); |
chp->ch_queue->active_xfer = NULL; |
|
ata_free_xfer(chp, xfer); |
|
|
WDCDEBUG_PRINT(("wdc_atapi_done: scsipi_done\n"), DEBUG_XFERS); |
if (chp->ch_drive[drive].drive_flags & DRIVE_WAITDRAIN) { |
|
sc_xfer->error = XS_DRIVER_STUFFUP; |
|
chp->ch_drive[drive].drive_flags &= ~DRIVE_WAITDRAIN; |
|
wakeup(&chp->ch_queue->active_xfer); |
|
} |
|
|
|
ATADEBUG_PRINT(("wdc_atapi_done: scsipi_done\n"), DEBUG_XFERS); |
scsipi_done(sc_xfer); |
scsipi_done(sc_xfer); |
WDCDEBUG_PRINT(("wdcstart from wdc_atapi_done, flags 0x%x\n", |
ATADEBUG_PRINT(("atastart from wdc_atapi_done, flags 0x%x\n", |
chp->ch_flags), DEBUG_XFERS); |
chp->ch_flags), DEBUG_XFERS); |
wdcstart(chp); |
atastart(chp); |
} |
} |
|
|
void |
static void |
wdc_atapi_reset(chp, xfer) |
wdc_atapi_reset(struct ata_channel *chp, struct ata_xfer *xfer) |
struct channel_softc *chp; |
|
struct wdc_xfer *xfer; |
|
{ |
{ |
struct ata_drive_datas *drvp = &chp->ch_drive[xfer->drive]; |
struct atac_softc *atac = chp->ch_atac; |
struct scsipi_xfer *sc_xfer = xfer->cmd; |
struct ata_drive_datas *drvp = &chp->ch_drive[xfer->c_drive]; |
|
struct scsipi_xfer *sc_xfer = xfer->c_cmd; |
|
|
wdccommandshort(chp, xfer->drive, ATAPI_SOFT_RESET); |
wdccommandshort(chp, xfer->c_drive, ATAPI_SOFT_RESET); |
drvp->state = 0; |
drvp->state = 0; |
if (wait_for_unbusy(chp, WDC_RESET_WAIT) != 0) { |
if (wdc_wait_for_unbusy(chp, WDC_RESET_WAIT, AT_POLL) != 0) { |
printf("%s:%d:%d: reset failed\n", |
printf("%s:%d:%d: reset failed\n", |
chp->wdc->sc_dev.dv_xname, chp->channel, |
atac->atac_dev.dv_xname, chp->ch_channel, |
xfer->drive); |
xfer->c_drive); |
sc_xfer->error = XS_SELTIMEOUT; |
sc_xfer->error = XS_SELTIMEOUT; |
} |
} |
wdc_atapi_done(chp, xfer); |
wdc_atapi_done(chp, xfer); |
return; |
return; |
} |
} |
|
|
void |
static void |
wdc_atapi_polldsc(arg) |
wdc_atapi_polldsc(void *arg) |
void *arg; |
|
{ |
{ |
|
|
wdc_atapi_phase_complete(arg); |
wdc_atapi_phase_complete(arg); |
} |
} |