diff mbox series

[5/6] platform/chrome: cros_ec_lpc: Correct ACPI name for Framework Laptop

Message ID 20240515055631.5775-6-ben@jubnut.com (mailing list archive)
State Superseded
Headers show
Series Fix MEC concurrency problems for Framework Laptop | expand

Commit Message

Ben Walsh May 15, 2024, 5:56 a.m. UTC
Framework Laptops' ACPI exposes the EC as name "PNP0C09". Use this to
find the device. This makes it easy to find the AML mutex via the
ACPI_COMPANION device.

The name "PNP0C09" is part of the ACPI standard, not Chrome-specific,
so only recognise the device if the DMI data is recognised too.

Signed-off-by: Ben Walsh <ben@jubnut.com>
---
 drivers/platform/chrome/cros_ec_lpc.c | 38 ++++++++++++++++-----------
 1 file changed, 22 insertions(+), 16 deletions(-)

Comments

Tzung-Bi Shih May 20, 2024, 9:47 a.m. UTC | #1
On Wed, May 15, 2024 at 06:56:30AM +0100, Ben Walsh wrote:
> Framework Laptops' ACPI exposes the EC as name "PNP0C09". Use this to
> find the device. This makes it easy to find the AML mutex via the
> ACPI_COMPANION device.
> 
> The name "PNP0C09" is part of the ACPI standard, not Chrome-specific,
> so only recognise the device if the DMI data is recognised too.

I don't quite understand the statement.  Why it needs DMI data?

> diff --git a/drivers/platform/chrome/cros_ec_lpc.c b/drivers/platform/chrome/cros_ec_lpc.c
[...]
> -/* True if ACPI device is present */
> -static bool cros_ec_lpc_acpi_device_found;
> +/*
> + * Index into cros_ec_lpc_acpi_device_ids of ACPI device,
> + * negative for ACPI device not found.
> + */
> +static int cros_ec_lpc_acpi_device_found;

Rename it to `cros_ec_lpc_acpi_device_index` for clarity?

>  static int __init cros_ec_lpc_init(void)
>  {
>  	int ret;
> -	acpi_status status;
>  	const struct dmi_system_id *dmi_match;
>  
> -	status = acpi_get_devices(ACPI_DRV_NAME, cros_ec_lpc_parse_device,
> -				  &cros_ec_lpc_acpi_device_found, NULL);
> -	if (ACPI_FAILURE(status))
> -		pr_warn(DRV_NAME ": Looking for %s failed\n", ACPI_DRV_NAME);
> +	cros_ec_lpc_acpi_device_found = cros_ec_lpc_find_acpi_dev(
> +		cros_ec_lpc_driver.driver.acpi_match_table);

Or just use `cros_ec_lpc_acpi_device_ids`.

> -	} else if (!cros_ec_lpc_acpi_device_found) {
> +	} else if (cros_ec_lpc_acpi_device_found <= 0) {
> +		/* Standard EC "PNP0C09" not supported without DMI data */

Asked the same question above, why PNP0C09 needs DMI data?  Also the way is
a bit confusing as "PNP0C09" must be at index 0 in the acpi_device_id.
Ben Walsh May 23, 2024, 6:42 p.m. UTC | #2
Tzung-Bi Shih <tzungbi@kernel.org> writes:

> On Wed, May 15, 2024 at 06:56:30AM +0100, Ben Walsh wrote:
>> Framework Laptops' ACPI exposes the EC as name "PNP0C09". Use this to
>> find the device. This makes it easy to find the AML mutex via the
>> ACPI_COMPANION device.
>>
>> The name "PNP0C09" is part of the ACPI standard, not Chrome-specific,
>> so only recognise the device if the DMI data is recognised too.
>
> I don't quite understand the statement.  Why it needs DMI data?

There are lots of computers with EC chips with ACPI name "PNP0C09"
because it's part of the ACPI standard (for example I have an Intel NUC
with one of these). Most of them don't support the cros_ec protocol, so
the cros_ec driver should ignore these chips. The Framework EC is
unusual in that it's called "PNP0C09" and supports the cros_ec protocol.

Before these patches, the cros_ec code just ignored PNP0C09 because it
wasn't in the match table. The cros_ec_lpc_init logic looked like:

  * dmi_match => ok
  * acpi_name == "GOOG0004" => ok
  * otherwise fail.

After the patch, cros_ec_lpc_init still has this behaviour. We have
"PNP0C09" in the match table so the driver gets hooked up correctly
with the right "ACPI_COMPANION" device, but we don't allow the match
to proceed unless we have the DMI data indicating it's a Framework EC.

