[U-Boot] [PATCH 0/7] [RFC] Driver model, take 1

I'm submitting hereby the initial code for the driver model. This is a RFC patch, please give it a spin and scream :-)
The GPIO api should now use the new approach on the sandbox target. There's also "dm" command, that allows dumping the driver tree.
Marek Vasut (5): dm: sandbox: Add necessary linker sections dm: sandbox: Add necessary GD sections dm: REMOVE: sandbox binding experiment dm: gpio: Add draft GPIO core and convert sandbox to use it dm: Add "dm dump" command
Pavel Herrmann (2): dm: Add skeleton support for cores and drivers dm: add dummy demo driver and core
Makefile | 3 + arch/sandbox/cpu/u-boot.lds | 35 ++- arch/sandbox/include/asm/global_data.h | 9 + arch/sandbox/lib/board.c | 25 ++ common/dm/Makefile | 40 ++++ common/dm/core.c | 150 ++++++++++++ common/dm/debug.c | 106 +++++++++ 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 +++ drivers/demo/Makefile | 42 ++++ drivers/demo/core.c | 236 +++++++++++++++++++ drivers/demo/demo.c | 67 ++++++ drivers/gpio/Makefile | 2 + drivers/gpio/core.c | 365 +++++++++++++++++++++++++++++ drivers/gpio/sandbox.c | 58 ++++- include/asm-generic/gpio.h | 19 ++ include/configs/sandbox.h | 2 + include/dm/core_numbering.h | 35 +++ include/dm/debug.h | 33 +++ include/dm/demo.h | 37 +++ include/dm/manager.h | 57 +++++ include/dm/options.h | 46 ++++ include/dm/structures.h | 154 ++++++++++++ 26 files changed, 2352 insertions(+), 9 deletions(-) create mode 100644 common/dm/Makefile create mode 100644 common/dm/core.c create mode 100644 common/dm/debug.c create mode 100644 common/dm/driver.c create mode 100644 common/dm/lists.c create mode 100644 common/dm/root.c create mode 100644 common/dm/tree.c create mode 100644 common/dm/tree.h create mode 100644 drivers/demo/Makefile create mode 100644 drivers/demo/core.c create mode 100644 drivers/demo/demo.c create mode 100644 drivers/gpio/core.c create mode 100644 include/dm/core_numbering.h create mode 100644 include/dm/debug.h create mode 100644 include/dm/demo.h create mode 100644 include/dm/manager.h create mode 100644 include/dm/options.h create mode 100644 include/dm/structures.h

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

On Tue, Aug 21, 2012 at 06:00:47PM +0200, Marek Vasut wrote:
From: Pavel Herrmann morpheus.ibis@gmail.com
Signed-off-by: Pavel Herrmann morpheus.ibis@gmail.com Signed-off-by: Marek Vasut marex@denx.de
More of a commit message please. At least point to the design docs that are now in tree.
+#ifndef _COMMON_DM_TREE_H_ +#define _COMMON_DM_TREE_H_ 1
#ifndef __COMMON_DM_TREE_H #define __COMMON_DM_TREE_H
etc, etc. You also pass in 'i' as a variable to one of the functions at least. Please make sure you use real variable names in functions.

Add linker sections necessary for driver model operation.
Signed-off-by: Marek Vasut marex@denx.de --- arch/sandbox/cpu/u-boot.lds | 35 ++++++++++++++++++++++++++++++++--- 1 file changed, 32 insertions(+), 3 deletions(-)
diff --git a/arch/sandbox/cpu/u-boot.lds b/arch/sandbox/cpu/u-boot.lds index 9960138..d83ee66 100644 --- a/arch/sandbox/cpu/u-boot.lds +++ b/arch/sandbox/cpu/u-boot.lds @@ -24,9 +24,38 @@
SECTIONS { - __u_boot_cmd_start = .; - _u_boot_cmd : { *(.u_boot_cmd) } - __u_boot_cmd_end = .; + .u_boot_cmd : { + __u_boot_cmd_start = .; + *(SORT(.u_boot_cmd.*)) + __u_boot_cmd_end = .; + } + + .u_boot_driver : { + __u_boot_driver_start = .; + __u_boot_driver_generic_start = .; + *(SORT(.u_boot_driver.generic.*)) + __u_boot_driver_generic_end = .; + + /* + * PCI and USB drivers have special needs, + * hence the separate lists + */ + __u_boot_driver_pci_start = .; + *(SORT(.u_boot_driver.pci.*)) + __u_boot_driver_pci_end = .; + + __u_boot_driver_usb_start = .; + *(SORT(.u_boot_driver.usb.*)) + __u_boot_driver_usb_end = .; + + __u_boot_driver_end = .; + } + + .u_boot_core : { + __u_boot_core_start = .; + *(SORT(.u_boot_core.*)) + __u_boot_core_end = .; + }
__u_boot_sandbox_option_start = .; _u_boot_sandbox_getopt : { *(.u_boot_sandbox_getopt) }

Add GD sections necessary for driver model operation.
Signed-off-by: Marek Vasut marex@denx.de --- arch/sandbox/include/asm/global_data.h | 9 +++++++++ 1 file changed, 9 insertions(+)
diff --git a/arch/sandbox/include/asm/global_data.h b/arch/sandbox/include/asm/global_data.h index 581fd2f..77c7508 100644 --- a/arch/sandbox/include/asm/global_data.h +++ b/arch/sandbox/include/asm/global_data.h @@ -25,6 +25,9 @@
#ifndef __ASM_GBL_DATA_H #define __ASM_GBL_DATA_H + +#include <linux/list.h> + /* * The following data structure is placed in some memory wich is * available very early after boot (like DPRAM on MPC8xx/MPC82xx, or @@ -43,6 +46,12 @@ typedef struct global_data { unsigned long fb_base; /* base address of frame buffer */ u8 *ram_buf; /* emulated RAM buffer */ phys_size_t ram_size; /* RAM size */ + +#ifdef CONFIG_DM + struct instance *dm_root; /* Root instance for Driver Model */ + struct list_head core_root; /* Head of core tree */ +#endif + const void *fdt_blob; /* Our device tree, NULL if none */ void **jt; /* jump table */ char env_buf[32]; /* buffer for getenv() before reloc. */

Signed-off-by: Marek Vasut marex@denx.de --- arch/sandbox/lib/board.c | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+)
diff --git a/arch/sandbox/lib/board.c b/arch/sandbox/lib/board.c index c173bf9..b6b3768 100644 --- a/arch/sandbox/lib/board.c +++ b/arch/sandbox/lib/board.c @@ -47,6 +47,8 @@
#include <os.h>
+#include <dm/manager.h> + DECLARE_GLOBAL_DATA_PTR;
static gd_t gd_mem; @@ -232,6 +234,23 @@ void board_init_r(gd_t *id, ulong dest_addr) mem_malloc_init((ulong)gd->ram_buf + gd->ram_size - TOTAL_MALLOC_LEN, TOTAL_MALLOC_LEN);
+ dm_init(); + static const struct driver_info info = { + .name = "demo_drv", + .platform_data = NULL + }; + struct instance *root = get_root_instance(); + struct instance *demo1, *demo2, *demo3; + demo1 = driver_bind(root, &info); + driver_bind(root, &info); + driver_bind(demo1, &info); + driver_bind(demo1, &info); + demo2 = driver_bind(demo1, &info); + demo3 = driver_bind(demo2, &info); + driver_bind(demo2, &info); + + demo_hello(demo2); + /* initialize environment */ env_relocate();

