diff options
author | Boris Brezillon <boris.brezillon@free-electrons.com> | 2017-03-01 09:48:51 -0500 |
---|---|---|
committer | Thierry Reding <thierry.reding@gmail.com> | 2017-04-06 10:12:52 -0400 |
commit | 2267517cd3ee4a1b02c7b9fead051c9d079c9fc3 (patch) | |
tree | 549bd2ae24ba06ae6bf9858a8336d2c0a7110681 /drivers/pwm/pwm-atmel-hlcdc.c | |
parent | c1ae3cfa0e89fa1a7ecc4c99031f5e9ae99d9201 (diff) |
pwm: atmel-hlcdc: Convert to the atomic PWM API
Implement the ->apply() hook and drop the ->enable(), ->disable,
->set_polarity and ->config() ones.
Signed-off-by: Boris Brezillon <boris.brezillon@free-electrons.com>
Acked-by: Nicolas Ferre <nicolas.ferre@microchip.com>
Signed-off-by: Thierry Reding <thierry.reding@gmail.com>
Diffstat (limited to 'drivers/pwm/pwm-atmel-hlcdc.c')
-rw-r--r-- | drivers/pwm/pwm-atmel-hlcdc.c | 227 |
1 files changed, 101 insertions, 126 deletions
diff --git a/drivers/pwm/pwm-atmel-hlcdc.c b/drivers/pwm/pwm-atmel-hlcdc.c index 999187277ea5..bcb6d946131d 100644 --- a/drivers/pwm/pwm-atmel-hlcdc.c +++ b/drivers/pwm/pwm-atmel-hlcdc.c | |||
@@ -49,162 +49,137 @@ static inline struct atmel_hlcdc_pwm *to_atmel_hlcdc_pwm(struct pwm_chip *chip) | |||
49 | return container_of(chip, struct atmel_hlcdc_pwm, chip); | 49 | return container_of(chip, struct atmel_hlcdc_pwm, chip); |
50 | } | 50 | } |
51 | 51 | ||
52 | static int atmel_hlcdc_pwm_config(struct pwm_chip *c, | 52 | static int atmel_hlcdc_pwm_apply(struct pwm_chip *c, struct pwm_device *pwm, |
53 | struct pwm_device *pwm, | 53 | struct pwm_state *state) |
54 | int duty_ns, int period_ns) | ||
55 | { | 54 | { |
56 | struct atmel_hlcdc_pwm *chip = to_atmel_hlcdc_pwm(c); | 55 | struct atmel_hlcdc_pwm *chip = to_atmel_hlcdc_pwm(c); |
57 | struct atmel_hlcdc *hlcdc = chip->hlcdc; | 56 | struct atmel_hlcdc *hlcdc = chip->hlcdc; |
58 | struct clk *new_clk = hlcdc->slow_clk; | 57 | unsigned int status; |
59 | u64 pwmcval = duty_ns * 256; | 58 | int ret; |
60 | unsigned long clk_freq; | ||
61 | u64 clk_period_ns; | ||
62 | u32 pwmcfg; | ||
63 | int pres; | ||
64 | |||
65 | if (!chip->errata || !chip->errata->slow_clk_erratum) { | ||
66 | clk_freq = clk_get_rate(new_clk); | ||
67 | if (!clk_freq) | ||
68 | return -EINVAL; | ||
69 | |||
70 | clk_period_ns = (u64)NSEC_PER_SEC * 256; | ||
71 | do_div(clk_period_ns, clk_freq); | ||
72 | } | ||
73 | |||
74 | /* Errata: cannot use slow clk on some IP revisions */ | ||
75 | if ((chip->errata && chip->errata->slow_clk_erratum) || | ||
76 | clk_period_ns > period_ns) { | ||
77 | new_clk = hlcdc->sys_clk; | ||
78 | clk_freq = clk_get_rate(new_clk); | ||
79 | if (!clk_freq) | ||
80 | return -EINVAL; | ||
81 | |||
82 | clk_period_ns = (u64)NSEC_PER_SEC * 256; | ||
83 | do_div(clk_period_ns, clk_freq); | ||
84 | } | ||
85 | 59 | ||
86 | for (pres = 0; pres <= ATMEL_HLCDC_PWMPS_MAX; pres++) { | 60 | if (state->enabled) { |
61 | struct clk *new_clk = hlcdc->slow_clk; | ||
62 | u64 pwmcval = state->duty_cycle * 256; | ||
63 | unsigned long clk_freq; | ||
64 | u64 clk_period_ns; | ||
65 | u32 pwmcfg; | ||
66 | int pres; | ||
67 | |||
68 | if (!chip->errata || !chip->errata->slow_clk_erratum) { | ||
69 | clk_freq = clk_get_rate(new_clk); | ||
70 | if (!clk_freq) | ||
71 | return -EINVAL; | ||
72 | |||
73 | clk_period_ns = (u64)NSEC_PER_SEC * 256; | ||
74 | do_div(clk_period_ns, clk_freq); | ||
75 | } | ||
76 | |||
77 | /* Errata: cannot use slow clk on some IP revisions */ | ||
78 | if ((chip->errata && chip->errata->slow_clk_erratum) || | ||
79 | clk_period_ns > state->period) { | ||
80 | new_clk = hlcdc->sys_clk; | ||
81 | clk_freq = clk_get_rate(new_clk); | ||
82 | if (!clk_freq) | ||
83 | return -EINVAL; | ||
84 | |||
85 | clk_period_ns = (u64)NSEC_PER_SEC * 256; | ||
86 | do_div(clk_period_ns, clk_freq); | ||
87 | } | ||
88 | |||
89 | for (pres = 0; pres <= ATMEL_HLCDC_PWMPS_MAX; pres++) { | ||
87 | /* Errata: cannot divide by 1 on some IP revisions */ | 90 | /* Errata: cannot divide by 1 on some IP revisions */ |
88 | if (!pres && chip->errata && chip->errata->div1_clk_erratum) | 91 | if (!pres && chip->errata && |
89 | continue; | 92 | chip->errata->div1_clk_erratum) |
90 | 93 | continue; | |
91 | if ((clk_period_ns << pres) >= period_ns) | ||
92 | break; | ||
93 | } | ||
94 | 94 | ||
95 | if (pres > ATMEL_HLCDC_PWMPS_MAX) | 95 | if ((clk_period_ns << pres) >= state->period) |
96 | return -EINVAL; | 96 | break; |
97 | } | ||
97 | 98 | ||
98 | pwmcfg = ATMEL_HLCDC_PWMPS(pres); | 99 | if (pres > ATMEL_HLCDC_PWMPS_MAX) |
99 | 100 | return -EINVAL; | |
100 | if (new_clk != chip->cur_clk) { | ||
101 | u32 gencfg = 0; | ||
102 | int ret; | ||
103 | |||
104 | ret = clk_prepare_enable(new_clk); | ||
105 | if (ret) | ||
106 | return ret; | ||
107 | 101 | ||
108 | clk_disable_unprepare(chip->cur_clk); | 102 | pwmcfg = ATMEL_HLCDC_PWMPS(pres); |
109 | chip->cur_clk = new_clk; | ||
110 | 103 | ||
111 | if (new_clk == hlcdc->sys_clk) | 104 | if (new_clk != chip->cur_clk) { |
112 | gencfg = ATMEL_HLCDC_CLKPWMSEL; | 105 | u32 gencfg = 0; |
106 | int ret; | ||
113 | 107 | ||
114 | ret = regmap_update_bits(hlcdc->regmap, ATMEL_HLCDC_CFG(0), | 108 | ret = clk_prepare_enable(new_clk); |
115 | ATMEL_HLCDC_CLKPWMSEL, gencfg); | 109 | if (ret) |
116 | if (ret) | 110 | return ret; |
117 | return ret; | ||
118 | } | ||
119 | 111 | ||
120 | do_div(pwmcval, period_ns); | 112 | clk_disable_unprepare(chip->cur_clk); |
113 | chip->cur_clk = new_clk; | ||
121 | 114 | ||
122 | /* | 115 | if (new_clk == hlcdc->sys_clk) |
123 | * The PWM duty cycle is configurable from 0/256 to 255/256 of the | 116 | gencfg = ATMEL_HLCDC_CLKPWMSEL; |
124 | * period cycle. Hence we can't set a duty cycle occupying the | ||
125 | * whole period cycle if we're asked to. | ||
126 | * Set it to 255 if pwmcval is greater than 256. | ||
127 | */ | ||
128 | if (pwmcval > 255) | ||
129 | pwmcval = 255; | ||
130 | 117 | ||
131 | pwmcfg |= ATMEL_HLCDC_PWMCVAL(pwmcval); | 118 | ret = regmap_update_bits(hlcdc->regmap, |
119 | ATMEL_HLCDC_CFG(0), | ||
120 | ATMEL_HLCDC_CLKPWMSEL, | ||
121 | gencfg); | ||
122 | if (ret) | ||
123 | return ret; | ||
124 | } | ||
132 | 125 | ||
133 | return regmap_update_bits(hlcdc->regmap, ATMEL_HLCDC_CFG(6), | 126 | do_div(pwmcval, state->period); |
134 | ATMEL_HLCDC_PWMCVAL_MASK | | ||
135 | ATMEL_HLCDC_PWMPS_MASK, | ||
136 | pwmcfg); | ||
137 | } | ||
138 | 127 | ||
139 | static int atmel_hlcdc_pwm_set_polarity(struct pwm_chip *c, | 128 | /* |
140 | struct pwm_device *pwm, | 129 | * The PWM duty cycle is configurable from 0/256 to 255/256 of |
141 | enum pwm_polarity polarity) | 130 | * the period cycle. Hence we can't set a duty cycle occupying |
142 | { | 131 | * the whole period cycle if we're asked to. |
143 | struct atmel_hlcdc_pwm *chip = to_atmel_hlcdc_pwm(c); | 132 | * Set it to 255 if pwmcval is greater than 256. |
144 | struct atmel_hlcdc *hlcdc = chip->hlcdc; | 133 | */ |
145 | u32 cfg = 0; | 134 | if (pwmcval > 255) |
135 | pwmcval = 255; | ||
146 | 136 | ||
147 | if (polarity == PWM_POLARITY_NORMAL) | 137 | pwmcfg |= ATMEL_HLCDC_PWMCVAL(pwmcval); |
148 | cfg = ATMEL_HLCDC_PWMPOL; | ||
149 | 138 | ||
150 | return regmap_update_bits(hlcdc->regmap, ATMEL_HLCDC_CFG(6), | 139 | if (state->polarity == PWM_POLARITY_NORMAL) |
151 | ATMEL_HLCDC_PWMPOL, cfg); | 140 | pwmcfg |= ATMEL_HLCDC_PWMPOL; |
152 | } | ||
153 | 141 | ||
154 | static int atmel_hlcdc_pwm_enable(struct pwm_chip *c, struct pwm_device *pwm) | 142 | ret = regmap_update_bits(hlcdc->regmap, ATMEL_HLCDC_CFG(6), |
155 | { | 143 | ATMEL_HLCDC_PWMCVAL_MASK | |
156 | struct atmel_hlcdc_pwm *chip = to_atmel_hlcdc_pwm(c); | 144 | ATMEL_HLCDC_PWMPS_MASK | |
157 | struct atmel_hlcdc *hlcdc = chip->hlcdc; | 145 | ATMEL_HLCDC_PWMPOL, |
158 | u32 status; | 146 | pwmcfg); |
159 | int ret; | 147 | if (ret) |
148 | return ret; | ||
160 | 149 | ||
161 | ret = regmap_write(hlcdc->regmap, ATMEL_HLCDC_EN, ATMEL_HLCDC_PWM); | 150 | ret = regmap_write(hlcdc->regmap, ATMEL_HLCDC_EN, |
162 | if (ret) | 151 | ATMEL_HLCDC_PWM); |
163 | return ret; | 152 | if (ret) |
153 | return ret; | ||
164 | 154 | ||
165 | while (true) { | 155 | ret = regmap_read_poll_timeout(hlcdc->regmap, ATMEL_HLCDC_SR, |
166 | ret = regmap_read(hlcdc->regmap, ATMEL_HLCDC_SR, &status); | 156 | status, |
157 | status & ATMEL_HLCDC_PWM, | ||
158 | 10, 0); | ||
159 | if (ret) | ||
160 | return ret; | ||
161 | } else { | ||
162 | ret = regmap_write(hlcdc->regmap, ATMEL_HLCDC_DIS, | ||
163 | ATMEL_HLCDC_PWM); | ||
167 | if (ret) | 164 | if (ret) |
168 | return ret; | 165 | return ret; |
169 | 166 | ||
170 | if ((status & ATMEL_HLCDC_PWM) != 0) | 167 | ret = regmap_read_poll_timeout(hlcdc->regmap, ATMEL_HLCDC_SR, |
171 | break; | 168 | status, |
169 | !(status & ATMEL_HLCDC_PWM), | ||
170 | 10, 0); | ||
171 | if (ret) | ||
172 | return ret; | ||
172 | 173 | ||
173 | usleep_range(1, 10); | 174 | clk_disable_unprepare(chip->cur_clk); |
175 | chip->cur_clk = NULL; | ||
174 | } | 176 | } |
175 | 177 | ||
176 | return 0; | 178 | return 0; |
177 | } | 179 | } |
178 | 180 | ||
179 | static void atmel_hlcdc_pwm_disable(struct pwm_chip *c, | ||
180 | struct pwm_device *pwm) | ||
181 | { | ||
182 | struct atmel_hlcdc_pwm *chip = to_atmel_hlcdc_pwm(c); | ||
183 | struct atmel_hlcdc *hlcdc = chip->hlcdc; | ||
184 | u32 status; | ||
185 | int ret; | ||
186 | |||
187 | ret = regmap_write(hlcdc->regmap, ATMEL_HLCDC_DIS, ATMEL_HLCDC_PWM); | ||
188 | if (ret) | ||
189 | return; | ||
190 | |||
191 | while (true) { | ||
192 | ret = regmap_read(hlcdc->regmap, ATMEL_HLCDC_SR, &status); | ||
193 | if (ret) | ||
194 | return; | ||
195 | |||
196 | if ((status & ATMEL_HLCDC_PWM) == 0) | ||
197 | break; | ||
198 | |||
199 | usleep_range(1, 10); | ||
200 | } | ||
201 | } | ||
202 | |||
203 | static const struct pwm_ops atmel_hlcdc_pwm_ops = { | 181 | static const struct pwm_ops atmel_hlcdc_pwm_ops = { |
204 | .config = atmel_hlcdc_pwm_config, | 182 | .apply = atmel_hlcdc_pwm_apply, |
205 | .set_polarity = atmel_hlcdc_pwm_set_polarity, | ||
206 | .enable = atmel_hlcdc_pwm_enable, | ||
207 | .disable = atmel_hlcdc_pwm_disable, | ||
208 | .owner = THIS_MODULE, | 183 | .owner = THIS_MODULE, |
209 | }; | 184 | }; |
210 | 185 | ||