Skip to content

Commit 5e355ab

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 a4f702b commit 5e355ab

File tree

5 files changed

+117
-8
lines changed

5 files changed

+117
-8
lines changed

drivers/usb/udc/udc_mcux_ehci.c

Lines changed: 92 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+
volatile bool enabled;
58+
volatile bool vbus_present;
5459
uint8_t controller_id; /* 0xFF is invalid value */
5560
};
5661

@@ -92,6 +97,41 @@ 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,
101+
const bool vbus_present)
102+
{
103+
struct udc_mcux_data *data = udc_get_private(dev);
104+
105+
if (data->enabled == enabled && data->vbus_present == vbus_present) {
106+
return;
107+
}
108+
109+
if (vbus_present != data->vbus_present) {
110+
udc_submit_event(data->dev,
111+
vbus_present ? UDC_EVT_VBUS_READY : UDC_EVT_VBUS_REMOVED, 0);
112+
}
113+
114+
if (enabled && vbus_present) {
115+
116+
/*
117+
* Block PM when usb is receives VBUS signal or device
118+
* is explicitly told to be enabled.
119+
*/
120+
pm_policy_device_power_lock_get(dev);
121+
} else if (data->enabled && data->vbus_present) {
122+
123+
/*
124+
* USB was previously busy, but now has either lost
125+
* VBUS signal or application has disabled udc.
126+
* UDC will now unblock PM.
127+
*/
128+
pm_policy_device_power_lock_put(dev);
129+
}
130+
131+
data->enabled = enabled;
132+
data->vbus_present = vbus_present;
133+
}
134+
95135
/* If ep is busy, return busy. Otherwise feed the buf to controller */
96136
static int udc_mcux_ep_feed(const struct device *dev,
97137
struct udc_ep_config *const cfg,
@@ -525,10 +565,10 @@ usb_status_t USB_DeviceNotificationTrigger(void *handle, void *msg)
525565
case kUSB_DeviceNotifyLPMSleep:
526566
break;
527567
case kUSB_DeviceNotifyDetach:
528-
udc_submit_event(dev, UDC_EVT_VBUS_REMOVED, 0);
568+
udc_mcux_change_state(dev, priv->enabled, 0);
529569
break;
530570
case kUSB_DeviceNotifyAttach:
531-
udc_submit_event(dev, UDC_EVT_VBUS_READY, 0);
571+
udc_mcux_change_state(dev, priv->enabled, 1);
532572
break;
533573
case kUSB_DeviceNotifySOF:
534574
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)
676716

677717
static int udc_mcux_enable(const struct device *dev)
678718
{
719+
struct udc_mcux_data *priv = udc_get_private(dev);
720+
721+
udc_mcux_change_state(dev, 1, priv->vbus_present);
722+
679723
return udc_mcux_control(dev, kUSB_DeviceControlRun, NULL);
680724
}
681725

682726
static int udc_mcux_disable(const struct device *dev)
683727
{
728+
struct udc_mcux_data *priv = udc_get_private(dev);
729+
730+
udc_mcux_change_state(dev, 0, priv->vbus_present);
731+
684732
return udc_mcux_control(dev, kUSB_DeviceControlStop, NULL);
685733
}
686734

@@ -759,7 +807,9 @@ static inline void udc_mcux_get_hal_driver_id(struct udc_mcux_data *priv,
759807
}
760808
}
761809

762-
static int udc_mcux_driver_preinit(const struct device *dev)
810+
811+
812+
static int udc_mcux_init_common(const struct device *dev)
763813
{
764814
const struct udc_mcux_config *config = dev->config;
765815
struct udc_data *data = dev->data;
@@ -771,10 +821,6 @@ static int udc_mcux_driver_preinit(const struct device *dev)
771821
return -ENOMEM;
772822
}
773823

774-
k_mutex_init(&data->mutex);
775-
k_fifo_init(&priv->fifo);
776-
k_work_init(&priv->work, udc_mcux_work_handler);
777-
778824
for (int i = 0; i < config->num_of_eps; i++) {
779825
config->ep_cfg_out[i].caps.out = 1;
780826
if (i == 0) {
@@ -823,11 +869,48 @@ static int udc_mcux_driver_preinit(const struct device *dev)
823869
data->caps.hs = true;
824870
priv->dev = dev;
825871

872+
826873
pinctrl_apply_state(config->pincfg, PINCTRL_STATE_DEFAULT);
827874

828875
return 0;
829876
}
830877

878+
static int udc_mcux_driver_preinit(const struct device *dev)
879+
{
880+
struct udc_data *data = dev->data;
881+
struct udc_mcux_data *priv = data->priv;
882+
883+
k_mutex_init(&data->mutex);
884+
k_fifo_init(&priv->fifo);
885+
k_work_init(&priv->work, udc_mcux_work_handler);
886+
return udc_mcux_init_common(dev);
887+
}
888+
889+
#ifdef CONFIG_PM_DEVICE
890+
static int udc_mcux_pm_action(const struct device *dev, enum pm_device_action action)
891+
{
892+
struct udc_mcux_data *priv = udc_get_private(dev);
893+
894+
switch (action) {
895+
case PM_DEVICE_ACTION_RESUME:
896+
case PM_DEVICE_ACTION_SUSPEND:
897+
break;
898+
case PM_DEVICE_ACTION_TURN_OFF:
899+
udc_mcux_shutdown(dev);
900+
break;
901+
case PM_DEVICE_ACTION_TURN_ON:
902+
udc_mcux_init_common(dev);
903+
if (priv->enabled) {
904+
udc_mcux_control(dev, kUSB_DeviceControlRun, NULL);
905+
}
906+
break;
907+
default:
908+
return -ENOTSUP;
909+
}
910+
return 0;
911+
}
912+
#endif /* CONFIG_PM_DEVICE */
913+
831914
static const struct udc_api udc_mcux_api = {
832915
.device_speed = udc_mcux_device_speed,
833916
.ep_enqueue = udc_mcux_ep_enqueue,
@@ -917,7 +1000,8 @@ static usb_phy_config_struct_t phy_config_##n = { \
9171000
.priv = &priv_data_##n, \
9181001
}; \
9191002
\
920-
DEVICE_DT_INST_DEFINE(n, udc_mcux_driver_preinit, NULL, \
1003+
PM_DEVICE_DT_INST_DEFINE(n, udc_mcux_pm_action); \
1004+
DEVICE_DT_INST_DEFINE(n, udc_mcux_driver_preinit, PM_DEVICE_DT_INST_GET(n), \
9211005
&udc_data_##n, &priv_config_##n, \
9221006
POST_KERNEL, CONFIG_KERNEL_INIT_PRIORITY_DEVICE, \
9231007
&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
@@ -122,6 +122,8 @@ BUILD_ASSERT(NUM_INSTS <= 1, "Only one USB device supported");
122122
*/
123123
#define USB_DEVICE_CONFIG_SOF_NOTIFICATIONS (1U)
124124

125+
#define USB_DEVICE_CONFIG_DETACH_ENABLE (1U)
126+
125127
#endif
126128

127129
#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)