From f23f1516b6757c326cc638bed8c402c77e2a596e Mon Sep 17 00:00:00 2001 From: Shiraz Hashim Date: Sat, 27 Oct 2012 15:21:36 +0530 Subject: gpiolib: provide provision to register pin ranges pinctrl subsystem needs gpio chip base to prepare set of gpio pin ranges, which a given pinctrl driver can handle. This is important to handle pinctrl gpio request calls in order to program a given pin properly for gpio operation. As gpio base is allocated dynamically during gpiochip registration, presently there exists no clean way to pass this information to the pinctrl subsystem. After few discussions from [1], it was concluded that may be gpio controller reporting the pin range it supports, is a better way than pinctrl subsystem directly registering it. [1] http://comments.gmane.org/gmane.linux.ports.arm.kernel/184816 Cc: Grant Likely Signed-off-by: Viresh Kumar Signed-off-by: Shiraz Hashim [Edited documentation a bit] Signed-off-by: Linus Walleij --- drivers/gpio/gpiolib-of.c | 56 +++++++++++++++++++++++++++++++++++++++++++++++ drivers/gpio/gpiolib.c | 43 ++++++++++++++++++++++++++++++++++++ 2 files changed, 99 insertions(+) (limited to 'drivers/gpio') diff --git a/drivers/gpio/gpiolib-of.c b/drivers/gpio/gpiolib-of.c index f1a45997aea8..a5b90c8e9844 100644 --- a/drivers/gpio/gpiolib-of.c +++ b/drivers/gpio/gpiolib-of.c @@ -19,6 +19,7 @@ #include #include #include +#include #include /* Private data structure for of_gpiochip_find_and_xlate */ @@ -216,6 +217,58 @@ err0: } EXPORT_SYMBOL(of_mm_gpiochip_add); +#ifdef CONFIG_PINCTRL +void of_gpiochip_add_pin_range(struct gpio_chip *chip) +{ + struct device_node *np = chip->of_node; + struct gpio_pin_range *pin_range; + struct of_phandle_args pinspec; + 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; + + pin_range = devm_kzalloc(chip->dev, sizeof(*pin_range), + GFP_KERNEL); + if (!pin_range) { + pr_err("%s: GPIO chip: failed to allocate pin ranges\n", + chip->label); + break; + } + + pin_range->range.name = chip->label; + pin_range->range.base = chip->base; + pin_range->range.pin_base = pinspec.args[0]; + pin_range->range.npins = pinspec.args[1]; + pin_range->pctldev = of_pinctrl_add_gpio_range(pinspec.np, + &pin_range->range); + + list_add_tail(&pin_range->node, &chip->pin_ranges); + + } while (index++); +} + +void of_gpiochip_remove_pin_range(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); + } +} +#else +void of_gpiochip_add_pin_range(struct gpio_chip *chip) {} +void of_gpiochip_remove_pin_range(struct gpio_chip *chip) {} +#endif + void of_gpiochip_add(struct gpio_chip *chip) { if ((!chip->of_node) && (chip->dev)) @@ -229,11 +282,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) { + of_gpiochip_remove_pin_range(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..f0b07bbfcc9a 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: @@ -1180,6 +1184,45 @@ struct gpio_chip *gpiochip_find(void *data, } EXPORT_SYMBOL_GPL(gpiochip_find); +#ifdef CONFIG_PINCTRL +void gpiochip_add_pin_range(struct gpio_chip *chip, const char *pinctl_name, + unsigned int pin_base, unsigned int npins) +{ + struct gpio_pin_range *pin_range; + + pin_range = devm_kzalloc(chip->dev, sizeof(*pin_range), GFP_KERNEL); + if (!pin_range) { + pr_err("%s: GPIO chip: failed to allocate pin ranges\n", + chip->label); + return; + } + + pin_range->range.name = chip->label; + pin_range->range.base = chip->base; + pin_range->range.pin_base = pin_base; + pin_range->range.npins = npins; + pin_range->pctldev = find_pinctrl_and_add_gpio_range(pinctl_name, + &pin_range->range); + + list_add_tail(&pin_range->node, &chip->pin_ranges); +} + +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); + } +} +#else +void gpiochip_add_pin_range(struct gpio_chip *chip, const char *pinctl_name, + unsigned int pin_base, unsigned int npins) {} +void gpiochip_remove_pin_ranges(struct gpio_chip *chip) {} +#endif + /* 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. -- cgit v1.2.3 From 165adc9c1734a3f3bdbc6dc7c7a29bbefb424006 Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Tue, 6 Nov 2012 14:49:39 +0100 Subject: gpiolib: fix up function prototypes etc Commit 69e1601bca88809dc118abd1becb02c15a02ec71 "gpiolib: provide provision to register pin ranges" Got most of it's function prototypes wrong, so fix this up by: - Moving the void declarations into static inlines in (previously the actual prototypes were declared here...) - Declare the gpiochip_add_pin_range() and gpiochip_remove_pin_ranges() functions in together with the pin range struct declaration itself. - Actually only implement these very functions in gpiolib.c if CONFIG_PINCTRL is set. - Additionally export the symbols since modules will need to be able to do this. Reviewed-by: Stephen Warren Signed-off-by: Linus Walleij --- drivers/gpio/gpiolib.c | 10 +++++----- include/asm-generic/gpio.h | 6 ++++++ include/linux/gpio.h | 24 ++++++++++++++++++------ 3 files changed, 29 insertions(+), 11 deletions(-) (limited to 'drivers/gpio') diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c index f0b07bbfcc9a..1e1a7cabc57e 100644 --- a/drivers/gpio/gpiolib.c +++ b/drivers/gpio/gpiolib.c @@ -1185,6 +1185,7 @@ struct gpio_chip *gpiochip_find(void *data, EXPORT_SYMBOL_GPL(gpiochip_find); #ifdef CONFIG_PINCTRL + void gpiochip_add_pin_range(struct gpio_chip *chip, const char *pinctl_name, unsigned int pin_base, unsigned int npins) { @@ -1206,6 +1207,7 @@ void gpiochip_add_pin_range(struct gpio_chip *chip, const char *pinctl_name, list_add_tail(&pin_range->node, &chip->pin_ranges); } +EXPORT_SYMBOL_GPL(gpiochip_add_pin_range); void gpiochip_remove_pin_ranges(struct gpio_chip *chip) { @@ -1217,11 +1219,9 @@ void gpiochip_remove_pin_ranges(struct gpio_chip *chip) &pin_range->range); } } -#else -void gpiochip_add_pin_range(struct gpio_chip *chip, const char *pinctl_name, - unsigned int pin_base, unsigned int npins) {} -void gpiochip_remove_pin_ranges(struct gpio_chip *chip) {} -#endif +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. diff --git a/include/asm-generic/gpio.h b/include/asm-generic/gpio.h index 92e5c432421c..2e60de4265ac 100644 --- a/include/asm-generic/gpio.h +++ b/include/asm-generic/gpio.h @@ -49,6 +49,7 @@ struct module; struct device_node; #ifdef CONFIG_PINCTRL + /** * struct gpio_pin_range - pin range controlled by a gpio chip * @head: list for maintaining set of pin ranges, used internally @@ -61,6 +62,11 @@ struct gpio_pin_range { struct pinctrl_dev *pctldev; struct pinctrl_gpio_range range; }; + +void gpiochip_add_pin_range(struct gpio_chip *chip, const char *pinctl_name, + unsigned int pin_base, unsigned int npins); +void gpiochip_remove_pin_ranges(struct gpio_chip *chip); + #endif /** diff --git a/include/linux/gpio.h b/include/linux/gpio.h index a28445992b7f..21d28b930dc7 100644 --- a/include/linux/gpio.h +++ b/include/linux/gpio.h @@ -72,9 +72,9 @@ static inline int irq_to_gpio(unsigned int irq) return -EINVAL; } -#endif +#endif /* ! CONFIG_ARCH_HAVE_CUSTOM_GPIO_H */ -#else +#else /* ! CONFIG_GENERIC_GPIO */ #include #include @@ -231,9 +231,21 @@ static inline int irq_to_gpio(unsigned irq) return -EINVAL; } -void gpiochip_add_pin_range(struct gpio_chip *chip, const char *pinctl_name, - unsigned int pin_base, unsigned int npins); -void gpiochip_remove_pin_ranges(struct gpio_chip *chip); -#endif +#ifdef CONFIG_PINCTRL + +static inline void +gpiochip_add_pin_range(struct gpio_chip *chip, const char *pinctl_name, + unsigned int pin_base, unsigned int npins) +{ +} + +static inline void +gpiochip_remove_pin_ranges(struct gpio_chip *chip) +{ +} + +#endif /* CONFIG_PINCTRL */ + +#endif /* ! CONFIG_GENERIC_GPIO */ #endif /* __LINUX_GPIO_H */ -- cgit v1.2.3 From 167c1af9443757bb9d27ceff8ba4304302cb0651 Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Tue, 6 Nov 2012 14:58:55 +0100 Subject: gpiolib-of: staticize the pin range calls Commit 69e1601bca88809dc118abd1becb02c15a02ec71 "gpiolib: provide provision to register pin ranges" Declared the of_gpiochip_[add|remove]_pin_range() global while they should be static as they are only ever used in this file. Let's convert them to static. Reviewed-by: Stephen Warren Reviewed-by: Viresh Kumar Signed-off-by: Linus Walleij --- drivers/gpio/gpiolib-of.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'drivers/gpio') diff --git a/drivers/gpio/gpiolib-of.c b/drivers/gpio/gpiolib-of.c index a5b90c8e9844..220caa5978f7 100644 --- a/drivers/gpio/gpiolib-of.c +++ b/drivers/gpio/gpiolib-of.c @@ -218,7 +218,7 @@ err0: EXPORT_SYMBOL(of_mm_gpiochip_add); #ifdef CONFIG_PINCTRL -void of_gpiochip_add_pin_range(struct gpio_chip *chip) +static void of_gpiochip_add_pin_range(struct gpio_chip *chip) { struct device_node *np = chip->of_node; struct gpio_pin_range *pin_range; @@ -254,7 +254,7 @@ void of_gpiochip_add_pin_range(struct gpio_chip *chip) } while (index++); } -void of_gpiochip_remove_pin_range(struct gpio_chip *chip) +static void of_gpiochip_remove_pin_range(struct gpio_chip *chip) { struct gpio_pin_range *pin_range, *tmp; @@ -265,8 +265,8 @@ void of_gpiochip_remove_pin_range(struct gpio_chip *chip) } } #else -void of_gpiochip_add_pin_range(struct gpio_chip *chip) {} -void of_gpiochip_remove_pin_range(struct gpio_chip *chip) {} +static void of_gpiochip_add_pin_range(struct gpio_chip *chip) {} +static void of_gpiochip_remove_pin_range(struct gpio_chip *chip) {} #endif void of_gpiochip_add(struct gpio_chip *chip) -- cgit v1.2.3 From e93fa3f24353e45b189bae656ba000d0533777a3 Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Tue, 6 Nov 2012 15:03:47 +0100 Subject: gpiolib: remove duplicate pin range code Commit 69e1601bca88809dc118abd1becb02c15a02ec71 "gpiolib: provide provision to register pin ranges" Introduced both of_gpiochip_remove_pin_range() and gpiochip_remove_pin_ranges(). But the contents are exactly the same so remove the OF one and rely on the range deletion in the core. Reviewed-by: Stephen Warren Reviewed-by: Viresh Kumar Signed-off-by: Linus Walleij --- drivers/gpio/gpiolib-of.c | 13 +------------ 1 file changed, 1 insertion(+), 12 deletions(-) (limited to 'drivers/gpio') diff --git a/drivers/gpio/gpiolib-of.c b/drivers/gpio/gpiolib-of.c index 220caa5978f7..67403e47e4dc 100644 --- a/drivers/gpio/gpiolib-of.c +++ b/drivers/gpio/gpiolib-of.c @@ -254,19 +254,8 @@ static void of_gpiochip_add_pin_range(struct gpio_chip *chip) } while (index++); } -static void of_gpiochip_remove_pin_range(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); - } -} #else static void of_gpiochip_add_pin_range(struct gpio_chip *chip) {} -static void of_gpiochip_remove_pin_range(struct gpio_chip *chip) {} #endif void of_gpiochip_add(struct gpio_chip *chip) @@ -288,7 +277,7 @@ void of_gpiochip_add(struct gpio_chip *chip) void of_gpiochip_remove(struct gpio_chip *chip) { - of_gpiochip_remove_pin_range(chip); + gpiochip_remove_pin_ranges(chip); if (chip->of_node) of_node_put(chip->of_node); -- cgit v1.2.3 From 9ef0d6f7628bdcb5cc3c11623930f2527a3881a0 Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Tue, 6 Nov 2012 15:15:44 +0100 Subject: gpiolib: call pin removal in chip removal function This makes us call gpiochio_remove_pin_ranges() in the gpiochip_remove() function, so we get rid of ranges when freeing the chip. Reviewed-by: Stephen Warren Reviewed-by: Viresh Kumar Signed-off-by: Linus Walleij --- drivers/gpio/gpiolib.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers/gpio') diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c index 1e1a7cabc57e..bcf9b9914fb7 100644 --- a/drivers/gpio/gpiolib.c +++ b/drivers/gpio/gpiolib.c @@ -1127,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++) { -- cgit v1.2.3 From 1e63d7b9363f0c57d00991f9f2e0af374dfc591a Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Tue, 6 Nov 2012 16:03:35 +0100 Subject: gpiolib: separation of pin concerns The fact that of_gpiochip_add_pin_range() and gpiochip_add_pin_range() share too much code is fragile and will invariably mean that bugs need to be fixed in two places instead of one. So separate the concerns of gpiolib.c and gpiolib-of.c and have the latter call the former as back-end. This is necessary also when going forward with other device descriptions such as ACPI. This is done by: - Adding a return code to gpiochip_add_pin_range() so we can reliably check whether this succeeds. - Get rid of the custom of_pinctrl_add_gpio_range() from pinctrl. Instead create of_pinctrl_get() to just retrive the pin controller per se from an OF node. This composite function was just begging to be deleted, it was way to purpose-specific. - Use pinctrl_dev_get_name() to get the name of the retrieved pin controller and use that to call back into the generic gpiochip_add_pin_range(). Now the pin range is only allocated and tied to a pin controller from the core implementation in gpiolib.c. Signed-off-by: Linus Walleij --- drivers/gpio/gpiolib-of.c | 23 +++++++++-------------- drivers/gpio/gpiolib.c | 8 +++++--- drivers/pinctrl/devicetree.c | 4 +--- include/asm-generic/gpio.h | 4 ++-- include/linux/gpio.h | 2 +- include/linux/pinctrl/pinctrl.h | 7 ++----- 6 files changed, 20 insertions(+), 28 deletions(-) (limited to 'drivers/gpio') diff --git a/drivers/gpio/gpiolib-of.c b/drivers/gpio/gpiolib-of.c index 67403e47e4dc..a40cd84c5c10 100644 --- a/drivers/gpio/gpiolib-of.c +++ b/drivers/gpio/gpiolib-of.c @@ -221,8 +221,8 @@ EXPORT_SYMBOL(of_mm_gpiochip_add); static void of_gpiochip_add_pin_range(struct gpio_chip *chip) { struct device_node *np = chip->of_node; - struct gpio_pin_range *pin_range; struct of_phandle_args pinspec; + struct pinctrl_dev *pctldev; int index = 0, ret; if (!np) @@ -234,22 +234,17 @@ static void of_gpiochip_add_pin_range(struct gpio_chip *chip) if (ret) break; - pin_range = devm_kzalloc(chip->dev, sizeof(*pin_range), - GFP_KERNEL); - if (!pin_range) { - pr_err("%s: GPIO chip: failed to allocate pin ranges\n", - chip->label); + pctldev = of_pinctrl_get(pinspec.np); + if (!pctldev) break; - } - pin_range->range.name = chip->label; - pin_range->range.base = chip->base; - pin_range->range.pin_base = pinspec.args[0]; - pin_range->range.npins = pinspec.args[1]; - pin_range->pctldev = of_pinctrl_add_gpio_range(pinspec.np, - &pin_range->range); + ret = gpiochip_add_pin_range(chip, + pinctrl_dev_get_name(pctldev), + pinspec.args[0], + pinspec.args[1]); - list_add_tail(&pin_range->node, &chip->pin_ranges); + if (ret) + break; } while (index++); } diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c index bcf9b9914fb7..c5f650095faa 100644 --- a/drivers/gpio/gpiolib.c +++ b/drivers/gpio/gpiolib.c @@ -1187,8 +1187,8 @@ EXPORT_SYMBOL_GPL(gpiochip_find); #ifdef CONFIG_PINCTRL -void gpiochip_add_pin_range(struct gpio_chip *chip, const char *pinctl_name, - unsigned int pin_base, unsigned int npins) +int gpiochip_add_pin_range(struct gpio_chip *chip, const char *pinctl_name, + unsigned int pin_base, unsigned int npins) { struct gpio_pin_range *pin_range; @@ -1196,7 +1196,7 @@ void gpiochip_add_pin_range(struct gpio_chip *chip, const char *pinctl_name, if (!pin_range) { pr_err("%s: GPIO chip: failed to allocate pin ranges\n", chip->label); - return; + return -ENOMEM; } pin_range->range.name = chip->label; @@ -1207,6 +1207,8 @@ void gpiochip_add_pin_range(struct gpio_chip *chip, const char *pinctl_name, &pin_range->range); list_add_tail(&pin_range->node, &chip->pin_ranges); + + return 0; } EXPORT_SYMBOL_GPL(gpiochip_add_pin_range); diff --git a/drivers/pinctrl/devicetree.c b/drivers/pinctrl/devicetree.c index 6728ec71cb65..fe2d1af7cfa0 100644 --- a/drivers/pinctrl/devicetree.c +++ b/drivers/pinctrl/devicetree.c @@ -106,8 +106,7 @@ static struct pinctrl_dev *find_pinctrl_by_of_node(struct device_node *np) return NULL; } -struct pinctrl_dev *of_pinctrl_add_gpio_range(struct device_node *np, - struct pinctrl_gpio_range *range) +struct pinctrl_dev *of_pinctrl_get(struct device_node *np) { struct pinctrl_dev *pctldev; @@ -115,7 +114,6 @@ struct pinctrl_dev *of_pinctrl_add_gpio_range(struct device_node *np, if (!pctldev) return NULL; - pinctrl_add_gpio_range(pctldev, range); return pctldev; } diff --git a/include/asm-generic/gpio.h b/include/asm-generic/gpio.h index 2e60de4265ac..50d995e7e44a 100644 --- a/include/asm-generic/gpio.h +++ b/include/asm-generic/gpio.h @@ -63,8 +63,8 @@ struct gpio_pin_range { struct pinctrl_gpio_range range; }; -void gpiochip_add_pin_range(struct gpio_chip *chip, const char *pinctl_name, - unsigned int pin_base, unsigned int npins); +int gpiochip_add_pin_range(struct gpio_chip *chip, const char *pinctl_name, + unsigned int pin_base, unsigned int npins); void gpiochip_remove_pin_ranges(struct gpio_chip *chip); #endif diff --git a/include/linux/gpio.h b/include/linux/gpio.h index 21d28b930dc7..81bbfe5b5de6 100644 --- a/include/linux/gpio.h +++ b/include/linux/gpio.h @@ -233,7 +233,7 @@ static inline int irq_to_gpio(unsigned irq) #ifdef CONFIG_PINCTRL -static inline void +static inline int gpiochip_add_pin_range(struct gpio_chip *chip, const char *pinctl_name, unsigned int pin_base, unsigned int npins) { diff --git a/include/linux/pinctrl/pinctrl.h b/include/linux/pinctrl/pinctrl.h index 434e5a94e131..4a58428bc793 100644 --- a/include/linux/pinctrl/pinctrl.h +++ b/include/linux/pinctrl/pinctrl.h @@ -141,16 +141,13 @@ extern struct pinctrl_dev *find_pinctrl_and_add_gpio_range(const char *devname, struct pinctrl_gpio_range *range); #ifdef CONFIG_OF -extern struct pinctrl_dev *of_pinctrl_add_gpio_range(struct device_node *np, - struct pinctrl_gpio_range *range); +extern struct pinctrl_dev *of_pinctrl_get(struct device_node *np); #else static inline -struct pinctrl_dev *of_pinctrl_add_gpio_range(struct device_node *np, - struct pinctrl_gpio_range *range) +struct pinctrl_dev *of_pinctrl_get(struct device_node *np) { return NULL; } - #endif /* CONFIG_OF */ extern const char *pinctrl_dev_get_name(struct pinctrl_dev *pctldev); -- cgit v1.2.3 From 3f0f8670608766ef26a178d4e80cad3ce030fecc Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Tue, 20 Nov 2012 12:40:15 +0100 Subject: gpiolib: let gpiochip_add_pin_range() specify offset Like with commit 3c739ad0df5eb41cd7adad879eda6aa09879eb76 it is not always enough to specify all the pins of a gpio_chip from offset zero to be added to a pin map range, since the mapping from GPIO to pin controller may not be linear at all, but need to be broken into a few consecutive sub-ranges or 1-pin entries for complicated cases. The ranges may also be sparse. This alters the signature of the function to accept offsets into both the GPIO-chip local pinspace and the pin controller local pinspace. Reviewed-by: Stephen Warren Reviewed-by: Viresh Kumar Signed-off-by: Linus Walleij --- drivers/gpio/gpiolib-of.c | 12 ++++++++++++ drivers/gpio/gpiolib.c | 32 +++++++++++++++++++++++++++++--- include/asm-generic/gpio.h | 6 ++++-- include/linux/gpio.h | 3 ++- 4 files changed, 47 insertions(+), 6 deletions(-) (limited to 'drivers/gpio') diff --git a/drivers/gpio/gpiolib-of.c b/drivers/gpio/gpiolib-of.c index a40cd84c5c10..d542a141811a 100644 --- a/drivers/gpio/gpiolib-of.c +++ b/drivers/gpio/gpiolib-of.c @@ -238,8 +238,20 @@ static void of_gpiochip_add_pin_range(struct gpio_chip *chip) 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]); diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c index c5f650095faa..6d13bea4778a 100644 --- a/drivers/gpio/gpiolib.c +++ b/drivers/gpio/gpiolib.c @@ -1187,24 +1187,45 @@ 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 + * @offset: the start offset in the current gpio_chip number space + * @pin_base: 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 pin_base, unsigned int npins) + unsigned int offset, unsigned int pin_base, + unsigned int npins) { struct gpio_pin_range *pin_range; - pin_range = devm_kzalloc(chip->dev, sizeof(*pin_range), GFP_KERNEL); + 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 = offset; + pin_range->range.gc = chip; pin_range->range.name = chip->label; - pin_range->range.base = chip->base; + pin_range->range.base = chip->base + offset; pin_range->range.pin_base = pin_base; pin_range->range.npins = npins; pin_range->pctldev = find_pinctrl_and_add_gpio_range(pinctl_name, &pin_range->range); + if (!pin_range->pctldev) { + pr_err("%s: GPIO chip: could not create pin range\n", + chip->label); + kfree(pin_range); + } + pr_debug("%s: GPIO chip: created GPIO range %d->%d ==> PIN %d->%d\n", + chip->label, offset, offset + npins - 1, + pin_base, pin_base + npins - 1); list_add_tail(&pin_range->node, &chip->pin_ranges); @@ -1212,6 +1233,10 @@ int gpiochip_add_pin_range(struct gpio_chip *chip, const char *pinctl_name, } 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; @@ -1220,6 +1245,7 @@ void gpiochip_remove_pin_ranges(struct gpio_chip *chip) 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); diff --git a/include/asm-generic/gpio.h b/include/asm-generic/gpio.h index 2b84fc32fae2..ec58fdbddb59 100644 --- a/include/asm-generic/gpio.h +++ b/include/asm-generic/gpio.h @@ -283,14 +283,16 @@ struct gpio_pin_range { }; int gpiochip_add_pin_range(struct gpio_chip *chip, const char *pinctl_name, - unsigned int pin_base, unsigned int npins); + unsigned int offset, unsigned int pin_base, + unsigned int npins); void gpiochip_remove_pin_ranges(struct gpio_chip *chip); #else static inline int gpiochip_add_pin_range(struct gpio_chip *chip, const char *pinctl_name, - unsigned int pin_base, unsigned int npins) + unsigned int offset, unsigned int pin_base, + unsigned int npins) { return 0; } diff --git a/include/linux/gpio.h b/include/linux/gpio.h index 7ba2762abbc9..99861c65dd8b 100644 --- a/include/linux/gpio.h +++ b/include/linux/gpio.h @@ -233,7 +233,8 @@ static inline int irq_to_gpio(unsigned irq) static inline int gpiochip_add_pin_range(struct gpio_chip *chip, const char *pinctl_name, - unsigned int pin_base, unsigned int npins) + unsigned int offset, unsigned int pin_base, + unsigned int npins) { WARN_ON(1); return -EINVAL; -- cgit v1.2.3 From 192c369c6165dff233f22aa70209a92b030d233d Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Tue, 20 Nov 2012 14:03:37 +0100 Subject: gpiolib: rename find_pinctrl_* Rename the function find_pinctrl_and_add_gpio_range() to pinctrl_find_and_add_gpio_range() so as to be consistent with the rest of the functions. Reviewed-by: Stephen Warren Reviewed-by: Viresh Kumar Signed-off-by: Linus Walleij --- drivers/gpio/gpiolib.c | 2 +- drivers/pinctrl/core.c | 4 ++-- include/linux/pinctrl/pinctrl.h | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) (limited to 'drivers/gpio') diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c index 6d13bea4778a..a59d13d746c5 100644 --- a/drivers/gpio/gpiolib.c +++ b/drivers/gpio/gpiolib.c @@ -1216,7 +1216,7 @@ int gpiochip_add_pin_range(struct gpio_chip *chip, const char *pinctl_name, pin_range->range.base = chip->base + offset; pin_range->range.pin_base = pin_base; pin_range->range.npins = npins; - pin_range->pctldev = find_pinctrl_and_add_gpio_range(pinctl_name, + pin_range->pctldev = pinctrl_find_and_add_gpio_range(pinctl_name, &pin_range->range); if (!pin_range->pctldev) { pr_err("%s: GPIO chip: could not create pin range\n", diff --git a/drivers/pinctrl/core.c b/drivers/pinctrl/core.c index 71db586b2afd..15f5ac864f14 100644 --- a/drivers/pinctrl/core.c +++ b/drivers/pinctrl/core.c @@ -345,7 +345,7 @@ void pinctrl_add_gpio_ranges(struct pinctrl_dev *pctldev, } EXPORT_SYMBOL_GPL(pinctrl_add_gpio_ranges); -struct pinctrl_dev *find_pinctrl_and_add_gpio_range(const char *devname, +struct pinctrl_dev *pinctrl_find_and_add_gpio_range(const char *devname, struct pinctrl_gpio_range *range) { struct pinctrl_dev *pctldev = get_pinctrl_dev_from_devname(devname); @@ -356,7 +356,7 @@ struct pinctrl_dev *find_pinctrl_and_add_gpio_range(const char *devname, pinctrl_add_gpio_range(pctldev, range); return pctldev; } -EXPORT_SYMBOL_GPL(find_pinctrl_and_add_gpio_range); +EXPORT_SYMBOL_GPL(pinctrl_find_and_add_gpio_range); /** * pinctrl_remove_gpio_range() - remove a range of GPIOs fro a pin controller diff --git a/include/linux/pinctrl/pinctrl.h b/include/linux/pinctrl/pinctrl.h index 4a58428bc793..7dee9acbd271 100644 --- a/include/linux/pinctrl/pinctrl.h +++ b/include/linux/pinctrl/pinctrl.h @@ -137,7 +137,7 @@ extern void pinctrl_add_gpio_ranges(struct pinctrl_dev *pctldev, extern void pinctrl_remove_gpio_range(struct pinctrl_dev *pctldev, struct pinctrl_gpio_range *range); -extern struct pinctrl_dev *find_pinctrl_and_add_gpio_range(const char *devname, +extern struct pinctrl_dev *pinctrl_find_and_add_gpio_range(const char *devname, struct pinctrl_gpio_range *range); #ifdef CONFIG_OF -- cgit v1.2.3 From 8f23ca1a73a096f21e6618d8e23df613daa5e532 Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Tue, 20 Nov 2012 14:56:25 +0100 Subject: gpiolib: return any error code from range creation If we try to create a range for a certain GPIO chip and the target pin controller is not yet available it may return a probe deferral error code, so handle this all the way our by checking the error code. Reviewed-by: Stephen Warren Reviewed-by: Viresh Kumar Signed-off-by: Linus Walleij --- drivers/gpio/gpiolib.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'drivers/gpio') diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c index a59d13d746c5..317ff0440e29 100644 --- a/drivers/gpio/gpiolib.c +++ b/drivers/gpio/gpiolib.c @@ -1218,10 +1218,11 @@ int gpiochip_add_pin_range(struct gpio_chip *chip, const char *pinctl_name, pin_range->range.npins = npins; pin_range->pctldev = pinctrl_find_and_add_gpio_range(pinctl_name, &pin_range->range); - if (!pin_range->pctldev) { + if (IS_ERR(pin_range->pctldev)) { pr_err("%s: GPIO chip: could not create pin range\n", chip->label); kfree(pin_range); + return PTR_ERR(pin_range->pctldev); } pr_debug("%s: GPIO chip: created GPIO range %d->%d ==> PIN %d->%d\n", chip->label, offset, offset + npins - 1, -- cgit v1.2.3 From 316511c0134acec8f4ea730bd1897c7a1124a7c1 Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Wed, 21 Nov 2012 08:48:09 +0100 Subject: gpiolib: rename pin range arguments To be crystal clear on what the arguments mean in this funtion dealing with both GPIO and PIN ranges with confusing naming, we now have gpio_offset and pin_offset and we are on the clear that these are offsets into the specific GPIO and pin controller respectively. The GPIO chip itself will of course keep track of the base offset into the global GPIO number space. Reviewed-by: Viresh Kumar Signed-off-by: Linus Walleij --- drivers/gpio/gpiolib.c | 19 ++++++++++--------- include/asm-generic/gpio.h | 4 ++-- include/linux/gpio.h | 2 +- 3 files changed, 13 insertions(+), 12 deletions(-) (limited to 'drivers/gpio') diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c index 317ff0440e29..26e27c1fecb1 100644 --- a/drivers/gpio/gpiolib.c +++ b/drivers/gpio/gpiolib.c @@ -1191,13 +1191,13 @@ EXPORT_SYMBOL_GPL(gpiochip_find); * 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 - * @offset: the start offset in the current gpio_chip number space - * @pin_base: the start offset in the pin controller number space + * @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 offset, unsigned int pin_base, + unsigned int gpio_offset, unsigned int pin_offset, unsigned int npins) { struct gpio_pin_range *pin_range; @@ -1210,11 +1210,11 @@ int gpiochip_add_pin_range(struct gpio_chip *chip, const char *pinctl_name, } /* Use local offset as range ID */ - pin_range->range.id = offset; + pin_range->range.id = gpio_offset; pin_range->range.gc = chip; pin_range->range.name = chip->label; - pin_range->range.base = chip->base + offset; - pin_range->range.pin_base = pin_base; + 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); @@ -1224,9 +1224,10 @@ int gpiochip_add_pin_range(struct gpio_chip *chip, const char *pinctl_name, kfree(pin_range); return PTR_ERR(pin_range->pctldev); } - pr_debug("%s: GPIO chip: created GPIO range %d->%d ==> PIN %d->%d\n", - chip->label, offset, offset + npins - 1, - pin_base, pin_base + npins - 1); + 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); diff --git a/include/asm-generic/gpio.h b/include/asm-generic/gpio.h index ec58fdbddb59..9fd3093d855a 100644 --- a/include/asm-generic/gpio.h +++ b/include/asm-generic/gpio.h @@ -283,7 +283,7 @@ struct gpio_pin_range { }; int gpiochip_add_pin_range(struct gpio_chip *chip, const char *pinctl_name, - unsigned int offset, unsigned int pin_base, + unsigned int gpio_offset, unsigned int pin_offset, unsigned int npins); void gpiochip_remove_pin_ranges(struct gpio_chip *chip); @@ -291,7 +291,7 @@ void gpiochip_remove_pin_ranges(struct gpio_chip *chip); static inline int gpiochip_add_pin_range(struct gpio_chip *chip, const char *pinctl_name, - unsigned int offset, unsigned int pin_base, + unsigned int gpio_offset, unsigned int pin_offset, unsigned int npins) { return 0; diff --git a/include/linux/gpio.h b/include/linux/gpio.h index 99861c65dd8b..bfe665621536 100644 --- a/include/linux/gpio.h +++ b/include/linux/gpio.h @@ -233,7 +233,7 @@ static inline int irq_to_gpio(unsigned irq) static inline int gpiochip_add_pin_range(struct gpio_chip *chip, const char *pinctl_name, - unsigned int offset, unsigned int pin_base, + unsigned int gpio_offset, unsigned int pin_offset, unsigned int npins) { WARN_ON(1); -- cgit v1.2.3 From b4d4b1f087b9d4d730eb70f24032395d7cd7e591 Mon Sep 17 00:00:00 2001 From: Axel Lin Date: Wed, 21 Nov 2012 14:33:56 +0800 Subject: gpiolib: Fix use after free in gpiochip_add_pin_range This is introduced by commit 9ab6e988 "gpiolib: return any error code from range creation". Signed-off-by: Axel Lin Signed-off-by: Linus Walleij --- drivers/gpio/gpiolib.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'drivers/gpio') diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c index 26e27c1fecb1..58b9838801c0 100644 --- a/drivers/gpio/gpiolib.c +++ b/drivers/gpio/gpiolib.c @@ -1201,6 +1201,7 @@ int gpiochip_add_pin_range(struct gpio_chip *chip, const char *pinctl_name, unsigned int npins) { struct gpio_pin_range *pin_range; + int ret; pin_range = kzalloc(sizeof(*pin_range), GFP_KERNEL); if (!pin_range) { @@ -1219,10 +1220,11 @@ int gpiochip_add_pin_range(struct gpio_chip *chip, const char *pinctl_name, 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 PTR_ERR(pin_range->pctldev); + 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, -- cgit v1.2.3