leds: Add generic GPIO LED driver
[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 <asm/gpio.h>
17
18 struct gpio_led_data {
19         struct led_classdev cdev;
20         unsigned gpio;
21         u8 active_low;
22 };
23
24
25 static void gpio_led_set(struct led_classdev *led_cdev,
26         enum led_brightness value)
27 {
28         struct gpio_led_data *led_dat =
29                 container_of(led_cdev, struct gpio_led_data, cdev);
30         int level;
31
32         if (value == LED_OFF)
33                 level = 0;
34         else
35                 level = 1;
36
37         if (led_dat->active_low)
38                 level = !level;
39
40         gpio_set_value(led_dat->gpio, level);
41 }
42
43 static int __init gpio_led_probe(struct platform_device *pdev)
44 {
45         struct gpio_led_platform_data *pdata = pdev->dev.platform_data;
46         struct gpio_led *cur_led;
47         struct gpio_led_data *leds_data, *led_dat;
48         int i, ret = 0;
49
50         if (!pdata)
51                 return -EBUSY;
52
53         leds_data = kzalloc(sizeof(struct gpio_led_data) * pdata->num_leds,
54                                 GFP_KERNEL);
55         if (!leds_data)
56                 return -ENOMEM;
57
58         for (i = 0; i < pdata->num_leds; i++) {
59                 cur_led = &pdata->leds[i];
60                 led_dat = &leds_data[i];
61
62                 led_dat->cdev.name = cur_led->name;
63                 led_dat->cdev.default_trigger = cur_led->default_trigger;
64                 led_dat->gpio = cur_led->gpio;
65                 led_dat->active_low = cur_led->active_low;
66                 led_dat->cdev.brightness_set = gpio_led_set;
67                 led_dat->cdev.brightness = cur_led->active_low ? LED_FULL : LED_OFF;
68
69                 ret = gpio_request(led_dat->gpio, led_dat->cdev.name);
70                 if (ret < 0)
71                         goto err;
72
73                 gpio_direction_output(led_dat->gpio, led_dat->active_low);
74
75                 ret = led_classdev_register(&pdev->dev, &led_dat->cdev);
76                 if (ret < 0) {
77                         gpio_free(led_dat->gpio);
78                         goto err;
79                 }
80         }
81
82         platform_set_drvdata(pdev, leds_data);
83
84         return 0;
85
86 err:
87         if (i > 0) {
88                 for (i = i - 1; i >= 0; i--) {
89                         led_classdev_unregister(&leds_data[i].cdev);
90                         gpio_free(leds_data[i].gpio);
91                 }
92         }
93         kfree(leds_data);
94
95         return ret;
96 }
97
98 static int __exit gpio_led_remove(struct platform_device *pdev)
99 {
100         int i;
101         struct gpio_led_platform_data *pdata = pdev->dev.platform_data;
102         struct gpio_led_data *leds_data;
103
104         leds_data = platform_get_drvdata(pdev);
105
106         for (i = 0; i < pdata->num_leds; i++) {
107                 led_classdev_unregister(&leds_data[i].cdev);
108                 gpio_free(leds_data[i].gpio);
109         }
110         
111         kfree(leds_data);
112
113         return 0;
114 }
115
116 #ifdef CONFIG_PM
117 static int gpio_led_suspend(struct platform_device *pdev, pm_message_t state)
118 {
119         struct gpio_led_platform_data *pdata = pdev->dev.platform_data;
120         struct gpio_led_data *leds_data;
121         int i;
122         
123         leds_data = platform_get_drvdata(pdev);
124
125         for (i = 0; i < pdata->num_leds; i++)
126                 led_classdev_suspend(&leds_data[i].cdev);
127
128         return 0;
129 }
130
131 static int gpio_led_resume(struct platform_device *pdev)
132 {
133         struct gpio_led_platform_data *pdata = pdev->dev.platform_data;
134         struct gpio_led_data *leds_data;
135         int i;
136
137         leds_data = platform_get_drvdata(pdev);
138
139         for (i = 0; i < pdata->num_leds; i++)
140                 led_classdev_resume(&leds_data[i].cdev);
141
142         return 0;
143 }
144 #else
145 #define gpio_led_suspend NULL
146 #define gpio_led_resume NULL
147 #endif
148
149 static struct platform_driver gpio_led_driver = {
150         .remove         = __exit_p(gpio_led_remove),
151         .suspend        = gpio_led_suspend,
152         .resume         = gpio_led_resume,
153         .driver         = {
154                 .name   = "leds-gpio",
155                 .owner  = THIS_MODULE,
156         },
157 };
158
159 static int __init gpio_led_init(void)
160 {
161         return platform_driver_probe(&gpio_led_driver, gpio_led_probe);
162 }
163
164 static void __exit gpio_led_exit(void)
165 {
166         platform_driver_unregister(&gpio_led_driver);
167 }
168
169 module_init(gpio_led_init);
170 module_exit(gpio_led_exit);
171
172 MODULE_AUTHOR("Raphael Assenat <raph@8d.com>");
173 MODULE_DESCRIPTION("GPIO LED driver");
174 MODULE_LICENSE("GPL");