>> -	} else if (!cros_ec_lpc_acpi_device_found) {
>> +	} else if (cros_ec_lpc_acpi_device_found <= 0) {
>> +		/* Standard EC "PNP0C09" not supported without DMI data */
>
> Also the way is a bit confusing as "PNP0C09" must be at index 0 in the
> acpi_device_id.

I need some way of saying "will we match PNP0C09?" The table index seems
a simple way of doing it. I could use a strcmp on the table match
instead?

Regarding your other emails, I agree with all your suggestions. Thanks
for reviewing!
Tzung-Bi Shih May 24, 2024, 2:26 a.m. UTC | #3
On Thu, May 23, 2024 at 07:42:00PM +0100, Ben Walsh wrote:
> Tzung-Bi Shih <tzungbi@kernel.org> writes:
> 
> > On Wed, May 15, 2024 at 06:56:30AM +0100, Ben Walsh wrote:
> >> Framework Laptops' ACPI exposes the EC as name "PNP0C09". Use this to
> >> find the device. This makes it easy to find the AML mutex via the
> >> ACPI_COMPANION device.
> >>
> >> The name "PNP0C09" is part of the ACPI standard, not Chrome-specific,
> >> so only recognise the device if the DMI data is recognised too.
> >
> > I don't quite understand the statement.  Why it needs DMI data?
> 
> There are lots of computers with EC chips with ACPI name "PNP0C09"
> because it's part of the ACPI standard (for example I have an Intel NUC
> with one of these). Most of them don't support the cros_ec protocol, so
> the cros_ec driver should ignore these chips. The Framework EC is
> unusual in that it's called "PNP0C09" and supports the cros_ec protocol.
> 
> Before these patches, the cros_ec code just ignored PNP0C09 because it
> wasn't in the match table. The cros_ec_lpc_init logic looked like:
> 
>   * dmi_match => ok
>   * acpi_name == "GOOG0004" => ok
>   * otherwise fail.
> 
> After the patch, cros_ec_lpc_init still has this behaviour. We have
> "PNP0C09" in the match table so the driver gets hooked up correctly
> with the right "ACPI_COMPANION" device, but we don't allow the match
> to proceed unless we have the DMI data indicating it's a Framework EC.

From the context you provided, instead of matching "PNP0C09" in the driver,
it makes more sense to me (for Framework EC):

* Mainly use DMI match.
* Add a quirk for looking up (acpi_get_devices()?) and binding
  (e.g. ACPI_COMPANION_SET()) the `adev` in cros_ec_lpc_probe().
Ben Walsh May 24, 2024, 6:35 p.m. UTC | #4
Tzung-Bi Shih <tzungbi@kernel.org> writes:

> From the context you provided, instead of matching "PNP0C09" in the driver,
> it makes more sense to me (for Framework EC):
>
> * Mainly use DMI match.
> * Add a quirk for looking up (acpi_get_devices()?) and binding
>   (e.g. ACPI_COMPANION_SET()) the `adev` in cros_ec_lpc_probe().

Sorry, I don't think I provided enough context. There is already a
platform device /sys/bus/platform/devices/PNP0C09:00 with a companion
acpi device /sys/bus/acpi/devices/PNP0C09:00. I think it makes sense to
bind the driver to the existing platform device.

I could add a new quirk which provides an alternative ACPI match table
to be used instead of the default. In the default case the match_table
will contain only "GOOG0004" as before. But in the Framework EC case the
match table will be "PNP0C09".
Dustin L. Howett May 24, 2024, 6:39 p.m. UTC | #5
On Fri, May 24, 2024 at 1:35 PM Ben Walsh <ben@jubnut.com> wrote:
>
> I could add a new quirk which provides an alternative ACPI match table
> to be used instead of the default. In the default case the match_table
> will contain only "GOOG0004" as before. But in the Framework EC case the
> match table will be "PNP0C09".

My biggest concern with putting PNP0C09 in the direct match table is
that it would cause cros_ec_lpcs to be loaded for *all* devices with
an ACPI-compatible embedded controller; it would likely print an error
and bail out early on, but it would still be unnecessary on 99% of
platforms.

This is my misunderstanding the driver model, but is it possible to
add a second companion device?
d
Ben Walsh May 24, 2024, 6:45 p.m. UTC | #6
Dustin Howett <dustin@howett.net> writes:

> On Fri, May 24, 2024 at 1:35 PM Ben Walsh <ben@jubnut.com> wrote:
>>
>> I could add a new quirk which provides an alternative ACPI match table
>> to be used instead of the default. In the default case the match_table
>> will contain only "GOOG0004" as before. But in the Framework EC case the
>> match table will be "PNP0C09".
>
> My biggest concern with putting PNP0C09 in the direct match table is
> that it would cause cros_ec_lpcs to be loaded for *all* devices with
> an ACPI-compatible embedded controller; it would likely print an error
> and bail out early on, but it would still be unnecessary on 99% of
> platforms.

