--- /dev/null
+From e19de30d20809af3221ef8a2648b8a8a52e02d90 Mon Sep 17 00:00:00 2001
+From: Daniel Golle <daniel@makrotopia.org>
+Date: Wed, 21 Sep 2022 01:23:14 +0100
+Subject: [PATCH 1/1] net: dsa: mt7530: add support for in-band link status
+
+Read link status from SGMII PCS for in-band managed 2500Base-X and
+1000Base-X connection on a MAC port of the MT7531. This is needed to
+get the SFP cage working which is connected to SGMII interface of
+port 5 of the MT7531 switch IC on the Bananapi BPi-R3 board.
+While at it also handle an_complete for both the autoneg and the
+non-autoneg codepath.
+
+Signed-off-by: Daniel Golle <daniel@makrotopia.org>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+---
+ drivers/net/dsa/mt7530.c | 50 +++++++++++++++++++++++++++++-----------
+ drivers/net/dsa/mt7530.h | 1 +
+ 2 files changed, 38 insertions(+), 13 deletions(-)
+
+--- a/drivers/net/dsa/mt7530.c
++++ b/drivers/net/dsa/mt7530.c
+@@ -2699,9 +2699,6 @@ mt7531_mac_config(struct dsa_switch *ds,
+ case PHY_INTERFACE_MODE_NA:
+ case PHY_INTERFACE_MODE_1000BASEX:
+ case PHY_INTERFACE_MODE_2500BASEX:
+- if (phylink_autoneg_inband(mode))
+- return -EINVAL;
+-
+ return mt7531_sgmii_setup_mode_force(priv, port, interface);
+ default:
+ return -EINVAL;
+@@ -2777,13 +2774,6 @@ unsupported:
+ return;
+ }
+
+- if (phylink_autoneg_inband(mode) &&
+- state->interface != PHY_INTERFACE_MODE_SGMII) {
+- dev_err(ds->dev, "%s: in-band negotiation unsupported\n",
+- __func__);
+- return;
+- }
+-
+ mcr_cur = mt7530_read(priv, MT7530_PMCR_P(port));
+ mcr_new = mcr_cur;
+ mcr_new &= ~PMCR_LINK_SETTINGS_MASK;
+@@ -2920,6 +2910,9 @@ static void mt753x_phylink_get_caps(stru
+ config->mac_capabilities = MAC_ASYM_PAUSE | MAC_SYM_PAUSE |
+ MAC_10 | MAC_100 | MAC_1000FD;
+
++ if ((priv->id == ID_MT7531) && mt753x_is_mac_port(port))
++ config->mac_capabilities |= MAC_2500FD;
++
+ /* This driver does not make use of the speed, duplex, pause or the
+ * advertisement in its mac_config, so it is safe to mark this driver
+ * as non-legacy.
+@@ -2985,6 +2978,7 @@ mt7531_sgmii_pcs_get_state_an(struct mt7
+
+ status = mt7530_read(priv, MT7531_PCS_CONTROL_1(port));
+ state->link = !!(status & MT7531_SGMII_LINK_STATUS);
++ state->an_complete = !!(status & MT7531_SGMII_AN_COMPLETE);
+ if (state->interface == PHY_INTERFACE_MODE_SGMII &&
+ (status & MT7531_SGMII_AN_ENABLE)) {
+ val = mt7530_read(priv, MT7531_PCS_SPEED_ABILITY(port));
+@@ -3015,16 +3009,44 @@ mt7531_sgmii_pcs_get_state_an(struct mt7
+ return 0;
+ }
+
++static void
++mt7531_sgmii_pcs_get_state_inband(struct mt7530_priv *priv, int port,
++ struct phylink_link_state *state)
++{
++ unsigned int val;
++
++ val = mt7530_read(priv, MT7531_PCS_CONTROL_1(port));
++ state->link = !!(val & MT7531_SGMII_LINK_STATUS);
++ if (!state->link)
++ return;
++
++ state->an_complete = state->link;
++
++ if (state->interface == PHY_INTERFACE_MODE_2500BASEX)
++ state->speed = SPEED_2500;
++ else
++ state->speed = SPEED_1000;
++
++ state->duplex = DUPLEX_FULL;
++ state->pause = MLO_PAUSE_NONE;
++}
++
+ static void mt7531_pcs_get_state(struct phylink_pcs *pcs,
+ struct phylink_link_state *state)
+ {
+ struct mt7530_priv *priv = pcs_to_mt753x_pcs(pcs)->priv;
+ int port = pcs_to_mt753x_pcs(pcs)->port;
+
+- if (state->interface == PHY_INTERFACE_MODE_SGMII)
++ if (state->interface == PHY_INTERFACE_MODE_SGMII) {
+ mt7531_sgmii_pcs_get_state_an(priv, port, state);
+- else
+- state->link = false;
++ return;
++ } else if ((state->interface == PHY_INTERFACE_MODE_1000BASEX) ||
++ (state->interface == PHY_INTERFACE_MODE_2500BASEX)) {
++ mt7531_sgmii_pcs_get_state_inband(priv, port, state);
++ return;
++ }
++
++ state->link = false;
+ }
+
+ static int mt753x_pcs_config(struct phylink_pcs *pcs, unsigned int mode,
+@@ -3065,6 +3087,8 @@ mt753x_setup(struct dsa_switch *ds)
+ priv->pcs[i].pcs.ops = priv->info->pcs_ops;
+ priv->pcs[i].priv = priv;
+ priv->pcs[i].port = i;
++ if (mt753x_is_mac_port(i))
++ priv->pcs[i].pcs.poll = 1;
+ }
+
+ ret = priv->info->sw_setup(ds);
+--- a/drivers/net/dsa/mt7530.h
++++ b/drivers/net/dsa/mt7530.h
+@@ -373,6 +373,7 @@ enum mt7530_vlan_port_acc_frm {
+ #define MT7531_SGMII_LINK_STATUS BIT(18)
+ #define MT7531_SGMII_AN_ENABLE BIT(12)
+ #define MT7531_SGMII_AN_RESTART BIT(9)
++#define MT7531_SGMII_AN_COMPLETE BIT(21)
+
+ /* Register for SGMII PCS_SPPED_ABILITY */
+ #define MT7531_PCS_SPEED_ABILITY(p) MT7531_SGMII_REG(p, 0x08)
--- /dev/null
+From 8475c4b70b040f9d8cbc308100f2c4d865f810b3 Mon Sep 17 00:00:00 2001
+From: "Russell King (Oracle)" <rmk+kernel@armlinux.org.uk>
+Date: Tue, 13 Sep 2022 20:06:27 +0100
+Subject: [PATCH 1/1] net: sfp: re-implement soft state polling setup
+
+Re-implement the decision making for soft state polling. Instead of
+generating the soft state mask in sfp_soft_start_poll() by looking at
+which GPIOs are available, record their availability in
+sfp_sm_mod_probe() in sfp->state_hw_mask.
+
+This will then allow us to clear bits in sfp->state_hw_mask in module
+specific quirks when the hardware signals should not be used, thereby
+allowing us to switch to using the software state polling.
+
+Signed-off-by: Russell King (Oracle) <rmk+kernel@armlinux.org.uk>
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+---
+ drivers/net/phy/sfp.c | 38 ++++++++++++++++++++++++++------------
+ 1 file changed, 26 insertions(+), 12 deletions(-)
+
+--- a/drivers/net/phy/sfp.c
++++ b/drivers/net/phy/sfp.c
+@@ -234,6 +234,7 @@ struct sfp {
+ bool need_poll;
+
+ struct mutex st_mutex; /* Protects state */
++ unsigned int state_hw_mask;
+ unsigned int state_soft_mask;
+ unsigned int state;
+ struct delayed_work poll;
+@@ -499,17 +500,18 @@ static void sfp_soft_set_state(struct sf
+ 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 &&
+- !sfp->gpio[GPIO_TX_DISABLE])
+- sfp->state_soft_mask |= SFP_F_TX_DISABLE;
+- if (id->ext.enhopts & SFP_ENHOPTS_SOFT_TX_FAULT &&
+- !sfp->gpio[GPIO_TX_FAULT])
+- sfp->state_soft_mask |= SFP_F_TX_FAULT;
+- if (id->ext.enhopts & SFP_ENHOPTS_SOFT_RX_LOS &&
+- !sfp->gpio[GPIO_LOS])
+- sfp->state_soft_mask |= SFP_F_LOS;
++ if (id->ext.enhopts & SFP_ENHOPTS_SOFT_TX_DISABLE)
++ mask |= SFP_F_TX_DISABLE;
++ if (id->ext.enhopts & SFP_ENHOPTS_SOFT_TX_FAULT)
++ mask |= SFP_F_TX_FAULT;
++ if (id->ext.enhopts & SFP_ENHOPTS_SOFT_RX_LOS)
++ mask |= SFP_F_LOS;
++
++ // 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)
+@@ -523,10 +525,11 @@ static void sfp_soft_stop_poll(struct sf
+
+ static unsigned int sfp_get_state(struct sfp *sfp)
+ {
+- unsigned int state = sfp->get_state(sfp);
++ unsigned int soft = sfp->state_soft_mask & (SFP_F_LOS | SFP_F_TX_FAULT);
++ unsigned int state;
+
+- if (state & SFP_F_PRESENT &&
+- sfp->state_soft_mask & (SFP_F_LOS | SFP_F_TX_FAULT))
++ state = sfp->get_state(sfp) & sfp->state_hw_mask;
++ if (state & SFP_F_PRESENT && soft)
+ state |= sfp_soft_get_state(sfp);
+
+ return state;
+@@ -1940,6 +1943,15 @@ static int sfp_sm_mod_probe(struct sfp *
+ if (ret < 0)
+ return ret;
+
++ /* Initialise state bits to use from hardware */
++ sfp->state_hw_mask = SFP_F_PRESENT;
++ if (sfp->gpio[GPIO_TX_DISABLE])
++ sfp->state_hw_mask |= SFP_F_TX_DISABLE;
++ if (sfp->gpio[GPIO_TX_FAULT])
++ sfp->state_hw_mask |= SFP_F_TX_FAULT;
++ if (sfp->gpio[GPIO_LOS])
++ sfp->state_hw_mask |= SFP_F_LOS;
++
+ if (!memcmp(id.base.vendor_name, "ALCATELLUCENT ", 16) &&
+ !memcmp(id.base.vendor_pn, "3FE46541AA ", 16))
+ sfp->module_t_start_up = T_START_UP_BAD_GPON;
+@@ -2565,6 +2577,8 @@ static int sfp_probe(struct platform_dev
+ return PTR_ERR(sfp->gpio[i]);
+ }
+
++ sfp->state_hw_mask = SFP_F_PRESENT;
++
+ sfp->get_state = sfp_gpio_get_state;
+ sfp->set_state = sfp_gpio_set_state;
+
--- /dev/null
+From 23571c7b96437483d28a990c906cc81f5f66374e Mon Sep 17 00:00:00 2001
+From: "Russell King (Oracle)" <rmk+kernel@armlinux.org.uk>
+Date: Tue, 13 Sep 2022 20:06:32 +0100
+Subject: [PATCH 1/1] 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>
+---
+ 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
+@@ -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_
+ };
+ 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)
+@@ -1964,6 +2053,8 @@ static int sfp_sm_mod_probe(struct sfp *
+ else
+ sfp->tx_fault_ignore = false;
+
++ sfp->quirk = sfp_lookup_quirk(&id);
++
+ return 0;
+ }
+
+@@ -2075,7 +2166,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);
--- /dev/null
+From 275416754e9a262c97a1ad6f806a4bc6e0464aa2 Mon Sep 17 00:00:00 2001
+From: "Russell King (Oracle)" <rmk+kernel@armlinux.org.uk>
+Date: Tue, 13 Sep 2022 20:06:37 +0100
+Subject: [PATCH 1/1] net: sfp: move Alcatel Lucent 3FE46541AA fixup
+
+Add a new fixup mechanism to the SFP quirks, and use it for this
+module.
+
+Signed-off-by: Russell King (Oracle) <rmk+kernel@armlinux.org.uk>
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+---
+ drivers/net/phy/sfp.c | 14 +++++++++-----
+ drivers/net/phy/sfp.h | 1 +
+ 2 files changed, 10 insertions(+), 5 deletions(-)
+
+--- a/drivers/net/phy/sfp.c
++++ b/drivers/net/phy/sfp.c
+@@ -311,6 +311,11 @@ static const struct of_device_id sfp_of_
+ };
+ MODULE_DEVICE_TABLE(of, sfp_of_match);
+
++static void sfp_fixup_long_startup(struct sfp *sfp)
++{
++ sfp->module_t_start_up = T_START_UP_BAD_GPON;
++}
++
+ static void sfp_quirk_2500basex(const struct sfp_eeprom_id *id,
+ unsigned long *modes)
+ {
+@@ -341,6 +346,7 @@ static const struct sfp_quirk sfp_quirks
+ .vendor = "ALCATELLUCENT",
+ .part = "3FE46541AA",
+ .modes = sfp_quirk_2500basex,
++ .fixup = sfp_fixup_long_startup,
+ }, {
+ // Huawei MA5671A can operate at 2500base-X, but report 1.2GBd
+ // NRZ in their EEPROM
+@@ -2041,11 +2047,7 @@ static int sfp_sm_mod_probe(struct sfp *
+ if (sfp->gpio[GPIO_LOS])
+ sfp->state_hw_mask |= SFP_F_LOS;
+
+- if (!memcmp(id.base.vendor_name, "ALCATELLUCENT ", 16) &&
+- !memcmp(id.base.vendor_pn, "3FE46541AA ", 16))
+- sfp->module_t_start_up = T_START_UP_BAD_GPON;
+- else
+- sfp->module_t_start_up = T_START_UP;
++ sfp->module_t_start_up = T_START_UP;
+
+ if (!memcmp(id.base.vendor_name, "HUAWEI ", 16) &&
+ !memcmp(id.base.vendor_pn, "MA5671A ", 16))
+@@ -2054,6 +2056,8 @@ static int sfp_sm_mod_probe(struct sfp *
+ sfp->tx_fault_ignore = false;
+
+ sfp->quirk = sfp_lookup_quirk(&id);
++ if (sfp->quirk && sfp->quirk->fixup)
++ sfp->quirk->fixup(sfp);
+
+ return 0;
+ }
+--- a/drivers/net/phy/sfp.h
++++ b/drivers/net/phy/sfp.h
+@@ -10,6 +10,7 @@ struct sfp_quirk {
+ const char *vendor;
+ const char *part;
+ void (*modes)(const struct sfp_eeprom_id *id, unsigned long *modes);
++ void (*fixup)(struct sfp *sfp);
+ };
+
+ struct sfp_socket_ops {
--- /dev/null
+From 5029be761161374a3624aa7b4670174c35449bf5 Mon Sep 17 00:00:00 2001
+From: "Russell King (Oracle)" <rmk+kernel@armlinux.org.uk>
+Date: Tue, 13 Sep 2022 20:06:42 +0100
+Subject: [PATCH 1/1] net: sfp: move Huawei MA5671A fixup
+
+Move this module over to the new fixup mechanism.
+
+Signed-off-by: Russell King (Oracle) <rmk+kernel@armlinux.org.uk>
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+---
+ drivers/net/phy/sfp.c | 12 +++++++-----
+ 1 file changed, 7 insertions(+), 5 deletions(-)
+
+--- a/drivers/net/phy/sfp.c
++++ b/drivers/net/phy/sfp.c
+@@ -316,6 +316,11 @@ static void sfp_fixup_long_startup(struc
+ sfp->module_t_start_up = T_START_UP_BAD_GPON;
+ }
+
++static void sfp_fixup_ignore_tx_fault(struct sfp *sfp)
++{
++ sfp->tx_fault_ignore = true;
++}
++
+ static void sfp_quirk_2500basex(const struct sfp_eeprom_id *id,
+ unsigned long *modes)
+ {
+@@ -353,6 +358,7 @@ static const struct sfp_quirk sfp_quirks
+ .vendor = "HUAWEI",
+ .part = "MA5671A",
+ .modes = sfp_quirk_2500basex,
++ .fixup = sfp_fixup_ignore_tx_fault,
+ }, {
+ // Lantech 8330-262D-E can operate at 2500base-X, but
+ // incorrectly report 2500MBd NRZ in their EEPROM
+@@ -2049,11 +2055,7 @@ static int sfp_sm_mod_probe(struct sfp *
+
+ sfp->module_t_start_up = T_START_UP;
+
+- if (!memcmp(id.base.vendor_name, "HUAWEI ", 16) &&
+- !memcmp(id.base.vendor_pn, "MA5671A ", 16))
+- sfp->tx_fault_ignore = true;
+- else
+- sfp->tx_fault_ignore = false;
++ sfp->tx_fault_ignore = false;
+
+ sfp->quirk = sfp_lookup_quirk(&id);
+ if (sfp->quirk && sfp->quirk->fixup)
--- /dev/null
+From 73472c830eae5fce2107f7f086f1e6827d215caf Mon Sep 17 00:00:00 2001
+From: "Russell King (Oracle)" <rmk+kernel@armlinux.org.uk>
+Date: Tue, 13 Sep 2022 20:06:48 +0100
+Subject: [PATCH 1/1] net: sfp: add support for HALNy GPON SFP
+
+Add a quirk for the HALNy HL-GSFP module, which appears to have an
+inverted RX_LOS signal, and maybe uses TX_FAULT as a serial port
+transmit pin. Rather than use these hardware signals, switch to
+using software polling for these status signals.
+
+Signed-off-by: Russell King (Oracle) <rmk+kernel@armlinux.org.uk>
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+---
+ drivers/net/phy/sfp-bus.c | 2 +-
+ drivers/net/phy/sfp.c | 21 ++++++++++++++++++---
+ 2 files changed, 19 insertions(+), 4 deletions(-)
+
+--- a/drivers/net/phy/sfp-bus.c
++++ b/drivers/net/phy/sfp-bus.c
+@@ -283,7 +283,7 @@ void sfp_parse_support(struct sfp_bus *b
+ phylink_set(modes, 2500baseX_Full);
+ }
+
+- if (bus->sfp_quirk)
++ if (bus->sfp_quirk && bus->sfp_quirk->modes)
+ bus->sfp_quirk->modes(id, modes);
+
+ linkmode_or(support, support, modes);
+--- a/drivers/net/phy/sfp.c
++++ b/drivers/net/phy/sfp.c
+@@ -321,6 +321,15 @@ static void sfp_fixup_ignore_tx_fault(st
+ sfp->tx_fault_ignore = true;
+ }
+
++static void sfp_fixup_halny_gsfp(struct sfp *sfp)
++{
++ /* Ignore the TX_FAULT and LOS signals on this module.
++ * these are possibly used for other purposes on this
++ * module, e.g. a serial port.
++ */
++ sfp->state_hw_mask &= ~(SFP_F_TX_FAULT | SFP_F_LOS);
++}
++
+ static void sfp_quirk_2500basex(const struct sfp_eeprom_id *id,
+ unsigned long *modes)
+ {
+@@ -353,6 +362,10 @@ static const struct sfp_quirk sfp_quirks
+ .modes = sfp_quirk_2500basex,
+ .fixup = sfp_fixup_long_startup,
+ }, {
++ .vendor = "HALNy",
++ .part = "HL-GSFP",
++ .fixup = sfp_fixup_halny_gsfp,
++ }, {
+ // Huawei MA5671A can operate at 2500base-X, but report 1.2GBd
+ // NRZ in their EEPROM
+ .vendor = "HUAWEI",
+@@ -369,16 +382,18 @@ static const struct sfp_quirk sfp_quirks
+ .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 */
++ /* Trailing characters should be filled with space chars, but
++ * some manufacturers can't read SFF-8472 and use NUL.
++ */
+ for (i = 0, size = 0; i < maxlen; i++)
+- if (str[i] != ' ')
++ if (str[i] != ' ' && str[i] != '\0')
+ size = i + 1;
+
+ return size;
--- a/drivers/net/phy/sfp.c
+++ b/drivers/net/phy/sfp.c
-@@ -369,6 +369,11 @@ static const struct sfp_quirk sfp_quirks
+@@ -373,6 +373,11 @@ static const struct sfp_quirk sfp_quirks
.modes = sfp_quirk_2500basex,
.fixup = sfp_fixup_ignore_tx_fault,
}, {
// Lantech 8330-262D-E can operate at 2500base-X, but
// incorrectly report 2500MBd NRZ in their EEPROM
.vendor = "Lantech",
-@@ -2303,7 +2308,8 @@ static void sfp_sm_main(struct sfp *sfp,
+@@ -2306,7 +2311,8 @@ static void sfp_sm_main(struct sfp *sfp,
* or t_start_up, so assume there is a fault.
*/
sfp_sm_fault(sfp, SFP_S_INIT_TX_FAULT,
} else if (event == SFP_E_TIMEOUT || event == SFP_E_TX_CLEAR) {
init_done:
sfp->sm_phy_retries = R_PHY_RETRY;
-@@ -2526,10 +2532,12 @@ static void sfp_check_state(struct sfp *
+@@ -2529,10 +2535,12 @@ static void sfp_check_state(struct sfp *
mutex_lock(&sfp->st_mutex);
state = sfp_get_state(sfp);
changed = state ^ sfp->state;
+++ /dev/null
-From 8e18c5fef75debfae3531fbd6901f3bf317d91ed Mon Sep 17 00:00:00 2001
-From: Daniel Golle <daniel@makrotopia.org>
-Date: Fri, 9 Sep 2022 04:28:43 +0100
-Subject: [PATCH] net: dsa: mt7530: add support for in-band link status
-To: linux-mediatek@lists.infradead.org,
- netdev@vger.kernel.org
-Cc: Russell King <linux@armlinux.org.uk>,
- Sean Wang <sean.wang@mediatek.com>,
- Landen Chao <Landen.Chao@mediatek.com>,
- DENG Qingfang <dqfext@gmail.com>,
- Andrew Lunn <andrew@lunn.ch>,
- Vivien Didelot <vivien.didelot@gmail.com>,
- Florian Fainelli <f.fainelli@gmail.com>,
- Vladimir Oltean <olteanv@gmail.com>,
- David S. Miller <davem@davemloft.net>,
- Eric Dumazet <edumazet@google.com>,
- Jakub Kicinski <kuba@kernel.org>,
- Paolo Abeni <pabeni@redhat.com>,
- Matthias Brugger <matthias.bgg@gmail.com>,
- Philipp Zabel <p.zabel@pengutronix.de>
-
-Read link status from SGMII PCS for in-band managed 2500Base-X and
-1000Base-X connection on a MAC port of the MT7531. This is needed to
-get the SFP cage working which is connected to SGMII interface of
-port 5 of the MT7531 switch IC on the Bananapi BPi-R3 board.
-
-Signed-off-by: Daniel Golle <daniel@makrotopia.org>
----
- drivers/net/dsa/mt7530.c | 48 +++++++++++++++++++++++++++++-----------
- 1 file changed, 35 insertions(+), 13 deletions(-)
-
---- a/drivers/net/dsa/mt7530.c
-+++ b/drivers/net/dsa/mt7530.c
-@@ -2703,9 +2703,6 @@ mt7531_mac_config(struct dsa_switch *ds,
- case PHY_INTERFACE_MODE_NA:
- case PHY_INTERFACE_MODE_1000BASEX:
- case PHY_INTERFACE_MODE_2500BASEX:
-- if (phylink_autoneg_inband(mode))
-- return -EINVAL;
--
- return mt7531_sgmii_setup_mode_force(priv, port, interface);
- default:
- return -EINVAL;
-@@ -2781,13 +2778,6 @@ unsupported:
- return;
- }
-
-- if (phylink_autoneg_inband(mode) &&
-- state->interface != PHY_INTERFACE_MODE_SGMII) {
-- dev_err(ds->dev, "%s: in-band negotiation unsupported\n",
-- __func__);
-- return;
-- }
--
- mcr_cur = mt7530_read(priv, MT7530_PMCR_P(port));
- mcr_new = mcr_cur;
- mcr_new &= ~PMCR_LINK_SETTINGS_MASK;
-@@ -2924,6 +2914,9 @@ static void mt753x_phylink_get_caps(stru
- config->mac_capabilities = MAC_ASYM_PAUSE | MAC_SYM_PAUSE |
- MAC_10 | MAC_100 | MAC_1000FD;
-
-+ if ((priv->id == ID_MT7531) && mt753x_is_mac_port(port))
-+ config->mac_capabilities |= MAC_2500FD;
-+
- /* This driver does not make use of the speed, duplex, pause or the
- * advertisement in its mac_config, so it is safe to mark this driver
- * as non-legacy.
-@@ -3019,16 +3012,43 @@ mt7531_sgmii_pcs_get_state_an(struct mt7
- return 0;
- }
-
-+static void
-+mt7531_sgmii_pcs_get_state_inband(struct mt7530_priv *priv, int port,
-+ struct phylink_link_state *state)
-+{
-+ unsigned int val;
-+
-+ val = mt7530_read(priv, MT7531_PCS_CONTROL_1(port));
-+ state->link = !!(val & MT7531_SGMII_LINK_STATUS);
-+ if (!state->link)
-+ return;
-+
-+ if (state->interface == PHY_INTERFACE_MODE_2500BASEX)
-+ state->speed = SPEED_2500;
-+ else
-+ state->speed = SPEED_1000;
-+
-+ state->duplex = DUPLEX_FULL;
-+ state->pause = 0;
-+}
-+
- static void mt7531_pcs_get_state(struct phylink_pcs *pcs,
- struct phylink_link_state *state)
- {
- struct mt7530_priv *priv = pcs_to_mt753x_pcs(pcs)->priv;
- int port = pcs_to_mt753x_pcs(pcs)->port;
-+ unsigned int val;
-
-- if (state->interface == PHY_INTERFACE_MODE_SGMII)
-+ if (state->interface == PHY_INTERFACE_MODE_SGMII) {
- mt7531_sgmii_pcs_get_state_an(priv, port, state);
-- else
-- state->link = false;
-+ return;
-+ } else if ((state->interface == PHY_INTERFACE_MODE_1000BASEX) ||
-+ (state->interface == PHY_INTERFACE_MODE_2500BASEX)) {
-+ mt7531_sgmii_pcs_get_state_inband(priv, port, state);
-+ return;
-+ }
-+
-+ state->link = false;
- }
-
- static int mt753x_pcs_config(struct phylink_pcs *pcs, unsigned int mode,
-@@ -3069,6 +3089,8 @@ mt753x_setup(struct dsa_switch *ds)
- priv->pcs[i].pcs.ops = priv->info->pcs_ops;
- priv->pcs[i].priv = priv;
- priv->pcs[i].port = i;
-+ if (mt753x_is_mac_port(i))
-+ priv->pcs[i].pcs.poll = 1;
- }
-
- ret = priv->info->sw_setup(ds);
+++ /dev/null
-From a4648a1957cd79bc389538aa0472db39a56e3df6 Mon Sep 17 00:00:00 2001
-From: "Russell King (Oracle)" <rmk+kernel@armlinux.org.uk>
-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) <rmk+kernel@armlinux.org.uk>
----
- 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);
+++ /dev/null
-From 21fdd8281de3022aee35dd5bfccc892bd46529a3 Mon Sep 17 00:00:00 2001
-From: "Russell King (Oracle)" <rmk+kernel@armlinux.org.uk>
-Date: Fri, 26 Aug 2022 08:43:35 +0100
-Subject: [PATCH 2/6] net: sfp: move Alcatel Lucent 3FE46541AA fixup
-
-Add a new fixup mechanism to the SFP quirks, and use it for this
-module.
-
-Signed-off-by: Russell King (Oracle) <rmk+kernel@armlinux.org.uk>
----
- drivers/net/phy/sfp.c | 14 +++++++++-----
- drivers/net/phy/sfp.h | 1 +
- 2 files changed, 10 insertions(+), 5 deletions(-)
-
---- a/drivers/net/phy/sfp.c
-+++ b/drivers/net/phy/sfp.c
-@@ -310,6 +310,11 @@ static const struct of_device_id sfp_of_
- };
- MODULE_DEVICE_TABLE(of, sfp_of_match);
-
-+static void sfp_fixup_long_startup(struct sfp *sfp)
-+{
-+ sfp->module_t_start_up = T_START_UP_BAD_GPON;
-+}
-+
- static void sfp_quirk_2500basex(const struct sfp_eeprom_id *id,
- unsigned long *modes)
- {
-@@ -340,6 +345,7 @@ static const struct sfp_quirk sfp_quirks
- .vendor = "ALCATELLUCENT",
- .part = "3FE46541AA",
- .modes = sfp_quirk_2500basex,
-+ .fixup = sfp_fixup_long_startup,
- }, {
- // Huawei MA5671A can operate at 2500base-X, but report 1.2GBd
- // NRZ in their EEPROM
-@@ -2029,11 +2035,7 @@ static int sfp_sm_mod_probe(struct sfp *
- if (ret < 0)
- return ret;
-
-- if (!memcmp(id.base.vendor_name, "ALCATELLUCENT ", 16) &&
-- !memcmp(id.base.vendor_pn, "3FE46541AA ", 16))
-- sfp->module_t_start_up = T_START_UP_BAD_GPON;
-- else
-- sfp->module_t_start_up = T_START_UP;
-+ sfp->module_t_start_up = T_START_UP;
-
- if (!memcmp(id.base.vendor_name, "HUAWEI ", 16) &&
- !memcmp(id.base.vendor_pn, "MA5671A ", 16))
-@@ -2042,6 +2044,8 @@ static int sfp_sm_mod_probe(struct sfp *
- sfp->tx_fault_ignore = false;
-
- sfp->quirk = sfp_lookup_quirk(&id);
-+ if (sfp->quirk && sfp->quirk->fixup)
-+ sfp->quirk->fixup(sfp);
-
- return 0;
- }
---- a/drivers/net/phy/sfp.h
-+++ b/drivers/net/phy/sfp.h
-@@ -10,6 +10,7 @@ struct sfp_quirk {
- const char *vendor;
- const char *part;
- void (*modes)(const struct sfp_eeprom_id *id, unsigned long *modes);
-+ void (*fixup)(struct sfp *sfp);
- };
-
- struct sfp_socket_ops {
+++ /dev/null
-From 4c9d8c654827ef42da702c5b6c3392e8ac0bc60a Mon Sep 17 00:00:00 2001
-From: "Russell King (Oracle)" <rmk+kernel@armlinux.org.uk>
-Date: Fri, 26 Aug 2022 08:43:40 +0100
-Subject: [PATCH 3/6] net: sfp: move Huawei MA5671A fixup
-
-Move this module over to the new fixup mechanism.
-
-Signed-off-by: Russell King (Oracle) <rmk+kernel@armlinux.org.uk>
----
- drivers/net/phy/sfp.c | 12 +++++++-----
- 1 file changed, 7 insertions(+), 5 deletions(-)
-
---- a/drivers/net/phy/sfp.c
-+++ b/drivers/net/phy/sfp.c
-@@ -315,6 +315,11 @@ static void sfp_fixup_long_startup(struc
- sfp->module_t_start_up = T_START_UP_BAD_GPON;
- }
-
-+static void sfp_fixup_ignore_tx_fault(struct sfp *sfp)
-+{
-+ sfp->tx_fault_ignore = true;
-+}
-+
- static void sfp_quirk_2500basex(const struct sfp_eeprom_id *id,
- unsigned long *modes)
- {
-@@ -352,6 +357,7 @@ static const struct sfp_quirk sfp_quirks
- .vendor = "HUAWEI",
- .part = "MA5671A",
- .modes = sfp_quirk_2500basex,
-+ .fixup = sfp_fixup_ignore_tx_fault,
- }, {
- // Lantech 8330-262D-E can operate at 2500base-X, but
- // incorrectly report 2500MBd NRZ in their EEPROM
-@@ -2037,11 +2043,7 @@ static int sfp_sm_mod_probe(struct sfp *
-
- sfp->module_t_start_up = T_START_UP;
-
-- if (!memcmp(id.base.vendor_name, "HUAWEI ", 16) &&
-- !memcmp(id.base.vendor_pn, "MA5671A ", 16))
-- sfp->tx_fault_ignore = true;
-- else
-- sfp->tx_fault_ignore = false;
-+ sfp->tx_fault_ignore = false;
-
- sfp->quirk = sfp_lookup_quirk(&id);
- if (sfp->quirk && sfp->quirk->fixup)
+++ /dev/null
-From 43ac680124bc57951a6d0356b41498c2324388bf Mon Sep 17 00:00:00 2001
-From: "Russell King (Oracle)" <rmk+kernel@armlinux.org.uk>
-Date: Fri, 26 Aug 2022 08:43:45 +0100
-Subject: [PATCH 4/6] net: sfp: add support for HALNy GPON SFP
-
-Add a quirk for the HALNy HL-GSFP module, which appears to have an
-inverted RX_LOS signal, and possibly uses TX_FAULT as an inverted
-host-link status signal. As we can't be certain about the modules
-use of TX_FAULT, we ignore it.
-
-Signed-off-by: Russell King (Oracle) <rmk+kernel@armlinux.org.uk>
----
- drivers/net/phy/sfp-bus.c | 2 +-
- drivers/net/phy/sfp.c | 29 ++++++++++++++++++++++++++---
- 2 files changed, 27 insertions(+), 4 deletions(-)
-
---- a/drivers/net/phy/sfp-bus.c
-+++ b/drivers/net/phy/sfp-bus.c
-@@ -283,7 +283,7 @@ void sfp_parse_support(struct sfp_bus *b
- phylink_set(modes, 2500baseX_Full);
- }
-
-- if (bus->sfp_quirk)
-+ if (bus->sfp_quirk && bus->sfp_quirk->modes)
- bus->sfp_quirk->modes(id, modes);
-
- linkmode_or(support, support, modes);
---- a/drivers/net/phy/sfp.c
-+++ b/drivers/net/phy/sfp.c
-@@ -320,6 +320,23 @@ static void sfp_fixup_ignore_tx_fault(st
- sfp->tx_fault_ignore = true;
- }
-
-+static void sfp_fixup_inverted_los(struct sfp *sfp)
-+{
-+ const __be16 los_inverted = cpu_to_be16(SFP_OPTIONS_LOS_INVERTED);
-+ const __be16 los_normal = cpu_to_be16(SFP_OPTIONS_LOS_NORMAL);
-+
-+ sfp->id.ext.options &= ~los_normal;
-+ sfp->id.ext.options |= los_inverted;
-+}
-+
-+static void sfp_fixup_halny_gsfp(struct sfp *sfp)
-+{
-+ /* LOS is inverted */
-+ sfp_fixup_inverted_los(sfp);
-+ /* TX fault might be inverted, but we don't know for certain. */
-+ sfp_fixup_ignore_tx_fault(sfp);
-+}
-+
- static void sfp_quirk_2500basex(const struct sfp_eeprom_id *id,
- unsigned long *modes)
- {
-@@ -352,6 +369,10 @@ static const struct sfp_quirk sfp_quirks
- .modes = sfp_quirk_2500basex,
- .fixup = sfp_fixup_long_startup,
- }, {
-+ .vendor = "HALNy",
-+ .part = "HL-GSFP",
-+ .fixup = sfp_fixup_halny_gsfp,
-+ }, {
- // Huawei MA5671A can operate at 2500base-X, but report 1.2GBd
- // NRZ in their EEPROM
- .vendor = "HUAWEI",
-@@ -368,16 +389,18 @@ static const struct sfp_quirk sfp_quirks
- .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 */
-+ /* Trailing characters should be filled with space chars, but
-+ * some manufacturers can't read SFF-8472 and use NUL.
-+ */
- for (i = 0, size = 0; i < maxlen; i++)
-- if (str[i] != ' ')
-+ if (str[i] != ' ' && str[i] != '\0')
- size = i + 1;
-
- return size;
+++ /dev/null
-From 9a84d699ddde0d4e272aa919ad8fd50271a3f932 Mon Sep 17 00:00:00 2001
-From: "Russell King (Oracle)" <rmk+kernel@armlinux.org.uk>
-Date: Fri, 26 Aug 2022 08:48:20 +0100
-Subject: [PATCH 5/6] net: sfp: redo soft state polling
-
-Signed-off-by: Russell King (Oracle) <rmk+kernel@armlinux.org.uk>
----
- drivers/net/phy/sfp.c | 35 ++++++++++++++++++++++++-----------
- 1 file changed, 24 insertions(+), 11 deletions(-)
-
---- a/drivers/net/phy/sfp.c
-+++ b/drivers/net/phy/sfp.c
-@@ -234,6 +234,7 @@ struct sfp {
- bool need_poll;
-
- struct mutex st_mutex; /* Protects state */
-+ unsigned int state_ignore_hw_mask;
- unsigned int state_soft_mask;
- unsigned int state;
- struct delayed_work poll;
-@@ -623,17 +624,18 @@ static void sfp_soft_set_state(struct sf
- 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 &&
-- !sfp->gpio[GPIO_TX_DISABLE])
-- sfp->state_soft_mask |= SFP_F_TX_DISABLE;
-- if (id->ext.enhopts & SFP_ENHOPTS_SOFT_TX_FAULT &&
-- !sfp->gpio[GPIO_TX_FAULT])
-- sfp->state_soft_mask |= SFP_F_TX_FAULT;
-- if (id->ext.enhopts & SFP_ENHOPTS_SOFT_RX_LOS &&
-- !sfp->gpio[GPIO_LOS])
-- sfp->state_soft_mask |= SFP_F_LOS;
-+ if (id->ext.enhopts & SFP_ENHOPTS_SOFT_TX_DISABLE)
-+ mask |= SFP_F_TX_DISABLE;
-+ if (id->ext.enhopts & SFP_ENHOPTS_SOFT_TX_FAULT)
-+ mask |= SFP_F_TX_FAULT;
-+ if (id->ext.enhopts & SFP_ENHOPTS_SOFT_RX_LOS)
-+ mask |= SFP_F_LOS;
-+
-+ // Poll the soft state for hardware pins we want to ignore
-+ sfp->state_soft_mask = sfp->state_ignore_hw_mask & mask;
-
- if (sfp->state_soft_mask & (SFP_F_LOS | SFP_F_TX_FAULT) &&
- !sfp->need_poll)
-@@ -647,10 +649,12 @@ static void sfp_soft_stop_poll(struct sf
-
- static unsigned int sfp_get_state(struct sfp *sfp)
- {
-+ unsigned int soft = sfp->state_soft_mask & (SFP_F_LOS | SFP_F_TX_FAULT);
- unsigned int state = sfp->get_state(sfp);
-
-- if (state & SFP_F_PRESENT &&
-- sfp->state_soft_mask & (SFP_F_LOS | SFP_F_TX_FAULT))
-+ state &= ~sfp->state_ignore_hw_mask;
-+
-+ if (state & SFP_F_PRESENT && soft)
- state |= sfp_soft_get_state(sfp);
-
- return state;
-@@ -2064,6 +2068,15 @@ static int sfp_sm_mod_probe(struct sfp *
- if (ret < 0)
- return ret;
-
-+ /* Initialise state bits to ignore from hardware */
-+ sfp->state_ignore_hw_mask = 0;
-+ if (!sfp->gpio[GPIO_TX_DISABLE])
-+ sfp->state_ignore_hw_mask |= SFP_F_TX_DISABLE;
-+ if (!sfp->gpio[GPIO_TX_FAULT])
-+ sfp->state_ignore_hw_mask |= SFP_F_TX_FAULT;
-+ if (!sfp->gpio[GPIO_LOS])
-+ sfp->state_ignore_hw_mask |= SFP_F_LOS;
-+
- sfp->module_t_start_up = T_START_UP;
-
- sfp->tx_fault_ignore = false;
+++ /dev/null
-From 32a59a1c5dc8f6fa755bab9a5f9751fdb66bb234 Mon Sep 17 00:00:00 2001
-From: "Russell King (Oracle)" <rmk+kernel@armlinux.org.uk>
-Date: Fri, 26 Aug 2022 08:48:25 +0100
-Subject: [PATCH 6/6] net: sfp: change HALNy to ignore hardware pins
-
-Signed-off-by: Russell King (Oracle) <rmk+kernel@armlinux.org.uk>
----
- drivers/net/phy/sfp.c | 14 +-------------
- 1 file changed, 1 insertion(+), 13 deletions(-)
-
---- a/drivers/net/phy/sfp.c
-+++ b/drivers/net/phy/sfp.c
-@@ -321,21 +321,9 @@ static void sfp_fixup_ignore_tx_fault(st
- sfp->tx_fault_ignore = true;
- }
-
--static void sfp_fixup_inverted_los(struct sfp *sfp)
--{
-- const __be16 los_inverted = cpu_to_be16(SFP_OPTIONS_LOS_INVERTED);
-- const __be16 los_normal = cpu_to_be16(SFP_OPTIONS_LOS_NORMAL);
--
-- sfp->id.ext.options &= ~los_normal;
-- sfp->id.ext.options |= los_inverted;
--}
--
- static void sfp_fixup_halny_gsfp(struct sfp *sfp)
- {
-- /* LOS is inverted */
-- sfp_fixup_inverted_los(sfp);
-- /* TX fault might be inverted, but we don't know for certain. */
-- sfp_fixup_ignore_tx_fault(sfp);
-+ sfp->state_ignore_hw_mask |= SFP_F_TX_FAULT | SFP_F_LOS;
- }
-
- static void sfp_quirk_2500basex(const struct sfp_eeprom_id *id,