sata, highbank: set tx_atten override bits
authorMark Langsdorf <mark.langsdorf@calxeda.com>
Wed, 14 Aug 2013 18:23:31 +0000 (13:23 -0500)
committerTejun Heo <tj@kernel.org>
Wed, 14 Aug 2013 20:43:29 +0000 (16:43 -0400)
Some board designs do not drive the SATA transmit lines within the
specification. The ECME can provide override settings, on a per board
basis, to bring the transmit lines within spec. Read those settings
from the DTB and program them in.

At the time of submission, no production hardware requires this patch.

Signed-off-by: Mark Langsdorf <mark.langsdorf@calxeda.com>
Signed-off-by: Tejun Heo <tj@kernel.org>
Documentation/devicetree/bindings/ata/sata_highbank.txt
drivers/ata/sata_highbank.c

index e7a6f2864f8bf73721dbaf9982ca42df06eb6a97..c84833e133637fb132142d17b453f737864d7e58 100644 (file)
@@ -20,6 +20,9 @@ Optional properties:
                        indicator lights using the indicated GPIOs
 - calxeda,led-order : a u32 array that map port numbers to offsets within the
                        SGPIO bitstream.
+- calxeda,tx-atten  : a u32 array that contains TX attenuation override
+                       codes, one per port. The upper 3 bytes are always
+                       0 and thus ignored.
 
 Example:
         sata@ffe08000 {
@@ -31,4 +34,5 @@ Example:
                                        &combophy0 2 &combophy0 3>;
                calxeda,sgpio-gpio =<&gpioh 5 1 &gpioh 6 1 &gpioh 7 1>;
                calxeda,led-order = <4 0 1 2 3>;
+               calxeda,tx-atten = <0xff 22 0xff 0xff 23>;
         };
index 5eb93de75e43d43f3a4b7abccb14dbdc65a0480b..ba43c72175455ca074e3243b69a3b9520bb741c1 100644 (file)
 #define CR_BUSY                                0x0001
 #define CR_START                       0x0001
 #define CR_WR_RDN                      0x0002
+#define CPHY_TX_INPUT_STS              0x2001
 #define CPHY_RX_INPUT_STS              0x2002
-#define CPHY_SATA_OVERRIDE             0x4000
-#define CPHY_OVERRIDE                  0x2005
+#define CPHY_SATA_TX_OVERRIDE          0x8000
+#define CPHY_SATA_RX_OVERRIDE          0x4000
+#define CPHY_TX_OVERRIDE               0x2004
+#define CPHY_RX_OVERRIDE               0x2005
 #define SPHY_LANE                      0x100
 #define SPHY_HALF_RATE                 0x0001
 #define CPHY_SATA_DPLL_MODE            0x0700
 #define CPHY_SATA_DPLL_SHIFT           8
 #define CPHY_SATA_DPLL_RESET           (1 << 11)
+#define CPHY_SATA_TX_ATTEN             0x1c00
+#define CPHY_SATA_TX_ATTEN_SHIFT       10
 #define CPHY_PHY_COUNT                 6
 #define CPHY_LANE_COUNT                        4
 #define CPHY_PORT_COUNT                        (CPHY_PHY_COUNT * CPHY_LANE_COUNT)
@@ -66,6 +71,7 @@ struct phy_lane_info {
        void __iomem *phy_base;
        u8 lane_mapping;
        u8 phy_devs;
+       u8 tx_atten;
 };
 static struct phy_lane_info port_data[CPHY_PORT_COUNT];
 
@@ -76,7 +82,6 @@ static DEFINE_SPINLOCK(sgpio_lock);
 #define SGPIO_PINS                     3
 #define SGPIO_PORTS                    8
 
-/* can be cast as an ahci_host_priv for compatibility with most functions */
 struct ecx_plat_data {
        u32             n_ports;
        unsigned        sgpio_gpio[SGPIO_PINS];
@@ -259,8 +264,27 @@ static void highbank_cphy_disable_overrides(u8 sata_port)
        if (unlikely(port_data[sata_port].phy_base == NULL))
                return;
        tmp = combo_phy_read(sata_port, CPHY_RX_INPUT_STS + lane * SPHY_LANE);
-       tmp &= ~CPHY_SATA_OVERRIDE;
-       combo_phy_write(sata_port, CPHY_OVERRIDE + lane * SPHY_LANE, tmp);
+       tmp &= ~CPHY_SATA_RX_OVERRIDE;
+       combo_phy_write(sata_port, CPHY_RX_OVERRIDE + lane * SPHY_LANE, tmp);
+}
+
+static void cphy_override_tx_attenuation(u8 sata_port, u32 val)
+{
+       u8 lane = port_data[sata_port].lane_mapping;
+       u32 tmp;
+
+       if (val & 0x8)
+               return;
+
+       tmp = combo_phy_read(sata_port, CPHY_TX_INPUT_STS + lane * SPHY_LANE);
+       tmp &= ~CPHY_SATA_TX_OVERRIDE;
+       combo_phy_write(sata_port, CPHY_TX_OVERRIDE + lane * SPHY_LANE, tmp);
+
+       tmp |= CPHY_SATA_TX_OVERRIDE;
+       combo_phy_write(sata_port, CPHY_TX_OVERRIDE + lane * SPHY_LANE, tmp);
+
+       tmp |= (val << CPHY_SATA_TX_ATTEN_SHIFT) & CPHY_SATA_TX_ATTEN;
+       combo_phy_write(sata_port, CPHY_TX_OVERRIDE + lane * SPHY_LANE, tmp);
 }
 
 static void cphy_override_rx_mode(u8 sata_port, u32 val)
