Skip to content

Commit 9a2b73a

Browse files
committed
[nrf noup] drivers: udc_dwc2: Add nRF54L quirks
Initial implementation of nRF54L quirks necessary for nRF54LM20A. To be upstreamed after TODO comments are resolved. Signed-off-by: Tomasz Moń <[email protected]>
1 parent aa04c0c commit 9a2b73a

File tree

1 file changed

+184
-0
lines changed

1 file changed

+184
-0
lines changed

drivers/usb/udc/udc_dwc2_vendor_quirks.h

Lines changed: 184 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -315,6 +315,190 @@ 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+
/* Power up peripheral */
392+
wrapper->ENABLE = USBHS_ENABLE_CORE_Msk;
393+
394+
/* Set ID to Device and force D+ pull-up off for now */
395+
wrapper->PHY.OVERRIDEVALUES = (1 << 31);
396+
wrapper->PHY.INPUTOVERRIDE = (1 << 31) | USBHS_PHY_INPUTOVERRIDE_VBUSVALID_Msk;
397+
398+
/* Release PHY power-on reset */
399+
wrapper->ENABLE = USBHS_ENABLE_PHY_Msk | USBHS_ENABLE_CORE_Msk;
400+
401+
/* Wait for PHY clock to start */
402+
k_busy_wait(45);
403+
404+
/* Release DWC2 reset */
405+
wrapper->TASKS_START = 1UL;
406+
407+
/* Wait for clock to start to avoid hang on too early register read */
408+
k_busy_wait(1);
409+
410+
/* DWC2 opmode is now guaranteed to be Non-Driving, allow D+ pull-up to
411+
* become active once driver clears DCTL SftDiscon bit.
412+
*/
413+
wrapper->PHY.INPUTOVERRIDE = (1 << 31);
414+
415+
return 0;
416+
}
417+
418+
static inline int usbhs_disable_core(const struct device *dev)
419+
{
420+
NRF_USBHS_Type *wrapper = USBHS_DT_WRAPPER_REG_ADDR(0);
421+
422+
/* Set ID to Device and forcefully disable D+ pull-up */
423+
wrapper->PHY.OVERRIDEVALUES = (1 << 31);
424+
wrapper->PHY.INPUTOVERRIDE = (1 << 31) | USBHS_PHY_INPUTOVERRIDE_VBUSVALID_Msk;
425+
426+
wrapper->ENABLE = 0UL;
427+
428+
/* TODO: Release PCLK24M using clock control driver */
429+
NRF_CLOCK->EVENTS_XO24MSTARTED = 0;
430+
NRF_CLOCK->TASKS_XO24MSTOP = 1;
431+
432+
return 0;
433+
}
434+
435+
static inline int usbhs_disable_vreg(const struct device *dev)
436+
{
437+
NRF_VREGUSB->INTEN = 0;
438+
NRF_VREGUSB->TASKS_STOP = 1;
439+
440+
return 0;
441+
}
442+
443+
static inline int usbhs_init_caps(const struct device *dev)
444+
{
445+
struct udc_data *data = dev->data;
446+
447+
data->caps.can_detect_vbus = true;
448+
data->caps.hs = true;
449+
450+
return 0;
451+
}
452+
453+
static inline int usbhs_is_phy_clk_off(const struct device *dev)
454+
{
455+
return !k_event_test(&usbhs_events, USBHS_VBUS_READY);
456+
}
457+
458+
static inline int usbhs_post_hibernation_entry(const struct device *dev)
459+
{
460+
const struct udc_dwc2_config *const config = dev->config;
461+
struct usb_dwc2_reg *const base = config->base;
462+
NRF_USBHS_Type *wrapper = USBHS_DT_WRAPPER_REG_ADDR(0);
463+
464+
sys_set_bits((mem_addr_t)&base->pcgcctl, USB_DWC2_PCGCCTL_GATEHCLK);
465+
466+
wrapper->TASKS_STOP = 1;
467+
468+
return 0;
469+
}
470+
471+
static inline int usbhs_pre_hibernation_exit(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_clear_bits((mem_addr_t)&base->pcgcctl, USB_DWC2_PCGCCTL_GATEHCLK);
478+
479+
wrapper->TASKS_START = 1;
480+
481+
return 0;
482+
}
483+
484+
#define QUIRK_NRF_USBHS_DEFINE(n) \
485+
struct dwc2_vendor_quirks dwc2_vendor_quirks_##n = { \
486+
.init = usbhs_enable_vreg, \
487+
.pre_enable = usbhs_enable_core, \
488+
.disable = usbhs_disable_core, \
489+
.shutdown = usbhs_disable_vreg, \
490+
.caps = usbhs_init_caps, \
491+
.is_phy_clk_off = usbhs_is_phy_clk_off, \
492+
.post_hibernation_entry = usbhs_post_hibernation_entry, \
493+
.pre_hibernation_exit = usbhs_pre_hibernation_exit, \
494+
};
495+
496+
DT_INST_FOREACH_STATUS_OKAY(QUIRK_NRF_USBHS_DEFINE)
497+
498+
#undef DT_DRV_COMPAT
499+
500+
#endif /*DT_HAS_COMPAT_STATUS_OKAY(nordic_nrf_usbhs_nrf54l) */
501+
318502
/* Add next vendor quirks definition above this line */
319503

320504
#endif /* ZEPHYR_DRIVERS_USB_UDC_DWC2_VENDOR_QUIRKS_H */

0 commit comments

Comments
 (0)