Signed-off-by: Marek Vasut marex@denx.de --- arch/sandbox/lib/board.c | 6 + drivers/gpio/Makefile | 2 + drivers/gpio/core.c | 365 +++++++++++++++++++++++++++++++++++++++++++ drivers/gpio/sandbox.c | 58 ++++++- include/asm-generic/gpio.h | 19 +++ include/configs/sandbox.h | 2 + include/dm/core_numbering.h | 1 + 7 files changed, 447 insertions(+), 6 deletions(-) create mode 100644 drivers/gpio/core.c
diff --git a/arch/sandbox/lib/board.c b/arch/sandbox/lib/board.c index b6b3768..c79cc62 100644 --- a/arch/sandbox/lib/board.c +++ b/arch/sandbox/lib/board.c @@ -239,6 +239,11 @@ void board_init_r(gd_t *id, ulong dest_addr) .name = "demo_drv", .platform_data = NULL }; + static const struct driver_info gs_info = { + .name = "gpio_sandbox", + .platform_data = NULL + }; + struct instance *root = get_root_instance(); struct instance *demo1, *demo2, *demo3; demo1 = driver_bind(root, &info); @@ -248,6 +253,7 @@ void board_init_r(gd_t *id, ulong dest_addr) demo2 = driver_bind(demo1, &info); demo3 = driver_bind(demo2, &info); driver_bind(demo2, &info); + driver_bind(root, &gs_info);
demo_hello(demo2);
diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile index 4b99b85..1d3aa02 100644 --- a/drivers/gpio/Makefile +++ b/drivers/gpio/Makefile @@ -25,6 +25,8 @@ include $(TOPDIR)/config.mk
LIB := $(obj)libgpio.o
+COBJS-$(CONFIG_DM) += core.o + COBJS-$(CONFIG_AT91_GPIO) += at91_gpio.o COBJS-$(CONFIG_KIRKWOOD_GPIO) += kw_gpio.o COBJS-$(CONFIG_MARVELL_GPIO) += mvgpio.o diff --git a/drivers/gpio/core.c b/drivers/gpio/core.c new file mode 100644 index 0000000..8fd83b5 --- /dev/null +++ b/drivers/gpio/core.c @@ -0,0 +1,365 @@ +#include <common.h> +#include <malloc.h> +#include <asm/gpio.h> +#include <dm/manager.h> +#include <linux/list.h> + +/* + * The idea here is to have GPIOs numbered like this from user point of view: + * + * 32 24 23 16 15 0 + * [ GPIO block ID ] [ GPIO chip ID ] [ GPIO ID within the GPIO chip ] + * + */ + +#define GPIO_TO_BLOCK_ID(x) (((x) >> 24) & 0xff) +#define GPIO_TO_CHIP_ID(x) (((x) >> 16) & 0xff) +#define GPIO_TO_CHIP_OFFSET(x) ((x) & 0xffff) + +struct gpio_core_entry { + struct list_head list; + struct instance *instance; + struct dm_gpio_ops *ops; + int id; +}; + +/** + * gpio_to_entry() - Convert GPIO number to entry in the list + * gpio: The numeric representation of the GPIO + * + * Convert the GPIO number to an entry in the list of GPIOs + * or GPIO blocks registered with the GPIO controller. Returns + * entry on success, NULL on error. + */ +static struct gpio_core_entry *gpio_to_entry(unsigned gpio) +{ + uint8_t block = GPIO_TO_BLOCK_ID(gpio); + uint8_t chip = GPIO_TO_CHIP_ID(gpio); + uint8_t offset = GPIO_TO_CHIP_OFFSET(gpio); + struct core_instance *core = get_core_instance(CORE_GPIO); + struct gpio_core_entry *tmp, *ret = NULL; + + list_for_each_entry(tmp, &core->succ, list) { + if (tmp->id != block) + continue; + if (tmp->ops->base != chip) + continue; + if (tmp->ops->ngpio < offset) + return NULL; + else { + ret = tmp; + break; + } + } + + if (ret) + driver_activate(ret->instance); + + return ret; +} + +/** + * gpio_request() - [COMPAT] Request GPIO + * gpio: GPIO number + * label: Name for the requested GPIO + * + * This function implements the API that's compatible with current + * GPIO API used in U-Boot. The request is forwarded to particular + * GPIO driver. Returns 0 on success, negative value on error. + */ +int gpio_request(unsigned gpio, const char *label) +{ + struct gpio_core_entry *e = gpio_to_entry(gpio); + if (!e) + return -EINVAL; + + return e->ops->gpio_request(gpio, label); +} + +/** + * gpio_free() - [COMPAT] Relinquish GPIO + * gpio: GPIO number + * + * This function implements the API that's compatible with current + * GPIO API used in U-Boot. The request is forwarded to particular + * GPIO driver. Returns 0 on success, negative value on error. + */ +int gpio_free(unsigned gpio) +{ + struct gpio_core_entry *e = gpio_to_entry(gpio); + if (!e) + return -EINVAL; + + return e->ops->gpio_free(gpio); +} + +/** + * gpio_direction_input() - [COMPAT] Set GPIO direction to input + * gpio: GPIO number + * + * This function implements the API that's compatible with current + * GPIO API used in U-Boot. The request is forwarded to particular + * GPIO driver. Returns 0 on success, negative value on error. + */ +int gpio_direction_input(unsigned gpio) +{ + struct gpio_core_entry *e = gpio_to_entry(gpio); + if (!e) + return -EINVAL; + + return e->ops->gpio_direction_input(gpio); +} + +/** + * gpio_direction_output() - [COMPAT] Set GPIO direction to output and set value + * gpio: GPIO number + * value: Logical value to be set on the GPIO pin + * + * This function implements the API that's compatible with current + * GPIO API used in U-Boot. The request is forwarded to particular + * GPIO driver. Returns 0 on success, negative value on error. + */ +int gpio_direction_output(unsigned gpio, int value) +{ + struct gpio_core_entry *e = gpio_to_entry(gpio); + if (!e) + return -EINVAL; + + return e->ops->gpio_direction_output(gpio, value); +} + +/** + * gpio_get_value() - [COMPAT] Sample GPIO pin and return it's value + * gpio: GPIO number + * + * This function implements the API that's compatible with current + * GPIO API used in U-Boot. The request is forwarded to particular + * GPIO driver. Returns the value of the GPIO pin, or negative value + * on error. + */ +int gpio_get_value(unsigned gpio) +{ + struct gpio_core_entry *e = gpio_to_entry(gpio); + if (!e) + return -EINVAL; + + return e->ops->gpio_get_value(gpio); +} + +/** + * gpio_set_value() - [COMPAT] Configure logical value on GPIO pin + * gpio: GPIO number + * value: Logical value to be set on the GPIO pin. + * + * This function implements the API that's compatible with current + * GPIO API used in U-Boot. The request is forwarded to particular + * GPIO driver. Returns 0 on success, negative value on error. + */ +int gpio_set_value(unsigned gpio, int value) +{ + struct gpio_core_entry *e = gpio_to_entry(gpio); + if (!e) + return -EINVAL; + + return e->ops->gpio_set_value(gpio, value); +} + +/** + * gpio_core_init() - Initialize the GPIO core + * core: Instance of the GPIO core + * + * This function does the initial configuration of the GPIO core's + * internal structures. Returns 0 always. + */ +static int gpio_core_init(struct core_instance *core) +{ + INIT_LIST_HEAD(&core->succ); + core->private_data = NULL; + + return 0; +} + +/** + * gpio_core_reloc() - Relocate the GPIO core + * new: New instance of the GPIO core + * old: Old instance of the GPIO core + * + * This function relocates the GPIO core. + * FIXME: Implement this. + */ +static int gpio_core_reloc(struct core_instance *new, struct core_instance *old) +{ + return 0; /* FIXME */ +} + +/** + * gpio_core_destroy() - Stop the GPIO core + * core: Instance of the GPIO core + * + * This function stops the GPIO core operation and disconnects all drivers from + * it. Returns 0 always. + */ +static int gpio_core_destroy(struct core_instance *core) +{ + struct gpio_core_entry *tmp, *n; + + list_for_each_entry_safe(tmp, n, &core->succ, list) { + list_del(&tmp->list); + free(tmp); + } + + return 0; +} + +/** + * gpio_core_get_count() - Return number of drivers connected to GPIO core + * core: Instance of the GPIO core + * + * This function counts the number of driver instances associated with the + * GPIO core and returns this number. + */ +static int gpio_core_get_count(struct core_instance *core) +{ + struct gpio_core_entry *tmp; + int i = 0; + + list_for_each_entry(tmp, &core->succ, list) + i++; + + return i; +} + +/** + * gpio_core_get_count() - Return n-th driver connected to GPIO core + * core: Instance of the GPIO core + * idx: Index of the driver + * + * This function returns the idx-th driver instance associated with the GPIO + * core. Returns the instance on success, NULL on error. + */ +static struct instance *gpio_core_get_child(struct core_instance *core, int idx) +{ + struct gpio_core_entry *tmp; + int i = 0; + + list_for_each_entry(tmp, &core->succ, list) { + if (i == idx) + return tmp->instance; + i++; + } + + return NULL; +} + +/** + * gpio_core_bind() - Bind instance of a driver to a GPIO core + * core: Instance of the GPIO core + * dev: Instance of the driver + * ops: Operations supplied by this driver, struct dm_gpio_ops * + * data: Auxiliary data, must be NULL for GPIO core + * + * This function binds a driver with a GPIO core. The driver is inserted into + * the list of driver instances the GPIO core tracks, so the GPIO core is aware + * of the driver. The driver instance is not yet activated. + * + * Returns 0 on success, negative value on error. + */ +static int gpio_core_bind(struct core_instance *core, struct instance *dev, + void *ops, void *data) +{ + struct gpio_core_entry *new, *tmp, *last = NULL; + + if (data || !ops) + return -EINVAL; + + new = malloc(sizeof(*new)); + if (!new) + return -ENOMEM; + + new->instance = dev; + new->ops = ops; + + list_for_each_entry(tmp, &core->succ, list) { + if (tmp->instance == dev) + last = tmp; + } + + if (!last) { + if (list_empty(&core->succ)) { + new->id = 0; + } else { + tmp = list_entry(core->succ.prev, + struct gpio_core_entry, list); + new->id = tmp->id + 1; + } + } else { + tmp = list_entry(&last->list, struct gpio_core_entry, list); + new->id = tmp->id; + } + + list_add_tail(&new->list, &core->succ); + + return 0; +} + +/** + * gpio_core_unbind() - Unbind instance of a driver from a GPIO core + * core: Instance of the GPIO core + * dev: Instance of the driver + * + * This function unbinds a driver from a GPIO core. The driver is removed from + * the list of driver instances the GPIO core tracks, so the GPIO core is no + * longer aware of the driver. The driver instance is not deactivated. + * + * Returns 0 always. + */ +static int gpio_core_unbind(struct core_instance *core, struct instance *dev) +{ + struct gpio_core_entry *tmp, *n; + + list_for_each_entry_safe(tmp, n, &core->succ, list) { + if (tmp->instance == dev) { + list_del(&tmp->list); + free(tmp); + } + } + + return 0; +} + +/** + * gpio_core_replace() - Find and replace instance of a driver in the list + * core: Instance of the GPIO core + * new: New instance of the driver, post relocation one + * old: Old instance of the driver, pre relocation one + * + * This function replaces pointers to pre-relocation driver instances in the + * core's list of drivers with pointers to post-relocation driver instances. + * Returns 0 on success, negative value on error. + */ +static int gpio_core_replace(struct core_instance *core, struct instance *new, + struct instance *old) +{ + struct gpio_core_entry *tmp; + + if (!core || !new || !old) + return -EINVAL; + + list_for_each_entry(tmp, &core->succ, list) { + if (tmp->instance != old) + continue; + tmp->instance = new; + } + + return 0; +} + +U_BOOT_CORE(CORE_GPIO, + gpio_core_init, + gpio_core_reloc, + gpio_core_destroy, + gpio_core_get_count, + gpio_core_get_child, + gpio_core_bind, + gpio_core_unbind, + gpio_core_replace); diff --git a/drivers/gpio/sandbox.c b/drivers/gpio/sandbox.c index 19d2db0..89ecdc5 100644 --- a/drivers/gpio/sandbox.c +++ b/drivers/gpio/sandbox.c @@ -21,6 +21,7 @@
#include <common.h> #include <asm/gpio.h> +#include <dm/manager.h>
/* Flags for each GPIO */ #define GPIOF_OUTPUT (1 << 0) /* Currently set as an output */ @@ -111,7 +112,7 @@ int sandbox_gpio_set_direction(unsigned gp, int output) */
/* set GPIO port 'gp' as an input */ -int gpio_direction_input(unsigned gp) +static int sb_gpio_direction_input(unsigned gp) { debug("%s: gp:%u\n", __func__, gp);
@@ -122,7 +123,7 @@ int gpio_direction_input(unsigned gp) }
/* set GPIO port 'gp' as an output, with polarity 'value' */ -int gpio_direction_output(unsigned gp, int value) +static int sb_gpio_direction_output(unsigned gp, int value) { debug("%s: gp:%u, value = %d\n", __func__, gp, value);
@@ -134,7 +135,7 @@ int gpio_direction_output(unsigned gp, int value) }
/* read GPIO IN value of port 'gp' */ -int gpio_get_value(unsigned gp) +static int sb_gpio_get_value(unsigned gp) { debug("%s: gp:%u\n", __func__, gp);
@@ -145,7 +146,7 @@ int gpio_get_value(unsigned gp) }
/* write GPIO OUT value to port 'gp' */ -int gpio_set_value(unsigned gp, int value) +static int sb_gpio_set_value(unsigned gp, int value) { debug("%s: gp:%u, value = %d\n", __func__, gp, value);
@@ -160,7 +161,7 @@ int gpio_set_value(unsigned gp, int value) return sandbox_gpio_set_value(gp, value); }
-int gpio_request(unsigned gp, const char *label) +static int sb_gpio_request(unsigned gp, const char *label) { debug("%s: gp:%u, label:%s\n", __func__, gp, label);
@@ -178,7 +179,7 @@ int gpio_request(unsigned gp, const char *label) return set_gpio_flag(gp, GPIOF_RESERVED, 1); }
-int gpio_free(unsigned gp) +static int sb_gpio_free(unsigned gp) { debug("%s: gp:%u\n", __func__, gp);
@@ -207,3 +208,48 @@ void gpio_info(void) label ? label : ""); } } + +#ifdef CONFIG_DM +static struct dm_gpio_ops gpio_sandbox_ops = { + .base = 0, + .ngpio = CONFIG_SANDBOX_GPIO_COUNT, + .gpio_request = sb_gpio_request, + .gpio_free = sb_gpio_free, + .gpio_direction_input = sb_gpio_direction_input, + .gpio_direction_output = sb_gpio_direction_output, + .gpio_get_value = sb_gpio_get_value, + .gpio_set_value = sb_gpio_set_value, +}; + +static int gpio_sandbox_bind(struct instance *dev) +{ + return core_bind(CORE_GPIO, dev, &gpio_sandbox_ops, NULL); +} + +static int gpio_sandbox_probe(struct instance *dev) +{ + return 0; +} + +static int gpio_sandbox_reloc(struct instance *new, struct instance *old) +{ + return 0; +} + +static int gpio_sandbox_remove(struct instance *dev) +{ + return 0; +} + +static int gpio_sandbox_unbind(struct instance *dev) +{ + return core_unbind(CORE_GPIO, dev); +} + +U_BOOT_DRIVER(gpio_sandbox, + gpio_sandbox_bind, + gpio_sandbox_probe, + gpio_sandbox_reloc, + gpio_sandbox_remove, + gpio_sandbox_unbind); +#endif diff --git a/include/asm-generic/gpio.h b/include/asm-generic/gpio.h index c19e16c..fac73ae 100644 --- a/include/asm-generic/gpio.h +++ b/include/asm-generic/gpio.h @@ -94,4 +94,23 @@ int gpio_get_value(unsigned gpio); */ int gpio_set_value(unsigned gpio, int value);
+/** + * Driver model GPIO operations, refer to functions above for description. + * These function copy the old API. + * + * This is trying to be close to Linux GPIO API. Once the U-Boot uses the + * new DM GPIO API, this should be really easy to flip over to the Linux + * GPIO API-alike interface. + */ +struct dm_gpio_ops { + int base; + u16 ngpio; + int (*gpio_request)(unsigned gpio, const char *label); + int (*gpio_free)(unsigned gpio); + int (*gpio_direction_input)(unsigned gpio); + int (*gpio_direction_output)(unsigned gpio, int value); + int (*gpio_get_value)(unsigned gpio); + int (*gpio_set_value)(unsigned gpio, int value); +}; + #endif /* _ASM_GENERIC_GPIO_H_ */ diff --git a/include/configs/sandbox.h b/include/configs/sandbox.h index 9c431bf..0220386 100644 --- a/include/configs/sandbox.h +++ b/include/configs/sandbox.h @@ -22,6 +22,8 @@ #ifndef __CONFIG_H #define __CONFIG_H
+#define CONFIG_DM + #define CONFIG_NR_DRAM_BANKS 1 #define CONFIG_DRAM_SIZE (128 << 20)
diff --git a/include/dm/core_numbering.h b/include/dm/core_numbering.h index 75a023f..b543b48 100644 --- a/include/dm/core_numbering.h +++ b/include/dm/core_numbering.h @@ -28,6 +28,7 @@
enum core_id { CORE_INVALID = 0, + CORE_GPIO, };
#endif

