123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321 |
- Ethernet Driver Guide
- =======================
- The networking stack in Das U-Boot is designed for multiple network devices
- to be easily added and controlled at runtime. This guide is meant for people
- who wish to review the net driver stack with an eye towards implementing your
- own ethernet device driver. Here we will describe a new pseudo 'APE' driver.
- Most existing drivers do already - and new network driver MUST - use the
- U-Boot core driver model. Generic information about this can be found in
- doc/driver-model/design.rst, this document will thus focus on the network
- specific code parts.
- Some drivers are still using the old Ethernet interface, differences between
- the two and hints about porting will be handled at the end.
- Driver framework
- ------------------
- A network driver following the driver model must declare itself using
- the UCLASS_ETH .id field in the U-Boot driver struct:
- .. code-block:: c
- U_BOOT_DRIVER(eth_ape) = {
- .name = "eth_ape",
- .id = UCLASS_ETH,
- .of_match = eth_ape_ids,
- .ofdata_to_platdata = eth_ape_ofdata_to_platdata,
- .probe = eth_ape_probe,
- .ops = ð_ape_ops,
- .priv_auto = sizeof(struct eth_ape_priv),
- .plat_auto = sizeof(struct eth_ape_pdata),
- .flags = DM_FLAG_ALLOC_PRIV_DMA,
- };
- struct eth_ape_priv contains runtime per-instance data, like buffers, pointers
- to current descriptors, current speed settings, pointers to PHY related data
- (like struct mii_dev) and so on. Declaring its size in .priv_auto
- will let the driver framework allocate it at the right time.
- It can be retrieved using a dev_get_priv(dev) call.
- struct eth_ape_pdata contains static platform data, like the MMIO base address,
- a hardware variant, the MAC address. ``struct eth_pdata eth_pdata``
- as the first member of this struct helps to avoid duplicated code.
- If you don't need any more platform data beside the standard member,
- just use sizeof(struct eth_pdata) for the plat_auto.
- PCI devices add a line pointing to supported vendor/device ID pairs:
- .. code-block:: c
- static struct pci_device_id supported[] = {
- { PCI_DEVICE(PCI_VENDOR_ID_APE, 0x4223) },
- {}
- };
- U_BOOT_PCI_DEVICE(eth_ape, supported);
- It is also possible to declare support for a whole class of PCI devices::
- { PCI_DEVICE_CLASS(PCI_CLASS_SYSTEM_SDHCI << 8, 0xffff00) },
- Device probing and instantiation will be handled by the driver model framework,
- so follow the guidelines there. The probe() function would initialise the
- platform specific parts of the hardware, like clocks, resets, GPIOs, the MDIO
- bus. Also it would take care of any special PHY setup (power rails, enable
- bits for internal PHYs, etc.).
- Driver methods
- ----------------
- The real work will be done in the driver method functions the driver provides
- by defining the members of struct eth_ops:
- .. code-block:: c
- struct eth_ops {
- int (*start)(struct udevice *dev);
- int (*send)(struct udevice *dev, void *packet, int length);
- int (*recv)(struct udevice *dev, int flags, uchar **packetp);
- int (*free_pkt)(struct udevice *dev, uchar *packet, int length);
- void (*stop)(struct udevice *dev);
- int (*mcast)(struct udevice *dev, const u8 *enetaddr, int join);
- int (*write_hwaddr)(struct udevice *dev);
- int (*read_rom_hwaddr)(struct udevice *dev);
- };
- An up-to-date version of this struct together with more information can be
- found in include/net.h.
- Only start, stop, send and recv are required, the rest are optional and are
- handled by generic code or ignored if not provided.
- The **start** function initialises the hardware and gets it ready for send/recv
- operations. You often do things here such as resetting the MAC
- and/or PHY, and waiting for the link to autonegotiate. You should also take
- the opportunity to program the device's MAC address with the enetaddr member
- of the generic struct eth_pdata (which would be the first member of your
- own plat struct). This allows the rest of U-Boot to dynamically change
- the MAC address and have the new settings be respected.
- The **send** function does what you think -- transmit the specified packet
- whose size is specified by length (in bytes). The packet buffer can (and
- will!) be reused for subsequent calls to send(), so it must be no longer
- used when the send() function returns. The easiest way to achieve this is
- to wait until the transmission is complete. Alternatively, if supported by
- the hardware, just waiting for the buffer to be consumed (by some DMA engine)
- might be an option as well.
- Another way of consuming the buffer could be to copy the data to be send,
- then just queue the copied packet (for instance handing it over to a DMA
- engine), and return immediately afterwards.
- In any case you should leave the state such that the send function can be
- called multiple times in a row.
- The **recv** function polls for availability of a new packet. If none is
- available, it must return with -EAGAIN.
- If a packet has been received, make sure it is accessible to the CPU
- (invalidate caches if needed), then write its address to the packetp pointer,
- and return the length. If there is an error (receive error, too short or too
- long packet), return 0 if you require the packet to be cleaned up normally,
- or a negative error code otherwise (cleanup not necessary or already done).
- The U-Boot network stack will then process the packet.
- If **free_pkt** is defined, U-Boot will call it after a received packet has
- been processed, so the packet buffer can be freed or recycled. Typically you
- would hand it back to the hardware to acquire another packet. free_pkt() will
- be called after recv(), for the same packet, so you don't necessarily need
- to infer the buffer to free from the ``packet`` pointer, but can rely on that
- being the last packet that recv() handled.
- The common code sets up packet buffers for you already in the .bss
- (net_rx_packets), so there should be no need to allocate your own. This doesn't
- mean you must use the net_rx_packets array however; you're free to use any
- buffer you wish.
- The **stop** function should turn off / disable the hardware and place it back
- in its reset state. It can be called at any time (before any call to the
- related start() function), so make sure it can handle this sort of thing.
- The (optional) **write_hwaddr** function should program the MAC address stored
- in pdata->enetaddr into the Ethernet controller.
- So the call graph at this stage would look something like:
- .. code-block:: c
- (some net operation (ping / tftp / whatever...))
- eth_init()
- ops->start()
- eth_send()
- ops->send()
- eth_rx()
- ops->recv()
- (process packet)
- if (ops->free_pkt)
- ops->free_pkt()
- eth_halt()
- ops->stop()
- CONFIG_PHYLIB / CONFIG_CMD_MII
- --------------------------------
- If your device supports banging arbitrary values on the MII bus (pretty much
- every device does), you should add support for the mii command. Doing so is
- fairly trivial and makes debugging mii issues a lot easier at runtime.
- In your driver's ``probe()`` function, add a call to mdio_alloc() and
- mdio_register() like so:
- .. code-block:: c
- bus = mdio_alloc();
- if (!bus) {
- ...
- return -ENOMEM;
- }
- bus->read = ape_mii_read;
- bus->write = ape_mii_write;
- mdio_register(bus);
- And then define the mii_read and mii_write functions if you haven't already.
- Their syntax is straightforward::
- int mii_read(struct mii_dev *bus, int addr, int devad, int reg);
- int mii_write(struct mii_dev *bus, int addr, int devad, int reg,
- u16 val);
- The read function should read the register 'reg' from the phy at address 'addr'
- and return the result to its caller. The implementation for the write function
- should logically follow.
- ................................................................
- Legacy network drivers
- ------------------------
- !!! WARNING !!!
- This section below describes the old way of doing things. No new Ethernet
- drivers should be implemented this way. All new drivers should be written
- against the U-Boot core driver model, as described above.
- The actual callback functions are fairly similar, the differences are:
- - ``start()`` is called ``init()``
- - ``stop()`` is called ``halt()``
- - The ``recv()`` function must loop until all packets have been received, for
- each packet it must call the net_process_received_packet() function,
- handing it over the pointer and the length. Afterwards it should free
- the packet, before checking for new data.
- For porting an old driver to the new driver model, split the existing recv()
- function into the actual new recv() function, just fetching **one** packet,
- remove the call to net_process_received_packet(), then move the packet
- cleanup into the ``free_pkt()`` function.
- Registering the driver and probing a device is handled very differently,
- follow the recommendations in the driver model design documentation for
- instructions on how to port this over. For the records, the old way of
- initialising a network driver is as follows:
- Old network driver registration
- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- When U-Boot initializes, it will call the common function eth_initialize().
- This will in turn call the board-specific board_eth_init() (or if that fails,
- the cpu-specific cpu_eth_init()). These board-specific functions can do random
- system handling, but ultimately they will call the driver-specific register
- function which in turn takes care of initializing that particular instance.
- Keep in mind that you should code the driver to avoid storing state in global
- data as someone might want to hook up two of the same devices to one board.
- Any such information that is specific to an interface should be stored in a
- private, driver-defined data structure and pointed to by eth->priv (see below).
- So the call graph at this stage would look something like:
- .. code-block:: c
- board_init()
- eth_initialize()
- board_eth_init() / cpu_eth_init()
- driver_register()
- initialize eth_device
- eth_register()
- At this point in time, the only thing you need to worry about is the driver's
- register function. The pseudo code would look something like:
- .. code-block:: c
- int ape_register(struct bd_info *bis, int iobase)
- {
- struct ape_priv *priv;
- struct eth_device *dev;
- struct mii_dev *bus;
- priv = malloc(sizeof(*priv));
- if (priv == NULL)
- return -ENOMEM;
- dev = malloc(sizeof(*dev));
- if (dev == NULL) {
- free(priv);
- return -ENOMEM;
- }
- /* setup whatever private state you need */
- memset(dev, 0, sizeof(*dev));
- sprintf(dev->name, "APE");
- /*
- * if your device has dedicated hardware storage for the
- * MAC, read it and initialize dev->enetaddr with it
- */
- ape_mac_read(dev->enetaddr);
- dev->iobase = iobase;
- dev->priv = priv;
- dev->init = ape_init;
- dev->halt = ape_halt;
- dev->send = ape_send;
- dev->recv = ape_recv;
- dev->write_hwaddr = ape_write_hwaddr;
- eth_register(dev);
- #ifdef CONFIG_PHYLIB
- bus = mdio_alloc();
- if (!bus) {
- free(priv);
- free(dev);
- return -ENOMEM;
- }
- bus->read = ape_mii_read;
- bus->write = ape_mii_write;
- mdio_register(bus);
- #endif
- return 1;
- }
- The exact arguments needed to initialize your device are up to you. If you
- need to pass more/less arguments, that's fine. You should also add the
- prototype for your new register function to include/netdev.h.
- The return value for this function should be as follows:
- < 0 - failure (hardware failure, not probe failure)
- >=0 - number of interfaces detected
- You might notice that many drivers seem to use xxx_initialize() rather than
- xxx_register(). This is the old naming convention and should be avoided as it
- causes confusion with the driver-specific init function.
- Other than locating the MAC address in dedicated hardware storage, you should
- not touch the hardware in anyway. That step is handled in the driver-specific
- init function. Remember that we are only registering the device here, we are
- not checking its state or doing random probing.
|