diff options
Diffstat (limited to 'qemu/roms/u-boot/doc/README.drivers.eth')
-rw-r--r-- | qemu/roms/u-boot/doc/README.drivers.eth | 190 |
1 files changed, 190 insertions, 0 deletions
diff --git a/qemu/roms/u-boot/doc/README.drivers.eth b/qemu/roms/u-boot/doc/README.drivers.eth new file mode 100644 index 000000000..eb83038b5 --- /dev/null +++ b/qemu/roms/u-boot/doc/README.drivers.eth @@ -0,0 +1,190 @@ +----------------------- + 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. + +------------------ + Driver Functions +------------------ + +All functions you will be implementing in this document have the return value +meaning of 0 for success and non-zero for failure. + + ---------- + Register + ---------- + +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: +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: +int ape_register(bd_t *bis, int iobase) +{ + struct ape_priv *priv; + struct eth_device *dev; + + priv = malloc(sizeof(*priv)); + if (priv == NULL) + return 1; + + dev = malloc(sizeof(*dev)); + if (dev == NULL) { + free(priv); + return 1; + } + + /* 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_CMD_MII) + miiphy_register(dev->name, ape_mii_read, ape_mii_write); +#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. + + ----------- + Callbacks + ----------- + +Now that we've registered with the ethernet layer, we can start getting some +real work done. You will need five functions: + int ape_init(struct eth_device *dev, bd_t *bis); + int ape_send(struct eth_device *dev, volatile void *packet, int length); + int ape_recv(struct eth_device *dev); + int ape_halt(struct eth_device *dev); + int ape_write_hwaddr(struct eth_device *dev); + +The init function checks the hardware (probing/identifying) 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 dev->enetaddr +member. 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). You should not return until the +transmission is complete, and you should leave the state such that the send +function can be called multiple times in a row. + +The recv function should process packets as long as the hardware has them +readily available before returning. i.e. you should drain the hardware fifo. +For each packet you receive, you should call the NetReceive() function on it +along with the packet length. The common code sets up packet buffers for you +already in the .bss (NetRxPackets), so there should be no need to allocate your +own. This doesn't mean you must use the NetRxPackets array however; you're +free to call the NetReceive() function with any buffer you wish. So the pseudo +code here would look something like: +int ape_recv(struct eth_device *dev) +{ + int length, i = 0; + ... + while (packets_are_available()) { + ... + length = ape_get_packet(&NetRxPackets[i]); + ... + NetReceive(&NetRxPackets[i], length); + ... + if (++i >= PKTBUFSRX) + i = 0; + ... + } + ... + return 0; +} + +The halt 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 +init function), so make sure it can handle this sort of thing. + +The write_hwaddr function should program the MAC address stored in dev->enetaddr +into the Ethernet controller. + +So the call graph at this stage would look something like: +some net operation (ping / tftp / whatever...) + eth_init() + dev->init() + eth_send() + dev->send() + eth_rx() + dev->recv() + eth_halt() + dev->halt() + +----------------------------- + CONFIG_MII / 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. + +After you have called eth_register() in your driver's register function, add +a call to miiphy_register() like so: +#if defined(CONFIG_MII) || defined(CONFIG_CMD_MII) + miiphy_register(dev->name, mii_read, mii_write); +#endif + +And then define the mii_read and mii_write functions if you haven't already. +Their syntax is straightforward: + int mii_read(char *devname, uchar addr, uchar reg, ushort *val); + int mii_write(char *devname, uchar addr, uchar reg, ushort val); + +The read function should read the register 'reg' from the phy at address 'addr' +and store the result in the pointer 'val'. The implementation for the write +function should logically follow. |