developer | ec4ebe4 | 2022-04-12 11:17:45 +0800 | [diff] [blame] | 1 | From f9a5a54b59cb904b37bf7409a43635ab195d0214 Mon Sep 17 00:00:00 2001 |
| 2 | From: Russell King <rmk+kernel@armlinux.org.uk> |
| 3 | Date: Tue, 19 Nov 2019 10:13:25 +0000 |
| 4 | Subject: [PATCH 646/660] net: sfp: add module start/stop upstream |
| 5 | notifications |
| 6 | |
| 7 | When dealing with some copper modules, we can't positively know the |
| 8 | module capabilities are until we have probed the PHY. Without the full |
| 9 | capabilities, we may end up failing a module that we could otherwise |
| 10 | drive with a restricted set of capabilities. |
| 11 | |
| 12 | An example of this would be a module with a NBASE-T PHY plugged into |
| 13 | a host that supports phy interface modes 2500BASE-X and SGMII. The |
| 14 | PHY supports 10GBASE-R, 5000BASE-X, 2500BASE-X, SGMII interface modes, |
| 15 | which means a subset of the capabilities are compatible with the host. |
| 16 | |
| 17 | However, reading the module EEPROM leads us to believe that the module |
| 18 | only supports ethtool link mode 10GBASE-T, which is incompatible with |
| 19 | the host - and thus results in the module being rejected. |
| 20 | |
| 21 | This patch adds an extra notification which are triggered after the |
| 22 | SFP module's PHY probe, and a corresponding notification just before |
| 23 | the PHY is removed. |
| 24 | |
| 25 | Signed-off-by: Russell King <rmk+kernel@armlinux.org.uk> |
| 26 | --- |
| 27 | drivers/net/phy/sfp-bus.c | 21 +++++++++++++++++++++ |
| 28 | drivers/net/phy/sfp.c | 8 ++++++++ |
| 29 | drivers/net/phy/sfp.h | 2 ++ |
| 30 | include/linux/sfp.h | 4 ++++ |
| 31 | 4 files changed, 35 insertions(+) |
| 32 | |
| 33 | --- a/drivers/net/phy/sfp-bus.c |
| 34 | +++ b/drivers/net/phy/sfp-bus.c |
| 35 | @@ -717,6 +717,27 @@ void sfp_module_remove(struct sfp_bus *b |
| 36 | } |
| 37 | EXPORT_SYMBOL_GPL(sfp_module_remove); |
| 38 | |
| 39 | +int sfp_module_start(struct sfp_bus *bus) |
| 40 | +{ |
| 41 | + const struct sfp_upstream_ops *ops = sfp_get_upstream_ops(bus); |
| 42 | + int ret = 0; |
| 43 | + |
| 44 | + if (ops && ops->module_start) |
| 45 | + ret = ops->module_start(bus->upstream); |
| 46 | + |
| 47 | + return ret; |
| 48 | +} |
| 49 | +EXPORT_SYMBOL_GPL(sfp_module_start); |
| 50 | + |
| 51 | +void sfp_module_stop(struct sfp_bus *bus) |
| 52 | +{ |
| 53 | + const struct sfp_upstream_ops *ops = sfp_get_upstream_ops(bus); |
| 54 | + |
| 55 | + if (ops && ops->module_stop) |
| 56 | + ops->module_stop(bus->upstream); |
| 57 | +} |
| 58 | +EXPORT_SYMBOL_GPL(sfp_module_stop); |
| 59 | + |
| 60 | static void sfp_socket_clear(struct sfp_bus *bus) |
| 61 | { |
| 62 | bus->sfp_dev = NULL; |
| 63 | --- a/drivers/net/phy/sfp.c |
| 64 | +++ b/drivers/net/phy/sfp.c |
| 65 | @@ -59,6 +59,7 @@ enum { |
| 66 | SFP_DEV_UP, |
| 67 | |
| 68 | SFP_S_DOWN = 0, |
| 69 | + SFP_S_FAIL, |
| 70 | SFP_S_WAIT, |
| 71 | SFP_S_INIT, |
| 72 | SFP_S_INIT_TX_FAULT, |
| 73 | @@ -122,6 +123,7 @@ static const char *event_to_str(unsigned |
| 74 | |
| 75 | static const char * const sm_state_strings[] = { |
| 76 | [SFP_S_DOWN] = "down", |
| 77 | + [SFP_S_FAIL] = "fail", |
| 78 | [SFP_S_WAIT] = "wait", |
| 79 | [SFP_S_INIT] = "init", |
| 80 | [SFP_S_INIT_TX_FAULT] = "init_tx_fault", |
developer | 53810fa | 2022-04-19 10:14:08 +0800 | [diff] [blame] | 81 | @@ -1926,6 +1928,8 @@ static void sfp_sm_main(struct sfp *sfp, |
developer | ec4ebe4 | 2022-04-12 11:17:45 +0800 | [diff] [blame] | 82 | if (sfp->sm_state == SFP_S_LINK_UP && |
| 83 | sfp->sm_dev_state == SFP_DEV_UP) |
| 84 | sfp_sm_link_down(sfp); |
| 85 | + if (sfp->sm_state > SFP_S_INIT) |
| 86 | + sfp_module_stop(sfp->sfp_bus); |
| 87 | if (sfp->mod_phy) |
| 88 | sfp_sm_phy_detach(sfp); |
| 89 | sfp_module_tx_disable(sfp); |
developer | 53810fa | 2022-04-19 10:14:08 +0800 | [diff] [blame] | 90 | @@ -1993,6 +1997,10 @@ static void sfp_sm_main(struct sfp *sfp, |
developer | ec4ebe4 | 2022-04-12 11:17:45 +0800 | [diff] [blame] | 91 | * clear. Probe for the PHY and check the LOS state. |
| 92 | */ |
| 93 | sfp_sm_probe_for_phy(sfp); |
| 94 | + if (sfp_module_start(sfp->sfp_bus)) { |
| 95 | + sfp_sm_next(sfp, SFP_S_FAIL, 0); |
| 96 | + break; |
| 97 | + } |
| 98 | sfp_sm_link_check_los(sfp); |
| 99 | |
| 100 | /* Reset the fault retry count */ |
| 101 | --- a/drivers/net/phy/sfp.h |
| 102 | +++ b/drivers/net/phy/sfp.h |
| 103 | @@ -22,6 +22,8 @@ void sfp_link_up(struct sfp_bus *bus); |
| 104 | void sfp_link_down(struct sfp_bus *bus); |
| 105 | int sfp_module_insert(struct sfp_bus *bus, const struct sfp_eeprom_id *id); |
| 106 | void sfp_module_remove(struct sfp_bus *bus); |
| 107 | +int sfp_module_start(struct sfp_bus *bus); |
| 108 | +void sfp_module_stop(struct sfp_bus *bus); |
| 109 | int sfp_link_configure(struct sfp_bus *bus, const struct sfp_eeprom_id *id); |
| 110 | struct sfp_bus *sfp_register_socket(struct device *dev, struct sfp *sfp, |
| 111 | const struct sfp_socket_ops *ops); |
| 112 | --- a/include/linux/sfp.h |
| 113 | +++ b/include/linux/sfp.h |
| 114 | @@ -507,6 +507,8 @@ struct sfp_bus; |
| 115 | * @module_insert: called after a module has been detected to determine |
| 116 | * whether the module is supported for the upstream device. |
| 117 | * @module_remove: called after the module has been removed. |
| 118 | + * @module_start: called after the PHY probe step |
| 119 | + * @module_stop: called before the PHY is removed |
| 120 | * @link_down: called when the link is non-operational for whatever |
| 121 | * reason. |
| 122 | * @link_up: called when the link is operational. |
| 123 | @@ -520,6 +522,8 @@ struct sfp_upstream_ops { |
| 124 | void (*detach)(void *priv, struct sfp_bus *bus); |
| 125 | int (*module_insert)(void *priv, const struct sfp_eeprom_id *id); |
| 126 | void (*module_remove)(void *priv); |
| 127 | + int (*module_start)(void *priv); |
| 128 | + void (*module_stop)(void *priv); |
| 129 | void (*link_down)(void *priv); |
| 130 | void (*link_up)(void *priv); |
| 131 | int (*connect_phy)(void *priv, struct phy_device *); |