[U-Boot] [RFC PATCH] WIP: Simplified device model implementation and demo

Simon Glass sjg at chromium.org
Wed Apr 24 17:53:09 CEST 2013


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

** Please note that this is very early code. I am sending out an RFC to
get comments. This is not a commit message. This will only build on sandbox
and all you can do it try the 'demo' command. I am most interested in
comments about how to optimise the implementation to minimise impact on
existing drivers, minimise code bloat and make use of existing knowledge
users may have another other driver model implementations (e.g. Linux).

This patch adds a very simple drive model implementation. It is taken from
the driver model code developed by:

   Marek Vasut <marex at denx.de>
   Pavel Herrmann <morpheus.ibis at gmail.com>
   Viktor Křivák <viktor.krivak at gmail.com>
   Tomas Hlavacek <tmshlvck at gmail.com>

and possible others?

Please see doc/driver-model/README.txt for details of how to run this and
what to look for.

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)

This patch requires sandbox generic board support and other enhancements.

Signed-off-by: Simon Glass <sjg at chromium.org>
---
 Makefile                          |   3 +
 arch/sandbox/include/asm/io.h     |   2 +-
 arch/sandbox/include/asm/types.h  |   4 +-
 common/Makefile                   |   1 +
 common/board_r.c                  |  56 ++++++
 common/cmd_demo.c                 | 126 +++++++++++++
 common/command.c                  |  10 +
 common/dm/Makefile                |  40 ++++
 common/dm/debug.c                 | 137 ++++++++++++++
 common/dm/driver.c                | 374 ++++++++++++++++++++++++++++++++++++++
 common/dm/lists.c                 | 138 ++++++++++++++
 common/dm/root.c                  | 101 ++++++++++
 common/dm/tree.c                  |  71 ++++++++
 common/dm/uclass.c                | 227 +++++++++++++++++++++++
 doc/driver-model/README.txt       | 307 +++++++++++++++++++++++++++++++
 drivers/demo/Makefile             |  44 +++++
 drivers/demo/demo-shape.c         | 106 +++++++++++
 drivers/demo/demo-simple.c        |  82 +++++++++
 drivers/demo/demo-uclass.c        |  52 ++++++
 include/asm-generic/global_data.h |   9 +
 include/asm-generic/gpio.h        |  20 ++
 include/command.h                 |   2 +
 include/common.h                  |   2 +-
 include/config_fallbacks.h        |   8 +
 include/configs/sandbox.h         |   2 +
 include/dm-demo.h                 |  47 +++++
 include/dm.h                      |  27 +++
 include/dm/debug.h                |  33 ++++
 include/dm/manager.h              |  57 ++++++
 include/dm/structures.h           |  97 ++++++++++
 include/dm/uclass.h               |  62 +++++++
 31 files changed, 2243 insertions(+), 4 deletions(-)
 create mode 100644 common/cmd_demo.c
 create mode 100644 common/dm/Makefile
 create mode 100644 common/dm/debug.c
 create mode 100644 common/dm/driver.c
 create mode 100644 common/dm/lists.c
 create mode 100644 common/dm/root.c
 create mode 100644 common/dm/tree.c
 create mode 100644 common/dm/uclass.c
 create mode 100644 doc/driver-model/README.txt
 create mode 100644 drivers/demo/Makefile
 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
 create mode 100644 include/dm.h
 create mode 100644 include/dm/debug.h
 create mode 100644 include/dm/manager.h
 create mode 100644 include/dm/structures.h
 create mode 100644 include/dm/uclass.h

diff --git a/Makefile b/Makefile
index 42f2b02..7f6e007 100644
--- a/Makefile
+++ b/Makefile
@@ -337,6 +337,9 @@ LIBS-y += api/libapi.o
 LIBS-y += post/libpost.o
 LIBS-y += test/libtest.o
 
+LIBS-$(CONFIG_DM) += common/dm/libdm.o
+LIBS-$(CONFIG_DM) += drivers/demo/libdemo.o
+
 ifneq ($(CONFIG_AM33XX)$(CONFIG_OMAP34XX)$(CONFIG_OMAP44XX)$(CONFIG_OMAP54XX)$(CONFIG_TI814X),)
 LIBS-y += $(CPUDIR)/omap-common/libomap-common.o
 endif
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/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/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/board_r.c b/common/board_r.c
index f801e41..436ae83 100644
--- a/common/board_r.c
+++ b/common/board_r.c
@@ -66,7 +66,9 @@
 #ifdef CONFIG_X86
 #include <asm/init_helpers.h>
 #endif
+#include <dm-demo.h>
 #include <linux/compiler.h>
+#include <linux/err.h>
 
 DECLARE_GLOBAL_DATA_PTR;
 
@@ -269,6 +271,57 @@ static int initr_malloc(void)
 	return 0;
 }
 
