Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

counter: stm32-timer-cnt: add power management support

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>

authored by

Fabrice Gasnier and committed by
Jonathan Cameron
c5b84255 b9cd7be0

+63
+63
drivers/counter/stm32-timer-cnt.c
··· 12 12 #include <linux/iio/types.h> 13 13 #include <linux/mfd/stm32-timers.h> 14 14 #include <linux/module.h> 15 + #include <linux/pinctrl/consumer.h> 15 16 #include <linux/platform_device.h> 16 17 17 18 #define TIM_CCMR_CCXS (BIT(8) | BIT(0)) ··· 21 20 #define TIM_CCER_MASK (TIM_CCER_CC1P | TIM_CCER_CC1NP | \ 22 21 TIM_CCER_CC2P | TIM_CCER_CC2NP) 23 22 23 + struct stm32_timer_regs { 24 + u32 cr1; 25 + u32 cnt; 26 + u32 smcr; 27 + u32 arr; 28 + }; 29 + 24 30 struct stm32_timer_cnt { 25 31 struct counter_device counter; 26 32 struct regmap *regmap; 27 33 struct clk *clk; 28 34 u32 ceiling; 35 + bool enabled; 36 + struct stm32_timer_regs bak; 29 37 }; 30 38 31 39 /** ··· 234 224 clk_disable(priv->clk); 235 225 } 236 226 227 + /* Keep enabled state to properly handle low power states */ 228 + priv->enabled = enable; 229 + 237 230 return len; 238 231 } 239 232 ··· 371 358 priv->counter.num_signals = ARRAY_SIZE(stm32_signals); 372 359 priv->counter.priv = priv; 373 360 361 + platform_set_drvdata(pdev, priv); 362 + 374 363 /* Register Counter device */ 375 364 return devm_counter_register(dev, &priv->counter); 376 365 } 366 + 367 + static int __maybe_unused stm32_timer_cnt_suspend(struct device *dev) 368 + { 369 + struct stm32_timer_cnt *priv = dev_get_drvdata(dev); 370 + 371 + /* Only take care of enabled counter: don't disturb other MFD child */ 372 + if (priv->enabled) { 373 + /* Backup registers that may get lost in low power mode */ 374 + regmap_read(priv->regmap, TIM_SMCR, &priv->bak.smcr); 375 + regmap_read(priv->regmap, TIM_ARR, &priv->bak.arr); 376 + regmap_read(priv->regmap, TIM_CNT, &priv->bak.cnt); 377 + regmap_read(priv->regmap, TIM_CR1, &priv->bak.cr1); 378 + 379 + /* Disable the counter */ 380 + regmap_update_bits(priv->regmap, TIM_CR1, TIM_CR1_CEN, 0); 381 + clk_disable(priv->clk); 382 + } 383 + 384 + return pinctrl_pm_select_sleep_state(dev); 385 + } 386 + 387 + static int __maybe_unused stm32_timer_cnt_resume(struct device *dev) 388 + { 389 + struct stm32_timer_cnt *priv = dev_get_drvdata(dev); 390 + int ret; 391 + 392 + ret = pinctrl_pm_select_default_state(dev); 393 + if (ret) 394 + return ret; 395 + 396 + if (priv->enabled) { 397 + clk_enable(priv->clk); 398 + 399 + /* Restore registers that may have been lost */ 400 + regmap_write(priv->regmap, TIM_SMCR, priv->bak.smcr); 401 + regmap_write(priv->regmap, TIM_ARR, priv->bak.arr); 402 + regmap_write(priv->regmap, TIM_CNT, priv->bak.cnt); 403 + 404 + /* Also re-enables the counter */ 405 + regmap_write(priv->regmap, TIM_CR1, priv->bak.cr1); 406 + } 407 + 408 + return 0; 409 + } 410 + 411 + static SIMPLE_DEV_PM_OPS(stm32_timer_cnt_pm_ops, stm32_timer_cnt_suspend, 412 + stm32_timer_cnt_resume); 377 413 378 414 static const struct of_device_id stm32_timer_cnt_of_match[] = { 379 415 { .compatible = "st,stm32-timer-counter", }, ··· 435 373 .driver = { 436 374 .name = "stm32-timer-counter", 437 375 .of_match_table = stm32_timer_cnt_of_match, 376 + .pm = &stm32_timer_cnt_pm_ops, 438 377 }, 439 378 }; 440 379 module_platform_driver(stm32_timer_cnt_driver);