realtek: Add phylink configuration routines for RTL93xx
authorBirger Koblitz <git@birger-koblitz.de>
Wed, 8 Sep 2021 14:13:18 +0000 (16:13 +0200)
committerJohn Crispin <john@phrozen.org>
Sat, 9 Oct 2021 06:25:06 +0000 (08:25 +0200)
This adds RTL93xx-specific MAC configuration routines that allow also configuration
of 10GBit links for phylink. There is support for the Realtek-specific HISGMI
protocol.

Signed-off-by: Birger Koblitz <git@birger-koblitz.de>
target/linux/realtek/files-5.10/drivers/net/dsa/rtl83xx/dsa.c
target/linux/realtek/files-5.10/drivers/net/dsa/rtl83xx/rtl838x.h
target/linux/realtek/patches-5.10/704-include-linux-add-phy-hsgmii-mode.patch [new file with mode: 0644]

index c79b4b2c811775a5e856e4e139a8feec6c7c3bc4..3a0806e70a1b6f8f8792dd17deffed7ef0beda80 100644 (file)
@@ -280,7 +280,7 @@ static void rtl83xx_phylink_validate(struct dsa_switch *ds, int port,
                phylink_set(mask, 1000baseX_Full);
 
        /* On the RTL839x family of SoCs, ports 48 to 51 are SFP ports */
-       if (port >=48 && port <= 51 && priv->family_id == RTL8390_FAMILY_ID)
+       if (port >= 48 && port <= 51 && priv->family_id == RTL8390_FAMILY_ID)
                phylink_set(mask, 1000baseX_Full);
 
        phylink_set(mask, 10baseT_Half);
@@ -294,6 +294,67 @@ static void rtl83xx_phylink_validate(struct dsa_switch *ds, int port,
                   __ETHTOOL_LINK_MODE_MASK_NBITS);
 }
 
