All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH net-next v5 0/3] Add phylib support to smsc95xx
@ 2020-08-26 11:17 Andre Edich
  2020-08-26 11:17 ` [PATCH net-next v5 1/3] smsc95xx: remove redundant function arguments Andre Edich
                   ` (3 more replies)
  0 siblings, 4 replies; 8+ messages in thread
From: Andre Edich @ 2020-08-26 11:17 UTC (permalink / raw)
  To: netdev, UNGLinuxDriver, steve.glendinning
  Cc: Parthiban.Veerasooran, Andre Edich

To allow to probe external PHY drivers, this patch series adds use of
phylib to the smsc95xx driver.

Changes in v5:
- Removed all phy_read calls from the smsc95xx driver.

Changes in v4:
- Removed useless inline type qualifier.

Changes in v3:
- Moved all MDI-X functionality to the corresponding phy driver;
- Removed field internal_phy from a struct smsc95xx_priv;
- Initialized field is_internal of a struct phy_device;
- Kconfig: Added selection of PHYLIB and SMSC_PHY for USB_NET_SMSC95XX.

Changes in v2:
- Moved 'net' patches from here to the separate patch series;
- Removed redundant call of the phy_start_aneg after phy_start;
- Removed netif_dbg tracing "speed, duplex, lcladv, and rmtadv";
- mdiobus: added dependency from the usbnet device;
- Moved making of the MII address from 'phy_id' and 'idx' into the
  function mii_address;
- Moved direct MDIO accesses under condition 'if (pdata->internal_phy)',
  as they only need for the internal PHY;
- To be sure, that this set of patches is git-bisectable, tested each
  sub-set of patches to be functional for both, internal and external
  PHYs, including suspend/resume test for the 'devices'
  (5.7.8-1-ARCH, Raspberry Pi 3 Model B).

Andre Edich (3):
  smsc95xx: remove redundant function arguments
  smsc95xx: use usbnet->driver_priv
  smsc95xx: add phylib support

 drivers/net/phy/smsc.c     |  67 ++++++
 drivers/net/usb/Kconfig    |   2 +
 drivers/net/usb/smsc95xx.c | 475 +++++++++++++------------------------
 3 files changed, 238 insertions(+), 306 deletions(-)

-- 
2.28.0


^ permalink raw reply	[flat|nested] 8+ messages in thread

* [PATCH net-next v5 1/3] smsc95xx: remove redundant function arguments
  2020-08-26 11:17 [PATCH net-next v5 0/3] Add phylib support to smsc95xx Andre Edich
@ 2020-08-26 11:17 ` Andre Edich
  2020-08-26 11:17 ` [PATCH net-next v5 2/3] smsc95xx: use usbnet->driver_priv Andre Edich
                   ` (2 subsequent siblings)
  3 siblings, 0 replies; 8+ messages in thread
From: Andre Edich @ 2020-08-26 11:17 UTC (permalink / raw)
  To: netdev, UNGLinuxDriver, steve.glendinning
  Cc: Parthiban.Veerasooran, Andre Edich

This patch removes arguments netdev and phy_id from the functions
smsc95xx_mdio_read_nopm and smsc95xx_mdio_write_nopm.  Both removed
arguments are recovered from a new argument `struct usbnet *dev`.

Signed-off-by: Andre Edich <andre.edich@microchip.com>
---
 drivers/net/usb/smsc95xx.c | 35 +++++++++++++++++------------------
 1 file changed, 17 insertions(+), 18 deletions(-)

diff --git a/drivers/net/usb/smsc95xx.c b/drivers/net/usb/smsc95xx.c
index bb4ccbda031a..3fdf7c2b2d25 100644
--- a/drivers/net/usb/smsc95xx.c
+++ b/drivers/net/usb/smsc95xx.c
@@ -261,16 +261,18 @@ static void __smsc95xx_mdio_write(struct net_device *netdev, int phy_id,
 	mutex_unlock(&dev->phy_mutex);
 }
 
-static int smsc95xx_mdio_read_nopm(struct net_device *netdev, int phy_id,
-				   int idx)
+static int smsc95xx_mdio_read_nopm(struct usbnet *dev, int idx)
 {
-	return __smsc95xx_mdio_read(netdev, phy_id, idx, 1);
+	struct mii_if_info *mii = &dev->mii;
+
+	return __smsc95xx_mdio_read(dev->net, mii->phy_id, idx, 1);
 }
 
-static void smsc95xx_mdio_write_nopm(struct net_device *netdev, int phy_id,
-				     int idx, int regval)
+static void smsc95xx_mdio_write_nopm(struct usbnet *dev, int idx, int regval)
 {
-	__smsc95xx_mdio_write(netdev, phy_id, idx, regval, 1);
+	struct mii_if_info *mii = &dev->mii;
+
+	__smsc95xx_mdio_write(dev->net, mii->phy_id, idx, regval, 1);
 }
 
 static int smsc95xx_mdio_read(struct net_device *netdev, int phy_id, int idx)
@@ -1347,39 +1349,37 @@ static u32 smsc_crc(const u8 *buffer, size_t len, int filter)
 
 static int smsc95xx_enable_phy_wakeup_interrupts(struct usbnet *dev, u16 mask)
 {
-	struct mii_if_info *mii = &dev->mii;
 	int ret;
 
 	netdev_dbg(dev->net, "enabling PHY wakeup interrupts\n");
 
 	/* read to clear */
-	ret = smsc95xx_mdio_read_nopm(dev->net, mii->phy_id, PHY_INT_SRC);
+	ret = smsc95xx_mdio_read_nopm(dev, PHY_INT_SRC);
 	if (ret < 0)
 		return ret;
 
 	/* enable interrupt source */
-	ret = smsc95xx_mdio_read_nopm(dev->net, mii->phy_id, PHY_INT_MASK);
+	ret = smsc95xx_mdio_read_nopm(dev, PHY_INT_MASK);
 	if (ret < 0)
 		return ret;
 
 	ret |= mask;
 
-	smsc95xx_mdio_write_nopm(dev->net, mii->phy_id, PHY_INT_MASK, ret);
+	smsc95xx_mdio_write_nopm(dev, PHY_INT_MASK, ret);
 
 	return 0;
 }
 
 static int smsc95xx_link_ok_nopm(struct usbnet *dev)
 {
-	struct mii_if_info *mii = &dev->mii;
 	int ret;
 
 	/* first, a dummy read, needed to latch some MII phys */
-	ret = smsc95xx_mdio_read_nopm(dev->net, mii->phy_id, MII_BMSR);
+	ret = smsc95xx_mdio_read_nopm(dev, MII_BMSR);
 	if (ret < 0)
 		return ret;
 
-	ret = smsc95xx_mdio_read_nopm(dev->net, mii->phy_id, MII_BMSR);
+	ret = smsc95xx_mdio_read_nopm(dev, MII_BMSR);
 	if (ret < 0)
 		return ret;
 
@@ -1428,7 +1428,6 @@ static int smsc95xx_enter_suspend0(struct usbnet *dev)
 static int smsc95xx_enter_suspend1(struct usbnet *dev)
 {
 	struct smsc95xx_priv *pdata = (struct smsc95xx_priv *)(dev->data[0]);
-	struct mii_if_info *mii = &dev->mii;
 	u32 val;
 	int ret;
 
@@ -1436,17 +1435,17 @@ static int smsc95xx_enter_suspend1(struct usbnet *dev)
 	 * compatibility with non-standard link partners
 	 */
 	if (pdata->features & FEATURE_PHY_NLP_CROSSOVER)
-		smsc95xx_mdio_write_nopm(dev->net, mii->phy_id,	PHY_EDPD_CONFIG,
-			PHY_EDPD_CONFIG_DEFAULT);
+		smsc95xx_mdio_write_nopm(dev, PHY_EDPD_CONFIG,
+					 PHY_EDPD_CONFIG_DEFAULT);
 
 	/* enable energy detect power-down mode */
-	ret = smsc95xx_mdio_read_nopm(dev->net, mii->phy_id, PHY_MODE_CTRL_STS);
+	ret = smsc95xx_mdio_read_nopm(dev, PHY_MODE_CTRL_STS);
 	if (ret < 0)
 		return ret;
 
 	ret |= MODE_CTRL_STS_EDPWRDOWN_;
 
-	smsc95xx_mdio_write_nopm(dev->net, mii->phy_id, PHY_MODE_CTRL_STS, ret);
+	smsc95xx_mdio_write_nopm(dev, PHY_MODE_CTRL_STS, ret);
 
 	/* enter SUSPEND1 mode */
 	ret = smsc95xx_read_reg_nopm(dev, PM_CTRL, &val);
-- 
2.28.0


^ permalink raw reply related	[flat|nested] 8+ messages in thread

* [PATCH net-next v5 2/3] smsc95xx: use usbnet->driver_priv
  2020-08-26 11:17 [PATCH net-next v5 0/3] Add phylib support to smsc95xx Andre Edich
  2020-08-26 11:17 ` [PATCH net-next v5 1/3] smsc95xx: remove redundant function arguments Andre Edich
