|
41 | 41 | #define AXI_PWMGEN_LOAD_CONIG BIT(1)
|
42 | 42 | #define AXI_PWMGEN_RESET BIT(0)
|
43 | 43 |
|
44 |
| -#define AXI_PWMGEN_PSEC_PER_SEC 1000000000000ULL |
45 | 44 | #define AXI_PWMGEN_N_MAX_PWMS 16
|
46 | 45 |
|
47 | 46 | static const unsigned long long axi_pwmgen_scales[] = {
|
@@ -88,29 +87,44 @@ static inline struct axi_pwmgen *to_axi_pwmgen(struct pwm_chip *chip)
|
88 | 87 | return container_of(chip, struct axi_pwmgen, chip);
|
89 | 88 | }
|
90 | 89 |
|
| 90 | +#ifndef mul_u64_u64_div_u64_roundclosest |
| 91 | +static u64 mul_u64_u64_div_u64_roundclosest(u64 a, u64 b, u64 c) |
| 92 | +{ |
| 93 | + u64 res = mul_u64_u64_div_u64(a, b, c); |
| 94 | + /* |
| 95 | + * Those multiplications might overflow but after the subtraction the |
| 96 | + * error cancels out. |
| 97 | + */ |
| 98 | + u64 rem = a * b - c * res; |
| 99 | + |
| 100 | + if (rem * 2 >= c) |
| 101 | + res += 1; |
| 102 | + |
| 103 | + return res; |
| 104 | +} |
| 105 | +#endif |
| 106 | + |
91 | 107 | static int axi_pwmgen_apply(struct pwm_chip *chip, struct pwm_device *pwm,
|
92 | 108 | const struct pwm_state *state)
|
93 | 109 | {
|
94 | 110 | unsigned long rate;
|
95 |
| - unsigned long long clk_period_ps, target, cnt; |
| 111 | + unsigned long long cnt; |
96 | 112 | unsigned int ch = pwm->hwpwm;
|
97 |
| - struct axi_pwmgen *pwmgen; |
| 113 | + struct axi_pwmgen *pwmgen = to_axi_pwmgen(chip); |
98 | 114 |
|
99 |
| - pwmgen = to_axi_pwmgen(chip); |
100 | 115 | rate = clk_get_rate(pwmgen->clk);
|
101 |
| - clk_period_ps = DIV_ROUND_CLOSEST_ULL(AXI_PWMGEN_PSEC_PER_SEC, rate); |
102 | 116 |
|
103 |
| - target = state->period * axi_pwmgen_scales[state->time_unit]; |
104 |
| - cnt = target ? DIV_ROUND_CLOSEST_ULL(target, clk_period_ps) : 0; |
| 117 | + cnt = mul_u64_u64_div_u64_roundclosest(state->period * axi_pwmgen_scales[state->time_unit], |
| 118 | + rate, PSEC_PER_SEC); |
105 | 119 | axi_pwmgen_write(pwmgen, AXI_PWMGEN_CHX_PERIOD(pwmgen, ch),
|
106 | 120 | state->enabled ? cnt : 0);
|
107 | 121 |
|
108 |
| - target = state->duty_cycle * axi_pwmgen_scales[state->time_unit]; |
109 |
| - cnt = target ? DIV_ROUND_CLOSEST_ULL(target, clk_period_ps) : 0; |
| 122 | + cnt = mul_u64_u64_div_u64_roundclosest(state->duty_cycle * axi_pwmgen_scales[state->time_unit], |
| 123 | + rate, PSEC_PER_SEC); |
110 | 124 | axi_pwmgen_write(pwmgen, AXI_PWMGEN_CHX_DUTY(pwmgen, ch), cnt);
|
111 | 125 |
|
112 |
| - target = state->phase * axi_pwmgen_scales[state->time_unit]; |
113 |
| - cnt = target ? DIV_ROUND_CLOSEST_ULL(target, clk_period_ps) : 0; |
| 126 | + cnt = mul_u64_u64_div_u64_roundclosest(state->phase * axi_pwmgen_scales[state->time_unit], |
| 127 | + rate, PSEC_PER_SEC); |
114 | 128 | axi_pwmgen_write(pwmgen, AXI_PWMGEN_CHX_PHASE(pwmgen, ch), cnt);
|
115 | 129 |
|
116 | 130 | /* Apply the new config */
|
|
0 commit comments