Hi
On Tuesday 21 August 2012 18:00:51 Marek Vasut wrote: ...snip...
+/**
- gpio_request() - [COMPAT] Request GPIO
- gpio: GPIO number
- label: Name for the requested GPIO
- This function implements the API that's compatible with current
- GPIO API used in U-Boot. The request is forwarded to particular
- GPIO driver. Returns 0 on success, negative value on error.
- */
+int gpio_request(unsigned gpio, const char *label) +{
- struct gpio_core_entry *e = gpio_to_entry(gpio);
- if (!e)
return -EINVAL;
- return e->ops->gpio_request(gpio, label);
+}
...snip...
Your core should have a driver API (as described in the core document), which should be in form of gpio_$fname for each $fname in the gpio_ops. on top of those you can have the command API, which accesses the gpio pins in a linear fashion (like what you have now)
the reason for this is if you have a device on gpio (say some LEDs), which knows (from platform data) that pins 3-6 of the parent device are connected to this device. in your API, this has no way of working - even if you put global pin numbering in the platform data, this would stop working if you had a PnP GPIO controllers (say USB).
regards Pavel Herrmann

From: Pavel Herrmann morpheus.ibis@gmail.com
Signed-off-by: Pavel Herrmann morpheus.ibis@gmail.com --- Makefile | 1 + drivers/demo/Makefile | 42 ++++++++ drivers/demo/core.c | 236 +++++++++++++++++++++++++++++++++++++++++++ drivers/demo/demo.c | 67 ++++++++++++ include/dm/core_numbering.h | 1 + include/dm/demo.h | 37 +++++++ 6 files changed, 384 insertions(+) create mode 100644 drivers/demo/Makefile create mode 100644 drivers/demo/core.c create mode 100644 drivers/demo/demo.c create mode 100644 include/dm/demo.h
diff --git a/Makefile b/Makefile index 0e008b1..ba74696 100644 --- a/Makefile +++ b/Makefile @@ -302,6 +302,7 @@ LIBS-y += post/libpost.o LIBS-y += test/libtest.o
LIBS-$(CONFIG_DM) += common/dm/libdm.o +LIBS-$(CONFIG_DM) += drivers/demo/libdemo.o
ifneq ($(CONFIG_AM33XX)$(CONFIG_OMAP34XX)$(CONFIG_OMAP44XX)$(CONFIG_OMAP54XX),) LIBS-y += $(CPUDIR)/omap-common/libomap-common.o diff --git a/drivers/demo/Makefile b/drivers/demo/Makefile new file mode 100644 index 0000000..192bd7d --- /dev/null +++ b/drivers/demo/Makefile @@ -0,0 +1,42 @@ +# See file CREDITS for list of people who contributed to this +# project. +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License as +# published by the Free Software Foundation; either version 2 of +# the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, +# MA 02111-1307 USA +# + +include $(TOPDIR)/config.mk + +LIB := $(obj)libdemo.o + +COBJS-y += demo.o core.o + +COBJS := $(COBJS-y) +SRCS := $(COBJS:.o=.c) +OBJS := $(addprefix $(obj),$(COBJS)) + +all: $(LIB) + +$(LIB): $(obj).depend $(OBJS) + $(call cmd_link_o_target, $(OBJS)) + +######################################################################### + +# defines $(obj).depend target +include $(SRCTREE)/rules.mk + +sinclude $(obj).depend + +######################################################################### diff --git a/drivers/demo/core.c b/drivers/demo/core.c new file mode 100644 index 0000000..1e6684b --- /dev/null +++ b/drivers/demo/core.c @@ -0,0 +1,236 @@ +/* + * (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 <dm/demo.h> +#include <linux/list.h> + +struct demo_core_entry { + struct list_head list; + struct instance *instance; + struct demo_ops *ops; +}; + +static struct demo_ops *get_ops(struct instance *inst) +{ + struct core_instance *core = NULL; + struct demo_core_entry *tmp; + + if (!inst) + return NULL; + + core = get_core_instance(CORE_DEMO); + if (!core) + /* something has gone terribly wrong here...*/ + return NULL; + + list_for_each_entry(tmp, &core->succ, list) { + if (tmp->instance == inst) + return tmp->ops; + } + + return NULL; +} + +static int demo_core_get_count(struct core_instance *core) +{ + struct demo_core_entry *tmp; + int i = 0; + + list_for_each_entry(tmp, &core->succ, list) + i++; + + return i; +} + +static struct instance *demo_core_get_child(struct core_instance *core, int idx) +{ + struct demo_core_entry *tmp; + int i = 0; + + list_for_each_entry(tmp, &core->succ, list) { + if (i == idx) + return tmp->instance; + i++; + } + + return NULL; +} + +static int demo_core_bind(struct core_instance *core, struct instance *dev, + void *ops, void *data) +{ + struct demo_core_entry *entry; + + if (data || !ops) + return -EINVAL; + + entry = malloc(sizeof(*entry)); + if (!entry) + return -ENOMEM; + + INIT_LIST_HEAD(&entry->list); + entry->instance = dev; + entry->ops = ops; + + list_add_tail(&entry->list, &core->succ); + return 0; +} + +static int demo_core_unbind(struct core_instance *core, struct instance *dev) +{ + struct demo_core_entry *tmp, *n; + + if (!dev) + return -EINVAL; + + list_for_each_entry_safe(tmp, n, &core->succ, list) { + if (tmp->instance == dev) { + list_del(&tmp->list); + free(tmp); + } + } + + return 0; +} + +static int demo_core_replace(struct core_instance *core, struct instance *new, + struct instance *old) +{ + struct demo_core_entry *tmp, *n; + + if (!new || !old) + return -EINVAL; + + list_for_each_entry_safe(tmp, n, &core->succ, list) + if (tmp->instance == old) + tmp->instance = new; + + return 0; +} + +static int demo_core_init(struct core_instance *core) +{ + core->private_data = NULL; + INIT_LIST_HEAD(&core->succ); + return 0; +} + +static int demo_core_reloc(struct core_instance *new, struct core_instance *old) +{ + /* no relocation at this point */ + return 0; +} + +static int demo_core_destroy(struct core_instance *core) +{ + /* no destruction at this point */ + return 0; +} + +U_BOOT_CORE(CORE_DEMO, + demo_core_init, + demo_core_reloc, + demo_core_destroy, + demo_core_get_count, + demo_core_get_child, + demo_core_bind, + demo_core_unbind, + demo_core_replace); + + +/* Driver API */ + +int demo_hello(struct instance *i) +{ + struct demo_ops *device_ops = NULL; + int status; + + device_ops = get_ops(i); + if (!device_ops) + return -ENOENT; + + status = driver_activate(i); + if (status != 0) + return status; + + if (device_ops->hello) + return device_ops->hello(i); + else + return 0; +} + + +/* Command API */ + +int demo_list(void) +{ + struct core_instance *core = NULL; + struct demo_core_entry *tmp = NULL; + int i = 0; + + core = get_core_instance(CORE_DEMO); + if (!core) + return -ENOMEM; + + puts("Demo core entries:\n"); + + list_for_each_entry(tmp, &core->succ, list) + printf("entry %d - instance 0x%p with ops 0x%p\n", + i++, tmp->instance, tmp->ops); + + return 0; +} + +static int do_demo(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) +{ + struct instance *dev; + int devnum = 0; + + switch (argc) { + case 2: + if (!strncmp(argv[1], "list", 4)) + return demo_list(); + break; + case 3: + if (!strncmp(argv[1], "call", 4)) + devnum = simple_strtoul(argv[2], NULL, 10); + dev = core_get_child(CORE_DEMO, devnum); + if (!dev) { + puts("device not found"); + return -EINVAL; + } else + return demo_hello(dev); + break; + } + + return -EINVAL; +} + +U_BOOT_CMD( + demo, 3, 1, do_demo, + "dm demo core ops\n", + "list\n" + "call <num>\n" +); diff --git a/drivers/demo/demo.c b/drivers/demo/demo.c new file mode 100644 index 0000000..d8610bd --- /dev/null +++ b/drivers/demo/demo.c @@ -0,0 +1,67 @@ +/* + * (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/demo.h> +#include <dm/manager.h> + +static int hello(struct instance *dev) +{ + printf("hello from %p\n", dev); + return 0; +} + +static struct demo_ops ops = { + .hello = hello, +}; + +static int bind(struct instance *dev) +{ + printf("bind from %p\n", dev); + return core_bind(CORE_DEMO, dev, &ops, NULL); +} + +static int probe(struct instance *dev) +{ + printf("probe from %p\n", dev); + return 0; +} + +static int reloc(struct instance *new, struct instance *old) +{ + printf("reloc from %p to %p\n", old, new); + return 0; +} + +static int remove(struct instance *dev) +{ + printf("remove from %p\n", dev); + return 0; +} + +static int unbind(struct instance *dev) +{ + printf("unbind from %p\n", dev); + return core_unbind(CORE_DEMO, dev); +} + +U_BOOT_DRIVER(demo_drv, bind, probe, reloc, remove, unbind); diff --git a/include/dm/core_numbering.h b/include/dm/core_numbering.h index b543b48..8dd6067 100644 --- a/include/dm/core_numbering.h +++ b/include/dm/core_numbering.h @@ -29,6 +29,7 @@ enum core_id { CORE_INVALID = 0, CORE_GPIO, + CORE_DEMO, };
#endif diff --git a/include/dm/demo.h b/include/dm/demo.h new file mode 100644 index 0000000..9fca7b8 --- /dev/null +++ b/include/dm/demo.h @@ -0,0 +1,37 @@ +/* + * (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_DEMO_H_ +#define _DM_DEMO_H_ 1 + +#include <dm/options.h> +#include <dm/structures.h> + +struct demo_ops { + int (*hello)(struct instance *i); +}; + +int demo_hello(struct instance *i); +int demo_list(void); + +#endif