That's exactly what we're talking about: how to *avoid* putting PNP0C09
in the match table.

My original idea was to put it in the match table, but only allow a
match to proceed if Framework EC is detected by DMI.

My new idea is to not to put it in the match table *at all*, but to
provide a new match table if Framework EC is detected by DMI.
Tzung-Bi Shih May 26, 2024, 1:26 a.m. UTC | #7
On Fri, May 24, 2024 at 07:35:22PM +0100, Ben Walsh wrote:
> Tzung-Bi Shih <tzungbi@kernel.org> writes:
> 
> > From the context you provided, instead of matching "PNP0C09" in the driver,
> > it makes more sense to me (for Framework EC):
> >
> > * Mainly use DMI match.
> > * Add a quirk for looking up (acpi_get_devices()?) and binding
> >   (e.g. ACPI_COMPANION_SET()) the `adev` in cros_ec_lpc_probe().
> 
> Sorry, I don't think I provided enough context. There is already a
> platform device /sys/bus/platform/devices/PNP0C09:00 with a companion
> acpi device /sys/bus/acpi/devices/PNP0C09:00. I think it makes sense to
> bind the driver to the existing platform device.
> 
> I could add a new quirk which provides an alternative ACPI match table
> to be used instead of the default. In the default case the match_table
> will contain only "GOOG0004" as before. But in the Framework EC case the
> match table will be "PNP0C09".

I think it doesn't work as the current quirk is handling in
cros_ec_lpc_probe() which is after matching.

My original idea: would it be possible to get the `adev` in cros_ec_lpc_probe()
via any lookup API?  If yes, it could still use DMI match and get `adev` if
required.
Ben Walsh May 27, 2024, 6:06 p.m. UTC | #8
Tzung-Bi Shih <tzungbi@kernel.org> writes:

> On Fri, May 24, 2024 at 07:35:22PM +0100, Ben Walsh wrote:

>> I could add a new quirk which provides an alternative ACPI match table
>> to be used instead of the default. In the default case the match_table
>> will contain only "GOOG0004" as before. But in the Framework EC case the
>> match table will be "PNP0C09".
>
> I think it doesn't work as the current quirk is handling in
> cros_ec_lpc_probe() which is after matching.

I was thinking of a new quirk called CROS_EC_LPC_QUIRK_ACPI_MATCH, and
putting it in cros_ec_lpc_init(), not cros_ec_lpc_probe(). Do we have to
do all quirk handling in cros_ec_lpc_probe()?

> My original idea: would it be possible to get the `adev` in cros_ec_lpc_probe()
> via any lookup API?  If yes, it could still use DMI match and get `adev` if
> required.

That works; I've tested it.

In this scenario we're not using the existing PNP0C09 platform device,
which means I can't look at
/sys/bus/acpi/devices/PNP0C09\:00/physical_node/driver and see the
driver. Is this OK?

(Note that ACPI_COMPANION_SET() doesn't fix this. You can use
acpi_bind_one() but that seems more like internal plumbing).
Tzung-Bi Shih May 28, 2024, 3:08 a.m. UTC | #9
On Mon, May 27, 2024 at 07:06:40PM +0100, Ben Walsh wrote:
> Tzung-Bi Shih <tzungbi@kernel.org> writes:
> 
> > On Fri, May 24, 2024 at 07:35:22PM +0100, Ben Walsh wrote:
> 
> >> I could add a new quirk which provides an alternative ACPI match table
> >> to be used instead of the default. In the default case the match_table
> >> will contain only "GOOG0004" as before. But in the Framework EC case the
> >> match table will be "PNP0C09".
> >
> > I think it doesn't work as the current quirk is handling in
> > cros_ec_lpc_probe() which is after matching.
> 
> I was thinking of a new quirk called CROS_EC_LPC_QUIRK_ACPI_MATCH, and
> putting it in cros_ec_lpc_init(), not cros_ec_lpc_probe(). Do we have to
> do all quirk handling in cros_ec_lpc_probe()?

No, but there is already code in cros_ec_lpc_probe() for handling quirks and
we would like to reuse them if we could.

Also a possible issue: MODULE_DEVICE_TABLE() wouldn't work if the table changes
during runtime.

> 
> > My original idea: would it be possible to get the `adev` in cros_ec_lpc_probe()
> > via any lookup API?  If yes, it could still use DMI match and get `adev` if
> > required.
> 
> That works; I've tested it.
> 
> In this scenario we're not using the existing PNP0C09 platform device,
> which means I can't look at
> /sys/bus/acpi/devices/PNP0C09\:00/physical_node/driver and see the
> driver. Is this OK?
> 
> (Note that ACPI_COMPANION_SET() doesn't fix this. You can use
> acpi_bind_one() but that seems more like internal plumbing).

