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

pwm: mediatek: Convert to waveform API

Implement the new waveform callbacks which makes the usage of this
hardware more flexible and allows to use it via the pwm character
device.

Signed-off-by: Uwe Kleine-König <u.kleine-koenig@baylibre.com>
Link: https://patch.msgid.link/20251013114258.149260-2-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
24ec5632 0251fa88

+201 -134
+201 -134
drivers/pwm/pwm-mediatek.c
··· 135 135 num * chip->soc->chanreg_width + offset); 136 136 } 137 137 138 - static void pwm_mediatek_enable(struct pwm_chip *chip, struct pwm_device *pwm) 138 + struct pwm_mediatek_waveform { 139 + u32 enable; 140 + u32 con; 141 + u32 width; 142 + u32 thres; 143 + }; 144 + 145 + static int pwm_mediatek_round_waveform_tohw(struct pwm_chip *chip, struct pwm_device *pwm, 146 + const struct pwm_waveform *wf, void *_wfhw) 139 147 { 140 - struct pwm_mediatek_chip *pc = to_pwm_mediatek_chip(chip); 141 - u32 value; 142 - 143 - value = readl(pc->regs); 144 - value |= BIT(pwm->hwpwm); 145 - writel(value, pc->regs); 146 - } 147 - 148 - static void pwm_mediatek_disable(struct pwm_chip *chip, struct pwm_device *pwm) 149 - { 150 - struct pwm_mediatek_chip *pc = to_pwm_mediatek_chip(chip); 151 - u32 value; 152 - 153 - value = readl(pc->regs); 154 - value &= ~BIT(pwm->hwpwm); 155 - writel(value, pc->regs); 156 - } 157 - 158 - static int pwm_mediatek_config(struct pwm_chip *chip, struct pwm_device *pwm, 159 - u64 duty_ns, u64 period_ns) 160 - { 148 + struct pwm_mediatek_waveform *wfhw = _wfhw; 161 149 struct pwm_mediatek_chip *pc = to_pwm_mediatek_chip(chip); 162 150 u32 clkdiv, enable; 163 - u32 reg_width = PWMDWIDTH, reg_thres = PWMTHRES; 164 151 u64 cnt_period, cnt_duty; 165 152 unsigned long clk_rate; 166 - int ret; 153 + int ret = 0; 167 154 168 - ret = pwm_mediatek_clk_enable(pc, pwm->hwpwm); 169 - if (ret < 0) 170 - return ret; 155 + if (wf->period_length_ns == 0) { 156 + *wfhw = (typeof(*wfhw)){ 157 + .enable = 0, 158 + }; 159 + 160 + return 0; 161 + } 162 + 163 + if (!pc->clk_pwms[pwm->hwpwm].rate) { 164 + struct clk *clk = pc->clk_pwms[pwm->hwpwm].clk; 165 + 166 + ret = clk_prepare_enable(clk); 167 + if (ret) 168 + return ret; 169 + 170 + pc->clk_pwms[pwm->hwpwm].rate = clk_get_rate(clk); 171 + 172 + clk_disable_unprepare(clk); 173 + } 171 174 172 175 clk_rate = pc->clk_pwms[pwm->hwpwm].rate; 176 + if (clk_rate == 0 || clk_rate > 1000000000) 177 + return -EINVAL; 173 178 174 - /* Make sure we use the bus clock and not the 26MHz clock */ 175 - if (pc->soc->pwm_ck_26m_sel_reg) 176 - writel(0, pc->regs + pc->soc->pwm_ck_26m_sel_reg); 177 - 178 - cnt_period = mul_u64_u64_div_u64(period_ns, clk_rate, NSEC_PER_SEC); 179 + cnt_period = mul_u64_u64_div_u64(wf->period_length_ns, clk_rate, NSEC_PER_SEC); 179 180 if (cnt_period == 0) { 180 - ret = -ERANGE; 181 - goto out; 181 + cnt_period = 1; 182 + ret = 1; 182 183 } 183 184 184 185 if (cnt_period > FIELD_MAX(PWMDWIDTH_PERIOD) + 1) { ··· 194 193 clkdiv = 0; 195 194 } 196 195 197 - cnt_duty = mul_u64_u64_div_u64(duty_ns, clk_rate, NSEC_PER_SEC) >> clkdiv; 196 + cnt_duty = mul_u64_u64_div_u64(wf->duty_length_ns, clk_rate, NSEC_PER_SEC) >> clkdiv; 198 197 if (cnt_duty > cnt_period) 199 198 cnt_duty = cnt_period; 200 199 ··· 207 206 208 207 cnt_period -= 1; 209 208 210 - dev_dbg(&chip->dev, "pwm#%u: %lld/%lld @%lu -> CON: %x, PERIOD: %llx, DUTY: %llx\n", 211 - pwm->hwpwm, duty_ns, period_ns, clk_rate, clkdiv, cnt_period, cnt_duty); 209 + dev_dbg(&chip->dev, "pwm#%u: %lld/%lld @%lu -> ENABLE: %x, CON: %x, PERIOD: %llx, DUTY: %llx\n", 210 + pwm->hwpwm, wf->duty_length_ns, wf->period_length_ns, clk_rate, 211 + enable, clkdiv, cnt_period, cnt_duty); 212 212 213 - if (pc->soc->pwm45_fixup && pwm->hwpwm > 2) { 213 + *wfhw = (typeof(*wfhw)){ 214 + .enable = enable, 215 + .con = clkdiv, 216 + .width = cnt_period, 217 + .thres = cnt_duty, 218 + }; 219 + 220 + return ret; 221 + } 222 + 223 + static int pwm_mediatek_round_waveform_fromhw(struct pwm_chip *chip, struct pwm_device *pwm, 224 + const void *_wfhw, struct pwm_waveform *wf) 225 + { 226 + const struct pwm_mediatek_waveform *wfhw = _wfhw; 227 + struct pwm_mediatek_chip *pc = to_pwm_mediatek_chip(chip); 228 + u32 clkdiv, cnt_period, cnt_duty; 229 + unsigned long clk_rate; 230 + 231 + /* 232 + * When _wfhw was populated, the clock was on, so .rate is 233 + * already set appropriately. 234 + */ 235 + clk_rate = pc->clk_pwms[pwm->hwpwm].rate; 236 + 237 + if (wfhw->enable) { 238 + clkdiv = FIELD_GET(PWMCON_CLKDIV, wfhw->con); 239 + cnt_period = FIELD_GET(PWMDWIDTH_PERIOD, wfhw->width); 240 + cnt_duty = FIELD_GET(PWMTHRES_DUTY, wfhw->thres); 241 + 214 242 /* 215 - * PWM[4,5] has distinct offset for PWMDWIDTH and PWMTHRES 216 - * from the other PWMs on MT7623. 243 + * cnt_period is a 13 bit value, NSEC_PER_SEC is 30 bits wide 244 + * and clkdiv is less than 8, so the multiplication doesn't 245 + * overflow an u64. 217 246 */ 218 - reg_width = PWM45DWIDTH_FIXUP; 219 - reg_thres = PWM45THRES_FIXUP; 220 - } 247 + *wf = (typeof(*wf)){ 248 + .period_length_ns = 249 + DIV_ROUND_UP_ULL((u64)(cnt_period + 1) * NSEC_PER_SEC << clkdiv, clk_rate), 250 + .duty_length_ns = 251 + DIV_ROUND_UP_ULL((u64)(cnt_duty + 1) * NSEC_PER_SEC << clkdiv, clk_rate), 252 + }; 253 + } else { 254 + clkdiv = 0; 255 + cnt_period = 0; 256 + cnt_duty = 0; 221 257 222 - pwm_mediatek_writel(pc, pwm->hwpwm, PWMCON, BIT(15) | clkdiv); 223 - pwm_mediatek_writel(pc, pwm->hwpwm, reg_width, cnt_period); 258 + /* 259 + * .enable = 0 is also used for too small duty_cycle values, so 260 + * report the HW as being enabled to communicate the minimal 261 + * period. 262 + */ 263 + *wf = (typeof(*wf)){ 264 + .period_length_ns = 265 + DIV_ROUND_UP_ULL(NSEC_PER_SEC, clk_rate), 266 + .duty_length_ns = 0, 267 + }; 268 + }; 269 + 270 + dev_dbg(&chip->dev, "pwm#%u: ENABLE: %x, CLKDIV: %x, PERIOD: %x, DUTY: %x @%lu -> %lld/%lld\n", 271 + pwm->hwpwm, wfhw->enable, clkdiv, cnt_period, cnt_duty, clk_rate, 272 + wf->duty_length_ns, wf->period_length_ns); 273 + 274 + return 0; 275 + } 276 + 277 + static int pwm_mediatek_read_waveform(struct pwm_chip *chip, 278 + struct pwm_device *pwm, void *_wfhw) 279 + { 280 + struct pwm_mediatek_waveform *wfhw = _wfhw; 281 + struct pwm_mediatek_chip *pc = to_pwm_mediatek_chip(chip); 282 + u32 enable, clkdiv, cnt_period, cnt_duty; 283 + u32 reg_width = PWMDWIDTH, reg_thres = PWMTHRES; 284 + int ret; 285 + 286 + ret = pwm_mediatek_clk_enable(pc, pwm->hwpwm); 287 + if (ret < 0) 288 + return ret; 289 + 290 + enable = readl(pc->regs) & BIT(pwm->hwpwm); 224 291 225 292 if (enable) { 226 - pwm_mediatek_writel(pc, pwm->hwpwm, reg_thres, cnt_duty); 227 - pwm_mediatek_enable(chip, pwm); 293 + if (pc->soc->pwm45_fixup && pwm->hwpwm > 2) { 294 + /* 295 + * PWM[4,5] has distinct offset for PWMDWIDTH and PWMTHRES 296 + * from the other PWMs on MT7623. 297 + */ 298 + reg_width = PWM45DWIDTH_FIXUP; 299 + reg_thres = PWM45THRES_FIXUP; 300 + } 301 + 302 + clkdiv = FIELD_GET(PWMCON_CLKDIV, pwm_mediatek_readl(pc, pwm->hwpwm, PWMCON)); 303 + cnt_period = FIELD_GET(PWMDWIDTH_PERIOD, pwm_mediatek_readl(pc, pwm->hwpwm, reg_width)); 304 + cnt_duty = FIELD_GET(PWMTHRES_DUTY, pwm_mediatek_readl(pc, pwm->hwpwm, reg_thres)); 305 + 306 + *wfhw = (typeof(*wfhw)){ 307 + .enable = enable, 308 + .con = BIT(15) | clkdiv, 309 + .width = cnt_period, 310 + .thres = cnt_duty, 311 + }; 228 312 } else { 229 - pwm_mediatek_disable(chip, pwm); 313 + *wfhw = (typeof(*wfhw)){ 314 + .enable = 0, 315 + }; 316 + } 317 + 318 + pwm_mediatek_clk_disable(pc, pwm->hwpwm); 319 + 320 + return ret; 321 + } 322 + 323 + static int pwm_mediatek_write_waveform(struct pwm_chip *chip, 324 + struct pwm_device *pwm, const void *_wfhw) 325 + { 326 + const struct pwm_mediatek_waveform *wfhw = _wfhw; 327 + struct pwm_mediatek_chip *pc = to_pwm_mediatek_chip(chip); 328 + u32 ctrl; 329 + int ret; 330 + 331 + ret = pwm_mediatek_clk_enable(pc, pwm->hwpwm); 332 + if (ret < 0) 333 + return ret; 334 + 335 + ctrl = readl(pc->regs); 336 + 337 + if (wfhw->enable) { 338 + u32 reg_width = PWMDWIDTH, reg_thres = PWMTHRES; 339 + 340 + if (pc->soc->pwm45_fixup && pwm->hwpwm > 2) { 341 + /* 342 + * PWM[4,5] has distinct offset for PWMDWIDTH and PWMTHRES 343 + * from the other PWMs on MT7623. 344 + */ 345 + reg_width = PWM45DWIDTH_FIXUP; 346 + reg_thres = PWM45THRES_FIXUP; 347 + } 348 + 349 + if (!(ctrl & BIT(pwm->hwpwm))) { 350 + /* 351 + * The clks are already on, just increasing the usage 352 + * counter doesn't fail. 353 + */ 354 + ret = pwm_mediatek_clk_enable(pc, pwm->hwpwm); 355 + if (unlikely(ret < 0)) 356 + goto out; 357 + 358 + ctrl |= BIT(pwm->hwpwm); 359 + writel(ctrl, pc->regs); 360 + } 361 + 362 + /* Make sure we use the bus clock and not the 26MHz clock */ 363 + if (pc->soc->pwm_ck_26m_sel_reg) 364 + writel(0, pc->regs + pc->soc->pwm_ck_26m_sel_reg); 365 + 366 + pwm_mediatek_writel(pc, pwm->hwpwm, PWMCON, BIT(15) | wfhw->con); 367 + pwm_mediatek_writel(pc, pwm->hwpwm, reg_width, wfhw->width); 368 + pwm_mediatek_writel(pc, pwm->hwpwm, reg_thres, wfhw->thres); 369 + } else { 370 + if (ctrl & BIT(pwm->hwpwm)) { 371 + ctrl &= ~BIT(pwm->hwpwm); 372 + writel(ctrl, pc->regs); 373 + 374 + pwm_mediatek_clk_disable(pc, pwm->hwpwm); 375 + } 230 376 } 231 377 232 378 out: ··· 382 234 return ret; 383 235 } 384 236 385 - static int pwm_mediatek_apply(struct pwm_chip *chip, struct pwm_device *pwm, 386 - const struct pwm_state *state) 387 - { 388 - struct pwm_mediatek_chip *pc = to_pwm_mediatek_chip(chip); 389 - int err; 390 - 391 - if (state->polarity != PWM_POLARITY_NORMAL) 392 - return -EINVAL; 393 - 394 - if (!state->enabled) { 395 - if (pwm->state.enabled) { 396 - pwm_mediatek_disable(chip, pwm); 397 - pwm_mediatek_clk_disable(pc, pwm->hwpwm); 398 - } 399 - 400 - return 0; 401 - } 402 - 403 - err = pwm_mediatek_config(chip, pwm, state->duty_cycle, state->period); 404 - if (err) 405 - return err; 406 - 407 - if (!pwm->state.enabled) 408 - err = pwm_mediatek_clk_enable(pc, pwm->hwpwm); 409 - 410 - return err; 411 - } 412 - 413 - static int pwm_mediatek_get_state(struct pwm_chip *chip, struct pwm_device *pwm, 414 - struct pwm_state *state) 415 - { 416 - struct pwm_mediatek_chip *pc = to_pwm_mediatek_chip(chip); 417 - int ret; 418 - u32 enable; 419 - u32 reg_width = PWMDWIDTH, reg_thres = PWMTHRES; 420 - 421 - if (pc->soc->pwm45_fixup && pwm->hwpwm > 2) { 422 - /* 423 - * PWM[4,5] has distinct offset for PWMDWIDTH and PWMTHRES 424 - * from the other PWMs on MT7623. 425 - */ 426 - reg_width = PWM45DWIDTH_FIXUP; 427 - reg_thres = PWM45THRES_FIXUP; 428 - } 429 - 430 - ret = pwm_mediatek_clk_enable(pc, pwm->hwpwm); 431 - if (ret < 0) 432 - return ret; 433 - 434 - enable = readl(pc->regs); 435 - if (enable & BIT(pwm->hwpwm)) { 436 - u32 clkdiv, cnt_period, cnt_duty; 437 - unsigned long clk_rate; 438 - 439 - clk_rate = pc->clk_pwms[pwm->hwpwm].rate; 440 - 441 - state->enabled = true; 442 - state->polarity = PWM_POLARITY_NORMAL; 443 - 444 - clkdiv = FIELD_GET(PWMCON_CLKDIV, 445 - pwm_mediatek_readl(pc, pwm->hwpwm, PWMCON)); 446 - cnt_period = FIELD_GET(PWMDWIDTH_PERIOD, 447 - pwm_mediatek_readl(pc, pwm->hwpwm, reg_width)); 448 - cnt_duty = FIELD_GET(PWMTHRES_DUTY, 449 - pwm_mediatek_readl(pc, pwm->hwpwm, reg_thres)); 450 - 451 - /* 452 - * cnt_period is a 13 bit value, NSEC_PER_SEC is 30 bits wide 453 - * and clkdiv is less than 8, so the multiplication doesn't 454 - * overflow an u64. 455 - */ 456 - state->period = 457 - DIV_ROUND_UP_ULL((u64)cnt_period * NSEC_PER_SEC << clkdiv, clk_rate); 458 - state->duty_cycle = 459 - DIV_ROUND_UP_ULL((u64)cnt_duty * NSEC_PER_SEC << clkdiv, clk_rate); 460 - } else { 461 - state->enabled = false; 462 - } 463 - 464 - pwm_mediatek_clk_disable(pc, pwm->hwpwm); 465 - 466 - return ret; 467 - } 468 - 469 237 static const struct pwm_ops pwm_mediatek_ops = { 470 - .apply = pwm_mediatek_apply, 471 - .get_state = pwm_mediatek_get_state, 238 + .sizeof_wfhw = sizeof(struct pwm_mediatek_waveform), 239 + .round_waveform_tohw = pwm_mediatek_round_waveform_tohw, 240 + .round_waveform_fromhw = pwm_mediatek_round_waveform_fromhw, 241 + .read_waveform = pwm_mediatek_read_waveform, 242 + .write_waveform = pwm_mediatek_write_waveform, 472 243 }; 473 244 474 245 static int pwm_mediatek_init_used_clks(struct pwm_mediatek_chip *pc)