Commit 3c173376 authored by Uwe Kleine-König's avatar Uwe Kleine-König Committed by Thierry Reding

pwm: renesas-tpu: Improve maths to compute register settings

The newly computed register values are intended to exactly match the
previously computed values. The main improvement is that the prescaler
is computed without a loop that involves two divisions in each step.
This uses the fact, that prescalers[i] = 1 << (2 * i).

Assuming a moderately smart compiler, the needed number of divisions for
the case where the requested period is too big, is reduced from 5 to 2.
Signed-off-by: default avatarUwe Kleine-König <u.kleine-koenig@pengutronix.de>
Reviewed-by: default avatarGeert Uytterhoeven <geert+renesas@glider.be>
Signed-off-by: default avatarThierry Reding <thierry.reding@gmail.com>
parent 208ab867
...@@ -244,7 +244,6 @@ static void tpu_pwm_free(struct pwm_chip *chip, struct pwm_device *pwm) ...@@ -244,7 +244,6 @@ static void tpu_pwm_free(struct pwm_chip *chip, struct pwm_device *pwm)
static int tpu_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm, static int tpu_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm,
int duty_ns, int period_ns, bool enabled) int duty_ns, int period_ns, bool enabled)
{ {
static const unsigned int prescalers[] = { 1, 4, 16, 64 };
struct tpu_pwm_device *tpd = pwm_get_chip_data(pwm); struct tpu_pwm_device *tpd = pwm_get_chip_data(pwm);
struct tpu_device *tpu = to_tpu_device(chip); struct tpu_device *tpu = to_tpu_device(chip);
unsigned int prescaler; unsigned int prescaler;
...@@ -254,26 +253,47 @@ static int tpu_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm, ...@@ -254,26 +253,47 @@ static int tpu_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm,
u32 duty; u32 duty;
int ret; int ret;
clk_rate = clk_get_rate(tpu->clk);
period = clk_rate / (NSEC_PER_SEC / period_ns);
/* /*
* Pick a prescaler to avoid overflowing the counter. * Find the minimal prescaler in [0..3] such that
* TODO: Pick the highest acceptable prescaler. *
* period >> (2 * prescaler) < 0x10000
*
* This could be calculated using something like:
*
* prescaler = max(ilog2(period) / 2, 7) - 7;
*
* but given there are only four allowed results and that ilog2 isn't
* cheap on all platforms using a switch statement is more effective.
*/ */
clk_rate = clk_get_rate(tpu->clk); switch (period) {
case 1 ... 0xffff:
prescaler = 0;
break;
for (prescaler = 0; prescaler < ARRAY_SIZE(prescalers); ++prescaler) { case 0x10000 ... 0x3ffff:
period = clk_rate / prescalers[prescaler] prescaler = 1;
/ (NSEC_PER_SEC / period_ns); break;
if (period <= 0xffff)
break;
}
if (prescaler == ARRAY_SIZE(prescalers) || period == 0) { case 0x40000 ... 0xfffff:
dev_err(&tpu->pdev->dev, "clock rate mismatch\n"); prescaler = 2;
return -ENOTSUPP; break;
case 0x100000 ... 0x3fffff:
prescaler = 3;
break;
default:
return -EINVAL;
} }
period >>= 2 * prescaler;
if (duty_ns) { if (duty_ns) {
duty = clk_rate / prescalers[prescaler] duty = (clk_rate >> 2 * prescaler)
/ (NSEC_PER_SEC / duty_ns); / (NSEC_PER_SEC / duty_ns);
if (duty > period) if (duty > period)
return -EINVAL; return -EINVAL;
...@@ -283,7 +303,7 @@ static int tpu_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm, ...@@ -283,7 +303,7 @@ static int tpu_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm,
dev_dbg(&tpu->pdev->dev, dev_dbg(&tpu->pdev->dev,
"rate %u, prescaler %u, period %u, duty %u\n", "rate %u, prescaler %u, period %u, duty %u\n",
clk_rate, prescalers[prescaler], period, duty); clk_rate, 1 << (2 * prescaler), period, duty);
if (tpd->prescaler == prescaler && tpd->period == period) if (tpd->prescaler == prescaler && tpd->period == period)
duty_only = true; duty_only = true;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment