From 9d9bf16aa8d966834ac1280f96c37d22552c33d1 Mon Sep 17 00:00:00 2001 From: Birger Koblitz Date: Wed, 8 Sep 2021 16:13:18 +0200 Subject: [PATCH] realtek: Add phylink configuration routines for RTL93xx 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 --- .../files-5.10/drivers/net/dsa/rtl83xx/dsa.c | 260 ++++++++++++++++-- .../drivers/net/dsa/rtl83xx/rtl838x.h | 1 + ...04-include-linux-add-phy-hsgmii-mode.patch | 19 ++ 3 files changed, 260 insertions(+), 20 deletions(-) create mode 100644 target/linux/realtek/patches-5.10/704-include-linux-add-phy-hsgmii-mode.patch diff --git a/target/linux/realtek/files-5.10/drivers/net/dsa/rtl83xx/dsa.c b/target/linux/realtek/files-5.10/drivers/net/dsa/rtl83xx/dsa.c index c79b4b2c8117..3a0806e70a1b 100644 --- a/target/linux/realtek/files-5.10/drivers/net/dsa/rtl83xx/dsa.c +++ b/target/linux/realtek/files-5.10/drivers/net/dsa/rtl83xx/dsa.c @@ -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, diff --git a/target/linux/realtek/files-5.10/drivers/net/dsa/rtl83xx/rtl838x.h b/target/linux/realtek/files-5.10/drivers/net/dsa/rtl83xx/rtl838x.h index a3e169f2887a..e695879da1ca 100644 --- a/target/linux/realtek/files-5.10/drivers/net/dsa/rtl83xx/rtl838x.h +++ b/target/linux/realtek/files-5.10/drivers/net/dsa/rtl83xx/rtl838x.h @@ -145,6 +145,7 @@ #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 index 000000000000..9a61a70c44d1 --- /dev/null +++ b/target/linux/realtek/patches-5.10/704-include-linux-add-phy-hsgmii-mode.patch @@ -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: -- 2.30.2