[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