diff options
-rw-r--r-- | arch/arm/dts/imx8mm-u-boot.dtsi | 6 | ||||
-rw-r--r-- | arch/arm/dts/imx8mn-u-boot.dtsi | 6 | ||||
-rw-r--r-- | arch/arm/dts/imx8mp-u-boot.dtsi | 6 | ||||
-rw-r--r-- | arch/arm/dts/imx8mq-u-boot.dtsi | 6 | ||||
-rw-r--r-- | board/phytec/phycore_imx8mp/spl.c | 2 | ||||
-rw-r--r-- | configs/phycore-imx8mp_defconfig | 2 | ||||
-rw-r--r-- | drivers/net/Kconfig | 14 | ||||
-rw-r--r-- | drivers/net/Makefile | 1 | ||||
-rw-r--r-- | drivers/net/fsl_enetc.c | 487 | ||||
-rw-r--r-- | drivers/net/fsl_enetc.h | 88 | ||||
-rw-r--r-- | drivers/net/fsl_enetc_mdio.c | 20 | ||||
-rw-r--r-- | drivers/net/fsl_enetc_netc_blk_ctrl.c | 346 | ||||
-rw-r--r-- | drivers/pinctrl/nxp/pinctrl-imx.c | 4 |
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 |