leds-bcm6358.c 5.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229
  1. // SPDX-License-Identifier: GPL-2.0-or-later
  2. /*
  3. * Driver for BCM6358 memory-mapped LEDs, based on leds-syscon.c
  4. *
  5. * Copyright 2015 Álvaro Fernández Rojas <noltari@gmail.com>
  6. */
  7. #include <linux/delay.h>
  8. #include <linux/io.h>
  9. #include <linux/leds.h>
  10. #include <linux/module.h>
  11. #include <linux/of.h>
  12. #include <linux/platform_device.h>
  13. #include <linux/spinlock.h>
  14. #define BCM6358_REG_MODE 0x0
  15. #define BCM6358_REG_CTRL 0x4
  16. #define BCM6358_SLED_CLKDIV_MASK 3
  17. #define BCM6358_SLED_CLKDIV_1 0
  18. #define BCM6358_SLED_CLKDIV_2 1
  19. #define BCM6358_SLED_CLKDIV_4 2
  20. #define BCM6358_SLED_CLKDIV_8 3
  21. #define BCM6358_SLED_POLARITY BIT(2)
  22. #define BCM6358_SLED_BUSY BIT(3)
  23. #define BCM6358_SLED_MAX_COUNT 32
  24. #define BCM6358_SLED_WAIT 100
  25. /**
  26. * struct bcm6358_led - state container for bcm6358 based LEDs
  27. * @cdev: LED class device for this LED
  28. * @mem: memory resource
  29. * @lock: memory lock
  30. * @pin: LED pin number
  31. * @active_low: LED is active low
  32. */
  33. struct bcm6358_led {
  34. struct led_classdev cdev;
  35. void __iomem *mem;
  36. spinlock_t *lock;
  37. unsigned long pin;
  38. bool active_low;
  39. };
  40. static void bcm6358_led_write(void __iomem *reg, unsigned long data)
  41. {
  42. #ifdef CONFIG_CPU_BIG_ENDIAN
  43. iowrite32be(data, reg);
  44. #else
  45. writel(data, reg);
  46. #endif
  47. }
  48. static unsigned long bcm6358_led_read(void __iomem *reg)
  49. {
  50. #ifdef CONFIG_CPU_BIG_ENDIAN
  51. return ioread32be(reg);
  52. #else
  53. return readl(reg);
  54. #endif
  55. }
  56. static unsigned long bcm6358_led_busy(void __iomem *mem)
  57. {
  58. unsigned long val;
  59. while ((val = bcm6358_led_read(mem + BCM6358_REG_CTRL)) &
  60. BCM6358_SLED_BUSY)
  61. udelay(BCM6358_SLED_WAIT);
  62. return val;
  63. }
  64. static void bcm6358_led_set(struct led_classdev *led_cdev,
  65. enum led_brightness value)
  66. {
  67. struct bcm6358_led *led =
  68. container_of(led_cdev, struct bcm6358_led, cdev);
  69. unsigned long flags, val;
  70. spin_lock_irqsave(led->lock, flags);
  71. bcm6358_led_busy(led->mem);
  72. val = bcm6358_led_read(led->mem + BCM6358_REG_MODE);
  73. if ((led->active_low && value == LED_OFF) ||
  74. (!led->active_low && value != LED_OFF))
  75. val |= BIT(led->pin);
  76. else
  77. val &= ~(BIT(led->pin));
  78. bcm6358_led_write(led->mem + BCM6358_REG_MODE, val);
  79. spin_unlock_irqrestore(led->lock, flags);
  80. }
  81. static int bcm6358_led(struct device *dev, struct device_node *nc, u32 reg,
  82. void __iomem *mem, spinlock_t *lock)
  83. {
  84. struct led_init_data init_data = {};
  85. struct bcm6358_led *led;
  86. const char *state;
  87. int rc;
  88. led = devm_kzalloc(dev, sizeof(*led), GFP_KERNEL);
  89. if (!led)
  90. return -ENOMEM;
  91. led->pin = reg;
  92. led->mem = mem;
  93. led->lock = lock;
  94. if (of_property_read_bool(nc, "active-low"))
  95. led->active_low = true;
  96. if (!of_property_read_string(nc, "default-state", &state)) {
  97. if (!strcmp(state, "on")) {
  98. led->cdev.brightness = LED_FULL;
  99. } else if (!strcmp(state, "keep")) {
  100. unsigned long val;
  101. val = bcm6358_led_read(led->mem + BCM6358_REG_MODE);
  102. val &= BIT(led->pin);
  103. if ((led->active_low && !val) ||
  104. (!led->active_low && val))
  105. led->cdev.brightness = LED_FULL;
  106. else
  107. led->cdev.brightness = LED_OFF;
  108. } else {
  109. led->cdev.brightness = LED_OFF;
  110. }
  111. } else {
  112. led->cdev.brightness = LED_OFF;
  113. }
  114. bcm6358_led_set(&led->cdev, led->cdev.brightness);
  115. led->cdev.brightness_set = bcm6358_led_set;
  116. init_data.fwnode = of_fwnode_handle(nc);
  117. rc = devm_led_classdev_register_ext(dev, &led->cdev, &init_data);
  118. if (rc < 0)
  119. return rc;
  120. dev_dbg(dev, "registered LED %s\n", led->cdev.name);
  121. return 0;
  122. }
  123. static int bcm6358_leds_probe(struct platform_device *pdev)
  124. {
  125. struct device *dev = &pdev->dev;
  126. struct device_node *np = dev_of_node(&pdev->dev);
  127. struct device_node *child;
  128. void __iomem *mem;
  129. spinlock_t *lock; /* memory lock */
  130. unsigned long val;
  131. u32 clk_div;
  132. mem = devm_platform_ioremap_resource(pdev, 0);
  133. if (IS_ERR(mem))
  134. return PTR_ERR(mem);
  135. lock = devm_kzalloc(dev, sizeof(*lock), GFP_KERNEL);
  136. if (!lock)
  137. return -ENOMEM;
  138. spin_lock_init(lock);
  139. val = bcm6358_led_busy(mem);
  140. val &= ~(BCM6358_SLED_POLARITY | BCM6358_SLED_CLKDIV_MASK);
  141. if (of_property_read_bool(np, "brcm,clk-dat-low"))
  142. val |= BCM6358_SLED_POLARITY;
  143. of_property_read_u32(np, "brcm,clk-div", &clk_div);
  144. switch (clk_div) {
  145. case 8:
  146. val |= BCM6358_SLED_CLKDIV_8;
  147. break;
  148. case 4:
  149. val |= BCM6358_SLED_CLKDIV_4;
  150. break;
  151. case 2:
  152. val |= BCM6358_SLED_CLKDIV_2;
  153. break;
  154. default:
  155. val |= BCM6358_SLED_CLKDIV_1;
  156. break;
  157. }
  158. bcm6358_led_write(mem + BCM6358_REG_CTRL, val);
  159. for_each_available_child_of_node(np, child) {
  160. int rc;
  161. u32 reg;
  162. if (of_property_read_u32(child, "reg", &reg))
  163. continue;
  164. if (reg >= BCM6358_SLED_MAX_COUNT) {
  165. dev_err(dev, "invalid LED (%u >= %d)\n", reg,
  166. BCM6358_SLED_MAX_COUNT);
  167. continue;
  168. }
  169. rc = bcm6358_led(dev, child, reg, mem, lock);
  170. if (rc < 0) {
  171. of_node_put(child);
  172. return rc;
  173. }
  174. }
  175. return 0;
  176. }
  177. static const struct of_device_id bcm6358_leds_of_match[] = {
  178. { .compatible = "brcm,bcm6358-leds", },
  179. { },
  180. };
  181. MODULE_DEVICE_TABLE(of, bcm6358_leds_of_match);
  182. static struct platform_driver bcm6358_leds_driver = {
  183. .probe = bcm6358_leds_probe,
  184. .driver = {
  185. .name = "leds-bcm6358",
  186. .of_match_table = bcm6358_leds_of_match,
  187. },
  188. };
  189. module_platform_driver(bcm6358_leds_driver);
  190. MODULE_AUTHOR("Álvaro Fernández Rojas <noltari@gmail.com>");
  191. MODULE_DESCRIPTION("LED driver for BCM6358 controllers");
  192. MODULE_LICENSE("GPL v2");
  193. MODULE_ALIAS("platform:leds-bcm6358");