[PATCH 01/13] net: fsl_enetc: Introduce enetc_is_ls1028a()

Introduce accurate test for LS1028A compatibility based both on IS_ENABLED(CONFIG_ARCH_LS1028A) and PCI vendor ID. This is done in preparation for adding ENETCv4 support, which has a different PCI vendor ID.
Signed-off-by: Marek Vasut marex@denx.de --- Cc: Alice Guo alice.guo@nxp.com Cc: Ilias Apalodimas ilias.apalodimas@linaro.org Cc: Jerome Forissier jerome.forissier@linaro.org Cc: Joe Hershberger joe.hershberger@ni.com Cc: Markus Gothe markus.gothe@genexis.eu Cc: Peng Fan peng.fan@nxp.com Cc: Ramon Fried rfried.dev@gmail.com Cc: Robert Marko robert.marko@sartura.hr Cc: Romain Naour romain.naour@smile.fr Cc: Simon Glass sjg@chromium.org Cc: Tim Harvey tharvey@gateworks.com Cc: Tom Rini trini@konsulko.com Cc: Ye Li ye.li@nxp.com Cc: u-boot@lists.denx.de --- drivers/net/fsl_enetc.c | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-)
diff --git a/drivers/net/fsl_enetc.c b/drivers/net/fsl_enetc.c index a6b0bafc8c6..d1414167723 100644 --- a/drivers/net/fsl_enetc.c +++ b/drivers/net/fsl_enetc.c @@ -23,6 +23,15 @@
static int enetc_remove(struct udevice *dev);
+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; +} + /* * sets the MAC address in IERB registers, this setting is persistent and * carried over to Linux. @@ -416,7 +425,7 @@ static int enetc_write_hwaddr(struct udevice *dev) struct enetc_priv *priv = dev_get_priv(dev); u8 *addr = plat->enetaddr;
- if (IS_ENABLED(CONFIG_ARCH_LS1028A)) + if (enetc_is_ls1028a(dev)) return enetc_ls1028a_write_hwaddr(dev);
u16 lower = *(const u16 *)(addr + 4);

Use enetc_is_ls1028() instead of ifdef around enetc_set_ierb_primary_mac() and clean up the function. No functional change intended.
Signed-off-by: Marek Vasut marex@denx.de --- Cc: Alice Guo alice.guo@nxp.com Cc: Ilias Apalodimas ilias.apalodimas@linaro.org Cc: Jerome Forissier jerome.forissier@linaro.org Cc: Joe Hershberger joe.hershberger@ni.com Cc: Markus Gothe markus.gothe@genexis.eu Cc: Peng Fan peng.fan@nxp.com Cc: Ramon Fried rfried.dev@gmail.com Cc: Robert Marko robert.marko@sartura.hr Cc: Romain Naour romain.naour@smile.fr Cc: Simon Glass sjg@chromium.org Cc: Tim Harvey tharvey@gateworks.com Cc: Tom Rini trini@konsulko.com Cc: Ye Li ye.li@nxp.com Cc: u-boot@lists.denx.de --- drivers/net/fsl_enetc.c | 32 ++++++++++++++++---------------- 1 file changed, 16 insertions(+), 16 deletions(-)
diff --git a/drivers/net/fsl_enetc.c b/drivers/net/fsl_enetc.c index d1414167723..fc51647257e 100644 --- a/drivers/net/fsl_enetc.c +++ b/drivers/net/fsl_enetc.c @@ -36,29 +36,29 @@ static int enetc_is_ls1028a(struct udevice *dev) * 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, int devfn, + const u8 *enetaddr) +{ + static int ierb_fn_to_pf[] = {0, 1, 2, -1, -1, -1, 3}; u16 lower = *(const u16 *)(enetaddr + 4); u32 upper = *(const u32 *)enetaddr;
- if (ierb_fn_to_pf[devfn] < 0) - 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 + 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. + */ + if (ierb_fn_to_pf[devfn] < 0) + 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); + } }
/* sets up primary MAC addresses in DT/IERB */

