| From 65c6f742ab14ab1a2679fba72b82dcc0289d96f1 Mon Sep 17 00:00:00 2001 |
| From: Laurentiu Mihalcea <laurentiu.mihalcea@nxp.com> |
| Date: Mon, 28 Jul 2025 15:41:44 -0400 |
| Subject: pwm: imx-tpm: Reset counter if CMOD is 0 |
| MIME-Version: 1.0 |
| Content-Type: text/plain; charset=UTF-8 |
| Content-Transfer-Encoding: 8bit |
| |
| From: Laurentiu Mihalcea <laurentiu.mihalcea@nxp.com> |
| |
| commit 65c6f742ab14ab1a2679fba72b82dcc0289d96f1 upstream. |
| |
| As per the i.MX93 TRM, section 67.3.2.1 "MOD register update", the value |
| of the TPM counter does NOT get updated when writing MOD.MOD unless |
| SC.CMOD != 0. Therefore, with the current code, assuming the following |
| sequence: |
| |
| 1) pwm_disable() |
| 2) pwm_apply_might_sleep() /* period is changed here */ |
| 3) pwm_enable() |
| |
| and assuming only one channel is active, if CNT.COUNT is higher than the |
| MOD.MOD value written during the pwm_apply_might_sleep() call then, when |
| re-enabling the PWM during pwm_enable(), the counter will end up resetting |
| after UINT32_MAX - CNT.COUNT + MOD.MOD cycles instead of MOD.MOD cycles as |
| normally expected. |
| |
| Fix this problem by forcing a reset of the TPM counter before MOD.MOD is |
| written. |
| |
| Fixes: 738a1cfec2ed ("pwm: Add i.MX TPM PWM driver support") |
| Cc: stable@vger.kernel.org |
| Signed-off-by: Laurentiu Mihalcea <laurentiu.mihalcea@nxp.com> |
| Link: https://lore.kernel.org/r/20250728194144.22884-1-laurentiumihalcea111@gmail.com |
| Signed-off-by: Uwe Kleine-Kรถnig <ukleinek@kernel.org> |
| Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> |
| --- |
| drivers/pwm/pwm-imx-tpm.c | 9 +++++++++ |
| 1 file changed, 9 insertions(+) |
| |
| --- a/drivers/pwm/pwm-imx-tpm.c |
| +++ b/drivers/pwm/pwm-imx-tpm.c |
| @@ -205,6 +205,15 @@ static int pwm_imx_tpm_apply_hw(struct p |
| writel(val, tpm->base + PWM_IMX_TPM_SC); |
| |
| /* |
| + * if the counter is disabled (CMOD == 0), programming the new |
| + * period length (MOD) will not reset the counter (CNT). If |
| + * CNT.COUNT happens to be bigger than the new MOD value then |
| + * the counter will end up being reset way too late. Therefore, |
| + * manually reset it to 0. |
| + */ |
| + if (!cmod) |
| + writel(0x0, tpm->base + PWM_IMX_TPM_CNT); |
| + /* |
| * set period count: |
| * if the PWM is disabled (CMOD[1:0] = 2b00), then MOD register |
| * is updated when MOD register is written. |