• Home
  • History
  • Annotate
  • Line#
  • Navigate
  • Raw
  • Download
  • only in /asuswrt-rt-n18u-9.0.0.4.380.2695/release/src-rt-6.x.4708/linux/linux-2.6.36/arch/arm/plat-pxa/
1/*
2 * linux/arch/arm/mach-pxa/pwm.c
3 *
4 * simple driver for PWM (Pulse Width Modulator) controller
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 version 2 as
8 * published by the Free Software Foundation.
9 *
10 * 2008-02-13	initial version
11 * 		eric miao <eric.miao@marvell.com>
12 */
13
14#include <linux/module.h>
15#include <linux/kernel.h>
16#include <linux/platform_device.h>
17#include <linux/slab.h>
18#include <linux/err.h>
19#include <linux/clk.h>
20#include <linux/io.h>
21#include <linux/pwm.h>
22
23#include <asm/div64.h>
24
25#define HAS_SECONDARY_PWM	0x10
26#define PWM_ID_BASE(d)		((d) & 0xf)
27
28static const struct platform_device_id pwm_id_table[] = {
29	/*   PWM    has_secondary_pwm? */
30	{ "pxa25x-pwm", 0 },
31	{ "pxa27x-pwm", 0 | HAS_SECONDARY_PWM },
32	{ "pxa168-pwm", 1 },
33	{ "pxa910-pwm", 1 },
34	{ },
35};
36MODULE_DEVICE_TABLE(platform, pwm_id_table);
37
38/* PWM registers and bits definitions */
39#define PWMCR		(0x00)
40#define PWMDCR		(0x04)
41#define PWMPCR		(0x08)
42
43#define PWMCR_SD	(1 << 6)
44#define PWMDCR_FD	(1 << 10)
45
46struct pwm_device {
47	struct list_head	node;
48	struct pwm_device	*secondary;
49	struct platform_device	*pdev;
50
51	const char	*label;
52	struct clk	*clk;
53	int		clk_enabled;
54	void __iomem	*mmio_base;
55
56	unsigned int	use_count;
57	unsigned int	pwm_id;
58};
59
60/*
61 * period_ns = 10^9 * (PRESCALE + 1) * (PV + 1) / PWM_CLK_RATE
62 * duty_ns   = 10^9 * (PRESCALE + 1) * DC / PWM_CLK_RATE
63 */
64int pwm_config(struct pwm_device *pwm, int duty_ns, int period_ns)
65{
66	unsigned long long c;
67	unsigned long period_cycles, prescale, pv, dc;
68
69	if (pwm == NULL || period_ns == 0 || duty_ns > period_ns)
70		return -EINVAL;
71
72	c = clk_get_rate(pwm->clk);
73	c = c * period_ns;
74	do_div(c, 1000000000);
75	period_cycles = c;
76
77	if (period_cycles < 1)
78		period_cycles = 1;
79	prescale = (period_cycles - 1) / 1024;
80	pv = period_cycles / (prescale + 1) - 1;
81
82	if (prescale > 63)
83		return -EINVAL;
84
85	if (duty_ns == period_ns)
86		dc = PWMDCR_FD;
87	else
88		dc = (pv + 1) * duty_ns / period_ns;
89
90	/* NOTE: the clock to PWM has to be enabled first
91	 * before writing to the registers
92	 */
93	clk_enable(pwm->clk);
94	__raw_writel(prescale, pwm->mmio_base + PWMCR);
95	__raw_writel(dc, pwm->mmio_base + PWMDCR);
96	__raw_writel(pv, pwm->mmio_base + PWMPCR);
97	clk_disable(pwm->clk);
98
99	return 0;
100}
101EXPORT_SYMBOL(pwm_config);
102
103int pwm_enable(struct pwm_device *pwm)
104{
105	int rc = 0;
106
107	if (!pwm->clk_enabled) {
108		rc = clk_enable(pwm->clk);
109		if (!rc)
110			pwm->clk_enabled = 1;
111	}
112	return rc;
113}
114EXPORT_SYMBOL(pwm_enable);
115
116void pwm_disable(struct pwm_device *pwm)
117{
118	if (pwm->clk_enabled) {
119		clk_disable(pwm->clk);
120		pwm->clk_enabled = 0;
121	}
122}
123EXPORT_SYMBOL(pwm_disable);
124
125static DEFINE_MUTEX(pwm_lock);
126static LIST_HEAD(pwm_list);
127
128struct pwm_device *pwm_request(int pwm_id, const char *label)
129{
130	struct pwm_device *pwm;
131	int found = 0;
132
133	mutex_lock(&pwm_lock);
134
135	list_for_each_entry(pwm, &pwm_list, node) {
136		if (pwm->pwm_id == pwm_id) {
137			found = 1;
138			break;
139		}
140	}
141
142	if (found) {
143		if (pwm->use_count == 0) {
144			pwm->use_count++;
145			pwm->label = label;
146		} else
147			pwm = ERR_PTR(-EBUSY);
148	} else
149		pwm = ERR_PTR(-ENOENT);
150
151	mutex_unlock(&pwm_lock);
152	return pwm;
153}
154EXPORT_SYMBOL(pwm_request);
155
156void pwm_free(struct pwm_device *pwm)
157{
158	mutex_lock(&pwm_lock);
159
160	if (pwm->use_count) {
161		pwm->use_count--;
162		pwm->label = NULL;
163	} else
164		pr_warning("PWM device already freed\n");
165
166	mutex_unlock(&pwm_lock);
167}
168EXPORT_SYMBOL(pwm_free);
169
170static inline void __add_pwm(struct pwm_device *pwm)
171{
172	mutex_lock(&pwm_lock);
173	list_add_tail(&pwm->node, &pwm_list);
174	mutex_unlock(&pwm_lock);
175}
176
177static int __devinit pwm_probe(struct platform_device *pdev)
178{
179	const struct platform_device_id *id = platform_get_device_id(pdev);
180	struct pwm_device *pwm, *secondary = NULL;
181	struct resource *r;
182	int ret = 0;
183
184	pwm = kzalloc(sizeof(struct pwm_device), GFP_KERNEL);
185	if (pwm == NULL) {
186		dev_err(&pdev->dev, "failed to allocate memory\n");
187		return -ENOMEM;
188	}
189
190	pwm->clk = clk_get(&pdev->dev, NULL);
191	if (IS_ERR(pwm->clk)) {
192		ret = PTR_ERR(pwm->clk);
193		goto err_free;
194	}
195	pwm->clk_enabled = 0;
196
197	pwm->use_count = 0;
198	pwm->pwm_id = PWM_ID_BASE(id->driver_data) + pdev->id;
199	pwm->pdev = pdev;
200
201	r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
202	if (r == NULL) {
203		dev_err(&pdev->dev, "no memory resource defined\n");
204		ret = -ENODEV;
205		goto err_free_clk;
206	}
207
208	r = request_mem_region(r->start, resource_size(r), pdev->name);
209	if (r == NULL) {
210		dev_err(&pdev->dev, "failed to request memory resource\n");
211		ret = -EBUSY;
212		goto err_free_clk;
213	}
214
215	pwm->mmio_base = ioremap(r->start, resource_size(r));
216	if (pwm->mmio_base == NULL) {
217		dev_err(&pdev->dev, "failed to ioremap() registers\n");
218		ret = -ENODEV;
219		goto err_free_mem;
220	}
221
222	if (id->driver_data & HAS_SECONDARY_PWM) {
223		secondary = kzalloc(sizeof(struct pwm_device), GFP_KERNEL);
224		if (secondary == NULL) {
225			ret = -ENOMEM;
226			goto err_free_mem;
227		}
228
229		*secondary = *pwm;
230		pwm->secondary = secondary;
231
232		/* registers for the second PWM has offset of 0x10 */
233		secondary->mmio_base = pwm->mmio_base + 0x10;
234		secondary->pwm_id = pdev->id + 2;
235	}
236
237	__add_pwm(pwm);
238	if (secondary)
239		__add_pwm(secondary);
240
241	platform_set_drvdata(pdev, pwm);
242	return 0;
243
244err_free_mem:
245	release_mem_region(r->start, resource_size(r));
246err_free_clk:
247	clk_put(pwm->clk);
248err_free:
249	kfree(pwm);
250	return ret;
251}
252
253static int __devexit pwm_remove(struct platform_device *pdev)
254{
255	struct pwm_device *pwm;
256	struct resource *r;
257
258	pwm = platform_get_drvdata(pdev);
259	if (pwm == NULL)
260		return -ENODEV;
261
262	mutex_lock(&pwm_lock);
263
264	if (pwm->secondary) {
265		list_del(&pwm->secondary->node);
266		kfree(pwm->secondary);
267	}
268
269	list_del(&pwm->node);
270	mutex_unlock(&pwm_lock);
271
272	iounmap(pwm->mmio_base);
273
274	r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
275	release_mem_region(r->start, resource_size(r));
276
277	clk_put(pwm->clk);
278	kfree(pwm);
279	return 0;
280}
281
282static struct platform_driver pwm_driver = {
283	.driver		= {
284		.name	= "pxa25x-pwm",
285		.owner	= THIS_MODULE,
286	},
287	.probe		= pwm_probe,
288	.remove		= __devexit_p(pwm_remove),
289	.id_table	= pwm_id_table,
290};
291
292static int __init pwm_init(void)
293{
294	return platform_driver_register(&pwm_driver);
295}
296arch_initcall(pwm_init);
297
298static void __exit pwm_exit(void)
299{
300	platform_driver_unregister(&pwm_driver);
301}
302module_exit(pwm_exit);
303
304MODULE_LICENSE("GPL v2");
305