summaryrefslogtreecommitdiff
path: root/drivers/usb/core/offload.c
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2025-10-04 16:07:08 -0700
committerLinus Torvalds <torvalds@linux-foundation.org>2025-10-04 16:07:08 -0700
commitc6006b8ca14dcc604567be99fc4863e6e11ab6e3 (patch)
treeb768136932f3c40765f754ed086c488b81c9ab73 /drivers/usb/core/offload.c
parent5d2f4730bb7550d64c87785f1035d0e641dbc979 (diff)
parente40b984b6c4ce3f80814f39f86f87b2a48f2e662 (diff)
Merge tag 'usb-6.18-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/usb
Pull USB / Thunderbolt updates from Greg KH: "Here is the big set of USB and thunderbolt drivers for 6.18-rc1. It was another normal development cycle, with lots of the usual drivers getting updates: - Thunderbolt driver cleanups and additions - dwc3 driver updates - dwc2 driver updates - typec driver updates - xhci driver updates and additions - offload USB engine updates for better power management - unused tracepoint removals - usb gadget fixes and updates as more users start to rely on these drivers instead of the "old" function gadget drivers - new USB device ids - other minor driver USB driver updates - new USB I/O driver framework and driver additions" The last item, the usb i/o driver, has an i2c and gpio driver added through this tree. Those drivers were acked by the respective subsystem maintainers. All of these have been in linux-next for a while" * tag 'usb-6.18-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/usb: (132 commits) usb: vhci-hcd: Prevent suspending virtually attached devices USB: serial: option: add SIMCom 8230C compositions thunderbolt: Fix use-after-free in tb_dp_dprx_work usb: xhci: align PORTSC trace with one-based port numbering usb: xhci: correct indentation for PORTSC tracing function usb: xhci: improve TR Dequeue Pointer mask usb: xhci-pci: add support for hosts with zero USB3 ports usb: xhci: Update a comment about Stop Endpoint retries Revert "usb: xhci: Avoid Stop Endpoint retry loop if the endpoint seems Running" usb: gadget: f_rndis: Refactor bind path to use __free() usb: gadget: f_ecm: Refactor bind path to use __free() usb: gadget: f_acm: Refactor bind path to use __free() usb: gadget: f_ncm: Refactor bind path to use __free() usb: gadget: Introduce free_usb_request helper usb: gadget: Store endpoint pointer in usb_request usb: host: xhci-rcar: Add Renesas RZ/G3E USB3 Host driver support usb: host: xhci-plat: Add .post_resume_quirk for struct xhci_plat_priv usb: host: xhci-rcar: Move R-Car reg definitions dt-bindings: usb: Document Renesas RZ/G3E USB3HOST usb: gadget: f_fs: Fix epfile null pointer access after ep enable. ...
Diffstat (limited to 'drivers/usb/core/offload.c')
-rw-r--r--drivers/usb/core/offload.c136
1 files changed, 136 insertions, 0 deletions
diff --git a/drivers/usb/core/offload.c b/drivers/usb/core/offload.c
new file mode 100644
index 000000000000..7c699f1b8d2b
--- /dev/null
+++ b/drivers/usb/core/offload.c
@@ -0,0 +1,136 @@
+// SPDX-License-Identifier: GPL-2.0
+
+/*
+ * offload.c - USB offload related functions
+ *
+ * Copyright (c) 2025, Google LLC.
+ *
+ * Author: Guan-Yu Lin
+ */
+
+#include <linux/usb.h>
+
+#include "usb.h"
+
+/**
+ * usb_offload_get - increment the offload_usage of a USB device
+ * @udev: the USB device to increment its offload_usage
+ *
+ * Incrementing the offload_usage of a usb_device indicates that offload is
+ * enabled on this usb_device; that is, another entity is actively handling USB
+ * transfers. This information allows the USB driver to adjust its power
+ * management policy based on offload activity.
+ *
+ * Return: 0 on success. A negative error code otherwise.
+ */
+int usb_offload_get(struct usb_device *udev)
+{
+ int ret;
+
+ usb_lock_device(udev);
+ if (udev->state == USB_STATE_NOTATTACHED) {
+ usb_unlock_device(udev);
+ return -ENODEV;
+ }
+
+ if (udev->state == USB_STATE_SUSPENDED ||
+ udev->offload_at_suspend) {
+ usb_unlock_device(udev);
+ return -EBUSY;
+ }
+
+ /*
+ * offload_usage could only be modified when the device is active, since
+ * it will alter the suspend flow of the device.
+ */
+ ret = usb_autoresume_device(udev);
+ if (ret < 0) {
+ usb_unlock_device(udev);
+ return ret;
+ }
+
+ udev->offload_usage++;
+ usb_autosuspend_device(udev);
+ usb_unlock_device(udev);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(usb_offload_get);
+
+/**
+ * usb_offload_put - drop the offload_usage of a USB device
+ * @udev: the USB device to drop its offload_usage
+ *
+ * The inverse operation of usb_offload_get, which drops the offload_usage of
+ * a USB device. This information allows the USB driver to adjust its power
+ * management policy based on offload activity.
+ *
+ * Return: 0 on success. A negative error code otherwise.
+ */
+int usb_offload_put(struct usb_device *udev)
+{
+ int ret;
+
+ usb_lock_device(udev);
+ if (udev->state == USB_STATE_NOTATTACHED) {
+ usb_unlock_device(udev);
+ return -ENODEV;
+ }
+
+ if (udev->state == USB_STATE_SUSPENDED ||
+ udev->offload_at_suspend) {
+ usb_unlock_device(udev);
+ return -EBUSY;
+ }
+
+ /*
+ * offload_usage could only be modified when the device is active, since
+ * it will alter the suspend flow of the device.
+ */
+ ret = usb_autoresume_device(udev);
+ if (ret < 0) {
+ usb_unlock_device(udev);
+ return ret;
+ }
+
+ /* Drop the count when it wasn't 0, ignore the operation otherwise. */
+ if (udev->offload_usage)
+ udev->offload_usage--;
+ usb_autosuspend_device(udev);
+ usb_unlock_device(udev);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(usb_offload_put);
+
+/**
+ * usb_offload_check - check offload activities on a USB device
+ * @udev: the USB device to check its offload activity.
+ *
+ * Check if there are any offload activity on the USB device right now. This
+ * information could be used for power management or other forms of resource
+ * management.
+ *
+ * The caller must hold @udev's device lock. In addition, the caller should
+ * ensure downstream usb devices are all either suspended or marked as
+ * "offload_at_suspend" to ensure the correctness of the return value.
+ *
+ * Returns true on any offload activity, false otherwise.
+ */
+bool usb_offload_check(struct usb_device *udev) __must_hold(&udev->dev->mutex)
+{
+ struct usb_device *child;
+ bool active;
+ int port1;
+
+ usb_hub_for_each_child(udev, port1, child) {
+ usb_lock_device(child);
+ active = usb_offload_check(child);
+ usb_unlock_device(child);
+ if (active)
+ return true;
+ }
+
+ return !!udev->offload_usage;
+}
+EXPORT_SYMBOL_GPL(usb_offload_check);