Merge branch 'spi/for-3.2' of git://git.pengutronix.de/git/wsa/linux-2.6
[pandora-kernel.git] / drivers / net / phy / marvell.c
index f0bd1a1..e8b9c53 100644 (file)
 #include <linux/ethtool.h>
 #include <linux/phy.h>
 #include <linux/marvell_phy.h>
+#include <linux/of.h>
 
 #include <asm/io.h>
 #include <asm/irq.h>
 #include <asm/uaccess.h>
 
+#define MII_MARVELL_PHY_PAGE           22
+
 #define MII_M1011_IEVENT               0x13
 #define MII_M1011_IEVENT_CLEAR         0x0000
 
@@ -80,7 +83,6 @@
 #define MII_88E1121_PHY_LED_CTRL       16
 #define MII_88E1121_PHY_LED_PAGE       3
 #define MII_88E1121_PHY_LED_DEF                0x0030
-#define MII_88E1121_PHY_PAGE           22
 
 #define MII_M1011_PHY_STATUS           0x11
 #define MII_M1011_PHY_STATUS_1000      0x8000
@@ -186,13 +188,94 @@ static int marvell_config_aneg(struct phy_device *phydev)
        return 0;
 }
 
+#ifdef CONFIG_OF_MDIO
+/*
+ * Set and/or override some configuration registers based on the
+ * marvell,reg-init property stored in the of_node for the phydev.
+ *
+ * marvell,reg-init = <reg-page reg mask value>,...;
+ *
+ * There may be one or more sets of <reg-page reg mask value>:
+ *
+ * reg-page: which register bank to use.
+ * reg: the register.
+ * mask: if non-zero, ANDed with existing register value.
+ * value: ORed with the masked value and written to the regiser.
+ *
+ */
+static int marvell_of_reg_init(struct phy_device *phydev)
+{
+       const __be32 *paddr;
+       int len, i, saved_page, current_page, page_changed, ret;
+
+       if (!phydev->dev.of_node)
+               return 0;
+
+       paddr = of_get_property(phydev->dev.of_node, "marvell,reg-init", &len);
+       if (!paddr || len < (4 * sizeof(*paddr)))
+               return 0;
+
+       saved_page = phy_read(phydev, MII_MARVELL_PHY_PAGE);
+       if (saved_page < 0)
+               return saved_page;
+       page_changed = 0;
+       current_page = saved_page;
+
+       ret = 0;
+       len /= sizeof(*paddr);
+       for (i = 0; i < len - 3; i += 4) {
+               u16 reg_page = be32_to_cpup(paddr + i);
+               u16 reg = be32_to_cpup(paddr + i + 1);
+               u16 mask = be32_to_cpup(paddr + i + 2);
+               u16 val_bits = be32_to_cpup(paddr + i + 3);
+               int val;
+
+               if (reg_page != current_page) {
+                       current_page = reg_page;
+                       page_changed = 1;
+                       ret = phy_write(phydev, MII_MARVELL_PHY_PAGE, reg_page);
+                       if (ret < 0)
+                               goto err;
+               }
+
+               val = 0;
+               if (mask) {
+                       val = phy_read(phydev, reg);
+                       if (val < 0) {
+                               ret = val;
+                               goto err;
+                       }
+                       val &= mask;
+               }
+               val |= val_bits;
+
+               ret = phy_write(phydev, reg, val);
+               if (ret < 0)
+                       goto err;
+
+       }
+err:
+       if (page_changed) {
+               i = phy_write(phydev, MII_MARVELL_PHY_PAGE, saved_page);
+               if (ret == 0)
+                       ret = i;
+       }
+       return ret;
+}
+#else
+static int marvell_of_reg_init(struct phy_device *phydev)
+{
+       return 0;
+}
+#endif /* CONFIG_OF_MDIO */
+
 static int m88e1121_config_aneg(struct phy_device *phydev)
 {
        int err, oldpage, mscr;
 
-       oldpage = phy_read(phydev, MII_88E1121_PHY_PAGE);
+       oldpage = phy_read(phydev, MII_MARVELL_PHY_PAGE);
 
-       err = phy_write(phydev, MII_88E1121_PHY_PAGE,
+       err = phy_write(phydev, MII_MARVELL_PHY_PAGE,
                        MII_88E1121_PHY_MSCR_PAGE);
        if (err < 0)
                return err;
@@ -218,7 +301,7 @@ static int m88e1121_config_aneg(struct phy_device *phydev)
                        return err;
        }
 
-       phy_write(phydev, MII_88E1121_PHY_PAGE, oldpage);
+       phy_write(phydev, MII_MARVELL_PHY_PAGE, oldpage);
 
        err = phy_write(phydev, MII_BMCR, BMCR_RESET);
        if (err < 0)
@@ -229,11 +312,11 @@ static int m88e1121_config_aneg(struct phy_device *phydev)
        if (err < 0)
                return err;
 
-       oldpage = phy_read(phydev, MII_88E1121_PHY_PAGE);
+       oldpage = phy_read(phydev, MII_MARVELL_PHY_PAGE);
 
-       phy_write(phydev, MII_88E1121_PHY_PAGE, MII_88E1121_PHY_LED_PAGE);
+       phy_write(phydev, MII_MARVELL_PHY_PAGE, MII_88E1121_PHY_LED_PAGE);
        phy_write(phydev, MII_88E1121_PHY_LED_CTRL, MII_88E1121_PHY_LED_DEF);
-       phy_write(phydev, MII_88E1121_PHY_PAGE, oldpage);
+       phy_write(phydev, MII_MARVELL_PHY_PAGE, oldpage);
 
        err = genphy_config_aneg(phydev);
 
@@ -244,9 +327,9 @@ static int m88e1318_config_aneg(struct phy_device *phydev)
 {
        int err, oldpage, mscr;
 
-       oldpage = phy_read(phydev, MII_88E1121_PHY_PAGE);
+       oldpage = phy_read(phydev, MII_MARVELL_PHY_PAGE);
 
-       err = phy_write(phydev, MII_88E1121_PHY_PAGE,
+       err = phy_write(phydev, MII_MARVELL_PHY_PAGE,
                        MII_88E1121_PHY_MSCR_PAGE);
        if (err < 0)
                return err;
@@ -258,7 +341,7 @@ static int m88e1318_config_aneg(struct phy_device *phydev)
        if (err < 0)
                return err;
 
-       err = phy_write(phydev, MII_88E1121_PHY_PAGE, oldpage);
+       err = phy_write(phydev, MII_MARVELL_PHY_PAGE, oldpage);
        if (err < 0)
                return err;
 
@@ -368,6 +451,9 @@ static int m88e1111_config_init(struct phy_device *phydev)
                        return err;
        }
 
+       err = marvell_of_reg_init(phydev);
+       if (err < 0)
+               return err;
 
        err = phy_write(phydev, MII_BMCR, BMCR_RESET);
        if (err < 0)
@@ -398,7 +484,7 @@ static int m88e1118_config_init(struct phy_device *phydev)
        int err;
 
        /* Change address */
-       err = phy_write(phydev, 0x16, 0x0002);
+       err = phy_write(phydev, MII_MARVELL_PHY_PAGE, 0x0002);
        if (err < 0)
                return err;
 
@@ -408,7 +494,7 @@ static int m88e1118_config_init(struct phy_device *phydev)
                return err;
 
        /* Change address */
-       err = phy_write(phydev, 0x16, 0x0003);
+       err = phy_write(phydev, MII_MARVELL_PHY_PAGE, 0x0003);
        if (err < 0)
                return err;
 
@@ -420,8 +506,42 @@ static int m88e1118_config_init(struct phy_device *phydev)
        if (err < 0)
                return err;
 
+       err = marvell_of_reg_init(phydev);
+       if (err < 0)
+               return err;
+
        /* Reset address */
-       err = phy_write(phydev, 0x16, 0x0);
+       err = phy_write(phydev, MII_MARVELL_PHY_PAGE, 0x0);
+       if (err < 0)
+               return err;
+
+       err = phy_write(phydev, MII_BMCR, BMCR_RESET);
+       if (err < 0)
+               return err;
+
+       return 0;
+}
+
+static int m88e1149_config_init(struct phy_device *phydev)
+{
+       int err;
+
+       /* Change address */
+       err = phy_write(phydev, MII_MARVELL_PHY_PAGE, 0x0002);
+       if (err < 0)
+               return err;
+
+       /* Enable 1000 Mbit */
+       err = phy_write(phydev, 0x15, 0x1048);
+       if (err < 0)
+               return err;
+
+       err = marvell_of_reg_init(phydev);
+       if (err < 0)
+               return err;
+
+       /* Reset address */
+       err = phy_write(phydev, MII_MARVELL_PHY_PAGE, 0x0);
        if (err < 0)
                return err;
 
@@ -491,6 +611,10 @@ static int m88e1145_config_init(struct phy_device *phydev)
                }
        }
 
