]> git.ipfire.org Git - thirdparty/u-boot.git/commitdiff
net: fsl_enetc: Add i.MX94 support to NETC block control driver
authorYe Li <ye.li@nxp.com>
Tue, 28 Oct 2025 02:46:29 +0000 (10:46 +0800)
committerFabio Estevam <festevam@nabladev.com>
Tue, 4 Nov 2025 15:39:46 +0000 (12:39 -0300)
Extend the NETC block control driver to support the i.MX94 SoC.

Acked-by: Peng Fan <peng.fan@nxp.com>
Reviewed-by: Jacky Bai <ping.bai@nxp.com>
Signed-off-by: Ye Li <ye.li@nxp.com>
Signed-off-by: Alice Guo <alice.guo@nxp.com>
drivers/net/Kconfig
drivers/net/fsl_enetc_netc_blk_ctrl.c

index c9b35b5a2ffc2fe6b78be034625a125f60a777db..913670ce412a059485172e7129d51cb72ea96c28 100644 (file)
@@ -1006,8 +1006,9 @@ config FSL_ENETC
 
 config FSL_ENETC_NETC_BLK_CTRL
        bool "NXP ENETC NETC blocks control driver"
-       depends on FSL_ENETC && IMX95
-       default y if IMX95
+       depends on FSL_ENETC
+       depends on IMX95 || IMX94
+       default y if IMX95 || IMX94
        help
          This driver configures Integrated Endpoint Register Block (IERB) and
          Privileged Register Block (PRB) of NETC. For i.MX platforms, it also
index fecd66eb15a8c1cf315fe866cf7821f14d3b7cf2..8577bb7563260256af26cc6bce4aafde024bda19 100644 (file)
 #define PCS_PROT_SFI                   BIT(4)
 #define PCS_PROT_10G_SXGMII            BIT(6)
 
+#define IMX94_MISC_SOC_CONTROL         0x0
+#define SEL_XPCS_1                     BIT(1)
+#define IMX94_XPCS_PORT_0              0x0
+#define IMX94_XPCS_PORT_1              0x1
+
+#define IMX94_EXT_PIN_CONTROL          0x10
+#define MAC2_MAC3_SEL                  BIT(1)
+
+#define IMX94_NETC_LINK_CFG(a)         (0x4c + (a) * 4)
+#define NETC_LINK_CFG_MII_PROT         GENMASK(3, 0)
+#define NETC_LINK_CFG_IO_VAR           GENMASK(19, 16)
+
 /* NETC privileged register block register */
 #define PRB_NETCRR                     0x100
 #define  NETCRR_SR                     BIT(0)
@@ -55,6 +67,7 @@
 /* NETC integrated endpoint register block register */
 #define IERB_EMDIOFAUXR                        0x344
 #define IERB_T0FAUXR                   0x444
+#define IERB_ETBCR(a)                  (0x300c + 0x100 * (a))
 #define IERB_EFAUXR(a)                 (0x3044 + 0x100 * (a))
 #define IERB_VFAUXR(a)                 (0x4004 + 0x40 * (a))
 #define FAUXR_LDID                     GENMASK(3, 0)
 #define IMX95_ENETC1_BUS_DEVFN         0x40
 #define IMX95_ENETC2_BUS_DEVFN         0x80
 
+#define IMX94_ENETC3_BUS_DEVFN         0x0
+#define IMX94_TIMER0_BUS_DEVFN         0x1
+#define IMX94_SWITCH_BUS_DEVFN         0x2
+#define IMX94_ENETC0_BUS_DEVFN         0x100
+#define IMX94_TIMER1_BUS_DEVFN         0x101
+#define IMX94_ENETC1_BUS_DEVFN         0x140
+#define IMX94_ENETC2_BUS_DEVFN         0x180
+#define IMX94_TIMER2_BUS_DEVFN         0x181
+#define IMX94_ENETC0_LINK              3
+#define IMX94_ENETC1_LINK              4
+#define IMX94_ENETC2_LINK              5
+#define IMX94_ENETC0_OFFSET            0
+#define IMX94_ENETC1_OFFSET            1
+#define IMX94_ENETC2_OFFSET            2
+#define IMX94_SWITCH_PORT2             2
+#define IMX94_SWITCH_CPU_PORT          3
+#define IMX94_TIMER0_ID                        0
+#define IMX94_TIMER1_ID                        1
+#define IMX94_TIMER2_ID                        2
+
 /* Flags for different platforms */
 #define NETC_HAS_NETCMIX               BIT(0)
 
