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