mbox series

[v2,0/5] ARM: Add GXP UMAC Support

Message ID 20230802201824.3683-1-nick.hawkins@hpe.com
Headers show
Series ARM: Add GXP UMAC Support | expand

Message

Hawkins, Nick Aug. 2, 2023, 8:18 p.m. UTC
From: Nick Hawkins <nick.hawkins@hpe.com>

The GXP contains two Ethernet MACs that can be
connected externally to several physical devices. From an external
interface perspective the BMC provides two SERDES interface connections
capable of either SGMII or 1000Base-X operation. The BMC also provides
a RMII interface for sideband connections to external Ethernet controllers.

The primary MAC (umac0) can be mapped to either SGMII/1000-BaseX
SERDES interface.  The secondary MAC (umac1) can be mapped to only
the second SGMII/1000-Base X Serdes interface or it can be mapped for
RMII sideband.

The MDIO(mdio0) interface from the primary MAC (umac0) is used for
external PHY status and configuration. The MDIO(mdio1) interface from
the secondary MAC (umac1) is routed to the SGMII/100Base-X IP blocks
on the two SERDES interface connections. In most cases the internal
phy connects directly to the external phy.

---

Changes since v1:
 *Corrected improper descriptions and use of | in yaml files
 *Used reverse christmas tree format for network drivers
 *Moved gxp-umac-mdio.c to /mdio/
 *Fixed dependencies on both Kconfigs
 *Added COMPILE_TEST to both Kconfigs
 *Used devm_ functions where possible in both drivers
 *Moved mac-address to inside of port in yaml files
 *Exchanged listing individual yaml files for hpe,gxp*
 *Restricted use of le32

Nick Hawkins (5):
  dt-bindings: net: Add HPE GXP UMAC MDIO
  net: hpe: Add GXP UMAC MDIO
  dt-bindings: net: Add HPE GXP UMAC
  net: hpe: Add GXP UMAC Driver
  MAINTAINERS: HPE: Add GXP UMAC Networking Files

 .../bindings/net/hpe,gxp-umac-mdio.yaml       |  50 +
 .../devicetree/bindings/net/hpe,gxp-umac.yaml | 112 +++
 MAINTAINERS                                   |   2 +
 drivers/net/ethernet/Kconfig                  |   1 +
 drivers/net/ethernet/Makefile                 |   1 +
 drivers/net/ethernet/hpe/Kconfig              |  32 +
 drivers/net/ethernet/hpe/Makefile             |   1 +
 drivers/net/ethernet/hpe/gxp-umac.c           | 889 ++++++++++++++++++
 drivers/net/ethernet/hpe/gxp-umac.h           |  89 ++
 drivers/net/mdio/Kconfig                      |  13 +
 drivers/net/mdio/Makefile                     |   1 +
 drivers/net/mdio/mdio-gxp-umac.c              | 142 +++
 12 files changed, 1333 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/net/hpe,gxp-umac-mdio.yaml
 create mode 100644 Documentation/devicetree/bindings/net/hpe,gxp-umac.yaml
 create mode 100644 drivers/net/ethernet/hpe/Kconfig
 create mode 100644 drivers/net/ethernet/hpe/Makefile
 create mode 100644 drivers/net/ethernet/hpe/gxp-umac.c
 create mode 100644 drivers/net/ethernet/hpe/gxp-umac.h
 create mode 100644 drivers/net/mdio/mdio-gxp-umac.c

Comments

Andrew Lunn Aug. 2, 2023, 10:39 p.m. UTC | #1
> +config GXP_UMAC_MDIO
> +	tristate "GXP UMAC mdio support"
> +	depends on ARCH_HPE || COMPILE_TEST
> +	depends on OF_MDIO && HAS_IOMEM
> +	depends on MDIO_DEVRES
> +        help

nitpick: help should be indented same as depends.

> +	  Say y here to support the GXP UMAC MDIO bus. The
> +	  MDIO(mdio0) interface from the primary MAC (umac0)
> +	  is used for external PHY status and configuration.

Is it an external MDIO bus? So anything could be connected to it,
e.g. an Ethernet switch. 

> +	  The MDIO(mdio1) interface from the secondary MAC
> +	  (umac1) is routed to the SGMII/100Base-X IP blocks
> +	  on the two SERDES interface connections.

