Blame view

drivers/pwm/pwm-sti.c 16.4 KB
2874c5fd2   Thomas Gleixner   treewide: Replace...
1
  // SPDX-License-Identifier: GPL-2.0-or-later
378fe115d   Lee Jones   pwm: sti: Add new...
2
  /*
7d8a600c9   Lee Jones   pwm: sti: Take th...
3
4
5
   * PWM device driver for ST SoCs
   *
   * Copyright (C) 2013-2016 STMicroelectronics (R&D) Limited
378fe115d   Lee Jones   pwm: sti: Add new...
6
   *
7d8a600c9   Lee Jones   pwm: sti: Take th...
7
8
   * Author: Ajit Pal Singh <ajitpal.singh@st.com>
   *         Lee Jones <lee.jones@linaro.org>
378fe115d   Lee Jones   pwm: sti: Add new...
9
   */
378fe115d   Lee Jones   pwm: sti: Add new...
10
  #include <linux/clk.h>
3f0925b5a   Lee Jones   pwm: sti: Initial...
11
  #include <linux/interrupt.h>
378fe115d   Lee Jones   pwm: sti: Add new...
12
13
14
15
16
17
18
  #include <linux/math64.h>
  #include <linux/mfd/syscon.h>
  #include <linux/module.h>
  #include <linux/of.h>
  #include <linux/platform_device.h>
  #include <linux/pwm.h>
  #include <linux/regmap.h>
3f0925b5a   Lee Jones   pwm: sti: Initial...
19
  #include <linux/sched.h>
378fe115d   Lee Jones   pwm: sti: Add new...
20
21
  #include <linux/slab.h>
  #include <linux/time.h>
3f0925b5a   Lee Jones   pwm: sti: Initial...
22
  #include <linux/wait.h>
378fe115d   Lee Jones   pwm: sti: Add new...
23

c5f94ae63   Lee Jones   pwm: sti: Reorgan...
24
  #define PWM_OUT_VAL(x)	(0x00 + (4 * (x))) /* Device's Duty Cycle register */
f66d78faf   Lee Jones   pwm: sti: Supply ...
25
26
  #define PWM_CPT_VAL(x)	(0x10 + (4 * (x))) /* Capture value */
  #define PWM_CPT_EDGE(x) (0x30 + (4 * (x))) /* Edge to capture on */
c5f94ae63   Lee Jones   pwm: sti: Reorgan...
27
28
29
  
  #define STI_PWM_CTRL		0x50	/* Control/Config register */
  #define STI_INT_EN		0x54	/* Interrupt Enable/Disable register */
f66d78faf   Lee Jones   pwm: sti: Supply ...
30
  #define STI_INT_STA		0x58	/* Interrupt Status register */
7d8a600c9   Lee Jones   pwm: sti: Take th...
31
32
33
34
35
  #define PWM_INT_ACK		0x5c
  #define PWM_PRESCALE_LOW_MASK	0x0f
  #define PWM_PRESCALE_HIGH_MASK	0xf0
  #define PWM_CPT_EDGE_MASK	0x03
  #define PWM_INT_ACK_MASK	0x1ff
f66d78faf   Lee Jones   pwm: sti: Supply ...
36

7d8a600c9   Lee Jones   pwm: sti: Take th...
37
38
  #define STI_MAX_CPT_DEVS	4
  #define CPT_DC_MAX		0xff
378fe115d   Lee Jones   pwm: sti: Add new...
39
40
41
  
  /* Regfield IDs */
  enum {
c5f94ae63   Lee Jones   pwm: sti: Reorgan...
42
  	/* Bits in PWM_CTRL*/
bf9cc80b6   Ajit Pal Singh   pwm: sti: Fix PWM...
43
44
  	PWMCLK_PRESCALE_LOW,
  	PWMCLK_PRESCALE_HIGH,
f66d78faf   Lee Jones   pwm: sti: Supply ...
45
  	CPTCLK_PRESCALE,
c5f94ae63   Lee Jones   pwm: sti: Reorgan...
46
47
  
  	PWM_OUT_EN,
f66d78faf   Lee Jones   pwm: sti: Supply ...
48
  	PWM_CPT_EN,
c5f94ae63   Lee Jones   pwm: sti: Reorgan...
49
50
  
  	PWM_CPT_INT_EN,
f66d78faf   Lee Jones   pwm: sti: Supply ...
51
  	PWM_CPT_INT_STAT,
378fe115d   Lee Jones   pwm: sti: Add new...
52
53
54
55
  
  	/* Keep last */
  	MAX_REGFIELDS
  };
7d8a600c9   Lee Jones   pwm: sti: Take th...
56
57
58
  /*
   * Each capture input can be programmed to detect rising-edge, falling-edge,
   * either edge or neither egde.
f66d78faf   Lee Jones   pwm: sti: Supply ...
59
60
61
62
63
64
65
   */
  enum sti_cpt_edge {
  	CPT_EDGE_DISABLED,
  	CPT_EDGE_RISING,
  	CPT_EDGE_FALLING,
  	CPT_EDGE_BOTH,
  };
3f0925b5a   Lee Jones   pwm: sti: Initial...
66
67
68
69
70
71
  struct sti_cpt_ddata {
  	u32 snapshot[3];
  	unsigned int index;
  	struct mutex lock;
  	wait_queue_head_t wait;
  };
