lkml.org 
[lkml]   [2019]   [Mar]   [27]   [last100]   RSS Feed
Views: [wrap][no wrap]   [headers]  [forward] 
 
Messages in this thread
Patch in this message
/
From
Subject[PATCH v11 14/27] pwm: jz4740: Improve algorithm of clock calculation
Date
The previous algorithm hardcoded details about how the TCU clocks work.

The new algorithm will compute the maximum frequency at which the clock
can run for the given period/duty parameters, and use clk_set_max_rate()
to ensure that we will always compute period/duty values that fit in our
16-bit register fields.

Signed-off-by: Paul Cercueil <paul@crapouillou.net>
Tested-by: Mathieu Malaterre <malat@debian.org>
Tested-by: Artur Rojek <contact@artur-rojek.eu>
---
drivers/pwm/pwm-jz4740.c | 51 +++++++++++++++++++++++++++++++++---------------
1 file changed, 35 insertions(+), 16 deletions(-)

diff --git a/drivers/pwm/pwm-jz4740.c b/drivers/pwm/pwm-jz4740.c
index 1a0eab0abafe..b55ee879e23d 100644
--- a/drivers/pwm/pwm-jz4740.c
+++ b/drivers/pwm/pwm-jz4740.c
@@ -108,24 +108,47 @@ static int jz4740_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm,
struct jz4740_pwm_chip *jz4740 = to_jz4740(pwm->chip);
struct clk *clk = pwm_get_chip_data(pwm),
*parent_clk = clk_get_parent(clk);
- unsigned long rate, period, duty;
+ unsigned long rate, parent_rate, period, duty;
unsigned long long tmp;
- unsigned int prescaler = 0;
+ int ret;

- rate = clk_get_rate(parent_clk);
- tmp = (unsigned long long)rate * state->period;
- do_div(tmp, 1000000000);
- period = tmp;
+ parent_rate = clk_get_rate(parent_clk);
+
+ jz4740_pwm_disable(chip, pwm);
+
+ /* Reset the clock to the maximum rate, and we'll reduce it if needed */
+ ret = clk_set_rate(clk, parent_rate);
+ if (ret)
+ return ret;

- while (period > 0xffff && prescaler < 6) {
- period >>= 2;
- rate >>= 2;
- ++prescaler;
+ /*
+ * Limit the clock to a maximum rate that still gives us a period value
+ * which fits in 16 bits.
+ */
+ tmp = 0xffffull * NSEC_PER_SEC;
+ do_div(tmp, state->period);
+
+ ret = clk_set_max_rate(clk, tmp);
+ if (ret) {
+ dev_err(chip->dev, "Unable to set max rate: %i\n", ret);
+ return ret;
}

- if (prescaler == 6)
- return -EINVAL;
+ /*
+ * Read back the clock rate, as it may have been modified by
+ * clk_set_max_rate()
+ */
+ rate = clk_get_rate(clk);
+
+ if (rate != parent_rate)
+ dev_dbg(chip->dev, "PWM clock updated to %lu Hz\n", rate);

+ /* Calculate period value */
+ tmp = (unsigned long long)rate * state->period;
+ do_div(tmp, NSEC_PER_SEC);
+ period = (unsigned long)tmp;
+
+ /* Calculate duty value */
tmp = (unsigned long long)period * state->duty_cycle;
do_div(tmp, state->period);
duty = period - tmp;
@@ -133,8 +156,6 @@ static int jz4740_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm,
if (duty >= period)
duty = period - 1;

- jz4740_pwm_disable(chip, pwm);
-
/*
* Set abrupt shutdown.
*
@@ -144,8 +165,6 @@ static int jz4740_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm,
regmap_update_bits(jz4740->map, TCU_REG_TCSRc(pwm->hwpwm),
TCU_TCSR_PWM_SD, TCU_TCSR_PWM_SD);

- clk_set_rate(clk, rate);
-
/* Reset counter to 0 */
regmap_write(jz4740->map, TCU_REG_TCNTc(pwm->hwpwm), 0);

--
2.11.0
\
 
 \ /
  Last update: 2019-03-28 00:19    [W:0.113 / U:10.284 seconds]
©2003-2020 Jasper Spaans|hosted at Digital Ocean and TransIP|Read the blog|Advertise on this site