-
Notifications
You must be signed in to change notification settings - Fork 7.8k
drivers: udc_dwc2: Add nRF54LM20A vendor quirks #92842
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -312,6 +312,204 @@ DT_INST_FOREACH_STATUS_OKAY(QUIRK_NRF_USBHS_DEFINE) | |
|
||
#endif /*DT_HAS_COMPAT_STATUS_OKAY(nordic_nrf_usbhs) */ | ||
|
||
#if DT_HAS_COMPAT_STATUS_OKAY(nordic_nrf_usbhs_nrf54l) | ||
|
||
#define USBHS_DT_WRAPPER_REG_ADDR(n) UINT_TO_POINTER(DT_INST_REG_ADDR_BY_NAME(n, wrapper)) | ||
|
||
#include <nrf.h> | ||
#include <zephyr/logging/log.h> | ||
#include <zephyr/drivers/clock_control.h> | ||
#include <zephyr/drivers/clock_control/nrf_clock_control.h> | ||
|
||
#define NRF_DEFAULT_IRQ_PRIORITY 1 | ||
|
||
/* | ||
* On USBHS, we cannot access the DWC2 register until VBUS is detected and | ||
* valid. If the user tries to force usbd_enable() and the corresponding | ||
* udc_enable() without a "VBUS ready" notification, the event wait will block | ||
* until a valid VBUS signal is detected or until the | ||
* CONFIG_UDC_DWC2_USBHS_VBUS_READY_TIMEOUT timeout expires. | ||
*/ | ||
static K_EVENT_DEFINE(usbhs_events); | ||
#define USBHS_VBUS_READY BIT(0) | ||
|
||
static struct onoff_manager *pclk24m_mgr; | ||
static struct onoff_client pclk24m_cli; | ||
|
||
static void vregusb_isr(const void *arg) | ||
{ | ||
const struct device *dev = arg; | ||
|
||
if (NRF_VREGUSB->EVENTS_VBUSDETECTED) { | ||
NRF_VREGUSB->EVENTS_VBUSDETECTED = 0; | ||
k_event_post(&usbhs_events, USBHS_VBUS_READY); | ||
udc_submit_event(dev, UDC_EVT_VBUS_READY, 0); | ||
} | ||
|
||
if (NRF_VREGUSB->EVENTS_VBUSREMOVED) { | ||
NRF_VREGUSB->EVENTS_VBUSREMOVED = 0; | ||
k_event_set_masked(&usbhs_events, 0, USBHS_VBUS_READY); | ||
udc_submit_event(dev, UDC_EVT_VBUS_REMOVED, 0); | ||
} | ||
} | ||
|
||
static inline int usbhs_init_vreg_and_clock(const struct device *dev) | ||
{ | ||
IRQ_CONNECT(VREGUSB_IRQn, NRF_DEFAULT_IRQ_PRIORITY, | ||
vregusb_isr, DEVICE_DT_INST_GET(0), 0); | ||
Comment on lines
+358
to
+359
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why use There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. There is no devicetree node for VREGUSB peripheral (there is devicetree for USBHS peripheral, but it is VREGUSB that is used here in vendor quirk). There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is acceptable for the time being that there is no node as explained above. This will also be looked at again by @jfischer-no in the near future for some additional work. |
||
|
||
NRF_VREGUSB->INTEN = VREGUSB_INTEN_VBUSDETECTED_Msk | | ||
VREGUSB_INTEN_VBUSREMOVED_Msk; | ||
NRF_VREGUSB->TASKS_START = 1; | ||
|
||
/* TODO: Determine conditions when VBUSDETECTED is not generated */ | ||
if (sys_read32((mem_addr_t)NRF_VREGUSB + 0x400) & BIT(2)) { | ||
k_event_post(&usbhs_events, USBHS_VBUS_READY); | ||
udc_submit_event(dev, UDC_EVT_VBUS_READY, 0); | ||
} | ||
|
||
irq_enable(VREGUSB_IRQn); | ||
pclk24m_mgr = z_nrf_clock_control_get_onoff(CLOCK_CONTROL_NRF_SUBSYS_HF24M); | ||
|
||
return 0; | ||
} | ||
|
||
static inline int usbhs_enable_core(const struct device *dev) | ||
{ | ||
LOG_MODULE_DECLARE(udc_dwc2, CONFIG_UDC_DRIVER_LOG_LEVEL); | ||
NRF_USBHS_Type *wrapper = USBHS_DT_WRAPPER_REG_ADDR(0); | ||
k_timeout_t timeout = K_FOREVER; | ||
int err; | ||
|
||
if (!k_event_wait(&usbhs_events, USBHS_VBUS_READY, false, K_NO_WAIT)) { | ||
LOG_WRN("VBUS is not ready, block udc_enable()"); | ||
if (!k_event_wait(&usbhs_events, USBHS_VBUS_READY, false, timeout)) { | ||
return -ETIMEDOUT; | ||
} | ||
} | ||
|
||
/* Request PCLK24M using clock control driver */ | ||
sys_notify_init_spinwait(&pclk24m_cli.notify); | ||
err = onoff_request(pclk24m_mgr, &pclk24m_cli); | ||
if (err < 0) { | ||
LOG_ERR("Failed to start PCLK24M %d", err); | ||
return err; | ||
} | ||
|
||
/* Power up peripheral */ | ||
wrapper->ENABLE = USBHS_ENABLE_CORE_Msk; | ||
|
||
/* Set ID to Device and force D+ pull-up off for now */ | ||
wrapper->PHY.OVERRIDEVALUES = (1 << 31); | ||
wrapper->PHY.INPUTOVERRIDE = (1 << 31) | USBHS_PHY_INPUTOVERRIDE_VBUSVALID_Msk; | ||
|
||
/* Release PHY power-on reset */ | ||
wrapper->ENABLE = USBHS_ENABLE_PHY_Msk | USBHS_ENABLE_CORE_Msk; | ||
|
||
/* Wait for PHY clock to start */ | ||
k_busy_wait(45); | ||
|
||
/* Release DWC2 reset */ | ||
wrapper->TASKS_START = 1UL; | ||
|
||
/* Wait for clock to start to avoid hang on too early register read */ | ||
k_busy_wait(1); | ||
|
||
/* DWC2 opmode is now guaranteed to be Non-Driving, allow D+ pull-up to | ||
* become active once driver clears DCTL SftDiscon bit. | ||
*/ | ||
wrapper->PHY.INPUTOVERRIDE = (1 << 31); | ||
|
||
return 0; | ||
} | ||
|
||
static inline int usbhs_disable_core(const struct device *dev) | ||
{ | ||
LOG_MODULE_DECLARE(udc_dwc2, CONFIG_UDC_DRIVER_LOG_LEVEL); | ||
NRF_USBHS_Type *wrapper = USBHS_DT_WRAPPER_REG_ADDR(0); | ||
int err; | ||
|
||
/* Set ID to Device and forcefully disable D+ pull-up */ | ||
wrapper->PHY.OVERRIDEVALUES = (1 << 31); | ||
wrapper->PHY.INPUTOVERRIDE = (1 << 31) | USBHS_PHY_INPUTOVERRIDE_VBUSVALID_Msk; | ||
|
||
wrapper->ENABLE = 0UL; | ||
|
||
/* Release PCLK24M using clock control driver */ | ||
err = onoff_cancel_or_release(pclk24m_mgr, &pclk24m_cli); | ||
if (err < 0) { | ||
LOG_ERR("Failed to stop PCLK24M %d", err); | ||
return err; | ||
} | ||
|
||
return 0; | ||
} | ||
|
||
static inline int usbhs_disable_vreg(const struct device *dev) | ||
{ | ||
NRF_VREGUSB->INTEN = 0; | ||
NRF_VREGUSB->TASKS_STOP = 1; | ||
|
||
return 0; | ||
} | ||
|
||
static inline int usbhs_init_caps(const struct device *dev) | ||
{ | ||
struct udc_data *data = dev->data; | ||
|
||
data->caps.can_detect_vbus = true; | ||
data->caps.hs = true; | ||
|
||
return 0; | ||
} | ||
|
||
static inline int usbhs_is_phy_clk_off(const struct device *dev) | ||
{ | ||
return !k_event_test(&usbhs_events, USBHS_VBUS_READY); | ||
} | ||
|
||
static inline int usbhs_post_hibernation_entry(const struct device *dev) | ||
{ | ||
const struct udc_dwc2_config *const config = dev->config; | ||
struct usb_dwc2_reg *const base = config->base; | ||
NRF_USBHS_Type *wrapper = USBHS_DT_WRAPPER_REG_ADDR(0); | ||
|
||
sys_set_bits((mem_addr_t)&base->pcgcctl, USB_DWC2_PCGCCTL_GATEHCLK); | ||
|
||
wrapper->TASKS_STOP = 1; | ||
|
||
return 0; | ||
} | ||
|
||
static inline int usbhs_pre_hibernation_exit(const struct device *dev) | ||
{ | ||
const struct udc_dwc2_config *const config = dev->config; | ||
struct usb_dwc2_reg *const base = config->base; | ||
NRF_USBHS_Type *wrapper = USBHS_DT_WRAPPER_REG_ADDR(0); | ||
|
||
sys_clear_bits((mem_addr_t)&base->pcgcctl, USB_DWC2_PCGCCTL_GATEHCLK); | ||
|
||
wrapper->TASKS_START = 1; | ||
|
||
return 0; | ||
} | ||
|
||
#define QUIRK_NRF_USBHS_DEFINE(n) \ | ||
struct dwc2_vendor_quirks dwc2_vendor_quirks_##n = { \ | ||
.init = usbhs_init_vreg_and_clock, \ | ||
.pre_enable = usbhs_enable_core, \ | ||
.disable = usbhs_disable_core, \ | ||
.shutdown = usbhs_disable_vreg, \ | ||
.caps = usbhs_init_caps, \ | ||
.is_phy_clk_off = usbhs_is_phy_clk_off, \ | ||
.post_hibernation_entry = usbhs_post_hibernation_entry, \ | ||
.pre_hibernation_exit = usbhs_pre_hibernation_exit, \ | ||
}; | ||
|
||
DT_INST_FOREACH_STATUS_OKAY(QUIRK_NRF_USBHS_DEFINE) | ||
|
||
#endif /*DT_HAS_COMPAT_STATUS_OKAY(nordic_nrf_usbhs_nrf54l) */ | ||
|
||
/* Add next vendor quirks definition above this line */ | ||
|
||
#endif /* ZEPHYR_DRIVERS_USB_UDC_DWC2_VENDOR_QUIRKS_H */ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.