Commit 05c3e68f8518809616cd4ec5523d3f1e423ee41a

Authored by Joe Hershberger
Committed by Simon Glass
1 parent 85848f0377

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

... ... @@ -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 -----------------------
  1 +config DM_ETH
  2 + bool "Enable Driver Model for Ethernet drivers"
  3 + depends on DM
  4 + help
  5 + Enable driver model for Ethernet.
  6 +
  7 + The eth_*() interface will be implemented by the UC_ETH class
  8 + This is currently implemented in net/eth.c
  9 + Look in include/net.h for details.
include/dm/uclass-id.h
... ... @@ -38,6 +38,7 @@
38 38 UCLASS_PCI, /* PCI bus */
39 39 UCLASS_PCI_GENERIC, /* Generic PCI bus device */
40 40 UCLASS_PCH, /* x86 platform controller hub */
  41 + UCLASS_ETH, /* Ethernet device */
41 42  
42 43 UCLASS_COUNT,
43 44 UCLASS_INVALID = -1,
... ... @@ -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 */
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)) {