@ 2020-08-26 11:17 ` Andre Edich
  2020-08-26 11:17 ` [PATCH net-next v5 3/3] smsc95xx: add phylib support Andre Edich
  2020-08-28 13:49 ` [PATCH net-next v5 0/3] Add phylib support to smsc95xx David Miller
  3 siblings, 0 replies; 8+ messages in thread
From: Andre Edich @ 2020-08-26 11:17 UTC (permalink / raw)
  To: netdev, UNGLinuxDriver, steve.glendinning
  Cc: Parthiban.Veerasooran, Andre Edich

Using `void *driver_priv` instead of `unsigned long data[]` is more
straightforward way to recover the `struct smsc95xx_priv *` from the
`struct net_device *`.

Signed-off-by: Andre Edich <andre.edich@microchip.com>
---
 drivers/net/usb/smsc95xx.c | 61 +++++++++++++++++---------------------
 1 file changed, 28 insertions(+), 33 deletions(-)

diff --git a/drivers/net/usb/smsc95xx.c b/drivers/net/usb/smsc95xx.c
index 3fdf7c2b2d25..f200684875fb 100644
--- a/drivers/net/usb/smsc95xx.c
+++ b/drivers/net/usb/smsc95xx.c
@@ -457,7 +457,7 @@ static unsigned int smsc95xx_hash(char addr[ETH_ALEN])
 static void smsc95xx_set_multicast(struct net_device *netdev)
 {
 	struct usbnet *dev = netdev_priv(netdev);
-	struct smsc95xx_priv *pdata = (struct smsc95xx_priv *)(dev->data[0]);
+	struct smsc95xx_priv *pdata = dev->driver_priv;
 	unsigned long flags;
 	int ret;
 
@@ -552,7 +552,7 @@ static int smsc95xx_phy_update_flowcontrol(struct usbnet *dev, u8 duplex,
 
 static int smsc95xx_link_reset(struct usbnet *dev)
 {
-	struct smsc95xx_priv *pdata = (struct smsc95xx_priv *)(dev->data[0]);
+	struct smsc95xx_priv *pdata = dev->driver_priv;
 	struct mii_if_info *mii = &dev->mii;
 	struct ethtool_cmd ecmd = { .cmd = ETHTOOL_GSET };
 	unsigned long flags;
@@ -620,7 +620,7 @@ static void smsc95xx_status(struct usbnet *dev, struct urb *urb)
 
 static void set_carrier(struct usbnet *dev, bool link)
 {
-	struct smsc95xx_priv *pdata = (struct smsc95xx_priv *)(dev->data[0]);
+	struct smsc95xx_priv *pdata = dev->driver_priv;
 
 	if (pdata->link_ok == link)
 		return;
@@ -749,7 +749,7 @@ static void smsc95xx_ethtool_get_wol(struct net_device *net,
 				     struct ethtool_wolinfo *wolinfo)
 {
 	struct usbnet *dev = netdev_priv(net);
-	struct smsc95xx_priv *pdata = (struct smsc95xx_priv *)(dev->data[0]);
+	struct smsc95xx_priv *pdata = dev->driver_priv;
 
 	wolinfo->supported = SUPPORTED_WAKE;
 	wolinfo->wolopts = pdata->wolopts;
@@ -759,7 +759,7 @@ static int smsc95xx_ethtool_set_wol(struct net_device *net,
 				    struct ethtool_wolinfo *wolinfo)
 {
 	struct usbnet *dev = netdev_priv(net);
-	struct smsc95xx_priv *pdata = (struct smsc95xx_priv *)(dev->data[0]);
+	struct smsc95xx_priv *pdata = dev->driver_priv;
 	int ret;
 
 	if (wolinfo->wolopts & ~SUPPORTED_WAKE)
@@ -798,7 +798,7 @@ static int get_mdix_status(struct net_device *net)
 static void set_mdix_status(struct net_device *net, __u8 mdix_ctrl)
 {
 	struct usbnet *dev = netdev_priv(net);
-	struct smsc95xx_priv *pdata = (struct smsc95xx_priv *)(dev->data[0]);
+	struct smsc95xx_priv *pdata = dev->driver_priv;
 	int buf;
 
 	if ((pdata->chip_id == ID_REV_CHIP_ID_9500A_) ||
@@ -847,7 +847,7 @@ static int smsc95xx_get_link_ksettings(struct net_device *net,
 				       struct ethtool_link_ksettings *cmd)
 {
 	struct usbnet *dev = netdev_priv(net);
-	struct smsc95xx_priv *pdata = (struct smsc95xx_priv *)(dev->data[0]);
+	struct smsc95xx_priv *pdata = dev->driver_priv;
 	int retval;
 
 	retval = usbnet_get_link_ksettings(net, cmd);
@@ -862,7 +862,7 @@ static int smsc95xx_set_link_ksettings(struct net_device *net,
 				       const struct ethtool_link_ksettings *cmd)
 {
 	struct usbnet *dev = netdev_priv(net);
-	struct smsc95xx_priv *pdata = (struct smsc95xx_priv *)(dev->data[0]);
+	struct smsc95xx_priv *pdata = dev->driver_priv;
 	int retval;
 
 	if (pdata->mdix_ctrl != cmd->base.eth_tp_mdix_ctrl)
@@ -944,7 +944,7 @@ static int smsc95xx_set_mac_address(struct usbnet *dev)
 /* starts the TX path */
 static int smsc95xx_start_tx_path(struct usbnet *dev)
 {
-	struct smsc95xx_priv *pdata = (struct smsc95xx_priv *)(dev->data[0]);
+	struct smsc95xx_priv *pdata = dev->driver_priv;
 	unsigned long flags;
 	int ret;
 
@@ -964,7 +964,7 @@ static int smsc95xx_start_tx_path(struct usbnet *dev)
 /* Starts the Receive path */
 static int smsc95xx_start_rx_path(struct usbnet *dev, int in_pm)
 {
-	struct smsc95xx_priv *pdata = (struct smsc95xx_priv *)(dev->data[0]);
+	struct smsc95xx_priv *pdata = dev->driver_priv;
 	unsigned long flags;
 
 	spin_lock_irqsave(&pdata->mac_cr_lock, flags);
@@ -1021,7 +1021,7 @@ static int smsc95xx_phy_initialize(struct usbnet *dev)
 
 static int smsc95xx_reset(struct usbnet *dev)
 {
-	struct smsc95xx_priv *pdata = (struct smsc95xx_priv *)(dev->data[0]);
+	struct smsc95xx_priv *pdata = dev->driver_priv;
 	u32 read_buf, write_buf, burst_cap;
 	int ret = 0, timeout;
 
@@ -1249,7 +1249,7 @@ static const struct net_device_ops smsc95xx_netdev_ops = {
 
 static int smsc95xx_bind(struct usbnet *dev, struct usb_interface *intf)
 {
-	struct smsc95xx_priv *pdata = NULL;
+	struct smsc95xx_priv *pdata;
 	u32 val;
 	int ret;
 
@@ -1261,13 +1261,12 @@ static int smsc95xx_bind(struct usbnet *dev, struct usb_interface *intf)
 		return ret;
 	}
 
-	dev->data[0] = (unsigned long)kzalloc(sizeof(struct smsc95xx_priv),
-					      GFP_KERNEL);
-
-	pdata = (struct smsc95xx_priv *)(dev->data[0]);
+	pdata = kzalloc(sizeof(*pdata), GFP_KERNEL);
 	if (!pdata)
 		return -ENOMEM;
 
+	dev->driver_priv = pdata;
+
 	spin_lock_init(&pdata->mac_cr_lock);
 
 	/* LAN95xx devices do not alter the computed checksum of 0 to 0xffff.
@@ -1330,15 +1329,11 @@ static int smsc95xx_bind(struct usbnet *dev, struct usb_interface *intf)
 
 static void smsc95xx_unbind(struct usbnet *dev, struct usb_interface *intf)
 {
-	struct smsc95xx_priv *pdata = (struct smsc95xx_priv *)(dev->data[0]);
-
-	if (pdata) {
-		cancel_delayed_work_sync(&pdata->carrier_check);
-		netif_dbg(dev, ifdown, dev->net, "free pdata\n");
-		kfree(pdata);
-		pdata = NULL;
-		dev->data[0] = 0;
-	}
+	struct smsc95xx_priv *pdata = dev->driver_priv;
+
+	cancel_delayed_work_sync(&pdata->carrier_check);
+	netif_dbg(dev, ifdown, dev->net, "free pdata\n");
+	kfree(pdata);
 }
 
 static u32 smsc_crc(const u8 *buffer, size_t len, int filter)
@@ -1388,7 +1383,7 @@ static int smsc95xx_link_ok_nopm(struct usbnet *dev)
 
 static int smsc95xx_enter_suspend0(struct usbnet *dev)
 {
-	struct smsc95xx_priv *pdata = (struct smsc95xx_priv *)(dev->data[0]);
+	struct smsc95xx_priv *pdata = dev->driver_priv;
 	u32 val;
 	int ret;
 
@@ -1427,7 +1422,7 @@ static int smsc95xx_enter_suspend0(struct usbnet *dev)
 
 static int smsc95xx_enter_suspend1(struct usbnet *dev)
 {
-	struct smsc95xx_priv *pdata = (struct smsc95xx_priv *)(dev->data[0]);
+	struct smsc95xx_priv *pdata = dev->driver_priv;
 	u32 val;
 	int ret;
 
@@ -1474,7 +1469,7 @@ static int smsc95xx_enter_suspend1(struct usbnet *dev)
 
 static int smsc95xx_enter_suspend2(struct usbnet *dev)
 {
-	struct smsc95xx_priv *pdata = (struct smsc95xx_priv *)(dev->data[0]);
+	struct smsc95xx_priv *pdata = dev->driver_priv;
 	u32 val;
 	int ret;
 
@@ -1496,7 +1491,7 @@ static int smsc95xx_enter_suspend2(struct usbnet *dev)
 
 static int smsc95xx_enter_suspend3(struct usbnet *dev)
 {
-	struct smsc95xx_priv *pdata = (struct smsc95xx_priv *)(dev->data[0]);
+	struct smsc95xx_priv *pdata = dev->driver_priv;
 	u32 val;
 	int ret;
 
@@ -1535,7 +1530,7 @@ static int smsc95xx_enter_suspend3(struct usbnet *dev)
 
 static int smsc95xx_autosuspend(struct usbnet *dev, u32 link_up)
 {
-	struct smsc95xx_priv *pdata = (struct smsc95xx_priv *)(dev->data[0]);
+	struct smsc95xx_priv *pdata = dev->driver_priv;
 	int ret;
 
 	if (!netif_running(dev->net)) {
@@ -1583,7 +1578,7 @@ static int smsc95xx_autosuspend(struct usbnet *dev, u32 link_up)
 static int smsc95xx_suspend(struct usb_interface *intf, pm_message_t message)
 {
 	struct usbnet *dev = usb_get_intfdata(intf);
-	struct smsc95xx_priv *pdata = (struct smsc95xx_priv *)(dev->data[0]);
+	struct smsc95xx_priv *pdata = dev->driver_priv;
 	u32 val, link_up;
 	int ret;
 
@@ -1854,7 +1849,7 @@ static int smsc95xx_resume(struct usb_interface *intf)
 	u32 val;
 
 	BUG_ON(!dev);
-	pdata = (struct smsc95xx_priv *)(dev->data[0]);
+	pdata = dev->driver_priv;
 	suspend_flags = pdata->suspend_flags;
 
 	netdev_dbg(dev->net, "resume suspend_flags=0x%02x\n", suspend_flags);
@@ -2074,7 +2069,7 @@ static struct sk_buff *smsc95xx_tx_fixup(struct usbnet *dev,
 
 static int smsc95xx_manage_power(struct usbnet *dev, int on)
 {
-	struct smsc95xx_priv *pdata = (struct smsc95xx_priv *)(dev->data[0]);
+	struct smsc95xx_priv *pdata = dev->driver_priv;
 
 	dev->intf->needs_remote_wakeup = on;
 
-- 
2.28.0


^ permalink raw reply related	[flat|nested] 8+ messages in thread

* [PATCH net-next v5 3/3] smsc95xx: add phylib support
  2020-08-26 11:17 [PATCH net-next v5 0/3] Add phylib support to smsc95xx Andre Edich
  2020-08-26 11:17 ` [PATCH net-next v5 1/3] smsc95xx: remove redundant function arguments Andre Edich
  2020-08-26 11:17 ` [PATCH net-next v5 2/3] smsc95xx: use usbnet->driver_priv Andre Edich
@ 2020-08-26 11:17 ` Andre Edich
  2021-03-28 19:59   ` Måns Rullgård
  2020-08-28 13:49 ` [PATCH net-next v5 0/3] Add phylib support to smsc95xx David Miller
  3 siblings, 1 reply; 8+ messages in thread
