diff options
Diffstat (limited to 'drivers/leds/leds-gpio.c')
-rw-r--r-- | drivers/leds/leds-gpio.c | 205 |
1 files changed, 162 insertions, 43 deletions
diff --git a/drivers/leds/leds-gpio.c b/drivers/leds/leds-gpio.c index 2e3df08b649b..f8bcf98fc15c 100644 --- a/drivers/leds/leds-gpio.c +++ b/drivers/leds/leds-gpio.c | |||
@@ -3,6 +3,7 @@ | |||
3 | * | 3 | * |
4 | * Copyright (C) 2007 8D Technologies inc. | 4 | * Copyright (C) 2007 8D Technologies inc. |
5 | * Raphael Assenat <raph@8d.com> | 5 | * Raphael Assenat <raph@8d.com> |
6 | * Copyright (C) 2008 Freescale Semiconductor, Inc. | ||
6 | * | 7 | * |
7 | * This program is free software; you can redistribute it and/or modify | 8 | * 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 | * it under the terms of the GNU General Public License version 2 as |
@@ -71,11 +72,57 @@ static int gpio_blink_set(struct led_classdev *led_cdev, | |||
71 | return led_dat->platform_gpio_blink_set(led_dat->gpio, delay_on, delay_off); | 72 | return led_dat->platform_gpio_blink_set(led_dat->gpio, delay_on, delay_off); |
72 | } | 73 | } |
73 | 74 | ||
75 | static int __devinit create_gpio_led(const struct gpio_led *template, | ||
76 | struct gpio_led_data *led_dat, struct device *parent, | ||
77 | int (*blink_set)(unsigned, unsigned long *, unsigned long *)) | ||
78 | { | ||
79 | int ret; | ||
80 | |||
81 | ret = gpio_request(template->gpio, template->name); | ||
82 | if (ret < 0) | ||
83 | return ret; | ||
84 | |||
85 | led_dat->cdev.name = template->name; | ||
86 | led_dat->cdev.default_trigger = template->default_trigger; | ||
87 | led_dat->gpio = template->gpio; | ||
88 | led_dat->can_sleep = gpio_cansleep(template->gpio); | ||
89 | led_dat->active_low = template->active_low; | ||
90 | if (blink_set) { | ||
91 | led_dat->platform_gpio_blink_set = blink_set; | ||
92 | led_dat->cdev.blink_set = gpio_blink_set; | ||
93 | } | ||
94 | led_dat->cdev.brightness_set = gpio_led_set; | ||
95 | led_dat->cdev.brightness = LED_OFF; | ||
96 | led_dat->cdev.flags |= LED_CORE_SUSPENDRESUME; | ||
97 | |||
98 | ret = gpio_direction_output(led_dat->gpio, led_dat->active_low); | ||
99 | if (ret < 0) | ||
100 | goto err; | ||
101 | |||
102 | INIT_WORK(&led_dat->work, gpio_led_work); | ||
103 | |||
104 | ret = led_classdev_register(parent, &led_dat->cdev); | ||
105 | if (ret < 0) | ||
106 | goto err; | ||
107 | |||
108 | return 0; | ||
109 | err: | ||
110 | gpio_free(led_dat->gpio); | ||
111 | return ret; | ||
112 | } | ||
113 | |||
114 | static void delete_gpio_led(struct gpio_led_data *led) | ||
115 | { | ||
116 | led_classdev_unregister(&led->cdev); | ||
117 | cancel_work_sync(&led->work); | ||
118 | gpio_free(led->gpio); | ||
119 | } | ||
120 | |||
121 | #ifdef CONFIG_LEDS_GPIO_PLATFORM | ||
74 | static int gpio_led_probe(struct platform_device *pdev) | 122 | static int gpio_led_probe(struct platform_device *pdev) |
75 | { | 123 | { |
76 | struct gpio_led_platform_data *pdata = pdev->dev.platform_data; | 124 | struct gpio_led_platform_data *pdata = pdev->dev.platform_data; |
77 | struct gpio_led *cur_led; | 125 | struct gpio_led_data *leds_data; |
78 | struct gpio_led_data *leds_data, *led_dat; | ||
79 | int i, ret = 0; | 126 | int i, ret = 0; |
80 | 127 | ||
81 | if (!pdata) | 128 | if (!pdata) |
@@ -87,35 +134,10 @@ static int gpio_led_probe(struct platform_device *pdev) | |||
87 | return -ENOMEM; | 134 | return -ENOMEM; |
88 | 135 | ||
89 | for (i = 0; i < pdata->num_leds; i++) { | 136 | for (i = 0; i < pdata->num_leds; i++) { |
90 | cur_led = &pdata->leds[i]; | 137 | ret = create_gpio_led(&pdata->leds[i], &leds_data[i], |
91 | led_dat = &leds_data[i]; | 138 | &pdev->dev, pdata->gpio_blink_set); |
92 | |||
93 | ret = gpio_request(cur_led->gpio, cur_led->name); | ||
94 | if (ret < 0) | 139 | if (ret < 0) |
95 | goto err; | 140 | 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 | led_dat->cdev.flags |= LED_CORE_SUSPENDRESUME; | ||
109 | |||
110 | gpio_direction_output(led_dat->gpio, led_dat->active_low); | ||
111 | |||
112 | INIT_WORK(&led_dat->work, gpio_led_work); | ||
113 | |||
114 | ret = led_classdev_register(&pdev->dev, &led_dat->cdev); | ||
115 | if (ret < 0) { | ||
116 | gpio_free(led_dat->gpio); | ||
117 | goto err; | ||
118 | } | ||
119 | } | 141 | } |
120 | 142 | ||
121 | platform_set_drvdata(pdev, leds_data); | 143 | platform_set_drvdata(pdev, leds_data); |
@@ -123,13 +145,8 @@ static int gpio_led_probe(struct platform_device *pdev) | |||
123 | return 0; | 145 | return 0; |
124 | 146 | ||
125 | err: | 147 | err: |
126 | if (i > 0) { | 148 | for (i = i - 1; i >= 0; i--) |
127 | for (i = i - 1; i >= 0; i--) { | 149 | delete_gpio_led(&leds_data[i]); |
128 | led_classdev_unregister(&leds_data[i].cdev); | ||
129 | cancel_work_sync(&leds_data[i].work); | ||
130 | gpio_free(leds_data[i].gpio); | ||
131 | } | ||
132 | } | ||
133 | 150 | ||
134 | kfree(leds_data); | 151 | kfree(leds_data); |
135 | 152 | ||
@@ -144,11 +161,8 @@ static int __devexit gpio_led_remove(struct platform_device *pdev) | |||
144 | 161 | ||
145 | leds_data = platform_get_drvdata(pdev); | 162 | leds_data = platform_get_drvdata(pdev); |
146 | 163 | ||
147 | for (i = 0; i < pdata->num_leds; i++) { | 164 | for (i = 0; i < pdata->num_leds; i++) |
148 | led_classdev_unregister(&leds_data[i].cdev); | 165 | delete_gpio_led(&leds_data[i]); |
149 | cancel_work_sync(&leds_data[i].work); | ||
150 | gpio_free(leds_data[i].gpio); | ||
151 | } | ||
152 | 166 | ||
153 | kfree(leds_data); | 167 | kfree(leds_data); |
154 | 168 | ||
@@ -177,7 +191,112 @@ static void __exit gpio_led_exit(void) | |||
177 | module_init(gpio_led_init); | 191 | module_init(gpio_led_init); |
178 | module_exit(gpio_led_exit); | 192 | module_exit(gpio_led_exit); |
179 | 193 | ||
180 | MODULE_AUTHOR("Raphael Assenat <raph@8d.com>"); | 194 | MODULE_ALIAS("platform:leds-gpio"); |
195 | #endif /* CONFIG_LEDS_GPIO_PLATFORM */ | ||
196 | |||
197 | /* Code to create from OpenFirmware platform devices */ | ||
198 | #ifdef CONFIG_LEDS_GPIO_OF | ||
199 | #include <linux/of_platform.h> | ||
200 | #include <linux/of_gpio.h> | ||
201 | |||
202 | struct gpio_led_of_platform_data { | ||
203 | int num_leds; | ||
204 | struct gpio_led_data led_data[]; | ||
205 | }; | ||
206 | |||
207 | static int __devinit of_gpio_leds_probe(struct of_device *ofdev, | ||
208 | const struct of_device_id *match) | ||
209 | { | ||
210 | struct device_node *np = ofdev->node, *child; | ||
211 | struct gpio_led led; | ||
212 | struct gpio_led_of_platform_data *pdata; | ||
213 | int count = 0, ret; | ||
214 | |||
215 | /* count LEDs defined by this device, so we know how much to allocate */ | ||
216 | for_each_child_of_node(np, child) | ||
217 | count++; | ||
218 | if (!count) | ||
219 | return 0; /* or ENODEV? */ | ||
220 | |||
221 | pdata = kzalloc(sizeof(*pdata) + sizeof(struct gpio_led_data) * count, | ||
222 | GFP_KERNEL); | ||
223 | if (!pdata) | ||
224 | return -ENOMEM; | ||
225 | |||
226 | memset(&led, 0, sizeof(led)); | ||
227 | for_each_child_of_node(np, child) { | ||
228 | enum of_gpio_flags flags; | ||
229 | |||
230 | led.gpio = of_get_gpio_flags(child, 0, &flags); | ||
231 | led.active_low = flags & OF_GPIO_ACTIVE_LOW; | ||
232 | led.name = of_get_property(child, "label", NULL) ? : child->name; | ||
233 | led.default_trigger = | ||
234 | of_get_property(child, "linux,default-trigger", NULL); | ||
235 | |||
236 | ret = create_gpio_led(&led, &pdata->led_data[pdata->num_leds++], | ||
237 | &ofdev->dev, NULL); | ||
238 | if (ret < 0) { | ||
239 | of_node_put(child); | ||
240 | goto err; | ||
241 | } | ||
242 | } | ||
243 | |||
244 | dev_set_drvdata(&ofdev->dev, pdata); | ||
245 | |||
246 | return 0; | ||
247 | |||
248 | err: | ||
249 | for (count = pdata->num_leds - 2; count >= 0; count--) | ||
250 | delete_gpio_led(&pdata->led_data[count]); | ||
251 | |||
252 | kfree(pdata); | ||
253 | |||
254 | return ret; | ||
255 | } | ||
256 | |||
257 | static int __devexit of_gpio_leds_remove(struct of_device *ofdev) | ||
258 | { | ||
259 | struct gpio_led_of_platform_data *pdata = dev_get_drvdata(&ofdev->dev); | ||
260 | int i; | ||
261 | |||
262 | for (i = 0; i < pdata->num_leds; i++) | ||
263 | delete_gpio_led(&pdata->led_data[i]); | ||
264 | |||
265 | kfree(pdata); | ||
266 | |||
267 | dev_set_drvdata(&ofdev->dev, NULL); | ||
268 | |||
269 | return 0; | ||
270 | } | ||
271 | |||
272 | static const struct of_device_id of_gpio_leds_match[] = { | ||
273 | { .compatible = "gpio-leds", }, | ||
274 | {}, | ||
275 | }; | ||
276 | |||
277 | static struct of_platform_driver of_gpio_leds_driver = { | ||
278 | .driver = { | ||
279 | .name = "of_gpio_leds", | ||
280 | .owner = THIS_MODULE, | ||
281 | }, | ||
282 | .match_table = of_gpio_leds_match, | ||
283 | .probe = of_gpio_leds_probe, | ||
284 | .remove = __devexit_p(of_gpio_leds_remove), | ||
285 | }; | ||
286 | |||
287 | static int __init of_gpio_leds_init(void) | ||
288 | { | ||
289 | return of_register_platform_driver(&of_gpio_leds_driver); | ||
290 | } | ||
291 | module_init(of_gpio_leds_init); | ||
292 | |||
293 | static void __exit of_gpio_leds_exit(void) | ||
294 | { | ||
295 | of_unregister_platform_driver(&of_gpio_leds_driver); | ||
296 | } | ||
297 | module_exit(of_gpio_leds_exit); | ||
298 | #endif | ||
299 | |||
300 | MODULE_AUTHOR("Raphael Assenat <raph@8d.com>, Trent Piepho <tpiepho@freescale.com>"); | ||
181 | MODULE_DESCRIPTION("GPIO LED driver"); | 301 | MODULE_DESCRIPTION("GPIO LED driver"); |
182 | MODULE_LICENSE("GPL"); | 302 | MODULE_LICENSE("GPL"); |
183 | MODULE_ALIAS("platform:leds-gpio"); | ||