[U-Boot] [RFC PATCH v2 06/14] dm: Add base driver model support

Simon Glass sjg at chromium.org
Tue May 7 21:42:02 CEST 2013


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 at chromium.org>
Signed-off-by: Marek Vasut <marex at denx.de>
Signed-off-by: Pavel Herrmann <morpheus.ibis at gmail.com>
Signed-off-by: Viktor Křivák <viktor.krivak at gmail.com>
Signed-off-by: Tomas Hlavacek <tmshlvck at 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 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 <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 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 <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 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 <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 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 <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 at gmail.com>
+ * Marek Vasut <marex at denx.de>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
+ * MA 02111-1307 USA
+ */
+
+#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 at gmail.com>
+ * Marek Vasut <marex at denx.de>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
+ * MA 02111-1307 USA
+ */
+
+#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 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_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 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_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 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_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 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_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 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_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 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 <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



More information about the U-Boot mailing list