Capebus is created to address the problem of many SoCs that can provide a
multitude of hardware interfaces but in order to keep costs down the main
boards only support a limited number of them. The rest are typically brought
out to pin connectors on to which other boards, named capes are connected and
allow those peripherals to be used.
These capes connect to the SoC interfaces but might also contain various other
parts that may need some kind of driver to work.
Since SoCs have limited pins and pin muxing options, not all capes can work
together so some kind of resource tracking (at least for the pins in use) is
required.
Before capebus all of this took place in the board support file, and frankly
for boards with too many capes it was becoming unmanageable.
Capebus provides a virtual bus, which along with a board specific controller,
cape drivers can be written using the standard Linux device model.
The core capebus infrastructure is not depended on any specific board.
However capebus needs a board controller to provide services to the cape devices
it controls. Services like addressing and resource reservation are provided
by the board controller.
Capebus at the moment only support TI's Beaglebone platform.
This RFC introduces the core concept; most supporting patches
have been posted to the relevant places.
If you have a beaglebone and want to check it out, you can do
so at:
git://github.com/pantoniou/linux-bbxm.git branch capebus-v3
Pantelis Antoniou (7):
capebus: Core capebus support
capebus: Add beaglebone board support
capebus: Beaglebone generic cape support
capebus: Beaglebone geiger cape support
capebus: Beaglebone capebus DT update
capebus: Document DT bindings
capebus: Documentation; capebus-summary
Documentation/capebus/capebus-summary | 40 +
.../capebus/bone-capebus-slot-override.txt | 28 +
.../devicetree/bindings/capebus/bone-capebus.txt | 50 ++
.../bindings/capebus/bone-geiger-cape.txt | 78 ++
.../bindings/capebus/bone-generic-cape.txt | 97 +++
.../devicetree/bindings/capebus/da8xx-dt.txt | 31 +
.../devicetree/bindings/capebus/i2c-dt.txt | 42 +
.../devicetree/bindings/capebus/spi-dt.txt | 37 +
.../devicetree/bindings/capebus/ti-tscadc-dt.txt | 34 +
arch/arm/boot/dts/am335x-bone-common.dtsi | 689 ++++++++++++++-
drivers/Kconfig | 2 +
drivers/Makefile | 3 +
drivers/capebus/Kconfig | 17 +
drivers/capebus/Makefile | 8 +
drivers/capebus/boards/Kconfig | 6 +
drivers/capebus/boards/Makefile | 3 +
drivers/capebus/boards/capebus-bone-generic.c | 237 ++++++
drivers/capebus/boards/capebus-bone-pdevs.c | 602 +++++++++++++
drivers/capebus/boards/capebus-bone.c | 931 +++++++++++++++++++++
drivers/capebus/capebus-driver.c | 608 ++++++++++++++
drivers/capebus/capebus-probe.c | 320 +++++++
drivers/capebus/capebus-sysfs.c | 52 ++
drivers/capebus/capes/Kconfig | 13 +
drivers/capebus/capes/Makefile | 2 +
drivers/capebus/capes/bone-geiger-cape.c | 506 +++++++++++
drivers/capebus/capes/bone-generic-cape.c | 96 +++
include/linux/capebus.h | 298 +++++++
include/linux/capebus/capebus-bone.h | 120 +++
28 files changed, 4920 insertions(+), 30 deletions(-)
create mode 100644 Documentation/capebus/capebus-summary
create mode 100644 Documentation/devicetree/bindings/capebus/bone-capebus-slot-override.txt
create mode 100644 Documentation/devicetree/bindings/capebus/bone-capebus.txt
create mode 100644 Documentation/devicetree/bindings/capebus/bone-geiger-cape.txt
create mode 100644 Documentation/devicetree/bindings/capebus/bone-generic-cape.txt
create mode 100644 Documentation/devicetree/bindings/capebus/da8xx-dt.txt
create mode 100644 Documentation/devicetree/bindings/capebus/i2c-dt.txt
create mode 100644 Documentation/devicetree/bindings/capebus/spi-dt.txt
create mode 100644 Documentation/devicetree/bindings/capebus/ti-tscadc-dt.txt
create mode 100644 drivers/capebus/Kconfig
create mode 100644 drivers/capebus/Makefile
create mode 100644 drivers/capebus/boards/Kconfig
create mode 100644 drivers/capebus/boards/Makefile
create mode 100644 drivers/capebus/boards/capebus-bone-generic.c
create mode 100644 drivers/capebus/boards/capebus-bone-pdevs.c
create mode 100644 drivers/capebus/boards/capebus-bone.c
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 drivers/capebus/capes/Kconfig
create mode 100644 drivers/capebus/capes/Makefile
create mode 100644 drivers/capebus/capes/bone-geiger-cape.c
create mode 100644 drivers/capebus/capes/bone-generic-cape.c
create mode 100644 include/linux/capebus.h
create mode 100644 include/linux/capebus/capebus-bone.h
--
1.7.12
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 <[email protected]>
---
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 <[email protected]>
+ * 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;
+ }
+ 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 <[email protected]>
+ * 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 <[email protected]>
+ * 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 <[email protected]>
+ * 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
Update the common beaglebone's DTS with the required DT
entries for all known working capes as of now.
Signed-off-by: Pantelis Antoniou <[email protected]>
---
arch/arm/boot/dts/am335x-bone-common.dtsi | 689 ++++++++++++++++++++++++++++--
1 file changed, 659 insertions(+), 30 deletions(-)
diff --git a/arch/arm/boot/dts/am335x-bone-common.dtsi b/arch/arm/boot/dts/am335x-bone-common.dtsi
index 99240a5..46d5f27 100644
--- a/arch/arm/boot/dts/am335x-bone-common.dtsi
+++ b/arch/arm/boot/dts/am335x-bone-common.dtsi
@@ -51,6 +51,143 @@
0x60 0x17 /* gpmc_a8.gpio1_24, OUTPUT_PULLUP | MODE7 */
>;
};
+ i2c2_pins: pinmux_i2c2_pins {
+ pinctrl-single,pins = <
+ 0x178 0x73 /* uart1_ctsn.i2c2_sda, SLEWCTRL_SLOW | INPUT_PULLUP | MODE3 */
+ 0x17c 0x73 /* uart1_rtsn.i2c2_scl, SLEWCTRL_SLOW | INPUT_PULLUP | MODE3 */
+ >;
+ };
+
+ bone_dvi_cape_led_pins: pinmux_bone_dvi_cape_led_pins {
+ pinctrl-single,pins = <
+ 0x48 0x07 /* gpmc_a2.gpio1_18, OUTPUT | MODE7 */
+ 0x4c 0x07 /* gpmc_a3.gpio1_19, OUTPUT | MODE7 */
+ >;
+ };
+
+ bone_dvi_cape_dvi_00A0_pins: pinmux_bone_dvi_cape_dvi_00A0_pins {
+ pinctrl-single,pins = <
+ 0x1c 0x07 /* gpmc_ad7.gpio1_7, OUTPUT | MODE7 - DVIPDn */
+
+ 0xa0 0x08 /* lcd_data0.lcd_data0, OMAP_MUX_MODE0 | AM33XX_PIN_OUTPUT | AM33XX_PULL_DISA */
+ 0xa4 0x08 /* lcd_data1.lcd_data1, OMAP_MUX_MODE0 | AM33XX_PIN_OUTPUT | AM33XX_PULL_DISA */
+ 0xa8 0x08 /* lcd_data2.lcd_data2, OMAP_MUX_MODE0 | AM33XX_PIN_OUTPUT | AM33XX_PULL_DISA */
+ 0xac 0x08 /* lcd_data3.lcd_data3, OMAP_MUX_MODE0 | AM33XX_PIN_OUTPUT | AM33XX_PULL_DISA */
+ 0xb0 0x08 /* lcd_data4.lcd_data4, OMAP_MUX_MODE0 | AM33XX_PIN_OUTPUT | AM33XX_PULL_DISA */
+ 0xb4 0x08 /* lcd_data5.lcd_data5, OMAP_MUX_MODE0 | AM33XX_PIN_OUTPUT | AM33XX_PULL_DISA */
+ 0xb8 0x08 /* lcd_data6.lcd_data6, OMAP_MUX_MODE0 | AM33XX_PIN_OUTPUT | AM33XX_PULL_DISA */
+ 0xbc 0x08 /* lcd_data7.lcd_data7, OMAP_MUX_MODE0 | AM33XX_PIN_OUTPUT | AM33XX_PULL_DISA */
+ 0xc0 0x08 /* lcd_data8.lcd_data8, OMAP_MUX_MODE0 | AM33XX_PIN_OUTPUT | AM33XX_PULL_DISA */
+ 0xc4 0x08 /* lcd_data9.lcd_data9, OMAP_MUX_MODE0 | AM33XX_PIN_OUTPUT | AM33XX_PULL_DISA */
+ 0xc8 0x08 /* lcd_data10.lcd_data10, OMAP_MUX_MODE0 | AM33XX_PIN_OUTPUT | AM33XX_PULL_DISA */
+ 0xcc 0x08 /* lcd_data11.lcd_data11, OMAP_MUX_MODE0 | AM33XX_PIN_OUTPUT | AM33XX_PULL_DISA */
+ 0xd0 0x08 /* lcd_data12.lcd_data12, OMAP_MUX_MODE0 | AM33XX_PIN_OUTPUT | AM33XX_PULL_DISA */
+ 0xd4 0x08 /* lcd_data13.lcd_data13, OMAP_MUX_MODE0 | AM33XX_PIN_OUTPUT | AM33XX_PULL_DISA */
+ 0xd8 0x08 /* lcd_data14.lcd_data14, OMAP_MUX_MODE0 | AM33XX_PIN_OUTPUT | AM33XX_PULL_DISA */
+ 0xdc 0x08 /* lcd_data15.lcd_data15, OMAP_MUX_MODE0 | AM33XX_PIN_OUTPUT | AM33XX_PULL_DISA */
+ 0xe0 0x00 /* lcd_vsync.lcd_vsync, OMAP_MUX_MODE0 | AM33XX_PIN_OUTPUT */
+ 0xe4 0x00 /* lcd_hsync.lcd_hsync, OMAP_MUX_MODE0 | AM33XX_PIN_OUTPUT */
+ 0xe8 0x00 /* lcd_pclk.lcd_pclk, OMAP_MUX_MODE0 | AM33XX_PIN_OUTPUT */
+ 0xec 0x00 /* lcd_ac_bias_en.lcd_ac_bias_en, OMAP_MUX_MODE0 | AM33XX_PIN_OUTPUT */
+ >;
+ };
+
+ bone_dvi_cape_dvi_00A1_pins: pinmux_bone_dvi_cape_dvi_00A1_pins {
+ pinctrl-single,pins = <
+ 0x84 0x07 /* gpmc_csn2.gpio1_31, OUTPUT | MODE7 - DVIPDn */
+
+ 0xa0 0x08 /* lcd_data0.lcd_data0, OMAP_MUX_MODE0 | AM33XX_PIN_OUTPUT | AM33XX_PULL_DISA */
+ 0xa4 0x08 /* lcd_data1.lcd_data1, OMAP_MUX_MODE0 | AM33XX_PIN_OUTPUT | AM33XX_PULL_DISA */
+ 0xa8 0x08 /* lcd_data2.lcd_data2, OMAP_MUX_MODE0 | AM33XX_PIN_OUTPUT | AM33XX_PULL_DISA */
+ 0xac 0x08 /* lcd_data3.lcd_data3, OMAP_MUX_MODE0 | AM33XX_PIN_OUTPUT | AM33XX_PULL_DISA */
+ 0xb0 0x08 /* lcd_data4.lcd_data4, OMAP_MUX_MODE0 | AM33XX_PIN_OUTPUT | AM33XX_PULL_DISA */
+ 0xb4 0x08 /* lcd_data5.lcd_data5, OMAP_MUX_MODE0 | AM33XX_PIN_OUTPUT | AM33XX_PULL_DISA */
+ 0xb8 0x08 /* lcd_data6.lcd_data6, OMAP_MUX_MODE0 | AM33XX_PIN_OUTPUT | AM33XX_PULL_DISA */
+ 0xbc 0x08 /* lcd_data7.lcd_data7, OMAP_MUX_MODE0 | AM33XX_PIN_OUTPUT | AM33XX_PULL_DISA */
+ 0xc0 0x08 /* lcd_data8.lcd_data8, OMAP_MUX_MODE0 | AM33XX_PIN_OUTPUT | AM33XX_PULL_DISA */
+ 0xc4 0x08 /* lcd_data9.lcd_data9, OMAP_MUX_MODE0 | AM33XX_PIN_OUTPUT | AM33XX_PULL_DISA */
+ 0xc8 0x08 /* lcd_data10.lcd_data10, OMAP_MUX_MODE0 | AM33XX_PIN_OUTPUT | AM33XX_PULL_DISA */
+ 0xcc 0x08 /* lcd_data11.lcd_data11, OMAP_MUX_MODE0 | AM33XX_PIN_OUTPUT | AM33XX_PULL_DISA */
+ 0xd0 0x08 /* lcd_data12.lcd_data12, OMAP_MUX_MODE0 | AM33XX_PIN_OUTPUT | AM33XX_PULL_DISA */
+ 0xd4 0x08 /* lcd_data13.lcd_data13, OMAP_MUX_MODE0 | AM33XX_PIN_OUTPUT | AM33XX_PULL_DISA */
+ 0xd8 0x08 /* lcd_data14.lcd_data14, OMAP_MUX_MODE0 | AM33XX_PIN_OUTPUT | AM33XX_PULL_DISA */
+ 0xdc 0x08 /* lcd_data15.lcd_data15, OMAP_MUX_MODE0 | AM33XX_PIN_OUTPUT | AM33XX_PULL_DISA */
+ 0xe0 0x00 /* lcd_vsync.lcd_vsync, OMAP_MUX_MODE0 | AM33XX_PIN_OUTPUT */
+ 0xe4 0x00 /* lcd_hsync.lcd_hsync, OMAP_MUX_MODE0 | AM33XX_PIN_OUTPUT */
+ 0xe8 0x00 /* lcd_pclk.lcd_pclk, OMAP_MUX_MODE0 | AM33XX_PIN_OUTPUT */
+ 0xec 0x00 /* lcd_ac_bias_en.lcd_ac_bias_en, OMAP_MUX_MODE0 | AM33XX_PIN_OUTPUT */
+ >;
+ };
+
+ bone_geiger_cape_led_pins: pinmux_bone_geiger_cape_led_pins {
+ pinctrl-single,pins = <
+ 0xe4 0x07 /* lcd_hsync.gpio2_23, OUTPUT | MODE7 */
+ 0xec 0x07 /* lcd_ac_bias_en.gpio2_25, OUTPUT | MODE7 */
+ >;
+ };
+
+ bone_geiger_cape_pins: pinmux_bone_geiger_cape_pins {
+ pinctrl-single,pins = <
+ 0x48 0x06 /* gpmc_a2.ehrpwm1a, OMAP_MUX_MODE6 | AM33XX_PIN_OUTPUT */
+ /* 0x19c 0x34 */ /* mcasp0_ahclkr.eCAP2_in_PWM2_out, OMAP_MUX_MODE4 | INPUT_PULLUP */
+ 0x19c 0x37 /* mcasp0_ahclkr.gpio3_17, OMAP_MUX_MODE4 | INPUT_PULLUP */
+ >;
+ };
+
+ bone_lcd3_cape_led_00A0_pins: pinmux_bone_lcd3_cape_led_00A0_pins {
+ pinctrl-single,pins = <
+ 0x48 0x07 /* gpmc_a2.gpio1_18, OUTPUT | MODE7 */
+ 0x4c 0x07 /* gpmc_a3.gpio1_19, OUTPUT | MODE7 */
+ >;
+ };
+
+ bone_lcd3_cape_lcd_pins: pinmux_bone_lcd3_cape_lcd_pins {
+ pinctrl-single,pins = <
+ 0xa0 0x08 /* lcd_data0.lcd_data0, OMAP_MUX_MODE0 | AM33XX_PIN_OUTPUT | AM33XX_PULL_DISA */
+ 0xa4 0x08 /* lcd_data1.lcd_data1, OMAP_MUX_MODE0 | AM33XX_PIN_OUTPUT | AM33XX_PULL_DISA */
+ 0xa8 0x08 /* lcd_data2.lcd_data2, OMAP_MUX_MODE0 | AM33XX_PIN_OUTPUT | AM33XX_PULL_DISA */
+ 0xac 0x08 /* lcd_data3.lcd_data3, OMAP_MUX_MODE0 | AM33XX_PIN_OUTPUT | AM33XX_PULL_DISA */
+ 0xb0 0x08 /* lcd_data4.lcd_data4, OMAP_MUX_MODE0 | AM33XX_PIN_OUTPUT | AM33XX_PULL_DISA */
+ 0xb4 0x08 /* lcd_data5.lcd_data5, OMAP_MUX_MODE0 | AM33XX_PIN_OUTPUT | AM33XX_PULL_DISA */
+ 0xb8 0x08 /* lcd_data6.lcd_data6, OMAP_MUX_MODE0 | AM33XX_PIN_OUTPUT | AM33XX_PULL_DISA */
+ 0xbc 0x08 /* lcd_data7.lcd_data7, OMAP_MUX_MODE0 | AM33XX_PIN_OUTPUT | AM33XX_PULL_DISA */
+ 0xc0 0x08 /* lcd_data8.lcd_data8, OMAP_MUX_MODE0 | AM33XX_PIN_OUTPUT | AM33XX_PULL_DISA */
+ 0xc4 0x08 /* lcd_data9.lcd_data9, OMAP_MUX_MODE0 | AM33XX_PIN_OUTPUT | AM33XX_PULL_DISA */
+ 0xc8 0x08 /* lcd_data10.lcd_data10, OMAP_MUX_MODE0 | AM33XX_PIN_OUTPUT | AM33XX_PULL_DISA */
+ 0xcc 0x08 /* lcd_data11.lcd_data11, OMAP_MUX_MODE0 | AM33XX_PIN_OUTPUT | AM33XX_PULL_DISA */
+ 0xd0 0x08 /* lcd_data12.lcd_data12, OMAP_MUX_MODE0 | AM33XX_PIN_OUTPUT | AM33XX_PULL_DISA */
+ 0xd4 0x08 /* lcd_data13.lcd_data13, OMAP_MUX_MODE0 | AM33XX_PIN_OUTPUT | AM33XX_PULL_DISA */
+ 0xd8 0x08 /* lcd_data14.lcd_data14, OMAP_MUX_MODE0 | AM33XX_PIN_OUTPUT | AM33XX_PULL_DISA */
+ 0xdc 0x08 /* lcd_data15.lcd_data15, OMAP_MUX_MODE0 | AM33XX_PIN_OUTPUT | AM33XX_PULL_DISA */
+ 0xe0 0x00 /* lcd_vsync.lcd_vsync, OMAP_MUX_MODE0 | AM33XX_PIN_OUTPUT */
+ 0xe4 0x00 /* lcd_hsync.lcd_hsync, OMAP_MUX_MODE0 | AM33XX_PIN_OUTPUT */
+ 0xe8 0x00 /* lcd_pclk.lcd_pclk, OMAP_MUX_MODE0 | AM33XX_PIN_OUTPUT */
+ 0xec 0x00 /* lcd_ac_bias_en.lcd_ac_bias_en, OMAP_MUX_MODE0 | AM33XX_PIN_OUTPUT */
+ >;
+ };
+
+ bone_lcd3_cape_keys_00A0_pins: pinmux_bone_lcd3_cape_keys_00A0_pins {
+ pinctrl-single,pins = <
+ 0x040 0x2f /* gpmc_a0.gpio1_16, INPUT | PULLDIS | MODE7 */
+ 0x044 0x2f /* gpmc_a1.gpio1_17, INPUT | PULLDIS | MODE7 */
+ 0x1a4 0x2f /* mcasp0_fsr.gpio3_19, INPUT | PULLDIS | MODE7 */
+ 0x078 0x2f /* gpmc_ben1.gpio1_28, INPUT | PULLDIS | MODE7 */
+ 0x164 0x2f /* ecap0_in_pwm0_out.gpio0_7, INPUT | PULLDIS | MODE7 */
+ >;
+ };
+
+ pwm_bl_pins: pinmux_pwm_bl_pins {
+ pinctrl-single,pins = <
+ 0x4c 0x06 /* gpmc_a3.ehrpwm1b, OMAP_MUX_MODE6 | AM33XX_PIN_OUTPUT */
+ // 0x48 0x06 /* gpmc_a2.ehrpwm1a, OMAP_MUX_MODE6 | AM33XX_PIN_OUTPUT */
+ >;
+ };
+
+ weather_cape_w1_pins: pinmux_weather_cape_w1_pins {
+ pinctrl-single,pins = <
+ 0x0c 0x37 /* gpmc_ad3.gpio1_3, OMAP_PIN_INPUT_PULLUP | OMAP_MUX_MODE7 - w1-gpio */
+ >;
+ };
};
ocp {
@@ -92,16 +229,6 @@
};
};
- i2c1: i2c@44e0b000 {
- status = "okay";
- clock-frequency = <400000>;
-
- tps: tps@24 {
- reg = <0x24>;
- };
-
- };
-
gpevt {
compatible = "gpevt";
pinctrl-names = "default";
@@ -110,15 +237,117 @@
dma-names = "gpioevt";
gpio-evt = <&gpio3 2 0>;
};
+
+ };
+
+ capebus: capebus@0 {
+ compatible = "bone-capebus";
+
+ bone_dvi_cape: cape@0 {
+ compatible = "bone-generic-cape";
+ };
+
+ bone_geiger_cape: cape@1 {
+ compatible = "bone-geiger-cape";
+ };
+
+ bone_lcd3_cape: cape@2 {
+ compatible = "bone-generic-cape";
+ };
+
+ bone_lcd7_cape: cape@3 {
+ compatible = "bone-lcd7-cape";
+ };
+
+ bone_weather_cape: cape@4 {
+ compatible = "bone-generic-cape";
+ };
+
+ bone_adafruit_cape: cape@5 {
+ compatible = "bone-generic-cape";
+ };
+
+ /* overrides; no EEPROM (prototyping) */
+// override@3 {
+// compatible = "bone-capebus-slot-override";
+// slot = <3>;
+// board-name = "Geiger Cape";
+// version = "00A0";
+// manufacturer = "Geiger Inc";
+// /* TODO: Add the rest */
+// };
+
+// override@2 {
+// compatible = "bone-capebus-slot-override";
+// slot = <2>;
+// board-name = "Weather Cape";
+// version = "00A0";
+// manufacturer = "CCO Inc";
+// /* TODO: Add the rest */
+// };
+
+// override@1 {
+// compatible = "bone-capebus-slot-override";
+// slot = <1>;
+// board-name = "Adafruit 1.8 Cape";
+// version = "00A0";
+// manufacturer = "Adafruit";
+// /* TODO: Add the rest */
+// };
+
+// override@0 {
+// compatible = "bone-capebus-slot-override";
+// slot = <0>;
+// board-name = "BeagleBone Weather CAPE";
+// version = "00A0";
+// manufacturer = "Beagleboardtoys";
+// /* TODO: Add the rest */
+// };
};
- backlight {
- compatible = "pwm-backlight";
- pwms = <&ehrpwm1 0 500000 0>;
- pwm-names = "st7735fb";
- brightness-levels = <0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100>;
- default-brightness-level = <50>; /* index to the array above */
- };
+};
+
+&i2c0 {
+ status = "okay";
+ clock-frequency = <400000>;
+
+ tps: tps@24 {
+ reg = <0x24>;
+ };
+
+ baseboard_eeprom: baseboard_eeprom@50 {
+ compatible = "at,24c256";
+ reg = <0x50>;
+ };
+};
+
+&i2c2 {
+ status = "okay";
+ pinctrl-names = "default";
+ pinctrl-0 = <&i2c2_pins>;
+
+ clock-frequency = <100000>;
+
+ /* OK, I know these are cape but for now it will do */
+ cape_eeprom_0: cape_eeprom_0@54 {
+ compatible = "at,24c256";
+ reg = <0x54>;
+ };
+
+ cape_eeprom_1: cape_eeprom_1@55 {
+ compatible = "at,24c256";
+ reg = <0x55>;
+ };
+
+ cape_eeprom_2: cape_eeprom_2@56 {
+ compatible = "at,24c256";
+ reg = <0x56>;
+ };
+
+ cape_eeprom_3: cape_eeprom_3@57 {
+ compatible = "at,24c256";
+ reg = <0x57>;
+ };
};
/include/ "tps65217.dtsi"
@@ -165,6 +394,7 @@
regulator-always-on;
};
};
+
};
&mmc1 {
@@ -172,21 +402,8 @@
};
&spi1 {
- status = "okay";
pinctrl-names = "default";
pinctrl-0 = <&spi1_pins>;
-
- lcd@0 {
- compatible = "adafruit,tft-lcd-1.8-green", "sitronix,st7735";
- spi-max-frequency = <8000000>;
- reg = <0>;
- spi-cpol;
- spi-cpha;
- pinctrl-names = "default";
- pinctrl-0 = <&lcd_pins>;
- st7735-rst = <&gpio4 19 0>;
- st7735-dc = <&gpio4 21 0>;
- };
};
&edma {
@@ -200,3 +417,415 @@
&cpsw_emac1 {
phy_id = "4a101000.mdio:01";
};
+
+&ehrpwm1 {
+ status = "okay";
+};
+
+&capebus {
+ slots = <&cape_eeprom_0 &cape_eeprom_1 &cape_eeprom_2 &cape_eeprom_3>;
+};
+
+&bone_dvi_cape {
+ board-name = "BeagleBone DVI-D CAPE";
+
+ /* hacky, since this is not a proper DT platform device */
+ /* but until we have DT bindings... */
+ version@00A0 {
+ version = "00A0";
+ dvi {
+ compatible = "da8xx-dt";
+ pinctrl-names = "default";
+ pinctrl-0 = <&bone_dvi_cape_dvi_00A0_pins>;
+ ti,hwmods = "lcdc";
+
+ disp-pll = <560000000>;
+ panel-type = "1024x768@60";
+ powerdn-gpio = <&gpio2 7 0>;
+ };
+ };
+
+ version@00A1 {
+ version = "00A1", "01";
+ dvi {
+ compatible = "da8xx-dt";
+ pinctrl-names = "default";
+ pinctrl-0 = <&bone_dvi_cape_dvi_00A1_pins>;
+ ti,hwmods = "lcdc";
+
+ disp-pll = <560000000>;
+ panel-type = "1024x768@60";
+ powerdn-gpio = <&gpio2 31 0>;
+ };
+ };
+
+ gpio-leds {
+ compatible = "gpio-leds";
+ pinctrl-names = "default";
+ pinctrl-0 = <&bone_dvi_cape_led_pins>;
+
+ dvi-led0 {
+ label = "dvi:green:usr0";
+ gpios = <&gpio2 18 0>;
+ linux,default-trigger = "heartbeat";
+ default-state = "off";
+ };
+
+ dvi-led1 {
+ label = "dvi:green:usr1";
+ gpios = <&gpio2 19 0>;
+ linux,default-trigger = "mmc0";
+ default-state = "off";
+ };
+ };
+};
+
+&bone_geiger_cape {
+ board-name = "Geiger Cape";
+
+ /* note that these can't be versioned... */
+ pinctrl-names = "default";
+ pinctrl-0 = <&bone_geiger_cape_pins>;
+
+ pwms = <&ehrpwm1 0 500000 0>;
+ pwm-names = "bone-geiger-cape";
+
+ pwm-frequency = <20000>; /* 20KHz */
+ pwm-duty-cycle = <60>; /* 60% */
+
+ event-blink-delay = <30>; /* 30ms */
+
+ gpios = <&gpio4 17 0>; /* pulse */
+
+ vsense-name = "AIN5"; /* analog vsense */
+ vsense-scale = <37325>; /* scaling */
+
+ tscadc {
+ compatible = "ti-tscadc-dt";
+
+ ti,hwmods = "adc_tsc";
+
+ adc-channels = <8>;
+ };
+
+ gpio-leds {
+ compatible = "gpio-leds";
+ pinctrl-names = "default";
+ pinctrl-0 = <&bone_geiger_cape_led_pins>;
+
+ geiger-led0 {
+ label = "geiger:green:usr0";
+ gpios = <&gpio3 23 0>;
+ linux,default-trigger = "geiger-run";
+ default-state = "off";
+ };
+
+ geiger-led1 {
+ label = "geiger:red:usr1";
+ gpios = <&gpio3 25 0>;
+ linux,default-trigger = "geiger-event";
+ default-state = "off";
+ };
+ };
+};
+
+&bone_lcd3_cape {
+ board-name = "BeagleBone LCD3 CAPE";
+
+ /* hacky, since this is not a proper DT platform device */
+ /* but until we have DT bindings... */
+ lcd3 {
+ compatible = "da8xx-dt";
+ pinctrl-names = "default";
+ pinctrl-0 = <&bone_lcd3_cape_lcd_pins>;
+
+ ti,hwmods = "lcdc";
+
+ disp-pll = <16000000>;
+ panel-type = "CDTech_S035Q01";
+ };
+
+ /* same thing as above */
+ tscadc {
+ compatible = "ti-tscadc-dt";
+
+ ti,hwmods = "adc_tsc";
+
+ tsc-wires = <4>;
+ tsc-x-plate-resistance = <200>;
+ tsc-steps = <6>;
+
+ adc-channels = <4>;
+ };
+
+ version@00A0 {
+ version = "00A0";
+
+ backlight {
+ compatible = "tps65217-backlight";
+ isel = <1>;
+ fdim = <200>;
+
+ tps = <&tps>; /* link to the tps */
+ brightness = <100>;
+ };
+
+ gpio-leds {
+ compatible = "gpio-leds";
+ pinctrl-names = "default";
+ pinctrl-0 = <&bone_lcd3_cape_led_00A0_pins>;
+
+ lcd3-led0 {
+ label = "lcd3:green:usr0";
+ gpios = <&gpio2 18 0>;
+ linux,default-trigger = "heartbeat";
+ default-state = "off";
+ };
+
+ lcd3-led1 {
+ label = "lcd3:green:usr1";
+ gpios = <&gpio2 19 0>;
+ linux,default-trigger = "cpu0";
+ default-state = "off";
+ };
+ };
+
+ gpio_keys {
+ compatible = "gpio-keys";
+ pinctrl-names = "default";
+ pinctrl-0 = <&bone_lcd3_cape_keys_00A0_pins>;
+
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ button@1 {
+ debounce_interval = <50>;
+ linux,code = <105>;
+ label = "left";
+ gpios = <&gpio2 16 0x0>;
+ gpio-key,wakeup;
+ autorepeat;
+ };
+ button@2 {
+ debounce_interval = <50>;
+ linux,code = <106>;
+ label = "right";
+ gpios = <&gpio2 17 0x0>;
+ gpio-key,wakeup;
+ autorepeat;
+ };
+ button@3 {
+ debounce_interval = <50>;
+ linux,code = <103>;
+ label = "up";
+ gpios = <&gpio4 19 0x0>;
+ gpio-key,wakeup;
+ autorepeat;
+ };
+ button@4 {
+ debounce_interval = <50>;
+ linux,code = <108>;
+ label = "down";
+ gpios = <&gpio2 28 0x0>;
+ gpio-key,wakeup;
+ autorepeat;
+ };
+ button@5 {
+ debounce_interval = <50>;
+ linux,code = <28>;
+ label = "enter";
+ gpios = <&gpio1 7 0x0>;
+ gpio-key,wakeup;
+ };
+ };
+ };
+};
+
+&bone_lcd7_cape {
+ board-name = "BeagleBone LCD7 CAPE";
+
+ /* hacky, since this is not a proper DT platform device */
+ /* but until we have DT bindings... */
+ lcd7 {
+ compatible = "da8xx-dt";
+ pinctrl-names = "default";
+ pinctrl-0 = <&bone_lcd3_cape_lcd_pins>;
+
+ ti,hwmods = "lcdc";
+
+ disp-pll = <60000000>;
+ panel-type = "TFC_S9700RTWV35TR_01B";
+ };
+
+ /* same thing as above */
+ tscadc {
+ compatible = "ti-tscadc-dt";
+
+ ti,hwmods = "adc_tsc";
+
+ tsc-wires = <4>;
+ tsc-x-plate-resistance = <200>;
+ tsc-steps = <6>;
+
+ adc-channels = <4>;
+ };
+
+ version@00A0 {
+ version = "00A0";
+
+ backlight {
+ compatible = "tps65217-backlight";
+ isel = <1>;
+ fdim = <200>;
+
+ tps = <&tps>; /* link to the tps */
+ brightness = <100>;
+ };
+
+ gpio-leds {
+ compatible = "gpio-leds";
+ pinctrl-names = "default";
+ pinctrl-0 = <&bone_lcd3_cape_led_00A0_pins>;
+
+ lcd3-led0 {
+ label = "lcd3:green:usr0";
+ gpios = <&gpio2 18 0>;
+ linux,default-trigger = "heartbeat";
+ default-state = "off";
+ };
+
+ lcd3-led1 {
+ label = "lcd3:green:usr1";
+ gpios = <&gpio2 19 0>;
+ linux,default-trigger = "cpu0";
+ default-state = "off";
+ };
+ };
+
+ gpio_keys {
+ compatible = "gpio-keys";
+ pinctrl-names = "default";
+ pinctrl-0 = <&bone_lcd3_cape_keys_00A0_pins>;
+
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ button@1 {
+ debounce_interval = <50>;
+ linux,code = <105>;
+ label = "left";
+ gpios = <&gpio2 16 0x0>;
+ gpio-key,wakeup;
+ autorepeat;
+ };
+ button@2 {
+ debounce_interval = <50>;
+ linux,code = <106>;
+ label = "right";
+ gpios = <&gpio2 17 0x0>;
+ gpio-key,wakeup;
+ autorepeat;
+ };
+ button@3 {
+ debounce_interval = <50>;
+ linux,code = <103>;
+ label = "up";
+ gpios = <&gpio4 19 0x0>;
+ gpio-key,wakeup;
+ autorepeat;
+ };
+ button@4 {
+ debounce_interval = <50>;
+ linux,code = <108>;
+ label = "down";
+ gpios = <&gpio2 28 0x0>;
+ gpio-key,wakeup;
+ autorepeat;
+ };
+ button@5 {
+ debounce_interval = <50>;
+ linux,code = <28>;
+ label = "enter";
+ gpios = <&gpio1 7 0x0>;
+ gpio-key,wakeup;
+ };
+ };
+ };
+};
+
+&bone_weather_cape {
+ board-name = "BeagleBone Weather CAPE";
+
+ i2c2-devices {
+ compatible = "i2c-dt";
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ parent = <&i2c2>;
+
+ /* Ambient light sensor */
+ tsl2550@39 {
+ compatible = "tsl,tsl2550";
+ reg = <0x39>;
+ };
+
+ /* Humidity Sensor */
+ sht21@40 {
+ compatible = "sensiron,sht21";
+ reg = <0x40>;
+ };
+
+ /* Barometric pressure sensor */
+ bmp085@77 {
+ compatible = "bosch,bmp085";
+ reg = <0x77>;
+ };
+ };
+
+ onewire@0 {
+ compatible = "w1-gpio";
+ pinctrl-names = "default";
+ pinctrl-0 = <&weather_cape_w1_pins>;
+ status = "okay";
+
+ gpios = <&gpio2 3 0>;
+ };
+
+};
+
+&bone_adafruit_cape {
+ board-name = "Adafruit 1.8 Cape";
+
+ backlight {
+ compatible = "pwm-backlight";
+ pinctrl-names = "default";
+ pinctrl-0 = <&pwm_bl_pins>;
+
+ pwms = <&ehrpwm1 1 500000 0>;
+ pwm-names = "st7735fb";
+ brightness-levels = <0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100>;
+ default-brightness-level = <50>; /* index to the array above */
+ };
+
+ spi1-devices {
+ compatible = "spi-dt";
+
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ parent = <&spi1>;
+
+ lcd@0 {
+ compatible = "adafruit,tft-lcd-1.8-red", "sitronix,st7735";
+ spi-max-frequency = <8000000>;
+ reg = <0>;
+ spi-cpol;
+ spi-cpha;
+ pinctrl-names = "default";
+ pinctrl-0 = <&lcd_pins>;
+ st7735-rst = <&gpio4 19 0>;
+ st7735-dc = <&gpio4 21 0>;
+ };
+
+ };
+};
--
1.7.12
Small summary of capebus.
Signed-off-by: Pantelis Antoniou <[email protected]>
---
Documentation/capebus/capebus-summary | 40 +++++++++++++++++++++++++++++++++++
1 file changed, 40 insertions(+)
create mode 100644 Documentation/capebus/capebus-summary
diff --git a/Documentation/capebus/capebus-summary b/Documentation/capebus/capebus-summary
new file mode 100644
index 0000000..742e33c
--- /dev/null
+++ b/Documentation/capebus/capebus-summary
@@ -0,0 +1,40 @@
+Overview of Linux kernel Capebus support
+========================================
+
+30-Oct-2012
+
+What is Capebus?
+----------------
+Capebus is an abstract concept. There's no such thing as a vanilla physical
+capebus, what is there is a concept and a method on how various capebus
+based implementations can be made.
+
+Capebus is created to address the problem of many SoCs that can provide a
+multitude of hardware interfaces but in order to keep costs down the main
+boards only support a limited number of them. The rest are typically brought
+out to pin connectors on to which other boards, named capes are connected and
+allow those peripherals to be used.
+
+These capes connect to the SoC interfaces but might also contain various other
+parts that may need some kind of driver to work.
+
+Since SoCs have limited pins and pin muxing options, not all capes can work
+together so some kind of resource tracking (at least for the pins in use) is
+required.
+
+Before capebus all of this took place in the board support file, and frankly
+for boards with too many capes it was becoming unmanageable.
+
+Capebus provides a virtual bus, which along with a board specific controller,
+cape drivers can be written using the standard Linux device model.
+
+What kind of systems/boards capebus supports?
+---------------------------------------------
+
+The core capebus infrastructure is not depended on any specific board.
+However capebus needs a board controller to provide services to the cape devices
+it controls. Services like addressing and resource reservation are provided
+by the board controller.
+
+Capebus at the moment only support TI's Beaglebone platform.
+
--
1.7.12
Describe capebus DT bindings in detail.
Signed-off-by: Pantelis Antoniou <[email protected]>
---
.../capebus/bone-capebus-slot-override.txt | 28 +++++++
.../devicetree/bindings/capebus/bone-capebus.txt | 50 +++++++++++
.../bindings/capebus/bone-geiger-cape.txt | 78 +++++++++++++++++
.../bindings/capebus/bone-generic-cape.txt | 97 ++++++++++++++++++++++
.../devicetree/bindings/capebus/da8xx-dt.txt | 31 +++++++
.../devicetree/bindings/capebus/i2c-dt.txt | 42 ++++++++++
.../devicetree/bindings/capebus/spi-dt.txt | 37 +++++++++
.../devicetree/bindings/capebus/ti-tscadc-dt.txt | 34 ++++++++
8 files changed, 397 insertions(+)
create mode 100644 Documentation/devicetree/bindings/capebus/bone-capebus-slot-override.txt
create mode 100644 Documentation/devicetree/bindings/capebus/bone-capebus.txt
create mode 100644 Documentation/devicetree/bindings/capebus/bone-geiger-cape.txt
create mode 100644 Documentation/devicetree/bindings/capebus/bone-generic-cape.txt
create mode 100644 Documentation/devicetree/bindings/capebus/da8xx-dt.txt
create mode 100644 Documentation/devicetree/bindings/capebus/i2c-dt.txt
create mode 100644 Documentation/devicetree/bindings/capebus/spi-dt.txt
create mode 100644 Documentation/devicetree/bindings/capebus/ti-tscadc-dt.txt
diff --git a/Documentation/devicetree/bindings/capebus/bone-capebus-slot-override.txt b/Documentation/devicetree/bindings/capebus/bone-capebus-slot-override.txt
new file mode 100644
index 0000000..733d977
--- /dev/null
+++ b/Documentation/devicetree/bindings/capebus/bone-capebus-slot-override.txt
@@ -0,0 +1,28 @@
+* Beagle bone capebus slot override bindings
+
+The beagle bone capebus node can have slot override nodes. These nodes describe
+ an override that will take place in the specified slot.
+ Many boards during the prototype phase don't have an EEPROM (or are even
+ wired in such a way that an eeprom cannot be added). In that case you can
+ specify an override that will make everything work as if a real EEPROM was
+ there.
+
+Required properties:
+- compatible: Override node must have the form "bone-capebus-slot-override"
+- slot: Identifies the slot# to override.
+- board-name: The cape's name as if provided by the board-name EEPROM field
+
+Optional properties:
+- version: The cape's version as if provided by the version EEPROM field.
+- manufacturer: The cape's manufacturer as if provided by the manufacturer
+ EEPROM field.
+
+Example:
+
+override@1 {
+ compatible = "bone-capebus-slot-override";
+ slot = <1>;
+ board-name = "Adafruit 1.8 Cape";
+ version = "00A0";
+ manufacturer = "Adafruit";
+};
diff --git a/Documentation/devicetree/bindings/capebus/bone-capebus.txt b/Documentation/devicetree/bindings/capebus/bone-capebus.txt
new file mode 100644
index 0000000..6d8a08d
--- /dev/null
+++ b/Documentation/devicetree/bindings/capebus/bone-capebus.txt
@@ -0,0 +1,50 @@
+* Beagle bone capebus bindings
+
+The beaglebone capebus implementation is using a single capebus
+node contained in the root node. A beaglebone cape is identified
+at by reading an EEPROM at on of 4 possible addresses on the I2C2 bus.
+
+Required properties:
+- compatible: Every beaglebone compatible capebus node shall have the
+ form "bone-capebus";
+- slots: An array of phandles pointing to the I2C node of an EEPROM that
+ contains the cape information.
+
+Optional properties:
+
+The child nodes of the capebus node can contain either cape nodes
+or override nodes. Those cape nodes are described in their respective
+binding files.
+
+- override nodes: Describe an override that will take place in the specified
+ slot. Many boards during the prototype phase don't have an EEPROM (or are even
+ wired in such a way that an eeprom cannot be added). In that case you can
+ specify an override that will make everything work as if a real EEPROM was
+ there.
+
+Override node required properties:
+- compatible: For override node must have the form "bone-capebus-slot-override"
+- slot: Identifies the slot# to override.
+- board-name: The cape's name as if provided by the board-name EEPROM field
+
+Override node optional properties:
+- version: The cape's version as if provided by the version EEPROM field.
+- manufacturer: The cape's manufacturer as if provided by the manufacturer
+ EEPROM field.
+
+Example:
+
+capebus: capebus@0 {
+ compatible = "bone-capebus";
+
+ slots = <&cape_eeprom_0 &cape_eeprom_1 &cape_eeprom_2 &cape_eeprom_3>;
+
+ [cape-nodes]
+ [override-nodes]
+
+ bone_adafruit_cape: cape@5 {
+ compatible = "bone-generic-cape";
+ // read the bone-generic-cape bindings for the rest
+ ...
+ };
+};
diff --git a/Documentation/devicetree/bindings/capebus/bone-geiger-cape.txt b/Documentation/devicetree/bindings/capebus/bone-geiger-cape.txt
new file mode 100644
index 0000000..a2ed81c
--- /dev/null
+++ b/Documentation/devicetree/bindings/capebus/bone-geiger-cape.txt
@@ -0,0 +1,78 @@
+* Beagle bone geiger cape
+
+ A geiger cape is your run of the mill particle detector measuring
+ ionizing radiation. It is also the first example of how a non-generic
+ cape can be supported by capebus.
+
+ The geiger cape can not be created via instantiation of a generic cape
+ since the component need to work together.
+
+Required properties:
+- compatible: Generic cape nodes must have the form "bone-geiger-cape"
+- board-name: The cape's name as if provided by the board-name EEPROM field
+- pwms: Pointer to the PWM we require
+- pwm-names: Name of the PWM.
+- pwm-frequency: Frequency of the PWM in Hz
+- pwm-duty-cycle: Duty cycle in percent
+- event-blink-delay: Blink delay of the event led
+- gpios: GPIO on which an event is detected
+- vsense-name: Name of the analog input for vsense
+- vsense-scale: Vsense scale to convert to mVolts
+- ti-tscadc-dt compatible node for the ADC configuration
+- gpio-leds compatible node for the LED configuration
+
+Optional properties:
+
+The geiger cape supports the standard pinctrl properties.
+
+Example:
+
+bone_geiger_cape: cape@1 {
+ compatible = "bone-geiger-cape";
+ board-name = "Geiger Cape";
+
+ /* note that these can't be versioned... */
+ pinctrl-names = "default";
+ pinctrl-0 = <&bone_geiger_cape_pins>;
+
+ pwms = <&ehrpwm1 0 500000 0>;
+ pwm-names = "bone-geiger-cape";
+
+ pwm-frequency = <20000>; /* 20KHz */
+ pwm-duty-cycle = <60>; /* 60% */
+
+ event-blink-delay = <30>; /* 30ms */
+
+ gpios = <&gpio4 17 0>; /* pulse */
+
+ vsense-name = "AIN5"; /* analog vsense */
+ vsense-scale = <37325>; /* scaling */
+
+ tscadc {
+ compatible = "ti-tscadc-dt";
+
+ ti,hwmods = "adc_tsc";
+
+ adc-channels = <8>;
+ };
+
+ gpio-leds {
+ compatible = "gpio-leds";
+ pinctrl-names = "default";
+ pinctrl-0 = <&bone_geiger_cape_led_pins>;
+
+ geiger-led0 {
+ label = "geiger:green:usr0";
+ gpios = <&gpio3 23 0>;
+ linux,default-trigger = "geiger-run";
+ default-state = "off";
+ };
+
+ geiger-led1 {
+ label = "geiger:red:usr1";
+ gpios = <&gpio3 25 0>;
+ linux,default-trigger = "geiger-event";
+ default-state = "off";
+ };
+ };
+};
diff --git a/Documentation/devicetree/bindings/capebus/bone-generic-cape.txt b/Documentation/devicetree/bindings/capebus/bone-generic-cape.txt
new file mode 100644
index 0000000..71ad2f4
--- /dev/null
+++ b/Documentation/devicetree/bindings/capebus/bone-generic-cape.txt
@@ -0,0 +1,97 @@
+* Beagle bone generic cape
+
+ A generic cape is one form of cape that doesn't require any per-cape
+ driver for it to function. It is enough for the cape's node to be
+ instantiated in the right manner and the functions that the cape provides
+ will be available to Linux. A large number of capes fall in this category
+ like LCD/DVI/VGA capes, audio capes, capes with a number of gpio buttons,
+ capes with standard I2C/SPI/W1 parts etc.
+ All that's required is to describe the parts of the cape in the DT and
+ how they utilize the SoC peripherals and it will work, without having
+ to write any other driver, or modify the board support package.
+
+ As a sidenote, a large part of the complexity of capes have to do
+ with version management. A cape as it goes through various steps of evolution
+ changes. These changes are codified by the version field on the EEPROM.
+ It is important that version management can be kept as simple as possible.
+
+ The generic concept works by instantiating the child nodes that are
+ contained in the cape node. By default nodes that are present in the
+ root of the node are activated for any version. Version nodes can
+ contain per-version changes. In the example the da8xx-dt node differs
+ in the way it's instantiated, and this is expressed by the version nodes.
+
+Required properties:
+- compatible: Generic cape nodes must have the form "bone-generic-cape"
+- board-name: The cape's name as if provided by the board-name EEPROM field
+
+Optional properties:
+- version: Only valid in a version node, and it contains the list of compatible
+ versions this node contains.
+
+The standard supported nodes of the generic cape are:
+
+gpio-leds, tps65217-backlight, gpio-keys, w1-gpio, pwm-backlight
+
+The following nodes are supported via a capebus specific bridge devices, and
+their bindings described in their respective files:
+
+ti-tscadc-dt, da8xx-dt, i2c-dt, spi-dt
+
+Example:
+
+bone_dvi_cape: cape@0 {
+
+ compatible = "bone-generic-cape";
+ board-name = "BeagleBone DVI-D CAPE";
+
+ /* hacky, since this is not a proper DT platform device */
+ /* but until we have DT bindings... */
+ version@00A0 {
+ version = "00A0";
+ dvi {
+ compatible = "da8xx-dt";
+ pinctrl-names = "default";
+ pinctrl-0 = <&bone_dvi_cape_dvi_00A0_pins>;
+ ti,hwmods = "lcdc";
+
+ disp-pll = <560000000>;
+ panel-type = "1024x768@60";
+ powerdn-gpio = <&gpio2 7 0>;
+ };
+ };
+
+ version@00A1 {
+ version = "00A1", "01";
+ dvi {
+ compatible = "da8xx-dt";
+ pinctrl-names = "default";
+ pinctrl-0 = <&bone_dvi_cape_dvi_00A1_pins>;
+ ti,hwmods = "lcdc";
+
+ disp-pll = <560000000>;
+ panel-type = "1024x768@60";
+ powerdn-gpio = <&gpio2 31 0>;
+ };
+ };
+
+ gpio-leds {
+ compatible = "gpio-leds";
+ pinctrl-names = "default";
+ pinctrl-0 = <&bone_dvi_cape_led_pins>;
+
+ dvi-led0 {
+ label = "dvi:green:usr0";
+ gpios = <&gpio2 18 0>;
+ linux,default-trigger = "heartbeat";
+ default-state = "off";
+ };
+
+ dvi-led1 {
+ label = "dvi:green:usr1";
+ gpios = <&gpio2 19 0>;
+ linux,default-trigger = "mmc0";
+ default-state = "off";
+ };
+ };
+};
diff --git a/Documentation/devicetree/bindings/capebus/da8xx-dt.txt b/Documentation/devicetree/bindings/capebus/da8xx-dt.txt
new file mode 100644
index 0000000..0b84063
--- /dev/null
+++ b/Documentation/devicetree/bindings/capebus/da8xx-dt.txt
@@ -0,0 +1,31 @@
+* DA8XX DT bindins
+
+ This is an adapter device for use in device tree cases, since the
+ drivers DT bindings are not yet ready. It is bound to get away once
+ they are in place. Note that there is no capebus prefix, since there's
+ not really any dependence on capebus; the drivers can be moved out of
+ capebus if need be.
+
+Required properties:
+- compatible: Must have the form "da8xx-dt"
+- ti,hwmods: Must have the form "lcdc" (until hwmod DT is complete)
+- disp-pll: PLL value
+- panel-type: Name of the panel type connected
+
+Optional properties:
+powerdn-gpio: GPIO controlling power
+
+Note that the pinctrl bindings are supported.
+
+Example:
+
+dvi {
+ compatible = "da8xx-dt";
+ pinctrl-names = "default";
+ pinctrl-0 = <&bone_dvi_cape_dvi_00A0_pins>;
+ ti,hwmods = "lcdc";
+
+ disp-pll = <560000000>;
+ panel-type = "1024x768@60";
+ powerdn-gpio = <&gpio2 7 0>;
+};
diff --git a/Documentation/devicetree/bindings/capebus/i2c-dt.txt b/Documentation/devicetree/bindings/capebus/i2c-dt.txt
new file mode 100644
index 0000000..79a042a
--- /dev/null
+++ b/Documentation/devicetree/bindings/capebus/i2c-dt.txt
@@ -0,0 +1,42 @@
+* I2C DT bindings
+
+ This is an adapter device for use in device tree cases.
+ What we want to do is to add a number of I2C devices connected
+ to a specified i2c adapter node, and instantiate them on
+ successful cape match. Not only that, normally I2C adapters
+ are disabled in the DT tree, so in that case we enable them
+ first and then go about our business.
+
+Required properties:
+- compatible: Must have the form "i2c-dt"
+- parent: The phandle of the i2c adapter node
+- #address-cells: The same as the parent i2c adapter node
+- #size-cells: The same as the parent i2c adapter node
+
+Example:
+
+i2c2-devices {
+ compatible = "i2c-dt";
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ parent = <&i2c2>;
+
+ /* Ambient light sensor */
+ tsl2550@39 {
+ compatible = "tsl,tsl2550";
+ reg = <0x39>;
+ };
+
+ /* Humidity Sensor */
+ sht21@40 {
+ compatible = "sensiron,sht21";
+ reg = <0x40>;
+ };
+
+ /* Barometric pressure sensor */
+ bmp085@77 {
+ compatible = "bosch,bmp085";
+ reg = <0x77>;
+ };
+};
diff --git a/Documentation/devicetree/bindings/capebus/spi-dt.txt b/Documentation/devicetree/bindings/capebus/spi-dt.txt
new file mode 100644
index 0000000..fd70fc4
--- /dev/null
+++ b/Documentation/devicetree/bindings/capebus/spi-dt.txt
@@ -0,0 +1,37 @@
+* SPI DT bindings
+
+ This is an adapter device for use in device tree cases.
+ What we want to do is to add a number of SPI devices connected
+ to a specified spi node, and instantiate them on
+ successful cape match. Not only that, normally SPI nodes
+ are disabled in the DT tree, so in that case we enable them
+ first and then go about our business.
+
+Required properties:
+- compatible: Must have the form "spi-dt"
+- parent: The phandle of the spi node
+- #address-cells: The same as the parent spi node
+- #size-cells: The same as the parent spi node
+
+Example:
+
+spi1-devices {
+ compatible = "spi-dt";
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ parent = <&spi1>;
+
+ lcd@0 {
+ compatible = "adafruit,tft-lcd-1.8-red", "sitronix,st7735";
+ spi-max-frequency = <8000000>;
+ reg = <0>;
+ spi-cpol;
+ spi-cpha;
+ pinctrl-names = "default";
+ pinctrl-0 = <&lcd_pins>;
+ st7735-rst = <&gpio4 19 0>;
+ st7735-dc = <&gpio4 21 0>;
+ };
+
+};
diff --git a/Documentation/devicetree/bindings/capebus/ti-tscadc-dt.txt b/Documentation/devicetree/bindings/capebus/ti-tscadc-dt.txt
new file mode 100644
index 0000000..44ad4f2
--- /dev/null
+++ b/Documentation/devicetree/bindings/capebus/ti-tscadc-dt.txt
@@ -0,0 +1,34 @@
+* TI's TSCADC Device Tree adapter
+
+ This is an adapter device for use in device tree cases, since the
+ drivers DT bindings are not yet ready. It is bound to get away once
+ they are in place. Note that there is no capebus prefix, since there's
+ not really any dependence on capebus; the drivers can be moved out of
+ capebus if need be.
+
+Required properties:
+- compatible: Must have the form "ti-tscadc-dt"
+- ti,hwmods: Must have the form "adc_tsc" (until hwmod DT is complete)
+
+Optional properties:
+- adc-channels: Number of ADC channels the driver should provide.
+- tsc-wires: Number of touchscreen wires
+- tsc-x-plate-resistance: Touchscreen X plate resistance value
+- tsc-steps: Touchscreen steps
+
+Note if either adc-channels or tsc-wires are missing their respective
+values are set to 0.
+
+Example:
+
+tscadc {
+ compatible = "ti-tscadc-dt";
+
+ ti,hwmods = "adc_tsc";
+
+ tsc-wires = <4>;
+ tsc-x-plate-resistance = <200>;
+ tsc-steps = <6>;
+
+ adc-channels = <4>;
+};
--
1.7.12
Support beaglebone's geiger cape.
The geiger cape allows you to measure the amount of
ionising radiation in your area, and as an example
of how to create a complex non-generic cape driver.
Signed-off-by: Pantelis Antoniou <[email protected]>
---
drivers/capebus/capes/Kconfig | 7 +
drivers/capebus/capes/Makefile | 1 +
drivers/capebus/capes/bone-geiger-cape.c | 506 +++++++++++++++++++++++++++++++
3 files changed, 514 insertions(+)
create mode 100644 drivers/capebus/capes/bone-geiger-cape.c
diff --git a/drivers/capebus/capes/Kconfig b/drivers/capebus/capes/Kconfig
index bfe54a6..0418bef 100644
--- a/drivers/capebus/capes/Kconfig
+++ b/drivers/capebus/capes/Kconfig
@@ -4,3 +4,10 @@ config CAPEBUS_BONE_GENERIC
default n
help
"Select this to enable a generic cape driver; LCD/DVI capes etc"
+
+config CAPEBUS_BONE_GEIGER
+ tristate "Beaglebone Geiger cape driver"
+ depends on CAPEBUS_BONE_CONTROLLER
+ default n
+ help
+ "Select this to enable a driver for the geiger cape"
diff --git a/drivers/capebus/capes/Makefile b/drivers/capebus/capes/Makefile
index 83da381..d6f94ce 100644
--- a/drivers/capebus/capes/Makefile
+++ b/drivers/capebus/capes/Makefile
@@ -1 +1,2 @@
obj-$(CONFIG_CAPEBUS_BONE_GENERIC) += bone-generic-cape.o
+obj-$(CONFIG_CAPEBUS_BONE_GEIGER) += bone-geiger-cape.o
diff --git a/drivers/capebus/capes/bone-geiger-cape.c b/drivers/capebus/capes/bone-geiger-cape.c
new file mode 100644
index 0000000..880eaae
--- /dev/null
+++ b/drivers/capebus/capes/bone-geiger-cape.c
@@ -0,0 +1,506 @@
+/*
+ * Driver for beaglebone Geiger cape
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/string.h>
+#include <linux/timer.h>
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/bitops.h>
+#include <linux/err.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/of_gpio.h>
+#include <linux/pinctrl/pinctrl.h>
+#include <linux/pinctrl/pinmux.h>
+#include <linux/pinctrl/consumer.h>
+#include <linux/atomic.h>
+#include <linux/clk.h>
+#include <linux/interrupt.h>
+#include <asm/barrier.h>
+#include <plat/clock.h>
+#include <plat/omap_device.h>
+#include <linux/clkdev.h>
+#include <linux/pwm.h>
+#include <linux/math64.h>
+#include <linux/atomic.h>
+#include <linux/leds.h>
+#include <linux/input/ti_am335x_tsc.h>
+#include <linux/platform_data/ti_am335x_adc.h>
+#include <linux/mfd/ti_am335x_tscadc.h>
+#include <plat/omap_device.h>
+#include <linux/iio/iio.h>
+#include <linux/iio/machine.h>
+#include <linux/iio/consumer.h>
+
+#include <linux/capebus/capebus-bone.h>
+
+/* fwd decl. */
+extern struct cape_driver bonegeiger_driver;
+
+struct bone_geiger_info {
+ struct cape_dev *dev;
+ struct bone_capebus_generic_info *geninfo;
+ struct pwm_device *pwm_dev;
+ int pwm_frequency;
+ int pwm_duty_cycle;
+ int run;
+ atomic64_t counter;
+ int event_gpio;
+ int event_irq;
+ struct led_trigger *event_led; /* event detect */
+ struct led_trigger *run_led; /* running */
+ unsigned long event_blink_delay;
+ struct sysfs_dirent *counter_sd; /* notifier */
+ const char *vsense_name;
+ unsigned int vsense_scale;
+ struct iio_channel *vsense_channel;
+};
+
+static const struct of_device_id bonegeiger_of_match[] = {
+ {
+ .compatible = "bone-geiger-cape",
+ },
+ { },
+};
+MODULE_DEVICE_TABLE(of, bonegeiger_of_match);
+
+static int bonegeiger_start(struct cape_dev *dev)
+{
+ struct bone_geiger_info *info = dev->drv_priv;
+ int duty, period;
+
+ if (info->run != 0)
+ return 0;
+
+ /* checks */
+ if (info->pwm_frequency < 1000 || info->pwm_frequency > 50000) {
+ dev_err(&dev->dev, "Cowardly refusing to use a "
+ "frequency of %d\n",
+ info->pwm_frequency);
+ return -EINVAL;
+ }
+ if (info->pwm_duty_cycle > 80) {
+ dev_err(&dev->dev, "Cowardly refusing to use a "
+ "duty cycle of %d\n",
+ info->pwm_duty_cycle);
+ return -EINVAL;
+ }
+
+ period = div_u64(1000000000LLU, info->pwm_frequency);
+ duty = (period * info->pwm_duty_cycle) / 100;
+
+ dev_info(&dev->dev, "starting geiger tube with "
+ "duty=%duns period=%dus\n",
+ duty, period);
+
+ pwm_config(info->pwm_dev, duty, period);
+ pwm_enable(info->pwm_dev);
+
+ info->run = 1;
+ led_trigger_event(info->run_led, LED_FULL);
+
+ return 0;
+}
+
+static int bonegeiger_stop(struct cape_dev *dev)
+{
+ struct bone_geiger_info *info = dev->drv_priv;
+
+ if (info->run == 0)
+ return 0;
+
+ dev_info(&dev->dev, "disabling geiger tube\n");
+ pwm_config(info->pwm_dev, 0, 50000); /* 0% duty cycle, 20KHz */
+ pwm_disable(info->pwm_dev);
+
+ info->run = 0;
+ led_trigger_event(info->run_led, LED_OFF);
+
+ return 0;
+}
+
+static ssize_t bonegeiger_show_run(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct cape_dev *cdev = to_cape_dev(dev);
+ struct bone_geiger_info *info = cdev->drv_priv;
+
+ return sprintf(buf, "%d\n", info->run);
+}
+
+static ssize_t bonegeiger_store_run(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct cape_dev *cdev = to_cape_dev(dev);
+ int run, err;
+
+ if (sscanf(buf, "%i", &run) != 1)
+ return -EINVAL;
+
+ if (run)
+ err = bonegeiger_start(cdev);
+ else
+ err = bonegeiger_stop(cdev);
+
+ return err ? err : count;
+}
+
+static ssize_t bonegeiger_show_counter(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct cape_dev *cdev = to_cape_dev(dev);
+ struct bone_geiger_info *info = cdev->drv_priv;
+
+ return sprintf(buf, "%llu\n", atomic64_read(&info->counter));
+}
+
+static ssize_t bonegeiger_store_counter(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct cape_dev *cdev = to_cape_dev(dev);
+ struct bone_geiger_info *info = cdev->drv_priv;
+
+ atomic64_set(&info->counter, 0); /* just reset */
+ return count;
+}
+
+static ssize_t bonegeiger_show_vsense(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct cape_dev *cdev = to_cape_dev(dev);
+ struct bone_geiger_info *info = cdev->drv_priv;
+ int ret, val;
+ u32 mvolts;
+
+ ret = iio_read_channel_raw(info->vsense_channel, &val);
+ if (ret < 0)
+ return ret;
+
+ /* V = (1800 / 4096) * val * scale) = (1.8 * val * scale / 4096) */
+ mvolts = div_u64(1800 * info->vsense_scale * (u64)val, 4096 * 100);
+
+ return sprintf(buf, "%d\n", mvolts);
+}
+
+static DEVICE_ATTR(run, S_IRUGO | S_IWUSR,
+ bonegeiger_show_run, bonegeiger_store_run);
+static DEVICE_ATTR(counter, S_IRUGO | S_IWUSR,
+ bonegeiger_show_counter, bonegeiger_store_counter);
+static DEVICE_ATTR(vsense, S_IRUGO,
+ bonegeiger_show_vsense, NULL);
+
+static int bonegeiger_sysfs_register(struct cape_dev *cdev)
+{
+ int err;
+
+ err = device_create_file(&cdev->dev, &dev_attr_run);
+ if (err != 0)
+ goto err_no_run;
+
+ err = device_create_file(&cdev->dev, &dev_attr_counter);
+ if (err != 0)
+ goto err_no_counter;
+
+ err = device_create_file(&cdev->dev, &dev_attr_vsense);
+ if (err != 0)
+ goto err_no_vsense;
+
+ return 0;
+
+err_no_vsense:
+ device_remove_file(&cdev->dev, &dev_attr_counter);
+err_no_counter:
+ device_remove_file(&cdev->dev, &dev_attr_run);
+err_no_run:
+ return err;
+}
+
+static void bonegeiger_sysfs_unregister(struct cape_dev *cdev)
+{
+ device_remove_file(&cdev->dev, &dev_attr_vsense);
+ device_remove_file(&cdev->dev, &dev_attr_counter);
+ device_remove_file(&cdev->dev, &dev_attr_run);
+}
+
+static irqreturn_t bonegeiger_irq_handler(int irq, void *dev_id)
+{
+ struct cape_dev *dev = dev_id;
+ struct bone_geiger_info *info = dev->drv_priv;
+
+ atomic64_inc(&info->counter);
+
+ led_trigger_blink_oneshot(info->event_led,
+ &info->event_blink_delay, &info->event_blink_delay, 0);
+
+ sysfs_notify_dirent(info->counter_sd);
+
+ return IRQ_HANDLED;
+}
+
+static int bonegeiger_probe(struct cape_dev *dev, const struct cape_device_id *id)
+{
+ char boardbuf[33];
+ char versionbuf[5];
+ const char *board_name;
+ const char *version;
+ struct bone_geiger_info *info;
+ struct pinctrl *pinctrl;
+ struct device_node *node, *pwm_node;
+ phandle phandle;
+ u32 val;
+ int err;
+
+ /* boiler plate probing */
+ err = bone_capebus_probe_prolog(dev, id);
+ if (err != 0)
+ return err;
+
+ /* get the board name (after check of cntrlboard match) */
+ board_name = bone_capebus_id_get_field(id, BONE_CAPEBUS_BOARD_NAME,
+ boardbuf, sizeof(boardbuf));
+ /* get the board version */
+ version = bone_capebus_id_get_field(id, BONE_CAPEBUS_VERSION,
+ versionbuf, sizeof(versionbuf));
+ /* should never happen; but check anyway */
+ if (board_name == NULL || version == NULL)
+ return -ENODEV;
+
+ dev->drv_priv = devm_kzalloc(&dev->dev, sizeof(*info), GFP_KERNEL);
+ if (dev->drv_priv == NULL) {
+ dev_err(&dev->dev, "Failed to allocate info\n");
+ err = -ENOMEM;
+ goto err_no_mem;
+ }
+ info = dev->drv_priv;
+
+ pinctrl = devm_pinctrl_get_select_default(&dev->dev);
+ if (IS_ERR(pinctrl))
+ dev_warn(&dev->dev,
+ "pins are not configured from the driver\n");
+
+ node = capebus_of_find_property_node(dev, "version", version, "pwms");
+ if (node == NULL) {
+ dev_err(&dev->dev, "unable to find pwms property\n");
+ err = -ENODEV;
+ goto err_no_pwm;
+ }
+
+ err = of_property_read_u32(node, "pwms", &val);
+ if (err != 0) {
+ dev_err(&dev->dev, "unable to read pwm handle\n");
+ goto err_no_pwm;
+ }
+ phandle = val;
+
+ pwm_node = of_find_node_by_phandle(phandle);
+ if (pwm_node == NULL) {
+ dev_err(&dev->dev, "Failed to pwm node\n");
+ err = -EINVAL;
+ goto err_no_pwm;
+ }
+
+ err = capebus_of_platform_device_enable(pwm_node);
+ of_node_put(pwm_node);
+ if (err != 0) {
+ dev_err(&dev->dev, "Failed to pwm node\n");
+ goto err_no_pwm;
+ }
+
+ info->pwm_dev = of_pwm_request(node, NULL);
+ of_node_put(node);
+ if (IS_ERR(info->pwm_dev)) {
+ dev_err(&dev->dev, "unable to request PWM\n");
+ err = PTR_ERR(info->pwm_dev);
+ goto err_no_pwm;
+ }
+
+ if (capebus_of_property_read_u32(dev,
+ "version", version,
+ "pwm-frequency", &val) != 0) {
+ val = 20000;
+ dev_warn(&dev->dev, "Could not read pwm-frequency property; "
+ "using default %u\n",
+ val);
+ }
+ info->pwm_frequency = val;
+
+ if (capebus_of_property_read_u32(dev,
+ "version", version,
+ "pwm-duty-cycle", &val) != 0) {
+ val = 60;
+ dev_warn(&dev->dev, "Could not read pwm-duty-cycle property; "
+ "using default %u\n",
+ val);
+ }
+ info->pwm_duty_cycle = val;
+
+ node = capebus_of_find_property_node(dev, "gpios", version, "pwms");
+ info->event_gpio = of_get_gpio_flags(node, 0, NULL);
+ of_node_put(node);
+ if (IS_ERR_VALUE(info->event_gpio)) {
+ dev_err(&dev->dev, "unable to get event GPIO\n");
+ err = info->event_gpio;
+ goto err_no_gpio;
+ }
+
+ err = gpio_request_one(info->event_gpio,
+ GPIOF_DIR_IN | GPIOF_EXPORT,
+ "bone-geiger-cape-event");
+ if (err != 0) {
+ dev_err(&dev->dev, "failed to request event GPIO\n");
+ goto err_no_gpio;
+ }
+
+ atomic64_set(&info->counter, 0);
+
+ info->event_irq = gpio_to_irq(info->event_gpio);
+ if (IS_ERR_VALUE(info->event_irq)) {
+ dev_err(&dev->dev, "unable to get event GPIO IRQ\n");
+ err = info->event_irq;
+ goto err_no_irq;
+ }
+
+ err = request_irq(info->event_irq, bonegeiger_irq_handler,
+ IRQF_TRIGGER_RISING | IRQF_SHARED,
+ "bone-geiger-irq", dev);
+ if (err != 0) {
+ dev_err(&dev->dev, "unable to request irq\n");
+ goto err_no_irq;
+ }
+
+ err = bonegeiger_sysfs_register(dev);
+ if (err != 0) {
+ dev_err(&dev->dev, "unable to register sysfs\n");
+ goto err_no_sysfs;
+ }
+
+ info->counter_sd = sysfs_get_dirent(dev->dev.kobj.sd, NULL, "counter");
+ if (info->counter_sd == NULL) {
+ dev_err(&dev->dev, "unable to get dirent of counter\n");
+ err = -ENODEV;
+ goto err_no_counter_dirent;
+ }
+
+ led_trigger_register_simple("geiger-event", &info->event_led);
+ led_trigger_register_simple("geiger-run", &info->run_led);
+
+ /* pick up the generics; tsc & leds */
+ info->geninfo = bone_capebus_probe_generic(dev, id);
+ if (info->geninfo == NULL) {
+ dev_err(&dev->dev, "Could not probe generic\n");
+ goto err_no_generic;
+ }
+
+ led_trigger_event(info->run_led, LED_OFF);
+
+ /* default */
+ if (capebus_of_property_read_u32(dev,
+ "version", version,
+ "event-blink-delay", &val) != 0) {
+ val = 30;
+ dev_warn(&dev->dev, "Could not read event-blink-delay "
+ "property; using default %u\n",
+ val);
+ }
+ info->event_blink_delay = val;
+
+ /* default */
+ if (capebus_of_property_read_string(dev,
+ "version", version,
+ "vsense-name", &info->vsense_name) != 0) {
+ info->vsense_name = "AIN5";
+ dev_warn(&dev->dev, "Could not read vsense-name property; "
+ "using default %u\n",
+ val);
+ }
+
+ if (capebus_of_property_read_u32(dev,
+ "version", version,
+ "vsense-scale", &info->vsense_scale) != 0) {
+ info->vsense_scale = 37325; /* 373.25 */
+ dev_warn(&dev->dev, "Could not read vsense-scale property; "
+ "using default %u\n",
+ info->vsense_scale);
+ }
+
+ info->vsense_channel = iio_channel_get(NULL, info->vsense_name);
+ if (IS_ERR(info->vsense_channel)) {
+ dev_err(&dev->dev, "Could not get AIN5 analog input\n");
+ err = PTR_ERR(info->vsense_channel);
+ goto err_no_vsense;
+ }
+
+ dev_info(&dev->dev, "ready\n");
+
+ err = bonegeiger_start(dev);
+ if (err != 0) {
+ dev_err(&dev->dev, "Could not start geiger device\n");
+ goto err_no_start;
+ }
+
+ return 0;
+
+err_no_start:
+ iio_channel_release(info->vsense_channel);
+err_no_vsense:
+ bone_capebus_remove_generic(info->geninfo);
+err_no_generic:
+ led_trigger_unregister_simple(info->run_led);
+ led_trigger_unregister_simple(info->event_led);
+ sysfs_put(info->counter_sd);
+err_no_counter_dirent:
+ bonegeiger_sysfs_unregister(dev);
+err_no_sysfs:
+ free_irq(info->event_irq, dev);
+err_no_irq:
+ gpio_free(info->event_gpio);
+err_no_gpio:
+ pwm_put(info->pwm_dev);
+err_no_pwm:
+ devm_kfree(&dev->dev, info);
+err_no_mem:
+ return err;
+}
+
+static void bonegeiger_remove(struct cape_dev *dev)
+{
+ struct bone_geiger_info *info = dev->drv_priv;
+
+ dev_info(&dev->dev, "Removing geiger cape driver...\n");
+
+ bonegeiger_stop(dev);
+
+ iio_channel_release(info->vsense_channel);
+ bone_capebus_remove_generic(info->geninfo);
+ led_trigger_unregister_simple(info->run_led);
+ led_trigger_unregister_simple(info->event_led);
+ sysfs_put(info->counter_sd);
+ bonegeiger_sysfs_unregister(dev);
+ free_irq(info->event_irq, dev);
+ gpio_free(info->event_gpio);
+ pwm_put(info->pwm_dev);
+}
+
+struct cape_driver bonegeiger_driver = {
+ .driver = {
+ .name = "bonegeiger",
+ .owner = THIS_MODULE,
+ .of_match_table = bonegeiger_of_match,
+ },
+ .probe = bonegeiger_probe,
+ .remove = bonegeiger_remove,
+};
+
+module_capebus_driver(bonegeiger_driver);
+
+MODULE_AUTHOR("Pantelis Antoniou");
+MODULE_DESCRIPTION("Beaglebone geiger cape");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:bone-geiger-cape");
--
1.7.12
Introduce beaglebone capebus board support.
This patch creates the beaglebone's board cape bus controller.
The board controller is responsible for the probing of capes
at the well defined I2C address for capes, parsing the EEPROM
info and matching them to specific cape drivers.
On top of that, adapter DT enabled devices are created for
am33xx devices that have no DT bindings yet, as well as generic
devices that can be used as building blocks for the cape drivers.
Signed-off-by: Pantelis Antoniou <[email protected]>
---
drivers/capebus/boards/Kconfig | 6 +
drivers/capebus/boards/Makefile | 3 +
drivers/capebus/boards/capebus-bone-generic.c | 237 +++++++
drivers/capebus/boards/capebus-bone-pdevs.c | 602 +++++++++++++++++
drivers/capebus/boards/capebus-bone.c | 931 ++++++++++++++++++++++++++
include/linux/capebus/capebus-bone.h | 120 ++++
6 files changed, 1899 insertions(+)
create mode 100644 drivers/capebus/boards/Kconfig
create mode 100644 drivers/capebus/boards/Makefile
create mode 100644 drivers/capebus/boards/capebus-bone-generic.c
create mode 100644 drivers/capebus/boards/capebus-bone-pdevs.c
create mode 100644 drivers/capebus/boards/capebus-bone.c
create mode 100644 include/linux/capebus/capebus-bone.h
diff --git a/drivers/capebus/boards/Kconfig b/drivers/capebus/boards/Kconfig
new file mode 100644
index 0000000..76b0f94
--- /dev/null
+++ b/drivers/capebus/boards/Kconfig
@@ -0,0 +1,6 @@
+config CAPEBUS_BONE_CONTROLLER
+ bool "Beaglebone capebus board controller"
+ depends on CAPEBUS && ARCH_OMAP2PLUS && OF && I2C
+ default n
+ help
+ "Select this to enable the beaglebone capebus board controller"
diff --git a/drivers/capebus/boards/Makefile b/drivers/capebus/boards/Makefile
new file mode 100644
index 0000000..9048231
--- /dev/null
+++ b/drivers/capebus/boards/Makefile
@@ -0,0 +1,3 @@
+obj-$(CONFIG_CAPEBUS_BONE_CONTROLLER) += capebus-bone.o \
+ capebus-bone-pdevs.o \
+ capebus-bone-generic.o \
diff --git a/drivers/capebus/boards/capebus-bone-generic.c b/drivers/capebus/boards/capebus-bone-generic.c
new file mode 100644
index 0000000..b1b79eb
--- /dev/null
+++ b/drivers/capebus/boards/capebus-bone-generic.c
@@ -0,0 +1,237 @@
+/*
+ * TI Beaglebone capebus controller - Generic devices
+ *
+ * Copyright (C) 2012 Pantelis Antoniou <[email protected]>
+ * 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/kernel.h>
+#include <linux/string.h>
+#include <linux/timer.h>
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/bitops.h>
+#include <linux/err.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/of_gpio.h>
+#include <video/da8xx-fb.h>
+#include <linux/pinctrl/pinctrl.h>
+#include <linux/pinctrl/pinmux.h>
+#include <linux/pinctrl/consumer.h>
+#include <linux/atomic.h>
+#include <linux/clk.h>
+#include <asm/barrier.h>
+#include <plat/clock.h>
+#include <plat/omap_device.h>
+#include <linux/clkdev.h>
+#include <linux/input/ti_am335x_tsc.h>
+#include <linux/platform_data/ti_am335x_adc.h>
+#include <linux/mfd/ti_am335x_tscadc.h>
+
+#include <linux/capebus/capebus-bone.h>
+
+int bone_capebus_probe_prolog(struct cape_dev *dev,
+ const struct cape_device_id *id)
+{
+ char boardbuf[33];
+ char versionbuf[5];
+ const char *board_name;
+ const char *version;
+ const struct of_device_id *match;
+ struct pinctrl *pinctrl;
+
+ /* get the board name (also matches the cntrlboard before checking) */
+ board_name = bone_capebus_id_get_field(id, BONE_CAPEBUS_BOARD_NAME,
+ boardbuf, sizeof(boardbuf));
+ if (board_name == NULL)
+ return -ENODEV;
+
+ /* match compatible? */
+ match = capebus_of_match_device(dev, "board-name", board_name);
+ if (match == NULL)
+ return -ENODEV;
+
+ /* get the board version */
+ version = bone_capebus_id_get_field(id, BONE_CAPEBUS_VERSION,
+ versionbuf, sizeof(versionbuf));
+ if (version == NULL)
+ return -ENODEV;
+
+ pinctrl = devm_pinctrl_get_select_default(&dev->dev);
+ if (IS_ERR(pinctrl))
+ dev_warn(&dev->dev,
+ "pins are not configured from the driver\n");
+
+ dev_info(&dev->dev, "%s: V=%s '%s'\n", board_name,
+ version, match->compatible);
+
+ return 0;
+}
+EXPORT_SYMBOL(bone_capebus_probe_prolog);
+
+static const struct bone_capebus_generic_device_data gendevs[] = {
+ {
+ .name = "leds",
+ .of_match = (const struct of_device_id []) {
+ { .compatible = "gpio-leds", }, { },
+ },
+ .units = 0, /* no limit */
+ }, {
+ .name = "tps-bl",
+ .of_match = (const struct of_device_id []) {
+ { .compatible = "tps65217-backlight", }, { },
+ },
+ .units = 0, /* no limit */
+ }, {
+ .name = "keys",
+ .of_match = (const struct of_device_id []) {
+ { .compatible = "gpio-keys", }, { },
+ },
+ .units = 0, /* no limit */
+ }, {
+ .name = "tscadc",
+ .of_match = (const struct of_device_id []) {
+ { .compatible = "ti-tscadc-dt", }, { },
+ },
+ .units = 1,
+ }, {
+ .name = "lcdc",
+ .of_match = (const struct of_device_id []) {
+ { .compatible = "da8xx-dt", }, { },
+ },
+ .units = 1,
+ },{
+ .name = "i2c-dt",
+ .of_match = (const struct of_device_id []) {
+ { .compatible = "i2c-dt", }, { },
+ },
+ .units = 0,
+ }, {
+ .name = "w1-gpio",
+ .of_match = (const struct of_device_id []) {
+ { .compatible = "w1-gpio", }, { },
+ },
+ .units = 0,
+ }, {
+ .name = "pwm-backlight",
+ .of_match = (const struct of_device_id []) {
+ { .compatible = "pwm-backlight", }, { },
+ },
+ .units = 0, /* no limit */
+ }, {
+ .name = "spi-dt",
+ .of_match = (const struct of_device_id []) {
+ { .compatible = "spi-dt", }, { },
+ },
+ .units = 0, /* no limit */
+ }
+};
+
+struct bone_capebus_generic_info *
+bone_capebus_probe_generic(struct cape_dev *dev,
+ const struct cape_device_id *id)
+{
+ struct bone_capebus_generic_info *info;
+ char boardbuf[33];
+ char versionbuf[5];
+ const char *board_name;
+ const char *version;
+ struct platform_device *pdev;
+ const struct bone_capebus_generic_device_data *dd;
+ struct bone_capebus_generic_device_entry *de;
+ int i;
+
+ /* get the board name (also matches the cntrlboard before checking) */
+ board_name = bone_capebus_id_get_field(id, BONE_CAPEBUS_BOARD_NAME,
+ boardbuf, sizeof(boardbuf));
+ /* get the board version */
+ version = bone_capebus_id_get_field(id, BONE_CAPEBUS_VERSION,
+ versionbuf, sizeof(versionbuf));
+
+ /* should never happen, but it doesn't hurt to play it safe */
+ if (board_name == NULL || version == NULL)
+ return ERR_PTR(-ENODEV);
+
+ info = devm_kzalloc(&dev->dev, sizeof(*info), GFP_KERNEL);
+ if (info == NULL) {
+ dev_err(&dev->dev, "Failed to allocate info\n");
+ return ERR_PTR(-ENOMEM);
+ }
+ info->dev = dev;
+ INIT_LIST_HEAD(&info->pdev_list);
+
+ /* iterate over the supported devices */
+ for (i = 0, dd = gendevs; i < ARRAY_SIZE(gendevs); i++, dd++) {
+
+ pdev = capebus_of_platform_compatible_device_create(dev,
+ dd->of_match, dd->name, "version", version);
+
+ /* node not found (mostly harmless) */
+ if (IS_ERR(pdev) && PTR_ERR(pdev) == -ENXIO) {
+ /* TODO: deal with required nodes */
+ continue;
+ }
+
+ /* failed to create due to an error; fatal */
+ if (IS_ERR_OR_NULL(pdev)) {
+ dev_err(&dev->dev, "failed to create device %s\n",
+ dd->name);
+ goto err_fail;
+ }
+
+ de = devm_kzalloc(&dev->dev, sizeof(*de), GFP_KERNEL);
+ if (de == NULL) {
+ dev_err(&dev->dev, "failed to allocate entry for %s\n",
+ dd->name);
+ goto err_fail;
+ }
+
+ /* add it to the list */
+ de->data = dd;
+ de->pdev = pdev;
+ list_add_tail(&de->node, &info->pdev_list);
+ }
+
+ return info;
+
+err_fail:
+ bone_capebus_remove_generic(info);
+ return NULL;
+}
+EXPORT_SYMBOL(bone_capebus_probe_generic);
+
+void bone_capebus_remove_generic(struct bone_capebus_generic_info *info)
+{
+ struct list_head *lh, *lhn;
+ struct bone_capebus_generic_device_entry *de;
+
+ if (info == NULL || info->dev == NULL)
+ return;
+
+ list_for_each_safe(lh, lhn, &info->pdev_list) {
+ de = list_entry(lh, struct bone_capebus_generic_device_entry,
+ node);
+ list_del(lh);
+ platform_device_unregister(de->pdev);
+ devm_kfree(&info->dev->dev, de);
+ }
+ devm_kfree(&info->dev->dev, info);
+}
+EXPORT_SYMBOL(bone_capebus_remove_generic);
diff --git a/drivers/capebus/boards/capebus-bone-pdevs.c b/drivers/capebus/boards/capebus-bone-pdevs.c
new file mode 100644
index 0000000..a55aad6
--- /dev/null
+++ b/drivers/capebus/boards/capebus-bone-pdevs.c
@@ -0,0 +1,602 @@
+/*
+ * TI Beaglebone capebus controller - Platform adapters
+ *
+ * Copyright (C) 2012 Pantelis Antoniou <[email protected]>
+ * 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/kernel.h>
+#include <linux/string.h>
+#include <linux/timer.h>
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/bitops.h>
+#include <linux/err.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/of_gpio.h>
+#include <video/da8xx-fb.h>
+#include <linux/pinctrl/pinctrl.h>
+#include <linux/pinctrl/pinmux.h>
+#include <linux/pinctrl/consumer.h>
+#include <linux/atomic.h>
+#include <linux/clk.h>
+#include <asm/barrier.h>
+#include <plat/clock.h>
+#include <plat/omap_device.h>
+#include <linux/clkdev.h>
+#include <linux/input/ti_am335x_tsc.h>
+#include <linux/platform_data/ti_am335x_adc.h>
+#include <linux/mfd/ti_am335x_tscadc.h>
+#include <linux/i2c.h>
+#include <linux/of_i2c.h>
+#include <linux/spi/spi.h>
+
+#include <linux/capebus/capebus-bone.h>
+
+#if defined(CONFIG_FB_DA8XX) || defined(CONFIG_FB_DA8XX_MODULE)
+
+struct da8xx_priv {
+ struct da8xx_lcdc_platform_data lcd_pdata;
+ struct lcd_ctrl_config lcd_cfg;
+ struct display_panel lcd_panel;
+ struct platform_device *lcdc_pdev;
+ struct omap_hwmod *lcdc_oh;
+ struct resource lcdc_res[1];
+ int power_dn_gpio;
+};
+
+static const struct of_device_id of_da8xx_dt_match[] = {
+ { .compatible = "da8xx-dt", },
+ {},
+};
+
+static int __devinit da8xx_dt_probe(struct platform_device *pdev)
+{
+ struct da8xx_priv *priv;
+ struct clk *disp_pll;
+ struct pinctrl *pinctrl;
+ u32 disp_pll_val;
+ const char *panel_type;
+ int ret = -EINVAL;
+
+ priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
+ if (priv == NULL) {
+ dev_err(&pdev->dev, "Failed to allocate priv\n");
+ return -ENOMEM;
+ }
+ priv->power_dn_gpio = -1;
+
+ pinctrl = devm_pinctrl_get_select_default(&pdev->dev);
+ if (IS_ERR(pinctrl))
+ dev_warn(&pdev->dev,
+ "pins are not configured from the driver\n");
+
+ ret = of_property_read_u32(pdev->dev.of_node, "disp-pll", &disp_pll_val);
+ if (ret != 0) {
+ dev_err(&pdev->dev, "Failed to read disp-pll property\n");
+ return ret;
+ }
+
+ ret = of_property_read_string(pdev->dev.of_node, "panel-type", &panel_type);
+ if (ret != 0) {
+ dev_err(&pdev->dev, "Failed to read panel-type property\n");
+ return ret;
+ }
+
+ /* conf_disp_pll(disp_pll); */
+ disp_pll = clk_get(NULL, "dpll_disp_ck");
+ if (IS_ERR(disp_pll)) {
+ dev_err(&pdev->dev, "Cannot clk_get disp_pll\n");
+ return PTR_ERR(disp_pll);
+ }
+ ret = clk_set_rate(disp_pll, disp_pll_val);
+ clk_put(disp_pll);
+ if (ret != 0) {
+ dev_err(&pdev->dev, "Failed to set disp_pll\n");
+ return ret;
+ }
+
+ ret = of_get_named_gpio_flags(pdev->dev.of_node, "powerdn-gpio",
+ 0, NULL);
+ if (IS_ERR_VALUE(ret)) {
+ dev_info(&pdev->dev, "No power down GPIO\n");
+ } else {
+ priv->power_dn_gpio = ret;
+
+ ret = devm_gpio_request(&pdev->dev, priv->power_dn_gpio, "bone-dvi-cape:DVI_PDN");
+ if (ret != 0) {
+ dev_err(&pdev->dev, "Failed to gpio_request\n");
+ return ret;
+ }
+
+ ret = gpio_direction_output(priv->power_dn_gpio, 1);
+ if (ret != 0) {
+ dev_err(&pdev->dev, "Failed to set powerdn to 1\n");
+ return ret;
+ }
+ }
+
+ /* display_panel */
+ priv->lcd_panel.panel_type = QVGA;
+ priv->lcd_panel.max_bpp = 16;
+ priv->lcd_panel.min_bpp = 16;
+ priv->lcd_panel.panel_shade = COLOR_ACTIVE;
+
+ /* lcd_ctrl_config */
+ priv->lcd_cfg.p_disp_panel = &priv->lcd_panel;
+ priv->lcd_cfg.ac_bias = 255;
+ priv->lcd_cfg.ac_bias_intrpt = 0;
+ priv->lcd_cfg.dma_burst_sz = 16;
+ priv->lcd_cfg.bpp = 16;
+ priv->lcd_cfg.fdd = 0x80;
+ priv->lcd_cfg.tft_alt_mode = 0;
+ priv->lcd_cfg.stn_565_mode = 0;
+ priv->lcd_cfg.mono_8bit_mode = 0;
+ priv->lcd_cfg.invert_line_clock = 1;
+ priv->lcd_cfg.invert_frm_clock = 1;
+ priv->lcd_cfg.sync_edge = 0;
+ priv->lcd_cfg.sync_ctrl = 1;
+ priv->lcd_cfg.raster_order = 0;
+
+ /* da8xx_lcdc_platform_data */
+ strcpy(priv->lcd_pdata.manu_name, "BBToys");
+ priv->lcd_pdata.controller_data = &priv->lcd_cfg;
+ strcpy(priv->lcd_pdata.type, panel_type);
+
+ priv->lcdc_oh = omap_hwmod_lookup("lcdc");
+ if (priv->lcdc_oh == NULL) {
+ dev_err(&pdev->dev, "Failed to lookup omap_hwmod lcdc\n");
+ return -ENODEV;
+ }
+
+ priv->lcdc_pdev = omap_device_build("da8xx_lcdc", 0, priv->lcdc_oh,
+ &priv->lcd_pdata,
+ sizeof(struct da8xx_lcdc_platform_data),
+ NULL, 0, 0);
+ if (priv->lcdc_pdev == NULL) {
+ dev_err(&pdev->dev, "Failed to build LCDC device\n");
+ return -ENODEV;
+ }
+
+ dev_info(&pdev->dev, "Registered bone LCDC OK.\n");
+
+ platform_set_drvdata(pdev, priv);
+
+ return 0;
+}
+
+static int __devexit da8xx_dt_remove(struct platform_device *pdev)
+{
+ return -EINVAL; /* not supporting removal yet */
+}
+
+static struct platform_driver da8xx_dt_driver = {
+ .probe = da8xx_dt_probe,
+ .remove = __devexit_p(da8xx_dt_remove),
+ .driver = {
+ .name = "da8xx-dt",
+ .owner = THIS_MODULE,
+ .of_match_table = of_da8xx_dt_match,
+ },
+};
+
+#endif
+
+#if defined(CONFIG_MFD_TI_AM335X_TSCADC) || defined(CONFIG_MFD_TI_AM335X_TSCADC_MODULE)
+
+struct ti_tscadc_priv {
+ struct omap_hwmod *tsc_oh;
+ struct tsc_data tsc_data;
+ struct adc_data adc_data;
+ struct mfd_tscadc_board tscadc_data;
+ struct platform_device *tscadc_pdev;
+};
+
+static const struct of_device_id of_ti_tscadc_dt_match[] = {
+ { .compatible = "ti-tscadc-dt", },
+ {},
+};
+
+static int __devinit ti_tscadc_dt_probe(struct platform_device *pdev)
+{
+ struct ti_tscadc_priv *priv;
+ struct pinctrl *pinctrl;
+ u32 val;
+ int ret;
+
+ priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
+ if (priv == NULL) {
+ dev_err(&pdev->dev, "Failed to allocate priv\n");
+ return -ENOMEM;
+ }
+
+ pinctrl = devm_pinctrl_get_select_default(&pdev->dev);
+ if (IS_ERR(pinctrl))
+ dev_warn(&pdev->dev,
+ "pins are not configured from the driver\n");
+
+ ret = of_property_read_u32(pdev->dev.of_node, "tsc-wires", &val);
+ if (ret != 0) {
+ dev_info(&pdev->dev, "no tsc-wires property; disabling TSC\n");
+ val = 0;
+ }
+ priv->tsc_data.wires = val;
+
+ if (priv->tsc_data.wires > 0) {
+ ret = of_property_read_u32(pdev->dev.of_node,
+ "tsc-x-plate-resistance", &val);
+ if (ret != 0) {
+ dev_err(&pdev->dev, "Failed to read "
+ "tsc-x-plate-resistance property\n");
+ return ret;
+ }
+ priv->tsc_data.x_plate_resistance = val;
+
+ ret = of_property_read_u32(pdev->dev.of_node,
+ "tsc-steps", &val);
+ if (ret != 0) {
+ dev_err(&pdev->dev, "Failed to read "
+ "tsc-steps property\n");
+ return ret;
+ }
+ priv->tsc_data.steps_to_configure = val;
+ }
+
+ ret = of_property_read_u32(pdev->dev.of_node, "adc-channels", &val);
+ if (ret != 0) {
+ dev_info(&pdev->dev, "No adc-channels property; disabling adc\n");
+ val = 0;
+ }
+ priv->adc_data.adc_channels = val;
+
+ priv->tscadc_data.tsc_init = &priv->tsc_data;
+ priv->tscadc_data.adc_init = &priv->adc_data;
+
+ priv->tsc_oh = omap_hwmod_lookup("adc_tsc");
+ if (priv->tsc_oh == NULL) {
+ dev_err(&pdev->dev, "Could not lookup HWMOD %s\n", "adc_tsc");
+ return -ENODEV;
+ }
+
+ priv->tscadc_pdev = omap_device_build("ti_tscadc", -1, priv->tsc_oh,
+ &priv->tscadc_data, sizeof(priv->tscadc_data),
+ NULL, 0, 0);
+ if (priv->tscadc_pdev == NULL) {
+ dev_err(&pdev->dev, "Could not create tsc_adc device\n");
+ return -ENODEV;
+ }
+
+ dev_info(&pdev->dev, "TI tscadc pdev created OK\n");
+
+ platform_set_drvdata(pdev, priv);
+
+ return 0;
+}
+
+static int __devexit ti_tscadc_dt_remove(struct platform_device *pdev)
+{
+ return -EINVAL; /* not supporting removal yet */
+}
+
+static struct platform_driver ti_tscadc_dt_driver = {
+ .probe = ti_tscadc_dt_probe,
+ .remove = __devexit_p(ti_tscadc_dt_remove),
+ .driver = {
+ .name = "ti_tscadc-dt",
+ .owner = THIS_MODULE,
+ .of_match_table = of_ti_tscadc_dt_match,
+ },
+};
+
+#endif
+
+struct i2c_priv {
+ struct i2c_adapter *i2c_adapter;
+ phandle parent_handle;
+};
+
+static const struct of_device_id of_i2c_dt_match[] = {
+ { .compatible = "i2c-dt", },
+ {},
+};
+
+static int __devinit i2c_dt_probe(struct platform_device *pdev)
+{
+ struct i2c_priv *priv = NULL;
+ int ret = -EINVAL;
+ struct device_node *adap_node;
+ u32 val;
+
+ if (pdev->dev.of_node == NULL) {
+ dev_err(&pdev->dev, "Only support OF case\n");
+ return -ENOMEM;
+ }
+
+ priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
+ if (priv == NULL) {
+ dev_err(&pdev->dev, "Failed to allocate priv\n");
+ return -ENOMEM;
+ }
+
+ ret = of_property_read_u32(pdev->dev.of_node, "parent", &val);
+ if (ret != 0) {
+ dev_err(&pdev->dev, "Failed to find parent property\n");
+ goto err_prop_fail;
+ }
+ priv->parent_handle = val;
+
+ adap_node = of_find_node_by_phandle(priv->parent_handle);
+ if (adap_node == NULL) {
+ dev_err(&pdev->dev, "Failed to find i2c adapter node\n");
+ ret = -EINVAL;
+ goto err_node_fail;
+ }
+
+ ret = capebus_of_platform_device_enable(adap_node);
+ if (ret != 0) {
+ dev_info(&pdev->dev, "I2C adapter platform device failed "
+ "to enable\n");
+ goto err_enable_fail;
+ }
+
+ priv->i2c_adapter = of_find_i2c_adapter_by_node(adap_node);
+ if (priv->i2c_adapter == NULL) {
+ dev_err(&pdev->dev, "Failed to find i2c adapter node\n");
+ ret = -EINVAL;
+ goto err_adap_fail;
+ }
+
+ of_i2c_register_node_devices(priv->i2c_adapter, pdev->dev.of_node);
+
+ of_node_put(adap_node);
+
+ dev_info(&pdev->dev, "Registered bone I2C OK.\n");
+
+ platform_set_drvdata(pdev, priv);
+
+ return 0;
+err_adap_fail:
+ of_node_put(adap_node);
+err_enable_fail:
+ /* nothing */
+err_node_fail:
+ /* nothing */
+err_prop_fail:
+ devm_kfree(&pdev->dev, priv);
+ return ret;
+}
+
+static int __devexit i2c_dt_remove(struct platform_device *pdev)
+{
+ return -EINVAL; /* not supporting removal yet */
+}
+
+static struct platform_driver i2c_dt_driver = {
+ .probe = i2c_dt_probe,
+ .remove = __devexit_p(i2c_dt_remove),
+ .driver = {
+ .name = "i2c-dt",
+ .owner = THIS_MODULE,
+ .of_match_table = of_i2c_dt_match,
+ },
+};
+
+struct spi_priv {
+ struct spi_master *master;
+ phandle parent_handle;
+};
+
+static const struct of_device_id of_spi_dt_match[] = {
+ { .compatible = "spi-dt", },
+ {},
+};
+
+static int of_dev_node_match(struct device *dev, void *data)
+{
+ return dev->of_node == data;
+}
+
+/* must call put_device() when done with returned i2c_adapter device */
+static struct spi_master *of_find_spi_master_by_node(struct device_node *node)
+{
+ struct device *dev;
+ struct spi_master *master;
+
+ dev = class_find_device(&spi_master_class, NULL, node,
+ of_dev_node_match);
+ if (!dev)
+ return NULL;
+
+ master = container_of(dev, struct spi_master, dev);
+
+ /* TODO: No checks what-so-ever... be careful. */
+ return master;
+}
+
+static int __devinit spi_dt_probe(struct platform_device *pdev)
+{
+ struct spi_priv *priv = NULL;
+ int ret = -EINVAL;
+ struct device_node *master_node;
+ u32 val;
+
+ if (pdev->dev.of_node == NULL) {
+ dev_err(&pdev->dev, "Only support OF case\n");
+ return -ENOMEM;
+ }
+
+ priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
+ if (priv == NULL) {
+ dev_err(&pdev->dev, "Failed to allocate priv\n");
+ return -ENOMEM;
+ }
+
+ ret = of_property_read_u32(pdev->dev.of_node, "parent", &val);
+ if (ret != 0) {
+ dev_err(&pdev->dev, "Failed to find parent property\n");
+ goto err_prop_fail;
+ }
+ priv->parent_handle = val;
+
+ master_node = of_find_node_by_phandle(priv->parent_handle);
+ if (master_node == NULL) {
+ dev_err(&pdev->dev, "Failed to find spi bus master node\n");
+ ret = -EINVAL;
+ goto err_node_fail;
+ }
+
+ ret = capebus_of_platform_device_enable(master_node);
+ if (ret != 0) {
+ dev_info(&pdev->dev, "SPI platform device failed to enable\n");
+ goto err_enable_fail;
+ }
+
+ priv->master = of_find_spi_master_by_node(master_node);
+ if (priv->master == NULL) {
+ dev_err(&pdev->dev, "Failed to find bus master node\n");
+ ret = -EINVAL;
+ goto err_master_fail;
+ }
+
+ of_register_node_spi_devices(priv->master, pdev->dev.of_node);
+
+ of_node_put(master_node);
+
+ dev_info(&pdev->dev, "Registered bone SPI OK.\n");
+
+ platform_set_drvdata(pdev, priv);
+
+ return 0;
+err_master_fail:
+ of_node_put(master_node);
+err_enable_fail:
+ /* nothing */
+err_node_fail:
+ /* nothing */
+err_prop_fail:
+ devm_kfree(&pdev->dev, priv);
+ return ret;
+}
+
+static int __devexit spi_dt_remove(struct platform_device *pdev)
+{
+ return -EINVAL; /* not supporting removal yet */
+}
+
+static struct platform_driver spi_dt_driver = {
+ .probe = spi_dt_probe,
+ .remove = __devexit_p(spi_dt_remove),
+ .driver = {
+ .name = "spi-dt",
+ .owner = THIS_MODULE,
+ .of_match_table = of_spi_dt_match,
+ },
+};
+
+/*
+ *
+ */
+struct bone_capebus_pdev_driver {
+ struct platform_driver *driver;
+ unsigned int registered : 1;
+ /* more? */
+};
+
+static struct bone_capebus_pdev_driver pdev_drivers[] = {
+#if defined(CONFIG_FB_DA8XX) || defined(CONFIG_FB_DA8XX_MODULE)
+ {
+ .driver = &da8xx_dt_driver,
+ },
+#endif
+#if defined(CONFIG_MFD_TI_AM335X_TSCADC) || defined(CONFIG_MFD_TI_AM335X_TSCADC_MODULE)
+ {
+ .driver = &ti_tscadc_dt_driver,
+ },
+#endif
+ {
+ .driver = &i2c_dt_driver,
+ },
+ {
+ .driver = &spi_dt_driver,
+ },
+ {
+ .driver = NULL,
+ }
+};
+
+int bone_capebus_register_pdev_adapters(struct bone_capebus_bus *bus)
+{
+ struct bone_capebus_pdev_driver *drvp;
+ int err;
+
+ /* first check if we do it twice */
+ for (drvp = pdev_drivers; drvp->driver != NULL; drvp++)
+ if (drvp->registered)
+ return -EBUSY;
+
+ for (drvp = pdev_drivers; drvp->driver != NULL; drvp++) {
+
+ err = platform_driver_register(drvp->driver);
+ if (err != 0)
+ goto err_out;
+
+ drvp->registered = 1;
+
+ dev_info(bus->dev, "Registered %s "
+ "platform driver\n", drvp->driver->driver.name);
+ }
+
+ return 0;
+
+err_out:
+ dev_err(bus->dev, "Failed to register %s "
+ "platform driver\n", drvp->driver->driver.name);
+
+ /* unregister */
+ while (--drvp >= pdev_drivers) {
+
+ if (!drvp->registered)
+ continue;
+
+ platform_driver_unregister(drvp->driver);
+ }
+
+ return err;
+}
+
+void bone_capebus_unregister_pdev_adapters(struct bone_capebus_bus *bus)
+{
+ struct bone_capebus_pdev_driver *drvp;
+
+ /* unregister */
+ drvp = &pdev_drivers[ARRAY_SIZE(pdev_drivers)];
+ while (--drvp >= pdev_drivers) {
+
+ if (drvp->driver == NULL) /* skip terminator */
+ continue;
+
+ if (!drvp->registered)
+ continue;
+
+ platform_driver_unregister(drvp->driver);
+
+ drvp->registered = 0;
+ }
+}
diff --git a/drivers/capebus/boards/capebus-bone.c b/drivers/capebus/boards/capebus-bone.c
new file mode 100644
index 0000000..33a6bde
--- /dev/null
+++ b/drivers/capebus/boards/capebus-bone.c
@@ -0,0 +1,931 @@
+/*
+ * TI Beaglebone capebus controller
+ *
+ * Copyright (C) 2012 Pantelis Antoniou <[email protected]>
+ * 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/delay.h>
+#include <linux/i2c.h>
+#include <linux/err.h>
+#include <linux/interrupt.h>
+#include <linux/completion.h>
+#include <linux/platform_device.h>
+#include <linux/clk.h>
+#include <linux/io.h>
+#include <linux/of.h>
+#include <linux/of_i2c.h>
+#include <linux/of_device.h>
+#include <linux/slab.h>
+#include <linux/pm_runtime.h>
+#include <linux/pinctrl/consumer.h>
+#include <linux/err.h>
+#include <linux/ctype.h>
+
+#include <linux/capebus.h>
+#include <linux/capebus/capebus-bone.h>
+
+/* what to fill in to the cntrlboard field of the id */
+#define BONE_CAPEBUS_CNTRLBOARD "beaglebone"
+
+/* various EEPROM definition for the bone */
+struct bone_capebus_eeprom_field {
+ const char *name;
+ int start;
+ int size;
+ unsigned int ascii : 1;
+ unsigned int strip_trailing_dots : 1;
+ const char *override;
+};
+
+static const struct bone_capebus_eeprom_field eeprom_fields[] = {
+ [BONE_CAPEBUS_HEADER] = {
+ .name = "header",
+ .start = 0,
+ .size = 4,
+ .ascii = 0,
+ .override = "\xaa\x55\x33\xee", /* AA 55 33 EE */
+ },
+ [BONE_CAPEBUS_EEPROM_REV] = {
+ .name = "eeprom-format-revision",
+ .start = 4,
+ .size = 2,
+ .ascii = 1,
+ .override = "A0",
+ },
+ [BONE_CAPEBUS_BOARD_NAME] = {
+ .name = "board-name",
+ .start = 6,
+ .size = 32,
+ .ascii = 1,
+ .strip_trailing_dots = 1,
+ .override = "Override Board Name",
+ },
+ [BONE_CAPEBUS_VERSION] = {
+ .name = "version",
+ .start = 38,
+ .size = 4,
+ .ascii = 1,
+ .override = "00A0",
+ },
+ [BONE_CAPEBUS_MANUFACTURER] = {
+ .name = "manufacturer",
+ .start = 42,
+ .size = 16,
+ .ascii = 1,
+ .strip_trailing_dots = 1,
+ .override = "Override Manuf",
+ },
+ [BONE_CAPEBUS_PART_NUMBER] = {
+ .name = "part-number",
+ .start = 58,
+ .size = 16,
+ .ascii = 1,
+ .override = "Override Part#",
+ },
+ [BONE_CAPEBUS_NUMBER_OF_PINS] = {
+ .name = "number-of-pins",
+ .start = 74,
+ .size = 2,
+ .ascii = 0,
+ .override = NULL,
+ },
+ [BONE_CAPEBUS_SERIAL_NUMBER] = {
+ .name = "serial-number",
+ .start = 76,
+ .size = 12,
+ .ascii = 1,
+ .override = "0000000000",
+ },
+ [BONE_CAPEBUS_PIN_USAGE] = {
+ .name = "pin-usage",
+ .start = 88,
+ .size = 140,
+ .ascii = 0,
+ .override = NULL,
+ },
+ [BONE_CAPEBUS_VDD_3V3EXP] = {
+ .name = "vdd-3v3exp",
+ .start = 228,
+ .size = 2,
+ .ascii = 0,
+ .override = NULL,
+ },
+ [BONE_CAPEBUS_VDD_5V] = {
+ .name = "vdd-5v",
+ .start = 230,
+ .size = 2,
+ .ascii = 0,
+ .override = NULL,
+ },
+ [BONE_CAPEBUS_SYS_5V] = {
+ .name = "sys-5v",
+ .start = 232,
+ .size = 2,
+ .ascii = 0,
+ .override = NULL,
+ },
+ [BONE_CAPEBUS_DC_SUPPLIED] = {
+ .name = "dc-supplied",
+ .start = 234,
+ .size = 2,
+ .ascii = 0,
+ .override = NULL,
+ },
+};
+
+char *bone_capebus_id_get_field(const struct cape_device_id *id,
+ int field, char *buf, int bufsz)
+{
+ const struct bone_capebus_eeprom_field *ee_field;
+ int len;
+
+ /* make sure the ID is valid for the bone */
+ if (bone_capebus_match_cntrlboard(id) != 0)
+ return NULL;
+
+ if ((unsigned int)field >= ARRAY_SIZE(eeprom_fields))
+ return NULL;
+
+ ee_field = &eeprom_fields[field];
+
+ /* enough space? */
+ if (bufsz < ee_field->size + ee_field->ascii)
+ return NULL;
+
+ memcpy(buf, (char *)id->data + ee_field->start, ee_field->size);
+
+ /* terminate ascii field */
+ if (ee_field->ascii)
+ buf[ee_field->size] = '\0';;
+
+ if (ee_field->strip_trailing_dots) {
+ len = strlen(buf);
+ while (len > 1 && buf[len - 1] == '.')
+ buf[--len] = '\0';
+ }
+
+ return buf;
+}
+EXPORT_SYMBOL(bone_capebus_id_get_field);
+
+int bone_capebus_match_cntrlboard(const struct cape_device_id *id)
+{
+ if (strcmp(id->cntrlboard, BONE_CAPEBUS_CNTRLBOARD) != 0)
+ return -ENODEV;
+ return 0;
+}
+EXPORT_SYMBOL(bone_capebus_match_cntrlboard);
+
+int bone_capebus_match_board(const struct cape_device_id *id,
+ const char **board_names)
+{
+ char rname[33];
+ const char *s;
+ int ret;
+ int i;
+
+ /* be safe; check for matching cntrlboard */
+ ret = bone_capebus_match_cntrlboard(id);
+ if (ret != 0)
+ return ret;
+
+ s = bone_capebus_id_get_field(id, BONE_CAPEBUS_BOARD_NAME,
+ rname, sizeof(rname));
+ if (s == NULL)
+ return -EINVAL;
+
+ i = 0;
+ while (*board_names) {
+ if (strcmp(rname, *board_names) == 0)
+ return i;
+ board_names++;
+ }
+
+ return -1;
+}
+EXPORT_SYMBOL(bone_capebus_match_board);
+
+#ifdef CONFIG_OF
+static const struct of_device_id bone_capebus_of_match[] = {
+ {
+ .compatible = "bone-capebus",
+ },
+ { },
+};
+MODULE_DEVICE_TABLE(of, bone_capebus_of_match);
+
+static const struct of_device_id slot_override_of_match[] = {
+ {
+ .compatible = "bone-capebus-slot-override",
+ },
+ { },
+};
+
+#endif
+
+const struct cape_device_id *bone_capebus_get_dev_id(struct cape_slot *slot)
+{
+ struct cape_bus *bus = slot->bus;
+ struct bone_capebus_slot *bone_slot = to_bone_capebus_slot(slot);
+ struct i2c_client *client = bone_slot->client;
+ struct cape_device_id *id;
+ const u8 *p;
+ int r;
+ char board_name[32+1];
+ char version[4+1];
+ char manufacturer[16+1];
+ char part_number[16+1];
+
+ id = &bone_slot->id;
+
+ /* need to read EEPROM? */
+ if (!bone_slot->eeprom_probed) {
+
+ bone_slot->eeprom_probed = 1;
+
+ if (!bone_slot->eeprom_override) {
+ r = i2c_memory_read(bone_slot->client,
+ bone_slot->eeprom_signature, 0,
+ sizeof(bone_slot->eeprom_signature));
+ if (r != sizeof(bone_slot->eeprom_signature)) {
+ dev_err(&bus->dev,
+ "bone: Failed to read EEPROM at "
+ "slot %d (addr 0x%02x)\n",
+ slot->slotno, client->addr & 0x7f);
+ bone_slot->eeprom_failed = 1;
+ return NULL;
+ }
+ } else
+ dev_info(&bus->dev,
+ "bone: Using override eeprom data at slot %d\n",
+ slot->slotno);
+
+ p = bone_slot->eeprom_signature;
+ if (BONE_CAPEBUS_MAKE_HEADER(p) != BONE_CAPEBUS_HEADER_VALID) {
+ dev_err(&bus->dev, "bone: Invalid EEPROM signature "
+ "'%08x' at slot %d (addr 0x%02x)\n",
+ BONE_CAPEBUS_MAKE_HEADER(p),
+ slot->slotno, client->addr & 0x7f);
+ bone_slot->eeprom_failed = 1;
+ return NULL;
+ }
+
+ bone_slot->id.cntrlboard = BONE_CAPEBUS_CNTRLBOARD;
+ bone_slot->id.len = sizeof(bone_slot->eeprom_signature);
+ bone_slot->id.data = bone_slot->eeprom_signature;
+
+ bone_capebus_id_get_field(id, BONE_CAPEBUS_BOARD_NAME,
+ board_name, sizeof(board_name));
+ bone_capebus_id_get_field(id, BONE_CAPEBUS_VERSION,
+ version, sizeof(version));
+ bone_capebus_id_get_field(id, BONE_CAPEBUS_MANUFACTURER,
+ manufacturer, sizeof(manufacturer));
+ bone_capebus_id_get_field(id, BONE_CAPEBUS_PART_NUMBER,
+ part_number, sizeof(part_number));
+
+ /* board_name,version,manufacturer,part_number */
+ snprintf(bone_slot->text_id, sizeof(bone_slot->text_id) - 1,
+ "%s,%s,%s,%s", board_name, version,
+ manufacturer, part_number);
+
+ /* terminate always */
+ bone_slot->text_id[sizeof(bone_slot->text_id) - 1] = '\0';
+
+ }
+
+ /* slot has failed and we don't support hotpluging */
+ if (bone_slot->eeprom_failed)
+ return NULL;
+
+ return id;
+}
+
+const char *bone_capebus_get_text_dev_id(struct cape_slot *slot)
+{
+ struct bone_capebus_slot *bone_slot = to_bone_capebus_slot(slot);
+
+ if (bone_slot->eeprom_failed || !bone_slot->eeprom_probed)
+ return NULL;
+
+ return bone_slot->text_id;
+}
+
+struct bonedev_ee_attribute {
+ struct device_attribute devattr;
+ unsigned int field;
+};
+#define to_bonedev_ee_attribute(x) \
+ container_of((x), struct bonedev_ee_attribute, devattr)
+
+static ssize_t bonedev_ee_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct bonedev_ee_attribute *ee_attr = to_bonedev_ee_attribute(attr);
+ struct cape_dev *cdev = to_cape_dev(dev);
+ const struct cape_device_id *id = cdev->id;
+ const struct bone_capebus_eeprom_field *ee_field;
+ int i, len;
+ char *p, *s;
+ u16 val;
+
+ if (id == NULL)
+ return -EINVAL;
+
+ /* add newline for ascii fields */
+ ee_field = &eeprom_fields[ee_attr->field];
+
+ len = ee_field->size + ee_field->ascii;
+ p = kmalloc(len, GFP_KERNEL);
+ if (p == NULL)
+ return -ENOMEM;
+
+ s = bone_capebus_id_get_field(id, ee_attr->field, p, len);
+ if (s == NULL)
+ return -EINVAL;
+
+ /* add newline for ascii fields and return */
+ if (ee_field->ascii) {
+ len = sprintf(buf, "%s\n", s);
+ goto out;
+ }
+
+ /* case by case handling */
+ switch (ee_attr->field) {
+ case BONE_CAPEBUS_HEADER:
+ len = sprintf(buf, "%02x %02x %02x %02x\n",
+ s[0], s[1], s[2], s[3]);
+ break;
+
+ /* 2 bytes */
+ case BONE_CAPEBUS_NUMBER_OF_PINS:
+ case BONE_CAPEBUS_VDD_3V3EXP:
+ case BONE_CAPEBUS_VDD_5V:
+ case BONE_CAPEBUS_SYS_5V:
+ case BONE_CAPEBUS_DC_SUPPLIED:
+ /* the bone is LE */
+ val = s[0] & (s[1] << 8);
+ len = sprintf(buf, "%u\n", (unsigned int)val & 0xffff);
+ break;
+
+ case BONE_CAPEBUS_PIN_USAGE:
+
+ len = 0;
+ for (i = 0; i < ee_field->size / 2; i++) {
+ /* the bone is LE */
+ val = s[0] & (s[1] << 8);
+ sprintf(buf, "%04x\n", val);
+ buf += 5;
+ len += 5;
+ s += 2;
+ }
+
+ break;
+
+ default:
+ *buf = '\0';
+ len = 0;
+ break;
+ }
+
+out:
+ kfree(p);
+
+ return len;
+}
+
+#define BONEDEV_EE_ATTR(_name, _field) \
+ { \
+ .devattr = __ATTR(_name, 0440, bonedev_ee_show, NULL), \
+ .field = BONE_CAPEBUS_##_field , \
+ }
+
+struct bonedev_ee_attribute ee_attrs[] = {
+ BONEDEV_EE_ATTR(header, HEADER),
+ BONEDEV_EE_ATTR(eeprom-format-revision, EEPROM_REV),
+ BONEDEV_EE_ATTR(board-name, BOARD_NAME),
+ BONEDEV_EE_ATTR(version, VERSION),
+ BONEDEV_EE_ATTR(manufacturer, MANUFACTURER),
+ BONEDEV_EE_ATTR(part-number, PART_NUMBER),
+ BONEDEV_EE_ATTR(number-of-pins, NUMBER_OF_PINS),
+ BONEDEV_EE_ATTR(serial-number, SERIAL_NUMBER),
+ BONEDEV_EE_ATTR(pin-usage, PIN_USAGE),
+ BONEDEV_EE_ATTR(vdd-3v3exp, VDD_3V3EXP),
+ BONEDEV_EE_ATTR(vdd-5v, VDD_5V),
+ BONEDEV_EE_ATTR(sys-5v, SYS_5V),
+ BONEDEV_EE_ATTR(dc-supplied, DC_SUPPLIED),
+};
+
+static struct attribute *ee_attrs_flat[] = {
+ &ee_attrs[BONE_CAPEBUS_HEADER ].devattr.attr,
+ &ee_attrs[BONE_CAPEBUS_EEPROM_REV ].devattr.attr,
+ &ee_attrs[BONE_CAPEBUS_BOARD_NAME ].devattr.attr,
+ &ee_attrs[BONE_CAPEBUS_VERSION ].devattr.attr,
+ &ee_attrs[BONE_CAPEBUS_MANUFACTURER ].devattr.attr,
+ &ee_attrs[BONE_CAPEBUS_PART_NUMBER ].devattr.attr,
+ &ee_attrs[BONE_CAPEBUS_NUMBER_OF_PINS ].devattr.attr,
+ &ee_attrs[BONE_CAPEBUS_SERIAL_NUMBER ].devattr.attr,
+ &ee_attrs[BONE_CAPEBUS_PIN_USAGE ].devattr.attr,
+ &ee_attrs[BONE_CAPEBUS_VDD_3V3EXP ].devattr.attr,
+ &ee_attrs[BONE_CAPEBUS_VDD_5V ].devattr.attr,
+ &ee_attrs[BONE_CAPEBUS_SYS_5V ].devattr.attr,
+ &ee_attrs[BONE_CAPEBUS_DC_SUPPLIED ].devattr.attr,
+ NULL,
+};
+
+static const struct attribute_group bone_ee_attrgroup = {
+ .name = "ee-fields",
+ .is_visible = NULL,
+ .attrs = ee_attrs_flat,
+};
+
+static int bone_capebus_sysfs_register(struct cape_dev *dev)
+{
+ return sysfs_create_group(&dev->dev.kobj, &bone_ee_attrgroup);
+}
+
+static void bone_capebus_sysfs_unregister(struct cape_dev *dev)
+{
+ sysfs_remove_group(&dev->dev.kobj, &bone_ee_attrgroup);
+}
+
+static int bone_capebus_dev_probed(struct cape_dev *dev)
+{
+ return 0;
+}
+
+static void bone_capebus_dev_removed(struct cape_dev *dev)
+{
+ bone_capebus_sysfs_unregister(dev);
+}
+
+static int bone_capebus_dev_registered(struct cape_dev *dev)
+{
+ int ret;
+
+ ret = bone_capebus_sysfs_register(dev);
+ if (ret != 0) {
+ dev_err(&dev->dev, "bone_capebus sysfs registration failed\n");
+ return ret;
+ }
+
+ return 0;
+}
+
+static struct cape_bus_ops bone_capebus_ops = {
+ .get_dev_id = bone_capebus_get_dev_id,
+ .get_text_dev_id = bone_capebus_get_text_dev_id,
+ .dev_probed = bone_capebus_dev_probed,
+ .dev_removed = bone_capebus_dev_removed,
+ .dev_registered = bone_capebus_dev_registered,
+};
+
+static ssize_t slots_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct bone_capebus_bus *bus = platform_get_drvdata(pdev);
+ struct bone_capebus_slot *slot;
+ ssize_t len, sz;
+ int i;
+
+ sz = 0;
+
+ for (i = 0; i < bus->slots_nr; i++) {
+ slot = &bus->slots[i];
+
+ len = sprintf(buf, "%02x:%c%c%c%c %s\n",
+ (int)slot->eeprom_addr & 0x7f,
+ slot->eeprom_probed ? 'P' : '-',
+ slot->eeprom_failed ? 'F' : '-',
+ slot->eeprom_override ? 'O' : '-',
+ (slot->cape_slot.dev && slot->cape_slot.dev->added) ? 'A' : '-',
+ slot->text_id);
+
+ buf += len;
+ sz += len;
+ }
+ return sz;
+}
+
+static ssize_t slots_store(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct bone_capebus_bus *bus = platform_get_drvdata(pdev);
+ int slotno, err, i, len;
+ char *s, *board_name, *version, *p;
+ const struct bone_capebus_eeprom_field *ee_field, *eebrd, *eevrs;
+ struct bone_capebus_slot *slot;
+
+ eebrd = &eeprom_fields[BONE_CAPEBUS_BOARD_NAME];
+ eevrs = &eeprom_fields[BONE_CAPEBUS_VERSION];
+
+ slotno = simple_strtoul(buf, &s, 10);
+ if (slotno < 0 || slotno >= bus->slots_nr)
+ return -EINVAL;
+ slot = &bus->slots[slotno];
+ if (slot->eeprom_override || (slot->cape_slot.dev && slot->cape_slot.dev->added))
+ return -EINVAL;
+
+ board_name = kzalloc(eebrd->size + 1 + eevrs->size + 1, GFP_KERNEL);
+ if (board_name == NULL)
+ return -ENOMEM;
+ version = board_name + eebrd->size + 1;
+
+ s = strchr(s, ':');
+ if (s == NULL) {
+ kfree(board_name);
+ return -EINVAL;
+ }
+ s++;
+ p = strchr(s, ':');
+ if (p == NULL) {
+ len = strlen(s);
+ strncpy(board_name, s, eebrd->size);
+ strcpy(version, "00A0");
+ } else {
+ len = p - s;
+ if (len > eebrd->size)
+ len = p - s;
+ memcpy(board_name, s, len);
+ board_name[len] = '\0';
+ strncpy(version, p + 1, eevrs->size);
+ }
+ board_name[eebrd->size] = '\0';
+ version[eevrs->size] = '\0';
+
+ /* strip trailing spaces, dots & newlines */
+ s = board_name + strlen(board_name);
+ while (s > board_name &&
+ (isspace(s[-1]) || s[-1] == '\n' || s[-1] == '.'))
+ *--s = '\0';
+
+ printk(KERN_INFO "Override for slot #%d, board-name '%s', version '%s'\n",
+ slotno, board_name, version);
+
+ slot->eeprom_override = 1;
+ slot->eeprom_failed = 0;
+ slot->eeprom_probed = 0;
+
+ /* zero out signature */
+ memset(slot->eeprom_signature, 0,
+ sizeof(slot->eeprom_signature));
+
+ /* create an eeprom field */
+ for (i = 0; i < ARRAY_SIZE(eeprom_fields); i++) {
+
+ ee_field = &eeprom_fields[i];
+
+ /* point to the entry */
+ p = slot->eeprom_signature + ee_field->start;
+
+ /* if no such property, assign default */
+ if (i != BONE_CAPEBUS_BOARD_NAME) {
+
+ if (ee_field->override)
+ memcpy(p, ee_field->override,
+ ee_field->size);
+ else
+ memset(p, 0, ee_field->size);
+
+ continue;
+ }
+
+ /* copy it to the eeprom signature buf */
+ len = strlen(board_name);
+ if (len > ee_field->size)
+ len = ee_field->size;
+
+ /* copy and zero out rest */
+ memcpy(p, board_name, len);
+ if (len < ee_field->size)
+ memset(p + len, 0, ee_field->size - len);
+ }
+
+ printk(KERN_INFO "calling cape_bus_scan_one_slot\n");
+ err = cape_bus_scan_one_slot(&bus->cape_bus, &slot->cape_slot);
+
+ printk(KERN_INFO "cape_bus_scan_one_slot returned %d\n", err);
+
+ /* failed to scan... */
+ if (err != 0)
+ slot->eeprom_override = 0;
+
+ kfree(board_name);
+
+ return strlen(buf);
+}
+
+static DEVICE_ATTR(slots, 0644, slots_show, slots_store);
+
+static int bone_capebus_bus_sysfs_register(struct bone_capebus_bus *bus)
+{
+ return device_create_file(bus->dev, &dev_attr_slots);
+}
+
+static void bone_capebus_bus_sysfs_unregister(struct bone_capebus_bus *bus)
+{
+ device_remove_file(bus->dev, &dev_attr_slots);
+}
+
+static int __devinit
+bone_capebus_probe(struct platform_device *pdev)
+{
+ struct bone_capebus_bus *bus;
+ struct device_node *pnode = pdev->dev.of_node;
+ const struct of_device_id *cntrlboard_match;
+ const struct of_device_id *dev_match;
+ struct bone_capebus_slot *slot;
+ const struct bone_capebus_eeprom_field *ee_field;
+ struct property *prop;
+ int length;
+ int r;
+ struct device_node *node;
+ struct i2c_client *client;
+ phandle handle;
+ u32 *slot_handles = NULL;
+ u32 val;
+ const char *str;
+ u8 *p;
+ int i, len;
+
+ /* we don't use platform_data */
+
+ bus = devm_kzalloc(&pdev->dev,
+ sizeof(struct bone_capebus_bus), GFP_KERNEL);
+ if (!bus) {
+ dev_err(&pdev->dev, "Failed to allocate device structure\n");
+ return -ENOMEM;
+ }
+
+ /* register the cape bus */
+ r = cape_bus_register(&bus->cape_bus, "bone", 0, &pdev->dev,
+ &bone_capebus_ops);
+ if (r != 0) {
+ dev_err(&pdev->dev, "Failed to register the cape device\n");
+ return r;
+ }
+
+ cntrlboard_match = of_match_device(of_match_ptr(bone_capebus_of_match),
+ &pdev->dev);
+ if (!cntrlboard_match) {
+ dev_err(&pdev->dev, "Failed to configure bone capebus\n");
+ return -ENODEV;
+ }
+ bus->dev = &pdev->dev;
+
+ prop = of_find_property(pnode, "slots", &length);
+ if (prop == NULL) {
+ dev_err(&pdev->dev, "Unable to find required "
+ "property 'slots'\n");
+ return -EINVAL;
+ }
+ bus->slots_nr = length / sizeof(u32);
+ bus->slots = devm_kzalloc(&pdev->dev,
+ sizeof(bus->slots[0]) * bus->slots_nr, GFP_KERNEL);
+ if (!bus->slots) {
+ dev_err(&pdev->dev, "Failed to allocate %d slot areas\n",
+ bus->slots_nr);
+ return -ENOMEM;
+ }
+ slot_handles = devm_kzalloc(&pdev->dev, length, GFP_KERNEL);
+ if (!slot_handles) {
+ dev_err(&pdev->dev, "Failed to allocate %d slot areas\n",
+ bus->slots_nr);
+ return -ENOMEM;
+ }
+ r = of_property_read_u32_array(pnode, "slots",
+ slot_handles, bus->slots_nr);
+ if (r < 0) {
+ dev_err(&pdev->dev, "Failed to read %d slot handles\n",
+ bus->slots_nr);
+ return r;
+ }
+
+ /* now we iterate over any overrides */
+ for_each_child_of_node(pnode, node) {
+
+ dev_match = of_match_node(slot_override_of_match, node);
+ if (!dev_match)
+ continue;
+
+ /* no reg property */
+ if (of_property_read_u32(node, "slot", &val) != 0) {
+ dev_warn(&pdev->dev, "override: Failed to read "
+ "slot property\n");
+ continue;
+ }
+
+ if (val >= bus->slots_nr) {
+ dev_warn(&pdev->dev, "override: invalid slot #%u\n",
+ val);
+ continue;
+ }
+
+ slot = &bus->slots[val];
+
+ if (slot->eeprom_override) {
+ dev_warn(&pdev->dev, "override: slot #%u is already "
+ "overriden\n", val);
+ continue;
+ }
+
+ slot->eeprom_override = 1;
+
+ /* zero out signature */
+ memset(slot->eeprom_signature, 0,
+ sizeof(slot->eeprom_signature));
+
+ /* for any matching field assign them */
+ for (i = 0; i < ARRAY_SIZE(eeprom_fields); i++) {
+
+ ee_field = &eeprom_fields[i];
+
+ /* point to the entry */
+ p = slot->eeprom_signature + ee_field->start;
+
+ /* if no such property, assign default */
+ if (of_property_read_string(node, ee_field->name,
+ &str) != 0) {
+
+ if (ee_field->override)
+ memcpy(p, ee_field->override,
+ ee_field->size);
+ else
+ memset(p, 0, ee_field->size);
+
+ continue;
+ }
+
+ /* copy it to the eeprom signature buf */
+ len = strlen(str);
+ if (len > ee_field->size)
+ len = ee_field->size;
+
+ /* copy and zero out rest */
+ memcpy(p, str, len);
+ if (len < ee_field->size)
+ memset(p + len, 0, ee_field->size - len);
+ }
+ }
+
+ platform_set_drvdata(pdev, bus);
+
+ /* now find the i2c clients */
+ for (i = 0; i < bus->slots_nr; i++) {
+
+ slot = &bus->slots[i];
+
+ handle = slot_handles[i];
+ node = of_find_node_by_phandle(handle);
+ if (node == NULL) {
+ dev_warn(&pdev->dev, "Failed to find node with phandle "
+ "0x%x (#%d)\n", handle, i);
+ continue;
+ }
+ dev_dbg(&pdev->dev, "Found device node for phandle "
+ "0x%x (#%d)\n", handle, i);
+
+ client = of_find_i2c_device_by_node(node);
+ if (client == NULL) {
+ dev_warn(&pdev->dev, "Invalid I2C client node with "
+ "phandle 0x%x (#%d)\n", handle, i);
+ continue;
+ }
+
+ slot->client = i2c_use_client(client);
+ /* no use for this anymore */
+ of_node_put(node);
+
+ /* save handle */
+ client = slot->client; /* get again */
+ slot->eeprom_addr = client->addr;
+ dev_dbg(&pdev->dev, "Found i2c_client at #%d "
+ "(address = 0x%02x)\n",
+ i, slot->eeprom_addr);
+
+ r = cape_bus_register_slot(&bus->cape_bus, &slot->cape_slot, i);
+ if (r != 0) {
+ dev_err(&pdev->dev, "Failed to register slot #%d\n", i);
+ continue;
+ }
+
+ dev_info(&pdev->dev, "Registered slot #%d OK\n", i);
+ }
+
+ /* we don't need the handles anymore */
+ devm_kfree(&pdev->dev, slot_handles);
+ slot_handles = NULL;
+
+ r = bone_capebus_register_pdev_adapters(bus);
+ if (r != 0) {
+ dev_err(&pdev->dev, "Failed to register the pdev adapters\n");
+ goto err_no_pdevs;
+ }
+
+ pm_runtime_enable(bus->dev);
+ r = pm_runtime_get_sync(bus->dev);
+ if (IS_ERR_VALUE(r)) {
+ dev_err(&pdev->dev, "Failed to pm_runtime_get_sync()\n");
+ goto err_exit;
+ }
+
+ pm_runtime_put(bus->dev);
+
+ bone_capebus_bus_sysfs_register(bus);
+
+ dev_info(&pdev->dev, "initialized OK.\n");
+
+ return 0;
+
+err_exit:
+ bone_capebus_unregister_pdev_adapters(bus);
+err_no_pdevs:
+ platform_set_drvdata(pdev, NULL);
+
+ return r;
+}
+
+static int __devexit bone_capebus_remove(struct platform_device *pdev)
+{
+ struct bone_capebus_bus *bus = platform_get_drvdata(pdev);
+ int ret;
+
+ bone_capebus_bus_sysfs_unregister(bus);
+ bone_capebus_unregister_pdev_adapters(bus);
+
+ platform_set_drvdata(pdev, NULL);
+
+ ret = pm_runtime_get_sync(&pdev->dev);
+ if (IS_ERR_VALUE(ret))
+ return ret;
+
+ pm_runtime_put(&pdev->dev);
+ pm_runtime_disable(&pdev->dev);
+
+ return 0;
+}
+
+#ifdef CONFIG_PM
+#ifdef CONFIG_PM_RUNTIME
+static int bone_capebus_runtime_suspend(struct device *dev)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct bone_capebus_bus *_dev = platform_get_drvdata(pdev);
+
+ (void)_dev;
+ return 0;
+}
+
+static int bone_capebus_runtime_resume(struct device *dev)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct bone_capebus_bus *_dev = platform_get_drvdata(pdev);
+
+ (void)_dev;
+ return 0;
+}
+#endif /* CONFIG_PM_RUNTIME */
+
+static struct dev_pm_ops bone_capebus_pm_ops = {
+ SET_RUNTIME_PM_OPS(bone_capebus_runtime_suspend,
+ bone_capebus_runtime_resume, NULL)
+};
+#define BONE_CAPEBUS_PM_OPS (&bone_capebus_pm_ops)
+#else
+#define BONE_CAPEBUS_PM_OPS NULL
+#endif /* CONFIG_PM */
+
+static struct platform_driver bone_capebus_driver = {
+ .probe = bone_capebus_probe,
+ .remove = __devexit_p(bone_capebus_remove),
+ .driver = {
+ .name = "bone-capebus",
+ .owner = THIS_MODULE,
+ .pm = BONE_CAPEBUS_PM_OPS,
+ .of_match_table = of_match_ptr(bone_capebus_of_match),
+ },
+};
+
+module_platform_driver(bone_capebus_driver);
+
+MODULE_AUTHOR("Pantelis Antoniou");
+MODULE_DESCRIPTION("Beaglebone cape bus controller");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:capebus_bone");
diff --git a/include/linux/capebus/capebus-bone.h b/include/linux/capebus/capebus-bone.h
new file mode 100644
index 0000000..e394304
--- /dev/null
+++ b/include/linux/capebus/capebus-bone.h
@@ -0,0 +1,120 @@
+/*
+ * capebus-bone.h
+ *
+ * Cape bus defines and function prototypes for the beaglebone
+ *
+ * Copyright (C) 2012 Pantelis Antoniou <[email protected]>
+ * 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_BONE_H
+#define LINUX_CAPEBUS_BONE_H
+
+#include <linux/list.h>
+#include <linux/capebus.h>
+
+struct bone_capebus_slot {
+ struct cape_slot cape_slot;
+ u32 slot_handle;
+ int eeprom_addr;
+ struct i2c_client *client;
+ unsigned int eeprom_probed : 1;
+ unsigned int eeprom_failed : 1;
+ unsigned int eeprom_override : 1;
+ struct cape_device_id id;
+ char text_id[256];
+ char eeprom_signature[256];
+};
+
+#define to_bone_capebus_slot(n) \
+ container_of(n, struct bone_capebus_slot, cape_slot)
+
+struct bone_capebus_bus {
+ struct cape_bus cape_bus;
+ struct device *dev; /* pdev->dev */
+ int slots_nr;
+ struct bone_capebus_slot *slots;
+};
+
+#define to_bone_capebus_bus(n) \
+ container_of(n, struct bone_capebus_bus, cape_bus)
+
+#define BONE_CAPEBUS_HEADER 0
+#define BONE_CAPEBUS_EEPROM_REV 1
+#define BONE_CAPEBUS_BOARD_NAME 2
+#define BONE_CAPEBUS_VERSION 3
+#define BONE_CAPEBUS_MANUFACTURER 4
+#define BONE_CAPEBUS_PART_NUMBER 5
+#define BONE_CAPEBUS_NUMBER_OF_PINS 6
+#define BONE_CAPEBUS_SERIAL_NUMBER 7
+#define BONE_CAPEBUS_PIN_USAGE 8
+#define BONE_CAPEBUS_VDD_3V3EXP 9
+#define BONE_CAPEBUS_VDD_5V 10
+#define BONE_CAPEBUS_SYS_5V 11
+#define BONE_CAPEBUS_DC_SUPPLIED 12
+#define BONE_CAPEBUS_FIELDS_NR 13
+
+#define BONE_CAPEBUS_MAKE_HEADER(p) \
+ ({ \
+ const u8 *_p = (p); \
+ (((u32)_p[0] << 24) | ((u32)_p[1] << 16) | \
+ ((u32)_p[2] << 8) | (u32)_p[3] ); \
+ })
+
+#define BONE_CAPEBUS_HEADER_VALID 0xaa5533ee
+
+char *bone_capebus_id_get_field(const struct cape_device_id *id,
+ int field, char *buf, int bufsz);
+
+int bone_capebus_match_cntrlboard(const struct cape_device_id *id);
+
+int bone_capebus_match_board(const struct cape_device_id *id,
+ const char **board_names);
+
+/* in pdevs */
+int bone_capebus_register_pdev_adapters(struct bone_capebus_bus *bus);
+void bone_capebus_unregister_pdev_adapters(struct bone_capebus_bus *bus);
+
+/* generic cape support */
+
+struct bone_capebus_generic_device_data {
+ const char *name;
+ const struct of_device_id *of_match;
+ unsigned int units;
+};
+
+struct bone_capebus_generic_device_entry {
+ struct list_head node;
+ const struct bone_capebus_generic_device_data *data;
+ struct platform_device *pdev;
+};
+
+struct bone_capebus_generic_info {
+ struct cape_dev *dev;
+ struct list_head pdev_list;
+};
+
+int bone_capebus_probe_prolog(struct cape_dev *dev,
+ const struct cape_device_id *id);
+
+struct bone_capebus_generic_info *
+bone_capebus_probe_generic(struct cape_dev *dev,
+ const struct cape_device_id *id);
+
+void bone_capebus_remove_generic(
+ struct bone_capebus_generic_info *info);
+
+#endif
--
1.7.12
Introducing beaglebone generic cape support.
With this you can create almost any kind of cape driver
that doesn't require complex interconnection of the parts.
Most beaglebone capes can be created with this, including
all the display capes (DVI/VGA/LCD) with touchscreen or not,
capes that only use i2c or spi devices, gpio-keys, leds etc.
Signed-off-by: Pantelis Antoniou <[email protected]>
---
drivers/capebus/capes/Kconfig | 6 ++
drivers/capebus/capes/Makefile | 1 +
drivers/capebus/capes/bone-generic-cape.c | 96 +++++++++++++++++++++++++++++++
3 files changed, 103 insertions(+)
create mode 100644 drivers/capebus/capes/Kconfig
create mode 100644 drivers/capebus/capes/Makefile
create mode 100644 drivers/capebus/capes/bone-generic-cape.c
diff --git a/drivers/capebus/capes/Kconfig b/drivers/capebus/capes/Kconfig
new file mode 100644
index 0000000..bfe54a6
--- /dev/null
+++ b/drivers/capebus/capes/Kconfig
@@ -0,0 +1,6 @@
+config CAPEBUS_BONE_GENERIC
+ tristate "Beaglebone Generic cape driver"
+ depends on CAPEBUS_BONE_CONTROLLER
+ default n
+ help
+ "Select this to enable a generic cape driver; LCD/DVI capes etc"
diff --git a/drivers/capebus/capes/Makefile b/drivers/capebus/capes/Makefile
new file mode 100644
index 0000000..83da381
--- /dev/null
+++ b/drivers/capebus/capes/Makefile
@@ -0,0 +1 @@
+obj-$(CONFIG_CAPEBUS_BONE_GENERIC) += bone-generic-cape.o
diff --git a/drivers/capebus/capes/bone-generic-cape.c b/drivers/capebus/capes/bone-generic-cape.c
new file mode 100644
index 0000000..70be50a
--- /dev/null
+++ b/drivers/capebus/capes/bone-generic-cape.c
@@ -0,0 +1,96 @@
+/*
+ * Generic cape support
+ *
+ * Copyright (C) 2012 Pantelis Antoniou <[email protected]>
+ * 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/kernel.h>
+#include <linux/string.h>
+#include <linux/timer.h>
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/bitops.h>
+#include <linux/err.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/of_gpio.h>
+#include <linux/pinctrl/pinctrl.h>
+#include <linux/pinctrl/pinmux.h>
+#include <linux/pinctrl/consumer.h>
+#include <linux/atomic.h>
+#include <linux/clk.h>
+#include <asm/barrier.h>
+#include <plat/clock.h>
+#include <plat/omap_device.h>
+#include <linux/clkdev.h>
+#include <linux/input/ti_am335x_tsc.h>
+#include <linux/platform_data/ti_am335x_adc.h>
+#include <linux/mfd/ti_am335x_tscadc.h>
+
+#include <linux/capebus/capebus-bone.h>
+
+/* fwd decl. */
+extern struct cape_driver bonegeneric_driver;
+
+static const struct of_device_id bonegeneric_of_match[] = {
+ {
+ .compatible = "bone-generic-cape",
+ }, { },
+};
+MODULE_DEVICE_TABLE(of, bonegeneric_of_match);
+
+static int bonegeneric_probe(struct cape_dev *dev,
+ const struct cape_device_id *id)
+{
+ struct bone_capebus_generic_info *ginfo;
+ int err;
+
+ err = bone_capebus_probe_prolog(dev, id);
+ if (err != 0)
+ return err;
+
+ ginfo = bone_capebus_probe_generic(dev, id);
+ if (IS_ERR_OR_NULL(ginfo))
+ return IS_ERR(ginfo) ? PTR_ERR(ginfo) : -ENODEV;
+ dev->drv_priv = ginfo;
+ return 0;
+}
+
+static void bonegeneric_remove(struct cape_dev *dev)
+{
+ bone_capebus_remove_generic(dev->drv_priv);
+}
+
+struct cape_driver bonegeneric_driver = {
+ .driver = {
+ .name = "bonegeneric",
+ .owner = THIS_MODULE,
+ .of_match_table = bonegeneric_of_match,
+ },
+ .probe = bonegeneric_probe,
+ .remove = bonegeneric_remove,
+};
+
+module_capebus_driver(bonegeneric_driver);
+
+MODULE_AUTHOR("Pantelis Antoniou");
+MODULE_DESCRIPTION("Beaglebone generic cape");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:bone-generic-cape");
--
1.7.12
* Pantelis Antoniou <[email protected]> [121030 12:00]:
> +#include <plat/clock.h>
> +#include <plat/omap_device.h>
We already have queued patches to make omap_device.h
private to arch/arm/mach-omap2. Then plat/clock.h will
be gone with the common clock framework patches.
Regards,
Tony
* Pantelis Antoniou <[email protected]> [121030 12:00]:
> +
> + priv->lcdc_oh = omap_hwmod_lookup("lcdc");
> + if (priv->lcdc_oh == NULL) {
> + dev_err(&pdev->dev, "Failed to lookup omap_hwmod lcdc\n");
> + return -ENODEV;
> + }
> +
> + priv->lcdc_pdev = omap_device_build("da8xx_lcdc", 0, priv->lcdc_oh,
> + &priv->lcd_pdata,
> + sizeof(struct da8xx_lcdc_platform_data),
> + NULL, 0, 0);
> + if (priv->lcdc_pdev == NULL) {
> + dev_err(&pdev->dev, "Failed to build LCDC device\n");
> + return -ENODEV;
> + }
..and these kind of things need to become private to
arch/arm/mach-omap2, we already have it working for other
devices with device tree.
Regards,
Tony
On Oct 30, 2012, at 9:39 PM, Tony Lindgren wrote:
> * Pantelis Antoniou <[email protected]> [121030 12:00]:
>> +
>> + priv->lcdc_oh = omap_hwmod_lookup("lcdc");
>> + if (priv->lcdc_oh == NULL) {
>> + dev_err(&pdev->dev, "Failed to lookup omap_hwmod lcdc\n");
>> + return -ENODEV;
>> + }
>> +
>> + priv->lcdc_pdev = omap_device_build("da8xx_lcdc", 0, priv->lcdc_oh,
>> + &priv->lcd_pdata,
>> + sizeof(struct da8xx_lcdc_platform_data),
>> + NULL, 0, 0);
>> + if (priv->lcdc_pdev == NULL) {
>> + dev_err(&pdev->dev, "Failed to build LCDC device\n");
>> + return -ENODEV;
>> + }
>
> ..and these kind of things need to become private to
> arch/arm/mach-omap2, we already have it working for other
> devices with device tree.
>
> Regards,
>
> Tony
I see,
I know that if the device driver is DTified it will pick up the hwmod automatically.
The issue is that the driver is question is not yet; how would I go about
creating the platform device and having it pick up the hwmod automatically?
Regards
-- Pantelis
* Pantelis Antoniou <[email protected]> [121030 13:18]:
> On Oct 30, 2012, at 9:39 PM, Tony Lindgren wrote:
>
> > * Pantelis Antoniou <[email protected]> [121030 12:00]:
> >> +
> >> + priv->lcdc_oh = omap_hwmod_lookup("lcdc");
> >> + if (priv->lcdc_oh == NULL) {
> >> + dev_err(&pdev->dev, "Failed to lookup omap_hwmod lcdc\n");
> >> + return -ENODEV;
> >> + }
> >> +
> >> + priv->lcdc_pdev = omap_device_build("da8xx_lcdc", 0, priv->lcdc_oh,
> >> + &priv->lcd_pdata,
> >> + sizeof(struct da8xx_lcdc_platform_data),
> >> + NULL, 0, 0);
> >> + if (priv->lcdc_pdev == NULL) {
> >> + dev_err(&pdev->dev, "Failed to build LCDC device\n");
> >> + return -ENODEV;
> >> + }
> >
> > ..and these kind of things need to become private to
> > arch/arm/mach-omap2, we already have it working for other
> > devices with device tree.
> >
> > Regards,
> >
> > Tony
>
> I see,
>
> I know that if the device driver is DTified it will pick up the hwmod automatically.
> The issue is that the driver is question is not yet; how would I go about
> creating the platform device and having it pick up the hwmod automatically?
Maybe you should make this DT only driver for omap? We already
have am33xx DT only to start with.
If you really need the platform device still, just take a look
at the many platform device creating files under arch/arm/mach-omap2.
Regards,
Tony
On Wed, Oct 31, 2012 at 9:52 AM, Pantelis Antoniou
<[email protected]> 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 <[email protected]>
> ---
> 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 <[email protected]>
> + * 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 <[email protected]>
> + * 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 <[email protected]>
> + * 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 <[email protected]>
> + * 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 [email protected]
> More majordomo info at http://vger.kernel.org/majordomo-info.html
On Wed, Oct 31, 2012 at 9:52 AM, Pantelis Antoniou
<[email protected]> wrote:
> Capebus is created to address the problem of many SoCs that can provide a
> multitude of hardware interfaces but in order to keep costs down the main
> boards only support a limited number of them. The rest are typically brought
> out to pin connectors on to which other boards, named capes are connected and
> allow those peripherals to be used.
>
> These capes connect to the SoC interfaces but might also contain various other
> parts that may need some kind of driver to work.
>
> Since SoCs have limited pins and pin muxing options, not all capes can work
> together so some kind of resource tracking (at least for the pins in use) is
> required.
>
> Before capebus all of this took place in the board support file, and frankly
> for boards with too many capes it was becoming unmanageable.
>
> Capebus provides a virtual bus, which along with a board specific controller,
> cape drivers can be written using the standard Linux device model.
>
> The core capebus infrastructure is not depended on any specific board.
> However capebus needs a board controller to provide services to the cape devices
> it controls. Services like addressing and resource reservation are provided
> by the board controller.
>
> Capebus at the moment only support TI's Beaglebone platform.
>
> This RFC introduces the core concept; most supporting patches
> have been posted to the relevant places.
There are quite a few TODOs in the code, any chance you could
summarize them in the next header email?
Hi Russ,
On Oct 31, 2012, at 11:56 PM, Russ Dill wrote:
> On Wed, Oct 31, 2012 at 9:52 AM, Pantelis Antoniou
> <[email protected]> wrote:
>> Capebus is created to address the problem of many SoCs that can provide a
>> multitude of hardware interfaces but in order to keep costs down the main
>> boards only support a limited number of them. The rest are typically brought
>> out to pin connectors on to which other boards, named capes are connected and
>> allow those peripherals to be used.
>>
>> These capes connect to the SoC interfaces but might also contain various other
>> parts that may need some kind of driver to work.
>>
>> Since SoCs have limited pins and pin muxing options, not all capes can work
>> together so some kind of resource tracking (at least for the pins in use) is
>> required.
>>
>> Before capebus all of this took place in the board support file, and frankly
>> for boards with too many capes it was becoming unmanageable.
>>
>> Capebus provides a virtual bus, which along with a board specific controller,
>> cape drivers can be written using the standard Linux device model.
>>
>> The core capebus infrastructure is not depended on any specific board.
>> However capebus needs a board controller to provide services to the cape devices
>> it controls. Services like addressing and resource reservation are provided
>> by the board controller.
>>
>> Capebus at the moment only support TI's Beaglebone platform.
>>
>> This RFC introduces the core concept; most supporting patches
>> have been posted to the relevant places.
>
> There are quite a few TODOs in the code, any chance you could
> summarize them in the next header email?
Yes,
Most of them deal with dealing properly with removal of the board driver and the core
capebus stuff. And of course PM is not yet handled properly.
After I get on with this part of the review, I plan to fill in the docs and
flesh out the TODOs more.
Regards
-- Pantelis
On Oct 31, 2012, at 11:55 PM, Russ Dill wrote:
> On Wed, Oct 31, 2012 at 9:52 AM, Pantelis Antoniou
> <[email protected]> wrote:
>> Introducing capebus; a bus that allows small boards (capes) to connect
>> to a complex SoC using simple expansion connectors.
>>
[snip]
>> + 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?
Yes, there is a corner case here.
There is the case where while the device is created there is no matching
driver yet. Either that's the case of a not supported cape, or the
cape driver hasn't been loaded yet.
We do need the device to be created, so that the user can browse in the
sysfs it's eeprom attributes.
There's some further complications with runtime cape overrides, but
that's the gist of it.
>> --
>> 1.7.12
>>
>> --
>> To unsubscribe from this list: send the line "unsubscribe linux-omap" in
>> the body of a message to [email protected]
>> More majordomo info at http://vger.kernel.org/majordomo-info.html
Regards
-- Pantelis
On Wed, Oct 31, 2012 at 3:07 PM, Pantelis Antoniou
<[email protected]> wrote:
>
> On Oct 31, 2012, at 11:55 PM, Russ Dill wrote:
>
>> On Wed, Oct 31, 2012 at 9:52 AM, Pantelis Antoniou
>> <[email protected]> wrote:
>>> Introducing capebus; a bus that allows small boards (capes) to connect
>>> to a complex SoC using simple expansion connectors.
>>>
>
> [snip]
>>> + 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?
>
>
> Yes, there is a corner case here.
>
> There is the case where while the device is created there is no matching
> driver yet. Either that's the case of a not supported cape, or the
> cape driver hasn't been loaded yet.
>
> We do need the device to be created, so that the user can browse in the
> sysfs it's eeprom attributes.
>
> There's some further complications with runtime cape overrides, but
> that's the gist of it.
I'm trying to figure out how that would come about, here is where
added is set to 1:
+ /* all is fine... */
+ cape_dev->driver = drv;
+ cape_dev->added = 1;
This is after calling drv->probe, so drv is not null.
There is a brief time here where added is 0, but driver is not.
+ 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;
+ }
+ if (drv->remove) {
+ pm_runtime_get_sync(dev);
+ drv->remove(cape_dev);
+ pm_runtime_put_noidle(dev);
+ }
+ cape_dev->driver = NULL;
Is one of the remove or resume functions check added in this case?