The entire content of the loop can be folded into enetc_set_ierb_primary_mac(), do it. This changes the behavior slightly such that the DT is only updated in case of a LS1028A, which is the only SoC with ethernet MAC path in DT matching "/soc/pcie@1f0000000/ethernet@%x,%x" anyway, so this slight change should have no impact.
Signed-off-by: Marek Vasut marex@denx.de --- Cc: Alice Guo alice.guo@nxp.com Cc: Ilias Apalodimas ilias.apalodimas@linaro.org Cc: Jerome Forissier jerome.forissier@linaro.org Cc: Joe Hershberger joe.hershberger@ni.com Cc: Markus Gothe markus.gothe@genexis.eu Cc: Peng Fan peng.fan@nxp.com Cc: Ramon Fried rfried.dev@gmail.com Cc: Robert Marko robert.marko@sartura.hr Cc: Romain Naour romain.naour@smile.fr Cc: Simon Glass sjg@chromium.org Cc: Tim Harvey tharvey@gateworks.com Cc: Tom Rini trini@konsulko.com Cc: Ye Li ye.li@nxp.com Cc: u-boot@lists.denx.de --- drivers/net/fsl_enetc.c | 39 +++++++++++++++++++-------------------- 1 file changed, 19 insertions(+), 20 deletions(-)
diff --git a/drivers/net/fsl_enetc.c b/drivers/net/fsl_enetc.c index fc51647257e..826b1971bb0 100644 --- a/drivers/net/fsl_enetc.c +++ b/drivers/net/fsl_enetc.c @@ -40,12 +40,16 @@ static int enetc_is_ls1028a(struct udevice *dev) #define IERB_PFMAC(pf, vf, n) (IERB_BASE + 0x8000 + (pf) * 0x100 + (vf) * 8 \ + (n) * 4)
-static void enetc_set_ierb_primary_mac(struct udevice *dev, int devfn, - const u8 *enetaddr) +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}; + 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); + 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_ls1028a(dev)) { /* @@ -53,24 +57,30 @@ static void enetc_set_ierb_primary_mac(struct udevice *dev, int devfn, * 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;
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; } + + 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) { @@ -78,18 +88,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); } }

Pass udevice pointer into enetc_enable_si_port() so tests like enetc_is_ls1028a() an be used in the function. No functional change.
Signed-off-by: Marek Vasut marex@denx.de --- Cc: Alice Guo alice.guo@nxp.com Cc: Ilias Apalodimas ilias.apalodimas@linaro.org Cc: Jerome Forissier jerome.forissier@linaro.org Cc: Joe Hershberger joe.hershberger@ni.com Cc: Markus Gothe markus.gothe@genexis.eu Cc: Peng Fan peng.fan@nxp.com Cc: Ramon Fried rfried.dev@gmail.com Cc: Robert Marko robert.marko@sartura.hr Cc: Romain Naour romain.naour@smile.fr Cc: Simon Glass sjg@chromium.org Cc: Tim Harvey tharvey@gateworks.com Cc: Tom Rini trini@konsulko.com Cc: Ye Li ye.li@nxp.com Cc: u-boot@lists.denx.de --- drivers/net/fsl_enetc.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-)
diff --git a/drivers/net/fsl_enetc.c b/drivers/net/fsl_enetc.c index 826b1971bb0..615646b76a3 100644 --- a/drivers/net/fsl_enetc.c +++ b/drivers/net/fsl_enetc.c @@ -437,8 +437,9 @@ static int enetc_write_hwaddr(struct udevice *dev) }
/* 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) { + struct enetc_priv *priv = dev_get_priv(dev); u32 val;
/* set Rx/Tx BDR count */ @@ -571,7 +572,7 @@ 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);

