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