Introduce sb_usage_count and corresponding apis to track sideband usage on each usb_device. A sideband refers to the co-processor that accesses the usb_device via shared control on the same USB host controller. To optimize power usage, it's essential to monitor whether ther sideband is actively using the usb_device. This information is vital when determining if a usb_device can be safely suspended during system power state transitions. Signed-off-by: Guan-Yu Lin <guanyulin@xxxxxxxxxx> --- drivers/usb/core/driver.c | 54 +++++++++++++++++++++++++++++++++++++++ include/linux/usb.h | 13 ++++++++++ 2 files changed, 67 insertions(+) diff --git a/drivers/usb/core/driver.c b/drivers/usb/core/driver.c index 0c3f12daac79..c1ba5ed15214 100644 --- a/drivers/usb/core/driver.c +++ b/drivers/usb/core/driver.c @@ -1673,6 +1673,60 @@ void usb_disable_autosuspend(struct usb_device *udev) } EXPORT_SYMBOL_GPL(usb_disable_autosuspend); +/** + * usb_sideband_get - notify usb driver there's a new active sideband + * @udev: the usb_device which has an active sideband + * + * An active sideband indicates that another entity is currently using the usb + * device. Notify the usb device by increasing the sb_usage_count. This allows + * usb driver to adjust power management policy based on sideband activities. + */ +void usb_sideband_get(struct usb_device *udev) +{ + struct usb_device *parent = udev; + + do { + atomic_inc(&parent->sb_usage_count); + parent = parent->parent; + } while (parent); +} +EXPORT_SYMBOL_GPL(usb_sideband_get); + +/** + * usb_sideband_put - notify usb driver there's a sideband deactivated + * @udev: the usb_device which has a sideband deactivated + * + * The inverse operation of usb_sideband_get, which notifies the usb device by + * decreasing the sb_usage_count. This allows usb driver to adjust power + * management policy based on sideband activities. + */ +void usb_sideband_put(struct usb_device *udev) +{ + struct usb_device *parent = udev; + + do { + atomic_dec(&parent->sb_usage_count); + parent = parent->parent; + } while (parent); +} +EXPORT_SYMBOL_GPL(usb_sideband_put); + +/** + * usb_sideband_check - check sideband activities on the host controller + * @udev: the usb_device which has a sideband deactivated + * + * Check if there are any sideband activity on the USB device right now. This + * information could be used for power management or other forms or resource + * management. + * + * Returns true on any active sideband existence, false otherwise + */ +bool usb_sideband_check(struct usb_device *udev) +{ + return !!atomic_read(&udev->sb_usage_count); +} +EXPORT_SYMBOL_GPL(usb_sideband_check); + /** * usb_autosuspend_device - delayed autosuspend of a USB device and its interfaces * @udev: the usb_device to autosuspend diff --git a/include/linux/usb.h b/include/linux/usb.h index 672d8fc2abdb..5b9fea378960 100644 --- a/include/linux/usb.h +++ b/include/linux/usb.h @@ -645,6 +645,7 @@ struct usb3_lpm_parameters { * parent->hub_delay + wHubDelay + tTPTransmissionDelay (40ns) * Will be used as wValue for SetIsochDelay requests. * @use_generic_driver: ask driver core to reprobe using the generic driver. + * @sb_usage_count: number of active sideband accessing this usb device. * * Notes: * Usbcore drivers should not set usbdev->state directly. Instead use @@ -731,6 +732,8 @@ struct usb_device { u16 hub_delay; unsigned use_generic_driver:1; + + atomic_t sb_usage_count; }; #define to_usb_device(__dev) container_of_const(__dev, struct usb_device, dev) @@ -798,6 +801,9 @@ static inline int usb_acpi_port_lpm_incapable(struct usb_device *hdev, int index #ifdef CONFIG_PM extern void usb_enable_autosuspend(struct usb_device *udev); extern void usb_disable_autosuspend(struct usb_device *udev); +extern void usb_sideband_get(struct usb_device *udev); +extern void usb_sideband_put(struct usb_device *udev); +extern bool usb_sideband_check(struct usb_device *udev); extern int usb_autopm_get_interface(struct usb_interface *intf); extern void usb_autopm_put_interface(struct usb_interface *intf); @@ -818,6 +824,13 @@ static inline int usb_enable_autosuspend(struct usb_device *udev) static inline int usb_disable_autosuspend(struct usb_device *udev) { return 0; } +static inline int usb_sideband_get(struct usb_device *udev) +{ return 0; } +static inline int usb_sideband_put(struct usb_device *udev) +{ return 0; } +static inline bool usb_sideband_check(struct usb_device *udev) +{ return false; } + static inline int usb_autopm_get_interface(struct usb_interface *intf) { return 0; } static inline int usb_autopm_get_interface_async(struct usb_interface *intf) -- 2.47.0.rc0.187.ge670bccf7e-goog