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