Blame view

drivers/pwm/pwm-meson.c 15.5 KB
1cdb44135   Neil Armstrong   pwm: meson: Updat...
1
  // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
211ed6307   Neil Armstrong   pwm: Add support ...
2
  /*
4ae42ce79   Martin Blumenstingl   pwm: meson: Add d...
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
   * PWM controller driver for Amlogic Meson SoCs.
   *
   * This PWM is only a set of Gates, Dividers and Counters:
   * PWM output is achieved by calculating a clock that permits calculating
   * two periods (low and high). The counter then has to be set to switch after
   * N cycles for the first half period.
   * The hardware has no "polarity" setting. This driver reverses the period
   * cycles (the low length is inverted with the high length) for
   * PWM_POLARITY_INVERSED. This means that .get_state cannot read the polarity
   * from the hardware.
   * Setting the duty cycle will disable and re-enable the PWM output.
   * Disabling the PWM stops the output immediately (without waiting for the
   * current period to complete first).
   *
   * The public S912 (GXM) datasheet contains some documentation for this PWM
   * controller starting on page 543:
   * https://dl.khadas.com/Hardware/VIM2/Datasheet/S912_Datasheet_V0.220170314publicversion-Wesion.pdf
   * An updated version of this IP block is found in S922X (G12B) SoCs. The
   * datasheet contains the description for this IP block revision starting at
   * page 1084:
   * https://dn.odroid.com/S922X/ODROID-N2/Datasheet/S922X_Public_Datasheet_V0.2.pdf
   *
211ed6307   Neil Armstrong   pwm: Add support ...
25
26
27
   * Copyright (c) 2016 BayLibre, SAS.
   * Author: Neil Armstrong <narmstrong@baylibre.com>
   * Copyright (C) 2014 Amlogic, Inc.
211ed6307   Neil Armstrong   pwm: Add support ...
28
   */
181164b66   Martin Blumenstingl   pwm: meson: Use G...
29
30
  #include <linux/bitfield.h>
  #include <linux/bits.h>
211ed6307   Neil Armstrong   pwm: Add support ...
31
32
33
34
35
  #include <linux/clk.h>
  #include <linux/clk-provider.h>
  #include <linux/err.h>
  #include <linux/io.h>
  #include <linux/kernel.h>
fb2081e87   Martin Blumenstingl   pwm: meson: Simpl...
36
  #include <linux/math64.h>
211ed6307   Neil Armstrong   pwm: Add support ...
37
38
39
40
41
42
43
44
45
46
  #include <linux/module.h>
  #include <linux/of.h>
  #include <linux/of_device.h>
  #include <linux/platform_device.h>
  #include <linux/pwm.h>
  #include <linux/slab.h>
  #include <linux/spinlock.h>
  
  #define REG_PWM_A		0x0
  #define REG_PWM_B		0x4
181164b66   Martin Blumenstingl   pwm: meson: Use G...
47
48
  #define PWM_LOW_MASK		GENMASK(15, 0)
  #define PWM_HIGH_MASK		GENMASK(31, 16)
211ed6307   Neil Armstrong   pwm: Add support ...
49
50
51
52
53
54
55
56
57
  
  #define REG_MISC_AB		0x8
  #define MISC_B_CLK_EN		BIT(23)
  #define MISC_A_CLK_EN		BIT(15)
  #define MISC_CLK_DIV_MASK	0x7f
  #define MISC_B_CLK_DIV_SHIFT	16
  #define MISC_A_CLK_DIV_SHIFT	8
  #define MISC_B_CLK_SEL_SHIFT	6
  #define MISC_A_CLK_SEL_SHIFT	4
33cefd84d   Martin Blumenstingl   pwm: meson: Chang...
58
  #define MISC_CLK_SEL_MASK	0x3
211ed6307   Neil Armstrong   pwm: Add support ...
59
60
  #define MISC_B_EN		BIT(1)
  #define MISC_A_EN		BIT(0)
a50a49a45   Martin Blumenstingl   pwm: meson: Add t...
61
  #define MESON_NUM_PWMS		2
