Beispiel #1
0
static int hc_charge_port_override(struct host_cmd_handler_args *args)
{
	const struct ec_params_charge_port_override *p = args->params;
	const int16_t override_port = p->override_port;

	if (override_port < OVERRIDE_DONT_CHARGE ||
	    override_port >= CONFIG_USB_PD_PORT_COUNT)
		return EC_RES_INVALID_PARAM;

	return charge_manager_set_override(override_port);
}
Beispiel #2
0
static void initialize_charge_table(int current, int voltage, int ceil)
{
	int i, j;
	struct charge_port_info charge;

	charge_manager_set_override(OVERRIDE_OFF);
	charge.current = current;
	charge.voltage = voltage;

	for (i = 0; i < PD_PORT_COUNT; ++i) {
		charge_manager_set_ceil(i, ceil);
		set_charger_role(i, DEDICATED_CHARGER);
		for (j = 0; j < CHARGE_SUPPLIER_COUNT; ++j)
			charge_manager_update(j, i, &charge);
	}
	wait_for_charge_manager_refresh();
}
Beispiel #3
0
static int command_charge_port_override(int argc, char **argv)
{
	int port = OVERRIDE_OFF;
	int ret = EC_SUCCESS;
	char *e;

	if (argc >= 2) {
		port = strtoi(argv[1], &e, 0);
		if (*e || port < OVERRIDE_DONT_CHARGE ||
		    port >= CONFIG_USB_PD_PORT_COUNT)
			return EC_ERROR_PARAM1;
		ret = charge_manager_set_override(port);
	}

	ccprintf("Override: %d\n", (argc >= 2 && ret == EC_SUCCESS) ?
					port : override_port);
	return ret;
}
Beispiel #4
0
static void initialize_charge_table(int current, int voltage, int ceil)
{
	int i, j;
	struct charge_port_info charge;

	charge_manager_set_override(OVERRIDE_OFF);
	set_charge_port_to_reject(CHARGE_PORT_NONE);
	charge.current = current;
	charge.voltage = voltage;

	for (i = 0; i < CONFIG_USB_PD_PORT_COUNT; ++i) {
		for (j = 0; j < CEIL_REQUESTOR_COUNT; ++j)
			charge_manager_set_ceil(i, j, ceil);
		charge_manager_update_dualrole(i, CAP_DEDICATED);
		pd_set_role(i, PD_ROLE_SINK);
		for (j = 0; j < CHARGE_SUPPLIER_COUNT; ++j)
			charge_manager_update_charge(j, i, &charge);
	}
	wait_for_charge_manager_refresh();
}
Beispiel #5
0
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);
}
Beispiel #6
0
static int test_unknown_dualrole_capability(void)
{
	struct charge_port_info charge;

	/* Initialize table to no charge. */
	initialize_charge_table(0, 5000, 2000);
	TEST_ASSERT(active_charge_port == CHARGE_PORT_NONE);

	/* Set a charge on P0 with unknown dualrole capability, */
	charge.current = 500;
	charge.voltage = 5000;
	charge_manager_update_dualrole(0, CAP_UNKNOWN);
	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 that port */
	TEST_ASSERT(active_charge_port == 0);
	TEST_ASSERT(active_charge_limit == 500);
#else
	/* Verify that we don't charge from the port. */
	TEST_ASSERT(active_charge_port == CHARGE_PORT_NONE);
#endif

	/* Toggle to dedicated and verify port becomes active. */
	charge_manager_update_dualrole(0, CAP_DEDICATED);
	wait_for_charge_manager_refresh();
	TEST_ASSERT(active_charge_port == 0);

	/* Add dualrole charger in port 1 */
	charge.current = 1000;
	charge_manager_update_dualrole(1, CAP_DUALROLE);
	charge_manager_update_charge(CHARGE_SUPPLIER_TEST2, 1, &charge);
	wait_for_charge_manager_refresh();
#ifdef CONFIG_CHARGE_MANAGER_DRP_CHARGING
	TEST_ASSERT(active_charge_port == 1);
	TEST_ASSERT(active_charge_limit == 1000);
#else
	TEST_ASSERT(active_charge_port == 0);
#endif

	/* Remove charger on port 0 */
	charge.current = 0;
	charge_manager_update_dualrole(0, CAP_UNKNOWN);
	charge_manager_update_charge(CHARGE_SUPPLIER_TEST2, 0, &charge);
	wait_for_charge_manager_refresh();
#ifdef CONFIG_CHARGE_MANAGER_DRP_CHARGING
	TEST_ASSERT(active_charge_port == 1);
	TEST_ASSERT(active_charge_limit == 1000);
#else
	TEST_ASSERT(active_charge_port == CHARGE_PORT_NONE);
#endif

	/* Set override to charge on port 1 */
	charge_manager_set_override(1);
	wait_for_charge_manager_refresh();
	TEST_ASSERT(active_charge_port == 1);

	/*
	 * Toggle port 0 to dedicated, verify that override is still kept
	 * because there's no charge on the port.
	 */
	charge_manager_update_dualrole(0, CAP_DEDICATED);
	wait_for_charge_manager_refresh();
	TEST_ASSERT(active_charge_port == 1);

	/* Insert UNKNOWN capability charger on port 0 */
	charge_manager_update_dualrole(0, CAP_UNKNOWN);
	charge.current = 2000;
	charge_manager_update_charge(CHARGE_SUPPLIER_TEST2, 0, &charge);
	wait_for_charge_manager_refresh();
	wait_for_charge_manager_refresh();
	wait_for_charge_manager_refresh();
#ifdef CONFIG_CHARGE_MANAGER_DRP_CHARGING
	/* Verify override is removed */
	TEST_ASSERT(active_charge_port == 0);
	TEST_ASSERT(active_charge_limit == 2000);
#else
	/* Verify override is still kept */
	TEST_ASSERT(active_charge_port == 1);
#endif

	/* Toggle to dualrole */
	charge_manager_update_dualrole(0, CAP_DUALROLE);
	wait_for_charge_manager_refresh();
#ifdef CONFIG_CHARGE_MANAGER_DRP_CHARGING
	/* Verify no change */
	TEST_ASSERT(active_charge_port == 0);
#else
	/* Verify override is still kept */
	TEST_ASSERT(active_charge_port == 1);
#endif

	/* Toggle to dedicated */
	charge_manager_update_dualrole(0, CAP_UNKNOWN);
	wait_for_charge_manager_refresh();
#ifdef CONFIG_CHARGE_MANAGER_DRP_CHARGING
	/* Verify no change */
	TEST_ASSERT(active_charge_port == 0);
#else
	/* Verify override is still kept */
	TEST_ASSERT(active_charge_port == 1);
#endif
	charge_manager_update_dualrole(0, CAP_DEDICATED);
	wait_for_charge_manager_refresh();
	TEST_ASSERT(active_charge_port == 0);

	return EC_SUCCESS;
}
Beispiel #7
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;
}
Beispiel #8
0
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;
}
Beispiel #9
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. Verify that we don't charge
	 * from the port.
	 */
	set_charger_role(0, DUAL_ROLE_CHARGER);
	charge.current = 500;
	charge.voltage = 5000;
	charge_manager_update(CHARGE_SUPPLIER_TEST2, 0, &charge);
	wait_for_charge_manager_refresh();
	TEST_ASSERT(active_charge_port == CHARGE_PORT_NONE);
	TEST_ASSERT(active_charge_limit == 0);

	/* 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);

	/* Remove override and verify we go back to not charging */
	charge_manager_set_override(OVERRIDE_OFF);
	wait_for_charge_manager_refresh();
	TEST_ASSERT(active_charge_port == CHARGE_PORT_NONE);
	TEST_ASSERT(active_charge_limit == 0);

	/* Mark P0 as the override port, verify that we again charge. */
	charge_manager_set_override(0);
	wait_for_charge_manager_refresh();
	TEST_ASSERT(active_charge_port == 0);
	TEST_ASSERT(active_charge_limit == 500);

	/* Insert a dedicated charger and verify override is removed */
	charge.current = 400;
	charge_manager_update(CHARGE_SUPPLIER_TEST6, 1, &charge);
	wait_for_charge_manager_refresh();
	TEST_ASSERT(active_charge_port == 1);
	TEST_ASSERT(active_charge_limit == 400);

	/*
	 * Verify the port is handled normally if the dual-role source is
	 * unplugged and replaced with a dedicated source.
	 */
	set_charger_role(0, DEDICATED_CHARGER);
	charge.current = 0;
	charge_manager_update(CHARGE_SUPPLIER_TEST2, 0, &charge);
	charge.current = 500;
	charge_manager_update(CHARGE_SUPPLIER_TEST2, 0, &charge);
	wait_for_charge_manager_refresh();
	TEST_ASSERT(active_charge_port == 0);
	TEST_ASSERT(active_charge_limit == 500);

	/*
	 * Verify that we charge from the dedicated port if a dual-role
	 * source is also attached.
	 */
	set_charger_role(0, DUAL_ROLE_CHARGER);
	charge.current = 0;
	charge_manager_update(CHARGE_SUPPLIER_TEST2, 0, &charge);
	charge.current = 500;
	charge_manager_update(CHARGE_SUPPLIER_TEST2, 0, &charge);
	charge.current = 200;
	charge_manager_update(CHARGE_SUPPLIER_TEST6, 1, &charge);
	wait_for_charge_manager_refresh();
	TEST_ASSERT(active_charge_port == 1);
	TEST_ASSERT(active_charge_limit == 200);

	return EC_SUCCESS;
}