xref: /rk3399_rockchip-uboot/drivers/misc/pca9551_led.c (revision f5df36d0c80eafee94d14e33a01f0f693f041a3c)
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 
35*f5df36d0SStefan Roese static int freq_last = -1;
36*f5df36d0SStefan Roese static int mask_last = -1;
37*f5df36d0SStefan Roese static int idx_last = -1;
38*f5df36d0SStefan Roese static int mode_last;
391cdd9412SStefan Roese 
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 
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 
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 /*
1191cdd9412SStefan Roese  * Functions referenced by cmd_led.c
1201cdd9412SStefan Roese  */
1211cdd9412SStefan Roese void __led_set(led_id_t mask, int state)
1221cdd9412SStefan Roese {
1231cdd9412SStefan Roese 	if (state == STATUS_LED_OFF)
1241cdd9412SStefan Roese 		pca9551_led_set_state(mask, PCA9551_LED_STATE_OFF);
1251cdd9412SStefan Roese 	else
1261cdd9412SStefan Roese 		pca9551_led_set_state(mask, PCA9551_LED_STATE_ON);
1271cdd9412SStefan Roese }
1281cdd9412SStefan Roese 
1291cdd9412SStefan Roese void __led_toggle(led_id_t mask)
1301cdd9412SStefan Roese {
1311cdd9412SStefan Roese 	int state = 0;
1321cdd9412SStefan Roese 
1331cdd9412SStefan Roese 	pca9551_led_get_state(mask, &state);
1341cdd9412SStefan Roese 	pca9551_led_set_state(mask, !state);
1351cdd9412SStefan Roese }
1361cdd9412SStefan Roese 
1371cdd9412SStefan Roese void __led_blink(led_id_t mask, int freq)
1381cdd9412SStefan Roese {
1391cdd9412SStefan Roese 	struct pca9551_blink_rate rate;
1401cdd9412SStefan Roese 	int mode;
141*f5df36d0SStefan Roese 	int idx;
1421cdd9412SStefan Roese 
143*f5df36d0SStefan Roese 	if ((freq == freq_last) || (mask == mask_last)) {
144*f5df36d0SStefan Roese 		idx = idx_last;
145*f5df36d0SStefan Roese 		mode = mode_last;
1461cdd9412SStefan Roese 	} else {
147*f5df36d0SStefan Roese 		/* Toggle blink index */
148*f5df36d0SStefan Roese 		if (idx_last == 0) {
149*f5df36d0SStefan Roese 			idx = 1;
1501cdd9412SStefan Roese 			mode = PCA9551_LED_STATE_BLINK1;
151*f5df36d0SStefan Roese 		} else {
152*f5df36d0SStefan Roese 			idx = 0;
153*f5df36d0SStefan Roese 			mode = PCA9551_LED_STATE_BLINK0;
1541cdd9412SStefan Roese 		}
1551cdd9412SStefan Roese 
156*f5df36d0SStefan Roese 		idx_last = idx;
157*f5df36d0SStefan Roese 		mode_last = mode;
158*f5df36d0SStefan Roese 	}
159*f5df36d0SStefan Roese 	freq_last = freq;
160*f5df36d0SStefan Roese 	mask_last = mask;
161*f5df36d0SStefan Roese 
1621cdd9412SStefan Roese 	rate.psc = ((freq * 38) / 1000) - 1;
1631cdd9412SStefan Roese 	rate.pwm = 128;		/* 50% duty cycle */
1641cdd9412SStefan Roese 
165*f5df36d0SStefan Roese 	pca9551_led_set_blink_rate(idx, rate);
1661cdd9412SStefan Roese 	pca9551_led_set_state(mask, mode);
1671cdd9412SStefan Roese }
168