diff options
author | Randall Spangler <rspangler@chromium.org> | 2013-12-09 16:05:28 -0800 |
---|---|---|
committer | chrome-internal-fetch <chrome-internal-fetch@google.com> | 2013-12-17 21:27:53 +0000 |
commit | 616e70998d2ea08cc480baac6aa3be5964241bef (patch) | |
tree | 5d45045c4b3452895100895cb7782bc77d11a674 /common/ap_hang_detect.c | |
parent | 54b5c71f0d61d761b2b41635f0d5c13bc1a3445c (diff) | |
download | chrome-ec-616e70998d2ea08cc480baac6aa3be5964241bef.tar.gz |
Add AP hang detection
BUG=chrome-os-partner:24558
BRANCH=none
TEST=see procedure in bug
Change-Id: I42614a1da5f24c93b6267d81339ff9d721bf0d8f
Signed-off-by: Randall Spangler <rspangler@chromium.org>
Reviewed-on: https://chromium-review.googlesource.com/180080
Reviewed-by: Bill Richardson <wfrichar@chromium.org>
Diffstat (limited to 'common/ap_hang_detect.c')
-rw-r--r-- | common/ap_hang_detect.c | 232 |
1 files changed, 232 insertions, 0 deletions
diff --git a/common/ap_hang_detect.c b/common/ap_hang_detect.c new file mode 100644 index 0000000000..f19f0b7e60 --- /dev/null +++ b/common/ap_hang_detect.c @@ -0,0 +1,232 @@ +/* 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. + */ + +/* 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 CPRINTF(format, args...) cprintf(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) +{ + /* 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) { + CPRINTF("[%T hang detect triggering warm reboot]\n"); + host_set_single_event(EC_HOST_EVENT_HANG_REBOOT); + chipset_reset(0); + active = 0; + return; + } + + /* Otherwise, we're starting with the host event */ + CPRINTF("[%T hang detect sending host event]\n"); + 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) { + CPRINTF("[%T hang detect continuing (for reboot)]\n"); + timeout_will_reboot = 1; + hook_call_deferred(hang_detect_deferred, + (hdparams.warm_reboot_timeout_msec - + hdparams.host_event_timeout_msec) * MSEC); + } else { + /* Not rebooting, so go back to idle */ + active = 0; + } +} +DECLARE_DEFERRED(hang_detect_deferred); + +/** + * 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) { + CPRINTF("[%T hang detect started on %s (for event)]\n", why); + timeout_will_reboot = 0; + active = 1; + hook_call_deferred(hang_detect_deferred, + hdparams.host_event_timeout_msec * MSEC); + } else if (hdparams.warm_reboot_timeout_msec) { + CPRINTF("[%T hang detect started on %s (for reboot)]\n", why); + timeout_will_reboot = 1; + active = 1; + hook_call_deferred(hang_detect_deferred, + hdparams.warm_reboot_timeout_msec * MSEC); + } +} + +/** + * Stop the hang detect timers. + */ +static void hang_detect_stop(const char *why) +{ + if (active) + CPRINTF("[%T hang detect stopped on %s]\n", 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 int 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; + } + + /* Save new params */ + hdparams = *p; + CPRINTF("[%T hang detect flags=0x%x, event=%d ms, reboot=%d ms]\n", + 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", + NULL); |