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.h> 16 #include <linux/leds.h> 17 #include <linux/module.h> 18 #include <linux/platform_data/leds-kirkwood-ns2.h> 19 #include <linux/of.h> 20 #include <linux/of_gpio.h> 21 #include "leds.h" 22 23 /* 24 * The Network Space v2 dual-GPIO LED is wired to a CPLD. Three different LED 25 * modes are available: off, on and SATA activity blinking. The LED modes are 26 * controlled through two GPIOs (command and slow): each combination of values 27 * for the command/slow GPIOs corresponds to a LED mode. 28 */ 29 30 struct ns2_led_data { 31 struct led_classdev cdev; 32 unsigned int cmd; 33 unsigned int slow; 34 bool can_sleep; 35 unsigned char sata; /* True when SATA mode active. */ 36 rwlock_t rw_lock; /* Lock GPIOs. */ 37 int num_modes; 38 struct ns2_led_modval *modval; 39 }; 40 41 static int ns2_led_get_mode(struct ns2_led_data *led_dat, 42 enum ns2_led_modes *mode) 43 { 44 int i; 45 int ret = -EINVAL; 46 int cmd_level; 47 int slow_level; 48 49 cmd_level = gpio_get_value_cansleep(led_dat->cmd); 50 slow_level = gpio_get_value_cansleep(led_dat->slow); 51 52 for (i = 0; i < led_dat->num_modes; i++) { 53 if (cmd_level == led_dat->modval[i].cmd_level && 54 slow_level == led_dat->modval[i].slow_level) { 55 *mode = led_dat->modval[i].mode; 56 ret = 0; 57 break; 58 } 59 } 60 61 return ret; 62 } 63 64 static void ns2_led_set_mode(struct ns2_led_data *led_dat, 65 enum ns2_led_modes mode) 66 { 67 int i; 68 bool found = false; 69 unsigned long flags; 70 71 for (i = 0; i < led_dat->num_modes; i++) 72 if (mode == led_dat->modval[i].mode) { 73 found = true; 74 break; 75 } 76 77 if (!found) 78 return; 79 80 write_lock_irqsave(&led_dat->rw_lock, flags); 81 82 if (!led_dat->can_sleep) { 83 gpio_set_value(led_dat->cmd, 84 led_dat->modval[i].cmd_level); 85 gpio_set_value(led_dat->slow, 86 led_dat->modval[i].slow_level); 87 goto exit_unlock; 88 } 89 90 gpio_set_value_cansleep(led_dat->cmd, led_dat->modval[i].cmd_level); 91 gpio_set_value_cansleep(led_dat->slow, led_dat->modval[i].slow_level); 92 93 exit_unlock: 94 write_unlock_irqrestore(&led_dat->rw_lock, flags); 95 } 96 97 static void ns2_led_set(struct led_classdev *led_cdev, 98 enum led_brightness value) 99 { 100 struct ns2_led_data *led_dat = 101 container_of(led_cdev, struct ns2_led_data, cdev); 102 enum ns2_led_modes mode; 103 104 if (value == LED_OFF) 105 mode = NS_V2_LED_OFF; 106 else if (led_dat->sata) 107 mode = NS_V2_LED_SATA; 108 else 109 mode = NS_V2_LED_ON; 110 111 ns2_led_set_mode(led_dat, 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_data *led_dat = 127 container_of(led_cdev, struct ns2_led_data, cdev); 128 int ret; 129 unsigned long enable; 130 131 ret = kstrtoul(buff, 10, &enable); 132 if (ret < 0) 133 return ret; 134 135 enable = !!enable; 136 137 if (led_dat->sata == enable) 138 goto exit; 139 140 led_dat->sata = enable; 141 142 if (!led_get_brightness(led_cdev)) 143 goto exit; 144 145 if (enable) 146 ns2_led_set_mode(led_dat, NS_V2_LED_SATA); 147 else 148 ns2_led_set_mode(led_dat, NS_V2_LED_ON); 149 150 exit: 151 return count; 152 } 153 154 static ssize_t ns2_led_sata_show(struct device *dev, 155 struct device_attribute *attr, char *buf) 156 { 157 struct led_classdev *led_cdev = dev_get_drvdata(dev); 158 struct ns2_led_data *led_dat = 159 container_of(led_cdev, struct ns2_led_data, cdev); 160 161 return sprintf(buf, "%d\n", led_dat->sata); 162 } 163 164 static DEVICE_ATTR(sata, 0644, ns2_led_sata_show, ns2_led_sata_store); 165 166 static struct attribute *ns2_led_attrs[] = { 167 &dev_attr_sata.attr, 168 NULL 169 }; 170 ATTRIBUTE_GROUPS(ns2_led); 171 172 static int 173 create_ns2_led(struct platform_device *pdev, struct ns2_led_data *led_dat, 174 const struct ns2_led *template) 175 { 176 int ret; 177 enum ns2_led_modes mode; 178 179 ret = devm_gpio_request_one(&pdev->dev, template->cmd, 180 gpio_get_value_cansleep(template->cmd) ? 181 GPIOF_OUT_INIT_HIGH : GPIOF_OUT_INIT_LOW, 182 template->name); 183 if (ret) { 184 dev_err(&pdev->dev, "%s: failed to setup command GPIO\n", 185 template->name); 186 return ret; 187 } 188 189 ret = devm_gpio_request_one(&pdev->dev, template->slow, 190 gpio_get_value_cansleep(template->slow) ? 191 GPIOF_OUT_INIT_HIGH : GPIOF_OUT_INIT_LOW, 192 template->name); 193 if (ret) { 194 dev_err(&pdev->dev, "%s: failed to setup slow GPIO\n", 195 template->name); 196 return ret; 197 } 198 199 rwlock_init(&led_dat->rw_lock); 200 201 led_dat->cdev.name = template->name; 202 led_dat->cdev.default_trigger = template->default_trigger; 203 led_dat->cdev.blink_set = NULL; 204 led_dat->cdev.flags |= LED_CORE_SUSPENDRESUME; 205 led_dat->cdev.groups = ns2_led_groups; 206 led_dat->cmd = template->cmd; 207 led_dat->slow = template->slow; 208 led_dat->can_sleep = gpio_cansleep(led_dat->cmd) | 209 gpio_cansleep(led_dat->slow); 210 if (led_dat->can_sleep) 211 led_dat->cdev.brightness_set_blocking = ns2_led_set_blocking; 212 else 213 led_dat->cdev.brightness_set = ns2_led_set; 214 led_dat->modval = template->modval; 215 led_dat->num_modes = template->num_modes; 216 217 ret = ns2_led_get_mode(led_dat, &mode); 218 if (ret < 0) 219 return ret; 220 221 /* Set LED initial state. */ 222 led_dat->sata = (mode == NS_V2_LED_SATA) ? 1 : 0; 223 led_dat->cdev.brightness = 224 (mode == NS_V2_LED_OFF) ? LED_OFF : LED_FULL; 225 226 ret = led_classdev_register(&pdev->dev, &led_dat->cdev); 227 if (ret < 0) 228 return ret; 229 230 return 0; 231 } 232 233 static void delete_ns2_led(struct ns2_led_data *led_dat) 234 { 235 led_classdev_unregister(&led_dat->cdev); 236 } 237 238 #ifdef CONFIG_OF_GPIO 239 /* 240 * Translate OpenFirmware node properties into platform_data. 241 */ 242 static int 243 ns2_leds_get_of_pdata(struct device *dev, struct ns2_led_platform_data *pdata) 244 { 245 struct device_node *np = dev->of_node; 246 struct device_node *child; 247 struct ns2_led *led, *leds; 248 int num_leds = 0; 249 250 num_leds = of_get_child_count(np); 251 if (!num_leds) 252 return -ENODEV; 253 254 leds = devm_kcalloc(dev, num_leds, sizeof(struct ns2_led), 255 GFP_KERNEL); 256 if (!leds) 257 return -ENOMEM; 258 259 led = leds; 260 for_each_child_of_node(np, child) { 261 const char *string; 262 int ret, i, num_modes; 263 struct ns2_led_modval *modval; 264 265 ret = of_get_named_gpio(child, "cmd-gpio", 0); 266 if (ret < 0) 267 return ret; 268 led->cmd = ret; 269 ret = of_get_named_gpio(child, "slow-gpio", 0); 270 if (ret < 0) 271 return ret; 272 led->slow = ret; 273 ret = of_property_read_string(child, "label", &string); 274 led->name = (ret == 0) ? string : child->name; 275 ret = of_property_read_string(child, "linux,default-trigger", 276 &string); 277 if (ret == 0) 278 led->default_trigger = string; 279 280 ret = of_property_count_u32_elems(child, "modes-map"); 281 if (ret < 0 || ret % 3) { 282 dev_err(dev, 283 "Missing or malformed modes-map property\n"); 284 return -EINVAL; 285 } 286 287 num_modes = ret / 3; 288 modval = devm_kcalloc(dev, 289 num_modes, 290 sizeof(struct ns2_led_modval), 291 GFP_KERNEL); 292 if (!modval) 293 return -ENOMEM; 294 295 for (i = 0; i < num_modes; i++) { 296 of_property_read_u32_index(child, 297 "modes-map", 3 * i, 298 (u32 *) &modval[i].mode); 299 of_property_read_u32_index(child, 300 "modes-map", 3 * i + 1, 301 (u32 *) &modval[i].cmd_level); 302 of_property_read_u32_index(child, 303 "modes-map", 3 * i + 2, 304 (u32 *) &modval[i].slow_level); 305 } 306 307 led->num_modes = num_modes; 308 led->modval = modval; 309 310 led++; 311 } 312 313 pdata->leds = leds; 314 pdata->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 #endif /* CONFIG_OF_GPIO */ 325 326 struct ns2_led_priv { 327 int num_leds; 328 struct ns2_led_data leds_data[]; 329 }; 330 331 static inline int sizeof_ns2_led_priv(int num_leds) 332 { 333 return sizeof(struct ns2_led_priv) + 334 (sizeof(struct ns2_led_data) * num_leds); 335 } 336 337 static int ns2_led_probe(struct platform_device *pdev) 338 { 339 struct ns2_led_platform_data *pdata = dev_get_platdata(&pdev->dev); 340 struct ns2_led_priv *priv; 341 int i; 342 int ret; 343 344 #ifdef CONFIG_OF_GPIO 345 if (!pdata) { 346 pdata = devm_kzalloc(&pdev->dev, 347 sizeof(struct ns2_led_platform_data), 348 GFP_KERNEL); 349 if (!pdata) 350 return -ENOMEM; 351 352 ret = ns2_leds_get_of_pdata(&pdev->dev, pdata); 353 if (ret) 354 return ret; 355 } 356 #else 357 if (!pdata) 358 return -EINVAL; 359 #endif /* CONFIG_OF_GPIO */ 360 361 priv = devm_kzalloc(&pdev->dev, 362 sizeof_ns2_led_priv(pdata->num_leds), GFP_KERNEL); 363 if (!priv) 364 return -ENOMEM; 365 priv->num_leds = pdata->num_leds; 366 367 for (i = 0; i < priv->num_leds; i++) { 368 ret = create_ns2_led(pdev, &priv->leds_data[i], 369 &pdata->leds[i]); 370 if (ret < 0) { 371 for (i = i - 1; i >= 0; i--) 372 delete_ns2_led(&priv->leds_data[i]); 373 return ret; 374 } 375 } 376 377 platform_set_drvdata(pdev, priv); 378 379 return 0; 380 } 381 382 static int ns2_led_remove(struct platform_device *pdev) 383 { 384 int i; 385 struct ns2_led_priv *priv; 386 387 priv = platform_get_drvdata(pdev); 388 389 for (i = 0; i < priv->num_leds; i++) 390 delete_ns2_led(&priv->leds_data[i]); 391 392 return 0; 393 } 394 395 static struct platform_driver ns2_led_driver = { 396 .probe = ns2_led_probe, 397 .remove = ns2_led_remove, 398 .driver = { 399 .name = "leds-ns2", 400 .of_match_table = of_match_ptr(of_ns2_leds_match), 401 }, 402 }; 403 404 module_platform_driver(ns2_led_driver); 405 406 MODULE_AUTHOR("Simon Guinot <sguinot@lacie.com>"); 407 MODULE_DESCRIPTION("Network Space v2 LED driver"); 408 MODULE_LICENSE("GPL"); 409 MODULE_ALIAS("platform:leds-ns2"); 410