xref: /openbmc/u-boot/drivers/misc/pca9551_led.c (revision 35546f6f2014282cc4f9772324b5588bd44a2938)
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 == STATUS_LED_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