From 418c1214741cf3ac5e420382a85c4f8a40586024 Mon Sep 17 00:00:00 2001 From: "Russell King (Oracle)" Date: Wed, 17 May 2023 11:37:47 +0100 Subject: [PATCH 1/7] net: sfp: add helper to modify signal states There are a couple of locations in the code where we modify sfp->state, and then call sfp_set_state(, sfp->state) to update the outputs/soft state to control the module. Provide a helper which takes a mask and new state so that this is encapsulated in one location. Reviewed-by: Simon Horman Signed-off-by: Russell King (Oracle) Signed-off-by: Jakub Kicinski --- drivers/net/phy/sfp.c | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/drivers/net/phy/sfp.c b/drivers/net/phy/sfp.c index 89636dc71e483..c97a87393fdb2 100644 --- a/drivers/net/phy/sfp.c +++ b/drivers/net/phy/sfp.c @@ -734,6 +734,12 @@ static void sfp_set_state(struct sfp *sfp, unsigned int state) sfp_soft_set_state(sfp, state); } +static void sfp_mod_state(struct sfp *sfp, unsigned int mask, unsigned int set) +{ + sfp->state = (sfp->state & ~mask) | set; + sfp_set_state(sfp, sfp->state); +} + static unsigned int sfp_check(void *buf, size_t len) { u8 *p, check; @@ -1537,16 +1543,14 @@ static void sfp_module_tx_disable(struct sfp *sfp) { dev_dbg(sfp->dev, "tx disable %u -> %u\n", sfp->state & SFP_F_TX_DISABLE ? 1 : 0, 1); - sfp->state |= SFP_F_TX_DISABLE; - sfp_set_state(sfp, sfp->state); + sfp_mod_state(sfp, SFP_F_TX_DISABLE, SFP_F_TX_DISABLE); } static void sfp_module_tx_enable(struct sfp *sfp) { dev_dbg(sfp->dev, "tx disable %u -> %u\n", sfp->state & SFP_F_TX_DISABLE ? 1 : 0, 0); - sfp->state &= ~SFP_F_TX_DISABLE; - sfp_set_state(sfp, sfp->state); + sfp_mod_state(sfp, SFP_F_TX_DISABLE, 0); } #if IS_ENABLED(CONFIG_DEBUG_FS) From d47e5a430dfd8b15739a118e4a2add5fa90347fd Mon Sep 17 00:00:00 2001 From: "Russell King (Oracle)" Date: Wed, 17 May 2023 11:37:52 +0100 Subject: [PATCH 2/7] net: sfp: move rtnl lock to cover reading state As preparation to moving st_mutex inside rtnl_lock, we need to first move the rtnl lock to cover reading the state. Reviewed-by: Simon Horman Signed-off-by: Russell King (Oracle) Signed-off-by: Jakub Kicinski --- drivers/net/phy/sfp.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/phy/sfp.c b/drivers/net/phy/sfp.c index c97a87393fdb2..3fc703e4dd215 100644 --- a/drivers/net/phy/sfp.c +++ b/drivers/net/phy/sfp.c @@ -2601,6 +2601,7 @@ static void sfp_check_state(struct sfp *sfp) unsigned int state, i, changed; mutex_lock(&sfp->st_mutex); + rtnl_lock(); state = sfp_get_state(sfp); changed = state ^ sfp->state; if (sfp->tx_fault_ignore) @@ -2616,7 +2617,6 @@ static void sfp_check_state(struct sfp *sfp) state |= sfp->state & (SFP_F_TX_DISABLE | SFP_F_RATE_SELECT); sfp->state = state; - rtnl_lock(); if (changed & SFP_F_PRESENT) sfp_sm_event(sfp, state & SFP_F_PRESENT ? SFP_E_INSERT : SFP_E_REMOVE); From a9fe964e7aaeb3fc06a91c21269d0ac8b5afcea8 Mon Sep 17 00:00:00 2001 From: "Russell King (Oracle)" Date: Wed, 17 May 2023 11:37:57 +0100 Subject: [PATCH 3/7] net: sfp: swap order of rtnl and st_mutex locks Swap the order of the rtnl and st_mutex locks - st_mutex is now nested beneath rtnl lock instead of rtnl being beneath st_mutex. This will allow us to hold st_mutex only while manipulating the module's hardware or software control state. Reviewed-by: Simon Horman Signed-off-by: Russell King (Oracle) Signed-off-by: Jakub Kicinski --- drivers/net/phy/sfp.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/net/phy/sfp.c b/drivers/net/phy/sfp.c index 3fc703e4dd215..ffb6c37dac967 100644 --- a/drivers/net/phy/sfp.c +++ b/drivers/net/phy/sfp.c @@ -2600,8 +2600,8 @@ static void sfp_check_state(struct sfp *sfp) { unsigned int state, i, changed; - mutex_lock(&sfp->st_mutex); rtnl_lock(); + mutex_lock(&sfp->st_mutex); state = sfp_get_state(sfp); changed = state ^ sfp->state; if (sfp->tx_fault_ignore) @@ -2628,8 +2628,8 @@ static void sfp_check_state(struct sfp *sfp) if (changed & SFP_F_LOS) sfp_sm_event(sfp, state & SFP_F_LOS ? SFP_E_LOS_HIGH : SFP_E_LOS_LOW); - rtnl_unlock(); mutex_unlock(&sfp->st_mutex); + rtnl_unlock(); } static irqreturn_t sfp_irq(int irq, void *data) From 97a492050aa5e15507fd7b8774e7adaf8d6e4bb5 Mon Sep 17 00:00:00 2001 From: "Russell King (Oracle)" Date: Wed, 17 May 2023 11:38:02 +0100 Subject: [PATCH 4/7] net: sfp: move sm_mutex into sfp_check_state() Provide an unlocked version of sfp_sm_event() which can be used by sfp_check_state() to avoid having to keep re-taking the lock if several signals have changed state. Reviewed-by: Simon Horman Signed-off-by: Russell King (Oracle) Signed-off-by: Jakub Kicinski --- drivers/net/phy/sfp.c | 23 ++++++++++++++--------- 1 file changed, 14 insertions(+), 9 deletions(-) diff --git a/drivers/net/phy/sfp.c b/drivers/net/phy/sfp.c index ffb6c37dac967..e5cd36e3a421e 100644 --- a/drivers/net/phy/sfp.c +++ b/drivers/net/phy/sfp.c @@ -2456,10 +2456,8 @@ static void sfp_sm_main(struct sfp *sfp, unsigned int event) } } -static void sfp_sm_event(struct sfp *sfp, unsigned int event) +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), @@ -2474,7 +2472,12 @@ static void sfp_sm_event(struct sfp *sfp, unsigned int event) mod_state_to_str(sfp->sm_mod_state), dev_state_to_str(sfp->sm_dev_state), sm_state_to_str(sfp->sm_state)); +} +static void sfp_sm_event(struct sfp *sfp, unsigned int event) +{ + mutex_lock(&sfp->sm_mutex); + __sfp_sm_event(sfp, event); mutex_unlock(&sfp->sm_mutex); } @@ -2617,17 +2620,19 @@ static void sfp_check_state(struct sfp *sfp) state |= sfp->state & (SFP_F_TX_DISABLE | SFP_F_RATE_SELECT); sfp->state = state; + mutex_lock(&sfp->sm_mutex); if (changed & SFP_F_PRESENT) - sfp_sm_event(sfp, state & SFP_F_PRESENT ? - SFP_E_INSERT : SFP_E_REMOVE); + __sfp_sm_event(sfp, state & SFP_F_PRESENT ? + SFP_E_INSERT : SFP_E_REMOVE); if (changed & SFP_F_TX_FAULT) - sfp_sm_event(sfp, state & SFP_F_TX_FAULT ? - SFP_E_TX_FAULT : SFP_E_TX_CLEAR); + __sfp_sm_event(sfp, state & SFP_F_TX_FAULT ? + SFP_E_TX_FAULT : SFP_E_TX_CLEAR); if (changed & SFP_F_LOS) - sfp_sm_event(sfp, state & SFP_F_LOS ? - SFP_E_LOS_HIGH : SFP_E_LOS_LOW); + __sfp_sm_event(sfp, state & SFP_F_LOS ? + SFP_E_LOS_HIGH : SFP_E_LOS_LOW); + mutex_unlock(&sfp->sm_mutex); mutex_unlock(&sfp->st_mutex); rtnl_unlock(); } From 1974fd3bf0f04589dea1a4a76273bbc8fd5760f6 Mon Sep 17 00:00:00 2001 From: "Russell King (Oracle)" Date: Wed, 17 May 2023 11:38:07 +0100 Subject: [PATCH 5/7] net: sfp: change st_mutex locking Change st_mutex's use within SFP such that it only protects the various state members, as it was originally supposed to, and isn't held while making various calls outside the driver. Reviewed-by: Simon Horman Signed-off-by: Russell King (Oracle) Signed-off-by: Jakub Kicinski --- drivers/net/phy/sfp.c | 56 ++++++++++++++++++++++++++++++++----------- 1 file changed, 42 insertions(+), 14 deletions(-) diff --git a/drivers/net/phy/sfp.c b/drivers/net/phy/sfp.c index e5cd36e3a421e..bf7dac9977e1b 100644 --- a/drivers/net/phy/sfp.c +++ b/drivers/net/phy/sfp.c @@ -242,10 +242,17 @@ struct sfp { bool need_poll; + /* Access rules: + * state_hw_drive: st_mutex held + * state_hw_mask: st_mutex held + * state_soft_mask: st_mutex held + * state: st_mutex held unless reading input bits + */ struct mutex st_mutex; /* Protects state */ unsigned int state_hw_mask; unsigned int state_soft_mask; unsigned int state; + struct delayed_work poll; struct delayed_work timeout; struct mutex sm_mutex; /* Protects state machine */ @@ -692,7 +699,6 @@ static void sfp_soft_start_poll(struct sfp *sfp) const struct sfp_eeprom_id *id = &sfp->id; unsigned int mask = 0; - sfp->state_soft_mask = 0; if (id->ext.enhopts & SFP_ENHOPTS_SOFT_TX_DISABLE) mask |= SFP_F_TX_DISABLE; if (id->ext.enhopts & SFP_ENHOPTS_SOFT_TX_FAULT) @@ -700,19 +706,26 @@ static void sfp_soft_start_poll(struct sfp *sfp) if (id->ext.enhopts & SFP_ENHOPTS_SOFT_RX_LOS) mask |= SFP_F_LOS; + mutex_lock(&sfp->st_mutex); // Poll the soft state for hardware pins we want to ignore sfp->state_soft_mask = ~sfp->state_hw_mask & mask; if (sfp->state_soft_mask & (SFP_F_LOS | SFP_F_TX_FAULT) && !sfp->need_poll) mod_delayed_work(system_wq, &sfp->poll, poll_jiffies); + mutex_unlock(&sfp->st_mutex); } static void sfp_soft_stop_poll(struct sfp *sfp) { + mutex_lock(&sfp->st_mutex); sfp->state_soft_mask = 0; + mutex_unlock(&sfp->st_mutex); } +/* sfp_get_state() - must be called with st_mutex held, or in the + * initialisation path. + */ static unsigned int sfp_get_state(struct sfp *sfp) { unsigned int soft = sfp->state_soft_mask & (SFP_F_LOS | SFP_F_TX_FAULT); @@ -725,6 +738,9 @@ static unsigned int sfp_get_state(struct sfp *sfp) return state; } +/* sfp_set_state() - must be called with st_mutex held, or in the + * initialisation path. + */ static void sfp_set_state(struct sfp *sfp, unsigned int state) { sfp->set_state(sfp, state); @@ -736,8 +752,10 @@ static void sfp_set_state(struct sfp *sfp, unsigned int state) static void sfp_mod_state(struct sfp *sfp, unsigned int mask, unsigned int set) { + mutex_lock(&sfp->st_mutex); sfp->state = (sfp->state & ~mask) | set; sfp_set_state(sfp, sfp->state); + mutex_unlock(&sfp->st_mutex); } static unsigned int sfp_check(void *buf, size_t len) @@ -1603,16 +1621,18 @@ static void sfp_debugfs_exit(struct sfp *sfp) static void sfp_module_tx_fault_reset(struct sfp *sfp) { - unsigned int state = sfp->state; - - if (state & SFP_F_TX_DISABLE) - return; + unsigned int state; - sfp_set_state(sfp, state | SFP_F_TX_DISABLE); + mutex_lock(&sfp->st_mutex); + state = sfp->state; + if (!(state & SFP_F_TX_DISABLE)) { + sfp_set_state(sfp, state | SFP_F_TX_DISABLE); - udelay(T_RESET_US); + udelay(T_RESET_US); - sfp_set_state(sfp, state); + sfp_set_state(sfp, state); + } + mutex_unlock(&sfp->st_mutex); } /* SFP state machine */ @@ -1957,6 +1977,7 @@ static int sfp_sm_mod_probe(struct sfp *sfp, bool report) /* SFP module inserted - read I2C data */ struct sfp_eeprom_id id; bool cotsworks_sfbg; + unsigned int mask; bool cotsworks; u8 check; int ret; @@ -2096,14 +2117,13 @@ static int sfp_sm_mod_probe(struct sfp *sfp, bool report) if (ret < 0) return ret; - /* Initialise state bits to use from hardware */ - sfp->state_hw_mask = SFP_F_PRESENT; + mask = SFP_F_PRESENT; if (sfp->gpio[GPIO_TX_DISABLE]) - sfp->state_hw_mask |= SFP_F_TX_DISABLE; + mask |= SFP_F_TX_DISABLE; if (sfp->gpio[GPIO_TX_FAULT]) - sfp->state_hw_mask |= SFP_F_TX_FAULT; + mask |= SFP_F_TX_FAULT; if (sfp->gpio[GPIO_LOS]) - sfp->state_hw_mask |= SFP_F_LOS; + mask |= SFP_F_LOS; sfp->module_t_start_up = T_START_UP; sfp->module_t_wait = T_WAIT; @@ -2121,8 +2141,14 @@ static int sfp_sm_mod_probe(struct sfp *sfp, bool report) sfp->mdio_protocol = MDIO_I2C_NONE; sfp->quirk = sfp_lookup_quirk(&id); + + mutex_lock(&sfp->st_mutex); + /* Initialise state bits to use from hardware */ + sfp->state_hw_mask = mask; + if (sfp->quirk && sfp->quirk->fixup) sfp->quirk->fixup(sfp); + mutex_unlock(&sfp->st_mutex); return 0; } @@ -2619,6 +2645,7 @@ static void sfp_check_state(struct sfp *sfp) state |= sfp->state & (SFP_F_TX_DISABLE | SFP_F_RATE_SELECT); sfp->state = state; + mutex_unlock(&sfp->st_mutex); mutex_lock(&sfp->sm_mutex); if (changed & SFP_F_PRESENT) @@ -2633,7 +2660,6 @@ static void sfp_check_state(struct sfp *sfp) __sfp_sm_event(sfp, state & SFP_F_LOS ? SFP_E_LOS_HIGH : SFP_E_LOS_LOW); mutex_unlock(&sfp->sm_mutex); - mutex_unlock(&sfp->st_mutex); rtnl_unlock(); } @@ -2652,6 +2678,8 @@ static void sfp_poll(struct work_struct *work) sfp_check_state(sfp); + // st_mutex doesn't need to be held here for state_soft_mask, + // it's unimportant if we race while reading this. if (sfp->state_soft_mask & (SFP_F_LOS | SFP_F_TX_FAULT) || sfp->need_poll) mod_delayed_work(system_wq, &sfp->poll, poll_jiffies); From dc18582211b34bce8250ddf3cac2a2230e192120 Mon Sep 17 00:00:00 2001 From: "Russell King (Oracle)" Date: Wed, 17 May 2023 11:38:12 +0100 Subject: [PATCH 6/7] net: sfp: add support for setting signalling rate Add support to the SFP layer to allow phylink to set the signalling rate for a SFP module. The rate given will be in units of kilo-baud (1000 baud). Reviewed-by: Simon Horman Signed-off-by: Russell King (Oracle) Signed-off-by: Jakub Kicinski --- drivers/net/phy/phylink.c | 24 ++++++++++++++++++++++++ drivers/net/phy/sfp-bus.c | 20 ++++++++++++++++++++ drivers/net/phy/sfp.c | 5 +++++ drivers/net/phy/sfp.h | 1 + include/linux/sfp.h | 6 ++++++ 5 files changed, 56 insertions(+) diff --git a/drivers/net/phy/phylink.c b/drivers/net/phy/phylink.c index dc9a740b1ff77..f2106d17847a5 100644 --- a/drivers/net/phy/phylink.c +++ b/drivers/net/phy/phylink.c @@ -156,6 +156,23 @@ static const char *phylink_an_mode_str(unsigned int mode) return mode < ARRAY_SIZE(modestr) ? modestr[mode] : "unknown"; } +static unsigned int phylink_interface_signal_rate(phy_interface_t interface) +{ + switch (interface) { + case PHY_INTERFACE_MODE_SGMII: + case PHY_INTERFACE_MODE_1000BASEX: /* 1.25Mbd */ + return 1250; + case PHY_INTERFACE_MODE_2500BASEX: /* 3.125Mbd */ + return 3125; + case PHY_INTERFACE_MODE_5GBASER: /* 5.15625Mbd */ + return 5156; + case PHY_INTERFACE_MODE_10GBASER: /* 10.3125Mbd */ + return 10313; + default: + return 0; + } +} + /** * phylink_interface_max_speed() - get the maximum speed of a phy interface * @interface: phy interface mode defined by &typedef phy_interface_t @@ -1025,6 +1042,7 @@ static void phylink_major_config(struct phylink *pl, bool restart, { struct phylink_pcs *pcs = NULL; bool pcs_changed = false; + unsigned int rate_kbd; int err; phylink_dbg(pl, "major config %s\n", phy_modes(state->interface)); @@ -1084,6 +1102,12 @@ static void phylink_major_config(struct phylink *pl, bool restart, ERR_PTR(err)); } + if (pl->sfp_bus) { + rate_kbd = phylink_interface_signal_rate(state->interface); + if (rate_kbd) + sfp_upstream_set_signal_rate(pl->sfp_bus, rate_kbd); + } + phylink_pcs_poll_start(pl); } diff --git a/drivers/net/phy/sfp-bus.c b/drivers/net/phy/sfp-bus.c index 9372e5a4cadcf..e8dd47bffe435 100644 --- a/drivers/net/phy/sfp-bus.c +++ b/drivers/net/phy/sfp-bus.c @@ -575,6 +575,26 @@ static void sfp_upstream_clear(struct sfp_bus *bus) bus->upstream = NULL; } +/** + * sfp_upstream_set_signal_rate() - set data signalling rate + * @bus: a pointer to the &struct sfp_bus structure for the sfp module + * @rate_kbd: signalling rate in units of 1000 baud + * + * Configure the rate select settings on the SFP module for the signalling + * rate (not the same as the data rate). + * + * Locks that may be held: + * Phylink's state_mutex + * rtnl lock + * SFP's sm_mutex + */ +void sfp_upstream_set_signal_rate(struct sfp_bus *bus, unsigned int rate_kbd) +{ + if (bus->registered) + bus->socket_ops->set_signal_rate(bus->sfp, rate_kbd); +} +EXPORT_SYMBOL_GPL(sfp_upstream_set_signal_rate); + /** * sfp_bus_find_fwnode() - parse and locate the SFP bus from fwnode * @fwnode: firmware node for the parent device (MAC or PHY) diff --git a/drivers/net/phy/sfp.c b/drivers/net/phy/sfp.c index bf7dac9977e1b..34bf724c00c76 100644 --- a/drivers/net/phy/sfp.c +++ b/drivers/net/phy/sfp.c @@ -2527,6 +2527,10 @@ static void sfp_stop(struct sfp *sfp) sfp_sm_event(sfp, SFP_E_DEV_DOWN); } +static void sfp_set_signal_rate(struct sfp *sfp, unsigned int rate_kbd) +{ +} + static int sfp_module_info(struct sfp *sfp, struct ethtool_modinfo *modinfo) { /* locking... and check module is present */ @@ -2611,6 +2615,7 @@ static const struct sfp_socket_ops sfp_module_ops = { .detach = sfp_detach, .start = sfp_start, .stop = sfp_stop, + .set_signal_rate = sfp_set_signal_rate, .module_info = sfp_module_info, .module_eeprom = sfp_module_eeprom, .module_eeprom_by_page = sfp_module_eeprom_by_page, diff --git a/drivers/net/phy/sfp.h b/drivers/net/phy/sfp.h index 6cf1643214d3e..c7cb50d100999 100644 --- a/drivers/net/phy/sfp.h +++ b/drivers/net/phy/sfp.h @@ -19,6 +19,7 @@ struct sfp_socket_ops { void (*detach)(struct sfp *sfp); void (*start)(struct sfp *sfp); void (*stop)(struct sfp *sfp); + void (*set_signal_rate)(struct sfp *sfp, unsigned int rate_kbd); int (*module_info)(struct sfp *sfp, struct ethtool_modinfo *modinfo); int (*module_eeprom)(struct sfp *sfp, struct ethtool_eeprom *ee, u8 *data); diff --git a/include/linux/sfp.h b/include/linux/sfp.h index ef06a195b3c2b..2f66e03e9dbd0 100644 --- a/include/linux/sfp.h +++ b/include/linux/sfp.h @@ -556,6 +556,7 @@ int sfp_get_module_eeprom_by_page(struct sfp_bus *bus, struct netlink_ext_ack *extack); void sfp_upstream_start(struct sfp_bus *bus); void sfp_upstream_stop(struct sfp_bus *bus); +void sfp_upstream_set_signal_rate(struct sfp_bus *bus, unsigned int rate_kbd); void sfp_bus_put(struct sfp_bus *bus); struct sfp_bus *sfp_bus_find_fwnode(const struct fwnode_handle *fwnode); int sfp_bus_add_upstream(struct sfp_bus *bus, void *upstream, @@ -615,6 +616,11 @@ static inline void sfp_upstream_stop(struct sfp_bus *bus) { } +static inline void sfp_upstream_set_signal_rate(struct sfp_bus *bus, + unsigned int rate_kbd) +{ +} + static inline void sfp_bus_put(struct sfp_bus *bus) { } From fc082b39d0a29891ab4b54c88a40f42385103f71 Mon Sep 17 00:00:00 2001 From: "Russell King (Oracle)" Date: Wed, 17 May 2023 11:38:17 +0100 Subject: [PATCH 7/7] net: sfp: add support for rate selection Add support for parsing the rate select thresholds and switching of the RS0 and RS1 signals to the transceiver. This is complicated by various revisions of SFF-8472 and interaction of SFF-8431, SFF-8079 and INF-8074. Reviewed-by: Simon Horman Signed-off-by: Russell King (Oracle) Reviewed-by: Andrew Lunn Signed-off-by: Jakub Kicinski --- drivers/net/phy/sfp.c | 212 +++++++++++++++++++++++++++++++++++++----- include/linux/sfp.h | 8 ++ 2 files changed, 196 insertions(+), 24 deletions(-) diff --git a/drivers/net/phy/sfp.c b/drivers/net/phy/sfp.c index 34bf724c00c76..4799976a16095 100644 --- a/drivers/net/phy/sfp.c +++ b/drivers/net/phy/sfp.c @@ -24,14 +24,18 @@ enum { GPIO_LOS, GPIO_TX_FAULT, GPIO_TX_DISABLE, - GPIO_RATE_SELECT, + GPIO_RS0, + GPIO_RS1, GPIO_MAX, SFP_F_PRESENT = BIT(GPIO_MODDEF0), SFP_F_LOS = BIT(GPIO_LOS), SFP_F_TX_FAULT = BIT(GPIO_TX_FAULT), SFP_F_TX_DISABLE = BIT(GPIO_TX_DISABLE), - SFP_F_RATE_SELECT = BIT(GPIO_RATE_SELECT), + SFP_F_RS0 = BIT(GPIO_RS0), + SFP_F_RS1 = BIT(GPIO_RS1), + + SFP_F_OUTPUTS = SFP_F_TX_DISABLE | SFP_F_RS0 | SFP_F_RS1, SFP_E_INSERT = 0, SFP_E_REMOVE, @@ -148,6 +152,7 @@ static const char *gpio_names[] = { "tx-fault", "tx-disable", "rate-select0", + "rate-select1", }; static const enum gpiod_flags gpio_flags[] = { @@ -156,6 +161,7 @@ static const enum gpiod_flags gpio_flags[] = { GPIOD_IN, GPIOD_ASIS, GPIOD_ASIS, + GPIOD_ASIS, }; /* t_start_up (SFF-8431) or t_init (SFF-8472) is the time required for a @@ -249,6 +255,7 @@ struct sfp { * state: st_mutex held unless reading input bits */ struct mutex st_mutex; /* Protects state */ + unsigned int state_hw_drive; unsigned int state_hw_mask; unsigned int state_soft_mask; unsigned int state; @@ -269,6 +276,10 @@ struct sfp { unsigned int module_t_start_up; unsigned int module_t_wait; + unsigned int rate_kbd; + unsigned int rs_threshold_kbd; + unsigned int rs_state_mask; + bool have_a2; bool tx_fault_ignore; @@ -319,7 +330,7 @@ static bool sfp_module_supported(const struct sfp_eeprom_id *id) static const struct sff_data sfp_data = { .gpios = SFP_F_PRESENT | SFP_F_LOS | SFP_F_TX_FAULT | - SFP_F_TX_DISABLE | SFP_F_RATE_SELECT, + SFP_F_TX_DISABLE | SFP_F_RS0 | SFP_F_RS1, .module_supported = sfp_module_supported, }; @@ -507,20 +518,37 @@ static unsigned int sff_gpio_get_state(struct sfp *sfp) static void sfp_gpio_set_state(struct sfp *sfp, unsigned int state) { - if (state & SFP_F_PRESENT) { - /* If the module is present, drive the signals */ - if (sfp->gpio[GPIO_TX_DISABLE]) + unsigned int drive; + + if (state & SFP_F_PRESENT) + /* If the module is present, drive the requested signals */ + drive = sfp->state_hw_drive; + else + /* Otherwise, let them float to the pull-ups */ + drive = 0; + + if (sfp->gpio[GPIO_TX_DISABLE]) { + if (drive & SFP_F_TX_DISABLE) gpiod_direction_output(sfp->gpio[GPIO_TX_DISABLE], state & SFP_F_TX_DISABLE); - if (state & SFP_F_RATE_SELECT) - gpiod_direction_output(sfp->gpio[GPIO_RATE_SELECT], - state & SFP_F_RATE_SELECT); - } else { - /* Otherwise, let them float to the pull-ups */ - if (sfp->gpio[GPIO_TX_DISABLE]) + else gpiod_direction_input(sfp->gpio[GPIO_TX_DISABLE]); - if (state & SFP_F_RATE_SELECT) - gpiod_direction_input(sfp->gpio[GPIO_RATE_SELECT]); + } + + if (sfp->gpio[GPIO_RS0]) { + if (drive & SFP_F_RS0) + gpiod_direction_output(sfp->gpio[GPIO_RS0], + state & SFP_F_RS0); + else + gpiod_direction_input(sfp->gpio[GPIO_RS0]); + } + + if (sfp->gpio[GPIO_RS1]) { + if (drive & SFP_F_RS1) + gpiod_direction_output(sfp->gpio[GPIO_RS1], + state & SFP_F_RS1); + else + gpiod_direction_input(sfp->gpio[GPIO_RS1]); } } @@ -682,16 +710,33 @@ static unsigned int sfp_soft_get_state(struct sfp *sfp) return state & sfp->state_soft_mask; } -static void sfp_soft_set_state(struct sfp *sfp, unsigned int state) +static void sfp_soft_set_state(struct sfp *sfp, unsigned int state, + unsigned int soft) { - u8 mask = SFP_STATUS_TX_DISABLE_FORCE; + u8 mask = 0; u8 val = 0; + if (soft & SFP_F_TX_DISABLE) + mask |= SFP_STATUS_TX_DISABLE_FORCE; if (state & SFP_F_TX_DISABLE) val |= SFP_STATUS_TX_DISABLE_FORCE; + if (soft & SFP_F_RS0) + mask |= SFP_STATUS_RS0_SELECT; + if (state & SFP_F_RS0) + val |= SFP_STATUS_RS0_SELECT; + + if (mask) + sfp_modify_u8(sfp, true, SFP_STATUS, mask, val); - sfp_modify_u8(sfp, true, SFP_STATUS, mask, val); + val = mask = 0; + if (soft & SFP_F_RS1) + mask |= SFP_EXT_STATUS_RS1_SELECT; + if (state & SFP_F_RS1) + val |= SFP_EXT_STATUS_RS1_SELECT; + + if (mask) + sfp_modify_u8(sfp, true, SFP_EXT_STATUS, mask, val); } static void sfp_soft_start_poll(struct sfp *sfp) @@ -705,6 +750,8 @@ static void sfp_soft_start_poll(struct sfp *sfp) mask |= SFP_F_TX_FAULT; if (id->ext.enhopts & SFP_ENHOPTS_SOFT_RX_LOS) mask |= SFP_F_LOS; + if (id->ext.enhopts & SFP_ENHOPTS_SOFT_RATE_SELECT) + mask |= sfp->rs_state_mask; mutex_lock(&sfp->st_mutex); // Poll the soft state for hardware pins we want to ignore @@ -743,11 +790,13 @@ static unsigned int sfp_get_state(struct sfp *sfp) */ static void sfp_set_state(struct sfp *sfp, unsigned int state) { + unsigned int soft; + sfp->set_state(sfp, state); - if (state & SFP_F_PRESENT && - sfp->state_soft_mask & SFP_F_TX_DISABLE) - sfp_soft_set_state(sfp, state); + soft = sfp->state_soft_mask & SFP_F_OUTPUTS; + if (state & SFP_F_PRESENT && soft) + sfp_soft_set_state(sfp, state, soft); } static void sfp_mod_state(struct sfp *sfp, unsigned int mask, unsigned int set) @@ -1589,10 +1638,15 @@ static int sfp_debug_state_show(struct seq_file *s, void *data) sfp->sm_fault_retries); seq_printf(s, "PHY probe remaining retries: %d\n", sfp->sm_phy_retries); + seq_printf(s, "Signalling rate: %u kBd\n", sfp->rate_kbd); + seq_printf(s, "Rate select threshold: %u kBd\n", + sfp->rs_threshold_kbd); seq_printf(s, "moddef0: %d\n", !!(sfp->state & SFP_F_PRESENT)); seq_printf(s, "rx_los: %d\n", !!(sfp->state & SFP_F_LOS)); seq_printf(s, "tx_fault: %d\n", !!(sfp->state & SFP_F_TX_FAULT)); seq_printf(s, "tx_disable: %d\n", !!(sfp->state & SFP_F_TX_DISABLE)); + seq_printf(s, "rs0: %d\n", !!(sfp->state & SFP_F_RS0)); + seq_printf(s, "rs1: %d\n", !!(sfp->state & SFP_F_RS1)); return 0; } DEFINE_SHOW_ATTRIBUTE(sfp_debug_state); @@ -1898,6 +1952,95 @@ static int sfp_sm_mod_hpower(struct sfp *sfp, bool enable) return 0; } +static void sfp_module_parse_rate_select(struct sfp *sfp) +{ + u8 rate_id; + + sfp->rs_threshold_kbd = 0; + sfp->rs_state_mask = 0; + + if (!(sfp->id.ext.options & cpu_to_be16(SFP_OPTIONS_RATE_SELECT))) + /* No support for RateSelect */ + return; + + /* Default to INF-8074 RateSelect operation. The signalling threshold + * rate is not well specified, so always select "Full Bandwidth", but + * SFF-8079 reveals that it is understood that RS0 will be low for + * 1.0625Gb/s and high for 2.125Gb/s. Choose a value half-way between. + * This method exists prior to SFF-8472. + */ + sfp->rs_state_mask = SFP_F_RS0; + sfp->rs_threshold_kbd = 1594; + + /* Parse the rate identifier, which is complicated due to history: + * SFF-8472 rev 9.5 marks this field as reserved. + * SFF-8079 references SFF-8472 rev 9.5 and defines bit 0. SFF-8472 + * compliance is not required. + * SFF-8472 rev 10.2 defines this field using values 0..4 + * SFF-8472 rev 11.0 redefines this field with bit 0 for SFF-8079 + * and even values. + */ + rate_id = sfp->id.base.rate_id; + if (rate_id == 0) + /* Unspecified */ + return; + + /* SFF-8472 rev 10.0..10.4 did not account for SFF-8079 using bit 0, + * and allocated value 3 to SFF-8431 independent tx/rx rate select. + * Convert this to a SFF-8472 rev 11.0 rate identifier. + */ + if (sfp->id.ext.sff8472_compliance >= SFP_SFF8472_COMPLIANCE_REV10_2 && + sfp->id.ext.sff8472_compliance < SFP_SFF8472_COMPLIANCE_REV11_0 && + rate_id == 3) + rate_id = SFF_RID_8431; + + if (rate_id & SFF_RID_8079) { + /* SFF-8079 RateSelect / Application Select in conjunction with + * SFF-8472 rev 9.5. SFF-8079 defines rate_id as a bitfield + * with only bit 0 used, which takes precedence over SFF-8472. + */ + if (!(sfp->id.ext.enhopts & SFP_ENHOPTS_APP_SELECT_SFF8079)) { + /* SFF-8079 Part 1 - rate selection between Fibre + * Channel 1.0625/2.125/4.25 Gbd modes. Note that RS0 + * is high for 2125, so we have to subtract 1 to + * include it. + */ + sfp->rs_threshold_kbd = 2125 - 1; + sfp->rs_state_mask = SFP_F_RS0; + } + return; + } + + /* SFF-8472 rev 9.5 does not define the rate identifier */ + if (sfp->id.ext.sff8472_compliance <= SFP_SFF8472_COMPLIANCE_REV9_5) + return; + + /* SFF-8472 rev 11.0 defines rate_id as a numerical value which will + * always have bit 0 clear due to SFF-8079's bitfield usage of rate_id. + */ + switch (rate_id) { + case SFF_RID_8431_RX_ONLY: + sfp->rs_threshold_kbd = 4250; + sfp->rs_state_mask = SFP_F_RS0; + break; + + case SFF_RID_8431_TX_ONLY: + sfp->rs_threshold_kbd = 4250; + sfp->rs_state_mask = SFP_F_RS1; + break; + + case SFF_RID_8431: + sfp->rs_threshold_kbd = 4250; + sfp->rs_state_mask = SFP_F_RS0 | SFP_F_RS1; + break; + + case SFF_RID_10G8G: + sfp->rs_threshold_kbd = 9000; + sfp->rs_state_mask = SFP_F_RS0 | SFP_F_RS1; + break; + } +} + /* GPON modules based on Realtek RTL8672 and RTL9601C chips (e.g. V-SOL * V2801F, CarlitoxxPro CPGOS03-0490, Ubiquiti U-Fiber Instant, ...) do * not support multibyte reads from the EEPROM. Each multi-byte read @@ -2117,6 +2260,8 @@ static int sfp_sm_mod_probe(struct sfp *sfp, bool report) if (ret < 0) return ret; + sfp_module_parse_rate_select(sfp); + mask = SFP_F_PRESENT; if (sfp->gpio[GPIO_TX_DISABLE]) mask |= SFP_F_TX_DISABLE; @@ -2124,6 +2269,10 @@ static int sfp_sm_mod_probe(struct sfp *sfp, bool report) mask |= SFP_F_TX_FAULT; if (sfp->gpio[GPIO_LOS]) mask |= SFP_F_LOS; + if (sfp->gpio[GPIO_RS0]) + mask |= SFP_F_RS0; + if (sfp->gpio[GPIO_RS1]) + mask |= SFP_F_RS1; sfp->module_t_start_up = T_START_UP; sfp->module_t_wait = T_WAIT; @@ -2146,6 +2295,9 @@ static int sfp_sm_mod_probe(struct sfp *sfp, bool report) /* Initialise state bits to use from hardware */ sfp->state_hw_mask = mask; + /* We want to drive the rate select pins that the module is using */ + sfp->state_hw_drive |= sfp->rs_state_mask; + if (sfp->quirk && sfp->quirk->fixup) sfp->quirk->fixup(sfp); mutex_unlock(&sfp->st_mutex); @@ -2162,6 +2314,7 @@ static void sfp_sm_mod_remove(struct sfp *sfp) memset(&sfp->id, 0, sizeof(sfp->id)); sfp->module_power_mW = 0; + sfp->state_hw_drive = SFP_F_TX_DISABLE; sfp->have_a2 = false; dev_info(sfp->dev, "module removed\n"); @@ -2529,6 +2682,16 @@ static void sfp_stop(struct sfp *sfp) static void sfp_set_signal_rate(struct sfp *sfp, unsigned int rate_kbd) { + unsigned int set; + + sfp->rate_kbd = rate_kbd; + + if (rate_kbd > sfp->rs_threshold_kbd) + set = sfp->rs_state_mask; + else + set = 0; + + sfp_mod_state(sfp, SFP_F_RS0 | SFP_F_RS1, set); } static int sfp_module_info(struct sfp *sfp, struct ethtool_modinfo *modinfo) @@ -2648,7 +2811,7 @@ static void sfp_check_state(struct sfp *sfp) dev_dbg(sfp->dev, "%s %u -> %u\n", gpio_names[i], !!(sfp->state & BIT(i)), !!(state & BIT(i))); - state |= sfp->state & (SFP_F_TX_DISABLE | SFP_F_RATE_SELECT); + state |= sfp->state & SFP_F_OUTPUTS; sfp->state = state; mutex_unlock(&sfp->st_mutex); @@ -2790,6 +2953,7 @@ static int sfp_probe(struct platform_device *pdev) } sfp->state_hw_mask = SFP_F_PRESENT; + sfp->state_hw_drive = SFP_F_TX_DISABLE; sfp->get_state = sfp_gpio_get_state; sfp->set_state = sfp_gpio_set_state; @@ -2815,9 +2979,9 @@ static int sfp_probe(struct platform_device *pdev) */ sfp->state = sfp_get_state(sfp) | SFP_F_TX_DISABLE; - if (sfp->gpio[GPIO_RATE_SELECT] && - gpiod_get_value_cansleep(sfp->gpio[GPIO_RATE_SELECT])) - sfp->state |= SFP_F_RATE_SELECT; + if (sfp->gpio[GPIO_RS0] && + gpiod_get_value_cansleep(sfp->gpio[GPIO_RS0])) + sfp->state |= SFP_F_RS0; sfp_set_state(sfp, sfp->state); sfp_module_tx_disable(sfp); if (sfp->state & SFP_F_PRESENT) { diff --git a/include/linux/sfp.h b/include/linux/sfp.h index 2f66e03e9dbd0..9346cd44814d6 100644 --- a/include/linux/sfp.h +++ b/include/linux/sfp.h @@ -342,6 +342,12 @@ enum { SFP_ENCODING = 11, SFP_BR_NOMINAL = 12, SFP_RATE_ID = 13, + SFF_RID_8079 = 0x01, + SFF_RID_8431_RX_ONLY = 0x02, + SFF_RID_8431_TX_ONLY = 0x04, + SFF_RID_8431 = 0x06, + SFF_RID_10G8G = 0x0e, + SFP_LINK_LEN_SM_KM = 14, SFP_LINK_LEN_SM_100M = 15, SFP_LINK_LEN_50UM_OM2_10M = 16, @@ -465,6 +471,7 @@ enum { SFP_STATUS = 110, SFP_STATUS_TX_DISABLE = BIT(7), SFP_STATUS_TX_DISABLE_FORCE = BIT(6), + SFP_STATUS_RS0_SELECT = BIT(3), SFP_STATUS_TX_FAULT = BIT(2), SFP_STATUS_RX_LOS = BIT(1), SFP_ALARM0 = 112, @@ -496,6 +503,7 @@ enum { SFP_WARN1_RXPWR_LOW = BIT(6), SFP_EXT_STATUS = 118, + SFP_EXT_STATUS_RS1_SELECT = BIT(3), SFP_EXT_STATUS_PWRLVL_SELECT = BIT(0), SFP_VSL = 120,