kernel: add SFP support for Methode DM7052 NBASE-T module
authorRussell King <linux@armlinux.org.uk>
Wed, 27 Nov 2019 11:45:30 +0000 (11:45 +0000)
committerJonas Gorski <jonas.gorski@gmail.com>
Wed, 4 Dec 2019 22:30:34 +0000 (23:30 +0100)
Add support for Methode DM7052 NBASE-T module to OpenWRT. These
patches are taken from my "phy" branch, and will be sent for the
next kernel merge window.

Signed-off-by: Russell King <linux@armlinux.org.uk>
[jonas.gorski: move patches to pending, refresh patches]
Signed-off-by: Jonas Gorski <jonas.gorski@gmail.com>
16 files changed:
package/kernel/linux/modules/netdevices.mk
target/linux/generic/config-4.19
target/linux/generic/pending-4.19/740-net-sfp-remove-incomplete-100BASE-FX-and-100BASE-LX-.patch [new file with mode: 0644]
target/linux/generic/pending-4.19/741-net-sfp-derive-interface-mode-from-ethtool-link-mode.patch [new file with mode: 0644]
target/linux/generic/pending-4.19/742-net-sfp-add-more-extended-compliance-codes.patch [new file with mode: 0644]
target/linux/generic/pending-4.19/743-net-sfp-add-module-start-stop-upstream-notifications.patch [new file with mode: 0644]
target/linux/generic/pending-4.19/744-net-sfp-move-phy_start-phy_stop-to-phylink.patch [new file with mode: 0644]
target/linux/generic/pending-4.19/745-net-mdio-i2c-add-support-for-Clause-45-accesses.patch [new file with mode: 0644]
target/linux/generic/pending-4.19/746-net-phylink-re-split-__phylink_connect_phy.patch [new file with mode: 0644]
target/linux/generic/pending-4.19/747-net-phylink-support-Clause-45-PHYs-on-SFP-modules.patch [new file with mode: 0644]
target/linux/generic/pending-4.19/748-net-phylink-split-link_an_mode-configured-and-curren.patch [new file with mode: 0644]
target/linux/generic/pending-4.19/749-net-phylink-split-phylink_sfp_module_insert.patch [new file with mode: 0644]
target/linux/generic/pending-4.19/750-net-phylink-delay-MAC-configuration-for-copper-SFP-m.patch [new file with mode: 0644]
target/linux/generic/pending-4.19/751-net-phylink-make-Broadcom-BCM84881-based-SFPs-work.patch [new file with mode: 0644]
target/linux/generic/pending-4.19/752-net-phy-add-Broadcom-BCM84881-PHY-driver.patch [new file with mode: 0644]
target/linux/generic/pending-4.19/753-net-sfp-add-support-for-Clause-45-PHYs.patch [new file with mode: 0644]

index 5f8d767679a0d3831233820d1adcd386132eb839..6e2a0333f680c9e283069ac3f6d2fc8b9e138a3f 100644 (file)
@@ -192,6 +192,23 @@ endef
 $(eval $(call KernelPackage,phy-broadcom))
 
 
+define KernelPackage/phy-bcm84881
+   SUBMENU:=$(NETWORK_DEVICES_MENU)
+   TITLE:=Broadcom BCM84881 PHY driver
+   KCONFIG:=CONFIG_BCM84881_PHY
+   DEPENDS:=+kmod-libphy
+   FILES:=$(LINUX_DIR)/drivers/net/phy/bcm84881.ko
+   AUTOLOAD:=$(call AutoLoad,18,bcm84881,1)
+endef
+
+define KernelPackage/phy-bcm84881/description
+   Supports the Broadcom 84881 PHY.
+endef
+
+$(eval $(call KernelPackage,phy-bcm84881))
+
+
+
 define KernelPackage/phy-realtek
    SUBMENU:=$(NETWORK_DEVICES_MENU)
    TITLE:=Realtek Ethernet PHY driver
index d6c6a14f38d8e5f81c817cb2fefbe358a0946860..7b81b82e19115b35cb2eef6b0089bfbf989b0d88 100644 (file)
@@ -479,6 +479,7 @@ CONFIG_BASE_SMALL=0
 # CONFIG_BCM63XX_PHY is not set
 # CONFIG_BCM7038_WDT is not set
 # CONFIG_BCM7XXX_PHY is not set
+# CONFIG_BCM84881_PHY is not set
 # CONFIG_BCM87XX_PHY is not set
 # CONFIG_BCMA is not set
 # CONFIG_BCMA_DRIVER_GPIO is not set
