NetBSD/sys/dev/ic/wdc.c
bouyer 1e0e78854a There's no ATA draft where it is required for the drive to set DRDY | DSC when
the disk is ready to transfer data, and in ATA-5 the DSC has been obsoleted.
So only wait for DRQ to transfer data. This can be made conditional on the
ATA version if it's proven to break with some drives (worked with all the
drives I have access to).
While I'm there correct a few typos.
1999-02-08 15:22:28 +00:00

1335 lines
38 KiB
C

/* $NetBSD: wdc.c,v 1.56 1999/02/08 15:22:28 bouyer Exp $ */
/*
* Copyright (c) 1998 Manuel Bouyer. All rights reserved.
*
* 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.
* 3. All advertising materials mentioning features or use of this software
* must display the following acknowledgement:
* This product includes software developed by Manuel Bouyer.
* 4. The name of the author may not be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* 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 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.
* 3. All advertising materials mentioning features or use of this software
* must display the following acknowledgement:
* This product includes software developed by the NetBSD
* Foundation, Inc. and its contributors.
* 4. Neither the name of The NetBSD Foundation nor the names of its
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* 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.
*/
/*
* CODE UNTESTED IN THE CURRENT REVISION:
*
*/
#define WDCDEBUG
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/kernel.h>
#include <sys/conf.h>
#include <sys/buf.h>
#include <sys/device.h>
#include <sys/malloc.h>
#include <sys/syslog.h>
#include <sys/proc.h>
#include <vm/vm.h>
#include <machine/intr.h>
#include <machine/bus.h>
#ifndef __BUS_SPACE_HAS_STREAM_METHODS
#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_read_multi_stream_2 bus_space_read_multi_2
#define bus_space_read_multi_stream_4 bus_space_read_multi_4
#endif /* __BUS_SPACE_HAS_STREAM_METHODS */
#include <dev/ata/atavar.h>
#include <dev/ata/atareg.h>
#include <dev/ic/wdcreg.h>
#include <dev/ic/wdcvar.h>
#include "atapibus.h"
#define WDCDELAY 100 /* 100 microseconds */
#define WDCNDELAY_RST (WDC_RESET_WAIT * 1000 / WDCDELAY)
#if 0
/* If you enable this, it will report any delays more than WDCDELAY * N long. */
#define WDCNDELAY_DEBUG 50
#endif
LIST_HEAD(xfer_free_list, wdc_xfer) xfer_free_list;
static void __wdcerror __P((struct channel_softc*, char *));
static int __wdcwait_reset __P((struct channel_softc *, int));
void __wdccommand_done __P((struct channel_softc *, struct wdc_xfer *));
void __wdccommand_start __P((struct channel_softc *, struct wdc_xfer *));
int __wdccommand_intr __P((struct channel_softc *, struct wdc_xfer *));
int wdprint __P((void *, const char *));
#define DEBUG_INTR 0x01
#define DEBUG_XFERS 0x02
#define DEBUG_STATUS 0x04
#define DEBUG_FUNCS 0x08
#define DEBUG_PROBE 0x10
#ifdef WDCDEBUG
int wdcdebug_mask = 0;
int wdc_nxfer = 0;
#define WDCDEBUG_PRINT(args, level) if (wdcdebug_mask & (level)) printf args
#else
#define WDCDEBUG_PRINT(args, level)
#endif
int
wdprint(aux, pnp)
void *aux;
const char *pnp;
{
struct ata_atapi_attach *aa_link = aux;
if (pnp)
printf("drive at %s", pnp);
printf(" channel %d drive %d", aa_link->aa_channel,
aa_link->aa_drv_data->drive);
return (UNCONF);
}
int
atapi_print(aux, pnp)
void *aux;
const char *pnp;
{
struct ata_atapi_attach *aa_link = aux;
if (pnp)
printf("atapibus at %s", pnp);
printf(" channel %d", aa_link->aa_channel);
return (UNCONF);
}
/* Test to see controller with at last one attached drive is there.
* Returns a bit for each possible drive found (0x01 for drive 0,
* 0x02 for drive 1).
* Logic:
* - If a status register is at 0xff, assume there is no drive here
* (ISA has pull-up resistors). If no drive at all -> return.
* - reset the controller, wait for it to complete (may take up to 31s !).
* If timeout -> return.
* - test ATA/ATAPI signatures. If at last one drive found -> return.
* - try an ATA command on the master.
*/
int
wdcprobe(chp)
struct channel_softc *chp;
{
u_int8_t st0, st1, sc, sn, cl, ch;
u_int8_t ret_value = 0x03;
u_int8_t drive;
/*
* Sanity check to see if the wdc channel responds at all.
*/
if (chp->wdc == NULL ||
(chp->wdc->cap & WDC_CAPABILITY_NO_EXTRA_RESETS) == 0) {
bus_space_write_1(chp->cmd_iot, chp->cmd_ioh, wd_sdh,
WDSD_IBM);
delay(1);
st0 = bus_space_read_1(chp->cmd_iot, chp->cmd_ioh, wd_status);
bus_space_write_1(chp->cmd_iot, chp->cmd_ioh, wd_sdh,
WDSD_IBM | 0x10);
delay(1);
st1 = bus_space_read_1(chp->cmd_iot, chp->cmd_ioh, wd_status);
WDCDEBUG_PRINT(("%s:%d: before reset, st0=0x%x, st1=0x%x\n",
chp->wdc ? chp->wdc->sc_dev.dv_xname : "wdcprobe",
chp->channel, st0, st1), DEBUG_PROBE);
if (st0 == 0xff)
ret_value &= ~0x01;
if (st1 == 0xff)
ret_value &= ~0x02;
if (ret_value == 0)
return 0;
}
/* assert SRST, wait for reset to complete */
bus_space_write_1(chp->cmd_iot, chp->cmd_ioh, wd_sdh,
WDSD_IBM);
delay(1);
bus_space_write_1(chp->ctl_iot, chp->ctl_ioh, wd_aux_ctlr,
WDCTL_RST | WDCTL_IDS);
DELAY(1000);
bus_space_write_1(chp->ctl_iot, chp->ctl_ioh, wd_aux_ctlr,
WDCTL_IDS);
delay(1000);
(void) bus_space_read_1(chp->cmd_iot, chp->cmd_ioh, wd_error);
bus_space_write_1(chp->ctl_iot, chp->ctl_ioh, wd_aux_ctlr, WDCTL_4BIT);
delay(1);
ret_value = __wdcwait_reset(chp, ret_value);
WDCDEBUG_PRINT(("%s:%d: after reset, ret_value=0x%d\n",
chp->wdc ? chp->wdc->sc_dev.dv_xname : "wdcprobe", chp->channel,
ret_value), DEBUG_PROBE);
/* if reset failed, there's nothing here */
if (ret_value == 0)
return 0;
/*
* Test presence of drives. First test register signatures looking for
* ATAPI devices , then rescan and try an ATA command, in case it's an
* old drive.
* Fill in drive_flags accordingly
*/
for (drive = 0; drive < 2; drive++) {
if ((ret_value & (0x01 << drive)) == 0)
continue;
bus_space_write_1(chp->cmd_iot, chp->cmd_ioh, wd_sdh,
WDSD_IBM | (drive << 4));
delay(1);
/* Save registers contents */
sc = bus_space_read_1(chp->cmd_iot, chp->cmd_ioh, wd_seccnt);
sn = bus_space_read_1(chp->cmd_iot, chp->cmd_ioh, wd_sector);
cl = bus_space_read_1(chp->cmd_iot, chp->cmd_ioh, wd_cyl_lo);
ch = bus_space_read_1(chp->cmd_iot, chp->cmd_ioh, wd_cyl_hi);
WDCDEBUG_PRINT(("%s:%d:%d: after reset, sc=0x%x sn=0x%x "
"cl=0x%x ch=0x%x\n",
chp->wdc ? chp->wdc->sc_dev.dv_xname : "wdcprobe",
chp->channel, drive, sc, sn, cl, ch), DEBUG_PROBE);
if (sc == 0x01 && sn == 0x01 && cl == 0x14 && ch == 0xeb) {
chp->ch_drive[drive].drive_flags |= DRIVE_ATAPI;
}
}
for (drive = 0; drive < 2; drive++) {
if ((ret_value & (0x01 << drive)) == 0 ||
(chp->ch_drive[drive].drive_flags & DRIVE_ATAPI) != 0)
continue;
bus_space_write_1(chp->cmd_iot, chp->cmd_ioh, wd_sdh,
WDSD_IBM | (drive << 4));
delay(1);
/*
* Maybe it's an old device, so don't rely on ATA sig.
* Test registers writability (Error register not writable,
* but cyllo is), then try an ATA command.
*/
bus_space_write_1(chp->cmd_iot, chp->cmd_ioh, wd_error, 0x58);
bus_space_write_1(chp->cmd_iot, chp->cmd_ioh, wd_cyl_lo, 0xa5);
if (bus_space_read_1(chp->cmd_iot, chp->cmd_ioh, wd_error) ==
0x58 ||
bus_space_read_1(chp->cmd_iot, chp->cmd_ioh, wd_cyl_lo) !=
0xa5) {
WDCDEBUG_PRINT(("%s:%d:%d: register writability "
"failed\n",
chp->wdc ? chp->wdc->sc_dev.dv_xname : "wdcprobe",
chp->channel, drive), DEBUG_PROBE);
ret_value &= ~(0x01 << drive);
continue;
}
if (wait_for_ready(chp, 10000) != 0) {
WDCDEBUG_PRINT(("%s:%d:%d: not ready\n",
chp->wdc ? chp->wdc->sc_dev.dv_xname : "wdcprobe",
chp->channel, drive), DEBUG_PROBE);
ret_value &= ~(0x01 << drive);
continue;
}
bus_space_write_1(chp->cmd_iot, chp->cmd_ioh, wd_command,
WDCC_DIAGNOSE);
if (wait_for_ready(chp, 10000) == 0) {
chp->ch_drive[drive].drive_flags |=
DRIVE_ATA;
} else {
WDCDEBUG_PRINT(("%s:%d:%d: WDCC_DIAGNOSE failed\n",
chp->wdc ? chp->wdc->sc_dev.dv_xname : "wdcprobe",
chp->channel, drive), DEBUG_PROBE);
ret_value &= ~(0x01 << drive);
}
}
return (ret_value);
}
void
wdcattach(chp)
struct channel_softc *chp;
{
int channel_flags, ctrl_flags, i, error;
struct ata_atapi_attach aa_link;
LIST_INIT(&xfer_free_list);
for (i = 0; i < 2; i++) {
chp->ch_drive[i].chnl_softc = chp;
chp->ch_drive[i].drive = i;
/* If controller can't do 16bit flag the drives as 32bit */
if ((chp->wdc->cap &
(WDC_CAPABILITY_DATA16 | WDC_CAPABILITY_DATA32)) ==
WDC_CAPABILITY_DATA32)
chp->ch_drive[i].drive_flags |= DRIVE_CAP32;
}
if ((error = wdc_addref(chp)) != 0) {
printf("%s: unable to enable controller\n",
chp->wdc->sc_dev.dv_xname);
return;
}
if (wdcprobe(chp) == 0) {
/* If no drives, abort attach here. */
wdc_delref(chp);
return;
}
TAILQ_INIT(&chp->ch_queue->sc_xfer);
ctrl_flags = chp->wdc->sc_dev.dv_cfdata->cf_flags;
channel_flags = (ctrl_flags >> (NBBY * chp->channel)) & 0xff;
WDCDEBUG_PRINT(("wdcattach: ch_drive_flags 0x%x 0x%x\n",
chp->ch_drive[0].drive_flags, chp->ch_drive[1].drive_flags),
DEBUG_PROBE);
/*
* Attach an ATAPI bus, if needed.
*/
if ((chp->ch_drive[0].drive_flags & DRIVE_ATAPI) ||
(chp->ch_drive[1].drive_flags & DRIVE_ATAPI)) {
#if NATAPIBUS > 0
wdc_atapibus_attach(chp);
#else
/*
* Fills in a fake aa_link and call config_found, so that
* the config machinery will print
* "atapibus at xxx not configured"
*/
memset(&aa_link, 0, sizeof(struct ata_atapi_attach));
aa_link.aa_type = T_ATAPI;
aa_link.aa_channel = chp->channel;
aa_link.aa_openings = 1;
aa_link.aa_drv_data = 0;
aa_link.aa_bus_private = NULL;
(void)config_found(&chp->wdc->sc_dev, (void *)&aa_link,
atapi_print);
#endif
}
for (i = 0; i < 2; i++) {
if ((chp->ch_drive[i].drive_flags & DRIVE_ATA) == 0) {
continue;
}
memset(&aa_link, 0, sizeof(struct ata_atapi_attach));
aa_link.aa_type = T_ATA;
aa_link.aa_channel = chp->channel;
aa_link.aa_openings = 1;
aa_link.aa_drv_data = &chp->ch_drive[i];
if (config_found(&chp->wdc->sc_dev, (void *)&aa_link, wdprint))
wdc_probe_caps(&chp->ch_drive[i]);
}
/*
* reset drive_flags for unnatached devices, reset state for attached
* ones
*/
for (i = 0; i < 2; i++) {
if (chp->ch_drive[i].drv_softc == NULL)
chp->ch_drive[i].drive_flags = 0;
else
chp->ch_drive[i].state = 0;
}
/*
* Reset channel. The probe, with some combinations of ATA/ATAPI
* devices keep it in a mostly working, but strange state (with busy
* led on)
*/
if ((chp->wdc->cap & WDC_CAPABILITY_NO_EXTRA_RESETS) == 0) {
wdcreset(chp, VERBOSE);
/*
* Read status registers to avoid spurious interrupts.
*/
for (i = 1; i >= 0; i--) {
if (chp->ch_drive[i].drive_flags & DRIVE) {
bus_space_write_1(chp->cmd_iot, chp->cmd_ioh,
wd_sdh, WDSD_IBM | (i << 4));
if (wait_for_unbusy(chp, 10000) < 0)
printf("%s:%d:%d: device busy\n",
chp->wdc->sc_dev.dv_xname,
chp->channel, i);
}
}
}
wdc_delref(chp);
}
/*
* Start I/O on a controller, for the given channel.
* The first xfer may be not for our channel if the channel queues
* are shared.
*/
void
wdcstart(chp)
struct channel_softc *chp;
{
struct wdc_xfer *xfer;
#ifdef WDC_DIAGNOSTIC
int spl1, spl2;
spl1 = splbio();
spl2 = splbio();
if (spl2 != spl1) {
printf("wdcstart: not at splbio()\n");
panic("wdcstart");
}
splx(spl2);
splx(spl1);
#endif /* WDC_DIAGNOSTIC */
/* is there a xfer ? */
if ((xfer = chp->ch_queue->sc_xfer.tqh_first) == NULL)
return;
/* adjust chp, in case we have a shared queue */
chp = xfer->chp;
if ((chp->ch_flags & WDCF_ACTIVE) != 0 ) {
return; /* channel aleady active */
}
#ifdef DIAGNOSTIC
if ((chp->ch_flags & WDCF_IRQ_WAIT) != 0)
panic("wdcstart: channel waiting for irq\n");
#endif
if (chp->wdc->cap & WDC_CAPABILITY_HWLOCK)
if (!(*chp->wdc->claim_hw)(chp, 0))
return;
WDCDEBUG_PRINT(("wdcstart: xfer %p channel %d drive %d\n", xfer,
chp->channel, xfer->drive), DEBUG_XFERS);
chp->ch_flags |= WDCF_ACTIVE;
if (chp->ch_drive[xfer->drive].drive_flags & DRIVE_RESET) {
chp->ch_drive[xfer->drive].drive_flags &= ~DRIVE_RESET;
chp->ch_drive[xfer->drive].state = 0;
}
xfer->c_start(chp, xfer);
}
/* restart an interrupted I/O */
void
wdcrestart(v)
void *v;
{
struct channel_softc *chp = v;
int s;
s = splbio();
wdcstart(chp);
splx(s);
}
/*
* Interrupt routine for the controller. Acknowledge the interrupt, check for
* errors on the current operation, mark it done if necessary, and start the
* next request. Also check for a partially done transfer, and continue with
* the next chunk if so.
*/
int
wdcintr(arg)
void *arg;
{
struct channel_softc *chp = arg;
struct wdc_xfer *xfer;
if ((chp->ch_flags & WDCF_IRQ_WAIT) == 0) {
#if 0
/* Clear the pending interrupt and abort. */
u_int8_t s =
bus_space_read_1(chp->cmd_iot, chp->cmd_ioh, wd_status);
#ifdef WDCDEBUG
u_int8_t e =
bus_space_read_1(chp->cmd_iot, chp->cmd_ioh, wd_error);
u_int8_t i =
bus_space_read_1(chp->cmd_iot, chp->cmd_ioh, wd_seccnt);
#else
bus_space_read_1(chp->cmd_iot, chp->cmd_ioh, wd_error);
bus_space_read_1(chp->cmd_iot, chp->cmd_ioh, wd_seccnt);
#endif
WDCDEBUG_PRINT(("wdcintr: inactive controller, "
"punting st=%02x er=%02x irr=%02x\n", s, e, i), DEBUG_INTR);
if (s & WDCS_DRQ) {
int len;
len = bus_space_read_1(chp->cmd_iot, chp->cmd_ioh,
wd_cyl_lo) + 256 * bus_space_read_1(chp->cmd_iot,
chp->cmd_ioh, wd_cyl_hi);
WDCDEBUG_PRINT(("wdcintr: clearing up %d bytes\n",
len), DEBUG_INTR);
wdcbit_bucket (chp, len);
}
#else
WDCDEBUG_PRINT(("wdcintr: inactive controller\n"), DEBUG_INTR);
#endif
return 0;
}
WDCDEBUG_PRINT(("wdcintr\n"), DEBUG_INTR);
untimeout(wdctimeout, chp);
chp->ch_flags &= ~WDCF_IRQ_WAIT;
xfer = chp->ch_queue->sc_xfer.tqh_first;
return xfer->c_intr(chp, xfer);
}
/* Put all disk in RESET state */
void wdc_reset_channel(drvp)
struct ata_drive_datas *drvp;
{
struct channel_softc *chp = drvp->chnl_softc;
int drive;
WDCDEBUG_PRINT(("ata_reset_channel %s:%d for drive %d\n",
chp->wdc->sc_dev.dv_xname, chp->channel, drvp->drive),
DEBUG_FUNCS);
(void) wdcreset(chp, VERBOSE);
for (drive = 0; drive < 2; drive++) {
chp->ch_drive[drive].state = 0;
}
}
int
wdcreset(chp, verb)
struct channel_softc *chp;
int verb;
{
int drv_mask1, drv_mask2;
bus_space_write_1(chp->cmd_iot, chp->cmd_ioh, wd_sdh,
WDSD_IBM); /* master */
bus_space_write_1(chp->ctl_iot, chp->ctl_ioh, wd_aux_ctlr,
WDCTL_RST | WDCTL_IDS);
delay(1000);
bus_space_write_1(chp->ctl_iot, chp->ctl_ioh, wd_aux_ctlr,
WDCTL_IDS);
delay(1000);
(void) bus_space_read_1(chp->cmd_iot, chp->cmd_ioh, wd_error);
bus_space_write_1(chp->ctl_iot, chp->ctl_ioh, wd_aux_ctlr,
WDCTL_4BIT);
drv_mask1 = (chp->ch_drive[0].drive_flags & DRIVE) ? 0x01:0x00;
drv_mask1 |= (chp->ch_drive[1].drive_flags & DRIVE) ? 0x02:0x00;
drv_mask2 = __wdcwait_reset(chp, drv_mask1);
if (verb && drv_mask2 != drv_mask1) {
printf("%s channel %d: reset failed for",
chp->wdc->sc_dev.dv_xname, chp->channel);
if ((drv_mask1 & 0x01) != 0 && (drv_mask2 & 0x01) == 0)
printf(" drive 0");
if ((drv_mask1 & 0x02) != 0 && (drv_mask2 & 0x02) == 0)
printf(" drive 1");
printf("\n");
}
return (drv_mask1 != drv_mask2) ? 1 : 0;
}
static int
__wdcwait_reset(chp, drv_mask)
struct channel_softc *chp;
int drv_mask;
{
int timeout;
u_int8_t st0, st1;
/* wait for BSY to deassert */
for (timeout = 0; timeout < WDCNDELAY_RST;timeout++) {
bus_space_write_1(chp->cmd_iot, chp->cmd_ioh, wd_sdh,
WDSD_IBM); /* master */
delay(1);
st0 = bus_space_read_1(chp->cmd_iot, chp->cmd_ioh, wd_status);
bus_space_write_1(chp->cmd_iot, chp->cmd_ioh, wd_sdh,
WDSD_IBM | 0x10); /* slave */
delay(1);
st1 = bus_space_read_1(chp->cmd_iot, chp->cmd_ioh, wd_status);
if ((drv_mask & 0x01) == 0) {
/* no master */
if ((drv_mask & 0x02) != 0 && (st1 & WDCS_BSY) == 0) {
/* No master, slave is ready, it's done */
return drv_mask;
}
} else if ((drv_mask & 0x02) == 0) {
/* no slave */
if ((drv_mask & 0x01) != 0 && (st0 & WDCS_BSY) == 0) {
/* No slave, master is ready, it's done */
return drv_mask;
}
} else {
/* Wait for both master and slave to be ready */
if ((st0 & WDCS_BSY) == 0 && (st1 & WDCS_BSY) == 0) {
return drv_mask;
}
}
delay(WDCDELAY);
}
/* Reset timed out. Maybe it's because drv_mask was not rigth */
if (st0 & WDCS_BSY)
drv_mask &= ~0x01;
if (st1 & WDCS_BSY)
drv_mask &= ~0x02;
return drv_mask;
}
/*
* Wait for a drive to be !BSY, and have mask in its status register.
* return -1 for a timeout after "timeout" ms.
*/
int
wdcwait(chp, mask, bits, timeout)
struct channel_softc *chp;
int mask, bits, timeout;
{
u_char status;
int time = 0;
#ifdef WDCNDELAY_DEBUG
extern int cold;
#endif
WDCDEBUG_PRINT(("wdcwait %s:%d\n", chp->wdc->sc_dev.dv_xname,
chp->channel), DEBUG_STATUS);
chp->ch_error = 0;
timeout = timeout * 1000 / WDCDELAY; /* delay uses microseconds */
for (;;) {
chp->ch_status = status =
bus_space_read_1(chp->cmd_iot, chp->cmd_ioh, wd_status);
if ((status & WDCS_BSY) == 0 && (status & mask) == bits)
break;
if (++time > timeout) {
WDCDEBUG_PRINT(("wdcwait: timeout, status %x "
"error %x\n", status,
bus_space_read_1(chp->cmd_iot, chp->cmd_ioh,
wd_error)),
DEBUG_STATUS);
return -1;
}
delay(WDCDELAY);
}
if (status & WDCS_ERR)
chp->ch_error = bus_space_read_1(chp->cmd_iot, chp->cmd_ioh,
wd_error);
#ifdef WDCNDELAY_DEBUG
/* After autoconfig, there should be no long delays. */
if (!cold && time > WDCNDELAY_DEBUG) {
struct wdc_xfer *xfer = chp->ch_queue->sc_xfer.tqh_first;
if (xfer == NULL)
printf("%s channel %d: warning: busy-wait took %dus\n",
chp->wdc->sc_dev.dv_xname, chp->channel,
WDCDELAY * time);
else
printf("%s:%d:%d: warning: busy-wait took %dus\n",
chp->wdc->sc_dev.dv_xname, chp->channel,
xfer->drive,
WDCDELAY * time);
}
#endif
return 0;
}
void
wdctimeout(arg)
void *arg;
{
struct channel_softc *chp = (struct channel_softc *)arg;
struct wdc_xfer *xfer = chp->ch_queue->sc_xfer.tqh_first;
int s;
WDCDEBUG_PRINT(("wdctimeout\n"), DEBUG_FUNCS);
s = splbio();
if ((chp->ch_flags & WDCF_IRQ_WAIT) != 0) {
__wdcerror(chp, "lost interrupt");
printf("\ttype: %s\n", (xfer->c_flags & C_ATAPI) ?
"atapi":"ata");
printf("\tc_bcount: %d\n", xfer->c_bcount);
printf("\tc_skip: %d\n", xfer->c_skip);
/*
* Call the interrupt routine. If we just missed and interrupt,
* it will do what's needed. Else, it will take the needed
* action (reset the device).
*/
xfer->c_flags |= C_TIMEOU;
chp->ch_flags &= ~WDCF_IRQ_WAIT;
xfer->c_intr(chp, xfer);
} else
__wdcerror(chp, "missing untimeout");
splx(s);
}
/*
* Probe drive's capabilites, for use by the controller later
* Assumes drvp points to an existing drive.
* XXX this should be a controller-indep function
*/
void
wdc_probe_caps(drvp)
struct ata_drive_datas *drvp;
{
struct ataparams params, params2;
struct channel_softc *chp = drvp->chnl_softc;
struct device *drv_dev = drvp->drv_softc;
struct wdc_softc *wdc = chp->wdc;
int i, printed;
char *sep = "";
int cf_flags;
if (ata_get_params(drvp, AT_POLL, &params) != CMD_OK) {
/* IDENTIFY failed. Can't tell more about the device */
return;
}
if ((wdc->cap & (WDC_CAPABILITY_DATA16 | WDC_CAPABILITY_DATA32)) ==
(WDC_CAPABILITY_DATA16 | WDC_CAPABILITY_DATA32)) {
/*
* Controller claims 16 and 32 bit transfers.
* Re-do an IDENTIFY with 32-bit transfers,
* and compare results.
*/
drvp->drive_flags |= DRIVE_CAP32;
ata_get_params(drvp, AT_POLL, &params2);
if (memcmp(&params, &params2, sizeof(struct ataparams)) != 0) {
/* Not good. fall back to 16bits */
drvp->drive_flags &= ~DRIVE_CAP32;
} else {
printf("%s: 32-bits data port", drv_dev->dv_xname);
}
}
#if 0 /* Some ultra-DMA drives claims to only support ATA-3. sigh */
if (params.atap_ata_major > 0x01 &&
params.atap_ata_major != 0xffff) {
for (i = 14; i > 0; i--) {
if (params.atap_ata_major & (1 << i)) {
if ((drvp->drive_flags & DRIVE_CAP32) == 0)
printf("%s: ", drv_dev->dv_xname);
else
printf(", ");
printf("ATA version %d\n", i);
drvp->ata_vers = i;
break;
}
}
} else if (drvp->drive_flags & DRIVE_CAP32)
#endif
printf("\n");
/* An ATAPI device is at last PIO mode 3 */
if (drvp->drive_flags & DRIVE_ATAPI)
drvp->PIO_mode = 3;
/*
* It's not in the specs, but it seems that some drive
* returns 0xffff in atap_extensions when this field is invalid
*/
if (params.atap_extensions != 0xffff &&
(params.atap_extensions & WDC_EXT_MODES)) {
printed = 0;
/*
* XXX some drives report something wrong here (they claim to
* support PIO mode 8 !). As mode is coded on 3 bits in
* SET FEATURE, limit it to 7 (so limit i to 4).
* If higther mode than 7 is found, abort.
*/
for (i = 7; i >= 0; i--) {
if ((params.atap_piomode_supp & (1 << i)) == 0)
continue;
if (i > 4)
return;
/*
* See if mode is accepted.
* If the controller can't set its PIO mode,
* assume the defaults are good, so don't try
* to set it
*/
if ((wdc->cap & WDC_CAPABILITY_MODE) != 0)
if (ata_set_mode(drvp, 0x08 | (i + 3),
AT_POLL) != CMD_OK)
continue;
if (!printed) {
printf("%s: drive supports PIO mode %d",
drv_dev->dv_xname, i + 3);
sep = ",";
printed = 1;
}
/*
* If controller's driver can't set its PIO mode,
* get the highter one for the drive.
*/
if ((wdc->cap & WDC_CAPABILITY_MODE) == 0 ||
wdc->PIO_cap >= i + 3) {
drvp->PIO_mode = i + 3;
drvp->PIO_cap = i + 3;
break;
}
}
if (!printed) {
/*
* We didn't find a valid PIO mode.
* Assume the values returned for DMA are buggy too
*/
return;
}
drvp->drive_flags |= DRIVE_MODE;
printed = 0;
for (i = 7; i >= 0; i--) {
if ((params.atap_dmamode_supp & (1 << i)) == 0)
continue;
if ((wdc->cap & WDC_CAPABILITY_DMA) &&
(wdc->cap & WDC_CAPABILITY_MODE))
if (ata_set_mode(drvp, 0x20 | i, AT_POLL)
!= CMD_OK)
continue;
if (!printed) {
printf("%s DMA mode %d", sep, i);
sep = ",";
printed = 1;
}
if (wdc->cap & WDC_CAPABILITY_DMA) {
if ((wdc->cap & WDC_CAPABILITY_MODE) &&
wdc->DMA_cap < i)
continue;
drvp->DMA_mode = i;
drvp->DMA_cap = i;
drvp->drive_flags |= DRIVE_DMA;
}
break;
}
if (params.atap_extensions & WDC_EXT_UDMA_MODES) {
for (i = 7; i >= 0; i--) {
if ((params.atap_udmamode_supp & (1 << i))
== 0)
continue;
if ((wdc->cap & WDC_CAPABILITY_MODE) &&
(wdc->cap & WDC_CAPABILITY_UDMA))
if (ata_set_mode(drvp, 0x40 | i,
AT_POLL) != CMD_OK)
continue;
printf("%s Ultra-DMA mode %d", sep, i);
sep = ",";
if (wdc->cap & WDC_CAPABILITY_UDMA) {
if ((wdc->cap & WDC_CAPABILITY_MODE) &&
wdc->UDMA_cap < i)
continue;
drvp->UDMA_mode = i;
drvp->UDMA_cap = i;
drvp->drive_flags |= DRIVE_UDMA;
}
break;
}
}
printf("\n");
}
/* Try to guess ATA version here, if it didn't get reported */
if (drvp->ata_vers == 0) {
if (drvp->drive_flags & DRIVE_UDMA)
drvp->ata_vers = 4; /* should be at last ATA-4 */
else if (drvp->PIO_cap > 2)
drvp->ata_vers = 2; /* should be at last ATA-2 */
}
cf_flags = drv_dev->dv_cfdata->cf_flags;
if (cf_flags & ATA_CONFIG_PIO_SET) {
drvp->PIO_mode =
(cf_flags & ATA_CONFIG_PIO_MODES) >> ATA_CONFIG_PIO_OFF;
drvp->drive_flags |= DRIVE_MODE;
}
if ((wdc->cap & WDC_CAPABILITY_DMA) == 0) {
/* don't care about DMA modes */
return;
}
if (cf_flags & ATA_CONFIG_DMA_SET) {
if ((cf_flags & ATA_CONFIG_DMA_MODES) ==
ATA_CONFIG_DMA_DISABLE) {
drvp->drive_flags &= ~DRIVE_DMA;
} else {
drvp->DMA_mode = (cf_flags & ATA_CONFIG_DMA_MODES) >>
ATA_CONFIG_DMA_OFF;
drvp->drive_flags |= DRIVE_DMA | DRIVE_MODE;
}
}
if (cf_flags & ATA_CONFIG_UDMA_SET) {
if ((cf_flags & ATA_CONFIG_UDMA_MODES) ==
ATA_CONFIG_UDMA_DISABLE) {
drvp->drive_flags &= ~DRIVE_UDMA;
} else {
drvp->UDMA_mode = (cf_flags & ATA_CONFIG_UDMA_MODES) >>
ATA_CONFIG_UDMA_OFF;
drvp->drive_flags |= DRIVE_UDMA | DRIVE_MODE;
}
}
}
/*
* downgrade the transfer mode of a drive after an error. return 1 if
* downgrade was possible, 0 otherwise.
*/
int
wdc_downgrade_mode(drvp)
struct ata_drive_datas *drvp;
{
struct channel_softc *chp = drvp->chnl_softc;
struct device *drv_dev = drvp->drv_softc;
struct wdc_softc *wdc = chp->wdc;
int cf_flags = drv_dev->dv_cfdata->cf_flags;
/* if drive or controller don't know its mode, we can't do much */
if ((drvp->drive_flags & DRIVE_MODE) == 0 ||
(wdc->cap & WDC_CAPABILITY_MODE) == 0)
return 0;
/* current drive mode was set by a config flag, let it this way */
if ((cf_flags & ATA_CONFIG_PIO_SET) ||
(cf_flags & ATA_CONFIG_DMA_SET) ||
(cf_flags & ATA_CONFIG_UDMA_SET))
return 0;
if (drvp->drive_flags & DRIVE_UDMA) {
drvp->drive_flags &= ~DRIVE_UDMA;
drvp->drive_flags |= DRIVE_DMA;
drvp->DMA_mode = drvp->DMA_cap;
printf("%s: transfer error, downgrading to DMA mode %d\n",
drv_dev->dv_xname, drvp->DMA_mode);
} else if (drvp->drive_flags & DRIVE_DMA) {
drvp->drive_flags &= ~DRIVE_DMA;
drvp->PIO_mode = drvp->PIO_cap;
printf("%s: transfer error, downgrading to PIO mode %d\n",
drv_dev->dv_xname, drvp->PIO_mode);
} else /* already using PIO, can't downgrade */
return 0;
wdc->set_modes(chp);
/* reset the channel, which will shedule all drives for setup */
wdc_reset_channel(drvp);
return 1;
}
int
wdc_exec_command(drvp, wdc_c)
struct ata_drive_datas *drvp;
struct wdc_command *wdc_c;
{
struct channel_softc *chp = drvp->chnl_softc;
struct wdc_xfer *xfer;
int s, ret;
WDCDEBUG_PRINT(("wdc_exec_command %s:%d:%d\n",
chp->wdc->sc_dev.dv_xname, chp->channel, drvp->drive),
DEBUG_FUNCS);
/* set up an xfer and queue. Wait for completion */
xfer = wdc_get_xfer(wdc_c->flags & AT_WAIT ? WDC_CANSLEEP :
WDC_NOSLEEP);
if (xfer == NULL) {
return WDC_TRY_AGAIN;
}
if (wdc_c->flags & AT_POLL)
xfer->c_flags |= C_POLL;
xfer->drive = drvp->drive;
xfer->databuf = wdc_c->data;
xfer->c_bcount = wdc_c->bcount;
xfer->cmd = wdc_c;
xfer->c_start = __wdccommand_start;
xfer->c_intr = __wdccommand_intr;
s = splbio();
wdc_exec_xfer(chp, xfer);
#ifdef DIAGNOSTIC
if ((wdc_c->flags & AT_POLL) != 0 &&
(wdc_c->flags & AT_DONE) == 0)
panic("wdc_exec_command: polled command not done\n");
#endif
if (wdc_c->flags & AT_DONE) {
ret = WDC_COMPLETE;
} else {
if (wdc_c->flags & AT_WAIT) {
tsleep(wdc_c, PRIBIO, "wdccmd", 0);
ret = WDC_COMPLETE;
} else {
ret = WDC_QUEUED;
}
}
splx(s);
return ret;
}
void
__wdccommand_start(chp, xfer)
struct channel_softc *chp;
struct wdc_xfer *xfer;
{
int drive = xfer->drive;
struct wdc_command *wdc_c = xfer->cmd;
WDCDEBUG_PRINT(("__wdccommand_start %s:%d:%d\n",
chp->wdc->sc_dev.dv_xname, chp->channel, xfer->drive),
DEBUG_FUNCS);
bus_space_write_1(chp->cmd_iot, chp->cmd_ioh, wd_sdh,
WDSD_IBM | (drive << 4));
if (wdcwait(chp, wdc_c->r_st_bmask, wdc_c->r_st_bmask,
wdc_c->timeout) != 0) {
wdc_c->flags |= AT_TIMEOU;
__wdccommand_done(chp, xfer);
return;
}
wdccommand(chp, drive, wdc_c->r_command, wdc_c->r_cyl, wdc_c->r_head,
wdc_c->r_sector, wdc_c->r_count, wdc_c->r_precomp);
if ((wdc_c->flags & AT_POLL) == 0) {
chp->ch_flags |= WDCF_IRQ_WAIT; /* wait for interrupt */
timeout(wdctimeout, chp, wdc_c->timeout / 1000 * hz);
return;
}
/*
* Polled command. Wait for drive ready or drq. Done in intr().
* Wait for at last 400ns for status bit to be valid.
*/
delay(10);
if (__wdccommand_intr(chp, xfer) == 0) {
wdc_c->flags |= AT_TIMEOU;
__wdccommand_done(chp, xfer);
}
}
int
__wdccommand_intr(chp, xfer)
struct channel_softc *chp;
struct wdc_xfer *xfer;
{
struct wdc_command *wdc_c = xfer->cmd;
int bcount = wdc_c->bcount;
char *data = wdc_c->data;
WDCDEBUG_PRINT(("__wdccommand_intr %s:%d:%d\n",
chp->wdc->sc_dev.dv_xname, chp->channel, xfer->drive), DEBUG_INTR);
if (wdcwait(chp, wdc_c->r_st_pmask, wdc_c->r_st_pmask,
wdc_c->timeout)) {
wdc_c->flags |= AT_ERROR;
__wdccommand_done(chp, xfer);
return 1;
}
if (wdc_c->flags & AT_READ) {
if (chp->ch_drive[xfer->drive].drive_flags & DRIVE_CAP32) {
bus_space_read_multi_4(chp->data32iot, chp->data32ioh,
0, (u_int32_t*)data, bcount >> 2);
data += bcount & 0xfffffffc;
bcount = bcount & 0x03;
}
if (bcount > 0)
bus_space_read_multi_2(chp->cmd_iot, chp->cmd_ioh,
wd_data, (u_int16_t *)data, bcount >> 1);
} else if (wdc_c->flags & AT_WRITE) {
if (chp->ch_drive[xfer->drive].drive_flags & DRIVE_CAP32) {
bus_space_write_multi_4(chp->data32iot, chp->data32ioh,
0, (u_int32_t*)data, bcount >> 2);
data += bcount & 0xfffffffc;
bcount = bcount & 0x03;
}
if (bcount > 0)
bus_space_write_multi_2(chp->cmd_iot, chp->cmd_ioh,
wd_data, (u_int16_t *)data, bcount >> 1);
}
__wdccommand_done(chp, xfer);
return 1;
}
void
__wdccommand_done(chp, xfer)
struct channel_softc *chp;
struct wdc_xfer *xfer;
{
int needdone = xfer->c_flags & C_NEEDDONE;
struct wdc_command *wdc_c = xfer->cmd;
WDCDEBUG_PRINT(("__wdccommand_done %s:%d:%d\n",
chp->wdc->sc_dev.dv_xname, chp->channel, xfer->drive), DEBUG_FUNCS);
if (chp->ch_status & WDCS_DWF)
wdc_c->flags |= AT_DF;
if (chp->ch_status & WDCS_ERR) {
wdc_c->flags |= AT_ERROR;
wdc_c->r_error = chp->ch_error;
}
wdc_c->flags |= AT_DONE;
if (wdc_c->flags & AT_READREG && (wdc_c->flags & (AT_ERROR | AT_DF))
== 0) {
wdc_c->r_head = bus_space_read_1(chp->cmd_iot, chp->cmd_ioh,
wd_sdh);
wdc_c->r_cyl = bus_space_read_1(chp->cmd_iot, chp->cmd_ioh,
wd_cyl_hi) << 8;
wdc_c->r_cyl |= bus_space_read_1(chp->cmd_iot, chp->cmd_ioh,
wd_cyl_lo);
wdc_c->r_sector = bus_space_read_1(chp->cmd_iot, chp->cmd_ioh,
wd_sector);
wdc_c->r_count = bus_space_read_1(chp->cmd_iot, chp->cmd_ioh,
wd_seccnt);
wdc_c->r_error = bus_space_read_1(chp->cmd_iot, chp->cmd_ioh,
wd_error);
wdc_c->r_precomp = bus_space_read_1(chp->cmd_iot, chp->cmd_ioh,
wd_precomp);
}
wdc_free_xfer(chp, xfer);
if (needdone) {
if (wdc_c->flags & AT_WAIT)
wakeup(wdc_c);
else
wdc_c->callback(wdc_c->callback_arg);
}
wdcstart(chp);
return;
}
/*
* Send a command. The drive should be ready.
* Assumes interrupts are blocked.
*/
void
wdccommand(chp, drive, command, cylin, head, sector, count, precomp)
struct channel_softc *chp;
u_int8_t drive;
u_int8_t command;
u_int16_t cylin;
u_int8_t head, sector, count, precomp;
{
WDCDEBUG_PRINT(("wdccommand %s:%d:%d: command=0x%x cylin=%d head=%d "
"sector=%d count=%d precomp=%d\n", chp->wdc->sc_dev.dv_xname,
chp->channel, drive, command, cylin, head, sector, count, precomp),
DEBUG_FUNCS);
/* Select drive, head, and addressing mode. */
bus_space_write_1(chp->cmd_iot, chp->cmd_ioh, wd_sdh,
WDSD_IBM | (drive << 4) | head);
/* Load parameters. wd_features(ATA/ATAPI) = wd_precomp(ST506) */
bus_space_write_1(chp->cmd_iot, chp->cmd_ioh, wd_precomp,
precomp);
bus_space_write_1(chp->cmd_iot, chp->cmd_ioh, wd_cyl_lo, cylin);
bus_space_write_1(chp->cmd_iot, chp->cmd_ioh, wd_cyl_hi, cylin >> 8);
bus_space_write_1(chp->cmd_iot, chp->cmd_ioh, wd_sector, sector);
bus_space_write_1(chp->cmd_iot, chp->cmd_ioh, wd_seccnt, count);
/* Send command. */
bus_space_write_1(chp->cmd_iot, chp->cmd_ioh, wd_command, command);
return;
}
/*
* Simplified version of wdccommand(). Unbusy/ready/drq must be
* tested by the caller.
*/
void
wdccommandshort(chp, drive, command)
struct channel_softc *chp;
int drive;
int command;
{
WDCDEBUG_PRINT(("wdccommandshort %s:%d:%d command 0x%x\n",
chp->wdc->sc_dev.dv_xname, chp->channel, drive, command),
DEBUG_FUNCS);
/* Select drive. */
bus_space_write_1(chp->cmd_iot, chp->cmd_ioh, wd_sdh,
WDSD_IBM | (drive << 4));
bus_space_write_1(chp->cmd_iot, chp->cmd_ioh, wd_command, command);
}
/* Add a command to the queue and start controller. Must be called at splbio */
void
wdc_exec_xfer(chp, xfer)
struct channel_softc *chp;
struct wdc_xfer *xfer;
{
WDCDEBUG_PRINT(("wdc_exec_xfer %p channel %d drive %d\n", xfer,
chp->channel, xfer->drive), DEBUG_XFERS);
/* complete xfer setup */
xfer->chp = chp;
/*
* If we are a polled command, and the list is not empty,
* we are doing a dump. Drop the list to allow the polled command
* to complete, we're going to reboot soon anyway.
*/
if ((xfer->c_flags & C_POLL) != 0 &&
chp->ch_queue->sc_xfer.tqh_first != NULL) {
TAILQ_INIT(&chp->ch_queue->sc_xfer);
}
/* insert at the end of command list */
TAILQ_INSERT_TAIL(&chp->ch_queue->sc_xfer,xfer , c_xferchain);
WDCDEBUG_PRINT(("wdcstart from wdc_exec_xfer, flags 0x%x\n",
chp->ch_flags), DEBUG_XFERS);
wdcstart(chp);
xfer->c_flags |= C_NEEDDONE; /* we can now call upper level done() */
}
struct wdc_xfer *
wdc_get_xfer(flags)
int flags;
{
struct wdc_xfer *xfer;
int s;
s = splbio();
if ((xfer = xfer_free_list.lh_first) != NULL) {
LIST_REMOVE(xfer, free_list);
splx(s);
#ifdef DIAGNOSTIC
if ((xfer->c_flags & C_INUSE) != 0)
panic("wdc_get_xfer: xfer already in use\n");
#endif
} else {
splx(s);
WDCDEBUG_PRINT(("wdc:making xfer %d\n",wdc_nxfer), DEBUG_XFERS);
xfer = malloc(sizeof(*xfer), M_DEVBUF,
((flags & WDC_NOSLEEP) != 0 ? M_NOWAIT : M_WAITOK));
if (xfer == NULL)
return 0;
#ifdef DIAGNOSTIC
xfer->c_flags &= ~C_INUSE;
#endif
#ifdef WDCDEBUG
wdc_nxfer++;
#endif
}
#ifdef DIAGNOSTIC
if ((xfer->c_flags & C_INUSE) != 0)
panic("wdc_get_xfer: xfer already in use\n");
#endif
memset(xfer, 0, sizeof(struct wdc_xfer));
xfer->c_flags = C_INUSE;
return xfer;
}
void
wdc_free_xfer(chp, xfer)
struct channel_softc *chp;
struct wdc_xfer *xfer;
{
struct wdc_softc *wdc = chp->wdc;
int s;
if (wdc->cap & WDC_CAPABILITY_HWLOCK)
(*wdc->free_hw)(chp);
s = splbio();
chp->ch_flags &= ~WDCF_ACTIVE;
TAILQ_REMOVE(&chp->ch_queue->sc_xfer, xfer, c_xferchain);
xfer->c_flags &= ~C_INUSE;
LIST_INSERT_HEAD(&xfer_free_list, xfer, free_list);
splx(s);
}
static void
__wdcerror(chp, msg)
struct channel_softc *chp;
char *msg;
{
struct wdc_xfer *xfer = chp->ch_queue->sc_xfer.tqh_first;
if (xfer == NULL)
printf("%s:%d: %s\n", chp->wdc->sc_dev.dv_xname, chp->channel,
msg);
else
printf("%s:%d:%d: %s\n", chp->wdc->sc_dev.dv_xname,
chp->channel, xfer->drive, msg);
}
/*
* the bit bucket
*/
void
wdcbit_bucket(chp, size)
struct channel_softc *chp;
int size;
{
for (; size >= 2; size -= 2)
(void)bus_space_read_2(chp->cmd_iot, chp->cmd_ioh, wd_data);
if (size)
(void)bus_space_read_1(chp->cmd_iot, chp->cmd_ioh, wd_data);
}
int
wdc_addref(chp)
struct channel_softc *chp;
{
struct wdc_softc *wdc = chp->wdc;
struct scsipi_adapter *adapter = &wdc->sc_atapi_adapter;
int s, error = 0;
s = splbio();
if (adapter->scsipi_refcnt++ == 0 &&
adapter->scsipi_enable != NULL) {
error = (*adapter->scsipi_enable)(wdc, 1);
if (error)
adapter->scsipi_refcnt--;
}
splx(s);
return (error);
}
void
wdc_delref(chp)
struct channel_softc *chp;
{
struct wdc_softc *wdc = chp->wdc;
struct scsipi_adapter *adapter = &wdc->sc_atapi_adapter;
int s;
s = splbio();
if (adapter->scsipi_refcnt-- == 1 &&
adapter->scsipi_enable != NULL)
(void) (*adapter->scsipi_enable)(wdc, 0);
splx(s);
}