Commit a23086981723570c8952520d9ffaf6995d45f63e
Committed by
Thierry Reding
1 parent
8f43e18e27
Exists in
smarc-imx_3.14.28_1.0.0_ga
and in
1 other branch
pwm: add ep93xx PWM support
Remove the non-standard EP93xx PWM driver in drivers/misc and add a new driver for the PWM controllers on the EP93xx platform based on the PWM framework. These PWM controllers each support 1 PWM channel with programmable duty cycle, frequency, and polarity inversion. Signed-off-by: H Hartley Sweeten <hsweeten@visionengravers.com> Cc: Ryan Mallon <rmallon@gmail.com> Acked-by: Arnd Bergmann <arnd@arndb.de> Acked-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> Signed-off-by: Thierry Reding <thierry.reding@gmail.com>
Showing 6 changed files with 240 additions and 300 deletions Side-by-side Diff
drivers/misc/Kconfig
... | ... | @@ -381,19 +381,6 @@ |
381 | 381 | This driver provides support for the Honeywell HMC6352 compass, |
382 | 382 | providing configuration and heading data via sysfs. |
383 | 383 | |
384 | -config EP93XX_PWM | |
385 | - tristate "EP93xx PWM support" | |
386 | - depends on ARCH_EP93XX | |
387 | - help | |
388 | - This option enables device driver support for the PWM channels | |
389 | - on the Cirrus EP93xx processors. The EP9307 chip only has one | |
390 | - PWM channel all the others have two, the second channel is an | |
391 | - alternate function of the EGPIO14 pin. A sysfs interface is | |
392 | - provided to control the PWM channels. | |
393 | - | |
394 | - To compile this driver as a module, choose M here: the module will | |
395 | - be called ep93xx_pwm. | |
396 | - | |
397 | 384 | config DS1682 |
398 | 385 | tristate "Dallas DS1682 Total Elapsed Time Recorder with Alarm" |
399 | 386 | depends on I2C |
drivers/misc/Makefile
... | ... | @@ -33,7 +33,6 @@ |
33 | 33 | obj-$(CONFIG_ISL29003) += isl29003.o |
34 | 34 | obj-$(CONFIG_ISL29020) += isl29020.o |
35 | 35 | obj-$(CONFIG_SENSORS_TSL2550) += tsl2550.o |
36 | -obj-$(CONFIG_EP93XX_PWM) += ep93xx_pwm.o | |
37 | 36 | obj-$(CONFIG_DS1682) += ds1682.o |
38 | 37 | obj-$(CONFIG_TI_DAC7512) += ti_dac7512.o |
39 | 38 | obj-$(CONFIG_C2PORT) += c2port/ |
drivers/misc/ep93xx_pwm.c
1 | -/* | |
2 | - * Simple PWM driver for EP93XX | |
3 | - * | |
4 | - * (c) Copyright 2009 Matthieu Crapet <mcrapet@gmail.com> | |
5 | - * (c) Copyright 2009 H Hartley Sweeten <hsweeten@visionengravers.com> | |
6 | - * | |
7 | - * This program is free software; you can redistribute it and/or | |
8 | - * modify it under the terms of the GNU General Public License | |
9 | - * as published by the Free Software Foundation; either version | |
10 | - * 2 of the License, or (at your option) any later version. | |
11 | - * | |
12 | - * EP9307 has only one channel: | |
13 | - * - PWMOUT | |
14 | - * | |
15 | - * EP9301/02/12/15 have two channels: | |
16 | - * - PWMOUT | |
17 | - * - PWMOUT1 (alternate function for EGPIO14) | |
18 | - */ | |
19 | - | |
20 | -#include <linux/module.h> | |
21 | -#include <linux/platform_device.h> | |
22 | -#include <linux/slab.h> | |
23 | -#include <linux/clk.h> | |
24 | -#include <linux/err.h> | |
25 | -#include <linux/io.h> | |
26 | - | |
27 | -#include <mach/platform.h> | |
28 | - | |
29 | -#define EP93XX_PWMx_TERM_COUNT 0x00 | |
30 | -#define EP93XX_PWMx_DUTY_CYCLE 0x04 | |
31 | -#define EP93XX_PWMx_ENABLE 0x08 | |
32 | -#define EP93XX_PWMx_INVERT 0x0C | |
33 | - | |
34 | -#define EP93XX_PWM_MAX_COUNT 0xFFFF | |
35 | - | |
36 | -struct ep93xx_pwm { | |
37 | - void __iomem *mmio_base; | |
38 | - struct clk *clk; | |
39 | - u32 duty_percent; | |
40 | -}; | |
41 | - | |
42 | -/* | |
43 | - * /sys/devices/platform/ep93xx-pwm.N | |
44 | - * /min_freq read-only minimum pwm output frequency | |
45 | - * /max_req read-only maximum pwm output frequency | |
46 | - * /freq read-write pwm output frequency (0 = disable output) | |
47 | - * /duty_percent read-write pwm duty cycle percent (1..99) | |
48 | - * /invert read-write invert pwm output | |
49 | - */ | |
50 | - | |
51 | -static ssize_t ep93xx_pwm_get_min_freq(struct device *dev, | |
52 | - struct device_attribute *attr, char *buf) | |
53 | -{ | |
54 | - struct platform_device *pdev = to_platform_device(dev); | |
55 | - struct ep93xx_pwm *pwm = platform_get_drvdata(pdev); | |
56 | - unsigned long rate = clk_get_rate(pwm->clk); | |
57 | - | |
58 | - return sprintf(buf, "%ld\n", rate / (EP93XX_PWM_MAX_COUNT + 1)); | |
59 | -} | |
60 | - | |
61 | -static ssize_t ep93xx_pwm_get_max_freq(struct device *dev, | |
62 | - struct device_attribute *attr, char *buf) | |
63 | -{ | |
64 | - struct platform_device *pdev = to_platform_device(dev); | |
65 | - struct ep93xx_pwm *pwm = platform_get_drvdata(pdev); | |
66 | - unsigned long rate = clk_get_rate(pwm->clk); | |
67 | - | |
68 | - return sprintf(buf, "%ld\n", rate / 2); | |
69 | -} | |
70 | - | |
71 | -static ssize_t ep93xx_pwm_get_freq(struct device *dev, | |
72 | - struct device_attribute *attr, char *buf) | |
73 | -{ | |
74 | - struct platform_device *pdev = to_platform_device(dev); | |
75 | - struct ep93xx_pwm *pwm = platform_get_drvdata(pdev); | |
76 | - | |
77 | - if (readl(pwm->mmio_base + EP93XX_PWMx_ENABLE) & 0x1) { | |
78 | - unsigned long rate = clk_get_rate(pwm->clk); | |
79 | - u16 term = readl(pwm->mmio_base + EP93XX_PWMx_TERM_COUNT); | |
80 | - | |
81 | - return sprintf(buf, "%ld\n", rate / (term + 1)); | |
82 | - } else { | |
83 | - return sprintf(buf, "disabled\n"); | |
84 | - } | |
85 | -} | |
86 | - | |
87 | -static ssize_t ep93xx_pwm_set_freq(struct device *dev, | |
88 | - struct device_attribute *attr, const char *buf, size_t count) | |
89 | -{ | |
90 | - struct platform_device *pdev = to_platform_device(dev); | |
91 | - struct ep93xx_pwm *pwm = platform_get_drvdata(pdev); | |
92 | - long val; | |
93 | - int err; | |
94 | - | |
95 | - err = kstrtol(buf, 10, &val); | |
96 | - if (err) | |
97 | - return -EINVAL; | |
98 | - | |
99 | - if (val == 0) { | |
100 | - writel(0x0, pwm->mmio_base + EP93XX_PWMx_ENABLE); | |
101 | - } else if (val <= (clk_get_rate(pwm->clk) / 2)) { | |
102 | - u32 term, duty; | |
103 | - | |
104 | - val = (clk_get_rate(pwm->clk) / val) - 1; | |
105 | - if (val > EP93XX_PWM_MAX_COUNT) | |
106 | - val = EP93XX_PWM_MAX_COUNT; | |
107 | - if (val < 1) | |
108 | - val = 1; | |
109 | - | |
110 | - term = readl(pwm->mmio_base + EP93XX_PWMx_TERM_COUNT); | |
111 | - duty = ((val + 1) * pwm->duty_percent / 100) - 1; | |
112 | - | |
113 | - /* If pwm is running, order is important */ | |
114 | - if (val > term) { | |
115 | - writel(val, pwm->mmio_base + EP93XX_PWMx_TERM_COUNT); | |
116 | - writel(duty, pwm->mmio_base + EP93XX_PWMx_DUTY_CYCLE); | |
117 | - } else { | |
118 | - writel(duty, pwm->mmio_base + EP93XX_PWMx_DUTY_CYCLE); | |
119 | - writel(val, pwm->mmio_base + EP93XX_PWMx_TERM_COUNT); | |
120 | - } | |
121 | - | |
122 | - if (!readl(pwm->mmio_base + EP93XX_PWMx_ENABLE) & 0x1) | |
123 | - writel(0x1, pwm->mmio_base + EP93XX_PWMx_ENABLE); | |
124 | - } else { | |
125 | - return -EINVAL; | |
126 | - } | |
127 | - | |
128 | - return count; | |
129 | -} | |
130 | - | |
131 | -static ssize_t ep93xx_pwm_get_duty_percent(struct device *dev, | |
132 | - struct device_attribute *attr, char *buf) | |
133 | -{ | |
134 | - struct platform_device *pdev = to_platform_device(dev); | |
135 | - struct ep93xx_pwm *pwm = platform_get_drvdata(pdev); | |
136 | - | |
137 | - return sprintf(buf, "%d\n", pwm->duty_percent); | |
138 | -} | |
139 | - | |
140 | -static ssize_t ep93xx_pwm_set_duty_percent(struct device *dev, | |
141 | - struct device_attribute *attr, const char *buf, size_t count) | |
142 | -{ | |
143 | - struct platform_device *pdev = to_platform_device(dev); | |
144 | - struct ep93xx_pwm *pwm = platform_get_drvdata(pdev); | |
145 | - long val; | |
146 | - int err; | |
147 | - | |
148 | - err = kstrtol(buf, 10, &val); | |
149 | - if (err) | |
150 | - return -EINVAL; | |
151 | - | |
152 | - if (val > 0 && val < 100) { | |
153 | - u32 term = readl(pwm->mmio_base + EP93XX_PWMx_TERM_COUNT); | |
154 | - u32 duty = ((term + 1) * val / 100) - 1; | |
155 | - | |
156 | - writel(duty, pwm->mmio_base + EP93XX_PWMx_DUTY_CYCLE); | |
157 | - pwm->duty_percent = val; | |
158 | - return count; | |
159 | - } | |
160 | - | |
161 | - return -EINVAL; | |
162 | -} | |
163 | - | |
164 | -static ssize_t ep93xx_pwm_get_invert(struct device *dev, | |
165 | - struct device_attribute *attr, char *buf) | |
166 | -{ | |
167 | - struct platform_device *pdev = to_platform_device(dev); | |
168 | - struct ep93xx_pwm *pwm = platform_get_drvdata(pdev); | |
169 | - int inverted = readl(pwm->mmio_base + EP93XX_PWMx_INVERT) & 0x1; | |
170 | - | |
171 | - return sprintf(buf, "%d\n", inverted); | |
172 | -} | |
173 | - | |
174 | -static ssize_t ep93xx_pwm_set_invert(struct device *dev, | |
175 | - struct device_attribute *attr, const char *buf, size_t count) | |
176 | -{ | |
177 | - struct platform_device *pdev = to_platform_device(dev); | |
178 | - struct ep93xx_pwm *pwm = platform_get_drvdata(pdev); | |
179 | - long val; | |
180 | - int err; | |
181 | - | |
182 | - err = kstrtol(buf, 10, &val); | |
183 | - if (err) | |
184 | - return -EINVAL; | |
185 | - | |
186 | - if (val == 0) | |
187 | - writel(0x0, pwm->mmio_base + EP93XX_PWMx_INVERT); | |
188 | - else if (val == 1) | |
189 | - writel(0x1, pwm->mmio_base + EP93XX_PWMx_INVERT); | |
190 | - else | |
191 | - return -EINVAL; | |
192 | - | |
193 | - return count; | |
194 | -} | |
195 | - | |
196 | -static DEVICE_ATTR(min_freq, S_IRUGO, ep93xx_pwm_get_min_freq, NULL); | |
197 | -static DEVICE_ATTR(max_freq, S_IRUGO, ep93xx_pwm_get_max_freq, NULL); | |
198 | -static DEVICE_ATTR(freq, S_IWUSR | S_IRUGO, | |
199 | - ep93xx_pwm_get_freq, ep93xx_pwm_set_freq); | |
200 | -static DEVICE_ATTR(duty_percent, S_IWUSR | S_IRUGO, | |
201 | - ep93xx_pwm_get_duty_percent, ep93xx_pwm_set_duty_percent); | |
202 | -static DEVICE_ATTR(invert, S_IWUSR | S_IRUGO, | |
203 | - ep93xx_pwm_get_invert, ep93xx_pwm_set_invert); | |
204 | - | |
205 | -static struct attribute *ep93xx_pwm_attrs[] = { | |
206 | - &dev_attr_min_freq.attr, | |
207 | - &dev_attr_max_freq.attr, | |
208 | - &dev_attr_freq.attr, | |
209 | - &dev_attr_duty_percent.attr, | |
210 | - &dev_attr_invert.attr, | |
211 | - NULL | |
212 | -}; | |
213 | - | |
214 | -static const struct attribute_group ep93xx_pwm_sysfs_files = { | |
215 | - .attrs = ep93xx_pwm_attrs, | |
216 | -}; | |
217 | - | |
218 | -static int ep93xx_pwm_probe(struct platform_device *pdev) | |
219 | -{ | |
220 | - struct ep93xx_pwm *pwm; | |
221 | - struct resource *res; | |
222 | - int ret; | |
223 | - | |
224 | - pwm = devm_kzalloc(&pdev->dev, sizeof(*pwm), GFP_KERNEL); | |
225 | - if (!pwm) | |
226 | - return -ENOMEM; | |
227 | - | |
228 | - pwm->clk = devm_clk_get(&pdev->dev, "pwm_clk"); | |
229 | - if (IS_ERR(pwm->clk)) | |
230 | - return PTR_ERR(pwm->clk); | |
231 | - | |
232 | - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); | |
233 | - pwm->mmio_base = devm_ioremap_resource(&pdev->dev, res); | |
234 | - if (IS_ERR(pwm->mmio_base)) | |
235 | - return PTR_ERR(pwm->mmio_base); | |
236 | - | |
237 | - ret = ep93xx_pwm_acquire_gpio(pdev); | |
238 | - if (ret) | |
239 | - return ret; | |
240 | - | |
241 | - ret = sysfs_create_group(&pdev->dev.kobj, &ep93xx_pwm_sysfs_files); | |
242 | - if (ret) { | |
243 | - ep93xx_pwm_release_gpio(pdev); | |
244 | - return ret; | |
245 | - } | |
246 | - | |
247 | - pwm->duty_percent = 50; | |
248 | - | |
249 | - /* disable pwm at startup. Avoids zero value. */ | |
250 | - writel(0x0, pwm->mmio_base + EP93XX_PWMx_ENABLE); | |
251 | - writel(EP93XX_PWM_MAX_COUNT, pwm->mmio_base + EP93XX_PWMx_TERM_COUNT); | |
252 | - writel(EP93XX_PWM_MAX_COUNT/2, pwm->mmio_base + EP93XX_PWMx_DUTY_CYCLE); | |
253 | - | |
254 | - clk_enable(pwm->clk); | |
255 | - | |
256 | - platform_set_drvdata(pdev, pwm); | |
257 | - return 0; | |
258 | -} | |
259 | - | |
260 | -static int ep93xx_pwm_remove(struct platform_device *pdev) | |
261 | -{ | |
262 | - struct ep93xx_pwm *pwm = platform_get_drvdata(pdev); | |
263 | - | |
264 | - writel(0x0, pwm->mmio_base + EP93XX_PWMx_ENABLE); | |
265 | - clk_disable(pwm->clk); | |
266 | - sysfs_remove_group(&pdev->dev.kobj, &ep93xx_pwm_sysfs_files); | |
267 | - ep93xx_pwm_release_gpio(pdev); | |
268 | - | |
269 | - return 0; | |
270 | -} | |
271 | - | |
272 | -static struct platform_driver ep93xx_pwm_driver = { | |
273 | - .driver = { | |
274 | - .name = "ep93xx-pwm", | |
275 | - .owner = THIS_MODULE, | |
276 | - }, | |
277 | - .probe = ep93xx_pwm_probe, | |
278 | - .remove = ep93xx_pwm_remove, | |
279 | -}; | |
280 | -module_platform_driver(ep93xx_pwm_driver); | |
281 | - | |
282 | -MODULE_AUTHOR("Matthieu Crapet <mcrapet@gmail.com>, " | |
283 | - "H Hartley Sweeten <hsweeten@visionengravers.com>"); | |
284 | -MODULE_DESCRIPTION("EP93xx PWM driver"); | |
285 | -MODULE_LICENSE("GPL"); | |
286 | -MODULE_ALIAS("platform:ep93xx-pwm"); |
drivers/pwm/Kconfig
... | ... | @@ -62,6 +62,15 @@ |
62 | 62 | To compile this driver as a module, choose M here: the module |
63 | 63 | will be called pwm-bfin. |
64 | 64 | |
65 | +config PWM_EP93XX | |
66 | + tristate "Cirrus Logic EP93xx PWM support" | |
67 | + depends on ARCH_EP93XX | |
68 | + help | |
69 | + Generic PWM framework driver for Cirrus Logic EP93xx. | |
70 | + | |
71 | + To compile this driver as a module, choose M here: the module | |
72 | + will be called pwm-ep93xx. | |
73 | + | |
65 | 74 | config PWM_IMX |
66 | 75 | tristate "i.MX PWM support" |
67 | 76 | depends on ARCH_MXC |
drivers/pwm/Makefile
... | ... | @@ -3,6 +3,7 @@ |
3 | 3 | obj-$(CONFIG_PWM_AB8500) += pwm-ab8500.o |
4 | 4 | obj-$(CONFIG_PWM_ATMEL_TCB) += pwm-atmel-tcb.o |
5 | 5 | obj-$(CONFIG_PWM_BFIN) += pwm-bfin.o |
6 | +obj-$(CONFIG_PWM_EP93XX) += pwm-ep93xx.o | |
6 | 7 | obj-$(CONFIG_PWM_IMX) += pwm-imx.o |
7 | 8 | obj-$(CONFIG_PWM_JZ4740) += pwm-jz4740.o |
8 | 9 | obj-$(CONFIG_PWM_LPC32XX) += pwm-lpc32xx.o |
drivers/pwm/pwm-ep93xx.c
1 | +/* | |
2 | + * PWM framework driver for Cirrus Logic EP93xx | |
3 | + * | |
4 | + * Copyright (c) 2009 Matthieu Crapet <mcrapet@gmail.com> | |
5 | + * Copyright (c) 2009, 2013 H Hartley Sweeten <hsweeten@visionengravers.com> | |
6 | + * | |
7 | + * EP9301/02 have only one channel: | |
8 | + * platform device ep93xx-pwm.1 - PWMOUT1 (EGPIO14) | |
9 | + * | |
10 | + * EP9307 has only one channel: | |
11 | + * platform device ep93xx-pwm.0 - PWMOUT | |
12 | + * | |
13 | + * EP9312/15 have two channels: | |
14 | + * platform device ep93xx-pwm.0 - PWMOUT | |
15 | + * platform device ep93xx-pwm.1 - PWMOUT1 (EGPIO14) | |
16 | + * | |
17 | + * This program is free software; you can redistribute it and/or | |
18 | + * modify it under the terms of the GNU General Public License | |
19 | + * as published by the Free Software Foundation; either version | |
20 | + * 2 of the License, or (at your option) any later version. | |
21 | + * | |
22 | + * This program is distributed in the hope that it will be useful, | |
23 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
24 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
25 | + * GNU General Public License for more details. | |
26 | + */ | |
27 | + | |
28 | +#include <linux/module.h> | |
29 | +#include <linux/platform_device.h> | |
30 | +#include <linux/slab.h> | |
31 | +#include <linux/clk.h> | |
32 | +#include <linux/err.h> | |
33 | +#include <linux/io.h> | |
34 | +#include <linux/pwm.h> | |
35 | + | |
36 | +#include <asm/div64.h> | |
37 | + | |
38 | +#include <mach/platform.h> /* for ep93xx_pwm_{acquire,release}_gpio() */ | |
39 | + | |
40 | +#define EP93XX_PWMx_TERM_COUNT 0x00 | |
41 | +#define EP93XX_PWMx_DUTY_CYCLE 0x04 | |
42 | +#define EP93XX_PWMx_ENABLE 0x08 | |
43 | +#define EP93XX_PWMx_INVERT 0x0c | |
44 | + | |
45 | +struct ep93xx_pwm { | |
46 | + void __iomem *base; | |
47 | + struct clk *clk; | |
48 | + struct pwm_chip chip; | |
49 | +}; | |
50 | + | |
51 | +static inline struct ep93xx_pwm *to_ep93xx_pwm(struct pwm_chip *chip) | |
52 | +{ | |
53 | + return container_of(chip, struct ep93xx_pwm, chip); | |
54 | +} | |
55 | + | |
56 | +static int ep93xx_pwm_request(struct pwm_chip *chip, struct pwm_device *pwm) | |
57 | +{ | |
58 | + struct platform_device *pdev = to_platform_device(chip->dev); | |
59 | + | |
60 | + return ep93xx_pwm_acquire_gpio(pdev); | |
61 | +} | |
62 | + | |
63 | +static void ep93xx_pwm_free(struct pwm_chip *chip, struct pwm_device *pwm) | |
64 | +{ | |
65 | + struct platform_device *pdev = to_platform_device(chip->dev); | |
66 | + | |
67 | + ep93xx_pwm_release_gpio(pdev); | |
68 | +} | |
69 | + | |
70 | +static int ep93xx_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm, | |
71 | + int duty_ns, int period_ns) | |
72 | +{ | |
73 | + struct ep93xx_pwm *ep93xx_pwm = to_ep93xx_pwm(chip); | |
74 | + void __iomem *base = ep93xx_pwm->base; | |
75 | + unsigned long long c; | |
76 | + unsigned long period_cycles; | |
77 | + unsigned long duty_cycles; | |
78 | + unsigned long term; | |
79 | + int ret = 0; | |
80 | + | |
81 | + /* | |
82 | + * The clock needs to be enabled to access the PWM registers. | |
83 | + * Configuration can be changed at any time. | |
84 | + */ | |
85 | + if (!test_bit(PWMF_ENABLED, &pwm->flags)) { | |
86 | + ret = clk_enable(ep93xx_pwm->clk); | |
87 | + if (ret) | |
88 | + return ret; | |
89 | + } | |
90 | + | |
91 | + c = clk_get_rate(ep93xx_pwm->clk); | |
92 | + c *= period_ns; | |
93 | + do_div(c, 1000000000); | |
94 | + period_cycles = c; | |
95 | + | |
96 | + c = period_cycles; | |
97 | + c *= duty_ns; | |
98 | + do_div(c, period_ns); | |
99 | + duty_cycles = c; | |
100 | + | |
101 | + if (period_cycles < 0x10000 && duty_cycles < 0x10000) { | |
102 | + term = readw(base + EP93XX_PWMx_TERM_COUNT); | |
103 | + | |
104 | + /* Order is important if PWM is running */ | |
105 | + if (period_cycles > term) { | |
106 | + writew(period_cycles, base + EP93XX_PWMx_TERM_COUNT); | |
107 | + writew(duty_cycles, base + EP93XX_PWMx_DUTY_CYCLE); | |
108 | + } else { | |
109 | + writew(duty_cycles, base + EP93XX_PWMx_DUTY_CYCLE); | |
110 | + writew(period_cycles, base + EP93XX_PWMx_TERM_COUNT); | |
111 | + } | |
112 | + } else { | |
113 | + ret = -EINVAL; | |
114 | + } | |
115 | + | |
116 | + if (!test_bit(PWMF_ENABLED, &pwm->flags)) | |
117 | + clk_disable(ep93xx_pwm->clk); | |
118 | + | |
119 | + return ret; | |
120 | +} | |
121 | + | |
122 | +static int ep93xx_pwm_polarity(struct pwm_chip *chip, struct pwm_device *pwm, | |
123 | + enum pwm_polarity polarity) | |
124 | +{ | |
125 | + struct ep93xx_pwm *ep93xx_pwm = to_ep93xx_pwm(chip); | |
126 | + int ret; | |
127 | + | |
128 | + /* | |
129 | + * The clock needs to be enabled to access the PWM registers. | |
130 | + * Polarity can only be changed when the PWM is disabled. | |
131 | + */ | |
132 | + ret = clk_enable(ep93xx_pwm->clk); | |
133 | + if (ret) | |
134 | + return ret; | |
135 | + | |
136 | + if (polarity == PWM_POLARITY_INVERSED) | |
137 | + writew(0x1, ep93xx_pwm->base + EP93XX_PWMx_INVERT); | |
138 | + else | |
139 | + writew(0x0, ep93xx_pwm->base + EP93XX_PWMx_INVERT); | |
140 | + | |
141 | + clk_disable(ep93xx_pwm->clk); | |
142 | + | |
143 | + return 0; | |
144 | +} | |
145 | + | |
146 | +static int ep93xx_pwm_enable(struct pwm_chip *chip, struct pwm_device *pwm) | |
147 | +{ | |
148 | + struct ep93xx_pwm *ep93xx_pwm = to_ep93xx_pwm(chip); | |
149 | + int ret; | |
150 | + | |
151 | + ret = clk_enable(ep93xx_pwm->clk); | |
152 | + if (ret) | |
153 | + return ret; | |
154 | + | |
155 | + writew(0x1, ep93xx_pwm->base + EP93XX_PWMx_ENABLE); | |
156 | + | |
157 | + return 0; | |
158 | +} | |
159 | + | |
160 | +static void ep93xx_pwm_disable(struct pwm_chip *chip, struct pwm_device *pwm) | |
161 | +{ | |
162 | + struct ep93xx_pwm *ep93xx_pwm = to_ep93xx_pwm(chip); | |
163 | + | |
164 | + writew(0x0, ep93xx_pwm->base + EP93XX_PWMx_ENABLE); | |
165 | + clk_disable(ep93xx_pwm->clk); | |
166 | +} | |
167 | + | |
168 | +static const struct pwm_ops ep93xx_pwm_ops = { | |
169 | + .request = ep93xx_pwm_request, | |
170 | + .free = ep93xx_pwm_free, | |
171 | + .config = ep93xx_pwm_config, | |
172 | + .set_polarity = ep93xx_pwm_polarity, | |
173 | + .enable = ep93xx_pwm_enable, | |
174 | + .disable = ep93xx_pwm_disable, | |
175 | + .owner = THIS_MODULE, | |
176 | +}; | |
177 | + | |
178 | +static int ep93xx_pwm_probe(struct platform_device *pdev) | |
179 | +{ | |
180 | + struct ep93xx_pwm *ep93xx_pwm; | |
181 | + struct resource *res; | |
182 | + int ret; | |
183 | + | |
184 | + ep93xx_pwm = devm_kzalloc(&pdev->dev, sizeof(*ep93xx_pwm), GFP_KERNEL); | |
185 | + if (!ep93xx_pwm) | |
186 | + return -ENOMEM; | |
187 | + | |
188 | + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); | |
189 | + ep93xx_pwm->base = devm_ioremap_resource(&pdev->dev, res); | |
190 | + if (IS_ERR(ep93xx_pwm->base)) | |
191 | + return PTR_ERR(ep93xx_pwm->base); | |
192 | + | |
193 | + ep93xx_pwm->clk = devm_clk_get(&pdev->dev, "pwm_clk"); | |
194 | + if (IS_ERR(ep93xx_pwm->clk)) | |
195 | + return PTR_ERR(ep93xx_pwm->clk); | |
196 | + | |
197 | + ep93xx_pwm->chip.dev = &pdev->dev; | |
198 | + ep93xx_pwm->chip.ops = &ep93xx_pwm_ops; | |
199 | + ep93xx_pwm->chip.base = -1; | |
200 | + ep93xx_pwm->chip.npwm = 1; | |
201 | + | |
202 | + ret = pwmchip_add(&ep93xx_pwm->chip); | |
203 | + if (ret < 0) | |
204 | + return ret; | |
205 | + | |
206 | + platform_set_drvdata(pdev, ep93xx_pwm); | |
207 | + return 0; | |
208 | +} | |
209 | + | |
210 | +static int ep93xx_pwm_remove(struct platform_device *pdev) | |
211 | +{ | |
212 | + struct ep93xx_pwm *ep93xx_pwm = platform_get_drvdata(pdev); | |
213 | + | |
214 | + return pwmchip_remove(&ep93xx_pwm->chip); | |
215 | +} | |
216 | + | |
217 | +static struct platform_driver ep93xx_pwm_driver = { | |
218 | + .driver = { | |
219 | + .name = "ep93xx-pwm", | |
220 | + }, | |
221 | + .probe = ep93xx_pwm_probe, | |
222 | + .remove = ep93xx_pwm_remove, | |
223 | +}; | |
224 | +module_platform_driver(ep93xx_pwm_driver); | |
225 | + | |
226 | +MODULE_DESCRIPTION("Cirrus Logic EP93xx PWM driver"); | |
227 | +MODULE_AUTHOR("Matthieu Crapet <mcrapet@gmail.com>, " | |
228 | + "H Hartley Sweeten <hsweeten@visionengravers.com>"); | |
229 | +MODULE_ALIAS("platform:ep93xx-pwm"); | |
230 | +MODULE_LICENSE("GPL"); |