aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--drivers/leds/Kconfig9
-rw-r--r--drivers/leds/Makefile1
-rw-r--r--drivers/leds/leds-lm3601x.c487
3 files changed, 497 insertions, 0 deletions
diff --git a/drivers/leds/Kconfig b/drivers/leds/Kconfig
index cfeb5fb28c2d..6e3a998f3370 100644
--- a/drivers/leds/Kconfig
+++ b/drivers/leds/Kconfig
@@ -158,6 +158,15 @@ config LEDS_LM3692X
158 This option enables support for the TI LM3692x family 158 This option enables support for the TI LM3692x family
159 of white LED string drivers used for backlighting. 159 of white LED string drivers used for backlighting.
160 160
161config LEDS_LM3601X
162 tristate "LED support for LM3601x Chips"
163 depends on LEDS_CLASS && I2C
164 depends on LEDS_CLASS_FLASH
165 select REGMAP_I2C
166 help
167 This option enables support for the TI LM3601x family
168 of flash, torch and indicator classes.
169
161config LEDS_LOCOMO 170config LEDS_LOCOMO
162 tristate "LED Support for Locomo device" 171 tristate "LED Support for Locomo device"
163 depends on LEDS_CLASS 172 depends on LEDS_CLASS
diff --git a/drivers/leds/Makefile b/drivers/leds/Makefile
index 2f7b5c31dc98..420b5d2cfa62 100644
--- a/drivers/leds/Makefile
+++ b/drivers/leds/Makefile
@@ -77,6 +77,7 @@ obj-$(CONFIG_LEDS_NIC78BX) += leds-nic78bx.o
77obj-$(CONFIG_LEDS_MT6323) += leds-mt6323.o 77obj-$(CONFIG_LEDS_MT6323) += leds-mt6323.o
78obj-$(CONFIG_LEDS_LM3692X) += leds-lm3692x.o 78obj-$(CONFIG_LEDS_LM3692X) += leds-lm3692x.o
79obj-$(CONFIG_LEDS_SC27XX_BLTC) += leds-sc27xx-bltc.o 79obj-$(CONFIG_LEDS_SC27XX_BLTC) += leds-sc27xx-bltc.o
80obj-$(CONFIG_LEDS_LM3601X) += leds-lm3601x.o
80 81
81# LED SPI Drivers 82# LED SPI Drivers
82obj-$(CONFIG_LEDS_CR0014114) += leds-cr0014114.o 83obj-$(CONFIG_LEDS_CR0014114) += leds-cr0014114.o
diff --git a/drivers/leds/leds-lm3601x.c b/drivers/leds/leds-lm3601x.c
new file mode 100644
index 000000000000..081aa71e43a3
--- /dev/null
+++ b/drivers/leds/leds-lm3601x.c
@@ -0,0 +1,487 @@
1// SPDX-License-Identifier: GPL-2.0
2// Flash and torch driver for Texas Instruments LM3601X LED
3// Flash driver chip family
4// Copyright (C) 2018 Texas Instruments Incorporated - http://www.ti.com/
5
6#include <linux/delay.h>
7#include <linux/i2c.h>
8#include <linux/leds.h>
9#include <linux/led-class-flash.h>
10#include <linux/module.h>
11#include <linux/regmap.h>
12#include <linux/slab.h>
13#include <uapi/linux/uleds.h>
14
15#define LM3601X_LED_IR 0x0
16#define LM3601X_LED_TORCH 0x1
17
18/* Registers */
19#define LM3601X_ENABLE_REG 0x01
20#define LM3601X_CFG_REG 0x02
21#define LM3601X_LED_FLASH_REG 0x03
22#define LM3601X_LED_TORCH_REG 0x04
23#define LM3601X_FLAGS_REG 0x05
24#define LM3601X_DEV_ID_REG 0x06
25
26#define LM3601X_SW_RESET BIT(7)
27
28/* Enable Mode bits */
29#define LM3601X_MODE_STANDBY 0x00
30#define LM3601X_MODE_IR_DRV BIT(0)
31#define LM3601X_MODE_TORCH BIT(1)
32#define LM3601X_MODE_STROBE (BIT(0) | BIT(1))
33#define LM3601X_STRB_EN BIT(2)
34#define LM3601X_STRB_EDGE_TRIG BIT(3)
35#define LM3601X_IVFM_EN BIT(4)
36
37#define LM36010_BOOST_LIMIT_28 BIT(5)
38#define LM36010_BOOST_FREQ_4MHZ BIT(6)
39#define LM36010_BOOST_MODE_PASS BIT(7)
40
41/* Flag Mask */
42#define LM3601X_FLASH_TIME_OUT BIT(0)
43#define LM3601X_UVLO_FAULT BIT(1)
44#define LM3601X_THERM_SHUTDOWN BIT(2)
45#define LM3601X_THERM_CURR BIT(3)
46#define LM36010_CURR_LIMIT BIT(4)
47#define LM3601X_SHORT_FAULT BIT(5)
48#define LM3601X_IVFM_TRIP BIT(6)
49#define LM36010_OVP_FAULT BIT(7)
50
51#define LM3601X_MAX_TORCH_I_UA 376000
52#define LM3601X_MIN_TORCH_I_UA 2400
53#define LM3601X_TORCH_REG_DIV 2965
54
55#define LM3601X_MAX_STROBE_I_UA 1500000
56#define LM3601X_MIN_STROBE_I_UA 11000
57#define LM3601X_STROBE_REG_DIV 11800
58
59#define LM3601X_TIMEOUT_MASK 0x1e
60#define LM3601X_ENABLE_MASK (LM3601X_MODE_IR_DRV | LM3601X_MODE_TORCH)
61
62#define LM3601X_LOWER_STEP_US 40000
63#define LM3601X_UPPER_STEP_US 200000
64#define LM3601X_MIN_TIMEOUT_US 40000
65#define LM3601X_MAX_TIMEOUT_US 1600000
66#define LM3601X_TIMEOUT_XOVER_US 400000
67
68enum lm3601x_type {
69 CHIP_LM36010 = 0,
70 CHIP_LM36011,
71};
72
73/**
74 * struct lm3601x_led -
75 * @fled_cdev: flash LED class device pointer
76 * @client: Pointer to the I2C client
77 * @regmap: Devices register map
78 * @lock: Lock for reading/writing the device
79 * @led_name: LED label for the Torch or IR LED
80 * @flash_timeout: the timeout for the flash
81 * @last_flag: last known flags register value
82 * @torch_current_max: maximum current for the torch
83 * @flash_current_max: maximum current for the flash
84 * @max_flash_timeout: maximum timeout for the flash
85 * @led_mode: The mode to enable either IR or Torch
86 */
87struct lm3601x_led {
88 struct led_classdev_flash fled_cdev;
89 struct i2c_client *client;
90 struct regmap *regmap;
91 struct mutex lock;
92
93 char led_name[LED_MAX_NAME_SIZE];
94
95 unsigned int flash_timeout;
96 unsigned int last_flag;
97
98 u32 torch_current_max;
99 u32 flash_current_max;
100 u32 max_flash_timeout;
101
102 u32 led_mode;
103};
104
105static const struct reg_default lm3601x_regmap_defs[] = {
106 { LM3601X_ENABLE_REG, 0x20 },
107 { LM3601X_CFG_REG, 0x15 },
108 { LM3601X_LED_FLASH_REG, 0x00 },
109 { LM3601X_LED_TORCH_REG, 0x00 },
110};
111
112static bool lm3601x_volatile_reg(struct device *dev, unsigned int reg)
113{
114 switch (reg) {
115 case LM3601X_FLAGS_REG:
116 return true;
117 default:
118 return false;
119 }
120}
121
122static const struct regmap_config lm3601x_regmap = {
123 .reg_bits = 8,
124 .val_bits = 8,
125
126 .max_register = LM3601X_DEV_ID_REG,
127 .reg_defaults = lm3601x_regmap_defs,
128 .num_reg_defaults = ARRAY_SIZE(lm3601x_regmap_defs),
129 .cache_type = REGCACHE_RBTREE,
130 .volatile_reg = lm3601x_volatile_reg,
131};
132
133static struct lm3601x_led *fled_cdev_to_led(struct led_classdev_flash *fled_cdev)
134{
135 return container_of(fled_cdev, struct lm3601x_led, fled_cdev);
136}
137
138static int lm3601x_read_faults(struct lm3601x_led *led)
139{
140 int flags_val;
141 int ret;
142
143 ret = regmap_read(led->regmap, LM3601X_FLAGS_REG, &flags_val);
144 if (ret < 0)
145 return -EIO;
146
147 led->last_flag = 0;
148
149 if (flags_val & LM36010_OVP_FAULT)
150 led->last_flag |= LED_FAULT_OVER_VOLTAGE;
151
152 if (flags_val & (LM3601X_THERM_SHUTDOWN | LM3601X_THERM_CURR))
153 led->last_flag |= LED_FAULT_OVER_TEMPERATURE;
154
155 if (flags_val & LM3601X_SHORT_FAULT)
156 led->last_flag |= LED_FAULT_SHORT_CIRCUIT;
157
158 if (flags_val & LM36010_CURR_LIMIT)
159 led->last_flag |= LED_FAULT_OVER_CURRENT;
160
161 if (flags_val & LM3601X_UVLO_FAULT)
162 led->last_flag |= LED_FAULT_UNDER_VOLTAGE;
163
164 if (flags_val & LM3601X_IVFM_TRIP)
165 led->last_flag |= LED_FAULT_INPUT_VOLTAGE;
166
167 if (flags_val & LM3601X_THERM_SHUTDOWN)
168 led->last_flag |= LED_FAULT_LED_OVER_TEMPERATURE;
169
170 return led->last_flag;
171}
172
173static int lm3601x_brightness_set(struct led_classdev *cdev,
174 enum led_brightness brightness)
175{
176 struct led_classdev_flash *fled_cdev = lcdev_to_flcdev(cdev);
177 struct lm3601x_led *led = fled_cdev_to_led(fled_cdev);
178 int ret, led_mode_val;
179
180 mutex_lock(&led->lock);
181
182 ret = lm3601x_read_faults(led);
183 if (ret < 0)
184 goto out;
185
186 if (led->led_mode == LM3601X_LED_TORCH)
187 led_mode_val = LM3601X_MODE_TORCH;
188 else
189 led_mode_val = LM3601X_MODE_IR_DRV;
190
191 if (brightness == LED_OFF) {
192 ret = regmap_update_bits(led->regmap, LM3601X_ENABLE_REG,
193 led_mode_val, LED_OFF);
194 goto out;
195 }
196
197 ret = regmap_write(led->regmap, LM3601X_LED_TORCH_REG, brightness);
198 if (ret < 0)
199 goto out;
200
201 ret = regmap_update_bits(led->regmap, LM3601X_ENABLE_REG,
202 LM3601X_MODE_TORCH | LM3601X_MODE_IR_DRV,
203 led_mode_val);
204out:
205 mutex_unlock(&led->lock);
206 return ret;
207}
208
209static int lm3601x_strobe_set(struct led_classdev_flash *fled_cdev,
210 bool state)
211{
212 struct lm3601x_led *led = fled_cdev_to_led(fled_cdev);
213 int timeout_reg_val;
214 int current_timeout;
215 int ret;
216
217 mutex_lock(&led->lock);
218
219 ret = regmap_read(led->regmap, LM3601X_CFG_REG, &current_timeout);
220 if (ret < 0)
221 goto out;
222
223 if (led->flash_timeout >= LM3601X_TIMEOUT_XOVER_US)
224 timeout_reg_val = led->flash_timeout / LM3601X_UPPER_STEP_US + 0x07;
225 else
226 timeout_reg_val = led->flash_timeout / LM3601X_LOWER_STEP_US - 0x01;
227
228 if (led->flash_timeout != current_timeout)
229 ret = regmap_update_bits(led->regmap, LM3601X_CFG_REG,
230 LM3601X_TIMEOUT_MASK, timeout_reg_val);
231
232 if (state)
233 ret = regmap_update_bits(led->regmap, LM3601X_ENABLE_REG,
234 LM3601X_MODE_TORCH | LM3601X_MODE_IR_DRV,
235 LM3601X_MODE_STROBE);
236 else
237 ret = regmap_update_bits(led->regmap, LM3601X_ENABLE_REG,
238 LM3601X_MODE_STROBE, LED_OFF);
239
240 ret = lm3601x_read_faults(led);
241out:
242 mutex_unlock(&led->lock);
243 return ret;
244}
245
246static int lm3601x_flash_brightness_set(struct led_classdev_flash *fled_cdev,
247 u32 brightness)
248{
249 struct lm3601x_led *led = fled_cdev_to_led(fled_cdev);
250 u8 brightness_val;
251 int ret;
252
253 mutex_lock(&led->lock);
254 ret = lm3601x_read_faults(led);
255 if (ret < 0)
256 goto out;
257
258 if (brightness == LED_OFF) {
259 ret = regmap_update_bits(led->regmap, LM3601X_ENABLE_REG,
260 LM3601X_MODE_STROBE, LED_OFF);
261 goto out;
262 }
263
264 brightness_val = brightness / LM3601X_STROBE_REG_DIV;
265
266 ret = regmap_write(led->regmap, LM3601X_LED_FLASH_REG, brightness_val);
267out:
268 mutex_unlock(&led->lock);
269 return ret;
270}
271
272static int lm3601x_flash_timeout_set(struct led_classdev_flash *fled_cdev,
273 u32 timeout)
274{
275 struct lm3601x_led *led = fled_cdev_to_led(fled_cdev);
276
277 mutex_lock(&led->lock);
278
279 led->flash_timeout = timeout;
280
281 mutex_unlock(&led->lock);
282
283 return 0;
284}
285
286static int lm3601x_strobe_get(struct led_classdev_flash *fled_cdev, bool *state)
287{
288 struct lm3601x_led *led = fled_cdev_to_led(fled_cdev);
289 int strobe_state;
290 int ret;
291
292 mutex_lock(&led->lock);
293
294 ret = regmap_read(led->regmap, LM3601X_ENABLE_REG, &strobe_state);
295 if (ret < 0)
296 goto out;
297
298 *state = strobe_state & LM3601X_MODE_STROBE;
299
300out:
301 mutex_unlock(&led->lock);
302 return ret;
303}
304
305static int lm3601x_flash_fault_get(struct led_classdev_flash *fled_cdev,
306 u32 *fault)
307{
308 struct lm3601x_led *led = fled_cdev_to_led(fled_cdev);
309
310 lm3601x_read_faults(led);
311
312 *fault = led->last_flag;
313
314 return 0;
315}
316
317static const struct led_flash_ops flash_ops = {
318 .flash_brightness_set = lm3601x_flash_brightness_set,
319 .strobe_set = lm3601x_strobe_set,
320 .strobe_get = lm3601x_strobe_get,
321 .timeout_set = lm3601x_flash_timeout_set,
322 .fault_get = lm3601x_flash_fault_get,
323};
324
325static int lm3601x_register_leds(struct lm3601x_led *led)
326{
327 struct led_classdev *led_cdev;
328 struct led_flash_setting *setting;
329
330 led->fled_cdev.ops = &flash_ops;
331
332 setting = &led->fled_cdev.timeout;
333 setting->min = LM3601X_MIN_TIMEOUT_US;
334 setting->max = led->max_flash_timeout;
335 setting->step = LM3601X_LOWER_STEP_US;
336 setting->val = led->max_flash_timeout;
337
338 setting = &led->fled_cdev.brightness;
339 setting->min = LM3601X_MIN_STROBE_I_UA;
340 setting->max = led->flash_current_max;
341 setting->step = LM3601X_TORCH_REG_DIV;
342 setting->val = led->flash_current_max;
343
344 led_cdev = &led->fled_cdev.led_cdev;
345 led_cdev->name = led->led_name;
346 led_cdev->brightness_set_blocking = lm3601x_brightness_set;
347 led_cdev->max_brightness = DIV_ROUND_UP(led->torch_current_max,
348 LM3601X_TORCH_REG_DIV);
349 led_cdev->flags |= LED_DEV_CAP_FLASH;
350
351 return led_classdev_flash_register(&led->client->dev, &led->fled_cdev);
352}
353
354static int lm3601x_parse_node(struct lm3601x_led *led)
355{
356 struct fwnode_handle *child = NULL;
357 int ret = -ENODEV;
358 const char *name;
359
360 child = device_get_next_child_node(&led->client->dev, child);
361 if (!child) {
362 dev_err(&led->client->dev, "No LED Child node\n");
363 return ret;
364 }
365
366 ret = fwnode_property_read_u32(child, "reg", &led->led_mode);
367 if (ret) {
368 dev_err(&led->client->dev, "reg DT property missing\n");
369 goto out_err;
370 }
371
372 if (led->led_mode > LM3601X_LED_TORCH ||
373 led->led_mode < LM3601X_LED_IR) {
374 dev_warn(&led->client->dev, "Invalid led mode requested\n");
375 ret = -EINVAL;
376 goto out_err;
377 }
378
379 ret = fwnode_property_read_string(child, "label", &name);
380 if (ret) {
381 if (led->led_mode == LM3601X_LED_TORCH)
382 name = "torch";
383 else
384 name = "infrared";
385 }
386
387 snprintf(led->led_name, sizeof(led->led_name),
388 "%s:%s", led->client->name, name);
389
390 ret = fwnode_property_read_u32(child, "led-max-microamp",
391 &led->torch_current_max);
392 if (ret) {
393 dev_warn(&led->client->dev,
394 "led-max-microamp DT property missing\n");
395 goto out_err;
396 }
397
398 ret = fwnode_property_read_u32(child, "flash-max-microamp",
399 &led->flash_current_max);
400 if (ret) {
401 dev_warn(&led->client->dev,
402 "flash-max-microamp DT property missing\n");
403 goto out_err;
404 }
405
406 ret = fwnode_property_read_u32(child, "flash-max-timeout-us",
407 &led->max_flash_timeout);
408 if (ret) {
409 dev_warn(&led->client->dev,
410 "flash-max-timeout-us DT property missing\n");
411 goto out_err;
412 }
413
414out_err:
415 fwnode_handle_put(child);
416 return ret;
417}
418
419static int lm3601x_probe(struct i2c_client *client)
420{
421 struct lm3601x_led *led;
422 int ret;
423
424 led = devm_kzalloc(&client->dev, sizeof(*led), GFP_KERNEL);
425 if (!led)
426 return -ENOMEM;
427
428 led->client = client;
429 i2c_set_clientdata(client, led);
430
431 ret = lm3601x_parse_node(led);
432 if (ret)
433 return -ENODEV;
434
435 led->regmap = devm_regmap_init_i2c(client, &lm3601x_regmap);
436 if (IS_ERR(led->regmap)) {
437 ret = PTR_ERR(led->regmap);
438 dev_err(&client->dev,
439 "Failed to allocate register map: %d\n", ret);
440 return ret;
441 }
442
443 mutex_init(&led->lock);
444
445 return lm3601x_register_leds(led);
446}
447
448static int lm3601x_remove(struct i2c_client *client)
449{
450 struct lm3601x_led *led = i2c_get_clientdata(client);
451
452 led_classdev_flash_unregister(&led->fled_cdev);
453 mutex_destroy(&led->lock);
454
455 return regmap_update_bits(led->regmap, LM3601X_ENABLE_REG,
456 LM3601X_ENABLE_MASK,
457 LM3601X_MODE_STANDBY);
458}
459
460static const struct i2c_device_id lm3601x_id[] = {
461 { "LM36010", CHIP_LM36010 },
462 { "LM36011", CHIP_LM36011 },
463 { }
464};
465MODULE_DEVICE_TABLE(i2c, lm3601x_id);
466
467static const struct of_device_id of_lm3601x_leds_match[] = {
468 { .compatible = "ti,lm36010", },
469 { .compatible = "ti,lm36011", },
470 { }
471};
472MODULE_DEVICE_TABLE(of, of_lm3601x_leds_match);
473
474static struct i2c_driver lm3601x_i2c_driver = {
475 .driver = {
476 .name = "lm3601x",
477 .of_match_table = of_lm3601x_leds_match,
478 },
479 .probe_new = lm3601x_probe,
480 .remove = lm3601x_remove,
481 .id_table = lm3601x_id,
482};
483module_i2c_driver(lm3601x_i2c_driver);
484
485MODULE_DESCRIPTION("Texas Instruments Flash Lighting driver for LM3601X");
486MODULE_AUTHOR("Dan Murphy <dmurphy@ti.com>");
487MODULE_LICENSE("GPL v2");