[U-Boot] [PATCH 1/7] dm: Add skeleton support for cores and drivers

Marek Vasut marex at denx.de
Tue Aug 21 18:00:47 CEST 2012


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

Signed-off-by: Pavel Herrmann <morpheus.ibis at gmail.com>
Signed-off-by: Marek Vasut <marex at denx.de>
---
 Makefile                    |    2 +
 common/dm/Makefile          |   40 +++++
 common/dm/core.c            |  150 ++++++++++++++++
 common/dm/driver.c          |  404 +++++++++++++++++++++++++++++++++++++++++++
 common/dm/lists.c           |  138 +++++++++++++++
 common/dm/root.c            |  103 +++++++++++
 common/dm/tree.c            |  164 ++++++++++++++++++
 common/dm/tree.h            |   31 ++++
 include/dm/core_numbering.h |   33 ++++
 include/dm/manager.h        |   57 ++++++
 include/dm/options.h        |   46 +++++
 include/dm/structures.h     |  154 +++++++++++++++++
 12 files changed, 1322 insertions(+)
 create mode 100644 common/dm/Makefile
 create mode 100644 common/dm/core.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/tree.h
 create mode 100644 include/dm/core_numbering.h
 create mode 100644 include/dm/manager.h
 create mode 100644 include/dm/options.h
 create mode 100644 include/dm/structures.h

diff --git a/Makefile b/Makefile
index 67c89ca..0e008b1 100644
--- a/Makefile
+++ b/Makefile
@@ -301,6 +301,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),)
 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..6510021
