From: Pavel Herrmann <[email protected]> This adds a README to help with understanding of this series.
Signed-off-by: Simon Glass <[email protected]> --- Changes in v2: - Removed pointer return values in favour of integer - Use driver_bind() in dm_init() instead of writing new code - Allow driver_bind() to support a NULL parent - Add dm_warn() to warn about impending doom - Standardise variable names (e.g. uclass instead of class) - Remove relocation functions - Add new header file for lists - Add new util file to hold utility functions - Allow a driver to bind to only one uclass - Remove unneeded arguments to uclass_bind(), uclass_unbind() - Rename struct device's 'bus' to 'parent' - Rename data structures to hopefully be clearer - Put platform_data definitions in their own header file - Add U_BOOT_DEVICE to declare platform_data - Add auto-probing feature for platform_data to avoid driver_bind() calls - Add simple unit test functions - Add test infrastructure for driver model - Add integration tests for driver model - Add device tree support in driver model - Add automatic allocation of platform_data for FDT - Add automatic allocation of priv data for devices - Add automatic allocation of device-specific priv data for uclasses - Add GPIO uclass and tests - Add sandbox GPIO driver - Update gpio command to use driver model - Add tests for core code - Add script to run tests - Add a single include/dm.h to bring in driver model code doc/driver-model/README.txt | 329 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 329 insertions(+) create mode 100644 doc/driver-model/README.txt diff --git a/doc/driver-model/README.txt b/doc/driver-model/README.txt new file mode 100644 index 0000000..230d276 --- /dev/null +++ b/doc/driver-model/README.txt @@ -0,0 +1,329 @@ +Driver Model RFC +================ + +This README contains information about an early RFC implementation of +driver model. The original work was done by: + + Marek Vasut <[email protected]> + Pavel Herrmann <[email protected]> + Viktor Křivák <[email protected]> + Tomas Hlavacek <[email protected]> + +The code was taken from branch dm at: + + git://git.denx.de/u-boot-marex.git + +and heavily modified. You can find a test version of the code used here in +branch dm2 at: + + http://git.denx.de/u-boot-x86.git + +(Branch dm contains the original implementation) + + +The goal of this effort is to get a very basic implementation into +the U-Boot source tree quickly - something which people can build on, +rather than something that solves all of the problems immediately. +The only driver support available is GPIO, because it is simple, +but non-trivial. + + +Terminology +----------- + +Uclass - a group of device which operate in the same way. A uclass provides + a way of accessing invidual 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 unified + 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_config + make + ./u-boot + + (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 100% code coverage of them. It does have multiple drivers, it +handles parameter data and platform_data (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/dm/test-dm.sh + + +What is going on? +----------------- + +Let's start at the top. The demo command is in common/cmd_demo.c. It does +the usual command procesing and then: + + struct device *demo_dev; + + ret = uclass_get_device(UCLASS_DEMO, &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 the device for the demo uclass. Given a device +number we can find the device because all devices have registered with +the UCLASS_DEMO uclass. + +Having found the device, we activate it with: + + ret = driver_activate(demo_dev); + +This is because all devices are inactive until used in U-Boot. The exact +mechanism of activating a driver will hopefully change. For example, i +may be possible to combine uclass_get_child() and driver_activate(). + +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 device *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 device *dev, int ch) +{ + const struct dm_demo_pdata *pdata = dev->platform_data; + + printf("Hello '%c' from %08x: %s %d\n", ch, map_to_sysmem(dev), + pdata->colour, pdata->sides); + + return 0; +} + + +So that is a trip from top to bottom 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 simple_ops = { + .hello = shape_hello, + .status = shape_status, +}; + +U_BOOT_DRIVER(demo_shape_drv) = { + .name = "demo_shape_drv", + .id = UCLASS_DEMO, + .ops = &simple_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->priv 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 probe, +and bind, 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. + + +Platform Data +------------- + +Where does the platform data come from? See common/board_r.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 acceess their data via dev->info->platform_data. 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", + .platform_data = &red_square, + }, + }; + + demo1 = driver_bind(root, &info[0]); + + +Device Tree +----------- + +This is now implemented. You can use device tree to specify the platform data +and activated drivers. In other words we replace the above code with the +following device treefragment: + + red-square { + compatible = "demo-shape"; + colour = "red"; + sides = <4>; + }; + + +The easiest way to make this work it to add a platform_data_size member to +the driver: + + .platform_data_size = sizeof(struct dm_test_pdata), + +This will be allocated and zeroed before the driver's probe method is called. +The driver can then read the information out of the device tree and put it +in dev->priv. + + +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. + + +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 so far +-------------- + +The documenation in this direction is out of data and until the patch +is in more solid form I have avoided updating it. This implementation +uses a very similar approach, but makes at least the following changes: + +- Tried to agressively 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 device +instead of struct instance, struct platform_data, 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 device'. +This removes a level of indirection that doesn't seem necessary. +- Built in device tree support, to avoid the need for platform_data +- 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 + +Proposed additional changes +--------------------------- + +- Maybe add device tree support to the demo driver +- Update the documenation to match the new implementation + + +Things to punt for later +------------------------ + +- SPL support - this will have to be present before many drivers can be +converted, but it seems like we can add it once we are happy with the +core implementation. +- Pre-relocation support - similar story + +That is not to say that no thinking has gone into these - in fact there +is quite a lot there. However, getting these right is non-trivial and +there is a high cost associated with going down the wrong path. + + +Simon Glass [email protected] +April 2013 +Updated 7-May-13 -- 1.8.2.1 _______________________________________________ U-Boot mailing list [email protected] http://lists.denx.de/mailman/listinfo/u-boot

