summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorFabrice Gasnier <fabrice.gasnier@st.com>2020-03-03 15:59:45 +0100
committerJonathan Cameron <Jonathan.Cameron@huawei.com>2020-03-08 17:28:53 +0000
commit736e19e684b3359b61740f3b75014c375c57bf9d (patch)
tree820d638f29bdb6d9f9574daa161fefd23aa62b8d
parente23aaafcdb395a3114f5f6c25070170d8c349d33 (diff)
downloadlwn-736e19e684b3359b61740f3b75014c375c57bf9d.tar.gz
lwn-736e19e684b3359b61740f3b75014c375c57bf9d.zip
iio: trigger: stm32-timer: add power management support
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 <fabrice.gasnier@st.com> Signed-off-by: Jonathan Cameron <Jonathan.Cameron@huawei.com>
-rw-r--r--drivers/iio/trigger/stm32-timer-trigger.c63
1 files changed, 63 insertions, 0 deletions
diff --git a/drivers/iio/trigger/stm32-timer-trigger.c b/drivers/iio/trigger/stm32-timer-trigger.c
index 32e1249f2b91..37545a8a02b7 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);