Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 84e2e577 authored by Mathias Nyman's avatar Mathias Nyman Committed by Greg Kroah-Hartman
Browse files

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: default avatarRon Lee <ron.lee@intel.com>
Signed-off-by: default avatarMathias Nyman <mathias.nyman@linux.intel.com>
Link: https://lore.kernel.org/r/20230116142216.1141605-7-mathias.nyman@linux.intel.com


Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@linuxfoundation.org>
parent 8a6e963b
Loading
Loading
Loading
Loading
+65 −0
Original line number Diff line number Diff line
@@ -37,6 +37,71 @@ bool usb_acpi_power_manageable(struct usb_device *hdev, int index)
}
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
 * resource
+3 −0
Original line number Diff line number Diff line
@@ -751,11 +751,14 @@ extern void usb_queue_reset_device(struct usb_interface *dev);
extern int usb_acpi_set_power_state(struct usb_device *hdev, int index,
	bool enable);
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
static inline int usb_acpi_set_power_state(struct usb_device *hdev, int index,
	bool enable) { return 0; }
static inline bool usb_acpi_power_manageable(struct usb_device *hdev, int index)
	{ return true; }
static inline int usb_acpi_port_lpm_incapable(struct usb_device *hdev, int index)
	{ return 0; }
#endif

/* USB autosuspend and autoresume */