Branch data Line data Source code
1 : : /*
2 : : * LEDs driver for GPIOs
3 : : *
4 : : * Copyright (C) 2007 8D Technologies inc.
5 : : * Raphael Assenat <raph@8d.com>
6 : : * Copyright (C) 2008 Freescale Semiconductor, Inc.
7 : : *
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.
11 : : *
12 : : */
13 : : #include <linux/kernel.h>
14 : : #include <linux/init.h>
15 : : #include <linux/platform_device.h>
16 : : #include <linux/gpio.h>
17 : : #include <linux/leds.h>
18 : : #include <linux/of.h>
19 : : #include <linux/of_platform.h>
20 : : #include <linux/of_gpio.h>
21 : : #include <linux/slab.h>
22 : : #include <linux/workqueue.h>
23 : : #include <linux/module.h>
24 : : #include <linux/err.h>
25 : :
26 : : struct gpio_led_data {
27 : : struct led_classdev cdev;
28 : : unsigned gpio;
29 : : struct work_struct work;
30 : : u8 new_level;
31 : : u8 can_sleep;
32 : : u8 active_low;
33 : : u8 blinking;
34 : : int (*platform_gpio_blink_set)(unsigned gpio, int state,
35 : : unsigned long *delay_on, unsigned long *delay_off);
36 : : };
37 : :
38 : 0 : static void gpio_led_work(struct work_struct *work)
39 : : {
40 : : struct gpio_led_data *led_dat =
41 : : container_of(work, struct gpio_led_data, work);
42 : :
43 [ # # ]: 0 : if (led_dat->blinking) {
44 : 0 : led_dat->platform_gpio_blink_set(led_dat->gpio,
45 : 0 : led_dat->new_level,
46 : : NULL, NULL);
47 : 0 : led_dat->blinking = 0;
48 : : } else
49 : 0 : gpio_set_value_cansleep(led_dat->gpio, led_dat->new_level);
50 : 0 : }
51 : :
52 : 0 : static void gpio_led_set(struct led_classdev *led_cdev,
53 : : enum led_brightness value)
54 : : {
55 : : struct gpio_led_data *led_dat =
56 : : container_of(led_cdev, struct gpio_led_data, cdev);
57 : : int level;
58 : :
59 [ + + ]: 11765081 : if (value == LED_OFF)
60 : : level = 0;
61 : : else
62 : : level = 1;
63 : :
64 [ - + ]: 11765081 : if (led_dat->active_low)
65 : 0 : level = !level;
66 : :
67 : : /* Setting GPIOs with I2C/etc requires a task context, and we don't
68 : : * seem to have a reliable way to know if we're already in one; so
69 : : * let's just assume the worst.
70 : : */
71 [ - + ]: 11765081 : if (led_dat->can_sleep) {
72 : 0 : led_dat->new_level = level;
73 : 0 : schedule_work(&led_dat->work);
74 : : } else {
75 [ - + ]: 11765081 : if (led_dat->blinking) {
76 : 0 : led_dat->platform_gpio_blink_set(led_dat->gpio, level,
77 : : NULL, NULL);
78 : 0 : led_dat->blinking = 0;
79 : : } else
80 : 11765081 : gpio_set_value(led_dat->gpio, level);
81 : : }
82 : 11765302 : }
83 : :
84 : 0 : static int gpio_blink_set(struct led_classdev *led_cdev,
85 : : unsigned long *delay_on, unsigned long *delay_off)
86 : : {
87 : : struct gpio_led_data *led_dat =
88 : : container_of(led_cdev, struct gpio_led_data, cdev);
89 : :
90 : 0 : led_dat->blinking = 1;
91 : 0 : return led_dat->platform_gpio_blink_set(led_dat->gpio, GPIO_LED_BLINK,
92 : : delay_on, delay_off);
93 : : }
94 : :
95 : 0 : static int create_gpio_led(const struct gpio_led *template,
96 : : struct gpio_led_data *led_dat, struct device *parent,
97 : : int (*blink_set)(unsigned, int, unsigned long *, unsigned long *))
98 : : {
99 : : int ret, state;
100 : :
101 : 0 : led_dat->gpio = -1;
102 : :
103 : : /* skip leds that aren't available */
104 [ # # ]: 0 : if (!gpio_is_valid(template->gpio)) {
105 : 0 : dev_info(parent, "Skipping unavailable LED gpio %d (%s)\n",
106 : : template->gpio, template->name);
107 : 0 : return 0;
108 : : }
109 : :
110 : 0 : ret = devm_gpio_request(parent, template->gpio, template->name);
111 [ # # ]: 0 : if (ret < 0)
112 : : return ret;
113 : :
114 : 0 : led_dat->cdev.name = template->name;
115 : 0 : led_dat->cdev.default_trigger = template->default_trigger;
116 : 0 : led_dat->gpio = template->gpio;
117 : 0 : led_dat->can_sleep = gpio_cansleep(template->gpio);
118 : 0 : led_dat->active_low = template->active_low;
119 : 0 : led_dat->blinking = 0;
120 [ # # ]: 0 : if (blink_set) {
121 : 0 : led_dat->platform_gpio_blink_set = blink_set;
122 : 0 : led_dat->cdev.blink_set = gpio_blink_set;
123 : : }
124 : 0 : led_dat->cdev.brightness_set = gpio_led_set;
125 [ # # ]: 0 : if (template->default_state == LEDS_GPIO_DEFSTATE_KEEP)
126 : 0 : state = !!gpio_get_value_cansleep(led_dat->gpio) ^ led_dat->active_low;
127 : : else
128 : 0 : state = (template->default_state == LEDS_GPIO_DEFSTATE_ON);
129 [ # # ]: 0 : led_dat->cdev.brightness = state ? LED_FULL : LED_OFF;
130 [ # # ]: 0 : if (!template->retain_state_suspended)
131 : 0 : led_dat->cdev.flags |= LED_CORE_SUSPENDRESUME;
132 : :
133 : 0 : ret = gpio_direction_output(led_dat->gpio, led_dat->active_low ^ state);
134 [ # # ]: 0 : if (ret < 0)
135 : : return ret;
136 : :
137 : 0 : INIT_WORK(&led_dat->work, gpio_led_work);
138 : :
139 : 0 : ret = led_classdev_register(parent, &led_dat->cdev);
140 [ # # ]: 0 : if (ret < 0)
141 : 0 : return ret;
142 : :
143 : : return 0;
144 : : }
145 : :
146 : 0 : static void delete_gpio_led(struct gpio_led_data *led)
147 : : {
148 [ # # ]: 0 : if (!gpio_is_valid(led->gpio))
149 : 0 : return;
150 : 0 : led_classdev_unregister(&led->cdev);
151 : 0 : cancel_work_sync(&led->work);
152 : : }
153 : :
154 : : struct gpio_leds_priv {
155 : : int num_leds;
156 : : struct gpio_led_data leds[];
157 : : };
158 : :
159 : : static inline int sizeof_gpio_leds_priv(int num_leds)
160 : : {
161 : 0 : return sizeof(struct gpio_leds_priv) +
162 : 0 : (sizeof(struct gpio_led_data) * num_leds);
163 : : }
164 : :
165 : : /* Code to create from OpenFirmware platform devices */
166 : : #ifdef CONFIG_OF_GPIO
167 : 0 : static struct gpio_leds_priv *gpio_leds_create_of(struct platform_device *pdev)
168 : : {
169 : 0 : struct device_node *np = pdev->dev.of_node, *child;
170 : : struct gpio_leds_priv *priv;
171 : : int count, ret;
172 : :
173 : : /* count LEDs in this device, so we know how much to allocate */
174 : : count = of_get_available_child_count(np);
175 [ # # ]: 0 : if (!count)
176 : : return ERR_PTR(-ENODEV);
177 : :
178 [ # # ]: 0 : for_each_available_child_of_node(np, child)
179 [ # # ]: 0 : if (of_get_gpio(child, 0) == -EPROBE_DEFER)
180 : : return ERR_PTR(-EPROBE_DEFER);
181 : :
182 : 0 : priv = devm_kzalloc(&pdev->dev, sizeof_gpio_leds_priv(count),
183 : : GFP_KERNEL);
184 [ # # ]: 0 : if (!priv)
185 : : return ERR_PTR(-ENOMEM);
186 : :
187 [ # # ]: 0 : for_each_available_child_of_node(np, child) {
188 : 0 : struct gpio_led led = {};
189 : : enum of_gpio_flags flags;
190 : : const char *state;
191 : :
192 : 0 : led.gpio = of_get_gpio_flags(child, 0, &flags);
193 : 0 : led.active_low = flags & OF_GPIO_ACTIVE_LOW;
194 [ # # ]: 0 : led.name = of_get_property(child, "label", NULL) ? : child->name;
195 : 0 : led.default_trigger =
196 : 0 : of_get_property(child, "linux,default-trigger", NULL);
197 : 0 : state = of_get_property(child, "default-state", NULL);
198 [ # # ]: 0 : if (state) {
199 [ # # ]: 0 : if (!strcmp(state, "keep"))
200 : 0 : led.default_state = LEDS_GPIO_DEFSTATE_KEEP;
201 [ # # ]: 0 : else if (!strcmp(state, "on"))
202 : 0 : led.default_state = LEDS_GPIO_DEFSTATE_ON;
203 : : else
204 : 0 : led.default_state = LEDS_GPIO_DEFSTATE_OFF;
205 : : }
206 : :
207 : 0 : ret = create_gpio_led(&led, &priv->leds[priv->num_leds++],
208 : : &pdev->dev, NULL);
209 [ # # ]: 0 : if (ret < 0) {
210 : : of_node_put(child);
211 : 0 : goto err;
212 : : }
213 : : }
214 : :
215 : : return priv;
216 : :
217 : : err:
218 [ # # ]: 0 : for (count = priv->num_leds - 2; count >= 0; count--)
219 : 0 : delete_gpio_led(&priv->leds[count]);
220 : : return ERR_PTR(-ENODEV);
221 : : }
222 : :
223 : : static const struct of_device_id of_gpio_leds_match[] = {
224 : : { .compatible = "gpio-leds", },
225 : : {},
226 : : };
227 : : #else /* CONFIG_OF_GPIO */
228 : : static struct gpio_leds_priv *gpio_leds_create_of(struct platform_device *pdev)
229 : : {
230 : : return ERR_PTR(-ENODEV);
231 : : }
232 : : #endif /* CONFIG_OF_GPIO */
233 : :
234 : :
235 : 0 : static int gpio_led_probe(struct platform_device *pdev)
236 : : {
237 : 0 : struct gpio_led_platform_data *pdata = dev_get_platdata(&pdev->dev);
238 : : struct gpio_leds_priv *priv;
239 : : int i, ret = 0;
240 : :
241 : :
242 [ # # ][ # # ]: 0 : if (pdata && pdata->num_leds) {
243 : 0 : priv = devm_kzalloc(&pdev->dev,
244 : : sizeof_gpio_leds_priv(pdata->num_leds),
245 : : GFP_KERNEL);
246 [ # # ]: 0 : if (!priv)
247 : : return -ENOMEM;
248 : :
249 : 0 : priv->num_leds = pdata->num_leds;
250 [ # # ]: 0 : for (i = 0; i < priv->num_leds; i++) {
251 : 0 : ret = create_gpio_led(&pdata->leds[i],
252 : : &priv->leds[i],
253 : : &pdev->dev, pdata->gpio_blink_set);
254 [ # # ]: 0 : if (ret < 0) {
255 : : /* On failure: unwind the led creations */
256 [ # # ]: 0 : for (i = i - 1; i >= 0; i--)
257 : 0 : delete_gpio_led(&priv->leds[i]);
258 : : return ret;
259 : : }
260 : : }
261 : : } else {
262 : 0 : priv = gpio_leds_create_of(pdev);
263 [ # # ]: 0 : if (IS_ERR(priv))
264 : 0 : return PTR_ERR(priv);
265 : : }
266 : :
267 : : platform_set_drvdata(pdev, priv);
268 : :
269 : 0 : return 0;
270 : : }
271 : :
272 : 0 : static int gpio_led_remove(struct platform_device *pdev)
273 : : {
274 : : struct gpio_leds_priv *priv = platform_get_drvdata(pdev);
275 : : int i;
276 : :
277 [ # # ]: 0 : for (i = 0; i < priv->num_leds; i++)
278 : 0 : delete_gpio_led(&priv->leds[i]);
279 : :
280 : 0 : return 0;
281 : : }
282 : :
283 : : static struct platform_driver gpio_led_driver = {
284 : : .probe = gpio_led_probe,
285 : : .remove = gpio_led_remove,
286 : : .driver = {
287 : : .name = "leds-gpio",
288 : : .owner = THIS_MODULE,
289 : : .of_match_table = of_match_ptr(of_gpio_leds_match),
290 : : },
291 : : };
292 : :
293 : 0 : module_platform_driver(gpio_led_driver);
294 : :
295 : : MODULE_AUTHOR("Raphael Assenat <raph@8d.com>, Trent Piepho <tpiepho@freescale.com>");
296 : : MODULE_DESCRIPTION("GPIO LED driver");
297 : : MODULE_LICENSE("GPL");
298 : : MODULE_ALIAS("platform:leds-gpio");
|