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 struct ns2_led_of_one { 34 const char *name; 35 const char *default_trigger; 36 struct gpio_desc *cmd; 37 struct gpio_desc *slow; 38 int num_modes; 39 struct ns2_led_modval *modval; 40 }; 41 42 struct ns2_led_of { 43 int num_leds; 44 struct ns2_led_of_one *leds; 45 }; 46 47 /* 48 * The Network Space v2 dual-GPIO LED is wired to a CPLD. Three different LED 49 * modes are available: off, on and SATA activity blinking. The LED modes are 50 * controlled through two GPIOs (command and slow): each combination of values 51 * for the command/slow GPIOs corresponds to a LED mode. 52 */ 53 54 struct ns2_led { 55 struct led_classdev cdev; 56 struct gpio_desc *cmd; 57 struct gpio_desc *slow; 58 bool can_sleep; 59 unsigned char sata; /* True when SATA mode active. */ 60 rwlock_t rw_lock; /* Lock GPIOs. */ 61 int num_modes; 62 struct ns2_led_modval *modval; 63 }; 64 65 static int ns2_led_get_mode(struct ns2_led *led, enum ns2_led_modes *mode) 66 { 67 int i; 68 int cmd_level; 69 int slow_level; 70 71 cmd_level = gpiod_get_value_cansleep(led->cmd); 72 slow_level = gpiod_get_value_cansleep(led->slow); 73 74 for (i = 0; i < led->num_modes; i++) { 75 if (cmd_level == led->modval[i].cmd_level && 76 slow_level == led->modval[i].slow_level) { 77 *mode = led->modval[i].mode; 78 return 0; 79 } 80 } 81 82 return -EINVAL; 83 } 84 85 static void ns2_led_set_mode(struct ns2_led *led, enum ns2_led_modes mode) 86 { 87 int i; 88 bool found = false; 89 unsigned long flags; 90 91 for (i = 0; i < led->num_modes; i++) 92 if (mode == led->modval[i].mode) { 93 found = true; 94 break; 95 } 96 97 if (!found) 98 return; 99 100 write_lock_irqsave(&led->rw_lock, flags); 101 102 if (!led->can_sleep) { 103 gpiod_set_value(led->cmd, led->modval[i].cmd_level); 104 gpiod_set_value(led->slow, led->modval[i].slow_level); 105 goto exit_unlock; 106 } 107 108 gpiod_set_value_cansleep(led->cmd, led->modval[i].cmd_level); 109 gpiod_set_value_cansleep(led->slow, led->modval[i].slow_level); 110 111 exit_unlock: 112 write_unlock_irqrestore(&led->rw_lock, flags); 113 } 114 115 static void ns2_led_set(struct led_classdev *led_cdev, 116 enum led_brightness value) 117 { 118 struct ns2_led *led = container_of(led_cdev, struct ns2_led, cdev); 119 enum ns2_led_modes mode; 120 121 if (value == LED_OFF) 122 mode = NS_V2_LED_OFF; 123 else if (led->sata) 124 mode = NS_V2_LED_SATA; 125 else 126 mode = NS_V2_LED_ON; 127 128 ns2_led_set_mode(led, mode); 129 } 130 131 static int ns2_led_set_blocking(struct led_classdev *led_cdev, 132 enum led_brightness value) 133 { 134 ns2_led_set(led_cdev, value); 135 return 0; 136 } 137 138 static ssize_t ns2_led_sata_store(struct device *dev, 139 struct device_attribute *attr, 140 const char *buff, size_t count) 141 { 142 struct led_classdev *led_cdev = dev_get_drvdata(dev); 143 struct ns2_led *led = container_of(led_cdev, struct ns2_led, cdev); 144 int ret; 145 unsigned long enable; 146 147 ret = kstrtoul(buff, 10, &enable); 148 if (ret < 0) 149 return ret; 150 151 enable = !!enable; 152 153 if (led->sata == enable) 154 goto exit; 155 156 led->sata = enable; 157 158 if (!led_get_brightness(led_cdev)) 159 goto exit; 160 161 if (enable) 162 ns2_led_set_mode(led, NS_V2_LED_SATA); 163 else 164 ns2_led_set_mode(led, NS_V2_LED_ON); 165 166 exit: 167 return count; 168 } 169 170 static ssize_t ns2_led_sata_show(struct device *dev, 171 struct device_attribute *attr, char *buf) 172 { 173 struct led_classdev *led_cdev = dev_get_drvdata(dev); 174 struct ns2_led *led = container_of(led_cdev, struct ns2_led, cdev); 175 176 return sprintf(buf, "%d\n", led->sata); 177 } 178 179 static DEVICE_ATTR(sata, 0644, ns2_led_sata_show, ns2_led_sata_store); 180 181 static struct attribute *ns2_led_attrs[] = { 182 &dev_attr_sata.attr, 183 NULL 184 }; 185 ATTRIBUTE_GROUPS(ns2_led); 186 187 static int 188 create_ns2_led(struct platform_device *pdev, struct ns2_led *led, 189 const struct ns2_led_of_one *template) 190 { 191 int ret; 192 enum ns2_led_modes mode; 193 194 rwlock_init(&led->rw_lock); 195 196 led->cdev.name = template->name; 197 led->cdev.default_trigger = template->default_trigger; 198 led->cdev.blink_set = NULL; 199 led->cdev.flags |= LED_CORE_SUSPENDRESUME; 200 led->cdev.groups = ns2_led_groups; 201 led->cmd = template->cmd; 202 led->slow = template->slow; 203 led->can_sleep = gpiod_cansleep(led->cmd) | gpiod_cansleep(led->slow); 204 if (led->can_sleep) 205 led->cdev.brightness_set_blocking = ns2_led_set_blocking; 206 else 207 led->cdev.brightness_set = ns2_led_set; 208 led->modval = template->modval; 209 led->num_modes = template->num_modes; 210 211 ret = ns2_led_get_mode(led, &mode); 212 if (ret < 0) 213 return ret; 214 215 /* Set LED initial state. */ 216 led->sata = (mode == NS_V2_LED_SATA) ? 1 : 0; 217 led->cdev.brightness = (mode == NS_V2_LED_OFF) ? LED_OFF : LED_FULL; 218 219 return devm_led_classdev_register(&pdev->dev, &led->cdev); 220 } 221 222 static int ns2_leds_parse_one(struct device *dev, struct device_node *np, 223 struct ns2_led_of_one *led) 224 { 225 struct ns2_led_modval *modval; 226 int nmodes, ret, i; 227 228 ret = of_property_read_string(np, "label", &led->name); 229 if (ret) 230 led->name = np->name; 231 232 led->cmd = devm_gpiod_get_from_of_node(dev, np, "cmd-gpio", 0, 233 GPIOD_ASIS, led->name); 234 if (IS_ERR(led->cmd)) 235 return PTR_ERR(led->cmd); 236 237 led->slow = devm_gpiod_get_from_of_node(dev, np, "slow-gpio", 0, 238 GPIOD_ASIS, led->name); 239 if (IS_ERR(led->slow)) 240 return PTR_ERR(led->slow); 241 242 of_property_read_string(np, "linux,default-trigger", 243 &led->default_trigger); 244 245 ret = of_property_count_u32_elems(np, "modes-map"); 246 if (ret < 0 || ret % 3) { 247 dev_err(dev, "Missing or malformed modes-map for %pOF\n", np); 248 return -EINVAL; 249 } 250 251 nmodes = ret / 3; 252 modval = devm_kcalloc(dev, nmodes, sizeof(*modval), GFP_KERNEL); 253 if (!modval) 254 return -ENOMEM; 255 256 for (i = 0; i < nmodes; i++) { 257 u32 val; 258 259 of_property_read_u32_index(np, "modes-map", 3 * i, &val); 260 modval[i].mode = val; 261 of_property_read_u32_index(np, "modes-map", 3 * i + 1, &val); 262 modval[i].cmd_level = val; 263 of_property_read_u32_index(np, "modes-map", 3 * i + 2, &val); 264 modval[i].slow_level = val; 265 } 266 267 led->num_modes = nmodes; 268 led->modval = modval; 269 270 return 0; 271 } 272 273 /* 274 * Translate OpenFirmware node properties into platform_data. 275 */ 276 static int 277 ns2_leds_parse_of(struct device *dev, struct ns2_led_of *ofdata) 278 { 279 struct device_node *np = dev_of_node(dev); 280 struct device_node *child; 281 struct ns2_led_of_one *led, *leds; 282 int ret, num_leds = 0; 283 284 num_leds = of_get_available_child_count(np); 285 if (!num_leds) 286 return -ENODEV; 287 288 leds = devm_kcalloc(dev, num_leds, sizeof(struct ns2_led), 289 GFP_KERNEL); 290 if (!leds) 291 return -ENOMEM; 292 293 led = leds; 294 for_each_available_child_of_node(np, child) { 295 ret = ns2_leds_parse_one(dev, child, led++); 296 if (ret < 0) { 297 of_node_put(child); 298 return ret; 299 } 300 } 301 302 ofdata->leds = leds; 303 ofdata->num_leds = num_leds; 304 305 return 0; 306 } 307 308 static const struct of_device_id of_ns2_leds_match[] = { 309 { .compatible = "lacie,ns2-leds", }, 310 {}, 311 }; 312 MODULE_DEVICE_TABLE(of, of_ns2_leds_match); 313 314 static int ns2_led_probe(struct platform_device *pdev) 315 { 316 struct ns2_led_of *ofdata; 317 struct ns2_led *leds; 318 int i; 319 int ret; 320 321 ofdata = devm_kzalloc(&pdev->dev, sizeof(struct ns2_led_of), 322 GFP_KERNEL); 323 if (!ofdata) 324 return -ENOMEM; 325 326 ret = ns2_leds_parse_of(&pdev->dev, ofdata); 327 if (ret) 328 return ret; 329 330 leds = devm_kzalloc(&pdev->dev, array_size(sizeof(*leds), 331 ofdata->num_leds), 332 GFP_KERNEL); 333 if (!leds) 334 return -ENOMEM; 335 336 for (i = 0; i < ofdata->num_leds; i++) { 337 ret = create_ns2_led(pdev, &leds[i], &ofdata->leds[i]); 338 if (ret < 0) 339 return ret; 340 } 341 342 return 0; 343 } 344 345 static struct platform_driver ns2_led_driver = { 346 .probe = ns2_led_probe, 347 .driver = { 348 .name = "leds-ns2", 349 .of_match_table = of_match_ptr(of_ns2_leds_match), 350 }, 351 }; 352 353 module_platform_driver(ns2_led_driver); 354 355 MODULE_AUTHOR("Simon Guinot <sguinot@lacie.com>"); 356 MODULE_DESCRIPTION("Network Space v2 LED driver"); 357 MODULE_LICENSE("GPL"); 358 MODULE_ALIAS("platform:leds-ns2"); 359