]> git.kernelconcepts.de Git - karo-tx-uboot.git/blob - net/eth.c
Merge branch 'u-boot-samsung/master' into 'u-boot-arm/master'
[karo-tx-uboot.git] / net / eth.c
1 /*
2  * (C) Copyright 2001-2010
3  * Wolfgang Denk, DENX Software Engineering, wd@denx.de.
4  *
5  * SPDX-License-Identifier:     GPL-2.0+
6  */
7
8 #include <common.h>
9 #include <command.h>
10 #include <net.h>
11 #include <miiphy.h>
12 #include <phy.h>
13
14 void eth_parse_enetaddr(const char *addr, uchar *enetaddr)
15 {
16         char *end;
17         int i;
18
19         for (i = 0; i < 6; ++i) {
20                 enetaddr[i] = addr ? simple_strtoul(addr, &end, 16) : 0;
21                 if (addr)
22                         addr = (*end) ? end + 1 : end;
23         }
24 }
25
26 int eth_getenv_enetaddr(char *name, uchar *enetaddr)
27 {
28         eth_parse_enetaddr(getenv(name), enetaddr);
29         return is_valid_ether_addr(enetaddr);
30 }
31
32 int eth_setenv_enetaddr(char *name, const uchar *enetaddr)
33 {
34         char buf[20];
35
36         sprintf(buf, "%pM", enetaddr);
37
38         return setenv(name, buf);
39 }
40
41 int eth_getenv_enetaddr_by_index(const char *base_name, int index,
42                                  uchar *enetaddr)
43 {
44         char enetvar[32];
45         sprintf(enetvar, index ? "%s%daddr" : "%saddr", base_name, index);
46         return eth_getenv_enetaddr(enetvar, enetaddr);
47 }
48
49 static inline int eth_setenv_enetaddr_by_index(const char *base_name, int index,
50                                  uchar *enetaddr)
51 {
52         char enetvar[32];
53         sprintf(enetvar, index ? "%s%daddr" : "%saddr", base_name, index);
54         return eth_setenv_enetaddr(enetvar, enetaddr);
55 }
56
57
58 static int eth_mac_skip(int index)
59 {
60         char enetvar[15];
61         char *skip_state;
62         sprintf(enetvar, index ? "eth%dmacskip" : "ethmacskip", index);
63         return ((skip_state = getenv(enetvar)) != NULL);
64 }
65
66 /*
67  * CPU and board-specific Ethernet initializations.  Aliased function
68  * signals caller to move on
69  */
70 static int __def_eth_init(bd_t *bis)
71 {
72         return -1;
73 }
74 int cpu_eth_init(bd_t *bis) __attribute__((weak, alias("__def_eth_init")));
75 int board_eth_init(bd_t *bis) __attribute__((weak, alias("__def_eth_init")));
76
77 #ifdef CONFIG_API
78 static struct {
79         uchar data[PKTSIZE];
80         int length;
81 } eth_rcv_bufs[PKTBUFSRX];
82
83 static unsigned int eth_rcv_current, eth_rcv_last;
84 #endif
85
86 static struct eth_device *eth_devices;
87 struct eth_device *eth_current;
88
89 struct eth_device *eth_get_dev_by_name(const char *devname)
90 {
91         struct eth_device *dev, *target_dev;
92
93         BUG_ON(devname == NULL);
94
95         if (!eth_devices)
96                 return NULL;
97
98         dev = eth_devices;
99         target_dev = NULL;
100         do {
101                 if (strcmp(devname, dev->name) == 0) {
102                         target_dev = dev;
103                         break;
104                 }
105                 dev = dev->next;
106         } while (dev != eth_devices);
107
108         return target_dev;
109 }
110
111 struct eth_device *eth_get_dev_by_index(int index)
112 {
113         struct eth_device *dev, *target_dev;
114
115         if (!eth_devices)
116                 return NULL;
117
118         dev = eth_devices;
119         target_dev = NULL;
120         do {
121                 if (dev->index == index) {
122                         target_dev = dev;
123                         break;
124                 }
125                 dev = dev->next;
126         } while (dev != eth_devices);
127
128         return target_dev;
129 }
130
131 int eth_get_dev_index(void)
132 {
133         if (!eth_current)
134                 return -1;
135
136         return eth_current->index;
137 }
138
139 static void eth_current_changed(void)
140 {
141         char *act = getenv("ethact");
142         /* update current ethernet name */
143         if (eth_current) {
144                 if (act == NULL || strcmp(act, eth_current->name) != 0)
145                         setenv("ethact", eth_current->name);
146         }
147         /*
148          * remove the variable completely if there is no active
149          * interface
150          */
151         else if (act != NULL)
152                 setenv("ethact", NULL);
153 }
154
155 int eth_write_hwaddr(struct eth_device *dev, const char *base_name,
156                    int eth_number)
157 {
158         unsigned char env_enetaddr[6];
159         int ret = 0;
160
161         eth_getenv_enetaddr_by_index(base_name, eth_number, env_enetaddr);
162
163         if (memcmp(env_enetaddr, "\0\0\0\0\0\0", 6)) {
164                 if (memcmp(dev->enetaddr, "\0\0\0\0\0\0", 6) &&
165                                 memcmp(dev->enetaddr, env_enetaddr, 6)) {
166                         printf("\nWarning: %s MAC addresses don't match:\n",
167                                 dev->name);
168                         printf("Address in SROM is         %pM\n",
169                                 dev->enetaddr);
170                         printf("Address in environment is  %pM\n",
171                                 env_enetaddr);
172                 }
173
174                 memcpy(dev->enetaddr, env_enetaddr, 6);
175         } else if (is_valid_ether_addr(dev->enetaddr)) {
176                 eth_setenv_enetaddr_by_index(base_name, eth_number,
177                                              dev->enetaddr);
178                 printf("\nWarning: %s using MAC address from net device\n",
179                         dev->name);
180         }
181
182         if (dev->write_hwaddr &&
183                         !eth_mac_skip(eth_number)) {
184                 if (!is_valid_ether_addr(dev->enetaddr))
185                         return -1;
186
187                 ret = dev->write_hwaddr(dev);
188         }
189
190         return ret;
191 }
192
193 int eth_register(struct eth_device *dev)
194 {
195         struct eth_device *d;
196         static int index;
197
198         assert(strlen(dev->name) < sizeof(dev->name));
199
200         if (!eth_devices) {
201                 eth_current = eth_devices = dev;
202                 eth_current_changed();
203         } else {
204                 for (d = eth_devices; d->next != eth_devices; d = d->next)
205                         ;
206                 d->next = dev;
207         }
208
209         dev->state = ETH_STATE_INIT;
210         dev->next  = eth_devices;
211         dev->index = index++;
212
213         return 0;
214 }
215
216 int eth_unregister(struct eth_device *dev)
217 {
218         struct eth_device *cur;
219
220         /* No device */
221         if (!eth_devices)
222                 return -1;
223
224         for (cur = eth_devices; cur->next != eth_devices && cur->next != dev;
225              cur = cur->next)
226                 ;
227
228         /* Device not found */
229         if (cur->next != dev)
230                 return -1;
231
232         cur->next = dev->next;
233
234         if (eth_devices == dev)
235                 eth_devices = dev->next == eth_devices ? NULL : dev->next;
236
237         if (eth_current == dev) {
238                 eth_current = eth_devices;
239                 eth_current_changed();
240         }
241
242         return 0;
243 }
244
245 static void eth_env_init(bd_t *bis)
246 {
247         const char *s;
248
249         if ((s = getenv("bootfile")) != NULL)
250                 copy_filename(BootFile, s, sizeof(BootFile));
251 }
252
253 int eth_initialize(bd_t *bis)
254 {
255         int num_devices = 0;
256         eth_devices = NULL;
257         eth_current = NULL;
258
259         bootstage_mark(BOOTSTAGE_ID_NET_ETH_START);
260 #if defined(CONFIG_MII) || defined(CONFIG_CMD_MII) || defined(CONFIG_PHYLIB)
261         miiphy_init();
262 #endif
263
264 #ifdef CONFIG_PHYLIB
265         phy_init();
266 #endif
267
268         eth_env_init(bis);
269
270         /*
271          * If board-specific initialization exists, call it.
272          * If not, call a CPU-specific one
273          */
274         if (board_eth_init != __def_eth_init) {
275                 if (board_eth_init(bis) < 0)
276                         printf("Board Net Initialization Failed\n");
277         } else if (cpu_eth_init != __def_eth_init) {
278                 if (cpu_eth_init(bis) < 0)
279                         printf("CPU Net Initialization Failed\n");
280         } else
281                 printf("Net Initialization Skipped\n");
282
283         if (!eth_devices) {
284                 puts("No ethernet found.\n");
285                 bootstage_error(BOOTSTAGE_ID_NET_ETH_START);
286         } else {
287                 struct eth_device *dev = eth_devices;
288                 char *ethprime = getenv("ethprime");
289
290                 bootstage_mark(BOOTSTAGE_ID_NET_ETH_INIT);
291                 do {
292                         if (dev->index)
293                                 puts(", ");
294
295                         printf("%s", dev->name);
296
297                         if (ethprime && strcmp(dev->name, ethprime) == 0) {
298                                 eth_current = dev;
299                                 puts(" [PRIME]");
300                         }
301
302                         if (strchr(dev->name, ' '))
303                                 puts("\nWarning: eth device name has a space!"
304                                         "\n");
305
306                         if (eth_write_hwaddr(dev, "eth", dev->index))
307                                 puts("\nWarning: failed to set MAC address\n");
308
309                         dev = dev->next;
310                         num_devices++;
311                 } while (dev != eth_devices);
312
313                 eth_current_changed();
314                 putc('\n');
315         }
316
317         return num_devices;
318 }
319
320 #ifdef CONFIG_MCAST_TFTP
321 /* Multicast.
322  * mcast_addr: multicast ipaddr from which multicast Mac is made
323  * join: 1=join, 0=leave.
324  */
325 int eth_mcast_join(IPaddr_t mcast_ip, u8 join)
326 {
327         u8 mcast_mac[6];
328         if (!eth_current || !eth_current->mcast)
329                 return -1;
330         mcast_mac[5] = htonl(mcast_ip) & 0xff;
331         mcast_mac[4] = (htonl(mcast_ip)>>8) & 0xff;
332         mcast_mac[3] = (htonl(mcast_ip)>>16) & 0x7f;
333         mcast_mac[2] = 0x5e;
334         mcast_mac[1] = 0x0;
335         mcast_mac[0] = 0x1;
336         return eth_current->mcast(eth_current, mcast_mac, join);
337 }
338
339 /* the 'way' for ethernet-CRC-32. Spliced in from Linux lib/crc32.c
340  * and this is the ethernet-crc method needed for TSEC -- and perhaps
341  * some other adapter -- hash tables
342  */
343 #define CRCPOLY_LE 0xedb88320
344 u32 ether_crc(size_t len, unsigned char const *p)
345 {
346         int i;
347         u32 crc;
348         crc = ~0;
349         while (len--) {
350                 crc ^= *p++;
351                 for (i = 0; i < 8; i++)
352                         crc = (crc >> 1) ^ ((crc & 1) ? CRCPOLY_LE : 0);
353         }
354         /* an reverse the bits, cuz of way they arrive -- last-first */
355         crc = (crc >> 16) | (crc << 16);
356         crc = (crc >> 8 & 0x00ff00ff) | (crc << 8 & 0xff00ff00);
357         crc = (crc >> 4 & 0x0f0f0f0f) | (crc << 4 & 0xf0f0f0f0);
358         crc = (crc >> 2 & 0x33333333) | (crc << 2 & 0xcccccccc);
359         crc = (crc >> 1 & 0x55555555) | (crc << 1 & 0xaaaaaaaa);
360         return crc;
361 }
362
363 #endif
364
365
366 int eth_init(bd_t *bis)
367 {
368         struct eth_device *old_current, *dev;
369
370         if (!eth_current) {
371                 puts("No ethernet found.\n");
372                 return -1;
373         }
374
375         /* Sync environment with network devices */
376         dev = eth_devices;
377         do {
378                 uchar env_enetaddr[6];
379
380                 if (eth_getenv_enetaddr_by_index("eth", dev->index,
381                                                  env_enetaddr))
382                         memcpy(dev->enetaddr, env_enetaddr, 6);
383
384                 dev = dev->next;
385         } while (dev != eth_devices);
386
387         old_current = eth_current;
388         do {
389                 debug("Trying %s\n", eth_current->name);
390
391                 if (eth_current->init(eth_current, bis) >= 0) {
392                         eth_current->state = ETH_STATE_ACTIVE;
393
394                         return 0;
395                 }
396                 debug("FAIL\n");
397
398                 eth_try_another(0);
399         } while (old_current != eth_current);
400
401         return -1;
402 }
403
404 void eth_halt(void)
405 {
406         if (!eth_current)
407                 return;
408
409         eth_current->halt(eth_current);
410
411         eth_current->state = ETH_STATE_PASSIVE;
412 }
413
414 int eth_send(void *packet, int length)
415 {
416         if (!eth_current)
417                 return -1;
418
419         return eth_current->send(eth_current, packet, length);
420 }
421
422 int eth_rx(void)
423 {
424         if (!eth_current)
425                 return -1;
426
427         return eth_current->recv(eth_current);
428 }
429
430 #ifdef CONFIG_API
431 static void eth_save_packet(void *packet, int length)
432 {
433         char *p = packet;
434         int i;
435
436         if ((eth_rcv_last+1) % PKTBUFSRX == eth_rcv_current)
437                 return;
438
439         if (PKTSIZE < length)
440                 return;
441
442         for (i = 0; i < length; i++)
443                 eth_rcv_bufs[eth_rcv_last].data[i] = p[i];
444
445         eth_rcv_bufs[eth_rcv_last].length = length;
446         eth_rcv_last = (eth_rcv_last + 1) % PKTBUFSRX;
447 }
448
449 int eth_receive(void *packet, int length)
450 {
451         char *p = packet;
452         void *pp = push_packet;
453         int i;
454
455         if (eth_rcv_current == eth_rcv_last) {
456                 push_packet = eth_save_packet;
457                 eth_rx();
458                 push_packet = pp;
459
460                 if (eth_rcv_current == eth_rcv_last)
461                         return -1;
462         }
463
464         length = min(eth_rcv_bufs[eth_rcv_current].length, length);
465
466         for (i = 0; i < length; i++)
467                 p[i] = eth_rcv_bufs[eth_rcv_current].data[i];
468
469         eth_rcv_current = (eth_rcv_current + 1) % PKTBUFSRX;
470         return length;
471 }
472 #endif /* CONFIG_API */
473
474 void eth_try_another(int first_restart)
475 {
476         static struct eth_device *first_failed;
477         char *ethrotate;
478
479         /*
480          * Do not rotate between network interfaces when
481          * 'ethrotate' variable is set to 'no'.
482          */
483         ethrotate = getenv("ethrotate");
484         if ((ethrotate != NULL) && (strcmp(ethrotate, "no") == 0))
485                 return;
486
487         if (!eth_current)
488                 return;
489
490         if (first_restart)
491                 first_failed = eth_current;
492
493         eth_current = eth_current->next;
494
495         eth_current_changed();
496
497         if (first_failed == eth_current)
498                 NetRestartWrap = 1;
499 }
500
501 void eth_set_current(void)
502 {
503         static char *act;
504         static int  env_changed_id;
505         struct eth_device *old_current;
506         int     env_id;
507
508         if (!eth_current)       /* XXX no current */
509                 return;
510
511         env_id = get_env_id();
512         if ((act == NULL) || (env_changed_id != env_id)) {
513                 act = getenv("ethact");
514                 env_changed_id = env_id;
515         }
516         if (act != NULL) {
517                 old_current = eth_current;
518                 do {
519                         if (strcmp(eth_current->name, act) == 0)
520                                 return;
521                         eth_current = eth_current->next;
522                 } while (old_current != eth_current);
523         }
524
525         eth_current_changed();
526 }
527
528 char *eth_get_name(void)
529 {
530         return eth_current ? eth_current->name : "unknown";
531 }