Commit 05c3e68f8518809616cd4ec5523d3f1e423ee41a
Committed by
Simon Glass
1 parent
85848f0377
Exists in
v2017.01-smarct4x
and in
34 other branches
dm: eth: Add basic driver model support to Ethernet stack
First just add support for MAC drivers. Signed-off-by: Joe Hershberger <joe.hershberger@ni.com> Reviewed-by: Simon Glass <sjg@chromium.org>
Showing 6 changed files with 410 additions and 6 deletions Side-by-side Diff
common/cmd_bdinfo.c
... | ... | @@ -34,6 +34,7 @@ |
34 | 34 | printf("%-12s= %s\n", name, val); |
35 | 35 | } |
36 | 36 | |
37 | +#ifndef CONFIG_DM_ETH | |
37 | 38 | __maybe_unused |
38 | 39 | static void print_eths(void) |
39 | 40 | { |
... | ... | @@ -52,6 +53,7 @@ |
52 | 53 | printf("current eth = %s\n", eth_get_name()); |
53 | 54 | printf("ip_addr = %s\n", getenv("ipaddr")); |
54 | 55 | } |
56 | +#endif | |
55 | 57 | |
56 | 58 | __maybe_unused |
57 | 59 | static void print_lnum(const char *name, unsigned long long value) |
doc/README.drivers.eth
1 | +!!! WARNING !!! | |
2 | + | |
3 | +This guide describes to the old way of doing things. No new Ethernet drivers | |
4 | +should be implemented this way. All new drivers should be written against the | |
5 | +U-Boot core driver model. See doc/driver-model/README.txt | |
6 | + | |
1 | 7 | ----------------------- |
2 | 8 | Ethernet Driver Guide |
3 | 9 | ----------------------- |
drivers/net/Kconfig
include/dm/uclass-id.h
include/net.h
... | ... | @@ -78,6 +78,57 @@ |
78 | 78 | ETH_STATE_ACTIVE |
79 | 79 | }; |
80 | 80 | |
81 | +#ifdef CONFIG_DM_ETH | |
82 | +/** | |
83 | + * struct eth_pdata - Platform data for Ethernet MAC controllers | |
84 | + * | |
85 | + * @iobase: The base address of the hardware registers | |
86 | + * @enetaddr: The Ethernet MAC address that is loaded from EEPROM or env | |
87 | + */ | |
88 | +struct eth_pdata { | |
89 | + phys_addr_t iobase; | |
90 | + unsigned char enetaddr[6]; | |
91 | +}; | |
92 | + | |
93 | +/** | |
94 | + * struct eth_ops - functions of Ethernet MAC controllers | |
95 | + * | |
96 | + * start: Prepare the hardware to send and receive packets | |
97 | + * send: Send the bytes passed in "packet" as a packet on the wire | |
98 | + * recv: Check if the hardware received a packet. Call the network stack if so | |
99 | + * stop: Stop the hardware from looking for packets - may be called even if | |
100 | + * state == PASSIVE | |
101 | + * mcast: Join or leave a multicast group (for TFTP) - optional | |
102 | + * write_hwaddr: Write a MAC address to the hardware (used to pass it to Linux | |
103 | + * on some platforms like ARM). This function expects the | |
104 | + * eth_pdata::enetaddr field to be populated - optional | |
105 | + * read_rom_hwaddr: Some devices have a backup of the MAC address stored in a | |
106 | + * ROM on the board. This is how the driver should expose it | |
107 | + * to the network stack. This function should fill in the | |
108 | + * eth_pdata::enetaddr field - optional | |
109 | + */ | |
110 | +struct eth_ops { | |
111 | + int (*start)(struct udevice *dev); | |
112 | + int (*send)(struct udevice *dev, void *packet, int length); | |
113 | + int (*recv)(struct udevice *dev); | |
114 | + void (*stop)(struct udevice *dev); | |
115 | +#ifdef CONFIG_MCAST_TFTP | |
116 | + int (*mcast)(struct udevice *dev, const u8 *enetaddr, int join); | |
117 | +#endif | |
118 | + int (*write_hwaddr)(struct udevice *dev); | |
119 | + int (*read_rom_hwaddr)(struct udevice *dev); | |
120 | +}; | |
121 | + | |
122 | +#define eth_get_ops(dev) ((struct eth_ops *)(dev)->driver->ops) | |
123 | + | |
124 | +struct udevice *eth_get_dev(void); /* get the current device */ | |
125 | +unsigned char *eth_get_ethaddr(void); /* get the current device MAC */ | |
126 | +/* Used only when NetConsole is enabled */ | |
127 | +int eth_init_state_only(void); /* Set active state */ | |
128 | +void eth_halt_state_only(void); /* Set passive state */ | |
129 | +#endif | |
130 | + | |
131 | +#ifndef CONFIG_DM_ETH | |
81 | 132 | struct eth_device { |
82 | 133 | char name[16]; |
83 | 134 | unsigned char enetaddr[6]; |
... | ... | @@ -144,6 +195,7 @@ |
144 | 195 | int eth_number); |
145 | 196 | |
146 | 197 | int usb_eth_initialize(bd_t *bi); |
198 | +#endif | |
147 | 199 | |
148 | 200 | int eth_initialize(void); /* Initialize network subsystem */ |
149 | 201 | void eth_try_another(int first_restart); /* Change the device */ |
net/eth.c
1 | 1 | /* |
2 | - * (C) Copyright 2001-2010 | |
2 | + * (C) Copyright 2001-2015 | |
3 | 3 | * Wolfgang Denk, DENX Software Engineering, wd@denx.de. |
4 | + * Joe Hershberger, National Instruments | |
4 | 5 | * |
5 | 6 | * SPDX-License-Identifier: GPL-2.0+ |
6 | 7 | */ |
7 | 8 | |
8 | 9 | #include <common.h> |
9 | 10 | #include <command.h> |
11 | +#include <dm.h> | |
10 | 12 | #include <net.h> |
11 | 13 | #include <miiphy.h> |
12 | 14 | #include <phy.h> |
13 | 15 | #include <asm/errno.h> |
16 | +#include <dm/device-internal.h> | |
14 | 17 | |
15 | 18 | DECLARE_GLOBAL_DATA_PTR; |
16 | 19 | |
... | ... | @@ -76,6 +79,339 @@ |
76 | 79 | |
77 | 80 | static void eth_current_changed(void); |
78 | 81 | |
82 | +#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 | + */ | |
88 | +struct 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 | + */ | |
97 | +struct eth_uclass_priv { | |
98 | + struct udevice *current; | |
99 | +}; | |
100 | + | |
101 | +static 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 | + | |
110 | +static 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 | + | |
121 | +struct 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 | + | |
132 | +static void eth_set_dev(struct udevice *dev) | |
133 | +{ | |
134 | + device_probe(dev); | |
135 | + eth_get_uclass_priv()->current = dev; | |
136 | +} | |
137 | + | |
138 | +unsigned 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 */ | |
151 | +int 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 */ | |
167 | +void 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 | + | |
180 | +int eth_get_dev_index(void) | |
181 | +{ | |
182 | + if (eth_get_dev()) | |
183 | + return eth_get_dev()->seq; | |
184 | + return -1; | |
185 | +} | |
186 | + | |
187 | +int 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 | + | |
231 | +void 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 | + | |
245 | +int 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 | + | |
259 | +int 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 | + | |
273 | +static 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 | + | |
298 | +int 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 | + | |
336 | +static 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 | + | |
347 | +static 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 | + | |
356 | +static 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 | + | |
395 | +static int eth_pre_remove(struct udevice *dev) | |
396 | +{ | |
397 | + eth_get_ops(dev)->stop(dev); | |
398 | + | |
399 | + return 0; | |
400 | +} | |
401 | + | |
402 | +UCLASS_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 | |
79 | 415 | /* |
80 | 416 | * CPU and board-specific Ethernet initializations. Aliased function |
81 | 417 | * signals caller to move on |
... | ... | @@ -427,6 +763,7 @@ |
427 | 763 | |
428 | 764 | return eth_current->recv(eth_current); |
429 | 765 | } |
766 | +#endif /* ifndef CONFIG_DM_ETH */ | |
430 | 767 | |
431 | 768 | #ifdef CONFIG_API |
432 | 769 | static void eth_save_packet(void *packet, int length) |
... | ... | @@ -490,7 +827,7 @@ |
490 | 827 | |
491 | 828 | void eth_try_another(int first_restart) |
492 | 829 | { |
493 | - static struct eth_device *first_failed; | |
830 | + static void *first_failed; | |
494 | 831 | char *ethrotate; |
495 | 832 | |
496 | 833 | /* |
497 | 834 | |
... | ... | @@ -519,11 +856,8 @@ |
519 | 856 | { |
520 | 857 | static char *act; |
521 | 858 | static int env_changed_id; |
522 | - struct eth_device *old_current; | |
859 | + void *old_current; | |
523 | 860 | int env_id; |
524 | - | |
525 | - if (!eth_get_dev()) /* XXX no current */ | |
526 | - return; | |
527 | 861 | |
528 | 862 | env_id = get_env_id(); |
529 | 863 | if ((act == NULL) || (env_changed_id != env_id)) { |