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>
master
Russell King 2019-11-27 11:45:25 +00:00 committed by Jonas Gorski
parent a07638eb24
commit 1c16b574c4
43 changed files with 4575 additions and 0 deletions

View File

@ -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 *));

View File

@ -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 *));

View File

@ -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;

View File

@ -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);

View File

@ -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

View File

@ -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);

View File

@ -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;
}

View File

@ -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);
}

View File

@ -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);

View File

@ -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);

View File

@ -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;

View File

@ -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),

View File

@ -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,

View File

@ -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);

View File

@ -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)

View File

@ -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

View File

@ -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.

View File

@ -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;
}

View File

@ -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),

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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;
}
}

View File

@ -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;

View File

@ -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");
}

View File

@ -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;
}

View File

@ -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:

View File

@ -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:

View File

@ -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:

View File

@ -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);

View File

@ -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:

View File

@ -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)

View File

@ -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;

View File

@ -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])

View File

@ -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;
}

View File

@ -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) {

View File

@ -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);

View File

@ -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)

View File

@ -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)

View File

@ -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);

View File

@ -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);

View File

@ -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);

View File

@ -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)

View File

@ -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),