From: Aziz Sellami When configuring the PHY as port EMDIO (by putting an mdio node as a child of the enetc port) the ierb needs to be configured with the right phy address. And since the configuration is harmless for the central EMDIO mode (current default behavior for i.MX 95 EVKs), put the configuration there anyway for code simplicity. Signed-off-by: Aziz Sellami Signed-off-by: Wei Fang --- .../ethernet/freescale/enetc/netc_blk_ctrl.c | 57 ++++++++++++++++++- 1 file changed, 56 insertions(+), 1 deletion(-) diff --git a/drivers/net/ethernet/freescale/enetc/netc_blk_ctrl.c b/drivers/net/ethernet/freescale/enetc/netc_blk_ctrl.c index d7aee3c934d3..1d499276465f 100644 --- a/drivers/net/ethernet/freescale/enetc/netc_blk_ctrl.c +++ b/drivers/net/ethernet/freescale/enetc/netc_blk_ctrl.c @@ -67,6 +67,9 @@ #define IERB_EMDIOFAUXR 0x344 #define IERB_T0FAUXR 0x444 #define IERB_ETBCR(a) (0x300c + 0x100 * (a)) +#define IERB_LBCR(a) (0x1010 + 0x40 * (a)) +#define LBCR_MDIO_PHYAD_PRTAD(addr) (((addr) & 0x1f) << 8) + #define IERB_EFAUXR(a) (0x3044 + 0x100 * (a)) #define IERB_VFAUXR(a) (0x4004 + 0x40 * (a)) #define FAUXR_LDID GENMASK(3, 0) @@ -322,6 +325,58 @@ static int netc_unlock_ierb_with_warm_reset(struct netc_blk_ctrl *priv) 1000, 100000, true, priv->prb, PRB_NETCRR); } +static int imx95_enetc_mdio_phyaddr_config(struct platform_device *pdev) +{ + struct netc_blk_ctrl *priv = platform_get_drvdata(pdev); + struct device_node *np = pdev->dev.of_node; + struct device_node *phy_node; + int bus_devfn, err; + u32 addr; + + /* Update the port EMDIO PHY address through parsing phy properties. + * This is needed when using the port EMDIO but it's harmless when + * using the central EMDIO. So apply it on all cases. + */ + 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; + + bus_devfn = netc_of_pci_get_bus_devfn(gchild); + if (bus_devfn < 0) + return bus_devfn; + + phy_node = of_parse_phandle(gchild, "phy-handle", 0); + if (!phy_node) + continue; + + err = of_property_read_u32(phy_node, "reg", &addr); + of_node_put(phy_node); + if (err) + return err; + + switch (bus_devfn) { + case IMX95_ENETC0_BUS_DEVFN: + netc_reg_write(priv->ierb, IERB_LBCR(0), + LBCR_MDIO_PHYAD_PRTAD(addr)); + break; + case IMX95_ENETC1_BUS_DEVFN: + netc_reg_write(priv->ierb, IERB_LBCR(1), + LBCR_MDIO_PHYAD_PRTAD(addr)); + break; + case IMX95_ENETC2_BUS_DEVFN: + netc_reg_write(priv->ierb, IERB_LBCR(2), + LBCR_MDIO_PHYAD_PRTAD(addr)); + break; + default: + break; + } + } + } + + return 0; +} + static int imx95_ierb_init(struct platform_device *pdev) { struct netc_blk_ctrl *priv = platform_get_drvdata(pdev); @@ -349,7 +404,7 @@ static int imx95_ierb_init(struct platform_device *pdev) /* NETC TIMER */ netc_reg_write(priv->ierb, IERB_T0FAUXR, 7); - return 0; + return imx95_enetc_mdio_phyaddr_config(pdev); } static int imx94_get_enetc_id(struct device_node *np) -- 2.34.1 NETC IP has only one external master MDIO interface (eMDIO) for managing the external PHYs. ENETC can use the interfaces provided by the EMDIO function or its port MDIO to access and manage its external PHY. Both the EMDIO function and the port MDIO are all virtual ports of the eMDIO. The difference is that the EMDIO function is a 'global port', it can access all the PHYs on the eMDIO, but port MDIO can only access its own PHY. To ensure that ENETC can only access its own PHY through port MDIO, LaBCR[MDIO_PHYAD_PRTAD] needs to be set, which represents the address of the external PHY connected to ENETC. If the accessed PHY address is not consistent with LaBCR[MDIO_PHYAD_PRTAD], then the MDIO access initiated by port MDIO will be invalid. Signed-off-by: Wei Fang --- .../ethernet/freescale/enetc/netc_blk_ctrl.c | 74 ++++++++++++++++--- 1 file changed, 64 insertions(+), 10 deletions(-) diff --git a/drivers/net/ethernet/freescale/enetc/netc_blk_ctrl.c b/drivers/net/ethernet/freescale/enetc/netc_blk_ctrl.c index 1d499276465f..4617cbc70f5a 100644 --- a/drivers/net/ethernet/freescale/enetc/netc_blk_ctrl.c +++ b/drivers/net/ethernet/freescale/enetc/netc_blk_ctrl.c @@ -325,13 +325,29 @@ static int netc_unlock_ierb_with_warm_reset(struct netc_blk_ctrl *priv) 1000, 100000, true, priv->prb, PRB_NETCRR); } +static int netc_get_phy_addr(struct device_node *np) +{ + struct device_node *phy_node; + u32 addr; + int err; + + phy_node = of_parse_phandle(np, "phy-handle", 0); + if (!phy_node) + return 0; + + err = of_property_read_u32(phy_node, "reg", &addr); + of_node_put(phy_node); + if (err) + return err; + + return addr; +} + static int imx95_enetc_mdio_phyaddr_config(struct platform_device *pdev) { struct netc_blk_ctrl *priv = platform_get_drvdata(pdev); struct device_node *np = pdev->dev.of_node; - struct device_node *phy_node; - int bus_devfn, err; - u32 addr; + int bus_devfn, addr; /* Update the port EMDIO PHY address through parsing phy properties. * This is needed when using the port EMDIO but it's harmless when @@ -346,14 +362,15 @@ static int imx95_enetc_mdio_phyaddr_config(struct platform_device *pdev) if (bus_devfn < 0) return bus_devfn; - phy_node = of_parse_phandle(gchild, "phy-handle", 0); - if (!phy_node) - continue; + addr = netc_get_phy_addr(gchild); + if (addr < 0) + return addr; - err = of_property_read_u32(phy_node, "reg", &addr); - of_node_put(phy_node); - if (err) - return err; + /* The default value of LaBCR[MDIO_PHYAD_PRTAD ] is + * 0, so no need to set the register. + */ + if (!addr) + continue; switch (bus_devfn) { case IMX95_ENETC0_BUS_DEVFN: @@ -479,6 +496,39 @@ static int imx94_enetc_update_tid(struct netc_blk_ctrl *priv, return 0; } +static int imx94_enetc_mdio_phyaddr_config(struct netc_blk_ctrl *priv, + struct device_node *np) +{ + int bus_devfn, addr; + + bus_devfn = netc_of_pci_get_bus_devfn(np); + if (bus_devfn < 0) + return bus_devfn; + + addr = netc_get_phy_addr(np); + if (addr <= 0) + return addr; + + switch (bus_devfn) { + case IMX94_ENETC0_BUS_DEVFN: + netc_reg_write(priv->ierb, IERB_LBCR(IMX94_ENETC0_LINK), + LBCR_MDIO_PHYAD_PRTAD(addr)); + break; + case IMX94_ENETC1_BUS_DEVFN: + netc_reg_write(priv->ierb, IERB_LBCR(IMX94_ENETC1_LINK), + LBCR_MDIO_PHYAD_PRTAD(addr)); + break; + case IMX94_ENETC2_BUS_DEVFN: + netc_reg_write(priv->ierb, IERB_LBCR(IMX94_ENETC2_LINK), + LBCR_MDIO_PHYAD_PRTAD(addr)); + break; + default: + break; + } + + return 0; +} + static int imx94_ierb_init(struct platform_device *pdev) { struct netc_blk_ctrl *priv = platform_get_drvdata(pdev); @@ -493,6 +543,10 @@ static int imx94_ierb_init(struct platform_device *pdev) err = imx94_enetc_update_tid(priv, gchild); if (err) return err; + + err = imx94_enetc_mdio_phyaddr_config(priv, gchild); + if (err) + return err; } } -- 2.34.1 The NETC IP provides two ways for ENETC to access its external PHY. One is that EMDIO function provides a means for different ENETCs to share a single set of MDIO signals to access their PHYs. And this EMDIO support has been added in the commit a52201fb9caa ("net: enetc: add i.MX95 EMDIO support"). The other one is that each ENETC has a set of MDIO registers to access and control its PHY. When the PHY node is a child node of the ENETC node, the ENETC will use its port MDIO to access its external PHY. For the on-die PHY (PCS/Serdes), each ENETC has an internal MDIO bus for managing the on-die PHY, this internal MDIO bus is controlled by a set of internal MDIO registers of the ENETC port. Because the port MDIO support has been added to the driver for ENETC v1. The difference between ENETC v4 and ENETC v1 is the base address of the MDIO registers, so we only need to add the new base address to add the port MDIO support for ENETC v4. Signed-off-by: Wei Fang --- drivers/net/ethernet/freescale/enetc/enetc4_hw.h | 6 ++++++ .../net/ethernet/freescale/enetc/enetc_pf_common.c | 14 ++++++++++++-- 2 files changed, 18 insertions(+), 2 deletions(-) diff --git a/drivers/net/ethernet/freescale/enetc/enetc4_hw.h b/drivers/net/ethernet/freescale/enetc/enetc4_hw.h index ebea4298791c..3ed0f7a02767 100644 --- a/drivers/net/ethernet/freescale/enetc/enetc4_hw.h +++ b/drivers/net/ethernet/freescale/enetc/enetc4_hw.h @@ -170,6 +170,9 @@ /* Port MAC 0/1 Maximum Frame Length Register */ #define ENETC4_PM_MAXFRM(mac) (0x5014 + (mac) * 0x400) +/* Port internal MDIO base address, use to access PCS */ +#define ENETC4_PM_IMDIO_BASE 0x5030 + /* Port MAC 0/1 Pause Quanta Register */ #define ENETC4_PM_PAUSE_QUANTA(mac) (0x5054 + (mac) * 0x400) @@ -198,6 +201,9 @@ #define SSP_1G 2 #define PM_IF_MODE_ENA BIT(15) +/* Port external MDIO Base address, use to access off-chip PHY */ +#define ENETC4_EMDIO_BASE 0x5c00 + /**********************ENETC Pseudo MAC port registers************************/ /* Port pseudo MAC receive octets counter (64-bit) */ #define ENETC4_PPMROCR 0x5080 diff --git a/drivers/net/ethernet/freescale/enetc/enetc_pf_common.c b/drivers/net/ethernet/freescale/enetc/enetc_pf_common.c index 9c634205e2a7..76263b8566bb 100644 --- a/drivers/net/ethernet/freescale/enetc/enetc_pf_common.c +++ b/drivers/net/ethernet/freescale/enetc/enetc_pf_common.c @@ -176,7 +176,12 @@ static int enetc_mdio_probe(struct enetc_pf *pf, struct device_node *np) bus->parent = dev; mdio_priv = bus->priv; mdio_priv->hw = &pf->si->hw; - mdio_priv->mdio_base = ENETC_EMDIO_BASE; + + if (is_enetc_rev1(pf->si)) + mdio_priv->mdio_base = ENETC_EMDIO_BASE; + else + mdio_priv->mdio_base = ENETC4_EMDIO_BASE; + snprintf(bus->id, MII_BUS_ID_SIZE, "%s", dev_name(dev)); err = of_mdiobus_register(bus, np); @@ -221,7 +226,12 @@ static int enetc_imdio_create(struct enetc_pf *pf) bus->phy_mask = ~0; mdio_priv = bus->priv; mdio_priv->hw = &pf->si->hw; - mdio_priv->mdio_base = ENETC_PM_IMDIO_BASE; + + if (is_enetc_rev1(pf->si)) + mdio_priv->mdio_base = ENETC_PM_IMDIO_BASE; + else + mdio_priv->mdio_base = ENETC4_PM_IMDIO_BASE; + snprintf(bus->id, MII_BUS_ID_SIZE, "%s-imdio", dev_name(dev)); err = mdiobus_register(bus); -- 2.34.1