developer | 81ac79a | 2022-10-31 13:34:43 +0800 | [diff] [blame] | 1 | diff --git a/drivers/pwm/pwm-mediatek.c b/drivers/pwm/pwm-mediatek.c |
| 2 | index 9701092..79d15a9 100644 |
| 3 | --- a/drivers/pwm/pwm-mediatek.c |
| 4 | +++ b/drivers/pwm/pwm-mediatek.c |
| 5 | @@ -152,8 +152,11 @@ static int pwm_mediatek_config(struct pwm_chip *chip, struct pwm_device *pwm, |
| 6 | int duty_ns, int period_ns) |
| 7 | { |
| 8 | struct pwm_mediatek_chip *pc = to_pwm_mediatek_chip(chip); |
| 9 | - u32 clkdiv = 0, cnt_period, cnt_duty, reg_width = PWMDWIDTH, |
| 10 | - reg_thres = PWMTHRES; |
| 11 | + /* The source clock is divided by 2^clkdiv or iff the clksel bit |
| 12 | + * is set by (2^clkdiv*1625) |
| 13 | + */ |
| 14 | + u32 clkdiv = 0, clksel = 0, cnt_period, cnt_duty, |
| 15 | + reg_width = PWMDWIDTH, reg_thres = PWMTHRES; |
| 16 | u64 resolution; |
| 17 | int ret; |
| 18 | |
| 19 | @@ -164,12 +167,30 @@ static int pwm_mediatek_config(struct pwm_chip *chip, struct pwm_device *pwm, |
| 20 | |
| 21 | /* Using resolution in picosecond gets accuracy higher */ |
| 22 | resolution = (u64)NSEC_PER_SEC * 1000; |
| 23 | + /* Calculate resolution based on current clock frequency */ |
| 24 | do_div(resolution, clk_get_rate(pc->clk_pwms[pwm->hwpwm])); |
| 25 | - |
| 26 | + /* Using resolution to calculate cnt_period which represents |
| 27 | + * the effective range of the PWM period counter |
| 28 | + */ |
| 29 | cnt_period = DIV_ROUND_CLOSEST_ULL((u64)period_ns * 1000, resolution); |
| 30 | while (cnt_period > 8191) { |
| 31 | + /* Using clkdiv to reduce clock frequency and calculate |
| 32 | + * new resolution based on new clock speed |
| 33 | + */ |
| 34 | resolution *= 2; |
| 35 | clkdiv++; |
| 36 | + if (clkdiv > PWM_CLK_DIV_MAX && !clksel) { |
| 37 | + /* Using clksel to divide the pwm source clock by |
| 38 | + * an additional 1625, and recalculate new clkdiv |
| 39 | + * and resolution |
| 40 | + */ |
| 41 | + clksel = 1; |
| 42 | + clkdiv = 0; |
| 43 | + resolution = (u64)NSEC_PER_SEC * 1000 * 1625; |
| 44 | + do_div(resolution, |
| 45 | + clk_get_rate(pc->clk_pwms[pwm->hwpwm])); |
| 46 | + } |
| 47 | + /* Calculate cnt_period based on resolution */ |
| 48 | cnt_period = DIV_ROUND_CLOSEST_ULL((u64)period_ns * 1000, |
| 49 | resolution); |
| 50 | } |
| 51 | @@ -189,8 +210,13 @@ static int pwm_mediatek_config(struct pwm_chip *chip, struct pwm_device *pwm, |
| 52 | reg_thres = PWM45THRES_FIXUP; |
| 53 | } |
| 54 | |
| 55 | + /* Calculate cnt_duty based on resolution */ |
| 56 | cnt_duty = DIV_ROUND_CLOSEST_ULL((u64)duty_ns * 1000, resolution); |
| 57 | - pwm_mediatek_writel(pc, pwm->hwpwm, PWMCON, BIT(15) | clkdiv); |
| 58 | + if (clksel) |
| 59 | + pwm_mediatek_writel(pc, pwm->hwpwm, PWMCON, BIT(15) | BIT(3) | |
| 60 | + clkdiv); |
| 61 | + else |
| 62 | + pwm_mediatek_writel(pc, pwm->hwpwm, PWMCON, BIT(15) | clkdiv); |
| 63 | pwm_mediatek_writel(pc, pwm->hwpwm, reg_width, cnt_period); |
| 64 | pwm_mediatek_writel(pc, pwm->hwpwm, reg_thres, cnt_duty); |
| 65 | |