xref: /openbmc/u-boot/drivers/misc/pca9551_led.c (revision baefb63a)
1 /*
2  * Copyright (C) 2015 Stefan Roese <sr@denx.de>
3  *
4  * SPDX-License-Identifier:	GPL-2.0+
5  */
6 
7 #include <common.h>
8 #include <errno.h>
9 #include <i2c.h>
10 
11 #ifndef CONFIG_PCA9551_I2C_ADDR
12 #error "CONFIG_PCA9551_I2C_ADDR not defined!"
13 #endif
14 
15 #define PCA9551_REG_INPUT	0x00	/* Input register (read only) */
16 #define PCA9551_REG_PSC0	0x01	/* Frequency prescaler 0 */
17 #define PCA9551_REG_PWM0	0x02	/* PWM0 */
18 #define PCA9551_REG_PSC1	0x03	/* Frequency prescaler 1 */
19 #define PCA9551_REG_PWM1	0x04	/* PWM1 */
20 #define PCA9551_REG_LS0		0x05	/* LED0 to LED3 selector */
21 #define PCA9551_REG_LS1		0x06	/* LED4 to LED7 selector */
22 
23 #define PCA9551_CTRL_AI		(1 << 4)	/* Auto-increment flag */
24 
25 #define PCA9551_LED_STATE_ON		0x00
26 #define PCA9551_LED_STATE_OFF		0x01
27 #define PCA9551_LED_STATE_BLINK0	0x02
28 #define PCA9551_LED_STATE_BLINK1	0x03
29 
30 struct pca9551_blink_rate {
31 	u8 psc;	/* Frequency preescaler, see PCA9551_7.pdf p. 6 */
32 	u8 pwm;	/* Pulse width modulation, see PCA9551_7.pdf p. 6 */
33 };
34 
35 static int freq_last = -1;
36 static int mask_last = -1;
37 static int idx_last = -1;
38 static int mode_last;
39 
40 static int pca9551_led_get_state(int led, int *state)
41 {
42 	unsigned int reg;
43 	u8 shift, buf;
44 	int ret;
45 
46 	if (led < 0 || led > 7) {
47 		return -EINVAL;
48 	} else if (led < 4) {
49 		reg = PCA9551_REG_LS0;
50 		shift = led << 1;
51 	} else {
52 		reg = PCA9551_REG_LS1;
53 		shift = (led - 4) << 1;
54 	}
55 
56 	ret = i2c_read(CONFIG_PCA9551_I2C_ADDR, reg, 1, &buf, 1);
57 	if (ret)
58 		return ret;
59 
60 	*state = (buf >> shift) & 0x03;
61 	return 0;
62 }
63 
64 static int pca9551_led_set_state(int led, int state)
65 {
66 	unsigned int reg;
67 	u8 shift, buf, mask;
68 	int ret;
69 
70 	if (led < 0 || led > 7) {
71 		return -EINVAL;
72 	} else if (led < 4) {
73 		reg = PCA9551_REG_LS0;
74 		shift = led << 1;
75 	} else {
76 		reg = PCA9551_REG_LS1;
77 		shift = (led - 4) << 1;
78 	}
79 	mask = 0x03 << shift;
80 
81 	ret = i2c_read(CONFIG_PCA9551_I2C_ADDR, reg, 1, &buf, 1);
82 	if (ret)
83 		return ret;
84 
85 	buf = (buf & ~mask) | ((state & 0x03) << shift);
86 
87 	ret = i2c_write(CONFIG_PCA9551_I2C_ADDR, reg, 1, &buf, 1);
88 	if (ret)
89 		return ret;
90 
91 	return 0;
92 }
93 
94 static int pca9551_led_set_blink_rate(int idx, struct pca9551_blink_rate rate)
95 {
96 	unsigned int reg;
97 	int ret;
98 
99 	switch (idx) {
100 	case 0:
101 		reg = PCA9551_REG_PSC0;
102 		break;
103 	case 1:
104 		reg = PCA9551_REG_PSC1;
105 		break;
106 	default:
107 		return -EINVAL;
108 	}
109 	reg |= PCA9551_CTRL_AI;
110 
111 	ret = i2c_write(CONFIG_PCA9551_I2C_ADDR, reg, 1, (u8 *)&rate, 2);
112 	if (ret)
113 		return ret;
114 
115 	return 0;
116 }
117 
118 /*
119  * Functions referenced by cmd_led.c or status_led.c
120  */
121 void __led_init(led_id_t id, int state)
122 {
123 }
124 
125 void __led_set(led_id_t mask, int state)
126 {
127 	if (state == CONFIG_LED_STATUS_OFF)
128 		pca9551_led_set_state(mask, PCA9551_LED_STATE_OFF);
129 	else
130 		pca9551_led_set_state(mask, PCA9551_LED_STATE_ON);
131 }
132 
133 void __led_toggle(led_id_t mask)
134 {
135 	int state = 0;
136 
137 	pca9551_led_get_state(mask, &state);
138 	pca9551_led_set_state(mask, !state);
139 }
140 
141 void __led_blink(led_id_t mask, int freq)
142 {
143 	struct pca9551_blink_rate rate;
144 	int mode;
145 	int idx;
146 
147 	if ((freq == freq_last) || (mask == mask_last)) {
148 		idx = idx_last;
149 		mode = mode_last;
150 	} else {
151 		/* Toggle blink index */
152 		if (idx_last == 0) {
153 			idx = 1;
154 			mode = PCA9551_LED_STATE_BLINK1;
155 		} else {
156 			idx = 0;
157 			mode = PCA9551_LED_STATE_BLINK0;
158 		}
159 
160 		idx_last = idx;
161 		mode_last = mode;
162 	}
163 	freq_last = freq;
164 	mask_last = mask;
165 
166 	rate.psc = ((freq * 38) / 1000) - 1;
167 	rate.pwm = 128;		/* 50% duty cycle */
168 
169 	pca9551_led_set_blink_rate(idx, rate);
170 	pca9551_led_set_state(mask, mode);
171 }
172