Split out power button code from switch.c

The power button code is platform-independent.  This change splits the
code out of the LM4 switch.c module so that a subseqent change to
STM32 platforms can start using it.

BUG=chrome-os-partner:18945
BRANCH=none
TEST=manual

1. Power+refresh+esc goes to recovery mode,
2. Press power button at recovery screen turns off.
3. With system off, power button turns system on.
4. Press power button for a second; screen locks.
5. Press power button while typing; blocks keystrokes while it's pressed.
6. Hold power button down for 8 sec; system forced to shutdown.
7. From EC console, with system on:
   hostevent clear
   hostevent -> event 0x04 is clear
   press power button
   hostevent -> event 0x04 is set
8. From EC console, with system off:
   powerbtn -> system turns on
   powerbtn 5000 -> system turns off, just like power button was held for 5 sec

Change-Id: If2a9b02514a201e1d03c857d128e2ccab51a16ef
Signed-off-by: Randall Spangler <rspangler@chromium.org>
Reviewed-on: https://gerrit.chromium.org/gerrit/49217
Reviewed-by: Bill Richardson <wfrichar@chromium.org>
This commit is contained in:
Randall Spangler
2013-04-23 17:02:07 -07:00
committed by ChromeBot
parent b4b2c6ae70
commit 45bc5c1a21
13 changed files with 261 additions and 175 deletions

View File

@@ -14,6 +14,7 @@
#include "lid_switch.h"
#include "lm4_adc.h"
#include "peci.h"
#include "power_button.h"
#include "registers.h"
#include "switch.h"
#include "temp_sensor.h"
@@ -26,7 +27,7 @@
const struct gpio_info gpio_list[GPIO_COUNT] = {
/* Inputs with interrupt handlers are first for efficiency */
{"POWER_BUTTON_L", LM4_GPIO_K, (1<<7), GPIO_INT_BOTH,
switch_interrupt},
power_button_interrupt},
{"LID_OPEN", LM4_GPIO_K, (1<<5), GPIO_INT_BOTH,
lid_interrupt},
/* Other inputs */

View File

@@ -32,6 +32,7 @@
#define CONFIG_ONEWIRE
#define CONFIG_ONEWIRE_LED
#define CONFIG_PECI
#define CONFIG_POWER_BUTTON
#define CONFIG_PWM
#define CONFIG_TEMP_SENSOR
#define CONFIG_TMP006

View File

