summaryrefslogtreecommitdiffstats
path: root/drivers/ata
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/ata')
-rw-r--r--drivers/ata/ahci.c95
1 files changed, 72 insertions, 23 deletions
diff --git a/drivers/ata/ahci.c b/drivers/ata/ahci.c
index fd27227771b..c31b6636632 100644
--- a/drivers/ata/ahci.c
+++ b/drivers/ata/ahci.c
@@ -188,8 +188,10 @@ struct ahci_sg {
};
struct ahci_host_priv {
- u32 cap; /* cache of HOST_CAP register */
- u32 port_map; /* cache of HOST_PORTS_IMPL reg */
+ u32 cap; /* cap to use */
+ u32 port_map; /* port map to use */
+ u32 saved_cap; /* saved initial cap */
+ u32 saved_port_map; /* saved initial port_map */
};
struct ahci_port_priv {
@@ -477,6 +479,65 @@ static inline void __iomem *ahci_port_base(void __iomem *base,
return base + 0x100 + (port * 0x80);
}
+/**
+ * ahci_save_initial_config - Save and fixup initial config values
+ * @probe_ent: probe_ent of target device
+ *
+ * Some registers containing configuration info might be setup by
+ * BIOS and might be cleared on reset. This function saves the
+ * initial values of those registers into @hpriv such that they
+ * can be restored after controller reset.
+ *
+ * If inconsistent, config values are fixed up by this function.
+ *
+ * LOCKING:
+ * None.
+ */
+static void ahci_save_initial_config(struct ata_probe_ent *probe_ent)
+{
+ struct ahci_host_priv *hpriv = probe_ent->private_data;
+ void __iomem *mmio = probe_ent->iomap[AHCI_PCI_BAR];
+ u32 cap, port_map;
+
+ /* Values prefixed with saved_ are written back to host after
+ * reset. Values without are used for driver operation.
+ */
+ hpriv->saved_cap = cap = readl(mmio + HOST_CAP);
+ hpriv->saved_port_map = port_map = readl(mmio + HOST_PORTS_IMPL);
+
+ /* fixup zero port_map */
+ if (!port_map) {
+ port_map = (1 << ahci_nr_ports(hpriv->cap)) - 1;
+ dev_printk(KERN_WARNING, probe_ent->dev,
+ "PORTS_IMPL is zero, forcing 0x%x\n", port_map);
+
+ /* write the fixed up value to the PI register */
+ hpriv->saved_port_map = port_map;
+ }
+
+ /* record values to use during operation */
+ hpriv->cap = cap;
+ hpriv->port_map = port_map;
+}
+
+/**
+ * ahci_restore_initial_config - Restore initial config
+ * @mmio: MMIO base for the host
+ * @hpriv: host private data
+ *
+ * Restore initial config stored by ahci_save_initial_config().
+ *
+ * LOCKING:
+ * None.
+ */
+static void ahci_restore_initial_config(void __iomem *mmio,
+ struct ahci_host_priv *hpriv)
+{
+ writel(hpriv->saved_cap, mmio + HOST_CAP);
+ writel(hpriv->saved_port_map, mmio + HOST_PORTS_IMPL);
+ (void) readl(mmio + HOST_PORTS_IMPL); /* flush */
+}
+
static u32 ahci_scr_read (struct ata_port *ap, unsigned int sc_reg_in)
{
unsigned int sc_reg;
@@ -653,12 +714,10 @@ static int ahci_deinit_port(void __iomem *port_mmio, u32 cap, const char **emsg)
return 0;
}
-static int ahci_reset_controller(void __iomem *mmio, struct pci_dev *pdev)
+static int ahci_reset_controller(void __iomem *mmio, struct pci_dev *pdev,
+ struct ahci_host_priv *hpriv)
{
- u32 cap_save, impl_save, tmp;
-
- cap_save = readl(mmio + HOST_CAP);
- impl_save = readl(mmio + HOST_PORTS_IMPL);
+ u32 tmp;
/* global controller reset */
tmp = readl(mmio + HOST_CTL);
@@ -683,18 +742,8 @@ static int ahci_reset_controller(void __iomem *mmio, struct pci_dev *pdev)
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.
- */
- if (!impl_save) {
- impl_save = (1 << ahci_nr_ports(cap_save)) - 1;
- dev_printk(KERN_WARNING, &pdev->dev,
- "PORTS_IMPL is zero, forcing 0x%x\n", impl_save);
- }
- writel(cap_save, mmio + HOST_CAP);
- writel(impl_save, mmio + HOST_PORTS_IMPL);
- (void) readl(mmio + HOST_PORTS_IMPL); /* flush */
+ /* some registers might be cleared on reset. restore initial values */
+ ahci_restore_initial_config(mmio, hpriv);
if (pdev->vendor == PCI_VENDOR_ID_INTEL) {
u16 tmp16;
@@ -1432,7 +1481,7 @@ static int ahci_pci_device_resume(struct pci_dev *pdev)
return rc;
if (pdev->dev.power.power_state.event == PM_EVENT_SUSPEND) {
- rc = ahci_reset_controller(mmio, pdev);
+ rc = ahci_reset_controller(mmio, pdev, hpriv);
if (rc)
return rc;
@@ -1543,12 +1592,10 @@ static int ahci_host_init(struct ata_probe_ent *probe_ent)
unsigned int i, cap_n_ports, using_dac;
int rc;
- rc = ahci_reset_controller(mmio, pdev);
+ rc = ahci_reset_controller(mmio, pdev, hpriv);
if (rc)
return rc;
- hpriv->cap = readl(mmio + HOST_CAP);
- hpriv->port_map = readl(mmio + HOST_PORTS_IMPL);
cap_n_ports = ahci_nr_ports(hpriv->cap);
VPRINTK("cap 0x%x port_map 0x%x n_ports %d\n",
@@ -1739,6 +1786,8 @@ static int ahci_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
probe_ent->private_data = hpriv;
/* initialize adapter */
+ ahci_save_initial_config(probe_ent);
+
rc = ahci_host_init(probe_ent);
if (rc)
return rc;