+static void rtl93xx_phylink_validate(struct dsa_switch *ds, int port,
+                                    unsigned long *supported,
+                                    struct phylink_link_state *state)
+{
+       __ETHTOOL_DECLARE_LINK_MODE_MASK(mask) = { 0, };
+
+       pr_debug("In %s port %d, state is %d (%s)", __func__, port, state->interface,
+                phy_modes(state->interface));
+
+       if (!phy_interface_mode_is_rgmii(state->interface) &&
+           state->interface != PHY_INTERFACE_MODE_NA &&
+           state->interface != PHY_INTERFACE_MODE_1000BASEX &&
+           state->interface != PHY_INTERFACE_MODE_MII &&
+           state->interface != PHY_INTERFACE_MODE_REVMII &&
+           state->interface != PHY_INTERFACE_MODE_GMII &&
+           state->interface != PHY_INTERFACE_MODE_QSGMII &&
+           state->interface != PHY_INTERFACE_MODE_XGMII &&
+           state->interface != PHY_INTERFACE_MODE_HSGMII &&
+           state->interface != PHY_INTERFACE_MODE_10GKR &&
+           state->interface != PHY_INTERFACE_MODE_USXGMII &&
+           state->interface != PHY_INTERFACE_MODE_INTERNAL &&
+           state->interface != PHY_INTERFACE_MODE_SGMII) {
+               bitmap_zero(supported, __ETHTOOL_LINK_MODE_MASK_NBITS);
+               dev_err(ds->dev,
+                       "Unsupported interface: %d for port %d\n",
+                       state->interface, port);
+               return;
+       }
+
+       /* Allow all the expected bits */
+       phylink_set(mask, Autoneg);
+       phylink_set_port_modes(mask);
+       phylink_set(mask, Pause);
+       phylink_set(mask, Asym_Pause);
+
+       /* With the exclusion of MII and Reverse MII, we support Gigabit,
+        * including Half duplex
+        */
+       if (state->interface != PHY_INTERFACE_MODE_MII &&
+           state->interface != PHY_INTERFACE_MODE_REVMII) {
+               phylink_set(mask, 1000baseT_Full);
+               phylink_set(mask, 1000baseT_Half);
+       }
+
+       /* On the RTL9300 family of SoCs, ports 26 to 27 may be SFP ports TODO: take out of .dts */
+       if (port >= 26 && port <= 27)
+               phylink_set(mask, 1000baseX_Full);
+       if (port >= 26 && port <= 27)
+               phylink_set(mask, 10000baseKR_Full);
+
+       phylink_set(mask, 10baseT_Half);
+       phylink_set(mask, 10baseT_Full);
+       phylink_set(mask, 100baseT_Half);
+       phylink_set(mask, 100baseT_Full);
+
+       bitmap_and(supported, supported, mask,
+                  __ETHTOOL_LINK_MODE_MASK_NBITS);
+       bitmap_and(state->advertising, state->advertising, mask,
+                  __ETHTOOL_LINK_MODE_MASK_NBITS);
+}
+
 static int rtl83xx_phylink_mac_link_state(struct dsa_switch *ds, int port,
                                          struct phylink_link_state *state)
 {
@@ -304,15 +365,8 @@ static int rtl83xx_phylink_mac_link_state(struct dsa_switch *ds, int port,
        if (port < 0 || port > priv->cpu_port)
                return -EINVAL;
 
-       /*
-        * On the RTL9300 for at least the RTL8226B PHY, the MAC-side link
-        * state needs to be read twice in order to read a correct result.
-        * This would not be necessary for ports connected e.g. to RTL8218D
-        * PHYs.
-        */
        state->link = 0;
        link = priv->r->get_port_reg_le(priv->r->mac_link_sts);
-       link = priv->r->get_port_reg_le(priv->r->mac_link_sts);
        if (link & BIT_ULL(port))
                state->link = 1;
        pr_debug("%s: link state port %d: %llx\n", __func__, port, link & BIT_ULL(port));
@@ -349,6 +403,70 @@ static int rtl83xx_phylink_mac_link_state(struct dsa_switch *ds, int port,
        return 1;
 }
 
+static int rtl93xx_phylink_mac_link_state(struct dsa_switch *ds, int port,
+                                         struct phylink_link_state *state)
+{
+       struct rtl838x_switch_priv *priv = ds->priv;
+       u64 speed;
+       u64 link;
+
+       if (port < 0 || port > priv->cpu_port)
+               return -EINVAL;
+
+       /*
+        * On the RTL9300 for at least the RTL8226B PHY, the MAC-side link
+        * state needs to be read twice in order to read a correct result.
+        * This would not be necessary for ports connected e.g. to RTL8218D
+        * PHYs.
+        */
+       state->link = 0;
+       link = priv->r->get_port_reg_le(priv->r->mac_link_sts);
+       link = priv->r->get_port_reg_le(priv->r->mac_link_sts);
+       if (link & BIT_ULL(port))
+               state->link = 1;
+       pr_debug("%s: link state port %d: %llx, media %08x\n", __func__, port,
+                link & BIT_ULL(port), sw_r32(RTL930X_MAC_LINK_MEDIA_STS));
+
+       state->duplex = 0;
+       if (priv->r->get_port_reg_le(priv->r->mac_link_dup_sts) & BIT_ULL(port))
+               state->duplex = 1;
+
+       speed = priv->r->get_port_reg_le(priv->r->mac_link_spd_sts(port));
+       speed >>= (port % 8) << 2;
+       switch (speed & 0xf) {
+       case 0:
+               state->speed = SPEED_10;
+               break;
+       case 1:
+               state->speed = SPEED_100;
+               break;
+       case 2:
+       case 7:
+               state->speed = SPEED_1000;
+               break;
+       case 4:
+               state->speed = SPEED_10000;
+               break;
+       case 5:
+       case 8:
+               state->speed = SPEED_2500;
+               break;
+       case 6:
+               state->speed = SPEED_5000;
+               break;
+       default:
+               pr_err("%s: unknown speed: %d\n", __func__, (u32)speed & 0xf);
+       }
+
+       pr_debug("%s: speed is: %d %d\n", __func__, (u32)speed & 0xf, state->speed);
+       state->pause &= (MLO_PAUSE_RX | MLO_PAUSE_TX);
+       if (priv->r->get_port_reg_le(priv->r->mac_rx_pause_sts) & BIT_ULL(port))
+               state->pause |= MLO_PAUSE_RX;
+       if (priv->r->get_port_reg_le(priv->r->mac_tx_pause_sts) & BIT_ULL(port))
+               state->pause |= MLO_PAUSE_TX;
+       return 1;
+}
+
 static void rtl83xx_config_interface(int port, phy_interface_t interface)
 {
        u32 old, int_shift, sds_shift;
@@ -396,10 +514,6 @@ static void rtl83xx_phylink_mac_config(struct dsa_switch *ds, int port,
 
        pr_debug("%s port %d, mode %x\n", __func__, port, mode);
 
-       // BUG: Make this work on RTL93XX
-       if (priv->family_id >= RTL9300_FAMILY_ID)
-               return;
-
        if (port == priv->cpu_port) {
                /* Set Speed, duplex, flow control
                 * FORCE_EN | LINK_EN | NWAY_EN | DUP_SEL
@@ -468,6 +582,81 @@ static void rtl83xx_phylink_mac_config(struct dsa_switch *ds, int port,
        sw_w32(reg, priv->r->mac_force_mode_ctrl(port));
 }
 
+static void rtl93xx_phylink_mac_config(struct dsa_switch *ds, int port,
+                                       unsigned int mode,
+                                       const struct phylink_link_state *state)
+{
+       struct rtl838x_switch_priv *priv = ds->priv;
+       int sds_num, sds_mode;
+       u32 reg;
+
+       pr_debug("%s port %d, mode %x, phy-mode: %s, speed %d, link %d\n", __func__,
+               port, mode, phy_modes(state->interface), state->speed, state->link);
+
+       // Nothing to be done for the CPU-port
+       if (port == priv->cpu_port)
+               return;
+
+       reg = sw_r32(priv->r->mac_force_mode_ctrl(port));
+       reg &= ~(0xf << 3);
+
+       // On the RTL930X, ports 24 to 27 are using an internal SerDes
+       if (port >=24 && port <= 27) {
+               sds_num = port - 18; // Port 24 mapped to SerDes 6, 25 to 7 ...
+               switch (state->interface) {
+               case PHY_INTERFACE_MODE_HSGMII:
+                       sds_mode = 0x12;
+                       break;
+               case PHY_INTERFACE_MODE_1000BASEX:
+                       sds_mode = 0x1b;  // 10G 1000X Auto
+                       break;
+               case PHY_INTERFACE_MODE_XGMII:
+                       sds_mode = 0x10;
+                       break;
+               case PHY_INTERFACE_MODE_10GKR:
+                       sds_mode = 0x1a;
+                       // We need to use media sel for fibre media:
+                       reg |= BIT(16);
+                       break;
+               case PHY_INTERFACE_MODE_USXGMII:
+                       sds_mode = 0x0d;
+                       break;
+               default:
+                       pr_err("%s: unknown serdes mode: %s\n",
+                              __func__, phy_modes(state->interface));
+                       return;
+               }
+               rtl9300_sds_rst(sds_num, sds_mode);
+       }
+
+       switch (state->speed) {
+       case SPEED_10000:
+               reg |= 4 << 3;
+               break;
+       case SPEED_5000:
+               reg |= 6 << 3;
+               break;
+       case SPEED_2500:
+               reg |= 5 << 3;
+               break;
+       case SPEED_1000:
+               reg |= 2 << 3;
+               break;
+       default:
+               reg |= 2 << 3;
+               break;
+       }
+
+       if (state->link)
+               reg |= FORCE_LINK_EN;
+
+       if (state->duplex == DUPLEX_FULL)
+                       reg |= BIT(2);
+
+       reg |= 1; // Force Link up
+       sw_w32(reg, priv->r->mac_force_mode_ctrl(port));
+}
+
 static void rtl83xx_phylink_mac_link_down(struct dsa_switch *ds, int port,
                                     unsigned int mode,
                                     phy_interface_t interface)
@@ -477,6 +666,18 @@ static void rtl83xx_phylink_mac_link_down(struct dsa_switch *ds, int port,
        sw_w32_mask(0x3, 0, priv->r->mac_port_ctrl(port));
 }
 
+static void rtl93xx_phylink_mac_link_down(struct dsa_switch *ds, int port,
+                                    unsigned int mode,
+                                    phy_interface_t interface)
+{
+       struct rtl838x_switch_priv *priv = ds->priv;
+       /* Stop TX/RX to port */
+       sw_w32_mask(0x3, 0, priv->r->mac_port_ctrl(port));
+
+       // No longer force link
+       sw_w32_mask(3, 0, priv->r->mac_force_mode_ctrl(port));
+}
+
 static void rtl83xx_phylink_mac_link_up(struct dsa_switch *ds, int port,
                                   unsigned int mode,
                                   phy_interface_t interface,
@@ -487,6 +688,21 @@ static void rtl83xx_phylink_mac_link_up(struct dsa_switch *ds, int port,
        struct rtl838x_switch_priv *priv = ds->priv;
        /* Restart TX/RX to port */
        sw_w32_mask(0, 0x3, priv->r->mac_port_ctrl(port));
+       // TODO: Set speed/duplex/pauses
+}
+
+static void rtl93xx_phylink_mac_link_up(struct dsa_switch *ds, int port,
+                                  unsigned int mode,
+                                  phy_interface_t interface,
+                                  struct phy_device *phydev,
+                                  int speed, int duplex,
+                                  bool tx_pause, bool rx_pause)
+{
+       struct rtl838x_switch_priv *priv = ds->priv;
+
+       /* Restart TX/RX to port */
+       sw_w32_mask(0, 0x3, priv->r->mac_port_ctrl(port));
+       // TODO: Set speed/duplex/pauses
 }
 
 static void rtl83xx_get_strings(struct dsa_switch *ds,
@@ -1183,7 +1399,10 @@ static int rtl83xx_port_fdb_dump(struct dsa_switch *ds, int port,
                if (!e.valid)
                        continue;
 
-               if (e.port == port) {
+               if (e.port == port || e.port == RTL930X_PORT_IGNORE) {
+                       u64 seed;
+                       u32 key;
+
                        fid = ((i >> 2) & 0x3ff) | (e.rvid & ~0x3ff);
                        mac = ether_addr_to_u64(&e.mac[0]);
                        pkey = priv->r->l2_hash_key(priv, priv->r->l2_hash_seed(mac, fid));
@@ -1191,13 +1410,14 @@ static int rtl83xx_port_fdb_dump(struct dsa_switch *ds, int port,
                        pr_info("-> index %d, key %x, bucket %d, dmac %016llx, fid: %x rvid: %x\n",
                                i, i >> 2, i & 0x3, mac, fid, e.rvid);
                        dump_l2_entry(&e);
-                       u64 seed = priv->r->l2_hash_seed(mac, e.rvid);
-                       u32 key = priv->r->l2_hash_key(priv, seed);
+                       seed = priv->r->l2_hash_seed(mac, e.rvid);
+                       key = priv->r->l2_hash_key(priv, seed);
                        pr_info("seed: %016llx, key based on rvid: %08x\n", seed, key);
                        cb(e.mac, e.vid, e.is_static, data);
                }
                if (e.type == L2_MULTICAST) {
                        u64 portmask = priv->r->read_mcast_pmask(e.mc_portmask_index);
+
                        if (portmask & BIT_ULL(port)) {
                                dump_l2_entry(&e);
                                pr_info("  PM: %016llx\n", portmask);
@@ -1558,11 +1778,11 @@ const struct dsa_switch_ops rtl930x_switch_ops = {
        .phy_read               = dsa_phy_read,
        .phy_write              = dsa_phy_write,
 
-       .phylink_validate       = rtl83xx_phylink_validate,
-       .phylink_mac_link_state = rtl83xx_phylink_mac_link_state,
-       .phylink_mac_config     = rtl83xx_phylink_mac_config,
-       .phylink_mac_link_down  = rtl83xx_phylink_mac_link_down,
-       .phylink_mac_link_up    = rtl83xx_phylink_mac_link_up,
+       .phylink_validate       = rtl93xx_phylink_validate,
+       .phylink_mac_link_state = rtl93xx_phylink_mac_link_state,
+       .phylink_mac_config     = rtl93xx_phylink_mac_config,
+       .phylink_mac_link_down  = rtl93xx_phylink_mac_link_down,
+       .phylink_mac_link_up    = rtl93xx_phylink_mac_link_up,
 
        .get_strings            = rtl83xx_get_strings,
        .get_ethtool_stats      = rtl83xx_get_ethtool_stats,
index a3e169f2887a041b701d9f44384b784c6fa96ecb..e695879da1cad5877d9faa705d23310f42f28784 100644 (file)
 #define RTL839X_MAC_RX_PAUSE_STS               (0x03c0)
 #define RTL930X_MAC_RX_PAUSE_STS               (0xCB30)
 #define RTL931X_MAC_RX_PAUSE_STS               (0x0F00)
+#define RTL930X_MAC_LINK_MEDIA_STS             (0xCB14)
 
 /* MAC link state bits */
 #define FORCE_EN                               (1 << 0)
diff --git a/target/linux/realtek/patches-5.10/704-include-linux-add-phy-hsgmii-mode.patch b/target/linux/realtek/patches-5.10/704-include-linux-add-phy-hsgmii-mode.patch
new file mode 100644 (file)
index 0000000..9a61a70
--- /dev/null
@@ -0,0 +1,19 @@
+--- a/include/linux/phy.h
++++ b/include/linux/phy.h
+@@ -93,6 +93,7 @@
+       PHY_INTERFACE_MODE_SMII,
+       PHY_INTERFACE_MODE_XGMII,
+       PHY_INTERFACE_MODE_MOCA,
++      PHY_INTERFACE_MODE_HSGMII,
+       PHY_INTERFACE_MODE_QSGMII,
+       PHY_INTERFACE_MODE_TRGMII,
+       PHY_INTERFACE_MODE_1000BASEX,
+@@ -163,6 +164,8 @@
+               return "xgmii";
+       case PHY_INTERFACE_MODE_MOCA:
+               return "moca";
++      case PHY_INTERFACE_MODE_HSGMII:
++              return "hsgmii";
+       case PHY_INTERFACE_MODE_QSGMII:
+               return "qsgmii";
+       case PHY_INTERFACE_MODE_TRGMII: