-
Notifications
You must be signed in to change notification settings - Fork 9.1k
drivers: usb: uhc: Added dwc2 vendor quirks for esp32 [part3] #102965
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Closed
roma-jam
wants to merge
2
commits into
zephyrproject-rtos:main
from
roma-jam-lab:pr-add-dwc2-uhc-vendor-quirks-esp32
Closed
Changes from all commits
Commits
Show all changes
2 commits
Select commit
Hold shift + click to select a range
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,26 @@ | ||
| # Copyright (c) 2026 Roman Leonov <jam_roma@yahoo.com> | ||
| # SPDX-License-Identifier: Apache-2.0 | ||
|
|
||
| config UHC_DWC2 | ||
| bool "DWC2 USB host controller driver" | ||
| depends on DT_HAS_SNPS_DWC2_ENABLED | ||
| default y | ||
| select EVENTS | ||
| help | ||
| DWC2 USB host controller driver. | ||
|
|
||
| if UHC_DWC2 | ||
|
|
||
| config UHC_DWC2_STACK_SIZE | ||
| int "UHC DWC2 driver thread stack size" | ||
| default 512 | ||
| help | ||
| DWC2 driver thread stack size. | ||
|
|
||
| config UHC_DWC2_THREAD_PRIORITY | ||
| int "UHC DWC2 driver thread priority" | ||
| default 8 | ||
| help | ||
| DWC2 driver thread priority. | ||
|
|
||
| endif # UHC_DWC2 |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,247 @@ | ||
| /* | ||
| * Copyright (c) 2026 Roman Leonov <jam_roma@yahoo.com> | ||
| * | ||
| * SPDX-License-Identifier: Apache-2.0 | ||
| */ | ||
|
|
||
| #define DT_DRV_COMPAT snps_dwc2 | ||
|
|
||
| #include "uhc_common.h" | ||
| #include "uhc_dwc2.h" | ||
|
|
||
| #include <zephyr/kernel.h> | ||
| #include <zephyr/devicetree.h> | ||
| #include <zephyr/sys/byteorder.h> | ||
| #include <zephyr/drivers/usb/uhc.h> | ||
| #include <zephyr/usb/usb_ch9.h> | ||
|
|
||
| #include <zephyr/logging/log.h> | ||
| LOG_MODULE_REGISTER(uhc_dwc2, CONFIG_UHC_DRIVER_LOG_LEVEL); | ||
|
|
||
| #define UHC_DWC2_CHAN_REG(base, chan_idx) \ | ||
| ((struct usb_dwc2_host_chan *)(((mem_addr_t)(base)) + USB_DWC2_HCCHAR(chan_idx))) | ||
|
|
||
| struct uhc_dwc2_data { | ||
| struct k_thread thread; | ||
| struct k_event event; | ||
| }; | ||
|
|
||
| static void uhc_dwc2_isr_handler(const struct device *dev) | ||
| { | ||
| /* TODO: Interrupt handling */ | ||
| } | ||
|
|
||
| static void uhc_dwc2_thread(void *arg0, void *arg1, void *arg2) | ||
| { | ||
| const struct device *const dev = (const struct device *)arg0; | ||
| struct uhc_dwc2_data *const priv = uhc_get_private(dev); | ||
| uint32_t event; | ||
|
|
||
| while (true) { | ||
| event = k_event_wait_safe(&priv->event, UINT32_MAX, false, K_FOREVER); | ||
|
|
||
| uhc_lock_internal(dev, K_FOREVER); | ||
|
|
||
| /* TODO: handle port and channel events */ | ||
| (void) event; | ||
|
|
||
| uhc_unlock_internal(dev); | ||
| } | ||
| } | ||
|
|
||
| static int uhc_dwc2_lock(const struct device *const dev) | ||
| { | ||
| struct uhc_data *data = dev->data; | ||
|
|
||
| return k_mutex_lock(&data->mutex, K_FOREVER); | ||
| } | ||
|
|
||
| static int uhc_dwc2_unlock(const struct device *const dev) | ||
| { | ||
| struct uhc_data *data = dev->data; | ||
|
|
||
| return k_mutex_unlock(&data->mutex); | ||
| } | ||
|
|
||
| static int uhc_dwc2_sof_enable(const struct device *const dev) | ||
| { | ||
| LOG_WRN("%s has not been implemented", __func__); | ||
|
|
||
| return -ENOSYS; | ||
| } | ||
|
|
||
| static int uhc_dwc2_bus_suspend(const struct device *const dev) | ||
| { | ||
| LOG_WRN("%s has not been implemented", __func__); | ||
|
|
||
| return -ENOSYS; | ||
| } | ||
|
|
||
| static int uhc_dwc2_bus_reset(const struct device *const dev) | ||
| { | ||
| LOG_WRN("%s has not been implemented", __func__); | ||
|
|
||
| return -ENOSYS; | ||
| } | ||
|
|
||
| static int uhc_dwc2_bus_resume(const struct device *const dev) | ||
| { | ||
| LOG_WRN("%s has not been implemented", __func__); | ||
|
|
||
| return -ENOSYS; | ||
| } | ||
|
|
||
| static int uhc_dwc2_enqueue(const struct device *const dev, struct uhc_transfer *const xfer) | ||
| { | ||
| LOG_WRN("%s has not been implemented", __func__); | ||
|
|
||
| return -ENOSYS; | ||
| } | ||
|
|
||
| static int uhc_dwc2_dequeue(const struct device *const dev, struct uhc_transfer *const xfer) | ||
| { | ||
| LOG_WRN("%s has not been implemented", __func__); | ||
|
|
||
| return -ENOSYS; | ||
| } | ||
|
|
||
| static int uhc_dwc2_preinit(const struct device *const dev) | ||
| { | ||
| const struct uhc_dwc2_config *const config = dev->config; | ||
| struct uhc_dwc2_data *const priv = uhc_get_private(dev); | ||
| struct uhc_data *const data = dev->data; | ||
|
|
||
| /* Initialize the private data structure */ | ||
| memset(priv, 0, sizeof(struct uhc_dwc2_data)); | ||
| k_mutex_init(&data->mutex); | ||
| k_event_init(&priv->event); | ||
|
|
||
| uhc_dwc2_quirk_caps(dev); | ||
|
|
||
| /* | ||
| * TODO: | ||
| * use devicetree to get GHWCFGn values and use them to determine the | ||
| * number and type of configured endpoints in the hardware as in udc? | ||
| */ | ||
|
|
||
| k_thread_create(&priv->thread, | ||
| config->stack, | ||
| config->stack_size, | ||
| uhc_dwc2_thread, | ||
| (void *)dev, NULL, NULL, | ||
| K_PRIO_COOP(CONFIG_UHC_DWC2_THREAD_PRIORITY), | ||
| K_ESSENTIAL, | ||
| K_NO_WAIT); | ||
| k_thread_name_set(&priv->thread, dev->name); | ||
|
|
||
| return 0; | ||
| } | ||
|
|
||
| static int uhc_dwc2_init(const struct device *const dev) | ||
| { | ||
| LOG_WRN("%s has not been implemented", __func__); | ||
|
|
||
| return -ENOSYS; | ||
| } | ||
|
|
||
| static int uhc_dwc2_enable(const struct device *const dev) | ||
| { | ||
| LOG_WRN("%s has not been implemented", __func__); | ||
|
|
||
| return -ENOSYS; | ||
| } | ||
|
|
||
| static int uhc_dwc2_disable(const struct device *const dev) | ||
| { | ||
| LOG_WRN("%s has not been implemented", __func__); | ||
|
|
||
| return -ENOSYS; | ||
| } | ||
|
|
||
| static int uhc_dwc2_shutdown(const struct device *const dev) | ||
| { | ||
| LOG_WRN("%s has not been implemented", __func__); | ||
|
|
||
| return -ENOSYS; | ||
| } | ||
|
|
||
| /* | ||
| * Device Definition and Initialization | ||
| */ | ||
| static const struct uhc_api uhc_dwc2_api = { | ||
| /* Common */ | ||
| .lock = uhc_dwc2_lock, | ||
| .unlock = uhc_dwc2_unlock, | ||
| .init = uhc_dwc2_init, | ||
| .enable = uhc_dwc2_enable, | ||
| .disable = uhc_dwc2_disable, | ||
| .shutdown = uhc_dwc2_shutdown, | ||
| /* Bus related */ | ||
| .bus_reset = uhc_dwc2_bus_reset, | ||
| .sof_enable = uhc_dwc2_sof_enable, | ||
| .bus_suspend = uhc_dwc2_bus_suspend, | ||
| .bus_resume = uhc_dwc2_bus_resume, | ||
| /* EP related */ | ||
| .ep_enqueue = uhc_dwc2_enqueue, | ||
| .ep_dequeue = uhc_dwc2_dequeue, | ||
| }; | ||
|
|
||
| #define UHC_DWC2_DT_INST_REG_ADDR(n) \ | ||
| COND_CODE_1(DT_NUM_REGS(DT_DRV_INST(n)), \ | ||
| (DT_INST_REG_ADDR(n)), \ | ||
| (DT_INST_REG_ADDR_BY_NAME(n, core))) | ||
|
|
||
| #if !defined(UHC_DWC2_IRQ_DT_INST_DEFINE) | ||
| #define UHC_DWC2_IRQ_FLAGS_TYPE0(n) 0 | ||
| #define UHC_DWC2_IRQ_FLAGS_TYPE1(n) DT_INST_IRQ(n, type) | ||
| #define DW_IRQ_FLAGS(n) \ | ||
| _CONCAT(UHC_DWC2_IRQ_FLAGS_TYPE, DT_INST_IRQ_HAS_CELL(n, type))(n) | ||
|
|
||
| #define UHC_DWC2_IRQ_DT_INST_DEFINE(n) \ | ||
| static void uhc_dwc2_irq_enable_func_##n(const struct device *dev) \ | ||
| { \ | ||
| IRQ_CONNECT(DT_INST_IRQN(n), \ | ||
| DT_INST_IRQ(n, priority), \ | ||
| uhc_dwc2_isr_handler, \ | ||
| DEVICE_DT_INST_GET(n), \ | ||
| DW_IRQ_FLAGS(n)); \ | ||
| \ | ||
| irq_enable(DT_INST_IRQN(n)); \ | ||
| } \ | ||
| \ | ||
| static void uhc_dwc2_irq_disable_func_##n(const struct device *dev) \ | ||
| { \ | ||
| irq_disable(DT_INST_IRQN(n)); \ | ||
| } | ||
| #endif | ||
|
|
||
| /* Multi-instance device definition for DWC2 host controller */ | ||
| #define UHC_DWC2_DEVICE_DEFINE(n) \ | ||
| \ | ||
| K_THREAD_STACK_DEFINE(uhc_dwc2_stack_##n, \ | ||
| CONFIG_UHC_DWC2_STACK_SIZE); \ | ||
| \ | ||
| UHC_DWC2_IRQ_DT_INST_DEFINE(n) \ | ||
| \ | ||
| static struct uhc_dwc2_data uhc_dwc2_priv_##n = { 0 }; \ | ||
| \ | ||
| static struct uhc_data uhc_data_##n = { \ | ||
| .mutex = Z_MUTEX_INITIALIZER(uhc_data_##n.mutex), \ | ||
| .priv = &uhc_dwc2_priv_##n, \ | ||
| }; \ | ||
| \ | ||
| static const struct uhc_dwc2_config uhc_dwc2_config_##n = { \ | ||
| .base = (struct usb_dwc2_reg *)UHC_DWC2_DT_INST_REG_ADDR(n), \ | ||
| .quirks = UHC_DWC2_VENDOR_QUIRK_GET(n), \ | ||
| .stack = uhc_dwc2_stack_##n, \ | ||
| .stack_size = K_THREAD_STACK_SIZEOF(uhc_dwc2_stack_##n), \ | ||
| .irq_enable_func = uhc_dwc2_irq_enable_func_##n, \ | ||
| .irq_disable_func = uhc_dwc2_irq_disable_func_##n, \ | ||
| }; \ | ||
| \ | ||
| DEVICE_DT_INST_DEFINE(n, uhc_dwc2_preinit, NULL, \ | ||
| &uhc_data_##n, &uhc_dwc2_config_##n, \ | ||
| POST_KERNEL, CONFIG_KERNEL_INIT_PRIORITY_DEVICE, \ | ||
| &uhc_dwc2_api); | ||
|
|
||
| DT_INST_FOREACH_STATUS_OKAY(UHC_DWC2_DEVICE_DEFINE) | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,95 @@ | ||
| /* | ||
| * Copyright (c) 2026 Roman Leonov <jam_roma@yahoo.com> | ||
| * | ||
| * SPDX-License-Identifier: Apache-2.0 | ||
| */ | ||
|
|
||
| #ifndef ZEPHYR_DRIVERS_USB_UHC_DWC2_H | ||
| #define ZEPHYR_DRIVERS_USB_UHC_DWC2_H | ||
|
|
||
| #include <stdint.h> | ||
| #include <zephyr/device.h> | ||
| #include <zephyr/drivers/usb/uhc.h> | ||
| #include <usb_dwc2_hw.h> | ||
|
|
||
| /* Vendor quirks per driver instance */ | ||
| struct uhc_dwc2_vendor_quirks { | ||
| /* Called at the beginning of uhc_dwc2_init() */ | ||
| int (*init)(const struct device *dev); | ||
| /* Called on uhc_dwc2_enable() before the controller is initialized */ | ||
| int (*pre_enable)(const struct device *dev); | ||
| /* Called on uhc_dwc2_enable() after the controller is initialized */ | ||
| int (*post_enable)(const struct device *dev); | ||
| /* Called at the end of uhc_dwc2_disable() */ | ||
| int (*disable)(const struct device *dev); | ||
| /* Called at the end of uhc_dwc2_shutdown() */ | ||
| int (*shutdown)(const struct device *dev); | ||
| /* Called at the end of IRQ handling */ | ||
| int (*irq_clear)(const struct device *dev); | ||
| /* Called on driver pre-init */ | ||
| int (*caps)(const struct device *dev); | ||
| /* Called on PHY pre-select */ | ||
| int (*phy_pre_select)(const struct device *dev); | ||
| /* Called on PHY post-select and core reset */ | ||
| int (*phy_post_select)(const struct device *dev); | ||
| /* Called while waiting for bits that require PHY to be clocked */ | ||
| int (*is_phy_clk_off)(const struct device *dev); | ||
| /* PHY get clock */ | ||
| int (*get_phy_clk)(const struct device *dev); | ||
| /* Called after hibernation entry sequence */ | ||
| int (*post_hibernation_entry)(const struct device *dev); | ||
| /* Called before hibernation exit sequence */ | ||
| int (*pre_hibernation_exit)(const struct device *dev); | ||
| }; | ||
|
|
||
| /* Driver configuration per instance */ | ||
| struct uhc_dwc2_config { | ||
| /* Pointer to base address of DWC_OTG registers */ | ||
| struct usb_dwc2_reg *const base; | ||
| /* Pointer to vendor quirks or NULL */ | ||
| const struct uhc_dwc2_vendor_quirks *const quirks; | ||
| /* Pointer to the stack used by the driver thread */ | ||
| k_thread_stack_t *stack; | ||
| /* Size of the stack used by the driver thread */ | ||
| size_t stack_size; | ||
|
|
||
| void (*irq_enable_func)(const struct device *dev); | ||
| void (*irq_disable_func)(const struct device *dev); | ||
| }; | ||
|
|
||
| #include "uhc_dwc2_vendor_quirks.h" | ||
|
|
||
| #define UHC_DWC2_VENDOR_QUIRK_GET(n) \ | ||
| COND_CODE_1(DT_NODE_VENDOR_HAS_IDX(DT_DRV_INST(n), 1), \ | ||
| (&uhc_dwc2_vendor_quirks_##n), \ | ||
| (NULL)) | ||
|
|
||
| #define DWC2_QUIRK_FUNC_DEFINE(fname) \ | ||
| static inline int uhc_dwc2_quirk_##fname(const struct device *dev) \ | ||
| { \ | ||
| const struct uhc_dwc2_config *const config = dev->config; \ | ||
| const struct uhc_dwc2_vendor_quirks *const quirks = \ | ||
| COND_CODE_1(IS_EQ(DT_NUM_INST_STATUS_OKAY(snps_dwc2), 1), \ | ||
| (UHC_DWC2_VENDOR_QUIRK_GET(0); ARG_UNUSED(config);), \ | ||
| (config->quirks;)) \ | ||
| if (quirks != NULL && quirks->fname != NULL) { \ | ||
| return quirks->fname(dev); \ | ||
| } \ | ||
| return 0; \ | ||
| } | ||
|
|
||
| DWC2_QUIRK_FUNC_DEFINE(init) | ||
| DWC2_QUIRK_FUNC_DEFINE(pre_enable) | ||
| DWC2_QUIRK_FUNC_DEFINE(post_enable) | ||
| DWC2_QUIRK_FUNC_DEFINE(disable) | ||
| DWC2_QUIRK_FUNC_DEFINE(shutdown) | ||
| DWC2_QUIRK_FUNC_DEFINE(irq_clear) | ||
| DWC2_QUIRK_FUNC_DEFINE(caps) | ||
| DWC2_QUIRK_FUNC_DEFINE(phy_pre_select) | ||
| DWC2_QUIRK_FUNC_DEFINE(phy_post_select) | ||
| DWC2_QUIRK_FUNC_DEFINE(is_phy_clk_off) | ||
| DWC2_QUIRK_FUNC_DEFINE(get_phy_clk) | ||
| DWC2_QUIRK_FUNC_DEFINE(post_hibernation_entry) | ||
| DWC2_QUIRK_FUNC_DEFINE(pre_hibernation_exit) | ||
|
|
||
| #endif /* ZEPHYR_DRIVERS_USB_UHC_DWC2_H */ |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.