* [U-Boot] [RFC PATCH v2 01/14] sandbox: Make map_to_sysmem() use a constant pointer
2013-05-07 19:41 [U-Boot] [RFC PATCH v2 0/14] Driver model implementation, tests, demo and GPIO Simon Glass
@ 2013-05-07 19:41 ` Simon Glass
2013-05-07 19:41 ` [U-Boot] [RFC PATCH v2 02/14] sandbox: Correct data sizes and printf() strings in fdtdec.c Simon Glass
` (13 subsequent siblings)
14 siblings, 0 replies; 17+ messages in thread
From: Simon Glass @ 2013-05-07 19:41 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 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 40e4b07..2bb0322 100644
--- a/include/common.h
+++ b/include/common.h
@@ -925,7 +925,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.2.1
^ permalink raw reply related [flat|nested] 17+ messages in thread* [U-Boot] [RFC PATCH v2 02/14] sandbox: Correct data sizes and printf() strings in fdtdec.c
2013-05-07 19:41 [U-Boot] [RFC PATCH v2 0/14] Driver model implementation, tests, demo and GPIO Simon Glass
2013-05-07 19:41 ` [U-Boot] [RFC PATCH v2 01/14] sandbox: Make map_to_sysmem() use a constant pointer Simon Glass
@ 2013-05-07 19:41 ` Simon Glass
2013-05-07 19:41 ` [U-Boot] [RFC PATCH v2 03/14] sandbox: config: Don't use 64-bit physical memory Simon Glass
` (12 subsequent siblings)
14 siblings, 0 replies; 17+ messages in thread
From: Simon Glass @ 2013-05-07 19:41 UTC (permalink / raw)
To: u-boot
There are a few wwrnings 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 v2: None
lib/fdtdec.c | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)
diff --git a/lib/fdtdec.c b/lib/fdtdec.c
index ac1fe0b..36f5d8e 100644
--- a/lib/fdtdec.c
+++ b/lib/fdtdec.c
@@ -92,10 +92,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;
}
@@ -617,7 +617,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.2.1
^ permalink raw reply related [flat|nested] 17+ messages in thread* [U-Boot] [RFC PATCH v2 03/14] sandbox: config: Don't use 64-bit physical memory
2013-05-07 19:41 [U-Boot] [RFC PATCH v2 0/14] Driver model implementation, tests, demo and GPIO Simon Glass
2013-05-07 19:41 ` [U-Boot] [RFC PATCH v2 01/14] sandbox: Make map_to_sysmem() use a constant pointer Simon Glass
2013-05-07 19:41 ` [U-Boot] [RFC PATCH v2 02/14] sandbox: Correct data sizes and printf() strings in fdtdec.c Simon Glass
@ 2013-05-07 19:41 ` Simon Glass
2013-05-07 19:42 ` [U-Boot] [RFC PATCH v2 04/14] Add cmd_process_error() to report and process errors Simon Glass
` (11 subsequent siblings)
14 siblings, 0 replies; 17+ messages in thread
From: Simon Glass @ 2013-05-07 19:41 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 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.2.1
^ permalink raw reply related [flat|nested] 17+ messages in thread* [U-Boot] [RFC PATCH v2 04/14] Add cmd_process_error() to report and process errors
2013-05-07 19:41 [U-Boot] [RFC PATCH v2 0/14] Driver model implementation, tests, demo and GPIO Simon Glass
` (2 preceding siblings ...)
2013-05-07 19:41 ` [U-Boot] [RFC PATCH v2 03/14] sandbox: config: Don't use 64-bit physical memory Simon Glass
@ 2013-05-07 19:42 ` Simon Glass
2013-05-07 19:42 ` [U-Boot] [RFC PATCH v2 05/14] sandbox: config: Enable driver model Simon Glass
` (10 subsequent siblings)
14 siblings, 0 replies; 17+ messages in thread
From: Simon Glass @ 2013-05-07 19:42 UTC (permalink / raw)
To: u-boot
U-Boot now uses errors defined in include/errno.h which are negative
integers. Command 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 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.2.1
^ permalink raw reply related [flat|nested] 17+ messages in thread* [U-Boot] [RFC PATCH v2 05/14] sandbox: config: Enable driver model
2013-05-07 19:41 [U-Boot] [RFC PATCH v2 0/14] Driver model implementation, tests, demo and GPIO Simon Glass
` (3 preceding siblings ...)
2013-05-07 19:42 ` [U-Boot] [RFC PATCH v2 04/14] Add cmd_process_error() to report and process errors Simon Glass
@ 2013-05-07 19:42 ` Simon Glass
2013-05-07 19:42 ` [U-Boot] [RFC PATCH v2 06/14] dm: Add base driver model support Simon Glass
` (9 subsequent siblings)
14 siblings, 0 replies; 17+ messages in thread
From: Simon Glass @ 2013-05-07 19:42 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 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 78eda90..c93970a 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.2.1
^ permalink raw reply related [flat|nested] 17+ messages in thread* [U-Boot] [RFC PATCH v2 06/14] dm: Add base driver model support
2013-05-07 19:41 [U-Boot] [RFC PATCH v2 0/14] Driver model implementation, tests, demo and GPIO Simon Glass
` (4 preceding siblings ...)
2013-05-07 19:42 ` [U-Boot] [RFC PATCH v2 05/14] sandbox: config: Enable driver model Simon Glass
@ 2013-05-07 19:42 ` Simon Glass
2013-05-07 19:42 ` [U-Boot] [RFC PATCH v2 07/14] dm: Set up driver model after relocation Simon Glass
` (8 subsequent siblings)
14 siblings, 0 replies; 17+ messages in thread
From: Simon Glass @ 2013-05-07 19:42 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 v2: None
Makefile | 2 +
common/dm/Makefile | 40 +++++
common/dm/device.c | 370 ++++++++++++++++++++++++++++++++++++++
common/dm/lists.c | 181 +++++++++++++++++++
common/dm/root.c | 124 +++++++++++++
common/dm/uclass.c | 362 +++++++++++++++++++++++++++++++++++++
common/dm/util.c | 50 ++++++
include/asm-generic/global_data.h | 9 +
include/dm.h | 27 +++
include/dm/device-internal.h | 38 ++++
include/dm/device.h | 72 ++++++++
include/dm/lists.h | 37 ++++
include/dm/platform_data.h | 38 ++++
include/dm/root.h | 38 ++++
include/dm/uclass-id.h | 42 +++++
include/dm/uclass-internal.h | 36 ++++
include/dm/uclass.h | 74 ++++++++
include/dm/util.h | 42 +++++
18 files changed, 1582 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 dbc4b70..8a29b0c 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..b6ca712
--- /dev/null
+++ b/common/dm/Makefile
@@ -0,0 +1,40 @@
+# See file CREDITS for list of people who contributed to this
+# project.
+#
+# 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..efdc7e8
--- /dev/null
+++ b/common/dm/device.c
@@ -0,0 +1,370 @@
+/*
+ * Device manager
+ *
+ * (C) Copyright 2013 Google, Inc
+ *
+ * (C) Copyright 2012
+ * Pavel Herrmann <morpheus.ibis@gmail.com>
+ *
+ * See file CREDITS for list of people who contributed to this
+ * project.
+ *
+ * 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;
+}
+
+/**
+ * device_bind() - Instantiate and connect a driver under a parent one
+ * @parent: The parent driver, under which the new driver will be connected
+ * @drv: Driver to use for this device
+ * @name: Name of device
+ * @platform_data: Platform data for this device, provided by the board,
+ * and used to tell the driver about its IO address and so on
+ * If there is none, this is NULL.
+ * @of_offset: Device tree node that is attached to this device, or -1 if none
+ * @devp: Returns a pointer to the newly created device, or NULL on error
+ * @return 0 on success, -ve on error
+ *
+ * Make a new device based on data passed by the user. This device is then
+ * connected under the parent driver. The device is not yet started after
+ * being bound, its only present in the driver tree.
+ */
+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;
+
+ /* 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;
+}
+
+/**
+ * device_bind_by_name() - Create a device by name
+ * @parent: The parent driver, under which the new device will be connected
+ * @info: Information about the driver and platform_data to use
+ * @devp: Returns a pointer to the newly created device, or NULL on error
+ * @return 0 on success, -ve on error
+ *
+ * Make a new device based on data passed by the user. This device is then
+ * connected under the parent device. The device is not yet started (probed)
+ * after being bound; it is merely present in the driver tree.
+ */
+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);
+}
+
+/**
+ * device_unbind() - Unbind driver from its parent
+ * @dev: The driver to be unbound
+ * @return 0 on success, -ve on error
+ *
+ * Disconnect a driver from its parent. The driver must be already removed,
+ * otherwise this function fails and doesn't unbind the driver.
+ */
+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_probe() - Start a device
+ * @dev: Device that is to be started
+ * @return 0 on success, -ve on error
+ *
+ * This function runs the device's probe() function, which initializes the
+ * hardware. The hardware and all its predecessors are started by this
+ * function.
+ */
+int device_probe(struct device *dev)
+{
+ struct driver *drv;
+ int size;
+ 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_size) {
+ dev->priv = calloc(1, drv->priv_size);
+ if (!dev->priv)
+ return -ENOMEM;
+ }
+ /* Allocate private data if requested */
+ if (drv->platform_data_size) {
+ dev->platform_data = calloc(1, drv->platform_data_size);
+ if (!dev->platform_data)
+ return -ENOMEM;
+ }
+ size = dev->uclass->uc_drv->per_device_priv_size;
+ if (size) {
+ dev->uclass_priv = calloc(1, size);
+ if (!dev->uclass_priv)
+ return -ENOMEM;
+ }
+
+ /* 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:
+ if (drv->priv_size) {
+ free(dev->priv);
+ dev->priv = NULL;
+ }
+
+ return ret;
+}
+
+/**
+ * device_remove() - Disable the device and its children
+ * @dev: The device to be disabled
+ * @return 0 on success, -ve on error
+ *
+ * Deconfigure the hardware and all its children.
+ */
+int device_remove(struct device *dev)
+{
+ struct driver *drv;
+ int size;
+ 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;
+ }
+
+ if (dev->driver->priv_size) {
+ free(dev->priv);
+ dev->priv = NULL;
+ }
+ if (dev->driver->platform_data_size) {
+ free(dev->platform_data);
+ dev->platform_data = NULL;
+ }
+ size = dev->uclass->uc_drv->per_device_priv_size;
+ if (size) {
+ free(dev->uclass_priv);
+ dev->uclass_priv = NULL;
+ }
+
+ 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..02bcb50
--- /dev/null
+++ b/common/dm/lists.c
@@ -0,0 +1,181 @@
+/*
+ * (C) Copyright 2012
+ * Marek Vasut <marex@denx.de>
+ *
+ * See file CREDITS for list of people who contributed to this
+ * project.
+ *
+ * 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>
+
+/**
+ * lists_driver_lookup_name() - Return u_boot_driver corresponding to instance
+ * instance: Instance of the driver
+ *
+ * This function returns pointer to an u_boot_driver, which is base for the
+ * supplied instance of a driver. Returns NULL on error.
+ */
+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;
+}
+
+/**
+ * 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)
+{
+ 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;
+}
+
+/**
+ * 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;
+}
diff --git a/common/dm/root.c b/common/dm/root.c
new file mode 100644
index 0000000..bba793a
--- /dev/null
+++ b/common/dm/root.c
@@ -0,0 +1,124 @@
+/*
+ * (C) Copyright 2012
+ * Pavel Herrmann <morpheus.ibis@gmail.com>
+ *
+ * See file CREDITS for list of people who contributed to this
+ * project.
+ *
+ * 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",
+};
+
+/**
+ * dm_root() - Return pointer to the top of the driver tree
+ *
+ * This function returns pointer to the root node of the driver tree,
+ * in case this root wasn't created yet, reports it and returns NULL.
+ */
+struct device *dm_root(void)
+{
+ if (!gd->dm_root) {
+ dm_warn("Virtual root driver does not exist!\n");
+ return NULL;
+ }
+
+ return gd->dm_root;
+}
+
+/**
+ * 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
+ */
+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;
+}
+
+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;
+}
+
+U_BOOT_DRIVER(root_driver) = {
+ .name = "root_driver",
+ .id = UCLASS_ROOT,
+};
+
+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..4ff308e
--- /dev/null
+++ b/common/dm/uclass.c
@@ -0,0 +1,362 @@
+/*
+ * (C) Copyright 2012
+ * Pavel Herrmann <morpheus.ibis@gmail.com>
+ *
+ * See file CREDITS for list of people who contributed to this
+ * project.
+ *
+ * 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;
+
+/**
+ * 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)
+{
+ 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_size) {
+ uc->priv = calloc(1, uc_drv->priv_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_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_size)
+ free(uc->priv);
+ free(uc);
+
+ return 0;
+}
+
+/**
+ * uclass_get() - Return or create uclass by id
+ * @id: Search uclass with this id or create new one
+ * @ucp: Returns pointer to uclass, or NULL on error
+ * @return 0 on success, -ve on error
+ *
+ * Try to find uclass in list structure. If fail create a new uclass and place
+ * it in list.
+ */
+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;
+}
+
+/**
+ * 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)
+{
+ 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;
+}
+
+/**
+ * uclass_get_device() - Return n-th child of uclass
+ * @id: Id number of the uclass
+ * @index: Position of the child in uclass's list
+ *
+ * The device is prepared for use (probed) if needed.
+ *
+ * Return the uclass pointer of a child at the given index or
+ * return NULL on error.
+ */
+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;
+}
+
+/**
+ * uclass_bind_device() - Associate device with a uclass
+ * @dev: Pointer to the device
+ *
+ * Connect the device into uclass's list of devices. Returns 0 on success,
+ * negative value on error.
+ */
+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;
+}
+
+/**
+ * uclass_unbind_device() - Deassociate device with a uclass
+ * @dev: Pointer to the device
+ *
+ * Disconnect the device from uclass's list of devices.
+ * Returns 0 on success, negative value on error.
+ */
+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;
+}
+
+/**
+ * uclass_post_probe_device() - Deal with a device that has just been probed
+ * @dev: Pointer to the device
+ *
+ * Perform any post-processing of a probed device that is needed by the
+ * uclass. Returns 0 on success,negative value on error.
+ */
+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;
+}
+
+/**
+ * uclass_pre_remove_device() - Handle a device which is about to be removed
+ * @dev: Pointer to the device
+ *
+ * Perform any pre-processing of a device that is about to be removed.
+ * Returns 0 on success, negative value on error.
+ */
+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_priv_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..01ffde6
--- /dev/null
+++ b/common/dm/util.c
@@ -0,0 +1,50 @@
+/*
+ * (C) Copyright 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 5416f46..f952d9a 100644
--- a/include/asm-generic/global_data.h
+++ b/include/asm-generic/global_data.h
@@ -24,6 +24,7 @@
#ifndef __ASM_GENERIC_GBL_DATA_H
#define __ASM_GENERIC_GBL_DATA_H
+
/*
* The following data structure is placed in some memory which is
* available very early after boot (like DPRAM on MPC8xx/MPC82xx, or
@@ -37,6 +38,8 @@
*/
#ifndef __ASSEMBLY__
+#include <linux/list.h>
+
typedef struct global_data {
bd_t *bd;
unsigned long flags;
@@ -80,6 +83,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..4dec8fd
--- /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..b760d5c
--- /dev/null
+++ b/include/dm/device-internal.h
@@ -0,0 +1,38 @@
+/*
+ * (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;
+
+/* driver manager API */
+int device_bind(struct device *parent, struct driver *drv,
+ const char *name, void *platform_data, int of_offset,
+ struct device **devp);
+int device_bind_by_name(struct device *parent, const struct driver_info *info,
+ struct device **devp);
+
+int device_probe(struct device *dev);
+int device_remove(struct device *dev);
+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..f1d3760
--- /dev/null
+++ b/include/dm/device.h
@@ -0,0 +1,72 @@
+/*
+ * (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;
+
+#define DM_FLAG_ACTIVATED 1
+
+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; /* attach devices to class */
+ struct list_head child_head; /* list of child devices */
+ struct list_head sibling_node; /* siblings */
+ uint32_t flags;
+};
+
+#define device_get_ops(dev) (dev->driver->ops)
+#define device_active(dev) ((dev)->flags & DM_FLAG_ACTIVATED)
+
+struct device_id {
+ const char *compatible;
+ ulong data;
+};
+
+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_size;
+ int platform_data_size;
+ const void *ops; /* driver-specific operations */
+};
+
+#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..8909202
--- /dev/null
+++ b/include/dm/lists.h
@@ -0,0 +1,37 @@
+/*
+ * (C) Copyright 2012
+ * Pavel Herrmann <morpheus.ibis@gmail.com>
+ *
+ * See file CREDITS for list of people who contributed to this
+ * project.
+ *
+ * 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>
+
+struct driver *lists_driver_lookup_name(const char *name);
+
+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..5420c03
--- /dev/null
+++ b/include/dm/platform_data.h
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2013 Google, Inc
+ *
+ * (C) Copyright 2012
+ * Pavel Herrmann <morpheus.ibis@gmail.com>
+ * Marek Vasut <marex@denx.de>
+ *
+ * See file CREDITS for list of people who contributed to this
+ * project.
+ *
+ * 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..9c3e732
--- /dev/null
+++ b/include/dm/root.h
@@ -0,0 +1,38 @@
+/*
+ * (C) Copyright 2012
+ * Pavel Herrmann <morpheus.ibis@gmail.com>
+ *
+ * See file CREDITS for list of people who contributed to this
+ * project.
+ *
+ * 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;
+
+/* tree creation helpers */
+struct device *dm_root(void);
+
+int dm_scan_platform_data(void);
+
+int dm_scan_fdt(const void *blob);
+
+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..9248ca5
--- /dev/null
+++ b/include/dm/uclass-id.h
@@ -0,0 +1,42 @@
+/*
+ * (C) Copyright 2012
+ * Pavel Herrmann <morpheus.ibis@gmail.com>
+ *
+ * See file CREDITS for list of people who contributed to this
+ * project.
+ *
+ * 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: 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..9424763
--- /dev/null
+++ b/include/dm/uclass-internal.h
@@ -0,0 +1,36 @@
+/*
+ * (C) Copyright 2012
+ * Pavel Herrmann <morpheus.ibis@gmail.com>
+ *
+ * See file CREDITS for list of people who contributed to this
+ * project.
+ *
+ * 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
+
+int uclass_find_device(enum uclass_id id, int index, struct device **devp);
+
+int uclass_bind_device(struct device *dev);
+int uclass_unbind_device(struct device *dev);
+int uclass_post_probe_device(struct device *dev);
+int uclass_pre_remove_device(struct device *dev);
+struct uclass *uclass_find(enum uclass_id key);
+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..4f210b8
--- /dev/null
+++ b/include/dm/uclass.h
@@ -0,0 +1,74 @@
+/*
+ * (C) Copyright 2012
+ * Pavel Herrmann <morpheus.ibis@gmail.com>
+ *
+ * See file CREDITS for list of people who contributed to this
+ * project.
+ *
+ * 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 {
+ void *priv;
+ struct uclass_driver *uc_drv;
+ struct list_head dev_head; /* devices in this class */
+ struct list_head sibling_node; /* next uclsas */
+};
+
+struct device;
+
+/* Linker list uclass description */
+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_size;
+ int per_device_priv_size;
+ const void *ops; /* class-specific operations */
+};
+
+#define UCLASS_DRIVER(__name) \
+ ll_entry_declare(struct uclass_driver, __name, uclass)
+
+/* uclass API wrappers */
+int uclass_get(enum uclass_id key, struct uclass **ucp);
+
+int uclass_get_device(enum uclass_id id, int index, struct device **ucp);
+
+int uclass_first_device(enum uclass_id id, struct device **devp);
+
+int uclass_next_device(struct device **devp);
+
+#define uclass_foreach_dev(pos, head) \
+ 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..0b41907
--- /dev/null
+++ b/include/dm/util.h
@@ -0,0 +1,42 @@
+/*
+ * (C) Copyright 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.2.1
^ permalink raw reply related [flat|nested] 17+ messages in thread* [U-Boot] [RFC PATCH v2 07/14] dm: Set up driver model after relocation
2013-05-07 19:41 [U-Boot] [RFC PATCH v2 0/14] Driver model implementation, tests, demo and GPIO Simon Glass
` (5 preceding siblings ...)
2013-05-07 19:42 ` [U-Boot] [RFC PATCH v2 06/14] dm: Add base driver model support Simon Glass
@ 2013-05-07 19:42 ` Simon Glass
2013-05-07 19:42 ` [U-Boot] [RFC PATCH v2 08/14] dm: Add basic tests Simon Glass
` (7 subsequent siblings)
14 siblings, 0 replies; 17+ messages in thread
From: Simon Glass @ 2013-05-07 19:42 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 v2: None
common/board_r.c | 33 +++++++++++++++++++++++++++++++++
common/dm/lists.c | 2 ++
common/dm/root.c | 2 ++
3 files changed, 37 insertions(+)
diff --git a/common/board_r.c b/common/board_r.c
index f801e41..de1768b 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;
@@ -765,6 +795,9 @@ init_fnc_t init_sequence_r[] = {
#endif
initr_barrier,
initr_malloc,
+#ifdef CONFIG_DM
+ initr_dm,
+#endif
#ifdef CONFIG_ARCH_EARLY_INIT_R
arch_early_init_r,
#endif
diff --git a/common/dm/lists.c b/common/dm/lists.c
index 02bcb50..ec7825b 100644
--- a/common/dm/lists.c
+++ b/common/dm/lists.c
@@ -110,6 +110,7 @@ int lists_bind_drivers(struct device *parent)
return result;
}
+#ifdef CONFIG_OF_CONTROL
/**
* driver_check_compatible() - Check if a driver is compatible with this node
*
@@ -179,3 +180,4 @@ int lists_bind_fdt(struct device *parent, const void *blob, int offset)
return result;
}
+#endif
diff --git a/common/dm/root.c b/common/dm/root.c
index bba793a..b6733dd 100644
--- a/common/dm/root.c
+++ b/common/dm/root.c
@@ -92,6 +92,7 @@ int dm_scan_platform_data(void)
return 0;
}
+#ifdef CONFIG_OF_CONTROL
int dm_scan_fdt(const void *blob)
{
int offset = 0;
@@ -112,6 +113,7 @@ int dm_scan_fdt(const void *blob)
return ret;
}
+#endif
U_BOOT_DRIVER(root_driver) = {
.name = "root_driver",
--
1.8.2.1
^ permalink raw reply related [flat|nested] 17+ messages in thread* [U-Boot] [RFC PATCH v2 08/14] dm: Add basic tests
2013-05-07 19:41 [U-Boot] [RFC PATCH v2 0/14] Driver model implementation, tests, demo and GPIO Simon Glass
` (6 preceding siblings ...)
2013-05-07 19:42 ` [U-Boot] [RFC PATCH v2 07/14] dm: Set up driver model after relocation Simon Glass
@ 2013-05-07 19:42 ` Simon Glass
2013-05-07 19:42 ` [U-Boot] [RFC PATCH v2 09/14] dm: Add a 'dm' command for testing Simon Glass
` (6 subsequent siblings)
14 siblings, 0 replies; 17+ messages in thread
From: Simon Glass @ 2013-05-07 19:42 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 v2: None
Makefile | 1 +
include/configs/sandbox.h | 1 +
include/dm/test.h | 132 +++++++++++
include/dm/ut.h | 83 +++++++
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 | 118 ++++++++++
test/dm/test.dts | 59 +++++
test/dm/ut.c | 46 ++++
12 files changed, 1476 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 8a29b0c..45970d1 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 c93970a..a212b14 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..956f93b
--- /dev/null
+++ b/include/dm/test.h
@@ -0,0 +1,132 @@
+/*
+ * Copyright (c) 2013 The Chromium OS Authors.
+ * See file CREDITS for list of people who contributed to this
+ * project.
+ *
+ * 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
+ *
+ */
+struct dm_test_pdata {
+ int ping_add;
+ uint32_t base;
+ uint32_t size;
+};
+
+struct test_ops {
+ int (*ping)(struct device *dev, int pingval, int *pingret);
+};
+
+/* Operations that our 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,
+};
+
+enum {
+ DM_TEST_TYPE_FIRST = 0,
+ DM_TEST_TYPE_SECOND,
+};
+
+#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 */
+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.
+ */
+struct dm_test_state {
+ struct device *root;
+ struct device *testdev;
+ int fail_count;
+ int force_fail_alloc; /* Force all memory allocs to fail */
+ int skip_post_probe; /* Skip uclass post-probe processing */
+};
+
+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 {
+ const char *name;
+ int (*func)(struct dm_test_state *dms);
+ int flags;
+};
+
+#define DM_TEST(_name, _flags) \
+ ll_entry_declare(struct dm_test, _name, dm_test) = { \
+ .name = #_name, \
+ .flags = _flags, \
+ .func = _name, \
+ }
+
+int test_ping(struct device *dev, int pingval, int *pingret);
+int testfdt_ping(struct device *dev, int pingval, int *pingret);
+
+int dm_check_operations(struct dm_test_state *dms, struct device *dev,
+ uint32_t base, struct dm_test_priv *priv);
+
+int dm_test_main(void);
+
+#endif
diff --git a/include/dm/ut.h b/include/dm/ut.h
new file mode 100644
index 0000000..0fbb19b
--- /dev/null
+++ b/include/dm/ut.h
@@ -0,0 +1,83 @@
+/*
+ * 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;
+
+void ut_fail(struct dm_test_state *dms, const char *fname, int line,
+ const char *func, const char *cond);
+
+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)));
+
+
+#define ut_assert(cond) \
+ if (!(cond)) { \
+ ut_fail(dms, __FILE__, __LINE__, __func__, #cond); \
+ return -1; \
+ }
+
+#define ut_assertf(cond, fmt, args...) \
+ if (!(cond)) { \
+ ut_failf(dms, __FILE__, __LINE__, __func__, #cond, \
+ fmt, ##args); \
+ return -1; \
+ }
+
+#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; \
+ } \
+}
+
+#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; \
+ } \
+}
+
+#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; \
+ } \
+}
+
+#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..916b142
--- /dev/null
+++ b/test/dm/Makefile
@@ -0,0 +1,52 @@
+#
+# (C) Copyright 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..283f8b4
--- /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..4600506
--- /dev/null
+++ b/test/dm/test-driver.c
@@ -0,0 +1,159 @@
+/*
+ * (C) Copyright 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_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_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..2a7312e
--- /dev/null
+++ b/test/dm/test-fdt.c
@@ -0,0 +1,148 @@
+/*
+ * (C) Copyright 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_size = sizeof(struct dm_test_priv),
+ .platform_data_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..fe17710
--- /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..a62c938
--- /dev/null
+++ b/test/dm/test-uclass.c
@@ -0,0 +1,118 @@
+/*
+ * (C) Copyright 2012
+ * Pavel Herrmann <morpheus.ibis@gmail.com>
+ *
+ * See file CREDITS for list of people who contributed to this
+ * project.
+ *
+ * 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_size = sizeof(struct dm_test_uclass_priv),
+ .per_device_priv_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..45a3a69
--- /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.2.1
^ permalink raw reply related [flat|nested] 17+ messages in thread* [U-Boot] [RFC PATCH v2 09/14] dm: Add a 'dm' command for testing
2013-05-07 19:41 [U-Boot] [RFC PATCH v2 0/14] Driver model implementation, tests, demo and GPIO Simon Glass
` (7 preceding siblings ...)
2013-05-07 19:42 ` [U-Boot] [RFC PATCH v2 08/14] dm: Add basic tests Simon Glass
@ 2013-05-07 19:42 ` Simon Glass
2013-05-07 19:42 ` [U-Boot] [RFC PATCH v2 10/14] dm: Add a demonstration/example driver Simon Glass
` (5 subsequent siblings)
14 siblings, 0 replies; 17+ messages in thread
From: Simon Glass @ 2013-05-07 19:42 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 v2: None
include/configs/sandbox.h | 1 +
test/dm/Makefile | 1 +
test/dm/cmd_dm.c | 147 ++++++++++++++++++++++++++++++++++++++++++++++
3 files changed, 149 insertions(+)
create mode 100644 test/dm/cmd_dm.c
diff --git a/include/configs/sandbox.h b/include/configs/sandbox.h
index a212b14..bf92a37 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 916b142..911a9b6 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..09321d9
--- /dev/null
+++ b/test/dm/cmd_dm.c
@@ -0,0 +1,147 @@
+/*
+ * (C) Copyright 2012
+ * Marek Vasut <marex@denx.de>
+ *
+ * See file CREDITS for list of people who contributed to this
+ * project.
+ *
+ * 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.2.1
^ permalink raw reply related [flat|nested] 17+ messages in thread* [U-Boot] [RFC PATCH v2 10/14] dm: Add a demonstration/example driver
2013-05-07 19:41 [U-Boot] [RFC PATCH v2 0/14] Driver model implementation, tests, demo and GPIO Simon Glass
` (8 preceding siblings ...)
2013-05-07 19:42 ` [U-Boot] [RFC PATCH v2 09/14] dm: Add a 'dm' command for testing Simon Glass
@ 2013-05-07 19:42 ` Simon Glass
2013-05-07 19:42 ` [U-Boot] [RFC PATCH v2 11/14] dm: Add GPIO support and tests Simon Glass
` (4 subsequent siblings)
14 siblings, 0 replies; 17+ messages in thread
From: Simon Glass @ 2013-05-07 19:42 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 v2: None
Makefile | 1 +
common/Makefile | 1 +
common/cmd_demo.c | 118 +++++++++++++++++++++++++++++++++++++++++++++
drivers/demo/Makefile | 44 +++++++++++++++++
drivers/demo/demo-pdata.c | 60 +++++++++++++++++++++++
drivers/demo/demo-shape.c | 107 ++++++++++++++++++++++++++++++++++++++++
drivers/demo/demo-simple.c | 46 ++++++++++++++++++
drivers/demo/demo-uclass.c | 54 +++++++++++++++++++++
include/configs/sandbox.h | 4 ++
include/dm-demo.h | 47 ++++++++++++++++++
10 files changed, 482 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 45970d1..89da309 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 0e0fff1..680ad01 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..97412d6
--- /dev/null
+++ b/common/cmd_demo.c
@@ -0,0 +1,118 @@
+/*
+ * Copyright (C) 2013 Google, Inc
+ *
+ * (C) Copyright 2012
+ * Pavel Herrmann <morpheus.ibis@gmail.com>
+ *
+ * See file CREDITS for list of people who contributed to this
+ * project.
+ *
+ * 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 = '@';
+
+ 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"
+ "hello <num> Say hello\n"
+ "status <num> Get demo device status"
+);
diff --git a/drivers/demo/Makefile b/drivers/demo/Makefile
new file mode 100644
index 0000000..a3648f5
--- /dev/null
+++ b/drivers/demo/Makefile
@@ -0,0 +1,44 @@
+# See file CREDITS for list of people who contributed to this
+# project.
+#
+# 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..48ca4e8
--- /dev/null
+++ b/drivers/demo/demo-pdata.c
@@ -0,0 +1,60 @@
+/*
+ * (C) Copyright 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..db31747
--- /dev/null
+++ b/drivers/demo/demo-shape.c
@@ -0,0 +1,107 @@
+/*
+ * (C) Copyright 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 <malloc.h>
+#include <dm-demo.h>
+#include <asm/io.h>
+
+/* 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;
+
+ 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,
+};
+
+U_BOOT_DRIVER(demo_shape_drv) = {
+ .name = "demo_shape_drv",
+ .id = UCLASS_DEMO,
+ .ops = &simple_ops,
+ .priv_size = sizeof(struct shape_data),
+};
diff --git a/drivers/demo/demo-simple.c b/drivers/demo/demo-simple.c
new file mode 100644
index 0000000..f8bee08
--- /dev/null
+++ b/drivers/demo/demo-simple.c
@@ -0,0 +1,46 @@
+/*
+ * (C) Copyright 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 '%c' from %08x: %s %d\n", ch, map_to_sysmem(dev),
+ pdata->colour, pdata->sides);
+
+ return 0;
+}
+
+static const struct demo_ops simple_ops = {
+ .hello = simple_hello,
+};
+
+U_BOOT_DRIVER(demo_simple_drv) = {
+ .name = "demo_simple_drv",
+ .id = UCLASS_DEMO,
+ .ops = &simple_ops,
+};
diff --git a/drivers/demo/demo-uclass.c b/drivers/demo/demo-uclass.c
new file mode 100644
index 0000000..7a1dde1
--- /dev/null
+++ b/drivers/demo/demo-uclass.c
@@ -0,0 +1,54 @@
+/*
+ * (C) Copyright 2012
+ * Pavel Herrmann <morpheus.ibis@gmail.com>
+ *
+ * See file CREDITS for list of people who contributed to this
+ * project.
+ *
+ * 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 <malloc.h>
+#include <asm/io.h>
+#include <linux/list.h>
+
+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);
+}
diff --git a/include/configs/sandbox.h b/include/configs/sandbox.h
index bf92a37..bd29a54 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..7dddd8a
--- /dev/null
+++ b/include/dm-demo.h
@@ -0,0 +1,47 @@
+/*
+ * Copyright (c) 2013 Google, Inc
+ * See file CREDITS for list of people who contributed to this
+ * project.
+ *
+ * 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
+ */
+struct dm_demo_pdata {
+ const char *colour;
+ int sides;
+};
+
+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);
+
+#endif
--
1.8.2.1
^ permalink raw reply related [flat|nested] 17+ messages in thread* [U-Boot] [RFC PATCH v2 11/14] dm: Add GPIO support and tests
2013-05-07 19:41 [U-Boot] [RFC PATCH v2 0/14] Driver model implementation, tests, demo and GPIO Simon Glass
` (9 preceding siblings ...)
2013-05-07 19:42 ` [U-Boot] [RFC PATCH v2 10/14] dm: Add a demonstration/example driver Simon Glass
@ 2013-05-07 19:42 ` Simon Glass
2013-05-07 19:42 ` [U-Boot] [RFC PATCH v2 12/14] sandbox: Convert GPIOs to use driver model Simon Glass
` (3 subsequent siblings)
14 siblings, 0 replies; 17+ messages in thread
From: Simon Glass @ 2013-05-07 19:42 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 drivers
are converted over
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, which each
GPIO device pricing 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 v2: None
drivers/gpio/Makefile | 2 +
drivers/gpio/gpio-uclass.c | 281 +++++++++++++++++++++++++++++++++++++++++++++
include/asm-generic/gpio.h | 50 ++++++++
test/dm/gpio.c | 124 ++++++++++++++++++++
4 files changed, 457 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 9df1e26..0605fbf 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..8289b62
--- /dev/null
+++ b/drivers/gpio/gpio-uclass.c
@@ -0,0 +1,281 @@
+/*
+ * Copyright (c) 2013 Google, Inc
+ * See file CREDITS for list of people who contributed to this
+ * project.
+ *
+ * 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_priv_size = sizeof(struct gpio_dev_priv),
+};
diff --git a/include/asm-generic/gpio.h b/include/asm-generic/gpio.h
index bfedbe4..bc237dd 100644
--- a/include/asm-generic/gpio.h
+++ b/include/asm-generic/gpio.h
@@ -94,4 +94,54 @@ 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;
+
+/*
+ * Driver model GPIO operations, refer to functions above for description.
+ * These function 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.
+ */
+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 {
+ const char *bank_name;
+ unsigned gpio_count;
+ unsigned gpio_base;
+};
+
+#define gpio_get_ops(dev) ((struct dm_gpio_ops *)(dev)->driver->ops)
+
+const char *gpio_get_bank_info(struct device *dev, int *offset_count);
+
+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.2.1
^ permalink raw reply related [flat|nested] 17+ messages in thread* [U-Boot] [RFC PATCH v2 12/14] sandbox: Convert GPIOs to use driver model
2013-05-07 19:41 [U-Boot] [RFC PATCH v2 0/14] Driver model implementation, tests, demo and GPIO Simon Glass
` (10 preceding siblings ...)
2013-05-07 19:42 ` [U-Boot] [RFC PATCH v2 11/14] dm: Add GPIO support and tests Simon Glass
@ 2013-05-07 19:42 ` Simon Glass
2013-05-07 19:42 ` [U-Boot] [RFC PATCH v2 13/14] dm: Enable gpio command to support " Simon Glass
` (2 subsequent siblings)
14 siblings, 0 replies; 17+ messages in thread
From: Simon Glass @ 2013-05-07 19:42 UTC (permalink / raw)
To: u-boot
Convert sandbox over to use driver model GPIOs.
Signed-off-by: Simon Glass <sjg@chromium.org>
---
Changes in v2: None
arch/sandbox/include/asm/gpio.h | 14 +--
board/sandbox/sandbox/sandbox.c | 7 +-
drivers/gpio/sandbox.c | 216 +++++++++++++++++++++++++---------------
include/configs/sandbox.h | 1 +
4 files changed, 150 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..de18d17 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,184 @@ 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;
+
+ debug("%s: offset:%u, label:%s\n", __func__, offset, label);
- if (gp >= ARRAY_SIZE(state)) {
- printf("sandbox_gpio: error: invalid gpio %u\n", gp);
+ 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;
- if (check_reserved(gp, __func__))
+ debug("%s: offset:%u\n", __func__, offset);
+
+ 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 int gpio_sandbox_remove(struct device *dev)
+{
+ 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,
+ .remove = gpio_sandbox_remove,
+ .ops = &gpio_sandbox_ops,
+};
diff --git a/include/configs/sandbox.h b/include/configs/sandbox.h
index bd29a54..9866231 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.2.1
^ permalink raw reply related [flat|nested] 17+ messages in thread* [U-Boot] [RFC PATCH v2 13/14] dm: Enable gpio command to support driver model
2013-05-07 19:41 [U-Boot] [RFC PATCH v2 0/14] Driver model implementation, tests, demo and GPIO Simon Glass
` (11 preceding siblings ...)
2013-05-07 19:42 ` [U-Boot] [RFC PATCH v2 12/14] sandbox: Convert GPIOs to use driver model Simon Glass
@ 2013-05-07 19:42 ` Simon Glass
2013-05-07 19:42 ` [U-Boot] [RFC PATCH v2 14/14] dm: Add README for " Simon Glass
2013-05-17 4:17 ` [U-Boot] [RFC PATCH v2 0/14] Driver model implementation, tests, demo and GPIO Simon Glass
14 siblings, 0 replies; 17+ messages in thread
From: Simon Glass @ 2013-05-07 19:42 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 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.2.1
^ permalink raw reply related [flat|nested] 17+ messages in thread* [U-Boot] [RFC PATCH v2 14/14] dm: Add README for driver model
2013-05-07 19:41 [U-Boot] [RFC PATCH v2 0/14] Driver model implementation, tests, demo and GPIO Simon Glass
` (12 preceding siblings ...)
2013-05-07 19:42 ` [U-Boot] [RFC PATCH v2 13/14] dm: Enable gpio command to support " Simon Glass
@ 2013-05-07 19:42 ` Simon Glass
2013-05-17 4:17 ` [U-Boot] [RFC PATCH v2 0/14] Driver model implementation, tests, demo and GPIO Simon Glass
14 siblings, 0 replies; 17+ messages in thread
From: Simon Glass @ 2013-05-07 19:42 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 v2:
- Removed pointer return values in favour of integer
- Use driver_bind() in dm_init() instead of writing new code
- Allow driver_bind() to support a NULL parent
- Add dm_warn() to warn about impending doom
- Standardise variable names (e.g. uclass instead of class)
- Remove relocation functions
- Add new header file for lists
- Add new util file to hold utility functions
- Allow a driver to bind to only one uclass
- Remove unneeded arguments to uclass_bind(), uclass_unbind()
- Rename struct device's 'bus' to 'parent'
- Rename data structures to hopefully be clearer
- Put platform_data definitions in their own header file
- Add U_BOOT_DEVICE to declare platform_data
- Add auto-probing feature for platform_data to avoid driver_bind() calls
- Add simple unit test functions
- Add test infrastructure for driver model
- Add integration tests for driver model
- Add device tree support in driver model
- Add automatic allocation of platform_data for FDT
- Add automatic allocation of priv data for devices
- Add automatic allocation of device-specific priv data for uclasses
- Add GPIO uclass and tests
- Add sandbox GPIO driver
- Update gpio command to use driver model
- Add tests for core code
- Add script to run tests
- Add a single include/dm.h to bring in driver model code
doc/driver-model/README.txt | 329 ++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 329 insertions(+)
create mode 100644 doc/driver-model/README.txt
diff --git a/doc/driver-model/README.txt b/doc/driver-model/README.txt
new file mode 100644
index 0000000..230d276
--- /dev/null
+++ b/doc/driver-model/README.txt
@@ -0,0 +1,329 @@
+Driver Model RFC
+================
+
+This README contains information about an early RFC implementation of
+driver model. The original work was done by:
+
+ Marek Vasut <marex@denx.de>
+ Pavel Herrmann <morpheus.ibis@gmail.com>
+ Viktor K?iv?k <viktor.krivak@gmail.com>
+ Tomas Hlavacek <tmshlvck@gmail.com>
+
+The code was taken from branch dm at:
+
+ git://git.denx.de/u-boot-marex.git
+
+and heavily modified. You can find a test version of the code used here in
+branch dm2 at:
+
+ http://git.denx.de/u-boot-x86.git
+
+(Branch dm contains the original implementation)
+
+
+The goal of this effort is to get a very basic implementation into
+the U-Boot source tree quickly - something which people can build on,
+rather than something that solves all of the problems immediately.
+The only driver support available is GPIO, because it is simple,
+but non-trivial.
+
+
+Terminology
+-----------
+
+Uclass - a group of device which operate in the same way. A uclass provides
+ a way of accessing invidual devices within the group, but always
+ using the same interface. For example a GPIO uclass provides
+ operations for get/set value. An I2C uclass may have 10 I2C ports,
+ 4 with one driver, and 6 with another.
+
+Driver - some code which talks to a peripheral and presents a unified
+ interface to it.
+
+Device - an instance of a driver, tied to a particular port or peripheral.
+
+
+How to try it
+-------------
+
+Build U-Boot sandbox and run it:
+
+ make sandbox_config
+ make
+ ./u-boot
+
+ (type 'reset' to exit U-Boot)
+
+
+There is a uclass called 'demo'. This uclass handles
+saying hello, and reporting its status. There are two drivers in this
+uclass:
+
+ - simple: Just prints a message for hello, doesn't implement status
+ - shape: Prints shapes and reports number of characters printed as status
+
+The demo class is pretty simple, but not trivial. The intention is that it
+can be used for testing, so it will implement all driver model features and
+provide 100% code coverage of them. It does have multiple drivers, it
+handles parameter data and platform_data (data which tells the driver how
+to operate on a particular platform) and it uses private driver data.
+
+To try it, see the example session below:
+
+=>demo hello 1
+Hello '@' from 07981110: red 4
+=>demo status 2
+Status: 0
+=>demo hello 2
+g
+r@
+e@@
+e@@@
+n@@@@
+g@@@@@
+=>demo status 2
+Status: 21
+=>demo hello 4 ^
+ y^^^
+ e^^^^^
+l^^^^^^^
+l^^^^^^^
+ o^^^^^
+ w^^^
+=>demo status 4
+Status: 36
+=>
+
+
+Running the tests
+-----------------
+
+The intent with driver model is that the core portion has 100% test coverage
+in sandbox, and every uclass has its own test. As a move towards this, tests
+are provided in test/dm. To run them, try:
+
+ ./test/dm/test-dm.sh
+
+
+What is going on?
+-----------------
+
+Let's start at the top. The demo command is in common/cmd_demo.c. It does
+the usual command procesing and then:
+
+ struct device *demo_dev;
+
+ ret = uclass_get_device(UCLASS_DEMO, &demo_dev);
+
+UCLASS_DEMO means the class of devices which implement 'demo'. Other
+classes might be MMC, or GPIO, hashing or serial. The idea is that the
+devices in the class all share a particular way of working. The class
+presents a unified view of all these devices to U-Boot.
+
+This function looks up the device for the demo uclass. Given a device
+number we can find the device because all devices have registered with
+the UCLASS_DEMO uclass.
+
+Having found the device, we activate it with:
+
+ ret = driver_activate(demo_dev);
+
+This is because all devices are inactive until used in U-Boot. The exact
+mechanism of activating a driver will hopefully change. For example, i
+may be possible to combine uclass_get_child() and driver_activate().
+
+Now that we have the device we can do things like:
+
+ return demo_hello(demo_dev, ch);
+
+This function is in the demo uclass. It takes care of calling the 'hello'
+method of the relevant driver. Bearing in mind that there are two drivers,
+this particular device may use one or other of them.
+
+The code for demo_hello() is in drivers/demo/demo-uclass.c:
+
+int demo_hello(struct device *dev, int ch)
+{
+ const struct demo_ops *ops = device_get_ops(dev);
+
+ if (!ops->hello)
+ return -ENOSYS;
+
+ return ops->hello(dev, ch);
+}
+
+As you can see it just calls the relevant driver method. One of these is
+in drivers/demo/demo-simple.c:
+
+static int simple_hello(struct device *dev, int ch)
+{
+ const struct dm_demo_pdata *pdata = dev->platform_data;
+
+ printf("Hello '%c' from %08x: %s %d\n", ch, map_to_sysmem(dev),
+ pdata->colour, pdata->sides);
+
+ return 0;
+}
+
+
+So that is a trip from top to bottom but it leaves a lot of topics to
+address.
+
+
+Declaring Drivers
+-----------------
+
+A driver declaration looks something like this (see
+drivers/demo/demo-shape.c):
+
+static const struct demo_ops simple_ops = {
+ .hello = shape_hello,
+ .status = shape_status,
+};
+
+U_BOOT_DRIVER(demo_shape_drv) = {
+ .name = "demo_shape_drv",
+ .id = UCLASS_DEMO,
+ .ops = &simple_ops,
+ .priv_data_size = sizeof(struct shape_data),
+};
+
+
+This driver has two methods (hello and status) and requires a bit
+of private data (accessible through dev->priv once the driver has
+been probed). It is a member of UCLASS_DEMO so will register itself
+there.
+
+In U_BOOT_DRIVER it is also possible to specify special methods for probe,
+and bind, and these are called at appropriate times. For many drivers
+it is hoped that only 'probe' and 'remove' will be needed.
+
+The U_BOOT_DRIVER macro creates a data structure accessible from C,
+so driver model can find the drivers that are available.
+
+
+Platform Data
+-------------
+
+Where does the platform data come from? See common/board_r.c which
+sets up a table of driver names and their associated platform data.
+The data can be interpreted by the drivers however they like - it is
+basically a communication scheme between the board-specific code and
+the generic drivers, which are intended to work on any board.
+
+Drivers can acceess their data via dev->info->platform_data. Here is
+the declaration for the platform data, which would normally appear
+in the board file.
+
+ static const struct dm_demo_cdata red_square = {
+ .colour = "red",
+ .sides = 4.
+ };
+ static const struct driver_info info[] = {
+ {
+ .name = "demo_shape_drv",
+ .platform_data = &red_square,
+ },
+ };
+
+ demo1 = driver_bind(root, &info[0]);
+
+
+Device Tree
+-----------
+
+This is now implemented. You can use device tree to specify the platform data
+and activated drivers. In other words we replace the above code with the
+following device treefragment:
+
+ red-square {
+ compatible = "demo-shape";
+ colour = "red";
+ sides = <4>;
+ };
+
+
+The easiest way to make this work it to add a platform_data_size member to
+the driver:
+
+ .platform_data_size = sizeof(struct dm_test_pdata),
+
+This will be allocated and zeroed before the driver's probe method is called.
+The driver can then read the information out of the device tree and put it
+in dev->priv.
+
+
+Declaring Uclasses
+------------------
+
+The demo uclass is declared like this:
+
+U_BOOT_CLASS(demo) = {
+ .id = UCLASS_DEMO,
+};
+
+It is also possible to specify special methods for probe, etc. The uclass
+numbering comes from include/dm/uclass.h.
+
+
+Data Structures
+---------------
+
+Driver model uses a doubly-linked list as the basic data structure. Some
+nodes have several lists running through them. Creating a more efficient
+data structure might be worthwhile in some rare cases, once we understand
+what the bottlenecks are.
+
+
+Changes so far
+--------------
+
+The documenation in this direction is out of data and until the patch
+is in more solid form I have avoided updating it. This implementation
+uses a very similar approach, but makes at least the following changes:
+
+- Tried to agressively remove boilerplate, so that for most drivers there
+is little or no 'driver model' code to write.
+- Moved some data from code into data structure - e.g. store a pointer to
+the driver operations structure in the driver, rather than passing it
+to the driver bind function.
+- Rename some structures to make them more similar to Linux (struct device
+instead of struct instance, struct platform_data, etc.)
+- Change the name 'core' to 'uclass', meaning U-Boot class. It seems that
+this concept relates to a class of drivers (or a subsystem). We shouldn't
+use 'class' since it is a C++ reserved word, so U-Boot class (uclass) seems
+better than 'core'.
+- Remove 'struct driver_instance' and just use a single 'struct device'.
+This removes a level of indirection that doesn't seem necessary.
+- Built in device tree support, to avoid the need for platform_data
+- Removed the concept of driver relocation, and just make it possible for
+the new driver (created after relocation) to access the old driver data.
+I feel that relocation is a very special case and will only apply to a few
+drivers, many of which can/will just re-init anyway. So the overhead of
+dealing with this might not be worth it.
+- Implemented a GPIO system, trying to keep it simple
+
+Proposed additional changes
+---------------------------
+
+- Maybe add device tree support to the demo driver
+- Update the documenation to match the new implementation
+
+
+Things to punt for later
+------------------------
+
+- SPL support - this will have to be present before many drivers can be
+converted, but it seems like we can add it once we are happy with the
+core implementation.
+- Pre-relocation support - similar story
+
+That is not to say that no thinking has gone into these - in fact there
+is quite a lot there. However, getting these right is non-trivial and
+there is a high cost associated with going down the wrong path.
+
+
+Simon Glass
+sjg at chromium.org
+April 2013
+Updated 7-May-13
--
1.8.2.1
^ permalink raw reply related [flat|nested] 17+ messages in thread* [U-Boot] [RFC PATCH v2 0/14] Driver model implementation, tests, demo and GPIO
2013-05-07 19:41 [U-Boot] [RFC PATCH v2 0/14] Driver model implementation, tests, demo and GPIO Simon Glass
` (13 preceding siblings ...)
2013-05-07 19:42 ` [U-Boot] [RFC PATCH v2 14/14] dm: Add README for " Simon Glass
@ 2013-05-17 4:17 ` Simon Glass
2013-05-17 5:08 ` Marek Vasut
14 siblings, 1 reply; 17+ messages in thread
From: Simon Glass @ 2013-05-17 4:17 UTC (permalink / raw)
To: u-boot
Hi,
On Tue, May 7, 2013 at 12:41 PM, Simon Glass <sjg@chromium.org> wrote:
>
> 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)
Does anyone have any comments on this series please?
Regards,
Simon
>
> 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 (13):
> 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
> 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
>
> Makefile | 4 +
> arch/sandbox/include/asm/gpio.h | 14 +-
> arch/sandbox/include/asm/io.h | 2 +-
> arch/sandbox/include/asm/types.h | 4 +-
> board/sandbox/sandbox/sandbox.c | 7 +-
> common/Makefile | 1 +
> common/board_r.c | 33 +++
> common/cmd_demo.c | 118 ++++++++
> common/cmd_gpio.c | 127 ++++++++-
> common/command.c | 10 +
> common/dm/Makefile | 40 +++
> common/dm/device.c | 370 +++++++++++++++++++++++++
> common/dm/lists.c | 183 +++++++++++++
> common/dm/root.c | 126 +++++++++
> common/dm/uclass.c | 362 +++++++++++++++++++++++++
> common/dm/util.c | 50 ++++
> doc/driver-model/README.txt | 329 ++++++++++++++++++++++
> drivers/demo/Makefile | 44 +++
> drivers/demo/demo-pdata.c | 60 ++++
> drivers/demo/demo-shape.c | 107 ++++++++
> drivers/demo/demo-simple.c | 46 ++++
> drivers/demo/demo-uclass.c | 54 ++++
> drivers/gpio/Makefile | 2 +
> drivers/gpio/gpio-uclass.c | 281 +++++++++++++++++++
> drivers/gpio/sandbox.c | 216 +++++++++------
> include/asm-generic/global_data.h | 9 +
> include/asm-generic/gpio.h | 50 ++++
> include/command.h | 9 +
> include/common.h | 2 +-
> include/configs/sandbox.h | 10 +-
> include/dm-demo.h | 47 ++++
> include/dm.h | 27 ++
> include/dm/device-internal.h | 38 +++
> include/dm/device.h | 72 +++++
> include/dm/lists.h | 37 +++
> include/dm/platform_data.h | 38 +++
> include/dm/root.h | 38 +++
> include/dm/test.h | 132 +++++++++
> include/dm/uclass-id.h | 42 +++
> include/dm/uclass-internal.h | 36 +++
> include/dm/uclass.h | 74 +++++
> include/dm/ut.h | 83 ++++++
> include/dm/util.h | 42 +++
> lib/fdtdec.c | 8 +-
> test/dm/Makefile | 53 ++++
> test/dm/cmd_dm.c | 147 ++++++++++
> 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 | 118 ++++++++
> test/dm/test.dts | 59 ++++
> test/dm/ut.c | 46 ++++
> 54 files changed, 4805 insertions(+), 110 deletions(-)
> 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 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.2.1
>
^ permalink raw reply [flat|nested] 17+ messages in thread* [U-Boot] [RFC PATCH v2 0/14] Driver model implementation, tests, demo and GPIO
2013-05-17 4:17 ` [U-Boot] [RFC PATCH v2 0/14] Driver model implementation, tests, demo and GPIO Simon Glass
@ 2013-05-17 5:08 ` Marek Vasut
0 siblings, 0 replies; 17+ messages in thread
From: Marek Vasut @ 2013-05-17 5:08 UTC (permalink / raw)
To: u-boot
Dear Simon Glass,
> Hi,
>
> On Tue, May 7, 2013 at 12:41 PM, Simon Glass <sjg@chromium.org> wrote:
> > 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)
>
> Does anyone have any comments on this series please?
I really cannot focus on much until 11th next month, sorry.
I should be able to review it after then ...
Best regards,
Marek Vasut
^ permalink raw reply [flat|nested] 17+ messages in thread