summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJonathan Brandmeyer <jbrandmeyer@chromium.org>2018-07-16 15:02:22 -0600
committerchrome-bot <chrome-bot@chromium.org>2018-07-26 04:07:41 -0700
commitdda2f778befed39e449d96b471b94d489ed23d60 (patch)
treefc0db938c092a609fbf47e9758d8f0113e5b0cce
parent48113728b689870e6aeda6534d36eeffd3b738b3 (diff)
downloadchrome-ec-dda2f778befed39e449d96b471b94d489ed23d60.tar.gz
reset: Log the reason for AP resets.
Provides a new EC host command 'uptime info' which gathers up some information which may be useful for debugging spurious resets on the AP (was the EC reset recently? Why was the EC reset? If the EC reset the AP, why did it do so?, etc.). Provide ectool support for the same. Example results of `ectool uptimeinfo`: ``` localhost ~ # ectool uptimeinfo EC uptime: 475.368 seconds AP resets since EC boot: 2 Most recent AP reset causes: 315.903: reset: console command 363.507: reset: keyboard warm reboot EC reset flags at last EC boot: reset-pin | sysjump ``` BRANCH=none TEST=Perform some `apreset` commands from the EC console and observe their side-effects via the `ectool uptimeinfo` command on the AP side. Test sequences include no-resets through 5 resets, observing that the ring buffer handling was correct. BUG=b:110788201, b:79529789 Signed-off-by: Jonathan Brandmeyer <jbrandmeyer@chromium.org> Change-Id: I0bf29d69de471c64f905ee8aa070b15b4f34f2ba Reviewed-on: https://chromium-review.googlesource.com/1139028 Commit-Ready: Jonathan Brandmeyer <jbrandmeyer@chromium.org> Tested-by: Jonathan Brandmeyer <jbrandmeyer@chromium.org> Reviewed-by: Jett Rink <jettrink@chromium.org>
-rw-r--r--baseboard/grunt/baseboard.h1
-rw-r--r--baseboard/octopus/baseboard.c2
-rw-r--r--board/host/chipset.c6
-rw-r--r--board/kukui/board.c2
-rw-r--r--board/rainier/board.c4
-rw-r--r--board/samus/power_sequence.c20
-rw-r--r--board/scarlet/board.c4
-rw-r--r--common/ap_hang_detect.c2
-rw-r--r--common/button.c4
-rw-r--r--common/charge_state_v2.c2
-rw-r--r--common/chipset.c73
-rw-r--r--common/keyboard_8042.c2
-rw-r--r--common/keyboard_scan.c2
-rw-r--r--common/thermal.c2
-rw-r--r--include/chipset.h85
-rw-r--r--include/config.h1
-rw-r--r--include/ec_commands.h49
-rw-r--r--power/apollolake.c8
-rw-r--r--power/braswell.c18
-rw-r--r--power/cannonlake.c7
-rw-r--r--power/intel_x86.c19
-rw-r--r--power/mt817x.c13
-rw-r--r--power/mt8183.c28
-rw-r--r--power/rk3288.c12
-rw-r--r--power/rk3399.c17
-rw-r--r--power/sdm845.c14
-rw-r--r--power/skylake.c3
-rw-r--r--power/stoney.c8
-rw-r--r--test/sbs_charging_v2.c2
-rw-r--r--util/ectool.c124
30 files changed, 447 insertions, 87 deletions
diff --git a/baseboard/grunt/baseboard.h b/baseboard/grunt/baseboard.h
index 1ba6354f33..5520affed1 100644
--- a/baseboard/grunt/baseboard.h
+++ b/baseboard/grunt/baseboard.h
@@ -34,6 +34,7 @@
#define CONFIG_HIBERNATE_PSL
#define CONFIG_HOSTCMD_LPC
#define CONFIG_HOSTCMD_SKUID
+#define CONFIG_CMD_AP_RESET_LOG
#define CONFIG_I2C
#define CONFIG_I2C_MASTER
#define CONFIG_PWM
diff --git a/baseboard/octopus/baseboard.c b/baseboard/octopus/baseboard.c
index 081a20278c..57e4ad3280 100644
--- a/baseboard/octopus/baseboard.c
+++ b/baseboard/octopus/baseboard.c
@@ -280,7 +280,7 @@ void board_hibernate(void)
* To support hibernate called from console commands, ectool commands
* and key sequence, shutdown the AP before hibernating.
*/
- chipset_force_shutdown();
+ chipset_force_shutdown(CHIPSET_SHUTDOWN_BOARD_CUSTOM);
#ifdef CONFIG_USBC_PPC_NX20P3483
/*
diff --git a/board/host/chipset.c b/board/host/chipset.c
index 62e7151e06..51e7222e2e 100644
--- a/board/host/chipset.c
+++ b/board/host/chipset.c
@@ -16,9 +16,9 @@ static int chipset_state = CHIPSET_STATE_SOFT_OFF;
static int power_on_req;
static int power_off_req;
-test_mockable void chipset_reset(void)
+test_mockable void chipset_reset(enum chipset_reset_reason reason)
{
- fprintf(stderr, "Chipset reset!\n");
+ fprintf(stderr, "Chipset reset: %d!\n", reason);
}
test_mockable void chipset_throttle_cpu(int throttle)
@@ -26,7 +26,7 @@ test_mockable void chipset_throttle_cpu(int throttle)
/* Do nothing */
}
-test_mockable void chipset_force_shutdown(void)
+test_mockable void chipset_force_shutdown(enum chipset_shutdown_reason reason)
{
/* Do nothing */
}
diff --git a/board/kukui/board.c b/board/kukui/board.c
index 001ba20649..ab46c6136f 100644
--- a/board/kukui/board.c
+++ b/board/kukui/board.c
@@ -57,7 +57,7 @@ static void tcpc_alert_event(enum gpio_signal signal)
static void warm_reset_request_interrupt(enum gpio_signal signal)
{
CPRINTS("AP wants warm reset");
- chipset_reset();
+ chipset_reset(CHIPSET_RESET_AP_REQ);
}
static void ap_watchdog_interrupt(enum gpio_signal signal)
diff --git a/board/rainier/board.c b/board/rainier/board.c
index 03e292bda4..0b61cd03e1 100644
--- a/board/rainier/board.c
+++ b/board/rainier/board.c
@@ -55,13 +55,13 @@ static void tcpc_alert_event(enum gpio_signal signal)
static void overtemp_interrupt(enum gpio_signal signal)
{
CPRINTS("AP wants shutdown");
- chipset_force_shutdown();
+ chipset_force_shutdown(CHIPSET_SHUTDOWN_THERMAL);
}
static void warm_reset_request_interrupt(enum gpio_signal signal)
{
CPRINTS("AP wants warm reset");
- chipset_reset();
+ chipset_reset(CHIPSET_RESET_AP_REQ);
}
#include "gpio_list.h"
diff --git a/board/samus/power_sequence.c b/board/samus/power_sequence.c
index 804d3ef208..717af8577c 100644
--- a/board/samus/power_sequence.c
+++ b/board/samus/power_sequence.c
@@ -65,9 +65,10 @@
static int throttle_cpu; /* Throttle CPU? */
static uint32_t pp5000_in_g3; /* Turn PP5000 on in G3? */
-void chipset_force_shutdown(void)
+void chipset_force_shutdown(enum chipset_shutdown_reason reason)
{
- CPRINTS("%s()", __func__);
+ CPRINTS("%s(%d)", __func__, reason);
+ report_ap_reset(reason);
/*
* Force off. This condition will reset once the state machine
@@ -111,9 +112,10 @@ static void chipset_reset_rtc(void)
udelay(10 * MSEC);
}
-void chipset_reset(void)
+void chipset_reset(enum chipset_reset_reason reason)
{
- CPRINTS("%s", __func__);
+ CPRINTS("%s(%d)", __func__, reason);
+ report_ap_reset(reason);
/*
* Send a RCIN# pulse to the PCH. This just causes it to
@@ -184,7 +186,7 @@ enum power_state power_handle_state(enum power_state state)
/* Check for state transitions */
if (!power_has_signals(IN_PGOOD_S3)) {
/* Required rail went away */
- chipset_force_shutdown();
+ chipset_force_shutdown(CHIPSET_SHUTDOWN_POWERFAIL);
return POWER_S3S5;
} else if (gpio_get_level(GPIO_PCH_SLP_S3_L) == 1) {
/* Power up to next state */
@@ -198,7 +200,7 @@ enum power_state power_handle_state(enum power_state state)
case POWER_S0:
if (!power_has_signals(IN_PGOOD_S0)) {
/* Required rail went away */
- chipset_force_shutdown();
+ chipset_force_shutdown(CHIPSET_SHUTDOWN_POWERFAIL);
return POWER_S0S3;
} else if (gpio_get_level(GPIO_PCH_SLP_S3_L) == 0) {
/* Power down to next state */
@@ -291,7 +293,7 @@ enum power_state power_handle_state(enum power_state state)
if (power_wait_signals(IN_PGOOD_S3)) {
gpio_set_level(GPIO_PP1800_EN, 0);
gpio_set_level(GPIO_PP1200_EN, 0);
- chipset_force_shutdown();
+ chipset_force_shutdown(CHIPSET_SHUTDOWN_WAIT);
return POWER_S5;
}
@@ -338,7 +340,7 @@ enum power_state power_handle_state(enum power_state state)
gpio_set_level(GPIO_TOUCHSCREEN_RESET_L, 0);
wireless_set_state(WIRELESS_OFF);
gpio_set_level(GPIO_PP3300_DSW_GATED_EN, 1);
- chipset_force_shutdown();
+ chipset_force_shutdown(CHIPSET_SHUTDOWN_WAIT);
return POWER_S3;
}
@@ -377,7 +379,7 @@ enum power_state power_handle_state(enum power_state state)
gpio_set_level(GPIO_TOUCHSCREEN_RESET_L, 0);
gpio_set_level(GPIO_PP3300_DSW_GATED_EN, 1);
wireless_set_state(WIRELESS_OFF);
- chipset_force_shutdown();
+ chipset_force_shutdown(CHIPSET_SHUTDOWN_WAIT);
return POWER_S3;
}
diff --git a/board/scarlet/board.c b/board/scarlet/board.c
index 9eb055e9f6..f4a6ae0b91 100644
--- a/board/scarlet/board.c
+++ b/board/scarlet/board.c
@@ -57,13 +57,13 @@ static void tcpc_alert_event(enum gpio_signal signal)
static void overtemp_interrupt(enum gpio_signal signal)
{
CPRINTS("AP wants shutdown");
- chipset_force_shutdown();
+ chipset_force_shutdown(CHIPSET_SHUTDOWN_THERMAL);
}
static void warm_reset_request_interrupt(enum gpio_signal signal)
{
CPRINTS("AP wants warm reset");
- chipset_reset();
+ chipset_reset(CHIPSET_RESET_AP_REQ);
}
#include "gpio_list.h"
diff --git a/common/ap_hang_detect.c b/common/ap_hang_detect.c
index 45e735e526..8b859aa2d7 100644
--- a/common/ap_hang_detect.c
+++ b/common/ap_hang_detect.c
@@ -41,7 +41,7 @@ static void hang_detect_deferred(void)
if (timeout_will_reboot) {
CPRINTS("hang detect triggering warm reboot");
host_set_single_event(EC_HOST_EVENT_HANG_REBOOT);
- chipset_reset();
+ chipset_reset(CHIPSET_RESET_HANG_REBOOT);
active = 0;
return;
}
diff --git a/common/button.c b/common/button.c
index 6190f414bb..1ccc548c75 100644
--- a/common/button.c
+++ b/common/button.c
@@ -416,7 +416,7 @@ static void debug_mode_handle(void)
host_send_sysrq('x');
CPRINTS("DEBUG MODE: sysrq-x sent");
} else {
- chipset_reset();
+ chipset_reset(CHIPSET_RESET_DBG_WARM_REBOOT);
CPRINTS("DEBUG MODE: Warm reset triggered");
}
recovery_button_pressed = 0;
@@ -531,7 +531,7 @@ static void debug_mode_transition(enum debug_state next_state)
break;
case STATE_WARM_RESET_EXEC:
/* Warm reset the host and transition to STATE_NONE. */
- chipset_reset();
+ chipset_reset(CHIPSET_RESET_DBG_WARM_REBOOT);
CPRINTS("DEBUG MODE: Warm reset triggered");
curr_debug_state = STATE_DEBUG_NONE;
break;
diff --git a/common/charge_state_v2.c b/common/charge_state_v2.c
index 666478293b..3387ac6302 100644
--- a/common/charge_state_v2.c
+++ b/common/charge_state_v2.c
@@ -1333,7 +1333,7 @@ static int shutdown_on_critical_battery(void)
/* Timeout waiting for AP to shut down, so kill it */
CPRINTS(
"charge force shutdown due to critical battery");
- chipset_force_shutdown();
+ chipset_force_shutdown(CHIPSET_SHUTDOWN_BATTERY_CRIT);
}
}
diff --git a/common/chipset.c b/common/chipset.c
index 7a2101624a..4342ba5fab 100644
--- a/common/chipset.c
+++ b/common/chipset.c
@@ -8,6 +8,11 @@
#include "chipset.h"
#include "common.h"
#include "console.h"
+#include "ec_commands.h"
+#include "host_command.h"
+#include "system.h"
+#include "task.h"
+#include "timer.h"
#include "util.h"
/* Console output macros */
@@ -22,7 +27,7 @@ static int command_apreset(int argc, char **argv)
{
/* Force the chipset to reset */
ccprintf("Issuing AP reset...\n");
- chipset_reset();
+ chipset_reset(CHIPSET_RESET_CONSOLE_CMD);
return EC_SUCCESS;
}
DECLARE_CONSOLE_COMMAND(apreset, command_apreset,
@@ -31,10 +36,74 @@ DECLARE_CONSOLE_COMMAND(apreset, command_apreset,
static int command_apshutdown(int argc, char **argv)
{
- chipset_force_shutdown();
+ chipset_force_shutdown(CHIPSET_SHUTDOWN_CONSOLE_CMD);
return EC_SUCCESS;
}
DECLARE_CONSOLE_COMMAND(apshutdown, command_apshutdown,
NULL,
"Force AP shutdown");
+
#endif
+
+#ifdef CONFIG_CMD_AP_RESET_LOG
+static struct mutex reset_log_mutex;
+static int next_reset_log;
+static uint32_t ap_resets_since_ec_boot;
+/* keep reset_logs size a power of 2 */
+static struct ap_reset_log_entry reset_logs[4];
+
+void report_ap_reset(enum chipset_shutdown_reason reason)
+{
+ timestamp_t now = get_time();
+ uint32_t now_ms = (uint32_t)(now.val / MSEC);
+
+ mutex_lock(&reset_log_mutex);
+ reset_logs[next_reset_log].reset_cause = reason;
+ reset_logs[next_reset_log++].reset_time_ms = now_ms;
+ next_reset_log &= ARRAY_SIZE(reset_logs) - 1;
+ ap_resets_since_ec_boot++;
+ mutex_unlock(&reset_log_mutex);
+}
+
+static int host_command_get_uptime_info(struct host_cmd_handler_args *args)
+{
+ /*
+ * In the current implementation, not all terms are preserved across a
+ * sysjump. Future implementations may preserve additional information.
+ *
+ * time_since_ec_boot_ms: preserved, but wraps at ~50 days
+ * ec_reset_flags: preserved, with 'sysjump' added
+ * ap_resets_since_ec_boot: Not preserved
+ * recent_ap_reset[*]: Not preserved
+ */
+ struct ec_response_uptime_info *r = args->response;
+ timestamp_t now = get_time();
+ uint32_t now_ms = (uint32_t)(now.val / MSEC);
+ size_t log_address = 0;
+ size_t i = 0;
+
+ r->time_since_ec_boot_ms = now_ms;
+ r->ec_reset_flags = system_get_reset_flags();
+
+ memset(r->recent_ap_reset, 0, sizeof(r->recent_ap_reset));
+
+ mutex_lock(&reset_log_mutex);
+ r->ap_resets_since_ec_boot = ap_resets_since_ec_boot;
+ for (i = 0;
+ i != ARRAY_SIZE(reset_logs) && i != ARRAY_SIZE(r->recent_ap_reset);
+ ++i) {
+ log_address = (next_reset_log + i) &
+ (ARRAY_SIZE(reset_logs) - 1);
+ r->recent_ap_reset[i] = reset_logs[log_address];
+ }
+ mutex_unlock(&reset_log_mutex);
+
+ args->response_size = sizeof(*r);
+ return EC_RES_SUCCESS;
+}
+DECLARE_HOST_COMMAND(EC_CMD_GET_UPTIME_INFO,
+ host_command_get_uptime_info,
+ EC_VER_MASK(0));
+
+#endif /* !CONFIG_AP_RESET_LOG */
+
diff --git a/common/keyboard_8042.c b/common/keyboard_8042.c
index 08ebeb43b1..04e00d2bb2 100644
--- a/common/keyboard_8042.c
+++ b/common/keyboard_8042.c
@@ -736,7 +736,7 @@ static int handle_keyboard_command(uint8_t command, uint8_t *output)
break;
case I8042_SYSTEM_RESET:
- chipset_reset();
+ chipset_reset(CHIPSET_RESET_KB_SYSRESET);
break;
default:
diff --git a/common/keyboard_scan.c b/common/keyboard_scan.c
index cc3379ef16..02a1c105a0 100644
--- a/common/keyboard_scan.c
+++ b/common/keyboard_scan.c
@@ -391,7 +391,7 @@ static int check_runtime_keys(const uint8_t *state)
/* R = reboot */
CPRINTS("KB warm reboot");
keyboard_clear_buffer();
- chipset_reset();
+ chipset_reset(CHIPSET_RESET_KB_WARM_REBOOT);
return 1;
} else if (state[KEYBOARD_COL_KEY_H] == KEYBOARD_MASK_KEY_H) {
/* H = hibernate */
diff --git a/common/thermal.c b/common/thermal.c
index 7d7438f3e5..60101516da 100644
--- a/common/thermal.c
+++ b/common/thermal.c
@@ -139,7 +139,7 @@ static void thermal_control(void)
if (cond_went_true(&cond_hot[EC_TEMP_THRESH_HALT])) {
CPRINTS("thermal SHUTDOWN");
- chipset_force_shutdown();
+ chipset_force_shutdown(CHIPSET_SHUTDOWN_THERMAL);
} else if (cond_went_false(&cond_hot[EC_TEMP_THRESH_HALT])) {
/* We don't reboot automatically - the user has to push
* the power button. It's likely that we can't even
diff --git a/include/chipset.h b/include/chipset.h
index 5169689d8a..5e51e5991a 100644
--- a/include/chipset.h
+++ b/include/chipset.h
@@ -39,6 +39,66 @@ enum chipset_state_mask {
CHIPSET_STATE_STANDBY),
};
+/*
+ * Reason codes used by the AP after a shutdown to figure out why it was reset
+ * by the EC. These are sent in EC commands. Therefore, to maintain protocol
+ * compatibility:
+ * - New entries must be inserted prior to the _COUNT field
+ * - If an existing entry is no longer in service, it must be replaced with a
+ * RESERVED entry instead.
+ * - The semantic meaning of an entry should not change.
+ * - Do not exceed 2^15 - 1 for reset reasons or 2^16 - 1 for shutdown reasons.
+ */
+enum chipset_reset_reason {
+ CHIPSET_RESET_BEGIN = 0,
+ CHIPSET_RESET_UNKNOWN = CHIPSET_RESET_BEGIN,
+ /* Custom reason defined by a board.c or baseboard.c file */
+ CHIPSET_RESET_BOARD_CUSTOM,
+ /* Believe that the AP has hung */
+ CHIPSET_RESET_HANG_REBOOT,
+ /* Reset by EC console command */
+ CHIPSET_RESET_CONSOLE_CMD,
+ /* Keyboard module reset key combination */
+ CHIPSET_RESET_KB_SYSRESET,
+ /* Keyboard module warm reboot */
+ CHIPSET_RESET_KB_WARM_REBOOT,
+ /* Debug module warm reboot */
+ CHIPSET_RESET_DBG_WARM_REBOOT,
+ /* I cannot self-terminate. You must lower me into the steel. */
+ CHIPSET_RESET_AP_REQ,
+ /* Reset as side-effect of startup sequence */
+ CHIPSET_RESET_INIT,
+ CHIPSET_RESET_COUNT,
+};
+
+/*
+ * Hard shutdowns are logged on the same path as resets.
+ */
+enum chipset_shutdown_reason {
+ CHIPSET_SHUTDOWN_BEGIN = 1 << 15,
+ CHIPSET_SHUTDOWN_POWERFAIL = CHIPSET_SHUTDOWN_BEGIN,
+ /* Forcing a shutdown as part of EC initialization */
+ CHIPSET_SHUTDOWN_INIT,
+ /* Custom reason on a per-board basis. */
+ CHIPSET_SHUTDOWN_BOARD_CUSTOM,
+ /* This is a reason to inhibit startup, not cause shut down. */
+ CHIPSET_SHUTDOWN_BATTERY_INHIBIT,
+ /* A power_wait_signal is being asserted */
+ CHIPSET_SHUTDOWN_WAIT,
+ /* Critical battery level. */
+ CHIPSET_SHUTDOWN_BATTERY_CRIT,
+ /* Because you told me to. */
+ CHIPSET_SHUTDOWN_CONSOLE_CMD,
+ /* Forcing a shutdown to effect entry to G3. */
+ CHIPSET_SHUTDOWN_G3,
+ /* Force shutdown due to over-temperature. */
+ CHIPSET_SHUTDOWN_THERMAL,
+ /* Force a chipset shutdown from the power button through EC */
+ CHIPSET_SHUTDOWN_BUTTON,
+
+ CHIPSET_SHUTDOWN_COUNT,
+};
+
#ifdef HAS_TASK_CHIPSET
/**
@@ -70,12 +130,12 @@ void chipset_throttle_cpu(int throttle);
* This is intended for use when the system is too hot or battery power is
* critical.
*/
-void chipset_force_shutdown(void);
+void chipset_force_shutdown(enum chipset_shutdown_reason reason);
/**
* Reset the CPU and/or chipset.
*/
-void chipset_reset(void);
+void chipset_reset(enum chipset_reset_reason reason);
/**
* Interrupt handler to power GPIO inputs.
@@ -102,8 +162,11 @@ static inline int chipset_in_state(int state_mask)
static inline void chipset_exit_hard_off(void) { }
static inline void chipset_throttle_cpu(int throttle) { }
-static inline void chipset_force_shutdown(void) { }
-static inline void chipset_reset(void) { }
+static inline void chipset_force_shutdown(enum chipset_shutdown_reason reason)
+{
+}
+
+static inline void chipset_reset(enum chipset_reset_reason reason) { }
static inline void power_interrupt(enum gpio_signal signal) { }
static inline void chipset_handle_espi_reset_assert(void) { }
static inline void chipset_handle_reboot(void) { }
@@ -139,4 +202,18 @@ void chipset_reset_request_interrupt(enum gpio_signal signal);
*/
void chipset_power_signal_interrupt(enum gpio_signal signal);
+
+#ifdef CONFIG_CMD_AP_RESET_LOG
+
+/**
+ * Report that the AP is being reset to the reset log.
+ */
+void report_ap_reset(enum chipset_shutdown_reason reason);
+
+#else
+
+static inline void report_ap_reset(enum chipset_shutdown_reason reason) { }
+
+#endif /* !CONFIG_CMD_AP_RESET_LOG */
+
#endif /* __CROS_EC_CHIPSET_H */
diff --git a/include/config.h b/include/config.h
index 719a10124c..0d363c4e57 100644
--- a/include/config.h
+++ b/include/config.h
@@ -953,6 +953,7 @@
#define CONFIG_CMD_USBMUX
#undef CONFIG_CMD_USB_PD_PE
#define CONFIG_CMD_WAITMS
+#undef CONFIG_CMD_AP_RESET_LOG
/*****************************************************************************/
diff --git a/include/ec_commands.h b/include/ec_commands.h
index f20113cf9c..ee91c80710 100644
--- a/include/ec_commands.h
+++ b/include/ec_commands.h
@@ -4796,6 +4796,55 @@ struct __ec_align1 ec_params_set_cbi {
uint8_t data[]; /* For string and raw data */
};
+/*
+ * Information about resets of the AP by the EC and the EC's own uptime.
+ */
+#define EC_CMD_GET_UPTIME_INFO 0x0121
+
+struct __ec_align4 ec_response_uptime_info {
+ /*
+ * Number of milliseconds since the last EC boot. Sysjump resets
+ * typically do not restart the EC's time_since_boot epoch.
+ *
+ * WARNING: The EC's sense of time is much less accurate than the AP's
+ * sense of time, in both phase and frequency. This timebase is similar
+ * to CLOCK_MONOTONIC_RAW, but with 1% or more frequency error.
+ */
+ uint32_t time_since_ec_boot_ms;
+
+ /*
+ * Number of times the AP was reset by the EC since the last EC boot.
+ * Note that the AP may be held in reset by the EC during the initial
+ * boot sequence, such that the very first AP boot may count as more
+ * than one here.
+ */
+ uint32_t ap_resets_since_ec_boot;
+
+ /*
+ * The set of flags which describe the EC's most recent reset. See
+ * include/system.h RESET_FLAG_* for details.
+ */
+ uint32_t ec_reset_flags;
+
+ /* Empty log entries have both the cause and timestamp set to zero. */
+ struct ap_reset_log_entry {
+ /*
+ * See include/chipset.h: enum chipset_{reset,shutdown}_reason
+ * for details.
+ */
+ uint16_t reset_cause;
+
+ /* Reserved for protocol growth. */
+ uint16_t reserved;
+
+ /*
+ * The time of the reset's assertion, in milliseconds since the
+ * last EC boot, in the same epoch as time_since_ec_boot_ms.
+ * Set to zero if the log entry is empty.
+ */
+ uint32_t reset_time_ms;
+ } recent_ap_reset[4];
+};
/*****************************************************************************/
/* The command range 0x200-0x2FF is reserved for Rotor. */
diff --git a/power/apollolake.c b/power/apollolake.c
index dda74a29b9..a9ac1c06d9 100644
--- a/power/apollolake.c
+++ b/power/apollolake.c
@@ -6,6 +6,7 @@
/* Apollolake chipset power control module for Chrome EC */
#include "apollolake.h"
+#include "chipset.h"
#include "console.h"
#include "gpio.h"
#include "intel_x86.h"
@@ -42,8 +43,11 @@ static void internal_chipset_shutdown(void)
chipset_do_shutdown();
}
-void chipset_force_shutdown(void)
+void chipset_force_shutdown(enum chipset_shutdown_reason reason)
{
+ CPRINTS("%s: %d", __func__, reason);
+ report_ap_reset(reason);
+
/*
* This function is called from multiple tasks and hence it is racy! But
* since things are going down hard, it does not matter if some task
@@ -55,7 +59,7 @@ void chipset_force_shutdown(void)
enum power_state chipset_force_g3(void)
{
- chipset_force_shutdown();
+ chipset_force_shutdown(CHIPSET_SHUTDOWN_G3);
return POWER_G3;
}
diff --git a/power/braswell.c b/power/braswell.c
index e04918c055..4140c02adf 100644
--- a/power/braswell.c
+++ b/power/braswell.c
@@ -53,9 +53,10 @@
static int throttle_cpu; /* Throttle CPU? */
static int forcing_shutdown; /* Forced shutdown in progress? */
-void chipset_force_shutdown(void)
+void chipset_force_shutdown(enum chipset_shutdown_reason reason)
{
- CPRINTS("%s()", __func__);
+ CPRINTS("%s(%d)", __func__, reason);
+ report_ap_reset(reason);
/*
* Force power off. This condition will reset once the state machine
@@ -68,9 +69,10 @@ void chipset_force_shutdown(void)
forcing_shutdown = 1;
}
-void chipset_reset(void)
+void chipset_reset(enum chipset_reset_reason reason)
{
- CPRINTS("%s", __func__);
+ CPRINTS("%s: %d", __func__, reason);
+ report_ap_reset(reason);
/*
* Send a reset pulse to the PCH. This just causes it to
@@ -138,7 +140,7 @@ enum power_state power_handle_state(enum power_state state)
CPRINTS("Exit SOC G3");
if (power_wait_signals(IN_PGOOD_S5)) {
- chipset_force_shutdown();
+ chipset_force_shutdown(CHIPSET_SHUTDOWN_WAIT);
return POWER_G3;
}
@@ -165,7 +167,7 @@ enum power_state power_handle_state(enum power_state state)
/* Check for state transitions */
if (!power_has_signals(IN_PGOOD_S3)) {
/* Required rail went away */
- chipset_force_shutdown();
+ chipset_force_shutdown(CHIPSET_SHUTDOWN_POWERFAIL);
return POWER_S3S5;
} else if (gpio_get_level(GPIO_PCH_SLP_S3_L) == 1) {
/* Power up to next state */
@@ -182,7 +184,7 @@ enum power_state power_handle_state(enum power_state state)
/*wireless_set_state(WIRELESS_ON);*/
if (!power_has_signals(IN_PGOOD_S3)) {
- chipset_force_shutdown();
+ chipset_force_shutdown(CHIPSET_SHUTDOWN_POWERFAIL);
/*wireless_set_state(WIRELESS_OFF);*/
return POWER_S3S5;
@@ -222,7 +224,7 @@ enum power_state power_handle_state(enum power_state state)
case POWER_S0:
if (!power_has_signals(IN_PGOOD_ALWAYS_ON)) {
- chipset_force_shutdown();
+ chipset_force_shutdown(CHIPSET_SHUTDOWN_POWERFAIL);
return POWER_S0S3;
}
diff --git a/power/cannonlake.c b/power/cannonlake.c
index 47b1c912f0..1822b1d3c2 100644
--- a/power/cannonlake.c
+++ b/power/cannonlake.c
@@ -20,9 +20,9 @@
static int forcing_shutdown; /* Forced shutdown in progress? */
-void chipset_force_shutdown(void)
+void chipset_force_shutdown(enum chipset_shutdown_reason reason)
{
- CPRINTS("%s()", __func__);
+ CPRINTS("%s(%d)", __func__, reason);
/*
* Force off. Sending a reset command to the PMIC will power off
@@ -32,6 +32,7 @@ void chipset_force_shutdown(void)
* hold time on the PMIC.
*/
if (!chipset_in_state(CHIPSET_STATE_ANY_OFF)) {
+ report_ap_reset(reason);
forcing_shutdown = 1;
power_button_pch_press();
}
@@ -55,7 +56,7 @@ void chipset_handle_espi_reset_assert(void)
enum power_state chipset_force_g3(void)
{
int timeout = 50;
- chipset_force_shutdown();
+ chipset_force_shutdown(CHIPSET_SHUTDOWN_G3);
/* Turn off DSW load switch. */
gpio_set_level(GPIO_EN_PP3300_DSW, 0);
diff --git a/power/intel_x86.c b/power/intel_x86.c
index 1cc8beadf5..15c1cf1b4a 100644
--- a/power/intel_x86.c
+++ b/power/intel_x86.c
@@ -219,7 +219,7 @@ enum power_state common_intel_x86_power_handle_state(enum power_state state)
case POWER_S3:
if (!power_has_signals(IN_PGOOD_ALL_CORE)) {
/* Required rail went away */
- chipset_force_shutdown();
+ chipset_force_shutdown(CHIPSET_SHUTDOWN_POWERFAIL);
return POWER_S3S5;
} else if (chipset_get_sleep_signal(SYS_SLEEP_S3) == 1) {
/* Power up to next state */
@@ -232,7 +232,7 @@ enum power_state common_intel_x86_power_handle_state(enum power_state state)
case POWER_S0:
if (!power_has_signals(IN_PGOOD_ALL_CORE)) {
- chipset_force_shutdown();
+ chipset_force_shutdown(CHIPSET_SHUTDOWN_POWERFAIL);
return POWER_S0S3;
} else if (chipset_get_sleep_signal(SYS_SLEEP_S3) == 0) {
/* Power down to next state */
@@ -293,7 +293,8 @@ enum power_state common_intel_x86_power_handle_state(enum power_state state)
if (tries == CHARGER_INITIALIZED_TRIES) {
CPRINTS("power-up inhibited");
power_up_inhibited = 1;
- chipset_force_shutdown();
+ chipset_force_shutdown(
+ CHIPSET_SHUTDOWN_BATTERY_INHIBIT);
return POWER_G3;
}
@@ -320,7 +321,7 @@ enum power_state common_intel_x86_power_handle_state(enum power_state state)
#endif
if (power_wait_signals(CHIPSET_G3S5_POWERUP_SIGNAL)) {
- chipset_force_shutdown();
+ chipset_force_shutdown(CHIPSET_SHUTDOWN_WAIT);
return POWER_G3;
}
@@ -330,7 +331,7 @@ enum power_state common_intel_x86_power_handle_state(enum power_state state)
case POWER_S5S3:
if (!power_has_signals(IN_PGOOD_ALL_CORE)) {
/* Required rail went away */
- chipset_force_shutdown();
+ chipset_force_shutdown(CHIPSET_SHUTDOWN_POWERFAIL);
return POWER_S5G3;
}
@@ -349,7 +350,7 @@ enum power_state common_intel_x86_power_handle_state(enum power_state state)
case POWER_S3S0:
if (!power_has_signals(IN_PGOOD_ALL_CORE)) {
/* Required rail went away */
- chipset_force_shutdown();
+ chipset_force_shutdown(CHIPSET_SHUTDOWN_POWERFAIL);
return POWER_S3S5;
}
@@ -563,7 +564,7 @@ void power_chipset_handle_host_sleep_event(enum host_sleep_event state)
#endif
-void chipset_reset(void)
+void chipset_reset(enum chipset_reset_reason reason)
{
/*
* Irrespective of cold_reset value, always toggle SYS_RESET_L to
@@ -574,7 +575,7 @@ void chipset_reset(void)
* The EC cannot control warm vs cold reset of the chipset using
* SYS_RESET_L; it's more of a request.
*/
- CPRINTS("%s", __func__);
+ CPRINTS("%s: %d", __func__, reason);
/*
* Toggling SYS_RESET_L will not have any impact when it's already
@@ -585,6 +586,8 @@ void chipset_reset(void)
return;
}
+ report_ap_reset(reason);
+
gpio_set_level(GPIO_SYS_RESET_L, 0);
/*
* Debounce time for SYS_RESET_L is 16 ms. Wait twice that period
diff --git a/power/mt817x.c b/power/mt817x.c
index 37db9fe698..f13e04a1e3 100644
--- a/power/mt817x.c
+++ b/power/mt817x.c
@@ -418,7 +418,7 @@ enum power_state power_chipset_init(void)
* The warm reset triggers AP into the recovery mode (
* flash SPI from USB).
*/
- chipset_reset();
+ chipset_reset(CHIPSET_RESET_UNKNOWN);
init_power_state = POWER_G3;
} else {
@@ -474,8 +474,11 @@ static void chipset_turn_off_power_rails(void)
set_system_power(0);
}
-void chipset_force_shutdown(void)
+void chipset_force_shutdown(enum chipset_shutdown_reason reason)
{
+ CPRINTS("%s: %d", __func__, reason);
+ report_ap_reset(reason);
+
chipset_turn_off_power_rails();
/* clean-up internal variable */
@@ -638,9 +641,11 @@ static void power_on(void)
CPRINTS("AP running ...");
}
-void chipset_reset(void)
+void chipset_reset(enum chipset_reset_reason reason)
{
- CPRINTS("EC triggered warm reboot");
+ CPRINTS("%s: %d", __func__, reason);
+ report_ap_reset(reason);
+
set_warm_reset(1);
usleep(PMIC_WARM_RESET_H_HOLD_TIME);
/* deassert the reset signals */
diff --git a/power/mt8183.c b/power/mt8183.c
index 7e8d5ba57b..13a76532a2 100644
--- a/power/mt8183.c
+++ b/power/mt8183.c
@@ -89,9 +89,10 @@ static const struct power_seq_op s3s5_power_seq[] = {
static int forcing_shutdown;
-void chipset_force_shutdown(void)
+void chipset_force_shutdown(enum chipset_shutdown_reason reason)
{
- CPRINTS("%s()", __func__);
+ CPRINTS("%s(%d)", __func__, reason);
+ report_ap_reset(reason);
/*
* Force power off. This condition will reset once the state machine
@@ -100,12 +101,18 @@ void chipset_force_shutdown(void)
forcing_shutdown = 1;
task_wake(TASK_ID_CHIPSET);
}
-DECLARE_DEFERRED(chipset_force_shutdown);
+
+void chipset_force_shutdown_button(void)
+{
+ chipset_force_shutdown(CHIPSET_SHUTDOWN_BUTTON);
+}
+DECLARE_DEFERRED(chipset_force_shutdown_button);
/* If chipset needs to be reset, EC also reboots to RO. */
-void chipset_reset(void)
+void chipset_reset(enum chipset_reset_reason reason)
{
- CPRINTS("%s", __func__);
+ CPRINTS("%s: %d", __func__, reason);
+ report_ap_reset(reason);
cflush();
system_reset(SYSTEM_RESET_HARD);
@@ -214,7 +221,7 @@ enum power_state power_handle_state(enum power_state state)
* after debounce (32 ms), minus PMIC_EN_PULSE_MS above.
* It would be good to avoid another _EN pulse above.
*/
- chipset_reset();
+ chipset_reset(CHIPSET_RESET_INIT);
}
/* Wait for PMIC to bring up rails. */
@@ -238,7 +245,7 @@ enum power_state power_handle_state(enum power_state state)
power_seq_run(s3s0_power_seq, ARRAY_SIZE(s3s0_power_seq));
if (power_wait_signals(IN_PGOOD_S0)) {
- chipset_force_shutdown();
+ chipset_force_shutdown(CHIPSET_SHUTDOWN_WAIT);
return POWER_S0S3;
}
@@ -277,7 +284,8 @@ enum power_state power_handle_state(enum power_state state)
*/
if (power_button_is_pressed()) {
forcing_shutdown = 1;
- hook_call_deferred(&chipset_force_shutdown_data, -1);
+ hook_call_deferred(&chipset_force_shutdown_button_data,
+ -1);
}
return POWER_S3;
@@ -306,11 +314,11 @@ static void power_button_changed(void)
chipset_exit_hard_off();
/* Delayed power down from S0/S3, cancel on PB release */
- hook_call_deferred(&chipset_force_shutdown_data,
+ hook_call_deferred(&chipset_force_shutdown_button_data,
FORCED_SHUTDOWN_DELAY);
} else {
/* Power button released, cancel deferred shutdown */
- hook_call_deferred(&chipset_force_shutdown_data, -1);
+ hook_call_deferred(&chipset_force_shutdown_button_data, -1);
}
}
DECLARE_HOOK(HOOK_POWER_BUTTON_CHANGE, power_button_changed, HOOK_PRIO_DEFAULT);
diff --git a/power/rk3288.c b/power/rk3288.c
index 22ec8eff5b..8144401b54 100644
--- a/power/rk3288.c
+++ b/power/rk3288.c
@@ -223,7 +223,7 @@ enum power_state power_chipset_init(void)
* The warm reset triggers AP into the RK recovery mode (
* flash SPI from USB).
*/
- chipset_reset();
+ chipset_reset(CHIPSET_RESET_INIT);
init_power_state = POWER_G3;
} else {
@@ -265,8 +265,10 @@ static void chipset_turn_off_power_rails(void)
set_pmic_warm_reset(1);
}
-void chipset_force_shutdown(void)
+void chipset_force_shutdown(enum chipset_shutdown_reason reason)
{
+ CPRINTS("%s(%d)", __func__, reason);
+ report_ap_reset(reason);
chipset_turn_off_power_rails();
/* clean-up internal variable */
@@ -380,9 +382,11 @@ static void power_off(void)
CPRINTS("power shutdown complete");
}
-void chipset_reset(void)
+void chipset_reset(enum chipset_reset_reason reason)
{
- CPRINTS("EC triggered warm reboot");
+ CPRINTS("%s(%d)", __func__, reason);
+ report_ap_reset(reason);
+
CPRINTS("assert GPIO_PMIC_WARM_RESET_L for %d ms",
PMIC_WARM_RESET_L_HOLD_TIME / MSEC);
set_pmic_warm_reset(1);
diff --git a/power/rk3399.c b/power/rk3399.c
index e70bd3e84f..b229684e7a 100644
--- a/power/rk3399.c
+++ b/power/rk3399.c
@@ -207,9 +207,10 @@ static const struct power_seq_op s3s5_power_seq[] = {
static int forcing_shutdown;
-void chipset_force_shutdown(void)
+void chipset_force_shutdown(enum chipset_shutdown_reason reason)
{
- CPRINTS("%s()", __func__);
+ CPRINTS("%s(%d)", __func__, reason);
+ report_ap_reset(reason);
/*
* Force power off. This condition will reset once the state machine
@@ -220,13 +221,14 @@ void chipset_force_shutdown(void)
}
#define SYS_RST_HOLD_US (1 * MSEC)
-void chipset_reset(void)
+void chipset_reset(enum chipset_reset_reason reason)
{
#ifdef CONFIG_CMD_RTC
/* Print out the RTC to help correlate resets in logs. */
print_system_rtc(CC_CHIPSET);
#endif
- CPRINTS("%s", __func__);
+ CPRINTS("%s(%d)", __func__, reason);
+ report_ap_reset(reason);
/* Pulse SYS_RST */
gpio_set_level(GPIO_SYS_RST_L, 0);
@@ -389,7 +391,8 @@ enum power_state power_handle_state(enum power_state state)
if (charge_want_shutdown() ||
tries > CHARGER_INITIALIZED_TRIES) {
CPRINTS("power-up inhibited");
- chipset_force_shutdown();
+ chipset_force_shutdown(
+ CHIPSET_SHUTDOWN_BATTERY_INHIBIT);
return POWER_G3;
}
@@ -408,7 +411,7 @@ enum power_state power_handle_state(enum power_state state)
#endif
if (power_wait_signals(IN_PGOOD_S3)) {
- chipset_force_shutdown();
+ chipset_force_shutdown(CHIPSET_SHUTDOWN_WAIT);
return POWER_S3S5;
}
@@ -458,7 +461,7 @@ enum power_state power_handle_state(enum power_state state)
#else
if (power_wait_signals(IN_PGOOD_S0)) {
#endif /* POWER_SIGNAL_POLLING */
- chipset_force_shutdown();
+ chipset_force_shutdown(CHIPSET_SHUTDOWN_WAIT);
return POWER_S0S3;
}
diff --git a/power/sdm845.c b/power/sdm845.c
index f6a22c54ae..350e940504 100644
--- a/power/sdm845.c
+++ b/power/sdm845.c
@@ -141,7 +141,7 @@ enum power_on_event_t {
static void chipset_reset_request_handler(void)
{
CPRINTS("AP wants reset");
- chipset_reset();
+ chipset_reset(CHIPSET_RESET_AP_REQ);
}
DECLARE_DEFERRED(chipset_reset_request_handler);
@@ -582,22 +582,26 @@ static int check_for_power_off_event(void)
/*****************************************************************************/
/* Chipset interface */
-void chipset_force_shutdown(void)
+void chipset_force_shutdown(enum chipset_shutdown_reason reason)
{
- CPRINTS("EC triggered shutdown");
+ CPRINTS("%s(%d)", __func__, reason);
+ report_ap_reset(reason);
+
power_off();
/* Clean-up internal variable */
power_request = POWER_REQ_NONE;
}
-void chipset_reset(void)
+void chipset_reset(enum chipset_reset_reason reason)
{
/*
* Before we can reprogram the PMIC to make the PMIC RESIN_N pin as
* reset pin and zero-latency. We do cold reset instead.
*/
- CPRINTS("EC triggered cold reboot");
+ CPRINTS("%s(%d)", __func__, reason);
+ report_ap_reset(reason);
+
bypass_power_lost_trigger = 1;
power_off();
bypass_power_lost_trigger = 0;
diff --git a/power/skylake.c b/power/skylake.c
index 8df01c542f..a5cf6aa0a5 100644
--- a/power/skylake.c
+++ b/power/skylake.c
@@ -23,7 +23,7 @@
static int forcing_shutdown; /* Forced shutdown in progress? */
-void chipset_force_shutdown(void)
+void chipset_force_shutdown(enum chipset_shutdown_reason reason)
{
CPRINTS("%s()", __func__);
@@ -35,6 +35,7 @@ void chipset_force_shutdown(void)
* hold time on the PMIC.
*/
if (!chipset_in_state(CHIPSET_STATE_ANY_OFF)) {
+ report_ap_reset(reason);
forcing_shutdown = 1;
power_button_pch_press();
}
diff --git a/power/stoney.c b/power/stoney.c
index 3034eee41e..1cc08de9fc 100644
--- a/power/stoney.c
+++ b/power/stoney.c
@@ -30,13 +30,14 @@
static int forcing_shutdown; /* Forced shutdown in progress? */
-void chipset_force_shutdown(void)
+void chipset_force_shutdown(enum chipset_shutdown_reason reason)
{
CPRINTS("%s()", __func__);
if (!chipset_in_state(CHIPSET_STATE_ANY_OFF)) {
forcing_shutdown = 1;
power_button_pch_press();
+ report_ap_reset(reason);
}
}
@@ -46,15 +47,16 @@ static void chipset_force_g3(void)
gpio_set_level(GPIO_EN_PWR_A, 0);
}
-void chipset_reset(void)
+void chipset_reset(enum chipset_reset_reason reason)
{
- CPRINTS("%s", __func__);
+ CPRINTS("%s: %d", __func__, reason);
if (chipset_in_state(CHIPSET_STATE_ANY_OFF)) {
CPRINTS("Can't reset: SOC is off");
return;
}
+ report_ap_reset(reason);
/*
* Send a pulse to SYS_RST to trigger a warm reset.
*/
diff --git a/test/sbs_charging_v2.c b/test/sbs_charging_v2.c
index e69cbdfb34..e266e59215 100644
--- a/test/sbs_charging_v2.c
+++ b/test/sbs_charging_v2.c
@@ -36,7 +36,7 @@ static void reset_mocks(void)
shutdown_warning_time.val = 0ULL;
}
-void chipset_force_shutdown(void)
+void chipset_force_shutdown(enum chipset_shutdown_reason reason)
{
is_shutdown = 1;
mock_chipset_state = CHIPSET_STATE_HARD_OFF;
diff --git a/util/ectool.c b/util/ectool.c
index f1de218278..cf28ccca08 100644
--- a/util/ectool.c
+++ b/util/ectool.c
@@ -17,6 +17,7 @@
#include "anx74xx.h"
#include "battery.h"
#include "comm-host.h"
+#include "chipset.h"
#include "compile_time_macros.h"
#include "cros_ec_dev.h"
#include "ec_panicinfo.h"
@@ -256,6 +257,9 @@ const char help_str[] =
" Get/set TMP006 calibration\n"
" tmp006raw <tmp006_index>\n"
" Get raw TMP006 data\n"
+ " uptimeinfo\n"
+ " Get info about how long the EC has been running and the most\n"
+ " recent AP resets\n"
" usbchargemode <port> <mode>\n"
" Set USB charging mode\n"
" usbmux <mux>\n"
@@ -622,6 +626,125 @@ int cmd_cmdversions(int argc, char *argv[])
return 0;
}
+/*
+ * Convert a reset cause ID to human-readable string, providing total coverage
+ * of the 'cause' space. The returned string points to static storage and must
+ * not be free()ed.
+ */
+static const char *reset_cause_to_str(uint16_t cause)
+{
+ static const char * const reset_causes[] = {
+ "(reset unknown)",
+ "reset: board custom",
+ "reset: ap hang detected",
+ "reset: console command",
+ "reset: keyboard sysreset",
+ "reset: keyboard warm reboot",
+ "reset: debug warm reboot",
+ "reset: at AP's request",
+ "reset: during EC initialization",
+ };
+ BUILD_ASSERT(ARRAY_SIZE(reset_causes) == CHIPSET_RESET_COUNT);
+
+ static const char * const shutdown_causes[] = {
+ "shutdown: power failure",
+ "shutdown: during EC initialization",
+ "shutdown: board custom",
+ "shutdown: battery voltage startup inhibit",
+ "shutdown: power wait asserted",
+ "shutdown: critical battery",
+ "shutdown: by console command",
+ "shutdown: entering G3",
+ "shutdown: thermal",
+ "shutdown: power button",
+ };
+ BUILD_ASSERT(ARRAY_SIZE(shutdown_causes) ==
+ CHIPSET_SHUTDOWN_COUNT - CHIPSET_SHUTDOWN_BEGIN);
+
+ if (cause < CHIPSET_RESET_COUNT)
+ return reset_causes[cause];
+
+ if (cause < CHIPSET_SHUTDOWN_BEGIN)
+ return "(reset unknown)";
+
+ if (cause < CHIPSET_SHUTDOWN_COUNT)
+ return shutdown_causes[cause - CHIPSET_SHUTDOWN_BEGIN];
+
+ return "(shutdown unknown)";
+}
+
+int cmd_uptimeinfo(int argc, char *argv[])
+{
+ static const char * const reset_flag_strings[] = {
+ "other",
+ "reset-pin",
+ "brownout",
+ "power-on",
+ "watchdog",
+ "soft",
+ "hibernate",
+ "rtc-alarm",
+ "wake-pin",
+ "low-battery",
+ "sysjump",
+ "hard",
+ "ap-off",
+ "preserved",
+ "usb-resume",
+ "rdd",
+ "rbox",
+ "security"
+ };
+
+ struct ec_response_uptime_info r;
+ int rv;
+ int i;
+ int flag_count;
+ uint32_t flag;
+
+ if (argc != 1) {
+ fprintf(stderr, "uptimeinfo takes no arguments");
+ return -1;
+ }
+
+ rv = ec_command(EC_CMD_GET_UPTIME_INFO, 0, NULL, 0, &r, sizeof(r));
+ if (rv < 0) {
+ fprintf(stderr, "ERROR: EC_CMD_GET_UPTIME_INFO failed; %d\n",
+ rv);
+ return rv;
+ }
+
+ printf("EC uptime: %d.%03d seconds\n",
+ r.time_since_ec_boot_ms / 1000,
+ r.time_since_ec_boot_ms % 1000);
+
+ printf("AP resets since EC boot: %d\n", r.ap_resets_since_ec_boot);
+
+ printf("Most recent AP reset causes:\n");
+ for (i = 0; i != ARRAY_SIZE(r.recent_ap_reset); ++i) {
+ if (r.recent_ap_reset[i].reset_time_ms == 0)
+ continue;
+
+ printf("\t%d.%03d: %s\n",
+ r.recent_ap_reset[i].reset_time_ms / 1000,
+ r.recent_ap_reset[i].reset_time_ms % 1000,
+ reset_cause_to_str(r.recent_ap_reset[i].reset_cause));
+ }
+
+ printf("EC reset flags at last EC boot: ");
+ flag_count = 0;
+ for (flag = 0; flag != ARRAY_SIZE(reset_flag_strings); ++flag) {
+ if ((r.ec_reset_flags & (1 << flag)) != 0) {
+ if (flag_count)
+ printf(" | ");
+ printf(reset_flag_strings[flag]);
+ flag_count++;
+ }
+ }
+ printf("\n");
+ return 0;
+}
+
int cmd_version(int argc, char *argv[])
{
struct ec_response_get_version r;
@@ -8115,6 +8238,7 @@ const struct command commands[] = {
{"tpframeget", cmd_tp_frame_get},
{"tmp006cal", cmd_tmp006cal},
{"tmp006raw", cmd_tmp006raw},
+ {"uptimeinfo", cmd_uptimeinfo},
{"usbchargemode", cmd_usb_charge_set_mode},
{"usbmux", cmd_usb_mux},
{"usbpd", cmd_usb_pd},