[U-Boot] [PATCH v2 12/13] drivers: net: add Microchip PIC32 ethernet controller driver.

Joe Hershberger
2016-01-05T23:00:10Z

On Mon, Jan 4, 2016 at 8:02 AM, Purna Chandra Mandal
<purna.mandal@microchip.com> wrote:
> This driver implements MAC and MII layer of PIC32 ethernet controller.
> This controller is capable of handling 100/10mbps full/half duplex
> ethernet communication. Network data transfer is handled by internal
> DMA engine.
>
> Signed-off-by: Purna Chandra Mandal <purna.mandal@microchip.com>
> ---
>
> Changes in v2: None
>
> drivers/net/Kconfig | 7 +
> drivers/net/Makefile | 1 +
> drivers/net/pic32_eth.c | 652 +++++++++++++++++++++++++++++++++++++++++++++++
> drivers/net/pic32_eth.h | 174 +++++++++++++
> drivers/net/pic32_mdio.c | 121 +++++++++
> 5 files changed, 955 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
> + 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..049e9df
> --- /dev/null
> +++ b/drivers/net/pic32_eth.c
> @@ -0,0 +1,652 @@
> +/*
> + * (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_device {
> + 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_device *pedev = (struct pic32eth_device *)dev;
> +
> + if (!dm_gpio_is_valid(&pedev->rst_gpio))
> + return;
> +
> + /* phy reset */
> + dm_gpio_set_value(&pedev->rst_gpio, 0);
> + udelay(300);
> + dm_gpio_set_value(&pedev->rst_gpio, 1);
> + udelay(300);
> +}
> +
> +/* Initialize mii(MDIO) interface, discover which PHY is
> + * attached to the device, and configure it properly.
> + */
> +static int _mdio_init(struct pic32eth_device *pedev)
> +{
> + struct pic32_ectl_regs *ectl_p = pedev->ectl_regs;
> + struct pic32_emac_regs *emac_p = pedev->emac_regs;
> +
> + /* board phy reset */
> + board_netphy_reset(pedev);
> +
> + /* 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);
> +
> + udelay(DELAY_10MSEC);

If you name the constant simply for the value it contains, there is
little value in having the constant. Perhaps name it for the purpose
it serves.

> + /* reset MAC */
> + writel(EMAC_SOFTRESET, &emac_p->cfg1.set); /* reset assert */
> + udelay(DELAY_10MSEC);
> + writel(EMAC_SOFTRESET, &emac_p->cfg1.clr); /* reset deassert */
> +
> + /* initialize MDIO/MII */
> + if (pedev->phyif == PHY_INTERFACE_MODE_RMII) {
> + writel(EMAC_RMII_RESET, &emac_p->supp.set);
> + udelay(DELAY_10MSEC);
> + writel(EMAC_RMII_RESET, &emac_p->supp.clr);
> + }
> +
> + return pic32_mdio_init(PIC32_MDIO_NAME, (ulong)&emac_p->mii);
> +}
> +
> +static int _phy_init(struct pic32eth_device *pedev, void *dev)
> +{
> + struct mii_dev *mii;
> +
> + mii = miiphy_get_dev_by_name(PIC32_MDIO_NAME);
> +
> + /* find & connect PHY */
> + pedev->phydev = phy_connect(mii, pedev->phy_addr,
> + dev, pedev->phyif);
> + if (!pedev->phydev) {
> + printf("%s: %s: Error, PHY connect\n", __FILE__, __func__);
> + return 0;
> + }
> +
> + /* Wait for phy to complete reset */
> + udelay(DELAY_10MSEC);
> +
> + /* configure supported modes */
> + pedev->phydev->supported = SUPPORTED_10baseT_Half |
> + SUPPORTED_10baseT_Full |
> + SUPPORTED_100baseT_Half |
> + SUPPORTED_100baseT_Full |
> + SUPPORTED_Autoneg;
> +
> + pedev->phydev->advertising = ADVERTISED_10baseT_Half |
> + ADVERTISED_10baseT_Full |
> + ADVERTISED_100baseT_Half |
> + ADVERTISED_100baseT_Full |
> + ADVERTISED_Autoneg;
> +
> + pedev->phydev->autoneg = AUTONEG_ENABLE;
> +
> + return 0;
> +}
> +
> +/* Configure MAC based on negotiated speed and duplex
> + * reported by PHY.
> + */
> +static int _mac_adjust_link(struct pic32eth_device *pedev)
> +{
> + struct phy_device *phydev = pedev->phydev;
> + struct pic32_emac_regs *emac_p = pedev->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 _mac_init(struct pic32eth_device *pedev, u8 *macaddr)
> +{
> + struct pic32_emac_regs *emac_p = pedev->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(pedev->phydev, pedev->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 */
> + udelay(DELAY_10MSEC);
> +}
> +
> +static void _mac_reset(struct pic32eth_device *pedev)
> +{
> + struct pic32_emac_regs *emac_p = pedev->emac_regs;
> + struct mii_dev *mii;
> +
> + /* Reset MAC */
> + writel(EMAC_SOFTRESET, &emac_p->cfg1.raw);
> + udelay(DELAY_10MSEC);
> +
> + /* clear reset */
> + writel(0, &emac_p->cfg1.raw);
> +
> + /* Reset MII */
> + mii = pedev->phydev->bus;
> + if (mii && mii->reset)
> + mii->reset(mii);
> +}
> +
> +/* initializes the MAC and PHY, then establishes a link */
> +static void _eth_ctrl_reset(struct pic32eth_device *pedev)
> +{
> + struct pic32_ectl_regs *ectl_p = pedev->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 _eth_desc_init(struct pic32eth_device *pedev)
> +{
> + struct pic32_ectl_regs *ectl_p = pedev->ectl_regs;
> + struct eth_dma_desc *rxd;
> + u32 idx, bufsz;
> +
> + pedev->rxd_idx = 0;
> + for (idx = 0; idx < MAX_RX_DESCR; idx++) {
> + rxd = &pedev->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(&pedev->rxd_ring[0]);
> +
> + /* flush rx ring */
> + __dcache_flush(pedev->rxd_ring, sizeof(pedev->rxd_ring));
> +
> + /* set rx desc-ring start address */
> + writel((ulong)virt_to_phys(&pedev->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 void _pic32eth_halt(struct pic32eth_device *pedev)

Name this stop.

> +{
> + struct pic32_ectl_regs *ectl_p = pedev->ectl_regs;
> + struct pic32_emac_regs *emac_p = pedev->emac_regs;
> +
> + /* Reset the phy if the controller is enabled */
> + if (readl(&ectl_p->con1.raw) & ETHCON_ON)
> + phy_reset(pedev->phydev);
> +
> + /* Shut down the PHY */
> + phy_shutdown(pedev->phydev);
> +
> + /* Stop rx/tx */
> + writel(ETHCON_TXRTS | ETHCON_RXEN, &ectl_p->con1.clr);
> + udelay(DELAY_10MSEC);
> +
> + /* reset MAC */
> + writel(EMAC_SOFTRESET, &emac_p->cfg1.raw);
> +
> + /* clear reset */
> + writel(0, &emac_p->cfg1.raw);
> + udelay(DELAY_10MSEC);
> +
> + /* disable controller */
> + writel(ETHCON_ON, &ectl_p->con1.clr);
> + udelay(DELAY_10MSEC);
> +
> + /* 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 _pic32eth_init(struct pic32eth_device *pedev, u8 *macaddr)

Name this start. Move this above stop.

> +{
> + int ret;
> +
> + /* configure controller */
> + _eth_ctrl_reset(pedev);
> +
> + /* reset mac_regs */
> + _mac_reset(pedev);
> +
> + /* configure the PHY */
> + phy_config(pedev->phydev);
> +
> + /* initialize MAC */
> + _mac_init(pedev, macaddr);
> +
> + /* init RX descriptor; TX descriptors are handled in xmit */
> + _eth_desc_init(pedev);
> +
> + /* Start up & update link status of PHY */
> + phy_startup(pedev->phydev);
> +
> + /* adjust mac with phy link status */
> + ret = _mac_adjust_link(pedev);
> + if (ret) {
> + _pic32eth_halt(pedev);
> + return ret;
> + }
> +
> + return 0;
> +}
> +
> +static int _pic32eth_xmit(struct pic32eth_device *pedev,
> + void *packet, int length)
> +{
> + struct pic32_ectl_regs *ectl_p = pedev->ectl_regs;
> + struct eth_dma_desc *txd;
> + u64 deadline;
> +
> + txd = &pedev->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;
> +
> + /* tx completed? */
> + if (readl(&ectl_p->con1.raw) & ETHCON_TXRTS)
> + continue;
> +
> + /* h/w not released ownership yet? */
> + __dcache_invalidate(txd, sizeof(*txd));
> + if (!(txd->hdr & EDH_EOWN))
> + break;
> +
> + if (ctrlc())
> + break;
> + }
> +
> + return 0;
> +}
> +
> +static int _pic32eth_free_packet(struct pic32eth_device *pedev, uchar *packet)

It would be more readable if this function is below the rx_poll function.

> +{
> + struct pic32_ectl_regs *ectl_p = pedev->ectl_regs;
> + struct eth_dma_desc *rxd;
> + int idx;
> +
> + idx = pedev->rxd_idx;
> + if (packet != net_rx_packets[idx]) {
> + printf("rxd_id %d: packet is not matched,\n", idx);
> + return -EAGAIN;
> + }
> +
> + /* prepare for new receive */
> + rxd = &pedev->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 /fill-idx %i, .hdr=%x, .data_buff %x, .stat %x, .nexted %x / rx-idx %i\n",
> + __func__, __LINE__, idx, rxd->hdr, rxd->data_buff,
> + rxd->stat2, rxd->next_ed, pedev->rxd_idx);
> +
> + pedev->rxd_idx = (pedev->rxd_idx + 1) % MAX_RX_DESCR;
> +
> + return 0;
> +}
> +
> +static int _pic32eth_rx_poll(struct pic32eth_device *pedev, uchar **packetp)
> +{
> + struct eth_dma_desc *rxd;
> + int rx_count, bytes_rcvd = 0;
> + int idx, top;
> + u32 rx_stat;
> +
> + top = (pedev->rxd_idx + MAX_RX_DESCR - 1) % MAX_RX_DESCR;
> +
> + /* non-blocking receive loop - receive until nothing left to receive */
> + for (idx = pedev->rxd_idx; idx != top; idx = (idx + 1) % MAX_RX_DESCR) {
> + rxd = &pedev->rxd_ring[idx];
> +
> + /* check ownership */
> + __dcache_invalidate(rxd, sizeof(*rxd));
> + if (rxd->hdr & EDH_EOWN)
> + break;
> +
> + if (rxd->hdr & EDH_SOP) {
> + if (!(rxd->hdr & EDH_EOP)) {
> + printf("%s: %s, rx pkt across multiple descr\n",
> + __FILE__, __func__);
> + goto refill_one;
> + }
> +
> + rx_stat = rxd->stat2;
> + rx_count = RSV_RX_COUNT(rx_stat);
> +
> + debug("%s: %d /rx-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);
> +
> + /* we can do some basic checks */
> + if ((!RSV_RX_OK(rx_stat)) || RSV_CRC_ERR(rx_stat)) {
> + debug("%s: %s: Error, rx problem detected\n",
> + __FILE__, __func__);
> + goto refill_one;
> + }
> +
> + /* invalidate dcache */
> + __dcache_invalidate(net_rx_packets[idx], rx_count);
> +
> + /* increment number of bytes rcvd (ignore CRC) */
> + bytes_rcvd += (rx_count - 4);
> +
> + /* Pass the packet to protocol layer */
> + *packetp = net_rx_packets[idx];
> + break;
> +refill_one:
> + _pic32eth_free_packet(pedev, net_rx_packets[idx]);
> + }
> +
> + if (ctrlc())
> + break;
> + }
> +
> + return bytes_rcvd ? bytes_rcvd : -EAGAIN;
> +}
> +
> +static int pic32_eth_start(struct udevice *dev)
> +{
> + struct eth_pdata *pdata = dev_get_platdata(dev);
> + struct pic32eth_device *priv = dev_get_priv(dev);
> +
> + return _pic32eth_init(priv, pdata->enetaddr);

The only reason this sort of pattern with wrappers is a good idea is
when you are trying to support driver model and the old way. Since
that is not the case for this driver, please just directly place the
requisite code in these functions.

> +}
> +
> +static int pic32_eth_send(struct udevice *dev, void *packet, int length)
> +{
> + struct pic32eth_device *priv = dev_get_priv(dev);
> +
> + return _pic32eth_xmit(priv, packet, length);
> +}
> +
> +static int pic32_eth_recv(struct udevice *dev, int flags, uchar **packetp)
> +{
> + struct pic32eth_device *priv = dev_get_priv(dev);
> +
> + return _pic32eth_rx_poll(priv, packetp);
> +}
> +
> +static int pic32_eth_free_pkt(struct udevice *dev, uchar *packet, int length)
> +{
> + struct pic32eth_device *priv = dev_get_priv(dev);
> +
> + return _pic32eth_free_packet(priv, packet);
> +}
> +
> +static void pic32_eth_stop(struct udevice *dev)
> +{
> + struct pic32eth_device *priv = dev_get_priv(dev);
> +
> + return _pic32eth_halt(priv);
> +}
> +
> +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_device *priv = dev_get_priv(dev);
> + void __iomem *iobase;
> + int phy_addr = 0;
> +
> +#if defined(CONFIG_PHY_ADDR)
> + phy_addr = CONFIG_PHY_ADDR;

Doesn't this usually come from the device tree these days?

> +#endif
> + iobase = pic32_ioremap((ulong)pdata->iobase);
> +
> + /* initialize */
> + priv->phy_addr = phy_addr;
> + priv->phyif = pdata->phy_interface;
> + priv->ectl_regs = (struct pic32_ectl_regs *)(iobase);
> + priv->emac_regs = (struct pic32_emac_regs *)(iobase + PIC32_EMAC1CFG1);
> +
> + gpio_request_by_name_nodev(gd->fdt_blob, dev->of_offset,
> + "reset-gpios", 0,
> + &priv->rst_gpio, GPIOD_IS_OUT);
> + _mdio_init(priv);
> +
> + return _phy_init(priv, dev);
> +}
> +
> +static int pic32_eth_remove(struct udevice *dev)
> +{
> + struct pic32eth_device *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);
> +
> + return 0;
> +}
> +
> +static int pic32_eth_ofdata_to_platdata(struct udevice *dev)
> +{
> + struct eth_pdata *pdata = dev_get_platdata(dev);
> + const char *phy_mode;
> +
> + pdata->iobase = dev_get_addr(dev);
> + 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;
> + }
> +
> + 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,
> + .ofdata_to_platdata = pic32_eth_ofdata_to_platdata,
> + .probe = pic32_eth_probe,
> + .remove = pic32_eth_remove,
> + .ops = &pic32_eth_ops,
> + .priv_auto_alloc_size = sizeof(struct pic32eth_device),
> + .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..5933661
> --- /dev/null
> +++ b/drivers/net/pic32_eth.h
> @@ -0,0 +1,174 @@
> +/*
> + * (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 */
> +};
> +
> +/* Delay/Timeout range */
> +#define DELAY_10MSEC 10000UL
> +
> +/* 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);
> +}
> --
> 1.8.3.1
>
>
> _______________________________________________
> U-Boot mailing list
> U-Boot at lists.denx.de
> http://lists.denx.de/mailman/listinfo/u-boot