On Thu, Mar 12, 2020 at 11:24 AM Ioana Ciornei <ioana.cior...@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.cior...@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? > +#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(®s->mdio_stat, MDIO_STAT_ENC); > + } else { > + memac_setbits_32(®s->mdio_stat, MDIO_STAT_ENC); > + } > + > + /* Wait till the bus is free */ > + while ((memac_in_32(®s->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(®s->mdio_ctl, mdio_ctl); > + > + /* Set the register address */ > + if (c45) > + memac_out_32(®s->mdio_addr, reg & 0xffff); > + > + /* Wait till the bus is free */ > + while ((memac_in_32(®s->mdio_stat)) & MDIO_STAT_BSY) > + ; > + > + /* Initiate the read */ > + mdio_ctl |= MDIO_CTL_READ; > + memac_out_32(®s->mdio_ctl, mdio_ctl); > + > + /* Wait till the MDIO write is complete */ > + while ((memac_in_32(®s->mdio_data)) & MDIO_DATA_BSY) > + ; > + > + /* Return all Fs if nothing was there */ > + if (memac_in_32(®s->mdio_stat) & MDIO_STAT_RD_ER) > + return 0xffff; > + > + return memac_in_32(®s->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(®s->mdio_stat, MDIO_STAT_ENC); > + } else { > + memac_setbits_32(®s->mdio_stat, MDIO_STAT_ENC); > + } > + > + /* Wait till the bus is free */ > + while ((memac_in_32(®s->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(®s->mdio_ctl, mdio_ctl); > + > + /* Set the register address */ > + if (c45) > + memac_out_32(®s->mdio_addr, reg & 0xffff); > + > + /* Wait till the bus is free */ > + while ((memac_in_32(®s->mdio_stat)) & MDIO_STAT_BSY) > + ; > + > + /* Write the value to the register */ > + memac_out_32(®s->mdio_data, MDIO_DATA(val)); > + > + /* Wait till the MDIO write is complete */ > + while ((memac_in_32(®s->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. > + > +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(®s->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 >