diff options
author | Grant Likely <grant.likely@secretlab.ca> | 2012-05-17 15:54:40 -0400 |
---|---|---|
committer | Grant Likely <grant.likely@secretlab.ca> | 2012-05-18 18:48:36 -0400 |
commit | 3d0f7cf0f3633f92ddeb767eb59cab73963d4dee (patch) | |
tree | be23f5d9657d558d690adc84b16bdd6d91668f47 /drivers/gpio/gpiolib-of.c | |
parent | 09d71ff19404b3957fab6de942fb8026ccfd8524 (diff) |
gpio: Adjust of_xlate API to support multiple GPIO chips
This patch changes the of_xlate API to make it possible for multiple
gpio_chips to refer to the same device tree node. This is useful for
banked GPIO controllers that use multiple gpio_chips for a single
device. With this change the core code will try calling of_xlate on
each gpio_chip that references the device_node and will return the
gpio number for the first one to return 'true'.
Tested-by: Roland Stigge <stigge@antcom.de>
Acked-by: Arnd Bergmann <arnd@arndb.de>
Signed-off-by: Grant Likely <grant.likely@secretlab.ca>
Diffstat (limited to 'drivers/gpio/gpiolib-of.c')
-rw-r--r-- | drivers/gpio/gpiolib-of.c | 80 |
1 files changed, 38 insertions, 42 deletions
diff --git a/drivers/gpio/gpiolib-of.c b/drivers/gpio/gpiolib-of.c index bf984b6dc477..d18068a9f3ec 100644 --- a/drivers/gpio/gpiolib-of.c +++ b/drivers/gpio/gpiolib-of.c | |||
@@ -15,11 +15,39 @@ | |||
15 | #include <linux/errno.h> | 15 | #include <linux/errno.h> |
16 | #include <linux/module.h> | 16 | #include <linux/module.h> |
17 | #include <linux/io.h> | 17 | #include <linux/io.h> |
18 | #include <linux/gpio.h> | ||
18 | #include <linux/of.h> | 19 | #include <linux/of.h> |
19 | #include <linux/of_address.h> | 20 | #include <linux/of_address.h> |
20 | #include <linux/of_gpio.h> | 21 | #include <linux/of_gpio.h> |
21 | #include <linux/slab.h> | 22 | #include <linux/slab.h> |
22 | 23 | ||
24 | /* Private data structure for of_gpiochip_is_match */ | ||
25 | struct gg_data { | ||
26 | enum of_gpio_flags *flags; | ||
27 | struct of_phandle_args gpiospec; | ||
28 | |||
29 | int out_gpio; | ||
30 | }; | ||
31 | |||
32 | /* Private function for resolving node pointer to gpio_chip */ | ||
33 | static int of_gpiochip_find_and_xlate(struct gpio_chip *gc, void *data) | ||
34 | { | ||
35 | struct gg_data *gg_data = data; | ||
36 | int ret; | ||
37 | |||
38 | if ((gc->of_node != gg_data->gpiospec.np) || | ||
39 | (gc->of_gpio_n_cells != gg_data->gpiospec.args_count) || | ||
40 | (!gc->of_xlate)) | ||
41 | return false; | ||
42 | |||
43 | ret = gc->of_xlate(gc, &gg_data->gpiospec, gg_data->flags); | ||
44 | if (ret < 0) | ||
45 | return false; | ||
46 | |||
47 | gg_data->out_gpio = ret + gc->base; | ||
48 | return true; | ||
49 | } | ||
50 | |||
23 | /** | 51 | /** |
24 | * of_get_named_gpio_flags() - Get a GPIO number and flags to use with GPIO API | 52 | * of_get_named_gpio_flags() - Get a GPIO number and flags to use with GPIO API |
25 | * @np: device node to get GPIO from | 53 | * @np: device node to get GPIO from |
@@ -34,46 +62,25 @@ | |||
34 | int of_get_named_gpio_flags(struct device_node *np, const char *propname, | 62 | int of_get_named_gpio_flags(struct device_node *np, const char *propname, |
35 | int index, enum of_gpio_flags *flags) | 63 | int index, enum of_gpio_flags *flags) |
36 | { | 64 | { |
65 | struct gg_data gg_data = { .flags = flags, .out_gpio = -ENODEV }; | ||
37 | int ret; | 66 | int ret; |
38 | struct gpio_chip *gc; | 67 | |
39 | struct of_phandle_args gpiospec; | 68 | /* .of_xlate might decide to not fill in the flags, so clear it. */ |
69 | if (flags) | ||
70 | *flags = 0; | ||
40 | 71 | ||
41 | ret = of_parse_phandle_with_args(np, propname, "#gpio-cells", index, | 72 | ret = of_parse_phandle_with_args(np, propname, "#gpio-cells", index, |
42 | &gpiospec); | 73 | &gg_data.gpiospec); |
43 | if (ret) { | 74 | if (ret) { |
44 | pr_debug("%s: can't parse gpios property\n", __func__); | 75 | pr_debug("%s: can't parse gpios property\n", __func__); |
45 | goto err0; | 76 | return -EINVAL; |
46 | } | ||
47 | |||
48 | gc = of_node_to_gpiochip(gpiospec.np); | ||
49 | if (!gc) { | ||
50 | pr_debug("%s: gpio controller %s isn't registered\n", | ||
51 | np->full_name, gpiospec.np->full_name); | ||
52 | ret = -ENODEV; | ||
53 | goto err1; | ||
54 | } | ||
55 | |||
56 | if (gpiospec.args_count != gc->of_gpio_n_cells) { | ||
57 | pr_debug("%s: wrong #gpio-cells for %s\n", | ||
58 | np->full_name, gpiospec.np->full_name); | ||
59 | ret = -EINVAL; | ||
60 | goto err1; | ||
61 | } | 77 | } |
62 | 78 | ||
63 | /* .xlate might decide to not fill in the flags, so clear it. */ | 79 | gpiochip_find(&gg_data, of_gpiochip_find_and_xlate); |
64 | if (flags) | ||
65 | *flags = 0; | ||
66 | |||
67 | ret = gc->of_xlate(gc, &gpiospec, flags); | ||
68 | if (ret < 0) | ||
69 | goto err1; | ||
70 | 80 | ||
71 | ret += gc->base; | 81 | of_node_put(gg_data.gpiospec.np); |
72 | err1: | ||
73 | of_node_put(gpiospec.np); | ||
74 | err0: | ||
75 | pr_debug("%s exited with status %d\n", __func__, ret); | 82 | pr_debug("%s exited with status %d\n", __func__, ret); |
76 | return ret; | 83 | return gg_data.out_gpio; |
77 | } | 84 | } |
78 | EXPORT_SYMBOL(of_get_named_gpio_flags); | 85 | EXPORT_SYMBOL(of_get_named_gpio_flags); |
79 | 86 | ||
@@ -227,14 +234,3 @@ void of_gpiochip_remove(struct gpio_chip *chip) | |||
227 | if (chip->of_node) | 234 | if (chip->of_node) |
228 | of_node_put(chip->of_node); | 235 | of_node_put(chip->of_node); |
229 | } | 236 | } |
230 | |||
231 | /* Private function for resolving node pointer to gpio_chip */ | ||
232 | static int of_gpiochip_is_match(struct gpio_chip *chip, const void *data) | ||
233 | { | ||
234 | return chip->of_node == data; | ||
235 | } | ||
236 | |||
237 | struct gpio_chip *of_node_to_gpiochip(struct device_node *np) | ||
238 | { | ||
239 | return gpiochip_find(np, of_gpiochip_is_match); | ||
240 | } | ||