From: Andre Edich @ 2020-08-26 11:17 UTC (permalink / raw)
  To: netdev, UNGLinuxDriver, steve.glendinning
  Cc: Parthiban.Veerasooran, Andre Edich

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 <andre.edich@microchip.com>
---
 drivers/net/phy/smsc.c     |  67 +++++++
 drivers/net/usb/Kconfig    |   2 +
 drivers/net/usb/smsc95xx.c | 399 +++++++++++++------------------------
 3 files changed, 203 insertions(+), 265 deletions(-)

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 <linux/netdevice.h>
 #include <linux/smscphy.h>
 
+/* 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,
diff --git a/drivers/net/usb/Kconfig b/drivers/net/usb/Kconfig
index a7fbc3ccd29e..0863f01937b3 100644
--- a/drivers/net/usb/Kconfig
+++ b/drivers/net/usb/Kconfig
@@ -345,6 +345,8 @@ config USB_NET_SMSC75XX
 config USB_NET_SMSC95XX
 	tristate "SMSC LAN95XX based USB 2.0 10/100 ethernet devices"
 	depends on USB_USBNET
+	select PHYLIB
+	select SMSC_PHY
 	select BITREVERSE
 	select CRC16
 	select CRC32
diff --git a/drivers/net/usb/smsc95xx.c b/drivers/net/usb/smsc95xx.c
index f200684875fb..601fb40a2a0a 100644
--- a/drivers/net/usb/smsc95xx.c
+++ b/drivers/net/usb/smsc95xx.c
@@ -18,10 +18,12 @@
 #include <linux/usb/usbnet.h>
 #include <linux/slab.h>
 #include <linux/of_net.h>
+#include <linux/mdio.h>
+#include <linux/phy.h>
 #include "smsc95xx.h"
 
 #define SMSC_CHIPNAME			"smsc95xx"
-#define SMSC_DRIVER_VERSION		"1.0.6"
+#define SMSC_DRIVER_VERSION		"2.0.0"
 #define HS_USB_PKT_SIZE			(512)
 #define FS_USB_PKT_SIZE			(64)
 #define DEFAULT_HS_BURST_CAP_SIZE	(16 * 1024 + 5 * HS_USB_PKT_SIZE)
@@ -49,10 +51,7 @@
 #define SUSPEND_ALLMODES		(SUSPEND_SUSPEND0 | SUSPEND_SUSPEND1 | \
 					 SUSPEND_SUSPEND2 | SUSPEND_SUSPEND3)
 
-#define CARRIER_CHECK_DELAY (2 * HZ)
-
 struct smsc95xx_priv {
-	u32 chip_id;
 	u32 mac_cr;
 	u32 hash_hi;
 	u32 hash_lo;
@@ -60,10 +59,8 @@ struct smsc95xx_priv {
 	spinlock_t mac_cr_lock;
 	u8 features;
 	u8 suspend_flags;
-	u8 mdix_ctrl;
-	bool link_ok;
-	struct delayed_work carrier_check;
-	struct usbnet *dev;
+	struct mii_bus *mdiobus;
+	struct phy_device *phydev;
 };
 
 static bool turbo_mode = true;
@@ -173,10 +170,14 @@ static int __must_check __smsc95xx_phy_wait_not_busy(struct usbnet *dev,
 	return -EIO;
 }
 
-static int __smsc95xx_mdio_read(struct net_device *netdev, int phy_id, int idx,
+static u32 mii_address_cmd(int phy_id, int idx, u16 op)
+{
+	return (phy_id & 0x1f) << 11 | (idx & 0x1f) << 6 | op;
+}
+
+static int __smsc95xx_mdio_read(struct usbnet *dev, int phy_id, int idx,
 				int in_pm)
 {
-	struct usbnet *dev = netdev_priv(netdev);
 	u32 val, addr;
 	int ret;
 
@@ -185,14 +186,12 @@ static int __smsc95xx_mdio_read(struct net_device *netdev, int phy_id, int idx,
 	/* confirm MII not busy */
 	ret = __smsc95xx_phy_wait_not_busy(dev, in_pm);
 	if (ret < 0) {
-		netdev_warn(dev->net, "MII is busy in smsc95xx_mdio_read\n");
+		netdev_warn(dev->net, "%s: MII is busy\n", __func__);
 		goto done;
 	}
 
 	/* set the address, index & direction (read from PHY) */
