This patch series adds power management support for STM32 LP Timer:
- PWM driver
- Document the pinctrl states for sleep mode
It also adds device link between the PWM consumer and the PWM provider.
This allows proper sequencing for suspend/resume (e.g. user will likely
do a pwm_disable() before the PWM provider suspend executes), see [1].
[1] https://lkml.org/lkml/2019/2/5/770
---
Changes in v2:
- Don't disable PWM channel in PWM provider: rather refuse to suspend
and report an error as suggested by Uwe and Thierry.
- Add patch 3/3 to propose device link addition.
- No updates for STM32 LP Timer IIO driver. Patches can be send separately.
Fabrice Gasnier (3):
dt-bindings: pwm-stm32-lp: document pinctrl sleep state
pwm: stm32-lp: Add power management support
pwm: core: add consumer device link
.../devicetree/bindings/pwm/pwm-stm32-lp.txt | 9 +++++---
drivers/pwm/core.c | 13 +++++++++--
drivers/pwm/pwm-stm32-lp.c | 25 ++++++++++++++++++++++
3 files changed, 42 insertions(+), 5 deletions(-)
--
1.9.1
Add suspend/resume PM sleep ops. When going to low power, enforce the PWM
channel isn't active. Let the PWM consumers disable it during their own
suspend sequence. Only perform a check here, and handle the pinctrl states.
See [1].
[1] https://lkml.org/lkml/2019/2/5/770
Signed-off-by: Fabrice Gasnier <[email protected]>
---
Changes in v2:
- Don't disable PWM channel: let the PWM user disable it. Refuse to
suspend in case it's been left enabled.
- Drop the ifdefs, use __maybe_unused instead.
---
drivers/pwm/pwm-stm32-lp.c | 25 +++++++++++++++++++++++++
1 file changed, 25 insertions(+)
diff --git a/drivers/pwm/pwm-stm32-lp.c b/drivers/pwm/pwm-stm32-lp.c
index 0059b24c..2211a64 100644
--- a/drivers/pwm/pwm-stm32-lp.c
+++ b/drivers/pwm/pwm-stm32-lp.c
@@ -13,6 +13,7 @@
#include <linux/mfd/stm32-lptimer.h>
#include <linux/module.h>
#include <linux/of.h>
+#include <linux/pinctrl/consumer.h>
#include <linux/platform_device.h>
#include <linux/pwm.h>
@@ -223,6 +224,29 @@ static int stm32_pwm_lp_remove(struct platform_device *pdev)
return pwmchip_remove(&priv->chip);
}
+static int __maybe_unused stm32_pwm_lp_suspend(struct device *dev)
+{
+ struct stm32_pwm_lp *priv = dev_get_drvdata(dev);
+ struct pwm_state state;
+
+ pwm_get_state(&priv->chip.pwms[0], &state);
+ if (state.enabled) {
+ dev_err(dev, "The consumer didn't stop us (%s)\n",
+ priv->chip.pwms[0].label);
+ return -EBUSY;
+ }
+
+ return pinctrl_pm_select_sleep_state(dev);
+}
+
+static int __maybe_unused stm32_pwm_lp_resume(struct device *dev)
+{
+ return pinctrl_pm_select_default_state(dev);
+}
+
+static SIMPLE_DEV_PM_OPS(stm32_pwm_lp_pm_ops, stm32_pwm_lp_suspend,
+ stm32_pwm_lp_resume);
+
static const struct of_device_id stm32_pwm_lp_of_match[] = {
{ .compatible = "st,stm32-pwm-lp", },
{},
@@ -235,6 +259,7 @@ static int stm32_pwm_lp_remove(struct platform_device *pdev)
.driver = {
.name = "stm32-pwm-lp",
.of_match_table = of_match_ptr(stm32_pwm_lp_of_match),
+ .pm = &stm32_pwm_lp_pm_ops,
},
};
module_platform_driver(stm32_pwm_lp_driver);
--
1.9.1
Add a device link between the PWM consumer and the PWM provider. This
enforces the PWM user to get suspended before the PWM provider. It
allows proper synchronization of suspend/resume sequences: the PWM user
is responsible for properly stopping PWM, before the provider gets
suspended: see [1]. Add the device link in:
- pwm_get()
- devm_pwm_get()
- devm_of_pwm_get()
as it requires a reference to the device for the PWM consumer.
[1] https://lkml.org/lkml/2019/2/5/770
Suggested-by: Thierry Reding <[email protected]>
Signed-off-by: Fabrice Gasnier <[email protected]>
---
drivers/pwm/core.c | 13 +++++++++++--
1 file changed, 11 insertions(+), 2 deletions(-)
diff --git a/drivers/pwm/core.c b/drivers/pwm/core.c
index 1581f6a..2835e27 100644
--- a/drivers/pwm/core.c
+++ b/drivers/pwm/core.c
@@ -770,8 +770,13 @@ struct pwm_device *pwm_get(struct device *dev, const char *con_id)
int err;
/* look up via DT first */
- if (IS_ENABLED(CONFIG_OF) && dev && dev->of_node)
- return of_pwm_get(dev->of_node, con_id);
+ if (IS_ENABLED(CONFIG_OF) && dev && dev->of_node) {
+ pwm = of_pwm_get(dev->of_node, con_id);
+ if (!IS_ERR(pwm))
+ device_link_add(dev, pwm->chip->dev,
+ DL_FLAG_AUTOREMOVE_CONSUMER);
+ return pwm;
+ }
/*
* We look up the provider in the static table typically provided by
@@ -851,6 +856,8 @@ struct pwm_device *pwm_get(struct device *dev, const char *con_id)
pwm->args.period = chosen->period;
pwm->args.polarity = chosen->polarity;
+ device_link_add(dev, pwm->chip->dev, DL_FLAG_AUTOREMOVE_CONSUMER);
+
return pwm;
}
EXPORT_SYMBOL_GPL(pwm_get);
@@ -943,6 +950,8 @@ struct pwm_device *devm_of_pwm_get(struct device *dev, struct device_node *np,
if (!IS_ERR(pwm)) {
*ptr = pwm;
devres_add(dev, ptr);
+ device_link_add(dev, pwm->chip->dev,
+ DL_FLAG_AUTOREMOVE_CONSUMER);
} else {
devres_free(ptr);
}
--
1.9.1
Add documentation for pinctrl sleep state on STM32 LPTimer PWM.
Signed-off-by: Fabrice Gasnier <[email protected]>
---
Documentation/devicetree/bindings/pwm/pwm-stm32-lp.txt | 9 ++++++---
1 file changed, 6 insertions(+), 3 deletions(-)
diff --git a/Documentation/devicetree/bindings/pwm/pwm-stm32-lp.txt b/Documentation/devicetree/bindings/pwm/pwm-stm32-lp.txt
index bd23302..6521bc4 100644
--- a/Documentation/devicetree/bindings/pwm/pwm-stm32-lp.txt
+++ b/Documentation/devicetree/bindings/pwm/pwm-stm32-lp.txt
@@ -11,8 +11,10 @@ Required parameters:
bindings defined in pwm.txt.
Optional properties:
-- pinctrl-names: Set to "default".
-- pinctrl-0: Phandle pointing to pin configuration node for PWM.
+- pinctrl-names: Set to "default". An additional "sleep" state can be
+ defined to set pins in sleep state when in low power.
+- pinctrl-n: Phandle(s) pointing to pin configuration node for PWM,
+ respectively for "default" and "sleep" states.
Example:
timer@40002400 {
@@ -21,7 +23,8 @@ Example:
pwm {
compatible = "st,stm32-pwm-lp";
#pwm-cells = <3>;
- pinctrl-names = "default";
+ pinctrl-names = "default", "sleep";
pinctrl-0 = <&lppwm1_pins>;
+ pinctrl-1 = <&lppwm1_sleep_pins>;
};
};
--
1.9.1
Hello Fabrice,
On Mon, Feb 11, 2019 at 05:12:02PM +0100, Fabrice Gasnier wrote:
> Add a device link between the PWM consumer and the PWM provider. This
> enforces the PWM user to get suspended before the PWM provider. It
> allows proper synchronization of suspend/resume sequences: the PWM user
> is responsible for properly stopping PWM, before the provider gets
> suspended: see [1]. Add the device link in:
> - pwm_get()
> - devm_pwm_get()
> - devm_of_pwm_get()
> as it requires a reference to the device for the PWM consumer.
>
> [1] https://lkml.org/lkml/2019/2/5/770
>
> Suggested-by: Thierry Reding <[email protected]>
> Signed-off-by: Fabrice Gasnier <[email protected]>
> ---
> drivers/pwm/core.c | 13 +++++++++++--
> 1 file changed, 11 insertions(+), 2 deletions(-)
>
> diff --git a/drivers/pwm/core.c b/drivers/pwm/core.c
> index 1581f6a..2835e27 100644
> --- a/drivers/pwm/core.c
> +++ b/drivers/pwm/core.c
> @@ -770,8 +770,13 @@ struct pwm_device *pwm_get(struct device *dev, const char *con_id)
> int err;
>
> /* look up via DT first */
> - if (IS_ENABLED(CONFIG_OF) && dev && dev->of_node)
> - return of_pwm_get(dev->of_node, con_id);
> + if (IS_ENABLED(CONFIG_OF) && dev && dev->of_node) {
> + pwm = of_pwm_get(dev->of_node, con_id);
> + if (!IS_ERR(pwm))
> + device_link_add(dev, pwm->chip->dev,
> + DL_FLAG_AUTOREMOVE_CONSUMER);
> + return pwm;
> + }
>
> /*
> * We look up the provider in the static table typically provided by
> @@ -851,6 +856,8 @@ struct pwm_device *pwm_get(struct device *dev, const char *con_id)
> pwm->args.period = chosen->period;
> pwm->args.polarity = chosen->polarity;
>
> + device_link_add(dev, pwm->chip->dev, DL_FLAG_AUTOREMOVE_CONSUMER);
> +
> return pwm;
> }
> EXPORT_SYMBOL_GPL(pwm_get);
> @@ -943,6 +950,8 @@ struct pwm_device *devm_of_pwm_get(struct device *dev, struct device_node *np,
> if (!IS_ERR(pwm)) {
> *ptr = pwm;
> devres_add(dev, ptr);
> + device_link_add(dev, pwm->chip->dev,
> + DL_FLAG_AUTOREMOVE_CONSUMER);
IMHO it's surprising that devm_of_pwm_get() does more than of_pwm_get()
+ devres stuff. I'd put device_link_add() into of_pwm_get().
Best regards
Uwe
--
Pengutronix e.K. | Uwe Kleine-K?nig |
Industrial Linux Solutions | http://www.pengutronix.de/ |
On 2/11/19 8:06 PM, Uwe Kleine-König wrote:
> Hello Fabrice,
>
> On Mon, Feb 11, 2019 at 05:12:02PM +0100, Fabrice Gasnier wrote:
>> Add a device link between the PWM consumer and the PWM provider. This
>> enforces the PWM user to get suspended before the PWM provider. It
>> allows proper synchronization of suspend/resume sequences: the PWM user
>> is responsible for properly stopping PWM, before the provider gets
>> suspended: see [1]. Add the device link in:
>> - pwm_get()
>> - devm_pwm_get()
>> - devm_of_pwm_get()
>> as it requires a reference to the device for the PWM consumer.
>>
>> [1] https://lkml.org/lkml/2019/2/5/770
>>
>> Suggested-by: Thierry Reding <[email protected]>
>> Signed-off-by: Fabrice Gasnier <[email protected]>
>> ---
>> drivers/pwm/core.c | 13 +++++++++++--
>> 1 file changed, 11 insertions(+), 2 deletions(-)
>>
>> diff --git a/drivers/pwm/core.c b/drivers/pwm/core.c
>> index 1581f6a..2835e27 100644
>> --- a/drivers/pwm/core.c
>> +++ b/drivers/pwm/core.c
>> @@ -770,8 +770,13 @@ struct pwm_device *pwm_get(struct device *dev, const char *con_id)
>> int err;
>>
>> /* look up via DT first */
>> - if (IS_ENABLED(CONFIG_OF) && dev && dev->of_node)
>> - return of_pwm_get(dev->of_node, con_id);
>> + if (IS_ENABLED(CONFIG_OF) && dev && dev->of_node) {
>> + pwm = of_pwm_get(dev->of_node, con_id);
>> + if (!IS_ERR(pwm))
>> + device_link_add(dev, pwm->chip->dev,
>> + DL_FLAG_AUTOREMOVE_CONSUMER);
>> + return pwm;
>> + }
>>
>> /*
>> * We look up the provider in the static table typically provided by
>> @@ -851,6 +856,8 @@ struct pwm_device *pwm_get(struct device *dev, const char *con_id)
>> pwm->args.period = chosen->period;
>> pwm->args.polarity = chosen->polarity;
>>
>> + device_link_add(dev, pwm->chip->dev, DL_FLAG_AUTOREMOVE_CONSUMER);
>> +
>> return pwm;
>> }
>> EXPORT_SYMBOL_GPL(pwm_get);
>> @@ -943,6 +950,8 @@ struct pwm_device *devm_of_pwm_get(struct device *dev, struct device_node *np,
>> if (!IS_ERR(pwm)) {
>> *ptr = pwm;
>> devres_add(dev, ptr);
>> + device_link_add(dev, pwm->chip->dev,
>> + DL_FLAG_AUTOREMOVE_CONSUMER);
>
> IMHO it's surprising that devm_of_pwm_get() does more than of_pwm_get()
> + devres stuff. I'd put device_link_add() into of_pwm_get().
Hi Uwe,
I also agree with this. But I think this implies modifying the API for
of_pwm_get():
/**
* of_pwm_get() - request a PWM via the PWM framework
+ * @dev: device for PWM consumer
* @np: device node to get the PWM from
* @con_id: consumer name
It seems there aren't much of_pwm_get() users currently.
Does this look sensible ?
Best regards,
Fabrice
>
> Best regards
> Uwe
>
Hello Fabrice,
On Tue, Feb 12, 2019 at 09:31:37AM +0100, Fabrice Gasnier wrote:
> On 2/11/19 8:06 PM, Uwe Kleine-K?nig wrote:
> > On Mon, Feb 11, 2019 at 05:12:02PM +0100, Fabrice Gasnier wrote:
> >> @@ -943,6 +950,8 @@ struct pwm_device *devm_of_pwm_get(struct device *dev, struct device_node *np,
> >> if (!IS_ERR(pwm)) {
> >> *ptr = pwm;
> >> devres_add(dev, ptr);
> >> + device_link_add(dev, pwm->chip->dev,
> >> + DL_FLAG_AUTOREMOVE_CONSUMER);
> >
> > IMHO it's surprising that devm_of_pwm_get() does more than of_pwm_get()
> > + devres stuff. I'd put device_link_add() into of_pwm_get().
>
> Hi Uwe,
>
> I also agree with this. But I think this implies modifying the API for
> of_pwm_get():
> /**
> * of_pwm_get() - request a PWM via the PWM framework
> + * @dev: device for PWM consumer
> * @np: device node to get the PWM from
> * @con_id: consumer name
>
> It seems there aren't much of_pwm_get() users currently.
> Does this look sensible ?
In my eyes this looks sensible, yes.
Best regards
Uwe
--
Pengutronix e.K. | Uwe Kleine-K?nig |
Industrial Linux Solutions | http://www.pengutronix.de/ |
On 2/12/19 10:03 AM, Uwe Kleine-König wrote:
> Hello Fabrice,
>
> On Tue, Feb 12, 2019 at 09:31:37AM +0100, Fabrice Gasnier wrote:
>> On 2/11/19 8:06 PM, Uwe Kleine-König wrote:
>>> On Mon, Feb 11, 2019 at 05:12:02PM +0100, Fabrice Gasnier wrote:
>>>> @@ -943,6 +950,8 @@ struct pwm_device *devm_of_pwm_get(struct device *dev, struct device_node *np,
>>>> if (!IS_ERR(pwm)) {
>>>> *ptr = pwm;
>>>> devres_add(dev, ptr);
>>>> + device_link_add(dev, pwm->chip->dev,
>>>> + DL_FLAG_AUTOREMOVE_CONSUMER);
>>>
>>> IMHO it's surprising that devm_of_pwm_get() does more than of_pwm_get()
>>> + devres stuff. I'd put device_link_add() into of_pwm_get().
>>
>> Hi Uwe,
>>
>> I also agree with this. But I think this implies modifying the API for
>> of_pwm_get():
>> /**
>> * of_pwm_get() - request a PWM via the PWM framework
>> + * @dev: device for PWM consumer
>> * @np: device node to get the PWM from
>> * @con_id: consumer name
>>
>> It seems there aren't much of_pwm_get() users currently.
>> Does this look sensible ?
>
> In my eyes this looks sensible, yes.
Hello Uwe,
I just sent a v3 with that change,
Thanks
Fabrice
>
> Best regards
> Uwe
>