mirror of
				git://git.openwrt.org/openwrt/openwrt.git
				synced 2025-11-04 06:54:27 -05:00 
			
		
		
		
	kernel: add backported phy/phylink/sfp patches
Backport the phy/phylink/sfp patches currently queued in netdev or in mainline necessary to support GPON popular modules, specifically to support Huawei and Nokia GPON modules. Signed-off-by: Russell King <linux@armlinux.org.uk> [jonas.gorski: include kernel version in file names, refresh patches] Signed-off-by: Jonas Gorski <jonas.gorski@gmail.com>
This commit is contained in:
		
							parent
							
								
									a07638eb24
								
							
						
					
					
						commit
						1c16b574c4
					
				@ -0,0 +1,49 @@
 | 
			
		||||
From 1da223db3a0c522300b519ecbe1dc45927e28088 Mon Sep 17 00:00:00 2001
 | 
			
		||||
From: Andrew Lunn <andrew@lunn.ch>
 | 
			
		||||
Date: Wed, 12 Sep 2018 01:53:15 +0200
 | 
			
		||||
Subject: [PATCH 600/660] net: ethernet: Add helper for MACs which support asym
 | 
			
		||||
 pause
 | 
			
		||||
 | 
			
		||||
Rather than have the MAC drivers manipulate phydev members to indicate
 | 
			
		||||
they support Asym Pause, add a helper function.
 | 
			
		||||
 | 
			
		||||
Signed-off-by: Andrew Lunn <andrew@lunn.ch>
 | 
			
		||||
Reviewed-by: Florian Fainelli <f.fainelli@gmail.com>
 | 
			
		||||
Signed-off-by: David S. Miller <davem@davemloft.net>
 | 
			
		||||
---
 | 
			
		||||
 drivers/net/phy/phy_device.c | 13 +++++++++++++
 | 
			
		||||
 include/linux/phy.h          |  1 +
 | 
			
		||||
 2 files changed, 14 insertions(+)
 | 
			
		||||
 | 
			
		||||
--- a/drivers/net/phy/phy_device.c
 | 
			
		||||
+++ b/drivers/net/phy/phy_device.c
 | 
			
		||||
@@ -1776,6 +1776,19 @@ int phy_set_max_speed(struct phy_device
 | 
			
		||||
 }
 | 
			
		||||
 EXPORT_SYMBOL(phy_set_max_speed);
 | 
			
		||||
 
 | 
			
		||||
+/**
 | 
			
		||||
+ * phy_support_asym_pause - Enable support of asym pause
 | 
			
		||||
+ * @phydev: target phy_device struct
 | 
			
		||||
+ *
 | 
			
		||||
+ * Description: Called by the MAC to indicate is supports Asym Pause.
 | 
			
		||||
+ */
 | 
			
		||||
+void phy_support_asym_pause(struct phy_device *phydev)
 | 
			
		||||
+{
 | 
			
		||||
+	phydev->supported |= SUPPORTED_Pause | SUPPORTED_Asym_Pause;
 | 
			
		||||
+	phydev->advertising = phydev->supported;
 | 
			
		||||
+}
 | 
			
		||||
+EXPORT_SYMBOL(phy_support_asym_pause);
 | 
			
		||||
