u-boot/doc/driver-model/UDM-design.txt
Marek Vasut 15c6935b0c dm: Initial import of design documents
This patch contains UDM-design.txt, which is document containing
general description of the driver model. The remaining files contains
descriptions of conversion process of particular subsystems.

Signed-off-by: Marek Vasut <marek.vasut@gmail.com>
2012-09-02 17:55:53 +02:00

315 lines
11 KiB
Text

The U-Boot Driver Model Project
===============================
Design document
===============
Marek Vasut <marek.vasut@gmail.com>
Pavel Herrmann <morpheus.ibis@gmail.com>
2012-05-17
I) The modular concept
----------------------
The driver core design is done with modularity in mind. The long-term plan is to
extend this modularity to allow loading not only drivers, but various other
objects into U-Boot at runtime -- like commands, support for other boards etc.
II) Driver core initialization stages
-------------------------------------
The drivers have to be initialized in two stages, since the U-Boot bootloader
runs in two stages itself. The first stage is the one which is executed before
the bootloader itself is relocated. The second stage then happens after
relocation.
1) First stage
--------------
The first stage runs after the bootloader did very basic hardware init. This
means the stack pointer was configured, caches disabled and that's about it.
The problem with this part is the memory management isn't running at all. To
make things even worse, at this point, the RAM is still likely uninitialized
and therefore unavailable.
2) Second stage
---------------
At this stage, the bootloader has initialized RAM and is running from it's
final location. Dynamic memory allocations are working at this point. Most of
the driver initialization is executed here.
III) The drivers
----------------
1) The structure of a driver
----------------------------
The driver will contain a structure located in a separate section, which
will allow linker to create a list of compiled-in drivers at compile time.
Let's call this list "driver_list".
struct driver __attribute__((section(driver_list))) {
/* The name of the driver */
char name[STATIC_CONFIG_DRIVER_NAME_LENGTH];
/*
* This function should connect this driver with cores it depends on and
* with other drivers, likely bus drivers
*/
int (*bind)(struct instance *i);
/* This function actually initializes the hardware. */
int (*probe)(struct instance *i);
/*
* The function of the driver called when U-Boot finished relocation.
* This is particularly important to eg. move pointers to DMA buffers
* and such from the location before relocation to their final location.
*/
int (*reloc)(struct instance *i);
/*
* This is called when the driver is shuting down, to deinitialize the
* hardware.
*/
int (*remove)(struct instance *i);
/* This is called to remove the driver from the driver tree */
int (*unbind)(struct instance *i);
/* This is a list of cores this driver depends on */
struct driver *cores[];
};
The cores[] array in here is very important. It allows u-boot to figure out,
in compile-time, which possible cores can be activated at runtime. Therefore
if there are cores that won't be ever activated, GCC LTO might remove them
from the final binary. Actually, this information might be used to drive build
of the cores.
FIXME: Should *cores[] be really struct driver, pointing to drivers that
represent the cores? Shouldn't it be core instance pointer?
2) Instantiation of a driver
----------------------------
The driver is instantiated by calling:
driver_bind(struct instance *bus, const struct driver_info *di)
The "struct instance *bus" is a pointer to a bus with which this driver should
be registered with. The "root" bus pointer is supplied to the board init
functions.
FIXME: We need some functions that will return list of busses of certain type
registered with the system so the user can find proper instance even if
he has no bus pointer (this will come handy if the user isn't
registering the driver from board init function, but somewhere else).
The "const struct driver_info *di" pointer points to a structure defining the
driver to be registered. The structure is defined as follows:
struct driver_info {
char name[STATIC_CONFIG_DRIVER_NAME_LENGTH];
void *platform_data;
}
The instantiation of a driver by calling driver_bind() creates an instance
of the driver by allocating "struct driver_instance". Note that only struct
instance is passed to the driver. The wrapping struct driver_instance is there
for purposes of the driver core:
struct driver_instance {
uint32_t flags;
struct instance i;
};
struct instance {
/* Pointer to a driver information passed by driver_register() */
const struct driver_info *info;
/* Pointer to a bus this driver is bound with */
struct instance *bus;
/* Pointer to this driver's own private data */
void *private_data;
/* Pointer to the first block of successor nodes (optional) */
struct successor_block *succ;
}
The instantiation of a driver does not mean the hardware is initialized. The
driver_bind() call only creates the instance of the driver, fills in the "bus"
pointer and calls the drivers' .bind() function. The .bind() function of the
driver should hook the driver with the remaining cores and/or drivers it
depends on.
It's important to note here, that in case the driver instance has multiple
parents, such parent can be connected with this instance by calling:
driver_link(struct instance *parent, struct instance *dev);
This will connect the other parent driver with the newly instantiated driver.
Note that this must be called after driver_bind() and before driver_acticate()
(driver_activate() will be explained below). To allow struct instance to have
multiple parent pointer, the struct instance *bus will utilize it's last bit
to indicate if this is a pointer to struct instance or to an array if
instances, struct successor block. The approach is similar as the approach to
*succ in struct instance, described in the following paragraph.
The last pointer of the struct instance, the pointer to successor nodes, is
used only in case of a bus driver. Otherwise the pointer contains NULL value.
The last bit of this field indicates if this is a bus having a single child
node (so the last bit is 0) or if this bus has multiple child nodes (the last
bit is 1). In the former case, the driver core should clear the last bit and
this pointer points directly to the child node. In the later case of a bus
driver, the pointer points to an instance of structure:
struct successor_block {
/* Array of pointers to instances of devices attached to this bus */
struct instance *dev[BLOCKING_FACTOR];
/* Pointer to next block of successors */
struct successor_block *next;
}
Some of the *dev[] array members might be NULL in case there are no more
devices attached. The *next is NULL in case the list of attached devices
doesn't continue anymore. The BLOCKING_FACTOR is used to allocate multiple
slots for successor devices at once to avoid fragmentation of memory.
3) The bind() function of a driver
----------------------------------
The bind function of a driver connects the driver with various cores the
driver provides functions for. The driver model related part will look like
the following example for a bus driver:
int driver_bind(struct instance *in)
{
...
core_bind(&core_i2c_static_instance, in, i2c_bus_funcs);
...
}
FIXME: What if we need to run-time determine, depending on some hardware
register, what kind of i2c_bus_funcs to pass?
This makes the i2c core aware of a new bus. The i2c_bus_funcs is a constant
structure of functions any i2c bus driver must provide to work. This will
allow the i2c command operate with the bus. The core_i2c_static_instance is
the pointer to the instance of a core this driver provides function to.
FIXME: Maybe replace "core-i2c" with CORE_I2C global pointer to an instance of
the core?
4) The instantiation of a core driver
-------------------------------------
The core driver is special in the way that it's single-instance driver. It is
always present in the system, though it might not be activated. The fact that
it's single instance allows it to be instantiated at compile time.
Therefore, all possible structures of this driver can be in read-only memory,
especially struct driver and struct driver_instance. But the successor list,
which needs special treatment.
To solve the problem with a successor list and the core driver flags, a new
entry in struct gd (global data) will be introduced. This entry will point to
runtime allocated array of struct driver_instance. It will be possible to
allocate the exact amount of struct driver_instance necessary, as the number
of cores that might be activated will be known at compile time. The cores will
then behave like any usual driver.
Pointers to the struct instance of cores can be computed at compile time,
therefore allowing the resulting u-boot binary to save some overhead.
5) The probe() function of a driver
-----------------------------------
The probe function of a driver allocates necessary resources and does required
initialization of the hardware itself. This is usually called only when the
driver is needed, as a part of the defered probe mechanism.
The driver core should implement a function called
int driver_activate(struct instance *in);
which should call the .probe() function of the driver and then configure the
state of the driver instance to "ACTIVATED". This state of a driver instance
should be stored in a wrap-around structure for the structure instance, the
struct driver_instance.
6) The command side interface to a driver
-----------------------------------------
The U-Boot command shall communicate only with the specific driver core. The
driver core in turn exports necessary API towards the command.
7) Demonstration imaginary board
--------------------------------
Consider the following computer:
*
|
+-- System power management logic
|
+-- CPU clock controlling logc
|
+-- NAND controller
| |
| +-- NAND flash chip
|
+-- 128MB of DDR DRAM
|
+-- I2C bus #0
| |
| +-- RTC
| |
| +-- EEPROM #0
| |
| +-- EEPROM #1
|
+-- USB host-only IP core
| |
| +-- USB storage device
|
+-- USB OTG-capable IP core
| |
| +-- connection to the host PC
|
+-- GPIO
| |
| +-- User LED #0
| |
| +-- User LED #1
|
+-- UART0
|
+-- UART1
|
+-- Ethernet controller #0
|
+-- Ethernet controller #1
|
+-- Audio codec
|
+-- PCI bridge
| |
| +-- Ethernet controller #2
| |
| +-- SPI host card
| | |
| | +-- Audio amplifier (must be operational before codec)
| |
| +-- GPIO host card
| |
| +-- User LED #2
|
+-- LCD controller
|
+-- PWM controller (must be enabled after LCD controller)
|
+-- SPI host controller
| |
| +-- SD/MMC connected via SPI
| |
| +-- SPI flash
|
+-- CPLD/FPGA with stored configuration of the board