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: Uwe Kleine-König <u.kleine-koenig@pengutronix.de> Reviewed-by: Geert Uytterhoeven <geert+renesas@glider.be> Signed-off-by: Thierry Reding <thierry.reding@gmail.com>
This commit is contained in:
parent
208ab8676b
commit
3c173376ef
|
@ -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,
|
||||
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_device *tpu = to_tpu_device(chip);
|
||||
unsigned int prescaler;
|
||||
|
@ -254,26 +253,47 @@ static int tpu_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm,
|
|||
u32 duty;
|
||||
int ret;
|
||||
|
||||
/*
|
||||
* Pick a prescaler to avoid overflowing the counter.
|
||||
* TODO: Pick the highest acceptable prescaler.
|
||||
*/
|
||||
clk_rate = clk_get_rate(tpu->clk);
|
||||
|
||||
for (prescaler = 0; prescaler < ARRAY_SIZE(prescalers); ++prescaler) {
|
||||
period = clk_rate / prescalers[prescaler]
|
||||
/ (NSEC_PER_SEC / period_ns);
|
||||
if (period <= 0xffff)
|
||||
break;
|
||||
period = clk_rate / (NSEC_PER_SEC / period_ns);
|
||||
|
||||
/*
|
||||
* Find the minimal prescaler in [0..3] such that
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
switch (period) {
|
||||
case 1 ... 0xffff:
|
||||
prescaler = 0;
|
||||
break;
|
||||
|
||||
case 0x10000 ... 0x3ffff:
|
||||
prescaler = 1;
|
||||
break;
|
||||
|
||||
case 0x40000 ... 0xfffff:
|
||||
prescaler = 2;
|
||||
break;
|
||||
|
||||
case 0x100000 ... 0x3fffff:
|
||||
prescaler = 3;
|
||||
break;
|
||||
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (prescaler == ARRAY_SIZE(prescalers) || period == 0) {
|
||||
dev_err(&tpu->pdev->dev, "clock rate mismatch\n");
|
||||
return -ENOTSUPP;
|
||||
}
|
||||
period >>= 2 * prescaler;
|
||||
|
||||
if (duty_ns) {
|
||||
duty = clk_rate / prescalers[prescaler]
|
||||
duty = (clk_rate >> 2 * prescaler)
|
||||
/ (NSEC_PER_SEC / duty_ns);
|
||||
if (duty > period)
|
||||
return -EINVAL;
|
||||
|
@ -283,7 +303,7 @@ static int tpu_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm,
|
|||
|
||||
dev_dbg(&tpu->pdev->dev,
|
||||
"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)
|
||||
duty_only = true;
|
||||
|
|
Loading…
Reference in New Issue