diff options
author | Sam Hurst <shurst@google.com> | 2020-06-09 12:23:10 -0700 |
---|---|---|
committer | Commit Bot <commit-bot@chromium.org> | 2020-07-21 00:55:28 +0000 |
commit | 3e083f33a6739b00ea69ba1387dedfc4af8821a6 (patch) | |
tree | b0240af1d86bf0c64198654d6ccbee91a73fd61d | |
parent | e4b4509e6b0e2b6015741c01284b4b2e7bdf7687 (diff) | |
download | chrome-ec-3e083f33a6739b00ea69ba1387dedfc4af8821a6.tar.gz |
servo_v4p1: Add DP functionality
Add DP functionality
BRANCH=none
BUG=b:146793000
TEST=make -j buildall
Signed-off-by: Sam Hurst <shurst@google.com>
Change-Id: I041628a2fe01123e168e78f01a5fba9b72e80dca
Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/platform/ec/+/2238676
-rw-r--r-- | board/servo_v4p1/board.c | 91 | ||||
-rw-r--r-- | board/servo_v4p1/usb_pd_config.h | 9 | ||||
-rw-r--r-- | board/servo_v4p1/usb_pd_policy.c | 249 |
3 files changed, 289 insertions, 60 deletions
diff --git a/board/servo_v4p1/board.c b/board/servo_v4p1/board.c index 4a389e30ca..8ee4e6f158 100644 --- a/board/servo_v4p1/board.c +++ b/board/servo_v4p1/board.c @@ -90,9 +90,85 @@ static void tca_evt(enum gpio_signal signal) } } +static volatile uint64_t hpd_prev_ts; +static volatile int hpd_prev_level; + +/** + * Hotplug detect deferred task + * + * Called after level change on hpd GPIO to evaluate (and debounce) what event + * has occurred. There are 3 events that occur on HPD: + * 1. low : downstream display sink is deattached + * 2. high : downstream display sink is attached + * 3. irq : downstream display sink signalling an interrupt. + * + * The debounce times for these various events are: + * HPD_USTREAM_DEBOUNCE_LVL : min pulse width of level value. + * HPD_USTREAM_DEBOUNCE_IRQ : min pulse width of IRQ low pulse. + * + * lvl(n-2) lvl(n-1) lvl prev_delta now_delta event + * ---------------------------------------------------- + * 1 0 1 <IRQ n/a low glitch (ignore) + * 1 0 1 >IRQ <LVL irq + * x 0 1 n/a >LVL high + * 0 1 0 <LVL n/a high glitch (ignore) + * x 1 0 n/a >LVL low + */ + +void hpd_irq_deferred(void) +{ + int dp_mode = pd_alt_mode(1, TCPC_TX_SOP, USB_SID_DISPLAYPORT); + + if (dp_mode) { + pd_send_hpd(DUT, hpd_irq); + ccprintf("HPD IRQ"); + } +} +DECLARE_DEFERRED(hpd_irq_deferred); + +void hpd_lvl_deferred(void) +{ + int level = gpio_get_level(GPIO_DP_HPD); + int dp_mode = pd_alt_mode(1, TCPC_TX_SOP, USB_SID_DISPLAYPORT); + + if (level != hpd_prev_level) { + /* It's a glitch while in deferred or canceled action */ + return; + } + + if (dp_mode) { + pd_send_hpd(DUT, level ? hpd_high : hpd_low); + ccprintf("HPD: %d", level); + } +} +DECLARE_DEFERRED(hpd_lvl_deferred); + static void dp_evt(enum gpio_signal signal) { - ccprintf("DP detected\n"); + timestamp_t now = get_time(); + int level = gpio_get_level(signal); + uint64_t cur_delta = now.val - hpd_prev_ts; + + /* Store current time */ + hpd_prev_ts = now.val; + + /* All previous hpd level events need to be re-triggered */ + hook_call_deferred(&hpd_lvl_deferred_data, -1); + + /* It's a glitch. Previous time moves but level is the same. */ + if (cur_delta < HPD_USTREAM_DEBOUNCE_IRQ) + return; + + if ((!hpd_prev_level && level) && + (cur_delta < HPD_USTREAM_DEBOUNCE_LVL)) { + /* It's an irq */ + hook_call_deferred(&hpd_irq_deferred_data, 0); + } else if (cur_delta >= HPD_USTREAM_DEBOUNCE_LVL) { + hook_call_deferred(&hpd_lvl_deferred_data, + HPD_USTREAM_DEBOUNCE_LVL); + } + + hpd_prev_level = level; } static void tcpc_evt(enum gpio_signal signal) @@ -118,6 +194,19 @@ static void init_uservo_port(void) /* Connect uservo to host hub */ uservo_fastboot_mux_sel(0); } + +void ext_hpd_detection_enable(int enable) +{ + if (enable) { + timestamp_t now = get_time(); + + hpd_prev_level = gpio_get_level(GPIO_DP_HPD); + hpd_prev_ts = now.val; + gpio_enable_interrupt(GPIO_DP_HPD); + } else { + gpio_disable_interrupt(GPIO_DP_HPD); + } +} #else void pd_task(void *u) { diff --git a/board/servo_v4p1/usb_pd_config.h b/board/servo_v4p1/usb_pd_config.h index 8bf1e6335b..4f33e83e0e 100644 --- a/board/servo_v4p1/usb_pd_config.h +++ b/board/servo_v4p1/usb_pd_config.h @@ -67,6 +67,15 @@ * (EMCA) servo (or non-EMCA) */ +/* Servo v4 DP alt-mode configuration */ +#define ALT_DP_ENABLE BIT(0) /* Enable DP alt-mode or not */ +#define ALT_DP_PIN_C BIT(1) /* Pin assignment C supported */ +#define ALT_DP_PIN_D BIT(2) /* Pin assignment D supported */ +#define ALT_DP_MF_PREF BIT(3) /* Multi-Function preferred */ +#define ALT_DP_PLUG BIT(4) /* Plug or receptacle */ +#define ALT_DP_OVERRIDE_HPD BIT(5) /* Override the HPD signal */ +#define ALT_DP_HPD_LVL BIT(6) /* HPD level if overridden */ + /* TX uses SPI1 on PB3-4 for CHG port, SPI2 on PB 13-14 for DUT port */ #define SPI_REGS(p) ((p) ? STM32_SPI2_REGS : STM32_SPI1_REGS) static inline void spi_enable_clock(int port) diff --git a/board/servo_v4p1/usb_pd_policy.c b/board/servo_v4p1/usb_pd_policy.c index e094557d3d..96af570aae 100644 --- a/board/servo_v4p1/usb_pd_policy.c +++ b/board/servo_v4p1/usb_pd_policy.c @@ -18,6 +18,7 @@ #include "task.h" #include "tcpm.h" #include "timer.h" +#include "tusb1064.h" #include "util.h" #include "usb_common.h" #include "usb_mux.h" @@ -89,15 +90,6 @@ #define DUT_ACTIVE_CC_OPEN(r) DUT_ACTIVE_CC_SET(r, GPIO_INPUT) #define DUT_INACTIVE_CC_OPEN(r) DUT_INACTIVE_CC_SET(r, GPIO_INPUT) -#define TUSB1064_I2C_ADDR_FLAG 0x12 - -#define TUSB1064_REG_GENERAL 0x0a -#define TUSB1064_MODE_POLARITY_INVERTED 0x4 -#define TUSB1064_MODE_DP_ENABLED 0x2 -#define TUSB1064_MODE_USB_ENABLED 0x1 - -#define TUSB1064_REG_DP_CTRL 0x13 - /* * 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 @@ -446,9 +438,10 @@ int pd_adc_read(int port, int cc) */ /* - * TODO: Fix this logic because of leakage "phantom detects" - * Or flat-out mis-detects..... talking on leaking CC2 line. - * And Vconn-swap case... and Ra on second line (SERVO_EMCA)... + * TODO(b/161260559): Fix this logic because of leakage + * "phantom detects" Or flat-out mis-detects..... talking on + * leaking CC2 line. And Vconn-swap case... and Ra on second + * line (SERVO_EMCA)... * * This is basically a hack faking "vOpen" from TCPCI spec. */ @@ -825,6 +818,35 @@ __override void pd_check_dr_role(int port, /* ----------------- Vendor Defined Messages ------------------ */ +/* + * DP alt-mode config, user configurable. + * Default is the mode disabled, supporting the C and D pin assignment, + * multi-function preferred, and a plug. + */ +static int alt_dp_config = (ALT_DP_PIN_C | ALT_DP_PIN_D | ALT_DP_MF_PREF | + ALT_DP_PLUG); + +/** + * Get the pins based on the user config. + */ +static int alt_dp_config_pins(void) +{ + int pins = 0; + + if (alt_dp_config & ALT_DP_PIN_C) + pins |= MODE_DP_PIN_C; + if (alt_dp_config & ALT_DP_PIN_D) + pins |= MODE_DP_PIN_D; + return pins; +} + +/** + * Get the cable outlet value (plug or receptacle) based on the user config. + */ +static int alt_dp_config_cable(void) +{ + return (alt_dp_config & ALT_DP_PLUG) ? CABLE_PLUG : CABLE_RECEPTACLE; +} const uint32_t vdo_idh = VDO_IDH(0, /* data caps as USB host */ 1, /* data caps as USB device */ @@ -844,16 +866,7 @@ const uint32_t vdo_ama = VDO_AMA(CONFIG_USB_PD_IDENTITY_HW_VERS, static int svdm_response_identity(int port, uint32_t *payload) { - /* - * TODO(b/137219603): Make whether servo supports DP alt-mode - * configurable, like through a console command. - * - * This version is to check if a monitor is plugged to the mini-DP port - * to decide if DP alt-mode is supported or not. So no alt-mode - * supported if no monitor is plugged before plugging the servo Type-C - * cable to DUT. This way doesn't affect PD FAFT results. - */ - int dp_supported = gpio_get_level(GPIO_DP_HPD); + int dp_supported = (alt_dp_config & ALT_DP_ENABLE) != 0; if (dp_supported) { payload[VDO_I(IDH)] = vdo_idh; @@ -878,21 +891,18 @@ static int svdm_response_svids(int port, uint32_t *payload) /* * The Type-C demux TUSB1064 supports pin assignment C and D. Response the DP * capabilities with supporting all of them. - * - * TODO(b/137219603): Make this pin assignment and plug/receptacle configurable - * by a console command that some tests can check different dongle behaviors. */ -const uint32_t vdo_dp_mode[MODE_CNT] = { - VDO_MODE_DP(0, /* UFP pin cfg supported: none */ - MODE_DP_PIN_C | MODE_DP_PIN_D | MODE_DP_PIN_E, /* DFP pin */ - 1, /* no usb2.0 signalling in AMode */ - CABLE_PLUG, /* Its a plug */ - MODE_DP_V13, /* DPv1.3 Support, no Gen2 */ - MODE_DP_SNK) /* Its a sink only */ -}; +uint32_t vdo_dp_mode[MODE_CNT]; static int svdm_response_modes(int port, uint32_t *payload) { + vdo_dp_mode[0] = + VDO_MODE_DP(0, /* UFP pin cfg supported: none */ + alt_dp_config_pins(), /* DFP pin */ + 1, /* no usb2.0 signalling in AMode */ + alt_dp_config_cable(), /* plug or receptacle */ + MODE_DP_V13, /* DPv1.3 Support, no Gen2 */ + MODE_DP_SNK); /* Its a sink only */ /* CCD uses the SBU lines; don't enable DP when dts-mode enabled */ if (!(cc_config & CC_DISABLE_DTS)) @@ -909,28 +919,32 @@ static int is_typec_dp_muxed(void) { int value; - i2c_read8(I2C_PORT_MASTER, TUSB1064_I2C_ADDR_FLAG, TUSB1064_REG_GENERAL, - &value); - return value & TUSB1064_MODE_DP_ENABLED ? 1 : 0; + value = tusb1064_read_byte(I2C_PORT_MASTER, TUSB1064_REG_GENERAL); + if (value < 0 || value & REG_GENERAL_CTLSEL_4DP_LANES) + return 0; + + return 1; } static void set_typec_mux(int pin_cfg) { int value; - i2c_read8(I2C_PORT_MASTER, TUSB1064_I2C_ADDR_FLAG, TUSB1064_REG_GENERAL, - &value); - value &= ~(TUSB1064_MODE_DP_ENABLED | TUSB1064_MODE_USB_ENABLED); + value = tusb1064_read_byte(I2C_PORT_MASTER, TUSB1064_REG_GENERAL); + if (value < 0) + return; + + value &= ~(REG_GENERAL_CTLSEL_4DP_LANES | REG_GENERAL_CTLSEL_USB3); switch (pin_cfg) { case 0: CPRINTS("PinCfg:off"); break; case MODE_DP_PIN_C: - value |= TUSB1064_MODE_DP_ENABLED; + value |= REG_GENERAL_CTLSEL_4DP_LANES; CPRINTS("PinCfg:C"); break; case MODE_DP_PIN_D: - value |= TUSB1064_MODE_DP_ENABLED | TUSB1064_MODE_USB_ENABLED; + value |= REG_GENERAL_CTLSEL_2DP_AND_USB3; CPRINTS("PinCfg:D"); break; default: @@ -938,34 +952,39 @@ static void set_typec_mux(int pin_cfg) return; } if (value && cc_config & CC_POLARITY) - value |= TUSB1064_MODE_POLARITY_INVERTED; + value |= REG_GENERAL_FLIPSEL; else - value &= ~TUSB1064_MODE_POLARITY_INVERTED; + value &= ~REG_GENERAL_FLIPSEL; - i2c_write8(I2C_PORT_MASTER, TUSB1064_I2C_ADDR_FLAG, - TUSB1064_REG_GENERAL, value); + tusb1064_write_byte(I2C_PORT_MASTER, TUSB1064_REG_GENERAL, value); +} + +static int get_hpd_level(void) +{ + if (alt_dp_config & ALT_DP_OVERRIDE_HPD) + return (alt_dp_config & ALT_DP_HPD_LVL) != 0; + else + return gpio_get_level(GPIO_DP_HPD); } static int dp_status(int port, uint32_t *payload) { int opos = PD_VDO_OPOS(payload[0]); - int hpd = gpio_get_level(GPIO_DP_HPD); + int hpd = get_hpd_level(); if (opos != OPOS) return 0; /* NAK */ - /* - * TODO(b/137219603): Make the Multi-Function Preferred bit - * configurable by a console command. - */ - payload[1] = VDO_DP_STATUS(0, /* IRQ_HPD */ - hpd, /* HPD_HI|LOW */ - 0, /* request exit DP */ - 0, /* request exit USB */ - 1, /* MF pref */ - is_typec_dp_muxed(), - 0, /* power low */ - 0x2); + payload[1] = VDO_DP_STATUS( + 0, /* IRQ_HPD */ + hpd, /* HPD_HI|LOW */ + 0, /* request exit DP */ + 0, /* request exit USB */ + (alt_dp_config & ALT_DP_MF_PREF) != 0, /* MF pref */ + is_typec_dp_muxed(), + 0, /* power low */ + hpd ? 0x2 : 0); + return 2; } @@ -1096,9 +1115,12 @@ static void do_cc(int cc_config_new) if ((cc_config & ~cc_config_new) & CC_DISABLE_DTS) { /* DTS-disabled -> DTS-enabled */ ccd_enable(1); + ext_hpd_detection_enable(0); } else if ((cc_config_new & ~cc_config) & CC_DISABLE_DTS) { /* DTS-enabled -> DTS-disabled */ ccd_enable(0); + if (!(alt_dp_config & ALT_DP_OVERRIDE_HPD)) + ext_hpd_detection_enable(1); } /* Accept new cc_config value */ @@ -1256,8 +1278,117 @@ DECLARE_CONSOLE_COMMAND(ada_srccaps, cmd_ada_srccaps, "", "Print adapter SrcCap"); +static void chg_pd_disconnect(void) +{ + /* Clear charger PDO on CHG port disconnected. */ + if (pd_is_disconnected(CHG)) + pd_set_src_caps(CHG, 0, NULL); +} +DECLARE_HOOK(HOOK_USB_PD_DISCONNECT, chg_pd_disconnect, HOOK_PRIO_DEFAULT); + +static int cmd_dp_action(int argc, char *argv[]) +{ + int i; + char *e; + + if (argc < 1) + return EC_ERROR_PARAM_COUNT; + + if (argc == 1) { + CPRINTS("DP alt-mode: %s", + (alt_dp_config & ALT_DP_ENABLE) ? "enable" : "disable"); + } + + if (!strcasecmp(argv[1], "enable")) { + alt_dp_config |= ALT_DP_ENABLE; + } else if (!strcasecmp(argv[1], "disable")) { + alt_dp_config &= ~ALT_DP_ENABLE; + } else if (!strcasecmp(argv[1], "pins")) { + if (argc >= 3) { + alt_dp_config &= ~(ALT_DP_PIN_C | ALT_DP_PIN_D); + for (i = 0; i < 3; i++) { + if (!argv[2][i]) + break; + + switch (argv[2][i]) { + case 'c': + case 'C': + alt_dp_config |= ALT_DP_PIN_C; + break; + case 'd': + case 'D': + alt_dp_config |= ALT_DP_PIN_D; + break; + } + } + } + CPRINTS("Pins: %s%s", + (alt_dp_config & ALT_DP_PIN_C) ? "C" : "", + (alt_dp_config & ALT_DP_PIN_D) ? "D" : ""); + } else if (!strcasecmp(argv[1], "mf")) { + if (argc >= 3) { + i = strtoi(argv[2], &e, 10); + if (*e) + return EC_ERROR_PARAM3; + if (i) + alt_dp_config |= ALT_DP_MF_PREF; + else + alt_dp_config &= ~ALT_DP_MF_PREF; + } + CPRINTS("MF pref: %d", (alt_dp_config & ALT_DP_MF_PREF) != 0); + } else if (!strcasecmp(argv[1], "plug")) { + if (argc >= 3) { + i = strtoi(argv[2], &e, 10); + if (*e) + return EC_ERROR_PARAM3; + if (i) + alt_dp_config |= ALT_DP_PLUG; + else + alt_dp_config &= ~ALT_DP_PLUG; + } + CPRINTS("Plug or receptacle: %d", + (alt_dp_config & ALT_DP_PLUG) != 0); + } else if (!strcasecmp(argv[1], "hpd")) { + if (argc >= 3) { + if (!strncasecmp(argv[2], "ext", 3)) { + alt_dp_config &= ~ALT_DP_OVERRIDE_HPD; + ext_hpd_detection_enable(1); + } else if (!strncasecmp(argv[2], "h", 1)) { + alt_dp_config |= ALT_DP_OVERRIDE_HPD; + alt_dp_config |= ALT_DP_HPD_LVL; + /* + * Modify the HPD to high. Need to enable the + * external HPD signal monitoring. A monitor + * may send a IRQ at any time to notify DUT. + */ + ext_hpd_detection_enable(1); + pd_send_hpd(DUT, hpd_high); + } else if (!strncasecmp(argv[2], "l", 1)) { + alt_dp_config |= ALT_DP_OVERRIDE_HPD; + alt_dp_config &= ~ALT_DP_HPD_LVL; + ext_hpd_detection_enable(0); + pd_send_hpd(DUT, hpd_low); + } else if (!strcasecmp(argv[2], "irq")) { + pd_send_hpd(DUT, hpd_irq); + } + } + CPRINTS("HPD source: %s", + (alt_dp_config & ALT_DP_OVERRIDE_HPD) ? "overridden" + : "external"); + CPRINTS("HPD level: %d", get_hpd_level()); + } else if (!strcasecmp(argv[1], "help")) { + CPRINTS("Usage: usbc_action dp [enable|disable|hpd|mf|pins|" + "plug]"); + } + + return EC_SUCCESS; +} + static int cmd_usbc_action(int argc, char *argv[]) { + if (argc >= 2 && !strcasecmp(argv[1], "dp")) + return cmd_dp_action(argc - 1, &argv[1]); + if (argc != 2 && argc != 3) return EC_ERROR_PARAM_COUNT; @@ -1314,5 +1445,5 @@ static int cmd_usbc_action(int argc, char *argv[]) return EC_SUCCESS; } DECLARE_CONSOLE_COMMAND(usbc_action, cmd_usbc_action, - "5v|12v|20v|dev|pol0|pol1|drp|chg x(x=voltage)", + "5v|12v|20v|dev|pol0|pol1|drp|dp|chg x(x=voltage)", "Set Servo v4 type-C port state"); |