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