summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-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 */