void chg_ramp_task(void) { int task_wait_time = -1; int i, lim; uint64_t detect_end_time_us = 0, time_us; int last_active_port = CHARGE_PORT_NONE; /* * Static initializer so that we don't clobber early calls to this * module. */ static enum chg_ramp_state ramp_st_prev = CHG_RAMP_DISCONNECTED, ramp_st_new = CHG_RAMP_DISCONNECTED; int active_icl_new; /* Clear last OCP supplier to guarantee we ramp on first connect */ for (i = 0; i < CONFIG_USB_PD_PORT_COUNT; i++) oc_info[i][0].sup = CHARGE_SUPPLIER_NONE; while (1) { ramp_st_new = ramp_st; active_icl_new = active_icl; switch (ramp_st) { case CHG_RAMP_DISCONNECTED: /* Do nothing */ task_wait_time = -1; break; case CHG_RAMP_CHARGE_DETECT_DELAY: /* Delay for charge_manager to determine supplier */ /* * On entry to state, or if port changes, store the * OC recovery time, and calculate the detect end * time to exit this state. */ if (ramp_st_prev != ramp_st || active_port != last_active_port) { last_active_port = active_port; ACTIVE_OC_INFO.recover = reg_time.val - ACTIVE_OC_INFO.ts.val; detect_end_time_us = get_time().val + CHARGE_DETECT_DELAY; task_wait_time = CHARGE_DETECT_DELAY; break; } /* If detect delay has not passed, set wait time */ time_us = get_time().val; if (time_us < detect_end_time_us) { task_wait_time = detect_end_time_us - time_us; break; } /* Detect delay is over, fall through to next state */ ramp_st_new = CHG_RAMP_OVERCURRENT_DETECT; /* notify host of power info change */ pd_send_host_event(PD_EVENT_POWER_CHANGE); case CHG_RAMP_OVERCURRENT_DETECT: /* Check if we should ramp or go straight to stable */ task_wait_time = SECOND; /* Skip ramp for specific suppliers */ if (!board_is_ramp_allowed(active_sup)) { active_icl_new = min_icl; ramp_st_new = CHG_RAMP_STABLE; break; } /* * If we are not drawing full charge, then don't ramp, * just wait in this state, until we are. */ if (!board_is_consuming_full_charge()) { task_wait_time = CURRENT_DRAW_DELAY; break; } /* * Compare recent OCP events, if all info matches, * then we don't need to ramp anymore. */ for (i = 0; i < RAMP_COUNT; i++) { if (oc_info[active_port][i].sup != active_sup || oc_info[active_port][i].recover > OC_RECOVER_MAX_TIME) break; } if (i == RAMP_COUNT) { /* Found OC threshold! */ active_icl_new = ACTIVE_OC_INFO.icl - RAMP_ICL_BACKOFF; ramp_st_new = CHG_RAMP_STABLE; } else { /* * Need to ramp to find OC threshold, start * at the minimum input current limit. */ active_icl_new = min_icl; ramp_st_new = CHG_RAMP_RAMP; } break; case CHG_RAMP_RAMP: /* Keep ramping until we find the limit */ task_wait_time = RAMP_CURR_DELAY; /* Pause ramping if we are not drawing full current */ if (!board_is_consuming_full_charge()) { task_wait_time = CURRENT_DRAW_DELAY; break; } /* If VBUS is sagging a lot, then stop ramping */ if (board_is_vbus_too_low(CHG_RAMP_VBUS_RAMPING)) { CPRINTS("VBUS low"); active_icl_new = MAX(min_icl, active_icl - RAMP_ICL_BACKOFF); ramp_st_new = CHG_RAMP_STABILIZE; task_wait_time = STABLIZE_DELAY; stablize_port = active_port; stablize_sup = active_sup; break; } /* Ramp the current limit if we haven't reached max */ if (active_icl == max_icl) ramp_st_new = CHG_RAMP_STABLE; else if (active_icl + RAMP_CURR_INCR_MA > max_icl) active_icl_new = max_icl; else active_icl_new = active_icl + RAMP_CURR_INCR_MA; break; case CHG_RAMP_STABILIZE: /* Wait for current to stabilize after ramp is done */ /* Use default delay for exiting this state */ task_wait_time = SECOND; if (active_port == stablize_port && active_sup == stablize_sup) { ramp_st_new = CHG_RAMP_STABLE; break; } ramp_st_new = active_port == CHARGE_PORT_NONE ? CHG_RAMP_DISCONNECTED : CHG_RAMP_CHARGE_DETECT_DELAY; break; case CHG_RAMP_STABLE: /* Maintain input current limit */ /* On entry log charging stats */ if (ramp_st_prev != ramp_st) { #ifdef CONFIG_USB_PD_LOGGING charge_manager_save_log(active_port); #endif /* notify host of power info change */ pd_send_host_event(PD_EVENT_POWER_CHANGE); } /* Keep an eye on VBUS and restart ramping if it dips */ if (board_is_ramp_allowed(active_sup) && board_is_vbus_too_low(CHG_RAMP_VBUS_STABLE)) { CPRINTS("VBUS low; Re-ramp"); active_icl_new = min_icl; ramp_st_new = CHG_RAMP_RAMP; } task_wait_time = STABLE_VBUS_MONITOR_INTERVAL; break; } if (ramp_st != ramp_st_new || active_icl != active_icl_new) CPRINTS("Ramp p%d st%d %dmA %dmA", active_port, ramp_st_new, min_icl, active_icl_new); ramp_st_prev = ramp_st; ramp_st = ramp_st_new; active_icl = active_icl_new; /* Set the input current limit */ lim = chg_ramp_get_current_limit(); board_set_charge_limit(active_port, active_sup, lim, lim); if (ramp_st == CHG_RAMP_STABILIZE) /* * When in stabilize state, supplier/port may change * and we don't want to wake up task until we have * slept this amount of time. */ usleep(task_wait_time); else task_wait_event(task_wait_time); } }
/** * Charge manager refresh -- responsible for selecting the active charge port * and charge power. Called as a deferred task. */ static void charge_manager_refresh(void) { int new_supplier, new_port; int new_charge_current, new_charge_current_uncapped; int new_charge_voltage, i; int updated_new_port = CHARGE_PORT_NONE; int updated_old_port = CHARGE_PORT_NONE; int ceil; /* Hunt for an acceptable charge port */ while (1) { charge_manager_get_best_charge_port(&new_port, &new_supplier); /* * If the port or supplier changed, make an attempt to switch to * the port. We will re-set the active port on a supplier change * to give the board-level function another chance to reject * the port, for example, if the port has become a charge * source. */ if ((new_port == charge_port && new_supplier == charge_supplier) || board_set_active_charge_port(new_port) == EC_SUCCESS) break; /* 'Dont charge' request must be accepted */ ASSERT(new_port != CHARGE_PORT_NONE); /* * Zero the available charge on the rejected port so that * it is no longer chosen. */ for (i = 0; i < CHARGE_SUPPLIER_COUNT; ++i) available_charge[i][new_port].current = 0; } /* * Clear override if it wasn't selected as the 'best' port -- it means * that no charge is available on the port, or the port was rejected. */ if (override_port >= 0 && override_port != new_port) override_port = OVERRIDE_OFF; if (new_supplier == CHARGE_SUPPLIER_NONE) { new_charge_current = 0; new_charge_current_uncapped = 0; new_charge_voltage = 0; } else { new_charge_current_uncapped = available_charge[new_supplier][new_port].current; #ifdef CONFIG_CHARGE_RAMP_HW /* * Allow to set the maximum current value, so the hardware can * know the range of acceptable current values for its ramping. */ if (board_is_ramp_allowed(new_supplier)) new_charge_current_uncapped = board_get_ramp_current_limit(new_supplier, new_charge_current_uncapped); #endif /* CONFIG_CHARGE_RAMP_HW */ /* Enforce port charge ceiling. */ ceil = charge_manager_get_ceil(new_port); if (ceil != CHARGE_CEIL_NONE) new_charge_current = MIN(ceil, new_charge_current_uncapped); else new_charge_current = new_charge_current_uncapped; new_charge_voltage = available_charge[new_supplier][new_port].voltage; } /* Change the charge limit + charge port/supplier if modified. */ if (new_port != charge_port || new_charge_current != charge_current || new_supplier != charge_supplier) { #ifdef HAS_TASK_CHG_RAMP chg_ramp_charge_supplier_change( new_port, new_supplier, new_charge_current, registration_time[new_port]); #else #ifdef CONFIG_CHARGE_RAMP_HW /* Enable or disable charge ramp */ charger_set_hw_ramp(board_is_ramp_allowed(new_supplier)); #endif board_set_charge_limit(new_charge_current); #endif CPRINTS("CL: p%d s%d i%d v%d", new_port, new_supplier, new_charge_current, new_charge_voltage); } /* * Signal new power request only if the port changed, the voltage * on the same port changed, or the actual uncapped current * on the same port changed (don't consider ceil). */ if (new_port != CHARGE_PORT_NONE && (new_port != charge_port || new_charge_current_uncapped != charge_current_uncapped || new_charge_voltage != charge_voltage)) updated_new_port = new_port; /* If charge port changed, cleanup old port */ if (charge_port != new_port && charge_port != CHARGE_PORT_NONE) { /* Check if need power swap */ charge_manager_switch_to_source(charge_port); /* Signal new power request on old port */ updated_old_port = charge_port; } /* Update globals to reflect current state. */ charge_current = new_charge_current; charge_current_uncapped = new_charge_current_uncapped; charge_voltage = new_charge_voltage; charge_supplier = new_supplier; charge_port = new_port; #ifdef CONFIG_USB_PD_LOGGING /* * Write a log under the following conditions: * 1. A port becomes active or * 2. A port becomes inactive or * 3. The active charge port power limit changes or * 4. Any supplier change on an inactive port */ if (updated_new_port != CHARGE_PORT_NONE) save_log[updated_new_port] = 1; /* Don't log non-meaningful changes on charge port */ else if (charge_port != CHARGE_PORT_NONE) save_log[charge_port] = 0; if (updated_old_port != CHARGE_PORT_NONE) save_log[updated_old_port] = 1; for (i = 0; i < CONFIG_USB_PD_PORT_COUNT; ++i) if (save_log[i]) charge_manager_save_log(i); #endif /* New power requests must be set only after updating the globals. */ if (updated_new_port != CHARGE_PORT_NONE) pd_set_new_power_request(updated_new_port); if (updated_old_port != CHARGE_PORT_NONE) pd_set_new_power_request(updated_old_port); }