]>
Commit | Line | Data |
---|---|---|
f9dc67d1 DZ |
1 | // SPDX-License-Identifier: GPL-2.0+ |
2 | /* | |
3 | * Copyright (C) 2018 Doug Zobel <[email protected]> | |
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 | ||
87 | struct lp5562_led_wrap_priv { | |
88 | struct gpio_desc enable_gpio; | |
89 | }; | |
90 | ||
91 | struct 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 */ | |
99 | enum 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 | */ | |
115 | static 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 | ||
f9dc67d1 DZ |
125 | /* |
126 | * Data sheet says "Delay between consecutive I2C writes to | |
81f3a665 | 127 | * ENABLE register (00h) need to be longer than 488 us |
f9dc67d1 | 128 | * (typical)." and "Delay between consecutive I2C writes to |
81f3a665 | 129 | * OP_MODE register need to be longer than 153 us (typ)." |
f9dc67d1 DZ |
130 | * |
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 | */ | |
154 | static 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 | ||
207 | done: | |
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 | */ | |
215 | static 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 | */ | |
234 | static 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 | */ | |
249 | static 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 | */ | |
265 | static 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 | */ | |
282 | static 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 | */ | |
302 | static 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 | */ | |
337 | static 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 | ||
449 | static 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 | ||
457 | static 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 | ||
500 | static 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 | ||
514 | U_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 | ||
f9dc67d1 DZ |
523 | static 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 | ||
558 | static int lp5562_led_wrap_bind(struct udevice *dev) | |
559 | { | |
560 | return led_bind_generic(dev, "lp5562-led"); | |
561 | } | |
562 | ||
563 | static const struct udevice_id lp5562_led_ids[] = { | |
564 | { .compatible = "ti,lp5562" }, | |
565 | { /* sentinel */ } | |
566 | }; | |
567 | ||
568 | U_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 | }; |