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 <linux/sched.h>
20 #include <asm/delay.h>
21 #include <mach/board.h>
22 #include <mach/dmtimer.h>
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
;
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
)
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
);
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
)
82 omap_dm_timer_enable(led
->intensity_timer
);
83 omap_dm_timer_set_source(led
->intensity_timer
, OMAP_TIMER_SRC_32_KHZ
);
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
)
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
)
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
);
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
);
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
);
181 val
= simple_strtoul(buf
, &after
, 10);
183 if (*after
&& isspace(*after
))
187 led
->on_period
= val
;
188 omap_pwm_led_set_blink(led
);
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
);
215 val
= simple_strtoul(buf
, &after
, 10);
217 if (*after
&& isspace(*after
))
221 led
->off_period
= val
;
222 omap_pwm_led_set_blink(led
);
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
;
240 led
= kzalloc(sizeof(struct omap_pwm_led
), GFP_KERNEL
);
242 dev_err(&pdev
->dev
, "No memory for device\n");
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
;
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
);
260 dev_err(&pdev
->dev
, "led_classdev_register failed\n");
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");
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");
280 omap_dm_timer_disable(led
->blink_timer
);
282 ret
= device_create_file(led
->cdev
.dev
,
283 &dev_attr_on_period
);
287 ret
= device_create_file(led
->cdev
.dev
,
288 &dev_attr_off_period
);
297 device_remove_file(led
->cdev
.dev
,
298 &dev_attr_on_period
);
300 dev_err(&pdev
->dev
, "failed to create device file(s)\n");
302 omap_dm_timer_free(led
->intensity_timer
);
304 led_classdev_unregister(&led
->cdev
);
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
);
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
);
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
);
346 #define omap_pwm_led_suspend NULL
347 #define omap_pwm_led_resume NULL
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
,
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");