Skip to content

Commit 3c17337

Browse files
Uwe Kleine-Königthierryreding
authored andcommitted
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 <[email protected]> Reviewed-by: Geert Uytterhoeven <[email protected]> Signed-off-by: Thierry Reding <[email protected]>
1 parent 208ab86 commit 3c17337

File tree

1 file changed

+35
-15
lines changed

1 file changed

+35
-15
lines changed

drivers/pwm/pwm-renesas-tpu.c

Lines changed: 35 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -244,7 +244,6 @@ static void tpu_pwm_free(struct pwm_chip *chip, struct pwm_device *pwm)
244244
static int tpu_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm,
245245
int duty_ns, int period_ns, bool enabled)
246246
{
247-
static const unsigned int prescalers[] = { 1, 4, 16, 64 };
248247
struct tpu_pwm_device *tpd = pwm_get_chip_data(pwm);
249248
struct tpu_device *tpu = to_tpu_device(chip);
250249
unsigned int prescaler;
@@ -254,26 +253,47 @@ static int tpu_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm,
254253
u32 duty;
255254
int ret;
256255

256+
clk_rate = clk_get_rate(tpu->clk);
257+
258+
period = clk_rate / (NSEC_PER_SEC / period_ns);
259+
257260
/*
258-
* Pick a prescaler to avoid overflowing the counter.
259-
* TODO: Pick the highest acceptable prescaler.
261+
* Find the minimal prescaler in [0..3] such that
262+
*
263+
* period >> (2 * prescaler) < 0x10000
264+
*
265+
* This could be calculated using something like:
266+
*
267+
* prescaler = max(ilog2(period) / 2, 7) - 7;
268+
*
269+
* but given there are only four allowed results and that ilog2 isn't
270+
* cheap on all platforms using a switch statement is more effective.
260271
*/
261-
clk_rate = clk_get_rate(tpu->clk);
272+
switch (period) {
273+
case 1 ... 0xffff:
274+
prescaler = 0;
275+
break;
262276

263-
for (prescaler = 0; prescaler < ARRAY_SIZE(prescalers); ++prescaler) {
264-
period = clk_rate / prescalers[prescaler]
265-
/ (NSEC_PER_SEC / period_ns);
266-
if (period <= 0xffff)
267-
break;
268-
}
277+
case 0x10000 ... 0x3ffff:
278+
prescaler = 1;
279+
break;
269280

270-
if (prescaler == ARRAY_SIZE(prescalers) || period == 0) {
271-
dev_err(&tpu->pdev->dev, "clock rate mismatch\n");
272-
return -ENOTSUPP;
281+
case 0x40000 ... 0xfffff:
282+
prescaler = 2;
283+
break;
284+
285+
case 0x100000 ... 0x3fffff:
286+
prescaler = 3;
287+
break;
288+
289+
default:
290+
return -EINVAL;
273291
}
274292

293+
period >>= 2 * prescaler;
294+
275295
if (duty_ns) {
276-
duty = clk_rate / prescalers[prescaler]
296+
duty = (clk_rate >> 2 * prescaler)
277297
/ (NSEC_PER_SEC / duty_ns);
278298
if (duty > period)
279299
return -EINVAL;
@@ -283,7 +303,7 @@ static int tpu_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm,
283303

284304
dev_dbg(&tpu->pdev->dev,
285305
"rate %u, prescaler %u, period %u, duty %u\n",
286-
clk_rate, prescalers[prescaler], period, duty);
306+
clk_rate, 1 << (2 * prescaler), period, duty);
287307

288308
if (tpd->prescaler == prescaler && tpd->period == period)
289309
duty_only = true;

0 commit comments

Comments
 (0)