Rename the current driver structure and matching ops and PCI IDs and add _ls suffix to indicate this content is LS specific. This is done in preparation for addition of i.MX95 ENETCv4 which will require slightly different structure content.
Signed-off-by: Marek Vasut marex@denx.de --- Cc: Alice Guo alice.guo@nxp.com Cc: Ilias Apalodimas ilias.apalodimas@linaro.org Cc: Jerome Forissier jerome.forissier@linaro.org Cc: Joe Hershberger joe.hershberger@ni.com Cc: Markus Gothe markus.gothe@genexis.eu Cc: Peng Fan peng.fan@nxp.com Cc: Ramon Fried rfried.dev@gmail.com Cc: Robert Marko robert.marko@sartura.hr Cc: Romain Naour romain.naour@smile.fr Cc: Simon Glass sjg@chromium.org Cc: Tim Harvey tharvey@gateworks.com Cc: Tom Rini trini@konsulko.com Cc: Ye Li ye.li@nxp.com Cc: u-boot@lists.denx.de --- drivers/net/fsl_enetc.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-)
diff --git a/drivers/net/fsl_enetc.c b/drivers/net/fsl_enetc.c index 615646b76a3..639853539a1 100644 --- a/drivers/net/fsl_enetc.c +++ b/drivers/net/fsl_enetc.c @@ -689,7 +689,7 @@ static int enetc_recv(struct udevice *dev, int flags, uchar **packetp) return len; }
-static const struct eth_ops enetc_ops = { +static const struct eth_ops enetc_ops_ls = { .start = enetc_start, .send = enetc_send, .recv = enetc_recv, @@ -697,20 +697,20 @@ static const struct eth_ops enetc_ops = { .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[] = { +static struct pci_device_id enetc_ids_ls[] = { { PCI_DEVICE(PCI_VENDOR_ID_FREESCALE, PCI_DEVICE_ID_ENETC_ETH) }, {} };
-U_BOOT_PCI_DEVICE(eth_enetc, enetc_ids); +U_BOOT_PCI_DEVICE(eth_enetc_ls, enetc_ids_ls);

Introduce mapping function enetc_dev_id(), which converts PCIe BDF of the ENETC into linear incrementing index usable e.g. as interface index. This replaces the current ad-hoc calculation used in the code with a dedicated function. No functional change.
Signed-off-by: Marek Vasut marex@denx.de --- Cc: Alice Guo alice.guo@nxp.com Cc: Ilias Apalodimas ilias.apalodimas@linaro.org Cc: Jerome Forissier jerome.forissier@linaro.org Cc: Joe Hershberger joe.hershberger@ni.com Cc: Markus Gothe markus.gothe@genexis.eu Cc: Peng Fan peng.fan@nxp.com Cc: Ramon Fried rfried.dev@gmail.com Cc: Robert Marko robert.marko@sartura.hr Cc: Romain Naour romain.naour@smile.fr Cc: Simon Glass sjg@chromium.org Cc: Tim Harvey tharvey@gateworks.com Cc: Tom Rini trini@konsulko.com Cc: Ye Li ye.li@nxp.com Cc: u-boot@lists.denx.de --- drivers/net/fsl_enetc.c | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-)
diff --git a/drivers/net/fsl_enetc.c b/drivers/net/fsl_enetc.c index 639853539a1..e6f5ca633aa 100644 --- a/drivers/net/fsl_enetc.c +++ b/drivers/net/fsl_enetc.c @@ -32,6 +32,14 @@ static int enetc_is_ls1028a(struct udevice *dev) pplat->vendor == PCI_VENDOR_ID_FREESCALE; }
+static int enetc_dev_id(struct udevice *dev) +{ + if (enetc_is_ls1028a(dev)) + return PCI_FUNC(pci_get_devfn(dev)); + + return 0; +} + /* * sets the MAC address in IERB registers, this setting is persistent and * carried over to Linux. @@ -109,7 +117,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);

Move register accessors from header files and turn them into proper inline functions, so typechecking can be done on them. Drop no longer enetc_port_regs() and unused enetc_read() and enetc_bdr_read().
Signed-off-by: Marek Vasut marex@denx.de --- Cc: Alice Guo alice.guo@nxp.com Cc: Ilias Apalodimas ilias.apalodimas@linaro.org Cc: Jerome Forissier jerome.forissier@linaro.org Cc: Joe Hershberger joe.hershberger@ni.com Cc: Markus Gothe markus.gothe@genexis.eu Cc: Peng Fan peng.fan@nxp.com Cc: Ramon Fried rfried.dev@gmail.com Cc: Robert Marko robert.marko@sartura.hr Cc: Romain Naour romain.naour@smile.fr Cc: Simon Glass sjg@chromium.org Cc: Tim Harvey tharvey@gateworks.com Cc: Tom Rini trini@konsulko.com Cc: Ye Li ye.li@nxp.com Cc: u-boot@lists.denx.de --- drivers/net/fsl_enetc.c | 34 ++++++++++++++++++++++++++++++++++ drivers/net/fsl_enetc.h | 20 -------------------- drivers/net/fsl_enetc_mdio.c | 10 ++++++++++ 3 files changed, 44 insertions(+), 20 deletions(-)
diff --git a/drivers/net/fsl_enetc.c b/drivers/net/fsl_enetc.c index e6f5ca633aa..ace1069efd9 100644 --- a/drivers/net/fsl_enetc.c +++ b/drivers/net/fsl_enetc.c @@ -40,6 +40,40 @@ static int enetc_dev_id(struct udevice *dev) return 0; }
+/* 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); +} + +/* port register accessors */ +static u32 enetc_read_port(struct enetc_priv *priv, u32 off) +{ + return enetc_read_reg(priv->port_regs + off); +} + +static void enetc_write_port(struct enetc_priv *priv, u32 off, u32 val) +{ + enetc_write_reg(priv->port_regs + 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. diff --git a/drivers/net/fsl_enetc.h b/drivers/net/fsl_enetc.h index f2acf367aa3..da7414cbe99 100644 --- a/drivers/net/fsl_enetc.h +++ b/drivers/net/fsl_enetc.h @@ -163,26 +163,6 @@ 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) - /* 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..6a628e78883 100644 --- a/drivers/net/fsl_enetc_mdio.c +++ b/drivers/net/fsl_enetc_mdio.c @@ -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;

Split register accessors to the port base/station interface/port/mac registers as those are at different offsets on different SoCs. This is a preparatory patch which will allow addition of adjusted offsets for new SoCs easily.
Signed-off-by: Marek Vasut marex@denx.de --- Cc: Alice Guo alice.guo@nxp.com Cc: Ilias Apalodimas ilias.apalodimas@linaro.org Cc: Jerome Forissier jerome.forissier@linaro.org Cc: Joe Hershberger joe.hershberger@ni.com Cc: Markus Gothe markus.gothe@genexis.eu Cc: Peng Fan peng.fan@nxp.com Cc: Ramon Fried rfried.dev@gmail.com Cc: Robert Marko robert.marko@sartura.hr Cc: Romain Naour romain.naour@smile.fr Cc: Simon Glass sjg@chromium.org Cc: Tim Harvey tharvey@gateworks.com Cc: Tom Rini trini@konsulko.com Cc: Ye Li ye.li@nxp.com Cc: u-boot@lists.denx.de --- drivers/net/fsl_enetc.c | 68 +++++++++++++++++++++++++++++++---------- drivers/net/fsl_enetc.h | 25 +++++++++------ 2 files changed, 67 insertions(+), 26 deletions(-)
diff --git a/drivers/net/fsl_enetc.c b/drivers/net/fsl_enetc.c index ace1069efd9..532a367c241 100644 --- a/drivers/net/fsl_enetc.c +++ b/drivers/net/fsl_enetc.c @@ -56,14 +56,52 @@ 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 enetc_priv *priv, u32 val) +{ + const u32 off = ENETC_PMR + ENETC_PMR_OFFSET_LS; + + enetc_write_reg(priv->port_regs + off, val); +} + +static void enetc_write_psipmar(struct enetc_priv *priv, int n, u32 val) +{ + const u32 off = (n ? ENETC_PSIPMAR1 : ENETC_PSIPMAR0) + + ENETC_PSIPMARn_OFFSET_LS; + + enetc_write_reg(priv->port_regs + off, val); +} + +/* port station register accessors */ +static void enetc_write_psicfgr(struct enetc_priv *priv, int port, u32 val) +{ + const u32 off = ENETC_PSICFGR(port, ENETC_PSICFGR_SHIFT_LS) + + ENETC_PSICFGR_OFFSET_LS; + + enetc_write_reg(priv->port_regs + off, val); +} + /* port register accessors */ -static u32 enetc_read_port(struct enetc_priv *priv, u32 off) +static u32 enetc_read_pcapr_mdio(struct enetc_priv *priv) +{ + const u32 off = ENETC_PCAPR0 + ENETC_PCAPR_OFFSET_LS; + u32 reg = enetc_read_reg(priv->port_regs + off); + + return reg & ENETC_PCAPRO_MDIO; +} + +/* MAC port register accessors */ +static u32 enetc_read_mac_port(struct enetc_priv *priv, u32 off) { + off += ENETC_PM_OFFSET_LS; + return enetc_read_reg(priv->port_regs + off); }
-static void enetc_write_port(struct enetc_priv *priv, u32 off, u32 val) +static void enetc_write_mac_port(struct enetc_priv *priv, u32 off, u32 val) { + off += ENETC_PM_OFFSET_LS; + enetc_write_reg(priv->port_regs + off, val); }
@@ -234,7 +272,7 @@ static void enetc_init_rgmii(struct udevice *dev, struct phy_device *phydev) struct enetc_priv *priv = dev_get_priv(dev); u32 old_val, val;
- old_val = val = enetc_read_port(priv, ENETC_PM_IF_MODE); + old_val = val = enetc_read_mac_port(priv, ENETC_PM_IF_MODE);
/* disable unreliable RGMII in-band signaling and force the MAC into * the speed negotiated by the PHY. @@ -260,7 +298,7 @@ static void enetc_init_rgmii(struct udevice *dev, struct phy_device *phydev) if (val == old_val) return;
- enetc_write_port(priv, ENETC_PM_IF_MODE, val); + enetc_write_mac_port(priv, ENETC_PM_IF_MODE, val); }
/* set up MAC configuration for the given interface type */ @@ -280,9 +318,9 @@ 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_read_mac_port(priv, ENETC_PM_IF_MODE); if_mode &= ~ENETC_PM_IF_IFMODE_MASK; - enetc_write_port(priv, ENETC_PM_IF_MODE, if_mode); + enetc_write_mac_port(priv, ENETC_PM_IF_MODE, if_mode); break; }; } @@ -313,7 +351,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(priv)) { priv->imdio.read = enetc_mdio_read; priv->imdio.write = enetc_mdio_write; priv->imdio.priv = priv->port_regs + ENETC_PM_IMDIO_BASE; @@ -472,8 +510,8 @@ static int enetc_write_hwaddr(struct udevice *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(priv, 0, upper); + enetc_write_psipmar(priv, 1, lower);
return 0; } @@ -482,18 +520,16 @@ static int enetc_write_hwaddr(struct udevice *dev) static void enetc_enable_si_port(struct udevice *dev) { struct enetc_priv *priv = dev_get_priv(dev); - u32 val;
/* 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(priv, 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(priv, ENETC_PM_MAXFRM, ENETC_RX_MAXFRM_SIZE); /* enable MAC port */ - enetc_write_port(priv, ENETC_PM_CC, ENETC_PM_CC_RX_TX_EN); + enetc_write_mac_port(priv, ENETC_PM_CC, ENETC_PM_CC_RX_TX_EN); /* enable port */ - enetc_write_port(priv, ENETC_PMR, ENETC_PMR_SI0_EN); + enetc_write_pmr(priv, ENETC_PMR_SI0_EN); /* set SI cache policy */ enetc_write(priv, ENETC_SICAR0, ENETC_SICAR_RD_CFG | ENETC_SICAR_WR_CFG); diff --git a/drivers/net/fsl_enetc.h b/drivers/net/fsl_enetc.h index da7414cbe99..15408b669b4 100644 --- a/drivers/net/fsl_enetc.h +++ b/drivers/net/fsl_enetc.h @@ -57,24 +57,29 @@ enum enetc_bdr_type {TX, RX}; #define ENETC_PORT_REGS_OFF 0x10000
/* Port registers */ +#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_PSIPMARn_OFFSET_LS 0x0080 +#define ENETC_PSIPMAR0 0x0080 +#define ENETC_PSIPMAR1 0x0084 +#define ENETC_PCAPR_OFFSET_LS 0x0900 +#define ENETC_PCAPR0 0x0000 #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_PSICFGR_OFFSET_LS 0x0940 +#define ENETC_PSICFGR_SHIFT_LS 0x10 +#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_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_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)

Introduce driver data for each PCI device. The driver data carry offsets of registers which differ between different SoCs.
Signed-off-by: Marek Vasut marex@denx.de --- Cc: Alice Guo alice.guo@nxp.com Cc: Ilias Apalodimas ilias.apalodimas@linaro.org Cc: Jerome Forissier jerome.forissier@linaro.org Cc: Joe Hershberger joe.hershberger@ni.com Cc: Markus Gothe markus.gothe@genexis.eu Cc: Peng Fan peng.fan@nxp.com Cc: Ramon Fried rfried.dev@gmail.com Cc: Robert Marko robert.marko@sartura.hr Cc: Romain Naour romain.naour@smile.fr Cc: Simon Glass sjg@chromium.org Cc: Tim Harvey tharvey@gateworks.com Cc: Tom Rini trini@konsulko.com Cc: Ye Li ye.li@nxp.com Cc: u-boot@lists.denx.de --- drivers/net/fsl_enetc.c | 13 ++++++++++++- drivers/net/fsl_enetc.h | 9 +++++++++ 2 files changed, 21 insertions(+), 1 deletion(-)
diff --git a/drivers/net/fsl_enetc.c b/drivers/net/fsl_enetc.c index 532a367c241..c92626a8782 100644 --- a/drivers/net/fsl_enetc.c +++ b/drivers/net/fsl_enetc.c @@ -786,8 +786,19 @@ U_BOOT_DRIVER(eth_enetc_ls) = { .plat_auto = sizeof(struct eth_pdata), };
+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) }, + { + PCI_DEVICE(PCI_VENDOR_ID_FREESCALE, PCI_DEVICE_ID_ENETC_ETH), + .driver_data = (ulong)&enetc_data_ls, + }, {} };
diff --git a/drivers/net/fsl_enetc.h b/drivers/net/fsl_enetc.h index 15408b669b4..b815474c246 100644 --- a/drivers/net/fsl_enetc.h +++ b/drivers/net/fsl_enetc.h @@ -168,6 +168,15 @@ struct enetc_priv { struct phy_device *phy; };
+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

