sfp: improve support for direct-attach copper cables
authorRussell King <rmk+kernel@armlinux.org.uk>
Fri, 29 Dec 2017 12:15:28 +0000 (12:15 +0000)
committerDavid S. Miller <davem@davemloft.net>
Wed, 3 Jan 2018 02:45:32 +0000 (21:45 -0500)
Improve the support for direct-attach copper so that we avoid kernel
warning messages, and report the appropriate PORT_DA type to userspace.
Direct Attach cables can use a number of protocols depending on their
range of speeds.

Signed-off-by: Russell King <rmk+kernel@armlinux.org.uk>
Signed-off-by: David S. Miller <davem@davemloft.net>
drivers/net/phy/sfp-bus.c
include/linux/sfp.h

index ba42e39bd1122bd436d7fd23560a84f8971d267b..3ecc378e07169eb5510e3e4a0b9105834cba337f 100644 (file)
@@ -57,21 +57,19 @@ int sfp_parse_port(struct sfp_bus *bus, const struct sfp_eeprom_id *id,
        case SFP_CONNECTOR_MT_RJ:
        case SFP_CONNECTOR_MU:
        case SFP_CONNECTOR_OPTICAL_PIGTAIL:
-               if (support)
-                       phylink_set(support, FIBRE);
                port = PORT_FIBRE;
                break;
 
        case SFP_CONNECTOR_RJ45:
-               if (support)
-                       phylink_set(support, TP);
                port = PORT_TP;
                break;
 
+       case SFP_CONNECTOR_COPPER_PIGTAIL:
+               port = PORT_DA;
+               break;
+
        case SFP_CONNECTOR_UNSPEC:
                if (id->base.e1000_base_t) {
-                       if (support)
-                               phylink_set(support, TP);
                        port = PORT_TP;
                        break;
                }
@@ -80,7 +78,6 @@ int sfp_parse_port(struct sfp_bus *bus, const struct sfp_eeprom_id *id,
        case SFP_CONNECTOR_MPO_1X12:
        case SFP_CONNECTOR_MPO_2X16:
        case SFP_CONNECTOR_HSSDC_II:
-       case SFP_CONNECTOR_COPPER_PIGTAIL:
        case SFP_CONNECTOR_NOSEPARATE:
        case SFP_CONNECTOR_MXC_2X16:
                port = PORT_OTHER;
@@ -92,6 +89,18 @@ int sfp_parse_port(struct sfp_bus *bus, const struct sfp_eeprom_id *id,
                break;
        }
 
+       if (support) {
+               switch (port) {
+               case PORT_FIBRE:
+                       phylink_set(support, FIBRE);
+                       break;
+
+               case PORT_TP:
+                       phylink_set(support, TP);
+                       break;
+               }
+       }
+
        return port;
 }
 EXPORT_SYMBOL_GPL(sfp_parse_port);
@@ -143,6 +152,11 @@ phy_interface_t sfp_parse_interface(struct sfp_bus *bus,
                break;
 
        default:
+               if (id->base.e1000_base_cx) {
+                       iface = PHY_INTERFACE_MODE_1000BASEX;
+                       break;
+               }
+
                iface = PHY_INTERFACE_MODE_NA;
                dev_err(bus->sfp_dev,
                        "SFP module encoding does not support 8b10b nor 64b66b\n");
@@ -208,6 +222,29 @@ void sfp_parse_support(struct sfp_bus *bus, const struct sfp_eeprom_id *id,
            br_min <= 1300 && br_max >= 1200)
                phylink_set(support, 1000baseX_Full);
 
+       /* For active or passive cables, select the link modes
+        * based on the bit rates and the cable compliance bytes.
+        */
+       if ((id->base.sfp_ct_passive || id->base.sfp_ct_active) && br_nom) {
+               /* This may look odd, but some manufacturers use 12000MBd */
+               if (br_min <= 12000 && br_max >= 10300)
+                       phylink_set(support, 10000baseCR_Full);
+               if (br_min <= 3200 && br_max >= 3100)
+                       phylink_set(support, 2500baseX_Full);
+               if (br_min <= 1300 && br_max >= 1200)
+                       phylink_set(support, 1000baseX_Full);
+       }
+       if (id->base.sfp_ct_passive) {
+               if (id->base.passive.sff8431_app_e)
+                       phylink_set(support, 10000baseCR_Full);
+       }
+       if (id->base.sfp_ct_active) {
+               if (id->base.active.sff8431_app_e ||
+                   id->base.active.sff8431_lim) {
+                       phylink_set(support, 10000baseCR_Full);
+               }
+       }
+
        switch (id->base.extended_cc) {
        case 0x00: /* Unspecified */
                break;
index 0c5c5f6ae1ec78ec125ff6461b2ed369560b57ae..e724d5a3dd80e5cee3bb4f31d06d76b3a9cf901e 100644 (file)
@@ -165,7 +165,41 @@ struct sfp_eeprom_base {
        char vendor_rev[4];
        union {
                __be16 optical_wavelength;
-               u8 cable_spec;
+               __be16 cable_compliance;
+               struct {
+#if defined __BIG_ENDIAN_BITFIELD
+                       u8 reserved60_2:6;
+                       u8 fc_pi_4_app_h:1;
+                       u8 sff8431_app_e:1;
+                       u8 reserved61:8;
+#elif defined __LITTLE_ENDIAN_BITFIELD
+                       u8 sff8431_app_e:1;
+                       u8 fc_pi_4_app_h:1;
+                       u8 reserved60_2:6;
+                       u8 reserved61:8;
+#else
+#error Unknown Endian
+#endif
+               } __packed passive;
+               struct {
+#if defined __BIG_ENDIAN_BITFIELD
+                       u8 reserved60_4:4;
+                       u8 fc_pi_4_lim:1;
+                       u8 sff8431_lim:1;
+                       u8 fc_pi_4_app_h:1;
+                       u8 sff8431_app_e:1;
+                       u8 reserved61:8;
+#elif defined __LITTLE_ENDIAN_BITFIELD
+                       u8 sff8431_app_e:1;
+                       u8 fc_pi_4_app_h:1;
+                       u8 sff8431_lim:1;
+                       u8 fc_pi_4_lim:1;
+                       u8 reserved60_4:4;
+                       u8 reserved61:8;
+#else
+#error Unknown Endian
+#endif
+               } __packed active;
        } __packed;
        u8 reserved62;
        u8 cc_base;