summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorShawn Nematbakhsh <shawnn@chromium.org>2014-10-28 09:20:18 -0700
committerchrome-internal-fetch <chrome-internal-fetch@google.com>2014-11-13 00:29:52 +0000
commit06fb4fe0f2ee33c912fa10ed7fecdbe8c9c193d6 (patch)
tree2da534db21eb2895df5fc052e007abfb734cf788
parent27367a07c923c778941a5c1a0635e1694d51030a (diff)
downloadchrome-ec-06fb4fe0f2ee33c912fa10ed7fecdbe8c9c193d6.tar.gz
usb_pd: Negotiate minimum power for unused ports
When a PD charger is detected, gets its max charge capability, but initially negotiate for its minimum charge mode. If we later determine that the port will be the one active charge port, re-negotiate for the max charge capability. BUG=chrome-os-partner:32003 TEST=Manual on Samus. Plug in Zinger, verify that current limit is initially set to 500 mA, then switches to 3000 mA shortly after. Plug in two Zingers, verify that one provides 3000 mA current while the other negotiates to 500 mA. Verify that the 500 mA charger bumps up to a high current once becoming active. BRANCH=Samus Change-Id: Ifa562b72d763642fc8bd62bc7f5aaa4eda1ef950 Signed-off-by: Shawn Nematbakhsh <shawnn@chromium.org> Reviewed-on: https://chromium-review.googlesource.com/225922 Reviewed-by: Alec Berg <alecaberg@chromium.org>
-rw-r--r--board/samus_pd/board.h2
-rw-r--r--board/samus_pd/usb_pd_policy.c32
-rw-r--r--common/charge_manager.c75
-rw-r--r--common/usb_pd_protocol.c83
-rw-r--r--include/charge_manager.h7
-rw-r--r--include/usb_pd.h24
6 files changed, 190 insertions, 33 deletions
diff --git a/board/samus_pd/board.h b/board/samus_pd/board.h
index b4a9e8d304..19a68e4867 100644
--- a/board/samus_pd/board.h
+++ b/board/samus_pd/board.h
@@ -19,6 +19,8 @@
#define CONFIG_ADC
#define CONFIG_BOARD_PRE_INIT
#define CONFIG_CHARGE_MANAGER
+/* Minimum ilim = 500 mA */
+#define CONFIG_CHARGER_INPUT_CURRENT PWM_0_MA
#undef CONFIG_CONSOLE_CMDHELP
#define CONFIG_FORCE_CONSOLE_RESUME
#define CONFIG_HIBERNATE_WAKEUP_PINS (STM32_PWR_CSR_EWUP3|STM32_PWR_CSR_EWUP8)
diff --git a/board/samus_pd/usb_pd_policy.c b/board/samus_pd/usb_pd_policy.c
index 0425ef66a9..1678a005f8 100644
--- a/board/samus_pd/usb_pd_policy.c
+++ b/board/samus_pd/usb_pd_policy.c
@@ -39,8 +39,9 @@ const int pd_snk_pdo_cnt = ARRAY_SIZE(pd_snk_pdo);
/* Cap on the max voltage requested as a sink (in millivolts) */
static unsigned max_mv = -1; /* no cap */
-int pd_choose_voltage(int cnt, uint32_t *src_caps, uint32_t *rdo,
- uint32_t *curr_limit, uint32_t *supply_voltage)
+int pd_choose_voltage_common(int cnt, uint32_t *src_caps, uint32_t *rdo,
+ uint32_t *curr_limit, uint32_t *supply_voltage,
+ int choose_min)
{
int i;
int sel_mv;
@@ -65,6 +66,12 @@ int pd_choose_voltage(int cnt, uint32_t *src_caps, uint32_t *rdo,
max_uw = uw;
sel_mv = mv;
}
+ /*
+ * Choose the first entry if seaching for minimum, which will
+ * always be vSafe5V.
+ */
+ if (choose_min)
+ break;
}
if (max_i < 0)
return -EC_ERROR_UNKNOWN;
@@ -80,7 +87,12 @@ int pd_choose_voltage(int cnt, uint32_t *src_caps, uint32_t *rdo,
max_i, sel_mv/1000, max);
} else {
int ma = 10 * (src_caps[max_i] & 0x3FF);
- max = MIN(ma, MAX_CURRENT_MA);
+ /*
+ * If we're choosing the minimum charge mode, limit our current
+ * to what we can set with ilim PWM (500mA)
+ */
+ max = MIN(ma, choose_min ? CONFIG_CHARGER_INPUT_CURRENT :
+ MAX_CURRENT_MA);
flags = (max * sel_mv) < (1000 * OPERATING_POWER_MW) ?
RDO_CAP_MISMATCH : 0;
max_ma = max;
@@ -98,6 +110,20 @@ int pd_choose_voltage(int cnt, uint32_t *src_caps, uint32_t *rdo,
return EC_SUCCESS;
}
+int pd_choose_voltage_min(int cnt, uint32_t *src_caps, uint32_t *rdo,
+ uint32_t *curr_limit, uint32_t *supply_voltage)
+{
+ return pd_choose_voltage_common(cnt, src_caps, rdo, curr_limit,
+ supply_voltage, 1);
+}
+
+int pd_choose_voltage(int cnt, uint32_t *src_caps, uint32_t *rdo,
+ uint32_t *curr_limit, uint32_t *supply_voltage)
+{
+ return pd_choose_voltage_common(cnt, src_caps, rdo, curr_limit,
+ supply_voltage, 0);
+}
+
void pd_set_max_voltage(unsigned mv)
{
max_mv = mv;
diff --git a/common/charge_manager.c b/common/charge_manager.c
index c884c05500..3cd3db2280 100644
--- a/common/charge_manager.c
+++ b/common/charge_manager.c
@@ -17,6 +17,12 @@
static struct charge_port_info available_charge[CHARGE_SUPPLIER_COUNT]
[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.
+ */
+static int charge_ceil[PD_PORT_COUNT];
+
/* Store current state of port enable / charge current. */
static int charge_port = CHARGE_PORT_NONE;
static int charge_current = CHARGE_CURRENT_UNINITIALIZED;
@@ -30,13 +36,15 @@ static void charge_manager_init(void)
{
int i, j;
- for (i = 0; i < CHARGE_SUPPLIER_COUNT; ++i)
- for (j = 0; j < PD_PORT_COUNT; ++j) {
- available_charge[i][j].current =
+ for (i = 0; i < PD_PORT_COUNT; ++i) {
+ for (j = 0; j < CHARGE_SUPPLIER_COUNT; ++j) {
+ available_charge[j][i].current =
CHARGE_CURRENT_UNINITIALIZED;
- available_charge[i][j].voltage =
+ available_charge[j][i].voltage =
CHARGE_VOLTAGE_UNINITIALIZED;
}
+ charge_ceil[i] = CHARGE_CEIL_NONE;
+ }
}
DECLARE_HOOK(HOOK_INIT, charge_manager_init, HOOK_PRIO_DEFAULT-1);
@@ -73,7 +81,7 @@ static void charge_manager_refresh(void)
{
int new_supplier = CHARGE_SUPPLIER_NONE;
int new_port = CHARGE_PORT_NONE;
- int new_charge_current, new_charge_voltage, i, j;
+ int new_charge_current, new_charge_voltage, i, j, old_port;
/*
* Charge supplier selection logic:
@@ -103,11 +111,16 @@ static void charge_manager_refresh(void)
else {
new_charge_current =
available_charge[new_supplier][new_port].current;
+ /* Enforce port charge ceiling. */
+ if (charge_ceil[new_port] != CHARGE_CEIL_NONE &&
+ charge_ceil[new_port] < new_charge_current)
+ new_charge_current = charge_ceil[new_port];
+
new_charge_voltage =
available_charge[new_supplier][new_port].voltage;
}
- /* Change the charge limit + charge port if changed. */
+ /* Change the charge limit + charge port if modified. */
if (new_port != charge_port || new_charge_current != charge_current) {
CPRINTS("New charge limit: supplier %d port %d current %d "
"voltage %d", new_supplier, new_port,
@@ -117,7 +130,13 @@ static void charge_manager_refresh(void)
charge_current = new_charge_current;
charge_supplier = new_supplier;
+ old_port = charge_port;
charge_port = new_port;
+
+ if (new_port != CHARGE_PORT_NONE)
+ pd_set_new_power_request(new_port);
+ if (old_port != CHARGE_PORT_NONE)
+ pd_set_new_power_request(old_port);
}
}
DECLARE_DEFERRED(charge_manager_refresh);
@@ -126,27 +145,21 @@ DECLARE_DEFERRED(charge_manager_refresh);
* Update available charge for a given port / supplier.
*
* @param supplier Charge supplier to update.
- * @param charge_port Charge port to update.
+ * @param port Charge port to update.
* @param charge Charge port current / voltage.
*/
void charge_manager_update(int supplier,
- int charge_port,
+ int port,
struct charge_port_info *charge)
{
- if (supplier < 0 || supplier >= CHARGE_SUPPLIER_COUNT) {
- CPRINTS("Invalid charge supplier: %d", supplier);
- return;
- }
+ ASSERT(supplier >= 0 && supplier < CHARGE_SUPPLIER_COUNT);
+ ASSERT(port >= 0 && port < PD_PORT_COUNT);
/* Update charge table if needed. */
- if (available_charge[supplier][charge_port].current !=
- charge->current ||
- available_charge[supplier][charge_port].voltage !=
- charge->voltage) {
- available_charge[supplier][charge_port].current =
- charge->current;
- available_charge[supplier][charge_port].voltage =
- charge->voltage;
+ if (available_charge[supplier][port].current != charge->current ||
+ available_charge[supplier][port].voltage != charge->voltage) {
+ available_charge[supplier][port].current = charge->current;
+ available_charge[supplier][port].voltage = charge->voltage;
/*
* Don't call charge_manager_refresh unless all ports +
@@ -159,6 +172,28 @@ void charge_manager_update(int supplier,
}
}
+/**
+ * Update charge ceiling for a given port.
+ *
+ * @param port Charge port to update.
+ * @param ceil Charge ceiling (mA).
+ */
+void charge_manager_set_ceil(int port, int ceil)
+{
+ ASSERT(port >= 0 && port < PD_PORT_COUNT);
+
+ if (charge_ceil[port] != ceil) {
+ charge_ceil[port] = ceil;
+ if (port == charge_port && charge_manager_is_seeded())
+ hook_call_deferred(charge_manager_refresh, 0);
+ }
+}
+
+int charge_manager_get_active_charge_port(void)
+{
+ return charge_port;
+}
+
static int hc_pd_power_info(struct host_cmd_handler_args *args)
{
const struct ec_params_usb_pd_power_info *p = args->params;
diff --git a/common/usb_pd_protocol.c b/common/usb_pd_protocol.c
index 4bfd8b69e0..0f791c2841 100644
--- a/common/usb_pd_protocol.c
+++ b/common/usb_pd_protocol.c
@@ -212,6 +212,11 @@ enum vdm_states {
VDM_STATE_BUSY = 2,
};
+enum pd_request_types {
+ PD_REQUEST_MIN,
+ PD_REQUEST_MAX,
+};
+
#ifdef CONFIG_USB_PD_DUAL_ROLE
/* Port dual-role state */
enum pd_dual_role_states drp_state = PD_DRP_TOGGLE_OFF;
@@ -219,8 +224,6 @@ enum pd_dual_role_states drp_state = PD_DRP_TOGGLE_OFF;
/* Last received source cap */
static uint32_t pd_src_caps[PD_PORT_COUNT][PDO_MAX_OBJECTS];
static int pd_src_cap_cnt[PD_PORT_COUNT];
-
-static int new_power_request;
#endif
static struct pd_protocol {
@@ -251,6 +254,12 @@ static struct pd_protocol {
/* Current limit / voltage based on the last request message */
uint32_t curr_limit;
uint32_t supply_voltage;
+ /* Signal charging update that affects the port */
+ int new_power_request;
+#ifdef CONFIG_CHARGE_MANAGER
+ /* Track last requested charge type (min / max) */
+ enum pd_request_types last_charge_request;
+#endif
#endif
/* PD state for Vendor Defined Messages */
@@ -700,6 +709,7 @@ static void execute_hard_reset(int port)
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);
#endif /* CONFIG_CHARGE_MANAGER */
#else
set_state(port, PD_STATE_SRC_DISCONNECTED);
@@ -742,7 +752,7 @@ static void pd_store_src_cap(int port, int cnt, uint32_t *src_caps)
pd_src_caps[port][i] = *src_caps++;
}
-static void pd_send_request_msg(int port)
+static void pd_send_request_msg(int port, enum pd_request_types request)
{
uint32_t rdo, curr_limit, supply_voltage;
int res;
@@ -751,6 +761,36 @@ static void pd_send_request_msg(int port)
res = pd_choose_voltage(pd_src_cap_cnt[port], pd_src_caps[port], &rdo,
&curr_limit, &supply_voltage);
if (res == EC_SUCCESS) {
+#ifdef CONFIG_CHARGE_MANAGER
+ /* Set max. limit, but apply 500mA ceiling */
+ charge_manager_set_ceil(port, PD_MIN_MA);
+ pd_set_input_current_limit(port,
+ curr_limit,
+ supply_voltage);
+ /* Negotiate for Vsafe5V, if requested */
+ if (request == PD_REQUEST_MIN) {
+ res = pd_choose_voltage_min(pd_src_cap_cnt[port],
+ pd_src_caps[port],
+ &rdo,
+ &curr_limit,
+ &supply_voltage);
+ if (res != EC_SUCCESS)
+ /*
+ * Successfully requested max. voltage mode,
+ * but unable to request min. for some reason.
+ * We have no choice but to negotiate for max.
+ * Update last_charge_request accordingly.
+ */
+ request = PD_REQUEST_MAX;
+ }
+ /*
+ * The request message may be later rejected, in which case
+ * last_charge_request may not reflect reality.
+ * TODO(shawnn): Handle last_charge_request correctly for this
+ * case. crosbug.com/p/33692.
+ */
+ pd[port].last_charge_request = request;
+#endif
pd[port].curr_limit = curr_limit;
pd[port].supply_voltage = supply_voltage;
/* src cap 0 should be fixed PDO, get dualrole power capable */
@@ -788,7 +828,7 @@ static void handle_data_request(int port, uint16_t head,
|| (pd[port].task_state == PD_STATE_SNK_TRANSITION)
|| (pd[port].task_state == PD_STATE_SNK_READY)) {
pd_store_src_cap(port, cnt, payload);
- pd_send_request_msg(port);
+ pd_send_request_msg(port, PD_REQUEST_MIN);
}
break;
#endif /* CONFIG_USB_PD_DUAL_ROLE */
@@ -861,8 +901,13 @@ static void handle_ctrl_request(int port, uint16_t head,
set_state(port, PD_STATE_HARD_RESET);
} else if (pd[port].power_role == PD_ROLE_SINK) {
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);
+#else
pd_set_input_current_limit(port, pd[port].curr_limit,
pd[port].supply_voltage);
+#endif
}
break;
case PD_CTRL_REJECT:
@@ -1315,6 +1360,14 @@ static inline int get_typec_current_limit(int cc_voltage)
return charge;
}
+
+/**
+ * Signal power request to indicate a charger update that affects the port.
+ */
+void pd_set_new_power_request(int port)
+{
+ pd[port].new_power_request = 1;
+}
#endif /* CONFIG_CHARGE_MANAGER */
void pd_task(void)
@@ -1694,7 +1747,6 @@ void pd_task(void)
break;
case PD_STATE_SNK_REQUESTED:
/* Ensure the power supply actually becomes ready */
- pd_set_input_current_limit(port, PD_MIN_MA, PD_MIN_MV);
set_state(port, PD_STATE_SNK_TRANSITION);
hard_reset_count = 0;
timeout = 10 * MSEC;
@@ -1716,9 +1768,20 @@ void pd_task(void)
}
/* we have power, check vitals from time to time */
- if (new_power_request) {
- pd_send_request_msg(port);
- new_power_request = 0;
+ if (pd[port].new_power_request) {
+ pd[port].new_power_request = 0;
+#ifdef CONFIG_CHARGE_MANAGER
+ if (charge_manager_get_active_charge_port()
+ != port && pd[port].last_charge_request
+ == PD_REQUEST_MAX)
+ pd_send_request_msg(port,
+ PD_REQUEST_MIN);
+ else if (charge_manager_get_active_charge_port()
+ == port && pd[port].last_charge_request
+ == PD_REQUEST_MIN)
+#endif
+ pd_send_request_msg(port,
+ PD_REQUEST_MAX);
}
timeout = 100*MSEC;
break;
@@ -1738,6 +1801,7 @@ 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);
#endif
set_state(port, PD_STATE_SNK_SWAP_SRC_DISABLE);
timeout = 10*MSEC;
@@ -1862,6 +1926,7 @@ 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);
#endif
/* set timeout small to reconnect fast */
timeout = 5*MSEC;
@@ -2026,7 +2091,7 @@ void pd_request_source_voltage(int port, int mv)
if (pd[port].task_state == PD_STATE_SNK_READY) {
/* Set flag to send new power request in pd_task */
- new_power_request = 1;
+ pd[port].new_power_request = 1;
} else {
pd[port].power_role = PD_ROLE_SINK;
pd_set_host_mode(port, 0);
diff --git a/include/charge_manager.h b/include/charge_manager.h
index 44ed605a23..d32436ab81 100644
--- a/include/charge_manager.h
+++ b/include/charge_manager.h
@@ -9,6 +9,7 @@
/* Charge port that indicates no active port */
#define CHARGE_SUPPLIER_NONE -1
#define CHARGE_PORT_NONE -1
+#define CHARGE_CEIL_NONE -1
/* Initial charge state */
#define CHARGE_CURRENT_UNINITIALIZED -1
@@ -27,4 +28,10 @@ void charge_manager_update(int supplier,
int charge_port,
struct charge_port_info *charge);
+/* Update charge ceiling for a given port */
+void charge_manager_set_ceil(int port, int ceil);
+
+/* Returns the current active charge port, as determined by charge manager */
+int charge_manager_get_active_charge_port(void);
+
#endif /* __CHARGE_MANAGER_H */
diff --git a/include/usb_pd.h b/include/usb_pd.h
index c5b90b4872..a2c92792b5 100644
--- a/include/usb_pd.h
+++ b/include/usb_pd.h
@@ -620,7 +620,8 @@ enum pd_data_msg_type {
/* --- Policy layer functions --- */
/**
- * Decide which voltage to use from the source capabilities.
+ * Decide which voltage to use from the source capabilities - prefer the
+ * mode which delivers the maximum allowable power.
*
* @param cnt the number of Power Data Objects.
* @param src_caps Power Data Objects representing the source capabilities.
@@ -633,6 +634,20 @@ int pd_choose_voltage(int cnt, uint32_t *src_caps, uint32_t *rdo,
uint32_t *curr_limit, uint32_t *supply_voltage);
/**
+ * Decide which voltage to use from the source capabilities - prefer the
+ * mode which delivers the minimum allowable power.
+ *
+ * @param cnt the number of Power Data Objects.
+ * @param src_caps Power Data Objects representing the source capabilities.
+ * @param rdo requested Request Data Object.
+ * @param curr_limit selected current limit (stored on success)
+ * @param supply_voltage selected supply voltage (stored on success)
+ * @return <0 if invalid, else EC_SUCCESS
+ */
+int pd_choose_voltage_min(int cnt, uint32_t *src_caps, uint32_t *rdo,
+ uint32_t *curr_limit, uint32_t *supply_voltage);
+
+/**
* Put a cap on the max voltage requested as a sink.
* @param mv maximum voltage in millivolts.
*/
@@ -1027,4 +1042,11 @@ void pd_ping_enable(int port, int enable);
/* Issue PD soft reset */
void pd_soft_reset(void);
+
+/**
+ * Signal power request to indicate a charger update that affects the port.
+ *
+ * @param port USB-C port number
+ */
+void pd_set_new_power_request(int port);
#endif /* __USB_PD_H */