summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBraden Kell <bradenkell@google.com>2017-05-24 21:17:54 +0000
committerandroid-build-merger <android-build-merger@google.com>2017-05-24 21:17:54 +0000
commitbf05b0b5cec5a6366283351051b9a9c4a282728c (patch)
tree166bfb3a8688097c31e7b9207a86db9029446979
parent6ca08085601903f10bd23fdc4e398fee317f7331 (diff)
parent0a4b801469aa372dfaf4696262cd7d48779b3aa7 (diff)
downloadpi-v4.4-bf05b0b5cec5a6366283351051b9a9c4a282728c.tar.gz
Merge "pio: Initial commit of runtime pin config code" into nyc-iot-dev
am: 0a4b801469 Change-Id: I9380956ddd58e9890081e9bc1782db4c86df1630
-rw-r--r--drivers/pinctrl/android-things/devices.c815
-rw-r--r--drivers/pinctrl/android-things/devicetree.c305
-rw-r--r--drivers/pinctrl/android-things/main.c103
-rw-r--r--drivers/pinctrl/android-things/platform_devices.c174
-rw-r--r--drivers/pinctrl/android-things/platform_devices.h123
-rw-r--r--drivers/pinctrl/android-things/runtimepinconfig.h79
-rw-r--r--drivers/pinctrl/android-things/sysfs.c102
7 files changed, 1701 insertions, 0 deletions
diff --git a/drivers/pinctrl/android-things/devices.c b/drivers/pinctrl/android-things/devices.c
new file mode 100644
index 000000000000..2c5fa8bcf99c
--- /dev/null
+++ b/drivers/pinctrl/android-things/devices.c
@@ -0,0 +1,815 @@
+/*
+ * devices.c
+ *
+ * Runtime pin configuration for Raspberry Pi
+ *
+ * Copyright (C) 2017 Google, Inc.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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.
+ */
+
+#include <linux/amba/bus.h>
+#include <linux/device.h>
+#include <linux/kdev_t.h>
+#include <linux/mutex.h>
+#include <linux/of.h>
+#include <linux/of_platform.h>
+#include <linux/slab.h>
+
+#include "runtimepinconfig.h"
+#include "platform_devices.h"
+
+DEFINE_MUTEX(sysfs_mutex);
+static struct pin_device **pin_array;
+
+static inline struct pin_device *get_pin_device_by_pin(u32 pin)
+{
+ if (!pin_array || pin >= pin_count)
+ return NULL;
+
+ return pin_array[pin];
+}
+
+/*
+ * Call a function for each pin in a device tree node. Look up the pin_device
+ * represnting the pin and pass that to the function. Don't call the function if
+ * no pin_device is found for a pin.
+ */
+static int device_for_each_pin(struct device_node *node,
+ int (*fn)(struct pin_device *pin))
+{
+ struct device_node *state;
+ struct pin_device *pin_dev;
+ int i = 0;
+ u32 j;
+ u32 pin;
+ int ret = 0;
+
+ while ((state = of_parse_phandle(node, STATE_0, i++))) {
+ j = 0;
+
+ while (!of_property_read_u32_index(state, PROP_PINS, j++,
+ &pin)) {
+ if (!(pin_dev = get_pin_device_by_pin(pin)))
+ /*
+ * This is normal for devices such as uart0,
+ * which has a third non-user pin in its device
+ * tree entry.
+ */
+ continue;
+
+ if ((ret = fn(pin_dev)))
+ break;
+ }
+
+ of_node_put(state);
+
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+
+/*
+ * track_pin_device() - Create a pin_device for a platform_device that has just
+ * been registered, or update it if one already exists. This function should be
+ * called from the driver's probe function.
+ *
+ * @dev: The platform_device for this pin.
+ * @class: The class to use when creating sysfs files for this device. For
+ * example, using the pinctrl class causes files to be created in
+ * /sys/class/pinctrl.
+ *
+ * Returns the new (or updated) pin_device on success, NULL on failure.
+ */
+struct pin_device *track_pin_device(struct platform_device *dev,
+ struct class *class)
+{
+ const char *name;
+ struct device *chardev;
+ struct pin_device *pin_dev;
+ int ret;
+ u32 pin;
+
+ if (!pin_array) {
+ pr_err(TAG "pin device array uninitialized\n");
+ return NULL;
+ }
+
+ if (of_property_read_string(dev->dev.of_node, "name", &name)) {
+ dev_err(&dev->dev, "no name property found\n");
+ return NULL;
+ }
+
+ if ((ret = get_pin(dev->dev.of_node, &pin))) {
+ dev_err(&dev->dev, "no pin property found\n");
+ return NULL;
+ }
+
+ if (pin >= pin_count) {
+ dev_err(&dev->dev, "pin property %d out of range %d\n", pin,
+ pin_count);
+ return NULL;
+ }
+
+ /* This pin device may be returning after having been unregistered. */
+ if ((pin_dev = get_pin_device_by_pin(pin))) {
+ dev_dbg(&dev->dev, "tracking existing device\n");
+
+ if (pin_dev->device != NULL)
+ dev_warn(&dev->dev, "device is being re-added to pin device %d\n",
+ pin);
+
+ pin_dev->set_gpio = 0;
+ pin_dev->device = dev;
+
+ return pin_dev;
+ }
+
+ if (!(pin_dev = kmalloc(sizeof(*pin_dev), GFP_KERNEL))) {
+ pr_err(TAG "kmalloc failed %s:%d\n", __FILE__, __LINE__);
+ return NULL;
+ }
+
+ chardev = device_create(class, NULL, MKDEV(0, 0), pin_dev, "%s", name);
+ if (IS_ERR(chardev)) {
+ dev_err(&dev->dev, "unable to create char device");
+ kfree(pin_dev);
+ return NULL;
+ }
+
+ pin_dev->pin = pin;
+ pin_dev->set_gpio = 0;
+ pin_dev->device = dev;
+ pin_dev->of_node = of_node_get(dev->dev.of_node);
+ pin_dev->char_device = chardev;
+ pin_array[pin] = pin_dev;
+
+ dev_dbg(&dev->dev, "tracking new device\n");
+
+ return pin_dev;
+}
+
+/*
+ * The pin device has been unregistered, but we still want to keep most of its
+ * data around in case it needs to be registered again. This function should be
+ * called from the driver's remove function.
+ */
+void untrack_pin_device(struct pin_device *dev)
+{
+ dev->device = NULL;
+}
+
+/*
+ * Unregister a generic device. Use the device's bus to determine its type, and
+ * call the unregister function for that device type. Perform additional cleanup
+ * for AMBA devices.
+ */
+static void unregister_device(struct device *dev)
+{
+ struct amba_device *adev;
+ int ret;
+
+ if (!dev) {
+ return;
+ } else if (dev->bus == &platform_bus_type) {
+ platform_device_unregister(to_platform_device(dev));
+ } else if (dev->bus == &amba_bustype) {
+ adev = to_amba_device(dev);
+
+ /*
+ * For whatever reason, the AMBA driver doesn't call
+ * release_resource (or amba_device_release doesn't get
+ * called). Call it here so we're able to add the device back
+ * later.
+ */
+ if (adev->res.parent) {
+ if ((ret = release_resource(&adev->res))) {
+ dev_warn(dev, "release_resource failed with %d\n",
+ ret);
+ }
+ }
+
+ amba_device_unregister(adev);
+ } else {
+ dev_warn(dev, "can't unregister with unknown bus type %s\n",
+ dev->bus->name);
+ }
+}
+
+/* Register a device by its device tree node. */
+static int register_device_by_node(struct device_node *node)
+{
+ struct device *parent;
+ int ret;
+
+ /*
+ * Mark the node as unpopulated so it will get registered by
+ * of_platform_popluate.
+ */
+ of_node_clear_flag(node, OF_POPULATED);
+ parent = find_device_by_node(node->parent);
+ if ((ret = of_platform_populate(node->parent, NULL, NULL, parent))) {
+ /*
+ * Something went wrong trying to bring the pin device back. Set
+ * this flag so we don't unintentionally attempt to bring it
+ * back next time we call of_platform_populate.
+ */
+ of_node_set_flag(node, OF_POPULATED);
+ pr_err(TAG "unable to register device for %s\n", node->name);
+ } else {
+ pr_debug(TAG "registered device for %s\n", node->name);
+ }
+
+ put_device(parent);
+
+ return ret;
+}
+
+/*
+ * If always_unreg_aux is set, we must unregister the auxiliary device any time
+ * we unregister the primary device. Otherwise we can leave the auxiliary device
+ * up while changing the pins.
+ */
+static void unregister_device_and_maybe_aux(struct device *dev,
+ struct bcm_device *bdev)
+{
+ struct device *aux_dev;
+
+ if (bdev->always_unreg_aux && bdev->aux_dev.of_node) {
+ aux_dev = find_device_by_node(bdev->aux_dev.of_node);
+ unregister_device(aux_dev);
+ }
+
+ unregister_device(dev);
+}
+
+/*
+ * If always_unreg_aux is set, we must unregister the auxiliary device BEFORE
+ * unregistering the primary device. Otherwise unregister the primary device
+ * first, then the auxiliary device.
+ */
+static void unregister_device_and_aux(struct device *dev,
+ struct bcm_device *bdev)
+{
+ struct device *aux_dev = NULL;
+
+ if (bdev->aux_dev.of_node)
+ aux_dev = find_device_by_node(bdev->aux_dev.of_node);
+
+ if (bdev->always_unreg_aux) {
+ unregister_device(aux_dev);
+ unregister_device(dev);
+ } else {
+ unregister_device(dev);
+ unregister_device(aux_dev);
+ }
+}
+
+/* Register a device, and its auxiliary device of always_unreg_aux is set. */
+static int register_device_and_maybe_aux(struct bcm_device *dev)
+{
+ int ret = register_device_by_node(dev->node.of_node);
+
+ if (ret)
+ return ret;
+
+ if (dev->aux_dev.of_node && dev->always_unreg_aux)
+ return register_device_by_node(dev->aux_dev.of_node);
+
+ return 0;
+}
+
+/* Register a device and its auxiliary device. */
+static int register_device_and_aux(struct bcm_device *dev)
+{
+ int ret;
+
+ if (!dev->aux_dev.of_node)
+ return register_device_by_node(dev->node.of_node);
+
+ if (dev->always_unreg_aux) {
+ if ((ret = register_device_by_node(dev->node.of_node)))
+ return ret;
+ return register_device_by_node(dev->aux_dev.of_node);
+ }
+
+ if ((ret = register_device_by_node(dev->aux_dev.of_node)))
+ return ret;
+ return register_device_by_node(dev->node.of_node);
+
+ return 0;
+}
+
+/* Restore the device's default pin configuration and register it. */
+static inline int register_default_device(struct bcm_device *dev)
+{
+ int ret = set_device_config(dev, NULL);
+
+ if (ret)
+ return ret;
+
+ return register_device_and_aux(dev);
+}
+
+/*
+ * Unregister all peripheral devices we know about and prepare their device tree
+ * properties for our use.
+ */
+int unregister_platform_devices(void)
+{
+ struct device *dev;
+ struct bcm_device *bdev;
+ int ret = 0;
+
+ if (pin_array) {
+ pr_warn(TAG "pin array is already initialized\n");
+ } else {
+ pin_array = kcalloc(pin_count, sizeof(*pin_array),
+ GFP_KERNEL);
+ if (!pin_array) {
+ pr_err(TAG "kmalloc failed %s:%d\n", __FILE__,
+ __LINE__);
+ return -ENOMEM;
+ }
+ }
+
+ for (bdev = platform_devices; bdev->name != NULL; bdev++) {
+ if (!bdev->node.path)
+ continue;
+
+ /* Populate the device and auxiliary device nodes. */
+ bdev->node.of_node = of_find_node_by_path(bdev->node.path);
+ if (!bdev->node.of_node) {
+ pr_warn(TAG "unable to find %s in the device tree\n",
+ bdev->node.path);
+ continue;
+ }
+
+ if (bdev->aux_dev.path)
+ bdev->aux_dev.of_node = of_find_node_by_path(
+ bdev->aux_dev.path);
+
+ /* Unregister the device and auxiliary device. */
+ if (!bdev->use_default) {
+ dev = find_device_by_node(bdev->node.of_node);
+ unregister_device_and_aux(dev, bdev);
+ }
+
+ if ((ret = expand_property(bdev, PROP_PULL, 0)))
+ return ret;
+ if ((ret = expand_property(bdev, PROP_FUNC,
+ bdev->pin_groups[0].function)))
+ return ret;
+
+ if (!bdev->use_default)
+ ret = set_device_config(bdev, &bdev->pin_groups[0]);
+ }
+
+ return ret;
+}
+
+/*
+ * Find the device using this pin. The returned device may be a pin device or
+ * peripheral device. We register pin devices for each pin when a peripheral
+ * device gets unregistered, so returning NULL here should never happen. The
+ * caller must call device_put on the returned device.
+ */
+static struct device *find_active_device_by_pin(u32 pin,
+ struct bcm_device **dev)
+{
+ struct device *pdev;
+ int has_pin;
+ size_t i;
+
+ for (i = 0; platform_devices[i].name != NULL; i++) {
+ if (!platform_devices[i].node.path)
+ continue;
+
+ if (!platform_devices[i].node.of_node) {
+ pr_warn(TAG "%s has no device tree node\n",
+ platform_devices[i].name);
+ continue;
+ }
+
+ pdev = find_device_by_node(platform_devices[i].node.of_node);
+ has_pin = device_has_pin(platform_devices[i].node.of_node, pin);
+ if (pdev && has_pin) {
+ /*
+ * We are assuming only one registered device has this
+ * pin in its pins property.
+ */
+
+ if (dev)
+ *dev = &platform_devices[i];
+
+ return pdev;
+ }
+ }
+
+ return NULL;
+}
+
+static int register_pin_device(struct pin_device *dev)
+{
+ return (!dev->device) ? register_device_by_node(dev->of_node) : 0;
+}
+
+static int set_gpio_flag(struct pin_device *pin)
+{
+ pin->set_gpio = 1;
+ return 0;
+}
+
+static int clear_gpio_flag(struct pin_device *pin)
+{
+ pin->set_gpio = 0;
+ return 0;
+}
+
+/*
+ * Free a pin for use by a device. If this pin is set to GPIO simply unregister
+ * its pin device. If this pin is owned by another device, unregister that
+ * device and set set_gpio for each of its pins. Later we will register a new
+ * pin device for every pin with set_gpio set.
+ */
+static int unregister_pin_for_device(struct pin_device *pin)
+{
+ struct device *otherdev;
+ struct bcm_device *dev;
+
+ if (pin->device) {
+ /* This pin is just owned by a pin device, so unregister it. */
+ platform_device_unregister(pin->device);
+ } else if ((otherdev = find_active_device_by_pin(pin->pin, &dev))) {
+ /*
+ * This pin is owned by a platform device. For each pin owned by
+ * this device, set its set_gpio flag. Later we will clear the
+ * flags of those pins used by the platform device we are
+ * bringing up, and will create pin devices for those that
+ * remain. This prevents having to use a ton of nested loops.
+ */
+ unregister_device_and_aux(otherdev, dev);
+
+ device_for_each_pin(dev->node.of_node, set_gpio_flag);
+
+ if (dev->use_default)
+ return register_default_device(dev);
+ } else {
+ /*
+ * Nothing is using this pin. It was (hopefully) being used by
+ * the platform device we will be bringing back up.
+ */
+ }
+
+ return 0;
+}
+
+/*
+ * Return a pointer to the pin property at the specified index. This pointer
+ * can be used to read or modify the property. A device's pin list may be spread
+ * across several nodes and properties.
+ */
+static __be32 *find_pin_property(struct device_node *node, const char *prop,
+ int index)
+{
+ struct property *pin_prop;
+ struct device_node *state;
+ int i = 0;
+ int j = 0;
+ int length;
+
+ while ((state = of_parse_phandle(node, STATE_0, i++))) {
+ pin_prop = of_find_property(state, prop, &length);
+ of_node_put(state);
+
+ if (!pin_prop) {
+ pr_warn(TAG "no property \"%s\" for %s[%d]\n",
+ prop, node->name, i);
+ continue;
+ }
+
+ length /= sizeof(__be32);
+ if (index >= j && index < j + length)
+ return ((__be32 *)pin_prop->value) + index - j;
+
+ j += length;
+ }
+
+ return NULL;
+}
+
+/*
+ * Set this pin to GPIO. If a device owns this pin, unregister it and register
+ * new pin devices for each of its pins.
+ */
+static inline int set_function_gpio(struct pin_device *dev)
+{
+ struct device *pdev;
+ struct bcm_device *bdev;
+ int ret;
+
+ if (dev->device) {
+ dev_dbg(&dev->device->dev, "already set to gpio\n");
+ return 0;
+ }
+
+ if (!(pdev = find_active_device_by_pin(dev->pin, &bdev))) {
+ /*
+ * Nothing is using this pin, so go ahead and register a pin
+ * device for it. We should never get here.
+ */
+ pr_warn(TAG "no device is using pin %d\n", dev->pin);
+ return register_pin_device(dev);
+ }
+
+ /* Unregister the platform device using this pin. */
+ unregister_device_and_aux(pdev, bdev);
+
+ dev_dbg(pdev, "unregistered to free pin %d\n", dev->pin);
+
+ /*
+ * Register pin devices for pins previously used by the platform
+ * device.
+ */
+ ret = device_for_each_pin(bdev->node.of_node, register_pin_device);
+ if (ret)
+ return ret;
+
+ if (bdev->use_default)
+ return register_default_device(bdev);
+
+ return 0;
+}
+
+/* Replace a pin used by a device and register a pin device for the old pin. */
+static inline int replace_pin(struct bcm_device *bcm_dev,
+ struct pin_group *group, u32 pin)
+{
+ struct pin_device *pin_dev;
+ int ret;
+ __be32 *pin_prop;
+ __be32 *function;
+ u32 pin_index;
+ u32 old_pin;
+
+ pin_index = pin - group->base;
+ pin_prop = find_pin_property(bcm_dev->node.of_node, PROP_PINS,
+ pin_index);
+
+ if (!pin_prop) {
+ pr_err(TAG "unable to find pin index %d in %s\n",
+ pin_index, bcm_dev->name);
+ return -EINVAL;
+ }
+
+ old_pin = be32_to_cpup(pin_prop);
+
+ *pin_prop = cpu_to_be32p(&pin);
+
+ if (!(pin_dev = get_pin_device_by_pin(old_pin))) {
+ /*
+ * This happens when we are bringing up a device that
+ * was previously using non-user pins.
+ */
+ } else if ((ret = register_pin_device(pin_dev))) {
+ return ret;
+ }
+
+ /* Set the pin's function property in the device tree. */
+ function = find_pin_property(bcm_dev->node.of_node, PROP_FUNC,
+ pin_index);
+ if (!function) {
+ pr_err(TAG "unable to find pin function index %d in %s\n",
+ pin_index, bcm_dev->name);
+ return -EINVAL;
+ }
+
+ *function = cpu_to_be32p(&group->function);
+
+ return 0;
+}
+
+/*
+ * Set a pin's function while holding sysfs_lock. If the device for this
+ * function is registered and doesn't own this pin, set the pin we replace to
+ * GPIO. Unregister all devices owning pins that this device needs and set the
+ * now unused pins to GPIO.
+ */
+static inline int __set_function(struct pin_device *dev,
+ struct bcm_device *bcm_dev,
+ struct pin_group *group)
+{
+ struct device *new_dev;
+ struct device *exdev;
+ struct bcm_device *exbdev;
+ struct pin_device *pin_dev;
+ size_t i = 0;
+ int has_pin;
+ int ret = 0;
+
+ if (!bcm_dev->node.path)
+ return set_function_gpio(dev);
+
+ if (!bcm_dev->node.of_node) {
+ pr_err(TAG "%s has no device tree node\n", bcm_dev->name);
+ return -EINVAL;
+ }
+
+ new_dev = find_device_by_node(bcm_dev->node.of_node);
+ has_pin = device_has_pin(bcm_dev->node.of_node, dev->pin);
+ if (new_dev && has_pin) {
+ /* The device already owns this pin and is registered. */
+ dev_dbg(new_dev, "pin %d is already reserved\n", dev->pin);
+ put_device(new_dev);
+ return 0;
+ }
+
+ /* Unregister this device so we can change its pins. */
+ unregister_device_and_maybe_aux(new_dev, bcm_dev);
+
+ if (bcm_dev->use_default) {
+ /*
+ * By default, this device uses non-user pins. Rather than mix
+ * user and non-user pins, set this device to use all user pins
+ * as soon as it is requested.
+ */
+ if ((ret = set_device_config(bcm_dev, group))) {
+ pr_err(TAG "unable to set config for %s\n",
+ bcm_dev->name);
+ return ret;
+ }
+ } else if (!has_pin) {
+ if ((ret = replace_pin(bcm_dev, group, dev->pin)))
+ return ret;
+ }
+
+ /*
+ * Unregister devices using pins we need, and set the set_gpio flag for
+ * each newly freed pin.
+ */
+ if ((ret = device_for_each_pin(bcm_dev->node.of_node,
+ unregister_pin_for_device))) {
+ pr_err(TAG "unable to unregister pin devices needed by %s\n",
+ bcm_dev->name);
+ return ret;
+ }
+
+ /* Unregister each mutually exclusive device. */
+ for (i = 0; bcm_dev->excl && (exbdev = bcm_dev->excl[i]); i++) {
+ if ((exdev = find_device_by_node(exbdev->node.of_node))) {
+ device_for_each_pin(exbdev->node.of_node,
+ set_gpio_flag);
+
+ unregister_device_and_aux(exdev, exbdev);
+
+ if (exbdev->use_default)
+ if ((ret = register_default_device(exbdev)))
+ return ret;
+ }
+ }
+
+ /* Clear the set_gpio flag for every pin we're using. */
+ device_for_each_pin(bcm_dev->node.of_node, clear_gpio_flag);
+
+ /* Register a pin device for each pin we're not using. */
+ for (i = 0, ret = 0; i < pin_count; i++) {
+ if (!(pin_dev = get_pin_device_by_pin(i)))
+ continue;
+
+ if (pin_dev->set_gpio)
+ if ((ret = register_pin_device(pin_dev)))
+ return ret;
+
+ pin_dev->set_gpio = 0;
+ }
+
+ /*
+ * Register this platform device. If the device was not previously
+ * registered, bring the auxiliary device up as well.
+ */
+ if (new_dev)
+ return register_device_and_maybe_aux(bcm_dev);
+ else
+ return register_device_and_aux(bcm_dev);
+}
+
+int set_function(struct pin_device *dev, struct bcm_device *bcm_dev,
+ struct pin_group *group)
+{
+ int ret;
+
+ mutex_lock(&sysfs_mutex);
+ ret = __set_function(dev, bcm_dev, group);
+ mutex_unlock(&sysfs_mutex);
+
+ return ret;
+}
+
+
+/*
+ * Find a matching pin group and return this pin's index in the property
+ * list.
+ */
+static inline u32 get_pin_property_index(struct bcm_device *dev, u32 pin)
+{
+ struct pin_group *groups;
+ size_t i;
+
+ groups = dev->pin_groups;
+ for (i = 0 ; dev->pin_group_count; i++) {
+ if (pin_in_group(pin, groups[i].base, dev->pin_count))
+ return pin - groups[i].base;
+ }
+
+ return -1;
+}
+
+/* Set a GPIO pin's pull-up/pull-down resistor configuration. */
+static inline int set_resistor_gpio(struct pin_device *dev, u32 resistor)
+{
+ __be32 *pull;
+
+ if (!(pull = find_pin_property(dev->of_node, PROP_PULL, 0))) {
+ pr_err(TAG "unable to find resistor index 0 in pin %d\n",
+ dev->pin);
+ return -EINVAL;
+ } else if (*pull == cpu_to_be32p(&resistor)) {
+ pr_debug(TAG "pin %d is already set to resistor %d\n",
+ dev->pin, resistor);
+ return 0;
+ }
+
+ /* Disable the device before changing the resistor. */
+ platform_device_unregister(dev->device);
+
+ *pull = cpu_to_be32p(&resistor);
+
+ return register_pin_device(dev);
+}
+
+/*
+ * Set a pin's pull-up/pull-down resistor configuration while holding
+ * sysfs_lock.
+ */
+static inline int __set_resistor(struct pin_device *dev, u32 resistor)
+{
+ struct device *pdev;
+ struct bcm_device *bdev;
+ int index;
+ __be32 *pull;
+
+ if (dev->device)
+ return set_resistor_gpio(dev, resistor);
+
+ if ((pdev = find_active_device_by_pin(dev->pin, &bdev))) {
+ index = get_pin_property_index(bdev, dev->pin);
+ if (index == -1) {
+ dev_err(pdev, "could not get resistor index for pin %d\n",
+ dev->pin);
+ return -EINVAL;
+ }
+ } else {
+ pr_err(TAG "no device is using pin %d\n", dev->pin);
+ put_device(pdev);
+ return -ENODEV;
+ }
+
+ pull = find_pin_property(bdev->node.of_node, PROP_PULL, index);
+ if (!pull) {
+ dev_err(pdev, "unable to find pin index %d\n", index);
+ put_device(pdev);
+ return -EINVAL;
+ } else if (*pull == cpu_to_be32p(&resistor)) {
+ dev_dbg(pdev, "pin %d is already set to resistor %d\n",
+ dev->pin, resistor);
+ put_device(pdev);
+ return 0;
+ }
+
+ /* Disable the device before changing the resistor. */
+ unregister_device_and_maybe_aux(pdev, bdev);
+
+ *pull = cpu_to_be32p(&resistor);
+
+ return register_device_and_maybe_aux(bdev);
+}
+
+int set_resistor(struct pin_device *dev, u32 resistor)
+{
+ int ret;
+
+ mutex_lock(&sysfs_mutex);
+ ret = __set_resistor(dev, resistor);
+ mutex_unlock(&sysfs_mutex);
+
+ return ret;
+}
diff --git a/drivers/pinctrl/android-things/devicetree.c b/drivers/pinctrl/android-things/devicetree.c
new file mode 100644
index 000000000000..1d75e53bb1d8
--- /dev/null
+++ b/drivers/pinctrl/android-things/devicetree.c
@@ -0,0 +1,305 @@
+/*
+ * devicetree.c
+ *
+ * Runtime pin configuration for Raspberry Pi
+ *
+ * Copyright (C) 2017 Google, Inc.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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.
+ */
+
+#include <linux/amba/bus.h>
+#include <linux/of.h>
+#include <linux/of_platform.h>
+#include <linux/slab.h>
+
+#include "runtimepinconfig.h"
+#include "platform_devices.h"
+
+/*
+ * get_pin() - Get the first pin in a device tree node. Only intended to be
+ * called for pin devices.
+ *
+ * @node: The device tree node to search.
+ * @pin: Set to the pin number if it is found.
+ *
+ * Returns 0 if the pin number is found.
+ */
+int get_pin(struct device_node *node, u32 *pin)
+{
+ struct device_node *statenode;
+ int ret;
+
+ if (!(statenode = of_parse_phandle(node, STATE_0, 0)))
+ return -EINVAL;
+
+ ret = of_property_read_u32(statenode, PROP_PINS, pin);
+ of_node_put(statenode);
+ return ret;
+}
+
+/*
+ * device_has_pin() - Check if a device tree node contains the specified pin.
+ *
+ * @node: The device tree node to search.
+ * @pin: The pin number to look for.
+ *
+ * Returns 1 if the device contains the pin, 0 otherwise.
+ */
+int device_has_pin(struct device_node *node, u32 pin)
+{
+ struct device_node *pinstate;
+ int i = 0;
+ u32 prop_pin;
+ u32 j = 0;
+
+ while ((pinstate = of_parse_phandle(node, STATE_0, i++))) {
+ j = 0;
+
+ while (!of_property_read_u32_index(pinstate, PROP_PINS, j++,
+ &prop_pin)) {
+ if (prop_pin == pin)
+ return 1;
+ }
+
+ of_node_put(pinstate);
+ }
+
+ return 0;
+}
+
+/*
+ * Creates a struct property with name prop and the specified length in
+ * bytes.
+ */
+static inline struct property *create_property(const char *prop, int length)
+{
+ struct property *ret;
+
+ if (!(ret = kzalloc(sizeof(*ret), GFP_KERNEL)))
+ goto err_alloc_struct;
+ if (!(ret->name = kstrdup(prop, GFP_KERNEL)))
+ goto err_alloc_name;
+ if (!(ret->value = kzalloc(length, GFP_KERNEL)))
+ goto err_alloc_value;
+
+ ret->length = length;
+ of_property_set_flag(ret, OF_DYNAMIC);
+ return ret;
+
+err_alloc_value:
+ kfree(ret->name);
+err_alloc_name:
+ kfree(ret);
+err_alloc_struct:
+ pr_err(TAG "kmalloc failed %s:%d\n", __FILE__, __LINE__);
+ return NULL;
+}
+
+static inline void fill_value(__be32 *array, u32 value, int length)
+{
+ int size = length / sizeof(*array);
+ int i;
+
+ for (i = 0; i < size; i++)
+ *array++ = cpu_to_be32p(&value);
+}
+
+/*
+ * expand_property() - Checks a device's pinctrl properties, and expands them to
+ * the proper length if necessary. For example, the resistor property may
+ * specify one value for all pins, one value for each pin, or no value. We want
+ * to have one value for each pin so we can configure pins individually, no
+ * matter which device is using them.
+ *
+ * @dev: The device to check.
+ * @prop_name: The string name of the property to check.
+ * @value: The new value to fill an expanded property with.
+ *
+ * Returns 0 on success.
+ */
+int expand_property(struct bcm_device *dev, const char *prop_name, u32 value)
+{
+ struct of_changeset changeset;
+ struct device_node *prop;
+ struct property *pins, *pull;
+ struct property *new_pull;
+ int pins_length, pull_length;
+ int total_pins = 0;
+ int ret;
+ unsigned long action;
+ int i = 0;
+
+ of_changeset_init(&changeset);
+
+ while ((prop = of_parse_phandle(dev->node.of_node, STATE_0, i++))) {
+ pins = of_find_property(prop, PROP_PINS, &pins_length);
+ pull = of_find_property(prop, prop_name, &pull_length);
+
+ if (!pins) {
+ pr_warn(TAG "%s[%d] has no %s property\n", dev->name,
+ i - 1, PROP_PINS);
+ of_node_put(prop);
+ continue;
+ }
+
+ total_pins += (pins_length / sizeof(value));
+ if (total_pins > dev->pin_count) {
+ pr_warn(TAG "%s stopping at %d pins out of %d\n",
+ dev->name, dev->pin_count, total_pins);
+ of_node_put(prop);
+ break;
+ }
+
+ if (pull && pull_length >= pins_length) {
+ /*
+ * The property already exists and is of the correct
+ * length.
+ */
+ of_node_put(prop);
+ continue;
+ }
+
+ new_pull = create_property(prop_name, pins_length);
+ if (!new_pull)
+ goto err_prop;
+
+ fill_value(new_pull->value, value, pins_length);
+
+ action = (!pull) ? OF_RECONFIG_ADD_PROPERTY
+ : OF_RECONFIG_UPDATE_PROPERTY;
+
+ if (of_changeset_action(&changeset, action, prop, new_pull)) {
+ pr_err(TAG "unable to update %s property for %s[%d]\n",
+ prop_name, dev->name, i - 1);
+ goto err_apply;
+ } else {
+ pr_debug(TAG "updated %s property for %s[%d]\n",
+ prop_name, dev->name, i - 1);
+ }
+
+ of_node_put(prop);
+ }
+
+ if ((ret = of_changeset_apply(&changeset)))
+ pr_err(TAG "unable to apply changeset for %s\n", dev->name);
+
+ of_changeset_destroy(&changeset);
+
+ return ret;
+
+err_apply:
+ kfree(new_pull);
+err_prop:
+ of_changeset_destroy(&changeset);
+ of_node_put(prop);
+ return -ENOMEM;
+}
+
+static int node_match_device(struct device *dev, void *data)
+{
+ return (dev->of_node == data);
+}
+
+/*
+ * find_device_by_node() - Finds a device from it's device tree node. The caller
+ * must call put_device on the returned device.
+ *
+ * Returns the device or NULL if no such device is registered.
+ */
+struct device *find_device_by_node(struct device_node *node)
+{
+ struct platform_device *pdev;
+
+ if ((pdev = of_find_device_by_node(node)))
+ return &pdev->dev;
+ else
+ return bus_find_device(&amba_bustype, NULL, node,
+ node_match_device);
+}
+
+/*
+ * set_device_config() - Sets a device's device tree configuration.
+ *
+ * @dev: The device to change.
+ * @group: The new pin group to set, or NULL for the default.
+ *
+ * Returns 0 on success.
+ */
+int set_device_config(struct bcm_device *dev, struct pin_group *group)
+{
+ struct device_node *prop;
+ struct property *pins, *function, *pull;
+ __be32 *list;
+ __be32 *flist = NULL;
+ __be32 *plist = NULL;
+ u32 pin;
+ int length;
+ int i, j, total;
+
+ if (!group)
+ group = &dev->pin_groups[0];
+
+ i = 0;
+ j = 0;
+ while ((prop = of_parse_phandle(dev->node.of_node, STATE_0, i++))) {
+ pins = of_find_property(prop, PROP_PINS, &length);
+ function = of_find_property(prop, PROP_FUNC, NULL);
+ pull = of_find_property(prop, PROP_PULL, NULL);
+
+ if (!pins || !function || !pull) {
+ pr_warn(TAG "%s[%d] is missing a property\n",
+ dev->name, i - 1);
+ of_node_put(prop);
+ continue;
+ }
+
+ if (function->length != length || pull->length != length) {
+ pr_err(TAG "%s[%d] property size mismatch\n",
+ dev->name, i - 1);
+ goto err_prop;
+ }
+
+ list = pins->value;
+ flist = function->value;
+ plist = pull->value;
+
+ length /= sizeof(*list);
+ total = j + length;
+
+ if (total > dev->pin_count) {
+ pr_warn(TAG "%s has %d pins, more than the %d we know about\n",
+ dev->name, total, dev->pin_count);
+ }
+
+ for ( ; j < total && j < dev->pin_count; j++) {
+ pin = group->base + j;
+ *list++ = cpu_to_be32p(&pin);
+ *flist++ = cpu_to_be32p(&group->function);
+ *plist++ = cpu_to_be32p(&dev->pin_pull[j]);
+ }
+
+ of_node_put(prop);
+
+ if (total > dev->pin_count) {
+ pr_warn(TAG "%s has %d pins, more than the %d we know about\n",
+ dev->name, total, dev->pin_count);
+ break;
+ }
+
+ j = total;
+ }
+
+ return 0;
+
+err_prop:
+ of_node_put(prop);
+ return -EINVAL;
+}
diff --git a/drivers/pinctrl/android-things/main.c b/drivers/pinctrl/android-things/main.c
new file mode 100644
index 000000000000..ad2813a84b70
--- /dev/null
+++ b/drivers/pinctrl/android-things/main.c
@@ -0,0 +1,103 @@
+/*
+ * main.c
+ *
+ * Runtime pin configuration for Raspberry Pi
+ *
+ * Copyright (C) 2017 Google, Inc.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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.
+ */
+
+#include <linux/kernel.h>
+#include <linux/kobject.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+
+#include "runtimepinconfig.h"
+
+MODULE_LICENSE("GPL v2");
+
+static struct platform_driver pin_driver;
+
+DEVICE_ATTR_WO(function);
+DEVICE_ATTR_WO(resistor);
+
+static struct attribute *pinctrl_attrs[] = {
+ &dev_attr_function.attr,
+ &dev_attr_resistor.attr,
+ NULL
+};
+ATTRIBUTE_GROUPS(pinctrl);
+
+static struct class pinctrl_class = {
+ .name = "pinctrl",
+ .owner = THIS_MODULE,
+ .dev_groups = pinctrl_groups
+};
+
+static int pin_probe(struct platform_device *dev)
+{
+ struct pin_device *pin_dev;
+
+ if ((pin_dev = track_pin_device(dev, &pinctrl_class)))
+ dev_set_drvdata(&dev->dev, pin_dev);
+
+ return 0;
+}
+
+static int pin_remove(struct platform_device *dev)
+{
+ untrack_pin_device(dev_get_drvdata(&dev->dev));
+ return 0;
+}
+
+static int __init runtimepinconfig_init(void)
+{
+ if (class_register(&pinctrl_class)) {
+ pr_err(TAG "unable create pinctrl class\n");
+ goto err_class;
+ }
+
+ if (unregister_platform_devices()) {
+ pr_err(TAG "unable to unregister platform devices\n");
+ goto err_unregister_or_driver;
+ }
+
+ if (__platform_driver_register(&pin_driver, THIS_MODULE)) {
+ pr_err(TAG "unable to register pin driver\n");
+ goto err_unregister_or_driver;
+ }
+
+ pr_debug(TAG "module loaded\n");
+ return 0;
+
+err_unregister_or_driver:
+ class_unregister(&pinctrl_class);
+err_class:
+ return -ECANCELED;
+}
+
+static const struct of_device_id pin_match[] = {
+ { .compatible = "google,android-things-pins" },
+ { }
+};
+MODULE_DEVICE_TABLE(of, pin_match);
+
+static struct platform_driver pin_driver = {
+ .probe = pin_probe,
+ .remove = pin_remove,
+ .driver = {
+ .name = "android-things-pins",
+ .of_match_table = pin_match
+ }
+};
+
+module_init(runtimepinconfig_init);
diff --git a/drivers/pinctrl/android-things/platform_devices.c b/drivers/pinctrl/android-things/platform_devices.c
new file mode 100644
index 000000000000..97a6c0b59b9a
--- /dev/null
+++ b/drivers/pinctrl/android-things/platform_devices.c
@@ -0,0 +1,174 @@
+/*
+ * platform_devices.c
+ *
+ * Runtime pin configuration for Raspberry Pi
+ *
+ * Copyright (C) 2017 Google, Inc.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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.
+ */
+
+#include "runtimepinconfig.h"
+#include "platform_devices.h"
+
+/*
+ * platform_devices - Specifies devices we want to control pinmuxing for.
+ *
+ * The device tree tells us which pins are used by which devices by default, but
+ * it doesn't tell us which other pins can be used by those devices. It also
+ * doesn't tell us which devices are mutually exclusive, or which devices these
+ * devices depend on.
+ */
+
+const int pin_count = 28;
+
+struct bcm_device platform_devices[] = {
+ {
+ .name = "SPI0",
+ .node = { .path = "/soc/spi@7e204000" },
+ .aux_dev = { .path = NULL },
+ .use_default = 0,
+ .always_unreg_aux = 0,
+ .pin_count = 5,
+ .pin_pull = (u32 []) { NONE, NONE, NONE, NONE, NONE },
+ .pin_group_count = 1,
+ .pin_groups = (struct pin_group []) {
+ {
+ .base = 7,
+ .function = ALT0
+ }
+ },
+ .excl = NULL
+ }, {
+ .name = "PWM",
+ .node = { .path = "/soc/pwm@7e20c000" },
+ .aux_dev = { .path = "/soc/cprman@7e101000" },
+ .use_default = 0,
+ .always_unreg_aux = 0,
+ .pin_count = 2,
+ .pin_pull = (u32 []) { NONE, NONE, },
+ .pin_group_count = 2,
+ .pin_groups = (struct pin_group []) {
+ {
+ .base = 12,
+ .function = ALT0
+ }, {
+ .base = 18,
+ .function = ALT5
+ }
+ },
+ .excl = (struct bcm_device *[]) {
+ &platform_devices[5],
+ NULL
+ }
+ }, {
+ .name = "I2C1",
+ .node = { .path = "/soc/i2c@7e804000" },
+ .aux_dev = { .path = NULL },
+ .use_default = 0,
+ .always_unreg_aux = 0,
+ .pin_count = 2,
+ .pin_pull = (u32 []) { NONE, NONE },
+ .pin_group_count = 1,
+ .pin_groups = (struct pin_group []) {
+ {
+ .base = 2,
+ .function = ALT0
+ }
+ },
+ .excl = NULL
+ }, {
+ .name = "UART0",
+ .node = { .path = "/soc/uart@7e201000" },
+ .aux_dev = { .path = NULL },
+ .use_default = 1,
+ .always_unreg_aux = 0,
+ .pin_count = 2,
+ .pin_pull = (u32 []) { NONE, UP },
+ .pin_group_count = 2,
+ .pin_groups = (struct pin_group []) {
+ {
+ .base = 32,
+ .function = ALT3
+ }, {
+ .base = 14,
+ .function = ALT0
+ }
+ },
+ .excl = NULL
+ }, {
+ .name = "UART1",
+ .node = { .path = "/soc/uart@7e215040" },
+ .aux_dev = { .path = NULL },
+ .use_default = 0,
+ .always_unreg_aux = 0,
+ .pin_count = 2,
+ .pin_pull = (u32 []) { NONE, UP },
+ .pin_group_count = 1,
+ .pin_groups = (struct pin_group []) {
+ {
+ .base = 14,
+ .function = ALT5
+ }
+ },
+ .excl = NULL
+ }, {
+ .name = "I2S1",
+ .node = { .path = "/soc/i2s@7e203000" },
+ .aux_dev = { .path = "/soc/sound" },
+ .use_default = 0,
+ .always_unreg_aux = 1,
+ .pin_count = 4,
+ .pin_pull = (u32 []) { NONE, NONE, NONE, NONE },
+ .pin_group_count = 1,
+ .pin_groups = (struct pin_group []) {
+ {
+ .base = 18,
+ .function = ALT0
+ }
+ },
+ .excl = (struct bcm_device *[]) {
+ &platform_devices[1],
+ NULL
+ }
+ }, {
+ .name = "GPIO",
+ .node = { .path = NULL },
+ .aux_dev = { .path = NULL },
+ .use_default = 0,
+ .always_unreg_aux = 0,
+ .pin_count = 26,
+ .pin_group_count = 1,
+ .pin_groups = (struct pin_group []) {
+ {
+ .base = 2,
+ .function = GPIO
+ }
+ },
+ .excl = NULL
+ }, {
+ .name = NULL
+ }
+};
+
+struct bcm_resistor platform_resistors[] = {
+ {
+ .name = "NONE",
+ .resistor = NONE
+ }, {
+ .name = "DOWN",
+ .resistor = DOWN
+ }, {
+ .name = "UP",
+ .resistor = UP
+ }, {
+ .name = NULL
+ }
+};
diff --git a/drivers/pinctrl/android-things/platform_devices.h b/drivers/pinctrl/android-things/platform_devices.h
new file mode 100644
index 000000000000..baea33c89f10
--- /dev/null
+++ b/drivers/pinctrl/android-things/platform_devices.h
@@ -0,0 +1,123 @@
+/*
+ * platform_devices.h
+ *
+ * Runtime pin configuration for Raspberry Pi
+ *
+ * Copyright (C) 2017 Google, Inc.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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.
+ */
+
+#ifndef PLATFORM_DEVICES_H_
+#define PLATFORM_DEVICES_H_
+
+#define PROP_PINS "brcm,pins"
+#define PROP_FUNC "brcm,function"
+#define PROP_PULL "brcm,pull"
+
+#define STATE_0 "pinctrl-0"
+
+/*
+ * struct node_path - Matches a device tree path to the corresponding node
+ * structure. Rather than look up the path every time we need to find a node,
+ * save a pointer to the node here.
+ */
+struct node_path {
+ const char *path;
+ struct device_node *of_node;
+};
+
+/* Broadcom pin function numbers. See drivers/pinctrl/bcm/pinctrl-bcm2835.c */
+enum bcm_fsel {
+ GPIO = 0,
+ ALT0 = 4,
+ ALT1 = 5,
+ ALT2 = 6,
+ ALT3 = 7,
+ ALT4 = 3,
+ ALT5 = 2
+};
+
+/*
+ * struct pin_group - Defines the starting pin and hardware-specific function
+ * number for a group of pins used by a peripheral. We assume that each group
+ * has the same function number and contains bcm_device.pin_count consecutive
+ * pins.
+ */
+struct pin_group {
+ u32 base;
+ enum bcm_fsel function;
+};
+
+/*
+ * struct bcm_device - Contains details about relevant peripherals on the chip.
+ *
+ * @name: A string used to look up this device. Used to assign a pin to a
+ * device through the sysfs interface.
+ * @node: A node_path containing the device tree node for this device.
+ * @aux_dev: A node_path containing the device tree node for this device's
+ * auxiliary device. The auxiliary device may depend on this
+ * device, or this device may depend on it.
+ * @use_default: Whether or not we should register this device on
+ * a default pin_group if nobody is using it. For example,
+ * uart0 on the Raspberry Pi 3 is also used for Bluetooth,
+ * so we need to register it again when the user stops
+ * using it directly. pin_groups[0] is the default
+ * pin_group.
+ * @always_unreg_aux: Whether or not we should unregister the auxiliary device
+ * whenever we unregister this device. This flag also
+ * controls the order in which the devices get
+ * registered/unregistered.
+ * @pin_count: The number of pins used by this peripheral.
+ * @pin_pull: Specifies default resistor values for this device. Only used
+ * with use_default.
+ * @pin_group_count: The number of pin_groups available to this device and
+ * the length of the following array.
+ * @pin_groups: Array of pin_groups for this device.
+ * @excl: NULL-terminated array of bcm_devices that are mutually exclusive
+ * with this device. Whenever we register this device, we must
+ * first unregister every device in this array. For example, i2s
+ * and pwm are mutually exclusive despite the fact that they can
+ * use non-overlapping groups of pins.
+ */
+struct bcm_device {
+ const char *name;
+ struct node_path node;
+ struct node_path aux_dev;
+ int use_default:1;
+ int always_unreg_aux:1;
+ int pin_count;
+ u32 *pin_pull;
+ int pin_group_count;
+ struct pin_group *pin_groups;
+ struct bcm_device **excl;
+};
+
+/* Broadcom pin resistor numbers. */
+enum bcm_rsel {
+ NONE = 0,
+ DOWN = 1,
+ UP = 2
+};
+
+/*
+ * struct bcm_resistor - Matches a string description to a hardware-specific
+ * resistor number. Used to set the resistor through the sysfs interface.
+ */
+struct bcm_resistor {
+ const char *name;
+ enum bcm_rsel resistor;
+};
+
+extern struct bcm_device platform_devices[];
+extern struct bcm_resistor platform_resistors[];
+extern const int pin_count;
+
+#endif /* PLATFORM_DEVICES_H_ */
diff --git a/drivers/pinctrl/android-things/runtimepinconfig.h b/drivers/pinctrl/android-things/runtimepinconfig.h
new file mode 100644
index 000000000000..b165436d65f9
--- /dev/null
+++ b/drivers/pinctrl/android-things/runtimepinconfig.h
@@ -0,0 +1,79 @@
+/*
+ * runtimepinconfig.h
+ *
+ * Runtime pin configuration for Raspberry Pi
+ *
+ * Copyright (C) 2017 Google, Inc.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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.
+ */
+
+#ifndef RUNTIMEPINCONFIG_H_
+#define RUNTIMEPINCONFIG_H_
+
+#include <linux/device.h>
+#include <linux/of.h>
+#include <linux/pinctrl/consumer.h>
+#include <linux/platform_device.h>
+
+#define TAG "runtimepinconfig: "
+
+struct pin_group;
+struct bcm_device;
+
+/*
+ * struct pin_device - Holds information about a pin device that has been
+ * registered with our driver.
+ *
+ * @pin: The physical pin number for this device.
+ * @set_gpio: Set by the driver to determine which pin devices to register
+ * unregister.
+ * @device: The platform_device that represents this pin. We set this to
+ * NULL when the pin device gets unregistered.
+ * @of_node: The device tree node for this device. We save a pointer to it
+ * here in case the pin device gets unregistered.
+ * @char_device: The character device used to create the sysfs files for
+ * this pin.
+ */
+struct pin_device {
+ u32 pin;
+ int set_gpio:1;
+ struct platform_device *device;
+ struct device_node *of_node;
+ struct device *char_device;
+};
+
+ssize_t function_store(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t buflen);
+ssize_t resistor_store(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t buflen);
+
+int set_function(struct pin_device *dev, struct bcm_device *bcm_dev,
+ struct pin_group *group);
+int set_resistor(struct pin_device *dev, u32 resistor);
+
+struct pin_device *track_pin_device(struct platform_device *dev,
+ struct class *class);
+void untrack_pin_device(struct pin_device *dev);
+
+int unregister_platform_devices(void);
+
+int get_pin(struct device_node *node, u32 *pin);
+int device_has_pin(struct device_node *node, u32 pin);
+int expand_property(struct bcm_device *dev, const char *prop_name, u32 value);
+struct device *find_device_by_node(struct device_node *node);
+int set_device_config(struct bcm_device *dev, struct pin_group *group);
+
+static inline int pin_in_group(int pin, int base, int count)
+{
+ return (pin >= base && pin < base + count);
+}
+
+#endif /* RUNTIMEPINCONFIG_H_ */
diff --git a/drivers/pinctrl/android-things/sysfs.c b/drivers/pinctrl/android-things/sysfs.c
new file mode 100644
index 000000000000..eb264489d243
--- /dev/null
+++ b/drivers/pinctrl/android-things/sysfs.c
@@ -0,0 +1,102 @@
+/*
+ * sysfs.c
+ *
+ * Runtime pin configuration for Raspberry Pi
+ *
+ * Copyright (C) 2017 Google, Inc.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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.
+ */
+
+#include <linux/device.h>
+#include <linux/of.h>
+
+#include "runtimepinconfig.h"
+#include "platform_devices.h"
+
+/*
+ * These functions are the sysfs interface. Parse the user input and match it to
+ * a peripheral device or resistor value. Use the pin number to determine the
+ * pin_group, if necessary.
+ */
+ssize_t function_store(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t bufsize)
+{
+ struct pin_device *pin_dev = dev_get_drvdata(dev);
+ struct bcm_device *bcm_dev = NULL;
+ size_t i, namelen, inlen;
+ struct pin_group *group;
+ int ret;
+
+ for (inlen = 0; buf[inlen] != '\n' && inlen < bufsize; inlen++)
+ ;
+
+ /* Match the input buffer to a platform device name. */
+ for (i = 0; platform_devices[i].name != NULL; i++) {
+ namelen = strlen(platform_devices[i].name);
+ if (inlen != namelen)
+ continue;
+
+ if (!strncmp(platform_devices[i].name, buf, namelen)) {
+ bcm_dev = &platform_devices[i];
+ break;
+ }
+ }
+
+ if (!bcm_dev) {
+ pr_warn(TAG "no matching platform device found on pin %d\n",
+ pin_dev->pin);
+ return -ENODEV;
+ }
+
+ group = bcm_dev->pin_groups;
+
+ /* Match the pin number to a pin group available to the device. */
+ for (i = 0; i < bcm_dev->pin_group_count; i++) {
+ if (pin_in_group(pin_dev->pin, group[i].base,
+ bcm_dev->pin_count)) {
+ if ((ret = set_function(pin_dev, bcm_dev, &group[i])))
+ pr_err(TAG "set function %s failed on pin %d\n",
+ bcm_dev->name, pin_dev->pin);
+ return (ret) ? ret : bufsize;
+ }
+ }
+
+ pr_warn(TAG "no matching pin group for function %s on pin %d\n",
+ bcm_dev->name, pin_dev->pin);
+ return -EINVAL;
+}
+
+ssize_t resistor_store(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t bufsize)
+{
+ struct pin_device *pin_dev = dev_get_drvdata(dev);
+ int ret;
+ size_t i, namelen, inlen;
+
+ for (inlen = 0; buf[inlen] != '\n' && inlen < bufsize; inlen++)
+ ;
+
+ for (i = 0; platform_resistors[i].name; i++) {
+ namelen = strlen(platform_resistors[i].name);
+ if (inlen != namelen)
+ continue;
+
+ if (!strncmp(platform_resistors[i].name, buf, namelen)) {
+ ret = set_resistor(pin_dev,
+ platform_resistors[i].resistor);
+ return (ret) ? ret : bufsize;
+ }
+ }
+
+ pr_warn(TAG "no matching resistor configuration found for pin %d\n",
+ pin_dev->pin);
+ return -EINVAL;
+}