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