-	phy_id &= dev->mii.phy_id_mask;
-	idx &= dev->mii.reg_num_mask;
-	addr = (phy_id << 11) | (idx << 6) | MII_READ_ | MII_BUSY_;
+	addr = mii_address_cmd(phy_id, idx, MII_READ_ | MII_BUSY_);
 	ret = __smsc95xx_write_reg(dev, MII_ADDR, addr, in_pm);
 	if (ret < 0) {
 		netdev_warn(dev->net, "Error writing MII_ADDR\n");
@@ -218,10 +217,9 @@ static int __smsc95xx_mdio_read(struct net_device *netdev, int phy_id, int idx,
 	return ret;
 }
 
-static void __smsc95xx_mdio_write(struct net_device *netdev, int phy_id,
+static void __smsc95xx_mdio_write(struct usbnet *dev, int phy_id,
 				  int idx, int regval, int in_pm)
 {
-	struct usbnet *dev = netdev_priv(netdev);
 	u32 val, addr;
 	int ret;
 
@@ -230,7 +228,7 @@ static void __smsc95xx_mdio_write(struct net_device *netdev, int phy_id,
 	/* confirm MII not busy */
 	ret = __smsc95xx_phy_wait_not_busy(dev, in_pm);
 	if (ret < 0) {
-		netdev_warn(dev->net, "MII is busy in smsc95xx_mdio_write\n");
+		netdev_warn(dev->net, "%s: MII is busy\n", __func__);
 		goto done;
 	}
 
@@ -242,9 +240,7 @@ static void __smsc95xx_mdio_write(struct net_device *netdev, int phy_id,
 	}
 
 	/* set the address, index & direction (write to PHY) */
-	phy_id &= dev->mii.phy_id_mask;
-	idx &= dev->mii.reg_num_mask;
-	addr = (phy_id << 11) | (idx << 6) | MII_WRITE_ | MII_BUSY_;
+	addr = mii_address_cmd(phy_id, idx, MII_WRITE_ | MII_BUSY_);
 	ret = __smsc95xx_write_reg(dev, MII_ADDR, addr, in_pm);
 	if (ret < 0) {
 		netdev_warn(dev->net, "Error writing MII_ADDR\n");
@@ -263,27 +259,32 @@ static void __smsc95xx_mdio_write(struct net_device *netdev, int phy_id,
 
 static int smsc95xx_mdio_read_nopm(struct usbnet *dev, int idx)
 {
-	struct mii_if_info *mii = &dev->mii;
+	struct smsc95xx_priv *pdata = dev->driver_priv;
 
-	return __smsc95xx_mdio_read(dev->net, mii->phy_id, idx, 1);
+	return __smsc95xx_mdio_read(dev, pdata->phydev->mdio.addr, idx, 1);
 }
 
 static void smsc95xx_mdio_write_nopm(struct usbnet *dev, int idx, int regval)
 {
-	struct mii_if_info *mii = &dev->mii;
+	struct smsc95xx_priv *pdata = dev->driver_priv;
 
-	__smsc95xx_mdio_write(dev->net, mii->phy_id, idx, regval, 1);
+	__smsc95xx_mdio_write(dev, pdata->phydev->mdio.addr, idx, regval, 1);
 }
 
-static int smsc95xx_mdio_read(struct net_device *netdev, int phy_id, int idx)
+static int smsc95xx_mdiobus_read(struct mii_bus *bus, int phy_id, int idx)
 {
-	return __smsc95xx_mdio_read(netdev, phy_id, idx, 0);
+	struct usbnet *dev = bus->priv;
+
+	return __smsc95xx_mdio_read(dev, phy_id, idx, 0);
 }
 
-static void smsc95xx_mdio_write(struct net_device *netdev, int phy_id, int idx,
-				int regval)
+static int smsc95xx_mdiobus_write(struct mii_bus *bus, int phy_id, int idx,
+				  u16 regval)
 {
-	__smsc95xx_mdio_write(netdev, phy_id, idx, regval, 0);
+	struct usbnet *dev = bus->priv;
+
+	__smsc95xx_mdio_write(dev, phy_id, idx, regval, 0);
+	return 0;
 }
 
 static int __must_check smsc95xx_wait_eeprom(struct usbnet *dev)
@@ -513,22 +514,23 @@ static void smsc95xx_set_multicast(struct net_device *netdev)
 		netdev_warn(dev->net, "failed to initiate async write to MAC_CR\n");
 }
 
-static int smsc95xx_phy_update_flowcontrol(struct usbnet *dev, u8 duplex,
-					   u16 lcladv, u16 rmtadv)
+static int smsc95xx_phy_update_flowcontrol(struct usbnet *dev)
 {
 	u32 flow = 0, afc_cfg;
+	struct smsc95xx_priv *pdata = dev->driver_priv;
+	bool tx_pause, rx_pause;
 
 	int ret = smsc95xx_read_reg(dev, AFC_CFG, &afc_cfg);
 	if (ret < 0)
 		return ret;
 
-	if (duplex == DUPLEX_FULL) {
-		u8 cap = mii_resolve_flowctrl_fdx(lcladv, rmtadv);
+	if (pdata->phydev->duplex == DUPLEX_FULL) {
+		phy_get_pause(pdata->phydev, &tx_pause, &rx_pause);
 
-		if (cap & FLOW_CTRL_RX)
+		if (rx_pause)
 			flow = 0xFFFF0002;
 
-		if (cap & FLOW_CTRL_TX) {
+		if (tx_pause) {
 			afc_cfg |= 0xF;
 			flow |= 0xFFFF0000;
 		} else {
@@ -536,8 +538,8 @@ static int smsc95xx_phy_update_flowcontrol(struct usbnet *dev, u8 duplex,
 		}
 
 		netif_dbg(dev, link, dev->net, "rx pause %s, tx pause %s\n",
-				   cap & FLOW_CTRL_RX ? "enabled" : "disabled",
-				   cap & FLOW_CTRL_TX ? "enabled" : "disabled");
+			  rx_pause ? "enabled" : "disabled",
+			  tx_pause ? "enabled" : "disabled");
 	} else {
 		netif_dbg(dev, link, dev->net, "half duplex\n");
 		afc_cfg |= 0xF;
@@ -553,32 +555,15 @@ static int smsc95xx_phy_update_flowcontrol(struct usbnet *dev, u8 duplex,
 static int smsc95xx_link_reset(struct usbnet *dev)
 {
 	struct smsc95xx_priv *pdata = dev->driver_priv;
-	struct mii_if_info *mii = &dev->mii;
-	struct ethtool_cmd ecmd = { .cmd = ETHTOOL_GSET };
 	unsigned long flags;
-	u16 lcladv, rmtadv;
 	int ret;
 
-	/* clear interrupt status */
-	ret = smsc95xx_mdio_read(dev->net, mii->phy_id, PHY_INT_SRC);
-	if (ret < 0)
-		return ret;
-
 	ret = smsc95xx_write_reg(dev, INT_STS, INT_STS_CLEAR_ALL_);
 	if (ret < 0)
 		return ret;
 
-	mii_check_media(mii, 1, 1);
-	mii_ethtool_gset(&dev->mii, &ecmd);
-	lcladv = smsc95xx_mdio_read(dev->net, mii->phy_id, MII_ADVERTISE);
-	rmtadv = smsc95xx_mdio_read(dev->net, mii->phy_id, MII_LPA);
-
-	netif_dbg(dev, link, dev->net,
-		  "speed: %u duplex: %d lcladv: %04x rmtadv: %04x\n",
-		  ethtool_cmd_speed(&ecmd), ecmd.duplex, lcladv, rmtadv);
-
 	spin_lock_irqsave(&pdata->mac_cr_lock, flags);
-	if (ecmd.duplex != DUPLEX_FULL) {
+	if (pdata->phydev->duplex != DUPLEX_FULL) {
 		pdata->mac_cr &= ~MAC_CR_FDPX_;
 		pdata->mac_cr |= MAC_CR_RCVOWN_;
 	} else {
@@ -591,7 +576,7 @@ static int smsc95xx_link_reset(struct usbnet *dev)
 	if (ret < 0)
 		return ret;
 
-	ret = smsc95xx_phy_update_flowcontrol(dev, ecmd.duplex, lcladv, rmtadv);
+	ret = smsc95xx_phy_update_flowcontrol(dev);
 	if (ret < 0)
 		netdev_warn(dev->net, "Error updating PHY flow control\n");
 
@@ -618,44 +603,6 @@ static void smsc95xx_status(struct usbnet *dev, struct urb *urb)
 			    intdata);
 }
 
-static void set_carrier(struct usbnet *dev, bool link)
-{
-	struct smsc95xx_priv *pdata = dev->driver_priv;
-
-	if (pdata->link_ok == link)
-		return;
-
-	pdata->link_ok = link;
-
-	if (link)
-		usbnet_link_change(dev, 1, 0);
-	else
-		usbnet_link_change(dev, 0, 0);
-}
-
-static void check_carrier(struct work_struct *work)
-{
-	struct smsc95xx_priv *pdata = container_of(work, struct smsc95xx_priv,
-						carrier_check.work);
-	struct usbnet *dev = pdata->dev;
-	int ret;
-
-	if (pdata->suspend_flags != 0)
-		return;
-
-	ret = smsc95xx_mdio_read(dev->net, dev->mii.phy_id, MII_BMSR);
-	if (ret < 0) {
-		netdev_warn(dev->net, "Failed to read MII_BMSR\n");
-		return;
-	}
-	if (ret & BMSR_LSTATUS)
-		set_carrier(dev, 1);
-	else
-		set_carrier(dev, 0);
-
-	schedule_delayed_work(&pdata->carrier_check, CARRIER_CHECK_DELAY);
-}
-
 /* Enable or disable Tx & Rx checksum offload engines */
 static int smsc95xx_set_features(struct net_device *netdev,
 	netdev_features_t features)
@@ -774,108 +721,15 @@ static int smsc95xx_ethtool_set_wol(struct net_device *net,
 	return ret;
 }
 
-static int get_mdix_status(struct net_device *net)
-{
-	struct usbnet *dev = netdev_priv(net);
-	u32 val;
-	int buf;
-
-	buf = smsc95xx_mdio_read(dev->net, dev->mii.phy_id, SPECIAL_CTRL_STS);
-	if (buf & SPECIAL_CTRL_STS_OVRRD_AMDIX_) {
-		if (buf & SPECIAL_CTRL_STS_AMDIX_ENABLE_)
-			return ETH_TP_MDI_AUTO;
-		else if (buf & SPECIAL_CTRL_STS_AMDIX_STATE_)
-			return ETH_TP_MDI_X;
-	} else {
-		buf = smsc95xx_read_reg(dev, STRAP_STATUS, &val);
-		if (val & STRAP_STATUS_AMDIX_EN_)
-			return ETH_TP_MDI_AUTO;
-	}
-
-	return ETH_TP_MDI;
-}
-
-static void set_mdix_status(struct net_device *net, __u8 mdix_ctrl)
-{
-	struct usbnet *dev = netdev_priv(net);
-	struct smsc95xx_priv *pdata = dev->driver_priv;
-	int buf;
-
-	if ((pdata->chip_id == ID_REV_CHIP_ID_9500A_) ||
-	    (pdata->chip_id == ID_REV_CHIP_ID_9530_) ||
-	    (pdata->chip_id == ID_REV_CHIP_ID_89530_) ||
-	    (pdata->chip_id == ID_REV_CHIP_ID_9730_)) {
-		/* Extend Manual AutoMDIX timer for 9500A/9500Ai */
-		buf = smsc95xx_mdio_read(dev->net, dev->mii.phy_id,
-					 PHY_EDPD_CONFIG);
-		buf |= PHY_EDPD_CONFIG_EXT_CROSSOVER_;
-		smsc95xx_mdio_write(dev->net, dev->mii.phy_id,
-				    PHY_EDPD_CONFIG, buf);
-	}
-
-	if (mdix_ctrl == ETH_TP_MDI) {
-		buf = smsc95xx_mdio_read(dev->net, dev->mii.phy_id,
-					 SPECIAL_CTRL_STS);
-		buf |= SPECIAL_CTRL_STS_OVRRD_AMDIX_;
-		buf &= ~(SPECIAL_CTRL_STS_AMDIX_ENABLE_ |
-			 SPECIAL_CTRL_STS_AMDIX_STATE_);
-		smsc95xx_mdio_write(dev->net, dev->mii.phy_id,
-				    SPECIAL_CTRL_STS, buf);
-	} else if (mdix_ctrl == ETH_TP_MDI_X) {
-		buf = smsc95xx_mdio_read(dev->net, dev->mii.phy_id,
-					 SPECIAL_CTRL_STS);
-		buf |= SPECIAL_CTRL_STS_OVRRD_AMDIX_;
-		buf &= ~(SPECIAL_CTRL_STS_AMDIX_ENABLE_ |
-			 SPECIAL_CTRL_STS_AMDIX_STATE_);
-		buf |= SPECIAL_CTRL_STS_AMDIX_STATE_;
-		smsc95xx_mdio_write(dev->net, dev->mii.phy_id,
-				    SPECIAL_CTRL_STS, buf);
-	} else if (mdix_ctrl == ETH_TP_MDI_AUTO) {
-		buf = smsc95xx_mdio_read(dev->net, dev->mii.phy_id,
-					 SPECIAL_CTRL_STS);
-		buf &= ~SPECIAL_CTRL_STS_OVRRD_AMDIX_;
-		buf &= ~(SPECIAL_CTRL_STS_AMDIX_ENABLE_ |
-			 SPECIAL_CTRL_STS_AMDIX_STATE_);
-		buf |= SPECIAL_CTRL_STS_AMDIX_ENABLE_;
-		smsc95xx_mdio_write(dev->net, dev->mii.phy_id,
-				    SPECIAL_CTRL_STS, buf);
-	}
-	pdata->mdix_ctrl = mdix_ctrl;
-}
-
-static int smsc95xx_get_link_ksettings(struct net_device *net,
-				       struct ethtool_link_ksettings *cmd)
+static u32 smsc95xx_get_link(struct net_device *net)
 {
-	struct usbnet *dev = netdev_priv(net);
-	struct smsc95xx_priv *pdata = dev->driver_priv;
-	int retval;
-
-	retval = usbnet_get_link_ksettings(net, cmd);
-
-	cmd->base.eth_tp_mdix = pdata->mdix_ctrl;
-	cmd->base.eth_tp_mdix_ctrl = pdata->mdix_ctrl;
-
-	return retval;
-}
-
-static int smsc95xx_set_link_ksettings(struct net_device *net,
-				       const struct ethtool_link_ksettings *cmd)
-{
-	struct usbnet *dev = netdev_priv(net);
-	struct smsc95xx_priv *pdata = dev->driver_priv;
-	int retval;
-
-	if (pdata->mdix_ctrl != cmd->base.eth_tp_mdix_ctrl)
-		set_mdix_status(net, cmd->base.eth_tp_mdix_ctrl);
-
-	retval = usbnet_set_link_ksettings(net, cmd);
-
-	return retval;
+	phy_read_status(net->phydev);
+	return net->phydev->link;
 }
 
 static const struct ethtool_ops smsc95xx_ethtool_ops = {
-	.get_link	= usbnet_get_link,
-	.nway_reset	= usbnet_nway_reset,
+	.get_link	= smsc95xx_get_link,
+	.nway_reset	= phy_ethtool_nway_reset,
 	.get_drvinfo	= usbnet_get_drvinfo,
 	.get_msglevel	= usbnet_get_msglevel,
 	.set_msglevel	= usbnet_set_msglevel,
@@ -886,19 +740,17 @@ static const struct ethtool_ops smsc95xx_ethtool_ops = {
 	.get_regs	= smsc95xx_ethtool_getregs,
 	.get_wol	= smsc95xx_ethtool_get_wol,
 	.set_wol	= smsc95xx_ethtool_set_wol,
-	.get_link_ksettings	= smsc95xx_get_link_ksettings,
-	.set_link_ksettings	= smsc95xx_set_link_ksettings,
+	.get_link_ksettings	= phy_ethtool_get_link_ksettings,
+	.set_link_ksettings	= phy_ethtool_set_link_ksettings,
 	.get_ts_info	= ethtool_op_get_ts_info,
 };
 
 static int smsc95xx_ioctl(struct net_device *netdev, struct ifreq *rq, int cmd)
 {
-	struct usbnet *dev = netdev_priv(netdev);
-
 	if (!netif_running(netdev))
 		return -EINVAL;
 
-	return generic_mii_ioctl(&dev->mii, if_mii(rq), cmd, NULL);
+	return phy_mii_ioctl(netdev->phydev, rq, cmd);
 }
 
 static void smsc95xx_init_mac_address(struct usbnet *dev)
@@ -974,51 +826,6 @@ static int smsc95xx_start_rx_path(struct usbnet *dev, int in_pm)
 	return __smsc95xx_write_reg(dev, MAC_CR, pdata->mac_cr, in_pm);
 }
 
-static int smsc95xx_phy_initialize(struct usbnet *dev)
-{
-	int bmcr, ret, timeout = 0;
-
-	/* Initialize MII structure */
-	dev->mii.dev = dev->net;
-	dev->mii.mdio_read = smsc95xx_mdio_read;
-	dev->mii.mdio_write = smsc95xx_mdio_write;
-	dev->mii.phy_id_mask = 0x1f;
-	dev->mii.reg_num_mask = 0x1f;
-	dev->mii.phy_id = SMSC95XX_INTERNAL_PHY_ID;
-
-	/* reset phy and wait for reset to complete */
-	smsc95xx_mdio_write(dev->net, dev->mii.phy_id, MII_BMCR, BMCR_RESET);
-
-	do {
-		msleep(10);
-		bmcr = smsc95xx_mdio_read(dev->net, dev->mii.phy_id, MII_BMCR);
-		timeout++;
-	} while ((bmcr & BMCR_RESET) && (timeout < 100));
-
-	if (timeout >= 100) {
-		netdev_warn(dev->net, "timeout on PHY Reset");
-		return -EIO;
-	}
-
-	smsc95xx_mdio_write(dev->net, dev->mii.phy_id, MII_ADVERTISE,
-		ADVERTISE_ALL | ADVERTISE_CSMA | ADVERTISE_PAUSE_CAP |
-		ADVERTISE_PAUSE_ASYM);
-
-	/* read to clear */
-	ret = smsc95xx_mdio_read(dev->net, dev->mii.phy_id, PHY_INT_SRC);
-	if (ret < 0) {
-		netdev_warn(dev->net, "Failed to read PHY_INT_SRC during init\n");
-		return ret;
-	}
-
-	smsc95xx_mdio_write(dev->net, dev->mii.phy_id, PHY_INT_MASK,
-		PHY_INT_MASK_DEFAULT_);
-	mii_nway_restart(&dev->mii);
-
-	netif_dbg(dev, ifup, dev->net, "phy initialised successfully\n");
-	return 0;
-}
-
 static int smsc95xx_reset(struct usbnet *dev)
 {
 	struct smsc95xx_priv *pdata = dev->driver_priv;
@@ -1200,12 +1007,6 @@ static int smsc95xx_reset(struct usbnet *dev)
 
 	smsc95xx_set_multicast(dev->net);
 
-	ret = smsc95xx_phy_initialize(dev);
-	if (ret < 0) {
-		netdev_warn(dev->net, "Failed to init PHY\n");
-		return ret;
-	}
-
 	ret = smsc95xx_read_reg(dev, INT_EP_CTL, &read_buf);
 	if (ret < 0)
 		return ret;
@@ -1250,6 +1051,7 @@ static const struct net_device_ops smsc95xx_netdev_ops = {
 static int smsc95xx_bind(struct usbnet *dev, struct usb_interface *intf)
 {
 	struct smsc95xx_priv *pdata;
+	bool is_internal_phy;
 	u32 val;
 	int ret;
 
@@ -1291,15 +1093,50 @@ static int smsc95xx_bind(struct usbnet *dev, struct usb_interface *intf)
 	if (ret)
 		goto free_pdata;
 
+	pdata->mdiobus = mdiobus_alloc();
+	if (!pdata->mdiobus) {
+		ret = -ENOMEM;
+		goto free_pdata;
+	}
+
+	ret = smsc95xx_read_reg(dev, HW_CFG, &val);
+	if (ret < 0)
+		goto free_mdio;
+
+	is_internal_phy = !(val & HW_CFG_PSEL_);
+	if (is_internal_phy)
+		pdata->mdiobus->phy_mask = ~(1u << SMSC95XX_INTERNAL_PHY_ID);
+
+	pdata->mdiobus->priv = dev;
+	pdata->mdiobus->read = smsc95xx_mdiobus_read;
+	pdata->mdiobus->write = smsc95xx_mdiobus_write;
+	pdata->mdiobus->name = "smsc95xx-mdiobus";
+	pdata->mdiobus->parent = &dev->udev->dev;
+
+	snprintf(pdata->mdiobus->id, ARRAY_SIZE(pdata->mdiobus->id),
+		 "usb-%03d:%03d", dev->udev->bus->busnum, dev->udev->devnum);
+
+	ret = mdiobus_register(pdata->mdiobus);
+	if (ret) {
+		netdev_err(dev->net, "Could not register MDIO bus\n");
+		goto free_mdio;
+	}
+
+	pdata->phydev = phy_find_first(pdata->mdiobus);
+	if (!pdata->phydev) {
+		netdev_err(dev->net, "no PHY found\n");
+		ret = -ENODEV;
+		goto unregister_mdio;
+	}
+
+	pdata->phydev->is_internal = is_internal_phy;
+
 	/* detect device revision as different features may be available */
 	ret = smsc95xx_read_reg(dev, ID_REV, &val);
 	if (ret < 0)
-		goto free_pdata;
+		goto unregister_mdio;
 
 	val >>= 16;
-	pdata->chip_id = val;
-	pdata->mdix_ctrl = get_mdix_status(dev->net);
-
 	if ((val == ID_REV_CHIP_ID_9500A_) || (val == ID_REV_CHIP_ID_9530_) ||
 	    (val == ID_REV_CHIP_ID_89530_) || (val == ID_REV_CHIP_ID_9730_))
 		pdata->features = (FEATURE_8_WAKEUP_FILTERS |
@@ -1315,12 +1152,13 @@ static int smsc95xx_bind(struct usbnet *dev, struct usb_interface *intf)
 	dev->net->min_mtu = ETH_MIN_MTU;
 	dev->net->max_mtu = ETH_DATA_LEN;
 	dev->hard_mtu = dev->net->mtu + dev->net->hard_header_len;
+	return 0;
 
-	pdata->dev = dev;
-	INIT_DELAYED_WORK(&pdata->carrier_check, check_carrier);
-	schedule_delayed_work(&pdata->carrier_check, CARRIER_CHECK_DELAY);
+unregister_mdio:
+	mdiobus_unregister(pdata->mdiobus);
 
-	return 0;
+free_mdio:
+	mdiobus_free(pdata->mdiobus);
 
 free_pdata:
 	kfree(pdata);
@@ -1331,11 +1169,47 @@ static void smsc95xx_unbind(struct usbnet *dev, struct usb_interface *intf)
 {
 	struct smsc95xx_priv *pdata = dev->driver_priv;
 
-	cancel_delayed_work_sync(&pdata->carrier_check);
+	mdiobus_unregister(pdata->mdiobus);
+	mdiobus_free(pdata->mdiobus);
 	netif_dbg(dev, ifdown, dev->net, "free pdata\n");
 	kfree(pdata);
 }
 
+static void smsc95xx_handle_link_change(struct net_device *net)
+{
+	phy_print_status(net->phydev);
+}
+
+static int smsc95xx_start_phy(struct usbnet *dev)
+{
+	struct smsc95xx_priv *pdata = dev->driver_priv;
+	struct net_device *net = dev->net;
+	int ret;
+
+	ret = smsc95xx_reset(dev);
+	if (ret < 0)
+		return ret;
+
+	ret = phy_connect_direct(net, pdata->phydev,
+				 &smsc95xx_handle_link_change,
+				 PHY_INTERFACE_MODE_MII);
+	if (ret) {
+		netdev_err(net, "can't attach PHY to %s\n", pdata->mdiobus->id);
+		return ret;
+	}
+
+	phy_attached_info(net->phydev);
+	phy_start(net->phydev);
+	return 0;
+}
+
+static int smsc95xx_disconnect_phy(struct usbnet *dev)
+{
+	phy_stop(dev->net->phydev);
+	phy_disconnect(dev->net->phydev);
+	return 0;
+}
+
 static u32 smsc_crc(const u8 *buffer, size_t len, int filter)
 {
 	u32 crc = bitrev16(crc16(0xFFFF, buffer, len));
@@ -1588,8 +1462,6 @@ static int smsc95xx_suspend(struct usb_interface *intf, pm_message_t message)
 		return ret;
 	}
 
-	cancel_delayed_work_sync(&pdata->carrier_check);
-
 	if (pdata->suspend_flags) {
 		netdev_warn(dev->net, "error during last resume\n");
 		pdata->suspend_flags = 0;
@@ -1833,10 +1705,6 @@ static int smsc95xx_suspend(struct usb_interface *intf, pm_message_t message)
 	if (ret && PMSG_IS_AUTO(message))
 		usbnet_resume(intf);
 
-	if (ret)
-		schedule_delayed_work(&pdata->carrier_check,
-				      CARRIER_CHECK_DELAY);
-
 	return ret;
 }
 
@@ -1856,7 +1724,6 @@ static int smsc95xx_resume(struct usb_interface *intf)
 
 	/* do this first to ensure it's cleared even in error case */
 	pdata->suspend_flags = 0;
-	schedule_delayed_work(&pdata->carrier_check, CARRIER_CHECK_DELAY);
 
 	if (suspend_flags & SUSPEND_ALLMODES) {
 		/* clear wake-up sources */
@@ -1887,6 +1754,7 @@ static int smsc95xx_resume(struct usb_interface *intf)
 	if (ret < 0)
 		netdev_warn(dev->net, "usbnet_resume error\n");
 
+	phy_init_hw(pdata->phydev);
 	return ret;
 }
 
@@ -2092,7 +1960,8 @@ static const struct driver_info smsc95xx_info = {
 	.bind		= smsc95xx_bind,
 	.unbind		= smsc95xx_unbind,
 	.link_reset	= smsc95xx_link_reset,
-	.reset		= smsc95xx_reset,
+	.reset		= smsc95xx_start_phy,
+	.stop		= smsc95xx_disconnect_phy,
 	.rx_fixup	= smsc95xx_rx_fixup,
 	.tx_fixup	= smsc95xx_tx_fixup,
 	.status		= smsc95xx_status,
-- 
2.28.0


^ permalink raw reply related	[flat|nested] 8+ messages in thread

* Re: [PATCH net-next v5 0/3] Add phylib support to smsc95xx
  2020-08-26 11:17 [PATCH net-next v5 0/3] Add phylib support to smsc95xx Andre Edich
                   ` (2 preceding siblings ...)
  2020-08-26 11:17 ` [PATCH net-next v5 3/3] smsc95xx: add phylib support Andre Edich
@ 2020-08-28 13:49 ` David Miller
  3 siblings, 0 replies; 8+ messages in thread
From: David Miller @ 2020-08-28 13:49 UTC (permalink / raw)
  To: andre.edich
  Cc: netdev, UNGLinuxDriver, steve.glendinning, Parthiban.Veerasooran

From: Andre Edich <andre.edich@microchip.com>
Date: Wed, 26 Aug 2020 13:17:14 +0200

> To allow to probe external PHY drivers, this patch series adds use of
> phylib to the smsc95xx driver.
 ...

Series applied, thank you.

^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: [PATCH net-next v5 3/3] smsc95xx: add phylib support
  2020-08-26 11:17 ` [PATCH net-next v5 3/3] smsc95xx: add phylib support Andre Edich
@ 2021-03-28 19:59   ` Måns Rullgård
  2021-03-28 20:31     ` Heiner Kallweit
  0 siblings, 1 reply; 8+ messages in thread
From: Måns Rullgård @ 2021-03-28 19:59 UTC (permalink / raw)
  To: Andre Edich
  Cc: netdev, UNGLinuxDriver, steve.glendinning, Parthiban.Veerasooran

Andre Edich <andre.edich@microchip.com> writes:

> 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 <andre.edich@microchip.com>
> ---
>  drivers/net/phy/smsc.c     |  67 +++++++
>  drivers/net/usb/Kconfig    |   2 +
>  drivers/net/usb/smsc95xx.c | 399 +++++++++++++------------------------
>  3 files changed, 203 insertions(+), 265 deletions(-)
>
> 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 <linux/netdevice.h>
>  #include <linux/smscphy.h>
>
> +/* 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,

This change seems to be causing some trouble I'm seeing with a LAN8710A.
Specifically lan87xx_config_aneg_ext() writes to register 16 which is
not documented for LAN8710A (nor for LAN8720A).  The effect is somewhat
random.  Sometimes, the device drops to 10 Mbps while the kernel still
reports the link speed as 100 Mbps.  Other times, it doesn't work at
all.  Everything works if I change config_aneg to lan87xx_config_aneg,
like this:

diff --git a/drivers/net/phy/smsc.c b/drivers/net/phy/smsc.c
index 10722fed666d..07c0a7e4a350 100644
--- a/drivers/net/phy/smsc.c
+++ b/drivers/net/phy/smsc.c
@@ -408,7 +408,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,
+       .config_aneg    = lan87xx_config_aneg,
 
        /* IRQ related */
        .ack_interrupt  = smsc_phy_ack_interrupt,

The internal phy of the LAN9500A does have a register 16 with
documentation matching the usage in this patch.  Unfortunately, there
doesn't seem to be any way of distinguishing this from the LAN8710A
based on register values.  Anyone got any clever ideas?

-- 
Måns Rullgård

^ permalink raw reply related	[flat|nested] 8+ messages in thread

* Re: [PATCH net-next v5 3/3] smsc95xx: add phylib support
  2021-03-28 19:59   ` Måns Rullgård
@ 2021-03-28 20:31     ` Heiner Kallweit
  2021-03-28 21:33       ` Måns Rullgård
  0 siblings, 1 reply; 8+ messages in thread
From: Heiner Kallweit @ 2021-03-28 20:31 UTC (permalink / raw)
  To: Måns Rullgård, Andre Edich
  Cc: netdev, UNGLinuxDriver, steve.glendinning, Parthiban.Veerasooran

On 28.03.2021 21:59, Måns Rullgård wrote:
> Andre Edich <andre.edich@microchip.com> writes:
> 
>> 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 <andre.edich@microchip.com>
>> ---
>>  drivers/net/phy/smsc.c     |  67 +++++++
>>  drivers/net/usb/Kconfig    |   2 +
>>  drivers/net/usb/smsc95xx.c | 399 +++++++++++++------------------------
>>  3 files changed, 203 insertions(+), 265 deletions(-)
>>
>> 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 <linux/netdevice.h>
>>  #include <linux/smscphy.h>
>>
>> +/* 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,
> 
> This change seems to be causing some trouble I'm seeing with a LAN8710A.
> Specifically lan87xx_config_aneg_ext() writes to register 16 which is
> not documented for LAN8710A (nor for LAN8720A).  The effect is somewhat
> random.  Sometimes, the device drops to 10 Mbps while the kernel still
> reports the link speed as 100 Mbps.  Other times, it doesn't work at
> all.  Everything works if I change config_aneg to lan87xx_config_aneg,
> like this:
> 
> diff --git a/drivers/net/phy/smsc.c b/drivers/net/phy/smsc.c
> index 10722fed666d..07c0a7e4a350 100644
> --- a/drivers/net/phy/smsc.c
> +++ b/drivers/net/phy/smsc.c
> @@ -408,7 +408,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,
> +       .config_aneg    = lan87xx_config_aneg,
>  
>         /* IRQ related */
>         .ack_interrupt  = smsc_phy_ack_interrupt,
> 
> The internal phy of the LAN9500A does have a register 16 with
> documentation matching the usage in this patch.  Unfortunately, there
> doesn't seem to be any way of distinguishing this from the LAN8710A
> based on register values.  Anyone got any clever ideas?
> 
After reading register PHY_EDPD_CONFIG you could check whether the
read value is plausible. On the PHY's not supporting this register,
what is the read value? 0x00 or 0xff? And is this value plausible
for PHY's supporting this register?

Currently the PHY driver doesn't check the revision number
(last four bits of PHY ID). Maybe they differ.

^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: [PATCH net-next v5 3/3] smsc95xx: add phylib support
  2021-03-28 20:31     ` Heiner Kallweit
@ 2021-03-28 21:33       ` Måns Rullgård
  0 siblings, 0 replies; 8+ messages in thread
From: Måns Rullgård @ 2021-03-28 21:33 UTC (permalink / raw)
  To: Heiner Kallweit
  Cc: Andre Edich, netdev, UNGLinuxDriver, steve.glendinning,
	Parthiban.Veerasooran

Heiner Kallweit <hkallweit1@gmail.com> writes:

> On 28.03.2021 21:59, Måns Rullgård wrote:
>> Andre Edich <andre.edich@microchip.com> writes:
>> 
>>> 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 <andre.edich@microchip.com>
>>> ---
>>>  drivers/net/phy/smsc.c     |  67 +++++++
>>>  drivers/net/usb/Kconfig    |   2 +
>>>  drivers/net/usb/smsc95xx.c | 399 +++++++++++++------------------------
>>>  3 files changed, 203 insertions(+), 265 deletions(-)
>>>
>>> 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 <linux/netdevice.h>
>>>  #include <linux/smscphy.h>
>>>
>>> +/* 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,
>> 
>> This change seems to be causing some trouble I'm seeing with a LAN8710A.
>> Specifically lan87xx_config_aneg_ext() writes to register 16 which is
>> not documented for LAN8710A (nor for LAN8720A).  The effect is somewhat
>> random.  Sometimes, the device drops to 10 Mbps while the kernel still
>> reports the link speed as 100 Mbps.  Other times, it doesn't work at
>> all.  Everything works if I change config_aneg to lan87xx_config_aneg,
>> like this:
>> 
>> diff --git a/drivers/net/phy/smsc.c b/drivers/net/phy/smsc.c
>> index 10722fed666d..07c0a7e4a350 100644
>> --- a/drivers/net/phy/smsc.c
>> +++ b/drivers/net/phy/smsc.c
>> @@ -408,7 +408,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,
>> +       .config_aneg    = lan87xx_config_aneg,
>>  
>>         /* IRQ related */
>>         .ack_interrupt  = smsc_phy_ack_interrupt,
>> 
>> The internal phy of the LAN9500A does have a register 16 with
>> documentation matching the usage in this patch.  Unfortunately, there
>> doesn't seem to be any way of distinguishing this from the LAN8710A
>> based on register values.  Anyone got any clever ideas?
>> 
> After reading register PHY_EDPD_CONFIG you could check whether the
> read value is plausible. On the PHY's not supporting this register,
> what is the read value? 0x00 or 0xff? And is this value plausible
> for PHY's supporting this register?

On the LAN8710A I have here, it reads as 0x40.  That bit is "reserved"
without a specified value on the LAN9500A.  On LAN8740A (which also has
a different ID value), it controls Energy Efficient Ethernet

> Currently the PHY driver doesn't check the revision number (last four
> bits of PHY ID). Maybe they differ.

Even if they are different today (I don't seem to have a LAN9500A to
check), nothing guarantees that this will remain the case with future
versions of the chips.

-- 
Måns Rullgård

^ permalink raw reply	[flat|nested] 8+ messages in thread

end of thread, other threads:[~2021-03-28 21:34 UTC | newest]

Thread overview: 8+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2020-08-26 11:17 [PATCH net-next v5 0/3] Add phylib support to smsc95xx Andre Edich
2020-08-26 11:17 ` [PATCH net-next v5 1/3] smsc95xx: remove redundant function arguments Andre Edich
2020-08-26 11:17 ` [PATCH net-next v5 2/3] smsc95xx: use usbnet->driver_priv Andre Edich
2020-08-26 11:17 ` [PATCH net-next v5 3/3] smsc95xx: add phylib support Andre Edich
2021-03-28 19:59   ` Måns Rullgård
2021-03-28 20:31     ` Heiner Kallweit
2021-03-28 21:33       ` Måns Rullgård
2020-08-28 13:49 ` [PATCH net-next v5 0/3] Add phylib support to smsc95xx David Miller

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.