leds-as3668.c 5.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202
  1. // SPDX-License-Identifier: GPL-2.0-or-later
  2. /*
  3. * Osram AMS AS3668 LED Driver IC
  4. *
  5. * Copyright (C) 2025 Lukas Timmermann <linux@timmermann.space>
  6. */
  7. #include <linux/bitfield.h>
  8. #include <linux/i2c.h>
  9. #include <linux/leds.h>
  10. #include <linux/module.h>
  11. #include <linux/uleds.h>
  12. #define AS3668_MAX_LEDS 4
  13. /* Chip Ident */
  14. #define AS3668_CHIP_ID1_REG 0x3e
  15. #define AS3668_CHIP_ID 0xa5
  16. /* Current Control */
  17. #define AS3668_CURR_MODE_REG 0x01
  18. #define AS3668_CURR_MODE_OFF 0x0
  19. #define AS3668_CURR_MODE_ON 0x1
  20. #define AS3668_CURR1_MODE_MASK GENMASK(1, 0)
  21. #define AS3668_CURR2_MODE_MASK GENMASK(3, 2)
  22. #define AS3668_CURR3_MODE_MASK GENMASK(5, 4)
  23. #define AS3668_CURR4_MODE_MASK GENMASK(7, 6)
  24. #define AS3668_CURR1_REG 0x02
  25. #define AS3668_CURR2_REG 0x03
  26. #define AS3668_CURR3_REG 0x04
  27. #define AS3668_CURR4_REG 0x05
  28. #define AS3668_CURR_MODE_PACK(mode) (((mode) << 0) | \
  29. ((mode) << 2) | \
  30. ((mode) << 4) | \
  31. ((mode) << 6))
  32. struct as3668_led {
  33. struct led_classdev cdev;
  34. struct as3668 *chip;
  35. struct fwnode_handle *fwnode;
  36. u8 mode_mask;
  37. u8 current_reg;
  38. };
  39. struct as3668 {
  40. struct i2c_client *client;
  41. struct as3668_led leds[AS3668_MAX_LEDS];
  42. };
  43. static int as3668_channel_mode_set(struct as3668_led *led, u8 mode)
  44. {
  45. int ret;
  46. u8 channel_modes;
  47. ret = i2c_smbus_read_byte_data(led->chip->client, AS3668_CURR_MODE_REG);
  48. if (ret < 0) {
  49. dev_err(led->cdev.dev, "failed to read channel modes\n");
  50. return ret;
  51. }
  52. channel_modes = (u8)ret;
  53. channel_modes &= ~led->mode_mask;
  54. channel_modes |= led->mode_mask & (AS3668_CURR_MODE_PACK(mode));
  55. return i2c_smbus_write_byte_data(led->chip->client, AS3668_CURR_MODE_REG, channel_modes);
  56. }
  57. static enum led_brightness as3668_brightness_get(struct led_classdev *cdev)
  58. {
  59. struct as3668_led *led = container_of(cdev, struct as3668_led, cdev);
  60. return i2c_smbus_read_byte_data(led->chip->client, led->current_reg);
  61. }
  62. static void as3668_brightness_set(struct led_classdev *cdev, enum led_brightness brightness)
  63. {
  64. struct as3668_led *led = container_of(cdev, struct as3668_led, cdev);
  65. int err;
  66. err = as3668_channel_mode_set(led, !!brightness);
  67. if (err)
  68. dev_err(cdev->dev, "failed to set channel mode: %d\n", err);
  69. err = i2c_smbus_write_byte_data(led->chip->client, led->current_reg, brightness);
  70. if (err)
  71. dev_err(cdev->dev, "failed to set brightness: %d\n", err);
  72. }
  73. static int as3668_dt_init(struct as3668 *as3668)
  74. {
  75. struct device *dev = &as3668->client->dev;
  76. struct as3668_led *led;
  77. struct led_init_data init_data = {};
  78. int err;
  79. u32 reg;
  80. for_each_available_child_of_node_scoped(dev_of_node(dev), child) {
  81. err = of_property_read_u32(child, "reg", &reg);
  82. if (err)
  83. return dev_err_probe(dev, err, "failed to read 'reg' property");
  84. if (reg < 0 || reg >= AS3668_MAX_LEDS)
  85. return dev_err_probe(dev, -EINVAL,
  86. "unsupported LED: %d\n", reg);
  87. led = &as3668->leds[reg];
  88. led->fwnode = of_fwnode_handle(child);
  89. led->current_reg = reg + AS3668_CURR1_REG;
  90. led->mode_mask = AS3668_CURR1_MODE_MASK << (reg * 2);
  91. led->chip = as3668;
  92. led->cdev.max_brightness = U8_MAX;
  93. led->cdev.brightness_get = as3668_brightness_get;
  94. led->cdev.brightness_set = as3668_brightness_set;
  95. init_data.fwnode = led->fwnode;
  96. init_data.default_label = ":";
  97. err = devm_led_classdev_register_ext(dev, &led->cdev, &init_data);
  98. if (err)
  99. return dev_err_probe(dev, err, "failed to register LED %d\n", reg);
  100. }
  101. return 0;
  102. }
  103. static int as3668_probe(struct i2c_client *client)
  104. {
  105. struct as3668 *as3668;
  106. int err;
  107. u8 chip_id;
  108. chip_id = i2c_smbus_read_byte_data(client, AS3668_CHIP_ID1_REG);
  109. if (chip_id != AS3668_CHIP_ID)
  110. return dev_err_probe(&client->dev, -ENODEV,
  111. "expected chip ID 0x%02x, got 0x%02x\n",
  112. AS3668_CHIP_ID, chip_id);
  113. as3668 = devm_kzalloc(&client->dev, sizeof(*as3668), GFP_KERNEL);
  114. if (!as3668)
  115. return -ENOMEM;
  116. as3668->client = client;
  117. err = as3668_dt_init(as3668);
  118. if (err)
  119. return err;
  120. /* Set all four channel modes to 'off' */
  121. err = i2c_smbus_write_byte_data(client, AS3668_CURR_MODE_REG,
  122. FIELD_PREP(AS3668_CURR1_MODE_MASK, AS3668_CURR_MODE_OFF) |
  123. FIELD_PREP(AS3668_CURR2_MODE_MASK, AS3668_CURR_MODE_OFF) |
  124. FIELD_PREP(AS3668_CURR3_MODE_MASK, AS3668_CURR_MODE_OFF) |
  125. FIELD_PREP(AS3668_CURR4_MODE_MASK, AS3668_CURR_MODE_OFF));
  126. /* Set initial currents to 0mA */
  127. err |= i2c_smbus_write_byte_data(client, AS3668_CURR1_REG, 0);
  128. err |= i2c_smbus_write_byte_data(client, AS3668_CURR2_REG, 0);
  129. err |= i2c_smbus_write_byte_data(client, AS3668_CURR3_REG, 0);
  130. err |= i2c_smbus_write_byte_data(client, AS3668_CURR4_REG, 0);
  131. if (err)
  132. return dev_err_probe(&client->dev, -EIO, "failed to set zero initial current levels\n");
  133. return 0;
  134. }
  135. static void as3668_remove(struct i2c_client *client)
  136. {
  137. i2c_smbus_write_byte_data(client, AS3668_CURR_MODE_REG, 0);
  138. }
  139. static const struct i2c_device_id as3668_idtable[] = {
  140. { "as3668" },
  141. { }
  142. };
  143. MODULE_DEVICE_TABLE(i2c, as3668_idtable);
  144. static const struct of_device_id as3668_match_table[] = {
  145. { .compatible = "ams,as3668" },
  146. { }
  147. };
  148. MODULE_DEVICE_TABLE(of, as3668_match_table);
  149. static struct i2c_driver as3668_driver = {
  150. .driver = {
  151. .name = "leds_as3668",
  152. .of_match_table = as3668_match_table,
  153. },
  154. .probe = as3668_probe,
  155. .remove = as3668_remove,
  156. .id_table = as3668_idtable,
  157. };
  158. module_i2c_driver(as3668_driver);
  159. MODULE_AUTHOR("Lukas Timmermann <linux@timmermann.space>");
  160. MODULE_DESCRIPTION("AS3668 LED driver");
  161. MODULE_LICENSE("GPL");