net: fsl_enetc: Add i.MX94 support to NETC block control driver

Extend the NETC block control driver to support the i.MX94 SoC.

Acked-by: Peng Fan <peng.fan@nxp.com>
Reviewed-by: Jacky Bai <ping.bai@nxp.com>
Signed-off-by: Ye Li <ye.li@nxp.com>
Signed-off-by: Alice Guo <alice.guo@nxp.com>
This commit is contained in:
Ye Li
2025-10-28 10:46:29 +08:00
committed by Fabio Estevam
parent e940ad5097
commit f4e9645108
2 changed files with 324 additions and 10 deletions

View File

@@ -1006,8 +1006,9 @@ config FSL_ENETC
config FSL_ENETC_NETC_BLK_CTRL
bool "NXP ENETC NETC blocks control driver"
depends on FSL_ENETC && IMX95
default y if IMX95
depends on FSL_ENETC
depends on IMX95 || IMX94
default y if IMX95 || IMX94
help
This driver configures Integrated Endpoint Register Block (IERB) and
Privileged Register Block (PRB) of NETC. For i.MX platforms, it also

View File

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