realtek: Add port to smi-bus address mapping
authorBirger Koblitz <git@birger-koblitz.de>
Mon, 6 Sep 2021 05:24:09 +0000 (07:24 +0200)
committerJohn Crispin <john@phrozen.org>
Sat, 9 Oct 2021 06:25:05 +0000 (08:25 +0200)
All RTL SoCs addresss PHYs via their port number, which is mapped to an
SMI address. Add support for configuring this mapping via the .dts on all
SoCs apart from the 839x, where the mapping to the 64 ports is fixed.

Signed-off-by: Birger Koblitz <git@birger-koblitz.de>
target/linux/realtek/files-5.10/drivers/net/ethernet/rtl838x_eth.c

index dfec2e55eee091fd72782c00eec86afdbdb294c7..0d9ced9b6ce4ce3e860aed96670bb46267a6dbd9 100644 (file)
@@ -47,6 +47,8 @@ extern struct rtl83xx_soc_info soc_info;
 #define RX_EN_93XX     0x10
 #define TX_DO          0x2
 #define WRAP           0x2
+#define MAX_PORTS      57
+#define MAX_SMI_BUSSES 4
 
 #define RING_BUFFER    1600
 
@@ -185,6 +187,10 @@ struct rtl838x_eth_priv {
        u32 lastEvent;
        u16 rxrings;
        u16 rxringlen;
+       u8 smi_bus[MAX_PORTS];
+       u8 smi_addr[MAX_PORTS];
+       bool smi_bus_isc45[MAX_SMI_BUSSES];
+       bool phy_is_internal[MAX_PORTS];
 };
 
 extern int rtl838x_phy_init(struct rtl838x_eth_priv *priv);