Pass struct udevice * into the register accessors, so the accessors can reach driver data, which contain device specific register offsets.
Signed-off-by: Marek Vasut marex@denx.de --- Cc: Alice Guo alice.guo@nxp.com Cc: Ilias Apalodimas ilias.apalodimas@linaro.org Cc: Jerome Forissier jerome.forissier@linaro.org Cc: Joe Hershberger joe.hershberger@ni.com Cc: Markus Gothe markus.gothe@genexis.eu Cc: Peng Fan peng.fan@nxp.com Cc: Ramon Fried rfried.dev@gmail.com Cc: Robert Marko robert.marko@sartura.hr Cc: Romain Naour romain.naour@smile.fr Cc: Simon Glass sjg@chromium.org Cc: Tim Harvey tharvey@gateworks.com Cc: Tom Rini trini@konsulko.com Cc: Ye Li ye.li@nxp.com Cc: u-boot@lists.denx.de --- drivers/net/fsl_enetc.c | 64 +++++++++++++++++++++++------------------ 1 file changed, 36 insertions(+), 28 deletions(-)
diff --git a/drivers/net/fsl_enetc.c b/drivers/net/fsl_enetc.c index c92626a8782..1a348f41ea0 100644 --- a/drivers/net/fsl_enetc.c +++ b/drivers/net/fsl_enetc.c @@ -57,52 +57,62 @@ static void enetc_write(struct enetc_priv *priv, u32 off, u32 val) }
/* base port register accessors */ -static void enetc_write_pmr(struct enetc_priv *priv, u32 val) +static void enetc_write_pmr(struct udevice *dev, u32 val) { - const u32 off = ENETC_PMR + ENETC_PMR_OFFSET_LS; + 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 enetc_priv *priv, int n, u32 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) + - ENETC_PSIPMARn_OFFSET_LS; + data->reg_offset_psipmar;
enetc_write_reg(priv->port_regs + off, val); }
/* port station register accessors */ -static void enetc_write_psicfgr(struct enetc_priv *priv, int port, u32 val) +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) + - ENETC_PSICFGR_OFFSET_LS; + data->reg_offset_psicfgr;
enetc_write_reg(priv->port_regs + off, val); }
/* port register accessors */ -static u32 enetc_read_pcapr_mdio(struct enetc_priv *priv) +static u32 enetc_read_pcapr_mdio(struct udevice *dev) { - const u32 off = ENETC_PCAPR0 + ENETC_PCAPR_OFFSET_LS; + 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; u32 reg = enetc_read_reg(priv->port_regs + off);
return reg & ENETC_PCAPRO_MDIO; }
/* MAC port register accessors */ -static u32 enetc_read_mac_port(struct enetc_priv *priv, u32 off) +static u32 enetc_read_mac_port(struct udevice *dev, u32 off) { - off += ENETC_PM_OFFSET_LS; + 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 + off); + return enetc_read_reg(priv->port_regs + data->reg_offset_mac + off); }
-static void enetc_write_mac_port(struct enetc_priv *priv, u32 off, u32 val) +static void enetc_write_mac_port(struct udevice *dev, u32 off, u32 val) { - off += ENETC_PM_OFFSET_LS; + 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 + off, val); + enetc_write_reg(priv->port_regs + data->reg_offset_mac + off, val); }
/* BDR register accessor, see also ENETC_BDR() */ @@ -269,10 +279,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;
- old_val = val = enetc_read_mac_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. @@ -298,7 +307,7 @@ static void enetc_init_rgmii(struct udevice *dev, struct phy_device *phydev) if (val == old_val) return;
- enetc_write_mac_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 */ @@ -318,9 +327,9 @@ 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_mac_port(priv, ENETC_PM_IF_MODE); + if_mode = enetc_read_mac_port(dev, ENETC_PM_IF_MODE); if_mode &= ~ENETC_PM_IF_IFMODE_MASK; - enetc_write_mac_port(priv, ENETC_PM_IF_MODE, if_mode); + enetc_write_mac_port(dev, ENETC_PM_IF_MODE, if_mode); break; }; } @@ -351,7 +360,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_pcapr_mdio(priv)) { + 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; @@ -501,7 +510,6 @@ 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 (enetc_is_ls1028a(dev)) @@ -510,8 +518,8 @@ static int enetc_write_hwaddr(struct udevice *dev) u16 lower = *(const u16 *)(addr + 4); u32 upper = *(const u32 *)addr;
- enetc_write_psipmar(priv, 0, upper); - enetc_write_psipmar(priv, 1, lower); + enetc_write_psipmar(dev, 0, upper); + enetc_write_psipmar(dev, 1, lower);
return 0; } @@ -522,14 +530,14 @@ static void enetc_enable_si_port(struct udevice *dev) struct enetc_priv *priv = dev_get_priv(dev);
/* set Rx/Tx BDR count */ - enetc_write_psicfgr(priv, 0, ENETC_PSICFGR_SET_BDR(ENETC_RX_BDR_CNT, - ENETC_TX_BDR_CNT)); + enetc_write_psicfgr(dev, 0, ENETC_PSICFGR_SET_BDR(ENETC_RX_BDR_CNT, + ENETC_TX_BDR_CNT)); /* set Rx max frame size */ - enetc_write_mac_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_mac_port(priv, ENETC_PM_CC, ENETC_PM_CC_RX_TX_EN); + enetc_write_mac_port(dev, ENETC_PM_CC, ENETC_PM_CC_RX_TX_EN); /* enable port */ - enetc_write_pmr(priv, ENETC_PMR_SI0_EN); + 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);

