xref: /openbmc/linux/drivers/leds/leds-ns2.c (revision 6d99a79c)
1 /*
2  * leds-ns2.c - Driver for the Network Space v2 (and parents) dual-GPIO LED
3  *
4  * Copyright (C) 2010 LaCie
5  *
6  * Author: Simon Guinot <sguinot@lacie.com>
7  *
8  * Based on leds-gpio.c by Raphael Assenat <raph@8d.com>
9  *
10  * This program is free software; you can redistribute it and/or modify
11  * it under the terms of the GNU General Public License as published by
12  * the Free Software Foundation; either version 2 of the License, or
13  * (at your option) any later version.
14  *
15  * This program is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  * GNU General Public License for more details.
19  *
20  * You should have received a copy of the GNU General Public License
21  * along with this program; if not, write to the Free Software
22  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
23  */
24 
25 #include <linux/kernel.h>
26 #include <linux/platform_device.h>
27 #include <linux/slab.h>
28 #include <linux/gpio.h>
29 #include <linux/leds.h>
30 #include <linux/module.h>
31 #include <linux/platform_data/leds-kirkwood-ns2.h>
32 #include <linux/of.h>
33 #include <linux/of_gpio.h>
34 #include "leds.h"
35 
36 /*
37  * The Network Space v2 dual-GPIO LED is wired to a CPLD. Three different LED
38  * modes are available: off, on and SATA activity blinking. The LED modes are
39  * controlled through two GPIOs (command and slow): each combination of values
40  * for the command/slow GPIOs corresponds to a LED mode.
41  */
42 
43 struct ns2_led_data {
44 	struct led_classdev	cdev;
45 	unsigned int		cmd;
46 	unsigned int		slow;
47 	bool			can_sleep;
48 	unsigned char		sata; /* True when SATA mode active. */
49 	rwlock_t		rw_lock; /* Lock GPIOs. */
50 	int			num_modes;
51 	struct ns2_led_modval	*modval;
52 };
53 
54 static int ns2_led_get_mode(struct ns2_led_data *led_dat,
55 			    enum ns2_led_modes *mode)
56 {
57 	int i;
58 	int ret = -EINVAL;
59 	int cmd_level;
60 	int slow_level;
61 
62 	cmd_level = gpio_get_value_cansleep(led_dat->cmd);
63 	slow_level = gpio_get_value_cansleep(led_dat->slow);
64 
65 	for (i = 0; i < led_dat->num_modes; i++) {
66 		if (cmd_level == led_dat->modval[i].cmd_level &&
67 		    slow_level == led_dat->modval[i].slow_level) {
68 			*mode = led_dat->modval[i].mode;
69 			ret = 0;
70 			break;
71 		}
72 	}
73 
74 	return ret;
75 }
76 
77 static void ns2_led_set_mode(struct ns2_led_data *led_dat,
78 			     enum ns2_led_modes mode)
79 {
80 	int i;
81 	bool found = false;
82 	unsigned long flags;
83 
84 	for (i = 0; i < led_dat->num_modes; i++)
85 		if (mode == led_dat->modval[i].mode) {
86 			found = true;
87 			break;
88 		}
89 
90 	if (!found)
91 		return;
92 
93 	write_lock_irqsave(&led_dat->rw_lock, flags);
94 
95 	if (!led_dat->can_sleep) {
96 		gpio_set_value(led_dat->cmd,
97 			       led_dat->modval[i].cmd_level);
98 		gpio_set_value(led_dat->slow,
99 			       led_dat->modval[i].slow_level);
100 		goto exit_unlock;
101 	}
102 
103 	gpio_set_value_cansleep(led_dat->cmd, led_dat->modval[i].cmd_level);
104 	gpio_set_value_cansleep(led_dat->slow, led_dat->modval[i].slow_level);
105 
106 exit_unlock:
107 	write_unlock_irqrestore(&led_dat->rw_lock, flags);
108 }
109 
110 static void ns2_led_set(struct led_classdev *led_cdev,
111 			enum led_brightness value)
112 {
113 	struct ns2_led_data *led_dat =
114 		container_of(led_cdev, struct ns2_led_data, cdev);
115 	enum ns2_led_modes mode;
116 
117 	if (value == LED_OFF)
118 		mode = NS_V2_LED_OFF;
119 	else if (led_dat->sata)
120 		mode = NS_V2_LED_SATA;
121 	else
122 		mode = NS_V2_LED_ON;
123 
124 	ns2_led_set_mode(led_dat, mode);
125 }
126 
127 static int ns2_led_set_blocking(struct led_classdev *led_cdev,
128 			enum led_brightness value)
129 {
130 	ns2_led_set(led_cdev, value);
131 	return 0;
132 }
133 
134 static ssize_t ns2_led_sata_store(struct device *dev,
135 				  struct device_attribute *attr,
136 				  const char *buff, size_t count)
137 {
138 	struct led_classdev *led_cdev = dev_get_drvdata(dev);
139 	struct ns2_led_data *led_dat =
140 		container_of(led_cdev, struct ns2_led_data, cdev);
141 	int ret;
142 	unsigned long enable;
143 
144 	ret = kstrtoul(buff, 10, &enable);
145 	if (ret < 0)
146 		return ret;
147 
148 	enable = !!enable;
149 
150 	if (led_dat->sata == enable)
151 		goto exit;
152 
153 	led_dat->sata = enable;
154 
155 	if (!led_get_brightness(led_cdev))
156 		goto exit;
157 
158 	if (enable)
159 		ns2_led_set_mode(led_dat, NS_V2_LED_SATA);
160 	else
161 		ns2_led_set_mode(led_dat, NS_V2_LED_ON);
162 
163 exit:
164 	return count;
165 }
166 
167 static ssize_t ns2_led_sata_show(struct device *dev,
168 				 struct device_attribute *attr, char *buf)
169 {
170 	struct led_classdev *led_cdev = dev_get_drvdata(dev);
171 	struct ns2_led_data *led_dat =
172 		container_of(led_cdev, struct ns2_led_data, cdev);
173 
174 	return sprintf(buf, "%d\n", led_dat->sata);
175 }
176 
177 static DEVICE_ATTR(sata, 0644, ns2_led_sata_show, ns2_led_sata_store);
178 
179 static struct attribute *ns2_led_attrs[] = {
180 	&dev_attr_sata.attr,
181 	NULL
182 };
183 ATTRIBUTE_GROUPS(ns2_led);
184 
185 static int
186 create_ns2_led(struct platform_device *pdev, struct ns2_led_data *led_dat,
187 	       const struct ns2_led *template)
188 {
189 	int ret;
190 	enum ns2_led_modes mode;
191 
192 	ret = devm_gpio_request_one(&pdev->dev, template->cmd,
193 			gpio_get_value_cansleep(template->cmd) ?
194 			GPIOF_OUT_INIT_HIGH : GPIOF_OUT_INIT_LOW,
195 			template->name);
196 	if (ret) {
197 		dev_err(&pdev->dev, "%s: failed to setup command GPIO\n",
198 			template->name);
199 		return ret;
200 	}
201 
202 	ret = devm_gpio_request_one(&pdev->dev, template->slow,
203 			gpio_get_value_cansleep(template->slow) ?
204 			GPIOF_OUT_INIT_HIGH : GPIOF_OUT_INIT_LOW,
205 			template->name);
206 	if (ret) {
207 		dev_err(&pdev->dev, "%s: failed to setup slow GPIO\n",
208 			template->name);
209 		return ret;
210 	}
211 
212 	rwlock_init(&led_dat->rw_lock);
213 
214 	led_dat->cdev.name = template->name;
215 	led_dat->cdev.default_trigger = template->default_trigger;
216 	led_dat->cdev.blink_set = NULL;
217 	led_dat->cdev.flags |= LED_CORE_SUSPENDRESUME;
218 	led_dat->cdev.groups = ns2_led_groups;
219 	led_dat->cmd = template->cmd;
220 	led_dat->slow = template->slow;
221 	led_dat->can_sleep = gpio_cansleep(led_dat->cmd) |
222 				gpio_cansleep(led_dat->slow);
223 	if (led_dat->can_sleep)
224 		led_dat->cdev.brightness_set_blocking = ns2_led_set_blocking;
225 	else
226 		led_dat->cdev.brightness_set = ns2_led_set;
227 	led_dat->modval = template->modval;
228 	led_dat->num_modes = template->num_modes;
229 
230 	ret = ns2_led_get_mode(led_dat, &mode);
231 	if (ret < 0)
232 		return ret;
233 
234 	/* Set LED initial state. */
235 	led_dat->sata = (mode == NS_V2_LED_SATA) ? 1 : 0;
236 	led_dat->cdev.brightness =
237 		(mode == NS_V2_LED_OFF) ? LED_OFF : LED_FULL;
238 
239 	ret = led_classdev_register(&pdev->dev, &led_dat->cdev);
240 	if (ret < 0)
241 		return ret;
242 
243 	return 0;
244 }
245 
246 static void delete_ns2_led(struct ns2_led_data *led_dat)
247 {
248 	led_classdev_unregister(&led_dat->cdev);
249 }
250 
251 #ifdef CONFIG_OF_GPIO
252 /*
253  * Translate OpenFirmware node properties into platform_data.
254  */
255 static int
256 ns2_leds_get_of_pdata(struct device *dev, struct ns2_led_platform_data *pdata)
257 {
258 	struct device_node *np = dev->of_node;
259 	struct device_node *child;
260 	struct ns2_led *led, *leds;
261 	int num_leds = 0;
262 
263 	num_leds = of_get_child_count(np);
264 	if (!num_leds)
265 		return -ENODEV;
266 
267 	leds = devm_kcalloc(dev, num_leds, sizeof(struct ns2_led),
268 			    GFP_KERNEL);
269 	if (!leds)
270 		return -ENOMEM;
271 
272 	led = leds;
273 	for_each_child_of_node(np, child) {
274 		const char *string;
275 		int ret, i, num_modes;
276 		struct ns2_led_modval *modval;
277 
278 		ret = of_get_named_gpio(child, "cmd-gpio", 0);
279 		if (ret < 0)
280 			return ret;
281 		led->cmd = ret;
282 		ret = of_get_named_gpio(child, "slow-gpio", 0);
283 		if (ret < 0)
284 			return ret;
285 		led->slow = ret;
286 		ret = of_property_read_string(child, "label", &string);
287 		led->name = (ret == 0) ? string : child->name;
288 		ret = of_property_read_string(child, "linux,default-trigger",
289 					      &string);
290 		if (ret == 0)
291 			led->default_trigger = string;
292 
293 		ret = of_property_count_u32_elems(child, "modes-map");
294 		if (ret < 0 || ret % 3) {
295 			dev_err(dev,
296 				"Missing or malformed modes-map property\n");
297 			return -EINVAL;
298 		}
299 
300 		num_modes = ret / 3;
301 		modval = devm_kcalloc(dev,
302 				      num_modes,
303 				      sizeof(struct ns2_led_modval),
304 				      GFP_KERNEL);
305 		if (!modval)
306 			return -ENOMEM;
307 
308 		for (i = 0; i < num_modes; i++) {
309 			of_property_read_u32_index(child,
310 						"modes-map", 3 * i,
311 						(u32 *) &modval[i].mode);
312 			of_property_read_u32_index(child,
313 						"modes-map", 3 * i + 1,
314 						(u32 *) &modval[i].cmd_level);
315 			of_property_read_u32_index(child,
316 						"modes-map", 3 * i + 2,
317 						(u32 *) &modval[i].slow_level);
318 		}
319 
320 		led->num_modes = num_modes;
321 		led->modval = modval;
322 
323 		led++;
324 	}
325 
326 	pdata->leds = leds;
327 	pdata->num_leds = num_leds;
328 
329 	return 0;
330 }
331 
332 static const struct of_device_id of_ns2_leds_match[] = {
333 	{ .compatible = "lacie,ns2-leds", },
334 	{},
335 };
336 MODULE_DEVICE_TABLE(of, of_ns2_leds_match);
337 #endif /* CONFIG_OF_GPIO */
338 
339 struct ns2_led_priv {
340 	int num_leds;
341 	struct ns2_led_data leds_data[];
342 };
343 
344 static inline int sizeof_ns2_led_priv(int num_leds)
345 {
346 	return sizeof(struct ns2_led_priv) +
347 		      (sizeof(struct ns2_led_data) * num_leds);
348 }
349 
350 static int ns2_led_probe(struct platform_device *pdev)
351 {
352 	struct ns2_led_platform_data *pdata = dev_get_platdata(&pdev->dev);
353 	struct ns2_led_priv *priv;
354 	int i;
355 	int ret;
356 
357 #ifdef CONFIG_OF_GPIO
358 	if (!pdata) {
359 		pdata = devm_kzalloc(&pdev->dev,
360 				     sizeof(struct ns2_led_platform_data),
361 				     GFP_KERNEL);
362 		if (!pdata)
363 			return -ENOMEM;
364 
365 		ret = ns2_leds_get_of_pdata(&pdev->dev, pdata);
366 		if (ret)
367 			return ret;
368 	}
369 #else
370 	if (!pdata)
371 		return -EINVAL;
372 #endif /* CONFIG_OF_GPIO */
373 
374 	priv = devm_kzalloc(&pdev->dev,
375 			    sizeof_ns2_led_priv(pdata->num_leds), GFP_KERNEL);
376 	if (!priv)
377 		return -ENOMEM;
378 	priv->num_leds = pdata->num_leds;
379 
380 	for (i = 0; i < priv->num_leds; i++) {
381 		ret = create_ns2_led(pdev, &priv->leds_data[i],
382 				     &pdata->leds[i]);
383 		if (ret < 0) {
384 			for (i = i - 1; i >= 0; i--)
385 				delete_ns2_led(&priv->leds_data[i]);
386 			return ret;
387 		}
388 	}
389 
390 	platform_set_drvdata(pdev, priv);
391 
392 	return 0;
393 }
394 
395 static int ns2_led_remove(struct platform_device *pdev)
396 {
397 	int i;
398 	struct ns2_led_priv *priv;
399 
400 	priv = platform_get_drvdata(pdev);
401 
402 	for (i = 0; i < priv->num_leds; i++)
403 		delete_ns2_led(&priv->leds_data[i]);
404 
405 	return 0;
406 }
407 
408 static struct platform_driver ns2_led_driver = {
409 	.probe		= ns2_led_probe,
410 	.remove		= ns2_led_remove,
411 	.driver		= {
412 		.name		= "leds-ns2",
413 		.of_match_table	= of_match_ptr(of_ns2_leds_match),
414 	},
415 };
416 
417 module_platform_driver(ns2_led_driver);
418 
419 MODULE_AUTHOR("Simon Guinot <sguinot@lacie.com>");
420 MODULE_DESCRIPTION("Network Space v2 LED driver");
421 MODULE_LICENSE("GPL");
422 MODULE_ALIAS("platform:leds-ns2");
423