On Wed, Oct 31, 2012 at 9:52 AM, Pantelis Antoniou <pa...@antoniou-consulting.com> wrote: > Introducing capebus; a bus that allows small boards (capes) to connect > to a complex SoC using simple expansion connectors. > > Up to now to support these kind of boards, one had to hack the board files, > and do all sort of gymnastics to handle all the different cases of > conflict resolution. > > Capebus provides abstractions that keep the pain to a minimum. > > This part of the series is introducing the core capebus functionality > dealing with the basic bus & driver probe functions. > > Signed-off-by: Pantelis Antoniou <pa...@antoniou-consulting.com> > --- > drivers/Kconfig | 2 + > drivers/Makefile | 3 + > drivers/capebus/Kconfig | 17 ++ > drivers/capebus/Makefile | 8 + > drivers/capebus/capebus-driver.c | 608 > +++++++++++++++++++++++++++++++++++++++ > drivers/capebus/capebus-probe.c | 320 +++++++++++++++++++++ > drivers/capebus/capebus-sysfs.c | 52 ++++ > include/linux/capebus.h | 298 +++++++++++++++++++ > 8 files changed, 1308 insertions(+) > create mode 100644 drivers/capebus/Kconfig > create mode 100644 drivers/capebus/Makefile > create mode 100644 drivers/capebus/capebus-driver.c > create mode 100644 drivers/capebus/capebus-probe.c > create mode 100644 drivers/capebus/capebus-sysfs.c > create mode 100644 include/linux/capebus.h > > diff --git a/drivers/Kconfig b/drivers/Kconfig > index dbdefa3..bfbe1d1 100644 > --- a/drivers/Kconfig > +++ b/drivers/Kconfig > @@ -156,4 +156,6 @@ source "drivers/pwm/Kconfig" > > source "drivers/irqchip/Kconfig" > > +source "drivers/capebus/Kconfig" > + > endmenu > diff --git a/drivers/Makefile b/drivers/Makefile > index a16a8d0..d7a103b 100644 > --- a/drivers/Makefile > +++ b/drivers/Makefile > @@ -145,3 +145,6 @@ obj-$(CONFIG_EXTCON) += extcon/ > obj-$(CONFIG_MEMORY) += memory/ > obj-$(CONFIG_IIO) += iio/ > obj-$(CONFIG_VME_BUS) += vme/ > + > +# Capebus > +obj-$(CONFIG_CAPEBUS) += capebus/ > diff --git a/drivers/capebus/Kconfig b/drivers/capebus/Kconfig > new file mode 100644 > index 0000000..cea1b68 > --- /dev/null > +++ b/drivers/capebus/Kconfig > @@ -0,0 +1,17 @@ > +# > +# Capebus core support > +# > + > +menu "CAPEBUS support" > + > +config CAPEBUS > + bool "Capebus support" > + default n > + help > + Enable to support capebus devices. > + > +source "drivers/capebus/boards/Kconfig" > + > +source "drivers/capebus/capes/Kconfig" > + > +endmenu > diff --git a/drivers/capebus/Makefile b/drivers/capebus/Makefile > new file mode 100644 > index 0000000..45aa303 > --- /dev/null > +++ b/drivers/capebus/Makefile > @@ -0,0 +1,8 @@ > +# > +# Makefile for CAPEBUS devices > +# > + > +obj-$(CONFIG_CAPEBUS) += capebus-probe.o \ > + capebus-driver.o capebus-sysfs.o > +obj-$(CONFIG_CAPEBUS) += boards/ > +obj-$(CONFIG_CAPEBUS) += capes/ > diff --git a/drivers/capebus/capebus-driver.c > b/drivers/capebus/capebus-driver.c > new file mode 100644 > index 0000000..82b1d1b > --- /dev/null > +++ b/drivers/capebus/capebus-driver.c > @@ -0,0 +1,608 @@ > +/* > + * Capebus driver infrastructure > + * > + * Copyright (C) 2012 Pantelis Antoniou <pa...@antoniou-consulting.com> > + * Copyright (C) 2012 Texas Instruments Inc. > + * > + * This program is free software; you can redistribute it and/or modify > + * it under the terms of the GNU General Public License as published by > + * the Free Software Foundation; either version 2 of the License, or > + * (at your option) any later version. > + * > + * This program is distributed in the hope that it will be useful, > + * but WITHOUT ANY WARRANTY; without even the implied warranty of > + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the > + * GNU General Public License for more details. > + * > + * You should have received a copy of the GNU General Public License > + * along with this program; if not, write to the Free Software > + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. > + */ > + > +#include <linux/module.h> > +#include <linux/init.h> > +#include <linux/device.h> > +#include <linux/mempolicy.h> > +#include <linux/string.h> > +#include <linux/slab.h> > +#include <linux/cpu.h> > +#include <linux/pm_runtime.h> > +#include <linux/suspend.h> > + > +#include <linux/of.h> > +#include <linux/of_device.h> > +#include <linux/of_platform.h> > + > +#include <linux/capebus.h> > + > +/** > + * capebus_match_device - Tell if a cape device structure has a > + * matching cape device id structure > + * @drv: the cape driver to match against > + * @dev: the cape device structure to match against > + * > + * Used by a driver to check whether a cape device present in the > + * system is in its list of supported devices. Returns the matching > + * cape_device_id structure or %NULL if there is no match. > + */ > +static const struct cape_device_id *capebus_match_device( > + struct cape_driver *drv, struct cape_dev *dev) > +{ > + struct cape_bus *bus = dev->bus; > + struct cape_slot *slot = dev->slot; > + > + BUG_ON(bus == NULL); > + BUG_ON(slot == NULL); > + BUG_ON(bus->ops == NULL); > + BUG_ON(bus->ops->get_dev_id == NULL); > + > + return bus->ops->get_dev_id(slot); > +} > + > +/** > + * capebus_device_probe - check if a driver wants to claim a > + * specific cape device > + * @dev: cape device being probed > + * > + * returns 0 on success, else error. > + * side-effect: cape_dev->driver is set to drv when drv claims cape_dev. > + */ > +static int capebus_device_probe(struct device *dev) > +{ > + const struct cape_device_id *id; > + int error = 0; > + struct cape_driver *drv; > + struct cape_dev *cape_dev; > + struct device *parent; > + > + drv = to_cape_driver(dev->driver); > + cape_dev = to_cape_dev(dev); > + cape_dev = capebus_dev_get(cape_dev); > + > + /* sanity checks */ > + if (cape_dev == NULL || > + cape_dev->bus == NULL || cape_dev->bus->ops == NULL || > + cape_dev->driver != NULL || drv->probe == NULL) { > + error = -EINVAL; > + goto err_no_sanity; > + } > + > + id = capebus_match_device(drv, cape_dev); > + if (!id) { > + error = -ENODEV; > + goto err_no_match; > + } > + > + /* The parent device must be in active state when probing */ > + parent = cape_dev->dev.parent; > + if (parent) > + pm_runtime_get_sync(parent); > + > + /* Unbound cape devices are always set to disabled and suspended. > + * During probe, the device is set to enabled and active and the > + * usage count is incremented. If the driver supports runtime PM, > + * it should call pm_runtime_put_noidle() in its probe routine and > + * pm_runtime_get_noresume() in its remove routine. > + */ > + pm_runtime_get_noresume(&cape_dev->dev); > + pm_runtime_set_active(&cape_dev->dev); > + pm_runtime_enable(&cape_dev->dev); > + > + /* call the driver's probe method */ > + error = drv->probe(cape_dev, id); > + > + /* release the parent no matter what */ > + if (parent) > + pm_runtime_put(parent); > + > + if (error != 0) > + goto err_probe_fail; > + > + /* call the probed bus method */ > + if (cape_dev->bus->ops->dev_probed != NULL) { > + error = cape_dev->bus->ops->dev_probed(cape_dev); > + if (error != 0) > + goto err_dev_probed_fail; > + } > + > + /* all is fine... */ > + cape_dev->driver = drv; > + cape_dev->added = 1; > + > + return 0; > + > +err_dev_probed_fail: > + if (drv->remove) { > + pm_runtime_get_sync(&cape_dev->dev); > + drv->remove(cape_dev); > + pm_runtime_put_noidle(&cape_dev->dev); > + } > +err_probe_fail: > + pm_runtime_disable(&cape_dev->dev); > + pm_runtime_set_suspended(&cape_dev->dev); > + pm_runtime_put_noidle(&cape_dev->dev); > +err_no_match: > + /* nothing */ > +err_no_sanity: > + capebus_dev_put(cape_dev); > + return error; > +} > + > +static int capebus_device_remove(struct device *dev) > +{ > + struct cape_dev *cape_dev = to_cape_dev(dev); > + struct cape_driver *drv = cape_dev->driver; > + > + if (drv) { > + /* call the removed bus method (if added prev.) */ > + if (cape_dev->added) { > + BUG_ON(cape_dev->bus == NULL); > + BUG_ON(cape_dev->bus->ops == NULL); > + if (cape_dev->bus->ops->dev_removed) > + cape_dev->bus->ops->dev_removed(cape_dev); > + cape_dev->added = 0; > + }
Is there any case where added will not track drv? > + if (drv->remove) { > + pm_runtime_get_sync(dev); > + drv->remove(cape_dev); > + pm_runtime_put_noidle(dev); > + } > + cape_dev->driver = NULL; > + } > + > + /* Undo the runtime PM settings in local_capebus_probe() */ > + pm_runtime_disable(dev); > + pm_runtime_set_suspended(dev); > + pm_runtime_put_noidle(dev); > + > + capebus_dev_put(cape_dev); > + return 0; > +} > + > +static void capebus_device_shutdown(struct device *dev) > +{ > + struct cape_dev *cape_dev = to_cape_dev(dev); > + struct cape_driver *drv = cape_dev->driver; > + > + if (drv && drv->shutdown) > + drv->shutdown(cape_dev); > + > + capebus_disable_device(cape_dev); > + > + if (!device_may_wakeup(dev)) > + capebus_enable_wake(cape_dev, false); > +} > + > +static int capebus_bus_match(struct device *dev, struct device_driver *drv); > +static int capebus_device_probe(struct device *dev); > +static int capebus_device_remove(struct device *dev); > +static void capebus_device_shutdown(struct device *dev); > + > +struct bus_type capebus_bus_type = { > + .name = "capebus", > + .match = capebus_bus_match, > + .probe = capebus_device_probe, > + .remove = capebus_device_remove, > + .shutdown = capebus_device_shutdown, > + .dev_attrs = capebus_dev_attrs, > + .bus_attrs = capebus_bus_attrs, > + .pm = NULL, /* No PM for now */ > +}; > +EXPORT_SYMBOL(capebus_bus_type); > + > +/** > + * __capebus_register_driver - register a new capebus driver > + * @drv: the driver structure to register > + * @owner: owner module of drv > + * @mod_name: module name string > + * > + * Adds the driver structure to the list of registered drivers. > + * Returns a negative value on error, otherwise 0. > + * If no error occurred, the driver remains registered even if > + * no device was claimed during registration. > + */ > +int __capebus_register_driver(struct cape_driver *drv, struct module *owner, > + const char *mod_name) > +{ > + /* initialize common driver fields */ > + drv->driver.bus = &capebus_bus_type; > + drv->driver.owner = owner; > + drv->driver.mod_name = mod_name; > + > + /* register with core */ > + return driver_register(&drv->driver); > +} > +EXPORT_SYMBOL(__capebus_register_driver); > + > +/** > + * capebus_unregister_driver - unregister a capebus driver > + * @drv: the driver structure to unregister > + * > + * Deletes the driver structure from the list of registered cape drivers, > + * gives it a chance to clean up by calling its remove() function for > + * each device it was responsible for, and marks those devices as > + * driverless. > + */ > + > +void > +capebus_unregister_driver(struct cape_driver *drv) > +{ > + /* TODO: not really working properly */ > + driver_unregister(&drv->driver); > +} > +EXPORT_SYMBOL(capebus_unregister_driver); > + > +/** > + * capebus_bus_match - Tell if a cape device structure has a matching > + * cape device id structure > + * @dev: the cape device structure to match against > + * @drv: the device driver to search for matching cape device id structures > + * > + * Used by a driver to check whether a cape device present in the > + * system is in its list of supported devices. Returns the matching > + * cape_device_id structure or %NULL if there is no match. > + */ > +static int capebus_bus_match(struct device *dev, struct device_driver *drv) > +{ > + struct cape_dev *cape_dev = to_cape_dev(dev); > + struct cape_driver *cape_drv = to_cape_driver(drv); > + const struct cape_device_id *found_id; > + > + found_id = capebus_match_device(cape_drv, cape_dev); > + if (found_id) > + return 1; > + > + return 0; > +} > + > +/** > + * capebus_dev_get - increments the reference count of the capebus > + * device structure > + * @dev: the device being referenced > + * > + * Each live reference to a device should be refcounted. > + * > + * Drivers for cape devices should normally record such references in > + * their probe() methods, when they bind to a device, and release > + * them by calling capebus_dev_put(), in their disconnect() methods. > + * > + * A pointer to the device with the incremented reference counter is > returned. > + */ > +struct cape_dev *capebus_dev_get(struct cape_dev *dev) > +{ > + if (dev) > + get_device(&dev->dev); > + return dev; > +} > +EXPORT_SYMBOL(capebus_dev_get); > + > +/** > + * capebus_dev_put - release a use of the capebus device structure > + * @dev: device that's been disconnected > + * > + * Must be called when a user of a device is finished with it. When the last > + * user of the device calls this function, the memory of the device is freed. > + */ > +void capebus_dev_put(struct cape_dev *dev) > +{ > + if (dev) > + put_device(&dev->dev); > +} > +EXPORT_SYMBOL(capebus_dev_put); > + > +static int __init capebus_driver_init(void) > +{ > + return bus_register(&capebus_bus_type); > +} > + > +postcore_initcall(capebus_driver_init); > + > +const struct of_device_id * > +capebus_of_match_device(struct cape_dev *cdev, > + const char *property, const char *value) > +{ > + struct cape_bus *bus = cdev->bus; > + struct device *dev = &cdev->dev; > + struct device_node *pnode = cape_bus_to_parent_of_node(bus); > + struct device_node *node; > + const struct of_device_id *match; > + const char* cp; > + int cplen, l; > + > + dev_dbg(dev, "Iterating on parent of node " > + "name='%s' type='%s' full_name='%s'\n", > + pnode->name, pnode->type, pnode->full_name); > + > + match = NULL; > + for_each_child_of_node(pnode, node) { > + > + dev->of_node = node; > + match = of_match_device(dev->driver->of_match_table, dev); > + if (!match) > + goto next_node; > + > + cp = of_get_property(node, property, &cplen); > + if (cp == NULL) > + goto next_node; > + > + while (cplen > 0) { > + if (of_compat_cmp(cp, value, strlen(value)) == 0) > + break; > + l = strlen(cp) + 1; > + cp += l; > + cplen -= l; > + } > + > + /* matched */ > + if (cplen > 0) > + break; > +next_node: > + match = NULL; > + dev->of_node = NULL; > + } > + > + if (match == NULL) { > + dev_dbg(dev, "Failed to find matching child-node\n"); > + return NULL; > + } > + > + dev_dbg(dev, "Found matching child node " > + "name='%s' type='%s' " > + "full_name='%s' (compatible='%s')\n", > + node->name, node->type, node->full_name, > + match->compatible); > + > + return match; > +} > +EXPORT_SYMBOL(capebus_of_match_device); > + > +struct device_node * > +capebus_of_compatible_device_property_match(struct cape_dev *dev, > + const struct of_device_id *matches, > + const char *prop, const char *prop_value) > +{ > + const struct of_device_id *match; > + struct device_node *node, *cnode; > + const char* cp; > + int cplen, l; > + > + if (prop == NULL || prop_value == NULL) > + goto try_non_property; > + > + /* at first try secondary match */ > + for_each_child_of_node(dev->dev.of_node, node) { > + > + cp = of_get_property(node, prop, &cplen); > + if (cp == NULL) > + continue; > + > + while (cplen > 0) { > + if (of_compat_cmp(cp, prop_value, > + strlen(prop_value)) == 0) > + break; > + l = strlen(cp) + 1; > + cp += l; > + cplen -= l; > + } > + > + /* not matched */ > + if (cplen <= 0) > + continue; > + > + /* now iterate in the children nodes */ > + for_each_child_of_node(node, cnode) { > + > + match = of_match_node(matches, cnode); > + if (match) { > + /* release reference to parent, keep this one > */ > + of_node_put(node); > + return cnode; > + } > + } > + } > + > +try_non_property: > + for_each_child_of_node(dev->dev.of_node, node) { > + > + match = of_match_node(matches, node); > + if (match) > + return node; > + } > + > + return NULL; > +} > +EXPORT_SYMBOL(capebus_of_compatible_device_property_match); > + > +struct platform_device * > +capebus_of_platform_compatible_device_create(struct cape_dev *dev, > + const struct of_device_id *matches, > + const char *pdev_name, > + const char *prop, const char *prop_value) > +{ > + struct device_node *node; > + struct platform_device *pdev; > + > + node = capebus_of_compatible_device_property_match(dev, matches, prop, > + prop_value); > + if (node == NULL) > + return ERR_PTR(-ENXIO); > + > + pdev = of_platform_device_create(node, pdev_name, > dev->bus->dev.parent); > + > + /* release the reference to the node */ > + of_node_put(node); > + node = NULL; > + > + if (pdev == NULL) { > + dev_err(&dev->dev, "Failed to create platform device '%s'\n", > + pdev_name); > + return ERR_PTR(-ENODEV); > + } > + > + return pdev; > +} > +EXPORT_SYMBOL(capebus_of_platform_compatible_device_create); > + > +struct device_node * > +capebus_of_find_property_node(struct cape_dev *dev, > + const char *prop, const char *prop_value, > + const char *name) > +{ > + struct device_node *node; > + const char* cp; > + int cplen, l; > + struct property *pp; > + > + node = NULL; > + if (prop == NULL || prop_value == NULL) > + goto find_direct; > + > + /* at first try secondary match */ > + for_each_child_of_node(dev->dev.of_node, node) { > + > + cp = of_get_property(node, prop, &cplen); > + if (cp == NULL) > + continue; > + > + while (cplen > 0) { > + if (of_compat_cmp(cp, prop_value, > + strlen(prop_value)) == 0) > + break; > + l = strlen(cp) + 1; > + cp += l; > + cplen -= l; > + } > + > + /* not matched */ > + if (cplen <= 0) > + continue; > + > + /* found ? */ > + pp = of_find_property(node, name, NULL); > + if (pp != NULL) > + return node; > + } > +find_direct: > + pp = of_find_property(dev->dev.of_node, name, NULL); > + if (pp == NULL) > + return NULL; > + > + return of_node_get(dev->dev.of_node); > +} > +EXPORT_SYMBOL_GPL(capebus_of_find_property_node); > + > +struct property * > +capebus_of_find_property(struct cape_dev *dev, > + const char *prop, const char *prop_value, > + const char *name, int *lenp) > +{ > + struct device_node *node; > + struct property *pp; > + > + node = capebus_of_find_property_node(dev, prop, prop_value, name); > + if (node == NULL) > + return NULL; > + > + pp = of_find_property(node, name, lenp); > + > + of_node_put(node); > + > + return pp; > +} > +EXPORT_SYMBOL_GPL(capebus_of_find_property); > + > +const void *capebus_of_get_property(struct cape_dev *dev, > + const char *prop, const char *prop_value, > + const char *name, int *lenp) > +{ > + struct property *pp; > + > + pp = capebus_of_find_property(dev, prop, prop_value, name, lenp); > + return pp ? pp->value : NULL; > +} > +EXPORT_SYMBOL_GPL(capebus_of_get_property); > + > +/* node exists, but it's not available? make it so */ > +int capebus_of_device_node_enable(struct device_node *node) > +{ > + struct property *prop; > + int ret; > + > + prop = kzalloc(sizeof(*prop), GFP_KERNEL); > + if (prop == NULL) > + goto err_no_prop_mem; > + > + prop->name = kstrdup("status", GFP_KERNEL); > + if (prop->name == NULL) > + goto err_no_name_mem; > + > + prop->value = kstrdup("okay", GFP_KERNEL); > + if (prop->value == NULL) > + goto err_no_value_mem; > + > + prop->length = strlen(prop->value) + 1; > + set_bit(OF_DYNAMIC, &prop->_flags); > + > + ret = prom_update_property(node, prop); > + if (ret != 0) > + goto err_update_failed; > + > + return 0; > + > +err_update_failed: > + kfree(prop->value); > +err_no_value_mem: > + kfree(prop->name); > +err_no_name_mem: > + kfree(prop); > +err_no_prop_mem: > + return -ENOMEM; > +} > +EXPORT_SYMBOL_GPL(capebus_of_device_node_enable); > + > +/* Make sure this node is activated (even if it was disabled) */ > +int capebus_of_platform_device_enable(struct device_node *node) > +{ > + struct platform_device *pdev, *ppdev; > + int ret; > + > + if (of_device_is_available(node)) > + return 0; > + > + ret = capebus_of_device_node_enable(node); > + if (ret != 0) > + return ret; > + > + /* now we need to find the parent of the node */ > + ppdev = of_find_device_by_node(node->parent); > + > + pdev = of_platform_device_create(node, NULL, > + ppdev ? &ppdev->dev : NULL); > + if (IS_ERR_OR_NULL(pdev)) { > + ret = pdev ? PTR_ERR(pdev) : -ENODEV; > + return ret; > + } > + > + return 0; > +} > +EXPORT_SYMBOL_GPL(capebus_of_platform_device_enable); > diff --git a/drivers/capebus/capebus-probe.c b/drivers/capebus/capebus-probe.c > new file mode 100644 > index 0000000..b46e915 > --- /dev/null > +++ b/drivers/capebus/capebus-probe.c > @@ -0,0 +1,320 @@ > +/* > + * Capebus bus infrastructure > + * > + * Copyright (C) 2012 Pantelis Antoniou <pa...@antoniou-consulting.com> > + * Copyright (C) 2012 Texas Instruments Inc. > + * > + * This program is free software; you can redistribute it and/or modify > + * it under the terms of the GNU General Public License as published by > + * the Free Software Foundation; either version 2 of the License, or > + * (at your option) any later version. > + * > + * This program is distributed in the hope that it will be useful, > + * but WITHOUT ANY WARRANTY; without even the implied warranty of > + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the > + * GNU General Public License for more details. > + * > + * You should have received a copy of the GNU General Public License > + * along with this program; if not, write to the Free Software > + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. > + */ > + > +#include <linux/module.h> > +#include <linux/err.h> > +#include <linux/io.h> > +#include <linux/of.h> > +#include <linux/of_i2c.h> > +#include <linux/of_device.h> > +#include <linux/pm_runtime.h> > +#include <linux/err.h> > +#include <linux/list.h> > +#include <linux/mutex.h> > +#include <linux/slab.h> > + > +#include <linux/capebus.h> > + > +LIST_HEAD(cape_buses); > +EXPORT_SYMBOL(cape_buses); > + > +DEFINE_MUTEX(cape_buses_mutex); > +EXPORT_SYMBOL(cape_buses_mutex); > + > +/* > + * Cape Bus Class > + */ > +static void release_capebus_dev(struct device *dev) > +{ > + struct cape_dev *cape_dev = to_cape_dev(dev); > + > + kfree(cape_dev); > +} > + > +static struct class capebus_class = { > + .name = "capebus", > + .dev_release = &release_capebus_dev, > +}; > + > +static int __init capebus_class_init(void) > +{ > + return class_register(&capebus_class); > +} > +postcore_initcall(capebus_class_init); > + > +static struct cape_bus *cape_bus_find(const char *name, int busno) > +{ > + struct cape_bus *bus; > + int found; > + > + if (busno < 0) > + return NULL; > + > + found = 0; > + cape_bus_for_each(bus) { > + if (strcmp(name, bus->name) == 0 && bus->busno == busno) { > + found = 1; > + break; > + } > + } > + return found ? bus : NULL; > +} > + > +static int cape_bus_pick_busno(const char *name, int busno) > +{ > + struct cape_bus *bus; > + > + BUG_ON(name == NULL); > + > + /* fixed id */ > + if (busno >= 0) > + return busno; > + > + /* dynamic id */ > + busno = -1; > + cape_bus_for_each(bus) { > + /* name must match */ > + if (strcmp(name, bus->name) != 0) > + continue; > + busno = max(busno, bus->busno); > + } > + return busno + 1; > +} > + > +int cape_bus_register(struct cape_bus *bus, const char *name, int busno, > + struct device *parent, struct cape_bus_ops *ops) > +{ > + struct cape_bus *b2; > + int r; > + > + if (name == NULL) > + return -EINVAL; > + > + INIT_LIST_HEAD(&bus->node); > + INIT_LIST_HEAD(&bus->devices); > + INIT_LIST_HEAD(&bus->slots); > + > + /* do everything under lock */ > + mutex_lock(&cape_buses_mutex); > + > + b2 = cape_bus_find(name, busno); > + if (b2 != NULL) { > + if (parent != NULL) > + dev_err(parent, "capebus %s:%d in use\n", name, > busno); > + else > + pr_err("capebus %s:%d in use\n", name, busno); > + r = -EBUSY; > + goto err_unlock; > + } > + bus->name = name; > + bus->busno = cape_bus_pick_busno(name, busno); > + bus->ops = ops; > + > + bus->dev.class = &capebus_class; > + bus->dev.parent = parent; > + dev_set_name(&bus->dev, "%s:%d", bus->name, bus->busno); > + r = device_register(&bus->dev); > + if (r != 0) { > + if (parent != NULL) > + dev_err(parent, "capebus #%d failed to register > dev\n", > + bus->busno); > + else > + pr_err("capebus #%d failed to register dev\n", > + bus->busno); > + goto err_unlock; > + } > + > + list_add_tail(&bus->node, &cape_buses); > + mutex_unlock(&cape_buses_mutex); > + > + dev_info(&bus->dev, "Registered\n"); > + > + return 0; > +err_unlock: > + mutex_unlock(&cape_buses_mutex); > + return r; > +} > + > +int cape_bus_deregister(struct cape_bus *bus) > +{ > + return -EINVAL; /* not yet supported */ > +} > + > +/* must have cape_buses_mutex */ > +struct cape_slot *cape_slot_find(struct cape_bus *bus, int slotno) > +{ > + struct cape_slot *slot; > + int found; > + > + found = 0; > + cape_slot_for_each(bus, slot) { > + if (slot->slotno == slotno) { > + found = 1; > + break; > + } > + } > + return found ? slot : NULL; > +} > + > +/** > + * cape_bus_release_dev - free a cape device structure when all users > + * of it are finished. > + * @dev: device that's been disconnected > + * > + * Will be called only by the device core when all users of this cape device > are > + * done. > + */ > +static void cape_bus_release_dev(struct device *dev) > +{ > + struct cape_dev *cdev; > + > + cdev = to_cape_dev(dev); > + /* cape_release_capabilities(cdev); TODO */ > + /* cape_release_of_node(cdev); TODO */ > + kfree(cdev); > +} > + > +/* mutex lock must be held */ > +static struct cape_dev *cape_bus_scan_slot(struct cape_slot *slot) > +{ > + struct cape_bus *bus = slot->bus; > + struct cape_dev *dev; > + const struct cape_device_id *id; > + > + /* get the ID (if a device exists) */ > + id = bus->ops->get_dev_id(slot); > + if (id == NULL) > + return ERR_PTR(-ENODEV); > + > + /* slot must not have a device yet */ > + dev = slot->dev; > + if (dev == NULL) { > + dev = kzalloc(sizeof(*dev), GFP_KERNEL); > + if (dev == NULL) { > + dev_info(&bus->dev, "Failed to allocate cape device " > + "for slot #%d\n", slot->slotno); > + return ERR_PTR(-ENOMEM); > + } > + > + INIT_LIST_HEAD(&dev->bus_list); > + dev->bus = bus; > + dev->slot = slot; > + } > + > + dev->id = id; > + dev->text_id = bus->ops->get_text_dev_id(slot); > + > + /* capebus_set_of_node(dev); TODO */ > + > + return dev; > +} > + > +int cape_bus_scan_one_slot(struct cape_bus *bus, struct cape_slot *slot) > +{ > + struct cape_dev *dev; > + int r; > + > + mutex_lock(&cape_buses_mutex); > + > + dev = slot->dev; > + if (dev == NULL) { > + > + dev = cape_bus_scan_slot(slot); > + if (IS_ERR(dev)) { > + r = PTR_ERR(dev); > + goto err_out; > + } > + > + dev_info(&bus->dev, "Slot #%d id='%s'\n", slot->slotno, > + dev->text_id ? dev->text_id : ""); > + > + slot->dev = dev; > + > + dev->dev.release = cape_bus_release_dev; > + dev->dev.parent = &dev->bus->dev; > + dev->dev.bus = &capebus_bus_type; > + dev_set_name(&dev->dev, "%s-%d:%d", > + dev->bus->name, dev->bus->busno, > + dev->slot->slotno); > + > + list_add_tail(&dev->bus_list, &bus->devices); > + > + } else { > + dev_info(&bus->dev, "Slot #%d id='%s' - rescan\n", > slot->slotno, > + dev->text_id ? dev->text_id : ""); > + > + if (dev->added) { > + r = -EEXIST; > + goto err_out; > + } > + } > + > + r = device_register(&dev->dev); > + if (r != 0) { > + dev_info(&bus->dev, "Slot #%d id='%s' - " > + "Failed to register\n", > + slot->slotno, > + dev->text_id ? dev->text_id : ""); > + r = 0; > + } else { > + if (dev->bus->ops->dev_registered) > + dev->bus->ops->dev_registered(dev); > + } > + > +err_out: > + mutex_unlock(&cape_buses_mutex); > + > + return r; > +} > + > +int cape_bus_register_slot(struct cape_bus *bus, struct cape_slot *slot, > + int slotno) > +{ > + struct cape_slot *s2; > + int r; > + > + r = 0; > + > + /* invalid (slot must always be numbered - no hotplug) */ > + if (slotno < 0) { > + dev_err(&bus->dev, "Slot registration #%d failed\n", slotno); > + return -EINVAL; > + } > + > + mutex_lock(&cape_buses_mutex); > + s2 = cape_slot_find(bus, slotno); > + if (s2 != NULL) { > + dev_err(&bus->dev, "Slot #%d already exists\n", slotno); > + mutex_unlock(&cape_buses_mutex); > + return -EINVAL; > + } > + > + INIT_LIST_HEAD(&slot->node); > + slot->bus = bus; > + list_add(&slot->node, &bus->slots); > + slot->slotno = slotno; > + slot->dev = NULL; > + mutex_unlock(&cape_buses_mutex); > + > + dev_info(&bus->dev, "Slot #%d registered\n", slot->slotno); > + > + return cape_bus_scan_one_slot(bus, slot); > +} > diff --git a/drivers/capebus/capebus-sysfs.c b/drivers/capebus/capebus-sysfs.c > new file mode 100644 > index 0000000..81c21fe > --- /dev/null > +++ b/drivers/capebus/capebus-sysfs.c > @@ -0,0 +1,52 @@ > +/* > + * drivers/capebus/capebus-sysfs.c > + * > + * sysfs for capebus devices > + * > + * Copyright (C) 2012 Pantelis Antoniou <pa...@antoniou-consulting.com> > + * Copyright (C) 2012 Texas Instruments Inc. > + * > + * This program is free software; you can redistribute it and/or modify > + * it under the terms of the GNU General Public License as published by > + * the Free Software Foundation; either version 2 of the License, or > + * (at your option) any later version. > + * > + * This program is distributed in the hope that it will be useful, > + * but WITHOUT ANY WARRANTY; without even the implied warranty of > + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the > + * GNU General Public License for more details. > + * > + * You should have received a copy of the GNU General Public License > + * along with this program; if not, write to the Free Software > + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. > + * > + * Modeled after PCI's pci-sysfs.c > + * > + */ > + > +#include <linux/kernel.h> > +#include <linux/stat.h> > +#include <linux/export.h> > +#include <linux/fs.h> > +#include <linux/slab.h> > +#include <linux/pm_runtime.h> > + > +#include <linux/capebus.h> > + > +static ssize_t id_show(struct device *dev, > + struct device_attribute *attr, char *buf) > +{ > + struct cape_dev *cdev; > + > + cdev = to_cape_dev(dev); > + return sprintf(buf, "%s\n", cdev->text_id); > +} > + > +struct device_attribute capebus_dev_attrs[] = { > + __ATTR_RO(id), > + __ATTR_NULL, > +}; > + > +struct bus_attribute capebus_bus_attrs[] = { > + __ATTR_NULL > +}; > diff --git a/include/linux/capebus.h b/include/linux/capebus.h > new file mode 100644 > index 0000000..7524401 > --- /dev/null > +++ b/include/linux/capebus.h > @@ -0,0 +1,298 @@ > +/* > + * capebus.h > + * > + * Cape bus defines and function prototypes > + * > + * Copyright (C) 2012 Pantelis Antoniou <pa...@antoniou-consulting.com> > + * Copyright (C) 2012 Texas Instruments Inc. > + * > + * This program is free software; you can redistribute it and/or modify > + * it under the terms of the GNU General Public License as published by > + * the Free Software Foundation; either version 2 of the License, or > + * (at your option) any later version. > + * > + * This program is distributed in the hope that it will be useful, > + * but WITHOUT ANY WARRANTY; without even the implied warranty of > + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the > + * GNU General Public License for more details. > + * > + * You should have received a copy of the GNU General Public License > + * along with this program; if not, write to the Free Software > + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. > + */ > +#ifndef LINUX_CAPEBUS_H > +#define LINUX_CAPEBUS_H > + > +#include <linux/list.h> > +#include <linux/device.h> > +#include <linux/spinlock.h> > +#include <linux/types.h> > +#include <linux/atomic.h> > +#include <linux/of.h> > +#include <linux/of_device.h> > + > +struct cape_device_id { > + const char *cntrlboard; /* controlling board; i.e. "beaglebone" */ > + int len; /* opaque addressing data */ > + const void *data; > +}; > + > +struct cape_dev; > +struct cape_bus; > +struct cape_slot; > + > +struct cape_slot { > + struct list_head node; > + struct cape_bus *bus; /* the bus this slot is on */ > + int slotno; /* index of this slot */ > + struct cape_dev *dev; /* the device (if found) */ > +}; > + > +struct cape_driver { > + struct list_head node; > + int (*probe)(struct cape_dev *dev, const struct cape_device_id *id); > + void (*remove)(struct cape_dev *dev); > + int (*suspend) (struct cape_dev *dev, pm_message_t state); > + int (*suspend_late) (struct cape_dev *dev, pm_message_t state); > + int (*resume_early) (struct cape_dev *dev); > + int (*resume) (struct cape_dev *dev); > + void (*shutdown) (struct cape_dev *dev); > + struct device_driver driver; > +}; > + > +/* > + * capebus_register_driver must be a macro so that > + * KBUILD_MODNAME can be expanded > + */ > +#define capebus_register_driver(driver) \ > + __capebus_register_driver(driver, THIS_MODULE, KBUILD_MODNAME) > + > +int __capebus_register_driver(struct cape_driver *drv, struct module *owner, > + const char *mod_name); > + > +void capebus_unregister_driver(struct cape_driver *dev); > + > +/** > + * module_capebus_driver() - Helper macro for registering a capebus driver > + * @__capebus_driver: capebus_driver struct > + * > + * Helper macro for capebus drivers which do not do anything special in > module > + * init/exit. This eliminates a lot of boilerplate. Each module may only > + * use this macro once, and calling it replaces module_init() and > module_exit() > + */ > +#define module_capebus_driver(__capebus_driver) \ > + module_driver(__capebus_driver, capebus_register_driver, \ > + capebus_unregister_driver) > + > +#define to_cape_driver(n) container_of(n, struct cape_driver, driver) > + > +struct cape_bus_ops { > + const struct cape_device_id *(*get_dev_id)(struct cape_slot *slot); > + const char *(*get_text_dev_id)(struct cape_slot *slot); > + int (*dev_probed)(struct cape_dev *dev); /* probed succesfully > */ > + void (*dev_removed)(struct cape_dev *dev); /* removed */ > + int (*dev_registered)(struct cape_dev *dev); /* registered OK */ > +}; > + > +struct cape_bus { > + struct list_head node; > + const char *name; > + struct list_head devices; > + struct cape_dev *self; > + struct list_head slots; > + struct cape_bus_ops *ops; > + int busno; > + struct device dev; > + /* TODO: resources.... */ > +}; > + > +#define to_cape_bus(n) container_of(n, struct cape_bus, dev) > + > +#define cape_bus_to_parent_of_node(n) ((n)->dev.parent->of_node) > + > +struct cape_dev { > + struct list_head bus_list; /* node in per-bus list */ > + struct cape_bus *bus; /* bus this device is on */ > + struct cape_slot *slot; /* cape slot of this device */ > + struct cape_driver *driver; /* driver of this device */ > + struct device dev; > + atomic_t enable_cnt; /* capebus_enable_device */ > + /* has been called */ > + const struct cape_device_id *id; > + const char *text_id; > + unsigned int added : 1; /* device has been added */ > + void *drv_priv; /* driver private data */ > +}; > + > +#define to_cape_dev(n) container_of(n, struct cape_dev, dev) > + > +struct cape_dev *capebus_dev_get(struct cape_dev *dev); > +void capebus_dev_put(struct cape_dev *dev); > + > +/* must have cape_buses_mutex */ > +#define cape_bus_for_each(_bus) \ > + list_for_each_entry(_bus, &cape_buses, node) > + > +#define cape_bus_for_each_safe(_bus, _busn) \ > + list_for_each_entry_safe(_bus, _busn, &cape_buses, node) > + > +int cape_bus_register(struct cape_bus *bus, const char *name, int busno, > + struct device *parent, struct cape_bus_ops *ops); > + > +/* must have cape_buses_mutex */ > +#define cape_slot_for_each(_bus, _slot) \ > + list_for_each_entry(_slot, &(_bus)->slots, node) > + > +#define cape_slot_for_each_safe(_bus, _slot, _slotn) \ > + list_for_each_entry_safe(_slot, _slotn, &(_bus)->slots, node) > + > +int cape_bus_register_slot(struct cape_bus *bus, > + struct cape_slot *slot, int slotno); > + > +int cape_bus_scan_one_slot(struct cape_bus *bus, struct cape_slot *slot); > +int cape_bus_scan(struct cape_bus *bus); > + > +extern struct list_head cape_buses; > +extern struct mutex cape_buses_mutex; > + > +static inline int capebus_is_enabled(struct cape_dev *cdev) > +{ > + return atomic_read(&cdev->enable_cnt) > 0; > +} > + > +static inline int capebus_enable_device(struct cape_dev *cdev) > +{ > + if (atomic_add_return(1, &cdev->enable_cnt) > 1) > + return 0; /* already enabled */ > + > + /* XXX do enable */ > + > + return 0; > +} > + > +static inline void capebus_disable_device(struct cape_dev *cdev) > +{ > + if (atomic_sub_return(1, &cdev->enable_cnt) != 0) > + return; > + > + /* callback to disable device? */ > +} > + > +static inline int capebus_enable_wake(struct cape_dev *dev, int what) > +{ > + return 0; > +} > + > +extern struct device_attribute capebus_dev_attrs[]; > +extern struct bus_attribute capebus_bus_attrs[]; > + > +extern struct bus_type capebus_bus_type; > + > +const struct of_device_id * > +capebus_of_match_device(struct cape_dev *cdev, > + const char *property, const char *value); > + > +struct device_node * > +capebus_of_compatible_device_property_match(struct cape_dev *dev, > + const struct of_device_id *matches, > + const char *prop, const char *prop_value); > + > +struct platform_device * > +capebus_of_platform_compatible_device_create(struct cape_dev *dev, > + const struct of_device_id *matches, > + const char *pdev_name, > + const char *prop, const char *prop_value); > + > +/* of tree support */ > + > +struct device_node * > +capebus_of_find_property_node(struct cape_dev *dev, > + const char *prop, const char *prop_value, > + const char *name); > + > +struct property * > +capebus_of_find_property(struct cape_dev *dev, > + const char *prop, const char *prop_value, > + const char *name, int *lenp); > + > +const void *capebus_of_get_property(struct cape_dev *dev, > + const char *prop, const char *prop_value, > + const char *name, int *lenp); > + > +static inline int capebus_of_property_read_u32_array(struct cape_dev *dev, > + const char *prop, const char *prop_value, > + const char *name, u32 *out_values, size_t sz) > +{ > + struct device_node *node; > + int ret; > + > + node = capebus_of_find_property_node(dev, prop, prop_value, name); > + ret = of_property_read_u32_array(node, name, out_values, sz); > + of_node_put(node); > + return ret; > +} > + > +static inline int capebus_of_property_read_u32(struct cape_dev *dev, > + const char *prop, const char *prop_value, > + const char *name, u32 *out_value) > +{ > + return capebus_of_property_read_u32_array(dev, prop, > + prop_value, name, out_value, 1); > +} > + > +static inline bool capebus_of_property_read_bool(struct cape_dev *dev, > + const char *prop, const char *prop_value, > + const char *name) > +{ > + struct device_node *node; > + bool ret; > + > + node = capebus_of_find_property_node(dev, prop, prop_value, name); > + ret = of_property_read_bool(node, name); > + of_node_put(node); > + return ret; > +} > + > +static inline int capebus_of_property_read_string(struct cape_dev *dev, > + const char *prop, const char *prop_value, > + const char *name, const char **out_string) > +{ > + struct device_node *node; > + int ret; > + > + node = capebus_of_find_property_node(dev, prop, prop_value, name); > + ret = of_property_read_string(node, name, out_string); > + of_node_put(node); > + return ret; > +} > + > +static inline int capebus_of_property_read_string_index(struct cape_dev *dev, > + const char *prop, const char *prop_value, > + const char *name, int index, const char **out_string) > +{ > + struct device_node *node; > + int ret; > + > + node = capebus_of_find_property_node(dev, prop, prop_value, name); > + ret = of_property_read_string_index(node, name, index, out_string); > + of_node_put(node); > + return ret; > +} > + > +static inline int capebus_of_property_read_u64(struct cape_dev *dev, > + const char *prop, const char *prop_value, > + const char *name, u64 *out_value) > +{ > + struct device_node *node; > + int ret; > + > + node = capebus_of_find_property_node(dev, prop, prop_value, name); > + ret = of_property_read_u64(node, name, out_value); > + of_node_put(node); > + return ret; > +} > + > +int capebus_of_device_node_enable(struct device_node *node); > +int capebus_of_platform_device_enable(struct device_node *node); > + > +#endif > -- > 1.7.12 > > -- > To unsubscribe from this list: send the line "unsubscribe linux-omap" in > the body of a message to majord...@vger.kernel.org > More majordomo info at http://vger.kernel.org/majordomo-info.html -- To unsubscribe from this list: send the line "unsubscribe linux-omap" in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html