+       err = marvell_of_reg_init(phydev);
+       if (err < 0)
+               return err;
+
        return 0;
 }
 
@@ -684,6 +808,19 @@ static struct phy_driver marvell_drivers[] = {
                .config_intr = &marvell_config_intr,
                .driver = { .owner = THIS_MODULE },
        },
+       {
+               .phy_id = MARVELL_PHY_ID_88E1149R,
+               .phy_id_mask = MARVELL_PHY_ID_MASK,
+               .name = "Marvell 88E1149R",
+               .features = PHY_GBIT_FEATURES,
+               .flags = PHY_HAS_INTERRUPT,
+               .config_init = &m88e1149_config_init,
+               .config_aneg = &m88e1118_config_aneg,
+               .read_status = &genphy_read_status,
+               .ack_interrupt = &marvell_ack_interrupt,
+               .config_intr = &marvell_config_intr,
+               .driver = { .owner = THIS_MODULE },
+       },
        {
                .phy_id = MARVELL_PHY_ID_88E1240,
                .phy_id_mask = MARVELL_PHY_ID_MASK,
@@ -735,6 +872,7 @@ static struct mdio_device_id __maybe_unused marvell_tbl[] = {
        { 0x01410e10, 0xfffffff0 },
        { 0x01410cb0, 0xfffffff0 },
        { 0x01410cd0, 0xfffffff0 },
+       { 0x01410e50, 0xfffffff0 },
        { 0x01410e30, 0xfffffff0 },
        { 0x01410e90, 0xfffffff0 },
        { }