diff options
author | Braden Kell <bradenkell@google.com> | 2017-05-24 21:17:54 +0000 |
---|---|---|
committer | android-build-merger <android-build-merger@google.com> | 2017-05-24 21:17:54 +0000 |
commit | bf05b0b5cec5a6366283351051b9a9c4a282728c (patch) | |
tree | 166bfb3a8688097c31e7b9207a86db9029446979 | |
parent | 6ca08085601903f10bd23fdc4e398fee317f7331 (diff) | |
parent | 0a4b801469aa372dfaf4696262cd7d48779b3aa7 (diff) | |
download | pi-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.c | 815 | ||||
-rw-r--r-- | drivers/pinctrl/android-things/devicetree.c | 305 | ||||
-rw-r--r-- | drivers/pinctrl/android-things/main.c | 103 | ||||
-rw-r--r-- | drivers/pinctrl/android-things/platform_devices.c | 174 | ||||
-rw-r--r-- | drivers/pinctrl/android-things/platform_devices.h | 123 | ||||
-rw-r--r-- | drivers/pinctrl/android-things/runtimepinconfig.h | 79 | ||||
-rw-r--r-- | drivers/pinctrl/android-things/sysfs.c | 102 |
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; +} |