diff --git a/boards/nordic/nrf54lm20dk/nrf54lm20a_cpuapp_common.dtsi b/boards/nordic/nrf54lm20dk/nrf54lm20a_cpuapp_common.dtsi index b096e0e9ebcfd..6dcba9030425e 100644 --- a/boards/nordic/nrf54lm20dk/nrf54lm20a_cpuapp_common.dtsi +++ b/boards/nordic/nrf54lm20dk/nrf54lm20a_cpuapp_common.dtsi @@ -51,6 +51,10 @@ regulator-initial-mode = ; }; +&vregusb { + status = "okay"; +}; + &grtc { owned-channels = <0 1 2 3 4 5 6 7 8 9 10 11>; /* Channels 7-11 reserved for Zero Latency IRQs, 3-4 for FLPR */ diff --git a/drivers/regulator/CMakeLists.txt b/drivers/regulator/CMakeLists.txt index b57d1e8a77b51..e2aa24f3cb793 100644 --- a/drivers/regulator/CMakeLists.txt +++ b/drivers/regulator/CMakeLists.txt @@ -16,6 +16,7 @@ zephyr_library_sources_ifdef(CONFIG_REGULATOR_NPM1100 regulator_npm1100.c) zephyr_library_sources_ifdef(CONFIG_REGULATOR_NPM13XX regulator_npm13xx.c) zephyr_library_sources_ifdef(CONFIG_REGULATOR_NPM2100 regulator_npm2100.c) zephyr_library_sources_ifdef(CONFIG_REGULATOR_NPM6001 regulator_npm6001.c) +zephyr_library_sources_ifdef(CONFIG_REGULATOR_NRF_VREGUSB regulator_nrf_vregusb.c) zephyr_library_sources_ifdef(CONFIG_REGULATOR_PCA9420 regulator_pca9420.c) zephyr_library_sources_ifdef(CONFIG_REGULATOR_PCA9422 regulator_pca9422.c) zephyr_library_sources_ifdef(CONFIG_REGULATOR_PF1550 regulator_pf1550.c) diff --git a/drivers/regulator/Kconfig b/drivers/regulator/Kconfig index 02a93e6f4cf55..6c30b3020ea9e 100644 --- a/drivers/regulator/Kconfig +++ b/drivers/regulator/Kconfig @@ -38,6 +38,7 @@ source "drivers/regulator/Kconfig.npm1100" source "drivers/regulator/Kconfig.npm13xx" source "drivers/regulator/Kconfig.npm2100" source "drivers/regulator/Kconfig.npm6001" +source "drivers/regulator/Kconfig.nrf_vregusb" source "drivers/regulator/Kconfig.pca9420" source "drivers/regulator/Kconfig.pca9422" source "drivers/regulator/Kconfig.pf1550" diff --git a/drivers/regulator/Kconfig.nrf_vregusb b/drivers/regulator/Kconfig.nrf_vregusb new file mode 100644 index 0000000000000..679193f730aa6 --- /dev/null +++ b/drivers/regulator/Kconfig.nrf_vregusb @@ -0,0 +1,16 @@ +# Copyright Nordic Semiconductor ASA +# SPDX-License-Identifier: Apache-2.0 + +config REGULATOR_NRF_VREGUSB + bool "Nordic USB VBUS regulator driver" + default y + depends on DT_HAS_NORDIC_VREGUSB_REGULATOR_ENABLED + help + Enable Nordic USB VBUS regulator driver. + +config REGULATOR_NRF_VREGUSB_INIT_PRIORITY + int "Nordic VBUS regulator driver init priority" + default KERNEL_INIT_PRIORITY_DEVICE + depends on REGULATOR_NRF_VREGUSB + help + Init priority for the Nordic USB VBUS regulator driver. diff --git a/drivers/regulator/regulator_nrf_vregusb.c b/drivers/regulator/regulator_nrf_vregusb.c new file mode 100644 index 0000000000000..f772e7ec1b63a --- /dev/null +++ b/drivers/regulator/regulator_nrf_vregusb.c @@ -0,0 +1,133 @@ +/* + * Copyright Nordic Semiconductor ASA + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include +#include +#include + +LOG_MODULE_REGISTER(vregusb, CONFIG_REGULATOR_LOG_LEVEL); + +struct vregusb_config { + struct regulator_common_config common; + NRF_VREGUSB_Type *base; + void (*irq_enable_func)(const struct device *dev); + void (*irq_disable_func)(const struct device *dev); +}; + +struct vregusb_data { + struct regulator_common_data data; + regulator_callback_t cb; + const void *user_data; +}; + +static void vregusb_isr(void *const arg) +{ + const struct device *const dev = arg; + const struct vregusb_config *const config = dev->config; + struct vregusb_data *const data = dev->data; + NRF_VREGUSB_Type *const base = config->base; + struct regulator_event event; + + if (base->EVENTS_VBUSDETECTED) { + LOG_DBG("VBUS detected"); + base->EVENTS_VBUSDETECTED = 0; + event.type = REGULATOR_VOLTAGE_DETECTED; + } + + if (base->EVENTS_VBUSREMOVED) { + LOG_DBG("VBUS removed"); + base->EVENTS_VBUSREMOVED = 0; + event.type = REGULATOR_VOLTAGE_REMOVED; + } + + if (data->cb != NULL) { + data->cb(dev, &event, data->user_data); + } +} + +static int vregusb_enable(const struct device *const dev) +{ + const struct vregusb_config *const config = dev->config; + NRF_VREGUSB_Type *const base = config->base; + + base->INTEN = VREGUSB_INTEN_VBUSDETECTED_Msk | VREGUSB_INTEN_VBUSREMOVED_Msk; + config->irq_enable_func(dev); + + base->TASKS_START = 1; + + return 0; +} + +static int vregusb_disable(const struct device *const dev) +{ + const struct vregusb_config *const config = dev->config; + NRF_VREGUSB_Type *const base = config->base; + + config->irq_disable_func(dev); + base->TASKS_STOP = 1; + + return 0; +} + +static int vregusb_set_callback(const struct device *const dev, + regulator_callback_t cb, const void *const user_data) +{ + struct vregusb_data *const data = dev->data; + + data->cb = cb; + data->user_data = user_data; + + return 0; +} + +static int regulator_vregusb_init(const struct device *const dev) +{ + regulator_common_data_init(dev); + + return 0; +} + +static DEVICE_API(regulator, api) = { + .enable = vregusb_enable, + .disable = vregusb_disable, + .set_callback = vregusb_set_callback, +}; + +#define DT_DRV_COMPAT nordic_vregusb_regulator + +#define REGULATOR_VREGUSB_DEFINE(n) \ + static void irq_enable_func_##n(const struct device *const dev) \ + { \ + IRQ_CONNECT(DT_INST_IRQN(n), \ + DT_INST_IRQ(n, priority), \ + vregusb_isr, \ + DEVICE_DT_INST_GET(n), \ + 0); \ + \ + irq_enable(DT_INST_IRQN(n)); \ + } \ + \ + static void irq_disable_func_##n(const struct device *const dev) \ + { \ + irq_disable(DT_INST_IRQN(n)); \ + } \ + \ + static struct vregusb_data data_##n; \ + \ + static const struct vregusb_config config_##n = { \ + .base = (void *)(DT_INST_REG_ADDR(n)), \ + .common = REGULATOR_DT_INST_COMMON_CONFIG_INIT(n), \ + .irq_enable_func = irq_enable_func_##n, \ + .irq_disable_func = irq_disable_func_##n, \ + }; \ + \ + DEVICE_DT_INST_DEFINE(n, regulator_vregusb_init, NULL, \ + &data_##n, &config_##n, \ + POST_KERNEL, \ + CONFIG_REGULATOR_NRF_VREGUSB_INIT_PRIORITY, &api);\ + +DT_INST_FOREACH_STATUS_OKAY(REGULATOR_VREGUSB_DEFINE) diff --git a/drivers/usb/udc/Kconfig.dwc2 b/drivers/usb/udc/Kconfig.dwc2 index ff3b169e66a51..28a3cf7665025 100644 --- a/drivers/usb/udc/Kconfig.dwc2 +++ b/drivers/usb/udc/Kconfig.dwc2 @@ -9,6 +9,7 @@ config UDC_DWC2 select NRFS if NRFS_HAS_VBUS_DETECTOR_SERVICE select NRFS_VBUS_DETECTOR_SERVICE_ENABLED if NRFS_HAS_VBUS_DETECTOR_SERVICE select EVENTS + select REGULATOR if SOC_SERIES_NRF54LX || SOC_SERIES_NRF92X help DWC2 USB device controller driver. diff --git a/drivers/usb/udc/udc_dwc2_vendor_quirks.h b/drivers/usb/udc/udc_dwc2_vendor_quirks.h index 6766649161ea8..93466a0aa421f 100644 --- a/drivers/usb/udc/udc_dwc2_vendor_quirks.h +++ b/drivers/usb/udc/udc_dwc2_vendor_quirks.h @@ -326,14 +326,13 @@ DT_INST_FOREACH_STATUS_OKAY(QUIRK_NRF_USBHS_DEFINE) #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)) +#define USBHS_DT_WRAPPER_REG_ADDR(n) UINT_TO_POINTER(DT_REG_ADDR(DT_INST_PARENT(n))) #include #include #include #include - -#define NRF_DEFAULT_IRQ_PRIORITY 1 +#include /* * On USBHS, we cannot access the DWC2 register until VBUS is detected and @@ -345,42 +344,46 @@ DT_INST_FOREACH_STATUS_OKAY(QUIRK_NRF_USBHS_DEFINE) static K_EVENT_DEFINE(usbhs_events); #define USBHS_VBUS_READY BIT(0) +const static struct device *const vregusb_dev = + DEVICE_DT_GET(DT_PHANDLE(DT_INST_PARENT(0), regulator)); static struct onoff_manager *pclk24m_mgr; static struct onoff_client pclk24m_cli; -static void vregusb_isr(const void *arg) +static void vregusb_event_cb(const struct device *dev, + const struct regulator_event *const evt, + const void *const user_data) { - const struct device *dev = arg; + ARG_UNUSED(dev); + const struct device *const udc_dev = user_data; - if (NRF_VREGUSB->EVENTS_VBUSDETECTED) { - NRF_VREGUSB->EVENTS_VBUSDETECTED = 0; + if (evt->type == REGULATOR_VOLTAGE_DETECTED) { k_event_post(&usbhs_events, USBHS_VBUS_READY); - udc_submit_event(dev, UDC_EVT_VBUS_READY, 0); + udc_submit_event(udc_dev, UDC_EVT_VBUS_READY, 0); } - if (NRF_VREGUSB->EVENTS_VBUSREMOVED) { - NRF_VREGUSB->EVENTS_VBUSREMOVED = 0; + if (evt->type == REGULATOR_VOLTAGE_REMOVED) { k_event_set_masked(&usbhs_events, 0, USBHS_VBUS_READY); - udc_submit_event(dev, UDC_EVT_VBUS_REMOVED, 0); + udc_submit_event(udc_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); + LOG_MODULE_DECLARE(udc_dwc2, CONFIG_UDC_DRIVER_LOG_LEVEL); + int err; - NRF_VREGUSB->INTEN = VREGUSB_INTEN_VBUSDETECTED_Msk | - VREGUSB_INTEN_VBUSREMOVED_Msk; - NRF_VREGUSB->TASKS_START = 1; + err = regulator_set_callback(vregusb_dev, vregusb_event_cb, dev); + if (err) { + LOG_ERR("Failed to set regulator callback"); + return err; + } - /* 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); + err = regulator_enable(vregusb_dev); + if (err) { + LOG_ERR("Failed to enable regulator"); + return err; } - irq_enable(VREGUSB_IRQn); pclk24m_mgr = z_nrf_clock_control_get_onoff(CLOCK_CONTROL_NRF_SUBSYS_HF24M); return 0; @@ -463,10 +466,9 @@ static inline int usbhs_disable_core(const struct device *dev) static inline int usbhs_disable_vreg(const struct device *dev) { - NRF_VREGUSB->INTEN = 0; - NRF_VREGUSB->TASKS_STOP = 1; + ARG_UNUSED(dev); - return 0; + return regulator_disable(vregusb_dev); } static inline int usbhs_init_caps(const struct device *dev) diff --git a/dts/bindings/regulator/nordic,vregusb-regulator.yaml b/dts/bindings/regulator/nordic,vregusb-regulator.yaml new file mode 100644 index 0000000000000..a68495d480948 --- /dev/null +++ b/dts/bindings/regulator/nordic,vregusb-regulator.yaml @@ -0,0 +1,20 @@ +# Copyright Nordic Semiconductor ASA +# SPDX-License-Identifier: Apache-2.0 + +description: | + Nordic VBUS regulator for USB peripheral + +compatible: "nordic,vregusb-regulator" + +include: + - name: base.yaml + - name: regulator.yaml + property-allowlist: + - regulator-name + +properties: + reg: + required: true + + regulator-name: + required: true diff --git a/dts/bindings/usb/nordic,nrf-usbhs-wrapper.yaml b/dts/bindings/usb/nordic,nrf-usbhs-wrapper.yaml new file mode 100644 index 0000000000000..fb168ab718f8f --- /dev/null +++ b/dts/bindings/usb/nordic,nrf-usbhs-wrapper.yaml @@ -0,0 +1,18 @@ +description: | + Nordic wrapper for USB controller and PHY hardware. The wrapper is used to + configure, control, and enable/disable the USB PHY, and to enable/disable USB + controller. + +compatible: "nordic,nrf-usbhs-wrapper" + +include: [base.yaml] + +properties: + reg: + required: true + + regulator: + required: true + type: phandle + description: | + USB PHY and controller function depends on the USB regulator VREGUSB. diff --git a/dts/vendor/nordic/nrf54lm20a.dtsi b/dts/vendor/nordic/nrf54lm20a.dtsi index 233e4417986a5..cb2480f9d2a30 100644 --- a/dts/vendor/nordic/nrf54lm20a.dtsi +++ b/dts/vendor/nordic/nrf54lm20a.dtsi @@ -210,17 +210,24 @@ prescaler = <0>; }; - usbhs: usbhs@5a000 { - compatible = "nordic,nrf-usbhs-nrf54l", "snps,dwc2"; - reg = <0x5a000 0x1000>, <0x50020000 0x1a000>; - reg-names = "wrapper", "core"; - interrupts = <90 NRF_DEFAULT_IRQ_PRIORITY>; - num-in-eps = <16>; - num-out-eps = <16>; - ghwcfg1 = <0x0>; - ghwcfg2 = <0x22affc52>; - ghwcfg4 = <0x3e10aa60>; - status = "disabled"; + usbhs_wrapper: usbhs_wrapper@5a000 { + compatible = "nordic,nrf-usbhs-wrapper"; + reg = <0x5a000 0x1000>; + regulator = <&vregusb>; + #address-cells = <1>; + #size-cells = <1>; + + usbhs: usbhs@50020000 { + compatible = "nordic,nrf-usbhs-nrf54l", "snps,dwc2"; + reg = <0x50020000 0x1a000>; + interrupts = <90 NRF_DEFAULT_IRQ_PRIORITY>; + num-in-eps = <16>; + num-out-eps = <16>; + ghwcfg1 = <0x0>; + ghwcfg2 = <0x22affc52>; + ghwcfg4 = <0x3e10aa60>; + status = "disabled"; + }; }; dppic10: dppic@82000 { @@ -827,6 +834,14 @@ regulator-name = "VREGMAIN"; regulator-initial-mode = ; }; + + vregusb: regulator@50121000 { + compatible = "nordic,vregusb-regulator"; + reg = <0x50121000 0x1>; + interrupts = <289 NRF_DEFAULT_IRQ_PRIORITY>; + status = "disabled"; + regulator-name = "VREGUSB"; + }; }; }; diff --git a/include/zephyr/drivers/regulator.h b/include/zephyr/drivers/regulator.h index 9146c69e4cc22..5ba97c538a79e 100644 --- a/include/zephyr/drivers/regulator.h +++ b/include/zephyr/drivers/regulator.h @@ -63,6 +63,33 @@ typedef uint8_t regulator_error_flags_t; /** @} */ +/** + * @brief Regulator event types + */ +enum regulator_event_type { + /** @brief Regulator voltage is detected */ + REGULATOR_VOLTAGE_DETECTED, + /** @brief Regulator voltage is removed */ + REGULATOR_VOLTAGE_REMOVED, +}; + +/** @brief Regulator event structure */ +struct regulator_event { + enum regulator_event_type type; +}; + +/** + * @typedef regulator_callback_t + * @brief Regulator callback function signature + * + * @param dev Regulator device instance + * @param evt Regulator event + * @param user_data User data + */ +typedef void (*regulator_callback_t)(const struct device *dev, + const struct regulator_event *const evt, + const void *const user_data); + /** @cond INTERNAL_HIDDEN */ typedef int (*regulator_dvs_state_set_t)(const struct device *dev, @@ -102,6 +129,8 @@ typedef int (*regulator_get_active_discharge_t)(const struct device *dev, bool *active_discharge); typedef int (*regulator_get_error_flags_t)( const struct device *dev, regulator_error_flags_t *flags); +typedef int (*regulator_set_callback_t)(const struct device *dev, + regulator_callback_t cb, const void *const user_data); /** @brief Driver-specific API functions to support regulator control. */ __subsystem struct regulator_driver_api { @@ -120,6 +149,7 @@ __subsystem struct regulator_driver_api { regulator_set_active_discharge_t set_active_discharge; regulator_get_active_discharge_t get_active_discharge; regulator_get_error_flags_t get_error_flags; + regulator_set_callback_t set_callback; }; /** @@ -765,6 +795,34 @@ static inline int regulator_get_error_flags(const struct device *dev, return api->get_error_flags(dev, flags); } +/** + * @brief Set event handler function. + * + * The regulator may generate an interrupt when, for example, the voltage is + * present. This can be used for USB VBUS voltage regulators to notify that a + * device is connected to a port. + * + * @param dev Regulator device instance + * @param cb Event handler + * @param user_data User data + * + * @retval 0 If successful. + * @retval -ENOSYS If not supported by the device. + */ +static inline int regulator_set_callback(const struct device *dev, + regulator_callback_t cb, + const void *const user_data) +{ + const struct regulator_driver_api *api = + (const struct regulator_driver_api *)dev->api; + + if (api->set_callback == NULL) { + return -ENOSYS; + } + + return api->set_callback(dev, cb, user_data); +} + #ifdef __cplusplus } #endif diff --git a/soc/nordic/validate_base_addresses.c b/soc/nordic/validate_base_addresses.c index 4f17e32e5d5b4..6f6a0cd6f3a1e 100644 --- a/soc/nordic/validate_base_addresses.c +++ b/soc/nordic/validate_base_addresses.c @@ -355,8 +355,6 @@ CHECK_DT_REG(cpuapp_uicr, NRF_APPLICATION_UICR); CHECK_DT_REG(bicr, NRF_APPLICATION_BICR); CHECK_DT_REG(cpurad_uicr, NRF_RADIOCORE_UICR); CHECK_DT_REG(usbd, NRF_USBD); -CHECK_DT_REG(usbhs, NRF_USBHS); -CHECK_DT_REG(usbhs_core, NRF_USBHSCORE0); CHECK_DT_REG(usbreg, NRF_USBREGULATOR); CHECK_DT_REG(vmc, NRF_VMC); CHECK_DT_REG(cpuflpr_clic, NRF_FLPR_VPRCLIC);