blob: c2597d8b6698bb582248fbfd3fcd88a2bd509a6e [file] [log] [blame]
Neil Armstrongda7f09c2020-10-01 10:04:56 +02001// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
2/*
3 * Copyright (C) 2020 BayLibre, SAS.
4 * Author: Neil Armstrong <narmstrong@baylibre.com>
5 * Copyright (C) 2014 Amlogic, Inc.
6 *
7 * This PWM is only a set of Gates, Dividers and Counters:
8 * PWM output is achieved by calculating a clock that permits calculating
9 * two periods (low and high). The counter then has to be set to switch after
10 * N cycles for the first half period.
11 * The hardware has no "polarity" setting. This driver reverses the period
12 * cycles (the low length is inverted with the high length) for
13 * PWM_POLARITY_INVERSED.
14 * Setting the polarity will disable and re-enable the PWM output.
15 * Disabling the PWM stops the output immediately (without waiting for the
16 * current period to complete first).
17 */
18
Neil Armstrongda7f09c2020-10-01 10:04:56 +020019#include <clk.h>
20#include <div64.h>
21#include <dm.h>
22#include <pwm.h>
23#include <regmap.h>
24#include <linux/io.h>
25#include <linux/math64.h>
26#include <linux/bitfield.h>
27#include <linux/clk-provider.h>
Igor Prusovc3421ea2023-11-09 20:10:04 +030028#include <linux/time.h>
Neil Armstrongda7f09c2020-10-01 10:04:56 +020029
30#define REG_PWM_A 0x0
31#define REG_PWM_B 0x4
32#define PWM_LOW_MASK GENMASK(15, 0)
33#define PWM_HIGH_MASK GENMASK(31, 16)
34
35#define REG_MISC_AB 0x8
36#define MISC_B_CLK_EN BIT(23)
37#define MISC_A_CLK_EN BIT(15)
38#define MISC_CLK_DIV_MASK 0x7f
39#define MISC_B_CLK_DIV_SHIFT 16
40#define MISC_A_CLK_DIV_SHIFT 8
41#define MISC_B_CLK_SEL_SHIFT 6
42#define MISC_A_CLK_SEL_SHIFT 4
43#define MISC_CLK_SEL_MASK 0x3
44#define MISC_B_EN BIT(1)
45#define MISC_A_EN BIT(0)
46
47#define MESON_NUM_PWMS 2
48
49static struct meson_pwm_channel_data {
50 u8 reg_offset;
51 u8 clk_sel_shift;
52 u8 clk_div_shift;
53 u32 clk_en_mask;
54 u32 pwm_en_mask;
55} meson_pwm_per_channel_data[MESON_NUM_PWMS] = {
56 {
57 .reg_offset = REG_PWM_A,
58 .clk_sel_shift = MISC_A_CLK_SEL_SHIFT,
59 .clk_div_shift = MISC_A_CLK_DIV_SHIFT,
60 .clk_en_mask = MISC_A_CLK_EN,
61 .pwm_en_mask = MISC_A_EN,
62 },
63 {
64 .reg_offset = REG_PWM_B,
65 .clk_sel_shift = MISC_B_CLK_SEL_SHIFT,
66 .clk_div_shift = MISC_B_CLK_DIV_SHIFT,
67 .clk_en_mask = MISC_B_CLK_EN,
68 .pwm_en_mask = MISC_B_EN,
69 }
70};
71
72struct meson_pwm_channel {
73 unsigned int hi;
74 unsigned int lo;
75 u8 pre_div;
76 uint period_ns;
77 uint duty_ns;
78 bool configured;
79 bool enabled;
80 bool polarity;
81 struct clk clk;
82};
83
84struct meson_pwm_data {
85 const long *parent_ids;
86 unsigned int num_parents;
87};
88
89struct meson_pwm {
90 const struct meson_pwm_data *data;
91 struct meson_pwm_channel channels[MESON_NUM_PWMS];
92 void __iomem *base;
93};
94
95static int meson_pwm_set_enable(struct udevice *dev, uint channel, bool enable);
96
97static int meson_pwm_set_config(struct udevice *dev, uint channeln,
98 uint period_ns, uint duty_ns)
99{
100 struct meson_pwm *priv = dev_get_priv(dev);
101 struct meson_pwm_channel *channel;
102 struct meson_pwm_channel_data *channel_data;
103 unsigned int duty, period, pre_div, cnt, duty_cnt;
104 unsigned long fin_freq;
105
106 if (channeln >= MESON_NUM_PWMS)
107 return -ENODEV;
108
109 channel = &priv->channels[channeln];
110 channel_data = &meson_pwm_per_channel_data[channeln];
111
112 period = period_ns;
113 if (channel->polarity)
114 duty = period_ns - duty_ns;
115 else
116 duty = duty_ns;
117
118 debug("%s%d: polarity %s duty %d period %d\n", __func__, channeln,
119 channel->polarity ? "true" : "false", duty, period);
120
121 fin_freq = clk_get_rate(&channel->clk);
122 if (fin_freq == 0) {
123 printf("%s%d: invalid source clock frequency\n", __func__, channeln);
124 return -EINVAL;
125 }
126
127 debug("%s%d: fin_freq: %lu Hz\n", __func__, channeln, fin_freq);
128
129 pre_div = div64_u64(fin_freq * (u64)period, NSEC_PER_SEC * 0xffffLL);
130 if (pre_div > MISC_CLK_DIV_MASK) {
131 printf("%s%d: unable to get period pre_div\n", __func__, channeln);
132 return -EINVAL;
133 }
134
135 cnt = div64_u64(fin_freq * (u64)period, NSEC_PER_SEC * (pre_div + 1));
136 if (cnt > 0xffff) {
137 printf("%s%d: unable to get period cnt\n", __func__, channeln);
138 return -EINVAL;
139 }
140
141 debug("%s%d: period=%u pre_div=%u cnt=%u\n", __func__, channeln, period, pre_div, cnt);
142
143 if (duty == period) {
144 channel->pre_div = pre_div;
145 channel->hi = cnt;
146 channel->lo = 0;
147 } else if (duty == 0) {
148 channel->pre_div = pre_div;
149 channel->hi = 0;
150 channel->lo = cnt;
151 } else {
152 /* Then check is we can have the duty with the same pre_div */
153 duty_cnt = div64_u64(fin_freq * (u64)duty, NSEC_PER_SEC * (pre_div + 1));
154 if (duty_cnt > 0xffff) {
155 printf("%s%d: unable to get duty cycle\n", __func__, channeln);
156 return -EINVAL;
157 }
158
159 debug("%s%d: duty=%u pre_div=%u duty_cnt=%u\n",
160 __func__, channeln, duty, pre_div, duty_cnt);
161
162 channel->pre_div = pre_div;
163 channel->hi = duty_cnt;
164 channel->lo = cnt - duty_cnt;
165 }
166
167 channel->period_ns = period_ns;
168 channel->duty_ns = duty_ns;
169 channel->configured = true;
170
171 if (channel->enabled) {
172 meson_pwm_set_enable(dev, channeln, false);
173 meson_pwm_set_enable(dev, channeln, true);
174 }
175
176 return 0;
177}
178
179static int meson_pwm_set_enable(struct udevice *dev, uint channeln, bool enable)
180{
181 struct meson_pwm *priv = dev_get_priv(dev);
182 struct meson_pwm_channel *channel;
183 struct meson_pwm_channel_data *channel_data;
184 u32 value;
185
186 if (channeln >= MESON_NUM_PWMS)
187 return -ENODEV;
188
189 channel = &priv->channels[channeln];
190 channel_data = &meson_pwm_per_channel_data[channeln];
191
192 if (!channel->configured)
193 return -EINVAL;
194
195 if (enable) {
196 if (channel->enabled)
197 return 0;
198
199 value = readl(priv->base + REG_MISC_AB);
200 value &= ~(MISC_CLK_DIV_MASK << channel_data->clk_div_shift);
201 value |= channel->pre_div << channel_data->clk_div_shift;
202 value |= channel_data->clk_en_mask;
203 writel(value, priv->base + REG_MISC_AB);
204
205 value = FIELD_PREP(PWM_HIGH_MASK, channel->hi) |
206 FIELD_PREP(PWM_LOW_MASK, channel->lo);
207 writel(value, priv->base + channel_data->reg_offset);
208
209 value = readl(priv->base + REG_MISC_AB);
210 value |= channel_data->pwm_en_mask;
211 writel(value, priv->base + REG_MISC_AB);
212
213 debug("%s%d: enabled\n", __func__, channeln);
214 channel->enabled = true;
215 } else {
216 if (!channel->enabled)
217 return 0;
218
219 value = readl(priv->base + REG_MISC_AB);
220 value &= channel_data->pwm_en_mask;
221 writel(value, priv->base + REG_MISC_AB);
222
223 debug("%s%d: disabled\n", __func__, channeln);
224 channel->enabled = false;
225 }
226
227 return 0;
228}
229
230static int meson_pwm_set_invert(struct udevice *dev, uint channeln, bool polarity)
231{
232 struct meson_pwm *priv = dev_get_priv(dev);
233 struct meson_pwm_channel *channel;
234
235 if (channeln >= MESON_NUM_PWMS)
236 return -ENODEV;
237
238 debug("%s%d: set invert %s\n", __func__, channeln, polarity ? "true" : "false");
239
240 channel = &priv->channels[channeln];
241
242 channel->polarity = polarity;
243
244 if (!channel->configured)
245 return 0;
246
247 return meson_pwm_set_config(dev, channeln, channel->period_ns, channel->duty_ns);
248}
249
Simon Glassaad29ae2020-12-03 16:55:21 -0700250static int meson_pwm_of_to_plat(struct udevice *dev)
Neil Armstrongda7f09c2020-10-01 10:04:56 +0200251{
252 struct meson_pwm *priv = dev_get_priv(dev);
253
254 priv->base = dev_read_addr_ptr(dev);
255
256 return 0;
257}
258
259static int meson_pwm_probe(struct udevice *dev)
260{
261 struct meson_pwm *priv = dev_get_priv(dev);
262 struct meson_pwm_data *data;
263 unsigned int i, p;
264 char name[255];
265 int err;
266 u32 reg;
267
268 data = (struct meson_pwm_data *)dev_get_driver_data(dev);
269 if (!data)
270 return -EINVAL;
271
272 for (i = 0; i < MESON_NUM_PWMS; i++) {
273 struct meson_pwm_channel *channel = &priv->channels[i];
274 struct meson_pwm_channel_data *channel_data = &meson_pwm_per_channel_data[i];
275
276 snprintf(name, sizeof(name), "clkin%u", i);
277
278 err = clk_get_by_name(dev, name, &channel->clk);
279 /* If clock is not specified, use the already set clock */
280 if (err == -ENODATA) {
281 struct udevice *cdev;
282 struct uclass *uc;
283
284 /* Get parent from mux */
285 p = (readl(priv->base + REG_MISC_AB) >> channel_data->clk_sel_shift) &
286 MISC_CLK_SEL_MASK;
287
288 if (p >= data->num_parents) {
289 printf("%s%d: hw parent is invalid\n", __func__, i);
290 return -EINVAL;
291 }
292
293 if (data->parent_ids[p] == -1) {
294 /* Search for xtal clk */
295 const char *str;
296
297 err = uclass_get(UCLASS_CLK, &uc);
298 if (err)
299 return err;
300
301 uclass_foreach_dev(cdev, uc) {
302 if (strcmp(cdev->driver->name, "fixed_rate_clock"))
303 continue;
304
Simon Glassa7ece582020-12-19 10:40:14 -0700305 str = ofnode_read_string(dev_ofnode(cdev),
306 "clock-output-names");
Neil Armstrongda7f09c2020-10-01 10:04:56 +0200307 if (!str)
308 continue;
309
310 if (!strcmp(str, "xtal")) {
311 err = uclass_get_device_by_ofnode(UCLASS_CLK,
Simon Glassa7ece582020-12-19 10:40:14 -0700312 dev_ofnode(cdev),
Neil Armstrongda7f09c2020-10-01 10:04:56 +0200313 &cdev);
314 if (err) {
315 printf("%s%d: Failed to get xtal clk\n", __func__, i);
316 return err;
317 }
318
319 break;
320 }
321 }
322
323 if (!cdev) {
324 printf("%s%d: Failed to find xtal clk device\n", __func__, i);
325 return -EINVAL;
326 }
327
328 channel->clk.dev = cdev;
329 channel->clk.id = 0;
330 channel->clk.data = 0;
331 } else {
332 /* Look for parent clock */
333 err = uclass_get(UCLASS_CLK, &uc);
334 if (err)
335 return err;
336
337 uclass_foreach_dev(cdev, uc) {
338 if (strstr(cdev->driver->name, "meson_clk"))
339 break;
340 }
341
342 if (!cdev) {
343 printf("%s%d: Failed to find clk device\n", __func__, i);
344 return -EINVAL;
345 }
346
Simon Glassa7ece582020-12-19 10:40:14 -0700347 err = uclass_get_device_by_ofnode(UCLASS_CLK,
348 dev_ofnode(cdev),
349 &cdev);
Neil Armstrongda7f09c2020-10-01 10:04:56 +0200350 if (err) {
351 printf("%s%d: Failed to get clk controller\n", __func__, i);
352 return err;
353 }
354
355 channel->clk.dev = cdev;
356 channel->clk.id = data->parent_ids[p];
357 channel->clk.data = 0;
358 }
359
360 /* We have our source clock, do not alter HW clock mux */
361 continue;
362 } else
363 return err;
364
365 /* Get id in list */
366 for (p = 0 ; p < data->num_parents ; ++p) {
367 if (!strcmp(channel->clk.dev->driver->name, "fixed_rate_clock")) {
368 if (data->parent_ids[p] == -1)
369 break;
370 } else {
371 if (data->parent_ids[p] == channel->clk.id)
372 break;
373 }
374 }
375
376 /* Invalid clock ID */
377 if (p == data->num_parents) {
378 printf("%s%d: source clock is invalid\n", __func__, i);
379 return -EINVAL;
380 }
381
382 /* switch parent in mux */
383 reg = readl(priv->base + REG_MISC_AB);
384
385 debug("%s%d: switching parent %d to %d\n", __func__, i,
386 (reg >> channel_data->clk_sel_shift) & MISC_CLK_SEL_MASK, p);
387
388 reg &= MISC_CLK_SEL_MASK << channel_data->clk_sel_shift;
389 reg |= (p & MISC_CLK_SEL_MASK) << channel_data->clk_sel_shift;
390 writel(reg, priv->base + REG_MISC_AB);
391 }
392
393 return 0;
394}
395
396static const struct pwm_ops meson_pwm_ops = {
397 .set_config = meson_pwm_set_config,
398 .set_enable = meson_pwm_set_enable,
399 .set_invert = meson_pwm_set_invert,
400};
401
Wolfgang Denk62fb2b42021-09-27 17:42:39 +0200402#define XTAL -1
Neil Armstrongda7f09c2020-10-01 10:04:56 +0200403
404/* Local clock ids aliases to avoid define conflicts */
405#define GXBB_CLKID_HDMI_PLL 2
406#define GXBB_CLKID_FCLK_DIV3 5
407#define GXBB_CLKID_FCLK_DIV4 6
408#define GXBB_CLKID_CLK81 12
409
410static const long pwm_gxbb_parent_ids[] = {
411 XTAL, GXBB_CLKID_HDMI_PLL, GXBB_CLKID_FCLK_DIV4, GXBB_CLKID_FCLK_DIV3
412};
413
414static const struct meson_pwm_data pwm_gxbb_data = {
415 .parent_ids = pwm_gxbb_parent_ids,
416 .num_parents = ARRAY_SIZE(pwm_gxbb_parent_ids),
417};
418
419/*
420 * Only the 2 first inputs of the GXBB AO PWMs are valid
421 * The last 2 are grounded
422 */
423static const long pwm_gxbb_ao_parent_ids[] = {
424 XTAL, GXBB_CLKID_CLK81
425};
426
427static const struct meson_pwm_data pwm_gxbb_ao_data = {
428 .parent_ids = pwm_gxbb_ao_parent_ids,
429 .num_parents = ARRAY_SIZE(pwm_gxbb_ao_parent_ids),
430};
431
432/* Local clock ids aliases to avoid define conflicts */
433#define AXG_CLKID_FCLK_DIV3 3
434#define AXG_CLKID_FCLK_DIV4 4
435#define AXG_CLKID_FCLK_DIV5 5
436#define AXG_CLKID_CLK81 10
437
438static const long pwm_axg_ee_parent_ids[] = {
439 XTAL, AXG_CLKID_FCLK_DIV5, AXG_CLKID_FCLK_DIV4, AXG_CLKID_FCLK_DIV3
440};
441
442static const struct meson_pwm_data pwm_axg_ee_data = {
443 .parent_ids = pwm_axg_ee_parent_ids,
444 .num_parents = ARRAY_SIZE(pwm_axg_ee_parent_ids),
445};
446
447static const long pwm_axg_ao_parent_ids[] = {
448 AXG_CLKID_CLK81, XTAL, AXG_CLKID_FCLK_DIV4, AXG_CLKID_FCLK_DIV5
449};
450
451static const struct meson_pwm_data pwm_axg_ao_data = {
452 .parent_ids = pwm_axg_ao_parent_ids,
453 .num_parents = ARRAY_SIZE(pwm_axg_ao_parent_ids),
454};
455
456/* Local clock ids aliases to avoid define conflicts */
457#define G12A_CLKID_FCLK_DIV3 3
458#define G12A_CLKID_FCLK_DIV4 4
459#define G12A_CLKID_FCLK_DIV5 5
460#define G12A_CLKID_CLK81 10
461#define G12A_CLKID_HDMI_PLL 128
462
463static const long pwm_g12a_ao_ab_parent_ids[] = {
464 XTAL, G12A_CLKID_CLK81, G12A_CLKID_FCLK_DIV4, G12A_CLKID_FCLK_DIV5
465};
466
467static const struct meson_pwm_data pwm_g12a_ao_ab_data = {
468 .parent_ids = pwm_g12a_ao_ab_parent_ids,
469 .num_parents = ARRAY_SIZE(pwm_g12a_ao_ab_parent_ids),
470};
471
472static const long pwm_g12a_ao_cd_parent_ids[] = {
473 XTAL, G12A_CLKID_CLK81,
474};
475
476static const struct meson_pwm_data pwm_g12a_ao_cd_data = {
477 .parent_ids = pwm_g12a_ao_cd_parent_ids,
478 .num_parents = ARRAY_SIZE(pwm_g12a_ao_cd_parent_ids),
479};
480
481static const long pwm_g12a_ee_parent_ids[] = {
482 XTAL, G12A_CLKID_HDMI_PLL, G12A_CLKID_FCLK_DIV4, G12A_CLKID_FCLK_DIV3
483};
484
485static const struct meson_pwm_data pwm_g12a_ee_data = {
486 .parent_ids = pwm_g12a_ee_parent_ids,
487 .num_parents = ARRAY_SIZE(pwm_g12a_ee_parent_ids),
488};
489
490static const struct udevice_id meson_pwm_ids[] = {
491 {
492 .compatible = "amlogic,meson-gxbb-pwm",
493 .data = (ulong)&pwm_gxbb_data
494 },
495 {
496 .compatible = "amlogic,meson-gxbb-ao-pwm",
497 .data = (ulong)&pwm_gxbb_ao_data
498 },
499 {
500 .compatible = "amlogic,meson-axg-ee-pwm",
501 .data = (ulong)&pwm_axg_ee_data
502 },
503 {
504 .compatible = "amlogic,meson-axg-ao-pwm",
505 .data = (ulong)&pwm_axg_ao_data
506 },
507 {
508 .compatible = "amlogic,meson-g12a-ee-pwm",
509 .data = (ulong)&pwm_g12a_ee_data
510 },
511 {
512 .compatible = "amlogic,meson-g12a-ao-pwm-ab",
513 .data = (ulong)&pwm_g12a_ao_ab_data
514 },
515 {
516 .compatible = "amlogic,meson-g12a-ao-pwm-cd",
517 .data = (ulong)&pwm_g12a_ao_cd_data
518 },
519};
520
521U_BOOT_DRIVER(meson_pwm) = {
522 .name = "meson_pwm",
523 .id = UCLASS_PWM,
524 .of_match = meson_pwm_ids,
525 .ops = &meson_pwm_ops,
Simon Glassaad29ae2020-12-03 16:55:21 -0700526 .of_to_plat = meson_pwm_of_to_plat,
Neil Armstrongda7f09c2020-10-01 10:04:56 +0200527 .probe = meson_pwm_probe,
Simon Glass8a2b47f2020-12-03 16:55:17 -0700528 .priv_auto = sizeof(struct meson_pwm),
Neil Armstrongda7f09c2020-10-01 10:04:56 +0200529};