[PATCH net-next 2/2] net: phy: DP83822: Add ability to advertise Fiber connection

From: Dan Murphy
Date: Thu May 14 2020 - 13:40:53 EST


The DP83822 can be configured to use a Fiber connection. The strap
register is read to determine if the device has been configured to use
a fiber connection. With the fiber connection the PHY can be configured
to detect whether the fiber connection is active by either a high signal
or a low signal.

Fiber mode is only applicable to the DP83822 so rework the PHY match
table so that non-fiber PHYs can still use the same driver but not call
or use any of the fiber features.

Signed-off-by: Dan Murphy <dmurphy@xxxxxx>
---
drivers/net/phy/dp83822.c | 140 +++++++++++++++++++++++++++++++++++---
1 file changed, 132 insertions(+), 8 deletions(-)

diff --git a/drivers/net/phy/dp83822.c b/drivers/net/phy/dp83822.c
index 1dd19d0cb269..fe7443bc8b06 100644
--- a/drivers/net/phy/dp83822.c
+++ b/drivers/net/phy/dp83822.c
@@ -27,6 +27,11 @@
#define MII_DP83822_MISR1 0x12
#define MII_DP83822_MISR2 0x13
#define MII_DP83822_RESET_CTRL 0x1f
+#define MII_DP83822_GENCFG 0x465
+#define MII_DP83822_SOR1 0x467
+
+/* GENCFG */
+#define DP83822_SIG_DET_POLARITY BIT(0)

#define DP83822_HW_RESET BIT(15)
#define DP83822_SW_RESET BIT(14)
@@ -77,6 +82,21 @@
#define DP83822_WOL_INDICATION_SEL BIT(8)
#define DP83822_WOL_CLR_INDICATION BIT(11)

+/* SOR1 bits */
+#define DP83822_FX_EN_STRAP BIT(11)
+#define DP83822_FX_DUPLEX_STRAP BIT(0)
+
+#define MII_DP83822_FIBER_ADVERTISE (SUPPORTED_AUI | SUPPORTED_FIBRE | \
+ SUPPORTED_BNC | SUPPORTED_Pause | \
+ SUPPORTED_Asym_Pause | \
+ SUPPORTED_100baseT_Full)
+
+struct dp83822_private {
+ bool fx_signal_detect_low;
+ int fx_enabled;
+ u16 fx_duplex_mode;
+};
+
static int dp83822_ack_interrupt(struct phy_device *phydev)
{
int err;
@@ -255,7 +275,7 @@ static int dp83822_config_intr(struct phy_device *phydev)
return phy_write(phydev, MII_DP83822_PHYSCR, physcr_status);
}

-static int dp83822_config_init(struct phy_device *phydev)
+static int dp8382x_disable_wol(struct phy_device *phydev)
{
int value = DP83822_WOL_EN | DP83822_WOL_MAGIC_EN |
DP83822_WOL_SECURE_ON;
@@ -264,6 +284,41 @@ static int dp83822_config_init(struct phy_device *phydev)
MII_DP83822_WOL_CFG, value);
}

