All of lore.kernel.org
 help / color / mirror / Atom feed
* [U-Boot] [PATCH v3 0/16] Driver model implementation, tests, demo and GPIO
@ 2013-06-19  3:52 Simon Glass
  2013-06-19  3:52 ` [U-Boot] [PATCH v3 01/16] sandbox: Make map_to_sysmem() use a constant pointer Simon Glass
                   ` (15 more replies)
  0 siblings, 16 replies; 30+ messages in thread
From: Simon Glass @ 2013-06-19  3:52 UTC (permalink / raw)
  To: u-boot


Note: If you are reviewing this code, but don't have a lot of time, please
consider starting with the 'demo' driver (patch 'dm: Add a
demonstration/example driver') since it clearly shows how devices and
uclasses work. Much of this series consists of test code and plumbing, so
is of less interest to driver authors.

This patch adds a driver model implementation. It is taken from
the driver model code developed 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>

Please see doc/driver-model/README.txt for details of how to run this and
what to look for. So far the documentation in doc/driver-model has not
been updated.

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)

Changes in v3:
- Add new patch to build a device tree file for sandbox
- Tidy up commenting of functions and structures
- Rename per_device_priv_size to per_device_auto_alloc_size, etc.
- Add a flag for tracking whether DM allocates/frees platform_data
- Add function/struct comments to tests
- Fix up demo command help
- Update demo driver to use device tree
- Update GPIO support to use new struct member names
- Tidy up comments/documentation in GPIO module
- Update sandbox GPIO header file comments
- Updated README.txt to cover changes since version 2
- Add new patch to move driver model documentation

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

Pavel Herrmann (1):
  dm: Add README for driver model

Simon Glass (15):
  sandbox: Make map_to_sysmem() use a constant pointer
  sandbox: Correct data sizes and printf() strings in fdtdec.c
  sandbox: config: Don't use 64-bit physical memory
  sandbox: Build a device tree file for sandbox
  Add cmd_process_error() to report and process errors
  sandbox: config: Enable driver model
  dm: Add base driver model support
  dm: Set up driver model after relocation
  dm: Add basic tests
  dm: Add a 'dm' command for testing
  dm: Add a demonstration/example driver
  dm: Add GPIO support and tests
  sandbox: Convert GPIOs to use driver model
  dm: Enable gpio command to support driver model
  dm: Move old driver model documentation into an 'old-docs' directory

 Makefile                                         |   5 +
 arch/sandbox/config.mk                           |   2 +
 arch/sandbox/include/asm/gpio.h                  |  14 +-
 arch/sandbox/include/asm/io.h                    |   2 +-
 arch/sandbox/include/asm/types.h                 |   4 +-
 board/sandbox/dts/sandbox.dts                    |  20 +
 board/sandbox/sandbox/sandbox.c                  |   7 +-
 common/Makefile                                  |   1 +
 common/board_r.c                                 |  33 ++
 common/cmd_demo.c                                | 115 +++++
 common/cmd_gpio.c                                | 127 +++++-
 common/command.c                                 |  10 +
 common/dm/Makefile                               |  43 ++
 common/dm/device.c                               | 331 ++++++++++++++
 common/dm/lists.c                                | 168 +++++++
 common/dm/root.c                                 | 115 +++++
 common/dm/uclass.c                               | 298 ++++++++++++
 common/dm/util.c                                 |  50 ++
 doc/driver-model/README.txt                      | 320 +++++++++++++
 doc/driver-model/old-docs/README                 |  29 ++
 doc/driver-model/{ => old-docs}/UDM-block.txt    |   0
 doc/driver-model/{ => old-docs}/UDM-cores.txt    |   0
 doc/driver-model/{ => old-docs}/UDM-design.txt   |   0
 doc/driver-model/{ => old-docs}/UDM-fpga.txt     |   0
 doc/driver-model/{ => old-docs}/UDM-gpio.txt     |   0
 doc/driver-model/{ => old-docs}/UDM-hwmon.txt    |   0
 doc/driver-model/{ => old-docs}/UDM-keyboard.txt |   0
 doc/driver-model/{ => old-docs}/UDM-mmc.txt      |   0
 doc/driver-model/{ => old-docs}/UDM-net.txt      |   0
 doc/driver-model/{ => old-docs}/UDM-pci.txt      |   0
 doc/driver-model/{ => old-docs}/UDM-pcmcia.txt   |   0
 doc/driver-model/{ => old-docs}/UDM-power.txt    |   0
 doc/driver-model/{ => old-docs}/UDM-rtc.txt      |   0
 doc/driver-model/{ => old-docs}/UDM-serial.txt   |   0
 doc/driver-model/{ => old-docs}/UDM-spi.txt      |   0
 doc/driver-model/{ => old-docs}/UDM-stdio.txt    |   0
 doc/driver-model/{ => old-docs}/UDM-tpm.txt      |   0
 doc/driver-model/{ => old-docs}/UDM-twserial.txt |   0
 doc/driver-model/{ => old-docs}/UDM-usb.txt      |   0
 doc/driver-model/{ => old-docs}/UDM-video.txt    |   0
 doc/driver-model/{ => old-docs}/UDM-watchdog.txt |   0
 drivers/demo/Makefile                            |  44 ++
 drivers/demo/demo-pdata.c                        |  60 +++
 drivers/demo/demo-shape.c                        | 143 ++++++
 drivers/demo/demo-simple.c                       |  61 +++
 drivers/demo/demo-uclass.c                       |  73 +++
 drivers/gpio/Makefile                            |   2 +
 drivers/gpio/gpio-uclass.c                       | 279 ++++++++++++
 drivers/gpio/sandbox.c                           | 210 +++++----
 include/asm-generic/global_data.h                |   8 +
 include/asm-generic/gpio.h                       | 104 +++++
 include/command.h                                |   9 +
 include/common.h                                 |   2 +-
 include/configs/sandbox.h                        |  11 +-
 include/dm-demo.h                                |  49 ++
 include/dm.h                                     |  27 ++
 include/dm/device-internal.h                     | 100 ++++
 include/dm/device.h                              | 149 ++++++
 include/dm/lists.h                               |  52 +++
 include/dm/platform_data.h                       |  35 ++
 include/dm/root.h                                |  66 +++
 include/dm/test.h                                | 180 ++++++++
 include/dm/uclass-id.h                           |  41 ++
 include/dm/uclass-internal.h                     |  98 ++++
 include/dm/uclass.h                              | 155 +++++++
 include/dm/ut.h                                  | 108 +++++
 include/dm/util.h                                |  42 ++
 lib/fdtdec.c                                     |   8 +-
 test/dm/Makefile                                 |  53 +++
 test/dm/cmd_dm.c                                 | 146 ++++++
 test/dm/core.c                                   | 557 +++++++++++++++++++++++
 test/dm/gpio.c                                   | 124 +++++
 test/dm/test-driver.c                            | 159 +++++++
 test/dm/test-fdt.c                               | 148 ++++++
 test/dm/test-main.c                              | 120 +++++
 test/dm/test-uclass.c                            | 117 +++++
 test/dm/test.dts                                 |  59 +++
 test/dm/ut.c                                     |  46 ++
 78 files changed, 5229 insertions(+), 110 deletions(-)
 create mode 100644 board/sandbox/dts/sandbox.dts
 create mode 100644 common/cmd_demo.c
 create mode 100644 common/dm/Makefile
 create mode 100644 common/dm/device.c
 create mode 100644 common/dm/lists.c
 create mode 100644 common/dm/root.c
 create mode 100644 common/dm/uclass.c
 create mode 100644 common/dm/util.c
 create mode 100644 doc/driver-model/README.txt
 create mode 100644 doc/driver-model/old-docs/README
 rename doc/driver-model/{ => old-docs}/UDM-block.txt (100%)
 rename doc/driver-model/{ => old-docs}/UDM-cores.txt (100%)
 rename doc/driver-model/{ => old-docs}/UDM-design.txt (100%)
 rename doc/driver-model/{ => old-docs}/UDM-fpga.txt (100%)
 rename doc/driver-model/{ => old-docs}/UDM-gpio.txt (100%)
 rename doc/driver-model/{ => old-docs}/UDM-hwmon.txt (100%)
 rename doc/driver-model/{ => old-docs}/UDM-keyboard.txt (100%)
 rename doc/driver-model/{ => old-docs}/UDM-mmc.txt (100%)
 rename doc/driver-model/{ => old-docs}/UDM-net.txt (100%)
 rename doc/driver-model/{ => old-docs}/UDM-pci.txt (100%)
 rename doc/driver-model/{ => old-docs}/UDM-pcmcia.txt (100%)
 rename doc/driver-model/{ => old-docs}/UDM-power.txt (100%)
 rename doc/driver-model/{ => old-docs}/UDM-rtc.txt (100%)
 rename doc/driver-model/{ => old-docs}/UDM-serial.txt (100%)
 rename doc/driver-model/{ => old-docs}/UDM-spi.txt (100%)
 rename doc/driver-model/{ => old-docs}/UDM-stdio.txt (100%)
 rename doc/driver-model/{ => old-docs}/UDM-tpm.txt (100%)
 rename doc/driver-model/{ => old-docs}/UDM-twserial.txt (100%)
 rename doc/driver-model/{ => old-docs}/UDM-usb.txt (100%)
 rename doc/driver-model/{ => old-docs}/UDM-video.txt (100%)
 rename doc/driver-model/{ => old-docs}/UDM-watchdog.txt (100%)
 create mode 100644 drivers/demo/Makefile
 create mode 100644 drivers/demo/demo-pdata.c
 create mode 100644 drivers/demo/demo-shape.c
 create mode 100644 drivers/demo/demo-simple.c
 create mode 100644 drivers/demo/demo-uclass.c
 create mode 100644 drivers/gpio/gpio-uclass.c
 create mode 100644 include/dm-demo.h
 create mode 100644 include/dm.h
 create mode 100644 include/dm/device-internal.h
 create mode 100644 include/dm/device.h
 create mode 100644 include/dm/lists.h
 create mode 100644 include/dm/platform_data.h
 create mode 100644 include/dm/root.h
 create mode 100644 include/dm/test.h
 create mode 100644 include/dm/uclass-id.h
 create mode 100644 include/dm/uclass-internal.h
 create mode 100644 include/dm/uclass.h
 create mode 100644 include/dm/ut.h
 create mode 100644 include/dm/util.h
 create mode 100644 test/dm/Makefile
 create mode 100644 test/dm/cmd_dm.c
 create mode 100644 test/dm/core.c
 create mode 100644 test/dm/gpio.c
 create mode 100644 test/dm/test-driver.c
 create mode 100644 test/dm/test-fdt.c
 create mode 100644 test/dm/test-main.c
 create mode 100644 test/dm/test-uclass.c
 create mode 100644 test/dm/test.dts
 create mode 100644 test/dm/ut.c

-- 
1.8.3

^ permalink raw reply	[flat|nested] 30+ messages in thread

* [U-Boot] [PATCH v3 01/16] sandbox: Make map_to_sysmem() use a constant pointer
  2013-06-19  3:52 [U-Boot] [PATCH v3 0/16] Driver model implementation, tests, demo and GPIO Simon Glass
@ 2013-06-19  3:52 ` Simon Glass
  2013-06-19  3:52 ` [U-Boot] [PATCH v3 02/16] sandbox: Correct data sizes and printf() strings in fdtdec.c Simon Glass
                   ` (14 subsequent siblings)
  15 siblings, 0 replies; 30+ messages in thread
From: Simon Glass @ 2013-06-19  3:52 UTC (permalink / raw)
  To: u-boot

Very often a constant pointer is passed to this function, so we should
declare this, since map_to_sysmem() does not change the pointer.

Signed-off-by: Simon Glass <sjg@chromium.org>
---
Changes in v3: None
Changes in v2: None

 arch/sandbox/include/asm/io.h | 2 +-
 include/common.h              | 2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/arch/sandbox/include/asm/io.h b/arch/sandbox/include/asm/io.h
index 0c022f1..321bc0d 100644
--- a/arch/sandbox/include/asm/io.h
+++ b/arch/sandbox/include/asm/io.h
@@ -54,6 +54,6 @@ static inline void unmap_sysmem(const void *vaddr)
 }
 
 /* Map from a pointer to our RAM buffer */
-phys_addr_t map_to_sysmem(void *ptr);
+phys_addr_t map_to_sysmem(const void *ptr);
 
 #endif
diff --git a/include/common.h b/include/common.h
index 126891d..ad3a64b 100644
--- a/include/common.h
+++ b/include/common.h
@@ -926,7 +926,7 @@ static inline void unmap_sysmem(const void *vaddr)
 {
 }
 
-static inline phys_addr_t map_to_sysmem(void *ptr)
+static inline phys_addr_t map_to_sysmem(const void *ptr)
 {
 	return (phys_addr_t)(uintptr_t)ptr;
 }
-- 
1.8.3

^ permalink raw reply related	[flat|nested] 30+ messages in thread

* [U-Boot] [PATCH v3 02/16] sandbox: Correct data sizes and printf() strings in fdtdec.c
  2013-06-19  3:52 [U-Boot] [PATCH v3 0/16] Driver model implementation, tests, demo and GPIO Simon Glass
  2013-06-19  3:52 ` [U-Boot] [PATCH v3 01/16] sandbox: Make map_to_sysmem() use a constant pointer Simon Glass
@ 2013-06-19  3:52 ` Simon Glass
  2013-06-19  3:52 ` [U-Boot] [PATCH v3 03/16] sandbox: config: Don't use 64-bit physical memory Simon Glass
                   ` (13 subsequent siblings)
  15 siblings, 0 replies; 30+ messages in thread
From: Simon Glass @ 2013-06-19  3:52 UTC (permalink / raw)
  To: u-boot

There are a few warnings in this file when building for sandbox. Addresses
coming from the device tree need to be treated as ulong as elsewhere in
U-Boot and we must use map_sysmem() to convert to a pointer when needed.

Signed-off-by: Simon Glass <sjg@chromium.org>
---
Changes in v3: None
Changes in v2: None

 lib/fdtdec.c | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/lib/fdtdec.c b/lib/fdtdec.c
index 005ad3d..dc53a1a 100644
--- a/lib/fdtdec.c
+++ b/lib/fdtdec.c
@@ -93,10 +93,10 @@ fdt_addr_t fdtdec_get_addr_size(const void *blob, int node,
 			size = (fdt_size_t *)((char *)cell +
 					sizeof(fdt_addr_t));
 			*sizep = fdt_size_to_cpu(*size);
-			debug("addr=%p, size=%p\n", (void *)addr,
-			      (void *)*sizep);
+			debug("addr=%08lx, size=%08x\n",
+			      (ulong)addr, *sizep);
 		} else {
-			debug("%p\n", (void *)addr);
+			debug("%08lx\n", (ulong)addr);
 		}
 		return addr;
 	}
@@ -618,7 +618,7 @@ int fdtdec_decode_region(const void *blob, int node,
 	if (!cell || (len != sizeof(fdt_addr_t) * 2))
 		return -1;
 
-	*ptrp = (void *)fdt_addr_to_cpu(*cell);
+	*ptrp = map_sysmem(fdt_addr_to_cpu(*cell), *size);
 	*size = fdt_size_to_cpu(cell[1]);
 	debug("%s: size=%zx\n", __func__, *size);
 	return 0;
-- 
1.8.3

^ permalink raw reply related	[flat|nested] 30+ messages in thread

* [U-Boot] [PATCH v3 03/16] sandbox: config: Don't use 64-bit physical memory
  2013-06-19  3:52 [U-Boot] [PATCH v3 0/16] Driver model implementation, tests, demo and GPIO Simon Glass
  2013-06-19  3:52 ` [U-Boot] [PATCH v3 01/16] sandbox: Make map_to_sysmem() use a constant pointer Simon Glass
  2013-06-19  3:52 ` [U-Boot] [PATCH v3 02/16] sandbox: Correct data sizes and printf() strings in fdtdec.c Simon Glass
@ 2013-06-19  3:52 ` Simon Glass
  2013-06-19  3:52 ` [U-Boot] [PATCH v3 04/16] sandbox: Build a device tree file for sandbox Simon Glass
                   ` (12 subsequent siblings)
  15 siblings, 0 replies; 30+ messages in thread
From: Simon Glass @ 2013-06-19  3:52 UTC (permalink / raw)
  To: u-boot

Sandbox uses an emulated memory map which is quite small. We don't need the
CONFIG_PHYS_64BIT option since we can address memory with a 32-bit offset
into our ram_buf.

Adjust the phys_addr_t and phys_size_t types accordingly.

Signed-off-by: Simon Glass <sjg@chromium.org>
---
Changes in v3: None
Changes in v2: None

 arch/sandbox/include/asm/types.h | 4 ++--
 include/configs/sandbox.h        | 1 -
 2 files changed, 2 insertions(+), 3 deletions(-)

diff --git a/arch/sandbox/include/asm/types.h b/arch/sandbox/include/asm/types.h
index 2316c2d..07c2986 100644
--- a/arch/sandbox/include/asm/types.h
+++ b/arch/sandbox/include/asm/types.h
@@ -64,8 +64,8 @@ typedef unsigned long long u64;
 #define BITS_PER_LONG	CONFIG_SANDBOX_BITS_PER_LONG
 
 typedef unsigned long dma_addr_t;
-typedef unsigned long phys_addr_t;
-typedef unsigned long phys_size_t;
+typedef u32 phys_addr_t;
+typedef u32 phys_size_t;
 
 #endif /* __KERNEL__ */
 
diff --git a/include/configs/sandbox.h b/include/configs/sandbox.h
index 788207d..78eda90 100644
--- a/include/configs/sandbox.h
+++ b/include/configs/sandbox.h
@@ -73,7 +73,6 @@
 #define CONFIG_SYS_LOAD_ADDR		0x00000000
 #define CONFIG_SYS_MEMTEST_START	0x00100000
 #define CONFIG_SYS_MEMTEST_END		(CONFIG_SYS_MEMTEST_START + 0x1000)
-#define CONFIG_PHYS_64BIT
 #define CONFIG_SYS_FDT_LOAD_ADDR	0x1000000
 
 /* Size of our emulated memory */
-- 
1.8.3

^ permalink raw reply related	[flat|nested] 30+ messages in thread

* [U-Boot] [PATCH v3 04/16] sandbox: Build a device tree file for sandbox
  2013-06-19  3:52 [U-Boot] [PATCH v3 0/16] Driver model implementation, tests, demo and GPIO Simon Glass
                   ` (2 preceding siblings ...)
  2013-06-19  3:52 ` [U-Boot] [PATCH v3 03/16] sandbox: config: Don't use 64-bit physical memory Simon Glass
@ 2013-06-19  3:52 ` Simon Glass
  2013-06-19  3:52 ` [U-Boot] [PATCH v3 05/16] Add cmd_process_error() to report and process errors Simon Glass
                   ` (11 subsequent siblings)
  15 siblings, 0 replies; 30+ messages in thread
From: Simon Glass @ 2013-06-19  3:52 UTC (permalink / raw)
  To: u-boot

Add support for building a device tree for sandbox's CONFIG_OF_HOSTFILE
option to make it easier to use device tree with sandbox.

This adjusts the Makefile to build a u-boot.dtb file which can be passed
to sandbox U-Boot with:

   ./u-boot -d u-boot.dtb

Signed-off-by: Simon Glass <sjg@chromium.org>
---
Changes in v3:
- Add new patch to build a device tree file for sandbox

Changes in v2: None

 Makefile                      |  1 +
 arch/sandbox/config.mk        |  2 ++
 board/sandbox/dts/sandbox.dts | 20 ++++++++++++++++++++
 include/configs/sandbox.h     |  1 +
 4 files changed, 24 insertions(+)
 create mode 100644 board/sandbox/dts/sandbox.dts

diff --git a/Makefile b/Makefile
index 693b3f2..ed8068b 100644
--- a/Makefile
+++ b/Makefile
@@ -413,6 +413,7 @@ ALL-$(CONFIG_NAND_U_BOOT) += $(obj)u-boot-nand.bin
 ALL-$(CONFIG_ONENAND_U_BOOT) += $(obj)u-boot-onenand.bin
 ALL-$(CONFIG_SPL) += $(obj)spl/u-boot-spl.bin
 ALL-$(CONFIG_OF_SEPARATE) += $(obj)u-boot.dtb $(obj)u-boot-dtb.bin
+ALL-$(CONFIG_OF_HOSTFILE) += $(obj)u-boot.dtb
 ifneq ($(CONFIG_SPL_TARGET),)
 ALL-$(CONFIG_SPL) += $(obj)$(subst ",,$(CONFIG_SPL_TARGET))
 endif
diff --git a/arch/sandbox/config.mk b/arch/sandbox/config.mk
index 988b52c..52d164f 100644
--- a/arch/sandbox/config.mk
+++ b/arch/sandbox/config.mk
@@ -23,3 +23,5 @@ PLATFORM_LIBS += -lrt
 
 # Support generic board on sandbox
 __HAVE_ARCH_GENERIC_BOARD := y
+
+CONFIG_ARCH_DEVICE_TREE := sandbox
diff --git a/board/sandbox/dts/sandbox.dts b/board/sandbox/dts/sandbox.dts
new file mode 100644
index 0000000..96a4438
--- /dev/null
+++ b/board/sandbox/dts/sandbox.dts
@@ -0,0 +1,20 @@
+/dts-v1/;
+
+/ {
+	triangle {
+		compatible = "demo-shape";
+		colour = "cyan";
+		sides = <3>;
+		character = <83>;
+	};
+	square {
+		compatible = "demo-shape";
+		colour = "blue";
+		sides = <4>;
+	};
+	hexagon {
+		compatible = "demo-simple";
+		colour = "white";
+		sides = <6>;
+	};
+};
diff --git a/include/configs/sandbox.h b/include/configs/sandbox.h
index 78eda90..14f5f98 100644
--- a/include/configs/sandbox.h
+++ b/include/configs/sandbox.h
@@ -31,6 +31,7 @@
 #define CONFIG_LMB
 #define CONFIG_FIT
 #define CONFIG_CMD_FDT
+#define CONFIG_DEFAULT_DEVICE_TREE	sandbox
 
 #define CONFIG_FS_FAT
 #define CONFIG_FS_EXT4
-- 
1.8.3

^ permalink raw reply related	[flat|nested] 30+ messages in thread

* [U-Boot] [PATCH v3 05/16] Add cmd_process_error() to report and process errors
  2013-06-19  3:52 [U-Boot] [PATCH v3 0/16] Driver model implementation, tests, demo and GPIO Simon Glass
                   ` (3 preceding siblings ...)
  2013-06-19  3:52 ` [U-Boot] [PATCH v3 04/16] sandbox: Build a device tree file for sandbox Simon Glass
@ 2013-06-19  3:52 ` Simon Glass
  2013-06-19  3:52 ` [U-Boot] [PATCH v3 06/16] sandbox: config: Enable driver model Simon Glass
                   ` (10 subsequent siblings)
  15 siblings, 0 replies; 30+ messages in thread
From: Simon Glass @ 2013-06-19  3:52 UTC (permalink / raw)
  To: u-boot

U-Boot now uses errors defined in include/errno.h which are negative
integers. Commands which fail need to report the error and return 1
to indicate failure. Add this functionality in cmd_process_error().

For now this merely reports the error number. It would be possible
also to produce a helpful error message by storing the error strings
in U-Boot.

Signed-off-by: Simon Glass <sjg@chromium.org>
---
Changes in v3: None
Changes in v2: None

 common/command.c  | 10 ++++++++++
 include/command.h |  9 +++++++++
 2 files changed, 19 insertions(+)

diff --git a/common/command.c b/common/command.c
index 305a236..70faa60 100644
--- a/common/command.c
+++ b/common/command.c
@@ -554,3 +554,13 @@ enum command_ret_t cmd_process(int flag, int argc, char * const argv[],
 		rc = cmd_usage(cmdtp);
 	return rc;
 }
