diff mbox series

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

Message ID 20200312160620.29593-2-ioana.ciornei@nxp.com
State New
Headers show
Series net: ldpaa_eth: transition to CONFIG_DM_ETH | expand

Commit Message

Ioana Ciornei March 12, 2020, 4:06 p.m. UTC
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

Comments

Joe Hershberger March 17, 2020, 6:53 p.m. UTC | #1
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?

> +#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.

> +
> +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
>
Priyanka Jain (OSS) March 18, 2020, 11:53 a.m. UTC | #2
>-----Original Message-----
>From: U-Boot <u-boot-bounces at lists.denx.de> On Behalf Of Ioana Ciornei
>Sent: Thursday, March 12, 2020 9:36 PM
>To: Priyanka Jain <priyanka.jain at nxp.com>; joe.hershberger at ni.com; u-
>boot at lists.denx.de
>Cc: Florin Laurentiu Chiculita <florinlaurentiu.chiculita at nxp.com>; Madalin
>Bucur (OSS) <madalin.bucur at oss.nxp.com>; Ioana Ciornei
><ioana.ciornei at nxp.com>
>Subject: [PATCH 01/14] drivers: net: add Layerscape mEMAC MDIO driver
>
>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
Please use prefix NXP for new configs
>+	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
>+#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;
I don't see devad getting used in below code. 
>+		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;
Sane commen as above
>+		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;
>+}
>+
>+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

Regards
Priyanka
Ioana Ciornei March 18, 2020, 12:03 p.m. UTC | #3
> Subject: RE: [PATCH 01/14] drivers: net: add Layerscape mEMAC MDIO driver
> 
> >-----Original Message-----
> >From: U-Boot <u-boot-bounces at lists.denx.de> On Behalf Of Ioana Ciornei
> >Sent: Thursday, March 12, 2020 9:36 PM
> >To: Priyanka Jain <priyanka.jain at nxp.com>; joe.hershberger at ni.com; u-
> >boot at lists.denx.de
> >Cc: Florin Laurentiu Chiculita <florinlaurentiu.chiculita at nxp.com>;
> >Madalin Bucur (OSS) <madalin.bucur at oss.nxp.com>; Ioana Ciornei
> ><ioana.ciornei at nxp.com>
> >Subject: [PATCH 01/14] drivers: net: add Layerscape mEMAC MDIO driver
> >
> >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
> Please use prefix NXP for new configs
> >+	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
> >+#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;
>
> I don't see devad getting used in below code.
>

It it used below in setting up the port and device address.
Below is the exact snippet:

+	mdio_ctl = MDIO_CTL_PORT_ADDR(addr) | MDIO_CTL_DEV_ADDR(devad);
+	memac_out_32(&regs->mdio_ctl, mdio_ctl);

> >+		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;
> Sane commen as above
> >+		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;
> >+}
> >+
> >+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
> 
> Regards
> Priyanka
Ioana Ciornei March 18, 2020, 12:43 p.m. UTC | #4
> 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
> >
Priyanka Jain (OSS) March 18, 2020, 2:08 p.m. UTC | #5
>-----Original Message-----
>From: Ioana Ciornei <ioana.ciornei at nxp.com>
>Sent: Wednesday, March 18, 2020 5:33 PM
>To: Priyanka Jain (OSS) <priyanka.jain at oss.nxp.com>;
>joe.hershberger at ni.com; u-boot at lists.denx.de
>Cc: Florin Laurentiu Chiculita <florinlaurentiu.chiculita at nxp.com>; Madalin
>Bucur (OSS) <madalin.bucur at oss.nxp.com>
>Subject: RE: [PATCH 01/14] drivers: net: add Layerscape mEMAC MDIO driver
>
>> Subject: RE: [PATCH 01/14] drivers: net: add Layerscape mEMAC MDIO
>> driver
>>
>> >-----Original Message-----
>> >From: U-Boot <u-boot-bounces at lists.denx.de> On Behalf Of Ioana
>> >Ciornei
>> >Sent: Thursday, March 12, 2020 9:36 PM
>> >To: Priyanka Jain <priyanka.jain at nxp.com>; joe.hershberger at ni.com; u-
>> >boot at lists.denx.de
>> >Cc: Florin Laurentiu Chiculita <florinlaurentiu.chiculita at nxp.com>;
>> >Madalin Bucur (OSS) <madalin.bucur at oss.nxp.com>; Ioana Ciornei
>> ><ioana.ciornei at nxp.com>
>> >Subject: [PATCH 01/14] drivers: net: add Layerscape mEMAC MDIO driver
>> >
>> >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
>> Please use prefix NXP for new configs
>> >+	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
>> >+#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;
>>
>> I don't see devad getting used in below code.
>>
>
>It it used below in setting up the port and device address.
>Below is the exact snippet:
>
>+	mdio_ctl = MDIO_CTL_PORT_ADDR(addr) |
>MDIO_CTL_DEV_ADDR(devad);
Thanks for pointing.
Priyanka
<snip>
diff mbox series

Patch

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
+#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;
+}
+
+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),
+};