@@ -15,6 +15,7 @@
#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"
@@ -43,7 +44,6 @@
* @S0 make code break code
*/
/* TODO: link to full power button / lid switch state machine description. */
#define PWRBTN_DEBOUNCE_US (30 * MSEC) /* Debounce time for power button */
#define PWRBTN_DELAY_T0 (32 * MSEC) /* 32ms (PCH requires >16ms) */
#define PWRBTN_DELAY_T1 (4 * SECOND - PWRBTN_DELAY_T0) /* 4 secs - t0 */
/*
@@ -109,8 +109,6 @@ static uint64_t tnext_state;
static uint64_t tdebounce_pwr;
static uint8_t *memmap_switches;
static int debounced_power_pressed;
static int simulate_power_pressed;
/**
* Update status of non-debounced switches.
@@ -147,23 +145,6 @@ static void set_pwrbtn_to_pch(int high)
gpio_set_level(GPIO_PCH_PWRBTN_L, high);
}
/**
* Get raw power button signal state.
*
* @return 1 if power button is pressed, 0 if not pressed.
*/
static int raw_power_button_pressed(void)
{
if (simulate_power_pressed)
return 1;
/* Ignore power button if lid is closed */
if (!lid_is_open())
return 0;
return gpio_get_level(GPIO_POWER_BUTTON_L) ? 0 : 1;
}
static void update_backlight(void)
{
/* Only enable the backlight if the lid is open */
@@ -181,20 +162,10 @@ static void update_backlight(void)
*/
static void power_button_pressed(uint64_t tnow)
{
if (debounced_power_pressed == 1) {
CPRINTF("[%T PB already pressed]\n");
return;
}
CPRINTF("[%T PB pressed]\n");
debounced_power_pressed = 1;
pwrbtn_state = PWRBTN_STATE_PRESSED;
tnext_state = tnow;
*memmap_switches |= EC_SWITCH_POWER_BUTTON_PRESSED;
#ifdef HAS_TASK_KEYPROTO
keyboard_set_power_button(1);
#endif
host_set_single_event(EC_HOST_EVENT_POWER_BUTTON);
}
/**
@@ -202,52 +173,10 @@ static void power_button_pressed(uint64_t tnow)
*/
static void power_button_released(uint64_t tnow)
{
if (debounced_power_pressed == 0) {
CPRINTF("[%T PB already released]\n");
return;
}
CPRINTF("[%T PB released]\n");
debounced_power_pressed = 0;
pwrbtn_state = PWRBTN_STATE_RELEASED;
tnext_state = tnow;
*memmap_switches &= ~EC_SWITCH_POWER_BUTTON_PRESSED;
#ifdef HAS_TASK_KEYPROTO
keyboard_set_power_button(0);
#endif
}
/**
* Handle debounced power button changing state.
*/
static void power_button_changed(uint64_t tnow)
{
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 (raw_power_button_pressed()) {
/* Power button pressed */
power_button_pressed(tnow);
} 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(tnow);
}
}
/**
@@ -257,16 +186,13 @@ static void set_initial_pwrbtn_state(void)
{
uint32_t reset_flags = system_get_reset_flags();
/* Set debounced power button state to initial button state */
debounced_power_pressed = raw_power_button_pressed();
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 (debounced_power_pressed) {
if (power_button_is_pressed()) {
*memmap_switches |= EC_SWITCH_POWER_BUTTON_PRESSED;
CPRINTF("[%T PB init-jumped-held]\n");
set_pwrbtn_to_pch(0);
@@ -288,7 +214,7 @@ static void set_initial_pwrbtn_state(void)
*/
CPRINTF("[%T PB init-off]\n");
set_pwrbtn_to_pch(1);
if (debounced_power_pressed)
if (power_button_is_pressed())
pwrbtn_state = PWRBTN_STATE_EAT_RELEASE;
else
pwrbtn_state = PWRBTN_STATE_IDLE;
@@ -386,7 +312,7 @@ static void state_machine(uint64_t tnow)
set_pwrbtn_to_pch(0);
tnext_state = get_time().val + PWRBTN_INITIAL_US;
if (debounced_power_pressed) {
if (power_button_is_pressed()) {
*memmap_switches |= EC_SWITCH_POWER_BUTTON_PRESSED;
if (system_get_reset_flags() & RESET_FLAG_RESET_PIN)
@@ -405,7 +331,7 @@ static void state_machine(uint64_t tnow)
* recovery combination doesn't cause the chipset to shut back
* down. */
set_pwrbtn_to_pch(1);
if (raw_power_button_pressed())
if (power_button_is_pressed())
pwrbtn_state = PWRBTN_STATE_EAT_RELEASE;
else
pwrbtn_state = PWRBTN_STATE_IDLE;
@@ -413,7 +339,7 @@ static void state_machine(uint64_t tnow)
case PWRBTN_STATE_WAS_OFF:
/* Done stretching initial power button signal, so show the
* true power button state to the PCH. */
if (raw_power_button_pressed()) {
if (power_button_is_pressed()) {
/* User is still holding the power button */
pwrbtn_state = PWRBTN_STATE_HELD;
} else {
@@ -437,22 +363,6 @@ void switch_task(void)
while (1) {
t = get_time().val;
/* Handle debounce timeout for power button */
if (tdebounce_pwr && t >= tdebounce_pwr) {
tdebounce_pwr = 0;
/*
* Re-enable keyboard scanning if the power button is
* no longer pressed.
*/
if (!raw_power_button_pressed())
keyboard_scan_enable(1);
if (raw_power_button_pressed() !=
debounced_power_pressed)
power_button_changed(t);
}
/* Handle non-debounced switches */
update_other_switches();
@@ -537,22 +447,47 @@ static void switch_lid_change(void)
}
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)
{
/* Reset debounce time for the changed signal */
switch (signal) {
case GPIO_POWER_BUTTON_L:
/* Reset power button debounce time */
tdebounce_pwr = get_time().val + PWRBTN_DEBOUNCE_US;
if (raw_power_button_pressed()) {
/*
* Disable the matrix scan as soon as possible to
* reduce the risk of false-reboot triggered by those
* keys on the same column with refresh key.
*/
keyboard_scan_enable(0);
}
break;
case GPIO_PCH_BKLTEN:
update_backlight();
break;
@@ -574,39 +509,6 @@ void switch_interrupt(enum gpio_signal signal)
task_wake(TASK_ID_SWITCH);
}
/*****************************************************************************/
/* Console commands */
static int command_powerbtn(int argc, char **argv)
{
int ms = PWRBTN_INITIAL_US / MSEC; /* Press duration in ms */
char *e;
if (argc > 1) {
ms = strtoi(argv[1], &e, 0);
if (*e)
return EC_ERROR_PARAM1;
}
ccprintf("Simulating %d ms power button press.\n", ms);
simulate_power_pressed = 1;
tdebounce_pwr = get_time().val + PWRBTN_DEBOUNCE_US;
task_wake(TASK_ID_SWITCH);
msleep(ms);
ccprintf("Simulating power button release.\n");
simulate_power_pressed = 0;
tdebounce_pwr = get_time().val + PWRBTN_DEBOUNCE_US;
task_wake(TASK_ID_SWITCH);
return EC_SUCCESS;
}
DECLARE_CONSOLE_COMMAND(powerbtn, command_powerbtn,
"[msec]",
"Simulate power button press",
NULL);
static int command_mmapinfo(int argc, char **argv)
{
uint8_t *memmap_switches = host_get_memmap(EC_MEMMAP_SWITCHES);

View File

@@ -30,6 +30,7 @@ common-$(CONFIG_LID_SWITCH)+=lid_switch.o
common-$(CONFIG_LP5562)+=lp5562.o lp5562_battery_led.o
common-$(CONFIG_LPC)+=port80.o
common-$(CONFIG_ONEWIRE_LED)+=onewire_led.o
common-$(CONFIG_POWER_BUTTON)+=power_button.o
common-$(CONFIG_PSTORE)+=pstore_commands.o
common-$(CONFIG_SMART_BATTERY)+=smart_battery.o smart_battery_stub.o
common-$(HAS_TASK_CHIPSET)+=chipset.o

View File

@@ -42,6 +42,7 @@ static const struct hook_ptrs hook_list[] = {
{__hooks_chipset_shutdown, __hooks_chipset_shutdown_end},
{__hooks_ac_change, __hooks_ac_change_end},
{__hooks_lid_change, __hooks_lid_change_end},
{__hooks_pwrbtn_change, __hooks_pwrbtn_change_end},
{__hooks_tick, __hooks_tick_end},
{__hooks_second, __hooks_second_end},
};

View File

@@ -15,6 +15,7 @@
#include "keyboard_protocol.h"
#include "lightbar.h"
#include "lpc.h"
#include "power_button.h"
#include "queue.h"
#include "registers.h"
#include "shared_mem.h"
@@ -831,34 +832,6 @@ static void keyboard_special(uint16_t k)
}
}
void keyboard_set_power_button(int pressed)
{
enum scancode_set_list code_set;
uint8_t code[2][2][3] = {
{ /* set 1 */
{0xe0, 0xde}, /* break */
{0xe0, 0x5e}, /* make */
}, { /* set 2 */
{0xe0, 0xf0, 0x37}, /* break */
{0xe0, 0x37}, /* make */
}
};
power_button_pressed = pressed;
/* Only send the scan code if main chipset is fully awake */
if (!chipset_in_state(CHIPSET_STATE_ON))
return;
code_set = acting_code_set(scancode_set);
if (keystroke_enabled) {
i8042_send_to_host(
(code_set == SCANCODE_SET_2 && !pressed) ? 3 : 2,
code[code_set - SCANCODE_SET_1][pressed]);
}
}
void keyboard_protocol_task(void)
{
int wait = -1;
@@ -1097,3 +1070,36 @@ static void keyboard_restore_state(void)
}
}
DECLARE_HOOK(HOOK_INIT, keyboard_restore_state, HOOK_PRIO_DEFAULT);
/**
* Handle power button changing state.
*/
static void keyboard_power_button(void)
{
enum scancode_set_list code_set;
uint8_t code[2][2][3] = {
{ /* set 1 */
{0xe0, 0xde}, /* break */
{0xe0, 0x5e}, /* make */
}, { /* set 2 */
{0xe0, 0xf0, 0x37}, /* break */
{0xe0, 0x37}, /* make */
}
};
power_button_pressed = power_button_is_pressed();
/*
* Only send the scan code if main chipset is fully awake and
* keystrokes are enabled.
*/
if (!chipset_in_state(CHIPSET_STATE_ON) || !keystroke_enabled)
return;
code_set = acting_code_set(scancode_set);
i8042_send_to_host(
(code_set == SCANCODE_SET_2 && !power_button_pressed) ? 3 : 2,
code[code_set - SCANCODE_SET_1][power_button_pressed]);
}
DECLARE_HOOK(HOOK_POWER_BUTTON_CHANGE, keyboard_power_button,
HOOK_PRIO_DEFAULT);

View File

@@ -10,6 +10,7 @@
#include "gpio.h"
#include "hooks.h"
#include "host_command.h"
#include "lid_switch.h"
#include "timer.h"
#include "util.h"

135
common/power_button.c Normal file
View File

@@ -0,0 +1,135 @@
/* 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 module for Chrome EC */
#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 "timer.h"
#include "util.h"
/* Console output macros */
#define CPUTS(outstr) cputs(CC_SWITCH, outstr)
#define CPRINTF(format, args...) cprintf(CC_SWITCH, format, ## args)
#define PWRBTN_DEBOUNCE_US (30 * MSEC) /* Debounce time for power button */
static int debounced_power_pressed; /* Debounced power button state */
static int simulate_power_pressed;
/**
* Get raw power button signal state.
*
* @return 1 if power button is pressed, 0 if not pressed.
*/
static int raw_power_button_pressed(void)
{
if (simulate_power_pressed)
return 1;
/* Ignore power button if lid is closed */
if (!lid_is_open())
return 0;
return gpio_get_level(GPIO_POWER_BUTTON_L) ? 0 : 1;
}
int power_button_is_pressed(void)
{
return debounced_power_pressed;
}
/**
* Handle power button initialization.
*/
static void power_button_init(void)
{
if (raw_power_button_pressed())
debounced_power_pressed = 1;
/* Enable interrupts, now that we've initialized */
gpio_enable_interrupt(GPIO_POWER_BUTTON_L);
}
DECLARE_HOOK(HOOK_INIT, power_button_init, HOOK_PRIO_INIT_POWER_BUTTON);
/**
* Handle debounced power button changing state.
*/
static void power_button_change_deferred(void)
{
const int new_pressed = raw_power_button_pressed();
/* If power button hasn't changed state, nothing to do */
if (new_pressed == debounced_power_pressed)
return;
debounced_power_pressed = new_pressed;
CPRINTF("[%T power button %s]\n", new_pressed ? "pressed" : "released");
/* Re-enable keyboard scanning if power button is no longer pressed */
if (!new_pressed)
keyboard_scan_enable(1);
/* Call hooks */
hook_notify(HOOK_POWER_BUTTON_CHANGE);
/* Notify host if power button has been pressed */
if (new_pressed)
host_set_single_event(EC_HOST_EVENT_POWER_BUTTON);
}
DECLARE_DEFERRED(power_button_change_deferred);
void power_button_interrupt(enum gpio_signal signal)
{
/*
* If power button is pressed, disable the matrix scan as soon as
* possible to reduce the risk of false-reboot triggered by those keys
* on the same column with refresh key.
*/
if (raw_power_button_pressed())
keyboard_scan_enable(0);
/* Reset power button debounce time */
hook_call_deferred(power_button_change_deferred, PWRBTN_DEBOUNCE_US);
}
/*****************************************************************************/
/* Console commands */
static int command_powerbtn(int argc, char **argv)
{
int ms = 200; /* Press duration in ms */
char *e;
if (argc > 1) {
ms = strtoi(argv[1], &e, 0);
if (*e)
return EC_ERROR_PARAM1;
}
ccprintf("Simulating %d ms power button press.\n", ms);
simulate_power_pressed = 1;
hook_call_deferred(power_button_change_deferred, 0);
msleep(ms);
ccprintf("Simulating power button release.\n");
simulate_power_pressed = 0;
hook_call_deferred(power_button_change_deferred, 0);
return EC_SUCCESS;
}
DECLARE_CONSOLE_COMMAND(powerbtn, command_powerbtn,
"[msec]",
"Simulate power button press",
NULL);

View File

@@ -96,6 +96,10 @@ SECTIONS
*(.rodata.HOOK_LID_CHANGE)
__hooks_lid_change_end = .;
__hooks_pwrbtn_change = .;
*(.rodata.HOOK_POWER_BUTTON_CHANGE)
__hooks_pwrbtn_change_end = .;
__hooks_tick = .;
*(.rodata.HOOK_TICK)
__hooks_tick_end = .;

View File

@@ -38,6 +38,8 @@ extern const struct hook_data __hooks_ac_change[];
extern const struct hook_data __hooks_ac_change_end[];
extern const struct hook_data __hooks_lid_change[];
extern const struct hook_data __hooks_lid_change_end[];
extern const struct hook_data __hooks_pwrbtn_change[];
extern const struct hook_data __hooks_pwrbtn_change_end[];
extern const struct hook_data __hooks_tick[];
extern const struct hook_data __hooks_tick_end[];
extern const struct hook_data __hooks_second[];

View File

@@ -25,6 +25,8 @@ enum hook_priority {
HOOK_PRIO_INIT_CHIPSET = HOOK_PRIO_FIRST + 2,
/* Lid switch inits before power button */
HOOK_PRIO_INIT_LID = HOOK_PRIO_FIRST + 3,
/* Power button inits before chipset and switch */
HOOK_PRIO_INIT_POWER_BUTTON = HOOK_PRIO_FIRST + 4,
};
enum hook_type {
@@ -105,6 +107,14 @@ enum hook_type {
*/
HOOK_LID_CHANGE,
/*
* Power button pressed or released. Based on debounced power button
* state, not raw GPIO input.
*
* Hook routines are called from the chipset task.
*/
HOOK_POWER_BUTTON_CHANGE,
/*
* Periodic tick, every HOOK_TICK_INTERVAL.
*

View File

@@ -27,9 +27,4 @@ void keyboard_host_write(int data, int is_cmd);
*/
void keyboard_state_changed(int row, int col, int is_pressed);
/**
* Send make/break code of power button to host.
*/
void keyboard_set_power_button(int pressed);
#endif /* __CROS_EC_KEYBOARD_8042_H */

27
include/power_button.h Normal file
View File

@@ -0,0 +1,27 @@
/* 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 API for Chrome EC */
#ifndef __CROS_EC_POWER_BUTTON_H
#define __CROS_EC_POWER_BUTTON_H
#include "common.h"
/**
* Return non-zero if power button is pressed.
*
* Uses the debounced button state, not the raw signal from the GPIO.
*/
int power_button_is_pressed(void);
/**
* Interrupt handler for power button.
*
* @param signal Signal which triggered the interrupt.
*/
void power_button_interrupt(enum gpio_signal signal);
#endif /* __CROS_EC_POWER_BUTTON_H */