[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