1 /* 2 * Copyright (C) 2015 Stefan Roese <sr@denx.de> 3 * 4 * SPDX-License-Identifier: GPL-2.0+ 5 */ 6 7 #include <common.h> 8 #include <errno.h> 9 #include <i2c.h> 10 11 #ifndef CONFIG_PCA9551_I2C_ADDR 12 #error "CONFIG_PCA9551_I2C_ADDR not defined!" 13 #endif 14 15 #define PCA9551_REG_INPUT 0x00 /* Input register (read only) */ 16 #define PCA9551_REG_PSC0 0x01 /* Frequency prescaler 0 */ 17 #define PCA9551_REG_PWM0 0x02 /* PWM0 */ 18 #define PCA9551_REG_PSC1 0x03 /* Frequency prescaler 1 */ 19 #define PCA9551_REG_PWM1 0x04 /* PWM1 */ 20 #define PCA9551_REG_LS0 0x05 /* LED0 to LED3 selector */ 21 #define PCA9551_REG_LS1 0x06 /* LED4 to LED7 selector */ 22 23 #define PCA9551_CTRL_AI (1 << 4) /* Auto-increment flag */ 24 25 #define PCA9551_LED_STATE_ON 0x00 26 #define PCA9551_LED_STATE_OFF 0x01 27 #define PCA9551_LED_STATE_BLINK0 0x02 28 #define PCA9551_LED_STATE_BLINK1 0x03 29 30 struct pca9551_blink_rate { 31 u8 psc; /* Frequency preescaler, see PCA9551_7.pdf p. 6 */ 32 u8 pwm; /* Pulse width modulation, see PCA9551_7.pdf p. 6 */ 33 }; 34 35 static int freq0, freq1; 36 37 static int pca9551_led_get_state(int led, int *state) 38 { 39 unsigned int reg; 40 u8 shift, buf; 41 int ret; 42 43 if (led < 0 || led > 7) { 44 return -EINVAL; 45 } else if (led < 4) { 46 reg = PCA9551_REG_LS0; 47 shift = led << 1; 48 } else { 49 reg = PCA9551_REG_LS1; 50 shift = (led - 4) << 1; 51 } 52 53 ret = i2c_read(CONFIG_PCA9551_I2C_ADDR, reg, 1, &buf, 1); 54 if (ret) 55 return ret; 56 57 *state = (buf >> shift) & 0x03; 58 return 0; 59 } 60 61 static int pca9551_led_set_state(int led, int state) 62 { 63 unsigned int reg; 64 u8 shift, buf, mask; 65 int ret; 66 67 if (led < 0 || led > 7) { 68 return -EINVAL; 69 } else if (led < 4) { 70 reg = PCA9551_REG_LS0; 71 shift = led << 1; 72 } else { 73 reg = PCA9551_REG_LS1; 74 shift = (led - 4) << 1; 75 } 76 mask = 0x03 << shift; 77 78 ret = i2c_read(CONFIG_PCA9551_I2C_ADDR, reg, 1, &buf, 1); 79 if (ret) 80 return ret; 81 82 buf = (buf & ~mask) | ((state & 0x03) << shift); 83 84 ret = i2c_write(CONFIG_PCA9551_I2C_ADDR, reg, 1, &buf, 1); 85 if (ret) 86 return ret; 87 88 return 0; 89 } 90 91 static int pca9551_led_set_blink_rate(int idx, struct pca9551_blink_rate rate) 92 { 93 unsigned int reg; 94 int ret; 95 96 switch (idx) { 97 case 0: 98 reg = PCA9551_REG_PSC0; 99 break; 100 case 1: 101 reg = PCA9551_REG_PSC1; 102 break; 103 default: 104 return -EINVAL; 105 } 106 reg |= PCA9551_CTRL_AI; 107 108 ret = i2c_write(CONFIG_PCA9551_I2C_ADDR, reg, 1, (u8 *)&rate, 2); 109 if (ret) 110 return ret; 111 112 return 0; 113 } 114 115 /* 116 * Functions referenced by cmd_led.c 117 */ 118 void __led_set(led_id_t mask, int state) 119 { 120 if (state == STATUS_LED_OFF) 121 pca9551_led_set_state(mask, PCA9551_LED_STATE_OFF); 122 else 123 pca9551_led_set_state(mask, PCA9551_LED_STATE_ON); 124 } 125 126 void __led_toggle(led_id_t mask) 127 { 128 int state = 0; 129 130 pca9551_led_get_state(mask, &state); 131 pca9551_led_set_state(mask, !state); 132 } 133 134 void __led_blink(led_id_t mask, int freq) 135 { 136 struct pca9551_blink_rate rate; 137 int mode; 138 int blink; 139 140 if ((freq0 == 0) || (freq == freq0)) { 141 blink = 0; 142 mode = PCA9551_LED_STATE_BLINK0; 143 freq0 = freq; 144 } else { 145 blink = 1; 146 mode = PCA9551_LED_STATE_BLINK1; 147 freq1 = freq; 148 } 149 150 rate.psc = ((freq * 38) / 1000) - 1; 151 rate.pwm = 128; /* 50% duty cycle */ 152 153 pca9551_led_set_blink_rate(blink, rate); 154 pca9551_led_set_state(mask, mode); 155 } 156