[RFC 1/7] capebus: Core capebus support

Russ Dill Russ.Dill at ti.com
Wed Oct 31 17:55:18 EDT 2012


On Wed, Oct 31, 2012 at 9:52 AM, Pantelis Antoniou
<panto at 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 <panto at 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 <panto at 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 <panto at 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 <panto at 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 <panto at 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 majordomo at vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html



More information about the linux-arm-kernel mailing list