diff mbox series

[v2] mmc: sdhci-pci: Add DMI quirk for missing CD GPIO on Vexia Edu Atla 10 tablet

Message ID 20241107100048.11661-1-hdegoede@redhat.com
State New
Headers show
Series [v2] mmc: sdhci-pci: Add DMI quirk for missing CD GPIO on Vexia Edu Atla 10 tablet | expand

Commit Message

Hans de Goede Nov. 7, 2024, 10 a.m. UTC
The Vexia Edu Atla 10 tablet distributed to schools in the Spanish
Andalucía region has no ACPI fwnode associated with the SDHCI controller
for its microsd-slot and thus has no ACPI GPIO resource info.

This causes the following error to be logged and the slot to not work:
[   10.572113] sdhci-pci 0000:00:12.0: failed to setup card detect gpio

Add a DMI quirk table for providing gpiod_lookup_tables with manually
provided CD GPIO info and use this DMI table to provide the CD GPIO info
on this tablet. This fixes the microsd-slot not working.

Signed-off-by: Hans de Goede <hdegoede@redhat.com>
---
Changes in v2:
- Make sdhci_pci_dmi_cd_gpio_overrides static const instead of just const
- Drop duplicate #include <linux/dmi.h> (already there at the end)
---
 drivers/mmc/host/sdhci-pci-core.c | 38 +++++++++++++++++++++++++++++++
 1 file changed, 38 insertions(+)

Comments

Hans de Goede Nov. 11, 2024, 10:26 a.m. UTC | #1
Hi Adrian,

Thank you for the review.

On 11-Nov-24 11:07 AM, Adrian Hunter wrote:
> On 7/11/24 12:00, Hans de Goede wrote:
>> The Vexia Edu Atla 10 tablet distributed to schools in the Spanish
>> Andalucía region has no ACPI fwnode associated with the SDHCI controller
>> for its microsd-slot and thus has no ACPI GPIO resource info.
>>
>> This causes the following error to be logged and the slot to not work:
>> [   10.572113] sdhci-pci 0000:00:12.0: failed to setup card detect gpio
>>
>> Add a DMI quirk table for providing gpiod_lookup_tables with manually
>> provided CD GPIO info and use this DMI table to provide the CD GPIO info
>> on this tablet. This fixes the microsd-slot not working.
>>
>> Signed-off-by: Hans de Goede <hdegoede@redhat.com>
>> ---
>> Changes in v2:
>> - Make sdhci_pci_dmi_cd_gpio_overrides static const instead of just const
>> - Drop duplicate #include <linux/dmi.h> (already there at the end)
>> ---
>>  drivers/mmc/host/sdhci-pci-core.c | 38 +++++++++++++++++++++++++++++++
>>  1 file changed, 38 insertions(+)
>>
>> diff --git a/drivers/mmc/host/sdhci-pci-core.c b/drivers/mmc/host/sdhci-pci-core.c
>> index ed45ed0bdafd..9c2bce5e88d9 100644
>> --- a/drivers/mmc/host/sdhci-pci-core.c
>> +++ b/drivers/mmc/host/sdhci-pci-core.c
>> @@ -21,6 +21,7 @@
>>  #include <linux/io.h>
>>  #include <linux/iopoll.h>
>>  #include <linux/gpio.h>
>> +#include <linux/gpio/machine.h>
>>  #include <linux/pm_runtime.h>
>>  #include <linux/pm_qos.h>
>>  #include <linux/debugfs.h>
>> @@ -2054,6 +2055,29 @@ static const struct dev_pm_ops sdhci_pci_pm_ops = {
>>   *                                                                           *
>>  \*****************************************************************************/
>>  
>> +/* DMI quirks for devices with missing or broken CD GPIO info */
>> +static struct gpiod_lookup_table vexia_edu_atla10_cd_gpios = {
>> +	.dev_id = "0000:00:12.0",
>> +	.table = {
>> +		GPIO_LOOKUP("INT33FC:00", 38, "cd", GPIO_ACTIVE_HIGH),
>> +		{ }
>> +	},
>> +};
>> +
>> +static const struct dmi_system_id sdhci_pci_dmi_cd_gpio_overrides[] = {
>> +	{
>> +		/* Vexia Edu Atla 10 tablet 9V version */
>> +		.matches = {
>> +			DMI_MATCH(DMI_BOARD_VENDOR, "AMI Corporation"),
>> +			DMI_MATCH(DMI_BOARD_NAME, "Aptio CRB"),
>> +			/* Above strings are too generic, also match on BIOS date */
>> +			DMI_MATCH(DMI_BIOS_DATE, "08/25/2014"),
>> +		},
>> +		.driver_data = (void *)&vexia_edu_atla10_cd_gpios,
>> +	},
>> +	{ }
>> +};
> 
> Can this be in struct sdhci_pci_fixes?

You mean at a "struct gpiod_lookup_table *cd_gpio_lookup_table" member
to struct sdhci_pci_fixes and then set that for this tablet from say
byt_sd_probe_slot() based on the DMI match (1) ?

Before you ask I just checked and the PCI subsystem ids are generic
(which matches how broken the firmware is on this device in general).

I did consider that, but that would require un-constifying 
"struct sdhci_pci_fixes sdhci_intel_byt_sd" so that byt_sd_probe_slot()
can modify it.

I went with the current approach to be able to keep that struct const.
With your async probing argument from below I think making
byt_sd_probe_slot() set cd_gpio_lookup_table in sdhci_pci_fixes
is a good idea, even if it does require removing the const from
"struct sdhci_pci_fixes sdhci_intel_byt_sd".

1) Before you ask I just checked and the PCI subsystem ids are generic,
which matches how broken the firmware is on this device in general.


