]> git.ipfire.org Git - thirdparty/kernel/stable.git/commitdiff
pwm: mediatek: Implement .get_state() callback
authorUwe Kleine-König <u.kleine-koenig@baylibre.com>
Fri, 25 Jul 2025 15:45:09 +0000 (17:45 +0200)
committerUwe Kleine-König <ukleinek@kernel.org>
Mon, 15 Sep 2025 09:39:45 +0000 (11:39 +0200)
The registers can be read out just fine on an MT8365. In the assumption
that this works on all supported devices, a .get_state() callback can be
implemented. This enables consumers to make use of pwm_get_state_hw() and
improves the usefulness of /sys/kernel/debug/pwm.

Signed-off-by: Uwe Kleine-König <u.kleine-koenig@baylibre.com>
Reviewed-by: AngeloGioacchino Del Regno <angelogioacchino.delregno@collabora.com>
Link: https://lore.kernel.org/r/20250725154506.2610172-15-u.kleine-koenig@baylibre.com
Signed-off-by: Uwe Kleine-König <ukleinek@kernel.org>
drivers/pwm/pwm-mediatek.c

index faa0205d4a0dc5f710bcd632b997d4845762f2dd..2a5323e9fa50c8824194ec4ebf1ebddcba47f3c3 100644 (file)
@@ -31,6 +31,7 @@
 #define PWMDWIDTH_PERIOD               GENMASK(12, 0)
 #define PWM45DWIDTH_FIXUP      0x30
 #define PWMTHRES               0x30
+#define PWMTHRES_DUTY                  GENMASK(12, 0)
 #define PWM45THRES_FIXUP       0x34
 #define PWM_CK_26M_SEL_V3      0x74
 #define PWM_CK_26M_SEL         0x210
@@ -108,6 +109,13 @@ static inline void pwm_mediatek_writel(struct pwm_mediatek_chip *chip,
               num * chip->soc->chanreg_width + offset);
 }
 
+static inline u32 pwm_mediatek_readl(struct pwm_mediatek_chip *chip,
+                                    unsigned int num, unsigned int offset)
+{
+       return readl(chip->regs + chip->soc->chanreg_base +
+                    num * chip->soc->chanreg_width + offset);
+}
+
 static void pwm_mediatek_enable(struct pwm_chip *chip, struct pwm_device *pwm)
 {
        struct pwm_mediatek_chip *pc = to_pwm_mediatek_chip(chip);
@@ -228,8 +236,70 @@ static int pwm_mediatek_apply(struct pwm_chip *chip, struct pwm_device *pwm,
        return err;
 }
 
+static int pwm_mediatek_get_state(struct pwm_chip *chip, struct pwm_device *pwm,
+                                 struct pwm_state *state)
+{
+       struct pwm_mediatek_chip *pc = to_pwm_mediatek_chip(chip);
+       int ret;
+       u32 enable;
+       u32 reg_width = PWMDWIDTH, reg_thres = PWMTHRES;
+
+       if (pc->soc->pwm45_fixup && pwm->hwpwm > 2) {
+               /*
+                * PWM[4,5] has distinct offset for PWMDWIDTH and PWMTHRES
+                * from the other PWMs on MT7623.
+                */
+               reg_width = PWM45DWIDTH_FIXUP;
+               reg_thres = PWM45THRES_FIXUP;
+       }
+
+       ret = pwm_mediatek_clk_enable(pc, pwm->hwpwm);
+       if (ret < 0)
+               return ret;
+
+       enable = readl(pc->regs);
+       if (enable & BIT(pwm->hwpwm)) {
+               u32 clkdiv, cnt_period, cnt_duty;
+               unsigned long clk_rate;
+
+               clk_rate = clk_get_rate(pc->clk_pwms[pwm->hwpwm]);
+               if (!clk_rate) {
+                       ret = -EINVAL;
+                       goto out;
+               }
+
+               state->enabled = true;
+               state->polarity = PWM_POLARITY_NORMAL;
+
+               clkdiv = FIELD_GET(PWMCON_CLKDIV,
+                                  pwm_mediatek_readl(pc, pwm->hwpwm, PWMCON));
+               cnt_period = FIELD_GET(PWMDWIDTH_PERIOD,
+                                      pwm_mediatek_readl(pc, pwm->hwpwm, reg_width));
+               cnt_duty = FIELD_GET(PWMTHRES_DUTY,
+                                    pwm_mediatek_readl(pc, pwm->hwpwm, reg_thres));
+
+               /*
+                * cnt_period is a 13 bit value, NSEC_PER_SEC is 30 bits wide
+                * and clkdiv is less than 8, so the multiplication doesn't
+                * overflow an u64.
+                */
+               state->period =
+                       DIV_ROUND_UP_ULL((u64)cnt_period * NSEC_PER_SEC << clkdiv, clk_rate);
+               state->duty_cycle =
+                       DIV_ROUND_UP_ULL((u64)cnt_duty * NSEC_PER_SEC << clkdiv, clk_rate);
+       } else {
+               state->enabled = false;
+       }
+
+out:
+       pwm_mediatek_clk_disable(pc, pwm->hwpwm);
+
+       return ret;
+}
+
 static const struct pwm_ops pwm_mediatek_ops = {
        .apply = pwm_mediatek_apply,
+       .get_state = pwm_mediatek_get_state,
 };
 
 static int pwm_mediatek_init_used_clks(struct pwm_mediatek_chip *pc)