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