diff --git a/drivers/net/ethernet/freescale/enetc/netc_blk_ctrl.c b/drivers/net/ethernet/freescale/enetc/netc_blk_ctrl.c index 92a0f824dae7..c7eb0234c785 100644 --- a/drivers/net/ethernet/freescale/enetc/netc_blk_ctrl.c +++ b/drivers/net/ethernet/freescale/enetc/netc_blk_ctrl.c @@ -261,40 +261,108 @@ static int imx94_link_config(struct netc_blk_ctrl *priv, } static int imx94_enetc_link_config(struct netc_blk_ctrl *priv, - struct device_node *np) + struct device_node *np, + bool *enetc0_en) { int link_id = imx94_enetc_get_link_id(np); if (link_id < 0) return link_id; + if (link_id == IMX94_ENETC0_LINK && of_device_is_available(np)) + *enetc0_en = true; + return imx94_link_config(priv, np, link_id); } +static int imx94_switch_link_config(struct netc_blk_ctrl *priv, + struct device_node *np, + bool *swp2_en) +{ + struct device_node *ports; + u32 port_id; + int err = 0; + + ports = of_get_child_by_name(np, "ethernet-ports"); + if (!ports) + return -ENODEV; + + /* The switch may be owned by a guest OS, in this case, the switch + * node in the host OS will be disabled, but we still hope that the + * host OS could do some configurations for the switch, as the + * netc_blk_ctrl is owned by host OS. So of_device_is_available() + * is not needed here. + */ + for_each_available_child_of_node_scoped(ports, child) { + if (of_property_read_u32(child, "reg", &port_id) < 0) { + err = -ENODEV; + goto end; + } + + switch (port_id) { + case 0 ... 2: /* External ports */ + err = imx94_link_config(priv, child, port_id); + if (err) + goto end; + + if (port_id == 2) + *swp2_en = true; + + break; + case 3: /* CPU port */ + break; + default: + err = -EINVAL; + goto end; + } + } + +end: + of_node_put(ports); + + return err; +} + static int imx94_netcmix_init(struct platform_device *pdev) { struct netc_blk_ctrl *priv = platform_get_drvdata(pdev); struct device_node *np = pdev->dev.of_node; + bool enetc0_en = false, swp2_en = false; u32 val; int err; for_each_child_of_node_scoped(np, child) { for_each_child_of_node_scoped(child, gchild) { - if (!of_device_is_compatible(gchild, "pci1131,e101")) - continue; - - err = imx94_enetc_link_config(priv, gchild); - if (err) - return err; + if (of_device_is_compatible(gchild, "pci1131,e101")) { + err = imx94_enetc_link_config(priv, gchild, + &enetc0_en); + if (err) + return err; + } else if (of_device_is_compatible(gchild, + "pci1131,eef2")) { + err = imx94_switch_link_config(priv, gchild, + &swp2_en); + if (err) + return err; + } } } - /* ENETC 0 and switch port 2 share the same parallel interface. - * Currently, the switch is not supported, so this interface is - * used by ENETC 0 by default. + if (enetc0_en && swp2_en) { + dev_err(&pdev->dev, + "Cannot enable swp2 and enetc0 at the same time\n"); + return -EINVAL; + } + + /* ENETC 0 and switch port 2 share the same parallel interface, they + * cannot be enabled at the same time. The interface is set for the + * ENETC 0 by default unless the switch port 2 is enabled in the DTS. */ val = netc_reg_read(priv->netcmix, IMX94_EXT_PIN_CONTROL); - val |= MAC2_MAC3_SEL; + if (!swp2_en) + val |= MAC2_MAC3_SEL; + else + val &= ~MAC2_MAC3_SEL; netc_reg_write(priv->netcmix, IMX94_EXT_PIN_CONTROL, val); return 0; @@ -610,6 +678,78 @@ static int imx94_enetc_mdio_phyaddr_config(struct netc_blk_ctrl *priv, return 0; } +static int imx94_ierb_enetc_init(struct netc_blk_ctrl *priv, + struct device_node *np, + u32 phy_mask) +{ + int err; + + err = imx94_enetc_update_tid(priv, np); + if (err) + return err; + + return imx94_enetc_mdio_phyaddr_config(priv, np, phy_mask); +} + +static int imx94_switch_mdio_phyaddr_config(struct netc_blk_ctrl *priv, + struct device_node *np, + u32 port_id, u32 phy_mask) +{ + int addr; + + /* The switch has 3 external ports at most */ + if (port_id > 2) + return 0; + + addr = netc_get_phy_addr(np); + if (addr < 0) { + if (addr == -ENODEV) + return 0; + + return addr; + } + + if (phy_mask & BIT(addr)) { + dev_err(&priv->pdev->dev, + "Found same PHY address in EMDIO and switch node\n"); + return -EINVAL; + } + + netc_reg_write(priv->ierb, IERB_LBCR(port_id), + LBCR_MDIO_PHYAD_PRTAD(addr)); + + return 0; +} + +static int imx94_ierb_switch_init(struct netc_blk_ctrl *priv, + struct device_node *np, + u32 phy_mask) +{ + struct device_node *ports; + u32 port_id; + int err = 0; + + ports = of_get_child_by_name(np, "ethernet-ports"); + if (!ports) + return -ENODEV; + + for_each_available_child_of_node_scoped(ports, child) { + err = of_property_read_u32(child, "reg", &port_id); + if (err) + goto end; + + err = imx94_switch_mdio_phyaddr_config(priv, child, + port_id, phy_mask); + if (err) + goto end; + } + +end: + of_node_put(ports); + + return err; +} + static int imx94_ierb_init(struct platform_device *pdev) { struct netc_blk_ctrl *priv = platform_get_drvdata(pdev); @@ -625,17 +765,18 @@ static int imx94_ierb_init(struct platform_device *pdev) for_each_child_of_node_scoped(np, child) { for_each_child_of_node_scoped(child, gchild) { - if (!of_device_is_compatible(gchild, "pci1131,e101")) - continue; - - err = imx94_enetc_update_tid(priv, gchild); - if (err) - return err; - - err = imx94_enetc_mdio_phyaddr_config(priv, gchild, - phy_mask); - if (err) - return err; + if (of_device_is_compatible(gchild, "pci1131,e101")) { + err = imx94_ierb_enetc_init(priv, gchild, + phy_mask); + if (err) + return err; + } else if (of_device_is_compatible(gchild, + "pci1131,eef2")) { + err = imx94_ierb_switch_init(priv, gchild, + phy_mask); + if (err) + return err; + } } }