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