/** * Select an 'override port', a port which is always the preferred charge port. * Returns EC_SUCCESS on success, ec_error_list status on failure. * * @param port Charge port to select as override, or * OVERRIDE_OFF to select no override port, * or OVERRIDE_DONT_CHARGE to specifc that no * charge port should be selected. */ int charge_manager_set_override(int port) { int retval = EC_SUCCESS; ASSERT(port >= OVERRIDE_DONT_CHARGE && port < CONFIG_USB_PD_PORT_COUNT); CPRINTS("Charge Override: %d", port); /* Supersede any pending delayed overrides. */ if (delayed_override_port != OVERRIDE_OFF) { if (delayed_override_port != port) charge_manager_cleanup_override_port( delayed_override_port); delayed_override_port = OVERRIDE_OFF; hook_call_deferred( charge_override_timeout, -1); } /* Set the override port if it's a sink. */ if (port < 0 || pd_get_role(port) == PD_ROLE_SINK) { if (override_port != port) { charge_manager_cleanup_override_port(override_port); override_port = port; if (charge_manager_is_seeded()) hook_call_deferred(charge_manager_refresh, 0); } } /* * If the attached device is capable of being a sink, request a * power swap and set the delayed override for swap completion. */ else if (pd_get_role(port) != PD_ROLE_SINK && dualrole_capability[port] == CAP_DUALROLE) { delayed_override_deadline.val = get_time().val + POWER_SWAP_TIMEOUT; delayed_override_port = port; hook_call_deferred( charge_override_timeout, POWER_SWAP_TIMEOUT); pd_request_power_swap(port); /* Can't charge from requested port -- return error. */ } else retval = EC_ERROR_INVAL; return retval; }
/** * Select an 'override port', a port which is always the preferred charge port. * Returns EC_SUCCESS on success, ec_error_list status on failure. * * @param port Charge port to select as override, or * OVERRIDE_OFF to select no override port, * or OVERRIDE_DONT_CHARGE to specifc that no * charge port should be selected. */ int charge_manager_set_override(int port) { int retval = EC_SUCCESS; ASSERT(port >= OVERRIDE_DONT_CHARGE && port < CONFIG_USB_PD_PORT_COUNT); CPRINTS("Charge Override: %d", port); /* * If attempting to change the override port, then return * error. Since we may be in the middle of a power swap on * the original override port, it's too complicated to * guarantee that the original override port is switched back * to source. */ if (delayed_override_port != OVERRIDE_OFF) return EC_ERROR_BUSY; /* Set the override port if it's a sink. */ if (port < 0 || pd_get_role(port) == PD_ROLE_SINK) { if (override_port != port) { override_port = port; if (charge_manager_is_seeded()) hook_call_deferred(charge_manager_refresh, 0); } } /* * If the attached device is capable of being a sink, request a * power swap and set the delayed override for swap completion. */ else if (pd_get_role(port) != PD_ROLE_SINK && dualrole_capability[port] == CAP_DUALROLE) { delayed_override_deadline.val = get_time().val + POWER_SWAP_TIMEOUT; delayed_override_port = port; hook_call_deferred( charge_override_timeout, POWER_SWAP_TIMEOUT); pd_request_power_swap(port); /* Can't charge from requested port -- return error. */ } else retval = EC_ERROR_INVAL; return retval; }
/** * Perform cleanup operations on an override port, when switching to a * different port. This involves switching the port from sink to source, * if applicable. */ static void charge_manager_cleanup_override_port(int port) { if (port < 0 || port >= CONFIG_USB_PD_PORT_COUNT) return; if (dualrole_capability[port] == CAP_DUALROLE && pd_get_role(port) == PD_ROLE_SINK) pd_request_power_swap(port); }
/** * Attempt to switch to power source on port if applicable. */ static void charge_manager_switch_to_source(int port) { if (port < 0 || port >= CONFIG_USB_PD_PORT_COUNT) return; /* If connected to dual-role device, then ask for a swap */ if (dualrole_capability[port] == CAP_DUALROLE && pd_get_role(port) == PD_ROLE_SINK) pd_request_power_swap(port); }
static void charge_manager_make_change(enum charge_manager_change_type change, int supplier, int port, struct charge_port_info *charge) { int i; int clear_override = 0; /* Determine if this is a change which can affect charge status */ switch (change) { case CHANGE_CHARGE: /* Ignore changes where charge is identical */ if (available_charge[supplier][port].current == charge->current && available_charge[supplier][port].voltage == charge->voltage) return; if (charge->current > 0 && available_charge[supplier][port].current == 0) clear_override = 1; #ifdef CONFIG_USB_PD_LOGGING save_log[port] = 1; #endif break; case CHANGE_DUALROLE: /* * Ignore all except for transition to non-dualrole, * which may occur some time after we see a charge */ #ifndef CONFIG_CHARGE_MANAGER_DRP_CHARGING if (dualrole_capability[port] != CAP_DEDICATED) #endif return; /* Clear override only if a charge is present on the port */ for (i = 0; i < CHARGE_SUPPLIER_COUNT; ++i) if (available_charge[i][port].current > 0) { clear_override = 1; break; } /* * If there is no charge present on the port, the dualrole * change is meaningless to charge_manager. */ if (!clear_override) return; break; } /* Remove override when a charger is plugged */ if (clear_override && override_port != port #ifndef CONFIG_CHARGE_MANAGER_DRP_CHARGING /* only remove override when it's a dedicated charger */ && dualrole_capability[port] == CAP_DEDICATED #endif ) { override_port = OVERRIDE_OFF; if (delayed_override_port != OVERRIDE_OFF) { delayed_override_port = OVERRIDE_OFF; hook_call_deferred( charge_override_timeout, -1); } } if (change == CHANGE_CHARGE) { available_charge[supplier][port].current = charge->current; available_charge[supplier][port].voltage = charge->voltage; registration_time[port] = get_time(); /* * If we have a charge on our delayed override port within * the deadline, make it our override port. */ if (port == delayed_override_port && charge->current > 0 && pd_get_role(delayed_override_port) == PD_ROLE_SINK && get_time().val < delayed_override_deadline.val) { delayed_override_port = OVERRIDE_OFF; hook_call_deferred(charge_override_timeout, -1); charge_manager_set_override(port); } } /* * Don't call charge_manager_refresh unless all ports + * suppliers have reported in. We don't want to make changes * to our charge port until we are certain we know what is * attached. */ if (charge_manager_is_seeded()) hook_call_deferred(charge_manager_refresh, 0); }
/** * Fills passed power_info structure with current info about the passed port. */ static void charge_manager_fill_power_info(int port, struct ec_response_usb_pd_power_info *r) { int sup = CHARGE_SUPPLIER_NONE; int i; /* Determine supplier information to show. */ if (port == charge_port) sup = charge_supplier; else /* Find highest priority supplier */ for (i = 0; i < CHARGE_SUPPLIER_COUNT; ++i) if (available_charge[i][port].current > 0 && available_charge[i][port].voltage > 0 && (sup == CHARGE_SUPPLIER_NONE || supplier_priority[i] < supplier_priority[sup] || (supplier_priority[i] == supplier_priority[sup] && POWER(available_charge[i][port]) > POWER(available_charge[sup] [port])))) sup = i; /* Fill in power role */ if (charge_port == port) r->role = USB_PD_PORT_POWER_SINK; else if (pd_is_connected(port) && pd_get_role(port) == PD_ROLE_SOURCE) r->role = USB_PD_PORT_POWER_SOURCE; else if (sup != CHARGE_SUPPLIER_NONE) r->role = USB_PD_PORT_POWER_SINK_NOT_CHARGING; else r->role = USB_PD_PORT_POWER_DISCONNECTED; /* Is port partner dual-role capable */ r->dualrole = (dualrole_capability[port] == CAP_DUALROLE); if (sup == CHARGE_SUPPLIER_NONE || r->role == USB_PD_PORT_POWER_SOURCE) { r->type = USB_CHG_TYPE_NONE; r->meas.voltage_max = 0; r->meas.voltage_now = r->role == USB_PD_PORT_POWER_SOURCE ? 5000 : 0; r->meas.current_max = 0; r->max_power = 0; } else { #if defined(HAS_TASK_CHG_RAMP) || defined(CONFIG_CHARGE_RAMP_HW) /* Read ramped current if active charging port */ int use_ramp_current = (charge_port == port); #else const int use_ramp_current = 0; #endif switch (sup) { case CHARGE_SUPPLIER_PD: r->type = USB_CHG_TYPE_PD; break; case CHARGE_SUPPLIER_TYPEC: r->type = USB_CHG_TYPE_C; break; case CHARGE_SUPPLIER_PROPRIETARY: r->type = USB_CHG_TYPE_PROPRIETARY; break; case CHARGE_SUPPLIER_BC12_DCP: r->type = USB_CHG_TYPE_BC12_DCP; break; case CHARGE_SUPPLIER_BC12_CDP: r->type = USB_CHG_TYPE_BC12_CDP; break; case CHARGE_SUPPLIER_BC12_SDP: r->type = USB_CHG_TYPE_BC12_SDP; break; case CHARGE_SUPPLIER_VBUS: r->type = USB_CHG_TYPE_VBUS; break; default: r->type = USB_CHG_TYPE_OTHER; } r->meas.voltage_max = available_charge[sup][port].voltage; if (use_ramp_current) { /* * If charge_ramp has not detected charger yet, * then charger type is unknown. */ if (!chg_ramp_is_detected()) r->type = USB_CHG_TYPE_UNKNOWN; /* Current limit is output of ramp module */ r->meas.current_lim = chg_ramp_get_current_limit(); /* * If ramp is allowed, then the max current depends * on if ramp is stable. If ramp is stable, then * max current is same as input current limit. If * ramp is not stable, then we report the maximum * current we could ramp up to for this supplier. * If ramp is not allowed, max current is just the * available charge current. */ if (board_is_ramp_allowed(sup)) { r->meas.current_max = chg_ramp_is_stable() ? r->meas.current_lim : board_get_ramp_current_limit( sup, available_charge[sup][port].current); } else { r->meas.current_max = available_charge[sup][port].current; } r->max_power = r->meas.current_max * r->meas.voltage_max; } else { r->meas.current_max = r->meas.current_lim = available_charge[sup][port].current; r->max_power = POWER(available_charge[sup][port]); } /* * If we are sourcing power, or sinking but not charging, then * VBUS must be 5V. If we are charging, then read VBUS ADC. */ if (r->role == USB_PD_PORT_POWER_SINK_NOT_CHARGING) r->meas.voltage_now = 5000; else if (ADC_VBUS >= 0) r->meas.voltage_now = adc_read_channel(ADC_VBUS); else /* No VBUS ADC channel - voltage is unknown */ r->meas.voltage_now = 0; } }
static int test_dual_role(void) { struct charge_port_info charge; /* Initialize table to no charge. */ initialize_charge_table(0, 5000, 1000); /* Mark P0 as dual-role and set a charge. */ charge_manager_update_dualrole(0, CAP_DUALROLE); charge.current = 500; charge.voltage = 5000; charge_manager_update_charge(CHARGE_SUPPLIER_TEST2, 0, &charge); wait_for_charge_manager_refresh(); #ifdef CONFIG_CHARGE_MANAGER_DRP_CHARGING /* Verify we do charge from dual-role port */ TEST_ASSERT(active_charge_port == 0); TEST_ASSERT(active_charge_limit == 500); #else /* Verify we don't charge from dual-role port */ TEST_ASSERT(active_charge_port == CHARGE_PORT_NONE); TEST_ASSERT(active_charge_limit == 0); #endif /* Mark P0 as the override port, verify that we now charge. */ charge_manager_set_override(0); wait_for_charge_manager_refresh(); TEST_ASSERT(active_charge_port == 0); TEST_ASSERT(active_charge_limit == 500); TEST_ASSERT(pd_get_role(0) == PD_ROLE_SINK); /* Remove override and verify we go back to previous state */ charge_manager_set_override(OVERRIDE_OFF); wait_for_charge_manager_refresh(); #ifdef CONFIG_CHARGE_MANAGER_DRP_CHARGING TEST_ASSERT(active_charge_port == 0); TEST_ASSERT(active_charge_limit == 500); #else TEST_ASSERT(active_charge_port == CHARGE_PORT_NONE); TEST_ASSERT(active_charge_limit == 0); TEST_ASSERT(pd_get_role(0) == PD_ROLE_SOURCE); #endif /* Mark P0 as the override port, verify that we again charge. */ charge_manager_set_override(0); charge.current = 550; charge_manager_update_charge(CHARGE_SUPPLIER_TEST2, 0, &charge); wait_for_charge_manager_refresh(); TEST_ASSERT(active_charge_port == 0); TEST_ASSERT(active_charge_limit == 550); TEST_ASSERT(pd_get_role(0) == PD_ROLE_SINK); /* * Insert a dual-role charger into P1 and set the override. Verify * that the override correctly changes. */ charge_manager_update_dualrole(1, CAP_DUALROLE); charge_manager_set_override(1); charge.current = 500; charge_manager_update_charge(CHARGE_SUPPLIER_TEST6, 1, &charge); wait_for_charge_manager_refresh(); TEST_ASSERT(active_charge_port == 1); TEST_ASSERT(active_charge_limit == 500); TEST_ASSERT(pd_get_role(1) == PD_ROLE_SINK); TEST_ASSERT(pd_get_role(0) == PD_ROLE_SOURCE); /* Set override back to P0 and verify switch */ charge_manager_set_override(0); charge.current = 600; charge_manager_update_charge(CHARGE_SUPPLIER_TEST2, 0, &charge); wait_for_charge_manager_refresh(); TEST_ASSERT(active_charge_port == 0); TEST_ASSERT(active_charge_limit == 600); TEST_ASSERT(pd_get_role(0) == PD_ROLE_SINK); TEST_ASSERT(pd_get_role(1) == PD_ROLE_SOURCE); /* Insert a dedicated charger and verify override is removed */ charge.current = 0; charge_manager_update_charge(CHARGE_SUPPLIER_TEST6, 1, &charge); wait_for_charge_manager_refresh(); charge_manager_update_dualrole(1, CAP_DEDICATED); charge.current = 400; charge_manager_update_charge(CHARGE_SUPPLIER_TEST6, 1, &charge); wait_for_charge_manager_refresh(); #ifdef CONFIG_CHARGE_MANAGER_DRP_CHARGING TEST_ASSERT(active_charge_port == 0); TEST_ASSERT(active_charge_limit == 600); #else TEST_ASSERT(active_charge_port == 1); TEST_ASSERT(active_charge_limit == 400); TEST_ASSERT(pd_get_role(0) == PD_ROLE_SOURCE); #endif /* * Verify the port is handled normally if the dual-role source is * unplugged and replaced with a dedicated source. */ charge_manager_update_dualrole(0, CAP_DEDICATED); charge.current = 0; charge_manager_update_charge(CHARGE_SUPPLIER_TEST2, 0, &charge); charge.current = 500; charge_manager_update_charge(CHARGE_SUPPLIER_TEST2, 0, &charge); wait_for_charge_manager_refresh(); TEST_ASSERT(active_charge_port == 0); TEST_ASSERT(active_charge_limit == 500); /* * Test one port connected to dedicated charger and one connected * to dual-role device. */ charge_manager_update_dualrole(0, CAP_DUALROLE); charge.current = 0; charge_manager_update_charge(CHARGE_SUPPLIER_TEST2, 0, &charge); charge.current = 500; charge_manager_update_charge(CHARGE_SUPPLIER_TEST2, 0, &charge); charge.current = 200; charge_manager_update_charge(CHARGE_SUPPLIER_TEST6, 1, &charge); wait_for_charge_manager_refresh(); #ifdef CONFIG_CHARGE_MANAGER_DRP_CHARGING /* Verify we charge from port with higher priority */ TEST_ASSERT(active_charge_port == 0); TEST_ASSERT(active_charge_limit == 500); #else /* * Verify that we charge from the dedicated port if a dual-role * source is also attached. */ TEST_ASSERT(active_charge_port == 1); TEST_ASSERT(active_charge_limit == 200); TEST_ASSERT(pd_get_role(0) == PD_ROLE_SOURCE); #endif return EC_SUCCESS; }
static int test_override(void) { struct charge_port_info charge; /* Initialize table to no charge */ initialize_charge_table(0, 5000, 1000); /* * Set a low-priority supplier on p0 and high-priority on p1, then * verify that p1 is selected. */ charge.current = 500; charge.voltage = 5000; charge_manager_update_charge(CHARGE_SUPPLIER_TEST2, 0, &charge); charge_manager_update_charge(CHARGE_SUPPLIER_TEST1, 1, &charge); wait_for_charge_manager_refresh(); TEST_ASSERT(active_charge_port == 1); TEST_ASSERT(active_charge_limit == 500); /* Set override to p0 and verify p0 is selected */ charge_manager_set_override(0); wait_for_charge_manager_refresh(); TEST_ASSERT(active_charge_port == 0); /* Remove override and verify p1 is again selected */ charge_manager_set_override(OVERRIDE_OFF); wait_for_charge_manager_refresh(); TEST_ASSERT(active_charge_port == 1); /* * Set override again to p0, but set p0 charge to 0, and verify p1 * is again selected. */ charge.current = 0; charge_manager_update_charge(CHARGE_SUPPLIER_TEST2, 0, &charge); charge_manager_set_override(0); wait_for_charge_manager_refresh(); TEST_ASSERT(active_charge_port == 1); /* Set non-zero charge on port 0 and verify override was auto-removed */ charge.current = 250; charge_manager_update_charge(CHARGE_SUPPLIER_TEST5, 0, &charge); wait_for_charge_manager_refresh(); TEST_ASSERT(active_charge_port == 1); /* * Verify current limit is still selected according to supplier * priority on the override port. */ charge.current = 300; charge_manager_update_charge(CHARGE_SUPPLIER_TEST2, 0, &charge); charge_manager_set_override(0); wait_for_charge_manager_refresh(); TEST_ASSERT(active_charge_port == 0); TEST_ASSERT(active_charge_limit == 300); charge.current = 100; charge_manager_update_charge(CHARGE_SUPPLIER_TEST1, 0, &charge); charge_manager_set_override(0); wait_for_charge_manager_refresh(); TEST_ASSERT(active_charge_port == 0); TEST_ASSERT(active_charge_limit == 100); /* * Verify that a don't charge override request on a dual-role * port causes a swap to source. */ pd_set_role(0, PD_ROLE_SINK); charge_manager_update_dualrole(0, CAP_DUALROLE); charge_manager_set_override(OVERRIDE_DONT_CHARGE); wait_for_charge_manager_refresh(); TEST_ASSERT(pd_get_role(0) == PD_ROLE_SOURCE); /* * Verify that an override request to a dual-role source port * causes a role swap to sink. */ charge_manager_set_override(0); wait_for_charge_manager_refresh(); charge.current = 200; charge_manager_update_charge(CHARGE_SUPPLIER_TEST1, 0, &charge); wait_for_charge_manager_refresh(); TEST_ASSERT(active_charge_port == 0); TEST_ASSERT(active_charge_limit == 200); TEST_ASSERT(pd_get_role(0) == PD_ROLE_SINK); /* Set override to "don't charge", then verify we're not charging */ charge_manager_set_override(OVERRIDE_DONT_CHARGE); wait_for_charge_manager_refresh(); TEST_ASSERT(active_charge_port == CHARGE_PORT_NONE); TEST_ASSERT(active_charge_limit == 0); TEST_ASSERT(pd_get_role(0) == PD_ROLE_SOURCE); /* Update a charge supplier, verify that we still aren't charging */ charge.current = 200; charge_manager_update_charge(CHARGE_SUPPLIER_TEST1, 0, &charge); wait_for_charge_manager_refresh(); TEST_ASSERT(active_charge_port == CHARGE_PORT_NONE); TEST_ASSERT(active_charge_limit == 0); TEST_ASSERT(pd_get_role(0) == PD_ROLE_SOURCE); /* Turn override off, verify that we go back to the correct charge */ charge_manager_set_override(OVERRIDE_OFF); wait_for_charge_manager_refresh(); TEST_ASSERT(active_charge_port == 1); TEST_ASSERT(active_charge_limit == 500); TEST_ASSERT(pd_get_role(0) == PD_ROLE_SOURCE); return EC_SUCCESS; }