avionic design with actual uboot and tooling
submodule of avionic design uboot bootloader and with included tools to get you started , read readme.md and readme-tk1-loader.md
This commit is contained in:
886
u-boot/doc/driver-model/README.txt
Normal file
886
u-boot/doc/driver-model/README.txt
Normal file
@@ -0,0 +1,886 @@
|
||||
Driver Model
|
||||
============
|
||||
|
||||
This README contains high-level information about driver model, a unified
|
||||
way of declaring and accessing drivers in U-Boot. The original work was done
|
||||
by:
|
||||
|
||||
Marek Vasut <marex@denx.de>
|
||||
Pavel Herrmann <morpheus.ibis@gmail.com>
|
||||
Viktor Křivák <viktor.krivak@gmail.com>
|
||||
Tomas Hlavacek <tmshlvck@gmail.com>
|
||||
|
||||
This has been both simplified and extended into the current implementation
|
||||
by:
|
||||
|
||||
Simon Glass <sjg@chromium.org>
|
||||
|
||||
|
||||
Terminology
|
||||
-----------
|
||||
|
||||
Uclass - a group of devices which operate in the same way. A uclass provides
|
||||
a way of accessing individual devices within the group, but always
|
||||
using the same interface. For example a GPIO uclass provides
|
||||
operations for get/set value. An I2C uclass may have 10 I2C ports,
|
||||
4 with one driver, and 6 with another.
|
||||
|
||||
Driver - some code which talks to a peripheral and presents a higher-level
|
||||
interface to it.
|
||||
|
||||
Device - an instance of a driver, tied to a particular port or peripheral.
|
||||
|
||||
|
||||
How to try it
|
||||
-------------
|
||||
|
||||
Build U-Boot sandbox and run it:
|
||||
|
||||
make sandbox_defconfig
|
||||
make
|
||||
./u-boot -d u-boot.dtb
|
||||
|
||||
(type 'reset' to exit U-Boot)
|
||||
|
||||
|
||||
There is a uclass called 'demo'. This uclass handles
|
||||
saying hello, and reporting its status. There are two drivers in this
|
||||
uclass:
|
||||
|
||||
- simple: Just prints a message for hello, doesn't implement status
|
||||
- shape: Prints shapes and reports number of characters printed as status
|
||||
|
||||
The demo class is pretty simple, but not trivial. The intention is that it
|
||||
can be used for testing, so it will implement all driver model features and
|
||||
provide good code coverage of them. It does have multiple drivers, it
|
||||
handles parameter data and platdata (data which tells the driver how
|
||||
to operate on a particular platform) and it uses private driver data.
|
||||
|
||||
To try it, see the example session below:
|
||||
|
||||
=>demo hello 1
|
||||
Hello '@' from 07981110: red 4
|
||||
=>demo status 2
|
||||
Status: 0
|
||||
=>demo hello 2
|
||||
g
|
||||
r@
|
||||
e@@
|
||||
e@@@
|
||||
n@@@@
|
||||
g@@@@@
|
||||
=>demo status 2
|
||||
Status: 21
|
||||
=>demo hello 4 ^
|
||||
y^^^
|
||||
e^^^^^
|
||||
l^^^^^^^
|
||||
l^^^^^^^
|
||||
o^^^^^
|
||||
w^^^
|
||||
=>demo status 4
|
||||
Status: 36
|
||||
=>
|
||||
|
||||
|
||||
Running the tests
|
||||
-----------------
|
||||
|
||||
The intent with driver model is that the core portion has 100% test coverage
|
||||
in sandbox, and every uclass has its own test. As a move towards this, tests
|
||||
are provided in test/dm. To run them, try:
|
||||
|
||||
./test/py/test.py --bd sandbox --build -k ut_dm -v
|
||||
|
||||
You should see something like this:
|
||||
|
||||
(venv)$ ./test/py/test.py --bd sandbox --build -k ut_dm -v
|
||||
+make O=/root/u-boot/build-sandbox -s sandbox_defconfig
|
||||
+make O=/root/u-boot/build-sandbox -s -j8
|
||||
============================= test session starts ==============================
|
||||
platform linux2 -- Python 2.7.5, pytest-2.9.0, py-1.4.31, pluggy-0.3.1 -- /root/u-boot/venv/bin/python
|
||||
cachedir: .cache
|
||||
rootdir: /root/u-boot, inifile:
|
||||
collected 199 items
|
||||
|
||||
test/py/tests/test_ut.py::test_ut_dm_init PASSED
|
||||
test/py/tests/test_ut.py::test_ut[ut_dm_adc_bind] PASSED
|
||||
test/py/tests/test_ut.py::test_ut[ut_dm_adc_multi_channel_conversion] PASSED
|
||||
test/py/tests/test_ut.py::test_ut[ut_dm_adc_multi_channel_shot] PASSED
|
||||
test/py/tests/test_ut.py::test_ut[ut_dm_adc_single_channel_conversion] PASSED
|
||||
test/py/tests/test_ut.py::test_ut[ut_dm_adc_single_channel_shot] PASSED
|
||||
test/py/tests/test_ut.py::test_ut[ut_dm_adc_supply] PASSED
|
||||
test/py/tests/test_ut.py::test_ut[ut_dm_adc_wrong_channel_selection] PASSED
|
||||
test/py/tests/test_ut.py::test_ut[ut_dm_autobind] PASSED
|
||||
test/py/tests/test_ut.py::test_ut[ut_dm_autobind_uclass_pdata_alloc] PASSED
|
||||
test/py/tests/test_ut.py::test_ut[ut_dm_autobind_uclass_pdata_valid] PASSED
|
||||
test/py/tests/test_ut.py::test_ut[ut_dm_autoprobe] PASSED
|
||||
test/py/tests/test_ut.py::test_ut[ut_dm_bus_child_post_bind] PASSED
|
||||
test/py/tests/test_ut.py::test_ut[ut_dm_bus_child_post_bind_uclass] PASSED
|
||||
test/py/tests/test_ut.py::test_ut[ut_dm_bus_child_pre_probe_uclass] PASSED
|
||||
test/py/tests/test_ut.py::test_ut[ut_dm_bus_children] PASSED
|
||||
test/py/tests/test_ut.py::test_ut[ut_dm_bus_children_funcs] PASSED
|
||||
test/py/tests/test_ut.py::test_ut[ut_dm_bus_children_iterators] PASSED
|
||||
test/py/tests/test_ut.py::test_ut[ut_dm_bus_parent_data] PASSED
|
||||
test/py/tests/test_ut.py::test_ut[ut_dm_bus_parent_data_uclass] PASSED
|
||||
test/py/tests/test_ut.py::test_ut[ut_dm_bus_parent_ops] PASSED
|
||||
test/py/tests/test_ut.py::test_ut[ut_dm_bus_parent_platdata] PASSED
|
||||
test/py/tests/test_ut.py::test_ut[ut_dm_bus_parent_platdata_uclass] PASSED
|
||||
test/py/tests/test_ut.py::test_ut[ut_dm_children] PASSED
|
||||
test/py/tests/test_ut.py::test_ut[ut_dm_clk_base] PASSED
|
||||
test/py/tests/test_ut.py::test_ut[ut_dm_clk_periph] PASSED
|
||||
test/py/tests/test_ut.py::test_ut[ut_dm_device_get_uclass_id] PASSED
|
||||
test/py/tests/test_ut.py::test_ut[ut_dm_eth] PASSED
|
||||
test/py/tests/test_ut.py::test_ut[ut_dm_eth_act] PASSED
|
||||
test/py/tests/test_ut.py::test_ut[ut_dm_eth_alias] PASSED
|
||||
test/py/tests/test_ut.py::test_ut[ut_dm_eth_prime] PASSED
|
||||
test/py/tests/test_ut.py::test_ut[ut_dm_eth_rotate] PASSED
|
||||
test/py/tests/test_ut.py::test_ut[ut_dm_fdt] PASSED
|
||||
test/py/tests/test_ut.py::test_ut[ut_dm_fdt_offset] PASSED
|
||||
test/py/tests/test_ut.py::test_ut[ut_dm_fdt_pre_reloc] PASSED
|
||||
test/py/tests/test_ut.py::test_ut[ut_dm_fdt_uclass_seq] PASSED
|
||||
test/py/tests/test_ut.py::test_ut[ut_dm_gpio] PASSED
|
||||
test/py/tests/test_ut.py::test_ut[ut_dm_gpio_anon] PASSED
|
||||
test/py/tests/test_ut.py::test_ut[ut_dm_gpio_copy] PASSED
|
||||
test/py/tests/test_ut.py::test_ut[ut_dm_gpio_leak] PASSED
|
||||
test/py/tests/test_ut.py::test_ut[ut_dm_gpio_phandles] PASSED
|
||||
test/py/tests/test_ut.py::test_ut[ut_dm_gpio_requestf] PASSED
|
||||
test/py/tests/test_ut.py::test_ut[ut_dm_i2c_bytewise] PASSED
|
||||
test/py/tests/test_ut.py::test_ut[ut_dm_i2c_find] PASSED
|
||||
test/py/tests/test_ut.py::test_ut[ut_dm_i2c_offset] PASSED
|
||||
test/py/tests/test_ut.py::test_ut[ut_dm_i2c_offset_len] PASSED
|
||||
test/py/tests/test_ut.py::test_ut[ut_dm_i2c_probe_empty] PASSED
|
||||
test/py/tests/test_ut.py::test_ut[ut_dm_i2c_read_write] PASSED
|
||||
test/py/tests/test_ut.py::test_ut[ut_dm_i2c_speed] PASSED
|
||||
test/py/tests/test_ut.py::test_ut[ut_dm_leak] PASSED
|
||||
test/py/tests/test_ut.py::test_ut[ut_dm_led_base] PASSED
|
||||
test/py/tests/test_ut.py::test_ut[ut_dm_led_gpio] PASSED
|
||||
test/py/tests/test_ut.py::test_ut[ut_dm_led_label] PASSED
|
||||
test/py/tests/test_ut.py::test_ut[ut_dm_lifecycle] PASSED
|
||||
test/py/tests/test_ut.py::test_ut[ut_dm_mmc_base] PASSED
|
||||
test/py/tests/test_ut.py::test_ut[ut_dm_net_retry] PASSED
|
||||
test/py/tests/test_ut.py::test_ut[ut_dm_operations] PASSED
|
||||
test/py/tests/test_ut.py::test_ut[ut_dm_ordering] PASSED
|
||||
test/py/tests/test_ut.py::test_ut[ut_dm_pci_base] PASSED
|
||||
test/py/tests/test_ut.py::test_ut[ut_dm_pci_busnum] PASSED
|
||||
test/py/tests/test_ut.py::test_ut[ut_dm_pci_swapcase] PASSED
|
||||
test/py/tests/test_ut.py::test_ut[ut_dm_platdata] PASSED
|
||||
test/py/tests/test_ut.py::test_ut[ut_dm_power_pmic_get] PASSED
|
||||
test/py/tests/test_ut.py::test_ut[ut_dm_power_pmic_io] PASSED
|
||||
test/py/tests/test_ut.py::test_ut[ut_dm_power_regulator_autoset] PASSED
|
||||
test/py/tests/test_ut.py::test_ut[ut_dm_power_regulator_autoset_list] PASSED
|
||||
test/py/tests/test_ut.py::test_ut[ut_dm_power_regulator_get] PASSED
|
||||
test/py/tests/test_ut.py::test_ut[ut_dm_power_regulator_set_get_current] PASSED
|
||||
test/py/tests/test_ut.py::test_ut[ut_dm_power_regulator_set_get_enable] PASSED
|
||||
test/py/tests/test_ut.py::test_ut[ut_dm_power_regulator_set_get_mode] PASSED
|
||||
test/py/tests/test_ut.py::test_ut[ut_dm_power_regulator_set_get_voltage] PASSED
|
||||
test/py/tests/test_ut.py::test_ut[ut_dm_pre_reloc] PASSED
|
||||
test/py/tests/test_ut.py::test_ut[ut_dm_ram_base] PASSED
|
||||
test/py/tests/test_ut.py::test_ut[ut_dm_regmap_base] PASSED
|
||||
test/py/tests/test_ut.py::test_ut[ut_dm_regmap_syscon] PASSED
|
||||
test/py/tests/test_ut.py::test_ut[ut_dm_remoteproc_base] PASSED
|
||||
test/py/tests/test_ut.py::test_ut[ut_dm_remove] PASSED
|
||||
test/py/tests/test_ut.py::test_ut[ut_dm_reset_base] PASSED
|
||||
test/py/tests/test_ut.py::test_ut[ut_dm_reset_walk] PASSED
|
||||
test/py/tests/test_ut.py::test_ut[ut_dm_rtc_base] PASSED
|
||||
test/py/tests/test_ut.py::test_ut[ut_dm_rtc_dual] PASSED
|
||||
test/py/tests/test_ut.py::test_ut[ut_dm_rtc_reset] PASSED
|
||||
test/py/tests/test_ut.py::test_ut[ut_dm_rtc_set_get] PASSED
|
||||
test/py/tests/test_ut.py::test_ut[ut_dm_spi_find] PASSED
|
||||
test/py/tests/test_ut.py::test_ut[ut_dm_spi_flash] PASSED
|
||||
test/py/tests/test_ut.py::test_ut[ut_dm_spi_xfer] PASSED
|
||||
test/py/tests/test_ut.py::test_ut[ut_dm_syscon_base] PASSED
|
||||
test/py/tests/test_ut.py::test_ut[ut_dm_syscon_by_driver_data] PASSED
|
||||
test/py/tests/test_ut.py::test_ut[ut_dm_timer_base] PASSED
|
||||
test/py/tests/test_ut.py::test_ut[ut_dm_uclass] PASSED
|
||||
test/py/tests/test_ut.py::test_ut[ut_dm_uclass_before_ready] PASSED
|
||||
test/py/tests/test_ut.py::test_ut[ut_dm_uclass_devices_find] PASSED
|
||||
test/py/tests/test_ut.py::test_ut[ut_dm_uclass_devices_find_by_name] PASSED
|
||||
test/py/tests/test_ut.py::test_ut[ut_dm_uclass_devices_get] PASSED
|
||||
test/py/tests/test_ut.py::test_ut[ut_dm_uclass_devices_get_by_name] PASSED
|
||||
test/py/tests/test_ut.py::test_ut[ut_dm_usb_base] PASSED
|
||||
test/py/tests/test_ut.py::test_ut[ut_dm_usb_flash] PASSED
|
||||
test/py/tests/test_ut.py::test_ut[ut_dm_usb_keyb] PASSED
|
||||
test/py/tests/test_ut.py::test_ut[ut_dm_usb_multi] PASSED
|
||||
test/py/tests/test_ut.py::test_ut[ut_dm_usb_remove] PASSED
|
||||
test/py/tests/test_ut.py::test_ut[ut_dm_usb_tree] PASSED
|
||||
test/py/tests/test_ut.py::test_ut[ut_dm_usb_tree_remove] PASSED
|
||||
test/py/tests/test_ut.py::test_ut[ut_dm_usb_tree_reorder] PASSED
|
||||
test/py/tests/test_ut.py::test_ut[ut_dm_video_base] PASSED
|
||||
test/py/tests/test_ut.py::test_ut[ut_dm_video_bmp] PASSED
|
||||
test/py/tests/test_ut.py::test_ut[ut_dm_video_bmp_comp] PASSED
|
||||
test/py/tests/test_ut.py::test_ut[ut_dm_video_chars] PASSED
|
||||
test/py/tests/test_ut.py::test_ut[ut_dm_video_context] PASSED
|
||||
test/py/tests/test_ut.py::test_ut[ut_dm_video_rotation1] PASSED
|
||||
test/py/tests/test_ut.py::test_ut[ut_dm_video_rotation2] PASSED
|
||||
test/py/tests/test_ut.py::test_ut[ut_dm_video_rotation3] PASSED
|
||||
test/py/tests/test_ut.py::test_ut[ut_dm_video_text] PASSED
|
||||
test/py/tests/test_ut.py::test_ut[ut_dm_video_truetype] PASSED
|
||||
test/py/tests/test_ut.py::test_ut[ut_dm_video_truetype_bs] PASSED
|
||||
test/py/tests/test_ut.py::test_ut[ut_dm_video_truetype_scroll] PASSED
|
||||
|
||||
======================= 84 tests deselected by '-kut_dm' =======================
|
||||
================== 115 passed, 84 deselected in 3.77 seconds ===================
|
||||
|
||||
What is going on?
|
||||
-----------------
|
||||
|
||||
Let's start at the top. The demo command is in common/cmd_demo.c. It does
|
||||
the usual command processing and then:
|
||||
|
||||
struct udevice *demo_dev;
|
||||
|
||||
ret = uclass_get_device(UCLASS_DEMO, devnum, &demo_dev);
|
||||
|
||||
UCLASS_DEMO means the class of devices which implement 'demo'. Other
|
||||
classes might be MMC, or GPIO, hashing or serial. The idea is that the
|
||||
devices in the class all share a particular way of working. The class
|
||||
presents a unified view of all these devices to U-Boot.
|
||||
|
||||
This function looks up a device for the demo uclass. Given a device
|
||||
number we can find the device because all devices have registered with
|
||||
the UCLASS_DEMO uclass.
|
||||
|
||||
The device is automatically activated ready for use by uclass_get_device().
|
||||
|
||||
Now that we have the device we can do things like:
|
||||
|
||||
return demo_hello(demo_dev, ch);
|
||||
|
||||
This function is in the demo uclass. It takes care of calling the 'hello'
|
||||
method of the relevant driver. Bearing in mind that there are two drivers,
|
||||
this particular device may use one or other of them.
|
||||
|
||||
The code for demo_hello() is in drivers/demo/demo-uclass.c:
|
||||
|
||||
int demo_hello(struct udevice *dev, int ch)
|
||||
{
|
||||
const struct demo_ops *ops = device_get_ops(dev);
|
||||
|
||||
if (!ops->hello)
|
||||
return -ENOSYS;
|
||||
|
||||
return ops->hello(dev, ch);
|
||||
}
|
||||
|
||||
As you can see it just calls the relevant driver method. One of these is
|
||||
in drivers/demo/demo-simple.c:
|
||||
|
||||
static int simple_hello(struct udevice *dev, int ch)
|
||||
{
|
||||
const struct dm_demo_pdata *pdata = dev_get_platdata(dev);
|
||||
|
||||
printf("Hello from %08x: %s %d\n", map_to_sysmem(dev),
|
||||
pdata->colour, pdata->sides);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
So that is a trip from top (command execution) to bottom (driver action)
|
||||
but it leaves a lot of topics to address.
|
||||
|
||||
|
||||
Declaring Drivers
|
||||
-----------------
|
||||
|
||||
A driver declaration looks something like this (see
|
||||
drivers/demo/demo-shape.c):
|
||||
|
||||
static const struct demo_ops shape_ops = {
|
||||
.hello = shape_hello,
|
||||
.status = shape_status,
|
||||
};
|
||||
|
||||
U_BOOT_DRIVER(demo_shape_drv) = {
|
||||
.name = "demo_shape_drv",
|
||||
.id = UCLASS_DEMO,
|
||||
.ops = &shape_ops,
|
||||
.priv_data_size = sizeof(struct shape_data),
|
||||
};
|
||||
|
||||
|
||||
This driver has two methods (hello and status) and requires a bit of
|
||||
private data (accessible through dev_get_priv(dev) once the driver has
|
||||
been probed). It is a member of UCLASS_DEMO so will register itself
|
||||
there.
|
||||
|
||||
In U_BOOT_DRIVER it is also possible to specify special methods for bind
|
||||
and unbind, and these are called at appropriate times. For many drivers
|
||||
it is hoped that only 'probe' and 'remove' will be needed.
|
||||
|
||||
The U_BOOT_DRIVER macro creates a data structure accessible from C,
|
||||
so driver model can find the drivers that are available.
|
||||
|
||||
The methods a device can provide are documented in the device.h header.
|
||||
Briefly, they are:
|
||||
|
||||
bind - make the driver model aware of a device (bind it to its driver)
|
||||
unbind - make the driver model forget the device
|
||||
ofdata_to_platdata - convert device tree data to platdata - see later
|
||||
probe - make a device ready for use
|
||||
remove - remove a device so it cannot be used until probed again
|
||||
|
||||
The sequence to get a device to work is bind, ofdata_to_platdata (if using
|
||||
device tree) and probe.
|
||||
|
||||
|
||||
Platform Data
|
||||
-------------
|
||||
|
||||
*** Note: platform data is the old way of doing things. It is
|
||||
*** basically a C structure which is passed to drivers to tell them about
|
||||
*** platform-specific settings like the address of its registers, bus
|
||||
*** speed, etc. Device tree is now the preferred way of handling this.
|
||||
*** Unless you have a good reason not to use device tree (the main one
|
||||
*** being you need serial support in SPL and don't have enough SRAM for
|
||||
*** the cut-down device tree and libfdt libraries) you should stay away
|
||||
*** from platform data.
|
||||
|
||||
Platform data is like Linux platform data, if you are familiar with that.
|
||||
It provides the board-specific information to start up a device.
|
||||
|
||||
Why is this information not just stored in the device driver itself? The
|
||||
idea is that the device driver is generic, and can in principle operate on
|
||||
any board that has that type of device. For example, with modern
|
||||
highly-complex SoCs it is common for the IP to come from an IP vendor, and
|
||||
therefore (for example) the MMC controller may be the same on chips from
|
||||
different vendors. It makes no sense to write independent drivers for the
|
||||
MMC controller on each vendor's SoC, when they are all almost the same.
|
||||
Similarly, we may have 6 UARTs in an SoC, all of which are mostly the same,
|
||||
but lie at different addresses in the address space.
|
||||
|
||||
Using the UART example, we have a single driver and it is instantiated 6
|
||||
times by supplying 6 lots of platform data. Each lot of platform data
|
||||
gives the driver name and a pointer to a structure containing information
|
||||
about this instance - e.g. the address of the register space. It may be that
|
||||
one of the UARTS supports RS-485 operation - this can be added as a flag in
|
||||
the platform data, which is set for this one port and clear for the rest.
|
||||
|
||||
Think of your driver as a generic piece of code which knows how to talk to
|
||||
a device, but needs to know where it is, any variant/option information and
|
||||
so on. Platform data provides this link between the generic piece of code
|
||||
and the specific way it is bound on a particular board.
|
||||
|
||||
Examples of platform data include:
|
||||
|
||||
- The base address of the IP block's register space
|
||||
- Configuration options, like:
|
||||
- the SPI polarity and maximum speed for a SPI controller
|
||||
- the I2C speed to use for an I2C device
|
||||
- the number of GPIOs available in a GPIO device
|
||||
|
||||
Where does the platform data come from? It is either held in a structure
|
||||
which is compiled into U-Boot, or it can be parsed from the Device Tree
|
||||
(see 'Device Tree' below).
|
||||
|
||||
For an example of how it can be compiled in, see demo-pdata.c which
|
||||
sets up a table of driver names and their associated platform data.
|
||||
The data can be interpreted by the drivers however they like - it is
|
||||
basically a communication scheme between the board-specific code and
|
||||
the generic drivers, which are intended to work on any board.
|
||||
|
||||
Drivers can access their data via dev->info->platdata. Here is
|
||||
the declaration for the platform data, which would normally appear
|
||||
in the board file.
|
||||
|
||||
static const struct dm_demo_cdata red_square = {
|
||||
.colour = "red",
|
||||
.sides = 4.
|
||||
};
|
||||
static const struct driver_info info[] = {
|
||||
{
|
||||
.name = "demo_shape_drv",
|
||||
.platdata = &red_square,
|
||||
},
|
||||
};
|
||||
|
||||
demo1 = driver_bind(root, &info[0]);
|
||||
|
||||
|
||||
Device Tree
|
||||
-----------
|
||||
|
||||
While platdata is useful, a more flexible way of providing device data is
|
||||
by using device tree. In U-Boot you should use this where possible. Avoid
|
||||
sending patches which make use of the U_BOOT_DEVICE() macro unless strictly
|
||||
necessary.
|
||||
|
||||
With device tree we replace the above code with the following device tree
|
||||
fragment:
|
||||
|
||||
red-square {
|
||||
compatible = "demo-shape";
|
||||
colour = "red";
|
||||
sides = <4>;
|
||||
};
|
||||
|
||||
This means that instead of having lots of U_BOOT_DEVICE() declarations in
|
||||
the board file, we put these in the device tree. This approach allows a lot
|
||||
more generality, since the same board file can support many types of boards
|
||||
(e,g. with the same SoC) just by using different device trees. An added
|
||||
benefit is that the Linux device tree can be used, thus further simplifying
|
||||
the task of board-bring up either for U-Boot or Linux devs (whoever gets to
|
||||
the board first!).
|
||||
|
||||
The easiest way to make this work it to add a few members to the driver:
|
||||
|
||||
.platdata_auto_alloc_size = sizeof(struct dm_test_pdata),
|
||||
.ofdata_to_platdata = testfdt_ofdata_to_platdata,
|
||||
|
||||
The 'auto_alloc' feature allowed space for the platdata to be allocated
|
||||
and zeroed before the driver's ofdata_to_platdata() method is called. The
|
||||
ofdata_to_platdata() method, which the driver write supplies, should parse
|
||||
the device tree node for this device and place it in dev->platdata. Thus
|
||||
when the probe method is called later (to set up the device ready for use)
|
||||
the platform data will be present.
|
||||
|
||||
Note that both methods are optional. If you provide an ofdata_to_platdata
|
||||
method then it will be called first (during activation). If you provide a
|
||||
probe method it will be called next. See Driver Lifecycle below for more
|
||||
details.
|
||||
|
||||
If you don't want to have the platdata automatically allocated then you
|
||||
can leave out platdata_auto_alloc_size. In this case you can use malloc
|
||||
in your ofdata_to_platdata (or probe) method to allocate the required memory,
|
||||
and you should free it in the remove method.
|
||||
|
||||
The driver model tree is intended to mirror that of the device tree. The
|
||||
root driver is at device tree offset 0 (the root node, '/'), and its
|
||||
children are the children of the root node.
|
||||
|
||||
|
||||
Declaring Uclasses
|
||||
------------------
|
||||
|
||||
The demo uclass is declared like this:
|
||||
|
||||
U_BOOT_CLASS(demo) = {
|
||||
.id = UCLASS_DEMO,
|
||||
};
|
||||
|
||||
It is also possible to specify special methods for probe, etc. The uclass
|
||||
numbering comes from include/dm/uclass.h. To add a new uclass, add to the
|
||||
end of the enum there, then declare your uclass as above.
|
||||
|
||||
|
||||
Device Sequence Numbers
|
||||
-----------------------
|
||||
|
||||
U-Boot numbers devices from 0 in many situations, such as in the command
|
||||
line for I2C and SPI buses, and the device names for serial ports (serial0,
|
||||
serial1, ...). Driver model supports this numbering and permits devices
|
||||
to be locating by their 'sequence'. This numbering uniquely identifies a
|
||||
device in its uclass, so no two devices within a particular uclass can have
|
||||
the same sequence number.
|
||||
|
||||
Sequence numbers start from 0 but gaps are permitted. For example, a board
|
||||
may have I2C buses 1, 4, 5 but no 0, 2 or 3. The choice of how devices are
|
||||
numbered is up to a particular board, and may be set by the SoC in some
|
||||
cases. While it might be tempting to automatically renumber the devices
|
||||
where there are gaps in the sequence, this can lead to confusion and is
|
||||
not the way that U-Boot works.
|
||||
|
||||
Each device can request a sequence number. If none is required then the
|
||||
device will be automatically allocated the next available sequence number.
|
||||
|
||||
To specify the sequence number in the device tree an alias is typically
|
||||
used. Make sure that the uclass has the DM_UC_FLAG_SEQ_ALIAS flag set.
|
||||
|
||||
aliases {
|
||||
serial2 = "/serial@22230000";
|
||||
};
|
||||
|
||||
This indicates that in the uclass called "serial", the named node
|
||||
("/serial@22230000") will be given sequence number 2. Any command or driver
|
||||
which requests serial device 2 will obtain this device.
|
||||
|
||||
More commonly you can use node references, which expand to the full path:
|
||||
|
||||
aliases {
|
||||
serial2 = &serial_2;
|
||||
};
|
||||
...
|
||||
serial_2: serial@22230000 {
|
||||
...
|
||||
};
|
||||
|
||||
The alias resolves to the same string in this case, but this version is
|
||||
easier to read.
|
||||
|
||||
Device sequence numbers are resolved when a device is probed. Before then
|
||||
the sequence number is only a request which may or may not be honoured,
|
||||
depending on what other devices have been probed. However the numbering is
|
||||
entirely under the control of the board author so a conflict is generally
|
||||
an error.
|
||||
|
||||
|
||||
Bus Drivers
|
||||
-----------
|
||||
|
||||
A common use of driver model is to implement a bus, a device which provides
|
||||
access to other devices. Example of buses include SPI and I2C. Typically
|
||||
the bus provides some sort of transport or translation that makes it
|
||||
possible to talk to the devices on the bus.
|
||||
|
||||
Driver model provides some useful features to help with implementing buses.
|
||||
Firstly, a bus can request that its children store some 'parent data' which
|
||||
can be used to keep track of child state. Secondly, the bus can define
|
||||
methods which are called when a child is probed or removed. This is similar
|
||||
to the methods the uclass driver provides. Thirdly, per-child platform data
|
||||
can be provided to specify things like the child's address on the bus. This
|
||||
persists across child probe()/remove() cycles.
|
||||
|
||||
For consistency and ease of implementation, the bus uclass can specify the
|
||||
per-child platform data, so that it can be the same for all children of buses
|
||||
in that uclass. There are also uclass methods which can be called when
|
||||
children are bound and probed.
|
||||
|
||||
Here an explanation of how a bus fits with a uclass may be useful. Consider
|
||||
a USB bus with several devices attached to it, each from a different (made
|
||||
up) uclass:
|
||||
|
||||
xhci_usb (UCLASS_USB)
|
||||
eth (UCLASS_ETHERNET)
|
||||
camera (UCLASS_CAMERA)
|
||||
flash (UCLASS_FLASH_STORAGE)
|
||||
|
||||
Each of the devices is connected to a different address on the USB bus.
|
||||
The bus device wants to store this address and some other information such
|
||||
as the bus speed for each device.
|
||||
|
||||
To achieve this, the bus device can use dev->parent_platdata in each of its
|
||||
three children. This can be auto-allocated if the bus driver (or bus uclass)
|
||||
has a non-zero value for per_child_platdata_auto_alloc_size. If not, then
|
||||
the bus device or uclass can allocate the space itself before the child
|
||||
device is probed.
|
||||
|
||||
Also the bus driver can define the child_pre_probe() and child_post_remove()
|
||||
methods to allow it to do some processing before the child is activated or
|
||||
after it is deactivated.
|
||||
|
||||
Similarly the bus uclass can define the child_post_bind() method to obtain
|
||||
the per-child platform data from the device tree and set it up for the child.
|
||||
The bus uclass can also provide a child_pre_probe() method. Very often it is
|
||||
the bus uclass that controls these features, since it avoids each driver
|
||||
having to do the same processing. Of course the driver can still tweak and
|
||||
override these activities.
|
||||
|
||||
Note that the information that controls this behaviour is in the bus's
|
||||
driver, not the child's. In fact it is possible that child has no knowledge
|
||||
that it is connected to a bus. The same child device may even be used on two
|
||||
different bus types. As an example. the 'flash' device shown above may also
|
||||
be connected on a SATA bus or standalone with no bus:
|
||||
|
||||
xhci_usb (UCLASS_USB)
|
||||
flash (UCLASS_FLASH_STORAGE) - parent data/methods defined by USB bus
|
||||
|
||||
sata (UCLASS_SATA)
|
||||
flash (UCLASS_FLASH_STORAGE) - parent data/methods defined by SATA bus
|
||||
|
||||
flash (UCLASS_FLASH_STORAGE) - no parent data/methods (not on a bus)
|
||||
|
||||
Above you can see that the driver for xhci_usb/sata controls the child's
|
||||
bus methods. In the third example the device is not on a bus, and therefore
|
||||
will not have these methods at all. Consider the case where the flash
|
||||
device defines child methods. These would be used for *its* children, and
|
||||
would be quite separate from the methods defined by the driver for the bus
|
||||
that the flash device is connetced to. The act of attaching a device to a
|
||||
parent device which is a bus, causes the device to start behaving like a
|
||||
bus device, regardless of its own views on the matter.
|
||||
|
||||
The uclass for the device can also contain data private to that uclass.
|
||||
But note that each device on the bus may be a memeber of a different
|
||||
uclass, and this data has nothing to do with the child data for each child
|
||||
on the bus. It is the bus' uclass that controls the child with respect to
|
||||
the bus.
|
||||
|
||||
|
||||
Driver Lifecycle
|
||||
----------------
|
||||
|
||||
Here are the stages that a device goes through in driver model. Note that all
|
||||
methods mentioned here are optional - e.g. if there is no probe() method for
|
||||
a device then it will not be called. A simple device may have very few
|
||||
methods actually defined.
|
||||
|
||||
1. Bind stage
|
||||
|
||||
U-Boot discovers devices using one of these two methods:
|
||||
|
||||
- Scan the U_BOOT_DEVICE() definitions. U-Boot looks up the name specified
|
||||
by each, to find the appropriate U_BOOT_DRIVER() definition. In this case,
|
||||
there is no path by which driver_data may be provided, but the U_BOOT_DEVICE()
|
||||
may provide platdata.
|
||||
|
||||
- Scan through the device tree definitions. U-Boot looks at top-level
|
||||
nodes in the the device tree. It looks at the compatible string in each node
|
||||
and uses the of_match table of the U_BOOT_DRIVER() structure to find the
|
||||
right driver for each node. In this case, the of_match table may provide a
|
||||
driver_data value, but platdata cannot be provided until later.
|
||||
|
||||
For each device that is discovered, U-Boot then calls device_bind() to create a
|
||||
new device, initializes various core fields of the device object such as name,
|
||||
uclass & driver, initializes any optional fields of the device object that are
|
||||
applicable such as of_offset, driver_data & platdata, and finally calls the
|
||||
driver's bind() method if one is defined.
|
||||
|
||||
At this point all the devices are known, and bound to their drivers. There
|
||||
is a 'struct udevice' allocated for all devices. However, nothing has been
|
||||
activated (except for the root device). Each bound device that was created
|
||||
from a U_BOOT_DEVICE() declaration will hold the platdata pointer specified
|
||||
in that declaration. For a bound device created from the device tree,
|
||||
platdata will be NULL, but of_offset will be the offset of the device tree
|
||||
node that caused the device to be created. The uclass is set correctly for
|
||||
the device.
|
||||
|
||||
The device's bind() method is permitted to perform simple actions, but
|
||||
should not scan the device tree node, not initialise hardware, nor set up
|
||||
structures or allocate memory. All of these tasks should be left for
|
||||
the probe() method.
|
||||
|
||||
Note that compared to Linux, U-Boot's driver model has a separate step of
|
||||
probe/remove which is independent of bind/unbind. This is partly because in
|
||||
U-Boot it may be expensive to probe devices and we don't want to do it until
|
||||
they are needed, or perhaps until after relocation.
|
||||
|
||||
2. Activation/probe
|
||||
|
||||
When a device needs to be used, U-Boot activates it, by following these
|
||||
steps (see device_probe()):
|
||||
|
||||
a. If priv_auto_alloc_size is non-zero, then the device-private space
|
||||
is allocated for the device and zeroed. It will be accessible as
|
||||
dev->priv. The driver can put anything it likes in there, but should use
|
||||
it for run-time information, not platform data (which should be static
|
||||
and known before the device is probed).
|
||||
|
||||
b. If platdata_auto_alloc_size is non-zero, then the platform data space
|
||||
is allocated. This is only useful for device tree operation, since
|
||||
otherwise you would have to specific the platform data in the
|
||||
U_BOOT_DEVICE() declaration. The space is allocated for the device and
|
||||
zeroed. It will be accessible as dev->platdata.
|
||||
|
||||
c. If the device's uclass specifies a non-zero per_device_auto_alloc_size,
|
||||
then this space is allocated and zeroed also. It is allocated for and
|
||||
stored in the device, but it is uclass data. owned by the uclass driver.
|
||||
It is possible for the device to access it.
|
||||
|
||||
d. If the device's immediate parent specifies a per_child_auto_alloc_size
|
||||
then this space is allocated. This is intended for use by the parent
|
||||
device to keep track of things related to the child. For example a USB
|
||||
flash stick attached to a USB host controller would likely use this
|
||||
space. The controller can hold information about the USB state of each
|
||||
of its children.
|
||||
|
||||
e. All parent devices are probed. It is not possible to activate a device
|
||||
unless its predecessors (all the way up to the root device) are activated.
|
||||
This means (for example) that an I2C driver will require that its bus
|
||||
be activated.
|
||||
|
||||
f. The device's sequence number is assigned, either the requested one
|
||||
(assuming no conflicts) or the next available one if there is a conflict
|
||||
or nothing particular is requested.
|
||||
|
||||
g. If the driver provides an ofdata_to_platdata() method, then this is
|
||||
called to convert the device tree data into platform data. This should
|
||||
do various calls like fdtdec_get_int(gd->fdt_blob, dev->of_offset, ...)
|
||||
to access the node and store the resulting information into dev->platdata.
|
||||
After this point, the device works the same way whether it was bound
|
||||
using a device tree node or U_BOOT_DEVICE() structure. In either case,
|
||||
the platform data is now stored in the platdata structure. Typically you
|
||||
will use the platdata_auto_alloc_size feature to specify the size of the
|
||||
platform data structure, and U-Boot will automatically allocate and zero
|
||||
it for you before entry to ofdata_to_platdata(). But if not, you can
|
||||
allocate it yourself in ofdata_to_platdata(). Note that it is preferable
|
||||
to do all the device tree decoding in ofdata_to_platdata() rather than
|
||||
in probe(). (Apart from the ugliness of mixing configuration and run-time
|
||||
data, one day it is possible that U-Boot will cache platformat data for
|
||||
devices which are regularly de/activated).
|
||||
|
||||
h. The device's probe() method is called. This should do anything that
|
||||
is required by the device to get it going. This could include checking
|
||||
that the hardware is actually present, setting up clocks for the
|
||||
hardware and setting up hardware registers to initial values. The code
|
||||
in probe() can access:
|
||||
|
||||
- platform data in dev->platdata (for configuration)
|
||||
- private data in dev->priv (for run-time state)
|
||||
- uclass data in dev->uclass_priv (for things the uclass stores
|
||||
about this device)
|
||||
|
||||
Note: If you don't use priv_auto_alloc_size then you will need to
|
||||
allocate the priv space here yourself. The same applies also to
|
||||
platdata_auto_alloc_size. Remember to free them in the remove() method.
|
||||
|
||||
i. The device is marked 'activated'
|
||||
|
||||
j. The uclass's post_probe() method is called, if one exists. This may
|
||||
cause the uclass to do some housekeeping to record the device as
|
||||
activated and 'known' by the uclass.
|
||||
|
||||
3. Running stage
|
||||
|
||||
The device is now activated and can be used. From now until it is removed
|
||||
all of the above structures are accessible. The device appears in the
|
||||
uclass's list of devices (so if the device is in UCLASS_GPIO it will appear
|
||||
as a device in the GPIO uclass). This is the 'running' state of the device.
|
||||
|
||||
4. Removal stage
|
||||
|
||||
When the device is no-longer required, you can call device_remove() to
|
||||
remove it. This performs the probe steps in reverse:
|
||||
|
||||
a. The uclass's pre_remove() method is called, if one exists. This may
|
||||
cause the uclass to do some housekeeping to record the device as
|
||||
deactivated and no-longer 'known' by the uclass.
|
||||
|
||||
b. All the device's children are removed. It is not permitted to have
|
||||
an active child device with a non-active parent. This means that
|
||||
device_remove() is called for all the children recursively at this point.
|
||||
|
||||
c. The device's remove() method is called. At this stage nothing has been
|
||||
deallocated so platform data, private data and the uclass data will all
|
||||
still be present. This is where the hardware can be shut down. It is
|
||||
intended that the device be completely inactive at this point, For U-Boot
|
||||
to be sure that no hardware is running, it should be enough to remove
|
||||
all devices.
|
||||
|
||||
d. The device memory is freed (platform data, private data, uclass data,
|
||||
parent data).
|
||||
|
||||
Note: Because the platform data for a U_BOOT_DEVICE() is defined with a
|
||||
static pointer, it is not de-allocated during the remove() method. For
|
||||
a device instantiated using the device tree data, the platform data will
|
||||
be dynamically allocated, and thus needs to be deallocated during the
|
||||
remove() method, either:
|
||||
|
||||
1. if the platdata_auto_alloc_size is non-zero, the deallocation
|
||||
happens automatically within the driver model core; or
|
||||
|
||||
2. when platdata_auto_alloc_size is 0, both the allocation (in probe()
|
||||
or preferably ofdata_to_platdata()) and the deallocation in remove()
|
||||
are the responsibility of the driver author.
|
||||
|
||||
e. The device sequence number is set to -1, meaning that it no longer
|
||||
has an allocated sequence. If the device is later reactivated and that
|
||||
sequence number is still free, it may well receive the name sequence
|
||||
number again. But from this point, the sequence number previously used
|
||||
by this device will no longer exist (think of SPI bus 2 being removed
|
||||
and bus 2 is no longer available for use).
|
||||
|
||||
f. The device is marked inactive. Note that it is still bound, so the
|
||||
device structure itself is not freed at this point. Should the device be
|
||||
activated again, then the cycle starts again at step 2 above.
|
||||
|
||||
5. Unbind stage
|
||||
|
||||
The device is unbound. This is the step that actually destroys the device.
|
||||
If a parent has children these will be destroyed first. After this point
|
||||
the device does not exist and its memory has be deallocated.
|
||||
|
||||
|
||||
Data Structures
|
||||
---------------
|
||||
|
||||
Driver model uses a doubly-linked list as the basic data structure. Some
|
||||
nodes have several lists running through them. Creating a more efficient
|
||||
data structure might be worthwhile in some rare cases, once we understand
|
||||
what the bottlenecks are.
|
||||
|
||||
|
||||
Changes since v1
|
||||
----------------
|
||||
|
||||
For the record, this implementation uses a very similar approach to the
|
||||
original patches, but makes at least the following changes:
|
||||
|
||||
- Tried to aggressively remove boilerplate, so that for most drivers there
|
||||
is little or no 'driver model' code to write.
|
||||
- Moved some data from code into data structure - e.g. store a pointer to
|
||||
the driver operations structure in the driver, rather than passing it
|
||||
to the driver bind function.
|
||||
- Rename some structures to make them more similar to Linux (struct udevice
|
||||
instead of struct instance, struct platdata, etc.)
|
||||
- Change the name 'core' to 'uclass', meaning U-Boot class. It seems that
|
||||
this concept relates to a class of drivers (or a subsystem). We shouldn't
|
||||
use 'class' since it is a C++ reserved word, so U-Boot class (uclass) seems
|
||||
better than 'core'.
|
||||
- Remove 'struct driver_instance' and just use a single 'struct udevice'.
|
||||
This removes a level of indirection that doesn't seem necessary.
|
||||
- Built in device tree support, to avoid the need for platdata
|
||||
- Removed the concept of driver relocation, and just make it possible for
|
||||
the new driver (created after relocation) to access the old driver data.
|
||||
I feel that relocation is a very special case and will only apply to a few
|
||||
drivers, many of which can/will just re-init anyway. So the overhead of
|
||||
dealing with this might not be worth it.
|
||||
- Implemented a GPIO system, trying to keep it simple
|
||||
|
||||
|
||||
Pre-Relocation Support
|
||||
----------------------
|
||||
|
||||
For pre-relocation we simply call the driver model init function. Only
|
||||
drivers marked with DM_FLAG_PRE_RELOC or the device tree
|
||||
'u-boot,dm-pre-reloc' flag are initialised prior to relocation. This helps
|
||||
to reduce the driver model overhead.
|
||||
|
||||
Then post relocation we throw that away and re-init driver model again.
|
||||
For drivers which require some sort of continuity between pre- and
|
||||
post-relocation devices, we can provide access to the pre-relocation
|
||||
device pointers, but this is not currently implemented (the root device
|
||||
pointer is saved but not made available through the driver model API).
|
||||
|
||||
|
||||
SPL Support
|
||||
-----------
|
||||
|
||||
Driver model can operate in SPL. Its efficient implementation and small code
|
||||
size provide for a small overhead which is acceptable for all but the most
|
||||
constrained systems.
|
||||
|
||||
To enable driver model in SPL, define CONFIG_SPL_DM. You might want to
|
||||
consider the following option also. See the main README for more details.
|
||||
|
||||
- CONFIG_SYS_MALLOC_SIMPLE
|
||||
- CONFIG_DM_WARN
|
||||
- CONFIG_DM_DEVICE_REMOVE
|
||||
- CONFIG_DM_STDIO
|
||||
|
||||
|
||||
Enabling Driver Model
|
||||
---------------------
|
||||
|
||||
Driver model is being brought into U-Boot gradually. As each subsystems gets
|
||||
support, a uclass is created and a CONFIG to enable use of driver model for
|
||||
that subsystem.
|
||||
|
||||
For example CONFIG_DM_SERIAL enables driver model for serial. With that
|
||||
defined, the old serial support is not enabled, and your serial driver must
|
||||
conform to driver model. With that undefined, the old serial support is
|
||||
enabled and driver model is not available for serial. This means that when
|
||||
you convert a driver, you must either convert all its boards, or provide for
|
||||
the driver to be compiled both with and without driver model (generally this
|
||||
is not very hard).
|
||||
|
||||
See the main README for full details of the available driver model CONFIG
|
||||
options.
|
||||
|
||||
|
||||
Things to punt for later
|
||||
------------------------
|
||||
|
||||
Uclasses are statically numbered at compile time. It would be possible to
|
||||
change this to dynamic numbering, but then we would require some sort of
|
||||
lookup service, perhaps searching by name. This is slightly less efficient
|
||||
so has been left out for now. One small advantage of dynamic numbering might
|
||||
be fewer merge conflicts in uclass-id.h.
|
||||
|
||||
|
||||
Simon Glass
|
||||
sjg@chromium.org
|
||||
April 2013
|
||||
Updated 7-May-13
|
||||
Updated 14-Jun-13
|
||||
Updated 18-Oct-13
|
||||
Updated 5-Nov-13
|
||||
135
u-boot/doc/driver-model/pci-info.txt
Normal file
135
u-boot/doc/driver-model/pci-info.txt
Normal file
@@ -0,0 +1,135 @@
|
||||
PCI with Driver Model
|
||||
=====================
|
||||
|
||||
How busses are scanned
|
||||
----------------------
|
||||
|
||||
Any config read will end up at pci_read_config(). This uses
|
||||
uclass_get_device_by_seq() to get the PCI bus for a particular bus number.
|
||||
Bus number 0 will need to be requested first, and the alias in the device
|
||||
tree file will point to the correct device:
|
||||
|
||||
|
||||
aliases {
|
||||
pci0 = &pci;
|
||||
};
|
||||
|
||||
pci: pci-controller {
|
||||
compatible = "sandbox,pci";
|
||||
...
|
||||
};
|
||||
|
||||
|
||||
If there is no alias the devices will be numbered sequentially in the device
|
||||
tree.
|
||||
|
||||
The call to uclass_get_device() will cause the PCI bus to be probed.
|
||||
This does a scan of the bus to locate available devices. These devices are
|
||||
bound to their appropriate driver if available. If there is no driver, then
|
||||
they are bound to a generic PCI driver which does nothing.
|
||||
|
||||
After probing a bus, the available devices will appear in the device tree
|
||||
under that bus.
|
||||
|
||||
Note that this is all done on a lazy basis, as needed, so until something is
|
||||
touched on PCI (eg: a call to pci_find_devices()) it will not be probed.
|
||||
|
||||
PCI devices can appear in the flattened device tree. If they do this serves to
|
||||
specify the driver to use for the device. In this case they will be bound at
|
||||
first. Each PCI device node must have a compatible string list as well as a
|
||||
<reg> property, as defined by the IEEE Std 1275-1994 PCI bus binding document
|
||||
v2.1. Note we must describe PCI devices with the same bus hierarchy as the
|
||||
hardware, otherwise driver model cannot detect the correct parent/children
|
||||
relationship during PCI bus enumeration thus PCI devices won't be bound to
|
||||
their drivers accordingly. A working example like below:
|
||||
|
||||
pci {
|
||||
#address-cells = <3>;
|
||||
#size-cells = <2>;
|
||||
compatible = "pci-x86";
|
||||
u-boot,dm-pre-reloc;
|
||||
ranges = <0x02000000 0x0 0x40000000 0x40000000 0 0x80000000
|
||||
0x42000000 0x0 0xc0000000 0xc0000000 0 0x20000000
|
||||
0x01000000 0x0 0x2000 0x2000 0 0xe000>;
|
||||
|
||||
pcie@17,0 {
|
||||
#address-cells = <3>;
|
||||
#size-cells = <2>;
|
||||
compatible = "pci-bridge";
|
||||
u-boot,dm-pre-reloc;
|
||||
reg = <0x0000b800 0x0 0x0 0x0 0x0>;
|
||||
|
||||
topcliff@0,0 {
|
||||
#address-cells = <3>;
|
||||
#size-cells = <2>;
|
||||
compatible = "pci-bridge";
|
||||
u-boot,dm-pre-reloc;
|
||||
reg = <0x00010000 0x0 0x0 0x0 0x0>;
|
||||
|
||||
pciuart0: uart@a,1 {
|
||||
compatible = "pci8086,8811.00",
|
||||
"pci8086,8811",
|
||||
"pciclass,070002",
|
||||
"pciclass,0700",
|
||||
"x86-uart";
|
||||
u-boot,dm-pre-reloc;
|
||||
reg = <0x00025100 0x0 0x0 0x0 0x0
|
||||
0x01025110 0x0 0x0 0x0 0x0>;
|
||||
......
|
||||
};
|
||||
|
||||
......
|
||||
};
|
||||
};
|
||||
|
||||
......
|
||||
};
|
||||
|
||||
In this example, the root PCI bus node is the "/pci" which matches "pci-x86"
|
||||
driver. It has a subnode "pcie@17,0" with driver "pci-bridge". "pcie@17,0"
|
||||
also has subnode "topcliff@0,0" which is a "pci-bridge" too. Under that bridge,
|
||||
a PCI UART device "uart@a,1" is described. This exactly reflects the hardware
|
||||
bus hierarchy: on the root PCI bus, there is a PCIe root port which connects
|
||||
to a downstream device Topcliff chipset. Inside Topcliff chipset, it has a
|
||||
PCIe-to-PCI bridge and all the chipset integrated devices like the PCI UART
|
||||
device are on the PCI bus. Like other devices in the device tree, if we want
|
||||
to bind PCI devices before relocation, "u-boot,dm-pre-reloc" must be declared
|
||||
in each of these nodes.
|
||||
|
||||
If PCI devices are not listed in the device tree, U_BOOT_PCI_DEVICE can be used
|
||||
to specify the driver to use for the device. The device tree takes precedence
|
||||
over U_BOOT_PCI_DEVICE. Plese note with U_BOOT_PCI_DEVICE, only drivers with
|
||||
DM_FLAG_PRE_RELOC will be bound before relocation. If neither device tree nor
|
||||
U_BOOT_PCI_DEVICE is provided, the built-in driver (either pci_bridge_drv or
|
||||
pci_generic_drv) will be used.
|
||||
|
||||
|
||||
Sandbox
|
||||
-------
|
||||
|
||||
With sandbox we need a device emulator for each device on the bus since there
|
||||
is no real PCI bus. This works by looking in the device tree node for a
|
||||
driver. For example:
|
||||
|
||||
|
||||
pci@1f,0 {
|
||||
compatible = "pci-generic";
|
||||
reg = <0xf800 0 0 0 0>;
|
||||
emul@1f,0 {
|
||||
compatible = "sandbox,swap-case";
|
||||
};
|
||||
};
|
||||
|
||||
This means that there is a 'sandbox,swap-case' driver at that bus position.
|
||||
Note that the first cell in the 'reg' value is the bus/device/function. See
|
||||
PCI_BDF() for the encoding (it is also specified in the IEEE Std 1275-1994
|
||||
PCI bus binding document, v2.1)
|
||||
|
||||
When this bus is scanned we will end up with something like this:
|
||||
|
||||
`- * pci-controller @ 05c660c8, 0
|
||||
`- pci@1f,0 @ 05c661c8, 63488
|
||||
`- emul@1f,0 @ 05c662c8
|
||||
|
||||
When accesses go to the pci@1f,0 device they are forwarded to its child, the
|
||||
emulator.
|
||||
140
u-boot/doc/driver-model/pmic-framework.txt
Normal file
140
u-boot/doc/driver-model/pmic-framework.txt
Normal file
@@ -0,0 +1,140 @@
|
||||
#
|
||||
# (C) Copyright 2014-2015 Samsung Electronics
|
||||
# Przemyslaw Marczak <p.marczak@samsung.com>
|
||||
#
|
||||
# SPDX-License-Identifier: GPL-2.0+
|
||||
#
|
||||
|
||||
PMIC framework based on Driver Model
|
||||
====================================
|
||||
TOC:
|
||||
1. Introduction
|
||||
2. How does it work
|
||||
3. Pmic uclass
|
||||
4. Regulator uclass
|
||||
|
||||
1. Introduction
|
||||
===============
|
||||
This is an introduction to driver-model multi uclass PMIC IC's support.
|
||||
At present it's based on two uclass types:
|
||||
- UCLASS_PMIC - basic uclass type for PMIC I/O, which provides common
|
||||
read/write interface.
|
||||
- UCLASS_REGULATOR - additional uclass type for specific PMIC features,
|
||||
which are Voltage/Current regulators.
|
||||
|
||||
New files:
|
||||
UCLASS_PMIC:
|
||||
- drivers/power/pmic/pmic-uclass.c
|
||||
- include/power/pmic.h
|
||||
UCLASS_REGULATOR:
|
||||
- drivers/power/regulator/regulator-uclass.c
|
||||
- include/power/regulator.h
|
||||
|
||||
Commands:
|
||||
- common/cmd_pmic.c
|
||||
- common/cmd_regulator.c
|
||||
|
||||
2. How doees it work
|
||||
====================
|
||||
The Power Management Integrated Circuits (PMIC) are used in embedded systems
|
||||
to provide stable, precise and specific voltage power source with over-voltage
|
||||
and thermal protection circuits.
|
||||
|
||||
The single PMIC can provide various functions by single or multiple interfaces,
|
||||
like in the example below.
|
||||
|
||||
-- SoC
|
||||
|
|
||||
| ______________________________________
|
||||
| BUS 0 | Multi interface PMIC IC |--> LDO out 1
|
||||
| e.g.I2C0 | |--> LDO out N
|
||||
|-----------|---- PMIC device 0 (READ/WRITE ops) |
|
||||
| or SPI0 | |_ REGULATOR device (ldo/... ops) |--> BUCK out 1
|
||||
| | |_ CHARGER device (charger ops) |--> BUCK out M
|
||||
| | |_ MUIC device (microUSB con ops) |
|
||||
| BUS 1 | |_ ... |---> BATTERY
|
||||
| e.g.I2C1 | |
|
||||
|-----------|---- PMIC device 1 (READ/WRITE ops) |---> USB in 1
|
||||
. or SPI1 | |_ RTC device (rtc ops) |---> USB in 2
|
||||
. |______________________________________|---> USB out
|
||||
.
|
||||
|
||||
Since U-Boot provides driver model features for I2C and SPI bus drivers,
|
||||
the PMIC devices should also support this. By the pmic and regulator API's,
|
||||
PMIC drivers can simply provide a common functions, for multi-interface and
|
||||
and multi-instance device support.
|
||||
|
||||
Basic design assumptions:
|
||||
|
||||
- Common I/O API - UCLASS_PMIC
|
||||
For the multi-function PMIC devices, this can be used as parent I/O device
|
||||
for each IC's interface. Then, each children uses the same dev for read/write.
|
||||
|
||||
- Common regulator API - UCLASS_REGULATOR
|
||||
For driving the regulator attributes, auto setting function or command line
|
||||
interface, based on kernel-style regulator device tree constraints.
|
||||
|
||||
For simple implementations, regulator drivers are not required, so the code can
|
||||
use pmic read/write directly.
|
||||
|
||||
3. Pmic uclass
|
||||
==============
|
||||
The basic information:
|
||||
* Uclass: 'UCLASS_PMIC'
|
||||
* Header: 'include/power/pmic.h'
|
||||
* Core: 'drivers/power/pmic/pmic-uclass.c'
|
||||
config: 'CONFIG_DM_PMIC'
|
||||
* Command: 'common/cmd_pmic.c'
|
||||
config: 'CONFIG_CMD_PMIC'
|
||||
* Example: 'drivers/power/pmic/max77686.c'
|
||||
|
||||
For detailed API description, please refer to the header file.
|
||||
|
||||
As an example of the pmic driver, please refer to the MAX77686 driver.
|
||||
|
||||
Please pay attention for the driver's bind() method. Exactly the function call:
|
||||
'pmic_bind_children()', which is used to bind the regulators by using the array
|
||||
of regulator's node, compatible prefixes.
|
||||
|
||||
The 'pmic; command also supports the new API. So the pmic command can be enabled
|
||||
by adding CONFIG_CMD_PMIC.
|
||||
The new pmic command allows to:
|
||||
- list pmic devices
|
||||
- choose the current device (like the mmc command)
|
||||
- read or write the pmic register
|
||||
- dump all pmic registers
|
||||
|
||||
This command can use only UCLASS_PMIC devices, since this uclass is designed
|
||||
for pmic I/O operations only.
|
||||
|
||||
For more information, please refer to the core file.
|
||||
|
||||
4. Regulator uclass
|
||||
===================
|
||||
The basic information:
|
||||
* Uclass: 'UCLASS_REGULATOR'
|
||||
* Header: 'include/power/regulator.h'
|
||||
* Core: 'drivers/power/regulator/regulator-uclass.c'
|
||||
config: 'CONFIG_DM_REGULATOR'
|
||||
binding: 'doc/device-tree-bindings/regulator/regulator.txt'
|
||||
* Command: 'common/cmd_regulator.c'
|
||||
config: 'CONFIG_CMD_REGULATOR'
|
||||
* Example: 'drivers/power/regulator/max77686.c'
|
||||
'drivers/power/pmic/max77686.c' (required I/O driver for the above)
|
||||
* Example: 'drivers/power/regulator/fixed.c'
|
||||
config" 'CONFIG_DM_REGULATOR_FIXED'
|
||||
|
||||
For detailed API description, please refer to the header file.
|
||||
|
||||
For the example regulator driver, please refer to the MAX77686 regulator driver,
|
||||
but this driver can't operate without pmic's example driver, which provides an
|
||||
I/O interface for MAX77686 regulator.
|
||||
|
||||
The second example is a fixed Voltage/Current regulator for a common use.
|
||||
|
||||
The 'regulator' command also supports the new API. The command allow:
|
||||
- list regulator devices
|
||||
- choose the current device (like the mmc command)
|
||||
- do all regulator-specific operations
|
||||
|
||||
For more information, please refer to the command file.
|
||||
168
u-boot/doc/driver-model/remoteproc-framework.txt
Normal file
168
u-boot/doc/driver-model/remoteproc-framework.txt
Normal file
@@ -0,0 +1,168 @@
|
||||
#
|
||||
# (C) Copyright 2015
|
||||
# Texas Instruments Incorporated - http://www.ti.com/
|
||||
# SPDX-License-Identifier: GPL-2.0+
|
||||
#
|
||||
|
||||
Remote Processor Framework
|
||||
==========================
|
||||
TOC:
|
||||
1. Introduction
|
||||
2. How does it work - The driver
|
||||
3. Describing the device using platform data
|
||||
4. Describing the device using device tree
|
||||
|
||||
1. Introduction
|
||||
===============
|
||||
|
||||
This is an introduction to driver-model for Remote Processors found
|
||||
on various System on Chip(SoCs). The term remote processor is used to
|
||||
indicate that this is not the processor on which U-Boot is operating
|
||||
on, instead is yet another processing entity that may be controlled by
|
||||
the processor on which we are functional.
|
||||
|
||||
The simplified model depends on a single UCLASS - UCLASS_REMOTEPROC
|
||||
|
||||
UCLASS_REMOTEPROC:
|
||||
- drivers/remoteproc/rproc-uclass.c
|
||||
- include/remoteproc.h
|
||||
|
||||
Commands:
|
||||
- common/cmd_remoteproc.c
|
||||
|
||||
Configuration:
|
||||
CONFIG_REMOTEPROC is selected by drivers as needed
|
||||
CONFIG_CMD_REMOTEPROC for the commands if required.
|
||||
|
||||
2. How does it work - The driver
|
||||
=================================
|
||||
|
||||
Overall, the driver statemachine transitions are typically as follows:
|
||||
(entry)
|
||||
+-------+
|
||||
+---+ init |
|
||||
| | | <---------------------+
|
||||
| +-------+ |
|
||||
| |
|
||||
| |
|
||||
| +--------+ |
|
||||
Load| | reset | |
|
||||
| | | <----------+ |
|
||||
| +--------+ | |
|
||||
| |Load | |
|
||||
| | | |
|
||||
| +----v----+ reset | |
|
||||
+-> | | (opt) | |
|
||||
| Loaded +-----------+ |
|
||||
| | |
|
||||
+----+----+ |
|
||||
| Start |
|
||||
+---v-----+ (opt) |
|
||||
+->| Running | Stop |
|
||||
Ping +- | +--------------------+
|
||||
(opt) +---------+
|
||||
|
||||
(is_running does not change state)
|
||||
opt: Optional state transition implemented by driver.
|
||||
|
||||
NOTE: It depends on the remote processor as to the exact behavior
|
||||
of the statemachine, remoteproc core does not intent to implement
|
||||
statemachine logic. Certain processors may allow start/stop without
|
||||
reloading the image in the middle, certain other processors may only
|
||||
allow us to start the processor(image from a EEPROM/OTP) etc.
|
||||
|
||||
It is hence the responsibility of the driver to handle the requisite
|
||||
state transitions of the device as necessary.
|
||||
|
||||
Basic design assumptions:
|
||||
|
||||
Remote processor can operate on a certain firmware that maybe loaded
|
||||
and released from reset.
|
||||
|
||||
The driver follows a standard UCLASS DM.
|
||||
|
||||
in the bare minimum form:
|
||||
|
||||
static const struct dm_rproc_ops sandbox_testproc_ops = {
|
||||
.load = sandbox_testproc_load,
|
||||
.start = sandbox_testproc_start,
|
||||
};
|
||||
|
||||
static const struct udevice_id sandbox_ids[] = {
|
||||
{.compatible = "sandbox,test-processor"},
|
||||
{}
|
||||
};
|
||||
|
||||
U_BOOT_DRIVER(sandbox_testproc) = {
|
||||
.name = "sandbox_test_proc",
|
||||
.of_match = sandbox_ids,
|
||||
.id = UCLASS_REMOTEPROC,
|
||||
.ops = &sandbox_testproc_ops,
|
||||
.probe = sandbox_testproc_probe,
|
||||
};
|
||||
|
||||
This allows for the device to be probed as part of the "init" command
|
||||
or invocation of 'rproc_init()' function as the system dependencies define.
|
||||
|
||||
The driver is expected to maintain it's own statemachine which is
|
||||
appropriate for the device it maintains. It must, at the very least
|
||||
provide a load and start function. We assume here that the device
|
||||
needs to be loaded and started, else, there is no real purpose of
|
||||
using the remoteproc framework.
|
||||
|
||||
3. Describing the device using platform data
|
||||
============================================
|
||||
|
||||
*IMPORTANT* NOTE: THIS SUPPORT IS NOT MEANT FOR USE WITH NEWER PLATFORM
|
||||
SUPPORT. THIS IS ONLY FOR LEGACY DEVICES. THIS MODE OF INITIALIZATION
|
||||
*WILL* BE EVENTUALLY REMOVED ONCE ALL NECESSARY PLATFORMS HAVE MOVED
|
||||
TO DM/FDT.
|
||||
|
||||
Considering that many platforms are yet to move to device-tree model,
|
||||
a simplified definition of a device is as follows:
|
||||
|
||||
struct dm_rproc_uclass_pdata proc_3_test = {
|
||||
.name = "proc_3_legacy",
|
||||
.mem_type = RPROC_INTERNAL_MEMORY_MAPPED,
|
||||
.driver_plat_data = &mydriver_data;
|
||||
};
|
||||
|
||||
U_BOOT_DEVICE(proc_3_demo) = {
|
||||
.name = "sandbox_test_proc",
|
||||
.platdata = &proc_3_test,
|
||||
};
|
||||
|
||||
There can be additional data that may be desired depending on the
|
||||
remoteproc driver specific needs (for example: SoC integration
|
||||
details such as clock handle or something similar). See appropriate
|
||||
documentation for specific remoteproc driver for further details.
|
||||
These are passed via driver_plat_data.
|
||||
|
||||
3. Describing the device using device tree
|
||||
==========================================
|
||||
/ {
|
||||
...
|
||||
aliases {
|
||||
...
|
||||
remoteproc0 = &rproc_1;
|
||||
remoteproc1 = &rproc_2;
|
||||
|
||||
};
|
||||
...
|
||||
|
||||
rproc_1: rproc@1 {
|
||||
compatible = "sandbox,test-processor";
|
||||
remoteproc-name = "remoteproc-test-dev1";
|
||||
};
|
||||
|
||||
rproc_2: rproc@2 {
|
||||
compatible = "sandbox,test-processor";
|
||||
internal-memory-mapped;
|
||||
remoteproc-name = "remoteproc-test-dev2";
|
||||
};
|
||||
...
|
||||
};
|
||||
|
||||
aliases usage is optional, but it is usually recommended to ensure the
|
||||
users have a consistent usage model for a platform.
|
||||
the compatible string used here is specific to the remoteproc driver involved.
|
||||
46
u-boot/doc/driver-model/serial-howto.txt
Normal file
46
u-boot/doc/driver-model/serial-howto.txt
Normal file
@@ -0,0 +1,46 @@
|
||||
How to port a serial driver to driver model
|
||||
===========================================
|
||||
|
||||
Almost all of the serial drivers have been converted as at January 2016. These
|
||||
ones remain:
|
||||
|
||||
mcfuart.c
|
||||
serial_bfin.c
|
||||
serial_pxa.c
|
||||
serial_s3c24x0.c
|
||||
|
||||
The deadline for this work was the end of January 2016. If no one steps
|
||||
forward to convert these, at some point there may come a patch to remove them!
|
||||
|
||||
Here is a suggested approach for converting your serial driver over to driver
|
||||
model. Please feel free to update this file with your ideas and suggestions.
|
||||
|
||||
- #ifdef out all your own serial driver code (#ifndef CONFIG_DM_SERIAL)
|
||||
- Define CONFIG_DM_SERIAL for your board, vendor or architecture
|
||||
- If the board does not already use driver model, you need CONFIG_DM also
|
||||
- Your board should then build, but will not boot since there will be no serial
|
||||
driver
|
||||
- Add the U_BOOT_DRIVER piece at the end (e.g. copy serial_s5p.c for example)
|
||||
- Add a private struct for the driver data - avoid using static variables
|
||||
- Implement each of the driver methods, perhaps by calling your old methods
|
||||
- You may need to adjust the function parameters so that the old and new
|
||||
implementations can share most of the existing code
|
||||
- If you convert all existing users of the driver, remove the pre-driver-model
|
||||
code
|
||||
|
||||
In terms of patches a conversion series typically has these patches:
|
||||
- clean up / prepare the driver for conversion
|
||||
- add driver model code
|
||||
- convert at least one existing board to use driver model serial
|
||||
- (if no boards remain that don't use driver model) remove the old code
|
||||
|
||||
This may be a good time to move your board to use device tree also. Mostly
|
||||
this involves these steps:
|
||||
|
||||
- define CONFIG_OF_CONTROL and CONFIG_OF_SEPARATE
|
||||
- add your device tree files to arch/<arch>/dts
|
||||
- update the Makefile there
|
||||
- Add stdout-path to your /chosen device tree node if it is not already there
|
||||
- build and get u-boot-dtb.bin so you can test it
|
||||
- Your drivers can now use device tree
|
||||
- For device tree in SPL, define CONFIG_SPL_OF_CONTROL
|
||||
628
u-boot/doc/driver-model/spi-howto.txt
Normal file
628
u-boot/doc/driver-model/spi-howto.txt
Normal file
@@ -0,0 +1,628 @@
|
||||
How to port a SPI driver to driver model
|
||||
========================================
|
||||
|
||||
Here is a rough step-by-step guide. It is based around converting the
|
||||
exynos SPI driver to driver model (DM) and the example code is based
|
||||
around U-Boot v2014.10-rc2 (commit be9f643). This has been updated for
|
||||
v2015.04.
|
||||
|
||||
It is quite long since it includes actual code examples.
|
||||
|
||||
Before driver model, SPI drivers have their own private structure which
|
||||
contains 'struct spi_slave'. With driver model, 'struct spi_slave' still
|
||||
exists, but now it is 'per-child data' for the SPI bus. Each child of the
|
||||
SPI bus is a SPI slave. The information that was stored in the
|
||||
driver-specific slave structure can now be port in private data for the
|
||||
SPI bus.
|
||||
|
||||
For example, struct tegra_spi_slave looks like this:
|
||||
|
||||
struct tegra_spi_slave {
|
||||
struct spi_slave slave;
|
||||
struct tegra_spi_ctrl *ctrl;
|
||||
};
|
||||
|
||||
In this case 'slave' will be in per-child data, and 'ctrl' will be in the
|
||||
SPI's buses private data.
|
||||
|
||||
|
||||
0. How long does this take?
|
||||
|
||||
You should be able to complete this within 2 hours, including testing but
|
||||
excluding preparing the patches. The API is basically the same as before
|
||||
with only minor changes:
|
||||
|
||||
- methods to set speed and mode are separated out
|
||||
- cs_info is used to get information on a chip select
|
||||
|
||||
|
||||
1. Enable driver mode for SPI and SPI flash
|
||||
|
||||
Add these to your board config:
|
||||
|
||||
CONFIG_DM_SPI
|
||||
CONFIG_DM_SPI_FLASH
|
||||
|
||||
|
||||
2. Add the skeleton
|
||||
|
||||
Put this code at the bottom of your existing driver file:
|
||||
|
||||
struct spi_slave *spi_setup_slave(unsigned int busnum, unsigned int cs,
|
||||
unsigned int max_hz, unsigned int mode)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
struct spi_slave *spi_setup_slave_fdt(const void *blob, int slave_node,
|
||||
int spi_node)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static int exynos_spi_ofdata_to_platdata(struct udevice *dev)
|
||||
{
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
static int exynos_spi_probe(struct udevice *dev)
|
||||
{
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
static int exynos_spi_remove(struct udevice *dev)
|
||||
{
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
static int exynos_spi_claim_bus(struct udevice *dev)
|
||||
{
|
||||
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
static int exynos_spi_release_bus(struct udevice *dev)
|
||||
{
|
||||
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
static int exynos_spi_xfer(struct udevice *dev, unsigned int bitlen,
|
||||
const void *dout, void *din, unsigned long flags)
|
||||
{
|
||||
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
static int exynos_spi_set_speed(struct udevice *dev, uint speed)
|
||||
{
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
static int exynos_spi_set_mode(struct udevice *dev, uint mode)
|
||||
{
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
static int exynos_cs_info(struct udevice *bus, uint cs,
|
||||
struct spi_cs_info *info)
|
||||
{
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
static const struct dm_spi_ops exynos_spi_ops = {
|
||||
.claim_bus = exynos_spi_claim_bus,
|
||||
.release_bus = exynos_spi_release_bus,
|
||||
.xfer = exynos_spi_xfer,
|
||||
.set_speed = exynos_spi_set_speed,
|
||||
.set_mode = exynos_spi_set_mode,
|
||||
.cs_info = exynos_cs_info,
|
||||
};
|
||||
|
||||
static const struct udevice_id exynos_spi_ids[] = {
|
||||
{ .compatible = "samsung,exynos-spi" },
|
||||
{ }
|
||||
};
|
||||
|
||||
U_BOOT_DRIVER(exynos_spi) = {
|
||||
.name = "exynos_spi",
|
||||
.id = UCLASS_SPI,
|
||||
.of_match = exynos_spi_ids,
|
||||
.ops = &exynos_spi_ops,
|
||||
.ofdata_to_platdata = exynos_spi_ofdata_to_platdata,
|
||||
.probe = exynos_spi_probe,
|
||||
.remove = exynos_spi_remove,
|
||||
};
|
||||
|
||||
|
||||
3. Replace 'exynos' in the above code with your driver name
|
||||
|
||||
|
||||
4. #ifdef out all of the code in your driver except for the above
|
||||
|
||||
This will allow you to get it building, which means you can work
|
||||
incrementally. Since all the methods return an error initially, there is
|
||||
less chance that you will accidentally leave something in.
|
||||
|
||||
Also, even though your conversion is basically a rewrite, it might help
|
||||
reviewers if you leave functions in the same place in the file,
|
||||
particularly for large drivers.
|
||||
|
||||
|
||||
5. Add some includes
|
||||
|
||||
Add these includes to your driver:
|
||||
|
||||
#include <dm.h>
|
||||
#include <errno.h>
|
||||
|
||||
|
||||
6. Build
|
||||
|
||||
At this point you should be able to build U-Boot for your board with the
|
||||
empty SPI driver. You still have empty methods in your driver, but we will
|
||||
write these one by one.
|
||||
|
||||
If you have spi_init() functions or the like that are called from your
|
||||
board then the build will fail. Remove these calls and make a note of the
|
||||
init that needs to be done.
|
||||
|
||||
|
||||
7. Set up your platform data structure
|
||||
|
||||
This will hold the information your driver to operate, like its hardware
|
||||
address or maximum frequency.
|
||||
|
||||
You may already have a struct like this, or you may need to create one
|
||||
from some of the #defines or global variables in the driver.
|
||||
|
||||
Note that this information is not the run-time information. It should not
|
||||
include state that changes. It should be fixed throughout the live of
|
||||
U-Boot. Run-time information comes later.
|
||||
|
||||
Here is what was in the exynos spi driver:
|
||||
|
||||
struct spi_bus {
|
||||
enum periph_id periph_id;
|
||||
s32 frequency; /* Default clock frequency, -1 for none */
|
||||
struct exynos_spi *regs;
|
||||
int inited; /* 1 if this bus is ready for use */
|
||||
int node;
|
||||
uint deactivate_delay_us; /* Delay to wait after deactivate */
|
||||
};
|
||||
|
||||
Of these, inited is handled by DM and node is the device tree node, which
|
||||
DM tells you. The name is not quite right. So in this case we would use:
|
||||
|
||||
struct exynos_spi_platdata {
|
||||
enum periph_id periph_id;
|
||||
s32 frequency; /* Default clock frequency, -1 for none */
|
||||
struct exynos_spi *regs;
|
||||
uint deactivate_delay_us; /* Delay to wait after deactivate */
|
||||
};
|
||||
|
||||
|
||||
8a. Write ofdata_to_platdata() [for device tree only]
|
||||
|
||||
This method will convert information in the device tree node into a C
|
||||
structure in your driver (called platform data). If you are not using
|
||||
device tree, go to 8b.
|
||||
|
||||
DM will automatically allocate the struct for us when we are using device
|
||||
tree, but we need to tell it the size:
|
||||
|
||||
U_BOOT_DRIVER(spi_exynos) = {
|
||||
...
|
||||
.platdata_auto_alloc_size = sizeof(struct exynos_spi_platdata),
|
||||
|
||||
|
||||
Here is a sample function. It gets a pointer to the platform data and
|
||||
fills in the fields from device tree.
|
||||
|
||||
static int exynos_spi_ofdata_to_platdata(struct udevice *bus)
|
||||
{
|
||||
struct exynos_spi_platdata *plat = bus->platdata;
|
||||
const void *blob = gd->fdt_blob;
|
||||
int node = bus->of_offset;
|
||||
|
||||
plat->regs = (struct exynos_spi *)fdtdec_get_addr(blob, node, "reg");
|
||||
plat->periph_id = pinmux_decode_periph_id(blob, node);
|
||||
|
||||
if (plat->periph_id == PERIPH_ID_NONE) {
|
||||
debug("%s: Invalid peripheral ID %d\n", __func__,
|
||||
plat->periph_id);
|
||||
return -FDT_ERR_NOTFOUND;
|
||||
}
|
||||
|
||||
/* Use 500KHz as a suitable default */
|
||||
plat->frequency = fdtdec_get_int(blob, node, "spi-max-frequency",
|
||||
500000);
|
||||
plat->deactivate_delay_us = fdtdec_get_int(blob, node,
|
||||
"spi-deactivate-delay", 0);
|
||||
debug("%s: regs=%p, periph_id=%d, max-frequency=%d, deactivate_delay=%d\n",
|
||||
__func__, plat->regs, plat->periph_id, plat->frequency,
|
||||
plat->deactivate_delay_us);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
8b. Add the platform data [non-device-tree only]
|
||||
|
||||
Specify this data in a U_BOOT_DEVICE() declaration in your board file:
|
||||
|
||||
struct exynos_spi_platdata platdata_spi0 = {
|
||||
.periph_id = ...
|
||||
.frequency = ...
|
||||
.regs = ...
|
||||
.deactivate_delay_us = ...
|
||||
};
|
||||
|
||||
U_BOOT_DEVICE(board_spi0) = {
|
||||
.name = "exynos_spi",
|
||||
.platdata = &platdata_spi0,
|
||||
};
|
||||
|
||||
You will unfortunately need to put the struct definition into a header file
|
||||
in this case so that your board file can use it.
|
||||
|
||||
|
||||
9. Add the device private data
|
||||
|
||||
Most devices have some private data which they use to keep track of things
|
||||
while active. This is the run-time information and needs to be stored in
|
||||
a structure. There is probably a structure in the driver that includes a
|
||||
'struct spi_slave', so you can use that.
|
||||
|
||||
struct exynos_spi_slave {
|
||||
struct spi_slave slave;
|
||||
struct exynos_spi *regs;
|
||||
unsigned int freq; /* Default frequency */
|
||||
unsigned int mode;
|
||||
enum periph_id periph_id; /* Peripheral ID for this device */
|
||||
unsigned int fifo_size;
|
||||
int skip_preamble;
|
||||
struct spi_bus *bus; /* Pointer to our SPI bus info */
|
||||
ulong last_transaction_us; /* Time of last transaction end */
|
||||
};
|
||||
|
||||
|
||||
We should rename this to make its purpose more obvious, and get rid of
|
||||
the slave structure, so we have:
|
||||
|
||||
struct exynos_spi_priv {
|
||||
struct exynos_spi *regs;
|
||||
unsigned int freq; /* Default frequency */
|
||||
unsigned int mode;
|
||||
enum periph_id periph_id; /* Peripheral ID for this device */
|
||||
unsigned int fifo_size;
|
||||
int skip_preamble;
|
||||
ulong last_transaction_us; /* Time of last transaction end */
|
||||
};
|
||||
|
||||
|
||||
DM can auto-allocate this also:
|
||||
|
||||
U_BOOT_DRIVER(spi_exynos) = {
|
||||
...
|
||||
.priv_auto_alloc_size = sizeof(struct exynos_spi_priv),
|
||||
|
||||
|
||||
Note that this is created before the probe method is called, and destroyed
|
||||
after the remove method is called. It will be zeroed when the probe
|
||||
method is called.
|
||||
|
||||
|
||||
10. Add the probe() and remove() methods
|
||||
|
||||
Note: It's a good idea to build repeatedly as you are working, to avoid a
|
||||
huge amount of work getting things compiling at the end.
|
||||
|
||||
The probe method is supposed to set up the hardware. U-Boot used to use
|
||||
spi_setup_slave() to do this. So take a look at this function and see
|
||||
what you can copy out to set things up.
|
||||
|
||||
|
||||
static int exynos_spi_probe(struct udevice *bus)
|
||||
{
|
||||
struct exynos_spi_platdata *plat = dev_get_platdata(bus);
|
||||
struct exynos_spi_priv *priv = dev_get_priv(bus);
|
||||
|
||||
priv->regs = plat->regs;
|
||||
if (plat->periph_id == PERIPH_ID_SPI1 ||
|
||||
plat->periph_id == PERIPH_ID_SPI2)
|
||||
priv->fifo_size = 64;
|
||||
else
|
||||
priv->fifo_size = 256;
|
||||
|
||||
priv->skip_preamble = 0;
|
||||
priv->last_transaction_us = timer_get_us();
|
||||
priv->freq = plat->frequency;
|
||||
priv->periph_id = plat->periph_id;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
This implementation doesn't actually touch the hardware, which is somewhat
|
||||
unusual for a driver. In this case we will do that when the device is
|
||||
claimed by something that wants to use the SPI bus.
|
||||
|
||||
For remove we could shut down the clocks, but in this case there is
|
||||
nothing to do. DM frees any memory that it allocated, so we can just
|
||||
remove exynos_spi_remove() and its reference in U_BOOT_DRIVER.
|
||||
|
||||
|
||||
11. Implement set_speed()
|
||||
|
||||
This should set up clocks so that the SPI bus is running at the right
|
||||
speed. With the old API spi_claim_bus() would normally do this and several
|
||||
of the following functions, so let's look at that function:
|
||||
|
||||
int spi_claim_bus(struct spi_slave *slave)
|
||||
{
|
||||
struct exynos_spi_slave *spi_slave = to_exynos_spi(slave);
|
||||
struct exynos_spi *regs = spi_slave->regs;
|
||||
u32 reg = 0;
|
||||
int ret;
|
||||
|
||||
ret = set_spi_clk(spi_slave->periph_id,
|
||||
spi_slave->freq);
|
||||
if (ret < 0) {
|
||||
debug("%s: Failed to setup spi clock\n", __func__);
|
||||
return ret;
|
||||
}
|
||||
|
||||
exynos_pinmux_config(spi_slave->periph_id, PINMUX_FLAG_NONE);
|
||||
|
||||
spi_flush_fifo(slave);
|
||||
|
||||
reg = readl(®s->ch_cfg);
|
||||
reg &= ~(SPI_CH_CPHA_B | SPI_CH_CPOL_L);
|
||||
|
||||
if (spi_slave->mode & SPI_CPHA)
|
||||
reg |= SPI_CH_CPHA_B;
|
||||
|
||||
if (spi_slave->mode & SPI_CPOL)
|
||||
reg |= SPI_CH_CPOL_L;
|
||||
|
||||
writel(reg, ®s->ch_cfg);
|
||||
writel(SPI_FB_DELAY_180, ®s->fb_clk);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
It sets up the speed, mode, pinmux, feedback delay and clears the FIFOs.
|
||||
With DM these will happen in separate methods.
|
||||
|
||||
|
||||
Here is an example for the speed part:
|
||||
|
||||
static int exynos_spi_set_speed(struct udevice *bus, uint speed)
|
||||
{
|
||||
struct exynos_spi_platdata *plat = bus->platdata;
|
||||
struct exynos_spi_priv *priv = dev_get_priv(bus);
|
||||
int ret;
|
||||
|
||||
if (speed > plat->frequency)
|
||||
speed = plat->frequency;
|
||||
ret = set_spi_clk(priv->periph_id, speed);
|
||||
if (ret)
|
||||
return ret;
|
||||
priv->freq = speed;
|
||||
debug("%s: regs=%p, speed=%d\n", __func__, priv->regs, priv->freq);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
12. Implement set_mode()
|
||||
|
||||
This should adjust the SPI mode (polarity, etc.). Again this code probably
|
||||
comes from the old spi_claim_bus(). Here is an example:
|
||||
|
||||
|
||||
static int exynos_spi_set_mode(struct udevice *bus, uint mode)
|
||||
{
|
||||
struct exynos_spi_priv *priv = dev_get_priv(bus);
|
||||
uint32_t reg;
|
||||
|
||||
reg = readl(&priv->regs->ch_cfg);
|
||||
reg &= ~(SPI_CH_CPHA_B | SPI_CH_CPOL_L);
|
||||
|
||||
if (mode & SPI_CPHA)
|
||||
reg |= SPI_CH_CPHA_B;
|
||||
|
||||
if (mode & SPI_CPOL)
|
||||
reg |= SPI_CH_CPOL_L;
|
||||
|
||||
writel(reg, &priv->regs->ch_cfg);
|
||||
priv->mode = mode;
|
||||
debug("%s: regs=%p, mode=%d\n", __func__, priv->regs, priv->mode);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
13. Implement claim_bus()
|
||||
|
||||
This is where a client wants to make use of the bus, so claims it first.
|
||||
At this point we need to make sure everything is set up ready for data
|
||||
transfer. Note that this function is wholly internal to the driver - at
|
||||
present the SPI uclass never calls it.
|
||||
|
||||
Here again we look at the old claim function and see some code that is
|
||||
needed. It is anything unrelated to speed and mode:
|
||||
|
||||
static int exynos_spi_claim_bus(struct udevice *bus)
|
||||
{
|
||||
struct exynos_spi_priv *priv = dev_get_priv(bus);
|
||||
|
||||
exynos_pinmux_config(priv->periph_id, PINMUX_FLAG_NONE);
|
||||
spi_flush_fifo(priv->regs);
|
||||
|
||||
writel(SPI_FB_DELAY_180, &priv->regs->fb_clk);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
The spi_flush_fifo() function is in the removed part of the code, so we
|
||||
need to expose it again (perhaps with an #endif before it and '#if 0'
|
||||
after it). It only needs access to priv->regs which is why we have
|
||||
passed that in:
|
||||
|
||||
/**
|
||||
* Flush spi tx, rx fifos and reset the SPI controller
|
||||
*
|
||||
* @param regs Pointer to SPI registers
|
||||
*/
|
||||
static void spi_flush_fifo(struct exynos_spi *regs)
|
||||
{
|
||||
clrsetbits_le32(®s->ch_cfg, SPI_CH_HS_EN, SPI_CH_RST);
|
||||
clrbits_le32(®s->ch_cfg, SPI_CH_RST);
|
||||
setbits_le32(®s->ch_cfg, SPI_TX_CH_ON | SPI_RX_CH_ON);
|
||||
}
|
||||
|
||||
|
||||
14. Implement release_bus()
|
||||
|
||||
This releases the bus - in our example the old code in spi_release_bus()
|
||||
is a call to spi_flush_fifo, so we add:
|
||||
|
||||
static int exynos_spi_release_bus(struct udevice *bus)
|
||||
{
|
||||
struct exynos_spi_priv *priv = dev_get_priv(bus);
|
||||
|
||||
spi_flush_fifo(priv->regs);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
15. Implement xfer()
|
||||
|
||||
This is the final method that we need to create, and it is where all the
|
||||
work happens. The method parameters are the same as the old spi_xfer() with
|
||||
the addition of a 'struct udevice' so conversion is pretty easy. Start
|
||||
by copying the contents of spi_xfer() to your new xfer() method and proceed
|
||||
from there.
|
||||
|
||||
If (flags & SPI_XFER_BEGIN) is non-zero then xfer() normally calls an
|
||||
activate function, something like this:
|
||||
|
||||
void spi_cs_activate(struct spi_slave *slave)
|
||||
{
|
||||
struct exynos_spi_slave *spi_slave = to_exynos_spi(slave);
|
||||
|
||||
/* If it's too soon to do another transaction, wait */
|
||||
if (spi_slave->bus->deactivate_delay_us &&
|
||||
spi_slave->last_transaction_us) {
|
||||
ulong delay_us; /* The delay completed so far */
|
||||
delay_us = timer_get_us() - spi_slave->last_transaction_us;
|
||||
if (delay_us < spi_slave->bus->deactivate_delay_us)
|
||||
udelay(spi_slave->bus->deactivate_delay_us - delay_us);
|
||||
}
|
||||
|
||||
clrbits_le32(&spi_slave->regs->cs_reg, SPI_SLAVE_SIG_INACT);
|
||||
debug("Activate CS, bus %d\n", spi_slave->slave.bus);
|
||||
spi_slave->skip_preamble = spi_slave->mode & SPI_PREAMBLE;
|
||||
}
|
||||
|
||||
The new version looks like this:
|
||||
|
||||
static void spi_cs_activate(struct udevice *dev)
|
||||
{
|
||||
struct udevice *bus = dev->parent;
|
||||
struct exynos_spi_platdata *pdata = dev_get_platdata(bus);
|
||||
struct exynos_spi_priv *priv = dev_get_priv(bus);
|
||||
|
||||
/* If it's too soon to do another transaction, wait */
|
||||
if (pdata->deactivate_delay_us &&
|
||||
priv->last_transaction_us) {
|
||||
ulong delay_us; /* The delay completed so far */
|
||||
delay_us = timer_get_us() - priv->last_transaction_us;
|
||||
if (delay_us < pdata->deactivate_delay_us)
|
||||
udelay(pdata->deactivate_delay_us - delay_us);
|
||||
}
|
||||
|
||||
clrbits_le32(&priv->regs->cs_reg, SPI_SLAVE_SIG_INACT);
|
||||
debug("Activate CS, bus '%s'\n", bus->name);
|
||||
priv->skip_preamble = priv->mode & SPI_PREAMBLE;
|
||||
}
|
||||
|
||||
All we have really done here is change the pointers and print the device name
|
||||
instead of the bus number. Other local static functions can be treated in
|
||||
the same way.
|
||||
|
||||
|
||||
16. Set up the per-child data and child pre-probe function
|
||||
|
||||
To minimise the pain and complexity of the SPI subsystem while the driver
|
||||
model change-over is in place, struct spi_slave is used to reference a
|
||||
SPI bus slave, even though that slave is actually a struct udevice. In fact
|
||||
struct spi_slave is the device's child data. We need to make sure this space
|
||||
is available. It is possible to allocate more space that struct spi_slave
|
||||
needs, but this is the minimum.
|
||||
|
||||
U_BOOT_DRIVER(exynos_spi) = {
|
||||
...
|
||||
.per_child_auto_alloc_size = sizeof(struct spi_slave),
|
||||
}
|
||||
|
||||
|
||||
17. Optional: Set up cs_info() if you want it
|
||||
|
||||
Sometimes it is useful to know whether a SPI chip select is valid, but this
|
||||
is not obvious from outside the driver. In this case you can provide a
|
||||
method for cs_info() to deal with this. If you don't provide it, then the
|
||||
device tree will be used to determine what chip selects are valid.
|
||||
|
||||
Return -ENODEV if the supplied chip select is invalid, or 0 if it is valid.
|
||||
If you don't provide the cs_info() method, -ENODEV is assumed for all
|
||||
chip selects that do not appear in the device tree.
|
||||
|
||||
|
||||
18. Test it
|
||||
|
||||
Now that you have the code written and it compiles, try testing it using
|
||||
the 'sf test' command. You may need to enable CONFIG_CMD_SF_TEST for your
|
||||
board.
|
||||
|
||||
|
||||
19. Prepare patches and send them to the mailing lists
|
||||
|
||||
You can use 'tools/patman/patman' to prepare, check and send patches for
|
||||
your work. See the README for details.
|
||||
|
||||
20. A little note about SPI uclass features:
|
||||
|
||||
The SPI uclass keeps some information about each device 'dev' on the bus:
|
||||
|
||||
struct dm_spi_slave_platdata - this is device_get_parent_platdata(dev)
|
||||
This is where the chip select number is stored, along with
|
||||
the default bus speed and mode. It is automatically read
|
||||
from the device tree in spi_child_post_bind(). It must not
|
||||
be changed at run-time after being set up because platform
|
||||
data is supposed to be immutable at run-time.
|
||||
struct spi_slave - this is device_get_parentdata(dev)
|
||||
Already mentioned above. It holds run-time information about
|
||||
the device.
|
||||
|
||||
There are also some SPI uclass methods that get called behind the scenes:
|
||||
|
||||
spi_post_bind() - called when a new bus is bound
|
||||
This scans the device tree for devices on the bus, and binds
|
||||
each one. This in turn causes spi_child_post_bind() to be
|
||||
called for each, which reads the device tree information
|
||||
into the parent (per-child) platform data.
|
||||
spi_child_post_bind() - called when a new child is bound
|
||||
As mentioned above this reads the device tree information
|
||||
into the per-child platform data
|
||||
spi_child_pre_probe() - called before a new child is probed
|
||||
This sets up the mode and speed in struct spi_slave by
|
||||
copying it from the parent's platform data for this child.
|
||||
It also sets the 'dev' pointer, needed to permit passing
|
||||
'struct spi_slave' around the place without needing a
|
||||
separate 'struct udevice' pointer.
|
||||
|
||||
The above housekeeping makes it easier to write your SPI driver.
|
||||
415
u-boot/doc/driver-model/usb-info.txt
Normal file
415
u-boot/doc/driver-model/usb-info.txt
Normal file
@@ -0,0 +1,415 @@
|
||||
How USB works with driver model
|
||||
===============================
|
||||
|
||||
Introduction
|
||||
------------
|
||||
|
||||
Driver model USB support makes use of existing features but changes how
|
||||
drivers are found. This document provides some information intended to help
|
||||
understand how things work with USB in U-Boot when driver model is enabled.
|
||||
|
||||
|
||||
Enabling driver model for USB
|
||||
-----------------------------
|
||||
|
||||
A new CONFIG_DM_USB option is provided to enable driver model for USB. This
|
||||
causes the USB uclass to be included, and drops the equivalent code in
|
||||
usb.c. In particular the usb_init() function is then implemented by the
|
||||
uclass.
|
||||
|
||||
|
||||
Support for EHCI and XHCI
|
||||
-------------------------
|
||||
|
||||
So far OHCI is not supported. Both EHCI and XHCI drivers should be declared
|
||||
as drivers in the USB uclass. For example:
|
||||
|
||||
static const struct udevice_id ehci_usb_ids[] = {
|
||||
{ .compatible = "nvidia,tegra20-ehci", .data = USB_CTLR_T20 },
|
||||
{ .compatible = "nvidia,tegra30-ehci", .data = USB_CTLR_T30 },
|
||||
{ .compatible = "nvidia,tegra114-ehci", .data = USB_CTLR_T114 },
|
||||
{ }
|
||||
};
|
||||
|
||||
U_BOOT_DRIVER(usb_ehci) = {
|
||||
.name = "ehci_tegra",
|
||||
.id = UCLASS_USB,
|
||||
.of_match = ehci_usb_ids,
|
||||
.ofdata_to_platdata = ehci_usb_ofdata_to_platdata,
|
||||
.probe = tegra_ehci_usb_probe,
|
||||
.remove = tegra_ehci_usb_remove,
|
||||
.ops = &ehci_usb_ops,
|
||||
.platdata_auto_alloc_size = sizeof(struct usb_platdata),
|
||||
.priv_auto_alloc_size = sizeof(struct fdt_usb),
|
||||
.flags = DM_FLAG_ALLOC_PRIV_DMA,
|
||||
};
|
||||
|
||||
Here ehci_usb_ids is used to list the controllers that the driver supports.
|
||||
Each has its own data value. Controllers must be in the UCLASS_USB uclass.
|
||||
|
||||
The ofdata_to_platdata() method allows the controller driver to grab any
|
||||
necessary settings from the device tree.
|
||||
|
||||
The ops here are ehci_usb_ops. All EHCI drivers will use these same ops in
|
||||
most cases, since they are all EHCI-compatible. For EHCI there are also some
|
||||
special operations that can be overridden when calling ehci_register().
|
||||
|
||||
The driver can use priv_auto_alloc_size to set the size of its private data.
|
||||
This can hold run-time information needed by the driver for operation. It
|
||||
exists when the device is probed (not when it is bound) and is removed when
|
||||
the driver is removed.
|
||||
|
||||
Note that usb_platdata is currently only used to deal with setting up a bus
|
||||
in USB device mode (OTG operation). It can be omitted if that is not
|
||||
supported.
|
||||
|
||||
The driver's probe() method should do the basic controller init and then
|
||||
call ehci_register() to register itself as an EHCI device. It should call
|
||||
ehci_deregister() in the remove() method. Registering a new EHCI device
|
||||
does not by itself cause the bus to be scanned.
|
||||
|
||||
The old ehci_hcd_init() function is no-longer used. Nor is it necessary to
|
||||
set up the USB controllers from board init code. When 'usb start' is used,
|
||||
each controller will be probed and its bus scanned.
|
||||
|
||||
XHCI works in a similar way.
|
||||
|
||||
|
||||
Data structures
|
||||
---------------
|
||||
|
||||
The following primary data structures are in use:
|
||||
|
||||
- struct usb_device
|
||||
This holds information about a device on the bus. All devices have
|
||||
this structure, even the root hub. The controller itself does not
|
||||
have this structure. You can access it for a device 'dev' with
|
||||
dev_get_parent_priv(dev). It matches the old structure except that the
|
||||
parent and child information is not present (since driver model
|
||||
handles that). Once the device is set up, you can find the device
|
||||
descriptor and current configuration descriptor in this structure.
|
||||
|
||||
- struct usb_platdata
|
||||
This holds platform data for a controller. So far this is only used
|
||||
as a work-around for controllers which can act as USB devices in OTG
|
||||
mode, since the gadget framework does not use driver model.
|
||||
|
||||
- struct usb_dev_platdata
|
||||
This holds platform data for a device. You can access it for a
|
||||
device 'dev' with dev_get_parent_platdata(dev). It holds the device
|
||||
address and speed - anything that can be determined before the device
|
||||
driver is actually set up. When probing the bus this structure is
|
||||
used to provide essential information to the device driver.
|
||||
|
||||
- struct usb_bus_priv
|
||||
This is private information for each controller, maintained by the
|
||||
controller uclass. It is mostly used to keep track of the next
|
||||
device address to use.
|
||||
|
||||
Of these, only struct usb_device was used prior to driver model.
|
||||
|
||||
|
||||
USB buses
|
||||
---------
|
||||
|
||||
Given a controller, you know the bus - it is the one attached to the
|
||||
controller. Each controller handles exactly one bus. Every controller has a
|
||||
root hub attached to it. This hub, which is itself a USB device, can provide
|
||||
one or more 'ports' to which additional devices can be attached. It is
|
||||
possible to power up a hub and find out which of its ports have devices
|
||||
attached.
|
||||
|
||||
Devices are given addresses starting at 1. The root hub is always address 1,
|
||||
and from there the devices are numbered in sequence. The USB uclass takes
|
||||
care of this numbering automatically during enumeration.
|
||||
|
||||
USB devices are enumerated by finding a device on a particular hub, and
|
||||
setting its address to the next available address. The USB bus stretches out
|
||||
in a tree structure, potentially with multiple hubs each with several ports
|
||||
and perhaps other hubs. Some hubs will have their own power since otherwise
|
||||
the 5V 500mA power supplied by the controller will not be sufficient to run
|
||||
very many devices.
|
||||
|
||||
Enumeration in U-Boot takes a long time since devices are probed one at a
|
||||
time, and each is given sufficient time to wake up and announce itself. The
|
||||
timeouts are set for the slowest device.
|
||||
|
||||
Up to 127 devices can be on each bus. USB has four bus speeds: low
|
||||
(1.5Mbps), full (12Mbps), high (480Mbps) which is only available with USB2
|
||||
and newer (EHCI), and super (5Gbps) which is only available with USB3 and
|
||||
newer (XHCI). If you connect a super-speed device to a high-speed hub, you
|
||||
will only get high-speed.
|
||||
|
||||
|
||||
USB operations
|
||||
--------------
|
||||
|
||||
As before driver model, messages can be sent using submit_bulk_msg() and the
|
||||
like. These are now implemented by the USB uclass and route through the
|
||||
controller drivers. Note that messages are not sent to the driver of the
|
||||
device itself - i.e. they don't pass down the stack to the controller.
|
||||
U-Boot simply finds the controller to which the device is attached, and sends
|
||||
the message there with an appropriate 'pipe' value so it can be addressed
|
||||
properly. Having said that, the USB device which should receive the message
|
||||
is passed in to the driver methods, for use by sandbox. This design decision
|
||||
is open for review and the code impact of changing it is small since the
|
||||
methods are typically implemented by the EHCI and XHCI stacks.
|
||||
|
||||
Controller drivers (in UCLASS_USB) themselves provide methods for sending
|
||||
each message type. For XHCI an additional alloc_device() method is provided
|
||||
since XHCI needs to allocate a device context before it can even read the
|
||||
device's descriptor.
|
||||
|
||||
These methods use a 'pipe' which is a collection of bit fields used to
|
||||
describe the type of message, direction of transfer and the intended
|
||||
recipient (device number).
|
||||
|
||||
|
||||
USB Devices
|
||||
-----------
|
||||
|
||||
USB devices are found using a simple algorithm which works through the
|
||||
available hubs in a depth-first search. Devices can be in any uclass, but
|
||||
are attached to a parent hub (or controller in the case of the root hub) and
|
||||
so have parent data attached to them (this is struct usb_device).
|
||||
|
||||
By the time the device's probe() method is called, it is enumerated and is
|
||||
ready to talk to the host.
|
||||
|
||||
The enumeration process needs to work out which driver to attach to each USB
|
||||
device. It does this by examining the device class, interface class, vendor
|
||||
ID, product ID, etc. See struct usb_driver_entry for how drivers are matched
|
||||
with USB devices - you can use the USB_DEVICE() macro to declare a USB
|
||||
driver. For example, usb_storage.c defines a USB_DEVICE() to handle storage
|
||||
devices, and it will be used for all USB devices which match.
|
||||
|
||||
|
||||
|
||||
Technical details on enumeration flow
|
||||
-------------------------------------
|
||||
|
||||
It is useful to understand precisely how a USB bus is enumerating to avoid
|
||||
confusion when dealing with USB devices.
|
||||
|
||||
Device initialisation happens roughly like this:
|
||||
|
||||
- At some point the 'usb start' command is run
|
||||
- This calls usb_init() which works through each controller in turn
|
||||
- The controller is probed(). This does no enumeration.
|
||||
- Then usb_scan_bus() is called. This calls usb_scan_device() to scan the
|
||||
(only) device that is attached to the controller - a root hub
|
||||
- usb_scan_device() sets up a fake struct usb_device and calls
|
||||
usb_setup_device(), passing the port number to be scanned, in this case port
|
||||
0
|
||||
- usb_setup_device() first calls usb_prepare_device() to set the device
|
||||
address, then usb_select_config() to select the first configuration
|
||||
- at this point the device is enumerated but we do not have a real struct
|
||||
udevice for it. But we do have the descriptor in struct usb_device so we can
|
||||
use this to figure out what driver to use
|
||||
- back in usb_scan_device(), we call usb_find_child() to try to find an
|
||||
existing device which matches the one we just found on the bus. This can
|
||||
happen if the device is mentioned in the device tree, or if we previously
|
||||
scanned the bus and so the device was created before
|
||||
- if usb_find_child() does not find an existing device, we call
|
||||
usb_find_and_bind_driver() which tries to bind one
|
||||
- usb_find_and_bind_driver() searches all available USB drivers (declared
|
||||
with USB_DEVICE()). If it finds a match it binds that driver to create a new
|
||||
device.
|
||||
- If it does not, it binds a generic driver. A generic driver is good enough
|
||||
to allow access to the device (sending it packets, etc.) but all
|
||||
functionality will need to be implemented outside the driver model.
|
||||
- in any case, when usb_find_child() and/or usb_find_and_bind_driver() are
|
||||
done, we have a device with the correct uclass. At this point we want to
|
||||
probe the device
|
||||
- first we store basic information about the new device (address, port,
|
||||
speed) in its parent platform data. We cannot store it its private data
|
||||
since that will not exist until the device is probed.
|
||||
- then we call device_probe() which probes the device
|
||||
- the first probe step is actually the USB controller's (or USB hubs's)
|
||||
child_pre_probe() method. This gets called before anything else and is
|
||||
intended to set up a child device ready to be used with its parent bus. For
|
||||
USB this calls usb_child_pre_probe() which grabs the information that was
|
||||
stored in the parent platform data and stores it in the parent private data
|
||||
(which is struct usb_device, a real one this time). It then calls
|
||||
usb_select_config() again to make sure that everything about the device is
|
||||
set up
|
||||
- note that we have called usb_select_config() twice. This is inefficient
|
||||
but the alternative is to store additional information in the platform data.
|
||||
The time taken is minimal and this way is simpler
|
||||
- at this point the device is set up and ready for use so far as the USB
|
||||
subsystem is concerned
|
||||
- the device's probe() method is then called. It can send messages and do
|
||||
whatever else it wants to make the device work.
|
||||
|
||||
Note that the first device is always a root hub, and this must be scanned to
|
||||
find any devices. The above steps will have created a hub (UCLASS_USB_HUB),
|
||||
given it address 1 and set the configuration.
|
||||
|
||||
For hubs, the hub uclass has a post_probe() method. This means that after
|
||||
any hub is probed, the uclass gets to do some processing. In this case
|
||||
usb_hub_post_probe() is called, and the following steps take place:
|
||||
|
||||
- usb_hub_post_probe() calls usb_hub_scan() to scan the hub, which in turn
|
||||
calls usb_hub_configure()
|
||||
- hub power is enabled
|
||||
- we loop through each port on the hub, performing the same steps for each
|
||||
- first, check if there is a device present. This happens in
|
||||
usb_hub_port_connect_change(). If so, then usb_scan_device() is called to
|
||||
scan the device, passing the appropriate port number.
|
||||
- you will recognise usb_scan_device() from the steps above. It sets up the
|
||||
device ready for use. If it is a hub, it will scan that hub before it
|
||||
continues here (recursively, depth-first)
|
||||
- once all hub ports are scanned in this way, the hub is ready for use and
|
||||
all of its downstream devices also
|
||||
- additional controllers are scanned in the same way
|
||||
|
||||
The above method has some nice properties:
|
||||
|
||||
- the bus enumeration happens by virtue of driver model's natural device flow
|
||||
- most logic is in the USB controller and hub uclasses; the actual device
|
||||
drivers do not need to know they are on a USB bus, at least so far as
|
||||
enumeration goes
|
||||
- hub scanning happens automatically after a hub is probed
|
||||
|
||||
|
||||
Hubs
|
||||
----
|
||||
|
||||
USB hubs are scanned as in the section above. While hubs have their own
|
||||
uclass, they share some common elements with controllers:
|
||||
|
||||
- they both attach private data to their children (struct usb_device,
|
||||
accessible for a child with dev_get_parent_priv(child))
|
||||
- they both use usb_child_pre_probe() to set up their children as proper USB
|
||||
devices
|
||||
|
||||
|
||||
Example - Mass Storage
|
||||
----------------------
|
||||
|
||||
As an example of a USB device driver, see usb_storage.c. It uses its own
|
||||
uclass and declares itself as follows:
|
||||
|
||||
U_BOOT_DRIVER(usb_mass_storage) = {
|
||||
.name = "usb_mass_storage",
|
||||
.id = UCLASS_MASS_STORAGE,
|
||||
.of_match = usb_mass_storage_ids,
|
||||
.probe = usb_mass_storage_probe,
|
||||
};
|
||||
|
||||
static const struct usb_device_id mass_storage_id_table[] = {
|
||||
{ .match_flags = USB_DEVICE_ID_MATCH_INT_CLASS,
|
||||
.bInterfaceClass = USB_CLASS_MASS_STORAGE},
|
||||
{ } /* Terminating entry */
|
||||
};
|
||||
|
||||
USB_DEVICE(usb_mass_storage, mass_storage_id_table);
|
||||
|
||||
The USB_DEVICE() macro attaches the given table of matching information to
|
||||
the given driver. Note that the driver is declared in U_BOOT_DRIVER() as
|
||||
'usb_mass_storage' and this must match the first parameter of USB_DEVICE.
|
||||
|
||||
When usb_find_and_bind_driver() is called on a USB device with the
|
||||
bInterfaceClass value of USB_CLASS_MASS_STORAGE, it will automatically find
|
||||
this driver and use it.
|
||||
|
||||
|
||||
Counter-example: USB Ethernet
|
||||
-----------------------------
|
||||
|
||||
As an example of the old way of doing things, see usb_ether.c. When the bus
|
||||
is scanned, all Ethernet devices will be created as generic USB devices (in
|
||||
uclass UCLASS_USB_DEV_GENERIC). Then, when the scan is completed,
|
||||
usb_host_eth_scan() will be called. This looks through all the devices on
|
||||
each bus and manually figures out which are Ethernet devices in the ways of
|
||||
yore.
|
||||
|
||||
In fact, usb_ether should be moved to driver model. Each USB Ethernet driver
|
||||
(e.g drivers/usb/eth/asix.c) should include a USB_DEVICE() declaration, so
|
||||
that it will be found as part of normal USB enumeration. Then, instead of a
|
||||
generic USB driver, a real (driver-model-aware) driver will be used. Since
|
||||
Ethernet now supports driver model, this should be fairly easy to achieve,
|
||||
and then usb_ether.c and the usb_host_eth_scan() will melt away.
|
||||
|
||||
|
||||
Sandbox
|
||||
-------
|
||||
|
||||
All driver model uclasses must have tests and USB is no exception. To
|
||||
achieve this, a sandbox USB controller is provided. This can make use of
|
||||
emulation drivers which pretend to be USB devices. Emulations are provided
|
||||
for a hub and a flash stick. These are enough to create a pretend USB bus
|
||||
(defined by the sandbox device tree sandbox.dts) which can be scanned and
|
||||
used.
|
||||
|
||||
Tests in test/dm/usb.c make use of this feature. It allows much of the USB
|
||||
stack to be tested without real hardware being needed.
|
||||
|
||||
Here is an example device tree fragment:
|
||||
|
||||
usb@1 {
|
||||
compatible = "sandbox,usb";
|
||||
hub {
|
||||
compatible = "usb-hub";
|
||||
usb,device-class = <USB_CLASS_HUB>;
|
||||
hub-emul {
|
||||
compatible = "sandbox,usb-hub";
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
flash-stick {
|
||||
reg = <0>;
|
||||
compatible = "sandbox,usb-flash";
|
||||
sandbox,filepath = "flash.bin";
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
This defines a single controller, containing a root hub (which is required).
|
||||
The hub is emulated by a hub emulator, and the emulated hub has a single
|
||||
flash stick to emulate on one of its ports.
|
||||
|
||||
When 'usb start' is used, the following 'dm tree' output will be available:
|
||||
|
||||
usb [ + ] `-- usb@1
|
||||
usb_hub [ + ] `-- hub
|
||||
usb_emul [ + ] |-- hub-emul
|
||||
usb_emul [ + ] | `-- flash-stick
|
||||
usb_mass_st [ + ] `-- usb_mass_storage
|
||||
|
||||
|
||||
This may look confusing. Most of it mirrors the device tree, but the
|
||||
'usb_mass_storage' device is not in the device tree. This is created by
|
||||
usb_find_and_bind_driver() based on the USB_DRIVER in usb_storage.c. While
|
||||
'flash-stick' is the emulation device, 'usb_mass_storage' is the real U-Boot
|
||||
USB device driver that talks to it.
|
||||
|
||||
|
||||
Future work
|
||||
-----------
|
||||
|
||||
It is pretty uncommon to have a large USB bus with lots of hubs on an
|
||||
embedded system. In fact anything other than a root hub is uncommon. Still
|
||||
it would be possible to speed up enumeration in two ways:
|
||||
|
||||
- breadth-first search would allow devices to be reset and probed in
|
||||
parallel to some extent
|
||||
- enumeration could be lazy, in the sense that we could enumerate just the
|
||||
root hub at first, then only progress to the next 'level' when a device is
|
||||
used that we cannot find. This could be made easier if the devices were
|
||||
statically declared in the device tree (which is acceptable for production
|
||||
boards where the same, known, things are on each bus).
|
||||
|
||||
But in common cases the current algorithm is sufficient.
|
||||
|
||||
Other things that need doing:
|
||||
- Convert usb_ether to use driver model as described above
|
||||
- Test that keyboards work (and convert to driver model)
|
||||
- Move the USB gadget framework to driver model
|
||||
- Implement OHCI in driver model
|
||||
- Implement USB PHYs in driver model
|
||||
- Work out a clever way to provide lazy init for USB devices
|
||||
|
||||
--
|
||||
Simon Glass <sjg@chromium.org>
|
||||
23-Mar-15
|
||||
Reference in New Issue
Block a user