leds-syscon.c 4.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167
  1. /*
  2. * Generic Syscon LEDs Driver
  3. *
  4. * Copyright (c) 2014, Linaro Limited
  5. * Author: Linus Walleij <linus.walleij@linaro.org>
  6. *
  7. * This program is free software; you can redistribute it and/or
  8. * modify it under the terms of the GNU General Public License as
  9. * published by the Free Software Foundation; either version 2 of
  10. * the License, or (at your option) any later version.
  11. *
  12. * This program is distributed in the hope that it will be useful,
  13. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  14. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  15. * GNU General Public License for more details.
  16. *
  17. * You should have received a copy of the GNU General Public License
  18. * along with this program; if not, write to the Free Software
  19. * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
  20. * MA 02111-1307 USA
  21. */
  22. #include <linux/io.h>
  23. #include <linux/module.h>
  24. #include <linux/of_device.h>
  25. #include <linux/of_address.h>
  26. #include <linux/platform_device.h>
  27. #include <linux/stat.h>
  28. #include <linux/slab.h>
  29. #include <linux/mfd/syscon.h>
  30. #include <linux/regmap.h>
  31. #include <linux/leds.h>
  32. /**
  33. * struct syscon_led - state container for syscon based LEDs
  34. * @cdev: LED class device for this LED
  35. * @map: regmap to access the syscon device backing this LED
  36. * @offset: the offset into the syscon regmap for the LED register
  37. * @mask: the bit in the register corresponding to the LED
  38. * @state: current state of the LED
  39. */
  40. struct syscon_led {
  41. struct led_classdev cdev;
  42. struct regmap *map;
  43. u32 offset;
  44. u32 mask;
  45. bool state;
  46. };
  47. static void syscon_led_set(struct led_classdev *led_cdev,
  48. enum led_brightness value)
  49. {
  50. struct syscon_led *sled =
  51. container_of(led_cdev, struct syscon_led, cdev);
  52. u32 val;
  53. int ret;
  54. if (value == LED_OFF) {
  55. val = 0;
  56. sled->state = false;
  57. } else {
  58. val = sled->mask;
  59. sled->state = true;
  60. }
  61. ret = regmap_update_bits(sled->map, sled->offset, sled->mask, val);
  62. if (ret < 0)
  63. dev_err(sled->cdev.dev, "error updating LED status\n");
  64. }
  65. static int syscon_led_probe(struct platform_device *pdev)
  66. {
  67. struct device *dev = &pdev->dev;
  68. struct device_node *np = dev->of_node;
  69. struct device *parent;
  70. struct regmap *map;
  71. struct syscon_led *sled;
  72. const char *state;
  73. int ret;
  74. parent = dev->parent;
  75. if (!parent) {
  76. dev_err(dev, "no parent for syscon LED\n");
  77. return -ENODEV;
  78. }
  79. map = syscon_node_to_regmap(parent->of_node);
  80. if (IS_ERR(map)) {
  81. dev_err(dev, "no regmap for syscon LED parent\n");
  82. return PTR_ERR(map);
  83. }
  84. sled = devm_kzalloc(dev, sizeof(*sled), GFP_KERNEL);
  85. if (!sled)
  86. return -ENOMEM;
  87. sled->map = map;
  88. if (of_property_read_u32(np, "offset", &sled->offset))
  89. return -EINVAL;
  90. if (of_property_read_u32(np, "mask", &sled->mask))
  91. return -EINVAL;
  92. sled->cdev.name =
  93. of_get_property(np, "label", NULL) ? : np->name;
  94. sled->cdev.default_trigger =
  95. of_get_property(np, "linux,default-trigger", NULL);
  96. state = of_get_property(np, "default-state", NULL);
  97. if (state) {
  98. if (!strcmp(state, "keep")) {
  99. u32 val;
  100. ret = regmap_read(map, sled->offset, &val);
  101. if (ret < 0)
  102. return ret;
  103. sled->state = !!(val & sled->mask);
  104. } else if (!strcmp(state, "on")) {
  105. sled->state = true;
  106. ret = regmap_update_bits(map, sled->offset,
  107. sled->mask,
  108. sled->mask);
  109. if (ret < 0)
  110. return ret;
  111. } else {
  112. sled->state = false;
  113. ret = regmap_update_bits(map, sled->offset,
  114. sled->mask, 0);
  115. if (ret < 0)
  116. return ret;
  117. }
  118. }
  119. sled->cdev.brightness_set = syscon_led_set;
  120. ret = led_classdev_register(dev, &sled->cdev);
  121. if (ret < 0)
  122. return ret;
  123. platform_set_drvdata(pdev, sled);
  124. dev_info(dev, "registered LED %s\n", sled->cdev.name);
  125. return 0;
  126. }
  127. static int syscon_led_remove(struct platform_device *pdev)
  128. {
  129. struct syscon_led *sled = platform_get_drvdata(pdev);
  130. led_classdev_unregister(&sled->cdev);
  131. /* Turn it off */
  132. regmap_update_bits(sled->map, sled->offset, sled->mask, 0);
  133. return 0;
  134. }
  135. static const struct of_device_id of_syscon_leds_match[] = {
  136. { .compatible = "register-bit-led", },
  137. {},
  138. };
  139. MODULE_DEVICE_TABLE(of, of_syscon_leds_match);
  140. static struct platform_driver syscon_led_driver = {
  141. .probe = syscon_led_probe,
  142. .remove = syscon_led_remove,
  143. .driver = {
  144. .name = "leds-syscon",
  145. .of_match_table = of_syscon_leds_match,
  146. },
  147. };
  148. module_platform_driver(syscon_led_driver);