xref: /openbmc/u-boot/net/eth_legacy.c (revision c68c03f52badc90951dbf8a054c0e500e04bf365)
1  /*
2   * (C) Copyright 2001-2015
3   * Wolfgang Denk, DENX Software Engineering, wd@denx.de.
4   * Joe Hershberger, National Instruments
5   *
6   * SPDX-License-Identifier:	GPL-2.0+
7   */
8  
9  #include <common.h>
10  #include <command.h>
11  #include <environment.h>
12  #include <net.h>
13  #include <phy.h>
14  #include <linux/errno.h>
15  #include "eth_internal.h"
16  
17  DECLARE_GLOBAL_DATA_PTR;
18  
19  /*
20   * CPU and board-specific Ethernet initializations.  Aliased function
21   * signals caller to move on
22   */
23  static int __def_eth_init(bd_t *bis)
24  {
25  	return -1;
26  }
27  int cpu_eth_init(bd_t *bis) __attribute__((weak, alias("__def_eth_init")));
28  int board_eth_init(bd_t *bis) __attribute__((weak, alias("__def_eth_init")));
29  
30  #ifdef CONFIG_API
31  static struct {
32  	uchar data[PKTSIZE];
33  	int length;
34  } eth_rcv_bufs[PKTBUFSRX];
35  
36  static unsigned int eth_rcv_current, eth_rcv_last;
37  #endif
38  
39  static struct eth_device *eth_devices;
40  struct eth_device *eth_current;
41  
42  void eth_set_current_to_next(void)
43  {
44  	eth_current = eth_current->next;
45  }
46  
47  void eth_set_dev(struct eth_device *dev)
48  {
49  	eth_current = dev;
50  }
51  
52  struct eth_device *eth_get_dev_by_name(const char *devname)
53  {
54  	struct eth_device *dev, *target_dev;
55  
56  	BUG_ON(devname == NULL);
57  
58  	if (!eth_devices)
59  		return NULL;
60  
61  	dev = eth_devices;
62  	target_dev = NULL;
63  	do {
64  		if (strcmp(devname, dev->name) == 0) {
65  			target_dev = dev;
66  			break;
67  		}
68  		dev = dev->next;
69  	} while (dev != eth_devices);
70  
71  	return target_dev;
72  }
73  
74  struct eth_device *eth_get_dev_by_index(int index)
75  {
76  	struct eth_device *dev, *target_dev;
77  
78  	if (!eth_devices)
79  		return NULL;
80  
81  	dev = eth_devices;
82  	target_dev = NULL;
83  	do {
84  		if (dev->index == index) {
85  			target_dev = dev;
86  			break;
87  		}
88  		dev = dev->next;
89  	} while (dev != eth_devices);
90  
91  	return target_dev;
92  }
93  
94  int eth_get_dev_index(void)
95  {
96  	if (!eth_current)
97  		return -1;
98  
99  	return eth_current->index;
100  }
101  
102  static int on_ethaddr(const char *name, const char *value, enum env_op op,
103  	int flags)
104  {
105  	int index;
106  	struct eth_device *dev;
107  
108  	if (!eth_devices)
109  		return 0;
110  
111  	/* look for an index after "eth" */
112  	index = simple_strtoul(name + 3, NULL, 10);
113  
114  	dev = eth_devices;
115  	do {
116  		if (dev->index == index) {
117  			switch (op) {
118  			case env_op_create:
119  			case env_op_overwrite:
120  				eth_parse_enetaddr(value, dev->enetaddr);
121  				eth_write_hwaddr(dev, "eth", dev->index);
122  				break;
123  			case env_op_delete:
124  				memset(dev->enetaddr, 0, ARP_HLEN);
125  			}
126  		}
127  		dev = dev->next;
128  	} while (dev != eth_devices);
129  
130  	return 0;
131  }
132  U_BOOT_ENV_CALLBACK(ethaddr, on_ethaddr);
133  
134  int eth_write_hwaddr(struct eth_device *dev, const char *base_name,
135  		   int eth_number)
136  {
137  	unsigned char env_enetaddr[ARP_HLEN];
138  	int ret = 0;
139  
140  	eth_env_get_enetaddr_by_index(base_name, eth_number, env_enetaddr);
141  
142  	if (!is_zero_ethaddr(env_enetaddr)) {
143  		if (!is_zero_ethaddr(dev->enetaddr) &&
144  		    memcmp(dev->enetaddr, env_enetaddr, ARP_HLEN)) {
145  			printf("\nWarning: %s MAC addresses don't match:\n",
146  			       dev->name);
147  			printf("Address in SROM is         %pM\n",
148  			       dev->enetaddr);
149  			printf("Address in environment is  %pM\n",
150  			       env_enetaddr);
151  		}
152  
153  		memcpy(dev->enetaddr, env_enetaddr, ARP_HLEN);
154  	} else if (is_valid_ethaddr(dev->enetaddr)) {
155  		eth_env_set_enetaddr_by_index(base_name, eth_number,
156  					      dev->enetaddr);
157  	} else if (is_zero_ethaddr(dev->enetaddr)) {
158  #ifdef CONFIG_NET_RANDOM_ETHADDR
159  		net_random_ethaddr(dev->enetaddr);
160  		printf("\nWarning: %s (eth%d) using random MAC address - %pM\n",
161  		       dev->name, eth_number, dev->enetaddr);
162  #else
163  		printf("\nError: %s address not set.\n",
164  		       dev->name);
165  		return -EINVAL;
166  #endif
167  	}
168  
169  	if (dev->write_hwaddr && !eth_mac_skip(eth_number)) {
170  		if (!is_valid_ethaddr(dev->enetaddr)) {
171  			printf("\nError: %s address %pM illegal value\n",
172  			       dev->name, dev->enetaddr);
173  			return -EINVAL;
174  		}
175  
176  		ret = dev->write_hwaddr(dev);
177  		if (ret)
178  			printf("\nWarning: %s failed to set MAC address\n",
179  			       dev->name);
180  	}
181  
182  	return ret;
183  }
184  
185  int eth_register(struct eth_device *dev)
186  {
187  	struct eth_device *d;
188  	static int index;
189  
190  	assert(strlen(dev->name) < sizeof(dev->name));
191  
192  	if (!eth_devices) {
193  		eth_devices = dev;
194  		eth_current = dev;
195  		eth_current_changed();
196  	} else {
197  		for (d = eth_devices; d->next != eth_devices; d = d->next)
198  			;
199  		d->next = dev;
200  	}
201  
202  	dev->state = ETH_STATE_INIT;
203  	dev->next  = eth_devices;
204  	dev->index = index++;
205  
206  	return 0;
207  }
208  
209  int eth_unregister(struct eth_device *dev)
210  {
211  	struct eth_device *cur;
212  
213  	/* No device */
214  	if (!eth_devices)
215  		return -ENODEV;
216  
217  	for (cur = eth_devices; cur->next != eth_devices && cur->next != dev;
218  	     cur = cur->next)
219  		;
220  
221  	/* Device not found */
222  	if (cur->next != dev)
223  		return -ENODEV;
224  
225  	cur->next = dev->next;
226  
227  	if (eth_devices == dev)
228  		eth_devices = dev->next == eth_devices ? NULL : dev->next;
229  
230  	if (eth_current == dev) {
231  		eth_current = eth_devices;
232  		eth_current_changed();
233  	}
234  
235  	return 0;
236  }
237  
238  int eth_initialize(void)
239  {
240  	int num_devices = 0;
241  
242  	eth_devices = NULL;
243  	eth_current = NULL;
244  	eth_common_init();
245  	/*
246  	 * If board-specific initialization exists, call it.
247  	 * If not, call a CPU-specific one
248  	 */
249  	if (board_eth_init != __def_eth_init) {
250  		if (board_eth_init(gd->bd) < 0)
251  			printf("Board Net Initialization Failed\n");
252  	} else if (cpu_eth_init != __def_eth_init) {
253  		if (cpu_eth_init(gd->bd) < 0)
254  			printf("CPU Net Initialization Failed\n");
255  	} else {
256  		printf("Net Initialization Skipped\n");
257  	}
258  
259  	if (!eth_devices) {
260  		puts("No ethernet found.\n");
261  		bootstage_error(BOOTSTAGE_ID_NET_ETH_START);
262  	} else {
263  		struct eth_device *dev = eth_devices;
264  		char *ethprime = env_get("ethprime");
265  
266  		bootstage_mark(BOOTSTAGE_ID_NET_ETH_INIT);
267  		do {
268  			if (dev->index)
269  				puts(", ");
270  
271  			printf("%s", dev->name);
272  
273  			if (ethprime && strcmp(dev->name, ethprime) == 0) {
274  				eth_current = dev;
275  				puts(" [PRIME]");
276  			}
277  
278  			if (strchr(dev->name, ' '))
279  				puts("\nWarning: eth device name has a space!"
280  					"\n");
281  
282  			eth_write_hwaddr(dev, "eth", dev->index);
283  
284  			dev = dev->next;
285  			num_devices++;
286  		} while (dev != eth_devices);
287  
288  		eth_current_changed();
289  		putc('\n');
290  	}
291  
292  	return num_devices;
293  }
294  
295  #ifdef CONFIG_MCAST_TFTP
296  /* Multicast.
297   * mcast_addr: multicast ipaddr from which multicast Mac is made
298   * join: 1=join, 0=leave.
299   */
300  int eth_mcast_join(struct in_addr mcast_ip, int join)
301  {
302  	u8 mcast_mac[ARP_HLEN];
303  	if (!eth_current || !eth_current->mcast)
304  		return -1;
305  	mcast_mac[5] = htonl(mcast_ip.s_addr) & 0xff;
306  	mcast_mac[4] = (htonl(mcast_ip.s_addr)>>8) & 0xff;
307  	mcast_mac[3] = (htonl(mcast_ip.s_addr)>>16) & 0x7f;
308  	mcast_mac[2] = 0x5e;
309  	mcast_mac[1] = 0x0;
310  	mcast_mac[0] = 0x1;
311  	return eth_current->mcast(eth_current, mcast_mac, join);
312  }
313  
314  /* the 'way' for ethernet-CRC-32. Spliced in from Linux lib/crc32.c
315   * and this is the ethernet-crc method needed for TSEC -- and perhaps
316   * some other adapter -- hash tables
317   */
318  #define CRCPOLY_LE 0xedb88320
319  u32 ether_crc(size_t len, unsigned char const *p)
320  {
321  	int i;
322  	u32 crc;
323  	crc = ~0;
324  	while (len--) {
325  		crc ^= *p++;
326  		for (i = 0; i < 8; i++)
327  			crc = (crc >> 1) ^ ((crc & 1) ? CRCPOLY_LE : 0);
328  	}
329  	/* an reverse the bits, cuz of way they arrive -- last-first */
330  	crc = (crc >> 16) | (crc << 16);
331  	crc = (crc >> 8 & 0x00ff00ff) | (crc << 8 & 0xff00ff00);
332  	crc = (crc >> 4 & 0x0f0f0f0f) | (crc << 4 & 0xf0f0f0f0);
333  	crc = (crc >> 2 & 0x33333333) | (crc << 2 & 0xcccccccc);
334  	crc = (crc >> 1 & 0x55555555) | (crc << 1 & 0xaaaaaaaa);
335  	return crc;
336  }
337  
338  #endif
339  
340  
341  int eth_init(void)
342  {
343  	struct eth_device *old_current;
344  
345  	if (!eth_current) {
346  		puts("No ethernet found.\n");
347  		return -ENODEV;
348  	}
349  
350  	old_current = eth_current;
351  	do {
352  		debug("Trying %s\n", eth_current->name);
353  
354  		if (eth_current->init(eth_current, gd->bd) >= 0) {
355  			eth_current->state = ETH_STATE_ACTIVE;
356  
357  			return 0;
358  		}
359  		debug("FAIL\n");
360  
361  		eth_try_another(0);
362  	} while (old_current != eth_current);
363  
364  	return -ETIMEDOUT;
365  }
366  
367  void eth_halt(void)
368  {
369  	if (!eth_current)
370  		return;
371  
372  	eth_current->halt(eth_current);
373  
374  	eth_current->state = ETH_STATE_PASSIVE;
375  }
376  
377  int eth_is_active(struct eth_device *dev)
378  {
379  	return dev && dev->state == ETH_STATE_ACTIVE;
380  }
381  
382  int eth_send(void *packet, int length)
383  {
384  	if (!eth_current)
385  		return -ENODEV;
386  
387  	return eth_current->send(eth_current, packet, length);
388  }
389  
390  int eth_rx(void)
391  {
392  	if (!eth_current)
393  		return -ENODEV;
394  
395  	return eth_current->recv(eth_current);
396  }
397  
398  #ifdef CONFIG_API
399  static void eth_save_packet(void *packet, int length)
400  {
401  	char *p = packet;
402  	int i;
403  
404  	if ((eth_rcv_last+1) % PKTBUFSRX == eth_rcv_current)
405  		return;
406  
407  	if (PKTSIZE < length)
408  		return;
409  
410  	for (i = 0; i < length; i++)
411  		eth_rcv_bufs[eth_rcv_last].data[i] = p[i];
412  
413  	eth_rcv_bufs[eth_rcv_last].length = length;
414  	eth_rcv_last = (eth_rcv_last + 1) % PKTBUFSRX;
415  }
416  
417  int eth_receive(void *packet, int length)
418  {
419  	char *p = packet;
420  	void *pp = push_packet;
421  	int i;
422  
423  	if (eth_rcv_current == eth_rcv_last) {
424  		push_packet = eth_save_packet;
425  		eth_rx();
426  		push_packet = pp;
427  
428  		if (eth_rcv_current == eth_rcv_last)
429  			return -1;
430  	}
431  
432  	length = min(eth_rcv_bufs[eth_rcv_current].length, length);
433  
434  	for (i = 0; i < length; i++)
435  		p[i] = eth_rcv_bufs[eth_rcv_current].data[i];
436  
437  	eth_rcv_current = (eth_rcv_current + 1) % PKTBUFSRX;
438  	return length;
439  }
440  #endif /* CONFIG_API */
441