From 5bfeef6bb36f7fe450f87c14e7d2791e0a33406a Mon Sep 17 00:00:00 2001 From: Edward Hill Date: Mon, 29 Jan 2018 11:27:56 -0700 Subject: [PATCH] grunt: Set USB-C DP HPD GPIOs correctly. Change the EC to drive the Hotplug Detect (HPD) GPIOs. Grunt HW has these driven from EC to SOC, unlike coral which had the TCPCs drive the HPD signals to SOC. BUG=b:71810897 BRANCH=none TEST=external display works using USB-C to DP adapter on both ports Change-Id: I22ec9eecc5bdf9c6463dd3ce208d051faf15c57a Signed-off-by: Edward Hill Reviewed-on: https://chromium-review.googlesource.com/892099 Reviewed-by: Aaron Durbin Reviewed-by: Jett Rink --- board/grunt/usb_pd_policy.c | 56 ++++++++++++++++++++++++++++++++----- 1 file changed, 49 insertions(+), 7 deletions(-) diff --git a/board/grunt/usb_pd_policy.c b/board/grunt/usb_pd_policy.c index 3f873beee4..52f9f9450c 100644 --- a/board/grunt/usb_pd_policy.c +++ b/board/grunt/usb_pd_policy.c @@ -313,39 +313,81 @@ static int svdm_dp_config(int port, uint32_t *payload) return 2; }; +/* + * timestamp of the next possible toggle to ensure the 2-ms spacing + * between IRQ_HPD. + */ +static uint64_t hpd_deadline[CONFIG_USB_PD_PORT_COUNT]; + +#define PORT_TO_HPD(port) ((port) ? GPIO_USB_C1_DP_HPD : GPIO_USB_C0_DP_HPD) static void svdm_dp_post_config(int port) { - const struct usb_mux *mux = &usb_muxes[port]; + const struct usb_mux * const mux = &usb_muxes[port]; dp_flags[port] |= DP_FLAGS_DP_ON; if (!(dp_flags[port] & DP_FLAGS_HPD_HI_PENDING)) return; + + gpio_set_level(PORT_TO_HPD(port), 1); + + /* set the minimum time delay (2ms) for the next HPD IRQ */ + hpd_deadline[port] = get_time().val + HPD_USTREAM_DEBOUNCE_LVL; mux->hpd_update(port, 1, 0); } static int svdm_dp_attention(int port, uint32_t *payload) { + int cur_lvl; int lvl = PD_VDO_DPSTS_HPD_LVL(payload[1]); int irq = PD_VDO_DPSTS_HPD_IRQ(payload[1]); - const struct usb_mux *mux = &usb_muxes[port]; + enum gpio_signal hpd = PORT_TO_HPD(port); + const struct usb_mux * const mux = &usb_muxes[port]; + cur_lvl = gpio_get_level(hpd); dp_status[port] = payload[1]; + + /* Its initial DP status message prior to config */ if (!(dp_flags[port] & DP_FLAGS_DP_ON)) { if (lvl) dp_flags[port] |= DP_FLAGS_HPD_HI_PENDING; - return 1; + return 1; /* ack */ + } + + if (irq && cur_lvl) { + uint64_t now = get_time().val; + /* wait for the minimum spacing between IRQ_HPD if needed */ + if (now < hpd_deadline[port]) + usleep(hpd_deadline[port] - now); + + /* generate IRQ_HPD pulse */ + gpio_set_level(hpd, 0); + usleep(HPD_DSTREAM_DEBOUNCE_IRQ); + gpio_set_level(hpd, 1); + + /* set the minimum time delay (2ms) for the next HPD IRQ */ + hpd_deadline[port] = get_time().val + HPD_USTREAM_DEBOUNCE_LVL; + } else if (irq && !cur_lvl) { + /* + * IRQ can only be generated when the level is high, because + * the IRQ is signaled by a short low pulse from the high level. + */ + CPRINTF("ERR:HPD:IRQ&LOW\n"); + return 0; /* nak */ + } else { + gpio_set_level(hpd, lvl); + /* set the minimum time delay (2ms) for the next HPD IRQ */ + hpd_deadline[port] = get_time().val + HPD_USTREAM_DEBOUNCE_LVL; } mux->hpd_update(port, lvl, irq); - - /* ack */ - return 1; + return 1; /* ack */ } static void svdm_exit_dp_mode(int port) { - const struct usb_mux *mux = &usb_muxes[port]; + const struct usb_mux * const mux = &usb_muxes[port]; svdm_safe_dp_mode(port); + gpio_set_level(PORT_TO_HPD(port), 0); mux->hpd_update(port, 0, 0); }