mirror of
https://github.com/Telecominfraproject/OpenCellular.git
synced 2025-12-27 18:25:05 +00:00
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>
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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
|
||||
|
||||
161
chip/stm32/power_led.c
Normal file
161
chip/stm32/power_led.c
Normal file
@@ -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
|
||||
@@ -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");
|
||||
}
|
||||
|
||||
|
||||
@@ -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 */
|
||||
|
||||
Reference in New Issue
Block a user