diff --git a/board/eve/board.c b/board/eve/board.c index a130b0f1fb..ea7a0e76b6 100644 --- a/board/eve/board.c +++ b/board/eve/board.c @@ -114,7 +114,13 @@ struct keyboard_scan_config keyscan_config = { /* PWM channels. Must be in the exactly same order as in enum pwm_channel. */ const struct pwm_t pwm_channels[] = { - [PWM_CH_KBLIGHT] = { 5, PWM_CONFIG_DSLEEP, 100 }, + [PWM_CH_KBLIGHT] = { 5, PWM_CONFIG_DSLEEP, 100 }, + [PWM_CH_LED_L_RED] = { 2, PWM_CONFIG_DSLEEP, 100 }, + [PWM_CH_LED_L_GREEN] = { 3, PWM_CONFIG_DSLEEP, 100 }, + [PWM_CH_LED_L_BLUE] = { 4, PWM_CONFIG_DSLEEP, 100 }, + [PWM_CH_LED_R_RED] = { 1, PWM_CONFIG_DSLEEP, 100 }, + [PWM_CH_LED_R_GREEN] = { 0, PWM_CONFIG_DSLEEP, 100 }, + [PWM_CH_LED_R_BLUE] = { 6, PWM_CONFIG_DSLEEP, 100 }, }; BUILD_ASSERT(ARRAY_SIZE(pwm_channels) == PWM_CH_COUNT); @@ -575,13 +581,6 @@ void board_hibernate_late(void) int i; const uint32_t hibernate_pins[][2] = { {GPIO_LID_OPEN, GPIO_INT_RISING}, - /* Turn off LEDs in hibernate */ - {GPIO_CHARGE_LED_1, GPIO_OUTPUT | GPIO_HIGH}, - {GPIO_CHARGE_LED_2, GPIO_OUTPUT | GPIO_HIGH}, - {GPIO_CHARGE_LED_3, GPIO_OUTPUT | GPIO_HIGH}, - {GPIO_CHARGE_LED_4, GPIO_OUTPUT | GPIO_HIGH}, - {GPIO_CHARGE_LED_5, GPIO_OUTPUT | GPIO_HIGH}, - {GPIO_CHARGE_LED_6, GPIO_OUTPUT | GPIO_HIGH}, /* * BD99956 handles charge input automatically. We'll disable * charge output in hibernate. Charger will assert ACOK_OD diff --git a/board/eve/board.h b/board/eve/board.h index 521fe1631f..73f15489d0 100644 --- a/board/eve/board.h +++ b/board/eve/board.h @@ -27,6 +27,7 @@ #define CONFIG_FPU #define CONFIG_I2C #define CONFIG_I2C_MASTER +#define CONFIG_LED_COMMON #define CONFIG_LID_SWITCH #define CONFIG_LTO #define CONFIG_PWM @@ -205,6 +206,12 @@ enum temp_sensor_id { enum pwm_channel { PWM_CH_KBLIGHT, + PWM_CH_LED_L_RED, + PWM_CH_LED_L_GREEN, + PWM_CH_LED_L_BLUE, + PWM_CH_LED_R_RED, + PWM_CH_LED_R_GREEN, + PWM_CH_LED_R_BLUE, PWM_CH_COUNT }; diff --git a/board/eve/build.mk b/board/eve/build.mk index 705891e38c..f47b5d9caf 100644 --- a/board/eve/build.mk +++ b/board/eve/build.mk @@ -9,6 +9,6 @@ CHIP:=npcx CHIP_VARIANT:=npcx5m6g -board-y=board.o +board-y=board.o led.o board-$(CONFIG_BATTERY_SMART)+=battery.o board-$(CONFIG_USB_POWER_DELIVERY)+=usb_pd_policy.o diff --git a/board/eve/gpio.inc b/board/eve/gpio.inc index 1814b32684..12960f467b 100644 --- a/board/eve/gpio.inc +++ b/board/eve/gpio.inc @@ -59,14 +59,6 @@ GPIO(I2C2_SDA, PIN(9, 1), GPIO_INPUT) /* EC_I2C2_SENSOR_3V3_SDA */ GPIO(I2C3_SCL, PIN(D, 1), GPIO_INPUT) /* EC_I2C3_POWER_SCL */ GPIO(I2C3_SDA, PIN(D, 0), GPIO_INPUT) /* EC_I2C3_POWER_SDA */ -/* Charge LED */ -GPIO(CHARGE_LED_1, PIN(8, 0), GPIO_OUT_LOW) /* Charge LED 1 */ -GPIO(CHARGE_LED_2, PIN(C, 4), GPIO_OUT_LOW) /* Charge LED 2 */ -GPIO(CHARGE_LED_3, PIN(B, 6), GPIO_OUT_LOW) /* Charge LED 3 */ -GPIO(CHARGE_LED_4, PIN(C, 0), GPIO_OUT_LOW) /* Charge LED 4 */ -GPIO(CHARGE_LED_5, PIN(C, 3), GPIO_OUT_LOW) /* Charge LED 5 */ -GPIO(CHARGE_LED_6, PIN(C, 2), GPIO_OUT_LOW) /* Charge LED 6 */ - /* AP wake sources when in Deep-S3 state */ GPIO(TOUCHPAD_WAKE_L, PIN(7, 1), GPIO_INPUT | GPIO_SEL_1P8V) /* INT# from Trackpad */ GPIO(DSP_WAKE_L, PIN(C, 6), GPIO_INPUT | GPIO_SEL_1P8V) /* INT# from DSP Mic */ @@ -103,6 +95,14 @@ ALTERNATE(PIN_MASK(B, 0x30), 1, MODULE_I2C, 0) /* GPIOB4-B5 */ /* EC_I2C00_USB ALTERNATE(PIN_MASK(B, 0x0C), 1, MODULE_I2C, 0) /* GPOPB2-B3 */ /* EC_I2C01_USB_C1_SDA/SCL */ ALTERNATE(PIN_MASK(D, 0x03), 1, MODULE_I2C, 0) /* GPIOD0-D1 */ /* EC_I2C3_POWER_SDA/SCL */ ALTERNATE(PIN_MASK(B, 0x80), 1, MODULE_PWM, 0) /* GPIOB7 */ /* KBD_BL_PWM */ +/* Left LED PWM Channels */ +ALTERNATE(PIN_MASK(C, 0x10), 1, MODULE_PWM, 0) /* GPIOC4 PWM2 Red*/ +ALTERNATE(PIN_MASK(B, 0x40), 1, MODULE_PWM, 0) /* GPOB6 PWM3 Green*/ +ALTERNATE(PIN_MASK(8, 0x01), 1, MODULE_PWM, 0) /* GPIO80 PWM4 Blue*/ +/* Right LED PWM Channels */ +ALTERNATE(PIN_MASK(C, 0x04), 1, MODULE_PWM, 0) /* GPIOC2 PWM1 Red*/ +ALTERNATE(PIN_MASK(C, 0x08), 1, MODULE_PWM, 0) /* GPIOC3 PWM0 Green */ +ALTERNATE(PIN_MASK(C, 0x01), 1, MODULE_PWM, 0) /* GPIOC0 PWM6 Blue */ /* Keyboard pins */ #define GPIO_KB_INPUT (GPIO_INPUT | GPIO_PULL_UP) diff --git a/board/eve/led.c b/board/eve/led.c new file mode 100644 index 0000000000..1e7f4222d4 --- /dev/null +++ b/board/eve/led.c @@ -0,0 +1,194 @@ +/* Copyright 2017 The Chromium OS Authors. All rights reserved. + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + * + * Power/Battery LED control for Eve + */ + +#include "charge_state.h" +#include "chipset.h" +#include "console.h" +#include "extpower.h" +#include "gpio.h" +#include "hooks.h" +#include "led_common.h" +#include "pwm.h" +#include "registers.h" +#include "util.h" + +#define CPRINTF(format, args...) cprintf(CC_PWM, format, ## args) +#define CPRINTS(format, args...) cprints(CC_PWM, format, ## args) + +#define LED_TOTAL_TICKS 16 +#define LED_ON_TICKS 8 + +static int led_debug; + +const enum ec_led_id supported_led_ids[] = { + EC_LED_ID_POWER_LED, EC_LED_ID_BATTERY_LED}; +const int supported_led_ids_count = ARRAY_SIZE(supported_led_ids); + +enum led_color { + LED_OFF = 0, + LED_RED, + LED_GREEN, + LED_BLUE, + + /* Number of colors, not a color itself */ + LED_COLOR_COUNT +}; + +/* Brightness vs. color, in the order of off, red, green and blue */ +static const uint8_t color_brightness[LED_COLOR_COUNT][3] = { + /* {Red, Green, Blue}, */ + [LED_OFF] = {100, 100, 100}, + [LED_RED] = {20, 100, 100}, + [LED_GREEN] = {100, 20, 100}, + [LED_BLUE] = {100, 100, 20}, +}; + +/** + * Set LED color + * + * @param color Enumerated color value + */ +static void set_color(enum led_color color) +{ + /* Set color for left LED */ + pwm_set_duty(PWM_CH_LED_L_RED, color_brightness[color][0]); + pwm_set_duty(PWM_CH_LED_L_GREEN, color_brightness[color][1]); + pwm_set_duty(PWM_CH_LED_L_BLUE, color_brightness[color][2]); + + /* Set color for right LED */ + pwm_set_duty(PWM_CH_LED_R_RED, color_brightness[color][0]); + pwm_set_duty(PWM_CH_LED_R_GREEN, color_brightness[color][1]); + pwm_set_duty(PWM_CH_LED_R_BLUE, color_brightness[color][2]); +} + +void led_get_brightness_range(enum ec_led_id led_id, uint8_t *brightness_range) +{ + brightness_range[EC_LED_COLOR_RED] = 100; + brightness_range[EC_LED_COLOR_BLUE] = 100; + brightness_range[EC_LED_COLOR_GREEN] = 100; +} + +int led_set_brightness(enum ec_led_id led_id, const uint8_t *brightness) +{ + /* Set brightness for left LED */ + pwm_set_duty(PWM_CH_LED_L_RED, brightness[EC_LED_COLOR_RED]); + pwm_set_duty(PWM_CH_LED_L_BLUE, brightness[EC_LED_COLOR_BLUE]); + pwm_set_duty(PWM_CH_LED_L_GREEN, brightness[EC_LED_COLOR_GREEN]); + + /* Set brightness for right LED */ + pwm_set_duty(PWM_CH_LED_R_RED, brightness[EC_LED_COLOR_RED]); + pwm_set_duty(PWM_CH_LED_R_BLUE, brightness[EC_LED_COLOR_BLUE]); + pwm_set_duty(PWM_CH_LED_R_GREEN, brightness[EC_LED_COLOR_GREEN]); + return EC_SUCCESS; +} + +static void eve_led_set_power_battery(void) +{ + static int power_ticks; + enum charge_state chg_state = charge_get_state(); + + if (chipset_in_state(CHIPSET_STATE_ON)) { + set_color(LED_BLUE); + return; + } + + /* Flash red on critical battery, which usually inhibits AP power-on. */ + if (battery_is_present() != BP_YES || + charge_get_percent() < CONFIG_CHARGER_MIN_BAT_PCT_FOR_POWER_ON) { + set_color(((power_ticks++ % LED_TOTAL_TICKS) < LED_ON_TICKS) ? + LED_RED : LED_OFF); + return; + } + + /* CHIPSET_STATE_OFF */ + switch (chg_state) { + case PWR_STATE_DISCHARGE: + set_color(LED_OFF); + break; + case PWR_STATE_CHARGE: + set_color(LED_RED); + break; + case PWR_STATE_ERROR: + set_color(((power_ticks++ % LED_TOTAL_TICKS) + < LED_ON_TICKS) ? LED_RED : LED_GREEN); + break; + case PWR_STATE_CHARGE_NEAR_FULL: + case PWR_STATE_IDLE: /* External power connected in IDLE. */ + set_color(LED_GREEN); + break; + default: + set_color(LED_RED); + break; + } + if (chg_state != PWR_STATE_ERROR) + power_ticks = 0; +} + +static void led_init(void) +{ + /* + * Enable PWMs and set to 0% duty cycle. If they're disabled, + * seems to ground the pins instead of letting them float. + */ + /* Initialize PWM channels for left LED */ + pwm_enable(PWM_CH_LED_L_RED, 1); + pwm_enable(PWM_CH_LED_L_GREEN, 1); + pwm_enable(PWM_CH_LED_L_BLUE, 1); + + /* Initialize PWM channels for right LED */ + pwm_enable(PWM_CH_LED_R_RED, 1); + pwm_enable(PWM_CH_LED_R_GREEN, 1); + pwm_enable(PWM_CH_LED_R_BLUE, 1); + + set_color(LED_OFF); +} +/* After pwm_pin_init() */ +DECLARE_HOOK(HOOK_INIT, led_init, HOOK_PRIO_DEFAULT); + +/** + * Called by hook task every 250 ms + */ +static void led_tick(void) +{ + if (led_debug) + return; + + if (led_auto_control_is_enabled(EC_LED_ID_POWER_LED) && + led_auto_control_is_enabled(EC_LED_ID_BATTERY_LED)) { + eve_led_set_power_battery(); + return; + } +} +DECLARE_HOOK(HOOK_TICK, led_tick, HOOK_PRIO_DEFAULT); + +/******************************************************************/ +/* Console commands */ +static int command_led(int argc, char **argv) +{ + if (argc > 1) { + ccprintf("cmd led %s\n", argv[1]); + if (!strcasecmp(argv[1], "debug")) { + led_debug ^= 1; + CPRINTF("led_debug = %d\n", led_debug); + } else if (!strcasecmp(argv[1], "off")) { + set_color(LED_OFF); + } else if (!strcasecmp(argv[1], "red")) { + set_color(LED_RED); + } else if (!strcasecmp(argv[1], "green")) { + set_color(LED_GREEN); + } else if (!strcasecmp(argv[1], "blue")) { + set_color(LED_BLUE); + } else { + /* maybe handle charger_discharge_on_ac() too? */ + return EC_ERROR_PARAM1; + } + } + return EC_SUCCESS; +} +DECLARE_CONSOLE_COMMAND(led, command_led, + "[debug|red|green|blue|off]", + "Change LED color");