diff options
| -rw-r--r-- | cpu/sh4/cpu.c | 8 | ||||
| -rw-r--r-- | drivers/net/sh_eth.c | 355 | ||||
| -rw-r--r-- | drivers/net/sh_eth.h | 10 | ||||
| -rw-r--r-- | include/netdev.h | 1 | 
4 files changed, 240 insertions, 134 deletions
| diff --git a/cpu/sh4/cpu.c b/cpu/sh4/cpu.c index d94e13981..52b6cfd68 100644 --- a/cpu/sh4/cpu.c +++ b/cpu/sh4/cpu.c @@ -82,3 +82,11 @@ int dcache_status (void)  {  	return 0;  } + +int cpu_eth_init(bd_t *bis) +{ +#ifdef CONFIG_SH_ETHER +	sh_eth_initialize(bis); +#endif +	return 0; +} diff --git a/drivers/net/sh_eth.c b/drivers/net/sh_eth.c index 9e3cf98b3..ebe858894 100644 --- a/drivers/net/sh_eth.c +++ b/drivers/net/sh_eth.c @@ -24,6 +24,7 @@  #include <common.h>  #include <malloc.h>  #include <net.h> +#include <netdev.h>  #include <asm/errno.h>  #include <asm/io.h> @@ -36,12 +37,7 @@  # error "Please define CONFIG_SH_ETHER_PHY_ADDR"  #endif -extern int eth_init(bd_t *bd); -extern void eth_halt(void); -extern int eth_rx(void); -extern int eth_send(volatile void *packet, int length); - -static struct dev_info_s *dev; +#define SH_ETH_PHY_DELAY 50000  /*   * Bits are written to the PHY serially using the @@ -89,7 +85,7 @@ static void sh_eth_mii_ind_bus_release(int port)  	udelay(1);  } -static int sh_eth_mii_read_phy_bits(int port, u32 * val, int len) +static void sh_eth_mii_read_phy_bits(int port, u32 *val, int len)  {  	int i;  	u32 pir; @@ -106,8 +102,6 @@ static int sh_eth_mii_read_phy_bits(int port, u32 * val, int len)  		outl(0, PIR(port));  		udelay(1);  	} - -	return 0;  }  #define PHY_INIT	0xFFFFFFFF @@ -183,26 +177,23 @@ static void sh_eth_mii_write_phy_reg(int port, u8 phy_addr, int reg, u16 val)  	sh_eth_mii_ind_bus_release(port);  } -void eth_halt(void) -{ -} - -int eth_send(volatile void *packet, int len) +int sh_eth_send(struct eth_device *dev, volatile void *packet, int len)  { -	int port = dev->port; -	struct port_info_s *port_info = &dev->port_info[port]; -	int timeout; -	int rc = 0; +	struct sh_eth_dev *eth = dev->priv; +	int port = eth->port, ret = 0, timeout; +	struct sh_eth_info *port_info = ð->port_info[port];  	if (!packet || len > 0xffff) { -		printf("eth_send: Invalid argument\n"); -		return -EINVAL; +		printf(SHETHER_NAME ": %s: Invalid argument\n", __func__); +		ret = -EINVAL; +		goto err;  	}  	/* packet must be a 4 byte boundary */  	if ((int)packet & (4 - 1)) { -		printf("eth_send: packet not 4 byte alligned\n"); -		return -EFAULT; +		printf(SHETHER_NAME ": %s: packet not 4 byte alligned\n", __func__); +		ret = -EFAULT; +		goto err;  	}  	/* Update tx descriptor */ @@ -224,24 +215,25 @@ int eth_send(volatile void *packet, int len)  		udelay(100);  	if (timeout < 0) { -		printf("eth_send: transmit timeout\n"); -		rc = -1; +		printf(SHETHER_NAME ": transmit timeout\n"); +		ret = -ETIMEDOUT;  		goto err;  	} -err:  	port_info->tx_desc_cur++;  	if (port_info->tx_desc_cur >= port_info->tx_desc_base + NUM_TX_DESC)  		port_info->tx_desc_cur = port_info->tx_desc_base; -	return rc; +	return ret; +err: +	return ret;  } -int eth_rx(void) +int sh_eth_recv(struct eth_device *dev)  { -	int port = dev->port; -	struct port_info_s *port_info = &dev->port_info[port]; -	int len = 0; +	struct sh_eth_dev *eth = dev->priv; +	int port = eth->port, len = 0; +	struct sh_eth_info *port_info = ð->port_info[port];  	volatile u8 *packet;  	/* Check if the rx descriptor is ready */ @@ -275,10 +267,10 @@ int eth_rx(void)  }  #define EDMR_INIT_CNT 1000 -static int sh_eth_reset(struct dev_info_s *dev) +static int sh_eth_reset(struct sh_eth_dev *eth)  { -	int port = dev->port; -	int i; +	int port = eth->port; +	int ret = 0, i;  	/* Start e-dmac transmitter and receiver */  	outl(EDSR_ENALL, EDSR(port)); @@ -292,33 +284,36 @@ static int sh_eth_reset(struct dev_info_s *dev)  	}  	if (i == EDMR_INIT_CNT) { -		printf("Error: Software reset timeout\n"); -		return -1; +		printf(SHETHER_NAME  ": Software reset timeout\n"); +		ret = -EIO;  	} -	return 0; + +	return ret;  } -static int sh_eth_tx_desc_init(struct dev_info_s *dev) +static int sh_eth_tx_desc_init(struct sh_eth_dev *eth)  { -	int port = dev->port; -	struct port_info_s *port_info = &dev->port_info[port]; +	int port = eth->port, i, ret = 0;  	u32 tmp_addr; +	struct sh_eth_info *port_info = ð->port_info[port];  	struct tx_desc_s *cur_tx_desc; -	int i; -	/* Allocate tx descriptors. They must be TX_DESC_SIZE bytes -	   aligned */ -	if (!(port_info->tx_desc_malloc = malloc(NUM_TX_DESC * +	/* +	 * Allocate tx descriptors. They must be TX_DESC_SIZE bytes aligned +	 */ +	port_info->tx_desc_malloc = malloc(NUM_TX_DESC *  						 sizeof(struct tx_desc_s) + -						 TX_DESC_SIZE - 1))) { -		printf("Error: malloc failed\n"); -		return -ENOMEM; +						 TX_DESC_SIZE - 1); +	if (!port_info->tx_desc_malloc) { +		printf(SHETHER_NAME ": malloc failed\n"); +		ret = -ENOMEM; +		goto err;  	} +  	tmp_addr = (u32) (((int)port_info->tx_desc_malloc + TX_DESC_SIZE - 1) &  			  ~(TX_DESC_SIZE - 1));  	/* Make sure we use a P2 address (non-cacheable) */  	port_info->tx_desc_base = (struct tx_desc_s *)ADDR_TO_P2(tmp_addr); -  	port_info->tx_desc_cur = port_info->tx_desc_base;  	/* Initialize all descriptors */ @@ -340,26 +335,30 @@ static int sh_eth_tx_desc_init(struct dev_info_s *dev)  	outl(ADDR_TO_PHY(cur_tx_desc), TDFXR(port));  	outl(0x01, TDFFR(port));/* Last discriptor bit */ -	return 0; +err: +	return ret;  } -static int sh_eth_rx_desc_init(struct dev_info_s *dev) +static int sh_eth_rx_desc_init(struct sh_eth_dev *eth)  { -	int port = dev->port; -	struct port_info_s *port_info = &dev->port_info[port]; -	u32 tmp_addr; +	int port = eth->port, i , ret = 0; +	struct sh_eth_info *port_info = ð->port_info[port];  	struct rx_desc_s *cur_rx_desc; +	u32 tmp_addr;  	u8 *rx_buf; -	int i; -	/* Allocate rx descriptors. They must be RX_DESC_SIZE bytes -	   aligned */ -	if (!(port_info->rx_desc_malloc = malloc(NUM_RX_DESC * +	/* +	 * Allocate rx descriptors. They must be RX_DESC_SIZE bytes aligned +	 */ +	port_info->rx_desc_malloc = malloc(NUM_RX_DESC *  						 sizeof(struct rx_desc_s) + -						 RX_DESC_SIZE - 1))) { -		printf("Error: malloc failed\n"); -		return -ENOMEM; +						 RX_DESC_SIZE - 1); +	if (!port_info->rx_desc_malloc) { +		printf(SHETHER_NAME ": malloc failed\n"); +		ret = -ENOMEM; +		goto err;  	} +  	tmp_addr = (u32) (((int)port_info->rx_desc_malloc + RX_DESC_SIZE - 1) &  			  ~(RX_DESC_SIZE - 1));  	/* Make sure we use a P2 address (non-cacheable) */ @@ -367,15 +366,17 @@ static int sh_eth_rx_desc_init(struct dev_info_s *dev)  	port_info->rx_desc_cur = port_info->rx_desc_base; -	/* Allocate rx data buffers. They must be 32 bytes aligned  and in -	   P2 area */ -	if (!(port_info->rx_buf_malloc = malloc(NUM_RX_DESC * MAX_BUF_SIZE + -						31))) { -		printf("Error: malloc failed\n"); -		free(port_info->rx_desc_malloc); -		port_info->rx_desc_malloc = NULL; -		return -ENOMEM; +	/* +	 * Allocate rx data buffers. They must be 32 bytes aligned  and in +	 * P2 area +	 */ +	port_info->rx_buf_malloc = malloc(NUM_RX_DESC * MAX_BUF_SIZE + 31); +	if (!port_info->rx_buf_malloc) { +		printf(SHETHER_NAME ": malloc failed\n"); +		ret = -ENOMEM; +		goto err_buf_malloc;  	} +  	tmp_addr = (u32)(((int)port_info->rx_buf_malloc + (32 - 1)) &  			  ~(32 - 1));  	port_info->rx_buf_base = (u8 *)ADDR_TO_P2(tmp_addr); @@ -399,18 +400,31 @@ static int sh_eth_rx_desc_init(struct dev_info_s *dev)  	outl(ADDR_TO_PHY(cur_rx_desc), RDFXR(port));  	outl(RDFFR_RDLF, RDFFR(port)); -	return 0; +	return ret; + +err_buf_malloc: +	free(port_info->rx_desc_malloc); +	port_info->rx_desc_malloc = NULL; + +err: +	return ret;  } -static void sh_eth_desc_free(struct dev_info_s *dev) +static void sh_eth_tx_desc_free(struct sh_eth_dev *eth)  { -	int port = dev->port; -	struct port_info_s *port_info = &dev->port_info[port]; +	int port = eth->port; +	struct sh_eth_info *port_info = ð->port_info[port];  	if (port_info->tx_desc_malloc) {  		free(port_info->tx_desc_malloc);  		port_info->tx_desc_malloc = NULL;  	} +} + +static void sh_eth_rx_desc_free(struct sh_eth_dev *eth) +{ +	int port = eth->port; +	struct sh_eth_info *port_info = ð->port_info[port];  	if (port_info->rx_desc_malloc) {  		free(port_info->rx_desc_malloc); @@ -423,36 +437,48 @@ static void sh_eth_desc_free(struct dev_info_s *dev)  	}  } -static int sh_eth_desc_init(struct dev_info_s *dev) +static int sh_eth_desc_init(struct sh_eth_dev *eth)  { -	int rc; +	int ret = 0; -	if ((rc = sh_eth_tx_desc_init(dev)) || (rc = sh_eth_rx_desc_init(dev))) { -		sh_eth_desc_free(dev); -		return rc; -	} +	ret = sh_eth_tx_desc_init(eth); +	if (ret) +		goto err_tx_init; -	return 0; +	ret = sh_eth_rx_desc_init(eth); +	if (ret) +		goto err_rx_init; + +	return ret; +err_rx_init: +	sh_eth_tx_desc_free(eth); + +err_tx_init: +	return ret;  } -static int sh_eth_phy_config(struct dev_info_s *dev) +static int sh_eth_phy_config(struct sh_eth_dev *eth)  { -	int port = dev->port; -	struct port_info_s *port_info = &dev->port_info[port]; -	int timeout; +	int port = eth->port, timeout, ret = 0; +	struct sh_eth_info *port_info = ð->port_info[port];  	u32 val; +  	/* Reset phy */ -	sh_eth_mii_write_phy_reg(port, port_info->phy_addr, PHY_CTRL, PHY_C_RESET); +	sh_eth_mii_write_phy_reg +		(port, port_info->phy_addr, PHY_CTRL, PHY_C_RESET);  	timeout = 10;  	while (timeout--) { -		val = sh_eth_mii_read_phy_reg(port, port_info->phy_addr, PHY_CTRL); +		val = sh_eth_mii_read_phy_reg(port, +				port_info->phy_addr, PHY_CTRL);  		if (!(val & PHY_C_RESET))  			break; -		udelay(50000); +		udelay(SH_ETH_PHY_DELAY);  	} +  	if (timeout < 0) { -		printf("%s phy reset timeout\n", __func__); -		return -1; +		printf(SHETHER_NAME ": phy reset timeout\n"); +		ret = -EIO; +		goto err_tout;  	}  	/* Advertise 100/10 baseT full/half duplex */ @@ -467,23 +493,27 @@ static int sh_eth_phy_config(struct dev_info_s *dev)  		val = sh_eth_mii_read_phy_reg(port, port_info->phy_addr, 1);  		if (val & PHY_S_ANEGC)  			break; -		udelay(50000); + +		udelay(SH_ETH_PHY_DELAY);  	} +  	if (timeout < 0) { -		printf("sh_eth_phy_config() phy auto-negotiation failed\n"); -		return -1; +		printf(SHETHER_NAME ": phy auto-negotiation failed\n"); +		ret = -ETIMEDOUT; +		goto err_tout;  	} -	return 0; +	return ret; + +err_tout: +	return ret;  } -static int sh_eth_config(struct dev_info_s *dev, bd_t * bd) +static int sh_eth_config(struct sh_eth_dev *eth, bd_t *bd)  { -	int port = dev->port; -	struct port_info_s *port_info = &dev->port_info[port]; -	u32 val; -	u32 phy_status; -	int rc; +	int port = eth->port, ret = 0; +	u32 val,  phy_status; +	struct sh_eth_info *port_info = ð->port_info[port];  	/* Configure e-dmac registers */  	outl((inl(EDMR(port)) & ~EMDR_DESC_R) | EDMR_EL, EDMR(port)); @@ -513,20 +543,20 @@ static int sh_eth_config(struct dev_info_s *dev, bd_t * bd)  	outl(TPAUSER_TPAUSE, TPAUSER(port));  	/* Configure phy */ -	if ((rc = sh_eth_phy_config(dev))) -		return rc; - +	ret = sh_eth_phy_config(eth); +	if (ret) { +		printf(SHETHER_NAME ":i phy config timeout\n"); +		goto err_phy_cfg; +	}  	/* Read phy status to finish configuring the e-mac */ -	phy_status = sh_eth_mii_read_phy_reg(dev->port, -					     dev->port_info[dev->port].phy_addr, -					     1); +	phy_status = sh_eth_mii_read_phy_reg(port, port_info->phy_addr, 1);  	/* Set the transfer speed */  	if (phy_status & (PHY_S_100X_F|PHY_S_100X_H)) { -		printf("100Base/"); +		printf(SHETHER_NAME ": 100Base/");  		outl(GECMR_100B, GECMR(port));  	} else { -		printf("10Base/"); +		printf(SHETHER_NAME ": 10Base/");  		outl(GECMR_10B, GECMR(port));  	} @@ -538,27 +568,34 @@ static int sh_eth_config(struct dev_info_s *dev, bd_t * bd)  		printf("Half\n");  		outl((ECMR_CHG_DM|ECMR_RE|ECMR_TE),  ECMR(port));  	} -	return 0; + +	return ret; + +err_phy_cfg: +	return ret;  } -static int sh_eth_start(struct dev_info_s *dev) +static void sh_eth_start(struct sh_eth_dev *eth)  {  	/*  	 * Enable the e-dmac receiver only. The transmitter will be enabled when  	 * we have something to transmit  	 */ -	outl(EDRRR_R, EDRRR(dev->port)); +	outl(EDRRR_R, EDRRR(eth->port)); +} -	return 0; +static void sh_eth_stop(struct sh_eth_dev *eth) +{ +	outl(~EDRRR_R, EDRRR(eth->port));  }  static int sh_eth_get_mac(bd_t *bd)  {  	char *s, *e; -	int i;  	s = getenv("ethaddr");  	if (s != NULL) { +		int i;  		for (i = 0; i < 6; ++i) {  			bd->bi_enetaddr[i] = s ? simple_strtoul(s, &e, 16) : 0;  			if (s) @@ -570,34 +607,92 @@ static int sh_eth_get_mac(bd_t *bd)  	return 0;  } -int eth_init(bd_t *bd) +int sh_eth_init(struct eth_device *dev, bd_t *bd)  { -	int rc; -	/* Allocate main device information structure */ -	if (!(dev = malloc(sizeof(*dev)))) { -		printf("eth_init: malloc failed\n"); -		return -ENOMEM; -	} +	int ret = 0; +	struct sh_eth_dev *eth = dev->priv; -	memset(dev, 0, sizeof(*dev)); +	ret = sh_eth_reset(eth); +	if (ret) +		goto err; -	dev->port = CONFIG_SH_ETHER_USE_PORT; -	dev->port_info[dev->port].phy_addr = CONFIG_SH_ETHER_PHY_ADDR; +	ret = sh_eth_desc_init(eth); +	if (ret) +		goto err; -	sh_eth_get_mac(bd); +	ret = sh_eth_config(eth, bd); +	if (ret) +		goto err_config; + +	sh_eth_start(eth); + +	return ret; -	if ((rc = sh_eth_reset(dev)) || (rc = sh_eth_desc_init(dev))) +err_config: +	sh_eth_tx_desc_free(eth); +	sh_eth_rx_desc_free(eth); + +err: +	return ret; +} + +void sh_eth_halt(struct eth_device *dev) +{ +	struct sh_eth_dev *eth = dev->priv; + +	sh_eth_reset(eth); +	sh_eth_stop(eth); +} + +int sh_eth_initialize(bd_t *bd) +{ +    int ret = 0; +	struct sh_eth_dev *eth = NULL; +    struct eth_device *dev = NULL; + +    eth = (struct sh_eth_dev *)malloc(sizeof(struct sh_eth_dev)); +	if (!eth) { +		printf(SHETHER_NAME ": %s: malloc failed\n", __func__); +		ret = -ENOMEM; +		goto err; +	} + +    dev = (struct eth_device *)malloc(sizeof(struct eth_device)); +	if (!dev) { +		printf(SHETHER_NAME ": %s: malloc failed\n", __func__); +		ret = -ENOMEM;  		goto err; +	} +    memset(dev, 0, sizeof(struct eth_device)); +    memset(eth, 0, sizeof(struct sh_eth_dev)); -	if ((rc = sh_eth_config(dev, bd)) || (rc = sh_eth_start(dev))) -		goto err_desc; +	eth->port = CONFIG_SH_ETHER_USE_PORT; +	eth->port_info[eth->port].phy_addr = CONFIG_SH_ETHER_PHY_ADDR; -	return 0; +    dev->priv = (void *)eth; +    dev->iobase = 0; +    dev->init = sh_eth_init; +    dev->halt = sh_eth_halt; +    dev->send = sh_eth_send; +    dev->recv = sh_eth_recv; +    eth->port_info[eth->port].dev = dev; + +	sprintf(dev->name, SHETHER_NAME); + +    /* Register Device to EtherNet subsystem  */ +    eth_register(dev); + +	sh_eth_get_mac(bd); + +	return ret; -err_desc: -	sh_eth_desc_free(dev);  err: -	free(dev); -	printf("eth_init: Failed\n"); -	return rc; +	if (dev) +		free(dev); + +	if (eth) +		free(eth); + +	printf(SHETHER_NAME ": Failed\n"); +	return ret;  } diff --git a/drivers/net/sh_eth.h b/drivers/net/sh_eth.h index 9cf0ea0b9..a13fff02c 100644 --- a/drivers/net/sh_eth.h +++ b/drivers/net/sh_eth.h @@ -20,6 +20,7 @@   * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.   */ +#include <netdev.h>  #include <asm/types.h>  #define SHETHER_NAME "sh_eth" @@ -48,7 +49,7 @@  #define TX_DESC_PADDING		4  #define TX_DESC_SIZE		(12 + TX_DESC_PADDING) -/* Tx descriptor. We always use 4 bytes of padding */ +/* Tx descriptor. We always use 3 bytes of padding */  struct tx_desc_s {  	volatile u32 td0;  	u32 td1; @@ -72,7 +73,7 @@ struct rx_desc_s {  	u32 padding;  }; -struct port_info_s { +struct sh_eth_info {  	struct tx_desc_s *tx_desc_malloc;  	struct tx_desc_s *tx_desc_base;  	struct tx_desc_s *tx_desc_cur; @@ -83,11 +84,12 @@ struct port_info_s {  	u8 *rx_buf_base;  	u8 mac_addr[6];  	u8 phy_addr; +	struct eth_device *dev;  }; -struct dev_info_s { +struct sh_eth_dev {  	int port; -	struct port_info_s port_info[MAX_PORT_NUM]; +	struct sh_eth_info port_info[MAX_PORT_NUM];  };  /* Register Address */ diff --git a/include/netdev.h b/include/netdev.h index 751f0dab5..a7d662d75 100644 --- a/include/netdev.h +++ b/include/netdev.h @@ -70,6 +70,7 @@ int skge_initialize(bd_t *bis);  int tsi108_eth_initialize(bd_t *bis);  int uec_initialize(int index);  int uli526x_initialize(bd_t *bis); +int sh_eth_initialize(bd_t *bis);  /* Boards with PCI network controllers can call this from their board_eth_init()   * function to initialize whatever's on board. |