378fe115d   Lee Jones   pwm: sti: Add new...
72
73
  struct sti_pwm_compat_data {
  	const struct reg_field *reg_fields;
3f0925b5a   Lee Jones   pwm: sti: Initial...
74
75
  	unsigned int pwm_num_devs;
  	unsigned int cpt_num_devs;
378fe115d   Lee Jones   pwm: sti: Add new...
76
77
78
79
80
81
  	unsigned int max_pwm_cnt;
  	unsigned int max_prescale;
  };
  
  struct sti_pwm_chip {
  	struct device *dev;
c5f94ae63   Lee Jones   pwm: sti: Reorgan...
82
  	struct clk *pwm_clk;
d66a928dc   Lee Jones   pwm: sti: Supply ...
83
  	struct clk *cpt_clk;
378fe115d   Lee Jones   pwm: sti: Add new...
84
85
  	struct regmap *regmap;
  	struct sti_pwm_compat_data *cdata;
bf9cc80b6   Ajit Pal Singh   pwm: sti: Fix PWM...
86
87
  	struct regmap_field *prescale_low;
  	struct regmap_field *prescale_high;
c5f94ae63   Lee Jones   pwm: sti: Reorgan...
88
  	struct regmap_field *pwm_out_en;
25eb53809   Lee Jones   pwm: sti: Add sup...
89
  	struct regmap_field *pwm_cpt_en;
c5f94ae63   Lee Jones   pwm: sti: Reorgan...
90
  	struct regmap_field *pwm_cpt_int_en;
25eb53809   Lee Jones   pwm: sti: Add sup...
91
  	struct regmap_field *pwm_cpt_int_stat;
378fe115d   Lee Jones   pwm: sti: Add new...
92
  	struct pwm_chip chip;
5165166e8   Ajit Pal Singh   pwm: sti: Ensure ...
93
  	struct pwm_device *cur;
cd264b6a6   Ajit Pal Singh   pwm: sti: Maintai...
94
  	unsigned long configured;
6ad6b838e   Ajit Pal Singh   pwm: sti: Sync be...
95
96
  	unsigned int en_count;
  	struct mutex sti_pwm_lock; /* To sync between enable/disable calls */
378fe115d   Lee Jones   pwm: sti: Add new...
97
98
99
100
  	void __iomem *mmio;
  };
  
  static const struct reg_field sti_pwm_regfields[MAX_REGFIELDS] = {
7d8a600c9   Lee Jones   pwm: sti: Take th...
101
102
103
104
105
106
107
  	[PWMCLK_PRESCALE_LOW] = REG_FIELD(STI_PWM_CTRL, 0, 3),
  	[PWMCLK_PRESCALE_HIGH] = REG_FIELD(STI_PWM_CTRL, 11, 14),
  	[CPTCLK_PRESCALE] = REG_FIELD(STI_PWM_CTRL, 4, 8),
  	[PWM_OUT_EN] = REG_FIELD(STI_PWM_CTRL, 9, 9),
  	[PWM_CPT_EN] = REG_FIELD(STI_PWM_CTRL, 10, 10),
  	[PWM_CPT_INT_EN] = REG_FIELD(STI_INT_EN, 1, 4),
  	[PWM_CPT_INT_STAT] = REG_FIELD(STI_INT_STA, 1, 4),
378fe115d   Lee Jones   pwm: sti: Add new...
108
109
110
111
112
113
114
115
  };
  
  static inline struct sti_pwm_chip *to_sti_pwmchip(struct pwm_chip *chip)
  {
  	return container_of(chip, struct sti_pwm_chip, chip);
  }
  
  /*
3aacd3e18   Ajit Pal Singh   pwm: sti: Remove ...
116
   * Calculate the prescaler value corresponding to the period.
378fe115d   Lee Jones   pwm: sti: Add new...
117
   */
3aacd3e18   Ajit Pal Singh   pwm: sti: Remove ...
118
119
  static int sti_pwm_get_prescale(struct sti_pwm_chip *pc, unsigned long period,
  				unsigned int *prescale)