The netc-blk-ctrl driver is used to configure Integrated Endpoint Register Block (IERB) and Privileged Register Block (PRB) of NETC. For i.MX platforms, it is also used to configure the 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.
Note the IERB configuration registers can only be written after being unlocked by PRB, otherwise, all write operations are inhibited. A warm reset is performed when the IERB is unlocked, and it results in an FLR to all NETC devices. Therefore, all NETC device drivers must be probed or initialized after the warm reset is finished.
Ported from Linux 6.13-rc as of commit fe5ba6bf91b3 ("net: enetc: add initial netc-blk-ctrl driver support")
Signed-off-by: Marek Vasut marex@denx.de --- Cc: Alice Guo alice.guo@nxp.com Cc: Ilias Apalodimas ilias.apalodimas@linaro.org Cc: Jerome Forissier jerome.forissier@linaro.org Cc: Joe Hershberger joe.hershberger@ni.com Cc: Markus Gothe markus.gothe@genexis.eu Cc: Peng Fan peng.fan@nxp.com Cc: Ramon Fried rfried.dev@gmail.com Cc: Robert Marko robert.marko@sartura.hr Cc: Romain Naour romain.naour@smile.fr Cc: Simon Glass sjg@chromium.org Cc: Tim Harvey tharvey@gateworks.com Cc: Tom Rini trini@konsulko.com Cc: Ye Li ye.li@nxp.com Cc: u-boot@lists.denx.de --- drivers/net/Kconfig | 14 ++ drivers/net/Makefile | 1 + drivers/net/fsl_enetc_netc_blk_ctrl.c | 346 ++++++++++++++++++++++++++ 3 files changed, 361 insertions(+) create mode 100644 drivers/net/fsl_enetc_netc_blk_ctrl.c
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_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, +};

