Skip to content

Commit 485b56f

Browse files
pcercueithierryreding
authored andcommitted
pwm: jz4740: Improve algorithm of clock calculation
The previous algorithm hardcoded details about how the TCU clocks work. The new algorithm will use clk_round_rate to find the perfect clock rate for the PWM channel. This code relies on the fact that clk_round_rate() will always round down, which is not a valid assumption given by the clk API, but only happens to be true with the clk drivers used for Ingenic SoCs. Right now, there is no alternative as the clk API does not have a round-down function (and won't have one for a while), but if it ever comes to light, a round-down function should be used instead. Signed-off-by: Paul Cercueil <[email protected]> Tested-by: Mathieu Malaterre <[email protected]> Tested-by: Artur Rojek <[email protected]> Signed-off-by: Thierry Reding <[email protected]>
1 parent ce1f9ce commit 485b56f

File tree

1 file changed

+29
-15
lines changed

1 file changed

+29
-15
lines changed

drivers/pwm/pwm-jz4740.c

Lines changed: 29 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -112,28 +112,42 @@ static int jz4740_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm,
112112
const struct pwm_state *state)
113113
{
114114
struct jz4740_pwm_chip *jz4740 = to_jz4740(pwm->chip);
115-
struct clk *clk = pwm_get_chip_data(pwm),
116-
*parent_clk = clk_get_parent(clk);
117-
unsigned long rate, period, duty;
118-
unsigned long long tmp;
119-
unsigned int prescaler = 0;
115+
unsigned long long tmp = 0xffffull * NSEC_PER_SEC;
116+
struct clk *clk = pwm_get_chip_data(pwm);
117+
unsigned long period, duty;
120118
uint16_t ctrl;
119+
long rate;
121120
int err;
122121

123-
rate = clk_get_rate(parent_clk);
124-
tmp = (unsigned long long)rate * state->period;
125-
do_div(tmp, 1000000000);
126-
period = tmp;
122+
/*
123+
* Limit the clock to a maximum rate that still gives us a period value
124+
* which fits in 16 bits.
125+
*/
126+
do_div(tmp, state->period);
127127

128-
while (period > 0xffff && prescaler < 6) {
129-
period >>= 2;
130-
rate >>= 2;
131-
++prescaler;
128+
/*
129+
* /!\ IMPORTANT NOTE:
130+
* -------------------
131+
* This code relies on the fact that clk_round_rate() will always round
132+
* down, which is not a valid assumption given by the clk API, but only
133+
* happens to be true with the clk drivers used for Ingenic SoCs.
134+
*
135+
* Right now, there is no alternative as the clk API does not have a
136+
* round-down function (and won't have one for a while), but if it ever
137+
* comes to light, a round-down function should be used instead.
138+
*/
139+
rate = clk_round_rate(clk, tmp);
140+
if (rate < 0) {
141+
dev_err(chip->dev, "Unable to round rate: %ld", rate);
142+
return rate;
132143
}
133144

134-
if (prescaler == 6)
135-
return -EINVAL;
145+
/* Calculate period value */
146+
tmp = (unsigned long long)rate * state->period;
147+
do_div(tmp, NSEC_PER_SEC);
148+
period = (unsigned long)tmp;
136149

150+
/* Calculate duty value */
137151
tmp = (unsigned long long)period * state->duty_cycle;
138152
do_div(tmp, state->period);
139153
duty = period - tmp;

0 commit comments

Comments
 (0)