xref: /openbmc/u-boot/drivers/misc/pca9551_led.c (revision 089fddfd)
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
120  */
121 void __led_set(led_id_t mask, int state)
122 {
123 	if (state == STATUS_LED_OFF)
124 		pca9551_led_set_state(mask, PCA9551_LED_STATE_OFF);
125 	else
126 		pca9551_led_set_state(mask, PCA9551_LED_STATE_ON);
127 }
128 
129 void __led_toggle(led_id_t mask)
130 {
131 	int state = 0;
132 
133 	pca9551_led_get_state(mask, &state);
134 	pca9551_led_set_state(mask, !state);
135 }
136 
137 void __led_blink(led_id_t mask, int freq)
138 {
139 	struct pca9551_blink_rate rate;
140 	int mode;
141 	int idx;
142 
143 	if ((freq == freq_last) || (mask == mask_last)) {
144 		idx = idx_last;
145 		mode = mode_last;
146 	} else {
147 		/* Toggle blink index */
148 		if (idx_last == 0) {
149 			idx = 1;
150 			mode = PCA9551_LED_STATE_BLINK1;
151 		} else {
152 			idx = 0;
153 			mode = PCA9551_LED_STATE_BLINK0;
154 		}
155 
156 		idx_last = idx;
157 		mode_last = mode;
158 	}
159 	freq_last = freq;
160 	mask_last = mask;
161 
162 	rate.psc = ((freq * 38) / 1000) - 1;
163 	rate.pwm = 128;		/* 50% duty cycle */
164 
165 	pca9551_led_set_blink_rate(idx, rate);
166 	pca9551_led_set_state(mask, mode);
167 }
168