From bedbef8ad3e75a304af6361af235302bcc61d06b Mon Sep 17 00:00:00 2001 From: hc <hc@nodka.com> Date: Tue, 14 May 2024 06:39:01 +0000 Subject: [PATCH] 修改内核路径 --- kernel/drivers/net/phy/sfp-bus.c | 317 ++++++++++++++++++++++++++++++++++++++-------------- 1 files changed, 228 insertions(+), 89 deletions(-) diff --git a/kernel/drivers/net/phy/sfp-bus.c b/kernel/drivers/net/phy/sfp-bus.c index 1fe7783..850915a 100644 --- a/kernel/drivers/net/phy/sfp-bus.c +++ b/kernel/drivers/net/phy/sfp-bus.c @@ -1,8 +1,10 @@ +// SPDX-License-Identifier: GPL-2.0-only #include <linux/export.h> #include <linux/kref.h> #include <linux/list.h> #include <linux/mutex.h> #include <linux/phylink.h> +#include <linux/property.h> #include <linux/rtnetlink.h> #include <linux/slab.h> @@ -30,7 +32,6 @@ const struct sfp_upstream_ops *upstream_ops; void *upstream; - struct net_device *netdev; struct phy_device *phydev; bool registered; @@ -41,6 +42,17 @@ unsigned long *modes) { phylink_set(modes, 2500baseX_Full); +} + +static void sfp_quirk_ubnt_uf_instant(const struct sfp_eeprom_id *id, + unsigned long *modes) +{ + /* Ubiquiti U-Fiber Instant module claims that support all transceiver + * types including 10G Ethernet which is not truth. So clear all claimed + * modes and set only one mode which module supports: 1000baseX_Full. + */ + phylink_zero(modes); + phylink_set(modes, 1000baseX_Full); } static const struct sfp_quirk sfp_quirks[] = { @@ -62,6 +74,16 @@ .vendor = "HUAWEI", .part = "MA5671A", .modes = sfp_quirk_2500basex, + }, { + // Lantech 8330-262D-E can operate at 2500base-X, but + // incorrectly report 2500MBd NRZ in their EEPROM + .vendor = "Lantech", + .part = "8330-262D-E", + .modes = sfp_quirk_2500basex, + }, { + .vendor = "UBNT", + .part = "UF-INSTANT", + .modes = sfp_quirk_ubnt_uf_instant, }, }; @@ -102,6 +124,7 @@ 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 @@ -123,35 +146,35 @@ /* 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: + fallthrough; + case SFF8024_CONNECTOR_SG: /* guess */ + case SFF8024_CONNECTOR_HSSDC_II: + case SFF8024_CONNECTOR_NOSEPARATE: + case SFF8024_CONNECTOR_MXC_2X16: port = PORT_OTHER; break; default: @@ -176,6 +199,33 @@ return port; } 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 @@ -260,21 +310,32 @@ } 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, @@ -294,14 +355,13 @@ } /* If we haven't discovered any modes that this module supports, try - * the encoding and bitrate to determine supported modes. Some BiDi - * modules (eg, 1310nm/1550nm) are not 1000BASE-BX compliant due to - * the differing wavelengths, so do not set any transceiver bits. + * the bitrate to determine supported modes. Some BiDi modules (eg, + * 1310nm/1550nm) are not 1000BASE-BX compliant due to the differing + * wavelengths, so do not set any transceiver bits. */ 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 && - br_min <= 1300 && br_max >= 1200) + /* If the bit rate allows 1000baseX */ + if (br_nom && br_min <= 1300 && br_max >= 1200) phylink_set(modes, 1000baseX_Full); } @@ -319,31 +379,27 @@ /** * 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) || phylink_test(link_modes, 10000baseSR_Full) || phylink_test(link_modes, 10000baseLR_Full) || phylink_test(link_modes, 10000baseLRM_Full) || - phylink_test(link_modes, 10000baseER_Full)) - return PHY_INTERFACE_MODE_10GKR; + phylink_test(link_modes, 10000baseER_Full) || + phylink_test(link_modes, 10000baseT_Full)) + return PHY_INTERFACE_MODE_10GBASER; 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 (phylink_test(link_modes, 1000baseT_Half) || + phylink_test(link_modes, 1000baseT_Full)) return PHY_INTERFACE_MODE_SGMII; if (phylink_test(link_modes, 1000baseX_Full)) @@ -403,10 +459,19 @@ kfree(bus); } -static void sfp_bus_put(struct sfp_bus *bus) +/** + * sfp_bus_put() - put a reference on the &struct sfp_bus + * @bus: the &struct sfp_bus found via sfp_bus_find_fwnode() + * + * Put a reference on the &struct sfp_bus and free the underlying structure + * if this was the last reference. + */ +void sfp_bus_put(struct sfp_bus *bus) { - kref_put_mutex(&bus->kref, sfp_bus_release, &sfp_mutex); + if (bus) + kref_put_mutex(&bus->kref, sfp_bus_release, &sfp_mutex); } +EXPORT_SYMBOL_GPL(sfp_bus_put); static int sfp_register_bus(struct sfp_bus *bus) { @@ -422,11 +487,11 @@ return ret; } } + bus->registered = true; bus->socket_ops->attach(bus->sfp); if (bus->started) bus->socket_ops->start(bus->sfp); - bus->netdev->sfp_bus = bus; - bus->registered = true; + bus->upstream_ops->attach(bus->upstream, bus); return 0; } @@ -434,8 +499,8 @@ { const struct sfp_upstream_ops *ops = bus->upstream_ops; - bus->netdev->sfp_bus = NULL; if (bus->registered) { + bus->upstream_ops->detach(bus->upstream, bus); if (bus->started) bus->socket_ops->stop(bus->sfp); bus->socket_ops->detach(bus->sfp); @@ -517,70 +582,123 @@ { bus->upstream_ops = NULL; bus->upstream = NULL; - bus->netdev = NULL; } /** - * sfp_register_upstream() - Register the neighbouring device - * @fwnode: firmware node for the SFP bus - * @ndev: network device associated with the interface - * @upstream: the upstream private data - * @ops: the upstream's &struct sfp_upstream_ops + * sfp_bus_find_fwnode() - parse and locate the SFP bus from fwnode + * @fwnode: firmware node for the parent device (MAC or PHY) * - * Register the upstream device (eg, PHY) with the SFP bus. MAC drivers - * should use phylink, which will call this function for them. Returns - * a pointer to the allocated &struct sfp_bus. + * Parse the parent device's firmware node for a SFP bus, and locate + * the sfp_bus structure, incrementing its reference count. This must + * be put via sfp_bus_put() when done. * - * On error, returns %NULL. + * Returns: + * - on success, a pointer to the sfp_bus structure, + * - %NULL if no SFP is specified, + * - on failure, an error pointer value: + * + * - corresponding to the errors detailed for + * fwnode_property_get_reference_args(). + * - %-ENOMEM if we failed to allocate the bus. + * - an error from the upstream's connect_phy() method. */ -struct sfp_bus *sfp_register_upstream(struct fwnode_handle *fwnode, - struct net_device *ndev, void *upstream, - const struct sfp_upstream_ops *ops) +struct sfp_bus *sfp_bus_find_fwnode(struct fwnode_handle *fwnode) { - struct sfp_bus *bus = sfp_bus_get(fwnode); - int ret = 0; + struct fwnode_reference_args ref; + struct sfp_bus *bus; + int ret; - if (bus) { - rtnl_lock(); - bus->upstream_ops = ops; - bus->upstream = upstream; - bus->netdev = ndev; + ret = fwnode_property_get_reference_args(fwnode, "sfp", NULL, + 0, 0, &ref); + if (ret == -ENOENT) + return NULL; + else if (ret < 0) + return ERR_PTR(ret); - if (bus->sfp) { - ret = sfp_register_bus(bus); - if (ret) - sfp_upstream_clear(bus); - } - rtnl_unlock(); + if (!fwnode_device_is_available(ref.fwnode)) { + fwnode_handle_put(ref.fwnode); + return NULL; } - if (ret) { - sfp_bus_put(bus); - bus = NULL; - } + bus = sfp_bus_get(ref.fwnode); + fwnode_handle_put(ref.fwnode); + if (!bus) + return ERR_PTR(-ENOMEM); return bus; } -EXPORT_SYMBOL_GPL(sfp_register_upstream); +EXPORT_SYMBOL_GPL(sfp_bus_find_fwnode); /** - * sfp_unregister_upstream() - Unregister sfp bus - * @bus: a pointer to the &struct sfp_bus structure for the sfp module + * sfp_bus_add_upstream() - parse and register the neighbouring device + * @bus: the &struct sfp_bus found via sfp_bus_find_fwnode() + * @upstream: the upstream private data + * @ops: the upstream's &struct sfp_upstream_ops * - * Unregister a previously registered upstream connection for the SFP - * module. @bus is returned from sfp_register_upstream(). + * Add upstream driver for the SFP bus, and if the bus is complete, register + * the SFP bus using sfp_register_upstream(). This takes a reference on the + * bus, so it is safe to put the bus after this call. + * + * Returns: + * - on success, a pointer to the sfp_bus structure, + * - %NULL if no SFP is specified, + * - on failure, an error pointer value: + * + * - corresponding to the errors detailed for + * fwnode_property_get_reference_args(). + * - %-ENOMEM if we failed to allocate the bus. + * - an error from the upstream's connect_phy() method. */ -void sfp_unregister_upstream(struct sfp_bus *bus) +int sfp_bus_add_upstream(struct sfp_bus *bus, void *upstream, + const struct sfp_upstream_ops *ops) { + int ret; + + /* If no bus, return success */ + if (!bus) + return 0; + rtnl_lock(); - if (bus->sfp) - sfp_unregister_bus(bus); - sfp_upstream_clear(bus); + kref_get(&bus->kref); + bus->upstream_ops = ops; + bus->upstream = upstream; + + if (bus->sfp) { + ret = sfp_register_bus(bus); + if (ret) + sfp_upstream_clear(bus); + } else { + ret = 0; + } rtnl_unlock(); - sfp_bus_put(bus); + if (ret) + sfp_bus_put(bus); + + return ret; } -EXPORT_SYMBOL_GPL(sfp_unregister_upstream); +EXPORT_SYMBOL_GPL(sfp_bus_add_upstream); + +/** + * sfp_bus_del_upstream() - Delete a sfp bus + * @bus: a pointer to the &struct sfp_bus structure for the sfp module + * + * Delete a previously registered upstream connection for the SFP + * module. @bus should have been added by sfp_bus_add_upstream(). + */ +void sfp_bus_del_upstream(struct sfp_bus *bus) +{ + if (bus) { + rtnl_lock(); + if (bus->sfp) + sfp_unregister_bus(bus); + sfp_upstream_clear(bus); + rtnl_unlock(); + + sfp_bus_put(bus); + } +} +EXPORT_SYMBOL_GPL(sfp_bus_del_upstream); /* Socket driver entry points */ int sfp_add_phy(struct sfp_bus *bus, struct phy_device *phydev) @@ -651,6 +769,27 @@ } 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; @@ -670,7 +809,7 @@ bus->sfp = sfp; bus->socket_ops = ops; - if (bus->netdev) { + if (bus->upstream_ops) { ret = sfp_register_bus(bus); if (ret) sfp_socket_clear(bus); @@ -690,7 +829,7 @@ void sfp_unregister_socket(struct sfp_bus *bus) { rtnl_lock(); - if (bus->netdev) + if (bus->upstream_ops) sfp_unregister_bus(bus); sfp_socket_clear(bus); rtnl_unlock(); -- Gitblit v1.6.2