]> git.proxmox.com Git - mirror_ubuntu-jammy-kernel.git/commitdiff
counter: stm32-timer-cnt: add power management support
authorFabrice Gasnier <fabrice.gasnier@st.com>
Mon, 10 Feb 2020 17:19:58 +0000 (18:19 +0100)
committerJonathan Cameron <Jonathan.Cameron@huawei.com>
Fri, 21 Feb 2020 10:02:15 +0000 (10:02 +0000)
Add suspend/resume PM sleep ops. When going to low power, enforce the
counter isn't active. Gracefully restore its state upon resume in case
it's been left enabled prior to suspend.

Acked-by: William Breathitt Gray <vilhelm.gray@gmail.com>
Signed-off-by: Fabrice Gasnier <fabrice.gasnier@st.com>
Signed-off-by: Jonathan Cameron <Jonathan.Cameron@huawei.com>
drivers/counter/stm32-timer-cnt.c

index 3eafccec3beb169f01d9e3fc724466e361f4a0fd..50496f453d6d06a128f002e54e88c4618e0efaee 100644 (file)
@@ -12,6 +12,7 @@
 #include <linux/iio/types.h>
 #include <linux/mfd/stm32-timers.h>
 #include <linux/module.h>
+#include <linux/pinctrl/consumer.h>
 #include <linux/platform_device.h>
 
 #define TIM_CCMR_CCXS  (BIT(8) | BIT(0))
 #define TIM_CCER_MASK  (TIM_CCER_CC1P | TIM_CCER_CC1NP | \
                         TIM_CCER_CC2P | TIM_CCER_CC2NP)
 
+struct stm32_timer_regs {
+       u32 cr1;
+       u32 cnt;
+       u32 smcr;
+       u32 arr;
+};
+
 struct stm32_timer_cnt {
        struct counter_device counter;
        struct regmap *regmap;
        struct clk *clk;
        u32 ceiling;
+       bool enabled;
+       struct stm32_timer_regs bak;
 };
 
 /**
@@ -224,6 +234,9 @@ static ssize_t stm32_count_enable_write(struct counter_device *counter,
                        clk_disable(priv->clk);
        }
 
+       /* Keep enabled state to properly handle low power states */
+       priv->enabled = enable;
+
        return len;
 }
 
@@ -358,10 +371,59 @@ static int stm32_timer_cnt_probe(struct platform_device *pdev)
        priv->counter.num_signals = ARRAY_SIZE(stm32_signals);
        priv->counter.priv = priv;
 
+       platform_set_drvdata(pdev, priv);
+
        /* Register Counter device */
        return devm_counter_register(dev, &priv->counter);
 }
 
+static int __maybe_unused stm32_timer_cnt_suspend(struct device *dev)
+{
+       struct stm32_timer_cnt *priv = dev_get_drvdata(dev);
+
+       /* Only take care of enabled counter: don't disturb other MFD child */
+       if (priv->enabled) {
+               /* Backup registers that may get lost in low power mode */
+               regmap_read(priv->regmap, TIM_SMCR, &priv->bak.smcr);
+               regmap_read(priv->regmap, TIM_ARR, &priv->bak.arr);
+               regmap_read(priv->regmap, TIM_CNT, &priv->bak.cnt);
+               regmap_read(priv->regmap, TIM_CR1, &priv->bak.cr1);
+
+               /* Disable the counter */
+               regmap_update_bits(priv->regmap, TIM_CR1, TIM_CR1_CEN, 0);
+               clk_disable(priv->clk);
+       }
+
+       return pinctrl_pm_select_sleep_state(dev);
+}
+
+static int __maybe_unused stm32_timer_cnt_resume(struct device *dev)
+{
+       struct stm32_timer_cnt *priv = dev_get_drvdata(dev);
+       int ret;
+
+       ret = pinctrl_pm_select_default_state(dev);
+       if (ret)
+               return ret;
+
+       if (priv->enabled) {
+               clk_enable(priv->clk);
+
+               /* Restore registers that may have been lost */
+               regmap_write(priv->regmap, TIM_SMCR, priv->bak.smcr);
+               regmap_write(priv->regmap, TIM_ARR, priv->bak.arr);
+               regmap_write(priv->regmap, TIM_CNT, priv->bak.cnt);
+
+               /* Also re-enables the counter */
+               regmap_write(priv->regmap, TIM_CR1, priv->bak.cr1);
+       }
+
+       return 0;
+}
+
+static SIMPLE_DEV_PM_OPS(stm32_timer_cnt_pm_ops, stm32_timer_cnt_suspend,
+                        stm32_timer_cnt_resume);
+
 static const struct of_device_id stm32_timer_cnt_of_match[] = {
        { .compatible = "st,stm32-timer-counter", },
        {},
@@ -373,6 +435,7 @@ static struct platform_driver stm32_timer_cnt_driver = {
        .driver = {
                .name = "stm32-timer-counter",
                .of_match_table = stm32_timer_cnt_of_match,
+               .pm = &stm32_timer_cnt_pm_ops,
        },
 };
 module_platform_driver(stm32_timer_cnt_driver);