From 05b35e7eb9a11bbe8102836965e634c04e712c88 Mon Sep 17 00:00:00 2001 From: Andre Edich Date: Wed, 26 Aug 2020 13:17:17 +0200 Subject: smsc95xx: add phylib support Generally, each PHY has their own configuration and it can be done through an external PHY driver. The smsc95xx driver uses only the hard-coded internal PHY configuration. This patch adds phylib support to probe external PHY drivers for configuring external PHYs. The MDI-X configuration for the internal PHYs moves from drivers/net/usb/smsc95xx.c to drivers/net/phy/smsc.c. Signed-off-by: Andre Edich Signed-off-by: David S. Miller --- drivers/net/phy/smsc.c | 67 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 67 insertions(+) (limited to 'drivers/net/phy/smsc.c') diff --git a/drivers/net/phy/smsc.c b/drivers/net/phy/smsc.c index 74568ae16125..638e8c3d1f4a 100644 --- a/drivers/net/phy/smsc.c +++ b/drivers/net/phy/smsc.c @@ -21,6 +21,17 @@ #include #include +/* Vendor-specific PHY Definitions */ +/* EDPD NLP / crossover time configuration */ +#define PHY_EDPD_CONFIG 16 +#define PHY_EDPD_CONFIG_EXT_CROSSOVER_ 0x0001 + +/* Control/Status Indication Register */ +#define SPECIAL_CTRL_STS 27 +#define SPECIAL_CTRL_STS_OVRRD_AMDIX_ 0x8000 +#define SPECIAL_CTRL_STS_AMDIX_ENABLE_ 0x4000 +#define SPECIAL_CTRL_STS_AMDIX_STATE_ 0x2000 + struct smsc_hw_stat { const char *string; u8 reg; @@ -96,6 +107,54 @@ static int lan911x_config_init(struct phy_device *phydev) return smsc_phy_ack_interrupt(phydev); } +static int lan87xx_config_aneg(struct phy_device *phydev) +{ + int rc; + int val; + + switch (phydev->mdix_ctrl) { + case ETH_TP_MDI: + val = SPECIAL_CTRL_STS_OVRRD_AMDIX_; + break; + case ETH_TP_MDI_X: + val = SPECIAL_CTRL_STS_OVRRD_AMDIX_ | + SPECIAL_CTRL_STS_AMDIX_STATE_; + break; + case ETH_TP_MDI_AUTO: + val = SPECIAL_CTRL_STS_AMDIX_ENABLE_; + break; + default: + return genphy_config_aneg(phydev); + } + + rc = phy_read(phydev, SPECIAL_CTRL_STS); + if (rc < 0) + return rc; + + rc &= ~(SPECIAL_CTRL_STS_OVRRD_AMDIX_ | + SPECIAL_CTRL_STS_AMDIX_ENABLE_ | + SPECIAL_CTRL_STS_AMDIX_STATE_); + rc |= val; + phy_write(phydev, SPECIAL_CTRL_STS, rc); + + phydev->mdix = phydev->mdix_ctrl; + return genphy_config_aneg(phydev); +} + +static int lan87xx_config_aneg_ext(struct phy_device *phydev) +{ + int rc; + + /* Extend Manual AutoMDIX timer */ + rc = phy_read(phydev, PHY_EDPD_CONFIG); + if (rc < 0) + return rc; + + rc |= PHY_EDPD_CONFIG_EXT_CROSSOVER_; + phy_write(phydev, PHY_EDPD_CONFIG, rc); + return lan87xx_config_aneg(phydev); +} + /* * The LAN87xx suffers from rare absence of the ENERGYON-bit when Ethernet cable * plugs in while LAN87xx is in Energy Detect Power-Down mode. This leads to @@ -250,6 +309,9 @@ static struct phy_driver smsc_phy_driver[] = { .suspend = genphy_suspend, .resume = genphy_resume, }, { + /* This covers internal PHY (phy_id: 0x0007C0C3) for + * LAN9500 (PID: 0x9500), LAN9514 (PID: 0xec00), LAN9505 (PID: 0x9505) + */ .phy_id = 0x0007c0c0, /* OUI=0x00800f, Model#=0x0c */ .phy_id_mask = 0xfffffff0, .name = "SMSC LAN8700", @@ -262,6 +324,7 @@ static struct phy_driver smsc_phy_driver[] = { .read_status = lan87xx_read_status, .config_init = smsc_phy_config_init, .soft_reset = smsc_phy_reset, + .config_aneg = lan87xx_config_aneg, /* IRQ related */ .ack_interrupt = smsc_phy_ack_interrupt, @@ -293,6 +356,9 @@ static struct phy_driver smsc_phy_driver[] = { .suspend = genphy_suspend, .resume = genphy_resume, }, { + /* This covers internal PHY (phy_id: 0x0007C0F0) for + * LAN9500A (PID: 0x9E00), LAN9505A (PID: 0x9E01) + */ .phy_id = 0x0007c0f0, /* OUI=0x00800f, Model#=0x0f */ .phy_id_mask = 0xfffffff0, .name = "SMSC LAN8710/LAN8720", @@ -306,6 +372,7 @@ static struct phy_driver smsc_phy_driver[] = { .read_status = lan87xx_read_status, .config_init = smsc_phy_config_init, .soft_reset = smsc_phy_reset, + .config_aneg = lan87xx_config_aneg_ext, /* IRQ related */ .ack_interrupt = smsc_phy_ack_interrupt, -- cgit v1.2.3 From 7365494550f6e96b8e444201bf5fdaec40e7e007 Mon Sep 17 00:00:00 2001 From: Marco Felsch Date: Wed, 9 Sep 2020 15:44:57 +0200 Subject: net: phy: smsc: skip ENERGYON interrupt if disabled Don't enable the interrupt if the platform disable the energy detection by "smsc,disable-energy-detect". Signed-off-by: Marco Felsch Reviewed-by: Andrew Lunn Reviewed-by: Florian Fainelli Signed-off-by: David S. Miller --- drivers/net/phy/smsc.c | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) (limited to 'drivers/net/phy/smsc.c') diff --git a/drivers/net/phy/smsc.c b/drivers/net/phy/smsc.c index 638e8c3d1f4a..19ab2821cff0 100644 --- a/drivers/net/phy/smsc.c +++ b/drivers/net/phy/smsc.c @@ -48,10 +48,17 @@ struct smsc_phy_priv { static int smsc_phy_config_intr(struct phy_device *phydev) { - int rc = phy_write (phydev, MII_LAN83C185_IM, - ((PHY_INTERRUPT_ENABLED == phydev->interrupts) - ? MII_LAN83C185_ISF_INT_PHYLIB_EVENTS - : 0)); + struct smsc_phy_priv *priv = phydev->priv; + u16 intmask = 0; + int rc; + + if (phydev->interrupts == PHY_INTERRUPT_ENABLED) { + intmask = MII_LAN83C185_ISF_INT4 | MII_LAN83C185_ISF_INT6; + if (priv->energy_enable) + intmask |= MII_LAN83C185_ISF_INT7; + } + + rc = phy_write(phydev, MII_LAN83C185_IM, intmask); return rc < 0 ? rc : 0; } -- cgit v1.2.3 From 436e380064ea27924f65b9af47876032c4162cf2 Mon Sep 17 00:00:00 2001 From: Marco Felsch Date: Wed, 9 Sep 2020 15:44:58 +0200 Subject: net: phy: smsc: simplify config_init callback Exit the driver specific config_init hook early if energy detection is disabled. We can do this because we don't need to clear the interrupt status here. Clearing the status should be removed anyway since this is handled by the phy_enable_interrupts(). Signed-off-by: Marco Felsch Reviewed-by: Florian Fainelli Signed-off-by: David S. Miller --- drivers/net/phy/smsc.c | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) (limited to 'drivers/net/phy/smsc.c') diff --git a/drivers/net/phy/smsc.c b/drivers/net/phy/smsc.c index 19ab2821cff0..0bcdf927a790 100644 --- a/drivers/net/phy/smsc.c +++ b/drivers/net/phy/smsc.c @@ -73,19 +73,21 @@ static int smsc_phy_ack_interrupt(struct phy_device *phydev) static int smsc_phy_config_init(struct phy_device *phydev) { struct smsc_phy_priv *priv = phydev->priv; + int rc; + + if (!priv->energy_enable) + return 0; - int rc = phy_read(phydev, MII_LAN83C185_CTRL_STATUS); + rc = phy_read(phydev, MII_LAN83C185_CTRL_STATUS); if (rc < 0) return rc; - if (priv->energy_enable) { - /* Enable energy detect mode for this SMSC Transceivers */ - rc = phy_write(phydev, MII_LAN83C185_CTRL_STATUS, - rc | MII_LAN83C185_EDPWRDOWN); - if (rc < 0) - return rc; - } + /* Enable energy detect mode for this SMSC Transceivers */ + rc = phy_write(phydev, MII_LAN83C185_CTRL_STATUS, + rc | MII_LAN83C185_EDPWRDOWN); + if (rc < 0) + return rc; return smsc_phy_ack_interrupt(phydev); } -- cgit v1.2.3 From bedd8d78aba300860cec3f85d6ff549b3b7f2679 Mon Sep 17 00:00:00 2001 From: Marco Felsch Date: Wed, 9 Sep 2020 15:45:00 +0200 Subject: net: phy: smsc: LAN8710/20: add phy refclk in support Add support to specify the clock provider for the PHY refclk and don't rely on 'magic' host clock setup. [1] tried to address this by introducing a flag and fixing the corresponding host. But this commit breaks the IRQ support since the irq setup during .config_intr() is thrown away because the reset comes from the side without respecting the current PHY state within the PHY library state machine. Furthermore the commit fixed the problem only for FEC based hosts other hosts acting like the FEC are not covered. This commit goes the other way around to address the bug fixed by [1]. Instead of resetting the device from the side every time the refclk gets (re-)enabled it requests and enables the clock till the device gets removed. Now the PHY library is the only place where the PHY gets reset to respect the PHY library state machine. [1] commit 7f64e5b18ebb ("net: phy: smsc: LAN8710/20: add PHY_RST_AFTER_CLK_EN flag") Signed-off-by: Marco Felsch Reviewed-by: Florian Fainelli Signed-off-by: David S. Miller --- drivers/net/phy/smsc.c | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) (limited to 'drivers/net/phy/smsc.c') diff --git a/drivers/net/phy/smsc.c b/drivers/net/phy/smsc.c index 0bcdf927a790..f181c1d1d17b 100644 --- a/drivers/net/phy/smsc.c +++ b/drivers/net/phy/smsc.c @@ -12,6 +12,7 @@ * */ +#include #include #include #include @@ -44,6 +45,7 @@ static struct smsc_hw_stat smsc_hw_stats[] = { struct smsc_phy_priv { bool energy_enable; + struct clk *refclk; }; static int smsc_phy_config_intr(struct phy_device *phydev) @@ -253,11 +255,20 @@ static void smsc_get_stats(struct phy_device *phydev, data[i] = smsc_get_stat(phydev, i); } +static void smsc_phy_remove(struct phy_device *phydev) +{ + struct smsc_phy_priv *priv = phydev->priv; + + clk_disable_unprepare(priv->refclk); + clk_put(priv->refclk); +} + static int smsc_phy_probe(struct phy_device *phydev) { struct device *dev = &phydev->mdio.dev; struct device_node *of_node = dev->of_node; struct smsc_phy_priv *priv; + int ret; priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); if (!priv) @@ -270,6 +281,19 @@ static int smsc_phy_probe(struct phy_device *phydev) phydev->priv = priv; + /* Make clk optional to keep DTB backward compatibility. */ + priv->refclk = clk_get_optional(dev, NULL); + if (IS_ERR(priv->refclk)) + dev_err_probe(dev, PTR_ERR(priv->refclk), "Failed to request clock\n"); + + ret = clk_prepare_enable(priv->refclk); + if (ret) + return ret; + + ret = clk_set_rate(priv->refclk, 50 * 1000 * 1000); + if (ret) + return ret; + return 0; } @@ -376,6 +400,7 @@ static struct phy_driver smsc_phy_driver[] = { .flags = PHY_RST_AFTER_CLK_EN, .probe = smsc_phy_probe, + .remove = smsc_phy_remove, /* basic functions */ .read_status = lan87xx_read_status, -- cgit v1.2.3 From d65af21842f8a7821fc3b28dc043c2f92b2b312c Mon Sep 17 00:00:00 2001 From: Marco Felsch Date: Wed, 9 Sep 2020 15:45:01 +0200 Subject: net: phy: smsc: LAN8710/20: remove PHY_RST_AFTER_CLK_EN flag Don't reset the phy without respect to the PHY library state machine because this breaks the phy IRQ mode. The same behaviour can be archived now by specifying the refclk. Signed-off-by: Marco Felsch Reviewed-by: Florian Fainelli Signed-off-by: David S. Miller --- drivers/net/phy/smsc.c | 1 - 1 file changed, 1 deletion(-) (limited to 'drivers/net/phy/smsc.c') diff --git a/drivers/net/phy/smsc.c b/drivers/net/phy/smsc.c index f181c1d1d17b..ec97669be5c2 100644 --- a/drivers/net/phy/smsc.c +++ b/drivers/net/phy/smsc.c @@ -397,7 +397,6 @@ static struct phy_driver smsc_phy_driver[] = { .name = "SMSC LAN8710/LAN8720", /* PHY_BASIC_FEATURES */ - .flags = PHY_RST_AFTER_CLK_EN, .probe = smsc_phy_probe, .remove = smsc_phy_remove, -- cgit v1.2.3