xref: /rk3399_rockchip-uboot/drivers/misc/pca9551_led.c (revision 2d8d190c8394b43c0989cdb04a50cb48d4e1f8da)
11cdd9412SStefan Roese /*
21cdd9412SStefan Roese  * Copyright (C) 2015 Stefan Roese <sr@denx.de>
31cdd9412SStefan Roese  *
41cdd9412SStefan Roese  * SPDX-License-Identifier:	GPL-2.0+
51cdd9412SStefan Roese  */
61cdd9412SStefan Roese 
71cdd9412SStefan Roese #include <common.h>
81cdd9412SStefan Roese #include <errno.h>
91cdd9412SStefan Roese #include <i2c.h>
101cdd9412SStefan Roese 
111cdd9412SStefan Roese #ifndef CONFIG_PCA9551_I2C_ADDR
121cdd9412SStefan Roese #error "CONFIG_PCA9551_I2C_ADDR not defined!"
131cdd9412SStefan Roese #endif
141cdd9412SStefan Roese 
151cdd9412SStefan Roese #define PCA9551_REG_INPUT	0x00	/* Input register (read only) */
161cdd9412SStefan Roese #define PCA9551_REG_PSC0	0x01	/* Frequency prescaler 0 */
171cdd9412SStefan Roese #define PCA9551_REG_PWM0	0x02	/* PWM0 */
181cdd9412SStefan Roese #define PCA9551_REG_PSC1	0x03	/* Frequency prescaler 1 */
191cdd9412SStefan Roese #define PCA9551_REG_PWM1	0x04	/* PWM1 */
201cdd9412SStefan Roese #define PCA9551_REG_LS0		0x05	/* LED0 to LED3 selector */
211cdd9412SStefan Roese #define PCA9551_REG_LS1		0x06	/* LED4 to LED7 selector */
221cdd9412SStefan Roese 
231cdd9412SStefan Roese #define PCA9551_CTRL_AI		(1 << 4)	/* Auto-increment flag */
241cdd9412SStefan Roese 
251cdd9412SStefan Roese #define PCA9551_LED_STATE_ON		0x00
261cdd9412SStefan Roese #define PCA9551_LED_STATE_OFF		0x01
271cdd9412SStefan Roese #define PCA9551_LED_STATE_BLINK0	0x02
281cdd9412SStefan Roese #define PCA9551_LED_STATE_BLINK1	0x03
291cdd9412SStefan Roese 
301cdd9412SStefan Roese struct pca9551_blink_rate {
311cdd9412SStefan Roese 	u8 psc;	/* Frequency preescaler, see PCA9551_7.pdf p. 6 */
321cdd9412SStefan Roese 	u8 pwm;	/* Pulse width modulation, see PCA9551_7.pdf p. 6 */
331cdd9412SStefan Roese };
341cdd9412SStefan Roese 
35f5df36d0SStefan Roese static int freq_last = -1;
36f5df36d0SStefan Roese static int mask_last = -1;
37f5df36d0SStefan Roese static int idx_last = -1;
38f5df36d0SStefan Roese static int mode_last;
391cdd9412SStefan Roese 
pca9551_led_get_state(int led,int * state)401cdd9412SStefan Roese static int pca9551_led_get_state(int led, int *state)
411cdd9412SStefan Roese {
421cdd9412SStefan Roese 	unsigned int reg;
431cdd9412SStefan Roese 	u8 shift, buf;
441cdd9412SStefan Roese 	int ret;
451cdd9412SStefan Roese 
461cdd9412SStefan Roese 	if (led < 0 || led > 7) {
471cdd9412SStefan Roese 		return -EINVAL;
481cdd9412SStefan Roese 	} else if (led < 4) {
491cdd9412SStefan Roese 		reg = PCA9551_REG_LS0;
501cdd9412SStefan Roese 		shift = led << 1;
511cdd9412SStefan Roese 	} else {
521cdd9412SStefan Roese 		reg = PCA9551_REG_LS1;
531cdd9412SStefan Roese 		shift = (led - 4) << 1;
541cdd9412SStefan Roese 	}
551cdd9412SStefan Roese 
561cdd9412SStefan Roese 	ret = i2c_read(CONFIG_PCA9551_I2C_ADDR, reg, 1, &buf, 1);
571cdd9412SStefan Roese 	if (ret)
581cdd9412SStefan Roese 		return ret;
591cdd9412SStefan Roese 
601cdd9412SStefan Roese 	*state = (buf >> shift) & 0x03;
611cdd9412SStefan Roese 	return 0;
621cdd9412SStefan Roese }
631cdd9412SStefan Roese 
pca9551_led_set_state(int led,int state)641cdd9412SStefan Roese static int pca9551_led_set_state(int led, int state)
651cdd9412SStefan Roese {
661cdd9412SStefan Roese 	unsigned int reg;
671cdd9412SStefan Roese 	u8 shift, buf, mask;
681cdd9412SStefan Roese 	int ret;
691cdd9412SStefan Roese 
701cdd9412SStefan Roese 	if (led < 0 || led > 7) {
711cdd9412SStefan Roese 		return -EINVAL;
721cdd9412SStefan Roese 	} else if (led < 4) {
731cdd9412SStefan Roese 		reg = PCA9551_REG_LS0;
741cdd9412SStefan Roese 		shift = led << 1;
751cdd9412SStefan Roese 	} else {
761cdd9412SStefan Roese 		reg = PCA9551_REG_LS1;
771cdd9412SStefan Roese 		shift = (led - 4) << 1;
781cdd9412SStefan Roese 	}
791cdd9412SStefan Roese 	mask = 0x03 << shift;
801cdd9412SStefan Roese 
811cdd9412SStefan Roese 	ret = i2c_read(CONFIG_PCA9551_I2C_ADDR, reg, 1, &buf, 1);
821cdd9412SStefan Roese 	if (ret)
831cdd9412SStefan Roese 		return ret;
841cdd9412SStefan Roese 
851cdd9412SStefan Roese 	buf = (buf & ~mask) | ((state & 0x03) << shift);
861cdd9412SStefan Roese 
871cdd9412SStefan Roese 	ret = i2c_write(CONFIG_PCA9551_I2C_ADDR, reg, 1, &buf, 1);
881cdd9412SStefan Roese 	if (ret)
891cdd9412SStefan Roese 		return ret;
901cdd9412SStefan Roese 
911cdd9412SStefan Roese 	return 0;
921cdd9412SStefan Roese }
931cdd9412SStefan Roese 
pca9551_led_set_blink_rate(int idx,struct pca9551_blink_rate rate)941cdd9412SStefan Roese static int pca9551_led_set_blink_rate(int idx, struct pca9551_blink_rate rate)
951cdd9412SStefan Roese {
961cdd9412SStefan Roese 	unsigned int reg;
971cdd9412SStefan Roese 	int ret;
981cdd9412SStefan Roese 
991cdd9412SStefan Roese 	switch (idx) {
1001cdd9412SStefan Roese 	case 0:
1011cdd9412SStefan Roese 		reg = PCA9551_REG_PSC0;
1021cdd9412SStefan Roese 		break;
1031cdd9412SStefan Roese 	case 1:
1041cdd9412SStefan Roese 		reg = PCA9551_REG_PSC1;
1051cdd9412SStefan Roese 		break;
1061cdd9412SStefan Roese 	default:
1071cdd9412SStefan Roese 		return -EINVAL;
1081cdd9412SStefan Roese 	}
1091cdd9412SStefan Roese 	reg |= PCA9551_CTRL_AI;
1101cdd9412SStefan Roese 
1111cdd9412SStefan Roese 	ret = i2c_write(CONFIG_PCA9551_I2C_ADDR, reg, 1, (u8 *)&rate, 2);
1121cdd9412SStefan Roese 	if (ret)
1131cdd9412SStefan Roese 		return ret;
1141cdd9412SStefan Roese 
1151cdd9412SStefan Roese 	return 0;
1161cdd9412SStefan Roese }
1171cdd9412SStefan Roese 
1181cdd9412SStefan Roese /*
11913cfbe51SBernhard Nortmann  * Functions referenced by cmd_led.c or status_led.c
1201cdd9412SStefan Roese  */
__led_init(led_id_t id,int state)12113cfbe51SBernhard Nortmann void __led_init(led_id_t id, int state)
12213cfbe51SBernhard Nortmann {
12313cfbe51SBernhard Nortmann }
12413cfbe51SBernhard Nortmann 
__led_set(led_id_t mask,int state)1251cdd9412SStefan Roese void __led_set(led_id_t mask, int state)
1261cdd9412SStefan Roese {
127*2d8d190cSUri Mashiach 	if (state == CONFIG_LED_STATUS_OFF)
1281cdd9412SStefan Roese 		pca9551_led_set_state(mask, PCA9551_LED_STATE_OFF);
1291cdd9412SStefan Roese 	else
1301cdd9412SStefan Roese 		pca9551_led_set_state(mask, PCA9551_LED_STATE_ON);
1311cdd9412SStefan Roese }
1321cdd9412SStefan Roese 
__led_toggle(led_id_t mask)1331cdd9412SStefan Roese void __led_toggle(led_id_t mask)
1341cdd9412SStefan Roese {
1351cdd9412SStefan Roese 	int state = 0;
1361cdd9412SStefan Roese 
1371cdd9412SStefan Roese 	pca9551_led_get_state(mask, &state);
1381cdd9412SStefan Roese 	pca9551_led_set_state(mask, !state);
1391cdd9412SStefan Roese }
1401cdd9412SStefan Roese 
__led_blink(led_id_t mask,int freq)1411cdd9412SStefan Roese void __led_blink(led_id_t mask, int freq)
1421cdd9412SStefan Roese {
1431cdd9412SStefan Roese 	struct pca9551_blink_rate rate;
1441cdd9412SStefan Roese 	int mode;
145f5df36d0SStefan Roese 	int idx;
1461cdd9412SStefan Roese 
147f5df36d0SStefan Roese 	if ((freq == freq_last) || (mask == mask_last)) {
148f5df36d0SStefan Roese 		idx = idx_last;
149f5df36d0SStefan Roese 		mode = mode_last;
1501cdd9412SStefan Roese 	} else {
151f5df36d0SStefan Roese 		/* Toggle blink index */
152f5df36d0SStefan Roese 		if (idx_last == 0) {
153f5df36d0SStefan Roese 			idx = 1;
1541cdd9412SStefan Roese 			mode = PCA9551_LED_STATE_BLINK1;
155f5df36d0SStefan Roese 		} else {
156f5df36d0SStefan Roese 			idx = 0;
157f5df36d0SStefan Roese 			mode = PCA9551_LED_STATE_BLINK0;
1581cdd9412SStefan Roese 		}
1591cdd9412SStefan Roese 
160f5df36d0SStefan Roese 		idx_last = idx;
161f5df36d0SStefan Roese 		mode_last = mode;
162f5df36d0SStefan Roese 	}
163f5df36d0SStefan Roese 	freq_last = freq;
164f5df36d0SStefan Roese 	mask_last = mask;
165f5df36d0SStefan Roese 
1661cdd9412SStefan Roese 	rate.psc = ((freq * 38) / 1000) - 1;
1671cdd9412SStefan Roese 	rate.pwm = 128;		/* 50% duty cycle */
1681cdd9412SStefan Roese 
169f5df36d0SStefan Roese 	pca9551_led_set_blink_rate(idx, rate);
1701cdd9412SStefan Roese 	pca9551_led_set_state(mask, mode);
1711cdd9412SStefan Roese }
172