>> +
>>  static struct sdhci_pci_slot *sdhci_pci_probe_slot(
>>  	struct pci_dev *pdev, struct sdhci_pci_chip *chip, int first_bar,
>>  	int slotno)
>> @@ -2129,8 +2153,22 @@ static struct sdhci_pci_slot *sdhci_pci_probe_slot(
>>  		device_init_wakeup(&pdev->dev, true);
>>  
>>  	if (slot->cd_idx >= 0) {
>> +		struct gpiod_lookup_table *cd_gpio_lookup_table = NULL;
>> +		const struct dmi_system_id *dmi_id;
>> +
>> +		dmi_id = dmi_first_match(sdhci_pci_dmi_cd_gpio_overrides);
>> +		if (dmi_id)
>> +			cd_gpio_lookup_table = dmi_id->driver_data;
>> +
>> +		if (cd_gpio_lookup_table)
>> +			gpiod_add_lookup_table(cd_gpio_lookup_table);
> 
> If we were probing asynchronously, gpiod_add_lookup_table() and
> gpiod_remove_lookup_table() could race.

That is a good point.

> I'd suggest making vexia_edu_atla10_cd_gpios const and kmemdup'ing
> and freeing it.
> 
> Add helper functions something like:
> 
> 		cd_gpio_lookup_table = sdhci_pci_add_gpio_lookup_table(chip);
> 		if (IS_ERR(cd_gpio_lookup_table)) {
> 			etc
> 		}
> 
> 		...
> 
> 		sdhci_pci_remove_gpio_lookup_table(cd_gpio_lookup_table);
> 
>> +
>>  		ret = mmc_gpiod_request_cd(host->mmc, "cd", slot->cd_idx,
>>  					   slot->cd_override_level, 0);
>> +
>> +		if (cd_gpio_lookup_table)
>> +			gpiod_remove_lookup_table(cd_gpio_lookup_table);
>> +
>>  		if (ret && ret != -EPROBE_DEFER)
>>  			ret = mmc_gpiod_request_cd(host->mmc, NULL,
>>  						   slot->cd_idx,
> 

That should work yes, I do wonder after adding cd_gpio_lookup_table to
sdhci_pci_fixes only 1 controller will have it set (only the one for
the external slot, not the SDIO wifi and eMMC controllers).
So I think that this race goes away then, avoiding the need to kmemdup ?

Note I'm fine with proceeding either way, just wondering if we cannot
keep things a bit more simple after adding cd_gpio_lookup_table to
sdhci_pci_fixes ?

Regards,

Hans
Hans de Goede Nov. 11, 2024, 11 a.m. UTC | #2
Hi,

On 11-Nov-24 11:56 AM, Adrian Hunter wrote:
> On 11/11/24 12:26, Hans de Goede wrote:
>> Hi Adrian,
>>
>> Thank you for the review.
>>
>> On 11-Nov-24 11:07 AM, Adrian Hunter wrote:
>>> On 7/11/24 12:00, Hans de Goede wrote:
>>>> The Vexia Edu Atla 10 tablet distributed to schools in the Spanish
>>>> Andalucía region has no ACPI fwnode associated with the SDHCI controller
>>>> for its microsd-slot and thus has no ACPI GPIO resource info.
>>>>
>>>> This causes the following error to be logged and the slot to not work:
>>>> [   10.572113] sdhci-pci 0000:00:12.0: failed to setup card detect gpio
>>>>
>>>> Add a DMI quirk table for providing gpiod_lookup_tables with manually
>>>> provided CD GPIO info and use this DMI table to provide the CD GPIO info
>>>> on this tablet. This fixes the microsd-slot not working.
>>>>
>>>> Signed-off-by: Hans de Goede <hdegoede@redhat.com>
>>>> ---
>>>> Changes in v2:
>>>> - Make sdhci_pci_dmi_cd_gpio_overrides static const instead of just const
>>>> - Drop duplicate #include <linux/dmi.h> (already there at the end)
>>>> ---
>>>>  drivers/mmc/host/sdhci-pci-core.c | 38 +++++++++++++++++++++++++++++++
>>>>  1 file changed, 38 insertions(+)
>>>>
>>>> diff --git a/drivers/mmc/host/sdhci-pci-core.c b/drivers/mmc/host/sdhci-pci-core.c
>>>> index ed45ed0bdafd..9c2bce5e88d9 100644
>>>> --- a/drivers/mmc/host/sdhci-pci-core.c
>>>> +++ b/drivers/mmc/host/sdhci-pci-core.c
>>>> @@ -21,6 +21,7 @@
>>>>  #include <linux/io.h>
>>>>  #include <linux/iopoll.h>
>>>>  #include <linux/gpio.h>
>>>> +#include <linux/gpio/machine.h>
>>>>  #include <linux/pm_runtime.h>
>>>>  #include <linux/pm_qos.h>
>>>>  #include <linux/debugfs.h>
>>>> @@ -2054,6 +2055,29 @@ static const struct dev_pm_ops sdhci_pci_pm_ops = {
>>>>   *                                                                           *
>>>>  \*****************************************************************************/
>>>>  
>>>> +/* DMI quirks for devices with missing or broken CD GPIO info */
>>>> +static struct gpiod_lookup_table vexia_edu_atla10_cd_gpios = {
>>>> +	.dev_id = "0000:00:12.0",
>>>> +	.table = {
>>>> +		GPIO_LOOKUP("INT33FC:00", 38, "cd", GPIO_ACTIVE_HIGH),
>>>> +		{ }
>>>> +	},
>>>> +};
>>>> +
>>>> +static const struct dmi_system_id sdhci_pci_dmi_cd_gpio_overrides[] = {
>>>> +	{
>>>> +		/* Vexia Edu Atla 10 tablet 9V version */
>>>> +		.matches = {
>>>> +			DMI_MATCH(DMI_BOARD_VENDOR, "AMI Corporation"),
>>>> +			DMI_MATCH(DMI_BOARD_NAME, "Aptio CRB"),
>>>> +			/* Above strings are too generic, also match on BIOS date */
>>>> +			DMI_MATCH(DMI_BIOS_DATE, "08/25/2014"),
>>>> +		},
>>>> +		.driver_data = (void *)&vexia_edu_atla10_cd_gpios,
>>>> +	},
>>>> +	{ }
>>>> +};
>>>
>>> Can this be in struct sdhci_pci_fixes?
>>
>> You mean at a "struct gpiod_lookup_table *cd_gpio_lookup_table" member
>> to struct sdhci_pci_fixes and then set that for this tablet from say
>> byt_sd_probe_slot() based on the DMI match (1) ?
> 
> I was thinking of adding sdhci_pci_dmi_cd_gpio_overrides to
> sdhci_pci_fixes and the whole lot can be const.  Can also add the same
> overrides to multiple sdhci_pci_fixes instances because it is just
> a pointer.
> 
> struct sdhci_pci_fixes {
> 
> 	const struct dmi_system_id *dmi_cd_gpio_overrides;
> 
> };

I see, will do for v3.

>> Before you ask I just checked and the PCI subsystem ids are generic
>> (which matches how broken the firmware is on this device in general).
>>
>> I did consider that, but that would require un-constifying 
>> "struct sdhci_pci_fixes sdhci_intel_byt_sd" so that byt_sd_probe_slot()
>> can modify it.
>>
>> I went with the current approach to be able to keep that struct const.
>> With your async probing argument from below I think making
>> byt_sd_probe_slot() set cd_gpio_lookup_table in sdhci_pci_fixes
>> is a good idea, even if it does require removing the const from
>> "struct sdhci_pci_fixes sdhci_intel_byt_sd".
>>
>> 1) Before you ask I just checked and the PCI subsystem ids are generic,
>> which matches how broken the firmware is on this device in general.
>>
>>
>>>> +
>>>>  static struct sdhci_pci_slot *sdhci_pci_probe_slot(
>>>>  	struct pci_dev *pdev, struct sdhci_pci_chip *chip, int first_bar,
>>>>  	int slotno)
>>>> @@ -2129,8 +2153,22 @@ static struct sdhci_pci_slot *sdhci_pci_probe_slot(
>>>>  		device_init_wakeup(&pdev->dev, true);
>>>>  
>>>>  	if (slot->cd_idx >= 0) {
>>>> +		struct gpiod_lookup_table *cd_gpio_lookup_table = NULL;
>>>> +		const struct dmi_system_id *dmi_id;
>>>> +
>>>> +		dmi_id = dmi_first_match(sdhci_pci_dmi_cd_gpio_overrides);
>>>> +		if (dmi_id)
>>>> +			cd_gpio_lookup_table = dmi_id->driver_data;
>>>> +
>>>> +		if (cd_gpio_lookup_table)
>>>> +			gpiod_add_lookup_table(cd_gpio_lookup_table);
>>>
>>> If we were probing asynchronously, gpiod_add_lookup_table() and
>>> gpiod_remove_lookup_table() could race.
>>
>> That is a good point.
>>
>>> I'd suggest making vexia_edu_atla10_cd_gpios const and kmemdup'ing
>>> and freeing it.
>>>
>>> Add helper functions something like:
>>>
>>> 		cd_gpio_lookup_table = sdhci_pci_add_gpio_lookup_table(chip);
>>> 		if (IS_ERR(cd_gpio_lookup_table)) {
>>> 			etc
>>> 		}
>>>
>>> 		...
>>>
>>> 		sdhci_pci_remove_gpio_lookup_table(cd_gpio_lookup_table);
>>>
>>>> +
>>>>  		ret = mmc_gpiod_request_cd(host->mmc, "cd", slot->cd_idx,
>>>>  					   slot->cd_override_level, 0);
>>>> +
>>>> +		if (cd_gpio_lookup_table)
>>>> +			gpiod_remove_lookup_table(cd_gpio_lookup_table);
>>>> +
>>>>  		if (ret && ret != -EPROBE_DEFER)
>>>>  			ret = mmc_gpiod_request_cd(host->mmc, NULL,
>>>>  						   slot->cd_idx,
>>>
>>
>> That should work yes, I do wonder after adding cd_gpio_lookup_table to
>> sdhci_pci_fixes only 1 controller will have it set (only the one for
>> the external slot, not the SDIO wifi and eMMC controllers).
>> So I think that this race goes away then, avoiding the need to kmemdup ?
> 
> Practically speaking, I guess, but I would prefer things to be correct
> no matter what/how hardware presents itself.
> 
>>
>> Note I'm fine with proceeding either way, just wondering if we cannot
>> keep things a bit more simple after adding cd_gpio_lookup_table to
>> sdhci_pci_fixes ?
> 
> Shouldn't be too bad.  Instead of sdhci_pci_dmi_cd_gpio_overrides being
> global it would be chip->fixes->dmi_cd_gpio_overrides, and add kmemdup
> and an error path for it.

Ok, I'll add the kmemdump helper for v3.

Regards,

Hans
diff mbox series

Patch

diff --git a/drivers/mmc/host/sdhci-pci-core.c b/drivers/mmc/host/sdhci-pci-core.c
index ed45ed0bdafd..9c2bce5e88d9 100644
--- a/drivers/mmc/host/sdhci-pci-core.c
+++ b/drivers/mmc/host/sdhci-pci-core.c
@@ -21,6 +21,7 @@ 
 #include <linux/io.h>
 #include <linux/iopoll.h>
 #include <linux/gpio.h>
+#include <linux/gpio/machine.h>
 #include <linux/pm_runtime.h>
 #include <linux/pm_qos.h>
 #include <linux/debugfs.h>
@@ -2054,6 +2055,29 @@  static const struct dev_pm_ops sdhci_pci_pm_ops = {
  *                                                                           *
 \*****************************************************************************/
 
+/* DMI quirks for devices with missing or broken CD GPIO info */
+static struct gpiod_lookup_table vexia_edu_atla10_cd_gpios = {
+	.dev_id = "0000:00:12.0",
+	.table = {
+		GPIO_LOOKUP("INT33FC:00", 38, "cd", GPIO_ACTIVE_HIGH),
+		{ }
+	},
+};
+
+static const struct dmi_system_id sdhci_pci_dmi_cd_gpio_overrides[] = {
+	{
+		/* Vexia Edu Atla 10 tablet 9V version */
+		.matches = {
+			DMI_MATCH(DMI_BOARD_VENDOR, "AMI Corporation"),
+			DMI_MATCH(DMI_BOARD_NAME, "Aptio CRB"),
+			/* Above strings are too generic, also match on BIOS date */
+			DMI_MATCH(DMI_BIOS_DATE, "08/25/2014"),
+		},
+		.driver_data = (void *)&vexia_edu_atla10_cd_gpios,
+	},
+	{ }
+};
+
 static struct sdhci_pci_slot *sdhci_pci_probe_slot(
 	struct pci_dev *pdev, struct sdhci_pci_chip *chip, int first_bar,
 	int slotno)
@@ -2129,8 +2153,22 @@  static struct sdhci_pci_slot *sdhci_pci_probe_slot(
 		device_init_wakeup(&pdev->dev, true);
 
 	if (slot->cd_idx >= 0) {
+		struct gpiod_lookup_table *cd_gpio_lookup_table = NULL;
+		const struct dmi_system_id *dmi_id;
+
+		dmi_id = dmi_first_match(sdhci_pci_dmi_cd_gpio_overrides);
+		if (dmi_id)
+			cd_gpio_lookup_table = dmi_id->driver_data;
+
+		if (cd_gpio_lookup_table)
+			gpiod_add_lookup_table(cd_gpio_lookup_table);
+
 		ret = mmc_gpiod_request_cd(host->mmc, "cd", slot->cd_idx,
 					   slot->cd_override_level, 0);
+
+		if (cd_gpio_lookup_table)
+			gpiod_remove_lookup_table(cd_gpio_lookup_table);
+
 		if (ret && ret != -EPROBE_DEFER)
 			ret = mmc_gpiod_request_cd(host->mmc, NULL,
 						   slot->cd_idx,