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