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