From patchwork Thu Nov 7 16:32:03 2013 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: Simon Glass X-Patchwork-Id: 289430 X-Patchwork-Delegate: trini@ti.com Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from theia.denx.de (theia.denx.de [85.214.87.163]) by ozlabs.org (Postfix) with ESMTP id 8D2922C0094 for ; Fri, 8 Nov 2013 03:37:55 +1100 (EST) Received: from localhost (localhost [127.0.0.1]) by theia.denx.de (Postfix) with ESMTP id 950AA4A3BB; Thu, 7 Nov 2013 17:36:52 +0100 (CET) X-Virus-Scanned: Debian amavisd-new at theia.denx.de Received: from theia.denx.de ([127.0.0.1]) by localhost (theia.denx.de [127.0.0.1]) (amavisd-new, port 10024) with ESMTP id 59xrk0ula4tC; Thu, 7 Nov 2013 17:36:52 +0100 (CET) Received: from theia.denx.de (localhost [127.0.0.1]) by theia.denx.de (Postfix) with ESMTP id 4D27C4A420; Thu, 7 Nov 2013 17:34:48 +0100 (CET) Received: from localhost (localhost [127.0.0.1]) by theia.denx.de (Postfix) with ESMTP id BC9F74A406 for ; Thu, 7 Nov 2013 17:34:23 +0100 (CET) X-Virus-Scanned: Debian amavisd-new at theia.denx.de Received: from theia.denx.de ([127.0.0.1]) by localhost (theia.denx.de [127.0.0.1]) (amavisd-new, port 10024) with ESMTP id l5Rs-hlPUoNH for ; Thu, 7 Nov 2013 17:34:18 +0100 (CET) X-policyd-weight: NOT_IN_SBL_XBL_SPAMHAUS=-1.5 NOT_IN_SPAMCOP=-1.5 NOT_IN_BL_NJABL=-1.5 (only DNSBL check requested) Received: from mail-pd0-f201.google.com (mail-pd0-f201.google.com [209.85.192.201]) by theia.denx.de (Postfix) with ESMTPS id 31A784A3C1 for ; Thu, 7 Nov 2013 17:33:29 +0100 (CET) Received: by mail-pd0-f201.google.com with SMTP id y10so71901pdj.4 for ; Thu, 07 Nov 2013 08:33:27 -0800 (PST) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20130820; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references:mime-version:content-type:content-transfer-encoding; bh=xWDyYgbTuyDmkZRLZaWvvmIvwU6b8flzGqZ0yzYvdoc=; b=WYuTtp/rTB232hoOjj1SnfKmLCDuw1X7J8Z4OUukAmes/8layWsXWCnNJAKVp3OcnV eJiqZ7nvcN//iAmdoRJIURewTlVywDzS3FCgJK08bp4xSbF28ggO01/vfaxDg49GOVB0 ZCTFhyY2FqAJvPOL6yjbvbWWV5p6WbYiATlLfy5GEsh0z3BgBSadsgTfEyZqgoPaq0OY Q/g+QPrXKxD3HZG+DVa2+V1GBF0Rkv+kNywx7Lmd9iIuFDZCOD0ao2pnR7385ISbanBe xerYg6d8S04wmJut1eLC4skRcNXu899FLZXKPBtmE+0HNdB+kauHn148lMvufePsEDMa GPkA== X-Gm-Message-State: ALoCoQk4bnspq07JDZbmMdXixokKUFaUZkstlFUv5dWi4yBuGJ3RoQLzCfnIzAru//8/ZWyqgjInMio4VwE/LVoksuqg+0dD3ttE5xw+hEuIOmCbnfi3U0hltdpXDiOdo/11ds5LNrZSHiz+KNhDFMWNJnLX/m9X7K7grxaLSrVfMPx1uaBUIuZXAxObeM9LrtjLurJxCOX7 X-Received: by 10.67.2.41 with SMTP id bl9mr3931912pad.16.1383842007804; Thu, 07 Nov 2013 08:33:27 -0800 (PST) Received: from corp2gmr1-2.hot.corp.google.com (corp2gmr1-2.hot.corp.google.com [172.24.189.93]) by gmr-mx.google.com with ESMTPS id m48si610808yho.6.2013.11.07.08.33.27 for (version=TLSv1.1 cipher=ECDHE-RSA-AES128-SHA bits=128/128); Thu, 07 Nov 2013 08:33:27 -0800 (PST) Received: from kaki.bld.corp.google.com (kaki.bld.corp.google.com [172.29.216.32]) by corp2gmr1-2.hot.corp.google.com (Postfix) with ESMTP id 8914A5A41EA; Thu, 7 Nov 2013 08:33:27 -0800 (PST) Received: by kaki.bld.corp.google.com (Postfix, from userid 121222) id 9B8A62210FB; Thu, 7 Nov 2013 09:32:32 -0700 (MST) From: Simon Glass To: U-Boot Mailing List Date: Thu, 7 Nov 2013 09:32:03 -0700 Message-Id: <1383841933-1800-8-git-send-email-sjg@chromium.org> X-Mailer: git-send-email 1.8.4.1 In-Reply-To: <1383841933-1800-1-git-send-email-sjg@chromium.org> References: <1383841933-1800-1-git-send-email-sjg@chromium.org> MIME-Version: 1.0 Cc: Tom Rini , u-boot-review@google.com Subject: [U-Boot] [PATCH v6 07/17] dm: Add README for driver model X-BeenThere: u-boot@lists.denx.de X-Mailman-Version: 2.1.11 Precedence: list List-Id: U-Boot discussion List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Sender: u-boot-bounces@lists.denx.de Errors-To: u-boot-bounces@lists.denx.de This adds a README to help with understanding of this series. Signed-off-by: Simon Glass --- Changes in v6: - Rename platform_data to platdata - Revise and update README Changes in v5: None Changes in v4: None Changes in v3: - Updated README.txt to cover changes since version 2 Changes in v2: - Add GPIO uclass and tests - Add U_BOOT_DEVICE to declare platform_data - Add a single include/dm.h to bring in driver model code - Add auto-probing feature for platform_data to avoid driver_bind() calls - Add automatic allocation of device-specific priv data for uclasses - Add automatic allocation of platform_data for FDT - Add automatic allocation of priv data for devices - Add device tree support in driver model - Add dm_warn() to warn about impending doom - Add integration tests for driver model - Add new header file for lists - Add new util file to hold utility functions - Add sandbox GPIO driver - Add script to run tests - Add simple unit test functions - Add test infrastructure for driver model - Add tests for core code - Allow a driver to bind to only one uclass - Allow driver_bind() to support a NULL parent - Put platform_data definitions in their own header file - Remove relocation functions - Remove unneeded arguments to uclass_bind(), uclass_unbind() - Removed pointer return values in favour of integer - Rename data structures to hopefully be clearer - Rename struct device's 'bus' to 'parent' - Standardise variable names (e.g. uclass instead of class) - Update gpio command to use driver model - Use driver_bind() in dm_init() instead of writing new code doc/driver-model/README.txt | 368 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 368 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..22c1481 --- /dev/null +++ b/doc/driver-model/README.txt @@ -0,0 +1,368 @@ +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 + Pavel Herrmann + Viktor Křivák + Tomas Hlavacek + +This has been both simplified and extended into the current implementation +by: + + Simon Glass + + +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 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_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 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/dm/test-dm.sh + +You should see something like this: + + <...U-Boot banner...> + Running 12 driver model tests + Test: dm_test_autobind + Test: dm_test_autoprobe + Test: dm_test_children + Test: dm_test_fdt + Test: dm_test_gpio + sandbox_gpio: sb_gpio_get_value: error: offset 4 not reserved + Test: dm_test_leak + Warning: Please add '#define DEBUG' to the top of common/dlmalloc.c + Warning: Please add '#define DEBUG' to the top of common/dlmalloc.c + Test: dm_test_lifecycle + Test: dm_test_operations + Test: dm_test_ordering + Test: dm_test_platdata + Test: dm_test_remove + Test: dm_test_uclass + Failures: 0 + +(You can add '#define DEBUG' as suggested to check for memory leaks) + + +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, 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 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_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 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_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 +------------- + +Where does the platform data come from? 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 acceess 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. With device tree we replace the above code with the +following device tree fragment: + + red-square { + compatible = "demo-shape"; + colour = "red"; + sides = <4>; + }; + + +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, + .probe = testfdt_drv_probe, + +The 'auto_alloc' feature allowed space for the platdata to be allocated +and zeroed before the driver's ofdata_to_platdata method is called. This +method reads the information out of the device tree and puts it in +dev->platdata. Then the probe method is called to set up the device. + +Note that both methods are optional. If you provide an ofdata_to_platdata +method then it wlil be called first (after bind). If you provide a probe +method it will be called next. + +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. + + +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. + + +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 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 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 device'. +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 + + +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. + +For SPL, it may be possible to fit in a simplified driver model with only +bind and probe methods, to reduce size. + +For pre-relocation we can simply call the driver model init function. 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-relocatoin device pointers. + +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