+static int initr_dm(void)
+{
+	dm_init();
+	static const struct dm_demo_cdata red_square = {
+		.colour = "red",
+		.sides = 4.
+	};
+	static const struct dm_demo_cdata green_triangle = {
+		.colour = "green",
+		.sides = 3.
+	};
+	static const struct dm_demo_cdata yellow_hexagon = {
+		.colour = "yellow",
+		.sides = 6.
+	};
+	static const struct driver_info info[] = {
+		{
+			.name = "demo_shape_drv",
+			.platform_data = &red_square,
+		},
+		{
+			.name = "demo_simple_drv",
+			.platform_data = &red_square,
+		},
+		{
+			.name = "demo_shape_drv",
+			.platform_data = &green_triangle,
+		},
+		{
+			.name = "demo_simple_drv",
+			.platform_data = &yellow_hexagon,
+		},
+		{
+			.name = "demo_shape_drv",
+			.platform_data = &yellow_hexagon,
+		},
+	};
+	struct device *root = get_root_instance();
+	struct device *demo1;
+
+	demo1 = driver_bind(root, &info[0]);
+	if (IS_ERR(demo1))
+		printf("Demo bind failed: %ld\n", PTR_ERR(demo1));
+	driver_bind(demo1, &info[1]);
+	driver_bind(root, &info[2]);
+	driver_bind(root, &info[3]);
+	driver_bind(root, &info[4]);
+
+	return 0;
+}
+
 __weak int power_init_board(void)
 {
 	return 0;
@@ -765,6 +818,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/cmd_demo.c b/common/cmd_demo.c
new file mode 100644
index 0000000..657a541
--- /dev/null
+++ b/common/cmd_demo.c
@@ -0,0 +1,126 @@
+/*
+ * Copyright (C) 2013 Google, Inc
+ *
+ * (C) Copyright 2012
+ * Pavel Herrmann <morpheus.ibis at 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 uclass *class;
+	struct device *dev;
+	int i = 0;
+
+	class = class_get_instance(UCLASS_DEMO);
+	if (!class)
+		return -ENOMEM;
+
+	puts("Demo class entries:\n");
+
+	list_for_each_entry(dev, &class->class_node, class_node) {
+		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->info->platform_data));
+	}
+
+	return 0;
+}
+
+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);
+		demo_dev = uclass_get_child(UCLASS_DEMO, devnum);
+		if (!demo_dev) {
+			puts("Device not found\n");
+			return 1;
+		}
+
+		ret = driver_activate(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/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/common/dm/Makefile b/common/dm/Makefile
new file mode 100644
index 0000000..982a3d5
--- /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	:= debug.o driver.o lists.o tree.o root.o uclass.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/debug.c b/common/dm/debug.c
new file mode 100644
index 0000000..cb36fb4
--- /dev/null
+++ b/common/dm/debug.c
@@ -0,0 +1,137 @@
+/*
+ * (C) Copyright 2012
+ * Marek Vasut <marex at 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 <malloc.h>
+#include <errno.h>
+#include <asm/io.h>
+#include <dm/debug.h>
+#include <dm/manager.h>
+#include <dm/structures.h>
+#include <dm/uclass.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->info->name, map_to_sysmem(in));
+	if (in->flags & DM_FLAG_ACTIVATED)
+		puts(" - activated");
+	puts("\n");
+
+	if (list_empty(&in->succ))
+		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->succ, sibling) {
+		if (ip++)
+			display_succ(prev, local);
+		prev = pos;
+	}
+
+	snprintf(local + len, 2, "`");
+	display_succ(prev, local);
+
+	return 0;
+}
+
+int dm_dump_all()
+{
+	struct device *root;
+
+	root = get_root_instance();
+	printf("ROOT %08x\n", map_to_sysmem(root));
+	return dm_dump(root);
+}
+
+int dm_dump(struct device *dev)
+{
+	if (!dev)
+		return -EINVAL;
+	return display_succ(dev, "");
+}
+
+static int dm_dump_classs(void)
+{
+	struct uclass *class;
+	int count, num;
+	int id;
+
+	for (id = 0; id < UCLASS_COUNT; id++) {
+		class = class_get_instance(id);
+		if (!class)
+			continue;
+
+		count = uclass_get_count(id);
+		printf("class %d: %d instances\n", id, count);
+		for (num = 0; num < count; num++) {
+			struct device *inst;
+
+			inst = uclass_get_child(id, num);
+			printf("   %08x:\n", map_to_sysmem(inst));
+		}
+		puts("\n");
+	}
+
+	return 0;
+}
+
+static int do_dm(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
+{
+	struct device *root;
+
+	if (argc != 2)
+		return -EINVAL;
+	if (!strncmp(argv[1], "dump", 4))
+		return dm_dump_all();
+
+	if (!strncmp(argv[1], "remove", 6)) {
+		root = get_root_instance();
+		return driver_remove(root);
+	}
+
+	if (!strncmp(argv[1], "unbind", 6)) {
+		root = get_root_instance();
+		return driver_unbind(root);
+	}
+
+	if (!strcmp(argv[1], "class"))
+		return dm_dump_classs();
+
+	return -EINVAL;
+}
+
+U_BOOT_CMD(
+	dm,	2,	1,	do_dm,
+	"Driver model ops",
+	"dump         dump driver model tree\n"
+	"class         Dump list of instances for each class"
+);
diff --git a/common/dm/driver.c b/common/dm/driver.c
new file mode 100644
index 0000000..597feab
--- /dev/null
+++ b/common/dm/driver.c
@@ -0,0 +1,374 @@
+/*
+ * (C) Copyright 2013 Google, Inc
+ *
+ * (C) Copyright 2012
+ * Pavel Herrmann <morpheus.ibis at 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 <malloc.h>
+#include <dm/manager.h>
+#include <linux/err.h>
+#include <linux/list.h>
+
+/*
+ * List management functions
+ */
+
+/**
+ * driver_add_child() - Connect one driver under another
+ * @parent:	Parent driver, under which will the driver be connected
+ * @child:	Child driver, which is to be connected under the parent
+ */
+static void driver_add_child(struct device *parent, struct device *child)
+{
+	list_add_tail(&child->sibling, &parent->succ);
+}
+
+/**
+ * driver_remove_child() - Unbind driver's child from the driver
+ * @parent:	The parent device, from which the child is unbound
+ * @child:	The child device, which is being unbound
+ */
+static void driver_remove_child(struct device *parent, struct device *child)
+{
+	list_del(&child->sibling);
+}
+
+/**
+ * driver_driver_reloc() - Relocate driver's children
+ * @old:	Instance of the old driver
+ * @new:	Instance of the new driver
+ *
+ * Relocate the children of a driver. The supplied old device
+ * contains the list of a children, usually generated during the
+ * pre-relocation stage, whereas the new device contains no
+ * children nodes yet. The new device is usually created at
+ * the post-relocation stage. The nodes from old instance are relocated
+ * and connected under the new instance.
+ */
+static int driver_driver_reloc(struct device *old,
+				struct device *new)
+{
+	struct device *pos, *n;
+	struct device *inst;
+
+	list_for_each_entry_safe(pos, n, &old->succ, sibling) {
+		inst = driver_relocate(pos, new);
+		if (IS_ERR(inst))
+			return PTR_ERR(inst);
+		driver_add_child(new, inst);
+	}
+
+	return 0;
+}
+
+/**
+ * driver_chld_unbind() - Unbind all driver's children from the driver
+ * @dev:	The driver that is to be stripped of off children
+ */
+static int driver_chld_unbind(struct device *dev)
+{
+	struct device *pos, *n;
+	int ret;
+
+	if (!dev)
+		return -EINVAL;
+
+	if (list_empty(&dev->succ))
+		return 0;
+
+	list_for_each_entry_safe(pos, n, &dev->succ, sibling) {
+		ret = driver_unbind(pos);
+		if (ret)
+			return ret;
+	}
+
+	return 0;
+}
+
+/**
+ * driver_chld_remove() - Stop all driver's children
+ * @dev:	The driver whose children are to be stopped
+ */
+static int driver_chld_remove(struct device *dev)
+{
+	struct device *pos, *n;
+	int ret;
+
+	if (!dev)
+		return -EINVAL;
+
+	list_for_each_entry_safe(pos, n, &dev->succ, sibling) {
+		ret = driver_remove(pos);
+		if (ret)
+			return ret;
+	}
+
+	return 0;
+}
+
+/*
+ * Driver management functions
+ */
+
+/**
+ * driver_bind() - Instantiate and connect a driver under a parent one
+ * @parent:	The parent driver, under which the new driver will be connected
+ * @info:	Information about the driver to be probed
+ *
+ * Make a new instance of a driver based on data passed by the user. This
+ * instance is then connected under the parent driver. The driver is not yet
+ * started after being bound, it's only present in the driver tree.
+ *
+ * Returns the instance of a new driver on success, NULL on error.
+ */
+struct device *driver_bind(struct device *parent,
+			   const struct driver_info *info)
+{
+	struct device *dev;
+	struct u_boot_driver *drv;
+	int ret = 0;
+
+	if (!info || !parent) {
+		ret = -EINVAL;
+		goto fail;
+	}
+
+	dev = calloc(1, sizeof(struct device));
+	if (!dev) {
+		ret = -ENOMEM;
+		goto fail;
+	}
+
+	INIT_LIST_HEAD(&dev->sibling);
+	INIT_LIST_HEAD(&dev->succ);
+	INIT_LIST_HEAD(&dev->class_node);
+	dev->info = info;
+	dev->bus = parent;
+	drv = get_driver_by_instance(dev);
+
+	if (!drv) {
+		ret = -ENOENT;
+		goto fail_free;
+	}
+	dev->driver = drv;
+
+	/* put dev into parents successor list */
+	driver_add_child(parent, dev);
+
+	ret = uclass_bind(drv->id, 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)
+			goto fail_bind;
+	}
+
+	return dev;
+
+fail_bind:
+	driver_remove_child(parent, dev);
+fail_free:
+	free(dev);
+fail:
+	return ERR_PTR(ret);
+}
+
+/**
+ * driver_activate() - Start a driver
+ * @i:		Instance of a driver that is to be started
+ *
+ * This function runs the .probe() function, which initializes the hardware.
+ * The hardware and all it's predecessors are started by this function.
+ */
+int driver_activate(struct device *inst)
+{
+	struct u_boot_driver *drv;
+	int ret;
+
+	if (!inst)
+		return -EINVAL;
+
+	drv = get_driver_by_instance(inst);
+	if (!drv)
+		return -ENOENT;
+
+	if (inst->flags & DM_FLAG_ACTIVATED)
+		return 0;
+
+	/* Allocate private data if requested */
+	if (drv->priv_data_size) {
+		inst->priv = calloc(1, drv->priv_data_size);
+		if (!inst->priv)
+			return -ENOMEM;
+	}
+
+	if (inst->bus) {
+		ret = driver_activate(inst->bus);
+		if (ret)
+			goto err;
+	}
+
+	if (drv->probe) {
+		ret = drv->probe(inst);
+		if (ret)
+			goto err;
+	}
+
+	inst->flags |= DM_FLAG_ACTIVATED;
+	return 0;
+err:
+	if (drv->priv_data_size)
+		free(inst->priv);
+
+	return ret;
+}
+
+/**
+ * driver_relocate() - Relocate an instance of a driver
+ * @dev:	The instance of a driver to be relocated
+ * @bus:	The instance of a new parent of this driver
+ *
+ * Relocate an instance of a driver and it's children. The new
+ * instance of a driver is allocated and connected under the
+ * already-relocated parent/bus. The children of the old driver
+ * are relocated afterwards as well.
+ */
+struct device *driver_relocate(struct device *dev, struct device *bus)
+{
+	struct u_boot_driver *ops;
+	struct device *new_dev;
+	int ret;
+
+	if (!dev)
+		return NULL;
+
+	ops = get_driver_by_instance(dev);
+	if (!ops || !ops->reloc)
+		return NULL;
+
+	new_dev = calloc(1, sizeof(struct device));
+	if (!new_dev)
+		return NULL;
+
+	INIT_LIST_HEAD(&new_dev->sibling);
+	INIT_LIST_HEAD(&new_dev->succ);
+	new_dev->flags = dev->flags;
+	new_dev->bus = bus;
+	new_dev->info = dev->info;
+	/* FIXME: handle driver_info in dynamic memory */
+	new_dev->priv = NULL;
+
+	/* relocate the instance */
+	ret = ops->reloc(new_dev, dev);
+	if (ret)
+		goto err;
+
+	ret = driver_driver_reloc(dev, new_dev);
+	if (ret)
+		goto err;
+
+	return new_dev;
+
+err:
+	free(new_dev);
+	return NULL;
+};
+
+/**
+ * driver_unbind() - Unbind driver from it's parent
+ * @dev:	The driver to be unbound
+ *
+ * Disconnect a driver from it's parent. The driver must be deactived,
+ * otherwise this function fails and doesn't unbind the driver.
+ */
+int driver_unbind(struct device *dev)
+{
+	struct u_boot_driver *ops;
+	int ret;
+
+	if (!dev)
+		return -EINVAL;
+
+	if (dev->flags & DM_FLAG_ACTIVATED)
+		return -EINVAL;
+
+	ops = get_driver_by_instance(dev);
+	if (!ops)
+		return -EINVAL;
+
+	ret = driver_chld_unbind(dev);
+	if (ret)
+		return ret;
+
+	if (ops->unbind) {
+		ret = ops->unbind(dev);
+		if (ret)
+			return ret;
+	}
+
+	if (dev->bus)
+		driver_remove_child(dev->bus, dev);
+
+	return 0;
+}
+
+/**
+ * driver_remove() - Disable the driver and it's children
+ * @dev:	The instance of a driver to be disabled
+ *
+ * Deconfigure the hardware and all it's children.
+ */
+int driver_remove(struct device *dev)
+{
+	struct u_boot_driver *ops;
+	int ret;
+
+	if (!dev)
+		return -EINVAL;
+
+	if (!(dev->flags & DM_FLAG_ACTIVATED))
+		return 0;
+
+	ops = get_driver_by_instance(dev);
+	if (!ops)
+		return -EINVAL;
+
+	ret = driver_chld_remove(dev);
+	if (ret)
+		return ret;
+
+	if (ops->remove) {
+		ret = ops->remove(dev);
+		if (ret)
+			return ret;
+	}
+
+	if (dev->driver->priv_data_size)
+		free(dev->priv);
+
+	dev->flags &= ~DM_FLAG_ACTIVATED;
+
+	return ret;
+}
diff --git a/common/dm/lists.c b/common/dm/lists.c
new file mode 100644
index 0000000..f914143
--- /dev/null
+++ b/common/dm/lists.c
@@ -0,0 +1,138 @@
+/*
+ * (C) Copyright 2012
+ * Marek Vasut <marex at 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 <dm/manager.h>
+
+/**
+ * get_driver_by_instance() - Return u_boot_driver classsponding 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 u_boot_driver *get_driver_by_instance(struct device *i)
+{
+	struct u_boot_driver *drv =
+		ll_entry_start(struct u_boot_driver, driver);
+	const int n_ents = ll_entry_count(struct u_boot_driver, driver);
+	struct u_boot_driver *entry;
+	const char *name;
+	int len;
+
+	if (!i || !drv || !n_ents)
+		return NULL;
+
+	name = i->info->name;
+	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;
+}
+
+/**
+ * get_usb_driver_by_ids() - Return u_boot_driver based on USB IDs
+ * major:	USB major ID
+ * minor:	USB minor ID
+ *
+ * This function finds an u_boot_driver inside the U-Boot USB driver list
+ * based on the USB's major and minor IDs. Returns pointer to the driver on
+ * success, NULL on error.
+ */
+struct u_boot_driver *get_usb_driver_by_ids(uint16_t major, uint16_t minor)
+{
+	struct u_boot_driver *drv =
+		ll_entry_start(struct u_boot_driver, driver);
+	const int n_ents = ll_entry_count(struct u_boot_driver, driver);
+	struct u_boot_driver *entry;
+	struct usb_ids *ids;
+	int i;
+
+	if (!drv || !n_ents)
+		return NULL;
+
+	/* First walk the ID tables */
+	for (entry = drv; entry != drv + n_ents; entry++) {
+		i = 0;
+		ids = entry->aux_data;
+		for (;;) {
+			if (!ids->ids[i].major && !ids->ids[i].minor)
+				break;
+
+			if (ids->ids[i].major != major) {
+				i++;
+				continue;
+			}
+
+			if (ids->ids[i].minor != minor) {
+				i++;
+				continue;
+			}
+
+			return entry;
+		}
+	}
+
+	/* Next, try the match functions */
+	for (entry = drv; entry != drv + n_ents; entry++) {
+		ids = entry->aux_data;
+		if (ids->match && ids->match())
+			return entry;
+	}
+
+	/* No driver found */
+	return NULL;
+}
+
+/**
+ * get_class_by_id() - Return U_BOOT_CLASS based on ID of the class
+ * id:		ID of the class
+ *
+ * This function returns the pointer to U_BOOT_CLASS, which is the class's
+ * base structure based on the ID of the class. Returns NULL on error.
+ */
+struct u_boot_class *get_class_by_id(enum uclass_id id)
+{
+	struct u_boot_class *class =
+		ll_entry_start(struct u_boot_class, class);
+	const int n_ents = ll_entry_count(struct u_boot_class, class);
+	struct u_boot_class *entry;
+
+	if ((id == UCLASS_INVALID) || !class)
+		return NULL;
+
+	for (entry = class; entry != class + n_ents; entry++) {
+		if (entry->id == id)
+			return entry;
+	}
+
+	return NULL;
+}
diff --git a/common/dm/root.c b/common/dm/root.c
new file mode 100644
index 0000000..3a569ef
--- /dev/null
+++ b/common/dm/root.c
@@ -0,0 +1,101 @@
+/*
+ * (C) Copyright 2012
+ * Pavel Herrmann <morpheus.ibis at 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 <dm/structures.h>
+#include <dm/manager.h>
+#include <malloc.h>
+#include <errno.h>
+#include <linux/list.h>
+
+DECLARE_GLOBAL_DATA_PTR;
+
+static const struct driver_info root_info = {
+	.name		= "root_driver",
+};
+
+/**
+ * create_root_instance() - Instantiate the root driver
+ *
+ * Create instance of the root driver, the root of the driver tree.
+ * This root driver is pointed to by the global data. Returns 0 on
+ * success, negative value on error.
+ */
+static int create_root_instance(void)
+{
+	struct device *di;
+
+	if (gd->dm_root) {
+		puts("Virtual root driver already exists!\n");
+		return -EINVAL;
+	}
+
+	di = calloc(1, sizeof(struct device));
+	if (di == NULL)
+		return -ENOMEM;
+
+	INIT_LIST_HEAD(&di->sibling);
+	INIT_LIST_HEAD(&di->succ);
+	di->info = &root_info;
+	gd->dm_root = di;
+
+	return 0;
+}
+
+/**
+ * get_root_instance() - 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 *get_root_instance(void)
+{
+	if (!gd->dm_root) {
+		puts("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 retval = 0;
+
+	retval = create_root_instance();
+	if (retval)
+		hang();
+
+	INIT_LIST_HEAD(&gd->class_root);
+
+	return retval;
+}
+
+U_BOOT_DRIVER(__root_driver) = {
+	.name	= "root_driver",
+};
diff --git a/common/dm/tree.c b/common/dm/tree.c
new file mode 100644
index 0000000..bd08b06
--- /dev/null
+++ b/common/dm/tree.c
@@ -0,0 +1,71 @@
+/*
+ * (C) Copyright 2012
+ * Viktor Krivak <viktor.krivak at gmail.com>
+ * Pavel Herrmann <morpheus.ibis at 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 <dm/manager.h>
+#include <dm/structures.h>
+#include <linux/list.h>
+#include <malloc.h>
+#include <errno.h>
+
+DECLARE_GLOBAL_DATA_PTR;
+
+struct node {
+	struct list_head list;
+	enum uclass_id key;
+	struct uclass class;
+};
+
+/**
+ * classs_relocate() - Relocate classs from early init memory
+ *
+ * Allocate new memory for each class, copy key and call reloc function found
+ * in specific class ops. Return 0 if succeeded.
+ */
+int classs_relocate(void)
+{
+	struct u_boot_class *ops;
+	struct node *node;
+	struct node *new;
+	struct list_head new_head;
+	INIT_LIST_HEAD(&new_head);
+	list_for_each_entry(node, &gd->class_root, list) {
+		ops = get_class_by_id(node->key);
+		if (!ops)
+			panic("%s: No ops\n", __func__);
+		new = malloc(sizeof(*new));
+		if (!new)
+			panic("%s: Unable to allocate memory!", __func__);
+		new->key = node->key;
+		/* No reloc method. It is strage but not error */
+		if (!ops->reloc)
+			continue;
+		if (!ops->reloc(&new->class, &node->class))
+			list_add_tail(&new->list, &new_head);
+		else
+			panic("%s: Relocation of class fail!", __func__);
+	}
+	gd->class_root = new_head;
+	return 0;
+}
+
diff --git a/common/dm/uclass.c b/common/dm/uclass.c
new file mode 100644
index 0000000..97430b2
--- /dev/null
+++ b/common/dm/uclass.c
@@ -0,0 +1,227 @@
+/*
+ * (C) Copyright 2012
+ * Pavel Herrmann <morpheus.ibis at 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 <malloc.h>
+#include <dm/manager.h>
+
+DECLARE_GLOBAL_DATA_PTR;
+
+/**
+ * search() - Search class by key in list
+ * @key:	Key used to search apropriet class instance
+ *
+ * Linear search in list and if found something, replace it at beginning
+ * of the list and return class instance inside node.
+ * If nothing was found return NULL.
+ */
+static struct uclass *class_search(enum uclass_id key)
+{
+	struct uclass *inst;
+
+	list_for_each_entry(inst, &gd->class_root, succ) {
+		if (inst->dm_class->id == key)
+			return inst;
+	}
+	return NULL;
+}
+
+/**
+ * class_add_instance() - Create and add new class in list
+ * @key: Key used to init class instance
+ *
+ * Call init_node to create class instance and its container structure.
+ * After that add this class to list.
+ */
+static struct uclass *class_add_instance(enum uclass_id key)
+{
+	struct u_boot_class *cls;
+	struct uclass *inst;
+
+	cls = get_class_by_id(key);
+	if (!cls)
+		panic("Cannot find class for id %d\n", key);
+	inst = calloc(1, sizeof(*inst));
+	inst->dm_class = cls;
+	INIT_LIST_HEAD(&inst->succ);
+	INIT_LIST_HEAD(&inst->class_node);
+	list_add(&inst->succ, &gd->class_root);
+
+	return inst;
+}
+
+/**
+ * class_get_instance() - Return or create class by key
+ * @key:	Search class with this key or create new one
+ *
+ * Try to find class in list structure. If fail create a new class and place it
+ * in list. If fail to create new class, return NULL. Otherwise return
+ * pointer to designated class.
+ */
+struct uclass *class_get_instance(enum uclass_id key)
+{
+	struct uclass *instance;
+
+	instance = class_search(key);
+	if (!instance)
+		instance = class_add_instance(key);
+	return instance;
+}
+
+/**
+ * uclass_get_count() - Return number of registered children with class
+ * @id:		ID of the class
+ *
+ * Count the number of members registered with this class and return
+ * the value. Negative value is returned in case of failure.
+ */
+int uclass_get_count(enum uclass_id id)
+{
+	struct uclass *class;
+	struct device *inst;
+	int count = 0;
+
+	class = class_get_instance(id);
+	if (!class)
+		return -ENOMEM;
+
+	list_for_each_entry(inst, &class->class_node, class_node)
+		count++;
+
+	return count;
+}
+
+/**
+ * uclass_get_child() - Return n-th child of class
+ * @id:		ID of the class
+ * @index:	Position of the child in class's list
+ *
+ * Return the instance pointer of a child at the given index or
+ * return NULL on error.
+ */
+struct device *uclass_get_child(enum uclass_id id, int index)
+{
+	struct uclass *class;
+	struct device *inst;
+
+	class = class_get_instance(id);
+	if (!class)
+		return NULL;
+
+	list_for_each_entry(inst, &class->class_node, class_node) {
+		if (!index--)
+			return inst;
+	}
+
+	return NULL;
+}
+
+/**
+ * uclass_bind() - Associate driver with a class
+ * @id:		ID of the class
+ * @dev:	Pointer to the driver's instance
+ * @ops:	Structure carrying operations this driver provides to the class
+ *
+ * Connect the driver into class's list of drivers. Returns 0 on success,
+ * negative value on error.
+ */
+int uclass_bind(enum uclass_id id, struct device *dev)
+{
+	struct uclass *class;
+	struct u_boot_class *class_ops;
+	int ret;
+
+	class = class_get_instance(id);
+	if (!class)
+		return -ENOMEM;
+
+	class_ops = get_class_by_id(id);
+	if (!class_ops)
+		return -ENOENT;
+	if (class_ops->ops)
+		return -EINVAL;
+
+	list_add_tail(&dev->class_node, &class->class_node);
+
+	if (class_ops->bind) {
+		ret = class_ops->bind(class, dev);
+		if (ret) {
+			list_del(&dev->class_node);
+			return ret;
+		}
+	}
+
+	return 0;
+}
+
+/**
+ * uclass_unbind() - Deassociate driver with a class
+ * @id:		ID of the class
+ * @dev:	Pointer to the driver's instance
+ *
+ * Disconnect the driver from class's list of drivers.
+ * Returns 0 on success, negative value on error.
+ */
+int uclass_unbind(enum uclass_id id, struct device *dev)
+{
+	struct uclass *class;
+	struct u_boot_class *class_ops;
+
+	class = class_get_instance(id);
+	if (!class)
+		return -ENOMEM;
+
+	class_ops = get_class_by_id(id);
+	if (!class_ops)
+		return -ENOENT;
+
+	return class_ops->unbind(class, dev);
+}
+
+/**
+ * uclass_replace() - Replace instance of a driver with another in class's list
+ * @id:		ID of the class
+ * @new:	Pointer to the driver's new instance
+ * @old:	Pointer to the driver's old instance
+ *
+ * Replace old instance of a driver, usually pre-relocation one, in the list
+ * of drivers associated with a class with a new one, usually a post-relocation
+ * one. All of the internal state of the class associated with the old instance
+ * is preserved.
+ *
+ * Returns 0 on success, negative value on error.
+ */
+int uclass_replace(enum uclass_id id, struct device *new, struct device *old)
+{
+	struct uclass *class;
+	struct u_boot_class *class_ops;
+
+	class = class_get_instance(id);
+	if (!class)
+		return -ENOMEM;
+
+	class_ops = get_class_by_id(id);
+	if (!class_ops)
+		return -ENOENT;
+
+	return class_ops->replace(class, new, old);
+}
diff --git a/doc/driver-model/README.txt b/doc/driver-model/README.txt
new file mode 100644
index 0000000..e077220
--- /dev/null
+++ b/doc/driver-model/README.txt
@@ -0,0 +1,307 @@
+Driver Model RFC
+================
+
+This README contains information about an early RFC implementation of
+driver model. The original work was done by:
+
+   Marek Vasut <marex at denx.de>
+   Pavel Herrmann <morpheus.ibis at gmail.com>
+   Viktor Křivák <viktor.krivak at gmail.com>
+   Tomas Hlavacek <tmshlvck at gmail.com>
+
+The code was taken from branch dm at:
+
+   git://git.denx.de/u-boot-marex.git
+
+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 that I am planning to add is GPIO, because
+it is simple, but non-trivial, and much of the work is already done
+by the people above.
+
+NOTE: This code is an early incoherent, inconsistent and largely
+incorrect draft. Please use it for making comments on the approach.
+Some knowledge of the existing driver model design is helpful.
+
+NOTE: Please read the above note again.
+
+
+How to try it
+-------------
+
+Build U-Boot sandbox and run it:
+
+   make sandbox_config
+   make
+   ./u-boot
+
+   (type 'reset' to exit U-Boot)
+
+
+The only available device class (uclass) is '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
+probe from 07981110
+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
+=>
+
+
+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;
+
+	demo_dev = uclass_get_child(UCLASS_DEMO, devnum);
+
+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_cdata *pdata = dev->info->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 not yet implemented but will be before this work leaves RFC status.
+We will use device tree to specify the platform data and activated drivers.
+In other words we replace the above code with the following device tree
+fragment:
+
+	red-square {
+		compatible = "demo-shape";
+		colour = "red";
+		sides = <4>;
+	};
+
+
+Driver model will look after creating a new device and feeding the device
+node into it. The driver itself will decode the device tree node and
+produce its own platform_data from it.
+
+
+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.
+
+
+Proposed additional changes
+---------------------------
+
+- Build in device tree support, to avoid the need for platform_data
+- Possibly remove 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. This is TBD
+though.
+- Implement for GPIO subsystem. An implementation is already done in the
+driver model tree
+
+
+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
diff --git a/drivers/demo/Makefile b/drivers/demo/Makefile
new file mode 100644
index 0000000..60a2b88
--- /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
+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-shape.c b/drivers/demo/demo-shape.c
new file mode 100644
index 0000000..b72fcba
--- /dev/null
+++ b/drivers/demo/demo-shape.c
@@ -0,0 +1,106 @@
+/*
+ * (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 <malloc.h>
+#include <dm-demo.h>
+#include <asm/io.h>
+#include <dm/manager.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_cdata *pdata = dev->info->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_data_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..e0c55f3
--- /dev/null
+++ b/drivers/demo/demo-simple.c
@@ -0,0 +1,82 @@
+/*
+ * (C) Copyright 2013 Google, Inc
+ *
+ * (C) Copyright 2012
+ * Pavel Herrmann <morpheus.ibis at gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
+ * MA 02111-1307 USA
+ */
+
+#include <common.h>
+#include <dm-demo.h>
+#include <asm/io.h>
+#include <dm/manager.h>
+
+static int simple_hello(struct device *dev, int ch)
+{
+	const struct dm_demo_cdata *pdata = dev->info->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,
+};
+
+static int simple_bind(struct device *dev)
+{
+	printf("bind from %08x\n", map_to_sysmem(dev));
+	return 0;
+}
+
+static int simple_probe(struct device *dev)
+{
+	printf("probe from %08x\n", map_to_sysmem(dev));
+	return 0;
+}
+
+static int simple_reloc(struct device *new, struct device *old)
+{
+	printf("reloc to %08x from %08x\n", map_to_sysmem(new),
+	       map_to_sysmem(old));
+	return 0;
+}
+
+static int simple_remove(struct device *dev)
+{
+	printf("remove from %08x\n", map_to_sysmem(dev));
+	return 0;
+}
+
+static int simple_unbind(struct device *dev)
+{
+	printf("unbind from %08x\n", map_to_sysmem(dev));
+	return 0;
+}
+
+U_BOOT_DRIVER(demo_simple_drv) = {
+	.name	= "demo_simple_drv",
+	.id	= UCLASS_DEMO,
+	.ops	= &simple_ops,
+	.bind	= simple_bind,
+	.probe	= simple_probe,
+	.reloc	= simple_reloc,
+	.remove	= simple_remove,
+	.unbind	= simple_unbind,
+};
diff --git a/drivers/demo/demo-uclass.c b/drivers/demo/demo-uclass.c
new file mode 100644
index 0000000..a5a3367
--- /dev/null
+++ b/drivers/demo/demo-uclass.c
@@ -0,0 +1,52 @@
+/*
+ * (C) Copyright 2012
+ * Pavel Herrmann <morpheus.ibis at 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 <malloc.h>
+#include <dm-demo.h>
+#include <asm/io.h>
+#include <dm/manager.h>
+#include <linux/list.h>
+
+U_BOOT_CLASS(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/asm-generic/global_data.h b/include/asm-generic/global_data.h
index 5416f46..4425284 100644
--- a/include/asm-generic/global_data.h
+++ b/include/asm-generic/global_data.h
@@ -24,6 +24,9 @@
 
 #ifndef __ASM_GENERIC_GBL_DATA_H
 #define __ASM_GENERIC_GBL_DATA_H
+
+#include <linux/list.h>
+
 /*
  * The following data structure is placed in some memory which is
  * available very early after boot (like DPRAM on MPC8xx/MPC82xx, or
@@ -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 class_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/asm-generic/gpio.h b/include/asm-generic/gpio.h
index bfedbe4..ead3515 100644
--- a/include/asm-generic/gpio.h
+++ b/include/asm-generic/gpio.h
@@ -94,4 +94,24 @@ int gpio_get_value(unsigned gpio);
  * @return 0 if ok, -1 on error
  */
 int gpio_set_value(unsigned gpio, int value);