+
 | 
			
		||||
 static void of_set_phy_supported(struct phy_device *phydev)
 | 
			
		||||
 {
 | 
			
		||||
 	struct device_node *node = phydev->mdio.dev.of_node;
 | 
			
		||||
--- a/include/linux/phy.h
 | 
			
		||||
+++ b/include/linux/phy.h
 | 
			
		||||
@@ -1049,6 +1049,7 @@ int phy_mii_ioctl(struct phy_device *phy
 | 
			
		||||
 int phy_start_interrupts(struct phy_device *phydev);
 | 
			
		||||
 void phy_print_status(struct phy_device *phydev);
 | 
			
		||||
 int phy_set_max_speed(struct phy_device *phydev, u32 max_speed);
 | 
			
		||||
+void phy_support_asym_pause(struct phy_device *phydev);
 | 
			
		||||
 
 | 
			
		||||
 int phy_register_fixup(const char *bus_id, u32 phy_uid, u32 phy_uid_mask,
 | 
			
		||||
 		       int (*run)(struct phy_device *));
 | 
			
		||||
@ -0,0 +1,66 @@
 | 
			
		||||
From ce825df56e0480a2cbb296e38976babafb57e503 Mon Sep 17 00:00:00 2001
 | 
			
		||||
From: Andrew Lunn <andrew@lunn.ch>
 | 
			
		||||
Date: Wed, 12 Sep 2018 01:53:17 +0200
 | 
			
		||||
Subject: [PATCH 601/660] net: ethernet: Add helper for set_pauseparam for Asym
 | 
			
		||||
 Pause
 | 
			
		||||
 | 
			
		||||
ethtool can be used to enable/disable pause. Add a helper to configure
 | 
			
		||||
the PHY when asym pause is supported.
 | 
			
		||||
 | 
			
		||||
Signed-off-by: Andrew Lunn <andrew@lunn.ch>
 | 
			
		||||
Reviewed-by: Florian Fainelli <f.fainelli@gmail.com>
 | 
			
		||||
Signed-off-by: David S. Miller <davem@davemloft.net>
 | 
			
		||||
---
 | 
			
		||||
 drivers/net/phy/phy_device.c | 30 ++++++++++++++++++++++++++++++
 | 
			
		||||
 include/linux/phy.h          |  1 +
 | 
			
		||||
 2 files changed, 31 insertions(+)
 | 
			
		||||
 | 
			
		||||
--- a/drivers/net/phy/phy_device.c
 | 
			
		||||
+++ b/drivers/net/phy/phy_device.c
 | 
			
		||||
@@ -1789,6 +1789,36 @@ void phy_support_asym_pause(struct phy_d
 | 
			
		||||
 }
 | 
			
		||||
 EXPORT_SYMBOL(phy_support_asym_pause);
 | 
			
		||||
 
 | 
			
		||||
+/**
 | 
			
		||||
+ * phy_set_asym_pause - Configure Pause and Asym Pause
 | 
			
		||||
+ * @phydev: target phy_device struct
 | 
			
		||||
+ * @rx: Receiver Pause is supported
 | 
			
		||||
+ * @tx: Transmit Pause is supported
 | 
			
		||||
+ *
 | 
			
		||||
+ * Description: Configure advertised Pause support depending on if
 | 
			
		||||
+ * transmit and receiver pause is supported. If there has been a
 | 
			
		||||
+ * change in adverting, trigger a new autoneg. Generally called from
 | 
			
		||||
+ * the set_pauseparam .ndo.
 | 
			
		||||
+ */
 | 
			
		||||
+void phy_set_asym_pause(struct phy_device *phydev, bool rx, bool tx)
 | 
			
		||||
+{
 | 
			
		||||
+	u16 oldadv = phydev->advertising;
 | 
			
		||||
+	u16 newadv = oldadv &= ~(SUPPORTED_Pause | SUPPORTED_Asym_Pause);
 | 
			
		||||
+
 | 
			
		||||
+	if (rx)
 | 
			
		||||
+		newadv |= SUPPORTED_Pause | SUPPORTED_Asym_Pause;
 | 
			
		||||
+	if (tx)
 | 
			
		||||
+		newadv ^= SUPPORTED_Asym_Pause;
 | 
			
		||||
+
 | 
			
		||||
+	if (oldadv != newadv) {
 | 
			
		||||
+		phydev->advertising = newadv;
 | 
			
		||||
+
 | 
			
		||||
+		if (phydev->autoneg)
 | 
			
		||||
+			phy_start_aneg(phydev);
 | 
			
		||||
+	}
 | 
			
		||||
+}
 | 
			
		||||
+EXPORT_SYMBOL(phy_set_asym_pause);
 | 
			
		||||
+
 | 
			
		||||
 static void of_set_phy_supported(struct phy_device *phydev)
 | 
			
		||||
 {
 | 
			
		||||
 	struct device_node *node = phydev->mdio.dev.of_node;
 | 
			
		||||
--- a/include/linux/phy.h
 | 
			
		||||
+++ b/include/linux/phy.h
 | 
			
		||||
@@ -1050,6 +1050,7 @@ int phy_start_interrupts(struct phy_devi
 | 
			
		||||
 void phy_print_status(struct phy_device *phydev);
 | 
			
		||||
 int phy_set_max_speed(struct phy_device *phydev, u32 max_speed);
 | 
			
		||||
 void phy_support_asym_pause(struct phy_device *phydev);
 | 
			
		||||
+void phy_set_asym_pause(struct phy_device *phydev, bool rx, bool tx);
 | 
			
		||||
 
 | 
			
		||||
 int phy_register_fixup(const char *bus_id, u32 phy_uid, u32 phy_uid_mask,
 | 
			
		||||
 		       int (*run)(struct phy_device *));
 | 
			
		||||
@ -0,0 +1,40 @@
 | 
			
		||||
From 1541649a9dd79e9b941d399de564475e426a2d0b Mon Sep 17 00:00:00 2001
 | 
			
		||||
From: Florian Fainelli <f.fainelli@gmail.com>
 | 
			
		||||
Date: Tue, 25 Sep 2018 11:28:45 -0700
 | 
			
		||||
Subject: [PATCH 602/660] net: phy: Stop with excessive soft reset
 | 
			
		||||
 | 
			
		||||
While consolidating the PHY reset in phy_init_hw() an unconditionaly
 | 
			
		||||
BMCR soft-reset I became quite trigger happy with those. This was later
 | 
			
		||||
on deactivated for the Generic PHY driver on the premise that a prior
 | 
			
		||||
software entity (e.g: bootloader) might have applied workarounds in
 | 
			
		||||
commit 0878fff1f42c ("net: phy: Do not perform software reset for
 | 
			
		||||
Generic PHY").
 | 
			
		||||
 | 
			
		||||
Since we have a hook to wire-up a soft_reset callback, just use that and
 | 
			
		||||
get rid of the call to genphy_soft_reset() entirely. This speeds up
 | 
			
		||||
initialization and link establishment for most PHYs out there that do
 | 
			
		||||
not require a reset.
 | 
			
		||||
 | 
			
		||||
Fixes: 87aa9f9c61ad ("net: phy: consolidate PHY reset in phy_init_hw()")
 | 
			
		||||
Tested-by: Wang, Dongsheng <dongsheng.wang@hxt-semitech.com>
 | 
			
		||||
Tested-by: Chris Healy <cphealy@gmail.com>
 | 
			
		||||
Tested-by: Andrew Lunn <andrew@lunn.ch>
 | 
			
		||||
Tested-by: Clemens Gruber <clemens.gruber@pqgruber.com>
 | 
			
		||||
Signed-off-by: Florian Fainelli <f.fainelli@gmail.com>
 | 
			
		||||
Signed-off-by: David S. Miller <davem@davemloft.net>
 | 
			
		||||
Signed-off-by: Russell King <rmk+kernel@armlinux.org.uk>
 | 
			
		||||
---
 | 
			
		||||
 drivers/net/phy/phy_device.c | 2 --
 | 
			
		||||
 1 file changed, 2 deletions(-)
 | 
			
		||||
 | 
			
		||||
--- a/drivers/net/phy/phy_device.c
 | 
			
		||||
+++ b/drivers/net/phy/phy_device.c
 | 
			
		||||
@@ -885,8 +885,6 @@ int phy_init_hw(struct phy_device *phyde
 | 
			
		||||
 
 | 
			
		||||
 	if (phydev->drv->soft_reset)
 | 
			
		||||
 		ret = phydev->drv->soft_reset(phydev);
 | 
			
		||||
-	else
 | 
			
		||||
-		ret = genphy_soft_reset(phydev);
 | 
			
		||||
 
 | 
			
		||||
 	if (ret < 0)
 | 
			
		||||
 		return ret;
 | 
			
		||||
@ -0,0 +1,375 @@
 | 
			
		||||
From 80758d9542205cd2e9fa730067bc3888d4f5a096 Mon Sep 17 00:00:00 2001
 | 
			
		||||
From: Nikita Yushchenko <nikita.yoush@cogentembedded.com>
 | 
			
		||||
Date: Wed, 6 Feb 2019 07:36:40 +0100
 | 
			
		||||
Subject: [PATCH 603/660] net: phy: provide full set of accessor functions to
 | 
			
		||||
 MMD registers
 | 
			
		||||
 | 
			
		||||
This adds full set of locked and unlocked accessor functions to read and
 | 
			
		||||
write PHY MMD registers and/or bitfields.
 | 
			
		||||
 | 
			
		||||
Set of functions exactly matches what is already available for PHY
 | 
			
		||||
legacy registers.
 | 
			
		||||
 | 
			
		||||
Signed-off-by: Nikita Yushchenko <nikita.yoush@cogentembedded.com>
 | 
			
		||||
Signed-off-by: Andrew Lunn <andrew@lunn.ch>
 | 
			
		||||
Signed-off-by: Heiner Kallweit <hkallweit1@gmail.com>
 | 
			
		||||
Signed-off-by: David S. Miller <davem@davemloft.net>
 | 
			
		||||
Signed-off-by: Russell King <rmk+kernel@armlinux.org.uk>
 | 
			
		||||
---
 | 
			
		||||
 drivers/net/phy/phy-core.c | 116 ++++++++++++++++++++++++++++----
 | 
			
		||||
 include/linux/phy.h        | 134 ++++++++++++++++++++++++++++++-------
 | 
			
		||||
 2 files changed, 214 insertions(+), 36 deletions(-)
 | 
			
		||||
 | 
			
		||||
--- a/drivers/net/phy/phy-core.c
 | 
			
		||||
+++ b/drivers/net/phy/phy-core.c
 | 
			
		||||
@@ -247,15 +247,15 @@ static void mmd_phy_indirect(struct mii_
 | 
			
		||||
 }
 | 
			
		||||
 
 | 
			
		||||
 /**
 | 
			
		||||
- * phy_read_mmd - Convenience function for reading a register
 | 
			
		||||
+ * __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 (0..31)
 | 
			
		||||
  * @regnum: The register on the MMD to read (0..65535)
 | 
			
		||||
  *
 | 
			
		||||
- * Same rules as for phy_read();
 | 
			
		||||
+ * Same rules as for __phy_read();
 | 
			
		||||
  */
 | 
			
		||||
-int phy_read_mmd(struct phy_device *phydev, int devad, u32 regnum)
 | 
			
		||||
+int __phy_read_mmd(struct phy_device *phydev, int devad, u32 regnum)
 | 
			
		||||
 {
 | 
			
		||||
 	int val;
 | 
			
		||||
 
 | 
			
		||||
@@ -267,33 +267,52 @@ int phy_read_mmd(struct phy_device *phyd
 | 
			
		||||
 	} else if (phydev->is_c45) {
 | 
			
		||||
 		u32 addr = MII_ADDR_C45 | (devad << 16) | (regnum & 0xffff);
 | 
			
		||||
 
 | 
			
		||||
-		val = mdiobus_read(phydev->mdio.bus, phydev->mdio.addr, addr);
 | 
			
		||||
+		val = __mdiobus_read(phydev->mdio.bus, phydev->mdio.addr, addr);
 | 
			
		||||
 	} else {
 | 
			
		||||
 		struct mii_bus *bus = phydev->mdio.bus;
 | 
			
		||||
 		int phy_addr = phydev->mdio.addr;
 | 
			
		||||
 
 | 
			
		||||
-		mutex_lock(&bus->mdio_lock);
 | 
			
		||||
 		mmd_phy_indirect(bus, phy_addr, devad, regnum);
 | 
			
		||||
 
 | 
			
		||||
 		/* Read the content of the MMD's selected register */
 | 
			
		||||
 		val = __mdiobus_read(bus, phy_addr, MII_MMD_DATA);
 | 
			
		||||
-		mutex_unlock(&bus->mdio_lock);
 | 
			
		||||
 	}
 | 
			
		||||
 	return val;
 | 
			
		||||
 }
 | 
			
		||||
+EXPORT_SYMBOL(__phy_read_mmd);
 | 
			
		||||
+
 | 
			
		||||
+/**
 | 
			
		||||
+ * 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
 | 
			
		||||
+ *
 | 
			
		||||
+ * Same rules as for phy_read();
 | 
			
		||||
+ */
 | 
			
		||||
+int phy_read_mmd(struct phy_device *phydev, int devad, u32 regnum)
 | 
			
		||||
+{
 | 
			
		||||
+	int ret;
 | 
			
		||||
+
 | 
			
		||||
+	mutex_lock(&phydev->mdio.bus->mdio_lock);
 | 
			
		||||
+	ret = __phy_read_mmd(phydev, devad, regnum);
 | 
			
		||||
+	mutex_unlock(&phydev->mdio.bus->mdio_lock);
 | 
			
		||||
+
 | 
			
		||||
+	return ret;
 | 
			
		||||
+}
 | 
			
		||||
 EXPORT_SYMBOL(phy_read_mmd);
 | 
			
		||||
 
 | 
			
		||||
 /**
 | 
			
		||||
- * phy_write_mmd - Convenience function for writing a register
 | 
			
		||||
+ * __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
 | 
			
		||||
  *
 | 
			
		||||
- * Same rules as for phy_write();
 | 
			
		||||
+ * Same rules as for __phy_write();
 | 
			
		||||
  */
 | 
			
		||||
-int phy_write_mmd(struct phy_device *phydev, int devad, u32 regnum, u16 val)
 | 
			
		||||
+int __phy_write_mmd(struct phy_device *phydev, int devad, u32 regnum, u16 val)
 | 
			
		||||
 {
 | 
			
		||||
 	int ret;
 | 
			
		||||
 
 | 
			
		||||
@@ -305,23 +324,43 @@ int phy_write_mmd(struct phy_device *phy
 | 
			
		||||
 	} else if (phydev->is_c45) {
 | 
			
		||||
 		u32 addr = MII_ADDR_C45 | (devad << 16) | (regnum & 0xffff);
 | 
			
		||||
 
 | 
			
		||||
-		ret = mdiobus_write(phydev->mdio.bus, phydev->mdio.addr,
 | 
			
		||||
-				    addr, val);
 | 
			
		||||
+		ret = __mdiobus_write(phydev->mdio.bus, phydev->mdio.addr,
 | 
			
		||||
+				      addr, val);
 | 
			
		||||
 	} else {
 | 
			
		||||
 		struct mii_bus *bus = phydev->mdio.bus;
 | 
			
		||||
 		int phy_addr = phydev->mdio.addr;
 | 
			
		||||
 
 | 
			
		||||
-		mutex_lock(&bus->mdio_lock);
 | 
			
		||||
 		mmd_phy_indirect(bus, phy_addr, devad, regnum);
 | 
			
		||||
 
 | 
			
		||||
 		/* Write the data into MMD's selected register */
 | 
			
		||||
 		__mdiobus_write(bus, phy_addr, MII_MMD_DATA, val);
 | 
			
		||||
-		mutex_unlock(&bus->mdio_lock);
 | 
			
		||||
 
 | 
			
		||||
 		ret = 0;
 | 
			
		||||
 	}
 | 
			
		||||
 	return ret;
 | 
			
		||||
 }
 | 
			
		||||
+EXPORT_SYMBOL(__phy_write_mmd);
 | 
			
		||||
+
 | 
			
		||||
+/**
 | 
			
		||||
+ * 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
 | 
			
		||||
+ *
 | 
			
		||||
+ * Same rules as for phy_write();
 | 
			
		||||
+ */
 | 
			
		||||
+int phy_write_mmd(struct phy_device *phydev, int devad, u32 regnum, u16 val)
 | 
			
		||||
+{
 | 
			
		||||
+	int ret;
 | 
			
		||||
+
 | 
			
		||||
+	mutex_lock(&phydev->mdio.bus->mdio_lock);
 | 
			
		||||
+	ret = __phy_write_mmd(phydev, devad, regnum, val);
 | 
			
		||||
+	mutex_unlock(&phydev->mdio.bus->mdio_lock);
 | 
			
		||||
+
 | 
			
		||||
+	return ret;
 | 
			
		||||
+}
 | 
			
		||||
 EXPORT_SYMBOL(phy_write_mmd);
 | 
			
		||||
 
 | 
			
		||||
 /**
 | 
			
		||||
@@ -371,6 +410,57 @@ int phy_modify(struct phy_device *phydev
 | 
			
		||||
 }
 | 
			
		||||
 EXPORT_SYMBOL_GPL(phy_modify);
 | 
			
		||||
 
 | 
			
		||||
+/**
 | 
			
		||||
+ * __phy_modify_mmd - Convenience function for modifying a register on MMD
 | 
			
		||||
+ * @phydev: the phy_device struct
 | 
			
		||||
+ * @devad: the MMD containing register to modify
 | 
			
		||||
+ * @regnum: register number to modify
 | 
			
		||||
+ * @mask: bit mask of bits to clear
 | 
			
		||||
+ * @set: new value of bits set in mask to write to @regnum
 | 
			
		||||
+ *
 | 
			
		||||
+ * Unlocked helper function which allows a MMD register to be modified as
 | 
			
		||||
+ * new register value = (old register value & ~mask) | set
 | 
			
		||||
+ */
 | 
			
		||||
+int __phy_modify_mmd(struct phy_device *phydev, int devad, u32 regnum,
 | 
			
		||||
+		     u16 mask, u16 set)
 | 
			
		||||
+{
 | 
			
		||||
+	int ret;
 | 
			
		||||
+
 | 
			
		||||
+	ret = __phy_read_mmd(phydev, devad, regnum);
 | 
			
		||||
+	if (ret < 0)
 | 
			
		||||
+		return ret;
 | 
			
		||||
+
 | 
			
		||||
+	ret = __phy_write_mmd(phydev, devad, regnum, (ret & ~mask) | set);
 | 
			
		||||
+
 | 
			
		||||
+	return ret < 0 ? ret : 0;
 | 
			
		||||
+}
 | 
			
		||||
+EXPORT_SYMBOL_GPL(__phy_modify_mmd);
 | 
			
		||||
+
 | 
			
		||||
+/**
 | 
			
		||||
+ * phy_modify_mmd - Convenience function for modifying a register on MMD
 | 
			
		||||
+ * @phydev: the phy_device struct
 | 
			
		||||
+ * @devad: the MMD containing register to modify
 | 
			
		||||
+ * @regnum: register number to modify
 | 
			
		||||
+ * @mask: bit mask of bits to clear
 | 
			
		||||
+ * @set: new value of bits set in mask to write to @regnum
 | 
			
		||||
+ *
 | 
			
		||||
+ * NOTE: MUST NOT be called from interrupt context,
 | 
			
		||||
+ * because the bus read/write functions may wait for an interrupt
 | 
			
		||||
+ * to conclude the operation.
 | 
			
		||||
+ */
 | 
			
		||||
+int phy_modify_mmd(struct phy_device *phydev, int devad, u32 regnum,
 | 
			
		||||
+		   u16 mask, u16 set)
 | 
			
		||||
+{
 | 
			
		||||
+	int ret;
 | 
			
		||||
+
 | 
			
		||||
+	mutex_lock(&phydev->mdio.bus->mdio_lock);
 | 
			
		||||
+	ret = __phy_modify_mmd(phydev, devad, regnum, mask, set);
 | 
			
		||||
+	mutex_unlock(&phydev->mdio.bus->mdio_lock);
 | 
			
		||||
+
 | 
			
		||||
+	return ret;
 | 
			
		||||
+}
 | 
			
		||||
+EXPORT_SYMBOL_GPL(phy_modify_mmd);
 | 
			
		||||
+
 | 
			
		||||
 static int __phy_read_page(struct phy_device *phydev)
 | 
			
		||||
 {
 | 
			
		||||
 	return phydev->drv->read_page(phydev);
 | 
			
		||||
--- a/include/linux/phy.h
 | 
			
		||||
+++ b/include/linux/phy.h
 | 
			
		||||
@@ -695,17 +695,6 @@ size_t phy_speeds(unsigned int *speeds,
 | 
			
		||||
 void phy_resolve_aneg_linkmode(struct phy_device *phydev);
 | 
			
		||||
 
 | 
			
		||||
 /**
 | 
			
		||||
- * 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
 | 
			
		||||
- *
 | 
			
		||||
- * Same rules as for phy_read();
 | 
			
		||||
- */
 | 
			
		||||
-int phy_read_mmd(struct phy_device *phydev, int devad, u32 regnum);
 | 
			
		||||
-
 | 
			
		||||
-/**
 | 
			
		||||
  * phy_read - Convenience function for reading a given PHY register
 | 
			
		||||
  * @phydev: the phy_device struct
 | 
			
		||||
  * @regnum: register number to read
 | 
			
		||||
@@ -760,9 +749,60 @@ static inline int __phy_write(struct phy
 | 
			
		||||
 			       val);
 | 
			
		||||
 }
 | 
			
		||||
 
 | 
			
		||||
+/**
 | 
			
		||||
+ * 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
 | 
			
		||||
+ *
 | 
			
		||||
+ * Same rules as for phy_read();
 | 
			
		||||
+ */
 | 
			
		||||
+int phy_read_mmd(struct phy_device *phydev, int devad, u32 regnum);
 | 
			
		||||
+
 | 
			
		||||
+/**
 | 
			
		||||
+ * __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
 | 
			
		||||
+ *
 | 
			
		||||
+ * Same rules as for __phy_read();
 | 
			
		||||
+ */
 | 
			
		||||
+int __phy_read_mmd(struct phy_device *phydev, int devad, u32 regnum);
 | 
			
		||||
+
 | 
			
		||||
+/**
 | 
			
		||||
+ * 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 write to
 | 
			
		||||
+ * @regnum: The register on the MMD to read
 | 
			
		||||
+ * @val: value to write to @regnum
 | 
			
		||||
+ *
 | 
			
		||||
+ * Same rules as for phy_write();
 | 
			
		||||
+ */
 | 
			
		||||
+int phy_write_mmd(struct phy_device *phydev, int devad, u32 regnum, u16 val);
 | 
			
		||||
+
 | 
			
		||||
+/**
 | 
			
		||||
+ * __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 write to
 | 
			
		||||
+ * @regnum: The register on the MMD to read
 | 
			
		||||
+ * @val: value to write to @regnum
 | 
			
		||||
+ *
 | 
			
		||||
+ * Same rules as for __phy_write();
 | 
			
		||||
+ */
 | 
			
		||||
+int __phy_write_mmd(struct phy_device *phydev, int devad, u32 regnum, u16 val);
 | 
			
		||||
+
 | 
			
		||||
 int __phy_modify(struct phy_device *phydev, u32 regnum, u16 mask, u16 set);
 | 
			
		||||
 int phy_modify(struct phy_device *phydev, u32 regnum, u16 mask, u16 set);
 | 
			
		||||
 
 | 
			
		||||
+int __phy_modify_mmd(struct phy_device *phydev, int devad, u32 regnum,
 | 
			
		||||
+		u16 mask, u16 set);
 | 
			
		||||
+int phy_modify_mmd(struct phy_device *phydev, int devad, u32 regnum,
 | 
			
		||||
+		u16 mask, u16 set);
 | 
			
		||||
+
 | 
			
		||||
 /**
 | 
			
		||||
  * __phy_set_bits - Convenience function for setting bits in a PHY register
 | 
			
		||||
  * @phydev: the phy_device struct
 | 
			
		||||
@@ -813,6 +853,66 @@ static inline int phy_clear_bits(struct
 | 
			
		||||
 }
 | 
			
		||||
 
 | 
			
		||||
 /**
 | 
			
		||||
+ * __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
 | 
			
		||||
+ *
 | 
			
		||||
+ * The caller must have taken the MDIO bus lock.
 | 
			
		||||
+ */
 | 
			
		||||
+static inline int __phy_set_bits_mmd(struct phy_device *phydev, int devad,
 | 
			
		||||
+		u32 regnum, u16 val)
 | 
			
		||||
+{
 | 
			
		||||
+	return __phy_modify_mmd(phydev, devad, regnum, 0, val);
 | 
			
		||||
+}
 | 
			
		||||
+
 | 
			
		||||
+/**
 | 
			
		||||
+ * __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
 | 
			
		||||
+ *
 | 
			
		||||
+ * The caller must have taken the MDIO bus lock.
 | 
			
		||||
+ */
 | 
			
		||||
+static inline int __phy_clear_bits_mmd(struct phy_device *phydev, int devad,
 | 
			
		||||
+		u32 regnum, u16 val)
 | 
			
		||||
+{
 | 
			
		||||
+	return __phy_modify_mmd(phydev, devad, regnum, val, 0);
 | 
			
		||||
+}
 | 
			
		||||
+
 | 
			
		||||
+/**
 | 
			
		||||
+ * 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
 | 
			
		||||
+ */
 | 
			
		||||
+static inline int phy_set_bits_mmd(struct phy_device *phydev, int devad,
 | 
			
		||||
+		u32 regnum, u16 val)
 | 
			
		||||
+{
 | 
			
		||||
+	return phy_modify_mmd(phydev, devad, regnum, 0, val);
 | 
			
		||||
+}
 | 
			
		||||
+
 | 
			
		||||
+/**
 | 
			
		||||
+ * 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
 | 
			
		||||
+ */
 | 
			
		||||
+static inline int phy_clear_bits_mmd(struct phy_device *phydev, int devad,
 | 
			
		||||
+		u32 regnum, u16 val)
 | 
			
		||||
+{
 | 
			
		||||
+	return phy_modify_mmd(phydev, devad, regnum, val, 0);
 | 
			
		||||
+}
 | 
			
		||||
+
 | 
			
		||||
+/**
 | 
			
		||||
  * phy_interrupt_is_valid - Convenience function for testing a given PHY irq
 | 
			
		||||
  * @phydev: the phy_device struct
 | 
			
		||||
  *
 | 
			
		||||
@@ -888,18 +988,6 @@ static inline bool phy_is_pseudo_fixed_l
 | 
			
		||||
 	return phydev->is_pseudo_fixed_link;
 | 
			
		||||
 }
 | 
			
		||||
 
 | 
			
		||||
-/**
 | 
			
		||||
- * 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
 | 
			
		||||
- *
 | 
			
		||||
- * Same rules as for phy_write();
 | 
			
		||||
- */
 | 
			
		||||
-int phy_write_mmd(struct phy_device *phydev, int devad, u32 regnum, u16 val);
 | 
			
		||||
-
 | 
			
		||||
 int phy_save_page(struct phy_device *phydev);
 | 
			
		||||
 int phy_select_page(struct phy_device *phydev, int page);
 | 
			
		||||
 int phy_restore_page(struct phy_device *phydev, int oldpage, int ret);
 | 
			
		||||
@ -0,0 +1,217 @@
 | 
			
		||||
From c1e3f753f6b85d7636024159bb78f764e09492f1 Mon Sep 17 00:00:00 2001
 | 
			
		||||
From: Heiner Kallweit <hkallweit1@gmail.com>
 | 
			
		||||
Date: Sun, 10 Feb 2019 19:57:56 +0100
 | 
			
		||||
Subject: [PATCH 604/660] net: phy: add register modifying helpers returning 1
 | 
			
		||||
 on change
 | 
			
		||||
 | 
			
		||||
When modifying registers there are scenarios where we need to know
 | 
			
		||||
whether the register content actually changed. This patch adds
 | 
			
		||||
new helpers to not break users of the current ones, phy_modify() etc.
 | 
			
		||||
 | 
			
		||||
Signed-off-by: Heiner Kallweit <hkallweit1@gmail.com>
 | 
			
		||||
Reviewed-by: Andrew Lunn <andrew@lunn.ch>
 | 
			
		||||
Reviewed-by: Florian Fainelli <f.fainelli@gmail.com>
 | 
			
		||||
Signed-off-by: David S. Miller <davem@davemloft.net>
 | 
			
		||||
Signed-off-by: Russell King <rmk+kernel@armlinux.org.uk>
 | 
			
		||||
---
 | 
			
		||||
 drivers/net/phy/phy-core.c | 127 ++++++++++++++++++++++++++++++++++---
 | 
			
		||||
 include/linux/phy.h        |  12 +++-
 | 
			
		||||
 2 files changed, 128 insertions(+), 11 deletions(-)
 | 
			
		||||
 | 
			
		||||
--- a/drivers/net/phy/phy-core.c
 | 
			
		||||
+++ b/drivers/net/phy/phy-core.c
 | 
			
		||||
@@ -364,7 +364,7 @@ int phy_write_mmd(struct phy_device *phy
 | 
			
		||||
 EXPORT_SYMBOL(phy_write_mmd);
 | 
			
		||||
 
 | 
			
		||||
 /**
 | 
			
		||||
- * __phy_modify() - Convenience function for modifying a PHY register
 | 
			
		||||
+ * __phy_modify_changed() - Convenience function for modifying a PHY register
 | 
			
		||||
  * @phydev: a pointer to a &struct phy_device
 | 
			
		||||
  * @regnum: register number
 | 
			
		||||
  * @mask: bit mask of bits to clear
 | 
			
		||||
@@ -372,16 +372,69 @@ EXPORT_SYMBOL(phy_write_mmd);
 | 
			
		||||
  *
 | 
			
		||||
  * Unlocked helper function which allows a PHY register to be modified as
 | 
			
		||||
  * new register value = (old register value & ~mask) | set
 | 
			
		||||
+ *
 | 
			
		||||
+ * Returns negative errno, 0 if there was no change, and 1 in case of change
 | 
			
		||||
  */
 | 
			
		||||
-int __phy_modify(struct phy_device *phydev, u32 regnum, u16 mask, u16 set)
 | 
			
		||||
+int __phy_modify_changed(struct phy_device *phydev, u32 regnum, u16 mask,
 | 
			
		||||
+			 u16 set)
 | 
			
		||||
 {
 | 
			
		||||
-	int ret;
 | 
			
		||||
+	int new, ret;
 | 
			
		||||
 
 | 
			
		||||
 	ret = __phy_read(phydev, regnum);
 | 
			
		||||
 	if (ret < 0)
 | 
			
		||||
 		return ret;
 | 
			
		||||
 
 | 
			
		||||
-	ret = __phy_write(phydev, regnum, (ret & ~mask) | set);
 | 
			
		||||
+	new = (ret & ~mask) | set;
 | 
			
		||||
+	if (new == ret)
 | 
			
		||||
+		return 0;
 | 
			
		||||
+
 | 
			
		||||
+	ret = __phy_write(phydev, regnum, new);
 | 
			
		||||
+
 | 
			
		||||
+	return ret < 0 ? ret : 1;
 | 
			
		||||
+}
 | 
			
		||||
+EXPORT_SYMBOL_GPL(__phy_modify_changed);
 | 
			
		||||
+
 | 
			
		||||
+/**
 | 
			
		||||
+ * phy_modify_changed - Function for modifying a PHY register
 | 
			
		||||
+ * @phydev: the phy_device struct
 | 
			
		||||
+ * @regnum: register number to modify
 | 
			
		||||
+ * @mask: bit mask of bits to clear
 | 
			
		||||
+ * @set: new value of bits set in mask to write to @regnum
 | 
			
		||||
+ *
 | 
			
		||||
+ * NOTE: MUST NOT be called from interrupt context,
 | 
			
		||||
+ * because the bus read/write functions may wait for an interrupt
 | 
			
		||||
+ * to conclude the operation.
 | 
			
		||||
+ *
 | 
			
		||||
+ * Returns negative errno, 0 if there was no change, and 1 in case of change
 | 
			
		||||
+ */
 | 
			
		||||
+int phy_modify_changed(struct phy_device *phydev, u32 regnum, u16 mask, u16 set)
 | 
			
		||||
+{
 | 
			
		||||
+	int ret;
 | 
			
		||||
+
 | 
			
		||||
+	mutex_lock(&phydev->mdio.bus->mdio_lock);
 | 
			
		||||
+	ret = __phy_modify_changed(phydev, regnum, mask, set);
 | 
			
		||||
+	mutex_unlock(&phydev->mdio.bus->mdio_lock);
 | 
			
		||||
+
 | 
			
		||||
+	return ret;
 | 
			
		||||
+}
 | 
			
		||||
+EXPORT_SYMBOL_GPL(phy_modify_changed);
 | 
			
		||||
+
 | 
			
		||||
+/**
 | 
			
		||||
+ * __phy_modify - Convenience function for modifying a PHY register
 | 
			
		||||
+ * @phydev: the phy_device struct
 | 
			
		||||
+ * @regnum: register number to modify
 | 
			
		||||
+ * @mask: bit mask of bits to clear
 | 
			
		||||
+ * @set: new value of bits set in mask to write to @regnum
 | 
			
		||||
+ *
 | 
			
		||||
+ * NOTE: MUST NOT be called from interrupt context,
 | 
			
		||||
+ * because the bus read/write functions may wait for an interrupt
 | 
			
		||||
+ * to conclude the operation.
 | 
			
		||||
+ */
 | 
			
		||||
+int __phy_modify(struct phy_device *phydev, u32 regnum, u16 mask, u16 set)
 | 
			
		||||
+{
 | 
			
		||||
+	int ret;
 | 
			
		||||
+
 | 
			
		||||
+	ret = __phy_modify_changed(phydev, regnum, mask, set);
 | 
			
		||||
 
 | 
			
		||||
 	return ret < 0 ? ret : 0;
 | 
			
		||||
 }
 | 
			
		||||
@@ -411,7 +464,7 @@ int phy_modify(struct phy_device *phydev
 | 
			
		||||
 EXPORT_SYMBOL_GPL(phy_modify);
 | 
			
		||||
 
 | 
			
		||||
 /**
 | 
			
		||||
- * __phy_modify_mmd - Convenience function for modifying a register on MMD
 | 
			
		||||
+ * __phy_modify_mmd_changed - Function for modifying a register on MMD
 | 
			
		||||
  * @phydev: the phy_device struct
 | 
			
		||||
  * @devad: the MMD containing register to modify
 | 
			
		||||
  * @regnum: register number to modify
 | 
			
		||||
@@ -420,17 +473,73 @@ EXPORT_SYMBOL_GPL(phy_modify);
 | 
			
		||||
  *
 | 
			
		||||
  * Unlocked helper function which allows a MMD register to be modified as
 | 
			
		||||
  * new register value = (old register value & ~mask) | set
 | 
			
		||||
+ *
 | 
			
		||||
+ * Returns negative errno, 0 if there was no change, and 1 in case of change
 | 
			
		||||
  */
 | 
			
		||||
-int __phy_modify_mmd(struct phy_device *phydev, int devad, u32 regnum,
 | 
			
		||||
-		     u16 mask, u16 set)
 | 
			
		||||
+int __phy_modify_mmd_changed(struct phy_device *phydev, int devad, u32 regnum,
 | 
			
		||||
+			     u16 mask, u16 set)
 | 
			
		||||
 {
 | 
			
		||||
-	int ret;
 | 
			
		||||
+	int new, ret;
 | 
			
		||||
 
 | 
			
		||||
 	ret = __phy_read_mmd(phydev, devad, regnum);
 | 
			
		||||
 	if (ret < 0)
 | 
			
		||||
 		return ret;
 | 
			
		||||
 
 | 
			
		||||
-	ret = __phy_write_mmd(phydev, devad, regnum, (ret & ~mask) | set);
 | 
			
		||||
+	new = (ret & ~mask) | set;
 | 
			
		||||
+	if (new == ret)
 | 
			
		||||
+		return 0;
 | 
			
		||||
+
 | 
			
		||||
+	ret = __phy_write_mmd(phydev, devad, regnum, new);
 | 
			
		||||
+
 | 
			
		||||
+	return ret < 0 ? ret : 1;
 | 
			
		||||
+}
 | 
			
		||||
+EXPORT_SYMBOL_GPL(__phy_modify_mmd_changed);
 | 
			
		||||
+
 | 
			
		||||
+/**
 | 
			
		||||
+ * phy_modify_mmd_changed - Function for modifying a register on MMD
 | 
			
		||||
+ * @phydev: the phy_device struct
 | 
			
		||||
+ * @devad: the MMD containing register to modify
 | 
			
		||||
+ * @regnum: register number to modify
 | 
			
		||||
+ * @mask: bit mask of bits to clear
 | 
			
		||||
+ * @set: new value of bits set in mask to write to @regnum
 | 
			
		||||
+ *
 | 
			
		||||
+ * NOTE: MUST NOT be called from interrupt context,
 | 
			
		||||
+ * because the bus read/write functions may wait for an interrupt
 | 
			
		||||
+ * to conclude the operation.
 | 
			
		||||
+ *
 | 
			
		||||
+ * Returns negative errno, 0 if there was no change, and 1 in case of change
 | 
			
		||||
+ */
 | 
			
		||||
+int phy_modify_mmd_changed(struct phy_device *phydev, int devad, u32 regnum,
 | 
			
		||||
+			   u16 mask, u16 set)
 | 
			
		||||
+{
 | 
			
		||||
+	int ret;
 | 
			
		||||
+
 | 
			
		||||
+	mutex_lock(&phydev->mdio.bus->mdio_lock);
 | 
			
		||||
+	ret = __phy_modify_mmd_changed(phydev, devad, regnum, mask, set);
 | 
			
		||||
+	mutex_unlock(&phydev->mdio.bus->mdio_lock);
 | 
			
		||||
+
 | 
			
		||||
+	return ret;
 | 
			
		||||
+}
 | 
			
		||||
+EXPORT_SYMBOL_GPL(phy_modify_mmd_changed);
 | 
			
		||||
+
 | 
			
		||||
+/**
 | 
			
		||||
+ * __phy_modify_mmd - Convenience function for modifying a register on MMD
 | 
			
		||||
+ * @phydev: the phy_device struct
 | 
			
		||||
+ * @devad: the MMD containing register to modify
 | 
			
		||||
+ * @regnum: register number to modify
 | 
			
		||||
+ * @mask: bit mask of bits to clear
 | 
			
		||||
+ * @set: new value of bits set in mask to write to @regnum
 | 
			
		||||
+ *
 | 
			
		||||
+ * NOTE: MUST NOT be called from interrupt context,
 | 
			
		||||
+ * because the bus read/write functions may wait for an interrupt
 | 
			
		||||
+ * to conclude the operation.
 | 
			
		||||
+ */
 | 
			
		||||
+int __phy_modify_mmd(struct phy_device *phydev, int devad, u32 regnum,
 | 
			
		||||
+		     u16 mask, u16 set)
 | 
			
		||||
+{
 | 
			
		||||
+	int ret;
 | 
			
		||||
+
 | 
			
		||||
+	ret = __phy_modify_mmd_changed(phydev, devad, regnum, mask, set);
 | 
			
		||||
 
 | 
			
		||||
 	return ret < 0 ? ret : 0;
 | 
			
		||||
 }
 | 
			
		||||
--- a/include/linux/phy.h
 | 
			
		||||
+++ b/include/linux/phy.h
 | 
			
		||||
@@ -795,13 +795,21 @@ int phy_write_mmd(struct phy_device *phy
 | 
			
		||||
  */
 | 
			
		||||
 int __phy_write_mmd(struct phy_device *phydev, int devad, u32 regnum, u16 val);
 | 
			
		||||
 
 | 
			
		||||
+int __phy_modify_changed(struct phy_device *phydev, u32 regnum, u16 mask,
 | 
			
		||||
+			 u16 set);
 | 
			
		||||
+int phy_modify_changed(struct phy_device *phydev, u32 regnum, u16 mask,
 | 
			
		||||
+		       u16 set);
 | 
			
		||||
 int __phy_modify(struct phy_device *phydev, u32 regnum, u16 mask, u16 set);
 | 
			
		||||
 int phy_modify(struct phy_device *phydev, u32 regnum, u16 mask, u16 set);
 | 
			
		||||
 
 | 
			
		||||
+int __phy_modify_mmd_changed(struct phy_device *phydev, int devad, u32 regnum,
 | 
			
		||||
+			     u16 mask, u16 set);
 | 
			
		||||
+int phy_modify_mmd_changed(struct phy_device *phydev, int devad, u32 regnum,
 | 
			
		||||
+			   u16 mask, u16 set);
 | 
			
		||||
 int __phy_modify_mmd(struct phy_device *phydev, int devad, u32 regnum,
 | 
			
		||||
-		u16 mask, u16 set);
 | 
			
		||||
+		     u16 mask, u16 set);
 | 
			
		||||
 int phy_modify_mmd(struct phy_device *phydev, int devad, u32 regnum,
 | 
			
		||||
-		u16 mask, u16 set);
 | 
			
		||||
+		   u16 mask, u16 set);
 | 
			
		||||
 
 | 
			
		||||
 /**
 | 
			
		||||
  * __phy_set_bits - Convenience function for setting bits in a PHY register
 | 
			
		||||
@ -0,0 +1,64 @@
 | 
			
		||||
From 2c3db705737cf52d7d24c993f0889b25b956c718 Mon Sep 17 00:00:00 2001
 | 
			
		||||
From: Heiner Kallweit <hkallweit1@gmail.com>
 | 
			
		||||
Date: Mon, 18 Feb 2019 21:27:18 +0100
 | 
			
		||||
Subject: [PATCH 605/660] net: phy: add genphy_c45_check_and_restart_aneg
 | 
			
		||||
 | 
			
		||||
This function will be used by config_aneg callback implementations of
 | 
			
		||||
PHY drivers and allows to reduce boilerplate code.
 | 
			
		||||
 | 
			
		||||
Signed-off-by: Heiner Kallweit <hkallweit1@gmail.com>
 | 
			
		||||
Signed-off-by: David S. Miller <davem@davemloft.net>
 | 
			
		||||
---
 | 
			
		||||
 drivers/net/phy/phy-c45.c | 30 ++++++++++++++++++++++++++++++
 | 
			
		||||
 include/linux/phy.h       |  1 +
 | 
			
		||||
 2 files changed, 31 insertions(+)
 | 
			
		||||
 | 
			
		||||
--- a/drivers/net/phy/phy-c45.c
 | 
			
		||||
+++ b/drivers/net/phy/phy-c45.c
 | 
			
		||||
@@ -110,6 +110,36 @@ int genphy_c45_restart_aneg(struct phy_d
 | 
			
		||||
 EXPORT_SYMBOL_GPL(genphy_c45_restart_aneg);
 | 
			
		||||
 
 | 
			
		||||
 /**
 | 
			
		||||
+ * genphy_c45_check_and_restart_aneg - Enable and restart auto-negotiation
 | 
			
		||||
+ * @phydev: target phy_device struct
 | 
			
		||||
+ * @restart: whether aneg restart is requested
 | 
			
		||||
+ *
 | 
			
		||||
+ * This assumes that the auto-negotiation MMD is present.
 | 
			
		||||
+ *
 | 
			
		||||
+ * Check, and restart auto-negotiation if needed.
 | 
			
		||||
+ */
 | 
			
		||||
+int genphy_c45_check_and_restart_aneg(struct phy_device *phydev, bool restart)
 | 
			
		||||
+{
 | 
			
		||||
+	int ret = 0;
 | 
			
		||||
+
 | 
			
		||||
+	if (!restart) {
 | 
			
		||||
+		/* Configure and restart aneg if it wasn't set before */
 | 
			
		||||
+		ret = phy_read_mmd(phydev, MDIO_MMD_AN, MDIO_CTRL1);
 | 
			
		||||
+		if (ret < 0)
 | 
			
		||||
+			return ret;
 | 
			
		||||
+
 | 
			
		||||
+		if (!(ret & MDIO_AN_CTRL1_ENABLE))
 | 
			
		||||
+			restart = true;
 | 
			
		||||
+	}
 | 
			
		||||
+
 | 
			
		||||
+	if (restart)
 | 
			
		||||
+		ret = genphy_c45_restart_aneg(phydev);
 | 
			
		||||
+
 | 
			
		||||
+	return ret;
 | 
			
		||||
+}
 | 
			
		||||
+EXPORT_SYMBOL_GPL(genphy_c45_check_and_restart_aneg);
 | 
			
		||||
+
 | 
			
		||||
+/**
 | 
			
		||||
  * genphy_c45_aneg_done - return auto-negotiation complete status
 | 
			
		||||
  * @phydev: target phy_device struct
 | 
			
		||||
  *
 | 
			
		||||
--- a/include/linux/phy.h
 | 
			
		||||
+++ b/include/linux/phy.h
 | 
			
		||||
@@ -1098,6 +1098,7 @@ int genphy_write_mmd_unsupported(struct
 | 
			
		||||
 
 | 
			
		||||
 /* Clause 45 PHY */
 | 
			
		||||
 int genphy_c45_restart_aneg(struct phy_device *phydev);
 | 
			
		||||
+int genphy_c45_check_and_restart_aneg(struct phy_device *phydev, bool restart);
 | 
			
		||||
 int genphy_c45_aneg_done(struct phy_device *phydev);
 | 
			
		||||
 int genphy_c45_read_link(struct phy_device *phydev, u32 mmd_mask);
 | 
			
		||||
 int genphy_c45_read_lpa(struct phy_device *phydev);
 | 
			
		||||
@ -0,0 +1,59 @@
 | 
			
		||||
From 4c4323084e9a67210c8d269dceba1be99356c414 Mon Sep 17 00:00:00 2001
 | 
			
		||||
From: Russell King <rmk+kernel@armlinux.org.uk>
 | 
			
		||||
Date: Tue, 28 May 2019 10:57:18 +0100
 | 
			
		||||
Subject: [PATCH 606/660] net: phylink: remove netdev from phylink mii ioctl
 | 
			
		||||
 emulation
 | 
			
		||||
 | 
			
		||||
The netdev used in the phylink ioctl emulation is never used, so let's
 | 
			
		||||
remove it.
 | 
			
		||||
 | 
			
		||||
Signed-off-by: Russell King <rmk+kernel@armlinux.org.uk>
 | 
			
		||||
Reviewed-by: Andrew Lunn <andrew@lunn.ch>
 | 
			
		||||
Signed-off-by: David S. Miller <davem@davemloft.net>
 | 
			
		||||
Signed-off-by: Russell King <rmk+kernel@armlinux.org.uk>
 | 
			
		||||
---
 | 
			
		||||
 drivers/net/phy/phylink.c | 12 ++++--------
 | 
			
		||||
 1 file changed, 4 insertions(+), 8 deletions(-)
 | 
			
		||||
 | 
			
		||||
--- a/drivers/net/phy/phylink.c
 | 
			
		||||
+++ b/drivers/net/phy/phylink.c
 | 
			
		||||
@@ -1360,8 +1360,8 @@ EXPORT_SYMBOL_GPL(phylink_ethtool_set_ee
 | 
			
		||||
  *
 | 
			
		||||
  * FIXME: should deal with negotiation state too.
 | 
			
		||||
  */
 | 
			
		||||
-static int phylink_mii_emul_read(struct net_device *ndev, unsigned int reg,
 | 
			
		||||
-				 struct phylink_link_state *state, bool aneg)
 | 
			
		||||
+static int phylink_mii_emul_read(unsigned int reg,
 | 
			
		||||
+				 struct phylink_link_state *state)
 | 
			
		||||
 {
 | 
			
		||||
 	struct fixed_phy_status fs;
 | 
			
		||||
 	int val;
 | 
			
		||||
@@ -1376,8 +1376,6 @@ static int phylink_mii_emul_read(struct
 | 
			
		||||
 	if (reg == MII_BMSR) {
 | 
			
		||||
 		if (!state->an_complete)
 | 
			
		||||
 			val &= ~BMSR_ANEGCOMPLETE;
 | 
			
		||||
-		if (!aneg)
 | 
			
		||||
-			val &= ~BMSR_ANEGCAPABLE;
 | 
			
		||||
 	}
 | 
			
		||||
 	return val;
 | 
			
		||||
 }
 | 
			
		||||
@@ -1473,8 +1471,7 @@ static int phylink_mii_read(struct phyli
 | 
			
		||||
 	case MLO_AN_FIXED:
 | 
			
		||||
 		if (phy_id == 0) {
 | 
			
		||||
 			phylink_get_fixed_state(pl, &state);
 | 
			
		||||
-			val = phylink_mii_emul_read(pl->netdev, reg, &state,
 | 
			
		||||
-						    true);
 | 
			
		||||
+			val = phylink_mii_emul_read(reg, &state);
 | 
			
		||||
 		}
 | 
			
		||||
 		break;
 | 
			
		||||
 
 | 
			
		||||
@@ -1487,8 +1484,7 @@ static int phylink_mii_read(struct phyli
 | 
			
		||||
 			if (val < 0)
 | 
			
		||||
 				return val;
 | 
			
		||||
 
 | 
			
		||||
-			val = phylink_mii_emul_read(pl->netdev, reg, &state,
 | 
			
		||||
-						    true);
 | 
			
		||||
+			val = phylink_mii_emul_read(reg, &state);
 | 
			
		||||
 		}
 | 
			
		||||
 		break;
 | 
			
		||||
 	}
 | 
			
		||||
@ -0,0 +1,90 @@
 | 
			
		||||
From cba0aba37d2228556e0d1f776d403435868cdbfa Mon Sep 17 00:00:00 2001
 | 
			
		||||
From: Russell King <rmk+kernel@armlinux.org.uk>
 | 
			
		||||
Date: Tue, 28 May 2019 10:57:23 +0100
 | 
			
		||||
Subject: [PATCH 607/660] net: phylink: support for link gpio interrupt
 | 
			
		||||
 | 
			
		||||
Add support for using GPIO interrupts with a fixed-link GPIO rather than
 | 
			
		||||
polling the GPIO every second and invoking the phylink resolution.  This
 | 
			
		||||
avoids unnecessary calls to mac_config().
 | 
			
		||||
 | 
			
		||||
Reviewed-by: Florian Fainelli <f.fainelli@gmail.com>
 | 
			
		||||
Signed-off-by: Russell King <rmk+kernel@armlinux.org.uk>
 | 
			
		||||
Signed-off-by: David S. Miller <davem@davemloft.net>
 | 
			
		||||
Signed-off-by: Russell King <rmk+kernel@armlinux.org.uk>
 | 
			
		||||
---
 | 
			
		||||
 drivers/net/phy/phylink.c | 36 ++++++++++++++++++++++++++++++++----
 | 
			
		||||
 1 file changed, 32 insertions(+), 4 deletions(-)
 | 
			
		||||
 | 
			
		||||
--- a/drivers/net/phy/phylink.c
 | 
			
		||||
+++ b/drivers/net/phy/phylink.c
 | 
			
		||||
@@ -59,6 +59,7 @@ struct phylink {
 | 
			
		||||
 	phy_interface_t cur_interface;
 | 
			
		||||
 
 | 
			
		||||
 	struct gpio_desc *link_gpio;
 | 
			
		||||
+	unsigned int link_irq;
 | 
			
		||||
 	struct timer_list link_poll;
 | 
			
		||||
 	void (*get_fixed_state)(struct net_device *dev,
 | 
			
		||||
 				struct phylink_link_state *s);
 | 
			
		||||
@@ -645,7 +646,7 @@ void phylink_destroy(struct phylink *pl)
 | 
			
		||||
 {
 | 
			
		||||
 	if (pl->sfp_bus)
 | 
			
		||||
 		sfp_unregister_upstream(pl->sfp_bus);
 | 
			
		||||
-	if (!IS_ERR_OR_NULL(pl->link_gpio))
 | 
			
		||||
+	if (pl->link_gpio)
 | 
			
		||||
 		gpiod_put(pl->link_gpio);
 | 
			
		||||
 
 | 
			
		||||
 	cancel_work_sync(&pl->resolve);
 | 
			
		||||
@@ -912,6 +913,15 @@ void phylink_mac_change(struct phylink *
 | 
			
		||||
 }
 | 
			
		||||
 EXPORT_SYMBOL_GPL(phylink_mac_change);
 | 
			
		||||
 
 | 
			
		||||
+static irqreturn_t phylink_link_handler(int irq, void *data)
 | 
			
		||||
+{
 | 
			
		||||
+	struct phylink *pl = data;
 | 
			
		||||
+
 | 
			
		||||
+	phylink_run_resolve(pl);
 | 
			
		||||
+
 | 
			
		||||
+	return IRQ_HANDLED;
 | 
			
		||||
+}
 | 
			
		||||
+
 | 
			
		||||
 /**
 | 
			
		||||
  * phylink_start() - start a phylink instance
 | 
			
		||||
  * @pl: a pointer to a &struct phylink returned from phylink_create()
 | 
			
		||||
@@ -947,7 +957,22 @@ void phylink_start(struct phylink *pl)
 | 
			
		||||
 	clear_bit(PHYLINK_DISABLE_STOPPED, &pl->phylink_disable_state);
 | 
			
		||||
 	phylink_run_resolve(pl);
 | 
			
		||||
 
 | 
			
		||||
-	if (pl->link_an_mode == MLO_AN_FIXED && !IS_ERR(pl->link_gpio))
 | 
			
		||||
+	if (pl->link_an_mode == MLO_AN_FIXED && pl->link_gpio) {
 | 
			
		||||
+		int irq = gpiod_to_irq(pl->link_gpio);
 | 
			
		||||
+
 | 
			
		||||
+		if (irq > 0) {
 | 
			
		||||
+			if (!request_irq(irq, phylink_link_handler,
 | 
			
		||||
+					 IRQF_TRIGGER_RISING |
 | 
			
		||||
+					 IRQF_TRIGGER_FALLING,
 | 
			
		||||
+					 "netdev link", pl))
 | 
			
		||||
+				pl->link_irq = irq;
 | 
			
		||||
+			else
 | 
			
		||||
+				irq = 0;
 | 
			
		||||
+		}
 | 
			
		||||
+		if (irq <= 0)
 | 
			
		||||
+			mod_timer(&pl->link_poll, jiffies + HZ);
 | 
			
		||||
+	}
 | 
			
		||||
+	if (pl->link_an_mode == MLO_AN_FIXED && pl->get_fixed_state)
 | 
			
		||||
 		mod_timer(&pl->link_poll, jiffies + HZ);
 | 
			
		||||
 	if (pl->sfp_bus)
 | 
			
		||||
 		sfp_upstream_start(pl->sfp_bus);
 | 
			
		||||
@@ -973,8 +998,11 @@ void phylink_stop(struct phylink *pl)
 | 
			
		||||
 		phy_stop(pl->phydev);
 | 
			
		||||
 	if (pl->sfp_bus)
 | 
			
		||||
 		sfp_upstream_stop(pl->sfp_bus);
 | 
			
		||||
-	if (pl->link_an_mode == MLO_AN_FIXED && !IS_ERR(pl->link_gpio))
 | 
			
		||||
-		del_timer_sync(&pl->link_poll);
 | 
			
		||||
+	del_timer_sync(&pl->link_poll);
 | 
			
		||||
+	if (pl->link_irq) {
 | 
			
		||||
+		free_irq(pl->link_irq, pl);
 | 
			
		||||
+		pl->link_irq = 0;
 | 
			
		||||
+	}
 | 
			
		||||
 
 | 
			
		||||
 	phylink_run_resolve_and_disable(pl, PHYLINK_DISABLE_STOPPED);
 | 
			
		||||
 }
 | 
			
		||||
@ -0,0 +1,77 @@
 | 
			
		||||
From eb5df3d026824832831376bbdf04e01a52776eea Mon Sep 17 00:00:00 2001
 | 
			
		||||
From: Russell King <rmk+kernel@armlinux.org.uk>
 | 
			
		||||
Date: Tue, 28 May 2019 10:57:29 +0100
 | 
			
		||||
Subject: [PATCH 608/660] net: phy: allow Clause 45 access via mii ioctl
 | 
			
		||||
 | 
			
		||||
Allow userspace to generate Clause 45 MII access cycles via phylib.
 | 
			
		||||
This is useful for tools such as mii-diag to be able to inspect Clause
 | 
			
		||||
45 PHYs.
 | 
			
		||||
 | 
			
		||||
Reviewed-by: Florian Fainelli <f.fainelli@gmail.com>
 | 
			
		||||
Signed-off-by: Russell King <rmk+kernel@armlinux.org.uk>
 | 
			
		||||
Signed-off-by: David S. Miller <davem@davemloft.net>
 | 
			
		||||
Signed-off-by: Russell King <rmk+kernel@armlinux.org.uk>
 | 
			
		||||
---
 | 
			
		||||
 drivers/net/phy/phy.c | 33 ++++++++++++++++++++++++---------
 | 
			
		||||
 1 file changed, 24 insertions(+), 9 deletions(-)
 | 
			
		||||
 | 
			
		||||
--- a/drivers/net/phy/phy.c
 | 
			
		||||
+++ b/drivers/net/phy/phy.c
 | 
			
		||||
@@ -397,6 +397,7 @@ int phy_mii_ioctl(struct phy_device *phy
 | 
			
		||||
 	struct mii_ioctl_data *mii_data = if_mii(ifr);
 | 
			
		||||
 	u16 val = mii_data->val_in;
 | 
			
		||||
 	bool change_autoneg = false;
 | 
			
		||||
+	int prtad, devad;
 | 
			
		||||
 
 | 
			
		||||
 	switch (cmd) {
 | 
			
		||||
 	case SIOCGMIIPHY:
 | 
			
		||||
@@ -404,14 +405,29 @@ int phy_mii_ioctl(struct phy_device *phy
 | 
			
		||||
 		/* fall through */
 | 
			
		||||
 
 | 
			
		||||
 	case SIOCGMIIREG:
 | 
			
		||||
-		mii_data->val_out = mdiobus_read(phydev->mdio.bus,
 | 
			
		||||
-						 mii_data->phy_id,
 | 
			
		||||
-						 mii_data->reg_num);
 | 
			
		||||
+		if (mdio_phy_id_is_c45(mii_data->phy_id)) {
 | 
			
		||||
+			prtad = mdio_phy_id_prtad(mii_data->phy_id);
 | 
			
		||||
+			devad = mdio_phy_id_devad(mii_data->phy_id);
 | 
			
		||||
+			devad = MII_ADDR_C45 | devad << 16 | mii_data->reg_num;
 | 
			
		||||
+		} else {
 | 
			
		||||
+			prtad = mii_data->phy_id;
 | 
			
		||||
+			devad = mii_data->reg_num;
 | 
			
		||||
+		}
 | 
			
		||||
+		mii_data->val_out = mdiobus_read(phydev->mdio.bus, prtad,
 | 
			
		||||
+						 devad);
 | 
			
		||||
 		return 0;
 | 
			
		||||
 
 | 
			
		||||
 	case SIOCSMIIREG:
 | 
			
		||||
-		if (mii_data->phy_id == phydev->mdio.addr) {
 | 
			
		||||
-			switch (mii_data->reg_num) {
 | 
			
		||||
+		if (mdio_phy_id_is_c45(mii_data->phy_id)) {
 | 
			
		||||
+			prtad = mdio_phy_id_prtad(mii_data->phy_id);
 | 
			
		||||
+			devad = mdio_phy_id_devad(mii_data->phy_id);
 | 
			
		||||
+			devad = MII_ADDR_C45 | devad << 16 | mii_data->reg_num;
 | 
			
		||||
+		} else {
 | 
			
		||||
+			prtad = mii_data->phy_id;
 | 
			
		||||
+			devad = mii_data->reg_num;
 | 
			
		||||
+		}
 | 
			
		||||
+		if (prtad == phydev->mdio.addr) {
 | 
			
		||||
+			switch (devad) {
 | 
			
		||||
 			case MII_BMCR:
 | 
			
		||||
 				if ((val & (BMCR_RESET | BMCR_ANENABLE)) == 0) {
 | 
			
		||||
 					if (phydev->autoneg == AUTONEG_ENABLE)
 | 
			
		||||
@@ -443,11 +459,10 @@ int phy_mii_ioctl(struct phy_device *phy
 | 
			
		||||
 			}
 | 
			
		||||
 		}
 | 
			
		||||
 
 | 
			
		||||
-		mdiobus_write(phydev->mdio.bus, mii_data->phy_id,
 | 
			
		||||
-			      mii_data->reg_num, val);
 | 
			
		||||
+		mdiobus_write(phydev->mdio.bus, prtad, devad, val);
 | 
			
		||||
 
 | 
			
		||||
-		if (mii_data->phy_id == phydev->mdio.addr &&
 | 
			
		||||
-		    mii_data->reg_num == MII_BMCR &&
 | 
			
		||||
+		if (prtad == phydev->mdio.addr &&
 | 
			
		||||
+		    devad == MII_BMCR &&
 | 
			
		||||
 		    val & BMCR_RESET)
 | 
			
		||||
 			return phy_init_hw(phydev);
 | 
			
		||||
 
 | 
			
		||||
@ -0,0 +1,94 @@
 | 
			
		||||
From aeabfaa63285470e81fa341e14f92d68880aa160 Mon Sep 17 00:00:00 2001
 | 
			
		||||
From: Russell King <rmk+kernel@armlinux.org.uk>
 | 
			
		||||
Date: Tue, 28 May 2019 10:57:34 +0100
 | 
			
		||||
Subject: [PATCH 609/660] net: sfp: add mandatory attach/detach methods for sfp
 | 
			
		||||
 buses
 | 
			
		||||
 | 
			
		||||
Add attach and detach methods for SFP buses, which will allow us to get
 | 
			
		||||
rid of the netdev storage in sfp-bus.
 | 
			
		||||
 | 
			
		||||
Signed-off-by: Russell King <rmk+kernel@armlinux.org.uk>
 | 
			
		||||
Reviewed-by: Andrew Lunn <andrew@lunn.ch>
 | 
			
		||||
Signed-off-by: David S. Miller <davem@davemloft.net>
 | 
			
		||||
Signed-off-by: Russell King <rmk+kernel@armlinux.org.uk>
 | 
			
		||||
---
 | 
			
		||||
 drivers/net/phy/phylink.c | 16 ++++++++++++++++
 | 
			
		||||
 drivers/net/phy/sfp-bus.c |  4 ++--
 | 
			
		||||
 include/linux/sfp.h       |  6 ++++++
 | 
			
		||||
 3 files changed, 24 insertions(+), 2 deletions(-)
 | 
			
		||||
 | 
			
		||||
--- a/drivers/net/phy/phylink.c
 | 
			
		||||
+++ b/drivers/net/phy/phylink.c
 | 
			
		||||
@@ -1615,6 +1615,20 @@ int phylink_mii_ioctl(struct phylink *pl
 | 
			
		||||
 }
 | 
			
		||||
 EXPORT_SYMBOL_GPL(phylink_mii_ioctl);
 | 
			
		||||
 
 | 
			
		||||
+static void phylink_sfp_attach(void *upstream, struct sfp_bus *bus)
 | 
			
		||||
+{
 | 
			
		||||
+	struct phylink *pl = upstream;
 | 
			
		||||
+
 | 
			
		||||
+	pl->netdev->sfp_bus = bus;
 | 
			
		||||
+}
 | 
			
		||||
+
 | 
			
		||||
+static void phylink_sfp_detach(void *upstream, struct sfp_bus *bus)
 | 
			
		||||
+{
 | 
			
		||||
+	struct phylink *pl = upstream;
 | 
			
		||||
+
 | 
			
		||||
+	pl->netdev->sfp_bus = NULL;
 | 
			
		||||
+}
 | 
			
		||||
+
 | 
			
		||||
 static int phylink_sfp_module_insert(void *upstream,
 | 
			
		||||
 				     const struct sfp_eeprom_id *id)
 | 
			
		||||
 {
 | 
			
		||||
@@ -1733,6 +1747,8 @@ static void phylink_sfp_disconnect_phy(v
 | 
			
		||||
 }
 | 
			
		||||
 
 | 
			
		||||
 static const struct sfp_upstream_ops sfp_phylink_ops = {
 | 
			
		||||
+	.attach = phylink_sfp_attach,
 | 
			
		||||
+	.detach = phylink_sfp_detach,
 | 
			
		||||
 	.module_insert = phylink_sfp_module_insert,
 | 
			
		||||
 	.link_up = phylink_sfp_link_up,
 | 
			
		||||
 	.link_down = phylink_sfp_link_down,
 | 
			
		||||
--- a/drivers/net/phy/sfp-bus.c
 | 
			
		||||
+++ b/drivers/net/phy/sfp-bus.c
 | 
			
		||||
@@ -350,7 +350,7 @@ static int sfp_register_bus(struct sfp_b
 | 
			
		||||
 	bus->socket_ops->attach(bus->sfp);
 | 
			
		||||
 	if (bus->started)
 | 
			
		||||
 		bus->socket_ops->start(bus->sfp);
 | 
			
		||||
-	bus->netdev->sfp_bus = bus;
 | 
			
		||||
+	bus->upstream_ops->attach(bus->upstream, bus);
 | 
			
		||||
 	bus->registered = true;
 | 
			
		||||
 	return 0;
 | 
			
		||||
 }
 | 
			
		||||
@@ -359,8 +359,8 @@ static void sfp_unregister_bus(struct sf
 | 
			
		||||
 {
 | 
			
		||||
 	const struct sfp_upstream_ops *ops = bus->upstream_ops;
 | 
			
		||||
 
 | 
			
		||||
-	bus->netdev->sfp_bus = NULL;
 | 
			
		||||
 	if (bus->registered) {
 | 
			
		||||
+		bus->upstream_ops->detach(bus->upstream, bus);
 | 
			
		||||
 		if (bus->started)
 | 
			
		||||
 			bus->socket_ops->stop(bus->sfp);
 | 
			
		||||
 		bus->socket_ops->detach(bus->sfp);
 | 
			
		||||
--- a/include/linux/sfp.h
 | 
			
		||||
+++ b/include/linux/sfp.h
 | 
			
		||||
@@ -469,6 +469,10 @@ struct sfp_bus;
 | 
			
		||||
 
 | 
			
		||||
 /**
 | 
			
		||||
  * struct sfp_upstream_ops - upstream operations structure
 | 
			
		||||
+ * @attach: called when the sfp socket driver is bound to the upstream
 | 
			
		||||
+ *   (mandatory).
 | 
			
		||||
+ * @detach: called when the sfp socket driver is unbound from the upstream
 | 
			
		||||
+ *   (mandatory).
 | 
			
		||||
  * @module_insert: called after a module has been detected to determine
 | 
			
		||||
  *   whether the module is supported for the upstream device.
 | 
			
		||||
  * @module_remove: called after the module has been removed.
 | 
			
		||||
@@ -481,6 +485,8 @@ struct sfp_bus;
 | 
			
		||||
  *   been removed.
 | 
			
		||||
  */
 | 
			
		||||
 struct sfp_upstream_ops {
 | 
			
		||||
+	void (*attach)(void *priv, struct sfp_bus *bus);
 | 
			
		||||
+	void (*detach)(void *priv, struct sfp_bus *bus);
 | 
			
		||||
 	int (*module_insert)(void *priv, const struct sfp_eeprom_id *id);
 | 
			
		||||
 	void (*module_remove)(void *priv);
 | 
			
		||||
 	void (*link_down)(void *priv);
 | 
			
		||||
@ -0,0 +1,118 @@
 | 
			
		||||
From 60d756717d772be90d07a07cd2cc140c76da3e4a Mon Sep 17 00:00:00 2001
 | 
			
		||||
From: Russell King <rmk+kernel@armlinux.org.uk>
 | 
			
		||||
Date: Tue, 28 May 2019 10:57:39 +0100
 | 
			
		||||
Subject: [PATCH 610/660] net: sfp: remove sfp-bus use of netdevs
 | 
			
		||||
 | 
			
		||||
The sfp-bus code now no longer has any use for the network device
 | 
			
		||||
structure, so remove its use.
 | 
			
		||||
 | 
			
		||||
Signed-off-by: Russell King <rmk+kernel@armlinux.org.uk>
 | 
			
		||||
Reviewed-by: Andrew Lunn <andrew@lunn.ch>
 | 
			
		||||
Signed-off-by: David S. Miller <davem@davemloft.net>
 | 
			
		||||
Signed-off-by: Russell King <rmk+kernel@armlinux.org.uk>
 | 
			
		||||
---
 | 
			
		||||
 drivers/net/phy/phylink.c |  3 +--
 | 
			
		||||
 drivers/net/phy/sfp-bus.c | 10 +++-------
 | 
			
		||||
 include/linux/sfp.h       |  6 ++----
 | 
			
		||||
 3 files changed, 6 insertions(+), 13 deletions(-)
 | 
			
		||||
 | 
			
		||||
--- a/drivers/net/phy/phylink.c
 | 
			
		||||
+++ b/drivers/net/phy/phylink.c
 | 
			
		||||
@@ -555,8 +555,7 @@ static int phylink_register_sfp(struct p
 | 
			
		||||
 		return ret;
 | 
			
		||||
 	}
 | 
			
		||||
 
 | 
			
		||||
-	pl->sfp_bus = sfp_register_upstream(ref.fwnode, pl->netdev, pl,
 | 
			
		||||
-					    &sfp_phylink_ops);
 | 
			
		||||
+	pl->sfp_bus = sfp_register_upstream(ref.fwnode, pl, &sfp_phylink_ops);
 | 
			
		||||
 	if (!pl->sfp_bus)
 | 
			
		||||
 		return -ENOMEM;
 | 
			
		||||
 
 | 
			
		||||
--- a/drivers/net/phy/sfp-bus.c
 | 
			
		||||
+++ b/drivers/net/phy/sfp-bus.c
 | 
			
		||||
@@ -23,7 +23,6 @@ struct sfp_bus {
 | 
			
		||||
 
 | 
			
		||||
 	const struct sfp_upstream_ops *upstream_ops;
 | 
			
		||||
 	void *upstream;
 | 
			
		||||
-	struct net_device *netdev;
 | 
			
		||||
 	struct phy_device *phydev;
 | 
			
		||||
 
 | 
			
		||||
 	bool registered;
 | 
			
		||||
@@ -442,13 +441,11 @@ static void sfp_upstream_clear(struct sf
 | 
			
		||||
 {
 | 
			
		||||
 	bus->upstream_ops = NULL;
 | 
			
		||||
 	bus->upstream = NULL;
 | 
			
		||||
-	bus->netdev = NULL;
 | 
			
		||||
 }
 | 
			
		||||
 
 | 
			
		||||
 /**
 | 
			
		||||
  * sfp_register_upstream() - Register the neighbouring device
 | 
			
		||||
  * @fwnode: firmware node for the SFP bus
 | 
			
		||||
- * @ndev: network device associated with the interface
 | 
			
		||||
  * @upstream: the upstream private data
 | 
			
		||||
  * @ops: the upstream's &struct sfp_upstream_ops
 | 
			
		||||
  *
 | 
			
		||||
@@ -459,7 +456,7 @@ static void sfp_upstream_clear(struct sf
 | 
			
		||||
  * On error, returns %NULL.
 | 
			
		||||
  */
 | 
			
		||||
 struct sfp_bus *sfp_register_upstream(struct fwnode_handle *fwnode,
 | 
			
		||||
-				      struct net_device *ndev, void *upstream,
 | 
			
		||||
+				      void *upstream,
 | 
			
		||||
 				      const struct sfp_upstream_ops *ops)
 | 
			
		||||
 {
 | 
			
		||||
 	struct sfp_bus *bus = sfp_bus_get(fwnode);
 | 
			
		||||
@@ -469,7 +466,6 @@ struct sfp_bus *sfp_register_upstream(st
 | 
			
		||||
 		rtnl_lock();
 | 
			
		||||
 		bus->upstream_ops = ops;
 | 
			
		||||
 		bus->upstream = upstream;
 | 
			
		||||
-		bus->netdev = ndev;
 | 
			
		||||
 
 | 
			
		||||
 		if (bus->sfp) {
 | 
			
		||||
 			ret = sfp_register_bus(bus);
 | 
			
		||||
@@ -591,7 +587,7 @@ struct sfp_bus *sfp_register_socket(stru
 | 
			
		||||
 		bus->sfp = sfp;
 | 
			
		||||
 		bus->socket_ops = ops;
 | 
			
		||||
 
 | 
			
		||||
-		if (bus->netdev) {
 | 
			
		||||
+		if (bus->upstream_ops) {
 | 
			
		||||
 			ret = sfp_register_bus(bus);
 | 
			
		||||
 			if (ret)
 | 
			
		||||
 				sfp_socket_clear(bus);
 | 
			
		||||
@@ -611,7 +607,7 @@ EXPORT_SYMBOL_GPL(sfp_register_socket);
 | 
			
		||||
 void sfp_unregister_socket(struct sfp_bus *bus)
 | 
			
		||||
 {
 | 
			
		||||
 	rtnl_lock();
 | 
			
		||||
-	if (bus->netdev)
 | 
			
		||||
+	if (bus->upstream_ops)
 | 
			
		||||
 		sfp_unregister_bus(bus);
 | 
			
		||||
 	sfp_socket_clear(bus);
 | 
			
		||||
 	rtnl_unlock();
 | 
			
		||||
--- a/include/linux/sfp.h
 | 
			
		||||
+++ b/include/linux/sfp.h
 | 
			
		||||
@@ -464,7 +464,6 @@ enum {
 | 
			
		||||
 struct fwnode_handle;
 | 
			
		||||
 struct ethtool_eeprom;
 | 
			
		||||
 struct ethtool_modinfo;
 | 
			
		||||
-struct net_device;
 | 
			
		||||
 struct sfp_bus;
 | 
			
		||||
 
 | 
			
		||||
 /**
 | 
			
		||||
@@ -510,7 +509,7 @@ int sfp_get_module_eeprom(struct sfp_bus
 | 
			
		||||
 void sfp_upstream_start(struct sfp_bus *bus);
 | 
			
		||||
 void sfp_upstream_stop(struct sfp_bus *bus);
 | 
			
		||||
 struct sfp_bus *sfp_register_upstream(struct fwnode_handle *fwnode,
 | 
			
		||||
-				      struct net_device *ndev, void *upstream,
 | 
			
		||||
+				      void *upstream,
 | 
			
		||||
 				      const struct sfp_upstream_ops *ops);
 | 
			
		||||
 void sfp_unregister_upstream(struct sfp_bus *bus);
 | 
			
		||||
 #else
 | 
			
		||||
@@ -555,8 +554,7 @@ static inline void sfp_upstream_stop(str
 | 
			
		||||
 }
 | 
			
		||||
 
 | 
			
		||||
 static inline struct sfp_bus *sfp_register_upstream(
 | 
			
		||||
-	struct fwnode_handle *fwnode,
 | 
			
		||||
-	struct net_device *ndev, void *upstream,
 | 
			
		||||
+	struct fwnode_handle *fwnode, void *upstream,
 | 
			
		||||
 	const struct sfp_upstream_ops *ops)
 | 
			
		||||
 {
 | 
			
		||||
 	return (struct sfp_bus *)-1;
 | 
			
		||||
@ -0,0 +1,84 @@
 | 
			
		||||
From 8ac1d3e5cf7d277769ba3403d99f643fab1e3fae Mon Sep 17 00:00:00 2001
 | 
			
		||||
From: Russell King <rmk+kernel@armlinux.org.uk>
 | 
			
		||||
Date: Sat, 23 Nov 2019 14:19:54 +0000
 | 
			
		||||
Subject: [PATCH 611/660] net: phylink: avoid reducing support mask
 | 
			
		||||
 | 
			
		||||
Avoid reducing the support mask as a result of the interface type
 | 
			
		||||
selected for SFP modules, or when setting the link settings through
 | 
			
		||||
ethtool - this should only change when the supported link modes of
 | 
			
		||||
the hardware combination change.
 | 
			
		||||
 | 
			
		||||
Signed-off-by: Russell King <rmk+kernel@armlinux.org.uk>
 | 
			
		||||
Signed-off-by: David S. Miller <davem@davemloft.net>
 | 
			
		||||
---
 | 
			
		||||
 drivers/net/phy/phylink.c | 13 +++++++++----
 | 
			
		||||
 1 file changed, 9 insertions(+), 4 deletions(-)
 | 
			
		||||
 | 
			
		||||
--- a/drivers/net/phy/phylink.c
 | 
			
		||||
+++ b/drivers/net/phy/phylink.c
 | 
			
		||||
@@ -1137,6 +1137,7 @@ EXPORT_SYMBOL_GPL(phylink_ethtool_ksetti
 | 
			
		||||
 int phylink_ethtool_ksettings_set(struct phylink *pl,
 | 
			
		||||
 				  const struct ethtool_link_ksettings *kset)
 | 
			
		||||
 {
 | 
			
		||||
+	__ETHTOOL_DECLARE_LINK_MODE_MASK(support);
 | 
			
		||||
 	struct ethtool_link_ksettings our_kset;
 | 
			
		||||
 	struct phylink_link_state config;
 | 
			
		||||
 	int ret;
 | 
			
		||||
@@ -1147,11 +1148,12 @@ int phylink_ethtool_ksettings_set(struct
 | 
			
		||||
 	    kset->base.autoneg != AUTONEG_ENABLE)
 | 
			
		||||
 		return -EINVAL;
 | 
			
		||||
 
 | 
			
		||||
+	linkmode_copy(support, pl->supported);
 | 
			
		||||
 	config = pl->link_config;
 | 
			
		||||
 
 | 
			
		||||
 	/* Mask out unsupported advertisements */
 | 
			
		||||
 	linkmode_and(config.advertising, kset->link_modes.advertising,
 | 
			
		||||
-		     pl->supported);
 | 
			
		||||
+		     support);
 | 
			
		||||
 
 | 
			
		||||
 	/* FIXME: should we reject autoneg if phy/mac does not support it? */
 | 
			
		||||
 	if (kset->base.autoneg == AUTONEG_DISABLE) {
 | 
			
		||||
@@ -1161,7 +1163,7 @@ int phylink_ethtool_ksettings_set(struct
 | 
			
		||||
 		 * duplex.
 | 
			
		||||
 		 */
 | 
			
		||||
 		s = phy_lookup_setting(kset->base.speed, kset->base.duplex,
 | 
			
		||||
-				       pl->supported,
 | 
			
		||||
+				       support,
 | 
			
		||||
 				       __ETHTOOL_LINK_MODE_MASK_NBITS, false);
 | 
			
		||||
 		if (!s)
 | 
			
		||||
 			return -EINVAL;
 | 
			
		||||
@@ -1191,7 +1193,7 @@ int phylink_ethtool_ksettings_set(struct
 | 
			
		||||
 		__set_bit(ETHTOOL_LINK_MODE_Autoneg_BIT, config.advertising);
 | 
			
		||||
 	}
 | 
			
		||||
 
 | 
			
		||||
-	if (phylink_validate(pl, pl->supported, &config))
 | 
			
		||||
+	if (phylink_validate(pl, support, &config))
 | 
			
		||||
 		return -EINVAL;
 | 
			
		||||
 
 | 
			
		||||
 	/* If autonegotiation is enabled, we must have an advertisement */
 | 
			
		||||
@@ -1633,6 +1635,7 @@ static int phylink_sfp_module_insert(voi
 | 
			
		||||
 {
 | 
			
		||||
 	struct phylink *pl = upstream;
 | 
			
		||||
 	__ETHTOOL_DECLARE_LINK_MODE_MASK(support) = { 0, };
 | 
			
		||||
+	__ETHTOOL_DECLARE_LINK_MODE_MASK(support1);
 | 
			
		||||
 	struct phylink_link_state config;
 | 
			
		||||
 	phy_interface_t iface;
 | 
			
		||||
 	int ret = 0;
 | 
			
		||||
@@ -1660,6 +1663,8 @@ static int phylink_sfp_module_insert(voi
 | 
			
		||||
 		return ret;
 | 
			
		||||
 	}
 | 
			
		||||
 
 | 
			
		||||
+	linkmode_copy(support1, support);
 | 
			
		||||
+
 | 
			
		||||
 	iface = sfp_select_interface(pl->sfp_bus, id, config.advertising);
 | 
			
		||||
 	if (iface == PHY_INTERFACE_MODE_NA) {
 | 
			
		||||
 		netdev_err(pl->netdev,
 | 
			
		||||
@@ -1669,7 +1674,7 @@ static int phylink_sfp_module_insert(voi
 | 
			
		||||
 	}
 | 
			
		||||
 
 | 
			
		||||
 	config.interface = iface;
 | 
			
		||||
-	ret = phylink_validate(pl, support, &config);
 | 
			
		||||
+	ret = phylink_validate(pl, support1, &config);
 | 
			
		||||
 	if (ret) {
 | 
			
		||||
 		netdev_err(pl->netdev, "validation of %s/%s with support %*pb failed: %d\n",
 | 
			
		||||
 			   phylink_an_mode_str(MLO_AN_INBAND),
 | 
			
		||||
@ -0,0 +1,94 @@
 | 
			
		||||
From 254236a22109efa84c9e9f5a9c76a1719439e309 Mon Sep 17 00:00:00 2001
 | 
			
		||||
From: Robert Hancock <hancock@sedsystems.ca>
 | 
			
		||||
Date: Fri, 7 Jun 2019 10:42:35 -0600
 | 
			
		||||
Subject: [PATCH 612/660] net: sfp: Stop SFP polling and interrupt handling
 | 
			
		||||
 during shutdown
 | 
			
		||||
 | 
			
		||||
SFP device polling can cause problems during the shutdown process if the
 | 
			
		||||
parent devices of the network controller have been shut down already.
 | 
			
		||||
This problem was seen on the iMX6 platform with PCIe devices, where
 | 
			
		||||
accessing the device after the bus is shut down causes a hang.
 | 
			
		||||
 | 
			
		||||
Free any acquired GPIO interrupts and stop all delayed work in the SFP
 | 
			
		||||
driver during the shutdown process, so that we ensure that no pending
 | 
			
		||||
operations are still occurring after the SFP shutdown completes.
 | 
			
		||||
 | 
			
		||||
Signed-off-by: Robert Hancock <hancock@sedsystems.ca>
 | 
			
		||||
Signed-off-by: David S. Miller <davem@davemloft.net>
 | 
			
		||||
---
 | 
			
		||||
 drivers/net/phy/sfp.c | 31 ++++++++++++++++++++++++++-----
 | 
			
		||||
 1 file changed, 26 insertions(+), 5 deletions(-)
 | 
			
		||||
 | 
			
		||||
--- a/drivers/net/phy/sfp.c
 | 
			
		||||
+++ b/drivers/net/phy/sfp.c
 | 
			
		||||
@@ -183,6 +183,7 @@ struct sfp {
 | 
			
		||||
 	int (*write)(struct sfp *, bool, u8, void *, size_t);
 | 
			
		||||
 
 | 
			
		||||
 	struct gpio_desc *gpio[GPIO_MAX];
 | 
			
		||||
+	int gpio_irq[GPIO_MAX];
 | 
			
		||||
 
 | 
			
		||||
 	bool attached;
 | 
			
		||||
 	struct mutex st_mutex;			/* Protects state */
 | 
			
		||||
@@ -1803,7 +1804,7 @@ static int sfp_probe(struct platform_dev
 | 
			
		||||
 	const struct sff_data *sff;
 | 
			
		||||
 	struct sfp *sfp;
 | 
			
		||||
 	bool poll = false;
 | 
			
		||||
-	int irq, err, i;
 | 
			
		||||
+	int err, i;
 | 
			
		||||
 
 | 
			
		||||
 	sfp = sfp_alloc(&pdev->dev);
 | 
			
		||||
 	if (IS_ERR(sfp))
 | 
			
		||||
@@ -1885,19 +1886,22 @@ static int sfp_probe(struct platform_dev
 | 
			
		||||
 		if (gpio_flags[i] != GPIOD_IN || !sfp->gpio[i])
 | 
			
		||||
 			continue;
 | 
			
		||||
 
 | 
			
		||||
-		irq = gpiod_to_irq(sfp->gpio[i]);
 | 
			
		||||
-		if (!irq) {
 | 
			
		||||
+		sfp->gpio_irq[i] = gpiod_to_irq(sfp->gpio[i]);
 | 
			
		||||
+		if (!sfp->gpio_irq[i]) {
 | 
			
		||||
 			poll = true;
 | 
			
		||||
 			continue;
 | 
			
		||||
 		}
 | 
			
		||||
 
 | 
			
		||||
-		err = devm_request_threaded_irq(sfp->dev, irq, NULL, sfp_irq,
 | 
			
		||||
+		err = devm_request_threaded_irq(sfp->dev, sfp->gpio_irq[i],
 | 
			
		||||
+						NULL, sfp_irq,
 | 
			
		||||
 						IRQF_ONESHOT |
 | 
			
		||||
 						IRQF_TRIGGER_RISING |
 | 
			
		||||
 						IRQF_TRIGGER_FALLING,
 | 
			
		||||
 						dev_name(sfp->dev), sfp);
 | 
			
		||||
-		if (err)
 | 
			
		||||
+		if (err) {
 | 
			
		||||
+			sfp->gpio_irq[i] = 0;
 | 
			
		||||
 			poll = true;
 | 
			
		||||
+		}
 | 
			
		||||
 	}
 | 
			
		||||
 
 | 
			
		||||
 	if (poll)
 | 
			
		||||
@@ -1928,9 +1932,26 @@ static int sfp_remove(struct platform_de
 | 
			
		||||
 	return 0;
 | 
			
		||||
 }
 | 
			
		||||
 
 | 
			
		||||
+static void sfp_shutdown(struct platform_device *pdev)
 | 
			
		||||
+{
 | 
			
		||||
+	struct sfp *sfp = platform_get_drvdata(pdev);
 | 
			
		||||
+	int i;
 | 
			
		||||
+
 | 
			
		||||
+	for (i = 0; i < GPIO_MAX; i++) {
 | 
			
		||||
+		if (!sfp->gpio_irq[i])
 | 
			
		||||
+			continue;
 | 
			
		||||
+
 | 
			
		||||
+		devm_free_irq(sfp->dev, sfp->gpio_irq[i], sfp);
 | 
			
		||||
+	}
 | 
			
		||||
+
 | 
			
		||||
+	cancel_delayed_work_sync(&sfp->poll);
 | 
			
		||||
+	cancel_delayed_work_sync(&sfp->timeout);
 | 
			
		||||
+}
 | 
			
		||||
+
 | 
			
		||||
 static struct platform_driver sfp_driver = {
 | 
			
		||||
 	.probe = sfp_probe,
 | 
			
		||||
 	.remove = sfp_remove,
 | 
			
		||||
+	.shutdown = sfp_shutdown,
 | 
			
		||||
 	.driver = {
 | 
			
		||||
 		.name = "sfp",
 | 
			
		||||
 		.of_match_table = sfp_of_match,
 | 
			
		||||
@ -0,0 +1,141 @@
 | 
			
		||||
From b8803113537a1c1f457eba6270d46e3af305031f Mon Sep 17 00:00:00 2001
 | 
			
		||||
From: Arseny Solokha <asolokha@kb.kras.ru>
 | 
			
		||||
Date: Wed, 24 Jul 2019 20:31:39 +0700
 | 
			
		||||
Subject: [PATCH 613/660] net: phylink: don't start and stop SGMII PHYs in SFP
 | 
			
		||||
 modules twice
 | 
			
		||||
 | 
			
		||||
SFP modules connected using the SGMII interface have their own PHYs which
 | 
			
		||||
are handled by the struct phylink's phydev field. On the other hand, for
 | 
			
		||||
the modules connected using 1000Base-X interface that field is not set.
 | 
			
		||||
 | 
			
		||||
Since commit ce0aa27ff3f6 ("sfp: add sfp-bus to bridge between network
 | 
			
		||||
devices and sfp cages") phylink_start() ends up setting the phydev field
 | 
			
		||||
using the sfp-bus infrastructure, which eventually calls phy_start() on it,
 | 
			
		||||
and then calling phy_start() again on the same phydev from phylink_start()
 | 
			
		||||
itself. Similar call sequence holds for phylink_stop(), only in the reverse
 | 
			
		||||
order. This results in WARNs during network interface bringup and shutdown
 | 
			
		||||
when a copper SFP module is connected, as phy_start() and phy_stop() are
 | 
			
		||||
called twice in a row for the same phy_device:
 | 
			
		||||
 | 
			
		||||
  % ip link set up dev eth0
 | 
			
		||||
  ------------[ cut here ]------------
 | 
			
		||||
  called from state UP
 | 
			
		||||
  WARNING: CPU: 1 PID: 155 at drivers/net/phy/phy.c:895 phy_start+0x74/0xc0
 | 
			
		||||
  Modules linked in:
 | 
			
		||||
  CPU: 1 PID: 155 Comm: backend Not tainted 5.2.0+ #1
 | 
			
		||||
  NIP:  c0227bf0 LR: c0227bf0 CTR: c004d224
 | 
			
		||||
  REGS: df547720 TRAP: 0700   Not tainted  (5.2.0+)
 | 
			
		||||
  MSR:  00029000 <CE,EE,ME>  CR: 24002822  XER: 00000000
 | 
			
		||||
 | 
			
		||||
  GPR00: c0227bf0 df5477d8 df5d7080 00000014 df9d2370 df9d5ac4 1f4eb000 00000001
 | 
			
		||||
  GPR08: c061fe58 00000000 00000000 df5477d8 0000003c 100c8768 00000000 00000000
 | 
			
		||||
  GPR16: df486a00 c046f1c8 c046eea0 00000000 c046e904 c0239604 db68449c 00000000
 | 
			
		||||
  GPR24: e9083204 00000000 00000001 db684460 e9083404 00000000 db6dce00 db6dcc00
 | 
			
		||||
  NIP [c0227bf0] phy_start+0x74/0xc0
 | 
			
		||||
  LR [c0227bf0] phy_start+0x74/0xc0
 | 
			
		||||
  Call Trace:
 | 
			
		||||
  [df5477d8] [c0227bf0] phy_start+0x74/0xc0 (unreliable)
 | 
			
		||||
  [df5477e8] [c023cad0] startup_gfar+0x398/0x3f4
 | 
			
		||||
  [df547828] [c023cf08] gfar_enet_open+0x364/0x374
 | 
			
		||||
  [df547898] [c029d870] __dev_open+0xe4/0x140
 | 
			
		||||
  [df5478c8] [c029db70] __dev_change_flags+0xf0/0x188
 | 
			
		||||
  [df5478f8] [c029dc28] dev_change_flags+0x20/0x54
 | 
			
		||||
  [df547918] [c02ae304] do_setlink+0x310/0x818
 | 
			
		||||
  [df547a08] [c02b1eb8] __rtnl_newlink+0x384/0x6b0
 | 
			
		||||
  [df547c28] [c02b222c] rtnl_newlink+0x48/0x68
 | 
			
		||||
  [df547c48] [c02ad7c8] rtnetlink_rcv_msg+0x240/0x27c
 | 
			
		||||
  [df547c98] [c02cc068] netlink_rcv_skb+0x8c/0xf0
 | 
			
		||||
  [df547cd8] [c02cba3c] netlink_unicast+0x114/0x19c
 | 
			
		||||
  [df547d08] [c02cbd74] netlink_sendmsg+0x2b0/0x2c0
 | 
			
		||||
  [df547d58] [c027b668] sock_sendmsg_nosec+0x20/0x40
 | 
			
		||||
  [df547d68] [c027d080] ___sys_sendmsg+0x17c/0x1dc
 | 
			
		||||
  [df547e98] [c027df7c] __sys_sendmsg+0x68/0x84
 | 
			
		||||
  [df547ef8] [c027e430] sys_socketcall+0x1a0/0x204
 | 
			
		||||
  [df547f38] [c000d1d8] ret_from_syscall+0x0/0x38
 | 
			
		||||
  --- interrupt: c01 at 0xfd4e030
 | 
			
		||||
      LR = 0xfd4e010
 | 
			
		||||
  Instruction dump:
 | 
			
		||||
  813f0188 38800000 2b890005 419d0014 3d40c046 5529103a 394aa208 7c8a482e
 | 
			
		||||
  3c60c046 3863a1b8 4cc63182 4be009a1 <0fe00000> 48000030 3c60c046 3863a1d0
 | 
			
		||||
  ---[ end trace d4c095aeaf6ea998 ]---
 | 
			
		||||
 | 
			
		||||
and
 | 
			
		||||
 | 
			
		||||
  % ip link set down dev eth0
 | 
			
		||||
  ------------[ cut here ]------------
 | 
			
		||||
  called from state HALTED
 | 
			
		||||
  WARNING: CPU: 1 PID: 184 at drivers/net/phy/phy.c:858 phy_stop+0x3c/0x88
 | 
			
		||||
 | 
			
		||||
  <...>
 | 
			
		||||
 | 
			
		||||
  Call Trace:
 | 
			
		||||
  [df581788] [c0228450] phy_stop+0x3c/0x88 (unreliable)
 | 
			
		||||
  [df581798] [c022d548] sfp_sm_phy_detach+0x1c/0x44
 | 
			
		||||
  [df5817a8] [c022e8cc] sfp_sm_event+0x4b0/0x87c
 | 
			
		||||
  [df581848] [c022f04c] sfp_upstream_stop+0x34/0x44
 | 
			
		||||
  [df581858] [c0225608] phylink_stop+0x7c/0xe4
 | 
			
		||||
  [df581868] [c023c57c] stop_gfar+0x7c/0x94
 | 
			
		||||
  [df581888] [c023c5b8] gfar_close+0x24/0x94
 | 
			
		||||
  [df5818a8] [c0298688] __dev_close_many+0xdc/0xf8
 | 
			
		||||
  [df5818c8] [c029db58] __dev_change_flags+0xd8/0x188
 | 
			
		||||
  [df5818f8] [c029dc28] dev_change_flags+0x20/0x54
 | 
			
		||||
  [df581918] [c02ae304] do_setlink+0x310/0x818
 | 
			
		||||
  [df581a08] [c02b1eb8] __rtnl_newlink+0x384/0x6b0
 | 
			
		||||
  [df581c28] [c02b222c] rtnl_newlink+0x48/0x68
 | 
			
		||||
  [df581c48] [c02ad7c8] rtnetlink_rcv_msg+0x240/0x27c
 | 
			
		||||
  [df581c98] [c02cc068] netlink_rcv_skb+0x8c/0xf0
 | 
			
		||||
  [df581cd8] [c02cba3c] netlink_unicast+0x114/0x19c
 | 
			
		||||
  [df581d08] [c02cbd74] netlink_sendmsg+0x2b0/0x2c0
 | 
			
		||||
  [df581d58] [c027b668] sock_sendmsg_nosec+0x20/0x40
 | 
			
		||||
  [df581d68] [c027d080] ___sys_sendmsg+0x17c/0x1dc
 | 
			
		||||
  [df581e98] [c027df7c] __sys_sendmsg+0x68/0x84
 | 
			
		||||
  [df581ef8] [c027e430] sys_socketcall+0x1a0/0x204
 | 
			
		||||
  [df581f38] [c000d1d8] ret_from_syscall+0x0/0x38
 | 
			
		||||
 | 
			
		||||
  <...>
 | 
			
		||||
 | 
			
		||||
  ---[ end trace d4c095aeaf6ea999 ]---
 | 
			
		||||
 | 
			
		||||
SFP modules with the 1000Base-X interface are not affected.
 | 
			
		||||
 | 
			
		||||
Place explicit calls to phy_start() and phy_stop() before enabling or after
 | 
			
		||||
disabling an attached SFP module, where phydev is not yet set (or is
 | 
			
		||||
already unset), so they will be made only from the inside of sfp-bus, if
 | 
			
		||||
needed.
 | 
			
		||||
 | 
			
		||||
Fixes: 217962615662 ("net: phy: warn if phy_start is called from invalid state")
 | 
			
		||||
Signed-off-by: Arseny Solokha <asolokha@kb.kras.ru>
 | 
			
		||||
Acked-by: Russell King <rmk+kernel@armlinux.org.uk>
 | 
			
		||||
Signed-off-by: David S. Miller <davem@davemloft.net>
 | 
			
		||||
---
 | 
			
		||||
 drivers/net/phy/phylink.c | 8 ++++----
 | 
			
		||||
 1 file changed, 4 insertions(+), 4 deletions(-)
 | 
			
		||||
 | 
			
		||||
--- a/drivers/net/phy/phylink.c
 | 
			
		||||
+++ b/drivers/net/phy/phylink.c
 | 
			
		||||
@@ -973,10 +973,10 @@ void phylink_start(struct phylink *pl)
 | 
			
		||||
 	}
 | 
			
		||||
 	if (pl->link_an_mode == MLO_AN_FIXED && pl->get_fixed_state)
 | 
			
		||||
 		mod_timer(&pl->link_poll, jiffies + HZ);
 | 
			
		||||
-	if (pl->sfp_bus)
 | 
			
		||||
-		sfp_upstream_start(pl->sfp_bus);
 | 
			
		||||
 	if (pl->phydev)
 | 
			
		||||
 		phy_start(pl->phydev);
 | 
			
		||||
+	if (pl->sfp_bus)
 | 
			
		||||
+		sfp_upstream_start(pl->sfp_bus);
 | 
			
		||||
 }
 | 
			
		||||
 EXPORT_SYMBOL_GPL(phylink_start);
 | 
			
		||||
 
 | 
			
		||||
@@ -993,10 +993,10 @@ void phylink_stop(struct phylink *pl)
 | 
			
		||||
 {
 | 
			
		||||
 	ASSERT_RTNL();
 | 
			
		||||
 
 | 
			
		||||
-	if (pl->phydev)
 | 
			
		||||
-		phy_stop(pl->phydev);
 | 
			
		||||
 	if (pl->sfp_bus)
 | 
			
		||||
 		sfp_upstream_stop(pl->sfp_bus);
 | 
			
		||||
+	if (pl->phydev)
 | 
			
		||||
+		phy_stop(pl->phydev);
 | 
			
		||||
 	del_timer_sync(&pl->link_poll);
 | 
			
		||||
 	if (pl->link_irq) {
 | 
			
		||||
 		free_irq(pl->link_irq, pl);
 | 
			
		||||
@ -0,0 +1,179 @@
 | 
			
		||||
From 4054955f0da08c81d42220cb445820d474f1ac92 Mon Sep 17 00:00:00 2001
 | 
			
		||||
From: Russell King <rmk+kernel@armlinux.org.uk>
 | 
			
		||||
Date: Sat, 14 Sep 2019 14:21:22 +0100
 | 
			
		||||
Subject: [PATCH 614/660] net: sfp: move fwnode parsing into sfp-bus layer
 | 
			
		||||
 | 
			
		||||
Rather than parsing the sfp firmware node in phylink, parse it in the
 | 
			
		||||
sfp-bus code, so we can re-use this code for PHYs without having to
 | 
			
		||||
duplicate the parsing.
 | 
			
		||||
 | 
			
		||||
Signed-off-by: Russell King <rmk+kernel@armlinux.org.uk>
 | 
			
		||||
---
 | 
			
		||||
 drivers/net/phy/phylink.c | 21 ++++---------
 | 
			
		||||
 drivers/net/phy/sfp-bus.c | 65 +++++++++++++++++++++++++--------------
 | 
			
		||||
 include/linux/sfp.h       | 10 +++---
 | 
			
		||||
 3 files changed, 53 insertions(+), 43 deletions(-)
 | 
			
		||||
 | 
			
		||||
--- a/drivers/net/phy/phylink.c
 | 
			
		||||
+++ b/drivers/net/phy/phylink.c
 | 
			
		||||
@@ -538,26 +538,17 @@ static const struct sfp_upstream_ops sfp
 | 
			
		||||
 static int phylink_register_sfp(struct phylink *pl,
 | 
			
		||||
 				struct fwnode_handle *fwnode)
 | 
			
		||||
 {
 | 
			
		||||
-	struct fwnode_reference_args ref;
 | 
			
		||||
+	struct sfp_bus *bus;
 | 
			
		||||
 	int ret;
 | 
			
		||||
 
 | 
			
		||||
-	if (!fwnode)
 | 
			
		||||
-		return 0;
 | 
			
		||||
-
 | 
			
		||||
-	ret = fwnode_property_get_reference_args(fwnode, "sfp", NULL,
 | 
			
		||||
-						 0, 0, &ref);
 | 
			
		||||
-	if (ret < 0) {
 | 
			
		||||
-		if (ret == -ENOENT)
 | 
			
		||||
-			return 0;
 | 
			
		||||
-
 | 
			
		||||
-		netdev_err(pl->netdev, "unable to parse \"sfp\" node: %d\n",
 | 
			
		||||
-			   ret);
 | 
			
		||||
+	bus = sfp_register_upstream_node(fwnode, pl, &sfp_phylink_ops);
 | 
			
		||||
+	if (IS_ERR(bus)) {
 | 
			
		||||
+		ret = PTR_ERR(bus);
 | 
			
		||||
+		netdev_err(pl->netdev, "unable to attach SFP bus: %d\n", ret);
 | 
			
		||||
 		return ret;
 | 
			
		||||
 	}
 | 
			
		||||
 
 | 
			
		||||
-	pl->sfp_bus = sfp_register_upstream(ref.fwnode, pl, &sfp_phylink_ops);
 | 
			
		||||
-	if (!pl->sfp_bus)
 | 
			
		||||
-		return -ENOMEM;
 | 
			
		||||
+	pl->sfp_bus = bus;
 | 
			
		||||
 
 | 
			
		||||
 	return 0;
 | 
			
		||||
 }
 | 
			
		||||
--- a/drivers/net/phy/sfp-bus.c
 | 
			
		||||
+++ b/drivers/net/phy/sfp-bus.c
 | 
			
		||||
@@ -3,6 +3,7 @@
 | 
			
		||||
 #include <linux/list.h>
 | 
			
		||||
 #include <linux/mutex.h>
 | 
			
		||||
 #include <linux/phylink.h>
 | 
			
		||||
+#include <linux/property.h>
 | 
			
		||||
 #include <linux/rtnetlink.h>
 | 
			
		||||
 #include <linux/slab.h>
 | 
			
		||||
 
 | 
			
		||||
@@ -444,45 +445,63 @@ static void sfp_upstream_clear(struct sf
 | 
			
		||||
 }
 | 
			
		||||
 
 | 
			
		||||
 /**
 | 
			
		||||
- * sfp_register_upstream() - Register the neighbouring device
 | 
			
		||||
- * @fwnode: firmware node for the SFP bus
 | 
			
		||||
+ * sfp_register_upstream_node() - parse and register the neighbouring device
 | 
			
		||||
+ * @fwnode: firmware node for the parent device (MAC or PHY)
 | 
			
		||||
  * @upstream: the upstream private data
 | 
			
		||||
  * @ops: the upstream's &struct sfp_upstream_ops
 | 
			
		||||
  *
 | 
			
		||||
- * Register the upstream device (eg, PHY) with the SFP bus. MAC drivers
 | 
			
		||||
- * should use phylink, which will call this function for them. Returns
 | 
			
		||||
- * a pointer to the allocated &struct sfp_bus.
 | 
			
		||||
+ * Parse the parent device's firmware node for a SFP bus, and register the
 | 
			
		||||
+ * SFP bus using sfp_register_upstream().
 | 
			
		||||
  *
 | 
			
		||||
- * On error, returns %NULL.
 | 
			
		||||
+ * Returns: on success, a pointer to the sfp_bus structure,
 | 
			
		||||
+ *	    %NULL if no SFP is specified,
 | 
			
		||||
+ * 	    on failure, an error pointer value:
 | 
			
		||||
+ * 		corresponding to the errors detailed for
 | 
			
		||||
+ * 		fwnode_property_get_reference_args().
 | 
			
		||||
+ * 	        %-ENOMEM if we failed to allocate the bus.
 | 
			
		||||
+ *		an error from the upstream's connect_phy() method.
 | 
			
		||||
  */
 | 
			
		||||
-struct sfp_bus *sfp_register_upstream(struct fwnode_handle *fwnode,
 | 
			
		||||
-				      void *upstream,
 | 
			
		||||
-				      const struct sfp_upstream_ops *ops)
 | 
			
		||||
-{
 | 
			
		||||
-	struct sfp_bus *bus = sfp_bus_get(fwnode);
 | 
			
		||||
-	int ret = 0;
 | 
			
		||||
-
 | 
			
		||||
-	if (bus) {
 | 
			
		||||
-		rtnl_lock();
 | 
			
		||||
-		bus->upstream_ops = ops;
 | 
			
		||||
-		bus->upstream = upstream;
 | 
			
		||||
+struct sfp_bus *sfp_register_upstream_node(struct fwnode_handle *fwnode,
 | 
			
		||||
+					   void *upstream,
 | 
			
		||||
+					   const struct sfp_upstream_ops *ops)
 | 
			
		||||
+{
 | 
			
		||||
+	struct fwnode_reference_args ref;
 | 
			
		||||
+	struct sfp_bus *bus;
 | 
			
		||||
+	int ret;
 | 
			
		||||
 
 | 
			
		||||
-		if (bus->sfp) {
 | 
			
		||||
-			ret = sfp_register_bus(bus);
 | 
			
		||||
-			if (ret)
 | 
			
		||||
-				sfp_upstream_clear(bus);
 | 
			
		||||
-		}
 | 
			
		||||
-		rtnl_unlock();
 | 
			
		||||
+	ret = fwnode_property_get_reference_args(fwnode, "sfp", NULL,
 | 
			
		||||
+						 0, 0, &ref);
 | 
			
		||||
+	if (ret == -ENOENT)
 | 
			
		||||
+		return NULL;
 | 
			
		||||
+	else if (ret < 0)
 | 
			
		||||
+		return ERR_PTR(ret);
 | 
			
		||||
+
 | 
			
		||||
+	bus = sfp_bus_get(ref.fwnode);
 | 
			
		||||
+	fwnode_handle_put(ref.fwnode);
 | 
			
		||||
+	if (!bus)
 | 
			
		||||
+		return ERR_PTR(-ENOMEM);
 | 
			
		||||
+
 | 
			
		||||
+	rtnl_lock();
 | 
			
		||||
+	bus->upstream_ops = ops;
 | 
			
		||||
+	bus->upstream = upstream;
 | 
			
		||||
+
 | 
			
		||||
+	if (bus->sfp) {
 | 
			
		||||
+		ret = sfp_register_bus(bus);
 | 
			
		||||
+		if (ret)
 | 
			
		||||
+			sfp_upstream_clear(bus);
 | 
			
		||||
+	} else {
 | 
			
		||||
+		ret = 0;
 | 
			
		||||
 	}
 | 
			
		||||
+	rtnl_unlock();
 | 
			
		||||
 
 | 
			
		||||
 	if (ret) {
 | 
			
		||||
 		sfp_bus_put(bus);
 | 
			
		||||
-		bus = NULL;
 | 
			
		||||
+		bus = ERR_PTR(ret);
 | 
			
		||||
 	}
 | 
			
		||||
 
 | 
			
		||||
 	return bus;
 | 
			
		||||
 }
 | 
			
		||||
-EXPORT_SYMBOL_GPL(sfp_register_upstream);
 | 
			
		||||
+EXPORT_SYMBOL_GPL(sfp_register_upstream_node);
 | 
			
		||||
 
 | 
			
		||||
 /**
 | 
			
		||||
  * sfp_unregister_upstream() - Unregister sfp bus
 | 
			
		||||
--- a/include/linux/sfp.h
 | 
			
		||||
+++ b/include/linux/sfp.h
 | 
			
		||||
@@ -508,9 +508,9 @@ int sfp_get_module_eeprom(struct sfp_bus
 | 
			
		||||
 			  u8 *data);
 | 
			
		||||
 void sfp_upstream_start(struct sfp_bus *bus);
 | 
			
		||||
 void sfp_upstream_stop(struct sfp_bus *bus);
 | 
			
		||||
-struct sfp_bus *sfp_register_upstream(struct fwnode_handle *fwnode,
 | 
			
		||||
-				      void *upstream,
 | 
			
		||||
-				      const struct sfp_upstream_ops *ops);
 | 
			
		||||
+struct sfp_bus *sfp_register_upstream_node(struct fwnode_handle *fwnode,
 | 
			
		||||
+					   void *upstream,
 | 
			
		||||
+					   const struct sfp_upstream_ops *ops);
 | 
			
		||||
 void sfp_unregister_upstream(struct sfp_bus *bus);
 | 
			
		||||
 #else
 | 
			
		||||
 static inline int sfp_parse_port(struct sfp_bus *bus,
 | 
			
		||||
@@ -553,11 +553,11 @@ static inline void sfp_upstream_stop(str
 | 
			
		||||
 {
 | 
			
		||||
 }
 | 
			
		||||
 
 | 
			
		||||
-static inline struct sfp_bus *sfp_register_upstream(
 | 
			
		||||
+static inline struct sfp_bus *sfp_register_upstream_node(
 | 
			
		||||
 	struct fwnode_handle *fwnode, void *upstream,
 | 
			
		||||
 	const struct sfp_upstream_ops *ops)
 | 
			
		||||
 {
 | 
			
		||||
-	return (struct sfp_bus *)-1;
 | 
			
		||||
+	return NULL;
 | 
			
		||||
 }
 | 
			
		||||
 
 | 
			
		||||
 static inline void sfp_unregister_upstream(struct sfp_bus *bus)
 | 
			
		||||
@ -0,0 +1,254 @@
 | 
			
		||||
From 863b5b6941f9f43b924393b6ba2b36647e7dee42 Mon Sep 17 00:00:00 2001
 | 
			
		||||
From: Russell King <rmk+kernel@armlinux.org.uk>
 | 
			
		||||
Date: Thu, 7 Nov 2019 17:06:08 +0000
 | 
			
		||||
Subject: [PATCH 615/660] net: sfp: rework upstream interface
 | 
			
		||||
 | 
			
		||||
The current upstream interface is an all-or-nothing, which is
 | 
			
		||||
sub-optimal for future changes, as it doesn't allow the upstream driver
 | 
			
		||||
to prepare for the SFP module becoming available, as it is at boot.
 | 
			
		||||
 | 
			
		||||
Switch to a find-sfp-bus, add-upstream, del-upstream, put-sfp-bus
 | 
			
		||||
interface structure instead, which allows the upstream driver to
 | 
			
		||||
prepare for a module being available as soon as add-upstream is called.
 | 
			
		||||
 | 
			
		||||
Signed-off-by: Russell King <rmk+kernel@armlinux.org.uk>
 | 
			
		||||
---
 | 
			
		||||
 drivers/net/phy/phylink.c | 10 +++--
 | 
			
		||||
 drivers/net/phy/sfp-bus.c | 92 +++++++++++++++++++++++++++------------
 | 
			
		||||
 include/linux/sfp.h       | 25 +++++++----
 | 
			
		||||
 3 files changed, 88 insertions(+), 39 deletions(-)
 | 
			
		||||
 | 
			
		||||
--- a/drivers/net/phy/phylink.c
 | 
			
		||||
+++ b/drivers/net/phy/phylink.c
 | 
			
		||||
@@ -541,7 +541,7 @@ static int phylink_register_sfp(struct p
 | 
			
		||||
 	struct sfp_bus *bus;
 | 
			
		||||
 	int ret;
 | 
			
		||||
 
 | 
			
		||||
-	bus = sfp_register_upstream_node(fwnode, pl, &sfp_phylink_ops);
 | 
			
		||||
+	bus = sfp_bus_find_fwnode(fwnode);
 | 
			
		||||
 	if (IS_ERR(bus)) {
 | 
			
		||||
 		ret = PTR_ERR(bus);
 | 
			
		||||
 		netdev_err(pl->netdev, "unable to attach SFP bus: %d\n", ret);
 | 
			
		||||
@@ -550,7 +550,10 @@ static int phylink_register_sfp(struct p
 | 
			
		||||
 
 | 
			
		||||
 	pl->sfp_bus = bus;
 | 
			
		||||
 
 | 
			
		||||
-	return 0;
 | 
			
		||||
+	ret = sfp_bus_add_upstream(bus, pl, &sfp_phylink_ops);
 | 
			
		||||
+	sfp_bus_put(bus);
 | 
			
		||||
+
 | 
			
		||||
+	return ret;
 | 
			
		||||
 }
 | 
			
		||||
 
 | 
			
		||||
 /**
 | 
			
		||||
@@ -634,8 +637,7 @@ EXPORT_SYMBOL_GPL(phylink_create);
 | 
			
		||||
  */
 | 
			
		||||
 void phylink_destroy(struct phylink *pl)
 | 
			
		||||
 {
 | 
			
		||||
-	if (pl->sfp_bus)
 | 
			
		||||
-		sfp_unregister_upstream(pl->sfp_bus);
 | 
			
		||||
+	sfp_bus_del_upstream(pl->sfp_bus);
 | 
			
		||||
 	if (pl->link_gpio)
 | 
			
		||||
 		gpiod_put(pl->link_gpio);
 | 
			
		||||
 
 | 
			
		||||
--- a/drivers/net/phy/sfp-bus.c
 | 
			
		||||
+++ b/drivers/net/phy/sfp-bus.c
 | 
			
		||||
@@ -328,10 +328,19 @@ static void sfp_bus_release(struct kref
 | 
			
		||||
 	kfree(bus);
 | 
			
		||||
 }
 | 
			
		||||
 
 | 
			
		||||
-static void sfp_bus_put(struct sfp_bus *bus)
 | 
			
		||||
+/**
 | 
			
		||||
+ * sfp_bus_put() - put a reference on the &struct sfp_bus
 | 
			
		||||
+ * bus: the &struct sfp_bus found via sfp_bus_find_fwnode()
 | 
			
		||||
+ *
 | 
			
		||||
+ * Put a reference on the &struct sfp_bus and free the underlying structure
 | 
			
		||||
+ * if this was the last reference.
 | 
			
		||||
+ */
 | 
			
		||||
+void sfp_bus_put(struct sfp_bus *bus)
 | 
			
		||||
 {
 | 
			
		||||
-	kref_put_mutex(&bus->kref, sfp_bus_release, &sfp_mutex);
 | 
			
		||||
+	if (bus)
 | 
			
		||||
+		kref_put_mutex(&bus->kref, sfp_bus_release, &sfp_mutex);
 | 
			
		||||
 }
 | 
			
		||||
+EXPORT_SYMBOL_GPL(sfp_bus_put);
 | 
			
		||||
 
 | 
			
		||||
 static int sfp_register_bus(struct sfp_bus *bus)
 | 
			
		||||
 {
 | 
			
		||||
@@ -347,11 +356,11 @@ static int sfp_register_bus(struct sfp_b
 | 
			
		||||
 				return ret;
 | 
			
		||||
 		}
 | 
			
		||||
 	}
 | 
			
		||||
+	bus->registered = true;
 | 
			
		||||
 	bus->socket_ops->attach(bus->sfp);
 | 
			
		||||
 	if (bus->started)
 | 
			
		||||
 		bus->socket_ops->start(bus->sfp);
 | 
			
		||||
 	bus->upstream_ops->attach(bus->upstream, bus);
 | 
			
		||||
-	bus->registered = true;
 | 
			
		||||
 	return 0;
 | 
			
		||||
 }
 | 
			
		||||
 
 | 
			
		||||
@@ -445,13 +454,12 @@ static void sfp_upstream_clear(struct sf
 | 
			
		||||
 }
 | 
			
		||||
 
 | 
			
		||||
 /**
 | 
			
		||||
- * sfp_register_upstream_node() - parse and register the neighbouring device
 | 
			
		||||
+ * sfp_bus_find_fwnode() - parse and locate the SFP bus from fwnode
 | 
			
		||||
  * @fwnode: firmware node for the parent device (MAC or PHY)
 | 
			
		||||
- * @upstream: the upstream private data
 | 
			
		||||
- * @ops: the upstream's &struct sfp_upstream_ops
 | 
			
		||||
  *
 | 
			
		||||
- * Parse the parent device's firmware node for a SFP bus, and register the
 | 
			
		||||
- * SFP bus using sfp_register_upstream().
 | 
			
		||||
+ * Parse the parent device's firmware node for a SFP bus, and locate
 | 
			
		||||
+ * the sfp_bus structure, incrementing its reference count.  This must
 | 
			
		||||
+ * be put via sfp_bus_put() when done.
 | 
			
		||||
  *
 | 
			
		||||
  * Returns: on success, a pointer to the sfp_bus structure,
 | 
			
		||||
  *	    %NULL if no SFP is specified,
 | 
			
		||||
@@ -461,9 +469,7 @@ static void sfp_upstream_clear(struct sf
 | 
			
		||||
  * 	        %-ENOMEM if we failed to allocate the bus.
 | 
			
		||||
  *		an error from the upstream's connect_phy() method.
 | 
			
		||||
  */
 | 
			
		||||
-struct sfp_bus *sfp_register_upstream_node(struct fwnode_handle *fwnode,
 | 
			
		||||
-					   void *upstream,
 | 
			
		||||
-					   const struct sfp_upstream_ops *ops)
 | 
			
		||||
+struct sfp_bus *sfp_bus_find_fwnode(struct fwnode_handle *fwnode)
 | 
			
		||||
 {
 | 
			
		||||
 	struct fwnode_reference_args ref;
 | 
			
		||||
 	struct sfp_bus *bus;
 | 
			
		||||
@@ -481,7 +487,39 @@ struct sfp_bus *sfp_register_upstream_no
 | 
			
		||||
 	if (!bus)
 | 
			
		||||
 		return ERR_PTR(-ENOMEM);
 | 
			
		||||
 
 | 
			
		||||
+	return bus;
 | 
			
		||||
+}
 | 
			
		||||
+EXPORT_SYMBOL_GPL(sfp_bus_find_fwnode);
 | 
			
		||||
+
 | 
			
		||||
+/**
 | 
			
		||||
+ * sfp_bus_add_upstream() - parse and register the neighbouring device
 | 
			
		||||
+ * @bus: the &struct sfp_bus found via sfp_bus_find_fwnode()
 | 
			
		||||
+ * @upstream: the upstream private data
 | 
			
		||||
+ * @ops: the upstream's &struct sfp_upstream_ops
 | 
			
		||||
+ *
 | 
			
		||||
+ * Add upstream driver for the SFP bus, and if the bus is complete, register
 | 
			
		||||
+ * the SFP bus using sfp_register_upstream().  This takes a reference on the
 | 
			
		||||
+ * bus, so it is safe to put the bus after this call.
 | 
			
		||||
+ *
 | 
			
		||||
+ * Returns: on success, a pointer to the sfp_bus structure,
 | 
			
		||||
+ *	    %NULL if no SFP is specified,
 | 
			
		||||
+ * 	    on failure, an error pointer value:
 | 
			
		||||
+ * 		corresponding to the errors detailed for
 | 
			
		||||
+ * 		fwnode_property_get_reference_args().
 | 
			
		||||
+ * 	        %-ENOMEM if we failed to allocate the bus.
 | 
			
		||||
+ *		an error from the upstream's connect_phy() method.
 | 
			
		||||
+ */
 | 
			
		||||
+int sfp_bus_add_upstream(struct sfp_bus *bus, void *upstream,
 | 
			
		||||
+			 const struct sfp_upstream_ops *ops)
 | 
			
		||||
+{
 | 
			
		||||
+	int ret;
 | 
			
		||||
+
 | 
			
		||||
+	/* If no bus, return success */
 | 
			
		||||
+	if (!bus)
 | 
			
		||||
+		return 0;
 | 
			
		||||
+
 | 
			
		||||
 	rtnl_lock();
 | 
			
		||||
+	kref_get(&bus->kref);
 | 
			
		||||
 	bus->upstream_ops = ops;
 | 
			
		||||
 	bus->upstream = upstream;
 | 
			
		||||
 
 | 
			
		||||
@@ -494,33 +532,33 @@ struct sfp_bus *sfp_register_upstream_no
 | 
			
		||||
 	}
 | 
			
		||||
 	rtnl_unlock();
 | 
			
		||||
 
 | 
			
		||||
-	if (ret) {
 | 
			
		||||
+	if (ret)
 | 
			
		||||
 		sfp_bus_put(bus);
 | 
			
		||||
-		bus = ERR_PTR(ret);
 | 
			
		||||
-	}
 | 
			
		||||
 
 | 
			
		||||
-	return bus;
 | 
			
		||||
+	return ret;
 | 
			
		||||
 }
 | 
			
		||||
-EXPORT_SYMBOL_GPL(sfp_register_upstream_node);
 | 
			
		||||
+EXPORT_SYMBOL_GPL(sfp_bus_add_upstream);
 | 
			
		||||
 
 | 
			
		||||
 /**
 | 
			
		||||
- * sfp_unregister_upstream() - Unregister sfp bus
 | 
			
		||||
+ * sfp_bus_del_upstream() - Delete a sfp bus
 | 
			
		||||
  * @bus: a pointer to the &struct sfp_bus structure for the sfp module
 | 
			
		||||
  *
 | 
			
		||||
- * Unregister a previously registered upstream connection for the SFP
 | 
			
		||||
- * module. @bus is returned from sfp_register_upstream().
 | 
			
		||||
+ * Delete a previously registered upstream connection for the SFP
 | 
			
		||||
+ * module. @bus should have been added by sfp_bus_add_upstream().
 | 
			
		||||
  */
 | 
			
		||||
-void sfp_unregister_upstream(struct sfp_bus *bus)
 | 
			
		||||
+void sfp_bus_del_upstream(struct sfp_bus *bus)
 | 
			
		||||
 {
 | 
			
		||||
-	rtnl_lock();
 | 
			
		||||
-	if (bus->sfp)
 | 
			
		||||
-		sfp_unregister_bus(bus);
 | 
			
		||||
-	sfp_upstream_clear(bus);
 | 
			
		||||
-	rtnl_unlock();
 | 
			
		||||
+	if (bus) {
 | 
			
		||||
+		rtnl_lock();
 | 
			
		||||
+		if (bus->sfp)
 | 
			
		||||
+			sfp_unregister_bus(bus);
 | 
			
		||||
+		sfp_upstream_clear(bus);
 | 
			
		||||
+		rtnl_unlock();
 | 
			
		||||
 
 | 
			
		||||
-	sfp_bus_put(bus);
 | 
			
		||||
+		sfp_bus_put(bus);
 | 
			
		||||
+	}
 | 
			
		||||
 }
 | 
			
		||||
-EXPORT_SYMBOL_GPL(sfp_unregister_upstream);
 | 
			
		||||
+EXPORT_SYMBOL_GPL(sfp_bus_del_upstream);
 | 
			
		||||
 
 | 
			
		||||
 /* Socket driver entry points */
 | 
			
		||||
 int sfp_add_phy(struct sfp_bus *bus, struct phy_device *phydev)
 | 
			
		||||
--- a/include/linux/sfp.h
 | 
			
		||||
+++ b/include/linux/sfp.h
 | 
			
		||||
@@ -508,10 +508,11 @@ int sfp_get_module_eeprom(struct sfp_bus
 | 
			
		||||
 			  u8 *data);
 | 
			
		||||
 void sfp_upstream_start(struct sfp_bus *bus);
 | 
			
		||||
 void sfp_upstream_stop(struct sfp_bus *bus);
 | 
			
		||||
-struct sfp_bus *sfp_register_upstream_node(struct fwnode_handle *fwnode,
 | 
			
		||||
-					   void *upstream,
 | 
			
		||||
-					   const struct sfp_upstream_ops *ops);
 | 
			
		||||
-void sfp_unregister_upstream(struct sfp_bus *bus);
 | 
			
		||||
+void sfp_bus_put(struct sfp_bus *bus);
 | 
			
		||||
+struct sfp_bus *sfp_bus_find_fwnode(struct fwnode_handle *fwnode);
 | 
			
		||||
+int sfp_bus_add_upstream(struct sfp_bus *bus, void *upstream,
 | 
			
		||||
+			 const struct sfp_upstream_ops *ops);
 | 
			
		||||
+void sfp_bus_del_upstream(struct sfp_bus *bus);
 | 
			
		||||
 #else
 | 
			
		||||
 static inline int sfp_parse_port(struct sfp_bus *bus,
 | 
			
		||||
 				 const struct sfp_eeprom_id *id,
 | 
			
		||||
@@ -553,14 +554,22 @@ static inline void sfp_upstream_stop(str
 | 
			
		||||
 {
 | 
			
		||||
 }
 | 
			
		||||
 
 | 
			
		||||
-static inline struct sfp_bus *sfp_register_upstream_node(
 | 
			
		||||
-	struct fwnode_handle *fwnode, void *upstream,
 | 
			
		||||
-	const struct sfp_upstream_ops *ops)
 | 
			
		||||
+static inline void sfp_bus_put(struct sfp_bus *bus)
 | 
			
		||||
+{
 | 
			
		||||
+}
 | 
			
		||||
+
 | 
			
		||||
+static inline struct sfp_bus *sfp_bus_find_fwnode(struct fwnode_handle *fwnode)
 | 
			
		||||
 {
 | 
			
		||||
 	return NULL;
 | 
			
		||||
 }
 | 
			
		||||
 
 | 
			
		||||
-static inline void sfp_unregister_upstream(struct sfp_bus *bus)
 | 
			
		||||
+static int sfp_bus_add_upstream(struct sfp_bus *bus, void *upstream,
 | 
			
		||||
+				const struct sfp_upstream_ops *ops)
 | 
			
		||||
+{
 | 
			
		||||
+	return 0;
 | 
			
		||||
+}
 | 
			
		||||
+
 | 
			
		||||
+static inline void sfp_bus_del_upstream(struct sfp_bus *bus)
 | 
			
		||||
 {
 | 
			
		||||
 }
 | 
			
		||||
 #endif
 | 
			
		||||
@ -0,0 +1,27 @@
 | 
			
		||||
From ea7bfd81921827d334c2a23bd11ef0e4e2abafd2 Mon Sep 17 00:00:00 2001
 | 
			
		||||
From: Russell King <rmk+kernel@armlinux.org.uk>
 | 
			
		||||
Date: Sat, 9 Nov 2019 08:13:50 +0000
 | 
			
		||||
Subject: [PATCH 616/660] net: sfp: fix sfp_bus_put() kernel documentation
 | 
			
		||||
 | 
			
		||||
The kbuild test robot found a problem with htmldocs with the recent
 | 
			
		||||
change to the SFP interfaces.  Fix the kernel documentation for
 | 
			
		||||
sfp_bus_put() which was missing an '@' before the argument name
 | 
			
		||||
description.
 | 
			
		||||
 | 
			
		||||
Fixes: 727b3668b730 ("net: sfp: rework upstream interface")
 | 
			
		||||
Signed-off-by: Russell King <rmk+kernel@armlinux.org.uk>
 | 
			
		||||
---
 | 
			
		||||
 drivers/net/phy/sfp-bus.c | 2 +-
 | 
			
		||||
 1 file changed, 1 insertion(+), 1 deletion(-)
 | 
			
		||||
 | 
			
		||||
--- a/drivers/net/phy/sfp-bus.c
 | 
			
		||||
+++ b/drivers/net/phy/sfp-bus.c
 | 
			
		||||
@@ -330,7 +330,7 @@ static void sfp_bus_release(struct kref
 | 
			
		||||
 
 | 
			
		||||
 /**
 | 
			
		||||
  * sfp_bus_put() - put a reference on the &struct sfp_bus
 | 
			
		||||
- * bus: the &struct sfp_bus found via sfp_bus_find_fwnode()
 | 
			
		||||
+ * @bus: the &struct sfp_bus found via sfp_bus_find_fwnode()
 | 
			
		||||
  *
 | 
			
		||||
  * Put a reference on the &struct sfp_bus and free the underlying structure
 | 
			
		||||
  * if this was the last reference.
 | 
			
		||||
@ -0,0 +1,27 @@
 | 
			
		||||
From f76d84cd85f8bd3f083495f7ca723822cba8abc9 Mon Sep 17 00:00:00 2001
 | 
			
		||||
From: Russell King <rmk+kernel@armlinux.org.uk>
 | 
			
		||||
Date: Mon, 11 Nov 2019 10:23:35 +0000
 | 
			
		||||
Subject: [PATCH 617/660] net: sfp: fix sfp_bus_add_upstream() warning
 | 
			
		||||
 | 
			
		||||
When building with SFP disabled, the stub for sfp_bus_add_upstream()
 | 
			
		||||
missed "inline".  Add it.
 | 
			
		||||
 | 
			
		||||
Fixes: 727b3668b730 ("net: sfp: rework upstream interface")
 | 
			
		||||
Signed-off-by: Russell King <rmk+kernel@armlinux.org.uk>
 | 
			
		||||
---
 | 
			
		||||
 include/linux/sfp.h | 4 ++--
 | 
			
		||||
 1 file changed, 2 insertions(+), 2 deletions(-)
 | 
			
		||||
 | 
			
		||||
--- a/include/linux/sfp.h
 | 
			
		||||
+++ b/include/linux/sfp.h
 | 
			
		||||
@@ -563,8 +563,8 @@ static inline struct sfp_bus *sfp_bus_fi
 | 
			
		||||
 	return NULL;
 | 
			
		||||
 }
 | 
			
		||||
 
 | 
			
		||||
-static int sfp_bus_add_upstream(struct sfp_bus *bus, void *upstream,
 | 
			
		||||
-				const struct sfp_upstream_ops *ops)
 | 
			
		||||
+static inline int sfp_bus_add_upstream(struct sfp_bus *bus, void *upstream,
 | 
			
		||||
+				       const struct sfp_upstream_ops *ops)
 | 
			
		||||
 {
 | 
			
		||||
 	return 0;
 | 
			
		||||
 }
 | 
			
		||||
@ -0,0 +1,124 @@
 | 
			
		||||
From b9d6ed5cdb67533feda7f221eb06f2f9f1ff5047 Mon Sep 17 00:00:00 2001
 | 
			
		||||
From: Russell King <rmk+kernel@armlinux.org.uk>
 | 
			
		||||
Date: Fri, 11 Oct 2019 19:33:58 +0100
 | 
			
		||||
Subject: [PATCH 618/660] net: sfp: move sfp sub-state machines into separate
 | 
			
		||||
 functions
 | 
			
		||||
 | 
			
		||||
Move the SFP sub-state machines out of the main state machine function,
 | 
			
		||||
in preparation for it doing a bit more with the device state.  By doing
 | 
			
		||||
so, we ensure that our debug after the main state machine is always
 | 
			
		||||
printed.
 | 
			
		||||
 | 
			
		||||
Signed-off-by: Russell King <rmk+kernel@armlinux.org.uk>
 | 
			
		||||
---
 | 
			
		||||
 drivers/net/phy/sfp.c | 74 +++++++++++++++++++++++++------------------
 | 
			
		||||
 1 file changed, 43 insertions(+), 31 deletions(-)
 | 
			
		||||
 | 
			
		||||
--- a/drivers/net/phy/sfp.c
 | 
			
		||||
+++ b/drivers/net/phy/sfp.c
 | 
			
		||||
@@ -1479,19 +1479,34 @@ static void sfp_sm_mod_remove(struct sfp
 | 
			
		||||
 	dev_info(sfp->dev, "module removed\n");
 | 
			
		||||
 }
 | 
			
		||||
 
 | 
			
		||||
-static void sfp_sm_event(struct sfp *sfp, unsigned int event)
 | 
			
		||||
+/* This state machine tracks the netdev up/down state */
 | 
			
		||||
+static void sfp_sm_device(struct sfp *sfp, unsigned int event)
 | 
			
		||||
 {
 | 
			
		||||
-	mutex_lock(&sfp->sm_mutex);
 | 
			
		||||
+	switch (sfp->sm_dev_state) {
 | 
			
		||||
+	default:
 | 
			
		||||
+		if (event == SFP_E_DEV_UP)
 | 
			
		||||
+			sfp->sm_dev_state = SFP_DEV_UP;
 | 
			
		||||
+		break;
 | 
			
		||||
 
 | 
			
		||||
-	dev_dbg(sfp->dev, "SM: enter %s:%s:%s event %s\n",
 | 
			
		||||
-		mod_state_to_str(sfp->sm_mod_state),
 | 
			
		||||
-		dev_state_to_str(sfp->sm_dev_state),
 | 
			
		||||
-		sm_state_to_str(sfp->sm_state),
 | 
			
		||||
-		event_to_str(event));
 | 
			
		||||
+	case SFP_DEV_UP:
 | 
			
		||||
+		if (event == SFP_E_DEV_DOWN) {
 | 
			
		||||
+			/* If the module has a PHY, avoid raising TX disable
 | 
			
		||||
+			 * as this resets the PHY. Otherwise, raise it to
 | 
			
		||||
+			 * turn the laser off.
 | 
			
		||||
+			 */
 | 
			
		||||
+			if (!sfp->mod_phy)
 | 
			
		||||
+				sfp_module_tx_disable(sfp);
 | 
			
		||||
+			sfp->sm_dev_state = SFP_DEV_DOWN;
 | 
			
		||||
+		}
 | 
			
		||||
+		break;
 | 
			
		||||
+	}
 | 
			
		||||
+}
 | 
			
		||||
 
 | 
			
		||||
-	/* This state machine tracks the insert/remove state of
 | 
			
		||||
-	 * the module, and handles probing the on-board EEPROM.
 | 
			
		||||
-	 */
 | 
			
		||||
+/* This state machine tracks the insert/remove state of
 | 
			
		||||
+ * the module, and handles probing the on-board EEPROM.
 | 
			
		||||
+ */
 | 
			
		||||
+static void sfp_sm_module(struct sfp *sfp, unsigned int event)
 | 
			
		||||
+{
 | 
			
		||||
 	switch (sfp->sm_mod_state) {
 | 
			
		||||
 	default:
 | 
			
		||||
 		if (event == SFP_E_INSERT && sfp->attached) {
 | 
			
		||||
@@ -1531,27 +1546,10 @@ static void sfp_sm_event(struct sfp *sfp
 | 
			
		||||
 		}
 | 
			
		||||
 		break;
 | 
			
		||||
 	}
 | 
			
		||||
+}
 | 
			
		||||
 
 | 
			
		||||
-	/* This state machine tracks the netdev up/down state */
 | 
			
		||||
-	switch (sfp->sm_dev_state) {
 | 
			
		||||
-	default:
 | 
			
		||||
-		if (event == SFP_E_DEV_UP)
 | 
			
		||||
-			sfp->sm_dev_state = SFP_DEV_UP;
 | 
			
		||||
-		break;
 | 
			
		||||
-
 | 
			
		||||
-	case SFP_DEV_UP:
 | 
			
		||||
-		if (event == SFP_E_DEV_DOWN) {
 | 
			
		||||
-			/* If the module has a PHY, avoid raising TX disable
 | 
			
		||||
-			 * as this resets the PHY. Otherwise, raise it to
 | 
			
		||||
-			 * turn the laser off.
 | 
			
		||||
-			 */
 | 
			
		||||
-			if (!sfp->mod_phy)
 | 
			
		||||
-				sfp_module_tx_disable(sfp);
 | 
			
		||||
-			sfp->sm_dev_state = SFP_DEV_DOWN;
 | 
			
		||||
-		}
 | 
			
		||||
-		break;
 | 
			
		||||
-	}
 | 
			
		||||
-
 | 
			
		||||
+static void sfp_sm_main(struct sfp *sfp, unsigned int event)
 | 
			
		||||
+{
 | 
			
		||||
 	/* Some events are global */
 | 
			
		||||
 	if (sfp->sm_state != SFP_S_DOWN &&
 | 
			
		||||
 	    (sfp->sm_mod_state != SFP_MOD_PRESENT ||
 | 
			
		||||
@@ -1562,7 +1560,6 @@ static void sfp_sm_event(struct sfp *sfp
 | 
			
		||||
 		if (sfp->mod_phy)
 | 
			
		||||
 			sfp_sm_phy_detach(sfp);
 | 
			
		||||
 		sfp_sm_next(sfp, SFP_S_DOWN, 0);
 | 
			
		||||
-		mutex_unlock(&sfp->sm_mutex);
 | 
			
		||||
 		return;
 | 
			
		||||
 	}
 | 
			
		||||
 
 | 
			
		||||
@@ -1617,6 +1614,21 @@ static void sfp_sm_event(struct sfp *sfp
 | 
			
		||||
 	case SFP_S_TX_DISABLE:
 | 
			
		||||
 		break;
 | 
			
		||||
 	}
 | 
			
		||||
+}
 | 
			
		||||
+
 | 
			
		||||
+static void sfp_sm_event(struct sfp *sfp, unsigned int event)
 | 
			
		||||
+{
 | 
			
		||||
+	mutex_lock(&sfp->sm_mutex);
 | 
			
		||||
+
 | 
			
		||||
+	dev_dbg(sfp->dev, "SM: enter %s:%s:%s event %s\n",
 | 
			
		||||
+		mod_state_to_str(sfp->sm_mod_state),
 | 
			
		||||
+		dev_state_to_str(sfp->sm_dev_state),
 | 
			
		||||
+		sm_state_to_str(sfp->sm_state),
 | 
			
		||||
+		event_to_str(event));
 | 
			
		||||
+
 | 
			
		||||
+	sfp_sm_module(sfp, event);
 | 
			
		||||
+	sfp_sm_device(sfp, event);
 | 
			
		||||
+	sfp_sm_main(sfp, event);
 | 
			
		||||
 
 | 
			
		||||
 	dev_dbg(sfp->dev, "SM: exit %s:%s:%s\n",
 | 
			
		||||
 		mod_state_to_str(sfp->sm_mod_state),
 | 
			
		||||
@ -0,0 +1,41 @@
 | 
			
		||||
From 7e89b737c97a9e7a81dd1584000bc136b92f12fd Mon Sep 17 00:00:00 2001
 | 
			
		||||
From: Russell King <rmk+kernel@armlinux.org.uk>
 | 
			
		||||
Date: Fri, 11 Oct 2019 22:14:47 +0100
 | 
			
		||||
Subject: [PATCH 619/660] net: sfp: move tx disable on device down to main
 | 
			
		||||
 state machine
 | 
			
		||||
 | 
			
		||||
Move the tx disable assertion on device down to the main state
 | 
			
		||||
machine.
 | 
			
		||||
 | 
			
		||||
Signed-off-by: Russell King <rmk+kernel@armlinux.org.uk>
 | 
			
		||||
---
 | 
			
		||||
 drivers/net/phy/sfp.c | 10 ++--------
 | 
			
		||||
 1 file changed, 2 insertions(+), 8 deletions(-)
 | 
			
		||||
 | 
			
		||||
--- a/drivers/net/phy/sfp.c
 | 
			
		||||
+++ b/drivers/net/phy/sfp.c
 | 
			
		||||
@@ -1489,15 +1489,8 @@ static void sfp_sm_device(struct sfp *sf
 | 
			
		||||
 		break;
 | 
			
		||||
 
 | 
			
		||||
 	case SFP_DEV_UP:
 | 
			
		||||
-		if (event == SFP_E_DEV_DOWN) {
 | 
			
		||||
-			/* If the module has a PHY, avoid raising TX disable
 | 
			
		||||
-			 * as this resets the PHY. Otherwise, raise it to
 | 
			
		||||
-			 * turn the laser off.
 | 
			
		||||
-			 */
 | 
			
		||||
-			if (!sfp->mod_phy)
 | 
			
		||||
-				sfp_module_tx_disable(sfp);
 | 
			
		||||
+		if (event == SFP_E_DEV_DOWN)
 | 
			
		||||
 			sfp->sm_dev_state = SFP_DEV_DOWN;
 | 
			
		||||
-		}
 | 
			
		||||
 		break;
 | 
			
		||||
 	}
 | 
			
		||||
 }
 | 
			
		||||
@@ -1559,6 +1552,7 @@ static void sfp_sm_main(struct sfp *sfp,
 | 
			
		||||
 			sfp_sm_link_down(sfp);
 | 
			
		||||
 		if (sfp->mod_phy)
 | 
			
		||||
 			sfp_sm_phy_detach(sfp);
 | 
			
		||||
+		sfp_module_tx_disable(sfp);
 | 
			
		||||
 		sfp_sm_next(sfp, SFP_S_DOWN, 0);
 | 
			
		||||
 		return;
 | 
			
		||||
 	}
 | 
			
		||||
@ -0,0 +1,71 @@
 | 
			
		||||
From f2a1ccfc4ad4f97c98c3cc18eb32992151ce089a Mon Sep 17 00:00:00 2001
 | 
			
		||||
From: Russell King <rmk+kernel@armlinux.org.uk>
 | 
			
		||||
Date: Fri, 11 Oct 2019 22:27:21 +0100
 | 
			
		||||
Subject: [PATCH 620/660] net: sfp: rename sfp_sm_ins_next() as
 | 
			
		||||
 sfp_sm_mod_next()
 | 
			
		||||
 | 
			
		||||
sfp_sm_ins_next() modifies the module state machine.  Change it's name
 | 
			
		||||
to reflect this.
 | 
			
		||||
 | 
			
		||||
Signed-off-by: Russell King <rmk+kernel@armlinux.org.uk>
 | 
			
		||||
---
 | 
			
		||||
 drivers/net/phy/sfp.c | 16 ++++++++--------
 | 
			
		||||
 1 file changed, 8 insertions(+), 8 deletions(-)
 | 
			
		||||
 | 
			
		||||
--- a/drivers/net/phy/sfp.c
 | 
			
		||||
+++ b/drivers/net/phy/sfp.c
 | 
			
		||||
@@ -1180,7 +1180,7 @@ static void sfp_sm_next(struct sfp *sfp,
 | 
			
		||||
 	sfp_sm_set_timer(sfp, timeout);
 | 
			
		||||
 }
 | 
			
		||||
 
 | 
			
		||||
-static void sfp_sm_ins_next(struct sfp *sfp, unsigned int state,
 | 
			
		||||
+static void sfp_sm_mod_next(struct sfp *sfp, unsigned int state,
 | 
			
		||||
 			    unsigned int timeout)
 | 
			
		||||
 {
 | 
			
		||||
 	sfp->sm_mod_state = state;
 | 
			
		||||
@@ -1504,22 +1504,22 @@ static void sfp_sm_module(struct sfp *sf
 | 
			
		||||
 	default:
 | 
			
		||||
 		if (event == SFP_E_INSERT && sfp->attached) {
 | 
			
		||||
 			sfp_module_tx_disable(sfp);
 | 
			
		||||
-			sfp_sm_ins_next(sfp, SFP_MOD_PROBE, T_PROBE_INIT);
 | 
			
		||||
+			sfp_sm_mod_next(sfp, SFP_MOD_PROBE, T_PROBE_INIT);
 | 
			
		||||
 		}
 | 
			
		||||
 		break;
 | 
			
		||||
 
 | 
			
		||||
 	case SFP_MOD_PROBE:
 | 
			
		||||
 		if (event == SFP_E_REMOVE) {
 | 
			
		||||
-			sfp_sm_ins_next(sfp, SFP_MOD_EMPTY, 0);
 | 
			
		||||
+			sfp_sm_mod_next(sfp, SFP_MOD_EMPTY, 0);
 | 
			
		||||
 		} else if (event == SFP_E_TIMEOUT) {
 | 
			
		||||
 			int val = sfp_sm_mod_probe(sfp);
 | 
			
		||||
 
 | 
			
		||||
 			if (val == 0)
 | 
			
		||||
-				sfp_sm_ins_next(sfp, SFP_MOD_PRESENT, 0);
 | 
			
		||||
+				sfp_sm_mod_next(sfp, SFP_MOD_PRESENT, 0);
 | 
			
		||||
 			else if (val > 0)
 | 
			
		||||
-				sfp_sm_ins_next(sfp, SFP_MOD_HPOWER, val);
 | 
			
		||||
+				sfp_sm_mod_next(sfp, SFP_MOD_HPOWER, val);
 | 
			
		||||
 			else if (val != -EAGAIN)
 | 
			
		||||
-				sfp_sm_ins_next(sfp, SFP_MOD_ERROR, 0);
 | 
			
		||||
+				sfp_sm_mod_next(sfp, SFP_MOD_ERROR, 0);
 | 
			
		||||
 			else
 | 
			
		||||
 				sfp_sm_set_timer(sfp, T_PROBE_RETRY);
 | 
			
		||||
 		}
 | 
			
		||||
@@ -1527,7 +1527,7 @@ static void sfp_sm_module(struct sfp *sf
 | 
			
		||||
 
 | 
			
		||||
 	case SFP_MOD_HPOWER:
 | 
			
		||||
 		if (event == SFP_E_TIMEOUT) {
 | 
			
		||||
-			sfp_sm_ins_next(sfp, SFP_MOD_PRESENT, 0);
 | 
			
		||||
+			sfp_sm_mod_next(sfp, SFP_MOD_PRESENT, 0);
 | 
			
		||||
 			break;
 | 
			
		||||
 		}
 | 
			
		||||
 		/* fallthrough */
 | 
			
		||||
@@ -1535,7 +1535,7 @@ static void sfp_sm_module(struct sfp *sf
 | 
			
		||||
 	case SFP_MOD_ERROR:
 | 
			
		||||
 		if (event == SFP_E_REMOVE) {
 | 
			
		||||
 			sfp_sm_mod_remove(sfp);
 | 
			
		||||
-			sfp_sm_ins_next(sfp, SFP_MOD_EMPTY, 0);
 | 
			
		||||
+			sfp_sm_mod_next(sfp, SFP_MOD_EMPTY, 0);
 | 
			
		||||
 		}
 | 
			
		||||
 		break;
 | 
			
		||||
 	}
 | 
			
		||||
@ -0,0 +1,53 @@
 | 
			
		||||
From d2591ea5520e2ee8fa557f96bb64c23cafac4b20 Mon Sep 17 00:00:00 2001
 | 
			
		||||
From: Russell King <rmk+kernel@armlinux.org.uk>
 | 
			
		||||
Date: Tue, 15 Oct 2019 10:33:13 +0100
 | 
			
		||||
Subject: [PATCH 621/660] net: sfp: handle module remove outside state machine
 | 
			
		||||
 | 
			
		||||
Removing a module resets the module state machine back to its initial
 | 
			
		||||
state.  Rather than explicitly handling this in every state, handle it
 | 
			
		||||
early on outside of the state machine.
 | 
			
		||||
 | 
			
		||||
Signed-off-by: Russell King <rmk+kernel@armlinux.org.uk>
 | 
			
		||||
---
 | 
			
		||||
 drivers/net/phy/sfp.c | 16 +++++++++-------
 | 
			
		||||
 1 file changed, 9 insertions(+), 7 deletions(-)
 | 
			
		||||
 | 
			
		||||
--- a/drivers/net/phy/sfp.c
 | 
			
		||||
+++ b/drivers/net/phy/sfp.c
 | 
			
		||||
@@ -1500,6 +1500,14 @@ static void sfp_sm_device(struct sfp *sf
 | 
			
		||||
  */
 | 
			
		||||
 static void sfp_sm_module(struct sfp *sfp, unsigned int event)
 | 
			
		||||
 {
 | 
			
		||||
+	/* Handle remove event globally, it resets this state machine */
 | 
			
		||||
+	if (event == SFP_E_REMOVE) {
 | 
			
		||||
+		if (sfp->sm_mod_state > SFP_MOD_PROBE)
 | 
			
		||||
+			sfp_sm_mod_remove(sfp);
 | 
			
		||||
+		sfp_sm_mod_next(sfp, SFP_MOD_EMPTY, 0);
 | 
			
		||||
+		return;
 | 
			
		||||
+	}
 | 
			
		||||
+
 | 
			
		||||
 	switch (sfp->sm_mod_state) {
 | 
			
		||||
 	default:
 | 
			
		||||
 		if (event == SFP_E_INSERT && sfp->attached) {
 | 
			
		||||
@@ -1509,9 +1517,7 @@ static void sfp_sm_module(struct sfp *sf
 | 
			
		||||
 		break;
 | 
			
		||||
 
 | 
			
		||||
 	case SFP_MOD_PROBE:
 | 
			
		||||
-		if (event == SFP_E_REMOVE) {
 | 
			
		||||
-			sfp_sm_mod_next(sfp, SFP_MOD_EMPTY, 0);
 | 
			
		||||
-		} else if (event == SFP_E_TIMEOUT) {
 | 
			
		||||
+		if (event == SFP_E_TIMEOUT) {
 | 
			
		||||
 			int val = sfp_sm_mod_probe(sfp);
 | 
			
		||||
 
 | 
			
		||||
 			if (val == 0)
 | 
			
		||||
@@ -1533,10 +1539,6 @@ static void sfp_sm_module(struct sfp *sf
 | 
			
		||||
 		/* fallthrough */
 | 
			
		||||
 	case SFP_MOD_PRESENT:
 | 
			
		||||
 	case SFP_MOD_ERROR:
 | 
			
		||||
-		if (event == SFP_E_REMOVE) {
 | 
			
		||||
-			sfp_sm_mod_remove(sfp);
 | 
			
		||||
-			sfp_sm_mod_next(sfp, SFP_MOD_EMPTY, 0);
 | 
			
		||||
-		}
 | 
			
		||||
 		break;
 | 
			
		||||
 	}
 | 
			
		||||
 }
 | 
			
		||||
@ -0,0 +1,51 @@
 | 
			
		||||
From 615090acb3c0b41691f3a03522ea38350387c0e4 Mon Sep 17 00:00:00 2001
 | 
			
		||||
From: Russell King <rmk+kernel@armlinux.org.uk>
 | 
			
		||||
Date: Tue, 15 Oct 2019 10:54:15 +0100
 | 
			
		||||
Subject: [PATCH 622/660] net: sfp: rename T_PROBE_WAIT to T_SERIAL
 | 
			
		||||
 | 
			
		||||
SFF-8472 rev 12.2 defines the time for the serial bus to become ready
 | 
			
		||||
using t_serial.  Use this as our identifier for this timeout to make
 | 
			
		||||
it clear what we are referring to.
 | 
			
		||||
 | 
			
		||||
Signed-off-by: Russell King <rmk+kernel@armlinux.org.uk>
 | 
			
		||||
---
 | 
			
		||||
 drivers/net/phy/sfp.c | 13 ++++++-------
 | 
			
		||||
 1 file changed, 6 insertions(+), 7 deletions(-)
 | 
			
		||||
 | 
			
		||||
--- a/drivers/net/phy/sfp.c
 | 
			
		||||
+++ b/drivers/net/phy/sfp.c
 | 
			
		||||
@@ -147,11 +147,10 @@ static const enum gpiod_flags gpio_flags
 | 
			
		||||
  * the same length on the PCB, which means it's possible for MOD DEF 0 to
 | 
			
		||||
  * connect before the I2C bus on MOD DEF 1/2.
 | 
			
		||||
  *
 | 
			
		||||
- * The SFP MSA specifies 300ms as t_init (the time taken for TX_FAULT to
 | 
			
		||||
- * be deasserted) but makes no mention of the earliest time before we can
 | 
			
		||||
- * access the I2C EEPROM.  However, Avago modules require 300ms.
 | 
			
		||||
+ * The SFF-8472 specifies t_serial ("Time from power on until module is
 | 
			
		||||
+ * ready for data transmission over the two wire serial bus.") as 300ms.
 | 
			
		||||
  */
 | 
			
		||||
-#define T_PROBE_INIT	msecs_to_jiffies(300)
 | 
			
		||||
+#define T_SERIAL	msecs_to_jiffies(300)
 | 
			
		||||
 #define T_HPOWER_LEVEL	msecs_to_jiffies(300)
 | 
			
		||||
 #define T_PROBE_RETRY	msecs_to_jiffies(100)
 | 
			
		||||
 
 | 
			
		||||
@@ -1495,8 +1494,8 @@ static void sfp_sm_device(struct sfp *sf
 | 
			
		||||
 	}
 | 
			
		||||
 }
 | 
			
		||||
 
 | 
			
		||||
-/* This state machine tracks the insert/remove state of
 | 
			
		||||
- * the module, and handles probing the on-board EEPROM.
 | 
			
		||||
+/* This state machine tracks the insert/remove state of the module, probes
 | 
			
		||||
+ * the on-board EEPROM, and sets up the power level.
 | 
			
		||||
  */
 | 
			
		||||
 static void sfp_sm_module(struct sfp *sfp, unsigned int event)
 | 
			
		||||
 {
 | 
			
		||||
@@ -1512,7 +1511,7 @@ static void sfp_sm_module(struct sfp *sf
 | 
			
		||||
 	default:
 | 
			
		||||
 		if (event == SFP_E_INSERT && sfp->attached) {
 | 
			
		||||
 			sfp_module_tx_disable(sfp);
 | 
			
		||||
-			sfp_sm_mod_next(sfp, SFP_MOD_PROBE, T_PROBE_INIT);
 | 
			
		||||
+			sfp_sm_mod_next(sfp, SFP_MOD_PROBE, T_SERIAL);
 | 
			
		||||
 		}
 | 
			
		||||
 		break;
 | 
			
		||||
 
 | 
			
		||||
@ -0,0 +1,115 @@
 | 
			
		||||
From d4b8746219e8c0361e5ed6e440ab3a8a600d1f76 Mon Sep 17 00:00:00 2001
 | 
			
		||||
From: Russell King <rmk+kernel@armlinux.org.uk>
 | 
			
		||||
Date: Fri, 11 Oct 2019 17:24:40 +0100
 | 
			
		||||
Subject: [PATCH 623/660] net: sfp: parse SFP power requirement earlier
 | 
			
		||||
 | 
			
		||||
Parse the SFP power requirement earlier, in preparation for moving the
 | 
			
		||||
power level setup code.
 | 
			
		||||
 | 
			
		||||
Signed-off-by: Russell King <rmk+kernel@armlinux.org.uk>
 | 
			
		||||
---
 | 
			
		||||
 drivers/net/phy/sfp.c | 42 +++++++++++++++++++++++++++++-------------
 | 
			
		||||
 1 file changed, 29 insertions(+), 13 deletions(-)
 | 
			
		||||
 | 
			
		||||
--- a/drivers/net/phy/sfp.c
 | 
			
		||||
+++ b/drivers/net/phy/sfp.c
 | 
			
		||||
@@ -196,6 +196,8 @@ struct sfp {
 | 
			
		||||
 	unsigned int sm_retries;
 | 
			
		||||
 
 | 
			
		||||
 	struct sfp_eeprom_id id;
 | 
			
		||||
+	unsigned int module_power_mW;
 | 
			
		||||
+
 | 
			
		||||
 #if IS_ENABLED(CONFIG_HWMON)
 | 
			
		||||
 	struct sfp_diag diag;
 | 
			
		||||
 	struct device *hwmon_dev;
 | 
			
		||||
@@ -1309,17 +1311,14 @@ static void sfp_sm_mod_init(struct sfp *
 | 
			
		||||
 		sfp_sm_probe_phy(sfp);
 | 
			
		||||
 }
 | 
			
		||||
 
 | 
			
		||||
-static int sfp_sm_mod_hpower(struct sfp *sfp)
 | 
			
		||||
+static int sfp_module_parse_power(struct sfp *sfp)
 | 
			
		||||
 {
 | 
			
		||||
-	u32 power;
 | 
			
		||||
-	u8 val;
 | 
			
		||||
-	int err;
 | 
			
		||||
+	u32 power_mW = 1000;
 | 
			
		||||
 
 | 
			
		||||
-	power = 1000;
 | 
			
		||||
 	if (sfp->id.ext.options & cpu_to_be16(SFP_OPTIONS_POWER_DECL))
 | 
			
		||||
-		power = 1500;
 | 
			
		||||
+		power_mW = 1500;
 | 
			
		||||
 	if (sfp->id.ext.options & cpu_to_be16(SFP_OPTIONS_HIGH_POWER_LEVEL))
 | 
			
		||||
-		power = 2000;
 | 
			
		||||
+		power_mW = 2000;
 | 
			
		||||
 
 | 
			
		||||
 	if (sfp->id.ext.sff8472_compliance == SFP_SFF8472_COMPLIANCE_NONE &&
 | 
			
		||||
 	    (sfp->id.ext.diagmon & (SFP_DIAGMON_DDM | SFP_DIAGMON_ADDRMODE)) !=
 | 
			
		||||
@@ -1328,23 +1327,33 @@ static int sfp_sm_mod_hpower(struct sfp
 | 
			
		||||
 		 * or requires an address change sequence, so assume that
 | 
			
		||||
 		 * the module powers up in the indicated power mode.
 | 
			
		||||
 		 */
 | 
			
		||||
-		if (power > sfp->max_power_mW) {
 | 
			
		||||
+		if (power_mW > sfp->max_power_mW) {
 | 
			
		||||
 			dev_err(sfp->dev,
 | 
			
		||||
 				"Host does not support %u.%uW modules\n",
 | 
			
		||||
-				power / 1000, (power / 100) % 10);
 | 
			
		||||
+				power_mW / 1000, (power_mW / 100) % 10);
 | 
			
		||||
 			return -EINVAL;
 | 
			
		||||
 		}
 | 
			
		||||
 		return 0;
 | 
			
		||||
 	}
 | 
			
		||||
 
 | 
			
		||||
-	if (power > sfp->max_power_mW) {
 | 
			
		||||
+	if (power_mW > sfp->max_power_mW) {
 | 
			
		||||
 		dev_warn(sfp->dev,
 | 
			
		||||
 			 "Host does not support %u.%uW modules, module left in power mode 1\n",
 | 
			
		||||
-			 power / 1000, (power / 100) % 10);
 | 
			
		||||
+			 power_mW / 1000, (power_mW / 100) % 10);
 | 
			
		||||
 		return 0;
 | 
			
		||||
 	}
 | 
			
		||||
 
 | 
			
		||||
-	if (power <= 1000)
 | 
			
		||||
+	sfp->module_power_mW = power_mW;
 | 
			
		||||
+
 | 
			
		||||
+	return 0;
 | 
			
		||||
+}
 | 
			
		||||
+
 | 
			
		||||
+static int sfp_sm_mod_hpower(struct sfp *sfp)
 | 
			
		||||
+{
 | 
			
		||||
+	u8 val;
 | 
			
		||||
+	int err;
 | 
			
		||||
+
 | 
			
		||||
+	if (sfp->module_power_mW <= 1000)
 | 
			
		||||
 		return 0;
 | 
			
		||||
 
 | 
			
		||||
 	err = sfp_read(sfp, true, SFP_EXT_STATUS, &val, sizeof(val));
 | 
			
		||||
@@ -1364,7 +1373,8 @@ static int sfp_sm_mod_hpower(struct sfp
 | 
			
		||||
 	}
 | 
			
		||||
 
 | 
			
		||||
 	dev_info(sfp->dev, "Module switched to %u.%uW power level\n",
 | 
			
		||||
-		 power / 1000, (power / 100) % 10);
 | 
			
		||||
+		 sfp->module_power_mW / 1000,
 | 
			
		||||
+		 (sfp->module_power_mW / 100) % 10);
 | 
			
		||||
 	return T_HPOWER_LEVEL;
 | 
			
		||||
 
 | 
			
		||||
 err:
 | 
			
		||||
@@ -1451,6 +1461,11 @@ static int sfp_sm_mod_probe(struct sfp *
 | 
			
		||||
 		dev_warn(sfp->dev,
 | 
			
		||||
 			 "module address swap to access page 0xA2 is not supported.\n");
 | 
			
		||||
 
 | 
			
		||||
+	/* Parse the module power requirement */
 | 
			
		||||
+	ret = sfp_module_parse_power(sfp);
 | 
			
		||||
+	if (ret < 0)
 | 
			
		||||
+		return ret;
 | 
			
		||||
+
 | 
			
		||||
 	ret = sfp_hwmon_insert(sfp);
 | 
			
		||||
 	if (ret < 0)
 | 
			
		||||
 		return ret;
 | 
			
		||||
@@ -1474,6 +1489,7 @@ static void sfp_sm_mod_remove(struct sfp
 | 
			
		||||
 	sfp_module_tx_disable(sfp);
 | 
			
		||||
 
 | 
			
		||||
 	memset(&sfp->id, 0, sizeof(sfp->id));
 | 
			
		||||
+	sfp->module_power_mW = 0;
 | 
			
		||||
 
 | 
			
		||||
 	dev_info(sfp->dev, "module removed\n");
 | 
			
		||||
 }
 | 
			
		||||
@ -0,0 +1,65 @@
 | 
			
		||||
From dca678b8838945572cf50584cb33a7199c1fd397 Mon Sep 17 00:00:00 2001
 | 
			
		||||
From: Russell King <rmk+kernel@armlinux.org.uk>
 | 
			
		||||
Date: Thu, 17 Oct 2019 00:24:18 +0100
 | 
			
		||||
Subject: [PATCH 624/660] net: sfp: avoid power switch on address-change
 | 
			
		||||
 modules
 | 
			
		||||
 | 
			
		||||
If the module indicates that it requires an address change sequence to
 | 
			
		||||
switch between address 0x50 and 0x51, which we don't support, we can't
 | 
			
		||||
write to the register that controls the power mode to switch to high
 | 
			
		||||
power mode.  Warn the user that the module may not be functional in
 | 
			
		||||
this case, and don't try to change the power mode.
 | 
			
		||||
 | 
			
		||||
Signed-off-by: Russell King <rmk+kernel@armlinux.org.uk>
 | 
			
		||||
---
 | 
			
		||||
 drivers/net/phy/sfp.c | 31 ++++++++++++++++++++-----------
 | 
			
		||||
 1 file changed, 20 insertions(+), 11 deletions(-)
 | 
			
		||||
 | 
			
		||||
--- a/drivers/net/phy/sfp.c
 | 
			
		||||
+++ b/drivers/net/phy/sfp.c
 | 
			
		||||
@@ -1320,25 +1320,34 @@ static int sfp_module_parse_power(struct
 | 
			
		||||
 	if (sfp->id.ext.options & cpu_to_be16(SFP_OPTIONS_HIGH_POWER_LEVEL))
 | 
			
		||||
 		power_mW = 2000;
 | 
			
		||||
 
 | 
			
		||||
-	if (sfp->id.ext.sff8472_compliance == SFP_SFF8472_COMPLIANCE_NONE &&
 | 
			
		||||
-	    (sfp->id.ext.diagmon & (SFP_DIAGMON_DDM | SFP_DIAGMON_ADDRMODE)) !=
 | 
			
		||||
-	    SFP_DIAGMON_DDM) {
 | 
			
		||||
-		/* The module appears not to implement bus address 0xa2,
 | 
			
		||||
-		 * or requires an address change sequence, so assume that
 | 
			
		||||
-		 * the module powers up in the indicated power mode.
 | 
			
		||||
-		 */
 | 
			
		||||
-		if (power_mW > sfp->max_power_mW) {
 | 
			
		||||
+	if (power_mW > sfp->max_power_mW) {
 | 
			
		||||
+		/* Module power specification exceeds the allowed maximum. */
 | 
			
		||||
+		if (sfp->id.ext.sff8472_compliance ==
 | 
			
		||||
+			SFP_SFF8472_COMPLIANCE_NONE &&
 | 
			
		||||
+		    !(sfp->id.ext.diagmon & SFP_DIAGMON_DDM)) {
 | 
			
		||||
+			/* The module appears not to implement bus address
 | 
			
		||||
+			 * 0xa2, so assume that the module powers up in the
 | 
			
		||||
+			 * indicated mode.
 | 
			
		||||
+			 */
 | 
			
		||||
 			dev_err(sfp->dev,
 | 
			
		||||
 				"Host does not support %u.%uW modules\n",
 | 
			
		||||
 				power_mW / 1000, (power_mW / 100) % 10);
 | 
			
		||||
 			return -EINVAL;
 | 
			
		||||
+		} else {
 | 
			
		||||
+			dev_warn(sfp->dev,
 | 
			
		||||
+				 "Host does not support %u.%uW modules, module left in power mode 1\n",
 | 
			
		||||
+				 power_mW / 1000, (power_mW / 100) % 10);
 | 
			
		||||
+			return 0;
 | 
			
		||||
 		}
 | 
			
		||||
-		return 0;
 | 
			
		||||
 	}
 | 
			
		||||
 
 | 
			
		||||
-	if (power_mW > sfp->max_power_mW) {
 | 
			
		||||
+	/* If the module requires a higher power mode, but also requires
 | 
			
		||||
+	 * an address change sequence, warn the user that the module may
 | 
			
		||||
+	 * not be functional.
 | 
			
		||||
+	 */
 | 
			
		||||
+	if (sfp->id.ext.diagmon & SFP_DIAGMON_ADDRMODE && power_mW > 1000) {
 | 
			
		||||
 		dev_warn(sfp->dev,
 | 
			
		||||
-			 "Host does not support %u.%uW modules, module left in power mode 1\n",
 | 
			
		||||
+			 "Address Change Sequence not supported but module requies %u.%uW, module may not be functional\n",
 | 
			
		||||
 			 power_mW / 1000, (power_mW / 100) % 10);
 | 
			
		||||
 		return 0;
 | 
			
		||||
 	}
 | 
			
		||||
@ -0,0 +1,52 @@
 | 
			
		||||
From df5c4d93c5a59cba0f7479a4cd4e22b50726ce88 Mon Sep 17 00:00:00 2001
 | 
			
		||||
From: Russell King <rmk+kernel@armlinux.org.uk>
 | 
			
		||||
Date: Thu, 17 Oct 2019 11:12:42 +0100
 | 
			
		||||
Subject: [PATCH 625/660] net: sfp: control TX_DISABLE and phy only from main
 | 
			
		||||
 state machine
 | 
			
		||||
 | 
			
		||||
We initialise TX_DISABLE when the sfp cage is probed, and then
 | 
			
		||||
maintain its state in the main state machine.  However, the module
 | 
			
		||||
state machine:
 | 
			
		||||
- negates it when detecting a newly inserted module when it's already
 | 
			
		||||
  guaranteed to be negated.
 | 
			
		||||
- negates it when the module is removed, but the main state machine
 | 
			
		||||
  will do this anyway.
 | 
			
		||||
 | 
			
		||||
Make TX_DISABLE entirely controlled by the main state machine.
 | 
			
		||||
 | 
			
		||||
The main state machine also probes the module for a PHY, and removes
 | 
			
		||||
the PHY when the the module is removed.  Hence, removing the PHY in
 | 
			
		||||
sfp_sm_module_remove() is also redundant, and is a left-over from
 | 
			
		||||
when we tried to probe for the PHY from the module state machine.
 | 
			
		||||
 | 
			
		||||
Signed-off-by: Russell King <rmk+kernel@armlinux.org.uk>
 | 
			
		||||
---
 | 
			
		||||
 drivers/net/phy/sfp.c | 9 +--------
 | 
			
		||||
 1 file changed, 1 insertion(+), 8 deletions(-)
 | 
			
		||||
 | 
			
		||||
--- a/drivers/net/phy/sfp.c
 | 
			
		||||
+++ b/drivers/net/phy/sfp.c
 | 
			
		||||
@@ -1492,11 +1492,6 @@ static void sfp_sm_mod_remove(struct sfp
 | 
			
		||||
 
 | 
			
		||||
 	sfp_hwmon_remove(sfp);
 | 
			
		||||
 
 | 
			
		||||
-	if (sfp->mod_phy)
 | 
			
		||||
-		sfp_sm_phy_detach(sfp);
 | 
			
		||||
-
 | 
			
		||||
-	sfp_module_tx_disable(sfp);
 | 
			
		||||
-
 | 
			
		||||
 	memset(&sfp->id, 0, sizeof(sfp->id));
 | 
			
		||||
 	sfp->module_power_mW = 0;
 | 
			
		||||
 
 | 
			
		||||
@@ -1534,10 +1529,8 @@ static void sfp_sm_module(struct sfp *sf
 | 
			
		||||
 
 | 
			
		||||
 	switch (sfp->sm_mod_state) {
 | 
			
		||||
 	default:
 | 
			
		||||
-		if (event == SFP_E_INSERT && sfp->attached) {
 | 
			
		||||
-			sfp_module_tx_disable(sfp);
 | 
			
		||||
+		if (event == SFP_E_INSERT && sfp->attached)
 | 
			
		||||
 			sfp_sm_mod_next(sfp, SFP_MOD_PROBE, T_SERIAL);
 | 
			
		||||
-		}
 | 
			
		||||
 		break;
 | 
			
		||||
 
 | 
			
		||||
 	case SFP_MOD_PROBE:
 | 
			
		||||
@ -0,0 +1,53 @@
 | 
			
		||||
From 5ed0bd49b2d3ac4439c2d7f44e5a82b7cf6f409a Mon Sep 17 00:00:00 2001
 | 
			
		||||
From: Russell King <rmk+kernel@armlinux.org.uk>
 | 
			
		||||
Date: Fri, 18 Oct 2019 10:09:02 +0100
 | 
			
		||||
Subject: [PATCH 626/660] net: sfp: split the PHY probe from sfp_sm_mod_init()
 | 
			
		||||
 | 
			
		||||
Move the PHY probe into a separate function, splitting it from
 | 
			
		||||
sfp_sm_mod_init().  This will allow us to eliminate the 50ms mdelay()
 | 
			
		||||
inside the state machine.
 | 
			
		||||
 | 
			
		||||
Signed-off-by: Russell King <rmk+kernel@armlinux.org.uk>
 | 
			
		||||
---
 | 
			
		||||
 drivers/net/phy/sfp.c | 21 +++++++++++++--------
 | 
			
		||||
 1 file changed, 13 insertions(+), 8 deletions(-)
 | 
			
		||||
 | 
			
		||||
--- a/drivers/net/phy/sfp.c
 | 
			
		||||
+++ b/drivers/net/phy/sfp.c
 | 
			
		||||
@@ -1288,14 +1288,10 @@ static void sfp_sm_fault(struct sfp *sfp
 | 
			
		||||
 static void sfp_sm_mod_init(struct sfp *sfp)
 | 
			
		||||
 {
 | 
			
		||||
 	sfp_module_tx_enable(sfp);
 | 
			
		||||
+}
 | 
			
		||||
 
 | 
			
		||||
-	/* Wait t_init before indicating that the link is up, provided the
 | 
			
		||||
-	 * current state indicates no TX_FAULT.  If TX_FAULT clears before
 | 
			
		||||
-	 * this time, that's fine too.
 | 
			
		||||
-	 */
 | 
			
		||||
-	sfp_sm_next(sfp, SFP_S_INIT, T_INIT_JIFFIES);
 | 
			
		||||
-	sfp->sm_retries = 5;
 | 
			
		||||
-
 | 
			
		||||
+static void sfp_sm_probe_for_phy(struct sfp *sfp)
 | 
			
		||||
+{
 | 
			
		||||
 	/* Setting the serdes link mode is guesswork: there's no
 | 
			
		||||
 	 * field in the EEPROM which indicates what mode should
 | 
			
		||||
 	 * be used.
 | 
			
		||||
@@ -1580,8 +1576,17 @@ static void sfp_sm_main(struct sfp *sfp,
 | 
			
		||||
 	switch (sfp->sm_state) {
 | 
			
		||||
 	case SFP_S_DOWN:
 | 
			
		||||
 		if (sfp->sm_mod_state == SFP_MOD_PRESENT &&
 | 
			
		||||
-		    sfp->sm_dev_state == SFP_DEV_UP)
 | 
			
		||||
+		    sfp->sm_dev_state == SFP_DEV_UP) {
 | 
			
		||||
 			sfp_sm_mod_init(sfp);
 | 
			
		||||
+			sfp_sm_probe_for_phy(sfp);
 | 
			
		||||
+
 | 
			
		||||
+			/* Wait t_init before indicating that the link is up,
 | 
			
		||||
+			 * provided the current state indicates no TX_FAULT. If
 | 
			
		||||
+			 * TX_FAULT clears before this time, that's fine too.
 | 
			
		||||
+			 */
 | 
			
		||||
+			sfp_sm_next(sfp, SFP_S_INIT, T_INIT_JIFFIES);
 | 
			
		||||
+			sfp->sm_retries = 5;
 | 
			
		||||
+		}
 | 
			
		||||
 		break;
 | 
			
		||||
 
 | 
			
		||||
 	case SFP_S_INIT:
 | 
			
		||||
@ -0,0 +1,130 @@
 | 
			
		||||
From 0fe72afaa31f98ebd71bd6683fc47021105d0157 Mon Sep 17 00:00:00 2001
 | 
			
		||||
From: Russell King <rmk+kernel@armlinux.org.uk>
 | 
			
		||||
Date: Fri, 18 Oct 2019 10:21:46 +0100
 | 
			
		||||
Subject: [PATCH 627/660] net: sfp: eliminate mdelay() from PHY probe
 | 
			
		||||
 | 
			
		||||
Rather than using mdelay() to wait before probing the PHY (which holds
 | 
			
		||||
several locks, including the rtnl lock), add an extra wait state to
 | 
			
		||||
the state machine to introduce the 50ms delay without holding any
 | 
			
		||||
locks.
 | 
			
		||||
 | 
			
		||||
Signed-off-by: Russell King <rmk+kernel@armlinux.org.uk>
 | 
			
		||||
---
 | 
			
		||||
 drivers/net/phy/sfp.c | 52 +++++++++++++++++++++++++++++++++----------
 | 
			
		||||
 1 file changed, 40 insertions(+), 12 deletions(-)
 | 
			
		||||
 | 
			
		||||
--- a/drivers/net/phy/sfp.c
 | 
			
		||||
+++ b/drivers/net/phy/sfp.c
 | 
			
		||||
@@ -52,6 +52,7 @@ enum {
 | 
			
		||||
 	SFP_DEV_UP,
 | 
			
		||||
 
 | 
			
		||||
 	SFP_S_DOWN = 0,
 | 
			
		||||
+	SFP_S_WAIT,
 | 
			
		||||
 	SFP_S_INIT,
 | 
			
		||||
 	SFP_S_WAIT_LOS,
 | 
			
		||||
 	SFP_S_LINK_UP,
 | 
			
		||||
@@ -108,6 +109,7 @@ static const char *event_to_str(unsigned
 | 
			
		||||
 
 | 
			
		||||
 static const char * const sm_state_strings[] = {
 | 
			
		||||
 	[SFP_S_DOWN] = "down",
 | 
			
		||||
+	[SFP_S_WAIT] = "wait",
 | 
			
		||||
 	[SFP_S_INIT] = "init",
 | 
			
		||||
 	[SFP_S_WAIT_LOS] = "wait_los",
 | 
			
		||||
 	[SFP_S_LINK_UP] = "link_up",
 | 
			
		||||
@@ -139,6 +141,7 @@ static const enum gpiod_flags gpio_flags
 | 
			
		||||
 	GPIOD_ASIS,
 | 
			
		||||
 };
 | 
			
		||||
 
 | 
			
		||||
+#define T_WAIT		msecs_to_jiffies(50)
 | 
			
		||||
 #define T_INIT_JIFFIES	msecs_to_jiffies(300)
 | 
			
		||||
 #define T_RESET_US	10
 | 
			
		||||
 #define T_FAULT_RECOVER	msecs_to_jiffies(1000)
 | 
			
		||||
@@ -159,9 +162,6 @@ static const enum gpiod_flags gpio_flags
 | 
			
		||||
  */
 | 
			
		||||
 #define SFP_PHY_ADDR	22
 | 
			
		||||
 
 | 
			
		||||
-/* Give this long for the PHY to reset. */
 | 
			
		||||
-#define T_PHY_RESET_MS	50
 | 
			
		||||
-
 | 
			
		||||
 struct sff_data {
 | 
			
		||||
 	unsigned int gpios;
 | 
			
		||||
 	bool (*module_supported)(const struct sfp_eeprom_id *id);
 | 
			
		||||
@@ -1202,8 +1202,6 @@ static void sfp_sm_probe_phy(struct sfp
 | 
			
		||||
 	struct phy_device *phy;
 | 
			
		||||
 	int err;
 | 
			
		||||
 
 | 
			
		||||
-	msleep(T_PHY_RESET_MS);
 | 
			
		||||
-
 | 
			
		||||
 	phy = mdiobus_scan(sfp->i2c_mii, SFP_PHY_ADDR);
 | 
			
		||||
 	if (phy == ERR_PTR(-ENODEV)) {
 | 
			
		||||
 		dev_info(sfp->dev, "no PHY detected\n");
 | 
			
		||||
@@ -1558,6 +1556,8 @@ static void sfp_sm_module(struct sfp *sf
 | 
			
		||||
 
 | 
			
		||||
 static void sfp_sm_main(struct sfp *sfp, unsigned int event)
 | 
			
		||||
 {
 | 
			
		||||
+	unsigned long timeout;
 | 
			
		||||
+
 | 
			
		||||
 	/* Some events are global */
 | 
			
		||||
 	if (sfp->sm_state != SFP_S_DOWN &&
 | 
			
		||||
 	    (sfp->sm_mod_state != SFP_MOD_PRESENT ||
 | 
			
		||||
@@ -1575,17 +1575,45 @@ static void sfp_sm_main(struct sfp *sfp,
 | 
			
		||||
 	/* The main state machine */
 | 
			
		||||
 	switch (sfp->sm_state) {
 | 
			
		||||
 	case SFP_S_DOWN:
 | 
			
		||||
-		if (sfp->sm_mod_state == SFP_MOD_PRESENT &&
 | 
			
		||||
-		    sfp->sm_dev_state == SFP_DEV_UP) {
 | 
			
		||||
-			sfp_sm_mod_init(sfp);
 | 
			
		||||
-			sfp_sm_probe_for_phy(sfp);
 | 
			
		||||
+		if (sfp->sm_mod_state != SFP_MOD_PRESENT ||
 | 
			
		||||
+		    sfp->sm_dev_state != SFP_DEV_UP)
 | 
			
		||||
+			break;
 | 
			
		||||
+
 | 
			
		||||
+		sfp_sm_mod_init(sfp);
 | 
			
		||||
+
 | 
			
		||||
+		/* Initialise the fault clearance retries */
 | 
			
		||||
+		sfp->sm_retries = 5;
 | 
			
		||||
+
 | 
			
		||||
+		/* We need to check the TX_FAULT state, which is not defined
 | 
			
		||||
+		 * while TX_DISABLE is asserted. The earliest we want to do
 | 
			
		||||
+		 * anything (such as probe for a PHY) is 50ms.
 | 
			
		||||
+		 */
 | 
			
		||||
+		sfp_sm_next(sfp, SFP_S_WAIT, T_WAIT);
 | 
			
		||||
+		break;
 | 
			
		||||
+
 | 
			
		||||
+	case SFP_S_WAIT:
 | 
			
		||||
+		if (event != SFP_E_TIMEOUT)
 | 
			
		||||
+			break;
 | 
			
		||||
+
 | 
			
		||||
+		sfp_sm_probe_for_phy(sfp);
 | 
			
		||||
 
 | 
			
		||||
+		if (sfp->state & SFP_F_TX_FAULT) {
 | 
			
		||||
 			/* Wait t_init before indicating that the link is up,
 | 
			
		||||
 			 * provided the current state indicates no TX_FAULT. If
 | 
			
		||||
 			 * TX_FAULT clears before this time, that's fine too.
 | 
			
		||||
 			 */
 | 
			
		||||
-			sfp_sm_next(sfp, SFP_S_INIT, T_INIT_JIFFIES);
 | 
			
		||||
-			sfp->sm_retries = 5;
 | 
			
		||||
+			timeout = T_INIT_JIFFIES;
 | 
			
		||||
+			if (timeout > T_WAIT)
 | 
			
		||||
+				timeout -= T_WAIT;
 | 
			
		||||
+			else
 | 
			
		||||
+				timeout = 1;
 | 
			
		||||
+
 | 
			
		||||
+			sfp_sm_next(sfp, SFP_S_INIT, timeout);
 | 
			
		||||
+		} else {
 | 
			
		||||
+			/* TX_FAULT is not asserted, assume the module has
 | 
			
		||||
+			 * finished initialising.
 | 
			
		||||
+			 */
 | 
			
		||||
+			goto init_done;
 | 
			
		||||
 		}
 | 
			
		||||
 		break;
 | 
			
		||||
 
 | 
			
		||||
@@ -1593,7 +1621,7 @@ static void sfp_sm_main(struct sfp *sfp,
 | 
			
		||||
 		if (event == SFP_E_TIMEOUT && sfp->state & SFP_F_TX_FAULT)
 | 
			
		||||
 			sfp_sm_fault(sfp, true);
 | 
			
		||||
 		else if (event == SFP_E_TIMEOUT || event == SFP_E_TX_CLEAR)
 | 
			
		||||
-			sfp_sm_link_check_los(sfp);
 | 
			
		||||
+	init_done:	sfp_sm_link_check_los(sfp);
 | 
			
		||||
 		break;
 | 
			
		||||
 
 | 
			
		||||
 	case SFP_S_WAIT_LOS:
 | 
			
		||||
@ -0,0 +1,69 @@
 | 
			
		||||
From 2aa424ee7fbe43e2cd24e28c2f6388c4e1796bd2 Mon Sep 17 00:00:00 2001
 | 
			
		||||
From: Russell King <rmk+kernel@armlinux.org.uk>
 | 
			
		||||
Date: Fri, 18 Oct 2019 09:58:33 +0100
 | 
			
		||||
Subject: [PATCH 628/660] net: sfp: allow fault processing to transition to
 | 
			
		||||
 other states
 | 
			
		||||
 | 
			
		||||
Add the next state to sfp_sm_fault() so that it can branch to other
 | 
			
		||||
states. This will be necessary to improve the initialisation path.
 | 
			
		||||
 | 
			
		||||
Signed-off-by: Russell King <rmk+kernel@armlinux.org.uk>
 | 
			
		||||
---
 | 
			
		||||
 drivers/net/phy/sfp.c | 12 ++++++------
 | 
			
		||||
 1 file changed, 6 insertions(+), 6 deletions(-)
 | 
			
		||||
 | 
			
		||||
--- a/drivers/net/phy/sfp.c
 | 
			
		||||
+++ b/drivers/net/phy/sfp.c
 | 
			
		||||
@@ -1269,7 +1269,7 @@ static bool sfp_los_event_inactive(struc
 | 
			
		||||
 		event == SFP_E_LOS_LOW);
 | 
			
		||||
 }
 | 
			
		||||
 
 | 
			
		||||
-static void sfp_sm_fault(struct sfp *sfp, bool warn)
 | 
			
		||||
+static void sfp_sm_fault(struct sfp *sfp, unsigned int next_state, bool warn)
 | 
			
		||||
 {
 | 
			
		||||
 	if (sfp->sm_retries && !--sfp->sm_retries) {
 | 
			
		||||
 		dev_err(sfp->dev,
 | 
			
		||||
@@ -1279,7 +1279,7 @@ static void sfp_sm_fault(struct sfp *sfp
 | 
			
		||||
 		if (warn)
 | 
			
		||||
 			dev_err(sfp->dev, "module transmit fault indicated\n");
 | 
			
		||||
 
 | 
			
		||||
-		sfp_sm_next(sfp, SFP_S_TX_FAULT, T_FAULT_RECOVER);
 | 
			
		||||
+		sfp_sm_next(sfp, next_state, T_FAULT_RECOVER);
 | 
			
		||||
 	}
 | 
			
		||||
 }
 | 
			
		||||
 
 | 
			
		||||
@@ -1619,14 +1619,14 @@ static void sfp_sm_main(struct sfp *sfp,
 | 
			
		||||
 
 | 
			
		||||
 	case SFP_S_INIT:
 | 
			
		||||
 		if (event == SFP_E_TIMEOUT && sfp->state & SFP_F_TX_FAULT)
 | 
			
		||||
-			sfp_sm_fault(sfp, true);
 | 
			
		||||
+			sfp_sm_fault(sfp, SFP_S_TX_FAULT, true);
 | 
			
		||||
 		else if (event == SFP_E_TIMEOUT || event == SFP_E_TX_CLEAR)
 | 
			
		||||
 	init_done:	sfp_sm_link_check_los(sfp);
 | 
			
		||||
 		break;
 | 
			
		||||
 
 | 
			
		||||
 	case SFP_S_WAIT_LOS:
 | 
			
		||||
 		if (event == SFP_E_TX_FAULT)
 | 
			
		||||
-			sfp_sm_fault(sfp, true);
 | 
			
		||||
+			sfp_sm_fault(sfp, SFP_S_TX_FAULT, true);
 | 
			
		||||
 		else if (sfp_los_event_inactive(sfp, event))
 | 
			
		||||
 			sfp_sm_link_up(sfp);
 | 
			
		||||
 		break;
 | 
			
		||||
@@ -1634,7 +1634,7 @@ static void sfp_sm_main(struct sfp *sfp,
 | 
			
		||||
 	case SFP_S_LINK_UP:
 | 
			
		||||
 		if (event == SFP_E_TX_FAULT) {
 | 
			
		||||
 			sfp_sm_link_down(sfp);
 | 
			
		||||
-			sfp_sm_fault(sfp, true);
 | 
			
		||||
+			sfp_sm_fault(sfp, SFP_S_TX_FAULT, true);
 | 
			
		||||
 		} else if (sfp_los_event_active(sfp, event)) {
 | 
			
		||||
 			sfp_sm_link_down(sfp);
 | 
			
		||||
 			sfp_sm_next(sfp, SFP_S_WAIT_LOS, 0);
 | 
			
		||||
@@ -1650,7 +1650,7 @@ static void sfp_sm_main(struct sfp *sfp,
 | 
			
		||||
 
 | 
			
		||||
 	case SFP_S_REINIT:
 | 
			
		||||
 		if (event == SFP_E_TIMEOUT && sfp->state & SFP_F_TX_FAULT) {
 | 
			
		||||
-			sfp_sm_fault(sfp, false);
 | 
			
		||||
+			sfp_sm_fault(sfp, SFP_S_TX_FAULT, false);
 | 
			
		||||
 		} else if (event == SFP_E_TIMEOUT || event == SFP_E_TX_CLEAR) {
 | 
			
		||||
 			dev_info(sfp->dev, "module transmit fault recovered\n");
 | 
			
		||||
 			sfp_sm_link_check_los(sfp);
 | 
			
		||||
@ -0,0 +1,80 @@
 | 
			
		||||
From 38b62a12231be4b86fc5ca5477579d29831c02a5 Mon Sep 17 00:00:00 2001
 | 
			
		||||
From: Russell King <rmk+kernel@armlinux.org.uk>
 | 
			
		||||
Date: Fri, 18 Oct 2019 10:31:07 +0100
 | 
			
		||||
Subject: [PATCH 629/660] net: sfp: ensure TX_FAULT has deasserted before
 | 
			
		||||
 probing the PHY
 | 
			
		||||
 | 
			
		||||
TX_FAULT should be deasserted to indicate that the module has completed
 | 
			
		||||
its initialisation.  This may include the on-board PHY, so wait until
 | 
			
		||||
the module has deasserted TX_FAULT before probing the PHY.
 | 
			
		||||
 | 
			
		||||
This means that we need an extra state to handle a TX_FAULT that
 | 
			
		||||
remains set for longer than t_init, since using the existing handling
 | 
			
		||||
state would bypass the PHY probe.
 | 
			
		||||
 | 
			
		||||
Signed-off-by: Russell King <rmk+kernel@armlinux.org.uk>
 | 
			
		||||
---
 | 
			
		||||
 drivers/net/phy/sfp.c | 31 +++++++++++++++++++++++++------
 | 
			
		||||
 1 file changed, 25 insertions(+), 6 deletions(-)
 | 
			
		||||
 | 
			
		||||
--- a/drivers/net/phy/sfp.c
 | 
			
		||||
+++ b/drivers/net/phy/sfp.c
 | 
			
		||||
@@ -54,6 +54,7 @@ enum {
 | 
			
		||||
 	SFP_S_DOWN = 0,
 | 
			
		||||
 	SFP_S_WAIT,
 | 
			
		||||
 	SFP_S_INIT,
 | 
			
		||||
+	SFP_S_INIT_TX_FAULT,
 | 
			
		||||
 	SFP_S_WAIT_LOS,
 | 
			
		||||
 	SFP_S_LINK_UP,
 | 
			
		||||
 	SFP_S_TX_FAULT,
 | 
			
		||||
@@ -111,6 +112,7 @@ static const char * const sm_state_strin
 | 
			
		||||
 	[SFP_S_DOWN] = "down",
 | 
			
		||||
 	[SFP_S_WAIT] = "wait",
 | 
			
		||||
 	[SFP_S_INIT] = "init",
 | 
			
		||||
+	[SFP_S_INIT_TX_FAULT] = "init_tx_fault",
 | 
			
		||||
 	[SFP_S_WAIT_LOS] = "wait_los",
 | 
			
		||||
 	[SFP_S_LINK_UP] = "link_up",
 | 
			
		||||
 	[SFP_S_TX_FAULT] = "tx_fault",
 | 
			
		||||
@@ -1595,8 +1597,6 @@ static void sfp_sm_main(struct sfp *sfp,
 | 
			
		||||
 		if (event != SFP_E_TIMEOUT)
 | 
			
		||||
 			break;
 | 
			
		||||
 
 | 
			
		||||
-		sfp_sm_probe_for_phy(sfp);
 | 
			
		||||
-
 | 
			
		||||
 		if (sfp->state & SFP_F_TX_FAULT) {
 | 
			
		||||
 			/* Wait t_init before indicating that the link is up,
 | 
			
		||||
 			 * provided the current state indicates no TX_FAULT. If
 | 
			
		||||
@@ -1618,10 +1618,29 @@ static void sfp_sm_main(struct sfp *sfp,
 | 
			
		||||
 		break;
 | 
			
		||||
 
 | 
			
		||||
 	case SFP_S_INIT:
 | 
			
		||||
-		if (event == SFP_E_TIMEOUT && sfp->state & SFP_F_TX_FAULT)
 | 
			
		||||
-			sfp_sm_fault(sfp, SFP_S_TX_FAULT, true);
 | 
			
		||||
-		else if (event == SFP_E_TIMEOUT || event == SFP_E_TX_CLEAR)
 | 
			
		||||
-	init_done:	sfp_sm_link_check_los(sfp);
 | 
			
		||||
+		if (event == SFP_E_TIMEOUT && sfp->state & SFP_F_TX_FAULT) {
 | 
			
		||||
+			/* TX_FAULT is still asserted after t_init, so assume
 | 
			
		||||
+			 * there is a fault.
 | 
			
		||||
+			 */
 | 
			
		||||
+			sfp_sm_fault(sfp, SFP_S_INIT_TX_FAULT,
 | 
			
		||||
+				     sfp->sm_retries == 5);
 | 
			
		||||
+		} else if (event == SFP_E_TIMEOUT || event == SFP_E_TX_CLEAR) {
 | 
			
		||||
+	init_done:	/* TX_FAULT deasserted or we timed out with TX_FAULT
 | 
			
		||||
+			 * clear.  Probe for the PHY and check the LOS state.
 | 
			
		||||
+			 */
 | 
			
		||||
+			sfp_sm_probe_for_phy(sfp);
 | 
			
		||||
+			sfp_sm_link_check_los(sfp);
 | 
			
		||||
+
 | 
			
		||||
+			/* Reset the fault retry count */
 | 
			
		||||
+			sfp->sm_retries = 5;
 | 
			
		||||
+		}
 | 
			
		||||
+		break;
 | 
			
		||||
+
 | 
			
		||||
+	case SFP_S_INIT_TX_FAULT:
 | 
			
		||||
+		if (event == SFP_E_TIMEOUT) {
 | 
			
		||||
+			sfp_module_tx_fault_reset(sfp);
 | 
			
		||||
+			sfp_sm_next(sfp, SFP_S_INIT, T_INIT_JIFFIES);
 | 
			
		||||
+		}
 | 
			
		||||
 		break;
 | 
			
		||||
 
 | 
			
		||||
 	case SFP_S_WAIT_LOS:
 | 
			
		||||
@ -0,0 +1,153 @@
 | 
			
		||||
From ec6036a58f979c66bbd5cd9d0d1c783a98c2c644 Mon Sep 17 00:00:00 2001
 | 
			
		||||
From: Russell King <rmk+kernel@armlinux.org.uk>
 | 
			
		||||
Date: Tue, 5 Nov 2019 12:57:40 +0000
 | 
			
		||||
Subject: [PATCH 630/660] net: sfp: track upstream's attachment state in state
 | 
			
		||||
 machine
 | 
			
		||||
 | 
			
		||||
Track the upstream's attachment state in the state machine rather than
 | 
			
		||||
maintaining a boolean, which ensures that we have a strict order of
 | 
			
		||||
ATTACH followed by an UP event - we can never believe that a newly
 | 
			
		||||
attached upstream will be anything but down.
 | 
			
		||||
 | 
			
		||||
Rearrange the order of state machines so we run the module state
 | 
			
		||||
machine after the upstream device's state machine, so the module state
 | 
			
		||||
machine can check the current state of the device and take action to
 | 
			
		||||
e.g. reset back to empty state when the upstream is detached.
 | 
			
		||||
 | 
			
		||||
This is to allow the module detection to run independently of the
 | 
			
		||||
network device becoming available.
 | 
			
		||||
 | 
			
		||||
Signed-off-by: Russell King <rmk+kernel@armlinux.org.uk>
 | 
			
		||||
---
 | 
			
		||||
 drivers/net/phy/sfp.c | 42 +++++++++++++++++++++++++++++-------------
 | 
			
		||||
 1 file changed, 29 insertions(+), 13 deletions(-)
 | 
			
		||||
 | 
			
		||||
--- a/drivers/net/phy/sfp.c
 | 
			
		||||
+++ b/drivers/net/phy/sfp.c
 | 
			
		||||
@@ -34,6 +34,8 @@ enum {
 | 
			
		||||
 
 | 
			
		||||
 	SFP_E_INSERT = 0,
 | 
			
		||||
 	SFP_E_REMOVE,
 | 
			
		||||
+	SFP_E_DEV_ATTACH,
 | 
			
		||||
+	SFP_E_DEV_DETACH,
 | 
			
		||||
 	SFP_E_DEV_DOWN,
 | 
			
		||||
 	SFP_E_DEV_UP,
 | 
			
		||||
 	SFP_E_TX_FAULT,
 | 
			
		||||
@@ -48,7 +50,8 @@ enum {
 | 
			
		||||
 	SFP_MOD_PRESENT,
 | 
			
		||||
 	SFP_MOD_ERROR,
 | 
			
		||||
 
 | 
			
		||||
-	SFP_DEV_DOWN = 0,
 | 
			
		||||
+	SFP_DEV_DETACHED = 0,
 | 
			
		||||
+	SFP_DEV_DOWN,
 | 
			
		||||
 	SFP_DEV_UP,
 | 
			
		||||
 
 | 
			
		||||
 	SFP_S_DOWN = 0,
 | 
			
		||||
@@ -78,6 +81,7 @@ static const char *mod_state_to_str(unsi
 | 
			
		||||
 }
 | 
			
		||||
 
 | 
			
		||||
 static const char * const dev_state_strings[] = {
 | 
			
		||||
+	[SFP_DEV_DETACHED] = "detached",
 | 
			
		||||
 	[SFP_DEV_DOWN] = "down",
 | 
			
		||||
 	[SFP_DEV_UP] = "up",
 | 
			
		||||
 };
 | 
			
		||||
@@ -92,6 +96,8 @@ static const char *dev_state_to_str(unsi
 | 
			
		||||
 static const char * const event_strings[] = {
 | 
			
		||||
 	[SFP_E_INSERT] = "insert",
 | 
			
		||||
 	[SFP_E_REMOVE] = "remove",
 | 
			
		||||
+	[SFP_E_DEV_ATTACH] = "dev_attach",
 | 
			
		||||
+	[SFP_E_DEV_DETACH] = "dev_detach",
 | 
			
		||||
 	[SFP_E_DEV_DOWN] = "dev_down",
 | 
			
		||||
 	[SFP_E_DEV_UP] = "dev_up",
 | 
			
		||||
 	[SFP_E_TX_FAULT] = "tx_fault",
 | 
			
		||||
@@ -186,7 +192,6 @@ struct sfp {
 | 
			
		||||
 	struct gpio_desc *gpio[GPIO_MAX];
 | 
			
		||||
 	int gpio_irq[GPIO_MAX];
 | 
			
		||||
 
 | 
			
		||||
-	bool attached;
 | 
			
		||||
 	struct mutex st_mutex;			/* Protects state */
 | 
			
		||||
 	unsigned int state;
 | 
			
		||||
 	struct delayed_work poll;
 | 
			
		||||
@@ -1494,17 +1499,26 @@ static void sfp_sm_mod_remove(struct sfp
 | 
			
		||||
 	dev_info(sfp->dev, "module removed\n");
 | 
			
		||||
 }
 | 
			
		||||
 
 | 
			
		||||
-/* This state machine tracks the netdev up/down state */
 | 
			
		||||
+/* This state machine tracks the upstream's state */
 | 
			
		||||
 static void sfp_sm_device(struct sfp *sfp, unsigned int event)
 | 
			
		||||
 {
 | 
			
		||||
 	switch (sfp->sm_dev_state) {
 | 
			
		||||
 	default:
 | 
			
		||||
-		if (event == SFP_E_DEV_UP)
 | 
			
		||||
+		if (event == SFP_E_DEV_ATTACH)
 | 
			
		||||
+			sfp->sm_dev_state = SFP_DEV_DOWN;
 | 
			
		||||
+		break;
 | 
			
		||||
+
 | 
			
		||||
+	case SFP_DEV_DOWN:
 | 
			
		||||
+		if (event == SFP_E_DEV_DETACH)
 | 
			
		||||
+			sfp->sm_dev_state = SFP_DEV_DETACHED;
 | 
			
		||||
+		else if (event == SFP_E_DEV_UP)
 | 
			
		||||
 			sfp->sm_dev_state = SFP_DEV_UP;
 | 
			
		||||
 		break;
 | 
			
		||||
 
 | 
			
		||||
 	case SFP_DEV_UP:
 | 
			
		||||
-		if (event == SFP_E_DEV_DOWN)
 | 
			
		||||
+		if (event == SFP_E_DEV_DETACH)
 | 
			
		||||
+			sfp->sm_dev_state = SFP_DEV_DETACHED;
 | 
			
		||||
+		else if (event == SFP_E_DEV_DOWN)
 | 
			
		||||
 			sfp->sm_dev_state = SFP_DEV_DOWN;
 | 
			
		||||
 		break;
 | 
			
		||||
 	}
 | 
			
		||||
@@ -1515,17 +1529,20 @@ static void sfp_sm_device(struct sfp *sf
 | 
			
		||||
  */
 | 
			
		||||
 static void sfp_sm_module(struct sfp *sfp, unsigned int event)
 | 
			
		||||
 {
 | 
			
		||||
-	/* Handle remove event globally, it resets this state machine */
 | 
			
		||||
-	if (event == SFP_E_REMOVE) {
 | 
			
		||||
+	/* Handle remove event globally, it resets this state machine.
 | 
			
		||||
+	 * Also deal with upstream detachment.
 | 
			
		||||
+	 */
 | 
			
		||||
+	if (event == SFP_E_REMOVE || sfp->sm_dev_state < SFP_DEV_DOWN) {
 | 
			
		||||
 		if (sfp->sm_mod_state > SFP_MOD_PROBE)
 | 
			
		||||
 			sfp_sm_mod_remove(sfp);
 | 
			
		||||
-		sfp_sm_mod_next(sfp, SFP_MOD_EMPTY, 0);
 | 
			
		||||
+		if (sfp->sm_mod_state != SFP_MOD_EMPTY)
 | 
			
		||||
+			sfp_sm_mod_next(sfp, SFP_MOD_EMPTY, 0);
 | 
			
		||||
 		return;
 | 
			
		||||
 	}
 | 
			
		||||
 
 | 
			
		||||
 	switch (sfp->sm_mod_state) {
 | 
			
		||||
 	default:
 | 
			
		||||
-		if (event == SFP_E_INSERT && sfp->attached)
 | 
			
		||||
+		if (event == SFP_E_INSERT)
 | 
			
		||||
 			sfp_sm_mod_next(sfp, SFP_MOD_PROBE, T_SERIAL);
 | 
			
		||||
 		break;
 | 
			
		||||
 
 | 
			
		||||
@@ -1691,8 +1708,8 @@ static void sfp_sm_event(struct sfp *sfp
 | 
			
		||||
 		sm_state_to_str(sfp->sm_state),
 | 
			
		||||
 		event_to_str(event));
 | 
			
		||||
 
 | 
			
		||||
-	sfp_sm_module(sfp, event);
 | 
			
		||||
 	sfp_sm_device(sfp, event);
 | 
			
		||||
+	sfp_sm_module(sfp, event);
 | 
			
		||||
 	sfp_sm_main(sfp, event);
 | 
			
		||||
 
 | 
			
		||||
 	dev_dbg(sfp->dev, "SM: exit %s:%s:%s\n",
 | 
			
		||||
@@ -1705,15 +1722,14 @@ static void sfp_sm_event(struct sfp *sfp
 | 
			
		||||
 
 | 
			
		||||
 static void sfp_attach(struct sfp *sfp)
 | 
			
		||||
 {
 | 
			
		||||
-	sfp->attached = true;
 | 
			
		||||
+	sfp_sm_event(sfp, SFP_E_DEV_ATTACH);
 | 
			
		||||
 	if (sfp->state & SFP_F_PRESENT)
 | 
			
		||||
 		sfp_sm_event(sfp, SFP_E_INSERT);
 | 
			
		||||
 }
 | 
			
		||||
 
 | 
			
		||||
 static void sfp_detach(struct sfp *sfp)
 | 
			
		||||
 {
 | 
			
		||||
-	sfp->attached = false;
 | 
			
		||||
-	sfp_sm_event(sfp, SFP_E_REMOVE);
 | 
			
		||||
+	sfp_sm_event(sfp, SFP_E_DEV_DETACH);
 | 
			
		||||
 }
 | 
			
		||||
 
 | 
			
		||||
 static void sfp_start(struct sfp *sfp)
 | 
			
		||||
@ -0,0 +1,184 @@
 | 
			
		||||
From fdff863a4ce3677907f64396e34c45025abb6600 Mon Sep 17 00:00:00 2001
 | 
			
		||||
From: Russell King <rmk+kernel@armlinux.org.uk>
 | 
			
		||||
Date: Tue, 5 Nov 2019 12:59:36 +0000
 | 
			
		||||
Subject: [PATCH 631/660] net: sfp: split power mode switching from probe
 | 
			
		||||
 | 
			
		||||
Switch the power mode switching from the probe, so that we don't
 | 
			
		||||
repeatedly re-probe the SFP device if there is a problem accessing
 | 
			
		||||
the registers at I2C address 0x51.
 | 
			
		||||
 | 
			
		||||
In splitting this out, we can also fix a bug where we leave the module
 | 
			
		||||
in high-power mode when the upstream device is detached but the module
 | 
			
		||||
is still inserted.
 | 
			
		||||
 | 
			
		||||
Signed-off-by: Russell King <rmk+kernel@armlinux.org.uk>
 | 
			
		||||
---
 | 
			
		||||
 drivers/net/phy/sfp.c | 101 ++++++++++++++++++++++++++----------------
 | 
			
		||||
 1 file changed, 64 insertions(+), 37 deletions(-)
 | 
			
		||||
 | 
			
		||||
--- a/drivers/net/phy/sfp.c
 | 
			
		||||
+++ b/drivers/net/phy/sfp.c
 | 
			
		||||
@@ -47,6 +47,7 @@ enum {
 | 
			
		||||
 	SFP_MOD_EMPTY = 0,
 | 
			
		||||
 	SFP_MOD_PROBE,
 | 
			
		||||
 	SFP_MOD_HPOWER,
 | 
			
		||||
+	SFP_MOD_WAITPWR,
 | 
			
		||||
 	SFP_MOD_PRESENT,
 | 
			
		||||
 	SFP_MOD_ERROR,
 | 
			
		||||
 
 | 
			
		||||
@@ -69,6 +70,7 @@ static const char  * const mod_state_str
 | 
			
		||||
 	[SFP_MOD_EMPTY] = "empty",
 | 
			
		||||
 	[SFP_MOD_PROBE] = "probe",
 | 
			
		||||
 	[SFP_MOD_HPOWER] = "hpower",
 | 
			
		||||
+	[SFP_MOD_WAITPWR] = "waitpwr",
 | 
			
		||||
 	[SFP_MOD_PRESENT] = "present",
 | 
			
		||||
 	[SFP_MOD_ERROR] = "error",
 | 
			
		||||
 };
 | 
			
		||||
@@ -1358,37 +1360,34 @@ static int sfp_module_parse_power(struct
 | 
			
		||||
 	return 0;
 | 
			
		||||
 }
 | 
			
		||||
 
 | 
			
		||||
-static int sfp_sm_mod_hpower(struct sfp *sfp)
 | 
			
		||||
+static int sfp_sm_mod_hpower(struct sfp *sfp, bool enable)
 | 
			
		||||
 {
 | 
			
		||||
 	u8 val;
 | 
			
		||||
 	int err;
 | 
			
		||||
 
 | 
			
		||||
-	if (sfp->module_power_mW <= 1000)
 | 
			
		||||
-		return 0;
 | 
			
		||||
-
 | 
			
		||||
 	err = sfp_read(sfp, true, SFP_EXT_STATUS, &val, sizeof(val));
 | 
			
		||||
 	if (err != sizeof(val)) {
 | 
			
		||||
 		dev_err(sfp->dev, "Failed to read EEPROM: %d\n", err);
 | 
			
		||||
-		err = -EAGAIN;
 | 
			
		||||
-		goto err;
 | 
			
		||||
+		return -EAGAIN;
 | 
			
		||||
 	}
 | 
			
		||||
 
 | 
			
		||||
-	val |= BIT(0);
 | 
			
		||||
+	if (enable)
 | 
			
		||||
+		val |= BIT(0);
 | 
			
		||||
+	else
 | 
			
		||||
+		val &= ~BIT(0);
 | 
			
		||||
 
 | 
			
		||||
 	err = sfp_write(sfp, true, SFP_EXT_STATUS, &val, sizeof(val));
 | 
			
		||||
 	if (err != sizeof(val)) {
 | 
			
		||||
 		dev_err(sfp->dev, "Failed to write EEPROM: %d\n", err);
 | 
			
		||||
-		err = -EAGAIN;
 | 
			
		||||
-		goto err;
 | 
			
		||||
+		return -EAGAIN;
 | 
			
		||||
 	}
 | 
			
		||||
 
 | 
			
		||||
-	dev_info(sfp->dev, "Module switched to %u.%uW power level\n",
 | 
			
		||||
-		 sfp->module_power_mW / 1000,
 | 
			
		||||
-		 (sfp->module_power_mW / 100) % 10);
 | 
			
		||||
-	return T_HPOWER_LEVEL;
 | 
			
		||||
+	if (enable)
 | 
			
		||||
+		dev_info(sfp->dev, "Module switched to %u.%uW power level\n",
 | 
			
		||||
+			 sfp->module_power_mW / 1000,
 | 
			
		||||
+			 (sfp->module_power_mW / 100) % 10);
 | 
			
		||||
 
 | 
			
		||||
-err:
 | 
			
		||||
-	return err;
 | 
			
		||||
+	return 0;
 | 
			
		||||
 }
 | 
			
		||||
 
 | 
			
		||||
 static int sfp_sm_mod_probe(struct sfp *sfp)
 | 
			
		||||
@@ -1484,7 +1483,7 @@ static int sfp_sm_mod_probe(struct sfp *
 | 
			
		||||
 	if (ret < 0)
 | 
			
		||||
 		return ret;
 | 
			
		||||
 
 | 
			
		||||
-	return sfp_sm_mod_hpower(sfp);
 | 
			
		||||
+	return 0;
 | 
			
		||||
 }
 | 
			
		||||
 
 | 
			
		||||
 static void sfp_sm_mod_remove(struct sfp *sfp)
 | 
			
		||||
@@ -1529,13 +1528,22 @@ static void sfp_sm_device(struct sfp *sf
 | 
			
		||||
  */
 | 
			
		||||
 static void sfp_sm_module(struct sfp *sfp, unsigned int event)
 | 
			
		||||
 {
 | 
			
		||||
-	/* Handle remove event globally, it resets this state machine.
 | 
			
		||||
-	 * Also deal with upstream detachment.
 | 
			
		||||
-	 */
 | 
			
		||||
-	if (event == SFP_E_REMOVE || sfp->sm_dev_state < SFP_DEV_DOWN) {
 | 
			
		||||
+	int err;
 | 
			
		||||
+
 | 
			
		||||
+	/* Handle remove event globally, it resets this state machine */
 | 
			
		||||
+	if (event == SFP_E_REMOVE) {
 | 
			
		||||
 		if (sfp->sm_mod_state > SFP_MOD_PROBE)
 | 
			
		||||
 			sfp_sm_mod_remove(sfp);
 | 
			
		||||
-		if (sfp->sm_mod_state != SFP_MOD_EMPTY)
 | 
			
		||||
+		sfp_sm_mod_next(sfp, SFP_MOD_EMPTY, 0);
 | 
			
		||||
+		return;
 | 
			
		||||
+	}
 | 
			
		||||
+
 | 
			
		||||
+	/* Handle device detach globally */
 | 
			
		||||
+	if (sfp->sm_dev_state < SFP_DEV_DOWN) {
 | 
			
		||||
+		if (sfp->module_power_mW > 1000 &&
 | 
			
		||||
+		    sfp->sm_mod_state > SFP_MOD_HPOWER)
 | 
			
		||||
+			sfp_sm_mod_hpower(sfp, false);
 | 
			
		||||
+		if (sfp->sm_mod_state > SFP_MOD_EMPTY)
 | 
			
		||||
 			sfp_sm_mod_next(sfp, SFP_MOD_EMPTY, 0);
 | 
			
		||||
 		return;
 | 
			
		||||
 	}
 | 
			
		||||
@@ -1547,26 +1555,45 @@ static void sfp_sm_module(struct sfp *sf
 | 
			
		||||
 		break;
 | 
			
		||||
 
 | 
			
		||||
 	case SFP_MOD_PROBE:
 | 
			
		||||
-		if (event == SFP_E_TIMEOUT) {
 | 
			
		||||
-			int val = sfp_sm_mod_probe(sfp);
 | 
			
		||||
+		if (event != SFP_E_TIMEOUT)
 | 
			
		||||
+			break;
 | 
			
		||||
 
 | 
			
		||||
-			if (val == 0)
 | 
			
		||||
-				sfp_sm_mod_next(sfp, SFP_MOD_PRESENT, 0);
 | 
			
		||||
-			else if (val > 0)
 | 
			
		||||
-				sfp_sm_mod_next(sfp, SFP_MOD_HPOWER, val);
 | 
			
		||||
-			else if (val != -EAGAIN)
 | 
			
		||||
-				sfp_sm_mod_next(sfp, SFP_MOD_ERROR, 0);
 | 
			
		||||
-			else
 | 
			
		||||
-				sfp_sm_set_timer(sfp, T_PROBE_RETRY);
 | 
			
		||||
+		err = sfp_sm_mod_probe(sfp);
 | 
			
		||||
+		if (err == -EAGAIN) {
 | 
			
		||||
+			sfp_sm_set_timer(sfp, T_PROBE_RETRY);
 | 
			
		||||
+			break;
 | 
			
		||||
 		}
 | 
			
		||||
-		break;
 | 
			
		||||
+		if (err < 0) {
 | 
			
		||||
+			sfp_sm_mod_next(sfp, SFP_MOD_ERROR, 0);
 | 
			
		||||
+			break;
 | 
			
		||||
+		}
 | 
			
		||||
+
 | 
			
		||||
+		/* If this is a power level 1 module, we are done */
 | 
			
		||||
+		if (sfp->module_power_mW <= 1000)
 | 
			
		||||
+			goto insert;
 | 
			
		||||
 
 | 
			
		||||
+		sfp_sm_mod_next(sfp, SFP_MOD_HPOWER, 0);
 | 
			
		||||
+		/* fall through */
 | 
			
		||||
 	case SFP_MOD_HPOWER:
 | 
			
		||||
-		if (event == SFP_E_TIMEOUT) {
 | 
			
		||||
-			sfp_sm_mod_next(sfp, SFP_MOD_PRESENT, 0);
 | 
			
		||||
+		/* Enable high power mode */
 | 
			
		||||
+		err = sfp_sm_mod_hpower(sfp, true);
 | 
			
		||||
+		if (err == 0)
 | 
			
		||||
+			sfp_sm_mod_next(sfp, SFP_MOD_WAITPWR, T_HPOWER_LEVEL);
 | 
			
		||||
+		else if (err != -EAGAIN)
 | 
			
		||||
+			sfp_sm_mod_next(sfp, SFP_MOD_ERROR, 0);
 | 
			
		||||
+		else
 | 
			
		||||
+			sfp_sm_set_timer(sfp, T_PROBE_RETRY);
 | 
			
		||||
+		break;
 | 
			
		||||
+
 | 
			
		||||
+	case SFP_MOD_WAITPWR:
 | 
			
		||||
+		/* Wait for T_HPOWER_LEVEL to time out */
 | 
			
		||||
+		if (event != SFP_E_TIMEOUT)
 | 
			
		||||
 			break;
 | 
			
		||||
-		}
 | 
			
		||||
-		/* fallthrough */
 | 
			
		||||
+
 | 
			
		||||
+	insert:
 | 
			
		||||
+		sfp_sm_mod_next(sfp, SFP_MOD_PRESENT, 0);
 | 
			
		||||
+		break;
 | 
			
		||||
+
 | 
			
		||||
 	case SFP_MOD_PRESENT:
 | 
			
		||||
 	case SFP_MOD_ERROR:
 | 
			
		||||
 		break;
 | 
			
		||||
@ -0,0 +1,159 @@
 | 
			
		||||
From 57cbf7453551db1df619b79410d79fc418d862d5 Mon Sep 17 00:00:00 2001
 | 
			
		||||
From: Russell King <rmk+kernel@armlinux.org.uk>
 | 
			
		||||
Date: Tue, 5 Nov 2019 13:00:45 +0000
 | 
			
		||||
Subject: [PATCH 632/660] net: sfp: move module insert reporting out of probe
 | 
			
		||||
 | 
			
		||||
Move the module insertion reporting out of the probe handling, but
 | 
			
		||||
after we have detected that the upstream has attached (since that is
 | 
			
		||||
whom we are reporting insertion to.)
 | 
			
		||||
 | 
			
		||||
Only report module removal if we had previously reported a module
 | 
			
		||||
insertion.
 | 
			
		||||
 | 
			
		||||
This gives cleaner semantics, and means we can probe the module before
 | 
			
		||||
we have an upstream attached.
 | 
			
		||||
 | 
			
		||||
Signed-off-by: Russell King <rmk+kernel@armlinux.org.uk>
 | 
			
		||||
---
 | 
			
		||||
 drivers/net/phy/sfp.c | 58 +++++++++++++++++++++++++++++--------------
 | 
			
		||||
 1 file changed, 40 insertions(+), 18 deletions(-)
 | 
			
		||||
 | 
			
		||||
--- a/drivers/net/phy/sfp.c
 | 
			
		||||
+++ b/drivers/net/phy/sfp.c
 | 
			
		||||
@@ -45,11 +45,12 @@ enum {
 | 
			
		||||
 	SFP_E_TIMEOUT,
 | 
			
		||||
 
 | 
			
		||||
 	SFP_MOD_EMPTY = 0,
 | 
			
		||||
+	SFP_MOD_ERROR,
 | 
			
		||||
 	SFP_MOD_PROBE,
 | 
			
		||||
+	SFP_MOD_WAITDEV,
 | 
			
		||||
 	SFP_MOD_HPOWER,
 | 
			
		||||
 	SFP_MOD_WAITPWR,
 | 
			
		||||
 	SFP_MOD_PRESENT,
 | 
			
		||||
-	SFP_MOD_ERROR,
 | 
			
		||||
 
 | 
			
		||||
 	SFP_DEV_DETACHED = 0,
 | 
			
		||||
 	SFP_DEV_DOWN,
 | 
			
		||||
@@ -68,11 +69,12 @@ enum {
 | 
			
		||||
 
 | 
			
		||||
 static const char  * const mod_state_strings[] = {
 | 
			
		||||
 	[SFP_MOD_EMPTY] = "empty",
 | 
			
		||||
+	[SFP_MOD_ERROR] = "error",
 | 
			
		||||
 	[SFP_MOD_PROBE] = "probe",
 | 
			
		||||
+	[SFP_MOD_WAITDEV] = "waitdev",
 | 
			
		||||
 	[SFP_MOD_HPOWER] = "hpower",
 | 
			
		||||
 	[SFP_MOD_WAITPWR] = "waitpwr",
 | 
			
		||||
 	[SFP_MOD_PRESENT] = "present",
 | 
			
		||||
-	[SFP_MOD_ERROR] = "error",
 | 
			
		||||
 };
 | 
			
		||||
 
 | 
			
		||||
 static const char *mod_state_to_str(unsigned short mod_state)
 | 
			
		||||
@@ -1479,16 +1481,13 @@ static int sfp_sm_mod_probe(struct sfp *
 | 
			
		||||
 	if (ret < 0)
 | 
			
		||||
 		return ret;
 | 
			
		||||
 
 | 
			
		||||
-	ret = sfp_module_insert(sfp->sfp_bus, &sfp->id);
 | 
			
		||||
-	if (ret < 0)
 | 
			
		||||
-		return ret;
 | 
			
		||||
-
 | 
			
		||||
 	return 0;
 | 
			
		||||
 }
 | 
			
		||||
 
 | 
			
		||||
 static void sfp_sm_mod_remove(struct sfp *sfp)
 | 
			
		||||
 {
 | 
			
		||||
-	sfp_module_remove(sfp->sfp_bus);
 | 
			
		||||
+	if (sfp->sm_mod_state > SFP_MOD_WAITDEV)
 | 
			
		||||
+		sfp_module_remove(sfp->sfp_bus);
 | 
			
		||||
 
 | 
			
		||||
 	sfp_hwmon_remove(sfp);
 | 
			
		||||
 
 | 
			
		||||
@@ -1539,12 +1538,12 @@ static void sfp_sm_module(struct sfp *sf
 | 
			
		||||
 	}
 | 
			
		||||
 
 | 
			
		||||
 	/* Handle device detach globally */
 | 
			
		||||
-	if (sfp->sm_dev_state < SFP_DEV_DOWN) {
 | 
			
		||||
+	if (sfp->sm_dev_state < SFP_DEV_DOWN &&
 | 
			
		||||
+	    sfp->sm_mod_state > SFP_MOD_WAITDEV) {
 | 
			
		||||
 		if (sfp->module_power_mW > 1000 &&
 | 
			
		||||
 		    sfp->sm_mod_state > SFP_MOD_HPOWER)
 | 
			
		||||
 			sfp_sm_mod_hpower(sfp, false);
 | 
			
		||||
-		if (sfp->sm_mod_state > SFP_MOD_EMPTY)
 | 
			
		||||
-			sfp_sm_mod_next(sfp, SFP_MOD_EMPTY, 0);
 | 
			
		||||
+		sfp_sm_mod_next(sfp, SFP_MOD_WAITDEV, 0);
 | 
			
		||||
 		return;
 | 
			
		||||
 	}
 | 
			
		||||
 
 | 
			
		||||
@@ -1555,6 +1554,7 @@ static void sfp_sm_module(struct sfp *sf
 | 
			
		||||
 		break;
 | 
			
		||||
 
 | 
			
		||||
 	case SFP_MOD_PROBE:
 | 
			
		||||
+		/* Wait for T_PROBE_INIT to time out */
 | 
			
		||||
 		if (event != SFP_E_TIMEOUT)
 | 
			
		||||
 			break;
 | 
			
		||||
 
 | 
			
		||||
@@ -1568,6 +1568,20 @@ static void sfp_sm_module(struct sfp *sf
 | 
			
		||||
 			break;
 | 
			
		||||
 		}
 | 
			
		||||
 
 | 
			
		||||
+		sfp_sm_mod_next(sfp, SFP_MOD_WAITDEV, 0);
 | 
			
		||||
+		/* fall through */
 | 
			
		||||
+	case SFP_MOD_WAITDEV:
 | 
			
		||||
+		/* Ensure that the device is attached before proceeding */
 | 
			
		||||
+		if (sfp->sm_dev_state < SFP_DEV_DOWN)
 | 
			
		||||
+			break;
 | 
			
		||||
+
 | 
			
		||||
+		/* Report the module insertion to the upstream device */
 | 
			
		||||
+		err = sfp_module_insert(sfp->sfp_bus, &sfp->id);
 | 
			
		||||
+		if (err < 0) {
 | 
			
		||||
+			sfp_sm_mod_next(sfp, SFP_MOD_ERROR, 0);
 | 
			
		||||
+			break;
 | 
			
		||||
+		}
 | 
			
		||||
+
 | 
			
		||||
 		/* If this is a power level 1 module, we are done */
 | 
			
		||||
 		if (sfp->module_power_mW <= 1000)
 | 
			
		||||
 			goto insert;
 | 
			
		||||
@@ -1577,12 +1591,17 @@ static void sfp_sm_module(struct sfp *sf
 | 
			
		||||
 	case SFP_MOD_HPOWER:
 | 
			
		||||
 		/* Enable high power mode */
 | 
			
		||||
 		err = sfp_sm_mod_hpower(sfp, true);
 | 
			
		||||
-		if (err == 0)
 | 
			
		||||
-			sfp_sm_mod_next(sfp, SFP_MOD_WAITPWR, T_HPOWER_LEVEL);
 | 
			
		||||
-		else if (err != -EAGAIN)
 | 
			
		||||
-			sfp_sm_mod_next(sfp, SFP_MOD_ERROR, 0);
 | 
			
		||||
-		else
 | 
			
		||||
-			sfp_sm_set_timer(sfp, T_PROBE_RETRY);
 | 
			
		||||
+		if (err < 0) {
 | 
			
		||||
+			if (err != -EAGAIN) {
 | 
			
		||||
+				sfp_module_remove(sfp->sfp_bus);
 | 
			
		||||
+				sfp_sm_mod_next(sfp, SFP_MOD_ERROR, 0);
 | 
			
		||||
+			} else {
 | 
			
		||||
+				sfp_sm_set_timer(sfp, T_PROBE_RETRY);
 | 
			
		||||
+			}
 | 
			
		||||
+			break;
 | 
			
		||||
+		}
 | 
			
		||||
+
 | 
			
		||||
+		sfp_sm_mod_next(sfp, SFP_MOD_WAITPWR, T_HPOWER_LEVEL);
 | 
			
		||||
 		break;
 | 
			
		||||
 
 | 
			
		||||
 	case SFP_MOD_WAITPWR:
 | 
			
		||||
@@ -1750,8 +1769,6 @@ static void sfp_sm_event(struct sfp *sfp
 | 
			
		||||
 static void sfp_attach(struct sfp *sfp)
 | 
			
		||||
 {
 | 
			
		||||
 	sfp_sm_event(sfp, SFP_E_DEV_ATTACH);
 | 
			
		||||
-	if (sfp->state & SFP_F_PRESENT)
 | 
			
		||||
-		sfp_sm_event(sfp, SFP_E_INSERT);
 | 
			
		||||
 }
 | 
			
		||||
 
 | 
			
		||||
 static void sfp_detach(struct sfp *sfp)
 | 
			
		||||
@@ -2001,6 +2018,11 @@ static int sfp_probe(struct platform_dev
 | 
			
		||||
 		sfp->state |= SFP_F_RATE_SELECT;
 | 
			
		||||
 	sfp_set_state(sfp, sfp->state);
 | 
			
		||||
 	sfp_module_tx_disable(sfp);
 | 
			
		||||
+	if (sfp->state & SFP_F_PRESENT) {
 | 
			
		||||
+		rtnl_lock();
 | 
			
		||||
+		sfp_sm_event(sfp, SFP_E_INSERT);
 | 
			
		||||
+		rtnl_unlock();
 | 
			
		||||
+	}
 | 
			
		||||
 
 | 
			
		||||
 	for (i = 0; i < GPIO_MAX; i++) {
 | 
			
		||||
 		if (gpio_flags[i] != GPIOD_IN || !sfp->gpio[i])
 | 
			
		||||
@ -0,0 +1,110 @@
 | 
			
		||||
From fb56cd08880aff8fb030e684fa4311bef712a499 Mon Sep 17 00:00:00 2001
 | 
			
		||||
From: Russell King <rmk+kernel@armlinux.org.uk>
 | 
			
		||||
Date: Tue, 5 Nov 2019 13:02:30 +0000
 | 
			
		||||
Subject: [PATCH 633/660] net: sfp: allow sfp to probe slow to initialise GPON
 | 
			
		||||
 modules
 | 
			
		||||
 | 
			
		||||
Some GPON modules (e.g. Huawei MA5671A) take a significant amount of
 | 
			
		||||
time to start responding on the I2C bus, contary to the SFF
 | 
			
		||||
specifications.
 | 
			
		||||
 | 
			
		||||
Work around this by implementing a two-level timeout strategy, where
 | 
			
		||||
we initially quickly retry for the module, and then use a slower retry
 | 
			
		||||
after we exceed a maximum number of quick attempts.
 | 
			
		||||
 | 
			
		||||
Signed-off-by: Russell King <rmk+kernel@armlinux.org.uk>
 | 
			
		||||
---
 | 
			
		||||
 drivers/net/phy/sfp.c | 38 ++++++++++++++++++++++++++++----------
 | 
			
		||||
 1 file changed, 28 insertions(+), 10 deletions(-)
 | 
			
		||||
 | 
			
		||||
--- a/drivers/net/phy/sfp.c
 | 
			
		||||
+++ b/drivers/net/phy/sfp.c
 | 
			
		||||
@@ -165,9 +165,12 @@ static const enum gpiod_flags gpio_flags
 | 
			
		||||
  * The SFF-8472 specifies t_serial ("Time from power on until module is
 | 
			
		||||
  * ready for data transmission over the two wire serial bus.") as 300ms.
 | 
			
		||||
  */
 | 
			
		||||
-#define T_SERIAL	msecs_to_jiffies(300)
 | 
			
		||||
-#define T_HPOWER_LEVEL	msecs_to_jiffies(300)
 | 
			
		||||
-#define T_PROBE_RETRY	msecs_to_jiffies(100)
 | 
			
		||||
+#define T_SERIAL		msecs_to_jiffies(300)
 | 
			
		||||
+#define T_HPOWER_LEVEL		msecs_to_jiffies(300)
 | 
			
		||||
+#define T_PROBE_RETRY_INIT	msecs_to_jiffies(100)
 | 
			
		||||
+#define R_PROBE_RETRY_INIT	10
 | 
			
		||||
+#define T_PROBE_RETRY_SLOW	msecs_to_jiffies(5000)
 | 
			
		||||
+#define R_PROBE_RETRY_SLOW	12
 | 
			
		||||
 
 | 
			
		||||
 /* SFP modules appear to always have their PHY configured for bus address
 | 
			
		||||
  * 0x56 (which with mdio-i2c, translates to a PHY address of 22).
 | 
			
		||||
@@ -202,6 +205,8 @@ struct sfp {
 | 
			
		||||
 	struct delayed_work timeout;
 | 
			
		||||
 	struct mutex sm_mutex;			/* Protects state machine */
 | 
			
		||||
 	unsigned char sm_mod_state;
 | 
			
		||||
+	unsigned char sm_mod_tries_init;
 | 
			
		||||
+	unsigned char sm_mod_tries;
 | 
			
		||||
 	unsigned char sm_dev_state;
 | 
			
		||||
 	unsigned short sm_state;
 | 
			
		||||
 	unsigned int sm_retries;
 | 
			
		||||
@@ -1392,7 +1397,7 @@ static int sfp_sm_mod_hpower(struct sfp
 | 
			
		||||
 	return 0;
 | 
			
		||||
 }
 | 
			
		||||
 
 | 
			
		||||
-static int sfp_sm_mod_probe(struct sfp *sfp)
 | 
			
		||||
+static int sfp_sm_mod_probe(struct sfp *sfp, bool report)
 | 
			
		||||
 {
 | 
			
		||||
 	/* SFP module inserted - read I2C data */
 | 
			
		||||
 	struct sfp_eeprom_id id;
 | 
			
		||||
@@ -1402,7 +1407,8 @@ static int sfp_sm_mod_probe(struct sfp *
 | 
			
		||||
 
 | 
			
		||||
 	ret = sfp_read(sfp, false, 0, &id, sizeof(id));
 | 
			
		||||
 	if (ret < 0) {
 | 
			
		||||
-		dev_err(sfp->dev, "failed to read EEPROM: %d\n", ret);
 | 
			
		||||
+		if (report)
 | 
			
		||||
+			dev_err(sfp->dev, "failed to read EEPROM: %d\n", ret);
 | 
			
		||||
 		return -EAGAIN;
 | 
			
		||||
 	}
 | 
			
		||||
 
 | 
			
		||||
@@ -1549,8 +1555,11 @@ static void sfp_sm_module(struct sfp *sf
 | 
			
		||||
 
 | 
			
		||||
 	switch (sfp->sm_mod_state) {
 | 
			
		||||
 	default:
 | 
			
		||||
-		if (event == SFP_E_INSERT)
 | 
			
		||||
+		if (event == SFP_E_INSERT) {
 | 
			
		||||
 			sfp_sm_mod_next(sfp, SFP_MOD_PROBE, T_SERIAL);
 | 
			
		||||
+			sfp->sm_mod_tries_init = R_PROBE_RETRY_INIT;
 | 
			
		||||
+			sfp->sm_mod_tries = R_PROBE_RETRY_SLOW;
 | 
			
		||||
+		}
 | 
			
		||||
 		break;
 | 
			
		||||
 
 | 
			
		||||
 	case SFP_MOD_PROBE:
 | 
			
		||||
@@ -1558,10 +1567,19 @@ static void sfp_sm_module(struct sfp *sf
 | 
			
		||||
 		if (event != SFP_E_TIMEOUT)
 | 
			
		||||
 			break;
 | 
			
		||||
 
 | 
			
		||||
-		err = sfp_sm_mod_probe(sfp);
 | 
			
		||||
+		err = sfp_sm_mod_probe(sfp, sfp->sm_mod_tries == 1);
 | 
			
		||||
 		if (err == -EAGAIN) {
 | 
			
		||||
-			sfp_sm_set_timer(sfp, T_PROBE_RETRY);
 | 
			
		||||
-			break;
 | 
			
		||||
+			if (sfp->sm_mod_tries_init &&
 | 
			
		||||
+			   --sfp->sm_mod_tries_init) {
 | 
			
		||||
+				sfp_sm_set_timer(sfp, T_PROBE_RETRY_INIT);
 | 
			
		||||
+				break;
 | 
			
		||||
+			} else if (sfp->sm_mod_tries && --sfp->sm_mod_tries) {
 | 
			
		||||
+				if (sfp->sm_mod_tries == R_PROBE_RETRY_SLOW - 1)
 | 
			
		||||
+					dev_warn(sfp->dev,
 | 
			
		||||
+						 "please wait, module slow to respond\n");
 | 
			
		||||
+				sfp_sm_set_timer(sfp, T_PROBE_RETRY_SLOW);
 | 
			
		||||
+				break;
 | 
			
		||||
+			}
 | 
			
		||||
 		}
 | 
			
		||||
 		if (err < 0) {
 | 
			
		||||
 			sfp_sm_mod_next(sfp, SFP_MOD_ERROR, 0);
 | 
			
		||||
@@ -1596,7 +1614,7 @@ static void sfp_sm_module(struct sfp *sf
 | 
			
		||||
 				sfp_module_remove(sfp->sfp_bus);
 | 
			
		||||
 				sfp_sm_mod_next(sfp, SFP_MOD_ERROR, 0);
 | 
			
		||||
 			} else {
 | 
			
		||||
-				sfp_sm_set_timer(sfp, T_PROBE_RETRY);
 | 
			
		||||
+				sfp_sm_set_timer(sfp, T_PROBE_RETRY_INIT);
 | 
			
		||||
 			}
 | 
			
		||||
 			break;
 | 
			
		||||
 		}
 | 
			
		||||
@ -0,0 +1,198 @@
 | 
			
		||||
From 559391fc20fae506adcb311b904cc544c76436c0 Mon Sep 17 00:00:00 2001
 | 
			
		||||
From: Russell King <rmk+kernel@armlinux.org.uk>
 | 
			
		||||
Date: Thu, 7 Nov 2019 18:52:07 +0000
 | 
			
		||||
Subject: [PATCH 634/660] net: sfp: allow modules with slow diagnostics to
 | 
			
		||||
 probe
 | 
			
		||||
 | 
			
		||||
When a module is inserted, we attempt to read read the ID from address
 | 
			
		||||
0x50.  Once we are able to read the ID, we immediately attempt to
 | 
			
		||||
initialise the hwmon support by reading from address 0x51.  If this
 | 
			
		||||
fails, then we fall into error state, and assume that the module is
 | 
			
		||||
not usable.
 | 
			
		||||
 | 
			
		||||
Modules such as the ALCATELLUCENT 3FE46541AA use a real EEPROM for
 | 
			
		||||
I2C address 0x50, which responds immediately.  However, address 0x51
 | 
			
		||||
is an emulated, which only becomes available once the on-board firmware
 | 
			
		||||
has booted.  This prompts us to fall into the error state.
 | 
			
		||||
 | 
			
		||||
Since the module may be usable without diagnostics, arrange for the
 | 
			
		||||
hwmon probe independent of the rest of the SFP itself, retrying every
 | 
			
		||||
5s for up to about 60s for the monitoring to become available, and
 | 
			
		||||
print an error message if it doesn't become available.
 | 
			
		||||
 | 
			
		||||
Signed-off-by: Russell King <rmk+kernel@armlinux.org.uk>
 | 
			
		||||
---
 | 
			
		||||
 drivers/net/phy/sfp.c | 96 +++++++++++++++++++++++++++++++++----------
 | 
			
		||||
 1 file changed, 74 insertions(+), 22 deletions(-)
 | 
			
		||||
 | 
			
		||||
--- a/drivers/net/phy/sfp.c
 | 
			
		||||
+++ b/drivers/net/phy/sfp.c
 | 
			
		||||
@@ -216,6 +216,8 @@ struct sfp {
 | 
			
		||||
 
 | 
			
		||||
 #if IS_ENABLED(CONFIG_HWMON)
 | 
			
		||||
 	struct sfp_diag diag;
 | 
			
		||||
+	struct delayed_work hwmon_probe;
 | 
			
		||||
+	unsigned int hwmon_tries;
 | 
			
		||||
 	struct device *hwmon_dev;
 | 
			
		||||
 	char *hwmon_name;
 | 
			
		||||
 #endif
 | 
			
		||||
@@ -1094,29 +1096,27 @@ static const struct hwmon_chip_info sfp_
 | 
			
		||||
 	.info = sfp_hwmon_info,
 | 
			
		||||
 };
 | 
			
		||||
 
 | 
			
		||||
-static int sfp_hwmon_insert(struct sfp *sfp)
 | 
			
		||||
+static void sfp_hwmon_probe(struct work_struct *work)
 | 
			
		||||
 {
 | 
			
		||||
+	struct sfp *sfp = container_of(work, struct sfp, hwmon_probe.work);
 | 
			
		||||
 	int err, i;
 | 
			
		||||
 
 | 
			
		||||
-	if (sfp->id.ext.sff8472_compliance == SFP_SFF8472_COMPLIANCE_NONE)
 | 
			
		||||
-		return 0;
 | 
			
		||||
-
 | 
			
		||||
-	if (!(sfp->id.ext.diagmon & SFP_DIAGMON_DDM))
 | 
			
		||||
-		return 0;
 | 
			
		||||
-
 | 
			
		||||
-	if (sfp->id.ext.diagmon & SFP_DIAGMON_ADDRMODE)
 | 
			
		||||
-		/* This driver in general does not support address
 | 
			
		||||
-		 * change.
 | 
			
		||||
-		 */
 | 
			
		||||
-		return 0;
 | 
			
		||||
-
 | 
			
		||||
 	err = sfp_read(sfp, true, 0, &sfp->diag, sizeof(sfp->diag));
 | 
			
		||||
-	if (err < 0)
 | 
			
		||||
-		return err;
 | 
			
		||||
+	if (err < 0) {
 | 
			
		||||
+		if (sfp->hwmon_tries--) {
 | 
			
		||||
+			mod_delayed_work(system_wq, &sfp->hwmon_probe,
 | 
			
		||||
+					 T_PROBE_RETRY_SLOW);
 | 
			
		||||
+		} else {
 | 
			
		||||
+			dev_warn(sfp->dev, "hwmon probe failed: %d\n", err);
 | 
			
		||||
+		}
 | 
			
		||||
+		return;
 | 
			
		||||
+	}
 | 
			
		||||
 
 | 
			
		||||
 	sfp->hwmon_name = kstrdup(dev_name(sfp->dev), GFP_KERNEL);
 | 
			
		||||
-	if (!sfp->hwmon_name)
 | 
			
		||||
-		return -ENODEV;
 | 
			
		||||
+	if (!sfp->hwmon_name) {
 | 
			
		||||
+		dev_err(sfp->dev, "out of memory for hwmon name\n");
 | 
			
		||||
+		return;
 | 
			
		||||
+	}
 | 
			
		||||
 
 | 
			
		||||
 	for (i = 0; sfp->hwmon_name[i]; i++)
 | 
			
		||||
 		if (hwmon_is_bad_char(sfp->hwmon_name[i]))
 | 
			
		||||
@@ -1126,18 +1126,52 @@ static int sfp_hwmon_insert(struct sfp *
 | 
			
		||||
 							 sfp->hwmon_name, sfp,
 | 
			
		||||
 							 &sfp_hwmon_chip_info,
 | 
			
		||||
 							 NULL);
 | 
			
		||||
+	if (IS_ERR(sfp->hwmon_dev))
 | 
			
		||||
+		dev_err(sfp->dev, "failed to register hwmon device: %ld\n",
 | 
			
		||||
+			PTR_ERR(sfp->hwmon_dev));
 | 
			
		||||
+}
 | 
			
		||||
+
 | 
			
		||||
+static int sfp_hwmon_insert(struct sfp *sfp)
 | 
			
		||||
+{
 | 
			
		||||
+	if (sfp->id.ext.sff8472_compliance == SFP_SFF8472_COMPLIANCE_NONE)
 | 
			
		||||
+		return 0;
 | 
			
		||||
+
 | 
			
		||||
+	if (!(sfp->id.ext.diagmon & SFP_DIAGMON_DDM))
 | 
			
		||||
+		return 0;
 | 
			
		||||
+
 | 
			
		||||
+	if (sfp->id.ext.diagmon & SFP_DIAGMON_ADDRMODE)
 | 
			
		||||
+		/* This driver in general does not support address
 | 
			
		||||
+		 * change.
 | 
			
		||||
+		 */
 | 
			
		||||
+		return 0;
 | 
			
		||||
+
 | 
			
		||||
+	mod_delayed_work(system_wq, &sfp->hwmon_probe, 1);
 | 
			
		||||
+	sfp->hwmon_tries = R_PROBE_RETRY_SLOW;
 | 
			
		||||
 
 | 
			
		||||
-	return PTR_ERR_OR_ZERO(sfp->hwmon_dev);
 | 
			
		||||
+	return 0;
 | 
			
		||||
 }
 | 
			
		||||
 
 | 
			
		||||
 static void sfp_hwmon_remove(struct sfp *sfp)
 | 
			
		||||
 {
 | 
			
		||||
+	cancel_delayed_work_sync(&sfp->hwmon_probe);
 | 
			
		||||
 	if (!IS_ERR_OR_NULL(sfp->hwmon_dev)) {
 | 
			
		||||
 		hwmon_device_unregister(sfp->hwmon_dev);
 | 
			
		||||
 		sfp->hwmon_dev = NULL;
 | 
			
		||||
 		kfree(sfp->hwmon_name);
 | 
			
		||||
 	}
 | 
			
		||||
 }
 | 
			
		||||
+
 | 
			
		||||
+static int sfp_hwmon_init(struct sfp *sfp)
 | 
			
		||||
+{
 | 
			
		||||
+	INIT_DELAYED_WORK(&sfp->hwmon_probe, sfp_hwmon_probe);
 | 
			
		||||
+
 | 
			
		||||
+	return 0;
 | 
			
		||||
+}
 | 
			
		||||
+
 | 
			
		||||
+static void sfp_hwmon_exit(struct sfp *sfp)
 | 
			
		||||
+{
 | 
			
		||||
+	cancel_delayed_work_sync(&sfp->hwmon_probe);
 | 
			
		||||
+}
 | 
			
		||||
 #else
 | 
			
		||||
 static int sfp_hwmon_insert(struct sfp *sfp)
 | 
			
		||||
 {
 | 
			
		||||
@@ -1147,6 +1181,15 @@ static int sfp_hwmon_insert(struct sfp *
 | 
			
		||||
 static void sfp_hwmon_remove(struct sfp *sfp)
 | 
			
		||||
 {
 | 
			
		||||
 }
 | 
			
		||||
+
 | 
			
		||||
+static int sfp_hwmon_init(struct sfp *sfp)
 | 
			
		||||
+{
 | 
			
		||||
+	return 0;
 | 
			
		||||
+}
 | 
			
		||||
+
 | 
			
		||||
+static void sfp_hwmon_exit(struct sfp *sfp)
 | 
			
		||||
+{
 | 
			
		||||
+}
 | 
			
		||||
 #endif
 | 
			
		||||
 
 | 
			
		||||
 /* Helpers */
 | 
			
		||||
@@ -1483,10 +1526,6 @@ static int sfp_sm_mod_probe(struct sfp *
 | 
			
		||||
 	if (ret < 0)
 | 
			
		||||
 		return ret;
 | 
			
		||||
 
 | 
			
		||||
-	ret = sfp_hwmon_insert(sfp);
 | 
			
		||||
-	if (ret < 0)
 | 
			
		||||
-		return ret;
 | 
			
		||||
-
 | 
			
		||||
 	return 0;
 | 
			
		||||
 }
 | 
			
		||||
 
 | 
			
		||||
@@ -1635,6 +1674,15 @@ static void sfp_sm_module(struct sfp *sf
 | 
			
		||||
 	case SFP_MOD_ERROR:
 | 
			
		||||
 		break;
 | 
			
		||||
 	}
 | 
			
		||||
+
 | 
			
		||||
+#if IS_ENABLED(CONFIG_HWMON)
 | 
			
		||||
+	if (sfp->sm_mod_state >= SFP_MOD_WAITDEV &&
 | 
			
		||||
+	    IS_ERR_OR_NULL(sfp->hwmon_dev)) {
 | 
			
		||||
+		err = sfp_hwmon_insert(sfp);
 | 
			
		||||
+		if (err)
 | 
			
		||||
+			dev_warn(sfp->dev, "hwmon probe failed: %d\n", err);
 | 
			
		||||
+	}
 | 
			
		||||
+#endif
 | 
			
		||||
 }
 | 
			
		||||
 
 | 
			
		||||
 static void sfp_sm_main(struct sfp *sfp, unsigned int event)
 | 
			
		||||
@@ -1936,6 +1984,8 @@ static struct sfp *sfp_alloc(struct devi
 | 
			
		||||
 	INIT_DELAYED_WORK(&sfp->poll, sfp_poll);
 | 
			
		||||
 	INIT_DELAYED_WORK(&sfp->timeout, sfp_timeout);
 | 
			
		||||
 
 | 
			
		||||
+	sfp_hwmon_init(sfp);
 | 
			
		||||
+
 | 
			
		||||
 	return sfp;
 | 
			
		||||
 }
 | 
			
		||||
 
 | 
			
		||||
@@ -1943,6 +1993,8 @@ static void sfp_cleanup(void *data)
 | 
			
		||||
 {
 | 
			
		||||
 	struct sfp *sfp = data;
 | 
			
		||||
 
 | 
			
		||||
+	sfp_hwmon_exit(sfp);
 | 
			
		||||
+
 | 
			
		||||
 	cancel_delayed_work_sync(&sfp->poll);
 | 
			
		||||
 	cancel_delayed_work_sync(&sfp->timeout);
 | 
			
		||||
 	if (sfp->i2c_mii) {
 | 
			
		||||
@ -0,0 +1,183 @@
 | 
			
		||||
From eb156db588ac583cdae7b91eaac9c0ad3a358e63 Mon Sep 17 00:00:00 2001
 | 
			
		||||
From: Russell King <rmk+kernel@armlinux.org.uk>
 | 
			
		||||
Date: Sun, 15 Sep 2019 20:05:34 +0100
 | 
			
		||||
Subject: [PATCH 635/660] net: phy: add core phylib sfp support
 | 
			
		||||
 | 
			
		||||
Add core phylib help for supporting SFP sockets on PHYs.  This provides
 | 
			
		||||
a mechanism to inform the SFP layer about PHY up/down events, and also
 | 
			
		||||
unregister the SFP bus when the PHY is going away.
 | 
			
		||||
 | 
			
		||||
Signed-off-by: Russell King <rmk+kernel@armlinux.org.uk>
 | 
			
		||||
---
 | 
			
		||||
 drivers/net/phy/phy.c        |  7 ++++
 | 
			
		||||
 drivers/net/phy/phy_device.c | 66 ++++++++++++++++++++++++++++++++++++
 | 
			
		||||
 include/linux/phy.h          | 11 ++++++
 | 
			
		||||
 3 files changed, 84 insertions(+)
 | 
			
		||||
 | 
			
		||||
--- a/drivers/net/phy/phy.c
 | 
			
		||||
+++ b/drivers/net/phy/phy.c
 | 
			
		||||
@@ -30,6 +30,7 @@
 | 
			
		||||
 #include <linux/ethtool.h>
 | 
			
		||||
 #include <linux/phy.h>
 | 
			
		||||
 #include <linux/phy_led_triggers.h>
 | 
			
		||||
+#include <linux/sfp.h>
 | 
			
		||||
 #include <linux/workqueue.h>
 | 
			
		||||
 #include <linux/mdio.h>
 | 
			
		||||
 #include <linux/io.h>
 | 
			
		||||
@@ -871,6 +872,9 @@ void phy_stop(struct phy_device *phydev)
 | 
			
		||||
 	if (phy_interrupt_is_valid(phydev))
 | 
			
		||||
 		phy_disable_interrupts(phydev);
 | 
			
		||||
 
 | 
			
		||||
+	if (phydev->sfp_bus)
 | 
			
		||||
+		sfp_upstream_stop(phydev->sfp_bus);
 | 
			
		||||
+
 | 
			
		||||
 	phydev->state = PHY_HALTED;
 | 
			
		||||
 
 | 
			
		||||
 out_unlock:
 | 
			
		||||
@@ -899,6 +903,9 @@ void phy_start(struct phy_device *phydev
 | 
			
		||||
 
 | 
			
		||||
 	mutex_lock(&phydev->lock);
 | 
			
		||||
 
 | 
			
		||||
+	if (phydev->sfp_bus)
 | 
			
		||||
+		sfp_upstream_start(phydev->sfp_bus);
 | 
			
		||||
+
 | 
			
		||||
 	switch (phydev->state) {
 | 
			
		||||
 	case PHY_STARTING:
 | 
			
		||||
 		phydev->state = PHY_PENDING;
 | 
			
		||||
--- a/drivers/net/phy/phy_device.c
 | 
			
		||||
+++ b/drivers/net/phy/phy_device.c
 | 
			
		||||
@@ -31,6 +31,7 @@
 | 
			
		||||
 #include <linux/ethtool.h>
 | 
			
		||||
 #include <linux/phy.h>
 | 
			
		||||
 #include <linux/phy_led_triggers.h>
 | 
			
		||||
+#include <linux/sfp.h>
 | 
			
		||||
 #include <linux/mdio.h>
 | 
			
		||||
 #include <linux/io.h>
 | 
			
		||||
 #include <linux/uaccess.h>
 | 
			
		||||
@@ -943,6 +944,65 @@ void phy_attached_print(struct phy_devic
 | 
			
		||||
 EXPORT_SYMBOL(phy_attached_print);
 | 
			
		||||
 
 | 
			
		||||
 /**
 | 
			
		||||
+ * phy_sfp_attach - attach the SFP bus to the PHY upstream network device
 | 
			
		||||
+ * @upstream: pointer to the phy device
 | 
			
		||||
+ * @bus: sfp bus representing cage being attached
 | 
			
		||||
+ *
 | 
			
		||||
+ * This is used to fill in the sfp_upstream_ops .attach member.
 | 
			
		||||
+ */
 | 
			
		||||
+void phy_sfp_attach(void *upstream, struct sfp_bus *bus)
 | 
			
		||||
+{
 | 
			
		||||
+	struct phy_device *phydev = upstream;
 | 
			
		||||
+
 | 
			
		||||
+	if (phydev->attached_dev)
 | 
			
		||||
+		phydev->attached_dev->sfp_bus = bus;
 | 
			
		||||
+	phydev->sfp_bus_attached = true;
 | 
			
		||||
+}
 | 
			
		||||
+EXPORT_SYMBOL(phy_sfp_attach);
 | 
			
		||||
+
 | 
			
		||||
+/**
 | 
			
		||||
+ * phy_sfp_detach - detach the SFP bus from the PHY upstream network device
 | 
			
		||||
+ * @upstream: pointer to the phy device
 | 
			
		||||
+ * @bus: sfp bus representing cage being attached
 | 
			
		||||
+ *
 | 
			
		||||
+ * This is used to fill in the sfp_upstream_ops .detach member.
 | 
			
		||||
+ */
 | 
			
		||||
+void phy_sfp_detach(void *upstream, struct sfp_bus *bus)
 | 
			
		||||
+{
 | 
			
		||||
+	struct phy_device *phydev = upstream;
 | 
			
		||||
+
 | 
			
		||||
+	if (phydev->attached_dev)
 | 
			
		||||
+		phydev->attached_dev->sfp_bus = NULL;
 | 
			
		||||
+	phydev->sfp_bus_attached = false;
 | 
			
		||||
+}
 | 
			
		||||
+EXPORT_SYMBOL(phy_sfp_detach);
 | 
			
		||||
+
 | 
			
		||||
+/**
 | 
			
		||||
+ * phy_sfp_probe - probe for a SFP cage attached to this PHY device
 | 
			
		||||
+ * @phydev: Pointer to phy_device
 | 
			
		||||
+ * @ops: SFP's upstream operations
 | 
			
		||||
+ */
 | 
			
		||||
+int phy_sfp_probe(struct phy_device *phydev,
 | 
			
		||||
+		  const struct sfp_upstream_ops *ops)
 | 
			
		||||
+{
 | 
			
		||||
+	struct sfp_bus *bus;
 | 
			
		||||
+	int ret;
 | 
			
		||||
+
 | 
			
		||||
+	if (phydev->mdio.dev.fwnode) {
 | 
			
		||||
+		bus = sfp_bus_find_fwnode(phydev->mdio.dev.fwnode);
 | 
			
		||||
+		if (IS_ERR(bus))
 | 
			
		||||
+			return PTR_ERR(bus);
 | 
			
		||||
+
 | 
			
		||||
+		phydev->sfp_bus = bus;
 | 
			
		||||
+
 | 
			
		||||
+		ret = sfp_bus_add_upstream(bus, phydev, ops);
 | 
			
		||||
+		sfp_bus_put(bus);
 | 
			
		||||
+	}
 | 
			
		||||
+	return 0;
 | 
			
		||||
+}
 | 
			
		||||
+EXPORT_SYMBOL(phy_sfp_probe);
 | 
			
		||||
+
 | 
			
		||||
+/**
 | 
			
		||||
  * phy_attach_direct - attach a network device to a given PHY device pointer
 | 
			
		||||
  * @dev: network device to attach
 | 
			
		||||
  * @phydev: Pointer to phy_device to attach
 | 
			
		||||
@@ -1015,6 +1075,9 @@ int phy_attach_direct(struct net_device
 | 
			
		||||
 	phydev->attached_dev = dev;
 | 
			
		||||
 	dev->phydev = phydev;
 | 
			
		||||
 
 | 
			
		||||
+	if (phydev->sfp_bus_attached)
 | 
			
		||||
+		dev->sfp_bus = phydev->sfp_bus;
 | 
			
		||||
+
 | 
			
		||||
 	/* Some Ethernet drivers try to connect to a PHY device before
 | 
			
		||||
 	 * calling register_netdevice() -> netdev_register_kobject() and
 | 
			
		||||
 	 * does the dev->dev.kobj initialization. Here we only check for
 | 
			
		||||
@@ -1949,6 +2012,9 @@ static int phy_remove(struct device *dev
 | 
			
		||||
 	phydev->state = PHY_DOWN;
 | 
			
		||||
 	mutex_unlock(&phydev->lock);
 | 
			
		||||
 
 | 
			
		||||
+	sfp_bus_del_upstream(phydev->sfp_bus);
 | 
			
		||||
+	phydev->sfp_bus = NULL;
 | 
			
		||||
+
 | 
			
		||||
 	if (phydev->drv && phydev->drv->remove) {
 | 
			
		||||
 		phydev->drv->remove(phydev);
 | 
			
		||||
 
 | 
			
		||||
--- a/include/linux/phy.h
 | 
			
		||||
+++ b/include/linux/phy.h
 | 
			
		||||
@@ -184,6 +184,8 @@ static inline const char *phy_modes(phy_
 | 
			
		||||
 
 | 
			
		||||
 struct device;
 | 
			
		||||
 struct phylink;
 | 
			
		||||
+struct sfp_bus;
 | 
			
		||||
+struct sfp_upstream_ops;
 | 
			
		||||
 struct sk_buff;
 | 
			
		||||
 
 | 
			
		||||
 /*
 | 
			
		||||
@@ -382,6 +384,8 @@ struct phy_c45_device_ids {
 | 
			
		||||
  * irq: IRQ number of the PHY's interrupt (-1 if none)
 | 
			
		||||
  * phy_timer: The timer for handling the state machine
 | 
			
		||||
  * phy_queue: A work_queue for the phy_mac_interrupt
 | 
			
		||||
+ * sfp_bus_attached: flag indicating whether the SFP bus has been attached
 | 
			
		||||
+ * sfp_bus: SFP bus attached to this PHY's fiber port
 | 
			
		||||
  * attached_dev: The attached enet driver's device instance ptr
 | 
			
		||||
  * adjust_link: Callback for the enet controller to respond to
 | 
			
		||||
  * changes in the link state.
 | 
			
		||||
@@ -471,6 +475,9 @@ struct phy_device {
 | 
			
		||||
 
 | 
			
		||||
 	struct mutex lock;
 | 
			
		||||
 
 | 
			
		||||
+	/* This may be modified under the rtnl lock */
 | 
			
		||||
+	bool sfp_bus_attached;
 | 
			
		||||
+	struct sfp_bus *sfp_bus;
 | 
			
		||||
 	struct phylink *phylink;
 | 
			
		||||
 	struct net_device *attached_dev;
 | 
			
		||||
 
 | 
			
		||||
@@ -1031,6 +1038,10 @@ int phy_suspend(struct phy_device *phyde
 | 
			
		||||
 int phy_resume(struct phy_device *phydev);
 | 
			
		||||
 int __phy_resume(struct phy_device *phydev);
 | 
			
		||||
 int phy_loopback(struct phy_device *phydev, bool enable);
 | 
			
		||||
+void phy_sfp_attach(void *upstream, struct sfp_bus *bus);
 | 
			
		||||
+void phy_sfp_detach(void *upstream, struct sfp_bus *bus);
 | 
			
		||||
+int phy_sfp_probe(struct phy_device *phydev,
 | 
			
		||||
+	          const struct sfp_upstream_ops *ops);
 | 
			
		||||
 struct phy_device *phy_attach(struct net_device *dev, const char *bus_id,
 | 
			
		||||
 			      phy_interface_t interface);
 | 
			
		||||
 struct phy_device *phy_find_first(struct mii_bus *bus);
 | 
			
		||||
@ -0,0 +1,67 @@
 | 
			
		||||
From 0836d9fb41ed90090ef4af0d7abe784ee7706f80 Mon Sep 17 00:00:00 2001
 | 
			
		||||
From: Russell King <rmk+kernel@armlinux.org.uk>
 | 
			
		||||
Date: Fri, 14 Apr 2017 14:21:25 +0100
 | 
			
		||||
Subject: [PATCH 636/660] net: phy: marvell10g: add SFP+ support
 | 
			
		||||
 | 
			
		||||
Add support for SFP+ cages to the Marvell 10G PHY driver. This is
 | 
			
		||||
slightly complicated by the way phylib works in that we need to use
 | 
			
		||||
a multi-step process to attach the SFP bus, and we also need to track
 | 
			
		||||
the phylink state machine to know when the module's transmit disable
 | 
			
		||||
signal should change state.
 | 
			
		||||
 | 
			
		||||
With appropriate DT changes, this allows the SFP+ canges on the
 | 
			
		||||
Macchiatobin platform to be functional.
 | 
			
		||||
 | 
			
		||||
Signed-off-by: Russell King <rmk+kernel@armlinux.org.uk>
 | 
			
		||||
---
 | 
			
		||||
 drivers/net/phy/marvell10g.c | 25 ++++++++++++++++++++++++-
 | 
			
		||||
 1 file changed, 24 insertions(+), 1 deletion(-)
 | 
			
		||||
 | 
			
		||||
--- a/drivers/net/phy/marvell10g.c
 | 
			
		||||
+++ b/drivers/net/phy/marvell10g.c
 | 
			
		||||
@@ -25,6 +25,7 @@
 | 
			
		||||
 #include <linux/hwmon.h>
 | 
			
		||||
 #include <linux/marvell_phy.h>
 | 
			
		||||
 #include <linux/phy.h>
 | 
			
		||||
+#include <linux/sfp.h>
 | 
			
		||||
 
 | 
			
		||||
 enum {
 | 
			
		||||
 	MV_PMA_BOOT		= 0xc050,
 | 
			
		||||
@@ -219,6 +220,28 @@ static int mv3310_hwmon_probe(struct phy
 | 
			
		||||
 }
 | 
			
		||||
 #endif
 | 
			
		||||
 
 | 
			
		||||
+static int mv3310_sfp_insert(void *upstream, const struct sfp_eeprom_id *id)
 | 
			
		||||
+{
 | 
			
		||||
+	struct phy_device *phydev = upstream;
 | 
			
		||||
+	__ETHTOOL_DECLARE_LINK_MODE_MASK(support) = { 0, };
 | 
			
		||||
+	phy_interface_t iface;
 | 
			
		||||
+
 | 
			
		||||
+	sfp_parse_support(phydev->sfp_bus, id, support);
 | 
			
		||||
+	iface = sfp_select_interface(phydev->sfp_bus, id, support);
 | 
			
		||||
+
 | 
			
		||||
+	if (iface != PHY_INTERFACE_MODE_10GKR) {
 | 
			
		||||
+		dev_err(&phydev->mdio.dev, "incompatible SFP module inserted\n");
 | 
			
		||||
+		return -EINVAL;
 | 
			
		||||
+	}
 | 
			
		||||
+	return 0;
 | 
			
		||||
+}
 | 
			
		||||
+
 | 
			
		||||
+static const struct sfp_upstream_ops mv3310_sfp_ops = {
 | 
			
		||||
+	.attach = phy_sfp_attach,
 | 
			
		||||
+	.detach = phy_sfp_detach,
 | 
			
		||||
+	.module_insert = mv3310_sfp_insert,
 | 
			
		||||
+};
 | 
			
		||||
+
 | 
			
		||||
 static int mv3310_probe(struct phy_device *phydev)
 | 
			
		||||
 {
 | 
			
		||||
 	struct mv3310_priv *priv;
 | 
			
		||||
@@ -249,7 +272,7 @@ static int mv3310_probe(struct phy_devic
 | 
			
		||||
 	if (ret)
 | 
			
		||||
 		return ret;
 | 
			
		||||
 
 | 
			
		||||
-	return 0;
 | 
			
		||||
+	return phy_sfp_probe(phydev, &mv3310_sfp_ops);
 | 
			
		||||
 }
 | 
			
		||||
 
 | 
			
		||||
 static int mv3310_suspend(struct phy_device *phydev)
 | 
			
		||||
@ -0,0 +1,45 @@
 | 
			
		||||
From 09d7d8395ec61fba4392b35baa6f71c4e36489df Mon Sep 17 00:00:00 2001
 | 
			
		||||
From: Russell King <rmk+kernel@armlinux.org.uk>
 | 
			
		||||
Date: Fri, 8 Nov 2019 15:18:02 +0000
 | 
			
		||||
Subject: [PATCH 637/660] net: phylink: update to use phy_support_asym_pause()
 | 
			
		||||
 | 
			
		||||
Use phy_support_asym_pause() rather than open-coding it.
 | 
			
		||||
 | 
			
		||||
Signed-off-by: Russell King <rmk+kernel@armlinux.org.uk>
 | 
			
		||||
---
 | 
			
		||||
 drivers/net/phy/phylink.c | 17 +++++++----------
 | 
			
		||||
 1 file changed, 7 insertions(+), 10 deletions(-)
 | 
			
		||||
 | 
			
		||||
--- a/drivers/net/phy/phylink.c
 | 
			
		||||
+++ b/drivers/net/phy/phylink.c
 | 
			
		||||
@@ -678,12 +678,6 @@ static int phylink_bringup_phy(struct ph
 | 
			
		||||
 	u32 advertising;
 | 
			
		||||
 	int ret;
 | 
			
		||||
 
 | 
			
		||||
-	memset(&config, 0, sizeof(config));
 | 
			
		||||
-	ethtool_convert_legacy_u32_to_link_mode(supported, phy->supported);
 | 
			
		||||
-	ethtool_convert_legacy_u32_to_link_mode(config.advertising,
 | 
			
		||||
-						phy->advertising);
 | 
			
		||||
-	config.interface = pl->link_config.interface;
 | 
			
		||||
-
 | 
			
		||||
 	/*
 | 
			
		||||
 	 * This is the new way of dealing with flow control for PHYs,
 | 
			
		||||
 	 * as described by Timur Tabi in commit 529ed1275263 ("net: phy:
 | 
			
		||||
@@ -691,10 +685,13 @@ static int phylink_bringup_phy(struct ph
 | 
			
		||||
 	 * using our validate call to the MAC, we rely upon the MAC
 | 
			
		||||
 	 * clearing the bits from both supported and advertising fields.
 | 
			
		||||
 	 */
 | 
			
		||||
-	if (phylink_test(supported, Pause))
 | 
			
		||||
-		phylink_set(config.advertising, Pause);
 | 
			
		||||
-	if (phylink_test(supported, Asym_Pause))
 | 
			
		||||
-		phylink_set(config.advertising, Asym_Pause);
 | 
			
		||||
+	phy_support_asym_pause(phy);
 | 
			
		||||
+
 | 
			
		||||
+	memset(&config, 0, sizeof(config));
 | 
			
		||||
+	ethtool_convert_legacy_u32_to_link_mode(supported, phy->supported);
 | 
			
		||||
+	ethtool_convert_legacy_u32_to_link_mode(config.advertising,
 | 
			
		||||
+						phy->advertising);
 | 
			
		||||
+	config.interface = pl->link_config.interface;
 | 
			
		||||
 
 | 
			
		||||
 	ret = phylink_validate(pl, supported, &config);
 | 
			
		||||
 	if (ret)
 | 
			
		||||
@ -0,0 +1,63 @@
 | 
			
		||||
From 1be8018db381200c24854e0c299206c557f76fe0 Mon Sep 17 00:00:00 2001
 | 
			
		||||
From: Russell King <rmk+kernel@armlinux.org.uk>
 | 
			
		||||
Date: Mon, 11 Nov 2019 11:58:09 +0000
 | 
			
		||||
Subject: [PATCH 638/660] net: phy: avoid matching all-ones clause 45 PHY IDs
 | 
			
		||||
 | 
			
		||||
We currently match clause 45 PHYs using any ID read from a MMD marked
 | 
			
		||||
as present in the "Devices in package" registers 5 and 6.  However,
 | 
			
		||||
this is incorrect.  45.2 says:
 | 
			
		||||
 | 
			
		||||
  "The definition of the term package is vendor specific and could be
 | 
			
		||||
   a chip, module, or other similar entity."
 | 
			
		||||
 | 
			
		||||
so a package could be more or less than the whole PHY - a PHY could be
 | 
			
		||||
made up of several modules instantiated onto a single chip such as the
 | 
			
		||||
Marvell 88x3310, or some of the MMDs could be disabled according to
 | 
			
		||||
chip configuration, such as the Broadcom 84881.
 | 
			
		||||
 | 
			
		||||
In the case of Broadcom 84881, the "Devices in package" registers
 | 
			
		||||
contain 0xc000009b, meaning that there is a PHYXS present in the
 | 
			
		||||
package, but all registers in MMD 4 return 0xffff.  This leads to our
 | 
			
		||||
matching code incorrectly binding this PHY to one of our generic PHY
 | 
			
		||||
drivers.
 | 
			
		||||
 | 
			
		||||
This patch changes the way we determine whether to attempt to match a
 | 
			
		||||
MMD identifier, or use it to request a module - if the identifier is
 | 
			
		||||
all-ones, then we skip over it. When reading the identifiers, we
 | 
			
		||||
initialise phydev->c45_ids.device_ids to all-ones, only reading the
 | 
			
		||||
device ID if the "Devices in package" registers indicates we should.
 | 
			
		||||
 | 
			
		||||
This avoids the generic drivers incorrectly matching on a PHY ID of
 | 
			
		||||
0xffffffff.
 | 
			
		||||
 | 
			
		||||
Signed-off-by: Russell King <rmk+kernel@armlinux.org.uk>
 | 
			
		||||
---
 | 
			
		||||
 drivers/net/phy/phy_device.c | 7 +++++--
 | 
			
		||||
 1 file changed, 5 insertions(+), 2 deletions(-)
 | 
			
		||||
 | 
			
		||||
--- a/drivers/net/phy/phy_device.c
 | 
			
		||||
+++ b/drivers/net/phy/phy_device.c
 | 
			
		||||
@@ -334,7 +334,7 @@ static int phy_bus_match(struct device *
 | 
			
		||||
 
 | 
			
		||||
 	if (phydev->is_c45) {
 | 
			
		||||
 		for (i = 1; i < num_ids; i++) {
 | 
			
		||||
-			if (!(phydev->c45_ids.devices_in_package & (1 << i)))
 | 
			
		||||
+			if (phydev->c45_ids.device_ids[i] == 0xffffffff)
 | 
			
		||||
 				continue;
 | 
			
		||||
 
 | 
			
		||||
 			if ((phydrv->phy_id & phydrv->phy_id_mask) ==
 | 
			
		||||
@@ -622,10 +622,13 @@ static int get_phy_id(struct mii_bus *bu
 | 
			
		||||
  */
 | 
			
		||||
 struct phy_device *get_phy_device(struct mii_bus *bus, int addr, bool is_c45)
 | 
			
		||||
 {
 | 
			
		||||
-	struct phy_c45_device_ids c45_ids = {0};
 | 
			
		||||
+	struct phy_c45_device_ids c45_ids;
 | 
			
		||||
 	u32 phy_id = 0;
 | 
			
		||||
 	int r;
 | 
			
		||||
 
 | 
			
		||||
+	c45_ids.devices_in_package = 0;
 | 
			
		||||
+	memset(c45_ids.device_ids, 0xff, sizeof(c45_ids.device_ids));
 | 
			
		||||
+
 | 
			
		||||
 	r = get_phy_id(bus, addr, &phy_id, is_c45, &c45_ids);
 | 
			
		||||
 	if (r)
 | 
			
		||||
 		return ERR_PTR(r);
 | 
			
		||||
@ -0,0 +1,66 @@
 | 
			
		||||
From 4c9633f75dc35abe1b9261e0415d77802f35741d Mon Sep 17 00:00:00 2001
 | 
			
		||||
From: Russell King <rmk+kernel@armlinux.org.uk>
 | 
			
		||||
Date: Tue, 5 Nov 2019 11:58:00 +0000
 | 
			
		||||
Subject: [PATCH 639/660] net: phylink: fix link mode modification in PHY mode
 | 
			
		||||
 | 
			
		||||
Modifying the link settings via phylink_ethtool_ksettings_set() and
 | 
			
		||||
phylink_ethtool_set_pauseparam() didn't always work as intended for
 | 
			
		||||
PHY based setups, as calling phylink_mac_config() would result in the
 | 
			
		||||
unresolved configuration being committed to the MAC, rather than the
 | 
			
		||||
configuration with the speed and duplex setting.
 | 
			
		||||
 | 
			
		||||
This would work fine if the update caused the link to renegotiate,
 | 
			
		||||
but if no settings have changed, phylib won't trigger a renegotiation
 | 
			
		||||
cycle, and the MAC will be left incorrectly configured.
 | 
			
		||||
 | 
			
		||||
Avoid calling phylink_mac_config() unless we are using an inband mode
 | 
			
		||||
in phylink_ethtool_ksettings_set(), and use phy_set_asym_pause() as
 | 
			
		||||
introduced in 4.20 to set the PHY settings in
 | 
			
		||||
phylink_ethtool_set_pauseparam().
 | 
			
		||||
 | 
			
		||||
Signed-off-by: Russell King <rmk+kernel@armlinux.org.uk>
 | 
			
		||||
---
 | 
			
		||||
 drivers/net/phy/phylink.c | 24 ++++++++++++++++--------
 | 
			
		||||
 1 file changed, 16 insertions(+), 8 deletions(-)
 | 
			
		||||
 | 
			
		||||
--- a/drivers/net/phy/phylink.c
 | 
			
		||||
+++ b/drivers/net/phy/phylink.c
 | 
			
		||||
@@ -1210,7 +1210,13 @@ int phylink_ethtool_ksettings_set(struct
 | 
			
		||||
 	pl->link_config.duplex = our_kset.base.duplex;
 | 
			
		||||
 	pl->link_config.an_enabled = our_kset.base.autoneg != AUTONEG_DISABLE;
 | 
			
		||||
 
 | 
			
		||||
-	if (!test_bit(PHYLINK_DISABLE_STOPPED, &pl->phylink_disable_state)) {
 | 
			
		||||
+	/* If we have a PHY, phylib will call our link state function if the
 | 
			
		||||
+	 * mode has changed, which will trigger a resolve and update the MAC
 | 
			
		||||
+	 * configuration. For a fixed link, this isn't able to change any
 | 
			
		||||
+	 * parameters, which just leaves inband mode.
 | 
			
		||||
+	 */
 | 
			
		||||
+	if (pl->link_an_mode == MLO_AN_INBAND &&
 | 
			
		||||
+	    !test_bit(PHYLINK_DISABLE_STOPPED, &pl->phylink_disable_state)) {
 | 
			
		||||
 		phylink_mac_config(pl, &pl->link_config);
 | 
			
		||||
 		phylink_mac_an_restart(pl);
 | 
			
		||||
 	}
 | 
			
		||||
@@ -1290,14 +1296,16 @@ int phylink_ethtool_set_pauseparam(struc
 | 
			
		||||
 	if (pause->tx_pause)
 | 
			
		||||
 		config->pause |= MLO_PAUSE_TX;
 | 
			
		||||
 
 | 
			
		||||
-	if (!test_bit(PHYLINK_DISABLE_STOPPED, &pl->phylink_disable_state)) {
 | 
			
		||||
+	/* If we have a PHY, phylib will call our link state function if the
 | 
			
		||||
+	 * mode has changed, which will trigger a resolve and update the MAC
 | 
			
		||||
+	 * configuration.
 | 
			
		||||
+	 */
 | 
			
		||||
+	if (pl->phydev) {
 | 
			
		||||
+		phy_set_asym_pause(pl->phydev, pause->rx_pause,
 | 
			
		||||
+				   pause->tx_pause);
 | 
			
		||||
+	} else if (!test_bit(PHYLINK_DISABLE_STOPPED,
 | 
			
		||||
+			     &pl->phylink_disable_state)) {
 | 
			
		||||
 		switch (pl->link_an_mode) {
 | 
			
		||||
-		case MLO_AN_PHY:
 | 
			
		||||
-			/* Silently mark the carrier down, and then trigger a resolve */
 | 
			
		||||
-			netif_carrier_off(pl->netdev);
 | 
			
		||||
-			phylink_run_resolve(pl);
 | 
			
		||||
-			break;
 | 
			
		||||
-
 | 
			
		||||
 		case MLO_AN_FIXED:
 | 
			
		||||
 			/* Should we allow fixed links to change against the config? */
 | 
			
		||||
 			phylink_resolve_flow(pl, config);
 | 
			
		||||
@ -0,0 +1,111 @@
 | 
			
		||||
From 8df5dd55cef48c0769379e04dbc085a899b106d4 Mon Sep 17 00:00:00 2001
 | 
			
		||||
From: Russell King <rmk+kernel@armlinux.org.uk>
 | 
			
		||||
Date: Fri, 8 Mar 2019 14:02:25 +0000
 | 
			
		||||
Subject: [PATCH 640/660] net: sfp: add support for module quirks
 | 
			
		||||
 | 
			
		||||
Add support for applying module quirks to the list of supported
 | 
			
		||||
ethtool link modes.
 | 
			
		||||
 | 
			
		||||
Signed-off-by: Russell King <rmk+kernel@armlinux.org.uk>
 | 
			
		||||
---
 | 
			
		||||
 drivers/net/phy/sfp-bus.c | 54 +++++++++++++++++++++++++++++++++++++++
 | 
			
		||||
 1 file changed, 54 insertions(+)
 | 
			
		||||
 | 
			
		||||
--- a/drivers/net/phy/sfp-bus.c
 | 
			
		||||
+++ b/drivers/net/phy/sfp-bus.c
 | 
			
		||||
@@ -9,6 +9,12 @@
 | 
			
		||||
 
 | 
			
		||||
 #include "sfp.h"
 | 
			
		||||
 
 | 
			
		||||
+struct sfp_quirk {
 | 
			
		||||
+	const char *vendor;
 | 
			
		||||
+	const char *part;
 | 
			
		||||
+	void (*modes)(const struct sfp_eeprom_id *id, unsigned long *modes);
 | 
			
		||||
+};
 | 
			
		||||
+
 | 
			
		||||
 /**
 | 
			
		||||
  * struct sfp_bus - internal representation of a sfp bus
 | 
			
		||||
  */
 | 
			
		||||
@@ -21,6 +27,7 @@ struct sfp_bus {
 | 
			
		||||
 	const struct sfp_socket_ops *socket_ops;
 | 
			
		||||
 	struct device *sfp_dev;
 | 
			
		||||
 	struct sfp *sfp;
 | 
			
		||||
+	const struct sfp_quirk *sfp_quirk;
 | 
			
		||||
 
 | 
			
		||||
 	const struct sfp_upstream_ops *upstream_ops;
 | 
			
		||||
 	void *upstream;
 | 
			
		||||
@@ -30,6 +37,46 @@ struct sfp_bus {
 | 
			
		||||
 	bool started;
 | 
			
		||||
 };
 | 
			
		||||
 
 | 
			
		||||
+static const struct sfp_quirk sfp_quirks[] = {
 | 
			
		||||
+};
 | 
			
		||||
+
 | 
			
		||||
+static size_t sfp_strlen(const char *str, size_t maxlen)
 | 
			
		||||
+{
 | 
			
		||||
+	size_t size, i;
 | 
			
		||||
+
 | 
			
		||||
+	/* Trailing characters should be filled with space chars */
 | 
			
		||||
+	for (i = 0, size = 0; i < maxlen; i++)
 | 
			
		||||
+		if (str[i] != ' ')
 | 
			
		||||
+			size = i + 1;
 | 
			
		||||
+
 | 
			
		||||
+	return size;
 | 
			
		||||
+}
 | 
			
		||||
+
 | 
			
		||||
+static bool sfp_match(const char *qs, const char *str, size_t len)
 | 
			
		||||
+{
 | 
			
		||||
+	if (!qs)
 | 
			
		||||
+		return true;
 | 
			
		||||
+	if (strlen(qs) != len)
 | 
			
		||||
+		return false;
 | 
			
		||||
+	return !strncmp(qs, str, len);
 | 
			
		||||
+}
 | 
			
		||||
+
 | 
			
		||||
+static const struct sfp_quirk *sfp_lookup_quirk(const struct sfp_eeprom_id *id)
 | 
			
		||||
+{
 | 
			
		||||
+	const struct sfp_quirk *q;
 | 
			
		||||
+	unsigned int i;
 | 
			
		||||
+	size_t vs, ps;
 | 
			
		||||
+
 | 
			
		||||
+	vs = sfp_strlen(id->base.vendor_name, ARRAY_SIZE(id->base.vendor_name));
 | 
			
		||||
+	ps = sfp_strlen(id->base.vendor_pn, ARRAY_SIZE(id->base.vendor_pn));
 | 
			
		||||
+
 | 
			
		||||
+	for (i = 0, q = sfp_quirks; i < ARRAY_SIZE(sfp_quirks); i++, q++)
 | 
			
		||||
+		if (sfp_match(q->vendor, id->base.vendor_name, vs) &&
 | 
			
		||||
+		    sfp_match(q->part, id->base.vendor_pn, ps))
 | 
			
		||||
+			return q;
 | 
			
		||||
+
 | 
			
		||||
+	return NULL;
 | 
			
		||||
+}
 | 
			
		||||
 /**
 | 
			
		||||
  * sfp_parse_port() - Parse the EEPROM base ID, setting the port type
 | 
			
		||||
  * @bus: a pointer to the &struct sfp_bus structure for the sfp module
 | 
			
		||||
@@ -233,6 +280,9 @@ void sfp_parse_support(struct sfp_bus *b
 | 
			
		||||
 			phylink_set(modes, 1000baseX_Full);
 | 
			
		||||
 	}
 | 
			
		||||
 
 | 
			
		||||
+	if (bus->sfp_quirk)
 | 
			
		||||
+		bus->sfp_quirk->modes(id, modes);
 | 
			
		||||
+
 | 
			
		||||
 	bitmap_or(support, support, modes, __ETHTOOL_LINK_MODE_MASK_NBITS);
 | 
			
		||||
 
 | 
			
		||||
 	phylink_set(support, Autoneg);
 | 
			
		||||
@@ -609,6 +659,8 @@ int sfp_module_insert(struct sfp_bus *bu
 | 
			
		||||
 	const struct sfp_upstream_ops *ops = sfp_get_upstream_ops(bus);
 | 
			
		||||
 	int ret = 0;
 | 
			
		||||
 
 | 
			
		||||
+	bus->sfp_quirk = sfp_lookup_quirk(id);
 | 
			
		||||
+
 | 
			
		||||
 	if (ops && ops->module_insert)
 | 
			
		||||
 		ret = ops->module_insert(bus->upstream, id);
 | 
			
		||||
 
 | 
			
		||||
@@ -622,6 +674,8 @@ void sfp_module_remove(struct sfp_bus *b
 | 
			
		||||
 
 | 
			
		||||
 	if (ops && ops->module_remove)
 | 
			
		||||
 		ops->module_remove(bus->upstream);
 | 
			
		||||
+
 | 
			
		||||
+	bus->sfp_quirk = NULL;
 | 
			
		||||
 }
 | 
			
		||||
 EXPORT_SYMBOL_GPL(sfp_module_remove);
 | 
			
		||||
 
 | 
			
		||||
@ -0,0 +1,52 @@
 | 
			
		||||
From ecaa542cfed078dbc356dadff0bad4b6a8e704a0 Mon Sep 17 00:00:00 2001
 | 
			
		||||
From: Russell King <rmk+kernel@armlinux.org.uk>
 | 
			
		||||
Date: Fri, 17 May 2019 10:14:45 +0100
 | 
			
		||||
Subject: [PATCH 641/660] net: sfp: add some quirks for GPON modules
 | 
			
		||||
 | 
			
		||||
Marc Micalizzi reports that Huawei MA5671A and Alcatel/Lucent G-010S-P
 | 
			
		||||
modules are capable of 2500base-X, but incorrectly report their
 | 
			
		||||
capabilities in the EEPROM.  It seems rather common that GPON modules
 | 
			
		||||
mis-report.
 | 
			
		||||
 | 
			
		||||
Let's fix these modules by adding some quirks.
 | 
			
		||||
 | 
			
		||||
Signed-off-by: Russell King <rmk+kernel@armlinux.org.uk>
 | 
			
		||||
---
 | 
			
		||||
 drivers/net/phy/sfp-bus.c | 25 +++++++++++++++++++++++++
 | 
			
		||||
 1 file changed, 25 insertions(+)
 | 
			
		||||
 | 
			
		||||
--- a/drivers/net/phy/sfp-bus.c
 | 
			
		||||
+++ b/drivers/net/phy/sfp-bus.c
 | 
			
		||||
@@ -37,7 +37,32 @@ struct sfp_bus {
 | 
			
		||||
 	bool started;
 | 
			
		||||
 };
 | 
			
		||||
 
 | 
			
		||||
+static void sfp_quirk_2500basex(const struct sfp_eeprom_id *id,
 | 
			
		||||
+				unsigned long *modes)
 | 
			
		||||
+{
 | 
			
		||||
+	phylink_set(modes, 2500baseX_Full);
 | 
			
		||||
+}
 | 
			
		||||
+
 | 
			
		||||
 static const struct sfp_quirk sfp_quirks[] = {
 | 
			
		||||
+	{
 | 
			
		||||
+		// Alcatel Lucent G-010S-P can operate at 2500base-X, but
 | 
			
		||||
+		// incorrectly report 2500MBd NRZ in their EEPROM
 | 
			
		||||
+		.vendor = "ALCATELLUCENT",
 | 
			
		||||
+		.part = "G010SP",
 | 
			
		||||
+		.modes = sfp_quirk_2500basex,
 | 
			
		||||
+	}, {
 | 
			
		||||
+		// Alcatel Lucent G-010S-A can operate at 2500base-X, but
 | 
			
		||||
+		// report 3.2GBd NRZ in their EEPROM
 | 
			
		||||
+		.vendor = "ALCATELLUCENT",
 | 
			
		||||
+		.part = "3FE46541AA",
 | 
			
		||||
+		.modes = sfp_quirk_2500basex,
 | 
			
		||||
+	}, {
 | 
			
		||||
+		// Huawei MA5671A can operate at 2500base-X, but report 1.2GBd
 | 
			
		||||
+		// NRZ in their EEPROM
 | 
			
		||||
+		.vendor = "HUAWEI",
 | 
			
		||||
+		.part = "MA5671A",
 | 
			
		||||
+		.modes = sfp_quirk_2500basex,
 | 
			
		||||
+	},
 | 
			
		||||
 };
 | 
			
		||||
 
 | 
			
		||||
 static size_t sfp_strlen(const char *str, size_t maxlen)
 | 
			
		||||
@ -0,0 +1,225 @@
 | 
			
		||||
From 40e0b3b15f7da92e6b065292b14af7b9bfb1c6e0 Mon Sep 17 00:00:00 2001
 | 
			
		||||
From: Russell King <rmk+kernel@armlinux.org.uk>
 | 
			
		||||
Date: Fri, 13 Sep 2019 23:00:35 +0100
 | 
			
		||||
Subject: [PATCH 642/660] net: sfp: soft status and control support
 | 
			
		||||
 | 
			
		||||
Add support for the soft status and control register, which allows
 | 
			
		||||
TX_FAULT and RX_LOS to be monitored and TX_DISABLE to be set.  We
 | 
			
		||||
make use of this when the board does not support GPIOs for these
 | 
			
		||||
signals.
 | 
			
		||||
 | 
			
		||||
Signed-off-by: Russell King <rmk+kernel@armlinux.org.uk>
 | 
			
		||||
---
 | 
			
		||||
 drivers/net/phy/sfp.c | 110 ++++++++++++++++++++++++++++++++++--------
 | 
			
		||||
 include/linux/sfp.h   |   4 ++
 | 
			
		||||
 2 files changed, 94 insertions(+), 20 deletions(-)
 | 
			
		||||
 | 
			
		||||
--- a/drivers/net/phy/sfp.c
 | 
			
		||||
+++ b/drivers/net/phy/sfp.c
 | 
			
		||||
@@ -199,7 +199,10 @@ struct sfp {
 | 
			
		||||
 	struct gpio_desc *gpio[GPIO_MAX];
 | 
			
		||||
 	int gpio_irq[GPIO_MAX];
 | 
			
		||||
 
 | 
			
		||||
+	bool need_poll;
 | 
			
		||||
+
 | 
			
		||||
 	struct mutex st_mutex;			/* Protects state */
 | 
			
		||||
+	unsigned int state_soft_mask;
 | 
			
		||||
 	unsigned int state;
 | 
			
		||||
 	struct delayed_work poll;
 | 
			
		||||
 	struct delayed_work timeout;
 | 
			
		||||
@@ -393,24 +396,90 @@ static int sfp_i2c_configure(struct sfp
 | 
			
		||||
 }
 | 
			
		||||
 
 | 
			
		||||
 /* Interface */
 | 
			
		||||
-static unsigned int sfp_get_state(struct sfp *sfp)
 | 
			
		||||
+static int sfp_read(struct sfp *sfp, bool a2, u8 addr, void *buf, size_t len)
 | 
			
		||||
 {
 | 
			
		||||
-	return sfp->get_state(sfp);
 | 
			
		||||
+	return sfp->read(sfp, a2, addr, buf, len);
 | 
			
		||||
 }
 | 
			
		||||
 
 | 
			
		||||
-static void sfp_set_state(struct sfp *sfp, unsigned int state)
 | 
			
		||||
+static int sfp_write(struct sfp *sfp, bool a2, u8 addr, void *buf, size_t len)
 | 
			
		||||
 {
 | 
			
		||||
-	sfp->set_state(sfp, state);
 | 
			
		||||
+	return sfp->write(sfp, a2, addr, buf, len);
 | 
			
		||||
 }
 | 
			
		||||
 
 | 
			
		||||
-static int sfp_read(struct sfp *sfp, bool a2, u8 addr, void *buf, size_t len)
 | 
			
		||||
+static unsigned int sfp_soft_get_state(struct sfp *sfp)
 | 
			
		||||
 {
 | 
			
		||||
-	return sfp->read(sfp, a2, addr, buf, len);
 | 
			
		||||
+	unsigned int state = 0;
 | 
			
		||||
+	u8 status;
 | 
			
		||||
+
 | 
			
		||||
+	if (sfp_read(sfp, true, SFP_STATUS, &status, sizeof(status)) ==
 | 
			
		||||
+		     sizeof(status)) {
 | 
			
		||||
+		if (status & SFP_STATUS_RX_LOS)
 | 
			
		||||
+			state |= SFP_F_LOS;
 | 
			
		||||
+		if (status & SFP_STATUS_TX_FAULT)
 | 
			
		||||
+			state |= SFP_F_TX_FAULT;
 | 
			
		||||
+	}
 | 
			
		||||
+
 | 
			
		||||
+	return state & sfp->state_soft_mask;
 | 
			
		||||
 }
 | 
			
		||||
 
 | 
			
		||||
-static int sfp_write(struct sfp *sfp, bool a2, u8 addr, void *buf, size_t len)
 | 
			
		||||
+static void sfp_soft_set_state(struct sfp *sfp, unsigned int state)
 | 
			
		||||
 {
 | 
			
		||||
-	return sfp->write(sfp, a2, addr, buf, len);
 | 
			
		||||
+	u8 status;
 | 
			
		||||
+
 | 
			
		||||
+	if (sfp_read(sfp, true, SFP_STATUS, &status, sizeof(status)) ==
 | 
			
		||||
+		     sizeof(status)) {
 | 
			
		||||
+		if (state & SFP_F_TX_DISABLE)
 | 
			
		||||
+			status |= SFP_STATUS_TX_DISABLE_FORCE;
 | 
			
		||||
+		else
 | 
			
		||||
+			status &= ~SFP_STATUS_TX_DISABLE_FORCE;
 | 
			
		||||
+
 | 
			
		||||
+		sfp_write(sfp, true, SFP_STATUS, &status, sizeof(status));
 | 
			
		||||
+	}
 | 
			
		||||
+}
 | 
			
		||||
+
 | 
			
		||||
+static void sfp_soft_start_poll(struct sfp *sfp)
 | 
			
		||||
+{
 | 
			
		||||
+	const struct sfp_eeprom_id *id = &sfp->id;
 | 
			
		||||
+
 | 
			
		||||
+	sfp->state_soft_mask = 0;
 | 
			
		||||
+	if (id->ext.enhopts & SFP_ENHOPTS_SOFT_TX_DISABLE &&
 | 
			
		||||
+	    !sfp->gpio[GPIO_TX_DISABLE])
 | 
			
		||||
+		sfp->state_soft_mask |= SFP_F_TX_DISABLE;
 | 
			
		||||
+	if (id->ext.enhopts & SFP_ENHOPTS_SOFT_TX_FAULT &&
 | 
			
		||||
+	    !sfp->gpio[GPIO_TX_FAULT])
 | 
			
		||||
+		sfp->state_soft_mask |= SFP_F_TX_FAULT;
 | 
			
		||||
+	if (id->ext.enhopts & SFP_ENHOPTS_SOFT_RX_LOS &&
 | 
			
		||||
+	    !sfp->gpio[GPIO_LOS])
 | 
			
		||||
+		sfp->state_soft_mask |= SFP_F_LOS;
 | 
			
		||||
+
 | 
			
		||||
+	if (sfp->state_soft_mask & (SFP_F_LOS | SFP_F_TX_FAULT) &&
 | 
			
		||||
+	    !sfp->need_poll)
 | 
			
		||||
+		mod_delayed_work(system_wq, &sfp->poll, poll_jiffies);
 | 
			
		||||
+}
 | 
			
		||||
+
 | 
			
		||||
+static void sfp_soft_stop_poll(struct sfp *sfp)
 | 
			
		||||
+{
 | 
			
		||||
+	sfp->state_soft_mask = 0;
 | 
			
		||||
+}
 | 
			
		||||
+
 | 
			
		||||
+static unsigned int sfp_get_state(struct sfp *sfp)
 | 
			
		||||
+{
 | 
			
		||||
+	unsigned int state = sfp->get_state(sfp);
 | 
			
		||||
+
 | 
			
		||||
+	if (state & SFP_F_PRESENT &&
 | 
			
		||||
+	    sfp->state_soft_mask & (SFP_F_LOS | SFP_F_TX_FAULT))
 | 
			
		||||
+		state |= sfp_soft_get_state(sfp);
 | 
			
		||||
+
 | 
			
		||||
+	return state;
 | 
			
		||||
+}
 | 
			
		||||
+
 | 
			
		||||
+static void sfp_set_state(struct sfp *sfp, unsigned int state)
 | 
			
		||||
+{
 | 
			
		||||
+	sfp->set_state(sfp, state);
 | 
			
		||||
+
 | 
			
		||||
+	if (state & SFP_F_PRESENT &&
 | 
			
		||||
+	    sfp->state_soft_mask & SFP_F_TX_DISABLE)
 | 
			
		||||
+		sfp_soft_set_state(sfp, state);
 | 
			
		||||
 }
 | 
			
		||||
 
 | 
			
		||||
 static unsigned int sfp_check(void *buf, size_t len)
 | 
			
		||||
@@ -1342,11 +1411,6 @@ static void sfp_sm_fault(struct sfp *sfp
 | 
			
		||||
 	}
 | 
			
		||||
 }
 | 
			
		||||
 
 | 
			
		||||
-static void sfp_sm_mod_init(struct sfp *sfp)
 | 
			
		||||
-{
 | 
			
		||||
-	sfp_module_tx_enable(sfp);
 | 
			
		||||
-}
 | 
			
		||||
-
 | 
			
		||||
 static void sfp_sm_probe_for_phy(struct sfp *sfp)
 | 
			
		||||
 {
 | 
			
		||||
 	/* Setting the serdes link mode is guesswork: there's no
 | 
			
		||||
@@ -1509,7 +1573,7 @@ static int sfp_sm_mod_probe(struct sfp *
 | 
			
		||||
 		 (int)sizeof(id.ext.datecode), id.ext.datecode);
 | 
			
		||||
 
 | 
			
		||||
 	/* Check whether we support this module */
 | 
			
		||||
-	if (!sfp->type->module_supported(&sfp->id)) {
 | 
			
		||||
+	if (!sfp->type->module_supported(&id)) {
 | 
			
		||||
 		dev_err(sfp->dev,
 | 
			
		||||
 			"module is not supported - phys id 0x%02x 0x%02x\n",
 | 
			
		||||
 			sfp->id.base.phys_id, sfp->id.base.phys_ext_id);
 | 
			
		||||
@@ -1699,6 +1763,7 @@ static void sfp_sm_main(struct sfp *sfp,
 | 
			
		||||
 		if (sfp->mod_phy)
 | 
			
		||||
 			sfp_sm_phy_detach(sfp);
 | 
			
		||||
 		sfp_module_tx_disable(sfp);
 | 
			
		||||
+		sfp_soft_stop_poll(sfp);
 | 
			
		||||
 		sfp_sm_next(sfp, SFP_S_DOWN, 0);
 | 
			
		||||
 		return;
 | 
			
		||||
 	}
 | 
			
		||||
@@ -1710,7 +1775,10 @@ static void sfp_sm_main(struct sfp *sfp,
 | 
			
		||||
 		    sfp->sm_dev_state != SFP_DEV_UP)
 | 
			
		||||
 			break;
 | 
			
		||||
 
 | 
			
		||||
-		sfp_sm_mod_init(sfp);
 | 
			
		||||
+		if (!(sfp->id.ext.diagmon & SFP_DIAGMON_ADDRMODE))
 | 
			
		||||
+			sfp_soft_start_poll(sfp);
 | 
			
		||||
+
 | 
			
		||||
+		sfp_module_tx_enable(sfp);
 | 
			
		||||
 
 | 
			
		||||
 		/* Initialise the fault clearance retries */
 | 
			
		||||
 		sfp->sm_retries = 5;
 | 
			
		||||
@@ -1966,7 +2034,10 @@ static void sfp_poll(struct work_struct
 | 
			
		||||
 	struct sfp *sfp = container_of(work, struct sfp, poll.work);
 | 
			
		||||
 
 | 
			
		||||
 	sfp_check_state(sfp);
 | 
			
		||||
-	mod_delayed_work(system_wq, &sfp->poll, poll_jiffies);
 | 
			
		||||
+
 | 
			
		||||
+	if (sfp->state_soft_mask & (SFP_F_LOS | SFP_F_TX_FAULT) ||
 | 
			
		||||
+	    sfp->need_poll)
 | 
			
		||||
+		mod_delayed_work(system_wq, &sfp->poll, poll_jiffies);
 | 
			
		||||
 }
 | 
			
		||||
 
 | 
			
		||||
 static struct sfp *sfp_alloc(struct device *dev)
 | 
			
		||||
@@ -2010,7 +2081,6 @@ static int sfp_probe(struct platform_dev
 | 
			
		||||
 {
 | 
			
		||||
 	const struct sff_data *sff;
 | 
			
		||||
 	struct sfp *sfp;
 | 
			
		||||
-	bool poll = false;
 | 
			
		||||
 	int err, i;
 | 
			
		||||
 
 | 
			
		||||
 	sfp = sfp_alloc(&pdev->dev);
 | 
			
		||||
@@ -2100,7 +2170,7 @@ static int sfp_probe(struct platform_dev
 | 
			
		||||
 
 | 
			
		||||
 		sfp->gpio_irq[i] = gpiod_to_irq(sfp->gpio[i]);
 | 
			
		||||
 		if (!sfp->gpio_irq[i]) {
 | 
			
		||||
-			poll = true;
 | 
			
		||||
+			sfp->need_poll = true;
 | 
			
		||||
 			continue;
 | 
			
		||||
 		}
 | 
			
		||||
 
 | 
			
		||||
@@ -2112,11 +2182,11 @@ static int sfp_probe(struct platform_dev
 | 
			
		||||
 						dev_name(sfp->dev), sfp);
 | 
			
		||||
 		if (err) {
 | 
			
		||||
 			sfp->gpio_irq[i] = 0;
 | 
			
		||||
-			poll = true;
 | 
			
		||||
+			sfp->need_poll = true;
 | 
			
		||||
 		}
 | 
			
		||||
 	}
 | 
			
		||||
 
 | 
			
		||||
-	if (poll)
 | 
			
		||||
+	if (sfp->need_poll)
 | 
			
		||||
 		mod_delayed_work(system_wq, &sfp->poll, poll_jiffies);
 | 
			
		||||
 
 | 
			
		||||
 	/* We could have an issue in cases no Tx disable pin is available or
 | 
			
		||||
--- a/include/linux/sfp.h
 | 
			
		||||
+++ b/include/linux/sfp.h
 | 
			
		||||
@@ -428,6 +428,10 @@ enum {
 | 
			
		||||
 	SFP_TEC_CUR			= 0x6c,
 | 
			
		||||
 
 | 
			
		||||
 	SFP_STATUS			= 0x6e,
 | 
			
		||||
+	SFP_STATUS_TX_DISABLE		= BIT(7),
 | 
			
		||||
+	SFP_STATUS_TX_DISABLE_FORCE	= BIT(6),
 | 
			
		||||
+	SFP_STATUS_TX_FAULT		= BIT(2),
 | 
			
		||||
+	SFP_STATUS_RX_LOS		= BIT(1),
 | 
			
		||||
 	SFP_ALARM0			= 0x70,
 | 
			
		||||
 	SFP_ALARM0_TEMP_HIGH		= BIT(7),
 | 
			
		||||
 	SFP_ALARM0_TEMP_LOW		= BIT(6),
 | 
			
		||||
		Loading…
	
		Reference in New Issue
	
	Block a user