mirror of
				https://github.com/smaeul/u-boot.git
				synced 2025-10-25 18:18:19 +01:00 
			
		
		
		
	As part of bringing the master branch back in to next, we need to allow for all of these changes to exist here. Reported-by: Jonas Karlman <jonas@kwiboo.se> Signed-off-by: Tom Rini <trini@konsulko.com>
		
			
				
	
	
		
			275 lines
		
	
	
		
			5.9 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			275 lines
		
	
	
		
			5.9 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| // SPDX-License-Identifier: GPL-2.0+
 | |
| /*
 | |
|  * Copyright 2014 Broadcom Corporation.
 | |
|  */
 | |
| 
 | |
| #include <log.h>
 | |
| #include <malloc.h>
 | |
| #include <net.h>
 | |
| #include <config.h>
 | |
| #include <linux/delay.h>
 | |
| #include <linux/printk.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) {
 | |
| 		pr_err("%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),
 | |
| 			     -1, 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) {
 | |
| 			pr_err("%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 *)net_rx_packets[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)) {
 | |
| 			pr_err("%s: Wrong Ethernet packet size (%d B), skip!\n",
 | |
| 			      __func__, rcvlen);
 | |
| 			break;
 | |
| 		} else {
 | |
| 			debug("recieved\n");
 | |
| 
 | |
| 			/* Forward received packet to uboot network handler */
 | |
| 			net_process_received_packet(buf, rcvlen);
 | |
| 
 | |
| 			if (++i >= PKTBUFSRX)
 | |
| 				i = 0;
 | |
| 			buf = net_rx_packets[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, struct bd_info *bt)
 | |
| {
 | |
| 	struct eth_info *eth = (struct eth_info *)(dev->priv);
 | |
| 	struct eth_dma *dma = &(eth->dma);
 | |
| 	int i;
 | |
| 
 | |
| 	debug("Enabling BCM SF2 Ethernet.\n");
 | |
| 
 | |
| 	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])) {
 | |
| 			pr_err("%s: PHY %d startup failed!\n", __func__, i);
 | |
| 			if (i == CONFIG_BCM_SF2_ETH_DEFAULT_PORT) {
 | |
| 				pr_err("%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(struct bd_info *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) {
 | |
| 		pr_err("%s: Not enough memory!\n", __func__);
 | |
| 		return -1;
 | |
| 	}
 | |
| 
 | |
| 	eth = (struct eth_info *)malloc(sizeof(struct eth_info));
 | |
| 	if (eth == NULL) {
 | |
| 		pr_err("%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);
 | |
| 		pr_err("%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
 | |
| 	int retval;
 | |
| 	struct mii_dev *mdiodev = mdio_alloc();
 | |
| 
 | |
| 	if (!mdiodev)
 | |
| 		return -ENOMEM;
 | |
| 	strlcpy(mdiodev->name, dev->name, MDIO_NAME_LEN);
 | |
| 	mdiodev->read = eth->miiphy_read;
 | |
| 	mdiodev->write = eth->miiphy_write;
 | |
| 
 | |
| 	retval = mdio_register(mdiodev);
 | |
| 	if (retval < 0)
 | |
| 		return retval;
 | |
| #endif
 | |
| 
 | |
| 	/* Initialization */
 | |
| 	debug("Ethernet initialization ...");
 | |
| 
 | |
| 	rc = bcm_sf2_eth_init(dev);
 | |
| 	if (rc != 0) {
 | |
| 		pr_err("%s: configuration failed!\n", __func__);
 | |
| 		return -1;
 | |
| 	}
 | |
| 
 | |
| 	printf("Basic ethernet functionality initialized\n");
 | |
| 
 | |
| 	return 0;
 | |
| }
 |