mirror of
https://github.com/Telecominfraproject/OpenCellular.git
synced 2026-01-10 17:41:54 +00:00
daisy: Refactor the power task
Add a set of functions to deal with power on/power off, and checking the power button. Then use these functions in a new top-level power control loop. This implements the following features: - Cold reset powers off the AP When powered off: - Press pwron turns on the AP - Hold pwron turns on the AP, and then 16s later turns it off and leaves it off until pwron is released and pressed again When powered on: - The PMIC PWRON signal is released one second after the power button is released (we expect that U-Boot as asserted XPSHOLD by then) - Holding pwron for 8s powers off the AP - Pressing and releasing pwron within that 8s is ignored - If XPSHOLD is dropped by the AP, then we power the AP off BUG=chrome-os-partner:9424 TEST=very ad-hoc: 1. build and boot on daisy, flash U-Boot with USB using 'cros_bundle_firmware -w usb', inserting daisy USB cable when it says 'Reseting board via servo...' 2. Press cold reset, then power on, see that it powers on 3. Then hold power-on for 8 seconds and see that it power off 4. XPSHOLD function not tested yet (this should work in Daisy 2) Change-Id: Ie471af0b4e690de7d6340e47e148c8ce3cda94f3 Signed-off-by: Simon Glass <sjg@chromium.org>
This commit is contained in:
@@ -3,7 +3,25 @@
|
||||
* found in the LICENSE file.
|
||||
*/
|
||||
|
||||
/* GAIA SoC power sequencing module for Chrome EC */
|
||||
/*
|
||||
* GAIA SoC power sequencing module for Chrome EC
|
||||
*
|
||||
* This implements the following features:
|
||||
*
|
||||
* - Cold reset powers off the AP
|
||||
*
|
||||
* When powered off:
|
||||
* - Press pwron turns on the AP
|
||||
* - Hold pwron turns on the AP, and then 16s later turns it off and leaves
|
||||
* it off until pwron is released and pressed again
|
||||
*
|
||||
* When powered on:
|
||||
* - The PMIC PWRON signal is released one second after the power button is
|
||||
* released (we expect that U-Boot as asserted XPSHOLD by then)
|
||||
* - Holding pwron for 8s powers off the AP
|
||||
* - Pressing and releasing pwron within that 8s is ignored
|
||||
* - If XPSHOLD is dropped by the AP, then we power the AP off
|
||||
*/
|
||||
|
||||
#include "board.h"
|
||||
#include "chipset.h" /* This module implements chipset functions too */
|
||||
@@ -26,6 +44,29 @@
|
||||
/* Long power key press to force shutdown */
|
||||
#define DELAY_FORCE_SHUTDOWN 8000000 /* 8s */
|
||||
|
||||
/*
|
||||
* If the power key is pressed to turn on, then held for this long, we
|
||||
* power off.
|
||||
*
|
||||
* The idea here is that behavior for 8s for AP shutdown is unchanged
|
||||
* but power-on is modified to allow enough time U-Boot to be updated
|
||||
* via USB (which takes about 10sec).
|
||||
*
|
||||
* So after power button is pressed:
|
||||
|
||||
* Normal case: User releases power button and gaia_power_task() goes
|
||||
* into the inner loop, waiting for next event to occur (power button
|
||||
* press or XPSHOLD == 0).
|
||||
*
|
||||
* U-Boot updating: User presses and holds power button. EC does not
|
||||
* check XPSHOLD, and waits up to 16sec for an event. If no event occurs
|
||||
* within 16sec, EC powers off AP.
|
||||
*/
|
||||
#define DELAY_SHUTDOWN_ON_POWER_HOLD (16 * 1000000)
|
||||
|
||||
/* Delay after power button release before we release GPIO_PMIC_PWRON_L */
|
||||
#define DELAY_RELEASE_PWRON 1000000 /* 1s */
|
||||
|
||||
/* debounce time to prevent accidental power-on after keyboard power off */
|
||||
#define KB_PWR_ON_DEBOUNCE 250 /* 250us */
|
||||
|
||||
@@ -43,6 +84,18 @@ static int ap_on;
|
||||
static int force_signal = -1;
|
||||
static int force_value;
|
||||
|
||||
/* 1 if the power button was pressed last time we checked */
|
||||
static char power_button_was_pressed;
|
||||
|
||||
/* time where we will power off, if power button still held down */
|
||||
static timestamp_t power_off_deadline;
|
||||
|
||||
/* 1 if we have released GPIO_PMIC_PWRON_L */
|
||||
static int pwron_released;
|
||||
|
||||
/* time where we will release GPIO_PMIC_PWRON_L */
|
||||
static timestamp_t pwron_deadline;
|
||||
|
||||
/*
|
||||
* Wait for GPIO "signal" to reach level "value".
|
||||
* Returns EC_ERROR_TIMEOUT if timeout before reaching the desired state.
|
||||
@@ -77,50 +130,47 @@ static int wait_in_signal(enum gpio_signal signal, int value, int timeout)
|
||||
return EC_SUCCESS;
|
||||
}
|
||||
|
||||
/* Wait for some event triggering the shutdown.
|
||||
/*
|
||||
* Check for some event triggering the shutdown.
|
||||
*
|
||||
* It can be either a long power button press or a shutdown triggered from the
|
||||
* AP and detected by reading XPSHOLD.
|
||||
*
|
||||
* @return 1 if a shutdown should happen, 0 if not
|
||||
*/
|
||||
static void wait_for_power_off(void)
|
||||
static int check_for_power_off_event(void)
|
||||
{
|
||||
timestamp_t deadline, now;
|
||||
timestamp_t now;
|
||||
int pressed = 0;
|
||||
|
||||
while (1) {
|
||||
/* wait for power button press or XPSHOLD falling edge */
|
||||
while ((gpio_get_level(GPIO_KB_PWR_ON_L) == 1) &&
|
||||
(gpio_get_level(GPIO_SOC1V8_XPSHOLD) == 1)) {
|
||||
task_wait_event(-1);
|
||||
}
|
||||
|
||||
/* XPSHOLD released by AP : shutdown immediatly */
|
||||
if (gpio_get_level(GPIO_SOC1V8_XPSHOLD) == 0)
|
||||
return;
|
||||
|
||||
/* relay to PMIC */
|
||||
gpio_set_level(GPIO_PMIC_PWRON_L, 0);
|
||||
|
||||
/* check if power button is pressed for 8s */
|
||||
deadline.val = get_time().val + DELAY_FORCE_SHUTDOWN;
|
||||
while ((gpio_get_level(GPIO_KB_PWR_ON_L) == 0) &&
|
||||
(gpio_get_level(GPIO_SOC1V8_XPSHOLD) == 1)) {
|
||||
now = get_time();
|
||||
if ((now.val >= deadline.val) ||
|
||||
(task_wait_event(deadline.val - now.val) ==
|
||||
TASK_EVENT_TIMER)) {
|
||||
gpio_set_level(GPIO_PMIC_PWRON_L, 1);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
gpio_set_level(GPIO_PMIC_PWRON_L, 1);
|
||||
|
||||
/*
|
||||
* Holding down the power button causes this loop to spin
|
||||
* endlessly, triggering the watchdog. So add a wait here.
|
||||
*/
|
||||
task_wait_event(-1);
|
||||
/* Check for power button press */
|
||||
if (gpio_get_level(GPIO_KB_PWR_ON_L) == 0) {
|
||||
udelay(KB_PWR_ON_DEBOUNCE);
|
||||
if (gpio_get_level(GPIO_KB_PWR_ON_L) == 0)
|
||||
pressed = 1;
|
||||
}
|
||||
|
||||
now = get_time();
|
||||
if (pressed) {
|
||||
if (!power_button_was_pressed) {
|
||||
power_off_deadline.val = now.val + DELAY_FORCE_SHUTDOWN;
|
||||
CPRINTF("Waiting for long power press %u\n",
|
||||
power_off_deadline.le.lo);
|
||||
} else if (timestamp_expired(power_off_deadline, &now)) {
|
||||
power_off_deadline.val = 0;
|
||||
CPRINTF("Power off after long press now=%u, %u\n",
|
||||
now.le.lo, power_off_deadline.le.lo);
|
||||
return 2;
|
||||
}
|
||||
} else if (power_button_was_pressed) {
|
||||
CPUTS("Cancel power off\n");
|
||||
}
|
||||
power_button_was_pressed = pressed;
|
||||
|
||||
/* XPSHOLD released by AP : shutdown immediately */
|
||||
if (pwron_released && gpio_get_level(GPIO_SOC1V8_XPSHOLD) == 0)
|
||||
return 3;
|
||||
return 0;
|
||||
}
|
||||
|
||||
void gaia_power_event(enum gpio_signal signal)
|
||||
@@ -159,75 +209,174 @@ int chipset_in_state(int state_mask)
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
void chipset_exit_hard_off(void)
|
||||
{
|
||||
/* TODO: implement, if/when we take the AP down to a hard-off state */
|
||||
}
|
||||
|
||||
/*****************************************************************************/
|
||||
|
||||
/**
|
||||
* Check if there has been a power-on event
|
||||
*
|
||||
* This waits for the power button to be pressed, then returns whether it
|
||||
* is still pressed, after a debounce period
|
||||
*
|
||||
* @return 1 if there has been a power-on event, 0 if not
|
||||
*/
|
||||
static int check_for_power_on_event(void)
|
||||
{
|
||||
/* wait for Power button press */
|
||||
wait_in_signal(GPIO_KB_PWR_ON_L, 0, -1);
|
||||
|
||||
udelay(KB_PWR_ON_DEBOUNCE);
|
||||
return gpio_get_level(GPIO_KB_PWR_ON_L) == 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Power on the AP
|
||||
*
|
||||
* @return 0 if ok, -1 on error (PP1800_LDO2 failed to come on)
|
||||
*/
|
||||
static int power_on(void)
|
||||
{
|
||||
/* Enable 5v power rail */
|
||||
gpio_set_level(GPIO_EN_PP5000, 1);
|
||||
/* wait to have stable power */
|
||||
usleep(DELAY_5V_SETUP);
|
||||
|
||||
/* Startup PMIC */
|
||||
gpio_set_level(GPIO_PMIC_PWRON_L, 0);
|
||||
/* wait for all PMIC regulators to be ready */
|
||||
wait_in_signal(GPIO_PP1800_LDO2, 1, PMIC_TIMEOUT);
|
||||
|
||||
/*
|
||||
* If PP1800_LDO2 did not come up (e.g. PMIC_TIMEOUT was reached),
|
||||
* turn off 5v rail and start over.
|
||||
*/
|
||||
if (gpio_get_level(GPIO_PP1800_LDO2) == 0) {
|
||||
gpio_set_level(GPIO_EN_PP5000, 0);
|
||||
usleep(DELAY_5V_SETUP);
|
||||
CPUTS("Fatal error: PMIC failed to enable\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Enable DDR 1.35v power rail */
|
||||
gpio_set_level(GPIO_EN_PP1350, 1);
|
||||
/* wait to avoid large inrush current */
|
||||
usleep(DELAY_RAIL_STAGGERING);
|
||||
/* Enable 3.3v power rail */
|
||||
gpio_set_level(GPIO_EN_PP3300, 1);
|
||||
CPUTS("AP running ...\n");
|
||||
ap_on = 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Wait for the power button to be released
|
||||
*
|
||||
* @return 0 if ok, -1 if power button failed to release
|
||||
*/
|
||||
static int wait_for_power_button_release(unsigned int timeout_us)
|
||||
{
|
||||
/* wait for Power button release */
|
||||
wait_in_signal(GPIO_KB_PWR_ON_L, 1, timeout_us);
|
||||
|
||||
udelay(KB_PWR_ON_DEBOUNCE);
|
||||
if (gpio_get_level(GPIO_KB_PWR_ON_L) == 0) {
|
||||
CPUTS("Power button was not released in time\n");
|
||||
return -1;
|
||||
}
|
||||
CPUTS("Power button released\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Power off the AP
|
||||
*/
|
||||
static void power_off(void)
|
||||
{
|
||||
/* switch off all rails */
|
||||
gpio_set_level(GPIO_EN_PP3300, 0);
|
||||
gpio_set_level(GPIO_EN_PP1350, 0);
|
||||
gpio_set_level(GPIO_PMIC_PWRON_L, 1);
|
||||
gpio_set_level(GPIO_EN_PP5000, 0);
|
||||
CPUTS("Shutdown complete.\n");
|
||||
ap_on = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set a timer to release GPIO_PMIC_PWRON_L in the future
|
||||
*/
|
||||
static void set_pwron_timer(void)
|
||||
{
|
||||
pwron_deadline = get_time();
|
||||
pwron_deadline.val += DELAY_RELEASE_PWRON;
|
||||
CPRINTF("Setting pwron timer %d\n", pwron_deadline.val);
|
||||
pwron_released = 0;
|
||||
}
|
||||
|
||||
static void check_pwron_release(void)
|
||||
{
|
||||
if (!pwron_released && timestamp_expired(pwron_deadline, NULL)) {
|
||||
pwron_deadline.val = 0;
|
||||
pwron_released = 1;
|
||||
gpio_set_level(GPIO_PMIC_PWRON_L, 1);
|
||||
CPRINTF("Releasing pwron\n");
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Calculates the delay in microseconds to the next time we have to check
|
||||
* for a power event,
|
||||
*
|
||||
*@return delay to next check, or -1 if no future check is needed
|
||||
*/
|
||||
static int next_pwr_event(void)
|
||||
{
|
||||
uint64_t next;
|
||||
|
||||
if (!pwron_deadline.val && !power_off_deadline.val)
|
||||
return -1;
|
||||
|
||||
/* We know that pwron_deadline will be earlier, if it exists */
|
||||
next = pwron_deadline.val ? pwron_deadline.val
|
||||
: power_off_deadline.val;
|
||||
return next - get_time().val;
|
||||
}
|
||||
|
||||
|
||||
/*****************************************************************************/
|
||||
|
||||
void gaia_power_task(void)
|
||||
{
|
||||
int value;
|
||||
|
||||
gaia_power_init();
|
||||
ap_on = 0;
|
||||
|
||||
while (1) {
|
||||
/* Power OFF state */
|
||||
ap_on = 0;
|
||||
/* Wait until we need to power on, then power on */
|
||||
while (!check_for_power_on_event())
|
||||
task_wait_event(-1);
|
||||
|
||||
/* wait for Power button press */
|
||||
wait_in_signal(GPIO_KB_PWR_ON_L, 0, -1);
|
||||
|
||||
usleep(KB_PWR_ON_DEBOUNCE);
|
||||
if (gpio_get_level(GPIO_KB_PWR_ON_L) == 1)
|
||||
continue;
|
||||
|
||||
/* Enable 5v power rail */
|
||||
gpio_set_level(GPIO_EN_PP5000, 1);
|
||||
/* wait to have stable power */
|
||||
usleep(DELAY_5V_SETUP);
|
||||
|
||||
/* Startup PMIC */
|
||||
gpio_set_level(GPIO_PMIC_PWRON_L, 0);
|
||||
/* wait for all PMIC regulators to be ready */
|
||||
wait_in_signal(GPIO_PP1800_LDO2, 1, PMIC_TIMEOUT);
|
||||
|
||||
/* if PP1800_LDO2 did not come up (e.g. PMIC_TIMEOUT was
|
||||
* reached), turn off 5v rail and start over */
|
||||
if (gpio_get_level(GPIO_PP1800_LDO2) == 0) {
|
||||
gpio_set_level(GPIO_EN_PP5000, 0);
|
||||
usleep(DELAY_5V_SETUP);
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Enable DDR 1.35v power rail */
|
||||
gpio_set_level(GPIO_EN_PP1350, 1);
|
||||
/* wait to avoid large inrush current */
|
||||
usleep(DELAY_RAIL_STAGGERING);
|
||||
/* Enable 3.3v power rail */
|
||||
gpio_set_level(GPIO_EN_PP3300, 1);
|
||||
|
||||
/* wait for the Application Processor to take control of the
|
||||
* PMIC.
|
||||
/*
|
||||
* If we can power on, and the power button is released,
|
||||
* start running!
|
||||
*/
|
||||
wait_in_signal(GPIO_SOC1V8_XPSHOLD, 1, FAIL_TIMEOUT);
|
||||
/* release PMIC startup signal */
|
||||
gpio_set_level(GPIO_PMIC_PWRON_L, 1);
|
||||
|
||||
/* Power ON state */
|
||||
ap_on = 1;
|
||||
CPUTS("AP running ...\n");
|
||||
|
||||
/* Wait for power off from AP or long power button press */
|
||||
wait_for_power_off();
|
||||
/* switch off all rails */
|
||||
gpio_set_level(GPIO_EN_PP3300, 0);
|
||||
gpio_set_level(GPIO_EN_PP1350, 0);
|
||||
gpio_set_level(GPIO_EN_PP5000, 0);
|
||||
CPUTS("Shutdown complete.\n");
|
||||
|
||||
/* Ensure the power button is released */
|
||||
wait_in_signal(GPIO_KB_PWR_ON_L, 1, -1);
|
||||
if (!power_on() && !wait_for_power_button_release(
|
||||
DELAY_SHUTDOWN_ON_POWER_HOLD)) {
|
||||
/* Wait until we need to power off, then power off */
|
||||
power_button_was_pressed = 0;
|
||||
set_pwron_timer();
|
||||
while (value = check_for_power_off_event(), !value) {
|
||||
check_pwron_release();
|
||||
task_wait_event(next_pwr_event());
|
||||
}
|
||||
CPRINTF("ending loop %d\n", value);
|
||||
}
|
||||
power_off();
|
||||
wait_for_power_button_release(-1);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user