@@ -268,21 +292,21 @@ static void cphy_override_rx_mode(u8 sata_port, u32 val)
        u8 lane = port_data[sata_port].lane_mapping;
        u32 tmp;
        tmp = combo_phy_read(sata_port, CPHY_RX_INPUT_STS + lane * SPHY_LANE);
-       tmp &= ~CPHY_SATA_OVERRIDE;
-       combo_phy_write(sata_port, CPHY_OVERRIDE + lane * SPHY_LANE, tmp);
+       tmp &= ~CPHY_SATA_RX_OVERRIDE;
+       combo_phy_write(sata_port, CPHY_RX_OVERRIDE + lane * SPHY_LANE, tmp);
 
-       tmp |= CPHY_SATA_OVERRIDE;
-       combo_phy_write(sata_port, CPHY_OVERRIDE + lane * SPHY_LANE, tmp);
+       tmp |= CPHY_SATA_RX_OVERRIDE;
+       combo_phy_write(sata_port, CPHY_RX_OVERRIDE + lane * SPHY_LANE, tmp);
 
        tmp &= ~CPHY_SATA_DPLL_MODE;
        tmp |= val << CPHY_SATA_DPLL_SHIFT;
-       combo_phy_write(sata_port, CPHY_OVERRIDE + lane * SPHY_LANE, tmp);
+       combo_phy_write(sata_port, CPHY_RX_OVERRIDE + lane * SPHY_LANE, tmp);
 
        tmp |= CPHY_SATA_DPLL_RESET;
-       combo_phy_write(sata_port, CPHY_OVERRIDE + lane * SPHY_LANE, tmp);
+       combo_phy_write(sata_port, CPHY_RX_OVERRIDE + lane * SPHY_LANE, tmp);
 
        tmp &= ~CPHY_SATA_DPLL_RESET;
-       combo_phy_write(sata_port, CPHY_OVERRIDE + lane * SPHY_LANE, tmp);
+       combo_phy_write(sata_port, CPHY_RX_OVERRIDE + lane * SPHY_LANE, tmp);
 
        msleep(15);
 }
@@ -299,16 +323,20 @@ static void highbank_cphy_override_lane(u8 sata_port)
                                                lane * SPHY_LANE);
        } while ((tmp & SPHY_HALF_RATE) && (k++ < 1000));
        cphy_override_rx_mode(sata_port, 3);
+       cphy_override_tx_attenuation(sata_port, port_data[sata_port].tx_atten);
 }
 
 static int highbank_initialize_phys(struct device *dev, void __iomem *addr)
 {
        struct device_node *sata_node = dev->of_node;
-       int phy_count = 0, phy, port = 0;
+       int phy_count = 0, phy, port = 0, i;
        void __iomem *cphy_base[CPHY_PHY_COUNT];
        struct device_node *phy_nodes[CPHY_PHY_COUNT];
+       u32 tx_atten[CPHY_PORT_COUNT];
+
        memset(port_data, 0, sizeof(struct phy_lane_info) * CPHY_PORT_COUNT);
        memset(phy_nodes, 0, sizeof(struct device_node*) * CPHY_PHY_COUNT);
+       memset(tx_atten, 0xff, CPHY_PORT_COUNT);
 
        do {
                u32 tmp;
@@ -336,6 +364,10 @@ static int highbank_initialize_phys(struct device *dev, void __iomem *addr)
                of_node_put(phy_data.np);
                port += 1;
        } while (port < CPHY_PORT_COUNT);
+       of_property_read_u32_array(sata_node, "calxeda,tx-atten",
+                               tx_atten, port);
+       for (i = 0; i < port; i++)
+               port_data[i].tx_atten = (u8) tx_atten[i];
        return 0;
 }