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

Configure Feed

Select the types of activity you want to include in your feed.

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>

authored by

Uwe Kleine-König and committed by
Uwe Kleine-König
edd6a37e a911f157

+70
+70
drivers/pwm/pwm-mediatek.c
··· 31 31 #define PWMDWIDTH_PERIOD GENMASK(12, 0) 32 32 #define PWM45DWIDTH_FIXUP 0x30 33 33 #define PWMTHRES 0x30 34 + #define PWMTHRES_DUTY GENMASK(12, 0) 34 35 #define PWM45THRES_FIXUP 0x34 35 36 #define PWM_CK_26M_SEL_V3 0x74 36 37 #define PWM_CK_26M_SEL 0x210 ··· 107 106 { 108 107 writel(value, chip->regs + chip->soc->chanreg_base + 109 108 num * chip->soc->chanreg_width + offset); 109 + } 110 + 111 + static inline u32 pwm_mediatek_readl(struct pwm_mediatek_chip *chip, 112 + unsigned int num, unsigned int offset) 113 + { 114 + return readl(chip->regs + chip->soc->chanreg_base + 115 + num * chip->soc->chanreg_width + offset); 110 116 } 111 117 112 118 static void pwm_mediatek_enable(struct pwm_chip *chip, struct pwm_device *pwm) ··· 236 228 return err; 237 229 } 238 230 231 + static int pwm_mediatek_get_state(struct pwm_chip *chip, struct pwm_device *pwm, 232 + struct pwm_state *state) 233 + { 234 + struct pwm_mediatek_chip *pc = to_pwm_mediatek_chip(chip); 235 + int ret; 236 + u32 enable; 237 + u32 reg_width = PWMDWIDTH, reg_thres = PWMTHRES; 238 + 239 + if (pc->soc->pwm45_fixup && pwm->hwpwm > 2) { 240 + /* 241 + * PWM[4,5] has distinct offset for PWMDWIDTH and PWMTHRES 242 + * from the other PWMs on MT7623. 243 + */ 244 + reg_width = PWM45DWIDTH_FIXUP; 245 + reg_thres = PWM45THRES_FIXUP; 246 + } 247 + 248 + ret = pwm_mediatek_clk_enable(pc, pwm->hwpwm); 249 + if (ret < 0) 250 + return ret; 251 + 252 + enable = readl(pc->regs); 253 + if (enable & BIT(pwm->hwpwm)) { 254 + u32 clkdiv, cnt_period, cnt_duty; 255 + unsigned long clk_rate; 256 + 257 + clk_rate = clk_get_rate(pc->clk_pwms[pwm->hwpwm]); 258 + if (!clk_rate) { 259 + ret = -EINVAL; 260 + goto out; 261 + } 262 + 263 + state->enabled = true; 264 + state->polarity = PWM_POLARITY_NORMAL; 265 + 266 + clkdiv = FIELD_GET(PWMCON_CLKDIV, 267 + pwm_mediatek_readl(pc, pwm->hwpwm, PWMCON)); 268 + cnt_period = FIELD_GET(PWMDWIDTH_PERIOD, 269 + pwm_mediatek_readl(pc, pwm->hwpwm, reg_width)); 270 + cnt_duty = FIELD_GET(PWMTHRES_DUTY, 271 + pwm_mediatek_readl(pc, pwm->hwpwm, reg_thres)); 272 + 273 + /* 274 + * cnt_period is a 13 bit value, NSEC_PER_SEC is 30 bits wide 275 + * and clkdiv is less than 8, so the multiplication doesn't 276 + * overflow an u64. 277 + */ 278 + state->period = 279 + DIV_ROUND_UP_ULL((u64)cnt_period * NSEC_PER_SEC << clkdiv, clk_rate); 280 + state->duty_cycle = 281 + DIV_ROUND_UP_ULL((u64)cnt_duty * NSEC_PER_SEC << clkdiv, clk_rate); 282 + } else { 283 + state->enabled = false; 284 + } 285 + 286 + out: 287 + pwm_mediatek_clk_disable(pc, pwm->hwpwm); 288 + 289 + return ret; 290 + } 291 + 239 292 static const struct pwm_ops pwm_mediatek_ops = { 240 293 .apply = pwm_mediatek_apply, 294 + .get_state = pwm_mediatek_get_state, 241 295 }; 242 296 243 297 static int pwm_mediatek_init_used_clks(struct pwm_mediatek_chip *pc)