diff options
author | Edward Hill <ecgh@chromium.org> | 2018-01-29 11:27:56 -0700 |
---|---|---|
committer | chrome-bot <chrome-bot@chromium.org> | 2018-02-01 21:29:38 -0800 |
commit | 5bfeef6bb36f7fe450f87c14e7d2791e0a33406a (patch) | |
tree | 0287ab79d2111f7cb70ce9b1648c1793565bb514 | |
parent | ebb34ee92f06eab6405397a6e5550dd13779c7d7 (diff) | |
download | chrome-ec-5bfeef6bb36f7fe450f87c14e7d2791e0a33406a.tar.gz |
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 <ecgh@chromium.org>
Reviewed-on: https://chromium-review.googlesource.com/892099
Reviewed-by: Aaron Durbin <adurbin@chromium.org>
Reviewed-by: Jett Rink <jettrink@chromium.org>
-rw-r--r-- | board/grunt/usb_pd_policy.c | 56 |
1 files 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 */ } - mux->hpd_update(port, lvl, irq); - /* ack */ - return 1; + 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); + 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); } |