| From 6697baae87bbd04741c77c498d560a16edceb42a Mon Sep 17 00:00:00 2001 |
| From: Stephen Warren <swarren@nvidia.com> |
| Date: Mon, 16 Apr 2012 10:51:00 -0600 |
| Subject: pinctrl: implement devm_pinctrl_get()/put() |
| |
| commit 6d4ca1fb467932773da7b808c52f3d7ef4461ba0 upstream. |
| |
| These functions allow the driver core to automatically clean up any |
| allocations made by drivers, thus leading to simplified drivers. |
| |
| Signed-off-by: Stephen Warren <swarren@nvidia.com> |
| Signed-off-by: Linus Walleij <linus.walleij@linaro.org> |
| --- |
| Documentation/driver-model/devres.txt | 4 ++ |
| Documentation/pinctrl.txt | 43 +++++++++++++++----------- |
| drivers/pinctrl/core.c | 56 ++++++++++++++++++++++++++++++++++ |
| include/linux/pinctrl/consumer.h | 44 ++++++++++++++++++++++++++ |
| 4 files changed, 130 insertions(+), 17 deletions(-) |
| |
| --- a/Documentation/driver-model/devres.txt |
| +++ b/Documentation/driver-model/devres.txt |
| @@ -276,3 +276,7 @@ REGULATOR |
| devm_regulator_get() |
| devm_regulator_put() |
| devm_regulator_bulk_get() |
| + |
| +PINCTRL |
| + devm_pinctrl_get() |
| + devm_pinctrl_put() |
| --- a/Documentation/pinctrl.txt |
| +++ b/Documentation/pinctrl.txt |
| @@ -951,13 +951,13 @@ case), we define a mapping like this: |
| The result of grabbing this mapping from the device with something like |
| this (see next paragraph): |
| |
| - p = pinctrl_get(dev); |
| + p = devm_pinctrl_get(dev); |
| s = pinctrl_lookup_state(p, "8bit"); |
| ret = pinctrl_select_state(p, s); |
| |
| or more simply: |
| |
| - p = pinctrl_get_select(dev, "8bit"); |
| + p = devm_pinctrl_get_select(dev, "8bit"); |
| |
| Will be that you activate all the three bottom records in the mapping at |
| once. Since they share the same name, pin controller device, function and |
| @@ -991,7 +991,7 @@ foo_probe() |
| /* Allocate a state holder named "foo" etc */ |
| struct foo_state *foo = ...; |
| |
| - foo->p = pinctrl_get(&device); |
| + foo->p = devm_pinctrl_get(&device); |
| if (IS_ERR(foo->p)) { |
| /* FIXME: clean up "foo" here */ |
| return PTR_ERR(foo->p); |
| @@ -999,24 +999,17 @@ foo_probe() |
| |
| foo->s = pinctrl_lookup_state(foo->p, PINCTRL_STATE_DEFAULT); |
| if (IS_ERR(foo->s)) { |
| - pinctrl_put(foo->p); |
| /* FIXME: clean up "foo" here */ |
| return PTR_ERR(s); |
| } |
| |
| ret = pinctrl_select_state(foo->s); |
| if (ret < 0) { |
| - pinctrl_put(foo->p); |
| /* FIXME: clean up "foo" here */ |
| return ret; |
| } |
| } |
| |
| -foo_remove() |
| -{ |
| - pinctrl_put(state->p); |
| -} |
| - |
| This get/lookup/select/put sequence can just as well be handled by bus drivers |
| if you don't want each and every driver to handle it and you know the |
| arrangement on your bus. |
| @@ -1028,6 +1021,21 @@ The semantics of the pinctrl APIs are: |
| kernel memory to hold the pinmux state. All mapping table parsing or similar |
| slow operations take place within this API. |
| |
| +- devm_pinctrl_get() is a variant of pinctrl_get() that causes pinctrl_put() |
| + to be called automatically on the retrieved pointer when the associated |
| + device is removed. It is recommended to use this function over plain |
| + pinctrl_get(). |
| + |
| +- devm_pinctrl_put() is a variant of pinctrl_put() that may be used to |
| + explicitly destroy a pinctrl object returned by devm_pinctrl_get(). |
| + However, use of this function will be rare, due to the automatic cleanup |
| + that will occur even without calling it. |
| + |
| + pinctrl_get() must be paired with a plain pinctrl_put(). |
| + pinctrl_get() may not be paired with devm_pinctrl_put(). |
| + devm_pinctrl_get() can optionally be paired with devm_pinctrl_put(). |
| + devm_pinctrl_get() may not be paired with plain pinctrl_put(). |
| + |
| - pinctrl_lookup_state() is called in process context to obtain a handle to a |
| specific state for a the client device. This operation may be slow too. |
| |
| @@ -1094,13 +1102,13 @@ on the pins defined by group B: |
| |
| #include <linux/pinctrl/consumer.h> |
| |
| -foo_switch() |
| -{ |
| - struct pinctrl *p; |
| - struct pinctrl_state *s1, *s2; |
| +struct pinctrl *p; |
| +struct pinctrl_state *s1, *s2; |
| |
| +foo_probe() |
| +{ |
| /* Setup */ |
| - p = pinctrl_get(&device); |
| + p = devm_pinctrl_get(&device); |
| if (IS_ERR(p)) |
| ... |
| |
| @@ -1111,7 +1119,10 @@ foo_switch() |
| s2 = pinctrl_lookup_state(foo->p, "pos-B"); |
| if (IS_ERR(s2)) |
| ... |
| +} |
| |
| +foo_switch() |
| +{ |
| /* Enable on position A */ |
| ret = pinctrl_select_state(s1); |
| if (ret < 0) |
| @@ -1125,8 +1136,6 @@ foo_switch() |
| ... |
| |
| ... |
| - |
| - pinctrl_put(p); |
| } |
| |
| The above has to be done from process context. |
| --- a/drivers/pinctrl/core.c |
| +++ b/drivers/pinctrl/core.c |
| @@ -23,6 +23,7 @@ |
| #include <linux/sysfs.h> |
| #include <linux/debugfs.h> |
| #include <linux/seq_file.h> |
| +#include <linux/pinctrl/consumer.h> |
| #include <linux/pinctrl/pinctrl.h> |
| #include <linux/pinctrl/machine.h> |
| #include "core.h" |
| @@ -797,6 +798,61 @@ int pinctrl_select_state(struct pinctrl |
| } |
| EXPORT_SYMBOL_GPL(pinctrl_select_state); |
| |
| +static void devm_pinctrl_release(struct device *dev, void *res) |
| +{ |
| + pinctrl_put(*(struct pinctrl **)res); |
| +} |
| + |
| +/** |
| + * struct devm_pinctrl_get() - Resource managed pinctrl_get() |
| + * @dev: the device to obtain the handle for |
| + * |
| + * If there is a need to explicitly destroy the returned struct pinctrl, |
| + * devm_pinctrl_put() should be used, rather than plain pinctrl_put(). |
| + */ |
| +struct pinctrl *devm_pinctrl_get(struct device *dev) |
| +{ |
| + struct pinctrl **ptr, *p; |
| + |
| + ptr = devres_alloc(devm_pinctrl_release, sizeof(*ptr), GFP_KERNEL); |
| + if (!ptr) |
| + return ERR_PTR(-ENOMEM); |
| + |
| + p = pinctrl_get(dev); |
| + if (!IS_ERR(p)) { |
| + *ptr = p; |
| + devres_add(dev, ptr); |
| + } else { |
| + devres_free(ptr); |
| + } |
| + |
| + return p; |
| +} |
| +EXPORT_SYMBOL_GPL(devm_pinctrl_get); |
| + |
| +static int devm_pinctrl_match(struct device *dev, void *res, void *data) |
| +{ |
| + struct pinctrl **p = res; |
| + |
| + return *p == data; |
| +} |
| + |
| +/** |
| + * devm_pinctrl_put() - Resource managed pinctrl_put() |
| + * @p: the pinctrl handle to release |
| + * |
| + * Deallocate a struct pinctrl obtained via devm_pinctrl_get(). Normally |
| + * this function will not need to be called and the resource management |
| + * code will ensure that the resource is freed. |
| + */ |
| +void devm_pinctrl_put(struct pinctrl *p) |
| +{ |
| + WARN_ON(devres_destroy(p->dev, devm_pinctrl_release, |
| + devm_pinctrl_match, p)); |
| + pinctrl_put(p); |
| +} |
| +EXPORT_SYMBOL_GPL(devm_pinctrl_put); |
| + |
| int pinctrl_register_map(struct pinctrl_map const *maps, unsigned num_maps, |
| bool dup, bool locked) |
| { |
| --- a/include/linux/pinctrl/consumer.h |
| +++ b/include/linux/pinctrl/consumer.h |
| @@ -36,6 +36,9 @@ extern struct pinctrl_state * __must_che |
| const char *name); |
| extern int pinctrl_select_state(struct pinctrl *p, struct pinctrl_state *s); |
| |
| +extern struct pinctrl * __must_check devm_pinctrl_get(struct device *dev); |
| +extern void devm_pinctrl_put(struct pinctrl *p); |
| + |
| #else /* !CONFIG_PINCTRL */ |
| |
| static inline int pinctrl_request_gpio(unsigned gpio) |
| @@ -79,6 +82,15 @@ static inline int pinctrl_select_state(s |
| return 0; |
| } |
| |
| +static inline struct pinctrl * __must_check devm_pinctrl_get(struct device *dev) |
| +{ |
| + return NULL; |
| +} |
| + |
| +static inline void devm_pinctrl_put(struct pinctrl *p) |
| +{ |
| +} |
| + |
| #endif /* CONFIG_PINCTRL */ |
| |
| static inline struct pinctrl * __must_check pinctrl_get_select( |
| @@ -113,6 +125,38 @@ static inline struct pinctrl * __must_ch |
| return pinctrl_get_select(dev, PINCTRL_STATE_DEFAULT); |
| } |
| |
| +static inline struct pinctrl * __must_check devm_pinctrl_get_select( |
| + struct device *dev, const char *name) |
| +{ |
| + struct pinctrl *p; |
| + struct pinctrl_state *s; |
| + int ret; |
| + |
| + p = devm_pinctrl_get(dev); |
| + if (IS_ERR(p)) |
| + return p; |
| + |
| + s = pinctrl_lookup_state(p, name); |
| + if (IS_ERR(s)) { |
| + devm_pinctrl_put(p); |
| + return ERR_PTR(PTR_ERR(s)); |
| + } |
| + |
| + ret = pinctrl_select_state(p, s); |
| + if (ret < 0) { |
| + devm_pinctrl_put(p); |
| + return ERR_PTR(ret); |
| + } |
| + |
| + return p; |
| +} |
| + |
| +static inline struct pinctrl * __must_check devm_pinctrl_get_select_default( |
| + struct device *dev) |
| +{ |
| + return devm_pinctrl_get_select(dev, PINCTRL_STATE_DEFAULT); |
| +} |
| + |
| #ifdef CONFIG_PINCONF |
| |
| extern int pin_config_get(const char *dev_name, const char *name, |