]> www.pilppa.org Git - linux-2.6-omap-h63xx.git/blob - drivers/leds/leds-omap-pwm.c
Merge omap-upstream
[linux-2.6-omap-h63xx.git] / drivers / leds / leds-omap-pwm.c
1 /* drivers/leds/leds-omap_pwm.c
2  *
3  * Driver to blink LEDs using OMAP PWM timers
4  *
5  * Copyright (C) 2006 Nokia Corporation
6  * Author: Timo Teras
7  *
8  * This program is free software; you can redistribute it and/or modify
9  * it under the terms of the GNU General Public License version 2 as
10  * published by the Free Software Foundation.
11 */
12
13 #include <linux/kernel.h>
14 #include <linux/init.h>
15 #include <linux/err.h>
16 #include <linux/platform_device.h>
17 #include <linux/leds.h>
18 #include <linux/ctype.h>
19 #include <asm/delay.h>
20 #include <asm/arch/board.h>
21 #include <asm/arch/dmtimer.h>
22
23 struct omap_pwm_led {
24         struct led_classdev cdev;
25         struct omap_pwm_led_platform_data *pdata;
26         struct omap_dm_timer *intensity_timer;
27         struct omap_dm_timer *blink_timer;
28         int powered;
29         unsigned int on_period, off_period;
30 };
31
32 static inline struct omap_pwm_led *pdev_to_omap_pwm_led(struct platform_device *pdev)
33 {
34         return platform_get_drvdata(pdev);
35 }
36
37 static inline struct omap_pwm_led *cdev_to_omap_pwm_led(struct led_classdev *led_cdev)
38 {
39         return container_of(led_cdev, struct omap_pwm_led, cdev);
40 }
41
42 static void omap_pwm_led_set_blink(struct omap_pwm_led *led)
43 {
44         if (!led->powered)
45                 return;
46
47         if (led->on_period != 0 && led->off_period != 0) {
48                 unsigned long load_reg, cmp_reg;
49
50                 load_reg = 32768 * (led->on_period + led->off_period) / 1000;
51                 cmp_reg = 32768 * led->on_period / 1000;
52
53                 omap_dm_timer_stop(led->blink_timer);
54                 omap_dm_timer_set_load(led->blink_timer, 1, -load_reg);
55                 omap_dm_timer_set_match(led->blink_timer, 1, -cmp_reg);
56                 omap_dm_timer_set_pwm(led->blink_timer, 1, 1,
57                                       OMAP_TIMER_TRIGGER_OVERFLOW_AND_COMPARE);
58                 omap_dm_timer_write_counter(led->blink_timer, -2);
59                 omap_dm_timer_start(led->blink_timer);
60         } else {
61                 omap_dm_timer_set_pwm(led->blink_timer, 1, 1,
62                                       OMAP_TIMER_TRIGGER_OVERFLOW_AND_COMPARE);
63                 omap_dm_timer_stop(led->blink_timer);
64         }
65 }
66
67 static void omap_pwm_led_power_on(struct omap_pwm_led *led)
68 {
69         if (led->powered)
70                 return;
71         led->powered = 1;
72
73         /* Select clock */
74         omap_dm_timer_enable(led->intensity_timer);
75         omap_dm_timer_set_source(led->intensity_timer, OMAP_TIMER_SRC_32_KHZ);
76
77         /* Turn voltage on */
78         if (led->pdata->set_power != NULL)
79                 led->pdata->set_power(led->pdata, 1);
80
81         /* Enable PWM timers */
82         if (led->blink_timer != NULL) {
83                 omap_dm_timer_enable(led->blink_timer);
84                 omap_dm_timer_set_source(led->blink_timer,
85                                          OMAP_TIMER_SRC_32_KHZ);
86                 omap_pwm_led_set_blink(led);
87         }
88
89         omap_dm_timer_set_load(led->intensity_timer, 1, 0xffffff00);
90 }
91
92 static void omap_pwm_led_power_off(struct omap_pwm_led *led)
93 {
94         if (!led->powered)
95                 return;
96         led->powered = 0;
97
98         /* Everything off */
99         omap_dm_timer_stop(led->intensity_timer);
100         omap_dm_timer_disable(led->intensity_timer);
101
102         if (led->blink_timer != NULL) {
103                 omap_dm_timer_stop(led->blink_timer);
104                 omap_dm_timer_disable(led->blink_timer);
105         }
106
107         if (led->pdata->set_power != NULL)
108                 led->pdata->set_power(led->pdata, 0);
109 }
110
111 static void omap_pwm_led_set_pwm_cycle(struct omap_pwm_led *led, int cycle)
112 {
113         int n;
114
115         if (cycle == 0)
116                 n = 0xff;
117         else    n = cycle - 1;
118
119         if (cycle == LED_FULL) {
120                 omap_dm_timer_set_pwm(led->intensity_timer, 1, 1,
121                                       OMAP_TIMER_TRIGGER_OVERFLOW_AND_COMPARE);
122                 omap_dm_timer_stop(led->intensity_timer);
123         } else {
124                 omap_dm_timer_set_pwm(led->intensity_timer, 0, 1,
125                                       OMAP_TIMER_TRIGGER_OVERFLOW_AND_COMPARE);
126                 omap_dm_timer_set_match(led->intensity_timer, 1,
127                                         (0xffffff00) | cycle);
128                 omap_dm_timer_start(led->intensity_timer);
129         }
130 }
131
132 static void omap_pwm_led_set(struct led_classdev *led_cdev,
133                              enum led_brightness value)
134 {
135         struct omap_pwm_led *led = cdev_to_omap_pwm_led(led_cdev);
136
137         if (value != LED_OFF) {
138                 omap_pwm_led_power_on(led);
139                 omap_pwm_led_set_pwm_cycle(led, value);
140         } else {
141                 omap_pwm_led_power_off(led);
142         }
143 }
144
145 static ssize_t omap_pwm_led_on_period_show(struct device *dev,
146                                 struct device_attribute *attr, char *buf)
147 {
148         struct led_classdev *led_cdev = dev_get_drvdata(dev);
149         struct omap_pwm_led *led = cdev_to_omap_pwm_led(led_cdev);
150
151         return sprintf(buf, "%u\n", led->on_period) + 1;
152 }
153
154 static ssize_t omap_pwm_led_on_period_store(struct device *dev,
155                                 struct device_attribute *attr,
156                                 const char *buf, size_t size)
157 {
158         struct led_classdev *led_cdev = dev_get_drvdata(dev);
159         struct omap_pwm_led *led = cdev_to_omap_pwm_led(led_cdev);
160         int ret = -EINVAL;
161         unsigned long val;
162         char *after;
163         size_t count;
164
165         val = simple_strtoul(buf, &after, 10);
166         count = after - buf;
167         if (*after && isspace(*after))
168                 count++;
169
170         if (count == size) {
171                 led->on_period = val;
172                 omap_pwm_led_set_blink(led);
173                 ret = count;
174         }
175
176         return ret;
177 }
178
179 static ssize_t omap_pwm_led_off_period_show(struct device *dev,
180                                 struct device_attribute *attr, char *buf)
181 {
182         struct led_classdev *led_cdev = dev_get_drvdata(dev);
183         struct omap_pwm_led *led = cdev_to_omap_pwm_led(led_cdev);
184
185         return sprintf(buf, "%u\n", led->off_period) + 1;
186 }
187
188 static ssize_t omap_pwm_led_off_period_store(struct device *dev,
189                                         struct device_attribute *attr,
190                                         const char *buf, size_t size)
191 {
192         struct led_classdev *led_cdev = dev_get_drvdata(dev);
193         struct omap_pwm_led *led = cdev_to_omap_pwm_led(led_cdev);
194         int ret = -EINVAL;
195         unsigned long val;
196         char *after;
197         size_t count;
198
199         val = simple_strtoul(buf, &after, 10);
200         count = after - buf;
201         if (*after && isspace(*after))
202                 count++;
203
204         if (count == size) {
205                 led->off_period = val;
206                 omap_pwm_led_set_blink(led);
207                 ret = count;
208         }
209
210         return ret;
211 }
212
213 static DEVICE_ATTR(on_period, 0644, omap_pwm_led_on_period_show,
214                                 omap_pwm_led_on_period_store);
215 static DEVICE_ATTR(off_period, 0644, omap_pwm_led_off_period_show,
216                                 omap_pwm_led_off_period_store);
217
218 static int omap_pwm_led_probe(struct platform_device *pdev)
219 {
220         struct omap_pwm_led_platform_data *pdata = pdev->dev.platform_data;
221         struct omap_pwm_led *led;
222         int ret;
223
224         led = kzalloc(sizeof(struct omap_pwm_led), GFP_KERNEL);
225         if (led == NULL) {
226                 dev_err(&pdev->dev, "No memory for device\n");
227                 return -ENOMEM;
228         }
229
230         platform_set_drvdata(pdev, led);
231         led->cdev.brightness_set = omap_pwm_led_set;
232         led->cdev.default_trigger = NULL;
233         led->cdev.name = pdata->name;
234         led->pdata = pdata;
235
236         dev_info(&pdev->dev, "OMAP PWM LED (%s) at GP timer %d/%d\n",
237                  pdata->name, pdata->intensity_timer, pdata->blink_timer);
238
239         /* register our new led device */
240         ret = led_classdev_register(&pdev->dev, &led->cdev);
241         if (ret < 0) {
242                 dev_err(&pdev->dev, "led_classdev_register failed\n");
243                 goto error_classdev;
244         }
245
246         /* get related dm timers */
247         led->intensity_timer = omap_dm_timer_request_specific(pdata->intensity_timer);
248         if (led->intensity_timer == NULL) {
249                 dev_err(&pdev->dev, "failed to request intensity pwm timer\n");
250                 ret = -ENODEV;
251                 goto error_intensity;
252         }
253         omap_dm_timer_disable(led->intensity_timer);
254
255         if (pdata->blink_timer != 0) {
256                 led->blink_timer = omap_dm_timer_request_specific(pdata->blink_timer);
257                 if (led->blink_timer == NULL) {
258                         dev_err(&pdev->dev, "failed to request blinking pwm timer\n");
259                         ret = -ENODEV;
260                         goto error_blink1;
261                 }
262                 omap_dm_timer_disable(led->blink_timer);
263
264                 ret = device_create_file(led->cdev.dev,
265                                                &dev_attr_on_period);
266                 if(ret)
267                         goto error_blink2;
268
269                 ret = device_create_file(led->cdev.dev,
270                                         &dev_attr_off_period);
271                 if(ret)
272                         goto error_blink3;
273
274         }
275
276         return 0;
277
278 error_blink3:
279         device_remove_file(led->cdev.dev,
280                                  &dev_attr_on_period);
281 error_blink2:
282         dev_err(&pdev->dev, "failed to create device file(s)\n");
283 error_blink1:
284         omap_dm_timer_free(led->intensity_timer);
285 error_intensity:
286         led_classdev_unregister(&led->cdev);
287 error_classdev:
288         kfree(led);
289         return ret;
290 }
291
292 static int omap_pwm_led_remove(struct platform_device *pdev)
293 {
294         struct omap_pwm_led *led = pdev_to_omap_pwm_led(pdev);
295
296         device_remove_file(led->cdev.dev,
297                                  &dev_attr_on_period);
298         device_remove_file(led->cdev.dev,
299                                  &dev_attr_off_period);
300         led_classdev_unregister(&led->cdev);
301
302         omap_pwm_led_set(&led->cdev, LED_OFF);
303         if (led->blink_timer != NULL)
304                 omap_dm_timer_free(led->blink_timer);
305         omap_dm_timer_free(led->intensity_timer);
306         kfree(led);
307
308         return 0;
309 }
310
311 #ifdef CONFIG_PM
312 static int omap_pwm_led_suspend(struct platform_device *pdev, pm_message_t state)
313 {
314         struct omap_pwm_led *led = pdev_to_omap_pwm_led(pdev);
315
316         led_classdev_suspend(&led->cdev);
317         return 0;
318 }
319
320 static int omap_pwm_led_resume(struct platform_device *pdev)
321 {
322         struct omap_pwm_led *led = pdev_to_omap_pwm_led(pdev);
323
324         led_classdev_resume(&led->cdev);
325         return 0;
326 }
327 #else
328 #define omap_pwm_led_suspend NULL
329 #define omap_pwm_led_resume NULL
330 #endif
331
332 static struct platform_driver omap_pwm_led_driver = {
333         .probe          = omap_pwm_led_probe,
334         .remove         = omap_pwm_led_remove,
335         .suspend        = omap_pwm_led_suspend,
336         .resume         = omap_pwm_led_resume,
337         .driver         = {
338                 .name           = "omap_pwm_led",
339                 .owner          = THIS_MODULE,
340         },
341 };
342
343 static int __init omap_pwm_led_init(void)
344 {
345         return platform_driver_register(&omap_pwm_led_driver);
346 }
347
348 static void __exit omap_pwm_led_exit(void)
349 {
350         platform_driver_unregister(&omap_pwm_led_driver);
351 }
352
353 module_init(omap_pwm_led_init);
354 module_exit(omap_pwm_led_exit);
355
356 MODULE_AUTHOR("Timo Teras");
357 MODULE_DESCRIPTION("OMAP PWM LED driver");
358 MODULE_LICENSE("GPL");