From: Uwe Kleine-König Date: Fri, 25 Jul 2025 15:45:09 +0000 (+0200) Subject: pwm: mediatek: Implement .get_state() callback X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=edd6a37e06f381af9a05c813a822ac8528da0fca;p=thirdparty%2Fkernel%2Fstable.git pwm: mediatek: Implement .get_state() callback 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 Reviewed-by: AngeloGioacchino Del Regno Link: https://lore.kernel.org/r/20250725154506.2610172-15-u.kleine-koenig@baylibre.com Signed-off-by: Uwe Kleine-König --- diff --git a/drivers/pwm/pwm-mediatek.c b/drivers/pwm/pwm-mediatek.c index faa0205d4a0dc..2a5323e9fa50c 100644 --- a/drivers/pwm/pwm-mediatek.c +++ b/drivers/pwm/pwm-mediatek.c @@ -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)