arm64: imx8mp: Auto-detect PHY on i.MX8MP DHCOM
authorMarek Vasut <marex@denx.de>
Sat, 11 Feb 2023 22:10:50 +0000 (23:10 +0100)
committerStefano Babic <sbabic@denx.de>
Thu, 30 Mar 2023 06:40:27 +0000 (08:40 +0200)
The i.MX8MP DHCOM SoM may be populated with either KSZ9131RNXI RGMII PHY
or LAN8740Ai RMII PHY attached to EQoS MAC, and either external RGMII PHY
or LAN8740Ai RMII PHY attached to FEC MAC. The SoM configuration can be
detected for each MAC by reading RX_CTL pull resistor state early on boot.
Make use of this, detect the exact PHY configuration, and patch control DT
accordingly so that the ethernet is configured correctly in U-Boot.

Signed-off-by: Marek Vasut <marex@denx.de>
board/dhelectronics/dh_imx8mp/imx8mp_dhcom_pdk2.c

index 9d8e19d..c690a5a 100644 (file)
@@ -5,12 +5,16 @@
 
 #include <common.h>
 #include <asm/arch/clock.h>
+#include <asm/mach-imx/iomux-v3.h>
+#include <asm/arch/imx8mp_pins.h>
 #include <asm/arch/sys_proto.h>
 #include <asm/io.h>
 #include <dm.h>
+#include <dt-bindings/clock/imx8mp-clock.h>
 #include <env.h>
 #include <env_internal.h>
 #include <i2c_eeprom.h>
+#include <linux/bitfield.h>
 #include <malloc.h>
 #include <net.h>
 #include <miiphy.h>
@@ -142,3 +146,227 @@ enum env_location env_get_location(enum env_operation op, int prio)
 {
        return prio ? ENVL_UNKNOWN : ENVL_SPI_FLASH;
 }
