|
|
@@ -0,0 +1,290 @@
|
|
|
+From a4648a1957cd79bc389538aa0472db39a56e3df6 Mon Sep 17 00:00:00 2001
|
|
|
+From: "Russell King (Oracle)" <[email protected]>
|
|
|
+Date: Fri, 26 Aug 2022 08:43:30 +0100
|
|
|
+Subject: [PATCH 1/6] net: sfp: move quirk handling into sfp.c
|
|
|
+
|
|
|
+We need to handle more quirks than just those which affect the link
|
|
|
+modes of the module. Move the quirk lookup into sfp.c, and pass the
|
|
|
+quirk to sfp-bus.c
|
|
|
+
|
|
|
+Signed-off-by: Russell King (Oracle) <[email protected]>
|
|
|
+---
|
|
|
+ drivers/net/phy/sfp-bus.c | 98 ++-------------------------------------
|
|
|
+ drivers/net/phy/sfp.c | 94 ++++++++++++++++++++++++++++++++++++-
|
|
|
+ drivers/net/phy/sfp.h | 9 +++-
|
|
|
+ 3 files changed, 104 insertions(+), 97 deletions(-)
|
|
|
+
|
|
|
+--- a/drivers/net/phy/sfp-bus.c
|
|
|
++++ b/drivers/net/phy/sfp-bus.c
|
|
|
+@@ -10,12 +10,6 @@
|
|
|
+
|
|
|
+ #include "sfp.h"
|
|
|
+
|
|
|
+-struct sfp_quirk {
|
|
|
+- const char *vendor;
|
|
|
+- const char *part;
|
|
|
+- void (*modes)(const struct sfp_eeprom_id *id, unsigned long *modes);
|
|
|
+-};
|
|
|
+-
|
|
|
+ /**
|
|
|
+ * struct sfp_bus - internal representation of a sfp bus
|
|
|
+ */
|
|
|
+@@ -38,93 +32,6 @@ struct sfp_bus {
|
|
|
+ bool started;
|
|
|
+ };
|
|
|
+
|
|
|
+-static void sfp_quirk_2500basex(const struct sfp_eeprom_id *id,
|
|
|
+- 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[] = {
|
|
|
+- {
|
|
|
+- // Alcatel Lucent G-010S-P can operate at 2500base-X, but
|
|
|
+- // incorrectly report 2500MBd NRZ in their EEPROM
|
|
|
+- .vendor = "ALCATELLUCENT",
|
|
|
+- .part = "G010SP",
|
|
|
+- .modes = sfp_quirk_2500basex,
|
|
|
+- }, {
|
|
|
+- // Alcatel Lucent G-010S-A can operate at 2500base-X, but
|
|
|
+- // report 3.2GBd NRZ in their EEPROM
|
|
|
+- .vendor = "ALCATELLUCENT",
|
|
|
+- .part = "3FE46541AA",
|
|
|
+- .modes = sfp_quirk_2500basex,
|
|
|
+- }, {
|
|
|
+- // Huawei MA5671A can operate at 2500base-X, but report 1.2GBd
|
|
|
+- // NRZ in their EEPROM
|
|
|
+- .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,
|
|
|
+- },
|
|
|
+-};
|
|
|
+-
|
|
|
+-static size_t sfp_strlen(const char *str, size_t maxlen)
|
|
|
+-{
|
|
|
+- size_t size, i;
|
|
|
+-
|
|
|
+- /* Trailing characters should be filled with space chars */
|
|
|
+- for (i = 0, size = 0; i < maxlen; i++)
|
|
|
+- if (str[i] != ' ')
|
|
|
+- size = i + 1;
|
|
|
+-
|
|
|
+- return size;
|
|
|
+-}
|
|
|
+-
|
|
|
+-static bool sfp_match(const char *qs, const char *str, size_t len)
|
|
|
+-{
|
|
|
+- if (!qs)
|
|
|
+- return true;
|
|
|
+- if (strlen(qs) != len)
|
|
|
+- return false;
|
|
|
+- return !strncmp(qs, str, len);
|
|
|
+-}
|
|
|
+-
|
|
|
+-static const struct sfp_quirk *sfp_lookup_quirk(const struct sfp_eeprom_id *id)
|
|
|
+-{
|
|
|
+- const struct sfp_quirk *q;
|
|
|
+- unsigned int i;
|
|
|
+- size_t vs, ps;
|
|
|
+-
|
|
|
+- vs = sfp_strlen(id->base.vendor_name, ARRAY_SIZE(id->base.vendor_name));
|
|
|
+- ps = sfp_strlen(id->base.vendor_pn, ARRAY_SIZE(id->base.vendor_pn));
|
|
|
+-
|
|
|
+- for (i = 0, q = sfp_quirks; i < ARRAY_SIZE(sfp_quirks); i++, q++)
|
|
|
+- if (sfp_match(q->vendor, id->base.vendor_name, vs) &&
|
|
|
+- sfp_match(q->part, id->base.vendor_pn, ps))
|
|
|
+- return q;
|
|
|
+-
|
|
|
+- 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
|
|
|
+@@ -786,12 +693,13 @@ void sfp_link_down(struct sfp_bus *bus)
|
|
|
+ }
|
|
|
+ EXPORT_SYMBOL_GPL(sfp_link_down);
|
|
|
+
|
|
|
+-int sfp_module_insert(struct sfp_bus *bus, const struct sfp_eeprom_id *id)
|
|
|
++int sfp_module_insert(struct sfp_bus *bus, const struct sfp_eeprom_id *id,
|
|
|
++ const struct sfp_quirk *quirk)
|
|
|
+ {
|
|
|
+ const struct sfp_upstream_ops *ops = sfp_get_upstream_ops(bus);
|
|
|
+ int ret = 0;
|
|
|
+
|
|
|
+- bus->sfp_quirk = sfp_lookup_quirk(id);
|
|
|
++ bus->sfp_quirk = quirk;
|
|
|
+
|
|
|
+ if (ops && ops->module_insert)
|
|
|
+ ret = ops->module_insert(bus->upstream, id);
|
|
|
+--- a/drivers/net/phy/sfp.c
|
|
|
++++ b/drivers/net/phy/sfp.c
|
|
|
+@@ -252,6 +252,8 @@ struct sfp {
|
|
|
+ unsigned int module_t_start_up;
|
|
|
+ bool tx_fault_ignore;
|
|
|
+
|
|
|
++ const struct sfp_quirk *quirk;
|
|
|
++
|
|
|
+ #if IS_ENABLED(CONFIG_HWMON)
|
|
|
+ struct sfp_diag diag;
|
|
|
+ struct delayed_work hwmon_probe;
|
|
|
+@@ -308,6 +310,93 @@ static const struct of_device_id sfp_of_
|
|
|
+ };
|
|
|
+ MODULE_DEVICE_TABLE(of, sfp_of_match);
|
|
|
+
|
|
|
++static void sfp_quirk_2500basex(const struct sfp_eeprom_id *id,
|
|
|
++ unsigned long *modes)
|
|
|
++{
|
|
|
++ linkmode_set_bit(ETHTOOL_LINK_MODE_2500baseX_Full_BIT, modes);
|
|
|
++}
|
|
|
++
|
|
|
++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.
|
|
|
++ */
|
|
|
++ linkmode_zero(modes);
|
|
|
++ linkmode_set_bit(ETHTOOL_LINK_MODE_1000baseX_Full_BIT, modes);
|
|
|
++}
|
|
|
++
|
|
|
++static const struct sfp_quirk sfp_quirks[] = {
|
|
|
++ {
|
|
|
++ // Alcatel Lucent G-010S-P can operate at 2500base-X, but
|
|
|
++ // incorrectly report 2500MBd NRZ in their EEPROM
|
|
|
++ .vendor = "ALCATELLUCENT",
|
|
|
++ .part = "G010SP",
|
|
|
++ .modes = sfp_quirk_2500basex,
|
|
|
++ }, {
|
|
|
++ // Alcatel Lucent G-010S-A can operate at 2500base-X, but
|
|
|
++ // report 3.2GBd NRZ in their EEPROM
|
|
|
++ .vendor = "ALCATELLUCENT",
|
|
|
++ .part = "3FE46541AA",
|
|
|
++ .modes = sfp_quirk_2500basex,
|
|
|
++ }, {
|
|
|
++ // Huawei MA5671A can operate at 2500base-X, but report 1.2GBd
|
|
|
++ // NRZ in their EEPROM
|
|
|
++ .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,
|
|
|
++ },
|
|
|
++};
|
|
|
++
|
|
|
++static size_t sfp_strlen(const char *str, size_t maxlen)
|
|
|
++{
|
|
|
++ size_t size, i;
|
|
|
++
|
|
|
++ /* Trailing characters should be filled with space chars */
|
|
|
++ for (i = 0, size = 0; i < maxlen; i++)
|
|
|
++ if (str[i] != ' ')
|
|
|
++ size = i + 1;
|
|
|
++
|
|
|
++ return size;
|
|
|
++}
|
|
|
++
|
|
|
++static bool sfp_match(const char *qs, const char *str, size_t len)
|
|
|
++{
|
|
|
++ if (!qs)
|
|
|
++ return true;
|
|
|
++ if (strlen(qs) != len)
|
|
|
++ return false;
|
|
|
++ return !strncmp(qs, str, len);
|
|
|
++}
|
|
|
++
|
|
|
++static const struct sfp_quirk *sfp_lookup_quirk(const struct sfp_eeprom_id *id)
|
|
|
++{
|
|
|
++ const struct sfp_quirk *q;
|
|
|
++ unsigned int i;
|
|
|
++ size_t vs, ps;
|
|
|
++
|
|
|
++ vs = sfp_strlen(id->base.vendor_name, ARRAY_SIZE(id->base.vendor_name));
|
|
|
++ ps = sfp_strlen(id->base.vendor_pn, ARRAY_SIZE(id->base.vendor_pn));
|
|
|
++
|
|
|
++ for (i = 0, q = sfp_quirks; i < ARRAY_SIZE(sfp_quirks); i++, q++)
|
|
|
++ if (sfp_match(q->vendor, id->base.vendor_name, vs) &&
|
|
|
++ sfp_match(q->part, id->base.vendor_pn, ps))
|
|
|
++ return q;
|
|
|
++
|
|
|
++ return NULL;
|
|
|
++}
|
|
|
++
|
|
|
+ static unsigned long poll_jiffies;
|
|
|
+
|
|
|
+ static unsigned int sfp_gpio_get_state(struct sfp *sfp)
|
|
|
+@@ -1952,6 +2041,8 @@ static int sfp_sm_mod_probe(struct sfp *
|
|
|
+ else
|
|
|
+ sfp->tx_fault_ignore = false;
|
|
|
+
|
|
|
++ sfp->quirk = sfp_lookup_quirk(&id);
|
|
|
++
|
|
|
+ return 0;
|
|
|
+ }
|
|
|
+
|
|
|
+@@ -2063,7 +2154,8 @@ static void sfp_sm_module(struct sfp *sf
|
|
|
+ break;
|
|
|
+
|
|
|
+ /* Report the module insertion to the upstream device */
|
|
|
+- err = sfp_module_insert(sfp->sfp_bus, &sfp->id);
|
|
|
++ err = sfp_module_insert(sfp->sfp_bus, &sfp->id,
|
|
|
++ sfp->quirk);
|
|
|
+ if (err < 0) {
|
|
|
+ sfp_sm_mod_next(sfp, SFP_MOD_ERROR, 0);
|
|
|
+ break;
|
|
|
+--- a/drivers/net/phy/sfp.h
|
|
|
++++ b/drivers/net/phy/sfp.h
|
|
|
+@@ -6,6 +6,12 @@
|
|
|
+
|
|
|
+ struct sfp;
|
|
|
+
|
|
|
++struct sfp_quirk {
|
|
|
++ const char *vendor;
|
|
|
++ const char *part;
|
|
|
++ void (*modes)(const struct sfp_eeprom_id *id, unsigned long *modes);
|
|
|
++};
|
|
|
++
|
|
|
+ struct sfp_socket_ops {
|
|
|
+ void (*attach)(struct sfp *sfp);
|
|
|
+ void (*detach)(struct sfp *sfp);
|
|
|
+@@ -23,7 +29,8 @@ int sfp_add_phy(struct sfp_bus *bus, str
|
|
|
+ void sfp_remove_phy(struct sfp_bus *bus);
|
|
|
+ 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);
|
|
|
++int sfp_module_insert(struct sfp_bus *bus, const struct sfp_eeprom_id *id,
|
|
|
++ const struct sfp_quirk *quirk);
|
|
|
+ void sfp_module_remove(struct sfp_bus *bus);
|
|
|
+ int sfp_module_start(struct sfp_bus *bus);
|
|
|
+ void sfp_module_stop(struct sfp_bus *bus);
|