378fe115d   Lee Jones   pwm: sti: Add new...
120
121
  {
  	struct sti_pwm_compat_data *cdata = pc->cdata;
d81738b76   Lee Jones   pwm: sti: Only re...
122
  	unsigned long clk_rate;
7d8a600c9   Lee Jones   pwm: sti: Take th...
123
  	unsigned long value;
3aacd3e18   Ajit Pal Singh   pwm: sti: Remove ...
124
  	unsigned int ps;
378fe115d   Lee Jones   pwm: sti: Add new...
125

d81738b76   Lee Jones   pwm: sti: Only re...
126
127
128
129
130
131
  	clk_rate = clk_get_rate(pc->pwm_clk);
  	if (!clk_rate) {
  		dev_err(pc->dev, "failed to get clock rate
  ");
  		return -EINVAL;
  	}
378fe115d   Lee Jones   pwm: sti: Add new...
132
  	/*
7d8a600c9   Lee Jones   pwm: sti: Take th...
133
  	 * prescale = ((period_ns * clk_rate) / (10^9 * (max_pwm_cnt + 1)) - 1
378fe115d   Lee Jones   pwm: sti: Add new...
134
  	 */
7d8a600c9   Lee Jones   pwm: sti: Take th...
135
136
  	value = NSEC_PER_SEC / clk_rate;
  	value *= cdata->max_pwm_cnt + 1;
378fe115d   Lee Jones   pwm: sti: Add new...
137

7d8a600c9   Lee Jones   pwm: sti: Take th...
138
  	if (period % value)
3aacd3e18   Ajit Pal Singh   pwm: sti: Remove ...
139
  		return -EINVAL;
7d8a600c9   Lee Jones   pwm: sti: Take th...
140
141
142
143
  
  	ps  = period / value - 1;
  	if (ps > cdata->max_prescale)
  		return -EINVAL;
3aacd3e18   Ajit Pal Singh   pwm: sti: Remove ...
144
145
146
  	*prescale = ps;
  
  	return 0;
378fe115d   Lee Jones   pwm: sti: Add new...
147
  }
378fe115d   Lee Jones   pwm: sti: Add new...
148
  /*
7d8a600c9   Lee Jones   pwm: sti: Take th...
149
150
151
152
153
154
155
   * For STiH4xx PWM IP, the PWM period is fixed to 256 local clock cycles. The
   * only way to change the period (apart from changing the PWM input clock) is
   * to change the PWM clock prescaler.
   *
   * The prescaler is of 8 bits, so 256 prescaler values and hence 256 possible
   * period values are supported (for a particular clock rate). The requested
   * period will be applied only if it matches one of these 256 values.
378fe115d   Lee Jones   pwm: sti: Add new...
156
157
   */
  static int sti_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm,
7d8a600c9   Lee Jones   pwm: sti: Take th...
158
  			  int duty_ns, int period_ns)
