[PATCH 01/14] drivers: net: add Layerscape mEMAC MDIO driver

Ioana Ciornei ioana.ciornei at nxp.com
Wed Mar 18 13:43:34 CET 2020


> Subject: Re: [PATCH 01/14] drivers: net: add Layerscape mEMAC MDIO driver
> 
> On Thu, Mar 12, 2020 at 11:24 AM Ioana Ciornei <ioana.ciornei at nxp.com>
> wrote:
> >
> > Add a driver for the MDIO interface integrated in the mEMAC
> > (Multi-rate Ethernet Media Access Controller) and the Fman 10G Ethernet
> MACs.
> >
> > Signed-off-by: Ioana Ciornei <ioana.ciornei at nxp.com>
> > ---
> >  drivers/net/Kconfig       |   7 ++
> >  drivers/net/Makefile      |   1 +
> >  drivers/net/fsl_ls_mdio.c | 158
> > ++++++++++++++++++++++++++++++++++++++
> >  3 files changed, 166 insertions(+)
> >  create mode 100644 drivers/net/fsl_ls_mdio.c
> >
> > diff --git a/drivers/net/Kconfig b/drivers/net/Kconfig index
> > 4d1013c98466..bc518f218da6 100644
> > --- a/drivers/net/Kconfig
> > +++ b/drivers/net/Kconfig
> > @@ -640,4 +640,11 @@ config MVMDIO
> >
> >           This driver is used by the MVPP2 and MVNETA drivers.
> >
> > +config FSL_LS_MDIO
> > +       bool "NXP Layerscape MDIO interface support"
> > +       depends on DM_MDIO
> > +       help
> > +         This driver supports the MDIO bus found on the Fman 10G Ethernet
> MACs and
> > +         on the mEMAC (which supports both Clauses 22 and 45).
> > +
> >  endif # NETDEVICES
> > diff --git a/drivers/net/Makefile b/drivers/net/Makefile index
> > 6e0a68834d97..6d9b8772b1a5 100644
> > --- a/drivers/net/Makefile
> > +++ b/drivers/net/Makefile
> > @@ -83,3 +83,4 @@ obj-y += mscc_eswitch/
> >  obj-$(CONFIG_HIGMACV300_ETH) += higmacv300.o
> >  obj-$(CONFIG_MDIO_SANDBOX) += mdio_sandbox.o
> >  obj-$(CONFIG_FSL_ENETC) += fsl_enetc.o fsl_enetc_mdio.o
> > +obj-$(CONFIG_FSL_LS_MDIO) += fsl_ls_mdio.o
> > diff --git a/drivers/net/fsl_ls_mdio.c b/drivers/net/fsl_ls_mdio.c new
> > file mode 100644 index 000000000000..845c0ac1ffd8
> > --- /dev/null
> > +++ b/drivers/net/fsl_ls_mdio.c
> > @@ -0,0 +1,158 @@
> > +// SPDX-License-Identifier: GPL-2.0+
> > +/*
> > + * Copyright 2020 NXP
> > + */
> > +
> > +#include <common.h>
> > +#include <dm.h>
> > +#include <errno.h>
> > +#include <miiphy.h>
> > +#include <asm/io.h>
> > +#include <fsl_memac.h>
> > +
> > +#ifdef CONFIG_SYS_MEMAC_LITTLE_ENDIAN
> 
> Is there a reason we have a special separate endian flag for this NIC instead of
> using a platform config?
> 

The same MAC IP (mEMAC) is integrated in both gen 1 DPAA (FMan) and gen 2 DPAA (WRIOP - Wire Rate IO Processor) based SoCs but with different endianness settings.
For example, in DPAA2 it's integrated as little-endian while in DPAA1 based SoCs (even those that are ARM based) it’s integrated as big-endian.

This is why a different config is used and not directly the platform config.

