This series adds power management support to stm32-timer-trigger driver,
it adds suspend/resume routines. A small precursor patch is used to rename
"enabled" flag.
Fabrice Gasnier (2):
iio: trigger: stm32-timer: rename enabled flag
iio: trigger: stm32-timer: add power management support
drivers/iio/trigger/stm32-timer-trigger.c | 91 ++++++++++++++++++++++++++-----
1 file changed, 77 insertions(+), 14 deletions(-)
--
2.7.4
Add suspend/resume PM sleep ops to stm32-timer-trigger driver.
Register contents may be lost depending on low power modes.
When going to low power, enforce the timer isn't active. Gracefully
restore its state upon resume in case it's been left enabled prior to
suspend.
Signed-off-by: Fabrice Gasnier <[email protected]>
---
drivers/iio/trigger/stm32-timer-trigger.c | 63 +++++++++++++++++++++++++++++++
1 file changed, 63 insertions(+)
diff --git a/drivers/iio/trigger/stm32-timer-trigger.c b/drivers/iio/trigger/stm32-timer-trigger.c
index 32e1249..37545a8 100644
--- a/drivers/iio/trigger/stm32-timer-trigger.c
+++ b/drivers/iio/trigger/stm32-timer-trigger.c
@@ -75,6 +75,15 @@ static const void *stm32h7_valids_table[][MAX_VALIDS] = {
{ }, /* timer 17 */
};
+struct stm32_timer_trigger_regs {
+ u32 cr1;
+ u32 cr2;
+ u32 psc;
+ u32 arr;
+ u32 cnt;
+ u32 smcr;
+};
+
struct stm32_timer_trigger {
struct device *dev;
struct regmap *regmap;
@@ -86,6 +95,7 @@ struct stm32_timer_trigger {
bool has_trgo2;
struct mutex lock; /* concurrent sysfs configuration */
struct list_head tr_list;
+ struct stm32_timer_trigger_regs bak;
};
struct stm32_timer_trigger_cfg {
@@ -812,6 +822,58 @@ static int stm32_timer_trigger_remove(struct platform_device *pdev)
return 0;
}
+static int __maybe_unused stm32_timer_trigger_suspend(struct device *dev)
+{
+ struct stm32_timer_trigger *priv = dev_get_drvdata(dev);
+
+ /* Only take care of enabled timer: don't disturb other MFD child */
+ if (priv->enabled) {
+ /* Backup registers that may get lost in low power mode */
+ regmap_read(priv->regmap, TIM_CR1, &priv->bak.cr1);
+ regmap_read(priv->regmap, TIM_CR2, &priv->bak.cr2);
+ regmap_read(priv->regmap, TIM_PSC, &priv->bak.psc);
+ regmap_read(priv->regmap, TIM_ARR, &priv->bak.arr);
+ regmap_read(priv->regmap, TIM_CNT, &priv->bak.cnt);
+ regmap_read(priv->regmap, TIM_SMCR, &priv->bak.smcr);
+
+ /* Disable the timer */
+ regmap_update_bits(priv->regmap, TIM_CR1, TIM_CR1_CEN, 0);
+ clk_disable(priv->clk);
+ }
+
+ return 0;
+}
+
+static int __maybe_unused stm32_timer_trigger_resume(struct device *dev)
+{
+ struct stm32_timer_trigger *priv = dev_get_drvdata(dev);
+ int ret;
+
+ if (priv->enabled) {
+ ret = clk_enable(priv->clk);
+ if (ret)
+ return ret;
+
+ /* restore master/slave modes */
+ regmap_write(priv->regmap, TIM_SMCR, priv->bak.smcr);
+ regmap_write(priv->regmap, TIM_CR2, priv->bak.cr2);
+
+ /* restore sampling_frequency (trgo / trgo2 triggers) */
+ regmap_write(priv->regmap, TIM_PSC, priv->bak.psc);
+ regmap_write(priv->regmap, TIM_ARR, priv->bak.arr);
+ regmap_write(priv->regmap, TIM_CNT, priv->bak.cnt);
+
+ /* Also re-enables the timer */
+ regmap_write(priv->regmap, TIM_CR1, priv->bak.cr1);
+ }
+
+ return 0;
+}
+
+static SIMPLE_DEV_PM_OPS(stm32_timer_trigger_pm_ops,
+ stm32_timer_trigger_suspend,
+ stm32_timer_trigger_resume);
+
static const struct stm32_timer_trigger_cfg stm32_timer_trg_cfg = {
.valids_table = valids_table,
.num_valids_table = ARRAY_SIZE(valids_table),
@@ -840,6 +902,7 @@ static struct platform_driver stm32_timer_trigger_driver = {
.driver = {
.name = "stm32-timer-trigger",
.of_match_table = stm32_trig_of_match,
+ .pm = &stm32_timer_trigger_pm_ops,
},
};
module_platform_driver(stm32_timer_trigger_driver);
--
2.7.4
"clk_enabled" flag reflects enabled state of the timer, for master mode,
slave mode or trigger (with sampling_frequency). So rename it to "enabled".
Signed-off-by: Fabrice Gasnier <[email protected]>
---
drivers/iio/trigger/stm32-timer-trigger.c | 28 ++++++++++++++--------------
1 file changed, 14 insertions(+), 14 deletions(-)
diff --git a/drivers/iio/trigger/stm32-timer-trigger.c b/drivers/iio/trigger/stm32-timer-trigger.c
index 16a3b6b..32e1249 100644
--- a/drivers/iio/trigger/stm32-timer-trigger.c
+++ b/drivers/iio/trigger/stm32-timer-trigger.c
@@ -79,7 +79,7 @@ struct stm32_timer_trigger {
struct device *dev;
struct regmap *regmap;
struct clk *clk;
- bool clk_enabled;
+ bool enabled;
u32 max_arr;
const void *triggers;
const void *valids;
@@ -140,8 +140,8 @@ static int stm32_timer_start(struct stm32_timer_trigger *priv,
return -EBUSY;
mutex_lock(&priv->lock);
- if (!priv->clk_enabled) {
- priv->clk_enabled = true;
+ if (!priv->enabled) {
+ priv->enabled = true;
clk_enable(priv->clk);
}
@@ -185,8 +185,8 @@ static void stm32_timer_stop(struct stm32_timer_trigger *priv)
/* Make sure that registers are updated */
regmap_update_bits(priv->regmap, TIM_EGR, TIM_EGR_UG, TIM_EGR_UG);
- if (priv->clk_enabled) {
- priv->clk_enabled = false;
+ if (priv->enabled) {
+ priv->enabled = false;
clk_disable(priv->clk);
}
mutex_unlock(&priv->lock);
@@ -305,9 +305,9 @@ static ssize_t stm32_tt_store_master_mode(struct device *dev,
if (!strncmp(master_mode_table[i], buf,
strlen(master_mode_table[i]))) {
mutex_lock(&priv->lock);
- if (!priv->clk_enabled) {
+ if (!priv->enabled) {
/* Clock should be enabled first */
- priv->clk_enabled = true;
+ priv->enabled = true;
clk_enable(priv->clk);
}
regmap_update_bits(priv->regmap, TIM_CR2, mask,
@@ -476,8 +476,8 @@ static int stm32_counter_write_raw(struct iio_dev *indio_dev,
case IIO_CHAN_INFO_ENABLE:
mutex_lock(&priv->lock);
if (val) {
- if (!priv->clk_enabled) {
- priv->clk_enabled = true;
+ if (!priv->enabled) {
+ priv->enabled = true;
clk_enable(priv->clk);
}
regmap_update_bits(priv->regmap, TIM_CR1, TIM_CR1_CEN,
@@ -485,8 +485,8 @@ static int stm32_counter_write_raw(struct iio_dev *indio_dev,
} else {
regmap_update_bits(priv->regmap, TIM_CR1, TIM_CR1_CEN,
0);
- if (priv->clk_enabled) {
- priv->clk_enabled = false;
+ if (priv->enabled) {
+ priv->enabled = false;
clk_disable(priv->clk);
}
}
@@ -594,9 +594,9 @@ static int stm32_set_enable_mode(struct iio_dev *indio_dev,
* enable counter clock, so it can use it. Keeps it in sync with CEN.
*/
mutex_lock(&priv->lock);
- if (sms == 6 && !priv->clk_enabled) {
+ if (sms == 6 && !priv->enabled) {
clk_enable(priv->clk);
- priv->clk_enabled = true;
+ priv->enabled = true;
}
mutex_unlock(&priv->lock);
@@ -806,7 +806,7 @@ static int stm32_timer_trigger_remove(struct platform_device *pdev)
if (!(val & TIM_CCER_CCXE))
regmap_update_bits(priv->regmap, TIM_CR1, TIM_CR1_CEN, 0);
- if (priv->clk_enabled)
+ if (priv->enabled)
clk_disable(priv->clk);
return 0;
--
2.7.4
On Tue, 3 Mar 2020 15:59:44 +0100
Fabrice Gasnier <[email protected]> wrote:
> "clk_enabled" flag reflects enabled state of the timer, for master mode,
> slave mode or trigger (with sampling_frequency). So rename it to "enabled".
>
> Signed-off-by: Fabrice Gasnier <[email protected]>
Applied to the togreg branch of iio.git and pushed out as testing for
the autobuilders to play with it.
Thanks,
Jonathan
> ---
> drivers/iio/trigger/stm32-timer-trigger.c | 28 ++++++++++++++--------------
> 1 file changed, 14 insertions(+), 14 deletions(-)
>
> diff --git a/drivers/iio/trigger/stm32-timer-trigger.c b/drivers/iio/trigger/stm32-timer-trigger.c
> index 16a3b6b..32e1249 100644
> --- a/drivers/iio/trigger/stm32-timer-trigger.c
> +++ b/drivers/iio/trigger/stm32-timer-trigger.c
> @@ -79,7 +79,7 @@ struct stm32_timer_trigger {
> struct device *dev;
> struct regmap *regmap;
> struct clk *clk;
> - bool clk_enabled;
> + bool enabled;
> u32 max_arr;
> const void *triggers;
> const void *valids;
> @@ -140,8 +140,8 @@ static int stm32_timer_start(struct stm32_timer_trigger *priv,
> return -EBUSY;
>
> mutex_lock(&priv->lock);
> - if (!priv->clk_enabled) {
> - priv->clk_enabled = true;
> + if (!priv->enabled) {
> + priv->enabled = true;
> clk_enable(priv->clk);
> }
>
> @@ -185,8 +185,8 @@ static void stm32_timer_stop(struct stm32_timer_trigger *priv)
> /* Make sure that registers are updated */
> regmap_update_bits(priv->regmap, TIM_EGR, TIM_EGR_UG, TIM_EGR_UG);
>
> - if (priv->clk_enabled) {
> - priv->clk_enabled = false;
> + if (priv->enabled) {
> + priv->enabled = false;
> clk_disable(priv->clk);
> }
> mutex_unlock(&priv->lock);
> @@ -305,9 +305,9 @@ static ssize_t stm32_tt_store_master_mode(struct device *dev,
> if (!strncmp(master_mode_table[i], buf,
> strlen(master_mode_table[i]))) {
> mutex_lock(&priv->lock);
> - if (!priv->clk_enabled) {
> + if (!priv->enabled) {
> /* Clock should be enabled first */
> - priv->clk_enabled = true;
> + priv->enabled = true;
> clk_enable(priv->clk);
> }
> regmap_update_bits(priv->regmap, TIM_CR2, mask,
> @@ -476,8 +476,8 @@ static int stm32_counter_write_raw(struct iio_dev *indio_dev,
> case IIO_CHAN_INFO_ENABLE:
> mutex_lock(&priv->lock);
> if (val) {
> - if (!priv->clk_enabled) {
> - priv->clk_enabled = true;
> + if (!priv->enabled) {
> + priv->enabled = true;
> clk_enable(priv->clk);
> }
> regmap_update_bits(priv->regmap, TIM_CR1, TIM_CR1_CEN,
> @@ -485,8 +485,8 @@ static int stm32_counter_write_raw(struct iio_dev *indio_dev,
> } else {
> regmap_update_bits(priv->regmap, TIM_CR1, TIM_CR1_CEN,
> 0);
> - if (priv->clk_enabled) {
> - priv->clk_enabled = false;
> + if (priv->enabled) {
> + priv->enabled = false;
> clk_disable(priv->clk);
> }
> }
> @@ -594,9 +594,9 @@ static int stm32_set_enable_mode(struct iio_dev *indio_dev,
> * enable counter clock, so it can use it. Keeps it in sync with CEN.
> */
> mutex_lock(&priv->lock);
> - if (sms == 6 && !priv->clk_enabled) {
> + if (sms == 6 && !priv->enabled) {
> clk_enable(priv->clk);
> - priv->clk_enabled = true;
> + priv->enabled = true;
> }
> mutex_unlock(&priv->lock);
>
> @@ -806,7 +806,7 @@ static int stm32_timer_trigger_remove(struct platform_device *pdev)
> if (!(val & TIM_CCER_CCXE))
> regmap_update_bits(priv->regmap, TIM_CR1, TIM_CR1_CEN, 0);
>
> - if (priv->clk_enabled)
> + if (priv->enabled)
> clk_disable(priv->clk);
>
> return 0;
On Tue, 3 Mar 2020 15:59:45 +0100
Fabrice Gasnier <[email protected]> wrote:
> Add suspend/resume PM sleep ops to stm32-timer-trigger driver.
> Register contents may be lost depending on low power modes.
> When going to low power, enforce the timer isn't active. Gracefully
> restore its state upon resume in case it's been left enabled prior to
> suspend.
>
> Signed-off-by: Fabrice Gasnier <[email protected]>
Seems sensible. Applied,
Thanks,
Jonathan
> ---
> drivers/iio/trigger/stm32-timer-trigger.c | 63 +++++++++++++++++++++++++++++++
> 1 file changed, 63 insertions(+)
>
> diff --git a/drivers/iio/trigger/stm32-timer-trigger.c b/drivers/iio/trigger/stm32-timer-trigger.c
> index 32e1249..37545a8 100644
> --- a/drivers/iio/trigger/stm32-timer-trigger.c
> +++ b/drivers/iio/trigger/stm32-timer-trigger.c
> @@ -75,6 +75,15 @@ static const void *stm32h7_valids_table[][MAX_VALIDS] = {
> { }, /* timer 17 */
> };
>
> +struct stm32_timer_trigger_regs {
> + u32 cr1;
> + u32 cr2;
> + u32 psc;
> + u32 arr;
> + u32 cnt;
> + u32 smcr;
> +};
> +
> struct stm32_timer_trigger {
> struct device *dev;
> struct regmap *regmap;
> @@ -86,6 +95,7 @@ struct stm32_timer_trigger {
> bool has_trgo2;
> struct mutex lock; /* concurrent sysfs configuration */
> struct list_head tr_list;
> + struct stm32_timer_trigger_regs bak;
> };
>
> struct stm32_timer_trigger_cfg {
> @@ -812,6 +822,58 @@ static int stm32_timer_trigger_remove(struct platform_device *pdev)
> return 0;
> }
>
> +static int __maybe_unused stm32_timer_trigger_suspend(struct device *dev)
> +{
> + struct stm32_timer_trigger *priv = dev_get_drvdata(dev);
> +
> + /* Only take care of enabled timer: don't disturb other MFD child */
> + if (priv->enabled) {
> + /* Backup registers that may get lost in low power mode */
> + regmap_read(priv->regmap, TIM_CR1, &priv->bak.cr1);
> + regmap_read(priv->regmap, TIM_CR2, &priv->bak.cr2);
> + regmap_read(priv->regmap, TIM_PSC, &priv->bak.psc);
> + regmap_read(priv->regmap, TIM_ARR, &priv->bak.arr);
> + regmap_read(priv->regmap, TIM_CNT, &priv->bak.cnt);
> + regmap_read(priv->regmap, TIM_SMCR, &priv->bak.smcr);
> +
> + /* Disable the timer */
> + regmap_update_bits(priv->regmap, TIM_CR1, TIM_CR1_CEN, 0);
> + clk_disable(priv->clk);
> + }
> +
> + return 0;
> +}
> +
> +static int __maybe_unused stm32_timer_trigger_resume(struct device *dev)
> +{
> + struct stm32_timer_trigger *priv = dev_get_drvdata(dev);
> + int ret;
> +
> + if (priv->enabled) {
> + ret = clk_enable(priv->clk);
> + if (ret)
> + return ret;
> +
> + /* restore master/slave modes */
> + regmap_write(priv->regmap, TIM_SMCR, priv->bak.smcr);
> + regmap_write(priv->regmap, TIM_CR2, priv->bak.cr2);
> +
> + /* restore sampling_frequency (trgo / trgo2 triggers) */
> + regmap_write(priv->regmap, TIM_PSC, priv->bak.psc);
> + regmap_write(priv->regmap, TIM_ARR, priv->bak.arr);
> + regmap_write(priv->regmap, TIM_CNT, priv->bak.cnt);
> +
> + /* Also re-enables the timer */
> + regmap_write(priv->regmap, TIM_CR1, priv->bak.cr1);
> + }
> +
> + return 0;
> +}
> +
> +static SIMPLE_DEV_PM_OPS(stm32_timer_trigger_pm_ops,
> + stm32_timer_trigger_suspend,
> + stm32_timer_trigger_resume);
> +
> static const struct stm32_timer_trigger_cfg stm32_timer_trg_cfg = {
> .valids_table = valids_table,
> .num_valids_table = ARRAY_SIZE(valids_table),
> @@ -840,6 +902,7 @@ static struct platform_driver stm32_timer_trigger_driver = {
> .driver = {
> .name = "stm32-timer-trigger",
> .of_match_table = stm32_trig_of_match,
> + .pm = &stm32_timer_trigger_pm_ops,
> },
> };
> module_platform_driver(stm32_timer_trigger_driver);