Skip to content

Commit 35f9e77

Browse files
jwrdegoederafaeljw
authored andcommitted
ACPI / x86: Add acpi_quirk_skip_[i2c_client|serdev]_enumeration() helpers
x86 ACPI boards which ship with only Android as their factory image usually declare a whole bunch of bogus I2C devs in their ACPI tables and sometimes there are issues with serdev devices on these boards too, e.g. the resource points to the wrong serdev_controller. Instantiating I2C / serdev devs for these bogus devs causes various issues, e.g. GPIO/IRQ resource conflicts because sometimes drivers do bind to them. The Android x86 kernel fork shipped on these devices has some special code to remove the bogus I2C clients (and serdevs are ignored completely). Introduce acpi_quirk_skip_i2c_client_enumeration() and acpi_quirk_skip_serdev_enumeration() helpers. Which can be used by the I2C/ serdev code to skip instantiating any I2C or serdev devs on broken boards. These 2 helpers are added to drivers/acpi/x86/utils.c so that the DMI table can be shared between the I2C and serdev code. Note these boards typically do actually have I2C and serdev devices, just different ones then the ones described in their DSDT. The devices which are actually present are manually instantiated by the drivers/platform/x86/x86-android-tablets.c kernel module. The new helpers are only build if CONFIG_X86_ANDROID_TABLETS is enabled, otherwise they are empty stubs to not unnecessarily grow the kernel size. Signed-off-by: Hans de Goede <hdegoede@redhat.com> Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
1 parent f85196b commit 35f9e77

2 files changed

Lines changed: 127 additions & 0 deletions

File tree

drivers/acpi/x86/utils.c

Lines changed: 111 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010

