aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/pwm
diff options
context:
space:
mode:
authorVladimir Zapolskiy <vz@mleia.com>2015-12-06 06:32:01 -0500
committerThierry Reding <thierry.reding@gmail.com>2015-12-16 11:00:49 -0500
commit5a9fc9c666d5d759699cf5495bda85f1da0d747e (patch)
tree9e4c8c374e2bb6c890a6b5739dc1f8f962f9123a /drivers/pwm
parent82aff048dde444334c7045fab620b13058ff15a7 (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.c53
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;