Skip to content

Commit d8e1ab8

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 d8e1ab8

File tree

5 files changed

+116
-8
lines changed

5 files changed

+116
-8
lines changed

drivers/usb/udc/udc_mcux_ehci.c

Lines changed: 91 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,40 @@ 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+
if (data->enabled == enabled && data->vbus_present == vbus_present) {
105+
return;
106+
}
107+
108+
if (vbus_present != data->vbus_present) {
109+
udc_submit_event(data->dev,
110+
vbus_present ? UDC_EVT_VBUS_READY : UDC_EVT_VBUS_REMOVED, 0);
111+
}
112+
113+
if (enabled && vbus_present) {
114+
115+
/*
116+
* Block PM when usb is recieves VBUS signal or device
117+
* is explicitly told to be enabled.
118+
*/
119+
pm_policy_device_power_lock_get(dev);
120+
} else if (data->enabled && data->vbus_present) {
121+
122+
/*
123+
* USB was previously busy, but now has either lost
124+
* VBUS signal or application has disabled udc.
125+
* UDC will now unblock PM.
126+
*/
127+
pm_policy_device_power_lock_put(dev);
128+
}
129+
130+
data->enabled = enabled;
131+
data->vbus_present = vbus_present;
132+
}
133+
95134
/* If ep is busy, return busy. Otherwise feed the buf to controller */
96135
static int udc_mcux_ep_feed(const struct device *dev,
97136
struct udc_ep_config *const cfg,
@@ -525,10 +564,10 @@ usb_status_t USB_DeviceNotificationTrigger(void *handle, void *msg)
525564
case kUSB_DeviceNotifyLPMSleep:
526565
break;
527566
case kUSB_DeviceNotifyDetach:
528-
udc_submit_event(dev, UDC_EVT_VBUS_REMOVED, 0);
567+
udc_mcux_change_state(dev, priv->enabled, 0);
529568
break;
530569
case kUSB_DeviceNotifyAttach:
531-
udc_submit_event(dev, UDC_EVT_VBUS_READY, 0);
570+
udc_mcux_change_state(dev, priv->enabled, 1);
532571
break;
533572
case kUSB_DeviceNotifySOF:
534573
udc_submit_event(dev, UDC_EVT_SOF, 0);
@@ -676,11 +715,19 @@ static int udc_mcux_set_address(const struct device *dev, const uint8_t addr)
676715

677716
static int udc_mcux_enable(const struct device *dev)
678717
{
718+
struct udc_mcux_data *priv = udc_get_private(dev);
719+
720+
udc_mcux_change_state(dev, 1, priv->vbus_present);
721+
679722
return udc_mcux_control(dev, kUSB_DeviceControlRun, NULL);
680723
}
681724

682725
static int udc_mcux_disable(const struct device *dev)
683726
{
727+
struct udc_mcux_data *priv = udc_get_private(dev);
728+
729+
udc_mcux_change_state(dev, 0, priv->vbus_present);
730+
684731
return udc_mcux_control(dev, kUSB_DeviceControlStop, NULL);
685732
}
686733

@@ -759,7 +806,9 @@ static inline void udc_mcux_get_hal_driver_id(struct udc_mcux_data *priv,
759806
}
760807
}
761808

762-
static int udc_mcux_driver_preinit(const struct device *dev)
809+
810+
811+
static int udc_mcux_init_common(const struct device *dev)
763812
{
764813
const struct udc_mcux_config *config = dev->config;
765814
struct udc_data *data = dev->data;
@@ -771,10 +820,6 @@ static int udc_mcux_driver_preinit(const struct device *dev)
771820
return -ENOMEM;
772821
}
773822

774-
k_mutex_init(&data->mutex);
775-
k_fifo_init(&priv->fifo);
776-
k_work_init(&priv->work, udc_mcux_work_handler);
777-
778823
for (int i = 0; i < config->num_of_eps; i++) {
779824
config->ep_cfg_out[i].caps.out = 1;
780825
if (i == 0) {
@@ -823,11 +868,48 @@ static int udc_mcux_driver_preinit(const struct device *dev)
823868
data->caps.hs = true;
824869
priv->dev = dev;
825870

871+
826872
pinctrl_apply_state(config->pincfg, PINCTRL_STATE_DEFAULT);
827873

828874
return 0;
829875
}
830876

877+
static int udc_mcux_driver_preinit(const struct device *dev)
878+
{
879+
struct udc_data *data = dev->data;
880+
struct udc_mcux_data *priv = data->priv;
881+
882+
k_mutex_init(&data->mutex);
883+
k_fifo_init(&priv->fifo);
884+
k_work_init(&priv->work, udc_mcux_work_handler);
885+
return udc_mcux_init_common(dev);
886+
}
887+
888+
#ifdef CONFIG_PM_DEVICE
889+
static int udc_mcux_pm_action(const struct device *dev, enum pm_device_action action)
890+
{
891+
struct udc_mcux_data *priv = udc_get_private(dev);
892+
893+
switch (action) {
894+
case PM_DEVICE_ACTION_RESUME:
895+
case PM_DEVICE_ACTION_SUSPEND:
896+
break;
897+
case PM_DEVICE_ACTION_TURN_OFF:
898+
udc_mcux_shutdown(dev);
899+
break;
900+
case PM_DEVICE_ACTION_TURN_ON:
901+
udc_mcux_init_common(dev);
902+
if (priv->enabled) {
903+
udc_mcux_control(dev, kUSB_DeviceControlRun, NULL);
904+
}
905+
break;
906+
default:
907+
return -ENOTSUP;
908+
}
909+
return 0;
910+
}
911+
#endif /* CONFIG_PM_DEVICE */
912+
831913
static const struct udc_api udc_mcux_api = {
832914
.device_speed = udc_mcux_device_speed,
833915
.ep_enqueue = udc_mcux_ep_enqueue,
@@ -917,7 +999,8 @@ static usb_phy_config_struct_t phy_config_##n = { \
917999
.priv = &priv_data_##n, \
9181000
}; \
9191001
\
920-
DEVICE_DT_INST_DEFINE(n, udc_mcux_driver_preinit, NULL, \
1002+
PM_DEVICE_DT_INST_DEFINE(n, udc_mcux_pm_action); \
1003+
DEVICE_DT_INST_DEFINE(n, udc_mcux_driver_preinit, PM_DEVICE_DT_INST_GET(n), \
9211004
&udc_data_##n, &priv_config_##n, \
9221005
POST_KERNEL, CONFIG_KERNEL_INIT_PRIORITY_DEVICE, \
9231006
&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)