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