summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--arch/arm/dts/imx8mm-u-boot.dtsi6
-rw-r--r--arch/arm/dts/imx8mn-u-boot.dtsi6
-rw-r--r--arch/arm/dts/imx8mp-u-boot.dtsi6
-rw-r--r--arch/arm/dts/imx8mq-u-boot.dtsi6
-rw-r--r--board/phytec/phycore_imx8mp/spl.c2
-rw-r--r--configs/phycore-imx8mp_defconfig2
-rw-r--r--drivers/net/Kconfig14
-rw-r--r--drivers/net/Makefile1
-rw-r--r--drivers/net/fsl_enetc.c487
-rw-r--r--drivers/net/fsl_enetc.h88
-rw-r--r--drivers/net/fsl_enetc_mdio.c20
-rw-r--r--drivers/net/fsl_enetc_netc_blk_ctrl.c346
-rw-r--r--drivers/pinctrl/nxp/pinctrl-imx.c4
13 files changed, 876 insertions, 112 deletions
diff --git a/arch/arm/dts/imx8mm-u-boot.dtsi b/arch/arm/dts/imx8mm-u-boot.dtsi
index ecc2319279e..8608fa004fd 100644
--- a/arch/arm/dts/imx8mm-u-boot.dtsi
+++ b/arch/arm/dts/imx8mm-u-boot.dtsi
@@ -164,6 +164,7 @@
};
#endif
+#ifdef CONFIG_OPTEE
tee: tee {
description = "OP-TEE";
type = "tee";
@@ -178,6 +179,7 @@
optional;
};
};
+#endif
binman_fip: fip {
arch = "arm64";
@@ -207,7 +209,11 @@
fdt = "fdt-SEQ";
firmware = "uboot";
#ifndef CONFIG_ARMV8_PSCI
+#ifdef CONFIG_OPTEE
loadables = "atf", "tee";
+#else
+ loadables = "atf";
+#endif
#endif
};
};
diff --git a/arch/arm/dts/imx8mn-u-boot.dtsi b/arch/arm/dts/imx8mn-u-boot.dtsi
index 4a4498b36b0..bf2bb0f17c7 100644
--- a/arch/arm/dts/imx8mn-u-boot.dtsi
+++ b/arch/arm/dts/imx8mn-u-boot.dtsi
@@ -240,6 +240,7 @@
};
#endif
+#ifdef CONFIG_OPTEE
tee: tee {
description = "OP-TEE";
type = "tee";
@@ -254,6 +255,7 @@
optional;
};
};
+#endif
binman_fip: fip {
arch = "arm64";
@@ -283,7 +285,11 @@
fdt = "fdt-SEQ";
firmware = "uboot";
#ifndef CONFIG_ARMV8_PSCI
+#ifdef CONFIG_OPTEE
loadables = "atf", "tee";
+#else
+ loadables = "atf";
+#endif
#endif
};
};
diff --git a/arch/arm/dts/imx8mp-u-boot.dtsi b/arch/arm/dts/imx8mp-u-boot.dtsi
index 9ede98a11e4..9e590c3bba0 100644
--- a/arch/arm/dts/imx8mp-u-boot.dtsi
+++ b/arch/arm/dts/imx8mp-u-boot.dtsi
@@ -185,6 +185,7 @@
};
#endif
+#ifdef CONFIG_OPTEE
tee: tee {
description = "OP-TEE";
type = "tee";
@@ -199,6 +200,7 @@
optional;
};
};
+#endif
@fdt-SEQ {
description = "NAME";
@@ -219,7 +221,11 @@
fdt = "fdt-SEQ";
firmware = "uboot";
#ifndef CONFIG_ARMV8_PSCI
+#ifdef CONFIG_OPTEE
loadables = "atf", "tee";
+#else
+ loadables = "atf";
+#endif
#endif
};
};
diff --git a/arch/arm/dts/imx8mq-u-boot.dtsi b/arch/arm/dts/imx8mq-u-boot.dtsi
index 93e2ef27f7c..458657fc474 100644
--- a/arch/arm/dts/imx8mq-u-boot.dtsi
+++ b/arch/arm/dts/imx8mq-u-boot.dtsi
@@ -144,6 +144,7 @@
};
#endif
+#ifdef CONFIG_OPTEE
tee: tee {
description = "OP-TEE";
type = "tee";
@@ -158,6 +159,7 @@
optional;
};
};
+#endif
fdt {
compression = "none";
@@ -179,7 +181,11 @@
fdt = "fdt";
firmware = "uboot";
#ifndef CONFIG_ARMV8_PSCI
+#ifdef CONFIG_OPTEE
loadables = "atf", "tee";
+#else
+ loadables = "atf";
+#endif
#endif
};
};
diff --git a/board/phytec/phycore_imx8mp/spl.c b/board/phytec/phycore_imx8mp/spl.c
index 0610d8bbd0b..cb8e450b995 100644
--- a/board/phytec/phycore_imx8mp/spl.c
+++ b/board/phytec/phycore_imx8mp/spl.c
@@ -165,6 +165,8 @@ int power_init_board(void)
void spl_board_init(void)
{
+ arch_misc_init();
+
/* Set GIC clock to 500Mhz for OD VDD_SOC. */
clock_enable(CCGR_GIC, 0);
clock_set_target_val(GIC_CLK_ROOT, CLK_ROOT_ON | CLK_ROOT_SOURCE_SEL(5));
diff --git a/configs/phycore-imx8mp_defconfig b/configs/phycore-imx8mp_defconfig
index ba716a6875b..c0501907b2c 100644
--- a/configs/phycore-imx8mp_defconfig
+++ b/configs/phycore-imx8mp_defconfig
@@ -52,7 +52,6 @@ CONFIG_SPL_CUSTOM_SYS_MALLOC_ADDR=0x42200000
CONFIG_SPL_SYS_MALLOC_SIZE=0x80000
CONFIG_SPL_SYS_MMCSD_RAW_MODE=y
CONFIG_SYS_MMCSD_RAW_MODE_U_BOOT_SECTOR=0x300
-# CONFIG_SPL_CRYPTO is not set
CONFIG_SPL_I2C=y
CONFIG_SPL_NOR_SUPPORT=y
CONFIG_SPL_POWER=y
@@ -180,4 +179,3 @@ CONFIG_USB_GADGET_MANUFACTURER="PHYTEC"
CONFIG_USB_GADGET_VENDOR_NUM=0x0525
CONFIG_USB_GADGET_PRODUCT_NUM=0xa4a5
CONFIG_IMX_WATCHDOG=y
-# CONFIG_SPL_SHA_HW_ACCEL is not set
diff --git a/drivers/net/Kconfig b/drivers/net/Kconfig
index 88ff025a37b..dcf6b4c81fc 100644
--- a/drivers/net/Kconfig
+++ b/drivers/net/Kconfig
@@ -1019,6 +1019,20 @@ config FSL_ENETC
This driver supports the NXP ENETC Ethernet controller found on some
of the NXP SoCs.
+config FSL_ENETC_NETC_BLK_CTRL
+ bool "NXP ENETC NETC blocks control driver"
+ depends on FSL_ENETC && IMX95
+ default y if IMX95
+ help
+ This driver configures Integrated Endpoint Register Block (IERB) and
+ Privileged Register Block (PRB) of NETC. For i.MX platforms, it also
+ includes the configuration of NETCMIX block.
+ The IERB contains registers that are used for pre-boot initialization,
+ debug, and non-customer configuration. The PRB controls global reset
+ and global error handling for NETC. The NETCMIX block is mainly used
+ to set MII protocol and PCS protocol of the links, it also contains
+ settings for some other functions.
+
config MDIO_GPIO_BITBANG
bool "GPIO bitbanging MDIO driver"
depends on DM_MDIO && DM_GPIO
diff --git a/drivers/net/Makefile b/drivers/net/Makefile
index e51a917933e..c6217f08f14 100644
--- a/drivers/net/Makefile
+++ b/drivers/net/Makefile
@@ -44,6 +44,7 @@ obj-$(CONFIG_FEC_MXC) += fec_mxc.o
obj-$(CONFIG_FMAN_ENET) += fm/
obj-$(CONFIG_FMAN_ENET) += fsl_mdio.o
obj-$(CONFIG_FSL_ENETC) += fsl_enetc.o fsl_enetc_mdio.o
+obj-$(CONFIG_FSL_ENETC_NETC_BLK_CTRL) += fsl_enetc_netc_blk_ctrl.o
obj-$(CONFIG_FSL_LS_MDIO) += fsl_ls_mdio.o
obj-$(CONFIG_FSL_MC_ENET) += fsl-mc/
obj-$(CONFIG_FSL_MC_ENET) += ldpaa_eth/
diff --git a/drivers/net/fsl_enetc.c b/drivers/net/fsl_enetc.c
index a6b0bafc8c6..67ef5f34a8a 100644
--- a/drivers/net/fsl_enetc.c
+++ b/drivers/net/fsl_enetc.c
@@ -2,6 +2,7 @@
/*
* ENETC ethernet controller driver
* Copyright 2017-2021 NXP
+ * Copyright 2023-2025 NXP
*/
#include <dm.h>
@@ -16,52 +17,267 @@
#include <miiphy.h>
#include <linux/bug.h>
#include <linux/delay.h>
+#include <linux/build_bug.h>
+
+#ifdef CONFIG_ARCH_IMX9
+#include <asm/mach-imx/sys_proto.h>
+#include <cpu_func.h>
+#endif
#include "fsl_enetc.h"
#define ENETC_DRIVER_NAME "enetc_eth"
+/*
+ * Calculate number of buffer descriptors per cacheline, and compile-time
+ * validate that:
+ * - the RX and TX descriptors are the same size
+ * - the descriptors fit exactly into cachelines without overlap
+ * - all descriptors fit exactly into cachelines
+ */
+#define ENETC_NUM_BD_IN_CL \
+ ((ARCH_DMA_MINALIGN / sizeof(struct enetc_tx_bd)) + \
+ BUILD_BUG_ON_ZERO(sizeof(struct enetc_tx_bd) != \
+ sizeof(union enetc_rx_bd)) + \
+ BUILD_BUG_ON_ZERO(ARCH_DMA_MINALIGN % sizeof(struct enetc_tx_bd)) + \
+ BUILD_BUG_ON_ZERO(ARCH_DMA_MINALIGN % sizeof(union enetc_rx_bd)) + \
+ BUILD_BUG_ON_ZERO(ENETC_BD_CNT % \
+ (ARCH_DMA_MINALIGN / sizeof(struct enetc_tx_bd))))
+
static int enetc_remove(struct udevice *dev);
+static int enetc_is_imx95(struct udevice *dev)
+{
+ struct pci_child_plat *pplat = dev_get_parent_plat(dev);
+
+ /* Test whether this is i.MX95 ENETCv4. This may be optimized out. */
+ return IS_ENABLED(CONFIG_ARCH_IMX9) &&
+ pplat->vendor == PCI_VENDOR_ID_PHILIPS;
+}
+
+static int enetc_is_ls1028a(struct udevice *dev)
+{
+ struct pci_child_plat *pplat = dev_get_parent_plat(dev);
+
+ /* Test whether this is LS1028A ENETC. This may be optimized out. */
+ return IS_ENABLED(CONFIG_ARCH_LS1028A) &&
+ pplat->vendor == PCI_VENDOR_ID_FREESCALE;
+}
+
+static int enetc_dev_id(struct udevice *dev)
+{
+ if (enetc_is_imx95(dev))
+ return PCI_DEV(pci_get_devfn(dev)) >> 3;
+ if (enetc_is_ls1028a(dev))
+ return PCI_FUNC(pci_get_devfn(dev));
+
+ return 0;
+}
+
+static void enetc_inval_rxbd(struct udevice *dev)
+{
+ struct enetc_priv *priv = dev_get_priv(dev);
+ union enetc_rx_bd *desc = &priv->enetc_rxbd[priv->rx_bdr.next_prod_idx];
+ unsigned long start = rounddown((unsigned long)desc, ARCH_DMA_MINALIGN);
+ unsigned long end = roundup((unsigned long)desc + sizeof(*desc),
+ ARCH_DMA_MINALIGN);
+
+ if (enetc_is_imx95(dev))
+ invalidate_dcache_range(start, end);
+}
+
+static void enetc_flush_bd(struct udevice *dev, int pi, bool tx)
+{
+ struct enetc_priv *priv = dev_get_priv(dev);
+ union enetc_rx_bd *rxdesc = &priv->enetc_rxbd[pi];
+ struct enetc_tx_bd *txdesc = &priv->enetc_txbd[pi];
+ unsigned long desc = tx ? (unsigned long)txdesc : (unsigned long)rxdesc;
+ unsigned long size = tx ? sizeof(*txdesc) : sizeof(*rxdesc);
+ unsigned long start = rounddown(desc, ARCH_DMA_MINALIGN);
+ unsigned long end = roundup(desc + size, ARCH_DMA_MINALIGN);
+
+ if (enetc_is_imx95(dev))
+ flush_dcache_range(start, end);
+}
+
+static void enetc_inval_buffer(struct udevice *dev, void *buf, size_t size)
+{
+ unsigned long start = rounddown((unsigned long)buf, ARCH_DMA_MINALIGN);
+ unsigned long end = roundup((unsigned long)buf + size,
+ ARCH_DMA_MINALIGN);
+
+ if (enetc_is_imx95(dev))
+ invalidate_dcache_range(start, end);
+}
+
+static void enetc_flush_buffer(struct udevice *dev, void *buf, size_t size)
+{
+ unsigned long start = rounddown((unsigned long)buf, ARCH_DMA_MINALIGN);
+ unsigned long end = roundup((unsigned long)buf + size,
+ ARCH_DMA_MINALIGN);
+
+ if (enetc_is_imx95(dev))
+ flush_dcache_range(start, end);
+}
+
+/* register accessors */
+static u32 enetc_read_reg(void __iomem *addr)
+{
+ return readl(addr);
+}
+
+static void enetc_write_reg(void __iomem *addr, u32 val)
+{
+ writel(val, addr);
+}
+
+static void enetc_write(struct enetc_priv *priv, u32 off, u32 val)
+{
+ enetc_write_reg(priv->regs_base + off, val);
+}
+
+/* base port register accessors */
+static void enetc_write_pmr(struct udevice *dev, u32 val)
+{
+ struct enetc_data *data = (struct enetc_data *)dev_get_driver_data(dev);
+ struct enetc_priv *priv = dev_get_priv(dev);
+ const u32 off = ENETC_PMR + data->reg_offset_pmr;
+
+ enetc_write_reg(priv->port_regs + off, val);
+}
+
+static void enetc_write_psipmar(struct udevice *dev, int n, u32 val)
+{
+ struct enetc_data *data = (struct enetc_data *)dev_get_driver_data(dev);
+ struct enetc_priv *priv = dev_get_priv(dev);
+ const u32 off = (n ? ENETC_PSIPMAR1 : ENETC_PSIPMAR0) +
+ data->reg_offset_psipmar;
+
+ enetc_write_reg(priv->port_regs + off, val);
+}
+
+/* port station register accessors */
+static void enetc_write_psicfgr(struct udevice *dev, int port, u32 val)
+{
+ struct enetc_data *data = (struct enetc_data *)dev_get_driver_data(dev);
+ struct enetc_priv *priv = dev_get_priv(dev);
+ const u32 off = ENETC_PSICFGR(port, ENETC_PSICFGR_SHIFT_LS) +
+ data->reg_offset_psicfgr;
+
+ enetc_write_reg(priv->port_regs + off, val);
+}
+
+/* port register accessors */
+static u32 enetc_read_pcapr_mdio(struct udevice *dev)
+{
+ struct enetc_data *data = (struct enetc_data *)dev_get_driver_data(dev);
+ struct enetc_priv *priv = dev_get_priv(dev);
+ const u32 off = ENETC_PCAPR0 + data->reg_offset_pcapr;
+ const u32 reg = enetc_read_reg(priv->port_regs + off);
+
+ if (enetc_is_imx95(dev))
+ return reg & ENETC_PCS_PROT;
+ else if (enetc_is_ls1028a(dev))
+ return reg & ENETC_PCAPRO_MDIO;
+
+ return 0;
+}
+
+static void enetc_write_port(struct enetc_priv *priv, u32 off, u32 val)
+{
+ enetc_write_reg(priv->port_regs + off, val);
+}
+
+/* MAC port register accessors */
+static u32 enetc_read_mac_port(struct udevice *dev, u32 off)
+{
+ struct enetc_data *data = (struct enetc_data *)dev_get_driver_data(dev);
+ struct enetc_priv *priv = dev_get_priv(dev);
+
+ return enetc_read_reg(priv->port_regs + data->reg_offset_mac + off);
+}
+
+static void enetc_write_mac_port(struct udevice *dev, u32 off, u32 val)
+{
+ struct enetc_data *data = (struct enetc_data *)dev_get_driver_data(dev);
+ struct enetc_priv *priv = dev_get_priv(dev);
+
+ enetc_write_reg(priv->port_regs + data->reg_offset_mac + off, val);
+}
+
+/* BDR register accessor, see also ENETC_BDR() */
+static void enetc_bdr_write(struct enetc_priv *priv, int type, int n,
+ u32 off, u32 val)
+{
+ enetc_write(priv, ENETC_BDR(type, n, off), val);
+}
+
/*
* sets the MAC address in IERB registers, this setting is persistent and
* carried over to Linux.
*/
-static void enetc_set_ierb_primary_mac(struct udevice *dev, int devfn,
- const u8 *enetaddr)
-{
-#ifdef CONFIG_ARCH_LS1028A
-/*
- * LS1028A is the only part with IERB at this time and there are plans to change
- * its structure, keep this LS1028A specific for now
- */
#define IERB_BASE 0x1f0800000ULL
#define IERB_PFMAC(pf, vf, n) (IERB_BASE + 0x8000 + (pf) * 0x100 + (vf) * 8 \
+ (n) * 4)
-static int ierb_fn_to_pf[] = {0, 1, 2, -1, -1, -1, 3};
-
+static void enetc_set_ierb_primary_mac(struct udevice *dev, void *blob)
+{
+ static int ierb_fn_to_pf[] = { 0, 1, 2, -1, -1, -1, 3 };
+ struct pci_child_plat *ppdata = dev_get_parent_plat(dev);
+ struct eth_pdata *pdata = dev_get_plat(dev);
+ struct enetc_priv *priv = dev_get_priv(dev);
+ const u8 *enetaddr = pdata->enetaddr;
u16 lower = *(const u16 *)(enetaddr + 4);
u32 upper = *(const u32 *)enetaddr;
+ int devfn, offset;
+ char path[256];
+
+ if (enetc_is_imx95(dev)) {
+ /*
+ * Configure the ENETC primary MAC addresses - Set register
+ * PMAR0/1 for SI 0 and PSIaPMAR0/1 for SI 1, 2 .. a
+ * (optionally pre-configured in IERB).
+ */
+ devfn = enetc_dev_id(dev);
+ if (devfn > 2)
+ return;
+
+ enetc_write(priv, IMX95_ENETC_SIPMAR0, upper);
+ enetc_write(priv, IMX95_ENETC_SIPMAR1, lower);
+
+ snprintf(path, 256, "/soc/pcie@%x/ethernet@%x,%x",
+ PCI_BUS(dm_pci_get_bdf(dev)), PCI_DEV(ppdata->devfn),
+ PCI_FUNC(ppdata->devfn));
+ } else if (enetc_is_ls1028a(dev)) {
+ /*
+ * LS1028A is the only part with IERB at this time and
+ * there are plans to change its structure, keep this
+ * LS1028A specific for now.
+ */
+ devfn = PCI_FUNC(ppdata->devfn);
+
+ if (ierb_fn_to_pf[devfn] < 0)
+ return;
- if (ierb_fn_to_pf[devfn] < 0)
+ out_le32(IERB_PFMAC(ierb_fn_to_pf[devfn], 0, 0), upper);
+ out_le32(IERB_PFMAC(ierb_fn_to_pf[devfn], 0, 1), (u32)lower);
+
+ snprintf(path, 256, "/soc/pcie@1f0000000/ethernet@%x,%x",
+ PCI_DEV(ppdata->devfn), PCI_FUNC(ppdata->devfn));
+ } else {
return;
+ }
- out_le32(IERB_PFMAC(ierb_fn_to_pf[devfn], 0, 0), upper);
- out_le32(IERB_PFMAC(ierb_fn_to_pf[devfn], 0, 1), (u32)lower);
-#endif
+ offset = fdt_path_offset(blob, path);
+ if (offset >= 0)
+ fdt_setprop(blob, offset, "mac-address", pdata->enetaddr, 6);
}
/* sets up primary MAC addresses in DT/IERB */
void fdt_fixup_enetc_mac(void *blob)
{
- struct pci_child_plat *ppdata;
- struct eth_pdata *pdata;
struct udevice *dev;
struct uclass *uc;
- char path[256];
- int offset;
- int devfn;
uclass_get(UCLASS_ETH, &uc);
uclass_foreach_dev(dev, uc) {
@@ -69,18 +285,7 @@ void fdt_fixup_enetc_mac(void *blob)
strcmp(dev->driver->name, ENETC_DRIVER_NAME))
continue;
- pdata = dev_get_plat(dev);
- ppdata = dev_get_parent_plat(dev);
- devfn = PCI_FUNC(ppdata->devfn);
-
- enetc_set_ierb_primary_mac(dev, devfn, pdata->enetaddr);
-
- snprintf(path, 256, "/soc/pcie@1f0000000/ethernet@%x,%x",
- PCI_DEV(ppdata->devfn), PCI_FUNC(ppdata->devfn));
- offset = fdt_path_offset(blob, path);
- if (offset < 0)
- continue;
- fdt_setprop(blob, offset, "mac-address", pdata->enetaddr, 6);
+ enetc_set_ierb_primary_mac(dev, blob);
}
}
@@ -101,7 +306,7 @@ static int enetc_bind(struct udevice *dev)
* PCI function # and enetc#N based on interface count
*/
if (ofnode_valid(dev_ofnode(dev)))
- sprintf(name, "enetc-%u", PCI_FUNC(pci_get_devfn(dev)));
+ sprintf(name, "enetc-%u", enetc_dev_id(dev));
else
sprintf(name, "enetc#%u", eth_num_devices++);
device_set_name(dev, name);
@@ -181,10 +386,9 @@ static int enetc_init_sgmii(struct udevice *dev)
/* set up MAC for RGMII */
static void enetc_init_rgmii(struct udevice *dev, struct phy_device *phydev)
{
- struct enetc_priv *priv = dev_get_priv(dev);
- u32 old_val, val;
+ u32 old_val, val, dpx = 0;
- old_val = val = enetc_read_port(priv, ENETC_PM_IF_MODE);
+ old_val = val = enetc_read_mac_port(dev, ENETC_PM_IF_MODE);
/* disable unreliable RGMII in-band signaling and force the MAC into
* the speed negotiated by the PHY.
@@ -202,15 +406,20 @@ static void enetc_init_rgmii(struct udevice *dev, struct phy_device *phydev)
val |= ENETC_PM_IFM_SSP_10;
}
+ if (enetc_is_imx95(dev))
+ dpx = ENETC_PM_IFM_FULL_DPX_IMX;
+ else if (enetc_is_ls1028a(dev))
+ dpx = ENETC_PM_IFM_FULL_DPX_LS;
+
if (phydev->duplex == DUPLEX_FULL)
- val |= ENETC_PM_IFM_FULL_DPX;
+ val |= dpx;
else
- val &= ~ENETC_PM_IFM_FULL_DPX;
+ val &= ~dpx;
if (val == old_val)
return;
- enetc_write_port(priv, ENETC_PM_IF_MODE, val);
+ enetc_write_mac_port(dev, ENETC_PM_IF_MODE, val);
}
/* set up MAC configuration for the given interface type */
@@ -230,9 +439,12 @@ static void enetc_setup_mac_iface(struct udevice *dev,
case PHY_INTERFACE_MODE_USXGMII:
case PHY_INTERFACE_MODE_10GBASER:
/* set ifmode to (US)XGMII */
- if_mode = enetc_read_port(priv, ENETC_PM_IF_MODE);
- if_mode &= ~ENETC_PM_IF_IFMODE_MASK;
- enetc_write_port(priv, ENETC_PM_IF_MODE, if_mode);
+ if_mode = enetc_read_mac_port(dev, ENETC_PM_IF_MODE);
+ if (enetc_is_imx95(dev))
+ if_mode &= ~ENETC_PM_IF_IFMODE_MASK_IMX;
+ else if (enetc_is_ls1028a(dev))
+ if_mode &= ~ENETC_PM_IF_IFMODE_MASK_LS;
+ enetc_write_mac_port(dev, ENETC_PM_IF_MODE, if_mode);
break;
};
}
@@ -263,7 +475,7 @@ static void enetc_start_pcs(struct udevice *dev)
struct enetc_priv *priv = dev_get_priv(dev);
/* register internal MDIO for debug purposes */
- if (enetc_read_port(priv, ENETC_PCAPR0) & ENETC_PCAPRO_MDIO) {
+ if (enetc_read_pcapr_mdio(dev)) {
priv->imdio.read = enetc_mdio_read;
priv->imdio.write = enetc_mdio_write;
priv->imdio.priv = priv->port_regs + ENETC_PM_IMDIO_BASE;
@@ -375,6 +587,21 @@ static int enetc_remove(struct udevice *dev)
return 0;
}
+static int enetc_imx95_write_hwaddr(struct udevice *dev)
+{
+ struct eth_pdata *plat = dev_get_plat(dev);
+ struct enetc_priv *priv = dev_get_priv(dev);
+ u8 *addr = plat->enetaddr;
+
+ u16 lower = *(const u16 *)(addr + 4);
+ u32 upper = *(const u32 *)addr;
+
+ enetc_write_port(priv, IMX95_ENETC_PMAR0, upper);
+ enetc_write_port(priv, IMX95_ENETC_PMAR1, lower);
+
+ return 0;
+}
+
/*
* LS1028A is the only part with IERB at this time and there are plans to
* change its structure, keep this LS1028A specific for now.
@@ -413,39 +640,46 @@ static int enetc_ls1028a_write_hwaddr(struct udevice *dev)
static int enetc_write_hwaddr(struct udevice *dev)
{
struct eth_pdata *plat = dev_get_plat(dev);
- struct enetc_priv *priv = dev_get_priv(dev);
u8 *addr = plat->enetaddr;
- if (IS_ENABLED(CONFIG_ARCH_LS1028A))
+ if (enetc_is_imx95(dev))
+ return enetc_imx95_write_hwaddr(dev);
+ if (enetc_is_ls1028a(dev))
return enetc_ls1028a_write_hwaddr(dev);
u16 lower = *(const u16 *)(addr + 4);
u32 upper = *(const u32 *)addr;
- enetc_write_port(priv, ENETC_PSIPMAR0, upper);
- enetc_write_port(priv, ENETC_PSIPMAR1, lower);
+ enetc_write_psipmar(dev, 0, upper);
+ enetc_write_psipmar(dev, 1, lower);
return 0;
}
/* Configure port parameters (# of rings, frame size, enable port) */
-static void enetc_enable_si_port(struct enetc_priv *priv)
+static void enetc_enable_si_port(struct udevice *dev)
{
- u32 val;
+ struct enetc_priv *priv = dev_get_priv(dev);
+ u32 val = ENETC_PM_CC_TXP_IMX | ENETC_PM_CC_TX | ENETC_PM_CC_RX;
/* set Rx/Tx BDR count */
- val = ENETC_PSICFGR_SET_TXBDR(ENETC_TX_BDR_CNT);
- val |= ENETC_PSICFGR_SET_RXBDR(ENETC_RX_BDR_CNT);
- enetc_write_port(priv, ENETC_PSICFGR(0), val);
+ enetc_write_psicfgr(dev, 0, ENETC_PSICFGR_SET_BDR(ENETC_RX_BDR_CNT,
+ ENETC_TX_BDR_CNT));
/* set Rx max frame size */
- enetc_write_port(priv, ENETC_PM_MAXFRM, ENETC_RX_MAXFRM_SIZE);
+ enetc_write_mac_port(dev, ENETC_PM_MAXFRM, ENETC_RX_MAXFRM_SIZE);
/* enable MAC port */
- enetc_write_port(priv, ENETC_PM_CC, ENETC_PM_CC_RX_TX_EN);
+ if (enetc_is_ls1028a(dev))
+ val |= ENETC_PM_CC_TXP_LS | ENETC_PM_CC_PROMIS;
+ enetc_write_mac_port(dev, ENETC_PM_CC, val);
/* enable port */
- enetc_write_port(priv, ENETC_PMR, ENETC_PMR_SI0_EN);
+ if (enetc_is_imx95(dev))
+ enetc_write_port(priv, ENETC_POR, 0x0);
+ enetc_write_pmr(dev, ENETC_PMR_SI0_EN);
/* set SI cache policy */
- enetc_write(priv, ENETC_SICAR0,
- ENETC_SICAR_RD_CFG | ENETC_SICAR_WR_CFG);
+ enetc_write(priv, ENETC_SICAR0, ENETC_SICAR_WR_CFG |
+ (enetc_is_imx95(dev) ?
+ ENETC_SICAR_RD_CFG_IMX :
+ ENETC_SICAR_RD_CFG_LS));
/* enable SI */
enetc_write(priv, ENETC_SIMR, ENETC_SIMR_EN);
}
@@ -536,6 +770,8 @@ static void enetc_setup_rx_bdr(struct udevice *dev)
priv->enetc_rxbd[i].w.addr = enetc_rxb_address(dev, i);
/* each RX buffer must be aligned to 64B */
WARN_ON(priv->enetc_rxbd[i].w.addr & (ARCH_DMA_MINALIGN - 1));
+
+ enetc_flush_bd(dev, i, false);
}
/* reset producer (ENETC owned) and consumer (SW owned) index */
@@ -556,6 +792,7 @@ static void enetc_setup_rx_bdr(struct udevice *dev)
*/
static int enetc_start(struct udevice *dev)
{
+ int ret;
struct enetc_priv *priv = dev_get_priv(dev);
/* reset and enable the PCI device */
@@ -563,15 +800,19 @@ static int enetc_start(struct udevice *dev)
dm_pci_clrset_config16(dev, PCI_COMMAND, 0,
PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER);
- enetc_enable_si_port(priv);
+ enetc_enable_si_port(dev);
/* setup Tx/Rx buffer descriptors */
enetc_setup_tx_bdr(dev);
enetc_setup_rx_bdr(dev);
+ ret = phy_startup(priv->phy);
+ if (ret)
+ return ret;
+
enetc_setup_mac_iface(dev, priv->phy);
- return phy_startup(priv->phy);
+ return 0;
}
/*
@@ -614,6 +855,8 @@ static int enetc_send(struct udevice *dev, void *packet, int length)
enetc_dbg(dev, "TxBD[%d]send: pkt_len=%d, buff @0x%x%08x\n", pi, length,
upper_32_bits((u64)nv_packet), lower_32_bits((u64)nv_packet));
+ enetc_flush_buffer(dev, packet, length);
+
/* prepare Tx BD */
memset(&priv->enetc_txbd[pi], 0x0, sizeof(struct enetc_tx_bd));
priv->enetc_txbd[pi].addr =
@@ -621,7 +864,10 @@ static int enetc_send(struct udevice *dev, void *packet, int length)
priv->enetc_txbd[pi].buf_len = cpu_to_le16(length);
priv->enetc_txbd[pi].frm_len = cpu_to_le16(length);
priv->enetc_txbd[pi].flags = cpu_to_le16(ENETC_TXBD_FLAGS_F);
+
dmb();
+ enetc_flush_bd(dev, pi, true);
+
/* send frame: increment producer index */
pi = (pi + 1) % txr->bd_count;
txr->next_prod_idx = pi;
@@ -643,15 +889,15 @@ static int enetc_recv(struct udevice *dev, int flags, uchar **packetp)
{
struct enetc_priv *priv = dev_get_priv(dev);
struct bd_ring *rxr = &priv->rx_bdr;
- int tries = ENETC_POLL_TRIES;
int pi = rxr->next_prod_idx;
- int ci = rxr->next_cons_idx;
+ int tries = ENETC_POLL_TRIES;
u32 status;
int len;
u8 rdy;
do {
dmb();
+ enetc_inval_rxbd(dev);
status = le32_to_cpu(priv->enetc_rxbd[pi].r.lstatus);
/* check if current BD is ready to be consumed */
rdy = ENETC_RXBD_STATUS_R(status);
@@ -663,45 +909,142 @@ static int enetc_recv(struct udevice *dev, int flags, uchar **packetp)
dmb();
len = le16_to_cpu(priv->enetc_rxbd[pi].r.buf_len);
*packetp = (uchar *)enetc_rxb_address(dev, pi);
+ enetc_inval_buffer(dev, *packetp, len);
enetc_dbg(dev, "RxBD[%d]: len=%d err=%d pkt=0x%x%08x\n", pi, len,
ENETC_RXBD_STATUS_ERRORS(status),
upper_32_bits((u64)*packetp), lower_32_bits((u64)*packetp));
- /* BD clean up and advance to next in ring */
- memset(&priv->enetc_rxbd[pi], 0, sizeof(union enetc_rx_bd));
- priv->enetc_rxbd[pi].w.addr = enetc_rxb_address(dev, pi);
+ return len;
+}
+
+static int enetc_free_pkt(struct udevice *dev, uchar *packet, int length)
+{
+ const int bd_num_in_cl = enetc_is_imx95(dev) ? ENETC_NUM_BD_IN_CL : 1;
+ struct enetc_priv *priv = dev_get_priv(dev);
+ struct bd_ring *rxr = &priv->rx_bdr;
+ int pi = rxr->next_prod_idx;
+ int ci = rxr->next_cons_idx;
+ uchar *packet_expected;
+ int i;
+
+ packet_expected = (uchar *)enetc_rxb_address(dev, pi);
+ if (packet != packet_expected) {
+ printf("%s: Unexpected packet (expected %p)\n", __func__,
+ packet_expected);
+ return -EINVAL;
+ }
+
rxr->next_prod_idx = (pi + 1) % rxr->bd_count;
ci = (ci + 1) % rxr->bd_count;
rxr->next_cons_idx = ci;
dmb();
- /* free up the slot in the ring for HW */
- enetc_write_reg(rxr->cons_idx, ci);
- return len;
+ if ((pi + 1) % bd_num_in_cl == 0) {
+ /* BD clean up and advance to next in ring */
+ for (i = 0; i < bd_num_in_cl; i++) {
+ memset(&priv->enetc_rxbd[pi - i], 0, sizeof(union enetc_rx_bd));
+ priv->enetc_rxbd[pi - i].w.addr = enetc_rxb_address(dev, pi - i);
+ }
+
+ /* Will flush all bds in one cacheline */
+ enetc_flush_bd(dev, pi - bd_num_in_cl + 1, false);
+
+ /* free up the slot in the ring for HW */
+ enetc_write_reg(rxr->cons_idx, ci);
+ }
+
+ return 0;
}
-static const struct eth_ops enetc_ops = {
+#if IS_ENABLED(CONFIG_ARCH_IMX9)
+static int enetc_read_rom_hwaddr(struct udevice *dev)
+{
+ struct eth_pdata *pdata = dev_get_plat(dev);
+ unsigned int dev_id = enetc_dev_id(dev);
+ unsigned char *mac = pdata->enetaddr;
+
+ if (dev_id > 2)
+ return -EINVAL;
+
+ imx_get_mac_from_fuse(dev_id, mac);
+
+ return !is_valid_ethaddr(mac);
+}
+
+static const struct eth_ops enetc_ops_imx = {
+ .start = enetc_start,
+ .send = enetc_send,
+ .recv = enetc_recv,
+ .stop = enetc_stop,
+ .free_pkt = enetc_free_pkt,
+ .write_hwaddr = enetc_write_hwaddr,
+ .read_rom_hwaddr = enetc_read_rom_hwaddr,
+};
+
+U_BOOT_DRIVER(eth_enetc_imx) = {
+ .name = ENETC_DRIVER_NAME,
+ .id = UCLASS_ETH,
+ .bind = enetc_bind,
+ .probe = enetc_probe,
+ .remove = enetc_remove,
+ .ops = &enetc_ops_imx,
+ .priv_auto = sizeof(struct enetc_priv),
+ .plat_auto = sizeof(struct eth_pdata),
+};
+
+static const struct enetc_data enetc_data_imx = {
+ .reg_offset_pmr = ENETC_PMR_OFFSET_IMX,
+ .reg_offset_psipmar = ENETC_PSIPMARn_OFFSET_IMX,
+ .reg_offset_pcapr = ENETC_PCAPR_OFFSET_IMX,
+ .reg_offset_psicfgr = ENETC_PSICFGR_OFFSET_IMX,
+ .reg_offset_mac = ENETC_PM_OFFSET_IMX,
+};
+
+static struct pci_device_id enetc_ids_imx[] = {
+ {
+ PCI_DEVICE(PCI_VENDOR_ID_PHILIPS, PCI_DEVICE_ID_ENETC4_ETH),
+ .driver_data = (ulong)&enetc_data_imx,
+ },
+ {}
+};
+
+U_BOOT_PCI_DEVICE(eth_enetc_imx, enetc_ids_imx);
+#endif
+
+static const struct eth_ops enetc_ops_ls = {
.start = enetc_start,
.send = enetc_send,
.recv = enetc_recv,
.stop = enetc_stop,
+ .free_pkt = enetc_free_pkt,
.write_hwaddr = enetc_write_hwaddr,
};
-U_BOOT_DRIVER(eth_enetc) = {
+U_BOOT_DRIVER(eth_enetc_ls) = {
.name = ENETC_DRIVER_NAME,
.id = UCLASS_ETH,
.bind = enetc_bind,
.probe = enetc_probe,
.remove = enetc_remove,
- .ops = &enetc_ops,
+ .ops = &enetc_ops_ls,
.priv_auto = sizeof(struct enetc_priv),
.plat_auto = sizeof(struct eth_pdata),
};
-static struct pci_device_id enetc_ids[] = {
- { PCI_DEVICE(PCI_VENDOR_ID_FREESCALE, PCI_DEVICE_ID_ENETC_ETH) },
+static const struct enetc_data enetc_data_ls = {
+ .reg_offset_pmr = ENETC_PMR_OFFSET_LS,
+ .reg_offset_psipmar = ENETC_PSIPMARn_OFFSET_LS,
+ .reg_offset_pcapr = ENETC_PCAPR_OFFSET_LS,
+ .reg_offset_psicfgr = ENETC_PSICFGR_OFFSET_LS,
+ .reg_offset_mac = ENETC_PM_OFFSET_LS,
+};
+
+static struct pci_device_id enetc_ids_ls[] = {
+ {
+ PCI_DEVICE(PCI_VENDOR_ID_FREESCALE, PCI_DEVICE_ID_ENETC_ETH),
+ .driver_data = (ulong)&enetc_data_ls,
+ },
{}
};
-U_BOOT_PCI_DEVICE(eth_enetc, enetc_ids);
+U_BOOT_PCI_DEVICE(eth_enetc_ls, enetc_ids_ls);
diff --git a/drivers/net/fsl_enetc.h b/drivers/net/fsl_enetc.h
index f2acf367aa3..804df853bf5 100644
--- a/drivers/net/fsl_enetc.h
+++ b/drivers/net/fsl_enetc.h
@@ -12,7 +12,9 @@
/* PCI function IDs */
#define PCI_DEVICE_ID_ENETC_ETH 0xE100
+#define PCI_DEVICE_ID_ENETC4_ETH 0xE101
#define PCI_DEVICE_ID_ENETC_MDIO 0xEE01
+#define PCI_DEVICE_ID_ENETC4_EMDIO 0xEE00
/* ENETC Ethernet controller registers */
/* Station interface register offsets */
@@ -22,7 +24,8 @@
/* write cache cfg: snoop, no allocate, data & BD coherent */
#define ENETC_SICAR_WR_CFG 0x6767
/* read cache cfg: coherent copy, look up, don't alloc in cache */
-#define ENETC_SICAR_RD_CFG 0x27270000
+#define ENETC_SICAR_RD_CFG_LS 0x27270000
+#define ENETC_SICAR_RD_CFG_IMX 0x2b2b0000
#define ENETC_SIROCT 0x300
#define ENETC_SIRFRM 0x308
#define ENETC_SITOCT 0x320
@@ -57,32 +60,60 @@ enum enetc_bdr_type {TX, RX};
#define ENETC_PORT_REGS_OFF 0x10000
/* Port registers */
+#define ENETC_PMR_OFFSET_IMX 0x0010
+#define ENETC_PMR_OFFSET_LS 0x0000
#define ENETC_PMR 0x0000
#define ENETC_PMR_SI0_EN BIT(16)
#define ENETC_PSIPMMR 0x0018
-#define ENETC_PSIPMAR0 0x0100
-#define ENETC_PSIPMAR1 0x0104
-#define ENETC_PCAPR0 0x0900
-#define ENETC_PCAPRO_MDIO BIT(11)
-#define ENETC_PSICFGR(n) (0x0940 + (n) * 0x10)
-#define ENETC_PSICFGR_SET_TXBDR(val) ((val) & 0xff)
-#define ENETC_PSICFGR_SET_RXBDR(val) (((val) & 0xff) << 16)
+#define ENETC_PSIPMARn_OFFSET_IMX 0x0000
+#define ENETC_PSIPMARn_OFFSET_LS 0x0080
+#define ENETC_PSIPMAR0 0x0080
+#define ENETC_PSIPMAR1 0x0084
+#define ENETC_PCAPR_OFFSET_IMX 0x4008
+#define ENETC_PCAPR_OFFSET_LS 0x0900
+#define ENETC_PCAPR0 0x0000
+#define ENETC_PCAPRO_MDIO BIT(11) /* LS only */
+#define ENETC_PCS_PROT GENMASK(15, 0) /* IMX only */
+/* ENETC base registers */
+#define ENETC_PSICFGR_OFFSET_LS 0x0940
+#define ENETC_PSICFGR_SHIFT_LS 0x10
+#define ENETC_PSICFGR_OFFSET_IMX 0x2010
+#define ENETC_PSICFGR_SHIFT_IMX 0x80
+#define ENETC_PSICFGR(n, s) ((n) * (s))
+#define ENETC_PSICFGR_SET_BDR(rx, tx) (((rx) << 16) | (tx))
/* MAC configuration */
-#define ENETC_PM_CC 0x8008
+#define ENETC_PM_OFFSET_IMX 0x5000
+#define ENETC_PM_OFFSET_LS 0x8000
+#define ENETC_PM_CC 0x0008
#define ENETC_PM_CC_DEFAULT 0x0810
-#define ENETC_PM_CC_RX_TX_EN 0x8813
-#define ENETC_PM_MAXFRM 0x8014
+#define ENETC_PM_CC_TXP_IMX BIT(15)
+#define ENETC_PM_CC_TXP_LS BIT(11)
+#define ENETC_PM_CC_PROMIS BIT(4)
+#define ENETC_PM_CC_TX BIT(1)
+#define ENETC_PM_CC_RX BIT(0)
+#define ENETC_PM_MAXFRM 0x0014
#define ENETC_RX_MAXFRM_SIZE PKTSIZE_ALIGN
-#define ENETC_PM_IMDIO_BASE 0x8030
-#define ENETC_PM_IF_MODE 0x8300
+#define ENETC_PM_IMDIO_BASE 0x0030
+#define ENETC_PM_IF_MODE 0x0300
#define ENETC_PM_IF_MODE_RG BIT(2)
#define ENETC_PM_IF_MODE_AN_ENA BIT(15)
#define ENETC_PM_IFM_SSP_MASK GENMASK(14, 13)
#define ENETC_PM_IFM_SSP_1000 (2 << 13)
#define ENETC_PM_IFM_SSP_100 (0 << 13)
#define ENETC_PM_IFM_SSP_10 (1 << 13)
-#define ENETC_PM_IFM_FULL_DPX BIT(12)
-#define ENETC_PM_IF_IFMODE_MASK GENMASK(1, 0)
+#define ENETC_PM_IFM_FULL_DPX_IMX BIT(6)
+#define ENETC_PM_IFM_FULL_DPX_LS BIT(12)
+#define ENETC_PM_IF_IFMODE_MASK_IMX GENMASK(2, 0)
+#define ENETC_PM_IF_IFMODE_MASK_LS GENMASK(1, 0)
+
+/* i.MX95 specific registers */
+#define IMX95_ENETC_SIPMAR0 0x80
+#define IMX95_ENETC_SIPMAR1 0x84
+
+/* Port registers */
+#define IMX95_ENETC_PMAR0 0x4020
+#define IMX95_ENETC_PMAR1 0x4024
+#define ENETC_POR 0x4100
/* buffer descriptors count must be multiple of 8 and aligned to 128 bytes */
#define ENETC_BD_CNT CONFIG_SYS_RX_ETH_BUFFER
@@ -163,25 +194,14 @@ struct enetc_priv {
struct phy_device *phy;
};
-/* register accessors */
-#define enetc_read_reg(x) readl((x))
-#define enetc_write_reg(x, val) writel((val), (x))
-#define enetc_read(priv, off) enetc_read_reg((priv)->regs_base + (off))
-#define enetc_write(priv, off, v) \
- enetc_write_reg((priv)->regs_base + (off), v)
-
-/* port register accessors */
-#define enetc_port_regs(priv, off) ((priv)->port_regs + (off))
-#define enetc_read_port(priv, off) \
- enetc_read_reg(enetc_port_regs((priv), (off)))
-#define enetc_write_port(priv, off, v) \
- enetc_write_reg(enetc_port_regs((priv), (off)), v)
-
-/* BDR register accessors, see ENETC_BDR() */
-#define enetc_bdr_read(priv, t, n, off) \
- enetc_read(priv, ENETC_BDR(t, n, off))
-#define enetc_bdr_write(priv, t, n, off, val) \
- enetc_write(priv, ENETC_BDR(t, n, off), val)
+struct enetc_data {
+ /* Register layout offsets */
+ u16 reg_offset_pmr;
+ u16 reg_offset_psipmar;
+ u16 reg_offset_pcapr;
+ u16 reg_offset_psicfgr;
+ u16 reg_offset_mac;
+};
/* PCS / internal SoC PHY ID, it defaults to 0 on all interfaces */
#define ENETC_PCS_PHY_ADDR 0
diff --git a/drivers/net/fsl_enetc_mdio.c b/drivers/net/fsl_enetc_mdio.c
index 2d5fcbb6dbd..c1d491f2c5a 100644
--- a/drivers/net/fsl_enetc_mdio.c
+++ b/drivers/net/fsl_enetc_mdio.c
@@ -1,7 +1,7 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* ENETC ethernet controller driver
- * Copyright 2019 NXP
+ * Copyright 2019-2025 NXP
*/
#include <dm.h>
@@ -14,6 +14,16 @@
#include "fsl_enetc.h"
+static u32 enetc_read(struct enetc_mdio_priv *priv, u32 off)
+{
+ return readl(priv->regs_base + off);
+}
+
+static void enetc_write(struct enetc_mdio_priv *priv, u32 off, u32 val)
+{
+ writel(val, priv->regs_base + off);
+}
+
static void enetc_mdio_wait_bsy(struct enetc_mdio_priv *priv)
{
int to = 10000;
@@ -122,7 +132,9 @@ static int enetc_mdio_bind(struct udevice *dev)
static int enetc_mdio_probe(struct udevice *dev)
{
+ struct pci_child_plat *pplat = dev_get_parent_plat(dev);
struct enetc_mdio_priv *priv = dev_get_priv(dev);
+ u16 cmd = PCI_COMMAND_MEMORY;
priv->regs_base = dm_pci_map_bar(dev, PCI_BASE_ADDRESS_0, 0, 0, PCI_REGION_TYPE, 0);
if (!priv->regs_base) {
@@ -132,7 +144,10 @@ static int enetc_mdio_probe(struct udevice *dev)
priv->regs_base += ENETC_MDIO_BASE;
- dm_pci_clrset_config16(dev, PCI_COMMAND, 0, PCI_COMMAND_MEMORY);
+ if (pplat->vendor == PCI_VENDOR_ID_PHILIPS) /* i.MX95 */
+ cmd |= PCI_COMMAND_MASTER;
+
+ dm_pci_clrset_config16(dev, PCI_COMMAND, 0, cmd);
return 0;
}
@@ -148,6 +163,7 @@ U_BOOT_DRIVER(enetc_mdio) = {
static struct pci_device_id enetc_mdio_ids[] = {
{ PCI_DEVICE(PCI_VENDOR_ID_FREESCALE, PCI_DEVICE_ID_ENETC_MDIO) },
+ { PCI_DEVICE(PCI_VENDOR_ID_PHILIPS, PCI_DEVICE_ID_ENETC4_EMDIO) },
{ }
};
diff --git a/drivers/net/fsl_enetc_netc_blk_ctrl.c b/drivers/net/fsl_enetc_netc_blk_ctrl.c
new file mode 100644
index 00000000000..46b68d3d8a4
--- /dev/null
+++ b/drivers/net/fsl_enetc_netc_blk_ctrl.c
@@ -0,0 +1,346 @@
+// SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause)
+/*
+ * NXP NETC Blocks Control Driver
+ *
+ * Copyright 2024 NXP
+ *
+ * This driver is used for pre-initialization of NETC, such as PCS and MII
+ * protocols, LDID, warm reset, etc. Therefore, all NETC device drivers can
+ * only be probed after the netc-blk-crtl driver has completed initialization.
+ * In addition, when the system enters suspend mode, IERB, PRB, and NETCMIX
+ * will be powered off, except for WOL. Therefore, when the system resumes,
+ * these blocks need to be reinitialized.
+ */
+
+#include <asm/io.h>
+#include <clk.h>
+#include <dm.h>
+#include <dm/device_compat.h>
+#include <linux/bitfield.h>
+#include <linux/delay.h>
+#include <linux/errno.h>
+#include <linux/iopoll.h>
+#include <phy_interface.h>
+
+/* NETCMIX registers */
+#define IMX95_CFG_LINK_IO_VAR 0x0
+#define IO_VAR_16FF_16G_SERDES 0x1
+#define IO_VAR(port, var) (((var) & 0xf) << ((port) << 2))
+
+#define IMX95_CFG_LINK_MII_PROT 0x4
+#define CFG_LINK_MII_PORT_0 GENMASK(3, 0)
+#define CFG_LINK_MII_PORT_1 GENMASK(7, 4)
+#define MII_PROT_MII 0x0
+#define MII_PROT_RMII 0x1
+#define MII_PROT_RGMII 0x2
+#define MII_PROT_SERIAL 0x3
+#define MII_PROT(port, prot) (((prot) & 0xf) << ((port) << 2))
+
+#define IMX95_CFG_LINK_PCS_PROT(a) (0x8 + (a) * 4)
+#define PCS_PROT_1G_SGMII BIT(0)
+#define PCS_PROT_2500M_SGMII BIT(1)
+#define PCS_PROT_XFI BIT(3)
+#define PCS_PROT_SFI BIT(4)
+#define PCS_PROT_10G_SXGMII BIT(6)
+
+/* NETC privileged register block register */
+#define PRB_NETCRR 0x100
+#define NETCRR_SR BIT(0)
+#define NETCRR_LOCK BIT(1)
+
+#define PRB_NETCSR 0x104
+#define NETCSR_ERROR BIT(0)
+#define NETCSR_STATE BIT(1)
+
+/* NETC integrated endpoint register block register */
+#define IERB_EMDIOFAUXR 0x344
+#define IERB_T0FAUXR 0x444
+#define IERB_EFAUXR(a) (0x3044 + 0x100 * (a))
+#define IERB_VFAUXR(a) (0x4004 + 0x40 * (a))
+#define FAUXR_LDID GENMASK(3, 0)
+
+/* Platform information */
+#define IMX95_ENETC0_BUS_DEVFN 0x0
+#define IMX95_ENETC1_BUS_DEVFN 0x40
+#define IMX95_ENETC2_BUS_DEVFN 0x80
+
+/* Flags for different platforms */
+#define NETC_HAS_NETCMIX BIT(0)
+
+struct netc_blk_ctrl {
+ void __iomem *prb;
+ void __iomem *ierb;
+ void __iomem *netcmix;
+};
+
+static void netc_reg_write(void __iomem *base, u32 offset, u32 val)
+{
+ writel(val, base + offset);
+}
+
+static u32 netc_reg_read(void __iomem *base, u32 offset)
+{
+ return readl(base + offset);
+}
+
+static int netc_of_pci_get_bus_devfn(ofnode node)
+{
+ u32 reg[5];
+ int error;
+
+ error = ofnode_read_u32_array(node, "reg", reg, ARRAY_SIZE(reg));
+ if (error)
+ return error;
+
+ return (reg[0] >> 8) & 0xffff;
+}
+
+static int netc_get_link_mii_protocol(phy_interface_t interface)
+{
+ switch (interface) {
+ case PHY_INTERFACE_MODE_MII:
+ return MII_PROT_MII;
+ case PHY_INTERFACE_MODE_RMII:
+ return MII_PROT_RMII;
+ case PHY_INTERFACE_MODE_RGMII:
+ case PHY_INTERFACE_MODE_RGMII_ID:
+ case PHY_INTERFACE_MODE_RGMII_RXID:
+ case PHY_INTERFACE_MODE_RGMII_TXID:
+ return MII_PROT_RGMII;
+ case PHY_INTERFACE_MODE_SGMII:
+ case PHY_INTERFACE_MODE_2500BASEX:
+ case PHY_INTERFACE_MODE_10GBASER:
+ case PHY_INTERFACE_MODE_XGMII:
+ case PHY_INTERFACE_MODE_USXGMII:
+ return MII_PROT_SERIAL;
+ default:
+ return -EINVAL;
+ }
+}
+
+static int imx95_netcmix_init(struct udevice *dev)
+{
+ struct netc_blk_ctrl *priv = dev_get_priv(dev);
+ ofnode child, gchild;
+ phy_interface_t interface;
+ int bus_devfn, mii_proto;
+ u32 val;
+
+ /* Default setting of MII protocol */
+ val = MII_PROT(0, MII_PROT_RGMII) | MII_PROT(1, MII_PROT_RGMII) |
+ MII_PROT(2, MII_PROT_SERIAL);
+
+ /* Update the link MII protocol through parsing phy-mode */
+ 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"))
+ continue;
+
+ bus_devfn = netc_of_pci_get_bus_devfn(gchild);
+ if (bus_devfn < 0)
+ return -EINVAL;
+
+ if (bus_devfn == IMX95_ENETC2_BUS_DEVFN)
+ continue;
+
+ interface = ofnode_read_phy_mode(gchild);
+ if (interface == -1)
+ continue;
+
+ mii_proto = netc_get_link_mii_protocol(interface);
+ if (mii_proto < 0)
+ return -EINVAL;
+
+ switch (bus_devfn) {
+ case IMX95_ENETC0_BUS_DEVFN:
+ val &= ~CFG_LINK_MII_PORT_0;
+ val |= FIELD_PREP(CFG_LINK_MII_PORT_0, mii_proto);
+ break;
+ case IMX95_ENETC1_BUS_DEVFN:
+ val &= ~CFG_LINK_MII_PORT_1;
+ val |= FIELD_PREP(CFG_LINK_MII_PORT_1, mii_proto);
+ break;
+ default:
+ return -EINVAL;
+ }
+ }
+ }
+
+ /* Configure Link I/O variant */
+ netc_reg_write(priv->netcmix, IMX95_CFG_LINK_IO_VAR,
+ IO_VAR(2, IO_VAR_16FF_16G_SERDES));
+ /* Configure Link 2 PCS protocol */
+ netc_reg_write(priv->netcmix, IMX95_CFG_LINK_PCS_PROT(2),
+ PCS_PROT_10G_SXGMII);
+ netc_reg_write(priv->netcmix, IMX95_CFG_LINK_MII_PROT, val);
+
+ return 0;
+}
+
+static bool netc_ierb_is_locked(struct netc_blk_ctrl *priv)
+{
+ return !!(netc_reg_read(priv->prb, PRB_NETCRR) & NETCRR_LOCK);
+}
+
+static int netc_lock_ierb(struct netc_blk_ctrl *priv)
+{
+ u32 val;
+
+ netc_reg_write(priv->prb, PRB_NETCRR, NETCRR_LOCK);
+
+ return readl_poll_timeout(priv->prb + PRB_NETCSR, val,
+ !(val & NETCSR_STATE), 2000);
+}
+
+static int netc_unlock_ierb_with_warm_reset(struct netc_blk_ctrl *priv)
+{
+ u32 val;
+
+ netc_reg_write(priv->prb, PRB_NETCRR, 0);
+
+ return readl_poll_timeout(priv->prb + PRB_NETCRR, val,
+ !(val & NETCRR_LOCK), 100000);
+}
+
+static int imx95_ierb_init(struct udevice *dev)
+{
+ struct netc_blk_ctrl *priv = dev_get_priv(dev);
+
+ /* EMDIO : No MSI-X intterupt */
+ netc_reg_write(priv->ierb, IERB_EMDIOFAUXR, 0);
+ /* ENETC0 PF */
+ netc_reg_write(priv->ierb, IERB_EFAUXR(0), 0);
+ /* ENETC0 VF0 */
+ netc_reg_write(priv->ierb, IERB_VFAUXR(0), 1);
+ /* ENETC0 VF1 */
+ netc_reg_write(priv->ierb, IERB_VFAUXR(1), 2);
+ /* ENETC1 PF */
+ netc_reg_write(priv->ierb, IERB_EFAUXR(1), 3);
+ /* ENETC1 VF0 */
+ netc_reg_write(priv->ierb, IERB_VFAUXR(2), 5);
+ /* ENETC1 VF1 */
+ netc_reg_write(priv->ierb, IERB_VFAUXR(3), 6);
+ /* ENETC2 PF */
+ netc_reg_write(priv->ierb, IERB_EFAUXR(2), 4);
+ /* ENETC2 VF0 */
+ netc_reg_write(priv->ierb, IERB_VFAUXR(4), 5);
+ /* ENETC2 VF1 */
+ netc_reg_write(priv->ierb, IERB_VFAUXR(5), 6);
+ /* NETC TIMER */
+ netc_reg_write(priv->ierb, IERB_T0FAUXR, 7);
+
+ return 0;
+}
+
+static int netc_ierb_init(struct udevice *dev)
+{
+ struct netc_blk_ctrl *priv = dev_get_priv(dev);
+ int err;
+
+ if (netc_ierb_is_locked(priv)) {
+ err = netc_unlock_ierb_with_warm_reset(priv);
+ if (err) {
+ dev_err(dev, "Unlock IERB failed.\n");
+ return err;
+ }
+ }
+
+ err = imx95_ierb_init(dev);
+ if (err)
+ return err;
+
+ err = netc_lock_ierb(priv);
+ if (err) {
+ dev_err(dev, "Lock IERB failed.\n");
+ return err;
+ }
+
+ return 0;
+}
+
+static int netc_prb_check_error(struct netc_blk_ctrl *priv)
+{
+ if (netc_reg_read(priv->prb, PRB_NETCSR) & NETCSR_ERROR)
+ return -1;
+
+ return 0;
+}
+
+static const struct udevice_id netc_blk_ctrl_match[] = {
+ { .compatible = "nxp,imx95-netc-blk-ctrl" },
+ {},
+};
+
+static int netc_blk_ctrl_probe(struct udevice *dev)
+{
+ struct netc_blk_ctrl *priv = dev_get_priv(dev);
+ struct clk *ipg_clk;
+ fdt_addr_t regs;
+ int err;
+
+ ipg_clk = devm_clk_get_optional(dev, "ipg");
+ if (IS_ERR(ipg_clk)) {
+ dev_err(dev, "Set ipg clock failed\n");
+ return PTR_ERR(ipg_clk);
+ }
+
+ err = clk_prepare_enable(ipg_clk);
+ if (err) {
+ dev_err(dev, "Enable ipg clock failed\n");
+ return PTR_ERR(ipg_clk);
+ }
+
+ regs = dev_read_addr_name(dev, "ierb");
+ if (regs == FDT_ADDR_T_NONE) {
+ dev_err(dev, "Missing IERB resource\n");
+ return -EINVAL;
+ }
+
+ priv->ierb = (void __iomem *)regs;
+ regs = dev_read_addr_name(dev, "prb");
+ if (regs == FDT_ADDR_T_NONE) {
+ dev_err(dev, "Missing PRB resource\n");
+ return -EINVAL;
+ }
+
+ priv->prb = (void __iomem *)regs;
+ regs = dev_read_addr_name(dev, "netcmix");
+ if (regs == FDT_ADDR_T_NONE) {
+ dev_err(dev, "Missing NETCMIX resource\n");
+ return -EINVAL;
+ }
+
+ priv->netcmix = (void __iomem *)regs;
+
+ err = imx95_netcmix_init(dev);
+ if (err) {
+ dev_err(dev, "Initializing NETCMIX failed\n");
+ return err;
+ }
+
+ err = netc_ierb_init(dev);
+ if (err) {
+ dev_err(dev, "Initializing IERB failed\n");
+ return err;
+ }
+
+ if (netc_prb_check_error(priv) < 0)
+ dev_warn(dev, "The current IERB configuration is invalid\n");
+
+ return 0;
+}
+
+U_BOOT_DRIVER(netc_blk_ctrl_drv) = {
+ .name = "netc_blk_ctrl",
+ .id = UCLASS_SIMPLE_BUS,
+ .of_match = netc_blk_ctrl_match,
+ .probe = netc_blk_ctrl_probe,
+ .priv_auto = sizeof(struct netc_blk_ctrl),
+ .flags = DM_FLAG_PRE_RELOC,
+};
diff --git a/drivers/pinctrl/nxp/pinctrl-imx.c b/drivers/pinctrl/nxp/pinctrl-imx.c
index b1960c56b51..54cec37327c 100644
--- a/drivers/pinctrl/nxp/pinctrl-imx.c
+++ b/drivers/pinctrl/nxp/pinctrl-imx.c
@@ -219,7 +219,7 @@ int imx_pinctrl_probe(struct udevice *dev,
if (info->flags & IMX8_USE_SCU)
return 0;
- addr = ofnode_get_addr_size_index(dev_ofnode(dev), 0, &size);
+ addr = ofnode_get_addr_size_index(node, 0, &size);
if (addr == FDT_ADDR_T_NONE)
return -EINVAL;
@@ -228,7 +228,7 @@ int imx_pinctrl_probe(struct udevice *dev,
return -ENOMEM;
priv->info = info;
- info->mux_mask = ofnode_read_u32(node, "fsl,mux_mask", 0);
+ info->mux_mask = ofnode_read_u32_default(node, "fsl,mux_mask", 0);
/*
* Refer to linux documentation for details:
* Documentation/devicetree/bindings/pinctrl/fsl,imx7d-pinctrl.txt