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 <linux/gpio.h>
24#include <linux/of_gpio.h>
25
26#define MAX_NAME_LENGTH 24
27#define NUM_COLORS 3
28
29static const char * const led_nu801_colors[] = { "blue", "green", "red" };
30
31struct 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
38struct 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
51static 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
86static 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
101static 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
129err:
130	kfree(led);
131	return ret;
132}
133
134static int __init
135led_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
201err_ret_lei:
202	if (controller->lei >= 0)
203		gpio_free(controller->lei);
204err_ret_sdi:
205	gpio_free(controller->sdi);
206err_ret_cki:
207	gpio_free(controller->cki);
208err_free_chain:
209	kfree(controller->led_chain);
210
211	return ret;
212}
213
214static 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
240static struct led_nu801_data * __init
241leds_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
303err:
304	for (i = i - 1; i >= 0; i--)
305		led_nu801_delete_chain(&controllers[i]);
306	kfree(controllers);
307	return NULL;
308}
309
310static 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
339err:
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
348static 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
364static const struct of_device_id of_numen_leds_match[] = {
365	{ .compatible = "numen,leds-nu801", },
366	{},
367};
368MODULE_DEVICE_TABLE(of, of_pwm_leds_match);
369
370static 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
380static int __init led_nu801_init(void)
381{
382	return platform_driver_register(&led_nu801_driver);
383}
384
385static void __exit led_nu801_exit(void)
386{
387	platform_driver_unregister(&led_nu801_driver);
388}
389
390module_init(led_nu801_init);
391module_exit(led_nu801_exit);
392
393MODULE_AUTHOR("Kevin Paul Herbert <kph@meraki.net>");
394MODULE_DESCRIPTION("NU801 LED driver");
395MODULE_LICENSE("GPL v2");
396MODULE_ALIAS("platform:leds-nu801");
397