Dumps the content of system tree
Signed-off-by: Marek Vasut marex@denx.de --- common/dm/Makefile | 2 +- common/dm/debug.c | 106 ++++++++++++++++++++++++++++++++++++++++++++++++++++ include/dm/debug.h | 33 ++++++++++++++++ 3 files changed, 140 insertions(+), 1 deletion(-) create mode 100644 common/dm/debug.c create mode 100644 include/dm/debug.h
diff --git a/common/dm/Makefile b/common/dm/Makefile index 6510021..2096c6b 100644 --- a/common/dm/Makefile +++ b/common/dm/Makefile @@ -21,7 +21,7 @@ include $(TOPDIR)/config.mk
LIB := $(obj)libdm.o
-COBJS := core.o driver.o lists.o tree.o root.o +COBJS := core.o debug.o driver.o lists.o tree.o root.o SRCS := $(COBJS:.o=.c) OBJS := $(addprefix $(obj),$(COBJS))
diff --git a/common/dm/debug.c b/common/dm/debug.c new file mode 100644 index 0000000..da669b7 --- /dev/null +++ b/common/dm/debug.c @@ -0,0 +1,106 @@ +/* + * (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 <malloc.h> +#include <dm/manager.h> +#include <dm/structures.h> +#include <dm/debug.h> +#include <common.h> +#include <errno.h> + +static int display_succ(struct instance *in, char *buf) +{ + int len; + int ip = 0; + char local[16]; + struct driver_instance *pos, *n, *prev = NULL, *outer; + + outer = container_of(in, struct driver_instance, i); + printf("%s- %s @ 0x%p", buf, in->info->name, in); + if (outer->flags & DRIVER_FLAG_ACTIVATED) + puts(" - activated"); + puts("\n"); + + if (list_empty(&in->succ)) + return 0; + + len = strlen(buf); + strncpy(local, buf, sizeof(local)); + snprintf(local + len, 2, "|"); + if (len && local[len - 1] == '`') + local[len - 1] = ' '; + + list_for_each_entry_safe(pos, n, &in->succ, list) { + if (ip++) + display_succ(&prev->i, local); + prev = pos; + } + + snprintf(local + len, 2, "`"); + display_succ(&prev->i, local); + + return 0; +} + +int dm_dump_all() +{ + struct instance *root; + root = get_root_instance(); + printf("ROOT 0x%p\n", root); + return dm_dump(root); +} + +int dm_dump(struct instance *dev) +{ + if (!dev) + return -EINVAL; + return display_succ(dev, ""); +} + +static int do_dm(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) +{ + struct instance *root; + + if (argc != 2) + return -EINVAL; + if (!strncmp(argv[1], "dump", 4)) + return dm_dump_all(); + + if (!strncmp(argv[1], "remove", 6)) { + root = get_root_instance(); + return driver_remove(root); + } + + if (!strncmp(argv[1], "unbind", 6)) { + root = get_root_instance(); + return driver_unbind(root); + } + + return -EINVAL; +} + +U_BOOT_CMD( + dm, 2, 1, do_dm, + "Driver model ops", + "<op>\n" +); diff --git a/include/dm/debug.h b/include/dm/debug.h new file mode 100644 index 0000000..df86113 --- /dev/null +++ b/include/dm/debug.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_DEBUG_H_ +#define _DM_DEBUG_H_ 1 + +#include <dm/structures.h> + +int dm_dump_all(void); +int dm_dump(struct instance *i); + +#endif

Dear Marek Vasut,
I'm submitting hereby the initial code for the driver model. This is a RFC patch, please give it a spin and scream :-)
The GPIO api should now use the new approach on the sandbox target. There's also "dm" command, that allows dumping the driver tree.
Expanding CC ... guys, please start ripping us to shreds :-)
Marek Vasut (5): dm: sandbox: Add necessary linker sections dm: sandbox: Add necessary GD sections dm: REMOVE: sandbox binding experiment dm: gpio: Add draft GPIO core and convert sandbox to use it dm: Add "dm dump" command
Pavel Herrmann (2): dm: Add skeleton support for cores and drivers dm: add dummy demo driver and core
Makefile | 3 + arch/sandbox/cpu/u-boot.lds | 35 ++- arch/sandbox/include/asm/global_data.h | 9 + arch/sandbox/lib/board.c | 25 ++ common/dm/Makefile | 40 ++++ common/dm/core.c | 150 ++++++++++++ common/dm/debug.c | 106 +++++++++ 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 +++ drivers/demo/Makefile | 42 ++++ drivers/demo/core.c | 236 +++++++++++++++++++ drivers/demo/demo.c | 67 ++++++ drivers/gpio/Makefile | 2 + drivers/gpio/core.c | 365 +++++++++++++++++++++++++++++ drivers/gpio/sandbox.c | 58 ++++- include/asm-generic/gpio.h | 19 ++ include/configs/sandbox.h | 2 + include/dm/core_numbering.h | 35 +++ include/dm/debug.h | 33 +++ include/dm/demo.h | 37 +++ include/dm/manager.h | 57 +++++ include/dm/options.h | 46 ++++ include/dm/structures.h | 154 ++++++++++++ 26 files changed, 2352 insertions(+), 9 deletions(-) create mode 100644 common/dm/Makefile create mode 100644 common/dm/core.c create mode 100644 common/dm/debug.c create mode 100644 common/dm/driver.c create mode 100644 common/dm/lists.c create mode 100644 common/dm/root.c create mode 100644 common/dm/tree.c create mode 100644 common/dm/tree.h create mode 100644 drivers/demo/Makefile create mode 100644 drivers/demo/core.c create mode 100644 drivers/demo/demo.c create mode 100644 drivers/gpio/core.c create mode 100644 include/dm/core_numbering.h create mode 100644 include/dm/debug.h create mode 100644 include/dm/demo.h create mode 100644 include/dm/manager.h create mode 100644 include/dm/options.h create mode 100644 include/dm/structures.h
Best regards, Marek Vasut

Hi DM Team,
On Thu, Aug 23, 2012 at 4:28 AM, Marek Vasut marek.vasut@gmail.com wrote:
Dear Marek Vasut,
I'm submitting hereby the initial code for the driver model. This is a RFC patch, please give it a spin and scream :-)
I haven't had a really deep play with this yet, but a couple of things do come to mind...
I've been thinking of a scenario of a simple LED module with both a Serial (RS-232) and USB interface (or maybe an I2C connection). I can think of two options: a) Write a separate driver for each interface b) Write a single driver that can handle all supported interfaces
If going with option b, the LED driver is going to need to know what type of interface it is attached on in order for it to issue the correct commands to the upstream driver. I can't see a way in the current model to determine the type of the upstream driver
Another thought is naming if instances (this was in particular relation to stdio) - If you have two UARTS onboard, it would make sense to name them in U-Boot according to how they are named on the board (UART1 / UART2, Port 1 / Port 2, Console / Diagnostics, etc.) and when directing stdio, using the name of the port.
Regards,
Graeme

Dear Graeme Russ,
Hi DM Team,
On Thu, Aug 23, 2012 at 4:28 AM, Marek Vasut marek.vasut@gmail.com wrote:
Dear Marek Vasut,
I'm submitting hereby the initial code for the driver model. This is a RFC patch, please give it a spin and scream :-)
I haven't had a really deep play with this yet, but a couple of things do come to mind...
I've been thinking of a scenario of a simple LED module with both a Serial (RS-232) and USB interface (or maybe an I2C connection). I can think of two options: a) Write a separate driver for each interface b) Write a single driver that can handle all supported interfaces
c) Write a muxing driver, basically you'd have two drivers -- "led-module-uart" and "led-module-usb" -- which would share common logic. The upcall towards the bus can also be pretty much covered by some local wrappers.
But I'm fairy sleepy now, had a hacknight tonight, so ...
If going with option b, the LED driver is going to need to know what type of interface it is attached on in order for it to issue the correct commands to the upstream driver. I can't see a way in the current model to determine the type of the upstream driver
You can have a shared part and separate part of the probe() function. Also, the probe function knows the name of the driver, it can decide based on that.
Another thought is naming if instances (this was in particular relation to stdio) - If you have two UARTS onboard, it would make sense to name them in U-Boot according to how they are named on the board (UART1 / UART2, Port 1 / Port 2, Console / Diagnostics, etc.) and when directing stdio, using the name of the port.
Basically aliases ... I'd postpone this. But it's good it's here, green on white with monospace font. I'll mark this as important.
Regards,
Graeme
Best regards, Marek Vasut

Hi Marek,
On Tue, Sep 4, 2012 at 4:44 PM, Marek Vasut marek.vasut@gmail.com wrote:
Dear Graeme Russ,
Hi DM Team,
On Thu, Aug 23, 2012 at 4:28 AM, Marek Vasut marek.vasut@gmail.com wrote:
Dear Marek Vasut,
I'm submitting hereby the initial code for the driver model. This is a RFC patch, please give it a spin and scream :-)
I haven't had a really deep play with this yet, but a couple of things do come to mind...
I've been thinking of a scenario of a simple LED module with both a Serial (RS-232) and USB interface (or maybe an I2C connection). I can think of two options: a) Write a separate driver for each interface b) Write a single driver that can handle all supported interfaces
c) Write a muxing driver, basically you'd have two drivers -- "led-module-uart" and "led-module-usb" -- which would share common logic. The upcall towards the bus can also be pretty much covered by some local wrappers.
Adds a little overhead, but sounds sound. But also, you should give the downstream driver the ability to check that it is being attached to a valid upstream driver - Attaching a USB ethernet dongle to PCI isn't going to cut it. This will be critical if we allow modularisation of the driver sub-system (i.e. dynamically load a driver module and bind it from the command line)
Regards,
Graeme

On Tue, Aug 21, 2012 at 06:00:46PM +0200, Marek Vasut wrote:
I'm submitting hereby the initial code for the driver model. This is a RFC patch, please give it a spin and scream :-)
The GPIO api should now use the new approach on the sandbox target. There's also "dm" command, that allows dumping the driver tree.
Marek Vasut (5): dm: sandbox: Add necessary linker sections dm: sandbox: Add necessary GD sections dm: REMOVE: sandbox binding experiment dm: gpio: Add draft GPIO core and convert sandbox to use it dm: Add "dm dump" command
Pavel Herrmann (2): dm: Add skeleton support for cores and drivers dm: add dummy demo driver and core
So, the design documents are in. And you've got a "simple" example. How have you progressed on migrating some of the more complex drivers that will really put DM through its paces, so to speak? Thanks!
participants (5)
-
Graeme Russ
-
Marek Vasut
-
Marek Vasut
-
Pavel Herrmann
-
Tom Rini