Skip to content

Commit 641b3e0

Browse files
committed
drivers: usb: udc: Enabling PM to udc_mcux_ehci
Enables PM support for USB devices that use udc_mcux_ehci. Signed-off-by: Emilio Benavente <emilio.benavente@nxp.com>
1 parent c576464 commit 641b3e0

File tree

5 files changed

+121
-8
lines changed

5 files changed

+121
-8
lines changed

drivers/usb/udc/udc_mcux_ehci.c

Lines changed: 96 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,8 @@
2323
#include "usb_phy.h"
2424

2525
#include <zephyr/logging/log.h>
26+
#include <zephyr/pm/device.h>
27+
#include <zephyr/pm/policy.h>
2628
LOG_MODULE_REGISTER(udc_mcux, CONFIG_UDC_DRIVER_LOG_LEVEL);
2729

2830
/*
@@ -34,6 +36,7 @@ LOG_MODULE_REGISTER(udc_mcux, CONFIG_UDC_DRIVER_LOG_LEVEL);
3436

3537
#define PRV_DATA_HANDLE(_handle) CONTAINER_OF(_handle, struct udc_mcux_data, mcux_device)
3638

39+
3740
struct udc_mcux_config {
3841
const usb_device_controller_interface_struct_t *mcux_if;
3942
void (*irq_enable_func)(const struct device *dev);
@@ -51,6 +54,8 @@ struct udc_mcux_data {
5154
usb_device_struct_t mcux_device;
5255
struct k_work work;
5356
struct k_fifo fifo;
57+
bool enabled;
58+
bool vbus_present;
5459
uint8_t controller_id; /* 0xFF is invalid value */
5560
};
5661

@@ -92,6 +97,44 @@ static int udc_mcux_control(const struct device *dev, usb_device_control_type_t
9297
return 0;
9398
}
9499

100+
static void udc_mcux_change_state(const struct device *dev, const bool enabled, const bool vbus_present)
101+
{
102+
struct udc_mcux_data *data = udc_get_private(dev);
103+
104+
unsigned int key = irq_lock();
105+
106+
if (data->enabled == enabled && data->vbus_present == vbus_present) {
107+
return;
108+
}
109+
110+
if (vbus_present != data->vbus_present) {
111+
udc_submit_event(data->dev,
112+
vbus_present ? UDC_EVT_VBUS_READY : UDC_EVT_VBUS_REMOVED, 0);
113+
}
114+
115+
if (enabled && vbus_present) {
116+
117+
/*
118+
* Block PM when usb is recieves VBUS signal or device
119+
* is explicitly told to be enabled.
120+
*/
121+
pm_policy_device_power_lock_get(dev);
122+
} else if (data->enabled && data->vbus_present) {
123+
124+
/*
125+
* USB was previously busy, but now has either lost
126+
* VBUS signal or application has disabled udc.
127+
* UDC will now unblock PM.
128+
*/
129+
pm_policy_device_power_lock_put(dev);
130+
}
131+
132+
data->enabled = enabled;
133+
data->vbus_present = vbus_present;
134+
135+
irq_unlock(key);
136+
}
137+
95138
/* If ep is busy, return busy. Otherwise feed the buf to controller */
96139
static int udc_mcux_ep_feed(const struct device *dev,
97140
struct udc_ep_config *const cfg,
@@ -525,10 +568,10 @@ usb_status_t USB_DeviceNotificationTrigger(void *handle, void *msg)
525568
case kUSB_DeviceNotifyLPMSleep:
526569
break;
527570
case kUSB_DeviceNotifyDetach:
528-
udc_submit_event(dev, UDC_EVT_VBUS_REMOVED, 0);
571+
udc_mcux_change_state(dev, priv->enabled, 0);
529572
break;
530573
case kUSB_DeviceNotifyAttach:
531-
udc_submit_event(dev, UDC_EVT_VBUS_READY, 0);
574+
udc_mcux_change_state(dev, priv->enabled, 1);
532575
break;
533576
case kUSB_DeviceNotifySOF:
534577
udc_submit_event(dev, UDC_EVT_SOF, 0);
@@ -676,11 +719,19 @@ static int udc_mcux_set_address(const struct device *dev, const uint8_t addr)
676719

677720
static int udc_mcux_enable(const struct device *dev)
678721
{
722+
struct udc_mcux_data *priv = udc_get_private(dev);
723+
724+
udc_mcux_change_state(dev, 1, priv->vbus_present);
725+
679726
return udc_mcux_control(dev, kUSB_DeviceControlRun, NULL);
680727
}
681728

682729
static int udc_mcux_disable(const struct device *dev)
683730
{
731+
struct udc_mcux_data *priv = udc_get_private(dev);
732+
733+
udc_mcux_change_state(dev, 0, priv->vbus_present);
734+
684735
return udc_mcux_control(dev, kUSB_DeviceControlStop, NULL);
685736
}
686737

@@ -759,7 +810,9 @@ static inline void udc_mcux_get_hal_driver_id(struct udc_mcux_data *priv,
759810
}
760811
}
761812

