]>
Commit | Line | Data |
---|---|---|
841e6f90 AA |
1 | /* |
2 | * NXP LPC18xx State Configurable Timer - Pulse Width Modulator driver | |
3 | * | |
4 | * Copyright (c) 2015 Ariel D'Alessandro <[email protected]> | |
5 | * | |
6 | * This program is free software; you can redistribute it and/or modify | |
7 | * it under the terms of the GNU General Public License as published by | |
8 | * the Free Software Foundation; either version 2 of the License. | |
9 | * | |
10 | * Notes | |
11 | * ===== | |
12 | * NXP LPC18xx provides a State Configurable Timer (SCT) which can be configured | |
13 | * as a Pulse Width Modulator. | |
14 | * | |
15 | * SCT supports 16 outputs, 16 events and 16 registers. Each event will be | |
16 | * triggered when its related register matches the SCT counter value, and it | |
17 | * will set or clear a selected output. | |
18 | * | |
19 | * One of the events is preselected to generate the period, thus the maximum | |
20 | * number of simultaneous channels is limited to 15. Notice that period is | |
21 | * global to all the channels, thus PWM driver will refuse setting different | |
22 | * values to it, unless there's only one channel requested. | |
23 | */ | |
24 | ||
25 | #include <linux/clk.h> | |
26 | #include <linux/err.h> | |
27 | #include <linux/io.h> | |
28 | #include <linux/module.h> | |
29 | #include <linux/platform_device.h> | |
30 | #include <linux/pwm.h> | |
31 | ||
32 | /* LPC18xx SCT registers */ | |
33 | #define LPC18XX_PWM_CONFIG 0x000 | |
34 | #define LPC18XX_PWM_CONFIG_UNIFY BIT(0) | |
35 | #define LPC18XX_PWM_CONFIG_NORELOAD BIT(7) | |
36 | ||
37 | #define LPC18XX_PWM_CTRL 0x004 | |
38 | #define LPC18XX_PWM_CTRL_HALT BIT(2) | |
39 | #define LPC18XX_PWM_BIDIR BIT(4) | |
40 | #define LPC18XX_PWM_PRE_SHIFT 5 | |
41 | #define LPC18XX_PWM_PRE_MASK (0xff << LPC18XX_PWM_PRE_SHIFT) | |
42 | #define LPC18XX_PWM_PRE(x) (x << LPC18XX_PWM_PRE_SHIFT) | |
43 | ||
44 | #define LPC18XX_PWM_LIMIT 0x008 | |
45 | ||
46 | #define LPC18XX_PWM_RES_BASE 0x058 | |
47 | #define LPC18XX_PWM_RES_SHIFT(_ch) (_ch * 2) | |
48 | #define LPC18XX_PWM_RES(_ch, _action) (_action << LPC18XX_PWM_RES_SHIFT(_ch)) | |
49 | #define LPC18XX_PWM_RES_MASK(_ch) (0x3 << LPC18XX_PWM_RES_SHIFT(_ch)) | |
50 | ||
51 | #define LPC18XX_PWM_MATCH_BASE 0x100 | |
52 | #define LPC18XX_PWM_MATCH(_ch) (LPC18XX_PWM_MATCH_BASE + _ch * 4) | |
53 | ||
54 | #define LPC18XX_PWM_MATCHREL_BASE 0x200 | |
55 | #define LPC18XX_PWM_MATCHREL(_ch) (LPC18XX_PWM_MATCHREL_BASE + _ch * 4) | |
56 | ||
57 | #define LPC18XX_PWM_EVSTATEMSK_BASE 0x300 | |
58 | #define LPC18XX_PWM_EVSTATEMSK(_ch) (LPC18XX_PWM_EVSTATEMSK_BASE + _ch * 8) | |
59 | #define LPC18XX_PWM_EVSTATEMSK_ALL 0xffffffff | |
60 | ||
61 | #define LPC18XX_PWM_EVCTRL_BASE 0x304 | |
62 | #define LPC18XX_PWM_EVCTRL(_ev) (LPC18XX_PWM_EVCTRL_BASE + _ev * 8) | |
63 | ||
64 | #define LPC18XX_PWM_EVCTRL_MATCH(_ch) _ch | |
65 | ||
66 | #define LPC18XX_PWM_EVCTRL_COMB_SHIFT 12 | |
67 | #define LPC18XX_PWM_EVCTRL_COMB_MATCH (0x1 << LPC18XX_PWM_EVCTRL_COMB_SHIFT) | |
68 | ||
69 | #define LPC18XX_PWM_OUTPUTSET_BASE 0x500 | |
70 | #define LPC18XX_PWM_OUTPUTSET(_ch) (LPC18XX_PWM_OUTPUTSET_BASE + _ch * 8) | |
71 | ||
72 | #define LPC18XX_PWM_OUTPUTCL_BASE 0x504 | |
73 | #define LPC18XX_PWM_OUTPUTCL(_ch) (LPC18XX_PWM_OUTPUTCL_BASE + _ch * 8) | |
74 | ||
75 | /* LPC18xx SCT unified counter */ | |
76 | #define LPC18XX_PWM_TIMER_MAX 0xffffffff | |
77 | ||
78 | /* LPC18xx SCT events */ | |
79 | #define LPC18XX_PWM_EVENT_PERIOD 0 | |
80 | #define LPC18XX_PWM_EVENT_MAX 16 | |
81 | ||
82 | /* SCT conflict resolution */ | |
83 | enum lpc18xx_pwm_res_action { | |
84 | LPC18XX_PWM_RES_NONE, | |
85 | LPC18XX_PWM_RES_SET, | |
86 | LPC18XX_PWM_RES_CLEAR, | |
87 | LPC18XX_PWM_RES_TOGGLE, | |
88 | }; | |
89 | ||
90 | struct lpc18xx_pwm_data { | |
91 | unsigned int duty_event; | |
92 | }; | |
93 | ||
94 | struct lpc18xx_pwm_chip { | |
95 | struct device *dev; | |
96 | struct pwm_chip chip; | |
97 | void __iomem *base; | |
98 | struct clk *pwm_clk; | |
99 | unsigned long clk_rate; | |
100 | unsigned int period_ns; | |
101 | unsigned int min_period_ns; | |
102 | unsigned int max_period_ns; | |
103 | unsigned int period_event; | |
104 | unsigned long event_map; | |
105 | struct mutex res_lock; | |
106 | struct mutex period_lock; | |
107 | }; | |
108 | ||
109 | static inline struct lpc18xx_pwm_chip * | |
110 | to_lpc18xx_pwm_chip(struct pwm_chip *chip) | |
111 | { | |
112 | return container_of(chip, struct lpc18xx_pwm_chip, chip); | |
113 | } | |
114 | ||
115 | static inline void lpc18xx_pwm_writel(struct lpc18xx_pwm_chip *lpc18xx_pwm, | |
116 | u32 reg, u32 val) | |
117 | { | |
118 | writel(val, lpc18xx_pwm->base + reg); | |
119 | } | |
120 | ||
121 | static inline u32 lpc18xx_pwm_readl(struct lpc18xx_pwm_chip *lpc18xx_pwm, | |
122 | u32 reg) | |
123 | { | |
124 | return readl(lpc18xx_pwm->base + reg); | |
125 | } | |
126 | ||
127 | static void lpc18xx_pwm_set_conflict_res(struct lpc18xx_pwm_chip *lpc18xx_pwm, | |
128 | struct pwm_device *pwm, | |
129 | enum lpc18xx_pwm_res_action action) | |
130 | { | |
131 | u32 val; | |
132 | ||
133 | mutex_lock(&lpc18xx_pwm->res_lock); | |
134 | ||
135 | /* | |
136 | * Simultaneous set and clear may happen on an output, that is the case | |
137 | * when duty_ns == period_ns. LPC18xx SCT allows to set a conflict | |
138 | * resolution action to be taken in such a case. | |
139 | */ | |
140 | val = lpc18xx_pwm_readl(lpc18xx_pwm, LPC18XX_PWM_RES_BASE); | |
141 | val &= ~LPC18XX_PWM_RES_MASK(pwm->hwpwm); | |
142 | val |= LPC18XX_PWM_RES(pwm->hwpwm, action); | |
143 | lpc18xx_pwm_writel(lpc18xx_pwm, LPC18XX_PWM_RES_BASE, val); | |
144 | ||
145 | mutex_unlock(&lpc18xx_pwm->res_lock); | |
146 | } | |
147 | ||
148 | static void lpc18xx_pwm_config_period(struct pwm_chip *chip, int period_ns) | |
149 | { | |
150 | struct lpc18xx_pwm_chip *lpc18xx_pwm = to_lpc18xx_pwm_chip(chip); | |
151 | u64 val; | |
152 | ||
153 | val = (u64)period_ns * lpc18xx_pwm->clk_rate; | |
154 | do_div(val, NSEC_PER_SEC); | |
155 | ||
156 | lpc18xx_pwm_writel(lpc18xx_pwm, | |
157 | LPC18XX_PWM_MATCH(lpc18xx_pwm->period_event), | |
158 | (u32)val - 1); | |
159 | ||
160 | lpc18xx_pwm_writel(lpc18xx_pwm, | |
161 | LPC18XX_PWM_MATCHREL(lpc18xx_pwm->period_event), | |
162 | (u32)val - 1); | |
163 | } | |
164 | ||
165 | static void lpc18xx_pwm_config_duty(struct pwm_chip *chip, | |
166 | struct pwm_device *pwm, int duty_ns) | |
167 | { | |
168 | struct lpc18xx_pwm_chip *lpc18xx_pwm = to_lpc18xx_pwm_chip(chip); | |
169 | struct lpc18xx_pwm_data *lpc18xx_data = pwm_get_chip_data(pwm); | |
170 | u64 val; | |
171 | ||
172 | val = (u64)duty_ns * lpc18xx_pwm->clk_rate; | |
173 | do_div(val, NSEC_PER_SEC); | |
174 | ||
175 | lpc18xx_pwm_writel(lpc18xx_pwm, | |
176 | LPC18XX_PWM_MATCH(lpc18xx_data->duty_event), | |
177 | (u32)val); | |
178 | ||
179 | lpc18xx_pwm_writel(lpc18xx_pwm, | |
180 | LPC18XX_PWM_MATCHREL(lpc18xx_data->duty_event), | |
181 | (u32)val); | |
182 | } | |
183 | ||
184 | static int lpc18xx_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm, | |
185 | int duty_ns, int period_ns) | |
186 | { | |
187 | struct lpc18xx_pwm_chip *lpc18xx_pwm = to_lpc18xx_pwm_chip(chip); | |
188 | int requested_events, i; | |
189 | ||
190 | if (period_ns < lpc18xx_pwm->min_period_ns || | |
191 | period_ns > lpc18xx_pwm->max_period_ns) { | |
192 | dev_err(chip->dev, "period %d not in range\n", period_ns); | |
193 | return -ERANGE; | |
194 | } | |
195 | ||
196 | mutex_lock(&lpc18xx_pwm->period_lock); | |
197 | ||
198 | requested_events = bitmap_weight(&lpc18xx_pwm->event_map, | |
199 | LPC18XX_PWM_EVENT_MAX); | |
200 | ||
201 | /* | |
202 | * The PWM supports only a single period for all PWM channels. | |
203 | * Once the period is set, it can only be changed if no more than one | |
204 | * channel is requested at that moment. | |
205 | */ | |
206 | if (requested_events > 2 && lpc18xx_pwm->period_ns != period_ns && | |
207 | lpc18xx_pwm->period_ns) { | |
208 | dev_err(chip->dev, "conflicting period requested for PWM %u\n", | |
209 | pwm->hwpwm); | |
210 | mutex_unlock(&lpc18xx_pwm->period_lock); | |
211 | return -EBUSY; | |
212 | } | |
213 | ||
214 | if ((requested_events <= 2 && lpc18xx_pwm->period_ns != period_ns) || | |
215 | !lpc18xx_pwm->period_ns) { | |
216 | lpc18xx_pwm->period_ns = period_ns; | |
217 | for (i = 0; i < chip->npwm; i++) | |
218 | pwm_set_period(&chip->pwms[i], period_ns); | |
219 | lpc18xx_pwm_config_period(chip, period_ns); | |
220 | } | |
221 | ||
222 | mutex_unlock(&lpc18xx_pwm->period_lock); | |
223 | ||
224 | lpc18xx_pwm_config_duty(chip, pwm, duty_ns); | |
225 | ||
226 | return 0; | |
227 | } | |
228 | ||
229 | static int lpc18xx_pwm_set_polarity(struct pwm_chip *chip, | |
230 | struct pwm_device *pwm, | |
231 | enum pwm_polarity polarity) | |
232 | { | |
233 | return 0; | |
234 | } | |
235 | ||
236 | static int lpc18xx_pwm_enable(struct pwm_chip *chip, struct pwm_device *pwm) | |
237 | { | |
238 | struct lpc18xx_pwm_chip *lpc18xx_pwm = to_lpc18xx_pwm_chip(chip); | |
239 | struct lpc18xx_pwm_data *lpc18xx_data = pwm_get_chip_data(pwm); | |
240 | enum lpc18xx_pwm_res_action res_action; | |
241 | unsigned int set_event, clear_event; | |
242 | ||
243 | lpc18xx_pwm_writel(lpc18xx_pwm, | |
244 | LPC18XX_PWM_EVCTRL(lpc18xx_data->duty_event), | |
245 | LPC18XX_PWM_EVCTRL_MATCH(lpc18xx_data->duty_event) | | |
246 | LPC18XX_PWM_EVCTRL_COMB_MATCH); | |
247 | ||
248 | lpc18xx_pwm_writel(lpc18xx_pwm, | |
249 | LPC18XX_PWM_EVSTATEMSK(lpc18xx_data->duty_event), | |
250 | LPC18XX_PWM_EVSTATEMSK_ALL); | |
251 | ||
4b58896f | 252 | if (pwm_get_polarity(pwm) == PWM_POLARITY_NORMAL) { |
841e6f90 AA |
253 | set_event = lpc18xx_pwm->period_event; |
254 | clear_event = lpc18xx_data->duty_event; | |
255 | res_action = LPC18XX_PWM_RES_SET; | |
256 | } else { | |
257 | set_event = lpc18xx_data->duty_event; | |
258 | clear_event = lpc18xx_pwm->period_event; | |
259 | res_action = LPC18XX_PWM_RES_CLEAR; | |
260 | } | |
261 | ||
262 | lpc18xx_pwm_writel(lpc18xx_pwm, LPC18XX_PWM_OUTPUTSET(pwm->hwpwm), | |
263 | BIT(set_event)); | |
264 | lpc18xx_pwm_writel(lpc18xx_pwm, LPC18XX_PWM_OUTPUTCL(pwm->hwpwm), | |
265 | BIT(clear_event)); | |
266 | lpc18xx_pwm_set_conflict_res(lpc18xx_pwm, pwm, res_action); | |
267 | ||
268 | return 0; | |
269 | } | |
270 | ||
271 | static void lpc18xx_pwm_disable(struct pwm_chip *chip, struct pwm_device *pwm) | |
272 | { | |
273 | struct lpc18xx_pwm_chip *lpc18xx_pwm = to_lpc18xx_pwm_chip(chip); | |
274 | struct lpc18xx_pwm_data *lpc18xx_data = pwm_get_chip_data(pwm); | |
275 | ||
276 | lpc18xx_pwm_writel(lpc18xx_pwm, | |
277 | LPC18XX_PWM_EVCTRL(lpc18xx_data->duty_event), 0); | |
278 | lpc18xx_pwm_writel(lpc18xx_pwm, LPC18XX_PWM_OUTPUTSET(pwm->hwpwm), 0); | |
279 | lpc18xx_pwm_writel(lpc18xx_pwm, LPC18XX_PWM_OUTPUTCL(pwm->hwpwm), 0); | |
280 | } | |
281 | ||
282 | static int lpc18xx_pwm_request(struct pwm_chip *chip, struct pwm_device *pwm) | |
283 | { | |
284 | struct lpc18xx_pwm_chip *lpc18xx_pwm = to_lpc18xx_pwm_chip(chip); | |
285 | struct lpc18xx_pwm_data *lpc18xx_data = pwm_get_chip_data(pwm); | |
286 | unsigned long event; | |
287 | ||
288 | event = find_first_zero_bit(&lpc18xx_pwm->event_map, | |
289 | LPC18XX_PWM_EVENT_MAX); | |
290 | ||
291 | if (event >= LPC18XX_PWM_EVENT_MAX) { | |
292 | dev_err(lpc18xx_pwm->dev, | |
293 | "maximum number of simultaneous channels reached\n"); | |
294 | return -EBUSY; | |
295 | }; | |
296 | ||
297 | set_bit(event, &lpc18xx_pwm->event_map); | |
298 | lpc18xx_data->duty_event = event; | |
299 | lpc18xx_pwm_config_duty(chip, pwm, pwm_get_duty_cycle(pwm)); | |
300 | ||
301 | return 0; | |
302 | } | |
303 | ||
304 | static void lpc18xx_pwm_free(struct pwm_chip *chip, struct pwm_device *pwm) | |
305 | { | |
306 | struct lpc18xx_pwm_chip *lpc18xx_pwm = to_lpc18xx_pwm_chip(chip); | |
307 | struct lpc18xx_pwm_data *lpc18xx_data = pwm_get_chip_data(pwm); | |
308 | ||
309 | pwm_disable(pwm); | |
310 | pwm_set_duty_cycle(pwm, 0); | |
311 | clear_bit(lpc18xx_data->duty_event, &lpc18xx_pwm->event_map); | |
312 | } | |
313 | ||
314 | static const struct pwm_ops lpc18xx_pwm_ops = { | |
315 | .config = lpc18xx_pwm_config, | |
316 | .set_polarity = lpc18xx_pwm_set_polarity, | |
317 | .enable = lpc18xx_pwm_enable, | |
318 | .disable = lpc18xx_pwm_disable, | |
319 | .request = lpc18xx_pwm_request, | |
320 | .free = lpc18xx_pwm_free, | |
321 | .owner = THIS_MODULE, | |
322 | }; | |
323 | ||
324 | static const struct of_device_id lpc18xx_pwm_of_match[] = { | |
325 | { .compatible = "nxp,lpc1850-sct-pwm" }, | |
326 | {} | |
327 | }; | |
328 | MODULE_DEVICE_TABLE(of, lpc18xx_pwm_of_match); | |
329 | ||
330 | static int lpc18xx_pwm_probe(struct platform_device *pdev) | |
331 | { | |
332 | struct lpc18xx_pwm_chip *lpc18xx_pwm; | |
333 | struct pwm_device *pwm; | |
334 | struct resource *res; | |
335 | int ret, i; | |
336 | u64 val; | |
337 | ||
338 | lpc18xx_pwm = devm_kzalloc(&pdev->dev, sizeof(*lpc18xx_pwm), | |
339 | GFP_KERNEL); | |
340 | if (!lpc18xx_pwm) | |
341 | return -ENOMEM; | |
342 | ||
343 | lpc18xx_pwm->dev = &pdev->dev; | |
344 | ||
345 | res = platform_get_resource(pdev, IORESOURCE_MEM, 0); | |
346 | lpc18xx_pwm->base = devm_ioremap_resource(&pdev->dev, res); | |
347 | if (IS_ERR(lpc18xx_pwm->base)) | |
348 | return PTR_ERR(lpc18xx_pwm->base); | |
349 | ||
350 | lpc18xx_pwm->pwm_clk = devm_clk_get(&pdev->dev, "pwm"); | |
351 | if (IS_ERR(lpc18xx_pwm->pwm_clk)) { | |
352 | dev_err(&pdev->dev, "failed to get pwm clock\n"); | |
353 | return PTR_ERR(lpc18xx_pwm->pwm_clk); | |
354 | } | |
355 | ||
356 | ret = clk_prepare_enable(lpc18xx_pwm->pwm_clk); | |
357 | if (ret < 0) { | |
358 | dev_err(&pdev->dev, "could not prepare or enable pwm clock\n"); | |
359 | return ret; | |
360 | } | |
361 | ||
362 | lpc18xx_pwm->clk_rate = clk_get_rate(lpc18xx_pwm->pwm_clk); | |
0e47b598 WS |
363 | if (!lpc18xx_pwm->clk_rate) { |
364 | dev_err(&pdev->dev, "pwm clock has no frequency\n"); | |
365 | ret = -EINVAL; | |
366 | goto disable_pwmclk; | |
367 | } | |
841e6f90 AA |
368 | |
369 | mutex_init(&lpc18xx_pwm->res_lock); | |
370 | mutex_init(&lpc18xx_pwm->period_lock); | |
371 | ||
372 | val = (u64)NSEC_PER_SEC * LPC18XX_PWM_TIMER_MAX; | |
373 | do_div(val, lpc18xx_pwm->clk_rate); | |
374 | lpc18xx_pwm->max_period_ns = val; | |
375 | ||
376 | lpc18xx_pwm->min_period_ns = DIV_ROUND_UP(NSEC_PER_SEC, | |
377 | lpc18xx_pwm->clk_rate); | |
378 | ||
379 | lpc18xx_pwm->chip.dev = &pdev->dev; | |
380 | lpc18xx_pwm->chip.ops = &lpc18xx_pwm_ops; | |
381 | lpc18xx_pwm->chip.base = -1; | |
382 | lpc18xx_pwm->chip.npwm = 16; | |
383 | lpc18xx_pwm->chip.of_xlate = of_pwm_xlate_with_flags; | |
384 | lpc18xx_pwm->chip.of_pwm_n_cells = 3; | |
385 | ||
386 | /* SCT counter must be in unify (32 bit) mode */ | |
387 | lpc18xx_pwm_writel(lpc18xx_pwm, LPC18XX_PWM_CONFIG, | |
388 | LPC18XX_PWM_CONFIG_UNIFY); | |
389 | ||
390 | /* | |
391 | * Everytime the timer counter reaches the period value, the related | |
392 | * event will be triggered and the counter reset to 0. | |
393 | */ | |
394 | set_bit(LPC18XX_PWM_EVENT_PERIOD, &lpc18xx_pwm->event_map); | |
395 | lpc18xx_pwm->period_event = LPC18XX_PWM_EVENT_PERIOD; | |
396 | ||
397 | lpc18xx_pwm_writel(lpc18xx_pwm, | |
398 | LPC18XX_PWM_EVSTATEMSK(lpc18xx_pwm->period_event), | |
399 | LPC18XX_PWM_EVSTATEMSK_ALL); | |
400 | ||
401 | val = LPC18XX_PWM_EVCTRL_MATCH(lpc18xx_pwm->period_event) | | |
402 | LPC18XX_PWM_EVCTRL_COMB_MATCH; | |
403 | lpc18xx_pwm_writel(lpc18xx_pwm, | |
404 | LPC18XX_PWM_EVCTRL(lpc18xx_pwm->period_event), val); | |
405 | ||
406 | lpc18xx_pwm_writel(lpc18xx_pwm, LPC18XX_PWM_LIMIT, | |
407 | BIT(lpc18xx_pwm->period_event)); | |
408 | ||
409 | ret = pwmchip_add(&lpc18xx_pwm->chip); | |
410 | if (ret < 0) { | |
411 | dev_err(&pdev->dev, "pwmchip_add failed: %d\n", ret); | |
412 | goto disable_pwmclk; | |
413 | } | |
414 | ||
415 | for (i = 0; i < lpc18xx_pwm->chip.npwm; i++) { | |
fe0e2cf9 | 416 | struct lpc18xx_pwm_data *data; |
417 | ||
841e6f90 | 418 | pwm = &lpc18xx_pwm->chip.pwms[i]; |
fe0e2cf9 | 419 | |
420 | data = devm_kzalloc(lpc18xx_pwm->dev, sizeof(*data), | |
421 | GFP_KERNEL); | |
422 | if (!data) { | |
841e6f90 AA |
423 | ret = -ENOMEM; |
424 | goto remove_pwmchip; | |
425 | } | |
fe0e2cf9 | 426 | |
427 | pwm_set_chip_data(pwm, data); | |
841e6f90 AA |
428 | } |
429 | ||
430 | platform_set_drvdata(pdev, lpc18xx_pwm); | |
431 | ||
432 | val = lpc18xx_pwm_readl(lpc18xx_pwm, LPC18XX_PWM_CTRL); | |
433 | val &= ~LPC18XX_PWM_BIDIR; | |
434 | val &= ~LPC18XX_PWM_CTRL_HALT; | |
435 | val &= ~LPC18XX_PWM_PRE_MASK; | |
436 | val |= LPC18XX_PWM_PRE(0); | |
437 | lpc18xx_pwm_writel(lpc18xx_pwm, LPC18XX_PWM_CTRL, val); | |
438 | ||
439 | return 0; | |
440 | ||
441 | remove_pwmchip: | |
442 | pwmchip_remove(&lpc18xx_pwm->chip); | |
443 | disable_pwmclk: | |
444 | clk_disable_unprepare(lpc18xx_pwm->pwm_clk); | |
445 | return ret; | |
446 | } | |
447 | ||
448 | static int lpc18xx_pwm_remove(struct platform_device *pdev) | |
449 | { | |
450 | struct lpc18xx_pwm_chip *lpc18xx_pwm = platform_get_drvdata(pdev); | |
451 | u32 val; | |
452 | ||
453 | val = lpc18xx_pwm_readl(lpc18xx_pwm, LPC18XX_PWM_CTRL); | |
454 | lpc18xx_pwm_writel(lpc18xx_pwm, LPC18XX_PWM_CTRL, | |
455 | val | LPC18XX_PWM_CTRL_HALT); | |
456 | ||
457 | clk_disable_unprepare(lpc18xx_pwm->pwm_clk); | |
458 | ||
459 | return pwmchip_remove(&lpc18xx_pwm->chip); | |
460 | } | |
461 | ||
462 | static struct platform_driver lpc18xx_pwm_driver = { | |
463 | .driver = { | |
464 | .name = "lpc18xx-sct-pwm", | |
465 | .of_match_table = lpc18xx_pwm_of_match, | |
466 | }, | |
467 | .probe = lpc18xx_pwm_probe, | |
468 | .remove = lpc18xx_pwm_remove, | |
469 | }; | |
470 | module_platform_driver(lpc18xx_pwm_driver); | |
471 | ||
472 | MODULE_AUTHOR("Ariel D'Alessandro <[email protected]>"); | |
473 | MODULE_DESCRIPTION("NXP LPC18xx PWM driver"); | |
474 | MODULE_LICENSE("GPL v2"); |