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

counter: stm32-lptimer-cnt: fix error handling when enabling

In case the stm32_lptim_set_enable_state() fails to update CMP and ARR,
a timeout error is raised, by regmap_read_poll_timeout. It may happen,
when the lptimer runs on a slow clock, and the clock is gated only
few times during the polling.

Badly, when this happen, STM32_LPTIM_ENABLE in CR register has been set.
So the 'enable' state in sysfs wrongly lies on the counter being
correctly enabled, due to CR is read as one in stm32_lptim_is_enabled().

To fix both issues:
- enable the clock before writing CMP, ARR and polling ISR bits. It will
avoid the possible timeout error.
- clear the ENABLE bit in CR and disable the clock in the error path.

Fixes: d8958824cf07 ("iio: counter: Add support for STM32 LPTimer")
Signed-off-by: Fabrice Gasnier <fabrice.gasnier@foss.st.com>
Link: https://lore.kernel.org/r/20250224170657.3368236-1-fabrice.gasnier@foss.st.com
Signed-off-by: William Breathitt Gray <wbg@kernel.org>

authored by

Fabrice Gasnier and committed by
William Breathitt Gray
8744dcd4 2014c95a

+15 -9
+15 -9
drivers/counter/stm32-lptimer-cnt.c
··· 58 58 return 0; 59 59 } 60 60 61 + ret = clk_enable(priv->clk); 62 + if (ret) 63 + goto disable_cnt; 64 + 61 65 /* LP timer must be enabled before writing CMP & ARR */ 62 66 ret = regmap_write(priv->regmap, STM32_LPTIM_ARR, priv->ceiling); 63 67 if (ret) 64 - return ret; 68 + goto disable_clk; 65 69 66 70 ret = regmap_write(priv->regmap, STM32_LPTIM_CMP, 0); 67 71 if (ret) 68 - return ret; 72 + goto disable_clk; 69 73 70 74 /* ensure CMP & ARR registers are properly written */ 71 75 ret = regmap_read_poll_timeout(priv->regmap, STM32_LPTIM_ISR, val, 72 76 (val & STM32_LPTIM_CMPOK_ARROK) == STM32_LPTIM_CMPOK_ARROK, 73 77 100, 1000); 74 78 if (ret) 75 - return ret; 79 + goto disable_clk; 76 80 77 81 ret = regmap_write(priv->regmap, STM32_LPTIM_ICR, 78 82 STM32_LPTIM_CMPOKCF_ARROKCF); 79 83 if (ret) 80 - return ret; 84 + goto disable_clk; 81 85 82 - ret = clk_enable(priv->clk); 83 - if (ret) { 84 - regmap_write(priv->regmap, STM32_LPTIM_CR, 0); 85 - return ret; 86 - } 87 86 priv->enabled = true; 88 87 89 88 /* Start LP timer in continuous mode */ 90 89 return regmap_update_bits(priv->regmap, STM32_LPTIM_CR, 91 90 STM32_LPTIM_CNTSTRT, STM32_LPTIM_CNTSTRT); 91 + 92 + disable_clk: 93 + clk_disable(priv->clk); 94 + disable_cnt: 95 + regmap_write(priv->regmap, STM32_LPTIM_CR, 0); 96 + 97 + return ret; 92 98 } 93 99 94 100 static int stm32_lptim_setup(struct stm32_lptim_cnt *priv, int enable)