From: Alice Guo alice.guo@nxp.com
The verdor ID and device ID of i.MX95 EMDIO are different from LS1028A EMDIO, so add new vendor ID and device ID to pci_device_id table to support i.MX95 EMDIO.
Signed-off-by: Alice Guo alice.guo@nxp.com Signed-off-by: Marek Vasut marex@denx.de # Clean up Signed-off-by: Ye Li ye.li@nxp.com --- Cc: Alice Guo alice.guo@nxp.com Cc: Ilias Apalodimas ilias.apalodimas@linaro.org Cc: Jerome Forissier jerome.forissier@linaro.org Cc: Joe Hershberger joe.hershberger@ni.com Cc: Markus Gothe markus.gothe@genexis.eu Cc: Peng Fan peng.fan@nxp.com Cc: Ramon Fried rfried.dev@gmail.com Cc: Robert Marko robert.marko@sartura.hr Cc: Romain Naour romain.naour@smile.fr Cc: Simon Glass sjg@chromium.org Cc: Tim Harvey tharvey@gateworks.com Cc: Tom Rini trini@konsulko.com Cc: Ye Li ye.li@nxp.com Cc: u-boot@lists.denx.de --- drivers/net/fsl_enetc.h | 1 + drivers/net/fsl_enetc_mdio.c | 10 ++++++++-- 2 files changed, 9 insertions(+), 2 deletions(-)
diff --git a/drivers/net/fsl_enetc.h b/drivers/net/fsl_enetc.h index b815474c246..82c2476e1fa 100644 --- a/drivers/net/fsl_enetc.h +++ b/drivers/net/fsl_enetc.h @@ -13,6 +13,7 @@ /* PCI function IDs */ #define PCI_DEVICE_ID_ENETC_ETH 0xE100 #define PCI_DEVICE_ID_ENETC_MDIO 0xEE01 +#define PCI_DEVICE_ID_ENETC4_EMDIO 0xEE00
/* ENETC Ethernet controller registers */ /* Station interface register offsets */ diff --git a/drivers/net/fsl_enetc_mdio.c b/drivers/net/fsl_enetc_mdio.c index 6a628e78883..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> @@ -132,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) { @@ -142,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; } @@ -158,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) }, { } };

