Skip to content

Commit 8035e6c

Browse files
Uwe Kleine-Königthierryreding
authored andcommitted
pwm: atmel: Improve duty cycle calculation in .apply()
In the calculation of the register value determining the duty cycle the requested period is used instead of the actually implemented period which results in suboptimal settings. The following example assumes an input clock of 133333333 Hz on one of the SoCs with 16 bit period. When the following state is to be applied: .period = 414727681 .duty_cycle = 652806 the following register values used to be calculated: PRES = 10 CPRD = 54000 CDTY = 53916 which yields an actual duty cycle of a bit more than 645120 ns. The setting PRES = 10 CPRD = 54000 CDTY = 53915 however yields a duty of 652800 ns which is between the current result and the requested value and so is a better approximation. The reason for this error is that for the calculation of CDTY the requested period was used instead of the actually implemented one. Signed-off-by: Uwe Kleine-König <[email protected]> Signed-off-by: Thierry Reding <[email protected]>
1 parent 453e8b3 commit 8035e6c

File tree

1 file changed

+16
-7
lines changed

1 file changed

+16
-7
lines changed

drivers/pwm/pwm-atmel.c

Lines changed: 16 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -124,6 +124,7 @@ static inline void atmel_pwm_ch_writel(struct atmel_pwm_chip *chip,
124124
}
125125

126126
static int atmel_pwm_calculate_cprd_and_pres(struct pwm_chip *chip,
127+
unsigned long clkrate,
127128
const struct pwm_state *state,
128129
unsigned long *cprd, u32 *pres)
129130
{
@@ -132,7 +133,7 @@ static int atmel_pwm_calculate_cprd_and_pres(struct pwm_chip *chip,
132133
int shift;
133134

134135
/* Calculate the period cycles and prescale value */
135-
cycles *= clk_get_rate(atmel_pwm->clk);
136+
cycles *= clkrate;
136137
do_div(cycles, NSEC_PER_SEC);
137138

138139
/*
@@ -158,12 +159,14 @@ static int atmel_pwm_calculate_cprd_and_pres(struct pwm_chip *chip,
158159
}
159160

160161
static void atmel_pwm_calculate_cdty(const struct pwm_state *state,
161-
unsigned long cprd, unsigned long *cdty)
162+
unsigned long clkrate, unsigned long cprd,
163+
u32 pres, unsigned long *cdty)
162164
{
163165
unsigned long long cycles = state->duty_cycle;
164166

165-
cycles *= cprd;
166-
do_div(cycles, state->period);
167+
cycles *= clkrate;
168+
do_div(cycles, NSEC_PER_SEC);
169+
cycles >>= pres;
167170
*cdty = cprd - cycles;
168171
}
169172

@@ -244,25 +247,31 @@ static int atmel_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm,
244247
pwm_get_state(pwm, &cstate);
245248

246249
if (state->enabled) {
250+
unsigned long clkrate = clk_get_rate(atmel_pwm->clk);
251+
247252
if (cstate.enabled &&
248253
cstate.polarity == state->polarity &&
249254
cstate.period == state->period) {
255+
u32 cmr = atmel_pwm_ch_readl(atmel_pwm, pwm->hwpwm, PWM_CMR);
256+
250257
cprd = atmel_pwm_ch_readl(atmel_pwm, pwm->hwpwm,
251258
atmel_pwm->data->regs.period);
252-
atmel_pwm_calculate_cdty(state, cprd, &cdty);
259+
pres = cmr & PWM_CMR_CPRE_MSK;
260+
261+
atmel_pwm_calculate_cdty(state, clkrate, cprd, pres, &cdty);
253262
atmel_pwm_update_cdty(chip, pwm, cdty);
254263
return 0;
255264
}
256265

257-
ret = atmel_pwm_calculate_cprd_and_pres(chip, state, &cprd,
266+
ret = atmel_pwm_calculate_cprd_and_pres(chip, clkrate, state, &cprd,
258267
&pres);
259268
if (ret) {
260269
dev_err(chip->dev,
261270
"failed to calculate cprd and prescaler\n");
262271
return ret;
263272
}
264273

265-
atmel_pwm_calculate_cdty(state, cprd, &cdty);
274+
atmel_pwm_calculate_cdty(state, clkrate, cprd, pres, &cdty);
266275

267276
if (cstate.enabled) {
268277
atmel_pwm_disable(chip, pwm, false);

0 commit comments

Comments
 (0)