| From foo@baz Thu Mar 22 14:57:33 CET 2018 |
| From: Florian Fainelli <f.fainelli@gmail.com> |
| Date: Wed, 1 Mar 2017 10:32:57 -0800 |
| Subject: pinctrl: Really force states during suspend/resume |
| |
| From: Florian Fainelli <f.fainelli@gmail.com> |
| |
| |
| [ Upstream commit 981ed1bfbc6c4660b2ddaa8392893e20a6255048 ] |
| |
| In case a platform only defaults a "default" set of pins, but not a |
| "sleep" set of pins, and this particular platform suspends and resumes |
| in a way that the pin states are not preserved by the hardware, when we |
| resume, we would call pinctrl_single_resume() -> pinctrl_force_default() |
| -> pinctrl_select_state() and the first thing we do is check that the |
| pins state is the same as before, and do nothing. |
| |
| In order to fix this, decouple the actual state change from |
| pinctrl_select_state() and move it pinctrl_commit_state(), while keeping |
| the p->state == state check in pinctrl_select_state() not to change the |
| caller assumptions. pinctrl_force_sleep() and pinctrl_force_default() |
| are updated to bypass the state check by calling pinctrl_commit_state(). |
| |
| [Linus Walleij] |
| The forced pin control states are currently only used in some pin |
| controller drivers that grab their own reference to their own pins. |
| This is equal to the pin control hogs: pins taken by pin control |
| devices since there are no corresponding device in the Linux device |
| hierarchy, such as memory controller lines or unused GPIO lines, |
| or GPIO lines that are used orthogonally from the GPIO subsystem |
| but pincontrol-wise managed as hogs (non-strict mode, allowing |
| simultaneous use by GPIO and pin control). For this case forcing |
| the state from the drivers' suspend()/resume() callbacks makes |
| sense and should semantically match the name of the function. |
| |
| Fixes: 6e5e959dde0d ("pinctrl: API changes to support multiple states per device") |
| Signed-off-by: Florian Fainelli <f.fainelli@gmail.com> |
| Reviewed-by: Andy Shevchenko <andy.shevchenko@gmail.com> |
| Signed-off-by: Linus Walleij <linus.walleij@linaro.org> |
| Signed-off-by: Sasha Levin <alexander.levin@microsoft.com> |
| Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> |
| --- |
| drivers/pinctrl/core.c | 24 +++++++++++++++++------- |
| 1 file changed, 17 insertions(+), 7 deletions(-) |
| |
| --- a/drivers/pinctrl/core.c |
| +++ b/drivers/pinctrl/core.c |
| @@ -979,19 +979,16 @@ struct pinctrl_state *pinctrl_lookup_sta |
| EXPORT_SYMBOL_GPL(pinctrl_lookup_state); |
| |
| /** |
| - * pinctrl_select_state() - select/activate/program a pinctrl state to HW |
| + * pinctrl_commit_state() - select/activate/program a pinctrl state to HW |
| * @p: the pinctrl handle for the device that requests configuration |
| * @state: the state handle to select/activate/program |
| */ |
| -int pinctrl_select_state(struct pinctrl *p, struct pinctrl_state *state) |
| +static int pinctrl_commit_state(struct pinctrl *p, struct pinctrl_state *state) |
| { |
| struct pinctrl_setting *setting, *setting2; |
| struct pinctrl_state *old_state = p->state; |
| int ret; |
| |
| - if (p->state == state) |
| - return 0; |
| - |
| if (p->state) { |
| /* |
| * For each pinmux setting in the old state, forget SW's record |
| @@ -1055,6 +1052,19 @@ unapply_new_state: |
| |
| return ret; |
| } |
| + |
| +/** |
| + * pinctrl_select_state() - select/activate/program a pinctrl state to HW |
| + * @p: the pinctrl handle for the device that requests configuration |
| + * @state: the state handle to select/activate/program |
| + */ |
| +int pinctrl_select_state(struct pinctrl *p, struct pinctrl_state *state) |
| +{ |
| + if (p->state == state) |
| + return 0; |
| + |
| + return pinctrl_commit_state(p, state); |
| +} |
| EXPORT_SYMBOL_GPL(pinctrl_select_state); |
| |
| static void devm_pinctrl_release(struct device *dev, void *res) |
| @@ -1223,7 +1233,7 @@ void pinctrl_unregister_map(struct pinct |
| int pinctrl_force_sleep(struct pinctrl_dev *pctldev) |
| { |
| if (!IS_ERR(pctldev->p) && !IS_ERR(pctldev->hog_sleep)) |
| - return pinctrl_select_state(pctldev->p, pctldev->hog_sleep); |
| + return pinctrl_commit_state(pctldev->p, pctldev->hog_sleep); |
| return 0; |
| } |
| EXPORT_SYMBOL_GPL(pinctrl_force_sleep); |
| @@ -1235,7 +1245,7 @@ EXPORT_SYMBOL_GPL(pinctrl_force_sleep); |
| int pinctrl_force_default(struct pinctrl_dev *pctldev) |
| { |
| if (!IS_ERR(pctldev->p) && !IS_ERR(pctldev->hog_default)) |
| - return pinctrl_select_state(pctldev->p, pctldev->hog_default); |
| + return pinctrl_commit_state(pctldev->p, pctldev->hog_default); |
| return 0; |
| } |
| EXPORT_SYMBOL_GPL(pinctrl_force_default); |