/** * Handle the hang detect timer expiring. */ static void hang_detect_deferred(void) { /* If we're no longer active, nothing to do */ if (!active) return; /* If we're rebooting the AP, stop hang detection */ if (timeout_will_reboot) { CPRINTS("hang detect triggering warm reboot"); host_set_single_event(EC_HOST_EVENT_HANG_REBOOT); chipset_reset(0); active = 0; return; } /* Otherwise, we're starting with the host event */ CPRINTS("hang detect sending host event"); host_set_single_event(EC_HOST_EVENT_HANG_DETECT); /* If we're also rebooting, defer for the remaining delay */ if (hdparams.warm_reboot_timeout_msec) { CPRINTS("hang detect continuing (for reboot)"); timeout_will_reboot = 1; hook_call_deferred(hang_detect_deferred, (hdparams.warm_reboot_timeout_msec - hdparams.host_event_timeout_msec) * MSEC); } else { /* Not rebooting, so go back to idle */ active = 0; } }
/** * Politely ask the CPU to enable/disable its own throttling. * * @param throttle Enable (!=0) or disable(0) throttling */ test_mockable void host_throttle_cpu(int throttle) { if (throttle) host_set_single_event(EC_HOST_EVENT_THROTTLE_START); else host_set_single_event(EC_HOST_EVENT_THROTTLE_STOP); }
/* * Send host events as the battery charge drops below certain thresholds. * We handle forced shutdown and other actions elsewhere; this is just for the * host events. We send these even if the AP is off, since the AP will read and * discard any events it doesn't care about the next time it wakes up. */ static void notify_host_of_low_battery(void) { /* We can't tell what the current charge is. Assume it's okay. */ if (curr.batt.flags & BATT_FLAG_BAD_STATE_OF_CHARGE) return; if (curr.batt.state_of_charge <= BATTERY_LEVEL_LOW && prev_charge > BATTERY_LEVEL_LOW) host_set_single_event(EC_HOST_EVENT_BATTERY_LOW); if (curr.batt.state_of_charge <= BATTERY_LEVEL_CRITICAL && prev_charge > BATTERY_LEVEL_CRITICAL) host_set_single_event(EC_HOST_EVENT_BATTERY_CRITICAL); }
/** * Handle debounced power button changing state. */ static void power_button_change_deferred(void) { const int new_pressed = raw_power_button_pressed(); /* Re-enable keyboard scanning if power button is no longer pressed */ if (!new_pressed) keyboard_scan_enable(1, KB_SCAN_DISABLE_POWER_BUTTON); /* If power button hasn't changed state, nothing to do */ if (new_pressed == debounced_power_pressed) { power_button_is_stable = 1; return; } debounced_power_pressed = new_pressed; power_button_is_stable = 1; CPRINTS("power button %s", new_pressed ? "pressed" : "released"); /* Call hooks */ hook_notify(HOOK_POWER_BUTTON_CHANGE); /* Notify host if power button has been pressed */ if (new_pressed) host_set_single_event(EC_HOST_EVENT_POWER_BUTTON); }
/** * Init state handler * * - check ac, charger, battery and temperature * - initialize charger * - new states: DISCHARGE, IDLE */ static enum charge_state state_init(struct charge_state_context *ctx) { /* Stop charger, unconditionally */ charge_request(0, 0); /* if battery was not detected initially, get battery info again */ if (ctx->battery == NULL) ctx->battery = battery_get_info(); /* Update static battery info */ update_battery_info(); /* Clear shutdown timer */ ctx->shutdown_warning_time.val = 0; /* If AC is not present, switch to discharging state */ if (!ctx->curr.ac) return PWR_STATE_DISCHARGE; /* Check general error conditions */ if (ctx->curr.error) return PWR_STATE_ERROR; /* Send battery event to host */ host_set_single_event(EC_HOST_EVENT_BATTERY); return PWR_STATE_IDLE0; }
/** * Deferred function to handle external power change */ static void extpower_deferred(void) { int extpower_presence = gpio_get_level(GPIO_AC_PRESENT); if (extpower_presence == debounced_extpower_presence) return; debounced_extpower_presence = extpower_presence; hook_notify(HOOK_AC_CHANGE); /* Forward notification to host */ if (extpower_presence) host_set_single_event(EC_HOST_EVENT_AC_CONNECTED); else host_set_single_event(EC_HOST_EVENT_AC_DISCONNECTED); }
/* Send host event up to AP */ void pd_send_host_event(int mask) { /* mask must be set */ if (!mask) return; atomic_or(&(host_event_status.status), mask); /* interrupt the AP */ host_set_single_event(EC_HOST_EVENT_PD_MCU); }
static void host_command_init(void) { /* Initialize memory map ID area */ host_get_memmap(EC_MEMMAP_ID)[0] = 'E'; host_get_memmap(EC_MEMMAP_ID)[1] = 'C'; *host_get_memmap(EC_MEMMAP_ID_VERSION) = 1; *host_get_memmap(EC_MEMMAP_EVENTS_VERSION) = 1; host_set_single_event(EC_HOST_EVENT_INTERFACE_READY); CPRINTS("hostcmd init 0x%x", host_get_events()); }
/* * 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(); } } }
static void panic_init(void) { #ifdef CONFIG_HOSTCMD_EVENTS struct panic_data *addr = panic_get_data(); /* Notify host of new panic event */ if (addr && !(addr->flags & PANIC_DATA_FLAG_OLD_HOSTEVENT)) { host_set_single_event(EC_HOST_EVENT_PANIC); addr->flags |= PANIC_DATA_FLAG_OLD_HOSTEVENT; } #endif }
static void board_startup_key_combo(void) { int vold = !gpio_get_level(GPIO_BTN_VOLD_L); int volu = !gpio_get_level(GPIO_BTN_VOLU_L); int pwr = power_button_signal_asserted(); /* * Determine recovery mode is requested by the power and * voldown buttons being pressed (while device was off). */ if (pwr && vold && !volu) { host_set_single_event(EC_HOST_EVENT_KEYBOARD_RECOVERY); CPRINTS("> RECOVERY mode"); } /* * Determine fastboot mode is requested by the power and * voldown buttons being pressed (while device was off). */ if (pwr && volu && !vold) { host_set_single_event(EC_HOST_EVENT_KEYBOARD_FASTBOOT); CPRINTS("> FASTBOOT mode"); } }
/** * 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(); } }
/** * Discharge state handler * * - detect ac status * - new state: INIT */ static enum charge_state state_discharge(struct charge_state_context *ctx) { struct batt_params *batt = &ctx->curr.batt; int8_t bat_temp_c = DECI_KELVIN_TO_CELSIUS(batt->temperature); if (ctx->curr.ac) return PWR_STATE_REINIT; if (ctx->curr.error) return PWR_STATE_ERROR; /* Handle overtemp in discharging state by powering off host */ if ((bat_temp_c >= ctx->battery->discharging_max_c || bat_temp_c < ctx->battery->discharging_min_c) && chipset_in_state(CHIPSET_STATE_ON)) { CPRINTS("charge force shutdown due to battery temp"); chipset_force_shutdown(); host_set_single_event(EC_HOST_EVENT_BATTERY_SHUTDOWN); } return PWR_STATE_UNCHANGE; }
static void thermal_control(void) { int i, j, t, rv, f; int count_over[EC_TEMP_THRESH_COUNT]; int count_under[EC_TEMP_THRESH_COUNT]; int num_valid_limits[EC_TEMP_THRESH_COUNT]; int num_sensors_read; int fmax; int dptf_tripped; int temp_fan_configured; /* Get ready to count things */ memset(count_over, 0, sizeof(count_over)); memset(count_under, 0, sizeof(count_under)); memset(num_valid_limits, 0, sizeof(num_valid_limits)); num_sensors_read = 0; fmax = 0; dptf_tripped = 0; temp_fan_configured = 0; /* go through all the sensors */ for (i = 0; i < TEMP_SENSOR_COUNT; ++i) { /* read one */ rv = temp_sensor_read(i, &t); if (rv != EC_SUCCESS) continue; else num_sensors_read++; /* check all the limits */ for (j = 0; j < EC_TEMP_THRESH_COUNT; j++) { int limit = thermal_params[i].temp_host[j]; if (limit) { num_valid_limits[j]++; if (t > limit) count_over[j]++; else if (t < limit) count_under[j]++; } } /* figure out the max fan needed, too */ if (thermal_params[i].temp_fan_off && thermal_params[i].temp_fan_max) { f = thermal_fan_percent(thermal_params[i].temp_fan_off, thermal_params[i].temp_fan_max, t); if (f > fmax) fmax = f; temp_fan_configured = 1; } /* and check the dptf thresholds */ dptf_tripped |= dpft_check_temp_threshold(i, t); } if (!num_sensors_read) { /* * Trigger a SMI event if we can't read any sensors. * * In theory we could do something more elaborate like forcing * the system to shut down if no sensors are available after * several retries. This is a very unlikely scenario - * particularly on LM4-based boards, since the LM4 has its own * internal temp sensor. It's most likely to occur during * bringup of a new board, where we haven't debugged the I2C * bus to the sensors; forcing a shutdown in that case would * merely hamper board bringup. */ smi_sensor_failure_warning(); return; } /* See what the aggregated limits are. Any temp over the limit * means it's hot, but all temps have to be under the limit to * be cool again. */ for (j = 0; j < EC_TEMP_THRESH_COUNT; j++) { if (count_over[j]) cond_set_true(&cond_hot[j]); else if (count_under[j] == num_valid_limits[j]) cond_set_false(&cond_hot[j]); } /* What do we do about it? (note hard-coded logic). */ if (cond_went_true(&cond_hot[EC_TEMP_THRESH_HALT])) { CPRINTS("thermal SHUTDOWN"); chipset_force_shutdown(); } 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 * detect this sensor transition until then, but we * do have to check in order to clear the cond_t. */ CPRINTS("thermal no longer shutdown"); } if (cond_went_true(&cond_hot[EC_TEMP_THRESH_HIGH])) { CPRINTS("thermal HIGH"); throttle_ap(THROTTLE_ON, THROTTLE_HARD, THROTTLE_SRC_THERMAL); } else if (cond_went_false(&cond_hot[EC_TEMP_THRESH_HIGH])) { CPRINTS("thermal no longer high"); throttle_ap(THROTTLE_OFF, THROTTLE_HARD, THROTTLE_SRC_THERMAL); } if (cond_went_true(&cond_hot[EC_TEMP_THRESH_WARN])) { CPRINTS("thermal WARN"); throttle_ap(THROTTLE_ON, THROTTLE_SOFT, THROTTLE_SRC_THERMAL); } else if (cond_went_false(&cond_hot[EC_TEMP_THRESH_WARN])) { CPRINTS("thermal no longer warn"); throttle_ap(THROTTLE_OFF, THROTTLE_SOFT, THROTTLE_SRC_THERMAL); } if (temp_fan_configured) { #ifdef CONFIG_FANS /* TODO(crosbug.com/p/23797): For now, we just treat all fans the * same. It would be better if we could assign different thermal * profiles to each fan - in case one fan cools the CPU while another * cools the radios or battery. */ for (i = 0; i < CONFIG_FANS; i++) fan_set_percent_needed(i, fmax); #endif } /* Don't forget to signal any DPTF thresholds */ if (dptf_tripped) host_set_single_event(EC_HOST_EVENT_THERMAL_THRESHOLD); }
test_mockable_static void smi_sensor_failure_warning(void) { CPRINTS("can't read any temp sensors!"); host_set_single_event(EC_HOST_EVENT_THERMAL); }
/** * Common handler for charging states. * * This handler gets battery charging parameters, charger state, ac state, and * timestamp. It also fills memory map and issues power events on state change. */ static int state_common(struct charge_state_context *ctx) { int rv, d; struct charge_state_data *curr = &ctx->curr; struct charge_state_data *prev = &ctx->prev; struct batt_params *batt = &ctx->curr.batt; uint8_t *batt_flags = ctx->memmap_batt_flags; /* Copy previous state and init new state */ ctx->prev = ctx->curr; curr->ts = get_time(); curr->error = 0; /* Detect AC change */ curr->ac = charge_get_flags() & CHARGE_FLAG_EXTERNAL_POWER; if (curr->ac != prev->ac) { if (curr->ac) { /* AC on * Initialize charger to power on reset mode */ rv = charger_post_init(); if (rv) curr->error |= F_CHARGER_INIT; } } if (curr->ac) { *batt_flags |= EC_BATT_FLAG_AC_PRESENT; if (charger_get_voltage(&curr->charging_voltage)) { charge_request(0, 0); curr->error |= F_CHARGER_VOLTAGE; } if (charger_get_current(&curr->charging_current)) { charge_request(0, 0); curr->error |= F_CHARGER_CURRENT; } #ifdef CONFIG_CHARGER_EN_GPIO if (!charge_get_charger_en_gpio()) { curr->charging_voltage = 0; curr->charging_current = 0; } #endif } else { *batt_flags &= ~EC_BATT_FLAG_AC_PRESENT; /* AC disconnected should get us out of force idle mode. */ state_machine_force_idle = 0; } #if defined(CONFIG_BATTERY_PRESENT_CUSTOM) || \ defined(CONFIG_BATTERY_PRESENT_GPIO) if (battery_is_present() == BP_NO) { curr->error |= F_BATTERY_NOT_CONNECTED; return curr->error; } #endif /* Read params and see if battery is responsive */ battery_get_params(batt); if (!(batt->flags & BATT_FLAG_RESPONSIVE)) { /* Check low battery condition and retry */ if (curr->ac && ctx->battery_responsive && !(curr->error & F_CHARGER_MASK)) { ctx->battery_responsive = 0; /* * Try to revive ultra low voltage pack. Charge * battery pack with minimum current and maximum * voltage for 30 seconds. */ charge_request(ctx->battery->voltage_max, ctx->battery->precharge_current); for (d = 0; d < PRECHARGE_TIMEOUT; d++) { sleep(1); battery_get_params(batt); if (batt->flags & BATT_FLAG_RESPONSIVE) { ctx->battery_responsive = 1; break; } } } /* Set error if battery is still unresponsive */ if (!(batt->flags & BATT_FLAG_RESPONSIVE)) { curr->error |= F_BATTERY_UNRESPONSIVE; return curr->error; } } else { ctx->battery_responsive = 1; } /* Translate flags */ if (batt->flags & BATT_FLAG_BAD_ANY) curr->error |= F_BATTERY_GET_PARAMS; if (batt->flags & BATT_FLAG_BAD_VOLTAGE) curr->error |= F_BATTERY_VOLTAGE; if (batt->flags & BATT_FLAG_BAD_STATE_OF_CHARGE) curr->error |= F_BATTERY_STATE_OF_CHARGE; *ctx->memmap_batt_volt = batt->voltage; /* Memory mapped value: discharge rate */ *ctx->memmap_batt_rate = batt->current < 0 ? -batt->current : batt->current; /* Fake state of charge if necessary */ if (fake_state_of_charge >= 0) { batt->state_of_charge = fake_state_of_charge; curr->error &= ~F_BATTERY_STATE_OF_CHARGE; } if (batt->state_of_charge != prev->batt.state_of_charge) { rv = battery_full_charge_capacity(&d); if (!rv && d != *(int *)host_get_memmap(EC_MEMMAP_BATT_LFCC)) { *(int *)host_get_memmap(EC_MEMMAP_BATT_LFCC) = d; /* Notify host to re-read battery information */ host_set_single_event(EC_HOST_EVENT_BATTERY); } } /* Prevent deep discharging */ if (!curr->ac) { if ((batt->state_of_charge < BATTERY_LEVEL_SHUTDOWN && !(curr->error & F_BATTERY_STATE_OF_CHARGE)) || (batt->voltage <= ctx->battery->voltage_min && !(curr->error & F_BATTERY_VOLTAGE))) low_battery_shutdown(ctx); } /* Check battery presence */ if (curr->error & F_BATTERY_MASK) { *ctx->memmap_batt_flags &= ~EC_BATT_FLAG_BATT_PRESENT; return curr->error; } *ctx->memmap_batt_flags |= EC_BATT_FLAG_BATT_PRESENT; /* Battery charge level low */ if (batt->state_of_charge <= BATTERY_LEVEL_LOW && prev->batt.state_of_charge > BATTERY_LEVEL_LOW) host_set_single_event(EC_HOST_EVENT_BATTERY_LOW); /* Battery charge level critical */ if (batt->state_of_charge <= BATTERY_LEVEL_CRITICAL) { *ctx->memmap_batt_flags |= EC_BATT_FLAG_LEVEL_CRITICAL; /* Send battery critical host event */ if (prev->batt.state_of_charge > BATTERY_LEVEL_CRITICAL) host_set_single_event(EC_HOST_EVENT_BATTERY_CRITICAL); } else { *ctx->memmap_batt_flags &= ~EC_BATT_FLAG_LEVEL_CRITICAL; } #ifdef CONFIG_BATTERY_OVERRIDE_PARAMS /* Apply battery pack vendor charging method */ battery_override_params(batt); #endif #ifdef CONFIG_CHARGER_CURRENT_LIMIT if (batt->desired_current > CONFIG_CHARGER_CURRENT_LIMIT) batt->desired_current = CONFIG_CHARGER_CURRENT_LIMIT; #endif if (batt->desired_current > user_current_limit) batt->desired_current = user_current_limit; if (fake_state_of_charge >= 0) *ctx->memmap_batt_cap = fake_state_of_charge * *(int *)host_get_memmap(EC_MEMMAP_BATT_LFCC) / 100; else if (battery_remaining_capacity(&d)) ctx->curr.error |= F_BATTERY_CAPACITY; else *ctx->memmap_batt_cap = d; return ctx->curr.error; }
static void pd_exchange_status(void) { struct ec_params_pd_status ec_status; struct ec_response_pd_status pd_status; int rv = 0; #ifdef CONFIG_HOSTCMD_PD_PANIC static int pd_in_rw; #endif /* Send PD charge state and battery state of charge */ ec_status.charge_state = charge_state; if (charge_get_flags() & CHARGE_FLAG_BATT_RESPONSIVE) ec_status.batt_soc = charge_get_percent(); else ec_status.batt_soc = -1; rv = pd_host_command(EC_CMD_PD_EXCHANGE_STATUS, 1, &ec_status, sizeof(struct ec_params_pd_status), &pd_status, sizeof(struct ec_response_pd_status)); /* If PD doesn't support new command version, try old version */ if (rv == -EC_RES_INVALID_VERSION) rv = pd_host_command(EC_CMD_PD_EXCHANGE_STATUS, 0, &ec_status, sizeof(struct ec_params_pd_status), &pd_status, sizeof(struct ec_response_pd_status)); if (rv < 0) { CPRINTS("Host command to PD MCU failed"); return; } #ifdef CONFIG_HOSTCMD_PD_PANIC /* * Check if PD MCU is in RW. If PD MCU was in RW and is now in RO * AND it did not sysjump to RO, then it must have crashed, and * therefore we should panic as well. */ if (pd_status.status & PD_STATUS_IN_RW) { pd_in_rw = 1; } else if (pd_in_rw && !(pd_status.status & PD_STATUS_JUMPED_TO_IMAGE)) { panic_printf("PD crash"); software_panic(PANIC_SW_PD_CRASH, 0); } #endif #ifdef HAS_TASK_LIGHTBAR /* * If charge port has changed, and it was initialized, then show * battery status on lightbar. */ if (pd_status.active_charge_port != charge_port) { if (charge_port != CHARGE_PORT_UNINITIALIZED) { charge_port = pd_status.active_charge_port; lightbar_sequence(LIGHTBAR_TAP); } else { charge_port = pd_status.active_charge_port; } } #else /* Store the active charge port */ charge_port = pd_status.active_charge_port; #endif /* Set input current limit */ rv = charge_set_input_current_limit(MAX(pd_status.curr_lim_ma, CONFIG_CHARGER_INPUT_CURRENT)); if (rv < 0) CPRINTS("Failed to set input current limit from PD MCU"); /* If PD is signalling host event, then pass it up to AP */ if (pd_status.status & PD_STATUS_HOST_EVENT) host_set_single_event(EC_HOST_EVENT_PD_MCU); }
static void update_dynamic_battery_info(void) { /* The memmap address is constant. We should fix these calls somehow. */ int *memmap_volt = (int *)host_get_memmap(EC_MEMMAP_BATT_VOLT); int *memmap_rate = (int *)host_get_memmap(EC_MEMMAP_BATT_RATE); int *memmap_cap = (int *)host_get_memmap(EC_MEMMAP_BATT_CAP); int *memmap_lfcc = (int *)host_get_memmap(EC_MEMMAP_BATT_LFCC); uint8_t *memmap_flags = host_get_memmap(EC_MEMMAP_BATT_FLAG); uint8_t tmp; int send_batt_status_event = 0; int send_batt_info_event = 0; static int __bss_slow batt_present; tmp = 0; if (curr.ac) tmp |= EC_BATT_FLAG_AC_PRESENT; if (curr.batt.is_present == BP_YES) { tmp |= EC_BATT_FLAG_BATT_PRESENT; batt_present = 1; /* Tell the AP to read battery info if it is newly present. */ if (!(*memmap_flags & EC_BATT_FLAG_BATT_PRESENT)) send_batt_info_event++; } else { /* * Require two consecutive updates with BP_NOT_SURE * before reporting it gone to the host. */ if (batt_present) tmp |= EC_BATT_FLAG_BATT_PRESENT; else if (*memmap_flags & EC_BATT_FLAG_BATT_PRESENT) send_batt_info_event++; batt_present = 0; } if (!(curr.batt.flags & BATT_FLAG_BAD_VOLTAGE)) *memmap_volt = curr.batt.voltage; if (!(curr.batt.flags & BATT_FLAG_BAD_CURRENT)) *memmap_rate = ABS(curr.batt.current); if (!(curr.batt.flags & BATT_FLAG_BAD_REMAINING_CAPACITY)) { /* * If we're running off the battery, it must have some charge. * Don't report zero charge, as that has special meaning * to Chrome OS powerd. */ if (curr.batt.remaining_capacity == 0 && !curr.batt_is_charging) *memmap_cap = 1; else *memmap_cap = curr.batt.remaining_capacity; } if (!(curr.batt.flags & BATT_FLAG_BAD_FULL_CAPACITY) && (curr.batt.full_capacity <= (*memmap_lfcc - LFCC_EVENT_THRESH) || curr.batt.full_capacity >= (*memmap_lfcc + LFCC_EVENT_THRESH))) { *memmap_lfcc = curr.batt.full_capacity; /* Poke the AP if the full_capacity changes. */ send_batt_info_event++; } if (curr.batt.is_present == BP_YES && !(curr.batt.flags & BATT_FLAG_BAD_STATE_OF_CHARGE) && curr.batt.state_of_charge <= BATTERY_LEVEL_CRITICAL) tmp |= EC_BATT_FLAG_LEVEL_CRITICAL; tmp |= curr.batt_is_charging ? EC_BATT_FLAG_CHARGING : EC_BATT_FLAG_DISCHARGING; /* Tell the AP to re-read battery status if charge state changes */ if (*memmap_flags != tmp) send_batt_status_event++; /* Update flags before sending host events. */ *memmap_flags = tmp; if (send_batt_info_event) host_set_single_event(EC_HOST_EVENT_BATTERY); if (send_batt_status_event) host_set_single_event(EC_HOST_EVENT_BATTERY_STATUS); }