summaryrefslogtreecommitdiffstats
path: root/sys/dev/ata/chipsets/ata-via.c
diff options
context:
space:
mode:
authormav <mav@FreeBSD.org>2010-11-18 08:03:40 +0000
committermav <mav@FreeBSD.org>2010-11-18 08:03:40 +0000
commit1dd61e867fb5da1d946423693838d10f5397a258 (patch)
treeb0d720f685061be6201a1d9b7efec05ab9d707a2 /sys/dev/ata/chipsets/ata-via.c
parent6858b8bb3347117f293ed046ae5886415ce60bf2 (diff)
downloadFreeBSD-src-1dd61e867fb5da1d946423693838d10f5397a258.zip
FreeBSD-src-1dd61e867fb5da1d946423693838d10f5397a258.tar.gz
Some VIA SATA controllers provide access to non-standard SATA registers via
PCI config space. Use them to implement hot-plug and link speed reporting. Tested on ASRock PV530 board with VX900 chipset.
Diffstat (limited to 'sys/dev/ata/chipsets/ata-via.c')
-rw-r--r--sys/dev/ata/chipsets/ata-via.c132
1 files changed, 128 insertions, 4 deletions
diff --git a/sys/dev/ata/chipsets/ata-via.c b/sys/dev/ata/chipsets/ata-via.c
index 347cd26..ee8405b 100644
--- a/sys/dev/ata/chipsets/ata-via.c
+++ b/sys/dev/ata/chipsets/ata-via.c
@@ -63,6 +63,12 @@ static int ata_via_new_setmode(device_t dev, int target, int mode);
static int ata_via_sata_ch_attach(device_t dev);
static int ata_via_sata_getrev(device_t dev, int target);
static int ata_via_sata_setmode(device_t dev, int target, int mode);
+static void ata_via_sata_reset(device_t dev);
+static int ata_via_sata_scr_read(device_t dev, int port, int reg,
+ u_int32_t *result);
+static int ata_via_sata_scr_write(device_t dev, int port, int reg,
+ u_int32_t value);
+static int ata_via_sata_status(device_t dev);
/* misc defines */
#define VIA33 0
@@ -153,11 +159,12 @@ ata_via_chipinit(device_t dev)
if (ata_ahci_chipinit(dev) != ENXIO)
return (0);
}
- /* 2 SATA without SATA registers on first channel + 1 PATA on second */
+ /* 2 SATA with "SATA registers" at PCI config space + PATA on secondary */
if (ctlr->chip->cfg2 & VIASATA) {
ctlr->ch_attach = ata_via_sata_ch_attach;
ctlr->setmode = ata_via_sata_setmode;
ctlr->getrev = ata_via_sata_getrev;
+ ctlr->reset = ata_via_sata_reset;
return 0;
}
/* Legacy SATA/SATA+PATA with SATA registers in BAR(5). */
@@ -405,18 +412,30 @@ ata_via_sata_ch_attach(device_t dev)
if (ata_pci_ch_attach(dev))
return ENXIO;
- if (ch->unit == 0)
+ if (ch->unit == 0) {
+ ch->hw.status = ata_via_sata_status;
+ ch->hw.pm_read = ata_via_sata_scr_read;
+ ch->hw.pm_write = ata_via_sata_scr_write;
+ ch->flags |= ATA_PERIODIC_POLL;
ch->flags |= ATA_SATA;
+ ata_sata_scr_write(ch, 0, ATA_SERROR, 0xffffffff);
+ ata_sata_scr_write(ch, 1, ATA_SERROR, 0xffffffff);
+ }
return (0);
}
static int
ata_via_sata_getrev(device_t dev, int target)
{
+ device_t parent = device_get_parent(dev);
struct ata_channel *ch = device_get_softc(dev);
- if (ch->unit == 0)
- return (1);
+ if (ch->unit == 0) {
+ if (pci_read_config(parent, 0xa0 + target, 1) & 0x10)
+ return (2);
+ else
+ return (1);
+ }
return (0);
}
@@ -430,5 +449,110 @@ ata_via_sata_setmode(device_t dev, int target, int mode)
return (ata_via_old_setmode(dev, target, mode));
}
+static void
+ata_via_sata_reset(device_t dev)
+{
+ struct ata_channel *ch = device_get_softc(dev);
+ int devs;
+
+ if (ch->unit == 0) {
+ devs = ata_sata_phy_reset(dev, 0, 0);
+ DELAY(10000);
+ devs += ata_sata_phy_reset(dev, 1, 0);
+ } else
+ devs = 1;
+ if (devs)
+ ata_generic_reset(dev);
+}
+
+static int
+ata_via_sata_scr_read(device_t dev, int port, int reg, u_int32_t *result)
+{
+ struct ata_channel *ch;
+ device_t parent;
+ uint32_t val;
+
+ parent = device_get_parent(dev);
+ ch = device_get_softc(dev);
+ port = (port == 1) ? 1 : 0;
+ switch (reg) {
+ case ATA_SSTATUS:
+ val = pci_read_config(parent, 0xa0 + port, 1);
+ *result = val & 0x03;
+ if (*result != ATA_SS_DET_NO_DEVICE) {
+ if (val & 0x04)
+ *result |= ATA_SS_IPM_PARTIAL;
+ else if (val & 0x08)
+ *result |= ATA_SS_IPM_SLUMBER;
+ else
+ *result |= ATA_SS_IPM_ACTIVE;
+ if (val & 0x10)
+ *result |= ATA_SS_SPD_GEN2;
+ else
+ *result |= ATA_SS_SPD_GEN1;
+ }
+ break;
+ case ATA_SERROR:
+ *result = pci_read_config(parent, 0xa8 + port * 4, 4);
+ break;
+ case ATA_SCONTROL:
+ val = pci_read_config(parent, 0xa4 + port, 1);
+ *result = 0;
+ if (val & 0x01)
+ *result |= ATA_SC_DET_RESET;
+ if (val & 0x02)
+ *result |= ATA_SC_DET_DISABLE;
+ if (val & 0x04)
+ *result |= ATA_SC_IPM_DIS_PARTIAL;
+ if (val & 0x08)
+ *result |= ATA_SC_IPM_DIS_SLUMBER;
+ break;
+ default:
+ return (EINVAL);
+ }
+ return (0);
+}
+
+static int
+ata_via_sata_scr_write(device_t dev, int port, int reg, u_int32_t value)
+{
+ struct ata_channel *ch;
+ device_t parent;
+ uint32_t val;
+
+ parent = device_get_parent(dev);
+ ch = device_get_softc(dev);
+ port = (port == 1) ? 1 : 0;
+ switch (reg) {
+ case ATA_SERROR:
+ pci_write_config(parent, 0xa8 + port * 4, value, 4);
+ break;
+ case ATA_SCONTROL:
+ val = 0;
+ if (value & ATA_SC_DET_RESET)
+ val |= 0x01;
+ if (value & ATA_SC_DET_DISABLE)
+ val |= 0x02;
+ if (value & ATA_SC_IPM_DIS_PARTIAL)
+ val |= 0x04;
+ if (value & ATA_SC_IPM_DIS_SLUMBER)
+ val |= 0x08;
+ pci_write_config(parent, 0xa4 + port, val, 1);
+ break;
+ default:
+ return (EINVAL);
+ }
+ return (0);
+}
+
+static int
+ata_via_sata_status(device_t dev)
+{
+
+ ata_sata_phy_check_events(dev, 0);
+ ata_sata_phy_check_events(dev, 1);
+ return (ata_pci_status(dev));
+}
+
ATA_DECLARE_DRIVER(ata_via);
MODULE_DEPEND(ata_via, ata_ahci, 1, 1, 1);
OpenPOWER on IntegriCloud