leds-ns2.c 6.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284
  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. #include <linux/kernel.h>
  12. #include <linux/platform_device.h>
  13. #include <linux/slab.h>
  14. #include <linux/gpio/consumer.h>
  15. #include <linux/leds.h>
  16. #include <linux/module.h>
  17. #include <linux/of.h>
  18. #include "leds.h"
  19. enum ns2_led_modes {
  20. NS_V2_LED_OFF,
  21. NS_V2_LED_ON,
  22. NS_V2_LED_SATA,
  23. };
  24. /*
  25. * If the size of this structure or types of its members is changed,
  26. * the filling of array modval in function ns2_led_register must be changed
  27. * accordingly.
  28. */
  29. struct ns2_led_modval {
  30. u32 mode;
  31. u32 cmd_level;
  32. u32 slow_level;
  33. } __packed;
  34. /*
  35. * The Network Space v2 dual-GPIO LED is wired to a CPLD. Three different LED
  36. * modes are available: off, on and SATA activity blinking. The LED modes are
  37. * controlled through two GPIOs (command and slow): each combination of values
  38. * for the command/slow GPIOs corresponds to a LED mode.
  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. static int ns2_led_get_mode(struct ns2_led *led, enum ns2_led_modes *mode)
  51. {
  52. int i;
  53. int cmd_level;
  54. int slow_level;
  55. cmd_level = gpiod_get_value_cansleep(led->cmd);
  56. slow_level = gpiod_get_value_cansleep(led->slow);
  57. for (i = 0; i < led->num_modes; i++) {
  58. if (cmd_level == led->modval[i].cmd_level &&
  59. slow_level == led->modval[i].slow_level) {
  60. *mode = led->modval[i].mode;
  61. return 0;
  62. }
  63. }
  64. return -EINVAL;
  65. }
  66. static void ns2_led_set_mode(struct ns2_led *led, enum ns2_led_modes mode)
  67. {
  68. int i;
  69. unsigned long flags;
  70. for (i = 0; i < led->num_modes; i++)
  71. if (mode == led->modval[i].mode)
  72. break;
  73. if (i == led->num_modes)
  74. return;
  75. write_lock_irqsave(&led->rw_lock, flags);
  76. if (!led->can_sleep) {
  77. gpiod_set_value(led->cmd, led->modval[i].cmd_level);
  78. gpiod_set_value(led->slow, led->modval[i].slow_level);
  79. goto exit_unlock;
  80. }
  81. gpiod_set_value_cansleep(led->cmd, led->modval[i].cmd_level);
  82. gpiod_set_value_cansleep(led->slow, led->modval[i].slow_level);
  83. exit_unlock:
  84. write_unlock_irqrestore(&led->rw_lock, flags);
  85. }
  86. static void ns2_led_set(struct led_classdev *led_cdev,
  87. enum led_brightness value)
  88. {
  89. struct ns2_led *led = container_of(led_cdev, struct ns2_led, cdev);
  90. enum ns2_led_modes mode;
  91. if (value == LED_OFF)
  92. mode = NS_V2_LED_OFF;
  93. else if (led->sata)
  94. mode = NS_V2_LED_SATA;
  95. else
  96. mode = NS_V2_LED_ON;
  97. ns2_led_set_mode(led, mode);
  98. }
  99. static int ns2_led_set_blocking(struct led_classdev *led_cdev,
  100. enum led_brightness value)
  101. {
  102. ns2_led_set(led_cdev, value);
  103. return 0;
  104. }
  105. static ssize_t ns2_led_sata_store(struct device *dev,
  106. struct device_attribute *attr,
  107. const char *buff, size_t count)
  108. {
  109. struct led_classdev *led_cdev = dev_get_drvdata(dev);
  110. struct ns2_led *led = container_of(led_cdev, struct ns2_led, cdev);
  111. int ret;
  112. unsigned long enable;
  113. ret = kstrtoul(buff, 10, &enable);
  114. if (ret < 0)
  115. return ret;
  116. enable = !!enable;
  117. if (led->sata == enable)
  118. goto exit;
  119. led->sata = enable;
  120. if (!led_get_brightness(led_cdev))
  121. goto exit;
  122. if (enable)
  123. ns2_led_set_mode(led, NS_V2_LED_SATA);
  124. else
  125. ns2_led_set_mode(led, NS_V2_LED_ON);
  126. exit:
  127. return count;
  128. }
  129. static ssize_t ns2_led_sata_show(struct device *dev,
  130. struct device_attribute *attr, char *buf)
  131. {
  132. struct led_classdev *led_cdev = dev_get_drvdata(dev);
  133. struct ns2_led *led = container_of(led_cdev, struct ns2_led, cdev);
  134. return sprintf(buf, "%d\n", led->sata);
  135. }
  136. static DEVICE_ATTR(sata, 0644, ns2_led_sata_show, ns2_led_sata_store);
  137. static struct attribute *ns2_led_attrs[] = {
  138. &dev_attr_sata.attr,
  139. NULL
  140. };
  141. ATTRIBUTE_GROUPS(ns2_led);
  142. static int ns2_led_register(struct device *dev, struct fwnode_handle *node,
  143. struct ns2_led *led)
  144. {
  145. struct led_init_data init_data = {};
  146. struct ns2_led_modval *modval;
  147. enum ns2_led_modes mode;
  148. int nmodes, ret;
  149. led->cmd = devm_fwnode_gpiod_get_index(dev, node, "cmd", 0, GPIOD_ASIS,
  150. fwnode_get_name(node));
  151. if (IS_ERR(led->cmd))
  152. return PTR_ERR(led->cmd);
  153. led->slow = devm_fwnode_gpiod_get_index(dev, node, "slow", 0,
  154. GPIOD_ASIS,
  155. fwnode_get_name(node));
  156. if (IS_ERR(led->slow))
  157. return PTR_ERR(led->slow);
  158. ret = fwnode_property_count_u32(node, "modes-map");
  159. if (ret < 0 || ret % 3) {
  160. dev_err(dev, "Missing or malformed modes-map for %pfw\n", node);
  161. return -EINVAL;
  162. }
  163. nmodes = ret / 3;
  164. modval = devm_kcalloc(dev, nmodes, sizeof(*modval), GFP_KERNEL);
  165. if (!modval)
  166. return -ENOMEM;
  167. fwnode_property_read_u32_array(node, "modes-map", (void *)modval,
  168. nmodes * 3);
  169. rwlock_init(&led->rw_lock);
  170. led->cdev.blink_set = NULL;
  171. led->cdev.flags |= LED_CORE_SUSPENDRESUME;
  172. led->cdev.groups = ns2_led_groups;
  173. led->can_sleep = gpiod_cansleep(led->cmd) || gpiod_cansleep(led->slow);
  174. if (led->can_sleep)
  175. led->cdev.brightness_set_blocking = ns2_led_set_blocking;
  176. else
  177. led->cdev.brightness_set = ns2_led_set;
  178. led->num_modes = nmodes;
  179. led->modval = modval;
  180. ret = ns2_led_get_mode(led, &mode);
  181. if (ret < 0)
  182. return ret;
  183. /* Set LED initial state. */
  184. led->sata = (mode == NS_V2_LED_SATA) ? 1 : 0;
  185. led->cdev.brightness = (mode == NS_V2_LED_OFF) ? LED_OFF : LED_FULL;
  186. init_data.fwnode = node;
  187. ret = devm_led_classdev_register_ext(dev, &led->cdev, &init_data);
  188. if (ret)
  189. dev_err(dev, "Failed to register LED for node %pfw\n", node);
  190. return ret;
  191. }
  192. static int ns2_led_probe(struct platform_device *pdev)
  193. {
  194. struct device *dev = &pdev->dev;
  195. struct fwnode_handle *child;
  196. struct ns2_led *leds;
  197. int count;
  198. int ret;
  199. count = device_get_child_node_count(dev);
  200. if (!count)
  201. return -ENODEV;
  202. leds = devm_kzalloc(dev, array_size(sizeof(*leds), count), GFP_KERNEL);
  203. if (!leds)
  204. return -ENOMEM;
  205. device_for_each_child_node(dev, child) {
  206. ret = ns2_led_register(dev, child, leds++);
  207. if (ret) {
  208. fwnode_handle_put(child);
  209. return ret;
  210. }
  211. }
  212. return 0;
  213. }
  214. static const struct of_device_id of_ns2_leds_match[] = {
  215. { .compatible = "lacie,ns2-leds", },
  216. {},
  217. };
  218. MODULE_DEVICE_TABLE(of, of_ns2_leds_match);
  219. static struct platform_driver ns2_led_driver = {
  220. .probe = ns2_led_probe,
  221. .driver = {
  222. .name = "leds-ns2",
  223. .of_match_table = of_ns2_leds_match,
  224. },
  225. };
  226. module_platform_driver(ns2_led_driver);
  227. MODULE_AUTHOR("Simon Guinot <sguinot@lacie.com>");
  228. MODULE_DESCRIPTION("Network Space v2 LED driver");
  229. MODULE_LICENSE("GPL");
  230. MODULE_ALIAS("platform:leds-ns2");