diff options
author | Jiandong Zheng <jdzheng@broadcom.com> | 2014-08-01 20:37:16 -0700 |
---|---|---|
committer | Tom Rini <trini@ti.com> | 2014-08-30 07:46:39 -0400 |
commit | 799e125cca9f75a6e3bc87676266f8da4ebefcfd (patch) | |
tree | 1548e0c775e78f41bbe8fc1b8da4b3820feb967a /drivers/net/bcm-sf2-eth.c | |
parent | 2d66a0fd58ac8623b3eea01e31b7917a20f287c1 (diff) | |
download | u-boot-imx-799e125cca9f75a6e3bc87676266f8da4ebefcfd.zip u-boot-imx-799e125cca9f75a6e3bc87676266f8da4ebefcfd.tar.gz u-boot-imx-799e125cca9f75a6e3bc87676266f8da4ebefcfd.tar.bz2 |
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: Jiandong Zheng <jdzheng@broadcom.com>
Signed-off-by: Steve Rae <srae@broadcom.com>
Diffstat (limited to 'drivers/net/bcm-sf2-eth.c')
-rw-r--r-- | drivers/net/bcm-sf2-eth.c | 268 |
1 files changed, 268 insertions, 0 deletions
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; +} |