378fe115d   Lee Jones   pwm: sti: Add new...
159
160
161
  {
  	struct sti_pwm_chip *pc = to_sti_pwmchip(chip);
  	struct sti_pwm_compat_data *cdata = pc->cdata;
7d8a600c9   Lee Jones   pwm: sti: Take th...
162
  	unsigned int ncfg, value, prescale = 0;
5165166e8   Ajit Pal Singh   pwm: sti: Ensure ...
163
  	struct pwm_device *cur = pc->cur;
378fe115d   Lee Jones   pwm: sti: Add new...
164
  	struct device *dev = pc->dev;
5165166e8   Ajit Pal Singh   pwm: sti: Ensure ...
165
  	bool period_same = false;
7d8a600c9   Lee Jones   pwm: sti: Take th...
166
  	int ret;
5165166e8   Ajit Pal Singh   pwm: sti: Ensure ...
167

cd264b6a6   Ajit Pal Singh   pwm: sti: Maintai...
168
  	ncfg = hweight_long(pc->configured);
5165166e8   Ajit Pal Singh   pwm: sti: Ensure ...
169
170
  	if (ncfg)
  		period_same = (period_ns == pwm_get_period(cur));
7d8a600c9   Lee Jones   pwm: sti: Take th...
171
172
173
  	/*
  	 * Allow configuration changes if one of the following conditions
  	 * satisfy.
09022e61d   Lee Jones   pwm: sti: Rename ...
174
  	 * 1. No devices have been configured.
7d8a600c9   Lee Jones   pwm: sti: Take th...
175
176
177
178
179
  	 * 2. Only one device has been configured and the new request is for
  	 *    the same device.
  	 * 3. Only one device has been configured and the new request is for
  	 *    a new device and period of the new device is same as the current
  	 *    configured period.
09022e61d   Lee Jones   pwm: sti: Rename ...
180
  	 * 4. More than one devices are configured and period of the new
5165166e8   Ajit Pal Singh   pwm: sti: Ensure ...
181
  	 *    requestis the same as the current period.
378fe115d   Lee Jones   pwm: sti: Add new...
182
  	 */
5165166e8   Ajit Pal Singh   pwm: sti: Ensure ...
183
184
185
186
187
  	if (!ncfg ||
  	    ((ncfg == 1) && (pwm->hwpwm == cur->hwpwm)) ||
  	    ((ncfg == 1) && (pwm->hwpwm != cur->hwpwm) && period_same) ||
  	    ((ncfg > 1) && period_same)) {
  		/* Enable clock before writing to PWM registers. */
c5f94ae63   Lee Jones   pwm: sti: Reorgan...
188
  		ret = clk_enable(pc->pwm_clk);
5165166e8   Ajit Pal Singh   pwm: sti: Ensure ...
189
190
  		if (ret)
  			return ret;
d66a928dc   Lee Jones   pwm: sti: Supply ...
191
192
193
  		ret = clk_enable(pc->cpt_clk);
  		if (ret)
  			return ret;
5165166e8   Ajit Pal Singh   pwm: sti: Ensure ...
194
  		if (!period_same) {
3aacd3e18   Ajit Pal Singh   pwm: sti: Remove ...
195
196
  			ret = sti_pwm_get_prescale(pc, period_ns, &prescale);
  			if (ret)
5165166e8   Ajit Pal Singh   pwm: sti: Ensure ...
197
  				goto clk_dis;
5165166e8   Ajit Pal Singh   pwm: sti: Ensure ...
198

7d8a600c9   Lee Jones   pwm: sti: Take th...
199
200
201
  			value = prescale & PWM_PRESCALE_LOW_MASK;
  
  			ret = regmap_field_write(pc->prescale_low, value);
5165166e8   Ajit Pal Singh   pwm: sti: Ensure ...
202
203
  			if (ret)
  				goto clk_dis;
7d8a600c9   Lee Jones   pwm: sti: Take th...
204
205
206
  			value = (prescale & PWM_PRESCALE_HIGH_MASK) >> 4;
  
  			ret = regmap_field_write(pc->prescale_high, value);
5165166e8   Ajit Pal Singh   pwm: sti: Ensure ...
207
208
209
210
211
212
213
214
215
216
  			if (ret)
  				goto clk_dis;
  		}
  
  		/*
  		 * When PWMVal == 0, PWM pulse = 1 local clock cycle.
  		 * When PWMVal == max_pwm_count,
  		 * PWM pulse = (max_pwm_count + 1) local cycles,
  		 * that is continuous pulse: signal never goes low.
  		 */
7d8a600c9   Lee Jones   pwm: sti: Take th...
217
  		value = cdata->max_pwm_cnt * duty_ns / period_ns;
5165166e8   Ajit Pal Singh   pwm: sti: Ensure ...
218

7d8a600c9   Lee Jones   pwm: sti: Take th...
219
  		ret = regmap_write(pc->regmap, PWM_OUT_VAL(pwm->hwpwm), value);
5165166e8   Ajit Pal Singh   pwm: sti: Ensure ...
220
221
  		if (ret)
  			goto clk_dis;
c5f94ae63   Lee Jones   pwm: sti: Reorgan...
222
  		ret = regmap_field_write(pc->pwm_cpt_int_en, 0);
5165166e8   Ajit Pal Singh   pwm: sti: Ensure ...
223

cd264b6a6   Ajit Pal Singh   pwm: sti: Maintai...
224
  		set_bit(pwm->hwpwm, &pc->configured);
5165166e8   Ajit Pal Singh   pwm: sti: Ensure ...
225
  		pc->cur = pwm;
7d8a600c9   Lee Jones   pwm: sti: Take th...
226
227
228
  		dev_dbg(dev, "prescale:%u, period:%i, duty:%i, value:%u
  ",
  			prescale, period_ns, duty_ns, value);
5165166e8   Ajit Pal Singh   pwm: sti: Ensure ...
229
  	} else {
378fe115d   Lee Jones   pwm: sti: Add new...
230
231
  		return -EINVAL;
  	}
378fe115d   Lee Jones   pwm: sti: Add new...
232
  clk_dis:
c5f94ae63   Lee Jones   pwm: sti: Reorgan...
233
  	clk_disable(pc->pwm_clk);
d66a928dc   Lee Jones   pwm: sti: Supply ...
234
  	clk_disable(pc->cpt_clk);
378fe115d   Lee Jones   pwm: sti: Add new...
235
236
237
238
239
240
241
  	return ret;
  }
  
  static int sti_pwm_enable(struct pwm_chip *chip, struct pwm_device *pwm)
  {
  	struct sti_pwm_chip *pc = to_sti_pwmchip(chip);
  	struct device *dev = pc->dev;
6ad6b838e   Ajit Pal Singh   pwm: sti: Sync be...
242
  	int ret = 0;
378fe115d   Lee Jones   pwm: sti: Add new...
243

6ad6b838e   Ajit Pal Singh   pwm: sti: Sync be...
244
  	/*
7d8a600c9   Lee Jones   pwm: sti: Take th...
245
246
  	 * Since we have a common enable for all PWM devices, do not enable if
  	 * already enabled.
6ad6b838e   Ajit Pal Singh   pwm: sti: Sync be...
247
248
  	 */
  	mutex_lock(&pc->sti_pwm_lock);
7d8a600c9   Lee Jones   pwm: sti: Take th...
249

6ad6b838e   Ajit Pal Singh   pwm: sti: Sync be...
250
  	if (!pc->en_count) {
c5f94ae63   Lee Jones   pwm: sti: Reorgan...
251
  		ret = clk_enable(pc->pwm_clk);
6ad6b838e   Ajit Pal Singh   pwm: sti: Sync be...
252
253
  		if (ret)
  			goto out;
378fe115d   Lee Jones   pwm: sti: Add new...
254

d66a928dc   Lee Jones   pwm: sti: Supply ...
255
256
257
  		ret = clk_enable(pc->cpt_clk);
  		if (ret)
  			goto out;
c5f94ae63   Lee Jones   pwm: sti: Reorgan...
258
  		ret = regmap_field_write(pc->pwm_out_en, 1);
6ad6b838e   Ajit Pal Singh   pwm: sti: Sync be...
259
  		if (ret) {
7d8a600c9   Lee Jones   pwm: sti: Take th...
260
261
262
  			dev_err(dev, "failed to enable PWM device %u: %d
  ",
  				pwm->hwpwm, ret);
6ad6b838e   Ajit Pal Singh   pwm: sti: Sync be...
263
264
265
  			goto out;
  		}
  	}
7d8a600c9   Lee Jones   pwm: sti: Take th...
266

6ad6b838e   Ajit Pal Singh   pwm: sti: Sync be...
267
  	pc->en_count++;
7d8a600c9   Lee Jones   pwm: sti: Take th...
268

6ad6b838e   Ajit Pal Singh   pwm: sti: Sync be...
269
270
  out:
  	mutex_unlock(&pc->sti_pwm_lock);
378fe115d   Lee Jones   pwm: sti: Add new...
271
272
273
274
275
276
  	return ret;
  }
  
  static void sti_pwm_disable(struct pwm_chip *chip, struct pwm_device *pwm)
  {
  	struct sti_pwm_chip *pc = to_sti_pwmchip(chip);
378fe115d   Lee Jones   pwm: sti: Add new...
277

6ad6b838e   Ajit Pal Singh   pwm: sti: Sync be...
278
  	mutex_lock(&pc->sti_pwm_lock);
7d8a600c9   Lee Jones   pwm: sti: Take th...
279

6ad6b838e   Ajit Pal Singh   pwm: sti: Sync be...
280
281
282
283
  	if (--pc->en_count) {
  		mutex_unlock(&pc->sti_pwm_lock);
  		return;
  	}
7d8a600c9   Lee Jones   pwm: sti: Take th...
284

c5f94ae63   Lee Jones   pwm: sti: Reorgan...
285
  	regmap_field_write(pc->pwm_out_en, 0);
378fe115d   Lee Jones   pwm: sti: Add new...
286

c5f94ae63   Lee Jones   pwm: sti: Reorgan...
287
  	clk_disable(pc->pwm_clk);
d66a928dc   Lee Jones   pwm: sti: Supply ...
288
  	clk_disable(pc->cpt_clk);
7d8a600c9   Lee Jones   pwm: sti: Take th...
289

6ad6b838e   Ajit Pal Singh   pwm: sti: Sync be...
290
  	mutex_unlock(&pc->sti_pwm_lock);
378fe115d   Lee Jones   pwm: sti: Add new...
291
  }
