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