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); }