239 lines
6.2 KiB
C
239 lines
6.2 KiB
C
|
/* Copyright 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.
|
||
|
*/
|
||
|
|
||
|
/* AP hang detect logic */
|
||
|
|
||
|
#include "ap_hang_detect.h"
|
||
|
#include "chipset.h"
|
||
|
#include "common.h"
|
||
|
#include "console.h"
|
||
|
#include "hooks.h"
|
||
|
#include "host_command.h"
|
||
|
#include "lid_switch.h"
|
||
|
#include "power_button.h"
|
||
|
#include "timer.h"
|
||
|
#include "util.h"
|
||
|
|
||
|
/* Console output macros */
|
||
|
#define CPUTS(outstr) cputs(CC_CHIPSET, outstr)
|
||
|
#define CPRINTS(format, args...) cprints(CC_CHIPSET, format, ## args)
|
||
|
|
||
|
static struct ec_params_hang_detect hdparams;
|
||
|
|
||
|
static int active; /* Is hang detect timer active / counting? */
|
||
|
static int timeout_will_reboot; /* Will the deferred call reboot the AP? */
|
||
|
|
||
|
/**
|
||
|
* Handle the hang detect timer expiring.
|
||
|
*/
|
||
|
static void hang_detect_deferred(void);
|
||
|
DECLARE_DEFERRED(hang_detect_deferred);
|
||
|
|
||
|
static void hang_detect_deferred(void)
|
||
|
{
|
||
|
/* If we're no longer active, nothing to do */
|
||
|
if (!active)
|
||
|
return;
|
||
|
|
||
|
/* If we're rebooting the AP, stop hang detection */
|
||
|
if (timeout_will_reboot) {
|
||
|
CPRINTS("hang detect triggering warm reboot");
|
||
|
host_set_single_event(EC_HOST_EVENT_HANG_REBOOT);
|
||
|
chipset_reset(CHIPSET_RESET_HANG_REBOOT);
|
||
|
active = 0;
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
/* Otherwise, we're starting with the host event */
|
||
|
CPRINTS("hang detect sending host event");
|
||
|
host_set_single_event(EC_HOST_EVENT_HANG_DETECT);
|
||
|
|
||
|
/* If we're also rebooting, defer for the remaining delay */
|
||
|
if (hdparams.warm_reboot_timeout_msec) {
|
||
|
CPRINTS("hang detect continuing (for reboot)");
|
||
|
timeout_will_reboot = 1;
|
||
|
hook_call_deferred(&hang_detect_deferred_data,
|
||
|
(hdparams.warm_reboot_timeout_msec -
|
||
|
hdparams.host_event_timeout_msec) * MSEC);
|
||
|
} else {
|
||
|
/* Not rebooting, so go back to idle */
|
||
|
active = 0;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Start the hang detect timers.
|
||
|
*/
|
||
|
static void hang_detect_start(const char *why)
|
||
|
{
|
||
|
/* If already active, don't restart timer */
|
||
|
if (active)
|
||
|
return;
|
||
|
|
||
|
if (hdparams.host_event_timeout_msec) {
|
||
|
CPRINTS("hang detect started on %s (for event)", why);
|
||
|
timeout_will_reboot = 0;
|
||
|
active = 1;
|
||
|
hook_call_deferred(&hang_detect_deferred_data,
|
||
|
hdparams.host_event_timeout_msec * MSEC);
|
||
|
} else if (hdparams.warm_reboot_timeout_msec) {
|
||
|
CPRINTS("hang detect started on %s (for reboot)", why);
|
||
|
timeout_will_reboot = 1;
|
||
|
active = 1;
|
||
|
hook_call_deferred(&hang_detect_deferred_data,
|
||
|
hdparams.warm_reboot_timeout_msec * MSEC);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Stop the hang detect timers.
|
||
|
*/
|
||
|
static void hang_detect_stop(const char *why)
|
||
|
{
|
||
|
if (active)
|
||
|
CPRINTS("hang detect stopped on %s", why);
|
||
|
|
||
|
active = 0;
|
||
|
}
|
||
|
|
||
|
void hang_detect_stop_on_host_command(void)
|
||
|
{
|
||
|
if (hdparams.flags & EC_HANG_STOP_ON_HOST_COMMAND)
|
||
|
hang_detect_stop("host cmd");
|
||
|
}
|
||
|
|
||
|
/*****************************************************************************/
|
||
|
/* Hooks */
|
||
|
|
||
|
static void hang_detect_power_button(void)
|
||
|
{
|
||
|
if (power_button_is_pressed()) {
|
||
|
if (hdparams.flags & EC_HANG_START_ON_POWER_PRESS)
|
||
|
hang_detect_start("power button");
|
||
|
} else {
|
||
|
if (hdparams.flags & EC_HANG_STOP_ON_POWER_RELEASE)
|
||
|
hang_detect_stop("power button");
|
||
|
}
|
||
|
}
|
||
|
DECLARE_HOOK(HOOK_POWER_BUTTON_CHANGE, hang_detect_power_button,
|
||
|
HOOK_PRIO_DEFAULT);
|
||
|
|
||
|
static void hang_detect_lid(void)
|
||
|
{
|
||
|
if (lid_is_open()) {
|
||
|
if (hdparams.flags & EC_HANG_START_ON_LID_OPEN)
|
||
|
hang_detect_start("lid open");
|
||
|
} else {
|
||
|
if (hdparams.flags & EC_HANG_START_ON_LID_CLOSE)
|
||
|
hang_detect_start("lid close");
|
||
|
}
|
||
|
}
|
||
|
DECLARE_HOOK(HOOK_LID_CHANGE, hang_detect_lid, HOOK_PRIO_DEFAULT);
|
||
|
|
||
|
static void hang_detect_resume(void)
|
||
|
{
|
||
|
if (hdparams.flags & EC_HANG_START_ON_RESUME)
|
||
|
hang_detect_start("resume");
|
||
|
}
|
||
|
DECLARE_HOOK(HOOK_CHIPSET_RESUME, hang_detect_resume, HOOK_PRIO_DEFAULT);
|
||
|
|
||
|
static void hang_detect_suspend(void)
|
||
|
{
|
||
|
if (hdparams.flags & EC_HANG_STOP_ON_SUSPEND)
|
||
|
hang_detect_stop("suspend");
|
||
|
}
|
||
|
DECLARE_HOOK(HOOK_CHIPSET_SUSPEND, hang_detect_suspend, HOOK_PRIO_DEFAULT);
|
||
|
|
||
|
static void hang_detect_shutdown(void)
|
||
|
{
|
||
|
/* Stop the timers */
|
||
|
hang_detect_stop("shutdown");
|
||
|
|
||
|
/* Disable hang detection; it must be enabled every boot */
|
||
|
memset(&hdparams, 0, sizeof(hdparams));
|
||
|
}
|
||
|
DECLARE_HOOK(HOOK_CHIPSET_SHUTDOWN, hang_detect_shutdown, HOOK_PRIO_DEFAULT);
|
||
|
|
||
|
/*****************************************************************************/
|
||
|
/* Host command */
|
||
|
|
||
|
static enum ec_status
|
||
|
hang_detect_host_command(struct host_cmd_handler_args *args)
|
||
|
{
|
||
|
const struct ec_params_hang_detect *p = args->params;
|
||
|
|
||
|
/* Handle stopping hang timer on request */
|
||
|
if (p->flags & EC_HANG_STOP_NOW) {
|
||
|
hang_detect_stop("ap request");
|
||
|
|
||
|
/* Ignore the other params */
|
||
|
return EC_RES_SUCCESS;
|
||
|
}
|
||
|
|
||
|
/* Handle starting hang timer on request */
|
||
|
if (p->flags & EC_HANG_START_NOW) {
|
||
|
hang_detect_start("ap request");
|
||
|
|
||
|
/* Ignore the other params */
|
||
|
return EC_RES_SUCCESS;
|
||
|
}
|
||
|
|
||
|
/* If hang detect transitioning to disabled, stop timers */
|
||
|
if (hdparams.flags && !p->flags)
|
||
|
hang_detect_stop("ap flags=0");
|
||
|
|
||
|
/* Save new params */
|
||
|
hdparams = *p;
|
||
|
CPRINTS("hang detect flags=0x%x, event=%d ms, reboot=%d ms",
|
||
|
hdparams.flags, hdparams.host_event_timeout_msec,
|
||
|
hdparams.warm_reboot_timeout_msec);
|
||
|
|
||
|
/*
|
||
|
* If warm reboot timeout is shorter than host event timeout, ignore
|
||
|
* the host event timeout because a warm reboot will win.
|
||
|
*/
|
||
|
if (hdparams.warm_reboot_timeout_msec &&
|
||
|
hdparams.warm_reboot_timeout_msec <=
|
||
|
hdparams.host_event_timeout_msec)
|
||
|
hdparams.host_event_timeout_msec = 0;
|
||
|
|
||
|
return EC_RES_SUCCESS;
|
||
|
}
|
||
|
DECLARE_HOST_COMMAND(EC_CMD_HANG_DETECT,
|
||
|
hang_detect_host_command,
|
||
|
EC_VER_MASK(0));
|
||
|
|
||
|
/*****************************************************************************/
|
||
|
/* Console command */
|
||
|
|
||
|
static int command_hang_detect(int argc, char **argv)
|
||
|
{
|
||
|
ccprintf("flags: 0x%x\n", hdparams.flags);
|
||
|
|
||
|
ccputs("event: ");
|
||
|
if (hdparams.host_event_timeout_msec)
|
||
|
ccprintf("%d ms\n", hdparams.host_event_timeout_msec);
|
||
|
else
|
||
|
ccputs("disabled\n");
|
||
|
|
||
|
ccputs("reboot: ");
|
||
|
if (hdparams.warm_reboot_timeout_msec)
|
||
|
ccprintf("%d ms\n", hdparams.warm_reboot_timeout_msec);
|
||
|
else
|
||
|
ccputs("disabled\n");
|
||
|
|
||
|
ccputs("status: ");
|
||
|
if (active)
|
||
|
ccprintf("active for %s\n",
|
||
|
timeout_will_reboot ? "reboot" : "event");
|
||
|
else
|
||
|
ccputs("inactive\n");
|
||
|
|
||
|
return EC_SUCCESS;
|
||
|
}
|
||
|
DECLARE_CONSOLE_COMMAND(hangdet, command_hang_detect,
|
||
|
NULL,
|
||
|
"Print hang detect state");
|