diff mbox

[U-Boot,v3,13/14] drivers: net: Add ethernet driver for Microchip PIC32.

Message ID 1452593909-16184-14-git-send-email-purna.mandal@microchip.com
State Superseded
Delegated to: Daniel Schwierzeck
Headers show

Commit Message

Purna Chandra Mandal Jan. 12, 2016, 10:18 a.m. UTC
This driver implements MAC and MII layer of the ethernet controller.
Network data transfer is handled by controller internal DMA engine.
Ethernet controller is configurable through device-tree file.

Signed-off-by: Purna Chandra Mandal <purna.mandal@microchip.com>


---

Changes in v3:
- merge wrappers with eth operation callbacks
- read phy address from device-tree
- rename functions (e.g. _eth_xyz() with pic32_eth_xyz())

Changes in v2: None

 drivers/net/Kconfig      |   7 +
 drivers/net/Makefile     |   1 +
 drivers/net/pic32_eth.c  | 606 +++++++++++++++++++++++++++++++++++++++++++++++
 drivers/net/pic32_eth.h  | 171 +++++++++++++
 drivers/net/pic32_mdio.c | 121 ++++++++++
 5 files changed, 906 insertions(+)
 create mode 100644 drivers/net/pic32_eth.c
 create mode 100644 drivers/net/pic32_eth.h
 create mode 100644 drivers/net/pic32_mdio.c

Comments

Tom Rini Jan. 13, 2016, 2:56 p.m. UTC | #1
On Tue, Jan 12, 2016 at 03:48:28PM +0530, Purna Chandra Mandal wrote:

> This driver implements MAC and MII layer of the ethernet controller.
> Network data transfer is handled by controller internal DMA engine.
> Ethernet controller is configurable through device-tree file.
> 
> Signed-off-by: Purna Chandra Mandal <purna.mandal@microchip.com>
[snip]
> +/* cache operation helper */
> +#define __dcache_flush(__a, __l) \
> +	flush_dcache_range((ulong)(__a),  ((__l) + (ulong)(__a)))
> +
> +#define __dcache_invalidate(__a, __l) \
> +	invalidate_dcache_range((ulong)(__a),  ((__l) + (ulong)(__a)))

Why using these helper functions instead of directly?  Yes, we may be
casting in some cases and if that's how it must be, so be it (it's how
we're doing it in other drivers).  Thanks!
Daniel Schwierzeck Jan. 13, 2016, 3:37 p.m. UTC | #2
Am Dienstag, den 12.01.2016, 15:48 +0530 schrieb Purna Chandra Mandal:
> This driver implements MAC and MII layer of the ethernet controller.
> Network data transfer is handled by controller internal DMA engine.
> Ethernet controller is configurable through device-tree file.
> 
> Signed-off-by: Purna Chandra Mandal <purna.mandal@microchip.com>
> 
> 
> ---
> 
> Changes in v3:
> - merge wrappers with eth operation callbacks
> - read phy address from device-tree
> - rename functions (e.g. _eth_xyz() with pic32_eth_xyz())
> 
> Changes in v2: None
> 
>  drivers/net/Kconfig      |   7 +
>  drivers/net/Makefile     |   1 +
>  drivers/net/pic32_eth.c  | 606
> +++++++++++++++++++++++++++++++++++++++++++++++
>  drivers/net/pic32_eth.h  | 171 +++++++++++++
>  drivers/net/pic32_mdio.c | 121 ++++++++++
>  5 files changed, 906 insertions(+)
>  create mode 100644 drivers/net/pic32_eth.c
>  create mode 100644 drivers/net/pic32_eth.h
>  create mode 100644 drivers/net/pic32_mdio.c
> 
> diff --git a/drivers/net/Kconfig b/drivers/net/Kconfig
> index ae5e78d..dc49493 100644
> --- a/drivers/net/Kconfig
> +++ b/drivers/net/Kconfig
> @@ -108,4 +108,11 @@ config ZYNQ_GEM
>  	help
>  	  This MAC is present in Xilinx Zynq and ZynqMP SoCs.
>  
> +config PIC32_ETH
> +	bool "Microchip PIC32 Ethernet Support"
> +	depends on MACH_PIC32

should be

depends on DM_ETH && MACH_PIC32
select PHYLIB

