Skip to content

Commit e10b748

Browse files
ukleineknunojsa
authored andcommitted
pwm: axi-pwmgen: Improve precision in .get_state()
axi_pwmgen_get_state() does an integer division first and then multiplies the result. This way the possible error of the result is the error of the division (i.e. 0.5) multiplied by the other factor. With a typical clk_rate of 166666665 Hz the error isn't that visible because AXI_PWMGEN_PSEC_PER_SEC / rate is nearly integer. But even then it's visible for big register values: With AXI_PWMGEN_CHX_PERIOD = 0xffffffff we get: clk_period_ps = 6000; /* exact value: 6000.00006 */ state->period = 25769803770; The exact value is 25769804027.69804 which corresponds to an absolute error of approximately 257.7 ns. With other rates, the error is considerably worse (consider rate = 176102844 Hz, with the same value of AXI_PWMGEN_CHX_PERIOD the absolute error is approx. 2147475 ns) Improve precision by doing the multiplication first and only divide then. In the above examples the error is reduced to less than 1. As a side effect the number of divisions is reduced from four to three which probably even improves run time. Signed-off-by: Uwe Kleine-König <u.kleine-koenig@baylibre.com>
1 parent 656624d commit e10b748

File tree

1 file changed

+6
-17
lines changed

1 file changed

+6
-17
lines changed

drivers/pwm/pwm-axi-pwmgen.c

Lines changed: 6 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -129,32 +129,21 @@ static void axi_pwmgen_get_state(struct pwm_chip *chip, struct pwm_device *pwm,
129129
{
130130
struct axi_pwmgen *pwmgen = to_axi_pwmgen(chip);
131131
unsigned long rate;
132-
unsigned long long cnt, clk_period_ps;
132+
unsigned long long cnt;
133133
unsigned int ch = pwm->hwpwm;
134134

135135
rate = clk_get_rate(pwmgen->clk);
136136
if (!rate)
137137
return;
138138

139-
clk_period_ps = DIV_ROUND_CLOSEST_ULL(AXI_PWMGEN_PSEC_PER_SEC, rate);
140139
cnt = axi_pwmgen_read(pwmgen, AXI_PWMGEN_CHX_PERIOD(pwmgen, ch));
141-
cnt *= clk_period_ps;
142-
if (cnt)
143-
state->period = DIV_ROUND_CLOSEST_ULL(cnt, PSEC_PER_NSEC);
144-
else
145-
state->period = 0;
140+
state->period = DIV_ROUND_CLOSEST_ULL(cnt * NSEC_PER_SEC, rate);
141+
146142
cnt = axi_pwmgen_read(pwmgen, AXI_PWMGEN_CHX_DUTY(pwmgen, ch));
147-
cnt *= clk_period_ps;
148-
if (cnt)
149-
state->duty_cycle = DIV_ROUND_CLOSEST_ULL(cnt, PSEC_PER_NSEC);
150-
else
151-
state->duty_cycle = 0;
143+
state->duty_cycle = DIV_ROUND_CLOSEST_ULL(cnt * NSEC_PER_SEC, rate);
144+
152145
cnt = axi_pwmgen_read(pwmgen, AXI_PWMGEN_CHX_PHASE(pwmgen, ch));
153-
cnt *= clk_period_ps;
154-
if (cnt)
155-
state->phase = DIV_ROUND_CLOSEST_ULL(cnt, PSEC_PER_NSEC);
156-
else
157-
state->phase = 0;
146+
state->phase = DIV_ROUND_CLOSEST_ULL(cnt * NSEC_PER_SEC, rate);
158147

159148
state->enabled = state->period > 0;
160149
state->time_unit = PWM_UNIT_NSEC;

0 commit comments

Comments
 (0)