* [U-Boot] [PATCH v1 13/18] drivers: net: add Microchip PIC32 ethernet controller driver
@ 2015-12-17 17:32 Purna Chandra Mandal
0 siblings, 0 replies; only message in thread
From: Purna Chandra Mandal @ 2015-12-17 17:32 UTC (permalink / raw)
To: u-boot
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>
---
drivers/net/Kconfig | 7 +
drivers/net/Makefile | 1 +
drivers/net/pic32_eth.c | 648 +++++++++++++++++++++++++++++++++++++++++++++++
drivers/net/pic32_eth.h | 184 ++++++++++++++
drivers/net/pic32_mdio.c | 143 +++++++++++
5 files changed, 983 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 6905cc0..2d7bf7c 100644
--- a/drivers/net/Kconfig
+++ b/drivers/net/Kconfig
@@ -107,4 +107,11 @@ config ZYNQ_GEM
help
This MAC is presetn 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..ba499fb
--- /dev/null
+++ b/drivers/net/pic32_eth.c
@@ -0,0 +1,648 @@
+/*
+ * (c) 2015 Purna Chandra Mandal <purna.mandal@microchip.com>
+ *
+ * SPDX-License-Identifier: GPL-2.0+
+ *
+ */
+#include <common.h>
+#include <malloc.h>
+#include <errno.h>
+#include <miiphy.h>
+#include <phy.h>
+#include <asm/io.h>
+#include <console.h>
+#include <dm.h>
+#include <net.h>
+#include <asm/gpio.h>
+
+#include "pic32_eth.h"
+
+/* local definitions */
+#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];
+ struct pic32_ectl_regs *ectl_regs;
+ struct pic32_emac_regs *emac_regs;
+ struct phy_device *phydev;
+ phy_interface_t phyif;
+ u32 phy_id; /* PHY addr */
+ u32 rxd_idx; /* index of RX desc to read */
+ struct gpio_desc rst_gpio;
+};
+
+__attribute__ ((weak)) void 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_netphy_reset(pedev);
+
+ /* disable RX, TX & all transactions */
+ writel(ETHCON_ON | ETHCON_TXRTS | ETHCON_RXEN, &ectl_p->con1.clr);
+
+ /* wait until not BUSY */
+ while (readl(&ectl_p->stat.raw) & ETHSTAT_BUSY)
+ ;
+
+ /* turn controller ON to access PHY over MII */
+ writel(ETHCON_ON, &ectl_p->con1.set);
+
+ udelay(DELAY_10MSEC);
+
+ /* 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_id,
+ 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 0;
+ }
+
+ 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 0;
+ }
+
+ 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 1;
+}
+
+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_id, 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 mii_dev *mii;
+ struct pic32_emac_regs *emac_p = pedev->emac_regs;
+
+ /* 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)
+{
+ u32 v;
+ struct pic32_ectl_regs *ectl_p = pedev->ectl_regs;
+
+ /* disable RX, TX & any other transactions */
+ writel(ETHCON_ON | ETHCON_TXRTS | ETHCON_RXEN, &ectl_p->con1.clr);
+
+ /* wait until not BUSY */
+ while (readl(&ectl_p->stat.raw) & ETHSTAT_BUSY)
+ ;
+ /* 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)
+{
+ u32 idx, bufsz;
+ struct eth_dma_desc *rxd;
+ struct pic32_ectl_regs *ectl_p = pedev->ectl_regs;
+
+ 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)
+{
+ 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 */
+ while (readl(&ectl_p->stat.raw) & ETHSTAT_BUSY)
+ ;
+
+ /* clear any existing interrupt event */
+ writel(0xffffffff, &ectl_p->irq.clr);
+}
+
+static int _pic32eth_init(struct pic32eth_device *pedev, u8 *macaddr)
+{
+ /* 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 descriptor is taken care in xmit */
+ _eth_desc_init(pedev);
+
+ /* Start up & update link status of PHY */
+ phy_startup(pedev->phydev);
+
+ /* adjust mac with phy link status */
+ if (!_mac_adjust_link(pedev)) {
+ _pic32eth_halt(pedev);
+ return -1;
+ }
+
+ /* If there's no link, fail */
+ return pedev->phydev->link ? 0 : -1;
+}
+
+static int _pic32eth_xmit(struct pic32eth_device *pedev,
+ void *packet, int length)
+{
+ u64 deadline;
+ struct eth_dma_desc *txd;
+ struct pic32_ectl_regs *ectl_p = pedev->ectl_regs;
+
+ 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)
+{
+ int idx;
+ struct eth_dma_desc *rxd;
+ struct pic32_ectl_regs *ectl_p = pedev->ectl_regs;
+
+ 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)
+{
+ int idx, top;
+ u32 rx_stat;
+ int rx_count, bytes_rcvd = 0;
+ struct eth_dma_desc *rxd;
+
+ 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);
+}
+
+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);
+ u32 iobase = pdata->iobase;
+ int phy_id = 0;
+
+#if defined(CONFIG_PHY_ADDR)
+ phy_id = CONFIG_PHY_ADDR;
+#endif
+ /* initialize */
+ priv->phy_id = phy_id;
+ 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..29b822a
--- /dev/null
+++ b/drivers/net/pic32_eth.h
@@ -0,0 +1,184 @@
+/*
+ * (c) 2015 Purna Chandra Mandal <purna.mandal@microchip.com>
+ *
+ * SPDX-License-Identifier: GPL-2.0+
+ *
+ */
+
+#ifndef PIC32_ETH_H
+#define PIC32_ETH_H
+
+#include <stddef.h>
+#include <asm/arch-pic32/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 resv1[2]; /* 0x2e0 - 0x2f0 */
+ struct pic32_reg_atomic sa0; /* 0x300 */
+ struct pic32_reg_atomic sa1; /* 0x310 */
+ struct pic32_reg_atomic sa2; /* 0x320 */
+};
+
+/* ETHCON1 Reg field */
+#define ETHCON_ON 0x8000
+#define ETHCON_TXRTS 0x0200
+#define ETHCON_RXEN 0x0100
+#define ETHCON_BUFCDEC 0x0001
+
+/* ETHCON2 Reg field */
+#define ETHCON_RXBUFSZ 0x7f
+#define ETHCON_RXBUFSZ_SHFT 0x00000004
+
+/* ETHSTAT Reg field */
+#define ETHSTAT_BUSY 0x00000080
+#define ETHSTAT_BUFCNT 0x00FF0000
+
+/* ETHRXFC Register fields */
+#define ETHRXFC_CRCOKEN 0x0040
+#define ETHRXFC_RUNTEN 0x0010
+#define ETHRXFC_UCEN 0x0008
+#define ETHRXFC_MCEN 0x0002
+#define ETHRXFC_BCEN 0x0001
+
+/* EMAC1CFG1 register offset */
+#define PIC32_EMAC1CFG1 0x0200
+
+/* EMAC1CFG1 register fields */
+#define EMAC_SOFTRESET 0x8000
+#define EMAC_TXPAUSE 0x0008
+#define EMAC_RXPAUSE 0x0004
+#define EMAC_RXENABLE 0x0001
+
+/* EMAC1CFG2 register fields */
+#define EMAC_EXCESS 0x4000
+#define EMAC_AUTOPAD 0x0080
+#define EMAC_PADENABLE 0x0020
+#define EMAC_CRCENABLE 0x0010
+#define EMAC_LENGTHCK 0x0002
+#define EMAC_FULLDUP 0x0001
+
+/* EMAC1IPGT register magic */
+#define FULLDUP_GAP_TIME 0x15
+#define HALFDUP_GAP_TIME 0x12
+
+/* EMAC1SUPP register fields */
+#define EMAC_RMII_SPD100 0x0100
+#define EMAC_RMII_RESET 0x0800
+
+/* MII Management Configuration Register */
+#define MIIMCFG_RSTMGMT 0x8000
+#define MIIMCFG_CLKSEL_DIV40 0x0020 /* 100Mhz / 40 */
+
+/* MII Management Command Register */
+#define MIIMCMD_READ 0x01
+#define MIIMCMD_SCAN 0x02
+
+/* MII Management Address Register */
+#define MIIMADD_PHYADDR_SHIFT 8
+#define MIIMADD_REGADDR 0x1f
+#define MIIMADD_REGADDR_SHIFT 0
+
+/* MII Management Indicator Register */
+#define MIIMIND_BUSY 0x01
+#define MIIMIND_NOTVALID 0x04
+#define MIIMIND_LINKFAIL 0x08
+
+/* Packet Descriptor
+ *
+ * Descriptor of a packet accepted by the TX/RX Ethernet engine.
+ * ref: PIC32 Family Reference Manual Table 35-7
+ *
+ * A packet handled by the Ethernet TX/RX engine is a list of buffer
+ * descriptors. A packet may consist of more than one buffer.
+ * Each buffer needs a descriptor.
+ *
+ */
+
+/* Received Packet Status */
+#define _RSV1_PKT_CSUM 0xffff
+#define _RSV2_RX_OK (1 << 23)
+#define _RSV2_LEN_ERR (1 << 21)
+#define _RSV2_CRC_ERR (1 << 20)
+#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 0x00000080
+#define EDH_NPV 0x00000100
+#define EDH_STICKY 0x00000200
+#define _EDH_BCOUNT 0x07ff0000
+#define EDH_EOP 0x40000000
+#define EDH_SOP 0x80000000
+#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 ops 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 /**/
diff --git a/drivers/net/pic32_mdio.c b/drivers/net/pic32_mdio.c
new file mode 100644
index 0000000..b27eac4
--- /dev/null
+++ b/drivers/net/pic32_mdio.c
@@ -0,0 +1,143 @@
+/*
+ * 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 <miiphy.h>
+#include <phy.h>
+#include <asm/io.h>
+
+#include "pic32_eth.h"
+
+#if !defined(CONFIG_MII)
+# error "MIC32-PEMAC requires MII -- missing CONFIG_MII"
+#endif
+
+#if !defined(CONFIG_PHYLIB)
+# error "PIC32-PEMAC requires PHYLIB -- missing CONFIG_PHYLIB"
+#endif
+
+static inline int wait_till_busy(struct pic32_mii_regs *mii_regs,
+ u32 mask, u32 exp)
+{
+ ulong start, timeout = get_tbclk() * 2;
+
+ start = get_timer(0);
+ for (;;) {
+ if ((readl(&mii_regs->mind) & mask) == exp)
+ return 0;
+
+ if (get_timer(start) > timeout)
+ break;
+ }
+
+ printf("%s, %s: wait timedout\n", __FILE__, __func__);
+ return -1;
+}
+
+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_till_busy(mii_regs, MIIMIND_BUSY, 0);
+
+ /* Put phyaddr and regaddr into MIIMADD */
+ v = (addr << MIIMADD_PHYADDR_SHIFT) | (reg & MIIMADD_REGADDR);
+ writel(v, &mii_regs->madr);
+
+ /* Initiate a write command */
+ writel(value, &mii_regs->mwtd);
+
+ /* Wait 30 clock cycles for busy flag to be set */
+ udelay(1);
+
+ /* Wait for write to complete */
+ wait_till_busy(mii_regs, MIIMIND_BUSY, 0);
+
+ 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_till_busy(mii_regs, MIIMIND_BUSY, 0);
+
+ /* Put phyaddr and regaddr into MIIMADD */
+ v = (addr << MIIMADD_PHYADDR_SHIFT) | (reg & MIIMADD_REGADDR);
+ writel(v, &mii_regs->madr);
+
+ /* Initiate a read command */
+ writel(MIIMCMD_READ, &mii_regs->mcmd);
+
+ /* Wait 30 clock cycles for busy flag to be set */
+ udelay(1);
+
+ /* Wait for read to complete */
+ wait_till_busy(mii_regs, MIIMIND_NOTVALID | MIIMIND_BUSY, 0);
+
+ /* Clear the command register */
+ writel(0, &mii_regs->mcmd);
+
+ /* Grab the value read from the PHY */
+ v = readl(&mii_regs->mrdd);
+ 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);
+
+ /* Wait for the operation to finish */
+ while (readl(&mii_regs->mind) & MIIMIND_BUSY)
+ ;
+
+ /* Clear reset bit */
+ writel(0, &mii_regs->mcfg);
+
+ /* Wait for the operation to finish */
+ while (readl(&mii_regs->mind) & MIIMIND_BUSY)
+ ;
+
+ /* Set the MII Management Clock (MDC) - no faster than 2.5 MHz */
+ writel(MIIMCFG_CLKSEL_DIV40, &mii_regs->mcfg);
+
+ /* Wait for the operation to finish */
+ while (readl(&mii_regs->mind) & MIIMIND_BUSY)
+ ;
+
+ 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 -1;
+ }
+
+ bus->read = pic32_mdio_read;
+ bus->write = pic32_mdio_write;
+ bus->reset = pic32_mdio_reset;
+ sprintf(bus->name, name);
+
+ bus->priv = (void *)ioaddr;
+
+ return mdio_register(bus);
+}
--
1.8.3.1
^ permalink raw reply related [flat|nested] only message in thread
only message in thread, other threads:[~2015-12-17 17:32 UTC | newest]
Thread overview: (only message) (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2015-12-17 17:32 [U-Boot] [PATCH v1 13/18] drivers: net: add Microchip PIC32 ethernet controller driver Purna Chandra Mandal
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.