usb: acpi: add helper to check port lpm capability using acpi _DSM
commit cd702d18c882d5a4ea44bbdb38edd5d5577ef640 upstream. Add a helper to evaluate ACPI usb device specific method (_DSM) provided in case the USB3 port shouldn't enter U1 and U2 link states. This _DSM was added as port specific retimer configuration may lead to exit latencies growing beyond U1/U2 exit limits, and OS needs a way to find which ports can't support U1/U2 link power management states. This _DSM is also used by windows: Link: https://docs.microsoft.com/en-us/windows-hardware/drivers/bringup/usb-device-specific-method---dsm- Some patch issues found in testing resolved by Ron Lee Cc: stable@vger.kernel.org Tested-by: Ron Lee <ron.lee@intel.com> Signed-off-by: Mathias Nyman <mathias.nyman@linux.intel.com> Link: https://lore.kernel.org/r/20230116142216.1141605-7-mathias.nyman@linux.intel.com Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
This commit is contained in:
parent
8c36de0935
commit
b7904d2015
@ -37,6 +37,71 @@ bool usb_acpi_power_manageable(struct usb_device *hdev, int index)
|
|||||||
}
|
}
|
||||||
EXPORT_SYMBOL_GPL(usb_acpi_power_manageable);
|
EXPORT_SYMBOL_GPL(usb_acpi_power_manageable);
|
||||||
|
|
||||||
|
#define UUID_USB_CONTROLLER_DSM "ce2ee385-00e6-48cb-9f05-2edb927c4899"
|
||||||
|
#define USB_DSM_DISABLE_U1_U2_FOR_PORT 5
|
||||||
|
|
||||||
|
/**
|
||||||
|
* usb_acpi_port_lpm_incapable - check if lpm should be disabled for a port.
|
||||||
|
* @hdev: USB device belonging to the usb hub
|
||||||
|
* @index: zero based port index
|
||||||
|
*
|
||||||
|
* Some USB3 ports may not support USB3 link power management U1/U2 states
|
||||||
|
* due to different retimer setup. ACPI provides _DSM method which returns 0x01
|
||||||
|
* if U1 and U2 states should be disabled. Evaluate _DSM with:
|
||||||
|
* Arg0: UUID = ce2ee385-00e6-48cb-9f05-2edb927c4899
|
||||||
|
* Arg1: Revision ID = 0
|
||||||
|
* Arg2: Function Index = 5
|
||||||
|
* Arg3: (empty)
|
||||||
|
*
|
||||||
|
* Return 1 if USB3 port is LPM incapable, negative on error, otherwise 0
|
||||||
|
*/
|
||||||
|
|
||||||
|
int usb_acpi_port_lpm_incapable(struct usb_device *hdev, int index)
|
||||||
|
{
|
||||||
|
union acpi_object *obj;
|
||||||
|
acpi_handle port_handle;
|
||||||
|
int port1 = index + 1;
|
||||||
|
guid_t guid;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
ret = guid_parse(UUID_USB_CONTROLLER_DSM, &guid);
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
port_handle = usb_get_hub_port_acpi_handle(hdev, port1);
|
||||||
|
if (!port_handle) {
|
||||||
|
dev_dbg(&hdev->dev, "port-%d no acpi handle\n", port1);
|
||||||
|
return -ENODEV;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!acpi_check_dsm(port_handle, &guid, 0,
|
||||||
|
BIT(USB_DSM_DISABLE_U1_U2_FOR_PORT))) {
|
||||||
|
dev_dbg(&hdev->dev, "port-%d no _DSM function %d\n",
|
||||||
|
port1, USB_DSM_DISABLE_U1_U2_FOR_PORT);
|
||||||
|
return -ENODEV;
|
||||||
|
}
|
||||||
|
|
||||||
|
obj = acpi_evaluate_dsm(port_handle, &guid, 0,
|
||||||
|
USB_DSM_DISABLE_U1_U2_FOR_PORT, NULL);
|
||||||
|
|
||||||
|
if (!obj)
|
||||||
|
return -ENODEV;
|
||||||
|
|
||||||
|
if (obj->type != ACPI_TYPE_INTEGER) {
|
||||||
|
dev_dbg(&hdev->dev, "evaluate port-%d _DSM failed\n", port1);
|
||||||
|
ACPI_FREE(obj);
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (obj->integer.value == 0x01)
|
||||||
|
ret = 1;
|
||||||
|
|
||||||
|
ACPI_FREE(obj);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
EXPORT_SYMBOL_GPL(usb_acpi_port_lpm_incapable);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* usb_acpi_set_power_state - control usb port's power via acpi power
|
* usb_acpi_set_power_state - control usb port's power via acpi power
|
||||||
* resource
|
* resource
|
||||||
|
@ -751,11 +751,14 @@ extern struct device *usb_intf_get_dma_device(struct usb_interface *intf);
|
|||||||
extern int usb_acpi_set_power_state(struct usb_device *hdev, int index,
|
extern int usb_acpi_set_power_state(struct usb_device *hdev, int index,
|
||||||
bool enable);
|
bool enable);
|
||||||
extern bool usb_acpi_power_manageable(struct usb_device *hdev, int index);
|
extern bool usb_acpi_power_manageable(struct usb_device *hdev, int index);
|
||||||
|
extern int usb_acpi_port_lpm_incapable(struct usb_device *hdev, int index);
|
||||||
#else
|
#else
|
||||||
static inline int usb_acpi_set_power_state(struct usb_device *hdev, int index,
|
static inline int usb_acpi_set_power_state(struct usb_device *hdev, int index,
|
||||||
bool enable) { return 0; }
|
bool enable) { return 0; }
|
||||||
static inline bool usb_acpi_power_manageable(struct usb_device *hdev, int index)
|
static inline bool usb_acpi_power_manageable(struct usb_device *hdev, int index)
|
||||||
{ return true; }
|
{ return true; }
|
||||||
|
static inline int usb_acpi_port_lpm_incapable(struct usb_device *hdev, int index)
|
||||||
|
{ return 0; }
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/* USB autosuspend and autoresume */
|
/* USB autosuspend and autoresume */
|
||||||
|
Loading…
x
Reference in New Issue
Block a user