-
Notifications
You must be signed in to change notification settings - Fork 9.1k
drivers: usb: Add VBUS detector #103840
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
Closed
drivers: usb: Add VBUS detector #103840
Changes from all commits
Commits
Show all changes
5 commits
Select commit
Hold shift + click to select a range
92381ca
dts: bindings: usb: Add VBUS detect GPIO binding
nandojve 57c33eb
drivers: usb: Add VBUS detection subsystem
nandojve 1d7d2ea
tests: drivers: usb: Add VBUS detect tests
nandojve 0f17e40
samples: drivers: usb: Add VBUS detect sample
nandojve 846ce7a
boards: disco_l475_iot1: Add VBUS detection
nandojve 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
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,6 @@ | ||
| # Copyright (c) 2026 Leica Geosystems AG | ||
| # SPDX-License-Identifier: Apache-2.0 | ||
|
|
||
| zephyr_library() | ||
|
|
||
| zephyr_library_sources_ifdef(CONFIG_USB_VBUS_DETECT_GPIO usb_vbus_detect_gpio.c) |
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,32 @@ | ||
| # Copyright (c) 2026 Leica Geosystems AG | ||
| # SPDX-License-Identifier: Apache-2.0 | ||
|
|
||
| menuconfig USB_VBUS_DETECT | ||
| bool "USB VBUS Detection Drivers" | ||
| default y if DT_HAS_ZEPHYR_USB_VBUS_DETECT_GPIO_ENABLED | ||
| depends on UDC_DRIVER | ||
| help | ||
| Enable USB VBUS detection driver support. Automatically enabled | ||
| when a VBUS detect node is present in the devicetree. | ||
|
|
||
| if USB_VBUS_DETECT | ||
|
|
||
| module = USB_VBUS_DETECT | ||
| module-str = usb_vbus_detect | ||
| source "subsys/logging/Kconfig.template.log_config" | ||
|
|
||
| config USB_VBUS_DETECT_INIT_PRIORITY | ||
| int "USB VBUS detect driver init priority" | ||
| default 80 | ||
| help | ||
| USB VBUS detection driver initialization priority. | ||
|
|
||
| config USB_VBUS_DETECT_GPIO | ||
| bool "GPIO-based VBUS detector" | ||
| default y | ||
| depends on DT_HAS_ZEPHYR_USB_VBUS_DETECT_GPIO_ENABLED | ||
| depends on GPIO | ||
| help | ||
| Enable GPIO-based USB VBUS detection driver. | ||
|
|
||
| endif # USB_VBUS_DETECT |
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,204 @@ | ||
| /* | ||
| * Copyright (c) 2026 Leica Geosystems AG | ||
| * | ||
| * SPDX-License-Identifier: Apache-2.0 | ||
| */ | ||
|
|
||
| #define DT_DRV_COMPAT zephyr_usb_vbus_detect_gpio | ||
|
|
||
| #include <zephyr/device.h> | ||
| #include <zephyr/drivers/gpio.h> | ||
| #include <zephyr/drivers/usb/udc.h> | ||
| #include <zephyr/drivers/usb/usb_vbus_detect.h> | ||
| #include <zephyr/kernel.h> | ||
| #include <zephyr/logging/log.h> | ||
|
|
||
| #include "../udc/udc_common.h" | ||
|
|
||
| LOG_MODULE_REGISTER(usb_vbus_detect_gpio, CONFIG_USB_VBUS_DETECT_LOG_LEVEL); | ||
|
|
||
| struct usb_vbus_detect_gpio_config { | ||
| struct gpio_dt_spec vbus_gpio; | ||
| const struct device *const *udc_list; | ||
| size_t udc_count; | ||
| uint32_t debounce_ms; | ||
| }; | ||
|
|
||
| struct usb_vbus_detect_gpio_data { | ||
| const struct device *dev; | ||
| struct gpio_callback gpio_cb; | ||
| struct k_work_delayable debounce_work; | ||
| bool last_state; | ||
| bool initialized; | ||
| }; | ||
|
|
||
| static int usb_vbus_notify_device(const struct device *udc_dev, | ||
| enum udc_event_type event) | ||
| { | ||
| if (!udc_is_initialized(udc_dev)) { | ||
| LOG_DBG("Device %s not initialized, skipping", udc_dev->name); | ||
| return -EPERM; | ||
| } | ||
|
|
||
| return udc_submit_event(udc_dev, event, 0); | ||
| } | ||
|
|
||
| static void usb_vbus_notify_all(const struct usb_vbus_detect_gpio_config *cfg, | ||
| bool vbus_ready) | ||
| { | ||
| enum udc_event_type event = vbus_ready ? UDC_EVT_VBUS_READY | ||
| : UDC_EVT_VBUS_REMOVED; | ||
| const char *event_name = vbus_ready ? "VBUS_READY" : "VBUS_REMOVED"; | ||
| int ret; | ||
|
|
||
| for (size_t i = 0; i < cfg->udc_count; i++) { | ||
| const struct device *udc_dev = cfg->udc_list[i]; | ||
|
|
||
| ret = usb_vbus_notify_device(udc_dev, event); | ||
| if (ret < 0 && ret != -EPERM) { | ||
| LOG_ERR("Failed to signal %s to %s: %d", | ||
| event_name, udc_dev->name, ret); | ||
| } else if (ret == 0) { | ||
| LOG_INF("%s sent to %s", event_name, udc_dev->name); | ||
| } | ||
| } | ||
| } | ||
|
|
||
| static void usb_vbus_detect_gpio_work_handler(struct k_work *work) | ||
| { | ||
| struct k_work_delayable *dwork = k_work_delayable_from_work(work); | ||
| struct usb_vbus_detect_gpio_data *data = | ||
| CONTAINER_OF(dwork, struct usb_vbus_detect_gpio_data, | ||
| debounce_work); | ||
| const struct device *dev = data->dev; | ||
| const struct usb_vbus_detect_gpio_config *cfg = dev->config; | ||
| int state; | ||
|
|
||
| state = gpio_pin_get_dt(&cfg->vbus_gpio); | ||
| if (state < 0) { | ||
| LOG_ERR("Failed to read VBUS GPIO: %d", state); | ||
| return; | ||
| } | ||
|
|
||
| LOG_DBG("VBUS state: %d (last: %d, initialized: %d)", | ||
| state, data->last_state, data->initialized); | ||
|
|
||
| if (!data->initialized) { | ||
| data->last_state = state; | ||
| data->initialized = true; | ||
| usb_vbus_notify_all(cfg, state != 0); | ||
| return; | ||
| } | ||
|
|
||
| if (state != data->last_state) { | ||
| data->last_state = state; | ||
| usb_vbus_notify_all(cfg, state != 0); | ||
| } | ||
| } | ||
|
|
||
| static void usb_vbus_detect_gpio_callback(const struct device *port, | ||
| struct gpio_callback *cb, | ||
| gpio_port_pins_t pins) | ||
| { | ||
| struct usb_vbus_detect_gpio_data *data = | ||
| CONTAINER_OF(cb, struct usb_vbus_detect_gpio_data, gpio_cb); | ||
| const struct device *dev = data->dev; | ||
| const struct usb_vbus_detect_gpio_config *cfg = dev->config; | ||
|
|
||
| ARG_UNUSED(port); | ||
| ARG_UNUSED(pins); | ||
|
|
||
| k_work_reschedule(&data->debounce_work, K_MSEC(cfg->debounce_ms)); | ||
| } | ||
|
|
||
| static int usb_vbus_detect_gpio_init(const struct device *dev) | ||
| { | ||
| const struct usb_vbus_detect_gpio_config *cfg = dev->config; | ||
| struct usb_vbus_detect_gpio_data *data = dev->data; | ||
| int ret; | ||
|
|
||
| data->dev = dev; | ||
| data->initialized = false; | ||
|
|
||
| if (!gpio_is_ready_dt(&cfg->vbus_gpio)) { | ||
| LOG_ERR("VBUS GPIO device not ready"); | ||
| return -ENODEV; | ||
| } | ||
|
|
||
| /* Check all UDC devices are ready and inform them about VBUS detect */ | ||
| for (size_t i = 0; i < cfg->udc_count; i++) { | ||
| const struct device *udc_dev = cfg->udc_list[i]; | ||
|
|
||
| if (!device_is_ready(udc_dev)) { | ||
| LOG_ERR("UDC device %s not ready", udc_dev->name); | ||
| return -ENODEV; | ||
| } | ||
|
|
||
| /* Inform UDC that external VBUS detection is available */ | ||
| udc_set_can_detect_vbus(udc_dev); | ||
| LOG_DBG("Registered VBUS detection for %s", udc_dev->name); | ||
| } | ||
|
|
||
| ret = gpio_pin_configure_dt(&cfg->vbus_gpio, GPIO_INPUT); | ||
| if (ret < 0) { | ||
| LOG_ERR("Failed to configure VBUS GPIO: %d", ret); | ||
| return ret; | ||
| } | ||
|
|
||
| k_work_init_delayable(&data->debounce_work, | ||
| usb_vbus_detect_gpio_work_handler); | ||
|
|
||
| gpio_init_callback(&data->gpio_cb, usb_vbus_detect_gpio_callback, | ||
| BIT(cfg->vbus_gpio.pin)); | ||
|
|
||
| ret = gpio_add_callback(cfg->vbus_gpio.port, &data->gpio_cb); | ||
| if (ret < 0) { | ||
| LOG_ERR("Failed to add GPIO callback: %d", ret); | ||
| return ret; | ||
| } | ||
|
|
||
| ret = gpio_pin_interrupt_configure_dt(&cfg->vbus_gpio, | ||
| GPIO_INT_EDGE_BOTH); | ||
| if (ret < 0) { | ||
| LOG_ERR("Failed to configure GPIO interrupt: %d", ret); | ||
| return ret; | ||
| } | ||
|
|
||
| /* Schedule initial state check */ | ||
| k_work_reschedule(&data->debounce_work, K_MSEC(cfg->debounce_ms)); | ||
|
|
||
| LOG_INF("USB VBUS detect initialized with %zu UDC device(s)", | ||
| cfg->udc_count); | ||
|
|
||
| return 0; | ||
| } | ||
|
|
||
| /* Helper macro to get device pointer for each phandle in udc list */ | ||
| #define USB_VBUS_UDC_DEVICE_GET(node_id, prop, idx) \ | ||
| DEVICE_DT_GET(DT_PHANDLE_BY_IDX(node_id, prop, idx)), | ||
|
|
||
| #define USB_VBUS_DETECT_GPIO_DEFINE(inst) \ | ||
| static struct usb_vbus_detect_gpio_data \ | ||
| usb_vbus_detect_gpio_data_##inst; \ | ||
| \ | ||
| static const struct device *const \ | ||
| usb_vbus_udc_list_##inst[] = { \ | ||
| DT_INST_FOREACH_PROP_ELEM(inst, udc, USB_VBUS_UDC_DEVICE_GET) \ | ||
| }; \ | ||
| \ | ||
| static const struct usb_vbus_detect_gpio_config \ | ||
| usb_vbus_detect_gpio_config_##inst = { \ | ||
| .vbus_gpio = GPIO_DT_SPEC_INST_GET(inst, vbus_gpios), \ | ||
| .udc_list = usb_vbus_udc_list_##inst, \ | ||
| .udc_count = ARRAY_SIZE(usb_vbus_udc_list_##inst), \ | ||
| .debounce_ms = DT_INST_PROP(inst, debounce_ms), \ | ||
| }; \ | ||
| \ | ||
| DEVICE_DT_INST_DEFINE(inst, usb_vbus_detect_gpio_init, NULL, \ | ||
| &usb_vbus_detect_gpio_data_##inst, \ | ||
| &usb_vbus_detect_gpio_config_##inst, \ | ||
| POST_KERNEL, \ | ||
| CONFIG_USB_VBUS_DETECT_INIT_PRIORITY, \ | ||
| NULL); | ||
|
|
||
| DT_INST_FOREACH_STATUS_OKAY(USB_VBUS_DETECT_GPIO_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,26 @@ | ||
| # Copyright (c) 2026 Leica Geosystems AG | ||
| # SPDX-License-Identifier: Apache-2.0 | ||
|
|
||
| description: GPIO-based USB VBUS detection | ||
|
|
||
| compatible: "zephyr,usb-vbus-detect-gpio" | ||
|
|
||
| include: base.yaml | ||
|
|
||
| properties: | ||
| vbus-gpios: | ||
| type: phandle-array | ||
| required: true | ||
| description: GPIO pin for VBUS sensing | ||
|
|
||
| udc: | ||
| type: phandles | ||
| required: true | ||
| description: | | ||
| List of USB device controllers to receive VBUS events. | ||
| All devices in this list will be notified when VBUS state changes. | ||
|
|
||
| debounce-ms: | ||
| type: int | ||
| default: 20 | ||
| description: Debounce interval in milliseconds |
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,52 @@ | ||
| /* | ||
| * Copyright (c) 2026 Leica Geosystems AG | ||
| * | ||
| * SPDX-License-Identifier: Apache-2.0 | ||
| */ | ||
|
|
||
| /** | ||
| * @file | ||
| * @brief USB VBUS detection API | ||
| * | ||
| * This API provides functions for VBUS detection drivers to notify | ||
| * the USB device controller of VBUS state changes. | ||
| */ | ||
|
|
||
| #ifndef ZEPHYR_INCLUDE_DRIVERS_USB_USB_VBUS_DETECT_H_ | ||
| #define ZEPHYR_INCLUDE_DRIVERS_USB_USB_VBUS_DETECT_H_ | ||
|
|
||
| #include <zephyr/device.h> | ||
|
|
||
| #ifdef __cplusplus | ||
| extern "C" { | ||
| #endif | ||
|
|
||
| /** | ||
| * @brief Notify USB stack that VBUS is ready. | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think this will need some versioning. |
||
| * | ||
| * Called by VBUS detection drivers when VBUS voltage reaches | ||
| * a stable valid level. | ||
| * | ||
| * @param udc_dev Pointer to USB device controller device. | ||
| * @return 0 on success, negative errno on failure. | ||
| * @retval -EPERM if controller is not initialized. | ||
| */ | ||
| int usb_vbus_ready(const struct device *udc_dev); | ||
|
|
||
| /** | ||
| * @brief Notify USB stack that VBUS was removed. | ||
| * | ||
| * Called by VBUS detection drivers when VBUS voltage drops | ||
| * below the valid threshold. | ||
| * | ||
| * @param udc_dev Pointer to USB device controller device. | ||
| * @return 0 on success, negative errno on failure. | ||
| * @retval -EPERM if controller is not initialized. | ||
| */ | ||
| int usb_vbus_removed(const struct device *udc_dev); | ||
|
|
||
| #ifdef __cplusplus | ||
| } | ||
| #endif | ||
|
|
||
| #endif /* ZEPHYR_INCLUDE_DRIVERS_USB_USB_VBUS_DETECT_H_ */ | ||
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,8 @@ | ||
| # Copyright (c) 2026 Leica Geosystems AG | ||
| # SPDX-License-Identifier: Apache-2.0 | ||
|
|
||
| cmake_minimum_required(VERSION 3.20.0) | ||
| find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE}) | ||
| project(usb_vbus_detect_sample) | ||
|
|
||
| target_sources(app PRIVATE src/main.c) |
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,9 @@ | ||
| # Copyright (c) 2026 Leica Geosystems AG | ||
| # SPDX-License-Identifier: Apache-2.0 | ||
|
|
||
| CONFIG_LOG=y | ||
| CONFIG_USB_DEVICE_STACK_NEXT=y | ||
| CONFIG_USB_VBUS_DETECT=y | ||
| CONFIG_USB_VBUS_DETECT_GPIO=y | ||
| CONFIG_USB_VBUS_DETECT_LOG_LEVEL_DBG=y | ||
| CONFIG_UDC_DRIVER_LOG_LEVEL_DBG=y |
Oops, something went wrong.
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.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
How is this synchronized against UDC init and USB stack using the value?