OMAP2/3: Add omap_type() for determining GP/EMU/HS
[linux-ginger.git] / drivers / leds / leds-omap-pwm.c
blob57eb38359f3310fa4cd3e89c5503f0bcaec06cf2
1 /* drivers/leds/leds-omap_pwm.c
3 * Driver to blink LEDs using OMAP PWM timers
5 * Copyright (C) 2006 Nokia Corporation
6 * Author: Timo Teras
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.
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>
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;
35 static inline struct omap_pwm_led *pdev_to_omap_pwm_led(struct platform_device *pdev)
37 return platform_get_drvdata(pdev);
40 static inline struct omap_pwm_led *cdev_to_omap_pwm_led(struct led_classdev *led_cdev)
42 return container_of(led_cdev, struct omap_pwm_led, cdev);
45 static inline struct omap_pwm_led *work_to_omap_pwm_led(struct work_struct *work)
47 return container_of(work, struct omap_pwm_led, work);
50 static void omap_pwm_led_set_blink(struct omap_pwm_led *led)
52 if (!led->powered)
53 return;
55 if (led->on_period != 0 && led->off_period != 0) {
56 unsigned long load_reg, cmp_reg;
58 load_reg = 32768 * (led->on_period + led->off_period) / 1000;
59 cmp_reg = 32768 * led->on_period / 1000;
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);
75 static void omap_pwm_led_power_on(struct omap_pwm_led *led)
77 if (led->powered)
78 return;
79 led->powered = 1;
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);
85 /* Turn voltage on */
86 if (led->pdata->set_power != NULL)
87 led->pdata->set_power(led->pdata, 1);
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);
97 omap_dm_timer_set_load(led->intensity_timer, 1, 0xffffff00);
100 static void omap_pwm_led_power_off(struct omap_pwm_led *led)
102 if (!led->powered)
103 return;
104 led->powered = 0;
106 /* Everything off */
107 omap_dm_timer_stop(led->intensity_timer);
108 omap_dm_timer_disable(led->intensity_timer);
110 if (led->blink_timer != NULL) {
111 omap_dm_timer_stop(led->blink_timer);
112 omap_dm_timer_disable(led->blink_timer);
115 if (led->pdata->set_power != NULL)
116 led->pdata->set_power(led->pdata, 0);
119 static void omap_pwm_led_set_pwm_cycle(struct omap_pwm_led *led, int cycle)
121 int n;
123 if (cycle == 0)
124 n = 0xff;
125 else n = cycle - 1;
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);
140 static void omap_pwm_led_set(struct led_classdev *led_cdev,
141 enum led_brightness value)
143 struct omap_pwm_led *led = cdev_to_omap_pwm_led(led_cdev);
145 led->brightness = value;
146 schedule_work(&led->work);
149 static void omap_pwm_led_work(struct work_struct *work)
151 struct omap_pwm_led *led = work_to_omap_pwm_led(work);
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);
161 static ssize_t omap_pwm_led_on_period_show(struct device *dev,
162 struct device_attribute *attr, char *buf)
164 struct led_classdev *led_cdev = dev_get_drvdata(dev);
165 struct omap_pwm_led *led = cdev_to_omap_pwm_led(led_cdev);
167 return sprintf(buf, "%u\n", led->on_period) + 1;
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)
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;
181 val = simple_strtoul(buf, &after, 10);
182 count = after - buf;
183 if (*after && isspace(*after))
184 count++;
186 if (count == size) {
187 led->on_period = val;
188 omap_pwm_led_set_blink(led);
189 ret = count;
192 return ret;
195 static ssize_t omap_pwm_led_off_period_show(struct device *dev,
196 struct device_attribute *attr, char *buf)
198 struct led_classdev *led_cdev = dev_get_drvdata(dev);
199 struct omap_pwm_led *led = cdev_to_omap_pwm_led(led_cdev);
201 return sprintf(buf, "%u\n", led->off_period) + 1;
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)
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;
215 val = simple_strtoul(buf, &after, 10);
216 count = after - buf;
217 if (*after && isspace(*after))
218 count++;
220 if (count == size) {
221 led->off_period = val;
222 omap_pwm_led_set_blink(led);
223 ret = count;
226 return ret;
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);
234 static int omap_pwm_led_probe(struct platform_device *pdev)
236 struct omap_pwm_led_platform_data *pdata = pdev->dev.platform_data;
237 struct omap_pwm_led *led;
238 int ret;
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;
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);
254 dev_info(&pdev->dev, "OMAP PWM LED (%s) at GP timer %d/%d\n",
255 pdata->name, pdata->intensity_timer, pdata->blink_timer);
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;
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;
271 omap_dm_timer_disable(led->intensity_timer);
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;
280 omap_dm_timer_disable(led->blink_timer);
282 ret = device_create_file(led->cdev.dev,
283 &dev_attr_on_period);
284 if(ret)
285 goto error_blink2;
287 ret = device_create_file(led->cdev.dev,
288 &dev_attr_off_period);
289 if(ret)
290 goto error_blink3;
294 return 0;
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;
310 static int omap_pwm_led_remove(struct platform_device *pdev)
312 struct omap_pwm_led *led = pdev_to_omap_pwm_led(pdev);
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);
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);
326 return 0;
329 #ifdef CONFIG_PM
330 static int omap_pwm_led_suspend(struct platform_device *pdev, pm_message_t state)
332 struct omap_pwm_led *led = pdev_to_omap_pwm_led(pdev);
334 led_classdev_suspend(&led->cdev);
335 return 0;
338 static int omap_pwm_led_resume(struct platform_device *pdev)
340 struct omap_pwm_led *led = pdev_to_omap_pwm_led(pdev);
342 led_classdev_resume(&led->cdev);
343 return 0;
345 #else
346 #define omap_pwm_led_suspend NULL
347 #define omap_pwm_led_resume NULL
348 #endif
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,
361 static int __init omap_pwm_led_init(void)
363 return platform_driver_register(&omap_pwm_led_driver);
366 static void __exit omap_pwm_led_exit(void)
368 platform_driver_unregister(&omap_pwm_led_driver);
371 module_init(omap_pwm_led_init);
372 module_exit(omap_pwm_led_exit);
374 MODULE_AUTHOR("Timo Teras");
375 MODULE_DESCRIPTION("OMAP PWM LED driver");
376 MODULE_LICENSE("GPL");