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