@@ -312,6 +312,204 @@ DT_INST_FOREACH_STATUS_OKAY(QUIRK_NRF_USBHS_DEFINE)
312
312
313
313
#endif /*DT_HAS_COMPAT_STATUS_OKAY(nordic_nrf_usbhs) */
314
314
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
+
315
513
/* Add next vendor quirks definition above this line */
316
514
317
515
#endif /* ZEPHYR_DRIVERS_USB_UDC_DWC2_VENDOR_QUIRKS_H */
0 commit comments