diff options
Diffstat (limited to 'board/servo_v4/usb_pd_policy.c')
-rw-r--r-- | board/servo_v4/usb_pd_policy.c | 206 |
1 files changed, 149 insertions, 57 deletions
diff --git a/board/servo_v4/usb_pd_policy.c b/board/servo_v4/usb_pd_policy.c index 0608060ad9..06462405f8 100644 --- a/board/servo_v4/usb_pd_policy.c +++ b/board/servo_v4/usb_pd_policy.c @@ -44,9 +44,7 @@ static const uint16_t pd_src_voltages_mv[] = { }; static uint32_t pd_src_chg_pdo[ARRAY_SIZE(pd_src_voltages_mv)]; 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, CHG_PDO_FIXED_FLAGS), PDO_BATT(4750, 21000, 15000), @@ -62,7 +60,14 @@ static struct vbus_prop vbus[CONFIG_USB_PD_PORT_COUNT]; static int active_charge_port = CHARGE_PORT_NONE; static enum charge_supplier active_charge_supplier; static uint8_t vbus_rp = TYPEC_RP_RESERVED; + +/* + * DTS mode: enabled connects resistors to both CC line to activate cr50, + * disabled connects to one only as in the standard USBC cable. + */ static int disable_dts_mode; +/* Do we allow charge through by policy? */ +static int allow_src_mode = 1; /* Voltage thresholds for no connect in DTS mode */ static int pd_src_vnc_dts[TYPEC_RP_RESERVED][2] = { @@ -107,9 +112,26 @@ static int charge_port_is_active(void) return active_charge_port == CHG && vbus[CHG].mv > 0; } +static void dut_allow_charge(void) +{ + /* + * Update to charge enable if charger still present and not + * already charging. + */ + if (charge_port_is_active() && allow_src_mode && + pd_get_dual_role(DUT) != PD_DRP_FORCE_SOURCE) { + CPRINTS("Enable DUT charge through"); + pd_set_dual_role(DUT, PD_DRP_FORCE_SOURCE); + pd_config_init(DUT, PD_ROLE_SOURCE); + pd_update_contract(DUT); + } +} +DECLARE_DEFERRED(dut_allow_charge); + static void board_manage_dut_port(void) { - int rp; + enum pd_dual_role_states allowed_role; + enum pd_dual_role_states current_role; /* * This function is called by the CHG port whenever there has been a @@ -118,21 +140,28 @@ static void board_manage_dut_port(void) * contract if it is connected. */ - /* Assume the default value of Rp */ - rp = TYPEC_RP_USB; - if (vbus[CHG].mv == PD_MIN_MV && charge_port_is_active()) { - /* 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; - } + /* Assume the default value of Rd */ + allowed_role = PD_DRP_FORCE_SINK; + + /* If VBUS charge through is available, mark as such. */ + if (charge_port_is_active() && allow_src_mode) + allowed_role = PD_DRP_FORCE_SOURCE; - /* Check if Rp setting needs to change from current value */ - if (vbus_rp != rp) - /* Present new Rp value */ - tcpm_select_rp_value(DUT, rp); + current_role = pd_get_dual_role(DUT); + if (current_role != allowed_role) { + /* Update role. */ + if (allowed_role == PD_DRP_FORCE_SINK) { + /* We've lost charge through. Disable VBUS. */ + gpio_set_level(GPIO_DUT_CHG_EN, 0); + + /* Mark as SNK only. */ + pd_set_dual_role(DUT, PD_DRP_FORCE_SINK); + pd_config_init(DUT, PD_ROLE_SINK); + } else { + /* Allow charge through after PD negotiate. */ + hook_call_deferred(&dut_allow_charge_data, 2000 * MSEC); + } + } /* * Update PD contract to reflect new available CHG @@ -151,7 +180,7 @@ static void update_ports(void) * state */ if (!charge_port_is_active()) { - /* CHG Vbus has dropped, so always source DUT Vbus from host */ + /* CHG Vbus has dropped, so become SNK. */ chg_pdo_cnt = 0; } else { /* Advertise the 'best' PDOs at various discrete voltages */ @@ -196,12 +225,6 @@ static void update_ports(void) /* Call DUT port manager to update Rp and possible PD contract */ board_manage_dut_port(); - - /* - * Supply VBUS from the CHG port if available. This may glitch VBUS - * on the DUT during switchover. - */ - gpio_set_level(GPIO_HOST_OR_CHG_CTL, chg_pdo_cnt > 0); } int board_set_active_charge_port(int charge_port) @@ -399,18 +422,15 @@ int board_select_rp_value(int port, int rp) int charge_manager_get_source_pdo(const uint32_t **src_pdo, const int port) { - int pdo_cnt; + int pdo_cnt = 0; /* * If CHG is providing VBUS, then advertise what's available on the CHG - * port, otherwise used the fixed value that matches host capabilities. + * port, otherwise we provide no power. */ if (charge_port_is_active()) { *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; @@ -463,20 +483,23 @@ int pd_set_power_supply_ready(int port) if (port == CHG) return EC_ERROR_INVAL; - /* Enable VBUS */ - gpio_set_level(GPIO_DUT_CHG_EN, 1); - if (charge_port_is_active()) { + /* Enable VBUS */ + gpio_set_level(GPIO_DUT_CHG_EN, 1); + if (vbus[CHG].mv != PD_MIN_MV) CPRINTS("ERROR, CHG port voltage %d != PD_MIN_MV", vbus[CHG].mv); vbus[DUT].mv = vbus[CHG].mv; vbus[DUT].ma = vbus[CHG].mv; + pd_set_dual_role(DUT, PD_DRP_FORCE_SOURCE); } else { - /* Host vbus is always 5V/500mA */ - vbus[DUT].mv = PD_MIN_MV; - vbus[DUT].ma = 500; + vbus[DUT].mv = 0; + vbus[DUT].ma = 0; + gpio_set_level(GPIO_DUT_CHG_EN, 0); + pd_set_dual_role(DUT, PD_DRP_FORCE_SINK); + return EC_ERROR_NOT_POWERED; } /* Enable CCD, if debuggable TS attached */ @@ -497,10 +520,6 @@ void pd_power_supply_reset(int port) /* Disable VBUS */ gpio_set_level(GPIO_DUT_CHG_EN, 0); - /* Host vbus is always 5V/500mA */ - vbus[DUT].mv = 0; - vbus[DUT].ma = 0; - /* DUT is lost, back to 5V limit on CHG */ pd_set_external_voltage_limit(CHG, PD_MIN_MV); } @@ -526,6 +545,14 @@ int pd_check_power_swap(int port) * SRC. Let servo_v4 have more control over its power role by always * rejecting power swap requests from the DUT. */ + + /* Port 0 can never provide vbus. */ + if (port == CHG) + return 0; + + if (pd_snk_is_vbus_provided(CHG)) + return 1; + return 0; } @@ -597,14 +624,91 @@ int pd_custom_vdm(int port, int cnt, uint32_t *payload, const struct svdm_amode_fx supported_modes[] = {}; const int supported_modes_cnt = ARRAY_SIZE(supported_modes); + +static void print_cc_mode(void) +{ + /* Get current CCD status */ + ccprintf("dts mode: %s\n", disable_dts_mode ? "off" : "on"); + ccprintf("chg mode: %s\n", + pd_get_dual_role(DUT) == PD_DRP_FORCE_SOURCE ? + "on" : "off"); + ccprintf("chg allowed: %s\n", allow_src_mode ? "on" : "off"); +} + + +static void do_cc(int disable_dts_new, int allow_src_new) +{ + if ((disable_dts_new != disable_dts_mode) || + (allow_src_new != allow_src_mode)) { + /* Force detach */ + pd_power_supply_reset(DUT); + /* Always set to 0 here so both CC lines are changed */ + disable_dts_mode = 0; + allow_src_mode = 0; + /* Remove Rp/Rd on both CC lines */ + board_select_rp_value(DUT, TYPEC_RP_RESERVED); + + /* Some time for DUT to detach, use tErrorRecovery */ + msleep(25); + + /* Accept new dts/src value */ + disable_dts_mode = disable_dts_new; + allow_src_mode = allow_src_new; + /* Can we charge? */ + pd_set_dual_role(DUT, + allow_src_mode && charge_port_is_active() ? + PD_DRP_FORCE_SOURCE : PD_DRP_FORCE_SINK); + + /* Present Rp or Rd on CC1 and CC2 based on disable_dts_mode */ + pd_config_init(DUT, + pd_get_dual_role(DUT) == PD_DRP_FORCE_SOURCE); + } + + print_cc_mode(); +} + +static int command_cc(int argc, char **argv) +{ + int disable_dts_new; + int allow_src_new; + + if (argc < 2) { + print_cc_mode(); + return EC_SUCCESS; + } + + if (!strcasecmp(argv[1], "src")) { + disable_dts_new = 1; + allow_src_new = 1; + } else if (!strcasecmp(argv[1], "snk")) { + disable_dts_new = 1; + allow_src_new = 0; + } else if (!strcasecmp(argv[1], "srcdts")) { + disable_dts_new = 0; + allow_src_new = 1; + } else if (!strcasecmp(argv[1], "snkdts")) { + disable_dts_new = 0; + allow_src_new = 0; + } else { + ccprintf("Try one of src, snk, srcdts, snkdts\n"); + return EC_ERROR_PARAM2; + } + do_cc(disable_dts_new, allow_src_new); + + return EC_SUCCESS; +} +DECLARE_CONSOLE_COMMAND(cc, command_cc, + "src|snk|srcdts|snkdts", + "Servo_v4 DTS and CHG mode"); + + static int command_dts(int argc, char **argv) { int disable_dts_new; int val; if (argc < 2) { - /* Get current CCD status */ - ccprintf("dts mode: %s\n", disable_dts_mode ? "off" : "on"); + print_cc_mode(); return EC_SUCCESS; } @@ -612,21 +716,9 @@ static int command_dts(int argc, char **argv) return EC_ERROR_PARAM2; disable_dts_new = val ^ 1; - if (disable_dts_new != disable_dts_mode) { - /* Force detach */ - pd_power_supply_reset(DUT); - /* Always set to 0 here so both CC lines are changed */ - disable_dts_mode = 0; - /* Remove Rp/Rd on both CC lines */ - board_select_rp_value(DUT, TYPEC_RP_RESERVED); - /* Accept new disable_dts value */ - disable_dts_mode = disable_dts_new; - /* Some time for DUT to detach */ - msleep(100); - /* Present RP_USB on CC1 and CC2 based on disable_dts_mode */ - board_select_rp_value(DUT, TYPEC_RP_USB); - ccprintf("dts mode: %s\n", disable_dts_mode ? "off" : "on"); - } + + /* Change dts without changing src. */ + do_cc(disable_dts_new, allow_src_mode); return EC_SUCCESS; } |