From 6614823a16b700b7d996e75b1200b29bec88f8ef Mon Sep 17 00:00:00 2001 From: Srinivas Pandruvada Date: Fri, 10 Feb 2023 22:32:47 -0800 Subject: platform/x86: ISST: Add TPMI target Add TPMI as one of the device type which can be registered with ISST common driver. Signed-off-by: Srinivas Pandruvada Link: https://lore.kernel.org/r/20230211063257.311746-3-srinivas.pandruvada@linux.intel.com Signed-off-by: Hans de Goede Reviewed-by: Hans de Goede --- drivers/platform/x86/intel/speed_select_if/isst_if_common.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'drivers/platform') diff --git a/drivers/platform/x86/intel/speed_select_if/isst_if_common.h b/drivers/platform/x86/intel/speed_select_if/isst_if_common.h index 35ff506b402e..967c338e83c5 100644 --- a/drivers/platform/x86/intel/speed_select_if/isst_if_common.h +++ b/drivers/platform/x86/intel/speed_select_if/isst_if_common.h @@ -30,7 +30,8 @@ #define ISST_IF_DEV_MBOX 0 #define ISST_IF_DEV_MMIO 1 -#define ISST_IF_DEV_MAX 2 +#define ISST_IF_DEV_TPMI 2 +#define ISST_IF_DEV_MAX 3 /** * struct isst_if_cmd_cb - Used to register a IOCTL handler -- cgit v1.2.3 From 33c16dc1a2d1697be0fec95a0393bef4fbc27a0f Mon Sep 17 00:00:00 2001 From: Srinivas Pandruvada Date: Fri, 10 Feb 2023 22:32:48 -0800 Subject: platform/x86: ISST: Add IOCTL default callback The common IOCTL handler has a predefined list of IOCTLs it can handle. There is no default handler, if there is no match. Allow a client driver to define their own version of default IOCTL callback. In this way the default handling is passed to the client drivers to handle. With the introduction of TPMI target, IOCTL list is extended. The additional TPMI specific IOCTLs will be passed to the TPMI client driver default IOCTL handler. Signed-off-by: Srinivas Pandruvada Link: https://lore.kernel.org/r/20230211063257.311746-4-srinivas.pandruvada@linux.intel.com Signed-off-by: Hans de Goede Reviewed-by: Hans de Goede --- drivers/platform/x86/intel/speed_select_if/isst_if_common.c | 11 +++++++++++ drivers/platform/x86/intel/speed_select_if/isst_if_common.h | 4 ++++ 2 files changed, 15 insertions(+) (limited to 'drivers/platform') diff --git a/drivers/platform/x86/intel/speed_select_if/isst_if_common.c b/drivers/platform/x86/intel/speed_select_if/isst_if_common.c index 0954a04623ed..cdf0501f4e4f 100644 --- a/drivers/platform/x86/intel/speed_select_if/isst_if_common.c +++ b/drivers/platform/x86/intel/speed_select_if/isst_if_common.c @@ -588,6 +588,7 @@ static long isst_if_def_ioctl(struct file *file, unsigned int cmd, struct isst_if_cmd_cb cmd_cb; struct isst_if_cmd_cb *cb; long ret = -ENOTTY; + int i; switch (cmd) { case ISST_IF_GET_PLATFORM_INFO: @@ -616,6 +617,16 @@ static long isst_if_def_ioctl(struct file *file, unsigned int cmd, ret = isst_if_exec_multi_cmd(argp, &cmd_cb); break; default: + for (i = 0; i < ISST_IF_DEV_MAX; ++i) { + struct isst_if_cmd_cb *cb = &punit_callbacks[i]; + int ret; + + if (cb->def_ioctl) { + ret = cb->def_ioctl(file, cmd, arg); + if (!ret) + return ret; + } + } break; } diff --git a/drivers/platform/x86/intel/speed_select_if/isst_if_common.h b/drivers/platform/x86/intel/speed_select_if/isst_if_common.h index 967c338e83c5..34a172e5c82c 100644 --- a/drivers/platform/x86/intel/speed_select_if/isst_if_common.h +++ b/drivers/platform/x86/intel/speed_select_if/isst_if_common.h @@ -48,6 +48,8 @@ * response to user ioctl buffer. The "resume" argument * can be used to avoid storing the command for replay * during system resume + * @def_ioctl: Default IOCTL handler callback, if there is no match in + * the existing list of IOCTL handled by the common handler. * * This structure is used to register an handler for IOCTL. To avoid * code duplication common code handles all the IOCTL command read/write @@ -58,8 +60,10 @@ struct isst_if_cmd_cb { int registered; int cmd_size; int offset; + struct module *owner; long (*cmd_callback)(u8 *ptr, int *write_only, int resume); + long (*def_ioctl)(struct file *file, unsigned int cmd, unsigned long arg); }; /* Internal interface functions */ -- cgit v1.2.3 From 0e8ae77c869ed425488e3db43dbb76ec9bca8655 Mon Sep 17 00:00:00 2001 From: Srinivas Pandruvada Date: Fri, 10 Feb 2023 22:32:49 -0800 Subject: platform/x86: ISST: Add API version of the target User space can get the API version using IOCTL ISST_IF_GET_PLATFORM_INFO. This information can be used to get IOCTLs supported by the kernel driver. This version is hardcoded in the driver. Allow the registered client to specify the supported API version. In this way a registered client can specify a higher API version to extend IOCTL set. Signed-off-by: Srinivas Pandruvada Link: https://lore.kernel.org/r/20230211063257.311746-5-srinivas.pandruvada@linux.intel.com Signed-off-by: Hans de Goede Reviewed-by: Hans de Goede --- drivers/platform/x86/intel/speed_select_if/isst_if_common.c | 8 +++++++- drivers/platform/x86/intel/speed_select_if/isst_if_common.h | 3 ++- 2 files changed, 9 insertions(+), 2 deletions(-) (limited to 'drivers/platform') diff --git a/drivers/platform/x86/intel/speed_select_if/isst_if_common.c b/drivers/platform/x86/intel/speed_select_if/isst_if_common.c index cdf0501f4e4f..19e671500f00 100644 --- a/drivers/platform/x86/intel/speed_select_if/isst_if_common.c +++ b/drivers/platform/x86/intel/speed_select_if/isst_if_common.c @@ -262,11 +262,13 @@ bool isst_if_mbox_cmd_set_req(struct isst_if_mbox_cmd *cmd) } EXPORT_SYMBOL_GPL(isst_if_mbox_cmd_set_req); +static int isst_if_api_version; + static int isst_if_get_platform_info(void __user *argp) { struct isst_if_platform_info info; - info.api_version = ISST_IF_API_VERSION; + info.api_version = isst_if_api_version; info.driver_version = ISST_IF_DRIVER_VERSION; info.max_cmds_per_ioctl = ISST_IF_CMD_LIMIT; info.mbox_supported = punit_callbacks[ISST_IF_DEV_MBOX].registered; @@ -767,6 +769,10 @@ int isst_if_cdev_register(int device_type, struct isst_if_cmd_cb *cb) mutex_unlock(&punit_misc_dev_open_lock); return -EAGAIN; } + if (!cb->api_version) + cb->api_version = ISST_IF_API_VERSION; + if (cb->api_version > isst_if_api_version) + isst_if_api_version = cb->api_version; memcpy(&punit_callbacks[device_type], cb, sizeof(*cb)); punit_callbacks[device_type].registered = 1; mutex_unlock(&punit_misc_dev_open_lock); diff --git a/drivers/platform/x86/intel/speed_select_if/isst_if_common.h b/drivers/platform/x86/intel/speed_select_if/isst_if_common.h index 34a172e5c82c..1004f2c9cca8 100644 --- a/drivers/platform/x86/intel/speed_select_if/isst_if_common.h +++ b/drivers/platform/x86/intel/speed_select_if/isst_if_common.h @@ -41,6 +41,7 @@ * @offset: Offset to the first valid member in command structure. * This will be the offset of the start of the command * after command count field + * @api_version: API version supported for this target. 0, if none. * @owner: Registered module owner * @cmd_callback: Callback function to handle IOCTL. The callback has the * command pointer with data for command. There is a pointer @@ -60,7 +61,7 @@ struct isst_if_cmd_cb { int registered; int cmd_size; int offset; - + int api_version; struct module *owner; long (*cmd_callback)(u8 *ptr, int *write_only, int resume); long (*def_ioctl)(struct file *file, unsigned int cmd, unsigned long arg); -- cgit v1.2.3 From 2f0cf1e85ddb5ae17284050dc1adafb89e4f1d8f Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Mon, 13 Feb 2023 21:35:53 +0100 Subject: platform/x86: x86-android-tablets: Add Acer Iconia One 7 B1-750 data The Acer Iconia One 7 B1-750 is a x86 ACPI tablet which ships with Android x86 as factory OS. Its DSDT contains a bunch of I2C devices which are not actually there, causing various resource conflicts. Enumeration of these is skipped through the acpi_quirk_skip_i2c_client_enumeration(). Add support for manually instantiating the I2C + other devices which are actually present on this tablet by adding the necessary device info to the x86-android-tablets module. Signed-off-by: Hans de Goede Reviewed-by: Andy Shevchenko Link: https://lore.kernel.org/r/20230301092331.7038-2-hdegoede@redhat.com --- drivers/platform/x86/x86-android-tablets.c | 101 ++++++++++++++++++++++++++--- 1 file changed, 91 insertions(+), 10 deletions(-) (limited to 'drivers/platform') diff --git a/drivers/platform/x86/x86-android-tablets.c b/drivers/platform/x86/x86-android-tablets.c index 111b007656fc..8405e1c58d52 100644 --- a/drivers/platform/x86/x86-android-tablets.c +++ b/drivers/platform/x86/x86-android-tablets.c @@ -265,6 +265,88 @@ static struct gpiod_lookup_table int3496_gpo2_pin22_gpios = { }, }; +static struct gpiod_lookup_table int3496_reference_gpios = { + .dev_id = "intel-int3496", + .table = { + GPIO_LOOKUP("INT33FC:01", 15, "vbus", GPIO_ACTIVE_HIGH), + GPIO_LOOKUP("INT33FC:02", 1, "mux", GPIO_ACTIVE_HIGH), + GPIO_LOOKUP("INT33FC:02", 18, "id", GPIO_ACTIVE_HIGH), + { } + }, +}; + +/* Acer Iconia One 7 B1-750 has an Android factory img with everything hardcoded */ +static const char * const acer_b1_750_mount_matrix[] = { + "-1", "0", "0", + "0", "1", "0", + "0", "0", "1" +}; + +static const struct property_entry acer_b1_750_bma250e_props[] = { + PROPERTY_ENTRY_STRING_ARRAY("mount-matrix", acer_b1_750_mount_matrix), + { } +}; + +static const struct software_node acer_b1_750_bma250e_node = { + .properties = acer_b1_750_bma250e_props, +}; + +static const struct x86_i2c_client_info acer_b1_750_i2c_clients[] __initconst = { + { + /* Novatek NVT-ts touchscreen */ + .board_info = { + .type = "NVT-ts", + .addr = 0x34, + .dev_name = "NVT-ts", + }, + .adapter_path = "\\_SB_.I2C4", + .irq_data = { + .type = X86_ACPI_IRQ_TYPE_GPIOINT, + .chip = "INT33FC:02", + .index = 3, + .trigger = ACPI_EDGE_SENSITIVE, + .polarity = ACPI_ACTIVE_LOW, + }, + }, { + /* BMA250E accelerometer */ + .board_info = { + .type = "bma250e", + .addr = 0x18, + .swnode = &acer_b1_750_bma250e_node, + }, + .adapter_path = "\\_SB_.I2C3", + .irq_data = { + .type = X86_ACPI_IRQ_TYPE_GPIOINT, + .chip = "INT33FC:02", + .index = 25, + .trigger = ACPI_LEVEL_SENSITIVE, + .polarity = ACPI_ACTIVE_HIGH, + }, + }, +}; + +static struct gpiod_lookup_table acer_b1_750_goodix_gpios = { + .dev_id = "i2c-NVT-ts", + .table = { + GPIO_LOOKUP("INT33FC:01", 26, "reset", GPIO_ACTIVE_LOW), + { } + }, +}; + +static struct gpiod_lookup_table * const acer_b1_750_gpios[] = { + &acer_b1_750_goodix_gpios, + &int3496_reference_gpios, + NULL +}; + +static const struct x86_dev_info acer_b1_750_info __initconst = { + .i2c_client_info = acer_b1_750_i2c_clients, + .i2c_client_count = ARRAY_SIZE(acer_b1_750_i2c_clients), + .pdev_info = int3496_pdevs, + .pdev_count = ARRAY_SIZE(int3496_pdevs), + .gpiod_lookup_tables = acer_b1_750_gpios, +}; + /* * Advantech MICA-071 * This is a standard Windows tablet, but it has an extra "quick launch" button @@ -1298,17 +1380,8 @@ static const struct x86_i2c_client_info nextbook_ares8_i2c_clients[] __initconst }, }; -static struct gpiod_lookup_table nextbook_ares8_int3496_gpios = { - .dev_id = "intel-int3496", - .table = { - GPIO_LOOKUP("INT33FC:02", 1, "mux", GPIO_ACTIVE_HIGH), - GPIO_LOOKUP("INT33FC:02", 18, "id", GPIO_ACTIVE_HIGH), - { } - }, -}; - static struct gpiod_lookup_table * const nextbook_ares8_gpios[] = { - &nextbook_ares8_int3496_gpios, + &int3496_reference_gpios, NULL }; @@ -1435,6 +1508,14 @@ static const struct x86_dev_info xiaomi_mipad2_info __initconst = { }; static const struct dmi_system_id x86_android_tablet_ids[] __initconst = { + { + /* Acer Iconia One 7 B1-750 */ + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Insyde"), + DMI_MATCH(DMI_PRODUCT_NAME, "VESPA2"), + }, + .driver_data = (void *)&acer_b1_750_info, + }, { /* Advantech MICA-071 */ .matches = { -- cgit v1.2.3 From afdcb5353f06cf5ddee3999f7e6353e889a50edc Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Sun, 19 Feb 2023 17:06:14 +0100 Subject: platform/x86: x86-android-tablets: Move into its own subdir Move the x86-android-tablets code into its own subdir, this is a preparation patch for splitting the somewhat large file into multiple smaller files. Signed-off-by: Hans de Goede Reviewed-by: Andy Shevchenko Link: https://lore.kernel.org/r/20230301092331.7038-3-hdegoede@redhat.com --- MAINTAINERS | 2 +- drivers/platform/x86/Kconfig | 17 +- drivers/platform/x86/Makefile | 2 +- drivers/platform/x86/x86-android-tablets.c | 1884 -------------------- drivers/platform/x86/x86-android-tablets/Kconfig | 21 + drivers/platform/x86/x86-android-tablets/Makefile | 8 + .../x86-android-tablets/x86-android-tablets-main.c | 1884 ++++++++++++++++++++ 7 files changed, 1916 insertions(+), 1902 deletions(-) delete mode 100644 drivers/platform/x86/x86-android-tablets.c create mode 100644 drivers/platform/x86/x86-android-tablets/Kconfig create mode 100644 drivers/platform/x86/x86-android-tablets/Makefile create mode 100644 drivers/platform/x86/x86-android-tablets/x86-android-tablets-main.c (limited to 'drivers/platform') diff --git a/MAINTAINERS b/MAINTAINERS index 8d5bc223f305..7b08a58506e3 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -22688,7 +22688,7 @@ M: Hans de Goede L: platform-driver-x86@vger.kernel.org S: Maintained T: git git://git.kernel.org/pub/scm/linux/kernel/git/pdx86/platform-drivers-x86.git -F: drivers/platform/x86/x86-android-tablets.c +F: drivers/platform/x86/x86-android-tablets/ X86 PLATFORM DRIVERS M: Hans de Goede diff --git a/drivers/platform/x86/Kconfig b/drivers/platform/x86/Kconfig index 4a01b315e0a9..46d9e3130ebb 100644 --- a/drivers/platform/x86/Kconfig +++ b/drivers/platform/x86/Kconfig @@ -978,22 +978,7 @@ config TOUCHSCREEN_DMI the OS-image for the device. This option supplies the missing info. Enable this for x86 tablets with Silead or Chipone touchscreens. -config X86_ANDROID_TABLETS - tristate "X86 Android tablet support" - depends on I2C && SPI && SERIAL_DEV_BUS && ACPI && EFI && GPIOLIB - help - X86 tablets which ship with Android as (part of) the factory image - typically have various problems with their DSDTs. The factory kernels - shipped on these devices typically have device addresses and GPIOs - hardcoded in the kernel, rather than specified in their DSDT. - - With the DSDT containing a random collection of devices which may or - may not actually be present. This driver contains various fixes for - such tablets, including instantiating kernel devices for devices which - are missing from the DSDT. - - If you have a x86 Android tablet say Y or M here, for a generic x86 - distro config say M here. +source "drivers/platform/x86/x86-android-tablets/Kconfig" config FW_ATTR_CLASS tristate diff --git a/drivers/platform/x86/Makefile b/drivers/platform/x86/Makefile index 1d3d1b02541b..0d9cc9af6ba7 100644 --- a/drivers/platform/x86/Makefile +++ b/drivers/platform/x86/Makefile @@ -112,7 +112,7 @@ obj-$(CONFIG_SERIAL_MULTI_INSTANTIATE) += serial-multi-instantiate.o obj-$(CONFIG_MLX_PLATFORM) += mlx-platform.o obj-$(CONFIG_TOUCHSCREEN_DMI) += touchscreen_dmi.o obj-$(CONFIG_WIRELESS_HOTKEY) += wireless-hotkey.o -obj-$(CONFIG_X86_ANDROID_TABLETS) += x86-android-tablets.o +obj-$(CONFIG_X86_ANDROID_TABLETS) += x86-android-tablets/ # Intel uncore drivers obj-$(CONFIG_INTEL_IPS) += intel_ips.o diff --git a/drivers/platform/x86/x86-android-tablets.c b/drivers/platform/x86/x86-android-tablets.c deleted file mode 100644 index 8405e1c58d52..000000000000 --- a/drivers/platform/x86/x86-android-tablets.c +++ /dev/null @@ -1,1884 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0+ -/* - * DMI based code to deal with broken DSDTs on X86 tablets which ship with - * Android as (part of) the factory image. The factory kernels shipped on these - * devices typically have a bunch of things hardcoded, rather than specified - * in their DSDT. - * - * Copyright (C) 2021-2022 Hans de Goede - */ - -#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -/* For gpio_get_desc() which is EXPORT_SYMBOL_GPL() */ -#include "../../gpio/gpiolib.h" -#include "../../gpio/gpiolib-acpi.h" - -/* - * Helper code to get Linux IRQ numbers given a description of the IRQ source - * (either IOAPIC index, or GPIO chip name + pin-number). - */ -enum x86_acpi_irq_type { - X86_ACPI_IRQ_TYPE_NONE, - X86_ACPI_IRQ_TYPE_APIC, - X86_ACPI_IRQ_TYPE_GPIOINT, - X86_ACPI_IRQ_TYPE_PMIC, -}; - -struct x86_acpi_irq_data { - char *chip; /* GPIO chip label (GPIOINT) or PMIC ACPI path (PMIC) */ - enum x86_acpi_irq_type type; - enum irq_domain_bus_token domain; - int index; - int trigger; /* ACPI_EDGE_SENSITIVE / ACPI_LEVEL_SENSITIVE */ - int polarity; /* ACPI_ACTIVE_HIGH / ACPI_ACTIVE_LOW / ACPI_ACTIVE_BOTH */ -}; - -static int gpiochip_find_match_label(struct gpio_chip *gc, void *data) -{ - return gc->label && !strcmp(gc->label, data); -} - -static int x86_android_tablet_get_gpiod(char *label, int pin, struct gpio_desc **desc) -{ - struct gpio_desc *gpiod; - struct gpio_chip *chip; - - chip = gpiochip_find(label, gpiochip_find_match_label); - if (!chip) { - pr_err("error cannot find GPIO chip %s\n", label); - return -ENODEV; - } - - gpiod = gpiochip_get_desc(chip, pin); - if (IS_ERR(gpiod)) { - pr_err("error %ld getting GPIO %s %d\n", PTR_ERR(gpiod), label, pin); - return PTR_ERR(gpiod); - } - - *desc = gpiod; - return 0; -} - -static int x86_acpi_irq_helper_get(const struct x86_acpi_irq_data *data) -{ - struct irq_fwspec fwspec = { }; - struct irq_domain *domain; - struct acpi_device *adev; - struct gpio_desc *gpiod; - unsigned int irq_type; - acpi_handle handle; - acpi_status status; - int irq, ret; - - switch (data->type) { - case X86_ACPI_IRQ_TYPE_APIC: - /* - * The DSDT may already reference the GSI in a device skipped by - * acpi_quirk_skip_i2c_client_enumeration(). Unregister the GSI - * to avoid EBUSY errors in this case. - */ - acpi_unregister_gsi(data->index); - irq = acpi_register_gsi(NULL, data->index, data->trigger, data->polarity); - if (irq < 0) - pr_err("error %d getting APIC IRQ %d\n", irq, data->index); - - return irq; - case X86_ACPI_IRQ_TYPE_GPIOINT: - /* Like acpi_dev_gpio_irq_get(), but without parsing ACPI resources */ - ret = x86_android_tablet_get_gpiod(data->chip, data->index, &gpiod); - if (ret) - return ret; - - irq = gpiod_to_irq(gpiod); - if (irq < 0) { - pr_err("error %d getting IRQ %s %d\n", irq, data->chip, data->index); - return irq; - } - - irq_type = acpi_dev_get_irq_type(data->trigger, data->polarity); - if (irq_type != IRQ_TYPE_NONE && irq_type != irq_get_trigger_type(irq)) - irq_set_irq_type(irq, irq_type); - - return irq; - case X86_ACPI_IRQ_TYPE_PMIC: - status = acpi_get_handle(NULL, data->chip, &handle); - if (ACPI_FAILURE(status)) { - pr_err("error could not get %s handle\n", data->chip); - return -ENODEV; - } - - adev = acpi_fetch_acpi_dev(handle); - if (!adev) { - pr_err("error could not get %s adev\n", data->chip); - return -ENODEV; - } - - fwspec.fwnode = acpi_fwnode_handle(adev); - domain = irq_find_matching_fwspec(&fwspec, data->domain); - if (!domain) { - pr_err("error could not find IRQ domain for %s\n", data->chip); - return -ENODEV; - } - - return irq_create_mapping(domain, data->index); - default: - return 0; - } -} - -struct x86_i2c_client_info { - struct i2c_board_info board_info; - char *adapter_path; - struct x86_acpi_irq_data irq_data; -}; - -struct x86_serdev_info { - const char *ctrl_hid; - const char *ctrl_uid; - const char *ctrl_devname; - /* - * ATM the serdev core only supports of or ACPI matching; and sofar all - * Android x86 tablets DSDTs have usable serdev nodes, but sometimes - * under the wrong controller. So we just tie the existing serdev ACPI - * node to the right controller. - */ - const char *serdev_hid; -}; - -struct x86_dev_info { - char *invalid_aei_gpiochip; - const char * const *modules; - const struct software_node *bat_swnode; - struct gpiod_lookup_table * const *gpiod_lookup_tables; - const struct x86_i2c_client_info *i2c_client_info; - const struct platform_device_info *pdev_info; - const struct x86_serdev_info *serdev_info; - int i2c_client_count; - int pdev_count; - int serdev_count; - int (*init)(void); - void (*exit)(void); -}; - -/* Generic / shared charger / battery settings */ -static const char * const tusb1211_chg_det_psy[] = { "tusb1211-charger-detect" }; -static const char * const bq24190_psy[] = { "bq24190-charger" }; -static const char * const bq25890_psy[] = { "bq25890-charger-0" }; - -static const struct property_entry fg_bq24190_supply_props[] = { - PROPERTY_ENTRY_STRING_ARRAY("supplied-from", bq24190_psy), - { } -}; - -static const struct software_node fg_bq24190_supply_node = { - .properties = fg_bq24190_supply_props, -}; - -static const struct property_entry fg_bq25890_supply_props[] = { - PROPERTY_ENTRY_STRING_ARRAY("supplied-from", bq25890_psy), - { } -}; - -static const struct software_node fg_bq25890_supply_node = { - .properties = fg_bq25890_supply_props, -}; - -/* LiPo HighVoltage (max 4.35V) settings used by most devs with a HV bat. */ -static const struct property_entry generic_lipo_hv_4v35_battery_props[] = { - PROPERTY_ENTRY_STRING("compatible", "simple-battery"), - PROPERTY_ENTRY_STRING("device-chemistry", "lithium-ion"), - PROPERTY_ENTRY_U32("precharge-current-microamp", 256000), - PROPERTY_ENTRY_U32("charge-term-current-microamp", 128000), - PROPERTY_ENTRY_U32("constant-charge-current-max-microamp", 1856000), - PROPERTY_ENTRY_U32("constant-charge-voltage-max-microvolt", 4352000), - PROPERTY_ENTRY_U32("factory-internal-resistance-micro-ohms", 150000), - { } -}; - -static const struct software_node generic_lipo_hv_4v35_battery_node = { - .properties = generic_lipo_hv_4v35_battery_props, -}; - -/* For enabling the bq24190 5V boost based on id-pin */ -static struct regulator_consumer_supply intel_int3496_consumer = { - .supply = "vbus", - .dev_name = "intel-int3496", -}; - -static const struct regulator_init_data bq24190_vbus_init_data = { - .constraints = { - .name = "bq24190_vbus", - .valid_ops_mask = REGULATOR_CHANGE_STATUS, - }, - .consumer_supplies = &intel_int3496_consumer, - .num_consumer_supplies = 1, -}; - -static struct bq24190_platform_data bq24190_pdata = { - .regulator_init_data = &bq24190_vbus_init_data, -}; - -static const char * const bq24190_modules[] __initconst = { - "intel_crystal_cove_charger", /* For the bq24190 IRQ */ - "bq24190_charger", /* For the Vbus regulator for intel-int3496 */ - NULL -}; - -/* Generic pdevs array and gpio-lookups for micro USB ID pin handling */ -static const struct platform_device_info int3496_pdevs[] __initconst = { - { - /* For micro USB ID pin handling */ - .name = "intel-int3496", - .id = PLATFORM_DEVID_NONE, - }, -}; - -static struct gpiod_lookup_table int3496_gpo2_pin22_gpios = { - .dev_id = "intel-int3496", - .table = { - GPIO_LOOKUP("INT33FC:02", 22, "id", GPIO_ACTIVE_HIGH), - { } - }, -}; - -static struct gpiod_lookup_table int3496_reference_gpios = { - .dev_id = "intel-int3496", - .table = { - GPIO_LOOKUP("INT33FC:01", 15, "vbus", GPIO_ACTIVE_HIGH), - GPIO_LOOKUP("INT33FC:02", 1, "mux", GPIO_ACTIVE_HIGH), - GPIO_LOOKUP("INT33FC:02", 18, "id", GPIO_ACTIVE_HIGH), - { } - }, -}; - -/* Acer Iconia One 7 B1-750 has an Android factory img with everything hardcoded */ -static const char * const acer_b1_750_mount_matrix[] = { - "-1", "0", "0", - "0", "1", "0", - "0", "0", "1" -}; - -static const struct property_entry acer_b1_750_bma250e_props[] = { - PROPERTY_ENTRY_STRING_ARRAY("mount-matrix", acer_b1_750_mount_matrix), - { } -}; - -static const struct software_node acer_b1_750_bma250e_node = { - .properties = acer_b1_750_bma250e_props, -}; - -static const struct x86_i2c_client_info acer_b1_750_i2c_clients[] __initconst = { - { - /* Novatek NVT-ts touchscreen */ - .board_info = { - .type = "NVT-ts", - .addr = 0x34, - .dev_name = "NVT-ts", - }, - .adapter_path = "\\_SB_.I2C4", - .irq_data = { - .type = X86_ACPI_IRQ_TYPE_GPIOINT, - .chip = "INT33FC:02", - .index = 3, - .trigger = ACPI_EDGE_SENSITIVE, - .polarity = ACPI_ACTIVE_LOW, - }, - }, { - /* BMA250E accelerometer */ - .board_info = { - .type = "bma250e", - .addr = 0x18, - .swnode = &acer_b1_750_bma250e_node, - }, - .adapter_path = "\\_SB_.I2C3", - .irq_data = { - .type = X86_ACPI_IRQ_TYPE_GPIOINT, - .chip = "INT33FC:02", - .index = 25, - .trigger = ACPI_LEVEL_SENSITIVE, - .polarity = ACPI_ACTIVE_HIGH, - }, - }, -}; - -static struct gpiod_lookup_table acer_b1_750_goodix_gpios = { - .dev_id = "i2c-NVT-ts", - .table = { - GPIO_LOOKUP("INT33FC:01", 26, "reset", GPIO_ACTIVE_LOW), - { } - }, -}; - -static struct gpiod_lookup_table * const acer_b1_750_gpios[] = { - &acer_b1_750_goodix_gpios, - &int3496_reference_gpios, - NULL -}; - -static const struct x86_dev_info acer_b1_750_info __initconst = { - .i2c_client_info = acer_b1_750_i2c_clients, - .i2c_client_count = ARRAY_SIZE(acer_b1_750_i2c_clients), - .pdev_info = int3496_pdevs, - .pdev_count = ARRAY_SIZE(int3496_pdevs), - .gpiod_lookup_tables = acer_b1_750_gpios, -}; - -/* - * Advantech MICA-071 - * This is a standard Windows tablet, but it has an extra "quick launch" button - * which is not described in the ACPI tables in anyway. - * Use the x86-android-tablets infra to create a gpio-button device for this. - */ -static struct gpio_keys_button advantech_mica_071_button = { - .code = KEY_PROG1, - /* .gpio gets filled in by advantech_mica_071_init() */ - .active_low = true, - .desc = "prog1_key", - .type = EV_KEY, - .wakeup = false, - .debounce_interval = 50, -}; - -static const struct gpio_keys_platform_data advantech_mica_071_button_pdata __initconst = { - .buttons = &advantech_mica_071_button, - .nbuttons = 1, - .name = "prog1_key", -}; - -static const struct platform_device_info advantech_mica_071_pdevs[] __initconst = { - { - .name = "gpio-keys", - .id = PLATFORM_DEVID_AUTO, - .data = &advantech_mica_071_button_pdata, - .size_data = sizeof(advantech_mica_071_button_pdata), - }, -}; - -static int __init advantech_mica_071_init(void) -{ - struct gpio_desc *gpiod; - int ret; - - ret = x86_android_tablet_get_gpiod("INT33FC:00", 2, &gpiod); - if (ret < 0) - return ret; - advantech_mica_071_button.gpio = desc_to_gpio(gpiod); - - return 0; -} - -static const struct x86_dev_info advantech_mica_071_info __initconst = { - .pdev_info = advantech_mica_071_pdevs, - .pdev_count = ARRAY_SIZE(advantech_mica_071_pdevs), - .init = advantech_mica_071_init, -}; - -/* Asus ME176C and TF103C tablets shared data */ -static struct gpio_keys_button asus_me176c_tf103c_lid = { - .code = SW_LID, - /* .gpio gets filled in by asus_me176c_tf103c_init() */ - .active_low = true, - .desc = "lid_sw", - .type = EV_SW, - .wakeup = true, - .debounce_interval = 50, -}; - -static const struct gpio_keys_platform_data asus_me176c_tf103c_lid_pdata __initconst = { - .buttons = &asus_me176c_tf103c_lid, - .nbuttons = 1, - .name = "lid_sw", -}; - -static const struct platform_device_info asus_me176c_tf103c_pdevs[] __initconst = { - { - .name = "gpio-keys", - .id = PLATFORM_DEVID_AUTO, - .data = &asus_me176c_tf103c_lid_pdata, - .size_data = sizeof(asus_me176c_tf103c_lid_pdata), - }, - { - /* For micro USB ID pin handling */ - .name = "intel-int3496", - .id = PLATFORM_DEVID_NONE, - }, -}; - -static int __init asus_me176c_tf103c_init(void) -{ - struct gpio_desc *gpiod; - int ret; - - ret = x86_android_tablet_get_gpiod("INT33FC:02", 12, &gpiod); - if (ret < 0) - return ret; - asus_me176c_tf103c_lid.gpio = desc_to_gpio(gpiod); - - return 0; -} - - -/* Asus ME176C tablets have an Android factory img with everything hardcoded */ -static const char * const asus_me176c_accel_mount_matrix[] = { - "-1", "0", "0", - "0", "1", "0", - "0", "0", "1" -}; - -static const struct property_entry asus_me176c_accel_props[] = { - PROPERTY_ENTRY_STRING_ARRAY("mount-matrix", asus_me176c_accel_mount_matrix), - { } -}; - -static const struct software_node asus_me176c_accel_node = { - .properties = asus_me176c_accel_props, -}; - -static const struct property_entry asus_me176c_bq24190_props[] = { - PROPERTY_ENTRY_STRING_ARRAY("supplied-from", tusb1211_chg_det_psy), - PROPERTY_ENTRY_REF("monitored-battery", &generic_lipo_hv_4v35_battery_node), - PROPERTY_ENTRY_U32("ti,system-minimum-microvolt", 3600000), - PROPERTY_ENTRY_BOOL("omit-battery-class"), - PROPERTY_ENTRY_BOOL("disable-reset"), - { } -}; - -static const struct software_node asus_me176c_bq24190_node = { - .properties = asus_me176c_bq24190_props, -}; - -static const struct property_entry asus_me176c_ug3105_props[] = { - PROPERTY_ENTRY_STRING_ARRAY("supplied-from", bq24190_psy), - PROPERTY_ENTRY_REF("monitored-battery", &generic_lipo_hv_4v35_battery_node), - PROPERTY_ENTRY_U32("upisemi,rsns-microohm", 10000), - { } -}; - -static const struct software_node asus_me176c_ug3105_node = { - .properties = asus_me176c_ug3105_props, -}; - -static const struct x86_i2c_client_info asus_me176c_i2c_clients[] __initconst = { - { - /* bq24297 battery charger */ - .board_info = { - .type = "bq24190", - .addr = 0x6b, - .dev_name = "bq24297", - .swnode = &asus_me176c_bq24190_node, - .platform_data = &bq24190_pdata, - }, - .adapter_path = "\\_SB_.I2C1", - .irq_data = { - .type = X86_ACPI_IRQ_TYPE_PMIC, - .chip = "\\_SB_.I2C7.PMIC", - .domain = DOMAIN_BUS_WAKEUP, - .index = 0, - }, - }, { - /* ug3105 battery monitor */ - .board_info = { - .type = "ug3105", - .addr = 0x70, - .dev_name = "ug3105", - .swnode = &asus_me176c_ug3105_node, - }, - .adapter_path = "\\_SB_.I2C1", - }, { - /* ak09911 compass */ - .board_info = { - .type = "ak09911", - .addr = 0x0c, - .dev_name = "ak09911", - }, - .adapter_path = "\\_SB_.I2C5", - }, { - /* kxtj21009 accel */ - .board_info = { - .type = "kxtj21009", - .addr = 0x0f, - .dev_name = "kxtj21009", - .swnode = &asus_me176c_accel_node, - }, - .adapter_path = "\\_SB_.I2C5", - .irq_data = { - .type = X86_ACPI_IRQ_TYPE_APIC, - .index = 0x44, - .trigger = ACPI_EDGE_SENSITIVE, - .polarity = ACPI_ACTIVE_LOW, - }, - }, { - /* goodix touchscreen */ - .board_info = { - .type = "GDIX1001:00", - .addr = 0x14, - .dev_name = "goodix_ts", - }, - .adapter_path = "\\_SB_.I2C6", - .irq_data = { - .type = X86_ACPI_IRQ_TYPE_APIC, - .index = 0x45, - .trigger = ACPI_EDGE_SENSITIVE, - .polarity = ACPI_ACTIVE_LOW, - }, - }, -}; - -static const struct x86_serdev_info asus_me176c_serdevs[] __initconst = { - { - .ctrl_hid = "80860F0A", - .ctrl_uid = "2", - .ctrl_devname = "serial0", - .serdev_hid = "BCM2E3A", - }, -}; - -static struct gpiod_lookup_table asus_me176c_goodix_gpios = { - .dev_id = "i2c-goodix_ts", - .table = { - GPIO_LOOKUP("INT33FC:00", 60, "reset", GPIO_ACTIVE_HIGH), - GPIO_LOOKUP("INT33FC:02", 28, "irq", GPIO_ACTIVE_HIGH), - { } - }, -}; - -static struct gpiod_lookup_table * const asus_me176c_gpios[] = { - &int3496_gpo2_pin22_gpios, - &asus_me176c_goodix_gpios, - NULL -}; - -static const struct x86_dev_info asus_me176c_info __initconst = { - .i2c_client_info = asus_me176c_i2c_clients, - .i2c_client_count = ARRAY_SIZE(asus_me176c_i2c_clients), - .pdev_info = asus_me176c_tf103c_pdevs, - .pdev_count = ARRAY_SIZE(asus_me176c_tf103c_pdevs), - .serdev_info = asus_me176c_serdevs, - .serdev_count = ARRAY_SIZE(asus_me176c_serdevs), - .gpiod_lookup_tables = asus_me176c_gpios, - .bat_swnode = &generic_lipo_hv_4v35_battery_node, - .modules = bq24190_modules, - .invalid_aei_gpiochip = "INT33FC:02", - .init = asus_me176c_tf103c_init, -}; - -/* Asus TF103C tablets have an Android factory img with everything hardcoded */ -static const char * const asus_tf103c_accel_mount_matrix[] = { - "0", "-1", "0", - "-1", "0", "0", - "0", "0", "1" -}; - -static const struct property_entry asus_tf103c_accel_props[] = { - PROPERTY_ENTRY_STRING_ARRAY("mount-matrix", asus_tf103c_accel_mount_matrix), - { } -}; - -static const struct software_node asus_tf103c_accel_node = { - .properties = asus_tf103c_accel_props, -}; - -static const struct property_entry asus_tf103c_touchscreen_props[] = { - PROPERTY_ENTRY_STRING("compatible", "atmel,atmel_mxt_ts"), - { } -}; - -static const struct software_node asus_tf103c_touchscreen_node = { - .properties = asus_tf103c_touchscreen_props, -}; - -static const struct property_entry asus_tf103c_battery_props[] = { - PROPERTY_ENTRY_STRING("compatible", "simple-battery"), - PROPERTY_ENTRY_STRING("device-chemistry", "lithium-ion-polymer"), - PROPERTY_ENTRY_U32("precharge-current-microamp", 256000), - PROPERTY_ENTRY_U32("charge-term-current-microamp", 128000), - PROPERTY_ENTRY_U32("constant-charge-current-max-microamp", 2048000), - PROPERTY_ENTRY_U32("constant-charge-voltage-max-microvolt", 4208000), - PROPERTY_ENTRY_U32("factory-internal-resistance-micro-ohms", 150000), - { } -}; - -static const struct software_node asus_tf103c_battery_node = { - .properties = asus_tf103c_battery_props, -}; - -static const struct property_entry asus_tf103c_bq24190_props[] = { - PROPERTY_ENTRY_STRING_ARRAY("supplied-from", tusb1211_chg_det_psy), - PROPERTY_ENTRY_REF("monitored-battery", &asus_tf103c_battery_node), - PROPERTY_ENTRY_U32("ti,system-minimum-microvolt", 3600000), - PROPERTY_ENTRY_BOOL("omit-battery-class"), - PROPERTY_ENTRY_BOOL("disable-reset"), - { } -}; - -static const struct software_node asus_tf103c_bq24190_node = { - .properties = asus_tf103c_bq24190_props, -}; - -static const struct property_entry asus_tf103c_ug3105_props[] = { - PROPERTY_ENTRY_STRING_ARRAY("supplied-from", bq24190_psy), - PROPERTY_ENTRY_REF("monitored-battery", &asus_tf103c_battery_node), - PROPERTY_ENTRY_U32("upisemi,rsns-microohm", 5000), - { } -}; - -static const struct software_node asus_tf103c_ug3105_node = { - .properties = asus_tf103c_ug3105_props, -}; - -static const struct x86_i2c_client_info asus_tf103c_i2c_clients[] __initconst = { - { - /* bq24297 battery charger */ - .board_info = { - .type = "bq24190", - .addr = 0x6b, - .dev_name = "bq24297", - .swnode = &asus_tf103c_bq24190_node, - .platform_data = &bq24190_pdata, - }, - .adapter_path = "\\_SB_.I2C1", - .irq_data = { - .type = X86_ACPI_IRQ_TYPE_PMIC, - .chip = "\\_SB_.I2C7.PMIC", - .domain = DOMAIN_BUS_WAKEUP, - .index = 0, - }, - }, { - /* ug3105 battery monitor */ - .board_info = { - .type = "ug3105", - .addr = 0x70, - .dev_name = "ug3105", - .swnode = &asus_tf103c_ug3105_node, - }, - .adapter_path = "\\_SB_.I2C1", - }, { - /* ak09911 compass */ - .board_info = { - .type = "ak09911", - .addr = 0x0c, - .dev_name = "ak09911", - }, - .adapter_path = "\\_SB_.I2C5", - }, { - /* kxtj21009 accel */ - .board_info = { - .type = "kxtj21009", - .addr = 0x0f, - .dev_name = "kxtj21009", - .swnode = &asus_tf103c_accel_node, - }, - .adapter_path = "\\_SB_.I2C5", - }, { - /* atmel touchscreen */ - .board_info = { - .type = "atmel_mxt_ts", - .addr = 0x4a, - .dev_name = "atmel_mxt_ts", - .swnode = &asus_tf103c_touchscreen_node, - }, - .adapter_path = "\\_SB_.I2C6", - .irq_data = { - .type = X86_ACPI_IRQ_TYPE_GPIOINT, - .chip = "INT33FC:02", - .index = 28, - .trigger = ACPI_EDGE_SENSITIVE, - .polarity = ACPI_ACTIVE_LOW, - }, - }, -}; - -static struct gpiod_lookup_table * const asus_tf103c_gpios[] = { - &int3496_gpo2_pin22_gpios, - NULL -}; - -static const struct x86_dev_info asus_tf103c_info __initconst = { - .i2c_client_info = asus_tf103c_i2c_clients, - .i2c_client_count = ARRAY_SIZE(asus_tf103c_i2c_clients), - .pdev_info = asus_me176c_tf103c_pdevs, - .pdev_count = ARRAY_SIZE(asus_me176c_tf103c_pdevs), - .gpiod_lookup_tables = asus_tf103c_gpios, - .bat_swnode = &asus_tf103c_battery_node, - .modules = bq24190_modules, - .invalid_aei_gpiochip = "INT33FC:02", - .init = asus_me176c_tf103c_init, -}; - -/* - * When booted with the BIOS set to Android mode the Chuwi Hi8 (CWI509) DSDT - * contains a whole bunch of bogus ACPI I2C devices and is missing entries - * for the touchscreen and the accelerometer. - */ -static const struct property_entry chuwi_hi8_gsl1680_props[] = { - PROPERTY_ENTRY_U32("touchscreen-size-x", 1665), - PROPERTY_ENTRY_U32("touchscreen-size-y", 1140), - PROPERTY_ENTRY_BOOL("touchscreen-swapped-x-y"), - PROPERTY_ENTRY_BOOL("silead,home-button"), - PROPERTY_ENTRY_STRING("firmware-name", "gsl1680-chuwi-hi8.fw"), - { } -}; - -static const struct software_node chuwi_hi8_gsl1680_node = { - .properties = chuwi_hi8_gsl1680_props, -}; - -static const char * const chuwi_hi8_mount_matrix[] = { - "1", "0", "0", - "0", "-1", "0", - "0", "0", "1" -}; - -static const struct property_entry chuwi_hi8_bma250e_props[] = { - PROPERTY_ENTRY_STRING_ARRAY("mount-matrix", chuwi_hi8_mount_matrix), - { } -}; - -static const struct software_node chuwi_hi8_bma250e_node = { - .properties = chuwi_hi8_bma250e_props, -}; - -static const struct x86_i2c_client_info chuwi_hi8_i2c_clients[] __initconst = { - { - /* Silead touchscreen */ - .board_info = { - .type = "gsl1680", - .addr = 0x40, - .swnode = &chuwi_hi8_gsl1680_node, - }, - .adapter_path = "\\_SB_.I2C4", - .irq_data = { - .type = X86_ACPI_IRQ_TYPE_APIC, - .index = 0x44, - .trigger = ACPI_EDGE_SENSITIVE, - .polarity = ACPI_ACTIVE_HIGH, - }, - }, { - /* BMA250E accelerometer */ - .board_info = { - .type = "bma250e", - .addr = 0x18, - .swnode = &chuwi_hi8_bma250e_node, - }, - .adapter_path = "\\_SB_.I2C3", - .irq_data = { - .type = X86_ACPI_IRQ_TYPE_GPIOINT, - .chip = "INT33FC:02", - .index = 23, - .trigger = ACPI_LEVEL_SENSITIVE, - .polarity = ACPI_ACTIVE_HIGH, - }, - }, -}; - -static int __init chuwi_hi8_init(void) -{ - /* - * Avoid the acpi_unregister_gsi() call in x86_acpi_irq_helper_get() - * breaking the touchscreen + logging various errors when the Windows - * BIOS is used. - */ - if (acpi_dev_present("MSSL0001", NULL, 1)) - return -ENODEV; - - return 0; -} - -static const struct x86_dev_info chuwi_hi8_info __initconst = { - .i2c_client_info = chuwi_hi8_i2c_clients, - .i2c_client_count = ARRAY_SIZE(chuwi_hi8_i2c_clients), - .init = chuwi_hi8_init, -}; - -#define CZC_EC_EXTRA_PORT 0x68 -#define CZC_EC_ANDROID_KEYS 0x63 - -static int __init czc_p10t_init(void) -{ - /* - * The device boots up in "Windows 7" mode, when the home button sends a - * Windows specific key sequence (Left Meta + D) and the second button - * sends an unknown one while also toggling the Radio Kill Switch. - * This is a surprising behavior when the second button is labeled "Back". - * - * The vendor-supplied Android-x86 build switches the device to a "Android" - * mode by writing value 0x63 to the I/O port 0x68. This just seems to just - * set bit 6 on address 0x96 in the EC region; switching the bit directly - * seems to achieve the same result. It uses a "p10t_switcher" to do the - * job. It doesn't seem to be able to do anything else, and no other use - * of the port 0x68 is known. - * - * In the Android mode, the home button sends just a single scancode, - * which can be handled in Linux userspace more reasonably and the back - * button only sends a scancode without toggling the kill switch. - * The scancode can then be mapped either to Back or RF Kill functionality - * in userspace, depending on how the button is labeled on that particular - * model. - */ - outb(CZC_EC_ANDROID_KEYS, CZC_EC_EXTRA_PORT); - return 0; -} - -static const struct x86_dev_info czc_p10t __initconst = { - .init = czc_p10t_init, -}; - -/* Lenovo Yoga Book X90F / X91F / X91L need manual instantiation of the fg client */ -static const struct x86_i2c_client_info lenovo_yogabook_x9x_i2c_clients[] __initconst = { - { - /* BQ27542 fuel-gauge */ - .board_info = { - .type = "bq27542", - .addr = 0x55, - .dev_name = "bq27542", - .swnode = &fg_bq25890_supply_node, - }, - .adapter_path = "\\_SB_.PCI0.I2C1", - }, -}; - -static const struct x86_dev_info lenovo_yogabook_x9x_info __initconst = { - .i2c_client_info = lenovo_yogabook_x9x_i2c_clients, - .i2c_client_count = ARRAY_SIZE(lenovo_yogabook_x9x_i2c_clients), -}; - -/* Lenovo Yoga Tablet 2 1050F/L's Android factory img has everything hardcoded */ -static const struct property_entry lenovo_yoga_tab2_830_1050_bq24190_props[] = { - PROPERTY_ENTRY_STRING_ARRAY("supplied-from", tusb1211_chg_det_psy), - PROPERTY_ENTRY_REF("monitored-battery", &generic_lipo_hv_4v35_battery_node), - PROPERTY_ENTRY_BOOL("omit-battery-class"), - PROPERTY_ENTRY_BOOL("disable-reset"), - { } -}; - -static const struct software_node lenovo_yoga_tab2_830_1050_bq24190_node = { - .properties = lenovo_yoga_tab2_830_1050_bq24190_props, -}; - -/* This gets filled by lenovo_yoga_tab2_830_1050_init() */ -static struct rmi_device_platform_data lenovo_yoga_tab2_830_1050_rmi_pdata = { }; - -static struct lp855x_platform_data lenovo_yoga_tab2_830_1050_lp8557_pdata = { - .device_control = 0x86, - .initial_brightness = 128, -}; - -static const struct x86_i2c_client_info lenovo_yoga_tab2_830_1050_i2c_clients[] __initconst = { - { - /* bq24292i battery charger */ - .board_info = { - .type = "bq24190", - .addr = 0x6b, - .dev_name = "bq24292i", - .swnode = &lenovo_yoga_tab2_830_1050_bq24190_node, - .platform_data = &bq24190_pdata, - }, - .adapter_path = "\\_SB_.I2C1", - .irq_data = { - .type = X86_ACPI_IRQ_TYPE_GPIOINT, - .chip = "INT33FC:02", - .index = 2, - .trigger = ACPI_EDGE_SENSITIVE, - .polarity = ACPI_ACTIVE_HIGH, - }, - }, { - /* BQ27541 fuel-gauge */ - .board_info = { - .type = "bq27541", - .addr = 0x55, - .dev_name = "bq27541", - .swnode = &fg_bq24190_supply_node, - }, - .adapter_path = "\\_SB_.I2C1", - }, { - /* Synaptics RMI touchscreen */ - .board_info = { - .type = "rmi4_i2c", - .addr = 0x38, - .dev_name = "rmi4_i2c", - .platform_data = &lenovo_yoga_tab2_830_1050_rmi_pdata, - }, - .adapter_path = "\\_SB_.I2C6", - .irq_data = { - .type = X86_ACPI_IRQ_TYPE_APIC, - .index = 0x45, - .trigger = ACPI_EDGE_SENSITIVE, - .polarity = ACPI_ACTIVE_HIGH, - }, - }, { - /* LP8557 Backlight controller */ - .board_info = { - .type = "lp8557", - .addr = 0x2c, - .dev_name = "lp8557", - .platform_data = &lenovo_yoga_tab2_830_1050_lp8557_pdata, - }, - .adapter_path = "\\_SB_.I2C3", - }, -}; - -static struct gpiod_lookup_table lenovo_yoga_tab2_830_1050_int3496_gpios = { - .dev_id = "intel-int3496", - .table = { - GPIO_LOOKUP("INT33FC:02", 1, "mux", GPIO_ACTIVE_LOW), - GPIO_LOOKUP("INT33FC:02", 24, "id", GPIO_ACTIVE_HIGH), - { } - }, -}; - -#define LENOVO_YOGA_TAB2_830_1050_CODEC_NAME "spi-10WM5102:00" - -static struct gpiod_lookup_table lenovo_yoga_tab2_830_1050_codec_gpios = { - .dev_id = LENOVO_YOGA_TAB2_830_1050_CODEC_NAME, - .table = { - GPIO_LOOKUP("gpio_crystalcove", 3, "reset", GPIO_ACTIVE_HIGH), - GPIO_LOOKUP("INT33FC:01", 23, "wlf,ldoena", GPIO_ACTIVE_HIGH), - GPIO_LOOKUP("arizona", 2, "wlf,spkvdd-ena", GPIO_ACTIVE_HIGH), - GPIO_LOOKUP("arizona", 4, "wlf,micd-pol", GPIO_ACTIVE_LOW), - { } - }, -}; - -static struct gpiod_lookup_table * const lenovo_yoga_tab2_830_1050_gpios[] = { - &lenovo_yoga_tab2_830_1050_int3496_gpios, - &lenovo_yoga_tab2_830_1050_codec_gpios, - NULL -}; - -static int __init lenovo_yoga_tab2_830_1050_init(void); -static void lenovo_yoga_tab2_830_1050_exit(void); - -static struct x86_dev_info lenovo_yoga_tab2_830_1050_info __initdata = { - .i2c_client_info = lenovo_yoga_tab2_830_1050_i2c_clients, - /* i2c_client_count gets set by lenovo_yoga_tab2_830_1050_init() */ - .pdev_info = int3496_pdevs, - .pdev_count = ARRAY_SIZE(int3496_pdevs), - .gpiod_lookup_tables = lenovo_yoga_tab2_830_1050_gpios, - .bat_swnode = &generic_lipo_hv_4v35_battery_node, - .modules = bq24190_modules, - .invalid_aei_gpiochip = "INT33FC:02", - .init = lenovo_yoga_tab2_830_1050_init, - .exit = lenovo_yoga_tab2_830_1050_exit, -}; - -/* - * The Lenovo Yoga Tablet 2 830 and 1050 (8" vs 10") versions use the same - * mainboard, but they need some different treatment related to the display: - * 1. The 830 uses a portrait LCD panel with a landscape touchscreen, requiring - * the touchscreen driver to adjust the touch-coords to match the LCD. - * 2. Both use an TI LP8557 LED backlight controller. On the 1050 the LP8557's - * PWM input is connected to the PMIC's PWM output and everything works fine - * with the defaults programmed into the LP8557 by the BIOS. - * But on the 830 the LP8557's PWM input is connected to a PWM output coming - * from the LCD panel's controller. The Android code has a hack in the i915 - * driver to write the non-standard DSI reg 0x9f with the desired backlight - * level to set the duty-cycle of the LCD's PWM output. - * - * To avoid having to have a similar hack in the mainline kernel the LP8557 - * entry in lenovo_yoga_tab2_830_1050_i2c_clients instead just programs the - * LP8557 to directly set the level, ignoring the PWM input. This means that - * the LP8557 i2c_client should only be instantiated on the 830. - */ -static int __init lenovo_yoga_tab2_830_1050_init_display(void) -{ - struct gpio_desc *gpiod; - int ret; - - /* Use PMIC GPIO 10 bootstrap pin to differentiate 830 vs 1050 */ - ret = x86_android_tablet_get_gpiod("gpio_crystalcove", 10, &gpiod); - if (ret) - return ret; - - ret = gpiod_get_value_cansleep(gpiod); - if (ret) { - pr_info("detected Lenovo Yoga Tablet 2 1050F/L\n"); - lenovo_yoga_tab2_830_1050_info.i2c_client_count = - ARRAY_SIZE(lenovo_yoga_tab2_830_1050_i2c_clients) - 1; - } else { - pr_info("detected Lenovo Yoga Tablet 2 830F/L\n"); - lenovo_yoga_tab2_830_1050_rmi_pdata.sensor_pdata.axis_align.swap_axes = true; - lenovo_yoga_tab2_830_1050_rmi_pdata.sensor_pdata.axis_align.flip_y = true; - lenovo_yoga_tab2_830_1050_info.i2c_client_count = - ARRAY_SIZE(lenovo_yoga_tab2_830_1050_i2c_clients); - } - - return 0; -} - -/* SUS (INT33FC:02) pin 6 needs to be configured as pmu_clk for the audio codec */ -static const struct pinctrl_map lenovo_yoga_tab2_830_1050_codec_pinctrl_map = - PIN_MAP_MUX_GROUP(LENOVO_YOGA_TAB2_830_1050_CODEC_NAME, "codec_32khz_clk", - "INT33FC:02", "pmu_clk2_grp", "pmu_clk"); - -static struct pinctrl *lenovo_yoga_tab2_830_1050_codec_pinctrl; -static struct sys_off_handler *lenovo_yoga_tab2_830_1050_sys_off_handler; - -static int __init lenovo_yoga_tab2_830_1050_init_codec(void) -{ - struct device *codec_dev; - struct pinctrl *pinctrl; - int ret; - - codec_dev = bus_find_device_by_name(&spi_bus_type, NULL, - LENOVO_YOGA_TAB2_830_1050_CODEC_NAME); - if (!codec_dev) { - pr_err("error cannot find %s device\n", LENOVO_YOGA_TAB2_830_1050_CODEC_NAME); - return -ENODEV; - } - - ret = pinctrl_register_mappings(&lenovo_yoga_tab2_830_1050_codec_pinctrl_map, 1); - if (ret) - goto err_put_device; - - pinctrl = pinctrl_get_select(codec_dev, "codec_32khz_clk"); - if (IS_ERR(pinctrl)) { - ret = dev_err_probe(codec_dev, PTR_ERR(pinctrl), "selecting codec_32khz_clk\n"); - goto err_unregister_mappings; - } - - /* We're done with the codec_dev now */ - put_device(codec_dev); - - lenovo_yoga_tab2_830_1050_codec_pinctrl = pinctrl; - return 0; - -err_unregister_mappings: - pinctrl_unregister_mappings(&lenovo_yoga_tab2_830_1050_codec_pinctrl_map); -err_put_device: - put_device(codec_dev); - return ret; -} - -/* - * These tablet's DSDT does not set acpi_gbl_reduced_hardware, so acpi_power_off - * gets used as pm_power_off handler. This causes "poweroff" on these tablets - * to hang hard. Requiring pressing the powerbutton for 30 seconds *twice* - * followed by a normal 3 second press to recover. Avoid this by doing an EFI - * poweroff instead. - */ -static int lenovo_yoga_tab2_830_1050_power_off(struct sys_off_data *data) -{ - efi.reset_system(EFI_RESET_SHUTDOWN, EFI_SUCCESS, 0, NULL); - - return NOTIFY_DONE; -} - -static int __init lenovo_yoga_tab2_830_1050_init(void) -{ - int ret; - - ret = lenovo_yoga_tab2_830_1050_init_display(); - if (ret) - return ret; - - ret = lenovo_yoga_tab2_830_1050_init_codec(); - if (ret) - return ret; - - /* SYS_OFF_PRIO_FIRMWARE + 1 so that it runs before acpi_power_off */ - lenovo_yoga_tab2_830_1050_sys_off_handler = - register_sys_off_handler(SYS_OFF_MODE_POWER_OFF, SYS_OFF_PRIO_FIRMWARE + 1, - lenovo_yoga_tab2_830_1050_power_off, NULL); - if (IS_ERR(lenovo_yoga_tab2_830_1050_sys_off_handler)) - return PTR_ERR(lenovo_yoga_tab2_830_1050_sys_off_handler); - - return 0; -} - -static void lenovo_yoga_tab2_830_1050_exit(void) -{ - unregister_sys_off_handler(lenovo_yoga_tab2_830_1050_sys_off_handler); - - if (lenovo_yoga_tab2_830_1050_codec_pinctrl) { - pinctrl_put(lenovo_yoga_tab2_830_1050_codec_pinctrl); - pinctrl_unregister_mappings(&lenovo_yoga_tab2_830_1050_codec_pinctrl_map); - } -} - -/* Lenovo Yoga Tab 3 Pro YT3-X90F */ - -/* - * There are 2 batteries, with 2 bq27500 fuel-gauges and 2 bq25892 chargers, - * "bq25890-charger-1" is instantiated from: drivers/i2c/busses/i2c-cht-wc.c. - */ -static const char * const lenovo_yt3_bq25892_0_suppliers[] = { "cht_wcove_pwrsrc" }; -static const char * const bq25890_1_psy[] = { "bq25890-charger-1" }; - -static const struct property_entry fg_bq25890_1_supply_props[] = { - PROPERTY_ENTRY_STRING_ARRAY("supplied-from", bq25890_1_psy), - { } -}; - -static const struct software_node fg_bq25890_1_supply_node = { - .properties = fg_bq25890_1_supply_props, -}; - -/* bq25892 charger settings for the flat lipo battery behind the screen */ -static const struct property_entry lenovo_yt3_bq25892_0_props[] = { - PROPERTY_ENTRY_STRING_ARRAY("supplied-from", lenovo_yt3_bq25892_0_suppliers), - PROPERTY_ENTRY_STRING("linux,power-supply-name", "bq25892-second-chrg"), - PROPERTY_ENTRY_U32("linux,iinlim-percentage", 40), - PROPERTY_ENTRY_BOOL("linux,skip-reset"), - /* Values taken from Android Factory Image */ - PROPERTY_ENTRY_U32("ti,charge-current", 2048000), - PROPERTY_ENTRY_U32("ti,battery-regulation-voltage", 4352000), - PROPERTY_ENTRY_U32("ti,termination-current", 128000), - PROPERTY_ENTRY_U32("ti,precharge-current", 128000), - PROPERTY_ENTRY_U32("ti,minimum-sys-voltage", 3700000), - PROPERTY_ENTRY_U32("ti,boost-voltage", 4998000), - PROPERTY_ENTRY_U32("ti,boost-max-current", 500000), - PROPERTY_ENTRY_BOOL("ti,use-ilim-pin"), - { } -}; - -static const struct software_node lenovo_yt3_bq25892_0_node = { - .properties = lenovo_yt3_bq25892_0_props, -}; - -static const struct x86_i2c_client_info lenovo_yt3_i2c_clients[] __initconst = { - { - /* bq27500 fuel-gauge for the flat lipo battery behind the screen */ - .board_info = { - .type = "bq27500", - .addr = 0x55, - .dev_name = "bq27500_0", - .swnode = &fg_bq25890_supply_node, - }, - .adapter_path = "\\_SB_.PCI0.I2C1", - }, { - /* bq25892 charger for the flat lipo battery behind the screen */ - .board_info = { - .type = "bq25892", - .addr = 0x6b, - .dev_name = "bq25892_0", - .swnode = &lenovo_yt3_bq25892_0_node, - }, - .adapter_path = "\\_SB_.PCI0.I2C1", - .irq_data = { - .type = X86_ACPI_IRQ_TYPE_GPIOINT, - .chip = "INT33FF:01", - .index = 5, - .trigger = ACPI_EDGE_SENSITIVE, - .polarity = ACPI_ACTIVE_LOW, - }, - }, { - /* bq27500 fuel-gauge for the round li-ion cells in the hinge */ - .board_info = { - .type = "bq27500", - .addr = 0x55, - .dev_name = "bq27500_1", - .swnode = &fg_bq25890_1_supply_node, - }, - .adapter_path = "\\_SB_.PCI0.I2C2", - } -}; - -static int __init lenovo_yt3_init(void) -{ - struct gpio_desc *gpiod; - int ret; - - /* - * The "bq25892_0" charger IC has its /CE (Charge-Enable) and OTG pins - * connected to GPIOs, rather then having them hardwired to the correct - * values as is normally done. - * - * The bq25890_charger driver controls these through I2C, but this only - * works if not overridden by the pins. Set these pins here: - * 1. Set /CE to 0 to allow charging. - * 2. Set OTG to 0 disable V5 boost output since the 5V boost output of - * the main "bq25892_1" charger is used when necessary. - */ - - /* /CE pin */ - ret = x86_android_tablet_get_gpiod("INT33FF:02", 22, &gpiod); - if (ret < 0) - return ret; - - /* - * The gpio_desc returned by x86_android_tablet_get_gpiod() is a "raw" - * gpio_desc, that is there is no way to pass lookup-flags like - * GPIO_ACTIVE_LOW. Set the GPIO to 0 here to enable charging since - * the /CE pin is active-low, but not marked as such in the gpio_desc. - */ - gpiod_set_value(gpiod, 0); - - /* OTG pin */ - ret = x86_android_tablet_get_gpiod("INT33FF:03", 19, &gpiod); - if (ret < 0) - return ret; - - gpiod_set_value(gpiod, 0); - - return 0; -} - -static const struct x86_dev_info lenovo_yt3_info __initconst = { - .i2c_client_info = lenovo_yt3_i2c_clients, - .i2c_client_count = ARRAY_SIZE(lenovo_yt3_i2c_clients), - .init = lenovo_yt3_init, -}; - -/* Medion Lifetab S10346 tablets have an Android factory img with everything hardcoded */ -static const char * const medion_lifetab_s10346_accel_mount_matrix[] = { - "0", "1", "0", - "1", "0", "0", - "0", "0", "1" -}; - -static const struct property_entry medion_lifetab_s10346_accel_props[] = { - PROPERTY_ENTRY_STRING_ARRAY("mount-matrix", medion_lifetab_s10346_accel_mount_matrix), - { } -}; - -static const struct software_node medion_lifetab_s10346_accel_node = { - .properties = medion_lifetab_s10346_accel_props, -}; - -/* Note the LCD panel is mounted upside down, this is correctly indicated in the VBT */ -static const struct property_entry medion_lifetab_s10346_touchscreen_props[] = { - PROPERTY_ENTRY_BOOL("touchscreen-inverted-x"), - PROPERTY_ENTRY_BOOL("touchscreen-swapped-x-y"), - { } -}; - -static const struct software_node medion_lifetab_s10346_touchscreen_node = { - .properties = medion_lifetab_s10346_touchscreen_props, -}; - -static const struct x86_i2c_client_info medion_lifetab_s10346_i2c_clients[] __initconst = { - { - /* kxtj21009 accel */ - .board_info = { - .type = "kxtj21009", - .addr = 0x0f, - .dev_name = "kxtj21009", - .swnode = &medion_lifetab_s10346_accel_node, - }, - .adapter_path = "\\_SB_.I2C3", - .irq_data = { - .type = X86_ACPI_IRQ_TYPE_GPIOINT, - .chip = "INT33FC:02", - .index = 23, - .trigger = ACPI_EDGE_SENSITIVE, - .polarity = ACPI_ACTIVE_HIGH, - }, - }, { - /* goodix touchscreen */ - .board_info = { - .type = "GDIX1001:00", - .addr = 0x14, - .dev_name = "goodix_ts", - .swnode = &medion_lifetab_s10346_touchscreen_node, - }, - .adapter_path = "\\_SB_.I2C4", - .irq_data = { - .type = X86_ACPI_IRQ_TYPE_APIC, - .index = 0x44, - .trigger = ACPI_EDGE_SENSITIVE, - .polarity = ACPI_ACTIVE_LOW, - }, - }, -}; - -static struct gpiod_lookup_table medion_lifetab_s10346_goodix_gpios = { - .dev_id = "i2c-goodix_ts", - .table = { - GPIO_LOOKUP("INT33FC:01", 26, "reset", GPIO_ACTIVE_HIGH), - GPIO_LOOKUP("INT33FC:02", 3, "irq", GPIO_ACTIVE_HIGH), - { } - }, -}; - -static struct gpiod_lookup_table * const medion_lifetab_s10346_gpios[] = { - &medion_lifetab_s10346_goodix_gpios, - NULL -}; - -static const struct x86_dev_info medion_lifetab_s10346_info __initconst = { - .i2c_client_info = medion_lifetab_s10346_i2c_clients, - .i2c_client_count = ARRAY_SIZE(medion_lifetab_s10346_i2c_clients), - .gpiod_lookup_tables = medion_lifetab_s10346_gpios, -}; - -/* Nextbook Ares 8 tablets have an Android factory img with everything hardcoded */ -static const char * const nextbook_ares8_accel_mount_matrix[] = { - "0", "-1", "0", - "-1", "0", "0", - "0", "0", "1" -}; - -static const struct property_entry nextbook_ares8_accel_props[] = { - PROPERTY_ENTRY_STRING_ARRAY("mount-matrix", nextbook_ares8_accel_mount_matrix), - { } -}; - -static const struct software_node nextbook_ares8_accel_node = { - .properties = nextbook_ares8_accel_props, -}; - -static const struct property_entry nextbook_ares8_touchscreen_props[] = { - PROPERTY_ENTRY_U32("touchscreen-size-x", 800), - PROPERTY_ENTRY_U32("touchscreen-size-y", 1280), - { } -}; - -static const struct software_node nextbook_ares8_touchscreen_node = { - .properties = nextbook_ares8_touchscreen_props, -}; - -static const struct x86_i2c_client_info nextbook_ares8_i2c_clients[] __initconst = { - { - /* Freescale MMA8653FC accel */ - .board_info = { - .type = "mma8653", - .addr = 0x1d, - .dev_name = "mma8653", - .swnode = &nextbook_ares8_accel_node, - }, - .adapter_path = "\\_SB_.I2C3", - }, { - /* FT5416DQ9 touchscreen controller */ - .board_info = { - .type = "edt-ft5x06", - .addr = 0x38, - .dev_name = "ft5416", - .swnode = &nextbook_ares8_touchscreen_node, - }, - .adapter_path = "\\_SB_.I2C4", - .irq_data = { - .type = X86_ACPI_IRQ_TYPE_GPIOINT, - .chip = "INT33FC:02", - .index = 3, - .trigger = ACPI_EDGE_SENSITIVE, - .polarity = ACPI_ACTIVE_LOW, - }, - }, -}; - -static struct gpiod_lookup_table * const nextbook_ares8_gpios[] = { - &int3496_reference_gpios, - NULL -}; - -static const struct x86_dev_info nextbook_ares8_info __initconst = { - .i2c_client_info = nextbook_ares8_i2c_clients, - .i2c_client_count = ARRAY_SIZE(nextbook_ares8_i2c_clients), - .pdev_info = int3496_pdevs, - .pdev_count = ARRAY_SIZE(int3496_pdevs), - .gpiod_lookup_tables = nextbook_ares8_gpios, - .invalid_aei_gpiochip = "INT33FC:02", -}; - -/* - * Whitelabel (sold as various brands) TM800A550L tablets. - * These tablet's DSDT contains a whole bunch of bogus ACPI I2C devices - * (removed through acpi_quirk_skip_i2c_client_enumeration()) and - * the touchscreen fwnode has the wrong GPIOs. - */ -static const char * const whitelabel_tm800a550l_accel_mount_matrix[] = { - "-1", "0", "0", - "0", "1", "0", - "0", "0", "1" -}; - -static const struct property_entry whitelabel_tm800a550l_accel_props[] = { - PROPERTY_ENTRY_STRING_ARRAY("mount-matrix", whitelabel_tm800a550l_accel_mount_matrix), - { } -}; - -static const struct software_node whitelabel_tm800a550l_accel_node = { - .properties = whitelabel_tm800a550l_accel_props, -}; - -static const struct property_entry whitelabel_tm800a550l_goodix_props[] = { - PROPERTY_ENTRY_STRING("firmware-name", "gt912-tm800a550l.fw"), - PROPERTY_ENTRY_STRING("goodix,config-name", "gt912-tm800a550l.cfg"), - PROPERTY_ENTRY_U32("goodix,main-clk", 54), - { } -}; - -static const struct software_node whitelabel_tm800a550l_goodix_node = { - .properties = whitelabel_tm800a550l_goodix_props, -}; - -static const struct x86_i2c_client_info whitelabel_tm800a550l_i2c_clients[] __initconst = { - { - /* goodix touchscreen */ - .board_info = { - .type = "GDIX1001:00", - .addr = 0x14, - .dev_name = "goodix_ts", - .swnode = &whitelabel_tm800a550l_goodix_node, - }, - .adapter_path = "\\_SB_.I2C2", - .irq_data = { - .type = X86_ACPI_IRQ_TYPE_APIC, - .index = 0x44, - .trigger = ACPI_EDGE_SENSITIVE, - .polarity = ACPI_ACTIVE_HIGH, - }, - }, { - /* kxcj91008 accel */ - .board_info = { - .type = "kxcj91008", - .addr = 0x0f, - .dev_name = "kxcj91008", - .swnode = &whitelabel_tm800a550l_accel_node, - }, - .adapter_path = "\\_SB_.I2C3", - }, -}; - -static struct gpiod_lookup_table whitelabel_tm800a550l_goodix_gpios = { - .dev_id = "i2c-goodix_ts", - .table = { - GPIO_LOOKUP("INT33FC:01", 26, "reset", GPIO_ACTIVE_HIGH), - GPIO_LOOKUP("INT33FC:02", 3, "irq", GPIO_ACTIVE_HIGH), - { } - }, -}; - -static struct gpiod_lookup_table * const whitelabel_tm800a550l_gpios[] = { - &whitelabel_tm800a550l_goodix_gpios, - NULL -}; - -static const struct x86_dev_info whitelabel_tm800a550l_info __initconst = { - .i2c_client_info = whitelabel_tm800a550l_i2c_clients, - .i2c_client_count = ARRAY_SIZE(whitelabel_tm800a550l_i2c_clients), - .gpiod_lookup_tables = whitelabel_tm800a550l_gpios, -}; - -/* - * If the EFI bootloader is not Xiaomi's own signed Android loader, then the - * Xiaomi Mi Pad 2 X86 tablet sets OSID in the DSDT to 1 (Windows), causing - * a bunch of devices to be hidden. - * - * This takes care of instantiating the hidden devices manually. - */ -static const struct x86_i2c_client_info xiaomi_mipad2_i2c_clients[] __initconst = { - { - /* BQ27520 fuel-gauge */ - .board_info = { - .type = "bq27520", - .addr = 0x55, - .dev_name = "bq27520", - .swnode = &fg_bq25890_supply_node, - }, - .adapter_path = "\\_SB_.PCI0.I2C1", - }, { - /* KTD2026 RGB notification LED controller */ - .board_info = { - .type = "ktd2026", - .addr = 0x30, - .dev_name = "ktd2026", - }, - .adapter_path = "\\_SB_.PCI0.I2C3", - }, -}; - -static const struct x86_dev_info xiaomi_mipad2_info __initconst = { - .i2c_client_info = xiaomi_mipad2_i2c_clients, - .i2c_client_count = ARRAY_SIZE(xiaomi_mipad2_i2c_clients), -}; - -static const struct dmi_system_id x86_android_tablet_ids[] __initconst = { - { - /* Acer Iconia One 7 B1-750 */ - .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "Insyde"), - DMI_MATCH(DMI_PRODUCT_NAME, "VESPA2"), - }, - .driver_data = (void *)&acer_b1_750_info, - }, - { - /* Advantech MICA-071 */ - .matches = { - DMI_EXACT_MATCH(DMI_SYS_VENDOR, "Advantech"), - DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "MICA-071"), - }, - .driver_data = (void *)&advantech_mica_071_info, - }, - { - /* Asus MeMO Pad 7 ME176C */ - .matches = { - DMI_EXACT_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."), - DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "ME176C"), - }, - .driver_data = (void *)&asus_me176c_info, - }, - { - /* Asus TF103C */ - .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."), - DMI_MATCH(DMI_PRODUCT_NAME, "TF103C"), - }, - .driver_data = (void *)&asus_tf103c_info, - }, - { - /* Chuwi Hi8 (CWI509) */ - .matches = { - DMI_MATCH(DMI_BOARD_VENDOR, "Hampoo"), - DMI_MATCH(DMI_BOARD_NAME, "BYT-PA03C"), - DMI_MATCH(DMI_SYS_VENDOR, "ilife"), - DMI_MATCH(DMI_PRODUCT_NAME, "S806"), - }, - .driver_data = (void *)&chuwi_hi8_info, - }, - { - /* CZC P10T */ - .ident = "CZC ODEON TPC-10 (\"P10T\")", - .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "CZC"), - DMI_MATCH(DMI_PRODUCT_NAME, "ODEON*TPC-10"), - }, - .driver_data = (void *)&czc_p10t, - }, - { - /* CZC P10T variant */ - .ident = "ViewSonic ViewPad 10", - .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "ViewSonic"), - DMI_MATCH(DMI_PRODUCT_NAME, "VPAD10"), - }, - .driver_data = (void *)&czc_p10t, - }, - { - /* Lenovo Yoga Book X90F / X91F / X91L */ - .matches = { - /* Non exact match to match all versions */ - DMI_MATCH(DMI_PRODUCT_NAME, "Lenovo YB1-X9"), - }, - .driver_data = (void *)&lenovo_yogabook_x9x_info, - }, - { - /* - * Lenovo Yoga Tablet 2 830F/L or 1050F/L (The 8" and 10" - * Lenovo Yoga Tablet 2 use the same mainboard) - */ - .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "Intel Corp."), - DMI_MATCH(DMI_PRODUCT_NAME, "VALLEYVIEW C0 PLATFORM"), - DMI_MATCH(DMI_BOARD_NAME, "BYT-T FFD8"), - /* Partial match on beginning of BIOS version */ - DMI_MATCH(DMI_BIOS_VERSION, "BLADE_21"), - }, - .driver_data = (void *)&lenovo_yoga_tab2_830_1050_info, - }, - { - /* Lenovo Yoga Tab 3 Pro YT3-X90F */ - .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "Intel Corporation"), - DMI_MATCH(DMI_PRODUCT_NAME, "CHERRYVIEW D1 PLATFORM"), - DMI_MATCH(DMI_PRODUCT_VERSION, "Blade3-10A-001"), - }, - .driver_data = (void *)&lenovo_yt3_info, - }, - { - /* Medion Lifetab S10346 */ - .matches = { - DMI_MATCH(DMI_BOARD_VENDOR, "AMI Corporation"), - DMI_MATCH(DMI_BOARD_NAME, "Aptio CRB"), - /* Above strings are much too generic, also match on BIOS date */ - DMI_MATCH(DMI_BIOS_DATE, "10/22/2015"), - }, - .driver_data = (void *)&medion_lifetab_s10346_info, - }, - { - /* Nextbook Ares 8 */ - .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "Insyde"), - DMI_MATCH(DMI_PRODUCT_NAME, "M890BAP"), - }, - .driver_data = (void *)&nextbook_ares8_info, - }, - { - /* Whitelabel (sold as various brands) TM800A550L */ - .matches = { - DMI_MATCH(DMI_BOARD_VENDOR, "AMI Corporation"), - DMI_MATCH(DMI_BOARD_NAME, "Aptio CRB"), - /* Above strings are too generic, also match on BIOS version */ - DMI_MATCH(DMI_BIOS_VERSION, "ZY-8-BI-PX4S70VTR400-X423B-005-D"), - }, - .driver_data = (void *)&whitelabel_tm800a550l_info, - }, - { - /* Xiaomi Mi Pad 2 */ - .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "Xiaomi Inc"), - DMI_MATCH(DMI_PRODUCT_NAME, "Mipad2"), - }, - .driver_data = (void *)&xiaomi_mipad2_info, - }, - { } -}; -MODULE_DEVICE_TABLE(dmi, x86_android_tablet_ids); - -static int i2c_client_count; -static int pdev_count; -static int serdev_count; -static struct i2c_client **i2c_clients; -static struct platform_device **pdevs; -static struct serdev_device **serdevs; -static struct gpiod_lookup_table * const *gpiod_lookup_tables; -static const struct software_node *bat_swnode; -static void (*exit_handler)(void); - -static __init int x86_instantiate_i2c_client(const struct x86_dev_info *dev_info, - int idx) -{ - const struct x86_i2c_client_info *client_info = &dev_info->i2c_client_info[idx]; - struct i2c_board_info board_info = client_info->board_info; - struct i2c_adapter *adap; - acpi_handle handle; - acpi_status status; - - board_info.irq = x86_acpi_irq_helper_get(&client_info->irq_data); - if (board_info.irq < 0) - return board_info.irq; - - status = acpi_get_handle(NULL, client_info->adapter_path, &handle); - if (ACPI_FAILURE(status)) { - pr_err("Error could not get %s handle\n", client_info->adapter_path); - return -ENODEV; - } - - adap = i2c_acpi_find_adapter_by_handle(handle); - if (!adap) { - pr_err("error could not get %s adapter\n", client_info->adapter_path); - return -ENODEV; - } - - i2c_clients[idx] = i2c_new_client_device(adap, &board_info); - put_device(&adap->dev); - if (IS_ERR(i2c_clients[idx])) - return dev_err_probe(&adap->dev, PTR_ERR(i2c_clients[idx]), - "creating I2C-client %d\n", idx); - - return 0; -} - -static __init int x86_instantiate_serdev(const struct x86_serdev_info *info, int idx) -{ - struct acpi_device *ctrl_adev, *serdev_adev; - struct serdev_device *serdev; - struct device *ctrl_dev; - int ret = -ENODEV; - - ctrl_adev = acpi_dev_get_first_match_dev(info->ctrl_hid, info->ctrl_uid, -1); - if (!ctrl_adev) { - pr_err("error could not get %s/%s ctrl adev\n", - info->ctrl_hid, info->ctrl_uid); - return -ENODEV; - } - - serdev_adev = acpi_dev_get_first_match_dev(info->serdev_hid, NULL, -1); - if (!serdev_adev) { - pr_err("error could not get %s serdev adev\n", info->serdev_hid); - goto put_ctrl_adev; - } - - /* get_first_physical_node() returns a weak ref, no need to put() it */ - ctrl_dev = acpi_get_first_physical_node(ctrl_adev); - if (!ctrl_dev) { - pr_err("error could not get %s/%s ctrl physical dev\n", - info->ctrl_hid, info->ctrl_uid); - goto put_serdev_adev; - } - - /* ctrl_dev now points to the controller's parent, get the controller */ - ctrl_dev = device_find_child_by_name(ctrl_dev, info->ctrl_devname); - if (!ctrl_dev) { - pr_err("error could not get %s/%s %s ctrl dev\n", - info->ctrl_hid, info->ctrl_uid, info->ctrl_devname); - goto put_serdev_adev; - } - - serdev = serdev_device_alloc(to_serdev_controller(ctrl_dev)); - if (!serdev) { - ret = -ENOMEM; - goto put_serdev_adev; - } - - ACPI_COMPANION_SET(&serdev->dev, serdev_adev); - acpi_device_set_enumerated(serdev_adev); - - ret = serdev_device_add(serdev); - if (ret) { - dev_err(&serdev->dev, "error %d adding serdev\n", ret); - serdev_device_put(serdev); - goto put_serdev_adev; - } - - serdevs[idx] = serdev; - -put_serdev_adev: - acpi_dev_put(serdev_adev); -put_ctrl_adev: - acpi_dev_put(ctrl_adev); - return ret; -} - -static void x86_android_tablet_cleanup(void) -{ - int i; - - for (i = 0; i < serdev_count; i++) { - if (serdevs[i]) - serdev_device_remove(serdevs[i]); - } - - kfree(serdevs); - - for (i = 0; i < pdev_count; i++) - platform_device_unregister(pdevs[i]); - - kfree(pdevs); - - for (i = 0; i < i2c_client_count; i++) - i2c_unregister_device(i2c_clients[i]); - - kfree(i2c_clients); - - if (exit_handler) - exit_handler(); - - for (i = 0; gpiod_lookup_tables && gpiod_lookup_tables[i]; i++) - gpiod_remove_lookup_table(gpiod_lookup_tables[i]); - - software_node_unregister(bat_swnode); -} - -static __init int x86_android_tablet_init(void) -{ - const struct x86_dev_info *dev_info; - const struct dmi_system_id *id; - struct gpio_chip *chip; - int i, ret = 0; - - id = dmi_first_match(x86_android_tablet_ids); - if (!id) - return -ENODEV; - - dev_info = id->driver_data; - - /* - * The broken DSDTs on these devices often also include broken - * _AEI (ACPI Event Interrupt) handlers, disable these. - */ - if (dev_info->invalid_aei_gpiochip) { - chip = gpiochip_find(dev_info->invalid_aei_gpiochip, - gpiochip_find_match_label); - if (!chip) { - pr_err("error cannot find GPIO chip %s\n", dev_info->invalid_aei_gpiochip); - return -ENODEV; - } - acpi_gpiochip_free_interrupts(chip); - } - - /* - * Since this runs from module_init() it cannot use -EPROBE_DEFER, - * instead pre-load any modules which are listed as requirements. - */ - for (i = 0; dev_info->modules && dev_info->modules[i]; i++) - request_module(dev_info->modules[i]); - - bat_swnode = dev_info->bat_swnode; - if (bat_swnode) { - ret = software_node_register(bat_swnode); - if (ret) - return ret; - } - - gpiod_lookup_tables = dev_info->gpiod_lookup_tables; - for (i = 0; gpiod_lookup_tables && gpiod_lookup_tables[i]; i++) - gpiod_add_lookup_table(gpiod_lookup_tables[i]); - - if (dev_info->init) { - ret = dev_info->init(); - if (ret < 0) { - x86_android_tablet_cleanup(); - return ret; - } - exit_handler = dev_info->exit; - } - - i2c_clients = kcalloc(dev_info->i2c_client_count, sizeof(*i2c_clients), GFP_KERNEL); - if (!i2c_clients) { - x86_android_tablet_cleanup(); - return -ENOMEM; - } - - i2c_client_count = dev_info->i2c_client_count; - for (i = 0; i < i2c_client_count; i++) { - ret = x86_instantiate_i2c_client(dev_info, i); - if (ret < 0) { - x86_android_tablet_cleanup(); - return ret; - } - } - - pdevs = kcalloc(dev_info->pdev_count, sizeof(*pdevs), GFP_KERNEL); - if (!pdevs) { - x86_android_tablet_cleanup(); - return -ENOMEM; - } - - pdev_count = dev_info->pdev_count; - for (i = 0; i < pdev_count; i++) { - pdevs[i] = platform_device_register_full(&dev_info->pdev_info[i]); - if (IS_ERR(pdevs[i])) { - x86_android_tablet_cleanup(); - return PTR_ERR(pdevs[i]); - } - } - - serdevs = kcalloc(dev_info->serdev_count, sizeof(*serdevs), GFP_KERNEL); - if (!serdevs) { - x86_android_tablet_cleanup(); - return -ENOMEM; - } - - serdev_count = dev_info->serdev_count; - for (i = 0; i < serdev_count; i++) { - ret = x86_instantiate_serdev(&dev_info->serdev_info[i], i); - if (ret < 0) { - x86_android_tablet_cleanup(); - return ret; - } - } - - return 0; -} - -module_init(x86_android_tablet_init); -module_exit(x86_android_tablet_cleanup); - -MODULE_AUTHOR("Hans de Goede "); -MODULE_DESCRIPTION("X86 Android tablets DSDT fixups driver"); -MODULE_LICENSE("GPL"); diff --git a/drivers/platform/x86/x86-android-tablets/Kconfig b/drivers/platform/x86/x86-android-tablets/Kconfig new file mode 100644 index 000000000000..2b3daca5380b --- /dev/null +++ b/drivers/platform/x86/x86-android-tablets/Kconfig @@ -0,0 +1,21 @@ +# SPDX-License-Identifier: GPL-2.0-or-later +# +# X86 Android tablet support Kconfig +# + +config X86_ANDROID_TABLETS + tristate "X86 Android tablet support" + depends on I2C && SPI && SERIAL_DEV_BUS && ACPI && EFI && GPIOLIB + help + X86 tablets which ship with Android as (part of) the factory image + typically have various problems with their DSDTs. The factory kernels + shipped on these devices typically have device addresses and GPIOs + hardcoded in the kernel, rather than specified in their DSDT. + + With the DSDT containing a random collection of devices which may or + may not actually be present. This driver contains various fixes for + such tablets, including instantiating kernel devices for devices which + are missing from the DSDT. + + If you have a x86 Android tablet say Y or M here, for a generic x86 + distro config say M here. diff --git a/drivers/platform/x86/x86-android-tablets/Makefile b/drivers/platform/x86/x86-android-tablets/Makefile new file mode 100644 index 000000000000..52a0d6de5fcb --- /dev/null +++ b/drivers/platform/x86/x86-android-tablets/Makefile @@ -0,0 +1,8 @@ +# SPDX-License-Identifier: GPL-2.0-or-later +# +# X86 Android tablet support Makefile +# + +obj-$(CONFIG_X86_ANDROID_TABLETS) += x86-android-tablets.o + +x86-android-tablets-y := x86-android-tablets-main.o diff --git a/drivers/platform/x86/x86-android-tablets/x86-android-tablets-main.c b/drivers/platform/x86/x86-android-tablets/x86-android-tablets-main.c new file mode 100644 index 000000000000..08cc5e158460 --- /dev/null +++ b/drivers/platform/x86/x86-android-tablets/x86-android-tablets-main.c @@ -0,0 +1,1884 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * DMI based code to deal with broken DSDTs on X86 tablets which ship with + * Android as (part of) the factory image. The factory kernels shipped on these + * devices typically have a bunch of things hardcoded, rather than specified + * in their DSDT. + * + * Copyright (C) 2021-2022 Hans de Goede + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +/* For gpio_get_desc() which is EXPORT_SYMBOL_GPL() */ +#include "../../../gpio/gpiolib.h" +#include "../../../gpio/gpiolib-acpi.h" + +/* + * Helper code to get Linux IRQ numbers given a description of the IRQ source + * (either IOAPIC index, or GPIO chip name + pin-number). + */ +enum x86_acpi_irq_type { + X86_ACPI_IRQ_TYPE_NONE, + X86_ACPI_IRQ_TYPE_APIC, + X86_ACPI_IRQ_TYPE_GPIOINT, + X86_ACPI_IRQ_TYPE_PMIC, +}; + +struct x86_acpi_irq_data { + char *chip; /* GPIO chip label (GPIOINT) or PMIC ACPI path (PMIC) */ + enum x86_acpi_irq_type type; + enum irq_domain_bus_token domain; + int index; + int trigger; /* ACPI_EDGE_SENSITIVE / ACPI_LEVEL_SENSITIVE */ + int polarity; /* ACPI_ACTIVE_HIGH / ACPI_ACTIVE_LOW / ACPI_ACTIVE_BOTH */ +}; + +static int gpiochip_find_match_label(struct gpio_chip *gc, void *data) +{ + return gc->label && !strcmp(gc->label, data); +} + +static int x86_android_tablet_get_gpiod(char *label, int pin, struct gpio_desc **desc) +{ + struct gpio_desc *gpiod; + struct gpio_chip *chip; + + chip = gpiochip_find(label, gpiochip_find_match_label); + if (!chip) { + pr_err("error cannot find GPIO chip %s\n", label); + return -ENODEV; + } + + gpiod = gpiochip_get_desc(chip, pin); + if (IS_ERR(gpiod)) { + pr_err("error %ld getting GPIO %s %d\n", PTR_ERR(gpiod), label, pin); + return PTR_ERR(gpiod); + } + + *desc = gpiod; + return 0; +} + +static int x86_acpi_irq_helper_get(const struct x86_acpi_irq_data *data) +{ + struct irq_fwspec fwspec = { }; + struct irq_domain *domain; + struct acpi_device *adev; + struct gpio_desc *gpiod; + unsigned int irq_type; + acpi_handle handle; + acpi_status status; + int irq, ret; + + switch (data->type) { + case X86_ACPI_IRQ_TYPE_APIC: + /* + * The DSDT may already reference the GSI in a device skipped by + * acpi_quirk_skip_i2c_client_enumeration(). Unregister the GSI + * to avoid EBUSY errors in this case. + */ + acpi_unregister_gsi(data->index); + irq = acpi_register_gsi(NULL, data->index, data->trigger, data->polarity); + if (irq < 0) + pr_err("error %d getting APIC IRQ %d\n", irq, data->index); + + return irq; + case X86_ACPI_IRQ_TYPE_GPIOINT: + /* Like acpi_dev_gpio_irq_get(), but without parsing ACPI resources */ + ret = x86_android_tablet_get_gpiod(data->chip, data->index, &gpiod); + if (ret) + return ret; + + irq = gpiod_to_irq(gpiod); + if (irq < 0) { + pr_err("error %d getting IRQ %s %d\n", irq, data->chip, data->index); + return irq; + } + + irq_type = acpi_dev_get_irq_type(data->trigger, data->polarity); + if (irq_type != IRQ_TYPE_NONE && irq_type != irq_get_trigger_type(irq)) + irq_set_irq_type(irq, irq_type); + + return irq; + case X86_ACPI_IRQ_TYPE_PMIC: + status = acpi_get_handle(NULL, data->chip, &handle); + if (ACPI_FAILURE(status)) { + pr_err("error could not get %s handle\n", data->chip); + return -ENODEV; + } + + adev = acpi_fetch_acpi_dev(handle); + if (!adev) { + pr_err("error could not get %s adev\n", data->chip); + return -ENODEV; + } + + fwspec.fwnode = acpi_fwnode_handle(adev); + domain = irq_find_matching_fwspec(&fwspec, data->domain); + if (!domain) { + pr_err("error could not find IRQ domain for %s\n", data->chip); + return -ENODEV; + } + + return irq_create_mapping(domain, data->index); + default: + return 0; + } +} + +struct x86_i2c_client_info { + struct i2c_board_info board_info; + char *adapter_path; + struct x86_acpi_irq_data irq_data; +}; + +struct x86_serdev_info { + const char *ctrl_hid; + const char *ctrl_uid; + const char *ctrl_devname; + /* + * ATM the serdev core only supports of or ACPI matching; and sofar all + * Android x86 tablets DSDTs have usable serdev nodes, but sometimes + * under the wrong controller. So we just tie the existing serdev ACPI + * node to the right controller. + */ + const char *serdev_hid; +}; + +struct x86_dev_info { + char *invalid_aei_gpiochip; + const char * const *modules; + const struct software_node *bat_swnode; + struct gpiod_lookup_table * const *gpiod_lookup_tables; + const struct x86_i2c_client_info *i2c_client_info; + const struct platform_device_info *pdev_info; + const struct x86_serdev_info *serdev_info; + int i2c_client_count; + int pdev_count; + int serdev_count; + int (*init)(void); + void (*exit)(void); +}; + +/* Generic / shared charger / battery settings */ +static const char * const tusb1211_chg_det_psy[] = { "tusb1211-charger-detect" }; +static const char * const bq24190_psy[] = { "bq24190-charger" }; +static const char * const bq25890_psy[] = { "bq25890-charger-0" }; + +static const struct property_entry fg_bq24190_supply_props[] = { + PROPERTY_ENTRY_STRING_ARRAY("supplied-from", bq24190_psy), + { } +}; + +static const struct software_node fg_bq24190_supply_node = { + .properties = fg_bq24190_supply_props, +}; + +static const struct property_entry fg_bq25890_supply_props[] = { + PROPERTY_ENTRY_STRING_ARRAY("supplied-from", bq25890_psy), + { } +}; + +static const struct software_node fg_bq25890_supply_node = { + .properties = fg_bq25890_supply_props, +}; + +/* LiPo HighVoltage (max 4.35V) settings used by most devs with a HV bat. */ +static const struct property_entry generic_lipo_hv_4v35_battery_props[] = { + PROPERTY_ENTRY_STRING("compatible", "simple-battery"), + PROPERTY_ENTRY_STRING("device-chemistry", "lithium-ion"), + PROPERTY_ENTRY_U32("precharge-current-microamp", 256000), + PROPERTY_ENTRY_U32("charge-term-current-microamp", 128000), + PROPERTY_ENTRY_U32("constant-charge-current-max-microamp", 1856000), + PROPERTY_ENTRY_U32("constant-charge-voltage-max-microvolt", 4352000), + PROPERTY_ENTRY_U32("factory-internal-resistance-micro-ohms", 150000), + { } +}; + +static const struct software_node generic_lipo_hv_4v35_battery_node = { + .properties = generic_lipo_hv_4v35_battery_props, +}; + +/* For enabling the bq24190 5V boost based on id-pin */ +static struct regulator_consumer_supply intel_int3496_consumer = { + .supply = "vbus", + .dev_name = "intel-int3496", +}; + +static const struct regulator_init_data bq24190_vbus_init_data = { + .constraints = { + .name = "bq24190_vbus", + .valid_ops_mask = REGULATOR_CHANGE_STATUS, + }, + .consumer_supplies = &intel_int3496_consumer, + .num_consumer_supplies = 1, +}; + +static struct bq24190_platform_data bq24190_pdata = { + .regulator_init_data = &bq24190_vbus_init_data, +}; + +static const char * const bq24190_modules[] __initconst = { + "intel_crystal_cove_charger", /* For the bq24190 IRQ */ + "bq24190_charger", /* For the Vbus regulator for intel-int3496 */ + NULL +}; + +/* Generic pdevs array and gpio-lookups for micro USB ID pin handling */ +static const struct platform_device_info int3496_pdevs[] __initconst = { + { + /* For micro USB ID pin handling */ + .name = "intel-int3496", + .id = PLATFORM_DEVID_NONE, + }, +}; + +static struct gpiod_lookup_table int3496_gpo2_pin22_gpios = { + .dev_id = "intel-int3496", + .table = { + GPIO_LOOKUP("INT33FC:02", 22, "id", GPIO_ACTIVE_HIGH), + { } + }, +}; + +static struct gpiod_lookup_table int3496_reference_gpios = { + .dev_id = "intel-int3496", + .table = { + GPIO_LOOKUP("INT33FC:01", 15, "vbus", GPIO_ACTIVE_HIGH), + GPIO_LOOKUP("INT33FC:02", 1, "mux", GPIO_ACTIVE_HIGH), + GPIO_LOOKUP("INT33FC:02", 18, "id", GPIO_ACTIVE_HIGH), + { } + }, +}; + +/* Acer Iconia One 7 B1-750 has an Android factory img with everything hardcoded */ +static const char * const acer_b1_750_mount_matrix[] = { + "-1", "0", "0", + "0", "1", "0", + "0", "0", "1" +}; + +static const struct property_entry acer_b1_750_bma250e_props[] = { + PROPERTY_ENTRY_STRING_ARRAY("mount-matrix", acer_b1_750_mount_matrix), + { } +}; + +static const struct software_node acer_b1_750_bma250e_node = { + .properties = acer_b1_750_bma250e_props, +}; + +static const struct x86_i2c_client_info acer_b1_750_i2c_clients[] __initconst = { + { + /* Novatek NVT-ts touchscreen */ + .board_info = { + .type = "NVT-ts", + .addr = 0x34, + .dev_name = "NVT-ts", + }, + .adapter_path = "\\_SB_.I2C4", + .irq_data = { + .type = X86_ACPI_IRQ_TYPE_GPIOINT, + .chip = "INT33FC:02", + .index = 3, + .trigger = ACPI_EDGE_SENSITIVE, + .polarity = ACPI_ACTIVE_LOW, + }, + }, { + /* BMA250E accelerometer */ + .board_info = { + .type = "bma250e", + .addr = 0x18, + .swnode = &acer_b1_750_bma250e_node, + }, + .adapter_path = "\\_SB_.I2C3", + .irq_data = { + .type = X86_ACPI_IRQ_TYPE_GPIOINT, + .chip = "INT33FC:02", + .index = 25, + .trigger = ACPI_LEVEL_SENSITIVE, + .polarity = ACPI_ACTIVE_HIGH, + }, + }, +}; + +static struct gpiod_lookup_table acer_b1_750_goodix_gpios = { + .dev_id = "i2c-NVT-ts", + .table = { + GPIO_LOOKUP("INT33FC:01", 26, "reset", GPIO_ACTIVE_LOW), + { } + }, +}; + +static struct gpiod_lookup_table * const acer_b1_750_gpios[] = { + &acer_b1_750_goodix_gpios, + &int3496_reference_gpios, + NULL +}; + +static const struct x86_dev_info acer_b1_750_info __initconst = { + .i2c_client_info = acer_b1_750_i2c_clients, + .i2c_client_count = ARRAY_SIZE(acer_b1_750_i2c_clients), + .pdev_info = int3496_pdevs, + .pdev_count = ARRAY_SIZE(int3496_pdevs), + .gpiod_lookup_tables = acer_b1_750_gpios, +}; + +/* + * Advantech MICA-071 + * This is a standard Windows tablet, but it has an extra "quick launch" button + * which is not described in the ACPI tables in anyway. + * Use the x86-android-tablets infra to create a gpio-button device for this. + */ +static struct gpio_keys_button advantech_mica_071_button = { + .code = KEY_PROG1, + /* .gpio gets filled in by advantech_mica_071_init() */ + .active_low = true, + .desc = "prog1_key", + .type = EV_KEY, + .wakeup = false, + .debounce_interval = 50, +}; + +static const struct gpio_keys_platform_data advantech_mica_071_button_pdata __initconst = { + .buttons = &advantech_mica_071_button, + .nbuttons = 1, + .name = "prog1_key", +}; + +static const struct platform_device_info advantech_mica_071_pdevs[] __initconst = { + { + .name = "gpio-keys", + .id = PLATFORM_DEVID_AUTO, + .data = &advantech_mica_071_button_pdata, + .size_data = sizeof(advantech_mica_071_button_pdata), + }, +}; + +static int __init advantech_mica_071_init(void) +{ + struct gpio_desc *gpiod; + int ret; + + ret = x86_android_tablet_get_gpiod("INT33FC:00", 2, &gpiod); + if (ret < 0) + return ret; + advantech_mica_071_button.gpio = desc_to_gpio(gpiod); + + return 0; +} + +static const struct x86_dev_info advantech_mica_071_info __initconst = { + .pdev_info = advantech_mica_071_pdevs, + .pdev_count = ARRAY_SIZE(advantech_mica_071_pdevs), + .init = advantech_mica_071_init, +}; + +/* Asus ME176C and TF103C tablets shared data */ +static struct gpio_keys_button asus_me176c_tf103c_lid = { + .code = SW_LID, + /* .gpio gets filled in by asus_me176c_tf103c_init() */ + .active_low = true, + .desc = "lid_sw", + .type = EV_SW, + .wakeup = true, + .debounce_interval = 50, +}; + +static const struct gpio_keys_platform_data asus_me176c_tf103c_lid_pdata __initconst = { + .buttons = &asus_me176c_tf103c_lid, + .nbuttons = 1, + .name = "lid_sw", +}; + +static const struct platform_device_info asus_me176c_tf103c_pdevs[] __initconst = { + { + .name = "gpio-keys", + .id = PLATFORM_DEVID_AUTO, + .data = &asus_me176c_tf103c_lid_pdata, + .size_data = sizeof(asus_me176c_tf103c_lid_pdata), + }, + { + /* For micro USB ID pin handling */ + .name = "intel-int3496", + .id = PLATFORM_DEVID_NONE, + }, +}; + +static int __init asus_me176c_tf103c_init(void) +{ + struct gpio_desc *gpiod; + int ret; + + ret = x86_android_tablet_get_gpiod("INT33FC:02", 12, &gpiod); + if (ret < 0) + return ret; + asus_me176c_tf103c_lid.gpio = desc_to_gpio(gpiod); + + return 0; +} + + +/* Asus ME176C tablets have an Android factory img with everything hardcoded */ +static const char * const asus_me176c_accel_mount_matrix[] = { + "-1", "0", "0", + "0", "1", "0", + "0", "0", "1" +}; + +static const struct property_entry asus_me176c_accel_props[] = { + PROPERTY_ENTRY_STRING_ARRAY("mount-matrix", asus_me176c_accel_mount_matrix), + { } +}; + +static const struct software_node asus_me176c_accel_node = { + .properties = asus_me176c_accel_props, +}; + +static const struct property_entry asus_me176c_bq24190_props[] = { + PROPERTY_ENTRY_STRING_ARRAY("supplied-from", tusb1211_chg_det_psy), + PROPERTY_ENTRY_REF("monitored-battery", &generic_lipo_hv_4v35_battery_node), + PROPERTY_ENTRY_U32("ti,system-minimum-microvolt", 3600000), + PROPERTY_ENTRY_BOOL("omit-battery-class"), + PROPERTY_ENTRY_BOOL("disable-reset"), + { } +}; + +static const struct software_node asus_me176c_bq24190_node = { + .properties = asus_me176c_bq24190_props, +}; + +static const struct property_entry asus_me176c_ug3105_props[] = { + PROPERTY_ENTRY_STRING_ARRAY("supplied-from", bq24190_psy), + PROPERTY_ENTRY_REF("monitored-battery", &generic_lipo_hv_4v35_battery_node), + PROPERTY_ENTRY_U32("upisemi,rsns-microohm", 10000), + { } +}; + +static const struct software_node asus_me176c_ug3105_node = { + .properties = asus_me176c_ug3105_props, +}; + +static const struct x86_i2c_client_info asus_me176c_i2c_clients[] __initconst = { + { + /* bq24297 battery charger */ + .board_info = { + .type = "bq24190", + .addr = 0x6b, + .dev_name = "bq24297", + .swnode = &asus_me176c_bq24190_node, + .platform_data = &bq24190_pdata, + }, + .adapter_path = "\\_SB_.I2C1", + .irq_data = { + .type = X86_ACPI_IRQ_TYPE_PMIC, + .chip = "\\_SB_.I2C7.PMIC", + .domain = DOMAIN_BUS_WAKEUP, + .index = 0, + }, + }, { + /* ug3105 battery monitor */ + .board_info = { + .type = "ug3105", + .addr = 0x70, + .dev_name = "ug3105", + .swnode = &asus_me176c_ug3105_node, + }, + .adapter_path = "\\_SB_.I2C1", + }, { + /* ak09911 compass */ + .board_info = { + .type = "ak09911", + .addr = 0x0c, + .dev_name = "ak09911", + }, + .adapter_path = "\\_SB_.I2C5", + }, { + /* kxtj21009 accel */ + .board_info = { + .type = "kxtj21009", + .addr = 0x0f, + .dev_name = "kxtj21009", + .swnode = &asus_me176c_accel_node, + }, + .adapter_path = "\\_SB_.I2C5", + .irq_data = { + .type = X86_ACPI_IRQ_TYPE_APIC, + .index = 0x44, + .trigger = ACPI_EDGE_SENSITIVE, + .polarity = ACPI_ACTIVE_LOW, + }, + }, { + /* goodix touchscreen */ + .board_info = { + .type = "GDIX1001:00", + .addr = 0x14, + .dev_name = "goodix_ts", + }, + .adapter_path = "\\_SB_.I2C6", + .irq_data = { + .type = X86_ACPI_IRQ_TYPE_APIC, + .index = 0x45, + .trigger = ACPI_EDGE_SENSITIVE, + .polarity = ACPI_ACTIVE_LOW, + }, + }, +}; + +static const struct x86_serdev_info asus_me176c_serdevs[] __initconst = { + { + .ctrl_hid = "80860F0A", + .ctrl_uid = "2", + .ctrl_devname = "serial0", + .serdev_hid = "BCM2E3A", + }, +}; + +static struct gpiod_lookup_table asus_me176c_goodix_gpios = { + .dev_id = "i2c-goodix_ts", + .table = { + GPIO_LOOKUP("INT33FC:00", 60, "reset", GPIO_ACTIVE_HIGH), + GPIO_LOOKUP("INT33FC:02", 28, "irq", GPIO_ACTIVE_HIGH), + { } + }, +}; + +static struct gpiod_lookup_table * const asus_me176c_gpios[] = { + &int3496_gpo2_pin22_gpios, + &asus_me176c_goodix_gpios, + NULL +}; + +static const struct x86_dev_info asus_me176c_info __initconst = { + .i2c_client_info = asus_me176c_i2c_clients, + .i2c_client_count = ARRAY_SIZE(asus_me176c_i2c_clients), + .pdev_info = asus_me176c_tf103c_pdevs, + .pdev_count = ARRAY_SIZE(asus_me176c_tf103c_pdevs), + .serdev_info = asus_me176c_serdevs, + .serdev_count = ARRAY_SIZE(asus_me176c_serdevs), + .gpiod_lookup_tables = asus_me176c_gpios, + .bat_swnode = &generic_lipo_hv_4v35_battery_node, + .modules = bq24190_modules, + .invalid_aei_gpiochip = "INT33FC:02", + .init = asus_me176c_tf103c_init, +}; + +/* Asus TF103C tablets have an Android factory img with everything hardcoded */ +static const char * const asus_tf103c_accel_mount_matrix[] = { + "0", "-1", "0", + "-1", "0", "0", + "0", "0", "1" +}; + +static const struct property_entry asus_tf103c_accel_props[] = { + PROPERTY_ENTRY_STRING_ARRAY("mount-matrix", asus_tf103c_accel_mount_matrix), + { } +}; + +static const struct software_node asus_tf103c_accel_node = { + .properties = asus_tf103c_accel_props, +}; + +static const struct property_entry asus_tf103c_touchscreen_props[] = { + PROPERTY_ENTRY_STRING("compatible", "atmel,atmel_mxt_ts"), + { } +}; + +static const struct software_node asus_tf103c_touchscreen_node = { + .properties = asus_tf103c_touchscreen_props, +}; + +static const struct property_entry asus_tf103c_battery_props[] = { + PROPERTY_ENTRY_STRING("compatible", "simple-battery"), + PROPERTY_ENTRY_STRING("device-chemistry", "lithium-ion-polymer"), + PROPERTY_ENTRY_U32("precharge-current-microamp", 256000), + PROPERTY_ENTRY_U32("charge-term-current-microamp", 128000), + PROPERTY_ENTRY_U32("constant-charge-current-max-microamp", 2048000), + PROPERTY_ENTRY_U32("constant-charge-voltage-max-microvolt", 4208000), + PROPERTY_ENTRY_U32("factory-internal-resistance-micro-ohms", 150000), + { } +}; + +static const struct software_node asus_tf103c_battery_node = { + .properties = asus_tf103c_battery_props, +}; + +static const struct property_entry asus_tf103c_bq24190_props[] = { + PROPERTY_ENTRY_STRING_ARRAY("supplied-from", tusb1211_chg_det_psy), + PROPERTY_ENTRY_REF("monitored-battery", &asus_tf103c_battery_node), + PROPERTY_ENTRY_U32("ti,system-minimum-microvolt", 3600000), + PROPERTY_ENTRY_BOOL("omit-battery-class"), + PROPERTY_ENTRY_BOOL("disable-reset"), + { } +}; + +static const struct software_node asus_tf103c_bq24190_node = { + .properties = asus_tf103c_bq24190_props, +}; + +static const struct property_entry asus_tf103c_ug3105_props[] = { + PROPERTY_ENTRY_STRING_ARRAY("supplied-from", bq24190_psy), + PROPERTY_ENTRY_REF("monitored-battery", &asus_tf103c_battery_node), + PROPERTY_ENTRY_U32("upisemi,rsns-microohm", 5000), + { } +}; + +static const struct software_node asus_tf103c_ug3105_node = { + .properties = asus_tf103c_ug3105_props, +}; + +static const struct x86_i2c_client_info asus_tf103c_i2c_clients[] __initconst = { + { + /* bq24297 battery charger */ + .board_info = { + .type = "bq24190", + .addr = 0x6b, + .dev_name = "bq24297", + .swnode = &asus_tf103c_bq24190_node, + .platform_data = &bq24190_pdata, + }, + .adapter_path = "\\_SB_.I2C1", + .irq_data = { + .type = X86_ACPI_IRQ_TYPE_PMIC, + .chip = "\\_SB_.I2C7.PMIC", + .domain = DOMAIN_BUS_WAKEUP, + .index = 0, + }, + }, { + /* ug3105 battery monitor */ + .board_info = { + .type = "ug3105", + .addr = 0x70, + .dev_name = "ug3105", + .swnode = &asus_tf103c_ug3105_node, + }, + .adapter_path = "\\_SB_.I2C1", + }, { + /* ak09911 compass */ + .board_info = { + .type = "ak09911", + .addr = 0x0c, + .dev_name = "ak09911", + }, + .adapter_path = "\\_SB_.I2C5", + }, { + /* kxtj21009 accel */ + .board_info = { + .type = "kxtj21009", + .addr = 0x0f, + .dev_name = "kxtj21009", + .swnode = &asus_tf103c_accel_node, + }, + .adapter_path = "\\_SB_.I2C5", + }, { + /* atmel touchscreen */ + .board_info = { + .type = "atmel_mxt_ts", + .addr = 0x4a, + .dev_name = "atmel_mxt_ts", + .swnode = &asus_tf103c_touchscreen_node, + }, + .adapter_path = "\\_SB_.I2C6", + .irq_data = { + .type = X86_ACPI_IRQ_TYPE_GPIOINT, + .chip = "INT33FC:02", + .index = 28, + .trigger = ACPI_EDGE_SENSITIVE, + .polarity = ACPI_ACTIVE_LOW, + }, + }, +}; + +static struct gpiod_lookup_table * const asus_tf103c_gpios[] = { + &int3496_gpo2_pin22_gpios, + NULL +}; + +static const struct x86_dev_info asus_tf103c_info __initconst = { + .i2c_client_info = asus_tf103c_i2c_clients, + .i2c_client_count = ARRAY_SIZE(asus_tf103c_i2c_clients), + .pdev_info = asus_me176c_tf103c_pdevs, + .pdev_count = ARRAY_SIZE(asus_me176c_tf103c_pdevs), + .gpiod_lookup_tables = asus_tf103c_gpios, + .bat_swnode = &asus_tf103c_battery_node, + .modules = bq24190_modules, + .invalid_aei_gpiochip = "INT33FC:02", + .init = asus_me176c_tf103c_init, +}; + +/* + * When booted with the BIOS set to Android mode the Chuwi Hi8 (CWI509) DSDT + * contains a whole bunch of bogus ACPI I2C devices and is missing entries + * for the touchscreen and the accelerometer. + */ +static const struct property_entry chuwi_hi8_gsl1680_props[] = { + PROPERTY_ENTRY_U32("touchscreen-size-x", 1665), + PROPERTY_ENTRY_U32("touchscreen-size-y", 1140), + PROPERTY_ENTRY_BOOL("touchscreen-swapped-x-y"), + PROPERTY_ENTRY_BOOL("silead,home-button"), + PROPERTY_ENTRY_STRING("firmware-name", "gsl1680-chuwi-hi8.fw"), + { } +}; + +static const struct software_node chuwi_hi8_gsl1680_node = { + .properties = chuwi_hi8_gsl1680_props, +}; + +static const char * const chuwi_hi8_mount_matrix[] = { + "1", "0", "0", + "0", "-1", "0", + "0", "0", "1" +}; + +static const struct property_entry chuwi_hi8_bma250e_props[] = { + PROPERTY_ENTRY_STRING_ARRAY("mount-matrix", chuwi_hi8_mount_matrix), + { } +}; + +static const struct software_node chuwi_hi8_bma250e_node = { + .properties = chuwi_hi8_bma250e_props, +}; + +static const struct x86_i2c_client_info chuwi_hi8_i2c_clients[] __initconst = { + { + /* Silead touchscreen */ + .board_info = { + .type = "gsl1680", + .addr = 0x40, + .swnode = &chuwi_hi8_gsl1680_node, + }, + .adapter_path = "\\_SB_.I2C4", + .irq_data = { + .type = X86_ACPI_IRQ_TYPE_APIC, + .index = 0x44, + .trigger = ACPI_EDGE_SENSITIVE, + .polarity = ACPI_ACTIVE_HIGH, + }, + }, { + /* BMA250E accelerometer */ + .board_info = { + .type = "bma250e", + .addr = 0x18, + .swnode = &chuwi_hi8_bma250e_node, + }, + .adapter_path = "\\_SB_.I2C3", + .irq_data = { + .type = X86_ACPI_IRQ_TYPE_GPIOINT, + .chip = "INT33FC:02", + .index = 23, + .trigger = ACPI_LEVEL_SENSITIVE, + .polarity = ACPI_ACTIVE_HIGH, + }, + }, +}; + +static int __init chuwi_hi8_init(void) +{ + /* + * Avoid the acpi_unregister_gsi() call in x86_acpi_irq_helper_get() + * breaking the touchscreen + logging various errors when the Windows + * BIOS is used. + */ + if (acpi_dev_present("MSSL0001", NULL, 1)) + return -ENODEV; + + return 0; +} + +static const struct x86_dev_info chuwi_hi8_info __initconst = { + .i2c_client_info = chuwi_hi8_i2c_clients, + .i2c_client_count = ARRAY_SIZE(chuwi_hi8_i2c_clients), + .init = chuwi_hi8_init, +}; + +#define CZC_EC_EXTRA_PORT 0x68 +#define CZC_EC_ANDROID_KEYS 0x63 + +static int __init czc_p10t_init(void) +{ + /* + * The device boots up in "Windows 7" mode, when the home button sends a + * Windows specific key sequence (Left Meta + D) and the second button + * sends an unknown one while also toggling the Radio Kill Switch. + * This is a surprising behavior when the second button is labeled "Back". + * + * The vendor-supplied Android-x86 build switches the device to a "Android" + * mode by writing value 0x63 to the I/O port 0x68. This just seems to just + * set bit 6 on address 0x96 in the EC region; switching the bit directly + * seems to achieve the same result. It uses a "p10t_switcher" to do the + * job. It doesn't seem to be able to do anything else, and no other use + * of the port 0x68 is known. + * + * In the Android mode, the home button sends just a single scancode, + * which can be handled in Linux userspace more reasonably and the back + * button only sends a scancode without toggling the kill switch. + * The scancode can then be mapped either to Back or RF Kill functionality + * in userspace, depending on how the button is labeled on that particular + * model. + */ + outb(CZC_EC_ANDROID_KEYS, CZC_EC_EXTRA_PORT); + return 0; +} + +static const struct x86_dev_info czc_p10t __initconst = { + .init = czc_p10t_init, +}; + +/* Lenovo Yoga Book X90F / X91F / X91L need manual instantiation of the fg client */ +static const struct x86_i2c_client_info lenovo_yogabook_x9x_i2c_clients[] __initconst = { + { + /* BQ27542 fuel-gauge */ + .board_info = { + .type = "bq27542", + .addr = 0x55, + .dev_name = "bq27542", + .swnode = &fg_bq25890_supply_node, + }, + .adapter_path = "\\_SB_.PCI0.I2C1", + }, +}; + +static const struct x86_dev_info lenovo_yogabook_x9x_info __initconst = { + .i2c_client_info = lenovo_yogabook_x9x_i2c_clients, + .i2c_client_count = ARRAY_SIZE(lenovo_yogabook_x9x_i2c_clients), +}; + +/* Lenovo Yoga Tablet 2 1050F/L's Android factory img has everything hardcoded */ +static const struct property_entry lenovo_yoga_tab2_830_1050_bq24190_props[] = { + PROPERTY_ENTRY_STRING_ARRAY("supplied-from", tusb1211_chg_det_psy), + PROPERTY_ENTRY_REF("monitored-battery", &generic_lipo_hv_4v35_battery_node), + PROPERTY_ENTRY_BOOL("omit-battery-class"), + PROPERTY_ENTRY_BOOL("disable-reset"), + { } +}; + +static const struct software_node lenovo_yoga_tab2_830_1050_bq24190_node = { + .properties = lenovo_yoga_tab2_830_1050_bq24190_props, +}; + +/* This gets filled by lenovo_yoga_tab2_830_1050_init() */ +static struct rmi_device_platform_data lenovo_yoga_tab2_830_1050_rmi_pdata = { }; + +static struct lp855x_platform_data lenovo_yoga_tab2_830_1050_lp8557_pdata = { + .device_control = 0x86, + .initial_brightness = 128, +}; + +static const struct x86_i2c_client_info lenovo_yoga_tab2_830_1050_i2c_clients[] __initconst = { + { + /* bq24292i battery charger */ + .board_info = { + .type = "bq24190", + .addr = 0x6b, + .dev_name = "bq24292i", + .swnode = &lenovo_yoga_tab2_830_1050_bq24190_node, + .platform_data = &bq24190_pdata, + }, + .adapter_path = "\\_SB_.I2C1", + .irq_data = { + .type = X86_ACPI_IRQ_TYPE_GPIOINT, + .chip = "INT33FC:02", + .index = 2, + .trigger = ACPI_EDGE_SENSITIVE, + .polarity = ACPI_ACTIVE_HIGH, + }, + }, { + /* BQ27541 fuel-gauge */ + .board_info = { + .type = "bq27541", + .addr = 0x55, + .dev_name = "bq27541", + .swnode = &fg_bq24190_supply_node, + }, + .adapter_path = "\\_SB_.I2C1", + }, { + /* Synaptics RMI touchscreen */ + .board_info = { + .type = "rmi4_i2c", + .addr = 0x38, + .dev_name = "rmi4_i2c", + .platform_data = &lenovo_yoga_tab2_830_1050_rmi_pdata, + }, + .adapter_path = "\\_SB_.I2C6", + .irq_data = { + .type = X86_ACPI_IRQ_TYPE_APIC, + .index = 0x45, + .trigger = ACPI_EDGE_SENSITIVE, + .polarity = ACPI_ACTIVE_HIGH, + }, + }, { + /* LP8557 Backlight controller */ + .board_info = { + .type = "lp8557", + .addr = 0x2c, + .dev_name = "lp8557", + .platform_data = &lenovo_yoga_tab2_830_1050_lp8557_pdata, + }, + .adapter_path = "\\_SB_.I2C3", + }, +}; + +static struct gpiod_lookup_table lenovo_yoga_tab2_830_1050_int3496_gpios = { + .dev_id = "intel-int3496", + .table = { + GPIO_LOOKUP("INT33FC:02", 1, "mux", GPIO_ACTIVE_LOW), + GPIO_LOOKUP("INT33FC:02", 24, "id", GPIO_ACTIVE_HIGH), + { } + }, +}; + +#define LENOVO_YOGA_TAB2_830_1050_CODEC_NAME "spi-10WM5102:00" + +static struct gpiod_lookup_table lenovo_yoga_tab2_830_1050_codec_gpios = { + .dev_id = LENOVO_YOGA_TAB2_830_1050_CODEC_NAME, + .table = { + GPIO_LOOKUP("gpio_crystalcove", 3, "reset", GPIO_ACTIVE_HIGH), + GPIO_LOOKUP("INT33FC:01", 23, "wlf,ldoena", GPIO_ACTIVE_HIGH), + GPIO_LOOKUP("arizona", 2, "wlf,spkvdd-ena", GPIO_ACTIVE_HIGH), + GPIO_LOOKUP("arizona", 4, "wlf,micd-pol", GPIO_ACTIVE_LOW), + { } + }, +}; + +static struct gpiod_lookup_table * const lenovo_yoga_tab2_830_1050_gpios[] = { + &lenovo_yoga_tab2_830_1050_int3496_gpios, + &lenovo_yoga_tab2_830_1050_codec_gpios, + NULL +}; + +static int __init lenovo_yoga_tab2_830_1050_init(void); +static void lenovo_yoga_tab2_830_1050_exit(void); + +static struct x86_dev_info lenovo_yoga_tab2_830_1050_info __initdata = { + .i2c_client_info = lenovo_yoga_tab2_830_1050_i2c_clients, + /* i2c_client_count gets set by lenovo_yoga_tab2_830_1050_init() */ + .pdev_info = int3496_pdevs, + .pdev_count = ARRAY_SIZE(int3496_pdevs), + .gpiod_lookup_tables = lenovo_yoga_tab2_830_1050_gpios, + .bat_swnode = &generic_lipo_hv_4v35_battery_node, + .modules = bq24190_modules, + .invalid_aei_gpiochip = "INT33FC:02", + .init = lenovo_yoga_tab2_830_1050_init, + .exit = lenovo_yoga_tab2_830_1050_exit, +}; + +/* + * The Lenovo Yoga Tablet 2 830 and 1050 (8" vs 10") versions use the same + * mainboard, but they need some different treatment related to the display: + * 1. The 830 uses a portrait LCD panel with a landscape touchscreen, requiring + * the touchscreen driver to adjust the touch-coords to match the LCD. + * 2. Both use an TI LP8557 LED backlight controller. On the 1050 the LP8557's + * PWM input is connected to the PMIC's PWM output and everything works fine + * with the defaults programmed into the LP8557 by the BIOS. + * But on the 830 the LP8557's PWM input is connected to a PWM output coming + * from the LCD panel's controller. The Android code has a hack in the i915 + * driver to write the non-standard DSI reg 0x9f with the desired backlight + * level to set the duty-cycle of the LCD's PWM output. + * + * To avoid having to have a similar hack in the mainline kernel the LP8557 + * entry in lenovo_yoga_tab2_830_1050_i2c_clients instead just programs the + * LP8557 to directly set the level, ignoring the PWM input. This means that + * the LP8557 i2c_client should only be instantiated on the 830. + */ +static int __init lenovo_yoga_tab2_830_1050_init_display(void) +{ + struct gpio_desc *gpiod; + int ret; + + /* Use PMIC GPIO 10 bootstrap pin to differentiate 830 vs 1050 */ + ret = x86_android_tablet_get_gpiod("gpio_crystalcove", 10, &gpiod); + if (ret) + return ret; + + ret = gpiod_get_value_cansleep(gpiod); + if (ret) { + pr_info("detected Lenovo Yoga Tablet 2 1050F/L\n"); + lenovo_yoga_tab2_830_1050_info.i2c_client_count = + ARRAY_SIZE(lenovo_yoga_tab2_830_1050_i2c_clients) - 1; + } else { + pr_info("detected Lenovo Yoga Tablet 2 830F/L\n"); + lenovo_yoga_tab2_830_1050_rmi_pdata.sensor_pdata.axis_align.swap_axes = true; + lenovo_yoga_tab2_830_1050_rmi_pdata.sensor_pdata.axis_align.flip_y = true; + lenovo_yoga_tab2_830_1050_info.i2c_client_count = + ARRAY_SIZE(lenovo_yoga_tab2_830_1050_i2c_clients); + } + + return 0; +} + +/* SUS (INT33FC:02) pin 6 needs to be configured as pmu_clk for the audio codec */ +static const struct pinctrl_map lenovo_yoga_tab2_830_1050_codec_pinctrl_map = + PIN_MAP_MUX_GROUP(LENOVO_YOGA_TAB2_830_1050_CODEC_NAME, "codec_32khz_clk", + "INT33FC:02", "pmu_clk2_grp", "pmu_clk"); + +static struct pinctrl *lenovo_yoga_tab2_830_1050_codec_pinctrl; +static struct sys_off_handler *lenovo_yoga_tab2_830_1050_sys_off_handler; + +static int __init lenovo_yoga_tab2_830_1050_init_codec(void) +{ + struct device *codec_dev; + struct pinctrl *pinctrl; + int ret; + + codec_dev = bus_find_device_by_name(&spi_bus_type, NULL, + LENOVO_YOGA_TAB2_830_1050_CODEC_NAME); + if (!codec_dev) { + pr_err("error cannot find %s device\n", LENOVO_YOGA_TAB2_830_1050_CODEC_NAME); + return -ENODEV; + } + + ret = pinctrl_register_mappings(&lenovo_yoga_tab2_830_1050_codec_pinctrl_map, 1); + if (ret) + goto err_put_device; + + pinctrl = pinctrl_get_select(codec_dev, "codec_32khz_clk"); + if (IS_ERR(pinctrl)) { + ret = dev_err_probe(codec_dev, PTR_ERR(pinctrl), "selecting codec_32khz_clk\n"); + goto err_unregister_mappings; + } + + /* We're done with the codec_dev now */ + put_device(codec_dev); + + lenovo_yoga_tab2_830_1050_codec_pinctrl = pinctrl; + return 0; + +err_unregister_mappings: + pinctrl_unregister_mappings(&lenovo_yoga_tab2_830_1050_codec_pinctrl_map); +err_put_device: + put_device(codec_dev); + return ret; +} + +/* + * These tablet's DSDT does not set acpi_gbl_reduced_hardware, so acpi_power_off + * gets used as pm_power_off handler. This causes "poweroff" on these tablets + * to hang hard. Requiring pressing the powerbutton for 30 seconds *twice* + * followed by a normal 3 second press to recover. Avoid this by doing an EFI + * poweroff instead. + */ +static int lenovo_yoga_tab2_830_1050_power_off(struct sys_off_data *data) +{ + efi.reset_system(EFI_RESET_SHUTDOWN, EFI_SUCCESS, 0, NULL); + + return NOTIFY_DONE; +} + +static int __init lenovo_yoga_tab2_830_1050_init(void) +{ + int ret; + + ret = lenovo_yoga_tab2_830_1050_init_display(); + if (ret) + return ret; + + ret = lenovo_yoga_tab2_830_1050_init_codec(); + if (ret) + return ret; + + /* SYS_OFF_PRIO_FIRMWARE + 1 so that it runs before acpi_power_off */ + lenovo_yoga_tab2_830_1050_sys_off_handler = + register_sys_off_handler(SYS_OFF_MODE_POWER_OFF, SYS_OFF_PRIO_FIRMWARE + 1, + lenovo_yoga_tab2_830_1050_power_off, NULL); + if (IS_ERR(lenovo_yoga_tab2_830_1050_sys_off_handler)) + return PTR_ERR(lenovo_yoga_tab2_830_1050_sys_off_handler); + + return 0; +} + +static void lenovo_yoga_tab2_830_1050_exit(void) +{ + unregister_sys_off_handler(lenovo_yoga_tab2_830_1050_sys_off_handler); + + if (lenovo_yoga_tab2_830_1050_codec_pinctrl) { + pinctrl_put(lenovo_yoga_tab2_830_1050_codec_pinctrl); + pinctrl_unregister_mappings(&lenovo_yoga_tab2_830_1050_codec_pinctrl_map); + } +} + +/* Lenovo Yoga Tab 3 Pro YT3-X90F */ + +/* + * There are 2 batteries, with 2 bq27500 fuel-gauges and 2 bq25892 chargers, + * "bq25890-charger-1" is instantiated from: drivers/i2c/busses/i2c-cht-wc.c. + */ +static const char * const lenovo_yt3_bq25892_0_suppliers[] = { "cht_wcove_pwrsrc" }; +static const char * const bq25890_1_psy[] = { "bq25890-charger-1" }; + +static const struct property_entry fg_bq25890_1_supply_props[] = { + PROPERTY_ENTRY_STRING_ARRAY("supplied-from", bq25890_1_psy), + { } +}; + +static const struct software_node fg_bq25890_1_supply_node = { + .properties = fg_bq25890_1_supply_props, +}; + +/* bq25892 charger settings for the flat lipo battery behind the screen */ +static const struct property_entry lenovo_yt3_bq25892_0_props[] = { + PROPERTY_ENTRY_STRING_ARRAY("supplied-from", lenovo_yt3_bq25892_0_suppliers), + PROPERTY_ENTRY_STRING("linux,power-supply-name", "bq25892-second-chrg"), + PROPERTY_ENTRY_U32("linux,iinlim-percentage", 40), + PROPERTY_ENTRY_BOOL("linux,skip-reset"), + /* Values taken from Android Factory Image */ + PROPERTY_ENTRY_U32("ti,charge-current", 2048000), + PROPERTY_ENTRY_U32("ti,battery-regulation-voltage", 4352000), + PROPERTY_ENTRY_U32("ti,termination-current", 128000), + PROPERTY_ENTRY_U32("ti,precharge-current", 128000), + PROPERTY_ENTRY_U32("ti,minimum-sys-voltage", 3700000), + PROPERTY_ENTRY_U32("ti,boost-voltage", 4998000), + PROPERTY_ENTRY_U32("ti,boost-max-current", 500000), + PROPERTY_ENTRY_BOOL("ti,use-ilim-pin"), + { } +}; + +static const struct software_node lenovo_yt3_bq25892_0_node = { + .properties = lenovo_yt3_bq25892_0_props, +}; + +static const struct x86_i2c_client_info lenovo_yt3_i2c_clients[] __initconst = { + { + /* bq27500 fuel-gauge for the flat lipo battery behind the screen */ + .board_info = { + .type = "bq27500", + .addr = 0x55, + .dev_name = "bq27500_0", + .swnode = &fg_bq25890_supply_node, + }, + .adapter_path = "\\_SB_.PCI0.I2C1", + }, { + /* bq25892 charger for the flat lipo battery behind the screen */ + .board_info = { + .type = "bq25892", + .addr = 0x6b, + .dev_name = "bq25892_0", + .swnode = &lenovo_yt3_bq25892_0_node, + }, + .adapter_path = "\\_SB_.PCI0.I2C1", + .irq_data = { + .type = X86_ACPI_IRQ_TYPE_GPIOINT, + .chip = "INT33FF:01", + .index = 5, + .trigger = ACPI_EDGE_SENSITIVE, + .polarity = ACPI_ACTIVE_LOW, + }, + }, { + /* bq27500 fuel-gauge for the round li-ion cells in the hinge */ + .board_info = { + .type = "bq27500", + .addr = 0x55, + .dev_name = "bq27500_1", + .swnode = &fg_bq25890_1_supply_node, + }, + .adapter_path = "\\_SB_.PCI0.I2C2", + } +}; + +static int __init lenovo_yt3_init(void) +{ + struct gpio_desc *gpiod; + int ret; + + /* + * The "bq25892_0" charger IC has its /CE (Charge-Enable) and OTG pins + * connected to GPIOs, rather then having them hardwired to the correct + * values as is normally done. + * + * The bq25890_charger driver controls these through I2C, but this only + * works if not overridden by the pins. Set these pins here: + * 1. Set /CE to 0 to allow charging. + * 2. Set OTG to 0 disable V5 boost output since the 5V boost output of + * the main "bq25892_1" charger is used when necessary. + */ + + /* /CE pin */ + ret = x86_android_tablet_get_gpiod("INT33FF:02", 22, &gpiod); + if (ret < 0) + return ret; + + /* + * The gpio_desc returned by x86_android_tablet_get_gpiod() is a "raw" + * gpio_desc, that is there is no way to pass lookup-flags like + * GPIO_ACTIVE_LOW. Set the GPIO to 0 here to enable charging since + * the /CE pin is active-low, but not marked as such in the gpio_desc. + */ + gpiod_set_value(gpiod, 0); + + /* OTG pin */ + ret = x86_android_tablet_get_gpiod("INT33FF:03", 19, &gpiod); + if (ret < 0) + return ret; + + gpiod_set_value(gpiod, 0); + + return 0; +} + +static const struct x86_dev_info lenovo_yt3_info __initconst = { + .i2c_client_info = lenovo_yt3_i2c_clients, + .i2c_client_count = ARRAY_SIZE(lenovo_yt3_i2c_clients), + .init = lenovo_yt3_init, +}; + +/* Medion Lifetab S10346 tablets have an Android factory img with everything hardcoded */ +static const char * const medion_lifetab_s10346_accel_mount_matrix[] = { + "0", "1", "0", + "1", "0", "0", + "0", "0", "1" +}; + +static const struct property_entry medion_lifetab_s10346_accel_props[] = { + PROPERTY_ENTRY_STRING_ARRAY("mount-matrix", medion_lifetab_s10346_accel_mount_matrix), + { } +}; + +static const struct software_node medion_lifetab_s10346_accel_node = { + .properties = medion_lifetab_s10346_accel_props, +}; + +/* Note the LCD panel is mounted upside down, this is correctly indicated in the VBT */ +static const struct property_entry medion_lifetab_s10346_touchscreen_props[] = { + PROPERTY_ENTRY_BOOL("touchscreen-inverted-x"), + PROPERTY_ENTRY_BOOL("touchscreen-swapped-x-y"), + { } +}; + +static const struct software_node medion_lifetab_s10346_touchscreen_node = { + .properties = medion_lifetab_s10346_touchscreen_props, +}; + +static const struct x86_i2c_client_info medion_lifetab_s10346_i2c_clients[] __initconst = { + { + /* kxtj21009 accel */ + .board_info = { + .type = "kxtj21009", + .addr = 0x0f, + .dev_name = "kxtj21009", + .swnode = &medion_lifetab_s10346_accel_node, + }, + .adapter_path = "\\_SB_.I2C3", + .irq_data = { + .type = X86_ACPI_IRQ_TYPE_GPIOINT, + .chip = "INT33FC:02", + .index = 23, + .trigger = ACPI_EDGE_SENSITIVE, + .polarity = ACPI_ACTIVE_HIGH, + }, + }, { + /* goodix touchscreen */ + .board_info = { + .type = "GDIX1001:00", + .addr = 0x14, + .dev_name = "goodix_ts", + .swnode = &medion_lifetab_s10346_touchscreen_node, + }, + .adapter_path = "\\_SB_.I2C4", + .irq_data = { + .type = X86_ACPI_IRQ_TYPE_APIC, + .index = 0x44, + .trigger = ACPI_EDGE_SENSITIVE, + .polarity = ACPI_ACTIVE_LOW, + }, + }, +}; + +static struct gpiod_lookup_table medion_lifetab_s10346_goodix_gpios = { + .dev_id = "i2c-goodix_ts", + .table = { + GPIO_LOOKUP("INT33FC:01", 26, "reset", GPIO_ACTIVE_HIGH), + GPIO_LOOKUP("INT33FC:02", 3, "irq", GPIO_ACTIVE_HIGH), + { } + }, +}; + +static struct gpiod_lookup_table * const medion_lifetab_s10346_gpios[] = { + &medion_lifetab_s10346_goodix_gpios, + NULL +}; + +static const struct x86_dev_info medion_lifetab_s10346_info __initconst = { + .i2c_client_info = medion_lifetab_s10346_i2c_clients, + .i2c_client_count = ARRAY_SIZE(medion_lifetab_s10346_i2c_clients), + .gpiod_lookup_tables = medion_lifetab_s10346_gpios, +}; + +/* Nextbook Ares 8 tablets have an Android factory img with everything hardcoded */ +static const char * const nextbook_ares8_accel_mount_matrix[] = { + "0", "-1", "0", + "-1", "0", "0", + "0", "0", "1" +}; + +static const struct property_entry nextbook_ares8_accel_props[] = { + PROPERTY_ENTRY_STRING_ARRAY("mount-matrix", nextbook_ares8_accel_mount_matrix), + { } +}; + +static const struct software_node nextbook_ares8_accel_node = { + .properties = nextbook_ares8_accel_props, +}; + +static const struct property_entry nextbook_ares8_touchscreen_props[] = { + PROPERTY_ENTRY_U32("touchscreen-size-x", 800), + PROPERTY_ENTRY_U32("touchscreen-size-y", 1280), + { } +}; + +static const struct software_node nextbook_ares8_touchscreen_node = { + .properties = nextbook_ares8_touchscreen_props, +}; + +static const struct x86_i2c_client_info nextbook_ares8_i2c_clients[] __initconst = { + { + /* Freescale MMA8653FC accel */ + .board_info = { + .type = "mma8653", + .addr = 0x1d, + .dev_name = "mma8653", + .swnode = &nextbook_ares8_accel_node, + }, + .adapter_path = "\\_SB_.I2C3", + }, { + /* FT5416DQ9 touchscreen controller */ + .board_info = { + .type = "edt-ft5x06", + .addr = 0x38, + .dev_name = "ft5416", + .swnode = &nextbook_ares8_touchscreen_node, + }, + .adapter_path = "\\_SB_.I2C4", + .irq_data = { + .type = X86_ACPI_IRQ_TYPE_GPIOINT, + .chip = "INT33FC:02", + .index = 3, + .trigger = ACPI_EDGE_SENSITIVE, + .polarity = ACPI_ACTIVE_LOW, + }, + }, +}; + +static struct gpiod_lookup_table * const nextbook_ares8_gpios[] = { + &int3496_reference_gpios, + NULL +}; + +static const struct x86_dev_info nextbook_ares8_info __initconst = { + .i2c_client_info = nextbook_ares8_i2c_clients, + .i2c_client_count = ARRAY_SIZE(nextbook_ares8_i2c_clients), + .pdev_info = int3496_pdevs, + .pdev_count = ARRAY_SIZE(int3496_pdevs), + .gpiod_lookup_tables = nextbook_ares8_gpios, + .invalid_aei_gpiochip = "INT33FC:02", +}; + +/* + * Whitelabel (sold as various brands) TM800A550L tablets. + * These tablet's DSDT contains a whole bunch of bogus ACPI I2C devices + * (removed through acpi_quirk_skip_i2c_client_enumeration()) and + * the touchscreen fwnode has the wrong GPIOs. + */ +static const char * const whitelabel_tm800a550l_accel_mount_matrix[] = { + "-1", "0", "0", + "0", "1", "0", + "0", "0", "1" +}; + +static const struct property_entry whitelabel_tm800a550l_accel_props[] = { + PROPERTY_ENTRY_STRING_ARRAY("mount-matrix", whitelabel_tm800a550l_accel_mount_matrix), + { } +}; + +static const struct software_node whitelabel_tm800a550l_accel_node = { + .properties = whitelabel_tm800a550l_accel_props, +}; + +static const struct property_entry whitelabel_tm800a550l_goodix_props[] = { + PROPERTY_ENTRY_STRING("firmware-name", "gt912-tm800a550l.fw"), + PROPERTY_ENTRY_STRING("goodix,config-name", "gt912-tm800a550l.cfg"), + PROPERTY_ENTRY_U32("goodix,main-clk", 54), + { } +}; + +static const struct software_node whitelabel_tm800a550l_goodix_node = { + .properties = whitelabel_tm800a550l_goodix_props, +}; + +static const struct x86_i2c_client_info whitelabel_tm800a550l_i2c_clients[] __initconst = { + { + /* goodix touchscreen */ + .board_info = { + .type = "GDIX1001:00", + .addr = 0x14, + .dev_name = "goodix_ts", + .swnode = &whitelabel_tm800a550l_goodix_node, + }, + .adapter_path = "\\_SB_.I2C2", + .irq_data = { + .type = X86_ACPI_IRQ_TYPE_APIC, + .index = 0x44, + .trigger = ACPI_EDGE_SENSITIVE, + .polarity = ACPI_ACTIVE_HIGH, + }, + }, { + /* kxcj91008 accel */ + .board_info = { + .type = "kxcj91008", + .addr = 0x0f, + .dev_name = "kxcj91008", + .swnode = &whitelabel_tm800a550l_accel_node, + }, + .adapter_path = "\\_SB_.I2C3", + }, +}; + +static struct gpiod_lookup_table whitelabel_tm800a550l_goodix_gpios = { + .dev_id = "i2c-goodix_ts", + .table = { + GPIO_LOOKUP("INT33FC:01", 26, "reset", GPIO_ACTIVE_HIGH), + GPIO_LOOKUP("INT33FC:02", 3, "irq", GPIO_ACTIVE_HIGH), + { } + }, +}; + +static struct gpiod_lookup_table * const whitelabel_tm800a550l_gpios[] = { + &whitelabel_tm800a550l_goodix_gpios, + NULL +}; + +static const struct x86_dev_info whitelabel_tm800a550l_info __initconst = { + .i2c_client_info = whitelabel_tm800a550l_i2c_clients, + .i2c_client_count = ARRAY_SIZE(whitelabel_tm800a550l_i2c_clients), + .gpiod_lookup_tables = whitelabel_tm800a550l_gpios, +}; + +/* + * If the EFI bootloader is not Xiaomi's own signed Android loader, then the + * Xiaomi Mi Pad 2 X86 tablet sets OSID in the DSDT to 1 (Windows), causing + * a bunch of devices to be hidden. + * + * This takes care of instantiating the hidden devices manually. + */ +static const struct x86_i2c_client_info xiaomi_mipad2_i2c_clients[] __initconst = { + { + /* BQ27520 fuel-gauge */ + .board_info = { + .type = "bq27520", + .addr = 0x55, + .dev_name = "bq27520", + .swnode = &fg_bq25890_supply_node, + }, + .adapter_path = "\\_SB_.PCI0.I2C1", + }, { + /* KTD2026 RGB notification LED controller */ + .board_info = { + .type = "ktd2026", + .addr = 0x30, + .dev_name = "ktd2026", + }, + .adapter_path = "\\_SB_.PCI0.I2C3", + }, +}; + +static const struct x86_dev_info xiaomi_mipad2_info __initconst = { + .i2c_client_info = xiaomi_mipad2_i2c_clients, + .i2c_client_count = ARRAY_SIZE(xiaomi_mipad2_i2c_clients), +}; + +static const struct dmi_system_id x86_android_tablet_ids[] __initconst = { + { + /* Acer Iconia One 7 B1-750 */ + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Insyde"), + DMI_MATCH(DMI_PRODUCT_NAME, "VESPA2"), + }, + .driver_data = (void *)&acer_b1_750_info, + }, + { + /* Advantech MICA-071 */ + .matches = { + DMI_EXACT_MATCH(DMI_SYS_VENDOR, "Advantech"), + DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "MICA-071"), + }, + .driver_data = (void *)&advantech_mica_071_info, + }, + { + /* Asus MeMO Pad 7 ME176C */ + .matches = { + DMI_EXACT_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."), + DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "ME176C"), + }, + .driver_data = (void *)&asus_me176c_info, + }, + { + /* Asus TF103C */ + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."), + DMI_MATCH(DMI_PRODUCT_NAME, "TF103C"), + }, + .driver_data = (void *)&asus_tf103c_info, + }, + { + /* Chuwi Hi8 (CWI509) */ + .matches = { + DMI_MATCH(DMI_BOARD_VENDOR, "Hampoo"), + DMI_MATCH(DMI_BOARD_NAME, "BYT-PA03C"), + DMI_MATCH(DMI_SYS_VENDOR, "ilife"), + DMI_MATCH(DMI_PRODUCT_NAME, "S806"), + }, + .driver_data = (void *)&chuwi_hi8_info, + }, + { + /* CZC P10T */ + .ident = "CZC ODEON TPC-10 (\"P10T\")", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "CZC"), + DMI_MATCH(DMI_PRODUCT_NAME, "ODEON*TPC-10"), + }, + .driver_data = (void *)&czc_p10t, + }, + { + /* CZC P10T variant */ + .ident = "ViewSonic ViewPad 10", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "ViewSonic"), + DMI_MATCH(DMI_PRODUCT_NAME, "VPAD10"), + }, + .driver_data = (void *)&czc_p10t, + }, + { + /* Lenovo Yoga Book X90F / X91F / X91L */ + .matches = { + /* Non exact match to match all versions */ + DMI_MATCH(DMI_PRODUCT_NAME, "Lenovo YB1-X9"), + }, + .driver_data = (void *)&lenovo_yogabook_x9x_info, + }, + { + /* + * Lenovo Yoga Tablet 2 830F/L or 1050F/L (The 8" and 10" + * Lenovo Yoga Tablet 2 use the same mainboard) + */ + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Intel Corp."), + DMI_MATCH(DMI_PRODUCT_NAME, "VALLEYVIEW C0 PLATFORM"), + DMI_MATCH(DMI_BOARD_NAME, "BYT-T FFD8"), + /* Partial match on beginning of BIOS version */ + DMI_MATCH(DMI_BIOS_VERSION, "BLADE_21"), + }, + .driver_data = (void *)&lenovo_yoga_tab2_830_1050_info, + }, + { + /* Lenovo Yoga Tab 3 Pro YT3-X90F */ + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Intel Corporation"), + DMI_MATCH(DMI_PRODUCT_NAME, "CHERRYVIEW D1 PLATFORM"), + DMI_MATCH(DMI_PRODUCT_VERSION, "Blade3-10A-001"), + }, + .driver_data = (void *)&lenovo_yt3_info, + }, + { + /* Medion Lifetab S10346 */ + .matches = { + DMI_MATCH(DMI_BOARD_VENDOR, "AMI Corporation"), + DMI_MATCH(DMI_BOARD_NAME, "Aptio CRB"), + /* Above strings are much too generic, also match on BIOS date */ + DMI_MATCH(DMI_BIOS_DATE, "10/22/2015"), + }, + .driver_data = (void *)&medion_lifetab_s10346_info, + }, + { + /* Nextbook Ares 8 */ + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Insyde"), + DMI_MATCH(DMI_PRODUCT_NAME, "M890BAP"), + }, + .driver_data = (void *)&nextbook_ares8_info, + }, + { + /* Whitelabel (sold as various brands) TM800A550L */ + .matches = { + DMI_MATCH(DMI_BOARD_VENDOR, "AMI Corporation"), + DMI_MATCH(DMI_BOARD_NAME, "Aptio CRB"), + /* Above strings are too generic, also match on BIOS version */ + DMI_MATCH(DMI_BIOS_VERSION, "ZY-8-BI-PX4S70VTR400-X423B-005-D"), + }, + .driver_data = (void *)&whitelabel_tm800a550l_info, + }, + { + /* Xiaomi Mi Pad 2 */ + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Xiaomi Inc"), + DMI_MATCH(DMI_PRODUCT_NAME, "Mipad2"), + }, + .driver_data = (void *)&xiaomi_mipad2_info, + }, + { } +}; +MODULE_DEVICE_TABLE(dmi, x86_android_tablet_ids); + +static int i2c_client_count; +static int pdev_count; +static int serdev_count; +static struct i2c_client **i2c_clients; +static struct platform_device **pdevs; +static struct serdev_device **serdevs; +static struct gpiod_lookup_table * const *gpiod_lookup_tables; +static const struct software_node *bat_swnode; +static void (*exit_handler)(void); + +static __init int x86_instantiate_i2c_client(const struct x86_dev_info *dev_info, + int idx) +{ + const struct x86_i2c_client_info *client_info = &dev_info->i2c_client_info[idx]; + struct i2c_board_info board_info = client_info->board_info; + struct i2c_adapter *adap; + acpi_handle handle; + acpi_status status; + + board_info.irq = x86_acpi_irq_helper_get(&client_info->irq_data); + if (board_info.irq < 0) + return board_info.irq; + + status = acpi_get_handle(NULL, client_info->adapter_path, &handle); + if (ACPI_FAILURE(status)) { + pr_err("Error could not get %s handle\n", client_info->adapter_path); + return -ENODEV; + } + + adap = i2c_acpi_find_adapter_by_handle(handle); + if (!adap) { + pr_err("error could not get %s adapter\n", client_info->adapter_path); + return -ENODEV; + } + + i2c_clients[idx] = i2c_new_client_device(adap, &board_info); + put_device(&adap->dev); + if (IS_ERR(i2c_clients[idx])) + return dev_err_probe(&adap->dev, PTR_ERR(i2c_clients[idx]), + "creating I2C-client %d\n", idx); + + return 0; +} + +static __init int x86_instantiate_serdev(const struct x86_serdev_info *info, int idx) +{ + struct acpi_device *ctrl_adev, *serdev_adev; + struct serdev_device *serdev; + struct device *ctrl_dev; + int ret = -ENODEV; + + ctrl_adev = acpi_dev_get_first_match_dev(info->ctrl_hid, info->ctrl_uid, -1); + if (!ctrl_adev) { + pr_err("error could not get %s/%s ctrl adev\n", + info->ctrl_hid, info->ctrl_uid); + return -ENODEV; + } + + serdev_adev = acpi_dev_get_first_match_dev(info->serdev_hid, NULL, -1); + if (!serdev_adev) { + pr_err("error could not get %s serdev adev\n", info->serdev_hid); + goto put_ctrl_adev; + } + + /* get_first_physical_node() returns a weak ref, no need to put() it */ + ctrl_dev = acpi_get_first_physical_node(ctrl_adev); + if (!ctrl_dev) { + pr_err("error could not get %s/%s ctrl physical dev\n", + info->ctrl_hid, info->ctrl_uid); + goto put_serdev_adev; + } + + /* ctrl_dev now points to the controller's parent, get the controller */ + ctrl_dev = device_find_child_by_name(ctrl_dev, info->ctrl_devname); + if (!ctrl_dev) { + pr_err("error could not get %s/%s %s ctrl dev\n", + info->ctrl_hid, info->ctrl_uid, info->ctrl_devname); + goto put_serdev_adev; + } + + serdev = serdev_device_alloc(to_serdev_controller(ctrl_dev)); + if (!serdev) { + ret = -ENOMEM; + goto put_serdev_adev; + } + + ACPI_COMPANION_SET(&serdev->dev, serdev_adev); + acpi_device_set_enumerated(serdev_adev); + + ret = serdev_device_add(serdev); + if (ret) { + dev_err(&serdev->dev, "error %d adding serdev\n", ret); + serdev_device_put(serdev); + goto put_serdev_adev; + } + + serdevs[idx] = serdev; + +put_serdev_adev: + acpi_dev_put(serdev_adev); +put_ctrl_adev: + acpi_dev_put(ctrl_adev); + return ret; +} + +static void x86_android_tablet_cleanup(void) +{ + int i; + + for (i = 0; i < serdev_count; i++) { + if (serdevs[i]) + serdev_device_remove(serdevs[i]); + } + + kfree(serdevs); + + for (i = 0; i < pdev_count; i++) + platform_device_unregister(pdevs[i]); + + kfree(pdevs); + + for (i = 0; i < i2c_client_count; i++) + i2c_unregister_device(i2c_clients[i]); + + kfree(i2c_clients); + + if (exit_handler) + exit_handler(); + + for (i = 0; gpiod_lookup_tables && gpiod_lookup_tables[i]; i++) + gpiod_remove_lookup_table(gpiod_lookup_tables[i]); + + software_node_unregister(bat_swnode); +} + +static __init int x86_android_tablet_init(void) +{ + const struct x86_dev_info *dev_info; + const struct dmi_system_id *id; + struct gpio_chip *chip; + int i, ret = 0; + + id = dmi_first_match(x86_android_tablet_ids); + if (!id) + return -ENODEV; + + dev_info = id->driver_data; + + /* + * The broken DSDTs on these devices often also include broken + * _AEI (ACPI Event Interrupt) handlers, disable these. + */ + if (dev_info->invalid_aei_gpiochip) { + chip = gpiochip_find(dev_info->invalid_aei_gpiochip, + gpiochip_find_match_label); + if (!chip) { + pr_err("error cannot find GPIO chip %s\n", dev_info->invalid_aei_gpiochip); + return -ENODEV; + } + acpi_gpiochip_free_interrupts(chip); + } + + /* + * Since this runs from module_init() it cannot use -EPROBE_DEFER, + * instead pre-load any modules which are listed as requirements. + */ + for (i = 0; dev_info->modules && dev_info->modules[i]; i++) + request_module(dev_info->modules[i]); + + bat_swnode = dev_info->bat_swnode; + if (bat_swnode) { + ret = software_node_register(bat_swnode); + if (ret) + return ret; + } + + gpiod_lookup_tables = dev_info->gpiod_lookup_tables; + for (i = 0; gpiod_lookup_tables && gpiod_lookup_tables[i]; i++) + gpiod_add_lookup_table(gpiod_lookup_tables[i]); + + if (dev_info->init) { + ret = dev_info->init(); + if (ret < 0) { + x86_android_tablet_cleanup(); + return ret; + } + exit_handler = dev_info->exit; + } + + i2c_clients = kcalloc(dev_info->i2c_client_count, sizeof(*i2c_clients), GFP_KERNEL); + if (!i2c_clients) { + x86_android_tablet_cleanup(); + return -ENOMEM; + } + + i2c_client_count = dev_info->i2c_client_count; + for (i = 0; i < i2c_client_count; i++) { + ret = x86_instantiate_i2c_client(dev_info, i); + if (ret < 0) { + x86_android_tablet_cleanup(); + return ret; + } + } + + pdevs = kcalloc(dev_info->pdev_count, sizeof(*pdevs), GFP_KERNEL); + if (!pdevs) { + x86_android_tablet_cleanup(); + return -ENOMEM; + } + + pdev_count = dev_info->pdev_count; + for (i = 0; i < pdev_count; i++) { + pdevs[i] = platform_device_register_full(&dev_info->pdev_info[i]); + if (IS_ERR(pdevs[i])) { + x86_android_tablet_cleanup(); + return PTR_ERR(pdevs[i]); + } + } + + serdevs = kcalloc(dev_info->serdev_count, sizeof(*serdevs), GFP_KERNEL); + if (!serdevs) { + x86_android_tablet_cleanup(); + return -ENOMEM; + } + + serdev_count = dev_info->serdev_count; + for (i = 0; i < serdev_count; i++) { + ret = x86_instantiate_serdev(&dev_info->serdev_info[i], i); + if (ret < 0) { + x86_android_tablet_cleanup(); + return ret; + } + } + + return 0; +} + +module_init(x86_android_tablet_init); +module_exit(x86_android_tablet_cleanup); + +MODULE_AUTHOR("Hans de Goede "); +MODULE_DESCRIPTION("X86 Android tablets DSDT fixups driver"); +MODULE_LICENSE("GPL"); -- cgit v1.2.3 From 62a5f689a068951de5ed8487ec7f0de52245bd92 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Sun, 19 Feb 2023 17:32:42 +0100 Subject: platform/x86: x86-android-tablets: Move core code into new core.c file Move the helpers to get IRQs + GPIOs as well as the core code for instantiating all the devices missing from ACPI into a new core.c file. Signed-off-by: Hans de Goede Reviewed-by: Andy Shevchenko Link: https://lore.kernel.org/r/20230301092331.7038-4-hdegoede@redhat.com --- drivers/platform/x86/x86-android-tablets/Makefile | 2 +- drivers/platform/x86/x86-android-tablets/core.c | 365 +++++++++++++++++++ .../x86-android-tablets/x86-android-tablets-main.c | 404 +-------------------- .../x86/x86-android-tablets/x86-android-tablets.h | 79 ++++ 4 files changed, 447 insertions(+), 403 deletions(-) create mode 100644 drivers/platform/x86/x86-android-tablets/core.c create mode 100644 drivers/platform/x86/x86-android-tablets/x86-android-tablets.h (limited to 'drivers/platform') diff --git a/drivers/platform/x86/x86-android-tablets/Makefile b/drivers/platform/x86/x86-android-tablets/Makefile index 52a0d6de5fcb..34016df3e54a 100644 --- a/drivers/platform/x86/x86-android-tablets/Makefile +++ b/drivers/platform/x86/x86-android-tablets/Makefile @@ -5,4 +5,4 @@ obj-$(CONFIG_X86_ANDROID_TABLETS) += x86-android-tablets.o -x86-android-tablets-y := x86-android-tablets-main.o +x86-android-tablets-y := core.o x86-android-tablets-main.o diff --git a/drivers/platform/x86/x86-android-tablets/core.c b/drivers/platform/x86/x86-android-tablets/core.c new file mode 100644 index 000000000000..ddd24881f392 --- /dev/null +++ b/drivers/platform/x86/x86-android-tablets/core.c @@ -0,0 +1,365 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * DMI based code to deal with broken DSDTs on X86 tablets which ship with + * Android as (part of) the factory image. The factory kernels shipped on these + * devices typically have a bunch of things hardcoded, rather than specified + * in their DSDT. + * + * Copyright (C) 2021-2023 Hans de Goede + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "x86-android-tablets.h" +/* For gpiochip_get_desc() which is EXPORT_SYMBOL_GPL() */ +#include "../../../gpio/gpiolib.h" +#include "../../../gpio/gpiolib-acpi.h" + +static int gpiochip_find_match_label(struct gpio_chip *gc, void *data) +{ + return gc->label && !strcmp(gc->label, data); +} + +int x86_android_tablet_get_gpiod(const char *label, int pin, struct gpio_desc **desc) +{ + struct gpio_desc *gpiod; + struct gpio_chip *chip; + + chip = gpiochip_find((void *)label, gpiochip_find_match_label); + if (!chip) { + pr_err("error cannot find GPIO chip %s\n", label); + return -ENODEV; + } + + gpiod = gpiochip_get_desc(chip, pin); + if (IS_ERR(gpiod)) { + pr_err("error %ld getting GPIO %s %d\n", PTR_ERR(gpiod), label, pin); + return PTR_ERR(gpiod); + } + + *desc = gpiod; + return 0; +} + +int x86_acpi_irq_helper_get(const struct x86_acpi_irq_data *data) +{ + struct irq_fwspec fwspec = { }; + struct irq_domain *domain; + struct acpi_device *adev; + struct gpio_desc *gpiod; + unsigned int irq_type; + acpi_handle handle; + acpi_status status; + int irq, ret; + + switch (data->type) { + case X86_ACPI_IRQ_TYPE_APIC: + /* + * The DSDT may already reference the GSI in a device skipped by + * acpi_quirk_skip_i2c_client_enumeration(). Unregister the GSI + * to avoid EBUSY errors in this case. + */ + acpi_unregister_gsi(data->index); + irq = acpi_register_gsi(NULL, data->index, data->trigger, data->polarity); + if (irq < 0) + pr_err("error %d getting APIC IRQ %d\n", irq, data->index); + + return irq; + case X86_ACPI_IRQ_TYPE_GPIOINT: + /* Like acpi_dev_gpio_irq_get(), but without parsing ACPI resources */ + ret = x86_android_tablet_get_gpiod(data->chip, data->index, &gpiod); + if (ret) + return ret; + + irq = gpiod_to_irq(gpiod); + if (irq < 0) { + pr_err("error %d getting IRQ %s %d\n", irq, data->chip, data->index); + return irq; + } + + irq_type = acpi_dev_get_irq_type(data->trigger, data->polarity); + if (irq_type != IRQ_TYPE_NONE && irq_type != irq_get_trigger_type(irq)) + irq_set_irq_type(irq, irq_type); + + return irq; + case X86_ACPI_IRQ_TYPE_PMIC: + status = acpi_get_handle(NULL, data->chip, &handle); + if (ACPI_FAILURE(status)) { + pr_err("error could not get %s handle\n", data->chip); + return -ENODEV; + } + + adev = acpi_fetch_acpi_dev(handle); + if (!adev) { + pr_err("error could not get %s adev\n", data->chip); + return -ENODEV; + } + + fwspec.fwnode = acpi_fwnode_handle(adev); + domain = irq_find_matching_fwspec(&fwspec, data->domain); + if (!domain) { + pr_err("error could not find IRQ domain for %s\n", data->chip); + return -ENODEV; + } + + return irq_create_mapping(domain, data->index); + default: + return 0; + } +} + +static int i2c_client_count; +static int pdev_count; +static int serdev_count; +static struct i2c_client **i2c_clients; +static struct platform_device **pdevs; +static struct serdev_device **serdevs; +static struct gpiod_lookup_table * const *gpiod_lookup_tables; +static const struct software_node *bat_swnode; +static void (*exit_handler)(void); + +static __init int x86_instantiate_i2c_client(const struct x86_dev_info *dev_info, + int idx) +{ + const struct x86_i2c_client_info *client_info = &dev_info->i2c_client_info[idx]; + struct i2c_board_info board_info = client_info->board_info; + struct i2c_adapter *adap; + acpi_handle handle; + acpi_status status; + + board_info.irq = x86_acpi_irq_helper_get(&client_info->irq_data); + if (board_info.irq < 0) + return board_info.irq; + + status = acpi_get_handle(NULL, client_info->adapter_path, &handle); + if (ACPI_FAILURE(status)) { + pr_err("Error could not get %s handle\n", client_info->adapter_path); + return -ENODEV; + } + + adap = i2c_acpi_find_adapter_by_handle(handle); + if (!adap) { + pr_err("error could not get %s adapter\n", client_info->adapter_path); + return -ENODEV; + } + + i2c_clients[idx] = i2c_new_client_device(adap, &board_info); + put_device(&adap->dev); + if (IS_ERR(i2c_clients[idx])) + return dev_err_probe(&adap->dev, PTR_ERR(i2c_clients[idx]), + "creating I2C-client %d\n", idx); + + return 0; +} + +static __init int x86_instantiate_serdev(const struct x86_serdev_info *info, int idx) +{ + struct acpi_device *ctrl_adev, *serdev_adev; + struct serdev_device *serdev; + struct device *ctrl_dev; + int ret = -ENODEV; + + ctrl_adev = acpi_dev_get_first_match_dev(info->ctrl_hid, info->ctrl_uid, -1); + if (!ctrl_adev) { + pr_err("error could not get %s/%s ctrl adev\n", + info->ctrl_hid, info->ctrl_uid); + return -ENODEV; + } + + serdev_adev = acpi_dev_get_first_match_dev(info->serdev_hid, NULL, -1); + if (!serdev_adev) { + pr_err("error could not get %s serdev adev\n", info->serdev_hid); + goto put_ctrl_adev; + } + + /* get_first_physical_node() returns a weak ref, no need to put() it */ + ctrl_dev = acpi_get_first_physical_node(ctrl_adev); + if (!ctrl_dev) { + pr_err("error could not get %s/%s ctrl physical dev\n", + info->ctrl_hid, info->ctrl_uid); + goto put_serdev_adev; + } + + /* ctrl_dev now points to the controller's parent, get the controller */ + ctrl_dev = device_find_child_by_name(ctrl_dev, info->ctrl_devname); + if (!ctrl_dev) { + pr_err("error could not get %s/%s %s ctrl dev\n", + info->ctrl_hid, info->ctrl_uid, info->ctrl_devname); + goto put_serdev_adev; + } + + serdev = serdev_device_alloc(to_serdev_controller(ctrl_dev)); + if (!serdev) { + ret = -ENOMEM; + goto put_serdev_adev; + } + + ACPI_COMPANION_SET(&serdev->dev, serdev_adev); + acpi_device_set_enumerated(serdev_adev); + + ret = serdev_device_add(serdev); + if (ret) { + dev_err(&serdev->dev, "error %d adding serdev\n", ret); + serdev_device_put(serdev); + goto put_serdev_adev; + } + + serdevs[idx] = serdev; + +put_serdev_adev: + acpi_dev_put(serdev_adev); +put_ctrl_adev: + acpi_dev_put(ctrl_adev); + return ret; +} + +static void x86_android_tablet_cleanup(void) +{ + int i; + + for (i = 0; i < serdev_count; i++) { + if (serdevs[i]) + serdev_device_remove(serdevs[i]); + } + + kfree(serdevs); + + for (i = 0; i < pdev_count; i++) + platform_device_unregister(pdevs[i]); + + kfree(pdevs); + + for (i = 0; i < i2c_client_count; i++) + i2c_unregister_device(i2c_clients[i]); + + kfree(i2c_clients); + + if (exit_handler) + exit_handler(); + + for (i = 0; gpiod_lookup_tables && gpiod_lookup_tables[i]; i++) + gpiod_remove_lookup_table(gpiod_lookup_tables[i]); + + software_node_unregister(bat_swnode); +} + +extern const struct dmi_system_id x86_android_tablet_ids[]; + +static __init int x86_android_tablet_init(void) +{ + const struct x86_dev_info *dev_info; + const struct dmi_system_id *id; + struct gpio_chip *chip; + int i, ret = 0; + + id = dmi_first_match(x86_android_tablet_ids); + if (!id) + return -ENODEV; + + dev_info = id->driver_data; + + /* + * The broken DSDTs on these devices often also include broken + * _AEI (ACPI Event Interrupt) handlers, disable these. + */ + if (dev_info->invalid_aei_gpiochip) { + chip = gpiochip_find(dev_info->invalid_aei_gpiochip, + gpiochip_find_match_label); + if (!chip) { + pr_err("error cannot find GPIO chip %s\n", dev_info->invalid_aei_gpiochip); + return -ENODEV; + } + acpi_gpiochip_free_interrupts(chip); + } + + /* + * Since this runs from module_init() it cannot use -EPROBE_DEFER, + * instead pre-load any modules which are listed as requirements. + */ + for (i = 0; dev_info->modules && dev_info->modules[i]; i++) + request_module(dev_info->modules[i]); + + bat_swnode = dev_info->bat_swnode; + if (bat_swnode) { + ret = software_node_register(bat_swnode); + if (ret) + return ret; + } + + gpiod_lookup_tables = dev_info->gpiod_lookup_tables; + for (i = 0; gpiod_lookup_tables && gpiod_lookup_tables[i]; i++) + gpiod_add_lookup_table(gpiod_lookup_tables[i]); + + if (dev_info->init) { + ret = dev_info->init(); + if (ret < 0) { + x86_android_tablet_cleanup(); + return ret; + } + exit_handler = dev_info->exit; + } + + i2c_clients = kcalloc(dev_info->i2c_client_count, sizeof(*i2c_clients), GFP_KERNEL); + if (!i2c_clients) { + x86_android_tablet_cleanup(); + return -ENOMEM; + } + + i2c_client_count = dev_info->i2c_client_count; + for (i = 0; i < i2c_client_count; i++) { + ret = x86_instantiate_i2c_client(dev_info, i); + if (ret < 0) { + x86_android_tablet_cleanup(); + return ret; + } + } + + pdevs = kcalloc(dev_info->pdev_count, sizeof(*pdevs), GFP_KERNEL); + if (!pdevs) { + x86_android_tablet_cleanup(); + return -ENOMEM; + } + + pdev_count = dev_info->pdev_count; + for (i = 0; i < pdev_count; i++) { + pdevs[i] = platform_device_register_full(&dev_info->pdev_info[i]); + if (IS_ERR(pdevs[i])) { + x86_android_tablet_cleanup(); + return PTR_ERR(pdevs[i]); + } + } + + serdevs = kcalloc(dev_info->serdev_count, sizeof(*serdevs), GFP_KERNEL); + if (!serdevs) { + x86_android_tablet_cleanup(); + return -ENOMEM; + } + + serdev_count = dev_info->serdev_count; + for (i = 0; i < serdev_count; i++) { + ret = x86_instantiate_serdev(&dev_info->serdev_info[i], i); + if (ret < 0) { + x86_android_tablet_cleanup(); + return ret; + } + } + + return 0; +} + +module_init(x86_android_tablet_init); +module_exit(x86_android_tablet_cleanup); + +MODULE_AUTHOR("Hans de Goede "); +MODULE_DESCRIPTION("X86 Android tablets DSDT fixups driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/platform/x86/x86-android-tablets/x86-android-tablets-main.c b/drivers/platform/x86/x86-android-tablets/x86-android-tablets-main.c index 08cc5e158460..7d3012ea7bb5 100644 --- a/drivers/platform/x86/x86-android-tablets/x86-android-tablets-main.c +++ b/drivers/platform/x86/x86-android-tablets/x86-android-tablets-main.c @@ -14,14 +14,8 @@ #include #include #include -#include -#include #include -#include #include -#include -#include -#include #include #include #include @@ -30,159 +24,9 @@ #include #include #include -#include #include -#include -/* For gpio_get_desc() which is EXPORT_SYMBOL_GPL() */ -#include "../../../gpio/gpiolib.h" -#include "../../../gpio/gpiolib-acpi.h" -/* - * Helper code to get Linux IRQ numbers given a description of the IRQ source - * (either IOAPIC index, or GPIO chip name + pin-number). - */ -enum x86_acpi_irq_type { - X86_ACPI_IRQ_TYPE_NONE, - X86_ACPI_IRQ_TYPE_APIC, - X86_ACPI_IRQ_TYPE_GPIOINT, - X86_ACPI_IRQ_TYPE_PMIC, -}; - -struct x86_acpi_irq_data { - char *chip; /* GPIO chip label (GPIOINT) or PMIC ACPI path (PMIC) */ - enum x86_acpi_irq_type type; - enum irq_domain_bus_token domain; - int index; - int trigger; /* ACPI_EDGE_SENSITIVE / ACPI_LEVEL_SENSITIVE */ - int polarity; /* ACPI_ACTIVE_HIGH / ACPI_ACTIVE_LOW / ACPI_ACTIVE_BOTH */ -}; - -static int gpiochip_find_match_label(struct gpio_chip *gc, void *data) -{ - return gc->label && !strcmp(gc->label, data); -} - -static int x86_android_tablet_get_gpiod(char *label, int pin, struct gpio_desc **desc) -{ - struct gpio_desc *gpiod; - struct gpio_chip *chip; - - chip = gpiochip_find(label, gpiochip_find_match_label); - if (!chip) { - pr_err("error cannot find GPIO chip %s\n", label); - return -ENODEV; - } - - gpiod = gpiochip_get_desc(chip, pin); - if (IS_ERR(gpiod)) { - pr_err("error %ld getting GPIO %s %d\n", PTR_ERR(gpiod), label, pin); - return PTR_ERR(gpiod); - } - - *desc = gpiod; - return 0; -} - -static int x86_acpi_irq_helper_get(const struct x86_acpi_irq_data *data) -{ - struct irq_fwspec fwspec = { }; - struct irq_domain *domain; - struct acpi_device *adev; - struct gpio_desc *gpiod; - unsigned int irq_type; - acpi_handle handle; - acpi_status status; - int irq, ret; - - switch (data->type) { - case X86_ACPI_IRQ_TYPE_APIC: - /* - * The DSDT may already reference the GSI in a device skipped by - * acpi_quirk_skip_i2c_client_enumeration(). Unregister the GSI - * to avoid EBUSY errors in this case. - */ - acpi_unregister_gsi(data->index); - irq = acpi_register_gsi(NULL, data->index, data->trigger, data->polarity); - if (irq < 0) - pr_err("error %d getting APIC IRQ %d\n", irq, data->index); - - return irq; - case X86_ACPI_IRQ_TYPE_GPIOINT: - /* Like acpi_dev_gpio_irq_get(), but without parsing ACPI resources */ - ret = x86_android_tablet_get_gpiod(data->chip, data->index, &gpiod); - if (ret) - return ret; - - irq = gpiod_to_irq(gpiod); - if (irq < 0) { - pr_err("error %d getting IRQ %s %d\n", irq, data->chip, data->index); - return irq; - } - - irq_type = acpi_dev_get_irq_type(data->trigger, data->polarity); - if (irq_type != IRQ_TYPE_NONE && irq_type != irq_get_trigger_type(irq)) - irq_set_irq_type(irq, irq_type); - - return irq; - case X86_ACPI_IRQ_TYPE_PMIC: - status = acpi_get_handle(NULL, data->chip, &handle); - if (ACPI_FAILURE(status)) { - pr_err("error could not get %s handle\n", data->chip); - return -ENODEV; - } - - adev = acpi_fetch_acpi_dev(handle); - if (!adev) { - pr_err("error could not get %s adev\n", data->chip); - return -ENODEV; - } - - fwspec.fwnode = acpi_fwnode_handle(adev); - domain = irq_find_matching_fwspec(&fwspec, data->domain); - if (!domain) { - pr_err("error could not find IRQ domain for %s\n", data->chip); - return -ENODEV; - } - - return irq_create_mapping(domain, data->index); - default: - return 0; - } -} - -struct x86_i2c_client_info { - struct i2c_board_info board_info; - char *adapter_path; - struct x86_acpi_irq_data irq_data; -}; - -struct x86_serdev_info { - const char *ctrl_hid; - const char *ctrl_uid; - const char *ctrl_devname; - /* - * ATM the serdev core only supports of or ACPI matching; and sofar all - * Android x86 tablets DSDTs have usable serdev nodes, but sometimes - * under the wrong controller. So we just tie the existing serdev ACPI - * node to the right controller. - */ - const char *serdev_hid; -}; - -struct x86_dev_info { - char *invalid_aei_gpiochip; - const char * const *modules; - const struct software_node *bat_swnode; - struct gpiod_lookup_table * const *gpiod_lookup_tables; - const struct x86_i2c_client_info *i2c_client_info; - const struct platform_device_info *pdev_info; - const struct x86_serdev_info *serdev_info; - int i2c_client_count; - int pdev_count; - int serdev_count; - int (*init)(void); - void (*exit)(void); -}; +#include "x86-android-tablets.h" /* Generic / shared charger / battery settings */ static const char * const tusb1211_chg_det_psy[] = { "tusb1211-charger-detect" }; @@ -1507,7 +1351,7 @@ static const struct x86_dev_info xiaomi_mipad2_info __initconst = { .i2c_client_count = ARRAY_SIZE(xiaomi_mipad2_i2c_clients), }; -static const struct dmi_system_id x86_android_tablet_ids[] __initconst = { +const struct dmi_system_id x86_android_tablet_ids[] __initconst = { { /* Acer Iconia One 7 B1-750 */ .matches = { @@ -1638,247 +1482,3 @@ static const struct dmi_system_id x86_android_tablet_ids[] __initconst = { { } }; MODULE_DEVICE_TABLE(dmi, x86_android_tablet_ids); - -static int i2c_client_count; -static int pdev_count; -static int serdev_count; -static struct i2c_client **i2c_clients; -static struct platform_device **pdevs; -static struct serdev_device **serdevs; -static struct gpiod_lookup_table * const *gpiod_lookup_tables; -static const struct software_node *bat_swnode; -static void (*exit_handler)(void); - -static __init int x86_instantiate_i2c_client(const struct x86_dev_info *dev_info, - int idx) -{ - const struct x86_i2c_client_info *client_info = &dev_info->i2c_client_info[idx]; - struct i2c_board_info board_info = client_info->board_info; - struct i2c_adapter *adap; - acpi_handle handle; - acpi_status status; - - board_info.irq = x86_acpi_irq_helper_get(&client_info->irq_data); - if (board_info.irq < 0) - return board_info.irq; - - status = acpi_get_handle(NULL, client_info->adapter_path, &handle); - if (ACPI_FAILURE(status)) { - pr_err("Error could not get %s handle\n", client_info->adapter_path); - return -ENODEV; - } - - adap = i2c_acpi_find_adapter_by_handle(handle); - if (!adap) { - pr_err("error could not get %s adapter\n", client_info->adapter_path); - return -ENODEV; - } - - i2c_clients[idx] = i2c_new_client_device(adap, &board_info); - put_device(&adap->dev); - if (IS_ERR(i2c_clients[idx])) - return dev_err_probe(&adap->dev, PTR_ERR(i2c_clients[idx]), - "creating I2C-client %d\n", idx); - - return 0; -} - -static __init int x86_instantiate_serdev(const struct x86_serdev_info *info, int idx) -{ - struct acpi_device *ctrl_adev, *serdev_adev; - struct serdev_device *serdev; - struct device *ctrl_dev; - int ret = -ENODEV; - - ctrl_adev = acpi_dev_get_first_match_dev(info->ctrl_hid, info->ctrl_uid, -1); - if (!ctrl_adev) { - pr_err("error could not get %s/%s ctrl adev\n", - info->ctrl_hid, info->ctrl_uid); - return -ENODEV; - } - - serdev_adev = acpi_dev_get_first_match_dev(info->serdev_hid, NULL, -1); - if (!serdev_adev) { - pr_err("error could not get %s serdev adev\n", info->serdev_hid); - goto put_ctrl_adev; - } - - /* get_first_physical_node() returns a weak ref, no need to put() it */ - ctrl_dev = acpi_get_first_physical_node(ctrl_adev); - if (!ctrl_dev) { - pr_err("error could not get %s/%s ctrl physical dev\n", - info->ctrl_hid, info->ctrl_uid); - goto put_serdev_adev; - } - - /* ctrl_dev now points to the controller's parent, get the controller */ - ctrl_dev = device_find_child_by_name(ctrl_dev, info->ctrl_devname); - if (!ctrl_dev) { - pr_err("error could not get %s/%s %s ctrl dev\n", - info->ctrl_hid, info->ctrl_uid, info->ctrl_devname); - goto put_serdev_adev; - } - - serdev = serdev_device_alloc(to_serdev_controller(ctrl_dev)); - if (!serdev) { - ret = -ENOMEM; - goto put_serdev_adev; - } - - ACPI_COMPANION_SET(&serdev->dev, serdev_adev); - acpi_device_set_enumerated(serdev_adev); - - ret = serdev_device_add(serdev); - if (ret) { - dev_err(&serdev->dev, "error %d adding serdev\n", ret); - serdev_device_put(serdev); - goto put_serdev_adev; - } - - serdevs[idx] = serdev; - -put_serdev_adev: - acpi_dev_put(serdev_adev); -put_ctrl_adev: - acpi_dev_put(ctrl_adev); - return ret; -} - -static void x86_android_tablet_cleanup(void) -{ - int i; - - for (i = 0; i < serdev_count; i++) { - if (serdevs[i]) - serdev_device_remove(serdevs[i]); - } - - kfree(serdevs); - - for (i = 0; i < pdev_count; i++) - platform_device_unregister(pdevs[i]); - - kfree(pdevs); - - for (i = 0; i < i2c_client_count; i++) - i2c_unregister_device(i2c_clients[i]); - - kfree(i2c_clients); - - if (exit_handler) - exit_handler(); - - for (i = 0; gpiod_lookup_tables && gpiod_lookup_tables[i]; i++) - gpiod_remove_lookup_table(gpiod_lookup_tables[i]); - - software_node_unregister(bat_swnode); -} - -static __init int x86_android_tablet_init(void) -{ - const struct x86_dev_info *dev_info; - const struct dmi_system_id *id; - struct gpio_chip *chip; - int i, ret = 0; - - id = dmi_first_match(x86_android_tablet_ids); - if (!id) - return -ENODEV; - - dev_info = id->driver_data; - - /* - * The broken DSDTs on these devices often also include broken - * _AEI (ACPI Event Interrupt) handlers, disable these. - */ - if (dev_info->invalid_aei_gpiochip) { - chip = gpiochip_find(dev_info->invalid_aei_gpiochip, - gpiochip_find_match_label); - if (!chip) { - pr_err("error cannot find GPIO chip %s\n", dev_info->invalid_aei_gpiochip); - return -ENODEV; - } - acpi_gpiochip_free_interrupts(chip); - } - - /* - * Since this runs from module_init() it cannot use -EPROBE_DEFER, - * instead pre-load any modules which are listed as requirements. - */ - for (i = 0; dev_info->modules && dev_info->modules[i]; i++) - request_module(dev_info->modules[i]); - - bat_swnode = dev_info->bat_swnode; - if (bat_swnode) { - ret = software_node_register(bat_swnode); - if (ret) - return ret; - } - - gpiod_lookup_tables = dev_info->gpiod_lookup_tables; - for (i = 0; gpiod_lookup_tables && gpiod_lookup_tables[i]; i++) - gpiod_add_lookup_table(gpiod_lookup_tables[i]); - - if (dev_info->init) { - ret = dev_info->init(); - if (ret < 0) { - x86_android_tablet_cleanup(); - return ret; - } - exit_handler = dev_info->exit; - } - - i2c_clients = kcalloc(dev_info->i2c_client_count, sizeof(*i2c_clients), GFP_KERNEL); - if (!i2c_clients) { - x86_android_tablet_cleanup(); - return -ENOMEM; - } - - i2c_client_count = dev_info->i2c_client_count; - for (i = 0; i < i2c_client_count; i++) { - ret = x86_instantiate_i2c_client(dev_info, i); - if (ret < 0) { - x86_android_tablet_cleanup(); - return ret; - } - } - - pdevs = kcalloc(dev_info->pdev_count, sizeof(*pdevs), GFP_KERNEL); - if (!pdevs) { - x86_android_tablet_cleanup(); - return -ENOMEM; - } - - pdev_count = dev_info->pdev_count; - for (i = 0; i < pdev_count; i++) { - pdevs[i] = platform_device_register_full(&dev_info->pdev_info[i]); - if (IS_ERR(pdevs[i])) { - x86_android_tablet_cleanup(); - return PTR_ERR(pdevs[i]); - } - } - - serdevs = kcalloc(dev_info->serdev_count, sizeof(*serdevs), GFP_KERNEL); - if (!serdevs) { - x86_android_tablet_cleanup(); - return -ENOMEM; - } - - serdev_count = dev_info->serdev_count; - for (i = 0; i < serdev_count; i++) { - ret = x86_instantiate_serdev(&dev_info->serdev_info[i], i); - if (ret < 0) { - x86_android_tablet_cleanup(); - return ret; - } - } - - return 0; -} - -module_init(x86_android_tablet_init); -module_exit(x86_android_tablet_cleanup); - -MODULE_AUTHOR("Hans de Goede "); -MODULE_DESCRIPTION("X86 Android tablets DSDT fixups driver"); -MODULE_LICENSE("GPL"); diff --git a/drivers/platform/x86/x86-android-tablets/x86-android-tablets.h b/drivers/platform/x86/x86-android-tablets/x86-android-tablets.h new file mode 100644 index 000000000000..434353572096 --- /dev/null +++ b/drivers/platform/x86/x86-android-tablets/x86-android-tablets.h @@ -0,0 +1,79 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later + * + * DMI based code to deal with broken DSDTs on X86 tablets which ship with + * Android as (part of) the factory image. The factory kernels shipped on these + * devices typically have a bunch of things hardcoded, rather than specified + * in their DSDT. + * + * Copyright (C) 2021-2023 Hans de Goede + */ +#ifndef __PDX86_X86_ANDROID_TABLETS_H +#define __PDX86_X86_ANDROID_TABLETS_H + +#include +#include + +struct gpio_desc; +struct gpiod_lookup_table; +struct platform_device_info; +struct software_node; + +/* + * Helpers to get Linux IRQ numbers given a description of the IRQ source + * (either IOAPIC index, or GPIO chip name + pin-number). + */ +enum x86_acpi_irq_type { + X86_ACPI_IRQ_TYPE_NONE, + X86_ACPI_IRQ_TYPE_APIC, + X86_ACPI_IRQ_TYPE_GPIOINT, + X86_ACPI_IRQ_TYPE_PMIC, +}; + +struct x86_acpi_irq_data { + char *chip; /* GPIO chip label (GPIOINT) or PMIC ACPI path (PMIC) */ + enum x86_acpi_irq_type type; + enum irq_domain_bus_token domain; + int index; + int trigger; /* ACPI_EDGE_SENSITIVE / ACPI_LEVEL_SENSITIVE */ + int polarity; /* ACPI_ACTIVE_HIGH / ACPI_ACTIVE_LOW / ACPI_ACTIVE_BOTH */ +}; + +/* Structs to describe devices to instantiate */ +struct x86_i2c_client_info { + struct i2c_board_info board_info; + char *adapter_path; + struct x86_acpi_irq_data irq_data; +}; + +struct x86_serdev_info { + const char *ctrl_hid; + const char *ctrl_uid; + const char *ctrl_devname; + /* + * ATM the serdev core only supports of or ACPI matching; and sofar all + * Android x86 tablets DSDTs have usable serdev nodes, but sometimes + * under the wrong controller. So we just tie the existing serdev ACPI + * node to the right controller. + */ + const char *serdev_hid; +}; + +struct x86_dev_info { + char *invalid_aei_gpiochip; + const char * const *modules; + const struct software_node *bat_swnode; + struct gpiod_lookup_table * const *gpiod_lookup_tables; + const struct x86_i2c_client_info *i2c_client_info; + const struct platform_device_info *pdev_info; + const struct x86_serdev_info *serdev_info; + int i2c_client_count; + int pdev_count; + int serdev_count; + int (*init)(void); + void (*exit)(void); +}; + +int x86_android_tablet_get_gpiod(const char *label, int pin, struct gpio_desc **desc); +int x86_acpi_irq_helper_get(const struct x86_acpi_irq_data *data); + +#endif -- cgit v1.2.3 From 3a75d1690b9adb116ca4d11578ff2fa358823382 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Sun, 19 Feb 2023 17:43:03 +0100 Subject: platform/x86: x86-android-tablets: Move DMI match table into its own dmi.c file In order to have a single MODULE_DEVICE_TABLE(dmi, ...), while allowing splitting the board descriptions into multiple files, add a new separate file for the DMI match table. Signed-off-by: Hans de Goede Reviewed-by: Andy Shevchenko Link: https://lore.kernel.org/r/20230301092331.7038-5-hdegoede@redhat.com --- drivers/platform/x86/x86-android-tablets/Makefile | 2 +- drivers/platform/x86/x86-android-tablets/core.c | 2 - drivers/platform/x86/x86-android-tablets/dmi.c | 148 +++++++++++++++++++ .../x86-android-tablets/x86-android-tablets-main.c | 160 ++------------------- .../x86/x86-android-tablets/x86-android-tablets.h | 20 +++ 5 files changed, 182 insertions(+), 150 deletions(-) create mode 100644 drivers/platform/x86/x86-android-tablets/dmi.c (limited to 'drivers/platform') diff --git a/drivers/platform/x86/x86-android-tablets/Makefile b/drivers/platform/x86/x86-android-tablets/Makefile index 34016df3e54a..230683cdd5be 100644 --- a/drivers/platform/x86/x86-android-tablets/Makefile +++ b/drivers/platform/x86/x86-android-tablets/Makefile @@ -5,4 +5,4 @@ obj-$(CONFIG_X86_ANDROID_TABLETS) += x86-android-tablets.o -x86-android-tablets-y := core.o x86-android-tablets-main.o +x86-android-tablets-y := core.o dmi.o x86-android-tablets-main.o diff --git a/drivers/platform/x86/x86-android-tablets/core.c b/drivers/platform/x86/x86-android-tablets/core.c index ddd24881f392..779d49c66987 100644 --- a/drivers/platform/x86/x86-android-tablets/core.c +++ b/drivers/platform/x86/x86-android-tablets/core.c @@ -253,8 +253,6 @@ static void x86_android_tablet_cleanup(void) software_node_unregister(bat_swnode); } -extern const struct dmi_system_id x86_android_tablet_ids[]; - static __init int x86_android_tablet_init(void) { const struct x86_dev_info *dev_info; diff --git a/drivers/platform/x86/x86-android-tablets/dmi.c b/drivers/platform/x86/x86-android-tablets/dmi.c new file mode 100644 index 000000000000..09bf8732b3cf --- /dev/null +++ b/drivers/platform/x86/x86-android-tablets/dmi.c @@ -0,0 +1,148 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * DMI based code to deal with broken DSDTs on X86 tablets which ship with + * Android as (part of) the factory image. The factory kernels shipped on these + * devices typically have a bunch of things hardcoded, rather than specified + * in their DSDT. + * + * Copyright (C) 2021-2023 Hans de Goede + */ + +#include +#include +#include +#include + +#include "x86-android-tablets.h" + +const struct dmi_system_id x86_android_tablet_ids[] __initconst = { + { + /* Acer Iconia One 7 B1-750 */ + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Insyde"), + DMI_MATCH(DMI_PRODUCT_NAME, "VESPA2"), + }, + .driver_data = (void *)&acer_b1_750_info, + }, + { + /* Advantech MICA-071 */ + .matches = { + DMI_EXACT_MATCH(DMI_SYS_VENDOR, "Advantech"), + DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "MICA-071"), + }, + .driver_data = (void *)&advantech_mica_071_info, + }, + { + /* Asus MeMO Pad 7 ME176C */ + .matches = { + DMI_EXACT_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."), + DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "ME176C"), + }, + .driver_data = (void *)&asus_me176c_info, + }, + { + /* Asus TF103C */ + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."), + DMI_MATCH(DMI_PRODUCT_NAME, "TF103C"), + }, + .driver_data = (void *)&asus_tf103c_info, + }, + { + /* Chuwi Hi8 (CWI509) */ + .matches = { + DMI_MATCH(DMI_BOARD_VENDOR, "Hampoo"), + DMI_MATCH(DMI_BOARD_NAME, "BYT-PA03C"), + DMI_MATCH(DMI_SYS_VENDOR, "ilife"), + DMI_MATCH(DMI_PRODUCT_NAME, "S806"), + }, + .driver_data = (void *)&chuwi_hi8_info, + }, + { + /* CZC P10T */ + .ident = "CZC ODEON TPC-10 (\"P10T\")", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "CZC"), + DMI_MATCH(DMI_PRODUCT_NAME, "ODEON*TPC-10"), + }, + .driver_data = (void *)&czc_p10t, + }, + { + /* CZC P10T variant */ + .ident = "ViewSonic ViewPad 10", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "ViewSonic"), + DMI_MATCH(DMI_PRODUCT_NAME, "VPAD10"), + }, + .driver_data = (void *)&czc_p10t, + }, + { + /* Lenovo Yoga Book X90F / X91F / X91L */ + .matches = { + /* Non exact match to match all versions */ + DMI_MATCH(DMI_PRODUCT_NAME, "Lenovo YB1-X9"), + }, + .driver_data = (void *)&lenovo_yogabook_x9x_info, + }, + { + /* + * Lenovo Yoga Tablet 2 830F/L or 1050F/L (The 8" and 10" + * Lenovo Yoga Tablet 2 use the same mainboard) + */ + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Intel Corp."), + DMI_MATCH(DMI_PRODUCT_NAME, "VALLEYVIEW C0 PLATFORM"), + DMI_MATCH(DMI_BOARD_NAME, "BYT-T FFD8"), + /* Partial match on beginning of BIOS version */ + DMI_MATCH(DMI_BIOS_VERSION, "BLADE_21"), + }, + .driver_data = (void *)&lenovo_yoga_tab2_830_1050_info, + }, + { + /* Lenovo Yoga Tab 3 Pro YT3-X90F */ + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Intel Corporation"), + DMI_MATCH(DMI_PRODUCT_NAME, "CHERRYVIEW D1 PLATFORM"), + DMI_MATCH(DMI_PRODUCT_VERSION, "Blade3-10A-001"), + }, + .driver_data = (void *)&lenovo_yt3_info, + }, + { + /* Medion Lifetab S10346 */ + .matches = { + DMI_MATCH(DMI_BOARD_VENDOR, "AMI Corporation"), + DMI_MATCH(DMI_BOARD_NAME, "Aptio CRB"), + /* Above strings are much too generic, also match on BIOS date */ + DMI_MATCH(DMI_BIOS_DATE, "10/22/2015"), + }, + .driver_data = (void *)&medion_lifetab_s10346_info, + }, + { + /* Nextbook Ares 8 */ + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Insyde"), + DMI_MATCH(DMI_PRODUCT_NAME, "M890BAP"), + }, + .driver_data = (void *)&nextbook_ares8_info, + }, + { + /* Whitelabel (sold as various brands) TM800A550L */ + .matches = { + DMI_MATCH(DMI_BOARD_VENDOR, "AMI Corporation"), + DMI_MATCH(DMI_BOARD_NAME, "Aptio CRB"), + /* Above strings are too generic, also match on BIOS version */ + DMI_MATCH(DMI_BIOS_VERSION, "ZY-8-BI-PX4S70VTR400-X423B-005-D"), + }, + .driver_data = (void *)&whitelabel_tm800a550l_info, + }, + { + /* Xiaomi Mi Pad 2 */ + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Xiaomi Inc"), + DMI_MATCH(DMI_PRODUCT_NAME, "Mipad2"), + }, + .driver_data = (void *)&xiaomi_mipad2_info, + }, + { } +}; +MODULE_DEVICE_TABLE(dmi, x86_android_tablet_ids); diff --git a/drivers/platform/x86/x86-android-tablets/x86-android-tablets-main.c b/drivers/platform/x86/x86-android-tablets/x86-android-tablets-main.c index 7d3012ea7bb5..4456cf5a2299 100644 --- a/drivers/platform/x86/x86-android-tablets/x86-android-tablets-main.c +++ b/drivers/platform/x86/x86-android-tablets/x86-android-tablets-main.c @@ -11,12 +11,10 @@ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt #include -#include #include #include #include #include -#include #include #include #include @@ -183,7 +181,7 @@ static struct gpiod_lookup_table * const acer_b1_750_gpios[] = { NULL }; -static const struct x86_dev_info acer_b1_750_info __initconst = { +const struct x86_dev_info acer_b1_750_info __initconst = { .i2c_client_info = acer_b1_750_i2c_clients, .i2c_client_count = ARRAY_SIZE(acer_b1_750_i2c_clients), .pdev_info = int3496_pdevs, @@ -235,7 +233,7 @@ static int __init advantech_mica_071_init(void) return 0; } -static const struct x86_dev_info advantech_mica_071_info __initconst = { +const struct x86_dev_info advantech_mica_071_info __initconst = { .pdev_info = advantech_mica_071_pdevs, .pdev_count = ARRAY_SIZE(advantech_mica_071_pdevs), .init = advantech_mica_071_init, @@ -416,7 +414,7 @@ static struct gpiod_lookup_table * const asus_me176c_gpios[] = { NULL }; -static const struct x86_dev_info asus_me176c_info __initconst = { +const struct x86_dev_info asus_me176c_info __initconst = { .i2c_client_info = asus_me176c_i2c_clients, .i2c_client_count = ARRAY_SIZE(asus_me176c_i2c_clients), .pdev_info = asus_me176c_tf103c_pdevs, @@ -561,7 +559,7 @@ static struct gpiod_lookup_table * const asus_tf103c_gpios[] = { NULL }; -static const struct x86_dev_info asus_tf103c_info __initconst = { +const struct x86_dev_info asus_tf103c_info __initconst = { .i2c_client_info = asus_tf103c_i2c_clients, .i2c_client_count = ARRAY_SIZE(asus_tf103c_i2c_clients), .pdev_info = asus_me176c_tf103c_pdevs, @@ -652,7 +650,7 @@ static int __init chuwi_hi8_init(void) return 0; } -static const struct x86_dev_info chuwi_hi8_info __initconst = { +const struct x86_dev_info chuwi_hi8_info __initconst = { .i2c_client_info = chuwi_hi8_i2c_clients, .i2c_client_count = ARRAY_SIZE(chuwi_hi8_i2c_clients), .init = chuwi_hi8_init, @@ -687,7 +685,7 @@ static int __init czc_p10t_init(void) return 0; } -static const struct x86_dev_info czc_p10t __initconst = { +const struct x86_dev_info czc_p10t __initconst = { .init = czc_p10t_init, }; @@ -705,7 +703,7 @@ static const struct x86_i2c_client_info lenovo_yogabook_x9x_i2c_clients[] __init }, }; -static const struct x86_dev_info lenovo_yogabook_x9x_info __initconst = { +const struct x86_dev_info lenovo_yogabook_x9x_info __initconst = { .i2c_client_info = lenovo_yogabook_x9x_i2c_clients, .i2c_client_count = ARRAY_SIZE(lenovo_yogabook_x9x_i2c_clients), }; @@ -816,7 +814,7 @@ static struct gpiod_lookup_table * const lenovo_yoga_tab2_830_1050_gpios[] = { static int __init lenovo_yoga_tab2_830_1050_init(void); static void lenovo_yoga_tab2_830_1050_exit(void); -static struct x86_dev_info lenovo_yoga_tab2_830_1050_info __initdata = { +struct x86_dev_info lenovo_yoga_tab2_830_1050_info __initdata = { .i2c_client_info = lenovo_yoga_tab2_830_1050_i2c_clients, /* i2c_client_count gets set by lenovo_yoga_tab2_830_1050_init() */ .pdev_info = int3496_pdevs, @@ -1081,7 +1079,7 @@ static int __init lenovo_yt3_init(void) return 0; } -static const struct x86_dev_info lenovo_yt3_info __initconst = { +const struct x86_dev_info lenovo_yt3_info __initconst = { .i2c_client_info = lenovo_yt3_i2c_clients, .i2c_client_count = ARRAY_SIZE(lenovo_yt3_i2c_clients), .init = lenovo_yt3_init, @@ -1163,7 +1161,7 @@ static struct gpiod_lookup_table * const medion_lifetab_s10346_gpios[] = { NULL }; -static const struct x86_dev_info medion_lifetab_s10346_info __initconst = { +const struct x86_dev_info medion_lifetab_s10346_info __initconst = { .i2c_client_info = medion_lifetab_s10346_i2c_clients, .i2c_client_count = ARRAY_SIZE(medion_lifetab_s10346_i2c_clients), .gpiod_lookup_tables = medion_lifetab_s10346_gpios, @@ -1229,7 +1227,7 @@ static struct gpiod_lookup_table * const nextbook_ares8_gpios[] = { NULL }; -static const struct x86_dev_info nextbook_ares8_info __initconst = { +const struct x86_dev_info nextbook_ares8_info __initconst = { .i2c_client_info = nextbook_ares8_i2c_clients, .i2c_client_count = ARRAY_SIZE(nextbook_ares8_i2c_clients), .pdev_info = int3496_pdevs, @@ -1312,7 +1310,7 @@ static struct gpiod_lookup_table * const whitelabel_tm800a550l_gpios[] = { NULL }; -static const struct x86_dev_info whitelabel_tm800a550l_info __initconst = { +const struct x86_dev_info whitelabel_tm800a550l_info __initconst = { .i2c_client_info = whitelabel_tm800a550l_i2c_clients, .i2c_client_count = ARRAY_SIZE(whitelabel_tm800a550l_i2c_clients), .gpiod_lookup_tables = whitelabel_tm800a550l_gpios, @@ -1346,139 +1344,7 @@ static const struct x86_i2c_client_info xiaomi_mipad2_i2c_clients[] __initconst }, }; -static const struct x86_dev_info xiaomi_mipad2_info __initconst = { +const struct x86_dev_info xiaomi_mipad2_info __initconst = { .i2c_client_info = xiaomi_mipad2_i2c_clients, .i2c_client_count = ARRAY_SIZE(xiaomi_mipad2_i2c_clients), }; - -const struct dmi_system_id x86_android_tablet_ids[] __initconst = { - { - /* Acer Iconia One 7 B1-750 */ - .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "Insyde"), - DMI_MATCH(DMI_PRODUCT_NAME, "VESPA2"), - }, - .driver_data = (void *)&acer_b1_750_info, - }, - { - /* Advantech MICA-071 */ - .matches = { - DMI_EXACT_MATCH(DMI_SYS_VENDOR, "Advantech"), - DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "MICA-071"), - }, - .driver_data = (void *)&advantech_mica_071_info, - }, - { - /* Asus MeMO Pad 7 ME176C */ - .matches = { - DMI_EXACT_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."), - DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "ME176C"), - }, - .driver_data = (void *)&asus_me176c_info, - }, - { - /* Asus TF103C */ - .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."), - DMI_MATCH(DMI_PRODUCT_NAME, "TF103C"), - }, - .driver_data = (void *)&asus_tf103c_info, - }, - { - /* Chuwi Hi8 (CWI509) */ - .matches = { - DMI_MATCH(DMI_BOARD_VENDOR, "Hampoo"), - DMI_MATCH(DMI_BOARD_NAME, "BYT-PA03C"), - DMI_MATCH(DMI_SYS_VENDOR, "ilife"), - DMI_MATCH(DMI_PRODUCT_NAME, "S806"), - }, - .driver_data = (void *)&chuwi_hi8_info, - }, - { - /* CZC P10T */ - .ident = "CZC ODEON TPC-10 (\"P10T\")", - .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "CZC"), - DMI_MATCH(DMI_PRODUCT_NAME, "ODEON*TPC-10"), - }, - .driver_data = (void *)&czc_p10t, - }, - { - /* CZC P10T variant */ - .ident = "ViewSonic ViewPad 10", - .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "ViewSonic"), - DMI_MATCH(DMI_PRODUCT_NAME, "VPAD10"), - }, - .driver_data = (void *)&czc_p10t, - }, - { - /* Lenovo Yoga Book X90F / X91F / X91L */ - .matches = { - /* Non exact match to match all versions */ - DMI_MATCH(DMI_PRODUCT_NAME, "Lenovo YB1-X9"), - }, - .driver_data = (void *)&lenovo_yogabook_x9x_info, - }, - { - /* - * Lenovo Yoga Tablet 2 830F/L or 1050F/L (The 8" and 10" - * Lenovo Yoga Tablet 2 use the same mainboard) - */ - .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "Intel Corp."), - DMI_MATCH(DMI_PRODUCT_NAME, "VALLEYVIEW C0 PLATFORM"), - DMI_MATCH(DMI_BOARD_NAME, "BYT-T FFD8"), - /* Partial match on beginning of BIOS version */ - DMI_MATCH(DMI_BIOS_VERSION, "BLADE_21"), - }, - .driver_data = (void *)&lenovo_yoga_tab2_830_1050_info, - }, - { - /* Lenovo Yoga Tab 3 Pro YT3-X90F */ - .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "Intel Corporation"), - DMI_MATCH(DMI_PRODUCT_NAME, "CHERRYVIEW D1 PLATFORM"), - DMI_MATCH(DMI_PRODUCT_VERSION, "Blade3-10A-001"), - }, - .driver_data = (void *)&lenovo_yt3_info, - }, - { - /* Medion Lifetab S10346 */ - .matches = { - DMI_MATCH(DMI_BOARD_VENDOR, "AMI Corporation"), - DMI_MATCH(DMI_BOARD_NAME, "Aptio CRB"), - /* Above strings are much too generic, also match on BIOS date */ - DMI_MATCH(DMI_BIOS_DATE, "10/22/2015"), - }, - .driver_data = (void *)&medion_lifetab_s10346_info, - }, - { - /* Nextbook Ares 8 */ - .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "Insyde"), - DMI_MATCH(DMI_PRODUCT_NAME, "M890BAP"), - }, - .driver_data = (void *)&nextbook_ares8_info, - }, - { - /* Whitelabel (sold as various brands) TM800A550L */ - .matches = { - DMI_MATCH(DMI_BOARD_VENDOR, "AMI Corporation"), - DMI_MATCH(DMI_BOARD_NAME, "Aptio CRB"), - /* Above strings are too generic, also match on BIOS version */ - DMI_MATCH(DMI_BIOS_VERSION, "ZY-8-BI-PX4S70VTR400-X423B-005-D"), - }, - .driver_data = (void *)&whitelabel_tm800a550l_info, - }, - { - /* Xiaomi Mi Pad 2 */ - .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "Xiaomi Inc"), - DMI_MATCH(DMI_PRODUCT_NAME, "Mipad2"), - }, - .driver_data = (void *)&xiaomi_mipad2_info, - }, - { } -}; -MODULE_DEVICE_TABLE(dmi, x86_android_tablet_ids); diff --git a/drivers/platform/x86/x86-android-tablets/x86-android-tablets.h b/drivers/platform/x86/x86-android-tablets/x86-android-tablets.h index 434353572096..19994b501903 100644 --- a/drivers/platform/x86/x86-android-tablets/x86-android-tablets.h +++ b/drivers/platform/x86/x86-android-tablets/x86-android-tablets.h @@ -76,4 +76,24 @@ struct x86_dev_info { int x86_android_tablet_get_gpiod(const char *label, int pin, struct gpio_desc **desc); int x86_acpi_irq_helper_get(const struct x86_acpi_irq_data *data); +/* + * Extern declarations of x86_dev_info structs so there can be a single + * MODULE_DEVICE_TABLE(dmi, ...), while splitting the board descriptions. + */ +extern const struct x86_dev_info acer_b1_750_info; +extern const struct x86_dev_info advantech_mica_071_info; +extern const struct x86_dev_info asus_me176c_info; +extern const struct x86_dev_info asus_tf103c_info; +extern const struct x86_dev_info chuwi_hi8_info; +extern const struct x86_dev_info czc_p10t; +extern const struct x86_dev_info lenovo_yogabook_x9x_info; +/* Not const as this gets modified by its init callback */ +extern struct x86_dev_info lenovo_yoga_tab2_830_1050_info; +extern const struct x86_dev_info lenovo_yt3_info; +extern const struct x86_dev_info medion_lifetab_s10346_info; +extern const struct x86_dev_info nextbook_ares8_info; +extern const struct x86_dev_info whitelabel_tm800a550l_info; +extern const struct x86_dev_info xiaomi_mipad2_info; +extern const struct dmi_system_id x86_android_tablet_ids[]; + #endif -- cgit v1.2.3 From 4ed14c23e1e82fbfb06cb37b9197ca56bbdf39f3 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Sun, 19 Feb 2023 20:20:45 +0100 Subject: platform/x86: x86-android-tablets: Move shared power-supply fw-nodes to a separate file Move the shared power-supply fw-nodes and related files to a new separate shared-psy-info.c file. Signed-off-by: Hans de Goede Reviewed-by: Andy Shevchenko Link: https://lore.kernel.org/r/20230301092331.7038-6-hdegoede@redhat.com --- drivers/platform/x86/x86-android-tablets/Makefile | 2 +- .../x86/x86-android-tablets/shared-psy-info.c | 100 ++++++++++++++++++++ .../x86/x86-android-tablets/shared-psy-info.h | 32 +++++++ .../x86-android-tablets/x86-android-tablets-main.c | 101 ++------------------- 4 files changed, 142 insertions(+), 93 deletions(-) create mode 100644 drivers/platform/x86/x86-android-tablets/shared-psy-info.c create mode 100644 drivers/platform/x86/x86-android-tablets/shared-psy-info.h (limited to 'drivers/platform') diff --git a/drivers/platform/x86/x86-android-tablets/Makefile b/drivers/platform/x86/x86-android-tablets/Makefile index 230683cdd5be..d4e0da886790 100644 --- a/drivers/platform/x86/x86-android-tablets/Makefile +++ b/drivers/platform/x86/x86-android-tablets/Makefile @@ -5,4 +5,4 @@ obj-$(CONFIG_X86_ANDROID_TABLETS) += x86-android-tablets.o -x86-android-tablets-y := core.o dmi.o x86-android-tablets-main.o +x86-android-tablets-y := core.o dmi.o shared-psy-info.o x86-android-tablets-main.o diff --git a/drivers/platform/x86/x86-android-tablets/shared-psy-info.c b/drivers/platform/x86/x86-android-tablets/shared-psy-info.c new file mode 100644 index 000000000000..d2d0aa51bc3f --- /dev/null +++ b/drivers/platform/x86/x86-android-tablets/shared-psy-info.c @@ -0,0 +1,100 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Shared psy info for X86 tablets which ship with Android as the factory image + * and which have broken DSDT tables. The factory kernels shipped on these + * devices typically have a bunch of things hardcoded, rather than specified + * in their DSDT. + * + * Copyright (C) 2021-2023 Hans de Goede + */ + +#include +#include +#include +#include +#include + +#include "shared-psy-info.h" + +/* Generic / shared charger / battery settings */ +const char * const tusb1211_chg_det_psy[] = { "tusb1211-charger-detect" }; +const char * const bq24190_psy[] = { "bq24190-charger" }; +const char * const bq25890_psy[] = { "bq25890-charger-0" }; + +static const struct property_entry fg_bq24190_supply_props[] = { + PROPERTY_ENTRY_STRING_ARRAY("supplied-from", bq24190_psy), + { } +}; + +const struct software_node fg_bq24190_supply_node = { + .properties = fg_bq24190_supply_props, +}; + +static const struct property_entry fg_bq25890_supply_props[] = { + PROPERTY_ENTRY_STRING_ARRAY("supplied-from", bq25890_psy), + { } +}; + +const struct software_node fg_bq25890_supply_node = { + .properties = fg_bq25890_supply_props, +}; + +/* LiPo HighVoltage (max 4.35V) settings used by most devs with a HV bat. */ +static const struct property_entry generic_lipo_hv_4v35_battery_props[] = { + PROPERTY_ENTRY_STRING("compatible", "simple-battery"), + PROPERTY_ENTRY_STRING("device-chemistry", "lithium-ion"), + PROPERTY_ENTRY_U32("precharge-current-microamp", 256000), + PROPERTY_ENTRY_U32("charge-term-current-microamp", 128000), + PROPERTY_ENTRY_U32("constant-charge-current-max-microamp", 1856000), + PROPERTY_ENTRY_U32("constant-charge-voltage-max-microvolt", 4352000), + PROPERTY_ENTRY_U32("factory-internal-resistance-micro-ohms", 150000), + { } +}; + +const struct software_node generic_lipo_hv_4v35_battery_node = { + .properties = generic_lipo_hv_4v35_battery_props, +}; + +/* For enabling the bq24190 5V boost based on id-pin */ +static struct regulator_consumer_supply intel_int3496_consumer = { + .supply = "vbus", + .dev_name = "intel-int3496", +}; + +static const struct regulator_init_data bq24190_vbus_init_data = { + .constraints = { + .name = "bq24190_vbus", + .valid_ops_mask = REGULATOR_CHANGE_STATUS, + }, + .consumer_supplies = &intel_int3496_consumer, + .num_consumer_supplies = 1, +}; + +struct bq24190_platform_data bq24190_pdata = { + .regulator_init_data = &bq24190_vbus_init_data, +}; + +const char * const bq24190_modules[] __initconst = { + "intel_crystal_cove_charger", /* For the bq24190 IRQ */ + "bq24190_charger", /* For the Vbus regulator for intel-int3496 */ + NULL +}; + +/* Generic pdevs array and gpio-lookups for micro USB ID pin handling */ +const struct platform_device_info int3496_pdevs[] __initconst = { + { + /* For micro USB ID pin handling */ + .name = "intel-int3496", + .id = PLATFORM_DEVID_NONE, + }, +}; + +struct gpiod_lookup_table int3496_reference_gpios = { + .dev_id = "intel-int3496", + .table = { + GPIO_LOOKUP("INT33FC:01", 15, "vbus", GPIO_ACTIVE_HIGH), + GPIO_LOOKUP("INT33FC:02", 1, "mux", GPIO_ACTIVE_HIGH), + GPIO_LOOKUP("INT33FC:02", 18, "id", GPIO_ACTIVE_HIGH), + { } + }, +}; diff --git a/drivers/platform/x86/x86-android-tablets/shared-psy-info.h b/drivers/platform/x86/x86-android-tablets/shared-psy-info.h new file mode 100644 index 000000000000..c2d2968cddc2 --- /dev/null +++ b/drivers/platform/x86/x86-android-tablets/shared-psy-info.h @@ -0,0 +1,32 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later + * + * Shared psy info for X86 tablets which ship with Android as the factory image + * and which have broken DSDT tables. The factory kernels shipped on these + * devices typically have a bunch of things hardcoded, rather than specified + * in their DSDT. + * + * Copyright (C) 2021-2023 Hans de Goede + */ +#ifndef __PDX86_SHARED_PSY_INFO_H +#define __PDX86_SHARED_PSY_INFO_H + +struct bq24190_platform_data; +struct gpiod_lookup_table; +struct platform_device_info; +struct software_node; + +extern const char * const tusb1211_chg_det_psy[]; +extern const char * const bq24190_psy[]; +extern const char * const bq25890_psy[]; + +extern const struct software_node fg_bq24190_supply_node; +extern const struct software_node fg_bq25890_supply_node; +extern const struct software_node generic_lipo_hv_4v35_battery_node; + +extern struct bq24190_platform_data bq24190_pdata; +extern const char * const bq24190_modules[]; + +extern const struct platform_device_info int3496_pdevs[]; +extern struct gpiod_lookup_table int3496_reference_gpios; + +#endif diff --git a/drivers/platform/x86/x86-android-tablets/x86-android-tablets-main.c b/drivers/platform/x86/x86-android-tablets/x86-android-tablets-main.c index 4456cf5a2299..225502794a89 100644 --- a/drivers/platform/x86/x86-android-tablets/x86-android-tablets-main.c +++ b/drivers/platform/x86/x86-android-tablets/x86-android-tablets-main.c @@ -19,86 +19,13 @@ #include #include #include -#include #include #include #include +#include "shared-psy-info.h" #include "x86-android-tablets.h" -/* Generic / shared charger / battery settings */ -static const char * const tusb1211_chg_det_psy[] = { "tusb1211-charger-detect" }; -static const char * const bq24190_psy[] = { "bq24190-charger" }; -static const char * const bq25890_psy[] = { "bq25890-charger-0" }; - -static const struct property_entry fg_bq24190_supply_props[] = { - PROPERTY_ENTRY_STRING_ARRAY("supplied-from", bq24190_psy), - { } -}; - -static const struct software_node fg_bq24190_supply_node = { - .properties = fg_bq24190_supply_props, -}; - -static const struct property_entry fg_bq25890_supply_props[] = { - PROPERTY_ENTRY_STRING_ARRAY("supplied-from", bq25890_psy), - { } -}; - -static const struct software_node fg_bq25890_supply_node = { - .properties = fg_bq25890_supply_props, -}; - -/* LiPo HighVoltage (max 4.35V) settings used by most devs with a HV bat. */ -static const struct property_entry generic_lipo_hv_4v35_battery_props[] = { - PROPERTY_ENTRY_STRING("compatible", "simple-battery"), - PROPERTY_ENTRY_STRING("device-chemistry", "lithium-ion"), - PROPERTY_ENTRY_U32("precharge-current-microamp", 256000), - PROPERTY_ENTRY_U32("charge-term-current-microamp", 128000), - PROPERTY_ENTRY_U32("constant-charge-current-max-microamp", 1856000), - PROPERTY_ENTRY_U32("constant-charge-voltage-max-microvolt", 4352000), - PROPERTY_ENTRY_U32("factory-internal-resistance-micro-ohms", 150000), - { } -}; - -static const struct software_node generic_lipo_hv_4v35_battery_node = { - .properties = generic_lipo_hv_4v35_battery_props, -}; - -/* For enabling the bq24190 5V boost based on id-pin */ -static struct regulator_consumer_supply intel_int3496_consumer = { - .supply = "vbus", - .dev_name = "intel-int3496", -}; - -static const struct regulator_init_data bq24190_vbus_init_data = { - .constraints = { - .name = "bq24190_vbus", - .valid_ops_mask = REGULATOR_CHANGE_STATUS, - }, - .consumer_supplies = &intel_int3496_consumer, - .num_consumer_supplies = 1, -}; - -static struct bq24190_platform_data bq24190_pdata = { - .regulator_init_data = &bq24190_vbus_init_data, -}; - -static const char * const bq24190_modules[] __initconst = { - "intel_crystal_cove_charger", /* For the bq24190 IRQ */ - "bq24190_charger", /* For the Vbus regulator for intel-int3496 */ - NULL -}; - -/* Generic pdevs array and gpio-lookups for micro USB ID pin handling */ -static const struct platform_device_info int3496_pdevs[] __initconst = { - { - /* For micro USB ID pin handling */ - .name = "intel-int3496", - .id = PLATFORM_DEVID_NONE, - }, -}; - static struct gpiod_lookup_table int3496_gpo2_pin22_gpios = { .dev_id = "intel-int3496", .table = { @@ -107,16 +34,6 @@ static struct gpiod_lookup_table int3496_gpo2_pin22_gpios = { }, }; -static struct gpiod_lookup_table int3496_reference_gpios = { - .dev_id = "intel-int3496", - .table = { - GPIO_LOOKUP("INT33FC:01", 15, "vbus", GPIO_ACTIVE_HIGH), - GPIO_LOOKUP("INT33FC:02", 1, "mux", GPIO_ACTIVE_HIGH), - GPIO_LOOKUP("INT33FC:02", 18, "id", GPIO_ACTIVE_HIGH), - { } - }, -}; - /* Acer Iconia One 7 B1-750 has an Android factory img with everything hardcoded */ static const char * const acer_b1_750_mount_matrix[] = { "-1", "0", "0", @@ -185,7 +102,7 @@ const struct x86_dev_info acer_b1_750_info __initconst = { .i2c_client_info = acer_b1_750_i2c_clients, .i2c_client_count = ARRAY_SIZE(acer_b1_750_i2c_clients), .pdev_info = int3496_pdevs, - .pdev_count = ARRAY_SIZE(int3496_pdevs), + .pdev_count = 1, .gpiod_lookup_tables = acer_b1_750_gpios, }; @@ -301,7 +218,7 @@ static const struct software_node asus_me176c_accel_node = { }; static const struct property_entry asus_me176c_bq24190_props[] = { - PROPERTY_ENTRY_STRING_ARRAY("supplied-from", tusb1211_chg_det_psy), + PROPERTY_ENTRY_STRING_ARRAY_LEN("supplied-from", tusb1211_chg_det_psy, 1), PROPERTY_ENTRY_REF("monitored-battery", &generic_lipo_hv_4v35_battery_node), PROPERTY_ENTRY_U32("ti,system-minimum-microvolt", 3600000), PROPERTY_ENTRY_BOOL("omit-battery-class"), @@ -314,7 +231,7 @@ static const struct software_node asus_me176c_bq24190_node = { }; static const struct property_entry asus_me176c_ug3105_props[] = { - PROPERTY_ENTRY_STRING_ARRAY("supplied-from", bq24190_psy), + PROPERTY_ENTRY_STRING_ARRAY_LEN("supplied-from", bq24190_psy, 1), PROPERTY_ENTRY_REF("monitored-battery", &generic_lipo_hv_4v35_battery_node), PROPERTY_ENTRY_U32("upisemi,rsns-microohm", 10000), { } @@ -469,7 +386,7 @@ static const struct software_node asus_tf103c_battery_node = { }; static const struct property_entry asus_tf103c_bq24190_props[] = { - PROPERTY_ENTRY_STRING_ARRAY("supplied-from", tusb1211_chg_det_psy), + PROPERTY_ENTRY_STRING_ARRAY_LEN("supplied-from", tusb1211_chg_det_psy, 1), PROPERTY_ENTRY_REF("monitored-battery", &asus_tf103c_battery_node), PROPERTY_ENTRY_U32("ti,system-minimum-microvolt", 3600000), PROPERTY_ENTRY_BOOL("omit-battery-class"), @@ -482,7 +399,7 @@ static const struct software_node asus_tf103c_bq24190_node = { }; static const struct property_entry asus_tf103c_ug3105_props[] = { - PROPERTY_ENTRY_STRING_ARRAY("supplied-from", bq24190_psy), + PROPERTY_ENTRY_STRING_ARRAY_LEN("supplied-from", bq24190_psy, 1), PROPERTY_ENTRY_REF("monitored-battery", &asus_tf103c_battery_node), PROPERTY_ENTRY_U32("upisemi,rsns-microohm", 5000), { } @@ -710,7 +627,7 @@ const struct x86_dev_info lenovo_yogabook_x9x_info __initconst = { /* Lenovo Yoga Tablet 2 1050F/L's Android factory img has everything hardcoded */ static const struct property_entry lenovo_yoga_tab2_830_1050_bq24190_props[] = { - PROPERTY_ENTRY_STRING_ARRAY("supplied-from", tusb1211_chg_det_psy), + PROPERTY_ENTRY_STRING_ARRAY_LEN("supplied-from", tusb1211_chg_det_psy, 1), PROPERTY_ENTRY_REF("monitored-battery", &generic_lipo_hv_4v35_battery_node), PROPERTY_ENTRY_BOOL("omit-battery-class"), PROPERTY_ENTRY_BOOL("disable-reset"), @@ -818,7 +735,7 @@ struct x86_dev_info lenovo_yoga_tab2_830_1050_info __initdata = { .i2c_client_info = lenovo_yoga_tab2_830_1050_i2c_clients, /* i2c_client_count gets set by lenovo_yoga_tab2_830_1050_init() */ .pdev_info = int3496_pdevs, - .pdev_count = ARRAY_SIZE(int3496_pdevs), + .pdev_count = 1, .gpiod_lookup_tables = lenovo_yoga_tab2_830_1050_gpios, .bat_swnode = &generic_lipo_hv_4v35_battery_node, .modules = bq24190_modules, @@ -1231,7 +1148,7 @@ const struct x86_dev_info nextbook_ares8_info __initconst = { .i2c_client_info = nextbook_ares8_i2c_clients, .i2c_client_count = ARRAY_SIZE(nextbook_ares8_i2c_clients), .pdev_info = int3496_pdevs, - .pdev_count = ARRAY_SIZE(int3496_pdevs), + .pdev_count = 1, .gpiod_lookup_tables = nextbook_ares8_gpios, .invalid_aei_gpiochip = "INT33FC:02", }; -- cgit v1.2.3 From 97abac9c8b893822255c7847823bb65b6c7efb6a Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Sun, 19 Feb 2023 20:22:39 +0100 Subject: platform/x86: x86-android-tablets: Move Asus tablets to their own file Move the info for the Asus tablets to their own asus.c file. Signed-off-by: Hans de Goede Reviewed-by: Andy Shevchenko Link: https://lore.kernel.org/r/20230301092331.7038-7-hdegoede@redhat.com --- drivers/platform/x86/x86-android-tablets/Makefile | 2 +- drivers/platform/x86/x86-android-tablets/asus.c | 358 +++++++++++++++++++++ .../x86-android-tablets/x86-android-tablets-main.c | 340 ------------------- 3 files changed, 359 insertions(+), 341 deletions(-) create mode 100644 drivers/platform/x86/x86-android-tablets/asus.c (limited to 'drivers/platform') diff --git a/drivers/platform/x86/x86-android-tablets/Makefile b/drivers/platform/x86/x86-android-tablets/Makefile index d4e0da886790..76f94831bf97 100644 --- a/drivers/platform/x86/x86-android-tablets/Makefile +++ b/drivers/platform/x86/x86-android-tablets/Makefile @@ -5,4 +5,4 @@ obj-$(CONFIG_X86_ANDROID_TABLETS) += x86-android-tablets.o -x86-android-tablets-y := core.o dmi.o shared-psy-info.o x86-android-tablets-main.o +x86-android-tablets-y := core.o dmi.o shared-psy-info.o asus.o x86-android-tablets-main.o diff --git a/drivers/platform/x86/x86-android-tablets/asus.c b/drivers/platform/x86/x86-android-tablets/asus.c new file mode 100644 index 000000000000..74c3b10e9ca7 --- /dev/null +++ b/drivers/platform/x86/x86-android-tablets/asus.c @@ -0,0 +1,358 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Board info for Asus X86 tablets which ship with Android as the factory image + * and which have broken DSDT tables. The factory kernels shipped on these + * devices typically have a bunch of things hardcoded, rather than specified + * in their DSDT. + * + * Copyright (C) 2021-2023 Hans de Goede + */ + +#include +#include +#include +#include +#include + +#include "shared-psy-info.h" +#include "x86-android-tablets.h" + +/* Asus ME176C and TF103C tablets shared data */ +static struct gpiod_lookup_table int3496_gpo2_pin22_gpios = { + .dev_id = "intel-int3496", + .table = { + GPIO_LOOKUP("INT33FC:02", 22, "id", GPIO_ACTIVE_HIGH), + { } + }, +}; + +static struct gpio_keys_button asus_me176c_tf103c_lid = { + .code = SW_LID, + /* .gpio gets filled in by asus_me176c_tf103c_init() */ + .active_low = true, + .desc = "lid_sw", + .type = EV_SW, + .wakeup = true, + .debounce_interval = 50, +}; + +static const struct gpio_keys_platform_data asus_me176c_tf103c_lid_pdata __initconst = { + .buttons = &asus_me176c_tf103c_lid, + .nbuttons = 1, + .name = "lid_sw", +}; + +static const struct platform_device_info asus_me176c_tf103c_pdevs[] __initconst = { + { + .name = "gpio-keys", + .id = PLATFORM_DEVID_AUTO, + .data = &asus_me176c_tf103c_lid_pdata, + .size_data = sizeof(asus_me176c_tf103c_lid_pdata), + }, + { + /* For micro USB ID pin handling */ + .name = "intel-int3496", + .id = PLATFORM_DEVID_NONE, + }, +}; + +static int __init asus_me176c_tf103c_init(void) +{ + struct gpio_desc *gpiod; + int ret; + + ret = x86_android_tablet_get_gpiod("INT33FC:02", 12, &gpiod); + if (ret < 0) + return ret; + asus_me176c_tf103c_lid.gpio = desc_to_gpio(gpiod); + + return 0; +} + + +/* Asus ME176C tablets have an Android factory img with everything hardcoded */ +static const char * const asus_me176c_accel_mount_matrix[] = { + "-1", "0", "0", + "0", "1", "0", + "0", "0", "1" +}; + +static const struct property_entry asus_me176c_accel_props[] = { + PROPERTY_ENTRY_STRING_ARRAY("mount-matrix", asus_me176c_accel_mount_matrix), + { } +}; + +static const struct software_node asus_me176c_accel_node = { + .properties = asus_me176c_accel_props, +}; + +static const struct property_entry asus_me176c_bq24190_props[] = { + PROPERTY_ENTRY_STRING_ARRAY_LEN("supplied-from", tusb1211_chg_det_psy, 1), + PROPERTY_ENTRY_REF("monitored-battery", &generic_lipo_hv_4v35_battery_node), + PROPERTY_ENTRY_U32("ti,system-minimum-microvolt", 3600000), + PROPERTY_ENTRY_BOOL("omit-battery-class"), + PROPERTY_ENTRY_BOOL("disable-reset"), + { } +}; + +static const struct software_node asus_me176c_bq24190_node = { + .properties = asus_me176c_bq24190_props, +}; + +static const struct property_entry asus_me176c_ug3105_props[] = { + PROPERTY_ENTRY_STRING_ARRAY_LEN("supplied-from", bq24190_psy, 1), + PROPERTY_ENTRY_REF("monitored-battery", &generic_lipo_hv_4v35_battery_node), + PROPERTY_ENTRY_U32("upisemi,rsns-microohm", 10000), + { } +}; + +static const struct software_node asus_me176c_ug3105_node = { + .properties = asus_me176c_ug3105_props, +}; + +static const struct x86_i2c_client_info asus_me176c_i2c_clients[] __initconst = { + { + /* bq24297 battery charger */ + .board_info = { + .type = "bq24190", + .addr = 0x6b, + .dev_name = "bq24297", + .swnode = &asus_me176c_bq24190_node, + .platform_data = &bq24190_pdata, + }, + .adapter_path = "\\_SB_.I2C1", + .irq_data = { + .type = X86_ACPI_IRQ_TYPE_PMIC, + .chip = "\\_SB_.I2C7.PMIC", + .domain = DOMAIN_BUS_WAKEUP, + .index = 0, + }, + }, { + /* ug3105 battery monitor */ + .board_info = { + .type = "ug3105", + .addr = 0x70, + .dev_name = "ug3105", + .swnode = &asus_me176c_ug3105_node, + }, + .adapter_path = "\\_SB_.I2C1", + }, { + /* ak09911 compass */ + .board_info = { + .type = "ak09911", + .addr = 0x0c, + .dev_name = "ak09911", + }, + .adapter_path = "\\_SB_.I2C5", + }, { + /* kxtj21009 accel */ + .board_info = { + .type = "kxtj21009", + .addr = 0x0f, + .dev_name = "kxtj21009", + .swnode = &asus_me176c_accel_node, + }, + .adapter_path = "\\_SB_.I2C5", + .irq_data = { + .type = X86_ACPI_IRQ_TYPE_APIC, + .index = 0x44, + .trigger = ACPI_EDGE_SENSITIVE, + .polarity = ACPI_ACTIVE_LOW, + }, + }, { + /* goodix touchscreen */ + .board_info = { + .type = "GDIX1001:00", + .addr = 0x14, + .dev_name = "goodix_ts", + }, + .adapter_path = "\\_SB_.I2C6", + .irq_data = { + .type = X86_ACPI_IRQ_TYPE_APIC, + .index = 0x45, + .trigger = ACPI_EDGE_SENSITIVE, + .polarity = ACPI_ACTIVE_LOW, + }, + }, +}; + +static const struct x86_serdev_info asus_me176c_serdevs[] __initconst = { + { + .ctrl_hid = "80860F0A", + .ctrl_uid = "2", + .ctrl_devname = "serial0", + .serdev_hid = "BCM2E3A", + }, +}; + +static struct gpiod_lookup_table asus_me176c_goodix_gpios = { + .dev_id = "i2c-goodix_ts", + .table = { + GPIO_LOOKUP("INT33FC:00", 60, "reset", GPIO_ACTIVE_HIGH), + GPIO_LOOKUP("INT33FC:02", 28, "irq", GPIO_ACTIVE_HIGH), + { } + }, +}; + +static struct gpiod_lookup_table * const asus_me176c_gpios[] = { + &int3496_gpo2_pin22_gpios, + &asus_me176c_goodix_gpios, + NULL +}; + +const struct x86_dev_info asus_me176c_info __initconst = { + .i2c_client_info = asus_me176c_i2c_clients, + .i2c_client_count = ARRAY_SIZE(asus_me176c_i2c_clients), + .pdev_info = asus_me176c_tf103c_pdevs, + .pdev_count = ARRAY_SIZE(asus_me176c_tf103c_pdevs), + .serdev_info = asus_me176c_serdevs, + .serdev_count = ARRAY_SIZE(asus_me176c_serdevs), + .gpiod_lookup_tables = asus_me176c_gpios, + .bat_swnode = &generic_lipo_hv_4v35_battery_node, + .modules = bq24190_modules, + .invalid_aei_gpiochip = "INT33FC:02", + .init = asus_me176c_tf103c_init, +}; + +/* Asus TF103C tablets have an Android factory img with everything hardcoded */ +static const char * const asus_tf103c_accel_mount_matrix[] = { + "0", "-1", "0", + "-1", "0", "0", + "0", "0", "1" +}; + +static const struct property_entry asus_tf103c_accel_props[] = { + PROPERTY_ENTRY_STRING_ARRAY("mount-matrix", asus_tf103c_accel_mount_matrix), + { } +}; + +static const struct software_node asus_tf103c_accel_node = { + .properties = asus_tf103c_accel_props, +}; + +static const struct property_entry asus_tf103c_touchscreen_props[] = { + PROPERTY_ENTRY_STRING("compatible", "atmel,atmel_mxt_ts"), + { } +}; + +static const struct software_node asus_tf103c_touchscreen_node = { + .properties = asus_tf103c_touchscreen_props, +}; + +static const struct property_entry asus_tf103c_battery_props[] = { + PROPERTY_ENTRY_STRING("compatible", "simple-battery"), + PROPERTY_ENTRY_STRING("device-chemistry", "lithium-ion-polymer"), + PROPERTY_ENTRY_U32("precharge-current-microamp", 256000), + PROPERTY_ENTRY_U32("charge-term-current-microamp", 128000), + PROPERTY_ENTRY_U32("constant-charge-current-max-microamp", 2048000), + PROPERTY_ENTRY_U32("constant-charge-voltage-max-microvolt", 4208000), + PROPERTY_ENTRY_U32("factory-internal-resistance-micro-ohms", 150000), + { } +}; + +static const struct software_node asus_tf103c_battery_node = { + .properties = asus_tf103c_battery_props, +}; + +static const struct property_entry asus_tf103c_bq24190_props[] = { + PROPERTY_ENTRY_STRING_ARRAY_LEN("supplied-from", tusb1211_chg_det_psy, 1), + PROPERTY_ENTRY_REF("monitored-battery", &asus_tf103c_battery_node), + PROPERTY_ENTRY_U32("ti,system-minimum-microvolt", 3600000), + PROPERTY_ENTRY_BOOL("omit-battery-class"), + PROPERTY_ENTRY_BOOL("disable-reset"), + { } +}; + +static const struct software_node asus_tf103c_bq24190_node = { + .properties = asus_tf103c_bq24190_props, +}; + +static const struct property_entry asus_tf103c_ug3105_props[] = { + PROPERTY_ENTRY_STRING_ARRAY_LEN("supplied-from", bq24190_psy, 1), + PROPERTY_ENTRY_REF("monitored-battery", &asus_tf103c_battery_node), + PROPERTY_ENTRY_U32("upisemi,rsns-microohm", 5000), + { } +}; + +static const struct software_node asus_tf103c_ug3105_node = { + .properties = asus_tf103c_ug3105_props, +}; + +static const struct x86_i2c_client_info asus_tf103c_i2c_clients[] __initconst = { + { + /* bq24297 battery charger */ + .board_info = { + .type = "bq24190", + .addr = 0x6b, + .dev_name = "bq24297", + .swnode = &asus_tf103c_bq24190_node, + .platform_data = &bq24190_pdata, + }, + .adapter_path = "\\_SB_.I2C1", + .irq_data = { + .type = X86_ACPI_IRQ_TYPE_PMIC, + .chip = "\\_SB_.I2C7.PMIC", + .domain = DOMAIN_BUS_WAKEUP, + .index = 0, + }, + }, { + /* ug3105 battery monitor */ + .board_info = { + .type = "ug3105", + .addr = 0x70, + .dev_name = "ug3105", + .swnode = &asus_tf103c_ug3105_node, + }, + .adapter_path = "\\_SB_.I2C1", + }, { + /* ak09911 compass */ + .board_info = { + .type = "ak09911", + .addr = 0x0c, + .dev_name = "ak09911", + }, + .adapter_path = "\\_SB_.I2C5", + }, { + /* kxtj21009 accel */ + .board_info = { + .type = "kxtj21009", + .addr = 0x0f, + .dev_name = "kxtj21009", + .swnode = &asus_tf103c_accel_node, + }, + .adapter_path = "\\_SB_.I2C5", + }, { + /* atmel touchscreen */ + .board_info = { + .type = "atmel_mxt_ts", + .addr = 0x4a, + .dev_name = "atmel_mxt_ts", + .swnode = &asus_tf103c_touchscreen_node, + }, + .adapter_path = "\\_SB_.I2C6", + .irq_data = { + .type = X86_ACPI_IRQ_TYPE_GPIOINT, + .chip = "INT33FC:02", + .index = 28, + .trigger = ACPI_EDGE_SENSITIVE, + .polarity = ACPI_ACTIVE_LOW, + }, + }, +}; + +static struct gpiod_lookup_table * const asus_tf103c_gpios[] = { + &int3496_gpo2_pin22_gpios, + NULL +}; + +const struct x86_dev_info asus_tf103c_info __initconst = { + .i2c_client_info = asus_tf103c_i2c_clients, + .i2c_client_count = ARRAY_SIZE(asus_tf103c_i2c_clients), + .pdev_info = asus_me176c_tf103c_pdevs, + .pdev_count = ARRAY_SIZE(asus_me176c_tf103c_pdevs), + .gpiod_lookup_tables = asus_tf103c_gpios, + .bat_swnode = &asus_tf103c_battery_node, + .modules = bq24190_modules, + .invalid_aei_gpiochip = "INT33FC:02", + .init = asus_me176c_tf103c_init, +}; diff --git a/drivers/platform/x86/x86-android-tablets/x86-android-tablets-main.c b/drivers/platform/x86/x86-android-tablets/x86-android-tablets-main.c index 225502794a89..10254db73286 100644 --- a/drivers/platform/x86/x86-android-tablets/x86-android-tablets-main.c +++ b/drivers/platform/x86/x86-android-tablets/x86-android-tablets-main.c @@ -26,14 +26,6 @@ #include "shared-psy-info.h" #include "x86-android-tablets.h" -static struct gpiod_lookup_table int3496_gpo2_pin22_gpios = { - .dev_id = "intel-int3496", - .table = { - GPIO_LOOKUP("INT33FC:02", 22, "id", GPIO_ACTIVE_HIGH), - { } - }, -}; - /* Acer Iconia One 7 B1-750 has an Android factory img with everything hardcoded */ static const char * const acer_b1_750_mount_matrix[] = { "-1", "0", "0", @@ -156,338 +148,6 @@ const struct x86_dev_info advantech_mica_071_info __initconst = { .init = advantech_mica_071_init, }; -/* Asus ME176C and TF103C tablets shared data */ -static struct gpio_keys_button asus_me176c_tf103c_lid = { - .code = SW_LID, - /* .gpio gets filled in by asus_me176c_tf103c_init() */ - .active_low = true, - .desc = "lid_sw", - .type = EV_SW, - .wakeup = true, - .debounce_interval = 50, -}; - -static const struct gpio_keys_platform_data asus_me176c_tf103c_lid_pdata __initconst = { - .buttons = &asus_me176c_tf103c_lid, - .nbuttons = 1, - .name = "lid_sw", -}; - -static const struct platform_device_info asus_me176c_tf103c_pdevs[] __initconst = { - { - .name = "gpio-keys", - .id = PLATFORM_DEVID_AUTO, - .data = &asus_me176c_tf103c_lid_pdata, - .size_data = sizeof(asus_me176c_tf103c_lid_pdata), - }, - { - /* For micro USB ID pin handling */ - .name = "intel-int3496", - .id = PLATFORM_DEVID_NONE, - }, -}; - -static int __init asus_me176c_tf103c_init(void) -{ - struct gpio_desc *gpiod; - int ret; - - ret = x86_android_tablet_get_gpiod("INT33FC:02", 12, &gpiod); - if (ret < 0) - return ret; - asus_me176c_tf103c_lid.gpio = desc_to_gpio(gpiod); - - return 0; -} - - -/* Asus ME176C tablets have an Android factory img with everything hardcoded */ -static const char * const asus_me176c_accel_mount_matrix[] = { - "-1", "0", "0", - "0", "1", "0", - "0", "0", "1" -}; - -static const struct property_entry asus_me176c_accel_props[] = { - PROPERTY_ENTRY_STRING_ARRAY("mount-matrix", asus_me176c_accel_mount_matrix), - { } -}; - -static const struct software_node asus_me176c_accel_node = { - .properties = asus_me176c_accel_props, -}; - -static const struct property_entry asus_me176c_bq24190_props[] = { - PROPERTY_ENTRY_STRING_ARRAY_LEN("supplied-from", tusb1211_chg_det_psy, 1), - PROPERTY_ENTRY_REF("monitored-battery", &generic_lipo_hv_4v35_battery_node), - PROPERTY_ENTRY_U32("ti,system-minimum-microvolt", 3600000), - PROPERTY_ENTRY_BOOL("omit-battery-class"), - PROPERTY_ENTRY_BOOL("disable-reset"), - { } -}; - -static const struct software_node asus_me176c_bq24190_node = { - .properties = asus_me176c_bq24190_props, -}; - -static const struct property_entry asus_me176c_ug3105_props[] = { - PROPERTY_ENTRY_STRING_ARRAY_LEN("supplied-from", bq24190_psy, 1), - PROPERTY_ENTRY_REF("monitored-battery", &generic_lipo_hv_4v35_battery_node), - PROPERTY_ENTRY_U32("upisemi,rsns-microohm", 10000), - { } -}; - -static const struct software_node asus_me176c_ug3105_node = { - .properties = asus_me176c_ug3105_props, -}; - -static const struct x86_i2c_client_info asus_me176c_i2c_clients[] __initconst = { - { - /* bq24297 battery charger */ - .board_info = { - .type = "bq24190", - .addr = 0x6b, - .dev_name = "bq24297", - .swnode = &asus_me176c_bq24190_node, - .platform_data = &bq24190_pdata, - }, - .adapter_path = "\\_SB_.I2C1", - .irq_data = { - .type = X86_ACPI_IRQ_TYPE_PMIC, - .chip = "\\_SB_.I2C7.PMIC", - .domain = DOMAIN_BUS_WAKEUP, - .index = 0, - }, - }, { - /* ug3105 battery monitor */ - .board_info = { - .type = "ug3105", - .addr = 0x70, - .dev_name = "ug3105", - .swnode = &asus_me176c_ug3105_node, - }, - .adapter_path = "\\_SB_.I2C1", - }, { - /* ak09911 compass */ - .board_info = { - .type = "ak09911", - .addr = 0x0c, - .dev_name = "ak09911", - }, - .adapter_path = "\\_SB_.I2C5", - }, { - /* kxtj21009 accel */ - .board_info = { - .type = "kxtj21009", - .addr = 0x0f, - .dev_name = "kxtj21009", - .swnode = &asus_me176c_accel_node, - }, - .adapter_path = "\\_SB_.I2C5", - .irq_data = { - .type = X86_ACPI_IRQ_TYPE_APIC, - .index = 0x44, - .trigger = ACPI_EDGE_SENSITIVE, - .polarity = ACPI_ACTIVE_LOW, - }, - }, { - /* goodix touchscreen */ - .board_info = { - .type = "GDIX1001:00", - .addr = 0x14, - .dev_name = "goodix_ts", - }, - .adapter_path = "\\_SB_.I2C6", - .irq_data = { - .type = X86_ACPI_IRQ_TYPE_APIC, - .index = 0x45, - .trigger = ACPI_EDGE_SENSITIVE, - .polarity = ACPI_ACTIVE_LOW, - }, - }, -}; - -static const struct x86_serdev_info asus_me176c_serdevs[] __initconst = { - { - .ctrl_hid = "80860F0A", - .ctrl_uid = "2", - .ctrl_devname = "serial0", - .serdev_hid = "BCM2E3A", - }, -}; - -static struct gpiod_lookup_table asus_me176c_goodix_gpios = { - .dev_id = "i2c-goodix_ts", - .table = { - GPIO_LOOKUP("INT33FC:00", 60, "reset", GPIO_ACTIVE_HIGH), - GPIO_LOOKUP("INT33FC:02", 28, "irq", GPIO_ACTIVE_HIGH), - { } - }, -}; - -static struct gpiod_lookup_table * const asus_me176c_gpios[] = { - &int3496_gpo2_pin22_gpios, - &asus_me176c_goodix_gpios, - NULL -}; - -const struct x86_dev_info asus_me176c_info __initconst = { - .i2c_client_info = asus_me176c_i2c_clients, - .i2c_client_count = ARRAY_SIZE(asus_me176c_i2c_clients), - .pdev_info = asus_me176c_tf103c_pdevs, - .pdev_count = ARRAY_SIZE(asus_me176c_tf103c_pdevs), - .serdev_info = asus_me176c_serdevs, - .serdev_count = ARRAY_SIZE(asus_me176c_serdevs), - .gpiod_lookup_tables = asus_me176c_gpios, - .bat_swnode = &generic_lipo_hv_4v35_battery_node, - .modules = bq24190_modules, - .invalid_aei_gpiochip = "INT33FC:02", - .init = asus_me176c_tf103c_init, -}; - -/* Asus TF103C tablets have an Android factory img with everything hardcoded */ -static const char * const asus_tf103c_accel_mount_matrix[] = { - "0", "-1", "0", - "-1", "0", "0", - "0", "0", "1" -}; - -static const struct property_entry asus_tf103c_accel_props[] = { - PROPERTY_ENTRY_STRING_ARRAY("mount-matrix", asus_tf103c_accel_mount_matrix), - { } -}; - -static const struct software_node asus_tf103c_accel_node = { - .properties = asus_tf103c_accel_props, -}; - -static const struct property_entry asus_tf103c_touchscreen_props[] = { - PROPERTY_ENTRY_STRING("compatible", "atmel,atmel_mxt_ts"), - { } -}; - -static const struct software_node asus_tf103c_touchscreen_node = { - .properties = asus_tf103c_touchscreen_props, -}; - -static const struct property_entry asus_tf103c_battery_props[] = { - PROPERTY_ENTRY_STRING("compatible", "simple-battery"), - PROPERTY_ENTRY_STRING("device-chemistry", "lithium-ion-polymer"), - PROPERTY_ENTRY_U32("precharge-current-microamp", 256000), - PROPERTY_ENTRY_U32("charge-term-current-microamp", 128000), - PROPERTY_ENTRY_U32("constant-charge-current-max-microamp", 2048000), - PROPERTY_ENTRY_U32("constant-charge-voltage-max-microvolt", 4208000), - PROPERTY_ENTRY_U32("factory-internal-resistance-micro-ohms", 150000), - { } -}; - -static const struct software_node asus_tf103c_battery_node = { - .properties = asus_tf103c_battery_props, -}; - -static const struct property_entry asus_tf103c_bq24190_props[] = { - PROPERTY_ENTRY_STRING_ARRAY_LEN("supplied-from", tusb1211_chg_det_psy, 1), - PROPERTY_ENTRY_REF("monitored-battery", &asus_tf103c_battery_node), - PROPERTY_ENTRY_U32("ti,system-minimum-microvolt", 3600000), - PROPERTY_ENTRY_BOOL("omit-battery-class"), - PROPERTY_ENTRY_BOOL("disable-reset"), - { } -}; - -static const struct software_node asus_tf103c_bq24190_node = { - .properties = asus_tf103c_bq24190_props, -}; - -static const struct property_entry asus_tf103c_ug3105_props[] = { - PROPERTY_ENTRY_STRING_ARRAY_LEN("supplied-from", bq24190_psy, 1), - PROPERTY_ENTRY_REF("monitored-battery", &asus_tf103c_battery_node), - PROPERTY_ENTRY_U32("upisemi,rsns-microohm", 5000), - { } -}; - -static const struct software_node asus_tf103c_ug3105_node = { - .properties = asus_tf103c_ug3105_props, -}; - -static const struct x86_i2c_client_info asus_tf103c_i2c_clients[] __initconst = { - { - /* bq24297 battery charger */ - .board_info = { - .type = "bq24190", - .addr = 0x6b, - .dev_name = "bq24297", - .swnode = &asus_tf103c_bq24190_node, - .platform_data = &bq24190_pdata, - }, - .adapter_path = "\\_SB_.I2C1", - .irq_data = { - .type = X86_ACPI_IRQ_TYPE_PMIC, - .chip = "\\_SB_.I2C7.PMIC", - .domain = DOMAIN_BUS_WAKEUP, - .index = 0, - }, - }, { - /* ug3105 battery monitor */ - .board_info = { - .type = "ug3105", - .addr = 0x70, - .dev_name = "ug3105", - .swnode = &asus_tf103c_ug3105_node, - }, - .adapter_path = "\\_SB_.I2C1", - }, { - /* ak09911 compass */ - .board_info = { - .type = "ak09911", - .addr = 0x0c, - .dev_name = "ak09911", - }, - .adapter_path = "\\_SB_.I2C5", - }, { - /* kxtj21009 accel */ - .board_info = { - .type = "kxtj21009", - .addr = 0x0f, - .dev_name = "kxtj21009", - .swnode = &asus_tf103c_accel_node, - }, - .adapter_path = "\\_SB_.I2C5", - }, { - /* atmel touchscreen */ - .board_info = { - .type = "atmel_mxt_ts", - .addr = 0x4a, - .dev_name = "atmel_mxt_ts", - .swnode = &asus_tf103c_touchscreen_node, - }, - .adapter_path = "\\_SB_.I2C6", - .irq_data = { - .type = X86_ACPI_IRQ_TYPE_GPIOINT, - .chip = "INT33FC:02", - .index = 28, - .trigger = ACPI_EDGE_SENSITIVE, - .polarity = ACPI_ACTIVE_LOW, - }, - }, -}; - -static struct gpiod_lookup_table * const asus_tf103c_gpios[] = { - &int3496_gpo2_pin22_gpios, - NULL -}; - -const struct x86_dev_info asus_tf103c_info __initconst = { - .i2c_client_info = asus_tf103c_i2c_clients, - .i2c_client_count = ARRAY_SIZE(asus_tf103c_i2c_clients), - .pdev_info = asus_me176c_tf103c_pdevs, - .pdev_count = ARRAY_SIZE(asus_me176c_tf103c_pdevs), - .gpiod_lookup_tables = asus_tf103c_gpios, - .bat_swnode = &asus_tf103c_battery_node, - .modules = bq24190_modules, - .invalid_aei_gpiochip = "INT33FC:02", - .init = asus_me176c_tf103c_init, -}; - /* * When booted with the BIOS set to Android mode the Chuwi Hi8 (CWI509) DSDT * contains a whole bunch of bogus ACPI I2C devices and is missing entries -- cgit v1.2.3 From 7bf974f695f1a978097a3b43ac26afd46eba626d Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Sun, 19 Feb 2023 21:15:24 +0100 Subject: platform/x86: x86-android-tablets: Move Lenovo tablets to their own file Move the info for the Lenovo tablets to their own lenovo.c file. Signed-off-by: Hans de Goede Reviewed-by: Andy Shevchenko Link: https://lore.kernel.org/r/20230301092331.7038-8-hdegoede@redhat.com --- drivers/platform/x86/x86-android-tablets/Makefile | 3 +- drivers/platform/x86/x86-android-tablets/lenovo.c | 419 +++++++++++++++++++++ .../x86-android-tablets/x86-android-tablets-main.c | 406 +------------------- 3 files changed, 422 insertions(+), 406 deletions(-) create mode 100644 drivers/platform/x86/x86-android-tablets/lenovo.c (limited to 'drivers/platform') diff --git a/drivers/platform/x86/x86-android-tablets/Makefile b/drivers/platform/x86/x86-android-tablets/Makefile index 76f94831bf97..d881a824fa3d 100644 --- a/drivers/platform/x86/x86-android-tablets/Makefile +++ b/drivers/platform/x86/x86-android-tablets/Makefile @@ -5,4 +5,5 @@ obj-$(CONFIG_X86_ANDROID_TABLETS) += x86-android-tablets.o -x86-android-tablets-y := core.o dmi.o shared-psy-info.o asus.o x86-android-tablets-main.o +x86-android-tablets-y := core.o dmi.o shared-psy-info.o \ + asus.o lenovo.o x86-android-tablets-main.o diff --git a/drivers/platform/x86/x86-android-tablets/lenovo.c b/drivers/platform/x86/x86-android-tablets/lenovo.c new file mode 100644 index 000000000000..290de07d8aa0 --- /dev/null +++ b/drivers/platform/x86/x86-android-tablets/lenovo.c @@ -0,0 +1,419 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Board info for Lenovo X86 tablets which ship with Android as the factory image + * and which have broken DSDT tables. The factory kernels shipped on these + * devices typically have a bunch of things hardcoded, rather than specified + * in their DSDT. + * + * Copyright (C) 2021-2023 Hans de Goede + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "shared-psy-info.h" +#include "x86-android-tablets.h" + +/* Lenovo Yoga Book X90F / X91F / X91L need manual instantiation of the fg client */ +static const struct x86_i2c_client_info lenovo_yogabook_x9x_i2c_clients[] __initconst = { + { + /* BQ27542 fuel-gauge */ + .board_info = { + .type = "bq27542", + .addr = 0x55, + .dev_name = "bq27542", + .swnode = &fg_bq25890_supply_node, + }, + .adapter_path = "\\_SB_.PCI0.I2C1", + }, +}; + +const struct x86_dev_info lenovo_yogabook_x9x_info __initconst = { + .i2c_client_info = lenovo_yogabook_x9x_i2c_clients, + .i2c_client_count = ARRAY_SIZE(lenovo_yogabook_x9x_i2c_clients), +}; + +/* Lenovo Yoga Tablet 2 1050F/L's Android factory img has everything hardcoded */ +static const struct property_entry lenovo_yoga_tab2_830_1050_bq24190_props[] = { + PROPERTY_ENTRY_STRING_ARRAY_LEN("supplied-from", tusb1211_chg_det_psy, 1), + PROPERTY_ENTRY_REF("monitored-battery", &generic_lipo_hv_4v35_battery_node), + PROPERTY_ENTRY_BOOL("omit-battery-class"), + PROPERTY_ENTRY_BOOL("disable-reset"), + { } +}; + +static const struct software_node lenovo_yoga_tab2_830_1050_bq24190_node = { + .properties = lenovo_yoga_tab2_830_1050_bq24190_props, +}; + +/* This gets filled by lenovo_yoga_tab2_830_1050_init() */ +static struct rmi_device_platform_data lenovo_yoga_tab2_830_1050_rmi_pdata = { }; + +static struct lp855x_platform_data lenovo_yoga_tab2_830_1050_lp8557_pdata = { + .device_control = 0x86, + .initial_brightness = 128, +}; + +static const struct x86_i2c_client_info lenovo_yoga_tab2_830_1050_i2c_clients[] __initconst = { + { + /* bq24292i battery charger */ + .board_info = { + .type = "bq24190", + .addr = 0x6b, + .dev_name = "bq24292i", + .swnode = &lenovo_yoga_tab2_830_1050_bq24190_node, + .platform_data = &bq24190_pdata, + }, + .adapter_path = "\\_SB_.I2C1", + .irq_data = { + .type = X86_ACPI_IRQ_TYPE_GPIOINT, + .chip = "INT33FC:02", + .index = 2, + .trigger = ACPI_EDGE_SENSITIVE, + .polarity = ACPI_ACTIVE_HIGH, + }, + }, { + /* BQ27541 fuel-gauge */ + .board_info = { + .type = "bq27541", + .addr = 0x55, + .dev_name = "bq27541", + .swnode = &fg_bq24190_supply_node, + }, + .adapter_path = "\\_SB_.I2C1", + }, { + /* Synaptics RMI touchscreen */ + .board_info = { + .type = "rmi4_i2c", + .addr = 0x38, + .dev_name = "rmi4_i2c", + .platform_data = &lenovo_yoga_tab2_830_1050_rmi_pdata, + }, + .adapter_path = "\\_SB_.I2C6", + .irq_data = { + .type = X86_ACPI_IRQ_TYPE_APIC, + .index = 0x45, + .trigger = ACPI_EDGE_SENSITIVE, + .polarity = ACPI_ACTIVE_HIGH, + }, + }, { + /* LP8557 Backlight controller */ + .board_info = { + .type = "lp8557", + .addr = 0x2c, + .dev_name = "lp8557", + .platform_data = &lenovo_yoga_tab2_830_1050_lp8557_pdata, + }, + .adapter_path = "\\_SB_.I2C3", + }, +}; + +static struct gpiod_lookup_table lenovo_yoga_tab2_830_1050_int3496_gpios = { + .dev_id = "intel-int3496", + .table = { + GPIO_LOOKUP("INT33FC:02", 1, "mux", GPIO_ACTIVE_LOW), + GPIO_LOOKUP("INT33FC:02", 24, "id", GPIO_ACTIVE_HIGH), + { } + }, +}; + +#define LENOVO_YOGA_TAB2_830_1050_CODEC_NAME "spi-10WM5102:00" + +static struct gpiod_lookup_table lenovo_yoga_tab2_830_1050_codec_gpios = { + .dev_id = LENOVO_YOGA_TAB2_830_1050_CODEC_NAME, + .table = { + GPIO_LOOKUP("gpio_crystalcove", 3, "reset", GPIO_ACTIVE_HIGH), + GPIO_LOOKUP("INT33FC:01", 23, "wlf,ldoena", GPIO_ACTIVE_HIGH), + GPIO_LOOKUP("arizona", 2, "wlf,spkvdd-ena", GPIO_ACTIVE_HIGH), + GPIO_LOOKUP("arizona", 4, "wlf,micd-pol", GPIO_ACTIVE_LOW), + { } + }, +}; + +static struct gpiod_lookup_table * const lenovo_yoga_tab2_830_1050_gpios[] = { + &lenovo_yoga_tab2_830_1050_int3496_gpios, + &lenovo_yoga_tab2_830_1050_codec_gpios, + NULL +}; + +static int __init lenovo_yoga_tab2_830_1050_init(void); +static void lenovo_yoga_tab2_830_1050_exit(void); + +struct x86_dev_info lenovo_yoga_tab2_830_1050_info __initdata = { + .i2c_client_info = lenovo_yoga_tab2_830_1050_i2c_clients, + /* i2c_client_count gets set by lenovo_yoga_tab2_830_1050_init() */ + .pdev_info = int3496_pdevs, + .pdev_count = 1, + .gpiod_lookup_tables = lenovo_yoga_tab2_830_1050_gpios, + .bat_swnode = &generic_lipo_hv_4v35_battery_node, + .modules = bq24190_modules, + .init = lenovo_yoga_tab2_830_1050_init, + .exit = lenovo_yoga_tab2_830_1050_exit, +}; + +/* + * The Lenovo Yoga Tablet 2 830 and 1050 (8" vs 10") versions use the same + * mainboard, but they need some different treatment related to the display: + * 1. The 830 uses a portrait LCD panel with a landscape touchscreen, requiring + * the touchscreen driver to adjust the touch-coords to match the LCD. + * 2. Both use an TI LP8557 LED backlight controller. On the 1050 the LP8557's + * PWM input is connected to the PMIC's PWM output and everything works fine + * with the defaults programmed into the LP8557 by the BIOS. + * But on the 830 the LP8557's PWM input is connected to a PWM output coming + * from the LCD panel's controller. The Android code has a hack in the i915 + * driver to write the non-standard DSI reg 0x9f with the desired backlight + * level to set the duty-cycle of the LCD's PWM output. + * + * To avoid having to have a similar hack in the mainline kernel the LP8557 + * entry in lenovo_yoga_tab2_830_1050_i2c_clients instead just programs the + * LP8557 to directly set the level, ignoring the PWM input. This means that + * the LP8557 i2c_client should only be instantiated on the 830. + */ +static int __init lenovo_yoga_tab2_830_1050_init_display(void) +{ + struct gpio_desc *gpiod; + int ret; + + /* Use PMIC GPIO 10 bootstrap pin to differentiate 830 vs 1050 */ + ret = x86_android_tablet_get_gpiod("gpio_crystalcove", 10, &gpiod); + if (ret) + return ret; + + ret = gpiod_get_value_cansleep(gpiod); + if (ret) { + pr_info("detected Lenovo Yoga Tablet 2 1050F/L\n"); + lenovo_yoga_tab2_830_1050_info.i2c_client_count = + ARRAY_SIZE(lenovo_yoga_tab2_830_1050_i2c_clients) - 1; + } else { + pr_info("detected Lenovo Yoga Tablet 2 830F/L\n"); + lenovo_yoga_tab2_830_1050_rmi_pdata.sensor_pdata.axis_align.swap_axes = true; + lenovo_yoga_tab2_830_1050_rmi_pdata.sensor_pdata.axis_align.flip_y = true; + lenovo_yoga_tab2_830_1050_info.i2c_client_count = + ARRAY_SIZE(lenovo_yoga_tab2_830_1050_i2c_clients); + } + + return 0; +} + +/* SUS (INT33FC:02) pin 6 needs to be configured as pmu_clk for the audio codec */ +static const struct pinctrl_map lenovo_yoga_tab2_830_1050_codec_pinctrl_map = + PIN_MAP_MUX_GROUP(LENOVO_YOGA_TAB2_830_1050_CODEC_NAME, "codec_32khz_clk", + "INT33FC:02", "pmu_clk2_grp", "pmu_clk"); + +static struct pinctrl *lenovo_yoga_tab2_830_1050_codec_pinctrl; +static struct sys_off_handler *lenovo_yoga_tab2_830_1050_sys_off_handler; + +static int __init lenovo_yoga_tab2_830_1050_init_codec(void) +{ + struct device *codec_dev; + struct pinctrl *pinctrl; + int ret; + + codec_dev = bus_find_device_by_name(&spi_bus_type, NULL, + LENOVO_YOGA_TAB2_830_1050_CODEC_NAME); + if (!codec_dev) { + pr_err("error cannot find %s device\n", LENOVO_YOGA_TAB2_830_1050_CODEC_NAME); + return -ENODEV; + } + + ret = pinctrl_register_mappings(&lenovo_yoga_tab2_830_1050_codec_pinctrl_map, 1); + if (ret) + goto err_put_device; + + pinctrl = pinctrl_get_select(codec_dev, "codec_32khz_clk"); + if (IS_ERR(pinctrl)) { + ret = dev_err_probe(codec_dev, PTR_ERR(pinctrl), "selecting codec_32khz_clk\n"); + goto err_unregister_mappings; + } + + /* We're done with the codec_dev now */ + put_device(codec_dev); + + lenovo_yoga_tab2_830_1050_codec_pinctrl = pinctrl; + return 0; + +err_unregister_mappings: + pinctrl_unregister_mappings(&lenovo_yoga_tab2_830_1050_codec_pinctrl_map); +err_put_device: + put_device(codec_dev); + return ret; +} + +/* + * These tablet's DSDT does not set acpi_gbl_reduced_hardware, so acpi_power_off + * gets used as pm_power_off handler. This causes "poweroff" on these tablets + * to hang hard. Requiring pressing the powerbutton for 30 seconds *twice* + * followed by a normal 3 second press to recover. Avoid this by doing an EFI + * poweroff instead. + */ +static int lenovo_yoga_tab2_830_1050_power_off(struct sys_off_data *data) +{ + efi.reset_system(EFI_RESET_SHUTDOWN, EFI_SUCCESS, 0, NULL); + + return NOTIFY_DONE; +} + +static int __init lenovo_yoga_tab2_830_1050_init(void) +{ + int ret; + + ret = lenovo_yoga_tab2_830_1050_init_display(); + if (ret) + return ret; + + ret = lenovo_yoga_tab2_830_1050_init_codec(); + if (ret) + return ret; + + /* SYS_OFF_PRIO_FIRMWARE + 1 so that it runs before acpi_power_off */ + lenovo_yoga_tab2_830_1050_sys_off_handler = + register_sys_off_handler(SYS_OFF_MODE_POWER_OFF, SYS_OFF_PRIO_FIRMWARE + 1, + lenovo_yoga_tab2_830_1050_power_off, NULL); + if (IS_ERR(lenovo_yoga_tab2_830_1050_sys_off_handler)) + return PTR_ERR(lenovo_yoga_tab2_830_1050_sys_off_handler); + + return 0; +} + +static void lenovo_yoga_tab2_830_1050_exit(void) +{ + unregister_sys_off_handler(lenovo_yoga_tab2_830_1050_sys_off_handler); + + if (lenovo_yoga_tab2_830_1050_codec_pinctrl) { + pinctrl_put(lenovo_yoga_tab2_830_1050_codec_pinctrl); + pinctrl_unregister_mappings(&lenovo_yoga_tab2_830_1050_codec_pinctrl_map); + } +} + +/* Lenovo Yoga Tab 3 Pro YT3-X90F */ + +/* + * There are 2 batteries, with 2 bq27500 fuel-gauges and 2 bq25892 chargers, + * "bq25890-charger-1" is instantiated from: drivers/i2c/busses/i2c-cht-wc.c. + */ +static const char * const lenovo_yt3_bq25892_0_suppliers[] = { "cht_wcove_pwrsrc" }; +static const char * const bq25890_1_psy[] = { "bq25890-charger-1" }; + +static const struct property_entry fg_bq25890_1_supply_props[] = { + PROPERTY_ENTRY_STRING_ARRAY("supplied-from", bq25890_1_psy), + { } +}; + +static const struct software_node fg_bq25890_1_supply_node = { + .properties = fg_bq25890_1_supply_props, +}; + +/* bq25892 charger settings for the flat lipo battery behind the screen */ +static const struct property_entry lenovo_yt3_bq25892_0_props[] = { + PROPERTY_ENTRY_STRING_ARRAY("supplied-from", lenovo_yt3_bq25892_0_suppliers), + PROPERTY_ENTRY_STRING("linux,power-supply-name", "bq25892-second-chrg"), + PROPERTY_ENTRY_U32("linux,iinlim-percentage", 40), + PROPERTY_ENTRY_BOOL("linux,skip-reset"), + /* Values taken from Android Factory Image */ + PROPERTY_ENTRY_U32("ti,charge-current", 2048000), + PROPERTY_ENTRY_U32("ti,battery-regulation-voltage", 4352000), + PROPERTY_ENTRY_U32("ti,termination-current", 128000), + PROPERTY_ENTRY_U32("ti,precharge-current", 128000), + PROPERTY_ENTRY_U32("ti,minimum-sys-voltage", 3700000), + PROPERTY_ENTRY_U32("ti,boost-voltage", 4998000), + PROPERTY_ENTRY_U32("ti,boost-max-current", 500000), + PROPERTY_ENTRY_BOOL("ti,use-ilim-pin"), + { } +}; + +static const struct software_node lenovo_yt3_bq25892_0_node = { + .properties = lenovo_yt3_bq25892_0_props, +}; + +static const struct x86_i2c_client_info lenovo_yt3_i2c_clients[] __initconst = { + { + /* bq27500 fuel-gauge for the flat lipo battery behind the screen */ + .board_info = { + .type = "bq27500", + .addr = 0x55, + .dev_name = "bq27500_0", + .swnode = &fg_bq25890_supply_node, + }, + .adapter_path = "\\_SB_.PCI0.I2C1", + }, { + /* bq25892 charger for the flat lipo battery behind the screen */ + .board_info = { + .type = "bq25892", + .addr = 0x6b, + .dev_name = "bq25892_0", + .swnode = &lenovo_yt3_bq25892_0_node, + }, + .adapter_path = "\\_SB_.PCI0.I2C1", + .irq_data = { + .type = X86_ACPI_IRQ_TYPE_GPIOINT, + .chip = "INT33FF:01", + .index = 5, + .trigger = ACPI_EDGE_SENSITIVE, + .polarity = ACPI_ACTIVE_LOW, + }, + }, { + /* bq27500 fuel-gauge for the round li-ion cells in the hinge */ + .board_info = { + .type = "bq27500", + .addr = 0x55, + .dev_name = "bq27500_1", + .swnode = &fg_bq25890_1_supply_node, + }, + .adapter_path = "\\_SB_.PCI0.I2C2", + } +}; + +static int __init lenovo_yt3_init(void) +{ + struct gpio_desc *gpiod; + int ret; + + /* + * The "bq25892_0" charger IC has its /CE (Charge-Enable) and OTG pins + * connected to GPIOs, rather then having them hardwired to the correct + * values as is normally done. + * + * The bq25890_charger driver controls these through I2C, but this only + * works if not overridden by the pins. Set these pins here: + * 1. Set /CE to 0 to allow charging. + * 2. Set OTG to 0 disable V5 boost output since the 5V boost output of + * the main "bq25892_1" charger is used when necessary. + */ + + /* /CE pin */ + ret = x86_android_tablet_get_gpiod("INT33FF:02", 22, &gpiod); + if (ret < 0) + return ret; + + /* + * The gpio_desc returned by x86_android_tablet_get_gpiod() is a "raw" + * gpio_desc, that is there is no way to pass lookup-flags like + * GPIO_ACTIVE_LOW. Set the GPIO to 0 here to enable charging since + * the /CE pin is active-low, but not marked as such in the gpio_desc. + */ + gpiod_set_value(gpiod, 0); + + /* OTG pin */ + ret = x86_android_tablet_get_gpiod("INT33FF:03", 19, &gpiod); + if (ret < 0) + return ret; + + gpiod_set_value(gpiod, 0); + + return 0; +} + +const struct x86_dev_info lenovo_yt3_info __initconst = { + .i2c_client_info = lenovo_yt3_i2c_clients, + .i2c_client_count = ARRAY_SIZE(lenovo_yt3_i2c_clients), + .init = lenovo_yt3_init, +}; diff --git a/drivers/platform/x86/x86-android-tablets/x86-android-tablets-main.c b/drivers/platform/x86/x86-android-tablets/x86-android-tablets-main.c index 10254db73286..0362194e225c 100644 --- a/drivers/platform/x86/x86-android-tablets/x86-android-tablets-main.c +++ b/drivers/platform/x86/x86-android-tablets/x86-android-tablets-main.c @@ -8,20 +8,12 @@ * Copyright (C) 2021-2022 Hans de Goede */ -#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt - #include -#include #include +#include #include #include -#include -#include -#include #include -#include -#include -#include #include "shared-psy-info.h" #include "x86-android-tablets.h" @@ -266,402 +258,6 @@ const struct x86_dev_info czc_p10t __initconst = { .init = czc_p10t_init, }; -/* Lenovo Yoga Book X90F / X91F / X91L need manual instantiation of the fg client */ -static const struct x86_i2c_client_info lenovo_yogabook_x9x_i2c_clients[] __initconst = { - { - /* BQ27542 fuel-gauge */ - .board_info = { - .type = "bq27542", - .addr = 0x55, - .dev_name = "bq27542", - .swnode = &fg_bq25890_supply_node, - }, - .adapter_path = "\\_SB_.PCI0.I2C1", - }, -}; - -const struct x86_dev_info lenovo_yogabook_x9x_info __initconst = { - .i2c_client_info = lenovo_yogabook_x9x_i2c_clients, - .i2c_client_count = ARRAY_SIZE(lenovo_yogabook_x9x_i2c_clients), -}; - -/* Lenovo Yoga Tablet 2 1050F/L's Android factory img has everything hardcoded */ -static const struct property_entry lenovo_yoga_tab2_830_1050_bq24190_props[] = { - PROPERTY_ENTRY_STRING_ARRAY_LEN("supplied-from", tusb1211_chg_det_psy, 1), - PROPERTY_ENTRY_REF("monitored-battery", &generic_lipo_hv_4v35_battery_node), - PROPERTY_ENTRY_BOOL("omit-battery-class"), - PROPERTY_ENTRY_BOOL("disable-reset"), - { } -}; - -static const struct software_node lenovo_yoga_tab2_830_1050_bq24190_node = { - .properties = lenovo_yoga_tab2_830_1050_bq24190_props, -}; - -/* This gets filled by lenovo_yoga_tab2_830_1050_init() */ -static struct rmi_device_platform_data lenovo_yoga_tab2_830_1050_rmi_pdata = { }; - -static struct lp855x_platform_data lenovo_yoga_tab2_830_1050_lp8557_pdata = { - .device_control = 0x86, - .initial_brightness = 128, -}; - -static const struct x86_i2c_client_info lenovo_yoga_tab2_830_1050_i2c_clients[] __initconst = { - { - /* bq24292i battery charger */ - .board_info = { - .type = "bq24190", - .addr = 0x6b, - .dev_name = "bq24292i", - .swnode = &lenovo_yoga_tab2_830_1050_bq24190_node, - .platform_data = &bq24190_pdata, - }, - .adapter_path = "\\_SB_.I2C1", - .irq_data = { - .type = X86_ACPI_IRQ_TYPE_GPIOINT, - .chip = "INT33FC:02", - .index = 2, - .trigger = ACPI_EDGE_SENSITIVE, - .polarity = ACPI_ACTIVE_HIGH, - }, - }, { - /* BQ27541 fuel-gauge */ - .board_info = { - .type = "bq27541", - .addr = 0x55, - .dev_name = "bq27541", - .swnode = &fg_bq24190_supply_node, - }, - .adapter_path = "\\_SB_.I2C1", - }, { - /* Synaptics RMI touchscreen */ - .board_info = { - .type = "rmi4_i2c", - .addr = 0x38, - .dev_name = "rmi4_i2c", - .platform_data = &lenovo_yoga_tab2_830_1050_rmi_pdata, - }, - .adapter_path = "\\_SB_.I2C6", - .irq_data = { - .type = X86_ACPI_IRQ_TYPE_APIC, - .index = 0x45, - .trigger = ACPI_EDGE_SENSITIVE, - .polarity = ACPI_ACTIVE_HIGH, - }, - }, { - /* LP8557 Backlight controller */ - .board_info = { - .type = "lp8557", - .addr = 0x2c, - .dev_name = "lp8557", - .platform_data = &lenovo_yoga_tab2_830_1050_lp8557_pdata, - }, - .adapter_path = "\\_SB_.I2C3", - }, -}; - -static struct gpiod_lookup_table lenovo_yoga_tab2_830_1050_int3496_gpios = { - .dev_id = "intel-int3496", - .table = { - GPIO_LOOKUP("INT33FC:02", 1, "mux", GPIO_ACTIVE_LOW), - GPIO_LOOKUP("INT33FC:02", 24, "id", GPIO_ACTIVE_HIGH), - { } - }, -}; - -#define LENOVO_YOGA_TAB2_830_1050_CODEC_NAME "spi-10WM5102:00" - -static struct gpiod_lookup_table lenovo_yoga_tab2_830_1050_codec_gpios = { - .dev_id = LENOVO_YOGA_TAB2_830_1050_CODEC_NAME, - .table = { - GPIO_LOOKUP("gpio_crystalcove", 3, "reset", GPIO_ACTIVE_HIGH), - GPIO_LOOKUP("INT33FC:01", 23, "wlf,ldoena", GPIO_ACTIVE_HIGH), - GPIO_LOOKUP("arizona", 2, "wlf,spkvdd-ena", GPIO_ACTIVE_HIGH), - GPIO_LOOKUP("arizona", 4, "wlf,micd-pol", GPIO_ACTIVE_LOW), - { } - }, -}; - -static struct gpiod_lookup_table * const lenovo_yoga_tab2_830_1050_gpios[] = { - &lenovo_yoga_tab2_830_1050_int3496_gpios, - &lenovo_yoga_tab2_830_1050_codec_gpios, - NULL -}; - -static int __init lenovo_yoga_tab2_830_1050_init(void); -static void lenovo_yoga_tab2_830_1050_exit(void); - -struct x86_dev_info lenovo_yoga_tab2_830_1050_info __initdata = { - .i2c_client_info = lenovo_yoga_tab2_830_1050_i2c_clients, - /* i2c_client_count gets set by lenovo_yoga_tab2_830_1050_init() */ - .pdev_info = int3496_pdevs, - .pdev_count = 1, - .gpiod_lookup_tables = lenovo_yoga_tab2_830_1050_gpios, - .bat_swnode = &generic_lipo_hv_4v35_battery_node, - .modules = bq24190_modules, - .invalid_aei_gpiochip = "INT33FC:02", - .init = lenovo_yoga_tab2_830_1050_init, - .exit = lenovo_yoga_tab2_830_1050_exit, -}; - -/* - * The Lenovo Yoga Tablet 2 830 and 1050 (8" vs 10") versions use the same - * mainboard, but they need some different treatment related to the display: - * 1. The 830 uses a portrait LCD panel with a landscape touchscreen, requiring - * the touchscreen driver to adjust the touch-coords to match the LCD. - * 2. Both use an TI LP8557 LED backlight controller. On the 1050 the LP8557's - * PWM input is connected to the PMIC's PWM output and everything works fine - * with the defaults programmed into the LP8557 by the BIOS. - * But on the 830 the LP8557's PWM input is connected to a PWM output coming - * from the LCD panel's controller. The Android code has a hack in the i915 - * driver to write the non-standard DSI reg 0x9f with the desired backlight - * level to set the duty-cycle of the LCD's PWM output. - * - * To avoid having to have a similar hack in the mainline kernel the LP8557 - * entry in lenovo_yoga_tab2_830_1050_i2c_clients instead just programs the - * LP8557 to directly set the level, ignoring the PWM input. This means that - * the LP8557 i2c_client should only be instantiated on the 830. - */ -static int __init lenovo_yoga_tab2_830_1050_init_display(void) -{ - struct gpio_desc *gpiod; - int ret; - - /* Use PMIC GPIO 10 bootstrap pin to differentiate 830 vs 1050 */ - ret = x86_android_tablet_get_gpiod("gpio_crystalcove", 10, &gpiod); - if (ret) - return ret; - - ret = gpiod_get_value_cansleep(gpiod); - if (ret) { - pr_info("detected Lenovo Yoga Tablet 2 1050F/L\n"); - lenovo_yoga_tab2_830_1050_info.i2c_client_count = - ARRAY_SIZE(lenovo_yoga_tab2_830_1050_i2c_clients) - 1; - } else { - pr_info("detected Lenovo Yoga Tablet 2 830F/L\n"); - lenovo_yoga_tab2_830_1050_rmi_pdata.sensor_pdata.axis_align.swap_axes = true; - lenovo_yoga_tab2_830_1050_rmi_pdata.sensor_pdata.axis_align.flip_y = true; - lenovo_yoga_tab2_830_1050_info.i2c_client_count = - ARRAY_SIZE(lenovo_yoga_tab2_830_1050_i2c_clients); - } - - return 0; -} - -/* SUS (INT33FC:02) pin 6 needs to be configured as pmu_clk for the audio codec */ -static const struct pinctrl_map lenovo_yoga_tab2_830_1050_codec_pinctrl_map = - PIN_MAP_MUX_GROUP(LENOVO_YOGA_TAB2_830_1050_CODEC_NAME, "codec_32khz_clk", - "INT33FC:02", "pmu_clk2_grp", "pmu_clk"); - -static struct pinctrl *lenovo_yoga_tab2_830_1050_codec_pinctrl; -static struct sys_off_handler *lenovo_yoga_tab2_830_1050_sys_off_handler; - -static int __init lenovo_yoga_tab2_830_1050_init_codec(void) -{ - struct device *codec_dev; - struct pinctrl *pinctrl; - int ret; - - codec_dev = bus_find_device_by_name(&spi_bus_type, NULL, - LENOVO_YOGA_TAB2_830_1050_CODEC_NAME); - if (!codec_dev) { - pr_err("error cannot find %s device\n", LENOVO_YOGA_TAB2_830_1050_CODEC_NAME); - return -ENODEV; - } - - ret = pinctrl_register_mappings(&lenovo_yoga_tab2_830_1050_codec_pinctrl_map, 1); - if (ret) - goto err_put_device; - - pinctrl = pinctrl_get_select(codec_dev, "codec_32khz_clk"); - if (IS_ERR(pinctrl)) { - ret = dev_err_probe(codec_dev, PTR_ERR(pinctrl), "selecting codec_32khz_clk\n"); - goto err_unregister_mappings; - } - - /* We're done with the codec_dev now */ - put_device(codec_dev); - - lenovo_yoga_tab2_830_1050_codec_pinctrl = pinctrl; - return 0; - -err_unregister_mappings: - pinctrl_unregister_mappings(&lenovo_yoga_tab2_830_1050_codec_pinctrl_map); -err_put_device: - put_device(codec_dev); - return ret; -} - -/* - * These tablet's DSDT does not set acpi_gbl_reduced_hardware, so acpi_power_off - * gets used as pm_power_off handler. This causes "poweroff" on these tablets - * to hang hard. Requiring pressing the powerbutton for 30 seconds *twice* - * followed by a normal 3 second press to recover. Avoid this by doing an EFI - * poweroff instead. - */ -static int lenovo_yoga_tab2_830_1050_power_off(struct sys_off_data *data) -{ - efi.reset_system(EFI_RESET_SHUTDOWN, EFI_SUCCESS, 0, NULL); - - return NOTIFY_DONE; -} - -static int __init lenovo_yoga_tab2_830_1050_init(void) -{ - int ret; - - ret = lenovo_yoga_tab2_830_1050_init_display(); - if (ret) - return ret; - - ret = lenovo_yoga_tab2_830_1050_init_codec(); - if (ret) - return ret; - - /* SYS_OFF_PRIO_FIRMWARE + 1 so that it runs before acpi_power_off */ - lenovo_yoga_tab2_830_1050_sys_off_handler = - register_sys_off_handler(SYS_OFF_MODE_POWER_OFF, SYS_OFF_PRIO_FIRMWARE + 1, - lenovo_yoga_tab2_830_1050_power_off, NULL); - if (IS_ERR(lenovo_yoga_tab2_830_1050_sys_off_handler)) - return PTR_ERR(lenovo_yoga_tab2_830_1050_sys_off_handler); - - return 0; -} - -static void lenovo_yoga_tab2_830_1050_exit(void) -{ - unregister_sys_off_handler(lenovo_yoga_tab2_830_1050_sys_off_handler); - - if (lenovo_yoga_tab2_830_1050_codec_pinctrl) { - pinctrl_put(lenovo_yoga_tab2_830_1050_codec_pinctrl); - pinctrl_unregister_mappings(&lenovo_yoga_tab2_830_1050_codec_pinctrl_map); - } -} - -/* Lenovo Yoga Tab 3 Pro YT3-X90F */ - -/* - * There are 2 batteries, with 2 bq27500 fuel-gauges and 2 bq25892 chargers, - * "bq25890-charger-1" is instantiated from: drivers/i2c/busses/i2c-cht-wc.c. - */ -static const char * const lenovo_yt3_bq25892_0_suppliers[] = { "cht_wcove_pwrsrc" }; -static const char * const bq25890_1_psy[] = { "bq25890-charger-1" }; - -static const struct property_entry fg_bq25890_1_supply_props[] = { - PROPERTY_ENTRY_STRING_ARRAY("supplied-from", bq25890_1_psy), - { } -}; - -static const struct software_node fg_bq25890_1_supply_node = { - .properties = fg_bq25890_1_supply_props, -}; - -/* bq25892 charger settings for the flat lipo battery behind the screen */ -static const struct property_entry lenovo_yt3_bq25892_0_props[] = { - PROPERTY_ENTRY_STRING_ARRAY("supplied-from", lenovo_yt3_bq25892_0_suppliers), - PROPERTY_ENTRY_STRING("linux,power-supply-name", "bq25892-second-chrg"), - PROPERTY_ENTRY_U32("linux,iinlim-percentage", 40), - PROPERTY_ENTRY_BOOL("linux,skip-reset"), - /* Values taken from Android Factory Image */ - PROPERTY_ENTRY_U32("ti,charge-current", 2048000), - PROPERTY_ENTRY_U32("ti,battery-regulation-voltage", 4352000), - PROPERTY_ENTRY_U32("ti,termination-current", 128000), - PROPERTY_ENTRY_U32("ti,precharge-current", 128000), - PROPERTY_ENTRY_U32("ti,minimum-sys-voltage", 3700000), - PROPERTY_ENTRY_U32("ti,boost-voltage", 4998000), - PROPERTY_ENTRY_U32("ti,boost-max-current", 500000), - PROPERTY_ENTRY_BOOL("ti,use-ilim-pin"), - { } -}; - -static const struct software_node lenovo_yt3_bq25892_0_node = { - .properties = lenovo_yt3_bq25892_0_props, -}; - -static const struct x86_i2c_client_info lenovo_yt3_i2c_clients[] __initconst = { - { - /* bq27500 fuel-gauge for the flat lipo battery behind the screen */ - .board_info = { - .type = "bq27500", - .addr = 0x55, - .dev_name = "bq27500_0", - .swnode = &fg_bq25890_supply_node, - }, - .adapter_path = "\\_SB_.PCI0.I2C1", - }, { - /* bq25892 charger for the flat lipo battery behind the screen */ - .board_info = { - .type = "bq25892", - .addr = 0x6b, - .dev_name = "bq25892_0", - .swnode = &lenovo_yt3_bq25892_0_node, - }, - .adapter_path = "\\_SB_.PCI0.I2C1", - .irq_data = { - .type = X86_ACPI_IRQ_TYPE_GPIOINT, - .chip = "INT33FF:01", - .index = 5, - .trigger = ACPI_EDGE_SENSITIVE, - .polarity = ACPI_ACTIVE_LOW, - }, - }, { - /* bq27500 fuel-gauge for the round li-ion cells in the hinge */ - .board_info = { - .type = "bq27500", - .addr = 0x55, - .dev_name = "bq27500_1", - .swnode = &fg_bq25890_1_supply_node, - }, - .adapter_path = "\\_SB_.PCI0.I2C2", - } -}; - -static int __init lenovo_yt3_init(void) -{ - struct gpio_desc *gpiod; - int ret; - - /* - * The "bq25892_0" charger IC has its /CE (Charge-Enable) and OTG pins - * connected to GPIOs, rather then having them hardwired to the correct - * values as is normally done. - * - * The bq25890_charger driver controls these through I2C, but this only - * works if not overridden by the pins. Set these pins here: - * 1. Set /CE to 0 to allow charging. - * 2. Set OTG to 0 disable V5 boost output since the 5V boost output of - * the main "bq25892_1" charger is used when necessary. - */ - - /* /CE pin */ - ret = x86_android_tablet_get_gpiod("INT33FF:02", 22, &gpiod); - if (ret < 0) - return ret; - - /* - * The gpio_desc returned by x86_android_tablet_get_gpiod() is a "raw" - * gpio_desc, that is there is no way to pass lookup-flags like - * GPIO_ACTIVE_LOW. Set the GPIO to 0 here to enable charging since - * the /CE pin is active-low, but not marked as such in the gpio_desc. - */ - gpiod_set_value(gpiod, 0); - - /* OTG pin */ - ret = x86_android_tablet_get_gpiod("INT33FF:03", 19, &gpiod); - if (ret < 0) - return ret; - - gpiod_set_value(gpiod, 0); - - return 0; -} - -const struct x86_dev_info lenovo_yt3_info __initconst = { - .i2c_client_info = lenovo_yt3_i2c_clients, - .i2c_client_count = ARRAY_SIZE(lenovo_yt3_i2c_clients), - .init = lenovo_yt3_init, -}; - /* Medion Lifetab S10346 tablets have an Android factory img with everything hardcoded */ static const char * const medion_lifetab_s10346_accel_mount_matrix[] = { "0", "1", "0", -- cgit v1.2.3 From cc183ad4590bef3831c0688ccfa386c595adc428 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Sun, 19 Feb 2023 21:36:23 +0100 Subject: platform/x86: x86-android-tablets: Move remaining tablets to other.c All that remains now in x86-android-tablets-main.c is info for other (non Asus / Lenovo) tablets. Rename it to other.c to reflect this. Signed-off-by: Hans de Goede Reviewed-by: Andy Shevchenko Link: https://lore.kernel.org/r/20230301092331.7038-9-hdegoede@redhat.com --- drivers/platform/x86/x86-android-tablets/Makefile | 2 +- drivers/platform/x86/x86-android-tablets/other.c | 523 +++++++++++++++++++++ .../x86-android-tablets/x86-android-tablets-main.c | 523 --------------------- 3 files changed, 524 insertions(+), 524 deletions(-) create mode 100644 drivers/platform/x86/x86-android-tablets/other.c delete mode 100644 drivers/platform/x86/x86-android-tablets/x86-android-tablets-main.c (limited to 'drivers/platform') diff --git a/drivers/platform/x86/x86-android-tablets/Makefile b/drivers/platform/x86/x86-android-tablets/Makefile index d881a824fa3d..41ece5a37137 100644 --- a/drivers/platform/x86/x86-android-tablets/Makefile +++ b/drivers/platform/x86/x86-android-tablets/Makefile @@ -6,4 +6,4 @@ obj-$(CONFIG_X86_ANDROID_TABLETS) += x86-android-tablets.o x86-android-tablets-y := core.o dmi.o shared-psy-info.o \ - asus.o lenovo.o x86-android-tablets-main.o + asus.o lenovo.o other.o diff --git a/drivers/platform/x86/x86-android-tablets/other.c b/drivers/platform/x86/x86-android-tablets/other.c new file mode 100644 index 000000000000..83b30b24d57f --- /dev/null +++ b/drivers/platform/x86/x86-android-tablets/other.c @@ -0,0 +1,523 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * DMI based code to deal with broken DSDTs on X86 tablets which ship with + * Android as (part of) the factory image. The factory kernels shipped on these + * devices typically have a bunch of things hardcoded, rather than specified + * in their DSDT. + * + * Copyright (C) 2021-2023 Hans de Goede + */ + +#include +#include +#include +#include +#include +#include + +#include "shared-psy-info.h" +#include "x86-android-tablets.h" + +/* Acer Iconia One 7 B1-750 has an Android factory img with everything hardcoded */ +static const char * const acer_b1_750_mount_matrix[] = { + "-1", "0", "0", + "0", "1", "0", + "0", "0", "1" +}; + +static const struct property_entry acer_b1_750_bma250e_props[] = { + PROPERTY_ENTRY_STRING_ARRAY("mount-matrix", acer_b1_750_mount_matrix), + { } +}; + +static const struct software_node acer_b1_750_bma250e_node = { + .properties = acer_b1_750_bma250e_props, +}; + +static const struct x86_i2c_client_info acer_b1_750_i2c_clients[] __initconst = { + { + /* Novatek NVT-ts touchscreen */ + .board_info = { + .type = "NVT-ts", + .addr = 0x34, + .dev_name = "NVT-ts", + }, + .adapter_path = "\\_SB_.I2C4", + .irq_data = { + .type = X86_ACPI_IRQ_TYPE_GPIOINT, + .chip = "INT33FC:02", + .index = 3, + .trigger = ACPI_EDGE_SENSITIVE, + .polarity = ACPI_ACTIVE_LOW, + }, + }, { + /* BMA250E accelerometer */ + .board_info = { + .type = "bma250e", + .addr = 0x18, + .swnode = &acer_b1_750_bma250e_node, + }, + .adapter_path = "\\_SB_.I2C3", + .irq_data = { + .type = X86_ACPI_IRQ_TYPE_GPIOINT, + .chip = "INT33FC:02", + .index = 25, + .trigger = ACPI_LEVEL_SENSITIVE, + .polarity = ACPI_ACTIVE_HIGH, + }, + }, +}; + +static struct gpiod_lookup_table acer_b1_750_goodix_gpios = { + .dev_id = "i2c-NVT-ts", + .table = { + GPIO_LOOKUP("INT33FC:01", 26, "reset", GPIO_ACTIVE_LOW), + { } + }, +}; + +static struct gpiod_lookup_table * const acer_b1_750_gpios[] = { + &acer_b1_750_goodix_gpios, + &int3496_reference_gpios, + NULL +}; + +const struct x86_dev_info acer_b1_750_info __initconst = { + .i2c_client_info = acer_b1_750_i2c_clients, + .i2c_client_count = ARRAY_SIZE(acer_b1_750_i2c_clients), + .pdev_info = int3496_pdevs, + .pdev_count = 1, + .gpiod_lookup_tables = acer_b1_750_gpios, +}; + +/* + * Advantech MICA-071 + * This is a standard Windows tablet, but it has an extra "quick launch" button + * which is not described in the ACPI tables in anyway. + * Use the x86-android-tablets infra to create a gpio-button device for this. + */ +static struct gpio_keys_button advantech_mica_071_button = { + .code = KEY_PROG1, + /* .gpio gets filled in by advantech_mica_071_init() */ + .active_low = true, + .desc = "prog1_key", + .type = EV_KEY, + .wakeup = false, + .debounce_interval = 50, +}; + +static const struct gpio_keys_platform_data advantech_mica_071_button_pdata __initconst = { + .buttons = &advantech_mica_071_button, + .nbuttons = 1, + .name = "prog1_key", +}; + +static const struct platform_device_info advantech_mica_071_pdevs[] __initconst = { + { + .name = "gpio-keys", + .id = PLATFORM_DEVID_AUTO, + .data = &advantech_mica_071_button_pdata, + .size_data = sizeof(advantech_mica_071_button_pdata), + }, +}; + +static int __init advantech_mica_071_init(void) +{ + struct gpio_desc *gpiod; + int ret; + + ret = x86_android_tablet_get_gpiod("INT33FC:00", 2, &gpiod); + if (ret < 0) + return ret; + advantech_mica_071_button.gpio = desc_to_gpio(gpiod); + + return 0; +} + +const struct x86_dev_info advantech_mica_071_info __initconst = { + .pdev_info = advantech_mica_071_pdevs, + .pdev_count = ARRAY_SIZE(advantech_mica_071_pdevs), + .init = advantech_mica_071_init, +}; + +/* + * When booted with the BIOS set to Android mode the Chuwi Hi8 (CWI509) DSDT + * contains a whole bunch of bogus ACPI I2C devices and is missing entries + * for the touchscreen and the accelerometer. + */ +static const struct property_entry chuwi_hi8_gsl1680_props[] = { + PROPERTY_ENTRY_U32("touchscreen-size-x", 1665), + PROPERTY_ENTRY_U32("touchscreen-size-y", 1140), + PROPERTY_ENTRY_BOOL("touchscreen-swapped-x-y"), + PROPERTY_ENTRY_BOOL("silead,home-button"), + PROPERTY_ENTRY_STRING("firmware-name", "gsl1680-chuwi-hi8.fw"), + { } +}; + +static const struct software_node chuwi_hi8_gsl1680_node = { + .properties = chuwi_hi8_gsl1680_props, +}; + +static const char * const chuwi_hi8_mount_matrix[] = { + "1", "0", "0", + "0", "-1", "0", + "0", "0", "1" +}; + +static const struct property_entry chuwi_hi8_bma250e_props[] = { + PROPERTY_ENTRY_STRING_ARRAY("mount-matrix", chuwi_hi8_mount_matrix), + { } +}; + +static const struct software_node chuwi_hi8_bma250e_node = { + .properties = chuwi_hi8_bma250e_props, +}; + +static const struct x86_i2c_client_info chuwi_hi8_i2c_clients[] __initconst = { + { + /* Silead touchscreen */ + .board_info = { + .type = "gsl1680", + .addr = 0x40, + .swnode = &chuwi_hi8_gsl1680_node, + }, + .adapter_path = "\\_SB_.I2C4", + .irq_data = { + .type = X86_ACPI_IRQ_TYPE_APIC, + .index = 0x44, + .trigger = ACPI_EDGE_SENSITIVE, + .polarity = ACPI_ACTIVE_HIGH, + }, + }, { + /* BMA250E accelerometer */ + .board_info = { + .type = "bma250e", + .addr = 0x18, + .swnode = &chuwi_hi8_bma250e_node, + }, + .adapter_path = "\\_SB_.I2C3", + .irq_data = { + .type = X86_ACPI_IRQ_TYPE_GPIOINT, + .chip = "INT33FC:02", + .index = 23, + .trigger = ACPI_LEVEL_SENSITIVE, + .polarity = ACPI_ACTIVE_HIGH, + }, + }, +}; + +static int __init chuwi_hi8_init(void) +{ + /* + * Avoid the acpi_unregister_gsi() call in x86_acpi_irq_helper_get() + * breaking the touchscreen + logging various errors when the Windows + * BIOS is used. + */ + if (acpi_dev_present("MSSL0001", NULL, 1)) + return -ENODEV; + + return 0; +} + +const struct x86_dev_info chuwi_hi8_info __initconst = { + .i2c_client_info = chuwi_hi8_i2c_clients, + .i2c_client_count = ARRAY_SIZE(chuwi_hi8_i2c_clients), + .init = chuwi_hi8_init, +}; + +#define CZC_EC_EXTRA_PORT 0x68 +#define CZC_EC_ANDROID_KEYS 0x63 + +static int __init czc_p10t_init(void) +{ + /* + * The device boots up in "Windows 7" mode, when the home button sends a + * Windows specific key sequence (Left Meta + D) and the second button + * sends an unknown one while also toggling the Radio Kill Switch. + * This is a surprising behavior when the second button is labeled "Back". + * + * The vendor-supplied Android-x86 build switches the device to a "Android" + * mode by writing value 0x63 to the I/O port 0x68. This just seems to just + * set bit 6 on address 0x96 in the EC region; switching the bit directly + * seems to achieve the same result. It uses a "p10t_switcher" to do the + * job. It doesn't seem to be able to do anything else, and no other use + * of the port 0x68 is known. + * + * In the Android mode, the home button sends just a single scancode, + * which can be handled in Linux userspace more reasonably and the back + * button only sends a scancode without toggling the kill switch. + * The scancode can then be mapped either to Back or RF Kill functionality + * in userspace, depending on how the button is labeled on that particular + * model. + */ + outb(CZC_EC_ANDROID_KEYS, CZC_EC_EXTRA_PORT); + return 0; +} + +const struct x86_dev_info czc_p10t __initconst = { + .init = czc_p10t_init, +}; + +/* Medion Lifetab S10346 tablets have an Android factory img with everything hardcoded */ +static const char * const medion_lifetab_s10346_accel_mount_matrix[] = { + "0", "1", "0", + "1", "0", "0", + "0", "0", "1" +}; + +static const struct property_entry medion_lifetab_s10346_accel_props[] = { + PROPERTY_ENTRY_STRING_ARRAY("mount-matrix", medion_lifetab_s10346_accel_mount_matrix), + { } +}; + +static const struct software_node medion_lifetab_s10346_accel_node = { + .properties = medion_lifetab_s10346_accel_props, +}; + +/* Note the LCD panel is mounted upside down, this is correctly indicated in the VBT */ +static const struct property_entry medion_lifetab_s10346_touchscreen_props[] = { + PROPERTY_ENTRY_BOOL("touchscreen-inverted-x"), + PROPERTY_ENTRY_BOOL("touchscreen-swapped-x-y"), + { } +}; + +static const struct software_node medion_lifetab_s10346_touchscreen_node = { + .properties = medion_lifetab_s10346_touchscreen_props, +}; + +static const struct x86_i2c_client_info medion_lifetab_s10346_i2c_clients[] __initconst = { + { + /* kxtj21009 accel */ + .board_info = { + .type = "kxtj21009", + .addr = 0x0f, + .dev_name = "kxtj21009", + .swnode = &medion_lifetab_s10346_accel_node, + }, + .adapter_path = "\\_SB_.I2C3", + .irq_data = { + .type = X86_ACPI_IRQ_TYPE_GPIOINT, + .chip = "INT33FC:02", + .index = 23, + .trigger = ACPI_EDGE_SENSITIVE, + .polarity = ACPI_ACTIVE_HIGH, + }, + }, { + /* goodix touchscreen */ + .board_info = { + .type = "GDIX1001:00", + .addr = 0x14, + .dev_name = "goodix_ts", + .swnode = &medion_lifetab_s10346_touchscreen_node, + }, + .adapter_path = "\\_SB_.I2C4", + .irq_data = { + .type = X86_ACPI_IRQ_TYPE_APIC, + .index = 0x44, + .trigger = ACPI_EDGE_SENSITIVE, + .polarity = ACPI_ACTIVE_LOW, + }, + }, +}; + +static struct gpiod_lookup_table medion_lifetab_s10346_goodix_gpios = { + .dev_id = "i2c-goodix_ts", + .table = { + GPIO_LOOKUP("INT33FC:01", 26, "reset", GPIO_ACTIVE_HIGH), + GPIO_LOOKUP("INT33FC:02", 3, "irq", GPIO_ACTIVE_HIGH), + { } + }, +}; + +static struct gpiod_lookup_table * const medion_lifetab_s10346_gpios[] = { + &medion_lifetab_s10346_goodix_gpios, + NULL +}; + +const struct x86_dev_info medion_lifetab_s10346_info __initconst = { + .i2c_client_info = medion_lifetab_s10346_i2c_clients, + .i2c_client_count = ARRAY_SIZE(medion_lifetab_s10346_i2c_clients), + .gpiod_lookup_tables = medion_lifetab_s10346_gpios, +}; + +/* Nextbook Ares 8 tablets have an Android factory img with everything hardcoded */ +static const char * const nextbook_ares8_accel_mount_matrix[] = { + "0", "-1", "0", + "-1", "0", "0", + "0", "0", "1" +}; + +static const struct property_entry nextbook_ares8_accel_props[] = { + PROPERTY_ENTRY_STRING_ARRAY("mount-matrix", nextbook_ares8_accel_mount_matrix), + { } +}; + +static const struct software_node nextbook_ares8_accel_node = { + .properties = nextbook_ares8_accel_props, +}; + +static const struct property_entry nextbook_ares8_touchscreen_props[] = { + PROPERTY_ENTRY_U32("touchscreen-size-x", 800), + PROPERTY_ENTRY_U32("touchscreen-size-y", 1280), + { } +}; + +static const struct software_node nextbook_ares8_touchscreen_node = { + .properties = nextbook_ares8_touchscreen_props, +}; + +static const struct x86_i2c_client_info nextbook_ares8_i2c_clients[] __initconst = { + { + /* Freescale MMA8653FC accel */ + .board_info = { + .type = "mma8653", + .addr = 0x1d, + .dev_name = "mma8653", + .swnode = &nextbook_ares8_accel_node, + }, + .adapter_path = "\\_SB_.I2C3", + }, { + /* FT5416DQ9 touchscreen controller */ + .board_info = { + .type = "edt-ft5x06", + .addr = 0x38, + .dev_name = "ft5416", + .swnode = &nextbook_ares8_touchscreen_node, + }, + .adapter_path = "\\_SB_.I2C4", + .irq_data = { + .type = X86_ACPI_IRQ_TYPE_GPIOINT, + .chip = "INT33FC:02", + .index = 3, + .trigger = ACPI_EDGE_SENSITIVE, + .polarity = ACPI_ACTIVE_LOW, + }, + }, +}; + +static struct gpiod_lookup_table * const nextbook_ares8_gpios[] = { + &int3496_reference_gpios, + NULL +}; + +const struct x86_dev_info nextbook_ares8_info __initconst = { + .i2c_client_info = nextbook_ares8_i2c_clients, + .i2c_client_count = ARRAY_SIZE(nextbook_ares8_i2c_clients), + .pdev_info = int3496_pdevs, + .pdev_count = 1, + .gpiod_lookup_tables = nextbook_ares8_gpios, + .invalid_aei_gpiochip = "INT33FC:02", +}; + +/* + * Whitelabel (sold as various brands) TM800A550L tablets. + * These tablet's DSDT contains a whole bunch of bogus ACPI I2C devices + * (removed through acpi_quirk_skip_i2c_client_enumeration()) and + * the touchscreen fwnode has the wrong GPIOs. + */ +static const char * const whitelabel_tm800a550l_accel_mount_matrix[] = { + "-1", "0", "0", + "0", "1", "0", + "0", "0", "1" +}; + +static const struct property_entry whitelabel_tm800a550l_accel_props[] = { + PROPERTY_ENTRY_STRING_ARRAY("mount-matrix", whitelabel_tm800a550l_accel_mount_matrix), + { } +}; + +static const struct software_node whitelabel_tm800a550l_accel_node = { + .properties = whitelabel_tm800a550l_accel_props, +}; + +static const struct property_entry whitelabel_tm800a550l_goodix_props[] = { + PROPERTY_ENTRY_STRING("firmware-name", "gt912-tm800a550l.fw"), + PROPERTY_ENTRY_STRING("goodix,config-name", "gt912-tm800a550l.cfg"), + PROPERTY_ENTRY_U32("goodix,main-clk", 54), + { } +}; + +static const struct software_node whitelabel_tm800a550l_goodix_node = { + .properties = whitelabel_tm800a550l_goodix_props, +}; + +static const struct x86_i2c_client_info whitelabel_tm800a550l_i2c_clients[] __initconst = { + { + /* goodix touchscreen */ + .board_info = { + .type = "GDIX1001:00", + .addr = 0x14, + .dev_name = "goodix_ts", + .swnode = &whitelabel_tm800a550l_goodix_node, + }, + .adapter_path = "\\_SB_.I2C2", + .irq_data = { + .type = X86_ACPI_IRQ_TYPE_APIC, + .index = 0x44, + .trigger = ACPI_EDGE_SENSITIVE, + .polarity = ACPI_ACTIVE_HIGH, + }, + }, { + /* kxcj91008 accel */ + .board_info = { + .type = "kxcj91008", + .addr = 0x0f, + .dev_name = "kxcj91008", + .swnode = &whitelabel_tm800a550l_accel_node, + }, + .adapter_path = "\\_SB_.I2C3", + }, +}; + +static struct gpiod_lookup_table whitelabel_tm800a550l_goodix_gpios = { + .dev_id = "i2c-goodix_ts", + .table = { + GPIO_LOOKUP("INT33FC:01", 26, "reset", GPIO_ACTIVE_HIGH), + GPIO_LOOKUP("INT33FC:02", 3, "irq", GPIO_ACTIVE_HIGH), + { } + }, +}; + +static struct gpiod_lookup_table * const whitelabel_tm800a550l_gpios[] = { + &whitelabel_tm800a550l_goodix_gpios, + NULL +}; + +const struct x86_dev_info whitelabel_tm800a550l_info __initconst = { + .i2c_client_info = whitelabel_tm800a550l_i2c_clients, + .i2c_client_count = ARRAY_SIZE(whitelabel_tm800a550l_i2c_clients), + .gpiod_lookup_tables = whitelabel_tm800a550l_gpios, +}; + +/* + * If the EFI bootloader is not Xiaomi's own signed Android loader, then the + * Xiaomi Mi Pad 2 X86 tablet sets OSID in the DSDT to 1 (Windows), causing + * a bunch of devices to be hidden. + * + * This takes care of instantiating the hidden devices manually. + */ +static const struct x86_i2c_client_info xiaomi_mipad2_i2c_clients[] __initconst = { + { + /* BQ27520 fuel-gauge */ + .board_info = { + .type = "bq27520", + .addr = 0x55, + .dev_name = "bq27520", + .swnode = &fg_bq25890_supply_node, + }, + .adapter_path = "\\_SB_.PCI0.I2C1", + }, { + /* KTD2026 RGB notification LED controller */ + .board_info = { + .type = "ktd2026", + .addr = 0x30, + .dev_name = "ktd2026", + }, + .adapter_path = "\\_SB_.PCI0.I2C3", + }, +}; + +const struct x86_dev_info xiaomi_mipad2_info __initconst = { + .i2c_client_info = xiaomi_mipad2_i2c_clients, + .i2c_client_count = ARRAY_SIZE(xiaomi_mipad2_i2c_clients), +}; diff --git a/drivers/platform/x86/x86-android-tablets/x86-android-tablets-main.c b/drivers/platform/x86/x86-android-tablets/x86-android-tablets-main.c deleted file mode 100644 index 0362194e225c..000000000000 --- a/drivers/platform/x86/x86-android-tablets/x86-android-tablets-main.c +++ /dev/null @@ -1,523 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0+ -/* - * DMI based code to deal with broken DSDTs on X86 tablets which ship with - * Android as (part of) the factory image. The factory kernels shipped on these - * devices typically have a bunch of things hardcoded, rather than specified - * in their DSDT. - * - * Copyright (C) 2021-2022 Hans de Goede - */ - -#include -#include -#include -#include -#include -#include - -#include "shared-psy-info.h" -#include "x86-android-tablets.h" - -/* Acer Iconia One 7 B1-750 has an Android factory img with everything hardcoded */ -static const char * const acer_b1_750_mount_matrix[] = { - "-1", "0", "0", - "0", "1", "0", - "0", "0", "1" -}; - -static const struct property_entry acer_b1_750_bma250e_props[] = { - PROPERTY_ENTRY_STRING_ARRAY("mount-matrix", acer_b1_750_mount_matrix), - { } -}; - -static const struct software_node acer_b1_750_bma250e_node = { - .properties = acer_b1_750_bma250e_props, -}; - -static const struct x86_i2c_client_info acer_b1_750_i2c_clients[] __initconst = { - { - /* Novatek NVT-ts touchscreen */ - .board_info = { - .type = "NVT-ts", - .addr = 0x34, - .dev_name = "NVT-ts", - }, - .adapter_path = "\\_SB_.I2C4", - .irq_data = { - .type = X86_ACPI_IRQ_TYPE_GPIOINT, - .chip = "INT33FC:02", - .index = 3, - .trigger = ACPI_EDGE_SENSITIVE, - .polarity = ACPI_ACTIVE_LOW, - }, - }, { - /* BMA250E accelerometer */ - .board_info = { - .type = "bma250e", - .addr = 0x18, - .swnode = &acer_b1_750_bma250e_node, - }, - .adapter_path = "\\_SB_.I2C3", - .irq_data = { - .type = X86_ACPI_IRQ_TYPE_GPIOINT, - .chip = "INT33FC:02", - .index = 25, - .trigger = ACPI_LEVEL_SENSITIVE, - .polarity = ACPI_ACTIVE_HIGH, - }, - }, -}; - -static struct gpiod_lookup_table acer_b1_750_goodix_gpios = { - .dev_id = "i2c-NVT-ts", - .table = { - GPIO_LOOKUP("INT33FC:01", 26, "reset", GPIO_ACTIVE_LOW), - { } - }, -}; - -static struct gpiod_lookup_table * const acer_b1_750_gpios[] = { - &acer_b1_750_goodix_gpios, - &int3496_reference_gpios, - NULL -}; - -const struct x86_dev_info acer_b1_750_info __initconst = { - .i2c_client_info = acer_b1_750_i2c_clients, - .i2c_client_count = ARRAY_SIZE(acer_b1_750_i2c_clients), - .pdev_info = int3496_pdevs, - .pdev_count = 1, - .gpiod_lookup_tables = acer_b1_750_gpios, -}; - -/* - * Advantech MICA-071 - * This is a standard Windows tablet, but it has an extra "quick launch" button - * which is not described in the ACPI tables in anyway. - * Use the x86-android-tablets infra to create a gpio-button device for this. - */ -static struct gpio_keys_button advantech_mica_071_button = { - .code = KEY_PROG1, - /* .gpio gets filled in by advantech_mica_071_init() */ - .active_low = true, - .desc = "prog1_key", - .type = EV_KEY, - .wakeup = false, - .debounce_interval = 50, -}; - -static const struct gpio_keys_platform_data advantech_mica_071_button_pdata __initconst = { - .buttons = &advantech_mica_071_button, - .nbuttons = 1, - .name = "prog1_key", -}; - -static const struct platform_device_info advantech_mica_071_pdevs[] __initconst = { - { - .name = "gpio-keys", - .id = PLATFORM_DEVID_AUTO, - .data = &advantech_mica_071_button_pdata, - .size_data = sizeof(advantech_mica_071_button_pdata), - }, -}; - -static int __init advantech_mica_071_init(void) -{ - struct gpio_desc *gpiod; - int ret; - - ret = x86_android_tablet_get_gpiod("INT33FC:00", 2, &gpiod); - if (ret < 0) - return ret; - advantech_mica_071_button.gpio = desc_to_gpio(gpiod); - - return 0; -} - -const struct x86_dev_info advantech_mica_071_info __initconst = { - .pdev_info = advantech_mica_071_pdevs, - .pdev_count = ARRAY_SIZE(advantech_mica_071_pdevs), - .init = advantech_mica_071_init, -}; - -/* - * When booted with the BIOS set to Android mode the Chuwi Hi8 (CWI509) DSDT - * contains a whole bunch of bogus ACPI I2C devices and is missing entries - * for the touchscreen and the accelerometer. - */ -static const struct property_entry chuwi_hi8_gsl1680_props[] = { - PROPERTY_ENTRY_U32("touchscreen-size-x", 1665), - PROPERTY_ENTRY_U32("touchscreen-size-y", 1140), - PROPERTY_ENTRY_BOOL("touchscreen-swapped-x-y"), - PROPERTY_ENTRY_BOOL("silead,home-button"), - PROPERTY_ENTRY_STRING("firmware-name", "gsl1680-chuwi-hi8.fw"), - { } -}; - -static const struct software_node chuwi_hi8_gsl1680_node = { - .properties = chuwi_hi8_gsl1680_props, -}; - -static const char * const chuwi_hi8_mount_matrix[] = { - "1", "0", "0", - "0", "-1", "0", - "0", "0", "1" -}; - -static const struct property_entry chuwi_hi8_bma250e_props[] = { - PROPERTY_ENTRY_STRING_ARRAY("mount-matrix", chuwi_hi8_mount_matrix), - { } -}; - -static const struct software_node chuwi_hi8_bma250e_node = { - .properties = chuwi_hi8_bma250e_props, -}; - -static const struct x86_i2c_client_info chuwi_hi8_i2c_clients[] __initconst = { - { - /* Silead touchscreen */ - .board_info = { - .type = "gsl1680", - .addr = 0x40, - .swnode = &chuwi_hi8_gsl1680_node, - }, - .adapter_path = "\\_SB_.I2C4", - .irq_data = { - .type = X86_ACPI_IRQ_TYPE_APIC, - .index = 0x44, - .trigger = ACPI_EDGE_SENSITIVE, - .polarity = ACPI_ACTIVE_HIGH, - }, - }, { - /* BMA250E accelerometer */ - .board_info = { - .type = "bma250e", - .addr = 0x18, - .swnode = &chuwi_hi8_bma250e_node, - }, - .adapter_path = "\\_SB_.I2C3", - .irq_data = { - .type = X86_ACPI_IRQ_TYPE_GPIOINT, - .chip = "INT33FC:02", - .index = 23, - .trigger = ACPI_LEVEL_SENSITIVE, - .polarity = ACPI_ACTIVE_HIGH, - }, - }, -}; - -static int __init chuwi_hi8_init(void) -{ - /* - * Avoid the acpi_unregister_gsi() call in x86_acpi_irq_helper_get() - * breaking the touchscreen + logging various errors when the Windows - * BIOS is used. - */ - if (acpi_dev_present("MSSL0001", NULL, 1)) - return -ENODEV; - - return 0; -} - -const struct x86_dev_info chuwi_hi8_info __initconst = { - .i2c_client_info = chuwi_hi8_i2c_clients, - .i2c_client_count = ARRAY_SIZE(chuwi_hi8_i2c_clients), - .init = chuwi_hi8_init, -}; - -#define CZC_EC_EXTRA_PORT 0x68 -#define CZC_EC_ANDROID_KEYS 0x63 - -static int __init czc_p10t_init(void) -{ - /* - * The device boots up in "Windows 7" mode, when the home button sends a - * Windows specific key sequence (Left Meta + D) and the second button - * sends an unknown one while also toggling the Radio Kill Switch. - * This is a surprising behavior when the second button is labeled "Back". - * - * The vendor-supplied Android-x86 build switches the device to a "Android" - * mode by writing value 0x63 to the I/O port 0x68. This just seems to just - * set bit 6 on address 0x96 in the EC region; switching the bit directly - * seems to achieve the same result. It uses a "p10t_switcher" to do the - * job. It doesn't seem to be able to do anything else, and no other use - * of the port 0x68 is known. - * - * In the Android mode, the home button sends just a single scancode, - * which can be handled in Linux userspace more reasonably and the back - * button only sends a scancode without toggling the kill switch. - * The scancode can then be mapped either to Back or RF Kill functionality - * in userspace, depending on how the button is labeled on that particular - * model. - */ - outb(CZC_EC_ANDROID_KEYS, CZC_EC_EXTRA_PORT); - return 0; -} - -const struct x86_dev_info czc_p10t __initconst = { - .init = czc_p10t_init, -}; - -/* Medion Lifetab S10346 tablets have an Android factory img with everything hardcoded */ -static const char * const medion_lifetab_s10346_accel_mount_matrix[] = { - "0", "1", "0", - "1", "0", "0", - "0", "0", "1" -}; - -static const struct property_entry medion_lifetab_s10346_accel_props[] = { - PROPERTY_ENTRY_STRING_ARRAY("mount-matrix", medion_lifetab_s10346_accel_mount_matrix), - { } -}; - -static const struct software_node medion_lifetab_s10346_accel_node = { - .properties = medion_lifetab_s10346_accel_props, -}; - -/* Note the LCD panel is mounted upside down, this is correctly indicated in the VBT */ -static const struct property_entry medion_lifetab_s10346_touchscreen_props[] = { - PROPERTY_ENTRY_BOOL("touchscreen-inverted-x"), - PROPERTY_ENTRY_BOOL("touchscreen-swapped-x-y"), - { } -}; - -static const struct software_node medion_lifetab_s10346_touchscreen_node = { - .properties = medion_lifetab_s10346_touchscreen_props, -}; - -static const struct x86_i2c_client_info medion_lifetab_s10346_i2c_clients[] __initconst = { - { - /* kxtj21009 accel */ - .board_info = { - .type = "kxtj21009", - .addr = 0x0f, - .dev_name = "kxtj21009", - .swnode = &medion_lifetab_s10346_accel_node, - }, - .adapter_path = "\\_SB_.I2C3", - .irq_data = { - .type = X86_ACPI_IRQ_TYPE_GPIOINT, - .chip = "INT33FC:02", - .index = 23, - .trigger = ACPI_EDGE_SENSITIVE, - .polarity = ACPI_ACTIVE_HIGH, - }, - }, { - /* goodix touchscreen */ - .board_info = { - .type = "GDIX1001:00", - .addr = 0x14, - .dev_name = "goodix_ts", - .swnode = &medion_lifetab_s10346_touchscreen_node, - }, - .adapter_path = "\\_SB_.I2C4", - .irq_data = { - .type = X86_ACPI_IRQ_TYPE_APIC, - .index = 0x44, - .trigger = ACPI_EDGE_SENSITIVE, - .polarity = ACPI_ACTIVE_LOW, - }, - }, -}; - -static struct gpiod_lookup_table medion_lifetab_s10346_goodix_gpios = { - .dev_id = "i2c-goodix_ts", - .table = { - GPIO_LOOKUP("INT33FC:01", 26, "reset", GPIO_ACTIVE_HIGH), - GPIO_LOOKUP("INT33FC:02", 3, "irq", GPIO_ACTIVE_HIGH), - { } - }, -}; - -static struct gpiod_lookup_table * const medion_lifetab_s10346_gpios[] = { - &medion_lifetab_s10346_goodix_gpios, - NULL -}; - -const struct x86_dev_info medion_lifetab_s10346_info __initconst = { - .i2c_client_info = medion_lifetab_s10346_i2c_clients, - .i2c_client_count = ARRAY_SIZE(medion_lifetab_s10346_i2c_clients), - .gpiod_lookup_tables = medion_lifetab_s10346_gpios, -}; - -/* Nextbook Ares 8 tablets have an Android factory img with everything hardcoded */ -static const char * const nextbook_ares8_accel_mount_matrix[] = { - "0", "-1", "0", - "-1", "0", "0", - "0", "0", "1" -}; - -static const struct property_entry nextbook_ares8_accel_props[] = { - PROPERTY_ENTRY_STRING_ARRAY("mount-matrix", nextbook_ares8_accel_mount_matrix), - { } -}; - -static const struct software_node nextbook_ares8_accel_node = { - .properties = nextbook_ares8_accel_props, -}; - -static const struct property_entry nextbook_ares8_touchscreen_props[] = { - PROPERTY_ENTRY_U32("touchscreen-size-x", 800), - PROPERTY_ENTRY_U32("touchscreen-size-y", 1280), - { } -}; - -static const struct software_node nextbook_ares8_touchscreen_node = { - .properties = nextbook_ares8_touchscreen_props, -}; - -static const struct x86_i2c_client_info nextbook_ares8_i2c_clients[] __initconst = { - { - /* Freescale MMA8653FC accel */ - .board_info = { - .type = "mma8653", - .addr = 0x1d, - .dev_name = "mma8653", - .swnode = &nextbook_ares8_accel_node, - }, - .adapter_path = "\\_SB_.I2C3", - }, { - /* FT5416DQ9 touchscreen controller */ - .board_info = { - .type = "edt-ft5x06", - .addr = 0x38, - .dev_name = "ft5416", - .swnode = &nextbook_ares8_touchscreen_node, - }, - .adapter_path = "\\_SB_.I2C4", - .irq_data = { - .type = X86_ACPI_IRQ_TYPE_GPIOINT, - .chip = "INT33FC:02", - .index = 3, - .trigger = ACPI_EDGE_SENSITIVE, - .polarity = ACPI_ACTIVE_LOW, - }, - }, -}; - -static struct gpiod_lookup_table * const nextbook_ares8_gpios[] = { - &int3496_reference_gpios, - NULL -}; - -const struct x86_dev_info nextbook_ares8_info __initconst = { - .i2c_client_info = nextbook_ares8_i2c_clients, - .i2c_client_count = ARRAY_SIZE(nextbook_ares8_i2c_clients), - .pdev_info = int3496_pdevs, - .pdev_count = 1, - .gpiod_lookup_tables = nextbook_ares8_gpios, - .invalid_aei_gpiochip = "INT33FC:02", -}; - -/* - * Whitelabel (sold as various brands) TM800A550L tablets. - * These tablet's DSDT contains a whole bunch of bogus ACPI I2C devices - * (removed through acpi_quirk_skip_i2c_client_enumeration()) and - * the touchscreen fwnode has the wrong GPIOs. - */ -static const char * const whitelabel_tm800a550l_accel_mount_matrix[] = { - "-1", "0", "0", - "0", "1", "0", - "0", "0", "1" -}; - -static const struct property_entry whitelabel_tm800a550l_accel_props[] = { - PROPERTY_ENTRY_STRING_ARRAY("mount-matrix", whitelabel_tm800a550l_accel_mount_matrix), - { } -}; - -static const struct software_node whitelabel_tm800a550l_accel_node = { - .properties = whitelabel_tm800a550l_accel_props, -}; - -static const struct property_entry whitelabel_tm800a550l_goodix_props[] = { - PROPERTY_ENTRY_STRING("firmware-name", "gt912-tm800a550l.fw"), - PROPERTY_ENTRY_STRING("goodix,config-name", "gt912-tm800a550l.cfg"), - PROPERTY_ENTRY_U32("goodix,main-clk", 54), - { } -}; - -static const struct software_node whitelabel_tm800a550l_goodix_node = { - .properties = whitelabel_tm800a550l_goodix_props, -}; - -static const struct x86_i2c_client_info whitelabel_tm800a550l_i2c_clients[] __initconst = { - { - /* goodix touchscreen */ - .board_info = { - .type = "GDIX1001:00", - .addr = 0x14, - .dev_name = "goodix_ts", - .swnode = &whitelabel_tm800a550l_goodix_node, - }, - .adapter_path = "\\_SB_.I2C2", - .irq_data = { - .type = X86_ACPI_IRQ_TYPE_APIC, - .index = 0x44, - .trigger = ACPI_EDGE_SENSITIVE, - .polarity = ACPI_ACTIVE_HIGH, - }, - }, { - /* kxcj91008 accel */ - .board_info = { - .type = "kxcj91008", - .addr = 0x0f, - .dev_name = "kxcj91008", - .swnode = &whitelabel_tm800a550l_accel_node, - }, - .adapter_path = "\\_SB_.I2C3", - }, -}; - -static struct gpiod_lookup_table whitelabel_tm800a550l_goodix_gpios = { - .dev_id = "i2c-goodix_ts", - .table = { - GPIO_LOOKUP("INT33FC:01", 26, "reset", GPIO_ACTIVE_HIGH), - GPIO_LOOKUP("INT33FC:02", 3, "irq", GPIO_ACTIVE_HIGH), - { } - }, -}; - -static struct gpiod_lookup_table * const whitelabel_tm800a550l_gpios[] = { - &whitelabel_tm800a550l_goodix_gpios, - NULL -}; - -const struct x86_dev_info whitelabel_tm800a550l_info __initconst = { - .i2c_client_info = whitelabel_tm800a550l_i2c_clients, - .i2c_client_count = ARRAY_SIZE(whitelabel_tm800a550l_i2c_clients), - .gpiod_lookup_tables = whitelabel_tm800a550l_gpios, -}; - -/* - * If the EFI bootloader is not Xiaomi's own signed Android loader, then the - * Xiaomi Mi Pad 2 X86 tablet sets OSID in the DSDT to 1 (Windows), causing - * a bunch of devices to be hidden. - * - * This takes care of instantiating the hidden devices manually. - */ -static const struct x86_i2c_client_info xiaomi_mipad2_i2c_clients[] __initconst = { - { - /* BQ27520 fuel-gauge */ - .board_info = { - .type = "bq27520", - .addr = 0x55, - .dev_name = "bq27520", - .swnode = &fg_bq25890_supply_node, - }, - .adapter_path = "\\_SB_.PCI0.I2C1", - }, { - /* KTD2026 RGB notification LED controller */ - .board_info = { - .type = "ktd2026", - .addr = 0x30, - .dev_name = "ktd2026", - }, - .adapter_path = "\\_SB_.PCI0.I2C3", - }, -}; - -const struct x86_dev_info xiaomi_mipad2_info __initconst = { - .i2c_client_info = xiaomi_mipad2_i2c_clients, - .i2c_client_count = ARRAY_SIZE(xiaomi_mipad2_i2c_clients), -}; -- cgit v1.2.3 From e2200d3f26dac108a519f014fb42a0a034105394 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Sun, 19 Feb 2023 22:50:19 +0100 Subject: platform/x86: x86-android-tablets: Add gpio_keys support to x86_android_tablet_init() Add gpio_keys instantation support to x86_android_tablet_init(), to avoid this having to be repeated in various x86_dev_info.init() functions. Signed-off-by: Hans de Goede Reviewed-by: Andy Shevchenko Link: https://lore.kernel.org/r/20230301092331.7038-10-hdegoede@redhat.com --- drivers/platform/x86/x86-android-tablets/asus.c | 65 ++++++---------------- drivers/platform/x86/x86-android-tablets/core.c | 30 +++++++++- drivers/platform/x86/x86-android-tablets/other.c | 51 ++++------------- .../x86/x86-android-tablets/x86-android-tablets.h | 8 +++ 4 files changed, 64 insertions(+), 90 deletions(-) (limited to 'drivers/platform') diff --git a/drivers/platform/x86/x86-android-tablets/asus.c b/drivers/platform/x86/x86-android-tablets/asus.c index 74c3b10e9ca7..cfa038b44b43 100644 --- a/drivers/platform/x86/x86-android-tablets/asus.c +++ b/drivers/platform/x86/x86-android-tablets/asus.c @@ -8,8 +8,6 @@ * Copyright (C) 2021-2023 Hans de Goede */ -#include -#include #include #include #include @@ -26,50 +24,19 @@ static struct gpiod_lookup_table int3496_gpo2_pin22_gpios = { }, }; -static struct gpio_keys_button asus_me176c_tf103c_lid = { - .code = SW_LID, - /* .gpio gets filled in by asus_me176c_tf103c_init() */ - .active_low = true, - .desc = "lid_sw", - .type = EV_SW, - .wakeup = true, - .debounce_interval = 50, -}; - -static const struct gpio_keys_platform_data asus_me176c_tf103c_lid_pdata __initconst = { - .buttons = &asus_me176c_tf103c_lid, - .nbuttons = 1, - .name = "lid_sw", -}; - -static const struct platform_device_info asus_me176c_tf103c_pdevs[] __initconst = { - { - .name = "gpio-keys", - .id = PLATFORM_DEVID_AUTO, - .data = &asus_me176c_tf103c_lid_pdata, - .size_data = sizeof(asus_me176c_tf103c_lid_pdata), - }, - { - /* For micro USB ID pin handling */ - .name = "intel-int3496", - .id = PLATFORM_DEVID_NONE, +static struct x86_gpio_button asus_me176c_tf103c_lid = { + .button = { + .code = SW_LID, + .active_low = true, + .desc = "lid_sw", + .type = EV_SW, + .wakeup = true, + .debounce_interval = 50, }, + .chip = "INT33FC:02", + .pin = 12, }; -static int __init asus_me176c_tf103c_init(void) -{ - struct gpio_desc *gpiod; - int ret; - - ret = x86_android_tablet_get_gpiod("INT33FC:02", 12, &gpiod); - if (ret < 0) - return ret; - asus_me176c_tf103c_lid.gpio = desc_to_gpio(gpiod); - - return 0; -} - - /* Asus ME176C tablets have an Android factory img with everything hardcoded */ static const char * const asus_me176c_accel_mount_matrix[] = { "-1", "0", "0", @@ -203,15 +170,15 @@ static struct gpiod_lookup_table * const asus_me176c_gpios[] = { const struct x86_dev_info asus_me176c_info __initconst = { .i2c_client_info = asus_me176c_i2c_clients, .i2c_client_count = ARRAY_SIZE(asus_me176c_i2c_clients), - .pdev_info = asus_me176c_tf103c_pdevs, - .pdev_count = ARRAY_SIZE(asus_me176c_tf103c_pdevs), + .pdev_info = int3496_pdevs, + .pdev_count = 1, .serdev_info = asus_me176c_serdevs, .serdev_count = ARRAY_SIZE(asus_me176c_serdevs), + .gpio_button = &asus_me176c_tf103c_lid, .gpiod_lookup_tables = asus_me176c_gpios, .bat_swnode = &generic_lipo_hv_4v35_battery_node, .modules = bq24190_modules, .invalid_aei_gpiochip = "INT33FC:02", - .init = asus_me176c_tf103c_init, }; /* Asus TF103C tablets have an Android factory img with everything hardcoded */ @@ -348,11 +315,11 @@ static struct gpiod_lookup_table * const asus_tf103c_gpios[] = { const struct x86_dev_info asus_tf103c_info __initconst = { .i2c_client_info = asus_tf103c_i2c_clients, .i2c_client_count = ARRAY_SIZE(asus_tf103c_i2c_clients), - .pdev_info = asus_me176c_tf103c_pdevs, - .pdev_count = ARRAY_SIZE(asus_me176c_tf103c_pdevs), + .pdev_info = int3496_pdevs, + .pdev_count = 1, + .gpio_button = &asus_me176c_tf103c_lid, .gpiod_lookup_tables = asus_tf103c_gpios, .bat_swnode = &asus_tf103c_battery_node, .modules = bq24190_modules, .invalid_aei_gpiochip = "INT33FC:02", - .init = asus_me176c_tf103c_init, }; diff --git a/drivers/platform/x86/x86-android-tablets/core.c b/drivers/platform/x86/x86-android-tablets/core.c index 779d49c66987..245167674aa2 100644 --- a/drivers/platform/x86/x86-android-tablets/core.c +++ b/drivers/platform/x86/x86-android-tablets/core.c @@ -322,7 +322,8 @@ static __init int x86_android_tablet_init(void) } } - pdevs = kcalloc(dev_info->pdev_count, sizeof(*pdevs), GFP_KERNEL); + /* + 1 to make space for (optional) gpio_keys_button pdev */ + pdevs = kcalloc(dev_info->pdev_count + 1, sizeof(*pdevs), GFP_KERNEL); if (!pdevs) { x86_android_tablet_cleanup(); return -ENOMEM; @@ -352,6 +353,33 @@ static __init int x86_android_tablet_init(void) } } + if (dev_info->gpio_button) { + struct gpio_keys_platform_data pdata = { + .buttons = &dev_info->gpio_button->button, + .nbuttons = 1, + }; + struct gpio_desc *gpiod; + + /* Get GPIO for the gpio-button */ + ret = x86_android_tablet_get_gpiod(dev_info->gpio_button->chip, + dev_info->gpio_button->pin, &gpiod); + if (ret < 0) { + x86_android_tablet_cleanup(); + return ret; + } + + dev_info->gpio_button->button.gpio = desc_to_gpio(gpiod); + + pdevs[pdev_count] = platform_device_register_data(NULL, "gpio-keys", + PLATFORM_DEVID_AUTO, + &pdata, sizeof(pdata)); + if (IS_ERR(pdevs[pdev_count])) { + x86_android_tablet_cleanup(); + return PTR_ERR(pdevs[pdev_count]); + } + pdev_count++; + } + return 0; } diff --git a/drivers/platform/x86/x86-android-tablets/other.c b/drivers/platform/x86/x86-android-tablets/other.c index 83b30b24d57f..c1bde42315c4 100644 --- a/drivers/platform/x86/x86-android-tablets/other.c +++ b/drivers/platform/x86/x86-android-tablets/other.c @@ -9,8 +9,6 @@ */ #include -#include -#include #include #include #include @@ -96,48 +94,21 @@ const struct x86_dev_info acer_b1_750_info __initconst = { * which is not described in the ACPI tables in anyway. * Use the x86-android-tablets infra to create a gpio-button device for this. */ -static struct gpio_keys_button advantech_mica_071_button = { - .code = KEY_PROG1, - /* .gpio gets filled in by advantech_mica_071_init() */ - .active_low = true, - .desc = "prog1_key", - .type = EV_KEY, - .wakeup = false, - .debounce_interval = 50, -}; - -static const struct gpio_keys_platform_data advantech_mica_071_button_pdata __initconst = { - .buttons = &advantech_mica_071_button, - .nbuttons = 1, - .name = "prog1_key", -}; - -static const struct platform_device_info advantech_mica_071_pdevs[] __initconst = { - { - .name = "gpio-keys", - .id = PLATFORM_DEVID_AUTO, - .data = &advantech_mica_071_button_pdata, - .size_data = sizeof(advantech_mica_071_button_pdata), +static struct x86_gpio_button advantech_mica_071_button = { + .button = { + .code = KEY_PROG1, + .active_low = true, + .desc = "prog1_key", + .type = EV_KEY, + .wakeup = false, + .debounce_interval = 50, }, + .chip = "INT33FC:00", + .pin = 2, }; -static int __init advantech_mica_071_init(void) -{ - struct gpio_desc *gpiod; - int ret; - - ret = x86_android_tablet_get_gpiod("INT33FC:00", 2, &gpiod); - if (ret < 0) - return ret; - advantech_mica_071_button.gpio = desc_to_gpio(gpiod); - - return 0; -} - const struct x86_dev_info advantech_mica_071_info __initconst = { - .pdev_info = advantech_mica_071_pdevs, - .pdev_count = ARRAY_SIZE(advantech_mica_071_pdevs), - .init = advantech_mica_071_init, + .gpio_button = &advantech_mica_071_button, }; /* diff --git a/drivers/platform/x86/x86-android-tablets/x86-android-tablets.h b/drivers/platform/x86/x86-android-tablets/x86-android-tablets.h index 19994b501903..fb499a2c42d9 100644 --- a/drivers/platform/x86/x86-android-tablets/x86-android-tablets.h +++ b/drivers/platform/x86/x86-android-tablets/x86-android-tablets.h @@ -10,6 +10,7 @@ #ifndef __PDX86_X86_ANDROID_TABLETS_H #define __PDX86_X86_ANDROID_TABLETS_H +#include #include #include @@ -58,6 +59,12 @@ struct x86_serdev_info { const char *serdev_hid; }; +struct x86_gpio_button { + struct gpio_keys_button button; + const char *chip; + int pin; +}; + struct x86_dev_info { char *invalid_aei_gpiochip; const char * const *modules; @@ -66,6 +73,7 @@ struct x86_dev_info { const struct x86_i2c_client_info *i2c_client_info; const struct platform_device_info *pdev_info; const struct x86_serdev_info *serdev_info; + struct x86_gpio_button *gpio_button; int i2c_client_count; int pdev_count; int serdev_count; -- cgit v1.2.3 From 772cbba5a877eb7c528f3659415373c40e48a057 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Mon, 20 Feb 2023 21:26:42 +0100 Subject: platform/x86: x86-android-tablets: Add support for the Dolby button on Peaq C1010 The Peaq C1010 tablet has a special "Dolby" button. This button has a WMI interface, but this is broken in several ways: 1. It only supports polling 2. The value read on polling goes from 0 -> 1 for one poll on both edges of the button, with no way to tell which edge causes the poll to return 1. 3. It uses a non unique GUID (it uses the Microsoft docs WMI example GUID). There currently is a WMI driver for this, but it uses several kludges to work around these issues and is not entirely reliable due to 2. Replace the unreliable WMI driver by using the x86-android-tablets code to instantiate a gpio_keys device for this. Signed-off-by: Hans de Goede Reviewed-by: Andy Shevchenko Link: https://lore.kernel.org/r/20230301092331.7038-11-hdegoede@redhat.com --- MAINTAINERS | 6 - drivers/platform/x86/Kconfig | 7 -- drivers/platform/x86/Makefile | 1 - drivers/platform/x86/peaq-wmi.c | 128 --------------------- drivers/platform/x86/x86-android-tablets/dmi.c | 8 ++ drivers/platform/x86/x86-android-tablets/other.c | 28 +++++ .../x86/x86-android-tablets/x86-android-tablets.h | 1 + 7 files changed, 37 insertions(+), 142 deletions(-) delete mode 100644 drivers/platform/x86/peaq-wmi.c (limited to 'drivers/platform') diff --git a/MAINTAINERS b/MAINTAINERS index 7b08a58506e3..f32538373164 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -16331,12 +16331,6 @@ S: Maintained F: crypto/pcrypt.c F: include/crypto/pcrypt.h -PEAQ WMI HOTKEYS DRIVER -M: Hans de Goede -L: platform-driver-x86@vger.kernel.org -S: Maintained -F: drivers/platform/x86/peaq-wmi.c - PECI HARDWARE MONITORING DRIVERS M: Iwona Winiarska L: linux-hwmon@vger.kernel.org diff --git a/drivers/platform/x86/Kconfig b/drivers/platform/x86/Kconfig index 46d9e3130ebb..d2619e7025c7 100644 --- a/drivers/platform/x86/Kconfig +++ b/drivers/platform/x86/Kconfig @@ -84,13 +84,6 @@ config MXM_WMI MXM is a standard for laptop graphics cards, the WMI interface is required for switchable nvidia graphics machines -config PEAQ_WMI - tristate "PEAQ 2-in-1 WMI hotkey driver" - depends on ACPI_WMI - depends on INPUT - help - Say Y here if you want to support WMI-based hotkeys on PEAQ 2-in-1s. - config NVIDIA_WMI_EC_BACKLIGHT tristate "EC Backlight Driver for Hybrid Graphics Notebook Systems" depends on ACPI_VIDEO diff --git a/drivers/platform/x86/Makefile b/drivers/platform/x86/Makefile index 0d9cc9af6ba7..12407f36d2be 100644 --- a/drivers/platform/x86/Makefile +++ b/drivers/platform/x86/Makefile @@ -12,7 +12,6 @@ obj-$(CONFIG_WMI_BMOF) += wmi-bmof.o obj-$(CONFIG_HUAWEI_WMI) += huawei-wmi.o obj-$(CONFIG_MXM_WMI) += mxm-wmi.o obj-$(CONFIG_NVIDIA_WMI_EC_BACKLIGHT) += nvidia-wmi-ec-backlight.o -obj-$(CONFIG_PEAQ_WMI) += peaq-wmi.o obj-$(CONFIG_XIAOMI_WMI) += xiaomi-wmi.o obj-$(CONFIG_GIGABYTE_WMI) += gigabyte-wmi.o obj-$(CONFIG_YOGABOOK_WMI) += lenovo-yogabook-wmi.o diff --git a/drivers/platform/x86/peaq-wmi.c b/drivers/platform/x86/peaq-wmi.c deleted file mode 100644 index cf9c44c20a82..000000000000 --- a/drivers/platform/x86/peaq-wmi.c +++ /dev/null @@ -1,128 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* - * PEAQ 2-in-1 WMI hotkey driver - * Copyright (C) 2017 Hans de Goede - */ - -#include -#include -#include -#include -#include - -#define PEAQ_DOLBY_BUTTON_GUID "ABBC0F6F-8EA1-11D1-00A0-C90629100000" -#define PEAQ_DOLBY_BUTTON_METHOD_ID 5 -#define PEAQ_POLL_INTERVAL_MS 250 -#define PEAQ_POLL_IGNORE_MS 500 -#define PEAQ_POLL_MAX_MS 1000 - -MODULE_ALIAS("wmi:"PEAQ_DOLBY_BUTTON_GUID); - -static struct input_dev *peaq_poll_dev; - -/* - * The Dolby button (yes really a Dolby button) causes an ACPI variable to get - * set on both press and release. The WMI method checks and clears that flag. - * So for a press + release we will get back One from the WMI method either once - * (if polling after the release) or twice (polling between press and release). - * We ignore events for 0.5s after the first event to avoid reporting 2 presses. - */ -static void peaq_wmi_poll(struct input_dev *input_dev) -{ - static unsigned long last_event_time; - static bool had_events; - union acpi_object obj; - acpi_status status; - u32 dummy = 0; - - struct acpi_buffer input = { sizeof(dummy), &dummy }; - struct acpi_buffer output = { sizeof(obj), &obj }; - - status = wmi_evaluate_method(PEAQ_DOLBY_BUTTON_GUID, 0, - PEAQ_DOLBY_BUTTON_METHOD_ID, - &input, &output); - if (ACPI_FAILURE(status)) - return; - - if (obj.type != ACPI_TYPE_INTEGER) { - dev_err(&input_dev->dev, - "Error WMBC did not return an integer\n"); - return; - } - - if (!obj.integer.value) - return; - - if (had_events && time_before(jiffies, last_event_time + - msecs_to_jiffies(PEAQ_POLL_IGNORE_MS))) - return; - - input_event(input_dev, EV_KEY, KEY_SOUND, 1); - input_sync(input_dev); - input_event(input_dev, EV_KEY, KEY_SOUND, 0); - input_sync(input_dev); - - last_event_time = jiffies; - had_events = true; -} - -/* Some other devices (Shuttle XS35) use the same WMI GUID for other purposes */ -static const struct dmi_system_id peaq_dmi_table[] __initconst = { - { - .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "PEAQ"), - DMI_MATCH(DMI_PRODUCT_NAME, "PEAQ PMM C1010 MD99187"), - }, - }, - {} -}; - -static int __init peaq_wmi_init(void) -{ - int err; - - /* WMI GUID is not unique, also check for a DMI match */ - if (!dmi_check_system(peaq_dmi_table)) - return -ENODEV; - - if (!wmi_has_guid(PEAQ_DOLBY_BUTTON_GUID)) - return -ENODEV; - - peaq_poll_dev = input_allocate_device(); - if (!peaq_poll_dev) - return -ENOMEM; - - peaq_poll_dev->name = "PEAQ WMI hotkeys"; - peaq_poll_dev->phys = "wmi/input0"; - peaq_poll_dev->id.bustype = BUS_HOST; - input_set_capability(peaq_poll_dev, EV_KEY, KEY_SOUND); - - err = input_setup_polling(peaq_poll_dev, peaq_wmi_poll); - if (err) - goto err_out; - - input_set_poll_interval(peaq_poll_dev, PEAQ_POLL_INTERVAL_MS); - input_set_max_poll_interval(peaq_poll_dev, PEAQ_POLL_MAX_MS); - - err = input_register_device(peaq_poll_dev); - if (err) - goto err_out; - - return 0; - -err_out: - input_free_device(peaq_poll_dev); - return err; -} - -static void __exit peaq_wmi_exit(void) -{ - input_unregister_device(peaq_poll_dev); -} - -module_init(peaq_wmi_init); -module_exit(peaq_wmi_exit); - -MODULE_DESCRIPTION("PEAQ 2-in-1 WMI hotkey driver"); -MODULE_AUTHOR("Hans de Goede "); -MODULE_LICENSE("GPL"); diff --git a/drivers/platform/x86/x86-android-tablets/dmi.c b/drivers/platform/x86/x86-android-tablets/dmi.c index 09bf8732b3cf..34af993d5392 100644 --- a/drivers/platform/x86/x86-android-tablets/dmi.c +++ b/drivers/platform/x86/x86-android-tablets/dmi.c @@ -125,6 +125,14 @@ const struct dmi_system_id x86_android_tablet_ids[] __initconst = { }, .driver_data = (void *)&nextbook_ares8_info, }, + { + /* Peaq C1010 */ + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "PEAQ"), + DMI_MATCH(DMI_PRODUCT_NAME, "PEAQ PMM C1010 MD99187"), + }, + .driver_data = (void *)&peaq_c1010_info, + }, { /* Whitelabel (sold as various brands) TM800A550L */ .matches = { diff --git a/drivers/platform/x86/x86-android-tablets/other.c b/drivers/platform/x86/x86-android-tablets/other.c index c1bde42315c4..83cd7e16c84c 100644 --- a/drivers/platform/x86/x86-android-tablets/other.c +++ b/drivers/platform/x86/x86-android-tablets/other.c @@ -380,6 +380,34 @@ const struct x86_dev_info nextbook_ares8_info __initconst = { .invalid_aei_gpiochip = "INT33FC:02", }; +/* + * Peaq C1010 + * This is a standard Windows tablet, but it has a special Dolby button. + * This button has a WMI interface, but that is broken. Instead of trying to + * use the broken WMI interface, instantiate a gpio_keys device for this. + */ +static struct x86_gpio_button peaq_c1010_button = { + .button = { + .code = KEY_SOUND, + .active_low = true, + .desc = "dolby_key", + .type = EV_KEY, + .wakeup = false, + .debounce_interval = 50, + }, + .chip = "INT33FC:00", + .pin = 3, +}; + +const struct x86_dev_info peaq_c1010_info __initconst = { + .gpio_button = &peaq_c1010_button, + /* + * Move the ACPI event handler used by the broken WMI interface out of + * the way. This is the only event handler on INT33FC:00. + */ + .invalid_aei_gpiochip = "INT33FC:00", +}; + /* * Whitelabel (sold as various brands) TM800A550L tablets. * These tablet's DSDT contains a whole bunch of bogus ACPI I2C devices diff --git a/drivers/platform/x86/x86-android-tablets/x86-android-tablets.h b/drivers/platform/x86/x86-android-tablets/x86-android-tablets.h index fb499a2c42d9..f7ee36a30a91 100644 --- a/drivers/platform/x86/x86-android-tablets/x86-android-tablets.h +++ b/drivers/platform/x86/x86-android-tablets/x86-android-tablets.h @@ -100,6 +100,7 @@ extern struct x86_dev_info lenovo_yoga_tab2_830_1050_info; extern const struct x86_dev_info lenovo_yt3_info; extern const struct x86_dev_info medion_lifetab_s10346_info; extern const struct x86_dev_info nextbook_ares8_info; +extern const struct x86_dev_info peaq_c1010_info; extern const struct x86_dev_info whitelabel_tm800a550l_info; extern const struct x86_dev_info xiaomi_mipad2_info; extern const struct dmi_system_id x86_android_tablet_ids[]; -- cgit v1.2.3 From 9b1d2662b8c586eb10cb939c7ae2c25bb36d2b61 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Tue, 21 Feb 2023 22:52:58 +0100 Subject: platform/x86: x86-android-tablets: Add touchscreen support for Lenovo Yoga Tab 3 Pro YT3-X90F Add the necessary info to instantiate the I2C device for the touchscreen on Lenovo Yoga Tab 3 Pro YT3-X90F tablets. Signed-off-by: Hans de Goede Reviewed-by: Andy Shevchenko Link: https://lore.kernel.org/r/20230301092331.7038-12-hdegoede@redhat.com --- drivers/platform/x86/x86-android-tablets/lenovo.c | 46 +++++++++++++++++++++++ 1 file changed, 46 insertions(+) (limited to 'drivers/platform') diff --git a/drivers/platform/x86/x86-android-tablets/lenovo.c b/drivers/platform/x86/x86-android-tablets/lenovo.c index 290de07d8aa0..4ba4b1e8c7d0 100644 --- a/drivers/platform/x86/x86-android-tablets/lenovo.c +++ b/drivers/platform/x86/x86-android-tablets/lenovo.c @@ -12,6 +12,7 @@ #include #include +#include #include #include #include @@ -334,6 +335,17 @@ static const struct software_node lenovo_yt3_bq25892_0_node = { .properties = lenovo_yt3_bq25892_0_props, }; +static const struct property_entry lenovo_yt3_hideep_ts_props[] = { + PROPERTY_ENTRY_U32("touchscreen-size-x", 1600), + PROPERTY_ENTRY_U32("touchscreen-size-y", 2560), + PROPERTY_ENTRY_U32("touchscreen-max-pressure", 255), + { } +}; + +static const struct software_node lenovo_yt3_hideep_ts_node = { + .properties = lenovo_yt3_hideep_ts_props, +}; + static const struct x86_i2c_client_info lenovo_yt3_i2c_clients[] __initconst = { { /* bq27500 fuel-gauge for the flat lipo battery behind the screen */ @@ -369,6 +381,22 @@ static const struct x86_i2c_client_info lenovo_yt3_i2c_clients[] __initconst = { .swnode = &fg_bq25890_1_supply_node, }, .adapter_path = "\\_SB_.PCI0.I2C2", + }, { + /* HiDeep IST520E Touchscreen */ + .board_info = { + .type = "hideep_ts", + .addr = 0x6c, + .dev_name = "hideep_ts", + .swnode = &lenovo_yt3_hideep_ts_node, + }, + .adapter_path = "\\_SB_.PCI0.I2C6", + .irq_data = { + .type = X86_ACPI_IRQ_TYPE_GPIOINT, + .chip = "INT33FF:03", + .index = 77, + .trigger = ACPI_LEVEL_SENSITIVE, + .polarity = ACPI_ACTIVE_LOW, + }, } }; @@ -409,11 +437,29 @@ static int __init lenovo_yt3_init(void) gpiod_set_value(gpiod, 0); + /* Enable the regulators used by the touchscreen */ + intel_soc_pmic_exec_mipi_pmic_seq_element(0x6e, 0x9b, 0x02, 0xff); + intel_soc_pmic_exec_mipi_pmic_seq_element(0x6e, 0xa0, 0x02, 0xff); + return 0; } +static struct gpiod_lookup_table lenovo_yt3_hideep_gpios = { + .dev_id = "i2c-hideep_ts", + .table = { + GPIO_LOOKUP("INT33FF:00", 7, "reset", GPIO_ACTIVE_LOW), + { } + }, +}; + +static struct gpiod_lookup_table * const lenovo_yt3_gpios[] = { + &lenovo_yt3_hideep_gpios, + NULL +}; + const struct x86_dev_info lenovo_yt3_info __initconst = { .i2c_client_info = lenovo_yt3_i2c_clients, .i2c_client_count = ARRAY_SIZE(lenovo_yt3_i2c_clients), + .gpiod_lookup_tables = lenovo_yt3_gpios, .init = lenovo_yt3_init, }; -- cgit v1.2.3 From 607fbac0fccba506b4f9e47994504121db776956 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Wed, 22 Feb 2023 22:42:26 +0100 Subject: platform/x86: x86-android-tablets: Add backlight ctrl for Lenovo Yoga Tab 3 Pro YT3-X90F The YT3 uses an TI LP8557 LED backlight controller, the LP8557's PWM input is connected to a PWM output coming from the LCD panel's controller. The Android kernel has a hack in the i915 driver to write the non-standard DSI reg 0x51 with the desired level to set the duty-cycle of the LCD's PWM. To avoid having to have a similar hack in the mainline kernel program instantiate an i2c-client for the LP8557 with platform-data to have the LP8557 to directly set the level (ignoring its PWM input), this allows backlight brightness control through a backlight device registered by the lp855x_bl driver. Signed-off-by: Hans de Goede Reviewed-by: Andy Shevchenko Link: https://lore.kernel.org/r/20230301092331.7038-13-hdegoede@redhat.com --- drivers/platform/x86/x86-android-tablets/lenovo.c | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) (limited to 'drivers/platform') diff --git a/drivers/platform/x86/x86-android-tablets/lenovo.c b/drivers/platform/x86/x86-android-tablets/lenovo.c index 4ba4b1e8c7d0..e9eae09784ef 100644 --- a/drivers/platform/x86/x86-android-tablets/lenovo.c +++ b/drivers/platform/x86/x86-android-tablets/lenovo.c @@ -346,6 +346,20 @@ static const struct software_node lenovo_yt3_hideep_ts_node = { .properties = lenovo_yt3_hideep_ts_props, }; +/* + * The YT3 uses an TI LP8557 LED backlight controller, the LP8557's PWM input is + * connected to a PWM output coming from the LCD panel's controller. The Android + * kernel has a hack in the i915 driver to write the non-standard DSI reg 0x51 + * with the desired level to set the duty-cycle of the LCD's PWM output. + * + * To avoid having to have a similar hack in the mainline kernel program the + * LP8557 to directly set the level and use the lp855x_bl driver for control. + */ +static struct lp855x_platform_data lenovo_yt3_lp8557_pdata = { + .device_control = 0x86, + .initial_brightness = 128, +}; + static const struct x86_i2c_client_info lenovo_yt3_i2c_clients[] __initconst = { { /* bq27500 fuel-gauge for the flat lipo battery behind the screen */ @@ -397,6 +411,15 @@ static const struct x86_i2c_client_info lenovo_yt3_i2c_clients[] __initconst = { .trigger = ACPI_LEVEL_SENSITIVE, .polarity = ACPI_ACTIVE_LOW, }, + }, { + /* LP8557 Backlight controller */ + .board_info = { + .type = "lp8557", + .addr = 0x2c, + .dev_name = "lp8557", + .platform_data = &lenovo_yt3_lp8557_pdata, + }, + .adapter_path = "\\_SB_.PCI0.I2C1", } }; -- cgit v1.2.3 From dadbc368f4f26f59debe49e1cdf44883e8799fd2 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Sat, 25 Feb 2023 17:11:04 +0100 Subject: platform/x86: x86-android-tablets: Add LID switch support for Yoga Tablet 2 1050/830 series The Yoga Tablet 2 1050/830 series have a HALL sensor for detecting when their (optional) case/cover is closed over the screen. Their Windows counterparts (alsmost the same HW, different BIOS) model this as a LID switch. Add support for reporting this as a LID switch on the Android models too. Signed-off-by: Hans de Goede Reviewed-by: Andy Shevchenko Link: https://lore.kernel.org/r/20230301092331.7038-14-hdegoede@redhat.com --- drivers/platform/x86/x86-android-tablets/lenovo.c | 14 ++++++++++++++ 1 file changed, 14 insertions(+) (limited to 'drivers/platform') diff --git a/drivers/platform/x86/x86-android-tablets/lenovo.c b/drivers/platform/x86/x86-android-tablets/lenovo.c index e9eae09784ef..ec29c4d24451 100644 --- a/drivers/platform/x86/x86-android-tablets/lenovo.c +++ b/drivers/platform/x86/x86-android-tablets/lenovo.c @@ -56,6 +56,19 @@ static const struct software_node lenovo_yoga_tab2_830_1050_bq24190_node = { .properties = lenovo_yoga_tab2_830_1050_bq24190_props, }; +static struct x86_gpio_button lenovo_yoga_tab2_830_1050_lid = { + .button = { + .code = SW_LID, + .active_low = true, + .desc = "lid_sw", + .type = EV_SW, + .wakeup = true, + .debounce_interval = 50, + }, + .chip = "INT33FC:02", + .pin = 26, +}; + /* This gets filled by lenovo_yoga_tab2_830_1050_init() */ static struct rmi_device_platform_data lenovo_yoga_tab2_830_1050_rmi_pdata = { }; @@ -154,6 +167,7 @@ struct x86_dev_info lenovo_yoga_tab2_830_1050_info __initdata = { /* i2c_client_count gets set by lenovo_yoga_tab2_830_1050_init() */ .pdev_info = int3496_pdevs, .pdev_count = 1, + .gpio_button = &lenovo_yoga_tab2_830_1050_lid, .gpiod_lookup_tables = lenovo_yoga_tab2_830_1050_gpios, .bat_swnode = &generic_lipo_hv_4v35_battery_node, .modules = bq24190_modules, -- cgit v1.2.3 From 5f250f8a77c4d30da52ee4184b770e327fbe76e2 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Tue, 28 Feb 2023 09:35:03 +0100 Subject: platform/x86: x86-android-tablets: Lenovo Yoga Book match is for YB1-X91 models only When support for instantiating an i2c-client for the fuel-gauge was added for the Windows based Yoga Book YB1-X91F/L models, the assumption was made that this would apply to the Android based YB1-X90F/L models too. But these have a completely different BIOS with completely different DMI strings. Update the existing YB1-X91 support to reflect that it only applies to the YB1-X91F/L models. Cc: Yauhen Kharuzhy Signed-off-by: Hans de Goede Reviewed-by: Andy Shevchenko Link: https://lore.kernel.org/r/20230301092331.7038-15-hdegoede@redhat.com --- drivers/platform/x86/x86-android-tablets/dmi.c | 8 ++++---- drivers/platform/x86/x86-android-tablets/lenovo.c | 10 +++++----- drivers/platform/x86/x86-android-tablets/x86-android-tablets.h | 2 +- 3 files changed, 10 insertions(+), 10 deletions(-) (limited to 'drivers/platform') diff --git a/drivers/platform/x86/x86-android-tablets/dmi.c b/drivers/platform/x86/x86-android-tablets/dmi.c index 34af993d5392..6ea47af1b480 100644 --- a/drivers/platform/x86/x86-android-tablets/dmi.c +++ b/drivers/platform/x86/x86-android-tablets/dmi.c @@ -77,12 +77,12 @@ const struct dmi_system_id x86_android_tablet_ids[] __initconst = { .driver_data = (void *)&czc_p10t, }, { - /* Lenovo Yoga Book X90F / X91F / X91L */ + /* Lenovo Yoga Book X91F / X91L */ .matches = { - /* Non exact match to match all versions */ - DMI_MATCH(DMI_PRODUCT_NAME, "Lenovo YB1-X9"), + /* Non exact match to match F + L versions */ + DMI_MATCH(DMI_PRODUCT_NAME, "Lenovo YB1-X91"), }, - .driver_data = (void *)&lenovo_yogabook_x9x_info, + .driver_data = (void *)&lenovo_yogabook_x91_info, }, { /* diff --git a/drivers/platform/x86/x86-android-tablets/lenovo.c b/drivers/platform/x86/x86-android-tablets/lenovo.c index ec29c4d24451..0baad9970820 100644 --- a/drivers/platform/x86/x86-android-tablets/lenovo.c +++ b/drivers/platform/x86/x86-android-tablets/lenovo.c @@ -24,8 +24,8 @@ #include "shared-psy-info.h" #include "x86-android-tablets.h" -/* Lenovo Yoga Book X90F / X91F / X91L need manual instantiation of the fg client */ -static const struct x86_i2c_client_info lenovo_yogabook_x9x_i2c_clients[] __initconst = { +/* Lenovo Yoga Book X91F/L Windows tablet needs manual instantiation of the fg client */ +static const struct x86_i2c_client_info lenovo_yogabook_x91_i2c_clients[] __initconst = { { /* BQ27542 fuel-gauge */ .board_info = { @@ -38,9 +38,9 @@ static const struct x86_i2c_client_info lenovo_yogabook_x9x_i2c_clients[] __init }, }; -const struct x86_dev_info lenovo_yogabook_x9x_info __initconst = { - .i2c_client_info = lenovo_yogabook_x9x_i2c_clients, - .i2c_client_count = ARRAY_SIZE(lenovo_yogabook_x9x_i2c_clients), +const struct x86_dev_info lenovo_yogabook_x91_info __initconst = { + .i2c_client_info = lenovo_yogabook_x91_i2c_clients, + .i2c_client_count = ARRAY_SIZE(lenovo_yogabook_x91_i2c_clients), }; /* Lenovo Yoga Tablet 2 1050F/L's Android factory img has everything hardcoded */ diff --git a/drivers/platform/x86/x86-android-tablets/x86-android-tablets.h b/drivers/platform/x86/x86-android-tablets/x86-android-tablets.h index f7ee36a30a91..cf8566b3fd9c 100644 --- a/drivers/platform/x86/x86-android-tablets/x86-android-tablets.h +++ b/drivers/platform/x86/x86-android-tablets/x86-android-tablets.h @@ -94,7 +94,7 @@ extern const struct x86_dev_info asus_me176c_info; extern const struct x86_dev_info asus_tf103c_info; extern const struct x86_dev_info chuwi_hi8_info; extern const struct x86_dev_info czc_p10t; -extern const struct x86_dev_info lenovo_yogabook_x9x_info; +extern const struct x86_dev_info lenovo_yogabook_x91_info; /* Not const as this gets modified by its init callback */ extern struct x86_dev_info lenovo_yoga_tab2_830_1050_info; extern const struct x86_dev_info lenovo_yt3_info; -- cgit v1.2.3 From 96c59f596ebe7d5f5a03f17a16a15e19f22cf666 Mon Sep 17 00:00:00 2001 From: Uwe Kleine-König Date: Thu, 2 Mar 2023 15:47:04 +0100 Subject: platform/x86: acer-wmi: Convert to platform remove callback returning void MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The .remove() callback for a platform driver returns an int which makes many driver authors wrongly assume it's possible to do error handling by returning an error code. However the value returned is (mostly) ignored and this typically results in resource leaks. To improve here there is a quest to make the remove callback return void. In the first step of this quest all drivers are converted to .remove_new() which already returns void. Trivially convert this driver from always returning zero in the remove callback to the void returning variant. Signed-off-by: Uwe Kleine-König Link: https://lore.kernel.org/r/20230302144732.1903781-2-u.kleine-koenig@pengutronix.de Reviewed-by: Hans de Goede Signed-off-by: Hans de Goede --- drivers/platform/x86/acer-wmi.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) (limited to 'drivers/platform') diff --git a/drivers/platform/x86/acer-wmi.c b/drivers/platform/x86/acer-wmi.c index ee67efdd5499..377a0becd1a1 100644 --- a/drivers/platform/x86/acer-wmi.c +++ b/drivers/platform/x86/acer-wmi.c @@ -2258,7 +2258,7 @@ error_mailled: return err; } -static int acer_platform_remove(struct platform_device *device) +static void acer_platform_remove(struct platform_device *device) { if (has_cap(ACER_CAP_MAILLED)) acer_led_exit(); @@ -2266,7 +2266,6 @@ static int acer_platform_remove(struct platform_device *device) acer_backlight_exit(); acer_rfkill_exit(); - return 0; } #ifdef CONFIG_PM_SLEEP @@ -2334,7 +2333,7 @@ static struct platform_driver acer_platform_driver = { .pm = &acer_pm, }, .probe = acer_platform_probe, - .remove = acer_platform_remove, + .remove_new = acer_platform_remove, .shutdown = acer_platform_shutdown, }; -- cgit v1.2.3 From db0664d1afe0600b07c1754fcc6bd300d8482071 Mon Sep 17 00:00:00 2001 From: Uwe Kleine-König Date: Thu, 2 Mar 2023 15:47:05 +0100 Subject: platform/x86: adv_swbutton: Convert to platform remove callback returning void MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The .remove() callback for a platform driver returns an int which makes many driver authors wrongly assume it's possible to do error handling by returning an error code. However the value returned is (mostly) ignored and this typically results in resource leaks. To improve here there is a quest to make the remove callback return void. In the first step of this quest all drivers are converted to .remove_new() which already returns void. Trivially convert this driver from always returning zero in the remove callback to the void returning variant. Signed-off-by: Uwe Kleine-König Link: https://lore.kernel.org/r/20230302144732.1903781-3-u.kleine-koenig@pengutronix.de Reviewed-by: Hans de Goede Signed-off-by: Hans de Goede --- drivers/platform/x86/adv_swbutton.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) (limited to 'drivers/platform') diff --git a/drivers/platform/x86/adv_swbutton.c b/drivers/platform/x86/adv_swbutton.c index 38693b735c87..6b23ba78e028 100644 --- a/drivers/platform/x86/adv_swbutton.c +++ b/drivers/platform/x86/adv_swbutton.c @@ -90,14 +90,12 @@ static int adv_swbutton_probe(struct platform_device *device) return 0; } -static int adv_swbutton_remove(struct platform_device *device) +static void adv_swbutton_remove(struct platform_device *device) { acpi_handle handle = ACPI_HANDLE(&device->dev); acpi_remove_notify_handler(handle, ACPI_DEVICE_NOTIFY, adv_swbutton_notify); - - return 0; } static const struct acpi_device_id button_device_ids[] = { @@ -112,7 +110,7 @@ static struct platform_driver adv_swbutton_driver = { .acpi_match_table = button_device_ids, }, .probe = adv_swbutton_probe, - .remove = adv_swbutton_remove, + .remove_new = adv_swbutton_remove, }; module_platform_driver(adv_swbutton_driver); -- cgit v1.2.3 From 5454b36274fd63e40935975fbd13f4abbb5c5270 Mon Sep 17 00:00:00 2001 From: Uwe Kleine-König Date: Thu, 2 Mar 2023 15:47:06 +0100 Subject: platform/x86: amd: hsmp: Convert to platform remove callback returning void MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The .remove() callback for a platform driver returns an int which makes many driver authors wrongly assume it's possible to do error handling by returning an error code. However the value returned is (mostly) ignored and this typically results in resource leaks. To improve here there is a quest to make the remove callback return void. In the first step of this quest all drivers are converted to .remove_new() which already returns void. Trivially convert this driver from always returning zero in the remove callback to the void returning variant. Signed-off-by: Uwe Kleine-König Link: https://lore.kernel.org/r/20230302144732.1903781-4-u.kleine-koenig@pengutronix.de Reviewed-by: Hans de Goede Signed-off-by: Hans de Goede --- drivers/platform/x86/amd/hsmp.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) (limited to 'drivers/platform') diff --git a/drivers/platform/x86/amd/hsmp.c b/drivers/platform/x86/amd/hsmp.c index 521c6a229362..31382ef52efb 100644 --- a/drivers/platform/x86/amd/hsmp.c +++ b/drivers/platform/x86/amd/hsmp.c @@ -340,16 +340,14 @@ static int hsmp_pltdrv_probe(struct platform_device *pdev) return misc_register(&hsmp_device); } -static int hsmp_pltdrv_remove(struct platform_device *pdev) +static void hsmp_pltdrv_remove(struct platform_device *pdev) { misc_deregister(&hsmp_device); - - return 0; } static struct platform_driver amd_hsmp_driver = { .probe = hsmp_pltdrv_probe, - .remove = hsmp_pltdrv_remove, + .remove_new = hsmp_pltdrv_remove, .driver = { .name = DRIVER_NAME, }, -- cgit v1.2.3 From 3359d99e7a4c1258804ba966ab62f6bc0b45ba78 Mon Sep 17 00:00:00 2001 From: Uwe Kleine-König Date: Thu, 2 Mar 2023 15:47:07 +0100 Subject: platform/x86: amd: pmc: Convert to platform remove callback returning void MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The .remove() callback for a platform driver returns an int which makes many driver authors wrongly assume it's possible to do error handling by returning an error code. However the value returned is (mostly) ignored and this typically results in resource leaks. To improve here there is a quest to make the remove callback return void. In the first step of this quest all drivers are converted to .remove_new() which already returns void. Trivially convert this driver from always returning zero in the remove callback to the void returning variant. Signed-off-by: Uwe Kleine-König Link: https://lore.kernel.org/r/20230302144732.1903781-5-u.kleine-koenig@pengutronix.de Reviewed-by: Hans de Goede Signed-off-by: Hans de Goede --- drivers/platform/x86/amd/pmc.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) (limited to 'drivers/platform') diff --git a/drivers/platform/x86/amd/pmc.c b/drivers/platform/x86/amd/pmc.c index 2edaae04a691..2761e9b76e95 100644 --- a/drivers/platform/x86/amd/pmc.c +++ b/drivers/platform/x86/amd/pmc.c @@ -1022,7 +1022,7 @@ err_pci_dev_put: return err; } -static int amd_pmc_remove(struct platform_device *pdev) +static void amd_pmc_remove(struct platform_device *pdev) { struct amd_pmc_dev *dev = platform_get_drvdata(pdev); @@ -1031,7 +1031,6 @@ static int amd_pmc_remove(struct platform_device *pdev) amd_pmc_dbgfs_unregister(dev); pci_dev_put(dev->rdev); mutex_destroy(&dev->lock); - return 0; } static const struct acpi_device_id amd_pmc_acpi_ids[] = { @@ -1054,7 +1053,7 @@ static struct platform_driver amd_pmc_driver = { .pm = pm_sleep_ptr(&amd_pmc_pm), }, .probe = amd_pmc_probe, - .remove = amd_pmc_remove, + .remove_new = amd_pmc_remove, }; module_platform_driver(amd_pmc_driver); -- cgit v1.2.3 From b444276b12e7da2069ea111116bccc21d864484e Mon Sep 17 00:00:00 2001 From: Uwe Kleine-König Date: Thu, 2 Mar 2023 15:47:08 +0100 Subject: platform/x86: amd: pmf: core: Convert to platform remove callback returning void MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The .remove() callback for a platform driver returns an int which makes many driver authors wrongly assume it's possible to do error handling by returning an error code. However the value returned is (mostly) ignored and this typically results in resource leaks. To improve here there is a quest to make the remove callback return void. In the first step of this quest all drivers are converted to .remove_new() which already returns void. Trivially convert this driver from always returning zero in the remove callback to the void returning variant. Signed-off-by: Uwe Kleine-König Link: https://lore.kernel.org/r/20230302144732.1903781-6-u.kleine-koenig@pengutronix.de Reviewed-by: Hans de Goede Signed-off-by: Hans de Goede --- drivers/platform/x86/amd/pmf/core.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) (limited to 'drivers/platform') diff --git a/drivers/platform/x86/amd/pmf/core.c b/drivers/platform/x86/amd/pmf/core.c index da23639071d7..800ca2c998d3 100644 --- a/drivers/platform/x86/amd/pmf/core.c +++ b/drivers/platform/x86/amd/pmf/core.c @@ -402,7 +402,7 @@ static int amd_pmf_probe(struct platform_device *pdev) return 0; } -static int amd_pmf_remove(struct platform_device *pdev) +static void amd_pmf_remove(struct platform_device *pdev) { struct amd_pmf_dev *dev = platform_get_drvdata(pdev); @@ -413,7 +413,6 @@ static int amd_pmf_remove(struct platform_device *pdev) mutex_destroy(&dev->lock); mutex_destroy(&dev->update_mutex); kfree(dev->buf); - return 0; } static const struct attribute_group *amd_pmf_driver_groups[] = { @@ -428,7 +427,7 @@ static struct platform_driver amd_pmf_driver = { .dev_groups = amd_pmf_driver_groups, }, .probe = amd_pmf_probe, - .remove = amd_pmf_remove, + .remove_new = amd_pmf_remove, }; module_platform_driver(amd_pmf_driver); -- cgit v1.2.3 From 03b5d07a9e2fcb56735a66f0fec109b47f8e82e9 Mon Sep 17 00:00:00 2001 From: Uwe Kleine-König Date: Thu, 2 Mar 2023 15:47:09 +0100 Subject: platform/x86: amilo-rfkill: Convert to platform remove callback returning void MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The .remove() callback for a platform driver returns an int which makes many driver authors wrongly assume it's possible to do error handling by returning an error code. However the value returned is (mostly) ignored and this typically results in resource leaks. To improve here there is a quest to make the remove callback return void. In the first step of this quest all drivers are converted to .remove_new() which already returns void. Trivially convert this driver from always returning zero in the remove callback to the void returning variant. Signed-off-by: Uwe Kleine-König Link: https://lore.kernel.org/r/20230302144732.1903781-7-u.kleine-koenig@pengutronix.de Reviewed-by: Hans de Goede Signed-off-by: Hans de Goede --- drivers/platform/x86/amilo-rfkill.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) (limited to 'drivers/platform') diff --git a/drivers/platform/x86/amilo-rfkill.c b/drivers/platform/x86/amilo-rfkill.c index 3e313c4d538d..efcf909786a5 100644 --- a/drivers/platform/x86/amilo-rfkill.c +++ b/drivers/platform/x86/amilo-rfkill.c @@ -124,11 +124,10 @@ fail: return rc; } -static int amilo_rfkill_remove(struct platform_device *device) +static void amilo_rfkill_remove(struct platform_device *device) { rfkill_unregister(amilo_rfkill_dev); rfkill_destroy(amilo_rfkill_dev); - return 0; } static struct platform_driver amilo_rfkill_driver = { @@ -136,7 +135,7 @@ static struct platform_driver amilo_rfkill_driver = { .name = KBUILD_MODNAME, }, .probe = amilo_rfkill_probe, - .remove = amilo_rfkill_remove, + .remove_new = amilo_rfkill_remove, }; static int __init amilo_rfkill_init(void) -- cgit v1.2.3 From f4278cc8772fad93f292e3ce0722c57fc1eea6eb Mon Sep 17 00:00:00 2001 From: Uwe Kleine-König Date: Thu, 2 Mar 2023 15:47:10 +0100 Subject: platform/x86: barco-p50-gpio: Convert to platform remove callback returning void MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The .remove() callback for a platform driver returns an int which makes many driver authors wrongly assume it's possible to do error handling by returning an error code. However the value returned is (mostly) ignored and this typically results in resource leaks. To improve here there is a quest to make the remove callback return void. In the first step of this quest all drivers are converted to .remove_new() which already returns void. Trivially convert this driver from always returning zero in the remove callback to the void returning variant. Signed-off-by: Uwe Kleine-König Link: https://lore.kernel.org/r/20230302144732.1903781-8-u.kleine-koenig@pengutronix.de Reviewed-by: Hans de Goede Signed-off-by: Hans de Goede --- drivers/platform/x86/barco-p50-gpio.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) (limited to 'drivers/platform') diff --git a/drivers/platform/x86/barco-p50-gpio.c b/drivers/platform/x86/barco-p50-gpio.c index 8dd672339485..af566f712057 100644 --- a/drivers/platform/x86/barco-p50-gpio.c +++ b/drivers/platform/x86/barco-p50-gpio.c @@ -370,7 +370,7 @@ err_leds: return ret; } -static int p50_gpio_remove(struct platform_device *pdev) +static void p50_gpio_remove(struct platform_device *pdev) { struct p50_gpio *p50 = platform_get_drvdata(pdev); @@ -378,8 +378,6 @@ static int p50_gpio_remove(struct platform_device *pdev) platform_device_unregister(p50->leds_pdev); gpiod_remove_lookup_table(&p50_gpio_led_table); - - return 0; } static struct platform_driver p50_gpio_driver = { @@ -387,7 +385,7 @@ static struct platform_driver p50_gpio_driver = { .name = DRIVER_NAME, }, .probe = p50_gpio_probe, - .remove = p50_gpio_remove, + .remove_new = p50_gpio_remove, }; /* Board setup */ -- cgit v1.2.3 From d1bda904933417209e0b8750d4fd3cf8b99fca1f Mon Sep 17 00:00:00 2001 From: Uwe Kleine-König Date: Thu, 2 Mar 2023 15:47:11 +0100 Subject: platform/x86: compal-laptop: Convert to platform remove callback returning void MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The .remove() callback for a platform driver returns an int which makes many driver authors wrongly assume it's possible to do error handling by returning an error code. However the value returned is (mostly) ignored and this typically results in resource leaks. To improve here there is a quest to make the remove callback return void. In the first step of this quest all drivers are converted to .remove_new() which already returns void. Trivially convert this driver from always returning zero in the remove callback to the void returning variant. Signed-off-by: Uwe Kleine-König Link: https://lore.kernel.org/r/20230302144732.1903781-9-u.kleine-koenig@pengutronix.de Reviewed-by: Hans de Goede Signed-off-by: Hans de Goede --- drivers/platform/x86/compal-laptop.c | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) (limited to 'drivers/platform') diff --git a/drivers/platform/x86/compal-laptop.c b/drivers/platform/x86/compal-laptop.c index e10d2f64dfad..61c745490d71 100644 --- a/drivers/platform/x86/compal-laptop.c +++ b/drivers/platform/x86/compal-laptop.c @@ -1003,12 +1003,12 @@ remove: return err; } -static int compal_remove(struct platform_device *pdev) +static void compal_remove(struct platform_device *pdev) { struct compal_data *data; if (!extra_features) - return 0; + return; pr_info("Unloading: resetting fan control to motherboard\n"); pwm_disable_control(); @@ -1017,8 +1017,6 @@ static int compal_remove(struct platform_device *pdev) power_supply_unregister(data->psy); sysfs_remove_group(&pdev->dev.kobj, &compal_platform_attr_group); - - return 0; } static struct platform_driver compal_driver = { @@ -1026,7 +1024,7 @@ static struct platform_driver compal_driver = { .name = DRIVER_NAME, }, .probe = compal_probe, - .remove = compal_remove, + .remove_new = compal_remove, }; static int __init compal_init(void) -- cgit v1.2.3 From 467daaf09496b96c8a8744615d481daeddd089c7 Mon Sep 17 00:00:00 2001 From: Uwe Kleine-König Date: Thu, 2 Mar 2023 15:47:12 +0100 Subject: platform/x86: dell: dcdbas: Convert to platform remove callback returning void MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The .remove() callback for a platform driver returns an int which makes many driver authors wrongly assume it's possible to do error handling by returning an error code. However the value returned is (mostly) ignored and this typically results in resource leaks. To improve here there is a quest to make the remove callback return void. In the first step of this quest all drivers are converted to .remove_new() which already returns void. Trivially convert this driver from always returning zero in the remove callback to the void returning variant. Signed-off-by: Uwe Kleine-König Link: https://lore.kernel.org/r/20230302144732.1903781-10-u.kleine-koenig@pengutronix.de Reviewed-by: Hans de Goede Signed-off-by: Hans de Goede --- drivers/platform/x86/dell/dcdbas.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) (limited to 'drivers/platform') diff --git a/drivers/platform/x86/dell/dcdbas.c b/drivers/platform/x86/dell/dcdbas.c index 0ecb7b164750..76787369d7fa 100644 --- a/drivers/platform/x86/dell/dcdbas.c +++ b/drivers/platform/x86/dell/dcdbas.c @@ -698,12 +698,10 @@ static int dcdbas_probe(struct platform_device *dev) return 0; } -static int dcdbas_remove(struct platform_device *dev) +static void dcdbas_remove(struct platform_device *dev) { unregister_reboot_notifier(&dcdbas_reboot_nb); sysfs_remove_group(&dev->dev.kobj, &dcdbas_attr_group); - - return 0; } static struct platform_driver dcdbas_driver = { @@ -711,7 +709,7 @@ static struct platform_driver dcdbas_driver = { .name = DRIVER_NAME, }, .probe = dcdbas_probe, - .remove = dcdbas_remove, + .remove_new = dcdbas_remove, }; static const struct platform_device_info dcdbas_dev_info __initconst = { -- cgit v1.2.3 From bb9d621f44069de18f102f57998277fdceb1133d Mon Sep 17 00:00:00 2001 From: Uwe Kleine-König Date: Thu, 2 Mar 2023 15:47:13 +0100 Subject: platform/x86: dell: dell-smo8800: Convert to platform remove callback returning void MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The .remove() callback for a platform driver returns an int which makes many driver authors wrongly assume it's possible to do error handling by returning an error code. However the value returned is (mostly) ignored and this typically results in resource leaks. To improve here there is a quest to make the remove callback return void. In the first step of this quest all drivers are converted to .remove_new() which already returns void. Trivially convert this driver from always returning zero in the remove callback to the void returning variant. Signed-off-by: Uwe Kleine-König Acked-by: Pali Rohár Link: https://lore.kernel.org/r/20230302144732.1903781-11-u.kleine-koenig@pengutronix.de Reviewed-by: Hans de Goede Signed-off-by: Hans de Goede --- drivers/platform/x86/dell/dell-smo8800.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) (limited to 'drivers/platform') diff --git a/drivers/platform/x86/dell/dell-smo8800.c b/drivers/platform/x86/dell/dell-smo8800.c index 8d6b7a83cf24..f7ec17c56833 100644 --- a/drivers/platform/x86/dell/dell-smo8800.c +++ b/drivers/platform/x86/dell/dell-smo8800.c @@ -154,14 +154,13 @@ error: return err; } -static int smo8800_remove(struct platform_device *device) +static void smo8800_remove(struct platform_device *device) { struct smo8800_device *smo8800 = platform_get_drvdata(device); free_irq(smo8800->irq, smo8800); misc_deregister(&smo8800->miscdev); dev_dbg(&device->dev, "device /dev/freefall unregistered\n"); - return 0; } /* NOTE: Keep this list in sync with drivers/i2c/busses/i2c-i801.c */ @@ -180,7 +179,7 @@ MODULE_DEVICE_TABLE(acpi, smo8800_ids); static struct platform_driver smo8800_driver = { .probe = smo8800_probe, - .remove = smo8800_remove, + .remove_new = smo8800_remove, .driver = { .name = DRIVER_NAME, .acpi_match_table = smo8800_ids, -- cgit v1.2.3 From 3b5b3e9b00a626f5defee82241a2ab64d93f38a1 Mon Sep 17 00:00:00 2001 From: Uwe Kleine-König Date: Thu, 2 Mar 2023 15:47:14 +0100 Subject: platform/x86: hp: hp_accel: Convert to platform remove callback returning void MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The .remove() callback for a platform driver returns an int which makes many driver authors wrongly assume it's possible to do error handling by returning an error code. However the value returned is (mostly) ignored and this typically results in resource leaks. To improve here there is a quest to make the remove callback return void. In the first step of this quest all drivers are converted to .remove_new() which already returns void. Trivially convert this driver from always returning zero in the remove callback to the void returning variant. Signed-off-by: Uwe Kleine-König Link: https://lore.kernel.org/r/20230302144732.1903781-12-u.kleine-koenig@pengutronix.de Reviewed-by: Hans de Goede Signed-off-by: Hans de Goede --- drivers/platform/x86/hp/hp_accel.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) (limited to 'drivers/platform') diff --git a/drivers/platform/x86/hp/hp_accel.c b/drivers/platform/x86/hp/hp_accel.c index 6477591747cf..52535576772a 100644 --- a/drivers/platform/x86/hp/hp_accel.c +++ b/drivers/platform/x86/hp/hp_accel.c @@ -342,7 +342,7 @@ static int lis3lv02d_probe(struct platform_device *device) return ret; } -static int lis3lv02d_remove(struct platform_device *device) +static void lis3lv02d_remove(struct platform_device *device) { i8042_remove_filter(hp_accel_i8042_filter); lis3lv02d_joystick_disable(&lis3_dev); @@ -352,7 +352,6 @@ static int lis3lv02d_remove(struct platform_device *device) flush_work(&hpled_led.work); lis3lv02d_remove_fs(&lis3_dev); - return 0; } static int __maybe_unused lis3lv02d_suspend(struct device *dev) @@ -373,7 +372,7 @@ static SIMPLE_DEV_PM_OPS(hp_accel_pm, lis3lv02d_suspend, lis3lv02d_resume); /* For the HP MDPS aka 3D Driveguard */ static struct platform_driver lis3lv02d_driver = { .probe = lis3lv02d_probe, - .remove = lis3lv02d_remove, + .remove_new = lis3lv02d_remove, .driver = { .name = "hp_accel", .pm = &hp_accel_pm, -- cgit v1.2.3 From fbc0e1e6a700146d5246df5021e497c27b121aaf Mon Sep 17 00:00:00 2001 From: Uwe Kleine-König Date: Thu, 2 Mar 2023 15:47:15 +0100 Subject: platform/x86: hp: tc1100-wmi: Convert to platform remove callback returning void MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The .remove() callback for a platform driver returns an int which makes many driver authors wrongly assume it's possible to do error handling by returning an error code. However the value returned is (mostly) ignored and this typically results in resource leaks. To improve here there is a quest to make the remove callback return void. In the first step of this quest all drivers are converted to .remove_new() which already returns void. Trivially convert this driver from always returning zero in the remove callback to the void returning variant. Signed-off-by: Uwe Kleine-König Link: https://lore.kernel.org/r/20230302144732.1903781-13-u.kleine-koenig@pengutronix.de Reviewed-by: Hans de Goede Signed-off-by: Hans de Goede --- drivers/platform/x86/hp/tc1100-wmi.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) (limited to 'drivers/platform') diff --git a/drivers/platform/x86/hp/tc1100-wmi.c b/drivers/platform/x86/hp/tc1100-wmi.c index ded26213c420..5298b0f6804f 100644 --- a/drivers/platform/x86/hp/tc1100-wmi.c +++ b/drivers/platform/x86/hp/tc1100-wmi.c @@ -170,11 +170,9 @@ static int __init tc1100_probe(struct platform_device *device) } -static int tc1100_remove(struct platform_device *device) +static void tc1100_remove(struct platform_device *device) { sysfs_remove_group(&device->dev.kobj, &tc1100_attribute_group); - - return 0; } #ifdef CONFIG_PM @@ -223,7 +221,7 @@ static struct platform_driver tc1100_driver = { .pm = &tc1100_pm_ops, #endif }, - .remove = tc1100_remove, + .remove_new = tc1100_remove, }; static int __init tc1100_init(void) -- cgit v1.2.3 From 7836f70a03a0e9384632a8300b6625f4e164a99c Mon Sep 17 00:00:00 2001 From: Uwe Kleine-König Date: Thu, 2 Mar 2023 15:47:16 +0100 Subject: platform/x86: huawei-wmi: Convert to platform remove callback returning void MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The .remove() callback for a platform driver returns an int which makes many driver authors wrongly assume it's possible to do error handling by returning an error code. However the value returned is (mostly) ignored and this typically results in resource leaks. To improve here there is a quest to make the remove callback return void. In the first step of this quest all drivers are converted to .remove_new() which already returns void. Trivially convert this driver from always returning zero in the remove callback to the void returning variant. Signed-off-by: Uwe Kleine-König Link: https://lore.kernel.org/r/20230302144732.1903781-14-u.kleine-koenig@pengutronix.de Reviewed-by: Hans de Goede Signed-off-by: Hans de Goede --- drivers/platform/x86/huawei-wmi.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) (limited to 'drivers/platform') diff --git a/drivers/platform/x86/huawei-wmi.c b/drivers/platform/x86/huawei-wmi.c index 2df1b2d5e3ea..70e5c4c0574d 100644 --- a/drivers/platform/x86/huawei-wmi.c +++ b/drivers/platform/x86/huawei-wmi.c @@ -830,7 +830,7 @@ static int huawei_wmi_probe(struct platform_device *pdev) return 0; } -static int huawei_wmi_remove(struct platform_device *pdev) +static void huawei_wmi_remove(struct platform_device *pdev) { const struct wmi_device_id *guid = huawei_wmi_events_id_table; @@ -846,8 +846,6 @@ static int huawei_wmi_remove(struct platform_device *pdev) huawei_wmi_battery_exit(&pdev->dev); huawei_wmi_fn_lock_exit(&pdev->dev); } - - return 0; } static struct platform_driver huawei_wmi_driver = { @@ -855,7 +853,7 @@ static struct platform_driver huawei_wmi_driver = { .name = "huawei-wmi", }, .probe = huawei_wmi_probe, - .remove = huawei_wmi_remove, + .remove_new = huawei_wmi_remove, }; static __init int huawei_wmi_init(void) -- cgit v1.2.3 From d21c474c2312037a845805350b9fa614ab5dfab6 Mon Sep 17 00:00:00 2001 From: Uwe Kleine-König Date: Thu, 2 Mar 2023 15:47:17 +0100 Subject: platform/x86: ideapad-laptop: Convert to platform remove callback returning void MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The .remove() callback for a platform driver returns an int which makes many driver authors wrongly assume it's possible to do error handling by returning an error code. However the value returned is (mostly) ignored and this typically results in resource leaks. To improve here there is a quest to make the remove callback return void. In the first step of this quest all drivers are converted to .remove_new() which already returns void. Trivially convert this driver from always returning zero in the remove callback to the void returning variant. Signed-off-by: Uwe Kleine-König Link: https://lore.kernel.org/r/20230302144732.1903781-15-u.kleine-koenig@pengutronix.de Reviewed-by: Hans de Goede Signed-off-by: Hans de Goede --- drivers/platform/x86/ideapad-laptop.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) (limited to 'drivers/platform') diff --git a/drivers/platform/x86/ideapad-laptop.c b/drivers/platform/x86/ideapad-laptop.c index 0eb5bfdd823a..b5ef3452da1f 100644 --- a/drivers/platform/x86/ideapad-laptop.c +++ b/drivers/platform/x86/ideapad-laptop.c @@ -1921,7 +1921,7 @@ input_failed: return err; } -static int ideapad_acpi_remove(struct platform_device *pdev) +static void ideapad_acpi_remove(struct platform_device *pdev) { struct ideapad_private *priv = dev_get_drvdata(&pdev->dev); int i; @@ -1942,8 +1942,6 @@ static int ideapad_acpi_remove(struct platform_device *pdev) ideapad_input_exit(priv); ideapad_debugfs_exit(priv); ideapad_sysfs_exit(priv); - - return 0; } #ifdef CONFIG_PM_SLEEP @@ -1970,7 +1968,7 @@ MODULE_DEVICE_TABLE(acpi, ideapad_device_ids); static struct platform_driver ideapad_acpi_driver = { .probe = ideapad_acpi_add, - .remove = ideapad_acpi_remove, + .remove_new = ideapad_acpi_remove, .driver = { .name = "ideapad_acpi", .pm = &ideapad_pm, -- cgit v1.2.3 From f9f23df7b640ff97b5a6f489f4ebfa9692d1e08e Mon Sep 17 00:00:00 2001 From: Uwe Kleine-König Date: Thu, 2 Mar 2023 15:47:18 +0100 Subject: platform/x86: intel: bxtwc_tmu: Convert to platform remove callback returning void MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The .remove() callback for a platform driver returns an int which makes many driver authors wrongly assume it's possible to do error handling by returning an error code. However the value returned is (mostly) ignored and this typically results in resource leaks. To improve here there is a quest to make the remove callback return void. In the first step of this quest all drivers are converted to .remove_new() which already returns void. Trivially convert this driver from always returning zero in the remove callback to the void returning variant. Signed-off-by: Uwe Kleine-König Link: https://lore.kernel.org/r/20230302144732.1903781-16-u.kleine-koenig@pengutronix.de Reviewed-by: Hans de Goede Signed-off-by: Hans de Goede --- drivers/platform/x86/intel/bxtwc_tmu.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) (limited to 'drivers/platform') diff --git a/drivers/platform/x86/intel/bxtwc_tmu.c b/drivers/platform/x86/intel/bxtwc_tmu.c index 7ccf583649e6..d0e2a3c293b0 100644 --- a/drivers/platform/x86/intel/bxtwc_tmu.c +++ b/drivers/platform/x86/intel/bxtwc_tmu.c @@ -89,7 +89,7 @@ static int bxt_wcove_tmu_probe(struct platform_device *pdev) return 0; } -static int bxt_wcove_tmu_remove(struct platform_device *pdev) +static void bxt_wcove_tmu_remove(struct platform_device *pdev) { struct wcove_tmu *wctmu = platform_get_drvdata(pdev); unsigned int val; @@ -101,7 +101,6 @@ static int bxt_wcove_tmu_remove(struct platform_device *pdev) regmap_read(wctmu->regmap, BXTWC_MTMUIRQ_REG, &val); regmap_write(wctmu->regmap, BXTWC_MTMUIRQ_REG, val | BXTWC_TMU_ALRM_MASK); - return 0; } #ifdef CONFIG_PM_SLEEP @@ -132,7 +131,7 @@ MODULE_DEVICE_TABLE(platform, bxt_wcove_tmu_id_table); static struct platform_driver bxt_wcove_tmu_driver = { .probe = bxt_wcove_tmu_probe, - .remove = bxt_wcove_tmu_remove, + .remove_new = bxt_wcove_tmu_remove, .driver = { .name = "bxt_wcove_tmu", .pm = &bxtwc_tmu_pm_ops, -- cgit v1.2.3 From bca92922e2a7800adf9d965e409f7ecb74ce9494 Mon Sep 17 00:00:00 2001 From: Uwe Kleine-König Date: Thu, 2 Mar 2023 15:47:19 +0100 Subject: platform/x86: intel: chtdc_ti_pwrbtn: Convert to platform remove callback returning void MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The .remove() callback for a platform driver returns an int which makes many driver authors wrongly assume it's possible to do error handling by returning an error code. However the value returned is (mostly) ignored and this typically results in resource leaks. To improve here there is a quest to make the remove callback return void. In the first step of this quest all drivers are converted to .remove_new() which already returns void. Trivially convert this driver from always returning zero in the remove callback to the void returning variant. Signed-off-by: Uwe Kleine-König Link: https://lore.kernel.org/r/20230302144732.1903781-17-u.kleine-koenig@pengutronix.de Reviewed-by: Hans de Goede Signed-off-by: Hans de Goede --- drivers/platform/x86/intel/chtdc_ti_pwrbtn.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) (limited to 'drivers/platform') diff --git a/drivers/platform/x86/intel/chtdc_ti_pwrbtn.c b/drivers/platform/x86/intel/chtdc_ti_pwrbtn.c index 9606a994af22..615f8d1a0c68 100644 --- a/drivers/platform/x86/intel/chtdc_ti_pwrbtn.c +++ b/drivers/platform/x86/intel/chtdc_ti_pwrbtn.c @@ -67,11 +67,10 @@ static int chtdc_ti_pwrbtn_probe(struct platform_device *pdev) return 0; } -static int chtdc_ti_pwrbtn_remove(struct platform_device *pdev) +static void chtdc_ti_pwrbtn_remove(struct platform_device *pdev) { dev_pm_clear_wake_irq(&pdev->dev); device_init_wakeup(&pdev->dev, false); - return 0; } static const struct platform_device_id chtdc_ti_pwrbtn_id_table[] = { @@ -85,7 +84,7 @@ static struct platform_driver chtdc_ti_pwrbtn_driver = { .name = KBUILD_MODNAME, }, .probe = chtdc_ti_pwrbtn_probe, - .remove = chtdc_ti_pwrbtn_remove, + .remove_new = chtdc_ti_pwrbtn_remove, .id_table = chtdc_ti_pwrbtn_id_table, }; module_platform_driver(chtdc_ti_pwrbtn_driver); -- cgit v1.2.3 From fba53497e3c0572a2d621bbd98615c6e07a9e518 Mon Sep 17 00:00:00 2001 From: Uwe Kleine-König Date: Thu, 2 Mar 2023 15:47:20 +0100 Subject: platform/x86: intel: chtwc_int33fe: Convert to platform remove callback returning void MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The .remove() callback for a platform driver returns an int which makes many driver authors wrongly assume it's possible to do error handling by returning an error code. However the value returned is (mostly) ignored and this typically results in resource leaks. To improve here there is a quest to make the remove callback return void. In the first step of this quest all drivers are converted to .remove_new() which already returns void. Trivially convert this driver from always returning zero in the remove callback to the void returning variant. Signed-off-by: Uwe Kleine-König Link: https://lore.kernel.org/r/20230302144732.1903781-18-u.kleine-koenig@pengutronix.de Reviewed-by: Hans de Goede Signed-off-by: Hans de Goede --- drivers/platform/x86/intel/chtwc_int33fe.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) (limited to 'drivers/platform') diff --git a/drivers/platform/x86/intel/chtwc_int33fe.c b/drivers/platform/x86/intel/chtwc_int33fe.c index 2c9a7d52be07..848baecc1bb0 100644 --- a/drivers/platform/x86/intel/chtwc_int33fe.c +++ b/drivers/platform/x86/intel/chtwc_int33fe.c @@ -405,7 +405,7 @@ out_remove_nodes: return ret; } -static int cht_int33fe_typec_remove(struct platform_device *pdev) +static void cht_int33fe_typec_remove(struct platform_device *pdev) { struct cht_int33fe_data *data = platform_get_drvdata(pdev); @@ -414,8 +414,6 @@ static int cht_int33fe_typec_remove(struct platform_device *pdev) i2c_unregister_device(data->battery_fg); cht_int33fe_remove_nodes(data); - - return 0; } static const struct acpi_device_id cht_int33fe_acpi_ids[] = { @@ -429,7 +427,7 @@ static struct platform_driver cht_int33fe_typec_driver = { .acpi_match_table = ACPI_PTR(cht_int33fe_acpi_ids), }, .probe = cht_int33fe_typec_probe, - .remove = cht_int33fe_typec_remove, + .remove_new = cht_int33fe_typec_remove, }; module_platform_driver(cht_int33fe_typec_driver); -- cgit v1.2.3 From 63b890167e7f697a5e22d83d59a2717535ffb50b Mon Sep 17 00:00:00 2001 From: Uwe Kleine-König Date: Thu, 2 Mar 2023 15:47:21 +0100 Subject: platform/x86: intel: hid: Convert to platform remove callback returning void MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The .remove() callback for a platform driver returns an int which makes many driver authors wrongly assume it's possible to do error handling by returning an error code. However the value returned is (mostly) ignored and this typically results in resource leaks. To improve here there is a quest to make the remove callback return void. In the first step of this quest all drivers are converted to .remove_new() which already returns void. Trivially convert this driver from always returning zero in the remove callback to the void returning variant. Signed-off-by: Uwe Kleine-König Link: https://lore.kernel.org/r/20230302144732.1903781-19-u.kleine-koenig@pengutronix.de Reviewed-by: Hans de Goede Signed-off-by: Hans de Goede --- drivers/platform/x86/intel/hid.c | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) (limited to 'drivers/platform') diff --git a/drivers/platform/x86/intel/hid.c b/drivers/platform/x86/intel/hid.c index b6c06b37862e..5632bd3c534a 100644 --- a/drivers/platform/x86/intel/hid.c +++ b/drivers/platform/x86/intel/hid.c @@ -720,7 +720,7 @@ err_remove_notify: return err; } -static int intel_hid_remove(struct platform_device *device) +static void intel_hid_remove(struct platform_device *device) { acpi_handle handle = ACPI_HANDLE(&device->dev); @@ -728,12 +728,6 @@ static int intel_hid_remove(struct platform_device *device) acpi_remove_notify_handler(handle, ACPI_DEVICE_NOTIFY, notify_handler); intel_hid_set_enable(&device->dev, false); intel_button_array_enable(&device->dev, false); - - /* - * Even if we failed to shut off the event stream, we can still - * safely detach from the device. - */ - return 0; } static struct platform_driver intel_hid_pl_driver = { @@ -743,7 +737,7 @@ static struct platform_driver intel_hid_pl_driver = { .pm = &intel_hid_pl_pm_ops, }, .probe = intel_hid_probe, - .remove = intel_hid_remove, + .remove_new = intel_hid_remove, }; /* -- cgit v1.2.3 From 445f79fdd43566695a97d2eb8b59419eecb12af4 Mon Sep 17 00:00:00 2001 From: Uwe Kleine-König Date: Thu, 2 Mar 2023 15:47:22 +0100 Subject: platform/x86: intel: int0002_vgpio: Convert to platform remove callback returning void MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The .remove() callback for a platform driver returns an int which makes many driver authors wrongly assume it's possible to do error handling by returning an error code. However the value returned is (mostly) ignored and this typically results in resource leaks. To improve here there is a quest to make the remove callback return void. In the first step of this quest all drivers are converted to .remove_new() which already returns void. Trivially convert this driver from always returning zero in the remove callback to the void returning variant. Signed-off-by: Uwe Kleine-König Link: https://lore.kernel.org/r/20230302144732.1903781-20-u.kleine-koenig@pengutronix.de Reviewed-by: Hans de Goede Signed-off-by: Hans de Goede --- drivers/platform/x86/intel/int0002_vgpio.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) (limited to 'drivers/platform') diff --git a/drivers/platform/x86/intel/int0002_vgpio.c b/drivers/platform/x86/intel/int0002_vgpio.c index 97cfbc520a02..b6708bab7c53 100644 --- a/drivers/platform/x86/intel/int0002_vgpio.c +++ b/drivers/platform/x86/intel/int0002_vgpio.c @@ -223,11 +223,10 @@ static int int0002_probe(struct platform_device *pdev) return 0; } -static int int0002_remove(struct platform_device *pdev) +static void int0002_remove(struct platform_device *pdev) { device_init_wakeup(&pdev->dev, false); acpi_unregister_wakeup_handler(int0002_check_wake, NULL); - return 0; } static int int0002_suspend(struct device *dev) @@ -273,7 +272,7 @@ static struct platform_driver int0002_driver = { .pm = &int0002_pm_ops, }, .probe = int0002_probe, - .remove = int0002_remove, + .remove_new = int0002_remove, }; module_platform_driver(int0002_driver); -- cgit v1.2.3 From e709455e9736b1cf15d2c6914e26d34d4ebbdcf3 Mon Sep 17 00:00:00 2001 From: Uwe Kleine-König Date: Thu, 2 Mar 2023 15:47:23 +0100 Subject: platform/x86: intel: int1092: intel_sar: Convert to platform remove callback returning void MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The .remove() callback for a platform driver returns an int which makes many driver authors wrongly assume it's possible to do error handling by returning an error code. However the value returned is (mostly) ignored and this typically results in resource leaks. To improve here there is a quest to make the remove callback return void. In the first step of this quest all drivers are converted to .remove_new() which already returns void. Trivially convert this driver from always returning zero in the remove callback to the void returning variant. Signed-off-by: Uwe Kleine-König Link: https://lore.kernel.org/r/20230302144732.1903781-21-u.kleine-koenig@pengutronix.de Reviewed-by: Hans de Goede Signed-off-by: Hans de Goede --- drivers/platform/x86/intel/int1092/intel_sar.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) (limited to 'drivers/platform') diff --git a/drivers/platform/x86/intel/int1092/intel_sar.c b/drivers/platform/x86/intel/int1092/intel_sar.c index 352fc4596494..6246c066ade2 100644 --- a/drivers/platform/x86/intel/int1092/intel_sar.c +++ b/drivers/platform/x86/intel/int1092/intel_sar.c @@ -292,7 +292,7 @@ r_free: return result; } -static int sar_remove(struct platform_device *device) +static void sar_remove(struct platform_device *device) { struct wwan_sar_context *context = dev_get_drvdata(&device->dev); int reg; @@ -304,12 +304,11 @@ static int sar_remove(struct platform_device *device) kfree(context->config_data[reg].device_mode_info); kfree(context); - return 0; } static struct platform_driver sar_driver = { .probe = sar_probe, - .remove = sar_remove, + .remove_new = sar_remove, .driver = { .name = DRVNAME, .acpi_match_table = ACPI_PTR(sar_device_ids) -- cgit v1.2.3 From 6ee08b4eba3d36d5cc2bf1594040de95ae12ee7d Mon Sep 17 00:00:00 2001 From: Uwe Kleine-König Date: Thu, 2 Mar 2023 15:47:24 +0100 Subject: platform/x86: intel: int3472: discrete: Convert to platform remove callback returning void MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The .remove() callback for a platform driver returns an int which makes many driver authors wrongly assume it's possible to do error handling by returning an error code. However the value returned is (mostly) ignored and this typically results in resource leaks. To improve here there is a quest to make the remove callback return void. In the first step of this quest all drivers are converted to .remove_new() which already returns void. Trivially convert this driver from always returning zero in the remove callback to the void returning variant. Signed-off-by: Uwe Kleine-König Link: https://lore.kernel.org/r/20230302144732.1903781-22-u.kleine-koenig@pengutronix.de Reviewed-by: Hans de Goede Signed-off-by: Hans de Goede --- drivers/platform/x86/intel/int3472/discrete.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) (limited to 'drivers/platform') diff --git a/drivers/platform/x86/intel/int3472/discrete.c b/drivers/platform/x86/intel/int3472/discrete.c index f064da74f50a..ef020e23e596 100644 --- a/drivers/platform/x86/intel/int3472/discrete.c +++ b/drivers/platform/x86/intel/int3472/discrete.c @@ -317,7 +317,7 @@ static int skl_int3472_parse_crs(struct int3472_discrete_device *int3472) return 0; } -static int skl_int3472_discrete_remove(struct platform_device *pdev) +static void skl_int3472_discrete_remove(struct platform_device *pdev) { struct int3472_discrete_device *int3472 = platform_get_drvdata(pdev); @@ -326,8 +326,6 @@ static int skl_int3472_discrete_remove(struct platform_device *pdev) skl_int3472_unregister_clock(int3472); skl_int3472_unregister_pled(int3472); skl_int3472_unregister_regulator(int3472); - - return 0; } static int skl_int3472_discrete_probe(struct platform_device *pdev) @@ -392,7 +390,7 @@ static struct platform_driver int3472_discrete = { .acpi_match_table = int3472_device_id, }, .probe = skl_int3472_discrete_probe, - .remove = skl_int3472_discrete_remove, + .remove_new = skl_int3472_discrete_remove, }; module_platform_driver(int3472_discrete); -- cgit v1.2.3 From ee12d8b43cf7e847af8b24988bcda7ae23265383 Mon Sep 17 00:00:00 2001 From: Uwe Kleine-König Date: Thu, 2 Mar 2023 15:47:25 +0100 Subject: platform/x86: intel: mrfld_pwrbtn: Convert to platform remove callback returning void MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The .remove() callback for a platform driver returns an int which makes many driver authors wrongly assume it's possible to do error handling by returning an error code. However the value returned is (mostly) ignored and this typically results in resource leaks. To improve here there is a quest to make the remove callback return void. In the first step of this quest all drivers are converted to .remove_new() which already returns void. Trivially convert this driver from always returning zero in the remove callback to the void returning variant. Signed-off-by: Uwe Kleine-König Link: https://lore.kernel.org/r/20230302144732.1903781-23-u.kleine-koenig@pengutronix.de Reviewed-by: Hans de Goede Signed-off-by: Hans de Goede --- drivers/platform/x86/intel/mrfld_pwrbtn.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) (limited to 'drivers/platform') diff --git a/drivers/platform/x86/intel/mrfld_pwrbtn.c b/drivers/platform/x86/intel/mrfld_pwrbtn.c index d58fea51747e..549a3f586f3b 100644 --- a/drivers/platform/x86/intel/mrfld_pwrbtn.c +++ b/drivers/platform/x86/intel/mrfld_pwrbtn.c @@ -78,13 +78,12 @@ static int mrfld_pwrbtn_probe(struct platform_device *pdev) return 0; } -static int mrfld_pwrbtn_remove(struct platform_device *pdev) +static void mrfld_pwrbtn_remove(struct platform_device *pdev) { struct device *dev = &pdev->dev; dev_pm_clear_wake_irq(dev); device_init_wakeup(dev, false); - return 0; } static const struct platform_device_id mrfld_pwrbtn_id_table[] = { @@ -98,7 +97,7 @@ static struct platform_driver mrfld_pwrbtn_driver = { .name = "mrfld_bcove_pwrbtn", }, .probe = mrfld_pwrbtn_probe, - .remove = mrfld_pwrbtn_remove, + .remove_new = mrfld_pwrbtn_remove, .id_table = mrfld_pwrbtn_id_table, }; module_platform_driver(mrfld_pwrbtn_driver); -- cgit v1.2.3 From 1655db18b8dd387185dd4b491ce03b21176ab545 Mon Sep 17 00:00:00 2001 From: Uwe Kleine-König Date: Thu, 2 Mar 2023 15:47:26 +0100 Subject: platform/x86: intel: pmc: core: Convert to platform remove callback returning void MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The .remove() callback for a platform driver returns an int which makes many driver authors wrongly assume it's possible to do error handling by returning an error code. However the value returned is (mostly) ignored and this typically results in resource leaks. To improve here there is a quest to make the remove callback return void. In the first step of this quest all drivers are converted to .remove_new() which already returns void. Trivially convert this driver from always returning zero in the remove callback to the void returning variant. Signed-off-by: Uwe Kleine-König Link: https://lore.kernel.org/r/20230302144732.1903781-24-u.kleine-koenig@pengutronix.de Reviewed-by: Hans de Goede Signed-off-by: Hans de Goede --- drivers/platform/x86/intel/pmc/core.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) (limited to 'drivers/platform') diff --git a/drivers/platform/x86/intel/pmc/core.c b/drivers/platform/x86/intel/pmc/core.c index 3a15d32d7644..e489d2175e42 100644 --- a/drivers/platform/x86/intel/pmc/core.c +++ b/drivers/platform/x86/intel/pmc/core.c @@ -1149,7 +1149,7 @@ static int pmc_core_probe(struct platform_device *pdev) return 0; } -static int pmc_core_remove(struct platform_device *pdev) +static void pmc_core_remove(struct platform_device *pdev) { struct pmc_dev *pmcdev = platform_get_drvdata(pdev); @@ -1157,7 +1157,6 @@ static int pmc_core_remove(struct platform_device *pdev) platform_set_drvdata(pdev, NULL); mutex_destroy(&pmcdev->lock); iounmap(pmcdev->regbase); - return 0; } static bool warn_on_s0ix_failures; @@ -1264,7 +1263,7 @@ static struct platform_driver pmc_core_driver = { .dev_groups = pmc_dev_groups, }, .probe = pmc_core_probe, - .remove = pmc_core_remove, + .remove_new = pmc_core_remove, }; module_platform_driver(pmc_core_driver); -- cgit v1.2.3 From 2d58b0092b7558fd944ff729dcd2fbe80b45629b Mon Sep 17 00:00:00 2001 From: Uwe Kleine-König Date: Thu, 2 Mar 2023 15:47:27 +0100 Subject: platform/x86: intel: telemetry: pltdrv: Convert to platform remove callback returning void MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The .remove() callback for a platform driver returns an int which makes many driver authors wrongly assume it's possible to do error handling by returning an error code. However the value returned is (mostly) ignored and this typically results in resource leaks. To improve here there is a quest to make the remove callback return void. In the first step of this quest all drivers are converted to .remove_new() which already returns void. Trivially convert this driver from always returning zero in the remove callback to the void returning variant. Signed-off-by: Uwe Kleine-König Link: https://lore.kernel.org/r/20230302144732.1903781-25-u.kleine-koenig@pengutronix.de Reviewed-by: Hans de Goede Signed-off-by: Hans de Goede --- drivers/platform/x86/intel/telemetry/pltdrv.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) (limited to 'drivers/platform') diff --git a/drivers/platform/x86/intel/telemetry/pltdrv.c b/drivers/platform/x86/intel/telemetry/pltdrv.c index 405dea87de6b..06311d0e9451 100644 --- a/drivers/platform/x86/intel/telemetry/pltdrv.c +++ b/drivers/platform/x86/intel/telemetry/pltdrv.c @@ -1156,15 +1156,14 @@ out: return ret; } -static int telemetry_pltdrv_remove(struct platform_device *pdev) +static void telemetry_pltdrv_remove(struct platform_device *pdev) { telemetry_clear_pltdata(); - return 0; } static struct platform_driver telemetry_soc_driver = { .probe = telemetry_pltdrv_probe, - .remove = telemetry_pltdrv_remove, + .remove_new = telemetry_pltdrv_remove, .driver = { .name = DRIVER_NAME, }, -- cgit v1.2.3 From 4222272a04c386743e37f951fd4fdf958ba7f4d6 Mon Sep 17 00:00:00 2001 From: Uwe Kleine-König Date: Thu, 2 Mar 2023 15:47:28 +0100 Subject: platform/x86: intel: vbtn: Convert to platform remove callback returning void MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The .remove() callback for a platform driver returns an int which makes many driver authors wrongly assume it's possible to do error handling by returning an error code. However the value returned is (mostly) ignored and this typically results in resource leaks. To improve here there is a quest to make the remove callback return void. In the first step of this quest all drivers are converted to .remove_new() which already returns void. Trivially convert this driver from always returning zero in the remove callback to the void returning variant. Signed-off-by: Uwe Kleine-König Link: https://lore.kernel.org/r/20230302144732.1903781-26-u.kleine-koenig@pengutronix.de Reviewed-by: Hans de Goede Signed-off-by: Hans de Goede --- drivers/platform/x86/intel/vbtn.c | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) (limited to 'drivers/platform') diff --git a/drivers/platform/x86/intel/vbtn.c b/drivers/platform/x86/intel/vbtn.c index c5e4e35c8d20..6fa1735ad7a4 100644 --- a/drivers/platform/x86/intel/vbtn.c +++ b/drivers/platform/x86/intel/vbtn.c @@ -325,18 +325,12 @@ static int intel_vbtn_probe(struct platform_device *device) return 0; } -static int intel_vbtn_remove(struct platform_device *device) +static void intel_vbtn_remove(struct platform_device *device) { acpi_handle handle = ACPI_HANDLE(&device->dev); device_init_wakeup(&device->dev, false); acpi_remove_notify_handler(handle, ACPI_DEVICE_NOTIFY, notify_handler); - - /* - * Even if we failed to shut off the event stream, we can still - * safely detach from the device. - */ - return 0; } static int intel_vbtn_pm_prepare(struct device *dev) @@ -377,7 +371,7 @@ static struct platform_driver intel_vbtn_pl_driver = { .pm = &intel_vbtn_pm_ops, }, .probe = intel_vbtn_probe, - .remove = intel_vbtn_remove, + .remove_new = intel_vbtn_remove, }; static acpi_status __init -- cgit v1.2.3 From df23e156a98800e0a379cb6fdc68425b453a2696 Mon Sep 17 00:00:00 2001 From: Uwe Kleine-König Date: Thu, 2 Mar 2023 15:47:29 +0100 Subject: platform/x86: samsung-q10: Convert to platform remove callback returning void MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The .remove() callback for a platform driver returns an int which makes many driver authors wrongly assume it's possible to do error handling by returning an error code. However the value returned is (mostly) ignored and this typically results in resource leaks. To improve here there is a quest to make the remove callback return void. In the first step of this quest all drivers are converted to .remove_new() which already returns void. Trivially convert this driver from always returning zero in the remove callback to the void returning variant. Signed-off-by: Uwe Kleine-König Link: https://lore.kernel.org/r/20230302144732.1903781-27-u.kleine-koenig@pengutronix.de Reviewed-by: Hans de Goede Signed-off-by: Hans de Goede --- drivers/platform/x86/samsung-q10.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) (limited to 'drivers/platform') diff --git a/drivers/platform/x86/samsung-q10.c b/drivers/platform/x86/samsung-q10.c index 6eb08b539311..134e2c3d91ca 100644 --- a/drivers/platform/x86/samsung-q10.c +++ b/drivers/platform/x86/samsung-q10.c @@ -65,14 +65,12 @@ static int samsungq10_probe(struct platform_device *pdev) return 0; } -static int samsungq10_remove(struct platform_device *pdev) +static void samsungq10_remove(struct platform_device *pdev) { struct backlight_device *bd = platform_get_drvdata(pdev); backlight_device_unregister(bd); - - return 0; } static struct platform_driver samsungq10_driver = { @@ -80,7 +78,7 @@ static struct platform_driver samsungq10_driver = { .name = KBUILD_MODNAME, }, .probe = samsungq10_probe, - .remove = samsungq10_remove, + .remove_new = samsungq10_remove, }; static struct platform_device *samsungq10_device; -- cgit v1.2.3 From 143b3c1af5884ba0900d721f984245ca9897700d Mon Sep 17 00:00:00 2001 From: Uwe Kleine-König Date: Thu, 2 Mar 2023 15:47:30 +0100 Subject: platform/x86: serial-multi-instantiate: Convert to platform remove callback returning void MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The .remove() callback for a platform driver returns an int which makes many driver authors wrongly assume it's possible to do error handling by returning an error code. However the value returned is (mostly) ignored and this typically results in resource leaks. To improve here there is a quest to make the remove callback return void. In the first step of this quest all drivers are converted to .remove_new() which already returns void. Trivially convert this driver from always returning zero in the remove callback to the void returning variant. Signed-off-by: Uwe Kleine-König Link: https://lore.kernel.org/r/20230302144732.1903781-28-u.kleine-koenig@pengutronix.de Reviewed-by: Hans de Goede Signed-off-by: Hans de Goede --- drivers/platform/x86/serial-multi-instantiate.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) (limited to 'drivers/platform') diff --git a/drivers/platform/x86/serial-multi-instantiate.c b/drivers/platform/x86/serial-multi-instantiate.c index 5362f1a7b77c..cd25125b7923 100644 --- a/drivers/platform/x86/serial-multi-instantiate.c +++ b/drivers/platform/x86/serial-multi-instantiate.c @@ -265,13 +265,11 @@ static int smi_probe(struct platform_device *pdev) } } -static int smi_remove(struct platform_device *pdev) +static void smi_remove(struct platform_device *pdev) { struct smi *smi = platform_get_drvdata(pdev); smi_devs_unregister(smi); - - return 0; } static const struct smi_node bsg1160_data = { @@ -339,7 +337,7 @@ static struct platform_driver smi_driver = { .acpi_match_table = smi_acpi_ids, }, .probe = smi_probe, - .remove = smi_remove, + .remove_new = smi_remove, }; module_platform_driver(smi_driver); -- cgit v1.2.3 From 0f16136b7acb20c0a6ade4328259d0db977d2ec1 Mon Sep 17 00:00:00 2001 From: Uwe Kleine-König Date: Thu, 2 Mar 2023 15:47:31 +0100 Subject: platform/x86: wmi: Convert to platform remove callback returning void MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The .remove() callback for a platform driver returns an int which makes many driver authors wrongly assume it's possible to do error handling by returning an error code. However the value returned is (mostly) ignored and this typically results in resource leaks. To improve here there is a quest to make the remove callback return void. In the first step of this quest all drivers are converted to .remove_new() which already returns void. Trivially convert this driver from always returning zero in the remove callback to the void returning variant. Signed-off-by: Uwe Kleine-König Link: https://lore.kernel.org/r/20230302144732.1903781-29-u.kleine-koenig@pengutronix.de Reviewed-by: Hans de Goede Signed-off-by: Hans de Goede --- drivers/platform/x86/wmi.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) (limited to 'drivers/platform') diff --git a/drivers/platform/x86/wmi.c b/drivers/platform/x86/wmi.c index 4fe7650dd014..d81319a502ef 100644 --- a/drivers/platform/x86/wmi.c +++ b/drivers/platform/x86/wmi.c @@ -1369,7 +1369,7 @@ static void acpi_wmi_notify_handler(acpi_handle handle, u32 event, event, 0); } -static int acpi_wmi_remove(struct platform_device *device) +static void acpi_wmi_remove(struct platform_device *device) { struct acpi_device *acpi_device = ACPI_COMPANION(&device->dev); @@ -1379,8 +1379,6 @@ static int acpi_wmi_remove(struct platform_device *device) ACPI_ADR_SPACE_EC, &acpi_wmi_ec_space_handler); wmi_free_devices(acpi_device); device_unregister(dev_get_drvdata(&device->dev)); - - return 0; } static int acpi_wmi_probe(struct platform_device *device) @@ -1468,7 +1466,7 @@ static struct platform_driver acpi_wmi_driver = { .acpi_match_table = wmi_device_ids, }, .probe = acpi_wmi_probe, - .remove = acpi_wmi_remove, + .remove_new = acpi_wmi_remove, }; static int __init acpi_wmi_init(void) -- cgit v1.2.3 From 9a318b8b069e38f1ebfe19afac1a2eac0c45ed32 Mon Sep 17 00:00:00 2001 From: Uwe Kleine-König Date: Thu, 2 Mar 2023 15:47:32 +0100 Subject: platform/x86: xo1-rfkill: Convert to platform remove callback returning void MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The .remove() callback for a platform driver returns an int which makes many driver authors wrongly assume it's possible to do error handling by returning an error code. However the value returned is (mostly) ignored and this typically results in resource leaks. To improve here there is a quest to make the remove callback return void. In the first step of this quest all drivers are converted to .remove_new() which already returns void. Trivially convert this driver from always returning zero in the remove callback to the void returning variant. Signed-off-by: Uwe Kleine-König Link: https://lore.kernel.org/r/20230302144732.1903781-30-u.kleine-koenig@pengutronix.de Reviewed-by: Hans de Goede Signed-off-by: Hans de Goede --- drivers/platform/x86/xo1-rfkill.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) (limited to 'drivers/platform') diff --git a/drivers/platform/x86/xo1-rfkill.c b/drivers/platform/x86/xo1-rfkill.c index cb3253c10ef3..e64d5646b4c7 100644 --- a/drivers/platform/x86/xo1-rfkill.c +++ b/drivers/platform/x86/xo1-rfkill.c @@ -56,12 +56,11 @@ static int xo1_rfkill_probe(struct platform_device *pdev) return 0; } -static int xo1_rfkill_remove(struct platform_device *pdev) +static void xo1_rfkill_remove(struct platform_device *pdev) { struct rfkill *rfk = platform_get_drvdata(pdev); rfkill_unregister(rfk); rfkill_destroy(rfk); - return 0; } static struct platform_driver xo1_rfkill_driver = { @@ -69,7 +68,7 @@ static struct platform_driver xo1_rfkill_driver = { .name = "xo1-rfkill", }, .probe = xo1_rfkill_probe, - .remove = xo1_rfkill_remove, + .remove_new = xo1_rfkill_remove, }; module_platform_driver(xo1_rfkill_driver); -- cgit v1.2.3 From 90caf1dfe9efb727874bcafd30fefb0807cb7670 Mon Sep 17 00:00:00 2001 From: Orlando Chamberlain Date: Fri, 3 Mar 2023 22:28:40 +1100 Subject: platform/x86: apple-gmux: use first bit to check switch state On T2 Macs with MMIO gmux, when GMUX_PORT_SWITCH_DISPLAY is read, it can have values of 2, 3, 4, and 5. Odd values correspond to the discrete gpu, and even values correspond to the integrated gpu. The current logic is that only 2 corresponds to IGD, but this doesn't work for T2 Macs. Instead, check the first bit to determine the connected gpu. As T2 Macs with gmux only can switch the internal display, it is untested if this change (or a similar change) would be applicable to GMUX_PORT_SWITCH_DDC and GMUX_PORT_SWITCH_EXTERNAL. Reviewed-by: Hans de Goede Signed-off-by: Orlando Chamberlain Link: https://lore.kernel.org/r/20230303112842.3094-2-orlandoch.dev@gmail.com Signed-off-by: Hans de Goede --- drivers/platform/x86/apple-gmux.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'drivers/platform') diff --git a/drivers/platform/x86/apple-gmux.c b/drivers/platform/x86/apple-gmux.c index 9333f82cfa8a..ec99e05e532c 100644 --- a/drivers/platform/x86/apple-gmux.c +++ b/drivers/platform/x86/apple-gmux.c @@ -346,10 +346,10 @@ static void gmux_read_switch_state(struct apple_gmux_data *gmux_data) else gmux_data->switch_state_ddc = VGA_SWITCHEROO_DIS; - if (gmux_read8(gmux_data, GMUX_PORT_SWITCH_DISPLAY) == 2) - gmux_data->switch_state_display = VGA_SWITCHEROO_IGD; - else + if (gmux_read8(gmux_data, GMUX_PORT_SWITCH_DISPLAY) & 1) gmux_data->switch_state_display = VGA_SWITCHEROO_DIS; + else + gmux_data->switch_state_display = VGA_SWITCHEROO_IGD; if (gmux_read8(gmux_data, GMUX_PORT_SWITCH_EXTERNAL) == 2) gmux_data->switch_state_external = VGA_SWITCHEROO_IGD; -- cgit v1.2.3 From 96ec2d9868c4afd3cea17f4ee18b84ccc1cde20f Mon Sep 17 00:00:00 2001 From: Orlando Chamberlain Date: Fri, 3 Mar 2023 22:28:41 +1100 Subject: platform/x86: apple-gmux: refactor gmux types Add apple_gmux_config struct containing operations and data specific to each mux type. This is in preparation for adding a third, MMIO based, gmux type. Reviewed-by: Hans de Goede Signed-off-by: Orlando Chamberlain Link: https://lore.kernel.org/r/20230303112842.3094-3-orlandoch.dev@gmail.com Signed-off-by: Hans de Goede --- drivers/platform/x86/apple-gmux.c | 93 ++++++++++++++++++++++++++------------- include/linux/apple-gmux.h | 18 +++++--- 2 files changed, 74 insertions(+), 37 deletions(-) (limited to 'drivers/platform') diff --git a/drivers/platform/x86/apple-gmux.c b/drivers/platform/x86/apple-gmux.c index ec99e05e532c..36208e93d745 100644 --- a/drivers/platform/x86/apple-gmux.c +++ b/drivers/platform/x86/apple-gmux.c @@ -5,6 +5,7 @@ * Copyright (C) Canonical Ltd. * Copyright (C) 2010-2012 Andreas Heider * Copyright (C) 2015 Lukas Wunner + * Copyright (C) 2023 Orlando Chamberlain */ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt @@ -43,10 +44,12 @@ * http://www.renesas.com/products/mpumcu/h8s/h8s2100/h8s2113/index.jsp */ +struct apple_gmux_config; + struct apple_gmux_data { unsigned long iostart; unsigned long iolen; - bool indexed; + const struct apple_gmux_config *config; struct mutex index_lock; struct backlight_device *bdev; @@ -64,6 +67,18 @@ struct apple_gmux_data { static struct apple_gmux_data *apple_gmux_data; +struct apple_gmux_config { + u8 (*read8)(struct apple_gmux_data *gmux_data, int port); + void (*write8)(struct apple_gmux_data *gmux_data, int port, u8 val); + u32 (*read32)(struct apple_gmux_data *gmux_data, int port); + void (*write32)(struct apple_gmux_data *gmux_data, int port, u32 val); + const struct vga_switcheroo_handler *gmux_handler; + enum vga_switcheroo_handler_flags_t handler_flags; + unsigned long resource_type; + bool read_version_as_u32; + char *name; +}; + #define GMUX_INTERRUPT_ENABLE 0xff #define GMUX_INTERRUPT_DISABLE 0x00 @@ -195,35 +210,23 @@ static void gmux_index_write32(struct apple_gmux_data *gmux_data, int port, static u8 gmux_read8(struct apple_gmux_data *gmux_data, int port) { - if (gmux_data->indexed) - return gmux_index_read8(gmux_data, port); - else - return gmux_pio_read8(gmux_data, port); + return gmux_data->config->read8(gmux_data, port); } static void gmux_write8(struct apple_gmux_data *gmux_data, int port, u8 val) { - if (gmux_data->indexed) - gmux_index_write8(gmux_data, port, val); - else - gmux_pio_write8(gmux_data, port, val); + return gmux_data->config->write8(gmux_data, port, val); } static u32 gmux_read32(struct apple_gmux_data *gmux_data, int port) { - if (gmux_data->indexed) - return gmux_index_read32(gmux_data, port); - else - return gmux_pio_read32(gmux_data, port); + return gmux_data->config->read32(gmux_data, port); } static void gmux_write32(struct apple_gmux_data *gmux_data, int port, u32 val) { - if (gmux_data->indexed) - gmux_index_write32(gmux_data, port, val); - else - gmux_pio_write32(gmux_data, port, val); + return gmux_data->config->write32(gmux_data, port, val); } /** @@ -463,19 +466,43 @@ static enum vga_switcheroo_client_id gmux_get_client_id(struct pci_dev *pdev) return VGA_SWITCHEROO_DIS; } -static const struct vga_switcheroo_handler gmux_handler_indexed = { +static const struct vga_switcheroo_handler gmux_handler_no_ddc = { .switchto = gmux_switchto, .power_state = gmux_set_power_state, .get_client_id = gmux_get_client_id, }; -static const struct vga_switcheroo_handler gmux_handler_classic = { +static const struct vga_switcheroo_handler gmux_handler_ddc = { .switchto = gmux_switchto, .switch_ddc = gmux_switch_ddc, .power_state = gmux_set_power_state, .get_client_id = gmux_get_client_id, }; +static const struct apple_gmux_config apple_gmux_pio = { + .read8 = &gmux_pio_read8, + .write8 = &gmux_pio_write8, + .read32 = &gmux_pio_read32, + .write32 = &gmux_pio_write32, + .gmux_handler = &gmux_handler_ddc, + .handler_flags = VGA_SWITCHEROO_CAN_SWITCH_DDC, + .resource_type = IORESOURCE_IO, + .read_version_as_u32 = false, + .name = "classic" +}; + +static const struct apple_gmux_config apple_gmux_index = { + .read8 = &gmux_index_read8, + .write8 = &gmux_index_write8, + .read32 = &gmux_index_read32, + .write32 = &gmux_index_write32, + .gmux_handler = &gmux_handler_no_ddc, + .handler_flags = VGA_SWITCHEROO_NEEDS_EDP_CONFIG, + .resource_type = IORESOURCE_IO, + .read_version_as_u32 = true, + .name = "indexed" +}; + /** * DOC: Interrupt * @@ -565,13 +592,13 @@ static int gmux_probe(struct pnp_dev *pnp, const struct pnp_device_id *id) int ret = -ENXIO; acpi_status status; unsigned long long gpe; - bool indexed = false; + enum apple_gmux_type type; u32 version; if (apple_gmux_data) return -EBUSY; - if (!apple_gmux_detect(pnp, &indexed)) { + if (!apple_gmux_detect(pnp, &type)) { pr_info("gmux device not present\n"); return -ENODEV; } @@ -581,6 +608,16 @@ static int gmux_probe(struct pnp_dev *pnp, const struct pnp_device_id *id) return -ENOMEM; pnp_set_drvdata(pnp, gmux_data); + switch (type) { + case APPLE_GMUX_TYPE_INDEXED: + gmux_data->config = &apple_gmux_index; + mutex_init(&gmux_data->index_lock); + break; + case APPLE_GMUX_TYPE_PIO: + gmux_data->config = &apple_gmux_pio; + break; + } + res = pnp_get_resource(pnp, IORESOURCE_IO, 0); gmux_data->iostart = res->start; gmux_data->iolen = resource_size(res); @@ -591,9 +628,7 @@ static int gmux_probe(struct pnp_dev *pnp, const struct pnp_device_id *id) goto err_free; } - if (indexed) { - mutex_init(&gmux_data->index_lock); - gmux_data->indexed = true; + if (gmux_data->config->read_version_as_u32) { version = gmux_read32(gmux_data, GMUX_PORT_VERSION_MAJOR); ver_major = (version >> 24) & 0xff; ver_minor = (version >> 16) & 0xff; @@ -604,7 +639,7 @@ static int gmux_probe(struct pnp_dev *pnp, const struct pnp_device_id *id) ver_release = gmux_read8(gmux_data, GMUX_PORT_VERSION_RELEASE); } pr_info("Found gmux version %d.%d.%d [%s]\n", ver_major, ver_minor, - ver_release, (gmux_data->indexed ? "indexed" : "classic")); + ver_release, gmux_data->config->name); memset(&props, 0, sizeof(props)); props.type = BACKLIGHT_PLATFORM; @@ -694,12 +729,8 @@ static int gmux_probe(struct pnp_dev *pnp, const struct pnp_device_id *id) * * Pre-retina MacBook Pros can switch the panel's DDC separately. */ - if (gmux_data->indexed) - ret = vga_switcheroo_register_handler(&gmux_handler_indexed, - VGA_SWITCHEROO_NEEDS_EDP_CONFIG); - else - ret = vga_switcheroo_register_handler(&gmux_handler_classic, - VGA_SWITCHEROO_CAN_SWITCH_DDC); + ret = vga_switcheroo_register_handler(gmux_data->config->gmux_handler, + gmux_data->config->handler_flags); if (ret) { pr_err("Failed to register vga_switcheroo handler\n"); goto err_register_handler; diff --git a/include/linux/apple-gmux.h b/include/linux/apple-gmux.h index 1f68b49bcd68..147dc1c52e08 100644 --- a/include/linux/apple-gmux.h +++ b/include/linux/apple-gmux.h @@ -36,6 +36,11 @@ #define GMUX_MIN_IO_LEN (GMUX_PORT_BRIGHTNESS + 4) +enum apple_gmux_type { + APPLE_GMUX_TYPE_PIO, + APPLE_GMUX_TYPE_INDEXED, +}; + #if IS_ENABLED(CONFIG_APPLE_GMUX) static inline bool apple_gmux_is_indexed(unsigned long iostart) { @@ -65,13 +70,13 @@ static inline bool apple_gmux_is_indexed(unsigned long iostart) * Return: %true if a supported gmux ACPI device is detected and the kernel * was configured with CONFIG_APPLE_GMUX, %false otherwise. */ -static inline bool apple_gmux_detect(struct pnp_dev *pnp_dev, bool *indexed_ret) +static inline bool apple_gmux_detect(struct pnp_dev *pnp_dev, enum apple_gmux_type *type_ret) { u8 ver_major, ver_minor, ver_release; struct device *dev = NULL; struct acpi_device *adev; struct resource *res; - bool indexed = false; + enum apple_gmux_type type = APPLE_GMUX_TYPE_PIO; bool ret = false; if (!pnp_dev) { @@ -99,13 +104,14 @@ static inline bool apple_gmux_detect(struct pnp_dev *pnp_dev, bool *indexed_ret) ver_minor = inb(res->start + GMUX_PORT_VERSION_MINOR); ver_release = inb(res->start + GMUX_PORT_VERSION_RELEASE); if (ver_major == 0xff && ver_minor == 0xff && ver_release == 0xff) { - indexed = apple_gmux_is_indexed(res->start); - if (!indexed) + if (apple_gmux_is_indexed(res->start)) + type = APPLE_GMUX_TYPE_INDEXED; + else goto out; } - if (indexed_ret) - *indexed_ret = indexed; + if (type_ret) + *type_ret = type; ret = true; out: -- cgit v1.2.3 From 0c18184de990e63f708b090bcb9fc6c0fbc427cd Mon Sep 17 00:00:00 2001 From: Orlando Chamberlain Date: Fri, 3 Mar 2023 22:28:42 +1100 Subject: platform/x86: apple-gmux: support MMIO gmux on T2 Macs In some newer dual gpu MacBooks, the T2 Coprocessor functions as the gmux, and the Intel side can interract with this new gmux type through MMIO. Add support for these gmux controllers to the apple-gmux driver. We start using the GMSP(0) acpi method on these gmux's when clearing interrupts, as this prevents a flood of status=0 interrupts that can't be cleared. It's unknown if this helps or hinders older gmux types, so it isn't enabled for those. Interestingly, the ACPI table only allocates 8 bytes for GMUX, but we actually need 16, and as such we request 16 with request_mem_region. Reading and writing from ports: 16 bytes from 0xfe0b0200 are used. 0x0 to 0x4 are where data to read appears, and where data to write goes. Writing to 0xe sets the gmux port being accessed, and writing to 0xf sends commands. These commands are 0x40 & data_length for write, and data_length for read, where data_length is 1, 2 or 4. Once byte base+0xf is 0, the command is done. Issues: As with other retina models, we can't switch DDC lines so switching at runtime doesn't work if the inactive gpu driver already disabled eDP due to it not being connected when that driver loaded. Additionally, turning on the dgpu back on on the MacBookPro16,1 does not work. Reviewed-by: Hans de Goede Signed-off-by: Orlando Chamberlain Link: https://lore.kernel.org/r/20230303112842.3094-4-orlandoch.dev@gmail.com Signed-off-by: Hans de Goede --- drivers/platform/x86/apple-gmux.c | 170 +++++++++++++++++++++++++++++++++++--- include/linux/apple-gmux.h | 56 ++++++++++--- 2 files changed, 200 insertions(+), 26 deletions(-) (limited to 'drivers/platform') diff --git a/drivers/platform/x86/apple-gmux.c b/drivers/platform/x86/apple-gmux.c index 36208e93d745..59c4ee8109dd 100644 --- a/drivers/platform/x86/apple-gmux.c +++ b/drivers/platform/x86/apple-gmux.c @@ -28,25 +28,35 @@ * DOC: Overview * * gmux is a microcontroller built into the MacBook Pro to support dual GPUs: - * A `Lattice XP2`_ on pre-retinas, a `Renesas R4F2113`_ on retinas. + * A `Lattice XP2`_ on pre-retinas, a `Renesas R4F2113`_ on pre-T2 retinas. + * + * On T2 Macbooks, the gmux is part of the T2 Coprocessor's SMC. The SMC has + * an I2C connection to a `NXP PCAL6524` GPIO expander, which enables/disables + * the voltage regulators of the discrete GPU, drives the display panel power, + * and has a GPIO to switch the eDP mux. The Intel CPU can interact with + * gmux through MMIO, similar to how the main SMC interface is controlled. * * (The MacPro6,1 2013 also has a gmux, however it is unclear why since it has * dual GPUs but no built-in display.) * * gmux is connected to the LPC bus of the southbridge. Its I/O ports are * accessed differently depending on the microcontroller: Driver functions - * to access a pre-retina gmux are infixed ``_pio_``, those for a retina gmux - * are infixed ``_index_``. + * to access a pre-retina gmux are infixed ``_pio_``, those for a pre-T2 + * retina gmux are infixed ``_index_``, and those on T2 Macs are infixed + * with ``_mmio_``. * * .. _Lattice XP2: * http://www.latticesemi.com/en/Products/FPGAandCPLD/LatticeXP2.aspx * .. _Renesas R4F2113: * http://www.renesas.com/products/mpumcu/h8s/h8s2100/h8s2113/index.jsp + * .. _NXP PCAL6524: + * https://www.nxp.com/docs/en/data-sheet/PCAL6524.pdf */ struct apple_gmux_config; struct apple_gmux_data { + u8 *__iomem iomem_base; unsigned long iostart; unsigned long iolen; const struct apple_gmux_config *config; @@ -208,6 +218,79 @@ static void gmux_index_write32(struct apple_gmux_data *gmux_data, int port, mutex_unlock(&gmux_data->index_lock); } +static int gmux_mmio_wait(struct apple_gmux_data *gmux_data) +{ + int i = 200; + u8 gwr = ioread8(gmux_data->iomem_base + GMUX_MMIO_COMMAND_SEND); + + while (i && gwr) { + gwr = ioread8(gmux_data->iomem_base + GMUX_MMIO_COMMAND_SEND); + udelay(100); + i--; + } + + return !!i; +} + +static u8 gmux_mmio_read8(struct apple_gmux_data *gmux_data, int port) +{ + u8 val; + + mutex_lock(&gmux_data->index_lock); + gmux_mmio_wait(gmux_data); + iowrite8((port & 0xff), gmux_data->iomem_base + GMUX_MMIO_PORT_SELECT); + iowrite8(GMUX_MMIO_READ | sizeof(val), + gmux_data->iomem_base + GMUX_MMIO_COMMAND_SEND); + gmux_mmio_wait(gmux_data); + val = ioread8(gmux_data->iomem_base); + mutex_unlock(&gmux_data->index_lock); + + return val; +} + +static void gmux_mmio_write8(struct apple_gmux_data *gmux_data, int port, + u8 val) +{ + mutex_lock(&gmux_data->index_lock); + gmux_mmio_wait(gmux_data); + iowrite8(val, gmux_data->iomem_base); + + iowrite8(port & 0xff, gmux_data->iomem_base + GMUX_MMIO_PORT_SELECT); + iowrite8(GMUX_MMIO_WRITE | sizeof(val), + gmux_data->iomem_base + GMUX_MMIO_COMMAND_SEND); + + gmux_mmio_wait(gmux_data); + mutex_unlock(&gmux_data->index_lock); +} + +static u32 gmux_mmio_read32(struct apple_gmux_data *gmux_data, int port) +{ + u32 val; + + mutex_lock(&gmux_data->index_lock); + gmux_mmio_wait(gmux_data); + iowrite8((port & 0xff), gmux_data->iomem_base + GMUX_MMIO_PORT_SELECT); + iowrite8(GMUX_MMIO_READ | sizeof(val), + gmux_data->iomem_base + GMUX_MMIO_COMMAND_SEND); + gmux_mmio_wait(gmux_data); + val = be32_to_cpu(ioread32(gmux_data->iomem_base)); + mutex_unlock(&gmux_data->index_lock); + + return val; +} + +static void gmux_mmio_write32(struct apple_gmux_data *gmux_data, int port, + u32 val) +{ + mutex_lock(&gmux_data->index_lock); + iowrite32(cpu_to_be32(val), gmux_data->iomem_base); + iowrite8(port & 0xff, gmux_data->iomem_base + GMUX_MMIO_PORT_SELECT); + iowrite8(GMUX_MMIO_WRITE | sizeof(val), + gmux_data->iomem_base + GMUX_MMIO_COMMAND_SEND); + gmux_mmio_wait(gmux_data); + mutex_unlock(&gmux_data->index_lock); +} + static u8 gmux_read8(struct apple_gmux_data *gmux_data, int port) { return gmux_data->config->read8(gmux_data, port); @@ -236,8 +319,8 @@ static void gmux_write32(struct apple_gmux_data *gmux_data, int port, * the GPU. On dual GPU MacBook Pros by contrast, either GPU may be suspended * to conserve energy. Hence the PWM signal needs to be generated by a separate * backlight driver which is controlled by gmux. The earliest generation - * MBP5 2008/09 uses a `TI LP8543`_ backlight driver. All newer models - * use a `TI LP8545`_. + * MBP5 2008/09 uses a `TI LP8543`_ backlight driver. Newer models + * use a `TI LP8545`_ or a TI LP8548. * * .. _TI LP8543: https://www.ti.com/lit/ds/symlink/lp8543.pdf * .. _TI LP8545: https://www.ti.com/lit/ds/symlink/lp8545.pdf @@ -301,8 +384,8 @@ static const struct backlight_ops gmux_bl_ops = { * connecting it either to the discrete GPU or the Thunderbolt controller. * Oddly enough, while the full port is no longer switchable, AUX and HPD * are still switchable by way of an `NXP CBTL03062`_ (on pre-retinas - * MBP8 2011 and MBP9 2012) or two `TI TS3DS10224`_ (on retinas) under the - * control of gmux. Since the integrated GPU is missing the main link, + * MBP8 2011 and MBP9 2012) or two `TI TS3DS10224`_ (on pre-t2 retinas) under + * the control of gmux. Since the integrated GPU is missing the main link, * external displays appear to it as phantoms which fail to link-train. * * gmux receives the HPD signal of all display connectors and sends an @@ -503,14 +586,42 @@ static const struct apple_gmux_config apple_gmux_index = { .name = "indexed" }; +static const struct apple_gmux_config apple_gmux_mmio = { + .read8 = &gmux_mmio_read8, + .write8 = &gmux_mmio_write8, + .read32 = &gmux_mmio_read32, + .write32 = &gmux_mmio_write32, + .gmux_handler = &gmux_handler_no_ddc, + .handler_flags = VGA_SWITCHEROO_NEEDS_EDP_CONFIG, + .resource_type = IORESOURCE_MEM, + .read_version_as_u32 = true, + .name = "T2" +}; + + /** * DOC: Interrupt * * gmux is also connected to a GPIO pin of the southbridge and thereby is able - * to trigger an ACPI GPE. On the MBP5 2008/09 it's GPIO pin 22 of the Nvidia - * MCP79, on all following generations it's GPIO pin 6 of the Intel PCH. + * to trigger an ACPI GPE. ACPI name GMGP holds this GPIO pin's number. On the + * MBP5 2008/09 it's GPIO pin 22 of the Nvidia MCP79, on following generations + * it's GPIO pin 6 of the Intel PCH, on MMIO gmux's it's pin 21. + * * The GPE merely signals that an interrupt occurred, the actual type of event * is identified by reading a gmux register. + * + * In addition to the GMGP name, gmux's ACPI device also has two methods GMSP + * and GMLV. GMLV likely means "GMUX Level", and reads the value of the GPIO, + * while GMSP likely means "GMUX Set Polarity", and seems to write to the GPIO's + * value. On newer Macbooks (This was introduced with or sometime before the + * MacBookPro14,3), the ACPI GPE method differentiates between the OS type: On + * Darwin, only a notification is signaled, whereas on other OSes, the GPIO's + * value is read and then inverted. + * + * Because Linux masquerades as Darwin, it ends up in the notification-only code + * path. On MMIO gmux's, this seems to lead to us being unable to clear interrupts, + * unless we call GMSP(0). Without this, there is a flood of status=0 interrupts + * that can't be cleared. This issue seems to be unique to MMIO gmux's. */ static inline void gmux_disable_interrupts(struct apple_gmux_data *gmux_data) @@ -537,6 +648,9 @@ static void gmux_clear_interrupts(struct apple_gmux_data *gmux_data) /* to clear interrupts write back current status */ status = gmux_interrupt_get_status(gmux_data); gmux_write8(gmux_data, GMUX_PORT_INTERRUPT_STATUS, status); + /* Prevent flood of status=0 interrupts */ + if (gmux_data->config == &apple_gmux_mmio) + acpi_execute_simple_method(gmux_data->dhandle, "GMSP", 0); } static void gmux_notify_handler(acpi_handle device, u32 value, void *context) @@ -609,6 +723,25 @@ static int gmux_probe(struct pnp_dev *pnp, const struct pnp_device_id *id) pnp_set_drvdata(pnp, gmux_data); switch (type) { + case APPLE_GMUX_TYPE_MMIO: + gmux_data->config = &apple_gmux_mmio; + mutex_init(&gmux_data->index_lock); + + res = pnp_get_resource(pnp, IORESOURCE_MEM, 0); + gmux_data->iostart = res->start; + /* Although the ACPI table only allocates 8 bytes, we need 16. */ + gmux_data->iolen = 16; + if (!request_mem_region(gmux_data->iostart, gmux_data->iolen, + "Apple gmux")) { + pr_err("gmux I/O already in use\n"); + goto err_free; + } + gmux_data->iomem_base = ioremap(gmux_data->iostart, gmux_data->iolen); + if (!gmux_data->iomem_base) { + pr_err("couldn't remap gmux mmio region"); + goto err_release; + } + goto get_version; case APPLE_GMUX_TYPE_INDEXED: gmux_data->config = &apple_gmux_index; mutex_init(&gmux_data->index_lock); @@ -628,6 +761,7 @@ static int gmux_probe(struct pnp_dev *pnp, const struct pnp_device_id *id) goto err_free; } +get_version: if (gmux_data->config->read_version_as_u32) { version = gmux_read32(gmux_data, GMUX_PORT_VERSION_MAJOR); ver_major = (version >> 24) & 0xff; @@ -658,7 +792,7 @@ static int gmux_probe(struct pnp_dev *pnp, const struct pnp_device_id *id) gmux_data, &gmux_bl_ops, &props); if (IS_ERR(bdev)) { ret = PTR_ERR(bdev); - goto err_release; + goto err_unmap; } gmux_data->bdev = bdev; @@ -725,7 +859,7 @@ static int gmux_probe(struct pnp_dev *pnp, const struct pnp_device_id *id) /* * Retina MacBook Pros cannot switch the panel's AUX separately * and need eDP pre-calibration. They are distinguishable from - * pre-retinas by having an "indexed" gmux. + * pre-retinas by having an "indexed" or "T2" gmux. * * Pre-retina MacBook Pros can switch the panel's DDC separately. */ @@ -750,8 +884,14 @@ err_enable_gpe: &gmux_notify_handler); err_notify: backlight_device_unregister(bdev); +err_unmap: + if (gmux_data->iomem_base) + iounmap(gmux_data->iomem_base); err_release: - release_region(gmux_data->iostart, gmux_data->iolen); + if (gmux_data->config->resource_type == IORESOURCE_MEM) + release_mem_region(gmux_data->iostart, gmux_data->iolen); + else + release_region(gmux_data->iostart, gmux_data->iolen); err_free: kfree(gmux_data); return ret; @@ -772,7 +912,11 @@ static void gmux_remove(struct pnp_dev *pnp) backlight_device_unregister(gmux_data->bdev); - release_region(gmux_data->iostart, gmux_data->iolen); + if (gmux_data->iomem_base) { + iounmap(gmux_data->iomem_base); + release_mem_region(gmux_data->iostart, gmux_data->iolen); + } else + release_region(gmux_data->iostart, gmux_data->iolen); apple_gmux_data = NULL; kfree(gmux_data); diff --git a/include/linux/apple-gmux.h b/include/linux/apple-gmux.h index 147dc1c52e08..272f63f8fd7c 100644 --- a/include/linux/apple-gmux.h +++ b/include/linux/apple-gmux.h @@ -34,11 +34,18 @@ #define GMUX_PORT_READ 0xd0 #define GMUX_PORT_WRITE 0xd4 +#define GMUX_MMIO_PORT_SELECT 0x0e +#define GMUX_MMIO_COMMAND_SEND 0x0f + +#define GMUX_MMIO_READ 0x00 +#define GMUX_MMIO_WRITE 0x40 + #define GMUX_MIN_IO_LEN (GMUX_PORT_BRIGHTNESS + 4) enum apple_gmux_type { APPLE_GMUX_TYPE_PIO, APPLE_GMUX_TYPE_INDEXED, + APPLE_GMUX_TYPE_MMIO, }; #if IS_ENABLED(CONFIG_APPLE_GMUX) @@ -57,6 +64,24 @@ static inline bool apple_gmux_is_indexed(unsigned long iostart) return false; } +static inline bool apple_gmux_is_mmio(unsigned long iostart) +{ + u8 *__iomem iomem_base = ioremap(iostart, 16); + u8 val; + + if (!iomem_base) + return false; + + /* + * If this is 0xff, then gmux must not be present, as the gmux would + * reset it to 0x00, or it would be one of 0x1, 0x4, 0x41, 0x44 if a + * command is currently being processed. + */ + val = ioread8(iomem_base + GMUX_MMIO_COMMAND_SEND); + iounmap(iomem_base); + return (val != 0xff); +} + /** * apple_gmux_detect() - detect if gmux is built into the machine * @@ -93,19 +118,24 @@ static inline bool apple_gmux_detect(struct pnp_dev *pnp_dev, enum apple_gmux_ty } res = pnp_get_resource(pnp_dev, IORESOURCE_IO, 0); - if (!res || resource_size(res) < GMUX_MIN_IO_LEN) - goto out; - - /* - * Invalid version information may indicate either that the gmux - * device isn't present or that it's a new one that uses indexed io. - */ - ver_major = inb(res->start + GMUX_PORT_VERSION_MAJOR); - ver_minor = inb(res->start + GMUX_PORT_VERSION_MINOR); - ver_release = inb(res->start + GMUX_PORT_VERSION_RELEASE); - if (ver_major == 0xff && ver_minor == 0xff && ver_release == 0xff) { - if (apple_gmux_is_indexed(res->start)) - type = APPLE_GMUX_TYPE_INDEXED; + if (res && resource_size(res) >= GMUX_MIN_IO_LEN) { + /* + * Invalid version information may indicate either that the gmux + * device isn't present or that it's a new one that uses indexed io. + */ + ver_major = inb(res->start + GMUX_PORT_VERSION_MAJOR); + ver_minor = inb(res->start + GMUX_PORT_VERSION_MINOR); + ver_release = inb(res->start + GMUX_PORT_VERSION_RELEASE); + if (ver_major == 0xff && ver_minor == 0xff && ver_release == 0xff) { + if (apple_gmux_is_indexed(res->start)) + type = APPLE_GMUX_TYPE_INDEXED; + else + goto out; + } + } else { + res = pnp_get_resource(pnp_dev, IORESOURCE_MEM, 0); + if (res && apple_gmux_is_mmio(res->start)) + type = APPLE_GMUX_TYPE_MMIO; else goto out; } -- cgit v1.2.3 From f863fac1b15f77fc3c115f5afbd535983bb961a1 Mon Sep 17 00:00:00 2001 From: Orlando Chamberlain Date: Fri, 3 Mar 2023 22:28:43 +1100 Subject: platform/x86: apple-gmux: add debugfs interface Allow reading and writing gmux ports from userspace. For example: echo 4 > /sys/kernel/debug/apple_gmux/selected_port cat /sys/kernel/debug/apple_gmux/selected_port_data | xxd -p Will show the gmux version information (00000005 in this case) Reviewed-by: Hans de Goede Signed-off-by: Orlando Chamberlain Link: https://lore.kernel.org/r/20230303112842.3094-5-orlandoch.dev@gmail.com Signed-off-by: Hans de Goede --- drivers/platform/x86/apple-gmux.c | 82 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 82 insertions(+) (limited to 'drivers/platform') diff --git a/drivers/platform/x86/apple-gmux.c b/drivers/platform/x86/apple-gmux.c index 59c4ee8109dd..757fd71c900f 100644 --- a/drivers/platform/x86/apple-gmux.c +++ b/drivers/platform/x86/apple-gmux.c @@ -22,6 +22,7 @@ #include #include #include +#include #include /** @@ -73,6 +74,10 @@ struct apple_gmux_data { enum vga_switcheroo_client_id switch_state_external; enum vga_switcheroo_state power_state; struct completion powerchange_done; + + /* debugfs data */ + u8 selected_port; + struct dentry *debug_dentry; }; static struct apple_gmux_data *apple_gmux_data; @@ -670,6 +675,81 @@ static void gmux_notify_handler(acpi_handle device, u32 value, void *context) complete(&gmux_data->powerchange_done); } +/** + * DOC: Debugfs Interface + * + * gmux ports can be accessed from userspace as a debugfs interface. For example: + * + * # echo 4 > /sys/kernel/debug/apple_gmux/selected_port + * # cat /sys/kernel/debug/apple_gmux/selected_port_data | xxd -p + * 00000005 + * + * Reads 4 bytes from port 4 (GMUX_PORT_VERSION_MAJOR). + * + * 1 and 4 byte writes are also allowed. + */ + +static ssize_t gmux_selected_port_data_write(struct file *file, + const char __user *userbuf, size_t count, loff_t *ppos) +{ + struct apple_gmux_data *gmux_data = file->private_data; + int ret; + + if (*ppos) + return -EINVAL; + + if (count == 1) { + u8 data; + + ret = copy_from_user(&data, userbuf, 1); + if (ret) + return ret; + gmux_write8(gmux_data, gmux_data->selected_port, data); + } else if (count == 4) { + u32 data; + + ret = copy_from_user(&data, userbuf, 4); + if (ret) + return ret; + gmux_write32(gmux_data, gmux_data->selected_port, data); + } else + return -EINVAL; + + return count; +} + +static ssize_t gmux_selected_port_data_read(struct file *file, + char __user *userbuf, size_t count, loff_t *ppos) +{ + struct apple_gmux_data *gmux_data = file->private_data; + u32 data; + + data = gmux_read32(gmux_data, gmux_data->selected_port); + + return simple_read_from_buffer(userbuf, count, ppos, &data, sizeof(data)); +} + +static const struct file_operations gmux_port_data_ops = { + .open = simple_open, + .write = gmux_selected_port_data_write, + .read = gmux_selected_port_data_read +}; + +static void gmux_init_debugfs(struct apple_gmux_data *gmux_data) +{ + gmux_data->debug_dentry = debugfs_create_dir(KBUILD_MODNAME, NULL); + + debugfs_create_u8("selected_port", 0644, gmux_data->debug_dentry, + &gmux_data->selected_port); + debugfs_create_file("selected_port_data", 0644, gmux_data->debug_dentry, + gmux_data, &gmux_port_data_ops); +} + +static void gmux_fini_debugfs(struct apple_gmux_data *gmux_data) +{ + debugfs_remove_recursive(gmux_data->debug_dentry); +} + static int gmux_suspend(struct device *dev) { struct pnp_dev *pnp = to_pnp_dev(dev); @@ -870,6 +950,7 @@ get_version: goto err_register_handler; } + gmux_init_debugfs(gmux_data); return 0; err_register_handler: @@ -901,6 +982,7 @@ static void gmux_remove(struct pnp_dev *pnp) { struct apple_gmux_data *gmux_data = pnp_get_drvdata(pnp); + gmux_fini_debugfs(gmux_data); vga_switcheroo_unregister_handler(); gmux_disable_interrupts(gmux_data); if (gmux_data->gpe >= 0) { -- cgit v1.2.3 From d2e6832147998ba12c810cf88862126f6fae9b0a Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Tue, 7 Mar 2023 12:38:13 +0100 Subject: platform/x86: apple-gmux: Add acpi_video_get_backlight_type() check Make apple-gmux backlight registration honor the acpi_backlight=... kernel commandline option which is used to select the backlight control method on x86/ACPI devices. Reported-and-tested-by: Aditya Garg Link: https://lore.kernel.org/platform-driver-x86/BM1PR01MB0931B467250831916F7C55B3B8A59@BM1PR01MB0931.INDPRD01.PROD.OUTLOOK.COM/ Signed-off-by: Hans de Goede Link: https://lore.kernel.org/r/20230307113813.359743-1-hdegoede@redhat.com --- drivers/platform/x86/apple-gmux.c | 61 ++++++++++++++++++++++----------------- 1 file changed, 34 insertions(+), 27 deletions(-) (limited to 'drivers/platform') diff --git a/drivers/platform/x86/apple-gmux.c b/drivers/platform/x86/apple-gmux.c index 757fd71c900f..ad81cb1df8f2 100644 --- a/drivers/platform/x86/apple-gmux.c +++ b/drivers/platform/x86/apple-gmux.c @@ -23,6 +23,7 @@ #include #include #include +#include #include /** @@ -781,8 +782,9 @@ static int gmux_probe(struct pnp_dev *pnp, const struct pnp_device_id *id) struct apple_gmux_data *gmux_data; struct resource *res; struct backlight_properties props; - struct backlight_device *bdev; + struct backlight_device *bdev = NULL; u8 ver_major, ver_minor, ver_release; + bool register_bdev = true; int ret = -ENXIO; acpi_status status; unsigned long long gpe; @@ -859,33 +861,38 @@ get_version: props.type = BACKLIGHT_PLATFORM; props.max_brightness = gmux_read32(gmux_data, GMUX_PORT_MAX_BRIGHTNESS); - /* - * Currently it's assumed that the maximum brightness is less than - * 2^24 for compatibility with old gmux versions. Cap the max - * brightness at this value, but print a warning if the hardware - * reports something higher so that it can be fixed. - */ - if (WARN_ON(props.max_brightness > GMUX_MAX_BRIGHTNESS)) - props.max_brightness = GMUX_MAX_BRIGHTNESS; - - bdev = backlight_device_register("gmux_backlight", &pnp->dev, - gmux_data, &gmux_bl_ops, &props); - if (IS_ERR(bdev)) { - ret = PTR_ERR(bdev); - goto err_unmap; - } - - gmux_data->bdev = bdev; - bdev->props.brightness = gmux_get_brightness(bdev); - backlight_update_status(bdev); +#if IS_REACHABLE(CONFIG_ACPI_VIDEO) + register_bdev = acpi_video_get_backlight_type() == acpi_backlight_apple_gmux; +#endif + if (register_bdev) { + /* + * Currently it's assumed that the maximum brightness is less than + * 2^24 for compatibility with old gmux versions. Cap the max + * brightness at this value, but print a warning if the hardware + * reports something higher so that it can be fixed. + */ + if (WARN_ON(props.max_brightness > GMUX_MAX_BRIGHTNESS)) + props.max_brightness = GMUX_MAX_BRIGHTNESS; + + bdev = backlight_device_register("gmux_backlight", &pnp->dev, + gmux_data, &gmux_bl_ops, &props); + if (IS_ERR(bdev)) { + ret = PTR_ERR(bdev); + goto err_unmap; + } - /* - * The backlight situation on Macs is complicated. If the gmux is - * present it's the best choice, because it always works for - * backlight control and supports more levels than other options. - * Disable the other backlight choices. - */ - apple_bl_unregister(); + gmux_data->bdev = bdev; + bdev->props.brightness = gmux_get_brightness(bdev); + backlight_update_status(bdev); + + /* + * The backlight situation on Macs is complicated. If the gmux is + * present it's the best choice, because it always works for + * backlight control and supports more levels than other options. + * Disable the other backlight choices. + */ + apple_bl_unregister(); + } gmux_data->power_state = VGA_SWITCHEROO_ON; -- cgit v1.2.3 From f0f2903de50418899a5e5dcecf2906da44a7a05e Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Fri, 10 Mar 2023 15:31:13 +0300 Subject: platform/x86: apple-gmux: return -EFAULT if copy fails The copy_to/from_user() functions return the number of bytes remaining to be copied, but we want to return -EFAULT to the user. Fixes: ce3fef2eb235 ("platform/x86: apple-gmux: add debugfs interface") Signed-off-by: Dan Carpenter Reviewed-by: Orlando Chamberlain Link: https://lore.kernel.org/r/0bdfa8c2-cb22-4bec-8773-584060613043@kili.mountain Reviewed-by: Hans de Goede Signed-off-by: Hans de Goede --- drivers/platform/x86/apple-gmux.c | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) (limited to 'drivers/platform') diff --git a/drivers/platform/x86/apple-gmux.c b/drivers/platform/x86/apple-gmux.c index ad81cb1df8f2..8aa81a3517b1 100644 --- a/drivers/platform/x86/apple-gmux.c +++ b/drivers/platform/x86/apple-gmux.c @@ -694,7 +694,6 @@ static ssize_t gmux_selected_port_data_write(struct file *file, const char __user *userbuf, size_t count, loff_t *ppos) { struct apple_gmux_data *gmux_data = file->private_data; - int ret; if (*ppos) return -EINVAL; @@ -702,16 +701,16 @@ static ssize_t gmux_selected_port_data_write(struct file *file, if (count == 1) { u8 data; - ret = copy_from_user(&data, userbuf, 1); - if (ret) - return ret; + if (copy_from_user(&data, userbuf, 1)) + return -EFAULT; + gmux_write8(gmux_data, gmux_data->selected_port, data); } else if (count == 4) { u32 data; - ret = copy_from_user(&data, userbuf, 4); - if (ret) - return ret; + if (copy_from_user(&data, userbuf, 4)) + return -EFAULT; + gmux_write32(gmux_data, gmux_data->selected_port, data); } else return -EINVAL; -- cgit v1.2.3 From 3608a2cd818a8d97bd3d4dc32f6c0afaf09b15ec Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Tue, 7 Mar 2023 13:05:40 +0100 Subject: backlight: apple_bl: Use acpi_video_get_backlight_type() On some MacBooks both the apple_bl and the apple-gmux backlight drivers may be able to export a /sys/class/backlight device. To avoid having 2 backlight devices for one LCD panel until now the apple-gmux driver has been calling apple_bl_unregister() to move the apple_bl backlight device out of the way when it loads. Similar problems exist on other x86 laptops and all backlight drivers which may be used on x86 laptops have moved to using acpi_video_get_backlight_type() to determine whether they should load or not. Switch apple_bl to this model too, so that it is consistent with all the other x86 backlight drivers. Besides code-simplification and consistency this has 2 other benefits: 1) It removes a race during boot where userspace will briefly see an apple_bl backlight and then have it disappear again, leading to e.g.: https://bbs.archlinux.org/viewtopic.php?id=269920 2) This allows user to switch between the drivers by passing acpi_backlight=apple_gmux or acpi_backlight=vendor on the kernel commandline. Signed-off-by: Hans de Goede Reviewed-by: Daniel Thompson Acked-by: Lee Jones Link: https://lore.kernel.org/r/20230307120540.389920-1-hdegoede@redhat.com --- drivers/platform/x86/Kconfig | 1 - drivers/platform/x86/apple-gmux.c | 11 ----------- drivers/video/backlight/Kconfig | 1 + drivers/video/backlight/apple_bl.c | 31 ++++++++++--------------------- include/linux/apple_bl.h | 27 --------------------------- 5 files changed, 11 insertions(+), 60 deletions(-) delete mode 100644 include/linux/apple_bl.h (limited to 'drivers/platform') diff --git a/drivers/platform/x86/Kconfig b/drivers/platform/x86/Kconfig index ec7c2b4e1721..1b3189d6e8d2 100644 --- a/drivers/platform/x86/Kconfig +++ b/drivers/platform/x86/Kconfig @@ -213,7 +213,6 @@ config APPLE_GMUX depends on ACPI && PCI depends on PNP depends on BACKLIGHT_CLASS_DEVICE - depends on BACKLIGHT_APPLE=n || BACKLIGHT_APPLE help This driver provides support for the gmux device found on many Apple laptops, which controls the display mux for the hybrid diff --git a/drivers/platform/x86/apple-gmux.c b/drivers/platform/x86/apple-gmux.c index 8aa81a3517b1..4c311e1dedad 100644 --- a/drivers/platform/x86/apple-gmux.c +++ b/drivers/platform/x86/apple-gmux.c @@ -16,7 +16,6 @@ #include #include #include -#include #include #include #include @@ -883,14 +882,6 @@ get_version: gmux_data->bdev = bdev; bdev->props.brightness = gmux_get_brightness(bdev); backlight_update_status(bdev); - - /* - * The backlight situation on Macs is complicated. If the gmux is - * present it's the best choice, because it always works for - * backlight control and supports more levels than other options. - * Disable the other backlight choices. - */ - apple_bl_unregister(); } gmux_data->power_state = VGA_SWITCHEROO_ON; @@ -1007,8 +998,6 @@ static void gmux_remove(struct pnp_dev *pnp) release_region(gmux_data->iostart, gmux_data->iolen); apple_gmux_data = NULL; kfree(gmux_data); - - apple_bl_register(); } static const struct pnp_device_id gmux_device_ids[] = { diff --git a/drivers/video/backlight/Kconfig b/drivers/video/backlight/Kconfig index 4c33e971c0f0..51387b1ef012 100644 --- a/drivers/video/backlight/Kconfig +++ b/drivers/video/backlight/Kconfig @@ -285,6 +285,7 @@ config BACKLIGHT_MT6370 config BACKLIGHT_APPLE tristate "Apple Backlight Driver" depends on X86 && ACPI + depends on ACPI_VIDEO=n || ACPI_VIDEO help If you have an Intel-based Apple say Y to enable a driver for its backlight. diff --git a/drivers/video/backlight/apple_bl.c b/drivers/video/backlight/apple_bl.c index e9e7acb577bf..aaa824437a2a 100644 --- a/drivers/video/backlight/apple_bl.c +++ b/drivers/video/backlight/apple_bl.c @@ -24,7 +24,7 @@ #include #include #include -#include +#include static struct backlight_device *apple_backlight_device; @@ -215,32 +215,21 @@ static struct acpi_driver apple_bl_driver = { }, }; -static atomic_t apple_bl_registered = ATOMIC_INIT(0); - -int apple_bl_register(void) -{ - if (atomic_xchg(&apple_bl_registered, 1) == 0) - return acpi_bus_register_driver(&apple_bl_driver); - - return 0; -} -EXPORT_SYMBOL_GPL(apple_bl_register); - -void apple_bl_unregister(void) -{ - if (atomic_xchg(&apple_bl_registered, 0) == 1) - acpi_bus_unregister_driver(&apple_bl_driver); -} -EXPORT_SYMBOL_GPL(apple_bl_unregister); - static int __init apple_bl_init(void) { - return apple_bl_register(); + /* + * Use ACPI video detection code to see if this driver should register + * or if another driver, e.g. the apple-gmux driver should be used. + */ + if (acpi_video_get_backlight_type() != acpi_backlight_vendor) + return -ENODEV; + + return acpi_bus_register_driver(&apple_bl_driver); } static void __exit apple_bl_exit(void) { - apple_bl_unregister(); + acpi_bus_unregister_driver(&apple_bl_driver); } module_init(apple_bl_init); diff --git a/include/linux/apple_bl.h b/include/linux/apple_bl.h deleted file mode 100644 index 445af2e3cc21..000000000000 --- a/include/linux/apple_bl.h +++ /dev/null @@ -1,27 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 */ -/* - * apple_bl exported symbols - */ - -#ifndef _LINUX_APPLE_BL_H -#define _LINUX_APPLE_BL_H - -#if defined(CONFIG_BACKLIGHT_APPLE) || defined(CONFIG_BACKLIGHT_APPLE_MODULE) - -extern int apple_bl_register(void); -extern void apple_bl_unregister(void); - -#else /* !CONFIG_BACKLIGHT_APPLE */ - -static inline int apple_bl_register(void) -{ - return 0; -} - -static inline void apple_bl_unregister(void) -{ -} - -#endif /* !CONFIG_BACKLIGHT_APPLE */ - -#endif /* _LINUX_APPLE_BL_H */ -- cgit v1.2.3 From b59018c14c1efe36ddc96f3d27308f18e7d1b03e Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Thu, 9 Mar 2023 10:40:35 +0100 Subject: platform/x86: x86-android-tablets: Add depends on PMIC_OPREGION Add a depends on PMIC_OPREGION to x86-android-tablets Kconfig to fix the following build error: ERROR: modpost: "intel_soc_pmic_exec_mipi_pmic_seq_element" [drivers/platform/x86/x86-android-tablets/x86-android-tablets.ko] undefined! Fixes: 9b1d2662b8c5 ("platform/x86: x86-android-tablets: Add touchscreen support for Lenovo Yoga Tab 3 Pro YT3-X90F") Reported-by: kernel test robot Link: https://lore.kernel.org/oe-kbuild-all/202303091711.howZNrIY-lkp@intel.com/ Signed-off-by: Hans de Goede Link: https://lore.kernel.org/r/20230309094035.18736-1-hdegoede@redhat.com --- drivers/platform/x86/x86-android-tablets/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/platform') diff --git a/drivers/platform/x86/x86-android-tablets/Kconfig b/drivers/platform/x86/x86-android-tablets/Kconfig index 2b3daca5380b..6603461d4273 100644 --- a/drivers/platform/x86/x86-android-tablets/Kconfig +++ b/drivers/platform/x86/x86-android-tablets/Kconfig @@ -5,7 +5,7 @@ config X86_ANDROID_TABLETS tristate "X86 Android tablet support" - depends on I2C && SPI && SERIAL_DEV_BUS && ACPI && EFI && GPIOLIB + depends on I2C && SPI && SERIAL_DEV_BUS && ACPI && EFI && GPIOLIB && PMIC_OPREGION help X86 tablets which ship with Android as (part of) the factory image typically have various problems with their DSDTs. The factory kernels -- cgit v1.2.3 From 94227b9c88f3df8ac3025611719cebd125b5981a Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Fri, 3 Mar 2023 23:19:28 +0100 Subject: platform/x86: Add intel_bytcrc_pwrsrc driver Add a new driver for the power-, wake- and reset-source functionality of the Bay Trail (BYT) version of the Crystal Cove PMIC. The main functionality here is detecting which power-sources (USB / DC in / battery) are active. This is normally exposed to userspace as a power_supply class charger device with an online sysfs attribute. But if a charger is online or not is already exposed on BYT-CRC devices through either an ACPI AC power_supply device, or through a native driver for the battery charger chip (e.g. a BQ24292i). So instead of adding duplicate info under the power_supply class this driver exports the info through debugfs and likewise adds debugfs files for the reset- and wake-source info / registers. Despite this driver only exporting debugfs bits it is still useful to have this driver because it clears the wake- and reset-source registers after reading them. Not clearing these can have undesirable side-effects. Specifically if the WAKESRC register contains 0x01 (wake by powerbutton) on reboot then the firmware on some tablets turns the reboot into a poweroff. I guess this may be necessary to make long power-presses turn into a poweroff somehow? Signed-off-by: Hans de Goede Link: https://lore.kernel.org/r/20230303221928.285477-1-hdegoede@redhat.com --- drivers/platform/x86/intel/Kconfig | 10 ++ drivers/platform/x86/intel/Makefile | 2 + drivers/platform/x86/intel/bytcrc_pwrsrc.c | 181 +++++++++++++++++++++++++++++ 3 files changed, 193 insertions(+) create mode 100644 drivers/platform/x86/intel/bytcrc_pwrsrc.c (limited to 'drivers/platform') diff --git a/drivers/platform/x86/intel/Kconfig b/drivers/platform/x86/intel/Kconfig index bbbd9e54e9ee..e9dc0c021029 100644 --- a/drivers/platform/x86/intel/Kconfig +++ b/drivers/platform/x86/intel/Kconfig @@ -80,6 +80,16 @@ config INTEL_BXTWC_PMIC_TMU This driver enables the alarm wakeup functionality in the TMU unit of Whiskey Cove PMIC. +config INTEL_BYTCRC_PWRSRC + tristate "Intel Bay Trail Crystal Cove power source driver" + depends on INTEL_SOC_PMIC + help + This option adds a power source driver for Crystal Cove PMICs + on Intel Bay Trail devices. + + To compile this driver as a module, choose M here: the module + will be called intel_bytcrc_pwrsrc. + config INTEL_CHTDC_TI_PWRBTN tristate "Intel Cherry Trail Dollar Cove TI power button driver" depends on INTEL_SOC_PMIC_CHTDC_TI diff --git a/drivers/platform/x86/intel/Makefile b/drivers/platform/x86/intel/Makefile index 411df4040427..c1d5fe05e3f3 100644 --- a/drivers/platform/x86/intel/Makefile +++ b/drivers/platform/x86/intel/Makefile @@ -38,6 +38,8 @@ intel_bxtwc_tmu-y := bxtwc_tmu.o obj-$(CONFIG_INTEL_BXTWC_PMIC_TMU) += intel_bxtwc_tmu.o intel_crystal_cove_charger-y := crystal_cove_charger.o obj-$(CONFIG_X86_ANDROID_TABLETS) += intel_crystal_cove_charger.o +intel_bytcrc_pwrsrc-y := bytcrc_pwrsrc.o +obj-$(CONFIG_INTEL_BYTCRC_PWRSRC) += intel_bytcrc_pwrsrc.o intel_chtdc_ti_pwrbtn-y := chtdc_ti_pwrbtn.o obj-$(CONFIG_INTEL_CHTDC_TI_PWRBTN) += intel_chtdc_ti_pwrbtn.o intel_chtwc_int33fe-y := chtwc_int33fe.o diff --git a/drivers/platform/x86/intel/bytcrc_pwrsrc.c b/drivers/platform/x86/intel/bytcrc_pwrsrc.c new file mode 100644 index 000000000000..8a022b90d12d --- /dev/null +++ b/drivers/platform/x86/intel/bytcrc_pwrsrc.c @@ -0,0 +1,181 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Power-source driver for Bay Trail Crystal Cove PMIC + * + * Copyright (c) 2023 Hans de Goede + * + * Based on intel_crystalcove_pwrsrc.c from Android kernel sources, which is: + * Copyright (C) 2013 Intel Corporation + */ + +#include +#include +#include +#include +#include + +#define CRYSTALCOVE_SPWRSRC_REG 0x1E +#define CRYSTALCOVE_RESETSRC0_REG 0x20 +#define CRYSTALCOVE_RESETSRC1_REG 0x21 +#define CRYSTALCOVE_WAKESRC_REG 0x22 + +struct crc_pwrsrc_data { + struct regmap *regmap; + struct dentry *debug_dentry; + unsigned int resetsrc0; + unsigned int resetsrc1; + unsigned int wakesrc; +}; + +static const char * const pwrsrc_pwrsrc_info[] = { + /* bit 0 */ "USB", + /* bit 1 */ "DC in", + /* bit 2 */ "Battery", + NULL, +}; + +static const char * const pwrsrc_resetsrc0_info[] = { + /* bit 0 */ "SOC reporting a thermal event", + /* bit 1 */ "critical PMIC temperature", + /* bit 2 */ "critical system temperature", + /* bit 3 */ "critical battery temperature", + /* bit 4 */ "VSYS under voltage", + /* bit 5 */ "VSYS over voltage", + /* bit 6 */ "battery removal", + NULL, +}; + +static const char * const pwrsrc_resetsrc1_info[] = { + /* bit 0 */ "VCRIT threshold", + /* bit 1 */ "BATID reporting battery removal", + /* bit 2 */ "user pressing the power button", + NULL, +}; + +static const char * const pwrsrc_wakesrc_info[] = { + /* bit 0 */ "user pressing the power button", + /* bit 1 */ "a battery insertion", + /* bit 2 */ "a USB charger insertion", + /* bit 3 */ "an adapter insertion", + NULL, +}; + +static void crc_pwrsrc_log(struct seq_file *seq, const char *prefix, + const char * const *info, unsigned int reg_val) +{ + int i; + + for (i = 0; info[i]; i++) { + if (reg_val & BIT(i)) + seq_printf(seq, "%s by %s\n", prefix, info[i]); + } +} + +static int pwrsrc_show(struct seq_file *seq, void *unused) +{ + struct crc_pwrsrc_data *data = seq->private; + unsigned int reg_val; + int ret; + + ret = regmap_read(data->regmap, CRYSTALCOVE_SPWRSRC_REG, ®_val); + if (ret) + return ret; + + crc_pwrsrc_log(seq, "System powered", pwrsrc_pwrsrc_info, reg_val); + return 0; +} + +static int resetsrc_show(struct seq_file *seq, void *unused) +{ + struct crc_pwrsrc_data *data = seq->private; + + crc_pwrsrc_log(seq, "Last shutdown caused", pwrsrc_resetsrc0_info, data->resetsrc0); + crc_pwrsrc_log(seq, "Last shutdown caused", pwrsrc_resetsrc1_info, data->resetsrc1); + return 0; +} + +static int wakesrc_show(struct seq_file *seq, void *unused) +{ + struct crc_pwrsrc_data *data = seq->private; + + crc_pwrsrc_log(seq, "Last wake caused", pwrsrc_wakesrc_info, data->wakesrc); + return 0; +} + +DEFINE_SHOW_ATTRIBUTE(pwrsrc); +DEFINE_SHOW_ATTRIBUTE(resetsrc); +DEFINE_SHOW_ATTRIBUTE(wakesrc); + +static int crc_pwrsrc_read_and_clear(struct crc_pwrsrc_data *data, + unsigned int reg, unsigned int *val) +{ + int ret; + + ret = regmap_read(data->regmap, reg, val); + if (ret) + return ret; + + return regmap_write(data->regmap, reg, *val); +} + +static int crc_pwrsrc_probe(struct platform_device *pdev) +{ + struct intel_soc_pmic *pmic = dev_get_drvdata(pdev->dev.parent); + struct crc_pwrsrc_data *data; + int ret; + + data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL); + if (!data) + return -ENOMEM; + + data->regmap = pmic->regmap; + + /* + * Read + clear resetsrc0/1 and wakesrc now, so that they get + * cleared even if the debugfs interface is never used. + * + * Properly clearing the wakesrc is important, leaving bit 0 of it + * set turns reboot into poweroff on some tablets. + */ + ret = crc_pwrsrc_read_and_clear(data, CRYSTALCOVE_RESETSRC0_REG, &data->resetsrc0); + if (ret) + return ret; + + ret = crc_pwrsrc_read_and_clear(data, CRYSTALCOVE_RESETSRC1_REG, &data->resetsrc1); + if (ret) + return ret; + + ret = crc_pwrsrc_read_and_clear(data, CRYSTALCOVE_WAKESRC_REG, &data->wakesrc); + if (ret) + return ret; + + data->debug_dentry = debugfs_create_dir(KBUILD_MODNAME, NULL); + debugfs_create_file("pwrsrc", 0444, data->debug_dentry, data, &pwrsrc_fops); + debugfs_create_file("resetsrc", 0444, data->debug_dentry, data, &resetsrc_fops); + debugfs_create_file("wakesrc", 0444, data->debug_dentry, data, &wakesrc_fops); + + platform_set_drvdata(pdev, data); + return 0; +} + +static int crc_pwrsrc_remove(struct platform_device *pdev) +{ + struct crc_pwrsrc_data *data = platform_get_drvdata(pdev); + + debugfs_remove_recursive(data->debug_dentry); + return 0; +} + +static struct platform_driver crc_pwrsrc_driver = { + .probe = crc_pwrsrc_probe, + .remove = crc_pwrsrc_remove, + .driver = { + .name = "crystal_cove_pwrsrc", + }, +}; +module_platform_driver(crc_pwrsrc_driver); + +MODULE_ALIAS("platform:crystal_cove_pwrsrc"); +MODULE_AUTHOR("Hans de Goede "); +MODULE_DESCRIPTION("Power-source driver for Bay Trail Crystal Cove PMIC"); +MODULE_LICENSE("GPL"); -- cgit v1.2.3 From b58a444d7f28ea59fd37a4615e6be86f5d45c880 Mon Sep 17 00:00:00 2001 From: Maximilian Luz Date: Sat, 4 Mar 2023 20:46:09 +0100 Subject: platform/surface: aggregator_tabletsw: Properly handle different posture source IDs The device posture subsystem (POS) can provide different posture sources. Different sources can provide different posture states and sources can be identified by their ID. For example, screen posture of the Surface Laptop Studio (SLS), which is currently the only supported source, uses a source ID of 0x03. The Surface Pro 9 uses the same subsystem for its Type-Cover, however, provides different states for that under the ID 0x00. To eventually support the Surface Pro 9 and potential future devices, we need to properly disambiguate between source IDs. Therefore, add the source ID to the state we carry and determine the tablet-mode state (as well as state names) based on that. Signed-off-by: Maximilian Luz Link: https://lore.kernel.org/r/20230304194611.87770-2-luzmaximilian@gmail.com Reviewed-by: Hans de Goede Signed-off-by: Hans de Goede --- .../platform/surface/surface_aggregator_tabletsw.c | 123 ++++++++++++++------- 1 file changed, 84 insertions(+), 39 deletions(-) (limited to 'drivers/platform') diff --git a/drivers/platform/surface/surface_aggregator_tabletsw.c b/drivers/platform/surface/surface_aggregator_tabletsw.c index 9fed800c7cc0..e8682f52558f 100644 --- a/drivers/platform/surface/surface_aggregator_tabletsw.c +++ b/drivers/platform/surface/surface_aggregator_tabletsw.c @@ -20,16 +20,23 @@ struct ssam_tablet_sw; +struct ssam_tablet_sw_state { + u32 source; + u32 state; +}; + struct ssam_tablet_sw_ops { - int (*get_state)(struct ssam_tablet_sw *sw, u32 *state); - const char *(*state_name)(struct ssam_tablet_sw *sw, u32 state); - bool (*state_is_tablet_mode)(struct ssam_tablet_sw *sw, u32 state); + int (*get_state)(struct ssam_tablet_sw *sw, struct ssam_tablet_sw_state *state); + const char *(*state_name)(struct ssam_tablet_sw *sw, + const struct ssam_tablet_sw_state *state); + bool (*state_is_tablet_mode)(struct ssam_tablet_sw *sw, + const struct ssam_tablet_sw_state *state); }; struct ssam_tablet_sw { struct ssam_device *sdev; - u32 state; + struct ssam_tablet_sw_state state; struct work_struct update_work; struct input_dev *mode_switch; @@ -45,9 +52,11 @@ struct ssam_tablet_sw_desc { struct { u32 (*notify)(struct ssam_event_notifier *nf, const struct ssam_event *event); - int (*get_state)(struct ssam_tablet_sw *sw, u32 *state); - const char *(*state_name)(struct ssam_tablet_sw *sw, u32 state); - bool (*state_is_tablet_mode)(struct ssam_tablet_sw *sw, u32 state); + int (*get_state)(struct ssam_tablet_sw *sw, struct ssam_tablet_sw_state *state); + const char *(*state_name)(struct ssam_tablet_sw *sw, + const struct ssam_tablet_sw_state *state); + bool (*state_is_tablet_mode)(struct ssam_tablet_sw *sw, + const struct ssam_tablet_sw_state *state); } ops; struct { @@ -61,7 +70,7 @@ struct ssam_tablet_sw_desc { static ssize_t state_show(struct device *dev, struct device_attribute *attr, char *buf) { struct ssam_tablet_sw *sw = dev_get_drvdata(dev); - const char *state = sw->ops.state_name(sw, sw->state); + const char *state = sw->ops.state_name(sw, &sw->state); return sysfs_emit(buf, "%s\n", state); } @@ -79,19 +88,19 @@ static const struct attribute_group ssam_tablet_sw_group = { static void ssam_tablet_sw_update_workfn(struct work_struct *work) { struct ssam_tablet_sw *sw = container_of(work, struct ssam_tablet_sw, update_work); + struct ssam_tablet_sw_state state; int tablet, status; - u32 state; status = sw->ops.get_state(sw, &state); if (status) return; - if (sw->state == state) + if (sw->state.source == state.source && sw->state.state == state.state) return; sw->state = state; /* Send SW_TABLET_MODE event. */ - tablet = sw->ops.state_is_tablet_mode(sw, state); + tablet = sw->ops.state_is_tablet_mode(sw, &state); input_report_switch(sw->mode_switch, SW_TABLET_MODE, tablet); input_sync(sw->mode_switch); } @@ -146,7 +155,7 @@ static int ssam_tablet_sw_probe(struct ssam_device *sdev) sw->mode_switch->id.bustype = BUS_HOST; sw->mode_switch->dev.parent = &sdev->dev; - tablet = sw->ops.state_is_tablet_mode(sw, sw->state); + tablet = sw->ops.state_is_tablet_mode(sw, &sw->state); input_set_capability(sw->mode_switch, EV_SW, SW_TABLET_MODE); input_report_switch(sw->mode_switch, SW_TABLET_MODE, tablet); @@ -203,9 +212,10 @@ enum ssam_kip_cover_state { SSAM_KIP_COVER_STATE_FOLDED_BACK = 0x05, }; -static const char *ssam_kip_cover_state_name(struct ssam_tablet_sw *sw, u32 state) +static const char *ssam_kip_cover_state_name(struct ssam_tablet_sw *sw, + const struct ssam_tablet_sw_state *state) { - switch (state) { + switch (state->state) { case SSAM_KIP_COVER_STATE_DISCONNECTED: return "disconnected"; @@ -222,14 +232,15 @@ static const char *ssam_kip_cover_state_name(struct ssam_tablet_sw *sw, u32 stat return "folded-back"; default: - dev_warn(&sw->sdev->dev, "unknown KIP cover state: %u\n", state); + dev_warn(&sw->sdev->dev, "unknown KIP cover state: %u\n", state->state); return ""; } } -static bool ssam_kip_cover_state_is_tablet_mode(struct ssam_tablet_sw *sw, u32 state) +static bool ssam_kip_cover_state_is_tablet_mode(struct ssam_tablet_sw *sw, + const struct ssam_tablet_sw_state *state) { - switch (state) { + switch (state->state) { case SSAM_KIP_COVER_STATE_DISCONNECTED: case SSAM_KIP_COVER_STATE_FOLDED_CANVAS: case SSAM_KIP_COVER_STATE_FOLDED_BACK: @@ -240,7 +251,7 @@ static bool ssam_kip_cover_state_is_tablet_mode(struct ssam_tablet_sw *sw, u32 s return false; default: - dev_warn(&sw->sdev->dev, "unknown KIP cover state: %d\n", sw->state); + dev_warn(&sw->sdev->dev, "unknown KIP cover state: %d\n", state->state); return true; } } @@ -252,7 +263,7 @@ SSAM_DEFINE_SYNC_REQUEST_R(__ssam_kip_get_cover_state, u8, { .instance_id = 0x00, }); -static int ssam_kip_get_cover_state(struct ssam_tablet_sw *sw, u32 *state) +static int ssam_kip_get_cover_state(struct ssam_tablet_sw *sw, struct ssam_tablet_sw_state *state) { int status; u8 raw; @@ -263,7 +274,8 @@ static int ssam_kip_get_cover_state(struct ssam_tablet_sw *sw, u32 *state) return status; } - *state = raw; + state->source = 0; /* Unused for KIP switch. */ + state->state = raw; return 0; } @@ -312,11 +324,15 @@ MODULE_PARM_DESC(tablet_mode_in_slate_state, "Enable tablet mode in slate device #define SSAM_EVENT_POS_CID_POSTURE_CHANGED 0x03 #define SSAM_POS_MAX_SOURCES 4 -enum ssam_pos_state { - SSAM_POS_POSTURE_LID_CLOSED = 0x00, - SSAM_POS_POSTURE_LAPTOP = 0x01, - SSAM_POS_POSTURE_SLATE = 0x02, - SSAM_POS_POSTURE_TABLET = 0x03, +enum ssam_pos_source_id { + SSAM_POS_SOURCE_SLS = 0x03, +}; + +enum ssam_pos_state_sls { + SSAM_POS_SLS_LID_CLOSED = 0x00, + SSAM_POS_SLS_LAPTOP = 0x01, + SSAM_POS_SLS_SLATE = 0x02, + SSAM_POS_SLS_TABLET = 0x03, }; struct ssam_sources_list { @@ -324,42 +340,68 @@ struct ssam_sources_list { __le32 id[SSAM_POS_MAX_SOURCES]; } __packed; -static const char *ssam_pos_state_name(struct ssam_tablet_sw *sw, u32 state) +static const char *ssam_pos_state_name_sls(struct ssam_tablet_sw *sw, u32 state) { switch (state) { - case SSAM_POS_POSTURE_LID_CLOSED: + case SSAM_POS_SLS_LID_CLOSED: return "closed"; - case SSAM_POS_POSTURE_LAPTOP: + case SSAM_POS_SLS_LAPTOP: return "laptop"; - case SSAM_POS_POSTURE_SLATE: + case SSAM_POS_SLS_SLATE: return "slate"; - case SSAM_POS_POSTURE_TABLET: + case SSAM_POS_SLS_TABLET: return "tablet"; default: - dev_warn(&sw->sdev->dev, "unknown device posture: %u\n", state); + dev_warn(&sw->sdev->dev, "unknown device posture for SLS: %u\n", state); return ""; } } -static bool ssam_pos_state_is_tablet_mode(struct ssam_tablet_sw *sw, u32 state) +static const char *ssam_pos_state_name(struct ssam_tablet_sw *sw, + const struct ssam_tablet_sw_state *state) +{ + switch (state->source) { + case SSAM_POS_SOURCE_SLS: + return ssam_pos_state_name_sls(sw, state->state); + + default: + dev_warn(&sw->sdev->dev, "unknown device posture source: %u\n", state->source); + return ""; + } +} + +static bool ssam_pos_state_is_tablet_mode_sls(struct ssam_tablet_sw *sw, u32 state) { switch (state) { - case SSAM_POS_POSTURE_LAPTOP: - case SSAM_POS_POSTURE_LID_CLOSED: + case SSAM_POS_SLS_LAPTOP: + case SSAM_POS_SLS_LID_CLOSED: return false; - case SSAM_POS_POSTURE_SLATE: + case SSAM_POS_SLS_SLATE: return tablet_mode_in_slate_state; - case SSAM_POS_POSTURE_TABLET: + case SSAM_POS_SLS_TABLET: return true; default: - dev_warn(&sw->sdev->dev, "unknown device posture: %u\n", state); + dev_warn(&sw->sdev->dev, "unknown device posture for SLS: %u\n", state); + return true; + } +} + +static bool ssam_pos_state_is_tablet_mode(struct ssam_tablet_sw *sw, + const struct ssam_tablet_sw_state *state) +{ + switch (state->source) { + case SSAM_POS_SOURCE_SLS: + return ssam_pos_state_is_tablet_mode_sls(sw, state->state); + + default: + dev_warn(&sw->sdev->dev, "unknown device posture source: %u\n", state->source); return true; } } @@ -450,9 +492,10 @@ static int ssam_pos_get_posture_for_source(struct ssam_tablet_sw *sw, u32 source return 0; } -static int ssam_pos_get_posture(struct ssam_tablet_sw *sw, u32 *state) +static int ssam_pos_get_posture(struct ssam_tablet_sw *sw, struct ssam_tablet_sw_state *state) { u32 source_id; + u32 source_state; int status; status = ssam_pos_get_source(sw, &source_id); @@ -461,13 +504,15 @@ static int ssam_pos_get_posture(struct ssam_tablet_sw *sw, u32 *state) return status; } - status = ssam_pos_get_posture_for_source(sw, source_id, state); + status = ssam_pos_get_posture_for_source(sw, source_id, &source_state); if (status) { dev_err(&sw->sdev->dev, "failed to get posture value for source %u: %d\n", source_id, status); return status; } + state->source = source_id; + state->state = source_state; return 0; } -- cgit v1.2.3 From 37ff64cd81ff0abbe901fd4cf401998d534c1cd7 Mon Sep 17 00:00:00 2001 From: Maximilian Luz Date: Sat, 4 Mar 2023 20:46:10 +0100 Subject: platform/surface: aggregator_tabletsw: Add support for Type-Cover posture source Implement support for the Type-Cover posture source (ID 0x00), found on the Surface Pro 9. Signed-off-by: Maximilian Luz Link: https://lore.kernel.org/r/20230304194611.87770-3-luzmaximilian@gmail.com Reviewed-by: Hans de Goede Signed-off-by: Hans de Goede --- .../platform/surface/surface_aggregator_tabletsw.c | 57 ++++++++++++++++++++++ 1 file changed, 57 insertions(+) (limited to 'drivers/platform') diff --git a/drivers/platform/surface/surface_aggregator_tabletsw.c b/drivers/platform/surface/surface_aggregator_tabletsw.c index e8682f52558f..8f52b62d1c19 100644 --- a/drivers/platform/surface/surface_aggregator_tabletsw.c +++ b/drivers/platform/surface/surface_aggregator_tabletsw.c @@ -325,9 +325,18 @@ MODULE_PARM_DESC(tablet_mode_in_slate_state, "Enable tablet mode in slate device #define SSAM_POS_MAX_SOURCES 4 enum ssam_pos_source_id { + SSAM_POS_SOURCE_COVER = 0x00, SSAM_POS_SOURCE_SLS = 0x03, }; +enum ssam_pos_state_cover { + SSAM_POS_COVER_DISCONNECTED = 0x01, + SSAM_POS_COVER_CLOSED = 0x02, + SSAM_POS_COVER_LAPTOP = 0x03, + SSAM_POS_COVER_FOLDED_CANVAS = 0x04, + SSAM_POS_COVER_FOLDED_BACK = 0x05, +}; + enum ssam_pos_state_sls { SSAM_POS_SLS_LID_CLOSED = 0x00, SSAM_POS_SLS_LAPTOP = 0x01, @@ -340,6 +349,30 @@ struct ssam_sources_list { __le32 id[SSAM_POS_MAX_SOURCES]; } __packed; +static const char *ssam_pos_state_name_cover(struct ssam_tablet_sw *sw, u32 state) +{ + switch (state) { + case SSAM_POS_COVER_DISCONNECTED: + return "disconnected"; + + case SSAM_POS_COVER_CLOSED: + return "closed"; + + case SSAM_POS_COVER_LAPTOP: + return "laptop"; + + case SSAM_POS_COVER_FOLDED_CANVAS: + return "folded-canvas"; + + case SSAM_POS_COVER_FOLDED_BACK: + return "folded-back"; + + default: + dev_warn(&sw->sdev->dev, "unknown device posture for type-cover: %u\n", state); + return ""; + } +} + static const char *ssam_pos_state_name_sls(struct ssam_tablet_sw *sw, u32 state) { switch (state) { @@ -365,6 +398,9 @@ static const char *ssam_pos_state_name(struct ssam_tablet_sw *sw, const struct ssam_tablet_sw_state *state) { switch (state->source) { + case SSAM_POS_SOURCE_COVER: + return ssam_pos_state_name_cover(sw, state->state); + case SSAM_POS_SOURCE_SLS: return ssam_pos_state_name_sls(sw, state->state); @@ -374,6 +410,24 @@ static const char *ssam_pos_state_name(struct ssam_tablet_sw *sw, } } +static bool ssam_pos_state_is_tablet_mode_cover(struct ssam_tablet_sw *sw, u32 state) +{ + switch (state) { + case SSAM_POS_COVER_DISCONNECTED: + case SSAM_POS_COVER_FOLDED_CANVAS: + case SSAM_POS_COVER_FOLDED_BACK: + return true; + + case SSAM_POS_COVER_CLOSED: + case SSAM_POS_COVER_LAPTOP: + return false; + + default: + dev_warn(&sw->sdev->dev, "unknown device posture for type-cover: %u\n", state); + return true; + } +} + static bool ssam_pos_state_is_tablet_mode_sls(struct ssam_tablet_sw *sw, u32 state) { switch (state) { @@ -397,6 +451,9 @@ static bool ssam_pos_state_is_tablet_mode(struct ssam_tablet_sw *sw, const struct ssam_tablet_sw_state *state) { switch (state->source) { + case SSAM_POS_SOURCE_COVER: + return ssam_pos_state_is_tablet_mode_cover(sw, state->state); + case SSAM_POS_SOURCE_SLS: return ssam_pos_state_is_tablet_mode_sls(sw, state->state); -- cgit v1.2.3 From 39e2ba648a6ee4519a5f0aa4cb995f8c97c5db6f Mon Sep 17 00:00:00 2001 From: Maximilian Luz Date: Sat, 4 Mar 2023 20:46:11 +0100 Subject: platform/surface: aggregator_registry: Add support for tablet-mode switch on Surface Pro 9 Add support for the POS-subsystem tablet-mode switch used on the Surface Pro 9. Signed-off-by: Maximilian Luz Link: https://lore.kernel.org/r/20230304194611.87770-4-luzmaximilian@gmail.com Reviewed-by: Hans de Goede Signed-off-by: Hans de Goede --- drivers/platform/surface/surface_aggregator_registry.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/platform') diff --git a/drivers/platform/surface/surface_aggregator_registry.c b/drivers/platform/surface/surface_aggregator_registry.c index 296f72d52e6a..0fe5be539652 100644 --- a/drivers/platform/surface/surface_aggregator_registry.c +++ b/drivers/platform/surface/surface_aggregator_registry.c @@ -305,7 +305,7 @@ static const struct software_node *ssam_node_group_sp9[] = { &ssam_node_bat_ac, &ssam_node_bat_main, &ssam_node_tmp_pprof, - /* TODO: Tablet mode switch (via POS subsystem) */ + &ssam_node_pos_tablet_switch, &ssam_node_hid_kip_keyboard, &ssam_node_hid_kip_penstash, &ssam_node_hid_kip_touchpad, -- cgit v1.2.3 From 3ed0b880b71489d59f89b0bc1323ad34a136cca0 Mon Sep 17 00:00:00 2001 From: Koba Ko Date: Wed, 8 Mar 2023 14:24:14 +0800 Subject: platform/x86: dell-laptop: Register ctl-led for speaker-mute Some platforms have the speaker-mute led and current driver doesn't control it. If the platform support the control of speaker-mute led, register it Signed-off-by: Koba Ko Link: https://lore.kernel.org/r/20230308062414.1048913-1-koba.ko@canonical.com Reviewed-by: Hans de Goede Signed-off-by: Hans de Goede --- drivers/platform/x86/dell/dell-laptop.c | 42 +++++++++++++++++++++++++++++++++ drivers/platform/x86/dell/dell-smbios.h | 2 ++ 2 files changed, 44 insertions(+) (limited to 'drivers/platform') diff --git a/drivers/platform/x86/dell/dell-laptop.c b/drivers/platform/x86/dell/dell-laptop.c index e92c3ad06d69..6586438356de 100644 --- a/drivers/platform/x86/dell/dell-laptop.c +++ b/drivers/platform/x86/dell/dell-laptop.c @@ -97,6 +97,7 @@ static struct rfkill *bluetooth_rfkill; static struct rfkill *wwan_rfkill; static bool force_rfkill; static bool micmute_led_registered; +static bool mute_led_registered; module_param(force_rfkill, bool, 0444); MODULE_PARM_DESC(force_rfkill, "enable rfkill on non whitelisted models"); @@ -2177,6 +2178,34 @@ static struct led_classdev micmute_led_cdev = { .default_trigger = "audio-micmute", }; +static int mute_led_set(struct led_classdev *led_cdev, + enum led_brightness brightness) +{ + struct calling_interface_buffer buffer; + struct calling_interface_token *token; + int state = brightness != LED_OFF; + + if (state == 0) + token = dell_smbios_find_token(GLOBAL_MUTE_DISABLE); + else + token = dell_smbios_find_token(GLOBAL_MUTE_ENABLE); + + if (!token) + return -ENODEV; + + dell_fill_request(&buffer, token->location, token->value, 0, 0); + dell_send_request(&buffer, CLASS_TOKEN_WRITE, SELECT_TOKEN_STD); + + return 0; +} + +static struct led_classdev mute_led_cdev = { + .name = "platform::mute", + .max_brightness = 1, + .brightness_set_blocking = mute_led_set, + .default_trigger = "audio-mute", +}; + static int __init dell_init(void) { struct calling_interface_token *token; @@ -2230,6 +2259,15 @@ static int __init dell_init(void) micmute_led_registered = true; } + if (dell_smbios_find_token(GLOBAL_MUTE_DISABLE) && + dell_smbios_find_token(GLOBAL_MUTE_ENABLE)) { + mute_led_cdev.brightness = ledtrig_audio_get(LED_AUDIO_MUTE); + ret = led_classdev_register(&platform_device->dev, &mute_led_cdev); + if (ret < 0) + goto fail_backlight; + mute_led_registered = true; + } + if (acpi_video_get_backlight_type() != acpi_backlight_vendor) return 0; @@ -2277,6 +2315,8 @@ fail_get_brightness: fail_backlight: if (micmute_led_registered) led_classdev_unregister(&micmute_led_cdev); + if (mute_led_registered) + led_classdev_unregister(&mute_led_cdev); fail_led: dell_cleanup_rfkill(); fail_rfkill: @@ -2299,6 +2339,8 @@ static void __exit dell_exit(void) backlight_device_unregister(dell_backlight_device); if (micmute_led_registered) led_classdev_unregister(&micmute_led_cdev); + if (mute_led_registered) + led_classdev_unregister(&mute_led_cdev); dell_cleanup_rfkill(); if (platform_device) { platform_device_unregister(platform_device); diff --git a/drivers/platform/x86/dell/dell-smbios.h b/drivers/platform/x86/dell/dell-smbios.h index 75fa8ea0476d..eb341bf000c6 100644 --- a/drivers/platform/x86/dell/dell-smbios.h +++ b/drivers/platform/x86/dell/dell-smbios.h @@ -34,6 +34,8 @@ #define KBD_LED_AUTO_100_TOKEN 0x02F6 #define GLOBAL_MIC_MUTE_ENABLE 0x0364 #define GLOBAL_MIC_MUTE_DISABLE 0x0365 +#define GLOBAL_MUTE_ENABLE 0x058C +#define GLOBAL_MUTE_DISABLE 0x058D struct notifier_block; -- cgit v1.2.3 From e6d3418130cdf718eff72db9a05d7262ebb49cff Mon Sep 17 00:00:00 2001 From: Srinivas Pandruvada Date: Tue, 7 Mar 2023 23:06:35 -0800 Subject: platform/x86: ISST: Add support for MSR 0x54 To map Linux CPU numbering scheme to hardware CPU numbering scheme MSR 0x53 is getting used. But for new generation of CPUs, this MSR is not valid. Since this is model specific MSR, this is possible. A new MSR 0x54 is defined for this purpose. User space can use the API version to distinguish format from MSR 0x53. Intel speed select utility is updated to use the new format based on the API version. Signed-off-by: Srinivas Pandruvada Reviewed-by: Zhang Rui Tested-by: Pragya Tanwar Link: https://lore.kernel.org/r/20230308070642.1727167-2-srinivas.pandruvada@linux.intel.com Reviewed-by: Hans de Goede Signed-off-by: Hans de Goede --- .../x86/intel/speed_select_if/isst_if_common.c | 28 ++++++++++++++++++++++ 1 file changed, 28 insertions(+) (limited to 'drivers/platform') diff --git a/drivers/platform/x86/intel/speed_select_if/isst_if_common.c b/drivers/platform/x86/intel/speed_select_if/isst_if_common.c index 19e671500f00..e0572a29212e 100644 --- a/drivers/platform/x86/intel/speed_select_if/isst_if_common.c +++ b/drivers/platform/x86/intel/speed_select_if/isst_if_common.c @@ -19,9 +19,13 @@ #include #include +#include +#include + #include "isst_if_common.h" #define MSR_THREAD_ID_INFO 0x53 +#define MSR_PM_LOGICAL_ID 0x54 #define MSR_CPU_BUS_NUMBER 0x128 static struct isst_if_cmd_cb punit_callbacks[ISST_IF_DEV_MAX]; @@ -31,6 +35,7 @@ static int punit_msr_white_list[] = { MSR_CONFIG_TDP_CONTROL, MSR_TURBO_RATIO_LIMIT1, MSR_TURBO_RATIO_LIMIT2, + MSR_PM_LOGICAL_ID, }; struct isst_valid_cmd_ranges { @@ -73,6 +78,8 @@ struct isst_cmd { u32 param; }; +static bool isst_hpm_support; + static DECLARE_HASHTABLE(isst_hash, 8); static DEFINE_MUTEX(isst_hash_lock); @@ -411,11 +418,20 @@ static int isst_if_cpu_online(unsigned int cpu) isst_cpu_info[cpu].pci_dev[1] = _isst_if_get_pci_dev(cpu, 1, 30, 1); } + if (isst_hpm_support) { + + ret = rdmsrl_safe(MSR_PM_LOGICAL_ID, &data); + if (!ret) + goto set_punit_id; + } + ret = rdmsrl_safe(MSR_THREAD_ID_INFO, &data); if (ret) { isst_cpu_info[cpu].punit_cpu_id = -1; return ret; } + +set_punit_id: isst_cpu_info[cpu].punit_cpu_id = data; isst_restore_msr_local(cpu); @@ -704,6 +720,12 @@ static struct miscdevice isst_if_char_driver = { .fops = &isst_if_char_driver_ops, }; +static const struct x86_cpu_id hpm_cpu_ids[] = { + X86_MATCH_INTEL_FAM6_MODEL(GRANITERAPIDS_X, NULL), + X86_MATCH_INTEL_FAM6_MODEL(SIERRAFOREST_X, NULL), + {} +}; + static int isst_misc_reg(void) { mutex_lock(&punit_misc_dev_reg_lock); @@ -711,6 +733,12 @@ static int isst_misc_reg(void) goto unlock_exit; if (!misc_usage_count) { + const struct x86_cpu_id *id; + + id = x86_match_cpu(hpm_cpu_ids); + if (id) + isst_hpm_support = true; + misc_device_ret = isst_if_cpu_info_init(); if (misc_device_ret) goto unlock_exit; -- cgit v1.2.3 From d805456c712f93ba8a012430f2a93bec133b6ff4 Mon Sep 17 00:00:00 2001 From: Srinivas Pandruvada Date: Tue, 7 Mar 2023 23:06:36 -0800 Subject: platform/x86: ISST: Enumerate TPMI SST and create framework Enumerate TPMI SST driver and create basic framework to add more features. The basic user space interface is still same as the legacy using /dev/isst_interface. Users of "intel-speed-select" utility should be able to use same commands as prior gens without being aware of new underlying hardware interface. TPMI SST driver enumerates on device "intel_vsec.tpmi-sst". Since there can be multiple instances and there is one common SST core, split implementation into two parts: A common core part and an enumeration part. The enumeration driver is loaded for each device instance and register with the TPMI SST core driver. On very first enumeration the TPMI SST core driver register with SST core driver to get IOCTL callbacks. The api_version is incremented for IOCTL ISST_IF_GET_PLATFORM_INFO, so that user space can issue new IOCTLs. Each TPMI package contains multiple power domains. Each power domain has its own set of SST controls. For each domain map the MMIO memory and update per domain struct tpmi_per_power_domain_info. This information will be used to implement other SST interfaces. Implement first IOCTL commands to get number of TPMI SST instances and instance mask as some of the power domains may not have any SST controls. Signed-off-by: Srinivas Pandruvada Reviewed-by: Hans de Goede Reviewed-by: Zhang Rui Tested-by: Pragya Tanwar Link: https://lore.kernel.org/r/20230308070642.1727167-3-srinivas.pandruvada@linux.intel.com Signed-off-by: Hans de Goede --- drivers/platform/x86/intel/speed_select_if/Kconfig | 4 + .../platform/x86/intel/speed_select_if/Makefile | 2 + .../platform/x86/intel/speed_select_if/isst_tpmi.c | 53 ++++ .../x86/intel/speed_select_if/isst_tpmi_core.c | 274 +++++++++++++++++++++ .../x86/intel/speed_select_if/isst_tpmi_core.h | 16 ++ include/uapi/linux/isst_if.h | 18 ++ 6 files changed, 367 insertions(+) create mode 100644 drivers/platform/x86/intel/speed_select_if/isst_tpmi.c create mode 100644 drivers/platform/x86/intel/speed_select_if/isst_tpmi_core.c create mode 100644 drivers/platform/x86/intel/speed_select_if/isst_tpmi_core.h (limited to 'drivers/platform') diff --git a/drivers/platform/x86/intel/speed_select_if/Kconfig b/drivers/platform/x86/intel/speed_select_if/Kconfig index ce3e3dc076d2..4eb3ad299db0 100644 --- a/drivers/platform/x86/intel/speed_select_if/Kconfig +++ b/drivers/platform/x86/intel/speed_select_if/Kconfig @@ -2,8 +2,12 @@ menu "Intel Speed Select Technology interface support" depends on PCI depends on X86_64 || COMPILE_TEST +config INTEL_SPEED_SELECT_TPMI + tristate + config INTEL_SPEED_SELECT_INTERFACE tristate "Intel(R) Speed Select Technology interface drivers" + select INTEL_SPEED_SELECT_TPMI if INTEL_TPMI help This config enables the Intel(R) Speed Select Technology interface drivers. The Intel(R) speed select technology features are non diff --git a/drivers/platform/x86/intel/speed_select_if/Makefile b/drivers/platform/x86/intel/speed_select_if/Makefile index 856076206f35..1d878a36d0ab 100644 --- a/drivers/platform/x86/intel/speed_select_if/Makefile +++ b/drivers/platform/x86/intel/speed_select_if/Makefile @@ -8,3 +8,5 @@ obj-$(CONFIG_INTEL_SPEED_SELECT_INTERFACE) += isst_if_common.o obj-$(CONFIG_INTEL_SPEED_SELECT_INTERFACE) += isst_if_mmio.o obj-$(CONFIG_INTEL_SPEED_SELECT_INTERFACE) += isst_if_mbox_pci.o obj-$(CONFIG_INTEL_SPEED_SELECT_INTERFACE) += isst_if_mbox_msr.o +obj-$(CONFIG_INTEL_SPEED_SELECT_TPMI) += isst_tpmi_core.o +obj-$(CONFIG_INTEL_SPEED_SELECT_TPMI) += isst_tpmi.o diff --git a/drivers/platform/x86/intel/speed_select_if/isst_tpmi.c b/drivers/platform/x86/intel/speed_select_if/isst_tpmi.c new file mode 100644 index 000000000000..7b4bdeefb8bc --- /dev/null +++ b/drivers/platform/x86/intel/speed_select_if/isst_tpmi.c @@ -0,0 +1,53 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * isst_tpmi.c: SST TPMI interface + * + * Copyright (c) 2023, Intel Corporation. + * All Rights Reserved. + * + */ + +#include +#include +#include + +#include "isst_tpmi_core.h" + +static int intel_sst_probe(struct auxiliary_device *auxdev, const struct auxiliary_device_id *id) +{ + int ret; + + ret = tpmi_sst_init(); + if (ret) + return ret; + + ret = tpmi_sst_dev_add(auxdev); + if (ret) + tpmi_sst_exit(); + + return ret; +} + +static void intel_sst_remove(struct auxiliary_device *auxdev) +{ + tpmi_sst_dev_remove(auxdev); + tpmi_sst_exit(); +} + +static const struct auxiliary_device_id intel_sst_id_table[] = { + { .name = "intel_vsec.tpmi-sst" }, + {} +}; +MODULE_DEVICE_TABLE(auxiliary, intel_sst_id_table); + +static struct auxiliary_driver intel_sst_aux_driver = { + .id_table = intel_sst_id_table, + .remove = intel_sst_remove, + .probe = intel_sst_probe, +}; + +module_auxiliary_driver(intel_sst_aux_driver); + +MODULE_IMPORT_NS(INTEL_TPMI_SST); +MODULE_DESCRIPTION("Intel TPMI SST Driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/platform/x86/intel/speed_select_if/isst_tpmi_core.c b/drivers/platform/x86/intel/speed_select_if/isst_tpmi_core.c new file mode 100644 index 000000000000..6b37016c0417 --- /dev/null +++ b/drivers/platform/x86/intel/speed_select_if/isst_tpmi_core.c @@ -0,0 +1,274 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * isst_tpmi.c: SST TPMI interface core + * + * Copyright (c) 2023, Intel Corporation. + * All Rights Reserved. + * + * This information will be useful to understand flows: + * In the current generation of platforms, TPMI is supported via OOB + * PCI device. This PCI device has one instance per CPU package. + * There is a unique TPMI ID for SST. Each TPMI ID also has multiple + * entries, representing per power domain information. + * + * There is one dev file for complete SST information and control same as the + * prior generation of hardware. User spaces don't need to know how the + * information is presented by the hardware. The TPMI core module implements + * the hardware mapping. + */ + +#include +#include +#include +#include +#include +#include + +#include "isst_tpmi_core.h" +#include "isst_if_common.h" + +/** + * struct tpmi_per_power_domain_info - Store per power_domain SST info + * @package_id: Package id for this power_domain + * @power_domain_id: Power domain id, Each entry from the SST-TPMI instance is a power_domain. + * @sst_base: Mapped SST base IO memory + * @auxdev: Auxiliary device instance enumerated this instance + * + * This structure is used store complete SST information for a power_domain. This information + * is used to read/write request for any SST IOCTL. Each physical CPU package can have multiple + * power_domains. Each power domain describes its own SST information and has its own controls. + */ +struct tpmi_per_power_domain_info { + int package_id; + int power_domain_id; + void __iomem *sst_base; + struct auxiliary_device *auxdev; +}; + +/** + * struct tpmi_sst_struct - Store sst info for a package + * @package_id: Package id for this aux device instance + * @number_of_power_domains: Number of power_domains pointed by power_domain_info pointer + * @power_domain_info: Pointer to power domains information + * + * This structure is used store full SST information for a package. + * Each package has a unique OOB PCI device, which enumerates TPMI. + * Each Package will have multiple power_domains. + */ +struct tpmi_sst_struct { + int package_id; + int number_of_power_domains; + struct tpmi_per_power_domain_info *power_domain_info; +}; + +/** + * struct tpmi_sst_common_struct - Store all SST instances + * @max_index: Maximum instances currently present + * @sst_inst: Pointer to per package instance + * + * Stores every SST Package instance. + */ +struct tpmi_sst_common_struct { + int max_index; + struct tpmi_sst_struct **sst_inst; +}; + +/* + * Each IOCTL request is processed under this lock. Also used to protect + * registration functions and common data structures. + */ +static DEFINE_MUTEX(isst_tpmi_dev_lock); + +/* Usage count to track, number of TPMI SST instances registered to this core. */ +static int isst_core_usage_count; + +/* Stores complete SST information for every package and power_domain */ +static struct tpmi_sst_common_struct isst_common; + +static int isst_if_get_tpmi_instance_count(void __user *argp) +{ + struct isst_tpmi_instance_count tpmi_inst; + struct tpmi_sst_struct *sst_inst; + int i; + + if (copy_from_user(&tpmi_inst, argp, sizeof(tpmi_inst))) + return -EFAULT; + + if (tpmi_inst.socket_id >= topology_max_packages()) + return -EINVAL; + + tpmi_inst.count = isst_common.sst_inst[tpmi_inst.socket_id]->number_of_power_domains; + + sst_inst = isst_common.sst_inst[tpmi_inst.socket_id]; + tpmi_inst.valid_mask = 0; + for (i = 0; i < sst_inst->number_of_power_domains; ++i) { + struct tpmi_per_power_domain_info *power_domain_info; + + power_domain_info = &sst_inst->power_domain_info[i]; + if (power_domain_info->sst_base) + tpmi_inst.valid_mask |= BIT(i); + } + + if (copy_to_user(argp, &tpmi_inst, sizeof(tpmi_inst))) + return -EFAULT; + + return 0; +} + +static long isst_if_def_ioctl(struct file *file, unsigned int cmd, + unsigned long arg) +{ + void __user *argp = (void __user *)arg; + long ret = -ENOTTY; + + mutex_lock(&isst_tpmi_dev_lock); + switch (cmd) { + case ISST_IF_COUNT_TPMI_INSTANCES: + ret = isst_if_get_tpmi_instance_count(argp); + break; + default: + break; + } + mutex_unlock(&isst_tpmi_dev_lock); + + return ret; +} + +int tpmi_sst_dev_add(struct auxiliary_device *auxdev) +{ + struct intel_tpmi_plat_info *plat_info; + struct tpmi_sst_struct *tpmi_sst; + int i, pkg = 0, inst = 0; + int num_resources; + + plat_info = tpmi_get_platform_data(auxdev); + if (!plat_info) { + dev_err(&auxdev->dev, "No platform info\n"); + return -EINVAL; + } + + pkg = plat_info->package_id; + if (pkg >= topology_max_packages()) { + dev_err(&auxdev->dev, "Invalid package id :%x\n", pkg); + return -EINVAL; + } + + if (isst_common.sst_inst[pkg]) + return -EEXIST; + + num_resources = tpmi_get_resource_count(auxdev); + + if (!num_resources) + return -EINVAL; + + tpmi_sst = devm_kzalloc(&auxdev->dev, sizeof(*tpmi_sst), GFP_KERNEL); + if (!tpmi_sst) + return -ENOMEM; + + tpmi_sst->power_domain_info = devm_kcalloc(&auxdev->dev, num_resources, + sizeof(*tpmi_sst->power_domain_info), + GFP_KERNEL); + if (!tpmi_sst->power_domain_info) + return -ENOMEM; + + tpmi_sst->number_of_power_domains = num_resources; + + for (i = 0; i < num_resources; ++i) { + struct resource *res; + + res = tpmi_get_resource_at_index(auxdev, i); + if (!res) { + tpmi_sst->power_domain_info[i].sst_base = NULL; + continue; + } + + tpmi_sst->power_domain_info[i].package_id = pkg; + tpmi_sst->power_domain_info[i].power_domain_id = i; + tpmi_sst->power_domain_info[i].auxdev = auxdev; + tpmi_sst->power_domain_info[i].sst_base = devm_ioremap_resource(&auxdev->dev, res); + if (IS_ERR(tpmi_sst->power_domain_info[i].sst_base)) + return PTR_ERR(tpmi_sst->power_domain_info[i].sst_base); + + ++inst; + } + + if (!inst) + return -ENODEV; + + tpmi_sst->package_id = pkg; + auxiliary_set_drvdata(auxdev, tpmi_sst); + + mutex_lock(&isst_tpmi_dev_lock); + if (isst_common.max_index < pkg) + isst_common.max_index = pkg; + isst_common.sst_inst[pkg] = tpmi_sst; + mutex_unlock(&isst_tpmi_dev_lock); + + return 0; +} +EXPORT_SYMBOL_NS_GPL(tpmi_sst_dev_add, INTEL_TPMI_SST); + +void tpmi_sst_dev_remove(struct auxiliary_device *auxdev) +{ + struct tpmi_sst_struct *tpmi_sst = auxiliary_get_drvdata(auxdev); + + mutex_lock(&isst_tpmi_dev_lock); + isst_common.sst_inst[tpmi_sst->package_id] = NULL; + mutex_unlock(&isst_tpmi_dev_lock); +} +EXPORT_SYMBOL_NS_GPL(tpmi_sst_dev_remove, INTEL_TPMI_SST); + +#define ISST_TPMI_API_VERSION 0x02 + +int tpmi_sst_init(void) +{ + struct isst_if_cmd_cb cb; + int ret = 0; + + mutex_lock(&isst_tpmi_dev_lock); + + if (isst_core_usage_count) { + ++isst_core_usage_count; + goto init_done; + } + + isst_common.sst_inst = kcalloc(topology_max_packages(), + sizeof(*isst_common.sst_inst), + GFP_KERNEL); + if (!isst_common.sst_inst) + return -ENOMEM; + + memset(&cb, 0, sizeof(cb)); + cb.cmd_size = sizeof(struct isst_if_io_reg); + cb.offset = offsetof(struct isst_if_io_regs, io_reg); + cb.cmd_callback = NULL; + cb.api_version = ISST_TPMI_API_VERSION; + cb.def_ioctl = isst_if_def_ioctl; + cb.owner = THIS_MODULE; + ret = isst_if_cdev_register(ISST_IF_DEV_TPMI, &cb); + if (ret) + kfree(isst_common.sst_inst); +init_done: + mutex_unlock(&isst_tpmi_dev_lock); + return ret; +} +EXPORT_SYMBOL_NS_GPL(tpmi_sst_init, INTEL_TPMI_SST); + +void tpmi_sst_exit(void) +{ + mutex_lock(&isst_tpmi_dev_lock); + if (isst_core_usage_count) + --isst_core_usage_count; + + if (!isst_core_usage_count) { + isst_if_cdev_unregister(ISST_IF_DEV_TPMI); + kfree(isst_common.sst_inst); + } + mutex_unlock(&isst_tpmi_dev_lock); +} +EXPORT_SYMBOL_NS_GPL(tpmi_sst_exit, INTEL_TPMI_SST); + +MODULE_IMPORT_NS(INTEL_TPMI); +MODULE_IMPORT_NS(INTEL_TPMI_POWER_DOMAIN); + +MODULE_LICENSE("GPL"); diff --git a/drivers/platform/x86/intel/speed_select_if/isst_tpmi_core.h b/drivers/platform/x86/intel/speed_select_if/isst_tpmi_core.h new file mode 100644 index 000000000000..356cb02273b1 --- /dev/null +++ b/drivers/platform/x86/intel/speed_select_if/isst_tpmi_core.h @@ -0,0 +1,16 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Intel Speed Select Interface: Drivers Internal defines + * Copyright (c) 2023, Intel Corporation. + * All rights reserved. + * + */ + +#ifndef _ISST_TPMI_CORE_H +#define _ISST_TPMI_CORE_H + +int tpmi_sst_init(void); +void tpmi_sst_exit(void); +int tpmi_sst_dev_add(struct auxiliary_device *auxdev); +void tpmi_sst_dev_remove(struct auxiliary_device *auxdev); +#endif diff --git a/include/uapi/linux/isst_if.h b/include/uapi/linux/isst_if.h index ba078f8e9add..bf32d959f6e8 100644 --- a/include/uapi/linux/isst_if.h +++ b/include/uapi/linux/isst_if.h @@ -163,10 +163,28 @@ struct isst_if_msr_cmds { struct isst_if_msr_cmd msr_cmd[1]; }; +/** + * struct isst_tpmi_instance_count - Get number of TPMI instances per socket + * @socket_id: Socket/package id + * @count: Number of instances + * @valid_mask: Mask of instances as there can be holes + * + * Structure used to get TPMI instances information using + * IOCTL ISST_IF_COUNT_TPMI_INSTANCES. + */ +struct isst_tpmi_instance_count { + __u8 socket_id; + __u8 count; + __u16 valid_mask; +}; + #define ISST_IF_MAGIC 0xFE #define ISST_IF_GET_PLATFORM_INFO _IOR(ISST_IF_MAGIC, 0, struct isst_if_platform_info *) #define ISST_IF_GET_PHY_ID _IOWR(ISST_IF_MAGIC, 1, struct isst_if_cpu_map *) #define ISST_IF_IO_CMD _IOW(ISST_IF_MAGIC, 2, struct isst_if_io_regs *) #define ISST_IF_MBOX_COMMAND _IOWR(ISST_IF_MAGIC, 3, struct isst_if_mbox_cmds *) #define ISST_IF_MSR_COMMAND _IOWR(ISST_IF_MAGIC, 4, struct isst_if_msr_cmds *) + +#define ISST_IF_COUNT_TPMI_INSTANCES _IOR(ISST_IF_MAGIC, 5, struct isst_tpmi_instance_count *) + #endif -- cgit v1.2.3 From 0ab147bb840fca2bc3bca88f320b34c5b5cc013c Mon Sep 17 00:00:00 2001 From: Srinivas Pandruvada Date: Tue, 7 Mar 2023 23:06:37 -0800 Subject: platform/x86: ISST: Parse SST MMIO and update instance SST registers are presented to OS in multi-layer structures starting with a SST header showing version information freezing current definition. For details on SST terminology refer to Documentation/admin-guide/pm/intel-speed-select.rst under the kernel documentation SST TPMI details are published in the following document: https://github.com/intel/tpmi_power_management/blob/main/SST_TPMI_public_disclosure_FINAL.docx SST MMIO structure layout follows: SST-HEADER SST-CP Header SST-CP CONTROL SST-CP STATUS SST-CP CONFIG0 SST-CP CONFIG1 ... ... SST-PP Header SST-PP OFFSET_0 SST-PP OFFSET_1 SST_PP_0_INFO SST_PP_1_INFO SST_PP_2_INFO SST_PP_3_INFO SST-PP CONTROL SST-PP STATUS Each register bank contains information to get to next lower level information. This information is parsed and stored in the struct tpmi_per_power_domain_info for each domain. This information is used to process each SST requests. Signed-off-by: Srinivas Pandruvada Reviewed-by: Zhang Rui Tested-by: Pragya Tanwar Link: https://lore.kernel.org/r/20230308070642.1727167-4-srinivas.pandruvada@linux.intel.com Signed-off-by: Hans de Goede --- .../x86/intel/speed_select_if/isst_tpmi_core.c | 291 ++++++++++++++++++++- 1 file changed, 287 insertions(+), 4 deletions(-) (limited to 'drivers/platform') diff --git a/drivers/platform/x86/intel/speed_select_if/isst_tpmi_core.c b/drivers/platform/x86/intel/speed_select_if/isst_tpmi_core.c index 6b37016c0417..3453708c2dd0 100644 --- a/drivers/platform/x86/intel/speed_select_if/isst_tpmi_core.c +++ b/drivers/platform/x86/intel/speed_select_if/isst_tpmi_core.c @@ -20,6 +20,7 @@ #include #include #include +#include #include #include #include @@ -27,10 +28,192 @@ #include "isst_tpmi_core.h" #include "isst_if_common.h" +/* Supported SST hardware version by this driver */ +#define ISST_HEADER_VERSION 1 + +/** + * struct sst_header - SST main header + * @interface_version: Version number for this interface + * @cap_mask: Bitmask of the supported sub features. 1=the sub feature is enabled. + * 0=disabled. + * Bit[8]= SST_CP enable (1), disable (0) + * bit[9]= SST_PP enable (1), disable (0) + * other bits are reserved for future use + * @cp_offset: Qword (8 bytes) offset to the SST_CP register bank + * @pp_offset: Qword (8 bytes) offset to the SST_PP register bank + * @reserved: Reserved for future use + * + * This register allows SW to discover SST capability and the offsets to SST-CP + * and SST-PP register banks. + */ +struct sst_header { + u8 interface_version; + u8 cap_mask; + u8 cp_offset; + u8 pp_offset; + u32 reserved; +} __packed; + +/** + * struct cp_header - SST-CP (core-power) header + * @feature_id: 0=SST-CP, 1=SST-PP, 2=SST-BF, 3=SST-TF + * @feature_rev: Interface Version number for this SST feature + * @ratio_unit: Frequency ratio unit. 00: 100MHz. All others are reserved + * @reserved: Reserved for future use + * + * This structure is used store SST-CP header. This is packed to the same + * format as defined in the specifications. + */ +struct cp_header { + u64 feature_id :4; + u64 feature_rev :8; + u64 ratio_unit :2; + u64 reserved :50; +} __packed; + +/** + * struct pp_header - SST-PP (Perf profile) header + * @feature_id: 0=SST-CP, 1=SST-PP, 2=SST-BF, 3=SST-TF + * @feature_rev: Interface Version number for this SST feature + * @level_en_mask: SST-PP level enable/disable fuse mask + * @allowed_level_mask: Allowed level mask used for dynamic config level switching + * @reserved0: Reserved for future use + * @ratio_unit: Frequency ratio unit. 00: 100MHz. All others are reserved + * @block_size: Size of PP block in Qword unit (8 bytes) + * @dynamic_switch: If set (1), dynamic switching of SST PP is supported + * @memory_ratio_unit: Memory Controller frequency ratio unit. 00: 100MHz, others reserved + * @reserved1: Reserved for future use + * + * This structure is used store SST-PP header. This is packed to the same + * format as defined in the specifications. + */ +struct pp_header { + u64 feature_id :4; + u64 feature_rev :8; + u64 level_en_mask :8; + u64 allowed_level_mask :8; + u64 reserved0 :4; + u64 ratio_unit :2; + u64 block_size :8; + u64 dynamic_switch :1; + u64 memory_ratio_unit :2; + u64 reserved1 :19; +} __packed; + +/** + * struct feature_offset - Offsets to SST-PP features + * @pp_offset: Qword offset within PP level for the SST_PP register bank + * @bf_offset: Qword offset within PP level for the SST_BF register bank + * @tf_offset: Qword offset within PP level for the SST_TF register bank + * @reserved: Reserved for future use + * + * This structure is used store offsets for SST features in the register bank. + * This is packed to the same format as defined in the specifications. + */ +struct feature_offset { + u64 pp_offset :8; + u64 bf_offset :8; + u64 tf_offset :8; + u64 reserved :40; +} __packed; + +/** + * struct levels_offset - Offsets to each SST PP level + * @sst_pp_level0_offset: Qword offset to the register block of PP level 0 + * @sst_pp_level1_offset: Qword offset to the register block of PP level 1 + * @sst_pp_level2_offset: Qword offset to the register block of PP level 2 + * @sst_pp_level3_offset: Qword offset to the register block of PP level 3 + * @sst_pp_level4_offset: Qword offset to the register block of PP level 4 + * @reserved: Reserved for future use + * + * This structure is used store offsets of SST PP levels in the register bank. + * This is packed to the same format as defined in the specifications. + */ +struct levels_offset { + u64 sst_pp_level0_offset :8; + u64 sst_pp_level1_offset :8; + u64 sst_pp_level2_offset :8; + u64 sst_pp_level3_offset :8; + u64 sst_pp_level4_offset :8; + u64 reserved :24; +} __packed; + +/** + * struct pp_control_offset - Offsets for SST PP controls + * @perf_level: A SST-PP level that SW intends to switch to + * @perf_level_lock: SST-PP level select lock. 0 - unlocked. 1 - locked till next reset + * @resvd0: Reserved for future use + * @current_state: Bit mask to control the enable(1)/disable(0) state of each feature + * of the current PP level, bit 0 = BF, bit 1 = TF, bit 2-7 = reserved + * @reserved: Reserved for future use + * + * This structure is used store offsets of SST PP controls in the register bank. + * This is packed to the same format as defined in the specifications. + */ +struct pp_control_offset { + u64 perf_level :3; + u64 perf_level_lock :1; + u64 resvd0 :4; + u64 current_state :8; + u64 reserved :48; +} __packed; + +/** + * struct pp_status_offset - Offsets for SST PP status fields + * @sst_pp_level: Returns the current SST-PP level + * @sst_pp_lock: Returns the lock bit setting of perf_level_lock in pp_control_offset + * @error_type: Returns last error of SST-PP level change request. 0: no error, + * 1: level change not allowed, others: reserved + * @feature_state: Bit mask to indicate the enable(1)/disable(0) state of each feature of the + * current PP level. bit 0 = BF, bit 1 = TF, bit 2-7 reserved + * @reserved0: Reserved for future use + * @feature_error_type: Returns last error of the specific feature. Three error_type bits per + * feature. i.e. ERROR_TYPE[2:0] for BF, ERROR_TYPE[5:3] for TF, etc. + * 0x0: no error, 0x1: The specific feature is not supported by the hardware. + * 0x2-0x6: Reserved. 0x7: feature state change is not allowed. + * @reserved1: Reserved for future use + * + * This structure is used store offsets of SST PP status in the register bank. + * This is packed to the same format as defined in the specifications. + */ +struct pp_status_offset { + u64 sst_pp_level :3; + u64 sst_pp_lock :1; + u64 error_type :4; + u64 feature_state :8; + u64 reserved0 :16; + u64 feature_error_type : 24; + u64 reserved1 :8; +} __packed; + +/** + * struct perf_level - Used to store perf level and mmio offset + * @mmio_offset: mmio offset for a perf level + * @level: perf level for this offset + * + * This structure is used store final mmio offset of each perf level from the + * SST base mmio offset. + */ +struct perf_level { + int mmio_offset; + int level; +}; + /** * struct tpmi_per_power_domain_info - Store per power_domain SST info * @package_id: Package id for this power_domain * @power_domain_id: Power domain id, Each entry from the SST-TPMI instance is a power_domain. + * @max_level: Max possible PP level possible for this power_domain + * @ratio_unit: Ratio unit for converting to MHz + * @avx_levels: Number of AVX levels + * @pp_block_size: Block size from PP header + * @sst_header: Store SST header for this power_domain + * @cp_header: Store SST-CP header for this power_domain + * @pp_header: Store SST-PP header for this power_domain + * @perf_levels: Pointer to each perf level to map level to mmio offset + * @feature_offsets: Store feature offsets for each PP-level + * @control_offset: Store the control offset for each PP-level + * @status_offset: Store the status offset for each PP-level * @sst_base: Mapped SST base IO memory * @auxdev: Auxiliary device instance enumerated this instance * @@ -41,6 +224,17 @@ struct tpmi_per_power_domain_info { int package_id; int power_domain_id; + int max_level; + int ratio_unit; + int avx_levels; + int pp_block_size; + struct sst_header sst_header; + struct cp_header cp_header; + struct pp_header pp_header; + struct perf_level *perf_levels; + struct feature_offset feature_offsets; + struct pp_control_offset control_offset; + struct pp_status_offset status_offset; void __iomem *sst_base; struct auxiliary_device *auxdev; }; @@ -85,6 +279,86 @@ static int isst_core_usage_count; /* Stores complete SST information for every package and power_domain */ static struct tpmi_sst_common_struct isst_common; +#define SST_MAX_AVX_LEVELS 3 + +#define SST_PP_OFFSET_0 8 +#define SST_PP_OFFSET_1 16 +#define SST_PP_OFFSET_SIZE 8 + +static int sst_add_perf_profiles(struct auxiliary_device *auxdev, + struct tpmi_per_power_domain_info *pd_info, + int levels) +{ + u64 perf_level_offsets; + int i; + + pd_info->perf_levels = devm_kcalloc(&auxdev->dev, levels, + sizeof(struct perf_level), + GFP_KERNEL); + if (!pd_info->perf_levels) + return 0; + + pd_info->ratio_unit = pd_info->pp_header.ratio_unit; + pd_info->avx_levels = SST_MAX_AVX_LEVELS; + pd_info->pp_block_size = pd_info->pp_header.block_size; + + /* Read PP Offset 0: Get feature offset with PP level */ + *((u64 *)&pd_info->feature_offsets) = readq(pd_info->sst_base + + pd_info->sst_header.pp_offset + + SST_PP_OFFSET_0); + + perf_level_offsets = readq(pd_info->sst_base + pd_info->sst_header.pp_offset + + SST_PP_OFFSET_1); + + for (i = 0; i < levels; ++i) { + u64 offset; + + offset = perf_level_offsets & (0xff << (i * SST_PP_OFFSET_SIZE)); + offset >>= (i * 8); + offset &= 0xff; + offset *= 8; /* Convert to byte from QWORD offset */ + pd_info->perf_levels[i].mmio_offset = pd_info->sst_header.pp_offset + offset; + } + + return 0; +} + +static int sst_main(struct auxiliary_device *auxdev, struct tpmi_per_power_domain_info *pd_info) +{ + int i, mask, levels; + + *((u64 *)&pd_info->sst_header) = readq(pd_info->sst_base); + pd_info->sst_header.cp_offset *= 8; + pd_info->sst_header.pp_offset *= 8; + + if (pd_info->sst_header.interface_version != ISST_HEADER_VERSION) { + dev_err(&auxdev->dev, "SST: Unsupported version:%x\n", + pd_info->sst_header.interface_version); + return -ENODEV; + } + + /* Read SST CP Header */ + *((u64 *)&pd_info->cp_header) = readq(pd_info->sst_base + pd_info->sst_header.cp_offset); + + /* Read PP header */ + *((u64 *)&pd_info->pp_header) = readq(pd_info->sst_base + pd_info->sst_header.pp_offset); + + /* Force level_en_mask level 0 */ + pd_info->pp_header.level_en_mask |= 0x01; + + mask = 0x01; + levels = 0; + for (i = 0; i < 8; ++i) { + if (pd_info->pp_header.level_en_mask & mask) + levels = i; + mask <<= 1; + } + pd_info->max_level = levels; + sst_add_perf_profiles(auxdev, pd_info, levels + 1); + + return 0; +} + static int isst_if_get_tpmi_instance_count(void __user *argp) { struct isst_tpmi_instance_count tpmi_inst; @@ -102,10 +376,10 @@ static int isst_if_get_tpmi_instance_count(void __user *argp) sst_inst = isst_common.sst_inst[tpmi_inst.socket_id]; tpmi_inst.valid_mask = 0; for (i = 0; i < sst_inst->number_of_power_domains; ++i) { - struct tpmi_per_power_domain_info *power_domain_info; + struct tpmi_per_power_domain_info *pd_info; - power_domain_info = &sst_inst->power_domain_info[i]; - if (power_domain_info->sst_base) + pd_info = &sst_inst->power_domain_info[i]; + if (pd_info->sst_base) tpmi_inst.valid_mask |= BIT(i); } @@ -134,11 +408,13 @@ static long isst_if_def_ioctl(struct file *file, unsigned int cmd, return ret; } +#define TPMI_SST_AUTO_SUSPEND_DELAY_MS 2000 + int tpmi_sst_dev_add(struct auxiliary_device *auxdev) { struct intel_tpmi_plat_info *plat_info; struct tpmi_sst_struct *tpmi_sst; - int i, pkg = 0, inst = 0; + int i, ret, pkg = 0, inst = 0; int num_resources; plat_info = tpmi_get_platform_data(auxdev); @@ -189,6 +465,13 @@ int tpmi_sst_dev_add(struct auxiliary_device *auxdev) if (IS_ERR(tpmi_sst->power_domain_info[i].sst_base)) return PTR_ERR(tpmi_sst->power_domain_info[i].sst_base); + ret = sst_main(auxdev, &tpmi_sst->power_domain_info[i]); + if (ret) { + devm_iounmap(&auxdev->dev, tpmi_sst->power_domain_info[i].sst_base); + tpmi_sst->power_domain_info[i].sst_base = NULL; + continue; + } + ++inst; } -- cgit v1.2.3 From 12a7d2cb811dd8a884dea088a2701fcb8d00136e Mon Sep 17 00:00:00 2001 From: Srinivas Pandruvada Date: Tue, 7 Mar 2023 23:06:38 -0800 Subject: platform/x86: ISST: Add SST-CP support via TPMI Intel Speed Select Technology Core Power (SST-CP) is an interface that allows users to define per core priority. This defines a mechanism to distribute power among cores when there is a power constrained scenario. This defines a class of service (CLOS) configuration. Three new IOCTLs are added: ISST_IF_CORE_POWER_STATE : Enable/Disable SST-CP ISST_IF_CLOS_PARAM : Configure CLOS parameters ISST_IF_CLOS_ASSOC : Associate CPUs to a CLOS To associate CPUs to CLOS, either Linux CPU numbering or PUNIT numbering scheme can be used, using parameter punit_cpu_map (1: for PUNIT numbering 0 for Linux CPU number). There is no change to IOCTL to get PUNIT CPU number for a CPU. Introduce get_instance() function, which is used by majority of IOCTLs processing to convert a socket and power domain to tpmi_per_power_domain_info * instance. This instance has all the MMIO offsets stored to read a particular field. Once an instance is identified, read or write from correct MMIO offset for a given field as defined in the specification. For details on SST CP operations using intel-speed-selet utility, refer to: Documentation/admin-guide/pm/intel-speed-select.rst under the kernel documentation Signed-off-by: Srinivas Pandruvada Reviewed-by: Zhang Rui Tested-by: Pragya Tanwar Link: https://lore.kernel.org/r/20230308070642.1727167-5-srinivas.pandruvada@linux.intel.com Signed-off-by: Hans de Goede --- .../x86/intel/speed_select_if/isst_tpmi_core.c | 264 +++++++++++++++++++++ include/uapi/linux/isst_if.h | 79 ++++++ 2 files changed, 343 insertions(+) (limited to 'drivers/platform') diff --git a/drivers/platform/x86/intel/speed_select_if/isst_tpmi_core.c b/drivers/platform/x86/intel/speed_select_if/isst_tpmi_core.c index 3453708c2dd0..bc1c1f26fbf9 100644 --- a/drivers/platform/x86/intel/speed_select_if/isst_tpmi_core.c +++ b/drivers/platform/x86/intel/speed_select_if/isst_tpmi_core.c @@ -31,6 +31,18 @@ /* Supported SST hardware version by this driver */ #define ISST_HEADER_VERSION 1 +/* + * Used to indicate if value read from MMIO needs to get multiplied + * to get to a standard unit or not. + */ +#define SST_MUL_FACTOR_NONE 1 + +/* Define 100 as a scaling factor frequency ratio to frequency conversion */ +#define SST_MUL_FACTOR_FREQ 100 + +/* All SST regs are 64 bit size */ +#define SST_REG_SIZE 8 + /** * struct sst_header - SST main header * @interface_version: Version number for this interface @@ -359,6 +371,249 @@ static int sst_main(struct auxiliary_device *auxdev, struct tpmi_per_power_domai return 0; } +/* + * Map a package and power_domain id to SST information structure unique for a power_domain. + * The caller should call under isst_tpmi_dev_lock. + */ +static struct tpmi_per_power_domain_info *get_instance(int pkg_id, int power_domain_id) +{ + struct tpmi_per_power_domain_info *power_domain_info; + struct tpmi_sst_struct *sst_inst; + + if (pkg_id < 0 || pkg_id > isst_common.max_index || + pkg_id >= topology_max_packages()) + return NULL; + + sst_inst = isst_common.sst_inst[pkg_id]; + if (!sst_inst) + return NULL; + + if (power_domain_id < 0 || power_domain_id >= sst_inst->number_of_power_domains) + return NULL; + + power_domain_info = &sst_inst->power_domain_info[power_domain_id]; + + if (power_domain_info && !power_domain_info->sst_base) + return NULL; + + return power_domain_info; +} + +static bool disable_dynamic_sst_features(void) +{ + u64 value; + + rdmsrl(MSR_PM_ENABLE, value); + return !(value & 0x1); +} + +#define _read_cp_info(name_str, name, offset, start, width, mult_factor)\ +{\ + u64 val, mask;\ + \ + val = readq(power_domain_info->sst_base + power_domain_info->sst_header.cp_offset +\ + (offset));\ + mask = GENMASK_ULL((start + width - 1), start);\ + val &= mask; \ + val >>= start;\ + name = (val * mult_factor);\ +} + +#define _write_cp_info(name_str, name, offset, start, width, div_factor)\ +{\ + u64 val, mask;\ + \ + val = readq(power_domain_info->sst_base +\ + power_domain_info->sst_header.cp_offset + (offset));\ + mask = GENMASK_ULL((start + width - 1), start);\ + val &= ~mask;\ + val |= (name / div_factor) << start;\ + writeq(val, power_domain_info->sst_base + power_domain_info->sst_header.cp_offset +\ + (offset));\ +} + +#define SST_CP_CONTROL_OFFSET 8 +#define SST_CP_STATUS_OFFSET 16 + +#define SST_CP_ENABLE_START 0 +#define SST_CP_ENABLE_WIDTH 1 + +#define SST_CP_PRIORITY_TYPE_START 1 +#define SST_CP_PRIORITY_TYPE_WIDTH 1 + +static long isst_if_core_power_state(void __user *argp) +{ + struct tpmi_per_power_domain_info *power_domain_info; + struct isst_core_power core_power; + + if (disable_dynamic_sst_features()) + return -EFAULT; + + if (copy_from_user(&core_power, argp, sizeof(core_power))) + return -EFAULT; + + power_domain_info = get_instance(core_power.socket_id, core_power.power_domain_id); + if (!power_domain_info) + return -EINVAL; + + if (core_power.get_set) { + _write_cp_info("cp_enable", core_power.enable, SST_CP_CONTROL_OFFSET, + SST_CP_ENABLE_START, SST_CP_ENABLE_WIDTH, SST_MUL_FACTOR_NONE) + _write_cp_info("cp_prio_type", core_power.priority_type, SST_CP_CONTROL_OFFSET, + SST_CP_PRIORITY_TYPE_START, SST_CP_PRIORITY_TYPE_WIDTH, + SST_MUL_FACTOR_NONE) + } else { + /* get */ + _read_cp_info("cp_enable", core_power.enable, SST_CP_STATUS_OFFSET, + SST_CP_ENABLE_START, SST_CP_ENABLE_WIDTH, SST_MUL_FACTOR_NONE) + _read_cp_info("cp_prio_type", core_power.priority_type, SST_CP_STATUS_OFFSET, + SST_CP_PRIORITY_TYPE_START, SST_CP_PRIORITY_TYPE_WIDTH, + SST_MUL_FACTOR_NONE) + core_power.supported = !!(power_domain_info->sst_header.cap_mask & BIT(0)); + if (copy_to_user(argp, &core_power, sizeof(core_power))) + return -EFAULT; + } + + return 0; +} + +#define SST_CLOS_CONFIG_0_OFFSET 24 + +#define SST_CLOS_CONFIG_PRIO_START 4 +#define SST_CLOS_CONFIG_PRIO_WIDTH 4 + +#define SST_CLOS_CONFIG_MIN_START 8 +#define SST_CLOS_CONFIG_MIN_WIDTH 8 + +#define SST_CLOS_CONFIG_MAX_START 16 +#define SST_CLOS_CONFIG_MAX_WIDTH 8 + +static long isst_if_clos_param(void __user *argp) +{ + struct tpmi_per_power_domain_info *power_domain_info; + struct isst_clos_param clos_param; + + if (copy_from_user(&clos_param, argp, sizeof(clos_param))) + return -EFAULT; + + power_domain_info = get_instance(clos_param.socket_id, clos_param.power_domain_id); + if (!power_domain_info) + return -EINVAL; + + if (clos_param.get_set) { + _write_cp_info("clos.min_freq", clos_param.min_freq_mhz, + (SST_CLOS_CONFIG_0_OFFSET + clos_param.clos * SST_REG_SIZE), + SST_CLOS_CONFIG_MIN_START, SST_CLOS_CONFIG_MIN_WIDTH, + SST_MUL_FACTOR_FREQ); + _write_cp_info("clos.max_freq", clos_param.max_freq_mhz, + (SST_CLOS_CONFIG_0_OFFSET + clos_param.clos * SST_REG_SIZE), + SST_CLOS_CONFIG_MAX_START, SST_CLOS_CONFIG_MAX_WIDTH, + SST_MUL_FACTOR_FREQ); + _write_cp_info("clos.prio", clos_param.prop_prio, + (SST_CLOS_CONFIG_0_OFFSET + clos_param.clos * SST_REG_SIZE), + SST_CLOS_CONFIG_PRIO_START, SST_CLOS_CONFIG_PRIO_WIDTH, + SST_MUL_FACTOR_NONE); + } else { + /* get */ + _read_cp_info("clos.min_freq", clos_param.min_freq_mhz, + (SST_CLOS_CONFIG_0_OFFSET + clos_param.clos * SST_REG_SIZE), + SST_CLOS_CONFIG_MIN_START, SST_CLOS_CONFIG_MIN_WIDTH, + SST_MUL_FACTOR_FREQ) + _read_cp_info("clos.max_freq", clos_param.max_freq_mhz, + (SST_CLOS_CONFIG_0_OFFSET + clos_param.clos * SST_REG_SIZE), + SST_CLOS_CONFIG_MAX_START, SST_CLOS_CONFIG_MAX_WIDTH, + SST_MUL_FACTOR_FREQ) + _read_cp_info("clos.prio", clos_param.prop_prio, + (SST_CLOS_CONFIG_0_OFFSET + clos_param.clos * SST_REG_SIZE), + SST_CLOS_CONFIG_PRIO_START, SST_CLOS_CONFIG_PRIO_WIDTH, + SST_MUL_FACTOR_NONE) + + if (copy_to_user(argp, &clos_param, sizeof(clos_param))) + return -EFAULT; + } + + return 0; +} + +#define SST_CLOS_ASSOC_0_OFFSET 56 +#define SST_CLOS_ASSOC_CPUS_PER_REG 16 +#define SST_CLOS_ASSOC_BITS_PER_CPU 4 + +static long isst_if_clos_assoc(void __user *argp) +{ + struct isst_if_clos_assoc_cmds assoc_cmds; + unsigned char __user *ptr; + int i; + + /* Each multi command has u16 command count as the first field */ + if (copy_from_user(&assoc_cmds, argp, sizeof(assoc_cmds))) + return -EFAULT; + + if (!assoc_cmds.cmd_count || assoc_cmds.cmd_count > ISST_IF_CMD_LIMIT) + return -EINVAL; + + ptr = argp + offsetof(struct isst_if_clos_assoc_cmds, assoc_info); + for (i = 0; i < assoc_cmds.cmd_count; ++i) { + struct tpmi_per_power_domain_info *power_domain_info; + struct isst_if_clos_assoc clos_assoc; + int punit_id, punit_cpu_no, pkg_id; + struct tpmi_sst_struct *sst_inst; + int offset, shift, cpu; + u64 val, mask, clos; + + if (copy_from_user(&clos_assoc, ptr, sizeof(clos_assoc))) + return -EFAULT; + + if (clos_assoc.socket_id > topology_max_packages()) + return -EINVAL; + + cpu = clos_assoc.logical_cpu; + clos = clos_assoc.clos; + + if (assoc_cmds.punit_cpu_map) + punit_cpu_no = cpu; + else + return -EOPNOTSUPP; + + if (punit_cpu_no < 0) + return -EINVAL; + + punit_id = clos_assoc.power_domain_id; + pkg_id = clos_assoc.socket_id; + + sst_inst = isst_common.sst_inst[pkg_id]; + + if (clos_assoc.power_domain_id > sst_inst->number_of_power_domains) + return -EINVAL; + + power_domain_info = &sst_inst->power_domain_info[punit_id]; + + offset = SST_CLOS_ASSOC_0_OFFSET + + (punit_cpu_no / SST_CLOS_ASSOC_CPUS_PER_REG) * SST_REG_SIZE; + shift = punit_cpu_no % SST_CLOS_ASSOC_CPUS_PER_REG; + shift *= SST_CLOS_ASSOC_BITS_PER_CPU; + + val = readq(power_domain_info->sst_base + + power_domain_info->sst_header.cp_offset + offset); + if (assoc_cmds.get_set) { + mask = GENMASK_ULL((shift + SST_CLOS_ASSOC_BITS_PER_CPU - 1), shift); + val &= ~mask; + val |= (clos << shift); + writeq(val, power_domain_info->sst_base + + power_domain_info->sst_header.cp_offset + offset); + } else { + val >>= shift; + clos_assoc.clos = val & GENMASK(SST_CLOS_ASSOC_BITS_PER_CPU - 1, 0); + if (copy_to_user(ptr, &clos_assoc, sizeof(clos_assoc))) + return -EFAULT; + } + + ptr += sizeof(clos_assoc); + } + + return 0; +} + static int isst_if_get_tpmi_instance_count(void __user *argp) { struct isst_tpmi_instance_count tpmi_inst; @@ -400,6 +655,15 @@ static long isst_if_def_ioctl(struct file *file, unsigned int cmd, case ISST_IF_COUNT_TPMI_INSTANCES: ret = isst_if_get_tpmi_instance_count(argp); break; + case ISST_IF_CORE_POWER_STATE: + ret = isst_if_core_power_state(argp); + break; + case ISST_IF_CLOS_PARAM: + ret = isst_if_clos_param(argp); + break; + case ISST_IF_CLOS_ASSOC: + ret = isst_if_clos_assoc(argp); + break; default: break; } diff --git a/include/uapi/linux/isst_if.h b/include/uapi/linux/isst_if.h index bf32d959f6e8..32687d8023ef 100644 --- a/include/uapi/linux/isst_if.h +++ b/include/uapi/linux/isst_if.h @@ -163,6 +163,82 @@ struct isst_if_msr_cmds { struct isst_if_msr_cmd msr_cmd[1]; }; +/** + * struct isst_core_power - Structure to get/set core_power feature + * @get_set: 0: Get, 1: Set + * @socket_id: Socket/package id + * @power_domain: Power Domain id + * @enable: Feature enable status + * @priority_type: Priority type for the feature (ordered/proportional) + * + * Structure to get/set core_power feature state using IOCTL + * ISST_IF_CORE_POWER_STATE. + */ +struct isst_core_power { + __u8 get_set; + __u8 socket_id; + __u8 power_domain_id; + __u8 enable; + __u8 supported; + __u8 priority_type; +}; + +/** + * struct isst_clos_param - Structure to get/set clos praram + * @get_set: 0: Get, 1: Set + * @socket_id: Socket/package id + * @power_domain: Power Domain id + * clos: Clos ID for the parameters + * min_freq_mhz: Minimum frequency in MHz + * max_freq_mhz: Maximum frequency in MHz + * prop_prio: Proportional priority from 0-15 + * + * Structure to get/set per clos property using IOCTL + * ISST_IF_CLOS_PARAM. + */ +struct isst_clos_param { + __u8 get_set; + __u8 socket_id; + __u8 power_domain_id; + __u8 clos; + __u16 min_freq_mhz; + __u16 max_freq_mhz; + __u8 prop_prio; +}; + +/** + * struct isst_if_clos_assoc - Structure to assign clos to a CPU + * @socket_id: Socket/package id + * @power_domain: Power Domain id + * @logical_cpu: CPU number + * @clos: Clos ID to assign to the logical CPU + * + * Structure to get/set core_power feature. + */ +struct isst_if_clos_assoc { + __u8 socket_id; + __u8 power_domain_id; + __u16 logical_cpu; + __u16 clos; +}; + +/** + * struct isst_if_clos_assoc_cmds - Structure to assign clos to CPUs + * @cmd_count: Number of cmds (cpus) in this request + * @get_set: Request is for get or set + * @punit_cpu_map: Set to 1 if the CPU number is punit numbering not + * Linux CPU number + * + * Structure used to get/set associate CPUs to clos using IOCTL + * ISST_IF_CLOS_ASSOC. + */ +struct isst_if_clos_assoc_cmds { + __u16 cmd_count; + __u16 get_set; + __u16 punit_cpu_map; + struct isst_if_clos_assoc assoc_info[1]; +}; + /** * struct isst_tpmi_instance_count - Get number of TPMI instances per socket * @socket_id: Socket/package id @@ -186,5 +262,8 @@ struct isst_tpmi_instance_count { #define ISST_IF_MSR_COMMAND _IOWR(ISST_IF_MAGIC, 4, struct isst_if_msr_cmds *) #define ISST_IF_COUNT_TPMI_INSTANCES _IOR(ISST_IF_MAGIC, 5, struct isst_tpmi_instance_count *) +#define ISST_IF_CORE_POWER_STATE _IOWR(ISST_IF_MAGIC, 6, struct isst_core_power *) +#define ISST_IF_CLOS_PARAM _IOWR(ISST_IF_MAGIC, 7, struct isst_clos_param *) +#define ISST_IF_CLOS_ASSOC _IOWR(ISST_IF_MAGIC, 8, struct isst_if_clos_assoc_cmds *) #endif -- cgit v1.2.3 From ea009e4769fa3bd05d4c111c3b6865eb3a9be829 Mon Sep 17 00:00:00 2001 From: Srinivas Pandruvada Date: Tue, 7 Mar 2023 23:06:39 -0800 Subject: platform/x86: ISST: Add SST-PP support via TPMI This Intel Speed Select Technology - Performance Profile (SST-PP) feature introduces a mechanism that allows multiple optimized performance profiles per system. Each profile defines a set of CPUs that need to be online and rest offline to sustain a guaranteed base frequency. Five new IOCTLs are added: ISST_IF_PERF_LEVELS : Get number of performance levels ISST_IF_PERF_SET_LEVEL : Set to a new performance level ISST_IF_PERF_SET_FEATURE : Activate SST-BF/SST-TF for a performance level ISST_IF_GET_PERF_LEVEL_INFO : Get parameters for a performance level ISST_IF_GET_PERF_LEVEL_CPU_MASK : Get CPU mask for a performance level Once an instance is identified, read or write from correct MMIO offset for a given field as defined in the specification. For details on SST PP operations using intel-speed-selet utility, refer to: Documentation/admin-guide/pm/intel-speed-select.rst under the kernel documentation Signed-off-by: Srinivas Pandruvada Reviewed-by: Zhang Rui Tested-by: Pragya Tanwar Link: https://lore.kernel.org/r/20230308070642.1727167-6-srinivas.pandruvada@linux.intel.com Signed-off-by: Hans de Goede --- .../x86/intel/speed_select_if/isst_tpmi_core.c | 417 ++++++++++++++++++++- include/uapi/linux/isst_if.h | 180 +++++++++ 2 files changed, 596 insertions(+), 1 deletion(-) (limited to 'drivers/platform') diff --git a/drivers/platform/x86/intel/speed_select_if/isst_tpmi_core.c b/drivers/platform/x86/intel/speed_select_if/isst_tpmi_core.c index bc1c1f26fbf9..c9b1321dfd1b 100644 --- a/drivers/platform/x86/intel/speed_select_if/isst_tpmi_core.c +++ b/drivers/platform/x86/intel/speed_select_if/isst_tpmi_core.c @@ -18,6 +18,7 @@ */ #include +#include #include #include #include @@ -325,7 +326,7 @@ static int sst_add_perf_profiles(struct auxiliary_device *auxdev, for (i = 0; i < levels; ++i) { u64 offset; - offset = perf_level_offsets & (0xff << (i * SST_PP_OFFSET_SIZE)); + offset = perf_level_offsets & (0xffULL << (i * SST_PP_OFFSET_SIZE)); offset >>= (i * 8); offset &= 0xff; offset *= 8; /* Convert to byte from QWORD offset */ @@ -614,6 +615,405 @@ static long isst_if_clos_assoc(void __user *argp) return 0; } +#define _read_pp_info(name_str, name, offset, start, width, mult_factor)\ +{\ + u64 val, _mask;\ + \ + val = readq(power_domain_info->sst_base + power_domain_info->sst_header.pp_offset +\ + (offset));\ + _mask = GENMASK_ULL((start + width - 1), start);\ + val &= _mask;\ + val >>= start;\ + name = (val * mult_factor);\ +} + +#define _write_pp_info(name_str, name, offset, start, width, div_factor)\ +{\ + u64 val, _mask;\ + \ + val = readq(power_domain_info->sst_base + power_domain_info->sst_header.pp_offset +\ + (offset));\ + _mask = GENMASK((start + width - 1), start);\ + val &= ~_mask;\ + val |= (name / div_factor) << start;\ + writeq(val, power_domain_info->sst_base + power_domain_info->sst_header.pp_offset +\ + (offset));\ +} + +#define _read_bf_level_info(name_str, name, level, offset, start, width, mult_factor)\ +{\ + u64 val, _mask;\ + \ + val = readq(power_domain_info->sst_base +\ + power_domain_info->perf_levels[level].mmio_offset +\ + (power_domain_info->feature_offsets.bf_offset * 8) + (offset));\ + _mask = GENMASK_ULL((start + width - 1), start);\ + val &= _mask; \ + val >>= start;\ + name = (val * mult_factor);\ +} + +#define _read_tf_level_info(name_str, name, level, offset, start, width, mult_factor)\ +{\ + u64 val, _mask;\ + \ + val = readq(power_domain_info->sst_base +\ + power_domain_info->perf_levels[level].mmio_offset +\ + (power_domain_info->feature_offsets.tf_offset * 8) + (offset));\ + _mask = GENMASK_ULL((start + width - 1), start);\ + val &= _mask; \ + val >>= start;\ + name = (val * mult_factor);\ +} + +#define SST_PP_STATUS_OFFSET 32 + +#define SST_PP_LEVEL_START 0 +#define SST_PP_LEVEL_WIDTH 3 + +#define SST_PP_LOCK_START 3 +#define SST_PP_LOCK_WIDTH 1 + +#define SST_PP_FEATURE_STATE_START 8 +#define SST_PP_FEATURE_STATE_WIDTH 8 + +#define SST_BF_FEATURE_SUPPORTED_START 12 +#define SST_BF_FEATURE_SUPPORTED_WIDTH 1 + +#define SST_TF_FEATURE_SUPPORTED_START 12 +#define SST_TF_FEATURE_SUPPORTED_WIDTH 1 + +static int isst_if_get_perf_level(void __user *argp) +{ + struct isst_perf_level_info perf_level; + struct tpmi_per_power_domain_info *power_domain_info; + + if (copy_from_user(&perf_level, argp, sizeof(perf_level))) + return -EFAULT; + + power_domain_info = get_instance(perf_level.socket_id, perf_level.power_domain_id); + if (!power_domain_info) + return -EINVAL; + + perf_level.max_level = power_domain_info->max_level; + perf_level.level_mask = power_domain_info->pp_header.allowed_level_mask; + perf_level.feature_rev = power_domain_info->pp_header.feature_rev; + _read_pp_info("current_level", perf_level.current_level, SST_PP_STATUS_OFFSET, + SST_PP_LEVEL_START, SST_PP_LEVEL_WIDTH, SST_MUL_FACTOR_NONE) + _read_pp_info("locked", perf_level.locked, SST_PP_STATUS_OFFSET, + SST_PP_LOCK_START, SST_PP_LEVEL_WIDTH, SST_MUL_FACTOR_NONE) + _read_pp_info("feature_state", perf_level.feature_state, SST_PP_STATUS_OFFSET, + SST_PP_FEATURE_STATE_START, SST_PP_FEATURE_STATE_WIDTH, SST_MUL_FACTOR_NONE) + perf_level.enabled = !!(power_domain_info->sst_header.cap_mask & BIT(1)); + + _read_bf_level_info("bf_support", perf_level.sst_bf_support, 0, 0, + SST_BF_FEATURE_SUPPORTED_START, SST_BF_FEATURE_SUPPORTED_WIDTH, + SST_MUL_FACTOR_NONE); + _read_tf_level_info("tf_support", perf_level.sst_tf_support, 0, 0, + SST_TF_FEATURE_SUPPORTED_START, SST_TF_FEATURE_SUPPORTED_WIDTH, + SST_MUL_FACTOR_NONE); + + if (copy_to_user(argp, &perf_level, sizeof(perf_level))) + return -EFAULT; + + return 0; +} + +#define SST_PP_CONTROL_OFFSET 24 +#define SST_PP_LEVEL_CHANGE_TIME_MS 5 +#define SST_PP_LEVEL_CHANGE_RETRY_COUNT 3 + +static int isst_if_set_perf_level(void __user *argp) +{ + struct isst_perf_level_control perf_level; + struct tpmi_per_power_domain_info *power_domain_info; + int level, retry = 0; + + if (disable_dynamic_sst_features()) + return -EFAULT; + + if (copy_from_user(&perf_level, argp, sizeof(perf_level))) + return -EFAULT; + + power_domain_info = get_instance(perf_level.socket_id, perf_level.power_domain_id); + if (!power_domain_info) + return -EINVAL; + + if (!(power_domain_info->pp_header.allowed_level_mask & BIT(perf_level.level))) + return -EINVAL; + + _read_pp_info("current_level", level, SST_PP_STATUS_OFFSET, + SST_PP_LEVEL_START, SST_PP_LEVEL_WIDTH, SST_MUL_FACTOR_NONE) + + /* If the requested new level is same as the current level, reject */ + if (perf_level.level == level) + return -EINVAL; + + _write_pp_info("perf_level", perf_level.level, SST_PP_CONTROL_OFFSET, + SST_PP_LEVEL_START, SST_PP_LEVEL_WIDTH, SST_MUL_FACTOR_NONE) + + /* It is possible that firmware is busy (although unlikely), so retry */ + do { + /* Give time to FW to process */ + msleep(SST_PP_LEVEL_CHANGE_TIME_MS); + + _read_pp_info("current_level", level, SST_PP_STATUS_OFFSET, + SST_PP_LEVEL_START, SST_PP_LEVEL_WIDTH, SST_MUL_FACTOR_NONE) + + /* Check if the new level is active */ + if (perf_level.level == level) + break; + + } while (retry++ < SST_PP_LEVEL_CHANGE_RETRY_COUNT); + + /* If the level change didn't happen, return fault */ + if (perf_level.level != level) + return -EFAULT; + + /* Reset the feature state on level change */ + _write_pp_info("perf_feature", 0, SST_PP_CONTROL_OFFSET, + SST_PP_FEATURE_STATE_START, SST_PP_FEATURE_STATE_WIDTH, + SST_MUL_FACTOR_NONE) + + /* Give time to FW to process */ + msleep(SST_PP_LEVEL_CHANGE_TIME_MS); + + return 0; +} + +static int isst_if_set_perf_feature(void __user *argp) +{ + struct isst_perf_feature_control perf_feature; + struct tpmi_per_power_domain_info *power_domain_info; + + if (disable_dynamic_sst_features()) + return -EFAULT; + + if (copy_from_user(&perf_feature, argp, sizeof(perf_feature))) + return -EFAULT; + + power_domain_info = get_instance(perf_feature.socket_id, perf_feature.power_domain_id); + if (!power_domain_info) + return -EINVAL; + + _write_pp_info("perf_feature", perf_feature.feature, SST_PP_CONTROL_OFFSET, + SST_PP_FEATURE_STATE_START, SST_PP_FEATURE_STATE_WIDTH, + SST_MUL_FACTOR_NONE) + + return 0; +} + +#define _read_pp_level_info(name_str, name, level, offset, start, width, mult_factor)\ +{\ + u64 val, _mask;\ + \ + val = readq(power_domain_info->sst_base +\ + power_domain_info->perf_levels[level].mmio_offset +\ + (power_domain_info->feature_offsets.pp_offset * 8) + (offset));\ + _mask = GENMASK_ULL((start + width - 1), start);\ + val &= _mask; \ + val >>= start;\ + name = (val * mult_factor);\ +} + +#define SST_PP_INFO_0_OFFSET 0 +#define SST_PP_INFO_1_OFFSET 8 +#define SST_PP_INFO_2_OFFSET 16 +#define SST_PP_INFO_3_OFFSET 24 + +/* SST_PP_INFO_4_OFFSET to SST_PP_INFO_9_OFFSET are trl levels */ +#define SST_PP_INFO_4_OFFSET 32 + +#define SST_PP_INFO_10_OFFSET 80 +#define SST_PP_INFO_11_OFFSET 88 + +#define SST_PP_P1_SSE_START 0 +#define SST_PP_P1_SSE_WIDTH 8 + +#define SST_PP_P1_AVX2_START 8 +#define SST_PP_P1_AVX2_WIDTH 8 + +#define SST_PP_P1_AVX512_START 16 +#define SST_PP_P1_AVX512_WIDTH 8 + +#define SST_PP_P1_AMX_START 24 +#define SST_PP_P1_AMX_WIDTH 8 + +#define SST_PP_TDP_START 32 +#define SST_PP_TDP_WIDTH 15 + +#define SST_PP_T_PROCHOT_START 47 +#define SST_PP_T_PROCHOT_WIDTH 8 + +#define SST_PP_MAX_MEMORY_FREQ_START 55 +#define SST_PP_MAX_MEMORY_FREQ_WIDTH 7 + +#define SST_PP_COOLING_TYPE_START 62 +#define SST_PP_COOLING_TYPE_WIDTH 2 + +#define SST_PP_TRL_0_RATIO_0_START 0 +#define SST_PP_TRL_0_RATIO_0_WIDTH 8 + +#define SST_PP_TRL_CORES_BUCKET_0_START 0 +#define SST_PP_TRL_CORES_BUCKET_0_WIDTH 8 + +#define SST_PP_CORE_RATIO_P0_START 0 +#define SST_PP_CORE_RATIO_P0_WIDTH 8 + +#define SST_PP_CORE_RATIO_P1_START 8 +#define SST_PP_CORE_RATIO_P1_WIDTH 8 + +#define SST_PP_CORE_RATIO_PN_START 16 +#define SST_PP_CORE_RATIO_PN_WIDTH 8 + +#define SST_PP_CORE_RATIO_PM_START 24 +#define SST_PP_CORE_RATIO_PM_WIDTH 8 + +#define SST_PP_CORE_RATIO_P0_FABRIC_START 32 +#define SST_PP_CORE_RATIO_P0_FABRIC_WIDTH 8 + +#define SST_PP_CORE_RATIO_P1_FABRIC_START 40 +#define SST_PP_CORE_RATIO_P1_FABRIC_WIDTH 8 + +#define SST_PP_CORE_RATIO_PM_FABRIC_START 48 +#define SST_PP_CORE_RATIO_PM_FABRIC_WIDTH 8 + +static int isst_if_get_perf_level_info(void __user *argp) +{ + struct isst_perf_level_data_info perf_level; + struct tpmi_per_power_domain_info *power_domain_info; + int i, j; + + if (copy_from_user(&perf_level, argp, sizeof(perf_level))) + return -EFAULT; + + power_domain_info = get_instance(perf_level.socket_id, perf_level.power_domain_id); + if (!power_domain_info) + return -EINVAL; + + if (perf_level.level > power_domain_info->max_level) + return -EINVAL; + + if (!(power_domain_info->pp_header.level_en_mask & BIT(perf_level.level))) + return -EINVAL; + + _read_pp_level_info("tdp_ratio", perf_level.tdp_ratio, perf_level.level, + SST_PP_INFO_0_OFFSET, SST_PP_P1_SSE_START, SST_PP_P1_SSE_WIDTH, + SST_MUL_FACTOR_NONE) + _read_pp_level_info("base_freq_mhz", perf_level.base_freq_mhz, perf_level.level, + SST_PP_INFO_0_OFFSET, SST_PP_P1_SSE_START, SST_PP_P1_SSE_WIDTH, + SST_MUL_FACTOR_FREQ) + _read_pp_level_info("base_freq_avx2_mhz", perf_level.base_freq_avx2_mhz, perf_level.level, + SST_PP_INFO_0_OFFSET, SST_PP_P1_AVX2_START, SST_PP_P1_AVX2_WIDTH, + SST_MUL_FACTOR_FREQ) + _read_pp_level_info("base_freq_avx512_mhz", perf_level.base_freq_avx512_mhz, + perf_level.level, SST_PP_INFO_0_OFFSET, SST_PP_P1_AVX512_START, + SST_PP_P1_AVX512_WIDTH, SST_MUL_FACTOR_FREQ) + _read_pp_level_info("base_freq_amx_mhz", perf_level.base_freq_amx_mhz, perf_level.level, + SST_PP_INFO_0_OFFSET, SST_PP_P1_AMX_START, SST_PP_P1_AMX_WIDTH, + SST_MUL_FACTOR_FREQ) + + _read_pp_level_info("thermal_design_power_w", perf_level.thermal_design_power_w, + perf_level.level, SST_PP_INFO_1_OFFSET, SST_PP_TDP_START, + SST_PP_TDP_WIDTH, SST_MUL_FACTOR_NONE) + perf_level.thermal_design_power_w /= 8; /* units are in 1/8th watt */ + _read_pp_level_info("tjunction_max_c", perf_level.tjunction_max_c, perf_level.level, + SST_PP_INFO_1_OFFSET, SST_PP_T_PROCHOT_START, SST_PP_T_PROCHOT_WIDTH, + SST_MUL_FACTOR_NONE) + _read_pp_level_info("max_memory_freq_mhz", perf_level.max_memory_freq_mhz, + perf_level.level, SST_PP_INFO_1_OFFSET, SST_PP_MAX_MEMORY_FREQ_START, + SST_PP_MAX_MEMORY_FREQ_WIDTH, SST_MUL_FACTOR_FREQ) + _read_pp_level_info("cooling_type", perf_level.cooling_type, perf_level.level, + SST_PP_INFO_1_OFFSET, SST_PP_COOLING_TYPE_START, + SST_PP_COOLING_TYPE_WIDTH, SST_MUL_FACTOR_NONE) + + for (i = 0; i < TRL_MAX_LEVELS; ++i) { + for (j = 0; j < TRL_MAX_BUCKETS; ++j) + _read_pp_level_info("trl*_bucket*_freq_mhz", + perf_level.trl_freq_mhz[i][j], perf_level.level, + SST_PP_INFO_4_OFFSET + (i * SST_PP_TRL_0_RATIO_0_WIDTH), + j * SST_PP_TRL_0_RATIO_0_WIDTH, + SST_PP_TRL_0_RATIO_0_WIDTH, + SST_MUL_FACTOR_FREQ); + } + + for (i = 0; i < TRL_MAX_BUCKETS; ++i) + _read_pp_level_info("bucket*_core_count", perf_level.bucket_core_counts[i], + perf_level.level, SST_PP_INFO_10_OFFSET, + SST_PP_TRL_CORES_BUCKET_0_WIDTH * i, + SST_PP_TRL_CORES_BUCKET_0_WIDTH, SST_MUL_FACTOR_NONE) + + perf_level.max_buckets = TRL_MAX_BUCKETS; + perf_level.max_trl_levels = TRL_MAX_LEVELS; + + _read_pp_level_info("p0_freq_mhz", perf_level.p0_freq_mhz, perf_level.level, + SST_PP_INFO_11_OFFSET, SST_PP_CORE_RATIO_P0_START, + SST_PP_CORE_RATIO_P0_WIDTH, SST_MUL_FACTOR_FREQ) + _read_pp_level_info("p1_freq_mhz", perf_level.p1_freq_mhz, perf_level.level, + SST_PP_INFO_11_OFFSET, SST_PP_CORE_RATIO_P1_START, + SST_PP_CORE_RATIO_P1_WIDTH, SST_MUL_FACTOR_FREQ) + _read_pp_level_info("pn_freq_mhz", perf_level.pn_freq_mhz, perf_level.level, + SST_PP_INFO_11_OFFSET, SST_PP_CORE_RATIO_PN_START, + SST_PP_CORE_RATIO_PN_WIDTH, SST_MUL_FACTOR_FREQ) + _read_pp_level_info("pm_freq_mhz", perf_level.pm_freq_mhz, perf_level.level, + SST_PP_INFO_11_OFFSET, SST_PP_CORE_RATIO_PM_START, + SST_PP_CORE_RATIO_PM_WIDTH, SST_MUL_FACTOR_FREQ) + _read_pp_level_info("p0_fabric_freq_mhz", perf_level.p0_fabric_freq_mhz, + perf_level.level, SST_PP_INFO_11_OFFSET, + SST_PP_CORE_RATIO_P0_FABRIC_START, + SST_PP_CORE_RATIO_P0_FABRIC_WIDTH, SST_MUL_FACTOR_FREQ) + _read_pp_level_info("p1_fabric_freq_mhz", perf_level.p1_fabric_freq_mhz, + perf_level.level, SST_PP_INFO_11_OFFSET, + SST_PP_CORE_RATIO_P1_FABRIC_START, + SST_PP_CORE_RATIO_P1_FABRIC_WIDTH, SST_MUL_FACTOR_FREQ) + _read_pp_level_info("pm_fabric_freq_mhz", perf_level.pm_fabric_freq_mhz, + perf_level.level, SST_PP_INFO_11_OFFSET, + SST_PP_CORE_RATIO_PM_FABRIC_START, + SST_PP_CORE_RATIO_PM_FABRIC_WIDTH, SST_MUL_FACTOR_FREQ) + + if (copy_to_user(argp, &perf_level, sizeof(perf_level))) + return -EFAULT; + + return 0; +} + +#define SST_PP_FUSED_CORE_COUNT_START 0 +#define SST_PP_FUSED_CORE_COUNT_WIDTH 8 + +#define SST_PP_RSLVD_CORE_COUNT_START 8 +#define SST_PP_RSLVD_CORE_COUNT_WIDTH 8 + +#define SST_PP_RSLVD_CORE_MASK_START 0 +#define SST_PP_RSLVD_CORE_MASK_WIDTH 64 + +static int isst_if_get_perf_level_mask(void __user *argp) +{ + static struct isst_perf_level_cpu_mask cpumask; + struct tpmi_per_power_domain_info *power_domain_info; + u64 mask; + + if (copy_from_user(&cpumask, argp, sizeof(cpumask))) + return -EFAULT; + + power_domain_info = get_instance(cpumask.socket_id, cpumask.power_domain_id); + if (!power_domain_info) + return -EINVAL; + + _read_pp_level_info("mask", mask, cpumask.level, SST_PP_INFO_2_OFFSET, + SST_PP_RSLVD_CORE_MASK_START, SST_PP_RSLVD_CORE_MASK_WIDTH, + SST_MUL_FACTOR_NONE) + + cpumask.mask = mask; + + if (!cpumask.punit_cpu_map) + return -EOPNOTSUPP; + + if (copy_to_user(argp, &cpumask, sizeof(cpumask))) + return -EFAULT; + + return 0; +} + static int isst_if_get_tpmi_instance_count(void __user *argp) { struct isst_tpmi_instance_count tpmi_inst; @@ -664,6 +1064,21 @@ static long isst_if_def_ioctl(struct file *file, unsigned int cmd, case ISST_IF_CLOS_ASSOC: ret = isst_if_clos_assoc(argp); break; + case ISST_IF_PERF_LEVELS: + ret = isst_if_get_perf_level(argp); + break; + case ISST_IF_PERF_SET_LEVEL: + ret = isst_if_set_perf_level(argp); + break; + case ISST_IF_PERF_SET_FEATURE: + ret = isst_if_set_perf_feature(argp); + break; + case ISST_IF_GET_PERF_LEVEL_INFO: + ret = isst_if_get_perf_level_info(argp); + break; + case ISST_IF_GET_PERF_LEVEL_CPU_MASK: + ret = isst_if_get_perf_level_mask(argp); + break; default: break; } diff --git a/include/uapi/linux/isst_if.h b/include/uapi/linux/isst_if.h index 32687d8023ef..c4b350ea5cbe 100644 --- a/include/uapi/linux/isst_if.h +++ b/include/uapi/linux/isst_if.h @@ -254,6 +254,178 @@ struct isst_tpmi_instance_count { __u16 valid_mask; }; +/** + * struct isst_perf_level_info - Structure to get information on SST-PP levels + * @socket_id: Socket/package id + * @power_domain: Power Domain id + * @logical_cpu: CPU number + * @clos: Clos ID to assign to the logical CPU + * @max_level: Maximum performance level supported by the platform + * @feature_rev: The feature revision for SST-PP supported by the platform + * @level_mask: Mask of supported performance levels + * @current_level: Current performance level + * @feature_state: SST-BF and SST-TF (enabled/disabled) status at current level + * @locked: SST-PP performance level change is locked/unlocked + * @enabled: SST-PP feature is enabled or not + * @sst-tf_support: SST-TF support status at this level + * @sst-bf_support: SST-BF support status at this level + * + * Structure to get SST-PP details using IOCTL ISST_IF_PERF_LEVELS. + */ +struct isst_perf_level_info { + __u8 socket_id; + __u8 power_domain_id; + __u8 max_level; + __u8 feature_rev; + __u8 level_mask; + __u8 current_level; + __u8 feature_state; + __u8 locked; + __u8 enabled; + __u8 sst_tf_support; + __u8 sst_bf_support; +}; + +/** + * struct isst_perf_level_control - Structure to set SST-PP level + * @socket_id: Socket/package id + * @power_domain: Power Domain id + * @level: level to set + * + * Structure used change SST-PP level using IOCTL ISST_IF_PERF_SET_LEVEL. + */ +struct isst_perf_level_control { + __u8 socket_id; + __u8 power_domain_id; + __u8 level; +}; + +/** + * struct isst_perf_feature_control - Structure to activate SST-BF/SST-TF + * @socket_id: Socket/package id + * @power_domain: Power Domain id + * @feature: bit 0 = SST-BF state, bit 1 = SST-TF state + * + * Structure used to enable SST-BF/SST-TF using IOCTL ISST_IF_PERF_SET_FEATURE. + */ +struct isst_perf_feature_control { + __u8 socket_id; + __u8 power_domain_id; + __u8 feature; +}; + +#define TRL_MAX_BUCKETS 8 +#define TRL_MAX_LEVELS 6 + +/** + * struct isst_perf_level_data_info - Structure to get SST-PP level details + * @socket_id: Socket/package id + * @power_domain: Power Domain id + * @level: SST-PP level for which caller wants to get information + * @tdp_ratio: TDP Ratio + * @base_freq_mhz: Base frequency in MHz + * @base_freq_avx2_mhz: AVX2 Base frequency in MHz + * @base_freq_avx512_mhz: AVX512 base frequency in MHz + * @base_freq_amx_mhz: AMX base frequency in MHz + * @thermal_design_power_w: Thermal design (TDP) power + * @tjunction_max_c: Max junction temperature + * @max_memory_freq_mhz: Max memory frequency in MHz + * @cooling_type: Type of cooling is used + * @p0_freq_mhz: core maximum frequency + * @p1_freq_mhz: Core TDP frequency + * @pn_freq_mhz: Core maximum efficiency frequency + * @pm_freq_mhz: Core minimum frequency + * @p0_fabric_freq_mhz: Fabric (Uncore) maximum frequency + * @p1_fabric_freq_mhz: Fabric (Uncore) TDP frequency + * @pn_fabric_freq_mhz: Fabric (Uncore) minimum efficiency frequency + * @pm_fabric_freq_mhz: Fabric (Uncore) minimum frequency + * @max_buckets: Maximum trl buckets + * @max_trl_levels: Maximum trl levels + * @bucket_core_counts[TRL_MAX_BUCKETS]: Number of cores per bucket + * @trl_freq_mhz[TRL_MAX_LEVELS][TRL_MAX_BUCKETS]: maximum frequency + * for a bucket and trl level + * + * Structure used to get information on frequencies and TDP for a SST-PP + * level using ISST_IF_GET_PERF_LEVEL_INFO. + */ +struct isst_perf_level_data_info { + __u8 socket_id; + __u8 power_domain_id; + __u16 level; + __u16 tdp_ratio; + __u16 base_freq_mhz; + __u16 base_freq_avx2_mhz; + __u16 base_freq_avx512_mhz; + __u16 base_freq_amx_mhz; + __u16 thermal_design_power_w; + __u16 tjunction_max_c; + __u16 max_memory_freq_mhz; + __u16 cooling_type; + __u16 p0_freq_mhz; + __u16 p1_freq_mhz; + __u16 pn_freq_mhz; + __u16 pm_freq_mhz; + __u16 p0_fabric_freq_mhz; + __u16 p1_fabric_freq_mhz; + __u16 pn_fabric_freq_mhz; + __u16 pm_fabric_freq_mhz; + __u16 max_buckets; + __u16 max_trl_levels; + __u16 bucket_core_counts[TRL_MAX_BUCKETS]; + __u16 trl_freq_mhz[TRL_MAX_LEVELS][TRL_MAX_BUCKETS]; +}; + +/** + * struct isst_perf_level_cpu_mask - Structure to get SST-PP level CPU mask + * @socket_id: Socket/package id + * @power_domain: Power Domain id + * @level: SST-PP level for which caller wants to get information + * @punit_cpu_map: Set to 1 if the CPU number is punit numbering not + * Linux CPU number. If 0 CPU buffer is copied to user space + * supplied cpu_buffer of size cpu_buffer_size. Punit + * cpu mask is copied to "mask" field. + * @mask: cpu mask for this PP level (punit CPU numbering) + * @cpu_buffer_size: size of cpu_buffer also used to return the copied CPU + * buffer size. + * @cpu_buffer: Buffer to copy CPU mask when punit_cpu_map is 0 + * + * Structure used to get cpumask for a SST-PP level using + * IOCTL ISST_IF_GET_PERF_LEVEL_CPU_MASK. Also used to get CPU mask for + * IOCTL ISST_IF_GET_BASE_FREQ_CPU_MASK for SST-BF. + */ +struct isst_perf_level_cpu_mask { + __u8 socket_id; + __u8 power_domain_id; + __u8 level; + __u8 punit_cpu_map; + __u64 mask; + __u16 cpu_buffer_size; + __s8 cpu_buffer[1]; +}; + +/** + * struct isst_base_freq_info - Structure to get SST-BF frequencies + * @socket_id: Socket/package id + * @power_domain: Power Domain id + * @level: SST-PP level for which caller wants to get information + * @high_base_freq_mhz: High priority CPU base frequency + * @low_base_freq_mhz: Low priority CPU base frequency + * @tjunction_max_c: Max junction temperature + * @thermal_design_power_w: Thermal design power in watts + * + * Structure used to get SST-BF information using + * IOCTL ISST_IF_GET_BASE_FREQ_INFO. + */ +struct isst_base_freq_info { + __u8 socket_id; + __u8 power_domain_id; + __u16 level; + __u16 high_base_freq_mhz; + __u16 low_base_freq_mhz; + __u16 tjunction_max_c; + __u16 thermal_design_power_w; +}; + #define ISST_IF_MAGIC 0xFE #define ISST_IF_GET_PLATFORM_INFO _IOR(ISST_IF_MAGIC, 0, struct isst_if_platform_info *) #define ISST_IF_GET_PHY_ID _IOWR(ISST_IF_MAGIC, 1, struct isst_if_cpu_map *) @@ -266,4 +438,12 @@ struct isst_tpmi_instance_count { #define ISST_IF_CLOS_PARAM _IOWR(ISST_IF_MAGIC, 7, struct isst_clos_param *) #define ISST_IF_CLOS_ASSOC _IOWR(ISST_IF_MAGIC, 8, struct isst_if_clos_assoc_cmds *) +#define ISST_IF_PERF_LEVELS _IOWR(ISST_IF_MAGIC, 9, struct isst_perf_level_info *) +#define ISST_IF_PERF_SET_LEVEL _IOW(ISST_IF_MAGIC, 10, struct isst_perf_level_control *) +#define ISST_IF_PERF_SET_FEATURE _IOW(ISST_IF_MAGIC, 11, struct isst_perf_feature_control *) +#define ISST_IF_GET_PERF_LEVEL_INFO _IOR(ISST_IF_MAGIC, 12, struct isst_perf_level_data_info *) +#define ISST_IF_GET_PERF_LEVEL_CPU_MASK _IOR(ISST_IF_MAGIC, 13, struct isst_perf_level_cpu_mask *) +#define ISST_IF_GET_BASE_FREQ_INFO _IOR(ISST_IF_MAGIC, 14, struct isst_base_freq_info *) +#define ISST_IF_GET_BASE_FREQ_CPU_MASK _IOR(ISST_IF_MAGIC, 15, struct isst_perf_level_cpu_mask *) + #endif -- cgit v1.2.3 From 06a61df83209ac7f376616f83f3485217c703d50 Mon Sep 17 00:00:00 2001 From: Srinivas Pandruvada Date: Tue, 7 Mar 2023 23:06:40 -0800 Subject: platform/x86: ISST: Add SST-BF support via TPMI The Intel Speed Select Technology - Base Frequency (SST-BF) feature lets the user control base frequency. If some critical workload threads demand constant high guaranteed performance, then this feature can be used to execute the thread at higher base frequency on specific sets of CPUs (high priority CPUs) at the cost of lower base frequency (low priority CPUs) on other CPUs. Two new IOCTLs are added: ISST_IF_GET_BASE_FREQ_INFO : Get frequency information for high and low priority CPUs ISST_IF_GET_BASE_FREQ_CPU_MASK : CPUs capable of higher frequency Once an instance is identified, read or write from correct MMIO offset for a given field as defined in the specification. For details on SST-BF operations using intel-speed-selet utility, refer to: Documentation/admin-guide/pm/intel-speed-select.rst under the kernel documentation Signed-off-by: Srinivas Pandruvada Reviewed-by: Zhang Rui Tested-by: Pragya Tanwar Link: https://lore.kernel.org/r/20230308070642.1727167-7-srinivas.pandruvada@linux.intel.com Signed-off-by: Hans de Goede --- .../x86/intel/speed_select_if/isst_tpmi_core.c | 87 ++++++++++++++++++++++ 1 file changed, 87 insertions(+) (limited to 'drivers/platform') diff --git a/drivers/platform/x86/intel/speed_select_if/isst_tpmi_core.c b/drivers/platform/x86/intel/speed_select_if/isst_tpmi_core.c index c9b1321dfd1b..0566c8d30181 100644 --- a/drivers/platform/x86/intel/speed_select_if/isst_tpmi_core.c +++ b/drivers/platform/x86/intel/speed_select_if/isst_tpmi_core.c @@ -1014,6 +1014,87 @@ static int isst_if_get_perf_level_mask(void __user *argp) return 0; } +#define SST_BF_INFO_0_OFFSET 0 +#define SST_BF_INFO_1_OFFSET 8 + +#define SST_BF_P1_HIGH_START 13 +#define SST_BF_P1_HIGH_WIDTH 8 + +#define SST_BF_P1_LOW_START 21 +#define SST_BF_P1_LOW_WIDTH 8 + +#define SST_BF_T_PROHOT_START 38 +#define SST_BF_T_PROHOT_WIDTH 8 + +#define SST_BF_TDP_START 46 +#define SST_BF_TDP_WIDTH 15 + +static int isst_if_get_base_freq_info(void __user *argp) +{ + static struct isst_base_freq_info base_freq; + struct tpmi_per_power_domain_info *power_domain_info; + + if (copy_from_user(&base_freq, argp, sizeof(base_freq))) + return -EFAULT; + + power_domain_info = get_instance(base_freq.socket_id, base_freq.power_domain_id); + if (!power_domain_info) + return -EINVAL; + + if (base_freq.level > power_domain_info->max_level) + return -EINVAL; + + _read_bf_level_info("p1_high", base_freq.high_base_freq_mhz, base_freq.level, + SST_BF_INFO_0_OFFSET, SST_BF_P1_HIGH_START, SST_BF_P1_HIGH_WIDTH, + SST_MUL_FACTOR_FREQ) + _read_bf_level_info("p1_low", base_freq.low_base_freq_mhz, base_freq.level, + SST_BF_INFO_0_OFFSET, SST_BF_P1_LOW_START, SST_BF_P1_LOW_WIDTH, + SST_MUL_FACTOR_FREQ) + _read_bf_level_info("BF-TJ", base_freq.tjunction_max_c, base_freq.level, + SST_BF_INFO_0_OFFSET, SST_BF_T_PROHOT_START, SST_BF_T_PROHOT_WIDTH, + SST_MUL_FACTOR_NONE) + _read_bf_level_info("BF-tdp", base_freq.thermal_design_power_w, base_freq.level, + SST_BF_INFO_0_OFFSET, SST_BF_TDP_START, SST_BF_TDP_WIDTH, + SST_MUL_FACTOR_NONE) + base_freq.thermal_design_power_w /= 8; /*unit = 1/8th watt*/ + + if (copy_to_user(argp, &base_freq, sizeof(base_freq))) + return -EFAULT; + + return 0; +} + +#define P1_HI_CORE_MASK_START 0 +#define P1_HI_CORE_MASK_WIDTH 64 + +static int isst_if_get_base_freq_mask(void __user *argp) +{ + static struct isst_perf_level_cpu_mask cpumask; + struct tpmi_per_power_domain_info *power_domain_info; + u64 mask; + + if (copy_from_user(&cpumask, argp, sizeof(cpumask))) + return -EFAULT; + + power_domain_info = get_instance(cpumask.socket_id, cpumask.power_domain_id); + if (!power_domain_info) + return -EINVAL; + + _read_bf_level_info("BF-cpumask", mask, cpumask.level, SST_BF_INFO_1_OFFSET, + P1_HI_CORE_MASK_START, P1_HI_CORE_MASK_WIDTH, + SST_MUL_FACTOR_NONE) + + cpumask.mask = mask; + + if (!cpumask.punit_cpu_map) + return -EOPNOTSUPP; + + if (copy_to_user(argp, &cpumask, sizeof(cpumask))) + return -EFAULT; + + return 0; +} + static int isst_if_get_tpmi_instance_count(void __user *argp) { struct isst_tpmi_instance_count tpmi_inst; @@ -1079,6 +1160,12 @@ static long isst_if_def_ioctl(struct file *file, unsigned int cmd, case ISST_IF_GET_PERF_LEVEL_CPU_MASK: ret = isst_if_get_perf_level_mask(argp); break; + case ISST_IF_GET_BASE_FREQ_INFO: + ret = isst_if_get_base_freq_info(argp); + break; + case ISST_IF_GET_BASE_FREQ_CPU_MASK: + ret = isst_if_get_base_freq_mask(argp); + break; default: break; } -- cgit v1.2.3 From f8e0077a9d526cac7abbc682d83e98f834ffa909 Mon Sep 17 00:00:00 2001 From: Srinivas Pandruvada Date: Tue, 7 Mar 2023 23:06:41 -0800 Subject: platform/x86: ISST: Add SST-TF support via TPMI MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The support of Intel Speed Select Technology - Turbo Frequency (SST-TF) feature enables the ability to set different “All core turbo ratio limits” to cores based on the priority. By using this feature, some cores can be configured to get higher turbo frequency by designating them as high priority at the cost of lower or no turbo frequency on the low priority cores. One new IOCTLs are added: ISST_IF_GET_TURBO_FREQ_INFO : Get information about turbo frequency buckets Once an instance is identified, read or write from correct MMIO offset for a given field as defined in the specification. For details on SST-TF operations using intel-speed-selet utility, refer to: Documentation/admin-guide/pm/intel-speed-select.rst under the kernel documentation Signed-off-by: Srinivas Pandruvada Reviewed-by: Zhang Rui Tested-by: Pragya Tanwar Link: https://lore.kernel.org/r/20230308070642.1727167-8-srinivas.pandruvada@linux.intel.com Signed-off-by: Hans de Goede --- .../x86/intel/speed_select_if/isst_tpmi_core.c | 66 ++++++++++++++++++++++ include/uapi/linux/isst_if.h | 26 +++++++++ 2 files changed, 92 insertions(+) (limited to 'drivers/platform') diff --git a/drivers/platform/x86/intel/speed_select_if/isst_tpmi_core.c b/drivers/platform/x86/intel/speed_select_if/isst_tpmi_core.c index 0566c8d30181..5104717afe0e 100644 --- a/drivers/platform/x86/intel/speed_select_if/isst_tpmi_core.c +++ b/drivers/platform/x86/intel/speed_select_if/isst_tpmi_core.c @@ -1125,6 +1125,69 @@ static int isst_if_get_tpmi_instance_count(void __user *argp) return 0; } +#define SST_TF_INFO_0_OFFSET 0 +#define SST_TF_INFO_1_OFFSET 8 +#define SST_TF_INFO_2_OFFSET 16 + +#define SST_TF_MAX_LP_CLIP_RATIOS TRL_MAX_LEVELS + +#define SST_TF_LP_CLIP_RATIO_0_START 16 +#define SST_TF_LP_CLIP_RATIO_0_WIDTH 8 + +#define SST_TF_RATIO_0_START 0 +#define SST_TF_RATIO_0_WIDTH 8 + +#define SST_TF_NUM_CORE_0_START 0 +#define SST_TF_NUM_CORE_0_WIDTH 8 + +static int isst_if_get_turbo_freq_info(void __user *argp) +{ + static struct isst_turbo_freq_info turbo_freq; + struct tpmi_per_power_domain_info *power_domain_info; + int i, j; + + if (copy_from_user(&turbo_freq, argp, sizeof(turbo_freq))) + return -EFAULT; + + power_domain_info = get_instance(turbo_freq.socket_id, turbo_freq.power_domain_id); + if (!power_domain_info) + return -EINVAL; + + if (turbo_freq.level > power_domain_info->max_level) + return -EINVAL; + + turbo_freq.max_buckets = TRL_MAX_BUCKETS; + turbo_freq.max_trl_levels = TRL_MAX_LEVELS; + turbo_freq.max_clip_freqs = SST_TF_MAX_LP_CLIP_RATIOS; + + for (i = 0; i < turbo_freq.max_clip_freqs; ++i) + _read_tf_level_info("lp_clip*", turbo_freq.lp_clip_freq_mhz[i], + turbo_freq.level, SST_TF_INFO_0_OFFSET, + SST_TF_LP_CLIP_RATIO_0_START + + (i * SST_TF_LP_CLIP_RATIO_0_WIDTH), + SST_TF_LP_CLIP_RATIO_0_WIDTH, SST_MUL_FACTOR_FREQ) + + for (i = 0; i < TRL_MAX_LEVELS; ++i) { + for (j = 0; j < TRL_MAX_BUCKETS; ++j) + _read_tf_level_info("cydn*_bucket_*_trl", + turbo_freq.trl_freq_mhz[i][j], turbo_freq.level, + SST_TF_INFO_2_OFFSET + (i * SST_TF_RATIO_0_WIDTH), + j * SST_TF_RATIO_0_WIDTH, SST_TF_RATIO_0_WIDTH, + SST_MUL_FACTOR_FREQ) + } + + for (i = 0; i < TRL_MAX_BUCKETS; ++i) + _read_tf_level_info("bucket_*_core_count", turbo_freq.bucket_core_counts[i], + turbo_freq.level, SST_TF_INFO_1_OFFSET, + SST_TF_NUM_CORE_0_WIDTH * i, SST_TF_NUM_CORE_0_WIDTH, + SST_MUL_FACTOR_NONE) + + if (copy_to_user(argp, &turbo_freq, sizeof(turbo_freq))) + return -EFAULT; + + return 0; +} + static long isst_if_def_ioctl(struct file *file, unsigned int cmd, unsigned long arg) { @@ -1166,6 +1229,9 @@ static long isst_if_def_ioctl(struct file *file, unsigned int cmd, case ISST_IF_GET_BASE_FREQ_CPU_MASK: ret = isst_if_get_base_freq_mask(argp); break; + case ISST_IF_GET_TURBO_FREQ_INFO: + ret = isst_if_get_turbo_freq_info(argp); + break; default: break; } diff --git a/include/uapi/linux/isst_if.h b/include/uapi/linux/isst_if.h index c4b350ea5cbe..0df1a1c3caf4 100644 --- a/include/uapi/linux/isst_if.h +++ b/include/uapi/linux/isst_if.h @@ -426,6 +426,31 @@ struct isst_base_freq_info { __u16 thermal_design_power_w; }; +/** + * struct isst_turbo_freq_info - Structure to get SST-TF frequencies + * @socket_id: Socket/package id + * @power_domain: Power Domain id + * @level: SST-PP level for which caller wants to get information + * @max_clip_freqs: Maximum number of low priority core clipping frequencies + * @lp_clip_freq_mhz: Clip frequencies per trl level + * @bucket_core_counts: Maximum number of cores for a bucket + * @trl_freq_mhz: Frequencies per trl level for each bucket + * + * Structure used to get SST-TF information using + * IOCTL ISST_IF_GET_TURBO_FREQ_INFO. + */ +struct isst_turbo_freq_info { + __u8 socket_id; + __u8 power_domain_id; + __u16 level; + __u16 max_clip_freqs; + __u16 max_buckets; + __u16 max_trl_levels; + __u16 lp_clip_freq_mhz[TRL_MAX_LEVELS]; + __u16 bucket_core_counts[TRL_MAX_BUCKETS]; + __u16 trl_freq_mhz[TRL_MAX_LEVELS][TRL_MAX_BUCKETS]; +}; + #define ISST_IF_MAGIC 0xFE #define ISST_IF_GET_PLATFORM_INFO _IOR(ISST_IF_MAGIC, 0, struct isst_if_platform_info *) #define ISST_IF_GET_PHY_ID _IOWR(ISST_IF_MAGIC, 1, struct isst_if_cpu_map *) @@ -445,5 +470,6 @@ struct isst_base_freq_info { #define ISST_IF_GET_PERF_LEVEL_CPU_MASK _IOR(ISST_IF_MAGIC, 13, struct isst_perf_level_cpu_mask *) #define ISST_IF_GET_BASE_FREQ_INFO _IOR(ISST_IF_MAGIC, 14, struct isst_base_freq_info *) #define ISST_IF_GET_BASE_FREQ_CPU_MASK _IOR(ISST_IF_MAGIC, 15, struct isst_perf_level_cpu_mask *) +#define ISST_IF_GET_TURBO_FREQ_INFO _IOR(ISST_IF_MAGIC, 16, struct isst_turbo_freq_info *) #endif -- cgit v1.2.3 From 91576acab0203062b93a578a7d05c8fe9e527f80 Mon Sep 17 00:00:00 2001 From: Srinivas Pandruvada Date: Tue, 7 Mar 2023 23:06:42 -0800 Subject: platform/x86: ISST: Add suspend/resume callbacks To support S3/S4 with TPMI interface add suspend/resume callbacks. Here HW state is stored in suspend callback and restored during resume callback. The hardware state which needs to be stored/restored: - CLOS configuration - CLOS Association - SST-CP enable/disable status - SST-PP perf level setting Signed-off-by: Srinivas Pandruvada Suggested-by: Hans de Goede Reviewed-by: Zhang Rui Tested-by: Pragya Tanwar Link: https://lore.kernel.org/r/20230308070642.1727167-9-srinivas.pandruvada@linux.intel.com Reviewed-by: Hans de Goede Signed-off-by: Hans de Goede --- .../platform/x86/intel/speed_select_if/isst_tpmi.c | 19 +++++++++ .../x86/intel/speed_select_if/isst_tpmi_core.c | 49 ++++++++++++++++++++++ .../x86/intel/speed_select_if/isst_tpmi_core.h | 2 + 3 files changed, 70 insertions(+) (limited to 'drivers/platform') diff --git a/drivers/platform/x86/intel/speed_select_if/isst_tpmi.c b/drivers/platform/x86/intel/speed_select_if/isst_tpmi.c index 7b4bdeefb8bc..17972191538a 100644 --- a/drivers/platform/x86/intel/speed_select_if/isst_tpmi.c +++ b/drivers/platform/x86/intel/speed_select_if/isst_tpmi.c @@ -34,6 +34,22 @@ static void intel_sst_remove(struct auxiliary_device *auxdev) tpmi_sst_exit(); } +static int intel_sst_suspend(struct device *dev) +{ + tpmi_sst_dev_suspend(to_auxiliary_dev(dev)); + + return 0; +} + +static int intel_sst_resume(struct device *dev) +{ + tpmi_sst_dev_resume(to_auxiliary_dev(dev)); + + return 0; +} + +static DEFINE_SIMPLE_DEV_PM_OPS(intel_sst_pm, intel_sst_suspend, intel_sst_resume); + static const struct auxiliary_device_id intel_sst_id_table[] = { { .name = "intel_vsec.tpmi-sst" }, {} @@ -44,6 +60,9 @@ static struct auxiliary_driver intel_sst_aux_driver = { .id_table = intel_sst_id_table, .remove = intel_sst_remove, .probe = intel_sst_probe, + .driver = { + .pm = pm_sleep_ptr(&intel_sst_pm), + }, }; module_auxiliary_driver(intel_sst_aux_driver); diff --git a/drivers/platform/x86/intel/speed_select_if/isst_tpmi_core.c b/drivers/platform/x86/intel/speed_select_if/isst_tpmi_core.c index 5104717afe0e..cdb56a18ea17 100644 --- a/drivers/platform/x86/intel/speed_select_if/isst_tpmi_core.c +++ b/drivers/platform/x86/intel/speed_select_if/isst_tpmi_core.c @@ -229,6 +229,10 @@ struct perf_level { * @status_offset: Store the status offset for each PP-level * @sst_base: Mapped SST base IO memory * @auxdev: Auxiliary device instance enumerated this instance + * @saved_sst_cp_control: Save SST-CP control configuration to store restore for suspend/resume + * @saved_clos_configs: Save SST-CP CLOS configuration to store restore for suspend/resume + * @saved_clos_assocs: Save SST-CP CLOS association to store restore for suspend/resume + * @saved_pp_control: Save SST-PP control information to store restore for suspend/resume * * This structure is used store complete SST information for a power_domain. This information * is used to read/write request for any SST IOCTL. Each physical CPU package can have multiple @@ -250,6 +254,10 @@ struct tpmi_per_power_domain_info { struct pp_status_offset status_offset; void __iomem *sst_base; struct auxiliary_device *auxdev; + u64 saved_sst_cp_control; + u64 saved_clos_configs[4]; + u64 saved_clos_assocs[4]; + u64 saved_pp_control; }; /** @@ -1333,6 +1341,47 @@ void tpmi_sst_dev_remove(struct auxiliary_device *auxdev) } EXPORT_SYMBOL_NS_GPL(tpmi_sst_dev_remove, INTEL_TPMI_SST); +void tpmi_sst_dev_suspend(struct auxiliary_device *auxdev) +{ + struct tpmi_sst_struct *tpmi_sst = auxiliary_get_drvdata(auxdev); + struct tpmi_per_power_domain_info *power_domain_info = tpmi_sst->power_domain_info; + void __iomem *cp_base; + + cp_base = power_domain_info->sst_base + power_domain_info->sst_header.cp_offset; + power_domain_info->saved_sst_cp_control = readq(cp_base + SST_CP_CONTROL_OFFSET); + + memcpy_fromio(power_domain_info->saved_clos_configs, cp_base + SST_CLOS_CONFIG_0_OFFSET, + sizeof(power_domain_info->saved_clos_configs)); + + memcpy_fromio(power_domain_info->saved_clos_assocs, cp_base + SST_CLOS_ASSOC_0_OFFSET, + sizeof(power_domain_info->saved_clos_assocs)); + + power_domain_info->saved_pp_control = readq(power_domain_info->sst_base + + power_domain_info->sst_header.pp_offset + + SST_PP_CONTROL_OFFSET); +} +EXPORT_SYMBOL_NS_GPL(tpmi_sst_dev_suspend, INTEL_TPMI_SST); + +void tpmi_sst_dev_resume(struct auxiliary_device *auxdev) +{ + struct tpmi_sst_struct *tpmi_sst = auxiliary_get_drvdata(auxdev); + struct tpmi_per_power_domain_info *power_domain_info = tpmi_sst->power_domain_info; + void __iomem *cp_base; + + cp_base = power_domain_info->sst_base + power_domain_info->sst_header.cp_offset; + writeq(power_domain_info->saved_sst_cp_control, cp_base + SST_CP_CONTROL_OFFSET); + + memcpy_toio(cp_base + SST_CLOS_CONFIG_0_OFFSET, power_domain_info->saved_clos_configs, + sizeof(power_domain_info->saved_clos_configs)); + + memcpy_toio(cp_base + SST_CLOS_ASSOC_0_OFFSET, power_domain_info->saved_clos_assocs, + sizeof(power_domain_info->saved_clos_assocs)); + + writeq(power_domain_info->saved_pp_control, power_domain_info->sst_base + + power_domain_info->sst_header.pp_offset + SST_PP_CONTROL_OFFSET); +} +EXPORT_SYMBOL_NS_GPL(tpmi_sst_dev_resume, INTEL_TPMI_SST); + #define ISST_TPMI_API_VERSION 0x02 int tpmi_sst_init(void) diff --git a/drivers/platform/x86/intel/speed_select_if/isst_tpmi_core.h b/drivers/platform/x86/intel/speed_select_if/isst_tpmi_core.h index 356cb02273b1..900b483703f9 100644 --- a/drivers/platform/x86/intel/speed_select_if/isst_tpmi_core.h +++ b/drivers/platform/x86/intel/speed_select_if/isst_tpmi_core.h @@ -13,4 +13,6 @@ int tpmi_sst_init(void); void tpmi_sst_exit(void); int tpmi_sst_dev_add(struct auxiliary_device *auxdev); void tpmi_sst_dev_remove(struct auxiliary_device *auxdev); +void tpmi_sst_dev_suspend(struct auxiliary_device *auxdev); +void tpmi_sst_dev_resume(struct auxiliary_device *auxdev); #endif -- cgit v1.2.3 From 8496bacae643018a2e24f76aa22426c4b9264933 Mon Sep 17 00:00:00 2001 From: Jiapeng Chong Date: Thu, 9 Mar 2023 14:10:45 +0800 Subject: platform/x86: acerhdf: Remove unneeded semicolon Fix the following warning: ./drivers/platform/x86/acerhdf.c:343:2-3: Unneeded semicolon. Reported-by: Abaci Robot Link: https://bugzilla.openanolis.cn/show_bug.cgi?id=2271 Signed-off-by: Jiapeng Chong Link: https://lore.kernel.org/r/20230309061045.25256-1-jiapeng.chong@linux.alibaba.com Reviewed-by: Hans de Goede Signed-off-by: Hans de Goede --- drivers/platform/x86/acerhdf.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/platform') diff --git a/drivers/platform/x86/acerhdf.c b/drivers/platform/x86/acerhdf.c index a48638ad2a8a..ec8cc780b822 100644 --- a/drivers/platform/x86/acerhdf.c +++ b/drivers/platform/x86/acerhdf.c @@ -341,7 +341,7 @@ static void acerhdf_check_param(struct thermal_zone_device *thermal) pr_err("fanoff temperature (%d) is above fanon temperature (%d), clamping to %d\n", fanoff, fanon, fanon); fanoff = fanon; - }; + } trips[0].temperature = fanon; trips[0].hysteresis = fanon - fanoff; -- cgit v1.2.3 From da62908efe80f132f691efc2ace4ca67626de86b Mon Sep 17 00:00:00 2001 From: Thomas Weißschuh Date: Sun, 12 Mar 2023 03:47:57 +0000 Subject: platform/x86: think-lmi: Properly interpret return value of tlmi_setting MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The return value of tlmi_settings() is an errorcode, not an acpi_status. Signed-off-by: Thomas Weißschuh Link: https://lore.kernel.org/r/20230312-think-lmi-status-v1-1-4e9f36322cc4@weissschuh.net Reviewed-by: Hans de Goede Signed-off-by: Hans de Goede --- drivers/platform/x86/think-lmi.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) (limited to 'drivers/platform') diff --git a/drivers/platform/x86/think-lmi.c b/drivers/platform/x86/think-lmi.c index 86b33b74519b..c924e9e4a6a5 100644 --- a/drivers/platform/x86/think-lmi.c +++ b/drivers/platform/x86/think-lmi.c @@ -1353,7 +1353,6 @@ static struct tlmi_pwd_setting *tlmi_create_auth(const char *pwd_type, static int tlmi_analyze(void) { - acpi_status status; int i, ret; if (wmi_has_guid(LENOVO_SET_BIOS_SETTINGS_GUID) && @@ -1390,8 +1389,8 @@ static int tlmi_analyze(void) char *p; tlmi_priv.setting[i] = NULL; - status = tlmi_setting(i, &item, LENOVO_BIOS_SETTING_GUID); - if (ACPI_FAILURE(status)) + ret = tlmi_setting(i, &item, LENOVO_BIOS_SETTING_GUID); + if (ret) break; if (!item) break; -- cgit v1.2.3 From daaa2a1f78347d73016b3a331fd48a0ca9e62192 Mon Sep 17 00:00:00 2001 From: Thomas Weißschuh Date: Tue, 14 Mar 2023 00:07:52 +0000 Subject: platform/x86: think-lmi: Remove custom kobject sysfs_ops MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit think-lmi defines its own sysfs_ops that are identical to the standard kobj_sysfs_ops. Use the standard definitions. Signed-off-by: Thomas Weißschuh Link: https://lore.kernel.org/r/20230314-think-lmi-sysfs_ops-v1-1-9d4f1cf9caec@weissschuh.net Reviewed-by: Hans de Goede Signed-off-by: Hans de Goede --- drivers/platform/x86/think-lmi.c | 31 ++----------------------------- 1 file changed, 2 insertions(+), 29 deletions(-) (limited to 'drivers/platform') diff --git a/drivers/platform/x86/think-lmi.c b/drivers/platform/x86/think-lmi.c index c924e9e4a6a5..cc66f7cbccf2 100644 --- a/drivers/platform/x86/think-lmi.c +++ b/drivers/platform/x86/think-lmi.c @@ -1047,33 +1047,6 @@ static const struct attribute_group tlmi_attr_group = { .attrs = tlmi_attrs, }; -static ssize_t tlmi_attr_show(struct kobject *kobj, struct attribute *attr, - char *buf) -{ - struct kobj_attribute *kattr; - - kattr = container_of(attr, struct kobj_attribute, attr); - if (kattr->show) - return kattr->show(kobj, kattr, buf); - return -EIO; -} - -static ssize_t tlmi_attr_store(struct kobject *kobj, struct attribute *attr, - const char *buf, size_t count) -{ - struct kobj_attribute *kattr; - - kattr = container_of(attr, struct kobj_attribute, attr); - if (kattr->store) - return kattr->store(kobj, kattr, buf, count); - return -EIO; -} - -static const struct sysfs_ops tlmi_kobj_sysfs_ops = { - .show = tlmi_attr_show, - .store = tlmi_attr_store, -}; - static void tlmi_attr_setting_release(struct kobject *kobj) { struct tlmi_attr_setting *setting = to_tlmi_attr_setting(kobj); @@ -1091,12 +1064,12 @@ static void tlmi_pwd_setting_release(struct kobject *kobj) static const struct kobj_type tlmi_attr_setting_ktype = { .release = &tlmi_attr_setting_release, - .sysfs_ops = &tlmi_kobj_sysfs_ops, + .sysfs_ops = &kobj_sysfs_ops, }; static const struct kobj_type tlmi_pwd_setting_ktype = { .release = &tlmi_pwd_setting_release, - .sysfs_ops = &tlmi_kobj_sysfs_ops, + .sysfs_ops = &kobj_sysfs_ops, }; static ssize_t pending_reboot_show(struct kobject *kobj, struct kobj_attribute *attr, -- cgit v1.2.3 From e970607955eba62bb361e9dea493cd103e5ebd6b Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Sun, 12 Mar 2023 14:26:23 +0100 Subject: platform/x86: classmate: mark SPI related data as maybe unused MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The driver can be compile tested as built-in making certain data unused: drivers/platform/x86/classmate-laptop.c:1137:36: error: ‘cmpc_device_ids’ defined but not used [-Werror=unused-const-variable=] Signed-off-by: Krzysztof Kozlowski Link: https://lore.kernel.org/r/20230312132624.352703-2-krzysztof.kozlowski@linaro.org Reviewed-by: Hans de Goede Signed-off-by: Hans de Goede --- drivers/platform/x86/classmate-laptop.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/platform') diff --git a/drivers/platform/x86/classmate-laptop.c b/drivers/platform/x86/classmate-laptop.c index 8b6a14611859..2edaea2492df 100644 --- a/drivers/platform/x86/classmate-laptop.c +++ b/drivers/platform/x86/classmate-laptop.c @@ -1134,7 +1134,7 @@ static void cmpc_exit(void) module_init(cmpc_init); module_exit(cmpc_exit); -static const struct acpi_device_id cmpc_device_ids[] = { +static const struct acpi_device_id cmpc_device_ids[] __maybe_unused = { {CMPC_ACCEL_HID, 0}, {CMPC_ACCEL_HID_V4, 0}, {CMPC_TABLET_HID, 0}, -- cgit v1.2.3 From 06984c83e9afbb718f501b17b1f0a7a4ae6d0bb5 Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Sun, 12 Mar 2023 14:26:24 +0100 Subject: platform/x86: sony: mark SPI related data as maybe unused MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The driver can be compile tested as built-in making certain data unused: drivers/platform/x86/sony-laptop.c:3290:36: error: ‘sony_device_ids’ defined but not used [-Werror=unused-const-variable=] Signed-off-by: Krzysztof Kozlowski Link: https://lore.kernel.org/r/20230312132624.352703-3-krzysztof.kozlowski@linaro.org Reviewed-by: Hans de Goede Signed-off-by: Hans de Goede --- drivers/platform/x86/sony-laptop.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/platform') diff --git a/drivers/platform/x86/sony-laptop.c b/drivers/platform/x86/sony-laptop.c index 537d6a2d0781..9569f11dec8c 100644 --- a/drivers/platform/x86/sony-laptop.c +++ b/drivers/platform/x86/sony-laptop.c @@ -3287,7 +3287,7 @@ static void sony_nc_remove(struct acpi_device *device) dprintk(SONY_NC_DRIVER_NAME " removed.\n"); } -static const struct acpi_device_id sony_device_ids[] = { +static const struct acpi_device_id sony_device_ids[] __maybe_unused = { {SONY_NC_HID, 0}, {SONY_PIC_HID, 0}, {"", 0}, -- cgit v1.2.3 From fd3c3584539026a708493a49f65d8ff3eb369002 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Mon, 13 Mar 2023 14:02:41 +0100 Subject: platform/x86: pcengines-apuv2: Drop platform:pcengines-apuv2 module-alias The driver auto-loads based on the DMI modaliases and this platform modalias is not necessary and broken: 1. Not necessary since the driver itself creates the platform_device, so it is already loaded when the modalias might be used 2. From a quick scan of the code it does not create any platform-devices called "platform:pcengines-apuv2" Signed-off-by: Hans de Goede Link: https://lore.kernel.org/r/20230313130241.778146-1-hdegoede@redhat.com --- drivers/platform/x86/pcengines-apuv2.c | 1 - 1 file changed, 1 deletion(-) (limited to 'drivers/platform') diff --git a/drivers/platform/x86/pcengines-apuv2.c b/drivers/platform/x86/pcengines-apuv2.c index d063d91db9bc..3aa63b18a2e1 100644 --- a/drivers/platform/x86/pcengines-apuv2.c +++ b/drivers/platform/x86/pcengines-apuv2.c @@ -291,5 +291,4 @@ MODULE_AUTHOR("Enrico Weigelt, metux IT consult "); MODULE_DESCRIPTION("PC Engines APUv2/APUv3 board GPIO/LEDs/keys driver"); MODULE_LICENSE("GPL"); MODULE_DEVICE_TABLE(dmi, apu_gpio_dmi_table); -MODULE_ALIAS("platform:pcengines-apuv2"); MODULE_SOFTDEP("pre: platform:" AMD_FCH_GPIO_DRIVER_NAME " platform:leds-gpio platform:gpio_keys_polled"); -- cgit v1.2.3 From d7b2b9387edac5a93058dcbf9b70017a764bfd18 Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Thu, 16 Mar 2023 17:03:24 +0100 Subject: platform/olpc: olpc-xo175-ec: Use SPI device ID data to bind device MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The driver defines spi_device_id table for module autoloading, but does not use it in id_table which causes W=1 warning: drivers/platform/olpc/olpc-xo175-ec.c:737:35: error: ‘olpc_xo175_ec_id_table’ defined but not used [-Werror=unused-const-variable=] Reference the SPI device ID table, so it can be also used for device binding. Signed-off-by: Krzysztof Kozlowski Link: https://lore.kernel.org/r/20230316160324.78856-1-krzysztof.kozlowski@linaro.org Reviewed-by: Hans de Goede Signed-off-by: Hans de Goede --- drivers/platform/olpc/olpc-xo175-ec.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers/platform') diff --git a/drivers/platform/olpc/olpc-xo175-ec.c b/drivers/platform/olpc/olpc-xo175-ec.c index 4823bd2819f6..62ccbcb15c74 100644 --- a/drivers/platform/olpc/olpc-xo175-ec.c +++ b/drivers/platform/olpc/olpc-xo175-ec.c @@ -746,6 +746,7 @@ static struct spi_driver olpc_xo175_ec_spi_driver = { .of_match_table = olpc_xo175_ec_of_match, .pm = &olpc_xo175_ec_pm_ops, }, + .id_table = olpc_xo175_ec_id_table, .probe = olpc_xo175_ec_probe, .remove = olpc_xo175_ec_remove, }; -- cgit v1.2.3 From 3f95ecf2a3e4db09e58d307932037e8f1210d6e7 Mon Sep 17 00:00:00 2001 From: "David E. Box" Date: Thu, 16 Mar 2023 15:46:28 -0700 Subject: platform/x86/intel: vsec: Explicitly enable capabilities Discovered Intel VSEC/DVSEC capabilities are enabled by default and only get disabled by quirk. Instead, remove such quirks and only enable support for capabilities that have been explicitly added to a new capabilities field. While here, also reorder the device info structures alphabetically. Signed-off-by: David E. Box Reviewed-by: Andy Shevchenko Link: https://lore.kernel.org/r/20230316224628.2855884-1-david.e.box@linux.intel.com Reviewed-by: Hans de Goede Signed-off-by: Hans de Goede --- drivers/platform/x86/intel/vsec.c | 69 +++++++++++++++++---------------------- drivers/platform/x86/intel/vsec.h | 9 ++++- 2 files changed, 38 insertions(+), 40 deletions(-) (limited to 'drivers/platform') diff --git a/drivers/platform/x86/intel/vsec.c b/drivers/platform/x86/intel/vsec.c index 13decf36c6de..01b1f6480e5c 100644 --- a/drivers/platform/x86/intel/vsec.c +++ b/drivers/platform/x86/intel/vsec.c @@ -67,14 +67,6 @@ enum intel_vsec_id { VSEC_ID_TPMI = 66, }; -static enum intel_vsec_id intel_vsec_allow_list[] = { - VSEC_ID_TELEMETRY, - VSEC_ID_WATCHER, - VSEC_ID_CRASHLOG, - VSEC_ID_SDSI, - VSEC_ID_TPMI, -}; - static const char *intel_vsec_name(enum intel_vsec_id id) { switch (id) { @@ -98,26 +90,19 @@ static const char *intel_vsec_name(enum intel_vsec_id id) } } -static bool intel_vsec_allowed(u16 id) -{ - int i; - - for (i = 0; i < ARRAY_SIZE(intel_vsec_allow_list); i++) - if (intel_vsec_allow_list[i] == id) - return true; - - return false; -} - -static bool intel_vsec_disabled(u16 id, unsigned long quirks) +static bool intel_vsec_supported(u16 id, unsigned long caps) { switch (id) { + case VSEC_ID_TELEMETRY: + return !!(caps & VSEC_CAP_TELEMETRY); case VSEC_ID_WATCHER: - return !!(quirks & VSEC_QUIRK_NO_WATCHER); - + return !!(caps & VSEC_CAP_WATCHER); case VSEC_ID_CRASHLOG: - return !!(quirks & VSEC_QUIRK_NO_CRASHLOG); - + return !!(caps & VSEC_CAP_CRASHLOG); + case VSEC_ID_SDSI: + return !!(caps & VSEC_CAP_SDSI); + case VSEC_ID_TPMI: + return !!(caps & VSEC_CAP_TPMI); default: return false; } @@ -205,7 +190,7 @@ static int intel_vsec_add_dev(struct pci_dev *pdev, struct intel_vsec_header *he unsigned long quirks = info->quirks; int i; - if (!intel_vsec_allowed(header->id) || intel_vsec_disabled(header->id, quirks)) + if (!intel_vsec_supported(header->id, info->caps)) return -EINVAL; if (!header->num_entries) { @@ -260,14 +245,14 @@ static int intel_vsec_add_dev(struct pci_dev *pdev, struct intel_vsec_header *he static bool intel_vsec_walk_header(struct pci_dev *pdev, struct intel_vsec_platform_info *info) { - struct intel_vsec_header **header = info->capabilities; + struct intel_vsec_header **header = info->headers; bool have_devices = false; int ret; for ( ; *header; header++) { ret = intel_vsec_add_dev(pdev, *header, info); if (ret) - dev_info(&pdev->dev, "Could not add device for DVSEC id %d\n", + dev_info(&pdev->dev, "Could not add device for VSEC id %d\n", (*header)->id); else have_devices = true; @@ -402,14 +387,8 @@ static int intel_vsec_pci_probe(struct pci_dev *pdev, const struct pci_device_id return 0; } -/* TGL info */ -static const struct intel_vsec_platform_info tgl_info = { - .quirks = VSEC_QUIRK_NO_WATCHER | VSEC_QUIRK_NO_CRASHLOG | - VSEC_QUIRK_TABLE_SHIFT | VSEC_QUIRK_EARLY_HW, -}; - /* DG1 info */ -static struct intel_vsec_header dg1_telemetry = { +static struct intel_vsec_header dg1_header = { .length = 0x10, .id = 2, .num_entries = 1, @@ -418,19 +397,31 @@ static struct intel_vsec_header dg1_telemetry = { .offset = 0x466000, }; -static struct intel_vsec_header *dg1_capabilities[] = { - &dg1_telemetry, +static struct intel_vsec_header *dg1_headers[] = { + &dg1_header, NULL }; static const struct intel_vsec_platform_info dg1_info = { - .capabilities = dg1_capabilities, + .caps = VSEC_CAP_TELEMETRY, + .headers = dg1_headers, .quirks = VSEC_QUIRK_NO_DVSEC | VSEC_QUIRK_EARLY_HW, }; /* MTL info */ static const struct intel_vsec_platform_info mtl_info = { - .quirks = VSEC_QUIRK_NO_WATCHER | VSEC_QUIRK_NO_CRASHLOG, + .caps = VSEC_CAP_TELEMETRY, +}; + +/* OOBMSM info */ +static const struct intel_vsec_platform_info oobmsm_info = { + .caps = VSEC_CAP_TELEMETRY | VSEC_CAP_SDSI | VSEC_CAP_TPMI, +}; + +/* TGL info */ +static const struct intel_vsec_platform_info tgl_info = { + .caps = VSEC_CAP_TELEMETRY, + .quirks = VSEC_QUIRK_TABLE_SHIFT | VSEC_QUIRK_EARLY_HW, }; #define PCI_DEVICE_ID_INTEL_VSEC_ADL 0x467d @@ -445,7 +436,7 @@ static const struct pci_device_id intel_vsec_pci_ids[] = { { PCI_DEVICE_DATA(INTEL, VSEC_DG1, &dg1_info) }, { PCI_DEVICE_DATA(INTEL, VSEC_MTL_M, &mtl_info) }, { PCI_DEVICE_DATA(INTEL, VSEC_MTL_S, &mtl_info) }, - { PCI_DEVICE_DATA(INTEL, VSEC_OOBMSM, &(struct intel_vsec_platform_info) {}) }, + { PCI_DEVICE_DATA(INTEL, VSEC_OOBMSM, &oobmsm_info) }, { PCI_DEVICE_DATA(INTEL, VSEC_RPL, &tgl_info) }, { PCI_DEVICE_DATA(INTEL, VSEC_TGL, &tgl_info) }, { } diff --git a/drivers/platform/x86/intel/vsec.h b/drivers/platform/x86/intel/vsec.h index ae8fe92c5595..0fd042c171ba 100644 --- a/drivers/platform/x86/intel/vsec.h +++ b/drivers/platform/x86/intel/vsec.h @@ -5,6 +5,12 @@ #include #include +#define VSEC_CAP_TELEMETRY BIT(0) +#define VSEC_CAP_WATCHER BIT(1) +#define VSEC_CAP_CRASHLOG BIT(2) +#define VSEC_CAP_SDSI BIT(3) +#define VSEC_CAP_TPMI BIT(4) + struct pci_dev; struct resource; @@ -27,7 +33,8 @@ enum intel_vsec_quirks { /* Platform specific data */ struct intel_vsec_platform_info { - struct intel_vsec_header **capabilities; + struct intel_vsec_header **headers; + unsigned long caps; unsigned long quirks; }; -- cgit v1.2.3 From d908084385a41cfdb82dca6ab4086e3f1a9dd4ae Mon Sep 17 00:00:00 2001 From: "David E. Box" Date: Thu, 16 Mar 2023 15:57:35 -0700 Subject: platform/x86/intel/pmt: Add INTEL_PMT module namespace Since the currently exported symbols in pmt_class are only used by other Intel PMT drivers, create an INTEL_PMT module namespace for them. Signed-off-by: David E. Box Reviewed-by: Andy Shevchenko Link: https://lore.kernel.org/r/20230316225736.2856521-1-david.e.box@linux.intel.com Reviewed-by: Hans de Goede Signed-off-by: Hans de Goede --- drivers/platform/x86/intel/pmt/class.c | 6 +++--- drivers/platform/x86/intel/pmt/crashlog.c | 1 + drivers/platform/x86/intel/pmt/telemetry.c | 1 + 3 files changed, 5 insertions(+), 3 deletions(-) (limited to 'drivers/platform') diff --git a/drivers/platform/x86/intel/pmt/class.c b/drivers/platform/x86/intel/pmt/class.c index 46598dcb634a..0b96d75f5924 100644 --- a/drivers/platform/x86/intel/pmt/class.c +++ b/drivers/platform/x86/intel/pmt/class.c @@ -33,7 +33,7 @@ bool intel_pmt_is_early_client_hw(struct device *dev) */ return !!(ivdev->info->quirks & VSEC_QUIRK_EARLY_HW); } -EXPORT_SYMBOL_GPL(intel_pmt_is_early_client_hw); +EXPORT_SYMBOL_NS_GPL(intel_pmt_is_early_client_hw, INTEL_PMT); static inline int pmt_memcpy64_fromio(void *to, const u64 __iomem *from, size_t count) @@ -327,7 +327,7 @@ int intel_pmt_dev_create(struct intel_pmt_entry *entry, struct intel_pmt_namespa return intel_pmt_dev_register(entry, ns, dev); } -EXPORT_SYMBOL_GPL(intel_pmt_dev_create); +EXPORT_SYMBOL_NS_GPL(intel_pmt_dev_create, INTEL_PMT); void intel_pmt_dev_destroy(struct intel_pmt_entry *entry, struct intel_pmt_namespace *ns) @@ -343,7 +343,7 @@ void intel_pmt_dev_destroy(struct intel_pmt_entry *entry, device_unregister(dev); xa_erase(ns->xa, entry->devid); } -EXPORT_SYMBOL_GPL(intel_pmt_dev_destroy); +EXPORT_SYMBOL_NS_GPL(intel_pmt_dev_destroy, INTEL_PMT); static int __init pmt_class_init(void) { diff --git a/drivers/platform/x86/intel/pmt/crashlog.c b/drivers/platform/x86/intel/pmt/crashlog.c index ace1239bc0a0..bbb3d61d09f4 100644 --- a/drivers/platform/x86/intel/pmt/crashlog.c +++ b/drivers/platform/x86/intel/pmt/crashlog.c @@ -328,3 +328,4 @@ module_exit(pmt_crashlog_exit); MODULE_AUTHOR("Alexander Duyck "); MODULE_DESCRIPTION("Intel PMT Crashlog driver"); MODULE_LICENSE("GPL v2"); +MODULE_IMPORT_NS(INTEL_PMT); diff --git a/drivers/platform/x86/intel/pmt/telemetry.c b/drivers/platform/x86/intel/pmt/telemetry.c index 5e4009c05ecf..71cdef42084a 100644 --- a/drivers/platform/x86/intel/pmt/telemetry.c +++ b/drivers/platform/x86/intel/pmt/telemetry.c @@ -160,3 +160,4 @@ module_exit(pmt_telem_exit); MODULE_AUTHOR("David E. Box "); MODULE_DESCRIPTION("Intel PMT Telemetry driver"); MODULE_LICENSE("GPL v2"); +MODULE_IMPORT_NS(INTEL_PMT); -- cgit v1.2.3 From 6f561677c2f234bcf215350b76f2a2fea95fbebf Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Mon, 20 Mar 2023 11:38:15 +0100 Subject: platform/x86/intel: vsec: Use intel_vsec_dev_release() to simplify init() error cleanup On auxiliary_device_init(auxdev) failure we need to do the exact same cleanup steps as on device.release(), so use the intel_vsec_dev_release() callback for this. Signed-off-by: Hans de Goede Reviewed-by: Andy Shevchenko Link: https://lore.kernel.org/r/20230320103815.229729-1-hdegoede@redhat.com --- drivers/platform/x86/intel/vsec.c | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) (limited to 'drivers/platform') diff --git a/drivers/platform/x86/intel/vsec.c b/drivers/platform/x86/intel/vsec.c index 01b1f6480e5c..42904b431d87 100644 --- a/drivers/platform/x86/intel/vsec.c +++ b/drivers/platform/x86/intel/vsec.c @@ -153,11 +153,7 @@ int intel_vsec_add_aux(struct pci_dev *pdev, struct device *parent, ret = auxiliary_device_init(auxdev); if (ret < 0) { - mutex_lock(&vsec_ida_lock); - ida_free(intel_vsec_dev->ida, auxdev->id); - mutex_unlock(&vsec_ida_lock); - kfree(intel_vsec_dev->resource); - kfree(intel_vsec_dev); + intel_vsec_dev_release(&auxdev->dev); return ret; } -- cgit v1.2.3 From 392cacf2aa10de005e58b68a58012c0c81a100c0 Mon Sep 17 00:00:00 2001 From: Nikita Kravets Date: Tue, 21 Mar 2023 01:55:09 +0300 Subject: platform/x86: Add new msi-ec driver Add a new driver to allow various MSI laptops' functionalities to be controlled from userspace. This includes such features as power profiles (aka shift modes), fan speed, charge thresholds, LEDs, etc. This driver contains EC memory configurations for different firmware versions and exports battery charge thresholds to userspace (note, that start and end thresholds control the same EC parameter and are always 10% apart). Link: https://github.com/BeardOverflow/msi-ec/ Link: https://github.com/BeardOverflow/msi-ec/pull/13 Cc: Aakash Singh Cc: Jose Angel Pastrana Signed-off-by: Nikita Kravets Link: https://lore.kernel.org/r/20230320225509.3559-1-teackot@gmail.com Reviewed-by: Hans de Goede Signed-off-by: Hans de Goede --- MAINTAINERS | 7 + drivers/platform/x86/Kconfig | 8 + drivers/platform/x86/Makefile | 1 + drivers/platform/x86/msi-ec.c | 897 ++++++++++++++++++++++++++++++++++++++++++ drivers/platform/x86/msi-ec.h | 122 ++++++ 5 files changed, 1035 insertions(+) create mode 100644 drivers/platform/x86/msi-ec.c create mode 100644 drivers/platform/x86/msi-ec.h (limited to 'drivers/platform') diff --git a/MAINTAINERS b/MAINTAINERS index f32538373164..0c9011f5fc17 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -14138,6 +14138,13 @@ S: Odd Fixes F: Documentation/devicetree/bindings/net/ieee802154/mrf24j40.txt F: drivers/net/ieee802154/mrf24j40.c +MSI EC DRIVER +M: Nikita Kravets +L: platform-driver-x86@vger.kernel.org +S: Maintained +W: https://github.com/BeardOverflow/msi-ec +F: drivers/platform/x86/msi-ec.* + MSI LAPTOP SUPPORT M: "Lee, Chun-Yi" L: platform-driver-x86@vger.kernel.org diff --git a/drivers/platform/x86/Kconfig b/drivers/platform/x86/Kconfig index aa8df8d4aee9..1899be67dc0c 100644 --- a/drivers/platform/x86/Kconfig +++ b/drivers/platform/x86/Kconfig @@ -635,6 +635,14 @@ config THINKPAD_LMI source "drivers/platform/x86/intel/Kconfig" +config MSI_EC + tristate "MSI EC Extras" + depends on ACPI + depends on ACPI_BATTERY + help + This driver allows various MSI laptops' functionalities to be + controlled from userspace, including battery charge threshold. + config MSI_LAPTOP tristate "MSI Laptop Extras" depends on ACPI diff --git a/drivers/platform/x86/Makefile b/drivers/platform/x86/Makefile index 12407f36d2be..27550cf6d4fb 100644 --- a/drivers/platform/x86/Makefile +++ b/drivers/platform/x86/Makefile @@ -70,6 +70,7 @@ obj-$(CONFIG_THINKPAD_LMI) += think-lmi.o obj-y += intel/ # MSI +obj-$(CONFIG_MSI_EC) += msi-ec.o obj-$(CONFIG_MSI_LAPTOP) += msi-laptop.o obj-$(CONFIG_MSI_WMI) += msi-wmi.o diff --git a/drivers/platform/x86/msi-ec.c b/drivers/platform/x86/msi-ec.c new file mode 100644 index 000000000000..ff93986e3d35 --- /dev/null +++ b/drivers/platform/x86/msi-ec.c @@ -0,0 +1,897 @@ +// SPDX-License-Identifier: GPL-2.0-or-later + +/* + * msi-ec: MSI laptops' embedded controller driver. + * + * This driver allows various MSI laptops' functionalities to be + * controlled from userspace. + * + * It contains EC memory configurations for different firmware versions + * and exports battery charge thresholds to userspace. + * + * Copyright (C) 2023 Jose Angel Pastrana + * Copyright (C) 2023 Aakash Singh + * Copyright (C) 2023 Nikita Kravets + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include "msi-ec.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +static const char *const SM_ECO_NAME = "eco"; +static const char *const SM_COMFORT_NAME = "comfort"; +static const char *const SM_SPORT_NAME = "sport"; +static const char *const SM_TURBO_NAME = "turbo"; + +static const char *const FM_AUTO_NAME = "auto"; +static const char *const FM_SILENT_NAME = "silent"; +static const char *const FM_BASIC_NAME = "basic"; +static const char *const FM_ADVANCED_NAME = "advanced"; + +static const char * const ALLOWED_FW_0[] __initconst = { + "14C1EMS1.012", + "14C1EMS1.101", + "14C1EMS1.102", + NULL +}; + +static struct msi_ec_conf CONF0 __initdata = { + .allowed_fw = ALLOWED_FW_0, + .charge_control = { + .address = 0xef, + .offset_start = 0x8a, + .offset_end = 0x80, + .range_min = 0x8a, + .range_max = 0xe4, + }, + .webcam = { + .address = 0x2e, + .block_address = 0x2f, + .bit = 1, + }, + .fn_super_swap = { + .address = 0xbf, + .bit = 4, + }, + .cooler_boost = { + .address = 0x98, + .bit = 7, + }, + .shift_mode = { + .address = 0xf2, + .modes = { + { SM_ECO_NAME, 0xc2 }, + { SM_COMFORT_NAME, 0xc1 }, + { SM_SPORT_NAME, 0xc0 }, + MSI_EC_MODE_NULL + }, + }, + .super_battery = { + .address = MSI_EC_ADDR_UNKNOWN, // 0xd5 needs testing + }, + .fan_mode = { + .address = 0xf4, + .modes = { + { FM_AUTO_NAME, 0x0d }, + { FM_SILENT_NAME, 0x1d }, + { FM_BASIC_NAME, 0x4d }, + { FM_ADVANCED_NAME, 0x8d }, + MSI_EC_MODE_NULL + }, + }, + .cpu = { + .rt_temp_address = 0x68, + .rt_fan_speed_address = 0x71, + .rt_fan_speed_base_min = 0x19, + .rt_fan_speed_base_max = 0x37, + .bs_fan_speed_address = 0x89, + .bs_fan_speed_base_min = 0x00, + .bs_fan_speed_base_max = 0x0f, + }, + .gpu = { + .rt_temp_address = 0x80, + .rt_fan_speed_address = 0x89, + }, + .leds = { + .micmute_led_address = 0x2b, + .mute_led_address = 0x2c, + .bit = 2, + }, + .kbd_bl = { + .bl_mode_address = 0x2c, // ? + .bl_modes = { 0x00, 0x08 }, // ? + .max_mode = 1, // ? + .bl_state_address = 0xf3, + .state_base_value = 0x80, + .max_state = 3, + }, +}; + +static const char * const ALLOWED_FW_1[] __initconst = { + "17F2EMS1.103", + "17F2EMS1.104", + "17F2EMS1.106", + "17F2EMS1.107", + NULL +}; + +static struct msi_ec_conf CONF1 __initdata = { + .allowed_fw = ALLOWED_FW_1, + .charge_control = { + .address = 0xef, + .offset_start = 0x8a, + .offset_end = 0x80, + .range_min = 0x8a, + .range_max = 0xe4, + }, + .webcam = { + .address = 0x2e, + .block_address = 0x2f, + .bit = 1, + }, + .fn_super_swap = { + .address = 0xbf, + .bit = 4, + }, + .cooler_boost = { + .address = 0x98, + .bit = 7, + }, + .shift_mode = { + .address = 0xf2, + .modes = { + { SM_ECO_NAME, 0xc2 }, + { SM_COMFORT_NAME, 0xc1 }, + { SM_SPORT_NAME, 0xc0 }, + { SM_TURBO_NAME, 0xc4 }, + MSI_EC_MODE_NULL + }, + }, + .super_battery = { + .address = MSI_EC_ADDR_UNKNOWN, + }, + .fan_mode = { + .address = 0xf4, + .modes = { + { FM_AUTO_NAME, 0x0d }, + { FM_BASIC_NAME, 0x4d }, + { FM_ADVANCED_NAME, 0x8d }, + MSI_EC_MODE_NULL + }, + }, + .cpu = { + .rt_temp_address = 0x68, + .rt_fan_speed_address = 0x71, + .rt_fan_speed_base_min = 0x19, + .rt_fan_speed_base_max = 0x37, + .bs_fan_speed_address = 0x89, + .bs_fan_speed_base_min = 0x00, + .bs_fan_speed_base_max = 0x0f, + }, + .gpu = { + .rt_temp_address = 0x80, + .rt_fan_speed_address = 0x89, + }, + .leds = { + .micmute_led_address = 0x2b, + .mute_led_address = 0x2c, + .bit = 2, + }, + .kbd_bl = { + .bl_mode_address = 0x2c, // ? + .bl_modes = { 0x00, 0x08 }, // ? + .max_mode = 1, // ? + .bl_state_address = 0xf3, + .state_base_value = 0x80, + .max_state = 3, + }, +}; + +static const char * const ALLOWED_FW_2[] __initconst = { + "1552EMS1.118", + NULL +}; + +static struct msi_ec_conf CONF2 __initdata = { + .allowed_fw = ALLOWED_FW_2, + .charge_control = { + .address = 0xd7, + .offset_start = 0x8a, + .offset_end = 0x80, + .range_min = 0x8a, + .range_max = 0xe4, + }, + .webcam = { + .address = 0x2e, + .block_address = 0x2f, + .bit = 1, + }, + .fn_super_swap = { + .address = 0xe8, + .bit = 4, + }, + .cooler_boost = { + .address = 0x98, + .bit = 7, + }, + .shift_mode = { + .address = 0xf2, + .modes = { + { SM_ECO_NAME, 0xc2 }, + { SM_COMFORT_NAME, 0xc1 }, + { SM_SPORT_NAME, 0xc0 }, + MSI_EC_MODE_NULL + }, + }, + .super_battery = { + .address = 0xeb, + .mask = 0x0f, + }, + .fan_mode = { + .address = 0xd4, + .modes = { + { FM_AUTO_NAME, 0x0d }, + { FM_SILENT_NAME, 0x1d }, + { FM_BASIC_NAME, 0x4d }, + { FM_ADVANCED_NAME, 0x8d }, + MSI_EC_MODE_NULL + }, + }, + .cpu = { + .rt_temp_address = 0x68, + .rt_fan_speed_address = 0x71, + .rt_fan_speed_base_min = 0x19, + .rt_fan_speed_base_max = 0x37, + .bs_fan_speed_address = 0x89, + .bs_fan_speed_base_min = 0x00, + .bs_fan_speed_base_max = 0x0f, + }, + .gpu = { + .rt_temp_address = 0x80, + .rt_fan_speed_address = 0x89, + }, + .leds = { + .micmute_led_address = 0x2c, + .mute_led_address = 0x2d, + .bit = 1, + }, + .kbd_bl = { + .bl_mode_address = 0x2c, // ? + .bl_modes = { 0x00, 0x08 }, // ? + .max_mode = 1, // ? + .bl_state_address = 0xd3, + .state_base_value = 0x80, + .max_state = 3, + }, +}; + +static const char * const ALLOWED_FW_3[] __initconst = { + "1592EMS1.111", + "E1592IMS.10C", + NULL +}; + +static struct msi_ec_conf CONF3 __initdata = { + .allowed_fw = ALLOWED_FW_3, + .charge_control = { + .address = 0xef, + .offset_start = 0x8a, + .offset_end = 0x80, + .range_min = 0x8a, + .range_max = 0xe4, + }, + .webcam = { + .address = 0x2e, + .block_address = 0x2f, + .bit = 1, + }, + .fn_super_swap = { + .address = 0xe8, + .bit = 4, + }, + .cooler_boost = { + .address = 0x98, + .bit = 7, + }, + .shift_mode = { + .address = 0xd2, + .modes = { + { SM_ECO_NAME, 0xc2 }, + { SM_COMFORT_NAME, 0xc1 }, + { SM_SPORT_NAME, 0xc0 }, + MSI_EC_MODE_NULL + }, + }, + .super_battery = { + .address = 0xeb, + .mask = 0x0f, + }, + .fan_mode = { + .address = 0xd4, + .modes = { + { FM_AUTO_NAME, 0x0d }, + { FM_SILENT_NAME, 0x1d }, + { FM_BASIC_NAME, 0x4d }, + { FM_ADVANCED_NAME, 0x8d }, + MSI_EC_MODE_NULL + }, + }, + .cpu = { + .rt_temp_address = 0x68, + .rt_fan_speed_address = 0xc9, + .rt_fan_speed_base_min = 0x19, + .rt_fan_speed_base_max = 0x37, + .bs_fan_speed_address = 0x89, // ? + .bs_fan_speed_base_min = 0x00, + .bs_fan_speed_base_max = 0x0f, + }, + .gpu = { + .rt_temp_address = 0x80, + .rt_fan_speed_address = 0x89, + }, + .leds = { + .micmute_led_address = 0x2b, + .mute_led_address = 0x2c, + .bit = 1, + }, + .kbd_bl = { + .bl_mode_address = 0x2c, // ? + .bl_modes = { 0x00, 0x08 }, // ? + .max_mode = 1, // ? + .bl_state_address = 0xd3, + .state_base_value = 0x80, + .max_state = 3, + }, +}; + +static const char * const ALLOWED_FW_4[] __initconst = { + "16V4EMS1.114", + NULL +}; + +static struct msi_ec_conf CONF4 __initdata = { + .allowed_fw = ALLOWED_FW_4, + .charge_control = { + .address = 0xd7, + .offset_start = 0x8a, + .offset_end = 0x80, + .range_min = 0x8a, + .range_max = 0xe4, + }, + .webcam = { + .address = 0x2e, + .block_address = 0x2f, + .bit = 1, + }, + .fn_super_swap = { + .address = MSI_EC_ADDR_UNKNOWN, // supported, but unknown + .bit = 4, + }, + .cooler_boost = { + .address = 0x98, + .bit = 7, + }, + .shift_mode = { + .address = 0xd2, + .modes = { + { SM_ECO_NAME, 0xc2 }, + { SM_COMFORT_NAME, 0xc1 }, + { SM_SPORT_NAME, 0xc0 }, + MSI_EC_MODE_NULL + }, + }, + .super_battery = { // may be supported, but address is unknown + .address = MSI_EC_ADDR_UNKNOWN, + .mask = 0x0f, + }, + .fan_mode = { + .address = 0xd4, + .modes = { + { FM_AUTO_NAME, 0x0d }, + { FM_SILENT_NAME, 0x1d }, + { FM_ADVANCED_NAME, 0x8d }, + MSI_EC_MODE_NULL + }, + }, + .cpu = { + .rt_temp_address = 0x68, // needs testing + .rt_fan_speed_address = 0x71, // needs testing + .rt_fan_speed_base_min = 0x19, + .rt_fan_speed_base_max = 0x37, + .bs_fan_speed_address = MSI_EC_ADDR_UNKNOWN, + .bs_fan_speed_base_min = 0x00, + .bs_fan_speed_base_max = 0x0f, + }, + .gpu = { + .rt_temp_address = 0x80, + .rt_fan_speed_address = MSI_EC_ADDR_UNKNOWN, + }, + .leds = { + .micmute_led_address = MSI_EC_ADDR_UNKNOWN, + .mute_led_address = MSI_EC_ADDR_UNKNOWN, + .bit = 1, + }, + .kbd_bl = { + .bl_mode_address = MSI_EC_ADDR_UNKNOWN, // ? + .bl_modes = { 0x00, 0x08 }, // ? + .max_mode = 1, // ? + .bl_state_address = MSI_EC_ADDR_UNSUPP, // 0xd3, not functional + .state_base_value = 0x80, + .max_state = 3, + }, +}; + +static const char * const ALLOWED_FW_5[] __initconst = { + "158LEMS1.103", + "158LEMS1.105", + "158LEMS1.106", + NULL +}; + +static struct msi_ec_conf CONF5 __initdata = { + .allowed_fw = ALLOWED_FW_5, + .charge_control = { + .address = 0xef, + .offset_start = 0x8a, + .offset_end = 0x80, + .range_min = 0x8a, + .range_max = 0xe4, + }, + .webcam = { + .address = 0x2e, + .block_address = 0x2f, + .bit = 1, + }, + .fn_super_swap = { // todo: reverse + .address = 0xbf, + .bit = 4, + }, + .cooler_boost = { + .address = 0x98, + .bit = 7, + }, + .shift_mode = { + .address = 0xf2, + .modes = { + { SM_ECO_NAME, 0xc2 }, + { SM_COMFORT_NAME, 0xc1 }, + { SM_TURBO_NAME, 0xc4 }, + MSI_EC_MODE_NULL + }, + }, + .super_battery = { // unsupported? + .address = MSI_EC_ADDR_UNKNOWN, + .mask = 0x0f, + }, + .fan_mode = { + .address = 0xf4, + .modes = { + { FM_AUTO_NAME, 0x0d }, + { FM_SILENT_NAME, 0x1d }, + { FM_ADVANCED_NAME, 0x8d }, + MSI_EC_MODE_NULL + }, + }, + .cpu = { + .rt_temp_address = 0x68, // needs testing + .rt_fan_speed_address = 0x71, // needs testing + .rt_fan_speed_base_min = 0x19, + .rt_fan_speed_base_max = 0x37, + .bs_fan_speed_address = MSI_EC_ADDR_UNSUPP, + .bs_fan_speed_base_min = 0x00, + .bs_fan_speed_base_max = 0x0f, + }, + .gpu = { + .rt_temp_address = MSI_EC_ADDR_UNKNOWN, + .rt_fan_speed_address = MSI_EC_ADDR_UNKNOWN, + }, + .leds = { + .micmute_led_address = 0x2b, + .mute_led_address = 0x2c, + .bit = 2, + }, + .kbd_bl = { + .bl_mode_address = MSI_EC_ADDR_UNKNOWN, // ? + .bl_modes = { 0x00, 0x08 }, // ? + .max_mode = 1, // ? + .bl_state_address = MSI_EC_ADDR_UNSUPP, // 0xf3, not functional + .state_base_value = 0x80, + .max_state = 3, + }, +}; + +static const char * const ALLOWED_FW_6[] __initconst = { + "1542EMS1.102", + "1542EMS1.104", + NULL +}; + +static struct msi_ec_conf CONF6 __initdata = { + .allowed_fw = ALLOWED_FW_6, + .charge_control = { + .address = 0xef, + .offset_start = 0x8a, + .offset_end = 0x80, + .range_min = 0x8a, + .range_max = 0xe4, + }, + .webcam = { + .address = 0x2e, + .block_address = MSI_EC_ADDR_UNSUPP, + .bit = 1, + }, + .fn_super_swap = { + .address = 0xbf, // todo: reverse + .bit = 4, + }, + .cooler_boost = { + .address = 0x98, + .bit = 7, + }, + .shift_mode = { + .address = 0xf2, + .modes = { + { SM_ECO_NAME, 0xc2 }, + { SM_COMFORT_NAME, 0xc1 }, + { SM_SPORT_NAME, 0xc0 }, + { SM_TURBO_NAME, 0xc4 }, + MSI_EC_MODE_NULL + }, + }, + .super_battery = { + .address = 0xd5, + .mask = 0x0f, + }, + .fan_mode = { + .address = 0xf4, + .modes = { + { FM_AUTO_NAME, 0x0d }, + { FM_SILENT_NAME, 0x1d }, + { FM_ADVANCED_NAME, 0x8d }, + MSI_EC_MODE_NULL + }, + }, + .cpu = { + .rt_temp_address = 0x68, + .rt_fan_speed_address = 0xc9, + .rt_fan_speed_base_min = 0x19, + .rt_fan_speed_base_max = 0x37, + .bs_fan_speed_address = MSI_EC_ADDR_UNSUPP, + .bs_fan_speed_base_min = 0x00, + .bs_fan_speed_base_max = 0x0f, + }, + .gpu = { + .rt_temp_address = 0x80, + .rt_fan_speed_address = MSI_EC_ADDR_UNKNOWN, + }, + .leds = { + .micmute_led_address = MSI_EC_ADDR_UNSUPP, + .mute_led_address = MSI_EC_ADDR_UNSUPP, + .bit = 2, + }, + .kbd_bl = { + .bl_mode_address = MSI_EC_ADDR_UNKNOWN, // ? + .bl_modes = { 0x00, 0x08 }, // ? + .max_mode = 1, // ? + .bl_state_address = MSI_EC_ADDR_UNSUPP, // 0xf3, not functional + .state_base_value = 0x80, + .max_state = 3, + }, +}; + +static const char * const ALLOWED_FW_7[] __initconst = { + "17FKEMS1.108", + "17FKEMS1.109", + "17FKEMS1.10A", + NULL +}; + +static struct msi_ec_conf CONF7 __initdata = { + .allowed_fw = ALLOWED_FW_7, + .charge_control = { + .address = 0xef, + .offset_start = 0x8a, + .offset_end = 0x80, + .range_min = 0x8a, + .range_max = 0xe4, + }, + .webcam = { + .address = 0x2e, + .block_address = MSI_EC_ADDR_UNSUPP, + .bit = 1, + }, + .fn_super_swap = { + .address = 0xbf, // needs testing + .bit = 4, + }, + .cooler_boost = { + .address = 0x98, + .bit = 7, + }, + .shift_mode = { + .address = 0xf2, + .modes = { + { SM_ECO_NAME, 0xc2 }, + { SM_COMFORT_NAME, 0xc1 }, + { SM_SPORT_NAME, 0xc0 }, + { SM_TURBO_NAME, 0xc4 }, + MSI_EC_MODE_NULL + }, + }, + .super_battery = { + .address = MSI_EC_ADDR_UNKNOWN, // 0xd5 but has its own wet of modes + .mask = 0x0f, + }, + .fan_mode = { + .address = 0xf4, + .modes = { + { FM_AUTO_NAME, 0x0d }, // d may not be relevant + { FM_SILENT_NAME, 0x1d }, + { FM_ADVANCED_NAME, 0x8d }, + MSI_EC_MODE_NULL + }, + }, + .cpu = { + .rt_temp_address = 0x68, + .rt_fan_speed_address = 0xc9, // needs testing + .rt_fan_speed_base_min = 0x19, + .rt_fan_speed_base_max = 0x37, + .bs_fan_speed_address = MSI_EC_ADDR_UNSUPP, + .bs_fan_speed_base_min = 0x00, + .bs_fan_speed_base_max = 0x0f, + }, + .gpu = { + .rt_temp_address = MSI_EC_ADDR_UNKNOWN, + .rt_fan_speed_address = MSI_EC_ADDR_UNKNOWN, + }, + .leds = { + .micmute_led_address = MSI_EC_ADDR_UNSUPP, + .mute_led_address = 0x2c, + .bit = 2, + }, + .kbd_bl = { + .bl_mode_address = MSI_EC_ADDR_UNKNOWN, // ? + .bl_modes = { 0x00, 0x08 }, // ? + .max_mode = 1, // ? + .bl_state_address = 0xf3, + .state_base_value = 0x80, + .max_state = 3, + }, +}; + +static struct msi_ec_conf *CONFIGS[] __initdata = { + &CONF0, + &CONF1, + &CONF2, + &CONF3, + &CONF4, + &CONF5, + &CONF6, + &CONF7, + NULL +}; + +static struct msi_ec_conf conf; // current configuration + +/* + * Helper functions + */ + +static int ec_read_seq(u8 addr, u8 *buf, u8 len) +{ + int result; + + for (u8 i = 0; i < len; i++) { + result = ec_read(addr + i, buf + i); + if (result < 0) + return result; + } + + return 0; +} + +static int ec_get_firmware_version(u8 buf[MSI_EC_FW_VERSION_LENGTH + 1]) +{ + int result; + + memset(buf, 0, MSI_EC_FW_VERSION_LENGTH + 1); + result = ec_read_seq(MSI_EC_FW_VERSION_ADDRESS, + buf, + MSI_EC_FW_VERSION_LENGTH); + if (result < 0) + return result; + + return MSI_EC_FW_VERSION_LENGTH + 1; +} + +/* + * Sysfs power_supply subsystem + */ + +static ssize_t charge_control_threshold_show(u8 offset, + struct device *device, + struct device_attribute *attr, + char *buf) +{ + u8 rdata; + int result; + + result = ec_read(conf.charge_control.address, &rdata); + if (result < 0) + return result; + + return sysfs_emit(buf, "%i\n", rdata - offset); +} + +static ssize_t charge_control_threshold_store(u8 offset, + struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + u8 wdata; + int result; + + result = kstrtou8(buf, 10, &wdata); + if (result < 0) + return result; + + wdata += offset; + if (wdata < conf.charge_control.range_min || + wdata > conf.charge_control.range_max) + return -EINVAL; + + result = ec_write(conf.charge_control.address, wdata); + if (result < 0) + return result; + + return count; +} + +static ssize_t charge_control_start_threshold_show(struct device *device, + struct device_attribute *attr, + char *buf) +{ + return charge_control_threshold_show(conf.charge_control.offset_start, + device, attr, buf); +} + +static ssize_t charge_control_start_threshold_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + return charge_control_threshold_store(conf.charge_control.offset_start, + dev, attr, buf, count); +} + +static ssize_t charge_control_end_threshold_show(struct device *device, + struct device_attribute *attr, + char *buf) +{ + return charge_control_threshold_show(conf.charge_control.offset_end, + device, attr, buf); +} + +static ssize_t charge_control_end_threshold_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + return charge_control_threshold_store(conf.charge_control.offset_end, + dev, attr, buf, count); +} + +static DEVICE_ATTR_RW(charge_control_start_threshold); +static DEVICE_ATTR_RW(charge_control_end_threshold); + +static struct attribute *msi_battery_attrs[] = { + &dev_attr_charge_control_start_threshold.attr, + &dev_attr_charge_control_end_threshold.attr, + NULL +}; + +ATTRIBUTE_GROUPS(msi_battery); + +static int msi_battery_add(struct power_supply *battery, + struct acpi_battery_hook *hook) +{ + return device_add_groups(&battery->dev, msi_battery_groups); +} + +static int msi_battery_remove(struct power_supply *battery, + struct acpi_battery_hook *hook) +{ + device_remove_groups(&battery->dev, msi_battery_groups); + return 0; +} + +static struct acpi_battery_hook battery_hook = { + .add_battery = msi_battery_add, + .remove_battery = msi_battery_remove, + .name = MSI_EC_DRIVER_NAME, +}; + +/* + * Module load/unload + */ + +static const struct dmi_system_id msi_dmi_table[] __initconst __maybe_unused = { + { + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "MICRO-STAR INT"), + }, + }, + { + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Micro-Star International"), + }, + }, + {} +}; +MODULE_DEVICE_TABLE(dmi, msi_dmi_table); + +static int __init load_configuration(void) +{ + int result; + + u8 fw_version[MSI_EC_FW_VERSION_LENGTH + 1]; + + /* get firmware version */ + result = ec_get_firmware_version(fw_version); + if (result < 0) + return result; + + /* load the suitable configuration, if exists */ + for (int i = 0; CONFIGS[i]; i++) { + if (match_string(CONFIGS[i]->allowed_fw, -1, fw_version) != -EINVAL) { + conf = *CONFIGS[i]; + conf.allowed_fw = NULL; + return 0; + } + } + + /* config not found */ + + for (int i = 0; i < MSI_EC_FW_VERSION_LENGTH; i++) { + if (!isgraph(fw_version[i])) { + pr_warn("Unable to find a valid firmware version!\n"); + return -EOPNOTSUPP; + } + } + + pr_warn("Firmware version is not supported: '%s'\n", fw_version); + return -EOPNOTSUPP; +} + +static int __init msi_ec_init(void) +{ + int result; + + result = load_configuration(); + if (result < 0) + return result; + + battery_hook_register(&battery_hook); + return 0; +} + +static void __exit msi_ec_exit(void) +{ + battery_hook_unregister(&battery_hook); +} + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Jose Angel Pastrana "); +MODULE_AUTHOR("Aakash Singh "); +MODULE_AUTHOR("Nikita Kravets "); +MODULE_DESCRIPTION("MSI Embedded Controller"); + +module_init(msi_ec_init); +module_exit(msi_ec_exit); diff --git a/drivers/platform/x86/msi-ec.h b/drivers/platform/x86/msi-ec.h new file mode 100644 index 000000000000..be3533dc9cc6 --- /dev/null +++ b/drivers/platform/x86/msi-ec.h @@ -0,0 +1,122 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +/* + * msi-ec: MSI laptops' embedded controller driver. + * + * Copyright (C) 2023 Jose Angel Pastrana + * Copyright (C) 2023 Aakash Singh + * Copyright (C) 2023 Nikita Kravets + */ + +#ifndef _MSI_EC_H_ +#define _MSI_EC_H_ + +#include + +#define MSI_EC_DRIVER_NAME "msi-ec" + +#define MSI_EC_ADDR_UNKNOWN 0xff01 // unknown address +#define MSI_EC_ADDR_UNSUPP 0xff01 // unsupported parameter + +// Firmware info addresses are universal +#define MSI_EC_FW_VERSION_ADDRESS 0xa0 +#define MSI_EC_FW_DATE_ADDRESS 0xac +#define MSI_EC_FW_TIME_ADDRESS 0xb4 +#define MSI_EC_FW_VERSION_LENGTH 12 +#define MSI_EC_FW_DATE_LENGTH 8 +#define MSI_EC_FW_TIME_LENGTH 8 + +struct msi_ec_charge_control_conf { + int address; + int offset_start; + int offset_end; + int range_min; + int range_max; +}; + +struct msi_ec_webcam_conf { + int address; + int block_address; + int bit; +}; + +struct msi_ec_fn_super_swap_conf { + int address; + int bit; +}; + +struct msi_ec_cooler_boost_conf { + int address; + int bit; +}; + +#define MSI_EC_MODE_NULL { NULL, 0 } +struct msi_ec_mode { + const char *name; + int value; +}; + +struct msi_ec_shift_mode_conf { + int address; + struct msi_ec_mode modes[5]; // fixed size for easier hard coding +}; + +struct msi_ec_super_battery_conf { + int address; + int mask; +}; + +struct msi_ec_fan_mode_conf { + int address; + struct msi_ec_mode modes[5]; // fixed size for easier hard coding +}; + +struct msi_ec_cpu_conf { + int rt_temp_address; + int rt_fan_speed_address; // realtime + int rt_fan_speed_base_min; + int rt_fan_speed_base_max; + int bs_fan_speed_address; // basic + int bs_fan_speed_base_min; + int bs_fan_speed_base_max; +}; + +struct msi_ec_gpu_conf { + int rt_temp_address; + int rt_fan_speed_address; // realtime +}; + +struct msi_ec_led_conf { + int micmute_led_address; + int mute_led_address; + int bit; +}; + +#define MSI_EC_KBD_BL_STATE_MASK 0x3 +struct msi_ec_kbd_bl_conf { + int bl_mode_address; + int bl_modes[2]; + int max_mode; + + int bl_state_address; + int state_base_value; + int max_state; +}; + +struct msi_ec_conf { + const char * const *allowed_fw; + + struct msi_ec_charge_control_conf charge_control; + struct msi_ec_webcam_conf webcam; + struct msi_ec_fn_super_swap_conf fn_super_swap; + struct msi_ec_cooler_boost_conf cooler_boost; + struct msi_ec_shift_mode_conf shift_mode; + struct msi_ec_super_battery_conf super_battery; + struct msi_ec_fan_mode_conf fan_mode; + struct msi_ec_cpu_conf cpu; + struct msi_ec_gpu_conf gpu; + struct msi_ec_led_conf leds; + struct msi_ec_kbd_bl_conf kbd_bl; +}; + +#endif // _MSI_EC_H_ -- cgit v1.2.3 From 67f88ffa6d35d6b4e776f98ea64aab0bc026e2a2 Mon Sep 17 00:00:00 2001 From: Jithu Joseph Date: Tue, 21 Mar 2023 17:33:51 -0700 Subject: platform/x86/intel/ifs: Separate ifs_pkg_auth from ifs_data In preparation to supporting additional tests, remove ifs_pkg_auth from per-test scope, as it is only applicable for one test type. This will simplify ifs_init() flow when multiple tests are added. Signed-off-by: Jithu Joseph Reviewed-by: Tony Luck Link: https://lore.kernel.org/r/20230322003359.213046-2-jithu.joseph@intel.com Reviewed-by: Hans de Goede Signed-off-by: Hans de Goede --- drivers/platform/x86/intel/ifs/core.c | 10 ++++++---- drivers/platform/x86/intel/ifs/ifs.h | 3 +-- drivers/platform/x86/intel/ifs/load.c | 6 +++--- 3 files changed, 10 insertions(+), 9 deletions(-) (limited to 'drivers/platform') diff --git a/drivers/platform/x86/intel/ifs/core.c b/drivers/platform/x86/intel/ifs/core.c index 206a617c2e02..3176d94b1fe5 100644 --- a/drivers/platform/x86/intel/ifs/core.c +++ b/drivers/platform/x86/intel/ifs/core.c @@ -20,6 +20,8 @@ static const struct x86_cpu_id ifs_cpu_ids[] __initconst = { }; MODULE_DEVICE_TABLE(x86cpu, ifs_cpu_ids); +bool *ifs_pkg_auth; + static struct ifs_device ifs_device = { .data = { .integrity_cap_bit = MSR_INTEGRITY_CAPS_PERIODIC_BIST_BIT, @@ -56,13 +58,13 @@ static int __init ifs_init(void) if (!(msrval & BIT(ifs_device.data.integrity_cap_bit))) return -ENODEV; - ifs_device.data.pkg_auth = kmalloc_array(topology_max_packages(), sizeof(bool), GFP_KERNEL); - if (!ifs_device.data.pkg_auth) + ifs_pkg_auth = kmalloc_array(topology_max_packages(), sizeof(bool), GFP_KERNEL); + if (!ifs_pkg_auth) return -ENOMEM; ret = misc_register(&ifs_device.misc); if (ret) { - kfree(ifs_device.data.pkg_auth); + kfree(ifs_pkg_auth); return ret; } @@ -72,7 +74,7 @@ static int __init ifs_init(void) static void __exit ifs_exit(void) { misc_deregister(&ifs_device.misc); - kfree(ifs_device.data.pkg_auth); + kfree(ifs_pkg_auth); } module_init(ifs_init); diff --git a/drivers/platform/x86/intel/ifs/ifs.h b/drivers/platform/x86/intel/ifs/ifs.h index 046e39304fd5..221413b79281 100644 --- a/drivers/platform/x86/intel/ifs/ifs.h +++ b/drivers/platform/x86/intel/ifs/ifs.h @@ -201,7 +201,6 @@ union ifs_status { * struct ifs_data - attributes related to intel IFS driver * @integrity_cap_bit: MSR_INTEGRITY_CAPS bit enumerating this test * @loaded_version: stores the currently loaded ifs image version. - * @pkg_auth: array of bool storing per package auth status * @loaded: If a valid test binary has been loaded into the memory * @loading_error: Error occurred on another CPU while loading image * @valid_chunks: number of chunks which could be validated. @@ -212,7 +211,6 @@ union ifs_status { */ struct ifs_data { int integrity_cap_bit; - bool *pkg_auth; int loaded_version; bool loaded; bool loading_error; @@ -241,6 +239,7 @@ static inline struct ifs_data *ifs_get_data(struct device *dev) return &d->data; } +extern bool *ifs_pkg_auth; int ifs_load_firmware(struct device *dev); int do_core_test(int cpu, struct device *dev); const struct attribute_group **ifs_get_groups(void); diff --git a/drivers/platform/x86/intel/ifs/load.c b/drivers/platform/x86/intel/ifs/load.c index c5c24e6fdc43..74a50e99cacd 100644 --- a/drivers/platform/x86/intel/ifs/load.c +++ b/drivers/platform/x86/intel/ifs/load.c @@ -192,7 +192,7 @@ static int scan_chunks_sanity_check(struct device *dev) struct ifs_work local_work; int curr_pkg, cpu, ret; - memset(ifsd->pkg_auth, 0, (topology_max_packages() * sizeof(bool))); + memset(ifs_pkg_auth, 0, (topology_max_packages() * sizeof(bool))); ret = validate_ifs_metadata(dev); if (ret) return ret; @@ -204,7 +204,7 @@ static int scan_chunks_sanity_check(struct device *dev) cpus_read_lock(); for_each_online_cpu(cpu) { curr_pkg = topology_physical_package_id(cpu); - if (ifsd->pkg_auth[curr_pkg]) + if (ifs_pkg_auth[curr_pkg]) continue; reinit_completion(&ifs_done); local_work.dev = dev; @@ -215,7 +215,7 @@ static int scan_chunks_sanity_check(struct device *dev) ret = -EIO; goto out; } - ifsd->pkg_auth[curr_pkg] = 1; + ifs_pkg_auth[curr_pkg] = 1; } ret = 0; out: -- cgit v1.2.3 From 54c9fcd187dd3bbc151cff9e5be01dda4f23dd0d Mon Sep 17 00:00:00 2001 From: Jithu Joseph Date: Tue, 21 Mar 2023 17:33:52 -0700 Subject: platform/x86/intel/ifs: Reorganize driver data The struct holding device driver data contained both read only(ro) and read write(rw) fields. Separating ro fields from rw fields was recommended as a preferable design pattern during review[1]. Group ro fields into a separate const struct. Associate it to the miscdevice being registered by keeping its pointer in the same container struct as the miscdevice. Link: https://lore.kernel.org/lkml/Y+9H9otxLYPqMkUh@kroah.com/ [1] Signed-off-by: Jithu Joseph Reviewed-by: Tony Luck Link: https://lore.kernel.org/r/20230322003359.213046-3-jithu.joseph@intel.com Reviewed-by: Hans de Goede Signed-off-by: Hans de Goede --- drivers/platform/x86/intel/ifs/core.c | 12 +++++++----- drivers/platform/x86/intel/ifs/ifs.h | 22 ++++++++++++++++------ drivers/platform/x86/intel/ifs/load.c | 3 ++- 3 files changed, 25 insertions(+), 12 deletions(-) (limited to 'drivers/platform') diff --git a/drivers/platform/x86/intel/ifs/core.c b/drivers/platform/x86/intel/ifs/core.c index 3176d94b1fe5..e2bf728eefdf 100644 --- a/drivers/platform/x86/intel/ifs/core.c +++ b/drivers/platform/x86/intel/ifs/core.c @@ -22,11 +22,13 @@ MODULE_DEVICE_TABLE(x86cpu, ifs_cpu_ids); bool *ifs_pkg_auth; +static const struct ifs_test_caps scan_test = { + .integrity_cap_bit = MSR_INTEGRITY_CAPS_PERIODIC_BIST_BIT, + .test_num = 0, +}; + static struct ifs_device ifs_device = { - .data = { - .integrity_cap_bit = MSR_INTEGRITY_CAPS_PERIODIC_BIST_BIT, - .test_num = 0, - }, + .test_caps = &scan_test, .misc = { .name = "intel_ifs_0", .nodename = "intel_ifs/0", @@ -55,7 +57,7 @@ static int __init ifs_init(void) ifs_device.misc.groups = ifs_get_groups(); - if (!(msrval & BIT(ifs_device.data.integrity_cap_bit))) + if (!(msrval & BIT(ifs_device.test_caps->integrity_cap_bit))) return -ENODEV; ifs_pkg_auth = kmalloc_array(topology_max_packages(), sizeof(bool), GFP_KERNEL); diff --git a/drivers/platform/x86/intel/ifs/ifs.h b/drivers/platform/x86/intel/ifs/ifs.h index 221413b79281..d9c1a1f3e31d 100644 --- a/drivers/platform/x86/intel/ifs/ifs.h +++ b/drivers/platform/x86/intel/ifs/ifs.h @@ -197,9 +197,13 @@ union ifs_status { #define IFS_SW_TIMEOUT 0xFD #define IFS_SW_PARTIAL_COMPLETION 0xFE +struct ifs_test_caps { + int integrity_cap_bit; + int test_num; +}; + /** * struct ifs_data - attributes related to intel IFS driver - * @integrity_cap_bit: MSR_INTEGRITY_CAPS bit enumerating this test * @loaded_version: stores the currently loaded ifs image version. * @loaded: If a valid test binary has been loaded into the memory * @loading_error: Error occurred on another CPU while loading image @@ -207,10 +211,8 @@ union ifs_status { * @status: it holds simple status pass/fail/untested * @scan_details: opaque scan status code from h/w * @cur_batch: number indicating the currently loaded test file - * @test_num: number indicating the test type */ struct ifs_data { - int integrity_cap_bit; int loaded_version; bool loaded; bool loading_error; @@ -218,7 +220,6 @@ struct ifs_data { int status; u64 scan_details; u32 cur_batch; - int test_num; }; struct ifs_work { @@ -227,7 +228,8 @@ struct ifs_work { }; struct ifs_device { - struct ifs_data data; + const struct ifs_test_caps *test_caps; + struct ifs_data rw_data; struct miscdevice misc; }; @@ -236,7 +238,15 @@ static inline struct ifs_data *ifs_get_data(struct device *dev) struct miscdevice *m = dev_get_drvdata(dev); struct ifs_device *d = container_of(m, struct ifs_device, misc); - return &d->data; + return &d->rw_data; +} + +static inline const struct ifs_test_caps *ifs_get_test_caps(struct device *dev) +{ + struct miscdevice *m = dev_get_drvdata(dev); + struct ifs_device *d = container_of(m, struct ifs_device, misc); + + return d->test_caps; } extern bool *ifs_pkg_auth; diff --git a/drivers/platform/x86/intel/ifs/load.c b/drivers/platform/x86/intel/ifs/load.c index 74a50e99cacd..61dffb4c8a1d 100644 --- a/drivers/platform/x86/intel/ifs/load.c +++ b/drivers/platform/x86/intel/ifs/load.c @@ -257,13 +257,14 @@ static int image_sanity_check(struct device *dev, const struct microcode_header_ */ int ifs_load_firmware(struct device *dev) { + const struct ifs_test_caps *test = ifs_get_test_caps(dev); struct ifs_data *ifsd = ifs_get_data(dev); const struct firmware *fw; char scan_path[64]; int ret = -EINVAL; snprintf(scan_path, sizeof(scan_path), "intel/ifs_%d/%02x-%02x-%02x-%02x.scan", - ifsd->test_num, boot_cpu_data.x86, boot_cpu_data.x86_model, + test->test_num, boot_cpu_data.x86, boot_cpu_data.x86_model, boot_cpu_data.x86_stepping, ifsd->cur_batch); ret = request_firmware_direct(&fw, scan_path, dev); -- cgit v1.2.3 From d847eddf0ee9c7112003becebe53fd8bf10d8671 Mon Sep 17 00:00:00 2001 From: Jithu Joseph Date: Tue, 21 Mar 2023 17:33:53 -0700 Subject: platform/x86/intel/ifs: IFS cleanup Cleanup incorporating misc review comments - Remove the subdirectory intel_ifs/0 for devicenode [1] - Make plat_ifs_groups non static and use it directly without using a function [2] Link: https://lore.kernel.org/lkml/Y+4kQOtrHt5pdsSO@kroah.com/ [1] Link: https://lore.kernel.org/lkml/Y9nyxNesVHCUXAcH@kroah.com/ [2] Signed-off-by: Jithu Joseph Reviewed-by: Tony Luck Reviewed-by: Hans de Goede Link: https://lore.kernel.org/r/20230322003359.213046-4-jithu.joseph@intel.com Signed-off-by: Hans de Goede --- drivers/platform/x86/intel/ifs/core.c | 6 +++--- drivers/platform/x86/intel/ifs/ifs.h | 2 +- drivers/platform/x86/intel/ifs/sysfs.c | 9 +-------- 3 files changed, 5 insertions(+), 12 deletions(-) (limited to 'drivers/platform') diff --git a/drivers/platform/x86/intel/ifs/core.c b/drivers/platform/x86/intel/ifs/core.c index e2bf728eefdf..f272644617a3 100644 --- a/drivers/platform/x86/intel/ifs/core.c +++ b/drivers/platform/x86/intel/ifs/core.c @@ -20,6 +20,8 @@ static const struct x86_cpu_id ifs_cpu_ids[] __initconst = { }; MODULE_DEVICE_TABLE(x86cpu, ifs_cpu_ids); +ATTRIBUTE_GROUPS(plat_ifs); + bool *ifs_pkg_auth; static const struct ifs_test_caps scan_test = { @@ -31,8 +33,8 @@ static struct ifs_device ifs_device = { .test_caps = &scan_test, .misc = { .name = "intel_ifs_0", - .nodename = "intel_ifs/0", .minor = MISC_DYNAMIC_MINOR, + .groups = plat_ifs_groups, }, }; @@ -55,8 +57,6 @@ static int __init ifs_init(void) if (rdmsrl_safe(MSR_INTEGRITY_CAPS, &msrval)) return -ENODEV; - ifs_device.misc.groups = ifs_get_groups(); - if (!(msrval & BIT(ifs_device.test_caps->integrity_cap_bit))) return -ENODEV; diff --git a/drivers/platform/x86/intel/ifs/ifs.h b/drivers/platform/x86/intel/ifs/ifs.h index d9c1a1f3e31d..55bcc70c2966 100644 --- a/drivers/platform/x86/intel/ifs/ifs.h +++ b/drivers/platform/x86/intel/ifs/ifs.h @@ -252,6 +252,6 @@ static inline const struct ifs_test_caps *ifs_get_test_caps(struct device *dev) extern bool *ifs_pkg_auth; int ifs_load_firmware(struct device *dev); int do_core_test(int cpu, struct device *dev); -const struct attribute_group **ifs_get_groups(void); +extern struct attribute *plat_ifs_attrs[]; #endif diff --git a/drivers/platform/x86/intel/ifs/sysfs.c b/drivers/platform/x86/intel/ifs/sysfs.c index ee636a76b083..2007d8054f04 100644 --- a/drivers/platform/x86/intel/ifs/sysfs.c +++ b/drivers/platform/x86/intel/ifs/sysfs.c @@ -141,7 +141,7 @@ static ssize_t image_version_show(struct device *dev, static DEVICE_ATTR_RO(image_version); /* global scan sysfs attributes */ -static struct attribute *plat_ifs_attrs[] = { +struct attribute *plat_ifs_attrs[] = { &dev_attr_details.attr, &dev_attr_status.attr, &dev_attr_run_test.attr, @@ -149,10 +149,3 @@ static struct attribute *plat_ifs_attrs[] = { &dev_attr_image_version.attr, NULL }; - -ATTRIBUTE_GROUPS(plat_ifs); - -const struct attribute_group **ifs_get_groups(void) -{ - return plat_ifs_groups; -} -- cgit v1.2.3 From d31bbdf42b46cb8dc81deb48c4bf5234dd63d939 Mon Sep 17 00:00:00 2001 From: Jithu Joseph Date: Tue, 21 Mar 2023 17:33:55 -0700 Subject: platform/x86/intel/ifs: Introduce Array Scan test to IFS Array BIST is a new type of core test introduced under the Intel Infield Scan (IFS) suite of tests. Emerald Rapids (EMR) is the first CPU to support Array BIST. Array BIST performs tests on some portions of the core logic such as caches and register files. These are different portions of the silicon compared to the parts tested by the first test type i.e Scan at Field (SAF). Make changes in the device driver init flow to register this new test type with the device driver framework. Each test will have its own sysfs directory (intel_ifs_0 , intel_ifs_1) under misc hierarchy to accommodate for the differences in test type and how they are initiated. Upcoming patches will add actual support. Signed-off-by: Jithu Joseph Reviewed-by: Tony Luck Link: https://lore.kernel.org/r/20230322003359.213046-6-jithu.joseph@intel.com Reviewed-by: Hans de Goede Signed-off-by: Hans de Goede --- drivers/platform/x86/intel/ifs/core.c | 65 +++++++++++++++++++++++++---------- drivers/platform/x86/intel/ifs/ifs.h | 3 ++ 2 files changed, 50 insertions(+), 18 deletions(-) (limited to 'drivers/platform') diff --git a/drivers/platform/x86/intel/ifs/core.c b/drivers/platform/x86/intel/ifs/core.c index f272644617a3..0067eee25f3c 100644 --- a/drivers/platform/x86/intel/ifs/core.c +++ b/drivers/platform/x86/intel/ifs/core.c @@ -16,6 +16,7 @@ static const struct x86_cpu_id ifs_cpu_ids[] __initconst = { X86_MATCH(SAPPHIRERAPIDS_X), + X86_MATCH(EMERALDRAPIDS_X), {} }; MODULE_DEVICE_TABLE(x86cpu, ifs_cpu_ids); @@ -26,23 +27,50 @@ bool *ifs_pkg_auth; static const struct ifs_test_caps scan_test = { .integrity_cap_bit = MSR_INTEGRITY_CAPS_PERIODIC_BIST_BIT, - .test_num = 0, + .test_num = IFS_TYPE_SAF, }; -static struct ifs_device ifs_device = { - .test_caps = &scan_test, - .misc = { - .name = "intel_ifs_0", - .minor = MISC_DYNAMIC_MINOR, - .groups = plat_ifs_groups, +static const struct ifs_test_caps array_test = { + .integrity_cap_bit = MSR_INTEGRITY_CAPS_ARRAY_BIST_BIT, + .test_num = IFS_TYPE_ARRAY_BIST, +}; + +static struct ifs_device ifs_devices[] = { + [IFS_TYPE_SAF] = { + .test_caps = &scan_test, + .misc = { + .name = "intel_ifs_0", + .minor = MISC_DYNAMIC_MINOR, + .groups = plat_ifs_groups, + }, + }, + [IFS_TYPE_ARRAY_BIST] = { + .test_caps = &array_test, + .misc = { + .name = "intel_ifs_1", + .minor = MISC_DYNAMIC_MINOR, + }, }, }; +#define IFS_NUMTESTS ARRAY_SIZE(ifs_devices) + +static void ifs_cleanup(void) +{ + int i; + + for (i = 0; i < IFS_NUMTESTS; i++) { + if (ifs_devices[i].misc.this_device) + misc_deregister(&ifs_devices[i].misc); + } + kfree(ifs_pkg_auth); +} + static int __init ifs_init(void) { const struct x86_cpu_id *m; u64 msrval; - int ret; + int i, ret; m = x86_match_cpu(ifs_cpu_ids); if (!m) @@ -57,26 +85,27 @@ static int __init ifs_init(void) if (rdmsrl_safe(MSR_INTEGRITY_CAPS, &msrval)) return -ENODEV; - if (!(msrval & BIT(ifs_device.test_caps->integrity_cap_bit))) - return -ENODEV; - ifs_pkg_auth = kmalloc_array(topology_max_packages(), sizeof(bool), GFP_KERNEL); if (!ifs_pkg_auth) return -ENOMEM; - ret = misc_register(&ifs_device.misc); - if (ret) { - kfree(ifs_pkg_auth); - return ret; + for (i = 0; i < IFS_NUMTESTS; i++) { + if (!(msrval & BIT(ifs_devices[i].test_caps->integrity_cap_bit))) + continue; + ret = misc_register(&ifs_devices[i].misc); + if (ret) + goto err_exit; } - return 0; + +err_exit: + ifs_cleanup(); + return ret; } static void __exit ifs_exit(void) { - misc_deregister(&ifs_device.misc); - kfree(ifs_pkg_auth); + ifs_cleanup(); } module_init(ifs_init); diff --git a/drivers/platform/x86/intel/ifs/ifs.h b/drivers/platform/x86/intel/ifs/ifs.h index 55bcc70c2966..14789b156299 100644 --- a/drivers/platform/x86/intel/ifs/ifs.h +++ b/drivers/platform/x86/intel/ifs/ifs.h @@ -137,6 +137,9 @@ #define SCAN_TEST_PASS 1 #define SCAN_TEST_FAIL 2 +#define IFS_TYPE_SAF 0 +#define IFS_TYPE_ARRAY_BIST 1 + /* MSR_SCAN_HASHES_STATUS bit fields */ union ifs_scan_hashes_status { u64 data; -- cgit v1.2.3 From 5210fb4e18806648b5e87ecc206f1b35e1253401 Mon Sep 17 00:00:00 2001 From: Jithu Joseph Date: Tue, 21 Mar 2023 17:33:56 -0700 Subject: platform/x86/intel/ifs: Sysfs interface for Array BIST The interface to trigger Array BIST test and obtain its result is similar to the existing scan test. The only notable difference is that, Array BIST doesn't require any test content to be loaded. So binary load related options are not needed for this test. Add sysfs interface for array BIST test, the testing support will be added by subsequent patch. Signed-off-by: Jithu Joseph Reviewed-by: Tony Luck Link: https://lore.kernel.org/r/20230322003359.213046-7-jithu.joseph@intel.com Reviewed-by: Hans de Goede Signed-off-by: Hans de Goede --- drivers/platform/x86/intel/ifs/core.c | 2 ++ drivers/platform/x86/intel/ifs/ifs.h | 1 + drivers/platform/x86/intel/ifs/runtest.c | 13 ++++++++++++- drivers/platform/x86/intel/ifs/sysfs.c | 14 +++++++++----- 4 files changed, 24 insertions(+), 6 deletions(-) (limited to 'drivers/platform') diff --git a/drivers/platform/x86/intel/ifs/core.c b/drivers/platform/x86/intel/ifs/core.c index 0067eee25f3c..306f886b52d2 100644 --- a/drivers/platform/x86/intel/ifs/core.c +++ b/drivers/platform/x86/intel/ifs/core.c @@ -22,6 +22,7 @@ static const struct x86_cpu_id ifs_cpu_ids[] __initconst = { MODULE_DEVICE_TABLE(x86cpu, ifs_cpu_ids); ATTRIBUTE_GROUPS(plat_ifs); +ATTRIBUTE_GROUPS(plat_ifs_array); bool *ifs_pkg_auth; @@ -49,6 +50,7 @@ static struct ifs_device ifs_devices[] = { .misc = { .name = "intel_ifs_1", .minor = MISC_DYNAMIC_MINOR, + .groups = plat_ifs_array_groups, }, }, }; diff --git a/drivers/platform/x86/intel/ifs/ifs.h b/drivers/platform/x86/intel/ifs/ifs.h index 14789b156299..a7d87fb4c412 100644 --- a/drivers/platform/x86/intel/ifs/ifs.h +++ b/drivers/platform/x86/intel/ifs/ifs.h @@ -256,5 +256,6 @@ extern bool *ifs_pkg_auth; int ifs_load_firmware(struct device *dev); int do_core_test(int cpu, struct device *dev); extern struct attribute *plat_ifs_attrs[]; +extern struct attribute *plat_ifs_array_attrs[]; #endif diff --git a/drivers/platform/x86/intel/ifs/runtest.c b/drivers/platform/x86/intel/ifs/runtest.c index 0bfd8fcdd7e8..323752fe5034 100644 --- a/drivers/platform/x86/intel/ifs/runtest.c +++ b/drivers/platform/x86/intel/ifs/runtest.c @@ -236,6 +236,8 @@ static void ifs_test_core(int cpu, struct device *dev) */ int do_core_test(int cpu, struct device *dev) { + const struct ifs_test_caps *test = ifs_get_test_caps(dev); + struct ifs_data *ifsd = ifs_get_data(dev); int ret = 0; /* Prevent CPUs from being taken offline during the scan test */ @@ -247,7 +249,16 @@ int do_core_test(int cpu, struct device *dev) goto out; } - ifs_test_core(cpu, dev); + switch (test->test_num) { + case IFS_TYPE_SAF: + if (!ifsd->loaded) + return -EPERM; + ifs_test_core(cpu, dev); + break; + case IFS_TYPE_ARRAY_BIST: + default: + return -EINVAL; + } out: cpus_read_unlock(); return ret; diff --git a/drivers/platform/x86/intel/ifs/sysfs.c b/drivers/platform/x86/intel/ifs/sysfs.c index 2007d8054f04..d856d6b8fc03 100644 --- a/drivers/platform/x86/intel/ifs/sysfs.c +++ b/drivers/platform/x86/intel/ifs/sysfs.c @@ -64,7 +64,6 @@ static ssize_t run_test_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { - struct ifs_data *ifsd = ifs_get_data(dev); unsigned int cpu; int rc; @@ -75,10 +74,7 @@ static ssize_t run_test_store(struct device *dev, if (down_interruptible(&ifs_sem)) return -EINTR; - if (!ifsd->loaded) - rc = -EPERM; - else - rc = do_core_test(cpu, dev); + rc = do_core_test(cpu, dev); up(&ifs_sem); @@ -149,3 +145,11 @@ struct attribute *plat_ifs_attrs[] = { &dev_attr_image_version.attr, NULL }; + +/* global array sysfs attributes */ +struct attribute *plat_ifs_array_attrs[] = { + &dev_attr_details.attr, + &dev_attr_status.attr, + &dev_attr_run_test.attr, + NULL +}; -- cgit v1.2.3 From fed696ce13662de882c8708ea7d0664d8e9e178b Mon Sep 17 00:00:00 2001 From: Jithu Joseph Date: Tue, 21 Mar 2023 17:33:57 -0700 Subject: platform/x86/intel/ifs: Implement Array BIST test Array BIST test (for a particular core) is triggered by writing to MSR_ARRAY_BIST from one sibling of the core. This will initiate a test for all supported arrays on that CPU. Array BIST test may be aborted before completing all the arrays in the event of an interrupt or other reasons. In this case, kernel will restart the test from that point onwards. Array test will also be aborted when the test fails, in which case the test is stopped immediately without further retry. Signed-off-by: Jithu Joseph Reviewed-by: Tony Luck Link: https://lore.kernel.org/r/20230322003359.213046-8-jithu.joseph@intel.com Reviewed-by: Hans de Goede Signed-off-by: Hans de Goede --- drivers/platform/x86/intel/ifs/ifs.h | 12 +++++ drivers/platform/x86/intel/ifs/runtest.c | 81 ++++++++++++++++++++++++++++++++ 2 files changed, 93 insertions(+) (limited to 'drivers/platform') diff --git a/drivers/platform/x86/intel/ifs/ifs.h b/drivers/platform/x86/intel/ifs/ifs.h index a7d87fb4c412..048131df13bc 100644 --- a/drivers/platform/x86/intel/ifs/ifs.h +++ b/drivers/platform/x86/intel/ifs/ifs.h @@ -127,6 +127,7 @@ #include #include +#define MSR_ARRAY_BIST 0x00000105 #define MSR_COPY_SCAN_HASHES 0x000002c2 #define MSR_SCAN_HASHES_STATUS 0x000002c3 #define MSR_AUTHENTICATE_AND_COPY_CHUNK 0x000002c4 @@ -192,6 +193,17 @@ union ifs_status { }; }; +/* MSR_ARRAY_BIST bit fields */ +union ifs_array { + u64 data; + struct { + u32 array_bitmask; + u16 array_bank; + u16 rsvd :15; + u16 ctrl_result :1; + }; +}; + /* * Driver populated error-codes * 0xFD: Test timed out before completing all the chunks. diff --git a/drivers/platform/x86/intel/ifs/runtest.c b/drivers/platform/x86/intel/ifs/runtest.c index 323752fe5034..1061eb7ec399 100644 --- a/drivers/platform/x86/intel/ifs/runtest.c +++ b/drivers/platform/x86/intel/ifs/runtest.c @@ -229,6 +229,85 @@ static void ifs_test_core(int cpu, struct device *dev) } } +#define SPINUNIT 100 /* 100 nsec */ +static atomic_t array_cpus_out; + +/* + * Simplified cpu sibling rendezvous loop based on microcode loader __wait_for_cpus() + */ +static void wait_for_sibling_cpu(atomic_t *t, long long timeout) +{ + int cpu = smp_processor_id(); + const struct cpumask *smt_mask = cpu_smt_mask(cpu); + int all_cpus = cpumask_weight(smt_mask); + + atomic_inc(t); + while (atomic_read(t) < all_cpus) { + if (timeout < SPINUNIT) + return; + ndelay(SPINUNIT); + timeout -= SPINUNIT; + touch_nmi_watchdog(); + } +} + +static int do_array_test(void *data) +{ + union ifs_array *command = data; + int cpu = smp_processor_id(); + int first; + + /* + * Only one logical CPU on a core needs to trigger the Array test via MSR write. + */ + first = cpumask_first(cpu_smt_mask(cpu)); + + if (cpu == first) { + wrmsrl(MSR_ARRAY_BIST, command->data); + /* Pass back the result of the test */ + rdmsrl(MSR_ARRAY_BIST, command->data); + } + + /* Tests complete faster if the sibling is spinning here */ + wait_for_sibling_cpu(&array_cpus_out, NSEC_PER_SEC); + + return 0; +} + +static void ifs_array_test_core(int cpu, struct device *dev) +{ + union ifs_array command = {}; + bool timed_out = false; + struct ifs_data *ifsd; + unsigned long timeout; + + ifsd = ifs_get_data(dev); + + command.array_bitmask = ~0U; + timeout = jiffies + HZ / 2; + + do { + if (time_after(jiffies, timeout)) { + timed_out = true; + break; + } + atomic_set(&array_cpus_out, 0); + stop_core_cpuslocked(cpu, do_array_test, &command); + + if (command.ctrl_result) + break; + } while (command.array_bitmask); + + ifsd->scan_details = command.data; + + if (command.ctrl_result) + ifsd->status = SCAN_TEST_FAIL; + else if (timed_out || command.array_bitmask) + ifsd->status = SCAN_NOT_TESTED; + else + ifsd->status = SCAN_TEST_PASS; +} + /* * Initiate per core test. It wakes up work queue threads on the target cpu and * its sibling cpu. Once all sibling threads wake up, the scan test gets executed and @@ -256,6 +335,8 @@ int do_core_test(int cpu, struct device *dev) ifs_test_core(cpu, dev); break; case IFS_TYPE_ARRAY_BIST: + ifs_array_test_core(cpu, dev); + break; default: return -EINVAL; } -- cgit v1.2.3 From 2b965dc05dc14bbe00efa3038aa1a3932ca30880 Mon Sep 17 00:00:00 2001 From: Jithu Joseph Date: Tue, 21 Mar 2023 17:33:58 -0700 Subject: platform/x86/intel/ifs: Update IFS doc Array BIST is the second test supported by IFS. Modify IFS doc entry to be more general. Signed-off-by: Jithu Joseph Reviewed-by: Tony Luck Link: https://lore.kernel.org/r/20230322003359.213046-9-jithu.joseph@intel.com Reviewed-by: Hans de Goede Signed-off-by: Hans de Goede --- drivers/platform/x86/intel/ifs/ifs.h | 25 ++++++++++++++----------- 1 file changed, 14 insertions(+), 11 deletions(-) (limited to 'drivers/platform') diff --git a/drivers/platform/x86/intel/ifs/ifs.h b/drivers/platform/x86/intel/ifs/ifs.h index 048131df13bc..93191855890f 100644 --- a/drivers/platform/x86/intel/ifs/ifs.h +++ b/drivers/platform/x86/intel/ifs/ifs.h @@ -17,7 +17,7 @@ * In Field Scan (IFS) is a hardware feature to run circuit level tests on * a CPU core to detect problems that are not caught by parity or ECC checks. * Future CPUs will support more than one type of test which will show up - * with a new platform-device instance-id, for now only .0 is exposed. + * with a new platform-device instance-id. * * * IFS Image @@ -25,7 +25,10 @@ * * Intel provides a firmware file containing the scan tests via * github [#f1]_. Similar to microcode there is a separate file for each - * family-model-stepping. + * family-model-stepping. IFS Images are not applicable for some test types. + * Wherever applicable the sysfs directory would provide a "current_batch" file + * (see below) for loading the image. + * * * IFS Image Loading * ----------------- @@ -35,7 +38,7 @@ * SHA hashes for the test. Then the tests themselves. Status MSRs provide * feedback on the success/failure of these steps. * - * The test files are kept in a fixed location: /lib/firmware/intel/ifs_0/ + * The test files are kept in a fixed location: /lib/firmware/intel/ifs_/ * For e.g if there are 3 test files, they would be named in the following * fashion: * ff-mm-ss-01.scan @@ -47,7 +50,7 @@ * (e.g 1, 2 or 3 in the above scenario) into the curent_batch file. * To load ff-mm-ss-02.scan, the following command can be used:: * - * # echo 2 > /sys/devices/virtual/misc/intel_ifs_0/current_batch + * # echo 2 > /sys/devices/virtual/misc/intel_ifs_/current_batch * * The above file can also be read to know the currently loaded image. * @@ -69,16 +72,16 @@ * to migrate those applications to other cores before running a core test. * It may also be necessary to redirect interrupts to other CPUs. * - * In all cases reading the SCAN_STATUS MSR provides details on what + * In all cases reading the corresponding test's STATUS MSR provides details on what * happened. The driver makes the value of this MSR visible to applications * via the "details" file (see below). Interrupted tests may be restarted. * - * The IFS driver provides sysfs interfaces via /sys/devices/virtual/misc/intel_ifs_0/ + * The IFS driver provides sysfs interfaces via /sys/devices/virtual/misc/intel_ifs_/ * to control execution: * * Test a specific core:: * - * # echo > /sys/devices/virtual/misc/intel_ifs_0/run_test + * # echo > /sys/devices/virtual/misc/intel_ifs_/run_test * * when HT is enabled any of the sibling cpu# can be specified to test * its corresponding physical core. Since the tests are per physical core, @@ -87,21 +90,21 @@ * * For e.g. to test core corresponding to cpu5 * - * # echo 5 > /sys/devices/virtual/misc/intel_ifs_0/run_test + * # echo 5 > /sys/devices/virtual/misc/intel_ifs_/run_test * * Results of the last test is provided in /sys:: * - * $ cat /sys/devices/virtual/misc/intel_ifs_0/status + * $ cat /sys/devices/virtual/misc/intel_ifs_/status * pass * * Status can be one of pass, fail, untested * * Additional details of the last test is provided by the details file:: * - * $ cat /sys/devices/virtual/misc/intel_ifs_0/details + * $ cat /sys/devices/virtual/misc/intel_ifs_/details * 0x8081 * - * The details file reports the hex value of the SCAN_STATUS MSR. + * The details file reports the hex value of the test specific status MSR. * Hardware defined error codes are documented in volume 4 of the Intel * Software Developer's Manual but the error_code field may contain one of * the following driver defined software codes: -- cgit v1.2.3 From 710ddfbfda939b84794fa3912047bbc6754cbddc Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Sat, 25 Mar 2023 14:08:02 +0300 Subject: platform/x86: ISST: unlock on error path in tpmi_sst_init() Call mutex_unlock(&isst_tpmi_dev_lock) before returning on this error path. Fixes: d805456c712f ("platform/x86: ISST: Enumerate TPMI SST and create framework") Signed-off-by: Dan Carpenter Acked-by: Srinivas Pandruvada Link: https://lore.kernel.org/r/dcdebbb7-7de6-4d04-8e7a-43d5ca043484@kili.mountain Reviewed-by: Hans de Goede Signed-off-by: Hans de Goede --- drivers/platform/x86/intel/speed_select_if/isst_tpmi_core.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) (limited to 'drivers/platform') diff --git a/drivers/platform/x86/intel/speed_select_if/isst_tpmi_core.c b/drivers/platform/x86/intel/speed_select_if/isst_tpmi_core.c index cdb56a18ea17..664d2ee60385 100644 --- a/drivers/platform/x86/intel/speed_select_if/isst_tpmi_core.c +++ b/drivers/platform/x86/intel/speed_select_if/isst_tpmi_core.c @@ -1399,8 +1399,10 @@ int tpmi_sst_init(void) isst_common.sst_inst = kcalloc(topology_max_packages(), sizeof(*isst_common.sst_inst), GFP_KERNEL); - if (!isst_common.sst_inst) - return -ENOMEM; + if (!isst_common.sst_inst) { + ret = -ENOMEM; + goto init_done; + } memset(&cb, 0, sizeof(cb)); cb.cmd_size = sizeof(struct isst_if_io_reg); -- cgit v1.2.3 From 0de0ab9a659a419d1aa9c2516887e95a51455da0 Mon Sep 17 00:00:00 2001 From: Andrew Kallmeyer Date: Tue, 28 Mar 2023 18:45:58 -0700 Subject: platform/x86: Move ideapad ACPI helpers to a new header MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit These functions will be used by a driver written by Gergo Koteles to detect the tablet mode switch in Lenovo Yoga laptops. These changes were discussed in review of that patch. This is the minimal set of functions needed in that driver, there are several more small functions left in the ACPI Helpers section in ideapad-laptop.c. The only change is the functions are now marked inline as requested in the review comments. Signed-off-by: Andrew Kallmeyer Link: https://lore.kernel.org/r/20221004214332.35934-1-soyer@irl.hu/ Tested-by: André Apitzsch Link: https://lore.kernel.org/r/20230329014559.44494-2-kallmeyeras@gmail.com Reviewed-by: Hans de Goede Signed-off-by: Hans de Goede --- drivers/platform/x86/ideapad-laptop.c | 135 +----------------------------- drivers/platform/x86/ideapad-laptop.h | 151 ++++++++++++++++++++++++++++++++++ 2 files changed, 152 insertions(+), 134 deletions(-) create mode 100644 drivers/platform/x86/ideapad-laptop.h (limited to 'drivers/platform') diff --git a/drivers/platform/x86/ideapad-laptop.c b/drivers/platform/x86/ideapad-laptop.c index b5ef3452da1f..0123391d45ea 100644 --- a/drivers/platform/x86/ideapad-laptop.c +++ b/drivers/platform/x86/ideapad-laptop.c @@ -20,7 +20,6 @@ #include #include #include -#include #include #include #include @@ -31,6 +30,7 @@ #include #include #include +#include "ideapad-laptop.h" #include @@ -85,33 +85,6 @@ enum { SALS_FNLOCK_OFF = 0xf, }; -enum { - VPCCMD_R_VPC1 = 0x10, - VPCCMD_R_BL_MAX, - VPCCMD_R_BL, - VPCCMD_W_BL, - VPCCMD_R_WIFI, - VPCCMD_W_WIFI, - VPCCMD_R_BT, - VPCCMD_W_BT, - VPCCMD_R_BL_POWER, - VPCCMD_R_NOVO, - VPCCMD_R_VPC2, - VPCCMD_R_TOUCHPAD, - VPCCMD_W_TOUCHPAD, - VPCCMD_R_CAMERA, - VPCCMD_W_CAMERA, - VPCCMD_R_3G, - VPCCMD_W_3G, - VPCCMD_R_ODD, /* 0x21 */ - VPCCMD_W_FAN, - VPCCMD_R_RF, - VPCCMD_W_RF, - VPCCMD_R_FAN = 0x2B, - VPCCMD_R_SPECIAL_BUTTONS = 0x31, - VPCCMD_W_BL_POWER = 0x33, -}; - struct ideapad_dytc_priv { enum platform_profile_option current_profile; struct platform_profile_handler pprof; @@ -227,7 +200,6 @@ static void ideapad_shared_exit(struct ideapad_private *priv) /* * ACPI Helpers */ -#define IDEAPAD_EC_TIMEOUT 200 /* in ms */ static int eval_int(acpi_handle handle, const char *name, unsigned long *res) { @@ -270,116 +242,11 @@ static int exec_sals(acpi_handle handle, unsigned long arg) return exec_simple_method(handle, "SALS", arg); } -static int eval_int_with_arg(acpi_handle handle, const char *name, unsigned long arg, unsigned long *res) -{ - struct acpi_object_list params; - unsigned long long result; - union acpi_object in_obj; - acpi_status status; - - params.count = 1; - params.pointer = &in_obj; - in_obj.type = ACPI_TYPE_INTEGER; - in_obj.integer.value = arg; - - status = acpi_evaluate_integer(handle, (char *)name, ¶ms, &result); - if (ACPI_FAILURE(status)) - return -EIO; - - if (res) - *res = result; - - return 0; -} - static int eval_dytc(acpi_handle handle, unsigned long cmd, unsigned long *res) { return eval_int_with_arg(handle, "DYTC", cmd, res); } -static int eval_vpcr(acpi_handle handle, unsigned long cmd, unsigned long *res) -{ - return eval_int_with_arg(handle, "VPCR", cmd, res); -} - -static int eval_vpcw(acpi_handle handle, unsigned long cmd, unsigned long data) -{ - struct acpi_object_list params; - union acpi_object in_obj[2]; - acpi_status status; - - params.count = 2; - params.pointer = in_obj; - in_obj[0].type = ACPI_TYPE_INTEGER; - in_obj[0].integer.value = cmd; - in_obj[1].type = ACPI_TYPE_INTEGER; - in_obj[1].integer.value = data; - - status = acpi_evaluate_object(handle, "VPCW", ¶ms, NULL); - if (ACPI_FAILURE(status)) - return -EIO; - - return 0; -} - -static int read_ec_data(acpi_handle handle, unsigned long cmd, unsigned long *data) -{ - unsigned long end_jiffies, val; - int err; - - err = eval_vpcw(handle, 1, cmd); - if (err) - return err; - - end_jiffies = jiffies + msecs_to_jiffies(IDEAPAD_EC_TIMEOUT) + 1; - - while (time_before(jiffies, end_jiffies)) { - schedule(); - - err = eval_vpcr(handle, 1, &val); - if (err) - return err; - - if (val == 0) - return eval_vpcr(handle, 0, data); - } - - acpi_handle_err(handle, "timeout in %s\n", __func__); - - return -ETIMEDOUT; -} - -static int write_ec_cmd(acpi_handle handle, unsigned long cmd, unsigned long data) -{ - unsigned long end_jiffies, val; - int err; - - err = eval_vpcw(handle, 0, data); - if (err) - return err; - - err = eval_vpcw(handle, 1, cmd); - if (err) - return err; - - end_jiffies = jiffies + msecs_to_jiffies(IDEAPAD_EC_TIMEOUT) + 1; - - while (time_before(jiffies, end_jiffies)) { - schedule(); - - err = eval_vpcr(handle, 1, &val); - if (err) - return err; - - if (val == 0) - return 0; - } - - acpi_handle_err(handle, "timeout in %s\n", __func__); - - return -ETIMEDOUT; -} - /* * debugfs */ diff --git a/drivers/platform/x86/ideapad-laptop.h b/drivers/platform/x86/ideapad-laptop.h new file mode 100644 index 000000000000..a99664725770 --- /dev/null +++ b/drivers/platform/x86/ideapad-laptop.h @@ -0,0 +1,151 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * ideapad-laptop.h - Lenovo IdeaPad ACPI Extras + * + * Copyright © 2010 Intel Corporation + * Copyright © 2010 David Woodhouse + */ + +#ifndef _IDEAPAD_LAPTOP_H_ +#define _IDEAPAD_LAPTOP_H_ + +#include +#include +#include + +enum { + VPCCMD_R_VPC1 = 0x10, + VPCCMD_R_BL_MAX, + VPCCMD_R_BL, + VPCCMD_W_BL, + VPCCMD_R_WIFI, + VPCCMD_W_WIFI, + VPCCMD_R_BT, + VPCCMD_W_BT, + VPCCMD_R_BL_POWER, + VPCCMD_R_NOVO, + VPCCMD_R_VPC2, + VPCCMD_R_TOUCHPAD, + VPCCMD_W_TOUCHPAD, + VPCCMD_R_CAMERA, + VPCCMD_W_CAMERA, + VPCCMD_R_3G, + VPCCMD_W_3G, + VPCCMD_R_ODD, /* 0x21 */ + VPCCMD_W_FAN, + VPCCMD_R_RF, + VPCCMD_W_RF, + VPCCMD_R_FAN = 0x2B, + VPCCMD_R_SPECIAL_BUTTONS = 0x31, + VPCCMD_W_BL_POWER = 0x33, +}; + +static inline int eval_int_with_arg(acpi_handle handle, const char *name, unsigned long arg, unsigned long *res) +{ + struct acpi_object_list params; + unsigned long long result; + union acpi_object in_obj; + acpi_status status; + + params.count = 1; + params.pointer = &in_obj; + in_obj.type = ACPI_TYPE_INTEGER; + in_obj.integer.value = arg; + + status = acpi_evaluate_integer(handle, (char *)name, ¶ms, &result); + if (ACPI_FAILURE(status)) + return -EIO; + + if (res) + *res = result; + + return 0; +} + +static inline int eval_vpcr(acpi_handle handle, unsigned long cmd, unsigned long *res) +{ + return eval_int_with_arg(handle, "VPCR", cmd, res); +} + +static inline int eval_vpcw(acpi_handle handle, unsigned long cmd, unsigned long data) +{ + struct acpi_object_list params; + union acpi_object in_obj[2]; + acpi_status status; + + params.count = 2; + params.pointer = in_obj; + in_obj[0].type = ACPI_TYPE_INTEGER; + in_obj[0].integer.value = cmd; + in_obj[1].type = ACPI_TYPE_INTEGER; + in_obj[1].integer.value = data; + + status = acpi_evaluate_object(handle, "VPCW", ¶ms, NULL); + if (ACPI_FAILURE(status)) + return -EIO; + + return 0; +} + +#define IDEAPAD_EC_TIMEOUT 200 /* in ms */ + +static inline int read_ec_data(acpi_handle handle, unsigned long cmd, unsigned long *data) +{ + unsigned long end_jiffies, val; + int err; + + err = eval_vpcw(handle, 1, cmd); + if (err) + return err; + + end_jiffies = jiffies + msecs_to_jiffies(IDEAPAD_EC_TIMEOUT) + 1; + + while (time_before(jiffies, end_jiffies)) { + schedule(); + + err = eval_vpcr(handle, 1, &val); + if (err) + return err; + + if (val == 0) + return eval_vpcr(handle, 0, data); + } + + acpi_handle_err(handle, "timeout in %s\n", __func__); + + return -ETIMEDOUT; +} + +static inline int write_ec_cmd(acpi_handle handle, unsigned long cmd, unsigned long data) +{ + unsigned long end_jiffies, val; + int err; + + err = eval_vpcw(handle, 0, data); + if (err) + return err; + + err = eval_vpcw(handle, 1, cmd); + if (err) + return err; + + end_jiffies = jiffies + msecs_to_jiffies(IDEAPAD_EC_TIMEOUT) + 1; + + while (time_before(jiffies, end_jiffies)) { + schedule(); + + err = eval_vpcr(handle, 1, &val); + if (err) + return err; + + if (val == 0) + return 0; + } + + acpi_handle_err(handle, "timeout in %s\n", __func__); + + return -ETIMEDOUT; +} + +#undef IDEAPAD_EC_TIMEOUT +#endif /* !_IDEAPAD_LAPTOP_H_ */ -- cgit v1.2.3 From e82882cdd241b78d305df1441c624fbbbd07af05 Mon Sep 17 00:00:00 2001 From: Gergo Koteles Date: Tue, 28 Mar 2023 18:45:59 -0700 Subject: platform/x86: Add driver for Yoga Tablet Mode switch MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This WMI driver for the tablet mode control switch for Lenovo Yoga notebooks was originally written by Gergo Koteles. The mode is mapped to a SW_TABLET_MODE switch capable input device. Andrew followed the suggestions that were posted in reply to Gergo's RFC patch, and on the v1 & v2 versions of this patch to follow-up and get it merged. Changes from Gergo's RFC: - Refactored obtaining a reference to the EC ACPI device needed for the quirk implementation as suggested by Hans de Goede - Applied small fixes and switched to always registering handles with the driver for automatic cleanup as suggested by Barnabás Pőcze. - Merged the lenovo_ymc_trigger_ec function with the ideapad_trigger_ymc_next_read function since it was no longer external. - Added the word "Tablet" to the driver description to hopefully make it more clear. - Fixed the LENOVO_YMC_QUERY_METHOD ID and the name string for the EC APCI device trigged for the quirk - Triggered the input event on probe so that the initial tablet mode state when the driver is loaded is reported to userspace as suggested by Armin Wolf. - Restricted the permissions of the ec_trigger parameter as suggested by Armin Wolf. Also updated the description. We have tested this on the Yoga 7 14AIL7 for the non-quirk path and on the Yoga 7 14ARB7 which has the firmware bug that requires triggering the embedded controller to send the mode change events. This workaround is also used by the Windows drivers. According to reports at https://github.com/lukas-w/yoga-usage-mode, which uses the same WMI devices, the following models should also work: Yoga C940, Ideapad flex 14API, Yoga 9 14IAP7, Yoga 7 14ARB7, etc. Signed-off-by: Gergo Koteles Co-developed-by: Andrew Kallmeyer Signed-off-by: Andrew Kallmeyer Link: https://lore.kernel.org/r/20221004214332.35934-1-soyer@irl.hu/ Link: https://lore.kernel.org/r/20230310041726.217447-1-kallmeyeras@gmail.com/ Link: https://lore.kernel.org/r/20230323025200.5462-1-kallmeyeras@gmail.com/ Tested-by: André Apitzsch Link: https://lore.kernel.org/r/20230329014559.44494-3-kallmeyeras@gmail.com Reviewed-by: Hans de Goede Signed-off-by: Hans de Goede --- drivers/platform/x86/Kconfig | 9 ++ drivers/platform/x86/Makefile | 1 + drivers/platform/x86/ideapad-laptop.h | 1 + drivers/platform/x86/lenovo-ymc.c | 187 ++++++++++++++++++++++++++++++++++ 4 files changed, 198 insertions(+) create mode 100644 drivers/platform/x86/lenovo-ymc.c (limited to 'drivers/platform') diff --git a/drivers/platform/x86/Kconfig b/drivers/platform/x86/Kconfig index 1899be67dc0c..22052031c719 100644 --- a/drivers/platform/x86/Kconfig +++ b/drivers/platform/x86/Kconfig @@ -461,6 +461,15 @@ config IDEAPAD_LAPTOP This is a driver for Lenovo IdeaPad netbooks contains drivers for rfkill switch, hotkey, fan control and backlight control. +config LENOVO_YMC + tristate "Lenovo Yoga Tablet Mode Control" + depends on ACPI_WMI + depends on INPUT + select INPUT_SPARSEKMAP + help + This driver maps the Tablet Mode Control switch to SW_TABLET_MODE input + events for Lenovo Yoga notebooks. + config SENSORS_HDAPS tristate "Thinkpad Hard Drive Active Protection System (hdaps)" depends on INPUT diff --git a/drivers/platform/x86/Makefile b/drivers/platform/x86/Makefile index 27550cf6d4fb..2cafe51ec4d8 100644 --- a/drivers/platform/x86/Makefile +++ b/drivers/platform/x86/Makefile @@ -62,6 +62,7 @@ obj-$(CONFIG_UV_SYSFS) += uv_sysfs.o # IBM Thinkpad and Lenovo obj-$(CONFIG_IBM_RTL) += ibm_rtl.o obj-$(CONFIG_IDEAPAD_LAPTOP) += ideapad-laptop.o +obj-$(CONFIG_LENOVO_YMC) += lenovo-ymc.o obj-$(CONFIG_SENSORS_HDAPS) += hdaps.o obj-$(CONFIG_THINKPAD_ACPI) += thinkpad_acpi.o obj-$(CONFIG_THINKPAD_LMI) += think-lmi.o diff --git a/drivers/platform/x86/ideapad-laptop.h b/drivers/platform/x86/ideapad-laptop.h index a99664725770..4498a96de597 100644 --- a/drivers/platform/x86/ideapad-laptop.h +++ b/drivers/platform/x86/ideapad-laptop.h @@ -35,6 +35,7 @@ enum { VPCCMD_W_FAN, VPCCMD_R_RF, VPCCMD_W_RF, + VPCCMD_W_YMC = 0x2A, VPCCMD_R_FAN = 0x2B, VPCCMD_R_SPECIAL_BUTTONS = 0x31, VPCCMD_W_BL_POWER = 0x33, diff --git a/drivers/platform/x86/lenovo-ymc.c b/drivers/platform/x86/lenovo-ymc.c new file mode 100644 index 000000000000..41676188b373 --- /dev/null +++ b/drivers/platform/x86/lenovo-ymc.c @@ -0,0 +1,187 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * lenovo-ymc.c - Lenovo Yoga Mode Control driver + * + * Copyright © 2022 Gergo Koteles + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include +#include +#include +#include +#include +#include "ideapad-laptop.h" + +#define LENOVO_YMC_EVENT_GUID "06129D99-6083-4164-81AD-F092F9D773A6" +#define LENOVO_YMC_QUERY_GUID "09B0EE6E-C3FD-4243-8DA1-7911FF80BB8C" + +#define LENOVO_YMC_QUERY_INSTANCE 0 +#define LENOVO_YMC_QUERY_METHOD 0x01 + +static bool ec_trigger __read_mostly; +module_param(ec_trigger, bool, 0444); +MODULE_PARM_DESC(ec_trigger, "Enable EC triggering work-around to force emitting tablet mode events"); + +static const struct dmi_system_id ec_trigger_quirk_dmi_table[] = { + { + /* Lenovo Yoga 7 14ARB7 */ + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"), + DMI_MATCH(DMI_PRODUCT_NAME, "82QF"), + }, + }, + { } +}; + +struct lenovo_ymc_private { + struct input_dev *input_dev; + struct acpi_device *ec_acpi_dev; +}; + +static void lenovo_ymc_trigger_ec(struct wmi_device *wdev, struct lenovo_ymc_private *priv) +{ + int err; + + if (!priv->ec_acpi_dev) + return; + + err = write_ec_cmd(priv->ec_acpi_dev->handle, VPCCMD_W_YMC, 1); + if (err) + dev_warn(&wdev->dev, "Could not write YMC: %d\n", err); +} + +static const struct key_entry lenovo_ymc_keymap[] = { + /* Laptop */ + { KE_SW, 0x01, { .sw = { SW_TABLET_MODE, 0 } } }, + /* Tablet */ + { KE_SW, 0x02, { .sw = { SW_TABLET_MODE, 1 } } }, + /* Drawing Board */ + { KE_SW, 0x03, { .sw = { SW_TABLET_MODE, 1 } } }, + /* Tent */ + { KE_SW, 0x04, { .sw = { SW_TABLET_MODE, 1 } } }, + { KE_END }, +}; + +static void lenovo_ymc_notify(struct wmi_device *wdev, union acpi_object *data) +{ + struct lenovo_ymc_private *priv = dev_get_drvdata(&wdev->dev); + u32 input_val = 0; + struct acpi_buffer input = { sizeof(input_val), &input_val }; + struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, NULL }; + union acpi_object *obj; + acpi_status status; + int code; + + status = wmi_evaluate_method(LENOVO_YMC_QUERY_GUID, + LENOVO_YMC_QUERY_INSTANCE, + LENOVO_YMC_QUERY_METHOD, + &input, &output); + + if (ACPI_FAILURE(status)) { + dev_warn(&wdev->dev, + "Failed to evaluate query method: %s\n", + acpi_format_exception(status)); + return; + } + + obj = output.pointer; + + if (obj->type != ACPI_TYPE_INTEGER) { + dev_warn(&wdev->dev, + "WMI event data is not an integer\n"); + goto free_obj; + } + code = obj->integer.value; + + if (!sparse_keymap_report_event(priv->input_dev, code, 1, true)) + dev_warn(&wdev->dev, "Unknown key %d pressed\n", code); + +free_obj: + kfree(obj); + lenovo_ymc_trigger_ec(wdev, priv); +} + +static void acpi_dev_put_helper(void *p) { acpi_dev_put(p); } + +static int lenovo_ymc_probe(struct wmi_device *wdev, const void *ctx) +{ + struct lenovo_ymc_private *priv; + struct input_dev *input_dev; + int err; + + ec_trigger |= dmi_check_system(ec_trigger_quirk_dmi_table); + + priv = devm_kzalloc(&wdev->dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + if (ec_trigger) { + pr_debug("Lenovo YMC enable EC triggering.\n"); + priv->ec_acpi_dev = acpi_dev_get_first_match_dev("VPC2004", NULL, -1); + + if (!priv->ec_acpi_dev) { + dev_err(&wdev->dev, "Could not find EC ACPI device.\n"); + return -ENODEV; + } + err = devm_add_action_or_reset(&wdev->dev, + acpi_dev_put_helper, priv->ec_acpi_dev); + if (err) { + dev_err(&wdev->dev, + "Could not clean up EC ACPI device: %d\n", err); + return err; + } + } + + input_dev = devm_input_allocate_device(&wdev->dev); + if (!input_dev) + return -ENOMEM; + + input_dev->name = "Lenovo Yoga Tablet Mode Control switch"; + input_dev->phys = LENOVO_YMC_EVENT_GUID "/input0"; + input_dev->id.bustype = BUS_HOST; + input_dev->dev.parent = &wdev->dev; + err = sparse_keymap_setup(input_dev, lenovo_ymc_keymap, NULL); + if (err) { + dev_err(&wdev->dev, + "Could not set up input device keymap: %d\n", err); + return err; + } + + err = input_register_device(input_dev); + if (err) { + dev_err(&wdev->dev, + "Could not register input device: %d\n", err); + return err; + } + + priv->input_dev = input_dev; + dev_set_drvdata(&wdev->dev, priv); + + /* Report the state for the first time on probe */ + lenovo_ymc_trigger_ec(wdev, priv); + lenovo_ymc_notify(wdev, NULL); + return 0; +} + +static const struct wmi_device_id lenovo_ymc_wmi_id_table[] = { + { .guid_string = LENOVO_YMC_EVENT_GUID }, + { } +}; +MODULE_DEVICE_TABLE(wmi, lenovo_ymc_wmi_id_table); + +static struct wmi_driver lenovo_ymc_driver = { + .driver = { + .name = "lenovo-ymc", + }, + .id_table = lenovo_ymc_wmi_id_table, + .probe = lenovo_ymc_probe, + .notify = lenovo_ymc_notify, +}; + +module_wmi_driver(lenovo_ymc_driver); + +MODULE_AUTHOR("Gergo Koteles "); +MODULE_DESCRIPTION("Lenovo Yoga Mode Control driver"); +MODULE_LICENSE("GPL"); -- cgit v1.2.3 From 3e9adae9336f1d530f342e5d5fbfc7d349f25b95 Mon Sep 17 00:00:00 2001 From: Thomas Weißschuh Date: Wed, 29 Mar 2023 04:50:25 +0000 Subject: platform/x86: think-lmi: Remove unnecessary casts for attributes MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit These casts are unnecessary and could break if structure layouts are randomized or implementation details change. Use the proper syntax that works without casts. Also remove some unnecessary braces that checkpatch complains about. Signed-off-by: Thomas Weißschuh Link: https://lore.kernel.org/r/20230329-think-lmi-attrs-v1-1-5794f2367cc2@weissschuh.net Reviewed-by: Hans de Goede Signed-off-by: Hans de Goede --- drivers/platform/x86/think-lmi.c | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) (limited to 'drivers/platform') diff --git a/drivers/platform/x86/think-lmi.c b/drivers/platform/x86/think-lmi.c index cc66f7cbccf2..5a7ac5ca283c 100644 --- a/drivers/platform/x86/think-lmi.c +++ b/drivers/platform/x86/think-lmi.c @@ -862,19 +862,18 @@ static umode_t auth_attr_is_visible(struct kobject *kobj, struct tlmi_pwd_setting *setting = to_tlmi_pwd_setting(kobj); /* We only want to display level and index settings on HDD/NVMe */ - if ((attr == (struct attribute *)&auth_index) || - (attr == (struct attribute *)&auth_level)) { + if (attr == &auth_index.attr || attr == &auth_level.attr) { if ((setting == tlmi_priv.pwd_hdd) || (setting == tlmi_priv.pwd_nvme)) return attr->mode; return 0; } /* We only display certificates on Admin account, if supported */ - if ((attr == (struct attribute *)&auth_certificate) || - (attr == (struct attribute *)&auth_signature) || - (attr == (struct attribute *)&auth_save_signature) || - (attr == (struct attribute *)&auth_cert_thumb) || - (attr == (struct attribute *)&auth_cert_to_password)) { + if (attr == &auth_certificate.attr || + attr == &auth_signature.attr || + attr == &auth_save_signature.attr || + attr == &auth_cert_thumb.attr || + attr == &auth_cert_to_password.attr) { if ((setting == tlmi_priv.pwd_admin) && tlmi_priv.certificate_support) return attr->mode; return 0; -- cgit v1.2.3 From 01862d01cb36ad36233939c07d6438a2f83dcd46 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Sat, 1 Apr 2023 17:07:35 +0200 Subject: platform/x86: x86-android-tablets: Use LP8557 in direct mode on both the Yoga 830 and the 1050 Both the Lenovo Yoga Tablet 2 830 and 1050 models use an TI LP8557 LED backlight controller. On the 1050 the LP8557's PWM input is connected to the PMIC's PWM output and everything works fine with the defaults programmed into the LP8557 by the BIOS. But on the 830 the LP8557's PWM input is connected to a PWM output coming from the LCD panel's controller. The Android code has a hack in the i915 driver to write the non-standard DSI reg 0x9f with the desired backlight level to set the duty-cycle of the LCD's PWM output. To avoid having to have a similar hack in the mainline kernel the LP8557 entry in lenovo_yoga_tab2_830_1050_i2c_clients instead just programs the LP8557 to directly set the level, ignoring the PWM input. So far we have only been instantiating the LP8557 i2c_client for direct backlight control on the 830 model. But we want hide/disable the intel_backlight interface on the 830 model to avoid having 2 backlight interfaces for the same LCD panel. And the 830 and 1050 share the same DMI strings. So this will hide the intel_backlight interface on the 1050 model too. To avoid this causing problems make the backlight handling consistent between the 2 models and always directly use the LP8557. This also simplifies the code. Signed-off-by: Hans de Goede Reviewed-by: Andy Shevchenko Link: https://lore.kernel.org/r/20230401150737.597417-1-hdegoede@redhat.com --- drivers/platform/x86/x86-android-tablets/lenovo.c | 29 +++++----------------- .../x86/x86-android-tablets/x86-android-tablets.h | 3 +-- 2 files changed, 7 insertions(+), 25 deletions(-) (limited to 'drivers/platform') diff --git a/drivers/platform/x86/x86-android-tablets/lenovo.c b/drivers/platform/x86/x86-android-tablets/lenovo.c index 0baad9970820..d9d6dccc53c8 100644 --- a/drivers/platform/x86/x86-android-tablets/lenovo.c +++ b/drivers/platform/x86/x86-android-tablets/lenovo.c @@ -162,9 +162,9 @@ static struct gpiod_lookup_table * const lenovo_yoga_tab2_830_1050_gpios[] = { static int __init lenovo_yoga_tab2_830_1050_init(void); static void lenovo_yoga_tab2_830_1050_exit(void); -struct x86_dev_info lenovo_yoga_tab2_830_1050_info __initdata = { +const struct x86_dev_info lenovo_yoga_tab2_830_1050_info __initconst = { .i2c_client_info = lenovo_yoga_tab2_830_1050_i2c_clients, - /* i2c_client_count gets set by lenovo_yoga_tab2_830_1050_init() */ + .i2c_client_count = ARRAY_SIZE(lenovo_yoga_tab2_830_1050_i2c_clients), .pdev_info = int3496_pdevs, .pdev_count = 1, .gpio_button = &lenovo_yoga_tab2_830_1050_lid, @@ -177,23 +177,10 @@ struct x86_dev_info lenovo_yoga_tab2_830_1050_info __initdata = { /* * The Lenovo Yoga Tablet 2 830 and 1050 (8" vs 10") versions use the same - * mainboard, but they need some different treatment related to the display: - * 1. The 830 uses a portrait LCD panel with a landscape touchscreen, requiring - * the touchscreen driver to adjust the touch-coords to match the LCD. - * 2. Both use an TI LP8557 LED backlight controller. On the 1050 the LP8557's - * PWM input is connected to the PMIC's PWM output and everything works fine - * with the defaults programmed into the LP8557 by the BIOS. - * But on the 830 the LP8557's PWM input is connected to a PWM output coming - * from the LCD panel's controller. The Android code has a hack in the i915 - * driver to write the non-standard DSI reg 0x9f with the desired backlight - * level to set the duty-cycle of the LCD's PWM output. - * - * To avoid having to have a similar hack in the mainline kernel the LP8557 - * entry in lenovo_yoga_tab2_830_1050_i2c_clients instead just programs the - * LP8557 to directly set the level, ignoring the PWM input. This means that - * the LP8557 i2c_client should only be instantiated on the 830. + * mainboard, but the 830 uses a portrait LCD panel with a landscape touchscreen, + * requiring the touchscreen driver to adjust the touch-coords to match the LCD. */ -static int __init lenovo_yoga_tab2_830_1050_init_display(void) +static int __init lenovo_yoga_tab2_830_1050_init_touchscreen(void) { struct gpio_desc *gpiod; int ret; @@ -206,14 +193,10 @@ static int __init lenovo_yoga_tab2_830_1050_init_display(void) ret = gpiod_get_value_cansleep(gpiod); if (ret) { pr_info("detected Lenovo Yoga Tablet 2 1050F/L\n"); - lenovo_yoga_tab2_830_1050_info.i2c_client_count = - ARRAY_SIZE(lenovo_yoga_tab2_830_1050_i2c_clients) - 1; } else { pr_info("detected Lenovo Yoga Tablet 2 830F/L\n"); lenovo_yoga_tab2_830_1050_rmi_pdata.sensor_pdata.axis_align.swap_axes = true; lenovo_yoga_tab2_830_1050_rmi_pdata.sensor_pdata.axis_align.flip_y = true; - lenovo_yoga_tab2_830_1050_info.i2c_client_count = - ARRAY_SIZE(lenovo_yoga_tab2_830_1050_i2c_clients); } return 0; @@ -281,7 +264,7 @@ static int __init lenovo_yoga_tab2_830_1050_init(void) { int ret; - ret = lenovo_yoga_tab2_830_1050_init_display(); + ret = lenovo_yoga_tab2_830_1050_init_touchscreen(); if (ret) return ret; diff --git a/drivers/platform/x86/x86-android-tablets/x86-android-tablets.h b/drivers/platform/x86/x86-android-tablets/x86-android-tablets.h index cf8566b3fd9c..c2b490519324 100644 --- a/drivers/platform/x86/x86-android-tablets/x86-android-tablets.h +++ b/drivers/platform/x86/x86-android-tablets/x86-android-tablets.h @@ -95,8 +95,7 @@ extern const struct x86_dev_info asus_tf103c_info; extern const struct x86_dev_info chuwi_hi8_info; extern const struct x86_dev_info czc_p10t; extern const struct x86_dev_info lenovo_yogabook_x91_info; -/* Not const as this gets modified by its init callback */ -extern struct x86_dev_info lenovo_yoga_tab2_830_1050_info; +extern const struct x86_dev_info lenovo_yoga_tab2_830_1050_info; extern const struct x86_dev_info lenovo_yt3_info; extern const struct x86_dev_info medion_lifetab_s10346_info; extern const struct x86_dev_info nextbook_ares8_info; -- cgit v1.2.3 From ec5a4565fd6e15ccbe5710a326a2f283c0507389 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Sat, 1 Apr 2023 17:07:36 +0200 Subject: platform/x86: x86-android-tablets: Share lp855x_platform_data between different models Various Lenovo models use a TI LP8557 LED backlight controller and the necessary platform_data is the same for the different models. Currently there are 2 identical copies and the upcoming support for the Lenovo Yoga Book X90F/L would add a 3th identical copy. Move to sharing the lp855x_platform_data between different models to avoid this duplication. Signed-off-by: Hans de Goede Reviewed-by: Andy Shevchenko Link: https://lore.kernel.org/r/20230401150737.597417-2-hdegoede@redhat.com --- drivers/platform/x86/x86-android-tablets/lenovo.c | 37 ++++++++++------------- 1 file changed, 16 insertions(+), 21 deletions(-) (limited to 'drivers/platform') diff --git a/drivers/platform/x86/x86-android-tablets/lenovo.c b/drivers/platform/x86/x86-android-tablets/lenovo.c index d9d6dccc53c8..5d217cbbde30 100644 --- a/drivers/platform/x86/x86-android-tablets/lenovo.c +++ b/drivers/platform/x86/x86-android-tablets/lenovo.c @@ -24,6 +24,20 @@ #include "shared-psy-info.h" #include "x86-android-tablets.h" +/* + * Various Lenovo models use a TI LP8557 LED backlight controller with its PWM + * input connected to a PWM output coming from the LCD panel's controller. + * The Android kernels have a hack in the i915 driver to write a non-standard + * panel specific DSI register to set the duty-cycle of the LCD's PWM output. + * + * To avoid having to have a similar hack in the mainline kernel program the + * LP8557 to directly set the level and use the lp855x_bl driver for control. + */ +static struct lp855x_platform_data lenovo_lp8557_pdata = { + .device_control = 0x86, + .initial_brightness = 128, +}; + /* Lenovo Yoga Book X91F/L Windows tablet needs manual instantiation of the fg client */ static const struct x86_i2c_client_info lenovo_yogabook_x91_i2c_clients[] __initconst = { { @@ -72,11 +86,6 @@ static struct x86_gpio_button lenovo_yoga_tab2_830_1050_lid = { /* This gets filled by lenovo_yoga_tab2_830_1050_init() */ static struct rmi_device_platform_data lenovo_yoga_tab2_830_1050_rmi_pdata = { }; -static struct lp855x_platform_data lenovo_yoga_tab2_830_1050_lp8557_pdata = { - .device_control = 0x86, - .initial_brightness = 128, -}; - static const struct x86_i2c_client_info lenovo_yoga_tab2_830_1050_i2c_clients[] __initconst = { { /* bq24292i battery charger */ @@ -125,7 +134,7 @@ static const struct x86_i2c_client_info lenovo_yoga_tab2_830_1050_i2c_clients[] .type = "lp8557", .addr = 0x2c, .dev_name = "lp8557", - .platform_data = &lenovo_yoga_tab2_830_1050_lp8557_pdata, + .platform_data = &lenovo_lp8557_pdata, }, .adapter_path = "\\_SB_.I2C3", }, @@ -343,20 +352,6 @@ static const struct software_node lenovo_yt3_hideep_ts_node = { .properties = lenovo_yt3_hideep_ts_props, }; -/* - * The YT3 uses an TI LP8557 LED backlight controller, the LP8557's PWM input is - * connected to a PWM output coming from the LCD panel's controller. The Android - * kernel has a hack in the i915 driver to write the non-standard DSI reg 0x51 - * with the desired level to set the duty-cycle of the LCD's PWM output. - * - * To avoid having to have a similar hack in the mainline kernel program the - * LP8557 to directly set the level and use the lp855x_bl driver for control. - */ -static struct lp855x_platform_data lenovo_yt3_lp8557_pdata = { - .device_control = 0x86, - .initial_brightness = 128, -}; - static const struct x86_i2c_client_info lenovo_yt3_i2c_clients[] __initconst = { { /* bq27500 fuel-gauge for the flat lipo battery behind the screen */ @@ -414,7 +409,7 @@ static const struct x86_i2c_client_info lenovo_yt3_i2c_clients[] __initconst = { .type = "lp8557", .addr = 0x2c, .dev_name = "lp8557", - .platform_data = &lenovo_yt3_lp8557_pdata, + .platform_data = &lenovo_lp8557_pdata, }, .adapter_path = "\\_SB_.PCI0.I2C1", } -- cgit v1.2.3 From c69fec50ec2e3aeea46b13235b72d9d5a1bf095a Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Sat, 1 Apr 2023 17:07:37 +0200 Subject: platform/x86: x86-android-tablets: Add Lenovo Yoga Book X90F/L data The Lenovo Yoga Book X90F/L is a x86 ACPI tablet which ships with Android x86 as factory OS. Its DSDT contains a bunch of I2C devices which are not actually there, causing various resource conflicts. Enumeration of these is skipped through the acpi_quirk_skip_i2c_client_enumeration(). Add support for manually instantiating the I2C + other devices which are actually present on this tablet by adding the necessary device info to the x86-android-tablets module. Signed-off-by: Hans de Goede Reviewed-by: Andy Shevchenko Link: https://lore.kernel.org/r/20230401150737.597417-3-hdegoede@redhat.com --- drivers/platform/x86/x86-android-tablets/dmi.c | 9 ++ drivers/platform/x86/x86-android-tablets/lenovo.c | 122 +++++++++++++++++++++ .../x86/x86-android-tablets/x86-android-tablets.h | 1 + 3 files changed, 132 insertions(+) (limited to 'drivers/platform') diff --git a/drivers/platform/x86/x86-android-tablets/dmi.c b/drivers/platform/x86/x86-android-tablets/dmi.c index 6ea47af1b480..23e640b7003d 100644 --- a/drivers/platform/x86/x86-android-tablets/dmi.c +++ b/drivers/platform/x86/x86-android-tablets/dmi.c @@ -76,6 +76,15 @@ const struct dmi_system_id x86_android_tablet_ids[] __initconst = { }, .driver_data = (void *)&czc_p10t, }, + { + /* Lenovo Yoga Book X90F / X90L */ + .matches = { + DMI_EXACT_MATCH(DMI_SYS_VENDOR, "Intel Corporation"), + DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "CHERRYVIEW D1 PLATFORM"), + DMI_EXACT_MATCH(DMI_PRODUCT_VERSION, "YETI-11"), + }, + .driver_data = (void *)&lenovo_yogabook_x90_info, + }, { /* Lenovo Yoga Book X91F / X91L */ .matches = { diff --git a/drivers/platform/x86/x86-android-tablets/lenovo.c b/drivers/platform/x86/x86-android-tablets/lenovo.c index 5d217cbbde30..a571dcd11544 100644 --- a/drivers/platform/x86/x86-android-tablets/lenovo.c +++ b/drivers/platform/x86/x86-android-tablets/lenovo.c @@ -38,6 +38,128 @@ static struct lp855x_platform_data lenovo_lp8557_pdata = { .initial_brightness = 128, }; +/* Lenovo Yoga Book X90F / X90L's Android factory img has everything hardcoded */ + +/* + * The HiDeep IST940E touchscreen comes up in HID mode and could alternatively + * be used in I2C-HID mode (I2C-HID descriptor reg 0x0020) if i2c-hid-of.c is + * modified to use generic (non-OF) device-properties and thought to deal with + * the reset GPIO. "hideep,force-native-protocol" resets it to native mode. + */ +static const struct property_entry lenovo_yb1_x90_hideep_ts_props[] = { + PROPERTY_ENTRY_U32("touchscreen-size-x", 1200), + PROPERTY_ENTRY_U32("touchscreen-size-y", 1920), + PROPERTY_ENTRY_U32("touchscreen-max-pressure", 16384), + PROPERTY_ENTRY_BOOL("hideep,force-native-protocol"), + { } +}; + +static const struct software_node lenovo_yb1_x90_hideep_ts_node = { + .properties = lenovo_yb1_x90_hideep_ts_props, +}; + +static const struct x86_i2c_client_info lenovo_yb1_x90_i2c_clients[] __initconst = { + { + /* BQ27542 fuel-gauge */ + .board_info = { + .type = "bq27542", + .addr = 0x55, + .dev_name = "bq27542", + .swnode = &fg_bq25890_supply_node, + }, + .adapter_path = "\\_SB_.PCI0.I2C1", + }, { + /* Goodix Touchscreen in keyboard half */ + .board_info = { + .type = "GDIX1001:00", + .addr = 0x14, + .dev_name = "goodix_ts", + }, + .adapter_path = "\\_SB_.PCI0.I2C2", + .irq_data = { + .type = X86_ACPI_IRQ_TYPE_GPIOINT, + .chip = "INT33FF:01", + .index = 56, + .trigger = ACPI_EDGE_SENSITIVE, + .polarity = ACPI_ACTIVE_LOW, + }, + }, { + /* LP8557 Backlight controller */ + .board_info = { + .type = "lp8557", + .addr = 0x2c, + .dev_name = "lp8557", + .platform_data = &lenovo_lp8557_pdata, + }, + .adapter_path = "\\_SB_.PCI0.I2C4", + }, { + /* HiDeep IST940E Touchscreen in display half */ + .board_info = { + .type = "hideep_ts", + .addr = 0x6c, + .dev_name = "hideep_ts", + .swnode = &lenovo_yb1_x90_hideep_ts_node, + }, + .adapter_path = "\\_SB_.PCI0.I2C6", + .irq_data = { + .type = X86_ACPI_IRQ_TYPE_GPIOINT, + .chip = "INT33FF:03", + .index = 77, + .trigger = ACPI_LEVEL_SENSITIVE, + .polarity = ACPI_ACTIVE_LOW, + }, + }, +}; + +static struct gpiod_lookup_table lenovo_yb1_x90_goodix_gpios = { + .dev_id = "i2c-goodix_ts", + .table = { + GPIO_LOOKUP("INT33FF:01", 53, "reset", GPIO_ACTIVE_HIGH), + GPIO_LOOKUP("INT33FF:01", 56, "irq", GPIO_ACTIVE_HIGH), + { } + }, +}; + +static struct gpiod_lookup_table lenovo_yb1_x90_hideep_gpios = { + .dev_id = "i2c-hideep_ts", + .table = { + GPIO_LOOKUP("INT33FF:00", 7, "reset", GPIO_ACTIVE_LOW), + { } + }, +}; + +static struct gpiod_lookup_table * const lenovo_yb1_x90_gpios[] = { + &lenovo_yb1_x90_hideep_gpios, + &lenovo_yb1_x90_goodix_gpios, + NULL +}; + +static int __init lenovo_yb1_x90_init(void) +{ + /* Enable the regulators used by the touchscreens */ + + /* Vprog3B 3.0V used by the goodix touchscreen in the keyboard half */ + intel_soc_pmic_exec_mipi_pmic_seq_element(0x6e, 0x9b, 0x02, 0xff); + + /* Vprog4D 3.0V used by the HiDeep touchscreen in the display half */ + intel_soc_pmic_exec_mipi_pmic_seq_element(0x6e, 0x9f, 0x02, 0xff); + + /* Vprog5A 1.8V used by the HiDeep touchscreen in the display half */ + intel_soc_pmic_exec_mipi_pmic_seq_element(0x6e, 0xa0, 0x02, 0xff); + + /* Vprog5B 1.8V used by the goodix touchscreen in the keyboard half */ + intel_soc_pmic_exec_mipi_pmic_seq_element(0x6e, 0xa1, 0x02, 0xff); + + return 0; +} + +const struct x86_dev_info lenovo_yogabook_x90_info __initconst = { + .i2c_client_info = lenovo_yb1_x90_i2c_clients, + .i2c_client_count = ARRAY_SIZE(lenovo_yb1_x90_i2c_clients), + .gpiod_lookup_tables = lenovo_yb1_x90_gpios, + .init = lenovo_yb1_x90_init, +}; + /* Lenovo Yoga Book X91F/L Windows tablet needs manual instantiation of the fg client */ static const struct x86_i2c_client_info lenovo_yogabook_x91_i2c_clients[] __initconst = { { diff --git a/drivers/platform/x86/x86-android-tablets/x86-android-tablets.h b/drivers/platform/x86/x86-android-tablets/x86-android-tablets.h index c2b490519324..b6802d75dbdd 100644 --- a/drivers/platform/x86/x86-android-tablets/x86-android-tablets.h +++ b/drivers/platform/x86/x86-android-tablets/x86-android-tablets.h @@ -94,6 +94,7 @@ extern const struct x86_dev_info asus_me176c_info; extern const struct x86_dev_info asus_tf103c_info; extern const struct x86_dev_info chuwi_hi8_info; extern const struct x86_dev_info czc_p10t; +extern const struct x86_dev_info lenovo_yogabook_x90_info; extern const struct x86_dev_info lenovo_yogabook_x91_info; extern const struct x86_dev_info lenovo_yoga_tab2_830_1050_info; extern const struct x86_dev_info lenovo_yt3_info; -- cgit v1.2.3 From fb131c472df92b2885262e747906c0a4038c82e5 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Tue, 4 Apr 2023 13:19:55 +0200 Subject: platform/x86: apple-gmux: Fix iomem_base __iomem annotation Fix the __iomem annotation of the iomem_base pointers in the apple-gmux code. The __iomem should go before the *. This fixes a bunch of sparse warnings like this one: drivers/platform/x86/apple-gmux.c:224:48: sparse: expected void const [noderef] __iomem * got unsigned char [usertype] * Fixes: 0c18184de990 ("platform/x86: apple-gmux: support MMIO gmux on T2 Macs") Reported-by: kernel test robot Link: https://lore.kernel.org/oe-kbuild-all/202304040401.IMxt7Ubi-lkp@intel.com/ Suggested-by: Dan Carpenter Signed-off-by: Hans de Goede Reviewed-by: Orlando Chamberlain Link: https://lore.kernel.org/r/20230404111955.43266-1-hdegoede@redhat.com --- drivers/platform/x86/apple-gmux.c | 2 +- include/linux/apple-gmux.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/platform') diff --git a/drivers/platform/x86/apple-gmux.c b/drivers/platform/x86/apple-gmux.c index 4c311e1dedad..e02b4aea4f1e 100644 --- a/drivers/platform/x86/apple-gmux.c +++ b/drivers/platform/x86/apple-gmux.c @@ -57,7 +57,7 @@ struct apple_gmux_config; struct apple_gmux_data { - u8 *__iomem iomem_base; + u8 __iomem *iomem_base; unsigned long iostart; unsigned long iolen; const struct apple_gmux_config *config; diff --git a/include/linux/apple-gmux.h b/include/linux/apple-gmux.h index a7a7d430024b..206d97ffda79 100644 --- a/include/linux/apple-gmux.h +++ b/include/linux/apple-gmux.h @@ -66,7 +66,7 @@ static inline bool apple_gmux_is_indexed(unsigned long iostart) static inline bool apple_gmux_is_mmio(unsigned long iostart) { - u8 *__iomem iomem_base = ioremap(iostart, 16); + u8 __iomem *iomem_base = ioremap(iostart, 16); u8 val; if (!iomem_base) -- cgit v1.2.3 From 7bef7eea539dac6182e25e2e8d4a33063d458878 Mon Sep 17 00:00:00 2001 From: Khalil Blaiech Date: Wed, 5 Apr 2023 09:16:29 -0400 Subject: platform/mellanox: add firmware reset support Add a new sysfs to initiate firmware reset in isolation mode. Reviewed-by: David Thompson Signed-off-by: Khalil Blaiech Link: https://lore.kernel.org/r/10da04fa43e8acce5d4ec173e225c28b9e979e4b.1675790783.git.kblaiech@nvidia.com Signed-off-by: Hans de Goede --- drivers/platform/mellanox/mlxbf-bootctl.c | 19 +++++++++++++++++++ drivers/platform/mellanox/mlxbf-bootctl.h | 6 ++++++ 2 files changed, 25 insertions(+) (limited to 'drivers/platform') diff --git a/drivers/platform/mellanox/mlxbf-bootctl.c b/drivers/platform/mellanox/mlxbf-bootctl.c index 1c7a288b59a5..5e41c270195c 100644 --- a/drivers/platform/mellanox/mlxbf-bootctl.c +++ b/drivers/platform/mellanox/mlxbf-bootctl.c @@ -244,11 +244,29 @@ static ssize_t secure_boot_fuse_state_show(struct device *dev, return buf_len; } +static ssize_t fw_reset_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + unsigned long key; + int err; + + err = kstrtoul(buf, 16, &key); + if (err) + return err; + + if (mlxbf_bootctl_smc(MLXBF_BOOTCTL_FW_RESET, key) < 0) + return -EINVAL; + + return count; +} + static DEVICE_ATTR_RW(post_reset_wdog); static DEVICE_ATTR_RW(reset_action); static DEVICE_ATTR_RW(second_reset_action); static DEVICE_ATTR_RO(lifecycle_state); static DEVICE_ATTR_RO(secure_boot_fuse_state); +static DEVICE_ATTR_WO(fw_reset); static struct attribute *mlxbf_bootctl_attrs[] = { &dev_attr_post_reset_wdog.attr, @@ -256,6 +274,7 @@ static struct attribute *mlxbf_bootctl_attrs[] = { &dev_attr_second_reset_action.attr, &dev_attr_lifecycle_state.attr, &dev_attr_secure_boot_fuse_state.attr, + &dev_attr_fw_reset.attr, NULL }; diff --git a/drivers/platform/mellanox/mlxbf-bootctl.h b/drivers/platform/mellanox/mlxbf-bootctl.h index 148fdb43b435..b48243f60a59 100644 --- a/drivers/platform/mellanox/mlxbf-bootctl.h +++ b/drivers/platform/mellanox/mlxbf-bootctl.h @@ -75,6 +75,12 @@ #define MLXBF_BOOTCTL_GET_DIMM_INFO 0x82000008 +/* + * Initiate Firmware Reset via TYU. This might be invoked during the reset + * flow in isolation mode. + */ +#define MLXBF_BOOTCTL_FW_RESET 0x8200000D + /* SMC function IDs for SiP Service queries */ #define MLXBF_BOOTCTL_SIP_SVC_CALL_COUNT 0x8200ff00 #define MLXBF_BOOTCTL_SIP_SVC_UID 0x8200ff01 -- cgit v1.2.3 From 4f59630a5ed0a4e7d275bd7e5d253a8f5a425c5a Mon Sep 17 00:00:00 2001 From: Srinivas Pandruvada Date: Thu, 30 Mar 2023 07:59:39 -0700 Subject: platform/x86: intel-uncore-freq: Add client processors Make Intel uncore frequency driver support to client processor starting from Alder Lake. Signed-off-by: Srinivas Pandruvada Link: https://lore.kernel.org/r/20230330145939.1022261-1-srinivas.pandruvada@linux.intel.com Reviewed-by: Hans de Goede Signed-off-by: Hans de Goede --- drivers/platform/x86/intel/uncore-frequency/uncore-frequency.c | 7 +++++++ 1 file changed, 7 insertions(+) (limited to 'drivers/platform') diff --git a/drivers/platform/x86/intel/uncore-frequency/uncore-frequency.c b/drivers/platform/x86/intel/uncore-frequency/uncore-frequency.c index 00ac7e381441..32e2515ee366 100644 --- a/drivers/platform/x86/intel/uncore-frequency/uncore-frequency.c +++ b/drivers/platform/x86/intel/uncore-frequency/uncore-frequency.c @@ -204,6 +204,13 @@ static const struct x86_cpu_id intel_uncore_cpu_ids[] = { X86_MATCH_INTEL_FAM6_MODEL(ICELAKE_D, NULL), X86_MATCH_INTEL_FAM6_MODEL(SAPPHIRERAPIDS_X, NULL), X86_MATCH_INTEL_FAM6_MODEL(EMERALDRAPIDS_X, NULL), + X86_MATCH_INTEL_FAM6_MODEL(ALDERLAKE, NULL), + X86_MATCH_INTEL_FAM6_MODEL(ALDERLAKE_L, NULL), + X86_MATCH_INTEL_FAM6_MODEL(RAPTORLAKE, NULL), + X86_MATCH_INTEL_FAM6_MODEL(RAPTORLAKE_P, NULL), + X86_MATCH_INTEL_FAM6_MODEL(RAPTORLAKE_S, NULL), + X86_MATCH_INTEL_FAM6_MODEL(METEORLAKE, NULL), + X86_MATCH_INTEL_FAM6_MODEL(METEORLAKE_L, NULL), {} }; MODULE_DEVICE_TABLE(x86cpu, intel_uncore_cpu_ids); -- cgit v1.2.3 From aec8298c093f052fc8a86f9411b69b23953b0edb Mon Sep 17 00:00:00 2001 From: Shyam Sundar S K Date: Thu, 6 Apr 2023 22:18:07 +0530 Subject: platform/x86/amd/pmf: Move out of BIOS SMN pair for driver probe The current SMN index used for the driver probe seems to be meant for the BIOS pair and there are potential concurrency problems that can occur with an inopportune SMI. It is been advised to use SMN_INDEX_0 instead of SMN_INDEX_2, which is what amd_nb.c provides and this function has protections to ensure that only one caller can use it at a time. Fixes: da5ce22df5fe ("platform/x86/amd/pmf: Add support for PMF core layer") Co-developed-by: Patil Rajesh Reddy Signed-off-by: Patil Rajesh Reddy Signed-off-by: Shyam Sundar S K Link: https://lore.kernel.org/r/20230406164807.50969-4-Shyam-sundar.S-k@amd.com Reviewed-by: Hans de Goede Signed-off-by: Hans de Goede --- drivers/platform/x86/amd/pmf/Kconfig | 1 + drivers/platform/x86/amd/pmf/core.c | 22 +++++----------------- 2 files changed, 6 insertions(+), 17 deletions(-) (limited to 'drivers/platform') diff --git a/drivers/platform/x86/amd/pmf/Kconfig b/drivers/platform/x86/amd/pmf/Kconfig index 6d89528c3177..d87986adf91e 100644 --- a/drivers/platform/x86/amd/pmf/Kconfig +++ b/drivers/platform/x86/amd/pmf/Kconfig @@ -7,6 +7,7 @@ config AMD_PMF tristate "AMD Platform Management Framework" depends on ACPI && PCI depends on POWER_SUPPLY + depends on AMD_NB select ACPI_PLATFORM_PROFILE help This driver provides support for the AMD Platform Management Framework. diff --git a/drivers/platform/x86/amd/pmf/core.c b/drivers/platform/x86/amd/pmf/core.c index 800ca2c998d3..d5bb775dadcf 100644 --- a/drivers/platform/x86/amd/pmf/core.c +++ b/drivers/platform/x86/amd/pmf/core.c @@ -8,6 +8,7 @@ * Author: Shyam Sundar S K */ +#include #include #include #include @@ -22,8 +23,6 @@ #define AMD_PMF_REGISTER_ARGUMENT 0xA58 /* Base address of SMU for mapping physical address to virtual address */ -#define AMD_PMF_SMU_INDEX_ADDRESS 0xB8 -#define AMD_PMF_SMU_INDEX_DATA 0xBC #define AMD_PMF_MAPPING_SIZE 0x01000 #define AMD_PMF_BASE_ADDR_OFFSET 0x10000 #define AMD_PMF_BASE_ADDR_LO 0x13B102E8 @@ -348,30 +347,19 @@ static int amd_pmf_probe(struct platform_device *pdev) } dev->cpu_id = rdev->device; - err = pci_write_config_dword(rdev, AMD_PMF_SMU_INDEX_ADDRESS, AMD_PMF_BASE_ADDR_LO); - if (err) { - dev_err(dev->dev, "error writing to 0x%x\n", AMD_PMF_SMU_INDEX_ADDRESS); - pci_dev_put(rdev); - return pcibios_err_to_errno(err); - } - err = pci_read_config_dword(rdev, AMD_PMF_SMU_INDEX_DATA, &val); + err = amd_smn_read(0, AMD_PMF_BASE_ADDR_LO, &val); if (err) { + dev_err(dev->dev, "error in reading from 0x%x\n", AMD_PMF_BASE_ADDR_LO); pci_dev_put(rdev); return pcibios_err_to_errno(err); } base_addr_lo = val & AMD_PMF_BASE_ADDR_HI_MASK; - err = pci_write_config_dword(rdev, AMD_PMF_SMU_INDEX_ADDRESS, AMD_PMF_BASE_ADDR_HI); - if (err) { - dev_err(dev->dev, "error writing to 0x%x\n", AMD_PMF_SMU_INDEX_ADDRESS); - pci_dev_put(rdev); - return pcibios_err_to_errno(err); - } - - err = pci_read_config_dword(rdev, AMD_PMF_SMU_INDEX_DATA, &val); + err = amd_smn_read(0, AMD_PMF_BASE_ADDR_HI, &val); if (err) { + dev_err(dev->dev, "error in reading from 0x%x\n", AMD_PMF_BASE_ADDR_HI); pci_dev_put(rdev); return pcibios_err_to_errno(err); } -- cgit v1.2.3 From b845772677ea19b8e4c032bc07393ef32de4ee39 Mon Sep 17 00:00:00 2001 From: Mario Limonciello Date: Mon, 10 Apr 2023 00:23:41 +0530 Subject: platform/x86/amd: pmc: Don't try to read SMU version on Picasso Picasso doesn't support the command in the uPEP mailbox to try to read the SMU version. Link: https://gitlab.freedesktop.org/drm/amd/-/issues/2449 Fixes: f6045de1f532 ("platform/x86: amd-pmc: Export Idlemask values based on the APU") Signed-off-by: Mario Limonciello Link: https://lore.kernel.org/r/20230409185348.556161-2-Shyam-sundar.S-k@amd.com Reviewed-by: Hans de Goede Signed-off-by: Hans de Goede --- drivers/platform/x86/amd/pmc.c | 3 +++ 1 file changed, 3 insertions(+) (limited to 'drivers/platform') diff --git a/drivers/platform/x86/amd/pmc.c b/drivers/platform/x86/amd/pmc.c index 2761e9b76e95..2758c0e63a1c 100644 --- a/drivers/platform/x86/amd/pmc.c +++ b/drivers/platform/x86/amd/pmc.c @@ -403,6 +403,9 @@ static int amd_pmc_get_smu_version(struct amd_pmc_dev *dev) int rc; u32 val; + if (dev->cpu_id == AMD_CPU_ID_PCO) + return -ENODEV; + rc = amd_pmc_send_cmd(dev, 0, &val, SMU_MSG_GETSMUVERSION, 1); if (rc) return rc; -- cgit v1.2.3 From 5ec9ee0d464750d72972d5685edf675824e259a1 Mon Sep 17 00:00:00 2001 From: Mario Limonciello Date: Mon, 10 Apr 2023 00:23:42 +0530 Subject: platform/x86/amd: pmc: Hide SMU version and program attributes for Picasso As the command to get version isn't supported on Picasso, we shouldn't be exposing this into sysfs either. Link: https://gitlab.freedesktop.org/drm/amd/-/issues/2449 Fixes: 7f1ea75d499a ("platform/x86/amd: pmc: Add sysfs files for SMU") Signed-off-by: Mario Limonciello Link: https://lore.kernel.org/r/20230409185348.556161-3-Shyam-sundar.S-k@amd.com Reviewed-by: Hans de Goede Signed-off-by: Hans de Goede --- drivers/platform/x86/amd/pmc.c | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) (limited to 'drivers/platform') diff --git a/drivers/platform/x86/amd/pmc.c b/drivers/platform/x86/amd/pmc.c index 2758c0e63a1c..08fda2f96463 100644 --- a/drivers/platform/x86/amd/pmc.c +++ b/drivers/platform/x86/amd/pmc.c @@ -452,12 +452,31 @@ static ssize_t smu_program_show(struct device *d, struct device_attribute *attr, static DEVICE_ATTR_RO(smu_fw_version); static DEVICE_ATTR_RO(smu_program); +static umode_t pmc_attr_is_visible(struct kobject *kobj, struct attribute *attr, int idx) +{ + struct device *dev = kobj_to_dev(kobj); + struct amd_pmc_dev *pdev = dev_get_drvdata(dev); + + if (pdev->cpu_id == AMD_CPU_ID_PCO) + return 0; + return 0444; +} + static struct attribute *pmc_attrs[] = { &dev_attr_smu_fw_version.attr, &dev_attr_smu_program.attr, NULL, }; -ATTRIBUTE_GROUPS(pmc); + +static struct attribute_group pmc_attr_group = { + .attrs = pmc_attrs, + .is_visible = pmc_attr_is_visible, +}; + +static const struct attribute_group *pmc_groups[] = { + &pmc_attr_group, + NULL, +}; static int smu_fw_info_show(struct seq_file *s, void *unused) { -- cgit v1.2.3 From 7abc3618b65304d409d9489d77e4a8f047842fb7 Mon Sep 17 00:00:00 2001 From: Mario Limonciello Date: Mon, 10 Apr 2023 00:23:43 +0530 Subject: platform/x86/amd: pmc: Don't dump data after resume from s0i3 on picasso This command isn't supported on Picasso, so guard against running it to avoid errors like `SMU cmd unknown. err: 0xfe` in the logs. Link: https://gitlab.freedesktop.org/drm/amd/-/issues/2449 Fixes: 766205674962 ("platform/x86: amd-pmc: Add support for logging SMU metrics") Signed-off-by: Mario Limonciello Link: https://lore.kernel.org/r/20230409185348.556161-4-Shyam-sundar.S-k@amd.com Reviewed-by: Hans de Goede Signed-off-by: Hans de Goede --- drivers/platform/x86/amd/pmc.c | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) (limited to 'drivers/platform') diff --git a/drivers/platform/x86/amd/pmc.c b/drivers/platform/x86/amd/pmc.c index 08fda2f96463..4ae622f36e20 100644 --- a/drivers/platform/x86/amd/pmc.c +++ b/drivers/platform/x86/amd/pmc.c @@ -834,6 +834,14 @@ static void amd_pmc_s2idle_check(void) dev_err(pdev->dev, "error writing to STB: %d\n", rc); } +static int amd_pmc_dump_data(struct amd_pmc_dev *pdev) +{ + if (pdev->cpu_id == AMD_CPU_ID_PCO) + return -ENODEV; + + return amd_pmc_send_cmd(pdev, 0, NULL, SMU_MSG_LOG_DUMP_DATA, 0); +} + static void amd_pmc_s2idle_restore(void) { struct amd_pmc_dev *pdev = &pmc; @@ -846,7 +854,7 @@ static void amd_pmc_s2idle_restore(void) dev_err(pdev->dev, "resume failed: %d\n", rc); /* Let SMU know that we are looking for stats */ - amd_pmc_send_cmd(pdev, 0, NULL, SMU_MSG_LOG_DUMP_DATA, 0); + amd_pmc_dump_data(pdev); rc = amd_pmc_write_stb(pdev, AMD_PMC_STB_S2IDLE_RESTORE); if (rc) -- cgit v1.2.3 From 9217bd1d7699f34a01b26ba14ff38c1714ce1185 Mon Sep 17 00:00:00 2001 From: Mario Limonciello Date: Mon, 10 Apr 2023 00:23:44 +0530 Subject: platform/x86/amd: pmc: Move idlemask check into `amd_pmc_idlemask_read` The version check requirement for idle mask support actually only applies to RN/CZN/BRC platforms. So far no issues have happened because the PMFW version string is bigger on other supported systems. This can be reset for any new platform so move the check to only RN/CZN/BRC case. Fixes: f6045de1f532 ("platform/x86: amd-pmc: Export Idlemask values based on the APU") Signed-off-by: Mario Limonciello Link: https://lore.kernel.org/r/20230409185348.556161-5-Shyam-sundar.S-k@amd.com Reviewed-by: Hans de Goede Signed-off-by: Hans de Goede --- drivers/platform/x86/amd/pmc.c | 74 +++++++++++++++++++----------------------- 1 file changed, 33 insertions(+), 41 deletions(-) (limited to 'drivers/platform') diff --git a/drivers/platform/x86/amd/pmc.c b/drivers/platform/x86/amd/pmc.c index 4ae622f36e20..8d03edd4d67c 100644 --- a/drivers/platform/x86/amd/pmc.c +++ b/drivers/platform/x86/amd/pmc.c @@ -342,33 +342,6 @@ static int amd_pmc_setup_smu_logging(struct amd_pmc_dev *dev) return 0; } -static int amd_pmc_idlemask_read(struct amd_pmc_dev *pdev, struct device *dev, - struct seq_file *s) -{ - u32 val; - - switch (pdev->cpu_id) { - case AMD_CPU_ID_CZN: - val = amd_pmc_reg_read(pdev, AMD_PMC_SCRATCH_REG_CZN); - break; - case AMD_CPU_ID_YC: - case AMD_CPU_ID_CB: - case AMD_CPU_ID_PS: - val = amd_pmc_reg_read(pdev, AMD_PMC_SCRATCH_REG_YC); - break; - default: - return -EINVAL; - } - - if (dev) - dev_dbg(pdev->dev, "SMU idlemask s0i3: 0x%x\n", val); - - if (s) - seq_printf(s, "SMU idlemask : 0x%x\n", val); - - return 0; -} - static int get_metrics_table(struct amd_pmc_dev *pdev, struct smu_metrics *table) { if (!pdev->smu_virt_addr) { @@ -543,28 +516,47 @@ static int s0ix_stats_show(struct seq_file *s, void *unused) } DEFINE_SHOW_ATTRIBUTE(s0ix_stats); -static int amd_pmc_idlemask_show(struct seq_file *s, void *unused) +static int amd_pmc_idlemask_read(struct amd_pmc_dev *pdev, struct device *dev, + struct seq_file *s) { - struct amd_pmc_dev *dev = s->private; + u32 val; int rc; - /* we haven't yet read SMU version */ - if (!dev->major) { - rc = amd_pmc_get_smu_version(dev); - if (rc) - return rc; + switch (pdev->cpu_id) { + case AMD_CPU_ID_CZN: + /* we haven't yet read SMU version */ + if (!pdev->major) { + rc = amd_pmc_get_smu_version(pdev); + if (rc) + return rc; + } + if (pdev->major > 56 || (pdev->major >= 55 && pdev->minor >= 37)) + val = amd_pmc_reg_read(pdev, AMD_PMC_SCRATCH_REG_CZN); + else + return -EINVAL; + break; + case AMD_CPU_ID_YC: + case AMD_CPU_ID_CB: + case AMD_CPU_ID_PS: + val = amd_pmc_reg_read(pdev, AMD_PMC_SCRATCH_REG_YC); + break; + default: + return -EINVAL; } - if (dev->major > 56 || (dev->major >= 55 && dev->minor >= 37)) { - rc = amd_pmc_idlemask_read(dev, NULL, s); - if (rc) - return rc; - } else { - seq_puts(s, "Unsupported SMU version for Idlemask\n"); - } + if (dev) + dev_dbg(pdev->dev, "SMU idlemask s0i3: 0x%x\n", val); + + if (s) + seq_printf(s, "SMU idlemask : 0x%x\n", val); return 0; } + +static int amd_pmc_idlemask_show(struct seq_file *s, void *unused) +{ + return amd_pmc_idlemask_read(s->private, NULL, s); +} DEFINE_SHOW_ATTRIBUTE(amd_pmc_idlemask); static void amd_pmc_dbgfs_unregister(struct amd_pmc_dev *dev) -- cgit v1.2.3 From 310e782a99c7f16fb533a45d8f9c16defefa5aab Mon Sep 17 00:00:00 2001 From: Shyam Sundar S K Date: Mon, 10 Apr 2023 00:23:45 +0530 Subject: platform/x86/amd: pmc: Utilize SMN index 0 for driver probe The current SMN index used for the driver probe seems to be meant for the BIOS pair and there are potential concurrency problems that can occur with an inopportune SMI. It is been advised to use SMN_INDEX_0 instead of SMN_INDEX_2, which is what amd_nb.c provides and this function has protections to ensure that only one caller can use it at a time. Fixes: 156ec4731cb2 ("platform/x86: amd-pmc: Add AMD platform support for S2Idle") Co-developed-by: Sanket Goswami Signed-off-by: Sanket Goswami Signed-off-by: Shyam Sundar S K Link: https://lore.kernel.org/r/20230409185348.556161-6-Shyam-sundar.S-k@amd.com Reviewed-by: Hans de Goede Signed-off-by: Hans de Goede --- drivers/platform/x86/amd/Kconfig | 2 +- drivers/platform/x86/amd/pmc.c | 23 +++++------------------ 2 files changed, 6 insertions(+), 19 deletions(-) (limited to 'drivers/platform') diff --git a/drivers/platform/x86/amd/Kconfig b/drivers/platform/x86/amd/Kconfig index 2ce8cb2170df..d9685aef0887 100644 --- a/drivers/platform/x86/amd/Kconfig +++ b/drivers/platform/x86/amd/Kconfig @@ -7,7 +7,7 @@ source "drivers/platform/x86/amd/pmf/Kconfig" config AMD_PMC tristate "AMD SoC PMC driver" - depends on ACPI && PCI && RTC_CLASS + depends on ACPI && PCI && RTC_CLASS && AMD_NB select SERIO help The driver provides support for AMD Power Management Controller diff --git a/drivers/platform/x86/amd/pmc.c b/drivers/platform/x86/amd/pmc.c index 8d03edd4d67c..256bd10f277e 100644 --- a/drivers/platform/x86/amd/pmc.c +++ b/drivers/platform/x86/amd/pmc.c @@ -10,6 +10,7 @@ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt +#include #include #include #include @@ -56,8 +57,6 @@ #define S2D_TELEMETRY_DRAMBYTES_MAX 0x1000000 /* Base address of SMU for mapping physical address to virtual address */ -#define AMD_PMC_SMU_INDEX_ADDRESS 0xB8 -#define AMD_PMC_SMU_INDEX_DATA 0xBC #define AMD_PMC_MAPPING_SIZE 0x01000 #define AMD_PMC_BASE_ADDR_OFFSET 0x10000 #define AMD_PMC_BASE_ADDR_LO 0x13B102E8 @@ -983,30 +982,18 @@ static int amd_pmc_probe(struct platform_device *pdev) dev->cpu_id = rdev->device; dev->rdev = rdev; - err = pci_write_config_dword(rdev, AMD_PMC_SMU_INDEX_ADDRESS, AMD_PMC_BASE_ADDR_LO); - if (err) { - dev_err(dev->dev, "error writing to 0x%x\n", AMD_PMC_SMU_INDEX_ADDRESS); - err = pcibios_err_to_errno(err); - goto err_pci_dev_put; - } - - err = pci_read_config_dword(rdev, AMD_PMC_SMU_INDEX_DATA, &val); + err = amd_smn_read(0, AMD_PMC_BASE_ADDR_LO, &val); if (err) { + dev_err(dev->dev, "error reading 0x%x\n", AMD_PMC_BASE_ADDR_LO); err = pcibios_err_to_errno(err); goto err_pci_dev_put; } base_addr_lo = val & AMD_PMC_BASE_ADDR_HI_MASK; - err = pci_write_config_dword(rdev, AMD_PMC_SMU_INDEX_ADDRESS, AMD_PMC_BASE_ADDR_HI); - if (err) { - dev_err(dev->dev, "error writing to 0x%x\n", AMD_PMC_SMU_INDEX_ADDRESS); - err = pcibios_err_to_errno(err); - goto err_pci_dev_put; - } - - err = pci_read_config_dword(rdev, AMD_PMC_SMU_INDEX_DATA, &val); + err = amd_smn_read(0, AMD_PMC_BASE_ADDR_HI, &val); if (err) { + dev_err(dev->dev, "error reading 0x%x\n", AMD_PMC_BASE_ADDR_HI); err = pcibios_err_to_errno(err); goto err_pci_dev_put; } -- cgit v1.2.3 From 8d99129eef8f42377b41c1bacee9f8ce806e9f44 Mon Sep 17 00:00:00 2001 From: Shyam Sundar S K Date: Mon, 10 Apr 2023 00:23:46 +0530 Subject: platform/x86/amd: pmc: Move out of BIOS SMN pair for STB init The current SMN index used for the driver probe seems to be meant for the BIOS pair and there are potential concurrency problems that can occur with an inopportune SMI. It is been advised to use SMN_INDEX_0 instead of SMN_INDEX_6, which is what amd_nb.c provides and this function has protections to ensure that only one caller can use it at a time. Fixes: 426c0ff27b83 ("platform/x86: amd-pmc: Add support for AMD Smart Trace Buffer") Co-developed-by: Sanket Goswami Signed-off-by: Sanket Goswami Signed-off-by: Shyam Sundar S K Link: https://lore.kernel.org/r/20230409185348.556161-7-Shyam-sundar.S-k@amd.com Reviewed-by: Hans de Goede Signed-off-by: Hans de Goede --- drivers/platform/x86/amd/pmc.c | 26 ++++---------------------- 1 file changed, 4 insertions(+), 22 deletions(-) (limited to 'drivers/platform') diff --git a/drivers/platform/x86/amd/pmc.c b/drivers/platform/x86/amd/pmc.c index 256bd10f277e..b921d37ed706 100644 --- a/drivers/platform/x86/amd/pmc.c +++ b/drivers/platform/x86/amd/pmc.c @@ -38,8 +38,6 @@ #define AMD_PMC_SCRATCH_REG_YC 0xD14 /* STB Registers */ -#define AMD_PMC_STB_INDEX_ADDRESS 0xF8 -#define AMD_PMC_STB_INDEX_DATA 0xFC #define AMD_PMC_STB_PMI_0 0x03E30600 #define AMD_PMC_STB_S2IDLE_PREPARE 0xC6000001 #define AMD_PMC_STB_S2IDLE_RESTORE 0xC6000002 @@ -923,17 +921,9 @@ static int amd_pmc_write_stb(struct amd_pmc_dev *dev, u32 data) { int err; - err = pci_write_config_dword(dev->rdev, AMD_PMC_STB_INDEX_ADDRESS, AMD_PMC_STB_PMI_0); + err = amd_smn_write(0, AMD_PMC_STB_PMI_0, data); if (err) { - dev_err(dev->dev, "failed to write addr in stb: 0x%X\n", - AMD_PMC_STB_INDEX_ADDRESS); - return pcibios_err_to_errno(err); - } - - err = pci_write_config_dword(dev->rdev, AMD_PMC_STB_INDEX_DATA, data); - if (err) { - dev_err(dev->dev, "failed to write data in stb: 0x%X\n", - AMD_PMC_STB_INDEX_DATA); + dev_err(dev->dev, "failed to write data in stb: 0x%X\n", AMD_PMC_STB_PMI_0); return pcibios_err_to_errno(err); } @@ -944,18 +934,10 @@ static int amd_pmc_read_stb(struct amd_pmc_dev *dev, u32 *buf) { int i, err; - err = pci_write_config_dword(dev->rdev, AMD_PMC_STB_INDEX_ADDRESS, AMD_PMC_STB_PMI_0); - if (err) { - dev_err(dev->dev, "error writing addr to stb: 0x%X\n", - AMD_PMC_STB_INDEX_ADDRESS); - return pcibios_err_to_errno(err); - } - for (i = 0; i < FIFO_SIZE; i++) { - err = pci_read_config_dword(dev->rdev, AMD_PMC_STB_INDEX_DATA, buf++); + err = amd_smn_read(0, AMD_PMC_STB_PMI_0, buf++); if (err) { - dev_err(dev->dev, "error reading data from stb: 0x%X\n", - AMD_PMC_STB_INDEX_DATA); + dev_err(dev->dev, "error reading data from stb: 0x%X\n", AMD_PMC_STB_PMI_0); return pcibios_err_to_errno(err); } } -- cgit v1.2.3 From 336ba968d3e30038c13b03a71f5a672db6c9e303 Mon Sep 17 00:00:00 2001 From: "David E. Box" Date: Sun, 9 Apr 2023 12:25:35 -0700 Subject: platform/x86/intel/pmc/mtl: Put GNA/IPU/VPU devices in D3 On Meteor Lake, the GNA, IPU, and VPU devices are booted in D0 power state and will block the SoC from going into the deepest Package C-state if a driver is not present. Put each device in D3hot if no driver is found. Signed-off-by: David E. Box Reviewed-by: Hans de Goede Link: https://lore.kernel.org/r/20230409192535.914540-1-david.e.box@linux.intel.com Signed-off-by: Hans de Goede --- drivers/platform/x86/intel/pmc/mtl.c | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) (limited to 'drivers/platform') diff --git a/drivers/platform/x86/intel/pmc/mtl.c b/drivers/platform/x86/intel/pmc/mtl.c index eeb3bd8c2502..e8cc156412ce 100644 --- a/drivers/platform/x86/intel/pmc/mtl.c +++ b/drivers/platform/x86/intel/pmc/mtl.c @@ -8,6 +8,7 @@ * */ +#include #include "core.h" const struct pmc_reg_map mtl_reg_map = { @@ -45,8 +46,38 @@ void mtl_core_configure(struct pmc_dev *pmcdev) pmc_core_send_ltr_ignore(pmcdev, 3); } +#define MTL_GNA_PCI_DEV 0x7e4c +#define MTL_IPU_PCI_DEV 0x7d19 +#define MTL_VPU_PCI_DEV 0x7d1d +static void mtl_set_device_d3(unsigned int device) +{ + struct pci_dev *pcidev; + + pcidev = pci_get_device(PCI_VENDOR_ID_INTEL, device, NULL); + if (pcidev) { + if (!device_trylock(&pcidev->dev)) { + pci_dev_put(pcidev); + return; + } + if (!pcidev->dev.driver) { + dev_info(&pcidev->dev, "Setting to D3hot\n"); + pci_set_power_state(pcidev, PCI_D3hot); + } + device_unlock(&pcidev->dev); + pci_dev_put(pcidev); + } +} + void mtl_core_init(struct pmc_dev *pmcdev) { pmcdev->map = &mtl_reg_map; pmcdev->core_configure = mtl_core_configure; + + /* + * Set power state of select devices that do not have drivers to D3 + * so that they do not block Package C entry. + */ + mtl_set_device_d3(MTL_GNA_PCI_DEV); + mtl_set_device_d3(MTL_IPU_PCI_DEV); + mtl_set_device_d3(MTL_VPU_PCI_DEV); } -- cgit v1.2.3 From 5b309e80f457ebb41770e0122766825a78cfc11b Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Mon, 10 Apr 2023 21:35:12 +0200 Subject: platform/x86: amd: pmc: Remove __maybe_unused from amd_pmc_suspend_handler() Now that the pmc code has switched to DEFINE_SIMPLE_DEV_PM_OPS() the __maybe_unused is no longer necessary, drop it. Signed-off-by: Hans de Goede Reviewed-by: Mario Limonciello Link: https://lore.kernel.org/r/20230410193512.64232-1-hdegoede@redhat.com --- drivers/platform/x86/amd/pmc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/platform') diff --git a/drivers/platform/x86/amd/pmc.c b/drivers/platform/x86/amd/pmc.c index b921d37ed706..877b629e5cae 100644 --- a/drivers/platform/x86/amd/pmc.c +++ b/drivers/platform/x86/amd/pmc.c @@ -859,7 +859,7 @@ static struct acpi_s2idle_dev_ops amd_pmc_s2idle_dev_ops = { .restore = amd_pmc_s2idle_restore, }; -static int __maybe_unused amd_pmc_suspend_handler(struct device *dev) +static int amd_pmc_suspend_handler(struct device *dev) { struct amd_pmc_dev *pdev = dev_get_drvdata(dev); -- cgit v1.2.3 From 82c3a0b7f0cb97445eeb8b91b0a37d95f8c2c2a9 Mon Sep 17 00:00:00 2001 From: Liming Sun Date: Tue, 11 Apr 2023 11:39:42 -0400 Subject: mlxbf-bootctl: Add sysfs file for BlueField boot fifo This commit adds sysfs file for BlueField boot fifo. The boot fifo is usually used to push boot stream via USB or PCIe. Once OS is up, it can be reused by applications to read data or configuration from external host. Signed-off-by: Liming Sun Reviewed-by: David Thompson Link: https://lore.kernel.org/r/52b0b00dacbc4aad3169dd3667d79c85e334783b.1680657571.git.limings@nvidia.com Reviewed-by: Hans de Goede Signed-off-by: Hans de Goede --- .../ABI/testing/sysfs-platform-mellanox-bootctl | 7 +++ drivers/platform/mellanox/mlxbf-bootctl.c | 68 ++++++++++++++++++++++ 2 files changed, 75 insertions(+) (limited to 'drivers/platform') diff --git a/Documentation/ABI/testing/sysfs-platform-mellanox-bootctl b/Documentation/ABI/testing/sysfs-platform-mellanox-bootctl index e79ca22e2f45..9b99a81babb1 100644 --- a/Documentation/ABI/testing/sysfs-platform-mellanox-bootctl +++ b/Documentation/ABI/testing/sysfs-platform-mellanox-bootctl @@ -68,3 +68,10 @@ Description: Wasted burnt and invalid Invalid not burnt but marked as valid (error state). ======= =============================================== + +What: /sys/bus/platform/devices/MLNXBF04:00/bootfifo +Date: Apr 2023 +KernelVersion: 6.4 +Contact: "Liming Sun " +Description: + The file used to access the BlueField boot fifo. diff --git a/drivers/platform/mellanox/mlxbf-bootctl.c b/drivers/platform/mellanox/mlxbf-bootctl.c index 5e41c270195c..1bad1d278672 100644 --- a/drivers/platform/mellanox/mlxbf-bootctl.c +++ b/drivers/platform/mellanox/mlxbf-bootctl.c @@ -10,6 +10,7 @@ #include #include +#include #include #include @@ -44,6 +45,10 @@ static const char * const mlxbf_bootctl_lifecycle_states[] = { [3] = "RMA", }; +/* Mapped pointer for RSH_BOOT_FIFO_DATA and RSH_BOOT_FIFO_COUNT register. */ +static void __iomem *mlxbf_rsh_boot_data; +static void __iomem *mlxbf_rsh_boot_cnt; + /* ARM SMC call which is atomic and no need for lock. */ static int mlxbf_bootctl_smc(unsigned int smc_op, int smc_arg) { @@ -287,6 +292,45 @@ static const struct acpi_device_id mlxbf_bootctl_acpi_ids[] = { MODULE_DEVICE_TABLE(acpi, mlxbf_bootctl_acpi_ids); +static ssize_t mlxbf_bootctl_bootfifo_read(struct file *filp, + struct kobject *kobj, + struct bin_attribute *bin_attr, + char *buf, loff_t pos, + size_t count) +{ + unsigned long timeout = msecs_to_jiffies(500); + unsigned long expire = jiffies + timeout; + u64 data, cnt = 0; + char *p = buf; + + while (count >= sizeof(data)) { + /* Give up reading if no more data within 500ms. */ + if (!cnt) { + cnt = readq(mlxbf_rsh_boot_cnt); + if (!cnt) { + if (time_after(jiffies, expire)) + break; + usleep_range(10, 50); + continue; + } + } + + data = readq(mlxbf_rsh_boot_data); + memcpy(p, &data, sizeof(data)); + count -= sizeof(data); + p += sizeof(data); + cnt--; + expire = jiffies + timeout; + } + + return p - buf; +} + +static struct bin_attribute mlxbf_bootctl_bootfifo_sysfs_attr = { + .attr = { .name = "bootfifo", .mode = 0400 }, + .read = mlxbf_bootctl_bootfifo_read, +}; + static bool mlxbf_bootctl_guid_match(const guid_t *guid, const struct arm_smccc_res *res) { @@ -304,6 +348,16 @@ static int mlxbf_bootctl_probe(struct platform_device *pdev) guid_t guid; int ret; + /* Get the resource of the bootfifo data register. */ + mlxbf_rsh_boot_data = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(mlxbf_rsh_boot_data)) + return PTR_ERR(mlxbf_rsh_boot_data); + + /* Get the resource of the bootfifo counter register. */ + mlxbf_rsh_boot_cnt = devm_platform_ioremap_resource(pdev, 1); + if (IS_ERR(mlxbf_rsh_boot_cnt)) + return PTR_ERR(mlxbf_rsh_boot_cnt); + /* Ensure we have the UUID we expect for this service. */ arm_smccc_smc(MLXBF_BOOTCTL_SIP_SVC_UID, 0, 0, 0, 0, 0, 0, 0, &res); guid_parse(mlxbf_bootctl_svc_uuid_str, &guid); @@ -321,11 +375,25 @@ static int mlxbf_bootctl_probe(struct platform_device *pdev) if (ret < 0) dev_warn(&pdev->dev, "Unable to reset the EMMC boot mode\n"); + ret = sysfs_create_bin_file(&pdev->dev.kobj, + &mlxbf_bootctl_bootfifo_sysfs_attr); + if (ret) + pr_err("Unable to create bootfifo sysfs file, error %d\n", ret); + + return ret; +} + +static int mlxbf_bootctl_remove(struct platform_device *pdev) +{ + sysfs_remove_bin_file(&pdev->dev.kobj, + &mlxbf_bootctl_bootfifo_sysfs_attr); + return 0; } static struct platform_driver mlxbf_bootctl_driver = { .probe = mlxbf_bootctl_probe, + .remove = mlxbf_bootctl_remove, .driver = { .name = "mlxbf-bootctl", .dev_groups = mlxbf_bootctl_groups, -- cgit v1.2.3 From f6e7ac4c35a28aef0be93b32c533ae678ad0b9e7 Mon Sep 17 00:00:00 2001 From: Feng Jiang Date: Wed, 12 Apr 2023 17:37:34 +0800 Subject: platform/x86/amd: pmc: Fix memory leak in amd_pmc_stb_debugfs_open_v2() Function amd_pmc_stb_debugfs_open_v2() may be called when the STB debug mechanism enabled. When amd_pmc_send_cmd() fails, the 'buf' needs to be released. Signed-off-by: Feng Jiang Link: https://lore.kernel.org/r/20230412093734.1126410-1-jiangfeng@kylinos.cn Reviewed-by: Hans de Goede Signed-off-by: Hans de Goede --- drivers/platform/x86/amd/pmc.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers/platform') diff --git a/drivers/platform/x86/amd/pmc.c b/drivers/platform/x86/amd/pmc.c index 877b629e5cae..14d3ff425300 100644 --- a/drivers/platform/x86/amd/pmc.c +++ b/drivers/platform/x86/amd/pmc.c @@ -265,6 +265,7 @@ static int amd_pmc_stb_debugfs_open_v2(struct inode *inode, struct file *filp) dev->msg_port = 0; if (ret) { dev_err(dev->dev, "error: S2D_NUM_SAMPLES not supported : %d\n", ret); + kfree(buf); return ret; } -- cgit v1.2.3 From efebfa80ce24cba9e7af786e9dec911cf9e7b35d Mon Sep 17 00:00:00 2001 From: Shyam Sundar S K Date: Wed, 12 Apr 2023 16:45:00 +0530 Subject: platform/x86: amd: pmc: provide user message where s0ix is not supported Some platforms do not support hardware backed s0i3 transitions. When such CPUs are detected, provide a warning message to the user. Suggested-by: Sanket Goswami Signed-off-by: Shyam Sundar S K Reviewed-by: Mario Limonciello Link: https://lore.kernel.org/r/20230412111500.2602529-1-Shyam-sundar.S-k@amd.com Reviewed-by: Hans de Goede Signed-off-by: Hans de Goede --- drivers/platform/x86/amd/pmc.c | 9 +++++++++ 1 file changed, 9 insertions(+) (limited to 'drivers/platform') diff --git a/drivers/platform/x86/amd/pmc.c b/drivers/platform/x86/amd/pmc.c index 14d3ff425300..5a935db80fab 100644 --- a/drivers/platform/x86/amd/pmc.c +++ b/drivers/platform/x86/amd/pmc.c @@ -94,6 +94,7 @@ #define AMD_CPU_ID_YC 0x14B5 #define AMD_CPU_ID_CB 0x14D8 #define AMD_CPU_ID_PS 0x14E8 +#define AMD_CPU_ID_SP 0x14A4 #define PMC_MSG_DELAY_MIN_US 50 #define RESPONSE_REGISTER_LOOP_MAX 20000 @@ -886,6 +887,7 @@ static const struct pci_device_id pmc_pci_ids[] = { { PCI_DEVICE(PCI_VENDOR_ID_AMD, AMD_CPU_ID_RN) }, { PCI_DEVICE(PCI_VENDOR_ID_AMD, AMD_CPU_ID_PCO) }, { PCI_DEVICE(PCI_VENDOR_ID_AMD, AMD_CPU_ID_RV) }, + { PCI_DEVICE(PCI_VENDOR_ID_AMD, AMD_CPU_ID_SP) }, { } }; @@ -964,6 +966,13 @@ static int amd_pmc_probe(struct platform_device *pdev) } dev->cpu_id = rdev->device; + + if (dev->cpu_id == AMD_CPU_ID_SP) { + dev_warn_once(dev->dev, "S0i3 is not supported on this hardware\n"); + err = -ENODEV; + goto err_pci_dev_put; + } + dev->rdev = rdev; err = amd_smn_read(0, AMD_PMC_BASE_ADDR_LO, &val); if (err) { -- cgit v1.2.3 From 3f581602a22cc5445a66501fa13a80894d83e42a Mon Sep 17 00:00:00 2001 From: "David E. Box" Date: Wed, 12 Apr 2023 18:29:22 -0700 Subject: platform/x86/intel/pmt: Ignore uninitialized entries MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit On Intel Xeon, unused PMT regions will have uninitialized discovery headers containing all 0xF. Instead of returning an error, just skip the region. Signed-off-by: David E. Box Reviewed-by: Ilpo Järvinen Link: https://lore.kernel.org/r/20230413012922.1521377-1-david.e.box@linux.intel.com Signed-off-by: Hans de Goede --- drivers/platform/x86/intel/pmt/telemetry.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/platform') diff --git a/drivers/platform/x86/intel/pmt/telemetry.c b/drivers/platform/x86/intel/pmt/telemetry.c index 71cdef42084a..39cbc87cc28a 100644 --- a/drivers/platform/x86/intel/pmt/telemetry.c +++ b/drivers/platform/x86/intel/pmt/telemetry.c @@ -78,7 +78,7 @@ static int pmt_telem_header_decode(struct intel_pmt_entry *entry, * reserved for future use. They have zero size. Do not fail * probe for these. Just ignore them. */ - if (header->size == 0) + if (header->size == 0 || header->access_type == 0xF) return 1; return 0; -- cgit v1.2.3 From 14f6f0e3709a8aa987ae78783d2e4d63f88cc13a Mon Sep 17 00:00:00 2001 From: "David E. Box" Date: Wed, 12 Apr 2023 18:32:30 -0700 Subject: platform/x86/intel/sdsi: Change mailbox timeout MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit On some platforms, it may take up to 400ms for the ready bit to be set in a successful mailbox transaction. Set the timeout to 500ms to cover the worst case. Signed-off-by: David E. Box Reviewed-by: Ilpo Järvinen Link: https://lore.kernel.org/r/20230413013230.1521584-1-david.e.box@linux.intel.com Signed-off-by: Hans de Goede --- drivers/platform/x86/intel/sdsi.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/platform') diff --git a/drivers/platform/x86/intel/sdsi.c b/drivers/platform/x86/intel/sdsi.c index 9e0ea2cdd704..556e7c6dbb05 100644 --- a/drivers/platform/x86/intel/sdsi.c +++ b/drivers/platform/x86/intel/sdsi.c @@ -49,7 +49,7 @@ #define SDSI_MBOX_CMD_SUCCESS 0x40 #define SDSI_MBOX_CMD_TIMEOUT 0x80 -#define MBOX_TIMEOUT_US 2000 +#define MBOX_TIMEOUT_US 500000 #define MBOX_TIMEOUT_ACQUIRE_US 1000 #define MBOX_POLLING_PERIOD_US 100 #define MBOX_ACQUIRE_NUM_RETRIES 5 -- cgit v1.2.3 From 3a133f7c51b20d5d330d5424f3ef0dc283992500 Mon Sep 17 00:00:00 2001 From: Daniel Bertalan Date: Fri, 14 Apr 2023 18:02:10 +0000 Subject: platform/x86: thinkpad_acpi: Fix Embedded Controller access on X380 Yoga On the X380 Yoga, the `ECRD` and `ECWR` ACPI objects cannot be used for accessing the Embedded Controller: instead of a method that reads from the EC's memory, `ECRD` is the name of a location in high memory. This meant that trying to call them would fail with the following message: ACPI: \_SB.PCI0.LPCB.EC.ECRD: 1 arguments were passed to a non-method ACPI object (RegionField) With this commit, it is now possible to access the EC and read temperature and fan speed information. Note that while writes to the HFSP register do go through (as indicated by subsequent reads showing the new value), the fan does not actually change its speed. Signed-off-by: Daniel Bertalan Link: https://lore.kernel.org/r/20230414180034.63914-1-dani@danielbertalan.dev Reviewed-by: Hans de Goede Signed-off-by: Hans de Goede --- drivers/platform/x86/thinkpad_acpi.c | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) (limited to 'drivers/platform') diff --git a/drivers/platform/x86/thinkpad_acpi.c b/drivers/platform/x86/thinkpad_acpi.c index 32c10457399e..826c522ed1c0 100644 --- a/drivers/platform/x86/thinkpad_acpi.c +++ b/drivers/platform/x86/thinkpad_acpi.c @@ -11691,6 +11691,7 @@ static int __init thinkpad_acpi_module_init(void) { const struct dmi_system_id *dmi_id; int ret, i; + acpi_object_type obj_type; tpacpi_lifecycle = TPACPI_LIFE_INIT; @@ -11716,6 +11717,21 @@ static int __init thinkpad_acpi_module_init(void) TPACPI_ACPIHANDLE_INIT(ecrd); TPACPI_ACPIHANDLE_INIT(ecwr); + /* + * Quirk: in some models (e.g. X380 Yoga), an object named ECRD + * exists, but it is a register, not a method. + */ + if (ecrd_handle) { + acpi_get_type(ecrd_handle, &obj_type); + if (obj_type != ACPI_TYPE_METHOD) + ecrd_handle = NULL; + } + if (ecwr_handle) { + acpi_get_type(ecwr_handle, &obj_type); + if (obj_type != ACPI_TYPE_METHOD) + ecwr_handle = NULL; + } + tpacpi_wq = create_singlethread_workqueue(TPACPI_WORKQUEUE_NAME); if (!tpacpi_wq) { thinkpad_acpi_module_exit(); -- cgit v1.2.3 From 02377e983e22396e52d43c4d0adb9bbc85fa9a26 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Sun, 16 Apr 2023 23:28:38 +0200 Subject: platform/x86: x86-android-tablets: Update Yoga Book HiDeep touchscreen comment After recent i2c-hid-of changes, the i2c-hid-of driver could be used for the Yoga Book HiDeep touchscreen comment instead of the native hideep driver. Update the comment to reflect this. Signed-off-by: Hans de Goede Link: https://lore.kernel.org/r/20230416212841.311152-1-hdegoede@redhat.com --- drivers/platform/x86/x86-android-tablets/lenovo.c | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) (limited to 'drivers/platform') diff --git a/drivers/platform/x86/x86-android-tablets/lenovo.c b/drivers/platform/x86/x86-android-tablets/lenovo.c index a571dcd11544..b5dd74bf9e8c 100644 --- a/drivers/platform/x86/x86-android-tablets/lenovo.c +++ b/drivers/platform/x86/x86-android-tablets/lenovo.c @@ -41,10 +41,13 @@ static struct lp855x_platform_data lenovo_lp8557_pdata = { /* Lenovo Yoga Book X90F / X90L's Android factory img has everything hardcoded */ /* - * The HiDeep IST940E touchscreen comes up in HID mode and could alternatively - * be used in I2C-HID mode (I2C-HID descriptor reg 0x0020) if i2c-hid-of.c is - * modified to use generic (non-OF) device-properties and thought to deal with - * the reset GPIO. "hideep,force-native-protocol" resets it to native mode. + * The HiDeep IST940E touchscreen comes up in I2C-HID mode. The native protocol + * reports ABS_MT_PRESSURE and ABS_MT_TOUCH_MAJOR which are not reported in HID + * mode, so using native mode is preferred. + * It could alternatively be used in HID mode by changing the properties to: + * PROPERTY_ENTRY_U32("hid-descr-addr", 0x0020), + * PROPERTY_ENTRY_U32("post-reset-deassert-delay-ms", 120), + * and changing board_info.type to "hid-over-i2c". */ static const struct property_entry lenovo_yb1_x90_hideep_ts_props[] = { PROPERTY_ENTRY_U32("touchscreen-size-x", 1200), -- cgit v1.2.3 From 95b829f89da3fd635b60b9b069395dede17a612c Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Sun, 16 Apr 2023 23:28:39 +0200 Subject: platform/x86: x86-android-tablets: Add Wacom digitizer info for Lenovo Yoga Book The Lenovo Yoga Book has a wacom digitizer in its keyboard half, add the necessary info to instantiate an i2c_client for the digitizer. Signed-off-by: Hans de Goede Link: https://lore.kernel.org/r/20230416212841.311152-2-hdegoede@redhat.com --- drivers/platform/x86/x86-android-tablets/lenovo.c | 35 +++++++++++++++++++++++ 1 file changed, 35 insertions(+) (limited to 'drivers/platform') diff --git a/drivers/platform/x86/x86-android-tablets/lenovo.c b/drivers/platform/x86/x86-android-tablets/lenovo.c index b5dd74bf9e8c..438de8b92dc6 100644 --- a/drivers/platform/x86/x86-android-tablets/lenovo.c +++ b/drivers/platform/x86/x86-android-tablets/lenovo.c @@ -40,6 +40,16 @@ static struct lp855x_platform_data lenovo_lp8557_pdata = { /* Lenovo Yoga Book X90F / X90L's Android factory img has everything hardcoded */ +static const struct property_entry lenovo_yb1_x90_wacom_props[] = { + PROPERTY_ENTRY_U32("hid-descr-addr", 0x0001), + PROPERTY_ENTRY_U32("post-reset-deassert-delay-ms", 150), + { } +}; + +static const struct software_node lenovo_yb1_x90_wacom_node = { + .properties = lenovo_yb1_x90_wacom_props, +}; + /* * The HiDeep IST940E touchscreen comes up in I2C-HID mode. The native protocol * reports ABS_MT_PRESSURE and ABS_MT_TOUCH_MAJOR which are not reported in HID @@ -86,6 +96,22 @@ static const struct x86_i2c_client_info lenovo_yb1_x90_i2c_clients[] __initconst .trigger = ACPI_EDGE_SENSITIVE, .polarity = ACPI_ACTIVE_LOW, }, + }, { + /* Wacom Digitizer in keyboard half */ + .board_info = { + .type = "hid-over-i2c", + .addr = 0x09, + .dev_name = "wacom", + .swnode = &lenovo_yb1_x90_wacom_node, + }, + .adapter_path = "\\_SB_.PCI0.I2C4", + .irq_data = { + .type = X86_ACPI_IRQ_TYPE_GPIOINT, + .chip = "INT33FF:01", + .index = 49, + .trigger = ACPI_LEVEL_SENSITIVE, + .polarity = ACPI_ACTIVE_LOW, + }, }, { /* LP8557 Backlight controller */ .board_info = { @@ -131,9 +157,18 @@ static struct gpiod_lookup_table lenovo_yb1_x90_hideep_gpios = { }, }; +static struct gpiod_lookup_table lenovo_yb1_x90_wacom_gpios = { + .dev_id = "i2c-wacom", + .table = { + GPIO_LOOKUP("INT33FF:00", 82, "reset", GPIO_ACTIVE_LOW), + { } + }, +}; + static struct gpiod_lookup_table * const lenovo_yb1_x90_gpios[] = { &lenovo_yb1_x90_hideep_gpios, &lenovo_yb1_x90_goodix_gpios, + &lenovo_yb1_x90_wacom_gpios, NULL }; -- cgit v1.2.3 From 1d3f7a31aa9671c43e03570edaebcd7e02751c15 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Sun, 16 Apr 2023 23:28:40 +0200 Subject: platform/x86: x86-android-tablets: Add "yogabook-touch-kbd-digitizer-switch" pdev for Lenovo Yoga Book Add a "yogabook-touch-kbd-digitizer-switch" platform-device, for the lenovo-yogabook driver to bind to, to the x86_dev_info for the Lenovo Yoga Book 1 Android models (yb1-x90f/l). Signed-off-by: Hans de Goede Link: https://lore.kernel.org/r/20230416212841.311152-3-hdegoede@redhat.com --- drivers/platform/x86/x86-android-tablets/lenovo.c | 9 +++++++++ 1 file changed, 9 insertions(+) (limited to 'drivers/platform') diff --git a/drivers/platform/x86/x86-android-tablets/lenovo.c b/drivers/platform/x86/x86-android-tablets/lenovo.c index 438de8b92dc6..cf1629970f98 100644 --- a/drivers/platform/x86/x86-android-tablets/lenovo.c +++ b/drivers/platform/x86/x86-android-tablets/lenovo.c @@ -140,6 +140,13 @@ static const struct x86_i2c_client_info lenovo_yb1_x90_i2c_clients[] __initconst }, }; +static const struct platform_device_info lenovo_yb1_x90_pdevs[] __initconst = { + { + .name = "yogabook-touch-kbd-digitizer-switch", + .id = PLATFORM_DEVID_NONE, + }, +}; + static struct gpiod_lookup_table lenovo_yb1_x90_goodix_gpios = { .dev_id = "i2c-goodix_ts", .table = { @@ -194,6 +201,8 @@ static int __init lenovo_yb1_x90_init(void) const struct x86_dev_info lenovo_yogabook_x90_info __initconst = { .i2c_client_info = lenovo_yb1_x90_i2c_clients, .i2c_client_count = ARRAY_SIZE(lenovo_yb1_x90_i2c_clients), + .pdev_info = lenovo_yb1_x90_pdevs, + .pdev_count = ARRAY_SIZE(lenovo_yb1_x90_pdevs), .gpiod_lookup_tables = lenovo_yb1_x90_gpios, .init = lenovo_yb1_x90_init, }; -- cgit v1.2.3 From e578c943e363ff47d08e7b4f5648f3da9db325d8 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Sun, 16 Apr 2023 23:28:41 +0200 Subject: platform/x86: x86-android-tablets: Add accelerometer support for Yoga Tablet 2 1050/830 series The Yoga Tablet 2 1050/830 series have a LSM303DA accelerometer + magnetometer (IMU), add this to the list of i2c_clients to instantiate on these models. Signed-off-by: Hans de Goede Link: https://lore.kernel.org/r/20230416212841.311152-4-hdegoede@redhat.com --- drivers/platform/x86/x86-android-tablets/lenovo.c | 32 ++++++++++++++++++++++- 1 file changed, 31 insertions(+), 1 deletion(-) (limited to 'drivers/platform') diff --git a/drivers/platform/x86/x86-android-tablets/lenovo.c b/drivers/platform/x86/x86-android-tablets/lenovo.c index cf1629970f98..65cfccaa2894 100644 --- a/drivers/platform/x86/x86-android-tablets/lenovo.c +++ b/drivers/platform/x86/x86-android-tablets/lenovo.c @@ -255,8 +255,19 @@ static struct x86_gpio_button lenovo_yoga_tab2_830_1050_lid = { /* This gets filled by lenovo_yoga_tab2_830_1050_init() */ static struct rmi_device_platform_data lenovo_yoga_tab2_830_1050_rmi_pdata = { }; -static const struct x86_i2c_client_info lenovo_yoga_tab2_830_1050_i2c_clients[] __initconst = { +static struct x86_i2c_client_info lenovo_yoga_tab2_830_1050_i2c_clients[] __initdata = { { + /* + * This must be the first entry because lenovo_yoga_tab2_830_1050_init() + * may update its swnode. LSM303DA accelerometer + magnetometer. + */ + .board_info = { + .type = "lsm303d", + .addr = 0x1d, + .dev_name = "lsm303d", + }, + .adapter_path = "\\_SB_.I2C5", + }, { /* bq24292i battery charger */ .board_info = { .type = "bq24190", @@ -357,7 +368,24 @@ const struct x86_dev_info lenovo_yoga_tab2_830_1050_info __initconst = { * The Lenovo Yoga Tablet 2 830 and 1050 (8" vs 10") versions use the same * mainboard, but the 830 uses a portrait LCD panel with a landscape touchscreen, * requiring the touchscreen driver to adjust the touch-coords to match the LCD. + * And requiring the accelerometer to have a mount-matrix set to correct for + * the 90° rotation of the LCD vs the frame. */ +static const char * const lenovo_yoga_tab2_830_lms303d_mount_matrix[] = { + "0", "1", "0", + "-1", "0", "0", + "0", "0", "1" +}; + +static const struct property_entry lenovo_yoga_tab2_830_lms303d_props[] = { + PROPERTY_ENTRY_STRING_ARRAY("mount-matrix", lenovo_yoga_tab2_830_lms303d_mount_matrix), + { } +}; + +static const struct software_node lenovo_yoga_tab2_830_lms303d_node = { + .properties = lenovo_yoga_tab2_830_lms303d_props, +}; + static int __init lenovo_yoga_tab2_830_1050_init_touchscreen(void) { struct gpio_desc *gpiod; @@ -375,6 +403,8 @@ static int __init lenovo_yoga_tab2_830_1050_init_touchscreen(void) pr_info("detected Lenovo Yoga Tablet 2 830F/L\n"); lenovo_yoga_tab2_830_1050_rmi_pdata.sensor_pdata.axis_align.swap_axes = true; lenovo_yoga_tab2_830_1050_rmi_pdata.sensor_pdata.axis_align.flip_y = true; + lenovo_yoga_tab2_830_1050_i2c_clients[0].board_info.swnode = + &lenovo_yoga_tab2_830_lms303d_node; } return 0; -- cgit v1.2.3