13014ca04a
If you just config KERNEL as usual there should be no apparent changes, you'll get all chipset support code compiled in. However there is now a way to only compile in code for chipsets needed on a pr vendor basis. ATA now has the following "device" entries: atacore: ATA core functionality, always needed for any ATA setup atacard: CARDBUS support atacbus: PC98 cbus support ataisa: ISA bus support atapci: PCI bus support only generic chipset support. ataahci: AHCI support, also pulled in by some vendor modules. ataacard, ataacerlabs, ataadaptec, ataamd, ataati, atacenatek, atacypress, atacyrix, atahighpoint, ataintel, ataite, atajmicron, atamarvell, atamicron, atanational, atanetcell, atanvidia, atapromise, ataserverworks, atasiliconimage, atasis, atavia; Vendor support, ie atavia for VIA chipsets atadisk: ATA disk driver ataraid: ATA softraid driver atapicd: ATAPI cd/dvd driver atapifd: ATAPI floppy/flashdisk driver atapist: ATAPI tape driver atausb: ATA<>USB bridge atapicam: ATA<>CAM bridge This makes it possible to config a kernel with just VIA chipset support by having the following ATA lines in the kernel config file: device atacore device atapci device atavia And then you need the atadisk, atapicd etc lines in there just as usual. If you use ATA as modules loaded at boot there is few changes except the rename of the "ata" module to "atacore", things looks just as usual. However under atapci you now have a whole bunch of vendor specific drivers, that you can kldload individually depending on you needs. Drivers have the same names as used in the kernel config explained above.
891 lines
27 KiB
C
891 lines
27 KiB
C
/*-
|
|
* Copyright (c) 1998 - 2008 Søren Schmidt <sos@FreeBSD.org>
|
|
* 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,
|
|
* without modification, immediately at the beginning of the file.
|
|
* 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>
|
|
__FBSDID("$FreeBSD$");
|
|
|
|
#include "opt_ata.h"
|
|
#include <sys/param.h>
|
|
#include <sys/module.h>
|
|
#include <sys/systm.h>
|
|
#include <sys/kernel.h>
|
|
#include <sys/ata.h>
|
|
#include <sys/bus.h>
|
|
#include <sys/endian.h>
|
|
#include <sys/malloc.h>
|
|
#include <sys/lock.h>
|
|
#include <sys/mutex.h>
|
|
#include <sys/sema.h>
|
|
#include <sys/taskqueue.h>
|
|
#include <vm/uma.h>
|
|
#include <machine/stdarg.h>
|
|
#include <machine/resource.h>
|
|
#include <machine/bus.h>
|
|
#include <sys/rman.h>
|
|
#include <dev/pci/pcivar.h>
|
|
#include <dev/pci/pcireg.h>
|
|
#include <dev/ata/ata-all.h>
|
|
#include <dev/ata/ata-pci.h>
|
|
#include <ata_if.h>
|
|
|
|
/* local prototypes */
|
|
static int ata_cmd_allocate(device_t dev);
|
|
static int ata_cmd_status(device_t dev);
|
|
static void ata_cmd_setmode(device_t dev, int mode);
|
|
static int ata_sii_allocate(device_t dev);
|
|
static int ata_sii_status(device_t dev);
|
|
static void ata_sii_reset(device_t dev);
|
|
static void ata_sii_setmode(device_t dev, int mode);
|
|
static int ata_siiprb_allocate(device_t dev);
|
|
static int ata_siiprb_status(device_t dev);
|
|
static int ata_siiprb_begin_transaction(struct ata_request *request);
|
|
static int ata_siiprb_end_transaction(struct ata_request *request);
|
|
static int ata_siiprb_pm_read(device_t dev, int port, int reg, u_int32_t *result);
|
|
static int ata_siiprb_pm_write(device_t dev, int port, int reg, u_int32_t result);
|
|
static u_int32_t ata_siiprb_softreset(device_t dev, int port);
|
|
static void ata_siiprb_reset(device_t dev);
|
|
static void ata_siiprb_dmasetprd(void *xsc, bus_dma_segment_t *segs, int nsegs, int error);
|
|
static void ata_siiprb_dmainit(device_t dev);
|
|
|
|
/* misc defines */
|
|
#define SII_MEMIO 1
|
|
#define SII_PRBIO 2
|
|
#define SII_INTR 0x01
|
|
#define SII_SETCLK 0x02
|
|
#define SII_BUG 0x04
|
|
#define SII_4CH 0x08
|
|
|
|
|
|
/*
|
|
* Silicon Image Inc. (SiI) (former CMD) chipset support functions
|
|
*/
|
|
static int
|
|
ata_sii_probe(device_t dev)
|
|
{
|
|
struct ata_pci_controller *ctlr = device_get_softc(dev);
|
|
static struct ata_chip_id ids[] =
|
|
{{ ATA_SII3114, 0x00, SII_MEMIO, SII_4CH, ATA_SA150, "3114" },
|
|
{ ATA_SII3512, 0x02, SII_MEMIO, 0, ATA_SA150, "3512" },
|
|
{ ATA_SII3112, 0x02, SII_MEMIO, 0, ATA_SA150, "3112" },
|
|
{ ATA_SII3112_1, 0x02, SII_MEMIO, 0, ATA_SA150, "3112" },
|
|
{ ATA_SII3512, 0x00, SII_MEMIO, SII_BUG, ATA_SA150, "3512" },
|
|
{ ATA_SII3112, 0x00, SII_MEMIO, SII_BUG, ATA_SA150, "3112" },
|
|
{ ATA_SII3112_1, 0x00, SII_MEMIO, SII_BUG, ATA_SA150, "3112" },
|
|
{ ATA_SII3124, 0x00, SII_PRBIO, SII_4CH, ATA_SA300, "3124" },
|
|
{ ATA_SII3132, 0x00, SII_PRBIO, 0, ATA_SA300, "3132" },
|
|
{ ATA_SII3132_1, 0x00, SII_PRBIO, 0, ATA_SA300, "3132" },
|
|
{ ATA_SII0680, 0x00, SII_MEMIO, SII_SETCLK, ATA_UDMA6, "680" },
|
|
{ ATA_CMD649, 0x00, 0, SII_INTR, ATA_UDMA5, "(CMD) 649" },
|
|
{ ATA_CMD648, 0x00, 0, SII_INTR, ATA_UDMA4, "(CMD) 648" },
|
|
{ ATA_CMD646, 0x07, 0, 0, ATA_UDMA2, "(CMD) 646U2" },
|
|
{ ATA_CMD646, 0x00, 0, 0, ATA_WDMA2, "(CMD) 646" },
|
|
{ 0, 0, 0, 0, 0, 0}};
|
|
|
|
if (pci_get_vendor(dev) != ATA_SILICON_IMAGE_ID)
|
|
return ENXIO;
|
|
|
|
if (!(ctlr->chip = ata_match_chip(dev, ids)))
|
|
return ENXIO;
|
|
|
|
ata_set_desc(dev);
|
|
ctlr->chipinit = ata_sii_chipinit;
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
ata_sii_chipinit(device_t dev)
|
|
{
|
|
struct ata_pci_controller *ctlr = device_get_softc(dev);
|
|
|
|
if (ata_setup_interrupt(dev, ata_generic_intr))
|
|
return ENXIO;
|
|
|
|
switch (ctlr->chip->cfg1) {
|
|
case SII_PRBIO:
|
|
ctlr->r_type1 = SYS_RES_MEMORY;
|
|
ctlr->r_rid1 = PCIR_BAR(0);
|
|
if (!(ctlr->r_res1 = bus_alloc_resource_any(dev, ctlr->r_type1,
|
|
&ctlr->r_rid1, RF_ACTIVE)))
|
|
return ENXIO;
|
|
|
|
ctlr->r_rid2 = PCIR_BAR(2);
|
|
ctlr->r_type2 = SYS_RES_MEMORY;
|
|
if (!(ctlr->r_res2 = bus_alloc_resource_any(dev, ctlr->r_type2,
|
|
&ctlr->r_rid2, RF_ACTIVE))){
|
|
bus_release_resource(dev, ctlr->r_type1, ctlr->r_rid1,ctlr->r_res1);
|
|
return ENXIO;
|
|
}
|
|
ctlr->allocate = ata_siiprb_allocate;
|
|
ctlr->reset = ata_siiprb_reset;
|
|
ctlr->dmainit = ata_siiprb_dmainit;
|
|
ctlr->setmode = ata_sata_setmode;
|
|
ctlr->channels = (ctlr->chip->cfg2 == SII_4CH) ? 4 : 2;
|
|
|
|
/* reset controller */
|
|
ATA_OUTL(ctlr->r_res1, 0x0040, 0x80000000);
|
|
DELAY(10000);
|
|
ATA_OUTL(ctlr->r_res1, 0x0040, 0x0000000f);
|
|
|
|
/* enable PCI interrupt */
|
|
pci_write_config(dev, PCIR_COMMAND,
|
|
pci_read_config(dev, PCIR_COMMAND, 2) & ~0x0400, 2);
|
|
break;
|
|
|
|
case SII_MEMIO:
|
|
ctlr->r_type2 = SYS_RES_MEMORY;
|
|
ctlr->r_rid2 = PCIR_BAR(5);
|
|
if (!(ctlr->r_res2 = bus_alloc_resource_any(dev, ctlr->r_type2,
|
|
&ctlr->r_rid2, RF_ACTIVE))){
|
|
if (ctlr->chip->chipid != ATA_SII0680 ||
|
|
(pci_read_config(dev, 0x8a, 1) & 1))
|
|
return ENXIO;
|
|
}
|
|
|
|
if (ctlr->chip->cfg2 & SII_SETCLK) {
|
|
if ((pci_read_config(dev, 0x8a, 1) & 0x30) != 0x10)
|
|
pci_write_config(dev, 0x8a,
|
|
(pci_read_config(dev, 0x8a, 1) & 0xcf)|0x10,1);
|
|
if ((pci_read_config(dev, 0x8a, 1) & 0x30) != 0x10)
|
|
device_printf(dev, "%s could not set ATA133 clock\n",
|
|
ctlr->chip->text);
|
|
}
|
|
|
|
/* if we have 4 channels enable the second set */
|
|
if (ctlr->chip->cfg2 & SII_4CH) {
|
|
ATA_OUTL(ctlr->r_res2, 0x0200, 0x00000002);
|
|
ctlr->channels = 4;
|
|
}
|
|
|
|
/* dont block interrupts from any channel */
|
|
pci_write_config(dev, 0x48,
|
|
(pci_read_config(dev, 0x48, 4) & ~0x03c00000), 4);
|
|
|
|
/* enable PCI interrupt as BIOS might not */
|
|
pci_write_config(dev, 0x8a, (pci_read_config(dev, 0x8a, 1) & 0x3f), 1);
|
|
|
|
if (ctlr->r_res2)
|
|
ctlr->allocate = ata_sii_allocate;
|
|
|
|
if (ctlr->chip->max_dma >= ATA_SA150) {
|
|
ctlr->reset = ata_sii_reset;
|
|
ctlr->setmode = ata_sata_setmode;
|
|
}
|
|
else
|
|
ctlr->setmode = ata_sii_setmode;
|
|
break;
|
|
|
|
default:
|
|
if ((pci_read_config(dev, 0x51, 1) & 0x08) != 0x08) {
|
|
device_printf(dev, "HW has secondary channel disabled\n");
|
|
ctlr->channels = 1;
|
|
}
|
|
|
|
/* enable interrupt as BIOS might not */
|
|
pci_write_config(dev, 0x71, 0x01, 1);
|
|
|
|
ctlr->allocate = ata_cmd_allocate;
|
|
ctlr->setmode = ata_cmd_setmode;
|
|
break;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
ata_cmd_allocate(device_t dev)
|
|
{
|
|
struct ata_pci_controller *ctlr = device_get_softc(device_get_parent(dev));
|
|
struct ata_channel *ch = device_get_softc(dev);
|
|
|
|
/* setup the usual register normal pci style */
|
|
if (ata_pci_allocate(dev))
|
|
return ENXIO;
|
|
|
|
if (ctlr->chip->cfg2 & SII_INTR)
|
|
ch->hw.status = ata_cmd_status;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
ata_cmd_status(device_t dev)
|
|
{
|
|
struct ata_channel *ch = device_get_softc(dev);
|
|
u_int8_t reg71;
|
|
|
|
if (((reg71 = pci_read_config(device_get_parent(dev), 0x71, 1)) &
|
|
(ch->unit ? 0x08 : 0x04))) {
|
|
pci_write_config(device_get_parent(dev), 0x71,
|
|
reg71 & ~(ch->unit ? 0x04 : 0x08), 1);
|
|
return ata_pci_status(dev);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static void
|
|
ata_cmd_setmode(device_t dev, int mode)
|
|
{
|
|
device_t gparent = GRANDPARENT(dev);
|
|
struct ata_pci_controller *ctlr = device_get_softc(gparent);
|
|
struct ata_channel *ch = device_get_softc(device_get_parent(dev));
|
|
struct ata_device *atadev = device_get_softc(dev);
|
|
int devno = (ch->unit << 1) + atadev->unit;
|
|
int error;
|
|
|
|
mode = ata_limit_mode(dev, mode, ctlr->chip->max_dma);
|
|
|
|
mode = ata_check_80pin(dev, mode);
|
|
|
|
error = ata_controlcmd(dev, ATA_SETFEATURES, ATA_SF_SETXFER, 0, mode);
|
|
|
|
if (bootverbose)
|
|
device_printf(dev, "%ssetting %s on %s chip\n",
|
|
(error) ? "FAILURE " : "",
|
|
ata_mode2str(mode), ctlr->chip->text);
|
|
if (!error) {
|
|
int treg = 0x54 + ((devno < 3) ? (devno << 1) : 7);
|
|
int ureg = ch->unit ? 0x7b : 0x73;
|
|
|
|
if (mode >= ATA_UDMA0) {
|
|
int udmatimings[][2] = { { 0x31, 0xc2 }, { 0x21, 0x82 },
|
|
{ 0x11, 0x42 }, { 0x25, 0x8a },
|
|
{ 0x15, 0x4a }, { 0x05, 0x0a } };
|
|
|
|
u_int8_t umode = pci_read_config(gparent, ureg, 1);
|
|
|
|
umode &= ~(atadev->unit == ATA_MASTER ? 0x35 : 0xca);
|
|
umode |= udmatimings[mode & ATA_MODE_MASK][atadev->unit];
|
|
pci_write_config(gparent, ureg, umode, 1);
|
|
}
|
|
else if (mode >= ATA_WDMA0) {
|
|
int dmatimings[] = { 0x87, 0x32, 0x3f };
|
|
|
|
pci_write_config(gparent, treg, dmatimings[mode & ATA_MODE_MASK],1);
|
|
pci_write_config(gparent, ureg,
|
|
pci_read_config(gparent, ureg, 1) &
|
|
~(atadev->unit == ATA_MASTER ? 0x35 : 0xca), 1);
|
|
}
|
|
else {
|
|
int piotimings[] = { 0xa9, 0x57, 0x44, 0x32, 0x3f };
|
|
pci_write_config(gparent, treg,
|
|
piotimings[(mode & ATA_MODE_MASK) - ATA_PIO0], 1);
|
|
pci_write_config(gparent, ureg,
|
|
pci_read_config(gparent, ureg, 1) &
|
|
~(atadev->unit == ATA_MASTER ? 0x35 : 0xca), 1);
|
|
}
|
|
atadev->mode = mode;
|
|
}
|
|
}
|
|
|
|
static int
|
|
ata_sii_allocate(device_t dev)
|
|
{
|
|
struct ata_pci_controller *ctlr = device_get_softc(device_get_parent(dev));
|
|
struct ata_channel *ch = device_get_softc(dev);
|
|
int unit01 = (ch->unit & 1), unit10 = (ch->unit & 2);
|
|
int i;
|
|
|
|
for (i = ATA_DATA; i <= ATA_COMMAND; i++) {
|
|
ch->r_io[i].res = ctlr->r_res2;
|
|
ch->r_io[i].offset = 0x80 + i + (unit01 << 6) + (unit10 << 8);
|
|
}
|
|
ch->r_io[ATA_CONTROL].res = ctlr->r_res2;
|
|
ch->r_io[ATA_CONTROL].offset = 0x8a + (unit01 << 6) + (unit10 << 8);
|
|
ch->r_io[ATA_IDX_ADDR].res = ctlr->r_res2;
|
|
ata_default_registers(dev);
|
|
|
|
ch->r_io[ATA_BMCMD_PORT].res = ctlr->r_res2;
|
|
ch->r_io[ATA_BMCMD_PORT].offset = 0x00 + (unit01 << 3) + (unit10 << 8);
|
|
ch->r_io[ATA_BMSTAT_PORT].res = ctlr->r_res2;
|
|
ch->r_io[ATA_BMSTAT_PORT].offset = 0x02 + (unit01 << 3) + (unit10 << 8);
|
|
ch->r_io[ATA_BMDTP_PORT].res = ctlr->r_res2;
|
|
ch->r_io[ATA_BMDTP_PORT].offset = 0x04 + (unit01 << 3) + (unit10 << 8);
|
|
|
|
if (ctlr->chip->max_dma >= ATA_SA150) {
|
|
ch->r_io[ATA_SSTATUS].res = ctlr->r_res2;
|
|
ch->r_io[ATA_SSTATUS].offset = 0x104 + (unit01 << 7) + (unit10 << 8);
|
|
ch->r_io[ATA_SERROR].res = ctlr->r_res2;
|
|
ch->r_io[ATA_SERROR].offset = 0x108 + (unit01 << 7) + (unit10 << 8);
|
|
ch->r_io[ATA_SCONTROL].res = ctlr->r_res2;
|
|
ch->r_io[ATA_SCONTROL].offset = 0x100 + (unit01 << 7) + (unit10 << 8);
|
|
ch->flags |= ATA_NO_SLAVE;
|
|
|
|
/* enable PHY state change interrupt */
|
|
ATA_OUTL(ctlr->r_res2, 0x148 + (unit01 << 7) + (unit10 << 8),(1 << 16));
|
|
}
|
|
|
|
if (ctlr->chip->cfg2 & SII_BUG) {
|
|
/* work around errata in early chips */
|
|
ch->dma.boundary = 8192;
|
|
ch->dma.segsize = 15 * DEV_BSIZE;
|
|
}
|
|
|
|
ata_pci_hw(dev);
|
|
ch->hw.status = ata_sii_status;
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
ata_sii_status(device_t dev)
|
|
{
|
|
struct ata_pci_controller *ctlr = device_get_softc(device_get_parent(dev));
|
|
struct ata_channel *ch = device_get_softc(dev);
|
|
int offset0 = ((ch->unit & 1) << 3) + ((ch->unit & 2) << 8);
|
|
int offset1 = ((ch->unit & 1) << 6) + ((ch->unit & 2) << 8);
|
|
|
|
/* do we have any PHY events ? */
|
|
if (ctlr->chip->max_dma >= ATA_SA150 &&
|
|
(ATA_INL(ctlr->r_res2, 0x10 + offset0) & 0x00000010))
|
|
ata_sata_phy_check_events(dev);
|
|
|
|
if (ATA_INL(ctlr->r_res2, 0xa0 + offset1) & 0x00000800)
|
|
return ata_pci_status(dev);
|
|
else
|
|
return 0;
|
|
}
|
|
|
|
static void
|
|
ata_sii_reset(device_t dev)
|
|
{
|
|
if (ata_sata_phy_reset(dev))
|
|
ata_generic_reset(dev);
|
|
}
|
|
|
|
static void
|
|
ata_sii_setmode(device_t dev, int mode)
|
|
{
|
|
device_t gparent = GRANDPARENT(dev);
|
|
struct ata_pci_controller *ctlr = device_get_softc(gparent);
|
|
struct ata_channel *ch = device_get_softc(device_get_parent(dev));
|
|
struct ata_device *atadev = device_get_softc(dev);
|
|
int rego = (ch->unit << 4) + (atadev->unit << 1);
|
|
int mreg = ch->unit ? 0x84 : 0x80;
|
|
int mask = 0x03 << (atadev->unit << 2);
|
|
int mval = pci_read_config(gparent, mreg, 1) & ~mask;
|
|
int error;
|
|
|
|
mode = ata_limit_mode(dev, mode, ctlr->chip->max_dma);
|
|
|
|
if (ctlr->chip->cfg2 & SII_SETCLK) {
|
|
if (mode > ATA_UDMA2 && (pci_read_config(gparent, 0x79, 1) &
|
|
(ch->unit ? 0x02 : 0x01))) {
|
|
ata_print_cable(dev, "controller");
|
|
mode = ATA_UDMA2;
|
|
}
|
|
}
|
|
else
|
|
mode = ata_check_80pin(dev, mode);
|
|
|
|
error = ata_controlcmd(dev, ATA_SETFEATURES, ATA_SF_SETXFER, 0, mode);
|
|
|
|
if (bootverbose)
|
|
device_printf(dev, "%ssetting %s on %s chip\n",
|
|
(error) ? "FAILURE " : "",
|
|
ata_mode2str(mode), ctlr->chip->text);
|
|
if (error)
|
|
return;
|
|
|
|
if (mode >= ATA_UDMA0) {
|
|
u_int8_t udmatimings[] = { 0xf, 0xb, 0x7, 0x5, 0x3, 0x2, 0x1 };
|
|
u_int8_t ureg = 0xac + rego;
|
|
|
|
pci_write_config(gparent, mreg,
|
|
mval | (0x03 << (atadev->unit << 2)), 1);
|
|
pci_write_config(gparent, ureg,
|
|
(pci_read_config(gparent, ureg, 1) & ~0x3f) |
|
|
udmatimings[mode & ATA_MODE_MASK], 1);
|
|
|
|
}
|
|
else if (mode >= ATA_WDMA0) {
|
|
u_int8_t dreg = 0xa8 + rego;
|
|
u_int16_t dmatimings[] = { 0x2208, 0x10c2, 0x10c1 };
|
|
|
|
pci_write_config(gparent, mreg,
|
|
mval | (0x02 << (atadev->unit << 2)), 1);
|
|
pci_write_config(gparent, dreg, dmatimings[mode & ATA_MODE_MASK], 2);
|
|
|
|
}
|
|
else {
|
|
u_int8_t preg = 0xa4 + rego;
|
|
u_int16_t piotimings[] = { 0x328a, 0x2283, 0x1104, 0x10c3, 0x10c1 };
|
|
|
|
pci_write_config(gparent, mreg,
|
|
mval | (0x01 << (atadev->unit << 2)), 1);
|
|
pci_write_config(gparent, preg, piotimings[mode & ATA_MODE_MASK], 2);
|
|
}
|
|
atadev->mode = mode;
|
|
}
|
|
|
|
|
|
struct ata_siiprb_dma_prdentry {
|
|
u_int64_t addr;
|
|
u_int32_t count;
|
|
u_int32_t control;
|
|
} __packed;
|
|
|
|
#define ATA_SIIPRB_DMA_ENTRIES 125
|
|
struct ata_siiprb_ata_command {
|
|
struct ata_siiprb_dma_prdentry prd[ATA_SIIPRB_DMA_ENTRIES];
|
|
} __packed;
|
|
|
|
struct ata_siiprb_atapi_command {
|
|
u_int8_t ccb[16];
|
|
struct ata_siiprb_dma_prdentry prd[ATA_SIIPRB_DMA_ENTRIES];
|
|
} __packed;
|
|
|
|
struct ata_siiprb_command {
|
|
u_int16_t control;
|
|
u_int16_t protocol_override;
|
|
u_int32_t transfer_count;
|
|
u_int8_t fis[24];
|
|
union {
|
|
struct ata_siiprb_ata_command ata;
|
|
struct ata_siiprb_atapi_command atapi;
|
|
} u;
|
|
} __packed;
|
|
|
|
static int
|
|
ata_siiprb_allocate(device_t dev)
|
|
{
|
|
struct ata_pci_controller *ctlr = device_get_softc(device_get_parent(dev));
|
|
struct ata_channel *ch = device_get_softc(dev);
|
|
int offset = ch->unit * 0x2000;
|
|
|
|
/* set the SATA resources */
|
|
ch->r_io[ATA_SSTATUS].res = ctlr->r_res2;
|
|
ch->r_io[ATA_SSTATUS].offset = 0x1f04 + offset;
|
|
ch->r_io[ATA_SERROR].res = ctlr->r_res2;
|
|
ch->r_io[ATA_SERROR].offset = 0x1f08 + offset;
|
|
ch->r_io[ATA_SCONTROL].res = ctlr->r_res2;
|
|
ch->r_io[ATA_SCONTROL].offset = 0x1f00 + offset;
|
|
ch->r_io[ATA_SACTIVE].res = ctlr->r_res2;
|
|
ch->r_io[ATA_SACTIVE].offset = 0x1f0c + offset;
|
|
|
|
ch->hw.status = ata_siiprb_status;
|
|
ch->hw.begin_transaction = ata_siiprb_begin_transaction;
|
|
ch->hw.end_transaction = ata_siiprb_end_transaction;
|
|
ch->hw.command = NULL; /* not used here */
|
|
ch->hw.softreset = ata_siiprb_softreset;
|
|
ch->hw.pm_read = ata_siiprb_pm_read;
|
|
ch->hw.pm_write = ata_siiprb_pm_write;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
ata_siiprb_status(device_t dev)
|
|
{
|
|
struct ata_pci_controller *ctlr = device_get_softc(device_get_parent(dev));
|
|
struct ata_channel *ch = device_get_softc(dev);
|
|
u_int32_t action = ATA_INL(ctlr->r_res1, 0x0044);
|
|
int offset = ch->unit * 0x2000;
|
|
|
|
if (action & (1 << ch->unit)) {
|
|
u_int32_t istatus = ATA_INL(ctlr->r_res2, 0x1008 + offset);
|
|
|
|
/* do we have any PHY events ? */
|
|
ata_sata_phy_check_events(dev);
|
|
|
|
/* clear interrupt(s) */
|
|
ATA_OUTL(ctlr->r_res2, 0x1008 + offset, istatus);
|
|
|
|
/* do we have any device action ? */
|
|
return (istatus & 0x00000003);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
ata_siiprb_begin_transaction(struct ata_request *request)
|
|
{
|
|
struct ata_pci_controller *ctlr=device_get_softc(GRANDPARENT(request->dev));
|
|
struct ata_channel *ch = device_get_softc(request->parent);
|
|
struct ata_siiprb_command *prb;
|
|
struct ata_siiprb_dma_prdentry *prd;
|
|
int offset = ch->unit * 0x2000;
|
|
u_int64_t prb_bus;
|
|
|
|
/* SOS XXX */
|
|
if (request->u.ata.command == ATA_DEVICE_RESET) {
|
|
request->result = 0;
|
|
return ATA_OP_FINISHED;
|
|
}
|
|
|
|
/* get a piece of the workspace for this request */
|
|
prb = (struct ata_siiprb_command *)
|
|
(ch->dma.work + (sizeof(struct ata_siiprb_command) * request->tag));
|
|
|
|
/* clear the prb structure */
|
|
bzero(prb, sizeof(struct ata_siiprb_command));
|
|
|
|
/* setup the FIS for this request */
|
|
if (!ata_request2fis_h2d(request, &prb->fis[0])) {
|
|
device_printf(request->dev, "setting up SATA FIS failed\n");
|
|
request->result = EIO;
|
|
return ATA_OP_FINISHED;
|
|
}
|
|
|
|
/* setup transfer type */
|
|
if (request->flags & ATA_R_ATAPI) {
|
|
struct ata_device *atadev = device_get_softc(request->dev);
|
|
|
|
bcopy(request->u.atapi.ccb, prb->u.atapi.ccb, 16);
|
|
if ((atadev->param.config & ATA_PROTO_MASK) == ATA_PROTO_ATAPI_12)
|
|
ATA_OUTL(ctlr->r_res2, 0x1004 + offset, 0x00000020);
|
|
else
|
|
ATA_OUTL(ctlr->r_res2, 0x1000 + offset, 0x00000020);
|
|
if (request->flags & ATA_R_READ)
|
|
prb->control = htole16(0x0010);
|
|
if (request->flags & ATA_R_WRITE)
|
|
prb->control = htole16(0x0020);
|
|
prd = &prb->u.atapi.prd[0];
|
|
}
|
|
else
|
|
prd = &prb->u.ata.prd[0];
|
|
|
|
/* if request moves data setup and load SG list */
|
|
if (request->flags & (ATA_R_READ | ATA_R_WRITE)) {
|
|
if (ch->dma.load(request, prd, NULL)) {
|
|
device_printf(request->dev, "setting up DMA failed\n");
|
|
request->result = EIO;
|
|
return ATA_OP_FINISHED;
|
|
}
|
|
}
|
|
|
|
/* activate the prb */
|
|
prb_bus = ch->dma.work_bus +
|
|
(sizeof(struct ata_siiprb_command) * request->tag);
|
|
ATA_OUTL(ctlr->r_res2,
|
|
0x1c00 + offset + (request->tag * sizeof(u_int64_t)), prb_bus);
|
|
ATA_OUTL(ctlr->r_res2,
|
|
0x1c04 + offset + (request->tag * sizeof(u_int64_t)), prb_bus>>32);
|
|
|
|
/* start the timeout */
|
|
callout_reset(&request->callout, request->timeout * hz,
|
|
(timeout_t*)ata_timeout, request);
|
|
return ATA_OP_CONTINUES;
|
|
}
|
|
|
|
static int
|
|
ata_siiprb_end_transaction(struct ata_request *request)
|
|
{
|
|
struct ata_pci_controller *ctlr=device_get_softc(GRANDPARENT(request->dev));
|
|
struct ata_channel *ch = device_get_softc(request->parent);
|
|
struct ata_siiprb_command *prb;
|
|
int offset = ch->unit * 0x2000;
|
|
int error, timeout;
|
|
|
|
/* kill the timeout */
|
|
callout_stop(&request->callout);
|
|
|
|
prb = (struct ata_siiprb_command *)
|
|
((u_int8_t *)rman_get_virtual(ctlr->r_res2)+(request->tag << 7)+offset);
|
|
|
|
/* any controller errors flagged ? */
|
|
if ((error = ATA_INL(ctlr->r_res2, 0x1024 + offset))) {
|
|
if (bootverbose)
|
|
printf("ata_siiprb_end_transaction %s error=%08x\n",
|
|
ata_cmd2str(request), error);
|
|
|
|
/* if device error status get details */
|
|
if (error == 1 || error == 2) {
|
|
request->status = prb->fis[2];
|
|
if (request->status & ATA_S_ERROR)
|
|
request->error = prb->fis[3];
|
|
}
|
|
|
|
/* SOS XXX handle other controller errors here */
|
|
|
|
/* initialize port */
|
|
ATA_OUTL(ctlr->r_res2, 0x1000 + offset, 0x00000004);
|
|
|
|
/* poll for port ready */
|
|
for (timeout = 0; timeout < 1000; timeout++) {
|
|
DELAY(1000);
|
|
if (ATA_INL(ctlr->r_res2, 0x1008 + offset) & 0x00040000)
|
|
break;
|
|
}
|
|
if (bootverbose) {
|
|
if (timeout >= 1000)
|
|
device_printf(ch->dev, "port initialize timeout\n");
|
|
else
|
|
device_printf(ch->dev, "port initialize time=%dms\n", timeout);
|
|
}
|
|
}
|
|
|
|
/* on control commands read back registers to the request struct */
|
|
if (request->flags & ATA_R_CONTROL) {
|
|
struct ata_device *atadev = device_get_softc(request->dev);
|
|
|
|
request->u.ata.count = prb->fis[12] | ((u_int16_t)prb->fis[13] << 8);
|
|
request->u.ata.lba = prb->fis[4] | ((u_int64_t)prb->fis[5] << 8) |
|
|
((u_int64_t)prb->fis[6] << 16);
|
|
if (atadev->flags & ATA_D_48BIT_ACTIVE)
|
|
request->u.ata.lba |= ((u_int64_t)prb->fis[8] << 24) |
|
|
((u_int64_t)prb->fis[9] << 32) |
|
|
((u_int64_t)prb->fis[10] << 40);
|
|
else
|
|
request->u.ata.lba |= ((u_int64_t)(prb->fis[7] & 0x0f) << 24);
|
|
}
|
|
|
|
/* update progress */
|
|
if (!(request->status & ATA_S_ERROR) && !(request->flags & ATA_R_TIMEOUT)) {
|
|
if (request->flags & ATA_R_READ)
|
|
request->donecount = prb->transfer_count;
|
|
else
|
|
request->donecount = request->bytecount;
|
|
}
|
|
|
|
/* release SG list etc */
|
|
ch->dma.unload(request);
|
|
|
|
return ATA_OP_FINISHED;
|
|
}
|
|
|
|
static int
|
|
ata_siiprb_issue_cmd(device_t dev)
|
|
{
|
|
struct ata_pci_controller *ctlr = device_get_softc(device_get_parent(dev));
|
|
struct ata_channel *ch = device_get_softc(dev);
|
|
u_int64_t prb_bus = ch->dma.work_bus;
|
|
u_int32_t status;
|
|
int offset = ch->unit * 0x2000;
|
|
int timeout;
|
|
|
|
/* issue command to chip */
|
|
ATA_OUTL(ctlr->r_res2, 0x1c00 + offset, prb_bus);
|
|
ATA_OUTL(ctlr->r_res2, 0x1c04 + offset, prb_bus >> 32);
|
|
|
|
/* poll for command finished */
|
|
for (timeout = 0; timeout < 10000; timeout++) {
|
|
DELAY(1000);
|
|
if ((status = ATA_INL(ctlr->r_res2, 0x1008 + offset)) & 0x00010000)
|
|
break;
|
|
}
|
|
// SOS XXX ATA_OUTL(ctlr->r_res2, 0x1008 + offset, 0x00010000);
|
|
ATA_OUTL(ctlr->r_res2, 0x1008 + offset, 0x08ff08ff);
|
|
|
|
if (timeout >= 1000)
|
|
return EIO;
|
|
|
|
if (bootverbose)
|
|
device_printf(dev, "siiprb_issue_cmd time=%dms status=%08x\n",
|
|
timeout, status);
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
ata_siiprb_pm_read(device_t dev, int port, int reg, u_int32_t *result)
|
|
{
|
|
struct ata_pci_controller *ctlr = device_get_softc(device_get_parent(dev));
|
|
struct ata_channel *ch = device_get_softc(dev);
|
|
struct ata_siiprb_command *prb = (struct ata_siiprb_command *)ch->dma.work;
|
|
int offset = ch->unit * 0x2000;
|
|
|
|
bzero(prb, sizeof(struct ata_siiprb_command));
|
|
prb->fis[0] = 0x27; /* host to device */
|
|
prb->fis[1] = 0x8f; /* command FIS to PM port */
|
|
prb->fis[2] = ATA_READ_PM;
|
|
prb->fis[3] = reg;
|
|
prb->fis[7] = port;
|
|
if (ata_siiprb_issue_cmd(dev)) {
|
|
device_printf(dev, "error reading PM port\n");
|
|
return EIO;
|
|
}
|
|
prb = (struct ata_siiprb_command *)
|
|
((u_int8_t *)rman_get_virtual(ctlr->r_res2) + offset);
|
|
*result = prb->fis[12]|(prb->fis[4]<<8)|(prb->fis[5]<<16)|(prb->fis[6]<<24);
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
ata_siiprb_pm_write(device_t dev, int port, int reg, u_int32_t value)
|
|
{
|
|
struct ata_pci_controller *ctlr = device_get_softc(device_get_parent(dev));
|
|
struct ata_channel *ch = device_get_softc(dev);
|
|
struct ata_siiprb_command *prb = (struct ata_siiprb_command *)ch->dma.work;
|
|
int offset = ch->unit * 0x2000;
|
|
|
|
bzero(prb, sizeof(struct ata_siiprb_command));
|
|
prb->fis[0] = 0x27; /* host to device */
|
|
prb->fis[1] = 0x8f; /* command FIS to PM port */
|
|
prb->fis[2] = ATA_WRITE_PM;
|
|
prb->fis[3] = reg;
|
|
prb->fis[7] = port;
|
|
prb->fis[12] = value & 0xff;
|
|
prb->fis[4] = (value >> 8) & 0xff;;
|
|
prb->fis[5] = (value >> 16) & 0xff;;
|
|
prb->fis[6] = (value >> 24) & 0xff;;
|
|
if (ata_siiprb_issue_cmd(dev)) {
|
|
device_printf(dev, "error writing PM port\n");
|
|
return ATA_E_ABORT;
|
|
}
|
|
prb = (struct ata_siiprb_command *)
|
|
((u_int8_t *)rman_get_virtual(ctlr->r_res2) + offset);
|
|
return prb->fis[3];
|
|
}
|
|
|
|
static u_int32_t
|
|
ata_siiprb_softreset(device_t dev, int port)
|
|
{
|
|
struct ata_pci_controller *ctlr = device_get_softc(device_get_parent(dev));
|
|
struct ata_channel *ch = device_get_softc(dev);
|
|
struct ata_siiprb_command *prb = (struct ata_siiprb_command *)ch->dma.work;
|
|
u_int32_t signature;
|
|
int offset = ch->unit * 0x2000;
|
|
|
|
/* setup the workspace for a soft reset command */
|
|
bzero(prb, sizeof(struct ata_siiprb_command));
|
|
prb->control = htole16(0x0080);
|
|
prb->fis[1] = port & 0x0f;
|
|
|
|
/* issue soft reset */
|
|
if (ata_siiprb_issue_cmd(dev))
|
|
return -1;
|
|
|
|
ata_udelay(150000);
|
|
|
|
/* get possible signature */
|
|
prb = (struct ata_siiprb_command *)
|
|
((u_int8_t *)rman_get_virtual(ctlr->r_res2) + offset);
|
|
signature=prb->fis[12]|(prb->fis[4]<<8)|(prb->fis[5]<<16)|(prb->fis[6]<<24);
|
|
|
|
/* clear error bits/interrupt */
|
|
ATA_IDX_OUTL(ch, ATA_SERROR, 0xffffffff);
|
|
|
|
return signature;
|
|
}
|
|
|
|
static void
|
|
ata_siiprb_reset(device_t dev)
|
|
{
|
|
struct ata_pci_controller *ctlr = device_get_softc(device_get_parent(dev));
|
|
struct ata_channel *ch = device_get_softc(dev);
|
|
int offset = ch->unit * 0x2000;
|
|
u_int32_t status, signature;
|
|
int timeout;
|
|
|
|
/* disable interrupts */
|
|
ATA_OUTL(ctlr->r_res2, 0x1014 + offset, 0x000000ff);
|
|
|
|
/* reset channel HW */
|
|
ATA_OUTL(ctlr->r_res2, 0x1000 + offset, 0x00000001);
|
|
DELAY(1000);
|
|
ATA_OUTL(ctlr->r_res2, 0x1004 + offset, 0x00000001);
|
|
DELAY(10000);
|
|
|
|
/* poll for channel ready */
|
|
for (timeout = 0; timeout < 1000; timeout++) {
|
|
if ((status = ATA_INL(ctlr->r_res2, 0x1008 + offset)) & 0x00040000)
|
|
break;
|
|
DELAY(1000);
|
|
}
|
|
|
|
if (bootverbose) {
|
|
if (timeout >= 1000)
|
|
device_printf(dev, "channel HW reset timeout\n");
|
|
else
|
|
device_printf(dev, "channel HW reset time=%dms\n", timeout);
|
|
}
|
|
|
|
/* reset phy */
|
|
if (!ata_sata_phy_reset(dev)) {
|
|
if (bootverbose)
|
|
device_printf(dev, "phy reset found no device\n");
|
|
ch->devices = 0;
|
|
goto finish;
|
|
}
|
|
|
|
/* issue soft reset */
|
|
signature = ata_siiprb_softreset(dev, ATA_PM);
|
|
if (bootverbose)
|
|
device_printf(dev, "SIGNATURE=%08x\n", signature);
|
|
|
|
/* figure out whats there */
|
|
switch (signature) {
|
|
case 0x00000101:
|
|
ch->devices = ATA_ATA_MASTER;
|
|
break;
|
|
case 0x96690101:
|
|
ch->devices = ATA_PORTMULTIPLIER;
|
|
ATA_OUTL(ctlr->r_res2, 0x1000 + offset, 0x2000); /* enable PM support */
|
|
//SOS XXX need to clear all PM status and interrupts!!!!
|
|
ata_pm_identify(dev);
|
|
break;
|
|
case 0xeb140101:
|
|
ch->devices = ATA_ATAPI_MASTER;
|
|
break;
|
|
default:
|
|
ch->devices = 0;
|
|
}
|
|
if (bootverbose)
|
|
device_printf(dev, "siiprb_reset devices=%08x\n", ch->devices);
|
|
|
|
finish:
|
|
/* clear interrupt(s) */
|
|
ATA_OUTL(ctlr->r_res2, 0x1008 + offset, 0x000008ff);
|
|
|
|
/* require explicit interrupt ack */
|
|
ATA_OUTL(ctlr->r_res2, 0x1000 + offset, 0x00000008);
|
|
|
|
/* 64bit mode */
|
|
ATA_OUTL(ctlr->r_res2, 0x1004 + offset, 0x00000400);
|
|
|
|
/* enable interrupts wanted */
|
|
ATA_OUTL(ctlr->r_res2, 0x1010 + offset, 0x000000ff);
|
|
}
|
|
|
|
static void
|
|
ata_siiprb_dmasetprd(void *xsc, bus_dma_segment_t *segs, int nsegs, int error)
|
|
{
|
|
struct ata_dmasetprd_args *args = xsc;
|
|
struct ata_siiprb_dma_prdentry *prd = args->dmatab;
|
|
int i;
|
|
|
|
if ((args->error = error))
|
|
return;
|
|
|
|
for (i = 0; i < nsegs; i++) {
|
|
prd[i].addr = htole64(segs[i].ds_addr);
|
|
prd[i].count = htole32(segs[i].ds_len);
|
|
}
|
|
prd[i - 1].control = htole32(ATA_DMA_EOT);
|
|
KASSERT(nsegs <= ATA_SIIPRB_DMA_ENTRIES,("too many DMA segment entries\n"));
|
|
args->nsegs = nsegs;
|
|
}
|
|
|
|
static void
|
|
ata_siiprb_dmainit(device_t dev)
|
|
{
|
|
struct ata_channel *ch = device_get_softc(dev);
|
|
|
|
ata_dmainit(dev);
|
|
/* note start and stop are not used here */
|
|
ch->dma.setprd = ata_siiprb_dmasetprd;
|
|
ch->dma.max_address = BUS_SPACE_MAXADDR;
|
|
}
|
|
|
|
ATA_DECLARE_DRIVER(ata_sii);
|