|
@@ -30,11 +30,14 @@
|
|
|
#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 },
|
|
|
{ }
|