diff options
author | Scott <scollyer@chromium.org> | 2015-04-28 10:07:56 -0700 |
---|---|---|
committer | ChromeOS Commit Bot <chromeos-commit-bot@chromium.org> | 2015-06-30 08:58:23 +0000 |
commit | 590c76d910d4e474b8713b71dae379608b06a265 (patch) | |
tree | ca46ba2209db4badd299b8373cb178d80cef61c4 | |
parent | 0ec956ea9d585f350a47db162e275037008d75cc (diff) | |
download | chrome-ec-590c76d910d4e474b8713b71dae379608b06a265.tar.gz |
honeybuns: Update VDM information for DisplayPort
Updated the VDM information to handle properly the DisplayPort alternate
mode.
Switch to 2+2 (DP+USB3.0) if mode D is entered else use 4 lanes of DP
for mode C.
Set the Multi-Function Preferred bit, so laptops select the mode D.
BUG=none
BRANCH=none
TEST=Tested with samus. Verified we get 36W of power + USB2.0 key +
USB3.0 key + external DP display.
Change-Id: I95e3b3640fd5952faeb24312e387468aed6266c7
Signed-off-by: Scott Collyer <scollyer@chromium.org>
Signed-off-by: Vincent Palatin <vpalatin@chromium.org>
Reviewed-on: https://chromium-review.googlesource.com/267688
Reviewed-by: Todd Broch <tbroch@chromium.org>
-rw-r--r-- | board/honeybuns/board.c | 143 | ||||
-rw-r--r-- | board/honeybuns/board.h | 11 | ||||
-rw-r--r-- | board/honeybuns/ec.tasklist | 2 | ||||
-rw-r--r-- | board/honeybuns/gpio.inc | 3 | ||||
-rw-r--r-- | board/honeybuns/usb_pd_policy.c | 243 | ||||
-rw-r--r-- | include/usb_pd.h | 8 |
6 files changed, 319 insertions, 91 deletions
diff --git a/board/honeybuns/board.c b/board/honeybuns/board.c index d7d504a581..bdcedf5599 100644 --- a/board/honeybuns/board.c +++ b/board/honeybuns/board.c @@ -16,10 +16,12 @@ #include "task.h" #include "timer.h" #include "usb.h" +#include "usb_bb.h" #include "usb_pd.h" #include "util.h" - +static volatile uint64_t hpd_prev_ts; +static volatile int hpd_prev_level; void vbus_event(enum gpio_signal signal) { @@ -29,6 +31,71 @@ void vbus_event(enum gpio_signal signal) #include "gpio_list.h" +/** + * 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) +{ + pd_send_hpd(0, hpd_irq); +} +DECLARE_DEFERRED(hpd_irq_deferred); + +void hpd_lvl_deferred(void) +{ + int level = gpio_get_level(GPIO_DP_HPD); + + if (level != hpd_prev_level) + /* It's a glitch while in deferred or canceled action */ + return; + + pd_send_hpd(0, (level) ? hpd_high : hpd_low); +} +DECLARE_DEFERRED(hpd_lvl_deferred); + +void hpd_event(enum gpio_signal signal) +{ + 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, -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, 0); + else if (cur_delta >= HPD_USTREAM_DEBOUNCE_LVL) + hook_call_deferred(hpd_lvl_deferred, HPD_USTREAM_DEBOUNCE_LVL); + + hpd_prev_level = level; +} static void honeybuns_test_led_update(void) { @@ -58,6 +125,18 @@ void board_config_pre_init(void) STM32_SYSCFG_CFGR1 |= (1 << 9) | (1 << 10); } +/* Initialize board. */ +static void board_init(void) +{ + timestamp_t now; + + now = get_time(); + hpd_prev_level = gpio_get_level(GPIO_DP_HPD); + hpd_prev_ts = now.val; + gpio_enable_interrupt(GPIO_DP_HPD); +} + +DECLARE_HOOK(HOOK_INIT, board_init, HOOK_PRIO_DEFAULT); /* ADC channels */ const struct adc_t adc_channels[] = { @@ -88,7 +167,6 @@ BUILD_ASSERT(ARRAY_SIZE(usb_strings) == USB_STR_COUNT); void board_set_usb_mux(int port, enum typec_mux mux, enum usb_switch usb, int polarity) { - if (mux == TYPEC_MUX_NONE) { /* put the mux in the high impedance state */ gpio_set_level(GPIO_SS_MUX_OE_L, 1); @@ -141,3 +219,64 @@ int board_get_usb_mux(int port, const char **dp_str, const char **usb_str) } return 1; } + +/** + * USB configuration + * Any type-C device with alternate mode capabilities must have the following + * set of descriptors. + * + * 1. Standard Device + * 2. BOS + * 2a. Container ID + * 2b. Billboard Caps + */ +struct my_bos { + struct usb_bos_hdr_descriptor bos; + struct usb_contid_caps_descriptor contid_caps; + struct usb_bb_caps_base_descriptor bb_caps; + struct usb_bb_caps_svid_descriptor bb_caps_svids[1]; +}; + +static struct my_bos bos_desc = { + .bos = { + .bLength = USB_DT_BOS_SIZE, + .bDescriptorType = USB_DT_BOS, + .wTotalLength = (USB_DT_BOS_SIZE + USB_DT_CONTID_SIZE + + USB_BB_CAPS_BASE_SIZE + + USB_BB_CAPS_SVID_SIZE * 1), + .bNumDeviceCaps = 2, /* contid + bb_caps */ + }, + .contid_caps = { + .bLength = USB_DT_CONTID_SIZE, + .bDescriptorType = USB_DT_DEVICE_CAPABILITY, + .bDevCapabilityType = USB_DC_DTYPE_CONTID, + .bReserved = 0, + .ContainerID = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + }, + .bb_caps = { + .bLength = (USB_BB_CAPS_BASE_SIZE + USB_BB_CAPS_SVID_SIZE * 1), + .bDescriptorType = USB_DT_DEVICE_CAPABILITY, + .bDevCapabilityType = USB_DC_DTYPE_BILLBOARD, + .iAdditionalInfoURL = USB_STR_BB_URL, + .bNumberOfAlternateModes = 1, + .bPreferredAlternateMode = 1, + .VconnPower = 0, + .bmConfigured = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 + }, + .bReserved = 0, + }, + .bb_caps_svids = { + { + .wSVID = USB_SID_DISPLAYPORT, + .bAlternateMode = 1, + .iAlternateModeString = USB_STR_BB_URL, + }, + }, +}; + +const struct bos_context bos_ctx = { + .descp = (void *)&bos_desc, + .size = sizeof(struct my_bos), +}; + diff --git a/board/honeybuns/board.h b/board/honeybuns/board.h index e527e9f367..2e23e93b4b 100644 --- a/board/honeybuns/board.h +++ b/board/honeybuns/board.h @@ -20,6 +20,7 @@ #define CONFIG_HW_CRC #define CONFIG_I2C #undef CONFIG_LID_SWITCH +#define CONFIG_SHA256 #define CONFIG_STM_HWTIMER32 #undef CONFIG_TASK_PROFILING #define CONFIG_USB @@ -28,6 +29,10 @@ #undef CONFIG_USB_PD_ALT_MODE_DFP #define CONFIG_USB_PD_CUSTOM_VDM #undef CONFIG_USB_PD_DUAL_ROLE +#define CONFIG_USB_PD_HW_DEV_ID_BOARD_MAJOR USB_PD_HW_DEV_ID_HONEYBUNS +#define CONFIG_USB_PD_HW_DEV_ID_BOARD_MINOR 0 +#define CONFIG_USB_PD_IDENTITY_HW_VERS 1 +#define CONFIG_USB_PD_IDENTITY_SW_VERS 1 #define CONFIG_USB_PD_INTERNAL_COMP #define CONFIG_USB_PD_PORT_COUNT 1 #define CONFIG_USB_PD_TCPC @@ -36,13 +41,12 @@ #define CONFIG_USBC_VCONN #undef CONFIG_WATCHDOG_HELP - - /* I2C ports configuration */ #define I2C_PORT_MASTER 0 /* USB configuration */ #define CONFIG_USB_PID 0x5015 +#define CONFIG_USB_BCD_DEV 0x0001 /* v 0.01 */ /* By default, enable all console messages excepted USB */ #define CC_DEFAULT (CC_ALL & ~CC_MASK(CC_USB)) @@ -103,6 +107,9 @@ enum usb_strings { /* Enable/disable USB Hub */ void hx3_enable(int enable); +/* DisplayPort hotplug detection interrupt */ +void hpd_event(enum gpio_signal signal); + #endif /* !__ASSEMBLER__ */ /* USB interface indexes (use define rather than enum to expand them) */ diff --git a/board/honeybuns/ec.tasklist b/board/honeybuns/ec.tasklist index ad72bfe7bb..f6d5c9c3f5 100644 --- a/board/honeybuns/ec.tasklist +++ b/board/honeybuns/ec.tasklist @@ -20,4 +20,4 @@ TASK_ALWAYS(HOOKS, hook_task, NULL, TASK_STACK_SIZE) \ TASK_ALWAYS(USBCFG, hx3_task, NULL, TASK_STACK_SIZE) \ TASK_ALWAYS(CONSOLE, console_task, NULL, LARGER_TASK_STACK_SIZE) \ - TASK_ALWAYS(PD, pd_task, NULL, TASK_STACK_SIZE) + TASK_ALWAYS(PD, pd_task, NULL, LARGER_TASK_STACK_SIZE) diff --git a/board/honeybuns/gpio.inc b/board/honeybuns/gpio.inc index c6a8ffdf08..a2a8f23e79 100644 --- a/board/honeybuns/gpio.inc +++ b/board/honeybuns/gpio.inc @@ -5,8 +5,7 @@ * found in the LICENSE file. */ -/*GPIO_INT(DP_HPD, PIN(A, 0), GPIO_INT_BOTH, hpd_event)*/ -GPIO(DP_HPD, PIN(A, 0), GPIO_INPUT) +GPIO_INT(DP_HPD, PIN(A, 0), GPIO_INT_BOTH, hpd_event) /* PD RX/TX */ GPIO(USB_CC1_PD, PIN(A, 1), GPIO_ANALOG) diff --git a/board/honeybuns/usb_pd_policy.c b/board/honeybuns/usb_pd_policy.c index f9d6caf858..6f2f33505d 100644 --- a/board/honeybuns/usb_pd_policy.c +++ b/board/honeybuns/usb_pd_policy.c @@ -6,6 +6,7 @@ #include "charger.h" #include "common.h" #include "console.h" +#include "ec_commands.h" #include "gpio.h" #include "hooks.h" #include "registers.h" @@ -13,6 +14,7 @@ #include "task.h" #include "timer.h" #include "util.h" +#include "usb.h" #include "usb_pd.h" @@ -162,111 +164,184 @@ int pd_alt_mode(int port, uint16_t svid) return 0; } - /* ----------------- Vendor Defined Messages ------------------ */ -/* TODO () The VDM section needs to be updated for honeybuns */ -const struct svdm_response svdm_rsp = { - .identity = NULL, - .svids = NULL, - .modes = NULL, -}; +const uint32_t vdo_idh = VDO_IDH(0, /* data caps as USB host */ + 1, /* data caps as USB device */ + IDH_PTYPE_AMA, /* Alternate mode */ + 1, /* supports alt modes */ + USB_VID_GOOGLE); + +const uint32_t vdo_product = VDO_PRODUCT(CONFIG_USB_PID, CONFIG_USB_BCD_DEV); + +const uint32_t vdo_ama = VDO_AMA(CONFIG_USB_PD_IDENTITY_HW_VERS, + CONFIG_USB_PD_IDENTITY_SW_VERS, + 0, 0, 0, 0, /* SS[TR][12] */ + 0, /* Vconn power */ + 0, /* Vconn power required */ + 0, /* Vbus power required */ + AMA_USBSS_U31_GEN1 /* USB SS support */); + +static int svdm_response_identity(int port, uint32_t *payload) +{ + payload[VDO_I(IDH)] = vdo_idh; + payload[VDO_I(CSTAT)] = VDO_CSTAT(0); + payload[VDO_I(PRODUCT)] = vdo_product; + payload[VDO_I(AMA)] = vdo_ama; + return VDO_I(AMA) + 1; +} -int pd_custom_vdm(int port, int cnt, uint32_t *payload, - uint32_t **rpayload) +static int svdm_response_svids(int port, uint32_t *payload) { - int cmd = PD_VDO_CMD(payload[0]); - uint16_t dev_id = 0; + payload[1] = VDO_SVID(USB_SID_DISPLAYPORT, USB_VID_GOOGLE); + payload[2] = 0; + return 3; +} - /* make sure we have some payload */ - if (cnt == 0) - return 0; +#define OPOS_DP 1 +#define OPOS_GFU 1 - switch (cmd) { - case VDO_CMD_VERSION: - /* guarantee last byte of payload is null character */ - *(payload + cnt - 1) = 0; - CPRINTF("version: %s\n", (char *)(payload+1)); - break; - case VDO_CMD_READ_INFO: - case VDO_CMD_SEND_INFO: - /* if last word is present, it contains lots of info */ - if (cnt == 7) { - dev_id = VDO_INFO_HW_DEV_ID(payload[6]); - CPRINTF("DevId:%d.%d SW:%d RW:%d\n", - HW_DEV_ID_MAJ(dev_id), - HW_DEV_ID_MIN(dev_id), - VDO_INFO_SW_DBG_VER(payload[6]), - VDO_INFO_IS_RW(payload[6])); - } - /* copy hash */ - if (cnt >= 6) - pd_dev_store_rw_hash(port, dev_id, payload + 1, - SYSTEM_IMAGE_UNKNOWN); +const uint32_t vdo_dp_modes[1] = { + VDO_MODE_DP(0, /* UFP pin cfg supported : none */ + MODE_DP_PIN_C | MODE_DP_PIN_D, /* DFP pin cfg supported */ + 0, /* usb2.0 signalling even in AMode */ + CABLE_PLUG, /* its a plug */ + MODE_DP_V13, /* DPv1.3 Support, no Gen2 */ + MODE_DP_SNK) /* Its a sink only */ +}; - break; +const uint32_t vdo_goog_modes[1] = { + VDO_MODE_GOOGLE(MODE_GOOGLE_FU) +}; + +static int svdm_response_modes(int port, uint32_t *payload) +{ + if (PD_VDO_VID(payload[0]) == USB_SID_DISPLAYPORT) { + memcpy(payload + 1, vdo_dp_modes, sizeof(vdo_dp_modes)); + return ARRAY_SIZE(vdo_dp_modes) + 1; + } else if (PD_VDO_VID(payload[0]) == USB_VID_GOOGLE) { + memcpy(payload + 1, vdo_goog_modes, sizeof(vdo_goog_modes)); + return ARRAY_SIZE(vdo_goog_modes) + 1; + } else { + return 0; /* nak */ } +} - return 0; +static int dp_status(int port, uint32_t *payload) +{ + int opos = PD_VDO_OPOS(payload[0]); + int hpd = gpio_get_level(GPIO_DP_HPD); + if (opos != OPOS_DP) + return 0; /* nak */ + + payload[1] = VDO_DP_STATUS(0, /* IRQ_HPD */ + (hpd == 1), /* HPD_HI|LOW */ + 0, /* request exit DP */ + 0, /* request exit USB */ + 1, /* MF pref */ + gpio_get_level(GPIO_PD_SBU_ENABLE), + 0, /* power low */ + 0x2); + return 2; } -static int svdm_enter_dp_mode(int port, uint32_t mode_caps) +static int dp_config(int port, uint32_t *payload) { - /* Only enter mode if device is DFP_D capable */ - if (mode_caps & MODE_DP_SNK) { - CPRINTF("Entering mode w/ vdo = %08x\n", mode_caps); - return 0; - } + /* is it a 2+2 or 4 DP lanes mode ? */ + enum typec_mux mux = PD_DP_CFG_PIN(payload[1]) & MODE_DP_PIN_MF_MASK ? + TYPEC_MUX_DOCK : TYPEC_MUX_DP; + + if (PD_DP_CFG_DPON(payload[1])) + gpio_set_level(GPIO_PD_SBU_ENABLE, 1); + /* Get the DP lanes (or DP+USB SS depending on the mode) */ + board_set_usb_mux(port, mux, USB_SWITCH_CONNECT, pd_get_polarity(port)); - return -1; + return 1; } -static int dp_on; +static int svdm_enter_mode(int port, uint32_t *payload) +{ + int rv = 0; /* will generate a NAK */ + + /* SID & mode request is valid */ + if ((PD_VDO_VID(payload[0]) == USB_SID_DISPLAYPORT) && + (PD_VDO_OPOS(payload[0]) == OPOS_DP)) { + alt_mode[PD_AMODE_DISPLAYPORT] = OPOS_DP; + rv = 1; + pd_log_event(PD_EVENT_VIDEO_DP_MODE, 0, 1, NULL); + } else if ((PD_VDO_VID(payload[0]) == USB_VID_GOOGLE) && + (PD_VDO_OPOS(payload[0]) == OPOS_GFU)) { + alt_mode[PD_AMODE_GOOGLE] = OPOS_GFU; + rv = 1; + } + + if (rv) + /* + * If we failed initial mode entry we'll have enumerated the USB + * Billboard class. If so we should disconnect. + */ + usb_disconnect(); -static int svdm_dp_status(int port, uint32_t *payload) + return rv; +} + +static int svdm_exit_mode(int port, uint32_t *payload) { - payload[0] = VDO(USB_SID_DISPLAYPORT, 1, CMD_DP_STATUS); - payload[1] = VDO_DP_STATUS(0, /* HPD IRQ ... not applicable */ - 0, /* HPD level ... not applicable */ - 0, /* exit DP? ... no */ - 0, /* usb mode? ... no */ - 0, /* multi-function ... no */ - dp_on, - 0, /* power low? ... no */ - dp_on); - return 2; + if (PD_VDO_VID(payload[0]) == USB_SID_DISPLAYPORT) { + gpio_set_level(GPIO_PD_SBU_ENABLE, 0); + alt_mode[PD_AMODE_DISPLAYPORT] = 0; + pd_log_event(PD_EVENT_VIDEO_DP_MODE, 0, 0, NULL); + } else if (PD_VDO_VID(payload[0]) == USB_VID_GOOGLE) { + alt_mode[PD_AMODE_GOOGLE] = 0; + } else { + CPRINTF("Unknown exit mode req:0x%08x\n", payload[0]); + } + + return 1; /* Must return ACK */ +} + +static struct amode_fx dp_fx = { + .status = &dp_status, + .config = &dp_config, }; -static int svdm_dp_config(int port, uint32_t *payload) -{ - board_set_usb_mux(port, TYPEC_MUX_DP, USB_SWITCH_CONNECT, - pd_get_polarity(port)); - dp_on = 1; - payload[0] = VDO(USB_SID_DISPLAYPORT, 1, CMD_DP_CONFIG); - payload[1] = VDO_DP_CFG(MODE_DP_PIN_E, /* pin mode */ - 1, /* DPv1.3 signaling */ - 2); /* UFP connected */ - return 2; +const struct svdm_response svdm_rsp = { + .identity = &svdm_response_identity, + .svids = &svdm_response_svids, + .modes = &svdm_response_modes, + .enter_mode = &svdm_enter_mode, + .amode = &dp_fx, + .exit_mode = &svdm_exit_mode, }; -static int svdm_dp_attention(int port, uint32_t *payload) +int pd_custom_vdm(int port, int cnt, uint32_t *payload, + uint32_t **rpayload) { - return 1; /* ack */ -} + int rsize; -static void svdm_exit_dp_mode(int port) -{ - CPRINTF("Exiting mode\n"); - /* return to safe config */ + if (PD_VDO_VID(payload[0]) != USB_VID_GOOGLE || + !alt_mode[PD_AMODE_GOOGLE]) + return 0; + + *rpayload = payload; + + rsize = pd_custom_flash_vdm(port, cnt, payload); + if (!rsize) { + int cmd = PD_VDO_CMD(payload[0]); + switch (cmd) { +#ifdef CONFIG_USB_PD_LOGGING + case VDO_CMD_GET_LOG: + rsize = pd_vdm_get_log_entry(payload); + break; +#endif + default: + /* Unknown : do not answer */ + return 0; + } + } + + /* respond (positively) to the request */ + payload[0] |= VDO_SRC_RESPONDER; + + return rsize; } -const struct svdm_amode_fx supported_modes[] = { - { - .svid = USB_SID_DISPLAYPORT, - .enter = &svdm_enter_dp_mode, - .status = &svdm_dp_status, - .config = &svdm_dp_config, - .attention = &svdm_dp_attention, - .exit = &svdm_exit_dp_mode, - }, -}; -const int supported_modes_cnt = ARRAY_SIZE(supported_modes); diff --git a/include/usb_pd.h b/include/usb_pd.h index 7e3a0a61e3..640c650a74 100644 --- a/include/usb_pd.h +++ b/include/usb_pd.h @@ -576,6 +576,13 @@ struct pd_policy { #define PD_DP_CFG_DPON(x) (((x & 0x3) == 1) || ((x & 0x3) == 2)) /* + * Get the pin assignment mask + * for backward compatibility, if it is null, + * get the former sink pin assignment we used to be in <23:16>. + */ +#define PD_DP_CFG_PIN(x) ((((x) >> 8) & 0xff) ? (((x) >> 8) & 0xff) \ + : (((x) >> 16) & 0xff)) +/* * ChromeOS specific PD device Hardware IDs. Used to identify unique * products and used in VDO_INFO. Note this field is 10 bits. */ @@ -584,6 +591,7 @@ struct pd_policy { #define USB_PD_HW_DEV_ID_MINIMUFFIN 2 #define USB_PD_HW_DEV_ID_DINGDONG 3 #define USB_PD_HW_DEV_ID_HOHO 4 +#define USB_PD_HW_DEV_ID_HONEYBUNS 5 /* * ChromeOS specific VDO_CMD_READ_INFO responds with device info including: |