ata: ahci: Introduce firmware-specific caps initialization
[linux-2.6-microblaze.git] / drivers / ata / libahci.c
index 1ffaa5f..954386a 100644 (file)
@@ -16,6 +16,7 @@
  * http://www.intel.com/technology/serialata/pdf/rev1_1.pdf
  */
 
+#include <linux/bitops.h>
 #include <linux/kernel.h>
 #include <linux/gfp.h>
 #include <linux/module.h>
@@ -443,16 +444,28 @@ static ssize_t ahci_show_em_supported(struct device *dev,
 void ahci_save_initial_config(struct device *dev, struct ahci_host_priv *hpriv)
 {
        void __iomem *mmio = hpriv->mmio;
-       u32 cap, cap2, vers, port_map;
+       void __iomem *port_mmio;
+       unsigned long port_map;
+       u32 cap, cap2, vers;
        int i;
 
        /* make sure AHCI mode is enabled before accessing CAP */
        ahci_enable_ahci(mmio);
 
-       /* Values prefixed with saved_ are written back to host after
-        * reset.  Values without are used for driver operation.
+       /*
+        * Values prefixed with saved_ are written back to the HBA and ports
+        * registers after reset. Values without are used for driver operation.
+        */
+
+       /*
+        * Override HW-init HBA capability fields with the platform-specific
+        * values. The rest of the HBA capabilities are defined as Read-only
+        * and can't be modified in CSR anyway.
         */
-       hpriv->saved_cap = cap = readl(mmio + HOST_CAP);
+       cap = readl(mmio + HOST_CAP);
+       if (hpriv->saved_cap)
+               cap = (cap & ~(HOST_CAP_SSS | HOST_CAP_MPS)) | hpriv->saved_cap;
+       hpriv->saved_cap = cap;
 
        /* CAP2 register is only defined for AHCI 1.2 and later */
        vers = readl(mmio + HOST_VERSION);
@@ -519,7 +532,7 @@ void ahci_save_initial_config(struct device *dev, struct ahci_host_priv *hpriv)
        /* Override the HBA ports mapping if the platform needs it */
        port_map = readl(mmio + HOST_PORTS_IMPL);
        if (hpriv->saved_port_map && port_map != hpriv->saved_port_map) {
-               dev_info(dev, "forcing port_map 0x%x -> 0x%x\n",
+               dev_info(dev, "forcing port_map 0x%lx -> 0x%x\n",
                         port_map, hpriv->saved_port_map);
                port_map = hpriv->saved_port_map;
        } else {
@@ -527,7 +540,7 @@ void ahci_save_initial_config(struct device *dev, struct ahci_host_priv *hpriv)
        }
 
        if (hpriv->mask_port_map) {
-               dev_warn(dev, "masking port_map 0x%x -> 0x%x\n",
+               dev_warn(dev, "masking port_map 0x%lx -> 0x%lx\n",
                        port_map,
                        port_map & hpriv->mask_port_map);
                port_map &= hpriv->mask_port_map;
@@ -546,7 +559,7 @@ void ahci_save_initial_config(struct device *dev, struct ahci_host_priv *hpriv)
                 */
                if (map_ports > ahci_nr_ports(cap)) {
                        dev_warn(dev,
-                                "implemented port map (0x%x) contains more ports than nr_ports (%u), using nr_ports\n",
+                                "implemented port map (0x%lx) contains more ports than nr_ports (%u), using nr_ports\n",
                                 port_map, ahci_nr_ports(cap));
                        port_map = 0;
                }
@@ -555,12 +568,26 @@ void ahci_save_initial_config(struct device *dev, struct ahci_host_priv *hpriv)
        /* fabricate port_map from cap.nr_ports for < AHCI 1.3 */
        if (!port_map && vers < 0x10300) {
                port_map = (1 << ahci_nr_ports(cap)) - 1;
-               dev_warn(dev, "forcing PORTS_IMPL to 0x%x\n", port_map);
+               dev_warn(dev, "forcing PORTS_IMPL to 0x%lx\n", port_map);
 
                /* write the fixed up value to the PI register */
                hpriv->saved_port_map = port_map;
        }
 
+       /*
+        * Preserve the ports capabilities defined by the platform. Note there
+        * is no need in storing the rest of the P#.CMD fields since they are
+        * volatile.
+        */
+       for_each_set_bit(i, &port_map, AHCI_MAX_PORTS) {
+               if (hpriv->saved_port_cap[i])
+                       continue;
+
+               port_mmio = __ahci_port_base(hpriv, i);
+               hpriv->saved_port_cap[i] =
+                       readl(port_mmio + PORT_CMD) & PORT_CMD_CAP;
+       }
+
        /* record values to use during operation */
        hpriv->cap = cap;
        hpriv->cap2 = cap2;
@@ -590,13 +617,21 @@ EXPORT_SYMBOL_GPL(ahci_save_initial_config);
 static void ahci_restore_initial_config(struct ata_host *host)
 {
        struct ahci_host_priv *hpriv = host->private_data;
+       unsigned long port_map = hpriv->port_map;
        void __iomem *mmio = hpriv->mmio;
+       void __iomem *port_mmio;
+       int i;
 
        writel(hpriv->saved_cap, mmio + HOST_CAP);
        if (hpriv->saved_cap2)
                writel(hpriv->saved_cap2, mmio + HOST_CAP2);
        writel(hpriv->saved_port_map, mmio + HOST_PORTS_IMPL);
        (void) readl(mmio + HOST_PORTS_IMPL);   /* flush */
+
+       for_each_set_bit(i, &port_map, AHCI_MAX_PORTS) {
+               port_mmio = __ahci_port_base(hpriv, i);
+               writel(hpriv->saved_port_cap[i], port_mmio + PORT_CMD);
+       }
 }
 
 static unsigned ahci_scr_offset(struct ata_port *ap, unsigned int sc_reg)