Commit 799e125c authored by Jiandong Zheng's avatar Jiandong Zheng Committed by Tom Rini

arm: bcm281xx: net: Add Ethernet Driver

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: default avatarJiandong Zheng <jdzheng@broadcom.com>
Signed-off-by: default avatarSteve Rae <srae@broadcom.com>
parent 2d66a0fd
......@@ -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
......
/*
* 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;