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