summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDavid Hendricks <dhendrix@chromium.org>2012-06-25 13:51:46 -0700
committerGerrit <chrome-bot@google.com>2012-07-02 15:26:02 -0700
commit1bedd55970387fd019c5a6fd368b1576606e8563 (patch)
treee5ecb8d1d60d90ad1a87adcb3f0232294fd9ad73
parentbb27b96f13541f8df5fd8c210a619705c2c24d26 (diff)
downloadchrome-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.c10
-rw-r--r--board/snow/board.h1
-rw-r--r--chip/stm32/build.mk2
-rw-r--r--chip/stm32/power_led.c161
-rw-r--r--common/gaia_power.c62
-rw-r--r--include/power_led.h10
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 */