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