summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorShawn Nematbakhsh <shawnn@chromium.org>2015-08-14 15:28:20 -0700
committerChromeOS Commit Bot <chromeos-commit-bot@chromium.org>2015-08-19 01:50:21 +0000
commit4d382ad6401cbe178fa9c53309bdc8a460cf3413 (patch)
tree30822cab382bbba385db6a1891d5df3daa060613
parentaa14b36f1890e373424463afc584659b2834dce6 (diff)
downloadchrome-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.c53
-rw-r--r--common/usb_pd_policy.c2
-rw-r--r--common/usb_pd_protocol.c16
-rw-r--r--include/charge_manager.h17
-rw-r--r--test/charge_manager.c35
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);