diff options
author | Vladimir Zapolskiy <vz@mleia.com> | 2015-12-06 06:32:01 -0500 |
---|---|---|
committer | Thierry Reding <thierry.reding@gmail.com> | 2015-12-16 11:00:49 -0500 |
commit | 5a9fc9c666d5d759699cf5495bda85f1da0d747e (patch) | |
tree | 9e4c8c374e2bb6c890a6b5739dc1f8f962f9123a /drivers/pwm | |
parent | 82aff048dde444334c7045fab620b13058ff15a7 (diff) |
pwm: lpc32xx: fix and simplify duty cycle and period calculations
The change fixes a problem, if duty_ns is too small in comparison
to period_ns (as a valid corner case duty_ns is 0 ns), then due to
PWM_DUTY() macro applied on a value the result is overflowed over 8
bits, and instead of the highest bitfield duty cycle value 0xff the
invalid duty cycle bitfield value 0x00 is written.
For reference the LPC32xx spec defines PWMx_DUTY bitfield description
is this way and it seems to be correct:
[Low]/[High] = [PWM_DUTY]/[256-PWM_DUTY], where 0 < PWM_DUTY <= 255.
In addition according to my oscilloscope measurements LPC32xx PWM is
"tristate" in sense that it produces a wave with floating min/max
voltage levels for different duty cycle values, for corner cases:
PWM_DUTY == 0x01 => signal is in range from -1.05v to 0v
....
PWM_DUTY == 0x80 => signal is in range from -0.75v to +0.75v
....
PWM_DUTY == 0xff => signal is in range from 0v to +1.05v
PWM_DUTY == 0x00 => signal is around 0v, PWM is off
Due to this peculiarity on very long period ranges (less than 1KHz)
and odd pre-divider values PWM generated wave does not remind a
clock shape signal, but rather a heartbit shape signal with positive
and negative peaks, so I would recommend to use high-speed HCLK clock
as a PWM parent clock and avoid using RTC clock as a parent.
The change corrects PWM output in corner cases and prevents any
possible overflows in calculation of values for PWM_DUTY and
PWM_RELOADV bitfields, thus helper macro definitions may be removed.
Signed-off-by: Vladimir Zapolskiy <vz@mleia.com>
Signed-off-by: Thierry Reding <thierry.reding@gmail.com>
Diffstat (limited to 'drivers/pwm')
-rw-r--r-- | drivers/pwm/pwm-lpc32xx.c | 53 |
1 files changed, 19 insertions, 34 deletions
diff --git a/drivers/pwm/pwm-lpc32xx.c b/drivers/pwm/pwm-lpc32xx.c index 63468a872ca7..294a68f82558 100644 --- a/drivers/pwm/pwm-lpc32xx.c +++ b/drivers/pwm/pwm-lpc32xx.c | |||
@@ -24,9 +24,7 @@ struct lpc32xx_pwm_chip { | |||
24 | void __iomem *base; | 24 | void __iomem *base; |
25 | }; | 25 | }; |
26 | 26 | ||
27 | #define PWM_ENABLE (1 << 31) | 27 | #define PWM_ENABLE BIT(31) |
28 | #define PWM_RELOADV(x) (((x) & 0xFF) << 8) | ||
29 | #define PWM_DUTY(x) ((x) & 0xFF) | ||
30 | 28 | ||
31 | #define to_lpc32xx_pwm_chip(_chip) \ | 29 | #define to_lpc32xx_pwm_chip(_chip) \ |
32 | container_of(_chip, struct lpc32xx_pwm_chip, chip) | 30 | container_of(_chip, struct lpc32xx_pwm_chip, chip) |
@@ -38,40 +36,27 @@ static int lpc32xx_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm, | |||
38 | unsigned long long c; | 36 | unsigned long long c; |
39 | int period_cycles, duty_cycles; | 37 | int period_cycles, duty_cycles; |
40 | u32 val; | 38 | u32 val; |
41 | 39 | c = clk_get_rate(lpc32xx->clk); | |
42 | c = clk_get_rate(lpc32xx->clk) / 256; | 40 | |
43 | c = c * period_ns; | 41 | /* The highest acceptable divisor is 256, which is represented by 0 */ |
44 | do_div(c, NSEC_PER_SEC); | 42 | period_cycles = div64_u64(c * period_ns, |
45 | 43 | (unsigned long long)NSEC_PER_SEC * 256); | |
46 | /* Handle high and low extremes */ | 44 | if (!period_cycles) |
47 | if (c == 0) | 45 | period_cycles = 1; |
48 | c = 1; | 46 | if (period_cycles > 255) |
49 | if (c > 255) | 47 | period_cycles = 0; |
50 | c = 0; /* 0 set division by 256 */ | 48 | |
51 | period_cycles = c; | 49 | /* Compute 256 x #duty/period value and care for corner cases */ |
52 | 50 | duty_cycles = div64_u64((unsigned long long)(period_ns - duty_ns) * 256, | |
53 | /* The duty-cycle value is as follows: | 51 | period_ns); |
54 | * | 52 | if (!duty_cycles) |
55 | * DUTY-CYCLE HIGH LEVEL | 53 | duty_cycles = 1; |
56 | * 1 99.9% | 54 | if (duty_cycles > 255) |
57 | * 25 90.0% | 55 | duty_cycles = 255; |
58 | * 128 50.0% | ||
59 | * 220 10.0% | ||
60 | * 255 0.1% | ||
61 | * 0 0.0% | ||
62 | * | ||
63 | * In other words, the register value is duty-cycle % 256 with | ||
64 | * duty-cycle in the range 1-256. | ||
65 | */ | ||
66 | c = 256 * duty_ns; | ||
67 | do_div(c, period_ns); | ||
68 | if (c > 255) | ||
69 | c = 255; | ||
70 | duty_cycles = 256 - c; | ||
71 | 56 | ||
72 | val = readl(lpc32xx->base + (pwm->hwpwm << 2)); | 57 | val = readl(lpc32xx->base + (pwm->hwpwm << 2)); |
73 | val &= ~0xFFFF; | 58 | val &= ~0xFFFF; |
74 | val |= PWM_RELOADV(period_cycles) | PWM_DUTY(duty_cycles); | 59 | val |= (period_cycles << 8) | duty_cycles; |
75 | writel(val, lpc32xx->base + (pwm->hwpwm << 2)); | 60 | writel(val, lpc32xx->base + (pwm->hwpwm << 2)); |
76 | 61 | ||
77 | return 0; | 62 | return 0; |