diff options
-rw-r--r-- | board/servo_v4/board.h | 5 | ||||
-rw-r--r-- | board/servo_v4/usb_pd_config.h | 100 | ||||
-rw-r--r-- | board/servo_v4/usb_pd_policy.c | 234 |
3 files changed, 271 insertions, 68 deletions
diff --git a/board/servo_v4/board.h b/board/servo_v4/board.h index 52bd17941f..eb5de84dbd 100644 --- a/board/servo_v4/board.h +++ b/board/servo_v4/board.h @@ -80,16 +80,19 @@ #define CONFIG_USB_POWER_DELIVERY #define CONFIG_USB_PD_DTS #define CONFIG_USB_PD_DUAL_ROLE +#define CONFIG_USB_PD_DYNAMIC_SRC_CAP #define CONFIG_USB_PD_INTERNAL_COMP #define CONFIG_USB_PD_PORT_COUNT 2 #define CONFIG_USB_PD_TCPC #define CONFIG_USB_PD_TCPM_STUB +#undef CONFIG_USB_PD_PULLUP +#define CONFIG_USB_PD_PULLUP TYPEC_RP_USB /* Override PD_ROLE_DEFAULT in usb_pd.h */ #define PD_ROLE_DEFAULT(port) ((port) ? PD_ROLE_SOURCE : PD_ROLE_SINK) /* 3.0A Standard-current Rp */ -#define PD_SRC_VNC PD_SRC_DEF_VNC_MV +#define PD_SRC_VNC PD_SRC_3_0_VNC_MV #define PD_SRC_RD_THRESHOLD PD_SRC_DEF_RD_THRESH_MV /* diff --git a/board/servo_v4/usb_pd_config.h b/board/servo_v4/usb_pd_config.h index c9c21726e2..838ead71d7 100644 --- a/board/servo_v4/usb_pd_config.h +++ b/board/servo_v4/usb_pd_config.h @@ -8,6 +8,7 @@ #include "console.h" #include "gpio.h" #include "ec_commands.h" +#include "usb_pd_tcpm.h" /* USB Power delivery board configuration */ @@ -20,6 +21,8 @@ * C1 DUT * */ +#define CHG 0 +#define DUT 1 /* Timer selection for baseband PD communication */ #define TIM_CLOCK_PD_TX_CHG 16 @@ -217,6 +220,62 @@ static inline void pd_tx_init(void) gpio_config_module(MODULE_USB_PD, 1); } +static inline int pd_set_rp_rd(int port, int cc_pull, int rp_value) +{ + /* Set Rd for CC1/CC2 to High-Z. */ + gpio_set_flags(GPIO_USB_DUT_CC1_RD, GPIO_INPUT); + gpio_set_flags(GPIO_USB_DUT_CC2_RD, GPIO_INPUT); + /* Set Rp for CC1/CC2 to High-Z. */ + gpio_set_flags(GPIO_USB_DUT_CC1_RP3A0, GPIO_INPUT); + gpio_set_flags(GPIO_USB_DUT_CC2_RP3A0, GPIO_INPUT); + gpio_set_flags(GPIO_USB_DUT_CC1_RP1A5, GPIO_INPUT); + gpio_set_flags(GPIO_USB_DUT_CC2_RP1A5, GPIO_INPUT); + gpio_set_flags(GPIO_USB_DUT_CC1_RPUSB, GPIO_INPUT); + gpio_set_flags(GPIO_USB_DUT_CC2_RPUSB, GPIO_INPUT); + + if (cc_pull == TYPEC_CC_RP) { + /* + * CC values for Debug sources (DTS) + * + * Source type Mode of Operation CC1 CC2 + * --------------------------------------------- + * DTS Default USB Power Rp3A0 Rp1A5 + * DTS USB-C @ 1.5 A Rp1A5 RpUSB + * DTS USB-C @ 3 A Rp3A0 RpUSB + */ + switch (rp_value) { + case TYPEC_RP_USB: + gpio_set_flags(GPIO_USB_DUT_CC1_RP3A0, GPIO_OUT_HIGH); + gpio_set_flags(GPIO_USB_DUT_CC2_RP1A5, GPIO_OUT_HIGH); + break; + case TYPEC_RP_1A5: + gpio_set_flags(GPIO_USB_DUT_CC1_RP1A5, GPIO_OUT_HIGH); + gpio_set_flags(GPIO_USB_DUT_CC2_RPUSB, GPIO_OUT_HIGH); + break; + case TYPEC_RP_3A0: + gpio_set_flags(GPIO_USB_DUT_CC1_RP3A0, GPIO_OUT_HIGH); + gpio_set_flags(GPIO_USB_DUT_CC2_RPUSB, GPIO_OUT_HIGH); + break; + case TYPEC_RP_RESERVED: + /* + * This case can be used to force a detach event since + * all values are set to inputs above. Nothing else to + * set. + */ + break; + default: + return EC_ERROR_INVAL; + } + + } else if (cc_pull == TYPEC_CC_RD) { + /* DTS in SNK mode presents Rd on CC1/CC2 */ + gpio_set_flags(GPIO_USB_DUT_CC1_RD, GPIO_OUT_LOW); + gpio_set_flags(GPIO_USB_DUT_CC2_RD, GPIO_OUT_LOW); + } + + return EC_SUCCESS; +} + static inline void pd_set_host_mode(int port, int enable) { /* @@ -231,42 +290,21 @@ static inline void pd_set_host_mode(int port, int enable) /* * Servo_v4 in SRC mode acts as a DTS (debug test * accessory) and needs to present Rp on both CC - * lines. In order to support orientation detection, the - * values of Rp1/Rp2 need to asymmetric with Rp1 > Rp2. + * lines. In order to support orientation detection, and + * advertise the correct TypeC current level, the + * values of Rp1/Rp2 need to asymmetric with Rp1 > Rp2. This + * function is called without a specified Rp value so assume the + * servo_v4 default of USB level current. If a higher current + * can be supported, then the Rp value will get adjusted when + * VBUS is enabled. */ + pd_set_rp_rd(port, TYPEC_CC_RP, TYPEC_RP_USB); - /* Set Rd for CC1/CC2 to High-Z. */ - gpio_set_flags(GPIO_USB_DUT_CC1_RD, GPIO_INPUT); - gpio_set_flags(GPIO_USB_DUT_CC2_RD, GPIO_INPUT); - - /* - * TODO(crosbug.com/p/60794): Currently always advertising - * 1.5A. If CHG port is attached then can advertise 3A - * instead. Additionally, if CHG port is not attached and having - * to use host provided vbus, can 1.5A be advertised? - */ - /* Pull up for host mode */ - gpio_set_flags(GPIO_USB_DUT_CC1_RP1A5, GPIO_OUTPUT); - gpio_set_level(GPIO_USB_DUT_CC1_RP1A5, 1); - gpio_set_flags(GPIO_USB_DUT_CC2_RPUSB, GPIO_OUTPUT); - gpio_set_level(GPIO_USB_DUT_CC2_RPUSB, 1); - /* Set TX Hi-Z */ gpio_set_flags(GPIO_USB_DUT_CC1_TX_DATA, GPIO_INPUT); gpio_set_flags(GPIO_USB_DUT_CC2_TX_DATA, GPIO_INPUT); } else { - /* Set Rp for CC1/CC2 to High-Z. */ - gpio_set_flags(GPIO_USB_DUT_CC1_RP3A0, GPIO_INPUT); - gpio_set_flags(GPIO_USB_DUT_CC2_RP3A0, GPIO_INPUT); - gpio_set_flags(GPIO_USB_DUT_CC1_RP1A5, GPIO_INPUT); - gpio_set_flags(GPIO_USB_DUT_CC2_RP1A5, GPIO_INPUT); - gpio_set_flags(GPIO_USB_DUT_CC1_RPUSB, GPIO_INPUT); - gpio_set_flags(GPIO_USB_DUT_CC2_RPUSB, GPIO_INPUT); - - /* DTS in SNK mode presents Rd on CC1/CC2 */ - gpio_set_flags(GPIO_USB_DUT_CC1_RD, GPIO_OUTPUT); - gpio_set_flags(GPIO_USB_DUT_CC2_RD, GPIO_OUTPUT); - gpio_set_level(GPIO_USB_DUT_CC1_RD, 0); - gpio_set_level(GPIO_USB_DUT_CC2_RD, 0); + /* Select Rd, the Rp value is a don't care */ + pd_set_rp_rd(port, TYPEC_CC_RD, TYPEC_RP_RESERVED); } } 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; } |