The ACPI tables on the Xiaomi Mi Pad 2 hide a number of devices from Linux, such as the fuel-gauge IC used for battery monitoring. This adds a xiaomi-mipad2 "board-file" which manually instantiates device-objects for the missing devices. When build as a module this will only ever get auto-loaded on Xiaomi Mi Pad 2 tablets, allowing us to support these tablets in generic X86 kernels at the cost of just a small amount of diskspace for the module. As discussed previously this is the least ugly option to get these devices to fully work and to do so without adding any extra code to the main kernel image (vmlinuz) when built as a module. Link: https://lore.kernel.org/platform-driver-x86/20211031162428.22368-1-hdegoede@xxxxxxxxxx/ Signed-off-by: Hans de Goede <hdegoede@xxxxxxxxxx> --- drivers/platform/x86/Kconfig | 13 +++ drivers/platform/x86/Makefile | 1 + drivers/platform/x86/xiaomi-mipad2.c | 126 +++++++++++++++++++++++++++ 3 files changed, 140 insertions(+) create mode 100644 drivers/platform/x86/xiaomi-mipad2.c diff --git a/drivers/platform/x86/Kconfig b/drivers/platform/x86/Kconfig index 9cb8d33cc6e1..df8101fdfc6a 100644 --- a/drivers/platform/x86/Kconfig +++ b/drivers/platform/x86/Kconfig @@ -958,6 +958,19 @@ config TOPSTAR_LAPTOP If you have a Topstar laptop, say Y or M here. +config XIAOMI_MIPAD2 + tristate "Xiaomi Mi Pad 2 support" + depends on I2C && ACPI + help + The ACPI tables on the Xiaomi Mi Pad 2 hide a number of devices + from Linux, such as the fuel-gauge IC used for battery monitoring. + + Say Y or M here to enable building a xiaomi-mipad2 board-file which + manually instantiates device-objects for the missing devices. + + If you have a Xiaomi Mi Pad 2 say Y or M here, for a generic X86 + distro config say M here. + config I2C_MULTI_INSTANTIATE tristate "I2C multi instantiate pseudo device driver" depends on I2C && ACPI diff --git a/drivers/platform/x86/Makefile b/drivers/platform/x86/Makefile index 3f610332b556..631f1b948cd0 100644 --- a/drivers/platform/x86/Makefile +++ b/drivers/platform/x86/Makefile @@ -105,6 +105,7 @@ obj-$(CONFIG_PANASONIC_LAPTOP) += panasonic-laptop.o obj-$(CONFIG_SONY_LAPTOP) += sony-laptop.o obj-$(CONFIG_SYSTEM76_ACPI) += system76_acpi.o obj-$(CONFIG_TOPSTAR_LAPTOP) += topstar-laptop.o +obj-$(CONFIG_XIAOMI_MIPAD2) += xiaomi-mipad2.o # Platform drivers obj-$(CONFIG_DMI_DEVICE_PROPERTIES) += dmi_device_properties.o diff --git a/drivers/platform/x86/xiaomi-mipad2.c b/drivers/platform/x86/xiaomi-mipad2.c new file mode 100644 index 000000000000..bf333aaa0edf --- /dev/null +++ b/drivers/platform/x86/xiaomi-mipad2.c @@ -0,0 +1,126 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * On the Xiaomi Mi Pad 2 X86 tablet a bunch of devices are hidden when + * the EFI if the tablet does thinks the OS it is booting is Windows + * (OSID in the DSDT is set to 1); and the EFI code thinks this as soon + * as the EFI bootloader is not Xiaomi's own signed Android loader :| + * + * This "board-file" takes care of instantiating the hidden devices manually. + * + * Copyright (C) 2021 Hans de Goede <hdegoede@xxxxxxxxxx> + */ + +#define pr_fmt(fmt) "xiaomi-mipad2: " fmt + +#include <linux/acpi.h> +#include <linux/dmi.h> +#include <linux/i2c.h> +#include <linux/module.h> +#include <linux/mod_devicetable.h> + +/********** BQ27520 fuel-gauge info **********/ +#define BQ27520_ADAPTER "\\_SB_.PCI0.I2C1" + +static const char * const bq27520_suppliers[] = { "bq25890-charger" }; + +static const struct property_entry bq27520_props[] = { + PROPERTY_ENTRY_STRING_ARRAY("supplied-from", bq27520_suppliers), + { } +}; + +static const struct software_node bq27520_node = { + .properties = bq27520_props, +}; + +static const struct i2c_board_info bq27520_board_info = { + .type = "bq27520", + .addr = 0x55, + .dev_name = "bq27520", + .swnode = &bq27520_node, +}; + +static struct i2c_client *bq27520_client; + +/********** KTD2026 RGB notification LED controller **********/ +#define KTD2026_ADAPTER "\\_SB_.PCI0.I2C3" + +static const struct i2c_board_info ktd2026_board_info = { + .type = "ktd2026", + .addr = 0x30, + .dev_name = "ktd2026", +}; + +static struct i2c_client *ktd2026_client; + +/********** DMI-match, probe(), etc. **********/ +static const struct dmi_system_id xiaomi_mipad2_ids[] __initconst = { + { + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Xiaomi Inc"), + DMI_MATCH(DMI_PRODUCT_NAME, "Mipad2"), + }, + }, + {} /* Terminating entry */ +}; +MODULE_DEVICE_TABLE(dmi, xiaomi_mipad2_ids); + +static __init struct i2c_client *xiaomi_instantiate_i2c_client( + char *adapter_path, + const struct i2c_board_info *board_info) +{ + struct i2c_client *client; + struct i2c_adapter *adap; + acpi_handle handle; + acpi_status status; + + status = acpi_get_handle(NULL, adapter_path, &handle); + if (ACPI_FAILURE(status)) { + pr_err("Error could not get %s handle\n", adapter_path); + return ERR_PTR(-ENODEV); + } + + adap = i2c_acpi_find_adapter_by_handle(handle); + if (!adap) { + pr_err("Error could not get %s adapter\n", adapter_path); + return ERR_PTR(-ENODEV); + } + + client = i2c_new_client_device(adap, board_info); + + put_device(&adap->dev); + + return client; +} + +static __init int xiaomi_mipad2_init(void) +{ + if (!dmi_first_match(xiaomi_mipad2_ids)) + return -ENODEV; + + bq27520_client = xiaomi_instantiate_i2c_client(BQ27520_ADAPTER, + &bq27520_board_info); + if (IS_ERR(bq27520_client)) + return PTR_ERR(bq27520_client); + + ktd2026_client = xiaomi_instantiate_i2c_client(KTD2026_ADAPTER, + &ktd2026_board_info); + if (IS_ERR(ktd2026_client)) { + i2c_unregister_device(bq27520_client); + return PTR_ERR(ktd2026_client); + } + + return 0; +} + +static __exit void xiaomi_mipad2_cleanup(void) +{ + i2c_unregister_device(ktd2026_client); + i2c_unregister_device(bq27520_client); +} + +module_init(xiaomi_mipad2_init); +module_exit(xiaomi_mipad2_cleanup); + +MODULE_AUTHOR("Hans de Goede <hdegoede@xxxxxxxxxx"); +MODULE_DESCRIPTION("Xiaomi Mi Pad 2 board-file"); +MODULE_LICENSE("GPL"); -- 2.31.1