diff options
Diffstat (limited to 'drivers/acpi')
-rw-r--r-- | drivers/acpi/bus.c | 119 |
1 files changed, 107 insertions, 12 deletions
diff --git a/drivers/acpi/bus.c b/drivers/acpi/bus.c index 1682f8b454a2..9c3fe08e8f18 100644 --- a/drivers/acpi/bus.c +++ b/drivers/acpi/bus.c @@ -281,10 +281,16 @@ bool osc_sb_apei_support_acked; bool osc_pc_lpi_support_confirmed; EXPORT_SYMBOL_GPL(osc_pc_lpi_support_confirmed); +/* + * ACPI 6.4 Operating System Capabilities for USB. + */ +bool osc_sb_native_usb4_support_confirmed; +EXPORT_SYMBOL_GPL(osc_sb_native_usb4_support_confirmed); + static u8 sb_uuid_str[] = "0811B06E-4A27-44F9-8D60-3CBBC22E7B48"; -static void acpi_bus_osc_support(void) +static void acpi_bus_osc_negotiate_platform_control(void) { - u32 capbuf[2]; + u32 capbuf[2], *capbuf_ret; struct acpi_osc_context context = { .uuid_str = sb_uuid_str, .rev = 1, @@ -317,21 +323,109 @@ static void acpi_bus_osc_support(void) if (IS_ENABLED(CONFIG_SCHED_MC_PRIO)) capbuf[OSC_SUPPORT_DWORD] |= OSC_SB_CPC_DIVERSE_HIGH_SUPPORT; + if (IS_ENABLED(CONFIG_USB4)) + capbuf[OSC_SUPPORT_DWORD] |= OSC_SB_NATIVE_USB4_SUPPORT; + if (!ghes_disable) capbuf[OSC_SUPPORT_DWORD] |= OSC_SB_APEI_SUPPORT; if (ACPI_FAILURE(acpi_get_handle(NULL, "\\_SB", &handle))) return; - if (ACPI_SUCCESS(acpi_run_osc(handle, &context))) { - u32 *capbuf_ret = context.ret.pointer; - if (context.ret.length > OSC_SUPPORT_DWORD) { - osc_sb_apei_support_acked = - capbuf_ret[OSC_SUPPORT_DWORD] & OSC_SB_APEI_SUPPORT; - osc_pc_lpi_support_confirmed = - capbuf_ret[OSC_SUPPORT_DWORD] & OSC_SB_PCLPI_SUPPORT; - } + + if (ACPI_FAILURE(acpi_run_osc(handle, &context))) + return; + + capbuf_ret = context.ret.pointer; + if (context.ret.length <= OSC_SUPPORT_DWORD) { kfree(context.ret.pointer); + return; + } + + /* + * Now run _OSC again with query flag clear and with the caps + * supported by both the OS and the platform. + */ + capbuf[OSC_QUERY_DWORD] = 0; + capbuf[OSC_SUPPORT_DWORD] = capbuf_ret[OSC_SUPPORT_DWORD]; + kfree(context.ret.pointer); + + if (ACPI_FAILURE(acpi_run_osc(handle, &context))) + return; + + capbuf_ret = context.ret.pointer; + if (context.ret.length > OSC_SUPPORT_DWORD) { + osc_sb_apei_support_acked = + capbuf_ret[OSC_SUPPORT_DWORD] & OSC_SB_APEI_SUPPORT; + osc_pc_lpi_support_confirmed = + capbuf_ret[OSC_SUPPORT_DWORD] & OSC_SB_PCLPI_SUPPORT; + osc_sb_native_usb4_support_confirmed = + capbuf_ret[OSC_SUPPORT_DWORD] & OSC_SB_NATIVE_USB4_SUPPORT; } - /* do we need to check other returned cap? Sounds no */ + + kfree(context.ret.pointer); +} + +/* + * Native control of USB4 capabilities. If any of the tunneling bits is + * set it means OS is in control and we use software based connection + * manager. + */ +u32 osc_sb_native_usb4_control; +EXPORT_SYMBOL_GPL(osc_sb_native_usb4_control); + +static void acpi_bus_decode_usb_osc(const char *msg, u32 bits) +{ + printk(KERN_INFO PREFIX "%s USB3%c DisplayPort%c PCIe%c XDomain%c\n", msg, + (bits & OSC_USB_USB3_TUNNELING) ? '+' : '-', + (bits & OSC_USB_DP_TUNNELING) ? '+' : '-', + (bits & OSC_USB_PCIE_TUNNELING) ? '+' : '-', + (bits & OSC_USB_XDOMAIN) ? '+' : '-'); +} + +static u8 sb_usb_uuid_str[] = "23A0D13A-26AB-486C-9C5F-0FFA525A575A"; +static void acpi_bus_osc_negotiate_usb_control(void) +{ + u32 capbuf[3]; + struct acpi_osc_context context = { + .uuid_str = sb_usb_uuid_str, + .rev = 1, + .cap.length = sizeof(capbuf), + .cap.pointer = capbuf, + }; + acpi_handle handle; + acpi_status status; + u32 control; + + if (!osc_sb_native_usb4_support_confirmed) + return; + + if (ACPI_FAILURE(acpi_get_handle(NULL, "\\_SB", &handle))) + return; + + control = OSC_USB_USB3_TUNNELING | OSC_USB_DP_TUNNELING | + OSC_USB_PCIE_TUNNELING | OSC_USB_XDOMAIN; + + capbuf[OSC_QUERY_DWORD] = 0; + capbuf[OSC_SUPPORT_DWORD] = 0; + capbuf[OSC_CONTROL_DWORD] = control; + + status = acpi_run_osc(handle, &context); + if (ACPI_FAILURE(status)) + return; + + if (context.ret.length != sizeof(capbuf)) { + printk(KERN_INFO PREFIX "USB4 _OSC: returned invalid length buffer\n"); + goto out_free; + } + + osc_sb_native_usb4_control = + control & ((u32 *)context.ret.pointer)[OSC_CONTROL_DWORD]; + + acpi_bus_decode_usb_osc("USB4 _OSC: OS supports", control); + acpi_bus_decode_usb_osc("USB4 _OSC: OS controls", + osc_sb_native_usb4_control); + +out_free: + kfree(context.ret.pointer); } /* -------------------------------------------------------------------------- @@ -1168,7 +1262,8 @@ static int __init acpi_bus_init(void) * _OSC method may exist in module level code, * so it must be run after ACPI_FULL_INITIALIZATION */ - acpi_bus_osc_support(); + acpi_bus_osc_negotiate_platform_control(); + acpi_bus_osc_negotiate_usb_control(); /* * _PDC control method may load dynamic SSDT tables, |