As long as ACPI_COMPANION() in the driver can get the correct `adev`, I guess
it's fine.  Otherwise, put the `adev` to somewhere device specific (e.g.
struct cros_ec_lpc) should also work.
diff mbox series

Patch

diff --git a/drivers/platform/chrome/cros_ec_lpc.c b/drivers/platform/chrome/cros_ec_lpc.c
index ab7779b57a13..a9200f0a1a37 100644
--- a/drivers/platform/chrome/cros_ec_lpc.c
+++ b/drivers/platform/chrome/cros_ec_lpc.c
@@ -29,10 +29,12 @@ 
 #include "cros_ec_lpc_mec.h"
 
 #define DRV_NAME "cros_ec_lpcs"
-#define ACPI_DRV_NAME "GOOG0004"
 
-/* True if ACPI device is present */
-static bool cros_ec_lpc_acpi_device_found;
+/*
+ * Index into cros_ec_lpc_acpi_device_ids of ACPI device,
+ * negative for ACPI device not found.
+ */
+static int cros_ec_lpc_acpi_device_found;
 
 /*
  * Indicates that lpc_driver_data.quirk_mmio_memory_base should
@@ -598,7 +600,8 @@  static void cros_ec_lpc_remove(struct platform_device *pdev)
 }
 
 static const struct acpi_device_id cros_ec_lpc_acpi_device_ids[] = {
-	{ ACPI_DRV_NAME, 0 },
+	{ "PNP0C09", 0 },    /* Standard ACPI EC name. Must be first in list */
+	{ "GOOG0004", 0 },
 	{ }
 };
 MODULE_DEVICE_TABLE(acpi, cros_ec_lpc_acpi_device_ids);
@@ -737,30 +740,33 @@  static struct platform_device cros_ec_lpc_device = {
 	.name = DRV_NAME
 };
 
-static acpi_status cros_ec_lpc_parse_device(acpi_handle handle, u32 level,
-					    void *context, void **retval)
+static int cros_ec_lpc_find_acpi_dev(const struct acpi_device_id *acpi_ids)
 {
-	*(bool *)context = true;
-	return AE_CTRL_TERMINATE;
+	int i;
+
+	for (i = 0; acpi_ids[i].id[0]; ++i) {
+		if (acpi_dev_present(acpi_ids[i].id, NULL, -1))
+			return i;
+	}
+
+	return -1;
 }
 
 static int __init cros_ec_lpc_init(void)
 {
 	int ret;
-	acpi_status status;
 	const struct dmi_system_id *dmi_match;
 
-	status = acpi_get_devices(ACPI_DRV_NAME, cros_ec_lpc_parse_device,
-				  &cros_ec_lpc_acpi_device_found, NULL);
-	if (ACPI_FAILURE(status))
-		pr_warn(DRV_NAME ": Looking for %s failed\n", ACPI_DRV_NAME);
+	cros_ec_lpc_acpi_device_found = cros_ec_lpc_find_acpi_dev(
+		cros_ec_lpc_driver.driver.acpi_match_table);
 
 	dmi_match = dmi_first_match(cros_ec_lpc_dmi_table);
 
 	if (dmi_match) {
 		/* Pass the DMI match's driver data down to the platform device */
 		cros_ec_lpc_driver_data = dmi_match->driver_data;
-	} else if (!cros_ec_lpc_acpi_device_found) {
+	} else if (cros_ec_lpc_acpi_device_found <= 0) {
+		/* Standard EC "PNP0C09" not supported without DMI data */
 		pr_err(DRV_NAME ": unsupported system.\n");
 		return -ENODEV;
 	}
@@ -772,7 +778,7 @@  static int __init cros_ec_lpc_init(void)
 		return ret;
 	}
 
-	if (!cros_ec_lpc_acpi_device_found) {
+	if (cros_ec_lpc_acpi_device_found < 0) {
 		/* Register the device, and it'll get hooked up automatically */
 		ret = platform_device_register(&cros_ec_lpc_device);
 		if (ret) {
@@ -786,7 +792,7 @@  static int __init cros_ec_lpc_init(void)
 
 static void __exit cros_ec_lpc_exit(void)
 {
-	if (!cros_ec_lpc_acpi_device_found)
+	if (cros_ec_lpc_acpi_device_found < 0)
 		platform_device_unregister(&cros_ec_lpc_device);
 	platform_driver_unregister(&cros_ec_lpc_driver);
 }