drivers: udc_dwc2: Abort wait when PHY is not clocked
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ń <tomasz.mon@nordicsemi.no>
This commit is contained in:
parent
5ac6335505
commit
e2afcafca5
@ -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);
|
||||
|
||||
@ -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 */
|
||||
|
||||
@ -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)
|
||||
|
||||
Loading…
Reference in New Issue
Block a user