diff options
author | Philip, Avinash <avinashphilip@ti.com> | 2012-07-25 07:28:19 -0400 |
---|---|---|
committer | Thierry Reding <thierry.reding@avionic-design.de> | 2012-07-26 01:45:20 -0400 |
commit | 19891b20e7c275feb92d669f4b1879861f7e8c25 (patch) | |
tree | 9b7e6421be6209a1e7666f90f2b269860bffdaa5 /drivers/pwm | |
parent | 8e0cb05b3b758885aab09883adf189f8dd8402b2 (diff) |
pwm: pwm-tiehrpwm: PWM driver support for EHRPWM
Enhanced high resolution PWM module (EHRPWM) hardware can be used to
generate PWM output over 2 channels. This commit adds PWM driver support
for EHRPWM device present on AM33XX SOC. Current implementation supports
simple PWM functionality.
Reviewed-by: Vaibhav Bedia <vaibhav.bedia@ti.com>
Signed-off-by: Philip, Avinash <avinashphilip@ti.com>
Signed-off-by: Thierry Reding <thierry.reding@avionic-design.de>
Diffstat (limited to 'drivers/pwm')
-rw-r--r-- | drivers/pwm/Kconfig | 10 | ||||
-rw-r--r-- | drivers/pwm/Makefile | 1 | ||||
-rw-r--r-- | drivers/pwm/pwm-tiehrpwm.c | 411 |
3 files changed, 422 insertions, 0 deletions
diff --git a/drivers/pwm/Kconfig b/drivers/pwm/Kconfig index 2b7db5d1f852..8fc3808d7a3e 100644 --- a/drivers/pwm/Kconfig +++ b/drivers/pwm/Kconfig | |||
@@ -86,6 +86,16 @@ config PWM_TIECAP | |||
86 | To compile this driver as a module, choose M here: the module | 86 | To compile this driver as a module, choose M here: the module |
87 | will be called pwm-tiecap. | 87 | will be called pwm-tiecap. |
88 | 88 | ||
89 | config PWM_TIEHRPWM | ||
90 | tristate "EHRPWM PWM support" | ||
91 | depends on SOC_AM33XX | ||
92 | help | ||
93 | PWM driver support for the EHRPWM controller found on AM33XX | ||
94 | TI SOC | ||
95 | |||
96 | To compile this driver as a module, choose M here: the module | ||
97 | will be called pwm-tiehrpwm. | ||
98 | |||
89 | config PWM_VT8500 | 99 | config PWM_VT8500 |
90 | tristate "vt8500 pwm support" | 100 | tristate "vt8500 pwm support" |
91 | depends on ARCH_VT8500 | 101 | depends on ARCH_VT8500 |
diff --git a/drivers/pwm/Makefile b/drivers/pwm/Makefile index 35fee430b9dd..e4b2c898964d 100644 --- a/drivers/pwm/Makefile +++ b/drivers/pwm/Makefile | |||
@@ -7,4 +7,5 @@ obj-$(CONFIG_PWM_PXA) += pwm-pxa.o | |||
7 | obj-$(CONFIG_PWM_SAMSUNG) += pwm-samsung.o | 7 | obj-$(CONFIG_PWM_SAMSUNG) += pwm-samsung.o |
8 | obj-$(CONFIG_PWM_TEGRA) += pwm-tegra.o | 8 | obj-$(CONFIG_PWM_TEGRA) += pwm-tegra.o |
9 | obj-$(CONFIG_PWM_TIECAP) += pwm-tiecap.o | 9 | obj-$(CONFIG_PWM_TIECAP) += pwm-tiecap.o |
10 | obj-$(CONFIG_PWM_TIEHRPWM) += pwm-tiehrpwm.o | ||
10 | obj-$(CONFIG_PWM_VT8500) += pwm-vt8500.o | 11 | obj-$(CONFIG_PWM_VT8500) += pwm-vt8500.o |
diff --git a/drivers/pwm/pwm-tiehrpwm.c b/drivers/pwm/pwm-tiehrpwm.c new file mode 100644 index 000000000000..010d232cb0c8 --- /dev/null +++ b/drivers/pwm/pwm-tiehrpwm.c | |||
@@ -0,0 +1,411 @@ | |||
1 | /* | ||
2 | * EHRPWM PWM driver | ||
3 | * | ||
4 | * Copyright (C) 2012 Texas Instruments, Inc. - http://www.ti.com/ | ||
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, or | ||
9 | * (at your option) any later version. | ||
10 | * | ||
11 | * This program is distributed in the hope that it will be useful, | ||
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
14 | * GNU General Public License for more details. | ||
15 | * | ||
16 | * You should have received a copy of the GNU General Public License | ||
17 | * along with this program; if not, write to the Free Software | ||
18 | * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. | ||
19 | */ | ||
20 | |||
21 | #include <linux/module.h> | ||
22 | #include <linux/platform_device.h> | ||
23 | #include <linux/pwm.h> | ||
24 | #include <linux/io.h> | ||
25 | #include <linux/err.h> | ||
26 | #include <linux/clk.h> | ||
27 | #include <linux/pm_runtime.h> | ||
28 | |||
29 | /* EHRPWM registers and bits definitions */ | ||
30 | |||
31 | /* Time base module registers */ | ||
32 | #define TBCTL 0x00 | ||
33 | #define TBPRD 0x0A | ||
34 | |||
35 | #define TBCTL_RUN_MASK (BIT(15) | BIT(14)) | ||
36 | #define TBCTL_STOP_NEXT 0 | ||
37 | #define TBCTL_STOP_ON_CYCLE BIT(14) | ||
38 | #define TBCTL_FREE_RUN (BIT(15) | BIT(14)) | ||
39 | #define TBCTL_PRDLD_MASK BIT(3) | ||
40 | #define TBCTL_PRDLD_SHDW 0 | ||
41 | #define TBCTL_PRDLD_IMDT BIT(3) | ||
42 | #define TBCTL_CLKDIV_MASK (BIT(12) | BIT(11) | BIT(10) | BIT(9) | \ | ||
43 | BIT(8) | BIT(7)) | ||
44 | #define TBCTL_CTRMODE_MASK (BIT(1) | BIT(0)) | ||
45 | #define TBCTL_CTRMODE_UP 0 | ||
46 | #define TBCTL_CTRMODE_DOWN BIT(0) | ||
47 | #define TBCTL_CTRMODE_UPDOWN BIT(1) | ||
48 | #define TBCTL_CTRMODE_FREEZE (BIT(1) | BIT(0)) | ||
49 | |||
50 | #define TBCTL_HSPCLKDIV_SHIFT 7 | ||
51 | #define TBCTL_CLKDIV_SHIFT 10 | ||
52 | |||
53 | #define CLKDIV_MAX 7 | ||
54 | #define HSPCLKDIV_MAX 7 | ||
55 | #define PERIOD_MAX 0xFFFF | ||
56 | |||
57 | /* compare module registers */ | ||
58 | #define CMPA 0x12 | ||
59 | #define CMPB 0x14 | ||
60 | |||
61 | /* Action qualifier module registers */ | ||
62 | #define AQCTLA 0x16 | ||
63 | #define AQCTLB 0x18 | ||
64 | #define AQSFRC 0x1A | ||
65 | #define AQCSFRC 0x1C | ||
66 | |||
67 | #define AQCTL_CBU_MASK (BIT(9) | BIT(8)) | ||
68 | #define AQCTL_CBU_FRCLOW BIT(8) | ||
69 | #define AQCTL_CBU_FRCHIGH BIT(9) | ||
70 | #define AQCTL_CBU_FRCTOGGLE (BIT(9) | BIT(8)) | ||
71 | #define AQCTL_CAU_MASK (BIT(5) | BIT(4)) | ||
72 | #define AQCTL_CAU_FRCLOW BIT(4) | ||
73 | #define AQCTL_CAU_FRCHIGH BIT(5) | ||
74 | #define AQCTL_CAU_FRCTOGGLE (BIT(5) | BIT(4)) | ||
75 | #define AQCTL_PRD_MASK (BIT(3) | BIT(2)) | ||
76 | #define AQCTL_PRD_FRCLOW BIT(2) | ||
77 | #define AQCTL_PRD_FRCHIGH BIT(3) | ||
78 | #define AQCTL_PRD_FRCTOGGLE (BIT(3) | BIT(2)) | ||
79 | #define AQCTL_ZRO_MASK (BIT(1) | BIT(0)) | ||
80 | #define AQCTL_ZRO_FRCLOW BIT(0) | ||
81 | #define AQCTL_ZRO_FRCHIGH BIT(1) | ||
82 | #define AQCTL_ZRO_FRCTOGGLE (BIT(1) | BIT(0)) | ||
83 | |||
84 | #define AQSFRC_RLDCSF_MASK (BIT(7) | BIT(6)) | ||
85 | #define AQSFRC_RLDCSF_ZRO 0 | ||
86 | #define AQSFRC_RLDCSF_PRD BIT(6) | ||
87 | #define AQSFRC_RLDCSF_ZROPRD BIT(7) | ||
88 | #define AQSFRC_RLDCSF_IMDT (BIT(7) | BIT(6)) | ||
89 | |||
90 | #define AQCSFRC_CSFB_MASK (BIT(3) | BIT(2)) | ||
91 | #define AQCSFRC_CSFB_FRCDIS 0 | ||
92 | #define AQCSFRC_CSFB_FRCLOW BIT(2) | ||
93 | #define AQCSFRC_CSFB_FRCHIGH BIT(3) | ||
94 | #define AQCSFRC_CSFB_DISSWFRC (BIT(3) | BIT(2)) | ||
95 | #define AQCSFRC_CSFA_MASK (BIT(1) | BIT(0)) | ||
96 | #define AQCSFRC_CSFA_FRCDIS 0 | ||
97 | #define AQCSFRC_CSFA_FRCLOW BIT(0) | ||
98 | #define AQCSFRC_CSFA_FRCHIGH BIT(1) | ||
99 | #define AQCSFRC_CSFA_DISSWFRC (BIT(1) | BIT(0)) | ||
100 | |||
101 | #define NUM_PWM_CHANNEL 2 /* EHRPWM channels */ | ||
102 | |||
103 | struct ehrpwm_pwm_chip { | ||
104 | struct pwm_chip chip; | ||
105 | unsigned int clk_rate; | ||
106 | void __iomem *mmio_base; | ||
107 | }; | ||
108 | |||
109 | static inline struct ehrpwm_pwm_chip *to_ehrpwm_pwm_chip(struct pwm_chip *chip) | ||
110 | { | ||
111 | return container_of(chip, struct ehrpwm_pwm_chip, chip); | ||
112 | } | ||
113 | |||
114 | static void ehrpwm_write(void *base, int offset, unsigned int val) | ||
115 | { | ||
116 | writew(val & 0xFFFF, base + offset); | ||
117 | } | ||
118 | |||
119 | static void ehrpwm_modify(void *base, int offset, | ||
120 | unsigned short mask, unsigned short val) | ||
121 | { | ||
122 | unsigned short regval; | ||
123 | |||
124 | regval = readw(base + offset); | ||
125 | regval &= ~mask; | ||
126 | regval |= val & mask; | ||
127 | writew(regval, base + offset); | ||
128 | } | ||
129 | |||
130 | /** | ||
131 | * set_prescale_div - Set up the prescaler divider function | ||
132 | * @rqst_prescaler: prescaler value min | ||
133 | * @prescale_div: prescaler value set | ||
134 | * @tb_clk_div: Time Base Control prescaler bits | ||
135 | */ | ||
136 | static int set_prescale_div(unsigned long rqst_prescaler, | ||
137 | unsigned short *prescale_div, unsigned short *tb_clk_div) | ||
138 | { | ||
139 | unsigned int clkdiv, hspclkdiv; | ||
140 | |||
141 | for (clkdiv = 0; clkdiv <= CLKDIV_MAX; clkdiv++) { | ||
142 | for (hspclkdiv = 0; hspclkdiv <= HSPCLKDIV_MAX; hspclkdiv++) { | ||
143 | |||
144 | /* | ||
145 | * calculations for prescaler value : | ||
146 | * prescale_div = HSPCLKDIVIDER * CLKDIVIDER. | ||
147 | * HSPCLKDIVIDER = 2 ** hspclkdiv | ||
148 | * CLKDIVIDER = (1), if clkdiv == 0 *OR* | ||
149 | * (2 * clkdiv), if clkdiv != 0 | ||
150 | * | ||
151 | * Configure prescale_div value such that period | ||
152 | * register value is less than 65535. | ||
153 | */ | ||
154 | |||
155 | *prescale_div = (1 << clkdiv) * | ||
156 | (hspclkdiv ? (hspclkdiv * 2) : 1); | ||
157 | if (*prescale_div > rqst_prescaler) { | ||
158 | *tb_clk_div = (clkdiv << TBCTL_CLKDIV_SHIFT) | | ||
159 | (hspclkdiv << TBCTL_HSPCLKDIV_SHIFT); | ||
160 | return 0; | ||
161 | } | ||
162 | } | ||
163 | } | ||
164 | return 1; | ||
165 | } | ||
166 | |||
167 | static void configure_chans(struct ehrpwm_pwm_chip *pc, int chan, | ||
168 | unsigned long duty_cycles) | ||
169 | { | ||
170 | int cmp_reg, aqctl_reg; | ||
171 | unsigned short aqctl_val, aqctl_mask; | ||
172 | |||
173 | /* | ||
174 | * Channels can be configured from action qualifier module. | ||
175 | * Channel 0 configured with compare A register and for | ||
176 | * up-counter mode. | ||
177 | * Channel 1 configured with compare B register and for | ||
178 | * up-counter mode. | ||
179 | */ | ||
180 | if (chan == 1) { | ||
181 | aqctl_reg = AQCTLB; | ||
182 | cmp_reg = CMPB; | ||
183 | /* Configure PWM Low from compare B value */ | ||
184 | aqctl_val = AQCTL_CBU_FRCLOW; | ||
185 | aqctl_mask = AQCTL_CBU_MASK; | ||
186 | } else { | ||
187 | cmp_reg = CMPA; | ||
188 | aqctl_reg = AQCTLA; | ||
189 | /* Configure PWM Low from compare A value*/ | ||
190 | aqctl_val = AQCTL_CAU_FRCLOW; | ||
191 | aqctl_mask = AQCTL_CAU_MASK; | ||
192 | } | ||
193 | |||
194 | /* Configure PWM High from period value and zero value */ | ||
195 | aqctl_val |= AQCTL_PRD_FRCHIGH | AQCTL_ZRO_FRCHIGH; | ||
196 | aqctl_mask |= AQCTL_PRD_MASK | AQCTL_ZRO_MASK; | ||
197 | ehrpwm_modify(pc->mmio_base, aqctl_reg, aqctl_mask, aqctl_val); | ||
198 | |||
199 | ehrpwm_write(pc->mmio_base, cmp_reg, duty_cycles); | ||
200 | } | ||
201 | |||
202 | /* | ||
203 | * period_ns = 10^9 * (ps_divval * period_cycles) / PWM_CLK_RATE | ||
204 | * duty_ns = 10^9 * (ps_divval * duty_cycles) / PWM_CLK_RATE | ||
205 | */ | ||
206 | static int ehrpwm_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm, | ||
207 | int duty_ns, int period_ns) | ||
208 | { | ||
209 | struct ehrpwm_pwm_chip *pc = to_ehrpwm_pwm_chip(chip); | ||
210 | unsigned long long c; | ||
211 | unsigned long period_cycles, duty_cycles; | ||
212 | unsigned short ps_divval, tb_divval; | ||
213 | |||
214 | if (period_ns < 0 || duty_ns < 0 || period_ns > NSEC_PER_SEC) | ||
215 | return -ERANGE; | ||
216 | |||
217 | c = pc->clk_rate; | ||
218 | c = c * period_ns; | ||
219 | do_div(c, NSEC_PER_SEC); | ||
220 | period_cycles = (unsigned long)c; | ||
221 | |||
222 | if (period_cycles < 1) { | ||
223 | period_cycles = 1; | ||
224 | duty_cycles = 1; | ||
225 | } else { | ||
226 | c = pc->clk_rate; | ||
227 | c = c * duty_ns; | ||
228 | do_div(c, NSEC_PER_SEC); | ||
229 | duty_cycles = (unsigned long)c; | ||
230 | } | ||
231 | |||
232 | /* Configure clock prescaler to support Low frequency PWM wave */ | ||
233 | if (set_prescale_div(period_cycles/PERIOD_MAX, &ps_divval, | ||
234 | &tb_divval)) { | ||
235 | dev_err(chip->dev, "Unsupported values\n"); | ||
236 | return -EINVAL; | ||
237 | } | ||
238 | |||
239 | pm_runtime_get_sync(chip->dev); | ||
240 | |||
241 | /* Update clock prescaler values */ | ||
242 | ehrpwm_modify(pc->mmio_base, TBCTL, TBCTL_CLKDIV_MASK, tb_divval); | ||
243 | |||
244 | /* Update period & duty cycle with presacler division */ | ||
245 | period_cycles = period_cycles / ps_divval; | ||
246 | duty_cycles = duty_cycles / ps_divval; | ||
247 | |||
248 | /* Configure shadow loading on Period register */ | ||
249 | ehrpwm_modify(pc->mmio_base, TBCTL, TBCTL_PRDLD_MASK, TBCTL_PRDLD_SHDW); | ||
250 | |||
251 | ehrpwm_write(pc->mmio_base, TBPRD, period_cycles); | ||
252 | |||
253 | /* Configure ehrpwm counter for up-count mode */ | ||
254 | ehrpwm_modify(pc->mmio_base, TBCTL, TBCTL_CTRMODE_MASK, | ||
255 | TBCTL_CTRMODE_UP); | ||
256 | |||
257 | /* Configure the channel for duty cycle */ | ||
258 | configure_chans(pc, pwm->hwpwm, duty_cycles); | ||
259 | pm_runtime_put_sync(chip->dev); | ||
260 | return 0; | ||
261 | } | ||
262 | |||
263 | static int ehrpwm_pwm_enable(struct pwm_chip *chip, struct pwm_device *pwm) | ||
264 | { | ||
265 | struct ehrpwm_pwm_chip *pc = to_ehrpwm_pwm_chip(chip); | ||
266 | unsigned short aqcsfrc_val, aqcsfrc_mask; | ||
267 | |||
268 | /* Leave clock enabled on enabling PWM */ | ||
269 | pm_runtime_get_sync(chip->dev); | ||
270 | |||
271 | /* Disabling Action Qualifier on PWM output */ | ||
272 | if (pwm->hwpwm) { | ||
273 | aqcsfrc_val = AQCSFRC_CSFB_FRCDIS; | ||
274 | aqcsfrc_mask = AQCSFRC_CSFB_MASK; | ||
275 | } else { | ||
276 | aqcsfrc_val = AQCSFRC_CSFA_FRCDIS; | ||
277 | aqcsfrc_mask = AQCSFRC_CSFA_MASK; | ||
278 | } | ||
279 | |||
280 | /* Changes to shadow mode */ | ||
281 | ehrpwm_modify(pc->mmio_base, AQSFRC, AQSFRC_RLDCSF_MASK, | ||
282 | AQSFRC_RLDCSF_ZRO); | ||
283 | |||
284 | ehrpwm_modify(pc->mmio_base, AQCSFRC, aqcsfrc_mask, aqcsfrc_val); | ||
285 | |||
286 | /* Enable time counter for free_run */ | ||
287 | ehrpwm_modify(pc->mmio_base, TBCTL, TBCTL_RUN_MASK, TBCTL_FREE_RUN); | ||
288 | return 0; | ||
289 | } | ||
290 | |||
291 | static void ehrpwm_pwm_disable(struct pwm_chip *chip, struct pwm_device *pwm) | ||
292 | { | ||
293 | struct ehrpwm_pwm_chip *pc = to_ehrpwm_pwm_chip(chip); | ||
294 | unsigned short aqcsfrc_val, aqcsfrc_mask; | ||
295 | |||
296 | /* Action Qualifier puts PWM output low forcefully */ | ||
297 | if (pwm->hwpwm) { | ||
298 | aqcsfrc_val = AQCSFRC_CSFB_FRCLOW; | ||
299 | aqcsfrc_mask = AQCSFRC_CSFB_MASK; | ||
300 | } else { | ||
301 | aqcsfrc_val = AQCSFRC_CSFA_FRCLOW; | ||
302 | aqcsfrc_mask = AQCSFRC_CSFA_MASK; | ||
303 | } | ||
304 | |||
305 | /* | ||
306 | * Changes to immediate action on Action Qualifier. This puts | ||
307 | * Action Qualifier control on PWM output from next TBCLK | ||
308 | */ | ||
309 | ehrpwm_modify(pc->mmio_base, AQSFRC, AQSFRC_RLDCSF_MASK, | ||
310 | AQSFRC_RLDCSF_IMDT); | ||
311 | |||
312 | ehrpwm_modify(pc->mmio_base, AQCSFRC, aqcsfrc_mask, aqcsfrc_val); | ||
313 | |||
314 | /* Stop Time base counter */ | ||
315 | ehrpwm_modify(pc->mmio_base, TBCTL, TBCTL_RUN_MASK, TBCTL_STOP_NEXT); | ||
316 | |||
317 | /* Disable clock on PWM disable */ | ||
318 | pm_runtime_put_sync(chip->dev); | ||
319 | } | ||
320 | |||
321 | static void ehrpwm_pwm_free(struct pwm_chip *chip, struct pwm_device *pwm) | ||
322 | { | ||
323 | if (test_bit(PWMF_ENABLED, &pwm->flags)) { | ||
324 | dev_warn(chip->dev, "Removing PWM device without disabling\n"); | ||
325 | pm_runtime_put_sync(chip->dev); | ||
326 | } | ||
327 | } | ||
328 | |||
329 | static const struct pwm_ops ehrpwm_pwm_ops = { | ||
330 | .free = ehrpwm_pwm_free, | ||
331 | .config = ehrpwm_pwm_config, | ||
332 | .enable = ehrpwm_pwm_enable, | ||
333 | .disable = ehrpwm_pwm_disable, | ||
334 | .owner = THIS_MODULE, | ||
335 | }; | ||
336 | |||
337 | static int __devinit ehrpwm_pwm_probe(struct platform_device *pdev) | ||
338 | { | ||
339 | int ret; | ||
340 | struct resource *r; | ||
341 | struct clk *clk; | ||
342 | struct ehrpwm_pwm_chip *pc; | ||
343 | |||
344 | pc = devm_kzalloc(&pdev->dev, sizeof(*pc), GFP_KERNEL); | ||
345 | if (!pc) { | ||
346 | dev_err(&pdev->dev, "failed to allocate memory\n"); | ||
347 | return -ENOMEM; | ||
348 | } | ||
349 | |||
350 | clk = devm_clk_get(&pdev->dev, "fck"); | ||
351 | if (IS_ERR(clk)) { | ||
352 | dev_err(&pdev->dev, "failed to get clock\n"); | ||
353 | return PTR_ERR(clk); | ||
354 | } | ||
355 | |||
356 | pc->clk_rate = clk_get_rate(clk); | ||
357 | if (!pc->clk_rate) { | ||
358 | dev_err(&pdev->dev, "failed to get clock rate\n"); | ||
359 | return -EINVAL; | ||
360 | } | ||
361 | |||
362 | pc->chip.dev = &pdev->dev; | ||
363 | pc->chip.ops = &ehrpwm_pwm_ops; | ||
364 | pc->chip.base = -1; | ||
365 | pc->chip.npwm = NUM_PWM_CHANNEL; | ||
366 | |||
367 | r = platform_get_resource(pdev, IORESOURCE_MEM, 0); | ||
368 | if (!r) { | ||
369 | dev_err(&pdev->dev, "no memory resource defined\n"); | ||
370 | return -ENODEV; | ||
371 | } | ||
372 | |||
373 | pc->mmio_base = devm_request_and_ioremap(&pdev->dev, r); | ||
374 | if (!pc->mmio_base) { | ||
375 | dev_err(&pdev->dev, "failed to ioremap() registers\n"); | ||
376 | return -EADDRNOTAVAIL; | ||
377 | } | ||
378 | |||
379 | ret = pwmchip_add(&pc->chip); | ||
380 | if (ret < 0) { | ||
381 | dev_err(&pdev->dev, "pwmchip_add() failed: %d\n", ret); | ||
382 | return ret; | ||
383 | } | ||
384 | |||
385 | pm_runtime_enable(&pdev->dev); | ||
386 | platform_set_drvdata(pdev, pc); | ||
387 | return 0; | ||
388 | } | ||
389 | |||
390 | static int __devexit ehrpwm_pwm_remove(struct platform_device *pdev) | ||
391 | { | ||
392 | struct ehrpwm_pwm_chip *pc = platform_get_drvdata(pdev); | ||
393 | |||
394 | pm_runtime_put_sync(&pdev->dev); | ||
395 | pm_runtime_disable(&pdev->dev); | ||
396 | return pwmchip_remove(&pc->chip); | ||
397 | } | ||
398 | |||
399 | static struct platform_driver ehrpwm_pwm_driver = { | ||
400 | .driver = { | ||
401 | .name = "ehrpwm", | ||
402 | }, | ||
403 | .probe = ehrpwm_pwm_probe, | ||
404 | .remove = __devexit_p(ehrpwm_pwm_remove), | ||
405 | }; | ||
406 | |||
407 | module_platform_driver(ehrpwm_pwm_driver); | ||
408 | |||
409 | MODULE_DESCRIPTION("EHRPWM PWM driver"); | ||
410 | MODULE_AUTHOR("Texas Instruments"); | ||
411 | MODULE_LICENSE("GPL"); | ||