aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/pwm/pwm-meson.c
diff options
context:
space:
mode:
authorJerome Brunet <jbrunet@baylibre.com>2017-06-08 08:24:16 -0400
committerThierry Reding <thierry.reding@gmail.com>2017-07-06 11:15:43 -0400
commitfd7b2be8cbcf6cd6d9c9e843ffff36fb91388e51 (patch)
tree38cb89e55f2c019beae1b503392f788eddf164ff /drivers/pwm/pwm-meson.c
parentd396b20a1e88ca2cabf7ec99c6c2138902aff1f3 (diff)
pwm: meson: Improve PWM calculation precision
When using input clocks with high rates, such as clk81 (166MHz), the fin_ns = NSEC_PER_SEC / fin_freq can introduce a significant error. Ex: fin_freq = 166666667, NSEC_PER_SEC = 1000000000 fin_ns = 5,9999999 which is, of course, rounded down to 5. This introduces an error of ~20% on the period requested from the PWM. This patch uses ps instead of ns (and 64 bit integers) to perform the calculation. This should give a good enough precision. Fixes: 211ed630753d ("pwm: Add support for Meson PWM Controller") Signed-off-by: Jerome Brunet <jbrunet@baylibre.com> Acked-by: Neil Armstrong <narmstrong@baylibre.com> Signed-off-by: Thierry Reding <thierry.reding@gmail.com> squash! pwm: meson: Improve pwm calculation precision
Diffstat (limited to 'drivers/pwm/pwm-meson.c')
-rw-r--r--drivers/pwm/pwm-meson.c16
1 files changed, 10 insertions, 6 deletions
diff --git a/drivers/pwm/pwm-meson.c b/drivers/pwm/pwm-meson.c
index defc27d880f3..cb845edfe2b4 100644
--- a/drivers/pwm/pwm-meson.c
+++ b/drivers/pwm/pwm-meson.c
@@ -163,7 +163,8 @@ static int meson_pwm_calc(struct meson_pwm *meson,
163 unsigned int duty, unsigned int period) 163 unsigned int duty, unsigned int period)
164{ 164{
165 unsigned int pre_div, cnt, duty_cnt; 165 unsigned int pre_div, cnt, duty_cnt;
166 unsigned long fin_freq = -1, fin_ns; 166 unsigned long fin_freq = -1;
167 u64 fin_ps;
167 168
168 if (~(meson->inverter_mask >> id) & 0x1) 169 if (~(meson->inverter_mask >> id) & 0x1)
169 duty = period - duty; 170 duty = period - duty;
@@ -179,13 +180,15 @@ static int meson_pwm_calc(struct meson_pwm *meson,
179 } 180 }
180 181
181 dev_dbg(meson->chip.dev, "fin_freq: %lu Hz\n", fin_freq); 182 dev_dbg(meson->chip.dev, "fin_freq: %lu Hz\n", fin_freq);
182 fin_ns = NSEC_PER_SEC / fin_freq; 183 fin_ps = (u64)NSEC_PER_SEC * 1000;
184 do_div(fin_ps, fin_freq);
183 185
184 /* Calc pre_div with the period */ 186 /* Calc pre_div with the period */
185 for (pre_div = 0; pre_div < MISC_CLK_DIV_MASK; pre_div++) { 187 for (pre_div = 0; pre_div < MISC_CLK_DIV_MASK; pre_div++) {
186 cnt = DIV_ROUND_CLOSEST(period, fin_ns * (pre_div + 1)); 188 cnt = DIV_ROUND_CLOSEST_ULL((u64)period * 1000,
187 dev_dbg(meson->chip.dev, "fin_ns=%lu pre_div=%u cnt=%u\n", 189 fin_ps * (pre_div + 1));
188 fin_ns, pre_div, cnt); 190 dev_dbg(meson->chip.dev, "fin_ps=%llu pre_div=%u cnt=%u\n",
191 fin_ps, pre_div, cnt);
189 if (cnt <= 0xffff) 192 if (cnt <= 0xffff)
190 break; 193 break;
191 } 194 }
@@ -208,7 +211,8 @@ static int meson_pwm_calc(struct meson_pwm *meson,
208 channel->lo = cnt; 211 channel->lo = cnt;
209 } else { 212 } else {
210 /* Then check is we can have the duty with the same pre_div */ 213 /* Then check is we can have the duty with the same pre_div */
211 duty_cnt = DIV_ROUND_CLOSEST(duty, fin_ns * (pre_div + 1)); 214 duty_cnt = DIV_ROUND_CLOSEST_ULL((u64)duty * 1000,
215 fin_ps * (pre_div + 1));
212 if (duty_cnt > 0xffff) { 216 if (duty_cnt > 0xffff) {
213 dev_err(meson->chip.dev, "unable to get duty cycle\n"); 217 dev_err(meson->chip.dev, "unable to get duty cycle\n");
214 return -EINVAL; 218 return -EINVAL;