summaryrefslogtreecommitdiff
path: root/board/servo_v4p1
diff options
context:
space:
mode:
authorSam Hurst <shurst@google.com>2020-06-09 12:23:10 -0700
committerCommit Bot <commit-bot@chromium.org>2020-07-21 00:55:28 +0000
commit3e083f33a6739b00ea69ba1387dedfc4af8821a6 (patch)
treeb0240af1d86bf0c64198654d6ccbee91a73fd61d /board/servo_v4p1
parente4b4509e6b0e2b6015741c01284b4b2e7bdf7687 (diff)
downloadchrome-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
Diffstat (limited to 'board/servo_v4p1')
-rw-r--r--board/servo_v4p1/board.c91
-rw-r--r--board/servo_v4p1/usb_pd_config.h9
-rw-r--r--board/servo_v4p1/usb_pd_policy.c249
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");