From: Steve Rae <srae@broadcom.com>
To: u-boot@lists.denx.de
Subject: [U-Boot] [PATCH v2 2/3] arm: bcm281xx: net: Add Ethernet Driver
Date: Fri, 1 Aug 2014 20:37:16 -0700 [thread overview]
Message-ID: <1406950637-19820-2-git-send-email-srae@broadcom.com> (raw)
In-Reply-To: <1406950637-19820-1-git-send-email-srae@broadcom.com>
From: Jiandong Zheng <jdzheng@broadcom.com>
The Broadcom StarFighter2 Ethernet driver is used in multiple Broadcom
SoC(s) and:
- supports multiple MAC blocks,
- provides support for the Broadcom GMAC.
This driver requires MII and PHYLIB.
Signed-off-by: Jiandong Zheng <jdzheng@broadcom.com>
Signed-off-by: Steve Rae <srae@broadcom.com>
---
Changes in v2:
- added Ethernet Driver
drivers/net/Makefile | 2 +
drivers/net/bcm-sf2-eth-gmac.c | 971 +++++++++++++++++++++++++++++++++++++++++
drivers/net/bcm-sf2-eth-gmac.h | 224 ++++++++++
drivers/net/bcm-sf2-eth.c | 268 ++++++++++++
drivers/net/bcm-sf2-eth.h | 70 +++
include/netdev.h | 1 +
6 files changed, 1536 insertions(+)
create mode 100644 drivers/net/bcm-sf2-eth-gmac.c
create mode 100644 drivers/net/bcm-sf2-eth-gmac.h
create mode 100644 drivers/net/bcm-sf2-eth.c
create mode 100644 drivers/net/bcm-sf2-eth.h
diff --git a/drivers/net/Makefile b/drivers/net/Makefile
index 7cc6b6f..14b5eb2 100644
--- a/drivers/net/Makefile
+++ b/drivers/net/Makefile
@@ -10,6 +10,8 @@ obj-$(CONFIG_ALTERA_TSE) += altera_tse.o
obj-$(CONFIG_ARMADA100_FEC) += armada100_fec.o
obj-$(CONFIG_DRIVER_AT91EMAC) += at91_emac.o
obj-$(CONFIG_DRIVER_AX88180) += ax88180.o
+obj-$(CONFIG_BCM_SF2_ETH) += bcm-sf2-eth.o
+obj-$(CONFIG_BCM_SF2_ETH_GMAC) += bcm-sf2-eth-gmac.o
obj-$(CONFIG_BFIN_MAC) += bfin_mac.o
obj-$(CONFIG_CALXEDA_XGMAC) += calxedaxgmac.o
obj-$(CONFIG_CS8900) += cs8900.o
diff --git a/drivers/net/bcm-sf2-eth-gmac.c b/drivers/net/bcm-sf2-eth-gmac.c
new file mode 100644
index 0000000..977feec
--- /dev/null
+++ b/drivers/net/bcm-sf2-eth-gmac.c
@@ -0,0 +1,971 @@
+/*
+ * Copyright 2014 Broadcom Corporation.
+ *
+ * SPDX-License-Identifier: GPL-2.0+
+ */
+
+#ifdef BCM_GMAC_DEBUG
+#ifndef DEBUG
+#define DEBUG
+#endif
+#endif
+
+#include <config.h>
+#include <common.h>
+#include <malloc.h>
+#include <net.h>
+#include <asm/io.h>
+#include <phy.h>
+
+#include "bcm-sf2-eth.h"
+#include "bcm-sf2-eth-gmac.h"
+
+#define SPINWAIT(exp, us) { \
+ uint countdown = (us) + 9; \
+ while ((exp) && (countdown >= 10)) {\
+ udelay(10); \
+ countdown -= 10; \
+ } \
+}
+
+static int gmac_disable_dma(struct eth_dma *dma, int dir);
+static int gmac_enable_dma(struct eth_dma *dma, int dir);
+
+/* DMA Descriptor */
+typedef struct {
+ /* misc control bits */
+ uint32_t ctrl1;
+ /* buffer count and address extension */
+ uint32_t ctrl2;
+ /* memory address of the date buffer, bits 31:0 */
+ uint32_t addrlow;
+ /* memory address of the date buffer, bits 63:32 */
+ uint32_t addrhigh;
+} dma64dd_t;
+
+uint32_t g_dmactrlflags;
+
+static uint32_t dma_ctrlflags(uint32_t mask, uint32_t flags)
+{
+ debug("%s enter\n", __func__);
+
+ g_dmactrlflags &= ~mask;
+ g_dmactrlflags |= flags;
+
+ /* If trying to enable parity, check if parity is actually supported */
+ if (g_dmactrlflags & DMA_CTRL_PEN) {
+ uint32_t control;
+
+ control = readl(GMAC0_DMA_TX_CTRL_ADDR);
+ writel(control | D64_XC_PD, GMAC0_DMA_TX_CTRL_ADDR);
+ if (readl(GMAC0_DMA_TX_CTRL_ADDR) & D64_XC_PD) {
+ /*
+ * We *can* disable it, therefore it is supported;
+ * restore control register
+ */
+ writel(control, GMAC0_DMA_TX_CTRL_ADDR);
+ } else {
+ /* Not supported, don't allow it to be enabled */
+ g_dmactrlflags &= ~DMA_CTRL_PEN;
+ }
+ }
+
+ return g_dmactrlflags;
+}
+
+static inline void reg32_clear_bits(uint32_t reg, uint32_t value)
+{
+ uint32_t v = readl(reg);
+ v &= ~(value);
+ writel(v, reg);
+}
+
+static inline void reg32_set_bits(uint32_t reg, uint32_t value)
+{
+ uint32_t v = readl(reg);
+ v |= value;
+ writel(v, reg);
+}
+
+#ifdef BCM_GMAC_DEBUG
+static void dma_tx_dump(struct eth_dma *dma)
+{
+ dma64dd_t *descp = NULL;
+ uint8_t *bufp;
+ int i;
+
+ printf("TX DMA Register:\n");
+ printf("control:0x%x; ptr:0x%x; addrl:0x%x; addrh:0x%x; stat0:0x%x, stat1:0x%x\n",
+ readl(GMAC0_DMA_TX_CTRL_ADDR),
+ readl(GMAC0_DMA_TX_PTR_ADDR),
+ readl(GMAC0_DMA_TX_ADDR_LOW_ADDR),
+ readl(GMAC0_DMA_TX_ADDR_HIGH_ADDR),
+ readl(GMAC0_DMA_TX_STATUS0_ADDR),
+ readl(GMAC0_DMA_TX_STATUS1_ADDR));
+
+ printf("TX Descriptors:\n");
+ for (i = 0; i < TX_BUF_NUM; i++) {
+ descp = (dma64dd_t *)(dma->tx_desc_aligned) + i;
+ printf("ctrl1:0x%08x; ctrl2:0x%08x; addr:0x%x 0x%08x\n",
+ descp->ctrl1, descp->ctrl2,
+ descp->addrhigh, descp->addrlow);
+ }
+
+ printf("TX Buffers:\n");
+ /* Initialize TX DMA descriptor table */
+ for (i = 0; i < TX_BUF_NUM; i++) {
+ bufp = (uint8_t *)(dma->tx_buf + i * TX_BUF_SIZE);
+ printf("buf%d:0x%x; ", i, (uint32_t)bufp);
+ }
+ printf("\n");
+}
+
+static void dma_rx_dump(struct eth_dma *dma)
+{
+ dma64dd_t *descp = NULL;
+ uint8_t *bufp;
+ int i;
+
+ printf("RX DMA Register:\n");
+ printf("control:0x%x; ptr:0x%x; addrl:0x%x; addrh:0x%x; stat0:0x%x, stat1:0x%x\n",
+ readl(GMAC0_DMA_RX_CTRL_ADDR),
+ readl(GMAC0_DMA_RX_PTR_ADDR),
+ readl(GMAC0_DMA_RX_ADDR_LOW_ADDR),
+ readl(GMAC0_DMA_RX_ADDR_HIGH_ADDR),
+ readl(GMAC0_DMA_RX_STATUS0_ADDR),
+ readl(GMAC0_DMA_RX_STATUS1_ADDR));
+
+ printf("RX Descriptors:\n");
+ for (i = 0; i < RX_BUF_NUM; i++) {
+ descp = (dma64dd_t *)(dma->rx_desc_aligned) + i;
+ printf("ctrl1:0x%08x; ctrl2:0x%08x; addr:0x%x 0x%08x\n",
+ descp->ctrl1, descp->ctrl2,
+ descp->addrhigh, descp->addrlow);
+ }
+
+ printf("RX Buffers:\n");
+ for (i = 0; i < RX_BUF_NUM; i++) {
+ bufp = dma->rx_buf + i * RX_BUF_SIZE;
+ printf("buf%d:0x%x; ", i, (uint32_t)bufp);
+ }
+ printf("\n");
+}
+#endif
+
+static int dma_tx_init(struct eth_dma *dma)
+{
+ dma64dd_t *descp = NULL;
+ uint8_t *bufp;
+ int i;
+ uint32_t ctrl;
+
+ debug("%s enter\n", __func__);
+
+ /* clear descriptor memory */
+ memset((void *)(dma->tx_desc_aligned), 0,
+ TX_BUF_NUM * sizeof(dma64dd_t));
+ memset(dma->tx_buf, 0, TX_BUF_NUM * TX_BUF_SIZE);
+
+ /* Initialize TX DMA descriptor table */
+ for (i = 0; i < TX_BUF_NUM; i++) {
+ descp = (dma64dd_t *)(dma->tx_desc_aligned) + i;
+ bufp = dma->tx_buf + i * TX_BUF_SIZE;
+ /* clear buffer memory */
+ memset((void *)bufp, 0, TX_BUF_SIZE);
+
+ ctrl = 0;
+ /* if last descr set endOfTable */
+ if (i == (TX_BUF_NUM-1))
+ ctrl = D64_CTRL1_EOT;
+ descp->ctrl1 = ctrl;
+ descp->ctrl2 = 0;
+ descp->addrlow = (uint32_t)bufp;
+ descp->addrhigh = 0;
+ }
+
+ /* flush descriptor and buffer */
+ descp = dma->tx_desc_aligned;
+ bufp = dma->tx_buf;
+ flush_dcache_range((unsigned long)descp,
+ (unsigned long)(descp +
+ sizeof(dma64dd_t) * TX_BUF_NUM));
+ flush_dcache_range((unsigned long)(bufp),
+ (unsigned long)(bufp + TX_BUF_SIZE * TX_BUF_NUM));
+
+ /* initialize the DMA channel */
+ writel((uint32_t)(dma->tx_desc_aligned), GMAC0_DMA_TX_ADDR_LOW_ADDR);
+ writel(0, GMAC0_DMA_TX_ADDR_HIGH_ADDR);
+
+ /* now update the dma last descriptor */
+ writel(((uint32_t)(dma->tx_desc_aligned)) & D64_XP_LD_MASK,
+ GMAC0_DMA_TX_PTR_ADDR);
+
+ return 0;
+}
+
+static int dma_rx_init(struct eth_dma *dma)
+{
+ uint32_t last_desc;
+ dma64dd_t *descp = NULL;
+ uint8_t *bufp;
+ uint32_t ctrl;
+ int i;
+
+ debug("%s enter\n", __func__);
+
+ /* clear descriptor memory */
+ memset((void *)(dma->rx_desc_aligned), 0,
+ RX_BUF_NUM * sizeof(dma64dd_t));
+ /* clear buffer memory */
+ memset(dma->rx_buf, 0, RX_BUF_NUM * RX_BUF_SIZE);
+
+ /* Initialize RX DMA descriptor table */
+ for (i = 0; i < RX_BUF_NUM; i++) {
+ descp = (dma64dd_t *)(dma->rx_desc_aligned) + i;
+ bufp = dma->rx_buf + i * RX_BUF_SIZE;
+ ctrl = 0;
+ /* if last descr set endOfTable */
+ if (i == (RX_BUF_NUM - 1))
+ ctrl = D64_CTRL1_EOT;
+ descp->ctrl1 = ctrl;
+ descp->ctrl2 = RX_BUF_SIZE;
+ descp->addrlow = (uint32_t)bufp;
+ descp->addrhigh = 0;
+
+ last_desc = ((uint32_t)(descp) & D64_XP_LD_MASK)
+ + sizeof(dma64dd_t);
+ }
+
+ descp = dma->rx_desc_aligned;
+ bufp = dma->rx_buf;
+ /* flush descriptor and buffer */
+ flush_dcache_range((unsigned long)descp,
+ (unsigned long)(descp +
+ sizeof(dma64dd_t) * RX_BUF_NUM));
+ flush_dcache_range((unsigned long)(bufp),
+ (unsigned long)(bufp + RX_BUF_SIZE * RX_BUF_NUM));
+
+ /* initailize the DMA channel */
+ writel((uint32_t)descp, GMAC0_DMA_RX_ADDR_LOW_ADDR);
+ writel(0, GMAC0_DMA_RX_ADDR_HIGH_ADDR);
+
+ /* now update the dma last descriptor */
+ writel(last_desc, GMAC0_DMA_RX_PTR_ADDR);
+
+ return 0;
+}
+
+static int dma_init(struct eth_dma *dma)
+{
+ debug(" %s enter\n", __func__);
+
+ /*
+ * Default flags: For backwards compatibility both
+ * Rx Overflow Continue and Parity are DISABLED.
+ */
+ dma_ctrlflags(DMA_CTRL_ROC | DMA_CTRL_PEN, 0);
+
+ debug("rx burst len 0x%x\n",
+ (readl(GMAC0_DMA_RX_CTRL_ADDR) & D64_RC_BL_MASK)
+ >> D64_RC_BL_SHIFT);
+ debug("tx burst len 0x%x\n",
+ (readl(GMAC0_DMA_TX_CTRL_ADDR) & D64_XC_BL_MASK)
+ >> D64_XC_BL_SHIFT);
+
+ dma_tx_init(dma);
+ dma_rx_init(dma);
+
+ /* From end of chip_init() */
+ /* enable the overflow continue feature and disable parity */
+ dma_ctrlflags(DMA_CTRL_ROC | DMA_CTRL_PEN /* mask */,
+ DMA_CTRL_ROC /* value */);
+
+ return 0;
+}
+
+static int dma_deinit(struct eth_dma *dma)
+{
+ debug(" %s enter\n", __func__);
+
+ gmac_disable_dma(dma, MAC_DMA_RX);
+ gmac_disable_dma(dma, MAC_DMA_TX);
+
+ free(dma->tx_buf);
+ dma->tx_buf = NULL;
+ free(dma->tx_desc);
+ dma->tx_desc = NULL;
+ dma->tx_desc_aligned = NULL;
+
+ free(dma->rx_buf);
+ dma->rx_buf = NULL;
+ free(dma->rx_desc);
+ dma->rx_desc = NULL;
+ dma->rx_desc_aligned = NULL;
+
+ return 0;
+}
+
+int gmac_tx_packet(struct eth_dma *dma, void *packet, int length)
+{
+ uint8_t *bufp = dma->tx_buf + dma->cur_tx_index * TX_BUF_SIZE;
+
+ /* kick off the dma */
+ size_t len = length;
+ int txout = dma->cur_tx_index;
+ uint32_t flags;
+ dma64dd_t *descp = NULL;
+ uint32_t ctrl;
+ uint32_t last_desc = (((uint32_t)dma->tx_desc_aligned) +
+ sizeof(dma64dd_t)) & D64_XP_LD_MASK;
+ size_t buflen;
+
+ debug("%s enter\n", __func__);
+
+ /* load the buffer */
+ memcpy(bufp, packet, len);
+
+ /* Add 4 bytes for Ethernet FCS/CRC */
+ buflen = len + 4;
+
+ ctrl = (buflen & D64_CTRL2_BC_MASK);
+
+ /* the transmit will only be one frame or set SOF, EOF */
+ /* also set int on completion */
+ flags = D64_CTRL1_SOF | D64_CTRL1_IOC | D64_CTRL1_EOF;
+
+ /* txout points to the descriptor to uset */
+ /* if last descriptor then set EOT */
+ if (txout == (TX_BUF_NUM - 1)) {
+ flags |= D64_CTRL1_EOT;
+ last_desc = ((uint32_t)(dma->tx_desc_aligned)) & D64_XP_LD_MASK;
+ }
+
+ /* write the descriptor */
+ descp = ((dma64dd_t *)(dma->tx_desc_aligned)) + txout;
+ descp->addrlow = (uint32_t)bufp;
+ descp->addrhigh = 0;
+ descp->ctrl1 = flags;
+ descp->ctrl2 = ctrl;
+
+ /* flush descriptor and buffer */
+ flush_dcache_range((unsigned long)descp,
+ (unsigned long)(descp + sizeof(dma64dd_t)));
+ flush_dcache_range((unsigned long)bufp,
+ (unsigned long)(bufp + TX_BUF_SIZE));
+
+ /* now update the dma last descriptor */
+ writel(last_desc, GMAC0_DMA_TX_PTR_ADDR);
+
+ /* tx dma should be enabled so packet should go out */
+
+ /* update txout */
+ dma->cur_tx_index = (txout + 1) & (TX_BUF_NUM - 1);
+
+ return 0;
+}
+
+bool gmac_check_tx_done(struct eth_dma *dma)
+{
+ /* wait for tx to complete */
+ uint32_t intstatus;
+ bool xfrdone = false;
+
+ debug("%s enter\n", __func__);
+
+ intstatus = readl(GMAC0_INT_STATUS_ADDR);
+
+ debug("int(0x%x)\n", intstatus);
+ if (intstatus & (I_XI0 | I_XI1 | I_XI2 | I_XI3)) {
+ xfrdone = true;
+ /* clear the int bits */
+ intstatus &= ~(I_XI0 | I_XI1 | I_XI2 | I_XI3);
+ writel(intstatus, GMAC0_INT_STATUS_ADDR);
+ } else {
+ debug("Tx int(0x%x)\n", intstatus);
+ }
+
+ return xfrdone;
+}
+
+int gmac_check_rx_done(struct eth_dma *dma, uint8_t *buf)
+{
+ void *bufp, *datap;
+ size_t rcvlen = 0, buflen = 0;
+ uint32_t stat0 = 0, stat1 = 0;
+ uint32_t control, offset;
+ uint8_t statbuf[HWRXOFF*2];
+
+ int index, curr, active;
+ dma64dd_t *descp = NULL;
+
+ /* udelay(50); */
+
+ /*
+ * this api will check if a packet has been received.
+ * If so it will return the address of the buffer and current
+ * descriptor index will be incremented to the
+ * next descriptor. Once done with the frame the buffer should be
+ * added back onto the descriptor and the lastdscr should be updated
+ * to this descriptor.
+ */
+ index = dma->cur_rx_index;
+ offset = (uint32_t)(dma->rx_desc_aligned);
+ stat0 = readl(GMAC0_DMA_RX_STATUS0_ADDR) & D64_RS0_CD_MASK;
+ stat1 = readl(GMAC0_DMA_RX_STATUS1_ADDR) & D64_RS0_CD_MASK;
+ curr = ((stat0 - offset) & D64_RS0_CD_MASK) / sizeof(dma64dd_t);
+ active = ((stat1 - offset) & D64_RS0_CD_MASK) / sizeof(dma64dd_t);
+
+ /* check if any frame */
+ if (index == curr)
+ return -1;
+
+ debug("received packet\n");
+ debug("expect(0x%x) curr(0x%x) active(0x%x)\n", index, curr, active);
+ /* remove warning */
+ if (index == active)
+ ;
+
+ /* get the packet pointer that corresponds to the rx descriptor */
+ bufp = dma->rx_buf + index * RX_BUF_SIZE;
+
+ descp = (dma64dd_t *)(dma->rx_desc_aligned) + index;
+ /* flush descriptor and buffer */
+ flush_dcache_range((unsigned long)descp,
+ (unsigned long)(descp + sizeof(dma64dd_t)));
+ flush_dcache_range((unsigned long)bufp,
+ (unsigned long)(bufp + RX_BUF_SIZE));
+
+ buflen = (descp->ctrl2 & D64_CTRL2_BC_MASK);
+
+ stat0 = readl(GMAC0_DMA_RX_STATUS0_ADDR);
+ stat1 = readl(GMAC0_DMA_RX_STATUS1_ADDR);
+
+ debug("bufp(0x%x) index(0x%x) buflen(0x%x) stat0(0x%x) stat1(0x%x)\n",
+ (uint32_t)bufp, index, buflen, stat0, stat1);
+
+ dma->cur_rx_index = (index + 1) & (RX_BUF_NUM - 1);
+
+ /* get buffer offset */
+ control = readl(GMAC0_DMA_RX_CTRL_ADDR);
+ offset = (control & D64_RC_RO_MASK) >> D64_RC_RO_SHIFT;
+ rcvlen = *(uint16_t *)bufp;
+
+ debug("Received %d bytes\n", rcvlen);
+ /* copy status into temp buf then copy data from rx buffer */
+ memcpy(statbuf, bufp, offset);
+ datap = (void *)((uint32_t)bufp + offset);
+ memcpy(buf, datap, rcvlen);
+
+ /* update descriptor that is being added back on ring */
+ descp->ctrl2 = RX_BUF_SIZE;
+ descp->addrlow = (uint32_t)bufp;
+ descp->addrhigh = 0;
+ /* flush descriptor */
+ flush_dcache_range((unsigned long)descp,
+ (unsigned long)(descp + sizeof(dma64dd_t)));
+
+ /* set the lastdscr for the rx ring */
+ writel(((uint32_t)descp) & D64_XP_LD_MASK, GMAC0_DMA_RX_PTR_ADDR);
+
+ return (int)rcvlen;
+}
+
+static int gmac_disable_dma(struct eth_dma *dma, int dir)
+{
+ int status;
+
+ debug("%s enter\n", __func__);
+
+ if (dir == MAC_DMA_TX) {
+ /* address PR8249/PR7577 issue */
+ /* suspend tx DMA first */
+ writel(D64_XC_SE, GMAC0_DMA_TX_CTRL_ADDR);
+ SPINWAIT(((status = (readl(GMAC0_DMA_TX_STATUS0_ADDR) &
+ D64_XS0_XS_MASK)) !=
+ D64_XS0_XS_DISABLED) &&
+ (status != D64_XS0_XS_IDLE) &&
+ (status != D64_XS0_XS_STOPPED), 10000);
+
+ /*
+ * PR2414 WAR: DMA engines are not disabled until
+ * transfer finishes
+ */
+ writel(0, GMAC0_DMA_TX_CTRL_ADDR);
+ SPINWAIT(((status = (readl(GMAC0_DMA_TX_STATUS0_ADDR) &
+ D64_XS0_XS_MASK)) !=
+ D64_XS0_XS_DISABLED), 10000);
+
+ /* wait for the last transaction to complete */
+ udelay(2);
+
+ status = (status == D64_XS0_XS_DISABLED);
+ } else {
+ /*
+ * PR2414 WAR: DMA engines are not disabled until
+ * transfer finishes
+ */
+ writel(0, GMAC0_DMA_RX_CTRL_ADDR);
+ SPINWAIT(((status = (readl(GMAC0_DMA_RX_STATUS0_ADDR) &
+ D64_RS0_RS_MASK)) !=
+ D64_RS0_RS_DISABLED), 10000);
+
+ status = (status == D64_RS0_RS_DISABLED);
+ }
+
+ return status;
+}
+
+static int gmac_enable_dma(struct eth_dma *dma, int dir)
+{
+ uint32_t control;
+
+ debug("%s enter\n", __func__);
+
+ if (dir == MAC_DMA_TX) {
+ dma->cur_tx_index = 0;
+
+ /*
+ * These bits 20:18 (burstLen) of control register can be
+ * written but will take effect only if these bits are
+ * valid. So this will not affect previous versions
+ * of the DMA. They will continue to have those bits set to 0.
+ */
+ control = readl(GMAC0_DMA_TX_CTRL_ADDR);
+
+ control |= D64_XC_XE;
+ if ((g_dmactrlflags & DMA_CTRL_PEN) == 0)
+ control |= D64_XC_PD;
+
+ writel(control, GMAC0_DMA_TX_CTRL_ADDR);
+
+ /* initailize the DMA channel */
+ writel((uint32_t)(dma->tx_desc_aligned),
+ GMAC0_DMA_TX_ADDR_LOW_ADDR);
+ writel(0, GMAC0_DMA_TX_ADDR_HIGH_ADDR);
+ } else {
+ dma->cur_rx_index = 0;
+
+ control = (readl(GMAC0_DMA_RX_CTRL_ADDR) &
+ D64_RC_AE) | D64_RC_RE;
+
+ if ((g_dmactrlflags & DMA_CTRL_PEN) == 0)
+ control |= D64_RC_PD;
+
+ if (g_dmactrlflags & DMA_CTRL_ROC)
+ control |= D64_RC_OC;
+
+ /*
+ * These bits 20:18 (burstLen) of control register can be
+ * written but will take effect only if these bits are
+ * valid. So this will not affect previous versions
+ * of the DMA. They will continue to have those bits set to 0.
+ */
+ control &= ~D64_RC_BL_MASK;
+ /* Keep default Rx burstlen */
+ control |= readl(GMAC0_DMA_RX_CTRL_ADDR) & D64_RC_BL_MASK;
+ control |= HWRXOFF << D64_RC_RO_SHIFT;
+
+ writel(control, GMAC0_DMA_RX_CTRL_ADDR);
+
+ /*
+ * the rx descriptor ring should have
+ * the addresses set properly;
+ * set the lastdscr for the rx ring
+ */
+ writel(((uint32_t)(dma->rx_desc_aligned) +
+ (RX_BUF_NUM - 1) * RX_BUF_SIZE) &
+ D64_XP_LD_MASK, GMAC0_DMA_RX_PTR_ADDR);
+ }
+
+ return 0;
+}
+
+bool gmac_mii_busywait(unsigned int timeout)
+{
+ uint32_t tmp = 0;
+
+ while (timeout > 10) {
+ tmp = readl(GMAC_MII_CTRL_ADDR);
+ if (tmp & (1 << GMAC_MII_BUSY_SHIFT)) {
+ udelay(10);
+ timeout -= 10;
+ } else {
+ break;
+ }
+ }
+ return tmp & (1 << GMAC_MII_BUSY_SHIFT);
+}
+
+int gmac_miiphy_read(const char *devname, unsigned char phyaddr,
+ unsigned char reg, unsigned short *value)
+{
+ uint32_t tmp = 0;
+
+ (void)devname;
+
+ /* Busy wait timeout is 1ms */
+ if (gmac_mii_busywait(1000)) {
+ error("%s: Prepare MII read: MII/MDIO busy\n", __func__);
+ return -1;
+ }
+
+ /* Read operation */
+ tmp = GMAC_MII_DATA_READ_CMD;
+ tmp |= (phyaddr << GMAC_MII_PHY_ADDR_SHIFT) |
+ (reg << GMAC_MII_PHY_REG_SHIFT);
+ debug("MII read cmd 0x%x, phy 0x%x, reg 0x%x\n", tmp, phyaddr, reg);
+ writel(tmp, GMAC_MII_DATA_ADDR);
+
+ if (gmac_mii_busywait(1000)) {
+ error("%s: MII read failure: MII/MDIO busy\n", __func__);
+ return -1;
+ }
+
+ *value = readl(GMAC_MII_DATA_ADDR) & 0xffff;
+ debug("MII read data 0x%x\n", *value);
+ return 0;
+}
+
+int gmac_miiphy_write(const char *devname, unsigned char phyaddr,
+ unsigned char reg, unsigned short value)
+{
+ uint32_t tmp = 0;
+
+ (void)devname;
+
+ /* Busy wait timeout is 1ms */
+ if (gmac_mii_busywait(1000)) {
+ error("%s: Prepare MII write: MII/MDIO busy\n", __func__);
+ return -1;
+ }
+
+ /* Write operation */
+ tmp = GMAC_MII_DATA_WRITE_CMD | (value & 0xffff);
+ tmp |= ((phyaddr << GMAC_MII_PHY_ADDR_SHIFT) |
+ (reg << GMAC_MII_PHY_REG_SHIFT));
+ debug("MII write cmd 0x%x, phy 0x%x, reg 0x%x, data 0x%x\n",
+ tmp, phyaddr, reg, value);
+ writel(tmp, GMAC_MII_DATA_ADDR);
+
+ if (gmac_mii_busywait(1000)) {
+ error("%s: MII write failure: MII/MDIO busy\n", __func__);
+ return -1;
+ }
+
+ return 0;
+}
+
+void gmac_init_reset(void)
+{
+ debug("%s enter\n", __func__);
+
+ /* set command config reg CC_SR */
+ reg32_set_bits(UNIMAC0_CMD_CFG_ADDR, CC_SR);
+ udelay(GMAC_RESET_DELAY);
+}
+
+void gmac_clear_reset(void)
+{
+ debug("%s enter\n", __func__);
+
+ /* clear command config reg CC_SR */
+ reg32_clear_bits(UNIMAC0_CMD_CFG_ADDR, CC_SR);
+ udelay(GMAC_RESET_DELAY);
+}
+
+static void gmac_enable_local(bool en)
+{
+ uint32_t cmdcfg;
+
+ debug("%s enter\n", __func__);
+
+ /* read command config reg */
+ cmdcfg = readl(UNIMAC0_CMD_CFG_ADDR);
+
+ /* put mac in reset */
+ gmac_init_reset();
+
+ cmdcfg |= CC_SR;
+
+ /* first deassert rx_ena and tx_ena while in reset */
+ cmdcfg &= ~(CC_RE | CC_TE);
+ /* write command config reg */
+ writel(cmdcfg, UNIMAC0_CMD_CFG_ADDR);
+
+ /* bring mac out of reset */
+ gmac_clear_reset();
+
+ /* if not enable exit now */
+ if (!en)
+ return;
+
+ /* enable the mac transmit and receive paths now */
+ udelay(2);
+ cmdcfg &= ~CC_SR;
+ cmdcfg |= (CC_RE | CC_TE);
+
+ /* assert rx_ena and tx_ena when out of reset to enable the mac */
+ writel(cmdcfg, UNIMAC0_CMD_CFG_ADDR);
+
+ return;
+}
+
+int gmac_enable(void)
+{
+ gmac_enable_local(1);
+
+ /* clear interrupts */
+ writel(I_INTMASK, GMAC0_INT_STATUS_ADDR);
+ return 0;
+}
+
+int gmac_disable(void)
+{
+ gmac_enable_local(0);
+ return 0;
+}
+
+int gmac_set_speed(int speed, int duplex)
+{
+ uint32_t cmdcfg;
+ uint32_t hd_ena;
+ uint32_t speed_cfg;
+
+ hd_ena = duplex ? 0 : CC_HD;
+ if (speed == 1000) {
+ speed_cfg = 2;
+ } else if (speed == 100) {
+ speed_cfg = 1;
+ } else if (speed == 10) {
+ speed_cfg = 0;
+ } else {
+ error("%s: Invalid GMAC speed(%d)!\n", __func__, speed);
+ return -1;
+ }
+
+ cmdcfg = readl(UNIMAC0_CMD_CFG_ADDR);
+ cmdcfg &= ~(CC_ES_MASK | CC_HD);
+ cmdcfg |= ((speed_cfg << CC_ES_SHIFT) | hd_ena);
+
+ printf("Change GMAC speed to %dMB\n", speed);
+ debug("GMAC speed cfg 0x%x\n", cmdcfg);
+ writel(cmdcfg, UNIMAC0_CMD_CFG_ADDR);
+
+ return 0;
+}
+
+int gmac_set_mac_addr(unsigned char *mac)
+{
+ /* set our local address */
+ debug("GMAC: %02x:%02x:%02x:%02x:%02x:%02x\n",
+ mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
+ writel(htonl(*(uint32_t *)mac), UNIMAC0_MAC_MSB_ADDR);
+ writew(htons(*(uint32_t *)&mac[4]), UNIMAC0_MAC_LSB_ADDR);
+
+ return 0;
+}
+
+int gmac_mac_init(struct eth_device *dev)
+{
+ struct eth_info *eth = (struct eth_info *)(dev->priv);
+ struct eth_dma *dma = &(eth->dma);
+
+ uint32_t tmp;
+ uint32_t cmdcfg;
+ int chipid;
+
+ debug("%s enter\n", __func__);
+
+ /* Always use GMAC0 */
+ printf("Using GMAC%d\n", 0);
+
+ /* Reset AMAC0 core */
+ writel(0, AMAC0_IDM_RESET_ADDR);
+ tmp = readl(AMAC0_IO_CTRL_DIRECT_ADDR);
+ /* Set clock */
+ tmp &= ~(1 << AMAC0_IO_CTRL_CLK_250_SEL_SHIFT);
+ tmp |= (1 << AMAC0_IO_CTRL_GMII_MODE_SHIFT);
+ /* Set Tx clock */
+ tmp &= ~(1 << AMAC0_IO_CTRL_DEST_SYNC_MODE_EN_SHIFT);
+ writel(tmp, AMAC0_IO_CTRL_DIRECT_ADDR);
+
+ /* reset gmac */
+ /*
+ * As AMAC is just reset, NO need?
+ * set eth_data into loopback mode to ensure no rx traffic
+ * gmac_loopback(eth_data, TRUE);
+ * ET_TRACE(("%s gmac loopback\n", __func__));
+ * udelay(1);
+ */
+
+ cmdcfg = readl(UNIMAC0_CMD_CFG_ADDR);
+ cmdcfg &= ~(CC_TE | CC_RE | CC_RPI | CC_TAI | CC_HD | CC_ML |
+ CC_CFE | CC_RL | CC_RED | CC_PE | CC_TPI |
+ CC_PAD_EN | CC_PF);
+ cmdcfg |= (CC_PROM | CC_NLC | CC_CFE);
+ /* put mac in reset */
+ gmac_init_reset();
+ writel(cmdcfg, UNIMAC0_CMD_CFG_ADDR);
+ gmac_clear_reset();
+
+ /* enable clear MIB on read */
+ reg32_set_bits(GMAC0_DEV_CTRL_ADDR, DC_MROR);
+ /* PHY: set smi_master to drive mdc_clk */
+ reg32_set_bits(GMAC0_PHY_CTRL_ADDR, PC_MTE);
+
+ /* clear persistent sw intstatus */
+ writel(0, GMAC0_INT_STATUS_ADDR);
+
+ if (dma_init(dma) < 0) {
+ error("%s: GMAC dma_init failed\n", __func__);
+ goto err_exit;
+ }
+
+ chipid = CHIPID;
+ printf("%s: Chip ID: 0x%x\n", __func__, chipid);
+
+ /* set switch bypass mode */
+ tmp = readl(SWITCH_GLOBAL_CONFIG_ADDR);
+ tmp |= (1 << CDRU_SWITCH_BYPASS_SWITCH_SHIFT);
+
+ /* Switch mode */
+ /* tmp &= ~(1 << CDRU_SWITCH_BYPASS_SWITCH_SHIFT); */
+
+ writel(tmp, SWITCH_GLOBAL_CONFIG_ADDR);
+
+ tmp = readl(CRMU_CHIP_IO_PAD_CONTROL_ADDR);
+ tmp &= ~(1 << CDRU_IOMUX_FORCE_PAD_IN_SHIFT);
+ writel(tmp, CRMU_CHIP_IO_PAD_CONTROL_ADDR);
+
+ /* Set MDIO to internal GPHY */
+ tmp = readl(GMAC_MII_CTRL_ADDR);
+ /* Select internal MDC/MDIO bus*/
+ tmp &= ~(1 << GMAC_MII_CTRL_BYP_SHIFT);
+ /* select MDC/MDIO connecting to on-chip internal PHYs */
+ tmp &= ~(1 << GMAC_MII_CTRL_EXT_SHIFT);
+ /*
+ * give bit[6:0](MDCDIV) with required divisor to set
+ * the MDC clock frequency, 66MHZ/0x1A=2.5MHZ
+ */
+ tmp |= 0x1A;
+
+ writel(tmp, GMAC_MII_CTRL_ADDR);
+
+ if (gmac_mii_busywait(1000)) {
+ error("%s: Configure MDIO: MII/MDIO busy\n", __func__);
+ goto err_exit;
+ }
+
+ /* Configure GMAC0 */
+ /* enable one rx interrupt per received frame */
+ writel(1 << GMAC0_IRL_FRAMECOUNT_SHIFT, GMAC0_INTR_RECV_LAZY_ADDR);
+
+ /* read command config reg */
+ cmdcfg = readl(UNIMAC0_CMD_CFG_ADDR);
+ /* enable 802.3x tx flow control (honor received PAUSE frames) */
+ cmdcfg &= ~CC_RPI;
+ /* enable promiscuous mode */
+ cmdcfg |= CC_PROM;
+ /* Disable loopback mode */
+ cmdcfg &= ~CC_ML;
+ /* set the speed */
+ cmdcfg &= ~(CC_ES_MASK | CC_HD);
+ /* Set to 1Gbps and full duplex by default */
+ cmdcfg |= (2 << CC_ES_SHIFT);
+
+ /* put mac in reset */
+ gmac_init_reset();
+ /* write register */
+ writel(cmdcfg, UNIMAC0_CMD_CFG_ADDR);
+ /* bring mac out of reset */
+ gmac_clear_reset();
+
+ /* set max frame lengths; account for possible vlan tag */
+ writel(PKTSIZE + 32, UNIMAC0_FRM_LENGTH_ADDR);
+
+ return 0;
+
+err_exit:
+ dma_deinit(dma);
+ return -1;
+}
+
+int gmac_add(struct eth_device *dev)
+{
+ struct eth_info *eth = (struct eth_info *)(dev->priv);
+ struct eth_dma *dma = &(eth->dma);
+ void *tmp;
+
+ /*
+ * Desc has to be 16-byte aligned ?
+ * If it is 8-byte aligned by malloc, fail Tx
+ */
+ tmp = malloc(sizeof(dma64dd_t) * TX_BUF_NUM + 8);
+ if (tmp == NULL) {
+ printf("%s: Failed to allocate TX desc Buffer\n", __func__);
+ return -1;
+ }
+
+ dma->tx_desc = (void *)tmp;
+ dma->tx_desc_aligned = (void *)(((uint32_t)tmp) & (~0xf));
+ debug("TX Descriptor Buffer: %p; length: 0x%x\n",
+ dma->tx_desc_aligned, sizeof(dma64dd_t) * TX_BUF_NUM);
+
+ tmp = malloc(TX_BUF_SIZE * TX_BUF_NUM);
+ if (tmp == NULL) {
+ printf("%s: Failed to allocate TX Data Buffer\n", __func__);
+ free(dma->tx_desc);
+ return -1;
+ }
+ dma->tx_buf = (uint8_t *)tmp;
+ debug("TX Data Buffer: %p; length: 0x%x\n",
+ dma->tx_buf, TX_BUF_SIZE * TX_BUF_NUM);
+
+ /* Desc has to be 16-byte aligned ? */
+ tmp = malloc(sizeof(dma64dd_t) * RX_BUF_NUM + 8);
+ if (tmp == NULL) {
+ printf("%s: Failed to allocate RX Descriptor\n", __func__);
+ free(dma->tx_desc);
+ free(dma->tx_buf);
+ return -1;
+ }
+ dma->rx_desc = tmp;
+ dma->rx_desc_aligned = (void *)(((uint32_t)tmp) & (~0xf));
+ debug("RX Descriptor Buffer: %p, length: 0x%x\n",
+ dma->rx_desc_aligned, sizeof(dma64dd_t) * RX_BUF_NUM);
+
+ tmp = malloc(RX_BUF_SIZE * RX_BUF_NUM);
+ if (tmp == NULL) {
+ printf("%s: Failed to allocate RX Data Buffer\n", __func__);
+ free(dma->tx_desc);
+ free(dma->tx_buf);
+ free(dma->rx_desc);
+ return -1;
+ }
+ dma->rx_buf = tmp;
+ debug("RX Data Buffer: %p; length: 0x%x\n",
+ dma->rx_buf, RX_BUF_SIZE * RX_BUF_NUM);
+
+ g_dmactrlflags = 0;
+
+ eth->phy_interface = PHY_INTERFACE_MODE_GMII;
+
+ dma->tx_packet = gmac_tx_packet;
+ dma->check_tx_done = gmac_check_tx_done;
+
+ dma->check_rx_done = gmac_check_rx_done;
+
+ dma->enable_dma = gmac_enable_dma;
+ dma->disable_dma = gmac_disable_dma;
+
+ eth->miiphy_read = gmac_miiphy_read;
+ eth->miiphy_write = gmac_miiphy_write;
+
+ eth->mac_init = gmac_mac_init;
+ eth->disable_mac = gmac_disable;
+ eth->enable_mac = gmac_enable;
+ eth->set_mac_addr = gmac_set_mac_addr;
+ eth->set_mac_speed = gmac_set_speed;
+
+ return 0;
+}
diff --git a/drivers/net/bcm-sf2-eth-gmac.h b/drivers/net/bcm-sf2-eth-gmac.h
new file mode 100644
index 0000000..810a61726
--- /dev/null
+++ b/drivers/net/bcm-sf2-eth-gmac.h
@@ -0,0 +1,224 @@
+/*
+ * Copyright 2014 Broadcom Corporation.
+ *
+ * SPDX-License-Identifier: GPL-2.0+
+ */
+
+#ifndef _BCM_SF2_ETH_GMAC_H_
+#define _BCM_SF2_ETH_GMAC_H_
+
+#define BCM_SF2_ETH_MAC_NAME "gmac"
+
+#ifndef ETHHW_PORT_INT
+#define ETHHW_PORT_INT 8
+#endif
+
+#define GMAC0_REG_BASE 0x18042000
+#define GMAC0_DEV_CTRL_ADDR GMAC0_REG_BASE
+#define GMAC0_INT_STATUS_ADDR (GMAC0_REG_BASE + 0x020)
+#define GMAC0_INTR_RECV_LAZY_ADDR (GMAC0_REG_BASE + 0x100)
+#define GMAC0_PHY_CTRL_ADDR (GMAC0_REG_BASE + 0x188)
+
+
+#define GMAC_DMA_PTR_OFFSET 0x04
+#define GMAC_DMA_ADDR_LOW_OFFSET 0x08
+#define GMAC_DMA_ADDR_HIGH_OFFSET 0x0c
+#define GMAC_DMA_STATUS0_OFFSET 0x10
+#define GMAC_DMA_STATUS1_OFFSET 0x14
+
+#define GMAC0_DMA_TX_CTRL_ADDR (GMAC0_REG_BASE + 0x200)
+#define GMAC0_DMA_TX_PTR_ADDR \
+ (GMAC0_DMA_TX_CTRL_ADDR + GMAC_DMA_PTR_OFFSET)
+#define GMAC0_DMA_TX_ADDR_LOW_ADDR \
+ (GMAC0_DMA_TX_CTRL_ADDR + GMAC_DMA_ADDR_LOW_OFFSET)
+#define GMAC0_DMA_TX_ADDR_HIGH_ADDR \
+ (GMAC0_DMA_TX_CTRL_ADDR + GMAC_DMA_ADDR_HIGH_OFFSET)
+#define GMAC0_DMA_TX_STATUS0_ADDR \
+ (GMAC0_DMA_TX_CTRL_ADDR + GMAC_DMA_STATUS0_OFFSET)
+#define GMAC0_DMA_TX_STATUS1_ADDR \
+ (GMAC0_DMA_TX_CTRL_ADDR + GMAC_DMA_STATUS1_OFFSET)
+
+#define GMAC0_DMA_RX_CTRL_ADDR (GMAC0_REG_BASE + 0x220)
+#define GMAC0_DMA_RX_PTR_ADDR \
+ (GMAC0_DMA_RX_CTRL_ADDR + GMAC_DMA_PTR_OFFSET)
+#define GMAC0_DMA_RX_ADDR_LOW_ADDR \
+ (GMAC0_DMA_RX_CTRL_ADDR + GMAC_DMA_ADDR_LOW_OFFSET)
+#define GMAC0_DMA_RX_ADDR_HIGH_ADDR \
+ (GMAC0_DMA_RX_CTRL_ADDR + GMAC_DMA_ADDR_HIGH_OFFSET)
+#define GMAC0_DMA_RX_STATUS0_ADDR \
+ (GMAC0_DMA_RX_CTRL_ADDR + GMAC_DMA_STATUS0_OFFSET)
+#define GMAC0_DMA_RX_STATUS1_ADDR \
+ (GMAC0_DMA_RX_CTRL_ADDR + GMAC_DMA_STATUS1_OFFSET)
+
+#define UNIMAC0_CMD_CFG_ADDR (GMAC0_REG_BASE + 0x808)
+#define UNIMAC0_MAC_MSB_ADDR (GMAC0_REG_BASE + 0x80c)
+#define UNIMAC0_MAC_LSB_ADDR (GMAC0_REG_BASE + 0x810)
+#define UNIMAC0_FRM_LENGTH_ADDR (GMAC0_REG_BASE + 0x814)
+
+#define GMAC0_IRL_FRAMECOUNT_SHIFT 24
+
+/* transmit channel control */
+/* transmit enable */
+#define D64_XC_XE 0x00000001
+/* transmit suspend request */
+#define D64_XC_SE 0x00000002
+/* parity check disable */
+#define D64_XC_PD 0x00000800
+/* BurstLen bits */
+#define D64_XC_BL_MASK 0x001C0000
+#define D64_XC_BL_SHIFT 18
+
+/* transmit descriptor table pointer */
+/* last valid descriptor */
+#define D64_XP_LD_MASK 0x00001fff
+
+/* transmit channel status */
+/* transmit state */
+#define D64_XS0_XS_MASK 0xf0000000
+#define D64_XS0_XS_SHIFT 28
+#define D64_XS0_XS_DISABLED 0x00000000
+#define D64_XS0_XS_ACTIVE 0x10000000
+#define D64_XS0_XS_IDLE 0x20000000
+#define D64_XS0_XS_STOPPED 0x30000000
+#define D64_XS0_XS_SUSP 0x40000000
+
+/* receive channel control */
+/* receive enable */
+#define D64_RC_RE 0x00000001
+/* address extension bits */
+#define D64_RC_AE 0x00030000
+/* overflow continue */
+#define D64_RC_OC 0x00000400
+/* parity check disable */
+#define D64_RC_PD 0x00000800
+/* receive frame offset */
+#define D64_RC_RO_MASK 0x000000fe
+#define D64_RC_RO_SHIFT 1
+/* BurstLen bits */
+#define D64_RC_BL_MASK 0x001C0000
+#define D64_RC_BL_SHIFT 18
+
+/* flags for dma controller */
+/* partity enable */
+#define DMA_CTRL_PEN (1 << 0)
+/* rx overflow continue */
+#define DMA_CTRL_ROC (1 << 1)
+
+/* receive descriptor table pointer */
+/* last valid descriptor */
+#define D64_RP_LD_MASK 0x00001fff
+
+/* receive channel status */
+/* current descriptor pointer */
+#define D64_RS0_CD_MASK 0x00001fff
+/* receive state */
+#define D64_RS0_RS_MASK 0xf0000000
+#define D64_RS0_RS_SHIFT 28
+#define D64_RS0_RS_DISABLED 0x00000000
+#define D64_RS0_RS_ACTIVE 0x10000000
+#define D64_RS0_RS_IDLE 0x20000000
+#define D64_RS0_RS_STOPPED 0x30000000
+#define D64_RS0_RS_SUSP 0x40000000
+
+/* descriptor control flags 1 */
+/* core specific flags */
+#define D64_CTRL_COREFLAGS 0x0ff00000
+/* end of descriptor table */
+#define D64_CTRL1_EOT ((uint32_t)1 << 28)
+/* interrupt on completion */
+#define D64_CTRL1_IOC ((uint32_t)1 << 29)
+/* end of frame */
+#define D64_CTRL1_EOF ((uint32_t)1 << 30)
+/* start of frame */
+#define D64_CTRL1_SOF ((uint32_t)1 << 31)
+
+/* descriptor control flags 2 */
+/* buffer byte count. real data len must <= 16KB */
+#define D64_CTRL2_BC_MASK 0x00007fff
+/* address extension bits */
+#define D64_CTRL2_AE 0x00030000
+#define D64_CTRL2_AE_SHIFT 16
+/* parity bit */
+#define D64_CTRL2_PARITY 0x00040000
+/* control flags in the range [27:20] are core-specific and not defined here */
+#define D64_CTRL_CORE_MASK 0x0ff00000
+
+#define DC_MROR 0x00000010
+#define PC_MTE 0x00800000
+
+/* command config */
+#define CC_TE 0x00000001
+#define CC_RE 0x00000002
+#define CC_ES_MASK 0x0000000c
+#define CC_ES_SHIFT 2
+#define CC_PROM 0x00000010
+#define CC_PAD_EN 0x00000020
+#define CC_CF 0x00000040
+#define CC_PF 0x00000080
+#define CC_RPI 0x00000100
+#define CC_TAI 0x00000200
+#define CC_HD 0x00000400
+#define CC_HD_SHIFT 10
+#define CC_SR 0x00002000
+#define CC_ML 0x00008000
+#define CC_AE 0x00400000
+#define CC_CFE 0x00800000
+#define CC_NLC 0x01000000
+#define CC_RL 0x02000000
+#define CC_RED 0x04000000
+#define CC_PE 0x08000000
+#define CC_TPI 0x10000000
+#define CC_AT 0x20000000
+
+#define I_PDEE 0x00000400
+#define I_PDE 0x00000800
+#define I_DE 0x00001000
+#define I_RDU 0x00002000
+#define I_RFO 0x00004000
+#define I_XFU 0x00008000
+#define I_RI 0x00010000
+#define I_XI0 0x01000000
+#define I_XI1 0x02000000
+#define I_XI2 0x04000000
+#define I_XI3 0x08000000
+#define I_ERRORS (I_PDEE | I_PDE | I_DE | I_RDU | I_RFO | I_XFU)
+#define DEF_INTMASK (I_XI0 | I_XI1 | I_XI2 | I_XI3 | I_RI | I_ERRORS)
+
+#define I_INTMASK 0x0f01fcff
+
+#define CHIP_DRU_BASE 0x0301d000
+#define CRMU_CHIP_IO_PAD_CONTROL_ADDR (CHIP_DRU_BASE + 0x0bc)
+#define SWITCH_GLOBAL_CONFIG_ADDR (CHIP_DRU_BASE + 0x194)
+
+#define CDRU_IOMUX_FORCE_PAD_IN_SHIFT 0
+#define CDRU_SWITCH_BYPASS_SWITCH_SHIFT 13
+
+#define AMAC0_IDM_RESET_ADDR 0x18110800
+#define AMAC0_IO_CTRL_DIRECT_ADDR 0x18110408
+#define AMAC0_IO_CTRL_CLK_250_SEL_SHIFT 6
+#define AMAC0_IO_CTRL_GMII_MODE_SHIFT 5
+#define AMAC0_IO_CTRL_DEST_SYNC_MODE_EN_SHIFT 3
+
+#define CHIPA_CHIP_ID_ADDR 0x18000000
+#define CHIPID (readl(CHIPA_CHIP_ID_ADDR) & 0xFFFF)
+#define CHIPREV (((readl(CHIPA_CHIP_ID_ADDR) >> 16) & 0xF)
+#define CHIPSKU (((readl(CHIPA_CHIP_ID_ADDR) >> 20) & 0xF)
+
+#define GMAC_MII_CTRL_ADDR 0x18002000
+#define GMAC_MII_CTRL_BYP_SHIFT 10
+#define GMAC_MII_CTRL_EXT_SHIFT 9
+#define GMAC_MII_DATA_ADDR 0x18002004
+#define GMAC_MII_DATA_READ_CMD 0x60020000
+#define GMAC_MII_DATA_WRITE_CMD 0x50020000
+#define GMAC_MII_BUSY_SHIFT 8
+#define GMAC_MII_PHY_ADDR_SHIFT 23
+#define GMAC_MII_PHY_REG_SHIFT 18
+
+#define GMAC_RESET_DELAY 2
+#define HWRXOFF 30
+#define MAXNAMEL 8
+#define NUMTXQ 4
+
+int gmac_add(struct eth_device *dev);
+
+#endif /* _BCM_SF2_ETH_GMAC_H_ */
diff --git a/drivers/net/bcm-sf2-eth.c b/drivers/net/bcm-sf2-eth.c
new file mode 100644
index 0000000..5252d49
--- /dev/null
+++ b/drivers/net/bcm-sf2-eth.c
@@ -0,0 +1,268 @@
+/*
+ * Copyright 2014 Broadcom Corporation.
+ *
+ * SPDX-License-Identifier: GPL-2.0+
+ */
+
+#include <common.h>
+#include <malloc.h>
+#include <net.h>
+#include <config.h>
+
+#include <phy.h>
+#include <miiphy.h>
+
+#include <asm/io.h>
+
+#include <netdev.h>
+#include "bcm-sf2-eth.h"
+
+#if defined(CONFIG_BCM_SF2_ETH_GMAC)
+#include "bcm-sf2-eth-gmac.h"
+#else
+#error "bcm_sf2_eth: NEED to define a MAC!"
+#endif
+
+#define BCM_NET_MODULE_DESCRIPTION "Broadcom Starfighter2 Ethernet driver"
+#define BCM_NET_MODULE_VERSION "0.1"
+#define BCM_SF2_ETH_DEV_NAME "bcm_sf2"
+
+static const char banner[] =
+ BCM_NET_MODULE_DESCRIPTION " " BCM_NET_MODULE_VERSION "\n";
+
+static int bcm_sf2_eth_init(struct eth_device *dev)
+{
+ struct eth_info *eth = (struct eth_info *)(dev->priv);
+ struct eth_dma *dma = &(eth->dma);
+ struct phy_device *phydev;
+ int rc = 0;
+ int i;
+
+ rc = eth->mac_init(dev);
+ if (rc) {
+ error("%s: Couldn't cofigure MAC!\n", __func__);
+ return rc;
+ }
+
+ /* disable DMA */
+ dma->disable_dma(dma, MAC_DMA_RX);
+ dma->disable_dma(dma, MAC_DMA_TX);
+
+ eth->port_num = 0;
+ debug("Connecting PHY 0...\n");
+ phydev = phy_connect(miiphy_get_dev_by_name(dev->name),
+ 0, dev, eth->phy_interface);
+ if (phydev != NULL) {
+ eth->port[0] = phydev;
+ eth->port_num += 1;
+ } else {
+ debug("No PHY found for port 0\n");
+ }
+
+ for (i = 0; i < eth->port_num; i++)
+ phy_config(eth->port[i]);
+
+ return rc;
+}
+
+/*
+ * u-boot net functions
+ */
+
+static int bcm_sf2_eth_send(struct eth_device *dev, void *packet, int length)
+{
+ struct eth_dma *dma = &(((struct eth_info *)(dev->priv))->dma);
+ uint8_t *buf = (uint8_t *)packet;
+ int rc = 0;
+ int i = 0;
+
+ debug("%s enter\n", __func__);
+
+ /* load buf and start transmit */
+ rc = dma->tx_packet(dma, buf, length);
+ if (rc) {
+ debug("ERROR - Tx failed\n");
+ return rc;
+ }
+
+ while (!(dma->check_tx_done(dma))) {
+ udelay(100);
+ debug(".");
+ i++;
+ if (i > 20) {
+ error("%s: Tx timeout: retried 20 times\n", __func__);
+ rc = -1;
+ break;
+ }
+ }
+
+ debug("%s exit rc(0x%x)\n", __func__, rc);
+ return rc;
+}
+
+static int bcm_sf2_eth_receive(struct eth_device *dev)
+{
+ struct eth_dma *dma = &(((struct eth_info *)(dev->priv))->dma);
+ uint8_t *buf = (uint8_t *)NetRxPackets[0];
+ int rcvlen;
+ int rc = 0;
+ int i = 0;
+
+ while (1) {
+ /* Poll Rx queue to get a packet */
+ rcvlen = dma->check_rx_done(dma, buf);
+ if (rcvlen < 0) {
+ /* No packet received */
+ rc = -1;
+ debug("\nNO More Rx\n");
+ break;
+ } else if ((rcvlen == 0) || (rcvlen > RX_BUF_SIZE)) {
+ error("%s: Wrong Ethernet packet size (%d B), skip!\n",
+ __func__, rcvlen);
+ break;
+ } else {
+ debug("recieved\n");
+
+ /* Forward received packet to uboot network handler */
+ NetReceive(buf, rcvlen);
+
+ if (++i >= PKTBUFSRX)
+ i = 0;
+ buf = NetRxPackets[i];
+ }
+ }
+
+ return rc;
+}
+
+static int bcm_sf2_eth_write_hwaddr(struct eth_device *dev)
+{
+ struct eth_info *eth = (struct eth_info *)(dev->priv);
+
+ printf(" ETH MAC: %02x:%02x:%02x:%02x:%02x:%02x\n",
+ dev->enetaddr[0], dev->enetaddr[1], dev->enetaddr[2],
+ dev->enetaddr[3], dev->enetaddr[4], dev->enetaddr[5]);
+
+ return eth->set_mac_addr(dev->enetaddr);
+}
+
+static int bcm_sf2_eth_open(struct eth_device *dev, bd_t *bt)
+{
+ struct eth_info *eth = (struct eth_info *)(dev->priv);
+ struct eth_dma *dma = &(eth->dma);
+ int i;
+
+ debug("Enabling BCM SF2 Ethernet.\n");
+
+ /* Set MAC address from env */
+ if (bcm_sf2_eth_write_hwaddr(dev) != 0) {
+ error("%s: MAC set error when opening !\n", __func__);
+ return -1;
+ }
+
+ eth->enable_mac();
+
+ /* enable tx and rx DMA */
+ dma->enable_dma(dma, MAC_DMA_RX);
+ dma->enable_dma(dma, MAC_DMA_TX);
+
+ /*
+ * Need to start PHY here because link speed can change
+ * before each ethernet operation
+ */
+ for (i = 0; i < eth->port_num; i++) {
+ if (phy_startup(eth->port[i])) {
+ error("%s: PHY %d startup failed!\n", __func__, i);
+ if (i == CONFIG_BCM_SF2_ETH_DEFAULT_PORT) {
+ error("%s: No default port %d!\n", __func__, i);
+ return -1;
+ }
+ }
+ }
+
+ /* Set MAC speed using default port */
+ i = CONFIG_BCM_SF2_ETH_DEFAULT_PORT;
+ debug("PHY %d: speed:%d, duplex:%d, link:%d\n", i,
+ eth->port[i]->speed, eth->port[i]->duplex, eth->port[i]->link);
+ eth->set_mac_speed(eth->port[i]->speed, eth->port[i]->duplex);
+
+ debug("Enable Ethernet Done.\n");
+
+ return 0;
+}
+
+static void bcm_sf2_eth_close(struct eth_device *dev)
+{
+ struct eth_info *eth = (struct eth_info *)(dev->priv);
+ struct eth_dma *dma = &(eth->dma);
+
+ /* disable DMA */
+ dma->disable_dma(dma, MAC_DMA_RX);
+ dma->disable_dma(dma, MAC_DMA_TX);
+
+ eth->disable_mac();
+}
+
+int bcm_sf2_eth_register(bd_t *bis, u8 dev_num)
+{
+ struct eth_device *dev;
+ struct eth_info *eth;
+ int rc;
+
+ dev = (struct eth_device *)malloc(sizeof(struct eth_device));
+ if (dev == NULL) {
+ error("%s: Not enough memory!\n", __func__);
+ return -1;
+ }
+
+ eth = (struct eth_info *)malloc(sizeof(struct eth_info));
+ if (eth == NULL) {
+ error("%s: Not enough memory!\n", __func__);
+ return -1;
+ }
+
+ printf(banner);
+
+ memset(dev, 0, sizeof(*dev));
+ sprintf(dev->name, "%s_%s-%hu", BCM_SF2_ETH_DEV_NAME,
+ BCM_SF2_ETH_MAC_NAME, dev_num);
+
+ dev->priv = (void *)eth;
+ dev->iobase = 0;
+
+ dev->init = bcm_sf2_eth_open;
+ dev->halt = bcm_sf2_eth_close;
+ dev->send = bcm_sf2_eth_send;
+ dev->recv = bcm_sf2_eth_receive;
+ dev->write_hwaddr = bcm_sf2_eth_write_hwaddr;
+
+#ifdef CONFIG_BCM_SF2_ETH_GMAC
+ if (gmac_add(dev)) {
+ free(eth);
+ free(dev);
+ error("%s: Adding GMAC failed!\n", __func__);
+ return -1;
+ }
+#else
+#error "bcm_sf2_eth: NEED to register a MAC!"
+#endif
+
+ eth_register(dev);
+
+#ifdef CONFIG_CMD_MII
+ miiphy_register(dev->name, eth->miiphy_read, eth->miiphy_write);
+#endif
+
+ /* Initialization */
+ debug("Ethernet initialization ...");
+
+ rc = bcm_sf2_eth_init(dev);
+ if (rc != 0) {
+ error("%s: configuration failed!\n", __func__);
+ return -1;
+ }
+
+ printf("Basic ethernet functionality initialized\n");
+
+ return 0;
+}
diff --git a/drivers/net/bcm-sf2-eth.h b/drivers/net/bcm-sf2-eth.h
new file mode 100644
index 0000000..49a5836
--- /dev/null
+++ b/drivers/net/bcm-sf2-eth.h
@@ -0,0 +1,70 @@
+/*
+ * Copyright 2014 Broadcom Corporation.
+ *
+ * SPDX-License-Identifier: GPL-2.0+
+ */
+
+#ifndef _BCM_SF2_ETH_H_
+#define _BCM_SF2_ETH_H_
+
+#include <phy.h>
+
+#define RX_BUF_SIZE 2048
+/* RX_BUF_NUM must be power of 2 */
+#define RX_BUF_NUM 32
+
+#define TX_BUF_SIZE 2048
+/* TX_BUF_NUM must be power of 2 */
+#define TX_BUF_NUM 2
+
+/* Support 2 Ethernet ports now */
+#define BCM_ETH_MAX_PORT_NUM 2
+
+#define CONFIG_BCM_SF2_ETH_DEFAULT_PORT 0
+
+enum {
+ MAC_DMA_TX = 1,
+ MAC_DMA_RX = 2
+};
+
+struct eth_dma {
+ void *tx_desc_aligned;
+ void *rx_desc_aligned;
+ void *tx_desc;
+ void *rx_desc;
+
+ uint8_t *tx_buf;
+ uint8_t *rx_buf;
+
+ int cur_tx_index;
+ int cur_rx_index;
+
+ int (*tx_packet)(struct eth_dma *dma, void *packet, int length);
+ bool (*check_tx_done)(struct eth_dma *dma);
+
+ int (*check_rx_done)(struct eth_dma *dma, uint8_t *buf);
+
+ int (*enable_dma)(struct eth_dma *dma, int dir);
+ int (*disable_dma)(struct eth_dma *dma, int dir);
+};
+
+struct eth_info {
+ struct eth_dma dma;
+ phy_interface_t phy_interface;
+ struct phy_device *port[BCM_ETH_MAX_PORT_NUM];
+ int port_num;
+
+ int (*miiphy_read)(const char *devname, unsigned char phyaddr,
+ unsigned char reg, unsigned short *value);
+ int (*miiphy_write)(const char *devname, unsigned char phyaddr,
+ unsigned char reg, unsigned short value);
+
+ int (*mac_init)(struct eth_device *dev);
+ int (*enable_mac)(void);
+ int (*disable_mac)(void);
+ int (*set_mac_addr)(unsigned char *mac);
+ int (*set_mac_speed)(int speed, int duplex);
+
+};
+
+#endif /* _BCM_SF2_ETH_H_ */
diff --git a/include/netdev.h b/include/netdev.h
index e45dd7a..b3b5930 100644
--- a/include/netdev.h
+++ b/include/netdev.h
@@ -31,6 +31,7 @@ int altera_tse_initialize(u8 dev_num, int mac_base,
int at91emac_register(bd_t *bis, unsigned long iobase);
int au1x00_enet_initialize(bd_t*);
int ax88180_initialize(bd_t *bis);
+int bcm_sf2_eth_register(bd_t *bis, u8 dev_num);
int bfin_EMAC_initialize(bd_t *bis);
int calxedaxgmac_initialize(u32 id, ulong base_addr);
int cs8900_initialize(u8 dev_num, int base_addr);
--
1.8.5
next prev parent reply other threads:[~2014-08-02 3:37 UTC|newest]
Thread overview: 7+ messages / expand[flat|nested] mbox.gz Atom feed top
2014-08-02 3:37 [U-Boot] [PATCH v2 1/3] arm: bcm281xx: Add Ethernet Clock support Steve Rae
2014-08-02 3:37 ` Steve Rae [this message]
2014-08-30 15:14 ` [U-Boot] [U-Boot, v2, 2/3] arm: bcm281xx: net: Add Ethernet Driver Tom Rini
2014-08-02 3:37 ` [U-Boot] [PATCH v2 3/3] arm: bcm281xx: add board with Ethernet capability Steve Rae
2014-08-30 15:14 ` [U-Boot] [U-Boot, v2, " Tom Rini
2014-08-28 18:09 ` [U-Boot] [PATCH v2 1/3] arm: bcm281xx: Add Ethernet Clock support Steve Rae
2014-08-30 15:14 ` [U-Boot] [U-Boot, v2, " Tom Rini
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=1406950637-19820-2-git-send-email-srae@broadcom.com \
--to=srae@broadcom.com \
--cc=u-boot@lists.denx.de \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
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.