Andre Przywara | 168ebe0 | 2019-11-25 01:32:15 +0000 | [diff] [blame] | 1 | Ethernet Driver Guide |
| 2 | ======================= |
| 3 | |
| 4 | The networking stack in Das U-Boot is designed for multiple network devices |
| 5 | to be easily added and controlled at runtime. This guide is meant for people |
| 6 | who wish to review the net driver stack with an eye towards implementing your |
| 7 | own ethernet device driver. Here we will describe a new pseudo 'APE' driver. |
| 8 | |
| 9 | Most existing drivers do already - and new network driver MUST - use the |
| 10 | U-Boot core driver model. Generic information about this can be found in |
| 11 | doc/driver-model/design.rst, this document will thus focus on the network |
| 12 | specific code parts. |
| 13 | Some drivers are still using the old Ethernet interface, differences between |
| 14 | the two and hints about porting will be handled at the end. |
| 15 | |
| 16 | Driver framework |
| 17 | ------------------ |
| 18 | |
| 19 | A network driver following the driver model must declare itself using |
| 20 | the UCLASS_ETH .id field in the U-Boot driver struct: |
| 21 | |
| 22 | .. code-block:: c |
| 23 | |
| 24 | U_BOOT_DRIVER(eth_ape) = { |
| 25 | .name = "eth_ape", |
| 26 | .id = UCLASS_ETH, |
| 27 | .of_match = eth_ape_ids, |
Simon Glass | aad29ae | 2020-12-03 16:55:21 -0700 | [diff] [blame] | 28 | .of_to_plat = eth_ape_of_to_plat, |
Andre Przywara | 168ebe0 | 2019-11-25 01:32:15 +0000 | [diff] [blame] | 29 | .probe = eth_ape_probe, |
| 30 | .ops = ð_ape_ops, |
Simon Glass | 8a2b47f | 2020-12-03 16:55:17 -0700 | [diff] [blame] | 31 | .priv_auto = sizeof(struct eth_ape_priv), |
Simon Glass | 71fa5b4 | 2020-12-03 16:55:18 -0700 | [diff] [blame] | 32 | .plat_auto = sizeof(struct eth_ape_pdata), |
Andre Przywara | 168ebe0 | 2019-11-25 01:32:15 +0000 | [diff] [blame] | 33 | .flags = DM_FLAG_ALLOC_PRIV_DMA, |
| 34 | }; |
| 35 | |
| 36 | struct eth_ape_priv contains runtime per-instance data, like buffers, pointers |
| 37 | to current descriptors, current speed settings, pointers to PHY related data |
Simon Glass | 8a2b47f | 2020-12-03 16:55:17 -0700 | [diff] [blame] | 38 | (like struct mii_dev) and so on. Declaring its size in .priv_auto |
Andre Przywara | 168ebe0 | 2019-11-25 01:32:15 +0000 | [diff] [blame] | 39 | will let the driver framework allocate it at the right time. |
| 40 | It can be retrieved using a dev_get_priv(dev) call. |
| 41 | |
| 42 | struct eth_ape_pdata contains static platform data, like the MMIO base address, |
| 43 | a hardware variant, the MAC address. ``struct eth_pdata eth_pdata`` |
| 44 | as the first member of this struct helps to avoid duplicated code. |
| 45 | If you don't need any more platform data beside the standard member, |
Simon Glass | 71fa5b4 | 2020-12-03 16:55:18 -0700 | [diff] [blame] | 46 | just use sizeof(struct eth_pdata) for the plat_auto. |
Andre Przywara | 168ebe0 | 2019-11-25 01:32:15 +0000 | [diff] [blame] | 47 | |
| 48 | PCI devices add a line pointing to supported vendor/device ID pairs: |
| 49 | |
| 50 | .. code-block:: c |
| 51 | |
| 52 | static struct pci_device_id supported[] = { |
| 53 | { PCI_DEVICE(PCI_VENDOR_ID_APE, 0x4223) }, |
| 54 | {} |
| 55 | }; |
| 56 | |
| 57 | U_BOOT_PCI_DEVICE(eth_ape, supported); |
| 58 | |
| 59 | It is also possible to declare support for a whole class of PCI devices:: |
| 60 | |
| 61 | { PCI_DEVICE_CLASS(PCI_CLASS_SYSTEM_SDHCI << 8, 0xffff00) }, |
| 62 | |
| 63 | Device probing and instantiation will be handled by the driver model framework, |
| 64 | so follow the guidelines there. The probe() function would initialise the |
| 65 | platform specific parts of the hardware, like clocks, resets, GPIOs, the MDIO |
| 66 | bus. Also it would take care of any special PHY setup (power rails, enable |
| 67 | bits for internal PHYs, etc.). |
| 68 | |
| 69 | Driver methods |
| 70 | ---------------- |
| 71 | |
| 72 | The real work will be done in the driver method functions the driver provides |
| 73 | by defining the members of struct eth_ops: |
| 74 | |
| 75 | .. code-block:: c |
| 76 | |
| 77 | struct eth_ops { |
| 78 | int (*start)(struct udevice *dev); |
| 79 | int (*send)(struct udevice *dev, void *packet, int length); |
| 80 | int (*recv)(struct udevice *dev, int flags, uchar **packetp); |
| 81 | int (*free_pkt)(struct udevice *dev, uchar *packet, int length); |
| 82 | void (*stop)(struct udevice *dev); |
| 83 | int (*mcast)(struct udevice *dev, const u8 *enetaddr, int join); |
| 84 | int (*write_hwaddr)(struct udevice *dev); |
| 85 | int (*read_rom_hwaddr)(struct udevice *dev); |
| 86 | }; |
| 87 | |
| 88 | An up-to-date version of this struct together with more information can be |
| 89 | found in include/net.h. |
| 90 | |
| 91 | Only start, stop, send and recv are required, the rest are optional and are |
| 92 | handled by generic code or ignored if not provided. |
| 93 | |
| 94 | The **start** function initialises the hardware and gets it ready for send/recv |
| 95 | operations. You often do things here such as resetting the MAC |
| 96 | and/or PHY, and waiting for the link to autonegotiate. You should also take |
| 97 | the opportunity to program the device's MAC address with the enetaddr member |
| 98 | of the generic struct eth_pdata (which would be the first member of your |
Simon Glass | 71fa5b4 | 2020-12-03 16:55:18 -0700 | [diff] [blame] | 99 | own plat struct). This allows the rest of U-Boot to dynamically change |
Andre Przywara | 168ebe0 | 2019-11-25 01:32:15 +0000 | [diff] [blame] | 100 | the MAC address and have the new settings be respected. |
| 101 | |
| 102 | The **send** function does what you think -- transmit the specified packet |
| 103 | whose size is specified by length (in bytes). The packet buffer can (and |
| 104 | will!) be reused for subsequent calls to send(), so it must be no longer |
| 105 | used when the send() function returns. The easiest way to achieve this is |
| 106 | to wait until the transmission is complete. Alternatively, if supported by |
| 107 | the hardware, just waiting for the buffer to be consumed (by some DMA engine) |
| 108 | might be an option as well. |
| 109 | Another way of consuming the buffer could be to copy the data to be send, |
| 110 | then just queue the copied packet (for instance handing it over to a DMA |
| 111 | engine), and return immediately afterwards. |
| 112 | In any case you should leave the state such that the send function can be |
| 113 | called multiple times in a row. |
| 114 | |
| 115 | The **recv** function polls for availability of a new packet. If none is |
| 116 | available, it must return with -EAGAIN. |
| 117 | If a packet has been received, make sure it is accessible to the CPU |
| 118 | (invalidate caches if needed), then write its address to the packetp pointer, |
| 119 | and return the length. If there is an error (receive error, too short or too |
| 120 | long packet), return 0 if you require the packet to be cleaned up normally, |
| 121 | or a negative error code otherwise (cleanup not necessary or already done). |
| 122 | The U-Boot network stack will then process the packet. |
| 123 | |
| 124 | If **free_pkt** is defined, U-Boot will call it after a received packet has |
| 125 | been processed, so the packet buffer can be freed or recycled. Typically you |
| 126 | would hand it back to the hardware to acquire another packet. free_pkt() will |
| 127 | be called after recv(), for the same packet, so you don't necessarily need |
| 128 | to infer the buffer to free from the ``packet`` pointer, but can rely on that |
| 129 | being the last packet that recv() handled. |
| 130 | The common code sets up packet buffers for you already in the .bss |
| 131 | (net_rx_packets), so there should be no need to allocate your own. This doesn't |
| 132 | mean you must use the net_rx_packets array however; you're free to use any |
| 133 | buffer you wish. |
| 134 | |
| 135 | The **stop** function should turn off / disable the hardware and place it back |
| 136 | in its reset state. It can be called at any time (before any call to the |
| 137 | related start() function), so make sure it can handle this sort of thing. |
| 138 | |
| 139 | The (optional) **write_hwaddr** function should program the MAC address stored |
| 140 | in pdata->enetaddr into the Ethernet controller. |
| 141 | |
| 142 | So the call graph at this stage would look something like: |
| 143 | |
| 144 | .. code-block:: c |
| 145 | |
| 146 | (some net operation (ping / tftp / whatever...)) |
| 147 | eth_init() |
| 148 | ops->start() |
| 149 | eth_send() |
| 150 | ops->send() |
| 151 | eth_rx() |
| 152 | ops->recv() |
| 153 | (process packet) |
| 154 | if (ops->free_pkt) |
| 155 | ops->free_pkt() |
| 156 | eth_halt() |
| 157 | ops->stop() |
| 158 | |
| 159 | |
| 160 | CONFIG_PHYLIB / CONFIG_CMD_MII |
| 161 | -------------------------------- |
| 162 | |
| 163 | If your device supports banging arbitrary values on the MII bus (pretty much |
| 164 | every device does), you should add support for the mii command. Doing so is |
| 165 | fairly trivial and makes debugging mii issues a lot easier at runtime. |
| 166 | |
| 167 | In your driver's ``probe()`` function, add a call to mdio_alloc() and |
| 168 | mdio_register() like so: |
| 169 | |
| 170 | .. code-block:: c |
| 171 | |
| 172 | bus = mdio_alloc(); |
| 173 | if (!bus) { |
| 174 | ... |
| 175 | return -ENOMEM; |
| 176 | } |
| 177 | |
| 178 | bus->read = ape_mii_read; |
| 179 | bus->write = ape_mii_write; |
| 180 | mdio_register(bus); |
| 181 | |
| 182 | And then define the mii_read and mii_write functions if you haven't already. |
| 183 | Their syntax is straightforward:: |
| 184 | |
| 185 | int mii_read(struct mii_dev *bus, int addr, int devad, int reg); |
| 186 | int mii_write(struct mii_dev *bus, int addr, int devad, int reg, |
| 187 | u16 val); |
| 188 | |
| 189 | The read function should read the register 'reg' from the phy at address 'addr' |
| 190 | and return the result to its caller. The implementation for the write function |
| 191 | should logically follow. |
| 192 | |
| 193 | ................................................................ |
| 194 | |
| 195 | Legacy network drivers |
| 196 | ------------------------ |
| 197 | |
| 198 | !!! WARNING !!! |
| 199 | |
| 200 | This section below describes the old way of doing things. No new Ethernet |
| 201 | drivers should be implemented this way. All new drivers should be written |
| 202 | against the U-Boot core driver model, as described above. |
| 203 | |
| 204 | The actual callback functions are fairly similar, the differences are: |
| 205 | |
| 206 | - ``start()`` is called ``init()`` |
| 207 | - ``stop()`` is called ``halt()`` |
| 208 | - The ``recv()`` function must loop until all packets have been received, for |
| 209 | each packet it must call the net_process_received_packet() function, |
| 210 | handing it over the pointer and the length. Afterwards it should free |
| 211 | the packet, before checking for new data. |
| 212 | |
| 213 | For porting an old driver to the new driver model, split the existing recv() |
| 214 | function into the actual new recv() function, just fetching **one** packet, |
| 215 | remove the call to net_process_received_packet(), then move the packet |
| 216 | cleanup into the ``free_pkt()`` function. |
| 217 | |
| 218 | Registering the driver and probing a device is handled very differently, |
| 219 | follow the recommendations in the driver model design documentation for |
| 220 | instructions on how to port this over. For the records, the old way of |
| 221 | initialising a network driver is as follows: |
| 222 | |
| 223 | Old network driver registration |
| 224 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
| 225 | |
| 226 | When U-Boot initializes, it will call the common function eth_initialize(). |
| 227 | This will in turn call the board-specific board_eth_init() (or if that fails, |
| 228 | the cpu-specific cpu_eth_init()). These board-specific functions can do random |
| 229 | system handling, but ultimately they will call the driver-specific register |
| 230 | function which in turn takes care of initializing that particular instance. |
| 231 | |
| 232 | Keep in mind that you should code the driver to avoid storing state in global |
| 233 | data as someone might want to hook up two of the same devices to one board. |
| 234 | Any such information that is specific to an interface should be stored in a |
| 235 | private, driver-defined data structure and pointed to by eth->priv (see below). |
| 236 | |
| 237 | So the call graph at this stage would look something like: |
| 238 | |
| 239 | .. code-block:: c |
| 240 | |
| 241 | board_init() |
| 242 | eth_initialize() |
| 243 | board_eth_init() / cpu_eth_init() |
| 244 | driver_register() |
| 245 | initialize eth_device |
| 246 | eth_register() |
| 247 | |
| 248 | At this point in time, the only thing you need to worry about is the driver's |
| 249 | register function. The pseudo code would look something like: |
| 250 | |
| 251 | .. code-block:: c |
| 252 | |
Masahiro Yamada | 940a922 | 2020-06-26 15:13:34 +0900 | [diff] [blame] | 253 | int ape_register(struct bd_info *bis, int iobase) |
Andre Przywara | 168ebe0 | 2019-11-25 01:32:15 +0000 | [diff] [blame] | 254 | { |
| 255 | struct ape_priv *priv; |
| 256 | struct eth_device *dev; |
| 257 | struct mii_dev *bus; |
| 258 | |
| 259 | priv = malloc(sizeof(*priv)); |
| 260 | if (priv == NULL) |
| 261 | return -ENOMEM; |
| 262 | |
| 263 | dev = malloc(sizeof(*dev)); |
| 264 | if (dev == NULL) { |
| 265 | free(priv); |
| 266 | return -ENOMEM; |
| 267 | } |
| 268 | |
| 269 | /* setup whatever private state you need */ |
| 270 | |
| 271 | memset(dev, 0, sizeof(*dev)); |
| 272 | sprintf(dev->name, "APE"); |
| 273 | |
| 274 | /* |
| 275 | * if your device has dedicated hardware storage for the |
| 276 | * MAC, read it and initialize dev->enetaddr with it |
| 277 | */ |
| 278 | ape_mac_read(dev->enetaddr); |
| 279 | |
| 280 | dev->iobase = iobase; |
| 281 | dev->priv = priv; |
| 282 | dev->init = ape_init; |
| 283 | dev->halt = ape_halt; |
| 284 | dev->send = ape_send; |
| 285 | dev->recv = ape_recv; |
| 286 | dev->write_hwaddr = ape_write_hwaddr; |
| 287 | |
| 288 | eth_register(dev); |
| 289 | |
| 290 | #ifdef CONFIG_PHYLIB |
| 291 | bus = mdio_alloc(); |
| 292 | if (!bus) { |
| 293 | free(priv); |
| 294 | free(dev); |
| 295 | return -ENOMEM; |
| 296 | } |
| 297 | |
| 298 | bus->read = ape_mii_read; |
| 299 | bus->write = ape_mii_write; |
| 300 | mdio_register(bus); |
| 301 | #endif |
| 302 | |
| 303 | return 1; |
| 304 | } |
| 305 | |
| 306 | The exact arguments needed to initialize your device are up to you. If you |
| 307 | need to pass more/less arguments, that's fine. You should also add the |
| 308 | prototype for your new register function to include/netdev.h. |
| 309 | |
| 310 | The return value for this function should be as follows: |
| 311 | < 0 - failure (hardware failure, not probe failure) |
| 312 | >=0 - number of interfaces detected |
| 313 | |
| 314 | You might notice that many drivers seem to use xxx_initialize() rather than |
| 315 | xxx_register(). This is the old naming convention and should be avoided as it |
| 316 | causes confusion with the driver-specific init function. |
| 317 | |
| 318 | Other than locating the MAC address in dedicated hardware storage, you should |
| 319 | not touch the hardware in anyway. That step is handled in the driver-specific |
| 320 | init function. Remember that we are only registering the device here, we are |
| 321 | not checking its state or doing random probing. |