From fa2de660a8d001fdce6209534b60b85b87cda84f Mon Sep 17 00:00:00 2001 From: Russell King Date: Wed, 11 Dec 2019 10:55:54 +0000 Subject: [PATCH 01/14] net: sfp: remove incomplete 100BASE-FX and 100BASE-LX support The 100BASE-FX and 100BASE-LX support assumes a PHY is present; this is probably an incorrect assumption. In any case, sfp_parse_support() will fail such a module. Let's stop pretending we support these modules. Reviewed-by: Andrew Lunn Signed-off-by: Russell King Signed-off-by: David S. Miller --- drivers/net/phy/sfp-bus.c | 4 +--- drivers/net/phy/sfp.c | 13 +------------ 2 files changed, 2 insertions(+), 15 deletions(-) diff --git a/drivers/net/phy/sfp-bus.c b/drivers/net/phy/sfp-bus.c index 5a72093ab6e7..02ab07624c89 100644 --- a/drivers/net/phy/sfp-bus.c +++ b/drivers/net/phy/sfp-bus.c @@ -342,9 +342,7 @@ phy_interface_t sfp_select_interface(struct sfp_bus *bus, if (phylink_test(link_modes, 2500baseX_Full)) return PHY_INTERFACE_MODE_2500BASEX; - if (id->base.e1000_base_t || - id->base.e100_base_lx || - id->base.e100_base_fx) + if (id->base.e1000_base_t) return PHY_INTERFACE_MODE_SGMII; if (phylink_test(link_modes, 1000baseX_Full)) diff --git a/drivers/net/phy/sfp.c b/drivers/net/phy/sfp.c index 27360d1840b2..ae6a52a19458 100644 --- a/drivers/net/phy/sfp.c +++ b/drivers/net/phy/sfp.c @@ -1489,18 +1489,7 @@ static void sfp_sm_fault(struct sfp *sfp, unsigned int next_state, bool warn) 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. - * - * If it's a gigabit-only fiber module, it probably does - * not have a PHY, so switch to 802.3z negotiation mode. - * Otherwise, switch to SGMII mode (which is required to - * support non-gigabit speeds) and probe for a PHY. - */ - if (sfp->id.base.e1000_base_t || - sfp->id.base.e100_base_lx || - sfp->id.base.e100_base_fx) + if (sfp->id.base.e1000_base_t) sfp_sm_probe_phy(sfp); } From a4516c7053b96fed98a0439a9226983b5275474b Mon Sep 17 00:00:00 2001 From: Russell King Date: Wed, 11 Dec 2019 10:55:59 +0000 Subject: [PATCH 02/14] net: sfp: derive interface mode from ethtool link modes We don't need the EEPROM ID to derive the phy interface mode as we can derive it merely from the ethtool link modes. Remove the EEPROM ID argument to sfp_select_interface(). Reviewed-by: Andrew Lunn Signed-off-by: Russell King Signed-off-by: David S. Miller --- drivers/net/phy/marvell10g.c | 2 +- drivers/net/phy/phylink.c | 2 +- drivers/net/phy/sfp-bus.c | 11 ++++------- include/linux/sfp.h | 2 -- 4 files changed, 6 insertions(+), 11 deletions(-) diff --git a/drivers/net/phy/marvell10g.c b/drivers/net/phy/marvell10g.c index 1bf13017d288..512f27b0b5cd 100644 --- a/drivers/net/phy/marvell10g.c +++ b/drivers/net/phy/marvell10g.c @@ -214,7 +214,7 @@ static int mv3310_sfp_insert(void *upstream, const struct sfp_eeprom_id *id) phy_interface_t iface; sfp_parse_support(phydev->sfp_bus, id, support); - iface = sfp_select_interface(phydev->sfp_bus, id, support); + iface = sfp_select_interface(phydev->sfp_bus, support); if (iface != PHY_INTERFACE_MODE_10GKR) { dev_err(&phydev->mdio.dev, "incompatible SFP module inserted\n"); diff --git a/drivers/net/phy/phylink.c b/drivers/net/phy/phylink.c index 9a616d6bc4eb..b3e4d9bf8113 100644 --- a/drivers/net/phy/phylink.c +++ b/drivers/net/phy/phylink.c @@ -1714,7 +1714,7 @@ static int phylink_sfp_module_insert(void *upstream, linkmode_copy(support1, support); - iface = sfp_select_interface(pl->sfp_bus, id, config.advertising); + iface = sfp_select_interface(pl->sfp_bus, config.advertising); if (iface == PHY_INTERFACE_MODE_NA) { phylink_err(pl, "selection of interface failed, advertisement %*pb\n", diff --git a/drivers/net/phy/sfp-bus.c b/drivers/net/phy/sfp-bus.c index 02ab07624c89..1561962fda30 100644 --- a/drivers/net/phy/sfp-bus.c +++ b/drivers/net/phy/sfp-bus.c @@ -320,16 +320,12 @@ EXPORT_SYMBOL_GPL(sfp_parse_support); /** * sfp_select_interface() - Select appropriate phy_interface_t mode * @bus: a pointer to the &struct sfp_bus structure for the sfp module - * @id: a pointer to the module's &struct sfp_eeprom_id * @link_modes: ethtool link modes mask * - * Derive the phy_interface_t mode for the information found in the - * module's identifying EEPROM and the link modes mask. There is no - * standard or defined way to derive this information, so we decide - * based upon the link mode mask. + * Derive the phy_interface_t mode for the SFP module from the link + * modes mask. */ phy_interface_t sfp_select_interface(struct sfp_bus *bus, - const struct sfp_eeprom_id *id, unsigned long *link_modes) { if (phylink_test(link_modes, 10000baseCR_Full) || @@ -342,7 +338,8 @@ phy_interface_t sfp_select_interface(struct sfp_bus *bus, if (phylink_test(link_modes, 2500baseX_Full)) return PHY_INTERFACE_MODE_2500BASEX; - if (id->base.e1000_base_t) + if (phylink_test(link_modes, 1000baseT_Half) || + phylink_test(link_modes, 1000baseT_Full)) return PHY_INTERFACE_MODE_SGMII; if (phylink_test(link_modes, 1000baseX_Full)) diff --git a/include/linux/sfp.h b/include/linux/sfp.h index 487fd9412d10..8d7b98c214d7 100644 --- a/include/linux/sfp.h +++ b/include/linux/sfp.h @@ -504,7 +504,6 @@ int sfp_parse_port(struct sfp_bus *bus, const struct sfp_eeprom_id *id, void sfp_parse_support(struct sfp_bus *bus, const struct sfp_eeprom_id *id, unsigned long *support); phy_interface_t sfp_select_interface(struct sfp_bus *bus, - const struct sfp_eeprom_id *id, unsigned long *link_modes); int sfp_get_module_info(struct sfp_bus *bus, struct ethtool_modinfo *modinfo); @@ -532,7 +531,6 @@ static inline void sfp_parse_support(struct sfp_bus *bus, } static inline phy_interface_t sfp_select_interface(struct sfp_bus *bus, - const struct sfp_eeprom_id *id, unsigned long *link_modes) { return PHY_INTERFACE_MODE_NA; From 0fbd26a9fb6875b98fcfff523831fec47bc5e9a2 Mon Sep 17 00:00:00 2001 From: Russell King Date: Wed, 11 Dec 2019 10:56:04 +0000 Subject: [PATCH 03/14] net: sfp: add more extended compliance codes SFF-8024 is used to define various constants re-used in several SFF SFP-related specifications. Split these constants from the enum, and rename them to indicate that they're defined by SFF-8024. Add and use updated SFF-8024 extended compliance code definitions for 10GBASE-T, 5GBASE-T and 2.5GBASE-T modules. Reviewed-by: Andrew Lunn Signed-off-by: Russell King Signed-off-by: David S. Miller --- drivers/net/phy/sfp-bus.c | 60 ++++++++++++++++------------ drivers/net/phy/sfp.c | 4 +- include/linux/sfp.h | 82 ++++++++++++++++++++++++++------------- 3 files changed, 93 insertions(+), 53 deletions(-) diff --git a/drivers/net/phy/sfp-bus.c b/drivers/net/phy/sfp-bus.c index 1561962fda30..c6627f1e5d68 100644 --- a/drivers/net/phy/sfp-bus.c +++ b/drivers/net/phy/sfp-bus.c @@ -124,35 +124,35 @@ int sfp_parse_port(struct sfp_bus *bus, const struct sfp_eeprom_id *id, /* port is the physical connector, set this from the connector field. */ switch (id->base.connector) { - case SFP_CONNECTOR_SC: - case SFP_CONNECTOR_FIBERJACK: - case SFP_CONNECTOR_LC: - case SFP_CONNECTOR_MT_RJ: - case SFP_CONNECTOR_MU: - case SFP_CONNECTOR_OPTICAL_PIGTAIL: + case SFF8024_CONNECTOR_SC: + case SFF8024_CONNECTOR_FIBERJACK: + case SFF8024_CONNECTOR_LC: + case SFF8024_CONNECTOR_MT_RJ: + case SFF8024_CONNECTOR_MU: + case SFF8024_CONNECTOR_OPTICAL_PIGTAIL: + case SFF8024_CONNECTOR_MPO_1X12: + case SFF8024_CONNECTOR_MPO_2X16: port = PORT_FIBRE; break; - case SFP_CONNECTOR_RJ45: + case SFF8024_CONNECTOR_RJ45: port = PORT_TP; break; - case SFP_CONNECTOR_COPPER_PIGTAIL: + case SFF8024_CONNECTOR_COPPER_PIGTAIL: port = PORT_DA; break; - case SFP_CONNECTOR_UNSPEC: + case SFF8024_CONNECTOR_UNSPEC: if (id->base.e1000_base_t) { port = PORT_TP; break; } /* fallthrough */ - case SFP_CONNECTOR_SG: /* guess */ - case SFP_CONNECTOR_MPO_1X12: - case SFP_CONNECTOR_MPO_2X16: - case SFP_CONNECTOR_HSSDC_II: - case SFP_CONNECTOR_NOSEPARATE: - case SFP_CONNECTOR_MXC_2X16: + case SFF8024_CONNECTOR_SG: /* guess */ + case SFF8024_CONNECTOR_HSSDC_II: + case SFF8024_CONNECTOR_NOSEPARATE: + case SFF8024_CONNECTOR_MXC_2X16: port = PORT_OTHER; break; default: @@ -261,22 +261,33 @@ void sfp_parse_support(struct sfp_bus *bus, const struct sfp_eeprom_id *id, } switch (id->base.extended_cc) { - case 0x00: /* Unspecified */ + case SFF8024_ECC_UNSPEC: break; - case 0x02: /* 100Gbase-SR4 or 25Gbase-SR */ + case SFF8024_ECC_100GBASE_SR4_25GBASE_SR: phylink_set(modes, 100000baseSR4_Full); phylink_set(modes, 25000baseSR_Full); break; - case 0x03: /* 100Gbase-LR4 or 25Gbase-LR */ - case 0x04: /* 100Gbase-ER4 or 25Gbase-ER */ + case SFF8024_ECC_100GBASE_LR4_25GBASE_LR: + case SFF8024_ECC_100GBASE_ER4_25GBASE_ER: phylink_set(modes, 100000baseLR4_ER4_Full); break; - case 0x0b: /* 100Gbase-CR4 or 25Gbase-CR CA-L */ - case 0x0c: /* 25Gbase-CR CA-S */ - case 0x0d: /* 25Gbase-CR CA-N */ + case SFF8024_ECC_100GBASE_CR4: phylink_set(modes, 100000baseCR4_Full); + /* fallthrough */ + case SFF8024_ECC_25GBASE_CR_S: + case SFF8024_ECC_25GBASE_CR_N: phylink_set(modes, 25000baseCR_Full); break; + case SFF8024_ECC_10GBASE_T_SFI: + case SFF8024_ECC_10GBASE_T_SR: + phylink_set(modes, 10000baseT_Full); + break; + case SFF8024_ECC_5GBASE_T: + phylink_set(modes, 5000baseT_Full); + break; + case SFF8024_ECC_2_5GBASE_T: + phylink_set(modes, 2500baseT_Full); + break; default: dev_warn(bus->sfp_dev, "Unknown/unsupported extended compliance code: 0x%02x\n", @@ -301,7 +312,7 @@ void sfp_parse_support(struct sfp_bus *bus, const struct sfp_eeprom_id *id, */ if (bitmap_empty(modes, __ETHTOOL_LINK_MODE_MASK_NBITS)) { /* If the encoding and bit rate allows 1000baseX */ - if (id->base.encoding == SFP_ENCODING_8B10B && br_nom && + if (id->base.encoding == SFF8024_ENCODING_8B10B && br_nom && br_min <= 1300 && br_max >= 1200) phylink_set(modes, 1000baseX_Full); } @@ -332,7 +343,8 @@ phy_interface_t sfp_select_interface(struct sfp_bus *bus, phylink_test(link_modes, 10000baseSR_Full) || phylink_test(link_modes, 10000baseLR_Full) || phylink_test(link_modes, 10000baseLRM_Full) || - phylink_test(link_modes, 10000baseER_Full)) + phylink_test(link_modes, 10000baseER_Full) || + phylink_test(link_modes, 10000baseT_Full)) return PHY_INTERFACE_MODE_10GKR; if (phylink_test(link_modes, 2500baseX_Full)) diff --git a/drivers/net/phy/sfp.c b/drivers/net/phy/sfp.c index ae6a52a19458..ad3808307dba 100644 --- a/drivers/net/phy/sfp.c +++ b/drivers/net/phy/sfp.c @@ -242,7 +242,7 @@ struct sfp { static bool sff_module_supported(const struct sfp_eeprom_id *id) { - return id->base.phys_id == SFP_PHYS_ID_SFF && + return id->base.phys_id == SFF8024_ID_SFF_8472 && id->base.phys_ext_id == SFP_PHYS_EXT_ID_SFP; } @@ -253,7 +253,7 @@ static const struct sff_data sff_data = { static bool sfp_module_supported(const struct sfp_eeprom_id *id) { - return id->base.phys_id == SFP_PHYS_ID_SFP && + return id->base.phys_id == SFF8024_ID_SFP && id->base.phys_ext_id == SFP_PHYS_EXT_ID_SFP; } diff --git a/include/linux/sfp.h b/include/linux/sfp.h index 8d7b98c214d7..373d8b67ea86 100644 --- a/include/linux/sfp.h +++ b/include/linux/sfp.h @@ -275,6 +275,61 @@ struct sfp_diag { __be16 cal_v_offset; } __packed; +/* SFF8024 defined constants */ +enum { + SFF8024_ID_UNK = 0x00, + SFF8024_ID_SFF_8472 = 0x02, + SFF8024_ID_SFP = 0x03, + SFF8024_ID_DWDM_SFP = 0x0b, + SFF8024_ID_QSFP_8438 = 0x0c, + SFF8024_ID_QSFP_8436_8636 = 0x0d, + SFF8024_ID_QSFP28_8636 = 0x11, + + SFF8024_ENCODING_UNSPEC = 0x00, + SFF8024_ENCODING_8B10B = 0x01, + SFF8024_ENCODING_4B5B = 0x02, + SFF8024_ENCODING_NRZ = 0x03, + SFF8024_ENCODING_8472_MANCHESTER= 0x04, + SFF8024_ENCODING_8472_SONET = 0x05, + SFF8024_ENCODING_8472_64B66B = 0x06, + SFF8024_ENCODING_8436_MANCHESTER= 0x06, + SFF8024_ENCODING_8436_SONET = 0x04, + SFF8024_ENCODING_8436_64B66B = 0x05, + SFF8024_ENCODING_256B257B = 0x07, + SFF8024_ENCODING_PAM4 = 0x08, + + SFF8024_CONNECTOR_UNSPEC = 0x00, + /* codes 01-05 not supportable on SFP, but some modules have single SC */ + SFF8024_CONNECTOR_SC = 0x01, + SFF8024_CONNECTOR_FIBERJACK = 0x06, + SFF8024_CONNECTOR_LC = 0x07, + SFF8024_CONNECTOR_MT_RJ = 0x08, + SFF8024_CONNECTOR_MU = 0x09, + SFF8024_CONNECTOR_SG = 0x0a, + SFF8024_CONNECTOR_OPTICAL_PIGTAIL= 0x0b, + SFF8024_CONNECTOR_MPO_1X12 = 0x0c, + SFF8024_CONNECTOR_MPO_2X16 = 0x0d, + SFF8024_CONNECTOR_HSSDC_II = 0x20, + SFF8024_CONNECTOR_COPPER_PIGTAIL= 0x21, + SFF8024_CONNECTOR_RJ45 = 0x22, + SFF8024_CONNECTOR_NOSEPARATE = 0x23, + SFF8024_CONNECTOR_MXC_2X16 = 0x24, + + SFF8024_ECC_UNSPEC = 0x00, + SFF8024_ECC_100G_25GAUI_C2M_AOC = 0x01, + SFF8024_ECC_100GBASE_SR4_25GBASE_SR = 0x02, + SFF8024_ECC_100GBASE_LR4_25GBASE_LR = 0x03, + SFF8024_ECC_100GBASE_ER4_25GBASE_ER = 0x04, + SFF8024_ECC_100GBASE_SR10 = 0x05, + SFF8024_ECC_100GBASE_CR4 = 0x0b, + SFF8024_ECC_25GBASE_CR_S = 0x0c, + SFF8024_ECC_25GBASE_CR_N = 0x0d, + SFF8024_ECC_10GBASE_T_SFI = 0x16, + SFF8024_ECC_10GBASE_T_SR = 0x1c, + SFF8024_ECC_5GBASE_T = 0x1d, + SFF8024_ECC_2_5GBASE_T = 0x1e, +}; + /* SFP EEPROM registers */ enum { SFP_PHYS_ID = 0x00, @@ -309,34 +364,7 @@ enum { SFP_SFF8472_COMPLIANCE = 0x5e, SFP_CC_EXT = 0x5f, - SFP_PHYS_ID_SFF = 0x02, - SFP_PHYS_ID_SFP = 0x03, SFP_PHYS_EXT_ID_SFP = 0x04, - SFP_CONNECTOR_UNSPEC = 0x00, - /* codes 01-05 not supportable on SFP, but some modules have single SC */ - SFP_CONNECTOR_SC = 0x01, - SFP_CONNECTOR_FIBERJACK = 0x06, - SFP_CONNECTOR_LC = 0x07, - SFP_CONNECTOR_MT_RJ = 0x08, - SFP_CONNECTOR_MU = 0x09, - SFP_CONNECTOR_SG = 0x0a, - SFP_CONNECTOR_OPTICAL_PIGTAIL = 0x0b, - SFP_CONNECTOR_MPO_1X12 = 0x0c, - SFP_CONNECTOR_MPO_2X16 = 0x0d, - SFP_CONNECTOR_HSSDC_II = 0x20, - SFP_CONNECTOR_COPPER_PIGTAIL = 0x21, - SFP_CONNECTOR_RJ45 = 0x22, - SFP_CONNECTOR_NOSEPARATE = 0x23, - SFP_CONNECTOR_MXC_2X16 = 0x24, - SFP_ENCODING_UNSPEC = 0x00, - SFP_ENCODING_8B10B = 0x01, - SFP_ENCODING_4B5B = 0x02, - SFP_ENCODING_NRZ = 0x03, - SFP_ENCODING_8472_MANCHESTER = 0x04, - SFP_ENCODING_8472_SONET = 0x05, - SFP_ENCODING_8472_64B66B = 0x06, - SFP_ENCODING_256B257B = 0x07, - SFP_ENCODING_PAM4 = 0x08, SFP_OPTIONS_HIGH_POWER_LEVEL = BIT(13), SFP_OPTIONS_PAGING_A2 = BIT(12), SFP_OPTIONS_RETIMER = BIT(11), From 74c551ca5a0edcc9cf66a3b73fd95b9a8615bfd0 Mon Sep 17 00:00:00 2001 From: Russell King Date: Wed, 11 Dec 2019 10:56:09 +0000 Subject: [PATCH 04/14] 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. Reviewed-by: Andrew Lunn Signed-off-by: Russell King Signed-off-by: David S. Miller --- 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(+) diff --git a/drivers/net/phy/sfp-bus.c b/drivers/net/phy/sfp-bus.c index c6627f1e5d68..eabc9e3f0a9e 100644 --- a/drivers/net/phy/sfp-bus.c +++ b/drivers/net/phy/sfp-bus.c @@ -712,6 +712,27 @@ void sfp_module_remove(struct sfp_bus *bus) } 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; diff --git a/drivers/net/phy/sfp.c b/drivers/net/phy/sfp.c index ad3808307dba..23f30dac0f17 100644 --- 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 short event) 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", @@ -1826,6 +1828,8 @@ static void sfp_sm_main(struct sfp *sfp, unsigned int event) 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); @@ -1893,6 +1897,10 @@ static void sfp_sm_main(struct sfp *sfp, unsigned int event) * 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 */ diff --git a/drivers/net/phy/sfp.h b/drivers/net/phy/sfp.h index 64f54b0bbd8c..b83f70526270 100644 --- 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); diff --git a/include/linux/sfp.h b/include/linux/sfp.h index 373d8b67ea86..66a56396e8e3 100644 --- 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 *); From 4882057ad434e8a6434fce20dc7b7315d40a9d4a Mon Sep 17 00:00:00 2001 From: Russell King Date: Wed, 11 Dec 2019 10:56:14 +0000 Subject: [PATCH 05/14] net: sfp: move phy_start()/phy_stop() to phylink Move phy_start() and phy_stop() into the module_start and module_stop notifications in phylink, rather than having them in the SFP code. This gives phylink responsibility for controlling the PHY, rather than having SFP start and stop the PHY state machine. Reviewed-by: Andrew Lunn Signed-off-by: Russell King Signed-off-by: David S. Miller --- drivers/net/phy/phylink.c | 22 ++++++++++++++++++++++ drivers/net/phy/sfp.c | 2 -- 2 files changed, 22 insertions(+), 2 deletions(-) diff --git a/drivers/net/phy/phylink.c b/drivers/net/phy/phylink.c index b3e4d9bf8113..a89219ac4490 100644 --- a/drivers/net/phy/phylink.c +++ b/drivers/net/phy/phylink.c @@ -1767,6 +1767,26 @@ static int phylink_sfp_module_insert(void *upstream, return ret; } +static int phylink_sfp_module_start(void *upstream) +{ + struct phylink *pl = upstream; + + /* If this SFP module has a PHY, start the PHY now. */ + if (pl->phydev) + phy_start(pl->phydev); + + return 0; +} + +static void phylink_sfp_module_stop(void *upstream) +{ + struct phylink *pl = upstream; + + /* If this SFP module has a PHY, stop it. */ + if (pl->phydev) + phy_stop(pl->phydev); +} + static void phylink_sfp_link_down(void *upstream) { struct phylink *pl = upstream; @@ -1802,6 +1822,8 @@ static const struct sfp_upstream_ops sfp_phylink_ops = { .attach = phylink_sfp_attach, .detach = phylink_sfp_detach, .module_insert = phylink_sfp_module_insert, + .module_start = phylink_sfp_module_start, + .module_stop = phylink_sfp_module_stop, .link_up = phylink_sfp_link_up, .link_down = phylink_sfp_link_down, .connect_phy = phylink_sfp_connect_phy, diff --git a/drivers/net/phy/sfp.c b/drivers/net/phy/sfp.c index 23f30dac0f17..d7d2c797c89c 100644 --- a/drivers/net/phy/sfp.c +++ b/drivers/net/phy/sfp.c @@ -1396,7 +1396,6 @@ static void sfp_sm_mod_next(struct sfp *sfp, unsigned int state, static void sfp_sm_phy_detach(struct sfp *sfp) { - phy_stop(sfp->mod_phy); sfp_remove_phy(sfp->sfp_bus); phy_device_remove(sfp->mod_phy); phy_device_free(sfp->mod_phy); @@ -1427,7 +1426,6 @@ static void sfp_sm_probe_phy(struct sfp *sfp) } sfp->mod_phy = phy; - phy_start(phy); } static void sfp_sm_link_up(struct sfp *sfp) From 6912b712cd75aaa1c8d90b291ec7d2504f17f263 Mon Sep 17 00:00:00 2001 From: Russell King Date: Wed, 11 Dec 2019 10:56:20 +0000 Subject: [PATCH 06/14] net: mdio-i2c: add support for Clause 45 accesses Some SFP+ modules have PHYs on them just like SFP modules do, except they are Clause 45 PHYs. The I2C protocol used to access them is modified slightly in order to send the device address and 16-bit register index. Reviewed-by: Andrew Lunn Signed-off-by: Russell King Signed-off-by: David S. Miller --- drivers/net/phy/mdio-i2c.c | 28 ++++++++++++++++++++-------- 1 file changed, 20 insertions(+), 8 deletions(-) diff --git a/drivers/net/phy/mdio-i2c.c b/drivers/net/phy/mdio-i2c.c index 0dce67672548..0746e2cc39ae 100644 --- a/drivers/net/phy/mdio-i2c.c +++ b/drivers/net/phy/mdio-i2c.c @@ -33,17 +33,24 @@ static int i2c_mii_read(struct mii_bus *bus, int phy_id, int reg) { struct i2c_adapter *i2c = bus->priv; struct i2c_msg msgs[2]; - u8 data[2], dev_addr = reg; + u8 addr[3], data[2], *p; int bus_addr, ret; if (!i2c_mii_valid_phy_id(phy_id)) return 0xffff; + p = addr; + if (reg & MII_ADDR_C45) { + *p++ = 0x20 | ((reg >> 16) & 31); + *p++ = reg >> 8; + } + *p++ = reg; + bus_addr = i2c_mii_phy_addr(phy_id); msgs[0].addr = bus_addr; msgs[0].flags = 0; - msgs[0].len = 1; - msgs[0].buf = &dev_addr; + msgs[0].len = p - addr; + msgs[0].buf = addr; msgs[1].addr = bus_addr; msgs[1].flags = I2C_M_RD; msgs[1].len = sizeof(data); @@ -61,18 +68,23 @@ static int i2c_mii_write(struct mii_bus *bus, int phy_id, int reg, u16 val) struct i2c_adapter *i2c = bus->priv; struct i2c_msg msg; int ret; - u8 data[3]; + u8 data[5], *p; if (!i2c_mii_valid_phy_id(phy_id)) return 0; - data[0] = reg; - data[1] = val >> 8; - data[2] = val; + p = data; + if (reg & MII_ADDR_C45) { + *p++ = (reg >> 16) & 31; + *p++ = reg >> 8; + } + *p++ = reg; + *p++ = val >> 8; + *p++ = val; msg.addr = i2c_mii_phy_addr(phy_id); msg.flags = 0; - msg.len = 3; + msg.len = p - data; msg.buf = data; ret = i2c_transfer(i2c, &msg, 1); From 938d44c2b502ba421a785683653e24b685f5d637 Mon Sep 17 00:00:00 2001 From: Russell King Date: Wed, 11 Dec 2019 10:56:25 +0000 Subject: [PATCH 07/14] net: phylink: re-split __phylink_connect_phy() In order to support Clause 45 PHYs on SFP+ modules, which have an indeterminant phy interface mode, we need to be able to call phylink_bringup_phy() with a different interface mode to that used when binding the PHY. Reduce __phylink_connect_phy() to an attach operation, and move the call to phylink_bringup_phy() to its call sites. Reviewed-by: Andrew Lunn Signed-off-by: Russell King Signed-off-by: David S. Miller --- drivers/net/phy/phylink.c | 39 ++++++++++++++++++++++++--------------- 1 file changed, 24 insertions(+), 15 deletions(-) diff --git a/drivers/net/phy/phylink.c b/drivers/net/phy/phylink.c index a89219ac4490..e5c2a56e3711 100644 --- a/drivers/net/phy/phylink.c +++ b/drivers/net/phy/phylink.c @@ -764,11 +764,9 @@ static int phylink_bringup_phy(struct phylink *pl, struct phy_device *phy) return 0; } -static int __phylink_connect_phy(struct phylink *pl, struct phy_device *phy, - phy_interface_t interface) +static int phylink_attach_phy(struct phylink *pl, struct phy_device *phy, + phy_interface_t interface) { - int ret; - if (WARN_ON(pl->link_an_mode == MLO_AN_FIXED || (pl->link_an_mode == MLO_AN_INBAND && phy_interface_mode_is_8023z(interface)))) @@ -777,15 +775,7 @@ static int __phylink_connect_phy(struct phylink *pl, struct phy_device *phy, if (pl->phydev) return -EBUSY; - ret = phy_attach_direct(pl->netdev, phy, 0, interface); - if (ret) - return ret; - - ret = phylink_bringup_phy(pl, phy); - if (ret) - phy_detach(phy); - - return ret; + return phy_attach_direct(pl->netdev, phy, 0, interface); } /** @@ -805,13 +795,23 @@ static int __phylink_connect_phy(struct phylink *pl, struct phy_device *phy, */ int phylink_connect_phy(struct phylink *pl, struct phy_device *phy) { + int ret; + /* Use PHY device/driver interface */ if (pl->link_interface == PHY_INTERFACE_MODE_NA) { pl->link_interface = phy->interface; pl->link_config.interface = pl->link_interface; } - return __phylink_connect_phy(pl, phy, pl->link_interface); + ret = phylink_attach_phy(pl, phy, pl->link_interface); + if (ret < 0) + return ret; + + ret = phylink_bringup_phy(pl, phy); + if (ret) + phy_detach(phy); + + return ret; } EXPORT_SYMBOL_GPL(phylink_connect_phy); @@ -1809,8 +1809,17 @@ static void phylink_sfp_link_up(void *upstream) static int phylink_sfp_connect_phy(void *upstream, struct phy_device *phy) { struct phylink *pl = upstream; + int ret; - return __phylink_connect_phy(upstream, phy, pl->link_config.interface); + ret = phylink_attach_phy(pl, phy, pl->link_config.interface); + if (ret < 0) + return ret; + + ret = phylink_bringup_phy(pl, phy); + if (ret) + phy_detach(phy); + + return ret; } static void phylink_sfp_disconnect_phy(void *upstream) From e45d1f5288b81e77eb87288ae2d0a0d00e0989d3 Mon Sep 17 00:00:00 2001 From: Russell King Date: Wed, 11 Dec 2019 10:56:30 +0000 Subject: [PATCH 08/14] net: phylink: support Clause 45 PHYs on SFP+ modules Some SFP+ modules have Clause 45 PHYs embedded on them, which need a little more handling in order to ensure that they are correctly setup, as they switch the PHY link mode according to the negotiated speed. With Clause 22 PHYs, we assumed that they would operate in SGMII mode, but this assumption is now false. Adapt phylink to support Clause 45 PHYs on SFP+ modules. Reviewed-by: Andrew Lunn Signed-off-by: Russell King Signed-off-by: David S. Miller --- drivers/net/phy/phylink.c | 21 ++++++++++++++++----- 1 file changed, 16 insertions(+), 5 deletions(-) diff --git a/drivers/net/phy/phylink.c b/drivers/net/phy/phylink.c index e5c2a56e3711..aeb8a56a1323 100644 --- a/drivers/net/phy/phylink.c +++ b/drivers/net/phy/phylink.c @@ -711,7 +711,8 @@ static void phylink_phy_change(struct phy_device *phydev, bool up, phy_duplex_to_str(phydev->duplex)); } -static int phylink_bringup_phy(struct phylink *pl, struct phy_device *phy) +static int phylink_bringup_phy(struct phylink *pl, struct phy_device *phy, + phy_interface_t interface) { struct phylink_link_state config; __ETHTOOL_DECLARE_LINK_MODE_MASK(supported); @@ -729,7 +730,7 @@ static int phylink_bringup_phy(struct phylink *pl, struct phy_device *phy) memset(&config, 0, sizeof(config)); linkmode_copy(supported, phy->supported); linkmode_copy(config.advertising, phy->advertising); - config.interface = pl->link_config.interface; + config.interface = interface; ret = phylink_validate(pl, supported, &config); if (ret) @@ -745,6 +746,7 @@ static int phylink_bringup_phy(struct phylink *pl, struct phy_device *phy) mutex_lock(&phy->lock); mutex_lock(&pl->state_mutex); pl->phydev = phy; + pl->phy_state.interface = interface; linkmode_copy(pl->supported, supported); linkmode_copy(pl->link_config.advertising, config.advertising); @@ -807,7 +809,7 @@ int phylink_connect_phy(struct phylink *pl, struct phy_device *phy) if (ret < 0) return ret; - ret = phylink_bringup_phy(pl, phy); + ret = phylink_bringup_phy(pl, phy, pl->link_config.interface); if (ret) phy_detach(phy); @@ -860,7 +862,7 @@ int phylink_of_phy_connect(struct phylink *pl, struct device_node *dn, if (!phy_dev) return -ENODEV; - ret = phylink_bringup_phy(pl, phy_dev); + ret = phylink_bringup_phy(pl, phy_dev, pl->link_config.interface); if (ret) phy_detach(phy_dev); @@ -1809,13 +1811,22 @@ static void phylink_sfp_link_up(void *upstream) static int phylink_sfp_connect_phy(void *upstream, struct phy_device *phy) { struct phylink *pl = upstream; + phy_interface_t interface = pl->link_config.interface; int ret; ret = phylink_attach_phy(pl, phy, pl->link_config.interface); if (ret < 0) return ret; - ret = phylink_bringup_phy(pl, phy); + /* Clause 45 PHYs switch their Serdes lane between several different + * modes, normally 10GBASE-R, SGMII. Some use 2500BASE-X for 2.5G + * speeds. We really need to know which interface modes the PHY and + * MAC supports to properly work out which linkmodes can be supported. + */ + if (phy->is_c45) + interface = PHY_INTERFACE_MODE_NA; + + ret = phylink_bringup_phy(pl, phy, interface); if (ret) phy_detach(phy); From 24cf0e693bb50a61e3281009a329f7553a4fbd96 Mon Sep 17 00:00:00 2001 From: Russell King Date: Wed, 11 Dec 2019 10:56:35 +0000 Subject: [PATCH 09/14] net: phylink: split link_an_mode configured and current settings Split link_an_mode between the configured setting and the current operating setting. This is an important distinction to make when we need to configure PHY mode for a plugged SFP+ module that does not use in-band signalling. Reviewed-by: Andrew Lunn Signed-off-by: Russell King Signed-off-by: David S. Miller --- drivers/net/phy/phylink.c | 59 ++++++++++++++++++++------------------- 1 file changed, 31 insertions(+), 28 deletions(-) diff --git a/drivers/net/phy/phylink.c b/drivers/net/phy/phylink.c index aeb8a56a1323..bce530fc956d 100644 --- a/drivers/net/phy/phylink.c +++ b/drivers/net/phy/phylink.c @@ -48,7 +48,8 @@ struct phylink { unsigned long phylink_disable_state; /* bitmask of disables */ struct phy_device *phydev; phy_interface_t link_interface; /* PHY_INTERFACE_xxx */ - u8 link_an_mode; /* MLO_AN_xxx */ + u8 cfg_link_an_mode; /* MLO_AN_xxx */ + u8 cur_link_an_mode; u8 link_port; /* The current non-phy ethtool port */ __ETHTOOL_DECLARE_LINK_MODE_MASK(supported); @@ -256,12 +257,12 @@ static int phylink_parse_mode(struct phylink *pl, struct fwnode_handle *fwnode) dn = fwnode_get_named_child_node(fwnode, "fixed-link"); if (dn || fwnode_property_present(fwnode, "fixed-link")) - pl->link_an_mode = MLO_AN_FIXED; + pl->cfg_link_an_mode = MLO_AN_FIXED; fwnode_handle_put(dn); if (fwnode_property_read_string(fwnode, "managed", &managed) == 0 && strcmp(managed, "in-band-status") == 0) { - if (pl->link_an_mode == MLO_AN_FIXED) { + if (pl->cfg_link_an_mode == MLO_AN_FIXED) { phylink_err(pl, "can't use both fixed-link and in-band-status\n"); return -EINVAL; @@ -273,7 +274,7 @@ static int phylink_parse_mode(struct phylink *pl, struct fwnode_handle *fwnode) phylink_set(pl->supported, Asym_Pause); phylink_set(pl->supported, Pause); pl->link_config.an_enabled = true; - pl->link_an_mode = MLO_AN_INBAND; + pl->cfg_link_an_mode = MLO_AN_INBAND; switch (pl->link_config.interface) { case PHY_INTERFACE_MODE_SGMII: @@ -333,14 +334,14 @@ static void phylink_mac_config(struct phylink *pl, { phylink_dbg(pl, "%s: mode=%s/%s/%s/%s adv=%*pb pause=%02x link=%u an=%u\n", - __func__, phylink_an_mode_str(pl->link_an_mode), + __func__, phylink_an_mode_str(pl->cur_link_an_mode), phy_modes(state->interface), phy_speed_to_str(state->speed), phy_duplex_to_str(state->duplex), __ETHTOOL_LINK_MODE_MASK_NBITS, state->advertising, state->pause, state->link, state->an_enabled); - pl->ops->mac_config(pl->config, pl->link_an_mode, state); + pl->ops->mac_config(pl->config, pl->cur_link_an_mode, state); } static void phylink_mac_config_up(struct phylink *pl, @@ -441,7 +442,7 @@ static void phylink_mac_link_up(struct phylink *pl, struct net_device *ndev = pl->netdev; pl->cur_interface = link_state.interface; - pl->ops->mac_link_up(pl->config, pl->link_an_mode, + pl->ops->mac_link_up(pl->config, pl->cur_link_an_mode, pl->phy_state.interface, pl->phydev); @@ -461,7 +462,7 @@ static void phylink_mac_link_down(struct phylink *pl) if (ndev) netif_carrier_off(ndev); - pl->ops->mac_link_down(pl->config, pl->link_an_mode, + pl->ops->mac_link_down(pl->config, pl->cur_link_an_mode, pl->cur_interface); phylink_info(pl, "Link is Down\n"); } @@ -480,7 +481,7 @@ static void phylink_resolve(struct work_struct *w) } else if (pl->mac_link_dropped) { link_state.link = false; } else { - switch (pl->link_an_mode) { + switch (pl->cur_link_an_mode) { case MLO_AN_PHY: link_state = pl->phy_state; phylink_resolve_flow(pl, &link_state); @@ -648,7 +649,7 @@ struct phylink *phylink_create(struct phylink_config *config, return ERR_PTR(ret); } - if (pl->link_an_mode == MLO_AN_FIXED) { + if (pl->cfg_link_an_mode == MLO_AN_FIXED) { ret = phylink_parse_fixedlink(pl, fwnode); if (ret < 0) { kfree(pl); @@ -656,6 +657,8 @@ struct phylink *phylink_create(struct phylink_config *config, } } + pl->cur_link_an_mode = pl->cfg_link_an_mode; + ret = phylink_register_sfp(pl, fwnode); if (ret < 0) { kfree(pl); @@ -769,8 +772,8 @@ static int phylink_bringup_phy(struct phylink *pl, struct phy_device *phy, static int phylink_attach_phy(struct phylink *pl, struct phy_device *phy, phy_interface_t interface) { - if (WARN_ON(pl->link_an_mode == MLO_AN_FIXED || - (pl->link_an_mode == MLO_AN_INBAND && + if (WARN_ON(pl->cfg_link_an_mode == MLO_AN_FIXED || + (pl->cfg_link_an_mode == MLO_AN_INBAND && phy_interface_mode_is_8023z(interface)))) return -EINVAL; @@ -837,8 +840,8 @@ int phylink_of_phy_connect(struct phylink *pl, struct device_node *dn, int ret; /* Fixed links and 802.3z are handled without needing a PHY */ - if (pl->link_an_mode == MLO_AN_FIXED || - (pl->link_an_mode == MLO_AN_INBAND && + if (pl->cfg_link_an_mode == MLO_AN_FIXED || + (pl->cfg_link_an_mode == MLO_AN_INBAND && phy_interface_mode_is_8023z(pl->link_interface))) return 0; @@ -849,7 +852,7 @@ int phylink_of_phy_connect(struct phylink *pl, struct device_node *dn, phy_node = of_parse_phandle(dn, "phy-device", 0); if (!phy_node) { - if (pl->link_an_mode == MLO_AN_PHY) + if (pl->cfg_link_an_mode == MLO_AN_PHY) return -ENODEV; return 0; } @@ -912,7 +915,7 @@ int phylink_fixed_state_cb(struct phylink *pl, /* It does not make sense to let the link be overriden unless we use * MLO_AN_FIXED */ - if (pl->link_an_mode != MLO_AN_FIXED) + if (pl->cfg_link_an_mode != MLO_AN_FIXED) return -EINVAL; mutex_lock(&pl->state_mutex); @@ -962,7 +965,7 @@ void phylink_start(struct phylink *pl) ASSERT_RTNL(); phylink_info(pl, "configuring for %s/%s link mode\n", - phylink_an_mode_str(pl->link_an_mode), + phylink_an_mode_str(pl->cur_link_an_mode), phy_modes(pl->link_config.interface)); /* Always set the carrier off */ @@ -985,7 +988,7 @@ 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 && pl->link_gpio) { + if (pl->cfg_link_an_mode == MLO_AN_FIXED && pl->link_gpio) { int irq = gpiod_to_irq(pl->link_gpio); if (irq > 0) { @@ -1000,7 +1003,7 @@ void phylink_start(struct phylink *pl) if (irq <= 0) mod_timer(&pl->link_poll, jiffies + HZ); } - if (pl->link_an_mode == MLO_AN_FIXED && pl->get_fixed_state) + if (pl->cfg_link_an_mode == MLO_AN_FIXED && pl->get_fixed_state) mod_timer(&pl->link_poll, jiffies + HZ); if (pl->phydev) phy_start(pl->phydev); @@ -1127,7 +1130,7 @@ int phylink_ethtool_ksettings_get(struct phylink *pl, linkmode_copy(kset->link_modes.supported, pl->supported); - switch (pl->link_an_mode) { + switch (pl->cur_link_an_mode) { case MLO_AN_FIXED: /* We are using fixed settings. Report these as the * current link settings - and note that these also @@ -1199,7 +1202,7 @@ int phylink_ethtool_ksettings_set(struct phylink *pl, /* If we have a fixed link (as specified by firmware), refuse * to change link parameters. */ - if (pl->link_an_mode == MLO_AN_FIXED && + if (pl->cur_link_an_mode == MLO_AN_FIXED && (s->speed != pl->link_config.speed || s->duplex != pl->link_config.duplex)) return -EINVAL; @@ -1211,7 +1214,7 @@ int phylink_ethtool_ksettings_set(struct phylink *pl, __clear_bit(ETHTOOL_LINK_MODE_Autoneg_BIT, config.advertising); } else { /* If we have a fixed link, refuse to enable autonegotiation */ - if (pl->link_an_mode == MLO_AN_FIXED) + if (pl->cur_link_an_mode == MLO_AN_FIXED) return -EINVAL; config.speed = SPEED_UNKNOWN; @@ -1253,7 +1256,7 @@ int phylink_ethtool_ksettings_set(struct phylink *pl, * 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 && + if (pl->cur_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); @@ -1343,7 +1346,7 @@ int phylink_ethtool_set_pauseparam(struct phylink *pl, pause->tx_pause); } else if (!test_bit(PHYLINK_DISABLE_STOPPED, &pl->phylink_disable_state)) { - switch (pl->link_an_mode) { + switch (pl->cur_link_an_mode) { case MLO_AN_FIXED: /* Should we allow fixed links to change against the config? */ phylink_resolve_flow(pl, config); @@ -1550,7 +1553,7 @@ static int phylink_mii_read(struct phylink *pl, unsigned int phy_id, struct phylink_link_state state; int val = 0xffff; - switch (pl->link_an_mode) { + switch (pl->cur_link_an_mode) { case MLO_AN_FIXED: if (phy_id == 0) { phylink_get_fixed_state(pl, &state); @@ -1575,7 +1578,7 @@ static int phylink_mii_read(struct phylink *pl, unsigned int phy_id, static int phylink_mii_write(struct phylink *pl, unsigned int phy_id, unsigned int reg, unsigned int val) { - switch (pl->link_an_mode) { + switch (pl->cur_link_an_mode) { case MLO_AN_FIXED: break; @@ -1748,10 +1751,10 @@ static int phylink_sfp_module_insert(void *upstream, linkmode_copy(pl->link_config.advertising, config.advertising); } - if (pl->link_an_mode != MLO_AN_INBAND || + if (pl->cur_link_an_mode != MLO_AN_INBAND || pl->link_config.interface != config.interface) { pl->link_config.interface = config.interface; - pl->link_an_mode = MLO_AN_INBAND; + pl->cur_link_an_mode = MLO_AN_INBAND; changed = true; From c0de2f47202941d936f0482ccd5b010b642a0008 Mon Sep 17 00:00:00 2001 From: Russell King Date: Wed, 11 Dec 2019 10:56:40 +0000 Subject: [PATCH 10/14] net: phylink: split phylink_sfp_module_insert() Split out the configuration step from phylink_sfp_module_insert() so we can re-use this later. Reviewed-by: Andrew Lunn Signed-off-by: Russell King Signed-off-by: David S. Miller --- drivers/net/phy/phylink.c | 47 +++++++++++++++++++++++---------------- 1 file changed, 28 insertions(+), 19 deletions(-) diff --git a/drivers/net/phy/phylink.c b/drivers/net/phy/phylink.c index bce530fc956d..571521d928f2 100644 --- a/drivers/net/phy/phylink.c +++ b/drivers/net/phy/phylink.c @@ -1684,25 +1684,21 @@ static void phylink_sfp_detach(void *upstream, struct sfp_bus *bus) pl->netdev->sfp_bus = NULL; } -static int phylink_sfp_module_insert(void *upstream, - const struct sfp_eeprom_id *id) +static int phylink_sfp_config(struct phylink *pl, u8 mode, u8 port, + const unsigned long *supported, + const unsigned long *advertising) { - struct phylink *pl = upstream; - __ETHTOOL_DECLARE_LINK_MODE_MASK(support) = { 0, }; __ETHTOOL_DECLARE_LINK_MODE_MASK(support1); + __ETHTOOL_DECLARE_LINK_MODE_MASK(support); struct phylink_link_state config; phy_interface_t iface; - int ret = 0; bool changed; - u8 port; + int ret; - ASSERT_RTNL(); - - sfp_parse_support(pl->sfp_bus, id, support); - port = sfp_parse_port(pl->sfp_bus, id, support); + linkmode_copy(support, supported); memset(&config, 0, sizeof(config)); - linkmode_copy(config.advertising, support); + linkmode_copy(config.advertising, advertising); config.interface = PHY_INTERFACE_MODE_NA; config.speed = SPEED_UNKNOWN; config.duplex = DUPLEX_UNKNOWN; @@ -1717,8 +1713,6 @@ static int phylink_sfp_module_insert(void *upstream, return ret; } - linkmode_copy(support1, support); - iface = sfp_select_interface(pl->sfp_bus, config.advertising); if (iface == PHY_INTERFACE_MODE_NA) { phylink_err(pl, @@ -1728,18 +1722,18 @@ static int phylink_sfp_module_insert(void *upstream, } config.interface = iface; + linkmode_copy(support1, support); ret = phylink_validate(pl, support1, &config); if (ret) { phylink_err(pl, "validation of %s/%s with support %*pb failed: %d\n", - phylink_an_mode_str(MLO_AN_INBAND), + phylink_an_mode_str(mode), phy_modes(config.interface), __ETHTOOL_LINK_MODE_MASK_NBITS, support, ret); return ret; } phylink_dbg(pl, "requesting link mode %s/%s with support %*pb\n", - phylink_an_mode_str(MLO_AN_INBAND), - phy_modes(config.interface), + phylink_an_mode_str(mode), phy_modes(config.interface), __ETHTOOL_LINK_MODE_MASK_NBITS, support); if (phy_interface_mode_is_8023z(iface) && pl->phydev) @@ -1751,15 +1745,15 @@ static int phylink_sfp_module_insert(void *upstream, linkmode_copy(pl->link_config.advertising, config.advertising); } - if (pl->cur_link_an_mode != MLO_AN_INBAND || + if (pl->cur_link_an_mode != mode || pl->link_config.interface != config.interface) { pl->link_config.interface = config.interface; - pl->cur_link_an_mode = MLO_AN_INBAND; + pl->cur_link_an_mode = mode; changed = true; phylink_info(pl, "switched to %s/%s link mode\n", - phylink_an_mode_str(MLO_AN_INBAND), + phylink_an_mode_str(mode), phy_modes(config.interface)); } @@ -1772,6 +1766,21 @@ static int phylink_sfp_module_insert(void *upstream, return ret; } +static int phylink_sfp_module_insert(void *upstream, + const struct sfp_eeprom_id *id) +{ + struct phylink *pl = upstream; + __ETHTOOL_DECLARE_LINK_MODE_MASK(support) = { 0, }; + u8 port; + + ASSERT_RTNL(); + + sfp_parse_support(pl->sfp_bus, id, support); + port = sfp_parse_port(pl->sfp_bus, id, support); + + return phylink_sfp_config(pl, MLO_AN_INBAND, port, support, support); +} + static int phylink_sfp_module_start(void *upstream) { struct phylink *pl = upstream; From 52c956003a9d5bcae1f445f9dfd42b624adb6e87 Mon Sep 17 00:00:00 2001 From: Russell King Date: Wed, 11 Dec 2019 10:56:45 +0000 Subject: [PATCH 11/14] net: phylink: delay MAC configuration for copper SFP modules Knowing whether we need to delay the MAC configuration because a module may have a PHY is useful to phylink to allow NBASE-T modules to work on systems supporting no more than 2.5G speeds. This commit allows us to delay such configuration until after the PHY has been probed by recording the parsed capabilities, and if the module may have a PHY, doing no more until the module_start() notification is called. At that point, we either have a PHY, or we don't. We move the PHY-based setup a little later, and use the PHYs support capabilities rather than the EEPROM parsed capabilities to determine whether we can support the PHY. Reviewed-by: Andrew Lunn Signed-off-by: Russell King Signed-off-by: David S. Miller --- drivers/net/phy/phylink.c | 53 +++++++++++++++++++++++++++++++-------- drivers/net/phy/sfp-bus.c | 28 +++++++++++++++++++++ include/linux/sfp.h | 7 ++++++ 3 files changed, 78 insertions(+), 10 deletions(-) diff --git a/drivers/net/phy/phylink.c b/drivers/net/phy/phylink.c index 571521d928f2..01f958508f80 100644 --- a/drivers/net/phy/phylink.c +++ b/drivers/net/phy/phylink.c @@ -72,6 +72,9 @@ struct phylink { bool mac_link_dropped; struct sfp_bus *sfp_bus; + bool sfp_may_have_phy; + __ETHTOOL_DECLARE_LINK_MODE_MASK(sfp_support); + u8 sfp_port; }; #define phylink_printk(level, pl, fmt, ...) \ @@ -1684,7 +1687,7 @@ static void phylink_sfp_detach(void *upstream, struct sfp_bus *bus) pl->netdev->sfp_bus = NULL; } -static int phylink_sfp_config(struct phylink *pl, u8 mode, u8 port, +static int phylink_sfp_config(struct phylink *pl, u8 mode, const unsigned long *supported, const unsigned long *advertising) { @@ -1757,7 +1760,7 @@ static int phylink_sfp_config(struct phylink *pl, u8 mode, u8 port, phy_modes(config.interface)); } - pl->link_port = port; + pl->link_port = pl->sfp_port; if (changed && !test_bit(PHYLINK_DISABLE_STOPPED, &pl->phylink_disable_state)) @@ -1770,15 +1773,20 @@ static int phylink_sfp_module_insert(void *upstream, const struct sfp_eeprom_id *id) { struct phylink *pl = upstream; - __ETHTOOL_DECLARE_LINK_MODE_MASK(support) = { 0, }; - u8 port; + unsigned long *support = pl->sfp_support; ASSERT_RTNL(); + linkmode_zero(support); sfp_parse_support(pl->sfp_bus, id, support); - port = sfp_parse_port(pl->sfp_bus, id, support); + pl->sfp_port = sfp_parse_port(pl->sfp_bus, id, support); - return phylink_sfp_config(pl, MLO_AN_INBAND, port, support, support); + /* If this module may have a PHY connecting later, defer until later */ + pl->sfp_may_have_phy = sfp_may_have_phy(pl->sfp_bus, id); + if (pl->sfp_may_have_phy) + return 0; + + return phylink_sfp_config(pl, MLO_AN_INBAND, support, support); } static int phylink_sfp_module_start(void *upstream) @@ -1786,10 +1794,19 @@ static int phylink_sfp_module_start(void *upstream) struct phylink *pl = upstream; /* If this SFP module has a PHY, start the PHY now. */ - if (pl->phydev) + if (pl->phydev) { phy_start(pl->phydev); + return 0; + } - return 0; + /* If the module may have a PHY but we didn't detect one we + * need to configure the MAC here. + */ + if (!pl->sfp_may_have_phy) + return 0; + + return phylink_sfp_config(pl, MLO_AN_INBAND, + pl->sfp_support, pl->sfp_support); } static void phylink_sfp_module_stop(void *upstream) @@ -1823,10 +1840,26 @@ static void phylink_sfp_link_up(void *upstream) static int phylink_sfp_connect_phy(void *upstream, struct phy_device *phy) { struct phylink *pl = upstream; - phy_interface_t interface = pl->link_config.interface; + phy_interface_t interface; int ret; - ret = phylink_attach_phy(pl, phy, 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: + * phy drivers should not set SUPPORTED_[Asym_]Pause") except + * using our validate call to the MAC, we rely upon the MAC + * clearing the bits from both supported and advertising fields. + */ + phy_support_asym_pause(phy); + + /* Do the initial configuration */ + ret = phylink_sfp_config(pl, MLO_AN_INBAND, phy->supported, + phy->advertising); + if (ret < 0) + return ret; + + interface = pl->link_config.interface; + ret = phylink_attach_phy(pl, phy, interface); if (ret < 0) return ret; diff --git a/drivers/net/phy/sfp-bus.c b/drivers/net/phy/sfp-bus.c index eabc9e3f0a9e..06e6429b8b71 100644 --- a/drivers/net/phy/sfp-bus.c +++ b/drivers/net/phy/sfp-bus.c @@ -103,6 +103,7 @@ static const struct sfp_quirk *sfp_lookup_quirk(const struct sfp_eeprom_id *id) 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 @@ -178,6 +179,33 @@ int sfp_parse_port(struct sfp_bus *bus, const struct sfp_eeprom_id *id, } EXPORT_SYMBOL_GPL(sfp_parse_port); +/** + * sfp_may_have_phy() - indicate whether the module may have a PHY + * @bus: a pointer to the &struct sfp_bus structure for the sfp module + * @id: a pointer to the module's &struct sfp_eeprom_id + * + * Parse the EEPROM identification given in @id, and return whether + * this module may have a PHY. + */ +bool sfp_may_have_phy(struct sfp_bus *bus, const struct sfp_eeprom_id *id) +{ + if (id->base.e1000_base_t) + return true; + + if (id->base.phys_id != SFF8024_ID_DWDM_SFP) { + switch (id->base.extended_cc) { + case SFF8024_ECC_10GBASE_T_SFI: + case SFF8024_ECC_10GBASE_T_SR: + case SFF8024_ECC_5GBASE_T: + case SFF8024_ECC_2_5GBASE_T: + return true; + } + } + + return false; +} +EXPORT_SYMBOL_GPL(sfp_may_have_phy); + /** * sfp_parse_support() - Parse the eeprom id for supported link modes * @bus: a pointer to the &struct sfp_bus structure for the sfp module diff --git a/include/linux/sfp.h b/include/linux/sfp.h index 66a56396e8e3..38893e4dd0f0 100644 --- a/include/linux/sfp.h +++ b/include/linux/sfp.h @@ -533,6 +533,7 @@ struct sfp_upstream_ops { #if IS_ENABLED(CONFIG_SFP) int sfp_parse_port(struct sfp_bus *bus, const struct sfp_eeprom_id *id, unsigned long *support); +bool sfp_may_have_phy(struct sfp_bus *bus, const struct sfp_eeprom_id *id); void sfp_parse_support(struct sfp_bus *bus, const struct sfp_eeprom_id *id, unsigned long *support); phy_interface_t sfp_select_interface(struct sfp_bus *bus, @@ -556,6 +557,12 @@ static inline int sfp_parse_port(struct sfp_bus *bus, return PORT_OTHER; } +static inline bool sfp_may_have_phy(struct sfp_bus *bus, + const struct sfp_eeprom_id *id) +{ + return false; +} + static inline void sfp_parse_support(struct sfp_bus *bus, const struct sfp_eeprom_id *id, unsigned long *support) From 7adb5b2126bc013f0964ddaefad6ad1b132e86c3 Mon Sep 17 00:00:00 2001 From: Russell King Date: Wed, 11 Dec 2019 10:56:50 +0000 Subject: [PATCH 12/14] net: phylink: make Broadcom BCM84881 based SFPs work The Broadcom BCM84881 does not appear to send the SGMII control word when operating in SGMII mode, which causes network adapters to fail to link with the PHY, or decide to operate at fixed 1G speed, even if the PHY negotiated 100M. Work around this by detecting the Broadcom BCM84881 and switch to phy mode rather than inband mode. Reviewed-by: Florian Fainelli Signed-off-by: Russell King Signed-off-by: David S. Miller --- drivers/net/phy/phylink.c | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/drivers/net/phy/phylink.c b/drivers/net/phy/phylink.c index 01f958508f80..47614f975344 100644 --- a/drivers/net/phy/phylink.c +++ b/drivers/net/phy/phylink.c @@ -1837,10 +1837,20 @@ static void phylink_sfp_link_up(void *upstream) phylink_run_resolve(pl); } +/* The Broadcom BCM84881 in the Methode DM7052 is unable to provide a SGMII + * or 802.3z control word, so inband will not work. + */ +static bool phylink_phy_no_inband(struct phy_device *phy) +{ + return phy->is_c45 && + (phy->c45_ids.device_ids[1] & 0xfffffff0) == 0xae025150; +} + static int phylink_sfp_connect_phy(void *upstream, struct phy_device *phy) { struct phylink *pl = upstream; phy_interface_t interface; + u8 mode; int ret; /* @@ -1852,9 +1862,13 @@ static int phylink_sfp_connect_phy(void *upstream, struct phy_device *phy) */ phy_support_asym_pause(phy); + if (phylink_phy_no_inband(phy)) + mode = MLO_AN_PHY; + else + mode = MLO_AN_INBAND; + /* Do the initial configuration */ - ret = phylink_sfp_config(pl, MLO_AN_INBAND, phy->supported, - phy->advertising); + ret = phylink_sfp_config(pl, mode, phy->supported, phy->advertising); if (ret < 0) return ret; From 75f4d8d10e016f7428c268424483a927ee7a78bb Mon Sep 17 00:00:00 2001 From: Russell King Date: Wed, 11 Dec 2019 10:56:56 +0000 Subject: [PATCH 13/14] net: phy: add Broadcom BCM84881 PHY driver Add a rudimentary Clause 45 driver for the BCM84881 PHY, found on Methode DM7052 SFPs. Reviewed-by: Florian Fainelli Signed-off-by: Russell King Signed-off-by: David S. Miller --- drivers/net/phy/Kconfig | 6 + drivers/net/phy/Makefile | 1 + drivers/net/phy/bcm84881.c | 269 +++++++++++++++++++++++++++++++++++++ 3 files changed, 276 insertions(+) create mode 100644 drivers/net/phy/bcm84881.c diff --git a/drivers/net/phy/Kconfig b/drivers/net/phy/Kconfig index 5848219005d7..2e016271e126 100644 --- a/drivers/net/phy/Kconfig +++ b/drivers/net/phy/Kconfig @@ -324,6 +324,12 @@ config BROADCOM_PHY Currently supports the BCM5411, BCM5421, BCM5461, BCM54616S, BCM5464, BCM5481, BCM54810 and BCM5482 PHYs. +config BCM84881_PHY + bool "Broadcom BCM84881 PHY" + depends on PHYLIB=y + ---help--- + Support the Broadcom BCM84881 PHY. + config CICADA_PHY tristate "Cicada PHYs" ---help--- diff --git a/drivers/net/phy/Makefile b/drivers/net/phy/Makefile index b433ec3bf9a6..d846b4dc1c68 100644 --- a/drivers/net/phy/Makefile +++ b/drivers/net/phy/Makefile @@ -62,6 +62,7 @@ obj-$(CONFIG_BCM87XX_PHY) += bcm87xx.o obj-$(CONFIG_BCM_CYGNUS_PHY) += bcm-cygnus.o obj-$(CONFIG_BCM_NET_PHYLIB) += bcm-phy-lib.o obj-$(CONFIG_BROADCOM_PHY) += broadcom.o +obj-$(CONFIG_BCM84881_PHY) += bcm84881.o obj-$(CONFIG_CICADA_PHY) += cicada.o obj-$(CONFIG_CORTINA_PHY) += cortina.o obj-$(CONFIG_DAVICOM_PHY) += davicom.o diff --git a/drivers/net/phy/bcm84881.c b/drivers/net/phy/bcm84881.c new file mode 100644 index 000000000000..db59911b9b3c --- /dev/null +++ b/drivers/net/phy/bcm84881.c @@ -0,0 +1,269 @@ +// SPDX-License-Identifier: GPL-2.0 +// Broadcom BCM84881 NBASE-T PHY driver, as found on a SFP+ module. +// Copyright (C) 2019 Russell King, Deep Blue Solutions Ltd. +// +// Like the Marvell 88x3310, the Broadcom 84881 changes its host-side +// interface according to the operating speed between 10GBASE-R, +// 2500BASE-X and SGMII (but unlike the 88x3310, without the control +// word). +// +// This driver only supports those aspects of the PHY that I'm able to +// observe and test with the SFP+ module, which is an incomplete subset +// of what this PHY is able to support. For example, I only assume it +// supports a single lane Serdes connection, but it may be that the PHY +// is able to support more than that. +#include +#include +#include + +enum { + MDIO_AN_C22 = 0xffe0, +}; + +static int bcm84881_wait_init(struct phy_device *phydev) +{ + unsigned int tries = 20; + int ret, val; + + do { + val = phy_read_mmd(phydev, MDIO_MMD_PMAPMD, MDIO_CTRL1); + if (val < 0) { + ret = val; + break; + } + if (!(val & MDIO_CTRL1_RESET)) { + ret = 0; + break; + } + if (!--tries) { + ret = -ETIMEDOUT; + break; + } + msleep(100); + } while (1); + + if (ret) + phydev_err(phydev, "%s failed: %d\n", __func__, ret); + + return ret; +} + +static int bcm84881_config_init(struct phy_device *phydev) +{ + switch (phydev->interface) { + case PHY_INTERFACE_MODE_SGMII: + case PHY_INTERFACE_MODE_2500BASEX: + case PHY_INTERFACE_MODE_10GKR: + break; + default: + return -ENODEV; + } + return 0; +} + +static int bcm84881_probe(struct phy_device *phydev) +{ + /* This driver requires PMAPMD and AN blocks */ + const u32 mmd_mask = MDIO_DEVS_PMAPMD | MDIO_DEVS_AN; + + if (!phydev->is_c45 || + (phydev->c45_ids.devices_in_package & mmd_mask) != mmd_mask) + return -ENODEV; + + return 0; +} + +static int bcm84881_get_features(struct phy_device *phydev) +{ + int ret; + + ret = genphy_c45_pma_read_abilities(phydev); + if (ret) + return ret; + + /* Although the PHY sets bit 1.11.8, it does not support 10M modes */ + linkmode_clear_bit(ETHTOOL_LINK_MODE_10baseT_Half_BIT, + phydev->supported); + linkmode_clear_bit(ETHTOOL_LINK_MODE_10baseT_Full_BIT, + phydev->supported); + + return 0; +} + +static int bcm84881_config_aneg(struct phy_device *phydev) +{ + bool changed = false; + u32 adv; + int ret; + + /* Wait for the PHY to finish initialising, otherwise our + * advertisement may be overwritten. + */ + ret = bcm84881_wait_init(phydev); + if (ret) + return ret; + + /* We don't support manual MDI control */ + phydev->mdix_ctrl = ETH_TP_MDI_AUTO; + + /* disabled autoneg doesn't seem to work with this PHY */ + if (phydev->autoneg == AUTONEG_DISABLE) + return -EINVAL; + + ret = genphy_c45_an_config_aneg(phydev); + if (ret < 0) + return ret; + if (ret > 0) + changed = true; + + adv = linkmode_adv_to_mii_ctrl1000_t(phydev->advertising); + ret = phy_modify_mmd_changed(phydev, MDIO_MMD_AN, + MDIO_AN_C22 + MII_CTRL1000, + ADVERTISE_1000FULL | ADVERTISE_1000HALF, + adv); + if (ret < 0) + return ret; + if (ret > 0) + changed = true; + + return genphy_c45_check_and_restart_aneg(phydev, changed); +} + +static int bcm84881_aneg_done(struct phy_device *phydev) +{ + int bmsr, val; + + val = phy_read_mmd(phydev, MDIO_MMD_AN, MDIO_STAT1); + if (val < 0) + return val; + + bmsr = phy_read_mmd(phydev, MDIO_MMD_AN, MDIO_AN_C22 + MII_BMSR); + if (bmsr < 0) + return val; + + return !!(val & MDIO_AN_STAT1_COMPLETE) && + !!(bmsr & BMSR_ANEGCOMPLETE); +} + +static int bcm84881_read_status(struct phy_device *phydev) +{ + unsigned int mode; + int bmsr, val; + + val = phy_read_mmd(phydev, MDIO_MMD_AN, MDIO_CTRL1); + if (val < 0) + return val; + + if (val & MDIO_AN_CTRL1_RESTART) { + phydev->link = 0; + return 0; + } + + val = phy_read_mmd(phydev, MDIO_MMD_AN, MDIO_STAT1); + if (val < 0) + return val; + + bmsr = phy_read_mmd(phydev, MDIO_MMD_AN, MDIO_AN_C22 + MII_BMSR); + if (bmsr < 0) + return val; + + phydev->autoneg_complete = !!(val & MDIO_AN_STAT1_COMPLETE) && + !!(bmsr & BMSR_ANEGCOMPLETE); + phydev->link = !!(val & MDIO_STAT1_LSTATUS) && + !!(bmsr & BMSR_LSTATUS); + if (phydev->autoneg == AUTONEG_ENABLE && !phydev->autoneg_complete) + phydev->link = false; + + if (!phydev->link) + return 0; + + linkmode_zero(phydev->lp_advertising); + phydev->speed = SPEED_UNKNOWN; + phydev->duplex = DUPLEX_UNKNOWN; + phydev->pause = 0; + phydev->asym_pause = 0; + phydev->mdix = 0; + + if (phydev->autoneg_complete) { + val = genphy_c45_read_lpa(phydev); + if (val < 0) + return val; + + val = phy_read_mmd(phydev, MDIO_MMD_AN, + MDIO_AN_C22 + MII_STAT1000); + if (val < 0) + return val; + + mii_stat1000_mod_linkmode_lpa_t(phydev->lp_advertising, val); + + if (phydev->autoneg == AUTONEG_ENABLE) + phy_resolve_aneg_linkmode(phydev); + } + + if (phydev->autoneg == AUTONEG_DISABLE) { + /* disabled autoneg doesn't seem to work, so force the link + * down. + */ + phydev->link = 0; + return 0; + } + + /* Set the host link mode - we set the phy interface mode and + * the speed according to this register so that downshift works. + * We leave the duplex setting as per the resolution from the + * above. + */ + val = phy_read_mmd(phydev, MDIO_MMD_VEND1, 0x4011); + mode = (val & 0x1e) >> 1; + if (mode == 1 || mode == 2) + phydev->interface = PHY_INTERFACE_MODE_SGMII; + else if (mode == 3) + phydev->interface = PHY_INTERFACE_MODE_10GKR; + else if (mode == 4) + phydev->interface = PHY_INTERFACE_MODE_2500BASEX; + switch (mode & 7) { + case 1: + phydev->speed = SPEED_100; + break; + case 2: + phydev->speed = SPEED_1000; + break; + case 3: + phydev->speed = SPEED_10000; + break; + case 4: + phydev->speed = SPEED_2500; + break; + case 5: + phydev->speed = SPEED_5000; + break; + } + + return genphy_c45_read_mdix(phydev); +} + +static struct phy_driver bcm84881_drivers[] = { + { + .phy_id = 0xae025150, + .phy_id_mask = 0xfffffff0, + .name = "Broadcom BCM84881", + .config_init = bcm84881_config_init, + .probe = bcm84881_probe, + .get_features = bcm84881_get_features, + .config_aneg = bcm84881_config_aneg, + .aneg_done = bcm84881_aneg_done, + .read_status = bcm84881_read_status, + }, +}; + +module_phy_driver(bcm84881_drivers); + +/* FIXME: module auto-loading for Clause 45 PHYs seems non-functional */ +static struct mdio_device_id __maybe_unused bcm84881_tbl[] = { + { 0xae025150, 0xfffffff0 }, + { }, +}; +MODULE_AUTHOR("Russell King"); +MODULE_DESCRIPTION("Broadcom BCM84881 PHY driver"); +MODULE_DEVICE_TABLE(mdio, bcm84881_tbl); +MODULE_LICENSE("GPL"); From 9a484621e88ac8386f131596ce8d76668daf7996 Mon Sep 17 00:00:00 2001 From: Russell King Date: Wed, 11 Dec 2019 10:57:01 +0000 Subject: [PATCH 14/14] net: sfp: add support for Clause 45 PHYs Some SFP+ modules have a Clause 45 PHY onboard, which is accessible via the normal I2C address. Detect 10G BASE-T PHYs which may have an accessible PHY and probe for it. Reviewed-by: Andrew Lunn Signed-off-by: Russell King Signed-off-by: David S. Miller --- drivers/net/phy/sfp.c | 44 +++++++++++++++++++++++++++++++++++++++---- 1 file changed, 40 insertions(+), 4 deletions(-) diff --git a/drivers/net/phy/sfp.c b/drivers/net/phy/sfp.c index d7d2c797c89c..bfe268028154 100644 --- a/drivers/net/phy/sfp.c +++ b/drivers/net/phy/sfp.c @@ -1402,12 +1402,12 @@ static void sfp_sm_phy_detach(struct sfp *sfp) sfp->mod_phy = NULL; } -static void sfp_sm_probe_phy(struct sfp *sfp) +static void sfp_sm_probe_phy(struct sfp *sfp, bool is_c45) { struct phy_device *phy; int err; - phy = mdiobus_scan(sfp->i2c_mii, SFP_PHY_ADDR); + phy = get_phy_device(sfp->i2c_mii, SFP_PHY_ADDR, is_c45); if (phy == ERR_PTR(-ENODEV)) { dev_info(sfp->dev, "no PHY detected\n"); return; @@ -1417,6 +1417,13 @@ static void sfp_sm_probe_phy(struct sfp *sfp) return; } + err = phy_device_register(phy); + if (err) { + phy_device_free(phy); + dev_err(sfp->dev, "phy_device_register failed: %d\n", err); + return; + } + err = sfp_add_phy(sfp->sfp_bus, phy); if (err) { phy_device_remove(phy); @@ -1487,10 +1494,32 @@ static void sfp_sm_fault(struct sfp *sfp, unsigned int next_state, bool warn) } } +/* Probe a SFP for a PHY device if the module supports copper - the PHY + * normally sits at I2C bus address 0x56, and may either be a clause 22 + * or clause 45 PHY. + * + * Clause 22 copper SFP modules normally operate in Cisco SGMII mode with + * negotiation enabled, but some may be in 1000base-X - which is for the + * PHY driver to determine. + * + * Clause 45 copper SFP+ modules (10G) appear to switch their interface + * mode according to the negotiated line speed. + */ static void sfp_sm_probe_for_phy(struct sfp *sfp) { - if (sfp->id.base.e1000_base_t) - sfp_sm_probe_phy(sfp); + switch (sfp->id.base.extended_cc) { + case SFF8024_ECC_10GBASE_T_SFI: + case SFF8024_ECC_10GBASE_T_SR: + case SFF8024_ECC_5GBASE_T: + case SFF8024_ECC_2_5GBASE_T: + sfp_sm_probe_phy(sfp, true); + break; + + default: + if (sfp->id.base.e1000_base_t) + sfp_sm_probe_phy(sfp, false); + break; + } } static int sfp_module_parse_power(struct sfp *sfp) @@ -1550,6 +1579,13 @@ static int sfp_sm_mod_hpower(struct sfp *sfp, bool enable) return -EAGAIN; } + /* DM7052 reports as a high power module, responds to reads (with + * all bytes 0xff) at 0x51 but does not accept writes. In any case, + * if the bit is already set, we're already in high power mode. + */ + if (!!(val & BIT(0)) == enable) + return 0; + if (enable) val |= BIT(0); else