diff options
author | Shawn Nematbakhsh <shawnn@chromium.org> | 2015-08-14 15:28:20 -0700 |
---|---|---|
committer | ChromeOS Commit Bot <chromeos-commit-bot@chromium.org> | 2015-08-19 01:50:21 +0000 |
commit | 4d382ad6401cbe178fa9c53309bdc8a460cf3413 (patch) | |
tree | 30822cab382bbba385db6a1891d5df3daa060613 | |
parent | aa14b36f1890e373424463afc584659b2834dce6 (diff) | |
download | chrome-ec-4d382ad6401cbe178fa9c53309bdc8a460cf3413.tar.gz |
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>
-rw-r--r-- | common/charge_manager.c | 53 | ||||
-rw-r--r-- | common/usb_pd_policy.c | 2 | ||||
-rw-r--r-- | common/usb_pd_protocol.c | 16 | ||||
-rw-r--r-- | include/charge_manager.h | 17 | ||||
-rw-r--r-- | test/charge_manager.c | 35 |
5 files changed, 100 insertions, 23 deletions
diff --git a/common/charge_manager.c b/common/charge_manager.c index da465d2d73..4e21bdf2bd 100644 --- a/common/charge_manager.c +++ b/common/charge_manager.c @@ -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; } @@ -317,6 +320,29 @@ static void charge_manager_cleanup_override_port(int 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); } diff --git a/common/usb_pd_policy.c b/common/usb_pd_policy.c index 2606ca651f..16a9fefa7a 100644 --- a/common/usb_pd_policy.c +++ b/common/usb_pd_policy.c @@ -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 } diff --git a/common/usb_pd_protocol.c b/common/usb_pd_protocol.c index 1ab2209d5d..3b5cd5b445 100644 --- a/common/usb_pd_protocol.c +++ b/common/usb_pd_protocol.c @@ -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; diff --git a/include/charge_manager.h b/include/charge_manager.h index 8758c3a19c..6cfd6cc7ce 100644 --- a/include/charge_manager.h +++ b/include/charge_manager.h @@ -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); diff --git a/test/charge_manager.c b/test/charge_manager.c index 1941af19c7..09b4540e8c 100644 --- a/test/charge_manager.c +++ b/test/charge_manager.c @@ -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); |