summaryrefslogtreecommitdiff
path: root/drivers/net/phy/sfp.c
diff options
context:
space:
mode:
authorRussell King (Oracle) <rmk+kernel@armlinux.org.uk>2022-09-13 20:06:32 +0100
committerJakub Kicinski <kuba@kernel.org>2022-09-20 07:54:13 -0700
commit23571c7b96437483d28a990c906cc81f5f66374e (patch)
treeea45c5063be4641aea19c5ec2613503d494bae79 /drivers/net/phy/sfp.c
parent8475c4b70b040f9d8cbc308100f2c4d865f810b3 (diff)
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) <rmk+kernel@armlinux.org.uk> Signed-off-by: Jakub Kicinski <kuba@kernel.org>
Diffstat (limited to 'drivers/net/phy/sfp.c')
-rw-r--r--drivers/net/phy/sfp.c94
1 files changed, 93 insertions, 1 deletions
diff --git a/drivers/net/phy/sfp.c b/drivers/net/phy/sfp.c
index b9fe1f554f27..3901919e4a3f 100644
--- a/drivers/net/phy/sfp.c
+++ b/drivers/net/phy/sfp.c
@@ -253,6 +253,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;
@@ -309,6 +311,93 @@ static const struct of_device_id sfp_of_match[] = {
};
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)
@@ -1926,6 +2015,8 @@ static int sfp_sm_mod_probe(struct sfp *sfp, bool report)
else
sfp->tx_fault_ignore = false;
+ sfp->quirk = sfp_lookup_quirk(&id);
+
return 0;
}
@@ -2038,7 +2129,8 @@ static void sfp_sm_module(struct sfp *sfp, unsigned int event)
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;