mirror of
				https://github.com/smaeul/u-boot.git
				synced 2025-11-04 05:50:17 +00:00 
			
		
		
		
	These docs are useful for developers, not users. Move them under that section. Suggested-by: Heinrich Schuchardt <xypron.glpk@gmx.de> Signed-off-by: Simon Glass <sjg@chromium.org>
		
			
				
	
	
		
			322 lines
		
	
	
		
			12 KiB
		
	
	
	
		
			ReStructuredText
		
	
	
	
	
	
			
		
		
	
	
			322 lines
		
	
	
		
			12 KiB
		
	
	
	
		
			ReStructuredText
		
	
	
	
	
	
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,
 | 
						|
		.of_to_plat	= eth_ape_of_to_plat,
 | 
						|
		.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.
 |