Commit | Line | Data |
---|---|---|
378fe115 LJ |
1 | /* |
2 | * PWM device driver for ST SoCs. | |
3 | * Author: Ajit Pal Singh <ajitpal.singh@st.com> | |
4 | * | |
5 | * Copyright (C) 2013-2014 STMicroelectronics (R&D) Limited | |
6 | * | |
7 | * This program is free software; you can redistribute it and/or modify | |
8 | * it under the terms of the GNU General Public License as published by | |
9 | * the Free Software Foundation; either version 2 of the License, or | |
10 | * (at your option) any later version. | |
11 | */ | |
12 | ||
378fe115 LJ |
13 | #include <linux/clk.h> |
14 | #include <linux/math64.h> | |
15 | #include <linux/mfd/syscon.h> | |
16 | #include <linux/module.h> | |
17 | #include <linux/of.h> | |
18 | #include <linux/platform_device.h> | |
19 | #include <linux/pwm.h> | |
20 | #include <linux/regmap.h> | |
21 | #include <linux/slab.h> | |
22 | #include <linux/time.h> | |
23 | ||
24 | #define STI_DS_REG(ch) (4 * (ch)) /* Channel's Duty Cycle register */ | |
25 | #define STI_PWMCR 0x50 /* Control/Config register */ | |
26 | #define STI_INTEN 0x54 /* Interrupt Enable/Disable register */ | |
bf9cc80b APS |
27 | #define PWM_PRESCALE_LOW_MASK 0x0f |
28 | #define PWM_PRESCALE_HIGH_MASK 0xf0 | |
378fe115 LJ |
29 | |
30 | /* Regfield IDs */ | |
31 | enum { | |
bf9cc80b APS |
32 | PWMCLK_PRESCALE_LOW, |
33 | PWMCLK_PRESCALE_HIGH, | |
378fe115 LJ |
34 | PWM_EN, |
35 | PWM_INT_EN, | |
36 | ||
37 | /* Keep last */ | |
38 | MAX_REGFIELDS | |
39 | }; | |
40 | ||
41 | struct sti_pwm_compat_data { | |
42 | const struct reg_field *reg_fields; | |
43 | unsigned int num_chan; | |
44 | unsigned int max_pwm_cnt; | |
45 | unsigned int max_prescale; | |
46 | }; | |
47 | ||
48 | struct sti_pwm_chip { | |
49 | struct device *dev; | |
50 | struct clk *clk; | |
51 | unsigned long clk_rate; | |
52 | struct regmap *regmap; | |
53 | struct sti_pwm_compat_data *cdata; | |
bf9cc80b APS |
54 | struct regmap_field *prescale_low; |
55 | struct regmap_field *prescale_high; | |
378fe115 LJ |
56 | struct regmap_field *pwm_en; |
57 | struct regmap_field *pwm_int_en; | |
378fe115 | 58 | struct pwm_chip chip; |
5165166e | 59 | struct pwm_device *cur; |
6ad6b838 APS |
60 | unsigned int en_count; |
61 | struct mutex sti_pwm_lock; /* To sync between enable/disable calls */ | |
378fe115 LJ |
62 | void __iomem *mmio; |
63 | }; | |
64 | ||
65 | static const struct reg_field sti_pwm_regfields[MAX_REGFIELDS] = { | |
bf9cc80b APS |
66 | [PWMCLK_PRESCALE_LOW] = REG_FIELD(STI_PWMCR, 0, 3), |
67 | [PWMCLK_PRESCALE_HIGH] = REG_FIELD(STI_PWMCR, 11, 14), | |
378fe115 LJ |
68 | [PWM_EN] = REG_FIELD(STI_PWMCR, 9, 9), |
69 | [PWM_INT_EN] = REG_FIELD(STI_INTEN, 0, 0), | |
70 | }; | |
71 | ||
72 | static inline struct sti_pwm_chip *to_sti_pwmchip(struct pwm_chip *chip) | |
73 | { | |
74 | return container_of(chip, struct sti_pwm_chip, chip); | |
75 | } | |
76 | ||
77 | /* | |
3aacd3e1 | 78 | * Calculate the prescaler value corresponding to the period. |
378fe115 | 79 | */ |
3aacd3e1 APS |
80 | static int sti_pwm_get_prescale(struct sti_pwm_chip *pc, unsigned long period, |
81 | unsigned int *prescale) | |
378fe115 LJ |
82 | { |
83 | struct sti_pwm_compat_data *cdata = pc->cdata; | |
378fe115 | 84 | unsigned long val; |
3aacd3e1 | 85 | unsigned int ps; |
378fe115 LJ |
86 | |
87 | /* | |
3aacd3e1 | 88 | * prescale = ((period_ns * clk_rate) / (10^9 * (max_pwm_count + 1)) - 1 |
378fe115 LJ |
89 | */ |
90 | val = NSEC_PER_SEC / pc->clk_rate; | |
91 | val *= cdata->max_pwm_cnt + 1; | |
92 | ||
3aacd3e1 APS |
93 | if (period % val) { |
94 | return -EINVAL; | |
95 | } else { | |
96 | ps = period / val - 1; | |
97 | if (ps > cdata->max_prescale) | |
98 | return -EINVAL; | |
378fe115 | 99 | } |
3aacd3e1 APS |
100 | *prescale = ps; |
101 | ||
102 | return 0; | |
378fe115 LJ |
103 | } |
104 | ||
5165166e APS |
105 | /* Calculate the number of PWM devices configured with a period. */ |
106 | static unsigned int sti_pwm_count_configured(struct pwm_chip *chip) | |
107 | { | |
108 | struct pwm_device *pwm; | |
109 | unsigned int ncfg = 0; | |
110 | unsigned int i; | |
111 | ||
112 | for (i = 0; i < chip->npwm; i++) { | |
113 | pwm = &chip->pwms[i]; | |
114 | if (test_bit(PWMF_REQUESTED, &pwm->flags)) { | |
115 | if (pwm_get_period(pwm)) | |
116 | ncfg++; | |
117 | } | |
118 | } | |
119 | ||
120 | return ncfg; | |
121 | } | |
122 | ||
378fe115 LJ |
123 | /* |
124 | * For STiH4xx PWM IP, the PWM period is fixed to 256 local clock cycles. | |
125 | * The only way to change the period (apart from changing the PWM input clock) | |
126 | * is to change the PWM clock prescaler. | |
bf9cc80b APS |
127 | * The prescaler is of 8 bits, so 256 prescaler values and hence |
128 | * 256 possible period values are supported (for a particular clock rate). | |
378fe115 | 129 | * The requested period will be applied only if it matches one of these |
bf9cc80b | 130 | * 256 values. |
378fe115 LJ |
131 | */ |
132 | static int sti_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm, | |
133 | int duty_ns, int period_ns) | |
134 | { | |
135 | struct sti_pwm_chip *pc = to_sti_pwmchip(chip); | |
136 | struct sti_pwm_compat_data *cdata = pc->cdata; | |
5165166e | 137 | struct pwm_device *cur = pc->cur; |
378fe115 | 138 | struct device *dev = pc->dev; |
5165166e | 139 | unsigned int prescale = 0, pwmvalx; |
378fe115 | 140 | int ret; |
5165166e APS |
141 | unsigned int ncfg; |
142 | bool period_same = false; | |
143 | ||
144 | ncfg = sti_pwm_count_configured(chip); | |
145 | if (ncfg) | |
146 | period_same = (period_ns == pwm_get_period(cur)); | |
147 | ||
148 | /* Allow configuration changes if one of the | |
149 | * following conditions satisfy. | |
150 | * 1. No channels have been configured. | |
151 | * 2. Only one channel has been configured and the new request | |
152 | * is for the same channel. | |
153 | * 3. Only one channel has been configured and the new request is | |
154 | * for a new channel and period of the new channel is same as | |
155 | * the current configured period. | |
156 | * 4. More than one channels are configured and period of the new | |
157 | * requestis the same as the current period. | |
378fe115 | 158 | */ |
5165166e APS |
159 | if (!ncfg || |
160 | ((ncfg == 1) && (pwm->hwpwm == cur->hwpwm)) || | |
161 | ((ncfg == 1) && (pwm->hwpwm != cur->hwpwm) && period_same) || | |
162 | ((ncfg > 1) && period_same)) { | |
163 | /* Enable clock before writing to PWM registers. */ | |
164 | ret = clk_enable(pc->clk); | |
165 | if (ret) | |
166 | return ret; | |
167 | ||
168 | if (!period_same) { | |
3aacd3e1 APS |
169 | ret = sti_pwm_get_prescale(pc, period_ns, &prescale); |
170 | if (ret) | |
5165166e | 171 | goto clk_dis; |
5165166e APS |
172 | |
173 | ret = | |
174 | regmap_field_write(pc->prescale_low, | |
175 | prescale & PWM_PRESCALE_LOW_MASK); | |
176 | if (ret) | |
177 | goto clk_dis; | |
178 | ||
179 | ret = | |
180 | regmap_field_write(pc->prescale_high, | |
181 | (prescale & PWM_PRESCALE_HIGH_MASK) >> 4); | |
182 | if (ret) | |
183 | goto clk_dis; | |
184 | } | |
185 | ||
186 | /* | |
187 | * When PWMVal == 0, PWM pulse = 1 local clock cycle. | |
188 | * When PWMVal == max_pwm_count, | |
189 | * PWM pulse = (max_pwm_count + 1) local cycles, | |
190 | * that is continuous pulse: signal never goes low. | |
191 | */ | |
192 | pwmvalx = cdata->max_pwm_cnt * duty_ns / period_ns; | |
193 | ||
194 | ret = regmap_write(pc->regmap, STI_DS_REG(pwm->hwpwm), pwmvalx); | |
195 | if (ret) | |
196 | goto clk_dis; | |
197 | ||
198 | ret = regmap_field_write(pc->pwm_int_en, 0); | |
199 | ||
200 | pc->cur = pwm; | |
201 | ||
202 | dev_dbg(dev, "prescale:%u, period:%i, duty:%i, pwmvalx:%u\n", | |
203 | prescale, period_ns, duty_ns, pwmvalx); | |
204 | } else { | |
378fe115 LJ |
205 | return -EINVAL; |
206 | } | |
207 | ||
378fe115 LJ |
208 | clk_dis: |
209 | clk_disable(pc->clk); | |
210 | return ret; | |
211 | } | |
212 | ||
213 | static int sti_pwm_enable(struct pwm_chip *chip, struct pwm_device *pwm) | |
214 | { | |
215 | struct sti_pwm_chip *pc = to_sti_pwmchip(chip); | |
216 | struct device *dev = pc->dev; | |
6ad6b838 | 217 | int ret = 0; |
378fe115 | 218 | |
6ad6b838 APS |
219 | /* |
220 | * Since we have a common enable for all PWM channels, | |
221 | * do not enable if already enabled. | |
222 | */ | |
223 | mutex_lock(&pc->sti_pwm_lock); | |
224 | if (!pc->en_count) { | |
225 | ret = clk_enable(pc->clk); | |
226 | if (ret) | |
227 | goto out; | |
378fe115 | 228 | |
6ad6b838 APS |
229 | ret = regmap_field_write(pc->pwm_en, 1); |
230 | if (ret) { | |
231 | dev_err(dev, "failed to enable PWM device:%d\n", | |
232 | pwm->hwpwm); | |
233 | goto out; | |
234 | } | |
235 | } | |
236 | pc->en_count++; | |
237 | out: | |
238 | mutex_unlock(&pc->sti_pwm_lock); | |
378fe115 LJ |
239 | return ret; |
240 | } | |
241 | ||
242 | static void sti_pwm_disable(struct pwm_chip *chip, struct pwm_device *pwm) | |
243 | { | |
244 | struct sti_pwm_chip *pc = to_sti_pwmchip(chip); | |
378fe115 | 245 | |
6ad6b838 APS |
246 | mutex_lock(&pc->sti_pwm_lock); |
247 | if (--pc->en_count) { | |
248 | mutex_unlock(&pc->sti_pwm_lock); | |
249 | return; | |
250 | } | |
378fe115 LJ |
251 | regmap_field_write(pc->pwm_en, 0); |
252 | ||
378fe115 | 253 | clk_disable(pc->clk); |
6ad6b838 | 254 | mutex_unlock(&pc->sti_pwm_lock); |
378fe115 LJ |
255 | } |
256 | ||
257 | static const struct pwm_ops sti_pwm_ops = { | |
258 | .config = sti_pwm_config, | |
259 | .enable = sti_pwm_enable, | |
260 | .disable = sti_pwm_disable, | |
261 | .owner = THIS_MODULE, | |
262 | }; | |
263 | ||
264 | static int sti_pwm_probe_dt(struct sti_pwm_chip *pc) | |
265 | { | |
266 | struct device *dev = pc->dev; | |
267 | const struct reg_field *reg_fields; | |
268 | struct device_node *np = dev->of_node; | |
269 | struct sti_pwm_compat_data *cdata = pc->cdata; | |
270 | u32 num_chan; | |
271 | ||
272 | of_property_read_u32(np, "st,pwm-num-chan", &num_chan); | |
273 | if (num_chan) | |
274 | cdata->num_chan = num_chan; | |
275 | ||
276 | reg_fields = cdata->reg_fields; | |
277 | ||
bf9cc80b APS |
278 | pc->prescale_low = devm_regmap_field_alloc(dev, pc->regmap, |
279 | reg_fields[PWMCLK_PRESCALE_LOW]); | |
280 | if (IS_ERR(pc->prescale_low)) | |
281 | return PTR_ERR(pc->prescale_low); | |
282 | ||
283 | pc->prescale_high = devm_regmap_field_alloc(dev, pc->regmap, | |
284 | reg_fields[PWMCLK_PRESCALE_HIGH]); | |
285 | if (IS_ERR(pc->prescale_high)) | |
286 | return PTR_ERR(pc->prescale_high); | |
378fe115 LJ |
287 | |
288 | pc->pwm_en = devm_regmap_field_alloc(dev, pc->regmap, | |
289 | reg_fields[PWM_EN]); | |
290 | if (IS_ERR(pc->pwm_en)) | |
291 | return PTR_ERR(pc->pwm_en); | |
292 | ||
293 | pc->pwm_int_en = devm_regmap_field_alloc(dev, pc->regmap, | |
294 | reg_fields[PWM_INT_EN]); | |
295 | if (IS_ERR(pc->pwm_int_en)) | |
296 | return PTR_ERR(pc->pwm_int_en); | |
297 | ||
298 | return 0; | |
299 | } | |
300 | ||
301 | static const struct regmap_config sti_pwm_regmap_config = { | |
302 | .reg_bits = 32, | |
303 | .val_bits = 32, | |
304 | .reg_stride = 4, | |
305 | }; | |
306 | ||
307 | static int sti_pwm_probe(struct platform_device *pdev) | |
308 | { | |
309 | struct device *dev = &pdev->dev; | |
310 | struct sti_pwm_compat_data *cdata; | |
311 | struct sti_pwm_chip *pc; | |
312 | struct resource *res; | |
313 | int ret; | |
314 | ||
315 | pc = devm_kzalloc(dev, sizeof(*pc), GFP_KERNEL); | |
316 | if (!pc) | |
317 | return -ENOMEM; | |
318 | ||
319 | cdata = devm_kzalloc(dev, sizeof(*cdata), GFP_KERNEL); | |
320 | if (!cdata) | |
321 | return -ENOMEM; | |
322 | ||
323 | res = platform_get_resource(pdev, IORESOURCE_MEM, 0); | |
324 | ||
325 | pc->mmio = devm_ioremap_resource(dev, res); | |
326 | if (IS_ERR(pc->mmio)) | |
327 | return PTR_ERR(pc->mmio); | |
328 | ||
329 | pc->regmap = devm_regmap_init_mmio(dev, pc->mmio, | |
330 | &sti_pwm_regmap_config); | |
331 | if (IS_ERR(pc->regmap)) | |
332 | return PTR_ERR(pc->regmap); | |
333 | ||
334 | /* | |
335 | * Setup PWM data with default values: some values could be replaced | |
336 | * with specific ones provided from Device Tree. | |
337 | */ | |
338 | cdata->reg_fields = &sti_pwm_regfields[0]; | |
339 | cdata->max_prescale = 0xff; | |
340 | cdata->max_pwm_cnt = 255; | |
341 | cdata->num_chan = 1; | |
342 | ||
343 | pc->cdata = cdata; | |
344 | pc->dev = dev; | |
6ad6b838 APS |
345 | pc->en_count = 0; |
346 | mutex_init(&pc->sti_pwm_lock); | |
378fe115 LJ |
347 | |
348 | ret = sti_pwm_probe_dt(pc); | |
349 | if (ret) | |
350 | return ret; | |
351 | ||
378fe115 LJ |
352 | pc->clk = of_clk_get_by_name(dev->of_node, "pwm"); |
353 | if (IS_ERR(pc->clk)) { | |
354 | dev_err(dev, "failed to get PWM clock\n"); | |
355 | return PTR_ERR(pc->clk); | |
356 | } | |
357 | ||
358 | pc->clk_rate = clk_get_rate(pc->clk); | |
359 | if (!pc->clk_rate) { | |
360 | dev_err(dev, "failed to get clock rate\n"); | |
361 | return -EINVAL; | |
362 | } | |
363 | ||
364 | ret = clk_prepare(pc->clk); | |
365 | if (ret) { | |
366 | dev_err(dev, "failed to prepare clock\n"); | |
367 | return ret; | |
368 | } | |
369 | ||
378fe115 LJ |
370 | pc->chip.dev = dev; |
371 | pc->chip.ops = &sti_pwm_ops; | |
372 | pc->chip.base = -1; | |
373 | pc->chip.npwm = pc->cdata->num_chan; | |
374 | pc->chip.can_sleep = true; | |
375 | ||
376 | ret = pwmchip_add(&pc->chip); | |
377 | if (ret < 0) { | |
378 | clk_unprepare(pc->clk); | |
379 | return ret; | |
380 | } | |
381 | ||
382 | platform_set_drvdata(pdev, pc); | |
383 | ||
384 | return 0; | |
385 | } | |
386 | ||
387 | static int sti_pwm_remove(struct platform_device *pdev) | |
388 | { | |
389 | struct sti_pwm_chip *pc = platform_get_drvdata(pdev); | |
390 | unsigned int i; | |
391 | ||
392 | for (i = 0; i < pc->cdata->num_chan; i++) | |
393 | pwm_disable(&pc->chip.pwms[i]); | |
394 | ||
395 | clk_unprepare(pc->clk); | |
396 | ||
397 | return pwmchip_remove(&pc->chip); | |
398 | } | |
399 | ||
400 | static const struct of_device_id sti_pwm_of_match[] = { | |
401 | { .compatible = "st,sti-pwm", }, | |
402 | { /* sentinel */ } | |
403 | }; | |
404 | MODULE_DEVICE_TABLE(of, sti_pwm_of_match); | |
405 | ||
406 | static struct platform_driver sti_pwm_driver = { | |
407 | .driver = { | |
408 | .name = "sti-pwm", | |
409 | .of_match_table = sti_pwm_of_match, | |
410 | }, | |
411 | .probe = sti_pwm_probe, | |
412 | .remove = sti_pwm_remove, | |
413 | }; | |
414 | module_platform_driver(sti_pwm_driver); | |
415 | ||
416 | MODULE_AUTHOR("Ajit Pal Singh <ajitpal.singh@st.com>"); | |
417 | MODULE_DESCRIPTION("STMicroelectronics ST PWM driver"); | |
418 | MODULE_LICENSE("GPL"); |