/* * Copyright 2014 Broadcom Corporation. * * SPDX-License-Identifier: GPL-2.0+ */ #include #include #include #include #include #include #include #include #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; }