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