aboutsummaryrefslogtreecommitdiff
path: root/sys/dev/ata/chipsets
diff options
context:
space:
mode:
authorAlexander Motin <mav@FreeBSD.org>2009-03-30 22:18:38 +0000
committerAlexander Motin <mav@FreeBSD.org>2009-03-30 22:18:38 +0000
commit9cf4fe2ebe5e7790a18b2b27f5c68b2b08ee34b7 (patch)
tree8b14f8d516ed7f249e9c59ba2ec27c475ea3a397 /sys/dev/ata/chipsets
parentf8ee854304a256737ebe10a3c9b928e5a95c2ab6 (diff)
downloadsrc-9cf4fe2ebe5e7790a18b2b27f5c68b2b08ee34b7.tar.gz
src-9cf4fe2ebe5e7790a18b2b27f5c68b2b08ee34b7.zip
Integrate user/mav/ata branch:
Add ch_suspend/ch_resume methods for PCI controllers and implement them for AHCI. Refactor AHCI channel initialization according to it. Fix Port Multipliers operation. It is far from perfect yet, but works now. Tested with JMicron JMB363 AHCI + SiI 3726 PMP pair. Previous version was also tested with SiI 4726 PMP. Hardware sponsored by: Vitsch Electronics / VEHosting.nl
Notes
Notes: svn path=/head/; revision=190581
Diffstat (limited to 'sys/dev/ata/chipsets')
-rw-r--r--sys/dev/ata/chipsets/ata-ahci.c159
-rw-r--r--sys/dev/ata/chipsets/ata-intel.c2
-rw-r--r--sys/dev/ata/chipsets/ata-jmicron.c28
-rw-r--r--sys/dev/ata/chipsets/ata-marvell.c2
-rw-r--r--sys/dev/ata/chipsets/ata-nvidia.c2
-rw-r--r--sys/dev/ata/chipsets/ata-promise.c4
-rw-r--r--sys/dev/ata/chipsets/ata-siliconimage.c4
-rw-r--r--sys/dev/ata/chipsets/ata-sis.c2
-rw-r--r--sys/dev/ata/chipsets/ata-via.c2
9 files changed, 155 insertions, 50 deletions
diff --git a/sys/dev/ata/chipsets/ata-ahci.c b/sys/dev/ata/chipsets/ata-ahci.c
index 991d2ad22f8d..9e7813263978 100644
--- a/sys/dev/ata/chipsets/ata-ahci.c
+++ b/sys/dev/ata/chipsets/ata-ahci.c
@@ -59,10 +59,16 @@ static int ata_ahci_begin_transaction(struct ata_request *request);
static int ata_ahci_end_transaction(struct ata_request *request);
static int ata_ahci_pm_read(device_t dev, int port, int reg, u_int32_t *result);
static int ata_ahci_pm_write(device_t dev, int port, int reg, u_int32_t result);
+static int ata_ahci_hardreset(device_t dev, int port, uint32_t *signature);
static u_int32_t ata_ahci_softreset(device_t dev, int port);
static void ata_ahci_dmasetprd(void *xsc, bus_dma_segment_t *segs, int nsegs, int error);
static int ata_ahci_setup_fis(struct ata_ahci_cmd_tab *ctp, struct ata_request *equest);
static void ata_ahci_dmainit(device_t dev);
+static void ata_ahci_start(device_t dev);
+static void ata_ahci_stop(device_t dev);
+static void ata_ahci_clo(device_t dev);
+static void ata_ahci_start_fr(device_t dev);
+static void ata_ahci_stop_fr(device_t dev);
/*
* AHCI v1.x compliant SATA chipset support functions
@@ -131,6 +137,8 @@ ata_ahci_chipinit(device_t dev)
ctlr->reset = ata_ahci_reset;
ctlr->ch_attach = ata_ahci_ch_attach;
ctlr->ch_detach = ata_ahci_ch_detach;
+ ctlr->ch_suspend = ata_ahci_ch_suspend;
+ ctlr->ch_resume = ata_ahci_ch_resume;
ctlr->setmode = ata_sata_setmode;
ctlr->suspend = ata_ahci_suspend;
ctlr->resume = ata_ahci_ctlr_reset;
@@ -192,7 +200,6 @@ ata_ahci_suspend(device_t dev)
return 0;
}
-
int
ata_ahci_ch_attach(device_t dev)
{
@@ -220,12 +227,22 @@ ata_ahci_ch_attach(device_t dev)
ch->hw.pm_read = ata_ahci_pm_read;
ch->hw.pm_write = ata_ahci_pm_write;
+ ata_ahci_ch_resume(dev);
return 0;
}
int
ata_ahci_ch_detach(device_t dev)
{
+
+ ata_ahci_ch_suspend(dev);
+ ata_dmafini(dev);
+ return (0);
+}
+
+int
+ata_ahci_ch_suspend(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 << 7;
@@ -233,6 +250,8 @@ ata_ahci_ch_detach(device_t dev)
/* Disable port interrupts. */
ATA_OUTL(ctlr->r_res2, ATA_AHCI_P_IE + offset, 0);
/* Reset command register. */
+ ata_ahci_stop(dev);
+ ata_ahci_stop_fr(dev);
ATA_OUTL(ctlr->r_res2, ATA_AHCI_P_CMD + offset, 0);
/* Allow everything including partial and slumber modes. */
@@ -243,7 +262,35 @@ ata_ahci_ch_detach(device_t dev)
/* Disable PHY. */
ATA_IDX_OUTL(ch, ATA_SCONTROL, ATA_SC_DET_DISABLE);
- ata_dmafini(dev);
+ return (0);
+}
+
+int
+ata_ahci_ch_resume(device_t dev)
+{
+ struct ata_pci_controller *ctlr = device_get_softc(device_get_parent(dev));
+ struct ata_channel *ch = device_get_softc(dev);
+ uint64_t work;
+ int offset = ch->unit << 7;
+
+ /* Disable port interrupts */
+ ATA_OUTL(ctlr->r_res2, ATA_AHCI_P_IE + offset, 0);
+
+ /* setup work areas */
+ work = ch->dma.work_bus + ATA_AHCI_CL_OFFSET;
+ ATA_OUTL(ctlr->r_res2, ATA_AHCI_P_CLB + offset, work & 0xffffffff);
+ ATA_OUTL(ctlr->r_res2, ATA_AHCI_P_CLBU + offset, work >> 32);
+
+ work = ch->dma.work_bus + ATA_AHCI_FB_OFFSET;
+ ATA_OUTL(ctlr->r_res2, ATA_AHCI_P_FB + offset, work & 0xffffffff);
+ ATA_OUTL(ctlr->r_res2, ATA_AHCI_P_FBU + offset, work >> 32);
+
+ /* activate the channel and power/spin up device */
+ ATA_OUTL(ctlr->r_res2, ATA_AHCI_P_CMD + offset,
+ (ATA_AHCI_P_CMD_ACTIVE | ATA_AHCI_P_CMD_POD | ATA_AHCI_P_CMD_SUD));
+ ata_ahci_start_fr(dev);
+ ata_ahci_start(dev);
+
return (0);
}
@@ -366,9 +413,6 @@ ata_ahci_begin_transaction(struct ata_request *request)
ATA_INL(ctlr->r_res2, ATA_AHCI_P_CMD + offset) &
~ATA_AHCI_P_CMD_ATAPI);
- /* set PM port to address */
- //ATA_OUTL(ctlr->r_res2, ATA_AHCI_P_FBS + offset, (port << 8) | 0x00000001);
-
/* issue command to controller */
ATA_OUTL(ctlr->r_res2, ATA_AHCI_P_CI + offset, (1 << request->tag));
@@ -465,9 +509,6 @@ ata_ahci_issue_cmd(device_t dev, u_int16_t flags, int timeout)
clp->bytecount = 0;
clp->cmd_table_phys = htole64(ch->dma.work_bus + ATA_AHCI_CT_OFFSET);
- /* set PM port */
- //ATA_OUTL(ctlr->r_res2, ATA_AHCI_P_FBS + offset, (port << 8) | 0x00000001);
-
/* issue command to controller */
ATA_OUTL(ctlr->r_res2, ATA_AHCI_P_CI + offset, 1);
@@ -480,7 +521,7 @@ ata_ahci_issue_cmd(device_t dev, u_int16_t flags, int timeout)
/* clear interrupts */
ATA_OUTL(ctlr->r_res2, ATA_AHCI_P_IS + offset,
- ATA_INL(ctlr->r_res2, ATA_AHCI_P_IS + offset));
+ ATA_INL(ctlr->r_res2, ATA_AHCI_P_IS + offset));
if (timeout && (count >= timeout)) {
if (bootverbose) {
@@ -559,7 +600,7 @@ ata_ahci_stop(device_t dev)
/* kill off all activity on this channel */
cmd = ATA_INL(ctlr->r_res2, ATA_AHCI_P_CMD + offset);
ATA_OUTL(ctlr->r_res2, ATA_AHCI_P_CMD + offset,
- cmd & ~(ATA_AHCI_P_CMD_FRE | ATA_AHCI_P_CMD_ST));
+ cmd & ~ATA_AHCI_P_CMD_ST);
/* XXX SOS this is not entirely wrong */
timeout = 0;
@@ -617,10 +658,47 @@ ata_ahci_start(device_t dev)
/* start operations on this channel */
cmd = ATA_INL(ctlr->r_res2, ATA_AHCI_P_CMD + offset);
ATA_OUTL(ctlr->r_res2, ATA_AHCI_P_CMD + offset,
- cmd | (ATA_AHCI_P_CMD_FRE | ATA_AHCI_P_CMD_ST) |
+ cmd | ATA_AHCI_P_CMD_ST |
(ch->devices & ATA_PORTMULTIPLIER ? ATA_AHCI_P_CMD_PMA : 0));
}
+static void
+ata_ahci_stop_fr(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 cmd;
+ int offset = ch->unit << 7;
+ int timeout;
+
+ /* kill off all activity on this channel */
+ cmd = ATA_INL(ctlr->r_res2, ATA_AHCI_P_CMD + offset);
+ ATA_OUTL(ctlr->r_res2, ATA_AHCI_P_CMD + offset, cmd & ~ATA_AHCI_P_CMD_FRE);
+
+ timeout = 0;
+ do {
+ DELAY(1000);
+ if (timeout++ > 1000) {
+ device_printf(dev, "stopping AHCI FR engine failed\n");
+ break;
+ }
+ }
+ while (ATA_INL(ctlr->r_res2, ATA_AHCI_P_CMD + offset) & ATA_AHCI_P_CMD_FR);
+}
+
+static void
+ata_ahci_start_fr(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 cmd;
+ int offset = ch->unit << 7;
+
+ /* start FIS reception on this channel */
+ cmd = ATA_INL(ctlr->r_res2, ATA_AHCI_P_CMD + offset);
+ ATA_OUTL(ctlr->r_res2, ATA_AHCI_P_CMD + offset, cmd | ATA_AHCI_P_CMD_FRE);
+}
+
static int
ata_ahci_wait_ready(device_t dev, int t)
{
@@ -628,13 +706,14 @@ ata_ahci_wait_ready(device_t dev, int t)
struct ata_channel *ch = device_get_softc(dev);
int offset = ch->unit << 7;
int timeout = 0;
+ uint32_t val;
- while (ATA_INL(ctlr->r_res2, ATA_AHCI_P_TFD + offset) &
+ while ((val = ATA_INL(ctlr->r_res2, ATA_AHCI_P_TFD + offset)) &
(ATA_S_BUSY | ATA_S_DRQ)) {
DELAY(1000);
if (timeout++ > t) {
- device_printf(dev, "port is not ready (timeout %dms)\n", t);
- return (-1);
+ device_printf(dev, "port is not ready (timeout %dms) tfd = %08x\n", t, val);
+ return (EBUSY);
}
}
if (bootverbose)
@@ -642,6 +721,28 @@ ata_ahci_wait_ready(device_t dev, int t)
return (0);
}
+static int
+ata_ahci_hardreset(device_t dev, int port, uint32_t *signature)
+{
+ struct ata_pci_controller *ctlr = device_get_softc(device_get_parent(dev));
+ struct ata_channel *ch = device_get_softc(dev);
+ int offset = ch->unit << 7;
+
+ *signature = 0xffffffff;
+ ata_ahci_stop(dev);
+ /* Reset port */
+ if (!ata_sata_phy_reset(dev, port, 0))
+ return (ENOENT);
+ /* Wait for clearing busy status. */
+ if (ata_ahci_wait_ready(dev, 10000)) {
+ device_printf(dev, "hardware reset timeout\n");
+ return (EBUSY);
+ }
+ *signature = ATA_INL(ctlr->r_res2, ATA_AHCI_P_SIG + offset);
+ ata_ahci_start(dev);
+ return (0);
+}
+
static u_int32_t
ata_ahci_softreset(device_t dev, int port)
{
@@ -679,9 +780,9 @@ ata_ahci_softreset(device_t dev, int port)
ctp->cfis[1] = port & 0x0f;
//ctp->cfis[7] = ATA_D_LBA | ATA_D_IBM;
ctp->cfis[15] = ATA_A_4BIT;
- ata_ahci_issue_cmd(dev, 0, 1000);
+ ata_ahci_issue_cmd(dev, 0, 3000);
- if (ata_ahci_wait_ready(dev, 1000)) {
+ if (ata_ahci_wait_ready(dev, 0)) {
device_printf(dev, "software reset clear timeout\n");
return (-1);
}
@@ -694,7 +795,6 @@ ata_ahci_reset(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 work;
u_int32_t signature;
int offset = ch->unit << 7;
@@ -704,25 +804,7 @@ ata_ahci_reset(device_t dev)
/* Disable port interrupts */
ATA_OUTL(ctlr->r_res2, ATA_AHCI_P_IE + offset, 0);
- /* setup work areas */
- work = ch->dma.work_bus + ATA_AHCI_CL_OFFSET;
- ATA_OUTL(ctlr->r_res2, ATA_AHCI_P_CLB + offset, work & 0xffffffff);
- ATA_OUTL(ctlr->r_res2, ATA_AHCI_P_CLBU + offset, work >> 32);
-
- work = ch->dma.work_bus + ATA_AHCI_FB_OFFSET;
- ATA_OUTL(ctlr->r_res2, ATA_AHCI_P_FB + offset, work & 0xffffffff);
- ATA_OUTL(ctlr->r_res2, ATA_AHCI_P_FBU + offset, work >> 32);
-
- /* activate the channel and power/spin up device */
- ATA_OUTL(ctlr->r_res2, ATA_AHCI_P_CMD + offset,
- (ATA_AHCI_P_CMD_ACTIVE | ATA_AHCI_P_CMD_POD | ATA_AHCI_P_CMD_SUD));
-
- ata_ahci_stop(dev);
-
- /* enable FIS based switching */
- //ATA_OUTL(ctlr->r_res2, ATA_AHCI_P_FBS + offset, 0x00000003);
-
- if (!ata_sata_phy_reset(dev)) {
+ if (ata_ahci_hardreset(dev, -1, &signature)) {
if (bootverbose)
device_printf(dev, "AHCI reset done: phy reset found no device\n");
ch->devices = 0;
@@ -733,8 +815,6 @@ ata_ahci_reset(device_t dev)
return;
}
- ata_ahci_start(dev);
-
/* enable wanted port interrupts */
ATA_OUTL(ctlr->r_res2, ATA_AHCI_P_IE + offset,
(ATA_AHCI_P_IX_CPD | ATA_AHCI_P_IX_TFE | ATA_AHCI_P_IX_HBF |
@@ -743,9 +823,6 @@ ata_ahci_reset(device_t dev)
ATA_AHCI_P_IX_UF | ATA_AHCI_P_IX_SDB | ATA_AHCI_P_IX_DS |
ATA_AHCI_P_IX_PS | ATA_AHCI_P_IX_DHR));
- /* Wait for initial TFD from device. */
- ata_ahci_wait_ready(dev, 10000);
-
/* only probe for PortMultiplier if HW has support */
if (ATA_INL(ctlr->r_res2, ATA_AHCI_CAP) & ATA_AHCI_CAP_SPM) {
signature = ata_ahci_softreset(dev, ATA_PM);
diff --git a/sys/dev/ata/chipsets/ata-intel.c b/sys/dev/ata/chipsets/ata-intel.c
index 8bd2f03e1e8b..cae1778bc28e 100644
--- a/sys/dev/ata/chipsets/ata-intel.c
+++ b/sys/dev/ata/chipsets/ata-intel.c
@@ -517,7 +517,7 @@ ata_intel_31244_tf_write(struct ata_request *request)
static void
ata_intel_31244_reset(device_t dev)
{
- if (ata_sata_phy_reset(dev))
+ if (ata_sata_phy_reset(dev, -1, 1))
ata_generic_reset(dev);
}
diff --git a/sys/dev/ata/chipsets/ata-jmicron.c b/sys/dev/ata/chipsets/ata-jmicron.c
index 301b99b533a1..b1e7aef4f67c 100644
--- a/sys/dev/ata/chipsets/ata-jmicron.c
+++ b/sys/dev/ata/chipsets/ata-jmicron.c
@@ -55,6 +55,8 @@ __FBSDID("$FreeBSD$");
static int ata_jmicron_chipinit(device_t dev);
static int ata_jmicron_ch_attach(device_t dev);
static int ata_jmicron_ch_detach(device_t dev);
+static int ata_jmicron_ch_suspend(device_t dev);
+static int ata_jmicron_ch_resume(device_t dev);
static void ata_jmicron_reset(device_t dev);
static void ata_jmicron_setmode(device_t dev, int mode);
@@ -127,6 +129,8 @@ ata_jmicron_chipinit(device_t dev)
ctlr->ch_attach = ata_jmicron_ch_attach;
ctlr->ch_detach = ata_jmicron_ch_detach;
+ ctlr->ch_suspend = ata_jmicron_ch_suspend;
+ ctlr->ch_resume = ata_jmicron_ch_resume;
ctlr->reset = ata_jmicron_reset;
ctlr->setmode = ata_jmicron_setmode;
@@ -173,6 +177,30 @@ ata_jmicron_ch_detach(device_t dev)
return (error);
}
+static int
+ata_jmicron_ch_suspend(device_t dev)
+{
+ struct ata_pci_controller *ctlr = device_get_softc(device_get_parent(dev));
+ struct ata_channel *ch = device_get_softc(dev);
+ int error = 0;
+
+ if (ch->unit < ctlr->chip->cfg1)
+ error = ata_ahci_ch_suspend(dev);
+ return error;
+}
+
+static int
+ata_jmicron_ch_resume(device_t dev)
+{
+ struct ata_pci_controller *ctlr = device_get_softc(device_get_parent(dev));
+ struct ata_channel *ch = device_get_softc(dev);
+ int error = 0;
+
+ if (ch->unit < ctlr->chip->cfg1)
+ error = ata_ahci_ch_resume(dev);
+ return (error);
+}
+
static void
ata_jmicron_reset(device_t dev)
{
diff --git a/sys/dev/ata/chipsets/ata-marvell.c b/sys/dev/ata/chipsets/ata-marvell.c
index a0fa48527f9a..361bc5b2dcbc 100644
--- a/sys/dev/ata/chipsets/ata-marvell.c
+++ b/sys/dev/ata/chipsets/ata-marvell.c
@@ -503,7 +503,7 @@ ata_marvell_edma_reset(device_t dev)
ATA_OUTL(ctlr->r_res1, 0x0200c + ATA_MV_EDMA_BASE(ch), ~0x0);
/* enable channel and test for devices */
- if (ata_sata_phy_reset(dev))
+ if (ata_sata_phy_reset(dev, -1, 1))
ata_generic_reset(dev);
/* enable EDMA machinery */
diff --git a/sys/dev/ata/chipsets/ata-nvidia.c b/sys/dev/ata/chipsets/ata-nvidia.c
index 7e03afae2fe9..9e1de81d0eef 100644
--- a/sys/dev/ata/chipsets/ata-nvidia.c
+++ b/sys/dev/ata/chipsets/ata-nvidia.c
@@ -249,7 +249,7 @@ ata_nvidia_status(device_t dev)
static void
ata_nvidia_reset(device_t dev)
{
- if (ata_sata_phy_reset(dev))
+ if (ata_sata_phy_reset(dev, -1, 1))
ata_generic_reset(dev);
}
diff --git a/sys/dev/ata/chipsets/ata-promise.c b/sys/dev/ata/chipsets/ata-promise.c
index 385653514da8..6b34596fa01d 100644
--- a/sys/dev/ata/chipsets/ata-promise.c
+++ b/sys/dev/ata/chipsets/ata-promise.c
@@ -769,7 +769,7 @@ ata_promise_mio_reset(device_t dev)
if ((ctlr->chip->cfg2 == PR_SATA) ||
((ctlr->chip->cfg2 == PR_CMBO) && (ch->unit < 2))) {
- if (ata_sata_phy_reset(dev))
+ if (ata_sata_phy_reset(dev, -1, 1))
ata_generic_reset(dev);
/* reset and enable plug/unplug intr */
@@ -805,7 +805,7 @@ ata_promise_mio_reset(device_t dev)
(ATA_INL(ctlr->r_res2, 0x414 + (ch->unit << 8)) &
~0x00000003) | 0x00000001);
- if (ata_sata_phy_reset(dev)) {
+ if (ata_sata_phy_reset(dev, -1, 1)) {
u_int32_t signature = ch->hw.softreset(dev, ATA_PM);
if (1 | bootverbose)
diff --git a/sys/dev/ata/chipsets/ata-siliconimage.c b/sys/dev/ata/chipsets/ata-siliconimage.c
index f5093c7c597b..b163276c72c4 100644
--- a/sys/dev/ata/chipsets/ata-siliconimage.c
+++ b/sys/dev/ata/chipsets/ata-siliconimage.c
@@ -380,7 +380,7 @@ ata_sii_status(device_t dev)
static void
ata_sii_reset(device_t dev)
{
- if (ata_sata_phy_reset(dev))
+ if (ata_sata_phy_reset(dev, -1, 1))
ata_generic_reset(dev);
}
@@ -832,7 +832,7 @@ ata_siiprb_reset(device_t dev)
}
/* reset phy */
- if (!ata_sata_phy_reset(dev)) {
+ if (!ata_sata_phy_reset(dev, -1, 1)) {
if (bootverbose)
device_printf(dev, "phy reset found no device\n");
ch->devices = 0;
diff --git a/sys/dev/ata/chipsets/ata-sis.c b/sys/dev/ata/chipsets/ata-sis.c
index 1d9dac72e658..8e02ee904453 100644
--- a/sys/dev/ata/chipsets/ata-sis.c
+++ b/sys/dev/ata/chipsets/ata-sis.c
@@ -226,7 +226,7 @@ ata_sis_ch_attach(device_t dev)
static void
ata_sis_reset(device_t dev)
{
- if (ata_sata_phy_reset(dev))
+ if (ata_sata_phy_reset(dev, -1, 1))
ata_generic_reset(dev);
}
diff --git a/sys/dev/ata/chipsets/ata-via.c b/sys/dev/ata/chipsets/ata-via.c
index 4530adfd76a3..525ca2fb571d 100644
--- a/sys/dev/ata/chipsets/ata-via.c
+++ b/sys/dev/ata/chipsets/ata-via.c
@@ -269,7 +269,7 @@ ata_via_reset(device_t dev)
if ((ctlr->chip->cfg2 & VIABAR) && (ch->unit > 1))
ata_generic_reset(dev);
else
- if (ata_sata_phy_reset(dev))
+ if (ata_sata_phy_reset(dev, -1, 1))
ata_generic_reset(dev);
}