diff mbox series

[RFC,1/1] net: xilinx: handle internal PHY/PCS

Message ID 20220802105308.29945-2-nikita.shubin@maquefel.me
State RFC
Delegated to: Michal Simek
Headers show
Series net: xilinx: handle internal PHY/PCS | expand

Commit Message

Nikita Shubin Aug. 2, 2022, 10:53 a.m. UTC
From: Nikita Shubin <n.shubin@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.shubin@yadro.com>
---
 drivers/net/xilinx_axi_emac.c | 50 +++++++++++++++++++++++++++++++++--
 1 file changed, 48 insertions(+), 2 deletions(-)

Comments

Michal Simek Aug. 11, 2022, 7:45 a.m. UTC | #1
Hi Nikita,

On 8/2/22 12:53, Nikita Shubin wrote:
> From: Nikita Shubin <n.shubin@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.shubin@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
Harini Katakam Aug. 24, 2022, 12:51 p.m. UTC | #2
Hi Nikita,

> -----Original Message-----
> From: Simek, Michal <michal.simek@amd.com>
> Sent: Thursday, August 11, 2022 1:16 PM
> To: Nikita Shubin <nikita.shubin@maquefel.me>; Agarwal, Swati
> <swati.agarwal@amd.com>; Pandey, Radhey Shyam
> <radhey.shyam.pandey@amd.com>
> Cc: linux@yadro.com; Nikita Shubin <n.shubin@yadro.com>; Joe
> Hershberger <joe.hershberger@ni.com>; Ramon Fried
> <rfried.dev@gmail.com>; u-boot@lists.denx.de
> Subject: Re: [RFC PATCH 1/1] net: xilinx: handle internal PHY/PCS
> 
> Hi Nikita,
> 
> On 8/2/22 12:53, Nikita Shubin wrote:
> > From: Nikita Shubin <n.shubin@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.shubin@yadro.com>
> > ---
<snip>
> > @@ -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);

Thanks for the patch.
Could you please confirm that BMCR_ISOLATE is also being handled in
the autonegotiation path? For ex., the equivalent in linux can be found
here:
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/drivers/net/phy/phylink.c#n3050

Could you also please handle the SGMII/1000BaseX standard selection
in the PCS PMA IP? For reference, please see:
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/drivers/net/ethernet/xilinx/xilinx_axienet_main.c#n1619

Regards,
Harini
Nikita Shubin Sept. 2, 2022, 10:25 a.m. UTC | #3
Hello Harini!

Sorry for long response time.

On Wed, 24 Aug 2022 12:51:08 +0000
"Katakam, Harini" <harini.katakam@amd.com> wrote:


> Hi Nikita,
> 
> > -----Original Message-----
> > From: Simek, Michal <michal.simek@amd.com>
> > Sent: Thursday, August 11, 2022 1:16 PM
> > To: Nikita Shubin <nikita.shubin@maquefel.me>; Agarwal, Swati
> > <swati.agarwal@amd.com>; Pandey, Radhey Shyam
> > <radhey.shyam.pandey@amd.com>
> > Cc: linux@yadro.com; Nikita Shubin <n.shubin@yadro.com>; Joe
> > Hershberger <joe.hershberger@ni.com>; Ramon Fried
> > <rfried.dev@gmail.com>; u-boot@lists.denx.de
> > Subject: Re: [RFC PATCH 1/1] net: xilinx: handle internal PHY/PCS
> > 
> > Hi Nikita,
> > 
> > On 8/2/22 12:53, Nikita Shubin wrote:  
> > > From: Nikita Shubin <n.shubin@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.shubin@yadro.com>
> > > ---  
> <snip>
> > > @@ -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);  
> 
> Thanks for the patch.
> Could you please confirm that BMCR_ISOLATE is also being handled in
> the autonegotiation path? For ex., the equivalent in linux can be
> found here:
> https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/drivers/net/phy/phylink.c#n3050

For linux BMCR_ISOLATE is cleared and BMCR_ANENABLE is set (if mode is
MLO_AN_INBAND which is definitely our case) in
phylink_mii_c22_pcs_config. 

In current patch BMCR_ISOLATE is cleared by write, may be indeed
something like:
phyread(priv, pcsdev->addr, MII_BMCR, &val);
val &= ~(BMCR_ISOLATE);
val |= BMCR_ANENABLE | BMCR_FULLDPLX | BMCR_SPEED1000;
phywrite(priv, pcsdev->addr, MII_BMCR, val);

Whould make more sense.

> 
> Could you also please handle the SGMII/1000BaseX standard selection
> in the PCS PMA IP? For reference, please see:
> https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/drivers/net/ethernet/xilinx/xilinx_axienet_main.c#n1619

You mean to handle "xlnx,switch-x-sgmii" device tree property in same
manner as linux does? No problem then.

Yours,
Nikita Shubin.


> 
> Regards,
> Harini
Harini Katakam Sept. 12, 2022, 10:26 a.m. UTC | #4
Hi Nikita,

<snip>
> > > >
> > > > +	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);
> >
> > Thanks for the patch.
> > Could you please confirm that BMCR_ISOLATE is also being handled in
> > the autonegotiation path? For ex., the equivalent in linux can be
> > found here:
> > https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tre
> > e/drivers/net/phy/phylink.c#n3050
> 
> For linux BMCR_ISOLATE is cleared and BMCR_ANENABLE is set (if mode is
> MLO_AN_INBAND which is definitely our case) in
> phylink_mii_c22_pcs_config.
> 
> In current patch BMCR_ISOLATE is cleared by write, may be indeed
> something like:
> phyread(priv, pcsdev->addr, MII_BMCR, &val); val &= ~(BMCR_ISOLATE); val
> |= BMCR_ANENABLE | BMCR_FULLDPLX | BMCR_SPEED1000; phywrite(priv,
> pcsdev->addr, MII_BMCR, val);
> 
> Whould make more sense.

Thanks, yes it is cleared by the explicit value written. That's fine.
The read-modify-write snippet you mentioned above is just good to have.

> 
> >
> > Could you also please handle the SGMII/1000BaseX standard selection in
> > the PCS PMA IP? For reference, please see:
> > https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tre
> > e/drivers/net/ethernet/xilinx/xilinx_axienet_main.c#n1619
> 
> You mean to handle "xlnx,switch-x-sgmii" device tree property in same
> manner as linux does? No problem then.

Yes, thanks.

Regards,
Harini
diff mbox series

Patch

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;