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