@@ -1727,52 +1733,56 @@ static int rtl930x_mdio_reset(struct mii_bus *bus)
 {
        int i;
        int pos;
+       struct rtl838x_eth_priv *priv = bus->priv;
+       u32 c45_mask = 0;
+       u32 poll_sel[2];
+       u32 poll_ctrl = 0;
 
-       pr_info("RTL930X_SMI_PORT0_15_POLLING_SEL %08x 16-27: %08x\n",
-               sw_r32(RTL930X_SMI_PORT0_15_POLLING_SEL),
-               sw_r32(RTL930X_SMI_PORT16_27_POLLING_SEL));
+       // Mapping of port to phy-addresses on an SMI bus
+       poll_sel[0] = poll_sel[1] = 0;
+       for (i = 0; i < 28; i++) {
+               pos = (i % 6) * 5;
+               sw_w32_mask(0x1f << pos, priv->smi_addr[i] << pos,
+                           RTL930X_SMI_PORT0_5_ADDR + (i / 6) * 4);
 
-       pr_info("%s: Enable SMI polling on SMI bus 0, SMI1, SMI2, disable on SMI3\n", __func__);
-       sw_w32_mask(BIT(20) | BIT(21) | BIT(22), BIT(23), RTL930X_SMI_GLB_CTRL);
+               pos = (i * 2) % 32;
+               poll_sel[i / 16] |= priv->smi_bus[i] << pos;
+               poll_ctrl |= BIT(20 + priv->smi_bus[i]);
+       }
 
-       pr_info("RTL9300 Powering on SerDes ports\n");
-       rtl9300_sds_power(24, 1);
-       rtl9300_sds_power(25, 1);
-       rtl9300_sds_power(26, 1);
-       rtl9300_sds_power(27, 1);
-       mdelay(200);
+       // Configure which SMI bus is behind which port number
+       sw_w32(poll_sel[0], RTL930X_SMI_PORT0_15_POLLING_SEL);
+       sw_w32(poll_sel[1], RTL930X_SMI_PORT16_27_POLLING_SEL);
 
-       // RTL930X_SMI_PORT0_15_POLLING_SEL 55550000 16-27: 00f9aaaa
-       // i.e SMI=0 for all ports
-       for (i = 0; i < 5; i++)
-               pr_info("port phy: %08x\n", sw_r32(RTL930X_SMI_PORT0_5_ADDR + i *4));
+       // Enable polling on the respective SMI busses
+       sw_w32_mask(0, poll_ctrl, RTL930X_SMI_GLB_CTRL);
 
-       // 1-to-1 mapping of port to phy-address
-       for (i = 0; i < 24; i++) {
-               pos = (i % 6) * 5;
-               sw_w32_mask(0x1f << pos, i << pos, RTL930X_SMI_PORT0_5_ADDR + (i / 6) * 4);
-       }
+       // Configure which SMI busses are polled in c45 based on a c45 PHY being on that bus
+       for (i = 0; i < 4; i++)
+               if (priv->smi_bus_isc45[i])
+                       c45_mask |= BIT(i + 16);
 
-       // ports 24 and 25 have PHY addresses 8 and 9, ports 26/27 PHY 26/27
-       sw_w32(8 | 9 << 5 | 26 << 10 | 27 << 15, RTL930X_SMI_PORT0_5_ADDR + 4 * 4);
+       pr_info("c45_mask: %08x\n", c45_mask);
+       sw_w32_mask(0, c45_mask, RTL930X_SMI_GLB_CTRL);
 
-       // Ports 24 and 25 live on SMI bus 1 and 2
-       sw_w32_mask(0x3 << 16, 0x1 << 16, RTL930X_SMI_PORT16_27_POLLING_SEL);
-       sw_w32_mask(0x3 << 18, 0x2 << 18, RTL930X_SMI_PORT16_27_POLLING_SEL);
-
-       // SMI bus 1 and 2 speak Clause 45 TODO: Configure from .dts
-       sw_w32_mask(0, BIT(17) | BIT(18), RTL930X_SMI_GLB_CTRL);
+       // Ports 24 to 27 are 2.5 or 10Gig, set this type (1) or (0) for internal SerDes
+       for (i = 24; i < 28; i++) {
+               pos = (i - 24) * 3 + 12;
+               if (priv->phy_is_internal[i])
+                       sw_w32_mask(0x7 << pos, 0 << pos, RTL930X_SMI_MAC_TYPE_CTRL);
+               else
+                       sw_w32_mask(0x7 << pos, 1 << pos, RTL930X_SMI_MAC_TYPE_CTRL);
+       }
 
-       // Ports 24 and 25 are 2.5 Gig, set this type (1)
-       sw_w32_mask(0x7 << 12, 1 << 12, RTL930X_SMI_MAC_TYPE_CTRL);
-       sw_w32_mask(0x7 << 15, 1 << 15, RTL930X_SMI_MAC_TYPE_CTRL);
+       // TODO: Set up RTL9300_SMI_10GPHY_POLLING_SEL_0 for Aquantia PHYs on e.g. XGS 1250
 
        return 0;
 }
 
 static int rtl838x_mdio_init(struct rtl838x_eth_priv *priv)
 {
-       struct device_node *mii_np;
+       struct device_node *mii_np, *dn;
+       u32 pn;
        int ret;
 
        pr_debug("%s called\n", __func__);
@@ -1825,6 +1835,33 @@ static int rtl838x_mdio_init(struct rtl838x_eth_priv *priv)
        priv->mii_bus->priv = priv;
        priv->mii_bus->parent = &priv->pdev->dev;
 
+       for_each_node_by_name(dn, "ethernet-phy") {
+               u32 smi_addr[2];
+
+               if (of_property_read_u32(dn, "reg", &pn))
+                       continue;
+
+               if (of_property_read_u32_array(dn, "rtl9300,smi-address", &smi_addr[0], 2)) {
+                       smi_addr[0] = 0;
+                       smi_addr[1] = pn;
+               }
+
+               if (pn < MAX_PORTS) {
+                       priv->smi_bus[pn] = smi_addr[0];
+                       priv->smi_addr[pn] = smi_addr[1];
+               } else {
+                       pr_err("%s: illegal port number %d\n", __func__, pn);
+               }
+
+               if (of_device_is_compatible(dn, "ethernet-phy-ieee802.3-c45"))
+                       priv->smi_bus_isc45[smi_addr[0]] = true;
+
+               if (of_property_read_bool(dn, "phy-is-integrated")) {
+                       priv->phy_is_internal[pn] = true;
+               }
+
+       }
+
        snprintf(priv->mii_bus->id, MII_BUS_ID_SIZE, "%pOFn", mii_np);
        ret = of_mdiobus_register(priv->mii_bus, mii_np);