/** * Handle a pending reboot command. */ static int handle_pending_reboot(enum ec_reboot_cmd cmd) { switch (cmd) { case EC_REBOOT_CANCEL: return EC_SUCCESS; case EC_REBOOT_JUMP_RO: return system_run_image_copy(SYSTEM_IMAGE_RO); case EC_REBOOT_JUMP_RW: return system_run_image_copy(SYSTEM_IMAGE_RW); case EC_REBOOT_COLD: #ifdef HAS_TASK_PDCMD /* Reboot the PD chip as well */ gpio_set_level(GPIO_USB_MCU_RST, 1); usleep(100); gpio_set_level(GPIO_USB_MCU_RST, 0); #endif system_reset(SYSTEM_RESET_HARD); /* That shouldn't return... */ return EC_ERROR_UNKNOWN; case EC_REBOOT_DISABLE_JUMP: system_disable_jump(); return EC_SUCCESS; #ifdef CONFIG_HIBERNATE case EC_REBOOT_HIBERNATE: CPRINTS("system hibernating"); system_hibernate(0, 0); /* That shouldn't return... */ return EC_ERROR_UNKNOWN; #endif default: return EC_ERROR_INVAL; } }
/** * Handle a pending reboot command. */ static int handle_pending_reboot(enum ec_reboot_cmd cmd) { switch (cmd) { case EC_REBOOT_CANCEL: return EC_SUCCESS; case EC_REBOOT_JUMP_RO: return system_run_image_copy(SYSTEM_IMAGE_RO); case EC_REBOOT_JUMP_RW: return system_run_image_copy(SYSTEM_IMAGE_RW); case EC_REBOOT_COLD: system_reset(SYSTEM_RESET_HARD); /* That shouldn't return... */ return EC_ERROR_UNKNOWN; case EC_REBOOT_DISABLE_JUMP: system_disable_jump(); return EC_SUCCESS; case EC_REBOOT_HIBERNATE: CPRINTS("system hibernating"); system_hibernate(0, 0); /* That shouldn't return... */ return EC_ERROR_UNKNOWN; default: return EC_ERROR_INVAL; } }
static void charge_shutdown(void) { /* Hibernate immediately if battery level is too low */ if (charge_want_shutdown()) { CPRINTS("charge force EC hibernate after " "shutdown due to low battery"); system_hibernate(0, 0); } }
/* * Send host event to the AP if the battery is temperature or charge level * is critical. Force-shutdown if the problem isn't corrected after timeout. */ static void shutdown_on_critical_battery(void) { int batt_temp_c; int battery_critical = 0; /* * TODO(crosbug.com/p/27642): The thermal loop should watch the battery * temp, so it can turn fans on. */ batt_temp_c = DECI_KELVIN_TO_CELSIUS(curr.batt.temperature); if (battery_too_hot(batt_temp_c)) { CPRINTS("Batt temp out of range: %dC", batt_temp_c); battery_critical = 1; } if (battery_too_low() && !curr.batt_is_charging) { CPRINTS("Low battery: %d%%, %dmV", curr.batt.state_of_charge, curr.batt.voltage); battery_critical = 1; } if (!battery_critical) { /* Reset shutdown warning time */ shutdown_warning_time.val = 0; return; } if (!shutdown_warning_time.val) { CPRINTS("charge warn shutdown due to critical battery"); shutdown_warning_time = get_time(); if (!chipset_in_state(CHIPSET_STATE_ANY_OFF)) host_set_single_event(EC_HOST_EVENT_BATTERY_SHUTDOWN); } else if (get_time().val > shutdown_warning_time.val + CRITICAL_BATTERY_SHUTDOWN_TIMEOUT_US) { if (chipset_in_state(CHIPSET_STATE_ANY_OFF)) { #ifdef CONFIG_HIBERNATE /* Timeout waiting for charger to provide more power */ CPRINTS( "charge force EC hibernate due to critical battery"); system_hibernate(0, 0); #elif defined(CONFIG_BATTERY_CRITICAL_SHUTDOWN_CUT_OFF) CPRINTS( "charge force battery cut-off due to critical level"); board_cut_off_battery(); #endif } else { /* Timeout waiting for AP to shut down, so kill it */ CPRINTS( "charge force shutdown due to critical battery"); chipset_force_shutdown(); } } }
/** * Prevent battery from going into deep discharge state */ static void low_battery_shutdown(struct charge_state_context *ctx) { if (chipset_in_state(CHIPSET_STATE_ANY_OFF)) { /* AP is off, so shut down the EC now */ CPRINTS("charge force EC hibernate due to low battery"); system_hibernate(0, 0); } else if (!ctx->shutdown_warning_time.val) { /* Warn AP battery level is so low we'll shut down */ CPRINTS("charge warn shutdown due to low battery"); ctx->shutdown_warning_time = get_time(); host_set_single_event(EC_HOST_EVENT_BATTERY_SHUTDOWN); } else if (get_time().val > ctx->shutdown_warning_time.val + LOW_BATTERY_SHUTDOWN_TIMEOUT_US) { /* Timeout waiting for AP to shut down, so kill it */ CPRINTS("charge force shutdown due to low battery"); chipset_force_shutdown(); } }
static int command_hibernate(int argc, char **argv) { int seconds = 0; int microseconds = 0; if (argc >= 2) seconds = strtoi(argv[1], NULL, 0); if (argc >= 3) microseconds = strtoi(argv[2], NULL, 0); if (seconds || microseconds) ccprintf("Hibernating for %d.%06d s\n", seconds, microseconds); else ccprintf("Hibernating until wake pin asserted.\n"); system_hibernate(seconds, microseconds); return EC_SUCCESS; }
/** * Common handler for steady states * * @param state Current power state * @return Updated power state */ static enum power_state power_common_state(enum power_state state) { switch (state) { case POWER_G3: if (want_g3_exit) { want_g3_exit = 0; return POWER_G3S5; } in_want = 0; #ifdef CONFIG_HIBERNATE if (extpower_is_present()) task_wait_event(-1); else { uint64_t target_time; uint64_t time_now = get_time().val; uint32_t delay = hibernate_delay; #ifdef CONFIG_HIBERNATE_BATT_PCT if (charge_get_percent() <= CONFIG_HIBERNATE_BATT_PCT && CONFIG_HIBERNATE_BATT_SEC < delay) delay = CONFIG_HIBERNATE_BATT_SEC; #endif target_time = last_shutdown_time + delay * 1000000ull; if (time_now > target_time) { /* * Time's up. Hibernate until wake pin * asserted. */ #ifdef CONFIG_LOW_POWER_PSEUDO_G3 enter_pseudo_g3(); #else CPRINTS("hibernating"); system_hibernate(0, 0); #endif } else { uint64_t wait = target_time - time_now; if (wait > TASK_MAX_WAIT_US) wait = TASK_MAX_WAIT_US; /* Wait for a message */ task_wait_event(wait); } } #else /* !CONFIG_HIBERNATE */ task_wait_event(-1); #endif break; case POWER_S5: /* * If the power button is pressed before S5 inactivity timer * expires, the timer will be cancelled and the task of the * power state machine will be back here again. Since we are * here, which means the system has been waiting for CPU * starting up, we don't need want_g3_exit flag to be set * anymore. Therefore, we can reset the flag here to prevent * the situation that the flag is still set after S5 inactivity * timer expires, which can cause the system to exit G3 again. */ want_g3_exit = 0; /* Wait for inactivity timeout */ power_wait_signals(0); if (task_wait_event(S5_INACTIVITY_TIMEOUT) == TASK_EVENT_TIMER) { /* Prepare to drop to G3; wake not requested yet */ return POWER_S5G3; } break; case POWER_S3: /* Wait for a message */ power_wait_signals(0); task_wait_event(-1); break; case POWER_S0: /* Wait for a message */ power_wait_signals(0); task_wait_event(-1); break; default: /* No common functionality for transition states */ break; } return state; }
/** * Common handler for steady states * * @param state Current power state * @return Updated power state */ static enum power_state power_common_state(enum power_state state) { switch (state) { case POWER_G3: if (want_g3_exit) { want_g3_exit = 0; return POWER_G3S5; } in_want = 0; #ifdef CONFIG_HIBERNATE if (extpower_is_present()) task_wait_event(-1); else { uint64_t target_time; uint64_t time_now = get_time().val; uint32_t delay = hibernate_delay; #ifdef CONFIG_HIBERNATE_BATT_PCT if (charge_get_percent() <= CONFIG_HIBERNATE_BATT_PCT && CONFIG_HIBERNATE_BATT_SEC < delay) delay = CONFIG_HIBERNATE_BATT_SEC; #endif target_time = last_shutdown_time + delay * 1000000ull; if (time_now > target_time) { /* * Time's up. Hibernate until wake pin * asserted. */ #ifdef CONFIG_LOW_POWER_PSEUDO_G3 enter_pseudo_g3(); #else CPRINTS("hibernating"); system_hibernate(0, 0); #endif } else { uint64_t wait = target_time - time_now; if (wait > TASK_MAX_WAIT_US) wait = TASK_MAX_WAIT_US; /* Wait for a message */ task_wait_event(wait); } } #else /* !CONFIG_HIBERNATE */ task_wait_event(-1); #endif break; case POWER_S5: /* Wait for inactivity timeout */ power_wait_signals(0); if (task_wait_event(S5_INACTIVITY_TIMEOUT) == TASK_EVENT_TIMER) { /* Drop to G3; wake not requested yet */ want_g3_exit = 0; return POWER_S5G3; } break; case POWER_S3: /* Wait for a message */ power_wait_signals(0); task_wait_event(-1); break; case POWER_S0: /* Wait for a message */ power_wait_signals(0); task_wait_event(-1); break; default: /* No common functionality for transition states */ break; } return state; }