blob: 66b3900d0d51b992ab542ed99d25cd2062df649c [file] [log] [blame]
#include <common.h>
#include <dm.h>
#include <efi.h>
#include <fs.h>
#include <i2c.h>
#include <led_aw2015.h>
#include <linux/printk.h>
#include <linux/ctype.h>
#include <vsprintf.h>
DECLARE_GLOBAL_DATA_PTR;
// Copied from
// http://eureka-partner/I746d141b78040e0772a4c1f4111eb5afc12d70e6
// Reads "led_calibration_LUT.txt" from the factory partition.
// Forwards errors, returns 0 on success.
int get_cal_string(char *buf, int len)
{
int ret;
loff_t len_read;
const loff_t seek = 21;
ret = fs_set_blk_dev("mmc", "0:3", FS_TYPE_EXT);
if (ret) {
pr_err("LED: fs_set_blk_dev error=%d\n", ret);
return ret;
}
// Leave space for terminator
ret = fs_read("led_calibration_LUT.txt", (ulong) buf, seek, len - 1,
&len_read);
if (ret) {
pr_err("LED: fs_read error=%d\n", ret);
return ret;
}
// fs_read does not add a null terminator
buf[len_read] = '\0';
return 0;
}
// Copied from
// http://eureka-partner/I746d141b78040e0772a4c1f4111eb5afc12d70e6
// Gets calibration settings for the status LED:
// pwm_r, pwm_g, pwm_b, current_r, current_g, current_b
// On failure, |settings| is not modified.
int get_cal_settings(unsigned int *settings, const char color_header[])
{
int i;
unsigned long settings_[N_CAL_SETTINGS];
// Max length to cover all color lines
// 4 colors * (color_name + ":4095:" + xxx[,|;|\n] * 6)
// 4 * (14 + 6 + 4 * 6 )
const int max_chars = 176;
// Add one for null terminator
char buf[max_chars + 1];
int ret;
ret = get_cal_string(buf, max_chars);
if (ret != 0) {
pr_err("LED: failed to get_cal_string\n");
return ret;
}
char *p, *p_next;
p = strstr(buf, color_header);
if (!p) {
pr_err("LED: \"%s\" not found in cal file segment: %s\n",
color_header, buf);
return -1;
}
p += strlen(color_header);
for (i = 0; i < N_CAL_SETTINGS; ++i) {
unsigned long value = simple_strtoul(p, &p_next, 10);
if (value > 255) {
pr_err("LED: value %ld is too large\n",
settings_[i]);
return -1;
}
settings_[i] = value;
// Skip check that |p_next| is valid on last iteration
if (i == 5)
break;
// Is there not a separator, or not another number
if ((*p_next != ',' && *p_next != ';') ||
!isdigit(p_next[1])) {
// Is this the first current
if (i == 3) {
// One current instead of RGB current
settings_[4] = value;
settings_[5] = value;
break;
}
// Invalid
pr_err("LED calibration file is malformed\n");
return -1;
}
p = p_next + 1;
}
for (i = 0; i < N_CAL_SETTINGS; ++i)
settings[i] = (unsigned int)settings_[i];
return 0;
}
void reg_write(struct udevice *led_devp, int reg, int mask, int val) {
int old_val;
int new_val;
old_val = dm_i2c_reg_read(led_devp, reg);
if (old_val < 0) {
pr_err("LED: read %d reg failed\n", reg);
return;
}
new_val = (old_val & ~mask) | (val & mask);
dm_i2c_reg_write(led_devp, reg, new_val);
}
void turn_on_white_led(struct udevice *led_devp) {
/* default calibration settings */
unsigned int cal_settings[N_CAL_SETTINGS] =
{128, 128, 128, 100, 100, 100};
int i;
/* TODO(b/195092280 uncomment once the calibration file is ready) */
if (get_cal_settings(cal_settings, "White:4095:")) {
pr_err("LED: failed to get_cal_settings for white led\n");
return;
}
for(i = 0; i < 3; ++i) {
reg_write(led_devp, AW2015_REG_LCFG1 + i, AW2015_LED_LEDMD_MASK,
AW2015_LED_ON_MODE);
reg_write(led_devp, AW2015_REG_PWM1 + i, AW2015_LED_PWM_MASK,
cal_settings[i]);
reg_write(led_devp, AW2015_REG_ILED1 + i, AW2015_LED_ILED_MASK,
cal_settings[3+i]);
}
}
void enable_led_chip(void)
{
int gpio_node;
int led_enable_gpio;
pr_info("%s\n", __func__);
/* pull high the LED enabling ping */
gpio_node = fdt_path_offset(gd->fdt_blob, "/led_enable_gpio");
if (gpio_node >= 0) {
qca_gpio_init(gpio_node);
led_enable_gpio = fdtdec_get_int(gd->fdt_blob, gpio_node, "led_enable_gpio", 0);
if (led_enable_gpio >= 0) {
gpio_set_value(led_enable_gpio, GPIO_OUT_HIGH);
pr_info("enabled LED pin %d\n", led_enable_gpio) ;
}
}
}
void sys_led_init(enum LED_ANIMATION led_animation)
{
int ret;
struct udevice *bus, *dev;
enable_led_chip();
ret = uclass_get_device_by_seq(UCLASS_I2C, BUS_NUM, &bus);
if (ret) {
pr_err("LED: i2c get bus failed\n");
return;
}
ret = dm_i2c_probe(bus, I2C_LED_REG, 0, &dev);
if (ret) {
pr_err("LED: dm_i2c_probe failed\n");
return;
}
reg_write(dev, AW2015_REG_RSTIDR, AW2015_LED_RSTIDR_MASK,
AW2015_LED_RSTIDR_RESET);
reg_write(dev, AW2015_REG_GCR, AW2015_LED_CHIPEN_MASK,
AW2015_LED_CHIP_ENABLE);
reg_write(dev, AW2015_REG_GCR, AW2015_LED_CHARGE_DISABLE_MASK,
AW2015_LED_CHARGE_DISABLE);
reg_write(dev, AW2015_REG_IMAX, AW2015_LED_IMX_MASK,
AW2015_LED_12_75mA);
reg_write(dev, AW2015_REG_LEDCTR, AW2015_LED_PWMLOG_MASK,
AW2015_LED_PWMLOG_LEANER);
/* disable LEDs to avoid red lighting first */
reg_write(dev, AW2015_REG_LEDEN, AW2015_LED_LEDEN_MASK,
AW2015_LED_LEDEN_TURN_OFF_ALL);
switch(led_animation) {
case WHITE:
turn_on_white_led(dev);
break;
default:
pr_err("LED: unknown led animation\n");
}
/* enable 3 LEDs at once after all set */
reg_write(dev, AW2015_REG_LEDEN, AW2015_LED_LEDEN_MASK,
AW2015_LED_LEDEN_TURN_ON_ALL);
}