+
+/*
+ * 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.
+ */
+struct dm_gpio_ops {
+	int	base;
+	u16	ngpio;
+	int	(*gpio_request)(unsigned gpio, const char *label);
+	int	(*gpio_free)(unsigned gpio);
+	int	(*gpio_direction_input)(unsigned gpio);
+	int	(*gpio_direction_output)(unsigned gpio, int value);
+	int	(*gpio_get_value)(unsigned gpio);
+	int	(*gpio_set_value)(unsigned gpio, int value);
+};
+
 #endif	/* _ASM_GENERIC_GPIO_H_ */
diff --git a/include/command.h b/include/command.h
index 65692fd..445c9e8 100644
--- a/include/command.h
+++ b/include/command.h
@@ -80,6 +80,8 @@ 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
 
+int cmd_process_error(cmd_tbl_t *cmdtp, int err);
+
 /*
  * Monitor Command
  *
diff --git a/include/common.h b/include/common.h
index 28aa4b9..bdf598f 100644
--- a/include/common.h
+++ b/include/common.h
@@ -911,7 +911,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;
 }
diff --git a/include/config_fallbacks.h b/include/config_fallbacks.h
index e59ee96..c91a383 100644
--- a/include/config_fallbacks.h
+++ b/include/config_fallbacks.h
@@ -53,4 +53,12 @@
 #define HAVE_BLOCK_DEVICE
 #endif
 
+#ifdef CONFIG_DM
+/* For now, enable the driver model demo command */
+#define CONFIG_DM_DEMO
+#define CONFIG_CMD_DEMO
+#define CONFIG_DM_DEMO_SIMPLE
+#define CONFIG_DM_DEMO_SHAPE
+#endif
+
 #endif	/* __CONFIG_FALLBACKS_H */
