b.liu | e958203 | 2025-04-17 19:18:16 +0800 | [diff] [blame] | 1 | From 52c956003a9d5bcae1f445f9dfd42b624adb6e87 Mon Sep 17 00:00:00 2001 |
| 2 | From: Russell King <rmk+kernel@armlinux.org.uk> |
| 3 | Date: Wed, 11 Dec 2019 10:56:45 +0000 |
| 4 | Subject: [PATCH] net: phylink: delay MAC configuration for copper SFP modules |
| 5 | |
| 6 | Knowing whether we need to delay the MAC configuration because a module |
| 7 | may have a PHY is useful to phylink to allow NBASE-T modules to work on |
| 8 | systems supporting no more than 2.5G speeds. |
| 9 | |
| 10 | This commit allows us to delay such configuration until after the PHY |
| 11 | has been probed by recording the parsed capabilities, and if the module |
| 12 | may have a PHY, doing no more until the module_start() notification is |
| 13 | called. At that point, we either have a PHY, or we don't. |
| 14 | |
| 15 | We move the PHY-based setup a little later, and use the PHYs support |
| 16 | capabilities rather than the EEPROM parsed capabilities to determine |
| 17 | whether we can support the PHY. |
| 18 | |
| 19 | Reviewed-by: Andrew Lunn <andrew@lunn.ch> |
| 20 | Signed-off-by: Russell King <rmk+kernel@armlinux.org.uk> |
| 21 | Signed-off-by: David S. Miller <davem@davemloft.net> |
| 22 | --- |
| 23 | drivers/net/phy/phylink.c | 53 +++++++++++++++++++++++++++++++-------- |
| 24 | drivers/net/phy/sfp-bus.c | 28 +++++++++++++++++++++ |
| 25 | include/linux/sfp.h | 7 ++++++ |
| 26 | 3 files changed, 78 insertions(+), 10 deletions(-) |
| 27 | |
| 28 | --- a/drivers/net/phy/phylink.c |
| 29 | +++ b/drivers/net/phy/phylink.c |
| 30 | @@ -72,6 +72,9 @@ struct phylink { |
| 31 | bool mac_link_dropped; |
| 32 | |
| 33 | struct sfp_bus *sfp_bus; |
| 34 | + bool sfp_may_have_phy; |
| 35 | + __ETHTOOL_DECLARE_LINK_MODE_MASK(sfp_support); |
| 36 | + u8 sfp_port; |
| 37 | }; |
| 38 | |
| 39 | #define phylink_printk(level, pl, fmt, ...) \ |
| 40 | @@ -1688,7 +1691,7 @@ static void phylink_sfp_detach(void *ups |
| 41 | pl->netdev->sfp_bus = NULL; |
| 42 | } |
| 43 | |
| 44 | -static int phylink_sfp_config(struct phylink *pl, u8 mode, u8 port, |
| 45 | +static int phylink_sfp_config(struct phylink *pl, u8 mode, |
| 46 | const unsigned long *supported, |
| 47 | const unsigned long *advertising) |
| 48 | { |
| 49 | @@ -1762,7 +1765,7 @@ static int phylink_sfp_config(struct phy |
| 50 | phy_modes(config.interface)); |
| 51 | } |
| 52 | |
| 53 | - pl->link_port = port; |
| 54 | + pl->link_port = pl->sfp_port; |
| 55 | |
| 56 | if (changed && !test_bit(PHYLINK_DISABLE_STOPPED, |
| 57 | &pl->phylink_disable_state)) |
| 58 | @@ -1775,15 +1778,20 @@ static int phylink_sfp_module_insert(voi |
| 59 | const struct sfp_eeprom_id *id) |
| 60 | { |
| 61 | struct phylink *pl = upstream; |
| 62 | - __ETHTOOL_DECLARE_LINK_MODE_MASK(support) = { 0, }; |
| 63 | - u8 port; |
| 64 | + unsigned long *support = pl->sfp_support; |
| 65 | |
| 66 | ASSERT_RTNL(); |
| 67 | |
| 68 | + linkmode_zero(support); |
| 69 | sfp_parse_support(pl->sfp_bus, id, support); |
| 70 | - port = sfp_parse_port(pl->sfp_bus, id, support); |
| 71 | + pl->sfp_port = sfp_parse_port(pl->sfp_bus, id, support); |
| 72 | |
| 73 | - return phylink_sfp_config(pl, MLO_AN_INBAND, port, support, support); |
| 74 | + /* If this module may have a PHY connecting later, defer until later */ |
| 75 | + pl->sfp_may_have_phy = sfp_may_have_phy(pl->sfp_bus, id); |
| 76 | + if (pl->sfp_may_have_phy) |
| 77 | + return 0; |
| 78 | + |
| 79 | + return phylink_sfp_config(pl, MLO_AN_INBAND, support, support); |
| 80 | } |
| 81 | |
| 82 | static int phylink_sfp_module_start(void *upstream) |
| 83 | @@ -1791,10 +1799,19 @@ static int phylink_sfp_module_start(void |
| 84 | struct phylink *pl = upstream; |
| 85 | |
| 86 | /* If this SFP module has a PHY, start the PHY now. */ |
| 87 | - if (pl->phydev) |
| 88 | + if (pl->phydev) { |
| 89 | phy_start(pl->phydev); |
| 90 | - |
| 91 | - return 0; |
| 92 | + return 0; |
| 93 | + } |
| 94 | + |
| 95 | + /* If the module may have a PHY but we didn't detect one we |
| 96 | + * need to configure the MAC here. |
| 97 | + */ |
| 98 | + if (!pl->sfp_may_have_phy) |
| 99 | + return 0; |
| 100 | + |
| 101 | + return phylink_sfp_config(pl, MLO_AN_INBAND, |
| 102 | + pl->sfp_support, pl->sfp_support); |
| 103 | } |
| 104 | |
| 105 | static void phylink_sfp_module_stop(void *upstream) |
| 106 | @@ -1828,10 +1845,26 @@ static void phylink_sfp_link_up(void *up |
| 107 | static int phylink_sfp_connect_phy(void *upstream, struct phy_device *phy) |
| 108 | { |
| 109 | struct phylink *pl = upstream; |
| 110 | - phy_interface_t interface = pl->link_config.interface; |
| 111 | + phy_interface_t interface; |
| 112 | int ret; |
| 113 | |
| 114 | - ret = phylink_attach_phy(pl, phy, pl->link_config.interface); |
| 115 | + /* |
| 116 | + * This is the new way of dealing with flow control for PHYs, |
| 117 | + * as described by Timur Tabi in commit 529ed1275263 ("net: phy: |
| 118 | + * phy drivers should not set SUPPORTED_[Asym_]Pause") except |
| 119 | + * using our validate call to the MAC, we rely upon the MAC |
| 120 | + * clearing the bits from both supported and advertising fields. |
| 121 | + */ |
| 122 | + phy_support_asym_pause(phy); |
| 123 | + |
| 124 | + /* Do the initial configuration */ |
| 125 | + ret = phylink_sfp_config(pl, MLO_AN_INBAND, phy->supported, |
| 126 | + phy->advertising); |
| 127 | + if (ret < 0) |
| 128 | + return ret; |
| 129 | + |
| 130 | + interface = pl->link_config.interface; |
| 131 | + ret = phylink_attach_phy(pl, phy, interface); |
| 132 | if (ret < 0) |
| 133 | return ret; |
| 134 | |
| 135 | --- a/drivers/net/phy/sfp-bus.c |
| 136 | +++ b/drivers/net/phy/sfp-bus.c |
| 137 | @@ -103,6 +103,7 @@ static const struct sfp_quirk *sfp_looku |
| 138 | |
| 139 | return NULL; |
| 140 | } |
| 141 | + |
| 142 | /** |
| 143 | * sfp_parse_port() - Parse the EEPROM base ID, setting the port type |
| 144 | * @bus: a pointer to the &struct sfp_bus structure for the sfp module |
| 145 | @@ -179,6 +180,33 @@ int sfp_parse_port(struct sfp_bus *bus, |
| 146 | EXPORT_SYMBOL_GPL(sfp_parse_port); |
| 147 | |
| 148 | /** |
| 149 | + * sfp_may_have_phy() - indicate whether the module may have a PHY |
| 150 | + * @bus: a pointer to the &struct sfp_bus structure for the sfp module |
| 151 | + * @id: a pointer to the module's &struct sfp_eeprom_id |
| 152 | + * |
| 153 | + * Parse the EEPROM identification given in @id, and return whether |
| 154 | + * this module may have a PHY. |
| 155 | + */ |
| 156 | +bool sfp_may_have_phy(struct sfp_bus *bus, const struct sfp_eeprom_id *id) |
| 157 | +{ |
| 158 | + if (id->base.e1000_base_t) |
| 159 | + return true; |
| 160 | + |
| 161 | + if (id->base.phys_id != SFF8024_ID_DWDM_SFP) { |
| 162 | + switch (id->base.extended_cc) { |
| 163 | + case SFF8024_ECC_10GBASE_T_SFI: |
| 164 | + case SFF8024_ECC_10GBASE_T_SR: |
| 165 | + case SFF8024_ECC_5GBASE_T: |
| 166 | + case SFF8024_ECC_2_5GBASE_T: |
| 167 | + return true; |
| 168 | + } |
| 169 | + } |
| 170 | + |
| 171 | + return false; |
| 172 | +} |
| 173 | +EXPORT_SYMBOL_GPL(sfp_may_have_phy); |
| 174 | + |
| 175 | +/** |
| 176 | * sfp_parse_support() - Parse the eeprom id for supported link modes |
| 177 | * @bus: a pointer to the &struct sfp_bus structure for the sfp module |
| 178 | * @id: a pointer to the module's &struct sfp_eeprom_id |
| 179 | --- a/include/linux/sfp.h |
| 180 | +++ b/include/linux/sfp.h |
| 181 | @@ -533,6 +533,7 @@ struct sfp_upstream_ops { |
| 182 | #if IS_ENABLED(CONFIG_SFP) |
| 183 | int sfp_parse_port(struct sfp_bus *bus, const struct sfp_eeprom_id *id, |
| 184 | unsigned long *support); |
| 185 | +bool sfp_may_have_phy(struct sfp_bus *bus, const struct sfp_eeprom_id *id); |
| 186 | void sfp_parse_support(struct sfp_bus *bus, const struct sfp_eeprom_id *id, |
| 187 | unsigned long *support); |
| 188 | phy_interface_t sfp_select_interface(struct sfp_bus *bus, |
| 189 | @@ -556,6 +557,12 @@ static inline int sfp_parse_port(struct |
| 190 | return PORT_OTHER; |
| 191 | } |
| 192 | |
| 193 | +static inline bool sfp_may_have_phy(struct sfp_bus *bus, |
| 194 | + const struct sfp_eeprom_id *id) |
| 195 | +{ |
| 196 | + return false; |
| 197 | +} |
| 198 | + |
| 199 | static inline void sfp_parse_support(struct sfp_bus *bus, |
| 200 | const struct sfp_eeprom_id *id, |
| 201 | unsigned long *support) |