1 /* drivers/leds/leds-omap_pwm.c
3 * Driver to blink LEDs using OMAP PWM timers
5 * Copyright (C) 2006 Nokia Corporation
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 <asm/delay.h>
20 #include <asm/arch/board.h>
21 #include <asm/arch/dmtimer.h>
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;
29 unsigned int on_period, off_period;
32 static inline struct omap_pwm_led *pdev_to_omap_pwm_led(struct platform_device *pdev)
34 return platform_get_drvdata(pdev);
37 static inline struct omap_pwm_led *cdev_to_omap_pwm_led(struct led_classdev *led_cdev)
39 return container_of(led_cdev, struct omap_pwm_led, cdev);
42 static void omap_pwm_led_set_blink(struct omap_pwm_led *led)
47 if (led->on_period != 0 && led->off_period != 0) {
48 unsigned long load_reg, cmp_reg;
50 load_reg = 32768 * (led->on_period + led->off_period) / 1000;
51 cmp_reg = 32768 * led->on_period / 1000;
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);
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);
67 static void omap_pwm_led_power_on(struct omap_pwm_led *led)
74 omap_dm_timer_enable(led->intensity_timer);
75 omap_dm_timer_set_source(led->intensity_timer, OMAP_TIMER_SRC_32_KHZ);
78 if (led->pdata->set_power != NULL)
79 led->pdata->set_power(led->pdata, 1);
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);
89 omap_dm_timer_set_load(led->intensity_timer, 1, 0xffffff00);
92 static void omap_pwm_led_power_off(struct omap_pwm_led *led)
99 omap_dm_timer_stop(led->intensity_timer);
100 omap_dm_timer_disable(led->intensity_timer);
102 if (led->blink_timer != NULL) {
103 omap_dm_timer_stop(led->blink_timer);
104 omap_dm_timer_disable(led->blink_timer);
107 if (led->pdata->set_power != NULL)
108 led->pdata->set_power(led->pdata, 0);
111 static void omap_pwm_led_set_pwm_cycle(struct omap_pwm_led *led, int cycle)
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);
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);
132 static void omap_pwm_led_set(struct led_classdev *led_cdev,
133 enum led_brightness value)
135 struct omap_pwm_led *led = cdev_to_omap_pwm_led(led_cdev);
137 if (value != LED_OFF) {
138 omap_pwm_led_power_on(led);
139 omap_pwm_led_set_pwm_cycle(led, value);
141 omap_pwm_led_power_off(led);
145 static ssize_t omap_pwm_led_on_period_show(struct device *dev,
146 struct device_attribute *attr, char *buf)
148 struct led_classdev *led_cdev = dev_get_drvdata(dev);
149 struct omap_pwm_led *led = cdev_to_omap_pwm_led(led_cdev);
151 return sprintf(buf, "%u\n", led->on_period) + 1;
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)
158 struct led_classdev *led_cdev = dev_get_drvdata(dev);
159 struct omap_pwm_led *led = cdev_to_omap_pwm_led(led_cdev);
165 val = simple_strtoul(buf, &after, 10);
167 if (*after && isspace(*after))
171 led->on_period = val;
172 omap_pwm_led_set_blink(led);
179 static ssize_t omap_pwm_led_off_period_show(struct device *dev,
180 struct device_attribute *attr, char *buf)
182 struct led_classdev *led_cdev = dev_get_drvdata(dev);
183 struct omap_pwm_led *led = cdev_to_omap_pwm_led(led_cdev);
185 return sprintf(buf, "%u\n", led->off_period) + 1;
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)
192 struct led_classdev *led_cdev = dev_get_drvdata(dev);
193 struct omap_pwm_led *led = cdev_to_omap_pwm_led(led_cdev);
199 val = simple_strtoul(buf, &after, 10);
201 if (*after && isspace(*after))
205 led->off_period = val;
206 omap_pwm_led_set_blink(led);
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);
218 static int omap_pwm_led_probe(struct platform_device *pdev)
220 struct omap_pwm_led_platform_data *pdata = pdev->dev.platform_data;
221 struct omap_pwm_led *led;
224 led = kzalloc(sizeof(struct omap_pwm_led), GFP_KERNEL);
226 dev_err(&pdev->dev, "No memory for device\n");
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;
236 dev_info(&pdev->dev, "OMAP PWM LED (%s) at GP timer %d/%d\n",
237 pdata->name, pdata->intensity_timer, pdata->blink_timer);
239 /* register our new led device */
240 ret = led_classdev_register(&pdev->dev, &led->cdev);
242 dev_err(&pdev->dev, "led_classdev_register failed\n");
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");
251 goto error_intensity;
253 omap_dm_timer_disable(led->intensity_timer);
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");
262 omap_dm_timer_disable(led->blink_timer);
264 ret = device_create_file(led->cdev.dev,
265 &dev_attr_on_period);
269 ret = device_create_file(led->cdev.dev,
270 &dev_attr_off_period);
279 device_remove_file(led->cdev.dev,
280 &dev_attr_on_period);
282 dev_err(&pdev->dev, "failed to create device file(s)\n");
284 omap_dm_timer_free(led->intensity_timer);
286 led_classdev_unregister(&led->cdev);
292 static int omap_pwm_led_remove(struct platform_device *pdev)
294 struct omap_pwm_led *led = pdev_to_omap_pwm_led(pdev);
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);
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);
312 static int omap_pwm_led_suspend(struct platform_device *pdev, pm_message_t state)
314 struct omap_pwm_led *led = pdev_to_omap_pwm_led(pdev);
316 led_classdev_suspend(&led->cdev);
320 static int omap_pwm_led_resume(struct platform_device *pdev)
322 struct omap_pwm_led *led = pdev_to_omap_pwm_led(pdev);
324 led_classdev_resume(&led->cdev);
328 #define omap_pwm_led_suspend NULL
329 #define omap_pwm_led_resume NULL
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,
338 .name = "omap_pwm_led",
339 .owner = THIS_MODULE,
343 static int __init omap_pwm_led_init(void)
345 return platform_driver_register(&omap_pwm_led_driver);
348 static void __exit omap_pwm_led_exit(void)
350 platform_driver_unregister(&omap_pwm_led_driver);
353 module_init(omap_pwm_led_init);
354 module_exit(omap_pwm_led_exit);
356 MODULE_AUTHOR("Timo Teras");
357 MODULE_DESCRIPTION("OMAP PWM LED driver");
358 MODULE_LICENSE("GPL");