diff options
author | Sean Wang <sean.wang@mediatek.com> | 2018-03-02 03:49:14 -0500 |
---|---|---|
committer | Thierry Reding <thierry.reding@gmail.com> | 2018-03-27 18:32:25 -0400 |
commit | 04c0a4e00dc11fedc0b0a8593adcf0f4310505d4 (patch) | |
tree | bf566c148ad879acf186107da631130d6ae8980d /drivers/pwm/pwm-mediatek.c | |
parent | f3617876364c0471b4779a0d05539f210693e45f (diff) |
pwm: mediatek: Improve precision in rate calculation
Add a way that turning resolution from in nanosecond into in picosecond
to improve noticeably almost 4.5% precision.
It's necessary to hold the new resolution with type u64 and thus related
operations on u64 are applied instead in those rate calculations.
And the patch has a dependency on [1].
[1] http://lists.infradead.org/pipermail/linux-mediatek/2018-March/012225.html
Cc: stable@vger.kernel.org
Fixes: caf065f8fd58 ("pwm: Add MediaTek PWM support")
Signed-off-by: Sean Wang <sean.wang@mediatek.com>
Signed-off-by: Thierry Reding <thierry.reding@gmail.com>
Diffstat (limited to 'drivers/pwm/pwm-mediatek.c')
-rw-r--r-- | drivers/pwm/pwm-mediatek.c | 17 |
1 files changed, 12 insertions, 5 deletions
diff --git a/drivers/pwm/pwm-mediatek.c b/drivers/pwm/pwm-mediatek.c index 502c366c7d7c..328c124773b2 100644 --- a/drivers/pwm/pwm-mediatek.c +++ b/drivers/pwm/pwm-mediatek.c | |||
@@ -135,19 +135,25 @@ static int mtk_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm, | |||
135 | { | 135 | { |
136 | struct mtk_pwm_chip *pc = to_mtk_pwm_chip(chip); | 136 | struct mtk_pwm_chip *pc = to_mtk_pwm_chip(chip); |
137 | struct clk *clk = pc->clks[MTK_CLK_PWM1 + pwm->hwpwm]; | 137 | struct clk *clk = pc->clks[MTK_CLK_PWM1 + pwm->hwpwm]; |
138 | u32 resolution, clkdiv = 0, reg_width = PWMDWIDTH, | 138 | u32 clkdiv = 0, cnt_period, cnt_duty, reg_width = PWMDWIDTH, |
139 | reg_thres = PWMTHRES; | 139 | reg_thres = PWMTHRES; |
140 | u64 resolution; | ||
140 | int ret; | 141 | int ret; |
141 | 142 | ||
142 | ret = mtk_pwm_clk_enable(chip, pwm); | 143 | ret = mtk_pwm_clk_enable(chip, pwm); |
143 | if (ret < 0) | 144 | if (ret < 0) |
144 | return ret; | 145 | return ret; |
145 | 146 | ||
146 | resolution = NSEC_PER_SEC / clk_get_rate(clk); | 147 | /* Using resolution in picosecond gets accuracy higher */ |
148 | resolution = (u64)NSEC_PER_SEC * 1000; | ||
149 | do_div(resolution, clk_get_rate(clk)); | ||
147 | 150 | ||
148 | while (period_ns / resolution > 8191) { | 151 | cnt_period = DIV_ROUND_CLOSEST_ULL((u64)period_ns * 1000, resolution); |
152 | while (cnt_period > 8191) { | ||
149 | resolution *= 2; | 153 | resolution *= 2; |
150 | clkdiv++; | 154 | clkdiv++; |
155 | cnt_period = DIV_ROUND_CLOSEST_ULL((u64)period_ns * 1000, | ||
156 | resolution); | ||
151 | } | 157 | } |
152 | 158 | ||
153 | if (clkdiv > PWM_CLK_DIV_MAX) { | 159 | if (clkdiv > PWM_CLK_DIV_MAX) { |
@@ -165,9 +171,10 @@ static int mtk_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm, | |||
165 | reg_thres = PWM45THRES_FIXUP; | 171 | reg_thres = PWM45THRES_FIXUP; |
166 | } | 172 | } |
167 | 173 | ||
174 | cnt_duty = DIV_ROUND_CLOSEST_ULL((u64)duty_ns * 1000, resolution); | ||
168 | mtk_pwm_writel(pc, pwm->hwpwm, PWMCON, BIT(15) | clkdiv); | 175 | mtk_pwm_writel(pc, pwm->hwpwm, PWMCON, BIT(15) | clkdiv); |
169 | mtk_pwm_writel(pc, pwm->hwpwm, reg_width, period_ns / resolution); | 176 | mtk_pwm_writel(pc, pwm->hwpwm, reg_width, cnt_period); |
170 | mtk_pwm_writel(pc, pwm->hwpwm, reg_thres, duty_ns / resolution); | 177 | mtk_pwm_writel(pc, pwm->hwpwm, reg_thres, cnt_duty); |
171 | 178 | ||
172 | mtk_pwm_clk_disable(chip, pwm); | 179 | mtk_pwm_clk_disable(chip, pwm); |
173 | 180 | ||