Skip to content

Commit d48efb5

Browse files
tmon-nordicrlubos
authored andcommitted
[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]> (cherry picked from commit e54edad)
1 parent 2b51f25 commit d48efb5

File tree

1 file changed

+185
-0
lines changed

1 file changed

+185
-0
lines changed

drivers/usb/udc/udc_dwc2_vendor_quirks.h

Lines changed: 185 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -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

Comments
 (0)