Hi Nikita,

On 8/2/22 12:53, Nikita Shubin wrote:
From: Nikita Shubin <n.shu...@yadro.com>

In SGMII/1000BaseX Xilinx AXI Ethernet may also have an
Internal PHY (PCS) in addition to external PHY, in that case
we should also set at least BMCR_ANENABLE.

PCS are not visible until axinet bringup, so init should be done after,
controller is brought up, then we should poll BMSR_ANEGCOMPLETE
prior to polling the external PHY.

The PCS handling relies on "pcs-handle" device tree handle which serves
the similar purpose in Linux device tree.

Signed-off-by: Nikita Shubin <n.shu...@yadro.com>
---
  drivers/net/xilinx_axi_emac.c | 50 +++++++++++++++++++++++++++++++++--
  1 file changed, 48 insertions(+), 2 deletions(-)

diff --git a/drivers/net/xilinx_axi_emac.c b/drivers/net/xilinx_axi_emac.c
index 04277b1269..edbcf6fae3 100644
--- a/drivers/net/xilinx_axi_emac.c
+++ b/drivers/net/xilinx_axi_emac.c
@@ -84,6 +84,9 @@ DECLARE_GLOBAL_DATA_PTR;
  #define DMAALIGN              128
  #define XXV_MIN_PKT_SIZE      60
+/* Xilinx internal PCS PHY ID */
+#define XILINX_PHY_ID                  0x01740c00
+
  static u8 rxframe[PKTSIZE_ALIGN] __attribute((aligned(DMAALIGN)));
  static u8 txminframe[XXV_MIN_PKT_SIZE] __attribute((aligned(DMAALIGN)));
@@ -108,6 +111,7 @@ struct axidma_plat {
        struct axidma_reg *dmatx;
        struct axidma_reg *dmarx;
        int phyaddr;
+       int pcsaddr;
        u8 eth_hasnobuf;
        int phy_of_handle;
        enum emac_variant mactype;
@@ -118,9 +122,11 @@ struct axidma_priv {
        struct axidma_reg *dmatx;
        struct axidma_reg *dmarx;
        int phyaddr;
+       int pcsaddr;
        struct axi_regs *iobase;
        phy_interface_t interface;
        struct phy_device *phydev;
+       struct phy_device *pcsdev;
        struct mii_dev *bus;
        u8 eth_hasnobuf;
        int phy_of_handle;
@@ -285,6 +291,7 @@ static int axiemac_phy_init(struct udevice *dev)
        struct axidma_priv *priv = dev_get_priv(dev);
        struct axi_regs *regs = priv->iobase;
        struct phy_device *phydev;
+       struct phy_device *pcsdev;
u32 supported = SUPPORTED_10baseT_Half |
                        SUPPORTED_10baseT_Full |
@@ -324,6 +331,15 @@ static int axiemac_phy_init(struct udevice *dev)
                priv->phydev->node = offset_to_ofnode(priv->phy_of_handle);
        phy_config(phydev);
+ /* Creating PCS phy here, but it shouldn't be accessed before axiemac_start */
+       if (priv->pcsaddr != -1) {
+               pcsdev = phy_device_create(priv->bus, priv->pcsaddr,
+                                               XILINX_PHY_ID, false);
+               pcsdev->dev = dev;
+               priv->pcsdev = pcsdev;
+               printf("axiemac: registered PCS phy, 0x%x\n", priv->pcsaddr);
+       }
+
        return 0;
  }
@@ -335,6 +351,7 @@ static int setup_phy(struct udevice *dev)
        struct axidma_priv *priv = dev_get_priv(dev);
        struct axi_regs *regs = priv->iobase;
        struct phy_device *phydev = priv->phydev;
+       struct phy_device *pcsdev = priv->pcsdev;
if (priv->interface == PHY_INTERFACE_MODE_SGMII) {
                /*
@@ -353,6 +370,12 @@ static int setup_phy(struct udevice *dev)
                }
        }
+ if (pcsdev && genphy_update_link(pcsdev)) {
+               printf("axiemac: could not initialize PCS %s\n",
+                       pcsdev->dev->name);
+               return 0;
+       }
+
        if (phy_startup(phydev)) {
                printf("axiemac: could not initialize PHY %s\n",
                       phydev->dev->name);
@@ -520,7 +543,9 @@ static void axi_dma_init(struct axidma_priv *priv)
  static int axiemac_start(struct udevice *dev)
  {
        struct axidma_priv *priv = dev_get_priv(dev);
+       struct phy_device *pcsdev = priv->pcsdev;
        u32 temp;
+       int ret;
debug("axiemac: Init started\n");
        /*
@@ -540,6 +565,20 @@ static int axiemac_start(struct udevice *dev)
                        return -1;
        }
+ if (pcsdev) {
+               /* It looks like we need a bit of delay for core to come up
+                * may be we could poll MgtRdy or PhyRstCmplt bit
+                * in 0x00000010, but 1 msec is no a big deal.
+                */
+               udelay(1000);
+               ret = phywrite(priv, pcsdev->addr, MII_BMCR,
+                               BMCR_ANENABLE | BMCR_FULLDPLX | BMCR_SPEED1000);
+               if (ret) {
+                       printf("%s: failed to init PCS\n", __func__);
+                       return -1;
+               }
+       }
+
        /* Disable all RX interrupts before RxBD space setup */
        temp = readl(&priv->dmarx->control);
        temp &= ~XAXIDMA_IRQ_ALL_MASK;
@@ -781,6 +820,7 @@ static int axi_emac_probe(struct udevice *dev)
                priv->phyaddr = plat->phyaddr;
                priv->phy_of_handle = plat->phy_of_handle;
                priv->interface = pdata->phy_interface;
+               priv->pcsaddr = plat->pcsaddr;
if (IS_ENABLED(CONFIG_DM_ETH_PHY))
                        priv->bus = eth_phy_get_mdio_bus(dev);
@@ -802,8 +842,9 @@ static int axi_emac_probe(struct udevice *dev)
                axiemac_phy_init(dev);
        }
- printf("AXI EMAC: %lx, phyaddr %d, interface %s\n", (ulong)pdata->iobase,
-              priv->phyaddr, phy_string_for_interface(pdata->phy_interface));
+       printf("AXI EMAC: %lx, phyaddr %d, pcsaddr %d, interface %s\n",
+              (ulong)pdata->iobase, priv->phyaddr, priv->pcsaddr,
+               phy_string_for_interface(pdata->phy_interface));
return 0;
  }
@@ -836,6 +877,7 @@ static int axi_emac_of_to_plat(struct udevice *dev)
        struct eth_pdata *pdata = &plat->eth_pdata;
        int node = dev_of_offset(dev);
        int offset = 0;
+       struct ofnode_phandle_args phandle_args;
pdata->iobase = dev_read_addr(dev);
        plat->mactype = dev_get_driver_data(dev);
@@ -872,6 +914,10 @@ static int axi_emac_of_to_plat(struct udevice *dev)
plat->eth_hasnobuf = fdtdec_get_bool(gd->fdt_blob, node,
                                                     "xlnx,eth-hasnobuf");
+               if (!dev_read_phandle_with_args(dev, "pcs-handle", NULL,
+                                               0, 0, &phandle_args))
+                       plat->pcsaddr = 
ofnode_read_u32_default(phandle_args.node,
+                                                               "reg", -1);
        }
return 0;

My collegue Swati is out of office right now and I need to get more information about this configuration. That's why please give us some time to get back to you on this.

Thanks,
Michal

Reply via email to