+
+static const char *iomuxc_compat = "fsl,imx8mp-iomuxc";
+static const char *lan_compat = "ethernet-phy-id0007.c110";
+static const char *ksz_compat = "ethernet-phy-id0022.1642";
+
+static int dh_dt_patch_som_eqos(const void *fdt_blob)
+{
+       const void __iomem *mux = (void __iomem *)IOMUXC_BASE_ADDR +
+               FIELD_GET(MUX_CTRL_OFS_MASK, MX8MP_PAD_ENET_RX_CTL__GPIO1_IO24);
+       int mac_node, mdio_node, iomuxc_node, ksz_node, lan_node, subnode;
+       const char *mac_compat = "nxp,imx8mp-dwmac-eqos";
+       void *blob = (void *)fdt_blob;
+       const fdt32_t *clk_prop;
+       bool is_gigabit;
+       u32 handle;
+       u32 clk[6];
+
+       setbits_le32(mux, IOMUX_CONFIG_SION);
+       is_gigabit = !(readl(GPIO1_BASE_ADDR) & BIT(24));
+       clrbits_le32(mux, IOMUX_CONFIG_SION);
+
+       /* Adjust EQoS node for Gigabit KSZ9131RNXI or Fast LAN8740Ai PHY */
+       mac_node = fdt_node_offset_by_compatible(blob, -1, mac_compat);
+       if (mac_node < 0)
+               return 0;
+
+       mdio_node = fdt_first_subnode(blob, mac_node);
+       if (mdio_node < 0)
+               return 0;
+
+       /* KSZ9131RNXI */
+       ksz_node = fdt_node_offset_by_compatible(blob, mdio_node, ksz_compat);
+       if (ksz_node < 0)
+               return 0;
+
+       /* LAN8740Ai */
+       lan_node = fdt_node_offset_by_compatible(blob, mdio_node, lan_compat);
+       if (lan_node < 0)
+               return 0;
+
+       iomuxc_node = fdt_node_offset_by_compatible(blob, -1, iomuxc_compat);
+       if (iomuxc_node < 0)
+               return 0;
+
+       /*
+        * The code below adjusts the following DT properties:
+        * - assigned-clock-parents .. 125 MHz RGMII / 50 MHz RMII ref clock
+        * - assigned-clock-rates .... 125 MHz RGMII / 50 MHz RMII ref clock
+        * - phy-handle .............. KSZ9131RNXI RGMII / LAN8740Ai RMII
+        * - phy-mode ................ RGMII / RMII
+        * - pinctrl-0 ............... RGMII / RMII
+        * - PHY subnode status ...... "disabled"/"okay" per RGMII / RMII
+        */
+
+       /* Perform all inplace changes first, string changes last. */
+       clk_prop = fdt_getprop(blob, mac_node, "assigned-clock-parents", NULL);
+       if (!clk_prop)
+               return 0;
+       clk[0] = clk_prop[0];
+       clk[1] = cpu_to_fdt32(IMX8MP_SYS_PLL1_266M);
+       clk[2] = clk_prop[2];
+       clk[3] = cpu_to_fdt32(IMX8MP_SYS_PLL2_100M);
+       clk[4] = clk_prop[4];
+       clk[5] = is_gigabit ? cpu_to_fdt32(IMX8MP_SYS_PLL2_125M) :
+                             cpu_to_fdt32(IMX8MP_SYS_PLL2_50M);
+       fdt_setprop_inplace(blob, mac_node, "assigned-clock-parents",
+                           clk, 6 * sizeof(u32));
+
+       clk[0] = cpu_to_fdt32(0);
+       clk[1] = cpu_to_fdt32(100000000);
+       clk[2] = is_gigabit ? cpu_to_fdt32(125000000) :
+                             cpu_to_fdt32(50000000);
+       fdt_setprop_inplace(blob, mac_node, "assigned-clock-rates",
+                           clk, 3 * sizeof(u32));
+
+       handle = fdt_get_phandle(blob, is_gigabit ? ksz_node : lan_node);
+       fdt_setprop_inplace_u32(blob, mac_node, "phy-handle", handle);
+
+       fdt_for_each_subnode(subnode, blob, iomuxc_node) {
+               if (!strstr(fdt_get_name(blob, subnode, NULL),
+                           is_gigabit ? "eqos-rgmii" : "eqos-rmii"))
+                       continue;
+
+               handle = fdt_get_phandle(blob, subnode);
+               fdt_setprop_inplace_u32(blob, mac_node, "pinctrl-0", handle);
+               break;
+       }
+
+       fdt_setprop_string(blob, mac_node, "phy-mode",
+                          is_gigabit ? "rgmii-id" : "rmii");
+
+       mac_node = fdt_node_offset_by_compatible(blob, -1, mac_compat);
+       mdio_node = fdt_first_subnode(blob, mac_node);
+       ksz_node = fdt_node_offset_by_compatible(blob, mdio_node, ksz_compat);
+       fdt_setprop_string(blob, ksz_node, "status",
+                          is_gigabit ? "okay" : "disabled");
+
+       mac_node = fdt_node_offset_by_compatible(blob, -1, mac_compat);
+       mdio_node = fdt_first_subnode(blob, mac_node);
+       lan_node = fdt_node_offset_by_compatible(blob, mdio_node, lan_compat);
+       fdt_setprop_string(blob, lan_node, "status",
+                          is_gigabit ? "disabled" : "okay");
+
+       return 0;
+}
+
+static int dh_dt_patch_som_fec(const void *fdt_blob)
+{
+       const void __iomem *mux = (void __iomem *)IOMUXC_BASE_ADDR +
+               FIELD_GET(MUX_CTRL_OFS_MASK, MX8MP_PAD_SAI1_TXFS__GPIO4_IO10);
+       int mac_node, mdio_node, iomuxc_node, lan_node, phy_node, subnode;
+       const char *mac_compat = "fsl,imx8mp-fec";
+       void *blob = (void *)fdt_blob;
+       const fdt32_t *clk_prop;
+       bool is_gigabit;
+       u32 handle;
+       u32 clk[8];
+
+       setbits_le32(mux, IOMUX_CONFIG_SION);
+       is_gigabit = !(readl(GPIO4_BASE_ADDR) & BIT(10));
+       clrbits_le32(mux, IOMUX_CONFIG_SION);
+
+       /* Test for non-default SoM with 100/Full PHY attached to FEC */
+       if (is_gigabit)
+               return 0;
+
+       /* Adjust FEC node for Fast LAN8740Ai PHY */
+       mac_node = fdt_node_offset_by_compatible(blob, -1, mac_compat);
+       if (mac_node < 0)
+               return 0;
+
+       /* Optional PHY pointed to by phy-handle, possibly on carrier board */
+       phy_node = fdtdec_lookup_phandle(blob, mac_node, "phy-handle");
+       if (phy_node > 0) {
+               fdt_setprop_string(blob, phy_node, "status", "disabled");
+               mac_node = fdt_node_offset_by_compatible(blob, -1, mac_compat);
+       }
+
+       mdio_node = fdt_first_subnode(blob, mac_node);
+       if (mdio_node < 0)
+               return 0;
+
+       /* LAN8740Ai */
+       lan_node = fdt_node_offset_by_compatible(blob, mdio_node, lan_compat);
+       if (lan_node < 0)
+               return 0;
+
+       iomuxc_node = fdt_node_offset_by_compatible(blob, -1, iomuxc_compat);
+       if (iomuxc_node < 0)
+               return 0;
+
+       /*
+        * The code below adjusts the following DT properties:
+        * - assigned-clock-parents .. 50 MHz RMII ref clock
+        * - assigned-clock-rates .... 50 MHz RMII ref clock
+        * - phy-handle .............. LAN8740Ai RMII
+        * - phy-mode ................ RMII
+        * - pinctrl-0 ............... RMII
+        * - PHY subnode status ...... "okay" for RMII PHY
+        */
+
+       /* Perform all inplace changes first, string changes last. */
+       clk_prop = fdt_getprop(blob, mac_node, "assigned-clock-parents", NULL);
+       if (!clk_prop)
+               return 0;
+       clk[0] = clk_prop[0];
+       clk[1] = cpu_to_fdt32(IMX8MP_SYS_PLL1_266M);
+       clk[2] = clk_prop[2];
+       clk[3] = cpu_to_fdt32(IMX8MP_SYS_PLL2_100M);
+       clk[4] = clk_prop[4];
+       clk[5] = cpu_to_fdt32(IMX8MP_SYS_PLL2_50M);
+       clk[6] = clk_prop[6];
+       clk[7] = cpu_to_fdt32(IMX8MP_SYS_PLL2_50M);
+       fdt_setprop_inplace(blob, mac_node, "assigned-clock-parents",
+                           clk, 8 * sizeof(u32));
+
+       clk[0] = cpu_to_fdt32(0);
+       clk[1] = cpu_to_fdt32(100000000);
+       clk[2] = cpu_to_fdt32(50000000);
+       clk[3] = cpu_to_fdt32(0);
+       fdt_setprop_inplace(blob, mac_node, "assigned-clock-rates",
+                           clk, 4 * sizeof(u32));
+
+       handle = fdt_get_phandle(blob, lan_node);
+       fdt_setprop_inplace_u32(blob, mac_node, "phy-handle", handle);
+
+       fdt_for_each_subnode(subnode, blob, iomuxc_node) {
+               if (!strstr(fdt_get_name(blob, subnode, NULL), "fec-rmii"))
+                       continue;
+
+               handle = fdt_get_phandle(blob, subnode);
+               fdt_setprop_inplace_u32(blob, mac_node, "pinctrl-0", handle);
+               break;
+       }
+
+       fdt_setprop_string(blob, mac_node, "phy-mode", "rmii");
+       mac_node = fdt_node_offset_by_compatible(blob, -1, mac_compat);
+       mdio_node = fdt_first_subnode(blob, mac_node);
+       lan_node = fdt_node_offset_by_compatible(blob, mdio_node, lan_compat);
+       fdt_setprop_string(blob, lan_node, "status", "okay");
+
+       return 0;
+}
+
+static int dh_dt_patch_som(const void *fdt_blob)
+{
+       int ret;
+
+       /* Do nothing if not i.MX8MP DHCOM SoM */
+       ret = fdt_node_check_compatible(fdt_blob, 0, "dh,imx8mp-dhcom-som");
+       if (ret)
+               return 0;
+
+       ret = dh_dt_patch_som_eqos(fdt_blob);
+       if (ret)
+               return ret;
+
+       return dh_dt_patch_som_fec(fdt_blob);
+}
+
+int fdtdec_board_setup(const void *fdt_blob)
+{
+       return dh_dt_patch_som(fdt_blob);
+}