diff mbox series

[4/6] ACPI: Execute platform _OSC also with query bit clear

Message ID 20210126155723.9388-5-mika.westerberg@linux.intel.com
State New
Headers show
Series thunderbolt / ACPI: Add support for USB4 _OSC | expand

Commit Message

Mika Westerberg Jan. 26, 2021, 3:57 p.m. UTC
From: Mario Limonciello <mario.limonciello@dell.com>

The platform _OSC can change the hardware state when query bit is not
set. According to ACPI spec it is recommended that the OS runs _OSC with
query bit set until the platform does not mask any of the capabilities.
Then it should run it with query bit clear in order to actually commit
the changes. At the moment Linux only runs the _OSC with query bit set
and this is going to cause problems with the USB4 CM (Connection
Manager) switch that is going to commit the switch only when the OS
requests control over the feature.

For this reason modify the _OSC support so that we first execute it with
query bit set, then use the returned valu as base of the features we
want to control and run the _OSC again with query bit clear.

Also rename the function to better match what it does.

Cc: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
Signed-off-by: Mario Limonciello <mario.limonciello@dell.com>
Signed-off-by: Mika Westerberg <mika.westerberg@linux.intel.com>
---
 drivers/acpi/bus.c | 43 +++++++++++++++++++++++++++++++------------
 1 file changed, 31 insertions(+), 12 deletions(-)

Comments

Yehezkel Bernat Jan. 26, 2021, 4:25 p.m. UTC | #1
On Tue, Jan 26, 2021 at 5:57 PM Mika Westerberg
<mika.westerberg@linux.intel.com> wrote:
>
> From: Mario Limonciello <mario.limonciello@dell.com>
>
> The platform _OSC can change the hardware state when query bit is not
> set. According to ACPI spec it is recommended that the OS runs _OSC with
> query bit set until the platform does not mask any of the capabilities.
> Then it should run it with query bit clear in order to actually commit
> the changes. At the moment Linux only runs the _OSC with query bit set
> and this is going to cause problems with the USB4 CM (Connection
> Manager) switch that is going to commit the switch only when the OS
> requests control over the feature.
>
> For this reason modify the _OSC support so that we first execute it with
> query bit set, then use the returned valu as base of the features we

Totally out of my depth here, but just noticed the typo (valu => value).

> want to control and run the _OSC again with query bit clear.
>
> Also rename the function to better match what it does.
>
> Cc: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
> Signed-off-by: Mario Limonciello <mario.limonciello@dell.com>
> Signed-off-by: Mika Westerberg <mika.westerberg@linux.intel.com>
> ---
Rafael J. Wysocki Jan. 26, 2021, 5:42 p.m. UTC | #2
On Tue, Jan 26, 2021 at 6:37 PM Limonciello, Mario
<Mario.Limonciello@dell.com> wrote:
>
> > On Tue, Jan 26, 2021 at 5:01 PM Mika Westerberg
> > <mika.westerberg@linux.intel.com> wrote:
> > >
> > > From: Mario Limonciello <mario.limonciello@dell.com>
> > >
> > > The platform _OSC can change the hardware state when query bit is not
> > > set. According to ACPI spec it is recommended that the OS runs _OSC with
> > > query bit set until the platform does not mask any of the capabilities.
> > > Then it should run it with query bit clear in order to actually commit
> > > the changes. At the moment Linux only runs the _OSC with query bit set
> >
> > And that's because there was nothing it could ask to control using the
> > _SB scope _OSC.
> >
> > Today it is just reporting what features are supported by it.
> >
> > However, with the upcoming USB4 CM support it needs to ask for the
> > control of that feature and that's why the _SB scope _OSC support
> > needs to be extended.  So it is not a fix for a bug or missing spec
> > coverage, which this part of the changelog kind of implies, it's just
> > enabling a new feature.
>
> Other operating systems behave as described in the ACPI spec long before USB4 CM
> support was added.  Admittedly this is semantics of whether to call it
> a "bug", but specifically the lack of this in the existing Linux kernel code
> *can* actually cause you to get into a situation where you have no functional
> USB4.  This will happen if you boot between two different kernels or potentially
> two different operating systems.  This is due to how the selection of FW or SW
> CM is made.  If this patch "alone" was brought further backward the older kernels
> FW CM mode would be activated in those situations.

I would put that information into the changelog.

Moreover, have you looked at acpi_pci_osc_control_set()?

What it does is analogous to what you are proposing, but a bit
different, and I would like to preserve consistency between _OSC use
cases.

So would it be possible to adjust the _SB _OSC evaluation flow to
follow the PCI _OSC one?  That is, if any control bits are there, pass
them along with the last evaluation of _OSC with the query flag clear.
Or is the latter defective and if so then why?

