charge_manager: Support multiple independent charge ceilings

We will soon have a need to independently set a charge ceiling from both
the PD state machine and from incoming host commands. Store these
ceilings separately, and have the minimum take effect.

BUG=chrome-os-partner:43285
TEST=Pass unit tests. Also, host command current limit takes effect with
subsequent commit.
BRANCH=None

Change-Id: I0ecfe888a7df0d5da5a68999c164c7c841da348b
Signed-off-by: Shawn Nematbakhsh <shawnn@chromium.org>
Reviewed-on: https://chromium-review.googlesource.com/293818
Reviewed-by: Alec Berg <alecaberg@chromium.org>
This commit is contained in:
Shawn Nematbakhsh
2015-08-14 15:28:20 -07:00
committed by ChromeOS Commit Bot
parent aa14b36f18
commit 4d382ad640
5 changed files with 100 additions and 23 deletions

View File

@@ -45,10 +45,12 @@ static struct charge_port_info available_charge[CHARGE_SUPPLIER_COUNT]
static timestamp_t registration_time[CONFIG_USB_PD_PORT_COUNT];
/*
* Charge ceiling for ports. This can be set to temporarily limit the charge
* pulled from a port, without influencing the port selection logic.
* Charge current ceiling (mA) for ports. This can be set to temporarily limit
* the charge pulled from a port, without influencing the port selection logic.
* The ceiling can be set independently from several requestors, with the
* minimum ceiling taking effect.
*/
static int charge_ceil[CONFIG_USB_PD_PORT_COUNT];
static int charge_ceil[CONFIG_USB_PD_PORT_COUNT][CEIL_REQUESTOR_COUNT];
/* Dual-role capability of attached partner port */
static enum dualrole_capabilities dualrole_capability[CONFIG_USB_PD_PORT_COUNT];
@@ -105,7 +107,8 @@ static void charge_manager_init(void)
available_charge[j][i].voltage =
CHARGE_VOLTAGE_UNINITIALIZED;
}
charge_ceil[i] = CHARGE_CEIL_NONE;
for (j = 0; j < CEIL_REQUESTOR_COUNT; ++j)
charge_ceil[i][j] = CHARGE_CEIL_NONE;
dualrole_capability[i] = spoof_capability ? CAP_DEDICATED :
CAP_UNKNOWN;
}
@@ -316,6 +319,29 @@ static void charge_manager_cleanup_override_port(int port)
pd_request_power_swap(port);
}
/**
* Return the computed charge ceiling for a port, which represents the
* minimum ceiling among all valid requestors.
*
* @param port Charge port.
* @return Charge ceiling (mA) or CHARGE_CEIL_NONE.
*/
static int charge_manager_get_ceil(int port)
{
int ceil = CHARGE_CEIL_NONE;
int val, i;
ASSERT(port >= 0 && port < CONFIG_USB_PD_PORT_COUNT);
for (i = 0; i < CEIL_REQUESTOR_COUNT; ++i) {
val = charge_ceil[port][i];
if (val != CHARGE_CEIL_NONE &&
(ceil == CHARGE_CEIL_NONE || val < ceil))
ceil = val;
}
return ceil;
}
/**
* Select the 'best' charge port, as defined by the supplier heirarchy and the
* ability of the port to provide power.
@@ -411,6 +437,7 @@ static void charge_manager_refresh(void)
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) {
@@ -467,8 +494,9 @@ static void charge_manager_refresh(void)
new_charge_current_uncapped);
#endif /* CONFIG_CHARGE_RAMP_HW */
/* Enforce port charge ceiling. */
if (charge_ceil[new_port] != CHARGE_CEIL_NONE)
new_charge_current = MIN(charge_ceil[new_port],
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;
@@ -677,17 +705,20 @@ void charge_manager_update_dualrole(int port, enum dualrole_capabilities cap)
}
/**
* Update charge ceiling for a given port.
* Update charge ceiling for a given port. The ceiling can be set independently
* for several requestors, and the min. ceil will be enforced.
*
* @param port Charge port to update.
* @param requestor Charge ceiling requestor.
* @param ceil Charge ceiling (mA).
*/
void charge_manager_set_ceil(int port, int ceil)
void charge_manager_set_ceil(int port, enum ceil_requestor requestor, int ceil)
{
ASSERT(port >= 0 && port < CONFIG_USB_PD_PORT_COUNT);
ASSERT(port >= 0 && port < CONFIG_USB_PD_PORT_COUNT &&
requestor >= 0 && requestor < CEIL_REQUESTOR_COUNT);
if (charge_ceil[port] != ceil) {
charge_ceil[port] = ceil;
if (charge_ceil[port][requestor] != ceil) {
charge_ceil[port][requestor] = ceil;
if (port == charge_port && charge_manager_is_seeded())
hook_call_deferred(charge_manager_refresh, 0);
}

View File

@@ -161,7 +161,7 @@ void pd_process_source_cap(int port, int cnt, uint32_t *src_caps)
pd_extract_pdo_power(src_caps[pdo_index], &ma, &mv);
/* Set max. limit, but apply 500mA ceiling */
charge_manager_set_ceil(port, PD_MIN_MA);
charge_manager_set_ceil(port, CEIL_REQUESTOR_PD, PD_MIN_MA);
pd_set_input_current_limit(port, ma, mv);
#endif
}

View File

@@ -265,7 +265,9 @@ static inline void set_state(int port, enum pd_states next_state)
pd_set_input_current_limit(port, 0, 0);
#ifdef CONFIG_CHARGE_MANAGER
typec_set_input_current_limit(port, 0, 0);
charge_manager_set_ceil(port, CHARGE_CEIL_NONE);
charge_manager_set_ceil(port,
CEIL_REQUESTOR_PD,
CHARGE_CEIL_NONE);
#endif
#ifdef CONFIG_USBC_VCONN
tcpm_set_vconn(port, 0);
@@ -505,7 +507,9 @@ void pd_execute_hard_reset(int port)
/* Clear the input current limit */
pd_set_input_current_limit(port, 0, 0);
#ifdef CONFIG_CHARGE_MANAGER
charge_manager_set_ceil(port, CHARGE_CEIL_NONE);
charge_manager_set_ceil(port,
CEIL_REQUESTOR_PD,
CHARGE_CEIL_NONE);
#endif /* CONFIG_CHARGE_MANAGER */
set_state(port, PD_STATE_SNK_HARD_RESET_RECOVER);
@@ -870,7 +874,9 @@ static void handle_ctrl_request(int port, uint16_t head,
set_state(port, PD_STATE_SNK_READY);
#ifdef CONFIG_CHARGE_MANAGER
/* Set ceiling based on what's negotiated */
charge_manager_set_ceil(port, pd[port].curr_limit);
charge_manager_set_ceil(port,
CEIL_REQUESTOR_PD,
pd[port].curr_limit);
#else
pd_set_input_current_limit(port, pd[port].curr_limit,
pd[port].supply_voltage);
@@ -2211,7 +2217,9 @@ void pd_task(void)
pd_set_input_current_limit(port, 0, 0);
#ifdef CONFIG_CHARGE_MANAGER
typec_set_input_current_limit(port, 0, 0);
charge_manager_set_ceil(port, CHARGE_CEIL_NONE);
charge_manager_set_ceil(port,
CEIL_REQUESTOR_PD,
CHARGE_CEIL_NONE);
#endif
set_state(port, PD_STATE_SNK_SWAP_SRC_DISABLE);
timeout = 10*MSEC;

View File

@@ -51,8 +51,21 @@ enum dualrole_capabilities {
/* Called by charging tasks to indicate partner dualrole capability change */
void charge_manager_update_dualrole(int port, enum dualrole_capabilities cap);
/* Update charge ceiling for a given port */
void charge_manager_set_ceil(int port, int ceil);
/*
* Charge ceiling can be set independently by different tasks / functions,
* for different purposes.
*/
enum ceil_requestor {
/* Set by PD task, during negotiation */
CEIL_REQUESTOR_PD,
/* Set by host commands */
CEIL_REQUESTOR_HOST,
/* Number of ceiling groups */
CEIL_REQUESTOR_COUNT,
};
/* Update charge ceiling for a given port / requestor */
void charge_manager_set_ceil(int port, enum ceil_requestor requestor, int ceil);
/* Select an 'override port', which is always the preferred charge port */
int charge_manager_set_override(int port);

View File

@@ -118,7 +118,8 @@ static void initialize_charge_table(int current, int voltage, int ceil)
charge.voltage = voltage;
for (i = 0; i < CONFIG_USB_PD_PORT_COUNT; ++i) {
charge_manager_set_ceil(i, ceil);
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)
@@ -246,13 +247,13 @@ static int test_charge_ceil(void)
/* Set a 500mA ceiling, verify port is unchanged */
port = active_charge_port;
charge_manager_set_ceil(port, 500);
charge_manager_set_ceil(port, 0, 500);
wait_for_charge_manager_refresh();
TEST_ASSERT(port == active_charge_port);
TEST_ASSERT(active_charge_limit == 500);
/* Raise the ceiling to 2A, verify limit goes back to 1A */
charge_manager_set_ceil(port, 2000);
charge_manager_set_ceil(port, 0, 2000);
wait_for_charge_manager_refresh();
TEST_ASSERT(port == active_charge_port);
TEST_ASSERT(active_charge_limit == 1000);
@@ -263,11 +264,35 @@ static int test_charge_ceil(void)
charge_manager_update_charge(0, 0, &charge);
charge.current = 2500;
charge_manager_update_charge(0, 1, &charge);
charge_manager_set_ceil(1, 750);
charge_manager_set_ceil(1, 0, 750);
wait_for_charge_manager_refresh();
TEST_ASSERT(active_charge_port == 1);
TEST_ASSERT(active_charge_limit == 750);
/* Set a secondary lower ceiling and verify it takes effect */
charge_manager_set_ceil(1, 1, 500);
wait_for_charge_manager_refresh();
TEST_ASSERT(active_charge_port == 1);
TEST_ASSERT(active_charge_limit == 500);
/* Raise the secondary ceiling and verify the primary takes effect */
charge_manager_set_ceil(1, 1, 800);
wait_for_charge_manager_refresh();
TEST_ASSERT(active_charge_port == 1);
TEST_ASSERT(active_charge_limit == 750);
/* Remove the primary celing and verify the secondary takes effect */
charge_manager_set_ceil(1, 0, CHARGE_CEIL_NONE);
wait_for_charge_manager_refresh();
TEST_ASSERT(active_charge_port == 1);
TEST_ASSERT(active_charge_limit == 800);
/* Remove all ceilings */
charge_manager_set_ceil(1, 1, CHARGE_CEIL_NONE);
wait_for_charge_manager_refresh();
TEST_ASSERT(active_charge_port == 1);
TEST_ASSERT(active_charge_limit == 2500);
return EC_SUCCESS;
}
@@ -293,7 +318,7 @@ static int test_new_power_request(void)
clear_new_power_requests();
/* Reduce port 1 through ceil and verify no NPR */
charge_manager_set_ceil(1, 500);
charge_manager_set_ceil(1, 0, 500);
wait_for_charge_manager_refresh();
TEST_ASSERT(new_power_request[0] == 0);
TEST_ASSERT(new_power_request[1] == 0);