From: Alice Guo alice.guo@nxp.com
i.MX95 uses enetc version 4.1 controller. Update the enetc for i.MX95. Add ARM-specific cache handling and i.MX95 specific register layout handling.
Signed-off-by: Alice Guo alice.guo@nxp.com Signed-off-by: Marek Vasut marex@denx.de # Clean up Signed-off-by: Ye Li ye.li@nxp.com --- Cc: Alice Guo alice.guo@nxp.com Cc: Ilias Apalodimas ilias.apalodimas@linaro.org Cc: Jerome Forissier jerome.forissier@linaro.org Cc: Joe Hershberger joe.hershberger@ni.com Cc: Markus Gothe markus.gothe@genexis.eu Cc: Peng Fan peng.fan@nxp.com Cc: Ramon Fried rfried.dev@gmail.com Cc: Robert Marko robert.marko@sartura.hr Cc: Romain Naour romain.naour@smile.fr Cc: Simon Glass sjg@chromium.org Cc: Tim Harvey tharvey@gateworks.com Cc: Tom Rini trini@konsulko.com Cc: Ye Li ye.li@nxp.com Cc: u-boot@lists.denx.de --- drivers/net/fsl_enetc.c | 275 +++++++++++++++++++++++++++++++++++++--- drivers/net/fsl_enetc.h | 35 ++++- 2 files changed, 286 insertions(+), 24 deletions(-)
diff --git a/drivers/net/fsl_enetc.c b/drivers/net/fsl_enetc.c index 1a348f41ea0..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,13 +17,44 @@ #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); @@ -34,12 +66,60 @@ static int enetc_is_ls1028a(struct udevice *dev)
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) { @@ -93,9 +173,19 @@ 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; - u32 reg = enetc_read_reg(priv->port_regs + off); + const u32 reg = enetc_read_reg(priv->port_regs + off);
- return reg & ENETC_PCAPRO_MDIO; + 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 */ @@ -135,13 +225,30 @@ 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_ls1028a(dev)) { + 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 @@ -279,7 +386,7 @@ 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) { - u32 old_val, val; + u32 old_val, val, dpx = 0;
old_val = val = enetc_read_mac_port(dev, ENETC_PM_IF_MODE);
@@ -299,10 +406,15 @@ 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; @@ -328,7 +440,10 @@ static void enetc_setup_mac_iface(struct udevice *dev, case PHY_INTERFACE_MODE_10GBASER: /* set ifmode to (US)XGMII */ if_mode = enetc_read_mac_port(dev, ENETC_PM_IF_MODE); - if_mode &= ~ENETC_PM_IF_IFMODE_MASK; + 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; }; @@ -472,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. @@ -512,6 +642,8 @@ static int enetc_write_hwaddr(struct udevice *dev) struct eth_pdata *plat = dev_get_plat(dev); u8 *addr = plat->enetaddr;
+ if (enetc_is_imx95(dev)) + return enetc_imx95_write_hwaddr(dev); if (enetc_is_ls1028a(dev)) return enetc_ls1028a_write_hwaddr(dev);
@@ -528,6 +660,7 @@ static int enetc_write_hwaddr(struct udevice *dev) static void enetc_enable_si_port(struct udevice *dev) { 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 */ enetc_write_psicfgr(dev, 0, ENETC_PSICFGR_SET_BDR(ENETC_RX_BDR_CNT, @@ -535,12 +668,18 @@ static void enetc_enable_si_port(struct udevice *dev) /* set Rx max frame size */ enetc_write_mac_port(dev, ENETC_PM_MAXFRM, ENETC_RX_MAXFRM_SIZE); /* enable MAC port */ - enetc_write_mac_port(dev, 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 */ + 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); } @@ -631,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 */ @@ -651,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 */ @@ -664,9 +806,13 @@ static int enetc_start(struct udevice *dev) 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; }
/* @@ -709,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 = @@ -716,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; @@ -738,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); @@ -758,28 +909,114 @@ 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; }
+#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, };
diff --git a/drivers/net/fsl_enetc.h b/drivers/net/fsl_enetc.h index 82c2476e1fa..804df853bf5 100644 --- a/drivers/net/fsl_enetc.h +++ b/drivers/net/fsl_enetc.h @@ -12,6 +12,7 @@
/* 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
@@ -23,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 @@ -58,25 +60,37 @@ 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_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) +#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_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_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 0x0030 @@ -87,8 +101,19 @@ enum enetc_bdr_type {TX, RX}; #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

On Wed, Jan 15, 2025 at 8:05 PM Marek Vasut marex@denx.de wrote:
From: Alice Guo alice.guo@nxp.com
i.MX95 uses enetc version 4.1 controller. Update the enetc for i.MX95. Add ARM-specific cache handling and i.MX95 specific register layout handling.
Signed-off-by: Alice Guo alice.guo@nxp.com Signed-off-by: Marek Vasut marex@denx.de # Clean up Signed-off-by: Ye Li ye.li@nxp.com
Hi Marek and Alice,
Reviewed-by: Tim Harvey tharvey@gateworks.com
This is very clean and works for me on the imx95-evk after: - arm64: dts: imx95: add NETC related nodes (upstream linux commit 4511acd9eb3c52efa0252cdd7e6438ab3073bfaa) - arm64: dts: imx95-19x19-evk: add ENETC 0 support (upstream linux commit 025cf78938c22a02ba7f8aae3f46186e59cfc3af) - enable CONFIG_PCI_INIT_R such that pci_init is called early in order to enumerate the network device
Best regards,
Tim

On Thu, Jan 16, 2025 at 1:05 AM Marek Vasut marex@denx.de wrote:
Introduce accurate test for LS1028A compatibility based both on IS_ENABLED(CONFIG_ARCH_LS1028A) and PCI vendor ID. This is done in preparation for adding ENETCv4 support, which has a different PCI vendor ID.
Signed-off-by: Marek Vasut marex@denx.de
Series applied, thanks.
participants (3)
-
Fabio Estevam
-
Marek Vasut
-
Tim Harvey