+static int dp83822_config_init(struct phy_device *phydev)
+{
+ struct dp83822_private *dp83822 = phydev->priv;
+ int err = 0;
+
+ if (dp83822->fx_enabled) {
+ linkmode_set_bit(ETHTOOL_LINK_MODE_FIBRE_BIT,
+ phydev->supported);
+ linkmode_set_bit(ETHTOOL_LINK_MODE_FIBRE_BIT,
+ phydev->advertising);
+
+ /* Auto negotiation is not available in fiber mode */
+ phydev->autoneg = AUTONEG_DISABLE;
+ phydev->speed = SPEED_100;
+ phydev->duplex = DUPLEX_FULL;
+
+ /* Setup fiber advertisement */
+ err = phy_modify_changed(phydev, MII_ADVERTISE,
+ ADVERTISE_1000XFULL |
+ ADVERTISE_1000XPAUSE |
+ ADVERTISE_1000XPSE_ASYM,
+ MII_DP83822_FIBER_ADVERTISE);
+
+ if (err < 0)
+ return err;
+ }
+
+ return dp8382x_disable_wol(phydev);
+}
+
+static int dp8382x_config_init(struct phy_device *phydev)
+{
+ return dp8382x_disable_wol(phydev);
+}
+
static int dp83822_phy_reset(struct phy_device *phydev)
{
int err;
@@ -272,7 +327,60 @@ static int dp83822_phy_reset(struct phy_device *phydev)
if (err < 0)
return err;

- dp83822_config_init(phydev);
+ return phydev->drv->config_init(phydev);
+}
+
+#ifdef CONFIG_OF_MDIO
+static int dp83822_of_init(struct phy_device *phydev)
+{
+ struct dp83822_private *dp83822 = phydev->priv;
+ struct device *dev = &phydev->mdio.dev;
+
+ if (dp83822->fx_enabled)
+ dp83822->fx_signal_detect_low = device_property_present(dev,
+ "ti,signal-polarity-low");
+
+ return 0;
+}
+#else
+static int dp83822_of_init(struct phy_device *phydev)
+{
+ return 0;
+}
+#endif /* CONFIG_OF_MDIO */
+
+static int dp83822_read_straps(struct phy_device *phydev)
+{
+ struct dp83822_private *dp83822 = phydev->priv;
+ u16 val;
+
+ val = phy_read_mmd(phydev, DP83822_DEVADDR, MII_DP83822_SOR1);
+ if (val < 0)
+ return val;
+
+ dp83822->fx_enabled = val & DP83822_FX_EN_STRAP;
+ dp83822->fx_duplex_mode = val & DP83822_FX_DUPLEX_STRAP;
+
+ return 0;
+}
+
+static int dp83822_probe(struct phy_device *phydev)
+{
+ struct dp83822_private *dp83822;
+ int ret;
+
+ dp83822 = devm_kzalloc(&phydev->mdio.dev, sizeof(*dp83822),
+ GFP_KERNEL);
+ if (!dp83822)
+ return -ENOMEM;
+
+ phydev->priv = dp83822;
+
+ ret = dp83822_read_straps(phydev);
+ if (ret)
+ return ret;
+
+ dp83822_of_init(phydev);

return 0;
}
@@ -308,6 +416,7 @@ static int dp83822_resume(struct phy_device *phydev)
PHY_ID_MATCH_MODEL(_id), \
.name = (_name), \
/* PHY_BASIC_FEATURES */ \
+ .probe = dp83822_probe, \
.soft_reset = dp83822_phy_reset, \
.config_init = dp83822_config_init, \
.get_wol = dp83822_get_wol, \
@@ -318,14 +427,29 @@ static int dp83822_resume(struct phy_device *phydev)
.resume = dp83822_resume, \
}

+#define DP8382X_PHY_DRIVER(_id, _name) \
+ { \
+ PHY_ID_MATCH_MODEL(_id), \
+ .name = (_name), \
+ /* PHY_BASIC_FEATURES */ \
+ .soft_reset = dp83822_phy_reset, \
+ .config_init = dp8382x_config_init, \
+ .get_wol = dp83822_get_wol, \
+ .set_wol = dp83822_set_wol, \
+ .ack_interrupt = dp83822_ack_interrupt, \
+ .config_intr = dp83822_config_intr, \
+ .suspend = dp83822_suspend, \
+ .resume = dp83822_resume, \
+ }
+
static struct phy_driver dp83822_driver[] = {
DP83822_PHY_DRIVER(DP83822_PHY_ID, "TI DP83822"),
- DP83822_PHY_DRIVER(DP83825I_PHY_ID, "TI DP83825I"),
- DP83822_PHY_DRIVER(DP83826C_PHY_ID, "TI DP83826C"),
- DP83822_PHY_DRIVER(DP83826NC_PHY_ID, "TI DP83826NC"),
- DP83822_PHY_DRIVER(DP83825S_PHY_ID, "TI DP83825S"),
- DP83822_PHY_DRIVER(DP83825CM_PHY_ID, "TI DP83825M"),
- DP83822_PHY_DRIVER(DP83825CS_PHY_ID, "TI DP83825CS"),
+ DP8382X_PHY_DRIVER(DP83825I_PHY_ID, "TI DP83825I"),
+ DP8382X_PHY_DRIVER(DP83826C_PHY_ID, "TI DP83826C"),
+ DP8382X_PHY_DRIVER(DP83826NC_PHY_ID, "TI DP83826NC"),
+ DP8382X_PHY_DRIVER(DP83825S_PHY_ID, "TI DP83825S"),
+ DP8382X_PHY_DRIVER(DP83825CM_PHY_ID, "TI DP83825M"),
+ DP8382X_PHY_DRIVER(DP83825CS_PHY_ID, "TI DP83825CS"),
};
module_phy_driver(dp83822_driver);

--
2.26.2