> > +#define memac_out_32(a, v)     out_le32(a, v)
> > +#define memac_clrbits_32(a, v) clrbits_le32(a, v) #define
> > +memac_setbits_32(a, v) setbits_le32(a, v) #else
> > +#define memac_out_32(a, v)     out_be32(a, v)
> > +#define memac_clrbits_32(a, v) clrbits_be32(a, v) #define
> > +memac_setbits_32(a, v) setbits_be32(a, v) #endif
> > +
> > +static u32 memac_in_32(u32 *reg)
> > +{
> > +#ifdef CONFIG_SYS_MEMAC_LITTLE_ENDIAN
> > +       return in_le32(reg);
> > +#else
> > +       return in_be32(reg);
> > +#endif
> > +}
> > +
> > +struct fsl_ls_mdio_priv {
> > +       void *regs_base;
> > +};
> > +
> > +static int dm_fsl_ls_mdio_read(struct udevice *dev, int addr,
> > +                              int devad, int reg) {
> > +       struct fsl_ls_mdio_priv *priv = dev_get_priv(dev);
> > +       struct memac_mdio_controller *regs;
> > +       u32 mdio_ctl;
> > +       u32 c45 = 1;
> > +
> > +       regs = (struct memac_mdio_controller *)(priv->regs_base);
> > +       if (devad == MDIO_DEVAD_NONE) {
> > +               c45 = 0; /* clause 22 */
> > +               devad = reg & 0x1f;
> > +               memac_clrbits_32(&regs->mdio_stat, MDIO_STAT_ENC);
> > +       } else {
> > +               memac_setbits_32(&regs->mdio_stat, MDIO_STAT_ENC);
> > +       }
> > +
> > +       /* Wait till the bus is free */
> > +       while ((memac_in_32(&regs->mdio_stat)) & MDIO_STAT_BSY)
> > +               ;
> > +
> > +       /* Set the Port and Device Addrs */
> > +       mdio_ctl = MDIO_CTL_PORT_ADDR(addr) |
> MDIO_CTL_DEV_ADDR(devad);
> > +       memac_out_32(&regs->mdio_ctl, mdio_ctl);
> > +
> > +       /* Set the register address */
> > +       if (c45)
> > +               memac_out_32(&regs->mdio_addr, reg & 0xffff);
> > +
> > +       /* Wait till the bus is free */
> > +       while ((memac_in_32(&regs->mdio_stat)) & MDIO_STAT_BSY)
> > +               ;
> > +
> > +       /* Initiate the read */
> > +       mdio_ctl |= MDIO_CTL_READ;
> > +       memac_out_32(&regs->mdio_ctl, mdio_ctl);
> > +
> > +       /* Wait till the MDIO write is complete */
> > +       while ((memac_in_32(&regs->mdio_data)) & MDIO_DATA_BSY)
> > +               ;
> > +
> > +       /* Return all Fs if nothing was there */
> > +       if (memac_in_32(&regs->mdio_stat) & MDIO_STAT_RD_ER)
> > +               return 0xffff;
> > +
> > +       return memac_in_32(&regs->mdio_data) & 0xffff;
> > +       return 0;
> > +}
> > +
> > +static int dm_fsl_ls_mdio_write(struct udevice *dev, int addr, int devad,
> > +                               int reg, u16 val) {
> > +       struct fsl_ls_mdio_priv *priv = dev_get_priv(dev);
> > +       struct memac_mdio_controller *regs;
> > +       u32 mdio_ctl;
> > +       u32 c45 = 1;
> > +
> > +       regs = (struct memac_mdio_controller *)(priv->regs_base);
> > +       if (devad == MDIO_DEVAD_NONE) {
> > +               c45 = 0; /* clause 22 */
> > +               devad = reg & 0x1f;
> > +               memac_clrbits_32(&regs->mdio_stat, MDIO_STAT_ENC);
> > +       } else {
> > +               memac_setbits_32(&regs->mdio_stat, MDIO_STAT_ENC);
> > +       }
> > +
> > +       /* Wait till the bus is free */
> > +       while ((memac_in_32(&regs->mdio_stat)) & MDIO_STAT_BSY)
> > +               ;
> > +
> > +       /* Set the port and dev addr */
> > +       mdio_ctl = MDIO_CTL_PORT_ADDR(addr) |
> MDIO_CTL_DEV_ADDR(devad);
> > +       memac_out_32(&regs->mdio_ctl, mdio_ctl);
> > +
> > +       /* Set the register address */
> > +       if (c45)
> > +               memac_out_32(&regs->mdio_addr, reg & 0xffff);
> > +
> > +       /* Wait till the bus is free */
> > +       while ((memac_in_32(&regs->mdio_stat)) & MDIO_STAT_BSY)
> > +               ;
> > +
> > +       /* Write the value to the register */
> > +       memac_out_32(&regs->mdio_data, MDIO_DATA(val));
> > +
> > +       /* Wait till the MDIO write is complete */
> > +       while ((memac_in_32(&regs->mdio_data)) & MDIO_DATA_BSY)
> > +               ;
> > +
> > +       return 0;
> > +}
> 
> These two functions are almost identical... It seems you could make a common
> function to reuse most of the access setup.
> 

That's a good idea.  I'll refactor this part in v2.

Thanks,
Ioana

> > +
> > +static const struct mdio_ops fsl_ls_mdio_ops = {
> > +       .read = dm_fsl_ls_mdio_read,
> > +       .write = dm_fsl_ls_mdio_write, };
> > +
> > +static int fsl_ls_mdio_probe(struct udevice *dev) {
> > +       struct fsl_ls_mdio_priv *priv = dev_get_priv(dev);
> > +       struct memac_mdio_controller *regs;
> > +
> > +       priv->regs_base = dev_read_addr_ptr(dev);
> > +       regs = (struct memac_mdio_controller *)(priv->regs_base);
> > +
> > +       memac_setbits_32(&regs->mdio_stat,
> > +                        MDIO_STAT_CLKDIV(258) | MDIO_STAT_NEG);
> > +
> > +       return 0;
> > +}
> > +
> > +static const struct udevice_id fsl_ls_mdio_of_ids[] = {
> > +       { .compatible = "fsl,ls-mdio" }, };
> > +
> > +U_BOOT_DRIVER(fsl_ls_mdio) = {
> > +       .name = "fsl_ls_mdio",
> > +       .id = UCLASS_MDIO,
> > +       .of_match = fsl_ls_mdio_of_ids,
> > +       .probe = fsl_ls_mdio_probe,
> > +       .ops = &fsl_ls_mdio_ops,
> > +       .priv_auto_alloc_size = sizeof(struct fsl_ls_mdio_priv), };
> > --
> > 2.17.1
> >


More information about the U-Boot mailing list