blob: 8b277a2212e23827990f9b03cf2fd96774cc21d0 [file] [log] [blame]
Neil Armstrong1cdb4412019-05-20 16:04:21 +02001// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
Neil Armstrong211ed632016-08-22 17:36:30 +02002/*
Neil Armstrong211ed632016-08-22 17:36:30 +02003 * Copyright (c) 2016 BayLibre, SAS.
4 * Author: Neil Armstrong <narmstrong@baylibre.com>
5 * Copyright (C) 2014 Amlogic, Inc.
Neil Armstrong211ed632016-08-22 17:36:30 +02006 */
7
8#include <linux/clk.h>
9#include <linux/clk-provider.h>
10#include <linux/err.h>
11#include <linux/io.h>
12#include <linux/kernel.h>
13#include <linux/module.h>
14#include <linux/of.h>
15#include <linux/of_device.h>
16#include <linux/platform_device.h>
17#include <linux/pwm.h>
18#include <linux/slab.h>
19#include <linux/spinlock.h>
20
21#define REG_PWM_A 0x0
22#define REG_PWM_B 0x4
23#define PWM_HIGH_SHIFT 16
24
25#define REG_MISC_AB 0x8
26#define MISC_B_CLK_EN BIT(23)
27#define MISC_A_CLK_EN BIT(15)
28#define MISC_CLK_DIV_MASK 0x7f
29#define MISC_B_CLK_DIV_SHIFT 16
30#define MISC_A_CLK_DIV_SHIFT 8
31#define MISC_B_CLK_SEL_SHIFT 6
32#define MISC_A_CLK_SEL_SHIFT 4
33#define MISC_CLK_SEL_WIDTH 2
34#define MISC_B_EN BIT(1)
35#define MISC_A_EN BIT(0)
36
37static const unsigned int mux_reg_shifts[] = {
38 MISC_A_CLK_SEL_SHIFT,
39 MISC_B_CLK_SEL_SHIFT
40};
41
42struct meson_pwm_channel {
43 unsigned int hi;
44 unsigned int lo;
45 u8 pre_div;
46
47 struct pwm_state state;
48
49 struct clk *clk_parent;
50 struct clk_mux mux;
51 struct clk *clk;
52};
53
54struct meson_pwm_data {
55 const char * const *parent_names;
Jerome Brunetd396b202017-06-08 14:24:15 +020056 unsigned int num_parents;
Neil Armstrong211ed632016-08-22 17:36:30 +020057};
58
59struct meson_pwm {
60 struct pwm_chip chip;
61 const struct meson_pwm_data *data;
62 void __iomem *base;
63 u8 inverter_mask;
Martin Blumenstinglf1737472019-04-01 19:57:48 +020064 /*
65 * Protects register (write) access to the REG_MISC_AB register
66 * that is shared between the two PWMs.
67 */
Neil Armstrong211ed632016-08-22 17:36:30 +020068 spinlock_t lock;
69};
70
71static inline struct meson_pwm *to_meson_pwm(struct pwm_chip *chip)
72{
73 return container_of(chip, struct meson_pwm, chip);
74}
75
76static int meson_pwm_request(struct pwm_chip *chip, struct pwm_device *pwm)
77{
78 struct meson_pwm_channel *channel = pwm_get_chip_data(pwm);
79 struct device *dev = chip->dev;
80 int err;
81
82 if (!channel)
83 return -ENODEV;
84
85 if (channel->clk_parent) {
86 err = clk_set_parent(channel->clk, channel->clk_parent);
87 if (err < 0) {
88 dev_err(dev, "failed to set parent %s for %s: %d\n",
89 __clk_get_name(channel->clk_parent),
90 __clk_get_name(channel->clk), err);
91 return err;
92 }
93 }
94
95 err = clk_prepare_enable(channel->clk);
96 if (err < 0) {
97 dev_err(dev, "failed to enable clock %s: %d\n",
98 __clk_get_name(channel->clk), err);
99 return err;
100 }
101
102 chip->ops->get_state(chip, pwm, &channel->state);
103
104 return 0;
105}
106
107static void meson_pwm_free(struct pwm_chip *chip, struct pwm_device *pwm)
108{
109 struct meson_pwm_channel *channel = pwm_get_chip_data(pwm);
110
111 if (channel)
112 clk_disable_unprepare(channel->clk);
113}
114
115static int meson_pwm_calc(struct meson_pwm *meson,
116 struct meson_pwm_channel *channel, unsigned int id,
117 unsigned int duty, unsigned int period)
118{
119 unsigned int pre_div, cnt, duty_cnt;
Jerome Brunetfd7b2be2017-06-08 14:24:16 +0200120 unsigned long fin_freq = -1;
121 u64 fin_ps;
Neil Armstrong211ed632016-08-22 17:36:30 +0200122
123 if (~(meson->inverter_mask >> id) & 0x1)
124 duty = period - duty;
125
126 if (period == channel->state.period &&
127 duty == channel->state.duty_cycle)
128 return 0;
129
130 fin_freq = clk_get_rate(channel->clk);
131 if (fin_freq == 0) {
132 dev_err(meson->chip.dev, "invalid source clock frequency\n");
133 return -EINVAL;
134 }
135
136 dev_dbg(meson->chip.dev, "fin_freq: %lu Hz\n", fin_freq);
Jerome Brunetfd7b2be2017-06-08 14:24:16 +0200137 fin_ps = (u64)NSEC_PER_SEC * 1000;
138 do_div(fin_ps, fin_freq);
Neil Armstrong211ed632016-08-22 17:36:30 +0200139
140 /* Calc pre_div with the period */
Martin Blumenstingl51496e42019-04-01 20:18:16 +0200141 for (pre_div = 0; pre_div <= MISC_CLK_DIV_MASK; pre_div++) {
Jerome Brunetfd7b2be2017-06-08 14:24:16 +0200142 cnt = DIV_ROUND_CLOSEST_ULL((u64)period * 1000,
143 fin_ps * (pre_div + 1));
144 dev_dbg(meson->chip.dev, "fin_ps=%llu pre_div=%u cnt=%u\n",
145 fin_ps, pre_div, cnt);
Neil Armstrong211ed632016-08-22 17:36:30 +0200146 if (cnt <= 0xffff)
147 break;
148 }
149
Martin Blumenstingl51496e42019-04-01 20:18:16 +0200150 if (pre_div > MISC_CLK_DIV_MASK) {
Neil Armstrong211ed632016-08-22 17:36:30 +0200151 dev_err(meson->chip.dev, "unable to get period pre_div\n");
152 return -EINVAL;
153 }
154
155 dev_dbg(meson->chip.dev, "period=%u pre_div=%u cnt=%u\n", period,
156 pre_div, cnt);
157
158 if (duty == period) {
159 channel->pre_div = pre_div;
160 channel->hi = cnt;
161 channel->lo = 0;
162 } else if (duty == 0) {
163 channel->pre_div = pre_div;
164 channel->hi = 0;
165 channel->lo = cnt;
166 } else {
167 /* Then check is we can have the duty with the same pre_div */
Jerome Brunetfd7b2be2017-06-08 14:24:16 +0200168 duty_cnt = DIV_ROUND_CLOSEST_ULL((u64)duty * 1000,
169 fin_ps * (pre_div + 1));
Neil Armstrong211ed632016-08-22 17:36:30 +0200170 if (duty_cnt > 0xffff) {
171 dev_err(meson->chip.dev, "unable to get duty cycle\n");
172 return -EINVAL;
173 }
174
175 dev_dbg(meson->chip.dev, "duty=%u pre_div=%u duty_cnt=%u\n",
176 duty, pre_div, duty_cnt);
177
178 channel->pre_div = pre_div;
179 channel->hi = duty_cnt;
180 channel->lo = cnt - duty_cnt;
181 }
182
183 return 0;
184}
185
Martin Blumenstingl084f1372019-06-12 21:58:58 +0200186static void meson_pwm_enable(struct meson_pwm *meson, struct pwm_device *pwm)
Neil Armstrong211ed632016-08-22 17:36:30 +0200187{
Martin Blumenstingl084f1372019-06-12 21:58:58 +0200188 struct meson_pwm_channel *channel = pwm_get_chip_data(pwm);
Neil Armstrong211ed632016-08-22 17:36:30 +0200189 u32 value, clk_shift, clk_enable, enable;
190 unsigned int offset;
Martin Blumenstinglf1737472019-04-01 19:57:48 +0200191 unsigned long flags;
Neil Armstrong211ed632016-08-22 17:36:30 +0200192
Martin Blumenstingl084f1372019-06-12 21:58:58 +0200193 switch (pwm->hwpwm) {
Neil Armstrong211ed632016-08-22 17:36:30 +0200194 case 0:
195 clk_shift = MISC_A_CLK_DIV_SHIFT;
196 clk_enable = MISC_A_CLK_EN;
197 enable = MISC_A_EN;
198 offset = REG_PWM_A;
199 break;
200
201 case 1:
202 clk_shift = MISC_B_CLK_DIV_SHIFT;
203 clk_enable = MISC_B_CLK_EN;
204 enable = MISC_B_EN;
205 offset = REG_PWM_B;
206 break;
Arnd Bergmann2fbc4872016-09-06 14:50:47 +0200207
208 default:
209 return;
Neil Armstrong211ed632016-08-22 17:36:30 +0200210 }
211
Martin Blumenstinglf1737472019-04-01 19:57:48 +0200212 spin_lock_irqsave(&meson->lock, flags);
213
Neil Armstrong211ed632016-08-22 17:36:30 +0200214 value = readl(meson->base + REG_MISC_AB);
215 value &= ~(MISC_CLK_DIV_MASK << clk_shift);
216 value |= channel->pre_div << clk_shift;
217 value |= clk_enable;
218 writel(value, meson->base + REG_MISC_AB);
219
220 value = (channel->hi << PWM_HIGH_SHIFT) | channel->lo;
221 writel(value, meson->base + offset);
222
223 value = readl(meson->base + REG_MISC_AB);
224 value |= enable;
225 writel(value, meson->base + REG_MISC_AB);
Martin Blumenstinglf1737472019-04-01 19:57:48 +0200226
227 spin_unlock_irqrestore(&meson->lock, flags);
Neil Armstrong211ed632016-08-22 17:36:30 +0200228}
229
Martin Blumenstingl084f1372019-06-12 21:58:58 +0200230static void meson_pwm_disable(struct meson_pwm *meson, struct pwm_device *pwm)
Neil Armstrong211ed632016-08-22 17:36:30 +0200231{
232 u32 value, enable;
Martin Blumenstinglf1737472019-04-01 19:57:48 +0200233 unsigned long flags;
Neil Armstrong211ed632016-08-22 17:36:30 +0200234
Martin Blumenstingl084f1372019-06-12 21:58:58 +0200235 switch (pwm->hwpwm) {
Neil Armstrong211ed632016-08-22 17:36:30 +0200236 case 0:
237 enable = MISC_A_EN;
238 break;
239
240 case 1:
241 enable = MISC_B_EN;
242 break;
Arnd Bergmann2fbc4872016-09-06 14:50:47 +0200243
244 default:
245 return;
Neil Armstrong211ed632016-08-22 17:36:30 +0200246 }
247
Martin Blumenstinglf1737472019-04-01 19:57:48 +0200248 spin_lock_irqsave(&meson->lock, flags);
249
Neil Armstrong211ed632016-08-22 17:36:30 +0200250 value = readl(meson->base + REG_MISC_AB);
251 value &= ~enable;
252 writel(value, meson->base + REG_MISC_AB);
Martin Blumenstinglf1737472019-04-01 19:57:48 +0200253
254 spin_unlock_irqrestore(&meson->lock, flags);
Neil Armstrong211ed632016-08-22 17:36:30 +0200255}
256
257static int meson_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm,
258 struct pwm_state *state)
259{
260 struct meson_pwm_channel *channel = pwm_get_chip_data(pwm);
261 struct meson_pwm *meson = to_meson_pwm(chip);
Neil Armstrong211ed632016-08-22 17:36:30 +0200262 int err = 0;
263
264 if (!state)
265 return -EINVAL;
266
Neil Armstrong211ed632016-08-22 17:36:30 +0200267 if (!state->enabled) {
Martin Blumenstingl084f1372019-06-12 21:58:58 +0200268 meson_pwm_disable(meson, pwm);
Neil Armstrong211ed632016-08-22 17:36:30 +0200269 channel->state.enabled = false;
270
Martin Blumenstinglf1737472019-04-01 19:57:48 +0200271 return 0;
Neil Armstrong211ed632016-08-22 17:36:30 +0200272 }
273
274 if (state->period != channel->state.period ||
275 state->duty_cycle != channel->state.duty_cycle ||
276 state->polarity != channel->state.polarity) {
Neil Armstrong211ed632016-08-22 17:36:30 +0200277 if (state->polarity != channel->state.polarity) {
278 if (state->polarity == PWM_POLARITY_NORMAL)
279 meson->inverter_mask |= BIT(pwm->hwpwm);
280 else
281 meson->inverter_mask &= ~BIT(pwm->hwpwm);
282 }
283
284 err = meson_pwm_calc(meson, channel, pwm->hwpwm,
285 state->duty_cycle, state->period);
286 if (err < 0)
Martin Blumenstinglf1737472019-04-01 19:57:48 +0200287 return err;
Neil Armstrong211ed632016-08-22 17:36:30 +0200288
289 channel->state.polarity = state->polarity;
290 channel->state.period = state->period;
291 channel->state.duty_cycle = state->duty_cycle;
292 }
293
294 if (state->enabled && !channel->state.enabled) {
Martin Blumenstingl084f1372019-06-12 21:58:58 +0200295 meson_pwm_enable(meson, pwm);
Neil Armstrong211ed632016-08-22 17:36:30 +0200296 channel->state.enabled = true;
297 }
298
Martin Blumenstinglf1737472019-04-01 19:57:48 +0200299 return 0;
Neil Armstrong211ed632016-08-22 17:36:30 +0200300}
301
302static void meson_pwm_get_state(struct pwm_chip *chip, struct pwm_device *pwm,
303 struct pwm_state *state)
304{
305 struct meson_pwm *meson = to_meson_pwm(chip);
306 u32 value, mask;
307
308 if (!state)
309 return;
310
311 switch (pwm->hwpwm) {
312 case 0:
313 mask = MISC_A_EN;
314 break;
315
316 case 1:
317 mask = MISC_B_EN;
318 break;
Arnd Bergmann2fbc4872016-09-06 14:50:47 +0200319
320 default:
321 return;
Neil Armstrong211ed632016-08-22 17:36:30 +0200322 }
323
324 value = readl(meson->base + REG_MISC_AB);
325 state->enabled = (value & mask) != 0;
326}
327
328static const struct pwm_ops meson_pwm_ops = {
329 .request = meson_pwm_request,
330 .free = meson_pwm_free,
331 .apply = meson_pwm_apply,
332 .get_state = meson_pwm_get_state,
333 .owner = THIS_MODULE,
334};
335
336static const char * const pwm_meson8b_parent_names[] = {
337 "xtal", "vid_pll", "fclk_div4", "fclk_div3"
338};
339
340static const struct meson_pwm_data pwm_meson8b_data = {
341 .parent_names = pwm_meson8b_parent_names,
Jerome Brunetd396b202017-06-08 14:24:15 +0200342 .num_parents = ARRAY_SIZE(pwm_meson8b_parent_names),
Neil Armstrong211ed632016-08-22 17:36:30 +0200343};
344
345static const char * const pwm_gxbb_parent_names[] = {
346 "xtal", "hdmi_pll", "fclk_div4", "fclk_div3"
347};
348
349static const struct meson_pwm_data pwm_gxbb_data = {
350 .parent_names = pwm_gxbb_parent_names,
Jerome Brunetd396b202017-06-08 14:24:15 +0200351 .num_parents = ARRAY_SIZE(pwm_gxbb_parent_names),
352};
353
354/*
355 * Only the 2 first inputs of the GXBB AO PWMs are valid
356 * The last 2 are grounded
357 */
358static const char * const pwm_gxbb_ao_parent_names[] = {
359 "xtal", "clk81"
360};
361
362static const struct meson_pwm_data pwm_gxbb_ao_data = {
363 .parent_names = pwm_gxbb_ao_parent_names,
364 .num_parents = ARRAY_SIZE(pwm_gxbb_ao_parent_names),
Neil Armstrong211ed632016-08-22 17:36:30 +0200365};
366
Jian Hubccaa3f2017-12-04 14:00:17 +0800367static const char * const pwm_axg_ee_parent_names[] = {
368 "xtal", "fclk_div5", "fclk_div4", "fclk_div3"
369};
370
371static const struct meson_pwm_data pwm_axg_ee_data = {
372 .parent_names = pwm_axg_ee_parent_names,
373 .num_parents = ARRAY_SIZE(pwm_axg_ee_parent_names),
374};
375
376static const char * const pwm_axg_ao_parent_names[] = {
377 "aoclk81", "xtal", "fclk_div4", "fclk_div5"
378};
379
380static const struct meson_pwm_data pwm_axg_ao_data = {
381 .parent_names = pwm_axg_ao_parent_names,
382 .num_parents = ARRAY_SIZE(pwm_axg_ao_parent_names),
383};
384
Neil Armstrong9bce02e2019-06-20 16:46:55 +0200385static const char * const pwm_g12a_ao_ab_parent_names[] = {
386 "xtal", "aoclk81", "fclk_div4", "fclk_div5"
387};
388
389static const struct meson_pwm_data pwm_g12a_ao_ab_data = {
390 .parent_names = pwm_g12a_ao_ab_parent_names,
391 .num_parents = ARRAY_SIZE(pwm_g12a_ao_ab_parent_names),
392};
393
Neil Armstrongf41efce2019-04-23 15:36:45 +0200394static const char * const pwm_g12a_ao_cd_parent_names[] = {
Neil Armstrong9bce02e2019-06-20 16:46:55 +0200395 "xtal", "aoclk81",
Neil Armstrongf41efce2019-04-23 15:36:45 +0200396};
397
398static const struct meson_pwm_data pwm_g12a_ao_cd_data = {
399 .parent_names = pwm_g12a_ao_cd_parent_names,
400 .num_parents = ARRAY_SIZE(pwm_g12a_ao_cd_parent_names),
401};
402
403static const char * const pwm_g12a_ee_parent_names[] = {
404 "xtal", "hdmi_pll", "fclk_div4", "fclk_div3"
405};
406
407static const struct meson_pwm_data pwm_g12a_ee_data = {
408 .parent_names = pwm_g12a_ee_parent_names,
409 .num_parents = ARRAY_SIZE(pwm_g12a_ee_parent_names),
410};
411
Neil Armstrong211ed632016-08-22 17:36:30 +0200412static const struct of_device_id meson_pwm_matches[] = {
Jerome Brunetd396b202017-06-08 14:24:15 +0200413 {
414 .compatible = "amlogic,meson8b-pwm",
415 .data = &pwm_meson8b_data
416 },
417 {
418 .compatible = "amlogic,meson-gxbb-pwm",
419 .data = &pwm_gxbb_data
420 },
421 {
422 .compatible = "amlogic,meson-gxbb-ao-pwm",
423 .data = &pwm_gxbb_ao_data
424 },
Jian Hubccaa3f2017-12-04 14:00:17 +0800425 {
426 .compatible = "amlogic,meson-axg-ee-pwm",
427 .data = &pwm_axg_ee_data
428 },
429 {
430 .compatible = "amlogic,meson-axg-ao-pwm",
431 .data = &pwm_axg_ao_data
432 },
Neil Armstrongf41efce2019-04-23 15:36:45 +0200433 {
434 .compatible = "amlogic,meson-g12a-ee-pwm",
435 .data = &pwm_g12a_ee_data
436 },
437 {
438 .compatible = "amlogic,meson-g12a-ao-pwm-ab",
Neil Armstrong9bce02e2019-06-20 16:46:55 +0200439 .data = &pwm_g12a_ao_ab_data
Neil Armstrongf41efce2019-04-23 15:36:45 +0200440 },
441 {
442 .compatible = "amlogic,meson-g12a-ao-pwm-cd",
443 .data = &pwm_g12a_ao_cd_data
444 },
Neil Armstrong211ed632016-08-22 17:36:30 +0200445 {},
446};
447MODULE_DEVICE_TABLE(of, meson_pwm_matches);
448
449static int meson_pwm_init_channels(struct meson_pwm *meson,
450 struct meson_pwm_channel *channels)
451{
452 struct device *dev = meson->chip.dev;
Neil Armstrong211ed632016-08-22 17:36:30 +0200453 struct clk_init_data init;
454 unsigned int i;
455 char name[255];
456 int err;
457
458 for (i = 0; i < meson->chip.npwm; i++) {
459 struct meson_pwm_channel *channel = &channels[i];
460
Jerome Brunetb96e9eb2018-08-01 12:57:20 +0200461 snprintf(name, sizeof(name), "%s#mux%u", dev_name(dev), i);
Neil Armstrong211ed632016-08-22 17:36:30 +0200462
463 init.name = name;
464 init.ops = &clk_mux_ops;
Stephen Boyd90b6c5c2019-04-25 10:57:37 -0700465 init.flags = 0;
Neil Armstrong211ed632016-08-22 17:36:30 +0200466 init.parent_names = meson->data->parent_names;
Jerome Brunetd396b202017-06-08 14:24:15 +0200467 init.num_parents = meson->data->num_parents;
Neil Armstrong211ed632016-08-22 17:36:30 +0200468
469 channel->mux.reg = meson->base + REG_MISC_AB;
470 channel->mux.shift = mux_reg_shifts[i];
471 channel->mux.mask = BIT(MISC_CLK_SEL_WIDTH) - 1;
472 channel->mux.flags = 0;
473 channel->mux.lock = &meson->lock;
474 channel->mux.table = NULL;
475 channel->mux.hw.init = &init;
476
477 channel->clk = devm_clk_register(dev, &channel->mux.hw);
478 if (IS_ERR(channel->clk)) {
479 err = PTR_ERR(channel->clk);
480 dev_err(dev, "failed to register %s: %d\n", name, err);
481 return err;
482 }
483
484 snprintf(name, sizeof(name), "clkin%u", i);
485
Martin Blumenstinglba4004c2019-06-12 21:58:59 +0200486 channel->clk_parent = devm_clk_get_optional(dev, name);
487 if (IS_ERR(channel->clk_parent))
488 return PTR_ERR(channel->clk_parent);
Neil Armstrong211ed632016-08-22 17:36:30 +0200489 }
490
491 return 0;
492}
493
494static void meson_pwm_add_channels(struct meson_pwm *meson,
495 struct meson_pwm_channel *channels)
496{
497 unsigned int i;
498
499 for (i = 0; i < meson->chip.npwm; i++)
500 pwm_set_chip_data(&meson->chip.pwms[i], &channels[i]);
501}
502
503static int meson_pwm_probe(struct platform_device *pdev)
504{
505 struct meson_pwm_channel *channels;
506 struct meson_pwm *meson;
507 struct resource *regs;
508 int err;
509
510 meson = devm_kzalloc(&pdev->dev, sizeof(*meson), GFP_KERNEL);
511 if (!meson)
512 return -ENOMEM;
513
514 regs = platform_get_resource(pdev, IORESOURCE_MEM, 0);
515 meson->base = devm_ioremap_resource(&pdev->dev, regs);
516 if (IS_ERR(meson->base))
517 return PTR_ERR(meson->base);
518
Axel Linc6999952016-09-10 09:55:49 +0800519 spin_lock_init(&meson->lock);
Neil Armstrong211ed632016-08-22 17:36:30 +0200520 meson->chip.dev = &pdev->dev;
521 meson->chip.ops = &meson_pwm_ops;
522 meson->chip.base = -1;
523 meson->chip.npwm = 2;
524 meson->chip.of_xlate = of_pwm_xlate_with_flags;
525 meson->chip.of_pwm_n_cells = 3;
526
527 meson->data = of_device_get_match_data(&pdev->dev);
528 meson->inverter_mask = BIT(meson->chip.npwm) - 1;
529
Martin Blumenstingl735596c2018-04-28 23:25:21 +0200530 channels = devm_kcalloc(&pdev->dev, meson->chip.npwm,
531 sizeof(*channels), GFP_KERNEL);
Neil Armstrong211ed632016-08-22 17:36:30 +0200532 if (!channels)
533 return -ENOMEM;
534
535 err = meson_pwm_init_channels(meson, channels);
536 if (err < 0)
537 return err;
538
539 err = pwmchip_add(&meson->chip);
540 if (err < 0) {
541 dev_err(&pdev->dev, "failed to register PWM chip: %d\n", err);
542 return err;
543 }
544
545 meson_pwm_add_channels(meson, channels);
546
547 platform_set_drvdata(pdev, meson);
548
549 return 0;
550}
551
552static int meson_pwm_remove(struct platform_device *pdev)
553{
554 struct meson_pwm *meson = platform_get_drvdata(pdev);
555
556 return pwmchip_remove(&meson->chip);
557}
558
559static struct platform_driver meson_pwm_driver = {
560 .driver = {
561 .name = "meson-pwm",
562 .of_match_table = meson_pwm_matches,
563 },
564 .probe = meson_pwm_probe,
565 .remove = meson_pwm_remove,
566};
567module_platform_driver(meson_pwm_driver);
568
Neil Armstrong211ed632016-08-22 17:36:30 +0200569MODULE_DESCRIPTION("Amlogic Meson PWM Generator driver");
570MODULE_AUTHOR("Neil Armstrong <narmstrong@baylibre.com>");
571MODULE_LICENSE("Dual BSD/GPL");