diff options
Diffstat (limited to 'board/servo_v4/usb_pd_policy.c')
-rw-r--r-- | board/servo_v4/usb_pd_policy.c | 208 |
1 files changed, 129 insertions, 79 deletions
diff --git a/board/servo_v4/usb_pd_policy.c b/board/servo_v4/usb_pd_policy.c index 9a781c6534..ec127c734f 100644 --- a/board/servo_v4/usb_pd_policy.c +++ b/board/servo_v4/usb_pd_policy.c @@ -32,6 +32,28 @@ #define VBUS_UNCHANGED(curr, pend, new) (curr == new && pend == new) +/* Macros to config the PD role */ +#define CONFIG_SET_CLEAR(c, set, clear) ((c | (set)) & ~(clear)) +#define CONFIG_SRC(c) CONFIG_SET_CLEAR(c, \ + CC_DISABLE_DTS | CC_ALLOW_SRC, \ + CC_ENABLE_DRP) +#define CONFIG_SNK(c) CONFIG_SET_CLEAR(c, \ + CC_DISABLE_DTS, \ + CC_ALLOW_SRC | CC_ENABLE_DRP) +#define CONFIG_DRP(c) CONFIG_SET_CLEAR(c, \ + CC_DISABLE_DTS | CC_ALLOW_SRC | CC_ENABLE_DRP, \ + 0) +#define CONFIG_SRCDTS(c) CONFIG_SET_CLEAR(c, \ + CC_ALLOW_SRC, \ + CC_ENABLE_DRP | CC_DISABLE_DTS) +#define CONFIG_SNKDTS(c) CONFIG_SET_CLEAR(c, \ + 0, \ + CC_ALLOW_SRC | CC_ENABLE_DRP | CC_DISABLE_DTS) +#define CONFIG_DRPDTS(c) CONFIG_SET_CLEAR(c, \ + CC_ALLOW_SRC | CC_ENABLE_DRP, \ + CC_DISABLE_DTS) + + /* * Dynamic PDO that reflects capabilities present on the CHG port. Allow for * multiple entries so that we can offer greater than 5V charging. The 1st @@ -61,15 +83,7 @@ static int active_charge_port = CHARGE_PORT_NONE; static enum charge_supplier active_charge_supplier; static uint8_t vbus_rp = TYPEC_RP_RESERVED; -/* Flag to emulate detach, i.e. making both CC lines open. */ -static int disable_cc; -/* - * 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; +static int cc_config = CC_ALLOW_SRC; /* Voltage thresholds for no connect in DTS mode */ static int pd_src_vnc_dts[TYPEC_RP_RESERVED][2] = { @@ -121,17 +135,35 @@ static int charge_port_is_active(void) return active_charge_port == CHG && vbus[CHG].mv > 0; } +static int is_charge_through_allowed(void) +{ + return charge_port_is_active() && cc_config & CC_ALLOW_SRC; +} + +static int get_dual_role_of_src(void) +{ + return cc_config & CC_ENABLE_DRP ? PD_DRP_TOGGLE_ON : + PD_DRP_FORCE_SOURCE; +} + 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) { + if (is_charge_through_allowed() && + pd_get_dual_role(DUT) != PD_DRP_FORCE_SOURCE && + pd_get_dual_role(DUT) != PD_DRP_TOGGLE_ON) { CPRINTS("Enable DUT charge through"); - pd_set_dual_role(DUT, PD_DRP_FORCE_SOURCE); - pd_set_host_mode(DUT, 1); + pd_set_dual_role(DUT, get_dual_role_of_src()); + /* + * If DRP role, don't set any CC pull resistor, the PD + * state machine will toggle and set the pull resistors + * when needed. + */ + if (!(cc_config & CC_ENABLE_DRP)) + pd_set_host_mode(DUT, 1); pd_update_contract(DUT); } } @@ -153,8 +185,8 @@ static void board_manage_dut_port(void) 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; + if (is_charge_through_allowed()) + allowed_role = get_dual_role_of_src(); current_role = pd_get_dual_role(DUT); if (current_role != allowed_role) { @@ -283,7 +315,7 @@ int pd_tcpc_cc_nc(int port, int cc_volt, int cc_sel) return 1; /* Select the correct voltage threshold for current Rp and DTS mode */ - if (disable_dts_mode) + if (cc_config & CC_DISABLE_DTS) nc = cc_volt >= pd_src_vnc[rp_index]; else nc = cc_volt >= pd_src_vnc_dts[rp_index][cc_sel]; @@ -309,7 +341,7 @@ int pd_tcpc_cc_ra(int port, int cc_volt, int cc_sel) return 0; /* Select the correct voltage threshold for current Rp and DTS mode */ - if (disable_dts_mode) + if (cc_config & CC_DISABLE_DTS) ra = cc_volt < pd_src_rd_threshold[rp_index]; else ra = cc_volt < pd_src_rd_threshold_dts[rp_index][cc_sel]; @@ -323,7 +355,7 @@ int pd_adc_read(int port, int cc) if (port == 0) mv = adc_read_channel(cc ? ADC_CHG_CC2_PD : ADC_CHG_CC1_PD); - else if (!disable_cc) { + else if (!(cc_config & CC_DETACH)) { /* * In servo v4 hardware logic, both CC lines are wired directly * to DUT. When servo v4 as a snk, DUT may source Vconn to CC2 @@ -338,15 +370,15 @@ int pd_adc_read(int port, int cc) * leaves CC2 open. Need change when it supports switching CC * polarity. */ - if (disable_dts_mode && cc_pull_stored == TYPEC_CC_RD && - port == DUT && cc == 1) + if ((cc_config & CC_DISABLE_DTS) && + cc_pull_stored == TYPEC_CC_RD && port == DUT && cc == 1) mv = 0; else mv = adc_read_channel(cc ? ADC_DUT_CC2_PD : ADC_DUT_CC1_PD); } else { /* - * When disable_cc, fake the voltage on CC to 0 to avoid + * When emulating detach, fake the voltage on CC to 0 to avoid * triggering some debounce logic. * * The servo v4 makes Rd/Rp open but the DUT may present Rd/Rp @@ -362,7 +394,7 @@ int pd_adc_read(int port, int cc) static int board_set_rp(int rp) { - if (disable_dts_mode) { + if (cc_config & CC_DISABLE_DTS) { /* * DTS mode is disabled, so only present the requested Rp value * on CC1 and leave all Rp/Rd resistors on CC2 disconnected. @@ -438,7 +470,7 @@ int pd_set_rp_rd(int port, int cc_pull, int rp_value) return EC_ERROR_UNIMPLEMENTED; /* CC is disabled for emulating detach. Don't change Rd/Rp. */ - if (disable_cc) + if (cc_config & CC_DETACH) return EC_SUCCESS; /* By default disconnect all Rp/Rd resistors from both CC lines */ @@ -467,7 +499,7 @@ int pd_set_rp_rd(int port, int cc_pull, int rp_value) * CC1. */ gpio_set_flags(GPIO_USB_DUT_CC1_RD, GPIO_OUT_LOW); - if (!disable_dts_mode) + if (!(cc_config & CC_DISABLE_DTS)) gpio_set_flags(GPIO_USB_DUT_CC2_RD, GPIO_OUT_LOW); } @@ -567,7 +599,7 @@ int pd_set_power_supply_ready(int port) vbus[DUT].mv = vbus[CHG].mv; vbus[DUT].ma = vbus[CHG].mv; - pd_set_dual_role(DUT, PD_DRP_FORCE_SOURCE); + pd_set_dual_role(DUT, get_dual_role_of_src()); } else { vbus[DUT].mv = 0; vbus[DUT].ma = 0; @@ -710,28 +742,26 @@ const int supported_modes_cnt = ARRAY_SIZE(supported_modes); static void print_cc_mode(void) { /* Get current CCD status */ - ccprintf("cc: %s\n", disable_cc ? "off" : "on"); - ccprintf("dts mode: %s\n", disable_dts_mode ? "off" : "on"); + ccprintf("cc: %s\n", cc_config & CC_DETACH ? "off" : "on"); + ccprintf("dts mode: %s\n", cc_config & CC_DISABLE_DTS ? "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"); + gpio_get_level(GPIO_DUT_CHG_EN) ? "on" : "off"); + ccprintf("chg allowed: %s\n", cc_config & CC_ALLOW_SRC ? "on" : "off"); + ccprintf("drp enabled: %s\n", cc_config & CC_ENABLE_DRP ? "on" : "off"); } -static void do_cc(int disable_cc_new, int disable_dts_new, int allow_src_new) +static void do_cc(int cc_config_new) { + int chargeable; int dualrole; - if ((disable_cc_new != disable_cc) || - (disable_dts_new != disable_dts_mode) || - (allow_src_new != allow_src_mode)) { - if (!disable_cc) { + if (cc_config_new != cc_config) { + if (!(cc_config & CC_DETACH)) { /* 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; + cc_config &= ~(CC_DISABLE_DTS & CC_ALLOW_SRC); /* Remove Rp/Rd on both CC lines */ pd_comm_enable(DUT, 0); @@ -741,36 +771,37 @@ static void do_cc(int disable_cc_new, int disable_dts_new, int allow_src_new) * If just changing mode (cc keeps enabled), give some * time for DUT to detach, use tErrorRecovery. */ - if (!disable_cc_new) + if (!(cc_config_new & CC_DETACH)) usleep(PD_T_ERROR_RECOVERY); } - /* Accept new cc/dts/src value */ - disable_cc = disable_cc_new; - disable_dts_mode = disable_dts_new; - allow_src_mode = allow_src_new; + /* Accept new cc_config value */ + cc_config = cc_config_new; - if (!disable_cc) { + if (!(cc_config & CC_DETACH)) { /* Can we source? */ - dualrole = allow_src_mode && charge_port_is_active(); - pd_set_dual_role(DUT, dualrole ? - PD_DRP_FORCE_SOURCE : PD_DRP_FORCE_SINK); - + chargeable = is_charge_through_allowed(); + dualrole = chargeable ? get_dual_role_of_src() : + PD_DRP_FORCE_SINK; + pd_set_dual_role(DUT, dualrole); /* - * Present Rp or Rd on CC1 and CC2 based on - * whether we can source or not. + * If force_source or force_sink role, explicitly set + * the Rp or Rd resistors on CC lines. + * + * If DRP role, don't set any CC pull resistor, the PD + * state machine will toggle and set the pull resistors + * when needed. */ - pd_set_host_mode(DUT, dualrole); - pd_comm_enable(DUT, dualrole); + if (dualrole != PD_DRP_TOGGLE_ON) + pd_set_host_mode(DUT, chargeable); + pd_comm_enable(DUT, chargeable); } } } static int command_cc(int argc, char **argv) { - int disable_cc_new; - int disable_dts_new; - int allow_src_new; + int cc_config_new = cc_config; if (argc < 2) { print_cc_mode(); @@ -778,49 +809,47 @@ static int command_cc(int argc, char **argv) } if (!strcasecmp(argv[1], "off")) { - disable_cc_new = 1; - disable_dts_new = 0; - allow_src_new = 0; - } else if (!strcasecmp(argv[1], "src")) { - disable_cc_new = 0; - disable_dts_new = 1; - allow_src_new = 1; - } else if (!strcasecmp(argv[1], "snk")) { - disable_cc_new = 0; - disable_dts_new = 1; - allow_src_new = 0; - } else if (!strcasecmp(argv[1], "srcdts")) { - disable_cc_new = 0; - disable_dts_new = 0; - allow_src_new = 1; - } else if (!strcasecmp(argv[1], "snkdts")) { - disable_cc_new = 0; - disable_dts_new = 0; - allow_src_new = 0; + cc_config_new |= CC_DETACH; + } else if (!strcasecmp(argv[1], "on")) { + cc_config_new &= ~CC_DETACH; } else { - ccprintf("Try one of off, src, snk, srcdts, snkdts\n"); - return EC_ERROR_PARAM2; + cc_config_new &= ~CC_DETACH; + if (!strcasecmp(argv[1], "src")) + cc_config_new = CONFIG_SRC(cc_config_new); + else if (!strcasecmp(argv[1], "snk")) + cc_config_new = CONFIG_SNK(cc_config_new); + else if (!strcasecmp(argv[1], "drp")) + cc_config_new = CONFIG_DRP(cc_config_new); + else if (!strcasecmp(argv[1], "srcdts")) + cc_config_new = CONFIG_SRCDTS(cc_config_new); + else if (!strcasecmp(argv[1], "snkdts")) + cc_config_new = CONFIG_SNKDTS(cc_config_new); + else if (!strcasecmp(argv[1], "drpdts")) + cc_config_new = CONFIG_DRPDTS(cc_config_new); + else + return EC_ERROR_PARAM2; } - do_cc(disable_cc_new, disable_dts_new, allow_src_new); + + do_cc(cc_config_new); print_cc_mode(); return EC_SUCCESS; } DECLARE_CONSOLE_COMMAND(cc, command_cc, - "off|src|snk|srcdts|snkdts", + "[off|on|src|snk|drp|srcdts|snkdts|drpdts]", "Servo_v4 DTS and CHG mode"); static void fake_disconnect_end(void) { /* Reenable CC lines with previous dts and src modes */ - do_cc(0, disable_dts_mode, allow_src_mode); + do_cc(cc_config & ~CC_DETACH); } DECLARE_DEFERRED(fake_disconnect_end); static void fake_disconnect_start(void) { /* Disable CC lines */ - do_cc(1, disable_dts_mode, allow_src_mode); + do_cc(cc_config | CC_DETACH); hook_call_deferred(&fake_disconnect_end_data, fake_pd_disconnect_duration_us); @@ -856,3 +885,24 @@ static int cmd_fake_disconnect(int argc, char *argv[]) } DECLARE_CONSOLE_COMMAND(fakedisconnect, cmd_fake_disconnect, "<delay_ms> <duration_ms>", NULL); + +static int cmd_usbc_action(int argc, char *argv[]) +{ + if (argc != 2) + return EC_ERROR_PARAM_COUNT; + + if (!strcasecmp(argv[1], "drp")) { + /* Toggle the DRP state, compatible with Plankton. */ + do_cc(cc_config ^ CC_ENABLE_DRP); + CPRINTF("DRP = %d, host_mode = %d\n", + !!(cc_config & CC_ENABLE_DRP), + !!(cc_config & CC_ALLOW_SRC)); + } else { + return EC_ERROR_PARAM1; + } + + return EC_SUCCESS; +} +DECLARE_CONSOLE_COMMAND(usbc_action, cmd_usbc_action, + "<drp>", + "Set Servo v4 type-C port state"); |