developer | 5d148cb | 2023-06-02 13:08:11 +0800 | [diff] [blame] | 1 | From 58d5a373d2d42b5292c1c2242133bcf0b082c6e0 Mon Sep 17 00:00:00 2001 |
| 2 | From: Sam Shih <sam.shih@mediatek.com> |
| 3 | Date: Fri, 2 Jun 2023 13:06:07 +0800 |
| 4 | Subject: [PATCH] |
| 5 | [slow-speed-io][999-2133-pwm-mediatek-add-longer-period-support.patch] |
| 6 | |
| 7 | --- |
| 8 | drivers/pwm/pwm-mediatek.c | 34 ++++++++++++++++++++++++++++++---- |
| 9 | 1 file changed, 30 insertions(+), 4 deletions(-) |
| 10 | |
developer | 81ac79a | 2022-10-31 13:34:43 +0800 | [diff] [blame] | 11 | diff --git a/drivers/pwm/pwm-mediatek.c b/drivers/pwm/pwm-mediatek.c |
developer | 5d148cb | 2023-06-02 13:08:11 +0800 | [diff] [blame] | 12 | index 9701092e7..79d15a9c0 100644 |
developer | 81ac79a | 2022-10-31 13:34:43 +0800 | [diff] [blame] | 13 | --- a/drivers/pwm/pwm-mediatek.c |
| 14 | +++ b/drivers/pwm/pwm-mediatek.c |
| 15 | @@ -152,8 +152,11 @@ static int pwm_mediatek_config(struct pwm_chip *chip, struct pwm_device *pwm, |
| 16 | int duty_ns, int period_ns) |
| 17 | { |
| 18 | struct pwm_mediatek_chip *pc = to_pwm_mediatek_chip(chip); |
| 19 | - u32 clkdiv = 0, cnt_period, cnt_duty, reg_width = PWMDWIDTH, |
| 20 | - reg_thres = PWMTHRES; |
| 21 | + /* The source clock is divided by 2^clkdiv or iff the clksel bit |
| 22 | + * is set by (2^clkdiv*1625) |
| 23 | + */ |
| 24 | + u32 clkdiv = 0, clksel = 0, cnt_period, cnt_duty, |
| 25 | + reg_width = PWMDWIDTH, reg_thres = PWMTHRES; |
| 26 | u64 resolution; |
| 27 | int ret; |
| 28 | |
| 29 | @@ -164,12 +167,30 @@ static int pwm_mediatek_config(struct pwm_chip *chip, struct pwm_device *pwm, |
| 30 | |
| 31 | /* Using resolution in picosecond gets accuracy higher */ |
| 32 | resolution = (u64)NSEC_PER_SEC * 1000; |
| 33 | + /* Calculate resolution based on current clock frequency */ |
| 34 | do_div(resolution, clk_get_rate(pc->clk_pwms[pwm->hwpwm])); |
| 35 | - |
| 36 | + /* Using resolution to calculate cnt_period which represents |
| 37 | + * the effective range of the PWM period counter |
| 38 | + */ |
| 39 | cnt_period = DIV_ROUND_CLOSEST_ULL((u64)period_ns * 1000, resolution); |
| 40 | while (cnt_period > 8191) { |
| 41 | + /* Using clkdiv to reduce clock frequency and calculate |
| 42 | + * new resolution based on new clock speed |
| 43 | + */ |
| 44 | resolution *= 2; |
| 45 | clkdiv++; |
| 46 | + if (clkdiv > PWM_CLK_DIV_MAX && !clksel) { |
| 47 | + /* Using clksel to divide the pwm source clock by |
| 48 | + * an additional 1625, and recalculate new clkdiv |
| 49 | + * and resolution |
| 50 | + */ |
| 51 | + clksel = 1; |
| 52 | + clkdiv = 0; |
| 53 | + resolution = (u64)NSEC_PER_SEC * 1000 * 1625; |
| 54 | + do_div(resolution, |
| 55 | + clk_get_rate(pc->clk_pwms[pwm->hwpwm])); |
| 56 | + } |
| 57 | + /* Calculate cnt_period based on resolution */ |
| 58 | cnt_period = DIV_ROUND_CLOSEST_ULL((u64)period_ns * 1000, |
| 59 | resolution); |
| 60 | } |
| 61 | @@ -189,8 +210,13 @@ static int pwm_mediatek_config(struct pwm_chip *chip, struct pwm_device *pwm, |
| 62 | reg_thres = PWM45THRES_FIXUP; |
| 63 | } |
| 64 | |
| 65 | + /* Calculate cnt_duty based on resolution */ |
| 66 | cnt_duty = DIV_ROUND_CLOSEST_ULL((u64)duty_ns * 1000, resolution); |
| 67 | - pwm_mediatek_writel(pc, pwm->hwpwm, PWMCON, BIT(15) | clkdiv); |
| 68 | + if (clksel) |
| 69 | + pwm_mediatek_writel(pc, pwm->hwpwm, PWMCON, BIT(15) | BIT(3) | |
| 70 | + clkdiv); |
| 71 | + else |
| 72 | + pwm_mediatek_writel(pc, pwm->hwpwm, PWMCON, BIT(15) | clkdiv); |
| 73 | pwm_mediatek_writel(pc, pwm->hwpwm, reg_width, cnt_period); |
| 74 | pwm_mediatek_writel(pc, pwm->hwpwm, reg_thres, cnt_duty); |
| 75 | |
developer | 5d148cb | 2023-06-02 13:08:11 +0800 | [diff] [blame] | 76 | -- |
| 77 | 2.34.1 |
| 78 | |