aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/leds
diff options
context:
space:
mode:
authorTrent Piepho <tpiepho@freescale.com>2009-01-10 12:26:01 -0500
committerRichard Purdie <rpurdie@linux.intel.com>2009-04-06 11:06:25 -0400
commita7d878af94b223013a48078e0c8c0a654c24a057 (patch)
tree65c288edc917ae01aec7bc14702e81ced8f6e916 /drivers/leds
parent0221c81b1b8eb0cbb6b30a0ced52ead32d2b4e4c (diff)
leds: Add openfirmware platform device support
Add bindings to support LEDs defined as of_platform devices in addition to the existing bindings for platform devices. New options in Kconfig allow the platform binding code and/or the of_platform code to be turned on. The of_platform code is of course only available on archs that have OF support. The existing probe and remove methods are refactored to use new functions create_gpio_led(), to create and register one led, and delete_gpio_led(), to unregister and free one led. The new probe and remove methods for the of_platform driver can then share most of the common probe and remove code with the platform driver. The suspend and resume methods aren't shared, but they are very short. The actual led driving code is the same for LEDs created by either binding. The OF bindings are based on patch by Anton Vorontsov <avorontsov@ru.mvista.com>. They have been extended to allow multiple LEDs per device. Signed-off-by: Trent Piepho <tpiepho@freescale.com> Acked-by: Grant Likely <grant.likely@secretlab.ca> Acked-by: Sean MacLennan <smaclennan@pikatech.com> Signed-off-by: Richard Purdie <rpurdie@linux.intel.com>
Diffstat (limited to 'drivers/leds')
-rw-r--r--drivers/leds/Kconfig21
-rw-r--r--drivers/leds/leds-gpio.c205
2 files changed, 182 insertions, 44 deletions
diff --git a/drivers/leds/Kconfig b/drivers/leds/Kconfig
index d9db17624f12..90d39e5803cd 100644
--- a/drivers/leds/Kconfig
+++ b/drivers/leds/Kconfig
@@ -117,7 +117,26 @@ config LEDS_GPIO
117 help 117 help
118 This option enables support for the LEDs connected to GPIO 118 This option enables support for the LEDs connected to GPIO
119 outputs. To be useful the particular board must have LEDs 119 outputs. To be useful the particular board must have LEDs
120 and they must be connected to the GPIO lines. 120 and they must be connected to the GPIO lines. The LEDs must be
121 defined as platform devices and/or OpenFirmware platform devices.
122 The code to use these bindings can be selected below.
123
124config LEDS_GPIO_PLATFORM
125 bool "Platform device bindings for GPIO LEDs"
126 depends on LEDS_GPIO
127 default y
128 help
129 Let the leds-gpio driver drive LEDs which have been defined as
130 platform devices. If you don't know what this means, say yes.
131
132config LEDS_GPIO_OF
133 bool "OpenFirmware platform device bindings for GPIO LEDs"
134 depends on LEDS_GPIO && OF_DEVICE
135 default y
136 help
137 Let the leds-gpio driver drive LEDs which have been defined as
138 of_platform devices. For instance, LEDs which are listed in a "dts"
139 file.
121 140
122config LEDS_CLEVO_MAIL 141config LEDS_CLEVO_MAIL
123 tristate "Mail LED on Clevo notebook (EXPERIMENTAL)" 142 tristate "Mail LED on Clevo notebook (EXPERIMENTAL)"
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
75static 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;
109err:
110 gpio_free(led_dat->gpio);
111 return ret;
112}
113
114static 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
74static int gpio_led_probe(struct platform_device *pdev) 122static 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
125err: 147err:
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)
177module_init(gpio_led_init); 191module_init(gpio_led_init);
178module_exit(gpio_led_exit); 192module_exit(gpio_led_exit);
179 193
180MODULE_AUTHOR("Raphael Assenat <raph@8d.com>"); 194MODULE_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
202struct gpio_led_of_platform_data {
203 int num_leds;
204 struct gpio_led_data led_data[];
205};
206
207static 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
248err:
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
257static 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
272static const struct of_device_id of_gpio_leds_match[] = {
273 { .compatible = "gpio-leds", },
274 {},
275};
276
277static 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
287static int __init of_gpio_leds_init(void)
288{
289 return of_register_platform_driver(&of_gpio_leds_driver);
290}
291module_init(of_gpio_leds_init);
292
293static void __exit of_gpio_leds_exit(void)
294{
295 of_unregister_platform_driver(&of_gpio_leds_driver);
296}
297module_exit(of_gpio_leds_exit);
298#endif
299
300MODULE_AUTHOR("Raphael Assenat <raph@8d.com>, Trent Piepho <tpiepho@freescale.com>");
181MODULE_DESCRIPTION("GPIO LED driver"); 301MODULE_DESCRIPTION("GPIO LED driver");
182MODULE_LICENSE("GPL"); 302MODULE_LICENSE("GPL");
183MODULE_ALIAS("platform:leds-gpio");