PNP: support optional IRQ resources
[linux-2.6] / drivers / leds / leds-gpio.c
1 /*
2  * LEDs driver for GPIOs
3  *
4  * Copyright (C) 2007 8D Technologies inc.
5  * Raphael Assenat <raph@8d.com>
6  *
7  * This program is free software; you can redistribute it and/or modify
8  * it under the terms of the GNU General Public License version 2 as
9  * published by the Free Software Foundation.
10  *
11  */
12 #include <linux/kernel.h>
13 #include <linux/init.h>
14 #include <linux/platform_device.h>
15 #include <linux/leds.h>
16 #include <linux/workqueue.h>
17
18 #include <asm/gpio.h>
19
20 struct gpio_led_data {
21         struct led_classdev cdev;
22         unsigned gpio;
23         struct work_struct work;
24         u8 new_level;
25         u8 can_sleep;
26         u8 active_low;
27         int (*platform_gpio_blink_set)(unsigned gpio,
28                         unsigned long *delay_on, unsigned long *delay_off);
29 };
30
31 static void gpio_led_work(struct work_struct *work)
32 {
33         struct gpio_led_data    *led_dat =
34                 container_of(work, struct gpio_led_data, work);
35
36         gpio_set_value_cansleep(led_dat->gpio, led_dat->new_level);
37 }
38
39 static void gpio_led_set(struct led_classdev *led_cdev,
40         enum led_brightness value)
41 {
42         struct gpio_led_data *led_dat =
43                 container_of(led_cdev, struct gpio_led_data, cdev);
44         int level;
45
46         if (value == LED_OFF)
47                 level = 0;
48         else
49                 level = 1;
50
51         if (led_dat->active_low)
52                 level = !level;
53
54         /* Setting GPIOs with I2C/etc requires a task context, and we don't
55          * seem to have a reliable way to know if we're already in one; so
56          * let's just assume the worst.
57          */
58         if (led_dat->can_sleep) {
59                 led_dat->new_level = level;
60                 schedule_work(&led_dat->work);
61         } else
62                 gpio_set_value(led_dat->gpio, level);
63 }
64
65 static int gpio_blink_set(struct led_classdev *led_cdev,
66         unsigned long *delay_on, unsigned long *delay_off)
67 {
68         struct gpio_led_data *led_dat =
69                 container_of(led_cdev, struct gpio_led_data, cdev);
70
71         return led_dat->platform_gpio_blink_set(led_dat->gpio, delay_on, delay_off);
72 }
73
74 static int gpio_led_probe(struct platform_device *pdev)
75 {
76         struct gpio_led_platform_data *pdata = pdev->dev.platform_data;
77         struct gpio_led *cur_led;
78         struct gpio_led_data *leds_data, *led_dat;
79         int i, ret = 0;
80
81         if (!pdata)
82                 return -EBUSY;
83
84         leds_data = kzalloc(sizeof(struct gpio_led_data) * pdata->num_leds,
85                                 GFP_KERNEL);
86         if (!leds_data)
87                 return -ENOMEM;
88
89         for (i = 0; i < pdata->num_leds; i++) {
90                 cur_led = &pdata->leds[i];
91                 led_dat = &leds_data[i];
92
93                 ret = gpio_request(cur_led->gpio, cur_led->name);
94                 if (ret < 0)
95                         goto err;
96
97                 led_dat->cdev.name = cur_led->name;
98                 led_dat->cdev.default_trigger = cur_led->default_trigger;
99                 led_dat->gpio = cur_led->gpio;
100                 led_dat->can_sleep = gpio_cansleep(cur_led->gpio);
101                 led_dat->active_low = cur_led->active_low;
102                 if (pdata->gpio_blink_set) {
103                         led_dat->platform_gpio_blink_set = pdata->gpio_blink_set;
104                         led_dat->cdev.blink_set = gpio_blink_set;
105                 }
106                 led_dat->cdev.brightness_set = gpio_led_set;
107                 led_dat->cdev.brightness = LED_OFF;
108
109                 gpio_direction_output(led_dat->gpio, led_dat->active_low);
110
111                 INIT_WORK(&led_dat->work, gpio_led_work);
112
113                 ret = led_classdev_register(&pdev->dev, &led_dat->cdev);
114                 if (ret < 0) {
115                         gpio_free(led_dat->gpio);
116                         goto err;
117                 }
118         }
119
120         platform_set_drvdata(pdev, leds_data);
121
122         return 0;
123
124 err:
125         if (i > 0) {
126                 for (i = i - 1; i >= 0; i--) {
127                         led_classdev_unregister(&leds_data[i].cdev);
128                         cancel_work_sync(&leds_data[i].work);
129                         gpio_free(leds_data[i].gpio);
130                 }
131         }
132
133         kfree(leds_data);
134
135         return ret;
136 }
137
138 static int __devexit gpio_led_remove(struct platform_device *pdev)
139 {
140         int i;
141         struct gpio_led_platform_data *pdata = pdev->dev.platform_data;
142         struct gpio_led_data *leds_data;
143
144         leds_data = platform_get_drvdata(pdev);
145
146         for (i = 0; i < pdata->num_leds; i++) {
147                 led_classdev_unregister(&leds_data[i].cdev);
148                 cancel_work_sync(&leds_data[i].work);
149                 gpio_free(leds_data[i].gpio);
150         }
151
152         kfree(leds_data);
153
154         return 0;
155 }
156
157 #ifdef CONFIG_PM
158 static int gpio_led_suspend(struct platform_device *pdev, pm_message_t state)
159 {
160         struct gpio_led_platform_data *pdata = pdev->dev.platform_data;
161         struct gpio_led_data *leds_data;
162         int i;
163
164         leds_data = platform_get_drvdata(pdev);
165
166         for (i = 0; i < pdata->num_leds; i++)
167                 led_classdev_suspend(&leds_data[i].cdev);
168
169         return 0;
170 }
171
172 static int gpio_led_resume(struct platform_device *pdev)
173 {
174         struct gpio_led_platform_data *pdata = pdev->dev.platform_data;
175         struct gpio_led_data *leds_data;
176         int i;
177
178         leds_data = platform_get_drvdata(pdev);
179
180         for (i = 0; i < pdata->num_leds; i++)
181                 led_classdev_resume(&leds_data[i].cdev);
182
183         return 0;
184 }
185 #else
186 #define gpio_led_suspend NULL
187 #define gpio_led_resume NULL
188 #endif
189
190 static struct platform_driver gpio_led_driver = {
191         .probe          = gpio_led_probe,
192         .remove         = __devexit_p(gpio_led_remove),
193         .suspend        = gpio_led_suspend,
194         .resume         = gpio_led_resume,
195         .driver         = {
196                 .name   = "leds-gpio",
197                 .owner  = THIS_MODULE,
198         },
199 };
200
201 static int __init gpio_led_init(void)
202 {
203         return platform_driver_register(&gpio_led_driver);
204 }
205
206 static void __exit gpio_led_exit(void)
207 {
208         platform_driver_unregister(&gpio_led_driver);
209 }
210
211 module_init(gpio_led_init);
212 module_exit(gpio_led_exit);
213
214 MODULE_AUTHOR("Raphael Assenat <raph@8d.com>");
215 MODULE_DESCRIPTION("GPIO LED driver");
216 MODULE_LICENSE("GPL");
217 MODULE_ALIAS("platform:leds-gpio");