From: Vladimir Zapolskiy <vz@mleia.com>
To: Thierry Reding <thierry.reding@gmail.com>,
Rob Herring <robh@kernel.org>, Arnd Bergmann <arnd@arndb.de>
Cc: Roland Stigge <stigge@antcom.de>,
linux-arm-kernel@lists.infradead.org, devicetree@vger.kernel.org,
linux-pwm@vger.kernel.org
Subject: [PATCH v2 5/6] pwm: lpc32xx: fix and simplify duty cycle and period calculations
Date: Sun, 6 Dec 2015 13:29:16 +0200 [thread overview]
Message-ID: <1449401357-22458-6-git-send-email-vz@mleia.com> (raw)
In-Reply-To: <1449401357-22458-1-git-send-email-vz@mleia.com>
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>
---
Changes from v1 to v2:
- none
drivers/pwm/pwm-lpc32xx.c | 53 +++++++++++++++++------------------------------
1 file changed, 19 insertions(+), 34 deletions(-)
diff --git a/drivers/pwm/pwm-lpc32xx.c b/drivers/pwm/pwm-lpc32xx.c
index 63468a8..294a68f 100644
--- a/drivers/pwm/pwm-lpc32xx.c
+++ b/drivers/pwm/pwm-lpc32xx.c
@@ -24,9 +24,7 @@ struct lpc32xx_pwm_chip {
void __iomem *base;
};
-#define PWM_ENABLE (1 << 31)
-#define PWM_RELOADV(x) (((x) & 0xFF) << 8)
-#define PWM_DUTY(x) ((x) & 0xFF)
+#define PWM_ENABLE BIT(31)
#define to_lpc32xx_pwm_chip(_chip) \
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,
unsigned long long c;
int period_cycles, duty_cycles;
u32 val;
-
- c = clk_get_rate(lpc32xx->clk) / 256;
- c = c * period_ns;
- do_div(c, NSEC_PER_SEC);
-
- /* Handle high and low extremes */
- if (c == 0)
- c = 1;
- if (c > 255)
- c = 0; /* 0 set division by 256 */
- period_cycles = c;
-
- /* The duty-cycle value is as follows:
- *
- * DUTY-CYCLE HIGH LEVEL
- * 1 99.9%
- * 25 90.0%
- * 128 50.0%
- * 220 10.0%
- * 255 0.1%
- * 0 0.0%
- *
- * In other words, the register value is duty-cycle % 256 with
- * duty-cycle in the range 1-256.
- */
- c = 256 * duty_ns;
- do_div(c, period_ns);
- if (c > 255)
- c = 255;
- duty_cycles = 256 - c;
+ c = clk_get_rate(lpc32xx->clk);
+
+ /* The highest acceptable divisor is 256, which is represented by 0 */
+ period_cycles = div64_u64(c * period_ns,
+ (unsigned long long)NSEC_PER_SEC * 256);
+ if (!period_cycles)
+ period_cycles = 1;
+ if (period_cycles > 255)
+ period_cycles = 0;
+
+ /* Compute 256 x #duty/period value and care for corner cases */
+ duty_cycles = div64_u64((unsigned long long)(period_ns - duty_ns) * 256,
+ period_ns);
+ if (!duty_cycles)
+ duty_cycles = 1;
+ if (duty_cycles > 255)
+ duty_cycles = 255;
val = readl(lpc32xx->base + (pwm->hwpwm << 2));
val &= ~0xFFFF;
- val |= PWM_RELOADV(period_cycles) | PWM_DUTY(duty_cycles);
+ val |= (period_cycles << 8) | duty_cycles;
writel(val, lpc32xx->base + (pwm->hwpwm << 2));
return 0;
--
2.1.4
next prev parent reply other threads:[~2015-12-06 11:29 UTC|newest]
Thread overview: 9+ messages / expand[flat|nested] mbox.gz Atom feed top
2015-12-06 11:29 [PATCH v2 0/6] pwm: lpc32xx: fixes in the LPC32xx PWM driver Vladimir Zapolskiy
2015-12-06 11:29 ` [PATCH v2 1/6] dt: lpc32xx: pwm: correct LPC32xx PWM device node example Vladimir Zapolskiy
2015-12-06 11:29 ` [PATCH v2 2/6] dt: lpc32xx: pwm: update documentation of LPC32xx PWM device Vladimir Zapolskiy
2015-12-06 11:29 ` [PATCH v2 3/6] pwm: lpc32xx: correct number of PWM channels from 2 to 1 Vladimir Zapolskiy
2015-12-06 11:29 ` [PATCH v2 4/6] pwm: lpc32xx: make device usable with common clock framework Vladimir Zapolskiy
2015-12-06 11:29 ` Vladimir Zapolskiy [this message]
2015-12-06 11:29 ` [PATCH v2 6/6] pwm: lpc32xx: return ERANGE, if requested period is not supported Vladimir Zapolskiy
-- strict thread matches above, loose matches on Subject: below --
2015-12-06 11:31 [PATCH v2 0/6] pwm: lpc32xx: fixes in the LPC32xx PWM driver Vladimir Zapolskiy
2015-12-06 11:32 ` [PATCH v2 5/6] pwm: lpc32xx: fix and simplify duty cycle and period calculations Vladimir Zapolskiy
2015-12-06 11:32 ` Vladimir Zapolskiy
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=1449401357-22458-6-git-send-email-vz@mleia.com \
--to=vz@mleia.com \
--cc=arnd@arndb.de \
--cc=devicetree@vger.kernel.org \
--cc=linux-arm-kernel@lists.infradead.org \
--cc=linux-pwm@vger.kernel.org \
--cc=robh@kernel.org \
--cc=stigge@antcom.de \
--cc=thierry.reding@gmail.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.