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>master
parent
2d66a0fd58
commit
799e125cca
@ -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; |
||||
} |
@ -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_ */ |
@ -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; |
||||
} |
@ -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_ */ |
Loading…
Reference in new issue