0dfc0156538f07d312ef76a9cc96239064f31e84
[openwrt.git] / target / linux / ar71xx / files / drivers / leds / leds-nu801.c
1 /*
2  * LED driver for NU801
3  *
4  * Kevin Paul Herbert
5  * Copyright (c) 2012, Meraki, Inc.
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
13 #include <linux/module.h>
14 #include <linux/kernel.h>
15 #include <linux/init.h>
16 #include <linux/slab.h>
17 #include <linux/platform_device.h>
18 #include <linux/leds.h>
19 #include <linux/workqueue.h>
20 #include <linux/delay.h>
21 #include <linux/leds-nu801.h>
22
23 #include <asm/gpio.h>
24 #include <linux/of_gpio.h>
25
26 #define MAX_NAME_LENGTH 24
27 #define NUM_COLORS 3
28
29 static const char * const led_nu801_colors[] = { "blue", "green", "red" };
30
31 struct led_nu801_led_data {
32         struct led_classdev cdev;
33         struct led_nu801_data *controller;
34         enum led_brightness level;
35         char name[MAX_NAME_LENGTH];
36 };
37
38 struct led_nu801_data {
39         unsigned cki;
40         unsigned sdi;
41         int lei;
42         struct delayed_work work;
43         struct led_nu801_led_data *led_chain;
44         int num_leds;
45         const char *device_name;
46         const char *name;
47         u32 ndelay;
48         atomic_t pending;
49 };
50
51 static void led_nu801_work(struct work_struct *work)
52 {
53         struct led_nu801_data   *controller =
54                 container_of(work, struct led_nu801_data, work.work);
55         struct led_nu801_led_data *led;
56         u16 bit;
57         u16 brightness;
58         int index;
59
60         for (index = 0; index < controller->num_leds; index++) {
61                 led = &controller->led_chain[index];
62                 brightness = led->level << 8; /* To do: gamma correction */
63                 for (bit = 0x8000; bit; bit = bit >> 1) {
64                         gpio_set_value(controller->sdi,
65                                        (brightness & bit) != 0);
66                         gpio_set_value(controller->cki, 1);
67                         if (unlikely(((index == (controller->num_leds - 1)) &&
68                                       (bit == 1) &&
69                                       (controller->lei < 0)))) {
70                                 udelay(600);
71                         } else {
72                                 ndelay(controller->ndelay);
73                         }
74                         gpio_set_value(controller->cki, 0);
75                         ndelay(controller->ndelay);
76                 }
77         }
78         if (controller->lei >= 0) {
79                 gpio_set_value(controller->lei, 1);
80                 ndelay(controller->ndelay);
81                 gpio_set_value(controller->lei, 0);
82         }
83         atomic_set(&controller->pending, 1);
84 }
85
86 static void led_nu801_set(struct led_classdev *led_cdev,
87                           enum led_brightness value)
88 {
89         struct led_nu801_led_data *led_dat =
90                 container_of(led_cdev, struct led_nu801_led_data, cdev);
91         struct led_nu801_data *controller = led_dat->controller;
92
93         if (led_dat->level != value) {
94                 led_dat->level = value;
95                 if (atomic_dec_and_test(&controller->pending))
96                         schedule_delayed_work(&led_dat->controller->work,
97                                               (HZ/1000) + 1);
98         }
99 }
100
101 static int __init led_nu801_create(struct led_nu801_data *controller,
102                                     struct device *parent,
103                                     int index,
104                                     enum led_brightness brightness,
105 #ifdef CONFIG_LEDS_TRIGGERS
106                                     const char *default_trigger,
107 #endif
108                                     const char *color)
109 {
110         struct led_nu801_led_data *led = &controller->led_chain[index];
111         int ret;
112
113         scnprintf(led->name, sizeof(led->name), "%s:%s:%s%d",
114                   controller->device_name, color, controller->name,
115                   (controller->num_leds - (index + 1)) / NUM_COLORS);
116         led->cdev.name = led->name;
117         led->cdev.brightness_set = led_nu801_set;
118 #ifdef CONFIG_LEDS_TRIGGERS
119         led->cdev.default_trigger = default_trigger;
120 #endif
121         led->level = brightness;
122         led->controller = controller;
123         ret = led_classdev_register(parent, &led->cdev);
124         if (ret < 0)
125                 goto err;
126
127         return 0;
128
129 err:
130         kfree(led);
131         return ret;
132 }
133
134 static int __init
135 led_nu801_create_chain(const struct led_nu801_template *template,
136                         struct led_nu801_data *controller,
137                         struct device *parent)
138 {
139         int ret;
140         int index;
141
142         controller->cki = template->cki;
143         controller->sdi = template->sdi;
144         controller->lei = template->lei;
145         controller->num_leds = template->num_leds * 3;
146         controller->device_name = template->device_name;
147         controller->name = template->name;
148         controller->ndelay = template->ndelay;
149         atomic_set(&controller->pending, 1);
150
151         controller->led_chain = kzalloc(sizeof(struct led_nu801_led_data) *
152                                         controller->num_leds, GFP_KERNEL);
153
154         if (!controller->led_chain)
155                 return -ENOMEM;
156
157         ret = gpio_request(controller->cki, template->name);
158         if (ret < 0)
159                 goto err_free_chain;
160
161         ret = gpio_request(controller->sdi, template->name);
162         if (ret < 0)
163                 goto err_ret_cki;
164
165         if (controller->lei >= 0) {
166                 ret = gpio_request(controller->lei, template->name);
167                 if (ret < 0)
168                         goto err_ret_sdi;
169                 ret = gpio_direction_output(controller->lei, 0);
170                 if (ret < 0)
171                         goto err_ret_lei;
172         }
173
174         ret = gpio_direction_output(controller->cki, 0);
175         if (ret < 0)
176                 goto err_ret_lei;
177
178         ret = gpio_direction_output(controller->sdi, 0);
179         if (ret < 0)
180                 goto err_ret_lei;
181
182         for (index = 0; index < controller->num_leds; index++) {
183                 ret = led_nu801_create(controller, parent, index,
184                         template->init_brightness
185                         [index % NUM_COLORS],
186 #ifdef CONFIG_LEDS_TRIGGERS
187                         template->default_trigger,
188 #endif
189                         template->led_colors[index % NUM_COLORS] ?
190                         template->led_colors[index % NUM_COLORS] :
191                         led_nu801_colors[index % NUM_COLORS]);
192                 if (ret < 0)
193                         goto err_ret_sdi;
194         }
195
196         INIT_DELAYED_WORK(&controller->work, led_nu801_work);
197         schedule_delayed_work(&controller->work, 0);
198
199         return 0;
200
201 err_ret_lei:
202         if (controller->lei >= 0)
203                 gpio_free(controller->lei);
204 err_ret_sdi:
205         gpio_free(controller->sdi);
206 err_ret_cki:
207         gpio_free(controller->cki);
208 err_free_chain:
209         kfree(controller->led_chain);
210
211         return ret;
212 }
213
214 static void led_nu801_delete_chain(struct led_nu801_data *controller)
215 {
216         struct led_nu801_led_data *led_chain;
217         struct led_nu801_led_data *led;
218         int index;
219         int num_leds;
220
221         led_chain = controller->led_chain;
222         controller->led_chain = 0;
223         num_leds = controller->num_leds;
224         controller->num_leds = 0;
225         cancel_delayed_work_sync(&controller->work);
226
227         for (index = 0; index < num_leds; index++) {
228                 led = &led_chain[index];
229                 led_classdev_unregister(&led->cdev);
230         }
231
232         gpio_free(controller->cki);
233         gpio_free(controller->sdi);
234         if (controller->lei >= 0)
235                 gpio_free(controller->lei);
236
237         kfree(led_chain);
238 }
239
240 static struct led_nu801_data * __init
241 leds_nu801_create_of(struct platform_device *pdev)
242 {
243         struct device_node *np = pdev->dev.of_node, *child;
244         struct led_nu801_data *controllers;
245         int count = 0, ret;
246         int i = 0;
247
248         for_each_child_of_node(np, child)
249                 count++;
250         if (!count)
251                 return NULL;
252
253         controllers = kzalloc(sizeof(struct led_nu801_data) * count,
254                               GFP_KERNEL);
255         if (!controllers)
256                 return NULL;
257
258         for_each_child_of_node(np, child) {
259                 const char *state;
260                 struct led_nu801_template template = {};
261                 struct device_node *colors;
262                 int jj;
263
264                 template.cki = of_get_named_gpio_flags(child, "cki", 0, NULL);
265                 template.sdi = of_get_named_gpio_flags(child, "sdi", 0, NULL);
266                 if (of_find_property(child, "lei", NULL)) {
267                         template.lei = of_get_named_gpio_flags(child, "lei",
268                                                                0, NULL);
269                 } else {
270                         template.lei = -1;
271                 }
272                 of_property_read_u32(child, "ndelay", &template.ndelay);
273                 of_property_read_u32(child, "num_leds", &template.num_leds);
274                 template.name = of_get_property(child, "label", NULL) ? :
275                         child->name;
276                 template.default_trigger = of_get_property(child,
277                         "default-trigger", NULL);
278
279                 jj = 0;
280                 for_each_child_of_node(child, colors) {
281                         template.led_colors[jj] = of_get_property(colors,
282                                  "label", NULL);
283                         state = of_get_property(colors, "state", NULL);
284                         if (!strncmp(state, "off", 3))
285                                 template.init_brightness[jj] = LED_OFF;
286                         else if (!strncmp(state, "half", 4))
287                                 template.init_brightness[jj] = LED_HALF;
288                         else if (!strncmp(state, "full", 4))
289                                 template.init_brightness[jj] = LED_FULL;
290                         jj++;
291                 }
292
293                 ret = led_nu801_create_chain(&template,
294                                              &controllers[i],
295                                              &pdev->dev);
296                 if (ret < 0)
297                         goto err;
298                 i++;
299         }
300
301         return controllers;
302
303 err:
304         for (i = i - 1; i >= 0; i--)
305                 led_nu801_delete_chain(&controllers[i]);
306         kfree(controllers);
307         return NULL;
308 }
309
310 static int __init led_nu801_probe(struct platform_device *pdev)
311 {
312         struct led_nu801_platform_data *pdata = pdev->dev.platform_data;
313         struct led_nu801_data *controllers;
314         int i, ret = 0;
315
316         if (!(pdata && pdata->num_controllers)) {
317                 controllers = leds_nu801_create_of(pdev);
318                 if (!controllers)
319                         return -ENODEV;
320         }
321
322         controllers = kzalloc(sizeof(struct led_nu801_data) *
323                               pdata->num_controllers, GFP_KERNEL);
324         if (!controllers)
325                 return -ENOMEM;
326
327         for (i = 0; i < pdata->num_controllers; i++) {
328                 ret = led_nu801_create_chain(&pdata->template[i],
329                                               &controllers[i],
330                                               &pdev->dev);
331                 if (ret < 0)
332                         goto err;
333         }
334
335         platform_set_drvdata(pdev, controllers);
336
337         return 0;
338
339 err:
340         for (i = i - 1; i >= 0; i--)
341                 led_nu801_delete_chain(&controllers[i]);
342
343         kfree(controllers);
344
345         return ret;
346 }
347
348 static int led_nu801_remove(struct platform_device *pdev)
349 {
350         int i;
351         struct led_nu801_platform_data *pdata = pdev->dev.platform_data;
352         struct led_nu801_data *controllers;
353
354         controllers = platform_get_drvdata(pdev);
355
356         for (i = 0; i < pdata->num_controllers; i++)
357                 led_nu801_delete_chain(&controllers[i]);
358
359         kfree(controllers);
360
361         return 0;
362 }
363
364 static const struct of_device_id of_numen_leds_match[] = {
365         { .compatible = "numen,leds-nu801", },
366         {},
367 };
368 MODULE_DEVICE_TABLE(of, of_pwm_leds_match);
369
370 static struct platform_driver led_nu801_driver = {
371         .probe          = led_nu801_probe,
372         .remove         = led_nu801_remove,
373         .driver         = {
374                 .name   = "leds-nu801",
375                 .owner  = THIS_MODULE,
376                 .of_match_table = of_numen_leds_match,
377         },
378 };
379
380 static int __init led_nu801_init(void)
381 {
382         return platform_driver_register(&led_nu801_driver);
383 }
384
385 static void __exit led_nu801_exit(void)
386 {
387         platform_driver_unregister(&led_nu801_driver);
388 }
389
390 module_init(led_nu801_init);
391 module_exit(led_nu801_exit);
392
393 MODULE_AUTHOR("Kevin Paul Herbert <kph@meraki.net>");
394 MODULE_DESCRIPTION("NU801 LED driver");
395 MODULE_LICENSE("GPL v2");
396 MODULE_ALIAS("platform:leds-nu801");