diff options
Diffstat (limited to 'board/servo_v4/usb_pd_policy.c')
-rw-r--r-- | board/servo_v4/usb_pd_policy.c | 234 |
1 files changed, 198 insertions, 36 deletions
diff --git a/board/servo_v4/usb_pd_policy.c b/board/servo_v4/usb_pd_policy.c index 48cce3519b..9ba5356791 100644 --- a/board/servo_v4/usb_pd_policy.c +++ b/board/servo_v4/usb_pd_policy.c @@ -14,34 +14,180 @@ #include "registers.h" #include "system.h" #include "task.h" +#include "tcpm.h" #include "timer.h" #include "util.h" #include "usb_mux.h" #include "usb_pd.h" +#include "usb_pd_config.h" +#include "usb_pd_tcpm.h" #define CPRINTF(format, args...) cprintf(CC_USBPD, format, ## args) #define CPRINTS(format, args...) cprints(CC_USBPD, format, ## args) -/* Define typical operating power and max power */ -/*#define OPERATING_POWER_MW 15000 */ -/*#define MAX_POWER_MW 60000 */ -/*#define MAX_CURRENT_MA 3000 */ +#define DUT_PDO_FIXED_FLAGS (PDO_FIXED_DUAL_ROLE | PDO_FIXED_DATA_SWAP |\ + PDO_FIXED_COMM_CAP) -#define PDO_FIXED_FLAGS (PDO_FIXED_DUAL_ROLE | PDO_FIXED_DATA_SWAP |\ - PDO_FIXED_COMM_CAP) +#define CHG_PDO_FIXED_FLAGS (PDO_FIXED_DATA_SWAP) -const uint32_t pd_src_pdo[] = { - PDO_FIXED(5000, 900, PDO_FIXED_FLAGS), -}; -const int pd_src_pdo_cnt = ARRAY_SIZE(pd_src_pdo); +#define VBUS_UNCHANGED(curr, pend, new) (curr == new && pend == new) +/* + * Dynamic PDO that reflects capabilities present on the CHG port. Allow for two + * entries so that can offer greater than 5V charging. The 1st entry will be + * fixed 5V, but its current value may change based on the CHG port vbus + * info. The 2nd entry is used for when offering vbus greater than 5V. + */ +static uint32_t pd_src_chg_pdo[2]; +static uint8_t chg_pdo_cnt; +static const uint32_t pd_src_host_pdo[] = { + PDO_FIXED(5000, 500, DUT_PDO_FIXED_FLAGS), +}; const uint32_t pd_snk_pdo[] = { - PDO_FIXED(5000, 500, PDO_FIXED_FLAGS), + PDO_FIXED(5000, 500, CHG_PDO_FIXED_FLAGS), PDO_BATT(4750, 21000, 15000), PDO_VAR(4750, 21000, 3000), }; const int pd_snk_pdo_cnt = ARRAY_SIZE(pd_snk_pdo); +struct vbus_prop { + int mv; + int ma; +}; +static struct vbus_prop vbus[CONFIG_USB_PD_PORT_COUNT]; +static struct vbus_prop vbus_pend; +static uint8_t vbus_rp = TYPEC_RP_RESERVED; + +static void board_manage_dut_port(void) +{ + int rp; + + /* + * This function is called by the CHG port whenever there has been a + * change in its vbus voltage or current. That change may necessitate + * that the DUT port present a different Rp value or renogiate its PD + * contract if it is connected. + */ + + /* Assume the default value of Rp */ + rp = TYPEC_RP_USB; + if (vbus[CHG].mv == 5000) { + /* Only advertise higher current via Rp if vbus == 5V */ + if (vbus[CHG].ma >= 3000) + /* CHG port is connected and DUt can advertise 3A */ + rp = TYPEC_RP_3A0; + else if (vbus[CHG].ma >= 1500) + rp = TYPEC_RP_1A5; + } + + /* Check if Rp setting needs to change from current value */ + if (vbus_rp != rp) { + /* Present new Rp value */ + tcpm_select_rp_value(DUT, rp); + /* Save new Rp value for DUT port */ + vbus_rp = rp; + } + + /* + * If CHG vbus voltage/current doesn't match the DUT port value, then + * update the contract. If DUT port is not in the correct state, then + * this call does nothing. + */ + if (vbus[CHG].mv != vbus[DUT].mv || vbus[CHG].ma != vbus[DUT].ma) + /* + * Update PD contract to reflect new available CHG + * voltage/current values. + */ + pd_update_contract(DUT); +} + +static void board_manage_chg_port(void) +{ + /* Update the voltage/current values for CHG port */ + vbus[CHG].mv = vbus_pend.mv; + vbus[CHG].ma = vbus_pend.ma; + + /* + * CHG Vbus has changed states, update PDO that reflects CHG port + * state + */ + if (!vbus[CHG].mv) { + /* CHG Vbus has dropped, so always source DUT Vbus from host */ + gpio_set_level(GPIO_HOST_OR_CHG_CTL, 0); + chg_pdo_cnt = 0; + } else { + int v5_ma = vbus[CHG].mv > 5000 ? 500 : vbus[CHG].ma; + + pd_src_chg_pdo[0] = PDO_FIXED_VOLT(5000) | + PDO_FIXED_CURR(v5_ma) | DUT_PDO_FIXED_FLAGS | + PDO_FIXED_EXTERNAL; + + chg_pdo_cnt = 1; + if (vbus[CHG].mv > 5000) { + /* + * CHG vbus is > 5V so need an entry for vSafe5V and an + * entry that reflects CHG VBUS + */ + pd_src_chg_pdo[1] = PDO_FIXED_VOLT(vbus[CHG].mv) | + PDO_FIXED_CURR(vbus[CHG].ma) | + DUT_PDO_FIXED_FLAGS | PDO_FIXED_EXTERNAL; + chg_pdo_cnt = 2; + } + } + + /* Call DUT port manager to update Rp and possible PD contract */ + board_manage_dut_port(); +} +DECLARE_DEFERRED(board_manage_chg_port); + +static void board_update_chg_vbus(int max_ma, int vbus_mv) +{ + /* + * Determine if vbus from CHG port has changed values and if the current + * state of CHG vbus is on or off. If the change is on, then schedule a + * deferred callback. If the change is off, then act immediately. + */ + if (VBUS_UNCHANGED(vbus[CHG].mv, vbus_pend.mv, vbus_mv) && + VBUS_UNCHANGED(vbus[CHG].ma, vbus_pend.ma, max_ma)) + /* No change in CHG VBUS detected, nothing else to do. */ + return; + + /* CHG port voltage and current values are now pending */ + vbus_pend.mv = vbus_mv; + vbus_pend.ma = max_ma; + + if (vbus_mv) + /* Wait enough time for PD contract to be established */ + hook_call_deferred(&board_manage_chg_port_data, + PD_T_SINK_WAIT_CAP * 3); + else + /* Update CHG port status now since vbus is off */ + hook_call_deferred(&board_manage_chg_port_data, 0); +} + +int board_select_rp_value(int port, int rp) +{ + return pd_set_rp_rd(port, TYPEC_CC_RP, rp); +} + +int charge_manager_get_source_pdo(const uint32_t **src_pdo) +{ + int pdo_cnt; + /* + * If CHG is providing VBUS, then advertise what's available on the CHG + * port, otherwise used the fixed value that matches host capabilities. + */ + if (vbus[CHG].mv) { + *src_pdo = pd_src_chg_pdo; + pdo_cnt = chg_pdo_cnt; + } else { + *src_pdo = pd_src_host_pdo; + pdo_cnt = ARRAY_SIZE(pd_src_host_pdo); + } + + return pdo_cnt; +} + int pd_is_valid_input_voltage(int mv) { /* Any voltage less than the max is allowed */ @@ -51,58 +197,73 @@ int pd_is_valid_input_voltage(int mv) void pd_transition_voltage(int idx) { /* - * TODO(crosbug.com/p/60794): Most likely this function is a don't care - * for servo_v4 since VBUS provided to the DUT port has just an on/off - * control. For now leave it as a no-op. + * Up to this point, VBUS will have been supplied by host. If + * vbus[CHG].mv is set, then that means the CHG port is in a steady + * state condition and its voltage/current values have been communicated + * to the DUT in the SRC_CAP message. If CHG vbus > 5V then 2 + * chg_src_pdo entries will have been sent. Only allow pass through + * charging from CHG vbus if the pdo idx requested by the DUT matches + * the number of chg_src_pdo entries. + * */ + if (vbus[CHG].mv && idx == chg_pdo_cnt) { + gpio_set_level(GPIO_HOST_OR_CHG_CTL, 1); + /* + * VBUS being provided by CHG port now. Update DUT port's vbus + * info with the CHG port's values. + */ + vbus[DUT].mv = vbus[CHG].mv; + vbus[DUT].ma = vbus[CHG].ma; + } } int pd_set_power_supply_ready(int port) { /* Port 0 can never provide vbus. */ - if (!port) + if (port == CHG) return EC_ERROR_INVAL; - /* - * TODO(crosbug.com/p/60794): For now always assume VBUS is supplied by - * host. No support yet for using CHG VBUS passthru mode. - */ - - /* - * Select Host as source for VBUS. - * To select host, set GPIO_HOST_OR_CHG_CTL low. To select CHG as VBUS - * source, then set GPIO_HOST_OR_CHG_CTL high. - */ + /* Only ever allow host vbus at this point */ gpio_set_level(GPIO_HOST_OR_CHG_CTL, 0); - /* Enable VBUS from the source selected above. */ + /* Enable VBUS */ gpio_set_level(GPIO_DUT_CHG_EN, 1); + /* Host vbus is always 5V/500mA */ + vbus[DUT].mv = 5000; + vbus[DUT].ma = 500; + return EC_SUCCESS; /* we are ready */ } void pd_power_supply_reset(int port) { + /* Port 0 can never provide vbus. */ + if (port == CHG) + return; + /* Disable VBUS */ gpio_set_level(GPIO_DUT_CHG_EN, 0); + /* Set default VBUS source to Host */ + gpio_set_level(GPIO_HOST_OR_CHG_CTL, 0); + + /* Host vbus is always 5V/500mA */ + vbus[DUT].mv = 0; + vbus[DUT].ma = 0; } void pd_set_input_current_limit(int port, uint32_t max_ma, uint32_t supply_voltage) { - /* - * TODO(crosbug.com/p/60794): Placeholder for now so that can compile - * with USB PD support. - */ + if (port == CHG) + board_update_chg_vbus(max_ma, supply_voltage); } void typec_set_input_current_limit(int port, uint32_t max_ma, uint32_t supply_voltage) { - /* - * TODO(crosbug.com/p/60794): Placeholder for now so that can compile - * with USB PD support. - */ + if (port == CHG) + board_update_chg_vbus(max_ma, supply_voltage); } int pd_snk_is_vbus_provided(int port) @@ -121,8 +282,9 @@ int pd_check_power_swap(int port) { /* * TODO(crosbug.com/p/60792): CHG port can't do a power swap as it's SNK - * only. DUT port should be able to support a power role swap, but VBUS - * will need to be present. For now, don't allow swaps on either port. + * only. Don't allow DUT port to accept a power role swap request. More + * support still needs to be added so that servo_v4 DUT port behaves + * properly when acting as a SNK device. */ return 0; } |