static int host_command_hibernation_delay(struct host_cmd_handler_args *args) { const struct ec_params_hibernation_delay *p = args->params; struct ec_response_hibernation_delay *r = args->response; uint32_t time_g3 = (uint32_t)((get_time().val - last_shutdown_time) / SECOND); /* Only change the hibernation delay if seconds is non-zero. */ if (p->seconds) hibernate_delay = p->seconds; if (state == POWER_G3 && !extpower_is_present()) r->time_g3 = time_g3; else r->time_g3 = 0; if ((time_g3 != 0) && (time_g3 > hibernate_delay)) r->time_remaining = 0; else r->time_remaining = hibernate_delay - time_g3; r->hibernate_delay = hibernate_delay; args->response_size = sizeof(struct ec_response_hibernation_delay); return EC_SUCCESS; }
/* Initialize board. */ static void board_init(void) { /* Enable PD MCU interrupt */ gpio_enable_interrupt(GPIO_PD_MCU_INT); /* Enable VBUS interrupt */ gpio_enable_interrupt(GPIO_USB_C0_VBUS_WAKE_L); gpio_enable_interrupt(GPIO_USB_C1_VBUS_WAKE_L); /* Enable pericom BC1.2 interrupts */ gpio_enable_interrupt(GPIO_USB_C0_BC12_INT_L); gpio_enable_interrupt(GPIO_USB_C1_BC12_INT_L); /* Provide AC status to the PCH */ gpio_set_level(GPIO_PCH_ACOK, extpower_is_present()); /* Proto board workarounds */ if (system_get_board_version() == 0) { /* Disable interrupt for SLP_S0 */ gpio_set_flags(GPIO_PCH_SLP_S0_L, GPIO_INPUT | GPIO_PULL_DOWN); /* Add internal pullup on PLATFORM_EC_PROCHOT */ gpio_set_flags(GPIO_PLATFORM_EC_PROCHOT, GPIO_INPUT | GPIO_PULL_UP); } }
static void fake_hibernate_ac_hook(void) { if (fake_hibernate && extpower_is_present()) { ccprints("%s() resets EC", __func__); cflush(); system_reset(SYSTEM_RESET_HARD); } }
enum battery_disconnect_state battery_get_disconnect_state(void) { uint8_t data[6]; int rv; /* * Take note if we find that the battery isn't in disconnect state, * and always return NOT_DISCONNECTED without probing the battery. * This assumes the battery will not go to disconnect state during * runtime. */ static int not_disconnected; if (not_disconnected) return BATTERY_NOT_DISCONNECTED; if (extpower_is_present()) { /* Check if battery charging + discharging is disabled. */ rv = sb_write(SB_MANUFACTURER_ACCESS, PARAM_OPERATION_STATUS); if (rv) return BATTERY_DISCONNECT_ERROR; rv = sb_read_string(I2C_PORT_BATTERY, BATTERY_ADDR, SB_ALT_MANUFACTURER_ACCESS, data, 6); if (rv || (~data[3] & (BATTERY_DISCHARGING_DISABLED | BATTERY_CHARGING_DISABLED))) { not_disconnected = 1; return BATTERY_NOT_DISCONNECTED; } /* * Battery is neither charging nor discharging. Verify that * we didn't enter this state due to a safety fault. */ rv = sb_write(SB_MANUFACTURER_ACCESS, PARAM_SAFETY_STATUS); if (rv) return BATTERY_DISCONNECT_ERROR; rv = sb_read_string(I2C_PORT_BATTERY, BATTERY_ADDR, SB_ALT_MANUFACTURER_ACCESS, data, 6); if (rv || data[2] || data[3] || data[4] || data[5]) return BATTERY_DISCONNECT_ERROR; /* * Battery is present and also the status is initialized and * no safety fault, battery is disconnected. */ if (battery_is_present() == BP_YES) return BATTERY_DISCONNECTED; } not_disconnected = 1; return BATTERY_NOT_DISCONNECTED; }
uint32_t charge_get_flags(void) { uint32_t flags = 0; if (state_machine_force_idle) flags |= CHARGE_FLAG_FORCE_IDLE; if (extpower_is_present()) flags |= CHARGE_FLAG_EXTERNAL_POWER; return flags; }
static void update_battery_led(void) { int alarm; int led_on = 0; if(extpower_is_present()){ battery_status(&alarm); if((alarm & ALARM_CHARGED) && !gpio_get_level(GPIO_CHARGER_EN)) led_on = 1; } gpio_set_level(GPIO_CHARGING_LED, led_on); }
static void power_ac_change(void) { if (extpower_is_present()) { CPRINTS("AC on"); } else { CPRINTS("AC off"); if (state == POWER_G3) { last_shutdown_time = get_time().val; task_wake(TASK_ID_CHIPSET); } } }
/* Initialize board. */ static void board_init(void) { /* Enable PD MCU interrupt */ gpio_enable_interrupt(GPIO_PD_MCU_INT); /* Enable VBUS interrupt */ gpio_enable_interrupt(GPIO_USB_C0_VBUS_WAKE_L); /* Enable pericom BC1.2 interrupts */ gpio_enable_interrupt(GPIO_USB_C0_BC12_INT_L); /* Provide AC status to the PCH */ gpio_set_level(GPIO_PCH_ACOK, extpower_is_present()); }
/** * AC change notification hook. * * This is triggered when the AC state changes, so that we can update the * memory-mapped AC status and our charging state. */ static void ac_change_hook(void) { /** * Update the memory-mapped AC_PRESENT flag immediately so the * state is correct prior to the host being notified of the AC * change event. */ if (extpower_is_present()) *task_ctx.memmap_batt_flags |= EC_BATT_FLAG_AC_PRESENT; else *task_ctx.memmap_batt_flags &= ~EC_BATT_FLAG_AC_PRESENT; /* Wake up the task now */ task_wake(TASK_ID_CHARGER); }
static int command_hibernation_delay(int argc, char **argv) { char *e; uint32_t time_g3 = ((uint32_t)(get_time().val - last_shutdown_time)) / SECOND; if (argc >= 2) { uint32_t s = strtoi(argv[1], &e, 0); if (*e) return EC_ERROR_PARAM1; hibernate_delay = s; } /* Print the current setting */ ccprintf("Hibernation delay: %d s\n", hibernate_delay); if (state == POWER_G3 && !extpower_is_present()) { ccprintf("Time G3: %d s\n", time_g3); ccprintf("Time left: %d s\n", hibernate_delay - time_g3); } return EC_SUCCESS; }
/* Main loop */ void charger_task(void) { int sleep_usec; int need_static = 1; const struct charger_info * const info = charger_get_info(); /* Get the battery-specific values */ batt_info = battery_get_info(); prev_ac = prev_charge = -1; chg_ctl_mode = CHARGE_CONTROL_NORMAL; shutdown_warning_time.val = 0UL; battery_seems_to_be_dead = 0; /* * If system is not locked and we don't have a battery to live on, * then use max input current limit so that we can pull as much power * as needed. */ battery_get_params(&curr.batt); prev_bp = curr.batt.is_present; curr.desired_input_current = get_desired_input_current(prev_bp, info); while (1) { #ifdef CONFIG_SB_FIRMWARE_UPDATE if (sb_fw_update_in_progress()) { task_wait_event(CHARGE_MAX_SLEEP_USEC); continue; } #endif /* Let's see what's going on... */ curr.ts = get_time(); sleep_usec = 0; problems_exist = 0; curr.ac = extpower_is_present(); if (curr.ac != prev_ac) { if (curr.ac) { /* * Some chargers are unpowered when the AC is * off, so we'll reinitialize it when AC * comes back and set the input current limit. * Try again if it fails. */ int rv = charger_post_init(); if (rv != EC_SUCCESS) { problem(PR_POST_INIT, rv); } else { if (curr.desired_input_current != CHARGE_CURRENT_UNINITIALIZED) rv = charger_set_input_current( curr.desired_input_current); if (rv != EC_SUCCESS) problem(PR_SET_INPUT_CURR, rv); else prev_ac = curr.ac; } } else { /* Some things are only meaningful on AC */ chg_ctl_mode = CHARGE_CONTROL_NORMAL; battery_seems_to_be_dead = 0; prev_ac = curr.ac; } } charger_get_params(&curr.chg); battery_get_params(&curr.batt); if (prev_bp != curr.batt.is_present) { prev_bp = curr.batt.is_present; /* Update battery info due to change of battery */ batt_info = battery_get_info(); need_static = 1; curr.desired_input_current = get_desired_input_current(prev_bp, info); if (curr.desired_input_current != CHARGE_CURRENT_UNINITIALIZED) charger_set_input_current( curr.desired_input_current); hook_notify(HOOK_BATTERY_SOC_CHANGE); } /* * TODO(crosbug.com/p/27527). Sometimes the battery thinks its * temperature is 6280C, which seems a bit high. Let's ignore * anything above the boiling point of tungsten until this bug * is fixed. If the battery is really that warm, we probably * have more urgent problems. */ if (curr.batt.temperature > CELSIUS_TO_DECI_KELVIN(5660)) { CPRINTS("ignoring ridiculous batt.temp of %dC", DECI_KELVIN_TO_CELSIUS(curr.batt.temperature)); curr.batt.flags |= BATT_FLAG_BAD_TEMPERATURE; } /* If the battery thinks it's above 100%, don't believe it */ if (curr.batt.state_of_charge > 100) { CPRINTS("ignoring ridiculous batt.soc of %d%%", curr.batt.state_of_charge); curr.batt.flags |= BATT_FLAG_BAD_STATE_OF_CHARGE; } /* * Now decide what we want to do about it. We'll normally just * pass along whatever the battery wants to the charger. Note * that if battery_get_params() can't get valid values from the * battery it uses (0, 0), which is probably safer than blindly * applying power to a battery we can't talk to. */ curr.requested_voltage = curr.batt.desired_voltage; curr.requested_current = curr.batt.desired_current; /* If we *know* there's no battery, wait for one to appear. */ if (curr.batt.is_present == BP_NO) { ASSERT(curr.ac); /* How are we running? */ curr.state = ST_IDLE; curr.batt_is_charging = 0; battery_was_removed = 1; goto wait_for_it; } /* * If we had trouble talking to the battery or the charger, we * should probably do nothing for a bit, and if it doesn't get * better then flag it as an error. */ if (curr.chg.flags & CHG_FLAG_BAD_ANY) problem(PR_CHG_FLAGS, curr.chg.flags); if (curr.batt.flags & BATT_FLAG_BAD_ANY) problem(PR_BATT_FLAGS, curr.batt.flags); /* * If AC is present, check if input current is sufficient to * actually charge battery. */ curr.batt_is_charging = curr.ac && (curr.batt.current >= 0); /* Don't let the battery hurt itself. */ shutdown_on_critical_battery(); if (!curr.ac) { curr.state = ST_DISCHARGE; goto wait_for_it; } /* Okay, we're on AC and we should have a battery. */ /* Used for factory tests. */ if (chg_ctl_mode != CHARGE_CONTROL_NORMAL) { curr.state = ST_IDLE; goto wait_for_it; } /* If the battery is not responsive, try to wake it up. */ if (!(curr.batt.flags & BATT_FLAG_RESPONSIVE)) { if (battery_seems_to_be_dead || battery_is_cut_off()) { /* It's dead, do nothing */ curr.state = ST_IDLE; curr.requested_voltage = 0; curr.requested_current = 0; } else if (curr.state == ST_PRECHARGE && (get_time().val > precharge_start_time.val + PRECHARGE_TIMEOUT_US)) { /* We've tried long enough, give up */ CPRINTS("battery seems to be dead"); battery_seems_to_be_dead = 1; curr.state = ST_IDLE; curr.requested_voltage = 0; curr.requested_current = 0; } else { /* See if we can wake it up */ if (curr.state != ST_PRECHARGE) { CPRINTS("try to wake battery"); precharge_start_time = get_time(); need_static = 1; } curr.state = ST_PRECHARGE; curr.requested_voltage = batt_info->voltage_max; curr.requested_current = batt_info->precharge_current; } goto wait_for_it; } else { /* The battery is responding. Yay. Try to use it. */ #ifdef CONFIG_BATTERY_REQUESTS_NIL_WHEN_DEAD /* * TODO (crosbug.com/p/29467): remove this workaround * for dead battery that requests no voltage/current */ if (curr.requested_voltage == 0 && curr.requested_current == 0 && curr.batt.state_of_charge == 0) { /* Battery is dead, give precharge current */ curr.requested_voltage = batt_info->voltage_max; curr.requested_current = batt_info->precharge_current; } else #endif #ifdef CONFIG_BATTERY_REVIVE_DISCONNECT battery_seems_to_be_disconnected = 0; if (curr.requested_voltage == 0 && curr.requested_current == 0 && battery_get_disconnect_state() == BATTERY_DISCONNECTED) { /* * Battery is in disconnect state. Apply a * current to kick it out of this state. */ CPRINTS("found battery in disconnect state"); curr.requested_voltage = batt_info->voltage_max; curr.requested_current = batt_info->precharge_current; battery_seems_to_be_disconnected = 1; } else #endif if (curr.state == ST_PRECHARGE || battery_seems_to_be_dead || battery_was_removed) { CPRINTS("battery woke up"); /* Update the battery-specific values */ batt_info = battery_get_info(); need_static = 1; } battery_seems_to_be_dead = battery_was_removed = 0; curr.state = ST_CHARGE; } /* * TODO(crosbug.com/p/27643): Quit trying if charging too long * without getting full (CONFIG_CHARGER_TIMEOUT_HOURS). */ wait_for_it: #ifdef CONFIG_CHARGER_PROFILE_OVERRIDE sleep_usec = charger_profile_override(&curr); if (sleep_usec < 0) problem(PR_CUSTOM, sleep_usec); #endif /* Keep the AP informed */ if (need_static) need_static = update_static_battery_info(); /* Wait on the dynamic info until the static info is good. */ if (!need_static) update_dynamic_battery_info(); notify_host_of_low_battery(); /* And the EC console */ is_full = calc_is_full(); if ((!(curr.batt.flags & BATT_FLAG_BAD_STATE_OF_CHARGE) && curr.batt.state_of_charge != prev_charge) || (is_full != prev_full)) { show_charging_progress(); prev_charge = curr.batt.state_of_charge; hook_notify(HOOK_BATTERY_SOC_CHANGE); } prev_full = is_full; /* Turn charger off if it's not needed */ if (curr.state == ST_IDLE || curr.state == ST_DISCHARGE) { curr.requested_voltage = 0; curr.requested_current = 0; } /* Apply external limits */ if (curr.requested_current > user_current_limit) curr.requested_current = user_current_limit; /* Round to valid values */ curr.requested_voltage = charger_closest_voltage(curr.requested_voltage); curr.requested_current = charger_closest_current(curr.requested_current); /* Charger only accpets request when AC is on. */ if (curr.ac) { /* * Some batteries would wake up after cut-off if we keep * charging it. Thus, we only charge when AC is on and * battery is not cut off yet. */ if (battery_is_cut_off()) charge_request(0, 0); /* * As a safety feature, some chargers will stop * charging if we don't communicate with it frequently * enough. In manual mode, we'll just tell it what it * knows. */ else if (manual_mode) { charge_request(curr.chg.voltage, curr.chg.current); } else { charge_request(curr.requested_voltage, curr.requested_current); } } else { charge_request( charger_closest_voltage( curr.batt.voltage + info->voltage_step), -1); } /* How long to sleep? */ if (problems_exist) /* If there are errors, don't wait very long. */ sleep_usec = CHARGE_POLL_PERIOD_SHORT; else if (sleep_usec <= 0) { /* default values depend on the state */ if (curr.state == ST_IDLE || curr.state == ST_DISCHARGE) { /* If AP is off, we can sleep a long time */ if (chipset_in_state(CHIPSET_STATE_ANY_OFF | CHIPSET_STATE_SUSPEND)) sleep_usec = CHARGE_POLL_PERIOD_VERY_LONG; else /* Discharging, not too urgent */ sleep_usec = CHARGE_POLL_PERIOD_LONG; } else { /* Charging, so pay closer attention */ sleep_usec = CHARGE_POLL_PERIOD_CHARGE; } } /* Adjust for time spent in this loop */ sleep_usec -= (int)(get_time().val - curr.ts.val); if (sleep_usec < CHARGE_MIN_SLEEP_USEC) sleep_usec = CHARGE_MIN_SLEEP_USEC; else if (sleep_usec > CHARGE_MAX_SLEEP_USEC) sleep_usec = CHARGE_MAX_SLEEP_USEC; task_wait_event(sleep_usec); } }
void charger_task(void) { int next_state; int wait_time = T1_USEC; timestamp_t pre_chg_start = get_time(); pmu_init(); /* Enable low current charging */ pmu_low_current_charging(1); /* Enable charger interrupt */ gpio_enable_interrupt(GPIO_CHARGER_INT_L); /* * EC STOP mode support * The charging loop can be stopped in idle state with AC unplugged. * Charging loop will be resumed by TPSCHROME interrupt. */ enable_charging(0); disable_sleep(SLEEP_MASK_CHARGING); while (1) { last_waken = get_time(); pmu_clear_irq(); #ifdef CONFIG_PMU_TPS65090_CHARGING_LED update_battery_led(); #endif /* * When battery is extremely low, the internal voltage can not * power on its gas guage IC. Charging loop will enable the * charger and turn on trickle charging. For safty reason, * charger should be disabled if the communication to battery * failed. */ if (current_state == ST_PRE_CHARGING && get_time().val - pre_chg_start.val >= PRE_CHARGING_TIMEOUT) next_state = ST_CHARGING_ERROR; else next_state = calc_next_state(current_state); if (next_state != current_state) { /* Reset state of charge moving average window */ rsoc_moving_average(-1); CPRINTS("batt state %s -> %s", state_list[current_state], state_list[next_state]); current_state = next_state; switch (current_state) { case ST_PRE_CHARGING: pre_chg_start = get_time(); /* Fall through */ case ST_CHARGING: if (pmu_blink_led(0)) next_state = ST_CHARGING_ERROR; else enable_charging(1); break; case ST_CHARGING_ERROR: /* * Enable hardware charging circuit after set * PMU to hardware error state. */ if (pmu_blink_led(1)) enable_charging(0); else enable_charging(1); break; case ST_IDLE: case ST_IDLE0: case ST_BAD_COND: case ST_DISCHARGING: enable_charging(0); /* Ignore charger error when discharging */ pmu_blink_led(0); break; } } switch (current_state) { case ST_CHARGING: case ST_CHARGING_ERROR: wait_time = T2_USEC; break; case ST_DISCHARGING: wait_time = T3_USEC; break; case ST_PRE_CHARGING: wait_time = T1_USEC; if (get_time().val - pre_chg_start.val >= PRE_CHARGING_TIMEOUT) enable_charging(0); break; default: if (extpower_is_present()) { wait_time = T1_USEC; break; } else if (chipset_in_state(CHIPSET_STATE_ANY_OFF)) { wait_time = T1_OFF_USEC; enable_sleep(SLEEP_MASK_CHARGING); } else if (chipset_in_state(CHIPSET_STATE_SUSPEND)) { wait_time = T1_SUSPEND_USEC; } else { wait_time = T1_USEC; } } if (!has_pending_event) { task_wait_event(wait_time); disable_sleep(SLEEP_MASK_CHARGING); } else { has_pending_event = 0; } } }
static int calc_next_state(int state) { struct batt_params batt; int alarm; battery_get_params_and_save_a_copy(&batt); switch (state) { case ST_IDLE0: case ST_BAD_COND: case ST_IDLE: /* Check AC and chiset state */ if (!extpower_is_present()) { if (chipset_in_state(CHIPSET_STATE_ON)) return ST_DISCHARGING; return ST_IDLE; } /* Stay in idle mode if charger overtemp */ if (pmu_is_charger_alarm()) return ST_BAD_COND; /* Enable charging when battery doesn't respond */ if (!(batt.flags & BATT_FLAG_RESPONSIVE)) return ST_PRE_CHARGING; /* Turn off charger when battery temperature is out * of the start charging range. */ if (!battery_start_charging_range(batt.temperature)) return ST_BAD_COND; /* Turn off charger on battery over temperature alarm */ if (battery_status(&alarm) || (alarm & ALARM_OVER_TEMP)) return ST_BAD_COND; /* Stop charging if the battery says it's charged */ if (alarm & ALARM_CHARGED) return ST_IDLE; /* Start charging only when battery charge lower than 100% */ if (!(batt.flags & BATT_FLAG_BAD_STATE_OF_CHARGE)) { if (batt.state_of_charge < 100) return ST_CHARGING; } return ST_IDLE; case ST_PRE_CHARGING: if (!extpower_is_present()) return ST_IDLE0; /* * If the battery goes online after enabling the charger, go * into charging state. */ if (batt.flags & BATT_FLAG_RESPONSIVE) { if (!battery_start_charging_range(batt.temperature)) return ST_IDLE0; if (!(batt.flags & BATT_FLAG_BAD_STATE_OF_CHARGE)) { if (batt.state_of_charge >= 100) return ST_IDLE0; } return ST_CHARGING; } return ST_PRE_CHARGING; case ST_CHARGING: /* Go back to idle state when AC is unplugged */ if (!extpower_is_present()) return ST_IDLE0; /* * Disable charging on battery access error, or charging * temperature out of range. */ if (!(batt.flags & BATT_FLAG_RESPONSIVE)) { CPRINTS("pmu charging: unable to get battery " "temperature"); return ST_IDLE0; } else if (!battery_charging_range(batt.temperature)) { CPRINTS("pmu charging: temperature out of range " "%dC", DECI_KELVIN_TO_CELSIUS(batt.temperature)); return ST_CHARGING_ERROR; } /* * Disable charging on battery alarm events or access error: * - over temperature * - over current */ if (battery_status(&alarm)) return ST_IDLE0; if (alarm & ALARM_OVER_TEMP) { CPRINTS("pmu charging: battery over temp"); return ST_CHARGING_ERROR; } /* Go to idle state if battery is fully charged */ if (alarm & ALARM_CHARGED) return ST_IDLE; /* * Disable charging on charger alarm events: * - charger over current * - charger over temperature */ if (pmu_is_charger_alarm()) { CPRINTS("pmu charging: charger alarm"); return ST_IDLE0; } return ST_CHARGING; case ST_CHARGING_ERROR: /* * This state indicates AC is plugged but the battery is not * charging. The conditions to exit this state: * - battery detected * - battery temperature is in start charging range * - no battery alarm */ if (extpower_is_present()) { if (battery_status(&alarm)) return ST_CHARGING_ERROR; if (alarm & ALARM_OVER_TEMP) return ST_CHARGING_ERROR; if (!(batt.flags & BATT_FLAG_RESPONSIVE)) return ST_CHARGING_ERROR; if (!battery_charging_range(batt.temperature)) return ST_CHARGING_ERROR; return ST_CHARGING; } return ST_IDLE0; case ST_DISCHARGING: /* Go back to idle state when AC is plugged */ if (extpower_is_present()) return ST_IDLE0; /* Prepare EC sleep after system stopped discharging */ if (chipset_in_state(CHIPSET_STATE_ANY_OFF)) return ST_IDLE0; /* Check battery discharging temperature range */ if (batt.flags & BATT_FLAG_RESPONSIVE) { if (!battery_discharging_range(batt.temperature)) { CPRINTS("pmu discharging: temperature out of " "range %dC", DECI_KELVIN_TO_CELSIUS( batt.temperature)); return system_off(); } } /* Check discharging alarm */ if (!battery_status(&alarm) && (alarm & ALARM_DISCHARGING)) { CPRINTS("pmu discharging: battery alarm %016b", alarm); return system_off(); } /* Check remaining charge % */ if (!(batt.flags & BATT_FLAG_BAD_STATE_OF_CHARGE)) { /* * Shutdown AP when state of charge < 1.5%. * Moving average is rounded to integer. */ if (rsoc_moving_average(batt.state_of_charge) < 2) return system_off(); else if (batt.state_of_charge < 4) notify_battery_low(); } return ST_DISCHARGING; } return ST_IDLE0; }
/** * 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; }
/** * Buffer the AC present GPIO to the PCH. */ static void board_extpower(void) { gpio_set_level(GPIO_PCH_ACOK, extpower_is_present()); }
/** * 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; }