[PATCH][RFT] libata AHCI reset speed-up, improvements

From: Jeff Garzik
Date: Tue Sep 26 2006 - 22:57:10 EST



This patch, diff'd against 2.6.18-git5 (or later), attempts the
following improvements:

* Speed up reset, by polling rather than unconditionally sleeping
for one seconds.
* Save the Ports-Implemented register across controller resets.
* Verify that AHCI mode was truly enabled.

I'm quite interested in hearing people's test feedback on this patch.
On my own ICH7 machine, it introduces a "softreset failed" error, but
EH recovers nicely and nonetheless configures everything successfully.
Probably just a slow ATAPI device, but I may have exposed a latent
problem in the driver that was hidden until now by the ssleep(1).

diff --git a/drivers/ata/ahci.c b/drivers/ata/ahci.c
index 1aabc81..1def9c9 100644
--- a/drivers/ata/ahci.c
+++ b/drivers/ata/ahci.c
@@ -40,6 +40,7 @@ #include <linux/blkdev.h>
#include <linux/delay.h>
#include <linux/interrupt.h>
#include <linux/sched.h>
+#include <linux/jiffies.h>
#include <linux/dma-mapping.h>
#include <linux/device.h>
#include <scsi/scsi_host.h>
@@ -593,11 +594,13 @@ static int ahci_deinit_port(void __iomem

static int ahci_reset_controller(void __iomem *mmio, struct pci_dev *pdev)
{
- u32 cap_save, tmp;
+ u32 cap_save, impl_save, tmp;
+ unsigned long end_time;

cap_save = readl(mmio + HOST_CAP);
cap_save &= ( (1<<28) | (1<<17) );
cap_save |= (1 << 27);
+ impl_save = readl(mmio + HOST_PORTS_IMPL);

/* global controller reset */
tmp = readl(mmio + HOST_CTL);
@@ -609,19 +612,36 @@ static int ahci_reset_controller(void __
/* reset must complete within 1 second, or
* the hardware should be considered fried.
*/
- ssleep(1);
+ end_time = jiffies + HZ;
+ while (1) {
+ msleep(100);
+
+ tmp = readl(mmio + HOST_CTL);
+ if (!(tmp & HOST_RESET))
+ break;
+ if (time_after(jiffies, end_time))
+ break;
+
+ }

- tmp = readl(mmio + HOST_CTL);
if (tmp & HOST_RESET) {
dev_printk(KERN_ERR, &pdev->dev,
"controller reset failed (0x%x)\n", tmp);
return -EIO;
}

+ /* turn on AHCI mode */
writel(HOST_AHCI_EN, mmio + HOST_CTL);
(void) readl(mmio + HOST_CTL); /* flush */
+
+ /*
+ * these write-once registers are normally cleared
+ * on reset. Restore BIOS values... which we HOPE
+ * were present before reset.
+ */
+
writel(cap_save, mmio + HOST_CAP);
- writel(0xf, mmio + HOST_PORTS_IMPL);
+ writel(impl_save, mmio + HOST_PORTS_IMPL);
(void) readl(mmio + HOST_PORTS_IMPL); /* flush */

if (pdev->vendor == PCI_VENDOR_ID_INTEL) {
@@ -633,6 +653,19 @@ static int ahci_reset_controller(void __
pci_write_config_word(pdev, 0x92, tmp16);
}

+ /*
+ * if AHCI failed to enable, AHCI function may not
+ * be present in the chipset. This is true of certain
+ * ICHx chipsets, which export the SATA phy registers
+ * via the AHCI memory space, but don't support full AHCI.
+ */
+ tmp = readl(mmio + HOST_CTL);
+ if (!(tmp & HOST_AHCI_EN)) {
+ dev_printk(KERN_ERR, &pdev->dev,
+ "AHCI enable failed (0x%x)\n", tmp);
+ return -ENODEV;
+ }
+
return 0;
}

-
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/