blob: a5776d3174fc83397dcdf63bb89eb48a8d8b1dfe [file] [log] [blame]
Doug Zobel72b5a882023-11-17 12:38:11 +01001// SPDX-License-Identifier: GPL-2.0+
2/*
3 * Copyright (C) 2018 Doug Zobel <douglas.zobel@climate.com>
4 *
5 * Driver for TI lp5562 4 channel LED driver. There are only 3
6 * engines available for the 4 LEDs, so white and blue LEDs share
7 * the same engine. This means that the blink period is shared
8 * between them. Changing the period of blue blink will affect
9 * the white period (and vice-versa). Blue and white On/Off
10 * states remain independent (as would PWM brightness if that's
11 * ever added to the LED core).
12 */
13
14#include <dm.h>
15#include <errno.h>
16#include <led.h>
17#include <i2c.h>
18#include <asm/gpio.h>
19#include <linux/delay.h>
20
21#define DEFAULT_CURRENT 100 /* 10 mA */
22#define MIN_BLINK_PERIOD 32 /* ms */
23#define MAX_BLINK_PERIOD 2248 /* ms */
24
25/* Register Map */
26#define REG_ENABLE 0x00
27#define REG_OP_MODE 0x01
28#define REG_B_PWM 0x02
29#define REG_G_PWM 0x03
30#define REG_R_PWM 0x04
31#define REG_B_CUR 0x05
32#define REG_G_CUR 0x06
33#define REG_R_CUR 0x07
34#define REG_CONFIG 0x08
35#define REG_ENG1_PC 0x09
36#define REG_ENG2_PC 0x0A
37#define REG_ENG3_PC 0x0B
38#define REG_STATUS 0x0C
39#define REG_RESET 0x0D
40#define REG_W_PWM 0x0E
41#define REG_W_CUR 0x0F
42#define REG_ENG1_MEM_BEGIN 0x10
43#define REG_ENG2_MEM_BEGIN 0x30
44#define REG_ENG3_MEM_BEGIN 0x50
45#define REG_LED_MAP 0x70
46
47/* LED Register Values */
48/* 0x00 ENABLE */
49#define REG_ENABLE_CHIP_ENABLE (0x1 << 6)
50#define REG_ENABLE_ENG_EXEC_HOLD 0x0
51#define REG_ENABLE_ENG_EXEC_RUN 0x2
52#define REG_ENABLE_ENG_EXEC_MASK 0x3
53
54/* 0x01 OP MODE */
55#define REG_OP_MODE_DISABLED 0x0
56#define REG_OP_MODE_LOAD_SRAM 0x1
57#define REG_OP_MODE_RUN 0x2
58#define REG_OP_MODE_MASK 0x3
59
60/* 0x02, 0x03, 0x04, 0x0E PWM */
61#define REG_PWM_MIN_VALUE 0
62#define REG_PWM_MAX_VALUE 0xFF
63
64/* 0x08 CONFIG */
65#define REG_CONFIG_EXT_CLK 0x0
66#define REG_CONFIG_INT_CLK 0x1
67#define REG_CONFIG_AUTO_CLK 0x2
68#define REG_CONFIG_CLK_MASK 0x3
69
70/* 0x0D RESET */
71#define REG_RESET_RESET 0xFF
72
73/* 0x70 LED MAP */
74#define REG_LED_MAP_ENG_MASK 0x03
75#define REG_LED_MAP_W_ENG_SHIFT 6
76#define REG_LED_MAP_R_ENG_SHIFT 4
77#define REG_LED_MAP_G_ENG_SHIFT 2
78#define REG_LED_MAP_B_ENG_SHIFT 0
79
80/* Engine program related */
81#define REG_ENGINE_MEM_SIZE 0x20
82#define LED_PGRM_RAMP_INCREMENT_SHIFT 0
83#define LED_PGRM_RAMP_SIGN_SHIFT 7
84#define LED_PGRM_RAMP_STEP_SHIFT 8
85#define LED_PGRM_RAMP_PRESCALE_SHIFT 14
86
87struct lp5562_led_wrap_priv {
88 struct gpio_desc enable_gpio;
89};
90
91struct lp5562_led_priv {
92 u8 reg_pwm;
93 u8 reg_current;
94 u8 map_shift;
95 u8 enginenum;
96};
97
98/* enum values map to LED_MAP (0x70) values */
99enum lp5562_led_ctl_mode {
100 I2C = 0x0,
101#ifdef CONFIG_LED_BLINK
102 ENGINE1 = 0x1,
103 ENGINE2 = 0x2,
104 ENGINE3 = 0x3
105#endif
106};
107
108/*
109 * Update a register value
110 * dev - I2C udevice (parent of led)
111 * regnum - register number to update
112 * value - value to write to register
113 * mask - mask of bits that should be changed
114 */
115static int lp5562_led_reg_update(struct udevice *dev, int regnum,
116 u8 value, u8 mask)
117{
118 int ret;
119
120 if (mask == 0xFF)
121 ret = dm_i2c_reg_write(dev, regnum, value);
122 else
123 ret = dm_i2c_reg_clrset(dev, regnum, mask, value);
124
Doug Zobel72b5a882023-11-17 12:38:11 +0100125 /*
126 * Data sheet says "Delay between consecutive I2C writes to
Michal Simekcc046dc2024-04-16 08:55:19 +0200127 * ENABLE register (00h) need to be longer than 488 us
Doug Zobel72b5a882023-11-17 12:38:11 +0100128 * (typical)." and "Delay between consecutive I2C writes to
Michal Simekcc046dc2024-04-16 08:55:19 +0200129 * OP_MODE register need to be longer than 153 us (typ)."
Doug Zobel72b5a882023-11-17 12:38:11 +0100130 *
131 * The linux driver does usleep_range(500, 600) and
132 * usleep_range(200, 300), respectively.
133 */
134 switch (regnum) {
135 case REG_ENABLE:
136 udelay(600);
137 break;
138 case REG_OP_MODE:
139 udelay(300);
140 break;
141 }
142
143 return ret;
144}
145
146#ifdef CONFIG_LED_BLINK
147/*
148 * Program the lp5562 engine
149 * dev - I2C udevice (parent of led)
150 * program - array of commands
151 * size - number of commands in program array (1-16)
152 * engine - engine number (1-3)
153 */
154static int lp5562_led_program_engine(struct udevice *dev, u16 *program,
155 u8 size, u8 engine)
156{
157 int ret, cmd;
158 u8 engine_reg = REG_ENG1_MEM_BEGIN +
159 ((engine - 1) * REG_ENGINE_MEM_SIZE);
160 u8 shift = (3 - engine) * 2;
161 __be16 prog_be[16];
162
163 if (size < 1 || size > 16 || engine < 1 || engine > 3)
164 return -EINVAL;
165
166 for (cmd = 0; cmd < size; cmd++)
167 prog_be[cmd] = cpu_to_be16(program[cmd]);
168
169 /* set engine mode to 'disabled' */
170 ret = lp5562_led_reg_update(dev, REG_OP_MODE,
171 REG_OP_MODE_DISABLED << shift,
172 REG_OP_MODE_MASK << shift);
173 if (ret != 0)
174 goto done;
175
176 /* set exec mode to 'hold' */
177 ret = lp5562_led_reg_update(dev, REG_ENABLE,
178 REG_ENABLE_ENG_EXEC_HOLD << shift,
179 REG_ENABLE_ENG_EXEC_MASK << shift);
180 if (ret != 0)
181 goto done;
182
183 /* set engine mode to 'load SRAM' */
184 ret = lp5562_led_reg_update(dev, REG_OP_MODE,
185 REG_OP_MODE_LOAD_SRAM << shift,
186 REG_OP_MODE_MASK << shift);
187 if (ret != 0)
188 goto done;
189
190 /* send the re-ordered program sequence */
191 ret = dm_i2c_write(dev, engine_reg, (uchar *)prog_be, sizeof(u16) * size);
192 if (ret != 0)
193 goto done;
194
195 /* set engine mode to 'run' */
196 ret = lp5562_led_reg_update(dev, REG_OP_MODE,
197 REG_OP_MODE_RUN << shift,
198 REG_OP_MODE_MASK << shift);
199 if (ret != 0)
200 goto done;
201
202 /* set engine exec to 'run' */
203 ret = lp5562_led_reg_update(dev, REG_ENABLE,
204 REG_ENABLE_ENG_EXEC_RUN << shift,
205 REG_ENABLE_ENG_EXEC_MASK << shift);
206
207done:
208 return ret;
209}
210
211/*
212 * Get the LED's current control mode (I2C or ENGINE[1-3])
213 * dev - led udevice (child udevice)
214 */
215static enum lp5562_led_ctl_mode lp5562_led_get_control_mode(struct udevice *dev)
216{
217 struct lp5562_led_priv *priv = dev_get_priv(dev);
218 u8 data;
219 enum lp5562_led_ctl_mode mode = I2C;
220
221 if (dm_i2c_read(dev_get_parent(dev), REG_LED_MAP, &data, 1) == 0)
222 mode = (data & (REG_LED_MAP_ENG_MASK << priv->map_shift))
223 >> priv->map_shift;
224
225 return mode;
226}
227#endif
228
229/*
230 * Set the LED's control mode to I2C or ENGINE[1-3]
231 * dev - led udevice (child udevice)
232 * mode - mode to change to
233 */
234static int lp5562_led_set_control_mode(struct udevice *dev,
235 enum lp5562_led_ctl_mode mode)
236{
237 struct lp5562_led_priv *priv = dev_get_priv(dev);
238
239 return (lp5562_led_reg_update(dev_get_parent(dev), REG_LED_MAP,
240 mode << priv->map_shift,
241 REG_LED_MAP_ENG_MASK << priv->map_shift));
242}
243
244/*
245 * Return the LED's PWM value; If LED is in BLINK state, then it is
246 * under engine control mode which doesn't use this PWM value.
247 * dev - led udevice (child udevice)
248 */
249static int lp5562_led_get_pwm(struct udevice *dev)
250{
251 struct lp5562_led_priv *priv = dev_get_priv(dev);
252 u8 data;
253
254 if (dm_i2c_read(dev_get_parent(dev), priv->reg_pwm, &data, 1) != 0)
255 return -EINVAL;
256
257 return data;
258}
259
260/*
261 * Set the LED's PWM value and configure it to use this (I2C mode).
262 * dev - led udevice (child udevice)
263 * value - PWM value (0 - 255)
264 */
265static int lp5562_led_set_pwm(struct udevice *dev, u8 value)
266{
267 struct lp5562_led_priv *priv = dev_get_priv(dev);
268
269 if (lp5562_led_reg_update(dev_get_parent(dev), priv->reg_pwm,
270 value, 0xff) != 0)
271 return -EINVAL;
272
273 /* set LED to I2C register mode */
274 return lp5562_led_set_control_mode(dev, I2C);
275}
276
277/*
278 * Return the led's current state
279 * dev - led udevice (child udevice)
280 *
281 */
282static enum led_state_t lp5562_led_get_state(struct udevice *dev)
283{
284 enum led_state_t state = LEDST_ON;
285
286 if (lp5562_led_get_pwm(dev) == REG_PWM_MIN_VALUE)
287 state = LEDST_OFF;
288
289#ifdef CONFIG_LED_BLINK
290 if (lp5562_led_get_control_mode(dev) != I2C)
291 state = LEDST_BLINK;
292#endif
293
294 return state;
295}
296
297/*
298 * Set the led state
299 * dev - led udevice (child udevice)
300 * state - State to set the LED to
301 */
302static int lp5562_led_set_state(struct udevice *dev, enum led_state_t state)
303{
304#ifdef CONFIG_LED_BLINK
305 struct lp5562_led_priv *priv = dev_get_priv(dev);
306#endif
307
308 switch (state) {
309 case LEDST_OFF:
310 return lp5562_led_set_pwm(dev, REG_PWM_MIN_VALUE);
311 case LEDST_ON:
312 return lp5562_led_set_pwm(dev, REG_PWM_MAX_VALUE);
313#ifdef CONFIG_LED_BLINK
314 case LEDST_BLINK:
315 return lp5562_led_set_control_mode(dev, priv->enginenum);
316#endif
317 case LEDST_TOGGLE:
318 if (lp5562_led_get_state(dev) == LEDST_OFF)
319 return lp5562_led_set_state(dev, LEDST_ON);
320 else
321 return lp5562_led_set_state(dev, LEDST_OFF);
322 break;
323 default:
324 return -EINVAL;
325 }
326
327 return 0;
328}
329
330#ifdef CONFIG_LED_BLINK
331/*
332 * Set the blink period of an LED; note blue and white share the same
333 * engine so changing the period of one affects the other.
334 * dev - led udevice (child udevice)
335 * period_ms - blink period in ms
336 */
337static int lp5562_led_set_period(struct udevice *dev, int period_ms)
338{
339 struct lp5562_led_priv *priv = dev_get_priv(dev);
340 u8 opcode = 0;
341 u16 program[7];
342 u16 wait_time;
343
344 /* Blink is implemented as an engine program. Simple on/off
345 * for short periods, or fade in/fade out for longer periods:
346 *
347 * if (period_ms < 500):
348 * set PWM to 100%
349 * pause for period / 2
350 * set PWM to 0%
351 * pause for period / 2
352 * goto start
353 *
354 * else
355 * raise PWM 0% -> 50% in 62.7 ms
356 * raise PWM 50% -> 100% in 62.7 ms
357 * pause for (period - 4 * 62.7) / 2
358 * lower PWM 100% -> 50% in 62.7 ms
359 * lower PWM 50% -> 0% in 62.7 ms
360 * pause for (period - 4 * 62.7) / 2
361 * goto start
362 */
363
364 if (period_ms < MIN_BLINK_PERIOD)
365 period_ms = MIN_BLINK_PERIOD;
366 else if (period_ms > MAX_BLINK_PERIOD)
367 period_ms = MAX_BLINK_PERIOD;
368
369 if (period_ms < 500) {
370 /* Simple on/off blink */
371 wait_time = period_ms / 2;
372
373 /* 1st command is full brightness */
374 program[opcode++] =
375 (1 << LED_PGRM_RAMP_PRESCALE_SHIFT) |
376 REG_PWM_MAX_VALUE;
377
378 /* 2nd command is wait (period / 2) using 15.6ms steps */
379 program[opcode++] =
380 (1 << LED_PGRM_RAMP_PRESCALE_SHIFT) |
381 (((wait_time * 10) / 156) << LED_PGRM_RAMP_STEP_SHIFT) |
382 (0 << LED_PGRM_RAMP_INCREMENT_SHIFT);
383
384 /* 3rd command is 0% brightness */
385 program[opcode++] =
386 (1 << LED_PGRM_RAMP_PRESCALE_SHIFT);
387
388 /* 4th command is wait (period / 2) using 15.6ms steps */
389 program[opcode++] =
390 (1 << LED_PGRM_RAMP_PRESCALE_SHIFT) |
391 (((wait_time * 10) / 156) << LED_PGRM_RAMP_STEP_SHIFT) |
392 (0 << LED_PGRM_RAMP_INCREMENT_SHIFT);
393
394 /* 5th command: repeat */
395 program[opcode++] = 0x00;
396 } else {
397 /* fade-in / fade-out blink */
398 wait_time = ((period_ms - 251) / 2);
399
400 /* ramp up time is 256 * 0.49ms (125.4ms) done in 2 steps */
401 /* 1st command is ramp up 1/2 way */
402 program[opcode++] =
403 (0 << LED_PGRM_RAMP_PRESCALE_SHIFT) |
404 (1 << LED_PGRM_RAMP_STEP_SHIFT) |
405 (127 << LED_PGRM_RAMP_INCREMENT_SHIFT);
406
407 /* 2nd command is ramp up rest of the way */
408 program[opcode++] =
409 (0 << LED_PGRM_RAMP_PRESCALE_SHIFT) |
410 (1 << LED_PGRM_RAMP_STEP_SHIFT) |
411 (127 << LED_PGRM_RAMP_INCREMENT_SHIFT);
412
413 /* 3rd: wait ((period - 2 * ramp_time) / 2) (15.6ms steps) */
414 program[opcode++] =
415 (1 << LED_PGRM_RAMP_PRESCALE_SHIFT) |
416 (((wait_time * 10) / 156) << LED_PGRM_RAMP_STEP_SHIFT) |
417 (0 << LED_PGRM_RAMP_INCREMENT_SHIFT);
418
419 /* ramp down is same as ramp up with sign bit set */
420 /* 4th command is ramp down 1/2 way */
421 program[opcode++] =
422 (0 << LED_PGRM_RAMP_PRESCALE_SHIFT) |
423 (1 << LED_PGRM_RAMP_STEP_SHIFT) |
424 (1 << LED_PGRM_RAMP_SIGN_SHIFT) |
425 (127 << LED_PGRM_RAMP_INCREMENT_SHIFT);
426
427 /* 5th command is ramp down rest of the way */
428 program[opcode++] =
429 (0 << LED_PGRM_RAMP_PRESCALE_SHIFT) |
430 (1 << LED_PGRM_RAMP_STEP_SHIFT) |
431 (1 << LED_PGRM_RAMP_SIGN_SHIFT) |
432 (127 << LED_PGRM_RAMP_INCREMENT_SHIFT);
433
434 /* 6th: wait ((period - 2 * ramp_time) / 2) (15.6ms steps) */
435 program[opcode++] =
436 (1 << LED_PGRM_RAMP_PRESCALE_SHIFT) |
437 (((wait_time * 10) / 156) << LED_PGRM_RAMP_STEP_SHIFT) |
438 (0 << LED_PGRM_RAMP_INCREMENT_SHIFT);
439
440 /* 7th command: repeat */
441 program[opcode++] = 0x00;
442 }
443
444 return lp5562_led_program_engine(dev_get_parent(dev), program,
445 opcode, priv->enginenum);
446}
447#endif
448
449static const struct led_ops lp5562_led_ops = {
450 .get_state = lp5562_led_get_state,
451 .set_state = lp5562_led_set_state,
452#ifdef CONFIG_LED_BLINK
453 .set_period = lp5562_led_set_period,
454#endif
455};
456
457static int lp5562_led_probe(struct udevice *dev)
458{
459 struct lp5562_led_priv *priv = dev_get_priv(dev);
460 u8 current;
461 int ret = 0;
462
463 /* Child LED nodes */
464 switch (dev_read_addr(dev)) {
465 case 0:
466 priv->reg_current = REG_R_CUR;
467 priv->reg_pwm = REG_R_PWM;
468 priv->map_shift = REG_LED_MAP_R_ENG_SHIFT;
469 priv->enginenum = 1;
470 break;
471 case 1:
472 priv->reg_current = REG_G_CUR;
473 priv->reg_pwm = REG_G_PWM;
474 priv->map_shift = REG_LED_MAP_G_ENG_SHIFT;
475 priv->enginenum = 2;
476 break;
477 case 2:
478 priv->reg_current = REG_B_CUR;
479 priv->reg_pwm = REG_B_PWM;
480 priv->map_shift = REG_LED_MAP_B_ENG_SHIFT;
481 priv->enginenum = 3; /* shared with white */
482 break;
483 case 3:
484 priv->reg_current = REG_W_CUR;
485 priv->map_shift = REG_LED_MAP_W_ENG_SHIFT;
486 priv->enginenum = 3; /* shared with blue */
487 break;
488 default:
489 return -EINVAL;
490 }
491
492 current = dev_read_u8_default(dev, "max-cur", DEFAULT_CURRENT);
493
494 ret = lp5562_led_reg_update(dev_get_parent(dev), priv->reg_current,
495 current, 0xff);
496
497 return ret;
498}
499
500static int lp5562_led_bind(struct udevice *dev)
501{
502 struct led_uc_plat *uc_plat = dev_get_uclass_plat(dev);
503
504 /*
505 * For the child nodes, parse a "chan-name" property, since
506 * the DT bindings for this device use that instead of
507 * "label".
508 */
509 uc_plat->label = dev_read_string(dev, "chan-name");
510
511 return 0;
512}
513
514U_BOOT_DRIVER(lp5562_led) = {
515 .name = "lp5562-led",
516 .id = UCLASS_LED,
517 .bind = lp5562_led_bind,
518 .probe = lp5562_led_probe,
519 .priv_auto = sizeof(struct lp5562_led_priv),
520 .ops = &lp5562_led_ops,
521};
522
Doug Zobel72b5a882023-11-17 12:38:11 +0100523static int lp5562_led_wrap_probe(struct udevice *dev)
524{
525 struct lp5562_led_wrap_priv *priv = dev_get_priv(dev);
526 u8 clock_mode;
527 int ret;
528
529 /* Enable gpio if needed */
530 if (gpio_request_by_name(dev, "enabled-gpios", 0,
531 &priv->enable_gpio, GPIOD_IS_OUT) == 0) {
532 dm_gpio_set_value(&priv->enable_gpio, 1);
533 udelay(1000);
534 }
535
536 /* Ensure all registers have default values. */
537 ret = lp5562_led_reg_update(dev, REG_RESET, REG_RESET_RESET, 0xff);
538 if (ret)
539 return ret;
540 udelay(10000);
541
542 /* Enable the chip */
543 ret = lp5562_led_reg_update(dev, REG_ENABLE, REG_ENABLE_CHIP_ENABLE, 0xff);
544 if (ret)
545 return ret;
546
547 /*
548 * The DT bindings say 0=auto, 1=internal, 2=external, while
549 * the register[0:1] values are 0=external, 1=internal,
550 * 2=auto.
551 */
552 clock_mode = dev_read_u8_default(dev, "clock-mode", 0);
553 ret = lp5562_led_reg_update(dev, REG_CONFIG, 2 - clock_mode, REG_CONFIG_CLK_MASK);
554
555 return ret;
556}
557
558static int lp5562_led_wrap_bind(struct udevice *dev)
559{
560 return led_bind_generic(dev, "lp5562-led");
561}
562
563static const struct udevice_id lp5562_led_ids[] = {
564 { .compatible = "ti,lp5562" },
565 { /* sentinel */ }
566};
567
568U_BOOT_DRIVER(lp5562_led_wrap) = {
569 .name = "lp5562-led-wrap",
570 .id = UCLASS_NOP,
571 .of_match = lp5562_led_ids,
572 .bind = lp5562_led_wrap_bind,
573 .probe = lp5562_led_wrap_probe,
574 .priv_auto = sizeof(struct lp5562_led_wrap_priv),
575};