Skip to content

Commit 8064463

Browse files
roma-jamJosuah Demangeon
authored andcommitted
drivers: usb: uhc: Added dwc2 initial files
Added placeholder driver API functions and minimal quirks handling for ESP32-S3, with support for the shell sample. Signed-off-by: Roman Leonov <[email protected]>
1 parent a6fb8b8 commit 8064463

7 files changed

Lines changed: 426 additions & 0 deletions

File tree

drivers/usb/uhc/CMakeLists.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,10 @@
33
# SPDX-License-Identifier: Apache-2.0
44

55
zephyr_library()
6+
zephyr_library_include_directories(${ZEPHYR_BASE}/drivers/usb/common/)
67

78
zephyr_library_sources(uhc_common.c)
9+
zephyr_library_sources_ifdef(CONFIG_UHC_DWC2 uhc_dwc2.c)
810
zephyr_library_sources_ifdef(CONFIG_UHC_MAX3421E uhc_max3421e.c)
911
zephyr_library_sources_ifdef(CONFIG_UHC_VIRTUAL uhc_virtual.c)
1012
zephyr_library_sources_ifdef(CONFIG_UHC_NXP_EHCI uhc_mcux_common.c uhc_mcux_ehci.c)

drivers/usb/uhc/Kconfig

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ module = UHC_DRIVER
3636
module-str = uhc drv
3737
source "subsys/logging/Kconfig.template.log_config"
3838

39+
source "drivers/usb/uhc/Kconfig.dwc2"
3940
source "drivers/usb/uhc/Kconfig.max3421e"
4041
source "drivers/usb/uhc/Kconfig.virtual"
4142
source "drivers/usb/uhc/Kconfig.mcux"

drivers/usb/uhc/Kconfig.dwc2

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
# Copyright (c) 2026 Roman Leonov <[email protected]>
2+
# SPDX-License-Identifier: Apache-2.0
3+
4+
config UHC_DWC2
5+
bool "DWC2 USB device controller driver"
6+
depends on DT_HAS_SNPS_DWC2_ENABLED
7+
default y
8+
select EVENTS
9+
help
10+
DWC2 USB host controller driver.
11+
12+
if UHC_DWC2
13+
14+
config UHC_DWC2_STACK_SIZE
15+
int "UHC DWC2 driver thread stack size"
16+
depends on UHC_DWC2
17+
default 512
18+
help
19+
DWC2 driver thread stack size.
20+
21+
config UHC_DWC2_THREAD_PRIORITY
22+
int "UHC DWC2 driver thread priority"
23+
depends on UHC_DWC2
24+
default 8
25+
help
26+
DWC2 driver thread priority.
27+
28+
endif # UHC_DWC2

drivers/usb/uhc/uhc_dwc2.c

