diff options
author | Linus Torvalds | 2012-12-11 11:21:33 -0800 |
---|---|---|
committer | Linus Torvalds | 2012-12-11 11:21:33 -0800 |
commit | 505cbedab9c7c565957e64af6348e5d84acd510e (patch) | |
tree | 4855caf82c434629432e22f03c96892d73383ba2 /drivers/gpio | |
parent | a8936db7c2d9ef7f8e080d629301e448291f3b75 (diff) | |
parent | 7c8f86a451fe8c010eb93c62d4d69727ccdbe435 (diff) |
Merge tag 'pinctrl-for-v3.8' of git://git.kernel.org/pub/scm/linux/kernel/git/linusw/linux-pinctrl
Pull pinctrl changes from Linus Walleij:
"These are the first and major pinctrl changes for the v3.8 merge
cycle. Some of this is used as merge base for other trees so I better
be early on the trigger.
As can be seen from the diffstat the major changes are:
- A big conversion of the AT91 pinctrl driver and the associated ACKed
platform changes under arch/arm/max-at91 and its device trees. This
has been coordinated with the AT91 maintainers to go in through the
pinctrl tree.
- A larger chunk of changes to the SPEAr drivers and the addition of
the "plgpio" driver for the SPEAr as well.
- The removal of the remnants of the Nomadik driver from the arch/arm
tree and fusion of that into the Nomadik driver and platform data
header files.
- Some local movement in the Marvell MVEBU drivers, these now have
their own subdirectory.
- The addition of a chunk of code to gpiolib under drivers/gpio to
register gpio-to-pin range mappings from the GPIO side of things.
This has been requested by Grant Likely and is now implemented, it
is particularly useful for device tree work.
Then we have incremental updates all over the place, many of these are
cleanups and fixes from Axel Lin who has done a great job of removing
minor mistakes and compilation annoyances."
* tag 'pinctrl-for-v3.8' of git://git.kernel.org/pub/scm/linux/kernel/git/linusw/linux-pinctrl: (114 commits)
ARM: mmp: select PINCTRL for ARCH_MMP
pinctrl: Drop selecting PINCONF for MMP2, PXA168 and PXA910
pinctrl: pinctrl-single: Fix error check condition
pinctrl: SPEAr: Update error check for unsigned variables
gpiolib: Fix use after free in gpiochip_add_pin_range
gpiolib: rename pin range arguments
pinctrl: single: support gpio request and free
pinctrl: generic: add input schmitt disable parameter
pinctrl/u300/coh901: stop spawning pinctrl from GPIO
pinctrl/u300/coh901: let the gpio_chip register the range
pinctrl: add function to retrieve range from pin
gpiolib: return any error code from range creation
pinctrl: make range registration defer properly
gpiolib: rename find_pinctrl_*
gpiolib: let gpiochip_add_pin_range() specify offset
ARM: at91: pm9g45: add mmc support
ARM: at91: Animeo IP: add mmc support
ARM: at91: dt: add mmc pinctrl for Atmel reference boards
ARM: at91: dt: at91sam9: add mmc pinctrl support
ARM: at91/dts: add nodes for atmel hsmci controllers for atmel boards
...
Diffstat (limited to 'drivers/gpio')
-rw-r--r-- | drivers/gpio/gpiolib-of.c | 52 | ||||
-rw-r--r-- | drivers/gpio/gpiolib.c | 76 |
2 files changed, 128 insertions, 0 deletions
diff --git a/drivers/gpio/gpiolib-of.c b/drivers/gpio/gpiolib-of.c index f1a45997aea8..d542a141811a 100644 --- a/drivers/gpio/gpiolib-of.c +++ b/drivers/gpio/gpiolib-of.c @@ -19,6 +19,7 @@ #include <linux/of.h> #include <linux/of_address.h> #include <linux/of_gpio.h> +#include <linux/pinctrl/pinctrl.h> #include <linux/slab.h> /* Private data structure for of_gpiochip_find_and_xlate */ @@ -216,6 +217,54 @@ err0: } EXPORT_SYMBOL(of_mm_gpiochip_add); +#ifdef CONFIG_PINCTRL +static void of_gpiochip_add_pin_range(struct gpio_chip *chip) +{ + struct device_node *np = chip->of_node; + struct of_phandle_args pinspec; + struct pinctrl_dev *pctldev; + int index = 0, ret; + + if (!np) + return; + + do { + ret = of_parse_phandle_with_args(np, "gpio-ranges", + "#gpio-range-cells", index, &pinspec); + if (ret) + break; + + pctldev = of_pinctrl_get(pinspec.np); + if (!pctldev) + break; + + /* + * This assumes that the n GPIO pins are consecutive in the + * GPIO number space, and that the pins are also consecutive + * in their local number space. Currently it is not possible + * to add different ranges for one and the same GPIO chip, + * as the code assumes that we have one consecutive range + * on both, mapping 1-to-1. + * + * TODO: make the OF bindings handle multiple sparse ranges + * on the same GPIO chip. + */ + ret = gpiochip_add_pin_range(chip, + pinctrl_dev_get_name(pctldev), + 0, /* offset in gpiochip */ + pinspec.args[0], + pinspec.args[1]); + + if (ret) + break; + + } while (index++); +} + +#else +static void of_gpiochip_add_pin_range(struct gpio_chip *chip) {} +#endif + void of_gpiochip_add(struct gpio_chip *chip) { if ((!chip->of_node) && (chip->dev)) @@ -229,11 +278,14 @@ void of_gpiochip_add(struct gpio_chip *chip) chip->of_xlate = of_gpio_simple_xlate; } + of_gpiochip_add_pin_range(chip); of_node_get(chip->of_node); } void of_gpiochip_remove(struct gpio_chip *chip) { + gpiochip_remove_pin_ranges(chip); + if (chip->of_node) of_node_put(chip->of_node); } diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c index 1c8d9e3380e1..58b9838801c0 100644 --- a/drivers/gpio/gpiolib.c +++ b/drivers/gpio/gpiolib.c @@ -1083,6 +1083,10 @@ int gpiochip_add(struct gpio_chip *chip) } } +#ifdef CONFIG_PINCTRL + INIT_LIST_HEAD(&chip->pin_ranges); +#endif + of_gpiochip_add(chip); unlock: @@ -1123,6 +1127,7 @@ int gpiochip_remove(struct gpio_chip *chip) spin_lock_irqsave(&gpio_lock, flags); + gpiochip_remove_pin_ranges(chip); of_gpiochip_remove(chip); for (id = chip->base; id < chip->base + chip->ngpio; id++) { @@ -1180,6 +1185,77 @@ struct gpio_chip *gpiochip_find(void *data, } EXPORT_SYMBOL_GPL(gpiochip_find); +#ifdef CONFIG_PINCTRL + +/** + * gpiochip_add_pin_range() - add a range for GPIO <-> pin mapping + * @chip: the gpiochip to add the range for + * @pinctrl_name: the dev_name() of the pin controller to map to + * @gpio_offset: the start offset in the current gpio_chip number space + * @pin_offset: the start offset in the pin controller number space + * @npins: the number of pins from the offset of each pin space (GPIO and + * pin controller) to accumulate in this range + */ +int gpiochip_add_pin_range(struct gpio_chip *chip, const char *pinctl_name, + unsigned int gpio_offset, unsigned int pin_offset, + unsigned int npins) +{ + struct gpio_pin_range *pin_range; + int ret; + + pin_range = kzalloc(sizeof(*pin_range), GFP_KERNEL); + if (!pin_range) { + pr_err("%s: GPIO chip: failed to allocate pin ranges\n", + chip->label); + return -ENOMEM; + } + + /* Use local offset as range ID */ + pin_range->range.id = gpio_offset; + pin_range->range.gc = chip; + pin_range->range.name = chip->label; + pin_range->range.base = chip->base + gpio_offset; + pin_range->range.pin_base = pin_offset; + pin_range->range.npins = npins; + pin_range->pctldev = pinctrl_find_and_add_gpio_range(pinctl_name, + &pin_range->range); + if (IS_ERR(pin_range->pctldev)) { + ret = PTR_ERR(pin_range->pctldev); + pr_err("%s: GPIO chip: could not create pin range\n", + chip->label); + kfree(pin_range); + return ret; + } + pr_debug("GPIO chip %s: created GPIO range %d->%d ==> %s PIN %d->%d\n", + chip->label, gpio_offset, gpio_offset + npins - 1, + pinctl_name, + pin_offset, pin_offset + npins - 1); + + list_add_tail(&pin_range->node, &chip->pin_ranges); + + return 0; +} +EXPORT_SYMBOL_GPL(gpiochip_add_pin_range); + +/** + * gpiochip_remove_pin_ranges() - remove all the GPIO <-> pin mappings + * @chip: the chip to remove all the mappings for + */ +void gpiochip_remove_pin_ranges(struct gpio_chip *chip) +{ + struct gpio_pin_range *pin_range, *tmp; + + list_for_each_entry_safe(pin_range, tmp, &chip->pin_ranges, node) { + list_del(&pin_range->node); + pinctrl_remove_gpio_range(pin_range->pctldev, + &pin_range->range); + kfree(pin_range); + } +} +EXPORT_SYMBOL_GPL(gpiochip_remove_pin_ranges); + +#endif /* CONFIG_PINCTRL */ + /* These "optional" allocation calls help prevent drivers from stomping * on each other, and help provide better diagnostics in debugfs. * They're called even less than the "set direction" calls. |