+
+int cmd_process_error(cmd_tbl_t *cmdtp, int err)
+{
+	if (err) {
+		printf("Command '%s' failed: Error %d\n", cmdtp->name, err);
+		return 1;
+	}
+
+	return 0;
+}
diff --git a/include/command.h b/include/command.h
index 65692fd..e82c555 100644
--- a/include/command.h
+++ b/include/command.h
@@ -80,6 +80,15 @@ extern int var_complete(int argc, char * const argv[], char last_char, int maxv,
 extern int cmd_auto_complete(const char *const prompt, char *buf, int *np, int *colp);
 #endif
 
+/**
+ * cmd_process_error() - report and process a possible error
+ *
+ * @cmdtp: Command which caused the error
+ * @err: Error code (0 if none, -ve for error, like -EIO)
+ * @return 0 if there is not error, 1 (CMD_RET_FAILURE) if an error is found
+ */
+int cmd_process_error(cmd_tbl_t *cmdtp, int err);
+
 /*
  * Monitor Command
  *
-- 
1.8.3

^ permalink raw reply related	[flat|nested] 30+ messages in thread

* [U-Boot] [PATCH v3 06/16] sandbox: config: Enable driver model
  2013-06-19  3:52 [U-Boot] [PATCH v3 0/16] Driver model implementation, tests, demo and GPIO Simon Glass
                   ` (4 preceding siblings ...)
  2013-06-19  3:52 ` [U-Boot] [PATCH v3 05/16] Add cmd_process_error() to report and process errors Simon Glass
@ 2013-06-19  3:52 ` Simon Glass
  2013-06-19  3:52 ` [U-Boot] [PATCH v3 07/16] dm: Add base driver model support Simon Glass
                   ` (9 subsequent siblings)
  15 siblings, 0 replies; 30+ messages in thread
From: Simon Glass @ 2013-06-19  3:52 UTC (permalink / raw)
  To: u-boot

Use driver model in sandbox to permit running of driver model unit test.

Signed-off-by: Simon Glass <sjg@chromium.org>
---
Changes in v3: None
Changes in v2: None

 include/configs/sandbox.h | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/include/configs/sandbox.h b/include/configs/sandbox.h
index 14f5f98..162c26c 100644
--- a/include/configs/sandbox.h
+++ b/include/configs/sandbox.h
@@ -22,6 +22,8 @@
 #ifndef __CONFIG_H
 #define __CONFIG_H
 
+#define CONFIG_DM
+
 /* Number of bits in a C 'long' on this architecture */
 #define CONFIG_SANDBOX_BITS_PER_LONG	64
 
-- 
1.8.3

^ permalink raw reply related	[flat|nested] 30+ messages in thread

* [U-Boot] [PATCH v3 07/16] dm: Add base driver model support
  2013-06-19  3:52 [U-Boot] [PATCH v3 0/16] Driver model implementation, tests, demo and GPIO Simon Glass
                   ` (5 preceding siblings ...)
  2013-06-19  3:52 ` [U-Boot] [PATCH v3 06/16] sandbox: config: Enable driver model Simon Glass
@ 2013-06-19  3:52 ` Simon Glass
  2013-06-28 20:53   ` Marek Vasut
  2013-06-19  3:52 ` [U-Boot] [PATCH v3 08/16] dm: Set up driver model after relocation Simon Glass
                   ` (8 subsequent siblings)
  15 siblings, 1 reply; 30+ messages in thread
From: Simon Glass @ 2013-06-19  3:52 UTC (permalink / raw)
  To: u-boot

Add driver model functionality for generic board.

This includes data structures and base code for registering devices and
uclasses (groups of devices with the same purpose, e.g. all I2C ports will
be in the same uclass).

The feature is enabled with CONFIG_DM.

Signed-off-by: Simon Glass <sjg@chromium.org>
Signed-off-by: Marek Vasut <marex@denx.de>
Signed-off-by: Pavel Herrmann <morpheus.ibis@gmail.com>
Signed-off-by: Viktor K?iv?k <viktor.krivak@gmail.com>
Signed-off-by: Tomas Hlavacek <tmshlvck@gmail.com>
---
Changes in v3:
- Tidy up commenting of functions and structures
- Rename per_device_priv_size to per_device_auto_alloc_size, etc.
- Add a flag for tracking whether DM allocates/frees platform_data

Changes in v2: None

 Makefile                          |   2 +
 common/dm/Makefile                |  43 +++++
 common/dm/device.c                | 331 ++++++++++++++++++++++++++++++++++++++
 common/dm/lists.c                 | 168 +++++++++++++++++++
 common/dm/root.c                  | 115 +++++++++++++
 common/dm/uclass.c                | 298 ++++++++++++++++++++++++++++++++++
 common/dm/util.c                  |  50 ++++++
 include/asm-generic/global_data.h |   8 +
 include/dm.h                      |  27 ++++
 include/dm/device-internal.h      | 100 ++++++++++++
 include/dm/device.h               | 149 +++++++++++++++++
 include/dm/lists.h                |  52 ++++++
 include/dm/platform_data.h        |  35 ++++
 include/dm/root.h                 |  66 ++++++++
 include/dm/uclass-id.h            |  41 +++++
 include/dm/uclass-internal.h      |  98 +++++++++++
 include/dm/uclass.h               | 155 ++++++++++++++++++
 include/dm/util.h                 |  42 +++++
 18 files changed, 1780 insertions(+)
 create mode 100644 common/dm/Makefile
 create mode 100644 common/dm/device.c
 create mode 100644 common/dm/lists.c
 create mode 100644 common/dm/root.c
 create mode 100644 common/dm/uclass.c
 create mode 100644 common/dm/util.c
 create mode 100644 include/dm.h
 create mode 100644 include/dm/device-internal.h
 create mode 100644 include/dm/device.h
 create mode 100644 include/dm/lists.h
 create mode 100644 include/dm/platform_data.h
 create mode 100644 include/dm/root.h
 create mode 100644 include/dm/uclass-id.h
 create mode 100644 include/dm/uclass-internal.h
 create mode 100644 include/dm/uclass.h
 create mode 100644 include/dm/util.h

diff --git a/Makefile b/Makefile
index ed8068b..c1c13c8 100644
--- a/Makefile
+++ b/Makefile
@@ -337,6 +337,8 @@ LIBS-y += api/libapi.o
 LIBS-y += post/libpost.o
 LIBS-y += test/libtest.o
 
+LIBS-$(CONFIG_DM) += common/dm/libdm.o
+
 ifneq ($(CONFIG_AM33XX)$(CONFIG_OMAP34XX)$(CONFIG_OMAP44XX)$(CONFIG_OMAP54XX)$(CONFIG_TI814X),)
 LIBS-y += $(CPUDIR)/omap-common/libomap-common.o
 endif
diff --git a/common/dm/Makefile b/common/dm/Makefile
new file mode 100644
index 0000000..3ee120a
--- /dev/null
+++ b/common/dm/Makefile
@@ -0,0 +1,43 @@
+#
+# Copyright (c) 2013 Google, Inc
+#
+# (C) Copyright 2004-2006
+# Wolfgang Denk, DENX Software Engineering, wd at denx.de.
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License as
+# published by the Free Software Foundation; either version 2 of
+# the License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston,
+# MA 02111-1307 USA
+#
+
+include $(TOPDIR)/config.mk
+
+LIB	:= $(obj)libdm.o
+
+COBJS	:= device.o lists.o root.o uclass.o util.o
+SRCS	:= $(COBJS:.o=.c)
+OBJS	:= $(addprefix $(obj),$(COBJS))
+
+all:	$(LIB)
+
+$(LIB):	$(obj).depend $(OBJS)
+	$(call cmd_link_o_target, $(OBJS))
+
+#########################################################################
+
+# defines $(obj).depend target
+include $(SRCTREE)/rules.mk
+
+sinclude $(obj).depend
+
+#########################################################################
diff --git a/common/dm/device.c b/common/dm/device.c
new file mode 100644
index 0000000..0d59f2c
--- /dev/null
+++ b/common/dm/device.c
@@ -0,0 +1,331 @@
+/*
+ * Device manager
+ *
+ * Copyright (c) 2013 Google, Inc
+ *
+ * (C) Copyright 2012
+ * Pavel Herrmann <morpheus.ibis@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
+ * MA 02111-1307 USA
+ */
+
+#include <common.h>
+#include <malloc.h>
+#include <dm/device.h>
+#include <dm/device-internal.h>
+#include <dm/lists.h>
+#include <dm/platform_data.h>
+#include <dm/uclass.h>
+#include <dm/uclass-internal.h>
+#include <dm/util.h>
+#include <linux/err.h>
+#include <linux/list.h>
+
+/**
+ * device_chld_unbind() - Unbind all device's children from the device
+ * @dev:	The device that is to be stripped of its children
+ * @return 0 on success, -ve on error
+ */
+static int device_chld_unbind(struct device *dev)
+{
+	struct device *pos, *n;
+	int ret;
+
+	assert(dev);
+
+	list_for_each_entry_safe(pos, n, &dev->child_head, sibling_node) {
+		ret = device_unbind(pos);
+		if (ret)
+			return ret;
+	}
+
+	return 0;
+}
+
+/**
+ * device_chld_remove() - Stop all device's children
+ * @dev:	The device whose children are to be removed
+ * @return 0 on success, -ve on error
+ */
+static int device_chld_remove(struct device *dev)
+{
+	struct device *pos, *n;
+	int ret;
+
+	assert(dev);
+
+	list_for_each_entry_safe(pos, n, &dev->child_head, sibling_node) {
+		ret = device_remove(pos);
+		if (ret)
+			return ret;
+	}
+
+	return 0;
+}
+
+int device_bind(struct device *parent, struct driver *drv, const char *name,
+		void *platform_data, int of_offset, struct device **devp)
+{
+	struct device *dev;
+	struct uclass *uc;
+	int ret = 0;
+
+	*devp = NULL;
+	if (!name)
+		return -EINVAL;
+
+	ret = uclass_get(drv->id, &uc);
+	if (ret)
+		return ret;
+
+	dev = calloc(1, sizeof(struct device));
+	if (!dev)
+		return -ENOMEM;
+
+	INIT_LIST_HEAD(&dev->sibling_node);
+	INIT_LIST_HEAD(&dev->child_head);
+	INIT_LIST_HEAD(&dev->uclass_node);
+	dev->platform_data = platform_data;
+	dev->name = name;
+	dev->of_offset = of_offset;
+	dev->parent = parent;
+	dev->driver = drv;
+	dev->uclass = uc;
+	if (!dev->platform_data && drv->platform_data_auto_alloc_size)
+		dev->flags |= DM_FLAG_ALLOC_PDATA;
+
+	/* put dev into parent's successor list */
+	if (parent)
+		list_add_tail(&dev->sibling_node, &parent->child_head);
+
+	ret = uclass_bind_device(dev);
+	if (ret)
+		goto fail_bind;
+
+	/* if we fail to bind we remove device from successors and free it */
+	if (drv->bind) {
+		ret = drv->bind(dev);
+		if (ret) {
+			if (uclass_unbind_device(dev)) {
+				dm_warn("Failed to unbind dev '%s' on error path\n",
+					dev->name);
+			}
+			goto fail_bind;
+		}
+	}
+	if (parent)
+		dm_dbg("Bound device %s to %s\n", dev->name, parent->name);
+	*devp = dev;
+
+	return 0;
+
+fail_bind:
+	list_del(&dev->sibling_node);
+	free(dev);
+	return ret;
+}
+
+int device_bind_by_name(struct device *parent, const struct driver_info *info,
+			struct device **devp)
+{
+	struct driver *drv;
+
+	drv = lists_driver_lookup_name(info->name);
+	if (!drv)
+		return -ENOENT;
+
+	return device_bind(parent, drv, info->name,
+			   (void *)info->platform_data, -1, devp);
+}
+
+int device_unbind(struct device *dev)
+{
+	struct driver *drv;
+	int ret;
+
+	if (!dev)
+		return -EINVAL;
+
+	if (dev->flags & DM_FLAG_ACTIVATED)
+		return -EINVAL;
+
+	drv = dev->driver;
+	assert(drv);
+
+	if (drv->unbind) {
+		ret = drv->unbind(dev);
+		if (ret)
+			return ret;
+	}
+
+	ret = device_chld_unbind(dev);
+	if (ret)
+		return ret;
+
+	ret = uclass_unbind_device(dev);
+	if (ret)
+		return ret;
+
+	if (dev->parent)
+		list_del(&dev->sibling_node);
+	free(dev);
+
+	return 0;
+}
+
+/**
+ * device_free() - Free memory buffers allocated by a device
+ * @dev:	Device that is to be started
+ */
+static void device_free(struct device *dev)
+{
+	int size;
+
+	if (dev->driver->priv_auto_alloc_size) {
+		free(dev->priv);
+		dev->priv = NULL;
+	}
+	if (dev->flags & DM_FLAG_ALLOC_PDATA) {
+		free(dev->platform_data);
+		dev->platform_data = NULL;
+	}
+	size = dev->uclass->uc_drv->per_device_auto_alloc_size;
+	if (size) {
+		free(dev->uclass_priv);
+		dev->uclass_priv = NULL;
+	}
+}
+
+int device_probe(struct device *dev)
+{
+	struct driver *drv;
+	int size = 0;
+	int ret;
+
+	if (!dev)
+		return -EINVAL;
+
+	if (dev->flags & DM_FLAG_ACTIVATED)
+		return 0;
+
+	drv = dev->driver;
+	assert(drv);
+
+	/* Allocate private data and platform_data if requested */
+	if (drv->priv_auto_alloc_size) {
+		dev->priv = calloc(1, drv->priv_auto_alloc_size);
+		if (!dev->priv) {
+			ret = -ENOMEM;
+			goto fail;
+		}
+	}
+	/* Allocate private data if requested */
+	if (dev->flags & DM_FLAG_ALLOC_PDATA) {
+		dev->platform_data = calloc(1, drv->platform_data_auto_alloc_size);
+		if (!dev->platform_data) {
+			ret = -ENOMEM;
+			goto fail;
+		}
+	}
+	size = dev->uclass->uc_drv->per_device_auto_alloc_size;
+	if (size) {
+		dev->uclass_priv = calloc(1, size);
+		if (!dev->uclass_priv) {
+			ret = -ENOMEM;
+			goto fail;
+		}
+	}
+
+	/* Ensure all parents are probed */
+	if (dev->parent) {
+		ret = device_probe(dev->parent);
+		if (ret)
+			goto fail;
+	}
+
+	if (drv->probe) {
+		ret = drv->probe(dev);
+		if (ret)
+			goto fail;
+	}
+
+	dev->flags |= DM_FLAG_ACTIVATED;
+
+	ret = uclass_post_probe_device(dev);
+	if (ret) {
+		dev->flags &= ~DM_FLAG_ACTIVATED;
+		goto fail_uclass;
+	}
+
+	return 0;
+fail_uclass:
+	if (device_remove(dev)) {
+		dm_warn("%s: Device '%s' failed to remove on error path\n",
+			__func__, dev->name);
+	}
+fail:
+	device_free(dev);
+
+	return ret;
+}
+
+int device_remove(struct device *dev)
+{
+	struct driver *drv;
+	int ret;
+
+	if (!dev)
+		return -EINVAL;
+
+	if (!(dev->flags & DM_FLAG_ACTIVATED))
+		return 0;
+
+	drv = dev->driver;
+	assert(drv);
+
+	ret = uclass_pre_remove_device(dev);
+	if (ret)
+		return ret;
+
+	ret = device_chld_remove(dev);
+	if (ret)
+		goto err;
+
+	if (drv->remove) {
+		ret = drv->remove(dev);
+		if (ret)
+			goto err_remove;
+	}
+
+	device_free(dev);
+
+	dev->flags &= ~DM_FLAG_ACTIVATED;
+
+	return 0;
+
+err_remove:
+	/* We can't put the children back */
+	dm_warn("%s: Device '%s' failed to remove, but children are gone\n",
+		__func__, dev->name);
+err:
+	ret = uclass_post_probe_device(dev);
+	if (ret) {
+		dm_warn("%s: Device '%s' failed to post_probe on error path\n",
+			__func__, dev->name);
+	}
+
+	return ret;
+}
diff --git a/common/dm/lists.c b/common/dm/lists.c
new file mode 100644
index 0000000..52172b5
--- /dev/null
+++ b/common/dm/lists.c
@@ -0,0 +1,168 @@
+/*
+ * Copyright (c) 2013 Google, Inc
+ *
+ * (C) Copyright 2012
+ * Marek Vasut <marex@denx.de>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
+ * MA 02111-1307 USA
+ */
+
+#include <common.h>
+#include <errno.h>
+#include <dm/device.h>
+#include <dm/device-internal.h>
+#include <dm/platform_data.h>
+#include <dm/uclass.h>
+#include <dm/util.h>
+#include <linux/compiler.h>
+
+struct driver *lists_driver_lookup_name(const char *name)
+{
+	struct driver *drv =
+		ll_entry_start(struct driver, driver);
+	const int n_ents = ll_entry_count(struct driver, driver);
+	struct driver *entry;
+	int len;
+
+	if (!drv || !n_ents)
+		return NULL;
+
+	len = strlen(name);
+
+	for (entry = drv; entry != drv + n_ents; entry++) {
+		if (strncmp(name, entry->name, len))
+			continue;
+
+		/* Full match */
+		if (len == strlen(entry->name))
+			return entry;
+	}
+
+	/* Not found */
+	return NULL;
+}
+
+struct uclass_driver *lists_uclass_lookup(enum uclass_id id)
+{
+	struct uclass_driver *uclass =
+		ll_entry_start(struct uclass_driver, uclass);
+	const int n_ents = ll_entry_count(struct uclass_driver, uclass);
+	struct uclass_driver *entry;
+
+	if ((id == UCLASS_INVALID) || !uclass)
+		return NULL;
+
+	for (entry = uclass; entry != uclass + n_ents; entry++) {
+		if (entry->id == id)
+			return entry;
+	}
+
+	return NULL;
+}
+
+int lists_bind_drivers(struct device *parent)
+{
+	struct driver_info *info =
+		ll_entry_start(struct driver_info, driver_info);
+	const int n_ents = ll_entry_count(struct driver_info, driver_info);
+	struct driver_info *entry;
+	struct device *dev;
+	int result = 0;
+	int ret;
+
+	for (entry = info; entry != info + n_ents; entry++) {
+		ret = device_bind_by_name(parent, entry, &dev);
+		if (ret) {
+			dm_warn("No match for driver '%s'\n", entry->name);
+			if (!result || ret != -ENOENT)
+				result = ret;
+		}
+	}
+
+	return result;
+}
+
+#ifdef CONFIG_OF_CONTROL
+/**
+ * driver_check_compatible() - Check if a driver is compatible with this node
+ *
+ * @param blob:		Device tree pointer
+ * @param offset:	Offset of node in device tree
+ * @param of_matchL	List of compatible strings to match
+ * @return 0 if there is a match, -ENOENT if no match, -ENODEV if the node
+ * does not have a compatible string, other error <0 if there is a device
+ * tree error
+ */
+static int driver_check_compatible(const void *blob, int offset,
+				   const struct device_id *of_match)
+{
+	int ret;
+
+	if (!of_match)
+		return -ENOENT;
+
+	while (of_match->compatible) {
+		ret = fdt_node_check_compatible(blob, offset,
+						of_match->compatible);
+		if (!ret)
+			return 0;
+		else if (ret == -FDT_ERR_NOTFOUND)
+			return -ENODEV;
+		else if (ret < 0)
+			return -EINVAL;
+		of_match++;
+	}
+
+	return -ENOENT;
+}
+
+int lists_bind_fdt(struct device *parent, const void *blob, int offset)
+{
+	struct driver *driver = ll_entry_start(struct driver, driver);
+	const int n_ents = ll_entry_count(struct driver, driver);
+	struct driver *entry;
+	struct device *dev;
+	const char *name;
+	int result = 0;
+	int ret;
+
+	dm_dbg("bind node %s\n", fdt_get_name(blob, offset, NULL));
+	for (entry = driver; entry != driver + n_ents; entry++) {
+		ret = driver_check_compatible(blob, offset, entry->of_match);
+		if (ret == -ENOENT) {
+			continue;
+		} else if (ret == -ENODEV) {
+			break;
+		} else if (ret) {
+			dm_warn("Device tree error at offset %d\n", offset);
+			if (!result || ret != -ENOENT)
+				result = ret;
+			break;
+		}
+
+		name = fdt_get_name(blob, offset, NULL);
+		dm_dbg("   - found match at '%s'\n", entry->name);
+		ret = device_bind(parent, entry, name, NULL, offset, &dev);
+		if (ret) {
+			dm_warn("No match for driver '%s'\n", entry->name);
+			if (!result || ret != -ENOENT)
+				result = ret;
+		}
+	}
+
+	return result;
+}
+#endif
diff --git a/common/dm/root.c b/common/dm/root.c
new file mode 100644
index 0000000..dc5e3fd
--- /dev/null
+++ b/common/dm/root.c
@@ -0,0 +1,115 @@
+/*
+ * Copyright (c) 2013 Google, Inc
+ *
+ * (C) Copyright 2012
+ * Pavel Herrmann <morpheus.ibis@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
+ * MA 02111-1307 USA
+ */
+
+#include <common.h>
+#include <errno.h>
+#include <malloc.h>
+#include <dm/device.h>
+#include <dm/device-internal.h>
+#include <dm/lists.h>
+#include <dm/platform_data.h>
+#include <dm/uclass.h>
+#include <dm/util.h>
+#include <linux/list.h>
+
+DECLARE_GLOBAL_DATA_PTR;
+
+static const struct driver_info root_info = {
+	.name		= "root_driver",
+};
+
+struct device *dm_root(void)
+{
+	if (!gd->dm_root) {
+		dm_warn("Virtual root driver does not exist!\n");
+		return NULL;
+	}
+
+	return gd->dm_root;
+}
+
+int dm_init(void)
+{
+	int ret;
+
+	if (gd->dm_root) {
+		dm_warn("Virtual root driver already exists!\n");
+		return -EINVAL;
+	}
+	INIT_LIST_HEAD(&gd->uclass_root);
+
+	ret = device_bind_by_name(NULL, &root_info, &gd->dm_root);
+	if (ret)
+		return ret;
+
+	return 0;
+}
+
+int dm_scan_platform_data(void)
+{
+	int ret;
+
+	ret = lists_bind_drivers(gd->dm_root);
+	if (ret == -ENOENT) {
+		dm_warn("Some drivers were not found\n");
+		ret = 0;
+	}
+	if (ret)
+		return ret;
+
+	return 0;
+}
+
+#ifdef CONFIG_OF_CONTROL
+int dm_scan_fdt(const void *blob)
+{
+	int offset = 0;
+	int ret = 0, err;
+	int depth = 0;
+
+	do {
+		offset = fdt_next_node(blob, offset, &depth);
+		if (offset > 0 && depth == 1) {
+			err = lists_bind_fdt(gd->dm_root, blob, offset);
+			if (err && !ret)
+				ret = err;
+		}
+	} while (offset > 0);
+
+	if (ret)
+		dm_warn("Some drivers failed to bind\n");
+
+	return ret;
+}
+#endif
+
+/* This is the root driver - all drivers are children of this */
+U_BOOT_DRIVER(root_driver) = {
+	.name	= "root_driver",
+	.id	= UCLASS_ROOT,
+};
+
+/* This is the root uclass */
+UCLASS_DRIVER(root) = {
+	.name	= "root",
+	.id	= UCLASS_ROOT,
+};
diff --git a/common/dm/uclass.c b/common/dm/uclass.c
new file mode 100644
index 0000000..6191dfd
--- /dev/null
+++ b/common/dm/uclass.c
@@ -0,0 +1,298 @@
+/*
+ * Copyright (c) 2013 Google, Inc
+ *
+ * (C) Copyright 2012
+ * Pavel Herrmann <morpheus.ibis@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
+ * MA 02111-1307 USA
+ */
+
+#include <common.h>
+#include <errno.h>
+#include <malloc.h>
+#include <dm/device.h>
+#include <dm/device-internal.h>
+#include <dm/lists.h>
+#include <dm/uclass.h>
+#include <dm/uclass-internal.h>
+#include <dm/util.h>
+
+DECLARE_GLOBAL_DATA_PTR;
+
+struct uclass *uclass_find(enum uclass_id key)
+{
+	struct uclass *uc;
+
+	/*
+	 * TODO(sjg at chromium.org): Optimise this, perhaps moving the found
+	 * node to the start of the list, or creating a linear array mapping
+	 * id to node.
+	 */
+	list_for_each_entry(uc, &gd->uclass_root, sibling_node) {
+		if (uc->uc_drv->id == key)
+			return uc;
+	}
+
+	return NULL;
+}
+
+/**
+ * uclass_add() - Create new uclass in list
+ * @id: Id number to create
+ * @ucp: Returns pointer to uclass, or NULL on error
+ * @return 0 on success, -ve on error
+ *
+ * The new uclass is added to the list. There must be only one uclass for
+ * each id.
+ */
+static int uclass_add(enum uclass_id id, struct uclass **ucp)
+{
+	struct uclass_driver *uc_drv;
+	struct uclass *uc;
+	int ret;
+
+	*ucp = NULL;
+	uc_drv = lists_uclass_lookup(id);
+	if (!uc_drv) {
+		dm_warn("Cannot find uclass for id %d: please add the UCLASS_DRIVER() declaration for this UCLASS_... id\n",
+			id);
+		return -ENOENT;
+	}
+	if (uc_drv->ops) {
+		dm_warn("No ops for uclass id %d\n", id);
+		return -EINVAL;
+	}
+	uc = calloc(1, sizeof(*uc));
+	if (!uc)
+		return -ENOMEM;
+	if (uc_drv->priv_auto_alloc_size) {
+		uc->priv = calloc(1, uc_drv->priv_auto_alloc_size);
+		if (!uc->priv) {
+			ret = -ENOMEM;
+			goto fail_mem;
+		}
+	}
+	uc->uc_drv = uc_drv;
+	INIT_LIST_HEAD(&uc->sibling_node);
+	INIT_LIST_HEAD(&uc->dev_head);
+	list_add(&uc->sibling_node, &gd->uclass_root);
+
+	if (uc_drv->init) {
+		ret = uc_drv->init(uc);
+		if (ret)
+			goto fail;
+	}
+
+	*ucp = uc;
+
+	return 0;
+fail:
+	if (uc_drv->priv_auto_alloc_size) {
+		free(uc->priv);
+		uc->priv = NULL;
+	}
+	list_del(&uc->sibling_node);
+fail_mem:
+	free(uc);
+
+	return ret;
+}
+
+int uclass_destroy(struct uclass *uc)
+{
+	struct uclass_driver *uc_drv;
+	struct device *dev, *tmp;
+	int ret;
+
+	list_for_each_entry_safe(dev, tmp, &uc->dev_head, uclass_node) {
+		ret = device_remove(dev);
+		if (ret)
+			return ret;
+		ret = device_unbind(dev);
+		if (ret)
+			return ret;
+	}
+
+	uc_drv = uc->uc_drv;
+	if (uc_drv->destroy)
+		uc_drv->destroy(uc);
+	list_del(&uc->sibling_node);
+	if (uc_drv->priv_auto_alloc_size)
+		free(uc->priv);
+	free(uc);
+
+	return 0;
+}
+
+int uclass_get(enum uclass_id id, struct uclass **ucp)
+{
+	struct uclass *uc;
+
+	*ucp = NULL;
+	uc = uclass_find(id);
+	if (!uc)
+		return uclass_add(id, ucp);
+	*ucp = uc;
+
+	return 0;
+}
+
+int uclass_find_device(enum uclass_id id, int index, struct device **devp)
+{
+	struct uclass *uc;
+	struct device *dev;
+	int ret;
+
+	*devp = NULL;
+	ret = uclass_get(id, &uc);
+	if (ret)
+		return ret;
+
+	list_for_each_entry(dev, &uc->dev_head, uclass_node) {
+		if (!index--) {
+			*devp = dev;
+			return 0;
+		}
+	}
+
+	return -ENODEV;
+}
+
+int uclass_get_device(enum uclass_id id, int index, struct device **devp)
+{
+	struct device *dev;
+	int ret;
+
+	*devp = NULL;
+	ret = uclass_find_device(id, index, &dev);
+	if (ret)
+		return ret;
+
+	ret = device_probe(dev);
+	if (ret)
+		return ret;
+
+	*devp = dev;
+
+	return 0;
+}
+
+int uclass_first_device(enum uclass_id id, struct device **devp)
+{
+	struct uclass *uc;
+	struct device *dev;
+	int ret;
+
+	*devp = NULL;
+	ret = uclass_get(id, &uc);
+	if (ret)
+		return ret;
+	if (list_empty(&uc->dev_head))
+		return 0;
+
+	dev = list_first_entry(&uc->dev_head, struct device, uclass_node);
+	ret = device_probe(dev);
+	if (ret)
+		return ret;
+	*devp = dev;
+
+	return 0;
+}
+
+int uclass_next_device(struct device **devp)
+{
+	struct device *dev = *devp;
+	int ret;
+
+	*devp = NULL;
+	if (list_is_last(&dev->uclass_node, &dev->uclass->dev_head))
+		return 0;
+
+	dev = list_entry(dev->uclass_node.next, struct device, uclass_node);
+	ret = device_probe(dev);
+	if (ret)
+		return ret;
+	*devp = dev;
+
+	return 0;
+}
+
+int uclass_bind_device(struct device *dev)
+{
+	struct uclass *uc;
+	int ret;
+
+	uc = dev->uclass;
+
+	list_add_tail(&dev->uclass_node, &uc->dev_head);
+
+	if (uc->uc_drv->post_bind) {
+		ret = uc->uc_drv->post_bind(dev);
+		if (ret) {
+			list_del(&dev->uclass_node);
+			return ret;
+		}
+	}
+
+	return 0;
+}
+
+int uclass_unbind_device(struct device *dev)
+{
+	struct uclass *uc;
+	int ret;
+
+	uc = dev->uclass;
+	if (uc->uc_drv->pre_unbind) {
+		ret = uc->uc_drv->pre_unbind(dev);
+		if (ret)
+			return ret;
+	}
+
+	list_del(&dev->uclass_node);
+	return 0;
+}
+
+int uclass_post_probe_device(struct device *dev)
+{
+	struct uclass_driver *uc_drv = dev->uclass->uc_drv;
+
+	if (uc_drv->post_probe)
+		return uc_drv->post_probe(dev);
+
+	return 0;
+}
+
+int uclass_pre_remove_device(struct device *dev)
+{
+	struct uclass_driver *uc_drv;
+	struct uclass *uc;
+	int ret;
+
+	uc = dev->uclass;
+	uc_drv = uc->uc_drv;
+	if (uc->uc_drv->pre_remove) {
+		ret = uc->uc_drv->pre_remove(dev);
+		if (ret)
+			return ret;
+	}
+	if (uc_drv->per_device_auto_alloc_size) {
+		free(dev->uclass_priv);
+		dev->uclass_priv = NULL;
+	}
+
+	return 0;
+}
diff --git a/common/dm/util.c b/common/dm/util.c
new file mode 100644
index 0000000..06672cd
--- /dev/null
+++ b/common/dm/util.c
@@ -0,0 +1,50 @@
+/*
+ * Copyright (c) 2013 Google, Inc
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
+ * MA 02111-1307 USA
+ */
+
+#include <common.h>
+#include <vsprintf.h>
+
+void dm_warn(const char *fmt, ...)
+{
+	va_list args;
+
+	va_start(args, fmt);
+	vprintf(fmt, args);
+	va_end(args);
+}
+
+void dm_dbg(const char *fmt, ...)
+{
+	va_list args;
+
+	va_start(args, fmt);
+	vprintf(fmt, args);
+	va_end(args);
+}
+
+int list_count_items(struct list_head *head)
+{
+	struct list_head *node;
+	int count = 0;
+
+	list_for_each(node, head)
+		count++;
+
+	return count;
+}
diff --git a/include/asm-generic/global_data.h b/include/asm-generic/global_data.h
index 3e9ca11..e0ec7a3 100644
--- a/include/asm-generic/global_data.h
+++ b/include/asm-generic/global_data.h
@@ -37,6 +37,8 @@
  */
 
 #ifndef __ASSEMBLY__
+#include <linux/list.h>
+
 typedef struct global_data {
 	bd_t *bd;
 	unsigned long flags;
@@ -77,6 +79,12 @@ typedef struct global_data {
 	unsigned long start_addr_sp;	/* start_addr_stackpointer */
 	unsigned long reloc_off;
 	struct global_data *new_gd;	/* relocated global data */
+
+#ifdef CONFIG_DM
+	struct device	*dm_root;	/* Root instance for Driver Model */
+	struct list_head uclass_root;	/* Head of core tree */
+#endif
+
 	const void *fdt_blob;	/* Our device tree, NULL if none */
 	void *new_fdt;		/* Relocated FDT */
 	unsigned long fdt_size;	/* Space reserved for relocated FDT */
diff --git a/include/dm.h b/include/dm.h
new file mode 100644
index 0000000..d879c49
--- /dev/null
+++ b/include/dm.h
@@ -0,0 +1,27 @@
+/*
+ * Copyright (c) 2013 Google, Inc
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
+ * MA 02111-1307 USA
+ */
+
+#ifndef _DM_H_
+#define _DM_H
+
+#include <dm/device.h>
+#include <dm/platform_data.h>
+#include <dm/uclass.h>
+
+#endif
diff --git a/include/dm/device-internal.h b/include/dm/device-internal.h
new file mode 100644
index 0000000..8562902
--- /dev/null
+++ b/include/dm/device-internal.h
@@ -0,0 +1,100 @@
+/*
+ * Copyright (C) 2013 Google, Inc
+ *
+ * (C) Copyright 2012
+ * Pavel Herrmann <morpheus.ibis@gmail.com>
+ * Marek Vasut <marex@denx.de>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
+ * MA 02111-1307 USA
+ */
+
+#ifndef _DM_DEVICE_INTERNAL_H
+#define _DM_DEVICE_INTERNAL_H
+
+struct device;
+
+/**
+ * device_bind() - Create a device and bind it to a driver
+ *
+ * Called to set up a new device attached to a driver. The device will either
+ * have platform_data, or a device tree node which can be used to create the
+ * platform_data.
+ *
+ * Once bound a device exists but is not yet active until device_probe() is
+ * called.
+ *
+ * @parent: Pointer to device's parent, under which this driver will exist
+ * @drv: Device's driver
+ * @name: Name of device (e.g. device tree node name)
+ * @platform_data: Pointer to data for this device - the structure is device-
+ * specific but may include the device's I/O address, etc.. This is NULL for
+ * devices which use device tree.
+ * @of_offset: Offset of device tree node for this device. This is -1 for
+ * devices which don't use device tree.
+ * @devp: Returns a pointer to the bound device
+ * @return 0 if OK, -ve on error
+ */
+int device_bind(struct device *parent, struct driver *drv,
+		const char *name, void *platform_data, int of_offset,
+		struct device **devp);
+
+/**
+ * device_bind_by_name: Create a device and bind it to a driver
+ *
+ * This is a helper function used to bind devices which do not use device
+ * tree.
+ *
+ * @parent: Pointer to device's parent
+ * @info: Name and platform_data for this device
+ * @devp: Returns a pointer to the bound device
+ * @return 0 if OK, -ve on error
+ */
+int device_bind_by_name(struct device *parent, const struct driver_info *info,
+			struct device **devp);
+
+/**
+ * device_probe() - Probe a device, activating it
+ *
+ * Activate a device so that it is ready for use. All its parents are probed
+ * first.
+ *
+ * @dev: Pointer to device to probe
+ * @return 0 if OK, -ve on error
+ */
+int device_probe(struct device *dev);
+
+/**
+ * device_remove() - Remove a device, de-activating it
+ *
+ * De-activate a device so that it is no longer ready for use. All its
+ * children are deactivated first.
+ *
+ * @dev: Pointer to device to remove
+ * @return 0 if OK, -ve on error (an error here is normally a very bad thing)
+ */
+int device_remove(struct device *dev);
+
+/**
+ * device_unbind() - Unbind a device, destroying it
+ *
+ * Unbind a device and remove all memory used by it
+ *
+ * @dev: Pointer to device to unbind
+ * @return 0 if OK, -ve on error
+ */
+int device_unbind(struct device *dev);
+
+#endif
diff --git a/include/dm/device.h b/include/dm/device.h
new file mode 100644
index 0000000..cb0ce5d
--- /dev/null
+++ b/include/dm/device.h
@@ -0,0 +1,149 @@
+/*
+ * Copyright (c) 2013 Google, Inc
+ *
+ * (C) Copyright 2012
+ * Pavel Herrmann <morpheus.ibis@gmail.com>
+ * Marek Vasut <marex@denx.de>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
+ * MA 02111-1307 USA
+ */
+
+#ifndef _DM_DEVICE_H
+#define _DM_DEVICE_H
+
+#include <dm/uclass-id.h>
+#include <linker_lists.h>
+#include <linux/list.h>
+
+struct driver_info;
+
+/* Driver is active (probed). Cleared when it is removed */
+#define DM_FLAG_ACTIVATED	(1 << 0)
+
+/* DM is responsible for allocating and freeing platform_data */
+#define DM_FLAG_ALLOC_PDATA	(2 << 0)
+
+/**
+ * struct device - An instance of a driver
+ *
+ * This holds information about a device, which is a driver bound to a
+ * particular port or peripheral (essentially a driver instance).
+ *
+ * A device will come into existence through a 'bind' call, either due to
+ * a U_BOOT_DEVICE() macro (in which case platform_data is non-NULL) or a node
+ * in the device tree (in which case of_offset is >= 0). In the latter case
+ * we translate the device tree information into platform_data in a function
+ * implemented by the driver and normally called ..._parse_dt().
+ *
+ * All three of platform_data, priv and uclass_priv can be allocated by the
+ * driver, or you can use the auto_alloc_size members of struct driver and
+ * struct uclass_driver to have driver model do this automatically.
+ *
+ * @driver: The driver used by this device
+ * @name: Name of device, typically the FDT node name
+ * @platform_data: Configuration data for this device
+ * @of_offset: Device tree node offset for this device (- for none)
+ * @parent: Parent of this device, or NULL for the top level device
+ * @priv: Private data for this device
+ * @uclass: Pointer to uclass for this device
+ * @uclass_priv: The uclass's private data for this device
+ * @uclass_node: Used by uclass to link its devices
+ * @child_head: List of children of this device
+ * @sibling_node: Next device in list of all devices
+ * @flags: Flags for this device DM_FLAG_...
+ */
+struct device {
+	struct driver *driver;
+	const char *name;
+	void *platform_data;
+	int of_offset;
+	struct device *parent;
+	void *priv;
+	struct uclass *uclass;
+	void *uclass_priv;
+	struct list_head uclass_node;
+	struct list_head child_head;
+	struct list_head sibling_node;
+	uint32_t flags;
+};
+
+/* Returns the operations for a device */
+#define device_get_ops(dev)	(dev->driver->ops)
+
+/* Returns non-zero if the device is active (probed and not removed) */
+#define device_active(dev)	((dev)->flags & DM_FLAG_ACTIVATED)
+
+/**
+ * struct device_id - Lists the compatible strings supported by a driver
+ * @compatible: Compatible string
+ * @data: Data for this compatible string
+ */
+struct device_id {
+	const char *compatible;
+	ulong data;
+};
+
+/**
+ * struct driver - A driver for a feature or peripheral
+ *
+ * This holds methods for setting up a new device, and also removing it.
+ * The device needs information to set itself up - this is provided either
+ * by platform_data or a device tree node (which we find by looking up
+ * matching compatible strings with of_match).
+ *
+ * Drivers all belong to a uclass, representing a class of devices of the
+ * same type. Common elements of the drivers can be implemented in the uclass,
+ * or the uclass can provide a consistent interface to the drivers within
+ * it.
+ *
+ * @name: Device name
+ * @id: Identiies the uclass we belong to
+ * @of_match: List of compatible strings to match, and any identifying data
+ * for each.
+ * @bind: Called to bind a device to its driver
+ * @probe: Called to probe a device, i.e. activate it
+ * @remove: Called to remove a device, i.e. de-activate it
+ * @unbind: Called to unbind a device from its driver
+ * @priv_auto_alloc_size: If non-zero this is the size of the private data
+ * to be allocated in the device's ->priv pointer. If zero, then the driver
+ * is responsible for allocating any data required.
+ * @platform_data_auto_alloc_size: If non-zero this is the size of the
+ * platform data to be allocated in the device's ->platform_data pointer.
+ * This is typically only useful for device-tree-aware drivers (those with
+ * an of_match), since drivers which use platform_data will have the data
+ * provided in the U_BOOT_DEVICE() instantiation.
+ * ops: Driver-specific operations. This is typically a list of function
+ * pointers defined by the driver, to implement driver functions required by
+ * the uclass.
+ */
+struct driver {
+	char *name;
+	enum uclass_id id;
+	const struct device_id *of_match;
+	int (*bind)(struct device *dev);
+	int (*probe)(struct device *dev);
+	int (*remove)(struct device *dev);
+	int (*unbind)(struct device *dev);
+	int priv_auto_alloc_size;
+	int platform_data_auto_alloc_size;
+	const void *ops;	/* driver-specific operations */
+};
+
+/* Declare a new U-Boot driver */
+#define U_BOOT_DRIVER(__name)						\
+	ll_entry_declare(struct driver, __name, driver)
+
+#endif
diff --git a/include/dm/lists.h b/include/dm/lists.h
new file mode 100644
index 0000000..89056e1
--- /dev/null
+++ b/include/dm/lists.h
@@ -0,0 +1,52 @@
+/*
+ * Copyright (c) 2013 Google, Inc
+ *
+ * (C) Copyright 2012
+ * Pavel Herrmann <morpheus.ibis@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
+ * MA 02111-1307 USA
+ */
+
+#ifndef _DM_LISTS_H_
+#define _DM_LISTS_H_
+
+#include <dm/uclass-id.h>
+
+/**
+ * lists_driver_lookup_name() - Return u_boot_driver corresponding to name
+ *
+ * This function returns a pointer to a driver given its name. This is used
+ * for binding a driver given its name and platform_data.
+ *
+ * @name: Name of driver to look up
+ * @return pointer to driver, or NULL if not found
+ */
+struct driver *lists_driver_lookup_name(const char *name);
+
+/**
+ * lists_uclass_lookup() - Return uclass_driver based on ID of the class
+ * id:		ID of the class
+ *
+ * This function returns the pointer to uclass_driver, which is the class's
+ * base structure based on the ID of the class. Returns NULL on error.
+ */
+struct uclass_driver *lists_uclass_lookup(enum uclass_id id);
+
+int lists_bind_drivers(struct device *parent);
+
+int lists_bind_fdt(struct device *parent, const void *blob, int offset);
+
+#endif
diff --git a/include/dm/platform_data.h b/include/dm/platform_data.h
new file mode 100644
index 0000000..aa1f3fc
--- /dev/null
+++ b/include/dm/platform_data.h
@@ -0,0 +1,35 @@
+/*
+ * Copyright (c) 2013 Google, Inc
+ *
+ * (C) Copyright 2012
+ * Pavel Herrmann <morpheus.ibis@gmail.com>
+ * Marek Vasut <marex@denx.de>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
+ * MA 02111-1307 USA
+ */
+
+#ifndef _DM_PLATFORM_DATA_H
+#define _DM_PLATFORM_DATA_H
+
+struct driver_info {
+	const char	*name;
+	const void	*platform_data;
+};
+
+#define U_BOOT_DEVICE(__name)						\
+	ll_entry_declare(struct driver_info, __name, driver_info)
+
+#endif
diff --git a/include/dm/root.h b/include/dm/root.h
new file mode 100644
index 0000000..ff84b3c
--- /dev/null
+++ b/include/dm/root.h
@@ -0,0 +1,66 @@
+/*
+ * Copyright (c) 2013 Google, Inc
+ *
+ * (C) Copyright 2012
+ * Pavel Herrmann <morpheus.ibis@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
+ * MA 02111-1307 USA
+ */
+
+#ifndef _DM_ROOT_H_
+#define _DM_ROOT_H_
+
+struct device;
+
+/**
+ * dm_root() - Return pointer to the top of the driver tree
+ *
+ * This function returns pointer to the root node of the driver tree,
+ *
+ * @return pointer to root device, or NULL if not inited yet
+ */
+struct device *dm_root(void);
+
+/**
+ * dm_scan_platform_data() - Scan all platform data and bind drivers
+ *
+ * This scans all available platform_data and creates drivers for each
+ *
+ * @return 0 if OK, -ve on error
+ */
+int dm_scan_platform_data(void);
+
+/**
+ * dm_scan_fdt() - Scan the device tree and bind drivers
+ *
+ * This scans the device tree and creates a driver for each node
+ *
+ * @blob: Pointer to device tree blob
+ * @return 0 if OK, -ve on error
+ */
+int dm_scan_fdt(const void *blob);
+
+/**
+ * dm_init() - Initialize Driver Model structures
+ *
+ * This function will initialize roots of driver tree and class tree.
+ * This needs to be called before anything uses the DM
+ *
+ * @return 0 if OK, -ve on error
+ */
+int dm_init(void);
+
+#endif
diff --git a/include/dm/uclass-id.h b/include/dm/uclass-id.h
new file mode 100644
index 0000000..2273dea
--- /dev/null
+++ b/include/dm/uclass-id.h
@@ -0,0 +1,41 @@
+/*
+ * Copyright (c) 2013 Google, Inc
+ *
+ * (C) Copyright 2012
+ * Pavel Herrmann <morpheus.ibis@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
+ * MA 02111-1307 USA
+ */
+
+#ifndef _DM_UCLASS_ID_H
+#define _DM_UCLASS_ID_H
+
+/* TODO(sjg at chromium.org): this could be compile-time generated */
+enum uclass_id {
+	/* These are used internally by driver model */
+	UCLASS_ROOT = 0,
+	UCLASS_DEMO,
+	UCLASS_TEST,
+	UCLASS_TEST_FDT,
+
+	/* U-Boot uclasses start here */
+	UCLASS_GPIO,
+
+	UCLASS_COUNT,
+	UCLASS_INVALID = -1,
+};
+
+#endif
diff --git a/include/dm/uclass-internal.h b/include/dm/uclass-internal.h
new file mode 100644
index 0000000..05cdf56
--- /dev/null
+++ b/include/dm/uclass-internal.h
@@ -0,0 +1,98 @@
+/*
+ * Copyright (c) 2013 Google, Inc
+ *
+ * (C) Copyright 2012
+ * Pavel Herrmann <morpheus.ibis@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
+ * MA 02111-1307 USA
+ */
+
+#ifndef _DM_UCLASS_INTERNAL_H
+#define _DM_UCLASS_INTERNAL_H
+
+/**
+ * uclass_find_device() - Return n-th child of uclass
+ * @id:		Id number of the uclass
+ * @index:	Position of the child in uclass's list
+ * #devp:	Returns pointer to device, or NULL on error
+ *
+ * The device is not prepared for use - this is an internal function
+ *
+ * @return the uclass pointer of a child at the given index or
+ * return NULL on error.
+ */
+int uclass_find_device(enum uclass_id id, int index, struct device **devp);
+
+/**
+ * uclass_bind_device() - Associate device with a uclass
+ *
+ * Connect the device into uclass's list of devices.
+ *
+ * @dev:	Pointer to the device
+ * #return 0 on success, -ve on error
+ */
+int uclass_bind_device(struct device *dev);
+
+/**
+ * uclass_unbind_device() - Deassociate device with a uclass
+ *
+ * Disconnect the device from uclass's list of devices.
+ *
+ * @dev:	Pointer to the device
+ * #return 0 on success, -ve on error
+ */
+int uclass_unbind_device(struct device *dev);
+
+/**
+ * uclass_post_probe_device() - Deal with a device that has just been probed
+ *
+ * Perform any post-processing of a probed device that is needed by the
+ * uclass.
+ *
+ * @dev:	Pointer to the device
+ * #return 0 on success, -ve on error
+ */
+int uclass_post_probe_device(struct device *dev);
+
+/**
+ * uclass_pre_remove_device() - Handle a device which is about to be removed
+ *
+ * Perform any pre-processing of a device that is about to be removed.
+ *
+ * @dev:	Pointer to the device
+ * #return 0 on success, -ve on error
+ */
+int uclass_pre_remove_device(struct device *dev);
+
+/**
+ * uclass_find() - Find uclass by its id
+ *
+ * @id:		Id to serach for
+ * @return pointer to uclass, or NULL if not found
+ */
+struct uclass *uclass_find(enum uclass_id key);
+
+/**
+ * uclass_destroy() - Destroy a uclass
+ *
+ * Destroy a uclass and all its devices
+ *
+ * @uc: uclass to destroy
+ * @return 0 on success, -ve on error
+ */
+int uclass_destroy(struct uclass *uc);
+
+#endif
diff --git a/include/dm/uclass.h b/include/dm/uclass.h
new file mode 100644
index 0000000..85e3d28
--- /dev/null
+++ b/include/dm/uclass.h
@@ -0,0 +1,155 @@
+/*
+ * Copyright (c) 2013 Google, Inc
+ *
+ * (C) Copyright 2012
+ * Pavel Herrmann <morpheus.ibis@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
+ * MA 02111-1307 USA
+ */
+
+#ifndef _DM_UCLASS_H
+#define _DM_UCLASS_H
+
+#include <dm/uclass-id.h>
+#include <linux/list.h>
+
+/**
+ * struct uclass - a U-Boot drive class, collecting together similar drivers
+ *
+ * A uclass provides an interface to a particular function, which is
+ * implemented by one or more drivers. Every driver belongs to a uclass even
+ * if it is the only driver in that uclass. An example uclass is GPIO, which
+ * provides the ability to change read inputs, set and clear outputs, etc.
+ * There may be drivers for on-chip SoC GPIO banks, I2C GPIO expanders and
+ * PMIC IO lines, all made available in a unified way through the uclass.
+ *
+ * @priv: Private data for this uclass
+ * @uc_drv: The driver for the uclass itself, not to be confused with a
+ * 'struct driver'
+ * dev_head: List of devices in this uclass (devices are attached to their
+ * uclass when their bind method is called)
+ * @sibling_node: Next uclass in the linked list of uclasses
+ */
+struct uclass {
+	void *priv;
+	struct uclass_driver *uc_drv;
+	struct list_head dev_head;
+	struct list_head sibling_node;
+};
+
+struct device;
+
+/**
+ * struct uclass_driver - Driver for the uclass
+ *
+ * A uclass_driver provides a consistent interface to a set of related
+ * drivers.
+ *
+ * @name: Name of uclass driver
+ * @id: ID number of this uclass
+ * @post_bind: Called after a new device is bound to this uclass
+ * @pre_unbind: Called before a device is unbound from this uclass
+ * @post_probe: Called after a new device is probed
+ * @pre_remove: Called before a device is removed
+ * @init: Called to set up the uclass
+ * @destroy: Called to destroy the uclass
+ * @priv_auto_alloc_size: If non-zero this is the size of the private data
+ * to be allocated in the uclass's ->priv pointer. If zero, then the uclass
+ * driver is responsible for allocating any data required.
+ * @per_device_auto_alloc_size: Each device can hold private data owned
+ * by the uclass. If required this will be automatically allocated if this
+ * value is non-zero.
+ * @ops: Uclass operations, providing the consistent interface to devices
+ * within the uclass.
+ */
+struct uclass_driver {
+	const char *name;
+	enum uclass_id id;
+	int (*post_bind)(struct device *dev);
+	int (*pre_unbind)(struct device *dev);
+	int (*post_probe)(struct device *dev);
+	int (*pre_remove)(struct device *dev);
+	int (*init)(struct uclass *class);
+	int (*destroy)(struct uclass *class);
+	int priv_auto_alloc_size;
+	int per_device_auto_alloc_size;
+	const void *ops;
+};
+
+/* Declare a new uclass_driver */
+#define UCLASS_DRIVER(__name)						\
+	ll_entry_declare(struct uclass_driver, __name, uclass)
+
+/**
+ * uclass_get() - Get a uclass based on an ID, creating it if needed
+ *
+ * Every uclass is identified by an ID, a number from 0 to n-1 where n is
+ * the number of uclasses. This function allows looking up a uclass by its
+ * ID.
+ *
+ * @key: ID to look up
+ * @ucp: Returns pointer to uclass (there is only one per ID)
+ * @return 0 if OK, -ve on error
+ */
+int uclass_get(enum uclass_id key, struct uclass **ucp);
+
+/**
+ * uclass_get_device() - Get a uclass device based on an ID and index
+ *
+ * id: ID to look up
+ * @index: Device number within that uclass (0=first)
+ * @ucp: Returns pointer to uclass (there is only one per for each ID)
+ * @return 0 if OK, -ve on error
+ */
+int uclass_get_device(enum uclass_id id, int index, struct device **ucp);
+
+/**
+ * uclass_first_device() - Get the first device in a uclass
+ *
+ * @id: Uclass ID to look up
+ * @devp: Returns pointer to the first device in that uclass, or NULL if none
+ * @return 0 if OK (found or not found), -1 on error
+ */
+int uclass_first_device(enum uclass_id id, struct device **devp);
+
+/**
+ * uclass_next_device() - Get the next device in a uclass
+ *
+ * @devp: On entry, pointer to device to lookup. On exit, returns pointer
+ * to the next device in the same uclass, or NULL if none
+ * @return 0 if OK (found or not found), -1 on error
+ */
+int uclass_next_device(struct device **devp);
+
+/**
+ * uclass_foreach_dev() - Helper function to iteration through devices
+ *
+ * This creates a for() loop which works through the available devices in
+ * a uclass in order from start to end.
+ *
+ * @pos: struct device * to hold the current device. Set to NULL when there
+ * are no more devices.
+ * uc: uclass to scan
+ */
+#define uclass_foreach_dev(pos, uc)					\
+	for (pos = list_entry((&(uc)->dev_head)->next, typeof(*pos),	\
+			uclass_node);					\
+	     prefetch(pos->uclass_node.next),				\
+			&pos->uclass_node != (&(uc)->dev_head);		\
+	     pos = list_entry(pos->uclass_node.next, typeof(*pos),	\
+			uclass_node))
+
+#endif
diff --git a/include/dm/util.h b/include/dm/util.h
new file mode 100644
index 0000000..84809cf
--- /dev/null
+++ b/include/dm/util.h
@@ -0,0 +1,42 @@
+/*
+ * Copyright (c) 2013 Google, Inc
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
+ * MA 02111-1307 USA
+ */
+
+#ifndef __DM_UTIL_H
+
+void dm_warn(const char *fmt, ...);
+
+#ifdef DEBUG
+void dm_dbg(const char *fmt, ...);
+#else
+static inline void dm_dbg(const char *fmt, ...)
+{
+}
+#endif
+
+struct list_head;
+
+/**
+ * list_count_items() - Count number of items in a list
+ *
+ * @param head:		Head of list
+ * @return number of items, or 0 if empty
+ */
+int list_count_items(struct list_head *head);
+
+#endif
-- 
1.8.3

^ permalink raw reply related	[flat|nested] 30+ messages in thread

* [U-Boot] [PATCH v3 08/16] dm: Set up driver model after relocation
  2013-06-19  3:52 [U-Boot] [PATCH v3 0/16] Driver model implementation, tests, demo and GPIO Simon Glass
                   ` (6 preceding siblings ...)
  2013-06-19  3:52 ` [U-Boot] [PATCH v3 07/16] dm: Add base driver model support Simon Glass
@ 2013-06-19  3:52 ` Simon Glass
  2013-06-28 20:53   ` Marek Vasut
  2013-06-19  3:52 ` [U-Boot] [PATCH v3 09/16] dm: Add basic tests Simon Glass
                   ` (7 subsequent siblings)
  15 siblings, 1 reply; 30+ messages in thread
From: Simon Glass @ 2013-06-19  3:52 UTC (permalink / raw)
  To: u-boot

Make driver model available after relocation, by setting up data structures
and scanning for devices using compiled-in platform_data and (when available)
the device tree.

Signed-off-by: Simon Glass <sjg@chromium.org>
---
Changes in v3: None
Changes in v2: None

 common/board_r.c | 33 +++++++++++++++++++++++++++++++++
 1 file changed, 33 insertions(+)

diff --git a/common/board_r.c b/common/board_r.c
index f5649c9..5fe86ef 100644
--- a/common/board_r.c
+++ b/common/board_r.c
@@ -34,6 +34,7 @@
 #ifdef CONFIG_HAS_DATAFLASH
 #include <dataflash.h>
 #endif
+#include <dm.h>
 #include <environment.h>
 #include <fdtdec.h>
 #if defined(CONFIG_CMD_IDE)
@@ -66,7 +67,9 @@
 #ifdef CONFIG_X86
 #include <asm/init_helpers.h>
 #endif
+#include <dm/root.h>
 #include <linux/compiler.h>
+#include <linux/err.h>
 
 DECLARE_GLOBAL_DATA_PTR;
 
@@ -269,6 +272,33 @@ static int initr_malloc(void)
 	return 0;
 }
 
+#ifdef CONFIG_DM
+static int initr_dm(void)
+{
+	int ret;
+
+	ret = dm_init();
+	if (ret) {
+		debug("dm_init() failed: %d\n", ret);
+		return ret;
+	}
+	ret = dm_scan_platform_data();
+	if (ret) {
+		debug("dm_scan_platform_data() failed: %d\n", ret);
+		return ret;
+	}
+#ifdef CONFIG_OF_CONTROL
+	ret = dm_scan_fdt(gd->fdt_blob);
+	if (ret) {
+		debug("dm_scan_fdt() failed: %d\n", ret);
+		return ret;
+	}
+#endif
+
+	return 0;
+}
+#endif
+
 __weak int power_init_board(void)
 {
 	return 0;
@@ -766,6 +796,9 @@ init_fnc_t init_sequence_r[] = {
 	initr_barrier,
 	initr_malloc,
 	bootstage_relocate,
+#ifdef CONFIG_DM
+	initr_dm,
+#endif
 #ifdef CONFIG_ARCH_EARLY_INIT_R
 	arch_early_init_r,
 #endif
-- 
1.8.3

^ permalink raw reply related	[flat|nested] 30+ messages in thread

* [U-Boot] [PATCH v3 09/16] dm: Add basic tests
  2013-06-19  3:52 [U-Boot] [PATCH v3 0/16] Driver model implementation, tests, demo and GPIO Simon Glass
                   ` (7 preceding siblings ...)
  2013-06-19  3:52 ` [U-Boot] [PATCH v3 08/16] dm: Set up driver model after relocation Simon Glass
@ 2013-06-19  3:52 ` Simon Glass
  2013-06-19  3:52 ` [U-Boot] [PATCH v3 10/16] dm: Add a 'dm' command for testing Simon Glass
                   ` (6 subsequent siblings)
  15 siblings, 0 replies; 30+ messages in thread
From: Simon Glass @ 2013-06-19  3:52 UTC (permalink / raw)
  To: u-boot

Add some tests of driver model functionality. Coverage includes:

- basic init
- binding of drivers to devices using platform_data
- automatic probing of devices when referenced
- availability of platform data to devices
- lifecycle from bind to probe to remove to unbind
- renumbering within a uclass when devices are probed/removed
- calling driver-defined operations
- deactivation of drivers when removed
- memory leak across creation and destruction of drivers/uclasses
- uclass init/destroy methods
- automatic probe/remove of children/parents when needed

This function is enabled for sandbox, using CONFIG_DM_TEST.

Signed-off-by: Simon Glass <sjg@chromium.org>
---
Changes in v3:
- Add function/struct comments to tests

Changes in v2: None

 Makefile                  |   1 +
 include/configs/sandbox.h |   1 +
 include/dm/test.h         | 180 +++++++++++++++
 include/dm/ut.h           | 108 +++++++++
 test/dm/Makefile          |  52 +++++
 test/dm/core.c            | 557 ++++++++++++++++++++++++++++++++++++++++++++++
 test/dm/test-driver.c     | 159 +++++++++++++
 test/dm/test-fdt.c        | 148 ++++++++++++
 test/dm/test-main.c       | 120 ++++++++++
 test/dm/test-uclass.c     | 117 ++++++++++
 test/dm/test.dts          |  59 +++++
 test/dm/ut.c              |  46 ++++
 12 files changed, 1548 insertions(+)
 create mode 100644 include/dm/test.h
 create mode 100644 include/dm/ut.h
 create mode 100644 test/dm/Makefile
 create mode 100644 test/dm/core.c
 create mode 100644 test/dm/test-driver.c
 create mode 100644 test/dm/test-fdt.c
 create mode 100644 test/dm/test-main.c
 create mode 100644 test/dm/test-uclass.c
 create mode 100644 test/dm/test.dts
 create mode 100644 test/dm/ut.c

diff --git a/Makefile b/Makefile
index c1c13c8..0655c9c 100644
--- a/Makefile
+++ b/Makefile
@@ -336,6 +336,7 @@ LIBS-y += lib/libfdt/libfdt.o
 LIBS-y += api/libapi.o
 LIBS-y += post/libpost.o
 LIBS-y += test/libtest.o
+LIBS-y += test/dm/libtestdm.o
 
 LIBS-$(CONFIG_DM) += common/dm/libdm.o
 
diff --git a/include/configs/sandbox.h b/include/configs/sandbox.h
index 162c26c..38e0384 100644
--- a/include/configs/sandbox.h
+++ b/include/configs/sandbox.h
@@ -23,6 +23,7 @@
 #define __CONFIG_H
 
 #define CONFIG_DM
+#define CONFIG_DM_TEST
 
 /* Number of bits in a C 'long' on this architecture */
 #define CONFIG_SANDBOX_BITS_PER_LONG	64
diff --git a/include/dm/test.h b/include/dm/test.h
new file mode 100644
index 0000000..fa514d8
--- /dev/null
+++ b/include/dm/test.h
@@ -0,0 +1,180 @@
+/*
+ * Copyright (c) 2013 Google, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
+ * MA 02111-1307 USA
+ */
+
+#ifndef __DM_TEST_H
+#define __DM_TEST_H
+
+#include <dm.h>
+
+/**
+ * struct dm_test_cdata - configuration data for test instance
+ *
+ * @ping_add: Amonut to add each time we get a ping
+ * @base: Base address of this device
+ */
+struct dm_test_pdata {
+	int ping_add;
+	uint32_t base;
+};
+
+/**
+ * struct test_ops - Operations supported by the test device
+ *
+ * @ping: Ping operation
+ *	@dev: Device to operate on
+ *	@pingval: Value to ping the device with
+ *	@pingret: Returns resulting value from driver
+ *	@return 0 if OK, -ve on error
+ */
+struct test_ops {
+	int (*ping)(struct device *dev, int pingval, int *pingret);
+};
+
+/* Operations that our test driver supports */
+enum {
+	DM_TEST_OP_BIND = 0,
+	DM_TEST_OP_UNBIND,
+	DM_TEST_OP_PROBE,
+	DM_TEST_OP_REMOVE,
+
+	/* For uclass */
+	DM_TEST_OP_POST_BIND,
+	DM_TEST_OP_PRE_UNBIND,
+	DM_TEST_OP_POST_PROBE,
+	DM_TEST_OP_PRE_REMOVE,
+	DM_TEST_OP_INIT,
+	DM_TEST_OP_DESTROY,
+
+	DM_TEST_OP_COUNT,
+};
+
+/* Test driver types */
+enum {
+	DM_TEST_TYPE_FIRST = 0,
+	DM_TEST_TYPE_SECOND,
+};
+
+/* The number added to the ping total on each probe */
+#define DM_TEST_START_TOTAL	5
+
+/**
+ * struct dm_test_priv - private data for the test devices
+ */
+struct dm_test_priv {
+	int ping_total;
+	int op_count[DM_TEST_OP_COUNT];
+};
+
+/**
+ * struct dm_test_perdev_class_priv - private per-device data for test uclass
+ */
+struct dm_test_uclass_perdev_priv {
+	int base_add;
+};
+
+/**
+ * struct dm_test_uclass_priv - private data for test uclass
+ */
+struct dm_test_uclass_priv {
+	int total_add;
+};
+
+/*
+ * Operation counts for the test driver, used to check that each method is
+ * called correctly
+ */
+extern int dm_testdrv_op_count[DM_TEST_OP_COUNT];
+
+extern struct dm_test_state global_test_state;
+
+/*
+ * struct dm_test_state - Entire state of dm test system
+ *
+ * This is often abreviated to dms.
+ *
+ * @root: Root device
+ * @testdev: Test device
+ * @fail_count: Number of tests that failed
+ * @force_fail_alloc: Force all memory allocs to fail
+ * @skip_post_probe: Skip uclass post-probe processing
+ */
+struct dm_test_state {
+	struct device *root;
+	struct device *testdev;
+	int fail_count;
+	int force_fail_alloc;
+	int skip_post_probe;
+};
+
+/* Test flags for each test */
+enum {
+	DM_TESTF_SCAN_PDATA	= 1 << 0,	/* test needs platform data */
+	DM_TESTF_PROBE_TEST	= 1 << 1,	/* probe test uclass */
+	DM_TESTF_SCAN_FDT	= 1 << 2,	/* scan device tree */
+};
+
+/**
+ * struct dm_test - Information about a driver model test
+ *
+ * @name: Name of test
+ * @func: Function to call to perform test
+ * @flags: Flags indicated pre-conditions for test
+ */
+struct dm_test {
+	const char *name;
+	int (*func)(struct dm_test_state *dms);
+	int flags;
+};
+
+/* Declare a new driver model test */
+#define DM_TEST(_name, _flags)						\
+	ll_entry_declare(struct dm_test, _name, dm_test) = {		\
+		.name = #_name,						\
+		.flags = _flags,					\
+		.func = _name,						\
+	}
+
+/* Declare ping methods for the drivers */
+int test_ping(struct device *dev, int pingval, int *pingret);
+int testfdt_ping(struct device *dev, int pingval, int *pingret);
+
+/**
+ * dm_check_operations() - Check that we can perform ping operations
+ *
+ * This checks that the ping operations work as expected for a device
+ *
+ * @dms: Overall test state
+ * @dev: Device to test
+ * @base: Base address, used to check ping return value
+ * @priv: Pointer to private test information
+ * @return 0 if OK, -ve on error
+ */
+int dm_check_operations(struct dm_test_state *dms, struct device *dev,
+			uint32_t base, struct dm_test_priv *priv);
+
+/**
+ * dm_test_main() - Run all the tests
+ *
+ * This runs all available driver model tests
+ *
+ * @return 0 if OK, -ve on error
+ */
+int dm_test_main(void);
+
+#endif
diff --git a/include/dm/ut.h b/include/dm/ut.h
new file mode 100644
index 0000000..aa849a7
--- /dev/null
+++ b/include/dm/ut.h
@@ -0,0 +1,108 @@
+/*
+ * Simple unit test library for driver model
+ *
+ * Copyright (c) 2013 Google, Inc
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
+ * MA 02111-1307 USA
+ */
+
+#ifndef __DM_UT_H
+#define __DM_UT_H
+
+struct dm_test_state;
+
+/**
+ * ut_fail() - Record failure of a unit test
+ *
+ * @dms: Test state
+ * @fname: Filename where the error occured
+ * @line: Line number where the error occured
+ * @func: Function name where the error occured
+ * @cond: The condition that failed
+ */
+void ut_fail(struct dm_test_state *dms, const char *fname, int line,
+	     const char *func, const char *cond);
+
+/**
+ * ut_failf() - Record failure of a unit test
+ *
+ * @dms: Test state
+ * @fname: Filename where the error occured
+ * @line: Line number where the error occured
+ * @func: Function name where the error occured
+ * @cond: The condition that failed
+ * @fmt: printf() format string for the error, followed by args
+ */
+void ut_failf(struct dm_test_state *dms, const char *fname, int line,
+	      const char *func, const char *cond, const char *fmt, ...)
+			__attribute__ ((format (__printf__, 6, 7)));
+
+
+/* Assert that a condition is non-zero */
+#define ut_assert(cond)							\
+	if (!(cond)) {							\
+		ut_fail(dms, __FILE__, __LINE__, __func__, #cond);	\
+		return -1;						\
+	}
+
+/* Assert that a condition is non-zero, with printf() string */
+#define ut_assertf(cond, fmt, args...)					\
+	if (!(cond)) {							\
+		ut_failf(dms, __FILE__, __LINE__, __func__, #cond,	\
+			 fmt, ##args);					\
+		return -1;						\
+	}
+
+/* Assert that two int expressions are equal */
+#define ut_asserteq(expr1, expr2) {					\
+	unsigned int val1 = (expr1), val2 = (expr2);			\
+									\
+	if (val1 != val2) {						\
+		ut_failf(dms, __FILE__, __LINE__, __func__,		\
+			 #expr1 " == " #expr2,				\
+			 "Expected %d, got %d", val1, val2);		\
+		return -1;						\
+	}								\
+}
+
+/* Assert that two string expressions are equal */
+#define ut_asserteq_str(expr1, expr2) {					\
+	const char *val1 = (expr1), *val2 = (expr2);			\
+									\
+	if (strcmp(val1, val2)) {					\
+		ut_failf(dms, __FILE__, __LINE__, __func__,		\
+			 #expr1 " = " #expr2,				\
+			 "Expected \"%s\", got \"%s\"", val1, val2);	\
+		return -1;						\
+	}								\
+}
+
+/* Assert that two pointers are equal */
+#define ut_asserteq_ptr(expr1, expr2) {					\
+	const void *val1 = (expr1), *val2 = (expr2);			\
+									\
+	if (val1 != val2) {						\
+		ut_failf(dms, __FILE__, __LINE__, __func__,		\
+			 #expr1 " = " #expr2,				\
+			 "Expected %p, got %p", val1, val2);		\
+		return -1;						\
+	}								\
+}
+
+/* Assert that an operation succeeds (returns 0) */
+#define ut_assertok(cond)	ut_asserteq(0, cond)
+
+#endif
diff --git a/test/dm/Makefile b/test/dm/Makefile
new file mode 100644
index 0000000..310aa05
--- /dev/null
+++ b/test/dm/Makefile
@@ -0,0 +1,52 @@
+#
+# Copyright (c) 2013 Google, Inc
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License as
+# published by the Free Software Foundation; either version 2 of
+# the License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.	See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston,
+# MA 02111-1307 USA
+#
+
+include $(TOPDIR)/config.mk
+
+LIB	= $(obj)libtestdm.o
+
+COBJS-$(CONFIG_DM_TEST) += test-driver.o
+COBJS-$(CONFIG_DM_TEST) += test-fdt.o
+COBJS-$(CONFIG_DM_TEST) += test-main.o
+COBJS-$(CONFIG_DM_TEST) += test-uclass.o
+COBJS-$(CONFIG_DM_TEST) += ut.o
+
+# Tests for particular subsystems - when enabling driver model for a new
+# subsystem you must add sandbox tests here.
+COBJS-$(CONFIG_DM_TEST) += core.o
+COBJS-$(CONFIG_DM_TEST) += ut.o
+COBJS-$(CONFIG_DM_GPIO) += gpio.o
+
+COBJS	:= $(sort $(COBJS-y))
+SRCS	:= $(COBJS:.o=.c)
+OBJS	:= $(addprefix $(obj),$(COBJS))
+
+all:	$(LIB) $(XOBJS)
+
+$(LIB): $(obj).depend $(OBJS)
+	$(call cmd_link_o_target, $(OBJS))
+
+#########################################################################
+
+# defines $(obj).depend target
+include $(SRCTREE)/rules.mk
+
+sinclude $(obj).depend
+
+#########################################################################
diff --git a/test/dm/core.c b/test/dm/core.c
new file mode 100644
index 0000000..c5984f8
--- /dev/null
+++ b/test/dm/core.c
@@ -0,0 +1,557 @@
+/*
+ * Tests for the core driver model code
+ *
+ * Copyright (c) 2013 Google, Inc
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
+ * MA 02111-1307 USA
+ */
+
+#include <common.h>
+#include <errno.h>
+#include <dm.h>
+#include <fdtdec.h>
+#include <malloc.h>
+#include <dm/device-internal.h>
+#include <dm/root.h>
+#include <dm/ut.h>
+#include <dm/util.h>
+#include <dm/test.h>
+#include <dm/uclass-internal.h>
+
+DECLARE_GLOBAL_DATA_PTR;
+
+enum {
+	TEST_INTVAL1		= 0,
+	TEST_INTVAL2		= 3,
+	TEST_INTVAL3		= 6,
+	TEST_INTVAL_MANUAL	= 101112,
+};
+
+static const struct dm_test_pdata test_pdata[] = {
+	{ .ping_add		= TEST_INTVAL1, },
+	{ .ping_add		= TEST_INTVAL2, },
+	{ .ping_add		= TEST_INTVAL3, },
+};
+
+static const struct dm_test_pdata test_pdata_manual = {
+	.ping_add		= TEST_INTVAL_MANUAL,
+};
+
+U_BOOT_DEVICE(dm_test_info1) = {
+	.name = "test_drv",
+	.platform_data = &test_pdata[0],
+};
+
+U_BOOT_DEVICE(dm_test_info2) = {
+	.name = "test_drv",
+	.platform_data = &test_pdata[1],
+};
+
+U_BOOT_DEVICE(dm_test_info3) = {
+	.name = "test_drv",
+	.platform_data = &test_pdata[2],
+};
+
+static struct driver_info driver_info_manual = {
+	.name = "test_manual_drv",
+	.platform_data = &test_pdata_manual,
+};
+
+/* Test that binding with platform_data occurs correctly */
+static int dm_test_autobind(struct dm_test_state *dms)
+{
+	struct device *dev;
+
+	/*
+	 * We should have a single class (UCLASS_ROOT) and a single root
+	 * device with no children.
+	 */
+	ut_assert(dms->root);
+	ut_asserteq(1, list_count_items(&gd->uclass_root));
+	ut_asserteq(0, list_count_items(&gd->dm_root->child_head));
+	ut_asserteq(0, dm_testdrv_op_count[DM_TEST_OP_POST_BIND]);
+
+	ut_assertok(dm_scan_platform_data());
+
+	/* We should have our test class now at least, plus more children */
+	ut_assert(1 < list_count_items(&gd->uclass_root));
+	ut_assert(0 < list_count_items(&gd->dm_root->child_head));
+
+	/* Our 3 dm_test_infox children should be bound to the test uclass */
+	ut_asserteq(3, dm_testdrv_op_count[DM_TEST_OP_POST_BIND]);
+
+	/* No devices should be probed */
+	list_for_each_entry(dev, &gd->dm_root->child_head, sibling_node)
+		ut_assert(!(dev->flags & DM_FLAG_ACTIVATED));
+
+	/* Our test driver should have been bound 3 times */
+	ut_assert(dm_testdrv_op_count[DM_TEST_OP_BIND] == 3);
+
+	return 0;
+}
+DM_TEST(dm_test_autobind, 0);
+
+/* Test that autoprobe finds all the expected devices */
+static int dm_test_autoprobe(struct dm_test_state *dms)
+{
+	int expected_base_add;
+	struct device *dev;
+	struct uclass *uc;
+	int i;
+
+	ut_assertok(uclass_get(UCLASS_TEST, &uc));
+	ut_assert(uc);
+
+	ut_asserteq(1, dm_testdrv_op_count[DM_TEST_OP_INIT]);
+	ut_asserteq(0, dm_testdrv_op_count[DM_TEST_OP_POST_PROBE]);
+
+	/* The root device should not be activated until needed */
+	ut_assert(!(dms->root->flags & DM_FLAG_ACTIVATED));
+
+	/*
+	 * We should be able to find the three test devices, and they should
+	 * all be activated as they are used (lazy activation, required by
+	 * U-Boot)
+	 */
+	for (i = 0; i < 3; i++) {
+		ut_assertok(uclass_find_device(UCLASS_TEST, i, &dev));
+		ut_assert(dev);
+		ut_assertf(!(dev->flags & DM_FLAG_ACTIVATED),
+			   "Driver %d/%s already activated", i, dev->name);
+
+		/* This should activate it */
+		ut_assertok(uclass_get_device(UCLASS_TEST, i, &dev));
+		ut_assert(dev);
+		ut_assert(dev->flags & DM_FLAG_ACTIVATED);
+
+		/* Activating a device should activate the root device */
+		if (!i)
+			ut_assert(dms->root->flags & DM_FLAG_ACTIVATED);
+	}
+
+	/* Our 3 dm_test_infox children should be passed to post_probe */
+	ut_asserteq(3, dm_testdrv_op_count[DM_TEST_OP_POST_PROBE]);
+
+	/* Also we can check the per-device data */
+	expected_base_add = 0;
+	for (i = 0; i < 3; i++) {
+		struct dm_test_uclass_perdev_priv *priv;
+		struct dm_test_pdata *pdata;
+
+		ut_assertok(uclass_find_device(UCLASS_TEST, i, &dev));
+		ut_assert(dev);
+
+		priv = dev->uclass_priv;
+		ut_assert(priv);
+		ut_asserteq(expected_base_add, priv->base_add);
+
+		pdata = dev->platform_data;
+		expected_base_add += pdata->ping_add;
+	}
+
+	return 0;
+}
+DM_TEST(dm_test_autoprobe, DM_TESTF_SCAN_PDATA);
+
+/* Check that we see the correct platform_data in each device */
+static int dm_test_platform_data(struct dm_test_state *dms)
+{
+	const struct dm_test_pdata *pdata;
+	struct device *dev;
+	int i;
+
+	for (i = 0; i < 3; i++) {
+		ut_assertok(uclass_find_device(UCLASS_TEST, i, &dev));
+		ut_assert(dev);
+		pdata = dev->platform_data;
+		ut_assert(pdata->ping_add == test_pdata[i].ping_add);
+	}
+
+	return 0;
+}
+DM_TEST(dm_test_platform_data, DM_TESTF_SCAN_PDATA);
+
+/* Test that we can bind, probe, remove, unbind a driver */
+static int dm_test_lifecycle(struct dm_test_state *dms)
+{
+	int op_count[DM_TEST_OP_COUNT];
+	struct device *dev, *test_dev;
+	int pingret;
+	int ret;
+
+	memcpy(op_count, dm_testdrv_op_count, sizeof(op_count));
+
+	ut_assertok(device_bind_by_name(dms->root, &driver_info_manual,
+					&dev));
+	ut_assert(dev);
+	ut_assert(dm_testdrv_op_count[DM_TEST_OP_BIND]
+			== op_count[DM_TEST_OP_BIND] + 1);
+	ut_assert(!dev->priv);
+
+	/* Probe the device - it should fail allocating private data */
+	dms->force_fail_alloc = 1;
+	ret = device_probe(dev);
+	ut_assert(ret == -ENOMEM);
+	ut_assert(dm_testdrv_op_count[DM_TEST_OP_PROBE]
+			== op_count[DM_TEST_OP_PROBE] + 1);
+	ut_assert(!dev->priv);
+
+	/* Try again without the alloc failure */
+	dms->force_fail_alloc = 0;
+	ut_assertok(device_probe(dev));
+	ut_assert(dm_testdrv_op_count[DM_TEST_OP_PROBE]
+			== op_count[DM_TEST_OP_PROBE] + 2);
+	ut_assert(dev->priv);
+
+	/* This should be device 3 in the uclass */
+	ut_assertok(uclass_find_device(UCLASS_TEST, 3, &test_dev));
+	ut_assert(dev == test_dev);
+
+	/* Try ping */
+	ut_assertok(test_ping(dev, 100, &pingret));
+	ut_assert(pingret == 102);
+
+	/* Now remove device 3 */
+	ut_asserteq(0, dm_testdrv_op_count[DM_TEST_OP_PRE_REMOVE]);
+	ut_assertok(device_remove(dev));
+	ut_asserteq(1, dm_testdrv_op_count[DM_TEST_OP_PRE_REMOVE]);
+
+	ut_asserteq(0, dm_testdrv_op_count[DM_TEST_OP_UNBIND]);
+	ut_asserteq(0, dm_testdrv_op_count[DM_TEST_OP_PRE_UNBIND]);
+	ut_assertok(device_unbind(dev));
+	ut_asserteq(1, dm_testdrv_op_count[DM_TEST_OP_UNBIND]);
+	ut_asserteq(1, dm_testdrv_op_count[DM_TEST_OP_PRE_UNBIND]);
+
+	return 0;
+}
+DM_TEST(dm_test_lifecycle, DM_TESTF_SCAN_PDATA | DM_TESTF_PROBE_TEST);
+
+/* Test that we can bind/unbind and the lists update correctly */
+static int dm_test_ordering(struct dm_test_state *dms)
+{
+	struct device *dev, *dev_penultimate, *dev_last, *test_dev;
+	int pingret;
+
+	ut_assertok(device_bind_by_name(dms->root, &driver_info_manual,
+					&dev));
+	ut_assert(dev);
+
+	/* Bind two new devices (numbers 4 and 5) */
+	ut_assertok(device_bind_by_name(dms->root, &driver_info_manual,
+					&dev_penultimate));
+	ut_assert(dev_penultimate);
+	ut_assertok(device_bind_by_name(dms->root, &driver_info_manual,
+					&dev_last));
+	ut_assert(dev_last);
+
+	/* Now remove device 3 */
+	ut_assertok(device_remove(dev));
+	ut_assertok(device_unbind(dev));
+
+	/* The device numbering should have shifted down one */
+	ut_assertok(uclass_find_device(UCLASS_TEST, 3, &test_dev));
+	ut_assert(dev_penultimate == test_dev);
+	ut_assertok(uclass_find_device(UCLASS_TEST, 4, &test_dev));
+	ut_assert(dev_last == test_dev);
+
+	/* Add back the original device 3, now in position 5 */
+	ut_assertok(device_bind_by_name(dms->root, &driver_info_manual, &dev));
+	ut_assert(dev);
+
+	/* Try ping */
+	ut_assertok(test_ping(dev, 100, &pingret));
+	ut_assert(pingret == 102);
+
+	/* Remove 3 and 4 */
+	ut_assertok(device_remove(dev_penultimate));
+	ut_assertok(device_unbind(dev_penultimate));
+	ut_assertok(device_remove(dev_last));
+	ut_assertok(device_unbind(dev_last));
+
+	/* Our device should now be in position 3 */
+	ut_assertok(uclass_find_device(UCLASS_TEST, 3, &test_dev));
+	ut_assert(dev == test_dev);
+
+	/* Now remove device 3 */
+	ut_assertok(device_remove(dev));
+	ut_assertok(device_unbind(dev));
+
+	return 0;
+}
+DM_TEST(dm_test_ordering, DM_TESTF_SCAN_PDATA);
+
+/* Check that we can perform operations on a device (do a ping) */
+int dm_check_operations(struct dm_test_state *dms, struct device *dev,
+			uint32_t base, struct dm_test_priv *priv)
+{
+	int expected;
+	int pingret;
+
+	/* Getting the child device should allocate platform_data / priv */
+	ut_assertok(testfdt_ping(dev, 10, &pingret));
+	ut_assert(dev->priv);
+	ut_assert(dev->platform_data);
+
+	expected = 10 + base;
+	ut_asserteq(expected, pingret);
+
+	/* Do another ping */
+	ut_assertok(testfdt_ping(dev, 20, &pingret));
+	expected = 20 + base;
+	ut_asserteq(expected, pingret);
+
+	/* Now check the ping_total */
+	priv = dev->priv;
+	ut_asserteq(DM_TEST_START_TOTAL + 10 + 20 + base * 2,
+		    priv->ping_total);
+
+	return 0;
+}
+
+/* Check that we can perform operations on devices */
+static int dm_test_operations(struct dm_test_state *dms)
+{
+	struct device *dev;
+	int i;
+
+	/*
+	 * Now check that the ping adds are what we expect. This is using the
+	 * ping-add property in each node.
+	 */
+	for (i = 0; i < ARRAY_SIZE(test_pdata); i++) {
+		uint32_t base;
+
+		ut_assertok(uclass_get_device(UCLASS_TEST, i, &dev));
+
+		/*
+		 * Get the 'reg' property, which tells us what the ping add
+		 * should be. We don't use the platform_data because we want
+		 * to test the code that sets that up (testfdt_drv_probe()).
+		 */
+		base = test_pdata[i].ping_add;
+		debug("dev=%d, base=%d\n", i, base);
+
+		ut_assert(!dm_check_operations(dms, dev, base, dev->priv));
+	}
+
+	return 0;
+}
+DM_TEST(dm_test_operations, DM_TESTF_SCAN_PDATA);
+
+/* Remove all drivers and check that things work */
+static int dm_test_remove(struct dm_test_state *dms)
+{
+	struct device *dev;
+	int i;
+
+	for (i = 0; i < 3; i++) {
+		ut_assertok(uclass_find_device(UCLASS_TEST, i, &dev));
+		ut_assert(dev);
+		ut_assertf(dev->flags & DM_FLAG_ACTIVATED,
+			   "Driver %d/%s not activated", i, dev->name);
+		ut_assertok(device_remove(dev));
+		ut_assertf(!(dev->flags & DM_FLAG_ACTIVATED),
+			   "Driver %d/%s should have deactivated", i,
+			   dev->name);
+		ut_assert(!dev->priv);
+	}
+
+	return 0;
+}
+DM_TEST(dm_test_remove, DM_TESTF_SCAN_PDATA | DM_TESTF_PROBE_TEST);
+
+/* Remove and recreate everything, check for memory leaks */
+static int dm_test_leak(struct dm_test_state *dms)
+{
+	int i;
+
+	for (i = 0; i < 2; i++) {
+		struct mallinfo start, end;
+		struct device *dev;
+		int ret;
+		int id;
+
+		start = mallinfo();
+		if (!start.uordblks)
+			puts("Warning: Please add '#define DEBUG' to the top of common/dlmalloc.c\n");
+
+		ut_assertok(dm_scan_platform_data());
+		ut_assertok(dm_scan_fdt(gd->fdt_blob));
+
+		/* Scanning the uclass is enough to probe all the devices */
+		for (id = UCLASS_ROOT; id < UCLASS_COUNT; id++) {
+			for (ret = uclass_first_device(UCLASS_TEST, &dev);
+			     dev;
+			     ret = uclass_next_device(&dev))
+				;
+			ut_assertok(ret);
+		}
+
+		/* Don't delete the root class, since we started with that */
+		for (id = UCLASS_ROOT + 1; id < UCLASS_COUNT; id++) {
+			struct uclass *uc;
+
+			uc = uclass_find(id);
+			if (!uc)
+				continue;
+			ut_assertok(uclass_destroy(uc));
+		}
+
+		end = mallinfo();
+		ut_asserteq(start.uordblks, end.uordblks);
+	}
+
+	return 0;
+}
+DM_TEST(dm_test_leak, 0);
+
+/* Test uclass init/destroy methods */
+static int dm_test_uclass(struct dm_test_state *dms)
+{
+	struct uclass *uc;
+
+	ut_assertok(uclass_get(UCLASS_TEST, &uc));
+	ut_asserteq(1, dm_testdrv_op_count[DM_TEST_OP_INIT]);
+	ut_asserteq(0, dm_testdrv_op_count[DM_TEST_OP_DESTROY]);
+	ut_assert(uc->priv);
+
+	ut_assertok(uclass_destroy(uc));
+	ut_asserteq(1, dm_testdrv_op_count[DM_TEST_OP_INIT]);
+	ut_asserteq(1, dm_testdrv_op_count[DM_TEST_OP_DESTROY]);
+
+	return 0;
+}
+DM_TEST(dm_test_uclass, 0);
+
+/**
+ * create_children() - Create children of a parent node
+ *
+ * @dms:	Test system state
+ * @parent:	Parent device
+ * @count:	Number of children to create
+ * @key:	Key value to put in first child. Subsequence children
+ *		receive an incrementing value
+ * @child:	If not NULL, then the child device pointers are written into
+ *		this array.
+ * @return 0 if OK, -ve on error
+ */
+static int create_children(struct dm_test_state *dms, struct device *parent,
+			   int count, int key, struct device *child[])
+{
+	struct device *dev;
+	int i;
+
+	for (i = 0; i < count; i++) {
+		struct dm_test_pdata *pdata;
+
+		ut_assertok(device_bind_by_name(parent, &driver_info_manual,
+						&dev));
+		pdata = calloc(1, sizeof(*pdata));
+		pdata->ping_add = key + i;
+		dev->platform_data = pdata;
+		if (child)
+			child[i] = dev;
+	}
+
+	return 0;
+}
+
+#define NODE_COUNT	10
+
+static int dm_test_children(struct dm_test_state *dms)
+{
+	struct device *top[NODE_COUNT];
+	struct device *child[NODE_COUNT];
+	struct device *grandchild[NODE_COUNT];
+	struct device *dev;
+	int total;
+	int ret;
+	int i;
+
+	/* We don't care about the numbering for this test */
+	dms->skip_post_probe = 1;
+
+	ut_assert(NODE_COUNT > 5);
+
+	/* First create 10 top-level children */
+	ut_assertok(create_children(dms, dms->root, NODE_COUNT, 0, top));
+
+	/* Now a few have their own children */
+	ut_assertok(create_children(dms, top[2], NODE_COUNT, 2, NULL));
+	ut_assertok(create_children(dms, top[5], NODE_COUNT, 5, child));
+
+	/* And grandchildren */
+	for (i = 0; i < NODE_COUNT; i++)
+		ut_assertok(create_children(dms, child[i], NODE_COUNT, 50 * i,
+					    i == 2 ? grandchild : NULL));
+
+	/* Check total number of devices */
+	total = NODE_COUNT * (3 + NODE_COUNT);
+	ut_asserteq(total, dm_testdrv_op_count[DM_TEST_OP_BIND]);
+
+	/* Try probing one of the grandchildren */
+	ut_assertok(uclass_get_device(UCLASS_TEST,
+				      NODE_COUNT * 3 + 2 * NODE_COUNT, &dev));
+	ut_asserteq_ptr(grandchild[0], dev);
+
+	/*
+	 * This should have probed the child and top node also, for a total
+	 * of 3 nodes.
+	 */
+	ut_asserteq(3, dm_testdrv_op_count[DM_TEST_OP_PROBE]);
+
+	/* Probe the other grandchildren */
+	for (i = 1; i < NODE_COUNT; i++)
+		ut_assertok(device_probe(grandchild[i]));
+
+	ut_asserteq(2 + NODE_COUNT, dm_testdrv_op_count[DM_TEST_OP_PROBE]);
+
+	/* Probe everything */
+	for (ret = uclass_first_device(UCLASS_TEST, &dev);
+	     dev;
+	     ret = uclass_next_device(&dev))
+		;
+	ut_assertok(ret);
+
+	ut_asserteq(total, dm_testdrv_op_count[DM_TEST_OP_PROBE]);
+
+	/* Remove a top-level child and check that the children are removed */
+	ut_assertok(device_remove(top[2]));
+	ut_asserteq(NODE_COUNT + 1, dm_testdrv_op_count[DM_TEST_OP_REMOVE]);
+	dm_testdrv_op_count[DM_TEST_OP_REMOVE] = 0;
+
+	/* Try one with grandchildren */
+	ut_assertok(uclass_get_device(UCLASS_TEST, 5, &dev));
+	ut_asserteq_ptr(dev, top[5]);
+	ut_assertok(device_remove(dev));
+	ut_asserteq(1 + NODE_COUNT * (1 + NODE_COUNT),
+		    dm_testdrv_op_count[DM_TEST_OP_REMOVE]);
+
+	/* Try the same with unbind */
+	ut_assertok(device_unbind(top[2]));
+	ut_asserteq(NODE_COUNT + 1, dm_testdrv_op_count[DM_TEST_OP_UNBIND]);
+	dm_testdrv_op_count[DM_TEST_OP_UNBIND] = 0;
+
+	/* Try one with grandchildren */
+	ut_assertok(uclass_get_device(UCLASS_TEST, 5, &dev));
+	ut_asserteq_ptr(dev, top[6]);
+	ut_assertok(device_unbind(top[5]));
+	ut_asserteq(1 + NODE_COUNT * (1 + NODE_COUNT),
+		    dm_testdrv_op_count[DM_TEST_OP_UNBIND]);
+
+	return 0;
+}
+DM_TEST(dm_test_children, 0);
diff --git a/test/dm/test-driver.c b/test/dm/test-driver.c
new file mode 100644
index 0000000..3864b31
--- /dev/null
+++ b/test/dm/test-driver.c
@@ -0,0 +1,159 @@
+/*
+ * Copyright (c) 2013 Google, Inc
+ *
+ * (C) Copyright 2012
+ * Pavel Herrmann <morpheus.ibis@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
+ * MA 02111-1307 USA
+ */
+
+#include <common.h>
+#include <dm.h>
+#include <errno.h>
+#include <malloc.h>
+#include <dm/test.h>
+#include <dm/ut.h>
+#include <asm/io.h>
+
+int dm_testdrv_op_count[DM_TEST_OP_COUNT];
+static struct dm_test_state *dms = &global_test_state;
+
+static int testdrv_ping(struct device *dev, int pingval, int *pingret)
+{
+	const struct dm_test_pdata *pdata = dev->platform_data;
+	struct dm_test_priv *priv = dev->priv;
+
+	*pingret = pingval + pdata->ping_add;
+	priv->ping_total += *pingret;
+
+	return 0;
+}
+
+static const struct test_ops test_ops = {
+	.ping = testdrv_ping,
+};
+
+static int test_bind(struct device *dev)
+{
+	/* Private data should not be allocated */
+	ut_assert(!dev->priv);
+
+	dm_testdrv_op_count[DM_TEST_OP_BIND]++;
+	return 0;
+}
+
+static int test_probe(struct device *dev)
+{
+	struct dm_test_priv *priv = dev->priv;
+
+	/* Private data should be allocated */
+	ut_assert(priv);
+
+	dm_testdrv_op_count[DM_TEST_OP_PROBE]++;
+	priv->ping_total += DM_TEST_START_TOTAL;
+	return 0;
+}
+
+static int test_remove(struct device *dev)
+{
+	/* Private data should still be allocated */
+	ut_assert(dev->priv);
+
+	dm_testdrv_op_count[DM_TEST_OP_REMOVE]++;
+	return 0;
+}
+
+static int test_unbind(struct device *dev)
+{
+	/* Private data should not be allocated */
+	ut_assert(!dev->priv);
+
+	dm_testdrv_op_count[DM_TEST_OP_UNBIND]++;
+	return 0;
+}
+
+U_BOOT_DRIVER(test_drv) = {
+	.name	= "test_drv",
+	.id	= UCLASS_TEST,
+	.ops	= &test_ops,
+	.bind	= test_bind,
+	.probe	= test_probe,
+	.remove	= test_remove,
+	.unbind	= test_unbind,
+	.priv_auto_alloc_size = sizeof(struct dm_test_priv),
+};
+
+U_BOOT_DRIVER(test2_drv) = {
+	.name	= "test2_drv",
+	.id	= UCLASS_TEST,
+	.ops	= &test_ops,
+	.bind	= test_bind,
+	.probe	= test_probe,
+	.remove	= test_remove,
+	.unbind	= test_unbind,
+	.priv_auto_alloc_size = sizeof(struct dm_test_priv),
+};
+
+static int test_manual_drv_ping(struct device *dev, int pingval, int *pingret)
+{
+	*pingret = pingval + 2;
+
+	return 0;
+}
+
+static const struct test_ops test_manual_ops = {
+	.ping = test_manual_drv_ping,
+};
+
+static int test_manual_bind(struct device *dev)
+{
+	dm_testdrv_op_count[DM_TEST_OP_BIND]++;
+
+	return 0;
+}
+
+static int test_manual_probe(struct device *dev)
+{
+	dm_testdrv_op_count[DM_TEST_OP_PROBE]++;
+	if (!dms->force_fail_alloc)
+		dev->priv = calloc(1, sizeof(struct dm_test_priv));
+	if (!dev->priv)
+		return -ENOMEM;
+
+	return 0;
+}
+
+static int test_manual_remove(struct device *dev)
+{
+	dm_testdrv_op_count[DM_TEST_OP_REMOVE]++;
+	return 0;
+}
+
+static int test_manual_unbind(struct device *dev)
+{
+	dm_testdrv_op_count[DM_TEST_OP_UNBIND]++;
+	return 0;
+}
+
+U_BOOT_DRIVER(test_manual_drv) = {
+	.name	= "test_manual_drv",
+	.id	= UCLASS_TEST,
+	.ops	= &test_manual_ops,
+	.bind	= test_manual_bind,
+	.probe	= test_manual_probe,
+	.remove	= test_manual_remove,
+	.unbind	= test_manual_unbind,
+};
diff --git a/test/dm/test-fdt.c b/test/dm/test-fdt.c
new file mode 100644
index 0000000..33ba599
--- /dev/null
+++ b/test/dm/test-fdt.c
@@ -0,0 +1,148 @@
+/*
+ * Copyright (c) 2013 Google, Inc
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
+ * MA 02111-1307 USA
+ */
+
+#include <common.h>
+#include <dm.h>
+#include <errno.h>
+#include <fdtdec.h>
+#include <malloc.h>
+#include <asm/io.h>
+#include <dm/test.h>
+#include <dm/root.h>
+#include <dm/ut.h>
+#include <dm/uclass-internal.h>
+#include <dm/util.h>
+
+DECLARE_GLOBAL_DATA_PTR;
+
+static int testfdt_drv_ping(struct device *dev, int pingval, int *pingret)
+{
+	const struct dm_test_pdata *pdata = dev->platform_data;
+	struct dm_test_priv *priv = dev->priv;
+
+	*pingret = pingval + pdata->ping_add;
+	priv->ping_total += *pingret;
+
+	return 0;
+}
+
+static const struct test_ops test_ops = {
+	.ping = testfdt_drv_ping,
+};
+
+static int testfdt_drv_probe(struct device *dev)
+{
+	struct dm_test_pdata *pdata = dev->platform_data;
+	struct dm_test_priv *priv = dev->priv;
+
+	pdata->ping_add = fdtdec_get_int(gd->fdt_blob, dev->of_offset,
+					"ping-add", -1);
+	pdata->base = fdtdec_get_addr(gd->fdt_blob, dev->of_offset, "reg");
+	priv->ping_total += DM_TEST_START_TOTAL;
+
+	return 0;
+}
+
+static const struct device_id testfdt_ids[] = {
+	{
+		.compatible = "denx,u-boot-fdt-test",
+		.data = DM_TEST_TYPE_FIRST },
+	{
+		.compatible = "google,another-fdt-test",
+		.data = DM_TEST_TYPE_SECOND },
+	{ }
+};
+
+U_BOOT_DRIVER(testfdt_drv) = {
+	.name	= "testfdt_drv",
+	.of_match	= testfdt_ids,
+	.id	= UCLASS_TEST_FDT,
+	.probe	= testfdt_drv_probe,
+	.ops	= &test_ops,
+	.priv_auto_alloc_size = sizeof(struct dm_test_priv),
+	.platform_data_auto_alloc_size = sizeof(struct dm_test_pdata),
+};
+
+/* From here is the testfdt uclass code */
+int testfdt_ping(struct device *dev, int pingval, int *pingret)
+{
+	const struct test_ops *ops = device_get_ops(dev);
+
+	if (!ops->ping)
+		return -ENOSYS;
+
+	return ops->ping(dev, pingval, pingret);
+}
+
+UCLASS_DRIVER(testfdt) = {
+	.name		= "testfdt",
+	.id		= UCLASS_TEST_FDT,
+};
+
+/* Test that FDT-based binding works correctly */
+static int dm_test_fdt(struct dm_test_state *dms)
+{
+	const int num_drivers = 3;
+	struct device *dev;
+	struct uclass *uc;
+	int ret;
+	int i;
+
+	ret = dm_scan_fdt(gd->fdt_blob);
+	ut_assert(!ret);
+
+	ret = uclass_get(UCLASS_TEST_FDT, &uc);
+	ut_assert(!ret);
+
+	/* These are num_drivers compatible root-level device tree nodes */
+	ut_asserteq(num_drivers, list_count_items(&uc->dev_head));
+
+	/* Each should have no platform_data / priv */
+	for (i = 0; i < num_drivers; i++) {
+		ret = uclass_find_device(UCLASS_TEST_FDT, i, &dev);
+		ut_assert(!ret);
+		ut_assert(!dev->priv);
+		ut_assert(!dev->platform_data);
+	}
+
+	/*
+	 * Now check that the ping adds are what we expect. This is using the
+	 * ping-add property in each node.
+	 */
+	for (i = 0; i < num_drivers; i++) {
+		uint32_t base;
+
+		ret = uclass_get_device(UCLASS_TEST_FDT, i, &dev);
+		ut_assert(!ret);
+
+		/*
+		 * Get the 'reg' property, which tells us what the ping add
+		 * should be. We don't use the platform_data because we want
+		 * to test the code that sets that up (testfdt_drv_probe()).
+		 */
+		base = fdtdec_get_addr(gd->fdt_blob, dev->of_offset, "reg");
+		debug("dev=%d, base=%d: %s\n", i, base,
+		      fdt_get_name(gd->fdt_blob, dev->of_offset, NULL));
+
+		ut_assert(!dm_check_operations(dms, dev, base, dev->priv));
+	}
+
+	return 0;
+}
+DM_TEST(dm_test_fdt, 0);
diff --git a/test/dm/test-main.c b/test/dm/test-main.c
new file mode 100644
index 0000000..8d4ef01
--- /dev/null
+++ b/test/dm/test-main.c
@@ -0,0 +1,120 @@
+/*
+ * Copyright (c) 2013 Google, Inc
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
+ * MA 02111-1307 USA
+ */
+
+#include <common.h>
+#include <dm.h>
+#include <errno.h>
+#include <dm/test.h>
+#include <dm/root.h>
+#include <dm/uclass-internal.h>
+#include <dm/ut.h>
+
+DECLARE_GLOBAL_DATA_PTR;
+
+struct dm_test_state global_test_state;
+
+/* Get ready for testing */
+static int dm_test_init(struct dm_test_state *dms)
+{
+	memset(dms, '\0', sizeof(*dms));
+	gd->dm_root = NULL;
+	memset(dm_testdrv_op_count, '\0', sizeof(dm_testdrv_op_count));
+
+	ut_assertok(dm_init());
+	dms->root = dm_root();
+
+	return 0;
+}
+
+/* Ensure all the test devices are probed */
+static int do_autoprobe(struct dm_test_state *dms)
+{
+	struct device *dev;
+	int ret;
+
+	/* Scanning the uclass is enough to probe all the devices */
+	for (ret = uclass_first_device(UCLASS_TEST, &dev);
+	     dev;
+	     ret = uclass_next_device(&dev))
+	     ;
+
+	return ret;
+}
+
+static int dm_test_destroy(struct dm_test_state *dms)
+{
+	int id;
+
+	for (id = 0; id < UCLASS_COUNT; id++) {
+		struct uclass *uc;
+
+		/*
+		 * If the uclass doesn't exist we don't want to create it. So
+		 * check that here before we call uclass_find_device()/
+		 */
+		uc = uclass_find(id);
+		if (!uc)
+			continue;
+		ut_assertok(uclass_destroy(uc));
+	}
+
+	return 0;
+}
+
+int dm_test_main(void)
+{
+	struct dm_test *tests = ll_entry_start(struct dm_test, dm_test);
+	const int n_ents = ll_entry_count(struct dm_test, dm_test);
+	struct dm_test_state *dms = &global_test_state;
+	struct dm_test *test;
+
+	/*
+	 * If we have no device tree, or it only has a root node, then these
+	 * tests clearly aren't going to work...
+	 */
+	if (!gd->fdt_blob || fdt_next_node(gd->fdt_blob, 0, NULL) < 0) {
+		puts("Please run with test device tree:\n"
+		     "     dtc -I dts -O dtb test/dm/test.dts  -o test/dm/out.dtb\n"
+		     "    ./u-boot -d test/dm/test.dtb\n");
+		ut_assert(gd->fdt_blob);
+	}
+
+	printf("Running %d driver model tests\n", n_ents);
+
+	for (test = tests; test < tests + n_ents; test++) {
+		printf("Test: %s\n", test->name);
+		ut_assertok(dm_test_init(dms));
+
+		if (test->flags & DM_TESTF_SCAN_PDATA)
+			ut_assertok(dm_scan_platform_data());
+		if (test->flags & DM_TESTF_PROBE_TEST)
+			ut_assertok(do_autoprobe(dms));
+		if (test->flags & DM_TESTF_SCAN_FDT)
+			ut_assertok(dm_scan_fdt(gd->fdt_blob));
+
+		if (test->func(dms))
+			break;
+
+		ut_assertok(dm_test_destroy(dms));
+	}
+
+	printf("Failures: %d\n", dms->fail_count);
+
+	return 0;
+}
diff --git a/test/dm/test-uclass.c b/test/dm/test-uclass.c
new file mode 100644
index 0000000..fb1ecc4
--- /dev/null
+++ b/test/dm/test-uclass.c
@@ -0,0 +1,117 @@
+/*
+ * Copyright (c) 2013 Google, Inc
+ *
+ * (C) Copyright 2012
+ * Pavel Herrmann <morpheus.ibis@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
+ * MA 02111-1307 USA
+ */
+
+#include <common.h>
+#include <malloc.h>
+#include <dm.h>
+#include <errno.h>
+#include <dm/test.h>
+#include <dm/ut.h>
+#include <asm/io.h>
+#include <linux/list.h>
+
+static struct dm_test_state *dms = &global_test_state;
+
+int test_ping(struct device *dev, int pingval, int *pingret)
+{
+	const struct test_ops *ops = device_get_ops(dev);
+
+	if (!ops->ping)
+		return -ENOSYS;
+
+	return ops->ping(dev, pingval, pingret);
+}
+
+static int test_post_bind(struct device *dev)
+{
+	dm_testdrv_op_count[DM_TEST_OP_POST_BIND]++;
+
+	return 0;
+}
+
+static int test_pre_unbind(struct device *dev)
+{
+	dm_testdrv_op_count[DM_TEST_OP_PRE_UNBIND]++;
+
+	return 0;
+}
+
+static int test_post_probe(struct device *dev)
+{
+	struct device *prev = list_entry(dev->uclass_node.prev, struct device,
+					 uclass_node);
+	struct dm_test_uclass_perdev_priv *priv = dev->uclass_priv;
+	struct uclass *uc = dev->uclass;
+
+	dm_testdrv_op_count[DM_TEST_OP_POST_PROBE]++;
+	ut_assert(priv);
+	ut_assert(device_active(dev));
+	priv->base_add = 0;
+	if (dms->skip_post_probe)
+		return 0;
+	if (&prev->uclass_node != &uc->dev_head) {
+		struct dm_test_uclass_perdev_priv *prev_uc_priv
+				= prev->uclass_priv;
+		struct dm_test_pdata *pdata = prev->platform_data;
+
+		ut_assert(pdata);
+		ut_assert(prev_uc_priv);
+		priv->base_add = prev_uc_priv->base_add + pdata->ping_add;
+	}
+
+	return 0;
+}
+
+static int test_pre_remove(struct device *dev)
+{
+	dm_testdrv_op_count[DM_TEST_OP_PRE_REMOVE]++;
+
+	return 0;
+}
+
+static int test_init(struct uclass *uc)
+{
+	dm_testdrv_op_count[DM_TEST_OP_INIT]++;
+	ut_assert(uc->priv);
+
+	return 0;
+}
+
+static int test_destroy(struct uclass *uc)
+{
+	dm_testdrv_op_count[DM_TEST_OP_DESTROY]++;
+
+	return 0;
+}
+
+UCLASS_DRIVER(test) = {
+	.name		= "test",
+	.id		= UCLASS_TEST,
+	.post_bind	= test_post_bind,
+	.pre_unbind	= test_pre_unbind,
+	.post_probe	= test_post_probe,
+	.pre_remove	= test_pre_remove,
+	.init		= test_init,
+	.destroy	= test_destroy,
+	.priv_auto_alloc_size	= sizeof(struct dm_test_uclass_priv),
+	.per_device_auto_alloc_size = sizeof(struct dm_test_uclass_perdev_priv),
+};
diff --git a/test/dm/test.dts b/test/dm/test.dts
new file mode 100644
index 0000000..ec5364f
--- /dev/null
+++ b/test/dm/test.dts
@@ -0,0 +1,59 @@
+/dts-v1/;
+
+/ {
+	model = "sandbox";
+	compatible = "sandbox";
+	#address-cells = <1>;
+	#size-cells = <0>;
+
+	a-test {
+		reg = <0>;
+		compatible = "denx,u-boot-fdt-test";
+		ping-add = <0>;
+	};
+
+	junk {
+		reg = <1>;
+		compatible = "not,compatible";
+	};
+
+	no-compatible {
+		reg = <2>;
+	};
+
+	b-test {
+		reg = <3>;
+		compatible = "denx,u-boot-fdt-test";
+		ping-add = <3>;
+	};
+
+	some-bus {
+		#address-cells = <1>;
+		#size-cells = <0>;
+		reg = <4>;
+		ping-add = <4>;
+		c-test {
+			compatible = "denx,u-boot-fdt-test";
+			reg = <5>;
+			ping-add = <5>;
+		};
+	};
+
+	d-test {
+		reg = <6>;
+		ping-add = <6>;
+		compatible = "google,another-fdt-test";
+	};
+
+	base-gpios {
+		compatible = "sandbox,gpio";
+		gpio-bank-name = "a";
+		num-gpios = <20>;
+	};
+
+	extra-gpios {
+		compatible = "sandbox,gpio";
+		gpio-bank-name = "b";
+		num-gpios = <10>;
+	};
+};
diff --git a/test/dm/ut.c b/test/dm/ut.c
new file mode 100644
index 0000000..9b12a0c
--- /dev/null
+++ b/test/dm/ut.c
@@ -0,0 +1,46 @@
+/*
+ * Simple unit test library for driver model
+ *
+ * Copyright (c) 2013 Google, Inc
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
+ * MA 02111-1307 USA
+ */
+
+#include <common.h>
+#include <dm/test.h>
+#include <dm/ut.h>
+
+struct dm_test_state;
+
+void ut_fail(struct dm_test_state *dms, const char *fname, int line,
+	     const char *func, const char *cond)
+{
+	printf("%s:%d, %s(): %s\n", fname, line, func, cond);
+	dms->fail_count++;
+}
+
+void ut_failf(struct dm_test_state *dms, const char *fname, int line,
+	      const char *func, const char *cond, const char *fmt, ...)
+{
+	va_list args;
+
+	printf("%s:%d, %s(): %s: ", fname, line, func, cond);
+	va_start(args, fmt);
+	vprintf(fmt, args);
+	va_end(args);
+	putc('\n');
+	dms->fail_count++;
+}
-- 
1.8.3

^ permalink raw reply related	[flat|nested] 30+ messages in thread

* [U-Boot] [PATCH v3 10/16] dm: Add a 'dm' command for testing
  2013-06-19  3:52 [U-Boot] [PATCH v3 0/16] Driver model implementation, tests, demo and GPIO Simon Glass
                   ` (8 preceding siblings ...)
  2013-06-19  3:52 ` [U-Boot] [PATCH v3 09/16] dm: Add basic tests Simon Glass
@ 2013-06-19  3:52 ` Simon Glass
  2013-06-28 20:57   ` Marek Vasut
  2013-06-19  3:52 ` [U-Boot] [PATCH v3 11/16] dm: Add a demonstration/example driver Simon Glass
                   ` (5 subsequent siblings)
  15 siblings, 1 reply; 30+ messages in thread
From: Simon Glass @ 2013-06-19  3:52 UTC (permalink / raw)
  To: u-boot

This command is not required for driver model operation, but can be useful
for testing. It provides simple dumps of internal data structures.

Signed-off-by: Simon Glass <sjg@chromium.org>
Signed-off-by: Marek Vasut <marex@denx.de>
Signed-off-by: Pavel Herrmann <morpheus.ibis@gmail.com>
Signed-off-by: Viktor K?iv?k <viktor.krivak@gmail.com>
Signed-off-by: Tomas Hlavacek <tmshlvck@gmail.com>
---
Changes in v3: None
Changes in v2: None

 include/configs/sandbox.h |   1 +
 test/dm/Makefile          |   1 +
 test/dm/cmd_dm.c          | 146 ++++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 148 insertions(+)
 create mode 100644 test/dm/cmd_dm.c

diff --git a/include/configs/sandbox.h b/include/configs/sandbox.h
index 38e0384..8e9a661 100644
--- a/include/configs/sandbox.h
+++ b/include/configs/sandbox.h
@@ -23,6 +23,7 @@
 #define __CONFIG_H
 
 #define CONFIG_DM
+#define CONFIG_CMD_DM
 #define CONFIG_DM_TEST
 
 /* Number of bits in a C 'long' on this architecture */
diff --git a/test/dm/Makefile b/test/dm/Makefile
index 310aa05..8f67215 100644
--- a/test/dm/Makefile
+++ b/test/dm/Makefile
@@ -21,6 +21,7 @@ include $(TOPDIR)/config.mk
 
 LIB	= $(obj)libtestdm.o
 
+COBJS-$(CONFIG_CMD_DM) += cmd_dm.o
 COBJS-$(CONFIG_DM_TEST) += test-driver.o
 COBJS-$(CONFIG_DM_TEST) += test-fdt.o
 COBJS-$(CONFIG_DM_TEST) += test-main.o
diff --git a/test/dm/cmd_dm.c b/test/dm/cmd_dm.c
new file mode 100644
index 0000000..49d7887
--- /dev/null
+++ b/test/dm/cmd_dm.c
@@ -0,0 +1,146 @@
+/*
+ * Copyright (c) 2013 Google, Inc
+ *
+ * (C) Copyright 2012
+ * Marek Vasut <marex@denx.de>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
+ * MA 02111-1307 USA
+ */
+
+#include <common.h>
+#include <dm.h>
+#include <malloc.h>
+#include <errno.h>
+#include <asm/io.h>
+#include <dm/root.h>
+#include <dm/test.h>
+#include <dm/uclass-internal.h>
+
+static int display_succ(struct device *in, char *buf)
+{
+	int len;
+	int ip = 0;
+	char local[16];
+	struct device *pos, *n, *prev = NULL;
+
+	printf("%s- %s @ %08x", buf, in->name, map_to_sysmem(in));
+	if (in->flags & DM_FLAG_ACTIVATED)
+		puts(" - activated");
+	puts("\n");
+
+	if (list_empty(&in->child_head))
+		return 0;
+
+	len = strlen(buf);
+	strncpy(local, buf, sizeof(local));
+	snprintf(local + len, 2, "|");
+	if (len && local[len - 1] == '`')
+		local[len - 1] = ' ';
+
+	list_for_each_entry_safe(pos, n, &in->child_head, sibling_node) {
+		if (ip++)
+			display_succ(prev, local);
+		prev = pos;
+	}
+
+	snprintf(local + len, 2, "`");
+	display_succ(prev, local);
+
+	return 0;
+}
+
+static int dm_dump(struct device *dev)
+{
+	if (!dev)
+		return -EINVAL;
+	return display_succ(dev, "");
+}
+
+static int do_dm_dump_all(cmd_tbl_t *cmdtp, int flag, int argc,
+			  char * const argv[])
+{
+	struct device *root;
+
+	root = dm_root();
+	printf("ROOT %08x\n", map_to_sysmem(root));
+	return dm_dump(root);
+}
+
+static int do_dm_dump_uclass(cmd_tbl_t *cmdtp, int flag, int argc,
+			     char * const argv[])
+{
+	struct uclass *uc;
+	int ret;
+	int id;
+
+	for (id = 0; id < UCLASS_COUNT; id++) {
+		struct device *dev;
+
+		ret = uclass_get(id, &uc);
+		if (ret)
+			continue;
+
+		printf("uclass %d: %s\n", id, uc->uc_drv->name);
+		for (ret = uclass_first_device(id, &dev);
+		     dev;
+		     ret = uclass_next_device(&dev)) {
+			printf("  %s @  %08x:\n", dev->name,
+			       map_to_sysmem(dev));
+		}
+		puts("\n");
+	}
+
+	return 0;
+}
+
+static int do_dm_test(cmd_tbl_t *cmdtp, int flag, int argc,
+			  char * const argv[])
+{
+	return dm_test_main();
+}
+
+static cmd_tbl_t test_commands[] = {
+	U_BOOT_CMD_MKENT(dump, 0, 1, do_dm_dump_all, "", ""),
+	U_BOOT_CMD_MKENT(uclass, 1, 1, do_dm_dump_uclass, "", ""),
+	U_BOOT_CMD_MKENT(test, 1, 1, do_dm_test, "", ""),
+};
+
+static int do_dm(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
+{
+	cmd_tbl_t *test_cmd;
+	int ret;
+
+	if (argc != 2)
+		return CMD_RET_USAGE;
+	test_cmd = find_cmd_tbl(argv[1], test_commands,
+				ARRAY_SIZE(test_commands));
+	argc -= 2;
+	argv += 2;
+	if (!test_cmd || argc > test_cmd->maxargs)
+		return CMD_RET_USAGE;
+
+	ret = test_cmd->cmd(test_cmd, flag, argc, argv);
+
+	return cmd_process_error(test_cmd, ret);
+}
+
+U_BOOT_CMD(
+	dm,	2,	1,	do_dm,
+	"Driver model low level access",
+	"dump         Dump driver model tree\n"
+	"dm uclass        Dump list of instances for each uclass\n"
+	"dm test         Run tests"
+);
-- 
1.8.3

^ permalink raw reply related	[flat|nested] 30+ messages in thread

* [U-Boot] [PATCH v3 11/16] dm: Add a demonstration/example driver
  2013-06-19  3:52 [U-Boot] [PATCH v3 0/16] Driver model implementation, tests, demo and GPIO Simon Glass
                   ` (9 preceding siblings ...)
  2013-06-19  3:52 ` [U-Boot] [PATCH v3 10/16] dm: Add a 'dm' command for testing Simon Glass
@ 2013-06-19  3:52 ` Simon Glass
  2013-06-28 21:46   ` Marek Vasut
  2013-06-19  3:52 ` [U-Boot] [PATCH v3 12/16] dm: Add GPIO support and tests Simon Glass
                   ` (4 subsequent siblings)
  15 siblings, 1 reply; 30+ messages in thread
From: Simon Glass @ 2013-06-19  3:52 UTC (permalink / raw)
  To: u-boot

As an example of how to write a uclass and a driver, provide a demo version
of each, accessible through the 'demo' command.

To use these with driver model, define CONFIG_CMD_DEMO and CONFIG_DM_DEMO.

The two demo drivers are enabled with CONFIG_DM_DEMO_SIMPLE and
CONFIG_DM_DEMO_SHAPE.

Signed-off-by: Simon Glass <sjg@chromium.org>
Signed-off-by: Marek Vasut <marex@denx.de>
Signed-off-by: Pavel Herrmann <morpheus.ibis@gmail.com>
Signed-off-by: Viktor K?iv?k <viktor.krivak@gmail.com>
Signed-off-by: Tomas Hlavacek <tmshlvck@gmail.com>
---
Changes in v3:
- Fix up demo command help
- Update demo driver to use device tree

Changes in v2: None

 Makefile                   |   1 +
 common/Makefile            |   1 +
 common/cmd_demo.c          | 115 ++++++++++++++++++++++++++++++++++++
 drivers/demo/Makefile      |  44 ++++++++++++++
 drivers/demo/demo-pdata.c  |  60 +++++++++++++++++++
 drivers/demo/demo-shape.c  | 143 +++++++++++++++++++++++++++++++++++++++++++++
 drivers/demo/demo-simple.c |  61 +++++++++++++++++++
 drivers/demo/demo-uclass.c |  73 +++++++++++++++++++++++
 include/configs/sandbox.h  |   4 ++
 include/dm-demo.h          |  49 ++++++++++++++++
 10 files changed, 551 insertions(+)
 create mode 100644 common/cmd_demo.c
 create mode 100644 drivers/demo/Makefile
 create mode 100644 drivers/demo/demo-pdata.c
 create mode 100644 drivers/demo/demo-shape.c
 create mode 100644 drivers/demo/demo-simple.c
 create mode 100644 drivers/demo/demo-uclass.c
 create mode 100644 include/dm-demo.h

diff --git a/Makefile b/Makefile
index 0655c9c..1654ec3 100644
--- a/Makefile
+++ b/Makefile
@@ -338,6 +338,7 @@ LIBS-y += post/libpost.o
 LIBS-y += test/libtest.o
 LIBS-y += test/dm/libtestdm.o
 
+LIBS-$(CONFIG_DM_DEMO) += drivers/demo/libdemo.o
 LIBS-$(CONFIG_DM) += common/dm/libdm.o
 
 ifneq ($(CONFIG_AM33XX)$(CONFIG_OMAP34XX)$(CONFIG_OMAP44XX)$(CONFIG_OMAP54XX)$(CONFIG_TI814X),)
diff --git a/common/Makefile b/common/Makefile
index 3ba4316..99921ca 100644
--- a/common/Makefile
+++ b/common/Makefile
@@ -85,6 +85,7 @@ COBJS-$(CONFIG_CMD_CONSOLE) += cmd_console.o
 COBJS-$(CONFIG_CMD_CPLBINFO) += cmd_cplbinfo.o
 COBJS-$(CONFIG_DATAFLASH_MMC_SELECT) += cmd_dataflash_mmc_mux.o
 COBJS-$(CONFIG_CMD_DATE) += cmd_date.o
+COBJS-$(CONFIG_CMD_DEMO) += cmd_demo.o
 COBJS-$(CONFIG_CMD_SOUND) += cmd_sound.o
 ifdef CONFIG_4xx
 COBJS-$(CONFIG_CMD_SETGETDCR) += cmd_dcr.o
diff --git a/common/cmd_demo.c b/common/cmd_demo.c
new file mode 100644
index 0000000..6ec91f8
--- /dev/null
+++ b/common/cmd_demo.c
@@ -0,0 +1,115 @@
+/*
+ * Copyright (c) 2013 Google, Inc
+ *
+ * (C) Copyright 2012
+ * Pavel Herrmann <morpheus.ibis@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
+ * MA 02111-1307 USA
+ */
+
+#include <common.h>
+#include <dm-demo.h>
+#include <asm/io.h>
+
+struct device *demo_dev;
+
+static int do_demo_hello(cmd_tbl_t *cmdtp, int flag, int argc,
+			 char * const argv[])
+{
+	int ch = 0;
+
+	if (argc)
+		ch = *argv[0];
+
+	return demo_hello(demo_dev, ch);
+}
+
+static int do_demo_status(cmd_tbl_t *cmdtp, int flag, int argc,
+			  char * const argv[])
+{
+	int status;
+	int ret;
+
+	ret = demo_status(demo_dev, &status);
+	if (ret)
+		return ret;
+
+	printf("Status: %d\n", status);
+
+	return 0;
+}
+
+int do_demo_list(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
+{
+	struct device *dev;
+	int i, ret;
+
+	puts("Demo uclass entries:\n");
+
+	for (i = 0, ret = uclass_first_device(UCLASS_DEMO, &dev);
+	     dev;
+	     ret = uclass_next_device(&dev)) {
+		printf("entry %d - instance %08x, ops %08x, platform_data %08x\n",
+		       i++, map_to_sysmem(dev),
+		       map_to_sysmem(dev->driver->ops),
+		       map_to_sysmem(dev->platform_data));
+	}
+
+	return cmd_process_error(cmdtp, ret);
+}
+
+static cmd_tbl_t demo_commands[] = {
+	U_BOOT_CMD_MKENT(list, 0, 1, do_demo_list, "", ""),
+	U_BOOT_CMD_MKENT(hello, 2, 1, do_demo_hello, "", ""),
+	U_BOOT_CMD_MKENT(status, 1, 1, do_demo_status, "", ""),
+};
+
+static int do_demo(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
+{
+	cmd_tbl_t *demo_cmd;
+	int devnum = 0;
+	int ret;
+
+	if (argc < 2)
+		return CMD_RET_USAGE;
+	demo_cmd = find_cmd_tbl(argv[1], demo_commands,
+				ARRAY_SIZE(demo_commands));
+	argc -= 2;
+	argv += 2;
+	if (!demo_cmd || argc > demo_cmd->maxargs)
+		return CMD_RET_USAGE;
+
+	if (argc) {
+		devnum = simple_strtoul(argv[0], NULL, 10);
+		ret = uclass_get_device(UCLASS_DEMO, devnum, &demo_dev);
+		if (ret)
+			return cmd_process_error(cmdtp, ret);
+		argc--;
+		argv++;
+	}
+
+	ret = demo_cmd->cmd(demo_cmd, flag, argc, argv);
+
+	return cmd_process_error(demo_cmd, ret);
+}
+
+U_BOOT_CMD(
+	demo,   4,      1,      do_demo,
+	"Driver model (dm) demo operations",
+	"list                     List available demo devices\n"
+	"demo hello <num> [<char>]     Say hello\n"
+	"demo status <num>             Get demo device status"
+);
diff --git a/drivers/demo/Makefile b/drivers/demo/Makefile
new file mode 100644
index 0000000..5267659
--- /dev/null
+++ b/drivers/demo/Makefile
@@ -0,0 +1,44 @@
+#
+# Copyright (c) 2013 Google, Inc
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License as
+# published by the Free Software Foundation; either version 2 of
+# the License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.	See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston,
+# MA 02111-1307 USA
+#
+
+include $(TOPDIR)/config.mk
+
+LIB	:= $(obj)libdemo.o
+
+COBJS-$(CONFIG_DM_DEMO) += demo-uclass.o demo-pdata.o
+COBJS-$(CONFIG_DM_DEMO_SIMPLE) += demo-simple.o
+COBJS-$(CONFIG_DM_DEMO_SHAPE) += demo-shape.o
+
+COBJS	:= $(COBJS-y)
+SRCS	:= $(COBJS:.o=.c)
+OBJS	:= $(addprefix $(obj),$(COBJS))
+
+all:	$(LIB)
+
+$(LIB):	$(obj).depend $(OBJS)
+	$(call cmd_link_o_target, $(OBJS))
+
+#########################################################################
+
+# defines $(obj).depend target
+include $(SRCTREE)/rules.mk
+
+sinclude $(obj).depend
+
+#########################################################################
diff --git a/drivers/demo/demo-pdata.c b/drivers/demo/demo-pdata.c
new file mode 100644
index 0000000..c04b26e
--- /dev/null
+++ b/drivers/demo/demo-pdata.c
@@ -0,0 +1,60 @@
+/*
+ * Copyright (c) 2013 Google, Inc
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
+ * MA 02111-1307 USA
+ */
+
+#include <common.h>
+#include <dm.h>
+#include <dm-demo.h>
+
+static const struct dm_demo_pdata red_square = {
+	.colour = "red",
+	.sides = 4.
+};
+static const struct dm_demo_pdata green_triangle = {
+	.colour = "green",
+	.sides = 3.
+};
+static const struct dm_demo_pdata yellow_hexagon = {
+	.colour = "yellow",
+	.sides = 6.
+};
+
+U_BOOT_DEVICE(demo0) = {
+	.name = "demo_shape_drv",
+	.platform_data = &red_square,
+};
+
+U_BOOT_DEVICE(demo1) = {
+	.name = "demo_simple_drv",
+	.platform_data = &red_square,
+};
+
+U_BOOT_DEVICE(demo2) = {
+	.name = "demo_shape_drv",
+	.platform_data = &green_triangle,
+};
+
+U_BOOT_DEVICE(demo3) = {
+	.name = "demo_simple_drv",
+	.platform_data = &yellow_hexagon,
+};
+
+U_BOOT_DEVICE(demo4) = {
+	.name = "demo_shape_drv",
+	.platform_data = &yellow_hexagon,
+};
diff --git a/drivers/demo/demo-shape.c b/drivers/demo/demo-shape.c
new file mode 100644
index 0000000..918115a
--- /dev/null
+++ b/drivers/demo/demo-shape.c
@@ -0,0 +1,143 @@
+/*
+ * Copyright (c) 2013 Google, Inc
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
+ * MA 02111-1307 USA
+ */
+
+#include <common.h>
+#include <dm.h>
+#include <errno.h>
+#include <fdtdec.h>
+#include <malloc.h>
+#include <dm-demo.h>
+#include <asm/io.h>
+
+DECLARE_GLOBAL_DATA_PTR;
+
+/* Shape size */
+#define WIDTH	8
+#define HEIGHT	6
+
+struct shape_data {
+	int num_chars;	/* Number of non-space characters output so far */
+};
+
+/* Crazy little function to draw shapes on the console */
+static int shape_hello(struct device *dev, int ch)
+{
+	const struct dm_demo_pdata *pdata = dev->platform_data;
+	struct shape_data *data = dev->priv;
+	static const struct shape {
+		int start;
+		int end;
+		int dstart;
+		int dend;
+	} shapes[3] = {
+		{ 0, 1, 0, 1 },
+		{ 0, WIDTH, 0, 0 },
+		{ HEIGHT / 2 - 1, WIDTH - HEIGHT / 2 + 1, -1, 1},
+	};
+	struct shape shape;
+	unsigned int index;
+	int line, pos, inside;
+	const char *colour = pdata->colour;
+	int first = 0;
+
+	if (!ch)
+		ch = pdata->default_char;
+	if (!ch)
+		ch = '@';
+
+	index = (pdata->sides / 2) - 1;
+	if (index >= ARRAY_SIZE(shapes))
+		return -EIO;
+	shape = shapes[index];
+
+	for (line = 0; line < HEIGHT; line++) {
+		first = 1;
+		for (pos = 0; pos < WIDTH; pos++) {
+			inside = pos >= shape.start && pos < shape.end;
+			if (inside) {
+				putc(first ? *colour++ : ch);
+				data->num_chars++;
+				first = 0;
+				if (!*colour)
+					colour = pdata->colour;
+			} else {
+				putc(' ');
+			}
+		}
+		putc('\n');
+		shape.start += shape.dstart;
+		shape.end += shape.dend;
+		if (shape.start < 0) {
+			shape.dstart = -shape.dstart;
+			shape.dend = -shape.dend;
+			shape.start += shape.dstart;
+			shape.end += shape.dend;
+		}
+	}
+
+	return 0;
+}
+
+static int shape_status(struct device *dev, int *status)
+{
+	struct shape_data *data = dev->priv;
+
+	*status = data->num_chars;
+	return 0;
+}
+
+static const struct demo_ops simple_ops = {
+	.hello = shape_hello,
+	.status = shape_status,
+};
+
+static int demo_shape_probe(struct device *dev)
+{
+	struct dm_demo_pdata *pdata = dev->platform_data;
+	int ret;
+
+	if (dev->of_offset < 0)
+		return 0;
+
+	/* Parse the data that is common with all demo devices */
+	ret = demo_parse_dt(dev);
+	if (ret)
+		return ret;
+
+	/* Parse the data that only we need */
+	pdata->default_char = fdtdec_get_int(gd->fdt_blob, dev->of_offset,
+					     "character", '@');
+
+	return 0;
+}
+
+static const struct device_id demo_shape_id[] = {
+	{ "demo-shape", 0 },
+	{ },
+};
+
+U_BOOT_DRIVER(demo_shape_drv) = {
+	.name	= "demo_shape_drv",
+	.of_match = demo_shape_id,
+	.id	= UCLASS_DEMO,
+	.probe	= demo_shape_probe,
+	.ops	= &simple_ops,
+	.priv_auto_alloc_size = sizeof(struct shape_data),
+	.platform_data_auto_alloc_size = sizeof(struct dm_demo_pdata),
+};
diff --git a/drivers/demo/demo-simple.c b/drivers/demo/demo-simple.c
new file mode 100644
index 0000000..2578f10
--- /dev/null
+++ b/drivers/demo/demo-simple.c
@@ -0,0 +1,61 @@
+/*
+ * Copyright (c) 2013 Google, Inc
+ *
+ * (C) Copyright 2012
+ * Pavel Herrmann <morpheus.ibis@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
+ * MA 02111-1307 USA
+ */
+
+#include <common.h>
+#include <dm.h>
+#include <dm-demo.h>
+#include <asm/io.h>
+
+static int simple_hello(struct device *dev, int ch)
+{
+	const struct dm_demo_pdata *pdata = dev->platform_data;
+
+	printf("Hello from %08x: %s %d\n", map_to_sysmem(dev), pdata->colour,
+	       pdata->sides);
+
+	return 0;
+}
+
+static const struct demo_ops simple_ops = {
+	.hello = simple_hello,
+};
+
+static int demo_shape_probe(struct device *dev)
+{
+	/* Parse the data that is common with all demo devices */
+	return demo_parse_dt(dev);
+}
+
+static const struct device_id demo_shape_id[] = {
+	{ "demo-simple", 0 },
+	{ },
+};
+
+U_BOOT_DRIVER(demo_simple_drv) = {
+	.name	= "demo_simple_drv",
+	.of_match = demo_shape_id,
+	.id	= UCLASS_DEMO,
+	.probe	= demo_shape_probe,
+	.ops	= &simple_ops,
+	.ops	= &simple_ops,
+	.platform_data_auto_alloc_size = sizeof(struct dm_demo_pdata),
+};
diff --git a/drivers/demo/demo-uclass.c b/drivers/demo/demo-uclass.c
new file mode 100644
index 0000000..a7eab32
--- /dev/null
+++ b/drivers/demo/demo-uclass.c
@@ -0,0 +1,73 @@
+/*
+ * Copyright (c) 2013 Google, Inc
+ *
+ * (C) Copyright 2012
+ * Pavel Herrmann <morpheus.ibis@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
+ * MA 02111-1307 USA
+ */
+
+#include <common.h>
+#include <dm.h>
+#include <dm-demo.h>
+#include <errno.h>
+#include <fdtdec.h>
+#include <malloc.h>
+#include <asm/io.h>
+#include <linux/list.h>
+
+DECLARE_GLOBAL_DATA_PTR;
+
+UCLASS_DRIVER(demo) = {
+	.id		= UCLASS_DEMO,
+};
+
+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);
+}
+
+int demo_status(struct device *dev, int *status)
+{
+	const struct demo_ops *ops = device_get_ops(dev);
+
+	if (!ops->status)
+		return -ENOSYS;
+
+	return ops->status(dev, status);
+}
+
+int demo_parse_dt(struct device *dev)
+{
+	struct dm_demo_pdata *pdata = dev->platform_data;
+	int dn = dev->of_offset;
+
+	if (dev->of_offset < 0)
+		return 0;
+	pdata->sides = fdtdec_get_int(gd->fdt_blob, dn, "sides", 0);
+	pdata->colour = fdt_getprop(gd->fdt_blob, dn, "colour", NULL);
+	if (!pdata->sides || !pdata->colour) {
+		debug("%s: Invalid device tree data\n", __func__);
+		return -EINVAL;
+	}
+
+	return 0;
+}
diff --git a/include/configs/sandbox.h b/include/configs/sandbox.h
index 8e9a661..4537f36 100644
--- a/include/configs/sandbox.h
+++ b/include/configs/sandbox.h
@@ -23,7 +23,11 @@
 #define __CONFIG_H
 
 #define CONFIG_DM
+#define CONFIG_CMD_DEMO
 #define CONFIG_CMD_DM
+#define CONFIG_DM_DEMO
+#define CONFIG_DM_DEMO_SIMPLE
+#define CONFIG_DM_DEMO_SHAPE
 #define CONFIG_DM_TEST
 
 /* Number of bits in a C 'long' on this architecture */
diff --git a/include/dm-demo.h b/include/dm-demo.h
new file mode 100644
index 0000000..1041d29
--- /dev/null
+++ b/include/dm-demo.h
@@ -0,0 +1,49 @@
+/*
+ * Copyright (c) 2013 Google, Inc
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
+ * MA 02111-1307 USA
+ */
+
+#ifndef __DM_DEMO_H
+#define __DM_DEMO_H
+
+#include <dm.h>
+
+/**
+ * struct dm_demo_pdata - configuration data for demo instance
+ *
+ * @colour: Color of the demo
+ * @sides: Numbers of sides
+ * @default_char: Default ASCII character to output (65 = 'A')
+ */
+struct dm_demo_pdata {
+	const char *colour;
+	int sides;
+	int default_char;
+};
+
+struct demo_ops {
+	int (*hello)(struct device *dev, int ch);
+	int (*status)(struct device *dev, int *status);
+};
+
+int demo_hello(struct device *dev, int ch);
+int demo_status(struct device *dev, int *status);
+int demo_list(void);
+
+int demo_parse_dt(struct device *dev);
+
+#endif
-- 
1.8.3

^ permalink raw reply related	[flat|nested] 30+ messages in thread

* [U-Boot] [PATCH v3 12/16] dm: Add GPIO support and tests
  2013-06-19  3:52 [U-Boot] [PATCH v3 0/16] Driver model implementation, tests, demo and GPIO Simon Glass
                   ` (10 preceding siblings ...)
  2013-06-19  3:52 ` [U-Boot] [PATCH v3 11/16] dm: Add a demonstration/example driver Simon Glass
@ 2013-06-19  3:52 ` Simon Glass
  2013-06-28 21:51   ` Marek Vasut
  2013-06-19  3:52 ` [U-Boot] [PATCH v3 13/16] sandbox: Convert GPIOs to use driver model Simon Glass
                   ` (3 subsequent siblings)
  15 siblings, 1 reply; 30+ messages in thread
From: Simon Glass @ 2013-06-19  3:52 UTC (permalink / raw)
  To: u-boot

Add driver model support for GPIOs. Since existing GPIO drivers do not use
driver model, this feature must be enabled by CONFIG_DM_GPIO. After all
GPO drivers are converted over we can perhaps remove this config.

Tests are provided for the sandbox implementation, and are a sufficient
sanity check for basic operation.

The GPIO uclass understands the concept of named banks of GPIOs, with each
GPIO device providing a single bank. Within each bank the GPIOs are numbered
using an offset from 0 to n-1. For example a bank named 'b' with 20
offsets will provide GPIOs named b0 to b19.

Anonymous GPIO banks are also supported, and are just numbered without any
prefix.

Each time a GPIO driver is added to the uclass, the GPIOs are renumbered
accordinging, so there is always a global GPIO numbering order.

Signed-off-by: Simon Glass <sjg@chromium.org>
Signed-off-by: Marek Vasut <marex@denx.de>
Signed-off-by: Pavel Herrmann <morpheus.ibis@gmail.com>
Signed-off-by: Viktor K?iv?k <viktor.krivak@gmail.com>
Signed-off-by: Tomas Hlavacek <tmshlvck@gmail.com>
---
Changes in v3:
- Update GPIO support to use new struct member names
- Tidy up comments/documentation in GPIO module

Changes in v2: None

 drivers/gpio/Makefile      |   2 +
 drivers/gpio/gpio-uclass.c | 279 +++++++++++++++++++++++++++++++++++++++++++++
 include/asm-generic/gpio.h | 104 +++++++++++++++++
 test/dm/gpio.c             | 124 ++++++++++++++++++++
 4 files changed, 509 insertions(+)
 create mode 100644 drivers/gpio/gpio-uclass.c
 create mode 100644 test/dm/gpio.c

diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile
index f77c1ec..87c8591 100644
--- a/drivers/gpio/Makefile
+++ b/drivers/gpio/Makefile
@@ -25,6 +25,8 @@ include $(TOPDIR)/config.mk
 
 LIB 	:= $(obj)libgpio.o
 
+COBJS-$(CONFIG_DM_GPIO)		+= gpio-uclass.o
+
 COBJS-$(CONFIG_AT91_GPIO)	+= at91_gpio.o
 COBJS-$(CONFIG_INTEL_ICH6_GPIO)	+= intel_ich6_gpio.o
 COBJS-$(CONFIG_KIRKWOOD_GPIO)	+= kw_gpio.o
diff --git a/drivers/gpio/gpio-uclass.c b/drivers/gpio/gpio-uclass.c
new file mode 100644
index 0000000..de32e20
--- /dev/null
+++ b/drivers/gpio/gpio-uclass.c
@@ -0,0 +1,279 @@
+/*
+ * Copyright (c) 2013 Google, Inc
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
+ * MA 02111-1307 USA
+ */
+
+#include <common.h>
+#include <dm.h>
+#include <errno.h>
+#include <asm/gpio.h>
+
+/**
+ * gpio_to_device() - Convert global GPIO number to device, number
+ * gpio:	The numeric representation of the GPIO
+ *
+ * Convert the GPIO number to an entry in the list of GPIOs
+ * or GPIO blocks registered with the GPIO controller. Returns
+ * entry on success, NULL on error.
+ */
+static int gpio_to_device(unsigned int gpio, struct device **devp,
+			  unsigned int *offset)
+{
+	struct gpio_dev_priv *uc_priv;
+	struct device *dev;
+	int ret;
+
+	for (ret = uclass_first_device(UCLASS_GPIO, &dev);
+	     dev;
+	     ret = uclass_next_device(&dev)) {
+		uc_priv = dev->uclass_priv;
+		if (gpio >= uc_priv->gpio_base &&
+		    gpio < uc_priv->gpio_base + uc_priv->gpio_count) {
+			*devp = dev;
+			*offset = gpio - uc_priv->gpio_base;
+			return 0;
+		}
+	}
+
+	/* No such GPIO */
+	return ret ? ret : -EINVAL;
+}
+
+int gpio_lookup_name(const char *name, struct device **devp,
+		     unsigned int *offsetp, unsigned int *gpiop)
+{
+	struct gpio_dev_priv *uc_priv;
+	struct device *dev;
+	int ret;
+
+	if (devp)
+		*devp = NULL;
+	for (ret = uclass_first_device(UCLASS_GPIO, &dev);
+	     dev;
+	     ret = uclass_next_device(&dev)) {
+		ulong offset;
+		int len;
+
+		uc_priv = dev->uclass_priv;
+		len = uc_priv->bank_name ? strlen(uc_priv->bank_name) : 0;
+
+		if (!strncmp(name, uc_priv->bank_name, len)) {
+			if (strict_strtoul(name + len, 10, &offset))
+				continue;
+			if (devp)
+				*devp = dev;
+			if (offsetp)
+				*offsetp = offset;
+			if (gpiop)
+				*gpiop = uc_priv->gpio_base + offset;
+			return 0;
+		}
+	}
+
+	return ret ? ret : -EINVAL;
+}
+
+/**
+ * gpio_request() - [COMPAT] Request GPIO
+ * gpio:	GPIO number
+ * label:	Name for the requested GPIO
+ *
+ * This function implements the API that's compatible with current
+ * GPIO API used in U-Boot. The request is forwarded to particular
+ * GPIO driver. Returns 0 on success, negative value on error.
+ */
+int gpio_request(unsigned gpio, const char *label)
+{
+	unsigned int offset;
+	struct device *dev;
+	int ret;
+
+	ret = gpio_to_device(gpio, &dev, &offset);
+	if (ret)
+		return ret;
+
+	if (!gpio_get_ops(dev)->request)
+		return 0;
+
+	return gpio_get_ops(dev)->request(dev, offset, label);
+}
+
+/**
+ * gpio_free() - [COMPAT] Relinquish GPIO
+ * gpio:	GPIO number
+ *
+ * This function implements the API that's compatible with current
+ * GPIO API used in U-Boot. The request is forwarded to particular
+ * GPIO driver. Returns 0 on success, negative value on error.
+ */
+int gpio_free(unsigned gpio)
+{
+	unsigned int offset;
+	struct device *dev;
+	int ret;
+
+	ret = gpio_to_device(gpio, &dev, &offset);
+	if (ret)
+		return ret;
+
+	if (!gpio_get_ops(dev)->free)
+		return 0;
+	return gpio_get_ops(dev)->free(dev, offset);
+}
+
+/**
+ * gpio_direction_input() - [COMPAT] Set GPIO direction to input
+ * gpio:	GPIO number
+ *
+ * This function implements the API that's compatible with current
+ * GPIO API used in U-Boot. The request is forwarded to particular
+ * GPIO driver. Returns 0 on success, negative value on error.
+ */
+int gpio_direction_input(unsigned gpio)
+{
+	unsigned int offset;
+	struct device *dev;
+	int ret;
+
+	ret = gpio_to_device(gpio, &dev, &offset);
+	if (ret)
+		return ret;
+
+	return gpio_get_ops(dev)->direction_input(dev, offset);
+}
+
+/**
+ * gpio_direction_output() - [COMPAT] Set GPIO direction to output and set value
+ * gpio:	GPIO number
+ * value:	Logical value to be set on the GPIO pin
+ *
+ * This function implements the API that's compatible with current
+ * GPIO API used in U-Boot. The request is forwarded to particular
+ * GPIO driver. Returns 0 on success, negative value on error.
+ */
+int gpio_direction_output(unsigned gpio, int value)
+{
+	unsigned int offset;
+	struct device *dev;
+	int ret;
+
+	ret = gpio_to_device(gpio, &dev, &offset);
+	if (ret)
+		return ret;
+
+	return gpio_get_ops(dev)->direction_output(dev, offset, value);
+}
+
+/**
+ * gpio_get_value() - [COMPAT] Sample GPIO pin and return it's value
+ * gpio:	GPIO number
+ *
+ * This function implements the API that's compatible with current
+ * GPIO API used in U-Boot. The request is forwarded to particular
+ * GPIO driver. Returns the value of the GPIO pin, or negative value
+ * on error.
+ */
+int gpio_get_value(unsigned gpio)
+{
+	unsigned int offset;
+	struct device *dev;
+	int ret;
+
+	ret = gpio_to_device(gpio, &dev, &offset);
+	if (ret)
+		return ret;
+
+	return gpio_get_ops(dev)->get_value(dev, offset);
+}
+
+/**
+ * gpio_set_value() - [COMPAT] Configure logical value on GPIO pin
+ * gpio:	GPIO number
+ * value:	Logical value to be set on the GPIO pin.
+ *
+ * This function implements the API that's compatible with current
+ * GPIO API used in U-Boot. The request is forwarded to particular
+ * GPIO driver. Returns 0 on success, negative value on error.
+ */
+int gpio_set_value(unsigned gpio, int value)
+{
+	unsigned int offset;
+	struct device *dev;
+	int ret;
+
+	ret = gpio_to_device(gpio, &dev, &offset);
+	if (ret)
+		return ret;
+
+	return gpio_get_ops(dev)->set_value(dev, offset, value);
+}
+
+const char *gpio_get_bank_info(struct device *dev, int *bit_count)
+{
+	struct gpio_dev_priv *priv;
+
+	/* Must be called on an active device */
+	priv = dev->uclass_priv;
+	assert(priv);
+
+	*bit_count = priv->gpio_count;
+	return priv->bank_name;
+}
+
+/* We need to renumber the GPIOs when any driver is probed/removed */
+static int gpio_renumber(void)
+{
+	struct gpio_dev_priv *uc_priv;
+	struct device *dev;
+	struct uclass *uc;
+	unsigned base;
+	int ret;
+
+	ret = uclass_get(UCLASS_GPIO, &uc);
+	if (ret)
+		return ret;
+
+	/* Ensure that we have a base for each bank */
+	base = 0;
+	uclass_foreach_dev(dev, uc) {
+		if (device_active(dev)) {
+			uc_priv = dev->uclass_priv;
+			uc_priv->gpio_base = base;
+			base += uc_priv->gpio_count;
+		}
+	}
+
+	return 0;
+}
+
+static int gpio_post_probe(struct device *dev)
+{
+	return gpio_renumber();
+}
+
+static int gpio_pre_remove(struct device *dev)
+{
+	return gpio_renumber();
+}
+
+UCLASS_DRIVER(gpio) = {
+	.id		= UCLASS_GPIO,
+	.name		= "gpio",
+	.post_probe	= gpio_post_probe,
+	.pre_remove	= gpio_pre_remove,
+	.per_device_auto_alloc_size = sizeof(struct gpio_dev_priv),
+};
diff --git a/include/asm-generic/gpio.h b/include/asm-generic/gpio.h
index bfedbe4..be7c840 100644
--- a/include/asm-generic/gpio.h
+++ b/include/asm-generic/gpio.h
@@ -94,4 +94,108 @@ int gpio_get_value(unsigned gpio);
  * @return 0 if ok, -1 on error
  */
 int gpio_set_value(unsigned gpio, int value);
+
+/* State of a GPIO, as reported by get_state() */
+enum {
+	GPIOF_INPUT = 0,
+	GPIOF_OUTPUT,
+	GPIOF_UNKNOWN,
+};
+
+struct device;
+
+/**
+ * struct struct dm_gpio_ops - Driver model GPIO operations
+ *
+ * Refer to functions above for description. These function largely copy
+ * the old API.
+ *
+ * This is trying to be close to Linux GPIO API. Once the U-Boot uses the
+ * new DM GPIO API, this should be really easy to flip over to the Linux
+ * GPIO API-alike interface.
+ *
+ * Akso it would be useful to standardise additional functions like
+ * pullup, slew rate and drive strength.
+ *
+ * gpio_request)( and gpio_free() are optional - if NULL then they will
+ * not be called.
+ *
+ * Note that @offset is the offset from the base GPIO of the device. So
+ * offset 0 is the device's first GPIO and offset o-1 is the last GPIO,
+ * where o is the number of GPIO lines controlled by the device. A device
+ * is typically used to control a single bank of GPIOs. Within complex
+ * SoCs there may be many banks and therefore many devices all referring
+ * to the different IO addresses within the SoC.
+ *
+ * The uclass combines all GPIO devices togther to provide a consistent
+ * numbering from 0 to n-1, where n is the number of GPIOs in total across
+ * all devices. Be careful not to confuse offset with gpio in the parameters.
+ */
+struct dm_gpio_ops {
+	int (*request)(struct device *dev, unsigned offset, const char *label);
+	int (*free)(struct device *dev, unsigned offset);
+	int (*direction_input)(struct device *dev, unsigned offset);
+	int (*direction_output)(struct device *dev, unsigned offset,
+				int value);
+	int (*get_value)(struct device *dev, unsigned offset);
+	int (*set_value)(struct device *dev, unsigned offset, int value);
+	int (*get_function)(struct device *dev, unsigned offset);
+	int (*get_state)(struct device *dev, unsigned offset, char *state,
+			 int maxlen);
+};
+
+/**
+ * struct gpio_dev_priv - information about a device used by the uclass
+ *
+ * The uclass combines all active GPIO devices into a unified numbering
+ * scheme. To do this it maintains some private information aobut each
+ * device.
+ *
+ * To implement driver model support in your GPIO driver, add a probe
+ * handler, and set @gpio_count and @bank_name correctly in that handler.
+ * This tells the uclass the name of the GPIO bank and the number of GPIOs
+ * it contains.
+ *
+ * @bank_name: Name of the GPIO device (e.g 'a' means GPIOs will be called
+ * 'A0', 'A1', etc.
+ * @gpio_count: Number of GPIOs in this device
+ * @gpio_base: Base GPIO number for this device. For the first active device
+ * this will be 0; the numbering for others will follow sequentially so that
+ * @gpio_base for device 1 will equal the number of GPIOs in device 0.
+ */
+struct gpio_dev_priv {
+	const char *bank_name;
+	unsigned gpio_count;
+	unsigned gpio_base;
+};
+
+/* Access the GPIO operations for a device */
+#define gpio_get_ops(dev)	((struct dm_gpio_ops *)(dev)->driver->ops)
+
+/**
+ * gpio_get_bank_info - Return information about a GPIO bank/device
+ *
+ * This looks up a device and returns both its GPIO base name and the number
+ * of GPIOs it controls.
+ *
+ * @dev: Device to look up
+ * @offset_count: Returns number of GPIOs within this bank
+ * @return bank name of this device
+ */
+const char *gpio_get_bank_info(struct device *dev, int *offset_count);
+
+/**
+ * gpio_lookup_name - Look up a GPIO name and return its details
+ *
+ * This is used to convert a named GPIO into a device, offset and GPIO
+ * number.
+ *
+ * @name: GPIO name to look up
+ * @devp: Returns pointer to device which contains this GPIO
+ * @offsetp: Returns the offset number within this device
+ * @gpiop: Returns the absolute GPIO number, numbered from 0
+ */
+int gpio_lookup_name(const char *name, struct device **devp,
+		     unsigned int *offsetp, unsigned int *gpiop);
+
 #endif	/* _ASM_GENERIC_GPIO_H_ */
diff --git a/test/dm/gpio.c b/test/dm/gpio.c
new file mode 100644
index 0000000..703970f
--- /dev/null
+++ b/test/dm/gpio.c
@@ -0,0 +1,124 @@
+/*
+ * Copyright (C) 2013 Google, Inc
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
+ * MA 02111-1307 USA
+ */
+
+#include <common.h>
+#include <fdtdec.h>
+#include <dm.h>
+#include <dm/ut.h>
+#include <dm/test.h>
+#include <dm/util.h>
+#include <asm/gpio.h>
+
+/* Test that sandbox GPIOs work correctly */
+static int dm_test_gpio(struct dm_test_state *dms)
+{
+	unsigned int offset, gpio;
+	struct dm_gpio_ops *ops;
+	struct device *dev;
+	const char *name;
+	int offset_count;
+	char buf[80];
+
+	/*
+	 * We expect to get 3 banks. One is anonymous (just numbered) and
+	 * comes from platform_data. The other two are named a (20 gpios)
+	 * and b (10 gpios) and come from the device tree. See
+	 * test/dm/test.dts.
+	 */
+	ut_assertok(gpio_lookup_name("b4", &dev, &offset, &gpio));
+	ut_asserteq_str(dev->name, "extra-gpios");
+	ut_asserteq(4, offset);
+	ut_asserteq(CONFIG_SANDBOX_GPIO_COUNT + 20 + 4, gpio);
+
+	name = gpio_get_bank_info(dev, &offset_count);
+	ut_asserteq_str("b", name);
+	ut_asserteq(10, offset_count);
+
+	/* Get the operations for this device */
+	ops = gpio_get_ops(dev);
+	ut_assert(ops->get_state);
+
+	/* Cannot get a value until it is reserved */
+	ut_asserteq(-1, ops->get_value(dev, offset));
+
+	/*
+	 * Now some tests that use the 'sandbox' back door. All GPIOs
+	 * should default to input, include b4 that we are using here.
+	 */
+	ut_assertok(ops->get_state(dev, offset, buf, sizeof(buf)));
+	ut_asserteq_str("b4:  in: 0 [ ]", buf);
+
+	/* Change it to an output */
+	sandbox_gpio_set_direction(dev, offset, 1);
+	ut_assertok(ops->get_state(dev, offset, buf, sizeof(buf)));
+	ut_asserteq_str("b4: out: 0 [ ]", buf);
+
+	sandbox_gpio_set_value(dev, offset, 1);
+	ut_assertok(ops->get_state(dev, offset, buf, sizeof(buf)));
+	ut_asserteq_str("b4: out: 1 [ ]", buf);
+
+	ut_assertok(ops->request(dev, offset, "testing"));
+	ut_assertok(ops->get_state(dev, offset, buf, sizeof(buf)));
+	ut_asserteq_str("b4: out: 1 [x] testing", buf);
+
+	/* Change the value a bit */
+	ut_asserteq(1, ops->get_value(dev, offset));
+	ut_assertok(ops->set_value(dev, offset, 0));
+	ut_asserteq(0, ops->get_value(dev, offset));
+	ut_assertok(ops->get_state(dev, offset, buf, sizeof(buf)));
+	ut_asserteq_str("b4: out: 0 [x] testing", buf);
+	ut_assertok(ops->set_value(dev, offset, 1));
+	ut_asserteq(1, ops->get_value(dev, offset));
+
+	/* Make it an input */
+	ut_assertok(ops->direction_input(dev, offset));
+	ut_assertok(ops->get_state(dev, offset, buf, sizeof(buf)));
+	ut_asserteq_str("b4:  in: 1 [x] testing", buf);
+	sandbox_gpio_set_value(dev, offset, 0);
+	ut_asserteq(0, sandbox_gpio_get_value(dev, offset));
+	ut_assertok(ops->get_state(dev, offset, buf, sizeof(buf)));
+	ut_asserteq_str("b4:  in: 0 [x] testing", buf);
+
+	ut_assertok(ops->free(dev, offset));
+	ut_assertok(ops->get_state(dev, offset, buf, sizeof(buf)));
+	ut_asserteq_str("b4:  in: 0 [ ]", buf);
+
+	/* Check the 'a' bank also */
+	ut_assertok(gpio_lookup_name("a15", &dev, &offset, &gpio));
+	ut_asserteq_str(dev->name, "base-gpios");
+	ut_asserteq(15, offset);
+	ut_asserteq(CONFIG_SANDBOX_GPIO_COUNT + 15, gpio);
+
+	name = gpio_get_bank_info(dev, &offset_count);
+	ut_asserteq_str("a", name);
+	ut_asserteq(20, offset_count);
+
+	/* And the anonymous bank */
+	ut_assertok(gpio_lookup_name("14", &dev, &offset, &gpio));
+	ut_asserteq_str(dev->name, "gpio_sandbox");
+	ut_asserteq(14, offset);
+	ut_asserteq(14, gpio);
+
+	name = gpio_get_bank_info(dev, &offset_count);
+	ut_asserteq_ptr(NULL, name);
+	ut_asserteq(CONFIG_SANDBOX_GPIO_COUNT, offset_count);
+
+	return 0;
+}
+DM_TEST(dm_test_gpio, DM_TESTF_SCAN_PDATA | DM_TESTF_SCAN_FDT);
-- 
1.8.3

^ permalink raw reply related	[flat|nested] 30+ messages in thread

* [U-Boot] [PATCH v3 13/16] sandbox: Convert GPIOs to use driver model
  2013-06-19  3:52 [U-Boot] [PATCH v3 0/16] Driver model implementation, tests, demo and GPIO Simon Glass
                   ` (11 preceding siblings ...)
  2013-06-19  3:52 ` [U-Boot] [PATCH v3 12/16] dm: Add GPIO support and tests Simon Glass
@ 2013-06-19  3:52 ` Simon Glass
  2013-06-19  3:52 ` [U-Boot] [PATCH v3 14/16] dm: Enable gpio command to support " Simon Glass
                   ` (2 subsequent siblings)
  15 siblings, 0 replies; 30+ messages in thread
From: Simon Glass @ 2013-06-19  3:52 UTC (permalink / raw)
  To: u-boot

Convert sandbox over to use driver model GPIOs.

Signed-off-by: Simon Glass <sjg@chromium.org>
---
Changes in v3:
- Update sandbox GPIO header file comments

Changes in v2: None

 arch/sandbox/include/asm/gpio.h |  14 +--
 board/sandbox/sandbox/sandbox.c |   7 +-
 drivers/gpio/sandbox.c          | 210 +++++++++++++++++++++++++---------------
 include/configs/sandbox.h       |   1 +
 4 files changed, 144 insertions(+), 88 deletions(-)

diff --git a/arch/sandbox/include/asm/gpio.h b/arch/sandbox/include/asm/gpio.h
index 0500c53..33a9929 100644
--- a/arch/sandbox/include/asm/gpio.h
+++ b/arch/sandbox/include/asm/gpio.h
@@ -45,7 +45,7 @@
  * @param gp	GPIO number
  * @return -1 on error, 0 if GPIO is low, >0 if high
  */
-int sandbox_gpio_get_value(unsigned gp);
+int sandbox_gpio_get_value(struct device *dev, unsigned int offset);
 
 /**
  * Set the simulated value of a GPIO (used only in sandbox test code)
@@ -54,7 +54,7 @@ int sandbox_gpio_get_value(unsigned gp);
  * @param value	value to set (0 for low, non-zero for high)
  * @return -1 on error, 0 if ok
  */
-int sandbox_gpio_set_value(unsigned gp, int value);
+int sandbox_gpio_set_value(struct device *dev, unsigned int offset, int value);
 
 /**
  * Return the simulated direction of a GPIO (used only in sandbox test code)
@@ -62,7 +62,7 @@ int sandbox_gpio_set_value(unsigned gp, int value);
  * @param gp	GPIO number
  * @return -1 on error, 0 if GPIO is input, >0 if output
  */
-int sandbox_gpio_get_direction(unsigned gp);
+int sandbox_gpio_get_direction(struct device *dev, unsigned int offset);
 
 /**
  * Set the simulated direction of a GPIO (used only in sandbox test code)
@@ -71,11 +71,7 @@ int sandbox_gpio_get_direction(unsigned gp);
  * @param output 0 to set as input, 1 to set as output
  * @return -1 on error, 0 if ok
  */
-int sandbox_gpio_set_direction(unsigned gp, int output);
-
-/* Display information about each GPIO */
-void gpio_info(void);
-
-#define gpio_status()	gpio_info()
+int sandbox_gpio_set_direction(struct device *dev, unsigned int offset,
+			       int output);
 
 #endif
diff --git a/board/sandbox/sandbox/sandbox.c b/board/sandbox/sandbox/sandbox.c
index 8bdba92..4143318 100644
--- a/board/sandbox/sandbox/sandbox.c
+++ b/board/sandbox/sandbox/sandbox.c
@@ -20,7 +20,7 @@
  */
 
 #include <common.h>
-
+#include <dm.h>
 #include <os.h>
 
 /*
@@ -30,6 +30,11 @@
  */
 gd_t *gd;
 
+/* Add a simple GPIO device */
+U_BOOT_DEVICE(gpio_sandbox) = {
+	.name = "gpio_sandbox",
+};
+
 void flush_cache(unsigned long start, unsigned long size)
 {
 }
diff --git a/drivers/gpio/sandbox.c b/drivers/gpio/sandbox.c
index 19d2db0..94a9fdd 100644
--- a/drivers/gpio/sandbox.c
+++ b/drivers/gpio/sandbox.c
@@ -20,8 +20,13 @@
  */
 
 #include <common.h>
+#include <dm.h>
+#include <fdtdec.h>
+#include <malloc.h>
 #include <asm/gpio.h>
 
+DECLARE_GLOBAL_DATA_PTR;
+
 /* Flags for each GPIO */
 #define GPIOF_OUTPUT	(1 << 0)	/* Currently set as an output */
 #define GPIOF_HIGH	(1 << 1)	/* Currently set high */
@@ -32,34 +37,30 @@ struct gpio_state {
 	u8 flags;		/* flags (GPIOF_...) */
 };
 
-/*
- * State of GPIOs
- * TODO: Put this into sandbox state
- */
-static struct gpio_state state[CONFIG_SANDBOX_GPIO_COUNT];
-
 /* Access routines for GPIO state */
-static u8 *get_gpio_flags(unsigned gp)
+static u8 *get_gpio_flags(struct device *dev, unsigned offset)
 {
-	/* assert()'s could be disabled, so make sure we handle that */
-	assert(gp < ARRAY_SIZE(state));
-	if (gp >= ARRAY_SIZE(state)) {
+	struct gpio_dev_priv *uc_priv = dev->uclass_priv;
+	struct gpio_state *state = dev->priv;
+
+	if (offset >= uc_priv->gpio_count) {
 		static u8 invalid_flags;
-		printf("sandbox_gpio: error: invalid gpio %u\n", gp);
+		printf("sandbox_gpio: error: invalid gpio %u\n", offset);
 		return &invalid_flags;
 	}
 
-	return &state[gp].flags;
+	return &state[offset].flags;
 }
 
-static int get_gpio_flag(unsigned gp, int flag)
+static int get_gpio_flag(struct device *dev, unsigned offset, int flag)
 {
-	return (*get_gpio_flags(gp) & flag) != 0;
+	return (*get_gpio_flags(dev, offset) & flag) != 0;
 }
 
-static int set_gpio_flag(unsigned gp, int flag, int value)
+static int set_gpio_flag(struct device *dev, unsigned offset, int flag,
+			 int value)
 {
-	u8 *gpio = get_gpio_flags(gp);
+	u8 *gpio = get_gpio_flags(dev, offset);
 
 	if (value)
 		*gpio |= flag;
@@ -69,11 +70,12 @@ static int set_gpio_flag(unsigned gp, int flag, int value)
 	return 0;
 }
 
-static int check_reserved(unsigned gpio, const char *func)
+static int check_reserved(struct device *dev, unsigned offset,
+			  const char *func)
 {
-	if (!get_gpio_flag(gpio, GPIOF_RESERVED)) {
-		printf("sandbox_gpio: %s: error: gpio %u not reserved\n",
-			func, gpio);
+	if (!get_gpio_flag(dev, offset, GPIOF_RESERVED)) {
+		printf("sandbox_gpio: %s: error: offset %u not reserved\n",
+		       func, offset);
 		return -1;
 	}
 
@@ -84,126 +86,178 @@ static int check_reserved(unsigned gpio, const char *func)
  * Back-channel sandbox-internal-only access to GPIO state
  */
 
-int sandbox_gpio_get_value(unsigned gp)
+int sandbox_gpio_get_value(struct device *dev, unsigned offset)
 {
-	if (get_gpio_flag(gp, GPIOF_OUTPUT))
-		debug("sandbox_gpio: get_value on output gpio %u\n", gp);
-	return get_gpio_flag(gp, GPIOF_HIGH);
+	if (get_gpio_flag(dev, offset, GPIOF_OUTPUT))
+		debug("sandbox_gpio: get_value on output gpio %u\n", offset);
+	return get_gpio_flag(dev, offset, GPIOF_HIGH);
 }
 
-int sandbox_gpio_set_value(unsigned gp, int value)
+int sandbox_gpio_set_value(struct device *dev, unsigned offset, int value)
 {
-	return set_gpio_flag(gp, GPIOF_HIGH, value);
+	return set_gpio_flag(dev, offset, GPIOF_HIGH, value);
 }
 
-int sandbox_gpio_get_direction(unsigned gp)
+int sandbox_gpio_get_direction(struct device *dev, unsigned offset)
 {
-	return get_gpio_flag(gp, GPIOF_OUTPUT);
+	return get_gpio_flag(dev, offset, GPIOF_OUTPUT);
 }
 
-int sandbox_gpio_set_direction(unsigned gp, int output)
+int sandbox_gpio_set_direction(struct device *dev, unsigned offset, int output)
 {
-	return set_gpio_flag(gp, GPIOF_OUTPUT, output);
+	return set_gpio_flag(dev, offset, GPIOF_OUTPUT, output);
 }
 
 /*
  * These functions implement the public interface within U-Boot
  */
 
-/* set GPIO port 'gp' as an input */
-int gpio_direction_input(unsigned gp)
+/* set GPIO port 'offset' as an input */
+static int sb_gpio_direction_input(struct device *dev, unsigned offset)
 {
-	debug("%s: gp:%u\n", __func__, gp);
+	debug("%s: offset:%u\n", __func__, offset);
 
-	if (check_reserved(gp, __func__))
+	if (check_reserved(dev, offset, __func__))
 		return -1;
 
-	return sandbox_gpio_set_direction(gp, 0);
+	return sandbox_gpio_set_direction(dev, offset, 0);
 }
 
-/* set GPIO port 'gp' as an output, with polarity 'value' */
-int gpio_direction_output(unsigned gp, int value)
+/* set GPIO port 'offset' as an output, with polarity 'value' */
+static int sb_gpio_direction_output(struct device *dev, unsigned offset,
+				    int value)
 {
-	debug("%s: gp:%u, value = %d\n", __func__, gp, value);
+	debug("%s: offset:%u, value = %d\n", __func__, offset, value);
 
-	if (check_reserved(gp, __func__))
+	if (check_reserved(dev, offset, __func__))
 		return -1;
 
-	return sandbox_gpio_set_direction(gp, 1) |
-		sandbox_gpio_set_value(gp, value);
+	return sandbox_gpio_set_direction(dev, offset, 1) |
+		sandbox_gpio_set_value(dev, offset, value);
 }
 
-/* read GPIO IN value of port 'gp' */
-int gpio_get_value(unsigned gp)
+/* read GPIO IN value of port 'offset' */
+static int sb_gpio_get_value(struct device *dev, unsigned offset)
 {
-	debug("%s: gp:%u\n", __func__, gp);
+	debug("%s: offset:%u\n", __func__, offset);
 
-	if (check_reserved(gp, __func__))
+	if (check_reserved(dev, offset, __func__))
 		return -1;
 
-	return sandbox_gpio_get_value(gp);
+	return sandbox_gpio_get_value(dev, offset);
 }
 
-/* write GPIO OUT value to port 'gp' */
-int gpio_set_value(unsigned gp, int value)
+/* write GPIO OUT value to port 'offset' */
+static int sb_gpio_set_value(struct device *dev, unsigned offset, int value)
 {
-	debug("%s: gp:%u, value = %d\n", __func__, gp, value);
+	debug("%s: offset:%u, value = %d\n", __func__, offset, value);
 
-	if (check_reserved(gp, __func__))
+	if (check_reserved(dev, offset, __func__))
 		return -1;
 
-	if (!sandbox_gpio_get_direction(gp)) {
-		printf("sandbox_gpio: error: set_value on input gpio %u\n", gp);
+	if (!sandbox_gpio_get_direction(dev, offset)) {
+		printf("sandbox_gpio: error: set_value on input gpio %u\n",
+		       offset);
 		return -1;
 	}
 
-	return sandbox_gpio_set_value(gp, value);
+	return sandbox_gpio_set_value(dev, offset, value);
 }
 
-int gpio_request(unsigned gp, const char *label)
+static int sb_gpio_request(struct device *dev, unsigned offset,
+			   const char *label)
 {
-	debug("%s: gp:%u, label:%s\n", __func__, gp, label);
+	struct gpio_dev_priv *uc_priv = dev->uclass_priv;
+	struct gpio_state *state = dev->priv;
 
-	if (gp >= ARRAY_SIZE(state)) {
-		printf("sandbox_gpio: error: invalid gpio %u\n", gp);
+	debug("%s: offset:%u, label:%s\n", __func__, offset, label);
+
+	if (offset >= uc_priv->gpio_count) {
+		printf("sandbox_gpio: error: invalid gpio %u\n", offset);
 		return -1;
 	}
 
-	if (get_gpio_flag(gp, GPIOF_RESERVED)) {
-		printf("sandbox_gpio: error: gpio %u already reserved\n", gp);
+	if (get_gpio_flag(dev, offset, GPIOF_RESERVED)) {
+		printf("sandbox_gpio: error: gpio %u already reserved\n",
+		       offset);
 		return -1;
 	}
 
-	state[gp].label = label;
-	return set_gpio_flag(gp, GPIOF_RESERVED, 1);
+	state[offset].label = label;
+	return set_gpio_flag(dev, offset, GPIOF_RESERVED, 1);
 }
 
-int gpio_free(unsigned gp)
+static int sb_gpio_free(struct device *dev, unsigned offset)
 {
-	debug("%s: gp:%u\n", __func__, gp);
+	struct gpio_state *state = dev->priv;
+
+	debug("%s: offset:%u\n", __func__, offset);
 
-	if (check_reserved(gp, __func__))
+	if (check_reserved(dev, offset, __func__))
 		return -1;
 
-	state[gp].label = NULL;
-	return set_gpio_flag(gp, GPIOF_RESERVED, 0);
+	state[offset].label = NULL;
+	return set_gpio_flag(dev, offset, GPIOF_RESERVED, 0);
 }
 
-/* Display GPIO information */
-void gpio_info(void)
+static int sb_gpio_get_state(struct device *dev, unsigned int offset,
+			     char *buf, int bufsize)
 {
-	unsigned gpio;
+	struct gpio_dev_priv *uc_priv = dev->uclass_priv;
+	struct gpio_state *state = dev->priv;
+	const char *label;
+
+	label = state[offset].label;
+	snprintf(buf, bufsize, "%s%d: %s: %d [%c]%s%s",
+		 uc_priv->bank_name ? uc_priv->bank_name : "", offset,
+		 sandbox_gpio_get_direction(dev, offset) ? "out" : " in",
+		 sandbox_gpio_get_value(dev, offset),
+		 get_gpio_flag(dev, offset, GPIOF_RESERVED) ? 'x' : ' ',
+		 label ? " " : "",
+		 label ? label : "");
 
-	puts("Sandbox GPIOs\n");
+	return 0;
+}
 
-	for (gpio = 0; gpio < ARRAY_SIZE(state); ++gpio) {
-		const char *label = state[gpio].label;
+static const struct dm_gpio_ops gpio_sandbox_ops = {
+	.request		= sb_gpio_request,
+	.free			= sb_gpio_free,
+	.direction_input	= sb_gpio_direction_input,
+	.direction_output	= sb_gpio_direction_output,
+	.get_value		= sb_gpio_get_value,
+	.set_value		= sb_gpio_set_value,
+	.get_state		= sb_gpio_get_state,
+};
 
-		printf("%4d: %s: %d [%c] %s\n",
-			gpio,
-			sandbox_gpio_get_direction(gpio) ? "out" : " in",
-			sandbox_gpio_get_value(gpio),
-			get_gpio_flag(gpio, GPIOF_RESERVED) ? 'x' : ' ',
-			label ? label : "");
+static int gpio_sandbox_probe(struct device *dev)
+{
+	struct gpio_dev_priv *uc_priv = dev->uclass_priv;
+
+	if (dev->of_offset != -1) {
+		uc_priv->gpio_count = fdtdec_get_int(gd->fdt_blob,
+						     dev->of_offset,
+						     "num-gpios", 0);
+		uc_priv->bank_name = fdt_getprop(gd->fdt_blob, dev->of_offset,
+						 "gpio-bank-name", NULL);
+	} else {
+		/* Tell the uclass how many GPIOs we have */
+		uc_priv->gpio_count = CONFIG_SANDBOX_GPIO_COUNT;
 	}
+
+	dev->priv = calloc(sizeof(struct gpio_state), uc_priv->gpio_count);
+
+	return 0;
 }
+
+static const struct device_id sandbox_gpio_ids[] = {
+	{ .compatible = "sandbox,gpio" },
+	{ }
+};
+
+U_BOOT_DRIVER(gpio_sandbox) = {
+	.name	= "gpio_sandbox",
+	.id	= UCLASS_GPIO,
+	.of_match = sandbox_gpio_ids,
+	.probe	= gpio_sandbox_probe,
+	.ops	= &gpio_sandbox_ops,
+};
diff --git a/include/configs/sandbox.h b/include/configs/sandbox.h
index 4537f36..8f05a01 100644
--- a/include/configs/sandbox.h
+++ b/include/configs/sandbox.h
@@ -28,6 +28,7 @@
 #define CONFIG_DM_DEMO
 #define CONFIG_DM_DEMO_SIMPLE
 #define CONFIG_DM_DEMO_SHAPE
+#define CONFIG_DM_GPIO
 #define CONFIG_DM_TEST
 
 /* Number of bits in a C 'long' on this architecture */
-- 
1.8.3

^ permalink raw reply related	[flat|nested] 30+ messages in thread

* [U-Boot] [PATCH v3 14/16] dm: Enable gpio command to support driver model
  2013-06-19  3:52 [U-Boot] [PATCH v3 0/16] Driver model implementation, tests, demo and GPIO Simon Glass
                   ` (12 preceding siblings ...)
  2013-06-19  3:52 ` [U-Boot] [PATCH v3 13/16] sandbox: Convert GPIOs to use driver model Simon Glass
@ 2013-06-19  3:52 ` Simon Glass
  2013-06-19  3:52 ` [U-Boot] [PATCH v3 15/16] dm: Add README for " Simon Glass
  2013-06-19  3:52 ` [U-Boot] [PATCH v3 16/16] dm: Move old driver model documentation into an 'old-docs' directory Simon Glass
  15 siblings, 0 replies; 30+ messages in thread
From: Simon Glass @ 2013-06-19  3:52 UTC (permalink / raw)
  To: u-boot

Now that named GPIO banks are supported, along with a way of obtaining
the status of a GPIO (input or output), we can provide an enhanced
GPIO command for driver model. Where the driver provides its own operation
for obtaining the GPIO state, this is used, otherwise a generic version
is sufficient.

Signed-off-by: Simon Glass <sjg@chromium.org>
---
Changes in v3: None
Changes in v2: None

 common/cmd_gpio.c | 127 ++++++++++++++++++++++++++++++++++++++++++++++++------
 1 file changed, 114 insertions(+), 13 deletions(-)

diff --git a/common/cmd_gpio.c b/common/cmd_gpio.c
index 47eee89..c6758c1 100644
--- a/common/cmd_gpio.c
+++ b/common/cmd_gpio.c
@@ -8,7 +8,7 @@
 
 #include <common.h>
 #include <command.h>
-
+#include <dm.h>
 #include <asm/gpio.h>
 
 #ifndef name_to_gpio
@@ -22,25 +22,115 @@ enum gpio_cmd {
 	GPIO_TOGGLE,
 };
 
+#if defined(CONFIG_DM_GPIO) && !defined(gpio_status)
+static const char * const gpio_function[] = {
+	"input",
+	"output",
+	"unknown",
+};
+
+static void show_gpio(struct device *dev, const char *bank_name, int offset)
+{
+	struct dm_gpio_ops *ops = gpio_get_ops(dev);
+	char buf[80];
+	int ret;
+
+	*buf = '\0';
+	if (ops->get_state) {
+		ret = ops->get_state(dev, offset, buf, sizeof(buf));
+		if (ret) {
+			puts("<unknown>");
+			return;
+		}
+	} else {
+		int func =  GPIOF_UNKNOWN;
+		int ret;
+
+		if (ops->get_function) {
+			ret = ops->get_function(dev, offset);
+			if (ret >= 0 && ret < ARRAY_SIZE(gpio_function))
+				func = ret;
+		}
+		sprintf(buf, "%s%u: %8s %d", bank_name, offset,
+			gpio_function[func], ops->get_value(dev, offset));
+	}
+
+	puts(buf);
+	puts("\n");
+}
+
+static int do_gpio_status(const char *gpio_name)
+{
+	struct device *dev;
+	int newline = 0;
+	int ret;
+
+	if (gpio_name && !*gpio_name)
+		gpio_name = NULL;
+	for (ret = uclass_first_device(UCLASS_GPIO, &dev);
+	     dev;
+	     ret = uclass_next_device(&dev)) {
+		const char *bank_name;
+		int num_bits;
+
+		bank_name = gpio_get_bank_info(dev, &num_bits);
+
+		if (!gpio_name || !bank_name ||
+		    !strncmp(gpio_name, bank_name, strlen(bank_name))) {
+			const char *p = NULL;
+			int offset;
+
+			if (bank_name) {
+				if (newline)
+					putc('\n');
+				printf("Bank %s:\n", bank_name);
+			}
+			newline = 1;
+			if (gpio_name && bank_name) {
+				p = gpio_name + strlen(bank_name);
+				offset = simple_strtoul(p, NULL, 10);
+				show_gpio(dev, bank_name, offset);
+			} else {
+				for (offset = 0; offset < num_bits; offset++)
+					show_gpio(dev, bank_name, offset);
+			}
+		}
+	}
+
+	return ret;
+}
+#endif
+
 static int do_gpio(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
 {
-	int gpio;
+	unsigned int gpio;
 	enum gpio_cmd sub_cmd;
 	ulong value;
-	const char *str_cmd, *str_gpio;
+	const char *str_cmd, *str_gpio = NULL;
+#ifdef CONFIG_DM_GPIO
+	int ret;
+#endif
 
+	if (argc < 2)
+ show_usage:
+		return CMD_RET_USAGE;
+	str_cmd = argv[1];
+	if (argc > 2)
+		str_gpio = argv[2];
+	if (!strcmp(str_cmd, "status")) {
+		/* Support deprecated gpio_status() */
 #ifdef gpio_status
-	if (argc == 2 && !strcmp(argv[1], "status")) {
 		gpio_status();
 		return 0;
-	}
+#elif defined(CONFIG_DM_GPIO)
+		return cmd_process_error(cmdtp, do_gpio_status(str_gpio));
+#else
+		goto show_usage;
 #endif
+	}
 
-	if (argc != 3)
- show_usage:
-		return CMD_RET_USAGE;
-	str_cmd = argv[1];
-	str_gpio = argv[2];
+	if (!str_gpio)
+		goto show_usage;
 
 	/* parse the behavior */
 	switch (*str_cmd) {
@@ -51,11 +141,21 @@ static int do_gpio(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
 		default:  goto show_usage;
 	}
 
+#if defined(CONFIG_DM_GPIO)
+	/*
+	 * TODO(sjg at chromium.org): Convert this code over to use the GPIO
+	 * uclass interface instead of the numbered GPIO compatibility
+	 * layer.
+	 */
+	ret = gpio_lookup_name(str_gpio, NULL, NULL, &gpio);
+	if (ret)
+		return cmd_process_error(cmdtp, ret);
+#else
 	/* turn the gpio name into a gpio number */
 	gpio = name_to_gpio(str_gpio);
 	if (gpio < 0)
 		goto show_usage;
-
+#endif
 	/* grab the pin before we tweak it */
 	if (gpio_request(gpio, "cmd_gpio")) {
 		printf("gpio: requesting pin %u failed\n", gpio);
@@ -84,6 +184,7 @@ static int do_gpio(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
 }
 
 U_BOOT_CMD(gpio, 3, 0, do_gpio,
-	"input/set/clear/toggle gpio pins",
+	"query and control gpio pins",
 	"<input|set|clear|toggle> <pin>\n"
-	"    - input/set/clear/toggle the specified pin");
+	"    - input/set/clear/toggle the specified pin\n"
+	"gpio status [<bank> | <pin>]");
-- 
1.8.3

^ permalink raw reply related	[flat|nested] 30+ messages in thread

* [U-Boot] [PATCH v3 15/16] dm: Add README for driver model
  2013-06-19  3:52 [U-Boot] [PATCH v3 0/16] Driver model implementation, tests, demo and GPIO Simon Glass
                   ` (13 preceding siblings ...)
  2013-06-19  3:52 ` [U-Boot] [PATCH v3 14/16] dm: Enable gpio command to support " Simon Glass
@ 2013-06-19  3:52 ` Simon Glass
  2013-06-28 21:26   ` Marek Vasut
  2013-06-19  3:52 ` [U-Boot] [PATCH v3 16/16] dm: Move old driver model documentation into an 'old-docs' directory Simon Glass
  15 siblings, 1 reply; 30+ messages in thread
From: Simon Glass @ 2013-06-19  3:52 UTC (permalink / raw)
  To: u-boot

From: Pavel Herrmann <morpheus.ibis@gmail.com>

This adds a README to help with understanding of this series.

Signed-off-by: Simon Glass <sjg@chromium.org>
---
Changes in v3:
- Updated README.txt to cover changes since version 2

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 | 320 ++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 320 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..02106d6
--- /dev/null
+++ b/doc/driver-model/README.txt
@@ -0,0 +1,320 @@
+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 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 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 from %08x: %s %d\n", 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 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->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
+-----------
+
+While platform_data 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 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_auto_alloc_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, typically using a ..._parse_dt() function.
+
+
+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 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
+
+
+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.
+
+
+Simon Glass
+sjg at chromium.org
+April 2013
+Updated 7-May-13
+Updated 14-Jun-13
-- 
1.8.3

^ permalink raw reply related	[flat|nested] 30+ messages in thread

* [U-Boot] [PATCH v3 16/16] dm: Move old driver model documentation into an 'old-docs' directory
  2013-06-19  3:52 [U-Boot] [PATCH v3 0/16] Driver model implementation, tests, demo and GPIO Simon Glass
                   ` (14 preceding siblings ...)
  2013-06-19  3:52 ` [U-Boot] [PATCH v3 15/16] dm: Add README for " Simon Glass
@ 2013-06-19  3:52 ` Simon Glass
  15 siblings, 0 replies; 30+ messages in thread
From: Simon Glass @ 2013-06-19  3:52 UTC (permalink / raw)
  To: u-boot

This documentation is still useful but is not fully correct with the API
changes since the original driver model implementation. So move it into
a separate directory, and create a README to describe what is going on.

This documentation pertains to the planned implementation of driver model
in U-Boot for each subsystem. It is probably better to have this
documentation in the source code for each subsystem where possible, so
that docbook will pick it up. Where this does not make sense, new
documentation can be placed in some suitable file in doc/driver-model.

Signed-off-by: Simon Glass <sjg@chromium.org>
---
Changes in v3:
- Add new patch to move driver model documentation

Changes in v2: None

 doc/driver-model/old-docs/README                 | 29 ++++++++++++++++++++++++
 doc/driver-model/{ => old-docs}/UDM-block.txt    |  0
 doc/driver-model/{ => old-docs}/UDM-cores.txt    |  0
 doc/driver-model/{ => old-docs}/UDM-design.txt   |  0
 doc/driver-model/{ => old-docs}/UDM-fpga.txt     |  0
 doc/driver-model/{ => old-docs}/UDM-gpio.txt     |  0
 doc/driver-model/{ => old-docs}/UDM-hwmon.txt    |  0
 doc/driver-model/{ => old-docs}/UDM-keyboard.txt |  0
 doc/driver-model/{ => old-docs}/UDM-mmc.txt      |  0
 doc/driver-model/{ => old-docs}/UDM-net.txt      |  0
 doc/driver-model/{ => old-docs}/UDM-pci.txt      |  0
 doc/driver-model/{ => old-docs}/UDM-pcmcia.txt   |  0
 doc/driver-model/{ => old-docs}/UDM-power.txt    |  0
 doc/driver-model/{ => old-docs}/UDM-rtc.txt      |  0
 doc/driver-model/{ => old-docs}/UDM-serial.txt   |  0
 doc/driver-model/{ => old-docs}/UDM-spi.txt      |  0
 doc/driver-model/{ => old-docs}/UDM-stdio.txt    |  0
 doc/driver-model/{ => old-docs}/UDM-tpm.txt      |  0
 doc/driver-model/{ => old-docs}/UDM-twserial.txt |  0
 doc/driver-model/{ => old-docs}/UDM-usb.txt      |  0
 doc/driver-model/{ => old-docs}/UDM-video.txt    |  0
 doc/driver-model/{ => old-docs}/UDM-watchdog.txt |  0
 22 files changed, 29 insertions(+)
 create mode 100644 doc/driver-model/old-docs/README
 rename doc/driver-model/{ => old-docs}/UDM-block.txt (100%)
 rename doc/driver-model/{ => old-docs}/UDM-cores.txt (100%)
 rename doc/driver-model/{ => old-docs}/UDM-design.txt (100%)
 rename doc/driver-model/{ => old-docs}/UDM-fpga.txt (100%)
 rename doc/driver-model/{ => old-docs}/UDM-gpio.txt (100%)
 rename doc/driver-model/{ => old-docs}/UDM-hwmon.txt (100%)
 rename doc/driver-model/{ => old-docs}/UDM-keyboard.txt (100%)
 rename doc/driver-model/{ => old-docs}/UDM-mmc.txt (100%)
 rename doc/driver-model/{ => old-docs}/UDM-net.txt (100%)
 rename doc/driver-model/{ => old-docs}/UDM-pci.txt (100%)
 rename doc/driver-model/{ => old-docs}/UDM-pcmcia.txt (100%)
 rename doc/driver-model/{ => old-docs}/UDM-power.txt (100%)
 rename doc/driver-model/{ => old-docs}/UDM-rtc.txt (100%)
 rename doc/driver-model/{ => old-docs}/UDM-serial.txt (100%)
 rename doc/driver-model/{ => old-docs}/UDM-spi.txt (100%)
 rename doc/driver-model/{ => old-docs}/UDM-stdio.txt (100%)
 rename doc/driver-model/{ => old-docs}/UDM-tpm.txt (100%)
 rename doc/driver-model/{ => old-docs}/UDM-twserial.txt (100%)
 rename doc/driver-model/{ => old-docs}/UDM-usb.txt (100%)
 rename doc/driver-model/{ => old-docs}/UDM-video.txt (100%)
 rename doc/driver-model/{ => old-docs}/UDM-watchdog.txt (100%)

diff --git a/doc/driver-model/old-docs/README b/doc/driver-model/old-docs/README
new file mode 100644
index 0000000..0de03bf
--- /dev/null
+++ b/doc/driver-model/old-docs/README
@@ -0,0 +1,29 @@
+The U-Boot Driver Model Project
+===============================
+
+This directory contains the original driver model documents. The documents
+are still useful and relevant, but some of the terminology has changed,
+and some of the APIs are a little different.
+
+The changes (which are not reflected in the docs in this directory) are:
+
+- 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
diff --git a/doc/driver-model/UDM-block.txt b/doc/driver-model/old-docs/UDM-block.txt
similarity index 100%
rename from doc/driver-model/UDM-block.txt
rename to doc/driver-model/old-docs/UDM-block.txt
diff --git a/doc/driver-model/UDM-cores.txt b/doc/driver-model/old-docs/UDM-cores.txt
similarity index 100%
rename from doc/driver-model/UDM-cores.txt
rename to doc/driver-model/old-docs/UDM-cores.txt
diff --git a/doc/driver-model/UDM-design.txt b/doc/driver-model/old-docs/UDM-design.txt
similarity index 100%
rename from doc/driver-model/UDM-design.txt
rename to doc/driver-model/old-docs/UDM-design.txt
diff --git a/doc/driver-model/UDM-fpga.txt b/doc/driver-model/old-docs/UDM-fpga.txt
similarity index 100%
rename from doc/driver-model/UDM-fpga.txt
rename to doc/driver-model/old-docs/UDM-fpga.txt
diff --git a/doc/driver-model/UDM-gpio.txt b/doc/driver-model/old-docs/UDM-gpio.txt
similarity index 100%
rename from doc/driver-model/UDM-gpio.txt
rename to doc/driver-model/old-docs/UDM-gpio.txt
diff --git a/doc/driver-model/UDM-hwmon.txt b/doc/driver-model/old-docs/UDM-hwmon.txt
similarity index 100%
rename from doc/driver-model/UDM-hwmon.txt
rename to doc/driver-model/old-docs/UDM-hwmon.txt
diff --git a/doc/driver-model/UDM-keyboard.txt b/doc/driver-model/old-docs/UDM-keyboard.txt
similarity index 100%
rename from doc/driver-model/UDM-keyboard.txt
rename to doc/driver-model/old-docs/UDM-keyboard.txt
diff --git a/doc/driver-model/UDM-mmc.txt b/doc/driver-model/old-docs/UDM-mmc.txt
similarity index 100%
rename from doc/driver-model/UDM-mmc.txt
rename to doc/driver-model/old-docs/UDM-mmc.txt
diff --git a/doc/driver-model/UDM-net.txt b/doc/driver-model/old-docs/UDM-net.txt
similarity index 100%
rename from doc/driver-model/UDM-net.txt
rename to doc/driver-model/old-docs/UDM-net.txt
diff --git a/doc/driver-model/UDM-pci.txt b/doc/driver-model/old-docs/UDM-pci.txt
similarity index 100%
rename from doc/driver-model/UDM-pci.txt
rename to doc/driver-model/old-docs/UDM-pci.txt
diff --git a/doc/driver-model/UDM-pcmcia.txt b/doc/driver-model/old-docs/UDM-pcmcia.txt
similarity index 100%
rename from doc/driver-model/UDM-pcmcia.txt
rename to doc/driver-model/old-docs/UDM-pcmcia.txt
diff --git a/doc/driver-model/UDM-power.txt b/doc/driver-model/old-docs/UDM-power.txt
similarity index 100%
rename from doc/driver-model/UDM-power.txt
rename to doc/driver-model/old-docs/UDM-power.txt
diff --git a/doc/driver-model/UDM-rtc.txt b/doc/driver-model/old-docs/UDM-rtc.txt
similarity index 100%
rename from doc/driver-model/UDM-rtc.txt
rename to doc/driver-model/old-docs/UDM-rtc.txt
diff --git a/doc/driver-model/UDM-serial.txt b/doc/driver-model/old-docs/UDM-serial.txt
similarity index 100%
rename from doc/driver-model/UDM-serial.txt
rename to doc/driver-model/old-docs/UDM-serial.txt
diff --git a/doc/driver-model/UDM-spi.txt b/doc/driver-model/old-docs/UDM-spi.txt
similarity index 100%
rename from doc/driver-model/UDM-spi.txt
rename to doc/driver-model/old-docs/UDM-spi.txt
diff --git a/doc/driver-model/UDM-stdio.txt b/doc/driver-model/old-docs/UDM-stdio.txt
similarity index 100%
rename from doc/driver-model/UDM-stdio.txt
rename to doc/driver-model/old-docs/UDM-stdio.txt
diff --git a/doc/driver-model/UDM-tpm.txt b/doc/driver-model/old-docs/UDM-tpm.txt
similarity index 100%
rename from doc/driver-model/UDM-tpm.txt
rename to doc/driver-model/old-docs/UDM-tpm.txt
diff --git a/doc/driver-model/UDM-twserial.txt b/doc/driver-model/old-docs/UDM-twserial.txt
similarity index 100%
rename from doc/driver-model/UDM-twserial.txt
rename to doc/driver-model/old-docs/UDM-twserial.txt
diff --git a/doc/driver-model/UDM-usb.txt b/doc/driver-model/old-docs/UDM-usb.txt
similarity index 100%
rename from doc/driver-model/UDM-usb.txt
rename to doc/driver-model/old-docs/UDM-usb.txt
diff --git a/doc/driver-model/UDM-video.txt b/doc/driver-model/old-docs/UDM-video.txt
similarity index 100%
rename from doc/driver-model/UDM-video.txt
rename to doc/driver-model/old-docs/UDM-video.txt
diff --git a/doc/driver-model/UDM-watchdog.txt b/doc/driver-model/old-docs/UDM-watchdog.txt
similarity index 100%
rename from doc/driver-model/UDM-watchdog.txt
rename to doc/driver-model/old-docs/UDM-watchdog.txt
-- 
1.8.3

^ permalink raw reply related	[flat|nested] 30+ messages in thread

* [U-Boot] [PATCH v3 07/16] dm: Add base driver model support
  2013-06-19  3:52 ` [U-Boot] [PATCH v3 07/16] dm: Add base driver model support Simon Glass
@ 2013-06-28 20:53   ` Marek Vasut
  2013-10-18 16:44     ` Simon Glass
  0 siblings, 1 reply; 30+ messages in thread
From: Marek Vasut @ 2013-06-28 20:53 UTC (permalink / raw)
  To: u-boot

Dear Simon Glass,

> Add driver model functionality for generic board.
> 
> This includes data structures and base code for registering devices and
> uclasses (groups of devices with the same purpose, e.g. all I2C ports will
> be in the same uclass).
> 
> The feature is enabled with CONFIG_DM.
> 
> Signed-off-by: Simon Glass <sjg@chromium.org>
> Signed-off-by: Marek Vasut <marex@denx.de>
> Signed-off-by: Pavel Herrmann <morpheus.ibis@gmail.com>
> Signed-off-by: Viktor K?iv?k <viktor.krivak@gmail.com>
> Signed-off-by: Tomas Hlavacek <tmshlvck@gmail.com>
> ---
> Changes in v3:
> - Tidy up commenting of functions and structures
> - Rename per_device_priv_size to per_device_auto_alloc_size, etc.
> - Add a flag for tracking whether DM allocates/frees platform_data
> 
> Changes in v2: None
> 
>  Makefile                          |   2 +
>  common/dm/Makefile                |  43 +++++
>  common/dm/device.c                | 331
> ++++++++++++++++++++++++++++++++++++++ common/dm/lists.c                 |
> 168 +++++++++++++++++++
>  common/dm/root.c                  | 115 +++++++++++++
>  common/dm/uclass.c                | 298 ++++++++++++++++++++++++++++++++++
>  common/dm/util.c                  |  50 ++++++

Maybe we should move these into drivers/core like Linux has it ?

[...]

> +#include <common.h>
> +#include <malloc.h>
> +#include <dm/device.h>
> +#include <dm/device-internal.h>
> +#include <dm/lists.h>
> +#include <dm/platform_data.h>
> +#include <dm/uclass.h>
> +#include <dm/uclass-internal.h>
> +#include <dm/util.h>
> +#include <linux/err.h>
> +#include <linux/list.h>
> +
> +/**
> + * device_chld_unbind() - Unbind all device's children from the device
> + * @dev:	The device that is to be stripped of its children
> + * @return 0 on success, -ve on error
> + */
> +static int device_chld_unbind(struct device *dev)
> +{
> +	struct device *pos, *n;
> +	int ret;
> +
> +	assert(dev);
> +
> +	list_for_each_entry_safe(pos, n, &dev->child_head, sibling_node) {
> +		ret = device_unbind(pos);
> +		if (ret)
> +			return ret;
> +	}

This is used for stuff like destroying USB bus topology after running "usb 
rescan" for the second time, right? We might want to continue unbinding and only 
report the error instead of returning on the first error.

> +	return 0;
> +}
> +
> +/**
> + * device_chld_remove() - Stop all device's children
> + * @dev:	The device whose children are to be removed
> + * @return 0 on success, -ve on error
> + */
> +static int device_chld_remove(struct device *dev)
> +{
> +	struct device *pos, *n;
> +	int ret;
> +
> +	assert(dev);
> +
> +	list_for_each_entry_safe(pos, n, &dev->child_head, sibling_node) {
> +		ret = device_remove(pos);
> +		if (ret)
> +			return ret;
> +	}
> +
> +	return 0;
> +}

[...]

We might really want to document these.

> +int device_bind_by_name(struct device *parent, const struct driver_info
> *info, +			struct device **devp)

[...]

> +struct uclass *uclass_find(enum uclass_id key)
> +{
> +	struct uclass *uc;
> +
> +	/*
> +	 * TODO(sjg at chromium.org): Optimise this, perhaps moving the found
> +	 * node to the start of the list, or creating a linear array mapping
> +	 * id to node.
> +	 */

We better do this later, after it's all in place.

> +	list_for_each_entry(uc, &gd->uclass_root, sibling_node) {
> +		if (uc->uc_drv->id == key)
> +			return uc;
> +	}
> +
> +	return NULL;
> +}
> +
> +/**
> + * uclass_add() - Create new uclass in list
> + * @id: Id number to create
> + * @ucp: Returns pointer to uclass, or NULL on error
> + * @return 0 on success, -ve on error
> + *
> + * The new uclass is added to the list. There must be only one uclass for
> + * each id.
> + */
> +static int uclass_add(enum uclass_id id, struct uclass **ucp)
> +{

I might be a bit lost here. Do we now support adding uclass at runtime ?

Best regards,

^ permalink raw reply	[flat|nested] 30+ messages in thread

* [U-Boot] [PATCH v3 08/16] dm: Set up driver model after relocation
  2013-06-19  3:52 ` [U-Boot] [PATCH v3 08/16] dm: Set up driver model after relocation Simon Glass
@ 2013-06-28 20:53   ` Marek Vasut
  2013-10-18 16:45     ` Simon Glass
  0 siblings, 1 reply; 30+ messages in thread
From: Marek Vasut @ 2013-06-28 20:53 UTC (permalink / raw)
  To: u-boot

Dear Simon Glass,

> Make driver model available after relocation, by setting up data structures
> and scanning for devices using compiled-in platform_data and (when
> available) the device tree.
> 
> Signed-off-by: Simon Glass <sjg@chromium.org>
> ---
> Changes in v3: None
> Changes in v2: None
> 
>  common/board_r.c | 33 +++++++++++++++++++++++++++++++++
>  1 file changed, 33 insertions(+)
> 
> diff --git a/common/board_r.c b/common/board_r.c
> index f5649c9..5fe86ef 100644
> --- a/common/board_r.c
> +++ b/common/board_r.c
> @@ -34,6 +34,7 @@
>  #ifdef CONFIG_HAS_DATAFLASH
>  #include <dataflash.h>
>  #endif
> +#include <dm.h>
>  #include <environment.h>
>  #include <fdtdec.h>
>  #if defined(CONFIG_CMD_IDE)
> @@ -66,7 +67,9 @@
>  #ifdef CONFIG_X86
>  #include <asm/init_helpers.h>
>  #endif
> +#include <dm/root.h>
>  #include <linux/compiler.h>
> +#include <linux/err.h>
> 
>  DECLARE_GLOBAL_DATA_PTR;
> 
> @@ -269,6 +272,33 @@ static int initr_malloc(void)
>  	return 0;
>  }
> 
> +#ifdef CONFIG_DM
> +static int initr_dm(void)
> +{
> +	int ret;
> +
> +	ret = dm_init();
> +	if (ret) {
> +		debug("dm_init() failed: %d\n", ret);
> +		return ret;
> +	}
> +	ret = dm_scan_platform_data();
> +	if (ret) {
> +		debug("dm_scan_platform_data() failed: %d\n", ret);
> +		return ret;
> +	}
> +#ifdef CONFIG_OF_CONTROL
> +	ret = dm_scan_fdt(gd->fdt_blob);

This part is _very_ nice!

> +	if (ret) {
> +		debug("dm_scan_fdt() failed: %d\n", ret);
> +		return ret;
> +	}
> +#endif
> +
> +	return 0;
> +}
> +#endif
> +
>  __weak int power_init_board(void)
>  {
>  	return 0;
> @@ -766,6 +796,9 @@ init_fnc_t init_sequence_r[] = {
>  	initr_barrier,
>  	initr_malloc,
>  	bootstage_relocate,
> +#ifdef CONFIG_DM
> +	initr_dm,
> +#endif
>  #ifdef CONFIG_ARCH_EARLY_INIT_R
>  	arch_early_init_r,
>  #endif

Best regards,
Marek Vasut

^ permalink raw reply	[flat|nested] 30+ messages in thread

* [U-Boot] [PATCH v3 10/16] dm: Add a 'dm' command for testing
  2013-06-19  3:52 ` [U-Boot] [PATCH v3 10/16] dm: Add a 'dm' command for testing Simon Glass
@ 2013-06-28 20:57   ` Marek Vasut
  2013-10-18 17:00     ` Simon Glass
  0 siblings, 1 reply; 30+ messages in thread
From: Marek Vasut @ 2013-06-28 20:57 UTC (permalink / raw)
  To: u-boot

Dear Simon Glass,

> +U_BOOT_CMD(
> +	dm,	2,	1,	do_dm,
> +	"Driver model low level access",
> +	"dump         Dump driver model tree\n"
> +	"dm uclass        Dump list of instances for each uclass\n"
> +	"dm test         Run tests"
> +);

Looking at this, remark comes to mind. We should really fix this inconsistency 
between the first "dump" and the rest of "dm uclass" and "dm test" etc. in the 
U_BOOT_CMD.

This is obviously out of scope of these patches.

Best regards,
Marek Vasut

^ permalink raw reply	[flat|nested] 30+ messages in thread

* [U-Boot] [PATCH v3 15/16] dm: Add README for driver model
  2013-06-19  3:52 ` [U-Boot] [PATCH v3 15/16] dm: Add README for " Simon Glass
@ 2013-06-28 21:26   ` Marek Vasut
  2013-10-18 17:05     ` Simon Glass
  0 siblings, 1 reply; 30+ messages in thread
From: Marek Vasut @ 2013-06-28 21:26 UTC (permalink / raw)
  To: u-boot

Dear Simon Glass,

> From: Pavel Herrmann <morpheus.ibis@gmail.com>
> 
> This adds a README to help with understanding of this series.
> 
> Signed-off-by: Simon Glass <sjg@chromium.org>
> ---
> Changes in v3:
> - Updated README.txt to cover changes since version 2
> 
> 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

You might _really_ want to reorder the readme in front of the actual DM patches.

Best regards,
Marek Vasut

^ permalink raw reply	[flat|nested] 30+ messages in thread

* [U-Boot] [PATCH v3 11/16] dm: Add a demonstration/example driver
  2013-06-19  3:52 ` [U-Boot] [PATCH v3 11/16] dm: Add a demonstration/example driver Simon Glass
@ 2013-06-28 21:46   ` Marek Vasut
  2013-10-18 19:44     ` Simon Glass
  0 siblings, 1 reply; 30+ messages in thread
From: Marek Vasut @ 2013-06-28 21:46 UTC (permalink / raw)
  To: u-boot

Dear Simon Glass,

> As an example of how to write a uclass and a driver, provide a demo version
> of each, accessible through the 'demo' command.
> 
> To use these with driver model, define CONFIG_CMD_DEMO and CONFIG_DM_DEMO.
> 
> The two demo drivers are enabled with CONFIG_DM_DEMO_SIMPLE and
> CONFIG_DM_DEMO_SHAPE.
> 
> Signed-off-by: Simon Glass <sjg@chromium.org>
> Signed-off-by: Marek Vasut <marex@denx.de>
> Signed-off-by: Pavel Herrmann <morpheus.ibis@gmail.com>
> Signed-off-by: Viktor K?iv?k <viktor.krivak@gmail.com>
> Signed-off-by: Tomas Hlavacek <tmshlvck@gmail.com>
> ---
> Changes in v3:
> - Fix up demo command help
> - Update demo driver to use device tree
> 
> Changes in v2: None

[...]

> +U_BOOT_DEVICE(demo4) = {
> +	.name = "demo_shape_drv",
> +	.platform_data = &yellow_hexagon,
> +};

So this is static instantiation of the drivers?

[...]

> +U_BOOT_DRIVER(demo_simple_drv) = {
> +	.name	= "demo_simple_drv",
> +	.of_match = demo_shape_id,
> +	.id	= UCLASS_DEMO,
> +	.probe	= demo_shape_probe,
> +	.ops	= &simple_ops,
> +	.ops	= &simple_ops,

This .ops is here twice ;-)

> +	.platform_data_auto_alloc_size = sizeof(struct dm_demo_pdata),
> +};

[...]

Best regards,
Marek Vasut

^ permalink raw reply	[flat|nested] 30+ messages in thread

* [U-Boot] [PATCH v3 12/16] dm: Add GPIO support and tests
  2013-06-19  3:52 ` [U-Boot] [PATCH v3 12/16] dm: Add GPIO support and tests Simon Glass
@ 2013-06-28 21:51   ` Marek Vasut
  2013-10-18 19:46     ` Simon Glass
  0 siblings, 1 reply; 30+ messages in thread
From: Marek Vasut @ 2013-06-28 21:51 UTC (permalink / raw)
  To: u-boot

Dear Simon Glass,

> Add driver model support for GPIOs. Since existing GPIO drivers do not use
> driver model, this feature must be enabled by CONFIG_DM_GPIO. After all
> GPO drivers are converted over we can perhaps remove this config.
> 
> Tests are provided for the sandbox implementation, and are a sufficient
> sanity check for basic operation.
> 
> The GPIO uclass understands the concept of named banks of GPIOs, with each
> GPIO device providing a single bank. Within each bank the GPIOs are
> numbered using an offset from 0 to n-1. For example a bank named 'b' with
> 20 offsets will provide GPIOs named b0 to b19.
> 
> Anonymous GPIO banks are also supported, and are just numbered without any
> prefix.
> 
> Each time a GPIO driver is added to the uclass, the GPIOs are renumbered
> accordinging, so there is always a global GPIO numbering order.

[...]

> +struct dm_gpio_ops {
> +	int (*request)(struct device *dev, unsigned offset, const char *label);
> +	int (*free)(struct device *dev, unsigned offset);
> +	int (*direction_input)(struct device *dev, unsigned offset);
> +	int (*direction_output)(struct device *dev, unsigned offset,
> +				int value);
> +	int (*get_value)(struct device *dev, unsigned offset);
> +	int (*set_value)(struct device *dev, unsigned offset, int value);
> +	int (*get_function)(struct device *dev, unsigned offset);

What's this "get_function()" call about? Is this to set GPIO AF ? If so, it's 
mixing pinmux and GPIO into one, that shouldn't happen.

> +	int (*get_state)(struct device *dev, unsigned offset, char *state,
> +			 int maxlen);
> +};

[...]

Best regards,
Marek Vasut

^ permalink raw reply	[flat|nested] 30+ messages in thread

* [U-Boot] [PATCH v3 07/16] dm: Add base driver model support
  2013-06-28 20:53   ` Marek Vasut
@ 2013-10-18 16:44     ` Simon Glass
  0 siblings, 0 replies; 30+ messages in thread
From: Simon Glass @ 2013-10-18 16:44 UTC (permalink / raw)
  To: u-boot

Hi Marek,

On Fri, Jun 28, 2013 at 2:53 PM, Marek Vasut <marex@denx.de> wrote:

> Dear Simon Glass,
>
> > Add driver model functionality for generic board.
> >
> > This includes data structures and base code for registering devices and
> > uclasses (groups of devices with the same purpose, e.g. all I2C ports
> will
> > be in the same uclass).
> >
> > The feature is enabled with CONFIG_DM.
> >
> > Signed-off-by: Simon Glass <sjg@chromium.org>
> > Signed-off-by: Marek Vasut <marex@denx.de>
> > Signed-off-by: Pavel Herrmann <morpheus.ibis@gmail.com>
> > Signed-off-by: Viktor K?iv?k <viktor.krivak@gmail.com>
> > Signed-off-by: Tomas Hlavacek <tmshlvck@gmail.com>
>

I am going to tidy this up for a v4, in case you are wondering why I am
only getting to this now.


> > ---
> > Changes in v3:
> > - Tidy up commenting of functions and structures
> > - Rename per_device_priv_size to per_device_auto_alloc_size, etc.
> > - Add a flag for tracking whether DM allocates/frees platform_data
> >
> > Changes in v2: None
> >
> >  Makefile                          |   2 +
> >  common/dm/Makefile                |  43 +++++
> >  common/dm/device.c                | 331
> > ++++++++++++++++++++++++++++++++++++++ common/dm/lists.c
> |
> > 168 +++++++++++++++++++
> >  common/dm/root.c                  | 115 +++++++++++++
> >  common/dm/uclass.c                | 298
> ++++++++++++++++++++++++++++++++++
> >  common/dm/util.c                  |  50 ++++++
>
> Maybe we should move these into drivers/core like Linux has it ?
>

Done


>
> [...]
>
> > +#include <common.h>
> > +#include <malloc.h>
> > +#include <dm/device.h>
> > +#include <dm/device-internal.h>
> > +#include <dm/lists.h>
> > +#include <dm/platform_data.h>
> > +#include <dm/uclass.h>
> > +#include <dm/uclass-internal.h>
> > +#include <dm/util.h>
> > +#include <linux/err.h>
> > +#include <linux/list.h>
> > +
> > +/**
> > + * device_chld_unbind() - Unbind all device's children from the device
> > + * @dev:     The device that is to be stripped of its children
> > + * @return 0 on success, -ve on error
> > + */
> > +static int device_chld_unbind(struct device *dev)
> > +{
> > +     struct device *pos, *n;
> > +     int ret;
> > +
> > +     assert(dev);
> > +
> > +     list_for_each_entry_safe(pos, n, &dev->child_head, sibling_node) {
> > +             ret = device_unbind(pos);
> > +             if (ret)
> > +                     return ret;
> > +     }
>
> This is used for stuff like destroying USB bus topology after running "usb
> rescan" for the second time, right? We might want to continue unbinding
> and only
> report the error instead of returning on the first error.
>

Done


>
> > +     return 0;
> > +}
> > +
> > +/**
> > + * device_chld_remove() - Stop all device's children
> > + * @dev:     The device whose children are to be removed
> > + * @return 0 on success, -ve on error
> > + */
> > +static int device_chld_remove(struct device *dev)
> > +{
> > +     struct device *pos, *n;
> > +     int ret;
> > +
> > +     assert(dev);
> > +
> > +     list_for_each_entry_safe(pos, n, &dev->child_head, sibling_node) {
> > +             ret = device_remove(pos);
> > +             if (ret)
> > +                     return ret;
> > +     }
> > +
> > +     return 0;
> > +}
>
> [...]
>
> We might really want to document these.
>

Yes - they are documented in the header file include/dm/device-internal.h


>
> > +int device_bind_by_name(struct device *parent, const struct driver_info
> > *info, +                      struct device **devp)
>
> [...]
>
> > +struct uclass *uclass_find(enum uclass_id key)
> > +{
> > +     struct uclass *uc;
> > +
> > +     /*
> > +      * TODO(sjg at chromium.org): Optimise this, perhaps moving the found
> > +      * node to the start of the list, or creating a linear array
> mapping
> > +      * id to node.
> > +      */
>
> We better do this later, after it's all in place.
>

Yes, I'm not sure how important it is. We only have about 3 uclasses. This
will be a nice problem to have when all the drivers are converted...



>
> > +     list_for_each_entry(uc, &gd->uclass_root, sibling_node) {
> > +             if (uc->uc_drv->id == key)
> > +                     return uc;
> > +     }
> > +
> > +     return NULL;
> > +}
> > +
> > +/**
> > + * uclass_add() - Create new uclass in list
> > + * @id: Id number to create
> > + * @ucp: Returns pointer to uclass, or NULL on error
> > + * @return 0 on success, -ve on error
> > + *
> > + * The new uclass is added to the list. There must be only one uclass
> for
> > + * each id.
> > + */
> > +static int uclass_add(enum uclass_id id, struct uclass **ucp)
> > +{
>
> I might be a bit lost here. Do we now support adding uclass at runtime ?
>

Sort-of. The uclass_get() function is the only one exported, but it in turn
calls uclass_add(). We could open this up later, but I don't see a lot of
benefit, since we only want one uclass per ID.

Regards,
Simon

^ permalink raw reply	[flat|nested] 30+ messages in thread

* [U-Boot] [PATCH v3 08/16] dm: Set up driver model after relocation
  2013-06-28 20:53   ` Marek Vasut
@ 2013-10-18 16:45     ` Simon Glass
  2013-10-19  0:45       ` Marek Vasut
  0 siblings, 1 reply; 30+ messages in thread
From: Simon Glass @ 2013-10-18 16:45 UTC (permalink / raw)
  To: u-boot

Hi Marek,

On Fri, Jun 28, 2013 at 2:53 PM, Marek Vasut <marex@denx.de> wrote:

> Dear Simon Glass,
>
> > Make driver model available after relocation, by setting up data
> structures
> > and scanning for devices using compiled-in platform_data and (when
> > available) the device tree.
> >
> > Signed-off-by: Simon Glass <sjg@chromium.org>
> > ---
> > Changes in v3: None
> > Changes in v2: None
> >
> >  common/board_r.c | 33 +++++++++++++++++++++++++++++++++
> >  1 file changed, 33 insertions(+)
> >
> > diff --git a/common/board_r.c b/common/board_r.c
> > index f5649c9..5fe86ef 100644
> > --- a/common/board_r.c
> > +++ b/common/board_r.c
> > @@ -34,6 +34,7 @@
> >  #ifdef CONFIG_HAS_DATAFLASH
> >  #include <dataflash.h>
> >  #endif
> > +#include <dm.h>
> >  #include <environment.h>
> >  #include <fdtdec.h>
> >  #if defined(CONFIG_CMD_IDE)
> > @@ -66,7 +67,9 @@
> >  #ifdef CONFIG_X86
> >  #include <asm/init_helpers.h>
> >  #endif
> > +#include <dm/root.h>
> >  #include <linux/compiler.h>
> > +#include <linux/err.h>
> >
> >  DECLARE_GLOBAL_DATA_PTR;
> >
> > @@ -269,6 +272,33 @@ static int initr_malloc(void)
> >       return 0;
> >  }
> >
> > +#ifdef CONFIG_DM
> > +static int initr_dm(void)
> > +{
> > +     int ret;
> > +
> > +     ret = dm_init();
> > +     if (ret) {
> > +             debug("dm_init() failed: %d\n", ret);
> > +             return ret;
> > +     }
> > +     ret = dm_scan_platform_data();
> > +     if (ret) {
> > +             debug("dm_scan_platform_data() failed: %d\n", ret);
> > +             return ret;
> > +     }
> > +#ifdef CONFIG_OF_CONTROL
> > +     ret = dm_scan_fdt(gd->fdt_blob);
>
> This part is _very_ nice!
>

Yes, it's great - it will provide automated availability of device
instances just from the device tree.

Regards,
Simon

^ permalink raw reply	[flat|nested] 30+ messages in thread

* [U-Boot] [PATCH v3 10/16] dm: Add a 'dm' command for testing
  2013-06-28 20:57   ` Marek Vasut
@ 2013-10-18 17:00     ` Simon Glass
  0 siblings, 0 replies; 30+ messages in thread
From: Simon Glass @ 2013-10-18 17:00 UTC (permalink / raw)
  To: u-boot

Hi Marek,

On Fri, Jun 28, 2013 at 2:57 PM, Marek Vasut <marex@denx.de> wrote:

> Dear Simon Glass,
>
> > +U_BOOT_CMD(
> > +     dm,     2,      1,      do_dm,
> > +     "Driver model low level access",
> > +     "dump         Dump driver model tree\n"
> > +     "dm uclass        Dump list of instances for each uclass\n"
> > +     "dm test         Run tests"
> > +);
>
> Looking at this, remark comes to mind. We should really fix this
> inconsistency
> between the first "dump" and the rest of "dm uclass" and "dm test" etc. in
> the
> U_BOOT_CMD.
>

I think I'll change this one to 'dm tree' instead of 'dm dump' since 'dump'
is pretty vague.


> This is obviously out of scope of these patches.


Regards,
Simon

^ permalink raw reply	[flat|nested] 30+ messages in thread

* [U-Boot] [PATCH v3 15/16] dm: Add README for driver model
  2013-06-28 21:26   ` Marek Vasut
@ 2013-10-18 17:05     ` Simon Glass
  0 siblings, 0 replies; 30+ messages in thread
From: Simon Glass @ 2013-10-18 17:05 UTC (permalink / raw)
  To: u-boot

Hi Marek,

On Fri, Jun 28, 2013 at 3:26 PM, Marek Vasut <marex@denx.de> wrote:

> Dear Simon Glass,
>
> > From: Pavel Herrmann <morpheus.ibis@gmail.com>
> >
> > This adds a README to help with understanding of this series.
> >
> > Signed-off-by: Simon Glass <sjg@chromium.org>
> > ---
> > Changes in v3:
> > - Updated README.txt to cover changes since version 2
> >
> > 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
>
> You might _really_ want to reorder the readme in front of the actual DM
> patches.
>

Will do.

Regards,
Simon

^ permalink raw reply	[flat|nested] 30+ messages in thread

* [U-Boot] [PATCH v3 11/16] dm: Add a demonstration/example driver
  2013-06-28 21:46   ` Marek Vasut
@ 2013-10-18 19:44     ` Simon Glass
  0 siblings, 0 replies; 30+ messages in thread
From: Simon Glass @ 2013-10-18 19:44 UTC (permalink / raw)
  To: u-boot

Hi Marek,

On Fri, Jun 28, 2013 at 3:46 PM, Marek Vasut <marex@denx.de> wrote:

> Dear Simon Glass,
>
> > As an example of how to write a uclass and a driver, provide a demo
> version
> > of each, accessible through the 'demo' command.
> >
> > To use these with driver model, define CONFIG_CMD_DEMO and
> CONFIG_DM_DEMO.
> >
> > The two demo drivers are enabled with CONFIG_DM_DEMO_SIMPLE and
> > CONFIG_DM_DEMO_SHAPE.
> >
> > Signed-off-by: Simon Glass <sjg@chromium.org>
> > Signed-off-by: Marek Vasut <marex@denx.de>
> > Signed-off-by: Pavel Herrmann <morpheus.ibis@gmail.com>
> > Signed-off-by: Viktor K?iv?k <viktor.krivak@gmail.com>
> > Signed-off-by: Tomas Hlavacek <tmshlvck@gmail.com>
> > ---
> > Changes in v3:
> > - Fix up demo command help
> > - Update demo driver to use device tree
> >
> > Changes in v2: None
>
> [...]
>
> > +U_BOOT_DEVICE(demo4) = {
> > +     .name = "demo_shape_drv",
> > +     .platform_data = &yellow_hexagon,
> > +};
>
> So this is static instantiation of the drivers?
>

Yes, it is possible to set up a device either with statically-linked
platform_data (as here) or with device tree.


>
> [...]
>
> > +U_BOOT_DRIVER(demo_simple_drv) = {
> > +     .name   = "demo_simple_drv",
> > +     .of_match = demo_shape_id,
> > +     .id     = UCLASS_DEMO,
> > +     .probe  = demo_shape_probe,
> > +     .ops    = &simple_ops,
> > +     .ops    = &simple_ops,
>
> This .ops is here twice ;-)
>

Fixed.



>
> > +     .platform_data_auto_alloc_size = sizeof(struct dm_demo_pdata),
> > +};
>
> [...]
>
>
>
Regards,
Simon

^ permalink raw reply	[flat|nested] 30+ messages in thread

* [U-Boot] [PATCH v3 12/16] dm: Add GPIO support and tests
  2013-06-28 21:51   ` Marek Vasut
@ 2013-10-18 19:46     ` Simon Glass
  0 siblings, 0 replies; 30+ messages in thread
From: Simon Glass @ 2013-10-18 19:46 UTC (permalink / raw)
  To: u-boot

Hi Marek,

On Fri, Jun 28, 2013 at 3:51 PM, Marek Vasut <marex@denx.de> wrote:

> Dear Simon Glass,
>
> > Add driver model support for GPIOs. Since existing GPIO drivers do not
> use
> > driver model, this feature must be enabled by CONFIG_DM_GPIO. After all
> > GPO drivers are converted over we can perhaps remove this config.
> >
> > Tests are provided for the sandbox implementation, and are a sufficient
> > sanity check for basic operation.
> >
> > The GPIO uclass understands the concept of named banks of GPIOs, with
> each
> > GPIO device providing a single bank. Within each bank the GPIOs are
> > numbered using an offset from 0 to n-1. For example a bank named 'b' with
> > 20 offsets will provide GPIOs named b0 to b19.
> >
> > Anonymous GPIO banks are also supported, and are just numbered without
> any
> > prefix.
> >
> > Each time a GPIO driver is added to the uclass, the GPIOs are renumbered
> > accordinging, so there is always a global GPIO numbering order.
>
> [...]
>
> > +struct dm_gpio_ops {
> > +     int (*request)(struct device *dev, unsigned offset, const char
> *label);
> > +     int (*free)(struct device *dev, unsigned offset);
> > +     int (*direction_input)(struct device *dev, unsigned offset);
> > +     int (*direction_output)(struct device *dev, unsigned offset,
> > +                             int value);
> > +     int (*get_value)(struct device *dev, unsigned offset);
> > +     int (*set_value)(struct device *dev, unsigned offset, int value);
> > +     int (*get_function)(struct device *dev, unsigned offset);
>
> What's this "get_function()" call about? Is this to set GPIO AF ? If so,
> it's
> mixing pinmux and GPIO into one, that shouldn't happen.
>

It's this:

static const char * const gpio_function[] = {
"input",
"output",
"unknown",
};

We need to know the whether the pin is in use for GPIO and if so, whether
it is input or output.

I have not looked at pinmux, but consider this out of scope for this series.


>
> > +     int (*get_state)(struct device *dev, unsigned offset, char *state,
> > +                      int maxlen);
> > +};
>
> [...]
>
>
> Regards,
Simon

^ permalink raw reply	[flat|nested] 30+ messages in thread

* [U-Boot] [PATCH v3 08/16] dm: Set up driver model after relocation
  2013-10-18 16:45     ` Simon Glass
@ 2013-10-19  0:45       ` Marek Vasut
  0 siblings, 0 replies; 30+ messages in thread
From: Marek Vasut @ 2013-10-19  0:45 UTC (permalink / raw)
  To: u-boot

Hi Simon,

> Hi Marek,
> 
> On Fri, Jun 28, 2013 at 2:53 PM, Marek Vasut <marex@denx.de> wrote:
> > Dear Simon Glass,
> > 
> > > Make driver model available after relocation, by setting up data
> > 
> > structures
> > 
> > > and scanning for devices using compiled-in platform_data and (when
> > > available) the device tree.
> > > 
> > > Signed-off-by: Simon Glass <sjg@chromium.org>
> > > ---
> > > Changes in v3: None
> > > Changes in v2: None
> > > 
> > >  common/board_r.c | 33 +++++++++++++++++++++++++++++++++
> > >  1 file changed, 33 insertions(+)
> > > 
> > > diff --git a/common/board_r.c b/common/board_r.c
> > > index f5649c9..5fe86ef 100644
> > > --- a/common/board_r.c
> > > +++ b/common/board_r.c
> > > @@ -34,6 +34,7 @@
> > > 
> > >  #ifdef CONFIG_HAS_DATAFLASH
> > >  #include <dataflash.h>
> > >  #endif
> > > 
> > > +#include <dm.h>
> > > 
> > >  #include <environment.h>
> > >  #include <fdtdec.h>
> > >  #if defined(CONFIG_CMD_IDE)
> > > 
> > > @@ -66,7 +67,9 @@
> > > 
> > >  #ifdef CONFIG_X86
> > >  #include <asm/init_helpers.h>
> > >  #endif
> > > 
> > > +#include <dm/root.h>
> > > 
> > >  #include <linux/compiler.h>
> > > 
> > > +#include <linux/err.h>
> > > 
> > >  DECLARE_GLOBAL_DATA_PTR;
> > > 
> > > @@ -269,6 +272,33 @@ static int initr_malloc(void)
> > > 
> > >       return 0;
> > >  
> > >  }
> > > 
> > > +#ifdef CONFIG_DM
> > > +static int initr_dm(void)
> > > +{
> > > +     int ret;
> > > +
> > > +     ret = dm_init();
> > > +     if (ret) {
> > > +             debug("dm_init() failed: %d\n", ret);
> > > +             return ret;
> > > +     }
> > > +     ret = dm_scan_platform_data();
> > > +     if (ret) {
> > > +             debug("dm_scan_platform_data() failed: %d\n", ret);
> > > +             return ret;
> > > +     }
> > > +#ifdef CONFIG_OF_CONTROL
> > > +     ret = dm_scan_fdt(gd->fdt_blob);
> > 
> > This part is _very_ nice!
> 
> Yes, it's great - it will provide automated availability of device
> instances just from the device tree.

Let us meet at the ELCE and discuss this. I'll not be half-asleep this time.

^ permalink raw reply	[flat|nested] 30+ messages in thread

end of thread, other threads:[~2013-10-19  0:45 UTC | newest]

Thread overview: 30+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2013-06-19  3:52 [U-Boot] [PATCH v3 0/16] Driver model implementation, tests, demo and GPIO Simon Glass
2013-06-19  3:52 ` [U-Boot] [PATCH v3 01/16] sandbox: Make map_to_sysmem() use a constant pointer Simon Glass
2013-06-19  3:52 ` [U-Boot] [PATCH v3 02/16] sandbox: Correct data sizes and printf() strings in fdtdec.c Simon Glass
2013-06-19  3:52 ` [U-Boot] [PATCH v3 03/16] sandbox: config: Don't use 64-bit physical memory Simon Glass
2013-06-19  3:52 ` [U-Boot] [PATCH v3 04/16] sandbox: Build a device tree file for sandbox Simon Glass
2013-06-19  3:52 ` [U-Boot] [PATCH v3 05/16] Add cmd_process_error() to report and process errors Simon Glass
2013-06-19  3:52 ` [U-Boot] [PATCH v3 06/16] sandbox: config: Enable driver model Simon Glass
2013-06-19  3:52 ` [U-Boot] [PATCH v3 07/16] dm: Add base driver model support Simon Glass
2013-06-28 20:53   ` Marek Vasut
2013-10-18 16:44     ` Simon Glass
2013-06-19  3:52 ` [U-Boot] [PATCH v3 08/16] dm: Set up driver model after relocation Simon Glass
2013-06-28 20:53   ` Marek Vasut
2013-10-18 16:45     ` Simon Glass
2013-10-19  0:45       ` Marek Vasut
2013-06-19  3:52 ` [U-Boot] [PATCH v3 09/16] dm: Add basic tests Simon Glass
2013-06-19  3:52 ` [U-Boot] [PATCH v3 10/16] dm: Add a 'dm' command for testing Simon Glass
2013-06-28 20:57   ` Marek Vasut
2013-10-18 17:00     ` Simon Glass
2013-06-19  3:52 ` [U-Boot] [PATCH v3 11/16] dm: Add a demonstration/example driver Simon Glass
2013-06-28 21:46   ` Marek Vasut
2013-10-18 19:44     ` Simon Glass
2013-06-19  3:52 ` [U-Boot] [PATCH v3 12/16] dm: Add GPIO support and tests Simon Glass
2013-06-28 21:51   ` Marek Vasut
2013-10-18 19:46     ` Simon Glass
2013-06-19  3:52 ` [U-Boot] [PATCH v3 13/16] sandbox: Convert GPIOs to use driver model Simon Glass
2013-06-19  3:52 ` [U-Boot] [PATCH v3 14/16] dm: Enable gpio command to support " Simon Glass
2013-06-19  3:52 ` [U-Boot] [PATCH v3 15/16] dm: Add README for " Simon Glass
2013-06-28 21:26   ` Marek Vasut
2013-10-18 17:05     ` Simon Glass
2013-06-19  3:52 ` [U-Boot] [PATCH v3 16/16] dm: Move old driver model documentation into an 'old-docs' directory Simon Glass

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.