leds-sc27xx-bltc.c 5.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244
  1. // SPDX-License-Identifier: GPL-2.0
  2. // Copyright (C) 2018 Spreadtrum Communications Inc.
  3. #include <linux/leds.h>
  4. #include <linux/module.h>
  5. #include <linux/of.h>
  6. #include <linux/platform_device.h>
  7. #include <linux/regmap.h>
  8. #include <uapi/linux/uleds.h>
  9. /* PMIC global control register definition */
  10. #define SC27XX_MODULE_EN0 0xc08
  11. #define SC27XX_CLK_EN0 0xc18
  12. #define SC27XX_RGB_CTRL 0xebc
  13. #define SC27XX_BLTC_EN BIT(9)
  14. #define SC27XX_RTC_EN BIT(7)
  15. #define SC27XX_RGB_PD BIT(0)
  16. /* Breathing light controller register definition */
  17. #define SC27XX_LEDS_CTRL 0x00
  18. #define SC27XX_LEDS_PRESCALE 0x04
  19. #define SC27XX_LEDS_DUTY 0x08
  20. #define SC27XX_LEDS_CURVE0 0x0c
  21. #define SC27XX_LEDS_CURVE1 0x10
  22. #define SC27XX_CTRL_SHIFT 4
  23. #define SC27XX_LED_RUN BIT(0)
  24. #define SC27XX_LED_TYPE BIT(1)
  25. #define SC27XX_DUTY_SHIFT 8
  26. #define SC27XX_DUTY_MASK GENMASK(15, 0)
  27. #define SC27XX_MOD_MASK GENMASK(7, 0)
  28. #define SC27XX_LEDS_OFFSET 0x10
  29. #define SC27XX_LEDS_MAX 3
  30. struct sc27xx_led {
  31. char name[LED_MAX_NAME_SIZE];
  32. struct led_classdev ldev;
  33. struct sc27xx_led_priv *priv;
  34. u8 line;
  35. bool active;
  36. };
  37. struct sc27xx_led_priv {
  38. struct sc27xx_led leds[SC27XX_LEDS_MAX];
  39. struct regmap *regmap;
  40. struct mutex lock;
  41. u32 base;
  42. };
  43. #define to_sc27xx_led(ldev) \
  44. container_of(ldev, struct sc27xx_led, ldev)
  45. static int sc27xx_led_init(struct regmap *regmap)
  46. {
  47. int err;
  48. err = regmap_update_bits(regmap, SC27XX_MODULE_EN0, SC27XX_BLTC_EN,
  49. SC27XX_BLTC_EN);
  50. if (err)
  51. return err;
  52. err = regmap_update_bits(regmap, SC27XX_CLK_EN0, SC27XX_RTC_EN,
  53. SC27XX_RTC_EN);
  54. if (err)
  55. return err;
  56. return regmap_update_bits(regmap, SC27XX_RGB_CTRL, SC27XX_RGB_PD, 0);
  57. }
  58. static u32 sc27xx_led_get_offset(struct sc27xx_led *leds)
  59. {
  60. return leds->priv->base + SC27XX_LEDS_OFFSET * leds->line;
  61. }
  62. static int sc27xx_led_enable(struct sc27xx_led *leds, enum led_brightness value)
  63. {
  64. u32 base = sc27xx_led_get_offset(leds);
  65. u32 ctrl_base = leds->priv->base + SC27XX_LEDS_CTRL;
  66. u8 ctrl_shift = SC27XX_CTRL_SHIFT * leds->line;
  67. struct regmap *regmap = leds->priv->regmap;
  68. int err;
  69. err = regmap_update_bits(regmap, base + SC27XX_LEDS_DUTY,
  70. SC27XX_DUTY_MASK,
  71. (value << SC27XX_DUTY_SHIFT) |
  72. SC27XX_MOD_MASK);
  73. if (err)
  74. return err;
  75. return regmap_update_bits(regmap, ctrl_base,
  76. (SC27XX_LED_RUN | SC27XX_LED_TYPE) << ctrl_shift,
  77. (SC27XX_LED_RUN | SC27XX_LED_TYPE) << ctrl_shift);
  78. }
  79. static int sc27xx_led_disable(struct sc27xx_led *leds)
  80. {
  81. struct regmap *regmap = leds->priv->regmap;
  82. u32 ctrl_base = leds->priv->base + SC27XX_LEDS_CTRL;
  83. u8 ctrl_shift = SC27XX_CTRL_SHIFT * leds->line;
  84. return regmap_update_bits(regmap, ctrl_base,
  85. (SC27XX_LED_RUN | SC27XX_LED_TYPE) << ctrl_shift, 0);
  86. }
  87. static int sc27xx_led_set(struct led_classdev *ldev, enum led_brightness value)
  88. {
  89. struct sc27xx_led *leds = to_sc27xx_led(ldev);
  90. int err;
  91. mutex_lock(&leds->priv->lock);
  92. if (value == LED_OFF)
  93. err = sc27xx_led_disable(leds);
  94. else
  95. err = sc27xx_led_enable(leds, value);
  96. mutex_unlock(&leds->priv->lock);
  97. return err;
  98. }
  99. static int sc27xx_led_register(struct device *dev, struct sc27xx_led_priv *priv)
  100. {
  101. int i, err;
  102. err = sc27xx_led_init(priv->regmap);
  103. if (err)
  104. return err;
  105. for (i = 0; i < SC27XX_LEDS_MAX; i++) {
  106. struct sc27xx_led *led = &priv->leds[i];
  107. if (!led->active)
  108. continue;
  109. led->line = i;
  110. led->priv = priv;
  111. led->ldev.name = led->name;
  112. led->ldev.brightness_set_blocking = sc27xx_led_set;
  113. err = devm_led_classdev_register(dev, &led->ldev);
  114. if (err)
  115. return err;
  116. }
  117. return 0;
  118. }
  119. static int sc27xx_led_probe(struct platform_device *pdev)
  120. {
  121. struct device *dev = &pdev->dev;
  122. struct device_node *np = dev->of_node, *child;
  123. struct sc27xx_led_priv *priv;
  124. const char *str;
  125. u32 base, count, reg;
  126. int err;
  127. count = of_get_child_count(np);
  128. if (!count || count > SC27XX_LEDS_MAX)
  129. return -EINVAL;
  130. err = of_property_read_u32(np, "reg", &base);
  131. if (err) {
  132. dev_err(dev, "fail to get reg of property\n");
  133. return err;
  134. }
  135. priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
  136. if (!priv)
  137. return -ENOMEM;
  138. platform_set_drvdata(pdev, priv);
  139. mutex_init(&priv->lock);
  140. priv->base = base;
  141. priv->regmap = dev_get_regmap(dev->parent, NULL);
  142. if (!priv->regmap) {
  143. err = -ENODEV;
  144. dev_err(dev, "failed to get regmap: %d\n", err);
  145. return err;
  146. }
  147. for_each_child_of_node(np, child) {
  148. err = of_property_read_u32(child, "reg", &reg);
  149. if (err) {
  150. of_node_put(child);
  151. mutex_destroy(&priv->lock);
  152. return err;
  153. }
  154. if (reg >= SC27XX_LEDS_MAX || priv->leds[reg].active) {
  155. of_node_put(child);
  156. mutex_destroy(&priv->lock);
  157. return -EINVAL;
  158. }
  159. priv->leds[reg].active = true;
  160. err = of_property_read_string(child, "label", &str);
  161. if (err)
  162. snprintf(priv->leds[reg].name, LED_MAX_NAME_SIZE,
  163. "sc27xx::");
  164. else
  165. snprintf(priv->leds[reg].name, LED_MAX_NAME_SIZE,
  166. "sc27xx:%s", str);
  167. }
  168. err = sc27xx_led_register(dev, priv);
  169. if (err)
  170. mutex_destroy(&priv->lock);
  171. return err;
  172. }
  173. static int sc27xx_led_remove(struct platform_device *pdev)
  174. {
  175. struct sc27xx_led_priv *priv = platform_get_drvdata(pdev);
  176. mutex_destroy(&priv->lock);
  177. return 0;
  178. }
  179. static const struct of_device_id sc27xx_led_of_match[] = {
  180. { .compatible = "sprd,sc2731-bltc", },
  181. { }
  182. };
  183. MODULE_DEVICE_TABLE(of, sc27xx_led_of_match);
  184. static struct platform_driver sc27xx_led_driver = {
  185. .driver = {
  186. .name = "sprd-bltc",
  187. .of_match_table = sc27xx_led_of_match,
  188. },
  189. .probe = sc27xx_led_probe,
  190. .remove = sc27xx_led_remove,
  191. };
  192. module_platform_driver(sc27xx_led_driver);
  193. MODULE_DESCRIPTION("Spreadtrum SC27xx breathing light controller driver");
  194. MODULE_AUTHOR("Xiaotong Lu <xiaotong.lu@spreadtrum.com>");
  195. MODULE_LICENSE("GPL v2");