Skip to content

Commit 66b938f

Browse files
tmon-nordiccfriedt
authored andcommitted
drivers: udc_dwc2: Add nRF54LM20A vendor quirks
Initial implementation of nRF54L quirks necessary for nRF54LM20A. Signed-off-by: Tomasz Moń <[email protected]> Signed-off-by: Johann Fischer <[email protected]>
1 parent 9759ca6 commit 66b938f

File tree

1 file changed

+198
-0
lines changed

1 file changed

+198
-0
lines changed

drivers/usb/udc/udc_dwc2_vendor_quirks.h

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

Comments
 (0)