diff options
-rw-r--r-- | board/samus_pd/board.h | 2 | ||||
-rw-r--r-- | board/samus_pd/usb_pd_policy.c | 32 | ||||
-rw-r--r-- | common/charge_manager.c | 75 | ||||
-rw-r--r-- | common/usb_pd_protocol.c | 83 | ||||
-rw-r--r-- | include/charge_manager.h | 7 | ||||
-rw-r--r-- | include/usb_pd.h | 24 |
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 */ |