summaryrefslogtreecommitdiff
path: root/drivers/net/phy
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/net/phy')
-rw-r--r--drivers/net/phy/marvell.c121
-rw-r--r--drivers/net/phy/phy.c180
2 files changed, 298 insertions, 3 deletions
diff --git a/drivers/net/phy/marvell.c b/drivers/net/phy/marvell.c
index a62c695c5c8..212a861596f 100644
--- a/drivers/net/phy/marvell.c
+++ b/drivers/net/phy/marvell.c
@@ -104,6 +104,88 @@
#define MIIM_88E151x_MODE_SGMII 1
#define MIIM_88E151x_RESET_OFFS 15
+#if IS_ENABLED(CONFIG_DM_ETH)
+static int marvell_read_page(struct phy_device *phydev)
+{
+ return phy_read(phydev, MDIO_DEVAD_NONE, MII_MARVELL_PHY_PAGE);
+}
+
+static int marvell_write_page(struct phy_device *phydev, int page)
+{
+ return phy_write(phydev, MDIO_DEVAD_NONE, MII_MARVELL_PHY_PAGE, page);
+}
+
+/* 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 *prop;
+ int len, i, saved_page, current_page, ret = 0;
+
+ if (!ofnode_valid(phydev->node))
+ return 0;
+
+ prop = ofnode_get_property(phydev->node, "marvell,reg-init", &len);
+ if (!prop)
+ return 0;
+
+ saved_page = marvell_read_page(phydev);
+ if (saved_page < 0)
+ goto err;
+ current_page = saved_page;
+
+ len /= sizeof(*prop);
+ for (i = 0; i < len - 3; i += 4) {
+ u16 page = be32_to_cpup(prop + i);
+ u16 reg = be32_to_cpup(prop + i + 1);
+ u16 mask = be32_to_cpup(prop + i + 2);
+ u16 val_bits = be32_to_cpup(prop + i + 3);
+ int val;
+
+ if (page != current_page) {
+ current_page = page;
+ ret = marvell_write_page(phydev, page);
+ if (ret < 0)
+ goto err;
+ }
+
+ val = 0;
+ if (mask) {
+ val = phy_read(phydev, MDIO_DEVAD_NONE, reg);
+ if (val < 0) {
+ ret = val;
+ goto err;
+ }
+ val &= mask;
+ }
+ val |= val_bits;
+
+ ret = phy_write(phydev, MDIO_DEVAD_NONE, reg, val);
+ if (ret < 0)
+ goto err;
+ }
+
+err:
+ return marvell_write_page(phydev, saved_page);
+}
+#else
+static int marvell_of_reg_init(struct phy_device *phydev)
+{
+ return 0;
+}
+#endif /* CONFIG_DM_ETH */
+
static int m88e1xxx_phy_extread(struct phy_device *phydev, int addr,
int devaddr, int regnum)
{
@@ -143,6 +225,8 @@ static int m88e1011s_config(struct phy_device *phydev)
phy_write(phydev, MDIO_DEVAD_NONE, MII_BMCR, BMCR_RESET);
+ marvell_of_reg_init(phydev);
+
genphy_config_aneg(phydev);
return 0;
@@ -298,6 +382,8 @@ static int m88e1111s_config(struct phy_device *phydev)
/* soft reset */
phy_reset(phydev);
+ marvell_of_reg_init(phydev);
+
genphy_config_aneg(phydev);
genphy_restart_aneg(phydev);
@@ -397,6 +483,8 @@ static int m88e151x_config(struct phy_device *phydev)
/* soft reset */
phy_reset(phydev);
+ marvell_of_reg_init(phydev);
+
genphy_config_aneg(phydev);
genphy_restart_aneg(phydev);
@@ -417,6 +505,8 @@ static int m88e1118_config(struct phy_device *phydev)
/* Change Page Number */
phy_write(phydev, MDIO_DEVAD_NONE, MIIM_88E1118_PHY_PAGE, 0x0000);
+ marvell_of_reg_init(phydev);
+
return genphy_config_aneg(phydev);
}
@@ -439,6 +529,8 @@ static int m88e1121_config(struct phy_device *phydev)
{
int pg;
+ marvell_of_reg_init(phydev);
+
/* Configure the PHY */
genphy_config_aneg(phydev);
@@ -479,6 +571,8 @@ static int m88e1145_config(struct phy_device *phydev)
MIIM_M88E1145_RGMII_TX_DELAY;
phy_write(phydev, MDIO_DEVAD_NONE, MIIM_88E1145_PHY_EXT_CR, reg);
+ marvell_of_reg_init(phydev);
+
genphy_config_aneg(phydev);
/* soft reset */
@@ -511,6 +605,8 @@ static int m88e1149_config(struct phy_device *phydev)
phy_write(phydev, MDIO_DEVAD_NONE, 0x1e, 0x0);
phy_write(phydev, MDIO_DEVAD_NONE, 0x1e, 0x100);
+ marvell_of_reg_init(phydev);
+
genphy_config_aneg(phydev);
phy_reset(phydev);
@@ -518,6 +614,16 @@ static int m88e1149_config(struct phy_device *phydev)
return 0;
}
+/* Marvell 88E1240 */
+static int m88e1240_config(struct phy_device *phydev)
+{
+ marvell_of_reg_init(phydev);
+
+ genphy_config_aneg(phydev);
+
+ return 0;
+}
+
/* Marvell 88E1310 */
static int m88e1310_config(struct phy_device *phydev)
{
@@ -544,6 +650,8 @@ static int m88e1310_config(struct phy_device *phydev)
/* Ensure to return to page 0 */
phy_write(phydev, MDIO_DEVAD_NONE, MIIM_88E1310_PHY_PAGE, 0x0000);
+ marvell_of_reg_init(phydev);
+
return genphy_config_aneg(phydev);
}
@@ -578,6 +686,8 @@ static int m88e1680_config(struct phy_device *phydev)
phy_write(phydev, MDIO_DEVAD_NONE, MIIM_88E1118_PHY_PAGE, 0x0000);
phy_write(phydev, MDIO_DEVAD_NONE, 0, 0x9140);
+ marvell_of_reg_init(phydev);
+
res = genphy_config_aneg(phydev);
if (res < 0)
return res;
@@ -660,6 +770,16 @@ static struct phy_driver M88E1149S_driver = {
.shutdown = &genphy_shutdown,
};
+static struct phy_driver M88E1240_driver = {
+ .name = "Marvell 88E1240",
+ .uid = 0x1410e30,
+ .mask = 0xffffff0,
+ .features = PHY_GBIT_FEATURES,
+ .config = &m88e1240_config,
+ .startup = &m88e1011s_startup,
+ .shutdown = &genphy_shutdown,
+};
+
static struct phy_driver M88E151x_driver = {
.name = "Marvell 88E151x",
.uid = 0x1410dd0,
@@ -702,6 +822,7 @@ int phy_marvell_init(void)
phy_register(&M88E1118R_driver);
phy_register(&M88E1111S_driver);
phy_register(&M88E1011S_driver);
+ phy_register(&M88E1240_driver);
phy_register(&M88E151x_driver);
phy_register(&M88E1680_driver);
diff --git a/drivers/net/phy/phy.c b/drivers/net/phy/phy.c
index 1121b99abff..0350afdd1b6 100644
--- a/drivers/net/phy/phy.c
+++ b/drivers/net/phy/phy.c
@@ -984,10 +984,10 @@ struct phy_device *fixed_phy_create(ofnode node)
}
phydev = phy_device_create(NULL, 0, PHY_FIXED_ID, false);
- if (phydev)
+ if (phydev) {
phydev->node = subnode;
-
- phydev->interface = ofnode_read_phy_mode(node);
+ phydev->interface = ofnode_read_phy_mode(node);
+ }
return phydev;
}
@@ -1101,3 +1101,177 @@ int phy_modify(struct phy_device *phydev, int devad, int regnum, u16 mask,
return phy_write(phydev, devad, regnum, (ret & ~mask) | set);
}
+
+/**
+ * phy_read - Convenience function for reading a given PHY register
+ * @phydev: the phy_device struct
+ * @devad: The MMD to read from
+ * @regnum: register number to read
+ * @return: value for success or negative errno for failure
+ */
+int phy_read(struct phy_device *phydev, int devad, int regnum)
+{
+ struct mii_dev *bus = phydev->bus;
+
+ if (!bus || !bus->read) {
+ debug("%s: No bus configured\n", __func__);
+ return -1;
+ }
+
+ return bus->read(bus, phydev->addr, devad, regnum);
+}
+
+/**
+ * phy_write - Convenience function for writing a given PHY register
+ * @phydev: the phy_device struct
+ * @devad: The MMD to read from
+ * @regnum: register number to write
+ * @val: value to write to @regnum
+ * @return: 0 for success or negative errno for failure
+ */
+int phy_write(struct phy_device *phydev, int devad, int regnum, u16 val)
+{
+ struct mii_dev *bus = phydev->bus;
+
+ if (!bus || !bus->write) {
+ debug("%s: No bus configured\n", __func__);
+ return -1;
+ }
+
+ return bus->write(bus, phydev->addr, devad, regnum, val);
+}
+
+/**
+ * phy_mmd_start_indirect - Convenience function for writing MMD registers
+ * @phydev: the phy_device struct
+ * @devad: The MMD to read from
+ * @regnum: register number to write
+ * @return: None
+ */
+void phy_mmd_start_indirect(struct phy_device *phydev, int devad, int regnum)
+{
+ /* Write the desired MMD Devad */
+ phy_write(phydev, MDIO_DEVAD_NONE, MII_MMD_CTRL, devad);
+
+ /* Write the desired MMD register address */
+ phy_write(phydev, MDIO_DEVAD_NONE, MII_MMD_DATA, regnum);
+
+ /* Select the Function : DATA with no post increment */
+ phy_write(phydev, MDIO_DEVAD_NONE, MII_MMD_CTRL,
+ (devad | MII_MMD_CTRL_NOINCR));
+}
+
+/**
+ * phy_read_mmd - Convenience function for reading a register
+ * from an MMD on a given PHY.
+ * @phydev: The phy_device struct
+ * @devad: The MMD to read from
+ * @regnum: The register on the MMD to read
+ * @return: Value for success or negative errno for failure
+ */
+int phy_read_mmd(struct phy_device *phydev, int devad, int regnum)
+{
+ struct phy_driver *drv = phydev->drv;
+
+ if (regnum > (u16)~0 || devad > 32)
+ return -EINVAL;
+
+ /* driver-specific access */
+ if (drv->read_mmd)
+ return drv->read_mmd(phydev, devad, regnum);
+
+ /* direct C45 / C22 access */
+ if ((drv->features & PHY_10G_FEATURES) == PHY_10G_FEATURES ||
+ devad == MDIO_DEVAD_NONE || !devad)
+ return phy_read(phydev, devad, regnum);
+
+ /* indirect C22 access */
+ phy_mmd_start_indirect(phydev, devad, regnum);
+
+ /* Read the content of the MMD's selected register */
+ return phy_read(phydev, MDIO_DEVAD_NONE, MII_MMD_DATA);
+}
+
+/**
+ * phy_write_mmd - Convenience function for writing a register
+ * on an MMD on a given PHY.
+ * @phydev: The phy_device struct
+ * @devad: The MMD to read from
+ * @regnum: The register on the MMD to read
+ * @val: value to write to @regnum
+ * @return: 0 for success or negative errno for failure
+ */
+int phy_write_mmd(struct phy_device *phydev, int devad, int regnum, u16 val)
+{
+ struct phy_driver *drv = phydev->drv;
+
+ if (regnum > (u16)~0 || devad > 32)
+ return -EINVAL;
+
+ /* driver-specific access */
+ if (drv->write_mmd)
+ return drv->write_mmd(phydev, devad, regnum, val);
+
+ /* direct C45 / C22 access */
+ if ((drv->features & PHY_10G_FEATURES) == PHY_10G_FEATURES ||
+ devad == MDIO_DEVAD_NONE || !devad)
+ return phy_write(phydev, devad, regnum, val);
+
+ /* indirect C22 access */
+ phy_mmd_start_indirect(phydev, devad, regnum);
+
+ /* Write the data into MMD's selected register */
+ return phy_write(phydev, MDIO_DEVAD_NONE, MII_MMD_DATA, val);
+}
+
+/**
+ * phy_set_bits_mmd - Convenience function for setting bits in a register
+ * on MMD
+ * @phydev: the phy_device struct
+ * @devad: the MMD containing register to modify
+ * @regnum: register number to modify
+ * @val: bits to set
+ * @return: 0 for success or negative errno for failure
+ */
+int phy_set_bits_mmd(struct phy_device *phydev, int devad, u32 regnum, u16 val)
+{
+ int value, ret;
+
+ value = phy_read_mmd(phydev, devad, regnum);
+ if (value < 0)
+ return value;
+
+ value |= val;
+
+ ret = phy_write_mmd(phydev, devad, regnum, value);
+ if (ret < 0)
+ return ret;
+
+ return 0;
+}
+
+/**
+ * phy_clear_bits_mmd - Convenience function for clearing bits in a register
+ * on MMD
+ * @phydev: the phy_device struct
+ * @devad: the MMD containing register to modify
+ * @regnum: register number to modify
+ * @val: bits to clear
+ * @return: 0 for success or negative errno for failure
+ */
+int phy_clear_bits_mmd(struct phy_device *phydev, int devad, u32 regnum, u16 val)
+{
+ int value, ret;
+
+ value = phy_read_mmd(phydev, devad, regnum);
+ if (value < 0)
+ return value;
+
+ value &= ~val;
+
+ ret = phy_write_mmd(phydev, devad, regnum, value);
+ if (ret < 0)
+ return ret;
+
+ return 0;
+}