Lines changed: 275 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,275 @@
1+
/*
2+
* SPDX-FileCopyrightText: Copyright (c) 2026 Roman Leonov <[email protected]>
3+
* SPDX-FileCopyrightText: Copyright Nordic Semiconductor ASA
4+
* SPDX-License-Identifier: Apache-2.0
5+
*/
6+
7+
#define DT_DRV_COMPAT snps_dwc2
8+
9+
#include <stdint.h>
10+
11+
#include <zephyr/device.h>
12+
#include <zephyr/kernel.h>
13+
#include <zephyr/devicetree.h>
14+
#include <zephyr/sys/byteorder.h>
15+
#include <zephyr/drivers/usb/uhc.h>
16+
#include <zephyr/usb/usb_ch9.h>
17+
18+
#include <zephyr/logging/log.h>
19+
LOG_MODULE_REGISTER(uhc_dwc2, CONFIG_UHC_DRIVER_LOG_LEVEL);
20+
21+
#include <usb_dwc2_hw.h>
22+
23+
#include "uhc_common.h"
24+
25+
#define UHC_DWC2_CHAN_REG(base, chan_idx) \
26+
((struct usb_dwc2_host_chan *)(((mem_addr_t)(base)) + USB_DWC2_HCCHAR(chan_idx)))
27+
28+
struct uhc_dwc2_config {
29+
/* Pointer to base address of DWC_OTG registers */
30+
struct usb_dwc2_reg *const base;
31+
/* Pointer to the stack used by the driver thread */
32+
k_thread_stack_t *stack;
33+
/* Size of the stack used by the driver thread */
34+
size_t stack_size;
35+
/* Sendor-specific structure */
36+
void *quirk_data;
37+
38+
void (*irq_enable_func)(const struct device *const dev);
39+
void (*irq_disable_func)(const struct device *const dev);
40+
};
41+
42+
struct uhc_dwc2_data {
43+
struct k_thread thread;
44+
struct k_event events;
45+
};
46+
47+
#include "uhc_dwc2_vendor_quirks.h"
48+
49+
/*
50+
* Event handling
51+
*/
52+
53+
static void uhc_dwc2_isr_handler(const struct device *dev)
54+
{
55+
/* TODO: Interrupt handling */
56+
57+
(void)uhc_dwc2_quirk_irq_clear(dev);
58+
}
59+
60+
static void uhc_dwc2_thread(void *arg0, void *arg1, void *arg2)
61+
{
62+
const struct device *const dev = (const struct device *)arg0;
63+
struct uhc_dwc2_data *const priv = uhc_get_private(dev);
64+
uint32_t events;
65+
66+
while (true) {
67+
events = k_event_wait_safe(&priv->events, UINT32_MAX, false, K_FOREVER);
68+
69+
uhc_lock_internal(dev, K_FOREVER);
70+
71+
/* TODO: handle port and channel events */
72+
(void)events;
73+
74+
uhc_unlock_internal(dev);
75+
}
76+
}
77+
78+
/*
79+
* Device driver API
80+
*/
81+
82+
static int uhc_dwc2_lock(const struct device *const dev)
83+
{
84+
struct uhc_data *data = dev->data;
85+
86+
return k_mutex_lock(&data->mutex, K_FOREVER);
87+
}
88+
89+
static int uhc_dwc2_unlock(const struct device *const dev)
90+
{
91+
struct uhc_data *data = dev->data;
92+
93+
return k_mutex_unlock(&data->mutex);
94+
}
95+
96+
static int uhc_dwc2_sof_enable(const struct device *const dev)
97+
{
98+
LOG_WRN("%s has not been implemented", __func__);
99+
100+
return -ENOSYS;
101+
}
102+
103+
static int uhc_dwc2_bus_suspend(const struct device *const dev)
104+
{
105+
LOG_WRN("%s has not been implemented", __func__);
106+
107+
return -ENOSYS;
108+
}
109+
110+
static int uhc_dwc2_bus_reset(const struct device *const dev)
111+
{
112+
LOG_WRN("%s has not been implemented", __func__);
113+
114+
return -ENOSYS;
115+
}
116+
117+
static int uhc_dwc2_bus_resume(const struct device *const dev)
118+
{
119+
LOG_WRN("%s has not been implemented", __func__);
120+
121+
return -ENOSYS;
122+
}
123+
124+
static int uhc_dwc2_enqueue(const struct device *const dev, struct uhc_transfer *const xfer)
125+
{
126+
LOG_WRN("%s has not been implemented", __func__);
127+
128+
return -ENOSYS;
129+
}
130+
131+
static int uhc_dwc2_dequeue(const struct device *const dev, struct uhc_transfer *const xfer)
132+
{
133+
LOG_WRN("%s has not been implemented", __func__);
134+
135+
return -ENOSYS;
136+
}
137+
138+
static int uhc_dwc2_preinit(const struct device *const dev)
139+
{
140+
const struct uhc_dwc2_config *const config = dev->config;
141+
struct uhc_dwc2_data *const priv = uhc_get_private(dev);
142+
int ret;
143+
144+
ret = uhc_dwc2_quirk_preinit(dev);
145+
if (ret != 0) {
146+
return ret;
147+
}
148+
149+
/*
150+
* TODO:
151+
* use devicetree to get GHWCFGn values and use them to determine the
152+
* number and type of configured endpoints in the hardware as in udc?
153+
*/
154+
155+
k_thread_create(&priv->thread,
156+
config->stack,
157+
config->stack_size,
158+
uhc_dwc2_thread,
159+
(void *)dev, NULL, NULL,
160+
K_PRIO_COOP(CONFIG_UHC_DWC2_THREAD_PRIORITY),
161+
K_ESSENTIAL,
162+
K_NO_WAIT);
163+
k_thread_name_set(&priv->thread, dev->name);
164+
165+
return 0;
166+
}
167+
168+
static int uhc_dwc2_init(const struct device *const dev)
169+
{
170+
int ret;
171+
172+
LOG_WRN("%s has not been implemented", __func__);
173+
174+
ret = uhc_dwc2_quirk_init(dev);
175+
if (ret != 0) {
176+
return ret;
177+
}
178+
179+
return -ENOSYS;
180+
}
181+
182+
static int uhc_dwc2_enable(const struct device *const dev)
183+
{
184+
int ret;
185+
186+
LOG_WRN("%s has not been implemented", __func__);
187+
188+
ret = uhc_dwc2_quirk_pre_enable(dev);
189+
if (ret != 0) {
190+
return ret;
191+
}
192+
193+
ret = uhc_dwc2_quirk_post_enable(dev);
194+
if (ret != 0) {
195+
return ret;
196+
}
197+
198+
return -ENOSYS;
199+
}
200+
201+
static int uhc_dwc2_disable(const struct device *const dev)
202+
{
203+
int ret;
204+
205+
LOG_WRN("%s has not been implemented", __func__);
206+
207+
ret = uhc_dwc2_quirk_disable(dev);
208+
if (ret != 0) {
209+
return ret;
210+
}
211+
212+
return -ENOSYS;
213+
}
214+
215+
static int uhc_dwc2_shutdown(const struct device *const dev)
216+
{
217+
int ret;
218+
219+
LOG_WRN("%s has not been implemented", __func__);
220+
221+
ret = uhc_dwc2_quirk_shutdown(dev);
222+
if (ret != 0) {
223+
return ret;
224+
}
225+
226+
return -ENOSYS;
227+
}
228+
229+
static const struct uhc_api uhc_dwc2_api = {
230+
/* Common */
231+
.lock = uhc_dwc2_lock,
232+
.unlock = uhc_dwc2_unlock,
233+
.init = uhc_dwc2_init,
234+
.enable = uhc_dwc2_enable,
235+
.disable = uhc_dwc2_disable,
236+
.shutdown = uhc_dwc2_shutdown,
237+
/* Bus related */
238+
.bus_reset = uhc_dwc2_bus_reset,
239+
.sof_enable = uhc_dwc2_sof_enable,
240+
.bus_suspend = uhc_dwc2_bus_suspend,
241+
.bus_resume = uhc_dwc2_bus_resume,
242+
/* EP related */
243+
.ep_enqueue = uhc_dwc2_enqueue,
244+
.ep_dequeue = uhc_dwc2_dequeue,
245+
};
246+
247+
#define UHC_DWC2_DEVICE_DEFINE(n) \
248+
K_THREAD_STACK_DEFINE(uhc_dwc2_stack_##n, CONFIG_UHC_DWC2_STACK_SIZE); \
249+
UHC_DWC2_IRQ_DT_INST_DEFINE(n) \
250+
UHC_DWC2_QUIRK_DATA(n) \
251+
\
252+
static struct uhc_dwc2_data uhc_dwc2_priv_##n = { \
253+
.events = Z_EVENT_INITIALIZER(uhc_dwc2_priv_##n.events), \
254+
}; \
255+
\
256+
static struct uhc_data uhc_data_##n = { \
257+
.mutex = Z_MUTEX_INITIALIZER(uhc_data_##n.mutex), \
258+
.priv = &uhc_dwc2_priv_##n, \
259+
}; \
260+
\
261+
static const struct uhc_dwc2_config uhc_dwc2_config_##n = { \
262+
.base = (struct usb_dwc2_reg *)DT_INST_REG_ADDR(n), \
263+
.stack = uhc_dwc2_stack_##n, \
264+
.stack_size = K_THREAD_STACK_SIZEOF(uhc_dwc2_stack_##n), \
265+
.quirk_data = &uhc_dwc2_quirk_data_##n, \
266+
.irq_enable_func = uhc_dwc2_irq_enable_func_##n, \
267+
.irq_disable_func = uhc_dwc2_irq_disable_func_##n, \
268+
}; \
269+
\
270+
DEVICE_DT_INST_DEFINE(n, uhc_dwc2_preinit, NULL, &uhc_data_##n, \
271+
&uhc_dwc2_config_##n, POST_KERNEL, \
272+
CONFIG_KERNEL_INIT_PRIORITY_DEVICE, \
273+
&uhc_dwc2_api);
274+
275+
DT_INST_FOREACH_STATUS_OKAY(UHC_DWC2_DEVICE_DEFINE)

0 commit comments

Comments
 (0)