mirror of
https://github.com/brain-hackers/u-boot-brain
synced 2024-08-19 08:28:51 +09:00
![Jiandong Zheng](/assets/img/avatar_default.png)
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>
269 lines
5.7 KiB
C
269 lines
5.7 KiB
C
/*
|
|
* 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;
|
|
}
|