diff --git a/include/configs/sandbox.h b/include/configs/sandbox.h
index 788207d..76c4398 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
 
diff --git a/include/dm-demo.h b/include/dm-demo.h
new file mode 100644
index 0000000..3bfac5a
--- /dev/null
+++ b/include/dm-demo.h
@@ -0,0 +1,47 @@
+/*
+ * 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_DEMO_H
+#define __DM_DEMO_H
+
+#include <dm.h>
+
+/**
+ * struct dm_demo_cdata - configuration data for demo instance
+ *
+ * @colour: Color of the demo
+ * @sides: Numbers of sides
+ */
+struct dm_demo_cdata {
+	const char *colour;
+	int sides;
+};
+
+struct demo_ops {
+	int (*hello)(struct device *i, int ch);
+	int (*status)(struct device *i, int *status);
+};
+
+int demo_hello(struct device *i, int ch);
+int demo_status(struct device *i, int *status);
+int demo_list(void);
+
+#endif
diff --git a/include/dm.h b/include/dm.h
new file mode 100644
index 0000000..6af3900
--- /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/manager.h>
+#include <dm/structures.h>
+#include <dm/uclass.h>
+
+#endif
diff --git a/include/dm/debug.h b/include/dm/debug.h
new file mode 100644
index 0000000..3f5ea7b
--- /dev/null
+++ b/include/dm/debug.h
@@ -0,0 +1,33 @@
+/*
+ * (C) Copyright 2012
+ * Pavel Herrmann <morpheus.ibis at 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_DEBUG_H_
+#define _DM_DEBUG_H_ 1
+
+#include <dm/structures.h>
+
+int dm_dump_all(void);
+int dm_dump(struct device *i);
+
+#endif
diff --git a/include/dm/manager.h b/include/dm/manager.h
new file mode 100644
index 0000000..fcc001e
--- /dev/null
+++ b/include/dm/manager.h
@@ -0,0 +1,57 @@
+/*
+ * (C) Copyright 2012
+ * Pavel Herrmann <morpheus.ibis at 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_MANAGER_H_
+#define _DM_MANAGER_H_
+
+#include <dm/uclass.h>
+#include <dm/structures.h>
+
+/* find-and-add function for the class tree */
+struct uclass *class_get_instance(enum uclass_id id);
+struct u_boot_class *get_class_by_id(enum uclass_id id);
+
+/* class API wrappers */
+int uclass_get_count(enum uclass_id id);
+struct device *uclass_get_child(enum uclass_id id, int index);
+int uclass_bind(enum uclass_id id, struct device *dev);
+int uclass_unbind(enum uclass_id id, struct device *dev);
+int uclass_replace(enum uclass_id id, struct device *new, struct device *old);
+
+/* driver manager API */
+struct device *driver_bind(struct device *parent,
+	const struct driver_info *info);
+int driver_activate(struct device *i);
+int driver_remove(struct device *i);
+int driver_unbind(struct device *dev);
+struct u_boot_driver *get_driver_by_instance(struct device *i);
+
+/* relocation stuff */
+struct device *driver_relocate(struct device *dev, struct device *bus);
+int classs_relocate(void);
+
+/* tree creation helpers */
+int dm_init(void);
+struct device *get_root_instance(void);
+
+#endif
diff --git a/include/dm/structures.h b/include/dm/structures.h
new file mode 100644
index 0000000..32ca832
--- /dev/null
+++ b/include/dm/structures.h
@@ -0,0 +1,97 @@
+/*
+ * (C) Copyright 2012
+ * Pavel Herrmann <morpheus.ibis at gmail.com>
+ * Marek Vasut <marex at 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_STRUCTURES_H_
+#define _DM_STRUCTURES_H_
+
+#include <common.h>
+#include <errno.h>
+#include <linker_lists.h>
+#include <dm/uclass.h>
+#include <linux/list.h>
+
+struct driver_info {
+	const char	*name;
+	const void	*platform_data;
+};
+
+#define DM_FLAG_ACTIVATED 1
+
+struct device {
+	struct u_boot_driver	*driver;
+	const struct driver_info *info;
+	struct device		*bus;
+	void			*priv;
+	struct list_head	class_node;	/* attach instances to class */
+	struct list_head	succ;		/* list of all instances */
+	struct list_head	sibling;	/* siblings */
+	uint32_t		flags;
+};
+
+#define device_get_ops(dev)	(dev->driver->ops)
+
+/* structures for driver manager */
+
+enum aux_data_type {
+	U_BOOT_DRIVER_AUX_NONE = 0,
+	U_BOOT_DRIVER_AUX_USB,
+	U_BOOT_DRIVER_AUX_PCI,
+};
+
+struct u_boot_driver {
+	char		*name;
+	enum		uclass_id id;
+	int		(*bind)(struct device *i);
+	int		(*probe)(struct device *i);
+	int		(*reloc)(struct device *new, struct device *old);
+	int		(*remove)(struct device *i);
+	int		(*unbind)(struct device *i);
+	int		priv_data_size;
+	const void	*ops;	/* driver-specific operations */
+	enum aux_data_type aux_data_type;
+	void		*aux_data;
+};
+
+struct usb_ids {
+	int			(*match)(void);
+	struct	{
+		uint16_t	major;
+		uint16_t	minor;
+	} ids[];
+};
+
+/*
+ * The USB driver has special needs, hence the separate type of driver.
+ * Yet, the struct u_boot_driver must be located at the first place, so the
+ * generic searching functions can be used.
+ */
+struct u_boot_usb_driver {
+	struct u_boot_driver	driver;
+	struct usb_ids		*ids;
+};
+
+#define U_BOOT_DRIVER(__name)						\
+	ll_entry_declare(struct u_boot_driver, __name, driver)
+
+#endif
diff --git a/include/dm/uclass.h b/include/dm/uclass.h
new file mode 100644
index 0000000..ce040e7
--- /dev/null
+++ b/include/dm/uclass.h
@@ -0,0 +1,62 @@
+/*
+ * (C) Copyright 2012
+ * Pavel Herrmann <morpheus.ibis at 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 <linux/list.h>
+
+/* TODO: this could be compile-time generated */
+enum uclass_id {
+	UCLASS_GPIO = 0,
+	UCLASS_DEMO,
+
+	UCLASS_COUNT,
+	UCLASS_INVALID = -1,
+};
+
+struct uclass {
+	void			*priv;
+	struct u_boot_class	*dm_class;
+	struct list_head	class_node;	/* instances for this class */
+	struct list_head	succ;
+};
+
+struct device;
+
+struct u_boot_class {
+	enum uclass_id	id;
+	int		(*bind)(struct uclass *class, struct device *dev);
+	int		(*unbind)(struct uclass *class, struct device *dev);
+	int		(*init)(struct uclass *class);
+	int		(*reloc)(struct uclass *new, struct uclass *old);
+	int		(*destroy)(struct uclass *class);
+	int		(*replace)(struct uclass *class, struct device *new,
+				   struct device *old);
+	const void	*ops;	/* class-specific operations */
+};
+
+#define U_BOOT_CLASS(__name)						\
+	ll_entry_declare(struct u_boot_class, __name, class)
+
+#endif
-- 
1.8.2.1



More information about the U-Boot mailing list