2 * Simple PWM driver for EP93XX
4 * (c) Copyright 2009 Matthieu Crapet <mcrapet@gmail.com>
5 * (c) Copyright 2009 H Hartley Sweeten <hsweeten@visionengravers.com>
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.
12 * EP9307 has only one channel:
15 * EP9301/02/12/15 have two channels:
17 * - PWMOUT1 (alternate function for EGPIO14)
20 #include <linux/module.h>
21 #include <linux/platform_device.h>
22 #include <linux/clk.h>
23 #include <linux/err.h>
26 #include <mach/platform.h>
28 #define EP93XX_PWMx_TERM_COUNT 0x00
29 #define EP93XX_PWMx_DUTY_CYCLE 0x04
30 #define EP93XX_PWMx_ENABLE 0x08
31 #define EP93XX_PWMx_INVERT 0x0C
33 #define EP93XX_PWM_MAX_COUNT 0xFFFF
36 void __iomem
*mmio_base
;
41 static inline void ep93xx_pwm_writel(struct ep93xx_pwm
*pwm
,
42 unsigned int val
, unsigned int off
)
44 __raw_writel(val
, pwm
->mmio_base
+ off
);
47 static inline unsigned int ep93xx_pwm_readl(struct ep93xx_pwm
*pwm
,
50 return __raw_readl(pwm
->mmio_base
+ off
);
53 static inline void ep93xx_pwm_write_tc(struct ep93xx_pwm
*pwm
, u16 value
)
55 ep93xx_pwm_writel(pwm
, value
, EP93XX_PWMx_TERM_COUNT
);
58 static inline u16
ep93xx_pwm_read_tc(struct ep93xx_pwm
*pwm
)
60 return ep93xx_pwm_readl(pwm
, EP93XX_PWMx_TERM_COUNT
);
63 static inline void ep93xx_pwm_write_dc(struct ep93xx_pwm
*pwm
, u16 value
)
65 ep93xx_pwm_writel(pwm
, value
, EP93XX_PWMx_DUTY_CYCLE
);
68 static inline void ep93xx_pwm_enable(struct ep93xx_pwm
*pwm
)
70 ep93xx_pwm_writel(pwm
, 0x1, EP93XX_PWMx_ENABLE
);
73 static inline void ep93xx_pwm_disable(struct ep93xx_pwm
*pwm
)
75 ep93xx_pwm_writel(pwm
, 0x0, EP93XX_PWMx_ENABLE
);
78 static inline int ep93xx_pwm_is_enabled(struct ep93xx_pwm
*pwm
)
80 return ep93xx_pwm_readl(pwm
, EP93XX_PWMx_ENABLE
) & 0x1;
83 static inline void ep93xx_pwm_invert(struct ep93xx_pwm
*pwm
)
85 ep93xx_pwm_writel(pwm
, 0x1, EP93XX_PWMx_INVERT
);
88 static inline void ep93xx_pwm_normal(struct ep93xx_pwm
*pwm
)
90 ep93xx_pwm_writel(pwm
, 0x0, EP93XX_PWMx_INVERT
);
93 static inline int ep93xx_pwm_is_inverted(struct ep93xx_pwm
*pwm
)
95 return ep93xx_pwm_readl(pwm
, EP93XX_PWMx_INVERT
) & 0x1;
99 * /sys/devices/platform/ep93xx-pwm.N
100 * /min_freq read-only minimum pwm output frequency
101 * /max_req read-only maximum pwm output frequency
102 * /freq read-write pwm output frequency (0 = disable output)
103 * /duty_percent read-write pwm duty cycle percent (1..99)
104 * /invert read-write invert pwm output
107 static ssize_t
ep93xx_pwm_get_min_freq(struct device
*dev
,
108 struct device_attribute
*attr
, char *buf
)
110 struct platform_device
*pdev
= to_platform_device(dev
);
111 struct ep93xx_pwm
*pwm
= platform_get_drvdata(pdev
);
112 unsigned long rate
= clk_get_rate(pwm
->clk
);
114 return sprintf(buf
, "%ld\n", rate
/ (EP93XX_PWM_MAX_COUNT
+ 1));
117 static ssize_t
ep93xx_pwm_get_max_freq(struct device
*dev
,
118 struct device_attribute
*attr
, char *buf
)
120 struct platform_device
*pdev
= to_platform_device(dev
);
121 struct ep93xx_pwm
*pwm
= platform_get_drvdata(pdev
);
122 unsigned long rate
= clk_get_rate(pwm
->clk
);
124 return sprintf(buf
, "%ld\n", rate
/ 2);
127 static ssize_t
ep93xx_pwm_get_freq(struct device
*dev
,
128 struct device_attribute
*attr
, char *buf
)
130 struct platform_device
*pdev
= to_platform_device(dev
);
131 struct ep93xx_pwm
*pwm
= platform_get_drvdata(pdev
);
133 if (ep93xx_pwm_is_enabled(pwm
)) {
134 unsigned long rate
= clk_get_rate(pwm
->clk
);
135 u16 term
= ep93xx_pwm_read_tc(pwm
);
137 return sprintf(buf
, "%ld\n", rate
/ (term
+ 1));
139 return sprintf(buf
, "disabled\n");
143 static ssize_t
ep93xx_pwm_set_freq(struct device
*dev
,
144 struct device_attribute
*attr
, const char *buf
, size_t count
)
146 struct platform_device
*pdev
= to_platform_device(dev
);
147 struct ep93xx_pwm
*pwm
= platform_get_drvdata(pdev
);
151 err
= strict_strtol(buf
, 10, &val
);
156 ep93xx_pwm_disable(pwm
);
157 } else if (val
<= (clk_get_rate(pwm
->clk
) / 2)) {
160 val
= (clk_get_rate(pwm
->clk
) / val
) - 1;
161 if (val
> EP93XX_PWM_MAX_COUNT
)
162 val
= EP93XX_PWM_MAX_COUNT
;
166 term
= ep93xx_pwm_read_tc(pwm
);
167 duty
= ((val
+ 1) * pwm
->duty_percent
/ 100) - 1;
169 /* If pwm is running, order is important */
171 ep93xx_pwm_write_tc(pwm
, val
);
172 ep93xx_pwm_write_dc(pwm
, duty
);
174 ep93xx_pwm_write_dc(pwm
, duty
);
175 ep93xx_pwm_write_tc(pwm
, val
);
178 if (!ep93xx_pwm_is_enabled(pwm
))
179 ep93xx_pwm_enable(pwm
);
187 static ssize_t
ep93xx_pwm_get_duty_percent(struct device
*dev
,
188 struct device_attribute
*attr
, char *buf
)
190 struct platform_device
*pdev
= to_platform_device(dev
);
191 struct ep93xx_pwm
*pwm
= platform_get_drvdata(pdev
);
193 return sprintf(buf
, "%d\n", pwm
->duty_percent
);
196 static ssize_t
ep93xx_pwm_set_duty_percent(struct device
*dev
,
197 struct device_attribute
*attr
, const char *buf
, size_t count
)
199 struct platform_device
*pdev
= to_platform_device(dev
);
200 struct ep93xx_pwm
*pwm
= platform_get_drvdata(pdev
);
204 err
= strict_strtol(buf
, 10, &val
);
208 if (val
> 0 && val
< 100) {
209 u32 term
= ep93xx_pwm_read_tc(pwm
);
210 ep93xx_pwm_write_dc(pwm
, ((term
+ 1) * val
/ 100) - 1);
211 pwm
->duty_percent
= val
;
218 static ssize_t
ep93xx_pwm_get_invert(struct device
*dev
,
219 struct device_attribute
*attr
, char *buf
)
221 struct platform_device
*pdev
= to_platform_device(dev
);
222 struct ep93xx_pwm
*pwm
= platform_get_drvdata(pdev
);
224 return sprintf(buf
, "%d\n", ep93xx_pwm_is_inverted(pwm
));
227 static ssize_t
ep93xx_pwm_set_invert(struct device
*dev
,
228 struct device_attribute
*attr
, const char *buf
, size_t count
)
230 struct platform_device
*pdev
= to_platform_device(dev
);
231 struct ep93xx_pwm
*pwm
= platform_get_drvdata(pdev
);
235 err
= strict_strtol(buf
, 10, &val
);
240 ep93xx_pwm_normal(pwm
);
242 ep93xx_pwm_invert(pwm
);
249 static DEVICE_ATTR(min_freq
, S_IRUGO
, ep93xx_pwm_get_min_freq
, NULL
);
250 static DEVICE_ATTR(max_freq
, S_IRUGO
, ep93xx_pwm_get_max_freq
, NULL
);
251 static DEVICE_ATTR(freq
, S_IWUGO
| S_IRUGO
,
252 ep93xx_pwm_get_freq
, ep93xx_pwm_set_freq
);
253 static DEVICE_ATTR(duty_percent
, S_IWUGO
| S_IRUGO
,
254 ep93xx_pwm_get_duty_percent
, ep93xx_pwm_set_duty_percent
);
255 static DEVICE_ATTR(invert
, S_IWUGO
| S_IRUGO
,
256 ep93xx_pwm_get_invert
, ep93xx_pwm_set_invert
);
258 static struct attribute
*ep93xx_pwm_attrs
[] = {
259 &dev_attr_min_freq
.attr
,
260 &dev_attr_max_freq
.attr
,
262 &dev_attr_duty_percent
.attr
,
263 &dev_attr_invert
.attr
,
267 static const struct attribute_group ep93xx_pwm_sysfs_files
= {
268 .attrs
= ep93xx_pwm_attrs
,
271 static int __init
ep93xx_pwm_probe(struct platform_device
*pdev
)
273 struct ep93xx_pwm
*pwm
;
274 struct resource
*res
;
277 err
= ep93xx_pwm_acquire_gpio(pdev
);
281 pwm
= kzalloc(sizeof(struct ep93xx_pwm
), GFP_KERNEL
);
287 res
= platform_get_resource(pdev
, IORESOURCE_MEM
, 0);
290 goto fail_no_mem_resource
;
293 res
= request_mem_region(res
->start
, resource_size(res
), pdev
->name
);
296 goto fail_no_mem_resource
;
299 pwm
->mmio_base
= ioremap(res
->start
, resource_size(res
));
300 if (pwm
->mmio_base
== NULL
) {
302 goto fail_no_ioremap
;
305 err
= sysfs_create_group(&pdev
->dev
.kobj
, &ep93xx_pwm_sysfs_files
);
309 pwm
->clk
= clk_get(&pdev
->dev
, "pwm_clk");
310 if (IS_ERR(pwm
->clk
)) {
311 err
= PTR_ERR(pwm
->clk
);
315 pwm
->duty_percent
= 50;
317 platform_set_drvdata(pdev
, pwm
);
319 /* disable pwm at startup. Avoids zero value. */
320 ep93xx_pwm_disable(pwm
);
321 ep93xx_pwm_write_tc(pwm
, EP93XX_PWM_MAX_COUNT
);
322 ep93xx_pwm_write_dc(pwm
, EP93XX_PWM_MAX_COUNT
/ 2);
324 clk_enable(pwm
->clk
);
329 sysfs_remove_group(&pdev
->dev
.kobj
, &ep93xx_pwm_sysfs_files
);
331 iounmap(pwm
->mmio_base
);
333 release_mem_region(res
->start
, resource_size(res
));
334 fail_no_mem_resource
:
337 ep93xx_pwm_release_gpio(pdev
);
341 static int __exit
ep93xx_pwm_remove(struct platform_device
*pdev
)
343 struct ep93xx_pwm
*pwm
= platform_get_drvdata(pdev
);
344 struct resource
*res
= platform_get_resource(pdev
, IORESOURCE_MEM
, 0);
346 ep93xx_pwm_disable(pwm
);
347 clk_disable(pwm
->clk
);
349 platform_set_drvdata(pdev
, NULL
);
350 sysfs_remove_group(&pdev
->dev
.kobj
, &ep93xx_pwm_sysfs_files
);
351 iounmap(pwm
->mmio_base
);
352 release_mem_region(res
->start
, resource_size(res
));
354 ep93xx_pwm_release_gpio(pdev
);
359 static struct platform_driver ep93xx_pwm_driver
= {
361 .name
= "ep93xx-pwm",
362 .owner
= THIS_MODULE
,
364 .remove
= __exit_p(ep93xx_pwm_remove
),
367 static int __init
ep93xx_pwm_init(void)
369 return platform_driver_probe(&ep93xx_pwm_driver
, ep93xx_pwm_probe
);
372 static void __exit
ep93xx_pwm_exit(void)
374 platform_driver_unregister(&ep93xx_pwm_driver
);
377 module_init(ep93xx_pwm_init
);
378 module_exit(ep93xx_pwm_exit
);
380 MODULE_AUTHOR("Matthieu Crapet <mcrapet@gmail.com>, "
381 "H Hartley Sweeten <hsweeten@visionengravers.com>");
382 MODULE_DESCRIPTION("EP93xx PWM driver");
383 MODULE_LICENSE("GPL");
384 MODULE_ALIAS("platform:ep93xx-pwm");