Skip to content
Snippets Groups Projects
pca9551_led.c 3.36 KiB
Newer Older
  • Learn to ignore specific revisions
  • /*
     * 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 == CONFIG_LED_STATUS_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;
    
    	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;
    
    
    	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);
    }