diff options
author | David Hendricks <dhendrix@chromium.org> | 2012-06-25 13:51:46 -0700 |
---|---|---|
committer | Gerrit <chrome-bot@google.com> | 2012-07-02 15:26:02 -0700 |
commit | 1bedd55970387fd019c5a6fd368b1576606e8563 (patch) | |
tree | e5ecb8d1d60d90ad1a87adcb3f0232294fd9ad73 | |
parent | bb27b96f13541f8df5fd8c210a619705c2c24d26 (diff) | |
download | chrome-ec-1bedd55970387fd019c5a6fd368b1576606e8563.tar.gz |
Daisy/Snow: Drive power LED with PWM
This drives the power LED for Snow (PB3) using TIM2 in PWM mode.
Since timer setup and manipulation is STM32-specific, the power LED
logic moved to to chip/stm32/power_led.c.
This also adds a "powerled" console command for testing.
Signed-off-by: David Hendricks <dhendrix@chromium.org>
BUG=chrome-os-partner:10647
TEST=Tested on Snow with powerled command, compiled for Daisy
Change-Id: I5a7dc20d201ea058767e3e76d54e7c8567a3b83c
Reviewed-on: https://gerrit.chromium.org/gerrit/26267
Commit-Ready: David Hendricks <dhendrix@chromium.org>
Reviewed-by: David Hendricks <dhendrix@chromium.org>
Tested-by: David Hendricks <dhendrix@chromium.org>
-rw-r--r-- | board/snow/board.c | 10 | ||||
-rw-r--r-- | board/snow/board.h | 1 | ||||
-rw-r--r-- | chip/stm32/build.mk | 2 | ||||
-rw-r--r-- | chip/stm32/power_led.c | 161 | ||||
-rw-r--r-- | common/gaia_power.c | 62 | ||||
-rw-r--r-- | include/power_led.h | 10 |
6 files changed, 186 insertions, 60 deletions
diff --git a/board/snow/board.c b/board/snow/board.c index b536ff3eda..4c9e30bd4b 100644 --- a/board/snow/board.c +++ b/board/snow/board.c @@ -56,7 +56,6 @@ const struct gpio_info gpio_list[GPIO_COUNT] = { {"PMIC_PWRON_L",GPIO_A, (1<<12), GPIO_OUT_HIGH, NULL}, {"ENTERING_RW", GPIO_D, (1<<0), GPIO_OUT_LOW, NULL}, {"CHARGER_EN", GPIO_B, (1<<2), GPIO_OUT_LOW, NULL}, - {"POWER_LED_L", GPIO_B, (1<<3), GPIO_OUT_HIGH, NULL}, {"EC_INT", GPIO_B, (1<<9), GPIO_HI_Z, NULL}, {"CODEC_INT", GPIO_D, (1<<1), GPIO_HI_Z, NULL}, {"KB_OUT00", GPIO_B, (1<<0), GPIO_KB_OUTPUT, NULL}, @@ -130,6 +129,15 @@ void configure_board(void) STM32_GPIO_AFIO_MAPR = (STM32_GPIO_AFIO_MAPR & ~(0x7 << 24)) | (2 << 24); + /* remap TIM2_CH2 to PB3 */ + STM32_GPIO_AFIO_MAPR = (STM32_GPIO_AFIO_MAPR & ~(0x3 << 8)) + | (1 << 8); + + /* set power LED to alternate function to be driven by TIM2/PWM */ + val = STM32_GPIO_CRL_OFF(GPIO_B) & ~0x0000f000; + val |= 0x00009000; + STM32_GPIO_CRL_OFF(GPIO_B) = val; + /* * I2C SCL/SDA on PB10-11 and PB6-7, bi-directional, no pull-up/down, * initialized as hi-Z until alt. function is set diff --git a/board/snow/board.h b/board/snow/board.h index 15db5a64d7..c78a30f574 100644 --- a/board/snow/board.h +++ b/board/snow/board.h @@ -79,7 +79,6 @@ enum gpio_signal { GPIO_PMIC_PWRON_L, /* 5v rail ready */ GPIO_EC_ENTERING_RW, /* EC is R/W mode for the kbc mux */ GPIO_CHARGER_EN, - GPIO_POWER_LED_L, /* Power state keyboard LED */ GPIO_EC_INT, GPIO_CODEC_INT, /* To audio codec (KB noise cancellation) */ GPIO_KB_OUT00, diff --git a/chip/stm32/build.mk b/chip/stm32/build.mk index 013569cd75..33a7ffb943 100644 --- a/chip/stm32/build.mk +++ b/chip/stm32/build.mk @@ -9,7 +9,7 @@ # STM32 SoC family has a Cortex-M3 ARM core CORE:=cortex-m -chip-y=dma.o gpio.o hwtimer.o system.o uart.o +chip-y=dma.o gpio.o hwtimer.o power_led.o system.o uart.o chip-y+=jtag-$(CHIP_VARIANT).o clock-$(CHIP_VARIANT).o gpio-$(CHIP_VARIANT).o chip-$(CONFIG_SPI)+=spi.o chip-$(CONFIG_I2C)+=i2c.o diff --git a/chip/stm32/power_led.c b/chip/stm32/power_led.c new file mode 100644 index 0000000000..a080dbfa07 --- /dev/null +++ b/chip/stm32/power_led.c @@ -0,0 +1,161 @@ +/* Copyright (c) 2012 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. + */ + +/* + * Keyboard power button LED state machine. + * + * This sets up TIM2 to drive the power button LED so that the duty cycle + * can range from 0-100%. + * + * In suspend mode, duty cycle transitions progressively slower from 0% + * to 100%, and progressively faster from 100% back down to 0%. This + * results in a breathing effect. It takes about 2sec for a full cycle. + */ + +#include "console.h" +#include "power_led.h" +#include "registers.h" +#include "task.h" +#include "timer.h" +#include "util.h" + +#define LED_STATE_TIMEOUT_MIN 15000 /* minimum of 15ms per step */ +#define LED_HOLD_TIME 330000 /* hold for 330ms at min/max */ +#define LED_STEP_PERCENT 4 /* incremental value of each step */ + +static enum powerled_state led_state; +static int power_led_percent; + +void powerled_set_state(enum powerled_state new_state) +{ + led_state = new_state; + /* Wake up the task */ + task_wake(TASK_ID_POWERLED); +} + +static void power_led_timer_init(void) +{ + /* enable TIM2 clock */ + STM32_RCC_APB1ENR |= 0x1; + + /* disable counter during setup */ + STM32_TIM_CR1(2) = 0x0000; + + /* + * CPU_CLOCK / PSC determines how fast the counter operates. + * ARR determines the wave period, CCRn determines duty cycle. + * Thus, frequency = CPU_CLOCK / PSC / ARR. + * + * Assuming 16MHz clock, the following yields: + * 16MHz / 1600 / 100 = 100Hz. + */ + STM32_TIM_PSC(2) = CPU_CLOCK / 10000; /* pre-scaler */ + STM32_TIM_ARR(2) = 100; /* auto-reload value */ + STM32_TIM_CCR2(2) = 100; /* duty cycle */ + + /* CC2 configured as output, PWM mode 1, preload enable */ + STM32_TIM_CCMR1(2) = (6 << 12) | (1 << 11); + + /* CC2 output enable, active low */ + STM32_TIM_CCER(2) = (1 << 4) | (1 << 5); + + /* generate update event to force loading of shadow registers */ + STM32_TIM_EGR(2) |= 1; + + /* enable auto-reload preload, start counting */ + STM32_TIM_CR1(2) |= (1 << 7) | (1 << 0); +} + +static void power_led_set_duty(int percent) +{ + ASSERT((percent >= 0) && (percent <= 100)); + power_led_percent = percent; + STM32_TIM_CCR2(2) = (STM32_TIM_ARR(2) / 100) * percent; +} + +/* returns the timeout period (in us) for current step */ +static int power_led_step(void) +{ + int state_timeout = 0; + static enum { DOWN = -1, UP = 1 } dir = UP; + + if (0 == power_led_percent) { + dir = UP; + state_timeout = LED_HOLD_TIME; + } else if (100 == power_led_percent) { + dir = DOWN; + state_timeout = LED_HOLD_TIME; + } else { + /* + * Decreases timeout as duty cycle percentage approaches + * 0%, increase as it appraoches 100%. + */ + state_timeout = LED_STATE_TIMEOUT_MIN + + LED_STATE_TIMEOUT_MIN * (power_led_percent / 33); + } + + /* + * The next duty cycle will take effect after the timeout has + * elapsed for this duty cycle and the power LED task calls this + * function again. + */ + power_led_set_duty(power_led_percent); + power_led_percent += dir * LED_STEP_PERCENT; + + return state_timeout; +} + +void power_led_task(void) +{ + power_led_timer_init(); + + while (1) { + int state_timeout = -1; + + switch (led_state) { + case POWERLED_STATE_ON: + power_led_set_duty(100); + state_timeout = -1; + break; + case POWERLED_STATE_OFF: + power_led_set_duty(0); + state_timeout = -1; + break; + case POWERLED_STATE_SUSPEND: + state_timeout = power_led_step(); + break; + default: + break; + } + + task_wait_event(state_timeout); + } +} + +#ifdef CONFIG_CMD_POWERLED +static int command_powerled(int argc, char **argv) +{ + enum powerled_state state = POWERLED_STATE_OFF; + + if (argc != 2) + return EC_ERROR_INVAL; + + if (!strcasecmp(argv[1], "off")) + state = POWERLED_STATE_OFF; + else if (!strcasecmp(argv[1], "on")) + state = POWERLED_STATE_ON; + else if (!strcasecmp(argv[1], "suspend")) + state = POWERLED_STATE_SUSPEND; + else + return EC_ERROR_INVAL; + + powerled_set_state(state); + return EC_SUCCESS; +} +DECLARE_CONSOLE_COMMAND(powerled, command_powerled, + "[off | on | suspend ]", + "Change power LED state", + NULL); +#endif diff --git a/common/gaia_power.c b/common/gaia_power.c index efbc4972fd..6f392adf78 100644 --- a/common/gaia_power.c +++ b/common/gaia_power.c @@ -28,6 +28,7 @@ #include "console.h" #include "gpio.h" #include "keyboard_scan.h" +#include "power_led.h" #include "task.h" #include "timer.h" #include "util.h" @@ -101,60 +102,6 @@ static timestamp_t pwron_deadline; /* force AP power on (used for recovery keypress) */ static int auto_power_on; -/*****************************************************************************/ -/* Keyboard power button LED state machine */ - -/* Suspend mode blinking duty cycle */ -#define LED_OFF_PERIOD 1500000 -#define LED_ON_PERIOD 50000 - -/* GPIO level for each LED state : it's an active low output */ -#define LED_GPIO_ON 0 -#define LED_GPIO_OFF 1 - -/* power LED FSM states */ -static enum power_led_state { - POWER_LED_OFF, - POWER_LED_ON, - POWER_LED_SUSP0, - POWER_LED_SUSP1, - - POWER_LED_STATE_COUNT -} led_state; - -/* power LED FSM parameters for each state */ -static const struct { - int gpio; - int duration; - enum power_led_state next; -} led_config[POWER_LED_STATE_COUNT] = { - [POWER_LED_OFF] = {LED_GPIO_OFF, -1, POWER_LED_OFF}, - [POWER_LED_ON] = {LED_GPIO_ON, -1, POWER_LED_ON}, - [POWER_LED_SUSP0] = {LED_GPIO_OFF, LED_OFF_PERIOD, POWER_LED_SUSP1}, - [POWER_LED_SUSP1] = {LED_GPIO_ON, LED_ON_PERIOD, POWER_LED_SUSP0}, -}; - -static void set_power_led(enum power_led_state new_state) -{ - led_state = new_state; - /* Wake up the task */ - task_wake(TASK_ID_POWERLED); -} - -void power_led_task(void) -{ - while (1) { - int state_timeout; - - gpio_set_level(GPIO_POWER_LED_L, led_config[led_state].gpio); - state_timeout = led_config[led_state].duration; - led_state = led_config[led_state].next; - task_wait_event(state_timeout); - } -} - -/*****************************************************************************/ - /* * Wait for GPIO "signal" to reach level "value". * Returns EC_ERROR_TIMEOUT if timeout before reaching the desired state. @@ -242,7 +189,8 @@ void gaia_suspend_event(enum gpio_signal signal) ap_suspended = !gpio_get_level(GPIO_SUSPEND_L); - set_power_led(ap_suspended ? POWER_LED_SUSP0 : POWER_LED_ON); + powerled_set_state(ap_suspended ? + POWERLED_STATE_SUSPEND : POWERLED_STATE_ON); } void gaia_power_event(enum gpio_signal signal) @@ -356,7 +304,7 @@ static int power_on(void) /* Enable 3.3v power rail */ gpio_set_level(GPIO_EN_PP3300, 1); ap_on = 1; - set_power_led(POWER_LED_ON); + powerled_set_state(POWERLED_STATE_ON); CPUTS("AP running ...\n"); return 0; } @@ -391,7 +339,7 @@ static void power_off(void) gpio_set_level(GPIO_PMIC_PWRON_L, 1); gpio_set_level(GPIO_EN_PP5000, 0); ap_on = 0; - set_power_led(POWER_LED_OFF); + powerled_set_state(POWERLED_STATE_OFF); CPUTS("Shutdown complete.\n"); } diff --git a/include/power_led.h b/include/power_led.h index 9c8fa49f86..9301a5fdf5 100644 --- a/include/power_led.h +++ b/include/power_led.h @@ -18,7 +18,17 @@ enum powerled_color { POWERLED_COLOR_COUNT /* Number of colors, not a color itself */ }; +enum powerled_state { + POWERLED_STATE_OFF, + POWERLED_STATE_ON, + POWERLED_STATE_SUSPEND, + POWERLED_STATE_COUNT +}; + /* Set the power adapter LED to the specified color. */ int powerled_set(enum powerled_color color); +/* Set the power LED according to the specified state. */ +void powerled_set_state(enum powerled_state state); + #endif /* __CROS_EC_POWER_LED_H */ |