diff --git a/drivers/usb/udc/udc_mcux_ehci.c b/drivers/usb/udc/udc_mcux_ehci.c index fc95f7a91d7eb..1c8ddd12b580a 100644 --- a/drivers/usb/udc/udc_mcux_ehci.c +++ b/drivers/usb/udc/udc_mcux_ehci.c @@ -23,6 +23,8 @@ #include "usb_phy.h" #include +#include +#include LOG_MODULE_REGISTER(udc_mcux, CONFIG_UDC_DRIVER_LOG_LEVEL); /* @@ -34,6 +36,7 @@ LOG_MODULE_REGISTER(udc_mcux, CONFIG_UDC_DRIVER_LOG_LEVEL); #define PRV_DATA_HANDLE(_handle) CONTAINER_OF(_handle, struct udc_mcux_data, mcux_device) + struct udc_mcux_config { const usb_device_controller_interface_struct_t *mcux_if; void (*irq_enable_func)(const struct device *dev); @@ -51,6 +54,8 @@ struct udc_mcux_data { usb_device_struct_t mcux_device; struct k_work work; struct k_fifo fifo; + volatile bool enabled; + volatile bool vbus_present; uint8_t controller_id; /* 0xFF is invalid value */ }; @@ -92,6 +97,41 @@ static int udc_mcux_control(const struct device *dev, usb_device_control_type_t return 0; } +static void udc_mcux_change_state(const struct device *dev, const bool enabled, + const bool vbus_present) +{ + struct udc_mcux_data *data = udc_get_private(dev); + + if (data->enabled == enabled && data->vbus_present == vbus_present) { + return; + } + + if (vbus_present != data->vbus_present) { + udc_submit_event(data->dev, + vbus_present ? UDC_EVT_VBUS_READY : UDC_EVT_VBUS_REMOVED, 0); + } + + if (enabled && vbus_present) { + + /* + * Block PM when usb is receives VBUS signal or device + * is explicitly told to be enabled. + */ + pm_policy_device_power_lock_get(dev); + } else if (data->enabled && data->vbus_present) { + + /* + * USB was previously busy, but now has either lost + * VBUS signal or application has disabled udc. + * UDC will now unblock PM. + */ + pm_policy_device_power_lock_put(dev); + } + + data->enabled = enabled; + data->vbus_present = vbus_present; +} + /* If ep is busy, return busy. Otherwise feed the buf to controller */ static int udc_mcux_ep_feed(const struct device *dev, struct udc_ep_config *const cfg, @@ -525,10 +565,10 @@ usb_status_t USB_DeviceNotificationTrigger(void *handle, void *msg) case kUSB_DeviceNotifyLPMSleep: break; case kUSB_DeviceNotifyDetach: - udc_submit_event(dev, UDC_EVT_VBUS_REMOVED, 0); + udc_mcux_change_state(dev, priv->enabled, 0); break; case kUSB_DeviceNotifyAttach: - udc_submit_event(dev, UDC_EVT_VBUS_READY, 0); + udc_mcux_change_state(dev, priv->enabled, 1); break; case kUSB_DeviceNotifySOF: udc_submit_event(dev, UDC_EVT_SOF, 0); @@ -676,11 +716,19 @@ static int udc_mcux_set_address(const struct device *dev, const uint8_t addr) static int udc_mcux_enable(const struct device *dev) { + struct udc_mcux_data *priv = udc_get_private(dev); + + udc_mcux_change_state(dev, 1, priv->vbus_present); + return udc_mcux_control(dev, kUSB_DeviceControlRun, NULL); } static int udc_mcux_disable(const struct device *dev) { + struct udc_mcux_data *priv = udc_get_private(dev); + + udc_mcux_change_state(dev, 0, priv->vbus_present); + return udc_mcux_control(dev, kUSB_DeviceControlStop, NULL); } @@ -759,7 +807,9 @@ static inline void udc_mcux_get_hal_driver_id(struct udc_mcux_data *priv, } } -static int udc_mcux_driver_preinit(const struct device *dev) + + +static int udc_mcux_init_common(const struct device *dev) { const struct udc_mcux_config *config = dev->config; struct udc_data *data = dev->data; @@ -771,10 +821,6 @@ static int udc_mcux_driver_preinit(const struct device *dev) return -ENOMEM; } - k_mutex_init(&data->mutex); - k_fifo_init(&priv->fifo); - k_work_init(&priv->work, udc_mcux_work_handler); - for (int i = 0; i < config->num_of_eps; i++) { config->ep_cfg_out[i].caps.out = 1; if (i == 0) { @@ -823,11 +869,48 @@ static int udc_mcux_driver_preinit(const struct device *dev) data->caps.hs = true; priv->dev = dev; + pinctrl_apply_state(config->pincfg, PINCTRL_STATE_DEFAULT); return 0; } +static int udc_mcux_driver_preinit(const struct device *dev) +{ + struct udc_data *data = dev->data; + struct udc_mcux_data *priv = data->priv; + + k_mutex_init(&data->mutex); + k_fifo_init(&priv->fifo); + k_work_init(&priv->work, udc_mcux_work_handler); + return udc_mcux_init_common(dev); +} + +#ifdef CONFIG_PM_DEVICE +static int udc_mcux_pm_action(const struct device *dev, enum pm_device_action action) +{ + struct udc_mcux_data *priv = udc_get_private(dev); + + switch (action) { + case PM_DEVICE_ACTION_RESUME: + case PM_DEVICE_ACTION_SUSPEND: + break; + case PM_DEVICE_ACTION_TURN_OFF: + udc_mcux_shutdown(dev); + break; + case PM_DEVICE_ACTION_TURN_ON: + udc_mcux_init_common(dev); + if (priv->enabled) { + udc_mcux_control(dev, kUSB_DeviceControlRun, NULL); + } + break; + default: + return -ENOTSUP; + } + return 0; +} +#endif /* CONFIG_PM_DEVICE */ + static const struct udc_api udc_mcux_api = { .device_speed = udc_mcux_device_speed, .ep_enqueue = udc_mcux_ep_enqueue, @@ -917,7 +1000,8 @@ static usb_phy_config_struct_t phy_config_##n = { \ .priv = &priv_data_##n, \ }; \ \ - DEVICE_DT_INST_DEFINE(n, udc_mcux_driver_preinit, NULL, \ + PM_DEVICE_DT_INST_DEFINE(n, udc_mcux_pm_action); \ + DEVICE_DT_INST_DEFINE(n, udc_mcux_driver_preinit, PM_DEVICE_DT_INST_GET(n), \ &udc_data_##n, &priv_config_##n, \ POST_KERNEL, CONFIG_KERNEL_INIT_PRIORITY_DEVICE, \ &udc_mcux_api); diff --git a/dts/arm/nxp/nxp_rw6xx_common.dtsi b/dts/arm/nxp/nxp_rw6xx_common.dtsi index e762411c6da71..7e535d252861b 100644 --- a/dts/arm/nxp/nxp_rw6xx_common.dtsi +++ b/dts/arm/nxp/nxp_rw6xx_common.dtsi @@ -238,6 +238,7 @@ interrupt-names = "usb_otg"; num-bidir-endpoints = <8>; power-domains = <&power_mode3_domain>; + zephyr,disabling-power-states = <&suspend &standby>; status = "disabled"; }; diff --git a/modules/hal_nxp/mcux/mcux-sdk-ng/middleware/usb_device_config.h b/modules/hal_nxp/mcux/mcux-sdk-ng/middleware/usb_device_config.h index 5f864827281d0..f6ba65231bad5 100644 --- a/modules/hal_nxp/mcux/mcux-sdk-ng/middleware/usb_device_config.h +++ b/modules/hal_nxp/mcux/mcux-sdk-ng/middleware/usb_device_config.h @@ -122,6 +122,8 @@ BUILD_ASSERT(NUM_INSTS <= 1, "Only one USB device supported"); */ #define USB_DEVICE_CONFIG_SOF_NOTIFICATIONS (1U) +#define USB_DEVICE_CONFIG_DETACH_ENABLE (1U) + #endif #endif /* __USB_DEVICE_CONFIG_H__ */ diff --git a/samples/subsys/usb/console-next/boards/frdm_rw612.overlay b/samples/subsys/usb/console-next/boards/frdm_rw612.overlay new file mode 100644 index 0000000000000..13c194169807c --- /dev/null +++ b/samples/subsys/usb/console-next/boards/frdm_rw612.overlay @@ -0,0 +1,17 @@ +/* + * Copyright 2025 NXP + * + * SPDX-License-Identifier: Apache-2.0 + */ + +/ { + chosen { + zephyr,console = &cdc_acm_uart0; + }; +}; + +&zephyr_udc0 { + cdc_acm_uart0: cdc_acm_uart0 { + compatible = "zephyr,cdc-acm-uart"; + }; +}; diff --git a/soc/nxp/rw/power.c b/soc/nxp/rw/power.c index a348a61093fc3..bd9dbab9d0abd 100644 --- a/soc/nxp/rw/power.c +++ b/soc/nxp/rw/power.c @@ -20,6 +20,11 @@ LOG_MODULE_DECLARE(soc, CONFIG_SOC_LOG_LEVEL); +#if CONFIG_PM && CONFIG_UDC_NXP_EHCI && DT_NODE_HAS_STATUS(DT_NODELABEL(standby), okay) +#error "UDC EHCI CURRENTLY DOES NOT SUPPORT STANDBY MODE" +#endif + + /* Active mode */ #define POWER_MODE0 0 /* Idle mode */