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 <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>
This commit is contained in:
Uwe Kleine-König 2025-07-25 17:45:09 +02:00 committed by Uwe Kleine-König
parent a911f15745
commit edd6a37e06

View 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)