xref: /openbmc/linux/drivers/leds/leds-ns2.c (revision 48b77cdc)
1 // SPDX-License-Identifier: GPL-2.0-or-later
2 /*
3  * leds-ns2.c - Driver for the Network Space v2 (and parents) dual-GPIO LED
4  *
5  * Copyright (C) 2010 LaCie
6  *
7  * Author: Simon Guinot <sguinot@lacie.com>
8  *
9  * Based on leds-gpio.c by Raphael Assenat <raph@8d.com>
10  */
11 
12 #include <linux/kernel.h>
13 #include <linux/platform_device.h>
14 #include <linux/slab.h>
15 #include <linux/gpio/consumer.h>
16 #include <linux/leds.h>
17 #include <linux/module.h>
18 #include <linux/of.h>
19 #include "leds.h"
20 
21 enum ns2_led_modes {
22 	NS_V2_LED_OFF,
23 	NS_V2_LED_ON,
24 	NS_V2_LED_SATA,
25 };
26 
27 struct ns2_led_modval {
28 	enum ns2_led_modes	mode;
29 	int			cmd_level;
30 	int			slow_level;
31 };
32 
33 /*
34  * The Network Space v2 dual-GPIO LED is wired to a CPLD. Three different LED
35  * modes are available: off, on and SATA activity blinking. The LED modes are
36  * controlled through two GPIOs (command and slow): each combination of values
37  * for the command/slow GPIOs corresponds to a LED mode.
38  */
39 
40 struct ns2_led {
41 	struct led_classdev	cdev;
42 	struct gpio_desc	*cmd;
43 	struct gpio_desc	*slow;
44 	bool			can_sleep;
45 	unsigned char		sata; /* True when SATA mode active. */
46 	rwlock_t		rw_lock; /* Lock GPIOs. */
47 	int			num_modes;
48 	struct ns2_led_modval	*modval;
49 };
50 
51 static int ns2_led_get_mode(struct ns2_led *led, enum ns2_led_modes *mode)
52 {
53 	int i;
54 	int cmd_level;
55 	int slow_level;
56 
57 	cmd_level = gpiod_get_value_cansleep(led->cmd);
58 	slow_level = gpiod_get_value_cansleep(led->slow);
59 
60 	for (i = 0; i < led->num_modes; i++) {
61 		if (cmd_level == led->modval[i].cmd_level &&
62 		    slow_level == led->modval[i].slow_level) {
63 			*mode = led->modval[i].mode;
64 			return 0;
65 		}
66 	}
67 
68 	return -EINVAL;
69 }
70 
71 static void ns2_led_set_mode(struct ns2_led *led, enum ns2_led_modes mode)
72 {
73 	int i;
74 	unsigned long flags;
75 
76 	for (i = 0; i < led->num_modes; i++)
77 		if (mode == led->modval[i].mode)
78 			break;
79 
80 	if (i == led->num_modes)
81 		return;
82 
83 	write_lock_irqsave(&led->rw_lock, flags);
84 
85 	if (!led->can_sleep) {
86 		gpiod_set_value(led->cmd, led->modval[i].cmd_level);
87 		gpiod_set_value(led->slow, led->modval[i].slow_level);
88 		goto exit_unlock;
89 	}
90 
91 	gpiod_set_value_cansleep(led->cmd, led->modval[i].cmd_level);
92 	gpiod_set_value_cansleep(led->slow, led->modval[i].slow_level);
93 
94 exit_unlock:
95 	write_unlock_irqrestore(&led->rw_lock, flags);
96 }
97 
98 static void ns2_led_set(struct led_classdev *led_cdev,
99 			enum led_brightness value)
100 {
101 	struct ns2_led *led = container_of(led_cdev, struct ns2_led, cdev);
102 	enum ns2_led_modes mode;
103 
104 	if (value == LED_OFF)
105 		mode = NS_V2_LED_OFF;
106 	else if (led->sata)
107 		mode = NS_V2_LED_SATA;
108 	else
109 		mode = NS_V2_LED_ON;
110 
111 	ns2_led_set_mode(led, mode);
112 }
113 
114 static int ns2_led_set_blocking(struct led_classdev *led_cdev,
115 			enum led_brightness value)
116 {
117 	ns2_led_set(led_cdev, value);
118 	return 0;
119 }
120 
121 static ssize_t ns2_led_sata_store(struct device *dev,
122 				  struct device_attribute *attr,
123 				  const char *buff, size_t count)
124 {
125 	struct led_classdev *led_cdev = dev_get_drvdata(dev);
126 	struct ns2_led *led = container_of(led_cdev, struct ns2_led, cdev);
127 	int ret;
128 	unsigned long enable;
129 
130 	ret = kstrtoul(buff, 10, &enable);
131 	if (ret < 0)
132 		return ret;
133 
134 	enable = !!enable;
135 
136 	if (led->sata == enable)
137 		goto exit;
138 
139 	led->sata = enable;
140 
141 	if (!led_get_brightness(led_cdev))
142 		goto exit;
143 
144 	if (enable)
145 		ns2_led_set_mode(led, NS_V2_LED_SATA);
146 	else
147 		ns2_led_set_mode(led, NS_V2_LED_ON);
148 
149 exit:
150 	return count;
151 }
152 
153 static ssize_t ns2_led_sata_show(struct device *dev,
154 				 struct device_attribute *attr, char *buf)
155 {
156 	struct led_classdev *led_cdev = dev_get_drvdata(dev);
157 	struct ns2_led *led = container_of(led_cdev, struct ns2_led, cdev);
158 
159 	return sprintf(buf, "%d\n", led->sata);
160 }
161 
162 static DEVICE_ATTR(sata, 0644, ns2_led_sata_show, ns2_led_sata_store);
163 
164 static struct attribute *ns2_led_attrs[] = {
165 	&dev_attr_sata.attr,
166 	NULL
167 };
168 ATTRIBUTE_GROUPS(ns2_led);
169 
170 static int ns2_led_register(struct device *dev, struct device_node *np,
171 			    struct ns2_led *led)
172 {
173 	struct ns2_led_modval *modval;
174 	enum ns2_led_modes mode;
175 	int nmodes, ret, i;
176 
177 	ret = of_property_read_string(np, "label", &led->cdev.name);
178 	if (ret)
179 		led->cdev.name = np->name;
180 
181 	led->cmd = devm_gpiod_get_from_of_node(dev, np, "cmd-gpio", 0,
182 					       GPIOD_ASIS, np->name);
183 	if (IS_ERR(led->cmd))
184 		return PTR_ERR(led->cmd);
185 
186 	led->slow = devm_gpiod_get_from_of_node(dev, np, "slow-gpio", 0,
187 						GPIOD_ASIS, np->name);
188 	if (IS_ERR(led->slow))
189 		return PTR_ERR(led->slow);
190 
191 	of_property_read_string(np, "linux,default-trigger",
192 				&led->cdev.default_trigger);
193 
194 	ret = of_property_count_u32_elems(np, "modes-map");
195 	if (ret < 0 || ret % 3) {
196 		dev_err(dev, "Missing or malformed modes-map for %pOF\n", np);
197 		return -EINVAL;
198 	}
199 
200 	nmodes = ret / 3;
201 	modval = devm_kcalloc(dev, nmodes, sizeof(*modval), GFP_KERNEL);
202 	if (!modval)
203 		return -ENOMEM;
204 
205 	for (i = 0; i < nmodes; i++) {
206 		u32 val;
207 
208 		of_property_read_u32_index(np, "modes-map", 3 * i, &val);
209 		modval[i].mode = val;
210 		of_property_read_u32_index(np, "modes-map", 3 * i + 1, &val);
211 		modval[i].cmd_level = val;
212 		of_property_read_u32_index(np, "modes-map", 3 * i + 2, &val);
213 		modval[i].slow_level = val;
214 	}
215 
216 	rwlock_init(&led->rw_lock);
217 
218 	led->cdev.blink_set = NULL;
219 	led->cdev.flags |= LED_CORE_SUSPENDRESUME;
220 	led->cdev.groups = ns2_led_groups;
221 	led->can_sleep = gpiod_cansleep(led->cmd) || gpiod_cansleep(led->slow);
222 	if (led->can_sleep)
223 		led->cdev.brightness_set_blocking = ns2_led_set_blocking;
224 	else
225 		led->cdev.brightness_set = ns2_led_set;
226 	led->num_modes = nmodes;
227 	led->modval = modval;
228 
229 	ret = ns2_led_get_mode(led, &mode);
230 	if (ret < 0)
231 		return ret;
232 
233 	/* Set LED initial state. */
234 	led->sata = (mode == NS_V2_LED_SATA) ? 1 : 0;
235 	led->cdev.brightness = (mode == NS_V2_LED_OFF) ? LED_OFF : LED_FULL;
236 
237 	ret = devm_led_classdev_register(dev, &led->cdev);
238 	if (ret)
239 		dev_err(dev, "Failed to register LED for node %pOF\n", np);
240 
241 	return ret;
242 }
243 
244 static const struct of_device_id of_ns2_leds_match[] = {
245 	{ .compatible = "lacie,ns2-leds", },
246 	{},
247 };
248 MODULE_DEVICE_TABLE(of, of_ns2_leds_match);
249 
250 static int ns2_led_probe(struct platform_device *pdev)
251 {
252 	struct device *dev = &pdev->dev;
253 	struct device_node *np, *child;
254 	struct ns2_led *leds;
255 	int count;
256 	int ret;
257 
258 	np = dev_of_node(dev);
259 	count = of_get_available_child_count(np);
260 	if (!count)
261 		return -ENODEV;
262 
263 	leds = devm_kzalloc(dev, array_size(sizeof(*leds), count), GFP_KERNEL);
264 	if (!leds)
265 		return -ENOMEM;
266 
267 	for_each_available_child_of_node(np, child) {
268 		ret = ns2_led_register(dev, child, leds++);
269 		if (ret) {
270 			of_node_put(child);
271 			return ret;
272 		}
273 	}
274 
275 	return 0;
276 }
277 
278 static struct platform_driver ns2_led_driver = {
279 	.probe		= ns2_led_probe,
280 	.driver		= {
281 		.name		= "leds-ns2",
282 		.of_match_table	= of_match_ptr(of_ns2_leds_match),
283 	},
284 };
285 
286 module_platform_driver(ns2_led_driver);
287 
288 MODULE_AUTHOR("Simon Guinot <sguinot@lacie.com>");
289 MODULE_DESCRIPTION("Network Space v2 LED driver");
290 MODULE_LICENSE("GPL");
291 MODULE_ALIAS("platform:leds-ns2");
292