cd264b6a6   Ajit Pal Singh   pwm: sti: Maintai...
292
293
294
295
296
297
  static void sti_pwm_free(struct pwm_chip *chip, struct pwm_device *pwm)
  {
  	struct sti_pwm_chip *pc = to_sti_pwmchip(chip);
  
  	clear_bit(pwm->hwpwm, &pc->configured);
  }
c97267ae8   Lee Jones   pwm: sti: Add PWM...
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
  static int sti_pwm_capture(struct pwm_chip *chip, struct pwm_device *pwm,
  			   struct pwm_capture *result, unsigned long timeout)
  {
  	struct sti_pwm_chip *pc = to_sti_pwmchip(chip);
  	struct sti_pwm_compat_data *cdata = pc->cdata;
  	struct sti_cpt_ddata *ddata = pwm_get_chip_data(pwm);
  	struct device *dev = pc->dev;
  	unsigned int effective_ticks;
  	unsigned long long high, low;
  	int ret;
  
  	if (pwm->hwpwm >= cdata->cpt_num_devs) {
  		dev_err(dev, "device %u is not valid
  ", pwm->hwpwm);
  		return -EINVAL;
  	}
  
  	mutex_lock(&ddata->lock);
  	ddata->index = 0;
  
  	/* Prepare capture measurement */
  	regmap_write(pc->regmap, PWM_CPT_EDGE(pwm->hwpwm), CPT_EDGE_RISING);
  	regmap_field_write(pc->pwm_cpt_int_en, BIT(pwm->hwpwm));
  
  	/* Enable capture */
  	ret = regmap_field_write(pc->pwm_cpt_en, 1);
  	if (ret) {
  		dev_err(dev, "failed to enable PWM capture %u: %d
  ",
  			pwm->hwpwm, ret);
  		goto out;
  	}
  
  	ret = wait_event_interruptible_timeout(ddata->wait, ddata->index > 1,
  					       msecs_to_jiffies(timeout));
  
  	regmap_write(pc->regmap, PWM_CPT_EDGE(pwm->hwpwm), CPT_EDGE_DISABLED);
  
  	if (ret == -ERESTARTSYS)
  		goto out;
  
  	switch (ddata->index) {
  	case 0:
  	case 1:
  		/*
  		 * Getting here could mean:
  		 *  - input signal is constant of less than 1 Hz
  		 *  - there is no input signal at all
  		 *
  		 * In such case the frequency is rounded down to 0
  		 */
  		result->period = 0;
  		result->duty_cycle = 0;
  
  		break;
  
  	case 2:
  		/* We have everying we need */
  		high = ddata->snapshot[1] - ddata->snapshot[0];
  		low = ddata->snapshot[2] - ddata->snapshot[1];
  
  		effective_ticks = clk_get_rate(pc->cpt_clk);
  
  		result->period = (high + low) * NSEC_PER_SEC;
fd0cd9728   Greg Kroah-Hartman   Revert "FROMLIST:...
362
  		result->period /= effective_ticks;
c97267ae8   Lee Jones   pwm: sti: Add PWM...
363
364
  
  		result->duty_cycle = high * NSEC_PER_SEC;
fd0cd9728   Greg Kroah-Hartman   Revert "FROMLIST:...
365
  		result->duty_cycle /= effective_ticks;
c97267ae8   Lee Jones   pwm: sti: Add PWM...
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
  
  		break;
  
  	default:
  		dev_err(dev, "internal error
  ");
  		break;
  	}
  
  out:
  	/* Disable capture */
  	regmap_field_write(pc->pwm_cpt_en, 0);
  
  	mutex_unlock(&ddata->lock);
  	return ret;
  }
378fe115d   Lee Jones   pwm: sti: Add new...
382
  static const struct pwm_ops sti_pwm_ops = {
c97267ae8   Lee Jones   pwm: sti: Add PWM...
383
  	.capture = sti_pwm_capture,
378fe115d   Lee Jones   pwm: sti: Add new...
384
385
386
  	.config = sti_pwm_config,
  	.enable = sti_pwm_enable,
  	.disable = sti_pwm_disable,
cd264b6a6   Ajit Pal Singh   pwm: sti: Maintai...
387
  	.free = sti_pwm_free,
378fe115d   Lee Jones   pwm: sti: Add new...
388
389
  	.owner = THIS_MODULE,
  };
25eb53809   Lee Jones   pwm: sti: Add sup...
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
  static irqreturn_t sti_pwm_interrupt(int irq, void *data)
  {
  	struct sti_pwm_chip *pc = data;
  	struct device *dev = pc->dev;
  	struct sti_cpt_ddata *ddata;
  	int devicenum;
  	unsigned int cpt_int_stat;
  	unsigned int reg;
  	int ret = IRQ_NONE;
  
  	ret = regmap_field_read(pc->pwm_cpt_int_stat, &cpt_int_stat);
  	if (ret)
  		return ret;
  
  	while (cpt_int_stat) {
  		devicenum = ffs(cpt_int_stat) - 1;
  
  		ddata = pwm_get_chip_data(&pc->chip.pwms[devicenum]);
  
  		/*
  		 * Capture input:
  		 *    _______                   _______
  		 *   |       |                 |       |
  		 * __|       |_________________|       |________
  		 *   ^0      ^1                ^2
  		 *
7d8a600c9   Lee Jones   pwm: sti: Take th...
416
417
418
  		 * Capture start by the first available rising edge. When a
  		 * capture event occurs, capture value (CPT_VALx) is stored,
  		 * index incremented, capture edge changed.
25eb53809   Lee Jones   pwm: sti: Add sup...
419
  		 *
7d8a600c9   Lee Jones   pwm: sti: Take th...
420
421
422
  		 * After the capture, if the index > 1, we have collected the
  		 * necessary data so we signal the thread waiting for it and
  		 * disable the capture by setting capture edge to none
25eb53809   Lee Jones   pwm: sti: Add sup...
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
  		 */
  
  		regmap_read(pc->regmap,
  			    PWM_CPT_VAL(devicenum),
  			    &ddata->snapshot[ddata->index]);
  
  		switch (ddata->index) {
  		case 0:
  		case 1:
  			regmap_read(pc->regmap, PWM_CPT_EDGE(devicenum), &reg);
  			reg ^= PWM_CPT_EDGE_MASK;
  			regmap_write(pc->regmap, PWM_CPT_EDGE(devicenum), reg);
  
  			ddata->index++;
  			break;
7d8a600c9   Lee Jones   pwm: sti: Take th...
438

25eb53809   Lee Jones   pwm: sti: Add sup...
439
440
441
442
443
444
  		case 2:
  			regmap_write(pc->regmap,
  				     PWM_CPT_EDGE(devicenum),
  				     CPT_EDGE_DISABLED);
  			wake_up(&ddata->wait);
  			break;
7d8a600c9   Lee Jones   pwm: sti: Take th...
445

25eb53809   Lee Jones   pwm: sti: Add sup...
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
  		default:
  			dev_err(dev, "Internal error
  ");
  		}
  
  		cpt_int_stat &= ~BIT_MASK(devicenum);
  
  		ret = IRQ_HANDLED;
  	}
  
  	/* Just ACK everything */
  	regmap_write(pc->regmap, PWM_INT_ACK, PWM_INT_ACK_MASK);
  
  	return ret;
  }
378fe115d   Lee Jones   pwm: sti: Add new...
461
462
463
464
465
466
  static int sti_pwm_probe_dt(struct sti_pwm_chip *pc)
  {
  	struct device *dev = pc->dev;
  	const struct reg_field *reg_fields;
  	struct device_node *np = dev->of_node;
  	struct sti_pwm_compat_data *cdata = pc->cdata;
09022e61d   Lee Jones   pwm: sti: Rename ...
467
  	u32 num_devs;
3f0925b5a   Lee Jones   pwm: sti: Initial...
468
  	int ret;
378fe115d   Lee Jones   pwm: sti: Add new...
469

3f0925b5a   Lee Jones   pwm: sti: Initial...
470
471
472
473
474
475
476
  	ret = of_property_read_u32(np, "st,pwm-num-chan", &num_devs);
  	if (!ret)
  		cdata->pwm_num_devs = num_devs;
  
  	ret = of_property_read_u32(np, "st,capture-num-chan", &num_devs);
  	if (!ret)
  		cdata->cpt_num_devs = num_devs;
378fe115d   Lee Jones   pwm: sti: Add new...
477

85a834c42   Lee Jones   pwm: sti: It's no...
478
479
480
481
482
  	if (!cdata->pwm_num_devs && !cdata->cpt_num_devs) {
  		dev_err(dev, "No channels configured
  ");
  		return -EINVAL;
  	}
378fe115d   Lee Jones   pwm: sti: Add new...
483
  	reg_fields = cdata->reg_fields;
bf9cc80b6   Ajit Pal Singh   pwm: sti: Fix PWM...
484
485
486
487
488
489
490
491
492
  	pc->prescale_low = devm_regmap_field_alloc(dev, pc->regmap,
  					reg_fields[PWMCLK_PRESCALE_LOW]);
  	if (IS_ERR(pc->prescale_low))
  		return PTR_ERR(pc->prescale_low);
  
  	pc->prescale_high = devm_regmap_field_alloc(dev, pc->regmap,
  					reg_fields[PWMCLK_PRESCALE_HIGH]);
  	if (IS_ERR(pc->prescale_high))
  		return PTR_ERR(pc->prescale_high);
378fe115d   Lee Jones   pwm: sti: Add new...
493

378fe115d   Lee Jones   pwm: sti: Add new...
494

c5f94ae63   Lee Jones   pwm: sti: Reorgan...
495
496
497
498
  	pc->pwm_out_en = devm_regmap_field_alloc(dev, pc->regmap,
  						 reg_fields[PWM_OUT_EN]);
  	if (IS_ERR(pc->pwm_out_en))
  		return PTR_ERR(pc->pwm_out_en);
c97267ae8   Lee Jones   pwm: sti: Add PWM...
499
500
501
502
  	pc->pwm_cpt_en = devm_regmap_field_alloc(dev, pc->regmap,
  						 reg_fields[PWM_CPT_EN]);
  	if (IS_ERR(pc->pwm_cpt_en))
  		return PTR_ERR(pc->pwm_cpt_en);
c5f94ae63   Lee Jones   pwm: sti: Reorgan...
503
  	pc->pwm_cpt_int_en = devm_regmap_field_alloc(dev, pc->regmap,
7d8a600c9   Lee Jones   pwm: sti: Take th...
504
  						reg_fields[PWM_CPT_INT_EN]);
c5f94ae63   Lee Jones   pwm: sti: Reorgan...
505
506
  	if (IS_ERR(pc->pwm_cpt_int_en))
  		return PTR_ERR(pc->pwm_cpt_int_en);
378fe115d   Lee Jones   pwm: sti: Add new...
507

25eb53809   Lee Jones   pwm: sti: Add sup...
508
509
510
511
  	pc->pwm_cpt_int_stat = devm_regmap_field_alloc(dev, pc->regmap,
  						reg_fields[PWM_CPT_INT_STAT]);
  	if (PTR_ERR_OR_ZERO(pc->pwm_cpt_int_stat))
  		return PTR_ERR(pc->pwm_cpt_int_stat);
378fe115d   Lee Jones   pwm: sti: Add new...
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
  	return 0;
  }
  
  static const struct regmap_config sti_pwm_regmap_config = {
  	.reg_bits = 32,
  	.val_bits = 32,
  	.reg_stride = 4,
  };
  
  static int sti_pwm_probe(struct platform_device *pdev)
  {
  	struct device *dev = &pdev->dev;
  	struct sti_pwm_compat_data *cdata;
  	struct sti_pwm_chip *pc;
  	struct resource *res;
3f0925b5a   Lee Jones   pwm: sti: Initial...
527
  	unsigned int i;
25eb53809   Lee Jones   pwm: sti: Add sup...
528
  	int irq, ret;
378fe115d   Lee Jones   pwm: sti: Add new...
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
  
  	pc = devm_kzalloc(dev, sizeof(*pc), GFP_KERNEL);
  	if (!pc)
  		return -ENOMEM;
  
  	cdata = devm_kzalloc(dev, sizeof(*cdata), GFP_KERNEL);
  	if (!cdata)
  		return -ENOMEM;
  
  	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
  
  	pc->mmio = devm_ioremap_resource(dev, res);
  	if (IS_ERR(pc->mmio))
  		return PTR_ERR(pc->mmio);
  
  	pc->regmap = devm_regmap_init_mmio(dev, pc->mmio,
  					   &sti_pwm_regmap_config);
  	if (IS_ERR(pc->regmap))
  		return PTR_ERR(pc->regmap);
25eb53809   Lee Jones   pwm: sti: Add sup...
548
  	irq = platform_get_irq(pdev, 0);
fb5a35dbe   Stephen Boyd   pwm: Remove dev_e...
549
  	if (irq < 0)
25eb53809   Lee Jones   pwm: sti: Add sup...
550
  		return irq;
25eb53809   Lee Jones   pwm: sti: Add sup...
551
552
553
554
555
556
557
558
  
  	ret = devm_request_irq(&pdev->dev, irq, sti_pwm_interrupt, 0,
  			       pdev->name, pc);
  	if (ret < 0) {
  		dev_err(&pdev->dev, "Failed to request IRQ
  ");
  		return ret;
  	}
378fe115d   Lee Jones   pwm: sti: Add new...
559
560
561
562
  	/*
  	 * Setup PWM data with default values: some values could be replaced
  	 * with specific ones provided from Device Tree.
  	 */
7d8a600c9   Lee Jones   pwm: sti: Take th...
563
  	cdata->reg_fields = sti_pwm_regfields;
378fe115d   Lee Jones   pwm: sti: Add new...
564
  	cdata->max_prescale = 0xff;
7d8a600c9   Lee Jones   pwm: sti: Take th...
565
  	cdata->max_pwm_cnt = 255;
85a834c42   Lee Jones   pwm: sti: It's no...
566
  	cdata->pwm_num_devs = 0;
3f0925b5a   Lee Jones   pwm: sti: Initial...
567
  	cdata->cpt_num_devs = 0;
378fe115d   Lee Jones   pwm: sti: Add new...
568
569
570
  
  	pc->cdata = cdata;
  	pc->dev = dev;
6ad6b838e   Ajit Pal Singh   pwm: sti: Sync be...
571
572
  	pc->en_count = 0;
  	mutex_init(&pc->sti_pwm_lock);
378fe115d   Lee Jones   pwm: sti: Add new...
573
574
575
576
  
  	ret = sti_pwm_probe_dt(pc);
  	if (ret)
  		return ret;
85a834c42   Lee Jones   pwm: sti: It's no...
577
578
  	if (!cdata->pwm_num_devs)
  		goto skip_pwm;
c5f94ae63   Lee Jones   pwm: sti: Reorgan...
579
580
  	pc->pwm_clk = of_clk_get_by_name(dev->of_node, "pwm");
  	if (IS_ERR(pc->pwm_clk)) {
378fe115d   Lee Jones   pwm: sti: Add new...
581
582
  		dev_err(dev, "failed to get PWM clock
  ");
c5f94ae63   Lee Jones   pwm: sti: Reorgan...
583
  		return PTR_ERR(pc->pwm_clk);
378fe115d   Lee Jones   pwm: sti: Add new...
584
  	}
c5f94ae63   Lee Jones   pwm: sti: Reorgan...
585
  	ret = clk_prepare(pc->pwm_clk);
378fe115d   Lee Jones   pwm: sti: Add new...
586
587
588
589
590
  	if (ret) {
  		dev_err(dev, "failed to prepare clock
  ");
  		return ret;
  	}
85a834c42   Lee Jones   pwm: sti: It's no...
591
592
593
  skip_pwm:
  	if (!cdata->cpt_num_devs)
  		goto skip_cpt;
d66a928dc   Lee Jones   pwm: sti: Supply ...
594
595
596
597
598
599
600
601
602
603
604
605
606
  	pc->cpt_clk = of_clk_get_by_name(dev->of_node, "capture");
  	if (IS_ERR(pc->cpt_clk)) {
  		dev_err(dev, "failed to get PWM capture clock
  ");
  		return PTR_ERR(pc->cpt_clk);
  	}
  
  	ret = clk_prepare(pc->cpt_clk);
  	if (ret) {
  		dev_err(dev, "failed to prepare clock
  ");
  		return ret;
  	}
85a834c42   Lee Jones   pwm: sti: It's no...
607
  skip_cpt:
378fe115d   Lee Jones   pwm: sti: Add new...
608
609
610
  	pc->chip.dev = dev;
  	pc->chip.ops = &sti_pwm_ops;
  	pc->chip.base = -1;
3f0925b5a   Lee Jones   pwm: sti: Initial...
611
  	pc->chip.npwm = pc->cdata->pwm_num_devs;
378fe115d   Lee Jones   pwm: sti: Add new...
612
613
614
  
  	ret = pwmchip_add(&pc->chip);
  	if (ret < 0) {
c5f94ae63   Lee Jones   pwm: sti: Reorgan...
615
  		clk_unprepare(pc->pwm_clk);
d66a928dc   Lee Jones   pwm: sti: Supply ...
616
  		clk_unprepare(pc->cpt_clk);
378fe115d   Lee Jones   pwm: sti: Add new...
617
618
  		return ret;
  	}
3f0925b5a   Lee Jones   pwm: sti: Initial...
619
620
621
622
623
624
625
626
627
628
629
630
  	for (i = 0; i < cdata->cpt_num_devs; i++) {
  		struct sti_cpt_ddata *ddata;
  
  		ddata = devm_kzalloc(dev, sizeof(*ddata), GFP_KERNEL);
  		if (!ddata)
  			return -ENOMEM;
  
  		init_waitqueue_head(&ddata->wait);
  		mutex_init(&ddata->lock);
  
  		pwm_set_chip_data(&pc->chip.pwms[i], ddata);
  	}
378fe115d   Lee Jones   pwm: sti: Add new...
631
632
633
634
635
636
637
638
639
  	platform_set_drvdata(pdev, pc);
  
  	return 0;
  }
  
  static int sti_pwm_remove(struct platform_device *pdev)
  {
  	struct sti_pwm_chip *pc = platform_get_drvdata(pdev);
  	unsigned int i;
3f0925b5a   Lee Jones   pwm: sti: Initial...
640
  	for (i = 0; i < pc->cdata->pwm_num_devs; i++)
378fe115d   Lee Jones   pwm: sti: Add new...
641
  		pwm_disable(&pc->chip.pwms[i]);
c5f94ae63   Lee Jones   pwm: sti: Reorgan...
642
  	clk_unprepare(pc->pwm_clk);
d66a928dc   Lee Jones   pwm: sti: Supply ...
643
  	clk_unprepare(pc->cpt_clk);
378fe115d   Lee Jones   pwm: sti: Add new...
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
  
  	return pwmchip_remove(&pc->chip);
  }
  
  static const struct of_device_id sti_pwm_of_match[] = {
  	{ .compatible = "st,sti-pwm", },
  	{ /* sentinel */ }
  };
  MODULE_DEVICE_TABLE(of, sti_pwm_of_match);
  
  static struct platform_driver sti_pwm_driver = {
  	.driver = {
  		.name = "sti-pwm",
  		.of_match_table = sti_pwm_of_match,
  	},
  	.probe = sti_pwm_probe,
  	.remove = sti_pwm_remove,
  };
  module_platform_driver(sti_pwm_driver);
  
  MODULE_AUTHOR("Ajit Pal Singh <ajitpal.singh@st.com>");
  MODULE_DESCRIPTION("STMicroelectronics ST PWM driver");
  MODULE_LICENSE("GPL");