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 35*f5df36d0SStefan Roese static int freq_last = -1; 36*f5df36d0SStefan Roese static int mask_last = -1; 37*f5df36d0SStefan Roese static int idx_last = -1; 38*f5df36d0SStefan Roese static int mode_last; 391cdd9412SStefan Roese 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 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 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 /* 1191cdd9412SStefan Roese * Functions referenced by cmd_led.c 1201cdd9412SStefan Roese */ 1211cdd9412SStefan Roese void __led_set(led_id_t mask, int state) 1221cdd9412SStefan Roese { 1231cdd9412SStefan Roese if (state == STATUS_LED_OFF) 1241cdd9412SStefan Roese pca9551_led_set_state(mask, PCA9551_LED_STATE_OFF); 1251cdd9412SStefan Roese else 1261cdd9412SStefan Roese pca9551_led_set_state(mask, PCA9551_LED_STATE_ON); 1271cdd9412SStefan Roese } 1281cdd9412SStefan Roese 1291cdd9412SStefan Roese void __led_toggle(led_id_t mask) 1301cdd9412SStefan Roese { 1311cdd9412SStefan Roese int state = 0; 1321cdd9412SStefan Roese 1331cdd9412SStefan Roese pca9551_led_get_state(mask, &state); 1341cdd9412SStefan Roese pca9551_led_set_state(mask, !state); 1351cdd9412SStefan Roese } 1361cdd9412SStefan Roese 1371cdd9412SStefan Roese void __led_blink(led_id_t mask, int freq) 1381cdd9412SStefan Roese { 1391cdd9412SStefan Roese struct pca9551_blink_rate rate; 1401cdd9412SStefan Roese int mode; 141*f5df36d0SStefan Roese int idx; 1421cdd9412SStefan Roese 143*f5df36d0SStefan Roese if ((freq == freq_last) || (mask == mask_last)) { 144*f5df36d0SStefan Roese idx = idx_last; 145*f5df36d0SStefan Roese mode = mode_last; 1461cdd9412SStefan Roese } else { 147*f5df36d0SStefan Roese /* Toggle blink index */ 148*f5df36d0SStefan Roese if (idx_last == 0) { 149*f5df36d0SStefan Roese idx = 1; 1501cdd9412SStefan Roese mode = PCA9551_LED_STATE_BLINK1; 151*f5df36d0SStefan Roese } else { 152*f5df36d0SStefan Roese idx = 0; 153*f5df36d0SStefan Roese mode = PCA9551_LED_STATE_BLINK0; 1541cdd9412SStefan Roese } 1551cdd9412SStefan Roese 156*f5df36d0SStefan Roese idx_last = idx; 157*f5df36d0SStefan Roese mode_last = mode; 158*f5df36d0SStefan Roese } 159*f5df36d0SStefan Roese freq_last = freq; 160*f5df36d0SStefan Roese mask_last = mask; 161*f5df36d0SStefan Roese 1621cdd9412SStefan Roese rate.psc = ((freq * 38) / 1000) - 1; 1631cdd9412SStefan Roese rate.pwm = 128; /* 50% duty cycle */ 1641cdd9412SStefan Roese 165*f5df36d0SStefan Roese pca9551_led_set_blink_rate(idx, rate); 1661cdd9412SStefan Roese pca9551_led_set_state(mask, mode); 1671cdd9412SStefan Roese } 168