NetBSD/sys/dev/ic/mvsata.c
christos c182898b0d We have three sets of DTYPE_ constants in the kernel:
altq		Drop 		Type
	disklabel	Disk 		Type
	file		Descriptor	Type
(not to mention constants that contain the string DTYPE).
Let's make them two, by changing the disklabel one to be DisK TYPE since the
other disklabel constants seem to do that. Not many userland programs use
these constants (and the ones that they do are mostly in ifdefs). They will
be fixed shortly.
2015-01-02 19:42:05 +00:00

3796 lines
110 KiB
C

/* $NetBSD: mvsata.c,v 1.34 2015/01/02 19:42:07 christos Exp $ */
/*
* Copyright (c) 2008 KIYOHARA Takashi
* 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.
*
* 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.
*/
#include <sys/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD: mvsata.c,v 1.34 2015/01/02 19:42:07 christos Exp $");
#include "opt_mvsata.h"
/* ATAPI implementation not finished. */
//#include "atapibus.h"
#include <sys/param.h>
#if NATAPIBUS > 0
#include <sys/buf.h>
#endif
#include <sys/bus.h>
#include <sys/cpu.h>
#include <sys/device.h>
#include <sys/disklabel.h>
#include <sys/errno.h>
#include <sys/kernel.h>
#include <sys/malloc.h>
#include <sys/proc.h>
#include <machine/vmparam.h>
#include <dev/ata/atareg.h>
#include <dev/ata/atavar.h>
#include <dev/ic/wdcvar.h>
#include <dev/ata/satapmpreg.h>
#include <dev/ata/satareg.h>
#include <dev/ata/satavar.h>
#if NATAPIBUS > 0
#include <dev/scsipi/scsi_all.h> /* for SCSI status */
#endif
#include <dev/pci/pcidevs.h>
#include <dev/ic/mvsatareg.h>
#include <dev/ic/mvsatavar.h>
#define MVSATA_DEV(sc) ((sc)->sc_wdcdev.sc_atac.atac_dev)
#define MVSATA_DEV2(mvport) ((mvport)->port_ata_channel.ch_atac->atac_dev)
#define MVSATA_HC_READ_4(hc, reg) \
bus_space_read_4((hc)->hc_iot, (hc)->hc_ioh, (reg))
#define MVSATA_HC_WRITE_4(hc, reg, val) \
bus_space_write_4((hc)->hc_iot, (hc)->hc_ioh, (reg), (val))
#define MVSATA_EDMA_READ_4(mvport, reg) \
bus_space_read_4((mvport)->port_iot, (mvport)->port_ioh, (reg))
#define MVSATA_EDMA_WRITE_4(mvport, reg, val) \
bus_space_write_4((mvport)->port_iot, (mvport)->port_ioh, (reg), (val))
#define MVSATA_WDC_READ_2(mvport, reg) \
bus_space_read_2((mvport)->port_iot, (mvport)->port_ioh, \
SHADOW_REG_BLOCK_OFFSET + (reg))
#define MVSATA_WDC_READ_1(mvport, reg) \
bus_space_read_1((mvport)->port_iot, (mvport)->port_ioh, \
SHADOW_REG_BLOCK_OFFSET + (reg))
#define MVSATA_WDC_WRITE_2(mvport, reg, val) \
bus_space_write_2((mvport)->port_iot, (mvport)->port_ioh, \
SHADOW_REG_BLOCK_OFFSET + (reg), (val))
#define MVSATA_WDC_WRITE_1(mvport, reg, val) \
bus_space_write_1((mvport)->port_iot, (mvport)->port_ioh, \
SHADOW_REG_BLOCK_OFFSET + (reg), (val))
#ifdef MVSATA_DEBUG
#define DPRINTF(x) if (mvsata_debug) printf x
#define DPRINTFN(n,x) if (mvsata_debug >= (n)) printf x
int mvsata_debug = 2;
#else
#define DPRINTF(x)
#define DPRINTFN(n,x)
#endif
#define ATA_DELAY 10000 /* 10s for a drive I/O */
#define ATAPI_DELAY 10 /* 10 ms, this is used only before
sending a cmd */
#define ATAPI_MODE_DELAY 1000 /* 1s, timeout for SET_FEATURE cmds */
#define MVSATA_EPRD_MAX_SIZE (sizeof(struct eprd) * (MAXPHYS / PAGE_SIZE))
static void mvsata_probe_drive(struct ata_channel *);
#ifndef MVSATA_WITHOUTDMA
static int mvsata_bio(struct ata_drive_datas *, struct ata_bio *);
static void mvsata_reset_drive(struct ata_drive_datas *, int, uint32_t *);
static void mvsata_reset_channel(struct ata_channel *, int);
static int mvsata_exec_command(struct ata_drive_datas *, struct ata_command *);
static int mvsata_addref(struct ata_drive_datas *);
static void mvsata_delref(struct ata_drive_datas *);
static void mvsata_killpending(struct ata_drive_datas *);
#if NATAPIBUS > 0
static void mvsata_atapibus_attach(struct atabus_softc *);
static void mvsata_atapi_scsipi_request(struct scsipi_channel *,
scsipi_adapter_req_t, void *);
static void mvsata_atapi_minphys(struct buf *);
static void mvsata_atapi_probe_device(struct atapibus_softc *, int);
static void mvsata_atapi_kill_pending(struct scsipi_periph *);
#endif
#endif
static void mvsata_setup_channel(struct ata_channel *);
#ifndef MVSATA_WITHOUTDMA
static void mvsata_bio_start(struct ata_channel *, struct ata_xfer *);
static int mvsata_bio_intr(struct ata_channel *, struct ata_xfer *, int);
static void mvsata_bio_kill_xfer(struct ata_channel *, struct ata_xfer *, int);
static void mvsata_bio_done(struct ata_channel *, struct ata_xfer *);
static int mvsata_bio_ready(struct mvsata_port *, struct ata_bio *, int,
int);
static void mvsata_wdc_cmd_start(struct ata_channel *, struct ata_xfer *);
static int mvsata_wdc_cmd_intr(struct ata_channel *, struct ata_xfer *, int);
static void mvsata_wdc_cmd_kill_xfer(struct ata_channel *, struct ata_xfer *,
int);
static void mvsata_wdc_cmd_done(struct ata_channel *, struct ata_xfer *);
static void mvsata_wdc_cmd_done_end(struct ata_channel *, struct ata_xfer *);
#if NATAPIBUS > 0
static void mvsata_atapi_start(struct ata_channel *, struct ata_xfer *);
static int mvsata_atapi_intr(struct ata_channel *, struct ata_xfer *, int);
static void mvsata_atapi_kill_xfer(struct ata_channel *, struct ata_xfer *,
int);
static void mvsata_atapi_reset(struct ata_channel *, struct ata_xfer *);
static void mvsata_atapi_phase_complete(struct ata_xfer *);
static void mvsata_atapi_done(struct ata_channel *, struct ata_xfer *);
static void mvsata_atapi_polldsc(void *);
#endif
static int mvsata_edma_enqueue(struct mvsata_port *, struct ata_bio *, void *);
static int mvsata_edma_handle(struct mvsata_port *, struct ata_xfer *);
static int mvsata_edma_wait(struct mvsata_port *, struct ata_xfer *, int);
static void mvsata_edma_timeout(void *);
static void mvsata_edma_rqq_remove(struct mvsata_port *, struct ata_xfer *);
#if NATAPIBUS > 0
static int mvsata_bdma_init(struct mvsata_port *, struct scsipi_xfer *, void *);
static void mvsata_bdma_start(struct mvsata_port *);
#endif
#endif
static int mvsata_port_init(struct mvsata_hc *, int);
static int mvsata_wdc_reg_init(struct mvsata_port *, struct wdc_regs *);
#ifndef MVSATA_WITHOUTDMA
static inline void mvsata_quetag_init(struct mvsata_port *);
static inline int mvsata_quetag_get(struct mvsata_port *);
static inline void mvsata_quetag_put(struct mvsata_port *, int);
static void *mvsata_edma_resource_prepare(struct mvsata_port *, bus_dma_tag_t,
bus_dmamap_t *, size_t, int);
static void mvsata_edma_resource_purge(struct mvsata_port *, bus_dma_tag_t,
bus_dmamap_t, void *);
static int mvsata_dma_bufload(struct mvsata_port *, int, void *, size_t, int);
static inline void mvsata_dma_bufunload(struct mvsata_port *, int, int);
#endif
static void mvsata_hreset_port(struct mvsata_port *);
static void mvsata_reset_port(struct mvsata_port *);
static void mvsata_reset_hc(struct mvsata_hc *);
static uint32_t mvsata_softreset(struct mvsata_port *, int);
#ifndef MVSATA_WITHOUTDMA
static void mvsata_edma_reset_qptr(struct mvsata_port *);
static inline void mvsata_edma_enable(struct mvsata_port *);
static int mvsata_edma_disable(struct mvsata_port *, int, int);
static void mvsata_edma_config(struct mvsata_port *, int);
static void mvsata_edma_setup_crqb(struct mvsata_port *, int, int,
struct ata_bio *);
#endif
static uint32_t mvsata_read_preamps_gen1(struct mvsata_port *);
static void mvsata_fix_phy_gen1(struct mvsata_port *);
static void mvsata_devconn_gen1(struct mvsata_port *);
static uint32_t mvsata_read_preamps_gen2(struct mvsata_port *);
static void mvsata_fix_phy_gen2(struct mvsata_port *);
#ifndef MVSATA_WITHOUTDMA
static void mvsata_edma_setup_crqb_gen2e(struct mvsata_port *, int, int,
struct ata_bio *);
#ifdef MVSATA_DEBUG
static void mvsata_print_crqb(struct mvsata_port *, int);
static void mvsata_print_crpb(struct mvsata_port *, int);
static void mvsata_print_eprd(struct mvsata_port *, int);
#endif
struct ata_bustype mvsata_ata_bustype = {
SCSIPI_BUSTYPE_ATA,
mvsata_bio,
mvsata_reset_drive,
mvsata_reset_channel,
mvsata_exec_command,
ata_get_params,
mvsata_addref,
mvsata_delref,
mvsata_killpending
};
#if NATAPIBUS > 0
static const struct scsipi_bustype mvsata_atapi_bustype = {
SCSIPI_BUSTYPE_ATAPI,
atapi_scsipi_cmd,
atapi_interpret_sense,
atapi_print_addr,
mvsata_atapi_kill_pending,
NULL,
};
#endif /* NATAPIBUS */
#endif
static void
mvsata_pmp_select(struct mvsata_port *mvport, int pmpport)
{
uint32_t ifctl;
KASSERT(pmpport < PMP_MAX_DRIVES);
#if defined(DIAGNOSTIC) || defined(MVSATA_DEBUG)
if ((MVSATA_EDMA_READ_4(mvport, EDMA_CMD) & EDMA_CMD_EENEDMA) != 0) {
panic("EDMA enabled");
}
#endif
ifctl = MVSATA_EDMA_READ_4(mvport, SATA_SATAICTL);
ifctl &= ~0xf;
ifctl |= pmpport;
MVSATA_EDMA_WRITE_4(mvport, SATA_SATAICTL, ifctl);
}
int
mvsata_attach(struct mvsata_softc *sc, struct mvsata_product *product,
int (*mvsata_sreset)(struct mvsata_softc *),
int (*mvsata_misc_reset)(struct mvsata_softc *),
int read_pre_amps)
{
struct mvsata_hc *mvhc;
struct mvsata_port *mvport;
uint32_t (*read_preamps)(struct mvsata_port *) = NULL;
void (*_fix_phy)(struct mvsata_port *) = NULL;
#ifndef MVSATA_WITHOUTDMA
void (*edma_setup_crqb)
(struct mvsata_port *, int, int, struct ata_bio *) = NULL;
#endif
int hc, port, channel;
aprint_normal_dev(MVSATA_DEV(sc), "Gen%s, %dhc, %dport/hc\n",
(product->generation == gen1) ? "I" :
((product->generation == gen2) ? "II" : "IIe"),
product->hc, product->port);
switch (product->generation) {
case gen1:
mvsata_sreset = NULL;
read_pre_amps = 1; /* MUST */
read_preamps = mvsata_read_preamps_gen1;
_fix_phy = mvsata_fix_phy_gen1;
#ifndef MVSATA_WITHOUTDMA
edma_setup_crqb = mvsata_edma_setup_crqb;
#endif
break;
case gen2:
read_preamps = mvsata_read_preamps_gen2;
_fix_phy = mvsata_fix_phy_gen2;
#ifndef MVSATA_WITHOUTDMA
edma_setup_crqb = mvsata_edma_setup_crqb;
#endif
break;
case gen2e:
read_preamps = mvsata_read_preamps_gen2;
_fix_phy = mvsata_fix_phy_gen2;
#ifndef MVSATA_WITHOUTDMA
edma_setup_crqb = mvsata_edma_setup_crqb_gen2e;
#endif
break;
}
sc->sc_gen = product->generation;
sc->sc_hc = product->hc;
sc->sc_port = product->port;
sc->sc_flags = product->flags;
#ifdef MVSATA_WITHOUTDMA
sc->sc_wdcdev.sc_atac.atac_cap |= ATAC_CAP_DATA16;
#else
sc->sc_edma_setup_crqb = edma_setup_crqb;
sc->sc_wdcdev.sc_atac.atac_cap |=
(ATAC_CAP_DATA16 | ATAC_CAP_DMA | ATAC_CAP_UDMA);
#endif
sc->sc_wdcdev.sc_atac.atac_pio_cap = 4;
#ifdef MVSATA_WITHOUTDMA
sc->sc_wdcdev.sc_atac.atac_dma_cap = 0;
sc->sc_wdcdev.sc_atac.atac_udma_cap = 0;
#else
sc->sc_wdcdev.sc_atac.atac_dma_cap = 2;
sc->sc_wdcdev.sc_atac.atac_udma_cap = 6;
#endif
sc->sc_wdcdev.sc_atac.atac_channels = sc->sc_ata_channels;
sc->sc_wdcdev.sc_atac.atac_nchannels = sc->sc_hc * sc->sc_port;
#ifndef MVSATA_WITHOUTDMA
sc->sc_wdcdev.sc_atac.atac_bustype_ata = &mvsata_ata_bustype;
#if NATAPIBUS > 0
sc->sc_wdcdev.sc_atac.atac_atapibus_attach = mvsata_atapibus_attach;
#endif
#endif
sc->sc_wdcdev.wdc_maxdrives = 1; /* SATA is always 1 drive */
sc->sc_wdcdev.sc_atac.atac_probe = mvsata_probe_drive;
sc->sc_wdcdev.sc_atac.atac_set_modes = mvsata_setup_channel;
sc->sc_wdc_regs =
malloc(sizeof(struct wdc_regs) * product->hc * product->port,
M_DEVBUF, M_NOWAIT);
if (sc->sc_wdc_regs == NULL) {
aprint_error_dev(MVSATA_DEV(sc),
"can't allocate wdc regs memory\n");
return ENOMEM;
}
sc->sc_wdcdev.regs = sc->sc_wdc_regs;
for (hc = 0; hc < sc->sc_hc; hc++) {
mvhc = &sc->sc_hcs[hc];
mvhc->hc = hc;
mvhc->hc_sc = sc;
mvhc->hc_iot = sc->sc_iot;
if (bus_space_subregion(sc->sc_iot, sc->sc_ioh,
hc * SATAHC_REGISTER_SIZE, SATAHC_REGISTER_SIZE,
&mvhc->hc_ioh)) {
aprint_error_dev(MVSATA_DEV(sc),
"can't subregion SATAHC %d registers\n", hc);
continue;
}
for (port = 0; port < sc->sc_port; port++)
if (mvsata_port_init(mvhc, port) == 0) {
int pre_amps;
mvport = mvhc->hc_ports[port];
pre_amps = read_pre_amps ?
read_preamps(mvport) : 0x00000720;
mvport->_fix_phy_param.pre_amps = pre_amps;
mvport->_fix_phy_param._fix_phy = _fix_phy;
if (!mvsata_sreset)
mvsata_reset_port(mvport);
}
if (!mvsata_sreset)
mvsata_reset_hc(mvhc);
}
if (mvsata_sreset)
mvsata_sreset(sc);
if (mvsata_misc_reset)
mvsata_misc_reset(sc);
for (hc = 0; hc < sc->sc_hc; hc++)
for (port = 0; port < sc->sc_port; port++) {
mvport = sc->sc_hcs[hc].hc_ports[port];
if (mvport == NULL)
continue;
if (mvsata_sreset)
mvport->_fix_phy_param._fix_phy(mvport);
}
for (channel = 0; channel < sc->sc_hc * sc->sc_port; channel++)
wdcattach(sc->sc_ata_channels[channel]);
return 0;
}
int
mvsata_intr(struct mvsata_hc *mvhc)
{
struct mvsata_softc *sc = mvhc->hc_sc;
struct mvsata_port *mvport;
uint32_t cause;
int port, handled = 0;
cause = MVSATA_HC_READ_4(mvhc, SATAHC_IC);
DPRINTFN(3, ("%s:%d: mvsata_intr: cause=0x%08x\n",
device_xname(MVSATA_DEV(sc)), mvhc->hc, cause));
if (cause & SATAHC_IC_SAINTCOAL)
MVSATA_HC_WRITE_4(mvhc, SATAHC_IC, ~SATAHC_IC_SAINTCOAL);
cause &= ~SATAHC_IC_SAINTCOAL;
for (port = 0; port < sc->sc_port; port++) {
mvport = mvhc->hc_ports[port];
if (cause & SATAHC_IC_DONE(port)) {
#ifndef MVSATA_WITHOUTDMA
handled = mvsata_edma_handle(mvport, NULL);
#endif
MVSATA_HC_WRITE_4(mvhc, SATAHC_IC,
~SATAHC_IC_DONE(port));
}
if (cause & SATAHC_IC_SADEVINTERRUPT(port)) {
wdcintr(&mvport->port_ata_channel);
MVSATA_HC_WRITE_4(mvhc, SATAHC_IC,
~SATAHC_IC_SADEVINTERRUPT(port));
handled = 1;
}
}
return handled;
}
int
mvsata_error(struct mvsata_port *mvport)
{
struct mvsata_softc *sc = device_private(MVSATA_DEV2(mvport));
uint32_t cause;
cause = MVSATA_EDMA_READ_4(mvport, EDMA_IEC);
/*
* We must ack SATA_SE and SATA_FISIC before acking coresponding bits
* in EDMA_IEC.
*/
if (cause & EDMA_IE_SERRINT) {
MVSATA_EDMA_WRITE_4(mvport, SATA_SE,
MVSATA_EDMA_READ_4(mvport, SATA_SEIM));
}
if (cause & EDMA_IE_ETRANSINT) {
MVSATA_EDMA_WRITE_4(mvport, SATA_FISIC,
~MVSATA_EDMA_READ_4(mvport, SATA_FISIM));
}
MVSATA_EDMA_WRITE_4(mvport, EDMA_IEC, ~cause);
DPRINTFN(3, ("%s:%d:%d:"
" mvsata_error: cause=0x%08x, mask=0x%08x, status=0x%08x\n",
device_xname(MVSATA_DEV2(mvport)), mvport->port_hc->hc,
mvport->port, cause, MVSATA_EDMA_READ_4(mvport, EDMA_IEM),
MVSATA_EDMA_READ_4(mvport, EDMA_S)));
cause &= MVSATA_EDMA_READ_4(mvport, EDMA_IEM);
if (!cause)
return 0;
if (cause & EDMA_IE_EDEVDIS) {
aprint_normal("%s:%d:%d: device disconnect\n",
device_xname(MVSATA_DEV2(mvport)),
mvport->port_hc->hc, mvport->port);
}
if (cause & EDMA_IE_EDEVCON) {
if (sc->sc_gen == gen1)
mvsata_devconn_gen1(mvport);
DPRINTFN(3, (" device connected\n"));
}
#ifndef MVSATA_WITHOUTDMA
if ((sc->sc_gen == gen1 && cause & EDMA_IE_ETRANSINT) ||
(sc->sc_gen != gen1 && cause & EDMA_IE_ESELFDIS)) {
switch (mvport->port_edmamode) {
case dma:
case queued:
case ncq:
mvsata_edma_reset_qptr(mvport);
mvsata_edma_enable(mvport);
if (cause & EDMA_IE_EDEVERR)
break;
/* FALLTHROUGH */
case nodma:
default:
aprint_error(
"%s:%d:%d: EDMA self disable happen 0x%x\n",
device_xname(MVSATA_DEV2(mvport)),
mvport->port_hc->hc, mvport->port, cause);
break;
}
}
#endif
if (cause & EDMA_IE_ETRANSINT) {
/* hot plug the Port Multiplier */
aprint_normal("%s:%d:%d: detect Port Multiplier?\n",
device_xname(MVSATA_DEV2(mvport)),
mvport->port_hc->hc, mvport->port);
}
return 1;
}
/*
* ATA callback entry points
*/
static void
mvsata_probe_drive(struct ata_channel *chp)
{
struct mvsata_port * const mvport = (struct mvsata_port *)chp;
uint32_t sstat, sig;
sstat = sata_reset_interface(chp, mvport->port_iot,
mvport->port_sata_scontrol, mvport->port_sata_sstatus, AT_WAIT);
switch (sstat) {
case SStatus_DET_DEV:
mvsata_pmp_select(mvport, PMP_PORT_CTL);
sig = mvsata_softreset(mvport, AT_WAIT);
sata_interpret_sig(chp, 0, sig);
break;
default:
break;
}
}
#ifndef MVSATA_WITHOUTDMA
static int
mvsata_bio(struct ata_drive_datas *drvp, struct ata_bio *ata_bio)
{
struct ata_channel *chp = drvp->chnl_softc;
struct atac_softc *atac = chp->ch_atac;
struct ata_xfer *xfer;
DPRINTFN(1, ("%s:%d: mvsata_bio: drive=%d, blkno=%" PRId64
", bcount=%ld\n", device_xname(atac->atac_dev), chp->ch_channel,
drvp->drive, ata_bio->blkno, ata_bio->bcount));
xfer = ata_get_xfer(ATAXF_NOSLEEP);
if (xfer == NULL)
return ATACMD_TRY_AGAIN;
if (atac->atac_cap & ATAC_CAP_NOIRQ)
ata_bio->flags |= ATA_POLL;
if (ata_bio->flags & ATA_POLL)
xfer->c_flags |= C_POLL;
if ((drvp->drive_flags & (ATA_DRIVE_DMA | ATA_DRIVE_UDMA)) &&
(ata_bio->flags & ATA_SINGLE) == 0)
xfer->c_flags |= C_DMA;
xfer->c_drive = drvp->drive;
xfer->c_cmd = ata_bio;
xfer->c_databuf = ata_bio->databuf;
xfer->c_bcount = ata_bio->bcount;
xfer->c_start = mvsata_bio_start;
xfer->c_intr = mvsata_bio_intr;
xfer->c_kill_xfer = mvsata_bio_kill_xfer;
ata_exec_xfer(chp, xfer);
return (ata_bio->flags & ATA_ITSDONE) ? ATACMD_COMPLETE : ATACMD_QUEUED;
}
static void
mvsata_reset_drive(struct ata_drive_datas *drvp, int flags, uint32_t *sigp)
{
struct ata_channel *chp = drvp->chnl_softc;
struct mvsata_port *mvport = (struct mvsata_port *)chp;
uint32_t edma_c;
uint32_t sig;
edma_c = MVSATA_EDMA_READ_4(mvport, EDMA_CMD);
DPRINTF(("%s:%d: mvsata_reset_drive: drive=%d (EDMA %sactive)\n",
device_xname(MVSATA_DEV2(mvport)), chp->ch_channel, drvp->drive,
(edma_c & EDMA_CMD_EENEDMA) ? "" : "not "));
if (edma_c & EDMA_CMD_EENEDMA)
mvsata_edma_disable(mvport, 10000, flags & AT_WAIT);
mvsata_pmp_select(mvport, drvp->drive);
sig = mvsata_softreset(mvport, flags & AT_WAIT);
if (sigp)
*sigp = sig;
if (edma_c & EDMA_CMD_EENEDMA) {
mvsata_edma_reset_qptr(mvport);
mvsata_edma_enable(mvport);
}
return;
}
static void
mvsata_reset_channel(struct ata_channel *chp, int flags)
{
struct mvsata_port *mvport = (struct mvsata_port *)chp;
struct mvsata_softc *sc = device_private(MVSATA_DEV2(mvport));
struct ata_xfer *xfer;
uint32_t sstat, ctrl;
int i;
DPRINTF(("%s: mvsata_reset_channel: channel=%d\n",
device_xname(MVSATA_DEV2(mvport)), chp->ch_channel));
mvsata_hreset_port(mvport);
sstat = sata_reset_interface(chp, mvport->port_iot,
mvport->port_sata_scontrol, mvport->port_sata_sstatus, flags);
if (flags & AT_WAIT && sstat == SStatus_DET_DEV_NE &&
sc->sc_gen != gen1) {
/* Downgrade to GenI */
const uint32_t val = SControl_IPM_NONE | SControl_SPD_ANY |
SControl_DET_DISABLE;
MVSATA_EDMA_WRITE_4(mvport, mvport->port_sata_scontrol, val);
ctrl = MVSATA_EDMA_READ_4(mvport, SATA_SATAICFG);
ctrl &= ~(1 << 17); /* Disable GenII */
MVSATA_EDMA_WRITE_4(mvport, SATA_SATAICFG, ctrl);
mvsata_hreset_port(mvport);
sata_reset_interface(chp, mvport->port_iot,
mvport->port_sata_scontrol, mvport->port_sata_sstatus,
flags);
}
for (i = 0; i < MVSATA_EDMAQ_LEN; i++) {
xfer = mvport->port_reqtbl[i].xfer;
if (xfer == NULL)
continue;
chp->ch_queue->active_xfer = xfer;
xfer->c_kill_xfer(chp, xfer, KILL_RESET);
}
mvsata_edma_config(mvport, mvport->port_edmamode);
mvsata_edma_reset_qptr(mvport);
mvsata_edma_enable(mvport);
return;
}
static int
mvsata_exec_command(struct ata_drive_datas *drvp, struct ata_command *ata_c)
{
struct ata_channel *chp = drvp->chnl_softc;
#ifdef MVSATA_DEBUG
struct mvsata_port *mvport = (struct mvsata_port *)chp;
#endif
struct ata_xfer *xfer;
int rv, s;
DPRINTFN(1, ("%s:%d: mvsata_exec_command: drive=%d, bcount=%d,"
" r_lba=0x%012"PRIx64", r_count=0x%04x, r_features=0x%04x,"
" r_device=0x%02x, r_command=0x%02x\n",
device_xname(MVSATA_DEV2(mvport)), chp->ch_channel,
drvp->drive, ata_c->bcount, ata_c->r_lba, ata_c->r_count,
ata_c->r_features, ata_c->r_device, ata_c->r_command));
xfer = ata_get_xfer(ata_c->flags & AT_WAIT ? ATAXF_CANSLEEP :
ATAXF_NOSLEEP);
if (xfer == NULL)
return ATACMD_TRY_AGAIN;
if (ata_c->flags & AT_POLL)
xfer->c_flags |= C_POLL;
if (ata_c->flags & AT_WAIT)
xfer->c_flags |= C_WAIT;
xfer->c_drive = drvp->drive;
xfer->c_databuf = ata_c->data;
xfer->c_bcount = ata_c->bcount;
xfer->c_cmd = ata_c;
xfer->c_start = mvsata_wdc_cmd_start;
xfer->c_intr = mvsata_wdc_cmd_intr;
xfer->c_kill_xfer = mvsata_wdc_cmd_kill_xfer;
s = splbio();
ata_exec_xfer(chp, xfer);
#ifdef DIAGNOSTIC
if ((ata_c->flags & AT_POLL) != 0 &&
(ata_c->flags & AT_DONE) == 0)
panic("mvsata_exec_command: polled command not done");
#endif
if (ata_c->flags & AT_DONE)
rv = ATACMD_COMPLETE;
else {
if (ata_c->flags & AT_WAIT) {
while ((ata_c->flags & AT_DONE) == 0)
tsleep(ata_c, PRIBIO, "mvsatacmd", 0);
rv = ATACMD_COMPLETE;
} else
rv = ATACMD_QUEUED;
}
splx(s);
return rv;
}
static int
mvsata_addref(struct ata_drive_datas *drvp)
{
return 0;
}
static void
mvsata_delref(struct ata_drive_datas *drvp)
{
return;
}
static void
mvsata_killpending(struct ata_drive_datas *drvp)
{
return;
}
#if NATAPIBUS > 0
static void
mvsata_atapibus_attach(struct atabus_softc *ata_sc)
{
struct ata_channel *chp = ata_sc->sc_chan;
struct atac_softc *atac = chp->ch_atac;
struct scsipi_adapter *adapt = &atac->atac_atapi_adapter._generic;
struct scsipi_channel *chan = &chp->ch_atapi_channel;
/*
* Fill in the scsipi_adapter.
*/
adapt->adapt_dev = atac->atac_dev;
adapt->adapt_nchannels = atac->atac_nchannels;
adapt->adapt_request = mvsata_atapi_scsipi_request;
adapt->adapt_minphys = mvsata_atapi_minphys;
atac->atac_atapi_adapter.atapi_probe_device = mvsata_atapi_probe_device;
/*
* Fill in the scsipi_channel.
*/
memset(chan, 0, sizeof(*chan));
chan->chan_adapter = adapt;
chan->chan_bustype = &mvsata_atapi_bustype;
chan->chan_channel = chp->ch_channel;
chan->chan_flags = SCSIPI_CHAN_OPENINGS;
chan->chan_openings = 1;
chan->chan_max_periph = 1;
chan->chan_ntargets = 1;
chan->chan_nluns = 1;
chp->atapibus =
config_found_ia(ata_sc->sc_dev, "atapi", chan, atapiprint);
}
static void
mvsata_atapi_scsipi_request(struct scsipi_channel *chan,
scsipi_adapter_req_t req, void *arg)
{
struct scsipi_adapter *adapt = chan->chan_adapter;
struct scsipi_periph *periph;
struct scsipi_xfer *sc_xfer;
struct mvsata_softc *sc = device_private(adapt->adapt_dev);
struct atac_softc *atac = &sc->sc_wdcdev.sc_atac;
struct ata_xfer *xfer;
int channel = chan->chan_channel;
int drive, s;
switch (req) {
case ADAPTER_REQ_RUN_XFER:
sc_xfer = arg;
periph = sc_xfer->xs_periph;
drive = periph->periph_target;
if (!device_is_active(atac->atac_dev)) {
sc_xfer->error = XS_DRIVER_STUFFUP;
scsipi_done(sc_xfer);
return;
}
xfer = ata_get_xfer(ATAXF_NOSLEEP);
if (xfer == NULL) {
sc_xfer->error = XS_RESOURCE_SHORTAGE;
scsipi_done(sc_xfer);
return;
}
if (sc_xfer->xs_control & XS_CTL_POLL)
xfer->c_flags |= C_POLL;
xfer->c_drive = drive;
xfer->c_flags |= C_ATAPI;
xfer->c_cmd = sc_xfer;
xfer->c_databuf = sc_xfer->data;
xfer->c_bcount = sc_xfer->datalen;
xfer->c_start = mvsata_atapi_start;
xfer->c_intr = mvsata_atapi_intr;
xfer->c_kill_xfer = mvsata_atapi_kill_xfer;
xfer->c_dscpoll = 0;
s = splbio();
ata_exec_xfer(atac->atac_channels[channel], xfer);
#ifdef DIAGNOSTIC
if ((sc_xfer->xs_control & XS_CTL_POLL) != 0 &&
(sc_xfer->xs_status & XS_STS_DONE) == 0)
panic("mvsata_atapi_scsipi_request:"
" polled command not done");
#endif
splx(s);
return;
default:
/* Not supported, nothing to do. */
;
}
}
static void
mvsata_atapi_minphys(struct buf *bp)
{
if (bp->b_bcount > MAXPHYS)
bp->b_bcount = MAXPHYS;
minphys(bp);
}
static void
mvsata_atapi_probe_device(struct atapibus_softc *sc, int target)
{
struct scsipi_channel *chan = sc->sc_channel;
struct scsipi_periph *periph;
struct ataparams ids;
struct ataparams *id = &ids;
struct mvsata_softc *mvc =
device_private(chan->chan_adapter->adapt_dev);
struct atac_softc *atac = &mvc->sc_wdcdev.sc_atac;
struct ata_channel *chp = atac->atac_channels[chan->chan_channel];
struct ata_drive_datas *drvp = &chp->ch_drive[target];
struct scsipibus_attach_args sa;
char serial_number[21], model[41], firmware_revision[9];
int s;
/* skip if already attached */
if (scsipi_lookup_periph(chan, target, 0) != NULL)
return;
/* if no ATAPI device detected at attach time, skip */
if (drvp->drive_type != ATA_DRIVET_ATAPI) {
DPRINTF(("%s:%d: mvsata_atapi_probe_device:"
" drive %d not present\n",
device_xname(atac->atac_dev), chp->ch_channel, target));
return;
}
/* Some ATAPI devices need a bit more time after software reset. */
delay(5000);
if (ata_get_params(drvp, AT_WAIT, id) == 0) {
#ifdef ATAPI_DEBUG_PROBE
log(LOG_DEBUG, "%s:%d: drive %d: cmdsz 0x%x drqtype 0x%x\n",
device_xname(atac->atac_dev), chp->ch_channel, target,
id->atap_config & ATAPI_CFG_CMD_MASK,
id->atap_config & ATAPI_CFG_DRQ_MASK);
#endif
periph = scsipi_alloc_periph(M_NOWAIT);
if (periph == NULL) {
aprint_error_dev(atac->atac_dev,
"unable to allocate periph"
" for channel %d drive %d\n",
chp->ch_channel, target);
return;
}
periph->periph_dev = NULL;
periph->periph_channel = chan;
periph->periph_switch = &atapi_probe_periphsw;
periph->periph_target = target;
periph->periph_lun = 0;
periph->periph_quirks = PQUIRK_ONLYBIG;
#ifdef SCSIPI_DEBUG
if (SCSIPI_DEBUG_TYPE == SCSIPI_BUSTYPE_ATAPI &&
SCSIPI_DEBUG_TARGET == target)
periph->periph_dbflags |= SCSIPI_DEBUG_FLAGS;
#endif
periph->periph_type = ATAPI_CFG_TYPE(id->atap_config);
if (id->atap_config & ATAPI_CFG_REMOV)
periph->periph_flags |= PERIPH_REMOVABLE;
if (periph->periph_type == T_SEQUENTIAL) {
s = splbio();
drvp->drive_flags |= ATA_DRIVE_ATAPIDSCW;
splx(s);
}
sa.sa_periph = periph;
sa.sa_inqbuf.type = ATAPI_CFG_TYPE(id->atap_config);
sa.sa_inqbuf.removable = id->atap_config & ATAPI_CFG_REMOV ?
T_REMOV : T_FIXED;
scsipi_strvis((u_char *)model, 40, id->atap_model, 40);
scsipi_strvis((u_char *)serial_number, 20, id->atap_serial, 20);
scsipi_strvis((u_char *)firmware_revision, 8, id->atap_revision,
8);
sa.sa_inqbuf.vendor = model;
sa.sa_inqbuf.product = serial_number;
sa.sa_inqbuf.revision = firmware_revision;
/*
* Determine the operating mode capabilities of the device.
*/
if ((id->atap_config & ATAPI_CFG_CMD_MASK) == ATAPI_CFG_CMD_16)
periph->periph_cap |= PERIPH_CAP_CMD16;
/* XXX This is gross. */
periph->periph_cap |= (id->atap_config & ATAPI_CFG_DRQ_MASK);
drvp->drv_softc = atapi_probe_device(sc, target, periph, &sa);
if (drvp->drv_softc)
ata_probe_caps(drvp);
else {
s = splbio();
drvp->drive_type = ATA_DRIVET_NONE;
splx(s);
}
} else {
DPRINTF(("%s:%d: mvsata_atapi_probe_device:"
" ATAPI_IDENTIFY_DEVICE failed for drive %d: error 0x%x\n",
device_xname(atac->atac_dev), chp->ch_channel, target,
chp->ch_error));
s = splbio();
drvp->drive_type = ATA_DRIVET_NONE;
splx(s);
}
}
/*
* Kill off all pending xfers for a periph.
*
* Must be called at splbio().
*/
static void
mvsata_atapi_kill_pending(struct scsipi_periph *periph)
{
struct atac_softc *atac =
device_private(periph->periph_channel->chan_adapter->adapt_dev);
struct ata_channel *chp =
atac->atac_channels[periph->periph_channel->chan_channel];
ata_kill_pending(&chp->ch_drive[periph->periph_target]);
}
#endif /* NATAPIBUS > 0 */
#endif /* MVSATA_WITHOUTDMA */
/*
* mvsata_setup_channel()
* Setup EDMA registers and prepare/purge DMA resources.
* We assuming already stopped the EDMA.
*/
static void
mvsata_setup_channel(struct ata_channel *chp)
{
#if !defined(MVSATA_WITHOUTDMA) || defined(MVSATA_DEBUG)
struct mvsata_port *mvport = (struct mvsata_port *)chp;
#endif
struct ata_drive_datas *drvp;
uint32_t edma_mode;
int drive, s;
#ifndef MVSATA_WITHOUTDMA
int i;
const int crqb_size = sizeof(union mvsata_crqb) * MVSATA_EDMAQ_LEN;
const int crpb_size = sizeof(struct crpb) * MVSATA_EDMAQ_LEN;
const int eprd_buf_size = MVSATA_EPRD_MAX_SIZE * MVSATA_EDMAQ_LEN;
#endif
DPRINTF(("%s:%d: mvsata_setup_channel: ",
device_xname(MVSATA_DEV2(mvport)), chp->ch_channel));
edma_mode = nodma;
for (drive = 0; drive < chp->ch_ndrives; drive++) {
drvp = &chp->ch_drive[drive];
/* If no drive, skip */
if (drvp->drive_type == ATA_DRIVET_NONE)
continue;
if (drvp->drive_flags & ATA_DRIVE_UDMA) {
/* use Ultra/DMA */
s = splbio();
drvp->drive_flags &= ~ATA_DRIVE_DMA;
splx(s);
}
if (drvp->drive_flags & (ATA_DRIVE_UDMA | ATA_DRIVE_DMA))
if (drvp->drive_type == ATA_DRIVET_ATA)
edma_mode = dma;
}
DPRINTF(("EDMA %sactive mode\n", (edma_mode == nodma) ? "not " : ""));
#ifndef MVSATA_WITHOUTDMA
if (edma_mode == nodma) {
no_edma:
if (mvport->port_crqb != NULL)
mvsata_edma_resource_purge(mvport, mvport->port_dmat,
mvport->port_crqb_dmamap, mvport->port_crqb);
if (mvport->port_crpb != NULL)
mvsata_edma_resource_purge(mvport, mvport->port_dmat,
mvport->port_crpb_dmamap, mvport->port_crpb);
if (mvport->port_eprd != NULL)
mvsata_edma_resource_purge(mvport, mvport->port_dmat,
mvport->port_eprd_dmamap, mvport->port_eprd);
return;
}
if (mvport->port_crqb == NULL)
mvport->port_crqb = mvsata_edma_resource_prepare(mvport,
mvport->port_dmat, &mvport->port_crqb_dmamap, crqb_size, 1);
if (mvport->port_crpb == NULL)
mvport->port_crpb = mvsata_edma_resource_prepare(mvport,
mvport->port_dmat, &mvport->port_crpb_dmamap, crpb_size, 0);
if (mvport->port_eprd == NULL) {
mvport->port_eprd = mvsata_edma_resource_prepare(mvport,
mvport->port_dmat, &mvport->port_eprd_dmamap, eprd_buf_size,
1);
for (i = 0; i < MVSATA_EDMAQ_LEN; i++) {
mvport->port_reqtbl[i].eprd_offset =
i * MVSATA_EPRD_MAX_SIZE;
mvport->port_reqtbl[i].eprd = mvport->port_eprd +
i * MVSATA_EPRD_MAX_SIZE / sizeof(struct eprd);
}
}
if (mvport->port_crqb == NULL || mvport->port_crpb == NULL ||
mvport->port_eprd == NULL) {
aprint_error_dev(MVSATA_DEV2(mvport),
"channel %d: can't use EDMA\n", chp->ch_channel);
s = splbio();
for (drive = 0; drive < chp->ch_ndrives; drive++) {
drvp = &chp->ch_drive[drive];
/* If no drive, skip */
if (drvp->drive_type == ATA_DRIVET_NONE)
continue;
drvp->drive_flags &= ~(ATA_DRIVE_UDMA | ATA_DRIVE_DMA);
}
splx(s);
goto no_edma;
}
mvsata_edma_config(mvport, edma_mode);
mvsata_edma_reset_qptr(mvport);
mvsata_edma_enable(mvport);
#endif
}
#ifndef MVSATA_WITHOUTDMA
static void
mvsata_bio_start(struct ata_channel *chp, struct ata_xfer *xfer)
{
struct mvsata_port *mvport = (struct mvsata_port *)chp;
struct mvsata_softc *sc = device_private(MVSATA_DEV2(mvport));
struct atac_softc *atac = chp->ch_atac;
struct wdc_softc *wdc = CHAN_TO_WDC(chp);
struct ata_bio *ata_bio = xfer->c_cmd;
struct ata_drive_datas *drvp = &chp->ch_drive[xfer->c_drive];
int wait_flags = (xfer->c_flags & C_POLL) ? AT_POLL : 0;
u_int16_t cyl;
u_int8_t head, sect, cmd = 0;
int nblks, error;
DPRINTFN(2, ("%s:%d: mvsata_bio_start: drive=%d\n",
device_xname(atac->atac_dev), chp->ch_channel, xfer->c_drive));
if (xfer->c_flags & C_DMA)
if (drvp->n_xfers <= NXFER)
drvp->n_xfers++;
again:
/*
*
* When starting a multi-sector transfer, or doing single-sector
* transfers...
*/
if (xfer->c_skip == 0 || (ata_bio->flags & ATA_SINGLE) != 0) {
if (ata_bio->flags & ATA_SINGLE)
nblks = 1;
else
nblks = xfer->c_bcount / ata_bio->lp->d_secsize;
/* Check for bad sectors and adjust transfer, if necessary. */
if ((ata_bio->lp->d_flags & D_BADSECT) != 0) {
long blkdiff;
int i;
for (i = 0; (blkdiff = ata_bio->badsect[i]) != -1;
i++) {
blkdiff -= ata_bio->blkno;
if (blkdiff < 0)
continue;
if (blkdiff == 0)
/* Replace current block of transfer. */
ata_bio->blkno =
ata_bio->lp->d_secperunit -
ata_bio->lp->d_nsectors - i - 1;
if (blkdiff < nblks) {
/* Bad block inside transfer. */
ata_bio->flags |= ATA_SINGLE;
nblks = 1;
}
break;
}
/* Transfer is okay now. */
}
if (xfer->c_flags & C_DMA) {
ata_bio->nblks = nblks;
ata_bio->nbytes = xfer->c_bcount;
if (xfer->c_flags & C_POLL)
sc->sc_enable_intr(mvport, 0 /*off*/);
error = mvsata_edma_enqueue(mvport, ata_bio,
(char *)xfer->c_databuf + xfer->c_skip);
if (error) {
if (error == EINVAL) {
/*
* We can't do DMA on this transfer
* for some reason. Fall back to
* PIO.
*/
xfer->c_flags &= ~C_DMA;
error = 0;
goto do_pio;
}
if (error == EBUSY) {
aprint_error_dev(atac->atac_dev,
"channel %d: EDMA Queue full\n",
chp->ch_channel);
/*
* XXXX: Perhaps, after it waits for
* a while, it is necessary to call
* bio_start again.
*/
}
ata_bio->error = ERR_DMA;
ata_bio->r_error = 0;
mvsata_bio_done(chp, xfer);
return;
}
chp->ch_flags |= ATACH_DMA_WAIT;
/* start timeout machinery */
if ((xfer->c_flags & C_POLL) == 0)
callout_reset(&chp->ch_callout,
ATA_DELAY / 1000 * hz,
mvsata_edma_timeout, xfer);
/* wait for irq */
goto intr;
} /* else not DMA */
do_pio:
if (ata_bio->flags & ATA_LBA48) {
sect = 0;
cyl = 0;
head = 0;
} else if (ata_bio->flags & ATA_LBA) {
sect = (ata_bio->blkno >> 0) & 0xff;
cyl = (ata_bio->blkno >> 8) & 0xffff;
head = (ata_bio->blkno >> 24) & 0x0f;
head |= WDSD_LBA;
} else {
int blkno = ata_bio->blkno;
sect = blkno % ata_bio->lp->d_nsectors;
sect++; /* Sectors begin with 1, not 0. */
blkno /= ata_bio->lp->d_nsectors;
head = blkno % ata_bio->lp->d_ntracks;
blkno /= ata_bio->lp->d_ntracks;
cyl = blkno;
head |= WDSD_CHS;
}
ata_bio->nblks = min(nblks, ata_bio->multi);
ata_bio->nbytes = ata_bio->nblks * ata_bio->lp->d_secsize;
KASSERT(nblks == 1 || (ata_bio->flags & ATA_SINGLE) == 0);
if (ata_bio->nblks > 1)
cmd = (ata_bio->flags & ATA_READ) ?
WDCC_READMULTI : WDCC_WRITEMULTI;
else
cmd = (ata_bio->flags & ATA_READ) ?
WDCC_READ : WDCC_WRITE;
/* EDMA disable, if enabled this channel. */
if (mvport->port_edmamode != nodma)
mvsata_edma_disable(mvport, 10 /* ms */, wait_flags);
mvsata_pmp_select(mvport, xfer->c_drive);
/* Do control operations specially. */
if (__predict_false(drvp->state < READY)) {
/*
* Actually, we want to be careful not to mess with
* the control state if the device is currently busy,
* but we can assume that we never get to this point
* if that's the case.
*/
/*
* If it's not a polled command, we need the kernel
* thread
*/
if ((xfer->c_flags & C_POLL) == 0 && cpu_intr_p()) {
chp->ch_queue->queue_freeze++;
wakeup(&chp->ch_thread);
return;
}
if (mvsata_bio_ready(mvport, ata_bio, xfer->c_drive,
(xfer->c_flags & C_POLL) ? AT_POLL : 0) != 0) {
mvsata_bio_done(chp, xfer);
return;
}
}
/* Initiate command! */
MVSATA_WDC_WRITE_1(mvport, SRB_H, WDSD_IBM);
switch(wdc_wait_for_ready(chp, ATA_DELAY, wait_flags)) {
case WDCWAIT_OK:
break;
case WDCWAIT_TOUT:
goto timeout;
case WDCWAIT_THR:
return;
}
if (ata_bio->flags & ATA_LBA48)
wdccommandext(chp, 0, atacmd_to48(cmd),
ata_bio->blkno, nblks, 0, WDSD_LBA);
else
wdccommand(chp, 0, cmd, cyl,
head, sect, nblks,
(ata_bio->lp->d_type == DKTYPE_ST506) ?
ata_bio->lp->d_precompcyl / 4 : 0);
/* start timeout machinery */
if ((xfer->c_flags & C_POLL) == 0)
callout_reset(&chp->ch_callout,
ATA_DELAY / 1000 * hz, wdctimeout, chp);
} else if (ata_bio->nblks > 1) {
/* The number of blocks in the last stretch may be smaller. */
nblks = xfer->c_bcount / ata_bio->lp->d_secsize;
if (ata_bio->nblks > nblks) {
ata_bio->nblks = nblks;
ata_bio->nbytes = xfer->c_bcount;
}
}
/* If this was a write and not using DMA, push the data. */
if ((ata_bio->flags & ATA_READ) == 0) {
/*
* we have to busy-wait here, we can't rely on running in
* thread context.
*/
if (wdc_wait_for_drq(chp, ATA_DELAY, AT_POLL) != 0) {
aprint_error_dev(atac->atac_dev,
"channel %d: drive %d timeout waiting for DRQ,"
" st=0x%02x, err=0x%02x\n",
chp->ch_channel, xfer->c_drive, chp->ch_status,
chp->ch_error);
ata_bio->error = TIMEOUT;
mvsata_bio_done(chp, xfer);
return;
}
if (chp->ch_status & WDCS_ERR) {
ata_bio->error = ERROR;
ata_bio->r_error = chp->ch_error;
mvsata_bio_done(chp, xfer);
return;
}
wdc->dataout_pio(chp, drvp->drive_flags,
(char *)xfer->c_databuf + xfer->c_skip, ata_bio->nbytes);
}
intr:
/* Wait for IRQ (either real or polled) */
if ((ata_bio->flags & ATA_POLL) == 0) {
chp->ch_flags |= ATACH_IRQ_WAIT;
} else {
/* Wait for at last 400ns for status bit to be valid */
delay(1);
if (chp->ch_flags & ATACH_DMA_WAIT) {
mvsata_edma_wait(mvport, xfer, ATA_DELAY);
sc->sc_enable_intr(mvport, 1 /*on*/);
chp->ch_flags &= ~ATACH_DMA_WAIT;
}
mvsata_bio_intr(chp, xfer, 0);
if ((ata_bio->flags & ATA_ITSDONE) == 0)
goto again;
}
return;
timeout:
aprint_error_dev(atac->atac_dev,
"channel %d: drive %d not ready, st=0x%02x, err=0x%02x\n",
chp->ch_channel, xfer->c_drive, chp->ch_status, chp->ch_error);
ata_bio->error = TIMEOUT;
mvsata_bio_done(chp, xfer);
return;
}
static int
mvsata_bio_intr(struct ata_channel *chp, struct ata_xfer *xfer, int irq)
{
struct atac_softc *atac = chp->ch_atac;
struct wdc_softc *wdc = CHAN_TO_WDC(chp);
struct ata_bio *ata_bio = xfer->c_cmd;
struct ata_drive_datas *drvp = &chp->ch_drive[xfer->c_drive];
DPRINTFN(2, ("%s:%d: mvsata_bio_intr: drive=%d\n",
device_xname(atac->atac_dev), chp->ch_channel, xfer->c_drive));
chp->ch_flags &= ~(ATACH_IRQ_WAIT|ATACH_DMA_WAIT);
/*
* If we missed an interrupt transfer, reset and restart.
* Don't try to continue transfer, we may have missed cycles.
*/
if (xfer->c_flags & C_TIMEOU) {
ata_bio->error = TIMEOUT;
mvsata_bio_done(chp, xfer);
return 1;
}
/* Is it not a transfer, but a control operation? */
if (!(xfer->c_flags & C_DMA) && drvp->state < READY) {
aprint_error_dev(atac->atac_dev,
"channel %d: drive %d bad state %d in mvsata_bio_intr\n",
chp->ch_channel, xfer->c_drive, drvp->state);
panic("mvsata_bio_intr: bad state");
}
/* Ack interrupt done by wdc_wait_for_unbusy */
if (!(xfer->c_flags & C_DMA) &&
(wdc_wait_for_unbusy(chp, (irq == 0) ? ATA_DELAY : 0, AT_POLL)
== WDCWAIT_TOUT)) {
if (irq && (xfer->c_flags & C_TIMEOU) == 0)
return 0; /* IRQ was not for us */
aprint_error_dev(atac->atac_dev,
"channel %d: drive %d timeout, c_bcount=%d, c_skip%d\n",
chp->ch_channel, xfer->c_drive, xfer->c_bcount,
xfer->c_skip);
ata_bio->error = TIMEOUT;
mvsata_bio_done(chp, xfer);
return 1;
}
if (xfer->c_flags & C_DMA) {
if (ata_bio->error == NOERROR)
goto end;
if (ata_bio->error == ERR_DMA)
ata_dmaerr(drvp,
(xfer->c_flags & C_POLL) ? AT_POLL : 0);
}
/* if we had an error, end */
if (ata_bio->error != NOERROR) {
mvsata_bio_done(chp, xfer);
return 1;
}
/* If this was a read and not using DMA, fetch the data. */
if ((ata_bio->flags & ATA_READ) != 0) {
if ((chp->ch_status & WDCS_DRQ) != WDCS_DRQ) {
aprint_error_dev(atac->atac_dev,
"channel %d: drive %d read intr before drq\n",
chp->ch_channel, xfer->c_drive);
ata_bio->error = TIMEOUT;
mvsata_bio_done(chp, xfer);
return 1;
}
wdc->datain_pio(chp, drvp->drive_flags,
(char *)xfer->c_databuf + xfer->c_skip, ata_bio->nbytes);
}
end:
ata_bio->blkno += ata_bio->nblks;
ata_bio->blkdone += ata_bio->nblks;
xfer->c_skip += ata_bio->nbytes;
xfer->c_bcount -= ata_bio->nbytes;
/* See if this transfer is complete. */
if (xfer->c_bcount > 0) {
if ((ata_bio->flags & ATA_POLL) == 0)
/* Start the next operation */
mvsata_bio_start(chp, xfer);
else
/* Let mvsata_bio_start do the loop */
return 1;
} else { /* Done with this transfer */
ata_bio->error = NOERROR;
mvsata_bio_done(chp, xfer);
}
return 1;
}
static void
mvsata_bio_kill_xfer(struct ata_channel *chp, struct ata_xfer *xfer, int reason)
{
struct mvsata_port *mvport = (struct mvsata_port *)chp;
struct atac_softc *atac = chp->ch_atac;
struct ata_bio *ata_bio = xfer->c_cmd;
int drive = xfer->c_drive;
DPRINTFN(2, ("%s:%d: mvsata_bio_kill_xfer: drive=%d\n",
device_xname(atac->atac_dev), chp->ch_channel, xfer->c_drive));
/* EDMA restart, if enabled */
if (!(xfer->c_flags & C_DMA) && mvport->port_edmamode != nodma) {
mvsata_edma_reset_qptr(mvport);
mvsata_edma_enable(mvport);
}
ata_free_xfer(chp, xfer);
ata_bio->flags |= ATA_ITSDONE;
switch (reason) {
case KILL_GONE:
ata_bio->error = ERR_NODEV;
break;
case KILL_RESET:
ata_bio->error = ERR_RESET;
break;
default:
aprint_error_dev(atac->atac_dev,
"mvsata_bio_kill_xfer: unknown reason %d\n", reason);
panic("mvsata_bio_kill_xfer");
}
ata_bio->r_error = WDCE_ABRT;
(*chp->ch_drive[drive].drv_done)(chp->ch_drive[drive].drv_softc);
}
static void
mvsata_bio_done(struct ata_channel *chp, struct ata_xfer *xfer)
{
struct mvsata_port *mvport = (struct mvsata_port *)chp;
struct ata_bio *ata_bio = xfer->c_cmd;
int drive = xfer->c_drive;
DPRINTFN(2, ("%s:%d: mvsata_bio_done: drive=%d, flags=0x%x\n",
device_xname(MVSATA_DEV2(mvport)), chp->ch_channel, xfer->c_drive,
(u_int)xfer->c_flags));
callout_stop(&chp->ch_callout);
/* EDMA restart, if enabled */
if (!(xfer->c_flags & C_DMA) && mvport->port_edmamode != nodma) {
mvsata_edma_reset_qptr(mvport);
mvsata_edma_enable(mvport);
}
/* feed back residual bcount to our caller */
ata_bio->bcount = xfer->c_bcount;
/* mark controller inactive and free xfer */
KASSERT(chp->ch_queue->active_xfer != NULL);
chp->ch_queue->active_xfer = NULL;
ata_free_xfer(chp, xfer);
if (chp->ch_drive[drive].drive_flags & ATA_DRIVE_WAITDRAIN) {
ata_bio->error = ERR_NODEV;
chp->ch_drive[drive].drive_flags &= ~ATA_DRIVE_WAITDRAIN;
wakeup(&chp->ch_queue->active_xfer);
}
ata_bio->flags |= ATA_ITSDONE;
(*chp->ch_drive[drive].drv_done)(chp->ch_drive[drive].drv_softc);
atastart(chp);
}
static int
mvsata_bio_ready(struct mvsata_port *mvport, struct ata_bio *ata_bio, int drive,
int flags)
{
struct ata_channel *chp = &mvport->port_ata_channel;
struct atac_softc *atac = chp->ch_atac;
struct ata_drive_datas *drvp = &chp->ch_drive[drive];
const char *errstring;
flags |= AT_POLL; /* XXX */
/*
* disable interrupts, all commands here should be quick
* enough to be able to poll, and we don't go here that often
*/
MVSATA_WDC_WRITE_1(mvport, SRB_CAS, WDCTL_4BIT | WDCTL_IDS);
MVSATA_WDC_WRITE_1(mvport, SRB_H, WDSD_IBM);
DELAY(10);
errstring = "wait";
if (wdcwait(chp, WDCS_DRDY, WDCS_DRDY, ATA_DELAY, flags))
goto ctrltimeout;
wdccommandshort(chp, 0, WDCC_RECAL);
/* Wait for at least 400ns for status bit to be valid */
DELAY(1);
errstring = "recal";
if (wdcwait(chp, WDCS_DRDY, WDCS_DRDY, ATA_DELAY, flags))
goto ctrltimeout;
if (chp->ch_status & (WDCS_ERR | WDCS_DWF))
goto ctrlerror;
/* Don't try to set modes if controller can't be adjusted */
if (atac->atac_set_modes == NULL)
goto geometry;
/* Also don't try if the drive didn't report its mode */
if ((drvp->drive_flags & ATA_DRIVE_MODE) == 0)
goto geometry;
wdccommand(chp, 0, SET_FEATURES, 0, 0, 0,
0x08 | drvp->PIO_mode, WDSF_SET_MODE);
errstring = "piomode";
if (wdcwait(chp, WDCS_DRDY, WDCS_DRDY, ATA_DELAY, flags))
goto ctrltimeout;
if (chp->ch_status & (WDCS_ERR | WDCS_DWF))
goto ctrlerror;
if (drvp->drive_flags & ATA_DRIVE_UDMA)
wdccommand(chp, 0, SET_FEATURES, 0, 0, 0,
0x40 | drvp->UDMA_mode, WDSF_SET_MODE);
else if (drvp->drive_flags & ATA_DRIVE_DMA)
wdccommand(chp, 0, SET_FEATURES, 0, 0, 0,
0x20 | drvp->DMA_mode, WDSF_SET_MODE);
else
goto geometry;
errstring = "dmamode";
if (wdcwait(chp, WDCS_DRDY, WDCS_DRDY, ATA_DELAY, flags))
goto ctrltimeout;
if (chp->ch_status & (WDCS_ERR | WDCS_DWF))
goto ctrlerror;
geometry:
if (ata_bio->flags & ATA_LBA)
goto multimode;
wdccommand(chp, 0, WDCC_IDP, ata_bio->lp->d_ncylinders,
ata_bio->lp->d_ntracks - 1, 0, ata_bio->lp->d_nsectors,
(ata_bio->lp->d_type == DKTYPE_ST506) ?
ata_bio->lp->d_precompcyl / 4 : 0);
errstring = "geometry";
if (wdcwait(chp, WDCS_DRDY, WDCS_DRDY, ATA_DELAY, flags))
goto ctrltimeout;
if (chp->ch_status & (WDCS_ERR | WDCS_DWF))
goto ctrlerror;
multimode:
if (ata_bio->multi == 1)
goto ready;
wdccommand(chp, 0, WDCC_SETMULTI, 0, 0, 0, ata_bio->multi, 0);
errstring = "setmulti";
if (wdcwait(chp, WDCS_DRDY, WDCS_DRDY, ATA_DELAY, flags))
goto ctrltimeout;
if (chp->ch_status & (WDCS_ERR | WDCS_DWF))
goto ctrlerror;
ready:
drvp->state = READY;
/*
* The drive is usable now
*/
MVSATA_WDC_WRITE_1(mvport, SRB_CAS, WDCTL_4BIT);
delay(10); /* some drives need a little delay here */
return 0;
ctrltimeout:
aprint_error_dev(atac->atac_dev, "channel %d: drive %d %s timed out\n",
chp->ch_channel, drive, errstring);
ata_bio->error = TIMEOUT;
goto ctrldone;
ctrlerror:
aprint_error_dev(atac->atac_dev, "channel %d: drive %d %s ",
chp->ch_channel, drive, errstring);
if (chp->ch_status & WDCS_DWF) {
aprint_error("drive fault\n");
ata_bio->error = ERR_DF;
} else {
aprint_error("error (%x)\n", chp->ch_error);
ata_bio->r_error = chp->ch_error;
ata_bio->error = ERROR;
}
ctrldone:
drvp->state = 0;
MVSATA_WDC_WRITE_1(mvport, SRB_CAS, WDCTL_4BIT);
return -1;
}
static void
mvsata_wdc_cmd_start(struct ata_channel *chp, struct ata_xfer *xfer)
{
struct mvsata_port *mvport = (struct mvsata_port *)chp;
int drive = xfer->c_drive;
int wait_flags = (xfer->c_flags & C_POLL) ? AT_POLL : 0;
struct ata_command *ata_c = xfer->c_cmd;
DPRINTFN(1, ("%s:%d: mvsata_cmd_start: drive=%d\n",
device_xname(MVSATA_DEV2(mvport)), chp->ch_channel, drive));
/* First, EDMA disable, if enabled this channel. */
if (mvport->port_edmamode != nodma)
mvsata_edma_disable(mvport, 10 /* ms */, wait_flags);
mvsata_pmp_select(mvport, drive);
MVSATA_WDC_WRITE_1(mvport, SRB_H, WDSD_IBM);
switch(wdcwait(chp, ata_c->r_st_bmask | WDCS_DRQ,
ata_c->r_st_bmask, ata_c->timeout, wait_flags)) {
case WDCWAIT_OK:
break;
case WDCWAIT_TOUT:
ata_c->flags |= AT_TIMEOU;
mvsata_wdc_cmd_done(chp, xfer);
return;
case WDCWAIT_THR:
return;
}
if (ata_c->flags & AT_POLL)
/* polled command, disable interrupts */
MVSATA_WDC_WRITE_1(mvport, SRB_CAS, WDCTL_4BIT | WDCTL_IDS);
if ((ata_c->flags & AT_LBA48) != 0) {
wdccommandext(chp, 0, ata_c->r_command,
ata_c->r_lba, ata_c->r_count, ata_c->r_features,
ata_c->r_device & ~0x10);
} else {
wdccommand(chp, 0, ata_c->r_command,
(ata_c->r_lba >> 8) & 0xffff,
(((ata_c->flags & AT_LBA) != 0) ? WDSD_LBA : 0) |
((ata_c->r_lba >> 24) & 0x0f),
ata_c->r_lba & 0xff,
ata_c->r_count & 0xff,
ata_c->r_features & 0xff);
}
if ((ata_c->flags & AT_POLL) == 0) {
chp->ch_flags |= ATACH_IRQ_WAIT; /* wait for interrupt */
callout_reset(&chp->ch_callout, ata_c->timeout / 1000 * hz,
wdctimeout, chp);
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); /* 400ns delay */
mvsata_wdc_cmd_intr(chp, xfer, 0);
}
static int
mvsata_wdc_cmd_intr(struct ata_channel *chp, struct ata_xfer *xfer, int irq)
{
struct mvsata_port *mvport = (struct mvsata_port *)chp;
struct wdc_softc *wdc = CHAN_TO_WDC(chp);
struct ata_command *ata_c = xfer->c_cmd;
int bcount = ata_c->bcount;
char *data = ata_c->data;
int wflags;
int drive_flags;
if (ata_c->r_command == WDCC_IDENTIFY ||
ata_c->r_command == ATAPI_IDENTIFY_DEVICE)
/*
* The IDENTIFY data has been designed as an array of
* u_int16_t, so we can byteswap it on the fly.
* Historically it's what we have always done so keeping it
* here ensure binary backward compatibility.
*/
drive_flags = ATA_DRIVE_NOSTREAM |
chp->ch_drive[xfer->c_drive].drive_flags;
else
/*
* Other data structure are opaque and should be transfered
* as is.
*/
drive_flags = chp->ch_drive[xfer->c_drive].drive_flags;
if ((ata_c->flags & (AT_WAIT | AT_POLL)) == (AT_WAIT | AT_POLL))
/* both wait and poll, we can tsleep here */
wflags = AT_WAIT | AT_POLL;
else
wflags = AT_POLL;
again:
DPRINTFN(1, ("%s:%d: mvsata_cmd_intr: drive=%d\n",
device_xname(MVSATA_DEV2(mvport)), chp->ch_channel, xfer->c_drive));
/*
* after a ATAPI_SOFT_RESET, the device will have released the bus.
* Reselect again, it doesn't hurt for others commands, and the time
* penalty for the extra register write is acceptable,
* wdc_exec_command() isn't called often (mostly for autoconfig)
*/
if ((xfer->c_flags & C_ATAPI) != 0) {
MVSATA_WDC_WRITE_1(mvport, SRB_H, WDSD_IBM);
}
if ((ata_c->flags & AT_XFDONE) != 0) {
/*
* We have completed a data xfer. The drive should now be
* in its initial state
*/
if (wdcwait(chp, ata_c->r_st_bmask | WDCS_DRQ,
ata_c->r_st_bmask, (irq == 0) ? ata_c->timeout : 0,
wflags) == WDCWAIT_TOUT) {
if (irq && (xfer->c_flags & C_TIMEOU) == 0)
return 0; /* IRQ was not for us */
ata_c->flags |= AT_TIMEOU;
}
goto out;
}
if (wdcwait(chp, ata_c->r_st_pmask, ata_c->r_st_pmask,
(irq == 0) ? ata_c->timeout : 0, wflags) == WDCWAIT_TOUT) {
if (irq && (xfer->c_flags & C_TIMEOU) == 0)
return 0; /* IRQ was not for us */
ata_c->flags |= AT_TIMEOU;
goto out;
}
delay(20); /* XXXXX: Delay more times. */
if (ata_c->flags & AT_READ) {
if ((chp->ch_status & WDCS_DRQ) == 0) {
ata_c->flags |= AT_TIMEOU;
goto out;
}
wdc->datain_pio(chp, drive_flags, data, bcount);
/* at this point the drive should be in its initial state */
ata_c->flags |= AT_XFDONE;
/*
* XXX checking the status register again here cause some
* hardware to timeout.
*/
} else if (ata_c->flags & AT_WRITE) {
if ((chp->ch_status & WDCS_DRQ) == 0) {
ata_c->flags |= AT_TIMEOU;
goto out;
}
wdc->dataout_pio(chp, drive_flags, data, bcount);
ata_c->flags |= AT_XFDONE;
if ((ata_c->flags & AT_POLL) == 0) {
chp->ch_flags |= ATACH_IRQ_WAIT; /* wait for intr */
callout_reset(&chp->ch_callout,
mstohz(ata_c->timeout), wdctimeout, chp);
return 1;
} else
goto again;
}
out:
mvsata_wdc_cmd_done(chp, xfer);
return 1;
}
static void
mvsata_wdc_cmd_kill_xfer(struct ata_channel *chp, struct ata_xfer *xfer,
int reason)
{
struct mvsata_port *mvport = (struct mvsata_port *)chp;
struct ata_command *ata_c = xfer->c_cmd;
DPRINTFN(1, ("%s:%d: mvsata_cmd_kill_xfer: drive=%d\n",
device_xname(MVSATA_DEV2(mvport)), chp->ch_channel, xfer->c_drive));
switch (reason) {
case KILL_GONE:
ata_c->flags |= AT_GONE;
break;
case KILL_RESET:
ata_c->flags |= AT_RESET;
break;
default:
aprint_error_dev(MVSATA_DEV2(mvport),
"mvsata_cmd_kill_xfer: unknown reason %d\n", reason);
panic("mvsata_cmd_kill_xfer");
}
mvsata_wdc_cmd_done_end(chp, xfer);
}
static void
mvsata_wdc_cmd_done(struct ata_channel *chp, struct ata_xfer *xfer)
{
struct mvsata_port *mvport = (struct mvsata_port *)chp;
struct atac_softc *atac = chp->ch_atac;
struct ata_command *ata_c = xfer->c_cmd;
DPRINTFN(1, ("%s:%d: mvsata_cmd_done: drive=%d, flags=0x%x\n",
device_xname(atac->atac_dev), chp->ch_channel, xfer->c_drive,
ata_c->flags));
if (chp->ch_status & WDCS_DWF)
ata_c->flags |= AT_DF;
if (chp->ch_status & WDCS_ERR) {
ata_c->flags |= AT_ERROR;
ata_c->r_error = chp->ch_error;
}
if ((ata_c->flags & AT_READREG) != 0 &&
device_is_active(atac->atac_dev) &&
(ata_c->flags & (AT_ERROR | AT_DF)) == 0) {
ata_c->r_status = MVSATA_WDC_READ_1(mvport, SRB_CS);
ata_c->r_error = MVSATA_WDC_READ_1(mvport, SRB_FE);
ata_c->r_count = MVSATA_WDC_READ_1(mvport, SRB_SC);
ata_c->r_lba =
(uint64_t)MVSATA_WDC_READ_1(mvport, SRB_LBAL) << 0;
ata_c->r_lba |=
(uint64_t)MVSATA_WDC_READ_1(mvport, SRB_LBAM) << 8;
ata_c->r_lba |=
(uint64_t)MVSATA_WDC_READ_1(mvport, SRB_LBAH) << 16;
ata_c->r_device = MVSATA_WDC_READ_1(mvport, SRB_H);
if ((ata_c->flags & AT_LBA48) != 0) {
if ((ata_c->flags & AT_POLL) != 0) {
MVSATA_WDC_WRITE_1(mvport, SRB_CAS,
WDCTL_HOB|WDCTL_4BIT|WDCTL_IDS);
} else {
MVSATA_WDC_WRITE_1(mvport, SRB_CAS,
WDCTL_HOB|WDCTL_4BIT);
}
ata_c->r_count |=
MVSATA_WDC_READ_1(mvport, SRB_SC) << 8;
ata_c->r_lba |=
(uint64_t)MVSATA_WDC_READ_1(mvport, SRB_LBAL) << 24;
ata_c->r_lba |=
(uint64_t)MVSATA_WDC_READ_1(mvport, SRB_LBAM) << 32;
ata_c->r_lba |=
(uint64_t)MVSATA_WDC_READ_1(mvport, SRB_LBAH) << 40;
if ((ata_c->flags & AT_POLL) != 0) {
MVSATA_WDC_WRITE_1(mvport, SRB_CAS,
WDCTL_4BIT|WDCTL_IDS);
} else {
MVSATA_WDC_WRITE_1(mvport, SRB_CAS,
WDCTL_4BIT);
}
} else {
ata_c->r_lba |=
(uint64_t)(ata_c->r_device & 0x0f) << 24;
}
}
callout_stop(&chp->ch_callout);
chp->ch_queue->active_xfer = NULL;
if (ata_c->flags & AT_POLL) {
/* enable interrupts */
MVSATA_WDC_WRITE_1(mvport, SRB_CAS, WDCTL_4BIT);
delay(10); /* some drives need a little delay here */
}
if (chp->ch_drive[xfer->c_drive].drive_flags & ATA_DRIVE_WAITDRAIN) {
mvsata_wdc_cmd_kill_xfer(chp, xfer, KILL_GONE);
chp->ch_drive[xfer->c_drive].drive_flags &= ~ATA_DRIVE_WAITDRAIN;
wakeup(&chp->ch_queue->active_xfer);
} else
mvsata_wdc_cmd_done_end(chp, xfer);
}
static void
mvsata_wdc_cmd_done_end(struct ata_channel *chp, struct ata_xfer *xfer)
{
struct mvsata_port *mvport = (struct mvsata_port *)chp;
struct ata_command *ata_c = xfer->c_cmd;
/* EDMA restart, if enabled */
if (mvport->port_edmamode != nodma) {
mvsata_edma_reset_qptr(mvport);
mvsata_edma_enable(mvport);
}
ata_c->flags |= AT_DONE;
ata_free_xfer(chp, xfer);
if (ata_c->flags & AT_WAIT)
wakeup(ata_c);
else if (ata_c->callback)
ata_c->callback(ata_c->callback_arg);
atastart(chp);
return;
}
#if NATAPIBUS > 0
static void
mvsata_atapi_start(struct ata_channel *chp, struct ata_xfer *xfer)
{
struct mvsata_softc *sc = (struct mvsata_softc *)chp->ch_atac;
struct mvsata_port *mvport = (struct mvsata_port *)chp;
struct atac_softc *atac = &sc->sc_wdcdev.sc_atac;
struct scsipi_xfer *sc_xfer = xfer->c_cmd;
struct ata_drive_datas *drvp = &chp->ch_drive[xfer->c_drive];
const int wait_flags = (xfer->c_flags & C_POLL) ? AT_POLL : 0;
const char *errstring;
DPRINTFN(2, ("%s:%d:%d: mvsata_atapi_start: scsi flags 0x%x\n",
device_xname(chp->ch_atac->atac_dev), chp->ch_channel,
xfer->c_drive, sc_xfer->xs_control));
if (mvport->port_edmamode != nodma)
mvsata_edma_disable(mvport, 10 /* ms */, wait_flags);
mvsata_pmp_select(mvport, xfer->c_drive);
if ((xfer->c_flags & C_DMA) && (drvp->n_xfers <= NXFER))
drvp->n_xfers++;
/* Do control operations specially. */
if (__predict_false(drvp->state < READY)) {
/* If it's not a polled command, we need the kernel thread */
if ((sc_xfer->xs_control & XS_CTL_POLL) == 0 && cpu_intr_p()) {
chp->ch_queue->queue_freeze++;
wakeup(&chp->ch_thread);
return;
}
/*
* disable interrupts, all commands here should be quick
* enough to be able to poll, and we don't go here that often
*/
MVSATA_WDC_WRITE_1(mvport, SRB_CAS, WDCTL_4BIT | WDCTL_IDS);
MVSATA_WDC_WRITE_1(mvport, SRB_H, WDSD_IBM);
/* 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 & ATA_DRIVE_MODE) == 0)
goto ready;
errstring = "unbusy";
if (wdc_wait_for_unbusy(chp, ATAPI_DELAY, wait_flags))
goto timeout;
wdccommand(chp, 0, 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.
*/
aprint_error_dev(atac->atac_dev,
"channel %d drive %d: PIO mode %d rejected,"
" falling back to PIO mode 3\n",
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 & ATA_DRIVE_UDMA)
wdccommand(chp, 0, SET_FEATURES, 0, 0, 0,
0x40 | drvp->UDMA_mode, WDSF_SET_MODE);
else
if (drvp->drive_flags & ATA_DRIVE_DMA)
wdccommand(chp, 0, 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 & ATA_DRIVE_UDMA)
goto error;
else {
/*
* The drive rejected our DMA setting.
* Fall back to mode 1.
*/
aprint_error_dev(atac->atac_dev,
"channel %d drive %d:"
" DMA mode %d rejected,"
" falling back to DMA mode 0\n",
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;
MVSATA_WDC_WRITE_1(mvport, SRB_CAS, WDCTL_4BIT);
delay(10); /* some drives need a little delay here */
}
/* start timeout machinery */
if ((sc_xfer->xs_control & XS_CTL_POLL) == 0)
callout_reset(&chp->ch_callout, mstohz(sc_xfer->timeout),
wdctimeout, chp);
MVSATA_WDC_WRITE_1(mvport, SRB_H, WDSD_IBM);
switch (wdc_wait_for_unbusy(chp, ATAPI_DELAY, wait_flags) < 0) {
case WDCWAIT_OK:
break;
case WDCWAIT_TOUT:
aprint_error_dev(atac->atac_dev, "not ready, st = %02x\n",
chp->ch_status);
sc_xfer->error = XS_TIMEOUT;
mvsata_atapi_reset(chp, xfer);
return;
case WDCWAIT_THR:
return;
}
/*
* Even with WDCS_ERR, the device should accept a command packet
* Limit length to what can be stuffed into the cylinder register
* (16 bits). Some CD-ROMs seem to interpret '0' as 65536,
* but not all devices do that and it's not obvious from the
* ATAPI spec that that behaviour should be expected. If more
* data is necessary, multiple data transfer phases will be done.
*/
wdccommand(chp, 0, ATAPI_PKT_CMD,
xfer->c_bcount <= 0xffff ? xfer->c_bcount : 0xffff, 0, 0, 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
* the interrupt routine. If it is a polled command, call the interrupt
* routine until command is done.
*/
if ((sc_xfer->xs_periph->periph_cap & ATAPI_CFG_DRQ_MASK) !=
ATAPI_CFG_IRQ_DRQ || (sc_xfer->xs_control & XS_CTL_POLL)) {
/* Wait for at last 400ns for status bit to be valid */
DELAY(1);
mvsata_atapi_intr(chp, xfer, 0);
} else
chp->ch_flags |= ATACH_IRQ_WAIT;
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) {
/* Wait for at last 400ns for status bit to be valid */
DELAY(1);
mvsata_atapi_intr(chp, xfer, 0);
}
}
return;
timeout:
aprint_error_dev(atac->atac_dev, "channel %d drive %d: %s timed out\n",
chp->ch_channel, xfer->c_drive, errstring);
sc_xfer->error = XS_TIMEOUT;
MVSATA_WDC_WRITE_1(mvport, SRB_CAS, WDCTL_4BIT);
delay(10); /* some drives need a little delay here */
mvsata_atapi_reset(chp, xfer);
return;
error:
aprint_error_dev(atac->atac_dev,
"channel %d drive %d: %s error (0x%x)\n",
chp->ch_channel, xfer->c_drive, errstring, chp->ch_error);
sc_xfer->error = XS_SHORTSENSE;
sc_xfer->sense.atapi_sense = chp->ch_error;
MVSATA_WDC_WRITE_1(mvport, SRB_CAS, WDCTL_4BIT);
delay(10); /* some drives need a little delay here */
mvsata_atapi_reset(chp, xfer);
return;
}
static int
mvsata_atapi_intr(struct ata_channel *chp, struct ata_xfer *xfer, int irq)
{
struct mvsata_port *mvport = (struct mvsata_port *)chp;
struct atac_softc *atac = chp->ch_atac;
struct wdc_softc *wdc = CHAN_TO_WDC(chp);
struct scsipi_xfer *sc_xfer = xfer->c_cmd;
struct ata_drive_datas *drvp = &chp->ch_drive[xfer->c_drive];
int len, phase, ire, error, retries=0, i;
void *cmd;
DPRINTFN(1, ("%s:%d:%d: mvsata_atapi_intr\n",
device_xname(atac->atac_dev), chp->ch_channel, xfer->c_drive));
/* Is it not a transfer, but a control operation? */
if (drvp->state < READY) {
aprint_error_dev(atac->atac_dev,
"channel %d drive %d: bad state %d\n",
chp->ch_channel, xfer->c_drive, drvp->state);
panic("mvsata_atapi_intr: bad state");
}
/*
* If we missed an interrupt in a PIO transfer, reset and restart.
* Don't try to continue transfer, we may have missed cycles.
*/
if ((xfer->c_flags & (C_TIMEOU | C_DMA)) == C_TIMEOU) {
sc_xfer->error = XS_TIMEOUT;
mvsata_atapi_reset(chp, xfer);
return 1;
}
/* Ack interrupt done in wdc_wait_for_unbusy */
MVSATA_WDC_WRITE_1(mvport, SRB_H, WDSD_IBM);
if (wdc_wait_for_unbusy(chp,
(irq == 0) ? sc_xfer->timeout : 0, AT_POLL) == WDCWAIT_TOUT) {
if (irq && (xfer->c_flags & C_TIMEOU) == 0)
return 0; /* IRQ was not for us */
aprint_error_dev(atac->atac_dev,
"channel %d: device timeout, c_bcount=%d, c_skip=%d\n",
chp->ch_channel, xfer->c_bcount, xfer->c_skip);
if (xfer->c_flags & C_DMA)
ata_dmaerr(drvp,
(xfer->c_flags & C_POLL) ? AT_POLL : 0);
sc_xfer->error = XS_TIMEOUT;
mvsata_atapi_reset(chp, xfer);
return 1;
}
/*
* If we missed an IRQ and were using DMA, flag it as a DMA error
* and reset device.
*/
if ((xfer->c_flags & C_TIMEOU) && (xfer->c_flags & C_DMA)) {
ata_dmaerr(drvp, (xfer->c_flags & C_POLL) ? AT_POLL : 0);
sc_xfer->error = XS_RESET;
mvsata_atapi_reset(chp, xfer);
return (1);
}
/*
* if the request sense command was aborted, report the short sense
* previously recorded, else continue normal processing
*/
again:
len = MVSATA_WDC_READ_1(mvport, SRB_LBAM) +
256 * MVSATA_WDC_READ_1(mvport, SRB_LBAH);
ire = MVSATA_WDC_READ_1(mvport, SRB_SC);
phase = (ire & (WDCI_CMD | WDCI_IN)) | (chp->ch_status & WDCS_DRQ);
DPRINTF((
"mvsata_atapi_intr: c_bcount %d len %d st 0x%x err 0x%x ire 0x%x :",
xfer->c_bcount, len, chp->ch_status, chp->ch_error, ire));
switch (phase) {
case PHASE_CMDOUT:
cmd = sc_xfer->cmd;
DPRINTF(("PHASE_CMDOUT\n"));
/* Init the DMA channel if necessary */
if (xfer->c_flags & C_DMA) {
error = mvsata_bdma_init(mvport, sc_xfer,
(char *)xfer->c_databuf + xfer->c_skip);
if (error) {
if (error == EINVAL) {
/*
* We can't do DMA on this transfer
* for some reason. Fall back to PIO.
*/
xfer->c_flags &= ~C_DMA;
error = 0;
} else {
sc_xfer->error = XS_DRIVER_STUFFUP;
break;
}
}
}
/* send packet command */
/* Commands are 12 or 16 bytes long. It's 32-bit aligned */
wdc->dataout_pio(chp, drvp->drive_flags, cmd, sc_xfer->cmdlen);
/* Start the DMA channel if necessary */
if (xfer->c_flags & C_DMA) {
mvsata_bdma_start(mvport);
chp->ch_flags |= ATACH_DMA_WAIT;
}
if ((sc_xfer->xs_control & XS_CTL_POLL) == 0)
chp->ch_flags |= ATACH_IRQ_WAIT;
return 1;
case PHASE_DATAOUT:
/* write data */
DPRINTF(("PHASE_DATAOUT\n"));
if ((sc_xfer->xs_control & XS_CTL_DATA_OUT) == 0 ||
(xfer->c_flags & C_DMA) != 0) {
aprint_error_dev(atac->atac_dev,
"channel %d drive %d: bad data phase DATAOUT\n",
chp->ch_channel, xfer->c_drive);
if (xfer->c_flags & C_DMA)
ata_dmaerr(drvp,
(xfer->c_flags & C_POLL) ? AT_POLL : 0);
sc_xfer->error = XS_TIMEOUT;
mvsata_atapi_reset(chp, xfer);
return 1;
}
xfer->c_lenoff = len - xfer->c_bcount;
if (xfer->c_bcount < len) {
aprint_error_dev(atac->atac_dev, "channel %d drive %d:"
" warning: write only %d of %d requested bytes\n",
chp->ch_channel, xfer->c_drive, xfer->c_bcount,
len);
len = xfer->c_bcount;
}
wdc->dataout_pio(chp, drvp->drive_flags,
(char *)xfer->c_databuf + xfer->c_skip, len);
for (i = xfer->c_lenoff; i > 0; i -= 2)
MVSATA_WDC_WRITE_2(mvport, SRB_PIOD, 0);
xfer->c_skip += len;
xfer->c_bcount -= len;
if ((sc_xfer->xs_control & XS_CTL_POLL) == 0)
chp->ch_flags |= ATACH_IRQ_WAIT;
return 1;
case PHASE_DATAIN:
/* Read data */
DPRINTF(("PHASE_DATAIN\n"));
if ((sc_xfer->xs_control & XS_CTL_DATA_IN) == 0 ||
(xfer->c_flags & C_DMA) != 0) {
aprint_error_dev(atac->atac_dev,
"channel %d drive %d: bad data phase DATAIN\n",
chp->ch_channel, xfer->c_drive);
if (xfer->c_flags & C_DMA)
ata_dmaerr(drvp,
(xfer->c_flags & C_POLL) ? AT_POLL : 0);
sc_xfer->error = XS_TIMEOUT;
mvsata_atapi_reset(chp, xfer);
return 1;
}
xfer->c_lenoff = len - xfer->c_bcount;
if (xfer->c_bcount < len) {
aprint_error_dev(atac->atac_dev, "channel %d drive %d:"
" warning: reading only %d of %d bytes\n",
chp->ch_channel, xfer->c_drive, xfer->c_bcount,
len);
len = xfer->c_bcount;
}
wdc->datain_pio(chp, drvp->drive_flags,
(char *)xfer->c_databuf + xfer->c_skip, len);
if (xfer->c_lenoff > 0)
wdcbit_bucket(chp, len - xfer->c_bcount);
xfer->c_skip += len;
xfer->c_bcount -= len;
if ((sc_xfer->xs_control & XS_CTL_POLL) == 0)
chp->ch_flags |= ATACH_IRQ_WAIT;
return 1;
case PHASE_ABORTED:
case PHASE_COMPLETED:
DPRINTF(("PHASE_COMPLETED\n"));
if (xfer->c_flags & C_DMA)
xfer->c_bcount -= sc_xfer->datalen;
sc_xfer->resid = xfer->c_bcount;
mvsata_atapi_phase_complete(xfer);
return 1;
default:
if (++retries<500) {
DELAY(100);
chp->ch_status = MVSATA_WDC_READ_1(mvport, SRB_CS);
chp->ch_error = MVSATA_WDC_READ_1(mvport, SRB_FE);
goto again;
}
aprint_error_dev(atac->atac_dev,
"channel %d drive %d: unknown phase 0x%x\n",
chp->ch_channel, xfer->c_drive, phase);
if (chp->ch_status & WDCS_ERR) {
sc_xfer->error = XS_SHORTSENSE;
sc_xfer->sense.atapi_sense = chp->ch_error;
} else {
if (xfer->c_flags & C_DMA)
ata_dmaerr(drvp,
(xfer->c_flags & C_POLL) ? AT_POLL : 0);
sc_xfer->error = XS_RESET;
mvsata_atapi_reset(chp, xfer);
return (1);
}
}
DPRINTF(("mvsata_atapi_intr: mvsata_atapi_done() (end), error 0x%x "
"sense 0x%x\n", sc_xfer->error, sc_xfer->sense.atapi_sense));
mvsata_atapi_done(chp, xfer);
return 1;
}
static void
mvsata_atapi_kill_xfer(struct ata_channel *chp, struct ata_xfer *xfer,
int reason)
{
struct mvsata_port *mvport = (struct mvsata_port *)chp;
struct scsipi_xfer *sc_xfer = xfer->c_cmd;
/* remove this command from xfer queue */
switch (reason) {
case KILL_GONE:
sc_xfer->error = XS_DRIVER_STUFFUP;
break;
case KILL_RESET:
sc_xfer->error = XS_RESET;
break;
default:
aprint_error_dev(MVSATA_DEV2(mvport),
"mvsata_atapi_kill_xfer: unknown reason %d\n", reason);
panic("mvsata_atapi_kill_xfer");
}
ata_free_xfer(chp, xfer);
scsipi_done(sc_xfer);
}
static void
mvsata_atapi_reset(struct ata_channel *chp, struct ata_xfer *xfer)
{
struct atac_softc *atac = chp->ch_atac;
struct ata_drive_datas *drvp = &chp->ch_drive[xfer->c_drive];
struct scsipi_xfer *sc_xfer = xfer->c_cmd;
mvsata_pmp_select(mvport, xfer->c_drive);
wdccommandshort(chp, 0, ATAPI_SOFT_RESET);
drvp->state = 0;
if (wdc_wait_for_unbusy(chp, WDC_RESET_WAIT, AT_POLL) != 0) {
printf("%s:%d:%d: reset failed\n", device_xname(atac->atac_dev),
chp->ch_channel, xfer->c_drive);
sc_xfer->error = XS_SELTIMEOUT;
}
mvsata_atapi_done(chp, xfer);
return;
}
static void
mvsata_atapi_phase_complete(struct ata_xfer *xfer)
{
struct ata_channel *chp = xfer->c_chp;
struct atac_softc *atac = chp->ch_atac;
struct wdc_softc *wdc = CHAN_TO_WDC(chp);
struct scsipi_xfer *sc_xfer = xfer->c_cmd;
struct ata_drive_datas *drvp = &chp->ch_drive[xfer->c_drive];
/* wait for DSC if needed */
if (drvp->drive_flags & ATA_DRIVE_ATAPIDSCW) {
DPRINTFN(1,
("%s:%d:%d: mvsata_atapi_phase_complete: polldsc %d\n",
device_xname(atac->atac_dev), chp->ch_channel,
xfer->c_drive, xfer->c_dscpoll));
if (cold)
panic("mvsata_atapi_phase_complete: cold");
if (wdcwait(chp, WDCS_DSC, WDCS_DSC, 10, AT_POLL) ==
WDCWAIT_TOUT) {
/* 10ms not enough, try again in 1 tick */
if (xfer->c_dscpoll++ > mstohz(sc_xfer->timeout)) {
aprint_error_dev(atac->atac_dev,
"channel %d: wait_for_dsc failed\n",
chp->ch_channel);
sc_xfer->error = XS_TIMEOUT;
mvsata_atapi_reset(chp, xfer);
return;
} else
callout_reset(&chp->ch_callout, 1,
mvsata_atapi_polldsc, xfer);
return;
}
}
/*
* Some drive occasionally set WDCS_ERR with
* "ATA illegal length indication" in the error
* register. If we read some data the sense is valid
* anyway, so don't report the error.
*/
if (chp->ch_status & WDCS_ERR &&
((sc_xfer->xs_control & XS_CTL_REQSENSE) == 0 ||
sc_xfer->resid == sc_xfer->datalen)) {
/* save the short sense */
sc_xfer->error = XS_SHORTSENSE;
sc_xfer->sense.atapi_sense = chp->ch_error;
if ((sc_xfer->xs_periph->periph_quirks & PQUIRK_NOSENSE) == 0) {
/* ask scsipi to send a REQUEST_SENSE */
sc_xfer->error = XS_BUSY;
sc_xfer->status = SCSI_CHECK;
} else
if (wdc->dma_status & (WDC_DMAST_NOIRQ | WDC_DMAST_ERR)) {
ata_dmaerr(drvp,
(xfer->c_flags & C_POLL) ? AT_POLL : 0);
sc_xfer->error = XS_RESET;
mvsata_atapi_reset(chp, xfer);
return;
}
}
if (xfer->c_bcount != 0)
DPRINTFN(1, ("%s:%d:%d: mvsata_atapi_intr:"
" bcount value is %d after io\n",
device_xname(atac->atac_dev), chp->ch_channel,
xfer->c_drive, xfer->c_bcount));
#ifdef DIAGNOSTIC
if (xfer->c_bcount < 0)
aprint_error_dev(atac->atac_dev,
"channel %d drive %d: mvsata_atapi_intr:"
" warning: bcount value is %d after io\n",
chp->ch_channel, xfer->c_drive, xfer->c_bcount);
#endif
DPRINTFN(1, ("%s:%d:%d: mvsata_atapi_phase_complete:"
" mvsata_atapi_done(), error 0x%x sense 0x%x\n",
device_xname(atac->atac_dev), chp->ch_channel, xfer->c_drive,
sc_xfer->error, sc_xfer->sense.atapi_sense));
mvsata_atapi_done(chp, xfer);
}
static void
mvsata_atapi_done(struct ata_channel *chp, struct ata_xfer *xfer)
{
struct atac_softc *atac = chp->ch_atac;
struct scsipi_xfer *sc_xfer = xfer->c_cmd;
int drive = xfer->c_drive;
DPRINTFN(1, ("%s:%d:%d: mvsata_atapi_done: flags 0x%x\n",
device_xname(atac->atac_dev), chp->ch_channel, xfer->c_drive,
(u_int)xfer->c_flags));
callout_stop(&chp->ch_callout);
/* mark controller inactive and free the command */
chp->ch_queue->active_xfer = NULL;
ata_free_xfer(chp, xfer);
if (chp->ch_drive[drive].drive_flags & ATA_DRIVE_WAITDRAIN) {
sc_xfer->error = XS_DRIVER_STUFFUP;
chp->ch_drive[drive].drive_flags &= ~ATA_DRIVE_WAITDRAIN;
wakeup(&chp->ch_queue->active_xfer);
}
DPRINTFN(1, ("%s:%d: mvsata_atapi_done: scsipi_done\n",
device_xname(atac->atac_dev), chp->ch_channel));
scsipi_done(sc_xfer);
DPRINTFN(1, ("%s:%d: atastart from wdc_atapi_done, flags 0x%x\n",
device_xname(atac->atac_dev), chp->ch_channel, chp->ch_flags));
atastart(chp);
}
static void
mvsata_atapi_polldsc(void *arg)
{
mvsata_atapi_phase_complete(arg);
}
#endif /* NATAPIBUS > 0 */
/*
* XXXX: Shall we need lock for race condition in mvsata_edma_enqueue{,_gen2}(),
* if supported queuing command by atabus? The race condition will not happen
* if this is called only to the thread of atabus.
*/
static int
mvsata_edma_enqueue(struct mvsata_port *mvport, struct ata_bio *ata_bio,
void *databuf)
{
struct mvsata_softc *sc = device_private(MVSATA_DEV2(mvport));
struct ata_channel *chp = &mvport->port_ata_channel;
struct eprd *eprd;
bus_addr_t crqb_base_addr;
bus_dmamap_t data_dmamap;
uint32_t reg;
int quetag, erqqip, erqqop, next, rv, i;
DPRINTFN(2, ("%s:%d:%d: mvsata_edma_enqueue:"
" blkno=0x%" PRIx64 ", nbytes=%d, flags=0x%x\n",
device_xname(MVSATA_DEV2(mvport)), mvport->port_hc->hc,
mvport->port, ata_bio->blkno, ata_bio->nbytes, ata_bio->flags));
reg = MVSATA_EDMA_READ_4(mvport, EDMA_REQQOP);
erqqop = (reg & EDMA_REQQP_ERQQP_MASK) >> EDMA_REQQP_ERQQP_SHIFT;
reg = MVSATA_EDMA_READ_4(mvport, EDMA_REQQIP);
erqqip = (reg & EDMA_REQQP_ERQQP_MASK) >> EDMA_REQQP_ERQQP_SHIFT;
next = erqqip;
MVSATA_EDMAQ_INC(next);
if (next == erqqop)
/* queue full */
return EBUSY;
if ((quetag = mvsata_quetag_get(mvport)) == -1)
/* tag nothing */
return EBUSY;
DPRINTFN(2, (" erqqip=%d, quetag=%d\n", erqqip, quetag));
rv = mvsata_dma_bufload(mvport, quetag, databuf, ata_bio->nbytes,
ata_bio->flags);
if (rv != 0)
return rv;
KASSERT(mvport->port_reqtbl[quetag].xfer == NULL);
KASSERT(chp->ch_queue->active_xfer != NULL);
mvport->port_reqtbl[quetag].xfer = chp->ch_queue->active_xfer;
/* setup EDMA Physical Region Descriptors (ePRD) Table Data */
data_dmamap = mvport->port_reqtbl[quetag].data_dmamap;
eprd = mvport->port_reqtbl[quetag].eprd;
for (i = 0; i < data_dmamap->dm_nsegs; i++) {
bus_addr_t ds_addr = data_dmamap->dm_segs[i].ds_addr;
bus_size_t ds_len = data_dmamap->dm_segs[i].ds_len;
eprd->prdbal = htole32(ds_addr & EPRD_PRDBAL_MASK);
eprd->bytecount = htole32(EPRD_BYTECOUNT(ds_len));
eprd->eot = htole16(0);
eprd->prdbah = htole32((ds_addr >> 16) >> 16);
eprd++;
}
(eprd - 1)->eot |= htole16(EPRD_EOT);
#ifdef MVSATA_DEBUG
if (mvsata_debug >= 3)
mvsata_print_eprd(mvport, quetag);
#endif
bus_dmamap_sync(mvport->port_dmat, mvport->port_eprd_dmamap,
mvport->port_reqtbl[quetag].eprd_offset, MVSATA_EPRD_MAX_SIZE,
BUS_DMASYNC_PREWRITE);
/* setup EDMA Command Request Block (CRQB) Data */
sc->sc_edma_setup_crqb(mvport, erqqip, quetag, ata_bio);
#ifdef MVSATA_DEBUG
if (mvsata_debug >= 3)
mvsata_print_crqb(mvport, erqqip);
#endif
bus_dmamap_sync(mvport->port_dmat, mvport->port_crqb_dmamap,
erqqip * sizeof(union mvsata_crqb),
sizeof(union mvsata_crqb), BUS_DMASYNC_PREWRITE);
MVSATA_EDMAQ_INC(erqqip);
crqb_base_addr = mvport->port_crqb_dmamap->dm_segs[0].ds_addr &
(EDMA_REQQP_ERQQBAP_MASK | EDMA_REQQP_ERQQBA_MASK);
MVSATA_EDMA_WRITE_4(mvport, EDMA_REQQBAH, (crqb_base_addr >> 16) >> 16);
MVSATA_EDMA_WRITE_4(mvport, EDMA_REQQIP,
crqb_base_addr | (erqqip << EDMA_REQQP_ERQQP_SHIFT));
return 0;
}
static int
mvsata_edma_handle(struct mvsata_port *mvport, struct ata_xfer *xfer1)
{
struct ata_channel *chp = &mvport->port_ata_channel;
struct crpb *crpb;
struct ata_bio *ata_bio;
struct ata_xfer *xfer;
uint32_t reg;
int erqqip, erqqop, erpqip, erpqop, prev_erpqop, quetag, handled = 0, n;
/* First, Sync for Request Queue buffer */
reg = MVSATA_EDMA_READ_4(mvport, EDMA_REQQOP);
erqqop = (reg & EDMA_REQQP_ERQQP_MASK) >> EDMA_REQQP_ERQQP_SHIFT;
if (mvport->port_prev_erqqop != erqqop) {
const int s = sizeof(union mvsata_crqb);
if (mvport->port_prev_erqqop < erqqop)
n = erqqop - mvport->port_prev_erqqop;
else {
if (erqqop > 0)
bus_dmamap_sync(mvport->port_dmat,
mvport->port_crqb_dmamap, 0, erqqop * s,
BUS_DMASYNC_POSTWRITE);
n = MVSATA_EDMAQ_LEN - mvport->port_prev_erqqop;
}
if (n > 0)
bus_dmamap_sync(mvport->port_dmat,
mvport->port_crqb_dmamap,
mvport->port_prev_erqqop * s, n * s,
BUS_DMASYNC_POSTWRITE);
mvport->port_prev_erqqop = erqqop;
}
reg = MVSATA_EDMA_READ_4(mvport, EDMA_RESQIP);
erpqip = (reg & EDMA_RESQP_ERPQP_MASK) >> EDMA_RESQP_ERPQP_SHIFT;
reg = MVSATA_EDMA_READ_4(mvport, EDMA_RESQOP);
erpqop = (reg & EDMA_RESQP_ERPQP_MASK) >> EDMA_RESQP_ERPQP_SHIFT;
DPRINTFN(3, ("%s:%d:%d: mvsata_edma_handle: erpqip=%d, erpqop=%d\n",
device_xname(MVSATA_DEV2(mvport)), mvport->port_hc->hc,
mvport->port, erpqip, erpqop));
if (erpqop == erpqip)
return 0;
if (erpqop < erpqip)
n = erpqip - erpqop;
else {
if (erpqip > 0)
bus_dmamap_sync(mvport->port_dmat,
mvport->port_crpb_dmamap,
0, erpqip * sizeof(struct crpb),
BUS_DMASYNC_POSTREAD);
n = MVSATA_EDMAQ_LEN - erpqop;
}
if (n > 0)
bus_dmamap_sync(mvport->port_dmat, mvport->port_crpb_dmamap,
erpqop * sizeof(struct crpb),
n * sizeof(struct crpb), BUS_DMASYNC_POSTREAD);
prev_erpqop = erpqop;
while (erpqop != erpqip) {
#ifdef MVSATA_DEBUG
if (mvsata_debug >= 3)
mvsata_print_crpb(mvport, erpqop);
#endif
crpb = mvport->port_crpb + erpqop;
quetag = CRPB_CHOSTQUETAG(le16toh(crpb->id));
KASSERT(chp->ch_queue->active_xfer != NULL);
xfer = chp->ch_queue->active_xfer;
KASSERT(xfer == mvport->port_reqtbl[quetag].xfer);
#ifdef DIAGNOSTIC
if (xfer == NULL)
panic("unknown response received: %s:%d:%d: tag 0x%x\n",
device_xname(MVSATA_DEV2(mvport)),
mvport->port_hc->hc, mvport->port, quetag);
#endif
bus_dmamap_sync(mvport->port_dmat, mvport->port_eprd_dmamap,
mvport->port_reqtbl[quetag].eprd_offset,
MVSATA_EPRD_MAX_SIZE, BUS_DMASYNC_POSTWRITE);
chp->ch_status = CRPB_CDEVSTS(le16toh(crpb->rspflg));
chp->ch_error = CRPB_CEDMASTS(le16toh(crpb->rspflg));
ata_bio = xfer->c_cmd;
ata_bio->error = NOERROR;
ata_bio->r_error = 0;
if (chp->ch_status & WDCS_ERR)
ata_bio->error = ERROR;
if (chp->ch_status & WDCS_BSY)
ata_bio->error = TIMEOUT;
if (chp->ch_error)
ata_bio->error = ERR_DMA;
mvsata_dma_bufunload(mvport, quetag, ata_bio->flags);
mvport->port_reqtbl[quetag].xfer = NULL;
mvsata_quetag_put(mvport, quetag);
MVSATA_EDMAQ_INC(erpqop);
#if 1 /* XXXX: flags clears here, because necessary the atabus layer. */
erqqip = (MVSATA_EDMA_READ_4(mvport, EDMA_REQQIP) &
EDMA_REQQP_ERQQP_MASK) >> EDMA_REQQP_ERQQP_SHIFT;
if (erpqop == erqqip)
chp->ch_flags &= ~(ATACH_DMA_WAIT | ATACH_IRQ_WAIT);
#endif
mvsata_bio_intr(chp, xfer, 1);
if (xfer1 == NULL)
handled++;
else if (xfer == xfer1) {
handled = 1;
break;
}
}
if (prev_erpqop < erpqop)
n = erpqop - prev_erpqop;
else {
if (erpqop > 0)
bus_dmamap_sync(mvport->port_dmat,
mvport->port_crpb_dmamap, 0,
erpqop * sizeof(struct crpb), BUS_DMASYNC_PREREAD);
n = MVSATA_EDMAQ_LEN - prev_erpqop;
}
if (n > 0)
bus_dmamap_sync(mvport->port_dmat, mvport->port_crpb_dmamap,
prev_erpqop * sizeof(struct crpb),
n * sizeof(struct crpb), BUS_DMASYNC_PREREAD);
reg &= ~EDMA_RESQP_ERPQP_MASK;
reg |= (erpqop << EDMA_RESQP_ERPQP_SHIFT);
MVSATA_EDMA_WRITE_4(mvport, EDMA_RESQOP, reg);
#if 0 /* already cleared ago? */
erqqip = (MVSATA_EDMA_READ_4(mvport, EDMA_REQQIP) &
EDMA_REQQP_ERQQP_MASK) >> EDMA_REQQP_ERQQP_SHIFT;
if (erpqop == erqqip)
chp->ch_flags &= ~(ATACH_DMA_WAIT | ATACH_IRQ_WAIT);
#endif
return handled;
}
static int
mvsata_edma_wait(struct mvsata_port *mvport, struct ata_xfer *xfer, int timeout)
{
struct ata_bio *ata_bio = xfer->c_cmd;
int xtime;
for (xtime = 0; xtime < timeout / 10; xtime++) {
if (mvsata_edma_handle(mvport, xfer))
return 0;
if (ata_bio->flags & ATA_POLL)
delay(10000);
else
tsleep(&xfer, PRIBIO, "mvsataipl", mstohz(10));
}
DPRINTF(("mvsata_edma_wait: timeout: %p\n", xfer));
mvsata_edma_rqq_remove(mvport, xfer);
xfer->c_flags |= C_TIMEOU;
return 1;
}
static void
mvsata_edma_timeout(void *arg)
{
struct ata_xfer *xfer = (struct ata_xfer *)arg;
struct ata_channel *chp = xfer->c_chp;
struct mvsata_port *mvport = (struct mvsata_port *)chp;
int s;
s = splbio();
DPRINTF(("mvsata_edma_timeout: %p\n", xfer));
if ((chp->ch_flags & ATACH_IRQ_WAIT) != 0) {
mvsata_edma_rqq_remove(mvport, xfer);
xfer->c_flags |= C_TIMEOU;
mvsata_bio_intr(chp, xfer, 1);
}
splx(s);
}
static void
mvsata_edma_rqq_remove(struct mvsata_port *mvport, struct ata_xfer *xfer)
{
struct mvsata_softc *sc = device_private(MVSATA_DEV2(mvport));
struct ata_bio *ata_bio;
bus_addr_t crqb_base_addr;
int erqqip, i;
/* First, hardware reset, stop EDMA */
mvsata_hreset_port(mvport);
/* cleanup completed EDMA safely */
mvsata_edma_handle(mvport, NULL);
bus_dmamap_sync(mvport->port_dmat, mvport->port_crqb_dmamap, 0,
sizeof(union mvsata_crqb) * MVSATA_EDMAQ_LEN, BUS_DMASYNC_PREWRITE);
for (i = 0, erqqip = 0; i < MVSATA_EDMAQ_LEN; i++) {
if (mvport->port_reqtbl[i].xfer == NULL)
continue;
ata_bio = mvport->port_reqtbl[i].xfer->c_cmd;
if (mvport->port_reqtbl[i].xfer == xfer) {
/* remove xfer from EDMA request queue */
bus_dmamap_sync(mvport->port_dmat,
mvport->port_eprd_dmamap,
mvport->port_reqtbl[i].eprd_offset,
MVSATA_EPRD_MAX_SIZE, BUS_DMASYNC_POSTWRITE);
mvsata_dma_bufunload(mvport, i, ata_bio->flags);
mvport->port_reqtbl[i].xfer = NULL;
mvsata_quetag_put(mvport, i);
continue;
}
sc->sc_edma_setup_crqb(mvport, erqqip, i, ata_bio);
erqqip++;
}
bus_dmamap_sync(mvport->port_dmat, mvport->port_crqb_dmamap, 0,
sizeof(union mvsata_crqb) * MVSATA_EDMAQ_LEN,
BUS_DMASYNC_POSTWRITE);
mvsata_edma_config(mvport, mvport->port_edmamode);
mvsata_edma_reset_qptr(mvport);
mvsata_edma_enable(mvport);
crqb_base_addr = mvport->port_crqb_dmamap->dm_segs[0].ds_addr &
(EDMA_REQQP_ERQQBAP_MASK | EDMA_REQQP_ERQQBA_MASK);
MVSATA_EDMA_WRITE_4(mvport, EDMA_REQQBAH, (crqb_base_addr >> 16) >> 16);
MVSATA_EDMA_WRITE_4(mvport, EDMA_REQQIP,
crqb_base_addr | (erqqip << EDMA_REQQP_ERQQP_SHIFT));
}
#if NATAPIBUS > 0
static int
mvsata_bdma_init(struct mvsata_port *mvport, struct scsipi_xfer *sc_xfer,
void *databuf)
{
struct mvsata_softc *sc = device_private(MVSATA_DEV2(mvport));
struct eprd *eprd;
bus_dmamap_t data_dmamap;
bus_addr_t eprd_addr;
int quetag, rv;
DPRINTFN(2,
("%s:%d:%d: mvsata_bdma_init: datalen=%d, xs_control=0x%x\n",
device_xname(MVSATA_DEV2(mvport)), mvport->port_hc->hc,
mvport->port, sc_xfer->datalen, sc_xfer->xs_control));
if ((quetag = mvsata_quetag_get(mvport)) == -1)
/* tag nothing */
return EBUSY;
DPRINTFN(2, (" quetag=%d\n", quetag));
rv = mvsata_dma_bufload(mvport, quetag, databuf, sc_xfer->datalen,
sc_xfer->xs_control & XS_CTL_DATA_IN ? ATA_READ : 0);
if (rv != 0)
return rv;
KASSERT(chp->ch_queue->active_xfer != NULL);
KASSERT(mvport->port_reqtbl[quetag].xfer == NULL);
mvport->port_reqtbl[quetag].xfer = chp->ch_queue->active_xfer;
/* setup EDMA Physical Region Descriptors (ePRD) Table Data */
data_dmamap = mvport->port_reqtbl[quetag].data_dmamap;
eprd = mvport->port_reqtbl[quetag].eprd;
for (i = 0; i < data_dmamap->dm_nsegs; i++) {
bus_addr_t ds_addr = data_dmamap->dm_segs[i].ds_addr;
bus_size_t ds_len = data_dmamap->dm_segs[i].ds_len;
eprd->prdbal = htole32(ds_addr & EPRD_PRDBAL_MASK);
eprd->bytecount = htole32(EPRD_BYTECOUNT(ds_len));
eprd->eot = htole16(0);
eprd->prdbah = htole32((ds_addr >> 16) >> 16);
eprd++;
}
(eprd - 1)->eot |= htole16(EPRD_EOT);
#ifdef MVSATA_DEBUG
if (mvsata_debug >= 3)
mvsata_print_eprd(mvport, quetag);
#endif
bus_dmamap_sync(mvport->port_dmat, mvport->port_eprd_dmamap,
mvport->port_reqtbl[quetag].eprd_offset, MVSATA_EPRD_MAX_SIZE,
BUS_DMASYNC_PREWRITE);
eprd_addr = mvport->port_eprd_dmamap->dm_segs[0].ds_addr +
mvport->port_reqtbl[quetag].eprd_offset;
MVSATA_EDMA_WRITE_4(mvport, DMA_DTLBA, eprd_addr & DMA_DTLBA_MASK);
MVSATA_EDMA_WRITE_4(mvport, DMA_DTHBA, (eprd_addr >> 16) >> 16);
if (sc_xfer->xs_control & XS_CTL_DATA_IN)
MVSATA_EDMA_WRITE_4(mvport, DMA_C, DMA_C_READ);
else
MVSATA_EDMA_WRITE_4(mvport, DMA_C, 0);
return 0;
}
static void
mvsata_bdma_start(struct mvsata_port *mvport)
{
#ifdef MVSATA_DEBUG
if (mvsata_debug >= 3)
mvsata_print_eprd(mvport, 0);
#endif
MVSATA_EDMA_WRITE_4(mvport, DMA_C,
MVSATA_EDMA_READ_4(mvport, DMA_C) | DMA_C_START);
}
#endif
#endif
static int
mvsata_port_init(struct mvsata_hc *mvhc, int port)
{
struct mvsata_softc *sc = mvhc->hc_sc;
struct mvsata_port *mvport;
struct ata_channel *chp;
int channel, rv, i;
const int crqbq_size = sizeof(union mvsata_crqb) * MVSATA_EDMAQ_LEN;
const int crpbq_size = sizeof(struct crpb) * MVSATA_EDMAQ_LEN;
const int eprd_buf_size = MVSATA_EPRD_MAX_SIZE * MVSATA_EDMAQ_LEN;
mvport = malloc(sizeof(struct mvsata_port), M_DEVBUF,
M_ZERO | M_NOWAIT);
if (mvport == NULL) {
aprint_error("%s:%d: can't allocate memory for port %d\n",
device_xname(MVSATA_DEV(sc)), mvhc->hc, port);
return ENOMEM;
}
mvport->port = port;
mvport->port_hc = mvhc;
mvport->port_edmamode = nodma;
rv = bus_space_subregion(mvhc->hc_iot, mvhc->hc_ioh,
EDMA_REGISTERS_OFFSET + port * EDMA_REGISTERS_SIZE,
EDMA_REGISTERS_SIZE, &mvport->port_ioh);
if (rv != 0) {
aprint_error("%s:%d: can't subregion EDMA %d registers\n",
device_xname(MVSATA_DEV(sc)), mvhc->hc, port);
goto fail0;
}
mvport->port_iot = mvhc->hc_iot;
rv = bus_space_subregion(mvport->port_iot, mvport->port_ioh, SATA_SS, 4,
&mvport->port_sata_sstatus);
if (rv != 0) {
aprint_error("%s:%d:%d: couldn't subregion sstatus regs\n",
device_xname(MVSATA_DEV(sc)), mvhc->hc, port);
goto fail0;
}
rv = bus_space_subregion(mvport->port_iot, mvport->port_ioh, SATA_SE, 4,
&mvport->port_sata_serror);
if (rv != 0) {
aprint_error("%s:%d:%d: couldn't subregion serror regs\n",
device_xname(MVSATA_DEV(sc)), mvhc->hc, port);
goto fail0;
}
if (sc->sc_rev == gen1)
rv = bus_space_subregion(mvhc->hc_iot, mvhc->hc_ioh,
SATAHC_I_R02(port), 4, &mvport->port_sata_scontrol);
else
rv = bus_space_subregion(mvport->port_iot, mvport->port_ioh,
SATA_SC, 4, &mvport->port_sata_scontrol);
if (rv != 0) {
aprint_error("%s:%d:%d: couldn't subregion scontrol regs\n",
device_xname(MVSATA_DEV(sc)), mvhc->hc, port);
goto fail0;
}
mvport->port_dmat = sc->sc_dmat;
#ifndef MVSATA_WITHOUTDMA
mvsata_quetag_init(mvport);
#endif
mvhc->hc_ports[port] = mvport;
channel = mvhc->hc * sc->sc_port + port;
chp = &mvport->port_ata_channel;
chp->ch_channel = channel;
chp->ch_atac = &sc->sc_wdcdev.sc_atac;
chp->ch_queue = &mvport->port_ata_queue;
sc->sc_ata_channels[channel] = chp;
rv = mvsata_wdc_reg_init(mvport, sc->sc_wdcdev.regs + channel);
if (rv != 0)
goto fail0;
rv = bus_dmamap_create(mvport->port_dmat, crqbq_size, 1, crqbq_size, 0,
BUS_DMA_NOWAIT, &mvport->port_crqb_dmamap);
if (rv != 0) {
aprint_error(
"%s:%d:%d: EDMA CRQB map create failed: error=%d\n",
device_xname(MVSATA_DEV(sc)), mvhc->hc, port, rv);
goto fail0;
}
rv = bus_dmamap_create(mvport->port_dmat, crpbq_size, 1, crpbq_size, 0,
BUS_DMA_NOWAIT, &mvport->port_crpb_dmamap);
if (rv != 0) {
aprint_error(
"%s:%d:%d: EDMA CRPB map create failed: error=%d\n",
device_xname(MVSATA_DEV(sc)), mvhc->hc, port, rv);
goto fail1;
}
rv = bus_dmamap_create(mvport->port_dmat, eprd_buf_size, 1,
eprd_buf_size, 0, BUS_DMA_NOWAIT, &mvport->port_eprd_dmamap);
if (rv != 0) {
aprint_error(
"%s:%d:%d: EDMA ePRD buffer map create failed: error=%d\n",
device_xname(MVSATA_DEV(sc)), mvhc->hc, port, rv);
goto fail2;
}
for (i = 0; i < MVSATA_EDMAQ_LEN; i++) {
rv = bus_dmamap_create(mvport->port_dmat, MAXPHYS,
MAXPHYS / PAGE_SIZE, MAXPHYS, 0, BUS_DMA_NOWAIT,
&mvport->port_reqtbl[i].data_dmamap);
if (rv != 0) {
aprint_error("%s:%d:%d:"
" EDMA data map(%d) create failed: error=%d\n",
device_xname(MVSATA_DEV(sc)), mvhc->hc, port, i,
rv);
goto fail3;
}
}
return 0;
fail3:
for (i--; i >= 0; i--)
bus_dmamap_destroy(mvport->port_dmat,
mvport->port_reqtbl[i].data_dmamap);
bus_dmamap_destroy(mvport->port_dmat, mvport->port_eprd_dmamap);
fail2:
bus_dmamap_destroy(mvport->port_dmat, mvport->port_crpb_dmamap);
fail1:
bus_dmamap_destroy(mvport->port_dmat, mvport->port_crqb_dmamap);
fail0:
return rv;
}
static int
mvsata_wdc_reg_init(struct mvsata_port *mvport, struct wdc_regs *wdr)
{
int hc, port, rv, i;
hc = mvport->port_hc->hc;
port = mvport->port;
/* Create subregion for Shadow Registers Map */
rv = bus_space_subregion(mvport->port_iot, mvport->port_ioh,
SHADOW_REG_BLOCK_OFFSET, SHADOW_REG_BLOCK_SIZE, &wdr->cmd_baseioh);
if (rv != 0) {
aprint_error("%s:%d:%d: couldn't subregion shadow block regs\n",
device_xname(MVSATA_DEV2(mvport)), hc, port);
return rv;
}
wdr->cmd_iot = mvport->port_iot;
/* Once create subregion for each command registers */
for (i = 0; i < WDC_NREG; i++) {
rv = bus_space_subregion(wdr->cmd_iot, wdr->cmd_baseioh,
i * 4, sizeof(uint32_t), &wdr->cmd_iohs[i]);
if (rv != 0) {
aprint_error("%s:%d:%d: couldn't subregion cmd regs\n",
device_xname(MVSATA_DEV2(mvport)), hc, port);
return rv;
}
}
/* Create subregion for Alternate Status register */
rv = bus_space_subregion(wdr->cmd_iot, wdr->cmd_baseioh,
i * 4, sizeof(uint32_t), &wdr->ctl_ioh);
if (rv != 0) {
aprint_error("%s:%d:%d: couldn't subregion cmd regs\n",
device_xname(MVSATA_DEV2(mvport)), hc, port);
return rv;
}
wdr->ctl_iot = mvport->port_iot;
wdc_init_shadow_regs(&mvport->port_ata_channel);
rv = bus_space_subregion(mvport->port_iot, mvport->port_ioh,
SATA_SS, sizeof(uint32_t) * 3, &wdr->sata_baseioh);
if (rv != 0) {
aprint_error("%s:%d:%d: couldn't subregion SATA regs\n",
device_xname(MVSATA_DEV2(mvport)), hc, port);
return rv;
}
wdr->sata_iot = mvport->port_iot;
rv = bus_space_subregion(mvport->port_iot, mvport->port_ioh,
SATA_SC, sizeof(uint32_t), &wdr->sata_control);
if (rv != 0) {
aprint_error("%s:%d:%d: couldn't subregion SControl\n",
device_xname(MVSATA_DEV2(mvport)), hc, port);
return rv;
}
rv = bus_space_subregion(mvport->port_iot, mvport->port_ioh,
SATA_SS, sizeof(uint32_t), &wdr->sata_status);
if (rv != 0) {
aprint_error("%s:%d:%d: couldn't subregion SStatus\n",
device_xname(MVSATA_DEV2(mvport)), hc, port);
return rv;
}
rv = bus_space_subregion(mvport->port_iot, mvport->port_ioh,
SATA_SE, sizeof(uint32_t), &wdr->sata_error);
if (rv != 0) {
aprint_error("%s:%d:%d: couldn't subregion SError\n",
device_xname(MVSATA_DEV2(mvport)), hc, port);
return rv;
}
return 0;
}
#ifndef MVSATA_WITHOUTDMA
/*
* There are functions to determine Host Queue Tag.
* XXXX: We hope to rotate Tag to facilitate debugging.
*/
static inline void
mvsata_quetag_init(struct mvsata_port *mvport)
{
mvport->port_quetagidx = 0;
}
static inline int
mvsata_quetag_get(struct mvsata_port *mvport)
{
int begin = mvport->port_quetagidx;
do {
if (mvport->port_reqtbl[mvport->port_quetagidx].xfer == NULL) {
MVSATA_EDMAQ_INC(mvport->port_quetagidx);
return mvport->port_quetagidx;
}
MVSATA_EDMAQ_INC(mvport->port_quetagidx);
} while (mvport->port_quetagidx != begin);
return -1;
}
static inline void
mvsata_quetag_put(struct mvsata_port *mvport, int quetag)
{
/* nothing */
}
static void *
mvsata_edma_resource_prepare(struct mvsata_port *mvport, bus_dma_tag_t dmat,
bus_dmamap_t *dmamap, size_t size, int write)
{
bus_dma_segment_t seg;
int nseg, rv;
void *kva;
rv = bus_dmamem_alloc(dmat, size, PAGE_SIZE, 0, &seg, 1, &nseg,
BUS_DMA_NOWAIT);
if (rv != 0) {
aprint_error("%s:%d:%d: DMA memory alloc failed: error=%d\n",
device_xname(MVSATA_DEV2(mvport)),
mvport->port_hc->hc, mvport->port, rv);
goto fail;
}
rv = bus_dmamem_map(dmat, &seg, nseg, size, &kva, BUS_DMA_NOWAIT);
if (rv != 0) {
aprint_error("%s:%d:%d: DMA memory map failed: error=%d\n",
device_xname(MVSATA_DEV2(mvport)),
mvport->port_hc->hc, mvport->port, rv);
goto free;
}
rv = bus_dmamap_load(dmat, *dmamap, kva, size, NULL,
BUS_DMA_NOWAIT | (write ? BUS_DMA_WRITE : BUS_DMA_READ));
if (rv != 0) {
aprint_error("%s:%d:%d: DMA map load failed: error=%d\n",
device_xname(MVSATA_DEV2(mvport)),
mvport->port_hc->hc, mvport->port, rv);
goto unmap;
}
if (!write)
bus_dmamap_sync(dmat, *dmamap, 0, size, BUS_DMASYNC_PREREAD);
return kva;
unmap:
bus_dmamem_unmap(dmat, kva, size);
free:
bus_dmamem_free(dmat, &seg, nseg);
fail:
return NULL;
}
/* ARGSUSED */
static void
mvsata_edma_resource_purge(struct mvsata_port *mvport, bus_dma_tag_t dmat,
bus_dmamap_t dmamap, void *kva)
{
bus_dmamap_unload(dmat, dmamap);
bus_dmamem_unmap(dmat, kva, dmamap->dm_mapsize);
bus_dmamem_free(dmat, dmamap->dm_segs, dmamap->dm_nsegs);
}
static int
mvsata_dma_bufload(struct mvsata_port *mvport, int index, void *databuf,
size_t datalen, int flags)
{
int rv, lop, sop;
bus_dmamap_t data_dmamap = mvport->port_reqtbl[index].data_dmamap;
lop = (flags & ATA_READ) ? BUS_DMA_READ : BUS_DMA_WRITE;
sop = (flags & ATA_READ) ? BUS_DMASYNC_PREREAD : BUS_DMASYNC_PREWRITE;
rv = bus_dmamap_load(mvport->port_dmat, data_dmamap, databuf, datalen,
NULL, BUS_DMA_NOWAIT | lop);
if (rv) {
aprint_error("%s:%d:%d: buffer load failed: error=%d",
device_xname(MVSATA_DEV2(mvport)), mvport->port_hc->hc,
mvport->port, rv);
return rv;
}
bus_dmamap_sync(mvport->port_dmat, data_dmamap, 0,
data_dmamap->dm_mapsize, sop);
return 0;
}
static inline void
mvsata_dma_bufunload(struct mvsata_port *mvport, int index, int flags)
{
bus_dmamap_t data_dmamap = mvport->port_reqtbl[index].data_dmamap;
bus_dmamap_sync(mvport->port_dmat, data_dmamap, 0,
data_dmamap->dm_mapsize,
(flags & ATA_READ) ? BUS_DMASYNC_POSTREAD : BUS_DMASYNC_POSTWRITE);
bus_dmamap_unload(mvport->port_dmat, data_dmamap);
}
#endif
static void
mvsata_hreset_port(struct mvsata_port *mvport)
{
struct mvsata_softc *sc = device_private(MVSATA_DEV2(mvport));
MVSATA_EDMA_WRITE_4(mvport, EDMA_CMD, EDMA_CMD_EATARST);
delay(25); /* allow reset propagation */
MVSATA_EDMA_WRITE_4(mvport, EDMA_CMD, 0);
mvport->_fix_phy_param._fix_phy(mvport);
if (sc->sc_gen == gen1)
delay(1000);
}
static void
mvsata_reset_port(struct mvsata_port *mvport)
{
device_t parent = device_parent(MVSATA_DEV2(mvport));
MVSATA_EDMA_WRITE_4(mvport, EDMA_CMD, EDMA_CMD_EDSEDMA);
mvsata_hreset_port(mvport);
if (device_is_a(parent, "pci"))
MVSATA_EDMA_WRITE_4(mvport, EDMA_CFG,
EDMA_CFG_RESERVED | EDMA_CFG_ERDBSZ);
else /* SoC */
MVSATA_EDMA_WRITE_4(mvport, EDMA_CFG,
EDMA_CFG_RESERVED | EDMA_CFG_RESERVED2);
MVSATA_EDMA_WRITE_4(mvport, EDMA_T, 0);
MVSATA_EDMA_WRITE_4(mvport, SATA_SEIM, 0x019c0000);
MVSATA_EDMA_WRITE_4(mvport, SATA_SE, ~0);
MVSATA_EDMA_WRITE_4(mvport, SATA_FISIC, 0);
MVSATA_EDMA_WRITE_4(mvport, EDMA_IEC, 0);
MVSATA_EDMA_WRITE_4(mvport, EDMA_IEM, 0);
MVSATA_EDMA_WRITE_4(mvport, EDMA_REQQBAH, 0);
MVSATA_EDMA_WRITE_4(mvport, EDMA_REQQIP, 0);
MVSATA_EDMA_WRITE_4(mvport, EDMA_REQQOP, 0);
MVSATA_EDMA_WRITE_4(mvport, EDMA_RESQBAH, 0);
MVSATA_EDMA_WRITE_4(mvport, EDMA_RESQIP, 0);
MVSATA_EDMA_WRITE_4(mvport, EDMA_RESQOP, 0);
MVSATA_EDMA_WRITE_4(mvport, EDMA_CMD, 0);
MVSATA_EDMA_WRITE_4(mvport, EDMA_TC, 0);
MVSATA_EDMA_WRITE_4(mvport, EDMA_IORT, 0xbc);
}
static void
mvsata_reset_hc(struct mvsata_hc *mvhc)
{
#if 0
uint32_t val;
#endif
MVSATA_HC_WRITE_4(mvhc, SATAHC_ICT, 0);
MVSATA_HC_WRITE_4(mvhc, SATAHC_ITT, 0);
MVSATA_HC_WRITE_4(mvhc, SATAHC_IC, 0);
#if 0 /* XXXX needs? */
MVSATA_HC_WRITE_4(mvhc, 0x01c, 0);
/*
* Keep the SS during power on and the reference clock bits (reset
* sample)
*/
val = MVSATA_HC_READ_4(mvhc, 0x020);
val &= 0x1c1c1c1c;
val |= 0x03030303;
MVSATA_HC_READ_4(mvhc, 0x020, 0);
#endif
}
#define WDCDELAY 100 /* 100 microseconds */
#define WDCNDELAY_RST (WDC_RESET_WAIT * 1000 / WDCDELAY)
static uint32_t
mvsata_softreset(struct mvsata_port *mvport, int waitok)
{
uint32_t sig0 = ~0;
int timeout, nloop;
uint8_t st0;
MVSATA_WDC_WRITE_1(mvport, SRB_CAS, WDCTL_RST | WDCTL_IDS | WDCTL_4BIT);
delay(10);
(void) MVSATA_WDC_READ_1(mvport, SRB_FE);
MVSATA_WDC_WRITE_1(mvport, SRB_CAS, WDCTL_IDS | WDCTL_4BIT);
delay(10);
if (!waitok)
nloop = WDCNDELAY_RST;
else
nloop = WDC_RESET_WAIT * hz / 1000;
/* wait for BSY to deassert */
for (timeout = 0; timeout < nloop; timeout++) {
st0 = MVSATA_WDC_READ_1(mvport, SRB_CS);
if ((st0 & WDCS_BSY) == 0) {
sig0 = MVSATA_WDC_READ_1(mvport, SRB_SC) << 0;
sig0 |= MVSATA_WDC_READ_1(mvport, SRB_LBAL) << 8;
sig0 |= MVSATA_WDC_READ_1(mvport, SRB_LBAM) << 16;
sig0 |= MVSATA_WDC_READ_1(mvport, SRB_LBAH) << 24;
goto out;
}
if (!waitok)
delay(WDCDELAY);
else
tsleep(&nloop, PRIBIO, "atarst", 1);
}
out:
MVSATA_WDC_WRITE_1(mvport, SRB_CAS, WDCTL_4BIT);
return sig0;
}
#ifndef MVSATA_WITHOUTDMA
static void
mvsata_edma_reset_qptr(struct mvsata_port *mvport)
{
const bus_addr_t crpb_addr =
mvport->port_crpb_dmamap->dm_segs[0].ds_addr;
const uint32_t crpb_addr_mask =
EDMA_RESQP_ERPQBAP_MASK | EDMA_RESQP_ERPQBA_MASK;
MVSATA_EDMA_WRITE_4(mvport, EDMA_REQQBAH, 0);
MVSATA_EDMA_WRITE_4(mvport, EDMA_REQQIP, 0);
MVSATA_EDMA_WRITE_4(mvport, EDMA_REQQOP, 0);
MVSATA_EDMA_WRITE_4(mvport, EDMA_RESQBAH, (crpb_addr >> 16) >> 16);
MVSATA_EDMA_WRITE_4(mvport, EDMA_RESQIP, 0);
MVSATA_EDMA_WRITE_4(mvport, EDMA_RESQOP, (crpb_addr & crpb_addr_mask));
}
static inline void
mvsata_edma_enable(struct mvsata_port *mvport)
{
MVSATA_EDMA_WRITE_4(mvport, EDMA_CMD, EDMA_CMD_EENEDMA);
}
static int
mvsata_edma_disable(struct mvsata_port *mvport, int timeout, int waitok)
{
uint32_t status, command;
int ms;
if (MVSATA_EDMA_READ_4(mvport, EDMA_CMD) & EDMA_CMD_EENEDMA) {
for (ms = 0; ms < timeout; ms++) {
status = MVSATA_EDMA_READ_4(mvport, EDMA_S);
if (status & EDMA_S_EDMAIDLE)
break;
if (waitok)
tsleep(&waitok, PRIBIO, "mvsata_edma1",
mstohz(1));
else
delay(1000);
}
if (ms == timeout) {
aprint_error("%s:%d:%d: unable to stop EDMA\n",
device_xname(MVSATA_DEV2(mvport)),
mvport->port_hc->hc, mvport->port);
return EBUSY;
}
/* The diable bit (eDsEDMA) is self negated. */
MVSATA_EDMA_WRITE_4(mvport, EDMA_CMD, EDMA_CMD_EDSEDMA);
for ( ; ms < timeout; ms++) {
command = MVSATA_EDMA_READ_4(mvport, EDMA_CMD);
if (!(command & EDMA_CMD_EENEDMA))
break;
if (waitok)
tsleep(&waitok, PRIBIO, "mvsata_edma2",
mstohz(1));
else
delay(1000);
}
if (ms == timeout) {
aprint_error("%s:%d:%d: unable to stop EDMA\n",
device_xname(MVSATA_DEV2(mvport)),
mvport->port_hc->hc, mvport->port);
return EBUSY;
}
}
return 0;
}
/*
* Set EDMA registers according to mode.
* ex. NCQ/TCQ(queued)/non queued.
*/
static void
mvsata_edma_config(struct mvsata_port *mvport, int mode)
{
struct mvsata_softc *sc = device_private(MVSATA_DEV2(mvport));
uint32_t reg;
reg = MVSATA_EDMA_READ_4(mvport, EDMA_CFG);
reg |= EDMA_CFG_RESERVED;
if (mode == ncq) {
if (sc->sc_gen == gen1) {
aprint_error_dev(MVSATA_DEV2(mvport),
"GenI not support NCQ\n");
return;
} else if (sc->sc_gen == gen2)
reg |= EDMA_CFG_EDEVERR;
reg |= EDMA_CFG_ESATANATVCMDQUE;
} else if (mode == queued) {
reg &= ~EDMA_CFG_ESATANATVCMDQUE;
reg |= EDMA_CFG_EQUE;
} else
reg &= ~(EDMA_CFG_ESATANATVCMDQUE | EDMA_CFG_EQUE);
if (sc->sc_gen == gen1)
reg |= EDMA_CFG_ERDBSZ;
else if (sc->sc_gen == gen2)
reg |= (EDMA_CFG_ERDBSZEXT | EDMA_CFG_EWRBUFFERLEN);
else if (sc->sc_gen == gen2e) {
device_t parent = device_parent(MVSATA_DEV(sc));
reg |= (EDMA_CFG_EMASKRXPM | EDMA_CFG_EHOSTQUEUECACHEEN);
reg &= ~(EDMA_CFG_EEDMAFBS | EDMA_CFG_EEDMAQUELEN);
if (device_is_a(parent, "pci"))
reg |= (
#if NATAPIBUS > 0
EDMA_CFG_EEARLYCOMPLETIONEN |
#endif
EDMA_CFG_ECUTTHROUGHEN |
EDMA_CFG_EWRBUFFERLEN |
EDMA_CFG_ERDBSZEXT);
}
MVSATA_EDMA_WRITE_4(mvport, EDMA_CFG, reg);
reg = (
EDMA_IE_EIORDYERR |
EDMA_IE_ETRANSINT |
EDMA_IE_EDEVCON |
EDMA_IE_EDEVDIS);
if (sc->sc_gen != gen1)
reg |= (
EDMA_IE_TRANSPROTERR |
EDMA_IE_LINKDATATXERR(EDMA_IE_LINKTXERR_FISTXABORTED) |
EDMA_IE_LINKDATATXERR(EDMA_IE_LINKXERR_OTHERERRORS) |
EDMA_IE_LINKDATATXERR(EDMA_IE_LINKXERR_LINKLAYERRESET) |
EDMA_IE_LINKDATATXERR(EDMA_IE_LINKXERR_INTERNALFIFO) |
EDMA_IE_LINKDATATXERR(EDMA_IE_LINKXERR_SATACRC) |
EDMA_IE_LINKCTLTXERR(EDMA_IE_LINKXERR_OTHERERRORS) |
EDMA_IE_LINKCTLTXERR(EDMA_IE_LINKXERR_LINKLAYERRESET) |
EDMA_IE_LINKCTLTXERR(EDMA_IE_LINKXERR_INTERNALFIFO) |
EDMA_IE_LINKDATARXERR(EDMA_IE_LINKXERR_OTHERERRORS) |
EDMA_IE_LINKDATARXERR(EDMA_IE_LINKXERR_LINKLAYERRESET) |
EDMA_IE_LINKDATARXERR(EDMA_IE_LINKXERR_INTERNALFIFO) |
EDMA_IE_LINKDATARXERR(EDMA_IE_LINKXERR_SATACRC) |
EDMA_IE_LINKCTLRXERR(EDMA_IE_LINKXERR_OTHERERRORS) |
EDMA_IE_LINKCTLRXERR(EDMA_IE_LINKXERR_LINKLAYERRESET) |
EDMA_IE_LINKCTLRXERR(EDMA_IE_LINKXERR_INTERNALFIFO) |
EDMA_IE_LINKCTLRXERR(EDMA_IE_LINKXERR_SATACRC) |
EDMA_IE_ESELFDIS);
if (mode == ncq)
reg |= EDMA_IE_EDEVERR;
MVSATA_EDMA_WRITE_4(mvport, EDMA_IEM, reg);
reg = MVSATA_EDMA_READ_4(mvport, EDMA_HC);
reg &= ~EDMA_IE_EDEVERR;
if (mode != ncq)
reg |= EDMA_IE_EDEVERR;
MVSATA_EDMA_WRITE_4(mvport, EDMA_HC, reg);
if (sc->sc_gen == gen2e) {
/*
* Clear FISWait4HostRdyEn[0] and [2].
* [0]: Device to Host FIS with <ERR> or <DF> bit set to 1.
* [2]: SDB FIS is received with <ERR> bit set to 1.
*/
reg = MVSATA_EDMA_READ_4(mvport, SATA_FISC);
reg &= ~(SATA_FISC_FISWAIT4HOSTRDYEN_B0 |
SATA_FISC_FISWAIT4HOSTRDYEN_B2);
MVSATA_EDMA_WRITE_4(mvport, SATA_FISC, reg);
}
mvport->port_edmamode = mode;
}
/*
* Generation dependent functions
*/
static void
mvsata_edma_setup_crqb(struct mvsata_port *mvport, int erqqip, int quetag,
struct ata_bio *ata_bio)
{
struct crqb *crqb;
bus_addr_t eprd_addr;
daddr_t blkno;
uint32_t rw;
uint8_t cmd, head;
int i;
const int drive =
mvport->port_ata_channel.ch_queue->active_xfer->c_drive;
eprd_addr = mvport->port_eprd_dmamap->dm_segs[0].ds_addr +
mvport->port_reqtbl[quetag].eprd_offset;
rw = (ata_bio->flags & ATA_READ) ? CRQB_CDIR_READ : CRQB_CDIR_WRITE;
cmd = (ata_bio->flags & ATA_READ) ? WDCC_READDMA : WDCC_WRITEDMA;
if (ata_bio->flags & (ATA_LBA|ATA_LBA48)) {
head = WDSD_LBA;
} else {
head = 0;
}
blkno = ata_bio->blkno;
if (ata_bio->flags & ATA_LBA48)
cmd = atacmd_to48(cmd);
else {
head |= ((ata_bio->blkno >> 24) & 0xf);
blkno &= 0xffffff;
}
crqb = &mvport->port_crqb->crqb + erqqip;
crqb->cprdbl = htole32(eprd_addr & CRQB_CRQBL_EPRD_MASK);
crqb->cprdbh = htole32((eprd_addr >> 16) >> 16);
crqb->ctrlflg =
htole16(rw | CRQB_CHOSTQUETAG(quetag) | CRQB_CPMPORT(drive));
i = 0;
if (mvport->port_edmamode == dma) {
if (ata_bio->flags & ATA_LBA48)
crqb->atacommand[i++] = htole16(CRQB_ATACOMMAND(
CRQB_ATACOMMAND_SECTORCOUNT, ata_bio->nblks >> 8));
crqb->atacommand[i++] = htole16(CRQB_ATACOMMAND(
CRQB_ATACOMMAND_SECTORCOUNT, ata_bio->nblks));
} else { /* ncq/queued */
/*
* XXXX: Oops, ata command is not correct. And, atabus layer
* has not been supported yet now.
* Queued DMA read/write.
* read/write FPDMAQueued.
*/
if (ata_bio->flags & ATA_LBA48)
crqb->atacommand[i++] = htole16(CRQB_ATACOMMAND(
CRQB_ATACOMMAND_FEATURES, ata_bio->nblks >> 8));
crqb->atacommand[i++] = htole16(CRQB_ATACOMMAND(
CRQB_ATACOMMAND_FEATURES, ata_bio->nblks));
crqb->atacommand[i++] = htole16(CRQB_ATACOMMAND(
CRQB_ATACOMMAND_SECTORCOUNT, quetag << 3));
}
if (ata_bio->flags & ATA_LBA48) {
crqb->atacommand[i++] = htole16(CRQB_ATACOMMAND(
CRQB_ATACOMMAND_LBALOW, blkno >> 24));
crqb->atacommand[i++] = htole16(CRQB_ATACOMMAND(
CRQB_ATACOMMAND_LBAMID, blkno >> 32));
crqb->atacommand[i++] = htole16(CRQB_ATACOMMAND(
CRQB_ATACOMMAND_LBAHIGH, blkno >> 40));
}
crqb->atacommand[i++] =
htole16(CRQB_ATACOMMAND(CRQB_ATACOMMAND_LBALOW, blkno));
crqb->atacommand[i++] =
htole16(CRQB_ATACOMMAND(CRQB_ATACOMMAND_LBAMID, blkno >> 8));
crqb->atacommand[i++] =
htole16(CRQB_ATACOMMAND(CRQB_ATACOMMAND_LBAHIGH, blkno >> 16));
crqb->atacommand[i++] =
htole16(CRQB_ATACOMMAND(CRQB_ATACOMMAND_DEVICE, head));
crqb->atacommand[i++] = htole16(
CRQB_ATACOMMAND(CRQB_ATACOMMAND_COMMAND, cmd) |
CRQB_ATACOMMAND_LAST);
}
#endif
static uint32_t
mvsata_read_preamps_gen1(struct mvsata_port *mvport)
{
struct mvsata_hc *hc = mvport->port_hc;
uint32_t reg;
reg = MVSATA_HC_READ_4(hc, SATAHC_I_PHYMODE(mvport->port));
/*
* [12:11] : pre
* [7:5] : amps
*/
return reg & 0x000018e0;
}
static void
mvsata_fix_phy_gen1(struct mvsata_port *mvport)
{
struct mvsata_softc *sc = device_private(MVSATA_DEV2(mvport));
struct mvsata_hc *mvhc = mvport->port_hc;
uint32_t reg;
int port = mvport->port, fix_apm_sq = 0;
if (sc->sc_model == PCI_PRODUCT_MARVELL_88SX5080) {
if (sc->sc_rev == 0x01)
fix_apm_sq = 1;
} else {
if (sc->sc_rev == 0x00)
fix_apm_sq = 1;
}
if (fix_apm_sq) {
/*
* Disable auto-power management
* 88SX50xx FEr SATA#12
*/
reg = MVSATA_HC_READ_4(mvhc, SATAHC_I_LTMODE(port));
reg |= (1 << 19);
MVSATA_HC_WRITE_4(mvhc, SATAHC_I_LTMODE(port), reg);
/*
* Fix squelch threshold
* 88SX50xx FEr SATA#9
*/
reg = MVSATA_HC_READ_4(mvhc, SATAHC_I_PHYCONTROL(port));
reg &= ~0x3;
reg |= 0x1;
MVSATA_HC_WRITE_4(mvhc, SATAHC_I_PHYCONTROL(port), reg);
}
/* Revert values of pre-emphasis and signal amps to the saved ones */
reg = MVSATA_HC_READ_4(mvhc, SATAHC_I_PHYMODE(port));
reg &= ~0x000018e0; /* pre and amps mask */
reg |= mvport->_fix_phy_param.pre_amps;
MVSATA_HC_WRITE_4(mvhc, SATAHC_I_PHYMODE(port), reg);
}
static void
mvsata_devconn_gen1(struct mvsata_port *mvport)
{
struct mvsata_softc *sc = device_private(MVSATA_DEV2(mvport));
/* Fix for 88SX50xx FEr SATA#2 */
mvport->_fix_phy_param._fix_phy(mvport);
/* If disk is connected, then enable the activity LED */
if (sc->sc_rev == 0x03) {
/* XXXXX */
}
}
static uint32_t
mvsata_read_preamps_gen2(struct mvsata_port *mvport)
{
uint32_t reg;
reg = MVSATA_EDMA_READ_4(mvport, SATA_PHYM2);
/*
* [10:8] : amps
* [7:5] : pre
*/
return reg & 0x000007e0;
}
static void
mvsata_fix_phy_gen2(struct mvsata_port *mvport)
{
struct mvsata_softc *sc = device_private(MVSATA_DEV2(mvport));
uint32_t reg;
if ((sc->sc_gen == gen2 && sc->sc_rev == 0x07) ||
sc->sc_gen == gen2e) {
/*
* Fix for
* 88SX60X1 FEr SATA #23
* 88SX6042/88SX7042 FEr SATA #23
* 88F5182 FEr #SATA-S13
* 88F5082 FEr #SATA-S13
*/
reg = MVSATA_EDMA_READ_4(mvport, SATA_PHYM2);
reg &= ~(1 << 16);
reg |= (1 << 31);
MVSATA_EDMA_WRITE_4(mvport, SATA_PHYM2, reg);
delay(200);
reg = MVSATA_EDMA_READ_4(mvport, SATA_PHYM2);
reg &= ~((1 << 16) | (1 << 31));
MVSATA_EDMA_WRITE_4(mvport, SATA_PHYM2, reg);
delay(200);
}
/* Fix values in PHY Mode 3 Register.*/
reg = MVSATA_EDMA_READ_4(mvport, SATA_PHYM3);
reg &= ~0x7F900000;
reg |= 0x2A800000;
/* Implement Guidline 88F5182, 88F5082, 88F6082 (GL# SATA-S11) */
if (sc->sc_model == PCI_PRODUCT_MARVELL_88F5082 ||
sc->sc_model == PCI_PRODUCT_MARVELL_88F5182 ||
sc->sc_model == PCI_PRODUCT_MARVELL_88F6082)
reg &= ~0x0000001c;
MVSATA_EDMA_WRITE_4(mvport, SATA_PHYM3, reg);
/*
* Fix values in PHY Mode 4 Register.
* 88SX60x1 FEr SATA#10
* 88F5182 GL #SATA-S10
* 88F5082 GL #SATA-S10
*/
if ((sc->sc_gen == gen2 && sc->sc_rev == 0x07) ||
sc->sc_gen == gen2e) {
uint32_t tmp = 0;
/* 88SX60x1 FEr SATA #13 */
if (sc->sc_gen == 2 && sc->sc_rev == 0x07)
tmp = MVSATA_EDMA_READ_4(mvport, SATA_PHYM3);
reg = MVSATA_EDMA_READ_4(mvport, SATA_PHYM4);
reg |= (1 << 0);
reg &= ~(1 << 1);
/* PHY Mode 4 Register of Gen IIE has some restriction */
if (sc->sc_gen == gen2e) {
reg &= ~0x5de3fffc;
reg |= (1 << 2);
}
MVSATA_EDMA_WRITE_4(mvport, SATA_PHYM4, reg);
/* 88SX60x1 FEr SATA #13 */
if (sc->sc_gen == 2 && sc->sc_rev == 0x07)
MVSATA_EDMA_WRITE_4(mvport, SATA_PHYM3, tmp);
}
/* Revert values of pre-emphasis and signal amps to the saved ones */
reg = MVSATA_EDMA_READ_4(mvport, SATA_PHYM2);
reg &= ~0x000007e0; /* pre and amps mask */
reg |= mvport->_fix_phy_param.pre_amps;
reg &= ~(1 << 16);
if (sc->sc_gen == gen2e) {
/*
* according to mvSata 3.6.1, some IIE values are fixed.
* some reserved fields must be written with fixed values.
*/
reg &= ~0xC30FF01F;
reg |= 0x0000900F;
}
MVSATA_EDMA_WRITE_4(mvport, SATA_PHYM2, reg);
}
#ifndef MVSATA_WITHOUTDMA
static void
mvsata_edma_setup_crqb_gen2e(struct mvsata_port *mvport, int erqqip, int quetag,
struct ata_bio *ata_bio)
{
struct crqb_gen2e *crqb;
bus_addr_t eprd_addr;
daddr_t blkno;
uint32_t ctrlflg, rw;
uint8_t cmd, head;
const int drive =
mvport->port_ata_channel.ch_queue->active_xfer->c_drive;
eprd_addr = mvport->port_eprd_dmamap->dm_segs[0].ds_addr +
mvport->port_reqtbl[quetag].eprd_offset;
rw = (ata_bio->flags & ATA_READ) ? CRQB_CDIR_READ : CRQB_CDIR_WRITE;
ctrlflg = (rw | CRQB_CDEVICEQUETAG(0) | CRQB_CPMPORT(drive) |
CRQB_CPRDMODE_EPRD | CRQB_CHOSTQUETAG_GEN2(quetag));
cmd = (ata_bio->flags & ATA_READ) ? WDCC_READDMA : WDCC_WRITEDMA;
if (ata_bio->flags & (ATA_LBA|ATA_LBA48)) {
head = WDSD_LBA;
} else {
head = 0;
}
blkno = ata_bio->blkno;
if (ata_bio->flags & ATA_LBA48)
cmd = atacmd_to48(cmd);
else {
head |= ((ata_bio->blkno >> 24) & 0xf);
blkno &= 0xffffff;
}
crqb = &mvport->port_crqb->crqb_gen2e + erqqip;
crqb->cprdbl = htole32(eprd_addr & CRQB_CRQBL_EPRD_MASK);
crqb->cprdbh = htole32((eprd_addr >> 16) >> 16);
crqb->ctrlflg = htole32(ctrlflg);
if (mvport->port_edmamode == dma) {
crqb->atacommand[0] = htole32(cmd << 16);
crqb->atacommand[1] = htole32((blkno & 0xffffff) | head << 24);
crqb->atacommand[2] = htole32(((blkno >> 24) & 0xffffff));
crqb->atacommand[3] = htole32(ata_bio->nblks & 0xffff);
} else { /* ncq/queued */
/*
* XXXX: Oops, ata command is not correct. And, atabus layer
* has not been supported yet now.
* Queued DMA read/write.
* read/write FPDMAQueued.
*/
crqb->atacommand[0] = htole32(
(cmd << 16) | ((ata_bio->nblks & 0xff) << 24));
crqb->atacommand[1] = htole32((blkno & 0xffffff) | head << 24);
crqb->atacommand[2] = htole32(((blkno >> 24) & 0xffffff) |
((ata_bio->nblks >> 8) & 0xff));
crqb->atacommand[3] = htole32(ata_bio->nblks & 0xffff);
crqb->atacommand[3] = htole32(quetag << 3);
}
}
#ifdef MVSATA_DEBUG
#define MVSATA_DEBUG_PRINT(type, size, n, p) \
do { \
int _i; \
u_char *_p = (p); \
\
printf(#type "(%d)", (n)); \
for (_i = 0; _i < (size); _i++, _p++) { \
if (_i % 16 == 0) \
printf("\n "); \
printf(" %02x", *_p); \
} \
printf("\n"); \
} while (0 /* CONSTCOND */)
static void
mvsata_print_crqb(struct mvsata_port *mvport, int n)
{
MVSATA_DEBUG_PRINT(crqb, sizeof(union mvsata_crqb),
n, (u_char *)(mvport->port_crqb + n));
}
static void
mvsata_print_crpb(struct mvsata_port *mvport, int n)
{
MVSATA_DEBUG_PRINT(crpb, sizeof(struct crpb),
n, (u_char *)(mvport->port_crpb + n));
}
static void
mvsata_print_eprd(struct mvsata_port *mvport, int n)
{
struct eprd *eprd;
int i = 0;
eprd = mvport->port_reqtbl[n].eprd;
while (1 /*CONSTCOND*/) {
MVSATA_DEBUG_PRINT(eprd, sizeof(struct eprd),
i, (u_char *)eprd);
if (eprd->eot & EPRD_EOT)
break;
eprd++;
i++;
}
}
#endif
#endif