|  | /* | 
|  | * 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; | 
|  | 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); | 
|  | } |