From e2afcafca508f507a894df0c305a2bc5cae533f5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Mo=C5=84?= Date: Wed, 29 May 2024 13:06:09 +0200 Subject: [PATCH] drivers: udc_dwc2: Abort wait when PHY is not clocked MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit On nRF54H20DK the USB PHY is powered from VBUS. When the USB cable is not connected, the PHY is not powered and the PHY clock disappears. Because the GOUTNAKEFF and INEPNAKEFF can only ever be set when PHY clock is active, the waits for these bits do timeout if cable is disconnected. Workaround the issue by aborting the wait if vendor quirk indicates that PHY clock has abruptly vanished. Signed-off-by: Tomasz Moń --- drivers/usb/udc/udc_dwc2.c | 18 +++++++++++++----- drivers/usb/udc/udc_dwc2.h | 3 +++ drivers/usb/udc/udc_dwc2_vendor_quirks.h | 6 ++++++ 3 files changed, 22 insertions(+), 5 deletions(-) diff --git a/drivers/usb/udc/udc_dwc2.c b/drivers/usb/udc/udc_dwc2.c index 98ded9e4b5d..9845efb4288 100644 --- a/drivers/usb/udc/udc_dwc2.c +++ b/drivers/usb/udc/udc_dwc2.c @@ -1176,7 +1176,8 @@ static int dwc2_unset_dedicated_fifo(const struct device *dev, return 0; } -static void dwc2_wait_for_bit(mem_addr_t addr, uint32_t bit) +static void dwc2_wait_for_bit(const struct device *dev, + mem_addr_t addr, uint32_t bit) { k_timepoint_t timeout = sys_timepoint_calc(K_MSEC(100)); @@ -1187,6 +1188,13 @@ static void dwc2_wait_for_bit(mem_addr_t addr, uint32_t bit) * Busy looping is most likely fine unless profiling shows otherwise. */ while (!(sys_read32(addr) & bit)) { + if (dwc2_quirk_is_phy_clk_off(dev)) { + /* No point in waiting, because the bit can only be set + * when the PHY is actively clocked. + */ + return; + } + if (sys_timepoint_expired(timeout)) { LOG_ERR("Timeout waiting for bit 0x%08X at 0x%08X", bit, (uint32_t)addr); @@ -1241,7 +1249,7 @@ static void udc_dwc2_ep_disable(const struct device *dev, dctl &= ~USB_DWC2_DCTL_SGOUTNAK; } - dwc2_wait_for_bit(gintsts_reg, USB_DWC2_GINTSTS_GOUTNAKEFF); + dwc2_wait_for_bit(dev, gintsts_reg, USB_DWC2_GINTSTS_GOUTNAKEFF); /* The application cannot disable control OUT endpoint 0. */ if (ep_idx != 0) { @@ -1257,7 +1265,7 @@ static void udc_dwc2_ep_disable(const struct device *dev, sys_write32(dxepctl, dxepctl_reg); if (ep_idx != 0) { - dwc2_wait_for_bit(doepint_reg, USB_DWC2_DOEPINT_EPDISBLD); + dwc2_wait_for_bit(dev, doepint_reg, USB_DWC2_DOEPINT_EPDISBLD); } /* Clear Endpoint Disabled interrupt */ @@ -1277,12 +1285,12 @@ static void udc_dwc2_ep_disable(const struct device *dev, } sys_write32(dxepctl, dxepctl_reg); - dwc2_wait_for_bit(diepint_reg, USB_DWC2_DIEPINT_INEPNAKEFF); + dwc2_wait_for_bit(dev, diepint_reg, USB_DWC2_DIEPINT_INEPNAKEFF); dxepctl |= USB_DWC2_DEPCTL_EPENA | USB_DWC2_DEPCTL_EPDIS; sys_write32(dxepctl, dxepctl_reg); - dwc2_wait_for_bit(diepint_reg, USB_DWC2_DIEPINT_EPDISBLD); + dwc2_wait_for_bit(dev, diepint_reg, USB_DWC2_DIEPINT_EPDISBLD); /* Clear Endpoint Disabled interrupt */ sys_write32(USB_DWC2_DIEPINT_EPDISBLD, diepint_reg); diff --git a/drivers/usb/udc/udc_dwc2.h b/drivers/usb/udc/udc_dwc2.h index b54cb82fd2a..07cd71bcb9c 100644 --- a/drivers/usb/udc/udc_dwc2.h +++ b/drivers/usb/udc/udc_dwc2.h @@ -28,6 +28,8 @@ struct dwc2_vendor_quirks { int (*irq_clear)(const struct device *dev); /* Called on driver pre-init */ int (*caps)(const struct device *dev); + /* Called while waiting for bits that require PHY to be clocked */ + int (*is_phy_clk_off)(const struct device *dev); }; /* Driver configuration per instance */ @@ -69,5 +71,6 @@ DWC2_QUIRK_FUNC_DEFINE(disable) DWC2_QUIRK_FUNC_DEFINE(shutdown) DWC2_QUIRK_FUNC_DEFINE(irq_clear) DWC2_QUIRK_FUNC_DEFINE(caps) +DWC2_QUIRK_FUNC_DEFINE(is_phy_clk_off) #endif /* ZEPHYR_DRIVERS_USB_UDC_DWC2_H */ diff --git a/drivers/usb/udc/udc_dwc2_vendor_quirks.h b/drivers/usb/udc/udc_dwc2_vendor_quirks.h index 2311dd80308..57ff4c4de04 100644 --- a/drivers/usb/udc/udc_dwc2_vendor_quirks.h +++ b/drivers/usb/udc/udc_dwc2_vendor_quirks.h @@ -244,6 +244,11 @@ static inline int usbhs_init_caps(const struct device *dev) return 0; } +static inline int usbhs_is_phy_clk_off(const struct device *dev) +{ + return !k_event_test(&usbhs_events, USBHS_VBUS_READY); +} + #define QUIRK_NRF_USBHS_DEFINE(n) \ struct dwc2_vendor_quirks dwc2_vendor_quirks_##n = { \ .init = usbhs_enable_nrfs_service, \ @@ -252,6 +257,7 @@ static inline int usbhs_init_caps(const struct device *dev) .shutdown = usbhs_disable_nrfs_service, \ .irq_clear = usbhs_irq_clear, \ .caps = usbhs_init_caps, \ + .is_phy_clk_off = usbhs_is_phy_clk_off, \ }; DT_INST_FOREACH_STATUS_OKAY(QUIRK_NRF_USBHS_DEFINE)