diff options
Diffstat (limited to 'drivers/net/fsl_enetc_netc_blk_ctrl.c')
| -rw-r--r-- | drivers/net/fsl_enetc_netc_blk_ctrl.c | 329 |
1 files changed, 321 insertions, 8 deletions
diff --git a/drivers/net/fsl_enetc_netc_blk_ctrl.c b/drivers/net/fsl_enetc_netc_blk_ctrl.c index fecd66eb15a..8577bb75632 100644 --- a/drivers/net/fsl_enetc_netc_blk_ctrl.c +++ b/drivers/net/fsl_enetc_netc_blk_ctrl.c @@ -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; } |