--- /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	:= core.o driver.o lists.o tree.o root.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/core.c b/common/dm/core.c
new file mode 100644
index 0000000..4a183d8
--- /dev/null
+++ b/common/dm/core.c
@@ -0,0 +1,150 @@
+/*
+ * (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/manager.h>
+
+/**
+ * core_get_count() - Return number of registered children with core
+ * @id:		ID of the core
+ *
+ * Count the number of members registered with this core and return
+ * the value. Negative value is returned in case of failure.
+ */
+int core_get_count(enum core_id id)
+{
+	struct core_instance *core;
+	struct u_boot_core *core_ops;
+
+	core = get_core_instance(id);
+	if (!core)
+		return -ENOMEM;
+
+	core_ops = get_core_by_id(id);
+	if (!core_ops)
+		return -ENOENT;
+
+	return core_ops->get_count(core);
+}
+
+/**
+ * core_get_child() - Return n-th child of core
+ * @id:		ID of the core
+ * @index:	Position of the child in core's list
+ *
+ * Return the instance pointer of a child at the given index or
+ * return NULL on error.
+ */
+struct instance *core_get_child(enum core_id id, int index)
+{
+	struct core_instance *core;
+	struct u_boot_core *core_ops;
+
+	core = get_core_instance(id);
+	if (!core)
+		return NULL;
+
+	core_ops = get_core_by_id(id);
+	if (!core_ops)
+		return NULL;
+
+	return core_ops->get_child(core, index);
+}
+
+/**
+ * core_bind() - Associate driver with a core
+ * @id:		ID of the core
+ * @dev:	Pointer to the driver's instance
+ * @ops:	Structure carrying operations this driver provides to the core
+ * @data:	Additional auxiliary data
+ *
+ * Connect the driver into core's list of drivers. Returns 0 on success,
+ * negative value on error.
+ */
+int core_bind(enum core_id id, struct instance *dev, void *ops, void* data)
+{
+	struct core_instance *core;
+	struct u_boot_core *core_ops;
+
+	core = get_core_instance(id);
+	if (!core)
+		return -ENOMEM;
+
+	core_ops = get_core_by_id(id);
+	if (!core_ops)
+		return -ENOENT;
+
+	return core_ops->bind(core, dev, ops, data);
+}
+
+/**
+ * core_unbind() - Deassociate driver with a core
+ * @id:		ID of the core
+ * @dev:	Pointer to the driver's instance
+ *
+ * Disconnect the driver from core's list of drivers.
+ * Returns 0 on success, negative value on error.
+ */
+int core_unbind(enum core_id id, struct instance *dev)
+{
+	struct core_instance *core;
+	struct u_boot_core *core_ops;
+
+	core = get_core_instance(id);
+	if (!core)
+		return -ENOMEM;
+
+	core_ops = get_core_by_id(id);
+	if (!core_ops)
+		return -ENOENT;
+
+	return core_ops->unbind(core, dev);
+}
+
+/**
+ * core_replace() - Replace instance of a driver with another in core's list
+ * @id:		ID of the core
+ * @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 core with a new one, usually a post-relocation
+ * one. All of the internal state of the core associated with the old instance
+ * is preserved.
+ *
+ * Returns 0 on success, negative value on error.
+ */
+int core_replace(enum core_id id, struct instance *new, struct instance *old)
+{
+	struct core_instance *core;
+	struct u_boot_core *core_ops;
+
+	core = get_core_instance(id);
+	if (!core)
+		return -ENOMEM;
+
+	core_ops = get_core_by_id(id);
+	if (!core_ops)
+		return -ENOENT;
+
+	return core_ops->replace(core, new, old);
+}
diff --git a/common/dm/driver.c b/common/dm/driver.c
new file mode 100644
index 0000000..3214675
--- /dev/null
+++ b/common/dm/driver.c
@@ -0,0 +1,404 @@
+/*
+ * (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/list.h>
+
+/*
+ * List management functions
+ */
+
+/**
+ * driver_driver_bind() - 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 int driver_driver_bind(struct instance *parent,
+			struct driver_instance *child)
+{
+	list_add_tail(&child->list, &parent->succ);
+
+	return 0;
+}
+
+/**
+ * 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 driver instance
+ * contains the list of a children, usually generated during the
+ * pre-relocation stage, whereas the new driver instance contains no
+ * children nodes yet. The new driver instance 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 instance *old,
+				struct instance *new)
+{
+	struct driver_instance *pos, *n, *nd;
+	struct instance *i;
+
+	if (list_empty(&old->succ))
+		return 0;
+
+	list_for_each_entry_safe(pos, n, &old->succ, list) {
+		i = driver_relocate(&pos->i, new);
+		if (!i)
+			goto err_reloc;
+		nd = container_of(i, struct driver_instance, i);
+		driver_driver_bind(new, nd);
+	}
+
+	return 0;
+
+err_reloc:
+	/* FIXME */
+	puts("Fatal error during relocation!\n");
+	hang();
+	return -EINVAL;
+}
+
+/**
+ * driver_driver_unbind() - Unbind driver's child from the driver
+ * @parent:	The parent driver instance, from which the child is unbound
+ * @child:	The child driver instance, which is being unbound
+ */
+static int driver_driver_unbind(struct instance *parent,
+				struct instance *child)
+{
+	struct driver_instance *di;
+	di = container_of(child, struct driver_instance, i);
+
+	list_del(&di->list);
+
+	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 instance *dev)
+{
+	struct driver_instance *pos, *n;
+	int ret;
+
+	if (!dev)
+		return -EINVAL;
+
+	if (list_empty(&dev->succ))
+		return 0;
+
+	list_for_each_entry_safe(pos, n, &dev->succ, list) {
+		ret = driver_unbind(&pos->i);
+		if (ret)
+			goto err_unbind;
+	}
+
+	return 0;
+
+err_unbind:
+	/* FIXME */
+	puts("Fatal error during unbinding!\n");
+	hang();
+	return -EINVAL;
+}
+
+/**
+ * driver_chld_remove() - Stop all driver's children
+ * @dev:	The driver whose children are to be stopped
+ */
+static int driver_chld_remove(struct instance *dev)
+{
+	struct driver_instance *pos, *n;
+	int ret;
+
+	if (!dev)
+		return -EINVAL;
+
+	if (list_empty(&dev->succ))
+		return 0;
+
+	list_for_each_entry_safe(pos, n, &dev->succ, list) {
+		ret = driver_remove(&pos->i);
+		if (ret)
+			goto err_remove;
+	}
+
+	return 0;
+
+err_remove:
+	/* FIXME */
+	puts("Fatal error during removal!\n");
+	hang();
+	return -EINVAL;
+}
+
+/*
+ * 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 instance *driver_bind(struct instance *parent,
+			const struct driver_info *info)
+{
+	struct driver_instance *outer = NULL;
+	struct instance *dev = NULL;
+	struct u_boot_driver *drv = NULL;
+	int retval = 0;
+
+	if (!info || !parent)
+		goto fail;
+
+	outer = calloc(1, sizeof(struct driver_instance));
+	if (!outer)
+		goto fail;
+
+	dev = &outer->i;
+	INIT_LIST_HEAD(&outer->list);
+	INIT_LIST_HEAD(&dev->succ);
+	dev->info = info;
+	drv = get_driver_by_instance(dev);
+	if (!drv || !drv->bind)
+		goto fail_free;
+
+	dev->bus = parent;
+	dev->private_data = NULL;
+
+	/* put dev into parents successor list */
+	retval = driver_driver_bind(parent, outer);
+	if (retval)
+		goto fail_free;
+
+	/* probe dev */
+	retval = drv->bind(dev);
+
+	/* if we fail to bind we remove device from successors and free it */
+	if (retval)
+		goto fail_free;
+
+	return dev;
+
+fail_free:
+	free(outer);
+fail:
+	return NULL;
+
+}
+
+/**
+ * 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 instance *i)
+{
+	struct driver_instance *outer = NULL;
+	struct u_boot_driver *ops = NULL;
+	int status = 0;
+
+	if (!i)
+		return -EINVAL;
+
+	outer =	container_of(i, struct driver_instance, i);
+	if (outer->flags & DRIVER_FLAG_ACTIVATED)
+		return 0;
+
+	if (i->bus) {
+		status = driver_activate(i->bus);
+		if (status != 0)
+			return status;
+	}
+
+	ops = get_driver_by_instance(i);
+	if (!ops)
+		return -ENOENT;
+
+	if (ops->probe)
+		status = ops->probe(i);
+	else
+		status = 0;
+
+	if (!status)
+		outer->flags |= DRIVER_FLAG_ACTIVATED;
+
+	return status;
+}
+
+/**
+ * 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 instance *driver_relocate(struct instance *dev, struct instance *bus)
+{
+	struct driver_instance *old;
+	struct driver_instance *new;
+	struct u_boot_driver *ops;
+	struct instance *new_dev;
+	int ret;
+
+	if (!dev)
+		return NULL;
+
+	ops = get_driver_by_instance(dev);
+	if (!ops || !ops->reloc)
+		return NULL;
+
+	old = container_of(dev, struct driver_instance, i);
+	new = calloc(1, sizeof(struct driver_instance));
+	if (!new)
+		return NULL;
+
+	new_dev = &new->i;
+
+	INIT_LIST_HEAD(&new->list);
+	INIT_LIST_HEAD(&new_dev->succ);
+	new->flags = old->flags;
+	new_dev->bus = bus;
+	new_dev->info = dev->info;
+	/* FIXME: handle driver_info in dynamic memory */
+	new_dev->private_data = 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);
+	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 instance *dev)
+{
+	struct driver_instance *outer;
+	struct u_boot_driver *ops;
+	int ret;
+
+	if (!dev)
+		return -EINVAL;
+
+	outer = container_of(dev, struct driver_instance, i);
+	if (!outer)
+		return -EINVAL;
+
+	if (outer->flags & DRIVER_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)
+		return 0;
+
+	ret = driver_driver_unbind(dev->bus, dev);
+	if (!ret)
+		free(outer);
+
+	return ret;
+}
+
+/**
+ * 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 instance *dev)
+{
+	struct driver_instance *outer;
+	struct u_boot_driver *ops;
+	int ret;
+
+	if (!dev)
+		return -EINVAL;
+
+	outer = container_of(dev, struct driver_instance, i);
+	if (!outer)
+		return -EINVAL;
+
+	if (!(outer->flags & DRIVER_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;
+	}
+
+	outer->flags &= ~DRIVER_FLAG_ACTIVATED;
+
+	return ret;
+}
diff --git a/common/dm/lists.c b/common/dm/lists.c
new file mode 100644
index 0000000..bdc6809
--- /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 coresponding 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 instance *i)
+{
+	struct u_boot_driver *drv = &__u_boot_driver_start;
+	const int n_ents = &__u_boot_driver_end - &__u_boot_driver_start;
+	struct u_boot_driver *entry;
+	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 = &__u_boot_driver_usb_start;
+	const int n_ents =
+		&__u_boot_driver_usb_end - &__u_boot_driver_usb_start;
+	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_core_by_id() - Return u_boot_core based on ID of the core
+ * id:		ID of the core
+ *
+ * This function returns the pointer to u_boot_core, which is the core's
+ * base structure based on the ID of the core. Returns NULL on error.
+ */
+struct u_boot_core *get_core_by_id(enum core_id id)
+{
+	struct u_boot_core *core = &__u_boot_core_start;
+	const int n_ents = &__u_boot_core_end - &__u_boot_core_start;
+	struct u_boot_core *entry;
+
+	if ((id == CORE_INVALID) || !core)
+		return NULL;
+
+	for (entry = core; entry != core + n_ents; entry++) {
+		if (entry->number == id)
+			return entry;
+	}
+
+	return NULL;
+}
diff --git a/common/dm/root.c b/common/dm/root.c
new file mode 100644
index 0000000..509c2a8
--- /dev/null
+++ b/common/dm/root.c
@@ -0,0 +1,103 @@
+/*
+ * (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>
+#include "tree.h"
+
+DECLARE_GLOBAL_DATA_PTR;
+
+static const struct driver_info root_info = {
+	.name		= "root_driver",
+	.platform_data	= NULL,
+};
+
+/**
+ * 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 driver_instance *di;
+
+	if (gd->dm_root) {
+		puts("Virtual root driver already exists!\n");
+		return -EINVAL;
+	}
+
+	di = calloc(1, sizeof(struct driver_instance));
+	if (di == NULL)
+		return -ENOMEM;
+
+	INIT_LIST_HEAD(&di->list);
+	INIT_LIST_HEAD(&di->i.succ);
+	di->i.info = &root_info;
+	gd->dm_root = &di->i;
+
+	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 instance *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 core 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();
+
+	retval = init_core_tree();
+	if (retval)
+		hang();
+
+	return retval;
+}
+
+U_BOOT_DRIVER(root_driver, NULL, NULL, NULL, NULL, NULL);
diff --git a/common/dm/tree.c b/common/dm/tree.c
new file mode 100644
index 0000000..f8ae0f1
--- /dev/null
+++ b/common/dm/tree.c
@@ -0,0 +1,164 @@
+/*
+ * (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 core_id key;
+	struct core_instance core;
+};
+
+/**
+ * init_node() - Initialize node
+ * @key:	Key used to init node
+ *
+ * Allocate memory for struct node and set appropriate key for instance.
+ * Then find ops for this core and call init function. If allocation or init
+ * method fail return NULL otherwise return pointer to new node.
+ */
+static struct node *init_node(enum core_id key)
+{
+	struct u_boot_core *ops = get_core_by_id(key);
+	struct node *new_node = malloc(sizeof(*new_node));
+	if (!new_node || !ops)
+		hang();
+	if (!ops->init)
+		return new_node;
+	if (ops->init(&new_node->core)) {
+		free(new_node);
+		return NULL;
+	}
+	new_node->key = key;
+	return new_node;
+}
+
+/**
+ * search() - Search core by key in list
+ * @key:	Key used to search apropriet core instance
+ *
+ * Linear search in list and if found something, replace it at beginning
+ * of the list and return core instance inside node.
+ * If nothing was found return NULL.
+ */
+static struct core_instance *search(enum core_id key)
+{
+	struct node *node;
+	list_for_each_entry(node, &gd->core_root, list) {
+		if (node->key != key)
+			continue;
+		list_move(&node->list, &gd->core_root);
+		return &node->core;
+	}
+	return NULL;
+}
+
+/**
+ * add_new_core_instance() - Create and add new core in list
+ * @key: Key used to init core instance
+ *
+ * Call init_node to create core instance and its container structure.
+ * After that add this core to list.
+ */
+static struct core_instance *add_new_core_instance(enum core_id key)
+{
+	struct node *node;
+	node = init_node(key);
+	if (!node)
+		return NULL;
+	list_add(&node->list, &gd->core_root);
+	return &node->core;
+}
+
+/**
+ * get_core_instance() - Return or create core by key
+ * @key:	Search core with this key or create new one
+ *
+ * Try to find core in list structure. If fail create a new core and place it
+ * in list. If fail to create new core, return NULL. Otherwise return
+ * pointer to designated core.
+ */
+struct core_instance *get_core_instance(enum core_id key)
+{
+	struct core_instance *instance;
+	instance = search(key);
+	if (!instance)
+		instance = add_new_core_instance(key);
+	return instance;
+}
+
+/**
+ * cores_relocate() - Relocate cores from early init memory
+ *
+ * Allocate new memory for each core, copy key and call reloc function found
+ * in specific core ops. Return 0 if succeeded.
+ */
+int cores_relocate(void)
+{
+	struct u_boot_core *ops;
+	struct node *node;
+	struct node *new;
+	struct list_head new_head;
+	INIT_LIST_HEAD(&new_head);
+	list_for_each_entry(node, &gd->core_root, list) {
+		ops = get_core_by_id(node->key);
+		if (!ops)
+			hang();
+		new = malloc(sizeof(*new));
+		if (!new) {
+			puts("Unable to allocate memory!");
+			hang();
+		}
+		new->key = node->key;
+		/* No reloc method. It is strage but not error */
+		if (!ops->reloc)
+			continue;
+		if (!ops->reloc(&new->core, &node->core))
+			list_add_tail(&new->list, &new_head);
+		else
+			puts("Relocation of core fail!");
+			hang();
+	}
+	gd->core_root = new_head;
+	return 0;
+}
+
+/**
+ * init_core_tree() - Init head of the cores list
+ *
+ * This method must be called before get_core_instance(). It simply initialize
+ * list head structure. This method can be call only once. Return 0
+ * if succeeded.
+ */
+int init_core_tree(void)
+{
+	INIT_LIST_HEAD(&gd->core_root);
+	return 0;
+}
diff --git a/common/dm/tree.h b/common/dm/tree.h
new file mode 100644
index 0000000..ed29b83
--- /dev/null
+++ b/common/dm/tree.h
@@ -0,0 +1,31 @@
+/*
+ * (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 _COMMON_DM_TREE_H_
+#define _COMMON_DM_TREE_H_ 1
+
+/* we dont want this to be in public headers */
+int init_core_tree(void);
+
+#endif
+
diff --git a/include/dm/core_numbering.h b/include/dm/core_numbering.h
new file mode 100644
index 0000000..75a023f
--- /dev/null
+++ b/include/dm/core_numbering.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_CORE_NUMBERING_H
+#define _DM_CORE_NUMBERING_H 1
+
+/* TODO: this could be compile-time generated */
+
+enum core_id {
+	CORE_INVALID = 0,
+};
+
+#endif
diff --git a/include/dm/manager.h b/include/dm/manager.h
new file mode 100644
index 0000000..93ff453
--- /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_ 1
+
+#include <dm/core_numbering.h>
+#include <dm/structures.h>
+
+/* find-and-add function for the core tree */
+struct core_instance *get_core_instance(enum core_id id);
+struct u_boot_core *get_core_by_id(enum core_id id);
+
+/* core API wrappers */
+int core_get_count(enum core_id id);
+struct instance *core_get_child(enum core_id id, int index);
+int core_bind(enum core_id id, struct instance *dev, void *ops, void *data);
+int core_unbind(enum core_id id, struct instance *dev);
+int core_replace(enum core_id id, struct instance *new, struct instance *old);
+
+/* driver manager API */
+struct instance *driver_bind(struct instance *parent,
+	const struct driver_info *info);
+int driver_activate(struct instance *i);
+int driver_remove(struct instance *i);
+int driver_unbind(struct instance *dev);
+struct u_boot_driver *get_driver_by_instance(struct instance *i);
+
+/* relocation stuff */
+struct instance *driver_relocate(struct instance *dev, struct instance *bus);
+int cores_relocate(void);
+
+/* tree creation helpers */
+int dm_init(void);
+struct instance *get_root_instance(void);
+
+#endif
diff --git a/include/dm/options.h b/include/dm/options.h
new file mode 100644
index 0000000..a8bd961
--- /dev/null
+++ b/include/dm/options.h
@@ -0,0 +1,46 @@
+/*
+ * (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_OPTIONS_H_
+#define _DM_OPTIONS_H_ 1
+
+#include <dm/structures.h>
+
+#define OPTION_TYPE_MASK 0x3
+#define OPTION_TYPE_EMPTY 0x0
+#define OPTION_TYPE_U 0x1
+#define OPTION_TYPE_S 0x2
+#define OPTION_TYPE_P 0x3
+
+#define OPTION_PTR_MALLOCED (0x1 << 31)
+
+struct option {
+	uint32_t flags;
+	union {
+		uint64_t data_u;
+		char *data_s;
+		void *data_p;
+	} data;
+};
+
+#endif
diff --git a/include/dm/structures.h b/include/dm/structures.h
new file mode 100644
index 0000000..dcf03c5
--- /dev/null
+++ b/include/dm/structures.h
@@ -0,0 +1,154 @@
+/*
+ * (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_ 1
+
+#include <common.h>
+#include <errno.h>
+#include <dm/core_numbering.h>
+#include <linux/list.h>
+
+/* TODO: use config.h instead of this define */
+#define BLOCKING_FACTOR 3
+
+/* structures for the driver instance tree */
+
+struct driver_info {
+	char	*name;
+	void	*platform_data;
+};
+
+struct instance {
+	const struct driver_info	*info;
+	struct instance			*bus;
+	void				*private_data;
+	struct list_head		succ;
+};
+
+struct core_instance {
+	void				*private_data;
+	struct list_head		succ;
+};
+
+struct driver_instance {
+	struct list_head	list;
+	uint32_t		flags;
+	struct instance		i;
+};
+
+#define DRIVER_FLAG_ACTIVATED 1
+
+
+/* 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;
+	int		(*bind)(struct instance *i);
+	int		(*probe)(struct instance *i);
+	int		(*reloc)(struct instance *new, struct instance *old);
+	int		(*remove)(struct instance *i);
+	int		(*unbind)(struct instance *i);
+	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;
+};
+
+struct u_boot_core {
+	enum core_id	number;
+	int		(*init)(struct core_instance *core);
+	int		(*reloc)(struct core_instance *new,
+				struct core_instance *old);
+	int		(*destroy)(struct core_instance *core);
+	int		(*get_count)(struct core_instance *core);
+	struct instance *(*get_child)(struct core_instance *core, int index);
+	int		(*bind)(struct core_instance *core,
+				struct instance *dev, void *ops, void *data);
+	int		(*unbind)(struct core_instance *core,
+				struct instance *dev);
+	int		(*replace)(struct core_instance *core,
+				struct instance *new, struct instance *old);
+};
+
+#define __U_BOOT_DRIVER(name, bind, probe, reloc, remove, unbind,	\
+			__type, aux_t, aux_d)				\
+	struct u_boot_driver __u_boot_driver_##name			\
+		Struct_Section(u_boot_driver.__type, name) = {	\
+			#name, bind, probe, reloc, remove, unbind,	\
+			.aux_data_type	= aux_t,			\
+			.aux_data	= aux_d,			\
+	}
+
+#define U_BOOT_DRIVER(name, bind, probe, reloc, remove, unbind)		\
+	__U_BOOT_DRIVER(name, bind, probe, reloc, remove, unbind,	\
+			generic, U_BOOT_DRIVER_AUX_NONE, NULL)
+#define U_BOOT_PCI_DRIVER(name, __ids, bind, probe, reloc, remove, unbind) \
+	__U_BOOT_DRIVER(name, bind, probe, reloc, remove, unbind,	\
+			pci, U_BOOT_DRIVER_AUX_PCI, __ids)
+#define U_BOOT_USB_DRIVER(name, __ids, bind, probe, reloc, remove, unbind) \
+	__U_BOOT_DRIVER(name, bind, probe, reloc, remove, unbind,	\
+			usb, U_BOOT_DRIVER_AUX_USB, __ids)
+
+extern struct u_boot_driver __u_boot_driver_start;
+extern struct u_boot_driver __u_boot_driver_end;
+extern struct u_boot_driver __u_boot_driver_pci_start;
+extern struct u_boot_driver __u_boot_driver_pci_end;
+extern struct u_boot_driver __u_boot_driver_usb_start;
+extern struct u_boot_driver __u_boot_driver_usb_end;
+
+#define U_BOOT_CORE(__id, init, reloc, destroy, get_count, get_child,	\
+			bind, unbind, replace)				\
+	struct u_boot_core __u_boot_core_##__id				\
+		Struct_Section(u_boot_core, __id) = {			\
+			__id, init, reloc, destroy, get_count,		\
+			get_child, bind, unbind, replace		\
+	}
+
+extern struct u_boot_core __u_boot_core_start;
+extern struct u_boot_core __u_boot_core_end;
+
+#endif
-- 
1.7.10.4



More information about the U-Boot mailing list