Is this one purely internal? If so, then the text is valid. If it also
goes external, there is no reason you could not put a PHY or an
Ethernet switch on it. You can then skip using the first MDIO bus all
together.

> +#define UMAC_MII                0x00  /* R/W MII Register */
> +#define UMAC_MII_PHY_ADDR_MASK  0x001F0000
> +#define UMAC_MII_PHY_ADDR_SHIFT 16
> +#define UMAC_MII_MOWNER         0x00000200
> +#define UMAC_MII_MRNW           0x00000100

Are these two bits? If so, please use BIT().

> +static int umac_mdio_read(struct mii_bus *bus, int phy_id, int reg)
> +{
> +	struct umac_mdio_priv *umac_mdio = bus->priv;
> +	unsigned int status;
> +	unsigned int value;
> +	int ret;
> +
> +	status = __raw_readl(umac_mdio->base + UMAC_MII);
> +
> +	status &= ~(UMAC_MII_PHY_ADDR_MASK | UMAC_MII_REG_ADDR_MASK);
> +	status |= ((phy_id << UMAC_MII_PHY_ADDR_SHIFT) &
> +			UMAC_MII_PHY_ADDR_MASK);
> +	status |= (reg & UMAC_MII_REG_ADDR_MASK);
> +	status |= UMAC_MII_MRNW; /* set bit for read mode */
> +	__raw_writel(status, umac_mdio->base + UMAC_MII);
> +
> +	status |= UMAC_MII_MOWNER; /* set bit to activate mii transfer */
> +	__raw_writel(status, umac_mdio->base + UMAC_MII);

I assume UMAC_MII_MOWNER must be set in a separate operation? But
using __raw_writel() i'm not sure there is any barrier between the two
writes.

	Andrew
Randy Dunlap Aug. 2, 2023, 10:42 p.m. UTC | #2
Hi Nick,

On 8/2/23 13:18, nick.hawkins@hpe.com wrote:
> diff --git a/drivers/net/mdio/Kconfig b/drivers/net/mdio/Kconfig
> index 9ff2e6f22f3f..58e054bff786 100644
> --- a/drivers/net/mdio/Kconfig
> +++ b/drivers/net/mdio/Kconfig
> @@ -115,6 +115,19 @@ config MDIO_GPIO
>  	  To compile this driver as a module, choose M here: the module
>  	  will be called mdio-gpio.
>  
> +config GXP_UMAC_MDIO
> +	tristate "GXP UMAC mdio support"
> +	depends on ARCH_HPE || COMPILE_TEST
> +	depends on OF_MDIO && HAS_IOMEM
> +	depends on MDIO_DEVRES
> +        help

Indent line above with one tab only.

> +	  Say y here to support the GXP UMAC MDIO bus. The
> +	  MDIO(mdio0) interface from the primary MAC (umac0)

Consistent spacing:
	  MDIO (mdio0)

> +	  is used for external PHY status and configuration.
> +	  The MDIO(mdio1) interface from the secondary MAC

	      MDIO (mdio1)

> +	  (umac1) is routed to the SGMII/100Base-X IP blocks
> +	  on the two SERDES interface connections.

thanks.
Andrew Lunn Aug. 2, 2023, 11:20 p.m. UTC | #3
> The MDIO(mdio0) interface from the primary MAC (umac0) is used for
> external PHY status and configuration.

This is not necessarily true. Linux does not care where the PHYs are,
they could be on a bit-banging bus, or if mdio1 also has external
pins, on there. Or there might not be any PHYs are all, because the
MAC is connected to an Ethernet switch etc.

The reference design is just a guide, the hardware designer is free to
do something else.

> diff --git a/drivers/net/ethernet/hpe/Kconfig b/drivers/net/ethernet/hpe/Kconfig
> new file mode 100644
> index 000000000000..c04aa22ce02f
> --- /dev/null
> +++ b/drivers/net/ethernet/hpe/Kconfig
> @@ -0,0 +1,32 @@
> +config NET_VENDOR_HPE
> +	bool "HPE device"
> +	default y
> +	depends on ARCH_HPE
> +	help
> +	  Say y here to support the HPE network devices.
> +	  The GXP contains two Ethernet MACs that can be
> +	  connected externally to several physical devices.
> +	  From an external interface perspective the BMC
> +	  provides two SERDES interface connections capable
> +	  of either SGMII or 1000Base-X operation. The BMC
> +	  also provides a RMII interface for sideband
> +	  connections to external Ethernet controllers.
> +
> +if NET_VENDOR_HPE
> +
> +config GXP_UMAC
> +	tristate "GXP UMAC support"
> +	depends on ARCH_HPE
> +	select CRC32
> +	select MII
> +	select PHYLIB
> +	select GXP_UMAC_MDIO
> +	help
> +	  Say y here to support the GXP UMACs interface. The
> +	  primary MAC (umac0) can be mapped to either
> +	  SGMII/1000-BaseX SERDES interface. The secondary MAC
> +	  (umac1) can be mapped to only the second
> +	  SGMII/1000-Base X Serdes interface or it can be
> +	  mapped for RMII sideband.

You also want to be able to build this driver with compile testing,
same as the MDIO driver.

> +#include <linux/dma-mapping.h>
> +#include <linux/etherdevice.h>
> +#include <linux/ethtool.h>
> +#include <linux/iopoll.h>
> +#include <linux/module.h>
> +#include <net/ncsi.h>
> +#include <linux/of_device.h>
> +#include <linux/of_mdio.h>
> +#include <linux/of_net.h>
> +#include <linux/phy.h>
> +#include "gxp-umac.h"
> +
> +#define PHY_88E1514_COPPER_CONTROL_REG		0
> +#define PHY_88E1514_PAGE_ADDRESS		22
> +
> +#define PHY_88E1514_GENERAL_CONTROL_REG1	20

Didn't i comment last time, that the MAC driver should never touch PHY
registers? 

> +
> +#define DRV_MODULE_NAME		"gxp-umac"
> +#define DRV_MODULE_VERSION	"0.1"

Versions are pointless. Please Remove it.

> +#define NUMBER_OF_PORTS 2
> +#define EXTERNAL_PORT 1
> +#define INTERNAL_PORT 0
> +
> +struct umac_priv {
> +	void __iomem *base;
> +	int irq;
> +	struct platform_device *pdev;
> +	struct umac_tx_descs *tx_descs;
> +	struct umac_rx_descs *rx_descs;
> +	dma_addr_t tx_descs_dma_addr;
> +	dma_addr_t rx_descs_dma_addr;
> +	unsigned int tx_cur;
> +	unsigned int tx_done;
> +	unsigned int rx_cur;
> +	struct napi_struct napi;
> +	struct net_device *ndev;
> +	struct phy_device *phy_dev;
> +	struct phy_device *int_phy_dev;
> +	struct ncsi_dev *ncsidev;
> +	bool use_ncsi;
> +};
> +
> +static void umac_get_drvinfo(struct net_device *ndev,
> +			     struct ethtool_drvinfo *info)
> +{
> +	strscpy(info->driver, DRV_MODULE_NAME, sizeof(info->driver));
> +	strscpy(info->version, DRV_MODULE_VERSION, sizeof(info->version));

Please drop this. The kernel will then fill version with the actual
kernel version, and i think git hash. That it useful, unlike your
"0.1".

> +static int umac_get_link_ksettings(struct net_device *ndev,
> +				   struct ethtool_link_ksettings *cmd)
> +{
> +	phy_ethtool_ksettings_get(ndev->phydev, cmd);

return what phy_ethtool_ksettings_get returns. Also, please use
phy_ethtool_get_link_ksettings().

> +	return phy_ethtool_ksettings_set(ndev->phydev, cmd);

phy_ethtool_set_link_ksettings(). Please look at what helpers are
available, and use them.

> +static u32 umac_get_link(struct net_device *ndev)
> +{
> +	int err;
> +
> +	err = genphy_update_link(ndev->phydev);
> +	if (err)
> +		return ethtool_op_get_link(ndev);
> +
> +	return ndev->phydev->link;
> +}

Should not be needed. 

> +static int umac_int_phy_init(struct umac_priv *umac)
> +{
> +	struct phy_device *phy_dev = umac->int_phy_dev;
> +	unsigned int value;
> +
> +	value = phy_read(phy_dev, 0);
> +	if (value & 0x4000)
> +		pr_info("Internal PHY loopback is enabled - clearing\n");

How is the PHY getting into loopback mode? The MAC driver should never
touch the PHY, because you have no idea what the PHY actually is,
unless it is internal. And i doubt you have licensed this PHY from
Marvell to make it internal.

> +static int umac_phy_fixup(struct phy_device *phy_dev)
> +{
> +	unsigned int value;
> +
> +	/* set phy mode to SGMII to copper */
> +	/* set page to 18 by writing 18 to register 22 */
> +	phy_write(phy_dev, PHY_88E1514_PAGE_ADDRESS, 18);
> +	value = phy_read(phy_dev, PHY_88E1514_GENERAL_CONTROL_REG1);
> +	value &= ~0x07;
> +	value |= 0x01;
> +	phy_write(phy_dev, PHY_88E1514_GENERAL_CONTROL_REG1, value);

The PHY driver should do this, not the MAC. When you connect the MAC
to the PHY, set the correct interface mode.

> +static int umac_init_hw(struct net_device *ndev)
> +{

...

> +		if (ndev->phydev->duplex)
> +			value |= UMAC_CFG_FULL_DUPLEX;
> +		else
> +			value &= ~UMAC_CFG_FULL_DUPLEX;
> +
> +		if (ndev->phydev->speed == SPEED_1000) {
> +			value &= ~UMAC_CFG_TX_CLK_EN;
> +			value |= UMAC_CFG_GTX_CLK_EN;
> +			value |= UMAC_CFG_GIGABIT_MODE;
> +		} else {
> +			value |= UMAC_CFG_TX_CLK_EN;
> +			value &= ~UMAC_CFG_GTX_CLK_EN;
> +			value &= ~UMAC_CFG_GIGABIT_MODE;
> +		}
> +	}

It is only safe to access members of phydev inside the adjust_link
callback. At that point, the members are guaranteed to the consistent.

> +static int umac_open(struct net_device *ndev)
> +{
> +
> +	netdev_info(ndev, "%s is OPENED\n", ndev->name);

Don't spam the log. netdev_dbg(), or nothing.

> +static int umac_init_mac_address(struct net_device *ndev)
> +{
> +	struct umac_priv *umac = netdev_priv(ndev);
> +	struct platform_device *pdev = umac->pdev;
> +	char addr[ETH_ALEN];
> +	int err;
> +
> +	err = of_get_mac_address(pdev->dev.of_node, addr);
> +	if (err)
> +		netdev_err(ndev, "Failed to get address from device-tree: %d\n",
> +			   err);
> +
> +	if (is_valid_ether_addr(addr)) {
> +		dev_addr_set(ndev, addr);
> +		netdev_info(ndev,
> +			    "Read MAC address %pM from DTB\n", ndev->dev_addr);
> +	} else {
> +		eth_hw_addr_random(ndev);
> +		netdev_info(ndev, "Generated random MAC address %pM\n",
> +			    ndev->dev_addr);

of_get_mac_address() should return an error if there is no MAC address
available. If you get this far, the MAC address in DT, or the NVMEM is
invalid. So you probably want to print an error message about the
invalid MAC address and return -EINVAL.

> +static void umac_adjust_link(struct net_device *ndev)
> +{
> +	struct umac_priv *umac = netdev_priv(ndev);
> +	int value;
> +
> +	if (ndev->phydev->link) {
> +		/* disable both clock */
> +		value = readl(umac->base + UMAC_CONFIG_STATUS);
> +		value &= 0xfffff9ff;
> +		writel(value, umac->base + UMAC_CONFIG_STATUS);
> +		udelay(2);
> +
> +		if (ndev->phydev->duplex)
> +			value |= UMAC_CFG_FULL_DUPLEX;
> +		else
> +			value &= ~UMAC_CFG_FULL_DUPLEX;
> +
> +		switch (ndev->phydev->speed) {
> +		case SPEED_1000:
> +			value &= ~UMAC_CFG_TX_CLK_EN;
> +			value |= UMAC_CFG_GTX_CLK_EN;
> +			value |= UMAC_CFG_GIGABIT_MODE;
> +			break;
> +		case SPEED_100:
> +			value |= UMAC_CFG_TX_CLK_EN;
> +			value &= ~UMAC_CFG_GTX_CLK_EN;
> +			value &= ~UMAC_CFG_GIGABIT_MODE;
> +			break;
> +		}

What about SPEED_10? value will be random from whatever is on the
stack, and you write it to UMAC_CONFIG_STATUS.

> +		/* update duplex and gigabit_mode to umac */
> +		writel(value, umac->base + UMAC_CONFIG_STATUS);
> +		udelay(2);
> +
> +		netif_carrier_on(ndev);

Should not be needed. phylib will do it for you.

> +		netif_carrier_off(ndev);

phylib will also do this.

> +static int umac_setup_phy(struct net_device *ndev)
> +{

...

> +				/* If the specified phy-handle has a fixed-link declaration, use the
> +				 * fixed-link properties to set the configuration for the PHY
> +				 */

This is wrong. Look at other drivers using fixed link.

     Andrew
Simon Horman Aug. 3, 2023, 11:39 a.m. UTC | #4
On Wed, Aug 02, 2023 at 03:18:21PM -0500, nick.hawkins@hpe.com wrote:
> From: Nick Hawkins <nick.hawkins@hpe.com>
> 
> The GXP contains two Universal Ethernet MACs that can be
> connected externally to several physical devices. From an external
> interface perspective the BMC provides two SERDES interface connections
> capable of either SGMII or 1000Base-X operation. The BMC also provides
> a RMII interface for sideband connections to external Ethernet controllers.
> 
> The primary MAC (umac0) can be mapped to either SGMII/1000-BaseX
> SERDES interface.  The secondary MAC (umac1) can be mapped to only
> the second SGMII/1000-Base X Serdes interface or it can be mapped for
> RMII sideband.
> 
> The MDIO(mdio0) interface from the primary MAC (umac0) is used for
> external PHY status and configuration. The MDIO(mdio1) interface from
> the secondary MAC (umac1) is routed to the SGMII/100Base-X IP blocks
> on the two SERDES interface connections.
> 
> Signed-off-by: Nick Hawkins <nick.hawkins@hpe.com>

...

> diff --git a/drivers/net/ethernet/Kconfig b/drivers/net/ethernet/Kconfig
> index 5a274b99f299..b4921b84be51 100644
> --- a/drivers/net/ethernet/Kconfig
> +++ b/drivers/net/ethernet/Kconfig
> @@ -80,6 +80,7 @@ source "drivers/net/ethernet/fujitsu/Kconfig"
>  source "drivers/net/ethernet/fungible/Kconfig"
>  source "drivers/net/ethernet/google/Kconfig"
>  source "drivers/net/ethernet/hisilicon/Kconfig"
> +source "drivers/net/ethernet/hpe/Kconfig"
>  source "drivers/net/ethernet/huawei/Kconfig"
>  source "drivers/net/ethernet/i825xx/Kconfig"
>  source "drivers/net/ethernet/ibm/Kconfig"

Hi Nick,

I think this hunk belongs in [PATCH v2 4/5] net: hpe: Add GXP UMAC Driver.
As it is that patch where drivers/net/ethernet/hpe/Kconfig is added.
And as things stands, the above caused a build failure.
Hawkins, Nick Aug. 4, 2023, 8:55 p.m. UTC | #5
Greetings Andrew,

For some reason I do not see your replies for v1 of this patch or
the mdio driver on lore.kernel. Apologies as I did not intend to
not address your previous review comments. My mistake.

>> +static int umac_int_phy_init(struct umac_priv *umac)
>> +{
>> + struct phy_device *phy_dev = umac->int_phy_dev;
>> + unsigned int value;
>> +
>> + value = phy_read(phy_dev, 0);
>> + if (value & 0x4000)
>> + pr_info("Internal PHY loopback is enabled - clearing\n");

> How is the PHY getting into loopback mode? The MAC driver should never
> touch the PHY, because you have no idea what the PHY actually is,
> unless it is internal. 

It would only be in loopback mode if it was previously configured
that way. I will remove it. The PHY is internal to the ASIC 
and is always the same. Given that information is it acceptable
to configure it here? If not where would be more appropriate?

To help picture the network hardware layout I have uploaded
an image to the hpe github wiki.
https://github.com/HewlettPackard/gxp-linux/wiki/Information-about-the-BMC

. . .

>> +static int umac_phy_fixup(struct phy_device *phy_dev)
>> +{
>> + unsigned int value;
>> +
>> + /* set phy mode to SGMII to copper */
>> + /* set page to 18 by writing 18 to register 22 */
>> + phy_write(phy_dev, PHY_88E1514_PAGE_ADDRESS, 18);
>> + value = phy_read(phy_dev, PHY_88E1514_GENERAL_CONTROL_REG1);
>> + value &= ~0x07;
>> + value |= 0x01;
>> + phy_write(phy_dev, PHY_88E1514_GENERAL_CONTROL_REG1, value);

> The PHY driver should do this, not the MAC. When you connect the MAC
> to the PHY, set the correct interface mode.

Is there a particular function I should be using to achieve this when I
connect to the PHY?

Thanks,

-Nick Hawkins
Andrew Lunn Aug. 5, 2023, 6:45 a.m. UTC | #6
On Fri, Aug 04, 2023 at 08:55:58PM +0000, Hawkins, Nick wrote:
> Greetings Andrew,
> 
> For some reason I do not see your replies for v1 of this patch or
> the mdio driver on lore.kernel. Apologies as I did not intend to
> not address your previous review comments. My mistake.
> 
> >> +static int umac_int_phy_init(struct umac_priv *umac)
> >> +{
> >> + struct phy_device *phy_dev = umac->int_phy_dev;
> >> + unsigned int value;
> >> +
> >> + value = phy_read(phy_dev, 0);
> >> + if (value & 0x4000)
> >> + pr_info("Internal PHY loopback is enabled - clearing\n");
> 
> > How is the PHY getting into loopback mode? The MAC driver should never
> > touch the PHY, because you have no idea what the PHY actually is,
> > unless it is internal. 
> 
> It would only be in loopback mode if it was previously configured
> that way. I will remove it. The PHY is internal to the ASIC 
> and is always the same. Given that information is it acceptable

Hi Nick

So what you call a PHY is probably a PCS. Please look at the API used
in driver/net/pcs/. The real PHYs are external.

Given that this is a BMC, you probably have lots of i2c busses. So you
can support an SFP on the SERDES. So it would be better if you used
the phylink interface, not phylib. This should also solve your
interface mode switching.

    Andrew
Hawkins, Nick Aug. 8, 2023, 8:39 p.m. UTC | #7
Hi Andrew,

Thank you for your feedback, I have several follow up questions:

> So what you call a PHY is probably a PCS. Please look at the API used
> in driver/net/pcs/. The real PHYs are external.

I doubled checked the internal PHY is considered a PHY, but I believe
I can represent it as a PCS. To confirm: I believe you are suggesting that
we create a driver here for the handling of the internal PHY but not the
external PHY?

If so would it be leveraging the MDIO driver already created in this
patchset? Or perhaps would it be replacing it for the internal PHY?


Thanks for the assistance,

-Nick Hawkins
Andrew Lunn Aug. 8, 2023, 8:54 p.m. UTC | #8
On Tue, Aug 08, 2023 at 08:39:39PM +0000, Hawkins, Nick wrote:
> Hi Andrew,
> 
> Thank you for your feedback, I have several follow up questions:
> 
> > So what you call a PHY is probably a PCS. Please look at the API used
> > in driver/net/pcs/. The real PHYs are external.
> 
> I doubled checked the internal PHY is considered a PHY, but I believe
> I can represent it as a PCS.

Is there proper documentation somewhere? register set? Is there
registers to kick off Base1000X/SGMII auto-neg? Somewhere to get the
results of the auto-neg? Since this is Base1000X/SGMII you want to
know if the link between it and the external PHY has established. And
if there is not an external PHY, but an SFP, this auto neg is with the
link peer, not the PHY. If it follows 802.3 clause 37, there should
already be a lot of helper code for you. Is this is licensed core?

> To confirm: I believe you are suggesting that
> we create a driver here for the handling of the internal PHY but not the
> external PHY?

The patches seem to suggest your board has an external Marvell PHY. So
i would expect it to use drivers/net/phy/marvell.c. Other boards,
using other PHYs, would use other PHY drivers.

> If so would it be leveraging the MDIO driver already created in this
> patchset? Or perhaps would it be replacing it for the internal PHY?

Many of the PCS drivers in driver/net/pcs are MDIO devices, so yes, it
would be layered on top of it.

      Andrew
Hawkins, Nick Aug. 9, 2023, 11:55 p.m. UTC | #9
> > > So what you call a PHY is probably a PCS. Please look at the API used
> > > in driver/net/pcs/. The real PHYs are external.
> > 
> > I doubled checked the internal PHY is considered a PHY, but I believe
> > I can represent it as a PCS.

Hi Andrew,

Thank you for the additional information.

> Is there proper documentation somewhere? register set? Is there
> registers to kick off Base1000X/SGMII auto-neg? Somewhere to get the
> results of the auto-neg? Since this is Base1000X/SGMII you want to
> know if the link between it and the external PHY has established. And
> if there is not an external PHY, but an SFP, this auto neg is with the
> link peer, not the PHY. If it follows 802.3 clause 37, there should
> already be a lot of helper code for you. Is this is licensed core?

After discussing with the ASIC team:
The vendor IP in our ASIC performs a parallel GMII to serial SGMII
translation, including clock recovery and framing. It implements
the entire IEEE 802.3z PCS and is managed with a MCD/MDIO
serial interface. The SGMII interface connects to an external PHY
(and the external PHY is managed with a separate MDIO interface).

Thanks,

-Nick Hawkins
Andrew Lunn Aug. 10, 2023, 1:54 a.m. UTC | #10
> After discussing with the ASIC team:
> The vendor IP in our ASIC performs a parallel GMII to serial SGMII

Which vendor ? Is it the Synopsys DesignWare XPCS? If so, take a look
at drivers/net/pcs/pcs-xpcs.c. You will want to use that code.

If it is a different vendor, you can probably still use bits of that
code to implement a driver for the vendor IP.

	Andrew
Hawkins, Nick Aug. 16, 2023, 12:55 a.m. UTC | #11
Greetings Andrew,

I have a follow up question below:

>> +static int umac_mdio_read(struct mii_bus *bus, int phy_id, int reg)
>> +{
>> + struct umac_mdio_priv *umac_mdio = bus->priv;
>> + unsigned int status;
>> + unsigned int value;
>> + int ret;
>> +
>> + status = __raw_readl(umac_mdio->base + UMAC_MII);
>> +
>> + status &= ~(UMAC_MII_PHY_ADDR_MASK | UMAC_MII_REG_ADDR_MASK);
>> + status |= ((phy_id << UMAC_MII_PHY_ADDR_SHIFT) &
>> + UMAC_MII_PHY_ADDR_MASK);
>> + status |= (reg & UMAC_MII_REG_ADDR_MASK);
>> + status |= UMAC_MII_MRNW; /* set bit for read mode */
>> + __raw_writel(status, umac_mdio->base + UMAC_MII);
>> +
>> + status |= UMAC_MII_MOWNER; /* set bit to activate mii transfer */
>> + __raw_writel(status, umac_mdio->base + UMAC_MII);


> I assume UMAC_MII_MOWNER must be set in a separate operation? But
> using __raw_writel() i'm not sure there is any barrier between the two
> writes.

Is there a function you would recommend using instead?

Thank you for the assistance,

-Nick Hawkins
Andrew Lunn Aug. 16, 2023, 1:29 a.m. UTC | #12
On Wed, Aug 16, 2023 at 12:55:56AM +0000, Hawkins, Nick wrote:
> Greetings Andrew,
> 
> I have a follow up question below:
> 
> >> +static int umac_mdio_read(struct mii_bus *bus, int phy_id, int reg)
> >> +{
> >> + struct umac_mdio_priv *umac_mdio = bus->priv;
> >> + unsigned int status;
> >> + unsigned int value;
> >> + int ret;
> >> +
> >> + status = __raw_readl(umac_mdio->base + UMAC_MII);
> >> +
> >> + status &= ~(UMAC_MII_PHY_ADDR_MASK | UMAC_MII_REG_ADDR_MASK);
> >> + status |= ((phy_id << UMAC_MII_PHY_ADDR_SHIFT) &
> >> + UMAC_MII_PHY_ADDR_MASK);
> >> + status |= (reg & UMAC_MII_REG_ADDR_MASK);
> >> + status |= UMAC_MII_MRNW; /* set bit for read mode */
> >> + __raw_writel(status, umac_mdio->base + UMAC_MII);
> >> +
> >> + status |= UMAC_MII_MOWNER; /* set bit to activate mii transfer */
> >> + __raw_writel(status, umac_mdio->base + UMAC_MII);
> 
> 
> > I assume UMAC_MII_MOWNER must be set in a separate operation? But
> > using __raw_writel() i'm not sure there is any barrier between the two
> > writes.
> 
> Is there a function you would recommend using instead?

writel().

In general, it is best to use writel()/readl() for correctness. In the
hot path, dealing with actually Ethernet frames where every uS counts,
you can then think about using writel_relaxed()/readl_relaxed(). But
for something slow like an MDIO bus driver, i would always avoid the
possibility of having hard to find bugs because of missing barriers.

	Andrew