pinctrl: Really force states during suspend/resume
authorFlorian Fainelli <f.fainelli@gmail.com>
Wed, 1 Mar 2017 18:32:57 +0000 (10:32 -0800)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Sat, 24 Mar 2018 10:01:26 +0000 (11:01 +0100)
[ 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

index 56fbe4c3e800ef78287d6bbc8433ec6724865654..c55517312485d5ab3f8257e2b7ada1fbe5856778 100644 (file)
@@ -1189,19 +1189,16 @@ struct pinctrl_state *pinctrl_lookup_state(struct pinctrl *p,
 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
@@ -1265,6 +1262,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)
@@ -1430,7 +1440,7 @@ void pinctrl_unregister_map(const struct pinctrl_map *map)
 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);
@@ -1442,7 +1452,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);