diff --git a/target/linux/generic/pending-4.19/740-net-sfp-remove-incomplete-100BASE-FX-and-100BASE-LX-.patch b/target/linux/generic/pending-4.19/740-net-sfp-remove-incomplete-100BASE-FX-and-100BASE-LX-.patch
new file mode 100644 (file)
index 0000000..aaeb657
--- /dev/null
@@ -0,0 +1,52 @@
+From 29cd215aaf6c2050c43e4de03aee436c16f90b96 Mon Sep 17 00:00:00 2001
+From: Russell King <rmk+kernel@armlinux.org.uk>
+Date: Thu, 21 Nov 2019 17:27:14 +0000
+Subject: [PATCH 643/660] 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.
+
+Signed-off-by: Russell King <rmk+kernel@armlinux.org.uk>
+---
+ drivers/net/phy/sfp-bus.c |  4 +---
+ drivers/net/phy/sfp.c     | 13 +------------
+ 2 files changed, 2 insertions(+), 15 deletions(-)
+
+--- a/drivers/net/phy/sfp-bus.c
++++ b/drivers/net/phy/sfp-bus.c
+@@ -341,9 +341,7 @@ phy_interface_t sfp_select_interface(str
+       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))
+--- a/drivers/net/phy/sfp.c
++++ b/drivers/net/phy/sfp.c
+@@ -1413,18 +1413,7 @@ static void sfp_sm_fault(struct sfp *sfp
+ 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);
+ }
diff --git a/target/linux/generic/pending-4.19/741-net-sfp-derive-interface-mode-from-ethtool-link-mode.patch b/target/linux/generic/pending-4.19/741-net-sfp-derive-interface-mode-from-ethtool-link-mode.patch
new file mode 100644 (file)
index 0000000..f20b66e
--- /dev/null
@@ -0,0 +1,89 @@
+From dc45d9e04572b5cd6d32f51cdf9f62b18022e6dd Mon Sep 17 00:00:00 2001
+From: Russell King <rmk+kernel@armlinux.org.uk>
+Date: Thu, 21 Nov 2019 17:32:59 +0000
+Subject: [PATCH 644/660] 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().
+
+Signed-off-by: Russell King <rmk+kernel@armlinux.org.uk>
+---
+ 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(-)
+
+--- a/drivers/net/phy/marvell10g.c
++++ b/drivers/net/phy/marvell10g.c
+@@ -227,7 +227,7 @@ static int mv3310_sfp_insert(void *upstr
+       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");
+--- a/drivers/net/phy/phylink.c
++++ b/drivers/net/phy/phylink.c
+@@ -1663,7 +1663,7 @@ static int phylink_sfp_module_insert(voi
+       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) {
+               netdev_err(pl->netdev,
+                          "selection of interface failed, advertisement %*pb\n",
+--- a/drivers/net/phy/sfp-bus.c
++++ b/drivers/net/phy/sfp-bus.c
+@@ -319,16 +319,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) ||
+@@ -341,7 +337,8 @@ phy_interface_t sfp_select_interface(str
+       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))
+--- a/include/linux/sfp.h
++++ b/include/linux/sfp.h
+@@ -504,7 +504,6 @@ int sfp_parse_port(struct sfp_bus *bus,
+ 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(str
+ }
+ 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;
diff --git a/target/linux/generic/pending-4.19/742-net-sfp-add-more-extended-compliance-codes.patch b/target/linux/generic/pending-4.19/742-net-sfp-add-more-extended-compliance-codes.patch
new file mode 100644 (file)
index 0000000..266c7c2
--- /dev/null
@@ -0,0 +1,251 @@
+From c66a4e76c8554c84e64b9315314576ac403c6641 Mon Sep 17 00:00:00 2001
+From: Russell King <rmk+kernel@armlinux.org.uk>
+Date: Thu, 26 Sep 2019 15:14:18 +0100
+Subject: [PATCH 645/660] 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.
+
+Signed-off-by: Russell King <rmk+kernel@armlinux.org.uk>
+---
+ 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(-)
+
+--- a/drivers/net/phy/sfp-bus.c
++++ b/drivers/net/phy/sfp-bus.c
+@@ -123,35 +123,35 @@ int sfp_parse_port(struct sfp_bus *bus,
+       /* 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:
+@@ -260,22 +260,33 @@ void sfp_parse_support(struct sfp_bus *b
+       }
+       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",
+@@ -300,7 +311,7 @@ void sfp_parse_support(struct sfp_bus *b
+        */
+       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);
+       }
+@@ -331,7 +342,8 @@ phy_interface_t sfp_select_interface(str
+           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))
+--- a/drivers/net/phy/sfp.c
++++ b/drivers/net/phy/sfp.c
+@@ -229,7 +229,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;
+ }
+@@ -240,7 +240,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;
+ }
+--- 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),
diff --git a/target/linux/generic/pending-4.19/743-net-sfp-add-module-start-stop-upstream-notifications.patch b/target/linux/generic/pending-4.19/743-net-sfp-add-module-start-stop-upstream-notifications.patch
new file mode 100644 (file)
index 0000000..e0eea5e
--- /dev/null
@@ -0,0 +1,131 @@
+From f9a5a54b59cb904b37bf7409a43635ab195d0214 Mon Sep 17 00:00:00 2001
+From: Russell King <rmk+kernel@armlinux.org.uk>
+Date: Tue, 19 Nov 2019 10:13:25 +0000
+Subject: [PATCH 646/660] 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.
+
+Signed-off-by: Russell King <rmk+kernel@armlinux.org.uk>
+---
+ 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(+)
+
+--- a/drivers/net/phy/sfp-bus.c
++++ b/drivers/net/phy/sfp-bus.c
+@@ -711,6 +711,27 @@ void sfp_module_remove(struct sfp_bus *b
+ }
+ 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;
+--- a/drivers/net/phy/sfp.c
++++ b/drivers/net/phy/sfp.c
+@@ -57,6 +57,7 @@ enum {
+       SFP_DEV_UP,
+       SFP_S_DOWN = 0,
++      SFP_S_FAIL,
+       SFP_S_WAIT,
+       SFP_S_INIT,
+       SFP_S_INIT_TX_FAULT,
+@@ -120,6 +121,7 @@ static const char *event_to_str(unsigned
+ 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",
+@@ -1749,6 +1751,8 @@ static void sfp_sm_main(struct sfp *sfp,
+               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);
+@@ -1815,6 +1819,10 @@ static void sfp_sm_main(struct sfp *sfp,
+                        * 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 */
+--- 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);
+--- 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 *);
diff --git a/target/linux/generic/pending-4.19/744-net-sfp-move-phy_start-phy_stop-to-phylink.patch b/target/linux/generic/pending-4.19/744-net-sfp-move-phy_start-phy_stop-to-phylink.patch
new file mode 100644 (file)
index 0000000..b3c47a0
--- /dev/null
@@ -0,0 +1,72 @@
+From e2dc261b872a92a055eb2e86ac136baf9b20f2f2 Mon Sep 17 00:00:00 2001
+From: Russell King <rmk+kernel@armlinux.org.uk>
+Date: Thu, 21 Nov 2019 17:21:33 +0000
+Subject: [PATCH 647/660] 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.
+
+Signed-off-by: Russell King <rmk+kernel@armlinux.org.uk>
+---
+ drivers/net/phy/phylink.c | 22 ++++++++++++++++++++++
+ drivers/net/phy/sfp.c     |  2 --
+ 2 files changed, 22 insertions(+), 2 deletions(-)
+
+--- a/drivers/net/phy/phylink.c
++++ b/drivers/net/phy/phylink.c
+@@ -1717,6 +1717,26 @@ static int phylink_sfp_module_insert(voi
+       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;
+@@ -1752,6 +1772,8 @@ static const struct sfp_upstream_ops sfp
+       .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,
+--- a/drivers/net/phy/sfp.c
++++ b/drivers/net/phy/sfp.c
+@@ -1320,7 +1320,6 @@ static void sfp_sm_mod_next(struct sfp *
+ 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);
+@@ -1351,7 +1350,6 @@ static void sfp_sm_probe_phy(struct sfp
+       }
+       sfp->mod_phy = phy;
+-      phy_start(phy);
+ }
+ static void sfp_sm_link_up(struct sfp *sfp)
diff --git a/target/linux/generic/pending-4.19/745-net-mdio-i2c-add-support-for-Clause-45-accesses.patch b/target/linux/generic/pending-4.19/745-net-mdio-i2c-add-support-for-Clause-45-accesses.patch
new file mode 100644 (file)
index 0000000..9a4bb5b
--- /dev/null
@@ -0,0 +1,74 @@
+From c9de73988a35c6c85810a992954ac568cca503e5 Mon Sep 17 00:00:00 2001
+From: Russell King <rmk+kernel@armlinux.org.uk>
+Date: Wed, 2 Oct 2019 10:31:10 +0100
+Subject: [PATCH 648/660] 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.
+
+Signed-off-by: Russell King <rmk+kernel@armlinux.org.uk>
+---
+ drivers/net/phy/mdio-i2c.c | 28 ++++++++++++++++++++--------
+ 1 file changed, 20 insertions(+), 8 deletions(-)
+
+--- a/drivers/net/phy/mdio-i2c.c
++++ b/drivers/net/phy/mdio-i2c.c
+@@ -36,17 +36,24 @@ static int i2c_mii_read(struct mii_bus *
+ {
+       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);
+@@ -64,18 +71,23 @@ static int i2c_mii_write(struct mii_bus
+       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);
diff --git a/target/linux/generic/pending-4.19/746-net-phylink-re-split-__phylink_connect_phy.patch b/target/linux/generic/pending-4.19/746-net-phylink-re-split-__phylink_connect_phy.patch
new file mode 100644 (file)
index 0000000..c74a56c
--- /dev/null
@@ -0,0 +1,93 @@
+From 0db7fba746b5608c30d4e2ba1c99a2a309e2d288 Mon Sep 17 00:00:00 2001
+From: Russell King <rmk+kernel@armlinux.org.uk>
+Date: Fri, 8 Nov 2019 15:22:48 +0000
+Subject: [PATCH 649/660] 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.
+
+Signed-off-by: Russell King <rmk+kernel@armlinux.org.uk>
+---
+ drivers/net/phy/phylink.c | 39 ++++++++++++++++++++++++---------------
+ 1 file changed, 24 insertions(+), 15 deletions(-)
+
+--- a/drivers/net/phy/phylink.c
++++ b/drivers/net/phy/phylink.c
+@@ -728,11 +728,9 @@ static int phylink_bringup_phy(struct ph
+       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))))
+@@ -741,15 +739,7 @@ static int __phylink_connect_phy(struct
+       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);
+ }
+ /**
+@@ -769,13 +759,23 @@ static int __phylink_connect_phy(struct
+  */
+ 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);
+@@ -1759,8 +1759,17 @@ static void phylink_sfp_link_up(void *up
+ 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)
diff --git a/target/linux/generic/pending-4.19/747-net-phylink-support-Clause-45-PHYs-on-SFP-modules.patch b/target/linux/generic/pending-4.19/747-net-phylink-support-Clause-45-PHYs-on-SFP-modules.patch
new file mode 100644 (file)
index 0000000..2cea118
--- /dev/null
@@ -0,0 +1,89 @@
+From caf32f96f13df7d3ae6cb8bf8001c88ae22025ca Mon Sep 17 00:00:00 2001
+From: Russell King <rmk+kernel@armlinux.org.uk>
+Date: Fri, 8 Nov 2019 15:28:22 +0000
+Subject: [PATCH 650/660] 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.
+
+Signed-off-by: Russell King <rmk+kernel@armlinux.org.uk>
+---
+ drivers/net/phy/phylink.c | 21 ++++++++++++++++-----
+ 1 file changed, 16 insertions(+), 5 deletions(-)
+
+--- a/drivers/net/phy/phylink.c
++++ b/drivers/net/phy/phylink.c
+@@ -671,7 +671,8 @@ static void phylink_phy_change(struct ph
+                  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);
+@@ -691,7 +692,7 @@ static int phylink_bringup_phy(struct ph
+       ethtool_convert_legacy_u32_to_link_mode(supported, phy->supported);
+       ethtool_convert_legacy_u32_to_link_mode(config.advertising,
+                                               phy->advertising);
+-      config.interface = pl->link_config.interface;
++      config.interface = interface;
+       ret = phylink_validate(pl, supported, &config);
+       if (ret)
+@@ -707,6 +708,7 @@ static int phylink_bringup_phy(struct ph
+       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);
+@@ -771,7 +773,7 @@ int phylink_connect_phy(struct phylink *
+       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);
+@@ -824,7 +826,7 @@ int phylink_of_phy_connect(struct phylin
+       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);
+@@ -1759,13 +1761,22 @@ static void phylink_sfp_link_up(void *up
+ 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);
diff --git a/target/linux/generic/pending-4.19/748-net-phylink-split-link_an_mode-configured-and-curren.patch b/target/linux/generic/pending-4.19/748-net-phylink-split-link_an_mode-configured-and-curren.patch
new file mode 100644 (file)
index 0000000..f30d37f
--- /dev/null
@@ -0,0 +1,254 @@
+From d1339d6956f0255b6ce2412328a98945be8cc3ca Mon Sep 17 00:00:00 2001
+From: Russell King <rmk+kernel@armlinux.org.uk>
+Date: Sat, 16 Nov 2019 11:30:18 +0000
+Subject: [PATCH 651/660] 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.
+
+Signed-off-by: Russell King <rmk+kernel@armlinux.org.uk>
+---
+ drivers/net/phy/phylink.c | 59 ++++++++++++++++++++-------------------
+ 1 file changed, 31 insertions(+), 28 deletions(-)
+
+--- 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);
+@@ -253,12 +254,12 @@ static int phylink_parse_mode(struct phy
+       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) {
+                       netdev_err(pl->netdev,
+                                  "can't use both fixed-link and in-band-status\n");
+                       return -EINVAL;
+@@ -270,7 +271,7 @@ static int phylink_parse_mode(struct phy
+               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:
+@@ -330,14 +331,14 @@ static void phylink_mac_config(struct ph
+ {
+       netdev_dbg(pl->netdev,
+                  "%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->netdev, pl->link_an_mode, state);
++      pl->ops->mac_config(pl->netdev, pl->cur_link_an_mode, state);
+ }
+ static void phylink_mac_config_up(struct phylink *pl,
+@@ -446,7 +447,7 @@ static void phylink_resolve(struct work_
+       } 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);
+@@ -483,12 +484,12 @@ static void phylink_resolve(struct work_
+       if (link_state.link != netif_carrier_ok(ndev)) {
+               if (!link_state.link) {
+                       netif_carrier_off(ndev);
+-                      pl->ops->mac_link_down(ndev, pl->link_an_mode,
++                      pl->ops->mac_link_down(ndev, pl->cur_link_an_mode,
+                                              pl->cur_interface);
+                       netdev_info(ndev, "Link is Down\n");
+               } else {
+                       pl->cur_interface = link_state.interface;
+-                      pl->ops->mac_link_up(ndev, pl->link_an_mode,
++                      pl->ops->mac_link_up(ndev, pl->cur_link_an_mode,
+                                            pl->cur_interface, pl->phydev);
+                       netif_carrier_on(ndev);
+@@ -610,7 +611,7 @@ struct phylink *phylink_create(struct ne
+               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);
+@@ -618,6 +619,8 @@ struct phylink *phylink_create(struct ne
+               }
+       }
++      pl->cur_link_an_mode = pl->cfg_link_an_mode;
++
+       ret = phylink_register_sfp(pl, fwnode);
+       if (ret < 0) {
+               kfree(pl);
+@@ -733,8 +736,8 @@ static int phylink_bringup_phy(struct ph
+ 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;
+@@ -801,8 +804,8 @@ int phylink_of_phy_connect(struct phylin
+       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;
+@@ -813,7 +816,7 @@ int phylink_of_phy_connect(struct phylin
+               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;
+       }
+@@ -876,7 +879,7 @@ int phylink_fixed_state_cb(struct phylin
+       /* 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);
+@@ -926,7 +929,7 @@ void phylink_start(struct phylink *pl)
+       ASSERT_RTNL();
+       netdev_info(pl->netdev, "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 */
+@@ -948,7 +951,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) {
+@@ -963,7 +966,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);
+@@ -1090,7 +1093,7 @@ int phylink_ethtool_ksettings_get(struct
+       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
+@@ -1163,7 +1166,7 @@ int phylink_ethtool_ksettings_set(struct
+               /* 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;
+@@ -1175,7 +1178,7 @@ int phylink_ethtool_ksettings_set(struct
+               __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;
+@@ -1217,7 +1220,7 @@ int phylink_ethtool_ksettings_set(struct
+        * 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);
+@@ -1307,7 +1310,7 @@ int phylink_ethtool_set_pauseparam(struc
+                                  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);
+@@ -1496,7 +1499,7 @@ static int phylink_mii_read(struct phyli
+       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);
+@@ -1524,7 +1527,7 @@ static int phylink_mii_read(struct phyli
+ 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;
+@@ -1698,10 +1701,10 @@ static int phylink_sfp_module_insert(voi
+               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;
diff --git a/target/linux/generic/pending-4.19/749-net-phylink-split-phylink_sfp_module_insert.patch b/target/linux/generic/pending-4.19/749-net-phylink-split-phylink_sfp_module_insert.patch
new file mode 100644 (file)
index 0000000..87d70d1
--- /dev/null
@@ -0,0 +1,120 @@
+From 36569971241ae6b81376da4937d2c8760122d10b Mon Sep 17 00:00:00 2001
+From: Russell King <rmk+kernel@armlinux.org.uk>
+Date: Thu, 21 Nov 2019 17:58:58 +0000
+Subject: [PATCH 652/660] net: phylink: split phylink_sfp_module_insert()
+
+Split out the configuration step from phylink_sfp_module_insert() so
+we can re-use this later.
+
+Signed-off-by: Russell King <rmk+kernel@armlinux.org.uk>
+---
+ drivers/net/phy/phylink.c | 47 +++++++++++++++++++++++----------------
+ 1 file changed, 28 insertions(+), 19 deletions(-)
+
+--- a/drivers/net/phy/phylink.c
++++ b/drivers/net/phy/phylink.c
+@@ -1633,25 +1633,21 @@ static void phylink_sfp_detach(void *ups
+       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;
+@@ -1666,8 +1662,6 @@ static int phylink_sfp_module_insert(voi
+               return ret;
+       }
+-      linkmode_copy(support1, support);
+-
+       iface = sfp_select_interface(pl->sfp_bus, config.advertising);
+       if (iface == PHY_INTERFACE_MODE_NA) {
+               netdev_err(pl->netdev,
+@@ -1677,18 +1671,18 @@ static int phylink_sfp_module_insert(voi
+       }
+       config.interface = iface;
++      linkmode_copy(support1, support);
+       ret = phylink_validate(pl, support1, &config);
+       if (ret) {
+               netdev_err(pl->netdev, "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;
+       }
+       netdev_dbg(pl->netdev, "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)
+@@ -1701,15 +1695,15 @@ static int phylink_sfp_module_insert(voi
+               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;
+               netdev_info(pl->netdev, "switched to %s/%s link mode\n",
+-                          phylink_an_mode_str(MLO_AN_INBAND),
++                          phylink_an_mode_str(mode),
+                           phy_modes(config.interface));
+       }
+@@ -1722,6 +1716,21 @@ static int phylink_sfp_module_insert(voi
+       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;
diff --git a/target/linux/generic/pending-4.19/750-net-phylink-delay-MAC-configuration-for-copper-SFP-m.patch b/target/linux/generic/pending-4.19/750-net-phylink-delay-MAC-configuration-for-copper-SFP-m.patch
new file mode 100644 (file)
index 0000000..358b9a1
--- /dev/null
@@ -0,0 +1,204 @@
+From eb514428f75bc67d12ff019c44a8f8ca9f33c54c Mon Sep 17 00:00:00 2001
+From: Russell King <rmk+kernel@armlinux.org.uk>
+Date: Thu, 21 Nov 2019 17:42:49 +0000
+Subject: [PATCH 653/660] 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.
+
+Signed-off-by: Russell King <rmk+kernel@armlinux.org.uk>
+---
+ drivers/net/phy/phylink.c | 59 +++++++++++++++++++++++++++++++--------
+ drivers/net/phy/sfp-bus.c | 28 +++++++++++++++++++
+ include/linux/sfp.h       |  7 +++++
+ 3 files changed, 83 insertions(+), 11 deletions(-)
+
+--- 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;
+ };
+ static inline void linkmode_zero(unsigned long *dst)
+@@ -1633,7 +1636,7 @@ static void phylink_sfp_detach(void *ups
+       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)
+ {
+@@ -1707,7 +1710,7 @@ static int phylink_sfp_config(struct phy
+                           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))
+@@ -1720,15 +1723,20 @@ static int phylink_sfp_module_insert(voi
+                                    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)
+@@ -1736,10 +1744,19 @@ static int phylink_sfp_module_start(void
+       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)
+@@ -1773,10 +1790,30 @@ static void phylink_sfp_link_up(void *up
+ static int phylink_sfp_connect_phy(void *upstream, struct phy_device *phy)
+ {
+       struct phylink *pl = upstream;
+-      phy_interface_t interface = pl->link_config.interface;
++      __ETHTOOL_DECLARE_LINK_MODE_MASK(supported);
++      __ETHTOOL_DECLARE_LINK_MODE_MASK(advertising);
++      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);
++
++      ethtool_convert_legacy_u32_to_link_mode(supported, phy->supported);
++      ethtool_convert_legacy_u32_to_link_mode(advertising, phy->advertising);
++
++      /* Do the initial configuration */
++      ret = phylink_sfp_config(pl, ML_AN_INBAND, supported, advertising);
++      if (ret < 0)
++              return ret;
++
++      interface = pl->link_config.interface;
++      ret = phylink_attach_phy(pl, phy, interface);
+       if (ret < 0)
+               return ret;
+--- a/drivers/net/phy/sfp-bus.c
++++ b/drivers/net/phy/sfp-bus.c
+@@ -102,6 +102,7 @@ static const struct sfp_quirk *sfp_looku
+       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,
+ 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
+  * @id: a pointer to the module's &struct sfp_eeprom_id
+--- 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
+       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)
diff --git a/target/linux/generic/pending-4.19/751-net-phylink-make-Broadcom-BCM84881-based-SFPs-work.patch b/target/linux/generic/pending-4.19/751-net-phylink-make-Broadcom-BCM84881-based-SFPs-work.patch
new file mode 100644 (file)
index 0000000..1724d44
--- /dev/null
@@ -0,0 +1,58 @@
+From 3d8592a23dd67fb78ad85ddf711a059d3880fcb4 Mon Sep 17 00:00:00 2001
+From: Russell King <rmk+kernel@armlinux.org.uk>
+Date: Fri, 8 Nov 2019 17:19:16 +0000
+Subject: [PATCH 654/660] 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.
+
+Signed-off-by: Russell King <rmk+kernel@armlinux.org.uk>
+---
+ drivers/net/phy/phylink.c | 17 ++++++++++++++++-
+ 1 file changed, 16 insertions(+), 1 deletion(-)
+
+--- a/drivers/net/phy/phylink.c
++++ b/drivers/net/phy/phylink.c
+@@ -1787,12 +1787,22 @@ static void phylink_sfp_link_up(void *up
+       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;
+       __ETHTOOL_DECLARE_LINK_MODE_MASK(supported);
+       __ETHTOOL_DECLARE_LINK_MODE_MASK(advertising);
+       phy_interface_t interface;
++      u8 mode;
+       int ret;
+       /*
+@@ -1807,8 +1817,13 @@ static int phylink_sfp_connect_phy(void
+       ethtool_convert_legacy_u32_to_link_mode(supported, phy->supported);
+       ethtool_convert_legacy_u32_to_link_mode(advertising, phy->advertising);
++      if (phylink_phy_no_inband(phy))
++              mode = MLO_AN_PHY;
++      else
++              mode = MLO_AN_INBAND;
++
+       /* Do the initial configuration */
+-      ret = phylink_sfp_config(pl, ML_AN_INBAND, supported, advertising);
++      ret = phylink_sfp_config(pl, mode, supported, advertising);
+       if (ret < 0)
+               return ret;
diff --git a/target/linux/generic/pending-4.19/752-net-phy-add-Broadcom-BCM84881-PHY-driver.patch b/target/linux/generic/pending-4.19/752-net-phy-add-Broadcom-BCM84881-PHY-driver.patch
new file mode 100644 (file)
index 0000000..a69cf39
--- /dev/null
@@ -0,0 +1,333 @@
+From 0f669e10ede7f06bb998373de6a9d169f47fcc66 Mon Sep 17 00:00:00 2001
+From: Russell King <rmk+kernel@armlinux.org.uk>
+Date: Tue, 5 Nov 2019 11:54:30 +0000
+Subject: [PATCH 655/660] net: phy: add Broadcom BCM84881 PHY driver
+
+Add a rudimentary Clause 45 driver for the BCM84881 PHY, found on
+Methode DM7052 SFPs.
+
+Signed-off-by: Russell King <rmk+kernel@armlinux.org.uk>
+---
+ drivers/net/phy/Kconfig    |   5 +
+ drivers/net/phy/Makefile   |   1 +
+ drivers/net/phy/bcm84881.c | 290 +++++++++++++++++++++++++++++++++++++
+ 3 files changed, 296 insertions(+)
+ create mode 100644 drivers/net/phy/bcm84881.c
+
+--- a/drivers/net/phy/Kconfig
++++ b/drivers/net/phy/Kconfig
+@@ -280,6 +280,11 @@ config BROADCOM_PHY
+         Currently supports the BCM5411, BCM5421, BCM5461, BCM54616S, BCM5464,
+         BCM5481, BCM54810 and BCM5482 PHYs.
++config BCM84881_PHY
++      tristate "Broadcom BCM84881 PHY"
++      ---help---
++        Support the Broadcom BCM84881 PHY.
++
+ config CICADA_PHY
+       tristate "Cicada PHYs"
+       ---help---
+--- a/drivers/net/phy/Makefile
++++ b/drivers/net/phy/Makefile
+@@ -54,6 +54,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
+--- /dev/null
++++ b/drivers/net/phy/bcm84881.c
+@@ -0,0 +1,290 @@
++// 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 <linux/delay.h>
++#include <linux/module.h>
++#include <linux/phy.h>
++
++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 genphy_c45_an_config_aneg(struct phy_device *phydev)
++{
++      bool changed = false;
++      u32 advertising;
++      int ret;
++
++      phydev->advertising &= phydev->supported;
++      advertising = phydev->advertising;
++
++      ret = phy_modify_mmd_changed(phydev, MDIO_MMD_AN, MDIO_AN_ADVERTISE,
++                                   ADVERTISE_ALL | ADVERTISE_100BASE4 |
++                                   ADVERTISE_PAUSE_CAP | ADVERTISE_PAUSE_ASYM,
++                                   ethtool_adv_to_mii_adv_t(advertising));
++      if (ret < 0)
++              return ret;
++      if (ret > 0)
++              changed = true;
++
++      ret = phy_modify_mmd_changed(phydev, MDIO_MMD_AN, MDIO_AN_10GBT_CTRL,
++                                   MDIO_AN_10GBT_CTRL_ADV10G,
++                                   advertising & ADVERTISED_10000baseT_Full ?
++                                      MDIO_AN_10GBT_CTRL_ADV10G : 0);
++      if (ret < 0)
++              return ret;
++      if (ret > 0)
++              changed = true;
++
++      return genphy_c45_check_and_restart_aneg(phydev, changed);
++}
++
++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 = ethtool_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)
++{
++      bool autoneg_complete;
++      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;
++
++      autoneg_complete = !!(val & MDIO_AN_STAT1_COMPLETE) &&
++                         !!(bmsr & BMSR_ANEGCOMPLETE);
++      phydev->link = !!(val & MDIO_STAT1_LSTATUS) &&
++                     !!(bmsr & BMSR_LSTATUS);
++      if (phydev->autoneg == AUTONEG_ENABLE && !autoneg_complete)
++              phydev->link = false;
++
++      if (!phydev->link)
++              return 0;
++
++      phydev->lp_advertising = 0;
++      phydev->speed = SPEED_UNKNOWN;
++      phydev->duplex = DUPLEX_UNKNOWN;
++      phydev->pause = 0;
++      phydev->asym_pause = 0;
++      phydev->mdix = 0;
++
++      if (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;
++
++              phydev->lp_advertising |= mii_stat1000_to_ethtool_lpa_t(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",
++              .features       = SUPPORTED_100baseT_Full |
++                                SUPPORTED_100baseT_Half |
++                                SUPPORTED_1000baseT_Full |
++                                SUPPORTED_Autoneg |
++                                SUPPORTED_TP |
++                                SUPPORTED_FIBRE |
++                                SUPPORTED_10000baseT_Full |
++                                SUPPORTED_Backplane,
++              .config_init    = bcm84881_config_init,
++              .probe          = bcm84881_probe,
++              .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");
diff --git a/target/linux/generic/pending-4.19/753-net-sfp-add-support-for-Clause-45-PHYs.patch b/target/linux/generic/pending-4.19/753-net-sfp-add-support-for-Clause-45-PHYs.patch
new file mode 100644 (file)
index 0000000..a710901
--- /dev/null
@@ -0,0 +1,94 @@
+From 6df6709dc3d00e0bc948d45dfa8d8f18ba379c48 Mon Sep 17 00:00:00 2001
+From: Russell King <rmk+kernel@armlinux.org.uk>
+Date: Tue, 5 Nov 2019 11:56:18 +0000
+Subject: [PATCH 656/660] 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.
+
+Signed-off-by: Russell King <rmk+kernel@armlinux.org.uk>
+---
+ drivers/net/phy/sfp.c | 44 +++++++++++++++++++++++++++++++++++++++----
+ 1 file changed, 40 insertions(+), 4 deletions(-)
+
+--- a/drivers/net/phy/sfp.c
++++ b/drivers/net/phy/sfp.c
+@@ -1326,12 +1326,12 @@ static void sfp_sm_phy_detach(struct 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;
+@@ -1341,6 +1341,13 @@ static void sfp_sm_probe_phy(struct 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);
+@@ -1411,10 +1418,32 @@ static void sfp_sm_fault(struct sfp *sfp
+       }
+ }
++/* 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)
+@@ -1474,6 +1503,13 @@ static int sfp_sm_mod_hpower(struct sfp
+               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