1111
#include <linux/acpi.h>
1212
#include <linux/dmi.h>
13+
#include <linux/platform_device.h>
1314
#include <asm/cpu_device_id.h>
1415
#include <asm/intel-family.h>
1516
#include "../internal.h"
@@ -160,3 +161,113 @@ bool force_storage_d3(void)
160161
{
161162
return x86_match_cpu(storage_d3_cpu_ids);
162163
}
164+
165+
#if IS_ENABLED(CONFIG_X86_ANDROID_TABLETS)
166+
/*
167+
* x86 ACPI boards which ship with only Android as their factory image usually
168+
* declare a whole bunch of bogus I2C devices in their ACPI tables and sometimes
169+
* there are issues with serdev devices on these boards too, e.g. the resource
170+
* points to the wrong serdev_controller.
171+
*
172+
* Instantiating I2C / serdev devs for these bogus devs causes various issues,
173+
* e.g. GPIO/IRQ resource conflicts because sometimes drivers do bind to them.
174+
* The Android x86 kernel fork shipped on these devices has some special code
175+
* to remove the bogus I2C clients (and AFAICT serdevs are ignored completely).
176+
*
177+
* The acpi_quirk_skip_*_enumeration() functions below are used by the I2C or
178+
* serdev code to skip instantiating any I2C or serdev devs on broken boards.
179+
*
180+
* In case of I2C an exception is made for HIDs on the i2c_acpi_known_good_ids
181+
* list. These are known to always be correct (and in case of the audio-codecs
182+
* the drivers heavily rely on the codec being enumerated through ACPI).
183+
*
184+
* Note these boards typically do actually have I2C and serdev devices,
185+
* just different ones then the ones described in their DSDT. The devices
186+
* which are actually present are manually instantiated by the
187+
* drivers/platform/x86/x86-android-tablets.c kernel module.
188+
*/
189+
#define ACPI_QUIRK_SKIP_I2C_CLIENTS BIT(0)
190+
#define ACPI_QUIRK_UART1_TTY_UART2_SKIP BIT(1)
191+
192+
static const struct dmi_system_id acpi_skip_serial_bus_enumeration_ids[] = {
193+
{
194+
.matches = {
195+
DMI_EXACT_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."),
196+
DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "ME176C"),
197+
},
198+
.driver_data = (void *)(ACPI_QUIRK_SKIP_I2C_CLIENTS |
199+
ACPI_QUIRK_UART1_TTY_UART2_SKIP),
200+
},
201+
{
202+
.matches = {
203+
DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."),
204+
DMI_MATCH(DMI_PRODUCT_NAME, "TF103C"),
205+
},
206+
.driver_data = (void *)ACPI_QUIRK_SKIP_I2C_CLIENTS,
207+
},
208+
{
209+
/* Whitelabel (sold as various brands) TM800A550L */
210+
.matches = {
211+
DMI_MATCH(DMI_BOARD_VENDOR, "AMI Corporation"),
212+
DMI_MATCH(DMI_BOARD_NAME, "Aptio CRB"),
213+
/* Above strings are too generic, also match on BIOS version */
214+
DMI_MATCH(DMI_BIOS_VERSION, "ZY-8-BI-PX4S70VTR400-X423B-005-D"),
215+
},
216+
.driver_data = (void *)ACPI_QUIRK_SKIP_I2C_CLIENTS,
217+
},
218+
{}
219+
};
220+
221+
static const struct acpi_device_id i2c_acpi_known_good_ids[] = {
222+
{ "10EC5640", 0 }, /* RealTek ALC5640 audio codec */
223+
{ "INT33F4", 0 }, /* X-Powers AXP288 PMIC */
224+
{ "INT33FD", 0 }, /* Intel Crystal Cove PMIC */
225+
{ "NPCE69A", 0 }, /* Asus Transformer keyboard dock */
226+
{}
227+
};
228+
229+
bool acpi_quirk_skip_i2c_client_enumeration(struct acpi_device *adev)
230+
{
231+
const struct dmi_system_id *dmi_id;
232+
long quirks;
233+
234+
dmi_id = dmi_first_match(acpi_skip_serial_bus_enumeration_ids);
235+
if (!dmi_id)
236+
return false;
237+
238+
quirks = (unsigned long)dmi_id->driver_data;
239+
if (!(quirks & ACPI_QUIRK_SKIP_I2C_CLIENTS))
240+
return false;
241+
242+
return acpi_match_device_ids(adev, i2c_acpi_known_good_ids);
243+
}
244+
EXPORT_SYMBOL_GPL(acpi_quirk_skip_i2c_client_enumeration);
245+
246+
int acpi_quirk_skip_serdev_enumeration(struct device *controller_parent, bool *skip)
247+
{
248+
struct acpi_device *adev = ACPI_COMPANION(controller_parent);
249+
const struct dmi_system_id *dmi_id;
250+
long quirks = 0;
251+
252+
*skip = false;
253+
254+
/* !dev_is_platform() to not match on PNP enumerated debug UARTs */
255+
if (!adev || !adev->pnp.unique_id || !dev_is_platform(controller_parent))
256+
return 0;
257+
258+
dmi_id = dmi_first_match(acpi_skip_serial_bus_enumeration_ids);
259+
if (dmi_id)
260+
quirks = (unsigned long)dmi_id->driver_data;
261+
262+
if (quirks & ACPI_QUIRK_UART1_TTY_UART2_SKIP) {
263+
if (!strcmp(adev->pnp.unique_id, "1"))
264+
return -ENODEV; /* Create tty cdev instead of serdev */
265+
266+
if (!strcmp(adev->pnp.unique_id, "2"))
267+
*skip = true;
268+
}
269+
270+
return 0;
271+
}
272+
EXPORT_SYMBOL_GPL(acpi_quirk_skip_serdev_enumeration);
273+
#endif

include/acpi/acpi_bus.h

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -622,6 +622,22 @@ static inline bool acpi_device_always_present(struct acpi_device *adev)
622622
}
623623
#endif
624624

625+
#if IS_ENABLED(CONFIG_X86_ANDROID_TABLETS)
626+
bool acpi_quirk_skip_i2c_client_enumeration(struct acpi_device *adev);
627+
int acpi_quirk_skip_serdev_enumeration(struct device *controller_parent, bool *skip);
628+
#else
629+
static inline bool acpi_quirk_skip_i2c_client_enumeration(struct acpi_device *adev)
630+
{
631+
return false;
632+
}
633+
static inline int
634+
acpi_quirk_skip_serdev_enumeration(struct device *controller_parent, bool *skip)
635+
{
636+
*skip = false;
637+
return 0;
638+
}
639+
#endif
640+
625641
#ifdef CONFIG_PM
626642
void acpi_pm_wakeup_event(struct device *dev);
627643
acpi_status acpi_add_pm_notifier(struct acpi_device *adev, struct device *dev,

0 commit comments

Comments
 (0)