blob: 1abf0274b489d98b79ef94885682fab0f18095e6 [file] [log] [blame]
wdenkc6097192002-11-03 00:24:07 +00001/*
Joe Hershbergerc7eceaf2015-03-22 17:09:10 -05002 * (C) Copyright 2001-2015
wdenkc6097192002-11-03 00:24:07 +00003 * Wolfgang Denk, DENX Software Engineering, wd@denx.de.
Joe Hershbergerc7eceaf2015-03-22 17:09:10 -05004 * Joe Hershberger, National Instruments
wdenkc6097192002-11-03 00:24:07 +00005 *
Wolfgang Denkd79de1d2013-07-08 09:37:19 +02006 * SPDX-License-Identifier: GPL-2.0+
wdenkc6097192002-11-03 00:24:07 +00007 */
8
9#include <common.h>
10#include <command.h>
Joe Hershbergerc7eceaf2015-03-22 17:09:10 -050011#include <dm.h>
wdenkc6097192002-11-03 00:24:07 +000012#include <net.h>
Marian Balakowiczcbdd1c82005-11-30 18:06:04 +010013#include <miiphy.h>
Andy Flemingaecf6fc2011-04-08 02:10:27 -050014#include <phy.h>
Pavel Machek222981f2014-07-13 10:27:02 +020015#include <asm/errno.h>
Joe Hershbergerc7eceaf2015-03-22 17:09:10 -050016#include <dm/device-internal.h>
wdenkc6097192002-11-03 00:24:07 +000017
Joe Hershberger3dbe17e2015-03-22 17:09:06 -050018DECLARE_GLOBAL_DATA_PTR;
19
Mike Frysingerc34319c2009-01-29 19:43:44 -050020void eth_parse_enetaddr(const char *addr, uchar *enetaddr)
21{
22 char *end;
23 int i;
24
25 for (i = 0; i < 6; ++i) {
26 enetaddr[i] = addr ? simple_strtoul(addr, &end, 16) : 0;
27 if (addr)
28 addr = (*end) ? end + 1 : end;
29 }
30}
31
32int eth_getenv_enetaddr(char *name, uchar *enetaddr)
33{
34 eth_parse_enetaddr(getenv(name), enetaddr);
35 return is_valid_ether_addr(enetaddr);
36}
37
38int eth_setenv_enetaddr(char *name, const uchar *enetaddr)
39{
40 char buf[20];
41
42 sprintf(buf, "%pM", enetaddr);
43
44 return setenv(name, buf);
45}
Mike Frysingere129d562009-07-15 21:31:28 -040046
Simon Glass62b36c92011-06-13 16:13:10 -070047int eth_getenv_enetaddr_by_index(const char *base_name, int index,
48 uchar *enetaddr)
Mike Frysingere129d562009-07-15 21:31:28 -040049{
50 char enetvar[32];
Simon Glass62b36c92011-06-13 16:13:10 -070051 sprintf(enetvar, index ? "%s%daddr" : "%saddr", base_name, index);
Mike Frysingere129d562009-07-15 21:31:28 -040052 return eth_getenv_enetaddr(enetvar, enetaddr);
53}
Mike Frysingerc34319c2009-01-29 19:43:44 -050054
Joe Hershberger6c6fe5f2012-07-10 16:23:22 -050055static inline int eth_setenv_enetaddr_by_index(const char *base_name, int index,
Rob Herringc2938c02012-04-14 18:06:49 +000056 uchar *enetaddr)
57{
58 char enetvar[32];
59 sprintf(enetvar, index ? "%s%daddr" : "%saddr", base_name, index);
60 return eth_setenv_enetaddr(enetvar, enetaddr);
61}
62
Joe Hershbergere1e8a8c2015-03-22 17:09:03 -050063static void eth_env_init(void)
64{
65 const char *s;
66
67 s = getenv("bootfile");
68 if (s != NULL)
69 copy_filename(BootFile, s, sizeof(BootFile));
70}
Rob Herringc2938c02012-04-14 18:06:49 +000071
Ben Warren6db991a2010-04-26 11:11:46 -070072static int eth_mac_skip(int index)
73{
74 char enetvar[15];
75 char *skip_state;
76 sprintf(enetvar, index ? "eth%dmacskip" : "ethmacskip", index);
77 return ((skip_state = getenv(enetvar)) != NULL);
78}
79
Joe Hershbergere1e8a8c2015-03-22 17:09:03 -050080static void eth_current_changed(void);
81
Joe Hershbergerc7eceaf2015-03-22 17:09:10 -050082#ifdef CONFIG_DM_ETH
83/**
84 * struct eth_device_priv - private structure for each Ethernet device
85 *
86 * @state: The state of the Ethernet MAC driver (defined by enum eth_state_t)
87 */
88struct eth_device_priv {
89 enum eth_state_t state;
90};
91
92/**
93 * struct eth_uclass_priv - The structure attached to the uclass itself
94 *
95 * @current: The Ethernet device that the network functions are using
96 */
97struct eth_uclass_priv {
98 struct udevice *current;
99};
100
101static struct eth_uclass_priv *eth_get_uclass_priv(void)
102{
103 struct uclass *uc;
104
105 uclass_get(UCLASS_ETH, &uc);
106 assert(uc);
107 return uc->priv;
108}
109
110static void eth_set_current_to_next(void)
111{
112 struct eth_uclass_priv *uc_priv;
113
114 uc_priv = eth_get_uclass_priv();
115 if (uc_priv->current)
116 uclass_next_device(&uc_priv->current);
117 if (!uc_priv->current)
118 uclass_first_device(UCLASS_ETH, &uc_priv->current);
119}
120
121struct udevice *eth_get_dev(void)
122{
123 struct eth_uclass_priv *uc_priv;
124
125 uc_priv = eth_get_uclass_priv();
126 if (!uc_priv->current)
127 uclass_first_device(UCLASS_ETH,
128 &uc_priv->current);
129 return uc_priv->current;
130}
131
132static void eth_set_dev(struct udevice *dev)
133{
134 device_probe(dev);
135 eth_get_uclass_priv()->current = dev;
136}
137
138unsigned char *eth_get_ethaddr(void)
139{
140 struct eth_pdata *pdata;
141
142 if (eth_get_dev()) {
143 pdata = eth_get_dev()->platdata;
144 return pdata->enetaddr;
145 }
146
147 return NULL;
148}
149
150/* Set active state without calling start on the driver */
151int eth_init_state_only(void)
152{
153 struct udevice *current;
154 struct eth_device_priv *priv;
155
156 current = eth_get_dev();
157 if (!current || !device_active(current))
158 return -EINVAL;
159
160 priv = current->uclass_priv;
161 priv->state = ETH_STATE_ACTIVE;
162
163 return 0;
164}
165
166/* Set passive state without calling stop on the driver */
167void eth_halt_state_only(void)
168{
169 struct udevice *current;
170 struct eth_device_priv *priv;
171
172 current = eth_get_dev();
173 if (!current || !device_active(current))
174 return;
175
176 priv = current->uclass_priv;
177 priv->state = ETH_STATE_PASSIVE;
178}
179
180int eth_get_dev_index(void)
181{
182 if (eth_get_dev())
183 return eth_get_dev()->seq;
184 return -1;
185}
186
187int eth_init(void)
188{
189 struct udevice *current;
190 struct udevice *old_current;
191
192 current = eth_get_dev();
193 if (!current) {
194 printf("No ethernet found.\n");
195 return -ENODEV;
196 }
197
198 old_current = current;
199 do {
200 debug("Trying %s\n", current->name);
201
202 if (device_active(current)) {
203 uchar env_enetaddr[6];
204 struct eth_pdata *pdata = current->platdata;
205
206 /* Sync environment with network device */
207 if (eth_getenv_enetaddr_by_index("eth", current->seq,
208 env_enetaddr))
209 memcpy(pdata->enetaddr, env_enetaddr, 6);
210 else
211 memset(pdata->enetaddr, 0, 6);
212
213 if (eth_get_ops(current)->start(current) >= 0) {
214 struct eth_device_priv *priv =
215 current->uclass_priv;
216
217 priv->state = ETH_STATE_ACTIVE;
218 return 0;
219 }
220 }
221 debug("FAIL\n");
222
223 /* This will ensure the new "current" attempted to probe */
224 eth_try_another(0);
225 current = eth_get_dev();
226 } while (old_current != current);
227
228 return -ENODEV;
229}
230
231void eth_halt(void)
232{
233 struct udevice *current;
234 struct eth_device_priv *priv;
235
236 current = eth_get_dev();
237 if (!current || !device_active(current))
238 return;
239
240 eth_get_ops(current)->stop(current);
241 priv = current->uclass_priv;
242 priv->state = ETH_STATE_PASSIVE;
243}
244
245int eth_send(void *packet, int length)
246{
247 struct udevice *current;
248
249 current = eth_get_dev();
250 if (!current)
251 return -ENODEV;
252
253 if (!device_active(current))
254 return -EINVAL;
255
256 return eth_get_ops(current)->send(current, packet, length);
257}
258
259int eth_rx(void)
260{
261 struct udevice *current;
262
263 current = eth_get_dev();
264 if (!current)
265 return -ENODEV;
266
267 if (!device_active(current))
268 return -EINVAL;
269
270 return eth_get_ops(current)->recv(current);
271}
272
273static int eth_write_hwaddr(struct udevice *dev)
274{
275 struct eth_pdata *pdata = dev->platdata;
276 int ret = 0;
277
278 if (!dev || !device_active(dev))
279 return -EINVAL;
280
281 /* seq is valid since the device is active */
282 if (eth_get_ops(dev)->write_hwaddr && !eth_mac_skip(dev->seq)) {
283 if (!is_valid_ether_addr(pdata->enetaddr)) {
284 printf("\nError: %s address %pM illegal value\n",
285 dev->name, pdata->enetaddr);
286 return -EINVAL;
287 }
288
289 ret = eth_get_ops(dev)->write_hwaddr(dev);
290 if (ret)
291 printf("\nWarning: %s failed to set MAC address\n",
292 dev->name);
293 }
294
295 return ret;
296}
297
298int eth_initialize(void)
299{
300 int num_devices = 0;
301 struct udevice *dev;
302
303 bootstage_mark(BOOTSTAGE_ID_NET_ETH_START);
304 eth_env_init();
305
306 /*
307 * Devices need to write the hwaddr even if not started so that Linux
308 * will have access to the hwaddr that u-boot stored for the device.
309 * This is accomplished by attempting to probe each device and calling
310 * their write_hwaddr() operation.
311 */
312 uclass_first_device(UCLASS_ETH, &dev);
313 if (!dev) {
314 printf("No ethernet found.\n");
315 bootstage_error(BOOTSTAGE_ID_NET_ETH_START);
316 } else {
317 bootstage_mark(BOOTSTAGE_ID_NET_ETH_INIT);
318 do {
319 if (num_devices)
320 printf(", ");
321
322 printf("eth%d: %s", dev->seq, dev->name);
323
324 eth_write_hwaddr(dev);
325
326 uclass_next_device(&dev);
327 num_devices++;
328 } while (dev);
329
330 putc('\n');
331 }
332
333 return num_devices;
334}
335
336static int eth_post_bind(struct udevice *dev)
337{
338 if (strchr(dev->name, ' ')) {
339 printf("\nError: eth device name \"%s\" has a space!\n",
340 dev->name);
341 return -EINVAL;
342 }
343
344 return 0;
345}
346
347static int eth_pre_unbind(struct udevice *dev)
348{
349 /* Don't hang onto a pointer that is going away */
350 if (dev == eth_get_uclass_priv()->current)
351 eth_set_dev(NULL);
352
353 return 0;
354}
355
356static int eth_post_probe(struct udevice *dev)
357{
358 struct eth_device_priv *priv = dev->uclass_priv;
359 struct eth_pdata *pdata = dev->platdata;
360 unsigned char env_enetaddr[6];
361
362 priv->state = ETH_STATE_INIT;
363
364 /* Check if the device has a MAC address in ROM */
365 if (eth_get_ops(dev)->read_rom_hwaddr)
366 eth_get_ops(dev)->read_rom_hwaddr(dev);
367
368 eth_getenv_enetaddr_by_index("eth", dev->seq, env_enetaddr);
369 if (!is_zero_ether_addr(env_enetaddr)) {
370 if (!is_zero_ether_addr(pdata->enetaddr) &&
371 memcmp(pdata->enetaddr, env_enetaddr, 6)) {
372 printf("\nWarning: %s MAC addresses don't match:\n",
373 dev->name);
374 printf("Address in SROM is %pM\n",
375 pdata->enetaddr);
376 printf("Address in environment is %pM\n",
377 env_enetaddr);
378 }
379
380 /* Override the ROM MAC address */
381 memcpy(pdata->enetaddr, env_enetaddr, 6);
382 } else if (is_valid_ether_addr(pdata->enetaddr)) {
383 eth_setenv_enetaddr_by_index("eth", dev->seq, pdata->enetaddr);
384 printf("\nWarning: %s using MAC address from ROM\n",
385 dev->name);
386 } else if (is_zero_ether_addr(pdata->enetaddr)) {
387 printf("\nError: %s address not set.\n",
388 dev->name);
389 return -EINVAL;
390 }
391
392 return 0;
393}
394
395static int eth_pre_remove(struct udevice *dev)
396{
397 eth_get_ops(dev)->stop(dev);
398
399 return 0;
400}
401
402UCLASS_DRIVER(eth) = {
403 .name = "eth",
404 .id = UCLASS_ETH,
405 .post_bind = eth_post_bind,
406 .pre_unbind = eth_pre_unbind,
407 .post_probe = eth_post_probe,
408 .pre_remove = eth_pre_remove,
409 .priv_auto_alloc_size = sizeof(struct eth_uclass_priv),
410 .per_device_auto_alloc_size = sizeof(struct eth_device_priv),
411};
412#endif
413
414#ifndef CONFIG_DM_ETH
Ben Warrend448a492008-06-23 22:57:27 -0700415/*
416 * CPU and board-specific Ethernet initializations. Aliased function
417 * signals caller to move on
418 */
419static int __def_eth_init(bd_t *bis)
420{
421 return -1;
422}
Peter Tyser21d2cd22009-04-20 11:08:46 -0500423int cpu_eth_init(bd_t *bis) __attribute__((weak, alias("__def_eth_init")));
424int board_eth_init(bd_t *bis) __attribute__((weak, alias("__def_eth_init")));
Ben Warrend448a492008-06-23 22:57:27 -0700425
Rafal Jaworowski1f2c9a42007-12-27 18:19:02 +0100426#ifdef CONFIG_API
Rafal Jaworowski1f2c9a42007-12-27 18:19:02 +0100427static struct {
428 uchar data[PKTSIZE];
429 int length;
430} eth_rcv_bufs[PKTBUFSRX];
431
Joe Hershberger5956ded2012-05-15 08:59:07 +0000432static unsigned int eth_rcv_current, eth_rcv_last;
Rafal Jaworowski1f2c9a42007-12-27 18:19:02 +0100433#endif
434
Joe Hershberger9f374062012-08-03 10:59:08 +0000435static struct eth_device *eth_devices;
436struct eth_device *eth_current;
wdenkc6097192002-11-03 00:24:07 +0000437
Joe Hershbergere1e8a8c2015-03-22 17:09:03 -0500438static void eth_set_current_to_next(void)
439{
440 eth_current = eth_current->next;
441}
442
Ben Warren97824d62010-07-29 12:56:11 -0700443struct eth_device *eth_get_dev_by_name(const char *devname)
Marian Balakowiczaab8c492005-10-28 22:30:33 +0200444{
445 struct eth_device *dev, *target_dev;
446
Helmut Raiger153bf3a2011-08-22 00:17:17 +0000447 BUG_ON(devname == NULL);
448
Marian Balakowiczaab8c492005-10-28 22:30:33 +0200449 if (!eth_devices)
450 return NULL;
451
452 dev = eth_devices;
453 target_dev = NULL;
454 do {
455 if (strcmp(devname, dev->name) == 0) {
456 target_dev = dev;
457 break;
458 }
459 dev = dev->next;
460 } while (dev != eth_devices);
461
462 return target_dev;
463}
464
Andy Fleminge7fd34b2009-02-11 15:07:24 -0600465struct eth_device *eth_get_dev_by_index(int index)
466{
467 struct eth_device *dev, *target_dev;
Andy Fleminge7fd34b2009-02-11 15:07:24 -0600468
469 if (!eth_devices)
470 return NULL;
471
472 dev = eth_devices;
473 target_dev = NULL;
474 do {
Michael Walle391da4c2011-10-27 11:31:35 +0000475 if (dev->index == index) {
Andy Fleminge7fd34b2009-02-11 15:07:24 -0600476 target_dev = dev;
477 break;
478 }
479 dev = dev->next;
Andy Fleminge7fd34b2009-02-11 15:07:24 -0600480 } while (dev != eth_devices);
481
482 return target_dev;
483}
484
Joe Hershberger5956ded2012-05-15 08:59:07 +0000485int eth_get_dev_index(void)
wdenkc6097192002-11-03 00:24:07 +0000486{
Joe Hershberger5956ded2012-05-15 08:59:07 +0000487 if (!eth_current)
Michael Walle391da4c2011-10-27 11:31:35 +0000488 return -1;
wdenkc6097192002-11-03 00:24:07 +0000489
Michael Walle391da4c2011-10-27 11:31:35 +0000490 return eth_current->index;
wdenkc6097192002-11-03 00:24:07 +0000491}
492
Simon Glass62b36c92011-06-13 16:13:10 -0700493int eth_write_hwaddr(struct eth_device *dev, const char *base_name,
494 int eth_number)
495{
496 unsigned char env_enetaddr[6];
497 int ret = 0;
498
Eric Miaoae97ef62012-01-18 22:56:33 +0000499 eth_getenv_enetaddr_by_index(base_name, eth_number, env_enetaddr);
Simon Glass62b36c92011-06-13 16:13:10 -0700500
Joe Hershbergerca044ad2015-03-22 17:09:01 -0500501 if (!is_zero_ether_addr(env_enetaddr)) {
502 if (!is_zero_ether_addr(dev->enetaddr) &&
503 memcmp(dev->enetaddr, env_enetaddr, 6)) {
Simon Glass62b36c92011-06-13 16:13:10 -0700504 printf("\nWarning: %s MAC addresses don't match:\n",
505 dev->name);
506 printf("Address in SROM is %pM\n",
507 dev->enetaddr);
508 printf("Address in environment is %pM\n",
509 env_enetaddr);
510 }
511
512 memcpy(dev->enetaddr, env_enetaddr, 6);
Rob Herringc2938c02012-04-14 18:06:49 +0000513 } else if (is_valid_ether_addr(dev->enetaddr)) {
514 eth_setenv_enetaddr_by_index(base_name, eth_number,
515 dev->enetaddr);
516 printf("\nWarning: %s using MAC address from net device\n",
517 dev->name);
Joe Hershbergerca044ad2015-03-22 17:09:01 -0500518 } else if (is_zero_ether_addr(dev->enetaddr)) {
Pavel Machek222981f2014-07-13 10:27:02 +0200519 printf("\nError: %s address not set.\n",
520 dev->name);
521 return -EINVAL;
Simon Glass62b36c92011-06-13 16:13:10 -0700522 }
523
Pavel Machek222981f2014-07-13 10:27:02 +0200524 if (dev->write_hwaddr && !eth_mac_skip(eth_number)) {
525 if (!is_valid_ether_addr(dev->enetaddr)) {
526 printf("\nError: %s address %pM illegal value\n",
527 dev->name, dev->enetaddr);
528 return -EINVAL;
529 }
Benoît Thébaudeau050219e2012-08-10 07:56:21 +0000530
Simon Glass62b36c92011-06-13 16:13:10 -0700531 ret = dev->write_hwaddr(dev);
Pavel Machek222981f2014-07-13 10:27:02 +0200532 if (ret)
533 printf("\nWarning: %s failed to set MAC address\n", dev->name);
Benoît Thébaudeau050219e2012-08-10 07:56:21 +0000534 }
Simon Glass62b36c92011-06-13 16:13:10 -0700535
536 return ret;
537}
538
Simon Glass1e9961d2011-02-16 11:14:33 -0800539int eth_register(struct eth_device *dev)
540{
541 struct eth_device *d;
Joe Hershberger5956ded2012-05-15 08:59:07 +0000542 static int index;
Michal Simek5b4945b2011-08-29 23:30:13 +0000543
Mike Frysinger6b300dc2011-11-10 14:11:04 +0000544 assert(strlen(dev->name) < sizeof(dev->name));
Michal Simek5b4945b2011-08-29 23:30:13 +0000545
Simon Glass1e9961d2011-02-16 11:14:33 -0800546 if (!eth_devices) {
547 eth_current = eth_devices = dev;
548 eth_current_changed();
wdenkc6097192002-11-03 00:24:07 +0000549 } else {
Joe Hershberger5956ded2012-05-15 08:59:07 +0000550 for (d = eth_devices; d->next != eth_devices; d = d->next)
Detlev Zundeld46bcd32010-03-31 17:56:08 +0200551 ;
wdenkc6097192002-11-03 00:24:07 +0000552 d->next = dev;
553 }
554
555 dev->state = ETH_STATE_INIT;
556 dev->next = eth_devices;
Michael Walle391da4c2011-10-27 11:31:35 +0000557 dev->index = index++;
wdenkc6097192002-11-03 00:24:07 +0000558
559 return 0;
560}
561
Vincent Palatin451be502012-01-09 08:32:36 +0000562int eth_unregister(struct eth_device *dev)
563{
564 struct eth_device *cur;
565
566 /* No device */
567 if (!eth_devices)
Joe Hershbergerd0ce3412015-03-22 17:09:04 -0500568 return -ENODEV;
Vincent Palatin451be502012-01-09 08:32:36 +0000569
570 for (cur = eth_devices; cur->next != eth_devices && cur->next != dev;
571 cur = cur->next)
572 ;
573
574 /* Device not found */
575 if (cur->next != dev)
Joe Hershbergerd0ce3412015-03-22 17:09:04 -0500576 return -ENODEV;
Vincent Palatin451be502012-01-09 08:32:36 +0000577
578 cur->next = dev->next;
579
580 if (eth_devices == dev)
581 eth_devices = dev->next == eth_devices ? NULL : dev->next;
582
583 if (eth_current == dev) {
584 eth_current = eth_devices;
585 eth_current_changed();
586 }
587
588 return 0;
589}
590
Joe Hershberger3dbe17e2015-03-22 17:09:06 -0500591int eth_initialize(void)
wdenkc6097192002-11-03 00:24:07 +0000592{
Michael Walle391da4c2011-10-27 11:31:35 +0000593 int num_devices = 0;
wdenkc6097192002-11-03 00:24:07 +0000594 eth_devices = NULL;
595 eth_current = NULL;
596
Simon Glass0169e6b2012-02-13 13:51:18 +0000597 bootstage_mark(BOOTSTAGE_ID_NET_ETH_START);
Alexey Brodkin37ecfa12014-01-10 19:58:11 +0400598#if defined(CONFIG_MII) || defined(CONFIG_CMD_MII) || defined(CONFIG_PHYLIB)
Marian Balakowiczcbdd1c82005-11-30 18:06:04 +0100599 miiphy_init();
600#endif
Andy Flemingaecf6fc2011-04-08 02:10:27 -0500601
602#ifdef CONFIG_PHYLIB
603 phy_init();
604#endif
605
Joe Hershbergere1e8a8c2015-03-22 17:09:03 -0500606 eth_env_init();
Mike Frysinger36fe7db2012-04-04 18:53:41 +0000607
Ben Warrencfdc8e02010-08-31 23:05:04 -0700608 /*
609 * If board-specific initialization exists, call it.
610 * If not, call a CPU-specific one
611 */
612 if (board_eth_init != __def_eth_init) {
Joe Hershberger3dbe17e2015-03-22 17:09:06 -0500613 if (board_eth_init(gd->bd) < 0)
Ben Warrencfdc8e02010-08-31 23:05:04 -0700614 printf("Board Net Initialization Failed\n");
615 } else if (cpu_eth_init != __def_eth_init) {
Joe Hershberger3dbe17e2015-03-22 17:09:06 -0500616 if (cpu_eth_init(gd->bd) < 0)
Ben Warrencfdc8e02010-08-31 23:05:04 -0700617 printf("CPU Net Initialization Failed\n");
618 } else
619 printf("Net Initialization Skipped\n");
Marian Balakowiczcbdd1c82005-11-30 18:06:04 +0100620
wdenkc6097192002-11-03 00:24:07 +0000621 if (!eth_devices) {
Joe Hershberger5956ded2012-05-15 08:59:07 +0000622 puts("No ethernet found.\n");
Simon Glass0169e6b2012-02-13 13:51:18 +0000623 bootstage_error(BOOTSTAGE_ID_NET_ETH_START);
wdenkc6097192002-11-03 00:24:07 +0000624 } else {
625 struct eth_device *dev = eth_devices;
Joe Hershberger5956ded2012-05-15 08:59:07 +0000626 char *ethprime = getenv("ethprime");
wdenkc6097192002-11-03 00:24:07 +0000627
Simon Glass0169e6b2012-02-13 13:51:18 +0000628 bootstage_mark(BOOTSTAGE_ID_NET_ETH_INIT);
wdenkc6097192002-11-03 00:24:07 +0000629 do {
Michael Walle391da4c2011-10-27 11:31:35 +0000630 if (dev->index)
Joe Hershberger5956ded2012-05-15 08:59:07 +0000631 puts(", ");
wdenkc6097192002-11-03 00:24:07 +0000632
633 printf("%s", dev->name);
634
Joe Hershberger5956ded2012-05-15 08:59:07 +0000635 if (ethprime && strcmp(dev->name, ethprime) == 0) {
wdenkc6097192002-11-03 00:24:07 +0000636 eth_current = dev;
Joe Hershberger5956ded2012-05-15 08:59:07 +0000637 puts(" [PRIME]");
wdenkc6097192002-11-03 00:24:07 +0000638 }
639
Mike Frysinger2843ed92010-06-09 22:10:48 -0400640 if (strchr(dev->name, ' '))
Joe Hershberger5956ded2012-05-15 08:59:07 +0000641 puts("\nWarning: eth device name has a space!"
642 "\n");
Mike Frysinger2843ed92010-06-09 22:10:48 -0400643
Pavel Machek222981f2014-07-13 10:27:02 +0200644 eth_write_hwaddr(dev, "eth", dev->index);
wdenkc6097192002-11-03 00:24:07 +0000645
wdenkc6097192002-11-03 00:24:07 +0000646 dev = dev->next;
Michael Walle391da4c2011-10-27 11:31:35 +0000647 num_devices++;
Joe Hershberger5956ded2012-05-15 08:59:07 +0000648 } while (dev != eth_devices);
wdenkc6097192002-11-03 00:24:07 +0000649
Simon Glass1e9961d2011-02-16 11:14:33 -0800650 eth_current_changed();
Joe Hershberger5956ded2012-05-15 08:59:07 +0000651 putc('\n');
wdenkc6097192002-11-03 00:24:07 +0000652 }
653
Michael Walle391da4c2011-10-27 11:31:35 +0000654 return num_devices;
wdenkc6097192002-11-03 00:24:07 +0000655}
656
David Updegraff7280da72007-06-11 10:41:07 -0500657#ifdef CONFIG_MCAST_TFTP
658/* Multicast.
659 * mcast_addr: multicast ipaddr from which multicast Mac is made
Wolfgang Denk627f5c32007-08-14 09:47:27 +0200660 * join: 1=join, 0=leave.
David Updegraff7280da72007-06-11 10:41:07 -0500661 */
Joe Hershbergeraca1c312015-03-22 17:09:05 -0500662int eth_mcast_join(IPaddr_t mcast_ip, int join)
David Updegraff7280da72007-06-11 10:41:07 -0500663{
Joe Hershberger5956ded2012-05-15 08:59:07 +0000664 u8 mcast_mac[6];
Wolfgang Denk627f5c32007-08-14 09:47:27 +0200665 if (!eth_current || !eth_current->mcast)
David Updegraff7280da72007-06-11 10:41:07 -0500666 return -1;
667 mcast_mac[5] = htonl(mcast_ip) & 0xff;
668 mcast_mac[4] = (htonl(mcast_ip)>>8) & 0xff;
669 mcast_mac[3] = (htonl(mcast_ip)>>16) & 0x7f;
670 mcast_mac[2] = 0x5e;
671 mcast_mac[1] = 0x0;
672 mcast_mac[0] = 0x1;
673 return eth_current->mcast(eth_current, mcast_mac, join);
674}
675
Wolfgang Denk627f5c32007-08-14 09:47:27 +0200676/* the 'way' for ethernet-CRC-32. Spliced in from Linux lib/crc32.c
677 * and this is the ethernet-crc method needed for TSEC -- and perhaps
David Updegraff7280da72007-06-11 10:41:07 -0500678 * some other adapter -- hash tables
679 */
680#define CRCPOLY_LE 0xedb88320
Joe Hershberger5956ded2012-05-15 08:59:07 +0000681u32 ether_crc(size_t len, unsigned char const *p)
David Updegraff7280da72007-06-11 10:41:07 -0500682{
683 int i;
684 u32 crc;
685 crc = ~0;
686 while (len--) {
687 crc ^= *p++;
688 for (i = 0; i < 8; i++)
689 crc = (crc >> 1) ^ ((crc & 1) ? CRCPOLY_LE : 0);
690 }
691 /* an reverse the bits, cuz of way they arrive -- last-first */
692 crc = (crc >> 16) | (crc << 16);
693 crc = (crc >> 8 & 0x00ff00ff) | (crc << 8 & 0xff00ff00);
694 crc = (crc >> 4 & 0x0f0f0f0f) | (crc << 4 & 0xf0f0f0f0);
695 crc = (crc >> 2 & 0x33333333) | (crc << 2 & 0xcccccccc);
696 crc = (crc >> 1 & 0x55555555) | (crc << 1 & 0xaaaaaaaa);
697 return crc;
698}
wdenkc6097192002-11-03 00:24:07 +0000699
David Updegraff7280da72007-06-11 10:41:07 -0500700#endif
701
wdenkc6097192002-11-03 00:24:07 +0000702
Joe Hershberger3dbe17e2015-03-22 17:09:06 -0500703int eth_init(void)
wdenkc6097192002-11-03 00:24:07 +0000704{
Mike Frysingere129d562009-07-15 21:31:28 -0400705 struct eth_device *old_current, *dev;
wdenkc6097192002-11-03 00:24:07 +0000706
Stefan Roese11da2be2008-03-04 17:40:41 +0100707 if (!eth_current) {
Joe Hershberger5956ded2012-05-15 08:59:07 +0000708 puts("No ethernet found.\n");
Joe Hershbergerd0ce3412015-03-22 17:09:04 -0500709 return -ENODEV;
Stefan Roese11da2be2008-03-04 17:40:41 +0100710 }
wdenkc6097192002-11-03 00:24:07 +0000711
Mike Frysingere129d562009-07-15 21:31:28 -0400712 /* Sync environment with network devices */
Mike Frysingere129d562009-07-15 21:31:28 -0400713 dev = eth_devices;
714 do {
715 uchar env_enetaddr[6];
716
Michael Walle391da4c2011-10-27 11:31:35 +0000717 if (eth_getenv_enetaddr_by_index("eth", dev->index,
Simon Glass62b36c92011-06-13 16:13:10 -0700718 env_enetaddr))
Mike Frysingere129d562009-07-15 21:31:28 -0400719 memcpy(dev->enetaddr, env_enetaddr, 6);
720
Mike Frysingere129d562009-07-15 21:31:28 -0400721 dev = dev->next;
722 } while (dev != eth_devices);
723
wdenkc6097192002-11-03 00:24:07 +0000724 old_current = eth_current;
725 do {
Robin Getz9e0a4d62009-07-23 03:01:03 -0400726 debug("Trying %s\n", eth_current->name);
wdenkc6097192002-11-03 00:24:07 +0000727
Joe Hershberger3dbe17e2015-03-22 17:09:06 -0500728 if (eth_current->init(eth_current, gd->bd) >= 0) {
wdenkc6097192002-11-03 00:24:07 +0000729 eth_current->state = ETH_STATE_ACTIVE;
730
Upakul Barkakatyaed33582007-11-29 12:16:13 +0530731 return 0;
wdenkc6097192002-11-03 00:24:07 +0000732 }
Robin Getz9e0a4d62009-07-23 03:01:03 -0400733 debug("FAIL\n");
wdenkc6097192002-11-03 00:24:07 +0000734
735 eth_try_another(0);
736 } while (old_current != eth_current);
737
Joe Hershbergerd0ce3412015-03-22 17:09:04 -0500738 return -ETIMEDOUT;
wdenkc6097192002-11-03 00:24:07 +0000739}
740
741void eth_halt(void)
742{
743 if (!eth_current)
744 return;
745
746 eth_current->halt(eth_current);
747
748 eth_current->state = ETH_STATE_PASSIVE;
749}
750
Joe Hershberger4b7747e2012-05-15 08:59:04 +0000751int eth_send(void *packet, int length)
wdenkc6097192002-11-03 00:24:07 +0000752{
753 if (!eth_current)
Joe Hershbergerd0ce3412015-03-22 17:09:04 -0500754 return -ENODEV;
wdenkc6097192002-11-03 00:24:07 +0000755
756 return eth_current->send(eth_current, packet, length);
757}
758
759int eth_rx(void)
760{
761 if (!eth_current)
Joe Hershbergerd0ce3412015-03-22 17:09:04 -0500762 return -ENODEV;
wdenkc6097192002-11-03 00:24:07 +0000763
764 return eth_current->recv(eth_current);
765}
Joe Hershbergerc7eceaf2015-03-22 17:09:10 -0500766#endif /* ifndef CONFIG_DM_ETH */
Rafal Jaworowski1f2c9a42007-12-27 18:19:02 +0100767
768#ifdef CONFIG_API
Joe Hershberger4b7747e2012-05-15 08:59:04 +0000769static void eth_save_packet(void *packet, int length)
Rafal Jaworowski1f2c9a42007-12-27 18:19:02 +0100770{
Joe Hershberger4b7747e2012-05-15 08:59:04 +0000771 char *p = packet;
Rafal Jaworowski1f2c9a42007-12-27 18:19:02 +0100772 int i;
773
774 if ((eth_rcv_last+1) % PKTBUFSRX == eth_rcv_current)
775 return;
776
777 if (PKTSIZE < length)
778 return;
779
780 for (i = 0; i < length; i++)
781 eth_rcv_bufs[eth_rcv_last].data[i] = p[i];
782
783 eth_rcv_bufs[eth_rcv_last].length = length;
784 eth_rcv_last = (eth_rcv_last + 1) % PKTBUFSRX;
785}
786
Joe Hershberger4b7747e2012-05-15 08:59:04 +0000787int eth_receive(void *packet, int length)
Rafal Jaworowski1f2c9a42007-12-27 18:19:02 +0100788{
Joe Hershberger4b7747e2012-05-15 08:59:04 +0000789 char *p = packet;
Rafal Jaworowski1f2c9a42007-12-27 18:19:02 +0100790 void *pp = push_packet;
791 int i;
792
793 if (eth_rcv_current == eth_rcv_last) {
794 push_packet = eth_save_packet;
795 eth_rx();
796 push_packet = pp;
797
798 if (eth_rcv_current == eth_rcv_last)
799 return -1;
800 }
801
Michael Walledf7c98a2012-06-22 11:24:28 +0000802 length = min(eth_rcv_bufs[eth_rcv_current].length, length);
Rafal Jaworowski1f2c9a42007-12-27 18:19:02 +0100803
804 for (i = 0; i < length; i++)
805 p[i] = eth_rcv_bufs[eth_rcv_current].data[i];
806
807 eth_rcv_current = (eth_rcv_current + 1) % PKTBUFSRX;
808 return length;
809}
810#endif /* CONFIG_API */
wdenkc6097192002-11-03 00:24:07 +0000811
Joe Hershbergere1e8a8c2015-03-22 17:09:03 -0500812static void eth_current_changed(void)
813{
814 char *act = getenv("ethact");
815 /* update current ethernet name */
816 if (eth_get_dev()) {
817 if (act == NULL || strcmp(act, eth_get_name()) != 0)
818 setenv("ethact", eth_get_name());
819 }
820 /*
821 * remove the variable completely if there is no active
822 * interface
823 */
824 else if (act != NULL)
825 setenv("ethact", NULL);
826}
827
wdenkc6097192002-11-03 00:24:07 +0000828void eth_try_another(int first_restart)
829{
Joe Hershbergerc7eceaf2015-03-22 17:09:10 -0500830 static void *first_failed;
Remy Bohmer0fce5152011-02-19 20:15:14 +0100831 char *ethrotate;
Matthias Fuchs204f0ec2008-01-17 07:45:05 +0100832
833 /*
834 * Do not rotate between network interfaces when
835 * 'ethrotate' variable is set to 'no'.
836 */
Joe Hershberger5956ded2012-05-15 08:59:07 +0000837 ethrotate = getenv("ethrotate");
838 if ((ethrotate != NULL) && (strcmp(ethrotate, "no") == 0))
Matthias Fuchs204f0ec2008-01-17 07:45:05 +0100839 return;
wdenkc6097192002-11-03 00:24:07 +0000840
Joe Hershbergere1e8a8c2015-03-22 17:09:03 -0500841 if (!eth_get_dev())
wdenkc6097192002-11-03 00:24:07 +0000842 return;
843
Joe Hershberger5956ded2012-05-15 08:59:07 +0000844 if (first_restart)
Joe Hershbergere1e8a8c2015-03-22 17:09:03 -0500845 first_failed = eth_get_dev();
wdenkc6097192002-11-03 00:24:07 +0000846
Joe Hershbergere1e8a8c2015-03-22 17:09:03 -0500847 eth_set_current_to_next();
wdenkc6097192002-11-03 00:24:07 +0000848
Simon Glass1e9961d2011-02-16 11:14:33 -0800849 eth_current_changed();
wdenk145d2c12004-04-15 21:48:45 +0000850
Joe Hershbergere1e8a8c2015-03-22 17:09:03 -0500851 if (first_failed == eth_get_dev())
wdenkc6097192002-11-03 00:24:07 +0000852 NetRestartWrap = 1;
wdenkc6097192002-11-03 00:24:07 +0000853}
854
wdenk145d2c12004-04-15 21:48:45 +0000855void eth_set_current(void)
856{
Joe Hershberger5956ded2012-05-15 08:59:07 +0000857 static char *act;
858 static int env_changed_id;
Joe Hershbergerc7eceaf2015-03-22 17:09:10 -0500859 void *old_current;
Heiko Schocher0c303fa2009-02-10 09:38:52 +0100860 int env_id;
wdenk145d2c12004-04-15 21:48:45 +0000861
Heiko Schocher0c303fa2009-02-10 09:38:52 +0100862 env_id = get_env_id();
863 if ((act == NULL) || (env_changed_id != env_id)) {
864 act = getenv("ethact");
865 env_changed_id = env_id;
866 }
wdenk145d2c12004-04-15 21:48:45 +0000867 if (act != NULL) {
Joe Hershbergere1e8a8c2015-03-22 17:09:03 -0500868 old_current = eth_get_dev();
wdenk145d2c12004-04-15 21:48:45 +0000869 do {
Joe Hershbergere1e8a8c2015-03-22 17:09:03 -0500870 if (strcmp(eth_get_name(), act) == 0)
wdenk145d2c12004-04-15 21:48:45 +0000871 return;
Joe Hershbergere1e8a8c2015-03-22 17:09:03 -0500872 eth_set_current_to_next();
873 } while (old_current != eth_get_dev());
wdenk145d2c12004-04-15 21:48:45 +0000874 }
875
Simon Glass1e9961d2011-02-16 11:14:33 -0800876 eth_current_changed();
wdenk145d2c12004-04-15 21:48:45 +0000877}
wdenk145d2c12004-04-15 21:48:45 +0000878
Joe Hershbergere1e8a8c2015-03-22 17:09:03 -0500879const char *eth_get_name(void)
wdenkc6abb7e2003-11-17 21:14:37 +0000880{
Joe Hershbergere1e8a8c2015-03-22 17:09:03 -0500881 return eth_get_dev() ? eth_get_dev()->name : "unknown";
wdenkc6abb7e2003-11-17 21:14:37 +0000882}