| From f9a5a54b59cb904b37bf7409a43635ab195d0214 Mon Sep 17 00:00:00 2001 |
| From: Russell King <rmk+kernel@armlinux.org.uk> |
| Date: Tue, 19 Nov 2019 10:13:25 +0000 |
| Subject: [PATCH 646/660] net: sfp: add module start/stop upstream |
| notifications |
| |
| When dealing with some copper modules, we can't positively know the |
| module capabilities are until we have probed the PHY. Without the full |
| capabilities, we may end up failing a module that we could otherwise |
| drive with a restricted set of capabilities. |
| |
| An example of this would be a module with a NBASE-T PHY plugged into |
| a host that supports phy interface modes 2500BASE-X and SGMII. The |
| PHY supports 10GBASE-R, 5000BASE-X, 2500BASE-X, SGMII interface modes, |
| which means a subset of the capabilities are compatible with the host. |
| |
| However, reading the module EEPROM leads us to believe that the module |
| only supports ethtool link mode 10GBASE-T, which is incompatible with |
| the host - and thus results in the module being rejected. |
| |
| This patch adds an extra notification which are triggered after the |
| SFP module's PHY probe, and a corresponding notification just before |
| the PHY is removed. |
| |
| Signed-off-by: Russell King <rmk+kernel@armlinux.org.uk> |
| --- |
| drivers/net/phy/sfp-bus.c | 21 +++++++++++++++++++++ |
| drivers/net/phy/sfp.c | 8 ++++++++ |
| drivers/net/phy/sfp.h | 2 ++ |
| include/linux/sfp.h | 4 ++++ |
| 4 files changed, 35 insertions(+) |
| |
| --- a/drivers/net/phy/sfp-bus.c |
| +++ b/drivers/net/phy/sfp-bus.c |
| @@ -717,6 +717,27 @@ void sfp_module_remove(struct sfp_bus *b |
| } |
| EXPORT_SYMBOL_GPL(sfp_module_remove); |
| |
| +int sfp_module_start(struct sfp_bus *bus) |
| +{ |
| + const struct sfp_upstream_ops *ops = sfp_get_upstream_ops(bus); |
| + int ret = 0; |
| + |
| + if (ops && ops->module_start) |
| + ret = ops->module_start(bus->upstream); |
| + |
| + return ret; |
| +} |
| +EXPORT_SYMBOL_GPL(sfp_module_start); |
| + |
| +void sfp_module_stop(struct sfp_bus *bus) |
| +{ |
| + const struct sfp_upstream_ops *ops = sfp_get_upstream_ops(bus); |
| + |
| + if (ops && ops->module_stop) |
| + ops->module_stop(bus->upstream); |
| +} |
| +EXPORT_SYMBOL_GPL(sfp_module_stop); |
| + |
| static void sfp_socket_clear(struct sfp_bus *bus) |
| { |
| bus->sfp_dev = NULL; |
| --- a/drivers/net/phy/sfp.c |
| +++ b/drivers/net/phy/sfp.c |
| @@ -59,6 +59,7 @@ enum { |
| SFP_DEV_UP, |
| |
| SFP_S_DOWN = 0, |
| + SFP_S_FAIL, |
| SFP_S_WAIT, |
| SFP_S_INIT, |
| SFP_S_INIT_TX_FAULT, |
| @@ -122,6 +123,7 @@ static const char *event_to_str(unsigned |
| |
| static const char * const sm_state_strings[] = { |
| [SFP_S_DOWN] = "down", |
| + [SFP_S_FAIL] = "fail", |
| [SFP_S_WAIT] = "wait", |
| [SFP_S_INIT] = "init", |
| [SFP_S_INIT_TX_FAULT] = "init_tx_fault", |
| @@ -1926,6 +1928,8 @@ static void sfp_sm_main(struct sfp *sfp, |
| if (sfp->sm_state == SFP_S_LINK_UP && |
| sfp->sm_dev_state == SFP_DEV_UP) |
| sfp_sm_link_down(sfp); |
| + if (sfp->sm_state > SFP_S_INIT) |
| + sfp_module_stop(sfp->sfp_bus); |
| if (sfp->mod_phy) |
| sfp_sm_phy_detach(sfp); |
| sfp_module_tx_disable(sfp); |
| @@ -1993,6 +1997,10 @@ static void sfp_sm_main(struct sfp *sfp, |
| * clear. Probe for the PHY and check the LOS state. |
| */ |
| sfp_sm_probe_for_phy(sfp); |
| + if (sfp_module_start(sfp->sfp_bus)) { |
| + sfp_sm_next(sfp, SFP_S_FAIL, 0); |
| + break; |
| + } |
| sfp_sm_link_check_los(sfp); |
| |
| /* Reset the fault retry count */ |
| --- a/drivers/net/phy/sfp.h |
| +++ b/drivers/net/phy/sfp.h |
| @@ -22,6 +22,8 @@ void sfp_link_up(struct sfp_bus *bus); |
| void sfp_link_down(struct sfp_bus *bus); |
| int sfp_module_insert(struct sfp_bus *bus, const struct sfp_eeprom_id *id); |
| void sfp_module_remove(struct sfp_bus *bus); |
| +int sfp_module_start(struct sfp_bus *bus); |
| +void sfp_module_stop(struct sfp_bus *bus); |
| int sfp_link_configure(struct sfp_bus *bus, const struct sfp_eeprom_id *id); |
| struct sfp_bus *sfp_register_socket(struct device *dev, struct sfp *sfp, |
| const struct sfp_socket_ops *ops); |
| --- a/include/linux/sfp.h |
| +++ b/include/linux/sfp.h |
| @@ -507,6 +507,8 @@ struct sfp_bus; |
| * @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. |
| + * @module_start: called after the PHY probe step |
| + * @module_stop: called before the PHY is removed |
| * @link_down: called when the link is non-operational for whatever |
| * reason. |
| * @link_up: called when the link is operational. |
| @@ -520,6 +522,8 @@ struct sfp_upstream_ops { |
| void (*detach)(void *priv, struct sfp_bus *bus); |
| int (*module_insert)(void *priv, const struct sfp_eeprom_id *id); |
| void (*module_remove)(void *priv); |
| + int (*module_start)(void *priv); |
| + void (*module_stop)(void *priv); |
| void (*link_down)(void *priv); |
| void (*link_up)(void *priv); |
| int (*connect_phy)(void *priv, struct phy_device *); |