@@ -315,6 +315,191 @@ DT_INST_FOREACH_STATUS_OKAY(QUIRK_NRF_USBHS_DEFINE)
315315
316316#endif /*DT_HAS_COMPAT_STATUS_OKAY(nordic_nrf_usbhs) */
317317
318+ #if DT_HAS_COMPAT_STATUS_OKAY (nordic_nrf_usbhs_nrf54l )
319+
320+ #define DT_DRV_COMPAT snps_dwc2
321+
322+ #define USBHS_DT_WRAPPER_REG_ADDR (n ) UINT_TO_POINTER(DT_INST_REG_ADDR_BY_NAME(n, wrapper))
323+
324+ #include <nrf.h>
325+
326+ #define NRF_DEFAULT_IRQ_PRIORITY 1
327+
328+ /*
329+ * On USBHS, we cannot access the DWC2 register until VBUS is detected and
330+ * valid. If the user tries to force usbd_enable() and the corresponding
331+ * udc_enable() without a "VBUS ready" notification, the event wait will block
332+ * until a valid VBUS signal is detected or until the
333+ * CONFIG_UDC_DWC2_USBHS_VBUS_READY_TIMEOUT timeout expires.
334+ */
335+ static K_EVENT_DEFINE (usbhs_events );
336+ #define USBHS_VBUS_READY BIT(0)
337+
338+ static void vregusb_isr (const void * arg )
339+ {
340+ const struct device * dev = arg ;
341+
342+ if (NRF_VREGUSB -> EVENTS_VBUSDETECTED ) {
343+ NRF_VREGUSB -> EVENTS_VBUSDETECTED = 0 ;
344+ k_event_post (& usbhs_events , USBHS_VBUS_READY );
345+ udc_submit_event (dev , UDC_EVT_VBUS_READY , 0 );
346+ }
347+
348+ if (NRF_VREGUSB -> EVENTS_VBUSREMOVED ) {
349+ NRF_VREGUSB -> EVENTS_VBUSREMOVED = 0 ;
350+ k_event_set_masked (& usbhs_events , 0 , USBHS_VBUS_READY );
351+ udc_submit_event (dev , UDC_EVT_VBUS_REMOVED , 0 );
352+ }
353+ }
354+
355+ static inline int usbhs_enable_vreg (const struct device * dev )
356+ {
357+ IRQ_CONNECT (VREGUSB_IRQn , NRF_DEFAULT_IRQ_PRIORITY ,
358+ vregusb_isr , DEVICE_DT_INST_GET (0 ), 0 );
359+
360+ NRF_VREGUSB -> INTEN = VREGUSB_INTEN_VBUSDETECTED_Msk |
361+ VREGUSB_INTEN_VBUSREMOVED_Msk ;
362+ NRF_VREGUSB -> TASKS_START = 1 ;
363+
364+ /* TODO: Determine conditions when VBUSDETECTED is not generated */
365+ if (sys_read32 ((mem_addr_t )NRF_VREGUSB + 0x400 ) & BIT (2 )) {
366+ k_event_post (& usbhs_events , USBHS_VBUS_READY );
367+ udc_submit_event (dev , UDC_EVT_VBUS_READY , 0 );
368+ }
369+
370+ irq_enable (VREGUSB_IRQn );
371+
372+ return 0 ;
373+ }
374+
375+ static inline int usbhs_enable_core (const struct device * dev )
376+ {
377+ NRF_USBHS_Type * wrapper = USBHS_DT_WRAPPER_REG_ADDR (0 );
378+ k_timeout_t timeout = K_FOREVER ;
379+
380+ if (!k_event_wait (& usbhs_events , USBHS_VBUS_READY , false, K_NO_WAIT )) {
381+ LOG_WRN ("VBUS is not ready, block udc_enable()" );
382+ if (!k_event_wait (& usbhs_events , USBHS_VBUS_READY , false, timeout )) {
383+ return - ETIMEDOUT ;
384+ }
385+ }
386+
387+ /* TODO: Request PCLK24M using clock control driver */
388+ NRF_CLOCK -> TASKS_XO24MSTART = 1 ;
389+ while (NRF_CLOCK -> EVENTS_XO24MSTARTED == 0 ) {
390+ }
391+
392+ /* Power up peripheral */
393+ wrapper -> ENABLE = USBHS_ENABLE_CORE_Msk ;
394+
395+ /* Set ID to Device and force D+ pull-up off for now */
396+ wrapper -> PHY .OVERRIDEVALUES = (1 << 31 );
397+ wrapper -> PHY .INPUTOVERRIDE = (1 << 31 ) | USBHS_PHY_INPUTOVERRIDE_VBUSVALID_Msk ;
398+
399+ /* Release PHY power-on reset */
400+ wrapper -> ENABLE = USBHS_ENABLE_PHY_Msk | USBHS_ENABLE_CORE_Msk ;
401+
402+ /* Wait for PHY clock to start */
403+ k_busy_wait (45 );
404+
405+ /* Release DWC2 reset */
406+ wrapper -> TASKS_START = 1UL ;
407+
408+ /* Wait for clock to start to avoid hang on too early register read */
409+ k_busy_wait (1 );
410+
411+ /* DWC2 opmode is now guaranteed to be Non-Driving, allow D+ pull-up to
412+ * become active once driver clears DCTL SftDiscon bit.
413+ */
414+ wrapper -> PHY .INPUTOVERRIDE = (1 << 31 );
415+
416+ return 0 ;
417+ }
418+
419+ static inline int usbhs_disable_core (const struct device * dev )
420+ {
421+ NRF_USBHS_Type * wrapper = USBHS_DT_WRAPPER_REG_ADDR (0 );
422+
423+ /* Set ID to Device and forcefully disable D+ pull-up */
424+ wrapper -> PHY .OVERRIDEVALUES = (1 << 31 );
425+ wrapper -> PHY .INPUTOVERRIDE = (1 << 31 ) | USBHS_PHY_INPUTOVERRIDE_VBUSVALID_Msk ;
426+
427+ wrapper -> ENABLE = 0UL ;
428+
429+ /* TODO: Release PCLK24M using clock control driver */
430+ NRF_CLOCK -> EVENTS_XO24MSTARTED = 0 ;
431+ NRF_CLOCK -> TASKS_XO24MSTOP = 1 ;
432+
433+ return 0 ;
434+ }
435+
436+ static inline int usbhs_disable_vreg (const struct device * dev )
437+ {
438+ NRF_VREGUSB -> INTEN = 0 ;
439+ NRF_VREGUSB -> TASKS_STOP = 1 ;
440+
441+ return 0 ;
442+ }
443+
444+ static inline int usbhs_init_caps (const struct device * dev )
445+ {
446+ struct udc_data * data = dev -> data ;
447+
448+ data -> caps .can_detect_vbus = true;
449+ data -> caps .hs = true;
450+
451+ return 0 ;
452+ }
453+
454+ static inline int usbhs_is_phy_clk_off (const struct device * dev )
455+ {
456+ return !k_event_test (& usbhs_events , USBHS_VBUS_READY );
457+ }
458+
459+ static inline int usbhs_post_hibernation_entry (const struct device * dev )
460+ {
461+ const struct udc_dwc2_config * const config = dev -> config ;
462+ struct usb_dwc2_reg * const base = config -> base ;
463+ NRF_USBHS_Type * wrapper = USBHS_DT_WRAPPER_REG_ADDR (0 );
464+
465+ sys_set_bits ((mem_addr_t )& base -> pcgcctl , USB_DWC2_PCGCCTL_GATEHCLK );
466+
467+ wrapper -> TASKS_STOP = 1 ;
468+
469+ return 0 ;
470+ }
471+
472+ static inline int usbhs_pre_hibernation_exit (const struct device * dev )
473+ {
474+ const struct udc_dwc2_config * const config = dev -> config ;
475+ struct usb_dwc2_reg * const base = config -> base ;
476+ NRF_USBHS_Type * wrapper = USBHS_DT_WRAPPER_REG_ADDR (0 );
477+
478+ sys_clear_bits ((mem_addr_t )& base -> pcgcctl , USB_DWC2_PCGCCTL_GATEHCLK );
479+
480+ wrapper -> TASKS_START = 1 ;
481+
482+ return 0 ;
483+ }
484+
485+ #define QUIRK_NRF_USBHS_DEFINE (n ) \
486+ struct dwc2_vendor_quirks dwc2_vendor_quirks_##n = { \
487+ .init = usbhs_enable_vreg, \
488+ .pre_enable = usbhs_enable_core, \
489+ .disable = usbhs_disable_core, \
490+ .shutdown = usbhs_disable_vreg, \
491+ .caps = usbhs_init_caps, \
492+ .is_phy_clk_off = usbhs_is_phy_clk_off, \
493+ .post_hibernation_entry = usbhs_post_hibernation_entry, \
494+ .pre_hibernation_exit = usbhs_pre_hibernation_exit, \
495+ };
496+
497+ DT_INST_FOREACH_STATUS_OKAY (QUIRK_NRF_USBHS_DEFINE )
498+
499+ #undef DT_DRV_COMPAT
500+
501+ #endif /*DT_HAS_COMPAT_STATUS_OKAY(nordic_nrf_usbhs_nrf54l) */
502+
318503/* Add next vendor quirks definition above this line */
319504
320505#endif /* ZEPHYR_DRIVERS_USB_UDC_DWC2_VENDOR_QUIRKS_H */
0 commit comments