@@ -73,6 +106,15 @@ struct netc_blk_ctrl {
        void __iomem *netcmix;
 };
 
+struct netc_devinfo {
+       int (*netcmix_init)(struct udevice *dev);
+       int (*ierb_init)(struct udevice *dev);
+       void (*xpcs_port_init)(struct netc_blk_ctrl *priv, int port);
+};
+
+static struct netc_blk_ctrl *netc_bc;
+static struct netc_devinfo *netc_di;
+
 static void netc_reg_write(void __iomem *base, u32 offset, u32 val)
 {
        writel(val, base + offset);
@@ -183,6 +225,142 @@ static int imx95_netcmix_init(struct udevice *dev)
        return 0;
 }
 
+static int imx94_enetc_get_link_num(ofnode np)
+{
+       int bus_devfn;
+
+       bus_devfn = netc_of_pci_get_bus_devfn(np);
+       if (bus_devfn < 0)
+               return -EINVAL;
+
+       /* Parse ENETC link number */
+       switch (bus_devfn) {
+       case IMX94_ENETC0_BUS_DEVFN:
+               return IMX94_ENETC0_LINK;
+       case IMX94_ENETC1_BUS_DEVFN:
+               return IMX94_ENETC1_LINK;
+       case IMX94_ENETC2_BUS_DEVFN:
+               return IMX94_ENETC2_LINK;
+       default:
+               return -EINVAL;
+       }
+}
+
+static int imx94_link_config(struct netc_blk_ctrl *priv,
+                            ofnode np, int link_id)
+{
+       phy_interface_t interface;
+       int mii_proto;
+       u32 val;
+
+       interface = ofnode_read_phy_mode(np);
+       if (interface == -1)
+               return -EINVAL;
+
+       mii_proto = netc_get_link_mii_protocol(interface);
+       if (mii_proto < 0)
+               return -EINVAL;
+
+       val = mii_proto & NETC_LINK_CFG_MII_PROT;
+       if (mii_proto == MII_PROT_SERIAL)
+               val = u32_replace_bits(val, IO_VAR_16FF_16G_SERDES,
+                                      NETC_LINK_CFG_IO_VAR);
+
+       netc_reg_write(priv->netcmix, IMX94_NETC_LINK_CFG(link_id), val);
+
+       if (link_id == IMX94_ENETC0_LINK) {
+               val = netc_reg_read(priv->netcmix, IMX94_EXT_PIN_CONTROL);
+               val |= MAC2_MAC3_SEL;
+               netc_reg_write(priv->netcmix, IMX94_EXT_PIN_CONTROL, val);
+       }
+
+       return 0;
+}
+
+static int imx94_enetc_link_config(struct netc_blk_ctrl *priv,
+                                  ofnode np, bool *enetc0_en)
+{
+       int link_id;
+
+       link_id = imx94_enetc_get_link_num(np);
+       if (link_id < 0)
+               return -EINVAL;
+
+       if (link_id == IMX94_ENETC0_LINK)
+               *enetc0_en = true;
+
+       return imx94_link_config(priv, np, link_id);
+}
+
+static int imx94_switch_link_config(struct netc_blk_ctrl *priv,
+                                   ofnode np, bool *swp2_en)
+{
+       ofnode ports, child;
+       int port_id, err = 0;
+
+       ports = ofnode_find_subnode(np, "ports");
+       if (!ofnode_valid(ports))
+               ports = ofnode_find_subnode(np, "ethernet-ports");
+       if (!ofnode_valid(ports))
+               return -ENODEV;
+
+       ofnode_for_each_subnode(child, ports) {
+               if (ofnode_read_u32(child, "reg", &port_id) < 0) {
+                       err = -ENODEV;
+                       goto end;
+               }
+
+               if (port_id == IMX94_SWITCH_CPU_PORT)
+                       continue;
+
+               if (port_id == IMX94_SWITCH_PORT2)
+                       *swp2_en = true;
+
+               err = imx94_link_config(priv, child, port_id);
+               if (err)
+                       goto end;
+       }
+
+end:
+       return err;
+}
+
+static int imx94_netcmix_init(struct udevice *dev)
+{
+       struct netc_blk_ctrl *priv = dev_get_priv(dev);
+       ofnode child, gchild;
+       bool enetc0_en = false, swp2_en = false;
+       int err;
+
+       dev_for_each_subnode(child, dev) {
+               if (!ofnode_is_enabled(child))
+                       continue;
+
+               ofnode_for_each_subnode(gchild, child) {
+                       if (!ofnode_is_enabled(gchild))
+                               continue;
+
+                       if (ofnode_device_is_compatible(gchild, "pci1131,e101")) {
+                               err = imx94_enetc_link_config(priv, gchild, &enetc0_en);
+                               if (err)
+                                       return err;
+                       } else if (ofnode_device_is_compatible(gchild, "pci1131,eef2")) {
+                               err = imx94_switch_link_config(priv, gchild, &swp2_en);
+                               if (err)
+                                       return err;
+                       }
+               }
+       }
+
+       if (enetc0_en && swp2_en) {
+               dev_err(dev, "Cannot enable swp2 and enetc0 at the same time\n");
+
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
 static bool netc_ierb_is_locked(struct netc_blk_ctrl *priv)
 {
        return !!(netc_reg_read(priv->prb, PRB_NETCRR) & NETCRR_LOCK);
@@ -238,9 +416,99 @@ static int imx95_ierb_init(struct udevice *dev)
        return 0;
 }
 
+static int imx94_enetc_get_enetc_offset(ofnode np)
+{
+       int bus_devfn;
+
+       bus_devfn = netc_of_pci_get_bus_devfn(np);
+       if (bus_devfn < 0)
+               return -EINVAL;
+
+       /* Parse ENETC offset */
+       switch (bus_devfn) {
+       case IMX94_ENETC0_BUS_DEVFN:
+               return IMX94_ENETC0_OFFSET;
+       case IMX94_ENETC1_BUS_DEVFN:
+               return IMX94_ENETC1_OFFSET;
+       case IMX94_ENETC2_BUS_DEVFN:
+               return IMX94_ENETC2_OFFSET;
+       default:
+               return -EINVAL;
+       }
+}
+
+static int imx94_enetc_get_timer_id(ofnode np)
+{
+       int bus_devfn;
+
+       bus_devfn = netc_of_pci_get_bus_devfn(np);
+       if (bus_devfn < 0)
+               return -EINVAL;
+
+       /* Parse ENETC PTP timer ID */
+       switch (bus_devfn) {
+       case IMX94_TIMER0_BUS_DEVFN:
+               return IMX94_TIMER0_ID;
+       case IMX94_TIMER1_BUS_DEVFN:
+               return IMX94_TIMER1_ID;
+       case IMX94_TIMER2_BUS_DEVFN:
+               return IMX94_TIMER2_ID;
+       default:
+               return -EINVAL;
+       }
+}
+
+static int imx94_enetc_update_tid(struct netc_blk_ctrl *priv, ofnode pf_np)
+{
+       ofnode timer_np;
+       int offset, tid;
+
+       offset = imx94_enetc_get_enetc_offset(pf_np);
+       if (offset < 0) {
+               printf("Find unknown PF node.\n");
+               return offset;
+       }
+
+       timer_np = ofnode_parse_phandle(pf_np, "nxp,ptp-timer", 0);
+       if (!ofnode_valid(timer_np)) {
+               /*
+                * If nxp,ptp-timer is not set, the first timer of the bus
+                * where enetc is located will be used as the default timer.
+                */
+               tid = IMX94_TIMER1_ID;
+               goto update_reg;
+       }
+
+       tid = imx94_enetc_get_timer_id(timer_np);
+       if (tid < 0) {
+               printf("Incorrect bus/devfn of ptp-timer.\n");
+               return tid;
+       }
+
+update_reg:
+       netc_reg_write(priv->ierb, IERB_ETBCR(offset), tid);
+
+       return 0;
+}
+
+static int imx94_ierb_init(struct udevice *dev)
+{
+       struct netc_blk_ctrl *priv = dev_get_priv(dev);
+       ofnode bus_np, pf_np;
+       int ret = 0;
+
+       dev_for_each_subnode(bus_np, dev)
+               ofnode_for_each_subnode(pf_np, bus_np)
+                       if (ofnode_device_is_compatible(pf_np, "pci1131,e101"))
+                               ret = imx94_enetc_update_tid(priv, pf_np);
+
+       return ret;
+}
+
 static int netc_ierb_init(struct udevice *dev)
 {
        struct netc_blk_ctrl *priv = dev_get_priv(dev);
+       struct netc_devinfo *devinfo = (struct netc_devinfo *)dev_get_driver_data(dev);
        int err;
 
        if (netc_ierb_is_locked(priv)) {
@@ -251,9 +519,11 @@ static int netc_ierb_init(struct udevice *dev)
                }
        }
 
-       err = imx95_ierb_init(dev);
-       if (err)
-               return err;
+       if (devinfo->ierb_init) {
+               err = devinfo->ierb_init(dev);
+               if (err)
+                       return err;
+       }
 
        err = netc_lock_ierb(priv);
        if (err) {
@@ -264,6 +534,31 @@ static int netc_ierb_init(struct udevice *dev)
        return 0;
 }
 
+static void imx94_netc_xpcs_port_init(struct netc_blk_ctrl *priv, int port)
+{
+       u32 val;
+
+       val = netc_reg_read(priv->netcmix, IMX94_MISC_SOC_CONTROL);
+       if (port == IMX94_XPCS_PORT_1)
+               val |= SEL_XPCS_1;
+       else
+               val &= ~SEL_XPCS_1;
+       netc_reg_write(priv->netcmix, IMX94_MISC_SOC_CONTROL, val);
+}
+
+void netc_xpcs_port_init(int port)
+{
+       struct netc_blk_ctrl *priv = netc_bc;
+       struct netc_devinfo *devinfo;
+
+       if (!priv)
+               return;
+
+       devinfo = netc_di;
+       if (devinfo->xpcs_port_init)
+               devinfo->xpcs_port_init(priv, port);
+}
+
 static int netc_prb_check_error(struct netc_blk_ctrl *priv)
 {
        if (netc_reg_read(priv->prb, PRB_NETCSR) & NETCSR_ERROR)
@@ -272,14 +567,27 @@ static int netc_prb_check_error(struct netc_blk_ctrl *priv)
        return 0;
 }
 
+static const struct netc_devinfo imx95_devinfo = {
+       .netcmix_init = imx95_netcmix_init,
+       .ierb_init = imx95_ierb_init,
+};
+
+static const struct netc_devinfo imx94_devinfo = {
+       .netcmix_init = imx94_netcmix_init,
+       .ierb_init = imx94_ierb_init,
+       .xpcs_port_init = imx94_netc_xpcs_port_init,
+};
+
 static const struct udevice_id netc_blk_ctrl_match[] = {
-       { .compatible = "nxp,imx95-netc-blk-ctrl" },
+       { .compatible = "nxp,imx95-netc-blk-ctrl", .data = (ulong)&imx95_devinfo },
+       { .compatible = "nxp,imx94-netc-blk-ctrl", .data = (ulong)&imx94_devinfo },
        {},
 };
 
 static int netc_blk_ctrl_probe(struct udevice *dev)
 {
        struct netc_blk_ctrl *priv = dev_get_priv(dev);
+       struct netc_devinfo *devinfo = (struct netc_devinfo *)dev_get_driver_data(dev);
        struct clk *ipg_clk;
        fdt_addr_t regs;
        int err;
@@ -318,10 +626,12 @@ static int netc_blk_ctrl_probe(struct udevice *dev)
 
        priv->netcmix = (void __iomem *)regs;
 
-       err = imx95_netcmix_init(dev);
-       if (err) {
-               dev_err(dev, "Initializing NETCMIX failed\n");
-               return err;
+       if (devinfo->netcmix_init) {
+               err = devinfo->netcmix_init(dev);
+               if (err) {
+                       dev_err(dev, "Initializing NETCMIX failed\n");
+                       return err;
+               }
        }
 
        err = netc_ierb_init(dev);
@@ -333,6 +643,9 @@ static int netc_blk_ctrl_probe(struct udevice *dev)
        if (netc_prb_check_error(priv) < 0)
                dev_warn(dev, "The current IERB configuration is invalid\n");
 
+       netc_bc = priv;
+       netc_di = devinfo;
+
        return 0;
 }