blob: 431d7e10ab74e2aba8f4964bfe641889315b0998 [file] [log] [blame]
Doug Zobelf9dc67d2023-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
125
126 /*
127 * Data sheet says "Delay between consecutive I2C writes to
128 * ENABLE register (00h) need to be longer than 488 μs
129 * (typical)." and "Delay between consecutive I2C writes to
130 * OP_MODE register need to be longer than 153 μs (typ)."
131 *
132 * The linux driver does usleep_range(500, 600) and
133 * usleep_range(200, 300), respectively.
134 */
135 switch (regnum) {
136 case REG_ENABLE:
137 udelay(600);
138 break;
139 case REG_OP_MODE:
140 udelay(300);
141 break;
142 }
143
144 return ret;
145}
146
147#ifdef CONFIG_LED_BLINK
148/*
149 * Program the lp5562 engine
150 * dev - I2C udevice (parent of led)
151 * program - array of commands
152 * size - number of commands in program array (1-16)
153 * engine - engine number (1-3)
154 */
155static int lp5562_led_program_engine(struct udevice *dev, u16 *program,
156 u8 size, u8 engine)
157{
158 int ret, cmd;
159 u8 engine_reg = REG_ENG1_MEM_BEGIN +
160 ((engine - 1) * REG_ENGINE_MEM_SIZE);
161 u8 shift = (3 - engine) * 2;
162 __be16 prog_be[16];
163
164 if (size < 1 || size > 16 || engine < 1 || engine > 3)
165 return -EINVAL;
166
167 for (cmd = 0; cmd < size; cmd++)
168 prog_be[cmd] = cpu_to_be16(program[cmd]);
169
170 /* set engine mode to 'disabled' */
171 ret = lp5562_led_reg_update(dev, REG_OP_MODE,
172 REG_OP_MODE_DISABLED << shift,
173 REG_OP_MODE_MASK << shift);
174 if (ret != 0)
175 goto done;
176
177 /* set exec mode to 'hold' */
178 ret = lp5562_led_reg_update(dev, REG_ENABLE,
179 REG_ENABLE_ENG_EXEC_HOLD << shift,
180 REG_ENABLE_ENG_EXEC_MASK << shift);
181 if (ret != 0)
182 goto done;
183
184 /* set engine mode to 'load SRAM' */
185 ret = lp5562_led_reg_update(dev, REG_OP_MODE,
186 REG_OP_MODE_LOAD_SRAM << shift,
187 REG_OP_MODE_MASK << shift);
188 if (ret != 0)
189 goto done;
190
191 /* send the re-ordered program sequence */
192 ret = dm_i2c_write(dev, engine_reg, (uchar *)prog_be, sizeof(u16) * size);
193 if (ret != 0)
194 goto done;
195
196 /* set engine mode to 'run' */
197 ret = lp5562_led_reg_update(dev, REG_OP_MODE,
198 REG_OP_MODE_RUN << shift,
199 REG_OP_MODE_MASK << shift);
200 if (ret != 0)
201 goto done;
202
203 /* set engine exec to 'run' */
204 ret = lp5562_led_reg_update(dev, REG_ENABLE,
205 REG_ENABLE_ENG_EXEC_RUN << shift,
206 REG_ENABLE_ENG_EXEC_MASK << shift);
207
208done:
209 return ret;
210}
211
212/*
213 * Get the LED's current control mode (I2C or ENGINE[1-3])
214 * dev - led udevice (child udevice)
215 */
216static enum lp5562_led_ctl_mode lp5562_led_get_control_mode(struct udevice *dev)
217{
218 struct lp5562_led_priv *priv = dev_get_priv(dev);
219 u8 data;
220 enum lp5562_led_ctl_mode mode = I2C;
221
222 if (dm_i2c_read(dev_get_parent(dev), REG_LED_MAP, &data, 1) == 0)
223 mode = (data & (REG_LED_MAP_ENG_MASK << priv->map_shift))
224 >> priv->map_shift;
225
226 return mode;
227}
228#endif
229
230/*
231 * Set the LED's control mode to I2C or ENGINE[1-3]
232 * dev - led udevice (child udevice)
233 * mode - mode to change to
234 */
235static int lp5562_led_set_control_mode(struct udevice *dev,
236 enum lp5562_led_ctl_mode mode)
237{
238 struct lp5562_led_priv *priv = dev_get_priv(dev);
239
240 return (lp5562_led_reg_update(dev_get_parent(dev), REG_LED_MAP,
241 mode << priv->map_shift,
242 REG_LED_MAP_ENG_MASK << priv->map_shift));
243}
244
245/*
246 * Return the LED's PWM value; If LED is in BLINK state, then it is
247 * under engine control mode which doesn't use this PWM value.
248 * dev - led udevice (child udevice)
249 */
250static int lp5562_led_get_pwm(struct udevice *dev)
251{
252 struct lp5562_led_priv *priv = dev_get_priv(dev);
253 u8 data;
254
255 if (dm_i2c_read(dev_get_parent(dev), priv->reg_pwm, &data, 1) != 0)
256 return -EINVAL;
257
258 return data;
259}
260
261/*
262 * Set the LED's PWM value and configure it to use this (I2C mode).
263 * dev - led udevice (child udevice)
264 * value - PWM value (0 - 255)
265 */
266static int lp5562_led_set_pwm(struct udevice *dev, u8 value)
267{
268 struct lp5562_led_priv *priv = dev_get_priv(dev);
269
270 if (lp5562_led_reg_update(dev_get_parent(dev), priv->reg_pwm,
271 value, 0xff) != 0)
272 return -EINVAL;
273
274 /* set LED to I2C register mode */
275 return lp5562_led_set_control_mode(dev, I2C);
276}
277
278/*
279 * Return the led's current state
280 * dev - led udevice (child udevice)
281 *
282 */
283static enum led_state_t lp5562_led_get_state(struct udevice *dev)
284{
285 enum led_state_t state = LEDST_ON;
286
287 if (lp5562_led_get_pwm(dev) == REG_PWM_MIN_VALUE)
288 state = LEDST_OFF;
289
290#ifdef CONFIG_LED_BLINK
291 if (lp5562_led_get_control_mode(dev) != I2C)
292 state = LEDST_BLINK;
293#endif
294
295 return state;
296}
297
298/*
299 * Set the led state
300 * dev - led udevice (child udevice)
301 * state - State to set the LED to
302 */
303static int lp5562_led_set_state(struct udevice *dev, enum led_state_t state)
304{
305#ifdef CONFIG_LED_BLINK
306 struct lp5562_led_priv *priv = dev_get_priv(dev);
307#endif
308
309 switch (state) {
310 case LEDST_OFF:
311 return lp5562_led_set_pwm(dev, REG_PWM_MIN_VALUE);
312 case LEDST_ON:
313 return lp5562_led_set_pwm(dev, REG_PWM_MAX_VALUE);
314#ifdef CONFIG_LED_BLINK
315 case LEDST_BLINK:
316 return lp5562_led_set_control_mode(dev, priv->enginenum);
317#endif
318 case LEDST_TOGGLE:
319 if (lp5562_led_get_state(dev) == LEDST_OFF)
320 return lp5562_led_set_state(dev, LEDST_ON);
321 else
322 return lp5562_led_set_state(dev, LEDST_OFF);
323 break;
324 default:
325 return -EINVAL;
326 }
327
328 return 0;
329}
330
331#ifdef CONFIG_LED_BLINK
332/*
333 * Set the blink period of an LED; note blue and white share the same
334 * engine so changing the period of one affects the other.
335 * dev - led udevice (child udevice)
336 * period_ms - blink period in ms
337 */
338static int lp5562_led_set_period(struct udevice *dev, int period_ms)
339{
340 struct lp5562_led_priv *priv = dev_get_priv(dev);
341 u8 opcode = 0;
342 u16 program[7];
343 u16 wait_time;
344
345 /* Blink is implemented as an engine program. Simple on/off
346 * for short periods, or fade in/fade out for longer periods:
347 *
348 * if (period_ms < 500):
349 * set PWM to 100%
350 * pause for period / 2
351 * set PWM to 0%
352 * pause for period / 2
353 * goto start
354 *
355 * else
356 * raise PWM 0% -> 50% in 62.7 ms
357 * raise PWM 50% -> 100% in 62.7 ms
358 * pause for (period - 4 * 62.7) / 2
359 * lower PWM 100% -> 50% in 62.7 ms
360 * lower PWM 50% -> 0% in 62.7 ms
361 * pause for (period - 4 * 62.7) / 2
362 * goto start
363 */
364
365 if (period_ms < MIN_BLINK_PERIOD)
366 period_ms = MIN_BLINK_PERIOD;
367 else if (period_ms > MAX_BLINK_PERIOD)
368 period_ms = MAX_BLINK_PERIOD;
369
370 if (period_ms < 500) {
371 /* Simple on/off blink */
372 wait_time = period_ms / 2;
373
374 /* 1st command is full brightness */
375 program[opcode++] =
376 (1 << LED_PGRM_RAMP_PRESCALE_SHIFT) |
377 REG_PWM_MAX_VALUE;
378
379 /* 2nd command is wait (period / 2) using 15.6ms steps */
380 program[opcode++] =
381 (1 << LED_PGRM_RAMP_PRESCALE_SHIFT) |
382 (((wait_time * 10) / 156) << LED_PGRM_RAMP_STEP_SHIFT) |
383 (0 << LED_PGRM_RAMP_INCREMENT_SHIFT);
384
385 /* 3rd command is 0% brightness */
386 program[opcode++] =
387 (1 << LED_PGRM_RAMP_PRESCALE_SHIFT);
388
389 /* 4th command is wait (period / 2) using 15.6ms steps */
390 program[opcode++] =
391 (1 << LED_PGRM_RAMP_PRESCALE_SHIFT) |
392 (((wait_time * 10) / 156) << LED_PGRM_RAMP_STEP_SHIFT) |
393 (0 << LED_PGRM_RAMP_INCREMENT_SHIFT);
394
395 /* 5th command: repeat */
396 program[opcode++] = 0x00;
397 } else {
398 /* fade-in / fade-out blink */
399 wait_time = ((period_ms - 251) / 2);
400
401 /* ramp up time is 256 * 0.49ms (125.4ms) done in 2 steps */
402 /* 1st command is ramp up 1/2 way */
403 program[opcode++] =
404 (0 << LED_PGRM_RAMP_PRESCALE_SHIFT) |
405 (1 << LED_PGRM_RAMP_STEP_SHIFT) |
406 (127 << LED_PGRM_RAMP_INCREMENT_SHIFT);
407
408 /* 2nd command is ramp up rest of the way */
409 program[opcode++] =
410 (0 << LED_PGRM_RAMP_PRESCALE_SHIFT) |
411 (1 << LED_PGRM_RAMP_STEP_SHIFT) |
412 (127 << LED_PGRM_RAMP_INCREMENT_SHIFT);
413
414 /* 3rd: wait ((period - 2 * ramp_time) / 2) (15.6ms steps) */
415 program[opcode++] =
416 (1 << LED_PGRM_RAMP_PRESCALE_SHIFT) |
417 (((wait_time * 10) / 156) << LED_PGRM_RAMP_STEP_SHIFT) |
418 (0 << LED_PGRM_RAMP_INCREMENT_SHIFT);
419
420 /* ramp down is same as ramp up with sign bit set */
421 /* 4th command is ramp down 1/2 way */
422 program[opcode++] =
423 (0 << LED_PGRM_RAMP_PRESCALE_SHIFT) |
424 (1 << LED_PGRM_RAMP_STEP_SHIFT) |
425 (1 << LED_PGRM_RAMP_SIGN_SHIFT) |
426 (127 << LED_PGRM_RAMP_INCREMENT_SHIFT);
427
428 /* 5th command is ramp down rest of the way */
429 program[opcode++] =
430 (0 << LED_PGRM_RAMP_PRESCALE_SHIFT) |
431 (1 << LED_PGRM_RAMP_STEP_SHIFT) |
432 (1 << LED_PGRM_RAMP_SIGN_SHIFT) |
433 (127 << LED_PGRM_RAMP_INCREMENT_SHIFT);
434
435 /* 6th: wait ((period - 2 * ramp_time) / 2) (15.6ms steps) */
436 program[opcode++] =
437 (1 << LED_PGRM_RAMP_PRESCALE_SHIFT) |
438 (((wait_time * 10) / 156) << LED_PGRM_RAMP_STEP_SHIFT) |
439 (0 << LED_PGRM_RAMP_INCREMENT_SHIFT);
440
441 /* 7th command: repeat */
442 program[opcode++] = 0x00;
443 }
444
445 return lp5562_led_program_engine(dev_get_parent(dev), program,
446 opcode, priv->enginenum);
447}
448#endif
449
450static const struct led_ops lp5562_led_ops = {
451 .get_state = lp5562_led_get_state,
452 .set_state = lp5562_led_set_state,
453#ifdef CONFIG_LED_BLINK
454 .set_period = lp5562_led_set_period,
455#endif
456};
457
458static int lp5562_led_probe(struct udevice *dev)
459{
460 struct lp5562_led_priv *priv = dev_get_priv(dev);
461 u8 current;
462 int ret = 0;
463
464 /* Child LED nodes */
465 switch (dev_read_addr(dev)) {
466 case 0:
467 priv->reg_current = REG_R_CUR;
468 priv->reg_pwm = REG_R_PWM;
469 priv->map_shift = REG_LED_MAP_R_ENG_SHIFT;
470 priv->enginenum = 1;
471 break;
472 case 1:
473 priv->reg_current = REG_G_CUR;
474 priv->reg_pwm = REG_G_PWM;
475 priv->map_shift = REG_LED_MAP_G_ENG_SHIFT;
476 priv->enginenum = 2;
477 break;
478 case 2:
479 priv->reg_current = REG_B_CUR;
480 priv->reg_pwm = REG_B_PWM;
481 priv->map_shift = REG_LED_MAP_B_ENG_SHIFT;
482 priv->enginenum = 3; /* shared with white */
483 break;
484 case 3:
485 priv->reg_current = REG_W_CUR;
486 priv->map_shift = REG_LED_MAP_W_ENG_SHIFT;
487 priv->enginenum = 3; /* shared with blue */
488 break;
489 default:
490 return -EINVAL;
491 }
492
493 current = dev_read_u8_default(dev, "max-cur", DEFAULT_CURRENT);
494
495 ret = lp5562_led_reg_update(dev_get_parent(dev), priv->reg_current,
496 current, 0xff);
497
498 return ret;
499}
500
501static int lp5562_led_bind(struct udevice *dev)
502{
503 struct led_uc_plat *uc_plat = dev_get_uclass_plat(dev);
504
505 /*
506 * For the child nodes, parse a "chan-name" property, since
507 * the DT bindings for this device use that instead of
508 * "label".
509 */
510 uc_plat->label = dev_read_string(dev, "chan-name");
511
512 return 0;
513}
514
515U_BOOT_DRIVER(lp5562_led) = {
516 .name = "lp5562-led",
517 .id = UCLASS_LED,
518 .bind = lp5562_led_bind,
519 .probe = lp5562_led_probe,
520 .priv_auto = sizeof(struct lp5562_led_priv),
521 .ops = &lp5562_led_ops,
522};
523
524
525static int lp5562_led_wrap_probe(struct udevice *dev)
526{
527 struct lp5562_led_wrap_priv *priv = dev_get_priv(dev);
528 u8 clock_mode;
529 int ret;
530
531 /* Enable gpio if needed */
532 if (gpio_request_by_name(dev, "enabled-gpios", 0,
533 &priv->enable_gpio, GPIOD_IS_OUT) == 0) {
534 dm_gpio_set_value(&priv->enable_gpio, 1);
535 udelay(1000);
536 }
537
538 /* Ensure all registers have default values. */
539 ret = lp5562_led_reg_update(dev, REG_RESET, REG_RESET_RESET, 0xff);
540 if (ret)
541 return ret;
542 udelay(10000);
543
544 /* Enable the chip */
545 ret = lp5562_led_reg_update(dev, REG_ENABLE, REG_ENABLE_CHIP_ENABLE, 0xff);
546 if (ret)
547 return ret;
548
549 /*
550 * The DT bindings say 0=auto, 1=internal, 2=external, while
551 * the register[0:1] values are 0=external, 1=internal,
552 * 2=auto.
553 */
554 clock_mode = dev_read_u8_default(dev, "clock-mode", 0);
555 ret = lp5562_led_reg_update(dev, REG_CONFIG, 2 - clock_mode, REG_CONFIG_CLK_MASK);
556
557 return ret;
558}
559
560static int lp5562_led_wrap_bind(struct udevice *dev)
561{
562 return led_bind_generic(dev, "lp5562-led");
563}
564
565static const struct udevice_id lp5562_led_ids[] = {
566 { .compatible = "ti,lp5562" },
567 { /* sentinel */ }
568};
569
570U_BOOT_DRIVER(lp5562_led_wrap) = {
571 .name = "lp5562-led-wrap",
572 .id = UCLASS_NOP,
573 .of_match = lp5562_led_ids,
574 .bind = lp5562_led_wrap_bind,
575 .probe = lp5562_led_wrap_probe,
576 .priv_auto = sizeof(struct lp5562_led_wrap_priv),
577};