8bbf31645   Martin Blumenstingl   pwm: meson: Add t...
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
  static struct meson_pwm_channel_data {
  	u8		reg_offset;
  	u8		clk_sel_shift;
  	u8		clk_div_shift;
  	u32		clk_en_mask;
  	u32		pwm_en_mask;
  } meson_pwm_per_channel_data[MESON_NUM_PWMS] = {
  	{
  		.reg_offset	= REG_PWM_A,
  		.clk_sel_shift	= MISC_A_CLK_SEL_SHIFT,
  		.clk_div_shift	= MISC_A_CLK_DIV_SHIFT,
  		.clk_en_mask	= MISC_A_CLK_EN,
  		.pwm_en_mask	= MISC_A_EN,
  	},
  	{
  		.reg_offset	= REG_PWM_B,
  		.clk_sel_shift	= MISC_B_CLK_SEL_SHIFT,
  		.clk_div_shift	= MISC_B_CLK_DIV_SHIFT,
  		.clk_en_mask	= MISC_B_CLK_EN,
  		.pwm_en_mask	= MISC_B_EN,
  	}
211ed6307   Neil Armstrong   pwm: Add support ...
83
84
85
86
87
88
  };
  
  struct meson_pwm_channel {
  	unsigned int hi;
  	unsigned int lo;
  	u8 pre_div;
211ed6307   Neil Armstrong   pwm: Add support ...
89
90
91
92
93
94
95
  	struct clk *clk_parent;
  	struct clk_mux mux;
  	struct clk *clk;
  };
  
  struct meson_pwm_data {
  	const char * const *parent_names;
d396b20a1   Jerome Brunet   pwm: meson: Add c...
96
  	unsigned int num_parents;
211ed6307   Neil Armstrong   pwm: Add support ...
97
98
99
100
101
  };
  
  struct meson_pwm {
  	struct pwm_chip chip;
  	const struct meson_pwm_data *data;
a50a49a45   Martin Blumenstingl   pwm: meson: Add t...
102
  	struct meson_pwm_channel channels[MESON_NUM_PWMS];
211ed6307   Neil Armstrong   pwm: Add support ...
103
  	void __iomem *base;
f173747ff   Martin Blumenstingl   pwm: meson: Use t...
104
105
106
107
  	/*
  	 * Protects register (write) access to the REG_MISC_AB register
  	 * that is shared between the two PWMs.
  	 */
211ed6307   Neil Armstrong   pwm: Add support ...
108
109
110
111
112
113
114
115
116
117
  	spinlock_t lock;
  };
  
  static inline struct meson_pwm *to_meson_pwm(struct pwm_chip *chip)
  {
  	return container_of(chip, struct meson_pwm, chip);
  }
  
  static int meson_pwm_request(struct pwm_chip *chip, struct pwm_device *pwm)
  {
1064c6bac   Martin Blumenstingl   pwm: meson: Move ...
118
119
  	struct meson_pwm *meson = to_meson_pwm(chip);
  	struct meson_pwm_channel *channel;
211ed6307   Neil Armstrong   pwm: Add support ...
120
121
  	struct device *dev = chip->dev;
  	int err;
1064c6bac   Martin Blumenstingl   pwm: meson: Move ...
122
123
124
125
126
  	channel = pwm_get_chip_data(pwm);
  	if (channel)
  		return 0;
  
  	channel = &meson->channels[pwm->hwpwm];
211ed6307   Neil Armstrong   pwm: Add support ...
127
128
129
130
131
132
133
134
  
  	if (channel->clk_parent) {
  		err = clk_set_parent(channel->clk, channel->clk_parent);
  		if (err < 0) {
  			dev_err(dev, "failed to set parent %s for %s: %d
  ",
  				__clk_get_name(channel->clk_parent),
  				__clk_get_name(channel->clk), err);
b33d232e6   Krzysztof Kozlowski   pwm: meson: Fix c...
135
  			return err;
211ed6307   Neil Armstrong   pwm: Add support ...
136
137
138
139
140
141
142
143
144
145
  		}
  	}
  
  	err = clk_prepare_enable(channel->clk);
  	if (err < 0) {
  		dev_err(dev, "failed to enable clock %s: %d
  ",
  			__clk_get_name(channel->clk), err);
  		return err;
  	}
1064c6bac   Martin Blumenstingl   pwm: meson: Move ...
146
  	return pwm_set_chip_data(pwm, channel);
211ed6307   Neil Armstrong   pwm: Add support ...
147
148
149
150
151
152
153
154
155
  }
  
  static void meson_pwm_free(struct pwm_chip *chip, struct pwm_device *pwm)
  {
  	struct meson_pwm_channel *channel = pwm_get_chip_data(pwm);
  
  	if (channel)
  		clk_disable_unprepare(channel->clk);
  }
