| !!! WARNING !!! | 
|   | 
| This guide describes to 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. See doc/driver-model/README.txt | 
|   | 
| ----------------------- | 
|  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; | 
|     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. | 
|   | 
|  ----------- | 
|   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 net_process_received_packet() function on it | 
| along with the packet length.  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 call the net_process_received_packet() 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(&net_rx_packets[i]); | 
|         ... | 
|         net_process_received_packet(&net_rx_packets[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_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. | 
|   | 
| After you have called eth_register() in your driver's register function, add | 
| a call to mdio_alloc() and mdio_register() like so: | 
|     bus = mdio_alloc(); | 
|     if (!bus) { | 
|         free(priv); | 
|         free(dev); | 
|         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. |