Merge tag 'for-linus' of git://git.kernel.org/pub/scm/virt/kvm/kvm
[linux-2.6-microblaze.git] / drivers / acpi / bus.c
index c4b0328..be7da23 100644 (file)
@@ -277,10 +277,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,
@@ -313,21 +319,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);
 }
 
 /* --------------------------------------------------------------------------
@@ -1161,7 +1255,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,