7e0321629   Martin Blumenstingl   pwm: meson: Pass ...
156
  static int meson_pwm_calc(struct meson_pwm *meson, struct pwm_device *pwm,
71523d181   Uwe Kleine-König   pwm: Ensure pwm_a...
157
  			  const struct pwm_state *state)
211ed6307   Neil Armstrong   pwm: Add support ...
158
  {
7e0321629   Martin Blumenstingl   pwm: meson: Pass ...
159
  	struct meson_pwm_channel *channel = pwm_get_chip_data(pwm);
b79c3670e   Martin Blumenstingl   pwm: meson: Don't...
160
  	unsigned int duty, period, pre_div, cnt, duty_cnt;
437fb760d   Colin Ian King   pwm: meson: Remov...
161
  	unsigned long fin_freq;
211ed6307   Neil Armstrong   pwm: Add support ...
162

b79c3670e   Martin Blumenstingl   pwm: meson: Don't...
163
164
165
166
  	duty = state->duty_cycle;
  	period = state->period;
  
  	if (state->polarity == PWM_POLARITY_INVERSED)
211ed6307   Neil Armstrong   pwm: Add support ...
167
  		duty = period - duty;
211ed6307   Neil Armstrong   pwm: Add support ...
168
169
170
171
172
173
174
175
176
  	fin_freq = clk_get_rate(channel->clk);
  	if (fin_freq == 0) {
  		dev_err(meson->chip.dev, "invalid source clock frequency
  ");
  		return -EINVAL;
  	}
  
  	dev_dbg(meson->chip.dev, "fin_freq: %lu Hz
  ", fin_freq);
211ed6307   Neil Armstrong   pwm: Add support ...
177

fb2081e87   Martin Blumenstingl   pwm: meson: Simpl...
178
  	pre_div = div64_u64(fin_freq * (u64)period, NSEC_PER_SEC * 0xffffLL);
51496e444   Martin Blumenstingl   pwm: meson: Consi...
179
  	if (pre_div > MISC_CLK_DIV_MASK) {
211ed6307   Neil Armstrong   pwm: Add support ...
180
181
182
183
  		dev_err(meson->chip.dev, "unable to get period pre_div
  ");
  		return -EINVAL;
  	}
fb2081e87   Martin Blumenstingl   pwm: meson: Simpl...
184
185
186
187
188
189
  	cnt = div64_u64(fin_freq * (u64)period, NSEC_PER_SEC * (pre_div + 1));
  	if (cnt > 0xffff) {
  		dev_err(meson->chip.dev, "unable to get period cnt
  ");
  		return -EINVAL;
  	}
211ed6307   Neil Armstrong   pwm: Add support ...
190
191
192
193
194
195
196
197
198
199
200
201
202
203
  	dev_dbg(meson->chip.dev, "period=%u pre_div=%u cnt=%u
  ", period,
  		pre_div, cnt);
  
  	if (duty == period) {
  		channel->pre_div = pre_div;
  		channel->hi = cnt;
  		channel->lo = 0;
  	} else if (duty == 0) {
  		channel->pre_div = pre_div;
  		channel->hi = 0;
  		channel->lo = cnt;
  	} else {
  		/* Then check is we can have the duty with the same pre_div */
fb2081e87   Martin Blumenstingl   pwm: meson: Simpl...
204
205
  		duty_cnt = div64_u64(fin_freq * (u64)duty,
  				     NSEC_PER_SEC * (pre_div + 1));
211ed6307   Neil Armstrong   pwm: Add support ...
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
  		if (duty_cnt > 0xffff) {
  			dev_err(meson->chip.dev, "unable to get duty cycle
  ");
  			return -EINVAL;
  		}
  
  		dev_dbg(meson->chip.dev, "duty=%u pre_div=%u duty_cnt=%u
  ",
  			duty, pre_div, duty_cnt);
  
  		channel->pre_div = pre_div;
  		channel->hi = duty_cnt;
  		channel->lo = cnt - duty_cnt;
  	}
  
  	return 0;
  }
084f13760   Martin Blumenstingl   pwm: meson: Unify...
223
  static void meson_pwm_enable(struct meson_pwm *meson, struct pwm_device *pwm)
211ed6307   Neil Armstrong   pwm: Add support ...
224
  {
084f13760   Martin Blumenstingl   pwm: meson: Unify...
225
  	struct meson_pwm_channel *channel = pwm_get_chip_data(pwm);
8bbf31645   Martin Blumenstingl   pwm: meson: Add t...
226
  	struct meson_pwm_channel_data *channel_data;
f173747ff   Martin Blumenstingl   pwm: meson: Use t...
227
  	unsigned long flags;
8bbf31645   Martin Blumenstingl   pwm: meson: Add t...
228
  	u32 value;
211ed6307   Neil Armstrong   pwm: Add support ...
229

8bbf31645   Martin Blumenstingl   pwm: meson: Add t...
230
  	channel_data = &meson_pwm_per_channel_data[pwm->hwpwm];
211ed6307   Neil Armstrong   pwm: Add support ...
231

f173747ff   Martin Blumenstingl   pwm: meson: Use t...
232
  	spin_lock_irqsave(&meson->lock, flags);
211ed6307   Neil Armstrong   pwm: Add support ...
233
  	value = readl(meson->base + REG_MISC_AB);
8bbf31645   Martin Blumenstingl   pwm: meson: Add t...
234
235
236
  	value &= ~(MISC_CLK_DIV_MASK << channel_data->clk_div_shift);
  	value |= channel->pre_div << channel_data->clk_div_shift;
  	value |= channel_data->clk_en_mask;
211ed6307   Neil Armstrong   pwm: Add support ...
237
  	writel(value, meson->base + REG_MISC_AB);
181164b66   Martin Blumenstingl   pwm: meson: Use G...
238
239
  	value = FIELD_PREP(PWM_HIGH_MASK, channel->hi) |
  		FIELD_PREP(PWM_LOW_MASK, channel->lo);
8bbf31645   Martin Blumenstingl   pwm: meson: Add t...
240
  	writel(value, meson->base + channel_data->reg_offset);
211ed6307   Neil Armstrong   pwm: Add support ...
241
242
  
  	value = readl(meson->base + REG_MISC_AB);
8bbf31645   Martin Blumenstingl   pwm: meson: Add t...
243
  	value |= channel_data->pwm_en_mask;
211ed6307   Neil Armstrong   pwm: Add support ...
244
  	writel(value, meson->base + REG_MISC_AB);
f173747ff   Martin Blumenstingl   pwm: meson: Use t...
245
246
  
  	spin_unlock_irqrestore(&meson->lock, flags);
211ed6307   Neil Armstrong   pwm: Add support ...
247
  }
084f13760   Martin Blumenstingl   pwm: meson: Unify...
248
  static void meson_pwm_disable(struct meson_pwm *meson, struct pwm_device *pwm)
211ed6307   Neil Armstrong   pwm: Add support ...
249
  {
f173747ff   Martin Blumenstingl   pwm: meson: Use t...
250
  	unsigned long flags;
8bbf31645   Martin Blumenstingl   pwm: meson: Add t...
251
  	u32 value;
211ed6307   Neil Armstrong   pwm: Add support ...
252

f173747ff   Martin Blumenstingl   pwm: meson: Use t...
253
  	spin_lock_irqsave(&meson->lock, flags);
211ed6307   Neil Armstrong   pwm: Add support ...
254
  	value = readl(meson->base + REG_MISC_AB);
8bbf31645   Martin Blumenstingl   pwm: meson: Add t...
255
  	value &= ~meson_pwm_per_channel_data[pwm->hwpwm].pwm_en_mask;
211ed6307   Neil Armstrong   pwm: Add support ...
256
  	writel(value, meson->base + REG_MISC_AB);
f173747ff   Martin Blumenstingl   pwm: meson: Use t...
257
258
  
  	spin_unlock_irqrestore(&meson->lock, flags);
211ed6307   Neil Armstrong   pwm: Add support ...
259
260
261
  }
  
  static int meson_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm,
71523d181   Uwe Kleine-König   pwm: Ensure pwm_a...
262
  			   const struct pwm_state *state)
211ed6307   Neil Armstrong   pwm: Add support ...
263
  {
7341c785d   Martin Blumenstingl   pwm: meson: Add s...
264
  	struct meson_pwm_channel *channel = pwm_get_chip_data(pwm);
211ed6307   Neil Armstrong   pwm: Add support ...
265
  	struct meson_pwm *meson = to_meson_pwm(chip);
211ed6307   Neil Armstrong   pwm: Add support ...
266
267
268
269
  	int err = 0;
  
  	if (!state)
  		return -EINVAL;
211ed6307   Neil Armstrong   pwm: Add support ...
270
  	if (!state->enabled) {
7341c785d   Martin Blumenstingl   pwm: meson: Add s...
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
  		if (state->polarity == PWM_POLARITY_INVERSED) {
  			/*
  			 * This IP block revision doesn't have an "always high"
  			 * setting which we can use for "inverted disabled".
  			 * Instead we achieve this using the same settings
  			 * that we use a pre_div of 0 (to get the shortest
  			 * possible duration for one "count") and
  			 * "period == duty_cycle". This results in a signal
  			 * which is LOW for one "count", while being HIGH for
  			 * the rest of the (so the signal is HIGH for slightly
  			 * less than 100% of the period, but this is the best
  			 * we can achieve).
  			 */
  			channel->pre_div = 0;
  			channel->hi = ~0;
  			channel->lo = 0;
  
  			meson_pwm_enable(meson, pwm);
  		} else {
  			meson_pwm_disable(meson, pwm);
  		}
d6885b3e0   Martin Blumenstingl   pwm: meson: Don't...
292
  	} else {
7e0321629   Martin Blumenstingl   pwm: meson: Pass ...
293
  		err = meson_pwm_calc(meson, pwm, state);
211ed6307   Neil Armstrong   pwm: Add support ...
294
  		if (err < 0)
f173747ff   Martin Blumenstingl   pwm: meson: Use t...
295
  			return err;
211ed6307   Neil Armstrong   pwm: Add support ...
296

084f13760   Martin Blumenstingl   pwm: meson: Unify...
297
  		meson_pwm_enable(meson, pwm);
211ed6307   Neil Armstrong   pwm: Add support ...
298
  	}
f173747ff   Martin Blumenstingl   pwm: meson: Use t...
299
  	return 0;
211ed6307   Neil Armstrong   pwm: Add support ...
300
  }
c375bcbaa   Martin Blumenstingl   pwm: meson: Read ...
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
  static unsigned int meson_pwm_cnt_to_ns(struct pwm_chip *chip,
  					struct pwm_device *pwm, u32 cnt)
  {
  	struct meson_pwm *meson = to_meson_pwm(chip);
  	struct meson_pwm_channel *channel;
  	unsigned long fin_freq;
  	u32 fin_ns;
  
  	/* to_meson_pwm() can only be used after .get_state() is called */
  	channel = &meson->channels[pwm->hwpwm];
  
  	fin_freq = clk_get_rate(channel->clk);
  	if (fin_freq == 0)
  		return 0;
  
  	fin_ns = div_u64(NSEC_PER_SEC, fin_freq);
  
  	return cnt * fin_ns * (channel->pre_div + 1);
  }
211ed6307   Neil Armstrong   pwm: Add support ...
320
321
322
323
  static void meson_pwm_get_state(struct pwm_chip *chip, struct pwm_device *pwm,
  				struct pwm_state *state)
  {
  	struct meson_pwm *meson = to_meson_pwm(chip);
c375bcbaa   Martin Blumenstingl   pwm: meson: Read ...
324
325
326
  	struct meson_pwm_channel_data *channel_data;
  	struct meson_pwm_channel *channel;
  	u32 value, tmp;
211ed6307   Neil Armstrong   pwm: Add support ...
327
328
329
  
  	if (!state)
  		return;
c375bcbaa   Martin Blumenstingl   pwm: meson: Read ...
330
331
  	channel = &meson->channels[pwm->hwpwm];
  	channel_data = &meson_pwm_per_channel_data[pwm->hwpwm];
211ed6307   Neil Armstrong   pwm: Add support ...
332
333
  
  	value = readl(meson->base + REG_MISC_AB);
c375bcbaa   Martin Blumenstingl   pwm: meson: Read ...
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
  
  	tmp = channel_data->pwm_en_mask | channel_data->clk_en_mask;
  	state->enabled = (value & tmp) == tmp;
  
  	tmp = value >> channel_data->clk_div_shift;
  	channel->pre_div = FIELD_GET(MISC_CLK_DIV_MASK, tmp);
  
  	value = readl(meson->base + channel_data->reg_offset);
  
  	channel->lo = FIELD_GET(PWM_LOW_MASK, value);
  	channel->hi = FIELD_GET(PWM_HIGH_MASK, value);
  
  	if (channel->lo == 0) {
  		state->period = meson_pwm_cnt_to_ns(chip, pwm, channel->hi);
  		state->duty_cycle = state->period;
  	} else if (channel->lo >= channel->hi) {
  		state->period = meson_pwm_cnt_to_ns(chip, pwm,
  						    channel->lo + channel->hi);
  		state->duty_cycle = meson_pwm_cnt_to_ns(chip, pwm,
  							channel->hi);
  	} else {
  		state->period = 0;
  		state->duty_cycle = 0;
  	}
211ed6307   Neil Armstrong   pwm: Add support ...
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
  }
  
  static const struct pwm_ops meson_pwm_ops = {
  	.request = meson_pwm_request,
  	.free = meson_pwm_free,
  	.apply = meson_pwm_apply,
  	.get_state = meson_pwm_get_state,
  	.owner = THIS_MODULE,
  };
  
  static const char * const pwm_meson8b_parent_names[] = {
  	"xtal", "vid_pll", "fclk_div4", "fclk_div3"
  };
  
  static const struct meson_pwm_data pwm_meson8b_data = {
  	.parent_names = pwm_meson8b_parent_names,
d396b20a1   Jerome Brunet   pwm: meson: Add c...
374
  	.num_parents = ARRAY_SIZE(pwm_meson8b_parent_names),
211ed6307   Neil Armstrong   pwm: Add support ...
375
376
377
378
379
380
381
382
  };
  
  static const char * const pwm_gxbb_parent_names[] = {
  	"xtal", "hdmi_pll", "fclk_div4", "fclk_div3"
  };
  
  static const struct meson_pwm_data pwm_gxbb_data = {
  	.parent_names = pwm_gxbb_parent_names,
d396b20a1   Jerome Brunet   pwm: meson: Add c...
383
384
385
386
387
388
389
390
391
392
393
394
395
396
  	.num_parents = ARRAY_SIZE(pwm_gxbb_parent_names),
  };
  
  /*
   * Only the 2 first inputs of the GXBB AO PWMs are valid
   * The last 2 are grounded
   */
  static const char * const pwm_gxbb_ao_parent_names[] = {
  	"xtal", "clk81"
  };
  
  static const struct meson_pwm_data pwm_gxbb_ao_data = {
  	.parent_names = pwm_gxbb_ao_parent_names,
  	.num_parents = ARRAY_SIZE(pwm_gxbb_ao_parent_names),
211ed6307   Neil Armstrong   pwm: Add support ...
397
  };
bccaa3f91   Jian Hu   pwm: meson: Add c...
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
  static const char * const pwm_axg_ee_parent_names[] = {
  	"xtal", "fclk_div5", "fclk_div4", "fclk_div3"
  };
  
  static const struct meson_pwm_data pwm_axg_ee_data = {
  	.parent_names = pwm_axg_ee_parent_names,
  	.num_parents = ARRAY_SIZE(pwm_axg_ee_parent_names),
  };
  
  static const char * const pwm_axg_ao_parent_names[] = {
  	"aoclk81", "xtal", "fclk_div4", "fclk_div5"
  };
  
  static const struct meson_pwm_data pwm_axg_ao_data = {
  	.parent_names = pwm_axg_ao_parent_names,
  	.num_parents = ARRAY_SIZE(pwm_axg_ao_parent_names),
  };
9bce02ef0   Neil Armstrong   pwm: meson: Fix t...
415
416
417
418
419
420
421
422
  static const char * const pwm_g12a_ao_ab_parent_names[] = {
  	"xtal", "aoclk81", "fclk_div4", "fclk_div5"
  };
  
  static const struct meson_pwm_data pwm_g12a_ao_ab_data = {
  	.parent_names = pwm_g12a_ao_ab_parent_names,
  	.num_parents = ARRAY_SIZE(pwm_g12a_ao_ab_parent_names),
  };
f41efceb4   Neil Armstrong   pwm: meson: Add c...
423
  static const char * const pwm_g12a_ao_cd_parent_names[] = {
9bce02ef0   Neil Armstrong   pwm: meson: Fix t...
424
  	"xtal", "aoclk81",
f41efceb4   Neil Armstrong   pwm: meson: Add c...
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
  };
  
  static const struct meson_pwm_data pwm_g12a_ao_cd_data = {
  	.parent_names = pwm_g12a_ao_cd_parent_names,
  	.num_parents = ARRAY_SIZE(pwm_g12a_ao_cd_parent_names),
  };
  
  static const char * const pwm_g12a_ee_parent_names[] = {
  	"xtal", "hdmi_pll", "fclk_div4", "fclk_div3"
  };
  
  static const struct meson_pwm_data pwm_g12a_ee_data = {
  	.parent_names = pwm_g12a_ee_parent_names,
  	.num_parents = ARRAY_SIZE(pwm_g12a_ee_parent_names),
  };
211ed6307   Neil Armstrong   pwm: Add support ...
440
  static const struct of_device_id meson_pwm_matches[] = {
d396b20a1   Jerome Brunet   pwm: meson: Add c...
441
442
443
444
445
446
447
448
449
450
451
452
  	{
  		.compatible = "amlogic,meson8b-pwm",
  		.data = &pwm_meson8b_data
  	},
  	{
  		.compatible = "amlogic,meson-gxbb-pwm",
  		.data = &pwm_gxbb_data
  	},
  	{
  		.compatible = "amlogic,meson-gxbb-ao-pwm",
  		.data = &pwm_gxbb_ao_data
  	},
bccaa3f91   Jian Hu   pwm: meson: Add c...
453
454
455
456
457
458
459
460
  	{
  		.compatible = "amlogic,meson-axg-ee-pwm",
  		.data = &pwm_axg_ee_data
  	},
  	{
  		.compatible = "amlogic,meson-axg-ao-pwm",
  		.data = &pwm_axg_ao_data
  	},
f41efceb4   Neil Armstrong   pwm: meson: Add c...
461
462
463
464
465
466
  	{
  		.compatible = "amlogic,meson-g12a-ee-pwm",
  		.data = &pwm_g12a_ee_data
  	},
  	{
  		.compatible = "amlogic,meson-g12a-ao-pwm-ab",
9bce02ef0   Neil Armstrong   pwm: meson: Fix t...
467
  		.data = &pwm_g12a_ao_ab_data
f41efceb4   Neil Armstrong   pwm: meson: Add c...
468
469
470
471
472
  	},
  	{
  		.compatible = "amlogic,meson-g12a-ao-pwm-cd",
  		.data = &pwm_g12a_ao_cd_data
  	},
211ed6307   Neil Armstrong   pwm: Add support ...
473
474
475
  	{},
  };
  MODULE_DEVICE_TABLE(of, meson_pwm_matches);
a50a49a45   Martin Blumenstingl   pwm: meson: Add t...
476
  static int meson_pwm_init_channels(struct meson_pwm *meson)
211ed6307   Neil Armstrong   pwm: Add support ...
477
478
  {
  	struct device *dev = meson->chip.dev;
211ed6307   Neil Armstrong   pwm: Add support ...
479
480
481
482
483
484
  	struct clk_init_data init;
  	unsigned int i;
  	char name[255];
  	int err;
  
  	for (i = 0; i < meson->chip.npwm; i++) {
a50a49a45   Martin Blumenstingl   pwm: meson: Add t...
485
  		struct meson_pwm_channel *channel = &meson->channels[i];
211ed6307   Neil Armstrong   pwm: Add support ...
486

b96e9eb62   Jerome Brunet   pwm: meson: Fix m...
487
  		snprintf(name, sizeof(name), "%s#mux%u", dev_name(dev), i);
211ed6307   Neil Armstrong   pwm: Add support ...
488
489
490
  
  		init.name = name;
  		init.ops = &clk_mux_ops;
90b6c5c73   Stephen Boyd   clk: Remove CLK_I...
491
  		init.flags = 0;
211ed6307   Neil Armstrong   pwm: Add support ...
492
  		init.parent_names = meson->data->parent_names;
d396b20a1   Jerome Brunet   pwm: meson: Add c...
493
  		init.num_parents = meson->data->num_parents;
211ed6307   Neil Armstrong   pwm: Add support ...
494
495
  
  		channel->mux.reg = meson->base + REG_MISC_AB;
8bbf31645   Martin Blumenstingl   pwm: meson: Add t...
496
497
  		channel->mux.shift =
  				meson_pwm_per_channel_data[i].clk_sel_shift;
33cefd84d   Martin Blumenstingl   pwm: meson: Chang...
498
  		channel->mux.mask = MISC_CLK_SEL_MASK;
211ed6307   Neil Armstrong   pwm: Add support ...
499
500
501
502
503
504
505
506
507
508
509
510
511
512
  		channel->mux.flags = 0;
  		channel->mux.lock = &meson->lock;
  		channel->mux.table = NULL;
  		channel->mux.hw.init = &init;
  
  		channel->clk = devm_clk_register(dev, &channel->mux.hw);
  		if (IS_ERR(channel->clk)) {
  			err = PTR_ERR(channel->clk);
  			dev_err(dev, "failed to register %s: %d
  ", name, err);
  			return err;
  		}
  
  		snprintf(name, sizeof(name), "clkin%u", i);
ba4004c71   Martin Blumenstingl   pwm: meson: Use d...
513
514
515
  		channel->clk_parent = devm_clk_get_optional(dev, name);
  		if (IS_ERR(channel->clk_parent))
  			return PTR_ERR(channel->clk_parent);
211ed6307   Neil Armstrong   pwm: Add support ...
516
517
518
519
  	}
  
  	return 0;
  }
211ed6307   Neil Armstrong   pwm: Add support ...
520
521
  static int meson_pwm_probe(struct platform_device *pdev)
  {
211ed6307   Neil Armstrong   pwm: Add support ...
522
523
524
525
526
527
528
529
530
531
532
533
  	struct meson_pwm *meson;
  	struct resource *regs;
  	int err;
  
  	meson = devm_kzalloc(&pdev->dev, sizeof(*meson), GFP_KERNEL);
  	if (!meson)
  		return -ENOMEM;
  
  	regs = platform_get_resource(pdev, IORESOURCE_MEM, 0);
  	meson->base = devm_ioremap_resource(&pdev->dev, regs);
  	if (IS_ERR(meson->base))
  		return PTR_ERR(meson->base);
c69999566   Axel Lin   pwm: meson: Add m...
534
  	spin_lock_init(&meson->lock);
211ed6307   Neil Armstrong   pwm: Add support ...
535
536
537
  	meson->chip.dev = &pdev->dev;
  	meson->chip.ops = &meson_pwm_ops;
  	meson->chip.base = -1;
a50a49a45   Martin Blumenstingl   pwm: meson: Add t...
538
  	meson->chip.npwm = MESON_NUM_PWMS;
211ed6307   Neil Armstrong   pwm: Add support ...
539
540
541
542
  	meson->chip.of_xlate = of_pwm_xlate_with_flags;
  	meson->chip.of_pwm_n_cells = 3;
  
  	meson->data = of_device_get_match_data(&pdev->dev);
211ed6307   Neil Armstrong   pwm: Add support ...
543

a50a49a45   Martin Blumenstingl   pwm: meson: Add t...
544
  	err = meson_pwm_init_channels(meson);
211ed6307   Neil Armstrong   pwm: Add support ...
545
546
547
548
549
550
551
552
553
  	if (err < 0)
  		return err;
  
  	err = pwmchip_add(&meson->chip);
  	if (err < 0) {
  		dev_err(&pdev->dev, "failed to register PWM chip: %d
  ", err);
  		return err;
  	}
211ed6307   Neil Armstrong   pwm: Add support ...
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
  	platform_set_drvdata(pdev, meson);
  
  	return 0;
  }
  
  static int meson_pwm_remove(struct platform_device *pdev)
  {
  	struct meson_pwm *meson = platform_get_drvdata(pdev);
  
  	return pwmchip_remove(&meson->chip);
  }
  
  static struct platform_driver meson_pwm_driver = {
  	.driver = {
  		.name = "meson-pwm",
  		.of_match_table = meson_pwm_matches,
  	},
  	.probe = meson_pwm_probe,
  	.remove = meson_pwm_remove,
  };
  module_platform_driver(meson_pwm_driver);
211ed6307   Neil Armstrong   pwm: Add support ...
575
576
577
  MODULE_DESCRIPTION("Amlogic Meson PWM Generator driver");
  MODULE_AUTHOR("Neil Armstrong <narmstrong@baylibre.com>");
  MODULE_LICENSE("Dual BSD/GPL");