summaryrefslogtreecommitdiff
path: root/drivers/pwm/pwm-renesas-tpu.c
diff options
context:
space:
mode:
authorUwe Kleine-König <u.kleine-koenig@pengutronix.de>2022-04-20 14:12:39 +0200
committerThierry Reding <thierry.reding@gmail.com>2022-05-20 16:18:05 +0200
commit3c173376efc461dc670b02ba2846c2a533491104 (patch)
treecd48b75dfc3f01046d8b3096088dfdb96aab2526 /drivers/pwm/pwm-renesas-tpu.c
parent208ab8676b9c787b4285364fcb52a159f94b861d (diff)
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>
Diffstat (limited to 'drivers/pwm/pwm-renesas-tpu.c')
-rw-r--r--drivers/pwm/pwm-renesas-tpu.c50
1 files changed, 35 insertions, 15 deletions
diff --git a/drivers/pwm/pwm-renesas-tpu.c b/drivers/pwm/pwm-renesas-tpu.c
index 5bced2050ece..4aff3870010c 100644
--- a/drivers/pwm/pwm-renesas-tpu.c
+++ b/drivers/pwm/pwm-renesas-tpu.c
@@ -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;
+ clk_rate = clk_get_rate(tpu->clk);
+
+ period = clk_rate / (NSEC_PER_SEC / period_ns);
+
/*
- * Pick a prescaler to avoid overflowing the counter.
- * TODO: Pick the highest acceptable prescaler.
+ * 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.
*/
- clk_rate = clk_get_rate(tpu->clk);
+ switch (period) {
+ case 1 ... 0xffff:
+ prescaler = 0;
+ break;
- for (prescaler = 0; prescaler < ARRAY_SIZE(prescalers); ++prescaler) {
- period = clk_rate / prescalers[prescaler]
- / (NSEC_PER_SEC / period_ns);
- if (period <= 0xffff)
- break;
- }
+ case 0x10000 ... 0x3ffff:
+ prescaler = 1;
+ break;
- if (prescaler == ARRAY_SIZE(prescalers) || period == 0) {
- dev_err(&tpu->pdev->dev, "clock rate mismatch\n");
- return -ENOTSUPP;
+ case 0x40000 ... 0xfffff:
+ prescaler = 2;
+ break;
+
+ case 0x100000 ... 0x3fffff:
+ prescaler = 3;
+ break;
+
+ default:
+ return -EINVAL;
}
+ 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;