172 lines
		
	
	
		
			3.4 KiB
		
	
	
	
		
			C
		
	
	
	
			
		
		
	
	
			172 lines
		
	
	
		
			3.4 KiB
		
	
	
	
		
			C
		
	
	
	
| /*
 | |
|  * Copyright (C) 2015 Stefan Roese <sr@denx.de>
 | |
|  *
 | |
|  * SPDX-License-Identifier:	GPL-2.0+
 | |
|  */
 | |
| 
 | |
| #include <common.h>
 | |
| #include <errno.h>
 | |
| #include <i2c.h>
 | |
| 
 | |
| #ifndef CONFIG_PCA9551_I2C_ADDR
 | |
| #error "CONFIG_PCA9551_I2C_ADDR not defined!"
 | |
| #endif
 | |
| 
 | |
| #define PCA9551_REG_INPUT	0x00	/* Input register (read only) */
 | |
| #define PCA9551_REG_PSC0	0x01	/* Frequency prescaler 0 */
 | |
| #define PCA9551_REG_PWM0	0x02	/* PWM0 */
 | |
| #define PCA9551_REG_PSC1	0x03	/* Frequency prescaler 1 */
 | |
| #define PCA9551_REG_PWM1	0x04	/* PWM1 */
 | |
| #define PCA9551_REG_LS0		0x05	/* LED0 to LED3 selector */
 | |
| #define PCA9551_REG_LS1		0x06	/* LED4 to LED7 selector */
 | |
| 
 | |
| #define PCA9551_CTRL_AI		(1 << 4)	/* Auto-increment flag */
 | |
| 
 | |
| #define PCA9551_LED_STATE_ON		0x00
 | |
| #define PCA9551_LED_STATE_OFF		0x01
 | |
| #define PCA9551_LED_STATE_BLINK0	0x02
 | |
| #define PCA9551_LED_STATE_BLINK1	0x03
 | |
| 
 | |
| struct pca9551_blink_rate {
 | |
| 	u8 psc;	/* Frequency preescaler, see PCA9551_7.pdf p. 6 */
 | |
| 	u8 pwm;	/* Pulse width modulation, see PCA9551_7.pdf p. 6 */
 | |
| };
 | |
| 
 | |
| static int freq_last = -1;
 | |
| static int mask_last = -1;
 | |
| static int idx_last = -1;
 | |
| static int mode_last;
 | |
| 
 | |
| static int pca9551_led_get_state(int led, int *state)
 | |
| {
 | |
| 	unsigned int reg;
 | |
| 	u8 shift, buf;
 | |
| 	int ret;
 | |
| 
 | |
| 	if (led < 0 || led > 7) {
 | |
| 		return -EINVAL;
 | |
| 	} else if (led < 4) {
 | |
| 		reg = PCA9551_REG_LS0;
 | |
| 		shift = led << 1;
 | |
| 	} else {
 | |
| 		reg = PCA9551_REG_LS1;
 | |
| 		shift = (led - 4) << 1;
 | |
| 	}
 | |
| 
 | |
| 	ret = i2c_read(CONFIG_PCA9551_I2C_ADDR, reg, 1, &buf, 1);
 | |
| 	if (ret)
 | |
| 		return ret;
 | |
| 
 | |
| 	*state = (buf >> shift) & 0x03;
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| static int pca9551_led_set_state(int led, int state)
 | |
| {
 | |
| 	unsigned int reg;
 | |
| 	u8 shift, buf, mask;
 | |
| 	int ret;
 | |
| 
 | |
| 	if (led < 0 || led > 7) {
 | |
| 		return -EINVAL;
 | |
| 	} else if (led < 4) {
 | |
| 		reg = PCA9551_REG_LS0;
 | |
| 		shift = led << 1;
 | |
| 	} else {
 | |
| 		reg = PCA9551_REG_LS1;
 | |
| 		shift = (led - 4) << 1;
 | |
| 	}
 | |
| 	mask = 0x03 << shift;
 | |
| 
 | |
| 	ret = i2c_read(CONFIG_PCA9551_I2C_ADDR, reg, 1, &buf, 1);
 | |
| 	if (ret)
 | |
| 		return ret;
 | |
| 
 | |
| 	buf = (buf & ~mask) | ((state & 0x03) << shift);
 | |
| 
 | |
| 	ret = i2c_write(CONFIG_PCA9551_I2C_ADDR, reg, 1, &buf, 1);
 | |
| 	if (ret)
 | |
| 		return ret;
 | |
| 
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| static int pca9551_led_set_blink_rate(int idx, struct pca9551_blink_rate rate)
 | |
| {
 | |
| 	unsigned int reg;
 | |
| 	int ret;
 | |
| 
 | |
| 	switch (idx) {
 | |
| 	case 0:
 | |
| 		reg = PCA9551_REG_PSC0;
 | |
| 		break;
 | |
| 	case 1:
 | |
| 		reg = PCA9551_REG_PSC1;
 | |
| 		break;
 | |
| 	default:
 | |
| 		return -EINVAL;
 | |
| 	}
 | |
| 	reg |= PCA9551_CTRL_AI;
 | |
| 
 | |
| 	ret = i2c_write(CONFIG_PCA9551_I2C_ADDR, reg, 1, (u8 *)&rate, 2);
 | |
| 	if (ret)
 | |
| 		return ret;
 | |
| 
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| /*
 | |
|  * Functions referenced by cmd_led.c or status_led.c
 | |
|  */
 | |
| void __led_init(led_id_t id, int state)
 | |
| {
 | |
| }
 | |
| 
 | |
| void __led_set(led_id_t mask, int state)
 | |
| {
 | |
| 	if (state == STATUS_LED_OFF)
 | |
| 		pca9551_led_set_state(mask, PCA9551_LED_STATE_OFF);
 | |
| 	else
 | |
| 		pca9551_led_set_state(mask, PCA9551_LED_STATE_ON);
 | |
| }
 | |
| 
 | |
| void __led_toggle(led_id_t mask)
 | |
| {
 | |
| 	int state = 0;
 | |
| 
 | |
| 	pca9551_led_get_state(mask, &state);
 | |
| 	pca9551_led_set_state(mask, !state);
 | |
| }
 | |
| 
 | |
| void __led_blink(led_id_t mask, int freq)
 | |
| {
 | |
| 	struct pca9551_blink_rate rate;
 | |
| 	int mode;
 | |
| 	int idx;
 | |
| 
 | |
| 	if ((freq == freq_last) || (mask == mask_last)) {
 | |
| 		idx = idx_last;
 | |
| 		mode = mode_last;
 | |
| 	} else {
 | |
| 		/* Toggle blink index */
 | |
| 		if (idx_last == 0) {
 | |
| 			idx = 1;
 | |
| 			mode = PCA9551_LED_STATE_BLINK1;
 | |
| 		} else {
 | |
| 			idx = 0;
 | |
| 			mode = PCA9551_LED_STATE_BLINK0;
 | |
| 		}
 | |
| 
 | |
| 		idx_last = idx;
 | |
| 		mode_last = mode;
 | |
| 	}
 | |
| 	freq_last = freq;
 | |
| 	mask_last = mask;
 | |
| 
 | |
| 	rate.psc = ((freq * 38) / 1000) - 1;
 | |
| 	rate.pwm = 128;		/* 50% duty cycle */
 | |
| 
 | |
| 	pca9551_led_set_blink_rate(idx, rate);
 | |
| 	pca9551_led_set_state(mask, mode);
 | |
| }
 |