>
> >
> > > and this is going to cause problems with the USB4 CM (Connection
> > > Manager) switch that is going to commit the switch only when the OS
> > > requests control over the feature.
> > >
> > > For this reason modify the _OSC support so that we first execute it with
> > > query bit set, then use the returned valu as base of the features we
> >
> > s/valu/value/
> >
> > > want to control and run the _OSC again with query bit clear.
> > >
> > > Also rename the function to better match what it does.
> > >
> > > Cc: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
> > > Signed-off-by: Mario Limonciello <mario.limonciello@dell.com>
> > > Signed-off-by: Mika Westerberg <mika.westerberg@linux.intel.com>
> > >
> > > ---
> > >  drivers/acpi/bus.c | 43 +++++++++++++++++++++++++++++++------------
> > >  1 file changed, 31 insertions(+), 12 deletions(-)
> > >
> > > diff --git a/drivers/acpi/bus.c b/drivers/acpi/bus.c
> > > index 1682f8b454a2..ca7c7b2bf56e 100644
> > > --- a/drivers/acpi/bus.c
> > > +++ b/drivers/acpi/bus.c
> > > @@ -282,9 +282,9 @@ bool osc_pc_lpi_support_confirmed;
> > >  EXPORT_SYMBOL_GPL(osc_pc_lpi_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,
> > > @@ -321,17 +321,36 @@ static void acpi_bus_osc_support(void)
> > >                 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;
> > >         }
> > > -       /* do we need to check other returned cap? Sounds no */
> > > +
> > > +       /*
> > > +        * Now run _OSC again with query flag clean and with the caps
> >
> > s/clean/clear/
> >
> > > +        * both platform and OS supports.
> >
> > s/both platform and OS supports/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;
> > > +       }
> > > +
> > > +       kfree(context.ret.pointer);
> > >  }
> > >
> > >  /* ------------------------------------------------------------------------
> > --
> > > @@ -1168,7 +1187,7 @@ 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();
> > >
> > >         /*
> > >          * _PDC control method may load dynamic SSDT tables,
> > > --
> > > 2.29.2
> > >
Mika Westerberg Jan. 27, 2021, 12:49 p.m. UTC | #3
On Tue, Jan 26, 2021 at 10:43:32PM +0000, Limonciello, Mario wrote:
> > I would put that information into the changelog.

> 

> Thanks, @Mika Westerberg can you collapse that in when you re-spin the

> series?


Sure.

> > 

> > Moreover, have you looked at acpi_pci_osc_control_set()?

> > 

> > What it does is analogous to what you are proposing, but a bit

> > different, and I would like to preserve consistency between _OSC use

> > cases.

> > 

> > So would it be possible to adjust the _SB _OSC evaluation flow to

> > follow the PCI _OSC one?  That is, if any control bits are there, pass

> > them along with the last evaluation of _OSC with the query flag clear.

> > Or is the latter defective and if so then why?

> 

> Basically the only difference is another line cloning OSC_CONTROL_DWORD from

> capbuf_ret to capbuf?

> 

> Yes, this actually sounds like it better adheres to the spec to me.

> 

> Quoting spec:

> " If the OS is granted control of a feature in the Control Field in one call to

> _OSC, then it must preserve the set state of that bit (requesting that feature)

> in all subsequent calls."


However, the platform wide _OSC does not actually have this
OSC_CONTROL_DWORD at all ;-)

I think what we do in this patch is already equivalent to what the PCI
_OSC is doing:

  1. Query bit set _OSC
  2. Take the returned OSC_SUPPORT_DWORD buffer and
  3. Pass it to the _OSC with query bit clear.

I may be missing something, though.
Rafael J. Wysocki Jan. 27, 2021, 1:50 p.m. UTC | #4
On Wed, Jan 27, 2021 at 1:49 PM Mika Westerberg
<mika.westerberg@linux.intel.com> wrote:
>

> On Tue, Jan 26, 2021 at 10:43:32PM +0000, Limonciello, Mario wrote:

> > > I would put that information into the changelog.

> >

> > Thanks, @Mika Westerberg can you collapse that in when you re-spin the

> > series?

>

> Sure.

>

> > >

> > > Moreover, have you looked at acpi_pci_osc_control_set()?

> > >

> > > What it does is analogous to what you are proposing, but a bit

> > > different, and I would like to preserve consistency between _OSC use

> > > cases.

> > >

> > > So would it be possible to adjust the _SB _OSC evaluation flow to

> > > follow the PCI _OSC one?  That is, if any control bits are there, pass

> > > them along with the last evaluation of _OSC with the query flag clear.

> > > Or is the latter defective and if so then why?

> >

> > Basically the only difference is another line cloning OSC_CONTROL_DWORD from

> > capbuf_ret to capbuf?

> >

> > Yes, this actually sounds like it better adheres to the spec to me.

> >

> > Quoting spec:

> > " If the OS is granted control of a feature in the Control Field in one call to

> > _OSC, then it must preserve the set state of that bit (requesting that feature)

> > in all subsequent calls."

>

> However, the platform wide _OSC does not actually have this

> OSC_CONTROL_DWORD at all ;-)


Right.

> I think what we do in this patch is already equivalent to what the PCI

> _OSC is doing:

>

>   1. Query bit set _OSC

>   2. Take the returned OSC_SUPPORT_DWORD buffer and

>   3. Pass it to the _OSC with query bit clear.


Yes, it is.

Given the way the USB4 _OSC protocol is defined (which admittedly
confused me somewhat), the code changes in this patch are fine by me.

Thanks and sorry for the confusion.
diff mbox series

Patch

diff --git a/drivers/acpi/bus.c b/drivers/acpi/bus.c
index 1682f8b454a2..ca7c7b2bf56e 100644
--- a/drivers/acpi/bus.c
+++ b/drivers/acpi/bus.c
@@ -282,9 +282,9 @@  bool osc_pc_lpi_support_confirmed;
 EXPORT_SYMBOL_GPL(osc_pc_lpi_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,
@@ -321,17 +321,36 @@  static void acpi_bus_osc_support(void)
 		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;
 	}
-	/* do we need to check other returned cap? Sounds no */
+
+	/*
+	 * Now run _OSC again with query flag clean and with the caps
+	 * both platform and OS supports.
+	 */
+	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;
+	}
+
+	kfree(context.ret.pointer);
 }
 
 /* --------------------------------------------------------------------------
@@ -1168,7 +1187,7 @@  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();
 
 	/*
 	 * _PDC control method may load dynamic SSDT tables,