diff options
-rw-r--r-- | baseboard/grunt/baseboard.h | 1 | ||||
-rw-r--r-- | baseboard/octopus/baseboard.c | 2 | ||||
-rw-r--r-- | board/host/chipset.c | 6 | ||||
-rw-r--r-- | board/kukui/board.c | 2 | ||||
-rw-r--r-- | board/rainier/board.c | 4 | ||||
-rw-r--r-- | board/samus/power_sequence.c | 20 | ||||
-rw-r--r-- | board/scarlet/board.c | 4 | ||||
-rw-r--r-- | common/ap_hang_detect.c | 2 | ||||
-rw-r--r-- | common/button.c | 4 | ||||
-rw-r--r-- | common/charge_state_v2.c | 2 | ||||
-rw-r--r-- | common/chipset.c | 73 | ||||
-rw-r--r-- | common/keyboard_8042.c | 2 | ||||
-rw-r--r-- | common/keyboard_scan.c | 2 | ||||
-rw-r--r-- | common/thermal.c | 2 | ||||
-rw-r--r-- | include/chipset.h | 85 | ||||
-rw-r--r-- | include/config.h | 1 | ||||
-rw-r--r-- | include/ec_commands.h | 49 | ||||
-rw-r--r-- | power/apollolake.c | 8 | ||||
-rw-r--r-- | power/braswell.c | 18 | ||||
-rw-r--r-- | power/cannonlake.c | 7 | ||||
-rw-r--r-- | power/intel_x86.c | 19 | ||||
-rw-r--r-- | power/mt817x.c | 13 | ||||
-rw-r--r-- | power/mt8183.c | 28 | ||||
-rw-r--r-- | power/rk3288.c | 12 | ||||
-rw-r--r-- | power/rk3399.c | 17 | ||||
-rw-r--r-- | power/sdm845.c | 14 | ||||
-rw-r--r-- | power/skylake.c | 3 | ||||
-rw-r--r-- | power/stoney.c | 8 | ||||
-rw-r--r-- | test/sbs_charging_v2.c | 2 | ||||
-rw-r--r-- | util/ectool.c | 124 |
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}, |