net: xilinx_axi: add PCS/PMA PHY
authorAndy Chiu <andy.chiu@sifive.com>
Tue, 1 Nov 2022 03:57:59 +0000 (11:57 +0800)
committerMichal Simek <michal.simek@amd.com>
Mon, 5 Dec 2022 07:55:54 +0000 (08:55 +0100)
If we bridge an external PHY to Xilinx's PCS/PMA PHY and would like to
get and set the real status of the PHY facing the external world. Then
we should phy_connect() to the external PHY instead of the PCS/PMA one.
Thus, we add a pcs-handle DT entry, which have been merged in Linux, and
leave the configuration of it to the driver itself.

Unlike Linux, where the PCS/PMA PHY is managed by phylink, managing the
PCS/PMA PHY is only internal to the driver in U-Boot. The PCS/PMA PHY
pressents only when the phy-mode is configured as SGMII or 1000Base-X,
so it is always 1 Gbps and full-duplex and we may skip passing link
information out.

Signed-off-by: Andy Chiu <andy.chiu@sifive.com>
Reviewed-by: Greentime Hu <greentime.hu@sifive.com>
Link: https://lore.kernel.org/r/20221101035800.912644-2-andy.chiu@sifive.com
Signed-off-by: Michal Simek <michal.simek@amd.com>
drivers/net/xilinx_axi_emac.c

index 5f5bc65..5a4d838 100644 (file)
@@ -109,6 +109,7 @@ struct axidma_plat {
        struct eth_pdata eth_pdata;
        struct axidma_reg *dmatx;
        struct axidma_reg *dmarx;
+       int pcsaddr;
        int phyaddr;
        u8 eth_hasnobuf;
        int phy_of_handle;
@@ -119,6 +120,7 @@ struct axidma_plat {
 struct axidma_priv {
        struct axidma_reg *dmatx;
        struct axidma_reg *dmarx;
+       int pcsaddr;
        int phyaddr;
        struct axi_regs *iobase;
        phy_interface_t interface;
@@ -301,6 +303,13 @@ static int axiemac_phy_init(struct udevice *dev)
        if (IS_ENABLED(CONFIG_DM_ETH_PHY))
                priv->phyaddr = eth_phy_get_addr(dev);
 
+       /*
+        * Set address of PCS/PMA PHY to the one pointed by phy-handle for
+        * backward compatibility.
+        */
+       if (priv->phyaddr != -1 && priv->pcsaddr == 0)
+               priv->pcsaddr = priv->phyaddr;
+
        if (priv->phyaddr == -1) {
                /* Detect the PHY address */
                for (i = 31; i >= 0; i--) {
@@ -348,12 +357,12 @@ static int setup_phy(struct udevice *dev)
                 * after DMA and ethernet resets and hence
                 * check and clear if set.
                 */
-               ret = phyread(priv, priv->phyaddr, MII_BMCR, &temp);
+               ret = phyread(priv, priv->pcsaddr, MII_BMCR, &temp);
                if (ret)
                        return 0;
                if (temp & BMCR_ISOLATE) {
                        temp &= ~BMCR_ISOLATE;
-                       ret = phywrite(priv, priv->phyaddr, MII_BMCR, temp);
+                       ret = phywrite(priv, priv->pcsaddr, MII_BMCR, temp);
                        if (ret)
                                return 0;
                }
@@ -784,6 +793,7 @@ static int axi_emac_probe(struct udevice *dev)
 
        if (priv->mactype == EMAC_1G) {
                priv->eth_hasnobuf = plat->eth_hasnobuf;
+               priv->pcsaddr = plat->pcsaddr;
                priv->phyaddr = plat->phyaddr;
                priv->phy_of_handle = plat->phy_of_handle;
                priv->interface = pdata->phy_interface;
@@ -861,6 +871,8 @@ static int axi_emac_of_to_plat(struct udevice *dev)
 
        if (plat->mactype == EMAC_1G) {
                plat->phyaddr = -1;
+               /* PHYAD 0 always redirects to the PCS/PMA PHY */
+               plat->pcsaddr = 0;
 
                offset = fdtdec_lookup_phandle(gd->fdt_blob, node,
                                               "phy-handle");
@@ -878,6 +890,16 @@ static int axi_emac_of_to_plat(struct udevice *dev)
 
                plat->eth_hasnobuf = fdtdec_get_bool(gd->fdt_blob, node,
                                                     "xlnx,eth-hasnobuf");
+
+               if (pdata->phy_interface == PHY_INTERFACE_MODE_SGMII ||
+                   pdata->phy_interface == PHY_INTERFACE_MODE_1000BASEX) {
+                       offset = fdtdec_lookup_phandle(gd->fdt_blob, node,
+                                                      "pcs-handle");
+                       if (offset > 0) {
+                               plat->pcsaddr = fdtdec_get_int(gd->fdt_blob,
+                                                              offset, "reg", -1);
+                       }
+               }
        }
 
        return 0;