> +	help
> +	  This driver implements 10/100 Mbps Ethernet and MAC layer
> for
> +	  Microchip PIC32 microcontrollers.
> +
>  endif # NETDEVICES
> diff --git a/drivers/net/Makefile b/drivers/net/Makefile
> index 150470c..33a81ee 100644
> --- a/drivers/net/Makefile
> +++ b/drivers/net/Makefile
> @@ -72,3 +72,4 @@ obj-$(CONFIG_FSL_MC_ENET) += fsl-mc/
>  obj-$(CONFIG_FSL_MC_ENET) += ldpaa_eth/
>  obj-$(CONFIG_FSL_MEMAC) += fm/memac_phy.o
>  obj-$(CONFIG_VSC9953) += vsc9953.o
> +obj-$(CONFIG_PIC32_ETH) += pic32_mdio.o pic32_eth.o
> diff --git a/drivers/net/pic32_eth.c b/drivers/net/pic32_eth.c
> new file mode 100644
> index 0000000..1cef62e
> --- /dev/null
> +++ b/drivers/net/pic32_eth.c
> @@ -0,0 +1,606 @@
> +/*
> + * (c) 2015 Purna Chandra Mandal <purna.mandal@microchip.com>
> + *
> + * SPDX-License-Identifier:	GPL-2.0+
> + *
> + */
> +#include <common.h>
> +#include <errno.h>
> +#include <dm.h>
> +#include <net.h>
> +#include <miiphy.h>
> +#include <console.h>
> +#include <wait_bit.h>
> +#include <asm/gpio.h>
> +
> +#include "pic32_eth.h"
> +
> +#define MAX_RX_BUF_SIZE		1536
> +#define MAX_RX_DESCR		PKTBUFSRX
> +#define MAX_TX_DESCR		2
> +
> +DECLARE_GLOBAL_DATA_PTR;
> +
> +struct pic32eth_dev {
> +	struct eth_dma_desc rxd_ring[MAX_RX_DESCR];
> +	struct eth_dma_desc txd_ring[MAX_TX_DESCR];
> +	u32 rxd_idx; /* index of RX desc to read */
> +	/* regs */
> +	struct pic32_ectl_regs *ectl_regs;
> +	struct pic32_emac_regs *emac_regs;
> +	/* Phy */
> +	struct phy_device *phydev;
> +	phy_interface_t phyif;
> +	u32 phy_addr;
> +	struct gpio_desc rst_gpio;
> +};
> +
> +void __weak board_netphy_reset(void *dev)
> +{
> +	struct pic32eth_dev *priv = (struct pic32eth_dev *)dev;

the cast is not necessary

> +
> +	if (!dm_gpio_is_valid(&priv->rst_gpio))
> +		return;
> +
> +	/* phy reset */
> +	dm_gpio_set_value(&priv->rst_gpio, 0);
> +	udelay(300);
> +	dm_gpio_set_value(&priv->rst_gpio, 1);
> +	udelay(300);
> +}
> +
> +/* Initialize mii(MDIO) interface, discover which PHY is
> + * attached to the device, and configure it properly.
> + */
> +static int pic32_mii_init(struct pic32eth_dev *priv)
> +{
> +	struct pic32_ectl_regs *ectl_p = priv->ectl_regs;
> +	struct pic32_emac_regs *emac_p = priv->emac_regs;
> +
> +	/* board phy reset */
> +	board_netphy_reset(priv);
> +
> +	/* disable RX, TX & all transactions */
> +	writel(ETHCON_ON | ETHCON_TXRTS | ETHCON_RXEN, &ectl_p
> ->con1.clr);
> +
> +	/* wait till busy */
> +	wait_for_bit(__func__, &ectl_p->stat.raw, ETHSTAT_BUSY,
> false,
> +		     CONFIG_SYS_HZ, false);
> +
> +	/* turn controller ON to access PHY over MII */
> +	writel(ETHCON_ON, &ectl_p->con1.set);
> +
> +	mdelay(10);
> +
> +	/* reset MAC */
> +	writel(EMAC_SOFTRESET, &emac_p->cfg1.set); /* reset assert
> */
> +	mdelay(10);
> +	writel(EMAC_SOFTRESET, &emac_p->cfg1.clr); /* reset deassert
> */
> +
> +	/* initialize MDIO/MII */
> +	if (priv->phyif == PHY_INTERFACE_MODE_RMII) {
> +		writel(EMAC_RMII_RESET, &emac_p->supp.set);
> +		mdelay(10);
> +		writel(EMAC_RMII_RESET, &emac_p->supp.clr);
> +	}
> +
> +	return pic32_mdio_init(PIC32_MDIO_NAME, (ulong)&emac_p
> ->mii);
> +}
> +
> +static int pic32_phy_init(struct pic32eth_dev *priv, struct udevice
> *dev)
> +{
> +	struct mii_dev *mii;
> +
> +	mii = miiphy_get_dev_by_name(PIC32_MDIO_NAME);
> +
> +	/* find & connect PHY */
> +	priv->phydev = phy_connect(mii, priv->phy_addr,
> +				   dev, priv->phyif);
> +	if (!priv->phydev) {
> +		printf("%s: %s: Error, PHY connect\n", __FILE__,
> __func__);
> +		return 0;
> +	}
> +
> +	/* Wait for phy to complete reset */
> +	mdelay(10);
> +
> +	/* configure supported modes */
> +	priv->phydev->supported = SUPPORTED_10baseT_Half |
> +				  SUPPORTED_10baseT_Full |
> +				  SUPPORTED_100baseT_Half |
> +				  SUPPORTED_100baseT_Full |
> +				  SUPPORTED_Autoneg;
> +
> +	priv->phydev->advertising = ADVERTISED_10baseT_Half |
> +				    ADVERTISED_10baseT_Full |
> +				    ADVERTISED_100baseT_Half |
> +				    ADVERTISED_100baseT_Full |
> +				    ADVERTISED_Autoneg;
> +
> +	priv->phydev->autoneg = AUTONEG_ENABLE;
> +
> +	return 0;
> +}
> +
> +/* Configure MAC based on negotiated speed and duplex
> + * reported by PHY.
> + */
> +static int pic32_mac_adjust_link(struct pic32eth_dev *priv)
> +{
> +	struct phy_device *phydev = priv->phydev;
> +	struct pic32_emac_regs *emac_p = priv->emac_regs;
> +
> +	if (!phydev->link) {
> +		printf("%s: No link.\n", phydev->dev->name);
> +		return -EINVAL;
> +	}
> +
> +	if (phydev->duplex) {
> +		writel(EMAC_FULLDUP, &emac_p->cfg2.set);
> +		writel(FULLDUP_GAP_TIME, &emac_p->ipgt.raw);
> +	} else {
> +		writel(EMAC_FULLDUP, &emac_p->cfg2.clr);
> +		writel(HALFDUP_GAP_TIME, &emac_p->ipgt.raw);
> +	}
> +
> +	switch (phydev->speed) {
> +	case SPEED_100:
> +		writel(EMAC_RMII_SPD100, &emac_p->supp.set);
> +		break;
> +	case SPEED_10:
> +		writel(EMAC_RMII_SPD100, &emac_p->supp.clr);
> +		break;
> +	default:
> +		printf("%s: Speed was bad\n", phydev->dev->name);
> +		return -EINVAL;
> +	}
> +
> +	printf("pic32eth: PHY is %s with %dbase%s, %s\n",
> +	       phydev->drv->name, phydev->speed,
> +	       (phydev->port == PORT_TP) ? "T" : "X",
> +	       (phydev->duplex) ? "full" : "half");
> +
> +	return 0;
> +}
> +
> +static void pic32_mac_init(struct pic32eth_dev *priv, u8 *macaddr)
> +{
> +	struct pic32_emac_regs *emac_p = priv->emac_regs;
> +	u32 stat = 0, v;
> +	u64 expire;
> +
> +	v = EMAC_TXPAUSE | EMAC_RXPAUSE | EMAC_RXENABLE;
> +	writel(v, &emac_p->cfg1.raw);
> +
> +	v = EMAC_EXCESS | EMAC_AUTOPAD | EMAC_PADENABLE |
> +	    EMAC_CRCENABLE | EMAC_LENGTHCK | EMAC_FULLDUP;
> +	writel(v, &emac_p->cfg2.raw);
> +
> +	/* recommended back-to-back inter-packet gap for 10 Mbps
> half duplex */
> +	writel(HALFDUP_GAP_TIME, &emac_p->ipgt.raw);
> +
> +	/* recommended non-back-to-back interpacket gap is 0xc12 */
> +	writel(0xc12, &emac_p->ipgr.raw);
> +
> +	/* recommended collision window retry limit is 0x370F */
> +	writel(0x370f, &emac_p->clrt.raw);
> +
> +	/* set maximum frame length: allow VLAN tagged frame */
> +	writel(0x600, &emac_p->maxf.raw);
> +
> +	/* set the mac address */
> +	writel(macaddr[0] | (macaddr[1] << 8), &emac_p->sa2.raw);
> +	writel(macaddr[2] | (macaddr[3] << 8), &emac_p->sa1.raw);
> +	writel(macaddr[4] | (macaddr[5] << 8), &emac_p->sa0.raw);
> +
> +	/* default, enable 10 Mbps operation */
> +	writel(EMAC_RMII_SPD100, &emac_p->supp.clr);
> +
> +	/* wait until link status UP or deadline elapsed */
> +	expire = get_ticks() + get_tbclk() * 2;
> +	for (; get_ticks() < expire;) {
> +		stat = phy_read(priv->phydev, priv->phy_addr,
> MII_BMSR);
> +		if (stat & BMSR_LSTATUS)
> +			break;
> +	}
> +
> +	if (!(stat & BMSR_LSTATUS))
> +		printf("MAC: Link is DOWN!\n");
> +
> +	/* delay to stabilize before any tx/rx */
> +	mdelay(10);
> +}
> +
> +static void pic32_mac_reset(struct pic32eth_dev *priv)
> +{
> +	struct pic32_emac_regs *emac_p = priv->emac_regs;
> +	struct mii_dev *mii;
> +
> +	/* Reset MAC */
> +	writel(EMAC_SOFTRESET, &emac_p->cfg1.raw);
> +	mdelay(10);
> +
> +	/* clear reset */
> +	writel(0, &emac_p->cfg1.raw);
> +
> +	/* Reset MII */
> +	mii = priv->phydev->bus;
> +	if (mii && mii->reset)
> +		mii->reset(mii);
> +}
> +
> +/* initializes the MAC and PHY, then establishes a link */
> +static void pic32_ctrl_reset(struct pic32eth_dev *priv)
> +{
> +	struct pic32_ectl_regs *ectl_p = priv->ectl_regs;
> +	u32 v;
> +
> +	/* disable RX, TX & any other transactions */
> +	writel(ETHCON_ON | ETHCON_TXRTS | ETHCON_RXEN, &ectl_p
> ->con1.clr);
> +
> +	/* wait till busy */
> +	wait_for_bit(__func__, &ectl_p->stat.raw, ETHSTAT_BUSY,
> false,
> +		     CONFIG_SYS_HZ, false);
> +	/* decrement received buffcnt to zero. */
> +	while (readl(&ectl_p->stat.raw) & ETHSTAT_BUFCNT)
> +		writel(ETHCON_BUFCDEC, &ectl_p->con1.set);
> +
> +	/* clear any existing interrupt event */
> +	writel(0xffffffff, &ectl_p->irq.clr);
> +
> +	/* clear RX/TX start address */
> +	writel(0xffffffff, &ectl_p->txst.clr);
> +	writel(0xffffffff, &ectl_p->rxst.clr);
> +
> +	/* clear the receive filters */
> +	writel(0x00ff, &ectl_p->rxfc.clr);
> +
> +	/* set the receive filters
> +	 * ETH_FILT_CRC_ERR_REJECT
> +	 * ETH_FILT_RUNT_REJECT
> +	 * ETH_FILT_UCAST_ACCEPT
> +	 * ETH_FILT_MCAST_ACCEPT
> +	 * ETH_FILT_BCAST_ACCEPT
> +	 */
> +	v = ETHRXFC_BCEN | ETHRXFC_MCEN | ETHRXFC_UCEN |
> +	    ETHRXFC_RUNTEN | ETHRXFC_CRCOKEN;
> +	writel(v, &ectl_p->rxfc.set);
> +
> +	/* turn controller ON to access PHY over MII */
> +	writel(ETHCON_ON, &ectl_p->con1.set);
> +}
> +
> +static void pic32_rx_desc_init(struct pic32eth_dev *priv)
> +{
> +	struct pic32_ectl_regs *ectl_p = priv->ectl_regs;
> +	struct eth_dma_desc *rxd;
> +	u32 idx, bufsz;
> +
> +	priv->rxd_idx = 0;
> +	for (idx = 0; idx < MAX_RX_DESCR; idx++) {
> +		rxd = &priv->rxd_ring[idx];
> +
> +		/* hw owned */
> +		rxd->hdr = EDH_NPV | EDH_EOWN | EDH_STICKY;
> +
> +		/* packet buffer address */
> +		rxd->data_buff = virt_to_phys(net_rx_packets[idx]);
> +
> +		/* link to next desc */
> +		rxd->next_ed = virt_to_phys(rxd + 1);
> +
> +		/* reset status */
> +		rxd->stat1 = 0;
> +		rxd->stat2 = 0;
> +
> +		/* decrement bufcnt */
> +		writel(ETHCON_BUFCDEC, &ectl_p->con1.set);
> +	}
> +
> +	/* link last descr to beginning of list */
> +	rxd->next_ed = virt_to_phys(&priv->rxd_ring[0]);
> +
> +	/* flush rx ring */
> +	__dcache_flush(priv->rxd_ring, sizeof(priv->rxd_ring));
> +
> +	/* set rx desc-ring start address */
> +	writel((ulong)virt_to_phys(&priv->rxd_ring[0]), &ectl_p
> ->rxst.raw);
> +
> +	/* RX Buffer size */
> +	bufsz = readl(&ectl_p->con2.raw);
> +	bufsz &= ~(ETHCON_RXBUFSZ << ETHCON_RXBUFSZ_SHFT);
> +	bufsz |= ((MAX_RX_BUF_SIZE / 16) << ETHCON_RXBUFSZ_SHFT);
> +	writel(bufsz, &ectl_p->con2.raw);
> +
> +	/* enable the receiver in hardware which allows hardware
> +	 * to DMA received pkts to the descriptor pointer address.
> +	 */
> +	writel(ETHCON_RXEN, &ectl_p->con1.set);
> +}
> +
> +static int pic32_eth_start(struct udevice *dev)
> +{
> +	struct eth_pdata *pdata = dev_get_platdata(dev);
> +	struct pic32eth_dev *priv = dev_get_priv(dev);
> +
> +	/* controller */
> +	pic32_ctrl_reset(priv);
> +
> +	/* reset MAC */
> +	pic32_mac_reset(priv);
> +
> +	/* configure PHY */
> +	phy_config(priv->phydev);
> +
> +	/* initialize MAC */
> +	pic32_mac_init(priv, &pdata->enetaddr[0]);
> +
> +	/* init RX descriptor; TX descriptors are handled in xmit */
> +	pic32_rx_desc_init(priv);
> +
> +	/* Start up & update link status of PHY */
> +	phy_startup(priv->phydev);
> +
> +	/* adjust mac with phy link status */
> +	return pic32_mac_adjust_link(priv);
> +}
> +
> +static void pic32_eth_stop(struct udevice *dev)
> +{
> +	struct pic32eth_dev *priv = dev_get_priv(dev);
> +	struct pic32_ectl_regs *ectl_p = priv->ectl_regs;
> +	struct pic32_emac_regs *emac_p = priv->emac_regs;
> +
> +	/* Reset the phy if the controller is enabled */
> +	if (readl(&ectl_p->con1.raw) & ETHCON_ON)
> +		phy_reset(priv->phydev);
> +
> +	/* Shut down the PHY */
> +	phy_shutdown(priv->phydev);
> +
> +	/* Stop rx/tx */
> +	writel(ETHCON_TXRTS | ETHCON_RXEN, &ectl_p->con1.clr);
> +	mdelay(10);
> +
> +	/* reset MAC */
> +	writel(EMAC_SOFTRESET, &emac_p->cfg1.raw);
> +
> +	/* clear reset */
> +	writel(0, &emac_p->cfg1.raw);
> +	mdelay(10);
> +
> +	/* disable controller */
> +	writel(ETHCON_ON, &ectl_p->con1.clr);
> +	mdelay(10);
> +
> +	/* wait until everything is down */
> +	wait_for_bit(__func__, &ectl_p->stat.raw, ETHSTAT_BUSY,
> false,
> +		     2 * CONFIG_SYS_HZ, false);
> +
> +	/* clear any existing interrupt event */
> +	writel(0xffffffff, &ectl_p->irq.clr);
> +}
> +
> +static int pic32_eth_send(struct udevice *dev, void *packet, int
> length)
> +{
> +	struct pic32eth_dev *priv = dev_get_priv(dev);
> +	struct pic32_ectl_regs *ectl_p = priv->ectl_regs;
> +	struct eth_dma_desc *txd;
> +	u64 deadline;
> +
> +	txd = &priv->txd_ring[0];
> +
> +	/* set proper flags & length in descriptor header */
> +	txd->hdr = EDH_SOP | EDH_EOP | EDH_EOWN |
> EDH_BCOUNT(length);
> +
> +	/* pass buffer address to hardware */
> +	txd->data_buff = virt_to_phys(packet);
> +
> +	debug("%s: %d / .hdr %x, .data_buff %x, .stat %x, .nexted
> %x\n",
> +	      __func__, __LINE__, txd->hdr, txd->data_buff, txd
> ->stat2,
> +	      txd->next_ed);
> +
> +	/* cache flush (packet) */
> +	__dcache_flush(packet, length);
> +
> +	/* cache flush (txd) */
> +	__dcache_flush(txd, sizeof(*txd));
> +
> +	/* pass descriptor table base to h/w */
> +	writel(virt_to_phys(txd), &ectl_p->txst.raw);
> +
> +	/* ready to send enabled, hardware can now send the
> packet(s) */
> +	writel(ETHCON_TXRTS | ETHCON_ON, &ectl_p->con1.set);
> +
> +	/* wait until tx has completed and h/w has released
> ownership
> +	 * of the tx descriptor or timeout elapsed.
> +	 */
> +	deadline = get_ticks() + get_tbclk();
> +	for (;;) {
> +		/* check timeout */
> +		if (get_ticks() > deadline)
> +			return -ETIMEDOUT;
> +
> +		if (ctrlc())
> +			return -EINTR;
> +
> +		/* tx completed ? */
> +		if (readl(&ectl_p->con1.raw) & ETHCON_TXRTS) {
> +			udelay(1);
> +			continue;
> +		}
> +
> +		/* h/w not released ownership yet? */
> +		__dcache_invalidate(txd, sizeof(*txd));
> +		if (!(txd->hdr & EDH_EOWN))
> +			break;
> +	}
> +
> +	return 0;
> +}
> +
> +static int pic32_eth_recv(struct udevice *dev, int flags, uchar
> **packetp)
> +{
> +	struct pic32eth_dev *priv = dev_get_priv(dev);
> +	struct eth_dma_desc *rxd;
> +	u32 idx = priv->rxd_idx;
> +	u32 rx_count;
> +
> +	/* find the next ready to receive */
> +	rxd = &priv->rxd_ring[idx];
> +
> +	__dcache_invalidate(rxd, sizeof(*rxd));
> +	/* check if owned by MAC */
> +	if (rxd->hdr & EDH_EOWN)
> +		return -EAGAIN;
> +
> +	/* Sanity check on header: SOP and EOP  */
> +	if ((rxd->hdr & (EDH_SOP | EDH_EOP)) != (EDH_SOP | EDH_EOP))
> {
> +		printf("%s: %s, rx pkt across multiple descr\n",
> +		       __FILE__, __func__);
> +		return 0;
> +	}
> +
> +	debug("%s: %d /idx %i, hdr=%x, data_buff %x, stat %x, nexted
> %x\n",
> +	      __func__, __LINE__, idx, rxd->hdr,
> +	      rxd->data_buff, rxd->stat2, rxd->next_ed);
> +
> +	/* Sanity check on rx_stat: OK, CRC */
> +	if (!RSV_RX_OK(rxd->stat2) || RSV_CRC_ERR(rxd->stat2)) {
> +		debug("%s: %s: Error, rx problem detected\n",
> +		      __FILE__, __func__);
> +		return 0;
> +	}
> +
> +	/* invalidate dcache */
> +	rx_count = RSV_RX_COUNT(rxd->stat2);
> +	__dcache_invalidate(net_rx_packets[idx], rx_count);
> +
> +	/* Pass the packet to protocol layer */
> +	*packetp = net_rx_packets[idx];
> +
> +	/* increment number of bytes rcvd (ignore CRC) */
> +	return rx_count - 4;
> +}
> +
> +static int pic32_eth_free_pkt(struct udevice *dev, uchar *packet,
> int length)
> +{
> +	struct pic32eth_dev *priv = dev_get_priv(dev);
> +	struct pic32_ectl_regs *ectl_p = priv->ectl_regs;
> +	struct eth_dma_desc *rxd;
> +	int idx = priv->rxd_idx;
> +
> +	/* sanity check */
> +	if (packet != net_rx_packets[idx]) {
> +		printf("rxd_id %d: packet is not matched,\n", idx);
> +		return -EAGAIN;
> +	}
> +
> +	/* prepare for receive */
> +	rxd = &priv->rxd_ring[idx];
> +	rxd->hdr = EDH_STICKY | EDH_NPV | EDH_EOWN;
> +
> +	__dcache_flush(rxd, sizeof(*rxd));
> +
> +	/* decrement rx pkt count */
> +	writel(ETHCON_BUFCDEC, &ectl_p->con1.set);
> +
> +	debug("%s: %d / idx %i, hdr %x, data_buff %x, stat %x,
> nexted %x\n",
> +	      __func__, __LINE__, idx, rxd->hdr, rxd->data_buff,
> +	      rxd->stat2, rxd->next_ed);
> +
> +	priv->rxd_idx = (priv->rxd_idx + 1) % MAX_RX_DESCR;
> +
> +	return 0;
> +}
> +
> +static const struct eth_ops pic32_eth_ops = {
> +	.start		= pic32_eth_start,
> +	.send		= pic32_eth_send,
> +	.recv		= pic32_eth_recv,
> +	.free_pkt	= pic32_eth_free_pkt,
> +	.stop		= pic32_eth_stop,
> +};
> +
> +static int pic32_eth_probe(struct udevice *dev)
> +{
> +	struct eth_pdata *pdata = dev_get_platdata(dev);

your driver private data should be stored in struct pic32eth_dev

> +	struct pic32eth_dev *priv = dev_get_priv(dev);
> +	const char *phy_mode;
> +	void __iomem *iobase;
> +	fdt_addr_t addr;
> +	fdt_size_t size;
> +	int offset = 0;
> +	int phy_addr = -1;
> +
> +	addr = fdtdec_get_addr_size(gd->fdt_blob, dev->of_offset,
> "reg", &size);
> +	if (addr == FDT_ADDR_T_NONE)
> +		return -EINVAL;
> +
> +	iobase = ioremap(addr, size);
> +	if (!iobase)
> +		return -EINVAL;

you can drop this check. ioremap() always returns a mapped address.

> +
> +	pdata->iobase = (phys_addr_t)addr;
> +
> +	/* get phy mode */
> +	pdata->phy_interface = -1;
> +	phy_mode = fdt_getprop(gd->fdt_blob, dev->of_offset, "phy
> -mode", NULL);
> +	if (phy_mode)
> +		pdata->phy_interface =
> phy_get_interface_by_name(phy_mode);
> +	if (pdata->phy_interface == -1) {
> +		debug("%s: Invalid PHY interface '%s'\n", __func__,
> phy_mode);
> +		return -EINVAL;
> +	}

your driver private data should be stored in struct pic32eth_dev. Only
if a arch/SoC does not support device-tree, platdata have to be
initialized with PHY address and PHY interface in the board code. Also
form a drivers point of view, platdata is read-only and provides board
-specific configuration for the driver.

> +
> +	/* get phy addr */
> +	offset = fdtdec_lookup_phandle(gd->fdt_blob, dev->of_offset,
> +				       "phy-handle");
> +	if (offset > 0)
> +		phy_addr = fdtdec_get_int(gd->fdt_blob, offset,
> "reg", -1);
> +
> +	/* phy reset gpio */
> +	gpio_request_by_name_nodev(gd->fdt_blob, dev->of_offset,
> +				   "reset-gpios", 0,
> +				   &priv->rst_gpio, GPIOD_IS_OUT);
> +
> +	priv->phyif	= pdata->phy_interface;
> +	priv->phy_addr	= phy_addr;
> +	priv->ectl_regs	= iobase;
> +	priv->emac_regs	= iobase + PIC32_EMAC1CFG1;
> +
> +	pic32_mii_init(priv);
> +
> +	return pic32_phy_init(priv, dev);
> +}
> +
> +static int pic32_eth_remove(struct udevice *dev)
> +{
> +	struct pic32eth_dev *priv = dev_get_priv(dev);
> +	struct mii_dev *bus;
> +
> +	dm_gpio_free(dev, &priv->rst_gpio);
> +	phy_shutdown(priv->phydev);
> +	free(priv->phydev);
> +	bus = miiphy_get_dev_by_name(PIC32_MDIO_NAME);
> +	mdio_unregister(bus);
> +	mdio_free(bus);
> +	iounmap(priv->ectl_regs);
> +	return 0;
> +}
> +
> +static const struct udevice_id pic32_eth_ids[] = {
> +	{ .compatible = "microchip,pic32mzda-eth" },
> +	{ }
> +};
> +
> +U_BOOT_DRIVER(pic32_ethernet) = {
> +	.name			= "pic32_ethernet",
> +	.id			= UCLASS_ETH,
> +	.of_match		= pic32_eth_ids,
> +	.probe			= pic32_eth_probe,
> +	.remove			= pic32_eth_remove,
> +	.ops			= &pic32_eth_ops,
> +	.priv_auto_alloc_size	= sizeof(struct pic32eth_dev),
> +	.platdata_auto_alloc_size	= sizeof(struct eth_pdata),
> +};
> diff --git a/drivers/net/pic32_eth.h b/drivers/net/pic32_eth.h
> new file mode 100644
> index 0000000..4dd443b
> --- /dev/null
> +++ b/drivers/net/pic32_eth.h
> @@ -0,0 +1,171 @@
> +/*
> + * (c) 2015 Purna Chandra Mandal <purna.mandal@microchip.com>
> + *
> + * SPDX-License-Identifier:	GPL-2.0+
> + *
> + */
> +
> +#ifndef __MICROCHIP_PIC32_ETH_H_
> +#define __MICROCHIP_PIC32_ETH_H_
> +
> +#include <mach/pic32.h>
> +
> +/* Ethernet */
> +struct pic32_ectl_regs {
> +	struct pic32_reg_atomic con1; /* 0x00 */
> +	struct pic32_reg_atomic con2; /* 0x10 */
> +	struct pic32_reg_atomic txst; /* 0x20 */
> +	struct pic32_reg_atomic rxst; /* 0x30 */
> +	struct pic32_reg_atomic ht0;  /* 0x40 */
> +	struct pic32_reg_atomic ht1;  /* 0x50 */
> +	struct pic32_reg_atomic pmm0; /* 0x60 */
> +	struct pic32_reg_atomic pmm1; /* 0x70 */
> +	struct pic32_reg_atomic pmcs; /* 0x80 */
> +	struct pic32_reg_atomic pmo;  /* 0x90 */
> +	struct pic32_reg_atomic rxfc; /* 0xa0 */
> +	struct pic32_reg_atomic rxwm; /* 0xb0 */
> +	struct pic32_reg_atomic ien;  /* 0xc0 */
> +	struct pic32_reg_atomic irq;  /* 0xd0 */
> +	struct pic32_reg_atomic stat; /* 0xe0 */
> +};
> +
> +struct pic32_mii_regs {
> +	struct pic32_reg_atomic mcfg; /* 0x280 */
> +	struct pic32_reg_atomic mcmd; /* 0x290 */
> +	struct pic32_reg_atomic madr; /* 0x2a0 */
> +	struct pic32_reg_atomic mwtd; /* 0x2b0 */
> +	struct pic32_reg_atomic mrdd; /* 0x2c0 */
> +	struct pic32_reg_atomic mind; /* 0x2d0 */
> +};
> +
> +struct pic32_emac_regs {
> +	struct pic32_reg_atomic cfg1; /* 0x200*/
> +	struct pic32_reg_atomic cfg2; /* 0x210*/
> +	struct pic32_reg_atomic ipgt; /* 0x220*/
> +	struct pic32_reg_atomic ipgr; /* 0x230*/
> +	struct pic32_reg_atomic clrt; /* 0x240*/
> +	struct pic32_reg_atomic maxf; /* 0x250*/
> +	struct pic32_reg_atomic supp; /* 0x260*/
> +	struct pic32_reg_atomic test; /* 0x270*/
> +	struct pic32_mii_regs mii;    /* 0x280 - 0x2d0 */
> +	struct pic32_reg_atomic res1; /* 0x2e0 */
> +	struct pic32_reg_atomic res2; /* 0x2f0 */
> +	struct pic32_reg_atomic sa0;  /* 0x300 */
> +	struct pic32_reg_atomic sa1;  /* 0x310 */
> +	struct pic32_reg_atomic sa2;  /* 0x320 */
> +};
> +
> +/* ETHCON1 Reg field */
> +#define ETHCON_BUFCDEC		BIT(0)
> +#define ETHCON_RXEN		BIT(8)
> +#define ETHCON_TXRTS		BIT(9)
> +#define ETHCON_ON		BIT(15)
> +
> +/* ETHCON2 Reg field */
> +#define ETHCON_RXBUFSZ		0x7f
> +#define ETHCON_RXBUFSZ_SHFT	0x4
> +
> +/* ETHSTAT Reg field */
> +#define ETHSTAT_BUSY		BIT(7)
> +#define ETHSTAT_BUFCNT		0x00ff0000
> +
> +/* ETHRXFC Register fields */
> +#define ETHRXFC_BCEN		BIT(0)
> +#define ETHRXFC_MCEN		BIT(1)
> +#define ETHRXFC_UCEN		BIT(3)
> +#define ETHRXFC_RUNTEN		BIT(4)
> +#define ETHRXFC_CRCOKEN		BIT(5)
> +
> +/* EMAC1CFG1 register offset */
> +#define PIC32_EMAC1CFG1		0x0200
> +
> +/* EMAC1CFG1 register fields */
> +#define EMAC_RXENABLE		BIT(0)
> +#define EMAC_RXPAUSE		BIT(2)
> +#define EMAC_TXPAUSE		BIT(3)
> +#define EMAC_SOFTRESET		BIT(15)
> +
> +/* EMAC1CFG2 register fields */
> +#define EMAC_FULLDUP		BIT(0)
> +#define EMAC_LENGTHCK		BIT(1)
> +#define EMAC_CRCENABLE		BIT(4)
> +#define EMAC_PADENABLE		BIT(5)
> +#define EMAC_AUTOPAD		BIT(7)
> +#define EMAC_EXCESS		BIT(14)
> +
> +/* EMAC1IPGT register magic */
> +#define FULLDUP_GAP_TIME	0x15
> +#define HALFDUP_GAP_TIME	0x12
> +
> +/* EMAC1SUPP register fields */
> +#define EMAC_RMII_SPD100	BIT(8)
> +#define EMAC_RMII_RESET		BIT(11)
> +
> +/* MII Management Configuration Register */
> +#define MIIMCFG_RSTMGMT		BIT(15)
> +#define MIIMCFG_CLKSEL_DIV40	0x0020	/* 100Mhz / 40 */
> +
> +/* MII Management Command Register */
> +#define MIIMCMD_READ		BIT(0)
> +#define MIIMCMD_SCAN		BIT(1)
> +
> +/* MII Management Address Register */
> +#define MIIMADD_REGADDR		0x1f
> +#define MIIMADD_REGADDR_SHIFT	0
> +#define MIIMADD_PHYADDR_SHIFT	8
> +
> +/* MII Management Indicator Register */
> +#define MIIMIND_BUSY		BIT(0)
> +#define MIIMIND_NOTVALID	BIT(2)
> +#define MIIMIND_LINKFAIL	BIT(3)
> +
> +/* Packet Descriptor */
> +/* Received Packet Status */
> +#define _RSV1_PKT_CSUM		0xffff
> +#define _RSV2_CRC_ERR		BIT(20)
> +#define _RSV2_LEN_ERR		BIT(21)
> +#define _RSV2_RX_OK		BIT(23)
> +#define _RSV2_RX_COUNT		0xffff
> +
> +#define RSV_RX_CSUM(__rsv1)	((__rsv1) & _RSV1_PKT_CSUM)
> +#define RSV_RX_COUNT(__rsv2)	((__rsv2) & _RSV2_RX_COUNT)
> +#define RSV_RX_OK(__rsv2)	((__rsv2) & _RSV2_RX_OK)
> +#define RSV_CRC_ERR(__rsv2)	((__rsv2) & _RSV2_CRC_ERR)
> +
> +/* Ethernet Hardware Descriptor Header bits */
> +#define EDH_EOWN		BIT(7)
> +#define EDH_NPV			BIT(8)
> +#define EDH_STICKY		BIT(9)
> +#define _EDH_BCOUNT		0x07ff0000
> +#define EDH_EOP			BIT(30)
> +#define EDH_SOP			BIT(31)
> +#define EDH_BCOUNT_SHIFT	16
> +#define EDH_BCOUNT(len)		((len) << EDH_BCOUNT_SHIFT)
> +
> +/* Ethernet Hardware Descriptors
> + * ref: PIC32 Family Reference Manual Table 35-7
> + * This structure represents the layout of the DMA
> + * memory shared between the CPU and the Ethernet
> + * controller.
> + */
> +/* TX/RX DMA descriptor */
> +struct eth_dma_desc {
> +	u32 hdr;	/* header */
> +	u32 data_buff;	/* data buffer address */
> +	u32 stat1;	/* transmit/receive packet status */
> +	u32 stat2;	/* transmit/receive packet status */
> +	u32 next_ed;	/* next descriptor */
> +};
> +
> +/* cache operation helper */
> +#define __dcache_flush(__a, __l) \
> +	flush_dcache_range((ulong)(__a),  ((__l) + (ulong)(__a)))
> +
> +#define __dcache_invalidate(__a, __l) \
> +	invalidate_dcache_range((ulong)(__a),  ((__l) +
> (ulong)(__a)))
> +
> +#define PIC32_MDIO_NAME "PIC32_EMAC"
> +
> +int pic32_mdio_init(const char *name, ulong ioaddr);
> +
> +#endif /* __MICROCHIP_PIC32_ETH_H_*/
> diff --git a/drivers/net/pic32_mdio.c b/drivers/net/pic32_mdio.c
> new file mode 100644
> index 0000000..578fc96
> --- /dev/null
> +++ b/drivers/net/pic32_mdio.c
> @@ -0,0 +1,121 @@
> +/*
> + * pic32_mdio.c: PIC32 MDIO/MII driver, part of pic32_eth.c.
> + *
> + * Copyright 2015 Microchip Inc.
> + *	Purna Chandra Mandal <purna.mandal@microchip.com>
> + *
> + * SPDX-License-Identifier:	GPL-2.0+
> + */
> +#include <common.h>
> +#include <phy.h>
> +#include <miiphy.h>
> +#include <errno.h>
> +#include <wait_bit.h>
> +#include <asm/io.h>
> +#include "pic32_eth.h"
> +
> +static int pic32_mdio_write(struct mii_dev *bus,
> +			    int addr, int dev_addr,
> +			    int reg, u16 value)
> +{
> +	u32 v;
> +	struct pic32_mii_regs *mii_regs = bus->priv;
> +
> +	/* Wait for the previous operation to finish */
> +	wait_for_bit(__func__, &mii_regs->mind.raw, MIIMIND_BUSY,
> +		     false, CONFIG_SYS_HZ, true);
> +
> +	/* Put phyaddr and regaddr into MIIMADD */
> +	v = (addr << MIIMADD_PHYADDR_SHIFT) | (reg &
> MIIMADD_REGADDR);
> +	writel(v, &mii_regs->madr.raw);
> +
> +	/* Initiate a write command */
> +	writel(value, &mii_regs->mwtd.raw);
> +
> +	/* Wait 30 clock cycles for busy flag to be set */
> +	udelay(12);
> +
> +	/* Wait for write to complete */
> +	wait_for_bit(__func__, &mii_regs->mind.raw, MIIMIND_BUSY,
> +		     false, CONFIG_SYS_HZ, true);
> +
> +	return 0;
> +}
> +
> +static int pic32_mdio_read(struct mii_dev *bus, int addr, int
> devaddr, int reg)
> +{
> +	u32 v;
> +	struct pic32_mii_regs *mii_regs = bus->priv;
> +
> +	/* Wait for the previous operation to finish */
> +	wait_for_bit(__func__, &mii_regs->mind.raw, MIIMIND_BUSY,
> +		     false, CONFIG_SYS_HZ, true);
> +
> +	/* Put phyaddr and regaddr into MIIMADD */
> +	v = (addr << MIIMADD_PHYADDR_SHIFT) | (reg &
> MIIMADD_REGADDR);
> +	writel(v, &mii_regs->madr.raw);
> +
> +	/* Initiate a read command */
> +	writel(MIIMCMD_READ, &mii_regs->mcmd.raw);
> +
> +	/* Wait 30 clock cycles for busy flag to be set */
> +	udelay(12);
> +
> +	/* Wait for read to complete */
> +	wait_for_bit(__func__, &mii_regs->mind.raw,
> +		     MIIMIND_NOTVALID | MIIMIND_BUSY,
> +		     false, CONFIG_SYS_HZ, false);
> +
> +	/* Clear the command register */
> +	writel(0, &mii_regs->mcmd.raw);
> +
> +	/* Grab the value read from the PHY */
> +	v = readl(&mii_regs->mrdd.raw);
> +	return v;
> +}
> +
> +static int pic32_mdio_reset(struct mii_dev *bus)
> +{
> +	struct pic32_mii_regs *mii_regs = bus->priv;
> +
> +	/* Reset MII (due to new addresses) */
> +	writel(MIIMCFG_RSTMGMT, &mii_regs->mcfg.raw);
> +
> +	/* Wait for the operation to finish */
> +	wait_for_bit(__func__, &mii_regs->mind.raw, MIIMIND_BUSY,
> +		     false, CONFIG_SYS_HZ, true);
> +
> +	/* Clear reset bit */
> +	writel(0, &mii_regs->mcfg);
> +
> +	/* Wait for the operation to finish */
> +	wait_for_bit(__func__, &mii_regs->mind.raw, MIIMIND_BUSY,
> +		     false, CONFIG_SYS_HZ, true);
> +
> +	/* Set the MII Management Clock (MDC) - no faster than 2.5
> MHz */
> +	writel(MIIMCFG_CLKSEL_DIV40, &mii_regs->mcfg.raw);
> +
> +	/* Wait for the operation to finish */
> +	wait_for_bit(__func__, &mii_regs->mind.raw, MIIMIND_BUSY,
> +		     false, CONFIG_SYS_HZ, true);
> +	return 0;
> +}
> +
> +int pic32_mdio_init(const char *name, ulong ioaddr)
> +{
> +	struct mii_dev *bus;
> +
> +	bus = mdio_alloc();
> +	if (!bus) {
> +		printf("Failed to allocate PIC32-MDIO bus\n");
> +		return -ENOMEM;
> +	}
> +
> +	bus->read = pic32_mdio_read;
> +	bus->write = pic32_mdio_write;
> +	bus->reset = pic32_mdio_reset;
> +	strncpy(bus->name, name, sizeof(bus->name));
> +	bus->priv = (void *)ioaddr;
> +
> +	return mdio_register(bus);
> +}
Purna Chandra Mandal Jan. 14, 2016, 10:05 a.m. UTC | #3
On 01/13/2016 09:07 PM, Daniel Schwierzeck wrote:

> Am Dienstag, den 12.01.2016, 15:48 +0530 schrieb Purna Chandra Mandal:
>> This driver implements MAC and MII layer of the ethernet controller.
>> Network data transfer is handled by controller internal DMA engine.
>> Ethernet controller is configurable through device-tree file.
>>
>> Signed-off-by: Purna Chandra Mandal <purna.mandal@microchip.com>
>>
>>
>> ---
>>
>> Changes in v3:
>> - merge wrappers with eth operation callbacks
>> - read phy address from device-tree
>> - rename functions (e.g. _eth_xyz() with pic32_eth_xyz())
>>
>> Changes in v2: None
>>
>>  drivers/net/Kconfig      |   7 +
>>  drivers/net/Makefile     |   1 +
>>  drivers/net/pic32_eth.c  | 606
>> +++++++++++++++++++++++++++++++++++++++++++++++
>>  drivers/net/pic32_eth.h  | 171 +++++++++++++
>>  drivers/net/pic32_mdio.c | 121 ++++++++++
>>  5 files changed, 906 insertions(+)
>>  create mode 100644 drivers/net/pic32_eth.c
>>  create mode 100644 drivers/net/pic32_eth.h
>>  create mode 100644 drivers/net/pic32_mdio.c
>>
>> diff --git a/drivers/net/Kconfig b/drivers/net/Kconfig
>> index ae5e78d..dc49493 100644
>> --- a/drivers/net/Kconfig
>> +++ b/drivers/net/Kconfig
>> @@ -108,4 +108,11 @@ config ZYNQ_GEM
>>  	help
>>  	  This MAC is present in Xilinx Zynq and ZynqMP SoCs.
>>  
>> +config PIC32_ETH
>> +	bool "Microchip PIC32 Ethernet Support"
>> +	depends on MACH_PIC32
> should be
>
> depends on DM_ETH && MACH_PIC32
> select PHYLIB

ack.

>> +	help
>> +	  This driver implements 10/100 Mbps Ethernet and MAC layer
>> for
>> +	  Microchip PIC32 microcontrollers.
>> +
>>  endif # NETDEVICES
>> diff --git a/drivers/net/Makefile b/drivers/net/Makefile
>> index 150470c..33a81ee 100644
>> --- a/drivers/net/Makefile
>> +++ b/drivers/net/Makefile
>> @@ -72,3 +72,4 @@ obj-$(CONFIG_FSL_MC_ENET) += fsl-mc/
>>  obj-$(CONFIG_FSL_MC_ENET) += ldpaa_eth/
>>  obj-$(CONFIG_FSL_MEMAC) += fm/memac_phy.o
>>  obj-$(CONFIG_VSC9953) += vsc9953.o
>> +obj-$(CONFIG_PIC32_ETH) += pic32_mdio.o pic32_eth.o
>> diff --git a/drivers/net/pic32_eth.c b/drivers/net/pic32_eth.c
>> new file mode 100644
>> index 0000000..1cef62e
>> --- /dev/null
>> +++ b/drivers/net/pic32_eth.c
>> @@ -0,0 +1,606 @@
>> +/*
>> + * (c) 2015 Purna Chandra Mandal <purna.mandal@microchip.com>
>> + *
>> + * SPDX-License-Identifier:	GPL-2.0+
>> + *
>> + */
>> +#include <common.h>
>> +#include <errno.h>
>> +#include <dm.h>
>> +#include <net.h>
>> +#include <miiphy.h>
>> +#include <console.h>
>> +#include <wait_bit.h>
>> +#include <asm/gpio.h>
>> +
>> +#include "pic32_eth.h"
>> +
>> +#define MAX_RX_BUF_SIZE		1536
>> +#define MAX_RX_DESCR		PKTBUFSRX
>> +#define MAX_TX_DESCR		2
>> +
>> +DECLARE_GLOBAL_DATA_PTR;
>> +
>> +struct pic32eth_dev {
>> +	struct eth_dma_desc rxd_ring[MAX_RX_DESCR];
>> +	struct eth_dma_desc txd_ring[MAX_TX_DESCR];
>> +	u32 rxd_idx; /* index of RX desc to read */
>> +	/* regs */
>> +	struct pic32_ectl_regs *ectl_regs;
>> +	struct pic32_emac_regs *emac_regs;
>> +	/* Phy */
>> +	struct phy_device *phydev;
>> +	phy_interface_t phyif;
>> +	u32 phy_addr;
>> +	struct gpio_desc rst_gpio;
>> +};
>> +
>> +void __weak board_netphy_reset(void *dev)
>> +{
>> +	struct pic32eth_dev *priv = (struct pic32eth_dev *)dev;
> the cast is not necessary

ack. Will remove,

>> +
>> +	if (!dm_gpio_is_valid(&priv->rst_gpio))
>> +		return;
>> +
>> +	/* phy reset */
>> +	dm_gpio_set_value(&priv->rst_gpio, 0);
>> +	udelay(300);
>> +	dm_gpio_set_value(&priv->rst_gpio, 1);
>> +	udelay(300);
>> +}
>> +
>> +/* Initialize mii(MDIO) interface, discover which PHY is
>> + * attached to the device, and configure it properly.
>> + */
>> +static int pic32_mii_init(struct pic32eth_dev *priv)
>> +{
>> +	struct pic32_ectl_regs *ectl_p = priv->ectl_regs;
>> +	struct pic32_emac_regs *emac_p = priv->emac_regs;
>> +
>> +	/* board phy reset */
>> +	board_netphy_reset(priv);
>> +
>> +	/* disable RX, TX & all transactions */
>> +	writel(ETHCON_ON | ETHCON_TXRTS | ETHCON_RXEN, &ectl_p
>> ->con1.clr);
>> +
>> +	/* wait till busy */
>> +	wait_for_bit(__func__, &ectl_p->stat.raw, ETHSTAT_BUSY,
>> false,
>> +		     CONFIG_SYS_HZ, false);
>> +
>> +	/* turn controller ON to access PHY over MII */
>> +	writel(ETHCON_ON, &ectl_p->con1.set);
>> +
>> +	mdelay(10);
>> +
>> +	/* reset MAC */
>> +	writel(EMAC_SOFTRESET, &emac_p->cfg1.set); /* reset assert
>> */
>> +	mdelay(10);
>> +	writel(EMAC_SOFTRESET, &emac_p->cfg1.clr); /* reset deassert
>> */
>> +
>> +	/* initialize MDIO/MII */
>> +	if (priv->phyif == PHY_INTERFACE_MODE_RMII) {
>> +		writel(EMAC_RMII_RESET, &emac_p->supp.set);
>> +		mdelay(10);
>> +		writel(EMAC_RMII_RESET, &emac_p->supp.clr);
>> +	}
>> +
>> +	return pic32_mdio_init(PIC32_MDIO_NAME, (ulong)&emac_p
>> ->mii);
>> +}
>> +
>> +static int pic32_phy_init(struct pic32eth_dev *priv, struct udevice
>> *dev)
>> +{
>> +	struct mii_dev *mii;
>> +
>> +	mii = miiphy_get_dev_by_name(PIC32_MDIO_NAME);
>> +
>> +	/* find & connect PHY */
>> +	priv->phydev = phy_connect(mii, priv->phy_addr,
>> +				   dev, priv->phyif);
>> +	if (!priv->phydev) {
>> +		printf("%s: %s: Error, PHY connect\n", __FILE__,
>> __func__);
>> +		return 0;
>> +	}
>> +
>> +	/* Wait for phy to complete reset */
>> +	mdelay(10);
>> +
>> +	/* configure supported modes */
>> +	priv->phydev->supported = SUPPORTED_10baseT_Half |
>> +				  SUPPORTED_10baseT_Full |
>> +				  SUPPORTED_100baseT_Half |
>> +				  SUPPORTED_100baseT_Full |
>> +				  SUPPORTED_Autoneg;
>> +
>> +	priv->phydev->advertising = ADVERTISED_10baseT_Half |
>> +				    ADVERTISED_10baseT_Full |
>> +				    ADVERTISED_100baseT_Half |
>> +				    ADVERTISED_100baseT_Full |
>> +				    ADVERTISED_Autoneg;
>> +
>> +	priv->phydev->autoneg = AUTONEG_ENABLE;
>> +
>> +	return 0;
>> +}
>> +
>> +/* Configure MAC based on negotiated speed and duplex
>> + * reported by PHY.
>> + */
>> +static int pic32_mac_adjust_link(struct pic32eth_dev *priv)
>> +{
>> +	struct phy_device *phydev = priv->phydev;
>> +	struct pic32_emac_regs *emac_p = priv->emac_regs;
>> +
>> +	if (!phydev->link) {
>> +		printf("%s: No link.\n", phydev->dev->name);
>> +		return -EINVAL;
>> +	}
>> +
>> +	if (phydev->duplex) {
>> +		writel(EMAC_FULLDUP, &emac_p->cfg2.set);
>> +		writel(FULLDUP_GAP_TIME, &emac_p->ipgt.raw);
>> +	} else {
>> +		writel(EMAC_FULLDUP, &emac_p->cfg2.clr);
>> +		writel(HALFDUP_GAP_TIME, &emac_p->ipgt.raw);
>> +	}
>> +
>> +	switch (phydev->speed) {
>> +	case SPEED_100:
>> +		writel(EMAC_RMII_SPD100, &emac_p->supp.set);
>> +		break;
>> +	case SPEED_10:
>> +		writel(EMAC_RMII_SPD100, &emac_p->supp.clr);
>> +		break;
>> +	default:
>> +		printf("%s: Speed was bad\n", phydev->dev->name);
>> +		return -EINVAL;
>> +	}
>> +
>> +	printf("pic32eth: PHY is %s with %dbase%s, %s\n",
>> +	       phydev->drv->name, phydev->speed,
>> +	       (phydev->port == PORT_TP) ? "T" : "X",
>> +	       (phydev->duplex) ? "full" : "half");
>> +
>> +	return 0;
>> +}
>> +
>> +static void pic32_mac_init(struct pic32eth_dev *priv, u8 *macaddr)
>> +{
>> +	struct pic32_emac_regs *emac_p = priv->emac_regs;
>> +	u32 stat = 0, v;
>> +	u64 expire;
>> +
>> +	v = EMAC_TXPAUSE | EMAC_RXPAUSE | EMAC_RXENABLE;
>> +	writel(v, &emac_p->cfg1.raw);
>> +
>> +	v = EMAC_EXCESS | EMAC_AUTOPAD | EMAC_PADENABLE |
>> +	    EMAC_CRCENABLE | EMAC_LENGTHCK | EMAC_FULLDUP;
>> +	writel(v, &emac_p->cfg2.raw);
>> +
>> +	/* recommended back-to-back inter-packet gap for 10 Mbps
>> half duplex */
>> +	writel(HALFDUP_GAP_TIME, &emac_p->ipgt.raw);
>> +
>> +	/* recommended non-back-to-back interpacket gap is 0xc12 */
>> +	writel(0xc12, &emac_p->ipgr.raw);
>> +
>> +	/* recommended collision window retry limit is 0x370F */
>> +	writel(0x370f, &emac_p->clrt.raw);
>> +
>> +	/* set maximum frame length: allow VLAN tagged frame */
>> +	writel(0x600, &emac_p->maxf.raw);
>> +
>> +	/* set the mac address */
>> +	writel(macaddr[0] | (macaddr[1] << 8), &emac_p->sa2.raw);
>> +	writel(macaddr[2] | (macaddr[3] << 8), &emac_p->sa1.raw);
>> +	writel(macaddr[4] | (macaddr[5] << 8), &emac_p->sa0.raw);
>> +
>> +	/* default, enable 10 Mbps operation */
>> +	writel(EMAC_RMII_SPD100, &emac_p->supp.clr);
>> +
>> +	/* wait until link status UP or deadline elapsed */
>> +	expire = get_ticks() + get_tbclk() * 2;
>> +	for (; get_ticks() < expire;) {
>> +		stat = phy_read(priv->phydev, priv->phy_addr,
>> MII_BMSR);
>> +		if (stat & BMSR_LSTATUS)
>> +			break;
>> +	}
>> +
>> +	if (!(stat & BMSR_LSTATUS))
>> +		printf("MAC: Link is DOWN!\n");
>> +
>> +	/* delay to stabilize before any tx/rx */
>> +	mdelay(10);
>> +}
>> +
>> +static void pic32_mac_reset(struct pic32eth_dev *priv)
>> +{
>> +	struct pic32_emac_regs *emac_p = priv->emac_regs;
>> +	struct mii_dev *mii;
>> +
>> +	/* Reset MAC */
>> +	writel(EMAC_SOFTRESET, &emac_p->cfg1.raw);
>> +	mdelay(10);
>> +
>> +	/* clear reset */
>> +	writel(0, &emac_p->cfg1.raw);
>> +
>> +	/* Reset MII */
>> +	mii = priv->phydev->bus;
>> +	if (mii && mii->reset)
>> +		mii->reset(mii);
>> +}
>> +
>> +/* initializes the MAC and PHY, then establishes a link */
>> +static void pic32_ctrl_reset(struct pic32eth_dev *priv)
>> +{
>> +	struct pic32_ectl_regs *ectl_p = priv->ectl_regs;
>> +	u32 v;
>> +
>> +	/* disable RX, TX & any other transactions */
>> +	writel(ETHCON_ON | ETHCON_TXRTS | ETHCON_RXEN, &ectl_p
>> ->con1.clr);
>> +
>> +	/* wait till busy */
>> +	wait_for_bit(__func__, &ectl_p->stat.raw, ETHSTAT_BUSY,
>> false,
>> +		     CONFIG_SYS_HZ, false);
>> +	/* decrement received buffcnt to zero. */
>> +	while (readl(&ectl_p->stat.raw) & ETHSTAT_BUFCNT)
>> +		writel(ETHCON_BUFCDEC, &ectl_p->con1.set);
>> +
>> +	/* clear any existing interrupt event */
>> +	writel(0xffffffff, &ectl_p->irq.clr);
>> +
>> +	/* clear RX/TX start address */
>> +	writel(0xffffffff, &ectl_p->txst.clr);
>> +	writel(0xffffffff, &ectl_p->rxst.clr);
>> +
>> +	/* clear the receive filters */
>> +	writel(0x00ff, &ectl_p->rxfc.clr);
>> +
>> +	/* set the receive filters
>> +	 * ETH_FILT_CRC_ERR_REJECT
>> +	 * ETH_FILT_RUNT_REJECT
>> +	 * ETH_FILT_UCAST_ACCEPT
>> +	 * ETH_FILT_MCAST_ACCEPT
>> +	 * ETH_FILT_BCAST_ACCEPT
>> +	 */
>> +	v = ETHRXFC_BCEN | ETHRXFC_MCEN | ETHRXFC_UCEN |
>> +	    ETHRXFC_RUNTEN | ETHRXFC_CRCOKEN;
>> +	writel(v, &ectl_p->rxfc.set);
>> +
>> +	/* turn controller ON to access PHY over MII */
>> +	writel(ETHCON_ON, &ectl_p->con1.set);
>> +}
>> +
>> +static void pic32_rx_desc_init(struct pic32eth_dev *priv)
>> +{
>> +	struct pic32_ectl_regs *ectl_p = priv->ectl_regs;
>> +	struct eth_dma_desc *rxd;
>> +	u32 idx, bufsz;
>> +
>> +	priv->rxd_idx = 0;
>> +	for (idx = 0; idx < MAX_RX_DESCR; idx++) {
>> +		rxd = &priv->rxd_ring[idx];
>> +
>> +		/* hw owned */
>> +		rxd->hdr = EDH_NPV | EDH_EOWN | EDH_STICKY;
>> +
>> +		/* packet buffer address */
>> +		rxd->data_buff = virt_to_phys(net_rx_packets[idx]);
>> +
>> +		/* link to next desc */
>> +		rxd->next_ed = virt_to_phys(rxd + 1);
>> +
>> +		/* reset status */
>> +		rxd->stat1 = 0;
>> +		rxd->stat2 = 0;
>> +
>> +		/* decrement bufcnt */
>> +		writel(ETHCON_BUFCDEC, &ectl_p->con1.set);
>> +	}
>> +
>> +	/* link last descr to beginning of list */
>> +	rxd->next_ed = virt_to_phys(&priv->rxd_ring[0]);
>> +
>> +	/* flush rx ring */
>> +	__dcache_flush(priv->rxd_ring, sizeof(priv->rxd_ring));
>> +
>> +	/* set rx desc-ring start address */
>> +	writel((ulong)virt_to_phys(&priv->rxd_ring[0]), &ectl_p
>> ->rxst.raw);
>> +
>> +	/* RX Buffer size */
>> +	bufsz = readl(&ectl_p->con2.raw);
>> +	bufsz &= ~(ETHCON_RXBUFSZ << ETHCON_RXBUFSZ_SHFT);
>> +	bufsz |= ((MAX_RX_BUF_SIZE / 16) << ETHCON_RXBUFSZ_SHFT);
>> +	writel(bufsz, &ectl_p->con2.raw);
>> +
>> +	/* enable the receiver in hardware which allows hardware
>> +	 * to DMA received pkts to the descriptor pointer address.
>> +	 */
>> +	writel(ETHCON_RXEN, &ectl_p->con1.set);
>> +}
>> +
>> +static int pic32_eth_start(struct udevice *dev)
>> +{
>> +	struct eth_pdata *pdata = dev_get_platdata(dev);
>> +	struct pic32eth_dev *priv = dev_get_priv(dev);
>> +
>> +	/* controller */
>> +	pic32_ctrl_reset(priv);
>> +
>> +	/* reset MAC */
>> +	pic32_mac_reset(priv);
>> +
>> +	/* configure PHY */
>> +	phy_config(priv->phydev);
>> +
>> +	/* initialize MAC */
>> +	pic32_mac_init(priv, &pdata->enetaddr[0]);
>> +
>> +	/* init RX descriptor; TX descriptors are handled in xmit */
>> +	pic32_rx_desc_init(priv);
>> +
>> +	/* Start up & update link status of PHY */
>> +	phy_startup(priv->phydev);
>> +
>> +	/* adjust mac with phy link status */
>> +	return pic32_mac_adjust_link(priv);
>> +}
>> +
>> +static void pic32_eth_stop(struct udevice *dev)
>> +{
>> +	struct pic32eth_dev *priv = dev_get_priv(dev);
>> +	struct pic32_ectl_regs *ectl_p = priv->ectl_regs;
>> +	struct pic32_emac_regs *emac_p = priv->emac_regs;
>> +
>> +	/* Reset the phy if the controller is enabled */
>> +	if (readl(&ectl_p->con1.raw) & ETHCON_ON)
>> +		phy_reset(priv->phydev);
>> +
>> +	/* Shut down the PHY */
>> +	phy_shutdown(priv->phydev);
>> +
>> +	/* Stop rx/tx */
>> +	writel(ETHCON_TXRTS | ETHCON_RXEN, &ectl_p->con1.clr);
>> +	mdelay(10);
>> +
>> +	/* reset MAC */
>> +	writel(EMAC_SOFTRESET, &emac_p->cfg1.raw);
>> +
>> +	/* clear reset */
>> +	writel(0, &emac_p->cfg1.raw);
>> +	mdelay(10);
>> +
>> +	/* disable controller */
>> +	writel(ETHCON_ON, &ectl_p->con1.clr);
>> +	mdelay(10);
>> +
>> +	/* wait until everything is down */
>> +	wait_for_bit(__func__, &ectl_p->stat.raw, ETHSTAT_BUSY,
>> false,
>> +		     2 * CONFIG_SYS_HZ, false);
>> +
>> +	/* clear any existing interrupt event */
>> +	writel(0xffffffff, &ectl_p->irq.clr);
>> +}
>> +
>> +static int pic32_eth_send(struct udevice *dev, void *packet, int
>> length)
>> +{
>> +	struct pic32eth_dev *priv = dev_get_priv(dev);
>> +	struct pic32_ectl_regs *ectl_p = priv->ectl_regs;
>> +	struct eth_dma_desc *txd;
>> +	u64 deadline;
>> +
>> +	txd = &priv->txd_ring[0];
>> +
>> +	/* set proper flags & length in descriptor header */
>> +	txd->hdr = EDH_SOP | EDH_EOP | EDH_EOWN |
>> EDH_BCOUNT(length);
>> +
>> +	/* pass buffer address to hardware */
>> +	txd->data_buff = virt_to_phys(packet);
>> +
>> +	debug("%s: %d / .hdr %x, .data_buff %x, .stat %x, .nexted
>> %x\n",
>> +	      __func__, __LINE__, txd->hdr, txd->data_buff, txd
>> ->stat2,
>> +	      txd->next_ed);
>> +
>> +	/* cache flush (packet) */
>> +	__dcache_flush(packet, length);
>> +
>> +	/* cache flush (txd) */
>> +	__dcache_flush(txd, sizeof(*txd));
>> +
>> +	/* pass descriptor table base to h/w */
>> +	writel(virt_to_phys(txd), &ectl_p->txst.raw);
>> +
>> +	/* ready to send enabled, hardware can now send the
>> packet(s) */
>> +	writel(ETHCON_TXRTS | ETHCON_ON, &ectl_p->con1.set);
>> +
>> +	/* wait until tx has completed and h/w has released
>> ownership
>> +	 * of the tx descriptor or timeout elapsed.
>> +	 */
>> +	deadline = get_ticks() + get_tbclk();
>> +	for (;;) {
>> +		/* check timeout */
>> +		if (get_ticks() > deadline)
>> +			return -ETIMEDOUT;
>> +
>> +		if (ctrlc())
>> +			return -EINTR;
>> +
>> +		/* tx completed ? */
>> +		if (readl(&ectl_p->con1.raw) & ETHCON_TXRTS) {
>> +			udelay(1);
>> +			continue;
>> +		}
>> +
>> +		/* h/w not released ownership yet? */
>> +		__dcache_invalidate(txd, sizeof(*txd));
>> +		if (!(txd->hdr & EDH_EOWN))
>> +			break;
>> +	}
>> +
>> +	return 0;
>> +}
>> +
>> +static int pic32_eth_recv(struct udevice *dev, int flags, uchar
>> **packetp)
>> +{
>> +	struct pic32eth_dev *priv = dev_get_priv(dev);
>> +	struct eth_dma_desc *rxd;
>> +	u32 idx = priv->rxd_idx;
>> +	u32 rx_count;
>> +
>> +	/* find the next ready to receive */
>> +	rxd = &priv->rxd_ring[idx];
>> +
>> +	__dcache_invalidate(rxd, sizeof(*rxd));
>> +	/* check if owned by MAC */
>> +	if (rxd->hdr & EDH_EOWN)
>> +		return -EAGAIN;
>> +
>> +	/* Sanity check on header: SOP and EOP  */
>> +	if ((rxd->hdr & (EDH_SOP | EDH_EOP)) != (EDH_SOP | EDH_EOP))
>> {
>> +		printf("%s: %s, rx pkt across multiple descr\n",
>> +		       __FILE__, __func__);
>> +		return 0;
>> +	}
>> +
>> +	debug("%s: %d /idx %i, hdr=%x, data_buff %x, stat %x, nexted
>> %x\n",
>> +	      __func__, __LINE__, idx, rxd->hdr,
>> +	      rxd->data_buff, rxd->stat2, rxd->next_ed);
>> +
>> +	/* Sanity check on rx_stat: OK, CRC */
>> +	if (!RSV_RX_OK(rxd->stat2) || RSV_CRC_ERR(rxd->stat2)) {
>> +		debug("%s: %s: Error, rx problem detected\n",
>> +		      __FILE__, __func__);
>> +		return 0;
>> +	}
>> +
>> +	/* invalidate dcache */
>> +	rx_count = RSV_RX_COUNT(rxd->stat2);
>> +	__dcache_invalidate(net_rx_packets[idx], rx_count);
>> +
>> +	/* Pass the packet to protocol layer */
>> +	*packetp = net_rx_packets[idx];
>> +
>> +	/* increment number of bytes rcvd (ignore CRC) */
>> +	return rx_count - 4;
>> +}
>> +
>> +static int pic32_eth_free_pkt(struct udevice *dev, uchar *packet,
>> int length)
>> +{
>> +	struct pic32eth_dev *priv = dev_get_priv(dev);
>> +	struct pic32_ectl_regs *ectl_p = priv->ectl_regs;
>> +	struct eth_dma_desc *rxd;
>> +	int idx = priv->rxd_idx;
>> +
>> +	/* sanity check */
>> +	if (packet != net_rx_packets[idx]) {
>> +		printf("rxd_id %d: packet is not matched,\n", idx);
>> +		return -EAGAIN;
>> +	}
>> +
>> +	/* prepare for receive */
>> +	rxd = &priv->rxd_ring[idx];
>> +	rxd->hdr = EDH_STICKY | EDH_NPV | EDH_EOWN;
>> +
>> +	__dcache_flush(rxd, sizeof(*rxd));
>> +
>> +	/* decrement rx pkt count */
>> +	writel(ETHCON_BUFCDEC, &ectl_p->con1.set);
>> +
>> +	debug("%s: %d / idx %i, hdr %x, data_buff %x, stat %x,
>> nexted %x\n",
>> +	      __func__, __LINE__, idx, rxd->hdr, rxd->data_buff,
>> +	      rxd->stat2, rxd->next_ed);
>> +
>> +	priv->rxd_idx = (priv->rxd_idx + 1) % MAX_RX_DESCR;
>> +
>> +	return 0;
>> +}
>> +
>> +static const struct eth_ops pic32_eth_ops = {
>> +	.start		= pic32_eth_start,
>> +	.send		= pic32_eth_send,
>> +	.recv		= pic32_eth_recv,
>> +	.free_pkt	= pic32_eth_free_pkt,
>> +	.stop		= pic32_eth_stop,
>> +};
>> +
>> +static int pic32_eth_probe(struct udevice *dev)
>> +{
>> +	struct eth_pdata *pdata = dev_get_platdata(dev);
> your driver private data should be stored in struct pic32eth_dev

Please see below.

>> +	struct pic32eth_dev *priv = dev_get_priv(dev);
>> +	const char *phy_mode;
>> +	void __iomem *iobase;
>> +	fdt_addr_t addr;
>> +	fdt_size_t size;
>> +	int offset = 0;
>> +	int phy_addr = -1;
>> +
>> +	addr = fdtdec_get_addr_size(gd->fdt_blob, dev->of_offset,
>> "reg", &size);
>> +	if (addr == FDT_ADDR_T_NONE)
>> +		return -EINVAL;
>> +
>> +	iobase = ioremap(addr, size);
>> +	if (!iobase)
>> +		return -EINVAL;
> you can drop this check. ioremap() always returns a mapped address.

ack.

>> +
>> +	pdata->iobase = (phys_addr_t)addr;
>> +
>> +	/* get phy mode */
>> +	pdata->phy_interface = -1;
>> +	phy_mode = fdt_getprop(gd->fdt_blob, dev->of_offset, "phy
>> -mode", NULL);
>> +	if (phy_mode)
>> +		pdata->phy_interface =
>> phy_get_interface_by_name(phy_mode);
>> +	if (pdata->phy_interface == -1) {
>> +		debug("%s: Invalid PHY interface '%s'\n", __func__,
>> phy_mode);
>> +		return -EINVAL;
>> +	}
> your driver private data should be stored in struct pic32eth_dev. Only
> if a arch/SoC does not support device-tree, platdata have to be
> initialized with PHY address and PHY interface in the board code. Also
> form a drivers point of view, platdata is read-only and provides board
> -specific configuration for the driver.

Please note here (as you pointed out earlier also) driver private data (as defined by struct pic32eth_dev) is maintained at priv field of udevice. This is sufficient for the driver to work.
On the other hand, u-boot network library expects 'struct eth_pdata' (as platdata) to be filled by the driver instance with correct information (like baseaddr, phy_interface, enetaddr). Network library uses this data structure to preprate, process (create-random-if-unset or download-from-some-storage-based-on-env or some else) and pass to the relevant ethernet device at different time as part of some callback. I have taken inspiration from other net drivers (drivers/net/).

>> +
>> +	/* get phy addr */
>> +	offset = fdtdec_lookup_phandle(gd->fdt_blob, dev->of_offset,
>> +				       "phy-handle");
>> +	if (offset > 0)
>> +		phy_addr = fdtdec_get_int(gd->fdt_blob, offset,
>> "reg", -1);
>> +
>> +	/* phy reset gpio */
>> +	gpio_request_by_name_nodev(gd->fdt_blob, dev->of_offset,
>> +				   "reset-gpios", 0,
>> +				   &priv->rst_gpio, GPIOD_IS_OUT);
>> +
>> +	priv->phyif	= pdata->phy_interface;
>> +	priv->phy_addr	= phy_addr;
>> +	priv->ectl_regs	= iobase;
>> +	priv->emac_regs	= iobase + PIC32_EMAC1CFG1;
>> +
>> +	pic32_mii_init(priv);
>> +
>> +	return pic32_phy_init(priv, dev);
>> +}
>> +
>> +static int pic32_eth_remove(struct udevice *dev)
>> +{
>> +	struct pic32eth_dev *priv = dev_get_priv(dev);
>> +	struct mii_dev *bus;
>> +
>> +	dm_gpio_free(dev, &priv->rst_gpio);
>> +	phy_shutdown(priv->phydev);
>> +	free(priv->phydev);
>> +	bus = miiphy_get_dev_by_name(PIC32_MDIO_NAME);
>> +	mdio_unregister(bus);
>> +	mdio_free(bus);
>> +	iounmap(priv->ectl_regs);
>> +	return 0;
>> +}
>> +
>> +static const struct udevice_id pic32_eth_ids[] = {
>> +	{ .compatible = "microchip,pic32mzda-eth" },
>> +	{ }
>> +};
>> +
>> +U_BOOT_DRIVER(pic32_ethernet) = {
>> +	.name			= "pic32_ethernet",
>> +	.id			= UCLASS_ETH,
>> +	.of_match		= pic32_eth_ids,
>> +	.probe			= pic32_eth_probe,
>> +	.remove			= pic32_eth_remove,
>> +	.ops			= &pic32_eth_ops,
>> +	.priv_auto_alloc_size	= sizeof(struct pic32eth_dev),
>> +	.platdata_auto_alloc_size	= sizeof(struct eth_pdata),
>> +};
>> diff --git a/drivers/net/pic32_eth.h b/drivers/net/pic32_eth.h
>> new file mode 100644
>> index 0000000..4dd443b
>> --- /dev/null
>> +++ b/drivers/net/pic32_eth.h
>> @@ -0,0 +1,171 @@
>> +/*
>> + * (c) 2015 Purna Chandra Mandal <purna.mandal@microchip.com>
>> + *
>> + * SPDX-License-Identifier:	GPL-2.0+
>> + *
>> + */
>> +
>> +#ifndef __MICROCHIP_PIC32_ETH_H_
>> +#define __MICROCHIP_PIC32_ETH_H_
>> +
>> +#include <mach/pic32.h>
>> +
>> +/* Ethernet */
>> +struct pic32_ectl_regs {
>> +	struct pic32_reg_atomic con1; /* 0x00 */
>> +	struct pic32_reg_atomic con2; /* 0x10 */
>> +	struct pic32_reg_atomic txst; /* 0x20 */
>> +	struct pic32_reg_atomic rxst; /* 0x30 */
>> +	struct pic32_reg_atomic ht0;  /* 0x40 */
>> +	struct pic32_reg_atomic ht1;  /* 0x50 */
>> +	struct pic32_reg_atomic pmm0; /* 0x60 */
>> +	struct pic32_reg_atomic pmm1; /* 0x70 */
>> +	struct pic32_reg_atomic pmcs; /* 0x80 */
>> +	struct pic32_reg_atomic pmo;  /* 0x90 */
>> +	struct pic32_reg_atomic rxfc; /* 0xa0 */
>> +	struct pic32_reg_atomic rxwm; /* 0xb0 */
>> +	struct pic32_reg_atomic ien;  /* 0xc0 */
>> +	struct pic32_reg_atomic irq;  /* 0xd0 */
>> +	struct pic32_reg_atomic stat; /* 0xe0 */
>> +};
>> +
>> +struct pic32_mii_regs {
>> +	struct pic32_reg_atomic mcfg; /* 0x280 */
>> +	struct pic32_reg_atomic mcmd; /* 0x290 */
>> +	struct pic32_reg_atomic madr; /* 0x2a0 */
>> +	struct pic32_reg_atomic mwtd; /* 0x2b0 */
>> +	struct pic32_reg_atomic mrdd; /* 0x2c0 */
>> +	struct pic32_reg_atomic mind; /* 0x2d0 */
>> +};
>> +
>> +struct pic32_emac_regs {
>> +	struct pic32_reg_atomic cfg1; /* 0x200*/
>> +	struct pic32_reg_atomic cfg2; /* 0x210*/
>> +	struct pic32_reg_atomic ipgt; /* 0x220*/
>> +	struct pic32_reg_atomic ipgr; /* 0x230*/
>> +	struct pic32_reg_atomic clrt; /* 0x240*/
>> +	struct pic32_reg_atomic maxf; /* 0x250*/
>> +	struct pic32_reg_atomic supp; /* 0x260*/
>> +	struct pic32_reg_atomic test; /* 0x270*/
>> +	struct pic32_mii_regs mii;    /* 0x280 - 0x2d0 */
>> +	struct pic32_reg_atomic res1; /* 0x2e0 */
>> +	struct pic32_reg_atomic res2; /* 0x2f0 */
>> +	struct pic32_reg_atomic sa0;  /* 0x300 */
>> +	struct pic32_reg_atomic sa1;  /* 0x310 */
>> +	struct pic32_reg_atomic sa2;  /* 0x320 */
>> +};
>> +
>> +/* ETHCON1 Reg field */
>> +#define ETHCON_BUFCDEC		BIT(0)
>> +#define ETHCON_RXEN		BIT(8)
>> +#define ETHCON_TXRTS		BIT(9)
>> +#define ETHCON_ON		BIT(15)
>> +
>> +/* ETHCON2 Reg field */
>> +#define ETHCON_RXBUFSZ		0x7f
>> +#define ETHCON_RXBUFSZ_SHFT	0x4
>> +
>> +/* ETHSTAT Reg field */
>> +#define ETHSTAT_BUSY		BIT(7)
>> +#define ETHSTAT_BUFCNT		0x00ff0000
>> +
>> +/* ETHRXFC Register fields */
>> +#define ETHRXFC_BCEN		BIT(0)
>> +#define ETHRXFC_MCEN		BIT(1)
>> +#define ETHRXFC_UCEN		BIT(3)
>> +#define ETHRXFC_RUNTEN		BIT(4)
>> +#define ETHRXFC_CRCOKEN		BIT(5)
>> +
>> +/* EMAC1CFG1 register offset */
>> +#define PIC32_EMAC1CFG1		0x0200
>> +
>> +/* EMAC1CFG1 register fields */
>> +#define EMAC_RXENABLE		BIT(0)
>> +#define EMAC_RXPAUSE		BIT(2)
>> +#define EMAC_TXPAUSE		BIT(3)
>> +#define EMAC_SOFTRESET		BIT(15)
>> +
>> +/* EMAC1CFG2 register fields */
>> +#define EMAC_FULLDUP		BIT(0)
>> +#define EMAC_LENGTHCK		BIT(1)
>> +#define EMAC_CRCENABLE		BIT(4)
>> +#define EMAC_PADENABLE		BIT(5)
>> +#define EMAC_AUTOPAD		BIT(7)
>> +#define EMAC_EXCESS		BIT(14)
>> +
>> +/* EMAC1IPGT register magic */
>> +#define FULLDUP_GAP_TIME	0x15
>> +#define HALFDUP_GAP_TIME	0x12
>> +
>> +/* EMAC1SUPP register fields */
>> +#define EMAC_RMII_SPD100	BIT(8)
>> +#define EMAC_RMII_RESET		BIT(11)
>> +
>> +/* MII Management Configuration Register */
>> +#define MIIMCFG_RSTMGMT		BIT(15)
>> +#define MIIMCFG_CLKSEL_DIV40	0x0020	/* 100Mhz / 40 */
>> +
>> +/* MII Management Command Register */
>> +#define MIIMCMD_READ		BIT(0)
>> +#define MIIMCMD_SCAN		BIT(1)
>> +
>> +/* MII Management Address Register */
>> +#define MIIMADD_REGADDR		0x1f
>> +#define MIIMADD_REGADDR_SHIFT	0
>> +#define MIIMADD_PHYADDR_SHIFT	8
>> +
>> +/* MII Management Indicator Register */
>> +#define MIIMIND_BUSY		BIT(0)
>> +#define MIIMIND_NOTVALID	BIT(2)
>> +#define MIIMIND_LINKFAIL	BIT(3)
>> +
>> +/* Packet Descriptor */
>> +/* Received Packet Status */
>> +#define _RSV1_PKT_CSUM		0xffff
>> +#define _RSV2_CRC_ERR		BIT(20)
>> +#define _RSV2_LEN_ERR		BIT(21)
>> +#define _RSV2_RX_OK		BIT(23)
>> +#define _RSV2_RX_COUNT		0xffff
>> +
>> +#define RSV_RX_CSUM(__rsv1)	((__rsv1) & _RSV1_PKT_CSUM)
>> +#define RSV_RX_COUNT(__rsv2)	((__rsv2) & _RSV2_RX_COUNT)
>> +#define RSV_RX_OK(__rsv2)	((__rsv2) & _RSV2_RX_OK)
>> +#define RSV_CRC_ERR(__rsv2)	((__rsv2) & _RSV2_CRC_ERR)
>> +
>> +/* Ethernet Hardware Descriptor Header bits */
>> +#define EDH_EOWN		BIT(7)
>> +#define EDH_NPV			BIT(8)
>> +#define EDH_STICKY		BIT(9)
>> +#define _EDH_BCOUNT		0x07ff0000
>> +#define EDH_EOP			BIT(30)
>> +#define EDH_SOP			BIT(31)
>> +#define EDH_BCOUNT_SHIFT	16
>> +#define EDH_BCOUNT(len)		((len) << EDH_BCOUNT_SHIFT)
>> +
>> +/* Ethernet Hardware Descriptors
>> + * ref: PIC32 Family Reference Manual Table 35-7
>> + * This structure represents the layout of the DMA
>> + * memory shared between the CPU and the Ethernet
>> + * controller.
>> + */
>> +/* TX/RX DMA descriptor */
>> +struct eth_dma_desc {
>> +	u32 hdr;	/* header */
>> +	u32 data_buff;	/* data buffer address */
>> +	u32 stat1;	/* transmit/receive packet status */
>> +	u32 stat2;	/* transmit/receive packet status */
>> +	u32 next_ed;	/* next descriptor */
>> +};
>> +
>> +/* cache operation helper */
>> +#define __dcache_flush(__a, __l) \
>> +	flush_dcache_range((ulong)(__a),  ((__l) + (ulong)(__a)))
>> +
>> +#define __dcache_invalidate(__a, __l) \
>> +	invalidate_dcache_range((ulong)(__a),  ((__l) +
>> (ulong)(__a)))
>> +
>> +#define PIC32_MDIO_NAME "PIC32_EMAC"
>> +
>> +int pic32_mdio_init(const char *name, ulong ioaddr);
>> +
>> +#endif /* __MICROCHIP_PIC32_ETH_H_*/
>> diff --git a/drivers/net/pic32_mdio.c b/drivers/net/pic32_mdio.c
>> new file mode 100644
>> index 0000000..578fc96
>> --- /dev/null
>> +++ b/drivers/net/pic32_mdio.c
>> @@ -0,0 +1,121 @@
>> +/*
>> + * pic32_mdio.c: PIC32 MDIO/MII driver, part of pic32_eth.c.
>> + *
>> + * Copyright 2015 Microchip Inc.
>> + *	Purna Chandra Mandal <purna.mandal@microchip.com>
>> + *
>> + * SPDX-License-Identifier:	GPL-2.0+
>> + */
>> +#include <common.h>
>> +#include <phy.h>
>> +#include <miiphy.h>
>> +#include <errno.h>
>> +#include <wait_bit.h>
>> +#include <asm/io.h>
>> +#include "pic32_eth.h"
>> +
>> +static int pic32_mdio_write(struct mii_dev *bus,
>> +			    int addr, int dev_addr,
>> +			    int reg, u16 value)
>> +{
>> +	u32 v;
>> +	struct pic32_mii_regs *mii_regs = bus->priv;
>> +
>> +	/* Wait for the previous operation to finish */
>> +	wait_for_bit(__func__, &mii_regs->mind.raw, MIIMIND_BUSY,
>> +		     false, CONFIG_SYS_HZ, true);
>> +
>> +	/* Put phyaddr and regaddr into MIIMADD */
>> +	v = (addr << MIIMADD_PHYADDR_SHIFT) | (reg &
>> MIIMADD_REGADDR);
>> +	writel(v, &mii_regs->madr.raw);
>> +
>> +	/* Initiate a write command */
>> +	writel(value, &mii_regs->mwtd.raw);
>> +
>> +	/* Wait 30 clock cycles for busy flag to be set */
>> +	udelay(12);
>> +
>> +	/* Wait for write to complete */
>> +	wait_for_bit(__func__, &mii_regs->mind.raw, MIIMIND_BUSY,
>> +		     false, CONFIG_SYS_HZ, true);
>> +
>> +	return 0;
>> +}
>> +
>> +static int pic32_mdio_read(struct mii_dev *bus, int addr, int
>> devaddr, int reg)
>> +{
>> +	u32 v;
>> +	struct pic32_mii_regs *mii_regs = bus->priv;
>> +
>> +	/* Wait for the previous operation to finish */
>> +	wait_for_bit(__func__, &mii_regs->mind.raw, MIIMIND_BUSY,
>> +		     false, CONFIG_SYS_HZ, true);
>> +
>> +	/* Put phyaddr and regaddr into MIIMADD */
>> +	v = (addr << MIIMADD_PHYADDR_SHIFT) | (reg &
>> MIIMADD_REGADDR);
>> +	writel(v, &mii_regs->madr.raw);
>> +
>> +	/* Initiate a read command */
>> +	writel(MIIMCMD_READ, &mii_regs->mcmd.raw);
>> +
>> +	/* Wait 30 clock cycles for busy flag to be set */
>> +	udelay(12);
>> +
>> +	/* Wait for read to complete */
>> +	wait_for_bit(__func__, &mii_regs->mind.raw,
>> +		     MIIMIND_NOTVALID | MIIMIND_BUSY,
>> +		     false, CONFIG_SYS_HZ, false);
>> +
>> +	/* Clear the command register */
>> +	writel(0, &mii_regs->mcmd.raw);
>> +
>> +	/* Grab the value read from the PHY */
>> +	v = readl(&mii_regs->mrdd.raw);
>> +	return v;
>> +}
>> +
>> +static int pic32_mdio_reset(struct mii_dev *bus)
>> +{
>> +	struct pic32_mii_regs *mii_regs = bus->priv;
>> +
>> +	/* Reset MII (due to new addresses) */
>> +	writel(MIIMCFG_RSTMGMT, &mii_regs->mcfg.raw);
>> +
>> +	/* Wait for the operation to finish */
>> +	wait_for_bit(__func__, &mii_regs->mind.raw, MIIMIND_BUSY,
>> +		     false, CONFIG_SYS_HZ, true);
>> +
>> +	/* Clear reset bit */
>> +	writel(0, &mii_regs->mcfg);
>> +
>> +	/* Wait for the operation to finish */
>> +	wait_for_bit(__func__, &mii_regs->mind.raw, MIIMIND_BUSY,
>> +		     false, CONFIG_SYS_HZ, true);
>> +
>> +	/* Set the MII Management Clock (MDC) - no faster than 2.5
>> MHz */
>> +	writel(MIIMCFG_CLKSEL_DIV40, &mii_regs->mcfg.raw);
>> +
>> +	/* Wait for the operation to finish */
>> +	wait_for_bit(__func__, &mii_regs->mind.raw, MIIMIND_BUSY,
>> +		     false, CONFIG_SYS_HZ, true);
>> +	return 0;
>> +}
>> +
>> +int pic32_mdio_init(const char *name, ulong ioaddr)
>> +{
>> +	struct mii_dev *bus;
>> +
>> +	bus = mdio_alloc();
>> +	if (!bus) {
>> +		printf("Failed to allocate PIC32-MDIO bus\n");
>> +		return -ENOMEM;
>> +	}
>> +
>> +	bus->read = pic32_mdio_read;
>> +	bus->write = pic32_mdio_write;
>> +	bus->reset = pic32_mdio_reset;
>> +	strncpy(bus->name, name, sizeof(bus->name));
>> +	bus->priv = (void *)ioaddr;
>> +
>> +	return mdio_register(bus);
>> +}
Purna Chandra Mandal Jan. 14, 2016, 10:29 a.m. UTC | #4
On 01/13/2016 08:26 PM, Tom Rini wrote:

> On Tue, Jan 12, 2016 at 03:48:28PM +0530, Purna Chandra Mandal wrote:
>
>> This driver implements MAC and MII layer of the ethernet controller.
>> Network data transfer is handled by controller internal DMA engine.
>> Ethernet controller is configurable through device-tree file.
>>
>> Signed-off-by: Purna Chandra Mandal <purna.mandal@microchip.com>
> [snip]
>> +/* cache operation helper */
>> +#define __dcache_flush(__a, __l) \
>> +	flush_dcache_range((ulong)(__a),  ((__l) + (ulong)(__a)))
>> +
>> +#define __dcache_invalidate(__a, __l) \
>> +	invalidate_dcache_range((ulong)(__a),  ((__l) + (ulong)(__a)))
> Why using these helper functions instead of directly?  Yes, we may be
> casting in some cases and if that's how it must be, so be it (it's how
> we're doing it in other drivers).  Thanks!

Thanks, Will drop these helpers/macros.
Daniel Schwierzeck Jan. 14, 2016, 2:01 p.m. UTC | #5
Am Donnerstag, den 14.01.2016, 15:35 +0530 schrieb Purna Chandra
Mandal:
> On 01/13/2016 09:07 PM, Daniel Schwierzeck wrote:
> 
> > Am Dienstag, den 12.01.2016, 15:48 +0530 schrieb Purna Chandra
> > Mandal:
> > > This driver implements MAC and MII layer of the ethernet
> > > controller.
> > > Network data transfer is handled by controller internal DMA
> > > engine.
> > > Ethernet controller is configurable through device-tree file.
> > > 
> > > Signed-off-by: Purna Chandra Mandal <purna.mandal@microchip.com>
> > > 
> > > 
> > > ---
> > > 
> > > Changes in v3:
> > > - merge wrappers with eth operation callbacks
> > > - read phy address from device-tree
> > > - rename functions (e.g. _eth_xyz() with pic32_eth_xyz())
> > > 
> > > Changes in v2: None
> > > 
> > >  drivers/net/Kconfig      |   7 +
> > >  drivers/net/Makefile     |   1 +
> > >  drivers/net/pic32_eth.c  | 606
> > > +++++++++++++++++++++++++++++++++++++++++++++++
> > >  drivers/net/pic32_eth.h  | 171 +++++++++++++
> > >  drivers/net/pic32_mdio.c | 121 ++++++++++
> > >  5 files changed, 906 insertions(+)
> > >  create mode 100644 drivers/net/pic32_eth.c
> > >  create mode 100644 drivers/net/pic32_eth.h
> > >  create mode 100644 drivers/net/pic32_mdio.c
> > > 
> > > diff --git a/drivers/net/Kconfig b/drivers/net/Kconfig
> > > index ae5e78d..dc49493 100644
> > > --- a/drivers/net/Kconfig
> > > +++ b/drivers/net/Kconfig
> > > @@ -108,4 +108,11 @@ config ZYNQ_GEM
> > >  	help
> > >  	  This MAC is present in Xilinx Zynq and ZynqMP SoCs.
> > >  
> > > +config PIC32_ETH
> > > +	bool "Microchip PIC32 Ethernet Support"
> > > +	depends on MACH_PIC32
> > should be
> > 
> > depends on DM_ETH && MACH_PIC32
> > select PHYLIB
> 
> ack.
> 
> > > +	help
> > > +	  This driver implements 10/100 Mbps Ethernet and MAC
> > > layer
> > > for
> > > +	  Microchip PIC32 microcontrollers.
> > > +
> > >  endif # NETDEVICES
> > > diff --git a/drivers/net/Makefile b/drivers/net/Makefile
> > > index 150470c..33a81ee 100644
> > > --- a/drivers/net/Makefile
> > > +++ b/drivers/net/Makefile
> > > @@ -72,3 +72,4 @@ obj-$(CONFIG_FSL_MC_ENET) += fsl-mc/
> > >  obj-$(CONFIG_FSL_MC_ENET) += ldpaa_eth/
> > >  obj-$(CONFIG_FSL_MEMAC) += fm/memac_phy.o
> > >  obj-$(CONFIG_VSC9953) += vsc9953.o
> > > +obj-$(CONFIG_PIC32_ETH) += pic32_mdio.o pic32_eth.o
> > > diff --git a/drivers/net/pic32_eth.c b/drivers/net/pic32_eth.c
> > > new file mode 100644
> > > index 0000000..1cef62e
> > > --- /dev/null
> > > +++ b/drivers/net/pic32_eth.c
> > > @@ -0,0 +1,606 @@
> > > +/*
> > > + * (c) 2015 Purna Chandra Mandal <purna.mandal@microchip.com>
> > > + *
> > > + * SPDX-License-Identifier:	GPL-2.0+
> > > + *
> > > + */
> > > +#include <common.h>
> > > +#include <errno.h>
> > > +#include <dm.h>
> > > +#include <net.h>
> > > +#include <miiphy.h>
> > > +#include <console.h>
> > > +#include <wait_bit.h>
> > > +#include <asm/gpio.h>
> > > +
> > > +#include "pic32_eth.h"
> > > +
> > > +#define MAX_RX_BUF_SIZE		1536
> > > +#define MAX_RX_DESCR		PKTBUFSRX
> > > +#define MAX_TX_DESCR		2
> > > +
> > > +DECLARE_GLOBAL_DATA_PTR;
> > > +
> > > +struct pic32eth_dev {
> > > +	struct eth_dma_desc rxd_ring[MAX_RX_DESCR];
> > > +	struct eth_dma_desc txd_ring[MAX_TX_DESCR];
> > > +	u32 rxd_idx; /* index of RX desc to read */
> > > +	/* regs */
> > > +	struct pic32_ectl_regs *ectl_regs;
> > > +	struct pic32_emac_regs *emac_regs;
> > > +	/* Phy */
> > > +	struct phy_device *phydev;
> > > +	phy_interface_t phyif;
> > > +	u32 phy_addr;
> > > +	struct gpio_desc rst_gpio;
> > > +};
> > > +
> > > +void __weak board_netphy_reset(void *dev)
> > > +{
> > > +	struct pic32eth_dev *priv = (struct pic32eth_dev *)dev;
> > the cast is not necessary
> 
> ack. Will remove,
> 
> > > +
> > > +	if (!dm_gpio_is_valid(&priv->rst_gpio))
> > > +		return;
> > > +
> > > +	/* phy reset */
> > > +	dm_gpio_set_value(&priv->rst_gpio, 0);
> > > +	udelay(300);
> > > +	dm_gpio_set_value(&priv->rst_gpio, 1);
> > > +	udelay(300);
> > > +}
> > > +
> > > +/* Initialize mii(MDIO) interface, discover which PHY is
> > > + * attached to the device, and configure it properly.
> > > + */
> > > +static int pic32_mii_init(struct pic32eth_dev *priv)
> > > +{
> > > +	struct pic32_ectl_regs *ectl_p = priv->ectl_regs;
> > > +	struct pic32_emac_regs *emac_p = priv->emac_regs;
> > > +
> > > +	/* board phy reset */
> > > +	board_netphy_reset(priv);
> > > +
> > > +	/* disable RX, TX & all transactions */
> > > +	writel(ETHCON_ON | ETHCON_TXRTS | ETHCON_RXEN, &ectl_p
> > > ->con1.clr);
> > > +
> > > +	/* wait till busy */
> > > +	wait_for_bit(__func__, &ectl_p->stat.raw, ETHSTAT_BUSY,
> > > false,
> > > +		     CONFIG_SYS_HZ, false);
> > > +
> > > +	/* turn controller ON to access PHY over MII */
> > > +	writel(ETHCON_ON, &ectl_p->con1.set);
> > > +
> > > +	mdelay(10);
> > > +
> > > +	/* reset MAC */
> > > +	writel(EMAC_SOFTRESET, &emac_p->cfg1.set); /* reset
> > > assert
> > > */
> > > +	mdelay(10);
> > > +	writel(EMAC_SOFTRESET, &emac_p->cfg1.clr); /* reset
> > > deassert
> > > */
> > > +
> > > +	/* initialize MDIO/MII */
> > > +	if (priv->phyif == PHY_INTERFACE_MODE_RMII) {
> > > +		writel(EMAC_RMII_RESET, &emac_p->supp.set);
> > > +		mdelay(10);
> > > +		writel(EMAC_RMII_RESET, &emac_p->supp.clr);
> > > +	}
> > > +
> > > +	return pic32_mdio_init(PIC32_MDIO_NAME, (ulong)&emac_p
> > > ->mii);
> > > +}
> > > +
> > > +static int pic32_phy_init(struct pic32eth_dev *priv, struct
> > > udevice
> > > *dev)
> > > +{
> > > +	struct mii_dev *mii;
> > > +
> > > +	mii = miiphy_get_dev_by_name(PIC32_MDIO_NAME);
> > > +
> > > +	/* find & connect PHY */
> > > +	priv->phydev = phy_connect(mii, priv->phy_addr,
> > > +				   dev, priv->phyif);
> > > +	if (!priv->phydev) {
> > > +		printf("%s: %s: Error, PHY connect\n", __FILE__,
> > > __func__);
> > > +		return 0;
> > > +	}
> > > +
> > > +	/* Wait for phy to complete reset */
> > > +	mdelay(10);
> > > +
> > > +	/* configure supported modes */
> > > +	priv->phydev->supported = SUPPORTED_10baseT_Half |
> > > +				  SUPPORTED_10baseT_Full |
> > > +				  SUPPORTED_100baseT_Half |
> > > +				  SUPPORTED_100baseT_Full |
> > > +				  SUPPORTED_Autoneg;
> > > +
> > > +	priv->phydev->advertising = ADVERTISED_10baseT_Half |
> > > +				    ADVERTISED_10baseT_Full |
> > > +				    ADVERTISED_100baseT_Half |
> > > +				    ADVERTISED_100baseT_Full |
> > > +				    ADVERTISED_Autoneg;
> > > +
> > > +	priv->phydev->autoneg = AUTONEG_ENABLE;
> > > +
> > > +	return 0;
> > > +}
> > > +
> > > +/* Configure MAC based on negotiated speed and duplex
> > > + * reported by PHY.
> > > + */
> > > +static int pic32_mac_adjust_link(struct pic32eth_dev *priv)
> > > +{
> > > +	struct phy_device *phydev = priv->phydev;
> > > +	struct pic32_emac_regs *emac_p = priv->emac_regs;
> > > +
> > > +	if (!phydev->link) {
> > > +		printf("%s: No link.\n", phydev->dev->name);
> > > +		return -EINVAL;
> > > +	}
> > > +
> > > +	if (phydev->duplex) {
> > > +		writel(EMAC_FULLDUP, &emac_p->cfg2.set);
> > > +		writel(FULLDUP_GAP_TIME, &emac_p->ipgt.raw);
> > > +	} else {
> > > +		writel(EMAC_FULLDUP, &emac_p->cfg2.clr);
> > > +		writel(HALFDUP_GAP_TIME, &emac_p->ipgt.raw);
> > > +	}
> > > +
> > > +	switch (phydev->speed) {
> > > +	case SPEED_100:
> > > +		writel(EMAC_RMII_SPD100, &emac_p->supp.set);
> > > +		break;
> > > +	case SPEED_10:
> > > +		writel(EMAC_RMII_SPD100, &emac_p->supp.clr);
> > > +		break;
> > > +	default:
> > > +		printf("%s: Speed was bad\n", phydev->dev
> > > ->name);
> > > +		return -EINVAL;
> > > +	}
> > > +
> > > +	printf("pic32eth: PHY is %s with %dbase%s, %s\n",
> > > +	       phydev->drv->name, phydev->speed,
> > > +	       (phydev->port == PORT_TP) ? "T" : "X",
> > > +	       (phydev->duplex) ? "full" : "half");
> > > +
> > > +	return 0;
> > > +}
> > > +
> > > +static void pic32_mac_init(struct pic32eth_dev *priv, u8
> > > *macaddr)
> > > +{
> > > +	struct pic32_emac_regs *emac_p = priv->emac_regs;
> > > +	u32 stat = 0, v;
> > > +	u64 expire;
> > > +
> > > +	v = EMAC_TXPAUSE | EMAC_RXPAUSE | EMAC_RXENABLE;
> > > +	writel(v, &emac_p->cfg1.raw);
> > > +
> > > +	v = EMAC_EXCESS | EMAC_AUTOPAD | EMAC_PADENABLE |
> > > +	    EMAC_CRCENABLE | EMAC_LENGTHCK | EMAC_FULLDUP;
> > > +	writel(v, &emac_p->cfg2.raw);
> > > +
> > > +	/* recommended back-to-back inter-packet gap for 10 Mbps
> > > half duplex */
> > > +	writel(HALFDUP_GAP_TIME, &emac_p->ipgt.raw);
> > > +
> > > +	/* recommended non-back-to-back interpacket gap is 0xc12
> > > */
> > > +	writel(0xc12, &emac_p->ipgr.raw);
> > > +
> > > +	/* recommended collision window retry limit is 0x370F */
> > > +	writel(0x370f, &emac_p->clrt.raw);
> > > +
> > > +	/* set maximum frame length: allow VLAN tagged frame */
> > > +	writel(0x600, &emac_p->maxf.raw);
> > > +
> > > +	/* set the mac address */
> > > +	writel(macaddr[0] | (macaddr[1] << 8), &emac_p
> > > ->sa2.raw);
> > > +	writel(macaddr[2] | (macaddr[3] << 8), &emac_p
> > > ->sa1.raw);
> > > +	writel(macaddr[4] | (macaddr[5] << 8), &emac_p
> > > ->sa0.raw);
> > > +
> > > +	/* default, enable 10 Mbps operation */
> > > +	writel(EMAC_RMII_SPD100, &emac_p->supp.clr);
> > > +
> > > +	/* wait until link status UP or deadline elapsed */
> > > +	expire = get_ticks() + get_tbclk() * 2;
> > > +	for (; get_ticks() < expire;) {
> > > +		stat = phy_read(priv->phydev, priv->phy_addr,
> > > MII_BMSR);
> > > +		if (stat & BMSR_LSTATUS)
> > > +			break;
> > > +	}
> > > +
> > > +	if (!(stat & BMSR_LSTATUS))
> > > +		printf("MAC: Link is DOWN!\n");
> > > +
> > > +	/* delay to stabilize before any tx/rx */
> > > +	mdelay(10);
> > > +}
> > > +
> > > +static void pic32_mac_reset(struct pic32eth_dev *priv)
> > > +{
> > > +	struct pic32_emac_regs *emac_p = priv->emac_regs;
> > > +	struct mii_dev *mii;
> > > +
> > > +	/* Reset MAC */
> > > +	writel(EMAC_SOFTRESET, &emac_p->cfg1.raw);
> > > +	mdelay(10);
> > > +
> > > +	/* clear reset */
> > > +	writel(0, &emac_p->cfg1.raw);
> > > +
> > > +	/* Reset MII */
> > > +	mii = priv->phydev->bus;
> > > +	if (mii && mii->reset)
> > > +		mii->reset(mii);
> > > +}
> > > +
> > > +/* initializes the MAC and PHY, then establishes a link */
> > > +static void pic32_ctrl_reset(struct pic32eth_dev *priv)
> > > +{
> > > +	struct pic32_ectl_regs *ectl_p = priv->ectl_regs;
> > > +	u32 v;
> > > +
> > > +	/* disable RX, TX & any other transactions */
> > > +	writel(ETHCON_ON | ETHCON_TXRTS | ETHCON_RXEN, &ectl_p
> > > ->con1.clr);
> > > +
> > > +	/* wait till busy */
> > > +	wait_for_bit(__func__, &ectl_p->stat.raw, ETHSTAT_BUSY,
> > > false,
> > > +		     CONFIG_SYS_HZ, false);
> > > +	/* decrement received buffcnt to zero. */
> > > +	while (readl(&ectl_p->stat.raw) & ETHSTAT_BUFCNT)
> > > +		writel(ETHCON_BUFCDEC, &ectl_p->con1.set);
> > > +
> > > +	/* clear any existing interrupt event */
> > > +	writel(0xffffffff, &ectl_p->irq.clr);
> > > +
> > > +	/* clear RX/TX start address */
> > > +	writel(0xffffffff, &ectl_p->txst.clr);
> > > +	writel(0xffffffff, &ectl_p->rxst.clr);
> > > +
> > > +	/* clear the receive filters */
> > > +	writel(0x00ff, &ectl_p->rxfc.clr);
> > > +
> > > +	/* set the receive filters
> > > +	 * ETH_FILT_CRC_ERR_REJECT
> > > +	 * ETH_FILT_RUNT_REJECT
> > > +	 * ETH_FILT_UCAST_ACCEPT
> > > +	 * ETH_FILT_MCAST_ACCEPT
> > > +	 * ETH_FILT_BCAST_ACCEPT
> > > +	 */
> > > +	v = ETHRXFC_BCEN | ETHRXFC_MCEN | ETHRXFC_UCEN |
> > > +	    ETHRXFC_RUNTEN | ETHRXFC_CRCOKEN;
> > > +	writel(v, &ectl_p->rxfc.set);
> > > +
> > > +	/* turn controller ON to access PHY over MII */
> > > +	writel(ETHCON_ON, &ectl_p->con1.set);
> > > +}
> > > +
> > > +static void pic32_rx_desc_init(struct pic32eth_dev *priv)
> > > +{
> > > +	struct pic32_ectl_regs *ectl_p = priv->ectl_regs;
> > > +	struct eth_dma_desc *rxd;
> > > +	u32 idx, bufsz;
> > > +
> > > +	priv->rxd_idx = 0;
> > > +	for (idx = 0; idx < MAX_RX_DESCR; idx++) {
> > > +		rxd = &priv->rxd_ring[idx];
> > > +
> > > +		/* hw owned */
> > > +		rxd->hdr = EDH_NPV | EDH_EOWN | EDH_STICKY;
> > > +
> > > +		/* packet buffer address */
> > > +		rxd->data_buff =
> > > virt_to_phys(net_rx_packets[idx]);
> > > +
> > > +		/* link to next desc */
> > > +		rxd->next_ed = virt_to_phys(rxd + 1);
> > > +
> > > +		/* reset status */
> > > +		rxd->stat1 = 0;
> > > +		rxd->stat2 = 0;
> > > +
> > > +		/* decrement bufcnt */
> > > +		writel(ETHCON_BUFCDEC, &ectl_p->con1.set);
> > > +	}
> > > +
> > > +	/* link last descr to beginning of list */
> > > +	rxd->next_ed = virt_to_phys(&priv->rxd_ring[0]);
> > > +
> > > +	/* flush rx ring */
> > > +	__dcache_flush(priv->rxd_ring, sizeof(priv->rxd_ring));
> > > +
> > > +	/* set rx desc-ring start address */
> > > +	writel((ulong)virt_to_phys(&priv->rxd_ring[0]), &ectl_p
> > > ->rxst.raw);
> > > +
> > > +	/* RX Buffer size */
> > > +	bufsz = readl(&ectl_p->con2.raw);
> > > +	bufsz &= ~(ETHCON_RXBUFSZ << ETHCON_RXBUFSZ_SHFT);
> > > +	bufsz |= ((MAX_RX_BUF_SIZE / 16) <<
> > > ETHCON_RXBUFSZ_SHFT);
> > > +	writel(bufsz, &ectl_p->con2.raw);
> > > +
> > > +	/* enable the receiver in hardware which allows hardware
> > > +	 * to DMA received pkts to the descriptor pointer
> > > address.
> > > +	 */
> > > +	writel(ETHCON_RXEN, &ectl_p->con1.set);
> > > +}
> > > +
> > > +static int pic32_eth_start(struct udevice *dev)
> > > +{
> > > +	struct eth_pdata *pdata = dev_get_platdata(dev);
> > > +	struct pic32eth_dev *priv = dev_get_priv(dev);
> > > +
> > > +	/* controller */
> > > +	pic32_ctrl_reset(priv);
> > > +
> > > +	/* reset MAC */
> > > +	pic32_mac_reset(priv);
> > > +
> > > +	/* configure PHY */
> > > +	phy_config(priv->phydev);
> > > +
> > > +	/* initialize MAC */
> > > +	pic32_mac_init(priv, &pdata->enetaddr[0]);
> > > +
> > > +	/* init RX descriptor; TX descriptors are handled in
> > > xmit */
> > > +	pic32_rx_desc_init(priv);
> > > +
> > > +	/* Start up & update link status of PHY */
> > > +	phy_startup(priv->phydev);
> > > +
> > > +	/* adjust mac with phy link status */
> > > +	return pic32_mac_adjust_link(priv);
> > > +}
> > > +
> > > +static void pic32_eth_stop(struct udevice *dev)
> > > +{
> > > +	struct pic32eth_dev *priv = dev_get_priv(dev);
> > > +	struct pic32_ectl_regs *ectl_p = priv->ectl_regs;
> > > +	struct pic32_emac_regs *emac_p = priv->emac_regs;
> > > +
> > > +	/* Reset the phy if the controller is enabled */
> > > +	if (readl(&ectl_p->con1.raw) & ETHCON_ON)
> > > +		phy_reset(priv->phydev);
> > > +
> > > +	/* Shut down the PHY */
> > > +	phy_shutdown(priv->phydev);
> > > +
> > > +	/* Stop rx/tx */
> > > +	writel(ETHCON_TXRTS | ETHCON_RXEN, &ectl_p->con1.clr);
> > > +	mdelay(10);
> > > +
> > > +	/* reset MAC */
> > > +	writel(EMAC_SOFTRESET, &emac_p->cfg1.raw);
> > > +
> > > +	/* clear reset */
> > > +	writel(0, &emac_p->cfg1.raw);
> > > +	mdelay(10);
> > > +
> > > +	/* disable controller */
> > > +	writel(ETHCON_ON, &ectl_p->con1.clr);
> > > +	mdelay(10);
> > > +
> > > +	/* wait until everything is down */
> > > +	wait_for_bit(__func__, &ectl_p->stat.raw, ETHSTAT_BUSY,
> > > false,
> > > +		     2 * CONFIG_SYS_HZ, false);
> > > +
> > > +	/* clear any existing interrupt event */
> > > +	writel(0xffffffff, &ectl_p->irq.clr);
> > > +}
> > > +
> > > +static int pic32_eth_send(struct udevice *dev, void *packet, int
> > > length)
> > > +{
> > > +	struct pic32eth_dev *priv = dev_get_priv(dev);
> > > +	struct pic32_ectl_regs *ectl_p = priv->ectl_regs;
> > > +	struct eth_dma_desc *txd;
> > > +	u64 deadline;
> > > +
> > > +	txd = &priv->txd_ring[0];
> > > +
> > > +	/* set proper flags & length in descriptor header */
> > > +	txd->hdr = EDH_SOP | EDH_EOP | EDH_EOWN |
> > > EDH_BCOUNT(length);
> > > +
> > > +	/* pass buffer address to hardware */
> > > +	txd->data_buff = virt_to_phys(packet);
> > > +
> > > +	debug("%s: %d / .hdr %x, .data_buff %x, .stat %x,
> > > .nexted
> > > %x\n",
> > > +	      __func__, __LINE__, txd->hdr, txd->data_buff, txd
> > > ->stat2,
> > > +	      txd->next_ed);
> > > +
> > > +	/* cache flush (packet) */
> > > +	__dcache_flush(packet, length);
> > > +
> > > +	/* cache flush (txd) */
> > > +	__dcache_flush(txd, sizeof(*txd));
> > > +
> > > +	/* pass descriptor table base to h/w */
> > > +	writel(virt_to_phys(txd), &ectl_p->txst.raw);
> > > +
> > > +	/* ready to send enabled, hardware can now send the
> > > packet(s) */
> > > +	writel(ETHCON_TXRTS | ETHCON_ON, &ectl_p->con1.set);
> > > +
> > > +	/* wait until tx has completed and h/w has released
> > > ownership
> > > +	 * of the tx descriptor or timeout elapsed.
> > > +	 */
> > > +	deadline = get_ticks() + get_tbclk();
> > > +	for (;;) {
> > > +		/* check timeout */
> > > +		if (get_ticks() > deadline)
> > > +			return -ETIMEDOUT;
> > > +
> > > +		if (ctrlc())
> > > +			return -EINTR;
> > > +
> > > +		/* tx completed ? */
> > > +		if (readl(&ectl_p->con1.raw) & ETHCON_TXRTS) {
> > > +			udelay(1);
> > > +			continue;
> > > +		}
> > > +
> > > +		/* h/w not released ownership yet? */
> > > +		__dcache_invalidate(txd, sizeof(*txd));
> > > +		if (!(txd->hdr & EDH_EOWN))
> > > +			break;
> > > +	}
> > > +
> > > +	return 0;
> > > +}
> > > +
> > > +static int pic32_eth_recv(struct udevice *dev, int flags, uchar
> > > **packetp)
> > > +{
> > > +	struct pic32eth_dev *priv = dev_get_priv(dev);
> > > +	struct eth_dma_desc *rxd;
> > > +	u32 idx = priv->rxd_idx;
> > > +	u32 rx_count;
> > > +
> > > +	/* find the next ready to receive */
> > > +	rxd = &priv->rxd_ring[idx];
> > > +
> > > +	__dcache_invalidate(rxd, sizeof(*rxd));
> > > +	/* check if owned by MAC */
> > > +	if (rxd->hdr & EDH_EOWN)
> > > +		return -EAGAIN;
> > > +
> > > +	/* Sanity check on header: SOP and EOP  */
> > > +	if ((rxd->hdr & (EDH_SOP | EDH_EOP)) != (EDH_SOP |
> > > EDH_EOP))
> > > {
> > > +		printf("%s: %s, rx pkt across multiple descr\n",
> > > +		       __FILE__, __func__);
> > > +		return 0;
> > > +	}
> > > +
> > > +	debug("%s: %d /idx %i, hdr=%x, data_buff %x, stat %x,
> > > nexted
> > > %x\n",
> > > +	      __func__, __LINE__, idx, rxd->hdr,
> > > +	      rxd->data_buff, rxd->stat2, rxd->next_ed);
> > > +
> > > +	/* Sanity check on rx_stat: OK, CRC */
> > > +	if (!RSV_RX_OK(rxd->stat2) || RSV_CRC_ERR(rxd->stat2)) {
> > > +		debug("%s: %s: Error, rx problem detected\n",
> > > +		      __FILE__, __func__);
> > > +		return 0;
> > > +	}
> > > +
> > > +	/* invalidate dcache */
> > > +	rx_count = RSV_RX_COUNT(rxd->stat2);
> > > +	__dcache_invalidate(net_rx_packets[idx], rx_count);
> > > +
> > > +	/* Pass the packet to protocol layer */
> > > +	*packetp = net_rx_packets[idx];
> > > +
> > > +	/* increment number of bytes rcvd (ignore CRC) */
> > > +	return rx_count - 4;
> > > +}
> > > +
> > > +static int pic32_eth_free_pkt(struct udevice *dev, uchar
> > > *packet,
> > > int length)
> > > +{
> > > +	struct pic32eth_dev *priv = dev_get_priv(dev);
> > > +	struct pic32_ectl_regs *ectl_p = priv->ectl_regs;
> > > +	struct eth_dma_desc *rxd;
> > > +	int idx = priv->rxd_idx;
> > > +
> > > +	/* sanity check */
> > > +	if (packet != net_rx_packets[idx]) {
> > > +		printf("rxd_id %d: packet is not matched,\n",
> > > idx);
> > > +		return -EAGAIN;
> > > +	}
> > > +
> > > +	/* prepare for receive */
> > > +	rxd = &priv->rxd_ring[idx];
> > > +	rxd->hdr = EDH_STICKY | EDH_NPV | EDH_EOWN;
> > > +
> > > +	__dcache_flush(rxd, sizeof(*rxd));
> > > +
> > > +	/* decrement rx pkt count */
> > > +	writel(ETHCON_BUFCDEC, &ectl_p->con1.set);
> > > +
> > > +	debug("%s: %d / idx %i, hdr %x, data_buff %x, stat %x,
> > > nexted %x\n",
> > > +	      __func__, __LINE__, idx, rxd->hdr, rxd->data_buff,
> > > +	      rxd->stat2, rxd->next_ed);
> > > +
> > > +	priv->rxd_idx = (priv->rxd_idx + 1) % MAX_RX_DESCR;
> > > +
> > > +	return 0;
> > > +}
> > > +
> > > +static const struct eth_ops pic32_eth_ops = {
> > > +	.start		= pic32_eth_start,
> > > +	.send		= pic32_eth_send,
> > > +	.recv		= pic32_eth_recv,
> > > +	.free_pkt	= pic32_eth_free_pkt,
> > > +	.stop		= pic32_eth_stop,
> > > +};
> > > +
> > > +static int pic32_eth_probe(struct udevice *dev)
> > > +{
> > > +	struct eth_pdata *pdata = dev_get_platdata(dev);
> > your driver private data should be stored in struct pic32eth_dev
> 
> Please see below.
> 
> > > +	struct pic32eth_dev *priv = dev_get_priv(dev);
> > > +	const char *phy_mode;
> > > +	void __iomem *iobase;
> > > +	fdt_addr_t addr;
> > > +	fdt_size_t size;
> > > +	int offset = 0;
> > > +	int phy_addr = -1;
> > > +
> > > +	addr = fdtdec_get_addr_size(gd->fdt_blob, dev
> > > ->of_offset,
> > > "reg", &size);
> > > +	if (addr == FDT_ADDR_T_NONE)
> > > +		return -EINVAL;
> > > +
> > > +	iobase = ioremap(addr, size);
> > > +	if (!iobase)
> > > +		return -EINVAL;
> > you can drop this check. ioremap() always returns a mapped address.
> 
> ack.
> 
> > > +
> > > +	pdata->iobase = (phys_addr_t)addr;
> > > +
> > > +	/* get phy mode */
> > > +	pdata->phy_interface = -1;
> > > +	phy_mode = fdt_getprop(gd->fdt_blob, dev->of_offset,
> > > "phy
> > > -mode", NULL);
> > > +	if (phy_mode)
> > > +		pdata->phy_interface =
> > > phy_get_interface_by_name(phy_mode);
> > > +	if (pdata->phy_interface == -1) {
> > > +		debug("%s: Invalid PHY interface '%s'\n",
> > > __func__,
> > > phy_mode);
> > > +		return -EINVAL;
> > > +	}
> > your driver private data should be stored in struct pic32eth_dev.
> > Only
> > if a arch/SoC does not support device-tree, platdata have to be
> > initialized with PHY address and PHY interface in the board code.
> > Also
> > form a drivers point of view, platdata is read-only and provides
> > board
> > -specific configuration for the driver.
> 
> Please note here (as you pointed out earlier also) driver private
> data (as defined by struct pic32eth_dev) is maintained at priv field
> of udevice. This is sufficient for the driver to work.
> On the other hand, u-boot network library expects 'struct eth_pdata'
> (as platdata) to be filled by the driver instance with correct
> information (like baseaddr, phy_interface, enetaddr). Network library
> uses this data structure to preprate, process (create-random-if-unset
> or download-from-some-storage-based-on-env or some else) and pass to
> the relevant ethernet device at different time as part of some
> callback. I have taken inspiration from other net drivers
> (drivers/net/).

ok, I've missed that part. I would expect that such data should be
stored in uclass private data. Please ignore my comment.

> 
> > > +
> > > +	/* get phy addr */
> > > +	offset = fdtdec_lookup_phandle(gd->fdt_blob, dev
> > > ->of_offset,
> > > +				       "phy-handle");
> > > +	if (offset > 0)
> > > +		phy_addr = fdtdec_get_int(gd->fdt_blob, offset,
> > > "reg", -1);
> > > +
> > > +	/* phy reset gpio */
> > > +	gpio_request_by_name_nodev(gd->fdt_blob, dev->of_offset,
> > > +				   "reset-gpios", 0,
> > > +				   &priv->rst_gpio,
> > > GPIOD_IS_OUT);
> > > +
> > > +	priv->phyif	= pdata->phy_interface;
> > > +	priv->phy_addr	= phy_addr;
> > > +	priv->ectl_regs	= iobase;
> > > +	priv->emac_regs	= iobase + PIC32_EMAC1CFG1;
> > > +
> > > +	pic32_mii_init(priv);
> > > +
> > > +	return pic32_phy_init(priv, dev);
> > > +}
> > > +
> > > +static int pic32_eth_remove(struct udevice *dev)
> > > +{
> > > +	struct pic32eth_dev *priv = dev_get_priv(dev);
> > > +	struct mii_dev *bus;
> > > +
> > > +	dm_gpio_free(dev, &priv->rst_gpio);
> > > +	phy_shutdown(priv->phydev);
> > > +	free(priv->phydev);
> > > +	bus = miiphy_get_dev_by_name(PIC32_MDIO_NAME);
> > > +	mdio_unregister(bus);
> > > +	mdio_free(bus);
> > > +	iounmap(priv->ectl_regs);
> > > +	return 0;
> > > +}
> > > +
> > > +static const struct udevice_id pic32_eth_ids[] = {
> > > +	{ .compatible = "microchip,pic32mzda-eth" },
> > > +	{ }
> > > +};
> > > +
> > > +U_BOOT_DRIVER(pic32_ethernet) = {
> > > +	.name			= "pic32_ethernet",
> > > +	.id			= UCLASS_ETH,
> > > +	.of_match		= pic32_eth_ids,
> > > +	.probe			= pic32_eth_probe,
> > > +	.remove			= pic32_eth_remove,
> > > +	.ops			= &pic32_eth_ops,
> > > +	.priv_auto_alloc_size	= sizeof(struct
> > > pic32eth_dev),
> > > +	.platdata_auto_alloc_size	= sizeof(struct
> > > eth_pdata),
> > > +};
> > > diff --git a/drivers/net/pic32_eth.h b/drivers/net/pic32_eth.h
> > > new file mode 100644
> > > index 0000000..4dd443b
> > > --- /dev/null
> > > +++ b/drivers/net/pic32_eth.h
> > > @@ -0,0 +1,171 @@
> > > +/*
> > > + * (c) 2015 Purna Chandra Mandal <purna.mandal@microchip.com>
> > > + *
> > > + * SPDX-License-Identifier:	GPL-2.0+
> > > + *
> > > + */
> > > +
> > > +#ifndef __MICROCHIP_PIC32_ETH_H_
> > > +#define __MICROCHIP_PIC32_ETH_H_
> > > +
> > > +#include <mach/pic32.h>
> > > +
> > > +/* Ethernet */
> > > +struct pic32_ectl_regs {
> > > +	struct pic32_reg_atomic con1; /* 0x00 */
> > > +	struct pic32_reg_atomic con2; /* 0x10 */
> > > +	struct pic32_reg_atomic txst; /* 0x20 */
> > > +	struct pic32_reg_atomic rxst; /* 0x30 */
> > > +	struct pic32_reg_atomic ht0;  /* 0x40 */
> > > +	struct pic32_reg_atomic ht1;  /* 0x50 */
> > > +	struct pic32_reg_atomic pmm0; /* 0x60 */
> > > +	struct pic32_reg_atomic pmm1; /* 0x70 */
> > > +	struct pic32_reg_atomic pmcs; /* 0x80 */
> > > +	struct pic32_reg_atomic pmo;  /* 0x90 */
> > > +	struct pic32_reg_atomic rxfc; /* 0xa0 */
> > > +	struct pic32_reg_atomic rxwm; /* 0xb0 */
> > > +	struct pic32_reg_atomic ien;  /* 0xc0 */
> > > +	struct pic32_reg_atomic irq;  /* 0xd0 */
> > > +	struct pic32_reg_atomic stat; /* 0xe0 */
> > > +};
> > > +
> > > +struct pic32_mii_regs {
> > > +	struct pic32_reg_atomic mcfg; /* 0x280 */
> > > +	struct pic32_reg_atomic mcmd; /* 0x290 */
> > > +	struct pic32_reg_atomic madr; /* 0x2a0 */
> > > +	struct pic32_reg_atomic mwtd; /* 0x2b0 */
> > > +	struct pic32_reg_atomic mrdd; /* 0x2c0 */
> > > +	struct pic32_reg_atomic mind; /* 0x2d0 */
> > > +};
> > > +
> > > +struct pic32_emac_regs {
> > > +	struct pic32_reg_atomic cfg1; /* 0x200*/
> > > +	struct pic32_reg_atomic cfg2; /* 0x210*/
> > > +	struct pic32_reg_atomic ipgt; /* 0x220*/
> > > +	struct pic32_reg_atomic ipgr; /* 0x230*/
> > > +	struct pic32_reg_atomic clrt; /* 0x240*/
> > > +	struct pic32_reg_atomic maxf; /* 0x250*/
> > > +	struct pic32_reg_atomic supp; /* 0x260*/
> > > +	struct pic32_reg_atomic test; /* 0x270*/
> > > +	struct pic32_mii_regs mii;    /* 0x280 - 0x2d0 */
> > > +	struct pic32_reg_atomic res1; /* 0x2e0 */
> > > +	struct pic32_reg_atomic res2; /* 0x2f0 */
> > > +	struct pic32_reg_atomic sa0;  /* 0x300 */
> > > +	struct pic32_reg_atomic sa1;  /* 0x310 */
> > > +	struct pic32_reg_atomic sa2;  /* 0x320 */
> > > +};
> > > +
> > > +/* ETHCON1 Reg field */
> > > +#define ETHCON_BUFCDEC		BIT(0)
> > > +#define ETHCON_RXEN		BIT(8)
> > > +#define ETHCON_TXRTS		BIT(9)
> > > +#define ETHCON_ON		BIT(15)
> > > +
> > > +/* ETHCON2 Reg field */
> > > +#define ETHCON_RXBUFSZ		0x7f
> > > +#define ETHCON_RXBUFSZ_SHFT	0x4
> > > +
> > > +/* ETHSTAT Reg field */
> > > +#define ETHSTAT_BUSY		BIT(7)
> > > +#define ETHSTAT_BUFCNT		0x00ff0000
> > > +
> > > +/* ETHRXFC Register fields */
> > > +#define ETHRXFC_BCEN		BIT(0)
> > > +#define ETHRXFC_MCEN		BIT(1)
> > > +#define ETHRXFC_UCEN		BIT(3)
> > > +#define ETHRXFC_RUNTEN		BIT(4)
> > > +#define ETHRXFC_CRCOKEN		BIT(5)
> > > +
> > > +/* EMAC1CFG1 register offset */
> > > +#define PIC32_EMAC1CFG1		0x0200
> > > +
> > > +/* EMAC1CFG1 register fields */
> > > +#define EMAC_RXENABLE		BIT(0)
> > > +#define EMAC_RXPAUSE		BIT(2)
> > > +#define EMAC_TXPAUSE		BIT(3)
> > > +#define EMAC_SOFTRESET		BIT(15)
> > > +
> > > +/* EMAC1CFG2 register fields */
> > > +#define EMAC_FULLDUP		BIT(0)
> > > +#define EMAC_LENGTHCK		BIT(1)
> > > +#define EMAC_CRCENABLE		BIT(4)
> > > +#define EMAC_PADENABLE		BIT(5)
> > > +#define EMAC_AUTOPAD		BIT(7)
> > > +#define EMAC_EXCESS		BIT(14)
> > > +
> > > +/* EMAC1IPGT register magic */
> > > +#define FULLDUP_GAP_TIME	0x15
> > > +#define HALFDUP_GAP_TIME	0x12
> > > +
> > > +/* EMAC1SUPP register fields */
> > > +#define EMAC_RMII_SPD100	BIT(8)
> > > +#define EMAC_RMII_RESET		BIT(11)
> > > +
> > > +/* MII Management Configuration Register */
> > > +#define MIIMCFG_RSTMGMT		BIT(15)
> > > +#define MIIMCFG_CLKSEL_DIV40	0x0020	/* 100Mhz / 40
> > > */
> > > +
> > > +/* MII Management Command Register */
> > > +#define MIIMCMD_READ		BIT(0)
> > > +#define MIIMCMD_SCAN		BIT(1)
> > > +
> > > +/* MII Management Address Register */
> > > +#define MIIMADD_REGADDR		0x1f
> > > +#define MIIMADD_REGADDR_SHIFT	0
> > > +#define MIIMADD_PHYADDR_SHIFT	8
> > > +
> > > +/* MII Management Indicator Register */
> > > +#define MIIMIND_BUSY		BIT(0)
> > > +#define MIIMIND_NOTVALID	BIT(2)
> > > +#define MIIMIND_LINKFAIL	BIT(3)
> > > +
> > > +/* Packet Descriptor */
> > > +/* Received Packet Status */
> > > +#define _RSV1_PKT_CSUM		0xffff
> > > +#define _RSV2_CRC_ERR		BIT(20)
> > > +#define _RSV2_LEN_ERR		BIT(21)
> > > +#define _RSV2_RX_OK		BIT(23)
> > > +#define _RSV2_RX_COUNT		0xffff
> > > +
> > > +#define RSV_RX_CSUM(__rsv1)	((__rsv1) & _RSV1_PKT_CSUM)
> > > +#define RSV_RX_COUNT(__rsv2)	((__rsv2) & _RSV2_RX_COUNT)
> > > +#define RSV_RX_OK(__rsv2)	((__rsv2) & _RSV2_RX_OK)
> > > +#define RSV_CRC_ERR(__rsv2)	((__rsv2) & _RSV2_CRC_ERR)
> > > +
> > > +/* Ethernet Hardware Descriptor Header bits */
> > > +#define EDH_EOWN		BIT(7)
> > > +#define EDH_NPV			BIT(8)
> > > +#define EDH_STICKY		BIT(9)
> > > +#define _EDH_BCOUNT		0x07ff0000
> > > +#define EDH_EOP			BIT(30)
> > > +#define EDH_SOP			BIT(31)
> > > +#define EDH_BCOUNT_SHIFT	16
> > > +#define EDH_BCOUNT(len)		((len) <<
> > > EDH_BCOUNT_SHIFT)
> > > +
> > > +/* Ethernet Hardware Descriptors
> > > + * ref: PIC32 Family Reference Manual Table 35-7
> > > + * This structure represents the layout of the DMA
> > > + * memory shared between the CPU and the Ethernet
> > > + * controller.
> > > + */
> > > +/* TX/RX DMA descriptor */
> > > +struct eth_dma_desc {
> > > +	u32 hdr;	/* header */
> > > +	u32 data_buff;	/* data buffer address */
> > > +	u32 stat1;	/* transmit/receive packet status */
> > > +	u32 stat2;	/* transmit/receive packet status */
> > > +	u32 next_ed;	/* next descriptor */
> > > +};
> > > +
> > > +/* cache operation helper */
> > > +#define __dcache_flush(__a, __l) \
> > > +	flush_dcache_range((ulong)(__a),  ((__l) +
> > > (ulong)(__a)))
> > > +
> > > +#define __dcache_invalidate(__a, __l) \
> > > +	invalidate_dcache_range((ulong)(__a),  ((__l) +
> > > (ulong)(__a)))
> > > +
> > > +#define PIC32_MDIO_NAME "PIC32_EMAC"
> > > +
> > > +int pic32_mdio_init(const char *name, ulong ioaddr);
> > > +
> > > +#endif /* __MICROCHIP_PIC32_ETH_H_*/
> > > diff --git a/drivers/net/pic32_mdio.c b/drivers/net/pic32_mdio.c
> > > new file mode 100644
> > > index 0000000..578fc96
> > > --- /dev/null
> > > +++ b/drivers/net/pic32_mdio.c
> > > @@ -0,0 +1,121 @@
> > > +/*
> > > + * pic32_mdio.c: PIC32 MDIO/MII driver, part of pic32_eth.c.
> > > + *
> > > + * Copyright 2015 Microchip Inc.
> > > + *	Purna Chandra Mandal <purna.mandal@microchip.com>
> > > + *
> > > + * SPDX-License-Identifier:	GPL-2.0+
> > > + */
> > > +#include <common.h>
> > > +#include <phy.h>
> > > +#include <miiphy.h>
> > > +#include <errno.h>
> > > +#include <wait_bit.h>
> > > +#include <asm/io.h>
> > > +#include "pic32_eth.h"
> > > +
> > > +static int pic32_mdio_write(struct mii_dev *bus,
> > > +			    int addr, int dev_addr,
> > > +			    int reg, u16 value)
> > > +{
> > > +	u32 v;
> > > +	struct pic32_mii_regs *mii_regs = bus->priv;
> > > +
> > > +	/* Wait for the previous operation to finish */
> > > +	wait_for_bit(__func__, &mii_regs->mind.raw,
> > > MIIMIND_BUSY,
> > > +		     false, CONFIG_SYS_HZ, true);
> > > +
> > > +	/* Put phyaddr and regaddr into MIIMADD */
> > > +	v = (addr << MIIMADD_PHYADDR_SHIFT) | (reg &
> > > MIIMADD_REGADDR);
> > > +	writel(v, &mii_regs->madr.raw);
> > > +
> > > +	/* Initiate a write command */
> > > +	writel(value, &mii_regs->mwtd.raw);
> > > +
> > > +	/* Wait 30 clock cycles for busy flag to be set */
> > > +	udelay(12);
> > > +
> > > +	/* Wait for write to complete */
> > > +	wait_for_bit(__func__, &mii_regs->mind.raw,
> > > MIIMIND_BUSY,
> > > +		     false, CONFIG_SYS_HZ, true);
> > > +
> > > +	return 0;
> > > +}
> > > +
> > > +static int pic32_mdio_read(struct mii_dev *bus, int addr, int
> > > devaddr, int reg)
> > > +{
> > > +	u32 v;
> > > +	struct pic32_mii_regs *mii_regs = bus->priv;
> > > +
> > > +	/* Wait for the previous operation to finish */
> > > +	wait_for_bit(__func__, &mii_regs->mind.raw,
> > > MIIMIND_BUSY,
> > > +		     false, CONFIG_SYS_HZ, true);
> > > +
> > > +	/* Put phyaddr and regaddr into MIIMADD */
> > > +	v = (addr << MIIMADD_PHYADDR_SHIFT) | (reg &
> > > MIIMADD_REGADDR);
> > > +	writel(v, &mii_regs->madr.raw);
> > > +
> > > +	/* Initiate a read command */
> > > +	writel(MIIMCMD_READ, &mii_regs->mcmd.raw);
> > > +
> > > +	/* Wait 30 clock cycles for busy flag to be set */
> > > +	udelay(12);
> > > +
> > > +	/* Wait for read to complete */
> > > +	wait_for_bit(__func__, &mii_regs->mind.raw,
> > > +		     MIIMIND_NOTVALID | MIIMIND_BUSY,
> > > +		     false, CONFIG_SYS_HZ, false);
> > > +
> > > +	/* Clear the command register */
> > > +	writel(0, &mii_regs->mcmd.raw);
> > > +
> > > +	/* Grab the value read from the PHY */
> > > +	v = readl(&mii_regs->mrdd.raw);
> > > +	return v;
> > > +}
> > > +
> > > +static int pic32_mdio_reset(struct mii_dev *bus)
> > > +{
> > > +	struct pic32_mii_regs *mii_regs = bus->priv;
> > > +
> > > +	/* Reset MII (due to new addresses) */
> > > +	writel(MIIMCFG_RSTMGMT, &mii_regs->mcfg.raw);
> > > +
> > > +	/* Wait for the operation to finish */
> > > +	wait_for_bit(__func__, &mii_regs->mind.raw,
> > > MIIMIND_BUSY,
> > > +		     false, CONFIG_SYS_HZ, true);
> > > +
> > > +	/* Clear reset bit */
> > > +	writel(0, &mii_regs->mcfg);
> > > +
> > > +	/* Wait for the operation to finish */
> > > +	wait_for_bit(__func__, &mii_regs->mind.raw,
> > > MIIMIND_BUSY,
> > > +		     false, CONFIG_SYS_HZ, true);
> > > +
> > > +	/* Set the MII Management Clock (MDC) - no faster than
> > > 2.5
> > > MHz */
> > > +	writel(MIIMCFG_CLKSEL_DIV40, &mii_regs->mcfg.raw);
> > > +
> > > +	/* Wait for the operation to finish */
> > > +	wait_for_bit(__func__, &mii_regs->mind.raw,
> > > MIIMIND_BUSY,
> > > +		     false, CONFIG_SYS_HZ, true);
> > > +	return 0;
> > > +}
> > > +
> > > +int pic32_mdio_init(const char *name, ulong ioaddr)
> > > +{
> > > +	struct mii_dev *bus;
> > > +
> > > +	bus = mdio_alloc();
> > > +	if (!bus) {
> > > +		printf("Failed to allocate PIC32-MDIO bus\n");
> > > +		return -ENOMEM;
> > > +	}
> > > +
> > > +	bus->read = pic32_mdio_read;
> > > +	bus->write = pic32_mdio_write;
> > > +	bus->reset = pic32_mdio_reset;
> > > +	strncpy(bus->name, name, sizeof(bus->name));
> > > +	bus->priv = (void *)ioaddr;
> > > +
> > > +	return mdio_register(bus);
> > > +}
>
diff mbox

Patch

diff --git a/drivers/net/Kconfig b/drivers/net/Kconfig
index ae5e78d..dc49493 100644
--- a/drivers/net/Kconfig
+++ b/drivers/net/Kconfig
@@ -108,4 +108,11 @@  config ZYNQ_GEM
 	help
 	  This MAC is present in Xilinx Zynq and ZynqMP SoCs.
 
+config PIC32_ETH
+	bool "Microchip PIC32 Ethernet Support"
+	depends on MACH_PIC32
+	help
+	  This driver implements 10/100 Mbps Ethernet and MAC layer for
+	  Microchip PIC32 microcontrollers.
+
 endif # NETDEVICES
diff --git a/drivers/net/Makefile b/drivers/net/Makefile
index 150470c..33a81ee 100644
--- a/drivers/net/Makefile
+++ b/drivers/net/Makefile
@@ -72,3 +72,4 @@  obj-$(CONFIG_FSL_MC_ENET) += fsl-mc/
 obj-$(CONFIG_FSL_MC_ENET) += ldpaa_eth/
 obj-$(CONFIG_FSL_MEMAC) += fm/memac_phy.o
 obj-$(CONFIG_VSC9953) += vsc9953.o
+obj-$(CONFIG_PIC32_ETH) += pic32_mdio.o pic32_eth.o
diff --git a/drivers/net/pic32_eth.c b/drivers/net/pic32_eth.c
new file mode 100644
index 0000000..1cef62e
--- /dev/null
+++ b/drivers/net/pic32_eth.c
@@ -0,0 +1,606 @@ 
+/*
+ * (c) 2015 Purna Chandra Mandal <purna.mandal@microchip.com>
+ *
+ * SPDX-License-Identifier:	GPL-2.0+
+ *
+ */
+#include <common.h>
+#include <errno.h>
+#include <dm.h>
+#include <net.h>
+#include <miiphy.h>
+#include <console.h>
+#include <wait_bit.h>
+#include <asm/gpio.h>
+
+#include "pic32_eth.h"
+
+#define MAX_RX_BUF_SIZE		1536
+#define MAX_RX_DESCR		PKTBUFSRX
+#define MAX_TX_DESCR		2
+
+DECLARE_GLOBAL_DATA_PTR;
+
+struct pic32eth_dev {
+	struct eth_dma_desc rxd_ring[MAX_RX_DESCR];
+	struct eth_dma_desc txd_ring[MAX_TX_DESCR];
+	u32 rxd_idx; /* index of RX desc to read */
+	/* regs */
+	struct pic32_ectl_regs *ectl_regs;
+	struct pic32_emac_regs *emac_regs;
+	/* Phy */
+	struct phy_device *phydev;
+	phy_interface_t phyif;
+	u32 phy_addr;
+	struct gpio_desc rst_gpio;
+};
+
+void __weak board_netphy_reset(void *dev)
+{
+	struct pic32eth_dev *priv = (struct pic32eth_dev *)dev;
+
+	if (!dm_gpio_is_valid(&priv->rst_gpio))
+		return;
+
+	/* phy reset */
+	dm_gpio_set_value(&priv->rst_gpio, 0);
+	udelay(300);
+	dm_gpio_set_value(&priv->rst_gpio, 1);
+	udelay(300);
+}
+
+/* Initialize mii(MDIO) interface, discover which PHY is
+ * attached to the device, and configure it properly.
+ */
+static int pic32_mii_init(struct pic32eth_dev *priv)
+{
+	struct pic32_ectl_regs *ectl_p = priv->ectl_regs;
+	struct pic32_emac_regs *emac_p = priv->emac_regs;
+
+	/* board phy reset */
+	board_netphy_reset(priv);
+
+	/* disable RX, TX & all transactions */
+	writel(ETHCON_ON | ETHCON_TXRTS | ETHCON_RXEN, &ectl_p->con1.clr);
+
+	/* wait till busy */
+	wait_for_bit(__func__, &ectl_p->stat.raw, ETHSTAT_BUSY, false,
+		     CONFIG_SYS_HZ, false);
+
+	/* turn controller ON to access PHY over MII */
+	writel(ETHCON_ON, &ectl_p->con1.set);
+
+	mdelay(10);
+
+	/* reset MAC */
+	writel(EMAC_SOFTRESET, &emac_p->cfg1.set); /* reset assert */
+	mdelay(10);
+	writel(EMAC_SOFTRESET, &emac_p->cfg1.clr); /* reset deassert */
+
+	/* initialize MDIO/MII */
+	if (priv->phyif == PHY_INTERFACE_MODE_RMII) {
+		writel(EMAC_RMII_RESET, &emac_p->supp.set);
+		mdelay(10);
+		writel(EMAC_RMII_RESET, &emac_p->supp.clr);
+	}
+
+	return pic32_mdio_init(PIC32_MDIO_NAME, (ulong)&emac_p->mii);
+}
+
+static int pic32_phy_init(struct pic32eth_dev *priv, struct udevice *dev)
+{
+	struct mii_dev *mii;
+
+	mii = miiphy_get_dev_by_name(PIC32_MDIO_NAME);
+
+	/* find & connect PHY */
+	priv->phydev = phy_connect(mii, priv->phy_addr,
+				   dev, priv->phyif);
+	if (!priv->phydev) {
+		printf("%s: %s: Error, PHY connect\n", __FILE__, __func__);
+		return 0;
+	}
+
+	/* Wait for phy to complete reset */
+	mdelay(10);
+
+	/* configure supported modes */
+	priv->phydev->supported = SUPPORTED_10baseT_Half |
+				  SUPPORTED_10baseT_Full |
+				  SUPPORTED_100baseT_Half |
+				  SUPPORTED_100baseT_Full |
+				  SUPPORTED_Autoneg;
+
+	priv->phydev->advertising = ADVERTISED_10baseT_Half |
+				    ADVERTISED_10baseT_Full |
+				    ADVERTISED_100baseT_Half |
+				    ADVERTISED_100baseT_Full |
+				    ADVERTISED_Autoneg;
+
+	priv->phydev->autoneg = AUTONEG_ENABLE;
+
+	return 0;
+}
+
+/* Configure MAC based on negotiated speed and duplex
+ * reported by PHY.
+ */
+static int pic32_mac_adjust_link(struct pic32eth_dev *priv)
+{
+	struct phy_device *phydev = priv->phydev;
+	struct pic32_emac_regs *emac_p = priv->emac_regs;
+
+	if (!phydev->link) {
+		printf("%s: No link.\n", phydev->dev->name);
+		return -EINVAL;
+	}
+
+	if (phydev->duplex) {
+		writel(EMAC_FULLDUP, &emac_p->cfg2.set);
+		writel(FULLDUP_GAP_TIME, &emac_p->ipgt.raw);
+	} else {
+		writel(EMAC_FULLDUP, &emac_p->cfg2.clr);
+		writel(HALFDUP_GAP_TIME, &emac_p->ipgt.raw);
+	}
+
+	switch (phydev->speed) {
+	case SPEED_100:
+		writel(EMAC_RMII_SPD100, &emac_p->supp.set);
+		break;
+	case SPEED_10:
+		writel(EMAC_RMII_SPD100, &emac_p->supp.clr);
+		break;
+	default:
+		printf("%s: Speed was bad\n", phydev->dev->name);
+		return -EINVAL;
+	}
+
+	printf("pic32eth: PHY is %s with %dbase%s, %s\n",
+	       phydev->drv->name, phydev->speed,
+	       (phydev->port == PORT_TP) ? "T" : "X",
+	       (phydev->duplex) ? "full" : "half");
+
+	return 0;
+}
+
+static void pic32_mac_init(struct pic32eth_dev *priv, u8 *macaddr)
+{
+	struct pic32_emac_regs *emac_p = priv->emac_regs;
+	u32 stat = 0, v;
+	u64 expire;
+
+	v = EMAC_TXPAUSE | EMAC_RXPAUSE | EMAC_RXENABLE;
+	writel(v, &emac_p->cfg1.raw);
+
+	v = EMAC_EXCESS | EMAC_AUTOPAD | EMAC_PADENABLE |
+	    EMAC_CRCENABLE | EMAC_LENGTHCK | EMAC_FULLDUP;
+	writel(v, &emac_p->cfg2.raw);
+
+	/* recommended back-to-back inter-packet gap for 10 Mbps half duplex */
+	writel(HALFDUP_GAP_TIME, &emac_p->ipgt.raw);
+
+	/* recommended non-back-to-back interpacket gap is 0xc12 */
+	writel(0xc12, &emac_p->ipgr.raw);
+
+	/* recommended collision window retry limit is 0x370F */
+	writel(0x370f, &emac_p->clrt.raw);
+
+	/* set maximum frame length: allow VLAN tagged frame */
+	writel(0x600, &emac_p->maxf.raw);
+
+	/* set the mac address */
+	writel(macaddr[0] | (macaddr[1] << 8), &emac_p->sa2.raw);
+	writel(macaddr[2] | (macaddr[3] << 8), &emac_p->sa1.raw);
+	writel(macaddr[4] | (macaddr[5] << 8), &emac_p->sa0.raw);
+
+	/* default, enable 10 Mbps operation */
+	writel(EMAC_RMII_SPD100, &emac_p->supp.clr);
+
+	/* wait until link status UP or deadline elapsed */
+	expire = get_ticks() + get_tbclk() * 2;
+	for (; get_ticks() < expire;) {
+		stat = phy_read(priv->phydev, priv->phy_addr, MII_BMSR);
+		if (stat & BMSR_LSTATUS)
+			break;
+	}
+
+	if (!(stat & BMSR_LSTATUS))
+		printf("MAC: Link is DOWN!\n");
+
+	/* delay to stabilize before any tx/rx */
+	mdelay(10);
+}
+
+static void pic32_mac_reset(struct pic32eth_dev *priv)
+{
+	struct pic32_emac_regs *emac_p = priv->emac_regs;
+	struct mii_dev *mii;
+
+	/* Reset MAC */
+	writel(EMAC_SOFTRESET, &emac_p->cfg1.raw);
+	mdelay(10);
+
+	/* clear reset */
+	writel(0, &emac_p->cfg1.raw);
+
+	/* Reset MII */
+	mii = priv->phydev->bus;
+	if (mii && mii->reset)
+		mii->reset(mii);
+}
+
+/* initializes the MAC and PHY, then establishes a link */
+static void pic32_ctrl_reset(struct pic32eth_dev *priv)
+{
+	struct pic32_ectl_regs *ectl_p = priv->ectl_regs;
+	u32 v;
+
+	/* disable RX, TX & any other transactions */
+	writel(ETHCON_ON | ETHCON_TXRTS | ETHCON_RXEN, &ectl_p->con1.clr);
+
+	/* wait till busy */
+	wait_for_bit(__func__, &ectl_p->stat.raw, ETHSTAT_BUSY, false,
+		     CONFIG_SYS_HZ, false);
+	/* decrement received buffcnt to zero. */
+	while (readl(&ectl_p->stat.raw) & ETHSTAT_BUFCNT)
+		writel(ETHCON_BUFCDEC, &ectl_p->con1.set);
+
+	/* clear any existing interrupt event */
+	writel(0xffffffff, &ectl_p->irq.clr);
+
+	/* clear RX/TX start address */
+	writel(0xffffffff, &ectl_p->txst.clr);
+	writel(0xffffffff, &ectl_p->rxst.clr);
+
+	/* clear the receive filters */
+	writel(0x00ff, &ectl_p->rxfc.clr);
+
+	/* set the receive filters
+	 * ETH_FILT_CRC_ERR_REJECT
+	 * ETH_FILT_RUNT_REJECT
+	 * ETH_FILT_UCAST_ACCEPT
+	 * ETH_FILT_MCAST_ACCEPT
+	 * ETH_FILT_BCAST_ACCEPT
+	 */
+	v = ETHRXFC_BCEN | ETHRXFC_MCEN | ETHRXFC_UCEN |
+	    ETHRXFC_RUNTEN | ETHRXFC_CRCOKEN;
+	writel(v, &ectl_p->rxfc.set);
+
+	/* turn controller ON to access PHY over MII */
+	writel(ETHCON_ON, &ectl_p->con1.set);
+}
+
+static void pic32_rx_desc_init(struct pic32eth_dev *priv)
+{
+	struct pic32_ectl_regs *ectl_p = priv->ectl_regs;
+	struct eth_dma_desc *rxd;
+	u32 idx, bufsz;
+
+	priv->rxd_idx = 0;
+	for (idx = 0; idx < MAX_RX_DESCR; idx++) {
+		rxd = &priv->rxd_ring[idx];
+
+		/* hw owned */
+		rxd->hdr = EDH_NPV | EDH_EOWN | EDH_STICKY;
+
+		/* packet buffer address */
+		rxd->data_buff = virt_to_phys(net_rx_packets[idx]);
+
+		/* link to next desc */
+		rxd->next_ed = virt_to_phys(rxd + 1);
+
+		/* reset status */
+		rxd->stat1 = 0;
+		rxd->stat2 = 0;
+
+		/* decrement bufcnt */
+		writel(ETHCON_BUFCDEC, &ectl_p->con1.set);
+	}
+
+	/* link last descr to beginning of list */
+	rxd->next_ed = virt_to_phys(&priv->rxd_ring[0]);
+
+	/* flush rx ring */
+	__dcache_flush(priv->rxd_ring, sizeof(priv->rxd_ring));
+
+	/* set rx desc-ring start address */
+	writel((ulong)virt_to_phys(&priv->rxd_ring[0]), &ectl_p->rxst.raw);
+
+	/* RX Buffer size */
+	bufsz = readl(&ectl_p->con2.raw);
+	bufsz &= ~(ETHCON_RXBUFSZ << ETHCON_RXBUFSZ_SHFT);
+	bufsz |= ((MAX_RX_BUF_SIZE / 16) << ETHCON_RXBUFSZ_SHFT);
+	writel(bufsz, &ectl_p->con2.raw);
+
+	/* enable the receiver in hardware which allows hardware
+	 * to DMA received pkts to the descriptor pointer address.
+	 */
+	writel(ETHCON_RXEN, &ectl_p->con1.set);
+}
+
+static int pic32_eth_start(struct udevice *dev)
+{
+	struct eth_pdata *pdata = dev_get_platdata(dev);
+	struct pic32eth_dev *priv = dev_get_priv(dev);
+
+	/* controller */
+	pic32_ctrl_reset(priv);
+
+	/* reset MAC */
+	pic32_mac_reset(priv);
+
+	/* configure PHY */
+	phy_config(priv->phydev);
+
+	/* initialize MAC */
+	pic32_mac_init(priv, &pdata->enetaddr[0]);
+
+	/* init RX descriptor; TX descriptors are handled in xmit */
+	pic32_rx_desc_init(priv);
+
+	/* Start up & update link status of PHY */
+	phy_startup(priv->phydev);
+
+	/* adjust mac with phy link status */
+	return pic32_mac_adjust_link(priv);
+}
+
+static void pic32_eth_stop(struct udevice *dev)
+{
+	struct pic32eth_dev *priv = dev_get_priv(dev);
+	struct pic32_ectl_regs *ectl_p = priv->ectl_regs;
+	struct pic32_emac_regs *emac_p = priv->emac_regs;
+
+	/* Reset the phy if the controller is enabled */
+	if (readl(&ectl_p->con1.raw) & ETHCON_ON)
+		phy_reset(priv->phydev);
+
+	/* Shut down the PHY */
+	phy_shutdown(priv->phydev);
+
+	/* Stop rx/tx */
+	writel(ETHCON_TXRTS | ETHCON_RXEN, &ectl_p->con1.clr);
+	mdelay(10);
+
+	/* reset MAC */
+	writel(EMAC_SOFTRESET, &emac_p->cfg1.raw);
+
+	/* clear reset */
+	writel(0, &emac_p->cfg1.raw);
+	mdelay(10);
+
+	/* disable controller */
+	writel(ETHCON_ON, &ectl_p->con1.clr);
+	mdelay(10);
+
+	/* wait until everything is down */
+	wait_for_bit(__func__, &ectl_p->stat.raw, ETHSTAT_BUSY, false,
+		     2 * CONFIG_SYS_HZ, false);
+
+	/* clear any existing interrupt event */
+	writel(0xffffffff, &ectl_p->irq.clr);
+}
+
+static int pic32_eth_send(struct udevice *dev, void *packet, int length)
+{
+	struct pic32eth_dev *priv = dev_get_priv(dev);
+	struct pic32_ectl_regs *ectl_p = priv->ectl_regs;
+	struct eth_dma_desc *txd;
+	u64 deadline;
+
+	txd = &priv->txd_ring[0];
+
+	/* set proper flags & length in descriptor header */
+	txd->hdr = EDH_SOP | EDH_EOP | EDH_EOWN | EDH_BCOUNT(length);
+
+	/* pass buffer address to hardware */
+	txd->data_buff = virt_to_phys(packet);
+
+	debug("%s: %d / .hdr %x, .data_buff %x, .stat %x, .nexted %x\n",
+	      __func__, __LINE__, txd->hdr, txd->data_buff, txd->stat2,
+	      txd->next_ed);
+
+	/* cache flush (packet) */
+	__dcache_flush(packet, length);
+
+	/* cache flush (txd) */
+	__dcache_flush(txd, sizeof(*txd));
+
+	/* pass descriptor table base to h/w */
+	writel(virt_to_phys(txd), &ectl_p->txst.raw);
+
+	/* ready to send enabled, hardware can now send the packet(s) */
+	writel(ETHCON_TXRTS | ETHCON_ON, &ectl_p->con1.set);
+
+	/* wait until tx has completed and h/w has released ownership
+	 * of the tx descriptor or timeout elapsed.
+	 */
+	deadline = get_ticks() + get_tbclk();
+	for (;;) {
+		/* check timeout */
+		if (get_ticks() > deadline)
+			return -ETIMEDOUT;
+
+		if (ctrlc())
+			return -EINTR;
+
+		/* tx completed ? */
+		if (readl(&ectl_p->con1.raw) & ETHCON_TXRTS) {
+			udelay(1);
+			continue;
+		}
+
+		/* h/w not released ownership yet? */
+		__dcache_invalidate(txd, sizeof(*txd));
+		if (!(txd->hdr & EDH_EOWN))
+			break;
+	}
+
+	return 0;
+}
+
+static int pic32_eth_recv(struct udevice *dev, int flags, uchar **packetp)
+{
+	struct pic32eth_dev *priv = dev_get_priv(dev);
+	struct eth_dma_desc *rxd;
+	u32 idx = priv->rxd_idx;
+	u32 rx_count;
+
+	/* find the next ready to receive */
+	rxd = &priv->rxd_ring[idx];
+
+	__dcache_invalidate(rxd, sizeof(*rxd));
+	/* check if owned by MAC */
+	if (rxd->hdr & EDH_EOWN)
+		return -EAGAIN;
+
+	/* Sanity check on header: SOP and EOP  */
+	if ((rxd->hdr & (EDH_SOP | EDH_EOP)) != (EDH_SOP | EDH_EOP)) {
+		printf("%s: %s, rx pkt across multiple descr\n",
+		       __FILE__, __func__);
+		return 0;
+	}
+
+	debug("%s: %d /idx %i, hdr=%x, data_buff %x, stat %x, nexted %x\n",
+	      __func__, __LINE__, idx, rxd->hdr,
+	      rxd->data_buff, rxd->stat2, rxd->next_ed);
+
+	/* Sanity check on rx_stat: OK, CRC */
+	if (!RSV_RX_OK(rxd->stat2) || RSV_CRC_ERR(rxd->stat2)) {
+		debug("%s: %s: Error, rx problem detected\n",
+		      __FILE__, __func__);
+		return 0;
+	}
+
+	/* invalidate dcache */
+	rx_count = RSV_RX_COUNT(rxd->stat2);
+	__dcache_invalidate(net_rx_packets[idx], rx_count);
+
+	/* Pass the packet to protocol layer */
+	*packetp = net_rx_packets[idx];
+
+	/* increment number of bytes rcvd (ignore CRC) */
+	return rx_count - 4;
+}
+
+static int pic32_eth_free_pkt(struct udevice *dev, uchar *packet, int length)
+{
+	struct pic32eth_dev *priv = dev_get_priv(dev);
+	struct pic32_ectl_regs *ectl_p = priv->ectl_regs;
+	struct eth_dma_desc *rxd;
+	int idx = priv->rxd_idx;
+
+	/* sanity check */
+	if (packet != net_rx_packets[idx]) {
+		printf("rxd_id %d: packet is not matched,\n", idx);
+		return -EAGAIN;
+	}
+
+	/* prepare for receive */
+	rxd = &priv->rxd_ring[idx];
+	rxd->hdr = EDH_STICKY | EDH_NPV | EDH_EOWN;
+
+	__dcache_flush(rxd, sizeof(*rxd));
+
+	/* decrement rx pkt count */
+	writel(ETHCON_BUFCDEC, &ectl_p->con1.set);
+
+	debug("%s: %d / idx %i, hdr %x, data_buff %x, stat %x, nexted %x\n",
+	      __func__, __LINE__, idx, rxd->hdr, rxd->data_buff,
+	      rxd->stat2, rxd->next_ed);
+
+	priv->rxd_idx = (priv->rxd_idx + 1) % MAX_RX_DESCR;
+
+	return 0;
+}
+
+static const struct eth_ops pic32_eth_ops = {
+	.start		= pic32_eth_start,
+	.send		= pic32_eth_send,
+	.recv		= pic32_eth_recv,
+	.free_pkt	= pic32_eth_free_pkt,
+	.stop		= pic32_eth_stop,
+};
+
+static int pic32_eth_probe(struct udevice *dev)
+{
+	struct eth_pdata *pdata = dev_get_platdata(dev);
+	struct pic32eth_dev *priv = dev_get_priv(dev);
+	const char *phy_mode;
+	void __iomem *iobase;
+	fdt_addr_t addr;
+	fdt_size_t size;
+	int offset = 0;
+	int phy_addr = -1;
+
+	addr = fdtdec_get_addr_size(gd->fdt_blob, dev->of_offset, "reg", &size);
+	if (addr == FDT_ADDR_T_NONE)
+		return -EINVAL;
+
+	iobase = ioremap(addr, size);
+	if (!iobase)
+		return -EINVAL;
+
+	pdata->iobase = (phys_addr_t)addr;
+
+	/* get phy mode */
+	pdata->phy_interface = -1;
+	phy_mode = fdt_getprop(gd->fdt_blob, dev->of_offset, "phy-mode", NULL);
+	if (phy_mode)
+		pdata->phy_interface = phy_get_interface_by_name(phy_mode);
+	if (pdata->phy_interface == -1) {
+		debug("%s: Invalid PHY interface '%s'\n", __func__, phy_mode);
+		return -EINVAL;
+	}
+
+	/* get phy addr */
+	offset = fdtdec_lookup_phandle(gd->fdt_blob, dev->of_offset,
+				       "phy-handle");
+	if (offset > 0)
+		phy_addr = fdtdec_get_int(gd->fdt_blob, offset, "reg", -1);
+
+	/* phy reset gpio */
+	gpio_request_by_name_nodev(gd->fdt_blob, dev->of_offset,
+				   "reset-gpios", 0,
+				   &priv->rst_gpio, GPIOD_IS_OUT);
+
+	priv->phyif	= pdata->phy_interface;
+	priv->phy_addr	= phy_addr;
+	priv->ectl_regs	= iobase;
+	priv->emac_regs	= iobase + PIC32_EMAC1CFG1;
+
+	pic32_mii_init(priv);
+
+	return pic32_phy_init(priv, dev);
+}
+
+static int pic32_eth_remove(struct udevice *dev)
+{
+	struct pic32eth_dev *priv = dev_get_priv(dev);
+	struct mii_dev *bus;
+
+	dm_gpio_free(dev, &priv->rst_gpio);
+	phy_shutdown(priv->phydev);
+	free(priv->phydev);
+	bus = miiphy_get_dev_by_name(PIC32_MDIO_NAME);
+	mdio_unregister(bus);
+	mdio_free(bus);
+	iounmap(priv->ectl_regs);
+	return 0;
+}
+
+static const struct udevice_id pic32_eth_ids[] = {
+	{ .compatible = "microchip,pic32mzda-eth" },
+	{ }
+};
+
+U_BOOT_DRIVER(pic32_ethernet) = {
+	.name			= "pic32_ethernet",
+	.id			= UCLASS_ETH,
+	.of_match		= pic32_eth_ids,
+	.probe			= pic32_eth_probe,
+	.remove			= pic32_eth_remove,
+	.ops			= &pic32_eth_ops,
+	.priv_auto_alloc_size	= sizeof(struct pic32eth_dev),
+	.platdata_auto_alloc_size	= sizeof(struct eth_pdata),
+};
diff --git a/drivers/net/pic32_eth.h b/drivers/net/pic32_eth.h
new file mode 100644
index 0000000..4dd443b
--- /dev/null
+++ b/drivers/net/pic32_eth.h
@@ -0,0 +1,171 @@ 
+/*
+ * (c) 2015 Purna Chandra Mandal <purna.mandal@microchip.com>
+ *
+ * SPDX-License-Identifier:	GPL-2.0+
+ *
+ */
+
+#ifndef __MICROCHIP_PIC32_ETH_H_
+#define __MICROCHIP_PIC32_ETH_H_
+
+#include <mach/pic32.h>
+
+/* Ethernet */
+struct pic32_ectl_regs {
+	struct pic32_reg_atomic con1; /* 0x00 */
+	struct pic32_reg_atomic con2; /* 0x10 */
+	struct pic32_reg_atomic txst; /* 0x20 */
+	struct pic32_reg_atomic rxst; /* 0x30 */
+	struct pic32_reg_atomic ht0;  /* 0x40 */
+	struct pic32_reg_atomic ht1;  /* 0x50 */
+	struct pic32_reg_atomic pmm0; /* 0x60 */
+	struct pic32_reg_atomic pmm1; /* 0x70 */
+	struct pic32_reg_atomic pmcs; /* 0x80 */
+	struct pic32_reg_atomic pmo;  /* 0x90 */
+	struct pic32_reg_atomic rxfc; /* 0xa0 */
+	struct pic32_reg_atomic rxwm; /* 0xb0 */
+	struct pic32_reg_atomic ien;  /* 0xc0 */
+	struct pic32_reg_atomic irq;  /* 0xd0 */
+	struct pic32_reg_atomic stat; /* 0xe0 */
+};
+
+struct pic32_mii_regs {
+	struct pic32_reg_atomic mcfg; /* 0x280 */
+	struct pic32_reg_atomic mcmd; /* 0x290 */
+	struct pic32_reg_atomic madr; /* 0x2a0 */
+	struct pic32_reg_atomic mwtd; /* 0x2b0 */
+	struct pic32_reg_atomic mrdd; /* 0x2c0 */
+	struct pic32_reg_atomic mind; /* 0x2d0 */
+};
+
+struct pic32_emac_regs {
+	struct pic32_reg_atomic cfg1; /* 0x200*/
+	struct pic32_reg_atomic cfg2; /* 0x210*/
+	struct pic32_reg_atomic ipgt; /* 0x220*/
+	struct pic32_reg_atomic ipgr; /* 0x230*/
+	struct pic32_reg_atomic clrt; /* 0x240*/
+	struct pic32_reg_atomic maxf; /* 0x250*/
+	struct pic32_reg_atomic supp; /* 0x260*/
+	struct pic32_reg_atomic test; /* 0x270*/
+	struct pic32_mii_regs mii;    /* 0x280 - 0x2d0 */
+	struct pic32_reg_atomic res1; /* 0x2e0 */
+	struct pic32_reg_atomic res2; /* 0x2f0 */
+	struct pic32_reg_atomic sa0;  /* 0x300 */
+	struct pic32_reg_atomic sa1;  /* 0x310 */
+	struct pic32_reg_atomic sa2;  /* 0x320 */
+};
+
+/* ETHCON1 Reg field */
+#define ETHCON_BUFCDEC		BIT(0)
+#define ETHCON_RXEN		BIT(8)
+#define ETHCON_TXRTS		BIT(9)
+#define ETHCON_ON		BIT(15)
+
+/* ETHCON2 Reg field */
+#define ETHCON_RXBUFSZ		0x7f
+#define ETHCON_RXBUFSZ_SHFT	0x4
+
+/* ETHSTAT Reg field */
+#define ETHSTAT_BUSY		BIT(7)
+#define ETHSTAT_BUFCNT		0x00ff0000
+
+/* ETHRXFC Register fields */
+#define ETHRXFC_BCEN		BIT(0)
+#define ETHRXFC_MCEN		BIT(1)
+#define ETHRXFC_UCEN		BIT(3)
+#define ETHRXFC_RUNTEN		BIT(4)
+#define ETHRXFC_CRCOKEN		BIT(5)
+
+/* EMAC1CFG1 register offset */
+#define PIC32_EMAC1CFG1		0x0200
+
+/* EMAC1CFG1 register fields */
+#define EMAC_RXENABLE		BIT(0)
+#define EMAC_RXPAUSE		BIT(2)
+#define EMAC_TXPAUSE		BIT(3)
+#define EMAC_SOFTRESET		BIT(15)
+
+/* EMAC1CFG2 register fields */
+#define EMAC_FULLDUP		BIT(0)
+#define EMAC_LENGTHCK		BIT(1)
+#define EMAC_CRCENABLE		BIT(4)
+#define EMAC_PADENABLE		BIT(5)
+#define EMAC_AUTOPAD		BIT(7)
+#define EMAC_EXCESS		BIT(14)
+
+/* EMAC1IPGT register magic */
+#define FULLDUP_GAP_TIME	0x15
+#define HALFDUP_GAP_TIME	0x12
+
+/* EMAC1SUPP register fields */
+#define EMAC_RMII_SPD100	BIT(8)
+#define EMAC_RMII_RESET		BIT(11)
+
+/* MII Management Configuration Register */
+#define MIIMCFG_RSTMGMT		BIT(15)
+#define MIIMCFG_CLKSEL_DIV40	0x0020	/* 100Mhz / 40 */
+
+/* MII Management Command Register */
+#define MIIMCMD_READ		BIT(0)
+#define MIIMCMD_SCAN		BIT(1)
+
+/* MII Management Address Register */
+#define MIIMADD_REGADDR		0x1f
+#define MIIMADD_REGADDR_SHIFT	0
+#define MIIMADD_PHYADDR_SHIFT	8
+
+/* MII Management Indicator Register */
+#define MIIMIND_BUSY		BIT(0)
+#define MIIMIND_NOTVALID	BIT(2)
+#define MIIMIND_LINKFAIL	BIT(3)
+
+/* Packet Descriptor */
+/* Received Packet Status */
+#define _RSV1_PKT_CSUM		0xffff
+#define _RSV2_CRC_ERR		BIT(20)
+#define _RSV2_LEN_ERR		BIT(21)
+#define _RSV2_RX_OK		BIT(23)
+#define _RSV2_RX_COUNT		0xffff
+
+#define RSV_RX_CSUM(__rsv1)	((__rsv1) & _RSV1_PKT_CSUM)
+#define RSV_RX_COUNT(__rsv2)	((__rsv2) & _RSV2_RX_COUNT)
+#define RSV_RX_OK(__rsv2)	((__rsv2) & _RSV2_RX_OK)
+#define RSV_CRC_ERR(__rsv2)	((__rsv2) & _RSV2_CRC_ERR)
+
+/* Ethernet Hardware Descriptor Header bits */
+#define EDH_EOWN		BIT(7)
+#define EDH_NPV			BIT(8)
+#define EDH_STICKY		BIT(9)
+#define _EDH_BCOUNT		0x07ff0000
+#define EDH_EOP			BIT(30)
+#define EDH_SOP			BIT(31)
+#define EDH_BCOUNT_SHIFT	16
+#define EDH_BCOUNT(len)		((len) << EDH_BCOUNT_SHIFT)
+
+/* Ethernet Hardware Descriptors
+ * ref: PIC32 Family Reference Manual Table 35-7
+ * This structure represents the layout of the DMA
+ * memory shared between the CPU and the Ethernet
+ * controller.
+ */
+/* TX/RX DMA descriptor */
+struct eth_dma_desc {
+	u32 hdr;	/* header */
+	u32 data_buff;	/* data buffer address */
+	u32 stat1;	/* transmit/receive packet status */
+	u32 stat2;	/* transmit/receive packet status */
+	u32 next_ed;	/* next descriptor */
+};
+
+/* cache operation helper */
+#define __dcache_flush(__a, __l) \
+	flush_dcache_range((ulong)(__a),  ((__l) + (ulong)(__a)))
+
+#define __dcache_invalidate(__a, __l) \
+	invalidate_dcache_range((ulong)(__a),  ((__l) + (ulong)(__a)))
+
+#define PIC32_MDIO_NAME "PIC32_EMAC"
+
+int pic32_mdio_init(const char *name, ulong ioaddr);
+
+#endif /* __MICROCHIP_PIC32_ETH_H_*/
diff --git a/drivers/net/pic32_mdio.c b/drivers/net/pic32_mdio.c
new file mode 100644
index 0000000..578fc96
--- /dev/null
+++ b/drivers/net/pic32_mdio.c
@@ -0,0 +1,121 @@ 
+/*
+ * pic32_mdio.c: PIC32 MDIO/MII driver, part of pic32_eth.c.
+ *
+ * Copyright 2015 Microchip Inc.
+ *	Purna Chandra Mandal <purna.mandal@microchip.com>
+ *
+ * SPDX-License-Identifier:	GPL-2.0+
+ */
+#include <common.h>
+#include <phy.h>
+#include <miiphy.h>
+#include <errno.h>
+#include <wait_bit.h>
+#include <asm/io.h>
+#include "pic32_eth.h"
+
+static int pic32_mdio_write(struct mii_dev *bus,
+			    int addr, int dev_addr,
+			    int reg, u16 value)
+{
+	u32 v;
+	struct pic32_mii_regs *mii_regs = bus->priv;
+
+	/* Wait for the previous operation to finish */
+	wait_for_bit(__func__, &mii_regs->mind.raw, MIIMIND_BUSY,
+		     false, CONFIG_SYS_HZ, true);
+
+	/* Put phyaddr and regaddr into MIIMADD */
+	v = (addr << MIIMADD_PHYADDR_SHIFT) | (reg & MIIMADD_REGADDR);
+	writel(v, &mii_regs->madr.raw);
+
+	/* Initiate a write command */
+	writel(value, &mii_regs->mwtd.raw);
+
+	/* Wait 30 clock cycles for busy flag to be set */
+	udelay(12);
+
+	/* Wait for write to complete */
+	wait_for_bit(__func__, &mii_regs->mind.raw, MIIMIND_BUSY,
+		     false, CONFIG_SYS_HZ, true);
+
+	return 0;
+}
+
+static int pic32_mdio_read(struct mii_dev *bus, int addr, int devaddr, int reg)
+{
+	u32 v;
+	struct pic32_mii_regs *mii_regs = bus->priv;
+
+	/* Wait for the previous operation to finish */
+	wait_for_bit(__func__, &mii_regs->mind.raw, MIIMIND_BUSY,
+		     false, CONFIG_SYS_HZ, true);
+
+	/* Put phyaddr and regaddr into MIIMADD */
+	v = (addr << MIIMADD_PHYADDR_SHIFT) | (reg & MIIMADD_REGADDR);
+	writel(v, &mii_regs->madr.raw);
+
+	/* Initiate a read command */
+	writel(MIIMCMD_READ, &mii_regs->mcmd.raw);
+
+	/* Wait 30 clock cycles for busy flag to be set */
+	udelay(12);
+
+	/* Wait for read to complete */
+	wait_for_bit(__func__, &mii_regs->mind.raw,
+		     MIIMIND_NOTVALID | MIIMIND_BUSY,
+		     false, CONFIG_SYS_HZ, false);
+
+	/* Clear the command register */
+	writel(0, &mii_regs->mcmd.raw);
+
+	/* Grab the value read from the PHY */
+	v = readl(&mii_regs->mrdd.raw);
+	return v;
+}
+
+static int pic32_mdio_reset(struct mii_dev *bus)
+{
+	struct pic32_mii_regs *mii_regs = bus->priv;
+
+	/* Reset MII (due to new addresses) */
+	writel(MIIMCFG_RSTMGMT, &mii_regs->mcfg.raw);
+
+	/* Wait for the operation to finish */
+	wait_for_bit(__func__, &mii_regs->mind.raw, MIIMIND_BUSY,
+		     false, CONFIG_SYS_HZ, true);
+
+	/* Clear reset bit */
+	writel(0, &mii_regs->mcfg);
+
+	/* Wait for the operation to finish */
+	wait_for_bit(__func__, &mii_regs->mind.raw, MIIMIND_BUSY,
+		     false, CONFIG_SYS_HZ, true);
+
+	/* Set the MII Management Clock (MDC) - no faster than 2.5 MHz */
+	writel(MIIMCFG_CLKSEL_DIV40, &mii_regs->mcfg.raw);
+
+	/* Wait for the operation to finish */
+	wait_for_bit(__func__, &mii_regs->mind.raw, MIIMIND_BUSY,
+		     false, CONFIG_SYS_HZ, true);
+	return 0;
+}
+
+int pic32_mdio_init(const char *name, ulong ioaddr)
+{
+	struct mii_dev *bus;
+
+	bus = mdio_alloc();
+	if (!bus) {
+		printf("Failed to allocate PIC32-MDIO bus\n");
+		return -ENOMEM;
+	}
+
+	bus->read = pic32_mdio_read;
+	bus->write = pic32_mdio_write;
+	bus->reset = pic32_mdio_reset;
+	strncpy(bus->name, name, sizeof(bus->name));
+	bus->priv = (void *)ioaddr;
+
+	return mdio_register(bus);
+}