1 // SPDX-License-Identifier: GPL-2.0-or-later
3 * leds-ns2.c - Driver for the Network Space v2 (and parents) dual-GPIO LED
5 * Copyright (C) 2010 LaCie
7 * Author: Simon Guinot <sguinot@lacie.com>
9 * Based on leds-gpio.c by Raphael Assenat <raph@8d.com>
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>
27 struct ns2_led_modval {
28 enum ns2_led_modes mode;
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.
41 struct led_classdev cdev;
42 struct gpio_desc *cmd;
43 struct gpio_desc *slow;
45 unsigned char sata; /* True when SATA mode active. */
46 rwlock_t rw_lock; /* Lock GPIOs. */
48 struct ns2_led_modval *modval;
51 static int ns2_led_get_mode(struct ns2_led *led, enum ns2_led_modes *mode)
57 cmd_level = gpiod_get_value_cansleep(led->cmd);
58 slow_level = gpiod_get_value_cansleep(led->slow);
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;
71 static void ns2_led_set_mode(struct ns2_led *led, enum ns2_led_modes mode)
77 for (i = 0; i < led->num_modes; i++)
78 if (mode == led->modval[i].mode) {
86 write_lock_irqsave(&led->rw_lock, flags);
88 if (!led->can_sleep) {
89 gpiod_set_value(led->cmd, led->modval[i].cmd_level);
90 gpiod_set_value(led->slow, led->modval[i].slow_level);
94 gpiod_set_value_cansleep(led->cmd, led->modval[i].cmd_level);
95 gpiod_set_value_cansleep(led->slow, led->modval[i].slow_level);
98 write_unlock_irqrestore(&led->rw_lock, flags);
101 static void ns2_led_set(struct led_classdev *led_cdev,
102 enum led_brightness value)
104 struct ns2_led *led = container_of(led_cdev, struct ns2_led, cdev);
105 enum ns2_led_modes mode;
107 if (value == LED_OFF)
108 mode = NS_V2_LED_OFF;
110 mode = NS_V2_LED_SATA;
114 ns2_led_set_mode(led, mode);
117 static int ns2_led_set_blocking(struct led_classdev *led_cdev,
118 enum led_brightness value)
120 ns2_led_set(led_cdev, value);
124 static ssize_t ns2_led_sata_store(struct device *dev,
125 struct device_attribute *attr,
126 const char *buff, size_t count)
128 struct led_classdev *led_cdev = dev_get_drvdata(dev);
129 struct ns2_led *led = container_of(led_cdev, struct ns2_led, cdev);
131 unsigned long enable;
133 ret = kstrtoul(buff, 10, &enable);
139 if (led->sata == enable)
144 if (!led_get_brightness(led_cdev))
148 ns2_led_set_mode(led, NS_V2_LED_SATA);
150 ns2_led_set_mode(led, NS_V2_LED_ON);
156 static ssize_t ns2_led_sata_show(struct device *dev,
157 struct device_attribute *attr, char *buf)
159 struct led_classdev *led_cdev = dev_get_drvdata(dev);
160 struct ns2_led *led = container_of(led_cdev, struct ns2_led, cdev);
162 return sprintf(buf, "%d\n", led->sata);
165 static DEVICE_ATTR(sata, 0644, ns2_led_sata_show, ns2_led_sata_store);
167 static struct attribute *ns2_led_attrs[] = {
171 ATTRIBUTE_GROUPS(ns2_led);
173 static int ns2_led_register(struct device *dev, struct device_node *np,
176 struct ns2_led_modval *modval;
177 enum ns2_led_modes mode;
180 ret = of_property_read_string(np, "label", &led->cdev.name);
182 led->cdev.name = np->name;
184 led->cmd = devm_gpiod_get_from_of_node(dev, np, "cmd-gpio", 0,
185 GPIOD_ASIS, np->name);
186 if (IS_ERR(led->cmd))
187 return PTR_ERR(led->cmd);
189 led->slow = devm_gpiod_get_from_of_node(dev, np, "slow-gpio", 0,
190 GPIOD_ASIS, np->name);
191 if (IS_ERR(led->slow))
192 return PTR_ERR(led->slow);
194 of_property_read_string(np, "linux,default-trigger",
195 &led->cdev.default_trigger);
197 ret = of_property_count_u32_elems(np, "modes-map");
198 if (ret < 0 || ret % 3) {
199 dev_err(dev, "Missing or malformed modes-map for %pOF\n", np);
204 modval = devm_kcalloc(dev, nmodes, sizeof(*modval), GFP_KERNEL);
208 for (i = 0; i < nmodes; i++) {
211 of_property_read_u32_index(np, "modes-map", 3 * i, &val);
212 modval[i].mode = val;
213 of_property_read_u32_index(np, "modes-map", 3 * i + 1, &val);
214 modval[i].cmd_level = val;
215 of_property_read_u32_index(np, "modes-map", 3 * i + 2, &val);
216 modval[i].slow_level = val;
219 rwlock_init(&led->rw_lock);
221 led->cdev.blink_set = NULL;
222 led->cdev.flags |= LED_CORE_SUSPENDRESUME;
223 led->cdev.groups = ns2_led_groups;
224 led->can_sleep = gpiod_cansleep(led->cmd) || gpiod_cansleep(led->slow);
226 led->cdev.brightness_set_blocking = ns2_led_set_blocking;
228 led->cdev.brightness_set = ns2_led_set;
229 led->num_modes = nmodes;
230 led->modval = modval;
232 ret = ns2_led_get_mode(led, &mode);
236 /* Set LED initial state. */
237 led->sata = (mode == NS_V2_LED_SATA) ? 1 : 0;
238 led->cdev.brightness = (mode == NS_V2_LED_OFF) ? LED_OFF : LED_FULL;
240 ret = devm_led_classdev_register(dev, &led->cdev);
242 dev_err(dev, "Failed to register LED for node %pOF\n", np);
247 static const struct of_device_id of_ns2_leds_match[] = {
248 { .compatible = "lacie,ns2-leds", },
251 MODULE_DEVICE_TABLE(of, of_ns2_leds_match);
253 static int ns2_led_probe(struct platform_device *pdev)
255 struct device *dev = &pdev->dev;
256 struct device_node *np, *child;
257 struct ns2_led *leds;
261 np = dev_of_node(dev);
262 count = of_get_available_child_count(np);
266 leds = devm_kzalloc(dev, array_size(sizeof(*leds), count), GFP_KERNEL);
270 for_each_available_child_of_node(np, child) {
271 ret = ns2_led_register(dev, child, leds++);
281 static struct platform_driver ns2_led_driver = {
282 .probe = ns2_led_probe,
285 .of_match_table = of_match_ptr(of_ns2_leds_match),
289 module_platform_driver(ns2_led_driver);
291 MODULE_AUTHOR("Simon Guinot <sguinot@lacie.com>");
292 MODULE_DESCRIPTION("Network Space v2 LED driver");
293 MODULE_LICENSE("GPL");
294 MODULE_ALIAS("platform:leds-ns2");