summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRandall Spangler <rspangler@chromium.org>2013-07-12 14:07:25 -0700
committerChromeBot <chrome-bot@google.com>2013-07-17 10:49:48 -0700
commit672057cb7e242574079b05e3028c0e337aa450e5 (patch)
treed5d76796ca1ff024fe3aa4bdcffca5d5cc6d2ae8
parentd48828757de2274938cf8ec862689c935adaae33 (diff)
downloadchrome-ec-672057cb7e242574079b05e3028c0e337aa450e5.tar.gz
Split x86 power button logic out of switch.c
Power button logic is common across all platforms and is not LM4-specific, so move it to its own module. Switch.c will eventually be moving to common/ and will common across all platforms (not just x86), and splitting out the x86 power button logic is needed before that too. BUG=chrome-os-partner:18343 BRANCH=none TEST=manual 1) power on system with both lid and power button. 2) power+refresh -> reboots 3) power+refresh+esc -> recovery mode 4) power+refresh+downarrow -> reboots, AP stays off 5) toggling recovery GPIO via servo should generate SW debug output showing bit 0x10 toggling Change-Id: I07714e2c035dceece66f90407983397d2697e7d5 Signed-off-by: Randall Spangler <rspangler@chromium.org> Reviewed-on: https://gerrit.chromium.org/gerrit/61780
-rw-r--r--board/bds/board.h3
-rw-r--r--board/falco/board.h1
-rw-r--r--board/falco/ec.tasklist2
-rw-r--r--board/link/board.h1
-rw-r--r--board/link/ec.tasklist2
-rw-r--r--board/peppy/board.h1
-rw-r--r--board/peppy/ec.tasklist2
-rw-r--r--board/slippy/board.h1
-rw-r--r--board/slippy/ec.tasklist2
-rw-r--r--board/wolf/board.h1
-rw-r--r--board/wolf/ec.tasklist2
-rw-r--r--chip/lm4/build.mk4
-rw-r--r--chip/lm4/config_chip.h1
-rw-r--r--chip/lm4/switch.c441
-rw-r--r--common/build.mk1
-rw-r--r--common/charge_state.c9
-rw-r--r--common/power_button_x86.c399
-rw-r--r--include/switch.h9
18 files changed, 456 insertions, 426 deletions
diff --git a/board/bds/board.h b/board/bds/board.h
index 0abb267637..00127fd8c0 100644
--- a/board/bds/board.h
+++ b/board/bds/board.h
@@ -21,6 +21,9 @@
#define CONFIG_EOPTION
#define CONFIG_PSTORE
+/* LM4 modules we want to exclude */
+#undef CONFIG_SWITCH
+
/* Write protect is active high */
#define CONFIG_WP_ACTIVE_HIGH
diff --git a/board/falco/board.h b/board/falco/board.h
index 9cecb1d000..7a9e8ce580 100644
--- a/board/falco/board.h
+++ b/board/falco/board.h
@@ -35,6 +35,7 @@
#define CONFIG_LPC
#define CONFIG_PECI
#define CONFIG_POWER_BUTTON
+#define CONFIG_POWER_BUTTON_X86
#define CONFIG_PWM_FAN
#define CONFIG_TEMP_SENSOR
#define CONFIG_TEMP_SENSOR_G781
diff --git a/board/falco/ec.tasklist b/board/falco/ec.tasklist
index 091e8b340e..40d5ec4f60 100644
--- a/board/falco/ec.tasklist
+++ b/board/falco/ec.tasklist
@@ -25,5 +25,5 @@
TASK_NOTEST(KEYPROTO, keyboard_protocol_task, NULL, TASK_STACK_SIZE) \
TASK_ALWAYS(HOSTCMD, host_command_task, NULL, TASK_STACK_SIZE) \
TASK_ALWAYS(CONSOLE, console_task, NULL, LARGER_TASK_STACK_SIZE) \
- TASK_ALWAYS(SWITCH, switch_task, NULL, TASK_STACK_SIZE) \
+ TASK_ALWAYS(POWERBTN, power_button_task, NULL, TASK_STACK_SIZE) \
TASK_NOTEST(KEYSCAN, keyboard_scan_task, NULL, TASK_STACK_SIZE)
diff --git a/board/link/board.h b/board/link/board.h
index 3c6900c497..abcdf04508 100644
--- a/board/link/board.h
+++ b/board/link/board.h
@@ -38,6 +38,7 @@
#define CONFIG_ONEWIRE_LED
#define CONFIG_PECI
#define CONFIG_POWER_BUTTON
+#define CONFIG_POWER_BUTTON_X86
#define CONFIG_PWM_FAN
#define CONFIG_PWM_KBLIGHT
#define CONFIG_TEMP_SENSOR
diff --git a/board/link/ec.tasklist b/board/link/ec.tasklist
index 07a0eb1d29..2b3233e8e0 100644
--- a/board/link/ec.tasklist
+++ b/board/link/ec.tasklist
@@ -26,5 +26,5 @@
TASK_NOTEST(KEYPROTO, keyboard_protocol_task, NULL, TASK_STACK_SIZE) \
TASK_ALWAYS(HOSTCMD, host_command_task, NULL, TASK_STACK_SIZE) \
TASK_ALWAYS(CONSOLE, console_task, NULL, LARGER_TASK_STACK_SIZE) \
- TASK_ALWAYS(SWITCH, switch_task, NULL, TASK_STACK_SIZE) \
+ TASK_ALWAYS(POWERBTN, power_button_task, NULL, TASK_STACK_SIZE) \
TASK_NOTEST(KEYSCAN, keyboard_scan_task, NULL, TASK_STACK_SIZE)
diff --git a/board/peppy/board.h b/board/peppy/board.h
index d5ce4c54a7..6651aa8961 100644
--- a/board/peppy/board.h
+++ b/board/peppy/board.h
@@ -34,6 +34,7 @@
#define CONFIG_LPC
#define CONFIG_PECI
#define CONFIG_POWER_BUTTON
+#define CONFIG_POWER_BUTTON_X86
#define CONFIG_PWM_FAN
#define CONFIG_TEMP_SENSOR
#define CONFIG_TEMP_SENSOR_G781
diff --git a/board/peppy/ec.tasklist b/board/peppy/ec.tasklist
index 091e8b340e..40d5ec4f60 100644
--- a/board/peppy/ec.tasklist
+++ b/board/peppy/ec.tasklist
@@ -25,5 +25,5 @@
TASK_NOTEST(KEYPROTO, keyboard_protocol_task, NULL, TASK_STACK_SIZE) \
TASK_ALWAYS(HOSTCMD, host_command_task, NULL, TASK_STACK_SIZE) \
TASK_ALWAYS(CONSOLE, console_task, NULL, LARGER_TASK_STACK_SIZE) \
- TASK_ALWAYS(SWITCH, switch_task, NULL, TASK_STACK_SIZE) \
+ TASK_ALWAYS(POWERBTN, power_button_task, NULL, TASK_STACK_SIZE) \
TASK_NOTEST(KEYSCAN, keyboard_scan_task, NULL, TASK_STACK_SIZE)
diff --git a/board/slippy/board.h b/board/slippy/board.h
index 44913b9e12..d27d3ec54b 100644
--- a/board/slippy/board.h
+++ b/board/slippy/board.h
@@ -33,6 +33,7 @@
#define CONFIG_LPC
#define CONFIG_PECI
#define CONFIG_POWER_BUTTON
+#define CONFIG_POWER_BUTTON_X86
#define CONFIG_PWM_FAN
#define CONFIG_TEMP_SENSOR
#define CONFIG_USB_PORT_POWER_DUMB
diff --git a/board/slippy/ec.tasklist b/board/slippy/ec.tasklist
index 091e8b340e..40d5ec4f60 100644
--- a/board/slippy/ec.tasklist
+++ b/board/slippy/ec.tasklist
@@ -25,5 +25,5 @@
TASK_NOTEST(KEYPROTO, keyboard_protocol_task, NULL, TASK_STACK_SIZE) \
TASK_ALWAYS(HOSTCMD, host_command_task, NULL, TASK_STACK_SIZE) \
TASK_ALWAYS(CONSOLE, console_task, NULL, LARGER_TASK_STACK_SIZE) \
- TASK_ALWAYS(SWITCH, switch_task, NULL, TASK_STACK_SIZE) \
+ TASK_ALWAYS(POWERBTN, power_button_task, NULL, TASK_STACK_SIZE) \
TASK_NOTEST(KEYSCAN, keyboard_scan_task, NULL, TASK_STACK_SIZE)
diff --git a/board/wolf/board.h b/board/wolf/board.h
index 98b036afbb..d015cec803 100644
--- a/board/wolf/board.h
+++ b/board/wolf/board.h
@@ -28,6 +28,7 @@
#define CONFIG_LPC
#define CONFIG_PECI
#define CONFIG_POWER_BUTTON
+#define CONFIG_POWER_BUTTON_X86
#define CONFIG_PWM_FAN
#define CONFIG_TEMP_SENSOR
#define CONFIG_USB_PORT_POWER_DUMB
diff --git a/board/wolf/ec.tasklist b/board/wolf/ec.tasklist
index dc735958f0..848b56830c 100644
--- a/board/wolf/ec.tasklist
+++ b/board/wolf/ec.tasklist
@@ -25,5 +25,5 @@
TASK_NOTEST(KEYPROTO, keyboard_protocol_task, NULL, TASK_STACK_SIZE) \
TASK_ALWAYS(HOSTCMD, host_command_task, NULL, TASK_STACK_SIZE) \
TASK_ALWAYS(CONSOLE, console_task, NULL, LARGER_TASK_STACK_SIZE) \
- TASK_ALWAYS(SWITCH, switch_task, NULL, TASK_STACK_SIZE) \
+ TASK_ALWAYS(POWERBTN, power_button_task, NULL, TASK_STACK_SIZE) \
TASK_NOTEST(KEYSCAN, keyboard_scan_task, NULL, TASK_STACK_SIZE)
diff --git a/chip/lm4/build.mk b/chip/lm4/build.mk
index a3178b88dc..39b14a5e77 100644
--- a/chip/lm4/build.mk
+++ b/chip/lm4/build.mk
@@ -23,6 +23,6 @@ chip-$(CONFIG_PECI)+=peci.o
chip-$(CONFIG_PWM_FAN)+=pwm_fan.o
chip-$(CONFIG_PWM_KBLIGHT)+=pwm_kblight.o
chip-$(CONFIG_SPI)+=spi.o
-chip-$(HAS_TASK_KEYSCAN)+=keyboard_raw.o
-chip-$(HAS_TASK_SWITCH)+=switch.o
+chip-$(CONFIG_SWITCH)+=switch.o
chip-$(CONFIG_WATCHDOG)+=watchdog.o
+chip-$(HAS_TASK_KEYSCAN)+=keyboard_raw.o
diff --git a/chip/lm4/config_chip.h b/chip/lm4/config_chip.h
index 849f68087d..1c7fdfb48b 100644
--- a/chip/lm4/config_chip.h
+++ b/chip/lm4/config_chip.h
@@ -104,6 +104,7 @@
#define CONFIG_FMAP
#define CONFIG_FPU
#define CONFIG_I2C
+#define CONFIG_SWITCH
#define CONFIG_WATCHDOG
/* Compile for running from RAM instead of flash */
diff --git a/chip/lm4/switch.c b/chip/lm4/switch.c
index 40b41a039d..73f1c16edb 100644
--- a/chip/lm4/switch.c
+++ b/chip/lm4/switch.c
@@ -3,123 +3,51 @@
* found in the LICENSE file.
*/
-/* Power button and lid switch module for Chrome EC */
+/* Switch module for Chrome EC */
-#include "charge_state.h"
-#include "chipset.h"
#include "common.h"
#include "console.h"
#include "flash.h"
#include "gpio.h"
#include "hooks.h"
#include "host_command.h"
-#include "keyboard_protocol.h"
-#include "keyboard_scan.h"
#include "lid_switch.h"
#include "power_button.h"
-#include "pwm.h"
#include "switch.h"
-#include "system.h"
-#include "task.h"
-#include "timer.h"
#include "util.h"
/* Console output macros */
#define CPUTS(outstr) cputs(CC_SWITCH, outstr)
#define CPRINTF(format, args...) cprintf(CC_SWITCH, format, ## args)
-/*
- * When chipset is on, we stretch the power button signal to it so chipset
- * hard-reset is triggered at ~8 sec, not ~4 sec:
- *
- * PWRBTN# --- ----
- * to EC |______________________|
- *
- *
- * PWRBTN# --- --------- ----
- * to PCH |__| |___________|
- * t0 t1 held down
- *
- * scan code | |
- * to host v v
- * @S0 make code break code
- */
-/* TODO: link to full power button / lid switch state machine description. */
-#define PWRBTN_DELAY_T0 (32 * MSEC) /* 32ms (PCH requires >16ms) */
-#define PWRBTN_DELAY_T1 (4 * SECOND - PWRBTN_DELAY_T0) /* 4 secs - t0 */
-/*
- * Length of time to stretch initial power button press to give chipset a
- * chance to wake up (~100ms) and react to the press (~16ms). Also used as
- * pulse length for simulated power button presses when the system is off.
- */
-#define PWRBTN_INITIAL_US (200 * MSEC)
-
-enum power_button_state {
- /* Button up; state machine idle */
- PWRBTN_STATE_IDLE = 0,
- /* Button pressed; debouncing done */
- PWRBTN_STATE_PRESSED,
- /* Button down, chipset on; sending initial short pulse */
- PWRBTN_STATE_T0,
- /* Button down, chipset on; delaying until we should reassert signal */
- PWRBTN_STATE_T1,
- /* Button down, signal asserted to chipset */
- PWRBTN_STATE_HELD,
- /* Force pulse due to lid-open event */
- PWRBTN_STATE_LID_OPEN,
- /* Button released; debouncing done */
- PWRBTN_STATE_RELEASED,
- /* Ignore next button release */
- PWRBTN_STATE_EAT_RELEASE,
- /*
- * Need to power on system after init, but waiting to find out if
- * sufficient battery power.
- */
- PWRBTN_STATE_INIT_ON,
- /* Forced pulse at EC boot due to keyboard controlled reset */
- PWRBTN_STATE_BOOT_KB_RESET,
- /* Power button pressed when chipset was off; stretching pulse */
- PWRBTN_STATE_WAS_OFF,
-};
-static enum power_button_state pwrbtn_state = PWRBTN_STATE_IDLE;
-
-static const char * const state_names[] = {
- "idle",
- "pressed",
- "t0",
- "t1",
- "held",
- "lid-open",
- "released",
- "eat-release",
- "init-on",
- "recovery",
- "was-off",
-};
-
-/*
- * Time for next state transition of power button state machine, or 0 if the
- * state doesn't have a timeout.
- */
-static uint64_t tnext_state;
-
-/*
- * Debounce timeout for power button. 0 means the signal is stable (not being
- * debounced).
- */
-static uint64_t tdebounce_pwr;
-
static uint8_t *memmap_switches;
/**
* Update status of non-debounced switches.
+ *
+ * Note that deferred functions are called in the same context as lid and
+ * power button changes, so we don't need a mutex.
*/
-static void update_other_switches(void)
+static void switch_update(void)
{
+ static uint8_t prev;
+
/* Make sure this is safe to call before power_button_init() */
if (!memmap_switches)
return;
+ prev = *memmap_switches;
+
+ if (power_button_is_pressed())
+ *memmap_switches |= EC_SWITCH_POWER_BUTTON_PRESSED;
+ else
+ *memmap_switches &= ~EC_SWITCH_POWER_BUTTON_PRESSED;
+
+ if (lid_is_open())
+ *memmap_switches |= EC_SWITCH_LID_OPEN;
+ else
+ *memmap_switches &= ~EC_SWITCH_LID_OPEN;
+
if ((flash_get_protect() & EC_FLASH_PROTECT_GPIO_ASSERTED) == 0)
*memmap_switches |= EC_SWITCH_WRITE_PROTECT_DISABLED;
else
@@ -129,259 +57,13 @@ static void update_other_switches(void)
*memmap_switches |= EC_SWITCH_DEDICATED_RECOVERY;
else
*memmap_switches &= ~EC_SWITCH_DEDICATED_RECOVERY;
-}
-
-static void set_pwrbtn_to_pch(int high)
-{
- /*
- * If the battery is discharging and low enough we'd shut down the
- * system, don't press the power button.
- */
- if (!high && charge_want_shutdown()) {
- CPRINTF("[%T PB PCH pwrbtn ignored due to battery level\n");
- high = 1;
- }
-
- CPRINTF("[%T PB PCH pwrbtn=%s]\n", high ? "HIGH" : "LOW");
- gpio_set_level(GPIO_PCH_PWRBTN_L, high);
-}
-
-/**
- * Handle debounced power button down.
- */
-static void power_button_pressed(uint64_t tnow)
-{
- CPRINTF("[%T PB pressed]\n");
- pwrbtn_state = PWRBTN_STATE_PRESSED;
- tnext_state = tnow;
- *memmap_switches |= EC_SWITCH_POWER_BUTTON_PRESSED;
-}
-
-/**
- * Handle debounced power button up.
- */
-static void power_button_released(uint64_t tnow)
-{
- CPRINTF("[%T PB released]\n");
- pwrbtn_state = PWRBTN_STATE_RELEASED;
- tnext_state = tnow;
- *memmap_switches &= ~EC_SWITCH_POWER_BUTTON_PRESSED;
-}
-/**
- * Set initial power button state.
- */
-static void set_initial_pwrbtn_state(void)
-{
- uint32_t reset_flags = system_get_reset_flags();
-
- if (system_jumped_to_this_image() &&
- chipset_in_state(CHIPSET_STATE_ON)) {
- /*
- * Jumped to this image while the chipset was already on, so
- * simply reflect the actual power button state.
- */
- if (power_button_is_pressed()) {
- *memmap_switches |= EC_SWITCH_POWER_BUTTON_PRESSED;
- CPRINTF("[%T PB init-jumped-held]\n");
- set_pwrbtn_to_pch(0);
- } else {
- CPRINTF("[%T PB init-jumped]\n");
- }
- } else if ((reset_flags & RESET_FLAG_AP_OFF) ||
- (keyboard_scan_get_boot_key() == BOOT_KEY_DOWN_ARROW)) {
- /*
- * Reset triggered by keyboard-controlled reset, and down-arrow
- * was held down. Or reset flags request AP off.
- *
- * Leave the main processor off. This is a fail-safe
- * combination for debugging failures booting the main
- * processor.
- *
- * Don't let the PCH see that the power button was pressed.
- * Otherwise, it might power on.
- */
- CPRINTF("[%T PB init-off]\n");
- set_pwrbtn_to_pch(1);
- if (power_button_is_pressed())
- pwrbtn_state = PWRBTN_STATE_EAT_RELEASE;
- else
- pwrbtn_state = PWRBTN_STATE_IDLE;
- } else {
- /*
- * All other EC reset conditions power on the main processor so
- * it can verify the EC.
- */
- CPRINTF("[%T PB init-on]\n");
- pwrbtn_state = PWRBTN_STATE_INIT_ON;
- }
+ if (prev != *memmap_switches)
+ CPRINTF("[%T SW 0x%02x]\n", *memmap_switches);
}
-
-/*****************************************************************************/
-/* Task / state machine */
-
-/**
- * Power button state machine.
- *
- * @param tnow Current time from usec counter
- */
-static void state_machine(uint64_t tnow)
-{
- /* Not the time to move onto next state */
- if (tnow < tnext_state)
- return;
-
- /* States last forever unless otherwise specified */
- tnext_state = 0;
-
- switch (pwrbtn_state) {
- case PWRBTN_STATE_PRESSED:
- if (chipset_in_state(CHIPSET_STATE_ANY_OFF)) {
- /*
- * Chipset is off, so wake the chipset and send it a
- * long enough pulse to wake up. After that we'll
- * reflect the true power button state. If we don't
- * stretch the pulse here, the user may release the
- * power button before the chipset finishes waking from
- * hard off state.
- */
- chipset_exit_hard_off();
- tnext_state = tnow + PWRBTN_INITIAL_US;
- pwrbtn_state = PWRBTN_STATE_WAS_OFF;
- } else {
- /* Chipset is on, so send the chipset a pulse */
- tnext_state = tnow + PWRBTN_DELAY_T0;
- pwrbtn_state = PWRBTN_STATE_T0;
- }
- set_pwrbtn_to_pch(0);
- break;
- case PWRBTN_STATE_T0:
- tnext_state = tnow + PWRBTN_DELAY_T1;
- pwrbtn_state = PWRBTN_STATE_T1;
- set_pwrbtn_to_pch(1);
- break;
- case PWRBTN_STATE_T1:
- /*
- * If the chipset is already off, don't tell it the power
- * button is down; it'll just cause the chipset to turn on
- * again.
- */
- if (chipset_in_state(CHIPSET_STATE_ANY_OFF))
- CPRINTF("[%T PB chipset already off]\n");
- else
- set_pwrbtn_to_pch(0);
- pwrbtn_state = PWRBTN_STATE_HELD;
- break;
- case PWRBTN_STATE_RELEASED:
- case PWRBTN_STATE_LID_OPEN:
- set_pwrbtn_to_pch(1);
- pwrbtn_state = PWRBTN_STATE_IDLE;
- break;
- case PWRBTN_STATE_INIT_ON:
- /*
- * Don't do anything until the charger knows the battery level.
- * Otherwise we could power on the AP only to shut it right
- * back down due to insufficient battery.
- */
-#ifdef HAS_TASK_CHARGER
- if (charge_get_state() == PWR_STATE_INIT)
- break;
-#endif
-
- /*
- * Power the system on if possible. Gating due to insufficient
- * battery is handled inside set_pwrbtn_to_pch().
- */
- chipset_exit_hard_off();
- set_pwrbtn_to_pch(0);
- tnext_state = get_time().val + PWRBTN_INITIAL_US;
-
- if (power_button_is_pressed()) {
- *memmap_switches |= EC_SWITCH_POWER_BUTTON_PRESSED;
-
- if (system_get_reset_flags() & RESET_FLAG_RESET_PIN)
- pwrbtn_state = PWRBTN_STATE_BOOT_KB_RESET;
- else
- pwrbtn_state = PWRBTN_STATE_WAS_OFF;
- } else {
- pwrbtn_state = PWRBTN_STATE_RELEASED;
- }
-
- break;
-
- case PWRBTN_STATE_BOOT_KB_RESET:
- /* Initial forced pulse is done. Ignore the actual power
- * button until it's released, so that holding down the
- * recovery combination doesn't cause the chipset to shut back
- * down. */
- set_pwrbtn_to_pch(1);
- if (power_button_is_pressed())
- pwrbtn_state = PWRBTN_STATE_EAT_RELEASE;
- else
- pwrbtn_state = PWRBTN_STATE_IDLE;
- break;
- case PWRBTN_STATE_WAS_OFF:
- /* Done stretching initial power button signal, so show the
- * true power button state to the PCH. */
- if (power_button_is_pressed()) {
- /* User is still holding the power button */
- pwrbtn_state = PWRBTN_STATE_HELD;
- } else {
- /* Stop stretching the power button press */
- power_button_released(tnow);
- }
- break;
- case PWRBTN_STATE_IDLE:
- case PWRBTN_STATE_HELD:
- case PWRBTN_STATE_EAT_RELEASE:
- /* Do nothing */
- break;
- }
-}
-
-void switch_task(void)
-{
- uint64_t t;
- uint64_t tsleep;
-
- while (1) {
- t = get_time().val;
-
- /* Handle non-debounced switches */
- update_other_switches();
-
- /* Update state machine */
- CPRINTF("[%T PB task %d = %s, sw 0x%02x]\n", pwrbtn_state,
- state_names[pwrbtn_state], *memmap_switches);
-
- state_machine(t);
-
- /* Sleep until our next timeout */
- tsleep = -1;
- if (tdebounce_pwr && tdebounce_pwr < tsleep)
- tsleep = tdebounce_pwr;
- if (tnext_state && tnext_state < tsleep)
- tsleep = tnext_state;
- t = get_time().val;
- if (tsleep > t) {
- unsigned d = tsleep == -1 ? -1 : (unsigned)(tsleep - t);
- /*
- * (Yes, the conversion from uint64_t to unsigned could
- * theoretically overflow if we wanted to sleep for
- * more than 2^32 us, but our timeouts are small enough
- * that can't happen - and even if it did, we'd just go
- * back to sleep after deciding that we woke up too
- * early.)
- */
- CPRINTF("[%T PB task %d = %s, wait %d]\n", pwrbtn_state,
- state_names[pwrbtn_state], d);
- task_wait_event(d);
- }
- }
-}
-
-/*****************************************************************************/
-/* Hooks */
+DECLARE_DEFERRED(switch_update);
+DECLARE_HOOK(HOOK_LID_CHANGE, switch_update, HOOK_PRIO_DEFAULT);
+DECLARE_HOOK(HOOK_POWER_BUTTON_CHANGE, switch_update, HOOK_PRIO_DEFAULT);
static void switch_init(void)
{
@@ -389,18 +71,18 @@ static void switch_init(void)
memmap_switches = host_get_memmap(EC_MEMMAP_SWITCHES);
*memmap_switches = 0;
- if (lid_is_open())
- *memmap_switches |= EC_SWITCH_LID_OPEN;
-
- update_other_switches();
- set_initial_pwrbtn_state();
+ switch_update();
/* Switch data is now present */
*host_get_memmap(EC_MEMMAP_SWITCHES_VERSION) = 1;
/* Enable interrupts, now that we've initialized */
- gpio_enable_interrupt(GPIO_POWER_BUTTON_L);
gpio_enable_interrupt(GPIO_RECOVERY_L);
+
+ /*
+ * TODO(rspangler): It's weird that flash_common.c owns reading the
+ * write protect signal, but we enable the interrupt for it here.
+ */
#ifdef CONFIG_WP_ACTIVE_HIGH
gpio_enable_interrupt(GPIO_WP);
#else
@@ -409,70 +91,9 @@ static void switch_init(void)
}
DECLARE_HOOK(HOOK_INIT, switch_init, HOOK_PRIO_DEFAULT);
-/**
- * Handle switch changes based on lid event.
- */
-static void switch_lid_change(void)
-{
- if (lid_is_open()) {
- *memmap_switches |= EC_SWITCH_LID_OPEN;
-
- /* If the chipset is off, pulse the power button to wake it. */
- if (chipset_in_state(CHIPSET_STATE_ANY_OFF)) {
- chipset_exit_hard_off();
- set_pwrbtn_to_pch(0);
- pwrbtn_state = PWRBTN_STATE_LID_OPEN;
- tnext_state = get_time().val + PWRBTN_INITIAL_US;
- task_wake(TASK_ID_SWITCH);
- }
-
- } else {
- *memmap_switches &= ~EC_SWITCH_LID_OPEN;
- }
-}
-DECLARE_HOOK(HOOK_LID_CHANGE, switch_lid_change, HOOK_PRIO_DEFAULT);
-
-/**
- * Handle debounced power button changing state.
- */
-static void power_button_changed(void)
-{
- if (pwrbtn_state == PWRBTN_STATE_BOOT_KB_RESET ||
- pwrbtn_state == PWRBTN_STATE_INIT_ON ||
- pwrbtn_state == PWRBTN_STATE_LID_OPEN ||
- pwrbtn_state == PWRBTN_STATE_WAS_OFF) {
- /* Ignore all power button changes during an initial pulse */
- CPRINTF("[%T PB ignoring change]\n");
- return;
- }
-
- if (power_button_is_pressed()) {
- /* Power button pressed */
- power_button_pressed(get_time().val);
- } else {
- /* Power button released */
- if (pwrbtn_state == PWRBTN_STATE_EAT_RELEASE) {
- /*
- * Ignore the first power button release if we already
- * told the PCH the power button was released.
- */
- CPRINTF("[%T PB ignoring release]\n");
- pwrbtn_state = PWRBTN_STATE_IDLE;
- return;
- }
-
- power_button_released(get_time().val);
- }
-
- /* Wake the switch task */
- task_wake(TASK_ID_SWITCH);
-}
-DECLARE_HOOK(HOOK_POWER_BUTTON_CHANGE, power_button_changed, HOOK_PRIO_DEFAULT);
-
void switch_interrupt(enum gpio_signal signal)
{
- /* Wake task to handle change in non-debounced switches */
- task_wake(TASK_ID_SWITCH);
+ hook_call_deferred(switch_update, 0);
}
static int command_mmapinfo(int argc, char **argv)
diff --git a/common/build.mk b/common/build.mk
index 0476fcb8b1..b5d717438a 100644
--- a/common/build.mk
+++ b/common/build.mk
@@ -45,6 +45,7 @@ common-$(CONFIG_LID_SWITCH)+=lid_switch.o
common-$(CONFIG_LPC)+=port80.o
common-$(CONFIG_ONEWIRE_LED)+=onewire_led.o
common-$(CONFIG_POWER_BUTTON)+=power_button.o
+common-$(CONFIG_POWER_BUTTON_X86)+=power_button_x86.o
common-$(CONFIG_PSTORE)+=pstore_commands.o
common-$(CONFIG_REGULATOR_IR357X)+=regulator_ir357x.o
common-$(CONFIG_SMART_BATTERY)+=smart_battery.o smart_battery_stub.o
diff --git a/common/charge_state.c b/common/charge_state.c
index 83df8ad8a5..02c556a54e 100644
--- a/common/charge_state.c
+++ b/common/charge_state.c
@@ -668,13 +668,18 @@ void charger_task(void)
state_name[new_state]);
}
+#ifdef HAS_TASK_POWERBTN
/*
- * After first init, wake the switch task so it can
+ * After first init, wake the power button task so it can
* power on the AP if necessary.
+ *
+ * TODO(rspangler): API, instead of assuming power button task
+ * exists
*/
if (ctx->prev.state == PWR_STATE_INIT &&
new_state != PWR_STATE_INIT)
- task_wake(TASK_ID_SWITCH);
+ task_wake(TASK_ID_POWERBTN);
+#endif
switch (new_state) {
case PWR_STATE_IDLE0:
diff --git a/common/power_button_x86.c b/common/power_button_x86.c
new file mode 100644
index 0000000000..2d353b74ed
--- /dev/null
+++ b/common/power_button_x86.c
@@ -0,0 +1,399 @@
+/* Copyright (c) 2013 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 button state machine for x86 platforms */
+
+#include "charge_state.h"
+#include "chipset.h"
+#include "common.h"
+#include "console.h"
+#include "gpio.h"
+#include "hooks.h"
+#include "host_command.h"
+#include "keyboard_scan.h"
+#include "lid_switch.h"
+#include "power_button.h"
+#include "switch.h"
+#include "system.h"
+#include "task.h"
+#include "timer.h"
+#include "util.h"
+
+/* Console output macros */
+#define CPUTS(outstr) cputs(CC_SWITCH, outstr)
+#define CPRINTF(format, args...) cprintf(CC_SWITCH, format, ## args)
+
+/*
+ * When chipset is on, we stretch the power button signal to it so chipset
+ * hard-reset is triggered at ~8 sec, not ~4 sec:
+ *
+ * PWRBTN# --- ----
+ * to EC |______________________|
+ *
+ *
+ * PWRBTN# --- --------- ----
+ * to PCH |__| |___________|
+ * t0 t1 held down
+ *
+ * scan code | |
+ * to host v v
+ * @S0 make code break code
+ */
+/* TODO: link to full power button / lid switch state machine description. */
+#define PWRBTN_DELAY_T0 (32 * MSEC) /* 32ms (PCH requires >16ms) */
+#define PWRBTN_DELAY_T1 (4 * SECOND - PWRBTN_DELAY_T0) /* 4 secs - t0 */
+/*
+ * Length of time to stretch initial power button press to give chipset a
+ * chance to wake up (~100ms) and react to the press (~16ms). Also used as
+ * pulse length for simulated power button presses when the system is off.
+ */
+#define PWRBTN_INITIAL_US (200 * MSEC)
+
+enum power_button_state {
+ /* Button up; state machine idle */
+ PWRBTN_STATE_IDLE = 0,
+ /* Button pressed; debouncing done */
+ PWRBTN_STATE_PRESSED,
+ /* Button down, chipset on; sending initial short pulse */
+ PWRBTN_STATE_T0,
+ /* Button down, chipset on; delaying until we should reassert signal */
+ PWRBTN_STATE_T1,
+ /* Button down, signal asserted to chipset */
+ PWRBTN_STATE_HELD,
+ /* Force pulse due to lid-open event */
+ PWRBTN_STATE_LID_OPEN,
+ /* Button released; debouncing done */
+ PWRBTN_STATE_RELEASED,
+ /* Ignore next button release */
+ PWRBTN_STATE_EAT_RELEASE,
+ /*
+ * Need to power on system after init, but waiting to find out if
+ * sufficient battery power.
+ */
+ PWRBTN_STATE_INIT_ON,
+ /* Forced pulse at EC boot due to keyboard controlled reset */
+ PWRBTN_STATE_BOOT_KB_RESET,
+ /* Power button pressed when chipset was off; stretching pulse */
+ PWRBTN_STATE_WAS_OFF,
+};
+static enum power_button_state pwrbtn_state = PWRBTN_STATE_IDLE;
+
+static const char * const state_names[] = {
+ "idle",
+ "pressed",
+ "t0",
+ "t1",
+ "held",
+ "lid-open",
+ "released",
+ "eat-release",
+ "init-on",
+ "recovery",
+ "was-off",
+};
+
+/*
+ * Time for next state transition of power button state machine, or 0 if the
+ * state doesn't have a timeout.
+ */
+static uint64_t tnext_state;
+
+static void set_pwrbtn_to_pch(int high)
+{
+ /*
+ * If the battery is discharging and low enough we'd shut down the
+ * system, don't press the power button.
+ */
+ if (!high && charge_want_shutdown()) {
+ CPRINTF("[%T PB PCH pwrbtn ignored due to battery level\n");
+ high = 1;
+ }
+
+ CPRINTF("[%T PB PCH pwrbtn=%s]\n", high ? "HIGH" : "LOW");
+ gpio_set_level(GPIO_PCH_PWRBTN_L, high);
+}
+
+/**
+ * Handle debounced power button down.
+ */
+static void power_button_pressed(uint64_t tnow)
+{
+ CPRINTF("[%T PB pressed]\n");
+ pwrbtn_state = PWRBTN_STATE_PRESSED;
+ tnext_state = tnow;
+}
+
+/**
+ * Handle debounced power button up.
+ */
+static void power_button_released(uint64_t tnow)
+{
+ CPRINTF("[%T PB released]\n");
+ pwrbtn_state = PWRBTN_STATE_RELEASED;
+ tnext_state = tnow;
+}
+
+/**
+ * Set initial power button state.
+ */
+static void set_initial_pwrbtn_state(void)
+{
+ uint32_t reset_flags = system_get_reset_flags();
+
+ if (system_jumped_to_this_image() &&
+ chipset_in_state(CHIPSET_STATE_ON)) {
+ /*
+ * Jumped to this image while the chipset was already on, so
+ * simply reflect the actual power button state.
+ */
+ if (power_button_is_pressed()) {
+ CPRINTF("[%T PB init-jumped-held]\n");
+ set_pwrbtn_to_pch(0);
+ } else {
+ CPRINTF("[%T PB init-jumped]\n");
+ }
+ } else if ((reset_flags & RESET_FLAG_AP_OFF) ||
+ (keyboard_scan_get_boot_key() == BOOT_KEY_DOWN_ARROW)) {
+ /*
+ * Reset triggered by keyboard-controlled reset, and down-arrow
+ * was held down. Or reset flags request AP off.
+ *
+ * Leave the main processor off. This is a fail-safe
+ * combination for debugging failures booting the main
+ * processor.
+ *
+ * Don't let the PCH see that the power button was pressed.
+ * Otherwise, it might power on.
+ */
+ CPRINTF("[%T PB init-off]\n");
+ set_pwrbtn_to_pch(1);
+ if (power_button_is_pressed())
+ pwrbtn_state = PWRBTN_STATE_EAT_RELEASE;
+ else
+ pwrbtn_state = PWRBTN_STATE_IDLE;
+ } else {
+ /*
+ * All other EC reset conditions power on the main processor so
+ * it can verify the EC.
+ */
+ CPRINTF("[%T PB init-on]\n");
+ pwrbtn_state = PWRBTN_STATE_INIT_ON;
+ }
+}
+
+/**
+ * Power button state machine.
+ *
+ * @param tnow Current time from usec counter
+ */
+static void state_machine(uint64_t tnow)
+{
+ /* Not the time to move onto next state */
+ if (tnow < tnext_state)
+ return;
+
+ /* States last forever unless otherwise specified */
+ tnext_state = 0;
+
+ switch (pwrbtn_state) {
+ case PWRBTN_STATE_PRESSED:
+ if (chipset_in_state(CHIPSET_STATE_ANY_OFF)) {
+ /*
+ * Chipset is off, so wake the chipset and send it a
+ * long enough pulse to wake up. After that we'll
+ * reflect the true power button state. If we don't
+ * stretch the pulse here, the user may release the
+ * power button before the chipset finishes waking from
+ * hard off state.
+ */
+ chipset_exit_hard_off();
+ tnext_state = tnow + PWRBTN_INITIAL_US;
+ pwrbtn_state = PWRBTN_STATE_WAS_OFF;
+ } else {
+ /* Chipset is on, so send the chipset a pulse */
+ tnext_state = tnow + PWRBTN_DELAY_T0;
+ pwrbtn_state = PWRBTN_STATE_T0;
+ }
+ set_pwrbtn_to_pch(0);
+ break;
+ case PWRBTN_STATE_T0:
+ tnext_state = tnow + PWRBTN_DELAY_T1;
+ pwrbtn_state = PWRBTN_STATE_T1;
+ set_pwrbtn_to_pch(1);
+ break;
+ case PWRBTN_STATE_T1:
+ /*
+ * If the chipset is already off, don't tell it the power
+ * button is down; it'll just cause the chipset to turn on
+ * again.
+ */
+ if (chipset_in_state(CHIPSET_STATE_ANY_OFF))
+ CPRINTF("[%T PB chipset already off]\n");
+ else
+ set_pwrbtn_to_pch(0);
+ pwrbtn_state = PWRBTN_STATE_HELD;
+ break;
+ case PWRBTN_STATE_RELEASED:
+ case PWRBTN_STATE_LID_OPEN:
+ set_pwrbtn_to_pch(1);
+ pwrbtn_state = PWRBTN_STATE_IDLE;
+ break;
+ case PWRBTN_STATE_INIT_ON:
+ /*
+ * Don't do anything until the charger knows the battery level.
+ * Otherwise we could power on the AP only to shut it right
+ * back down due to insufficient battery.
+ */
+#ifdef HAS_TASK_CHARGER
+ if (charge_get_state() == PWR_STATE_INIT)
+ break;
+#endif
+
+ /*
+ * Power the system on if possible. Gating due to insufficient
+ * battery is handled inside set_pwrbtn_to_pch().
+ */
+ chipset_exit_hard_off();
+ set_pwrbtn_to_pch(0);
+ tnext_state = get_time().val + PWRBTN_INITIAL_US;
+
+ if (power_button_is_pressed()) {
+ if (system_get_reset_flags() & RESET_FLAG_RESET_PIN)
+ pwrbtn_state = PWRBTN_STATE_BOOT_KB_RESET;
+ else
+ pwrbtn_state = PWRBTN_STATE_WAS_OFF;
+ } else {
+ pwrbtn_state = PWRBTN_STATE_RELEASED;
+ }
+
+ break;
+
+ case PWRBTN_STATE_BOOT_KB_RESET:
+ /* Initial forced pulse is done. Ignore the actual power
+ * button until it's released, so that holding down the
+ * recovery combination doesn't cause the chipset to shut back
+ * down. */
+ set_pwrbtn_to_pch(1);
+ if (power_button_is_pressed())
+ pwrbtn_state = PWRBTN_STATE_EAT_RELEASE;
+ else
+ pwrbtn_state = PWRBTN_STATE_IDLE;
+ break;
+ case PWRBTN_STATE_WAS_OFF:
+ /* Done stretching initial power button signal, so show the
+ * true power button state to the PCH. */
+ if (power_button_is_pressed()) {
+ /* User is still holding the power button */
+ pwrbtn_state = PWRBTN_STATE_HELD;
+ } else {
+ /* Stop stretching the power button press */
+ power_button_released(tnow);
+ }
+ break;
+ case PWRBTN_STATE_IDLE:
+ case PWRBTN_STATE_HELD:
+ case PWRBTN_STATE_EAT_RELEASE:
+ /* Do nothing */
+ break;
+ }
+}
+
+void power_button_task(void)
+{
+ uint64_t t;
+ uint64_t tsleep;
+
+ while (1) {
+ t = get_time().val;
+
+ /* Update state machine */
+ CPRINTF("[%T PB task %d = %s]\n", pwrbtn_state,
+ state_names[pwrbtn_state]);
+
+ state_machine(t);
+
+ /* Sleep until our next timeout */
+ tsleep = -1;
+ if (tnext_state && tnext_state < tsleep)
+ tsleep = tnext_state;
+ t = get_time().val;
+ if (tsleep > t) {
+ unsigned d = tsleep == -1 ? -1 : (unsigned)(tsleep - t);
+ /*
+ * (Yes, the conversion from uint64_t to unsigned could
+ * theoretically overflow if we wanted to sleep for
+ * more than 2^32 us, but our timeouts are small enough
+ * that can't happen - and even if it did, we'd just go
+ * back to sleep after deciding that we woke up too
+ * early.)
+ */
+ CPRINTF("[%T PB task %d = %s, wait %d]\n", pwrbtn_state,
+ state_names[pwrbtn_state], d);
+ task_wait_event(d);
+ }
+ }
+}
+
+/*****************************************************************************/
+/* Hooks */
+
+static void powerbtn_x86_init(void)
+{
+ set_initial_pwrbtn_state();
+}
+DECLARE_HOOK(HOOK_INIT, powerbtn_x86_init, HOOK_PRIO_DEFAULT);
+
+/**
+ * Handle switch changes based on lid event.
+ */
+static void powerbtn_x86_lid_change(void)
+{
+ /* If chipset is off, pulse the power button on lid open to wake it. */
+ if (lid_is_open() && chipset_in_state(CHIPSET_STATE_ANY_OFF)) {
+ chipset_exit_hard_off();
+ set_pwrbtn_to_pch(0);
+ pwrbtn_state = PWRBTN_STATE_LID_OPEN;
+ tnext_state = get_time().val + PWRBTN_INITIAL_US;
+ task_wake(TASK_ID_POWERBTN);
+ }
+}
+DECLARE_HOOK(HOOK_LID_CHANGE, powerbtn_x86_lid_change, HOOK_PRIO_DEFAULT);
+
+/**
+ * Handle debounced power button changing state.
+ */
+static void powerbtn_x86_changed(void)
+{
+ if (pwrbtn_state == PWRBTN_STATE_BOOT_KB_RESET ||
+ pwrbtn_state == PWRBTN_STATE_INIT_ON ||
+ pwrbtn_state == PWRBTN_STATE_LID_OPEN ||
+ pwrbtn_state == PWRBTN_STATE_WAS_OFF) {
+ /* Ignore all power button changes during an initial pulse */
+ CPRINTF("[%T PB ignoring change]\n");
+ return;
+ }
+
+ if (power_button_is_pressed()) {
+ /* Power button pressed */
+ power_button_pressed(get_time().val);
+ } else {
+ /* Power button released */
+ if (pwrbtn_state == PWRBTN_STATE_EAT_RELEASE) {
+ /*
+ * Ignore the first power button release if we already
+ * told the PCH the power button was released.
+ */
+ CPRINTF("[%T PB ignoring release]\n");
+ pwrbtn_state = PWRBTN_STATE_IDLE;
+ return;
+ }
+
+ power_button_released(get_time().val);
+ }
+
+ /* Wake the power button task */
+ task_wake(TASK_ID_POWERBTN);
+}
+DECLARE_HOOK(HOOK_POWER_BUTTON_CHANGE, powerbtn_x86_changed, HOOK_PRIO_DEFAULT);
diff --git a/include/switch.h b/include/switch.h
index 2cd732774e..f59e56df52 100644
--- a/include/switch.h
+++ b/include/switch.h
@@ -11,7 +11,7 @@
#include "common.h"
#include "gpio.h"
-#ifdef HAS_TASK_SWITCH
+#ifdef CONFIG_SWITCH
/**
* Interrupt handler for switch inputs.
*
@@ -20,11 +20,6 @@
void switch_interrupt(enum gpio_signal signal);
#else
#define switch_interrupt NULL
-#endif /* HAS_TASK_SWITCH */
-
-/**
- * Return non-zero if write protect signal is asserted.
- */
-int switch_get_write_protect(void);
+#endif /* CONFIG_SWITCH */
#endif /* __CROS_EC_SWITCH_H */