762-
static int udc_mcux_driver_preinit(const struct device *dev)
813+
814+
815+
static int udc_mcux_init_common(const struct device *dev)
763816
{
764817
const struct udc_mcux_config *config = dev->config;
765818
struct udc_data *data = dev->data;
@@ -771,10 +824,6 @@ static int udc_mcux_driver_preinit(const struct device *dev)
771824
return -ENOMEM;
772825
}
773826

774-
k_mutex_init(&data->mutex);
775-
k_fifo_init(&priv->fifo);
776-
k_work_init(&priv->work, udc_mcux_work_handler);
777-
778827
for (int i = 0; i < config->num_of_eps; i++) {
779828
config->ep_cfg_out[i].caps.out = 1;
780829
if (i == 0) {
@@ -823,11 +872,49 @@ static int udc_mcux_driver_preinit(const struct device *dev)
823872
data->caps.hs = true;
824873
priv->dev = dev;
825874

875+
826876
pinctrl_apply_state(config->pincfg, PINCTRL_STATE_DEFAULT);
827877

828878
return 0;
829879
}
830880

881+
static int udc_mcux_driver_preinit(const struct device *dev)
882+
{
883+
const struct udc_mcux_config *config = dev->config;
884+
struct udc_data *data = dev->data;
885+
struct udc_mcux_data *priv = data->priv;
886+
887+
k_mutex_init(&data->mutex);
888+
k_fifo_init(&priv->fifo);
889+
k_work_init(&priv->work, udc_mcux_work_handler);
890+
return udc_mcux_init_common(dev);
891+
}
892+
893+
#ifdef CONFIG_PM_DEVICE
894+
static int udc_mcux_pm_action(const struct device *dev, enum pm_device_action action)
895+
{
896+
struct udc_mcux_data *priv = udc_get_private(dev);
897+
898+
switch (action) {
899+
case PM_DEVICE_ACTION_RESUME:
900+
case PM_DEVICE_ACTION_SUSPEND:
901+
break;
902+
case PM_DEVICE_ACTION_TURN_OFF:
903+
udc_mcux_shutdown(dev);
904+
break;
905+
case PM_DEVICE_ACTION_TURN_ON:
906+
udc_mcux_init_common(dev);
907+
if (priv->enabled) {
908+
udc_mcux_control(dev, kUSB_DeviceControlRun, NULL);
909+
}
910+
break;
911+
default:
912+
return -ENOTSUP;
913+
}
914+
return 0;
915+
}
916+
#endif /* CONFIG_PM_DEVICE */
917+
831918
static const struct udc_api udc_mcux_api = {
832919
.device_speed = udc_mcux_device_speed,
833920
.ep_enqueue = udc_mcux_ep_enqueue,
@@ -917,7 +1004,8 @@ static usb_phy_config_struct_t phy_config_##n = { \
9171004
.priv = &priv_data_##n, \
9181005
}; \
9191006
\
920-
DEVICE_DT_INST_DEFINE(n, udc_mcux_driver_preinit, NULL, \
1007+
PM_DEVICE_DT_INST_DEFINE(n, udc_mcux_pm_action); \
1008+
DEVICE_DT_INST_DEFINE(n, udc_mcux_driver_preinit, PM_DEVICE_DT_INST_GET(n), \
9211009
&udc_data_##n, &priv_config_##n, \
9221010
POST_KERNEL, CONFIG_KERNEL_INIT_PRIORITY_DEVICE, \
9231011
&udc_mcux_api);

dts/arm/nxp/nxp_rw6xx_common.dtsi

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -238,6 +238,7 @@
238238
interrupt-names = "usb_otg";
239239
num-bidir-endpoints = <8>;
240240
power-domains = <&power_mode3_domain>;
241+
zephyr,disabling-power-states = <&suspend &standby>;
241242
status = "disabled";
242243
};
243244

modules/hal_nxp/mcux/mcux-sdk-ng/middleware/usb_device_config.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,8 @@ BUILD_ASSERT(NUM_INSTS <= 1, "Only one USB device supported");
114114
*/
115115
#define USB_DEVICE_CONFIG_SOF_NOTIFICATIONS (1U)
116116

117+
#define USB_DEVICE_CONFIG_DETACH_ENABLE (1U)
118+
117119
#endif
118120

119121
#endif /* __USB_DEVICE_CONFIG_H__ */
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
/*
2+
* Copyright 2025 NXP
3+
*
4+
* SPDX-License-Identifier: Apache-2.0
5+
*/
6+
7+
/ {
8+
chosen {
9+
zephyr,console = &cdc_acm_uart0;
10+
};
11+
};
12+
13+
&zephyr_udc0 {
14+
cdc_acm_uart0: cdc_acm_uart0 {
15+
compatible = "zephyr,cdc-acm-uart";
16+
};
17+
};

soc/nxp/rw/power.c

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,11 @@
2020

2121
LOG_MODULE_DECLARE(soc, CONFIG_SOC_LOG_LEVEL);
2222

23+
#if CONFIG_PM && CONFIG_UDC_NXP_EHCI && DT_NODE_HAS_STATUS(DT_NODELABEL(standby), okay)
24+
#error "UDC EHCI CURRENTLY DOES NOT SUPPORT STANDBY MODE"
25+
#endif
26+
27+
2328
/* Active mode */
2429
#define POWER_MODE0 0
2530
/* Idle mode */

0 commit comments

Comments
 (0)