From 6614823a16b700b7d996e75b1200b29bec88f8ef Mon Sep 17 00:00:00 2001 From: Srinivas Pandruvada Date: Fri, 10 Feb 2023 22:32:47 -0800 Subject: [PATCH 001/178] 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(-) 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 From 33c16dc1a2d1697be0fec95a0393bef4fbc27a0f Mon Sep 17 00:00:00 2001 From: Srinivas Pandruvada Date: Fri, 10 Feb 2023 22:32:48 -0800 Subject: [PATCH 002/178] 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 --- .../x86/intel/speed_select_if/isst_if_common.c | 11 +++++++++++ .../x86/intel/speed_select_if/isst_if_common.h | 4 ++++ 2 files changed, 15 insertions(+) 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 */ From 0e8ae77c869ed425488e3db43dbb76ec9bca8655 Mon Sep 17 00:00:00 2001 From: Srinivas Pandruvada Date: Fri, 10 Feb 2023 22:32:49 -0800 Subject: [PATCH 003/178] 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 --- .../platform/x86/intel/speed_select_if/isst_if_common.c | 8 +++++++- .../platform/x86/intel/speed_select_if/isst_if_common.h | 3 ++- 2 files changed, 9 insertions(+), 2 deletions(-) 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); From 2f0cf1e85ddb5ae17284050dc1adafb89e4f1d8f Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Mon, 13 Feb 2023 21:35:53 +0100 Subject: [PATCH 004/178] 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(-) 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 = { From afdcb5353f06cf5ddee3999f7e6353e889a50edc Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Sun, 19 Feb 2023 17:06:14 +0100 Subject: [PATCH 005/178] 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 +- .../platform/x86/x86-android-tablets/Kconfig | 21 +++++++++++++++++++ .../platform/x86/x86-android-tablets/Makefile | 8 +++++++ .../x86-android-tablets-main.c} | 4 ++-- 6 files changed, 34 insertions(+), 20 deletions(-) create mode 100644 drivers/platform/x86/x86-android-tablets/Kconfig create mode 100644 drivers/platform/x86/x86-android-tablets/Makefile rename drivers/platform/x86/{x86-android-tablets.c => x86-android-tablets/x86-android-tablets-main.c} (99%) 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/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.c b/drivers/platform/x86/x86-android-tablets/x86-android-tablets-main.c similarity index 99% rename from drivers/platform/x86/x86-android-tablets.c rename to drivers/platform/x86/x86-android-tablets/x86-android-tablets-main.c index 8405e1c58d52..08cc5e158460 100644 --- a/drivers/platform/x86/x86-android-tablets.c +++ b/drivers/platform/x86/x86-android-tablets/x86-android-tablets-main.c @@ -34,8 +34,8 @@ #include #include /* For gpio_get_desc() which is EXPORT_SYMBOL_GPL() */ -#include "../../gpio/gpiolib.h" -#include "../../gpio/gpiolib-acpi.h" +#include "../../../gpio/gpiolib.h" +#include "../../../gpio/gpiolib-acpi.h" /* * Helper code to get Linux IRQ numbers given a description of the IRQ source From 62a5f689a068951de5ed8487ec7f0de52245bd92 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Sun, 19 Feb 2023 17:32:42 +0100 Subject: [PATCH 006/178] 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 --- .../platform/x86/x86-android-tablets/Makefile | 2 +- .../platform/x86/x86-android-tablets/core.c | 365 ++++++++++++++++ .../x86-android-tablets-main.c | 404 +----------------- .../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 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 From 3a75d1690b9adb116ca4d11578ff2fa358823382 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Sun, 19 Feb 2023 17:43:03 +0100 Subject: [PATCH 007/178] 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 --- .../platform/x86/x86-android-tablets/Makefile | 2 +- .../platform/x86/x86-android-tablets/core.c | 2 - .../platform/x86/x86-android-tablets/dmi.c | 148 ++++++++++++++++ .../x86-android-tablets-main.c | 160 ++---------------- .../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 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 From 4ed14c23e1e82fbfb06cb37b9197ca56bbdf39f3 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Sun, 19 Feb 2023 20:20:45 +0100 Subject: [PATCH 008/178] 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 --- .../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-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 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", }; From 97abac9c8b893822255c7847823bb65b6c7efb6a Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Sun, 19 Feb 2023 20:22:39 +0100 Subject: [PATCH 009/178] 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 --- .../platform/x86/x86-android-tablets/Makefile | 2 +- .../platform/x86/x86-android-tablets/asus.c | 358 ++++++++++++++++++ .../x86-android-tablets-main.c | 340 ----------------- 3 files changed, 359 insertions(+), 341 deletions(-) create mode 100644 drivers/platform/x86/x86-android-tablets/asus.c 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 From 7bf974f695f1a978097a3b43ac26afd46eba626d Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Sun, 19 Feb 2023 21:15:24 +0100 Subject: [PATCH 010/178] 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 --- .../platform/x86/x86-android-tablets/Makefile | 3 +- .../platform/x86/x86-android-tablets/lenovo.c | 419 ++++++++++++++++++ .../x86-android-tablets-main.c | 406 +---------------- 3 files changed, 422 insertions(+), 406 deletions(-) create mode 100644 drivers/platform/x86/x86-android-tablets/lenovo.c 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", From cc183ad4590bef3831c0688ccfa386c595adc428 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Sun, 19 Feb 2023 21:36:23 +0100 Subject: [PATCH 011/178] 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 +- .../{x86-android-tablets-main.c => other.c} | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) rename drivers/platform/x86/x86-android-tablets/{x86-android-tablets-main.c => other.c} (99%) 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/x86-android-tablets-main.c b/drivers/platform/x86/x86-android-tablets/other.c similarity index 99% rename from drivers/platform/x86/x86-android-tablets/x86-android-tablets-main.c rename to drivers/platform/x86/x86-android-tablets/other.c index 0362194e225c..83b30b24d57f 100644 --- a/drivers/platform/x86/x86-android-tablets/x86-android-tablets-main.c +++ b/drivers/platform/x86/x86-android-tablets/other.c @@ -1,11 +1,11 @@ -// SPDX-License-Identifier: GPL-2.0+ +// 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-2022 Hans de Goede + * Copyright (C) 2021-2023 Hans de Goede */ #include From e2200d3f26dac108a519f014fb42a0a034105394 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Sun, 19 Feb 2023 22:50:19 +0100 Subject: [PATCH 012/178] 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 --- .../platform/x86/x86-android-tablets/asus.c | 65 +++++-------------- .../platform/x86/x86-android-tablets/core.c | 30 ++++++++- .../platform/x86/x86-android-tablets/other.c | 51 ++++----------- .../x86-android-tablets/x86-android-tablets.h | 8 +++ 4 files changed, 64 insertions(+), 90 deletions(-) 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; From 772cbba5a877eb7c528f3659415373c40e48a057 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Mon, 20 Feb 2023 21:26:42 +0100 Subject: [PATCH 013/178] 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 ------------------ .../platform/x86/x86-android-tablets/dmi.c | 8 ++ .../platform/x86/x86-android-tablets/other.c | 28 ++++ .../x86-android-tablets/x86-android-tablets.h | 1 + 7 files changed, 37 insertions(+), 142 deletions(-) delete mode 100644 drivers/platform/x86/peaq-wmi.c 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[]; From 9b1d2662b8c586eb10cb939c7ae2c25bb36d2b61 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Tue, 21 Feb 2023 22:52:58 +0100 Subject: [PATCH 014/178] 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 --- .../platform/x86/x86-android-tablets/lenovo.c | 46 +++++++++++++++++++ 1 file changed, 46 insertions(+) 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, }; From 607fbac0fccba506b4f9e47994504121db776956 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Wed, 22 Feb 2023 22:42:26 +0100 Subject: [PATCH 015/178] 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 --- .../platform/x86/x86-android-tablets/lenovo.c | 23 +++++++++++++++++++ 1 file changed, 23 insertions(+) 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", } }; From dadbc368f4f26f59debe49e1cdf44883e8799fd2 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Sat, 25 Feb 2023 17:11:04 +0100 Subject: [PATCH 016/178] 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(+) 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, From 5f250f8a77c4d30da52ee4184b770e327fbe76e2 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Tue, 28 Feb 2023 09:35:03 +0100 Subject: [PATCH 017/178] 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 +++++----- .../x86/x86-android-tablets/x86-android-tablets.h | 2 +- 3 files changed, 10 insertions(+), 10 deletions(-) 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; From 96c59f596ebe7d5f5a03f17a16a15e19f22cf666 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig?= Date: Thu, 2 Mar 2023 15:47:04 +0100 Subject: [PATCH 018/178] 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(-) 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, }; From db0664d1afe0600b07c1754fcc6bd300d8482071 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig?= Date: Thu, 2 Mar 2023 15:47:05 +0100 Subject: [PATCH 019/178] 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(-) 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); From 5454b36274fd63e40935975fbd13f4abbb5c5270 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig?= Date: Thu, 2 Mar 2023 15:47:06 +0100 Subject: [PATCH 020/178] 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(-) 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, }, From 3359d99e7a4c1258804ba966ab62f6bc0b45ba78 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig?= Date: Thu, 2 Mar 2023 15:47:07 +0100 Subject: [PATCH 021/178] 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(-) 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); From b444276b12e7da2069ea111116bccc21d864484e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig?= Date: Thu, 2 Mar 2023 15:47:08 +0100 Subject: [PATCH 022/178] 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(-) 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); From 03b5d07a9e2fcb56735a66f0fec109b47f8e82e9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig?= Date: Thu, 2 Mar 2023 15:47:09 +0100 Subject: [PATCH 023/178] 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(-) 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) From f4278cc8772fad93f292e3ce0722c57fc1eea6eb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig?= Date: Thu, 2 Mar 2023 15:47:10 +0100 Subject: [PATCH 024/178] 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(-) 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 */ From d1bda904933417209e0b8750d4fd3cf8b99fca1f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig?= Date: Thu, 2 Mar 2023 15:47:11 +0100 Subject: [PATCH 025/178] 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(-) 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) From 467daaf09496b96c8a8744615d481daeddd089c7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig?= Date: Thu, 2 Mar 2023 15:47:12 +0100 Subject: [PATCH 026/178] 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(-) 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 = { From bb9d621f44069de18f102f57998277fdceb1133d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig?= Date: Thu, 2 Mar 2023 15:47:13 +0100 Subject: [PATCH 027/178] 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(-) 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, From 3b5b3e9b00a626f5defee82241a2ab64d93f38a1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig?= Date: Thu, 2 Mar 2023 15:47:14 +0100 Subject: [PATCH 028/178] 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(-) 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, From fbc0e1e6a700146d5246df5021e497c27b121aaf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig?= Date: Thu, 2 Mar 2023 15:47:15 +0100 Subject: [PATCH 029/178] 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(-) 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) From 7836f70a03a0e9384632a8300b6625f4e164a99c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig?= Date: Thu, 2 Mar 2023 15:47:16 +0100 Subject: [PATCH 030/178] 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(-) 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) From d21c474c2312037a845805350b9fa614ab5dfab6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig?= Date: Thu, 2 Mar 2023 15:47:17 +0100 Subject: [PATCH 031/178] 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(-) 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, From f9f23df7b640ff97b5a6f489f4ebfa9692d1e08e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig?= Date: Thu, 2 Mar 2023 15:47:18 +0100 Subject: [PATCH 032/178] 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(-) 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, From bca92922e2a7800adf9d965e409f7ecb74ce9494 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig?= Date: Thu, 2 Mar 2023 15:47:19 +0100 Subject: [PATCH 033/178] 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(-) 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); From fba53497e3c0572a2d621bbd98615c6e07a9e518 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig?= Date: Thu, 2 Mar 2023 15:47:20 +0100 Subject: [PATCH 034/178] 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(-) 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); From 63b890167e7f697a5e22d83d59a2717535ffb50b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig?= Date: Thu, 2 Mar 2023 15:47:21 +0100 Subject: [PATCH 035/178] 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(-) 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, }; /* From 445f79fdd43566695a97d2eb8b59419eecb12af4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig?= Date: Thu, 2 Mar 2023 15:47:22 +0100 Subject: [PATCH 036/178] 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(-) 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); From e709455e9736b1cf15d2c6914e26d34d4ebbdcf3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig?= Date: Thu, 2 Mar 2023 15:47:23 +0100 Subject: [PATCH 037/178] 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(-) 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) From 6ee08b4eba3d36d5cc2bf1594040de95ae12ee7d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig?= Date: Thu, 2 Mar 2023 15:47:24 +0100 Subject: [PATCH 038/178] 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(-) 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); From ee12d8b43cf7e847af8b24988bcda7ae23265383 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig?= Date: Thu, 2 Mar 2023 15:47:25 +0100 Subject: [PATCH 039/178] 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(-) 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); From 1655db18b8dd387185dd4b491ce03b21176ab545 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig?= Date: Thu, 2 Mar 2023 15:47:26 +0100 Subject: [PATCH 040/178] 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(-) 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); From 2d58b0092b7558fd944ff729dcd2fbe80b45629b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig?= Date: Thu, 2 Mar 2023 15:47:27 +0100 Subject: [PATCH 041/178] 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(-) 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, }, From 4222272a04c386743e37f951fd4fdf958ba7f4d6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig?= Date: Thu, 2 Mar 2023 15:47:28 +0100 Subject: [PATCH 042/178] 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(-) 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 From df23e156a98800e0a379cb6fdc68425b453a2696 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig?= Date: Thu, 2 Mar 2023 15:47:29 +0100 Subject: [PATCH 043/178] 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(-) 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; From 143b3c1af5884ba0900d721f984245ca9897700d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig?= Date: Thu, 2 Mar 2023 15:47:30 +0100 Subject: [PATCH 044/178] 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(-) 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); From 0f16136b7acb20c0a6ade4328259d0db977d2ec1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig?= Date: Thu, 2 Mar 2023 15:47:31 +0100 Subject: [PATCH 045/178] 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(-) 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) From 9a318b8b069e38f1ebfe19afac1a2eac0c45ed32 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig?= Date: Thu, 2 Mar 2023 15:47:32 +0100 Subject: [PATCH 046/178] 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(-) 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); From 90caf1dfe9efb727874bcafd30fefb0807cb7670 Mon Sep 17 00:00:00 2001 From: Orlando Chamberlain Date: Fri, 3 Mar 2023 22:28:40 +1100 Subject: [PATCH 047/178] 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(-) 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; From 96ec2d9868c4afd3cea17f4ee18b84ccc1cde20f Mon Sep 17 00:00:00 2001 From: Orlando Chamberlain Date: Fri, 3 Mar 2023 22:28:41 +1100 Subject: [PATCH 048/178] 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(-) 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: From 0c18184de990e63f708b090bcb9fc6c0fbc427cd Mon Sep 17 00:00:00 2001 From: Orlando Chamberlain Date: Fri, 3 Mar 2023 22:28:42 +1100 Subject: [PATCH 049/178] 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(-) 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; } From f863fac1b15f77fc3c115f5afbd535983bb961a1 Mon Sep 17 00:00:00 2001 From: Orlando Chamberlain Date: Fri, 3 Mar 2023 22:28:43 +1100 Subject: [PATCH 050/178] 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(+) 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) { From d2e6832147998ba12c810cf88862126f6fae9b0a Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Tue, 7 Mar 2023 12:38:13 +0100 Subject: [PATCH 051/178] 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 | 59 +++++++++++++++++-------------- 1 file changed, 33 insertions(+), 26 deletions(-) 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,34 +861,39 @@ 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; +#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; + 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); + + /* + * 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; gmux_data->dhandle = ACPI_HANDLE(&pnp->dev); From ad3d0ee8dca5fa4a656057e20f9605bbe7ff067c Mon Sep 17 00:00:00 2001 From: Orlando Chamberlain Date: Thu, 9 Mar 2023 21:43:54 +1100 Subject: [PATCH 052/178] platform/x86: apple-gmux: Update apple_gmux_detect documentation Commit fc83fbc80e1a ("platform/x86: apple-gmux: refactor gmux types") neglected to update the documentation of apple_gmux_detect's arguments. Update the documentation to reflect that commit's changes. include/linux/apple-gmux.h:99: warning: Function parameter or member 'type_ret' not described in 'apple_gmux_detect' include/linux/apple-gmux.h:99: warning: Excess function parameter 'indexed_ret' description in 'apple_gmux_detect' Reported-by: Stephen Rothwell Link: https://lore.kernel.org/lkml/20230309122822.77435e33@canb.auug.org.au/ Fixes: fc83fbc80e1a ("platform/x86: apple-gmux: refactor gmux types") Signed-off-by: Orlando Chamberlain Link: https://lore.kernel.org/r/20230309104353.10905-1-orlandoch.dev@gmail.com Reviewed-by: Hans de Goede Signed-off-by: Hans de Goede --- include/linux/apple-gmux.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/linux/apple-gmux.h b/include/linux/apple-gmux.h index 272f63f8fd7c..a7a7d430024b 100644 --- a/include/linux/apple-gmux.h +++ b/include/linux/apple-gmux.h @@ -86,7 +86,7 @@ static inline bool apple_gmux_is_mmio(unsigned long iostart) * apple_gmux_detect() - detect if gmux is built into the machine * * @pnp_dev: Device to probe or NULL to use the first matching device - * @indexed_ret: Returns (by reference) if the gmux is indexed or not + * @type_ret: Returns (by reference) the apple_gmux_type of the device * * Detect if a supported gmux device is present by actually probing it. * This avoids the false positives returned on some models by From f0f2903de50418899a5e5dcecf2906da44a7a05e Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Fri, 10 Mar 2023 15:31:13 +0300 Subject: [PATCH 053/178] 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(-) 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; From 3608a2cd818a8d97bd3d4dc32f6c0afaf09b15ec Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Tue, 7 Mar 2023 13:05:40 +0100 Subject: [PATCH 054/178] 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 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 */ From b59018c14c1efe36ddc96f3d27308f18e7d1b03e Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Thu, 9 Mar 2023 10:40:35 +0100 Subject: [PATCH 055/178] 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(-) 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 From 94227b9c88f3df8ac3025611719cebd125b5981a Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Fri, 3 Mar 2023 23:19:28 +0100 Subject: [PATCH 056/178] 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 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"); From b58a444d7f28ea59fd37a4615e6be86f5d45c880 Mon Sep 17 00:00:00 2001 From: Maximilian Luz Date: Sat, 4 Mar 2023 20:46:09 +0100 Subject: [PATCH 057/178] 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 --- .../surface/surface_aggregator_tabletsw.c | 123 ++++++++++++------ 1 file changed, 84 insertions(+), 39 deletions(-) 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; } From 37ff64cd81ff0abbe901fd4cf401998d534c1cd7 Mon Sep 17 00:00:00 2001 From: Maximilian Luz Date: Sat, 4 Mar 2023 20:46:10 +0100 Subject: [PATCH 058/178] 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 --- .../surface/surface_aggregator_tabletsw.c | 57 +++++++++++++++++++ 1 file changed, 57 insertions(+) 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); From 39e2ba648a6ee4519a5f0aa4cb995f8c97c5db6f Mon Sep 17 00:00:00 2001 From: Maximilian Luz Date: Sat, 4 Mar 2023 20:46:11 +0100 Subject: [PATCH 059/178] 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(-) 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, From 3ed0b880b71489d59f89b0bc1323ad34a136cca0 Mon Sep 17 00:00:00 2001 From: Koba Ko Date: Wed, 8 Mar 2023 14:24:14 +0800 Subject: [PATCH 060/178] 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(+) 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; From e6d3418130cdf718eff72db9a05d7262ebb49cff Mon Sep 17 00:00:00 2001 From: Srinivas Pandruvada Date: Tue, 7 Mar 2023 23:06:35 -0800 Subject: [PATCH 061/178] 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 --- .../intel/speed_select_if/isst_if_common.c | 28 +++++++++++++++++++ 1 file changed, 28 insertions(+) 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; From d805456c712f93ba8a012430f2a93bec133b6ff4 Mon Sep 17 00:00:00 2001 From: Srinivas Pandruvada Date: Tue, 7 Mar 2023 23:06:36 -0800 Subject: [PATCH 062/178] 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 --- .../x86/intel/speed_select_if/Kconfig | 4 + .../x86/intel/speed_select_if/Makefile | 2 + .../x86/intel/speed_select_if/isst_tpmi.c | 53 ++++ .../intel/speed_select_if/isst_tpmi_core.c | 274 ++++++++++++++++++ .../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 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 From 0ab147bb840fca2bc3bca88f320b34c5b5cc013c Mon Sep 17 00:00:00 2001 From: Srinivas Pandruvada Date: Tue, 7 Mar 2023 23:06:37 -0800 Subject: [PATCH 063/178] 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 --- .../intel/speed_select_if/isst_tpmi_core.c | 291 +++++++++++++++++- 1 file changed, 287 insertions(+), 4 deletions(-) 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; } From 12a7d2cb811dd8a884dea088a2701fcb8d00136e Mon Sep 17 00:00:00 2001 From: Srinivas Pandruvada Date: Tue, 7 Mar 2023 23:06:38 -0800 Subject: [PATCH 064/178] 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 --- .../intel/speed_select_if/isst_tpmi_core.c | 264 ++++++++++++++++++ include/uapi/linux/isst_if.h | 79 ++++++ 2 files changed, 343 insertions(+) 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 From ea009e4769fa3bd05d4c111c3b6865eb3a9be829 Mon Sep 17 00:00:00 2001 From: Srinivas Pandruvada Date: Tue, 7 Mar 2023 23:06:39 -0800 Subject: [PATCH 065/178] 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 --- .../intel/speed_select_if/isst_tpmi_core.c | 417 +++++++++++++++++- include/uapi/linux/isst_if.h | 180 ++++++++ 2 files changed, 596 insertions(+), 1 deletion(-) 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 From 06a61df83209ac7f376616f83f3485217c703d50 Mon Sep 17 00:00:00 2001 From: Srinivas Pandruvada Date: Tue, 7 Mar 2023 23:06:40 -0800 Subject: [PATCH 066/178] 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 --- .../intel/speed_select_if/isst_tpmi_core.c | 87 +++++++++++++++++++ 1 file changed, 87 insertions(+) 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; } From f8e0077a9d526cac7abbc682d83e98f834ffa909 Mon Sep 17 00:00:00 2001 From: Srinivas Pandruvada Date: Tue, 7 Mar 2023 23:06:41 -0800 Subject: [PATCH 067/178] 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 --- .../intel/speed_select_if/isst_tpmi_core.c | 66 +++++++++++++++++++ include/uapi/linux/isst_if.h | 26 ++++++++ 2 files changed, 92 insertions(+) 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 From 91576acab0203062b93a578a7d05c8fe9e527f80 Mon Sep 17 00:00:00 2001 From: Srinivas Pandruvada Date: Tue, 7 Mar 2023 23:06:42 -0800 Subject: [PATCH 068/178] 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 --- .../x86/intel/speed_select_if/isst_tpmi.c | 19 +++++++ .../intel/speed_select_if/isst_tpmi_core.c | 49 +++++++++++++++++++ .../intel/speed_select_if/isst_tpmi_core.h | 2 + 3 files changed, 70 insertions(+) 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 From 8496bacae643018a2e24f76aa22426c4b9264933 Mon Sep 17 00:00:00 2001 From: Jiapeng Chong Date: Thu, 9 Mar 2023 14:10:45 +0800 Subject: [PATCH 069/178] 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(-) 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; From da62908efe80f132f691efc2ace4ca67626de86b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20Wei=C3=9Fschuh?= Date: Sun, 12 Mar 2023 03:47:57 +0000 Subject: [PATCH 070/178] 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(-) 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; From daaa2a1f78347d73016b3a331fd48a0ca9e62192 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20Wei=C3=9Fschuh?= Date: Tue, 14 Mar 2023 00:07:52 +0000 Subject: [PATCH 071/178] 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(-) 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, From e970607955eba62bb361e9dea493cd103e5ebd6b Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Sun, 12 Mar 2023 14:26:23 +0100 Subject: [PATCH 072/178] 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(-) 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}, From 06984c83e9afbb718f501b17b1f0a7a4ae6d0bb5 Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Sun, 12 Mar 2023 14:26:24 +0100 Subject: [PATCH 073/178] 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(-) 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}, From fd3c3584539026a708493a49f65d8ff3eb369002 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Mon, 13 Mar 2023 14:02:41 +0100 Subject: [PATCH 074/178] 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(-) 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"); From d7b2b9387edac5a93058dcbf9b70017a764bfd18 Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Thu, 16 Mar 2023 17:03:24 +0100 Subject: [PATCH 075/178] 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(+) 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, }; From 3f95ecf2a3e4db09e58d307932037e8f1210d6e7 Mon Sep 17 00:00:00 2001 From: "David E. Box" Date: Thu, 16 Mar 2023 15:46:28 -0700 Subject: [PATCH 076/178] 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(-) 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; }; From d908084385a41cfdb82dca6ab4086e3f1a9dd4ae Mon Sep 17 00:00:00 2001 From: "David E. Box" Date: Thu, 16 Mar 2023 15:57:35 -0700 Subject: [PATCH 077/178] 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(-) 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); From 6f561677c2f234bcf215350b76f2a2fea95fbebf Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Mon, 20 Mar 2023 11:38:15 +0100 Subject: [PATCH 078/178] 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(-) 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; } From 7c7e7c0d396b99d5b41d052dbf2b2bddcd5f7f3c Mon Sep 17 00:00:00 2001 From: Zhang Rui Date: Sat, 20 Aug 2022 18:11:21 +0800 Subject: [PATCH 079/178] tools/power/x86/intel-speed-select: Unify TRL levels TRL supports different levels including SSE/AVX2/AVX512. Avoid using hardcoded level name and structure fields, so that a loop can be used to parse each TRL level instead. This reduces several lines of source code. No functional changes are expected. Signed-off-by: Zhang Rui [srinivas.pandruvada@linux.intel.com: changelog edits] Signed-off-by: Srinivas Pandruvada --- .../power/x86/intel-speed-select/isst-core.c | 47 +++++++++++------- .../x86/intel-speed-select/isst-display.c | 48 ++++--------------- tools/power/x86/intel-speed-select/isst.h | 11 +++-- 3 files changed, 45 insertions(+), 61 deletions(-) diff --git a/tools/power/x86/intel-speed-select/isst-core.c b/tools/power/x86/intel-speed-select/isst-core.c index 2bfc118c4b87..aec81b5658a9 100644 --- a/tools/power/x86/intel-speed-select/isst-core.c +++ b/tools/power/x86/intel-speed-select/isst-core.c @@ -6,6 +6,25 @@ #include "isst.h" +int isst_get_trl_max_levels(void) +{ + return 3; +} + +char *isst_get_trl_level_name(int level) +{ + switch (level) { + case 0: + return "sse"; + case 1: + return "avx2"; + case 2: + return "avx512"; + default: + return NULL; + } +} + int isst_write_pm_config(struct isst_id *id, int cp_state) { unsigned int req, resp; @@ -795,6 +814,8 @@ int isst_get_process_ctdp(struct isst_id *id, int tdp_level, struct isst_pkg_ctd for (i = 0; i <= pkg_dev->levels; ++i) { struct isst_pkg_ctdp_level_info *ctdp_level; + int trl_max_levels = isst_get_trl_max_levels(); + int j; if (tdp_level != 0xff && i != tdp_level) continue; @@ -838,8 +859,8 @@ int isst_get_process_ctdp(struct isst_id *id, int tdp_level, struct isst_pkg_ctd ctdp_level->tdp_ratio = ctdp_level->sse_p1; } - isst_get_get_trl_from_msr(id, ctdp_level->trl_sse_active_cores); - isst_get_trl_bucket_info(id, &ctdp_level->buckets_info); + isst_get_get_trl_from_msr(id, ctdp_level->trl_ratios[0]); + isst_get_trl_bucket_info(id, &ctdp_level->trl_cores); continue; } @@ -861,24 +882,16 @@ int isst_get_process_ctdp(struct isst_id *id, int tdp_level, struct isst_pkg_ctd if (ret) return ret; - ret = isst_get_trl_bucket_info(id, &ctdp_level->buckets_info); + ret = isst_get_trl_bucket_info(id, &ctdp_level->trl_cores); if (ret) return ret; - ret = isst_get_get_trl(id, i, 0, - ctdp_level->trl_sse_active_cores); - if (ret) - return ret; - - ret = isst_get_get_trl(id, i, 1, - ctdp_level->trl_avx_active_cores); - if (ret) - return ret; - - ret = isst_get_get_trl(id, i, 2, - ctdp_level->trl_avx_512_active_cores); - if (ret) - return ret; + for (j = 0; j < trl_max_levels; j++) { + ret = isst_get_get_trl(id, i, j, + ctdp_level->trl_ratios[j]); + if (ret) + return ret; + } isst_get_uncore_p0_p1_info(id, i, ctdp_level); isst_get_p1_info(id, i, ctdp_level); diff --git a/tools/power/x86/intel-speed-select/isst-display.c b/tools/power/x86/intel-speed-select/isst-display.c index 7feadac04a6f..b9efbdf77dc1 100644 --- a/tools/power/x86/intel-speed-select/isst-display.c +++ b/tools/power/x86/intel-speed-select/isst-display.c @@ -354,6 +354,7 @@ void isst_ctdp_display_information(struct isst_id *id, FILE *outf, int tdp_level char header[256]; char value[512]; static int level; + int trl_max_levels = isst_get_trl_max_levels(); int i; if (pkg_dev->processed) @@ -361,7 +362,7 @@ void isst_ctdp_display_information(struct isst_id *id, FILE *outf, int tdp_level for (i = 0; i <= pkg_dev->levels; ++i) { struct isst_pkg_ctdp_level_info *ctdp_level; - int j; + int j, k; ctdp_level = &pkg_dev->ctdp_level[i]; if (!ctdp_level->processed) @@ -505,54 +506,21 @@ void isst_ctdp_display_information(struct isst_id *id, FILE *outf, int tdp_level format_and_print(outf, level + 2, header, value); } - snprintf(header, sizeof(header), "turbo-ratio-limits-sse"); - format_and_print(outf, level + 2, header, NULL); - for (j = 0; j < 8; ++j) { - snprintf(header, sizeof(header), "bucket-%d", j); - format_and_print(outf, level + 3, header, NULL); - - snprintf(header, sizeof(header), "core-count"); - snprintf(value, sizeof(value), "%llu", (ctdp_level->buckets_info >> (j * 8)) & 0xff); - format_and_print(outf, level + 4, header, value); - - snprintf(header, sizeof(header), - "max-turbo-frequency(MHz)"); - snprintf(value, sizeof(value), "%d", - ctdp_level->trl_sse_active_cores[j] * - DISP_FREQ_MULTIPLIER); - format_and_print(outf, level + 4, header, value); - } - - if (ctdp_level->trl_avx_active_cores[0]) { - snprintf(header, sizeof(header), "turbo-ratio-limits-avx2"); + for (k = 0; k < trl_max_levels; k++) { + snprintf(header, sizeof(header), "turbo-ratio-limits-%s", isst_get_trl_level_name(k)); format_and_print(outf, level + 2, header, NULL); + for (j = 0; j < 8; ++j) { snprintf(header, sizeof(header), "bucket-%d", j); format_and_print(outf, level + 3, header, NULL); snprintf(header, sizeof(header), "core-count"); - snprintf(value, sizeof(value), "%llu", (ctdp_level->buckets_info >> (j * 8)) & 0xff); + + snprintf(value, sizeof(value), "%llu", (ctdp_level->trl_cores >> (j * 8)) & 0xff); format_and_print(outf, level + 4, header, value); snprintf(header, sizeof(header), "max-turbo-frequency(MHz)"); - snprintf(value, sizeof(value), "%d", ctdp_level->trl_avx_active_cores[j] * DISP_FREQ_MULTIPLIER); - format_and_print(outf, level + 4, header, value); - } - } - - if (ctdp_level->trl_avx_512_active_cores[0]) { - snprintf(header, sizeof(header), "turbo-ratio-limits-avx512"); - format_and_print(outf, level + 2, header, NULL); - for (j = 0; j < 8; ++j) { - snprintf(header, sizeof(header), "bucket-%d", j); - format_and_print(outf, level + 3, header, NULL); - - snprintf(header, sizeof(header), "core-count"); - snprintf(value, sizeof(value), "%llu", (ctdp_level->buckets_info >> (j * 8)) & 0xff); - format_and_print(outf, level + 4, header, value); - - snprintf(header, sizeof(header), "max-turbo-frequency(MHz)"); - snprintf(value, sizeof(value), "%d", ctdp_level->trl_avx_512_active_cores[j] * DISP_FREQ_MULTIPLIER); + snprintf(value, sizeof(value), "%d", ctdp_level->trl_ratios[k][j] * DISP_FREQ_MULTIPLIER); format_and_print(outf, level + 4, header, value); } } diff --git a/tools/power/x86/intel-speed-select/isst.h b/tools/power/x86/intel-speed-select/isst.h index 824876e31e23..8f563ff300bb 100644 --- a/tools/power/x86/intel-speed-select/isst.h +++ b/tools/power/x86/intel-speed-select/isst.h @@ -28,6 +28,8 @@ #include #include +#include + #define BIT(x) (1 << (x)) #define BIT_ULL(nr) (1ULL << (nr)) #define GENMASK(h, l) (((~0UL) << (l)) & (~0UL >> (sizeof(long) * 8 - 1 - (h)))) @@ -153,10 +155,8 @@ struct isst_pkg_ctdp_level_info { size_t core_cpumask_size; cpu_set_t *core_cpumask; int cpu_count; - unsigned long long buckets_info; - int trl_sse_active_cores[ISST_TRL_MAX_ACTIVE_CORES]; - int trl_avx_active_cores[ISST_TRL_MAX_ACTIVE_CORES]; - int trl_avx_512_active_cores[ISST_TRL_MAX_ACTIVE_CORES]; + unsigned long long trl_cores; /* Buckets info */ + int trl_ratios[TRL_MAX_LEVELS][ISST_TRL_MAX_ACTIVE_CORES]; int kobj_bucket_index; int active_bucket; int fact_max_index; @@ -205,6 +205,9 @@ extern int isst_send_mbox_command(unsigned int cpu, unsigned char command, extern int isst_send_msr_command(unsigned int cpu, unsigned int command, int write, unsigned long long *req_resp); +extern int isst_get_trl_max_levels(void); +extern char *isst_get_trl_level_name(int level); + extern int isst_get_ctdp_levels(struct isst_id *id, struct isst_pkg_ctdp *pkg_dev); extern int isst_get_ctdp_control(struct isst_id *id, int config_index, struct isst_pkg_ctdp_level_info *ctdp_level); From 16c1892080d841e5d4b59295b96ae760b83c0b7f Mon Sep 17 00:00:00 2001 From: Zhang Rui Date: Sat, 20 Aug 2022 18:33:01 +0800 Subject: [PATCH 080/178] tools/power/x86/intel-speed-select: Follow TRL nameing for FACT info SST-TF high priority core count and ratios and low priority core ratios are also per TRL level. Cleanup the code to follow the same nameing convention as TRL. This removes hardcoded TRL level names and variables. No functional changes are expected. Signed-off-by: Zhang Rui Signed-off-by: Srinivas Pandruvada --- .../power/x86/intel-speed-select/isst-core.c | 28 ++------ .../x86/intel-speed-select/isst-display.c | 70 ++++++++----------- tools/power/x86/intel-speed-select/isst.h | 10 +-- 3 files changed, 38 insertions(+), 70 deletions(-) diff --git a/tools/power/x86/intel-speed-select/isst-core.c b/tools/power/x86/intel-speed-select/isst-core.c index aec81b5658a9..8aa2ee4bb232 100644 --- a/tools/power/x86/intel-speed-select/isst-core.c +++ b/tools/power/x86/intel-speed-select/isst-core.c @@ -592,7 +592,7 @@ int isst_get_fact_bucket_info(struct isst_id *id, int level, id->cpu, i, level, resp); for (j = 0; j < 4; ++j) { - bucket_info[j + (i * 4)].high_priority_cores_count = + bucket_info[j + (i * 4)].hp_cores = (resp >> (j * 8)) & 0xff; } } @@ -613,22 +613,8 @@ int isst_get_fact_bucket_info(struct isst_id *id, int level, id->cpu, i, level, k, resp); for (j = 0; j < 4; ++j) { - switch (k) { - case 0: - bucket_info[j + (i * 4)].sse_trl = - (resp >> (j * 8)) & 0xff; - break; - case 1: - bucket_info[j + (i * 4)].avx_trl = - (resp >> (j * 8)) & 0xff; - break; - case 2: - bucket_info[j + (i * 4)].avx512_trl = - (resp >> (j * 8)) & 0xff; - break; - default: - break; - } + bucket_info[j + (i * 4)].hp_ratios[k] = + (resp >> (j * 8)) & 0xff; } } } @@ -672,9 +658,9 @@ int isst_get_fact_info(struct isst_id *id, int level, int fact_bucket, struct is debug_printf("cpu:%d CONFIG_TDP_GET_FACT_LP_CLIPPING_RATIO resp:%x\n", id->cpu, resp); - fact_info->lp_clipping_ratio_license_sse = resp & 0xff; - fact_info->lp_clipping_ratio_license_avx2 = (resp >> 8) & 0xff; - fact_info->lp_clipping_ratio_license_avx512 = (resp >> 16) & 0xff; + fact_info->lp_ratios[0] = resp & 0xff; + fact_info->lp_ratios[1] = (resp >> 8) & 0xff; + fact_info->lp_ratios[2] = (resp >> 16) & 0xff; ret = isst_get_fact_bucket_info(id, level, fact_info->bucket_info); if (ret) @@ -685,7 +671,7 @@ int isst_get_fact_info(struct isst_id *id, int level, int fact_bucket, struct is if (fact_bucket != 0xff && fact_bucket != j) continue; - if (!fact_info->bucket_info[j].high_priority_cores_count) + if (!fact_info->bucket_info[j].hp_cores) break; print = 1; diff --git a/tools/power/x86/intel-speed-select/isst-display.c b/tools/power/x86/intel-speed-select/isst-display.c index b9efbdf77dc1..5e57623196fc 100644 --- a/tools/power/x86/intel-speed-select/isst-display.c +++ b/tools/power/x86/intel-speed-select/isst-display.c @@ -235,6 +235,7 @@ static void _isst_fact_display_information(struct isst_id *id, FILE *outf, int l int base_level) { struct isst_fact_bucket_info *bucket_info = fact_info->bucket_info; + int trl_max_levels = isst_get_trl_max_levels(); char header[256]; char value[256]; int print = 0, j; @@ -243,7 +244,7 @@ static void _isst_fact_display_information(struct isst_id *id, FILE *outf, int l if (fact_bucket != 0xff && fact_bucket != j) continue; - if (!bucket_info[j].high_priority_cores_count) + if (!bucket_info[j].hp_cores) break; print = 1; @@ -256,10 +257,12 @@ static void _isst_fact_display_information(struct isst_id *id, FILE *outf, int l snprintf(header, sizeof(header), "speed-select-turbo-freq-properties"); format_and_print(outf, base_level, header, NULL); for (j = 0; j < ISST_FACT_MAX_BUCKETS; ++j) { + int i; + if (fact_bucket != 0xff && fact_bucket != j) continue; - if (!bucket_info[j].high_priority_cores_count) + if (!bucket_info[j].hp_cores) break; snprintf(header, sizeof(header), "bucket-%d", j); @@ -267,54 +270,37 @@ static void _isst_fact_display_information(struct isst_id *id, FILE *outf, int l snprintf(header, sizeof(header), "high-priority-cores-count"); snprintf(value, sizeof(value), "%d", - bucket_info[j].high_priority_cores_count); + bucket_info[j].hp_cores); format_and_print(outf, base_level + 2, header, value); - - if (fact_avx & 0x01) { - snprintf(header, sizeof(header), - "high-priority-max-frequency(MHz)"); + for (i = 0; i < trl_max_levels; i++) { + if (fact_avx != 0xFF && !(fact_avx & (1 << i))) + continue; + if (i == 0) + snprintf(header, sizeof(header), + "high-priority-max-frequency(MHz)"); + else + snprintf(header, sizeof(header), + "high-priority-max-%s-frequency(MHz)", isst_get_trl_level_name(i)); snprintf(value, sizeof(value), "%d", - bucket_info[j].sse_trl * DISP_FREQ_MULTIPLIER); - format_and_print(outf, base_level + 2, header, value); - } - - if (fact_avx & 0x02) { - snprintf(header, sizeof(header), - "high-priority-max-avx2-frequency(MHz)"); - snprintf(value, sizeof(value), "%d", - bucket_info[j].avx_trl * DISP_FREQ_MULTIPLIER); - format_and_print(outf, base_level + 2, header, value); - } - - if (fact_avx & 0x04) { - snprintf(header, sizeof(header), - "high-priority-max-avx512-frequency(MHz)"); - snprintf(value, sizeof(value), "%d", - bucket_info[j].avx512_trl * - DISP_FREQ_MULTIPLIER); + bucket_info[j].hp_ratios[i] * DISP_FREQ_MULTIPLIER); format_and_print(outf, base_level + 2, header, value); } } snprintf(header, sizeof(header), "speed-select-turbo-freq-clip-frequencies"); format_and_print(outf, base_level + 1, header, NULL); - snprintf(header, sizeof(header), "low-priority-max-frequency(MHz)"); - snprintf(value, sizeof(value), "%d", - fact_info->lp_clipping_ratio_license_sse * - DISP_FREQ_MULTIPLIER); - format_and_print(outf, base_level + 2, header, value); - snprintf(header, sizeof(header), - "low-priority-max-avx2-frequency(MHz)"); - snprintf(value, sizeof(value), "%d", - fact_info->lp_clipping_ratio_license_avx2 * - DISP_FREQ_MULTIPLIER); - format_and_print(outf, base_level + 2, header, value); - snprintf(header, sizeof(header), - "low-priority-max-avx512-frequency(MHz)"); - snprintf(value, sizeof(value), "%d", - fact_info->lp_clipping_ratio_license_avx512 * - DISP_FREQ_MULTIPLIER); - format_and_print(outf, base_level + 2, header, value); + + for (j = 0; j < trl_max_levels; j++) { + /* No AVX level name for SSE to be consistent with previous formatting */ + if (j == 0) + snprintf(header, sizeof(header), "low-priority-max-frequency(MHz)"); + else + snprintf(header, sizeof(header), "low-priority-max-%s-frequency(MHz)", + isst_get_trl_level_name(j)); + snprintf(value, sizeof(value), "%d", + fact_info->lp_ratios[j] * DISP_FREQ_MULTIPLIER); + format_and_print(outf, base_level + 2, header, value); + } } void isst_ctdp_display_core_info(struct isst_id *id, FILE *outf, char *prefix, diff --git a/tools/power/x86/intel-speed-select/isst.h b/tools/power/x86/intel-speed-select/isst.h index 8f563ff300bb..727afc594109 100644 --- a/tools/power/x86/intel-speed-select/isst.h +++ b/tools/power/x86/intel-speed-select/isst.h @@ -98,10 +98,8 @@ struct isst_clos_config { }; struct isst_fact_bucket_info { - int high_priority_cores_count; - int sse_trl; - int avx_trl; - int avx512_trl; + int hp_cores; + int hp_ratios[TRL_MAX_LEVELS]; }; struct isst_pbf_info { @@ -119,9 +117,7 @@ struct isst_pbf_info { #define ISST_TRL_MAX_ACTIVE_CORES 8 #define ISST_FACT_MAX_BUCKETS 8 struct isst_fact_info { - int lp_clipping_ratio_license_sse; - int lp_clipping_ratio_license_avx2; - int lp_clipping_ratio_license_avx512; + int lp_ratios[TRL_MAX_LEVELS]; struct isst_fact_bucket_info bucket_info[ISST_FACT_MAX_BUCKETS]; }; From e157c8475e7c2d62d7b28983ea81167f3e6a5a57 Mon Sep 17 00:00:00 2001 From: Zhang Rui Date: Wed, 17 Aug 2022 14:51:40 +0800 Subject: [PATCH 081/178] tools/power/x86/intel-speed-select: Introduce punit to isst_id Punit id can also be retrieved from ISST_IF_GET_PHY_ID. punit id is unique within a Package/Die, and together with Package id and Die id, they can be used to refer to a specific SST power domain. For current platforms, Punit id is always Zero. So no functional changes are expected for the current platforms. While here, prevent issuing IOCTL if the file /dev/isst_interface can't be opened. Signed-off-by: Zhang Rui [srinivas.pandruvada@linux.intel.com: changelog edits] Signed-off-by: Srinivas Pandruvada --- .../x86/intel-speed-select/isst-config.c | 74 +++++++++++++------ tools/power/x86/intel-speed-select/isst.h | 6 +- 2 files changed, 57 insertions(+), 23 deletions(-) diff --git a/tools/power/x86/intel-speed-select/isst-config.c b/tools/power/x86/intel-speed-select/isst-config.c index 55d0a35df41c..f17492fcf97c 100644 --- a/tools/power/x86/intel-speed-select/isst-config.c +++ b/tools/power/x86/intel-speed-select/isst-config.c @@ -61,6 +61,7 @@ struct _cpu_map { unsigned short core_id; unsigned short pkg_id; unsigned short die_id; + unsigned short punit_id; unsigned short punit_cpu; unsigned short punit_cpu_core; unsigned short initialized; @@ -378,6 +379,17 @@ static int get_physical_die_id(int cpu) return ret; } +static int get_physical_punit_id(int cpu) +{ + if (cpu < 0) + return -1; + + if (cpu_map && cpu_map[cpu].initialized) + return cpu_map[cpu].punit_id; + + return -1; +} + void set_isst_id(struct isst_id *id, int cpu) { id->cpu = cpu; @@ -389,6 +401,10 @@ void set_isst_id(struct isst_id *id, int cpu) id->die = get_physical_die_id(cpu); if (id->die >= MAX_DIE_PER_PACKAGE) id->die = -1; + + id->punit = get_physical_punit_id(cpu); + if (id->punit >= MAX_PUNIT_PER_DIE) + id->punit = -1; } int is_cpu_in_power_domain(int cpu, struct isst_id *id) @@ -397,7 +413,7 @@ int is_cpu_in_power_domain(int cpu, struct isst_id *id) set_isst_id(&tid, cpu); - if (id->pkg == tid.pkg && id->die == tid.die) + if (id->pkg == tid.pkg && id->die == tid.die && id->punit == tid.punit) return 1; return 0; @@ -660,7 +676,7 @@ static void create_cpu_map(void) for (i = 0; i < topo_max_cpus; ++i) { char buffer[256]; - int pkg_id, die_id, core_id; + int pkg_id, die_id, core_id, punit_id; /* check if CPU is online */ snprintf(buffer, sizeof(buffer), @@ -682,31 +698,47 @@ static void create_cpu_map(void) cpu_map[i].pkg_id = pkg_id; cpu_map[i].die_id = die_id; cpu_map[i].core_id = core_id; + + + punit_id = 0; + + if (fd >= 0) { + map.cmd_count = 1; + map.cpu_map[0].logical_cpu = i; + debug_printf(" map logical_cpu:%d\n", + map.cpu_map[0].logical_cpu); + if (ioctl(fd, ISST_IF_GET_PHY_ID, &map) == -1) { + perror("ISST_IF_GET_PHY_ID"); + fprintf(outf, "Error: map logical_cpu:%d\n", + map.cpu_map[0].logical_cpu); + } else { + /* + * Format + * Bit 0 – thread ID + * Bit 8:1 – core ID + * Bit 13:9 – punit ID + */ + cpu_map[i].punit_cpu = map.cpu_map[0].physical_cpu & 0x1ff; + cpu_map[i].punit_cpu_core = (cpu_map[i].punit_cpu >> + 1); // shift to get core id + punit_id = (map.cpu_map[0].physical_cpu >> 9) & 0x1f; + + if (punit_id >= MAX_PUNIT_PER_DIE) + punit_id = 0; + + cpu_map[i].punit_id = punit_id; + } + } + cpu_map[i].initialized = 1; cpu_cnt[pkg_id][die_id]++; - if (fd < 0) - continue; - map.cmd_count = 1; - map.cpu_map[0].logical_cpu = i; - debug_printf(" map logical_cpu:%d\n", - map.cpu_map[0].logical_cpu); - if (ioctl(fd, ISST_IF_GET_PHY_ID, &map) == -1) { - perror("ISST_IF_GET_PHY_ID"); - fprintf(outf, "Error: map logical_cpu:%d\n", - map.cpu_map[0].logical_cpu); - continue; - } - cpu_map[i].punit_cpu = map.cpu_map[0].physical_cpu; - cpu_map[i].punit_cpu_core = (map.cpu_map[0].physical_cpu >> - 1); // shift to get core id - debug_printf( - "map logical_cpu:%d core: %d die:%d pkg:%d punit_cpu:%d punit_core:%d\n", + "map logical_cpu:%d core: %d die:%d pkg:%d punit:%d punit_cpu:%d punit_core:%d\n", i, cpu_map[i].core_id, cpu_map[i].die_id, - cpu_map[i].pkg_id, cpu_map[i].punit_cpu, - cpu_map[i].punit_cpu_core); + cpu_map[i].pkg_id, cpu_map[i].punit_id, + cpu_map[i].punit_cpu, cpu_map[i].punit_cpu_core); } if (fd >= 0) close(fd); diff --git a/tools/power/x86/intel-speed-select/isst.h b/tools/power/x86/intel-speed-select/isst.h index 727afc594109..148389c5ab84 100644 --- a/tools/power/x86/intel-speed-select/isst.h +++ b/tools/power/x86/intel-speed-select/isst.h @@ -79,14 +79,16 @@ #define DISP_FREQ_MULTIPLIER 100 -#define MAX_PACKAGE_COUNT 8 -#define MAX_DIE_PER_PACKAGE 2 +#define MAX_PACKAGE_COUNT 8 +#define MAX_DIE_PER_PACKAGE 2 +#define MAX_PUNIT_PER_DIE 8 /* Unified structure to specific a CPU or a Power Domain */ struct isst_id { int cpu; int pkg; int die; + int punit; }; struct isst_clos_config { From 57ef2436a1759ae9d9b4d1536aed4701bfb4f737 Mon Sep 17 00:00:00 2001 From: Zhang Rui Date: Wed, 17 Aug 2022 15:33:23 +0800 Subject: [PATCH 082/178] tools/power/x86/intel-speed-select: Introduce isst_is_punit_valid() Introduce isst_is_punit_valid() for checking a valid domain. For current platforms, it requires a punit 0 in a valid Package/Die. No functional changes are expected. Signed-off-by: Zhang Rui [srinivas.pandruvada@linux.intel.com: changelog edits] Signed-off-by: Srinivas Pandruvada --- tools/power/x86/intel-speed-select/isst-core.c | 11 +++++++++++ tools/power/x86/intel-speed-select/isst.h | 1 + 2 files changed, 12 insertions(+) diff --git a/tools/power/x86/intel-speed-select/isst-core.c b/tools/power/x86/intel-speed-select/isst-core.c index 8aa2ee4bb232..f712da2b5146 100644 --- a/tools/power/x86/intel-speed-select/isst-core.c +++ b/tools/power/x86/intel-speed-select/isst-core.c @@ -25,6 +25,17 @@ char *isst_get_trl_level_name(int level) } } +int isst_is_punit_valid(struct isst_id *id) +{ + if (id->cpu < 0) + return 0; + + if (id->pkg < 0 || id->die < 0 || id->punit) + return 0; + + return 1; +} + int isst_write_pm_config(struct isst_id *id, int cp_state) { unsigned int req, resp; diff --git a/tools/power/x86/intel-speed-select/isst.h b/tools/power/x86/intel-speed-select/isst.h index 148389c5ab84..ef884d83d388 100644 --- a/tools/power/x86/intel-speed-select/isst.h +++ b/tools/power/x86/intel-speed-select/isst.h @@ -205,6 +205,7 @@ extern int isst_send_msr_command(unsigned int cpu, unsigned int command, extern int isst_get_trl_max_levels(void); extern char *isst_get_trl_level_name(int level); +extern int isst_is_punit_valid(struct isst_id *id); extern int isst_get_ctdp_levels(struct isst_id *id, struct isst_pkg_ctdp *pkg_dev); extern int isst_get_ctdp_control(struct isst_id *id, int config_index, From b4edf3854a01fefe2eb2b4bb329ab97aaec5d5f5 Mon Sep 17 00:00:00 2001 From: Zhang Rui Date: Thu, 18 Aug 2022 01:06:45 +0800 Subject: [PATCH 083/178] tools/power/x86/intel-speed-select: Introduce support for multi-punit New platforms may have more than 1 punit in a Package/Die, thus it can have multiple power domains in a Package/Die. Package id and die id is not sufficient to refer to a specific Power domain. Introduce support for multi-punit per package/die. Signed-off-by: Zhang Rui [srinivas.pandruvada@linux.intel.com: changelog edits] Signed-off-by: Srinivas Pandruvada --- .../x86/intel-speed-select/isst-config.c | 74 ++++++++++--------- .../x86/intel-speed-select/isst-daemon.c | 21 +++--- 2 files changed, 52 insertions(+), 43 deletions(-) diff --git a/tools/power/x86/intel-speed-select/isst-config.c b/tools/power/x86/intel-speed-select/isst-config.c index f17492fcf97c..4c9623220e3b 100644 --- a/tools/power/x86/intel-speed-select/isst-config.c +++ b/tools/power/x86/intel-speed-select/isst-config.c @@ -502,46 +502,54 @@ void for_each_online_package_in_set(void (*callback)(struct isst_id *, void *, v void *arg1, void *arg2, void *arg3, void *arg4) { - int max_packages[MAX_PACKAGE_COUNT * MAX_PACKAGE_COUNT]; - int pkg_index = 0, i; struct isst_id id; + int cpus[MAX_PACKAGE_COUNT][MAX_DIE_PER_PACKAGE][MAX_PUNIT_PER_DIE]; + int valid_mask[MAX_PACKAGE_COUNT][MAX_DIE_PER_PACKAGE] = {0}; + int i, j, k; + + memset(cpus, -1, sizeof(cpus)); - memset(max_packages, 0xff, sizeof(max_packages)); for (i = 0; i < topo_max_cpus; ++i) { - int j, online, pkg_id, die_id = 0, skip = 0; + int online; if (!CPU_ISSET_S(i, present_cpumask_size, present_cpumask)) continue; - if (i) - online = parse_int_file( - 1, "/sys/devices/system/cpu/cpu%d/online", i); - else - online = - 1; /* online entry for CPU 0 needs some special configs */ - die_id = get_physical_die_id(i); - if (die_id < 0) - die_id = 0; + online = parse_int_file( + i != 0, "/sys/devices/system/cpu/cpu%d/online", i); + if (online < 0) + online = 1; /* online entry for CPU 0 needs some special configs */ - pkg_id = parse_int_file(0, - "/sys/devices/system/cpu/cpu%d/topology/physical_package_id", i); - if (pkg_id < 0) + if (!online) continue; - /* Create an unique id for package, die combination to store */ - pkg_id = (MAX_PACKAGE_COUNT * pkg_id + die_id); - - for (j = 0; j < pkg_index; ++j) { - if (max_packages[j] == pkg_id) { - skip = 1; - break; - } - } - set_isst_id(&id, i); - if (!skip && online && callback) { - callback(&id, arg1, arg2, arg3, arg4); - max_packages[pkg_index++] = pkg_id; + + if (id.pkg < 0 || id.die < 0 || id.punit < 0) + continue; + + valid_mask[id.pkg][id.die] = 1; + + if (cpus[id.pkg][id.die][id.punit] == -1) + cpus[id.pkg][id.die][id.punit] = i; + } + + for (i = 0; i < MAX_PACKAGE_COUNT; i++) { + for (j = 0; j < MAX_DIE_PER_PACKAGE; j++) { + /* + * Fix me: + * How to check a non-cpu die for a package/die with all cpu offlined? + */ + if (!valid_mask[i][j]) + continue; + for (k = 0; k < MAX_PUNIT_PER_DIE; k++) { + id.cpu = cpus[i][j][k]; + id.pkg = i; + id.die = j; + id.punit = k; + if (isst_is_punit_valid(&id)) + callback(&id, arg1, arg2, arg3, arg4); + } } } } @@ -626,7 +634,7 @@ void free_cpu_set(cpu_set_t *cpu_set) CPU_FREE(cpu_set); } -static int cpu_cnt[MAX_PACKAGE_COUNT][MAX_DIE_PER_PACKAGE]; +static int cpu_cnt[MAX_PACKAGE_COUNT][MAX_DIE_PER_PACKAGE][MAX_PUNIT_PER_DIE]; int get_max_punit_core_id(struct isst_id *id) { @@ -648,10 +656,10 @@ int get_max_punit_core_id(struct isst_id *id) int get_cpu_count(struct isst_id *id) { - if (id->pkg < 0 || id->die < 0) + if (id->pkg < 0 || id->die < 0 || id->punit < 0) return 0; - return cpu_cnt[id->pkg][id->die]; + return cpu_cnt[id->pkg][id->die][id->punit]; } static void create_cpu_map(void) @@ -732,7 +740,7 @@ static void create_cpu_map(void) cpu_map[i].initialized = 1; - cpu_cnt[pkg_id][die_id]++; + cpu_cnt[pkg_id][die_id][punit_id]++; debug_printf( "map logical_cpu:%d core: %d die:%d pkg:%d punit:%d punit_cpu:%d punit_core:%d\n", diff --git a/tools/power/x86/intel-speed-select/isst-daemon.c b/tools/power/x86/intel-speed-select/isst-daemon.c index c2290ef0e3af..cc0eb9886411 100644 --- a/tools/power/x86/intel-speed-select/isst-daemon.c +++ b/tools/power/x86/intel-speed-select/isst-daemon.c @@ -20,16 +20,17 @@ #include "isst.h" -static int per_package_levels_info[MAX_PACKAGE_COUNT][MAX_DIE_PER_PACKAGE]; -static time_t per_package_levels_tm[MAX_PACKAGE_COUNT][MAX_DIE_PER_PACKAGE]; +static int per_package_levels_info[MAX_PACKAGE_COUNT][MAX_DIE_PER_PACKAGE][MAX_PUNIT_PER_DIE]; +static time_t per_package_levels_tm[MAX_PACKAGE_COUNT][MAX_DIE_PER_PACKAGE][MAX_PUNIT_PER_DIE]; static void init_levels(void) { - int i, j; + int i, j, k; for (i = 0; i < MAX_PACKAGE_COUNT; ++i) for (j = 0; j < MAX_DIE_PER_PACKAGE; ++j) - per_package_levels_info[i][j] = -1; + for (k = 0; k < MAX_PUNIT_PER_DIE; ++k) + per_package_levels_info[i][j][k] = -1; } void process_level_change(struct isst_id *id) @@ -39,16 +40,16 @@ void process_level_change(struct isst_id *id) time_t tm; int ret; - if (id->pkg < 0 || id->die < 0) { + if (id->pkg < 0 || id->die < 0 || id->punit < 0) { debug_printf("Invalid package/die info for cpu:%d\n", id->cpu); return; } tm = time(NULL); - if (tm - per_package_levels_tm[id->pkg][id->die] < 2) + if (tm - per_package_levels_tm[id->pkg][id->die][id->punit] < 2) return; - per_package_levels_tm[id->pkg][id->die] = tm; + per_package_levels_tm[id->pkg][id->die][id->punit] = tm; ret = isst_get_ctdp_levels(id, &pkg_dev); if (ret) { @@ -64,14 +65,14 @@ void process_level_change(struct isst_id *id) return; } - if (per_package_levels_info[id->pkg][id->die] == pkg_dev.current_level) + if (per_package_levels_info[id->pkg][id->die][id->punit] == pkg_dev.current_level) return; debug_printf("**Config level change for cpu:%d pkg:%d die:%d from %d to %d\n", - id->cpu, id->pkg, id->die, per_package_levels_info[id->pkg][id->die], + id->cpu, id->pkg, id->die, per_package_levels_info[id->pkg][id->die][id->punit], pkg_dev.current_level); - per_package_levels_info[id->pkg][id->die] = pkg_dev.current_level; + per_package_levels_info[id->pkg][id->die][id->punit] = pkg_dev.current_level; ctdp_level.core_cpumask_size = alloc_cpu_set(&ctdp_level.core_cpumask); From c77a8d4af68d98d0d83aa68f90041b395213c589 Mon Sep 17 00:00:00 2001 From: Zhang Rui Date: Wed, 17 Aug 2022 15:50:43 +0800 Subject: [PATCH 084/178] tools/power/x86/intel-speed-select: Rename for_each_online_package_in_set for_each_online_package_in_set is actually used to invoke callback for each power domain. This is not a problem when there is a single power domain within a package/die, but it does not reflect the truth in multi-punit case. Rename for_each_online_package_in_set to for_each_online_power_domain_in_set. Signed-off-by: Zhang Rui Signed-off-by: Srinivas Pandruvada --- .../x86/intel-speed-select/isst-config.c | 26 +++++++++---------- .../x86/intel-speed-select/isst-daemon.c | 2 +- tools/power/x86/intel-speed-select/isst.h | 2 +- 3 files changed, 15 insertions(+), 15 deletions(-) diff --git a/tools/power/x86/intel-speed-select/isst-config.c b/tools/power/x86/intel-speed-select/isst-config.c index 4c9623220e3b..42eaabc153d9 100644 --- a/tools/power/x86/intel-speed-select/isst-config.c +++ b/tools/power/x86/intel-speed-select/isst-config.c @@ -497,7 +497,7 @@ static void force_all_cpus_online(void) unlink("/var/run/isst_cpu_topology.dat"); } -void for_each_online_package_in_set(void (*callback)(struct isst_id *, void *, void *, +void for_each_online_power_domain_in_set(void (*callback)(struct isst_id *, void *, void *, void *, void *), void *arg1, void *arg2, void *arg3, void *arg4) @@ -1150,7 +1150,7 @@ static void exec_on_get_ctdp_cpu(struct isst_id *id, void *arg1, void *arg2, voi exec_on_get_ctdp_cpu, isst_get_ctdp_##suffix, \ &ctdp, desc, &ctdp.object); \ else \ - for_each_online_package_in_set(exec_on_get_ctdp_cpu, \ + for_each_online_power_domain_in_set(exec_on_get_ctdp_cpu, \ isst_get_ctdp_##suffix, \ &ctdp, desc, \ &ctdp.object); \ @@ -1354,7 +1354,7 @@ static void dump_isst_config(int arg) if (max_target_cpus) for_each_online_target_cpu_in_set(fn, NULL, NULL, NULL, NULL); else - for_each_online_package_in_set(fn, NULL, NULL, NULL, NULL); + for_each_online_power_domain_in_set(fn, NULL, NULL, NULL, NULL); isst_ctdp_display_information_end(outf); } @@ -1465,7 +1465,7 @@ static void set_tdp_level(int arg) for_each_online_target_cpu_in_set(set_tdp_level_for_cpu, NULL, NULL, NULL, NULL); else - for_each_online_package_in_set(set_tdp_level_for_cpu, NULL, + for_each_online_power_domain_in_set(set_tdp_level_for_cpu, NULL, NULL, NULL, NULL); isst_ctdp_display_information_end(outf); } @@ -1534,7 +1534,7 @@ static void dump_pbf_config(int arg) if (max_target_cpus) for_each_online_target_cpu_in_set(fn, NULL, NULL, NULL, NULL); else - for_each_online_package_in_set(fn, NULL, NULL, NULL, NULL); + for_each_online_power_domain_in_set(fn, NULL, NULL, NULL, NULL); isst_ctdp_display_information_end(outf); } @@ -1940,7 +1940,7 @@ static void set_pbf_enable(int arg) for_each_online_target_cpu_in_set(set_pbf_for_cpu, NULL, NULL, NULL, &enable); else - for_each_online_package_in_set(set_pbf_for_cpu, NULL, NULL, + for_each_online_power_domain_in_set(set_pbf_for_cpu, NULL, NULL, NULL, &enable); isst_ctdp_display_information_end(outf); } @@ -1986,7 +1986,7 @@ static void dump_fact_config(int arg) for_each_online_target_cpu_in_set(dump_fact_config_for_cpu, NULL, NULL, NULL, NULL); else - for_each_online_package_in_set(dump_fact_config_for_cpu, NULL, + for_each_online_power_domain_in_set(dump_fact_config_for_cpu, NULL, NULL, NULL, NULL); isst_ctdp_display_information_end(outf); } @@ -2095,7 +2095,7 @@ static void set_fact_enable(int arg) for_each_online_target_cpu_in_set(set_fact_for_cpu, NULL, NULL, NULL, &enable); else - for_each_online_package_in_set(set_fact_for_cpu, NULL, NULL, + for_each_online_power_domain_in_set(set_fact_for_cpu, NULL, NULL, NULL, &enable); isst_ctdp_display_information_end(outf); @@ -2234,7 +2234,7 @@ static void set_clos_enable(int arg) for_each_online_target_cpu_in_set(enable_clos_qos_config, NULL, NULL, NULL, &enable); else - for_each_online_package_in_set(enable_clos_qos_config, NULL, + for_each_online_power_domain_in_set(enable_clos_qos_config, NULL, NULL, NULL, &enable); isst_ctdp_display_information_end(outf); } @@ -2273,7 +2273,7 @@ static void dump_clos_config(int arg) for_each_online_target_cpu_in_set(dump_clos_config_for_cpu, NULL, NULL, NULL, NULL); else - for_each_online_package_in_set(dump_clos_config_for_cpu, NULL, + for_each_online_power_domain_in_set(dump_clos_config_for_cpu, NULL, NULL, NULL, NULL); isst_ctdp_display_information_end(outf); } @@ -2309,7 +2309,7 @@ static void dump_clos_info(int arg) for_each_online_target_cpu_in_set(get_clos_info_for_cpu, NULL, NULL, NULL, NULL); else - for_each_online_package_in_set(get_clos_info_for_cpu, NULL, + for_each_online_power_domain_in_set(get_clos_info_for_cpu, NULL, NULL, NULL, NULL); isst_ctdp_display_information_end(outf); @@ -2381,7 +2381,7 @@ static void set_clos_config(int arg) for_each_online_target_cpu_in_set(set_clos_config_for_cpu, NULL, NULL, NULL, NULL); else - for_each_online_package_in_set(set_clos_config_for_cpu, NULL, + for_each_online_power_domain_in_set(set_clos_config_for_cpu, NULL, NULL, NULL, NULL); isst_ctdp_display_information_end(outf); } @@ -2548,7 +2548,7 @@ static void process_trl(int arg) for_each_online_target_cpu_in_set(get_set_trl, NULL, NULL, NULL, &arg); else - for_each_online_package_in_set(get_set_trl, NULL, + for_each_online_power_domain_in_set(get_set_trl, NULL, NULL, NULL, &arg); isst_ctdp_display_information_end(outf); } diff --git a/tools/power/x86/intel-speed-select/isst-daemon.c b/tools/power/x86/intel-speed-select/isst-daemon.c index cc0eb9886411..6fa2942b5e12 100644 --- a/tools/power/x86/intel-speed-select/isst-daemon.c +++ b/tools/power/x86/intel-speed-select/isst-daemon.c @@ -109,7 +109,7 @@ static void _poll_for_config_change(struct isst_id *id, void *arg1, void *arg2, static void poll_for_config_change(void) { - for_each_online_package_in_set(_poll_for_config_change, NULL, NULL, + for_each_online_power_domain_in_set(_poll_for_config_change, NULL, NULL, NULL, NULL); } diff --git a/tools/power/x86/intel-speed-select/isst.h b/tools/power/x86/intel-speed-select/isst.h index ef884d83d388..c43a73751007 100644 --- a/tools/power/x86/intel-speed-select/isst.h +++ b/tools/power/x86/intel-speed-select/isst.h @@ -271,7 +271,7 @@ extern int is_icx_platform(void); extern void isst_trl_display_information(struct isst_id *id, FILE *outf, unsigned long long trl); extern void set_cpu_online_offline(int cpu, int state); -extern void for_each_online_package_in_set(void (*callback)(struct isst_id *, void *, void *, +extern void for_each_online_power_domain_in_set(void (*callback)(struct isst_id *, void *, void *, void *, void *), void *arg1, void *arg2, void *arg3, void *arg4); From ad7e17fcb1261a4445f973369b610c972098e87f Mon Sep 17 00:00:00 2001 From: Zhang Rui Date: Wed, 17 Aug 2022 16:11:13 +0800 Subject: [PATCH 085/178] tools/power/x86/intel-speed-select: Improve isst_print_extended_platform_info The main thing done in isst_print_extended_platform_info is to get the isst feature status by checking one of the power domains of the platform. This can be done using the for_each_online_power_domain_in_set() function, which makes the code clean and easier to read. No functional changes are expected. Signed-off-by: Zhang Rui [srinivas.pandruvada@linux.intel.com: changelog edits] Signed-off-by: Srinivas Pandruvada --- .../x86/intel-speed-select/isst-config.c | 93 +++++++++++-------- 1 file changed, 53 insertions(+), 40 deletions(-) diff --git a/tools/power/x86/intel-speed-select/isst-config.c b/tools/power/x86/intel-speed-select/isst-config.c index 42eaabc153d9..ef604c46cda7 100644 --- a/tools/power/x86/intel-speed-select/isst-config.c +++ b/tools/power/x86/intel-speed-select/isst-config.c @@ -1002,74 +1002,87 @@ static int isst_fill_platform_info(void) return 0; } -static void isst_print_extended_platform_info(void) +void get_isst_status(struct isst_id *id, void *arg1, void *arg2, void *arg3, void *arg4) { - int cp_state, cp_cap, fact_support = 0, pbf_support = 0; - struct isst_pkg_ctdp_level_info ctdp_level; struct isst_pkg_ctdp pkg_dev; - int ret, i, j; - FILE *filep; - struct isst_id id; + struct isst_id *tid = (struct isst_id *)arg2; + int *mask = (int *)arg3; + int *max_level = (int *)arg4; + int j, ret; - for (i = 0; i < 256; ++i) { - char path[256]; - - snprintf(path, sizeof(path), - "/sys/devices/system/cpu/cpu%d/topology/thread_siblings", i); - filep = fopen(path, "r"); - if (filep) - break; - } - - if (!filep) + /* Only check the first cpu power domain */ + if (id->cpu < 0 || tid->cpu >= 0) return; - fclose(filep); - - set_isst_id(&id, i); - ret = isst_get_ctdp_levels(&id, &pkg_dev); + ret = isst_get_ctdp_levels(id, &pkg_dev); if (ret) return; - if (pkg_dev.enabled) { + if (pkg_dev.enabled) + *mask |= BIT(0); + + if (pkg_dev.locked) + *mask |= BIT(1); + + if (*max_level < pkg_dev.levels) + *max_level = pkg_dev.levels; + + for (j = 0; j <= pkg_dev.levels; ++j) { + struct isst_pkg_ctdp_level_info ctdp_level; + + ret = isst_get_ctdp_control(id, j, &ctdp_level); + if (ret) + continue; + + if (ctdp_level.fact_support) + *mask |= BIT(2); + + if (ctdp_level.pbf_support) + *mask |= BIT(3); + } + + tid->cpu = id->cpu; + tid->pkg = id->pkg; + tid->die = id->die; + tid->punit = id->punit; +} + +static void isst_print_extended_platform_info(void) +{ + int cp_state, cp_cap; + struct isst_id id; + int mask = 0, max_level = 0; + + id.cpu = -1; + for_each_online_power_domain_in_set(get_isst_status, NULL, &id, &mask, &max_level); + + if (mask & BIT(0)) { fprintf(outf, "Intel(R) SST-PP (feature perf-profile) is supported\n"); } else { fprintf(outf, "Intel(R) SST-PP (feature perf-profile) is not supported\n"); fprintf(outf, "Only performance level 0 (base level) is present\n"); } - if (pkg_dev.locked) + if (mask & BIT(1)) fprintf(outf, "TDP level change control is locked\n"); else - fprintf(outf, "TDP level change control is unlocked, max level: %d \n", pkg_dev.levels); + fprintf(outf, "TDP level change control is unlocked, max level: %d\n", max_level); - for (j = 0; j <= pkg_dev.levels; ++j) { - ret = isst_get_ctdp_control(&id, j, &ctdp_level); - if (ret) - continue; - - if (!fact_support && ctdp_level.fact_support) - fact_support = 1; - - if (!pbf_support && ctdp_level.pbf_support) - pbf_support = 1; - } - - if (fact_support) + if (mask & BIT(2)) fprintf(outf, "Intel(R) SST-TF (feature turbo-freq) is supported\n"); else fprintf(outf, "Intel(R) SST-TF (feature turbo-freq) is not supported\n"); - if (pbf_support) + if (mask & BIT(3)) fprintf(outf, "Intel(R) SST-BF (feature base-freq) is supported\n"); else fprintf(outf, "Intel(R) SST-BF (feature base-freq) is not supported\n"); - ret = isst_read_pm_config(&id, &cp_state, &cp_cap); - if (ret) { + if (isst_read_pm_config(&id, &cp_state, &cp_cap)) { fprintf(outf, "Intel(R) SST-CP (feature core-power) status is unknown\n"); return; } + if (cp_cap) fprintf(outf, "Intel(R) SST-CP (feature core-power) is supported\n"); else From 2042c0abf050ce9de508c9efda891de496240708 Mon Sep 17 00:00:00 2001 From: Zhang Rui Date: Sat, 20 Aug 2022 18:38:11 +0800 Subject: [PATCH 086/178] tools/power/x86/intel-speed-select: Move mbox functions to isst-core.c isst-config.c should only contain generic code. Move mbox functions which are platform specific code to isst-core.c. As there are some platform specific parameters set via generic application options, introduce isst_update_platform_param to pass these parameters to platform specific code. Signed-off-by: Zhang Rui Signed-off-by: Srinivas Pandruvada --- .../x86/intel-speed-select/isst-config.c | 185 +--------------- .../power/x86/intel-speed-select/isst-core.c | 198 ++++++++++++++++++ tools/power/x86/intel-speed-select/isst.h | 7 +- 3 files changed, 209 insertions(+), 181 deletions(-) diff --git a/tools/power/x86/intel-speed-select/isst-config.c b/tools/power/x86/intel-speed-select/isst-config.c index ef604c46cda7..f2da55ea7364 100644 --- a/tools/power/x86/intel-speed-select/isst-config.c +++ b/tools/power/x86/intel-speed-select/isst-config.c @@ -45,9 +45,6 @@ static int force_online_offline; static int auto_mode; static int fact_enable_fail; -static int mbox_delay; -static int mbox_retries = 3; - /* clos related */ static int current_clos = -1; static int clos_epp = -1; @@ -799,184 +796,7 @@ int find_phy_core_num(int logical_cpu) return -EINVAL; } -static int isst_send_mmio_command(unsigned int cpu, unsigned int reg, int write, - unsigned int *value) -{ - struct isst_if_io_regs io_regs; - const char *pathname = "/dev/isst_interface"; - int cmd; - int fd; - debug_printf("mmio_cmd cpu:%d reg:%d write:%d\n", cpu, reg, write); - - fd = open(pathname, O_RDWR); - if (fd < 0) - err(-1, "%s open failed", pathname); - - io_regs.req_count = 1; - io_regs.io_reg[0].logical_cpu = cpu; - io_regs.io_reg[0].reg = reg; - cmd = ISST_IF_IO_CMD; - if (write) { - io_regs.io_reg[0].read_write = 1; - io_regs.io_reg[0].value = *value; - } else { - io_regs.io_reg[0].read_write = 0; - } - - if (ioctl(fd, cmd, &io_regs) == -1) { - if (errno == ENOTTY) { - perror("ISST_IF_IO_COMMAND\n"); - fprintf(stderr, "Check presence of kernel modules: isst_if_mmio\n"); - exit(0); - } - fprintf(outf, "Error: mmio_cmd cpu:%d reg:%x read_write:%x\n", - cpu, reg, write); - } else { - if (!write) - *value = io_regs.io_reg[0].value; - - debug_printf( - "mmio_cmd response: cpu:%d reg:%x rd_write:%x resp:%x\n", - cpu, reg, write, *value); - } - - close(fd); - - return 0; -} - -int isst_send_mbox_command(unsigned int cpu, unsigned char command, - unsigned char sub_command, unsigned int parameter, - unsigned int req_data, unsigned int *resp) -{ - const char *pathname = "/dev/isst_interface"; - int fd, retry; - struct isst_if_mbox_cmds mbox_cmds = { 0 }; - - debug_printf( - "mbox_send: cpu:%d command:%x sub_command:%x parameter:%x req_data:%x\n", - cpu, command, sub_command, parameter, req_data); - - if (!is_skx_based_platform() && command == CONFIG_CLOS && - sub_command != CLOS_PM_QOS_CONFIG) { - unsigned int value; - int write = 0; - int clos_id, core_id, ret = 0; - - debug_printf("CPU %d\n", cpu); - - if (parameter & BIT(MBOX_CMD_WRITE_BIT)) { - value = req_data; - write = 1; - } - - switch (sub_command) { - case CLOS_PQR_ASSOC: - core_id = parameter & 0xff; - ret = isst_send_mmio_command( - cpu, PQR_ASSOC_OFFSET + core_id * 4, write, - &value); - if (!ret && !write) - *resp = value; - break; - case CLOS_PM_CLOS: - clos_id = parameter & 0x03; - ret = isst_send_mmio_command( - cpu, PM_CLOS_OFFSET + clos_id * 4, write, - &value); - if (!ret && !write) - *resp = value; - break; - case CLOS_STATUS: - break; - default: - break; - } - return ret; - } - - mbox_cmds.cmd_count = 1; - mbox_cmds.mbox_cmd[0].logical_cpu = cpu; - mbox_cmds.mbox_cmd[0].command = command; - mbox_cmds.mbox_cmd[0].sub_command = sub_command; - mbox_cmds.mbox_cmd[0].parameter = parameter; - mbox_cmds.mbox_cmd[0].req_data = req_data; - - if (mbox_delay) - usleep(mbox_delay * 1000); - - fd = open(pathname, O_RDWR); - if (fd < 0) - err(-1, "%s open failed", pathname); - - retry = mbox_retries; - - do { - if (ioctl(fd, ISST_IF_MBOX_COMMAND, &mbox_cmds) == -1) { - if (errno == ENOTTY) { - perror("ISST_IF_MBOX_COMMAND\n"); - fprintf(stderr, "Check presence of kernel modules: isst_if_mbox_pci or isst_if_mbox_msr\n"); - exit(0); - } - debug_printf( - "Error: mbox_cmd cpu:%d command:%x sub_command:%x parameter:%x req_data:%x errorno:%d\n", - cpu, command, sub_command, parameter, req_data, errno); - --retry; - } else { - *resp = mbox_cmds.mbox_cmd[0].resp_data; - debug_printf( - "mbox_cmd response: cpu:%d command:%x sub_command:%x parameter:%x req_data:%x resp:%x\n", - cpu, command, sub_command, parameter, req_data, *resp); - break; - } - } while (retry); - - close(fd); - - if (!retry) { - debug_printf("Failed mbox command even after retries\n"); - return -1; - - } - return 0; -} - -int isst_send_msr_command(unsigned int cpu, unsigned int msr, int write, - unsigned long long *req_resp) -{ - struct isst_if_msr_cmds msr_cmds; - const char *pathname = "/dev/isst_interface"; - int fd; - - fd = open(pathname, O_RDWR); - if (fd < 0) - err(-1, "%s open failed", pathname); - - msr_cmds.cmd_count = 1; - msr_cmds.msr_cmd[0].logical_cpu = cpu; - msr_cmds.msr_cmd[0].msr = msr; - msr_cmds.msr_cmd[0].read_write = write; - if (write) - msr_cmds.msr_cmd[0].data = *req_resp; - - if (ioctl(fd, ISST_IF_MSR_COMMAND, &msr_cmds) == -1) { - perror("ISST_IF_MSR_COMMAND"); - fprintf(outf, "Error: msr_cmd cpu:%d msr:%x read_write:%d\n", - cpu, msr, write); - } else { - if (!write) - *req_resp = msr_cmds.msr_cmd[0].data; - - debug_printf( - "msr_cmd response: cpu:%d msr:%x rd_write:%x resp:%llx %llx\n", - cpu, msr, write, *req_resp, msr_cmds.msr_cmd[0].data); - } - - close(fd); - - return 0; -} static int isst_fill_platform_info(void) { @@ -2971,6 +2791,7 @@ static void cmdline(int argc, char **argv) int oob_mode = 0; int poll_interval = -1; int no_daemon = 0; + int mbox_delay = 0, mbox_retries = 3; static struct option long_options[] = { { "all-cpus-online", no_argument, 0, 'a' }, @@ -3080,6 +2901,10 @@ static void cmdline(int argc, char **argv) usage(); exit(0); } + + isst_update_platform_param(ISST_PARAM_MBOX_DELAY, mbox_delay); + isst_update_platform_param(ISST_PARAM_MBOX_RETRIES, mbox_retries); + set_max_cpu_num(); if (force_cpus_online) force_all_cpus_online(); diff --git a/tools/power/x86/intel-speed-select/isst-core.c b/tools/power/x86/intel-speed-select/isst-core.c index f712da2b5146..385bc6c05545 100644 --- a/tools/power/x86/intel-speed-select/isst-core.c +++ b/tools/power/x86/intel-speed-select/isst-core.c @@ -6,6 +6,23 @@ #include "isst.h" +static int mbox_delay; +static int mbox_retries = 3; + +void isst_update_platform_param(enum isst_platform_param param, int value) +{ + switch (param) { + case ISST_PARAM_MBOX_DELAY: + mbox_delay = value; + break; + case ISST_PARAM_MBOX_RETRIES: + mbox_retries = value; + break; + default: + break; + } +} + int isst_get_trl_max_levels(void) { return 3; @@ -36,6 +53,187 @@ int isst_is_punit_valid(struct isst_id *id) return 1; } +static int isst_send_mmio_command(unsigned int cpu, unsigned int reg, int write, + unsigned int *value) +{ + struct isst_if_io_regs io_regs; + const char *pathname = "/dev/isst_interface"; + int cmd; + FILE *outf = get_output_file(); + int fd; + + debug_printf("mmio_cmd cpu:%d reg:%d write:%d\n", cpu, reg, write); + + fd = open(pathname, O_RDWR); + if (fd < 0) + err(-1, "%s open failed", pathname); + + io_regs.req_count = 1; + io_regs.io_reg[0].logical_cpu = cpu; + io_regs.io_reg[0].reg = reg; + cmd = ISST_IF_IO_CMD; + if (write) { + io_regs.io_reg[0].read_write = 1; + io_regs.io_reg[0].value = *value; + } else { + io_regs.io_reg[0].read_write = 0; + } + + if (ioctl(fd, cmd, &io_regs) == -1) { + if (errno == ENOTTY) { + perror("ISST_IF_IO_COMMAND\n"); + fprintf(stderr, "Check presence of kernel modules: isst_if_mmio\n"); + exit(0); + } + fprintf(outf, "Error: mmio_cmd cpu:%d reg:%x read_write:%x\n", + cpu, reg, write); + } else { + if (!write) + *value = io_regs.io_reg[0].value; + + debug_printf( + "mmio_cmd response: cpu:%d reg:%x rd_write:%x resp:%x\n", + cpu, reg, write, *value); + } + + close(fd); + + return 0; +} + +int isst_send_mbox_command(unsigned int cpu, unsigned char command, + unsigned char sub_command, unsigned int parameter, + unsigned int req_data, unsigned int *resp) +{ + const char *pathname = "/dev/isst_interface"; + int fd, retry; + struct isst_if_mbox_cmds mbox_cmds = { 0 }; + + debug_printf( + "mbox_send: cpu:%d command:%x sub_command:%x parameter:%x req_data:%x\n", + cpu, command, sub_command, parameter, req_data); + + if (!is_skx_based_platform() && command == CONFIG_CLOS && + sub_command != CLOS_PM_QOS_CONFIG) { + unsigned int value; + int write = 0; + int clos_id, core_id, ret = 0; + + debug_printf("CPU %d\n", cpu); + + if (parameter & BIT(MBOX_CMD_WRITE_BIT)) { + value = req_data; + write = 1; + } + + switch (sub_command) { + case CLOS_PQR_ASSOC: + core_id = parameter & 0xff; + ret = isst_send_mmio_command( + cpu, PQR_ASSOC_OFFSET + core_id * 4, write, + &value); + if (!ret && !write) + *resp = value; + break; + case CLOS_PM_CLOS: + clos_id = parameter & 0x03; + ret = isst_send_mmio_command( + cpu, PM_CLOS_OFFSET + clos_id * 4, write, + &value); + if (!ret && !write) + *resp = value; + break; + case CLOS_STATUS: + break; + default: + break; + } + return ret; + } + + mbox_cmds.cmd_count = 1; + mbox_cmds.mbox_cmd[0].logical_cpu = cpu; + mbox_cmds.mbox_cmd[0].command = command; + mbox_cmds.mbox_cmd[0].sub_command = sub_command; + mbox_cmds.mbox_cmd[0].parameter = parameter; + mbox_cmds.mbox_cmd[0].req_data = req_data; + + if (mbox_delay) + usleep(mbox_delay * 1000); + + fd = open(pathname, O_RDWR); + if (fd < 0) + err(-1, "%s open failed", pathname); + + retry = mbox_retries; + + do { + if (ioctl(fd, ISST_IF_MBOX_COMMAND, &mbox_cmds) == -1) { + if (errno == ENOTTY) { + perror("ISST_IF_MBOX_COMMAND\n"); + fprintf(stderr, "Check presence of kernel modules: isst_if_mbox_pci or isst_if_mbox_msr\n"); + exit(0); + } + debug_printf( + "Error: mbox_cmd cpu:%d command:%x sub_command:%x parameter:%x req_data:%x errorno:%d\n", + cpu, command, sub_command, parameter, req_data, errno); + --retry; + } else { + *resp = mbox_cmds.mbox_cmd[0].resp_data; + debug_printf( + "mbox_cmd response: cpu:%d command:%x sub_command:%x parameter:%x req_data:%x resp:%x\n", + cpu, command, sub_command, parameter, req_data, *resp); + break; + } + } while (retry); + + close(fd); + + if (!retry) { + debug_printf("Failed mbox command even after retries\n"); + return -1; + + } + return 0; +} + +int isst_send_msr_command(unsigned int cpu, unsigned int msr, int write, + unsigned long long *req_resp) +{ + struct isst_if_msr_cmds msr_cmds; + const char *pathname = "/dev/isst_interface"; + FILE *outf = get_output_file(); + int fd; + + fd = open(pathname, O_RDWR); + if (fd < 0) + err(-1, "%s open failed", pathname); + + msr_cmds.cmd_count = 1; + msr_cmds.msr_cmd[0].logical_cpu = cpu; + msr_cmds.msr_cmd[0].msr = msr; + msr_cmds.msr_cmd[0].read_write = write; + if (write) + msr_cmds.msr_cmd[0].data = *req_resp; + + if (ioctl(fd, ISST_IF_MSR_COMMAND, &msr_cmds) == -1) { + perror("ISST_IF_MSR_COMMAND"); + fprintf(outf, "Error: msr_cmd cpu:%d msr:%x read_write:%d\n", + cpu, msr, write); + } else { + if (!write) + *req_resp = msr_cmds.msr_cmd[0].data; + + debug_printf( + "msr_cmd response: cpu:%d msr:%x rd_write:%x resp:%llx %llx\n", + cpu, msr, write, *req_resp, msr_cmds.msr_cmd[0].data); + } + + close(fd); + + return 0; +} + int isst_write_pm_config(struct isst_id *id, int cp_state) { unsigned int req, resp; diff --git a/tools/power/x86/intel-speed-select/isst.h b/tools/power/x86/intel-speed-select/isst.h index c43a73751007..6f60fcf409ee 100644 --- a/tools/power/x86/intel-speed-select/isst.h +++ b/tools/power/x86/intel-speed-select/isst.h @@ -176,6 +176,11 @@ struct isst_pkg_ctdp { struct isst_pkg_ctdp_level_info ctdp_level[ISST_MAX_TDP_LEVELS]; }; +enum isst_platform_param { + ISST_PARAM_MBOX_DELAY, + ISST_PARAM_MBOX_RETRIES, +}; + extern int is_cpu_in_power_domain(int cpu, struct isst_id *id); extern int get_topo_max_cpus(void); extern int get_cpu_count(struct isst_id *id); @@ -194,7 +199,6 @@ extern void set_cpu_mask_from_punit_coremask(struct isst_id *id, size_t core_cpumask_size, cpu_set_t *core_cpumask, int *cpu_cnt); - extern int isst_send_mbox_command(unsigned int cpu, unsigned char command, unsigned char sub_command, unsigned int write, @@ -203,6 +207,7 @@ extern int isst_send_mbox_command(unsigned int cpu, unsigned char command, extern int isst_send_msr_command(unsigned int cpu, unsigned int command, int write, unsigned long long *req_resp); +extern void isst_update_platform_param(enum isst_platform_param, int vale); extern int isst_get_trl_max_levels(void); extern char *isst_get_trl_level_name(int level); extern int isst_is_punit_valid(struct isst_id *id); From 13b868f8928c947cbf51d167f1727c446beb9933 Mon Sep 17 00:00:00 2001 From: Zhang Rui Date: Sat, 20 Aug 2022 18:44:59 +0800 Subject: [PATCH 087/178] tools/power/x86/intel-speed-select: Introduce isst_get_disp_freq_multiplier Remove hardcoded DISP_FREQ_MULTIPLIER in the code and use isst_get_disp_freq_multiplier() instead. No functional changes are expected. Signed-off-by: Zhang Rui Signed-off-by: Srinivas Pandruvada --- .../x86/intel-speed-select/isst-config.c | 6 ++-- .../power/x86/intel-speed-select/isst-core.c | 5 ++++ .../x86/intel-speed-select/isst-display.c | 28 +++++++++---------- tools/power/x86/intel-speed-select/isst.h | 1 + 4 files changed, 23 insertions(+), 17 deletions(-) diff --git a/tools/power/x86/intel-speed-select/isst-config.c b/tools/power/x86/intel-speed-select/isst-config.c index f2da55ea7364..806533fd4626 100644 --- a/tools/power/x86/intel-speed-select/isst-config.c +++ b/tools/power/x86/intel-speed-select/isst-config.c @@ -2556,7 +2556,7 @@ static void parse_cmd_args(int argc, int start, char **argv) break; case 'd': clos_desired = atoi(optarg); - clos_desired /= DISP_FREQ_MULTIPLIER; + clos_desired /= isst_get_disp_freq_multiplier(); break; case 'e': clos_epp = atoi(optarg); @@ -2567,11 +2567,11 @@ static void parse_cmd_args(int argc, int start, char **argv) break; case 'n': clos_min = atoi(optarg); - clos_min /= DISP_FREQ_MULTIPLIER; + clos_min /= isst_get_disp_freq_multiplier(); break; case 'm': clos_max = atoi(optarg); - clos_max /= DISP_FREQ_MULTIPLIER; + clos_max /= isst_get_disp_freq_multiplier(); break; case 'p': clos_priority_type = atoi(optarg); diff --git a/tools/power/x86/intel-speed-select/isst-core.c b/tools/power/x86/intel-speed-select/isst-core.c index 385bc6c05545..8d63db2bf07c 100644 --- a/tools/power/x86/intel-speed-select/isst-core.c +++ b/tools/power/x86/intel-speed-select/isst-core.c @@ -23,6 +23,11 @@ void isst_update_platform_param(enum isst_platform_param param, int value) } } +int isst_get_disp_freq_multiplier(void) +{ + return DISP_FREQ_MULTIPLIER; +} + int isst_get_trl_max_levels(void) { return 3; diff --git a/tools/power/x86/intel-speed-select/isst-display.c b/tools/power/x86/intel-speed-select/isst-display.c index 5e57623196fc..af97ba5dbcc2 100644 --- a/tools/power/x86/intel-speed-select/isst-display.c +++ b/tools/power/x86/intel-speed-select/isst-display.c @@ -198,7 +198,7 @@ static void _isst_pbf_display_information(struct isst_id *id, FILE *outf, int le snprintf(header, sizeof(header), "high-priority-base-frequency(MHz)"); snprintf(value, sizeof(value), "%d", - pbf_info->p1_high * DISP_FREQ_MULTIPLIER); + pbf_info->p1_high * isst_get_disp_freq_multiplier()); format_and_print(outf, disp_level + 1, header, value); snprintf(header, sizeof(header), "high-priority-cpu-mask"); @@ -214,7 +214,7 @@ static void _isst_pbf_display_information(struct isst_id *id, FILE *outf, int le snprintf(header, sizeof(header), "low-priority-base-frequency(MHz)"); snprintf(value, sizeof(value), "%d", - pbf_info->p1_low * DISP_FREQ_MULTIPLIER); + pbf_info->p1_low * isst_get_disp_freq_multiplier()); format_and_print(outf, disp_level + 1, header, value); if (is_clx_n_platform()) @@ -282,7 +282,7 @@ static void _isst_fact_display_information(struct isst_id *id, FILE *outf, int l snprintf(header, sizeof(header), "high-priority-max-%s-frequency(MHz)", isst_get_trl_level_name(i)); snprintf(value, sizeof(value), "%d", - bucket_info[j].hp_ratios[i] * DISP_FREQ_MULTIPLIER); + bucket_info[j].hp_ratios[i] * isst_get_disp_freq_multiplier()); format_and_print(outf, base_level + 2, header, value); } } @@ -298,7 +298,7 @@ static void _isst_fact_display_information(struct isst_id *id, FILE *outf, int l snprintf(header, sizeof(header), "low-priority-max-%s-frequency(MHz)", isst_get_trl_level_name(j)); snprintf(value, sizeof(value), "%d", - fact_info->lp_ratios[j] * DISP_FREQ_MULTIPLIER); + fact_info->lp_ratios[j] * isst_get_disp_freq_multiplier()); format_and_print(outf, base_level + 2, header, value); } } @@ -393,41 +393,41 @@ void isst_ctdp_display_information(struct isst_id *id, FILE *outf, int tdp_level if (!ctdp_level->sse_p1) ctdp_level->sse_p1 = ctdp_level->tdp_ratio; snprintf(value, sizeof(value), "%d", - ctdp_level->sse_p1 * DISP_FREQ_MULTIPLIER); + ctdp_level->sse_p1 * isst_get_disp_freq_multiplier()); format_and_print(outf, level + 2, header, value); if (ctdp_level->avx2_p1) { snprintf(header, sizeof(header), "base-frequency-avx2(MHz)"); snprintf(value, sizeof(value), "%d", - ctdp_level->avx2_p1 * DISP_FREQ_MULTIPLIER); + ctdp_level->avx2_p1 * isst_get_disp_freq_multiplier()); format_and_print(outf, level + 2, header, value); } if (ctdp_level->avx512_p1) { snprintf(header, sizeof(header), "base-frequency-avx512(MHz)"); snprintf(value, sizeof(value), "%d", - ctdp_level->avx512_p1 * DISP_FREQ_MULTIPLIER); + ctdp_level->avx512_p1 * isst_get_disp_freq_multiplier()); format_and_print(outf, level + 2, header, value); } if (ctdp_level->uncore_pm) { snprintf(header, sizeof(header), "uncore-frequency-min(MHz)"); snprintf(value, sizeof(value), "%d", - ctdp_level->uncore_pm * DISP_FREQ_MULTIPLIER); + ctdp_level->uncore_pm * isst_get_disp_freq_multiplier()); format_and_print(outf, level + 2, header, value); } if (ctdp_level->uncore_p0) { snprintf(header, sizeof(header), "uncore-frequency-max(MHz)"); snprintf(value, sizeof(value), "%d", - ctdp_level->uncore_p0 * DISP_FREQ_MULTIPLIER); + ctdp_level->uncore_p0 * isst_get_disp_freq_multiplier()); format_and_print(outf, level + 2, header, value); } if (ctdp_level->uncore_p1) { snprintf(header, sizeof(header), "uncore-frequency-base(MHz)"); snprintf(value, sizeof(value), "%d", - ctdp_level->uncore_p1 * DISP_FREQ_MULTIPLIER); + ctdp_level->uncore_p1 * isst_get_disp_freq_multiplier()); format_and_print(outf, level + 2, header, value); } @@ -506,7 +506,7 @@ void isst_ctdp_display_information(struct isst_id *id, FILE *outf, int tdp_level format_and_print(outf, level + 4, header, value); snprintf(header, sizeof(header), "max-turbo-frequency(MHz)"); - snprintf(value, sizeof(value), "%d", ctdp_level->trl_ratios[k][j] * DISP_FREQ_MULTIPLIER); + snprintf(value, sizeof(value), "%d", ctdp_level->trl_ratios[k][j] * isst_get_disp_freq_multiplier()); format_and_print(outf, level + 4, header, value); } } @@ -585,18 +585,18 @@ void isst_clos_display_information(struct isst_id *id, FILE *outf, int clos, format_and_print(outf, level + 2, header, value); snprintf(header, sizeof(header), "clos-min"); - snprintf(value, sizeof(value), "%d MHz", clos_config->clos_min * DISP_FREQ_MULTIPLIER); + snprintf(value, sizeof(value), "%d MHz", clos_config->clos_min * isst_get_disp_freq_multiplier()); format_and_print(outf, level + 2, header, value); snprintf(header, sizeof(header), "clos-max"); if (clos_config->clos_max == 0xff) snprintf(value, sizeof(value), "Max Turbo frequency"); else - snprintf(value, sizeof(value), "%d MHz", clos_config->clos_max * DISP_FREQ_MULTIPLIER); + snprintf(value, sizeof(value), "%d MHz", clos_config->clos_max * isst_get_disp_freq_multiplier()); format_and_print(outf, level + 2, header, value); snprintf(header, sizeof(header), "clos-desired"); - snprintf(value, sizeof(value), "%d MHz", clos_config->clos_desired * DISP_FREQ_MULTIPLIER); + snprintf(value, sizeof(value), "%d MHz", clos_config->clos_desired * isst_get_disp_freq_multiplier()); format_and_print(outf, level + 2, header, value); format_and_print(outf, level, NULL, NULL); diff --git a/tools/power/x86/intel-speed-select/isst.h b/tools/power/x86/intel-speed-select/isst.h index 6f60fcf409ee..8de9dd479884 100644 --- a/tools/power/x86/intel-speed-select/isst.h +++ b/tools/power/x86/intel-speed-select/isst.h @@ -208,6 +208,7 @@ extern int isst_send_msr_command(unsigned int cpu, unsigned int command, int write, unsigned long long *req_resp); extern void isst_update_platform_param(enum isst_platform_param, int vale); +extern int isst_get_disp_freq_multiplier(void); extern int isst_get_trl_max_levels(void); extern char *isst_get_trl_level_name(int level); extern int isst_is_punit_valid(struct isst_id *id); From e9f79348aedbd1a262bdac0f9e8b104590a30cc0 Mon Sep 17 00:00:00 2001 From: Zhang Rui Date: Sat, 20 Aug 2022 21:50:53 +0800 Subject: [PATCH 088/178] tools/power/x86/intel-speed-select: Always invoke isst_fill_platform_info isst_fill_platform_info fills platform specific information. And it is the proper place to set platform specific callbacks, as done in next patch. As the platform specific callbacks are needed in all cases, including isst_print_platform_information. The best way to achieve both is to invoke isst_fill_platform_info unconditionally, and make isst_print_platform_information leverage the data already filled. No functional changes are expected. Signed-off-by: Zhang Rui Signed-off-by: Srinivas Pandruvada --- .../x86/intel-speed-select/isst-config.c | 42 +++++++------------ 1 file changed, 16 insertions(+), 26 deletions(-) diff --git a/tools/power/x86/intel-speed-select/isst-config.c b/tools/power/x86/intel-speed-select/isst-config.c index 806533fd4626..d4567b8d72db 100644 --- a/tools/power/x86/intel-speed-select/isst-config.c +++ b/tools/power/x86/intel-speed-select/isst-config.c @@ -803,6 +803,9 @@ static int isst_fill_platform_info(void) const char *pathname = "/dev/isst_interface"; int fd; + if (is_clx_n_platform()) + return 0; + fd = open(pathname, O_RDWR); if (fd < 0) err(-1, "%s open failed", pathname); @@ -911,10 +914,6 @@ static void isst_print_extended_platform_info(void) static void isst_print_platform_information(void) { - struct isst_if_platform_info platform_info; - const char *pathname = "/dev/isst_interface"; - int fd; - if (is_clx_n_platform()) { fprintf(stderr, "\nThis option in not supported on this platform\n"); exit(0); @@ -924,25 +923,15 @@ static void isst_print_platform_information(void) set_max_cpu_num(); create_cpu_map(); - fd = open(pathname, O_RDWR); - if (fd < 0) - err(-1, "%s open failed", pathname); - - if (ioctl(fd, ISST_IF_GET_PLATFORM_INFO, &platform_info) == -1) { - perror("ISST_IF_GET_PLATFORM_INFO"); - } else { - fprintf(outf, "Platform: API version : %d\n", - platform_info.api_version); - fprintf(outf, "Platform: Driver version : %d\n", - platform_info.driver_version); - fprintf(outf, "Platform: mbox supported : %d\n", - platform_info.mbox_supported); - fprintf(outf, "Platform: mmio supported : %d\n", - platform_info.mmio_supported); - isst_print_extended_platform_info(); - } - - close(fd); + fprintf(outf, "Platform: API version : %d\n", + isst_platform_info.api_version); + fprintf(outf, "Platform: Driver version : %d\n", + isst_platform_info.driver_version); + fprintf(outf, "Platform: mbox supported : %d\n", + isst_platform_info.mbox_supported); + fprintf(outf, "Platform: mmio supported : %d\n", + isst_platform_info.mmio_supported); + isst_print_extended_platform_info(); exit(0); } @@ -2832,6 +2821,10 @@ static void cmdline(int argc, char **argv) fclose(fp); } + ret = isst_fill_platform_info(); + if (ret) + goto out; + progname = argv[0]; while ((opt = getopt_long_only(argc, argv, "+c:df:hio:vabw:n", long_options, &option_index)) != -1) { @@ -2922,9 +2915,6 @@ static void cmdline(int argc, char **argv) } if (!is_clx_n_platform()) { - ret = isst_fill_platform_info(); - if (ret) - goto out; process_command(argc, argv, isst_help_cmds, isst_cmds); } else { process_command(argc, argv, clx_n_help_cmds, clx_n_cmds); From d0d1a603c5fea39ada5d3c48f8b7273a2109af07 Mon Sep 17 00:00:00 2001 From: Zhang Rui Date: Sat, 20 Aug 2022 18:59:54 +0800 Subject: [PATCH 089/178] tools/power/x86/intel-speed-select: Introduce isst-core-mbox.c isst-core.c should contain generic core APIs only. Platform specific implementations/configurations should be removed from this file. Introduce isst-core-mbox.c and move all mbox/mmio specific functions to this file. Introduce struct isst_platform_ops which contains a series of callbacks that used by the core APIs but need platform specific implementation. No functional changes are expected. Signed-off-by: Zhang Rui [srinivas.pandruvada@linux.intel.com: changelog edits] Signed-off-by: Srinivas Pandruvada --- tools/power/x86/intel-speed-select/Build | 2 +- .../x86/intel-speed-select/isst-config.c | 8 +++- .../x86/intel-speed-select/isst-core-mbox.c | 43 +++++++++++++++++++ .../power/x86/intel-speed-select/isst-core.c | 37 ++++++++++------ tools/power/x86/intel-speed-select/isst.h | 11 +++++ 5 files changed, 87 insertions(+), 14 deletions(-) create mode 100644 tools/power/x86/intel-speed-select/isst-core-mbox.c diff --git a/tools/power/x86/intel-speed-select/Build b/tools/power/x86/intel-speed-select/Build index 81e36bd578b1..32a2493b5233 100644 --- a/tools/power/x86/intel-speed-select/Build +++ b/tools/power/x86/intel-speed-select/Build @@ -1 +1 @@ -intel-speed-select-y += isst-config.o isst-core.o isst-display.o isst-daemon.o hfi-events.o +intel-speed-select-y += isst-config.o isst-core.o isst-display.o isst-daemon.o hfi-events.o isst-core-mbox.o diff --git a/tools/power/x86/intel-speed-select/isst-config.c b/tools/power/x86/intel-speed-select/isst-config.c index d4567b8d72db..1f357d499c89 100644 --- a/tools/power/x86/intel-speed-select/isst-config.c +++ b/tools/power/x86/intel-speed-select/isst-config.c @@ -804,7 +804,7 @@ static int isst_fill_platform_info(void) int fd; if (is_clx_n_platform()) - return 0; + goto set_platform_ops; fd = open(pathname, O_RDWR); if (fd < 0) @@ -822,6 +822,12 @@ static int isst_fill_platform_info(void) printf("Incompatible API versions; Upgrade of tool is required\n"); return -1; } + +set_platform_ops: + if (isst_set_platform_ops()) { + fprintf(stderr, "Failed to set platform callbacks\n"); + exit(0); + } return 0; } diff --git a/tools/power/x86/intel-speed-select/isst-core-mbox.c b/tools/power/x86/intel-speed-select/isst-core-mbox.c new file mode 100644 index 000000000000..221caa35c10c --- /dev/null +++ b/tools/power/x86/intel-speed-select/isst-core-mbox.c @@ -0,0 +1,43 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Intel Speed Select -- Enumerate and control features for Mailbox Interface + * Copyright (c) 2023 Intel Corporation. + */ +#include "isst.h" + +static int mbox_get_disp_freq_multiplier(void) +{ + return DISP_FREQ_MULTIPLIER; +} + +static int mbox_get_trl_max_levels(void) +{ + return 3; +} + +static char *mbox_get_trl_level_name(int level) +{ + switch (level) { + case 0: + return "sse"; + case 1: + return "avx2"; + case 2: + return "avx512"; + default: + return NULL; + } +} + + + +static struct isst_platform_ops mbox_ops = { + .get_disp_freq_multiplier = mbox_get_disp_freq_multiplier, + .get_trl_max_levels = mbox_get_trl_max_levels, + .get_trl_level_name = mbox_get_trl_level_name, +}; + +struct isst_platform_ops *mbox_get_platform_ops(void) +{ + return &mbox_ops; +} diff --git a/tools/power/x86/intel-speed-select/isst-core.c b/tools/power/x86/intel-speed-select/isst-core.c index 8d63db2bf07c..bf7b667d5ffa 100644 --- a/tools/power/x86/intel-speed-select/isst-core.c +++ b/tools/power/x86/intel-speed-select/isst-core.c @@ -9,6 +9,25 @@ static int mbox_delay; static int mbox_retries = 3; +static struct isst_platform_ops *isst_ops; + +#define CHECK_CB(_name) \ + do { \ + if (!isst_ops || !isst_ops->_name) { \ + fprintf(stderr, "Invalid ops\n"); \ + exit(0); \ + } \ + } while (0) + +int isst_set_platform_ops(void) +{ + isst_ops = mbox_get_platform_ops(); + + if (!isst_ops) + return -1; + return 0; +} + void isst_update_platform_param(enum isst_platform_param param, int value) { switch (param) { @@ -25,26 +44,20 @@ void isst_update_platform_param(enum isst_platform_param param, int value) int isst_get_disp_freq_multiplier(void) { - return DISP_FREQ_MULTIPLIER; + CHECK_CB(get_disp_freq_multiplier); + return isst_ops->get_disp_freq_multiplier(); } int isst_get_trl_max_levels(void) { - return 3; + CHECK_CB(get_trl_max_levels); + return isst_ops->get_trl_max_levels(); } char *isst_get_trl_level_name(int level) { - switch (level) { - case 0: - return "sse"; - case 1: - return "avx2"; - case 2: - return "avx512"; - default: - return NULL; - } + CHECK_CB(get_trl_level_name); + return isst_ops->get_trl_level_name(level); } int isst_is_punit_valid(struct isst_id *id) diff --git a/tools/power/x86/intel-speed-select/isst.h b/tools/power/x86/intel-speed-select/isst.h index 8de9dd479884..7bb74d0912c2 100644 --- a/tools/power/x86/intel-speed-select/isst.h +++ b/tools/power/x86/intel-speed-select/isst.h @@ -181,6 +181,12 @@ enum isst_platform_param { ISST_PARAM_MBOX_RETRIES, }; +struct isst_platform_ops { + int (*get_disp_freq_multiplier)(void); + int (*get_trl_max_levels)(void); + char *(*get_trl_level_name)(int level); +}; + extern int is_cpu_in_power_domain(int cpu, struct isst_id *id); extern int get_topo_max_cpus(void); extern int get_cpu_count(struct isst_id *id); @@ -207,6 +213,7 @@ extern int isst_send_mbox_command(unsigned int cpu, unsigned char command, extern int isst_send_msr_command(unsigned int cpu, unsigned int command, int write, unsigned long long *req_resp); +extern int isst_set_platform_ops(void); extern void isst_update_platform_param(enum isst_platform_param, int vale); extern int isst_get_disp_freq_multiplier(void); extern int isst_get_trl_max_levels(void); @@ -285,4 +292,8 @@ extern int isst_daemon(int debug_mode, int poll_interval, int no_daemon); extern void process_level_change(struct isst_id *id); extern int hfi_main(void); extern void hfi_exit(void); + +/* Interface specific callbacks */ +extern struct isst_platform_ops *mbox_get_platform_ops(void); + #endif From 143584e8484fd5225efc4da490d744d26d2cf64e Mon Sep 17 00:00:00 2001 From: Zhang Rui Date: Sat, 20 Aug 2022 19:39:52 +0800 Subject: [PATCH 090/178] tools/power/x86/intel-speed-select: Abstract is_punit_valid Allow platform specific implementation to identify a valid punit. No functional changes are expected. Signed-off-by: Zhang Rui [srinivas.pandruvada@linux.intel.com: changelog edits] Signed-off-by: Srinivas Pandruvada --- tools/power/x86/intel-speed-select/isst-core-mbox.c | 10 ++++++++++ tools/power/x86/intel-speed-select/isst-core.c | 9 ++------- tools/power/x86/intel-speed-select/isst.h | 1 + 3 files changed, 13 insertions(+), 7 deletions(-) diff --git a/tools/power/x86/intel-speed-select/isst-core-mbox.c b/tools/power/x86/intel-speed-select/isst-core-mbox.c index 221caa35c10c..0d14286e0bae 100644 --- a/tools/power/x86/intel-speed-select/isst-core-mbox.c +++ b/tools/power/x86/intel-speed-select/isst-core-mbox.c @@ -29,12 +29,22 @@ static char *mbox_get_trl_level_name(int level) } } +static int mbox_is_punit_valid(struct isst_id *id) +{ + if (id->cpu < 0) + return 0; + if (id->pkg < 0 || id->die < 0 || id->punit) + return 0; + + return 1; +} static struct isst_platform_ops mbox_ops = { .get_disp_freq_multiplier = mbox_get_disp_freq_multiplier, .get_trl_max_levels = mbox_get_trl_max_levels, .get_trl_level_name = mbox_get_trl_level_name, + .is_punit_valid = mbox_is_punit_valid, }; struct isst_platform_ops *mbox_get_platform_ops(void) diff --git a/tools/power/x86/intel-speed-select/isst-core.c b/tools/power/x86/intel-speed-select/isst-core.c index bf7b667d5ffa..714ecd710935 100644 --- a/tools/power/x86/intel-speed-select/isst-core.c +++ b/tools/power/x86/intel-speed-select/isst-core.c @@ -62,13 +62,8 @@ char *isst_get_trl_level_name(int level) int isst_is_punit_valid(struct isst_id *id) { - if (id->cpu < 0) - return 0; - - if (id->pkg < 0 || id->die < 0 || id->punit) - return 0; - - return 1; + CHECK_CB(is_punit_valid); + return isst_ops->is_punit_valid(id); } static int isst_send_mmio_command(unsigned int cpu, unsigned int reg, int write, diff --git a/tools/power/x86/intel-speed-select/isst.h b/tools/power/x86/intel-speed-select/isst.h index 7bb74d0912c2..ce3638c0ab4d 100644 --- a/tools/power/x86/intel-speed-select/isst.h +++ b/tools/power/x86/intel-speed-select/isst.h @@ -185,6 +185,7 @@ struct isst_platform_ops { int (*get_disp_freq_multiplier)(void); int (*get_trl_max_levels)(void); char *(*get_trl_level_name)(int level); + int (*is_punit_valid)(struct isst_id *id); }; extern int is_cpu_in_power_domain(int cpu, struct isst_id *id); From 724387448a45346768ca23ea92b99c67c89b82c8 Mon Sep 17 00:00:00 2001 From: Zhang Rui Date: Mon, 8 Aug 2022 20:23:26 +0800 Subject: [PATCH 091/178] tools/power/x86/intel-speed-select: Abstract get_config_levels Allow platform specific implementation to get SST-PP level. No functional changes are expected. Signed-off-by: Zhang Rui [srinivas.pandruvada@linux.intel.com: changelog edits] Signed-off-by: Srinivas Pandruvada --- .../x86/intel-speed-select/isst-core-mbox.c | 28 +++++++++++++++++++ .../power/x86/intel-speed-select/isst-core.c | 25 ++--------------- tools/power/x86/intel-speed-select/isst.h | 1 + 3 files changed, 31 insertions(+), 23 deletions(-) diff --git a/tools/power/x86/intel-speed-select/isst-core-mbox.c b/tools/power/x86/intel-speed-select/isst-core-mbox.c index 0d14286e0bae..2d8043845ef3 100644 --- a/tools/power/x86/intel-speed-select/isst-core-mbox.c +++ b/tools/power/x86/intel-speed-select/isst-core-mbox.c @@ -40,11 +40,39 @@ static int mbox_is_punit_valid(struct isst_id *id) return 1; } +static int mbox_get_config_levels(struct isst_id *id, struct isst_pkg_ctdp *pkg_dev) +{ + unsigned int resp; + int ret; + + ret = isst_send_mbox_command(id->cpu, CONFIG_TDP, + CONFIG_TDP_GET_LEVELS_INFO, 0, 0, &resp); + if (ret) { + pkg_dev->levels = 0; + pkg_dev->locked = 1; + pkg_dev->current_level = 0; + pkg_dev->version = 0; + pkg_dev->enabled = 0; + return 0; + } + + debug_printf("cpu:%d CONFIG_TDP_GET_LEVELS_INFO resp:%x\n", id->cpu, resp); + + pkg_dev->version = resp & 0xff; + pkg_dev->levels = (resp >> 8) & 0xff; + pkg_dev->current_level = (resp >> 16) & 0xff; + pkg_dev->locked = !!(resp & BIT(24)); + pkg_dev->enabled = !!(resp & BIT(31)); + + return 0; +} + static struct isst_platform_ops mbox_ops = { .get_disp_freq_multiplier = mbox_get_disp_freq_multiplier, .get_trl_max_levels = mbox_get_trl_max_levels, .get_trl_level_name = mbox_get_trl_level_name, .is_punit_valid = mbox_is_punit_valid, + .get_config_levels = mbox_get_config_levels, }; struct isst_platform_ops *mbox_get_platform_ops(void) diff --git a/tools/power/x86/intel-speed-select/isst-core.c b/tools/power/x86/intel-speed-select/isst-core.c index 714ecd710935..0cc5075a7178 100644 --- a/tools/power/x86/intel-speed-select/isst-core.c +++ b/tools/power/x86/intel-speed-select/isst-core.c @@ -287,29 +287,8 @@ int isst_read_pm_config(struct isst_id *id, int *cp_state, int *cp_cap) int isst_get_ctdp_levels(struct isst_id *id, struct isst_pkg_ctdp *pkg_dev) { - unsigned int resp; - int ret; - - ret = isst_send_mbox_command(id->cpu, CONFIG_TDP, - CONFIG_TDP_GET_LEVELS_INFO, 0, 0, &resp); - if (ret) { - pkg_dev->levels = 0; - pkg_dev->locked = 1; - pkg_dev->current_level = 0; - pkg_dev->version = 0; - pkg_dev->enabled = 0; - return 0; - } - - debug_printf("cpu:%d CONFIG_TDP_GET_LEVELS_INFO resp:%x\n", id->cpu, resp); - - pkg_dev->version = resp & 0xff; - pkg_dev->levels = (resp >> 8) & 0xff; - pkg_dev->current_level = (resp >> 16) & 0xff; - pkg_dev->locked = !!(resp & BIT(24)); - pkg_dev->enabled = !!(resp & BIT(31)); - - return 0; + CHECK_CB(get_config_levels); + return isst_ops->get_config_levels(id, pkg_dev); } int isst_get_ctdp_control(struct isst_id *id, int config_index, diff --git a/tools/power/x86/intel-speed-select/isst.h b/tools/power/x86/intel-speed-select/isst.h index ce3638c0ab4d..03ad9e305b9d 100644 --- a/tools/power/x86/intel-speed-select/isst.h +++ b/tools/power/x86/intel-speed-select/isst.h @@ -186,6 +186,7 @@ struct isst_platform_ops { int (*get_trl_max_levels)(void); char *(*get_trl_level_name)(int level); int (*is_punit_valid)(struct isst_id *id); + int (*get_config_levels)(struct isst_id *id, struct isst_pkg_ctdp *pkg_ctdp); }; extern int is_cpu_in_power_domain(int cpu, struct isst_id *id); From bbe32d87524b7b3fe7f758b3f789af4d8997c271 Mon Sep 17 00:00:00 2001 From: Zhang Rui Date: Mon, 8 Aug 2022 20:28:06 +0800 Subject: [PATCH 092/178] tools/power/x86/intel-speed-select: Abstract get_ctdp_control Allow platform specific implementation to get SST-TF/BF/CP capabilities and status. No functional changes are expected. Signed-off-by: Zhang Rui [srinivas.pandruvada@linux.intel.com: changelog edits] Signed-off-by: Srinivas Pandruvada --- .../x86/intel-speed-select/isst-core-mbox.c | 36 +++++++++++++++++++ .../power/x86/intel-speed-select/isst-core.c | 32 ++--------------- tools/power/x86/intel-speed-select/isst.h | 1 + 3 files changed, 39 insertions(+), 30 deletions(-) diff --git a/tools/power/x86/intel-speed-select/isst-core-mbox.c b/tools/power/x86/intel-speed-select/isst-core-mbox.c index 2d8043845ef3..0394b5b1e4f0 100644 --- a/tools/power/x86/intel-speed-select/isst-core-mbox.c +++ b/tools/power/x86/intel-speed-select/isst-core-mbox.c @@ -67,12 +67,48 @@ static int mbox_get_config_levels(struct isst_id *id, struct isst_pkg_ctdp *pkg_ return 0; } +static int mbox_get_ctdp_control(struct isst_id *id, int config_index, + struct isst_pkg_ctdp_level_info *ctdp_level) +{ + int cp_state, cp_cap; + unsigned int resp; + int ret; + + ret = isst_send_mbox_command(id->cpu, CONFIG_TDP, + CONFIG_TDP_GET_TDP_CONTROL, 0, + config_index, &resp); + if (ret) + return ret; + + ctdp_level->fact_support = resp & BIT(0); + ctdp_level->pbf_support = !!(resp & BIT(1)); + ctdp_level->fact_enabled = !!(resp & BIT(16)); + ctdp_level->pbf_enabled = !!(resp & BIT(17)); + + ret = isst_read_pm_config(id, &cp_state, &cp_cap); + if (ret) { + debug_printf("cpu:%d pm_config is not supported\n", id->cpu); + } else { + debug_printf("cpu:%d pm_config SST-CP state:%d cap:%d\n", id->cpu, cp_state, cp_cap); + ctdp_level->sst_cp_support = cp_cap; + ctdp_level->sst_cp_enabled = cp_state; + } + + debug_printf( + "cpu:%d CONFIG_TDP_GET_TDP_CONTROL resp:%x fact_support:%d pbf_support: %d fact_enabled:%d pbf_enabled:%d\n", + id->cpu, resp, ctdp_level->fact_support, ctdp_level->pbf_support, + ctdp_level->fact_enabled, ctdp_level->pbf_enabled); + + return 0; +} + static struct isst_platform_ops mbox_ops = { .get_disp_freq_multiplier = mbox_get_disp_freq_multiplier, .get_trl_max_levels = mbox_get_trl_max_levels, .get_trl_level_name = mbox_get_trl_level_name, .is_punit_valid = mbox_is_punit_valid, .get_config_levels = mbox_get_config_levels, + .get_ctdp_control = mbox_get_ctdp_control, }; struct isst_platform_ops *mbox_get_platform_ops(void) diff --git a/tools/power/x86/intel-speed-select/isst-core.c b/tools/power/x86/intel-speed-select/isst-core.c index 0cc5075a7178..3d6fbf754d47 100644 --- a/tools/power/x86/intel-speed-select/isst-core.c +++ b/tools/power/x86/intel-speed-select/isst-core.c @@ -294,36 +294,8 @@ int isst_get_ctdp_levels(struct isst_id *id, struct isst_pkg_ctdp *pkg_dev) int isst_get_ctdp_control(struct isst_id *id, int config_index, struct isst_pkg_ctdp_level_info *ctdp_level) { - int cp_state, cp_cap; - unsigned int resp; - int ret; - - ret = isst_send_mbox_command(id->cpu, CONFIG_TDP, - CONFIG_TDP_GET_TDP_CONTROL, 0, - config_index, &resp); - if (ret) - return ret; - - ctdp_level->fact_support = resp & BIT(0); - ctdp_level->pbf_support = !!(resp & BIT(1)); - ctdp_level->fact_enabled = !!(resp & BIT(16)); - ctdp_level->pbf_enabled = !!(resp & BIT(17)); - - ret = isst_read_pm_config(id, &cp_state, &cp_cap); - if (ret) { - debug_printf("cpu:%d pm_config is not supported\n", id->cpu); - } else { - debug_printf("cpu:%d pm_config SST-CP state:%d cap:%d\n", id->cpu, cp_state, cp_cap); - ctdp_level->sst_cp_support = cp_cap; - ctdp_level->sst_cp_enabled = cp_state; - } - - debug_printf( - "cpu:%d CONFIG_TDP_GET_TDP_CONTROL resp:%x fact_support:%d pbf_support: %d fact_enabled:%d pbf_enabled:%d\n", - id->cpu, resp, ctdp_level->fact_support, ctdp_level->pbf_support, - ctdp_level->fact_enabled, ctdp_level->pbf_enabled); - - return 0; + CHECK_CB(get_ctdp_control); + return isst_ops->get_ctdp_control(id, config_index, ctdp_level); } int isst_get_tdp_info(struct isst_id *id, int config_index, diff --git a/tools/power/x86/intel-speed-select/isst.h b/tools/power/x86/intel-speed-select/isst.h index 03ad9e305b9d..496e5c4e5b0d 100644 --- a/tools/power/x86/intel-speed-select/isst.h +++ b/tools/power/x86/intel-speed-select/isst.h @@ -187,6 +187,7 @@ struct isst_platform_ops { char *(*get_trl_level_name)(int level); int (*is_punit_valid)(struct isst_id *id); int (*get_config_levels)(struct isst_id *id, struct isst_pkg_ctdp *pkg_ctdp); + int (*get_ctdp_control)(struct isst_id *id, int config_index, struct isst_pkg_ctdp_level_info *ctdp_level); }; extern int is_cpu_in_power_domain(int cpu, struct isst_id *id); From 645b66054cb588c4aa47920643372dd6879ef3b4 Mon Sep 17 00:00:00 2001 From: Zhang Rui Date: Mon, 8 Aug 2022 20:31:38 +0800 Subject: [PATCH 093/178] tools/power/x86/intel-speed-select: Abstract get_tdp_info Allow platform specific implementation to get TDP information. No functional changes are expected. Signed-off-by: Zhang Rui [srinivas.pandruvada@linux.intel.com: changelog edits] Signed-off-by: Srinivas Pandruvada --- .../x86/intel-speed-select/isst-core-mbox.c | 24 +++++++++++++++++++ .../power/x86/intel-speed-select/isst-core.c | 20 ++-------------- tools/power/x86/intel-speed-select/isst.h | 1 + 3 files changed, 27 insertions(+), 18 deletions(-) diff --git a/tools/power/x86/intel-speed-select/isst-core-mbox.c b/tools/power/x86/intel-speed-select/isst-core-mbox.c index 0394b5b1e4f0..33537864a068 100644 --- a/tools/power/x86/intel-speed-select/isst-core-mbox.c +++ b/tools/power/x86/intel-speed-select/isst-core-mbox.c @@ -102,6 +102,29 @@ static int mbox_get_ctdp_control(struct isst_id *id, int config_index, return 0; } +static int mbox_get_tdp_info(struct isst_id *id, int config_index, + struct isst_pkg_ctdp_level_info *ctdp_level) +{ + unsigned int resp; + int ret; + + ret = isst_send_mbox_command(id->cpu, CONFIG_TDP, CONFIG_TDP_GET_TDP_INFO, + 0, config_index, &resp); + if (ret) { + isst_display_error_info_message(1, "Invalid level, Can't get TDP information at level", 1, config_index); + return ret; + } + + ctdp_level->pkg_tdp = resp & GENMASK(14, 0); + ctdp_level->tdp_ratio = (resp & GENMASK(23, 16)) >> 16; + + debug_printf( + "cpu:%d ctdp:%d CONFIG_TDP_GET_TDP_INFO resp:%x tdp_ratio:%d pkg_tdp:%d\n", + id->cpu, config_index, resp, ctdp_level->tdp_ratio, + ctdp_level->pkg_tdp); + return 0; +} + static struct isst_platform_ops mbox_ops = { .get_disp_freq_multiplier = mbox_get_disp_freq_multiplier, .get_trl_max_levels = mbox_get_trl_max_levels, @@ -109,6 +132,7 @@ static struct isst_platform_ops mbox_ops = { .is_punit_valid = mbox_is_punit_valid, .get_config_levels = mbox_get_config_levels, .get_ctdp_control = mbox_get_ctdp_control, + .get_tdp_info = mbox_get_tdp_info, }; struct isst_platform_ops *mbox_get_platform_ops(void) diff --git a/tools/power/x86/intel-speed-select/isst-core.c b/tools/power/x86/intel-speed-select/isst-core.c index 3d6fbf754d47..b4f285d85be9 100644 --- a/tools/power/x86/intel-speed-select/isst-core.c +++ b/tools/power/x86/intel-speed-select/isst-core.c @@ -301,24 +301,8 @@ int isst_get_ctdp_control(struct isst_id *id, int config_index, int isst_get_tdp_info(struct isst_id *id, int config_index, struct isst_pkg_ctdp_level_info *ctdp_level) { - unsigned int resp; - int ret; - - ret = isst_send_mbox_command(id->cpu, CONFIG_TDP, CONFIG_TDP_GET_TDP_INFO, - 0, config_index, &resp); - if (ret) { - isst_display_error_info_message(1, "Invalid level, Can't get TDP information at level", 1, config_index); - return ret; - } - - ctdp_level->pkg_tdp = resp & GENMASK(14, 0); - ctdp_level->tdp_ratio = (resp & GENMASK(23, 16)) >> 16; - - debug_printf( - "cpu:%d ctdp:%d CONFIG_TDP_GET_TDP_INFO resp:%x tdp_ratio:%d pkg_tdp:%d\n", - id->cpu, config_index, resp, ctdp_level->tdp_ratio, - ctdp_level->pkg_tdp); - return 0; + CHECK_CB(get_tdp_info); + return isst_ops->get_tdp_info(id, config_index, ctdp_level); } int isst_get_pwr_info(struct isst_id *id, int config_index, diff --git a/tools/power/x86/intel-speed-select/isst.h b/tools/power/x86/intel-speed-select/isst.h index 496e5c4e5b0d..ed8bcb2a2837 100644 --- a/tools/power/x86/intel-speed-select/isst.h +++ b/tools/power/x86/intel-speed-select/isst.h @@ -188,6 +188,7 @@ struct isst_platform_ops { int (*is_punit_valid)(struct isst_id *id); int (*get_config_levels)(struct isst_id *id, struct isst_pkg_ctdp *pkg_ctdp); int (*get_ctdp_control)(struct isst_id *id, int config_index, struct isst_pkg_ctdp_level_info *ctdp_level); + int (*get_tdp_info)(struct isst_id *id, int config_index, struct isst_pkg_ctdp_level_info *ctdp_level); }; extern int is_cpu_in_power_domain(int cpu, struct isst_id *id); From e107dec9a8ddd97488e579f1d0cf10a849037cdf Mon Sep 17 00:00:00 2001 From: Zhang Rui Date: Mon, 8 Aug 2022 20:34:45 +0800 Subject: [PATCH 094/178] tools/power/x86/intel-speed-select: Abstract get_pwr_info Allow platform specific implementation to get min and max power for a given SST-PP level. No functional changes are expected. Signed-off-by: Zhang Rui [srinivas.pandruvada@linux.intel.com: changelog edits] Signed-off-by: Srinivas Pandruvada --- .../x86/intel-speed-select/isst-core-mbox.c | 24 +++++++++++++++++++ .../power/x86/intel-speed-select/isst-core.c | 19 ++------------- tools/power/x86/intel-speed-select/isst.h | 1 + 3 files changed, 27 insertions(+), 17 deletions(-) diff --git a/tools/power/x86/intel-speed-select/isst-core-mbox.c b/tools/power/x86/intel-speed-select/isst-core-mbox.c index 33537864a068..40c856f4c708 100644 --- a/tools/power/x86/intel-speed-select/isst-core-mbox.c +++ b/tools/power/x86/intel-speed-select/isst-core-mbox.c @@ -125,6 +125,29 @@ static int mbox_get_tdp_info(struct isst_id *id, int config_index, return 0; } +static int mbox_get_pwr_info(struct isst_id *id, int config_index, + struct isst_pkg_ctdp_level_info *ctdp_level) +{ + unsigned int resp; + int ret; + + ret = isst_send_mbox_command(id->cpu, CONFIG_TDP, CONFIG_TDP_GET_PWR_INFO, + 0, config_index, &resp); + if (ret) + return ret; + + ctdp_level->pkg_max_power = resp & GENMASK(14, 0); + ctdp_level->pkg_min_power = (resp & GENMASK(30, 16)) >> 16; + + debug_printf( + "cpu:%d ctdp:%d CONFIG_TDP_GET_PWR_INFO resp:%x pkg_max_power:%d pkg_min_power:%d\n", + id->cpu, config_index, resp, ctdp_level->pkg_max_power, + ctdp_level->pkg_min_power); + + return 0; +} + + static struct isst_platform_ops mbox_ops = { .get_disp_freq_multiplier = mbox_get_disp_freq_multiplier, .get_trl_max_levels = mbox_get_trl_max_levels, @@ -133,6 +156,7 @@ static struct isst_platform_ops mbox_ops = { .get_config_levels = mbox_get_config_levels, .get_ctdp_control = mbox_get_ctdp_control, .get_tdp_info = mbox_get_tdp_info, + .get_pwr_info = mbox_get_pwr_info, }; struct isst_platform_ops *mbox_get_platform_ops(void) diff --git a/tools/power/x86/intel-speed-select/isst-core.c b/tools/power/x86/intel-speed-select/isst-core.c index b4f285d85be9..def0a6888907 100644 --- a/tools/power/x86/intel-speed-select/isst-core.c +++ b/tools/power/x86/intel-speed-select/isst-core.c @@ -308,23 +308,8 @@ int isst_get_tdp_info(struct isst_id *id, int config_index, int isst_get_pwr_info(struct isst_id *id, int config_index, struct isst_pkg_ctdp_level_info *ctdp_level) { - unsigned int resp; - int ret; - - ret = isst_send_mbox_command(id->cpu, CONFIG_TDP, CONFIG_TDP_GET_PWR_INFO, - 0, config_index, &resp); - if (ret) - return ret; - - ctdp_level->pkg_max_power = resp & GENMASK(14, 0); - ctdp_level->pkg_min_power = (resp & GENMASK(30, 16)) >> 16; - - debug_printf( - "cpu:%d ctdp:%d CONFIG_TDP_GET_PWR_INFO resp:%x pkg_max_power:%d pkg_min_power:%d\n", - id->cpu, config_index, resp, ctdp_level->pkg_max_power, - ctdp_level->pkg_min_power); - - return 0; + CHECK_CB(get_pwr_info); + return isst_ops->get_pwr_info(id, config_index, ctdp_level); } void isst_get_uncore_p0_p1_info(struct isst_id *id, int config_index, diff --git a/tools/power/x86/intel-speed-select/isst.h b/tools/power/x86/intel-speed-select/isst.h index ed8bcb2a2837..5dc2613a39ff 100644 --- a/tools/power/x86/intel-speed-select/isst.h +++ b/tools/power/x86/intel-speed-select/isst.h @@ -189,6 +189,7 @@ struct isst_platform_ops { int (*get_config_levels)(struct isst_id *id, struct isst_pkg_ctdp *pkg_ctdp); int (*get_ctdp_control)(struct isst_id *id, int config_index, struct isst_pkg_ctdp_level_info *ctdp_level); int (*get_tdp_info)(struct isst_id *id, int config_index, struct isst_pkg_ctdp_level_info *ctdp_level); + int (*get_pwr_info)(struct isst_id *id, int config_index, struct isst_pkg_ctdp_level_info *ctdp_level); }; extern int is_cpu_in_power_domain(int cpu, struct isst_id *id); From e4cbd0f13f9ab3b3aefe79c45ab4d01984c37551 Mon Sep 17 00:00:00 2001 From: Zhang Rui Date: Mon, 8 Aug 2022 20:49:08 +0800 Subject: [PATCH 095/178] tools/power/x86/intel-speed-select: Move code right before its caller Some functions are defined far from its only caller. Rearrange the code. No functional changes are expected. Signed-off-by: Zhang Rui [srinivas.pandruvada@linux.intel.com: changelog edits] Signed-off-by: Srinivas Pandruvada --- .../power/x86/intel-speed-select/isst-core.c | 203 +++++++++--------- 1 file changed, 102 insertions(+), 101 deletions(-) diff --git a/tools/power/x86/intel-speed-select/isst-core.c b/tools/power/x86/intel-speed-select/isst-core.c index def0a6888907..9b993b00cd7d 100644 --- a/tools/power/x86/intel-speed-select/isst-core.c +++ b/tools/power/x86/intel-speed-select/isst-core.c @@ -312,107 +312,6 @@ int isst_get_pwr_info(struct isst_id *id, int config_index, return isst_ops->get_pwr_info(id, config_index, ctdp_level); } -void isst_get_uncore_p0_p1_info(struct isst_id *id, int config_index, - struct isst_pkg_ctdp_level_info *ctdp_level) -{ - unsigned int resp; - int ret; - - ctdp_level->uncore_pm = 0; - ctdp_level->uncore_p0 = 0; - ctdp_level->uncore_p1 = 0; - - ret = isst_send_mbox_command(id->cpu, CONFIG_TDP, - CONFIG_TDP_GET_RATIO_INFO, 0, - (BIT(16) | config_index), &resp); - if (ret) - goto try_uncore_mbox; - - ctdp_level->uncore_p0 = resp & GENMASK(7, 0); - ctdp_level->uncore_p1 = (resp & GENMASK(15, 8)) >> 8; - ctdp_level->uncore_pm = (resp & GENMASK(31, 24)) >> 24; - - debug_printf( - "cpu:%d ctdp:%d CONFIG_TDP_GET_RATIO_INFO resp:%x uncore p0:%d uncore p1:%d uncore pm:%d\n", - id->cpu, config_index, resp, ctdp_level->uncore_p0, ctdp_level->uncore_p1, - ctdp_level->uncore_pm); - - return; - -try_uncore_mbox: - ret = isst_send_mbox_command(id->cpu, CONFIG_TDP, - CONFIG_TDP_GET_UNCORE_P0_P1_INFO, 0, - config_index, &resp); - if (ret) { - ctdp_level->uncore_p0 = 0; - ctdp_level->uncore_p1 = 0; - return; - } - - ctdp_level->uncore_p0 = resp & GENMASK(7, 0); - ctdp_level->uncore_p1 = (resp & GENMASK(15, 8)) >> 8; - debug_printf( - "cpu:%d ctdp:%d CONFIG_TDP_GET_UNCORE_P0_P1_INFO resp:%x uncore p0:%d uncore p1:%d\n", - id->cpu, config_index, resp, ctdp_level->uncore_p0, - ctdp_level->uncore_p1); -} - -void isst_get_p1_info(struct isst_id *id, int config_index, - struct isst_pkg_ctdp_level_info *ctdp_level) -{ - unsigned int resp; - int ret; - ret = isst_send_mbox_command(id->cpu, CONFIG_TDP, CONFIG_TDP_GET_P1_INFO, 0, - config_index, &resp); - if (ret) { - ctdp_level->sse_p1 = 0; - ctdp_level->avx2_p1 = 0; - ctdp_level->avx512_p1 = 0; - return; - } - - ctdp_level->sse_p1 = resp & GENMASK(7, 0); - ctdp_level->avx2_p1 = (resp & GENMASK(15, 8)) >> 8; - ctdp_level->avx512_p1 = (resp & GENMASK(23, 16)) >> 16; - debug_printf( - "cpu:%d ctdp:%d CONFIG_TDP_GET_P1_INFO resp:%x sse_p1:%d avx2_p1:%d avx512_p1:%d\n", - id->cpu, config_index, resp, ctdp_level->sse_p1, - ctdp_level->avx2_p1, ctdp_level->avx512_p1); -} - -void isst_get_uncore_mem_freq(struct isst_id *id, int config_index, - struct isst_pkg_ctdp_level_info *ctdp_level) -{ - unsigned int resp; - int ret; - - ret = isst_send_mbox_command(id->cpu, CONFIG_TDP, CONFIG_TDP_GET_MEM_FREQ, - 0, config_index, &resp); - if (ret) { - ctdp_level->mem_freq = 0; - return; - } - - ctdp_level->mem_freq = resp & GENMASK(7, 0); - if (is_spr_platform()) { - ctdp_level->mem_freq *= 200; - } else if (is_icx_platform()) { - if (ctdp_level->mem_freq < 7) { - ctdp_level->mem_freq = (12 - ctdp_level->mem_freq) * 133.33 * 2 * 10; - ctdp_level->mem_freq /= 10; - if (ctdp_level->mem_freq % 10 > 5) - ctdp_level->mem_freq++; - } else { - ctdp_level->mem_freq = 0; - } - } else { - ctdp_level->mem_freq = 0; - } - debug_printf( - "cpu:%d ctdp:%d CONFIG_TDP_GET_MEM_FREQ resp:%x uncore mem_freq:%d\n", - id->cpu, config_index, resp, ctdp_level->mem_freq); -} - int isst_get_tjmax_info(struct isst_id *id, int config_index, struct isst_pkg_ctdp_level_info *ctdp_level) { @@ -917,6 +816,108 @@ void isst_get_process_ctdp_complete(struct isst_id *id, struct isst_pkg_ctdp *pk } } +void isst_get_uncore_p0_p1_info(struct isst_id *id, int config_index, + struct isst_pkg_ctdp_level_info *ctdp_level) +{ + unsigned int resp; + int ret; + + ctdp_level->uncore_pm = 0; + ctdp_level->uncore_p0 = 0; + ctdp_level->uncore_p1 = 0; + + ret = isst_send_mbox_command(id->cpu, CONFIG_TDP, + CONFIG_TDP_GET_RATIO_INFO, 0, + (BIT(16) | config_index) , &resp); + if (ret) { + goto try_uncore_mbox; + } + + ctdp_level->uncore_p0 = resp & GENMASK(7, 0); + ctdp_level->uncore_p1 = (resp & GENMASK(15, 8)) >> 8; + ctdp_level->uncore_pm = (resp & GENMASK(31, 24)) >> 24; + + debug_printf( + "cpu:%d ctdp:%d CONFIG_TDP_GET_RATIO_INFO resp:%x uncore p0:%d uncore p1:%d uncore pm:%d\n", + id->cpu, config_index, resp, ctdp_level->uncore_p0, ctdp_level->uncore_p1, + ctdp_level->uncore_pm); + + return; + +try_uncore_mbox: + ret = isst_send_mbox_command(id->cpu, CONFIG_TDP, + CONFIG_TDP_GET_UNCORE_P0_P1_INFO, 0, + config_index, &resp); + if (ret) { + ctdp_level->uncore_p0 = 0; + ctdp_level->uncore_p1 = 0; + return; + } + + ctdp_level->uncore_p0 = resp & GENMASK(7, 0); + ctdp_level->uncore_p1 = (resp & GENMASK(15, 8)) >> 8; + debug_printf( + "cpu:%d ctdp:%d CONFIG_TDP_GET_UNCORE_P0_P1_INFO resp:%x uncore p0:%d uncore p1:%d\n", + id->cpu, config_index, resp, ctdp_level->uncore_p0, + ctdp_level->uncore_p1); +} + +void isst_get_p1_info(struct isst_id *id, int config_index, + struct isst_pkg_ctdp_level_info *ctdp_level) +{ + unsigned int resp; + int ret; + ret = isst_send_mbox_command(id->cpu, CONFIG_TDP, CONFIG_TDP_GET_P1_INFO, 0, + config_index, &resp); + if (ret) { + ctdp_level->sse_p1 = 0; + ctdp_level->avx2_p1 = 0; + ctdp_level->avx512_p1 = 0; + return; + } + + ctdp_level->sse_p1 = resp & GENMASK(7, 0); + ctdp_level->avx2_p1 = (resp & GENMASK(15, 8)) >> 8; + ctdp_level->avx512_p1 = (resp & GENMASK(23, 16)) >> 16; + debug_printf( + "cpu:%d ctdp:%d CONFIG_TDP_GET_P1_INFO resp:%x sse_p1:%d avx2_p1:%d avx512_p1:%d\n", + id->cpu, config_index, resp, ctdp_level->sse_p1, + ctdp_level->avx2_p1, ctdp_level->avx512_p1); +} + +void isst_get_uncore_mem_freq(struct isst_id *id, int config_index, + struct isst_pkg_ctdp_level_info *ctdp_level) +{ + unsigned int resp; + int ret; + + ret = isst_send_mbox_command(id->cpu, CONFIG_TDP, CONFIG_TDP_GET_MEM_FREQ, + 0, config_index, &resp); + if (ret) { + ctdp_level->mem_freq = 0; + return; + } + + ctdp_level->mem_freq = resp & GENMASK(7, 0); + if (is_spr_platform()) { + ctdp_level->mem_freq *= 200; + } else if (is_icx_platform()) { + if (ctdp_level->mem_freq < 7) { + ctdp_level->mem_freq = (12 - ctdp_level->mem_freq) * 133.33 * 2 * 10; + ctdp_level->mem_freq /= 10; + if (ctdp_level->mem_freq % 10 > 5) + ctdp_level->mem_freq++; + } else { + ctdp_level->mem_freq = 0; + } + } else { + ctdp_level->mem_freq = 0; + } + debug_printf( + "cpu:%d ctdp:%d CONFIG_TDP_GET_MEM_FREQ resp:%x uncore mem_freq:%d\n", + id->cpu, config_index, resp, ctdp_level->mem_freq); +} + int isst_get_process_ctdp(struct isst_id *id, int tdp_level, struct isst_pkg_ctdp *pkg_dev) { int i, ret, valid = 0; From a30cbd2270865136fbdcf0fbf7fadda707f32bba Mon Sep 17 00:00:00 2001 From: Zhang Rui Date: Mon, 8 Aug 2022 20:52:23 +0800 Subject: [PATCH 096/178] tools/power/x86/intel-speed-select: Abstract get_tjmax_info Allow platform specific implementation to get the Tjmax info for a given SST-PP level. No functional changes are expected. Signed-off-by: Zhang Rui [srinivas.pandruvada@linux.intel.com: changelog edits] Signed-off-by: Srinivas Pandruvada --- .../x86/intel-speed-select/isst-core-mbox.c | 13 +++++++++- .../power/x86/intel-speed-select/isst-core.c | 24 ------------------- 2 files changed, 12 insertions(+), 25 deletions(-) diff --git a/tools/power/x86/intel-speed-select/isst-core-mbox.c b/tools/power/x86/intel-speed-select/isst-core-mbox.c index 40c856f4c708..2ae46aa7f9c4 100644 --- a/tools/power/x86/intel-speed-select/isst-core-mbox.c +++ b/tools/power/x86/intel-speed-select/isst-core-mbox.c @@ -122,6 +122,18 @@ static int mbox_get_tdp_info(struct isst_id *id, int config_index, "cpu:%d ctdp:%d CONFIG_TDP_GET_TDP_INFO resp:%x tdp_ratio:%d pkg_tdp:%d\n", id->cpu, config_index, resp, ctdp_level->tdp_ratio, ctdp_level->pkg_tdp); + + ret = isst_send_mbox_command(id->cpu, CONFIG_TDP, CONFIG_TDP_GET_TJMAX_INFO, + 0, config_index, &resp); + if (ret) + return ret; + + ctdp_level->t_proc_hot = resp & GENMASK(7, 0); + + debug_printf( + "cpu:%d ctdp:%d CONFIG_TDP_GET_TJMAX_INFO resp:%x t_proc_hot:%d\n", + id->cpu, config_index, resp, ctdp_level->t_proc_hot); + return 0; } @@ -147,7 +159,6 @@ static int mbox_get_pwr_info(struct isst_id *id, int config_index, return 0; } - static struct isst_platform_ops mbox_ops = { .get_disp_freq_multiplier = mbox_get_disp_freq_multiplier, .get_trl_max_levels = mbox_get_trl_max_levels, diff --git a/tools/power/x86/intel-speed-select/isst-core.c b/tools/power/x86/intel-speed-select/isst-core.c index 9b993b00cd7d..d93735fbecea 100644 --- a/tools/power/x86/intel-speed-select/isst-core.c +++ b/tools/power/x86/intel-speed-select/isst-core.c @@ -312,26 +312,6 @@ int isst_get_pwr_info(struct isst_id *id, int config_index, return isst_ops->get_pwr_info(id, config_index, ctdp_level); } -int isst_get_tjmax_info(struct isst_id *id, int config_index, - struct isst_pkg_ctdp_level_info *ctdp_level) -{ - unsigned int resp; - int ret; - - ret = isst_send_mbox_command(id->cpu, CONFIG_TDP, CONFIG_TDP_GET_TJMAX_INFO, - 0, config_index, &resp); - if (ret) - return ret; - - ctdp_level->t_proc_hot = resp & GENMASK(7, 0); - - debug_printf( - "cpu:%d ctdp:%d CONFIG_TDP_GET_TJMAX_INFO resp:%x t_proc_hot:%d\n", - id->cpu, config_index, resp, ctdp_level->t_proc_hot); - - return 0; -} - int isst_get_coremask_info(struct isst_id *id, int config_index, struct isst_pkg_ctdp_level_info *ctdp_level) { @@ -1001,10 +981,6 @@ int isst_get_process_ctdp(struct isst_id *id, int tdp_level, struct isst_pkg_ctd if (ret) return ret; - ret = isst_get_tjmax_info(id, i, ctdp_level); - if (ret) - return ret; - ctdp_level->core_cpumask_size = alloc_cpu_set(&ctdp_level->core_cpumask); ret = isst_get_coremask_info(id, i, ctdp_level); From 668cc16cc8a0b7e3d7f2f2acff2ca3214fbe38d7 Mon Sep 17 00:00:00 2001 From: Zhang Rui Date: Tue, 9 Aug 2022 00:34:34 +0800 Subject: [PATCH 097/178] tools/power/x86/intel-speed-select: Abstract get_coremask_info Allow platform specific implementation to get the core mask for a given SST-PP level. No functional changes are expected. Signed-off-by: Zhang Rui [srinivas.pandruvada@linux.intel.com: changelog edits] Signed-off-by: Srinivas Pandruvada --- .../x86/intel-speed-select/isst-core-mbox.c | 35 +++++++++++++++++++ .../power/x86/intel-speed-select/isst-core.c | 31 ++-------------- tools/power/x86/intel-speed-select/isst.h | 1 + 3 files changed, 38 insertions(+), 29 deletions(-) diff --git a/tools/power/x86/intel-speed-select/isst-core-mbox.c b/tools/power/x86/intel-speed-select/isst-core-mbox.c index 2ae46aa7f9c4..51f589fe6bf9 100644 --- a/tools/power/x86/intel-speed-select/isst-core-mbox.c +++ b/tools/power/x86/intel-speed-select/isst-core-mbox.c @@ -159,6 +159,40 @@ static int mbox_get_pwr_info(struct isst_id *id, int config_index, return 0; } +static int mbox_get_coremask_info(struct isst_id *id, int config_index, + struct isst_pkg_ctdp_level_info *ctdp_level) +{ + unsigned int resp; + int i, ret; + + ctdp_level->cpu_count = 0; + for (i = 0; i < 2; ++i) { + unsigned long long mask; + int cpu_count = 0; + + ret = isst_send_mbox_command(id->cpu, CONFIG_TDP, + CONFIG_TDP_GET_CORE_MASK, 0, + (i << 8) | config_index, &resp); + if (ret) + return ret; + + debug_printf( + "cpu:%d ctdp:%d mask:%d CONFIG_TDP_GET_CORE_MASK resp:%x\n", + id->cpu, config_index, i, resp); + + mask = (unsigned long long)resp << (32 * i); + set_cpu_mask_from_punit_coremask(id, mask, + ctdp_level->core_cpumask_size, + ctdp_level->core_cpumask, + &cpu_count); + ctdp_level->cpu_count += cpu_count; + debug_printf("cpu:%d ctdp:%d mask:%d cpu count:%d\n", id->cpu, + config_index, i, ctdp_level->cpu_count); + } + + return 0; +} + static struct isst_platform_ops mbox_ops = { .get_disp_freq_multiplier = mbox_get_disp_freq_multiplier, .get_trl_max_levels = mbox_get_trl_max_levels, @@ -168,6 +202,7 @@ static struct isst_platform_ops mbox_ops = { .get_ctdp_control = mbox_get_ctdp_control, .get_tdp_info = mbox_get_tdp_info, .get_pwr_info = mbox_get_pwr_info, + .get_coremask_info = mbox_get_coremask_info, }; struct isst_platform_ops *mbox_get_platform_ops(void) diff --git a/tools/power/x86/intel-speed-select/isst-core.c b/tools/power/x86/intel-speed-select/isst-core.c index d93735fbecea..43884632694d 100644 --- a/tools/power/x86/intel-speed-select/isst-core.c +++ b/tools/power/x86/intel-speed-select/isst-core.c @@ -315,35 +315,8 @@ int isst_get_pwr_info(struct isst_id *id, int config_index, int isst_get_coremask_info(struct isst_id *id, int config_index, struct isst_pkg_ctdp_level_info *ctdp_level) { - unsigned int resp; - int i, ret; - - ctdp_level->cpu_count = 0; - for (i = 0; i < 2; ++i) { - unsigned long long mask; - int cpu_count = 0; - - ret = isst_send_mbox_command(id->cpu, CONFIG_TDP, - CONFIG_TDP_GET_CORE_MASK, 0, - (i << 8) | config_index, &resp); - if (ret) - return ret; - - debug_printf( - "cpu:%d ctdp:%d mask:%d CONFIG_TDP_GET_CORE_MASK resp:%x\n", - id->cpu, config_index, i, resp); - - mask = (unsigned long long)resp << (32 * i); - set_cpu_mask_from_punit_coremask(id, mask, - ctdp_level->core_cpumask_size, - ctdp_level->core_cpumask, - &cpu_count); - ctdp_level->cpu_count += cpu_count; - debug_printf("cpu:%d ctdp:%d mask:%d cpu count:%d\n", id->cpu, - config_index, i, ctdp_level->cpu_count); - } - - return 0; + CHECK_CB(get_coremask_info); + return isst_ops->get_coremask_info(id, config_index, ctdp_level); } int isst_get_get_trl_from_msr(struct isst_id *id, int *trl) diff --git a/tools/power/x86/intel-speed-select/isst.h b/tools/power/x86/intel-speed-select/isst.h index 5dc2613a39ff..0d5d35582d55 100644 --- a/tools/power/x86/intel-speed-select/isst.h +++ b/tools/power/x86/intel-speed-select/isst.h @@ -190,6 +190,7 @@ struct isst_platform_ops { int (*get_ctdp_control)(struct isst_id *id, int config_index, struct isst_pkg_ctdp_level_info *ctdp_level); int (*get_tdp_info)(struct isst_id *id, int config_index, struct isst_pkg_ctdp_level_info *ctdp_level); int (*get_pwr_info)(struct isst_id *id, int config_index, struct isst_pkg_ctdp_level_info *ctdp_level); + int (*get_coremask_info)(struct isst_id *id, int config_index, struct isst_pkg_ctdp_level_info *ctdp_level); }; extern int is_cpu_in_power_domain(int cpu, struct isst_id *id); From 39f768c341fa429bf9d37f477902aaf0f5aa904f Mon Sep 17 00:00:00 2001 From: Zhang Rui Date: Wed, 17 Aug 2022 23:19:15 +0800 Subject: [PATCH 098/178] tools/power/x86/intel-speed-select: Abstract get_get_trl Allow platform specific implementation to get turbo ratio limit of the selected SST-PP level, and AVX level. No functional changes are expected. Signed-off-by: Zhang Rui [srinivas.pandruvada@linux.intel.com: changelog edits] Signed-off-by: Srinivas Pandruvada --- .../x86/intel-speed-select/isst-core-mbox.c | 40 +++++++++++++++++++ .../power/x86/intel-speed-select/isst-core.c | 37 +---------------- tools/power/x86/intel-speed-select/isst.h | 1 + 3 files changed, 43 insertions(+), 35 deletions(-) diff --git a/tools/power/x86/intel-speed-select/isst-core-mbox.c b/tools/power/x86/intel-speed-select/isst-core-mbox.c index 51f589fe6bf9..4d31b49b4a26 100644 --- a/tools/power/x86/intel-speed-select/isst-core-mbox.c +++ b/tools/power/x86/intel-speed-select/isst-core-mbox.c @@ -193,6 +193,45 @@ static int mbox_get_coremask_info(struct isst_id *id, int config_index, return 0; } +static int mbox_get_get_trl(struct isst_id *id, int level, int avx_level, int *trl) +{ + unsigned int req, resp; + int ret; + + req = level | (avx_level << 16); + ret = isst_send_mbox_command(id->cpu, CONFIG_TDP, + CONFIG_TDP_GET_TURBO_LIMIT_RATIOS, 0, req, + &resp); + if (ret) + return ret; + + debug_printf( + "cpu:%d CONFIG_TDP_GET_TURBO_LIMIT_RATIOS req:%x resp:%x\n", + id->cpu, req, resp); + + trl[0] = resp & GENMASK(7, 0); + trl[1] = (resp & GENMASK(15, 8)) >> 8; + trl[2] = (resp & GENMASK(23, 16)) >> 16; + trl[3] = (resp & GENMASK(31, 24)) >> 24; + + req = level | BIT(8) | (avx_level << 16); + ret = isst_send_mbox_command(id->cpu, CONFIG_TDP, + CONFIG_TDP_GET_TURBO_LIMIT_RATIOS, 0, req, + &resp); + if (ret) + return ret; + + debug_printf("cpu:%d CONFIG_TDP_GET_TURBO_LIMIT req:%x resp:%x\n", id->cpu, + req, resp); + + trl[4] = resp & GENMASK(7, 0); + trl[5] = (resp & GENMASK(15, 8)) >> 8; + trl[6] = (resp & GENMASK(23, 16)) >> 16; + trl[7] = (resp & GENMASK(31, 24)) >> 24; + + return 0; +} + static struct isst_platform_ops mbox_ops = { .get_disp_freq_multiplier = mbox_get_disp_freq_multiplier, .get_trl_max_levels = mbox_get_trl_max_levels, @@ -203,6 +242,7 @@ static struct isst_platform_ops mbox_ops = { .get_tdp_info = mbox_get_tdp_info, .get_pwr_info = mbox_get_pwr_info, .get_coremask_info = mbox_get_coremask_info, + .get_get_trl = mbox_get_get_trl, }; struct isst_platform_ops *mbox_get_platform_ops(void) diff --git a/tools/power/x86/intel-speed-select/isst-core.c b/tools/power/x86/intel-speed-select/isst-core.c index 43884632694d..6afae60d68fe 100644 --- a/tools/power/x86/intel-speed-select/isst-core.c +++ b/tools/power/x86/intel-speed-select/isst-core.c @@ -342,41 +342,8 @@ int isst_get_get_trl_from_msr(struct isst_id *id, int *trl) int isst_get_get_trl(struct isst_id *id, int level, int avx_level, int *trl) { - unsigned int req, resp; - int ret; - - req = level | (avx_level << 16); - ret = isst_send_mbox_command(id->cpu, CONFIG_TDP, - CONFIG_TDP_GET_TURBO_LIMIT_RATIOS, 0, req, - &resp); - if (ret) - return ret; - - debug_printf( - "cpu:%d CONFIG_TDP_GET_TURBO_LIMIT_RATIOS req:%x resp:%x\n", - id->cpu, req, resp); - - trl[0] = resp & GENMASK(7, 0); - trl[1] = (resp & GENMASK(15, 8)) >> 8; - trl[2] = (resp & GENMASK(23, 16)) >> 16; - trl[3] = (resp & GENMASK(31, 24)) >> 24; - - req = level | BIT(8) | (avx_level << 16); - ret = isst_send_mbox_command(id->cpu, CONFIG_TDP, - CONFIG_TDP_GET_TURBO_LIMIT_RATIOS, 0, req, - &resp); - if (ret) - return ret; - - debug_printf("cpu:%d CONFIG_TDP_GET_TURBO_LIMIT req:%x resp:%x\n", id->cpu, - req, resp); - - trl[4] = resp & GENMASK(7, 0); - trl[5] = (resp & GENMASK(15, 8)) >> 8; - trl[6] = (resp & GENMASK(23, 16)) >> 16; - trl[7] = (resp & GENMASK(31, 24)) >> 24; - - return 0; + CHECK_CB(get_get_trl); + return isst_ops->get_get_trl(id, level, avx_level, trl); } int isst_get_trl_bucket_info(struct isst_id *id, unsigned long long *buckets_info) diff --git a/tools/power/x86/intel-speed-select/isst.h b/tools/power/x86/intel-speed-select/isst.h index 0d5d35582d55..b8b1bdafc18b 100644 --- a/tools/power/x86/intel-speed-select/isst.h +++ b/tools/power/x86/intel-speed-select/isst.h @@ -191,6 +191,7 @@ struct isst_platform_ops { int (*get_tdp_info)(struct isst_id *id, int config_index, struct isst_pkg_ctdp_level_info *ctdp_level); int (*get_pwr_info)(struct isst_id *id, int config_index, struct isst_pkg_ctdp_level_info *ctdp_level); int (*get_coremask_info)(struct isst_id *id, int config_index, struct isst_pkg_ctdp_level_info *ctdp_level); + int (*get_get_trl)(struct isst_id *id, int level, int avx_level, int *trl); }; extern int is_cpu_in_power_domain(int cpu, struct isst_id *id); From 1e37f1b21c7022f1d9dd641a6f2d4ffd2950202c Mon Sep 17 00:00:00 2001 From: Zhang Rui Date: Mon, 8 Aug 2022 21:01:08 +0800 Subject: [PATCH 099/178] tools/power/x86/intel-speed-select: Abstract get_trl_bucket_info Allow platform specific implementation to get buckets info. No functional changes are expected. Signed-off-by: Zhang Rui [srinivas.pandruvada@linux.intel.com: changelog edits] Signed-off-by: Srinivas Pandruvada --- .../x86/intel-speed-select/isst-core-mbox.c | 19 ++++++++++++++++ .../power/x86/intel-speed-select/isst-core.c | 22 +++++-------------- tools/power/x86/intel-speed-select/isst.h | 1 + 3 files changed, 25 insertions(+), 17 deletions(-) diff --git a/tools/power/x86/intel-speed-select/isst-core-mbox.c b/tools/power/x86/intel-speed-select/isst-core-mbox.c index 4d31b49b4a26..0cc08dbb0cc4 100644 --- a/tools/power/x86/intel-speed-select/isst-core-mbox.c +++ b/tools/power/x86/intel-speed-select/isst-core-mbox.c @@ -232,6 +232,24 @@ static int mbox_get_get_trl(struct isst_id *id, int level, int avx_level, int *t return 0; } +static int mbox_get_trl_bucket_info(struct isst_id *id, int level, unsigned long long *buckets_info) +{ + int ret; + + debug_printf("cpu:%d bucket info via MSR\n", id->cpu); + + *buckets_info = 0; + + ret = isst_send_msr_command(id->cpu, 0x1ae, 0, buckets_info); + if (ret) + return ret; + + debug_printf("cpu:%d bucket info via MSR successful 0x%llx\n", id->cpu, + *buckets_info); + + return 0; +} + static struct isst_platform_ops mbox_ops = { .get_disp_freq_multiplier = mbox_get_disp_freq_multiplier, .get_trl_max_levels = mbox_get_trl_max_levels, @@ -243,6 +261,7 @@ static struct isst_platform_ops mbox_ops = { .get_pwr_info = mbox_get_pwr_info, .get_coremask_info = mbox_get_coremask_info, .get_get_trl = mbox_get_get_trl, + .get_trl_bucket_info = mbox_get_trl_bucket_info, }; struct isst_platform_ops *mbox_get_platform_ops(void) diff --git a/tools/power/x86/intel-speed-select/isst-core.c b/tools/power/x86/intel-speed-select/isst-core.c index 6afae60d68fe..bd4facdf746e 100644 --- a/tools/power/x86/intel-speed-select/isst-core.c +++ b/tools/power/x86/intel-speed-select/isst-core.c @@ -346,22 +346,10 @@ int isst_get_get_trl(struct isst_id *id, int level, int avx_level, int *trl) return isst_ops->get_get_trl(id, level, avx_level, trl); } -int isst_get_trl_bucket_info(struct isst_id *id, unsigned long long *buckets_info) +int isst_get_trl_bucket_info(struct isst_id *id, int level, unsigned long long *buckets_info) { - int ret; - - debug_printf("cpu:%d bucket info via MSR\n", id->cpu); - - *buckets_info = 0; - - ret = isst_send_msr_command(id->cpu, 0x1ae, 0, buckets_info); - if (ret) - return ret; - - debug_printf("cpu:%d bucket info via MSR successful 0x%llx\n", id->cpu, - *buckets_info); - - return 0; + CHECK_CB(get_trl_bucket_info); + return isst_ops->get_trl_bucket_info(id, level, buckets_info); } int isst_set_tdp_level(struct isst_id *id, int tdp_level) @@ -909,7 +897,7 @@ int isst_get_process_ctdp(struct isst_id *id, int tdp_level, struct isst_pkg_ctd } isst_get_get_trl_from_msr(id, ctdp_level->trl_ratios[0]); - isst_get_trl_bucket_info(id, &ctdp_level->trl_cores); + isst_get_trl_bucket_info(id, i, &ctdp_level->trl_cores); continue; } @@ -927,7 +915,7 @@ int isst_get_process_ctdp(struct isst_id *id, int tdp_level, struct isst_pkg_ctd if (ret) return ret; - ret = isst_get_trl_bucket_info(id, &ctdp_level->trl_cores); + ret = isst_get_trl_bucket_info(id, i, &ctdp_level->trl_cores); if (ret) return ret; diff --git a/tools/power/x86/intel-speed-select/isst.h b/tools/power/x86/intel-speed-select/isst.h index b8b1bdafc18b..465e07960ccb 100644 --- a/tools/power/x86/intel-speed-select/isst.h +++ b/tools/power/x86/intel-speed-select/isst.h @@ -192,6 +192,7 @@ struct isst_platform_ops { int (*get_pwr_info)(struct isst_id *id, int config_index, struct isst_pkg_ctdp_level_info *ctdp_level); int (*get_coremask_info)(struct isst_id *id, int config_index, struct isst_pkg_ctdp_level_info *ctdp_level); int (*get_get_trl)(struct isst_id *id, int level, int avx_level, int *trl); + int (*get_trl_bucket_info)(struct isst_id *id, int level, unsigned long long *buckets_info); }; extern int is_cpu_in_power_domain(int cpu, struct isst_id *id); From f88c3c4bd431a81e50e126342f8daa6ee9171b82 Mon Sep 17 00:00:00 2001 From: Zhang Rui Date: Tue, 9 Aug 2022 01:49:48 +0800 Subject: [PATCH 100/178] tools/power/x86/intel-speed-select: Abstract set_tdp_level Allow platform specific implementation to set a SST-PP level. No functional changes are expected. Signed-off-by: Zhang Rui [srinivas.pandruvada@linux.intel.com: changelog edits] Signed-off-by: Srinivas Pandruvada --- .../x86/intel-speed-select/isst-core-mbox.c | 23 +++++++++++++++++++ .../power/x86/intel-speed-select/isst-core.c | 20 ++-------------- tools/power/x86/intel-speed-select/isst.h | 1 + 3 files changed, 26 insertions(+), 18 deletions(-) diff --git a/tools/power/x86/intel-speed-select/isst-core-mbox.c b/tools/power/x86/intel-speed-select/isst-core-mbox.c index 0cc08dbb0cc4..f6f1632e35b9 100644 --- a/tools/power/x86/intel-speed-select/isst-core-mbox.c +++ b/tools/power/x86/intel-speed-select/isst-core-mbox.c @@ -250,6 +250,28 @@ static int mbox_get_trl_bucket_info(struct isst_id *id, int level, unsigned long return 0; } +static int mbox_set_tdp_level(struct isst_id *id, int tdp_level) +{ + unsigned int resp; + int ret; + + + if (isst_get_config_tdp_lock_status(id)) { + isst_display_error_info_message(1, "TDP is locked", 0, 0); + return -1; + + } + + ret = isst_send_mbox_command(id->cpu, CONFIG_TDP, CONFIG_TDP_SET_LEVEL, 0, + tdp_level, &resp); + if (ret) { + isst_display_error_info_message(1, "Set TDP level failed for level", 1, tdp_level); + return ret; + } + + return 0; +} + static struct isst_platform_ops mbox_ops = { .get_disp_freq_multiplier = mbox_get_disp_freq_multiplier, .get_trl_max_levels = mbox_get_trl_max_levels, @@ -262,6 +284,7 @@ static struct isst_platform_ops mbox_ops = { .get_coremask_info = mbox_get_coremask_info, .get_get_trl = mbox_get_get_trl, .get_trl_bucket_info = mbox_get_trl_bucket_info, + .set_tdp_level = mbox_set_tdp_level, }; struct isst_platform_ops *mbox_get_platform_ops(void) diff --git a/tools/power/x86/intel-speed-select/isst-core.c b/tools/power/x86/intel-speed-select/isst-core.c index bd4facdf746e..53bfc5d47f52 100644 --- a/tools/power/x86/intel-speed-select/isst-core.c +++ b/tools/power/x86/intel-speed-select/isst-core.c @@ -354,24 +354,8 @@ int isst_get_trl_bucket_info(struct isst_id *id, int level, unsigned long long * int isst_set_tdp_level(struct isst_id *id, int tdp_level) { - unsigned int resp; - int ret; - - - if (isst_get_config_tdp_lock_status(id)) { - isst_display_error_info_message(1, "TDP is locked", 0, 0); - return -1; - - } - - ret = isst_send_mbox_command(id->cpu, CONFIG_TDP, CONFIG_TDP_SET_LEVEL, 0, - tdp_level, &resp); - if (ret) { - isst_display_error_info_message(1, "Set TDP level failed for level", 1, tdp_level); - return ret; - } - - return 0; + CHECK_CB(set_tdp_level); + return isst_ops->set_tdp_level(id, tdp_level); } int isst_get_pbf_info(struct isst_id *id, int level, struct isst_pbf_info *pbf_info) diff --git a/tools/power/x86/intel-speed-select/isst.h b/tools/power/x86/intel-speed-select/isst.h index 465e07960ccb..cc1487c7b9b7 100644 --- a/tools/power/x86/intel-speed-select/isst.h +++ b/tools/power/x86/intel-speed-select/isst.h @@ -193,6 +193,7 @@ struct isst_platform_ops { int (*get_coremask_info)(struct isst_id *id, int config_index, struct isst_pkg_ctdp_level_info *ctdp_level); int (*get_get_trl)(struct isst_id *id, int level, int avx_level, int *trl); int (*get_trl_bucket_info)(struct isst_id *id, int level, unsigned long long *buckets_info); + int (*set_tdp_level)(struct isst_id *id, int tdp_level); }; extern int is_cpu_in_power_domain(int cpu, struct isst_id *id); From 7a1962904fe459d4763c6e02cd4b3ea503328a11 Mon Sep 17 00:00:00 2001 From: Zhang Rui Date: Mon, 8 Aug 2022 21:13:59 +0800 Subject: [PATCH 101/178] tools/power/x86/intel-speed-select: Abstract get_pbf_info Allow platform specific implementation to get SST-BF information. No functional changes are expected. Signed-off-by: Zhang Rui [srinivas.pandruvada@linux.intel.com: changelog edits] Signed-off-by: Srinivas Pandruvada --- .../x86/intel-speed-select/isst-core-mbox.c | 68 +++++++++++++++++++ .../power/x86/intel-speed-select/isst-core.c | 64 +---------------- tools/power/x86/intel-speed-select/isst.h | 1 + 3 files changed, 72 insertions(+), 61 deletions(-) diff --git a/tools/power/x86/intel-speed-select/isst-core-mbox.c b/tools/power/x86/intel-speed-select/isst-core-mbox.c index f6f1632e35b9..40e44fad9589 100644 --- a/tools/power/x86/intel-speed-select/isst-core-mbox.c +++ b/tools/power/x86/intel-speed-select/isst-core-mbox.c @@ -272,6 +272,73 @@ static int mbox_set_tdp_level(struct isst_id *id, int tdp_level) return 0; } +static int mbox_get_pbf_info(struct isst_id *id, int level, struct isst_pbf_info *pbf_info) +{ + int max_punit_core, max_mask_index; + unsigned int req, resp; + int i, ret; + + max_punit_core = get_max_punit_core_id(id); + max_mask_index = max_punit_core > 32 ? 2 : 1; + + for (i = 0; i < max_mask_index; ++i) { + unsigned long long mask; + int count; + + ret = isst_send_mbox_command(id->cpu, CONFIG_TDP, + CONFIG_TDP_PBF_GET_CORE_MASK_INFO, + 0, (i << 8) | level, &resp); + if (ret) + break; + + debug_printf( + "cpu:%d CONFIG_TDP_PBF_GET_CORE_MASK_INFO resp:%x\n", + id->cpu, resp); + + mask = (unsigned long long)resp << (32 * i); + set_cpu_mask_from_punit_coremask(id, mask, + pbf_info->core_cpumask_size, + pbf_info->core_cpumask, + &count); + } + + req = level; + ret = isst_send_mbox_command(id->cpu, CONFIG_TDP, + CONFIG_TDP_PBF_GET_P1HI_P1LO_INFO, 0, req, + &resp); + if (ret) + return ret; + + debug_printf("cpu:%d CONFIG_TDP_PBF_GET_P1HI_P1LO_INFO resp:%x\n", id->cpu, + resp); + + pbf_info->p1_low = resp & 0xff; + pbf_info->p1_high = (resp & GENMASK(15, 8)) >> 8; + + req = level; + ret = isst_send_mbox_command( + id->cpu, CONFIG_TDP, CONFIG_TDP_PBF_GET_TDP_INFO, 0, req, &resp); + if (ret) + return ret; + + debug_printf("cpu:%d CONFIG_TDP_PBF_GET_TDP_INFO resp:%x\n", id->cpu, resp); + + pbf_info->tdp = resp & 0xffff; + + req = level; + ret = isst_send_mbox_command( + id->cpu, CONFIG_TDP, CONFIG_TDP_PBF_GET_TJ_MAX_INFO, 0, req, &resp); + if (ret) + return ret; + + debug_printf("cpu:%d CONFIG_TDP_PBF_GET_TJ_MAX_INFO resp:%x\n", id->cpu, + resp); + pbf_info->t_control = (resp >> 8) & 0xff; + pbf_info->t_prochot = resp & 0xff; + + return 0; +} + static struct isst_platform_ops mbox_ops = { .get_disp_freq_multiplier = mbox_get_disp_freq_multiplier, .get_trl_max_levels = mbox_get_trl_max_levels, @@ -285,6 +352,7 @@ static struct isst_platform_ops mbox_ops = { .get_get_trl = mbox_get_get_trl, .get_trl_bucket_info = mbox_get_trl_bucket_info, .set_tdp_level = mbox_set_tdp_level, + .get_pbf_info = mbox_get_pbf_info, }; struct isst_platform_ops *mbox_get_platform_ops(void) diff --git a/tools/power/x86/intel-speed-select/isst-core.c b/tools/power/x86/intel-speed-select/isst-core.c index 53bfc5d47f52..030c0bd4e5f0 100644 --- a/tools/power/x86/intel-speed-select/isst-core.c +++ b/tools/power/x86/intel-speed-select/isst-core.c @@ -362,8 +362,7 @@ int isst_get_pbf_info(struct isst_id *id, int level, struct isst_pbf_info *pbf_i { struct isst_pkg_ctdp_level_info ctdp_level; struct isst_pkg_ctdp pkg_dev; - int i, ret, max_punit_core, max_mask_index; - unsigned int req, resp; + int ret; ret = isst_get_ctdp_levels(id, &pkg_dev); if (ret) { @@ -387,65 +386,8 @@ int isst_get_pbf_info(struct isst_id *id, int level, struct isst_pbf_info *pbf_i pbf_info->core_cpumask_size = alloc_cpu_set(&pbf_info->core_cpumask); - max_punit_core = get_max_punit_core_id(id); - max_mask_index = max_punit_core > 32 ? 2 : 1; - - for (i = 0; i < max_mask_index; ++i) { - unsigned long long mask; - int count; - - ret = isst_send_mbox_command(id->cpu, CONFIG_TDP, - CONFIG_TDP_PBF_GET_CORE_MASK_INFO, - 0, (i << 8) | level, &resp); - if (ret) - break; - - debug_printf( - "cpu:%d CONFIG_TDP_PBF_GET_CORE_MASK_INFO resp:%x\n", - id->cpu, resp); - - mask = (unsigned long long)resp << (32 * i); - set_cpu_mask_from_punit_coremask(id, mask, - pbf_info->core_cpumask_size, - pbf_info->core_cpumask, - &count); - } - - req = level; - ret = isst_send_mbox_command(id->cpu, CONFIG_TDP, - CONFIG_TDP_PBF_GET_P1HI_P1LO_INFO, 0, req, - &resp); - if (ret) - return ret; - - debug_printf("cpu:%d CONFIG_TDP_PBF_GET_P1HI_P1LO_INFO resp:%x\n", id->cpu, - resp); - - pbf_info->p1_low = resp & 0xff; - pbf_info->p1_high = (resp & GENMASK(15, 8)) >> 8; - - req = level; - ret = isst_send_mbox_command( - id->cpu, CONFIG_TDP, CONFIG_TDP_PBF_GET_TDP_INFO, 0, req, &resp); - if (ret) - return ret; - - debug_printf("cpu:%d CONFIG_TDP_PBF_GET_TDP_INFO resp:%x\n", id->cpu, resp); - - pbf_info->tdp = resp & 0xffff; - - req = level; - ret = isst_send_mbox_command( - id->cpu, CONFIG_TDP, CONFIG_TDP_PBF_GET_TJ_MAX_INFO, 0, req, &resp); - if (ret) - return ret; - - debug_printf("cpu:%d CONFIG_TDP_PBF_GET_TJ_MAX_INFO resp:%x\n", id->cpu, - resp); - pbf_info->t_control = (resp >> 8) & 0xff; - pbf_info->t_prochot = resp & 0xff; - - return 0; + CHECK_CB(get_pbf_info); + return isst_ops->get_pbf_info(id, level, pbf_info); } void isst_get_pbf_info_complete(struct isst_pbf_info *pbf_info) diff --git a/tools/power/x86/intel-speed-select/isst.h b/tools/power/x86/intel-speed-select/isst.h index cc1487c7b9b7..18659506bf68 100644 --- a/tools/power/x86/intel-speed-select/isst.h +++ b/tools/power/x86/intel-speed-select/isst.h @@ -194,6 +194,7 @@ struct isst_platform_ops { int (*get_get_trl)(struct isst_id *id, int level, int avx_level, int *trl); int (*get_trl_bucket_info)(struct isst_id *id, int level, unsigned long long *buckets_info); int (*set_tdp_level)(struct isst_id *id, int tdp_level); + int (*get_pbf_info)(struct isst_id *id, int level, struct isst_pbf_info *pbf_info); }; extern int is_cpu_in_power_domain(int cpu, struct isst_id *id); From 05ece6916e9a302f0d1eae1744ab72946f9ff32c Mon Sep 17 00:00:00 2001 From: Zhang Rui Date: Mon, 8 Aug 2022 21:37:23 +0800 Subject: [PATCH 102/178] tools/power/x86/intel-speed-select: Remove isst_get_pbf_info_complete isst_get_pbf_info_complete does nothing but just free the core_mask. Remove the function and do free core_mask directly and free core mask in the caller. No functional changes are expected. Signed-off-by: Zhang Rui [srinivas.pandruvada@linux.intel.com: changelog edits] Signed-off-by: Srinivas Pandruvada --- tools/power/x86/intel-speed-select/isst-config.c | 2 +- tools/power/x86/intel-speed-select/isst-core.c | 5 ----- tools/power/x86/intel-speed-select/isst.h | 1 - 3 files changed, 1 insertion(+), 7 deletions(-) diff --git a/tools/power/x86/intel-speed-select/isst-config.c b/tools/power/x86/intel-speed-select/isst-config.c index 1f357d499c89..71e00d03062f 100644 --- a/tools/power/x86/intel-speed-select/isst-config.c +++ b/tools/power/x86/intel-speed-select/isst-config.c @@ -1331,7 +1331,7 @@ static void dump_pbf_config_for_cpu(struct isst_id *id, void *arg1, void *arg2, exit(1); } else { isst_pbf_display_information(id, outf, tdp_level, &pbf_info); - isst_get_pbf_info_complete(&pbf_info); + free_cpu_set(pbf_info.core_cpumask); } } diff --git a/tools/power/x86/intel-speed-select/isst-core.c b/tools/power/x86/intel-speed-select/isst-core.c index 030c0bd4e5f0..41aa1adc2025 100644 --- a/tools/power/x86/intel-speed-select/isst-core.c +++ b/tools/power/x86/intel-speed-select/isst-core.c @@ -390,11 +390,6 @@ int isst_get_pbf_info(struct isst_id *id, int level, struct isst_pbf_info *pbf_i return isst_ops->get_pbf_info(id, level, pbf_info); } -void isst_get_pbf_info_complete(struct isst_pbf_info *pbf_info) -{ - free_cpu_set(pbf_info->core_cpumask); -} - int isst_set_pbf_fact_status(struct isst_id *id, int pbf, int enable) { struct isst_pkg_ctdp pkg_dev; diff --git a/tools/power/x86/intel-speed-select/isst.h b/tools/power/x86/intel-speed-select/isst.h index 18659506bf68..06e089eaaa28 100644 --- a/tools/power/x86/intel-speed-select/isst.h +++ b/tools/power/x86/intel-speed-select/isst.h @@ -253,7 +253,6 @@ extern int isst_set_tdp_level(struct isst_id *id, int tdp_level); extern int isst_set_pbf_fact_status(struct isst_id *id, int pbf, int enable); extern int isst_get_pbf_info(struct isst_id *id, int level, struct isst_pbf_info *pbf_info); -extern void isst_get_pbf_info_complete(struct isst_pbf_info *pbf_info); extern int isst_get_fact_info(struct isst_id *id, int level, int fact_bucket, struct isst_fact_info *fact_info); extern int isst_get_fact_bucket_info(struct isst_id *id, int level, From 5843f2177058dab4d656fee692a0e8a2370e2d3d Mon Sep 17 00:00:00 2001 From: Zhang Rui Date: Mon, 8 Aug 2022 21:44:04 +0800 Subject: [PATCH 103/178] tools/power/x86/intel-speed-select: Abstract set_pbf_fact_status Allow platform specific implementation to enable/disable SST-TF/BF. No functional changes are expected. Signed-off-by: Zhang Rui [srinivas.pandruvada@linux.intel.com: changelog edits] Signed-off-by: Srinivas Pandruvada --- .../x86/intel-speed-select/isst-core-mbox.c | 52 +++++++++++++++++++ .../power/x86/intel-speed-select/isst-core.c | 49 +---------------- tools/power/x86/intel-speed-select/isst.h | 1 + 3 files changed, 55 insertions(+), 47 deletions(-) diff --git a/tools/power/x86/intel-speed-select/isst-core-mbox.c b/tools/power/x86/intel-speed-select/isst-core-mbox.c index 40e44fad9589..e6d32786a6ad 100644 --- a/tools/power/x86/intel-speed-select/isst-core-mbox.c +++ b/tools/power/x86/intel-speed-select/isst-core-mbox.c @@ -339,6 +339,57 @@ static int mbox_get_pbf_info(struct isst_id *id, int level, struct isst_pbf_info return 0; } +static int mbox_set_pbf_fact_status(struct isst_id *id, int pbf, int enable) +{ + struct isst_pkg_ctdp pkg_dev; + struct isst_pkg_ctdp_level_info ctdp_level; + int current_level; + unsigned int req = 0, resp; + int ret; + + ret = isst_get_ctdp_levels(id, &pkg_dev); + if (ret) + debug_printf("cpu:%d No support for dynamic ISST\n", id->cpu); + + current_level = pkg_dev.current_level; + + ret = isst_get_ctdp_control(id, current_level, &ctdp_level); + if (ret) + return ret; + + if (pbf) { + if (ctdp_level.fact_enabled) + req = BIT(16); + + if (enable) + req |= BIT(17); + else + req &= ~BIT(17); + } else { + + if (enable && !ctdp_level.sst_cp_enabled) + isst_display_error_info_message(0, "Make sure to execute before: core-power enable", 0, 0); + + if (ctdp_level.pbf_enabled) + req = BIT(17); + + if (enable) + req |= BIT(16); + else + req &= ~BIT(16); + } + + ret = isst_send_mbox_command(id->cpu, CONFIG_TDP, + CONFIG_TDP_SET_TDP_CONTROL, 0, req, &resp); + if (ret) + return ret; + + debug_printf("cpu:%d CONFIG_TDP_SET_TDP_CONTROL pbf/fact:%d req:%x\n", + id->cpu, pbf, req); + + return 0; +} + static struct isst_platform_ops mbox_ops = { .get_disp_freq_multiplier = mbox_get_disp_freq_multiplier, .get_trl_max_levels = mbox_get_trl_max_levels, @@ -353,6 +404,7 @@ static struct isst_platform_ops mbox_ops = { .get_trl_bucket_info = mbox_get_trl_bucket_info, .set_tdp_level = mbox_set_tdp_level, .get_pbf_info = mbox_get_pbf_info, + .set_pbf_fact_status = mbox_set_pbf_fact_status, }; struct isst_platform_ops *mbox_get_platform_ops(void) diff --git a/tools/power/x86/intel-speed-select/isst-core.c b/tools/power/x86/intel-speed-select/isst-core.c index 41aa1adc2025..3765a1eefcc5 100644 --- a/tools/power/x86/intel-speed-select/isst-core.c +++ b/tools/power/x86/intel-speed-select/isst-core.c @@ -392,53 +392,8 @@ int isst_get_pbf_info(struct isst_id *id, int level, struct isst_pbf_info *pbf_i int isst_set_pbf_fact_status(struct isst_id *id, int pbf, int enable) { - struct isst_pkg_ctdp pkg_dev; - struct isst_pkg_ctdp_level_info ctdp_level; - int current_level; - unsigned int req = 0, resp; - int ret; - - ret = isst_get_ctdp_levels(id, &pkg_dev); - if (ret) - debug_printf("cpu:%d No support for dynamic ISST\n", id->cpu); - - current_level = pkg_dev.current_level; - - ret = isst_get_ctdp_control(id, current_level, &ctdp_level); - if (ret) - return ret; - - if (pbf) { - if (ctdp_level.fact_enabled) - req = BIT(16); - - if (enable) - req |= BIT(17); - else - req &= ~BIT(17); - } else { - - if (enable && !ctdp_level.sst_cp_enabled) - isst_display_error_info_message(0, "Make sure to execute before: core-power enable", 0, 0); - - if (ctdp_level.pbf_enabled) - req = BIT(17); - - if (enable) - req |= BIT(16); - else - req &= ~BIT(16); - } - - ret = isst_send_mbox_command(id->cpu, CONFIG_TDP, - CONFIG_TDP_SET_TDP_CONTROL, 0, req, &resp); - if (ret) - return ret; - - debug_printf("cpu:%d CONFIG_TDP_SET_TDP_CONTROL pbf/fact:%d req:%x\n", - id->cpu, pbf, req); - - return 0; + CHECK_CB(set_pbf_fact_status); + return isst_ops->set_pbf_fact_status(id, pbf, enable); } int isst_get_fact_bucket_info(struct isst_id *id, int level, diff --git a/tools/power/x86/intel-speed-select/isst.h b/tools/power/x86/intel-speed-select/isst.h index 06e089eaaa28..e8251a1c34ca 100644 --- a/tools/power/x86/intel-speed-select/isst.h +++ b/tools/power/x86/intel-speed-select/isst.h @@ -195,6 +195,7 @@ struct isst_platform_ops { int (*get_trl_bucket_info)(struct isst_id *id, int level, unsigned long long *buckets_info); int (*set_tdp_level)(struct isst_id *id, int tdp_level); int (*get_pbf_info)(struct isst_id *id, int level, struct isst_pbf_info *pbf_info); + int (*set_pbf_fact_status)(struct isst_id *id, int pbf, int enable); }; extern int is_cpu_in_power_domain(int cpu, struct isst_id *id); From 7b5f586dcfdf94dad72a7f3569c100087b80a9ac Mon Sep 17 00:00:00 2001 From: Zhang Rui Date: Mon, 8 Aug 2022 21:52:17 +0800 Subject: [PATCH 104/178] tools/power/x86/intel-speed-select: Abstract get_fact_info Allow platform specific implementation to get SST-TF info. No functional changes are expected. Signed-off-by: Zhang Rui [srinivas.pandruvada@linux.intel.com: changelog edits] Signed-off-by: Srinivas Pandruvada --- .../x86/intel-speed-select/isst-core-mbox.c | 92 +++++++++++++++++++ .../power/x86/intel-speed-select/isst-core.c | 88 +----------------- tools/power/x86/intel-speed-select/isst.h | 3 +- 3 files changed, 96 insertions(+), 87 deletions(-) diff --git a/tools/power/x86/intel-speed-select/isst-core-mbox.c b/tools/power/x86/intel-speed-select/isst-core-mbox.c index e6d32786a6ad..bf873dfe72e6 100644 --- a/tools/power/x86/intel-speed-select/isst-core-mbox.c +++ b/tools/power/x86/intel-speed-select/isst-core-mbox.c @@ -390,6 +390,97 @@ static int mbox_set_pbf_fact_status(struct isst_id *id, int pbf, int enable) return 0; } +static int _get_fact_bucket_info(struct isst_id *id, int level, + struct isst_fact_bucket_info *bucket_info) +{ + unsigned int resp; + int i, k, ret; + + for (i = 0; i < 2; ++i) { + int j; + + ret = isst_send_mbox_command( + id->cpu, CONFIG_TDP, + CONFIG_TDP_GET_FACT_HP_TURBO_LIMIT_NUMCORES, 0, + (i << 8) | level, &resp); + if (ret) + return ret; + + debug_printf( + "cpu:%d CONFIG_TDP_GET_FACT_HP_TURBO_LIMIT_NUMCORES index:%d level:%d resp:%x\n", + id->cpu, i, level, resp); + + for (j = 0; j < 4; ++j) { + bucket_info[j + (i * 4)].hp_cores = + (resp >> (j * 8)) & 0xff; + } + } + + for (k = 0; k < 3; ++k) { + for (i = 0; i < 2; ++i) { + int j; + + ret = isst_send_mbox_command( + id->cpu, CONFIG_TDP, + CONFIG_TDP_GET_FACT_HP_TURBO_LIMIT_RATIOS, 0, + (k << 16) | (i << 8) | level, &resp); + if (ret) + return ret; + + debug_printf( + "cpu:%d CONFIG_TDP_GET_FACT_HP_TURBO_LIMIT_RATIOS index:%d level:%d avx:%d resp:%x\n", + id->cpu, i, level, k, resp); + + for (j = 0; j < 4; ++j) { + bucket_info[j + (i * 4)].hp_ratios[k] = + (resp >> (j * 8)) & 0xff; + } + } + } + + return 0; +} + +static int mbox_get_fact_info(struct isst_id *id, int level, int fact_bucket, struct isst_fact_info *fact_info) +{ + unsigned int resp; + int j, ret, print; + + ret = isst_send_mbox_command(id->cpu, CONFIG_TDP, + CONFIG_TDP_GET_FACT_LP_CLIPPING_RATIO, 0, + level, &resp); + if (ret) + return ret; + + debug_printf("cpu:%d CONFIG_TDP_GET_FACT_LP_CLIPPING_RATIO resp:%x\n", + id->cpu, resp); + + fact_info->lp_ratios[0] = resp & 0xff; + fact_info->lp_ratios[1] = (resp >> 8) & 0xff; + fact_info->lp_ratios[2] = (resp >> 16) & 0xff; + + ret = _get_fact_bucket_info(id, level, fact_info->bucket_info); + if (ret) + return ret; + + print = 0; + for (j = 0; j < ISST_FACT_MAX_BUCKETS; ++j) { + if (fact_bucket != 0xff && fact_bucket != j) + continue; + + if (!fact_info->bucket_info[j].hp_cores) + break; + + print = 1; + } + if (!print) { + isst_display_error_info_message(1, "Invalid bucket", 0, 0); + return -1; + } + + return 0; +} + static struct isst_platform_ops mbox_ops = { .get_disp_freq_multiplier = mbox_get_disp_freq_multiplier, .get_trl_max_levels = mbox_get_trl_max_levels, @@ -405,6 +496,7 @@ static struct isst_platform_ops mbox_ops = { .set_tdp_level = mbox_set_tdp_level, .get_pbf_info = mbox_get_pbf_info, .set_pbf_fact_status = mbox_set_pbf_fact_status, + .get_fact_info = mbox_get_fact_info, }; struct isst_platform_ops *mbox_get_platform_ops(void) diff --git a/tools/power/x86/intel-speed-select/isst-core.c b/tools/power/x86/intel-speed-select/isst-core.c index 3765a1eefcc5..e42469363803 100644 --- a/tools/power/x86/intel-speed-select/isst-core.c +++ b/tools/power/x86/intel-speed-select/isst-core.c @@ -396,63 +396,13 @@ int isst_set_pbf_fact_status(struct isst_id *id, int pbf, int enable) return isst_ops->set_pbf_fact_status(id, pbf, enable); } -int isst_get_fact_bucket_info(struct isst_id *id, int level, - struct isst_fact_bucket_info *bucket_info) -{ - unsigned int resp; - int i, k, ret; - for (i = 0; i < 2; ++i) { - int j; - - ret = isst_send_mbox_command( - id->cpu, CONFIG_TDP, - CONFIG_TDP_GET_FACT_HP_TURBO_LIMIT_NUMCORES, 0, - (i << 8) | level, &resp); - if (ret) - return ret; - - debug_printf( - "cpu:%d CONFIG_TDP_GET_FACT_HP_TURBO_LIMIT_NUMCORES index:%d level:%d resp:%x\n", - id->cpu, i, level, resp); - - for (j = 0; j < 4; ++j) { - bucket_info[j + (i * 4)].hp_cores = - (resp >> (j * 8)) & 0xff; - } - } - - for (k = 0; k < 3; ++k) { - for (i = 0; i < 2; ++i) { - int j; - - ret = isst_send_mbox_command( - id->cpu, CONFIG_TDP, - CONFIG_TDP_GET_FACT_HP_TURBO_LIMIT_RATIOS, 0, - (k << 16) | (i << 8) | level, &resp); - if (ret) - return ret; - - debug_printf( - "cpu:%d CONFIG_TDP_GET_FACT_HP_TURBO_LIMIT_RATIOS index:%d level:%d avx:%d resp:%x\n", - id->cpu, i, level, k, resp); - - for (j = 0; j < 4; ++j) { - bucket_info[j + (i * 4)].hp_ratios[k] = - (resp >> (j * 8)) & 0xff; - } - } - } - - return 0; -} int isst_get_fact_info(struct isst_id *id, int level, int fact_bucket, struct isst_fact_info *fact_info) { struct isst_pkg_ctdp_level_info ctdp_level; struct isst_pkg_ctdp pkg_dev; - unsigned int resp; - int j, ret, print; + int ret; ret = isst_get_ctdp_levels(id, &pkg_dev); if (ret) { @@ -473,40 +423,8 @@ int isst_get_fact_info(struct isst_id *id, int level, int fact_bucket, struct is isst_display_error_info_message(1, "turbo-freq feature is not present at this level", 1, level); return -1; } - - ret = isst_send_mbox_command(id->cpu, CONFIG_TDP, - CONFIG_TDP_GET_FACT_LP_CLIPPING_RATIO, 0, - level, &resp); - if (ret) - return ret; - - debug_printf("cpu:%d CONFIG_TDP_GET_FACT_LP_CLIPPING_RATIO resp:%x\n", - id->cpu, resp); - - fact_info->lp_ratios[0] = resp & 0xff; - fact_info->lp_ratios[1] = (resp >> 8) & 0xff; - fact_info->lp_ratios[2] = (resp >> 16) & 0xff; - - ret = isst_get_fact_bucket_info(id, level, fact_info->bucket_info); - if (ret) - return ret; - - print = 0; - for (j = 0; j < ISST_FACT_MAX_BUCKETS; ++j) { - if (fact_bucket != 0xff && fact_bucket != j) - continue; - - if (!fact_info->bucket_info[j].hp_cores) - break; - - print = 1; - } - if (!print) { - isst_display_error_info_message(1, "Invalid bucket", 0, 0); - return -1; - } - - return 0; + CHECK_CB(get_fact_info); + return isst_ops->get_fact_info(id, level, fact_bucket, fact_info); } int isst_get_trl(struct isst_id *id, unsigned long long *trl) diff --git a/tools/power/x86/intel-speed-select/isst.h b/tools/power/x86/intel-speed-select/isst.h index e8251a1c34ca..4fd901ef2437 100644 --- a/tools/power/x86/intel-speed-select/isst.h +++ b/tools/power/x86/intel-speed-select/isst.h @@ -196,6 +196,7 @@ struct isst_platform_ops { int (*set_tdp_level)(struct isst_id *id, int tdp_level); int (*get_pbf_info)(struct isst_id *id, int level, struct isst_pbf_info *pbf_info); int (*set_pbf_fact_status)(struct isst_id *id, int pbf, int enable); + int (*get_fact_info)(struct isst_id *id, int level, int fact_bucket, struct isst_fact_info *fact_info); }; extern int is_cpu_in_power_domain(int cpu, struct isst_id *id); @@ -256,8 +257,6 @@ extern int isst_get_pbf_info(struct isst_id *id, int level, struct isst_pbf_info *pbf_info); extern int isst_get_fact_info(struct isst_id *id, int level, int fact_bucket, struct isst_fact_info *fact_info); -extern int isst_get_fact_bucket_info(struct isst_id *id, int level, - struct isst_fact_bucket_info *bucket_info); extern void isst_fact_display_information(struct isst_id *id, FILE *outf, int level, int fact_bucket, int fact_avx, struct isst_fact_info *fact_info); From 00c26c1f7e3dfc0ec2e39deb1cffc2acad1d2ae4 Mon Sep 17 00:00:00 2001 From: Zhang Rui Date: Thu, 18 Aug 2022 00:28:41 +0800 Subject: [PATCH 105/178] tools/power/x86/intel-speed-select: Abstract get_uncore_p0_p1_info Allow platform specific implementation to get uncore frequency info. No functional changes are expected. Signed-off-by: Zhang Rui [srinivas.pandruvada@linux.intel.com: changelog edits] Signed-off-by: Srinivas Pandruvada --- .../x86/intel-speed-select/isst-core-mbox.c | 47 +++++++++++++++++++ .../power/x86/intel-speed-select/isst-core.c | 44 +---------------- tools/power/x86/intel-speed-select/isst.h | 1 + 3 files changed, 50 insertions(+), 42 deletions(-) diff --git a/tools/power/x86/intel-speed-select/isst-core-mbox.c b/tools/power/x86/intel-speed-select/isst-core-mbox.c index bf873dfe72e6..466ed0934b42 100644 --- a/tools/power/x86/intel-speed-select/isst-core-mbox.c +++ b/tools/power/x86/intel-speed-select/isst-core-mbox.c @@ -102,6 +102,52 @@ static int mbox_get_ctdp_control(struct isst_id *id, int config_index, return 0; } +static void mbox_get_uncore_p0_p1_info(struct isst_id *id, int config_index, + struct isst_pkg_ctdp_level_info *ctdp_level) +{ + unsigned int resp; + int ret; + + ctdp_level->uncore_pm = 0; + ctdp_level->uncore_p0 = 0; + ctdp_level->uncore_p1 = 0; + + ret = isst_send_mbox_command(id->cpu, CONFIG_TDP, + CONFIG_TDP_GET_RATIO_INFO, 0, + (BIT(16) | config_index) , &resp); + if (ret) { + goto try_uncore_mbox; + } + + ctdp_level->uncore_p0 = resp & GENMASK(7, 0); + ctdp_level->uncore_p1 = (resp & GENMASK(15, 8)) >> 8; + ctdp_level->uncore_pm = (resp & GENMASK(31, 24)) >> 24; + + debug_printf( + "cpu:%d ctdp:%d CONFIG_TDP_GET_RATIO_INFO resp:%x uncore p0:%d uncore p1:%d uncore pm:%d\n", + id->cpu, config_index, resp, ctdp_level->uncore_p0, ctdp_level->uncore_p1, + ctdp_level->uncore_pm); + + return; + +try_uncore_mbox: + ret = isst_send_mbox_command(id->cpu, CONFIG_TDP, + CONFIG_TDP_GET_UNCORE_P0_P1_INFO, 0, + config_index, &resp); + if (ret) { + ctdp_level->uncore_p0 = 0; + ctdp_level->uncore_p1 = 0; + return; + } + + ctdp_level->uncore_p0 = resp & GENMASK(7, 0); + ctdp_level->uncore_p1 = (resp & GENMASK(15, 8)) >> 8; + debug_printf( + "cpu:%d ctdp:%d CONFIG_TDP_GET_UNCORE_P0_P1_INFO resp:%x uncore p0:%d uncore p1:%d\n", + id->cpu, config_index, resp, ctdp_level->uncore_p0, + ctdp_level->uncore_p1); +} + static int mbox_get_tdp_info(struct isst_id *id, int config_index, struct isst_pkg_ctdp_level_info *ctdp_level) { @@ -497,6 +543,7 @@ static struct isst_platform_ops mbox_ops = { .get_pbf_info = mbox_get_pbf_info, .set_pbf_fact_status = mbox_set_pbf_fact_status, .get_fact_info = mbox_get_fact_info, + .get_uncore_p0_p1_info = mbox_get_uncore_p0_p1_info, }; struct isst_platform_ops *mbox_get_platform_ops(void) diff --git a/tools/power/x86/intel-speed-select/isst-core.c b/tools/power/x86/intel-speed-select/isst-core.c index e42469363803..5d0db8424361 100644 --- a/tools/power/x86/intel-speed-select/isst-core.c +++ b/tools/power/x86/intel-speed-select/isst-core.c @@ -521,47 +521,8 @@ void isst_get_process_ctdp_complete(struct isst_id *id, struct isst_pkg_ctdp *pk void isst_get_uncore_p0_p1_info(struct isst_id *id, int config_index, struct isst_pkg_ctdp_level_info *ctdp_level) { - unsigned int resp; - int ret; - - ctdp_level->uncore_pm = 0; - ctdp_level->uncore_p0 = 0; - ctdp_level->uncore_p1 = 0; - - ret = isst_send_mbox_command(id->cpu, CONFIG_TDP, - CONFIG_TDP_GET_RATIO_INFO, 0, - (BIT(16) | config_index) , &resp); - if (ret) { - goto try_uncore_mbox; - } - - ctdp_level->uncore_p0 = resp & GENMASK(7, 0); - ctdp_level->uncore_p1 = (resp & GENMASK(15, 8)) >> 8; - ctdp_level->uncore_pm = (resp & GENMASK(31, 24)) >> 24; - - debug_printf( - "cpu:%d ctdp:%d CONFIG_TDP_GET_RATIO_INFO resp:%x uncore p0:%d uncore p1:%d uncore pm:%d\n", - id->cpu, config_index, resp, ctdp_level->uncore_p0, ctdp_level->uncore_p1, - ctdp_level->uncore_pm); - - return; - -try_uncore_mbox: - ret = isst_send_mbox_command(id->cpu, CONFIG_TDP, - CONFIG_TDP_GET_UNCORE_P0_P1_INFO, 0, - config_index, &resp); - if (ret) { - ctdp_level->uncore_p0 = 0; - ctdp_level->uncore_p1 = 0; - return; - } - - ctdp_level->uncore_p0 = resp & GENMASK(7, 0); - ctdp_level->uncore_p1 = (resp & GENMASK(15, 8)) >> 8; - debug_printf( - "cpu:%d ctdp:%d CONFIG_TDP_GET_UNCORE_P0_P1_INFO resp:%x uncore p0:%d uncore p1:%d\n", - id->cpu, config_index, resp, ctdp_level->uncore_p0, - ctdp_level->uncore_p1); + CHECK_CB(get_uncore_p0_p1_info); + return isst_ops->get_uncore_p0_p1_info(id, config_index, ctdp_level); } void isst_get_p1_info(struct isst_id *id, int config_index, @@ -719,7 +680,6 @@ int isst_get_process_ctdp(struct isst_id *id, int tdp_level, struct isst_pkg_ctd if (ret) return ret; } - isst_get_uncore_p0_p1_info(id, i, ctdp_level); isst_get_p1_info(id, i, ctdp_level); isst_get_uncore_mem_freq(id, i, ctdp_level); diff --git a/tools/power/x86/intel-speed-select/isst.h b/tools/power/x86/intel-speed-select/isst.h index 4fd901ef2437..e113a2bef404 100644 --- a/tools/power/x86/intel-speed-select/isst.h +++ b/tools/power/x86/intel-speed-select/isst.h @@ -197,6 +197,7 @@ struct isst_platform_ops { int (*get_pbf_info)(struct isst_id *id, int level, struct isst_pbf_info *pbf_info); int (*set_pbf_fact_status)(struct isst_id *id, int pbf, int enable); int (*get_fact_info)(struct isst_id *id, int level, int fact_bucket, struct isst_fact_info *fact_info); + void (*get_uncore_p0_p1_info)(struct isst_id *id, int config_index, struct isst_pkg_ctdp_level_info *ctdp_level); }; extern int is_cpu_in_power_domain(int cpu, struct isst_id *id); From 855932831511eef31b5e71c3b6155fd8c57e2a0b Mon Sep 17 00:00:00 2001 From: Zhang Rui Date: Thu, 2 Feb 2023 14:14:54 +0800 Subject: [PATCH 106/178] tools/power/x86/intel-speed-select: Enhance get_tdp_info mbox_get_uncore_p0_p1_info/get_p1_info/get_uncore_mem_freq can be done inside get_tdp_info(). Fold the code into get_tdp_info(). No functional changes are expected. Signed-off-by: Zhang Rui [srinivas.pandruvada@linux.intel.com: changelog edits] Signed-off-by: Srinivas Pandruvada --- .../x86/intel-speed-select/isst-core-mbox.c | 60 +++++++++++++++++++ .../power/x86/intel-speed-select/isst-core.c | 59 ------------------ 2 files changed, 60 insertions(+), 59 deletions(-) diff --git a/tools/power/x86/intel-speed-select/isst-core-mbox.c b/tools/power/x86/intel-speed-select/isst-core-mbox.c index 466ed0934b42..8d62bafcaca5 100644 --- a/tools/power/x86/intel-speed-select/isst-core-mbox.c +++ b/tools/power/x86/intel-speed-select/isst-core-mbox.c @@ -148,6 +148,62 @@ try_uncore_mbox: ctdp_level->uncore_p1); } +static void _get_p1_info(struct isst_id *id, int config_index, + struct isst_pkg_ctdp_level_info *ctdp_level) +{ + unsigned int resp; + int ret; + ret = isst_send_mbox_command(id->cpu, CONFIG_TDP, CONFIG_TDP_GET_P1_INFO, 0, + config_index, &resp); + if (ret) { + ctdp_level->sse_p1 = 0; + ctdp_level->avx2_p1 = 0; + ctdp_level->avx512_p1 = 0; + return; + } + + ctdp_level->sse_p1 = resp & GENMASK(7, 0); + ctdp_level->avx2_p1 = (resp & GENMASK(15, 8)) >> 8; + ctdp_level->avx512_p1 = (resp & GENMASK(23, 16)) >> 16; + debug_printf( + "cpu:%d ctdp:%d CONFIG_TDP_GET_P1_INFO resp:%x sse_p1:%d avx2_p1:%d avx512_p1:%d\n", + id->cpu, config_index, resp, ctdp_level->sse_p1, + ctdp_level->avx2_p1, ctdp_level->avx512_p1); +} + +static void _get_uncore_mem_freq(struct isst_id *id, int config_index, + struct isst_pkg_ctdp_level_info *ctdp_level) +{ + unsigned int resp; + int ret; + + ret = isst_send_mbox_command(id->cpu, CONFIG_TDP, CONFIG_TDP_GET_MEM_FREQ, + 0, config_index, &resp); + if (ret) { + ctdp_level->mem_freq = 0; + return; + } + + ctdp_level->mem_freq = resp & GENMASK(7, 0); + if (is_spr_platform()) { + ctdp_level->mem_freq *= 200; + } else if (is_icx_platform()) { + if (ctdp_level->mem_freq < 7) { + ctdp_level->mem_freq = (12 - ctdp_level->mem_freq) * 133.33 * 2 * 10; + ctdp_level->mem_freq /= 10; + if (ctdp_level->mem_freq % 10 > 5) + ctdp_level->mem_freq++; + } else { + ctdp_level->mem_freq = 0; + } + } else { + ctdp_level->mem_freq = 0; + } + debug_printf( + "cpu:%d ctdp:%d CONFIG_TDP_GET_MEM_FREQ resp:%x uncore mem_freq:%d\n", + id->cpu, config_index, resp, ctdp_level->mem_freq); +} + static int mbox_get_tdp_info(struct isst_id *id, int config_index, struct isst_pkg_ctdp_level_info *ctdp_level) { @@ -176,6 +232,10 @@ static int mbox_get_tdp_info(struct isst_id *id, int config_index, ctdp_level->t_proc_hot = resp & GENMASK(7, 0); + mbox_get_uncore_p0_p1_info(id, config_index, ctdp_level); + _get_p1_info(id, config_index, ctdp_level); + _get_uncore_mem_freq(id, config_index, ctdp_level); + debug_printf( "cpu:%d ctdp:%d CONFIG_TDP_GET_TJMAX_INFO resp:%x t_proc_hot:%d\n", id->cpu, config_index, resp, ctdp_level->t_proc_hot); diff --git a/tools/power/x86/intel-speed-select/isst-core.c b/tools/power/x86/intel-speed-select/isst-core.c index 5d0db8424361..27049e283eb3 100644 --- a/tools/power/x86/intel-speed-select/isst-core.c +++ b/tools/power/x86/intel-speed-select/isst-core.c @@ -525,62 +525,6 @@ void isst_get_uncore_p0_p1_info(struct isst_id *id, int config_index, return isst_ops->get_uncore_p0_p1_info(id, config_index, ctdp_level); } -void isst_get_p1_info(struct isst_id *id, int config_index, - struct isst_pkg_ctdp_level_info *ctdp_level) -{ - unsigned int resp; - int ret; - ret = isst_send_mbox_command(id->cpu, CONFIG_TDP, CONFIG_TDP_GET_P1_INFO, 0, - config_index, &resp); - if (ret) { - ctdp_level->sse_p1 = 0; - ctdp_level->avx2_p1 = 0; - ctdp_level->avx512_p1 = 0; - return; - } - - ctdp_level->sse_p1 = resp & GENMASK(7, 0); - ctdp_level->avx2_p1 = (resp & GENMASK(15, 8)) >> 8; - ctdp_level->avx512_p1 = (resp & GENMASK(23, 16)) >> 16; - debug_printf( - "cpu:%d ctdp:%d CONFIG_TDP_GET_P1_INFO resp:%x sse_p1:%d avx2_p1:%d avx512_p1:%d\n", - id->cpu, config_index, resp, ctdp_level->sse_p1, - ctdp_level->avx2_p1, ctdp_level->avx512_p1); -} - -void isst_get_uncore_mem_freq(struct isst_id *id, int config_index, - struct isst_pkg_ctdp_level_info *ctdp_level) -{ - unsigned int resp; - int ret; - - ret = isst_send_mbox_command(id->cpu, CONFIG_TDP, CONFIG_TDP_GET_MEM_FREQ, - 0, config_index, &resp); - if (ret) { - ctdp_level->mem_freq = 0; - return; - } - - ctdp_level->mem_freq = resp & GENMASK(7, 0); - if (is_spr_platform()) { - ctdp_level->mem_freq *= 200; - } else if (is_icx_platform()) { - if (ctdp_level->mem_freq < 7) { - ctdp_level->mem_freq = (12 - ctdp_level->mem_freq) * 133.33 * 2 * 10; - ctdp_level->mem_freq /= 10; - if (ctdp_level->mem_freq % 10 > 5) - ctdp_level->mem_freq++; - } else { - ctdp_level->mem_freq = 0; - } - } else { - ctdp_level->mem_freq = 0; - } - debug_printf( - "cpu:%d ctdp:%d CONFIG_TDP_GET_MEM_FREQ resp:%x uncore mem_freq:%d\n", - id->cpu, config_index, resp, ctdp_level->mem_freq); -} - int isst_get_process_ctdp(struct isst_id *id, int tdp_level, struct isst_pkg_ctdp *pkg_dev) { int i, ret, valid = 0; @@ -680,9 +624,6 @@ int isst_get_process_ctdp(struct isst_id *id, int tdp_level, struct isst_pkg_ctd if (ret) return ret; } - isst_get_uncore_p0_p1_info(id, i, ctdp_level); - isst_get_p1_info(id, i, ctdp_level); - isst_get_uncore_mem_freq(id, i, ctdp_level); } if (!valid) From 4a17b29188734f6ec4d9835d74a7489e3409c692 Mon Sep 17 00:00:00 2001 From: Zhang Rui Date: Tue, 9 Aug 2022 01:52:11 +0800 Subject: [PATCH 107/178] tools/power/x86/intel-speed-select: Abstract get_get_trls Allow platform specific implementation to get turbo ratio limits of each AVX level, for a selected SST-PP level. No functional changes are expected. Signed-off-by: Zhang Rui [srinivas.pandruvada@linux.intel.com: changelog edits] Signed-off-by: Srinivas Pandruvada --- .../x86/intel-speed-select/isst-core-mbox.c | 14 ++++++++++++++ tools/power/x86/intel-speed-select/isst-core.c | 17 +++++++++-------- tools/power/x86/intel-speed-select/isst.h | 1 + 3 files changed, 24 insertions(+), 8 deletions(-) diff --git a/tools/power/x86/intel-speed-select/isst-core-mbox.c b/tools/power/x86/intel-speed-select/isst-core-mbox.c index 8d62bafcaca5..2ea7ef4ef446 100644 --- a/tools/power/x86/intel-speed-select/isst-core-mbox.c +++ b/tools/power/x86/intel-speed-select/isst-core-mbox.c @@ -338,6 +338,19 @@ static int mbox_get_get_trl(struct isst_id *id, int level, int avx_level, int *t return 0; } +static int mbox_get_get_trls(struct isst_id *id, int level, struct isst_pkg_ctdp_level_info *ctdp_level) +{ + int trl_max_levels = isst_get_trl_max_levels(); + int i, ret; + + for (i = 0; i < trl_max_levels; i++) { + ret = mbox_get_get_trl(id, level, i, ctdp_level->trl_ratios[i]); + if (ret) + return ret; + } + return 0; +} + static int mbox_get_trl_bucket_info(struct isst_id *id, int level, unsigned long long *buckets_info) { int ret; @@ -598,6 +611,7 @@ static struct isst_platform_ops mbox_ops = { .get_pwr_info = mbox_get_pwr_info, .get_coremask_info = mbox_get_coremask_info, .get_get_trl = mbox_get_get_trl, + .get_get_trls = mbox_get_get_trls, .get_trl_bucket_info = mbox_get_trl_bucket_info, .set_tdp_level = mbox_set_tdp_level, .get_pbf_info = mbox_get_pbf_info, diff --git a/tools/power/x86/intel-speed-select/isst-core.c b/tools/power/x86/intel-speed-select/isst-core.c index 27049e283eb3..f4e3a91dc4c2 100644 --- a/tools/power/x86/intel-speed-select/isst-core.c +++ b/tools/power/x86/intel-speed-select/isst-core.c @@ -346,6 +346,12 @@ int isst_get_get_trl(struct isst_id *id, int level, int avx_level, int *trl) return isst_ops->get_get_trl(id, level, avx_level, trl); } +int isst_get_get_trls(struct isst_id *id, int level, struct isst_pkg_ctdp_level_info *ctdp_level) +{ + CHECK_CB(get_get_trls); + return isst_ops->get_get_trls(id, level, ctdp_level); +} + int isst_get_trl_bucket_info(struct isst_id *id, int level, unsigned long long *buckets_info) { CHECK_CB(get_trl_bucket_info); @@ -550,8 +556,6 @@ int isst_get_process_ctdp(struct isst_id *id, int tdp_level, struct isst_pkg_ctd for (i = 0; i <= pkg_dev->levels; ++i) { struct isst_pkg_ctdp_level_info *ctdp_level; - int trl_max_levels = isst_get_trl_max_levels(); - int j; if (tdp_level != 0xff && i != tdp_level) continue; @@ -618,12 +622,9 @@ int isst_get_process_ctdp(struct isst_id *id, int tdp_level, struct isst_pkg_ctd if (ret) return ret; - for (j = 0; j < trl_max_levels; j++) { - ret = isst_get_get_trl(id, i, j, - ctdp_level->trl_ratios[j]); - if (ret) - return ret; - } + ret = isst_get_get_trls(id, i, ctdp_level); + if (ret) + return ret; } if (!valid) diff --git a/tools/power/x86/intel-speed-select/isst.h b/tools/power/x86/intel-speed-select/isst.h index e113a2bef404..b17fcf737560 100644 --- a/tools/power/x86/intel-speed-select/isst.h +++ b/tools/power/x86/intel-speed-select/isst.h @@ -192,6 +192,7 @@ struct isst_platform_ops { int (*get_pwr_info)(struct isst_id *id, int config_index, struct isst_pkg_ctdp_level_info *ctdp_level); int (*get_coremask_info)(struct isst_id *id, int config_index, struct isst_pkg_ctdp_level_info *ctdp_level); int (*get_get_trl)(struct isst_id *id, int level, int avx_level, int *trl); + int (*get_get_trls)(struct isst_id *id, int level, struct isst_pkg_ctdp_level_info *ctdp_level); int (*get_trl_bucket_info)(struct isst_id *id, int level, unsigned long long *buckets_info); int (*set_tdp_level)(struct isst_id *id, int tdp_level); int (*get_pbf_info)(struct isst_id *id, int level, struct isst_pbf_info *pbf_info); From a07bdb81c1434f41eb444e51c2a4028ca2d21b30 Mon Sep 17 00:00:00 2001 From: Zhang Rui Date: Mon, 8 Aug 2022 22:42:47 +0800 Subject: [PATCH 108/178] tools/power/x86/intel-speed-select: Abstract get_clos_information Allow platform specific implementation to get CLOS config setting. No functional changes are expected. Signed-off-by: Zhang Rui [srinivas.pandruvada@linux.intel.com: changelog edits] Signed-off-by: Srinivas Pandruvada --- .../x86/intel-speed-select/isst-core-mbox.c | 25 +++++++++++++++++++ .../power/x86/intel-speed-select/isst-core.c | 23 ++--------------- tools/power/x86/intel-speed-select/isst.h | 1 + 3 files changed, 28 insertions(+), 21 deletions(-) diff --git a/tools/power/x86/intel-speed-select/isst-core-mbox.c b/tools/power/x86/intel-speed-select/isst-core-mbox.c index 2ea7ef4ef446..a96d6bcf7d93 100644 --- a/tools/power/x86/intel-speed-select/isst-core-mbox.c +++ b/tools/power/x86/intel-speed-select/isst-core-mbox.c @@ -600,6 +600,30 @@ static int mbox_get_fact_info(struct isst_id *id, int level, int fact_bucket, st return 0; } +static int mbox_get_clos_information(struct isst_id *id, int *enable, int *type) +{ + unsigned int resp; + int ret; + + ret = isst_send_mbox_command(id->cpu, CONFIG_CLOS, CLOS_PM_QOS_CONFIG, 0, 0, + &resp); + if (ret) + return ret; + + debug_printf("cpu:%d CLOS_PM_QOS_CONFIG resp:%x\n", id->cpu, resp); + + if (resp & BIT(1)) + *enable = 1; + else + *enable = 0; + + if (resp & BIT(2)) + *type = 1; + else + *type = 0; + + return 0; +} static struct isst_platform_ops mbox_ops = { .get_disp_freq_multiplier = mbox_get_disp_freq_multiplier, .get_trl_max_levels = mbox_get_trl_max_levels, @@ -618,6 +642,7 @@ static struct isst_platform_ops mbox_ops = { .set_pbf_fact_status = mbox_set_pbf_fact_status, .get_fact_info = mbox_get_fact_info, .get_uncore_p0_p1_info = mbox_get_uncore_p0_p1_info, + .get_clos_information = mbox_get_clos_information, }; struct isst_platform_ops *mbox_get_platform_ops(void) diff --git a/tools/power/x86/intel-speed-select/isst-core.c b/tools/power/x86/intel-speed-select/isst-core.c index f4e3a91dc4c2..33ee086458e9 100644 --- a/tools/power/x86/intel-speed-select/isst-core.c +++ b/tools/power/x86/intel-speed-select/isst-core.c @@ -635,27 +635,8 @@ int isst_get_process_ctdp(struct isst_id *id, int tdp_level, struct isst_pkg_ctd int isst_clos_get_clos_information(struct isst_id *id, int *enable, int *type) { - unsigned int resp; - int ret; - - ret = isst_send_mbox_command(id->cpu, CONFIG_CLOS, CLOS_PM_QOS_CONFIG, 0, 0, - &resp); - if (ret) - return ret; - - debug_printf("cpu:%d CLOS_PM_QOS_CONFIG resp:%x\n", id->cpu, resp); - - if (resp & BIT(1)) - *enable = 1; - else - *enable = 0; - - if (resp & BIT(2)) - *type = 1; - else - *type = 0; - - return 0; + CHECK_CB(get_clos_information); + return isst_ops->get_clos_information(id, enable, type); } int isst_pm_qos_config(struct isst_id *id, int enable_clos, int priority_type) diff --git a/tools/power/x86/intel-speed-select/isst.h b/tools/power/x86/intel-speed-select/isst.h index b17fcf737560..d6cbc0bcc1e3 100644 --- a/tools/power/x86/intel-speed-select/isst.h +++ b/tools/power/x86/intel-speed-select/isst.h @@ -199,6 +199,7 @@ struct isst_platform_ops { int (*set_pbf_fact_status)(struct isst_id *id, int pbf, int enable); int (*get_fact_info)(struct isst_id *id, int level, int fact_bucket, struct isst_fact_info *fact_info); void (*get_uncore_p0_p1_info)(struct isst_id *id, int config_index, struct isst_pkg_ctdp_level_info *ctdp_level); + int (*get_clos_information)(struct isst_id *id, int *enable, int *type); }; extern int is_cpu_in_power_domain(int cpu, struct isst_id *id); From 904d2baa8b04691613a5e1e94061f99f0db2832c Mon Sep 17 00:00:00 2001 From: Zhang Rui Date: Mon, 8 Aug 2022 22:46:57 +0800 Subject: [PATCH 109/178] tools/power/x86/intel-speed-select: Abstract pm_qos_config Allow platform specific implementation to set CLOS config settings. No functional changes are expected. Signed-off-by: Zhang Rui [srinivas.pandruvada@linux.intel.com: changelog edits] Signed-off-by: Srinivas Pandruvada --- .../x86/intel-speed-select/isst-core-mbox.c | 90 +++++++++++++++++++ .../power/x86/intel-speed-select/isst-core.c | 86 +----------------- tools/power/x86/intel-speed-select/isst.h | 1 + 3 files changed, 93 insertions(+), 84 deletions(-) diff --git a/tools/power/x86/intel-speed-select/isst-core-mbox.c b/tools/power/x86/intel-speed-select/isst-core-mbox.c index a96d6bcf7d93..27d802173d10 100644 --- a/tools/power/x86/intel-speed-select/isst-core-mbox.c +++ b/tools/power/x86/intel-speed-select/isst-core-mbox.c @@ -624,6 +624,95 @@ static int mbox_get_clos_information(struct isst_id *id, int *enable, int *type) return 0; } + +static int _write_pm_config(struct isst_id *id, int cp_state) +{ + unsigned int req, resp; + int ret; + + if (cp_state) + req = BIT(16); + else + req = 0; + + ret = isst_send_mbox_command(id->cpu, WRITE_PM_CONFIG, PM_FEATURE, 0, req, + &resp); + if (ret) + return ret; + + debug_printf("cpu:%d WRITE_PM_CONFIG resp:%x\n", id->cpu, resp); + + return 0; +} + +static int mbox_pm_qos_config(struct isst_id *id, int enable_clos, int priority_type) +{ + unsigned int req, resp; + int ret; + + if (!enable_clos) { + struct isst_pkg_ctdp pkg_dev; + struct isst_pkg_ctdp_level_info ctdp_level; + + ret = isst_get_ctdp_levels(id, &pkg_dev); + if (ret) { + debug_printf("isst_get_ctdp_levels\n"); + return ret; + } + + ret = isst_get_ctdp_control(id, pkg_dev.current_level, + &ctdp_level); + if (ret) + return ret; + + if (ctdp_level.fact_enabled) { + isst_display_error_info_message(1, "Ignoring request, turbo-freq feature is still enabled", 0, 0); + return -EINVAL; + } + ret = _write_pm_config(id, 0); + if (ret) + isst_display_error_info_message(0, "WRITE_PM_CONFIG command failed, ignoring error", 0, 0); + } else { + ret = _write_pm_config(id, 1); + if (ret) + isst_display_error_info_message(0, "WRITE_PM_CONFIG command failed, ignoring error", 0, 0); + } + + ret = isst_send_mbox_command(id->cpu, CONFIG_CLOS, CLOS_PM_QOS_CONFIG, 0, 0, + &resp); + if (ret) { + isst_display_error_info_message(1, "CLOS_PM_QOS_CONFIG command failed", 0, 0); + return ret; + } + + debug_printf("cpu:%d CLOS_PM_QOS_CONFIG resp:%x\n", id->cpu, resp); + + req = resp; + + if (enable_clos) + req = req | BIT(1); + else + req = req & ~BIT(1); + + if (priority_type > 1) + isst_display_error_info_message(1, "Invalid priority type: Changing type to ordered", 0, 0); + + if (priority_type) + req = req | BIT(2); + else + req = req & ~BIT(2); + + ret = isst_send_mbox_command(id->cpu, CONFIG_CLOS, CLOS_PM_QOS_CONFIG, + BIT(MBOX_CMD_WRITE_BIT), req, &resp); + if (ret) + return ret; + + debug_printf("cpu:%d CLOS_PM_QOS_CONFIG priority type:%d req:%x\n", id->cpu, + priority_type, req); + + return 0; +} + static struct isst_platform_ops mbox_ops = { .get_disp_freq_multiplier = mbox_get_disp_freq_multiplier, .get_trl_max_levels = mbox_get_trl_max_levels, @@ -643,6 +732,7 @@ static struct isst_platform_ops mbox_ops = { .get_fact_info = mbox_get_fact_info, .get_uncore_p0_p1_info = mbox_get_uncore_p0_p1_info, .get_clos_information = mbox_get_clos_information, + .pm_qos_config = mbox_pm_qos_config, }; struct isst_platform_ops *mbox_get_platform_ops(void) diff --git a/tools/power/x86/intel-speed-select/isst-core.c b/tools/power/x86/intel-speed-select/isst-core.c index 33ee086458e9..39b9842ee666 100644 --- a/tools/power/x86/intel-speed-select/isst-core.c +++ b/tools/power/x86/intel-speed-select/isst-core.c @@ -247,26 +247,6 @@ int isst_send_msr_command(unsigned int cpu, unsigned int msr, int write, return 0; } -int isst_write_pm_config(struct isst_id *id, int cp_state) -{ - unsigned int req, resp; - int ret; - - if (cp_state) - req = BIT(16); - else - req = 0; - - ret = isst_send_mbox_command(id->cpu, WRITE_PM_CONFIG, PM_FEATURE, 0, req, - &resp); - if (ret) - return ret; - - debug_printf("cpu:%d WRITE_PM_CONFIG resp:%x\n", id->cpu, resp); - - return 0; -} - int isst_read_pm_config(struct isst_id *id, int *cp_state, int *cp_cap) { unsigned int resp; @@ -641,70 +621,8 @@ int isst_clos_get_clos_information(struct isst_id *id, int *enable, int *type) int isst_pm_qos_config(struct isst_id *id, int enable_clos, int priority_type) { - unsigned int req, resp; - int ret; - - if (!enable_clos) { - struct isst_pkg_ctdp pkg_dev; - struct isst_pkg_ctdp_level_info ctdp_level; - - ret = isst_get_ctdp_levels(id, &pkg_dev); - if (ret) { - debug_printf("isst_get_ctdp_levels\n"); - return ret; - } - - ret = isst_get_ctdp_control(id, pkg_dev.current_level, - &ctdp_level); - if (ret) - return ret; - - if (ctdp_level.fact_enabled) { - isst_display_error_info_message(1, "Ignoring request, turbo-freq feature is still enabled", 0, 0); - return -EINVAL; - } - ret = isst_write_pm_config(id, 0); - if (ret) - isst_display_error_info_message(0, "WRITE_PM_CONFIG command failed, ignoring error", 0, 0); - } else { - ret = isst_write_pm_config(id, 1); - if (ret) - isst_display_error_info_message(0, "WRITE_PM_CONFIG command failed, ignoring error", 0, 0); - } - - ret = isst_send_mbox_command(id->cpu, CONFIG_CLOS, CLOS_PM_QOS_CONFIG, 0, 0, - &resp); - if (ret) { - isst_display_error_info_message(1, "CLOS_PM_QOS_CONFIG command failed", 0, 0); - return ret; - } - - debug_printf("cpu:%d CLOS_PM_QOS_CONFIG resp:%x\n", id->cpu, resp); - - req = resp; - - if (enable_clos) - req = req | BIT(1); - else - req = req & ~BIT(1); - - if (priority_type > 1) - isst_display_error_info_message(1, "Invalid priority type: Changing type to ordered", 0, 0); - - if (priority_type) - req = req | BIT(2); - else - req = req & ~BIT(2); - - ret = isst_send_mbox_command(id->cpu, CONFIG_CLOS, CLOS_PM_QOS_CONFIG, - BIT(MBOX_CMD_WRITE_BIT), req, &resp); - if (ret) - return ret; - - debug_printf("cpu:%d CLOS_PM_QOS_CONFIG priority type:%d req:%x\n", id->cpu, - priority_type, req); - - return 0; + CHECK_CB(pm_qos_config); + return isst_ops->pm_qos_config(id, enable_clos, priority_type); } int isst_pm_get_clos(struct isst_id *id, int clos, struct isst_clos_config *clos_config) diff --git a/tools/power/x86/intel-speed-select/isst.h b/tools/power/x86/intel-speed-select/isst.h index d6cbc0bcc1e3..1f60acef0950 100644 --- a/tools/power/x86/intel-speed-select/isst.h +++ b/tools/power/x86/intel-speed-select/isst.h @@ -200,6 +200,7 @@ struct isst_platform_ops { int (*get_fact_info)(struct isst_id *id, int level, int fact_bucket, struct isst_fact_info *fact_info); void (*get_uncore_p0_p1_info)(struct isst_id *id, int config_index, struct isst_pkg_ctdp_level_info *ctdp_level); int (*get_clos_information)(struct isst_id *id, int *enable, int *type); + int (*pm_qos_config)(struct isst_id *id, int enable_clos, int priority_type); }; extern int is_cpu_in_power_domain(int cpu, struct isst_id *id); From 43314e798c92b93b6f899c4dc9e027652990bdbc Mon Sep 17 00:00:00 2001 From: Zhang Rui Date: Mon, 8 Aug 2022 22:50:28 +0800 Subject: [PATCH 110/178] tools/power/x86/intel-speed-select: Abstract pm_get_clos Allow platform specific implementation to get CLOS priority setting. No functional changes are expected. Signed-off-by: Zhang Rui [srinivas.pandruvada@linux.intel.com: changelog edits] Signed-off-by: Srinivas Pandruvada --- .../x86/intel-speed-select/isst-core-mbox.c | 20 +++++++++++++++++++ .../power/x86/intel-speed-select/isst-core.c | 17 ++-------------- tools/power/x86/intel-speed-select/isst.h | 1 + 3 files changed, 23 insertions(+), 15 deletions(-) diff --git a/tools/power/x86/intel-speed-select/isst-core-mbox.c b/tools/power/x86/intel-speed-select/isst-core-mbox.c index 27d802173d10..6a6298e3a895 100644 --- a/tools/power/x86/intel-speed-select/isst-core-mbox.c +++ b/tools/power/x86/intel-speed-select/isst-core-mbox.c @@ -713,6 +713,25 @@ static int mbox_pm_qos_config(struct isst_id *id, int enable_clos, int priority_ return 0; } +static int mbox_pm_get_clos(struct isst_id *id, int clos, struct isst_clos_config *clos_config) +{ + unsigned int resp; + int ret; + + ret = isst_send_mbox_command(id->cpu, CONFIG_CLOS, CLOS_PM_CLOS, clos, 0, + &resp); + if (ret) + return ret; + + clos_config->epp = resp & 0x0f; + clos_config->clos_prop_prio = (resp >> 4) & 0x0f; + clos_config->clos_min = (resp >> 8) & 0xff; + clos_config->clos_max = (resp >> 16) & 0xff; + clos_config->clos_desired = (resp >> 24) & 0xff; + + return 0; +} + static struct isst_platform_ops mbox_ops = { .get_disp_freq_multiplier = mbox_get_disp_freq_multiplier, .get_trl_max_levels = mbox_get_trl_max_levels, @@ -733,6 +752,7 @@ static struct isst_platform_ops mbox_ops = { .get_uncore_p0_p1_info = mbox_get_uncore_p0_p1_info, .get_clos_information = mbox_get_clos_information, .pm_qos_config = mbox_pm_qos_config, + .pm_get_clos = mbox_pm_get_clos, }; struct isst_platform_ops *mbox_get_platform_ops(void) diff --git a/tools/power/x86/intel-speed-select/isst-core.c b/tools/power/x86/intel-speed-select/isst-core.c index 39b9842ee666..11a07eb4aaf0 100644 --- a/tools/power/x86/intel-speed-select/isst-core.c +++ b/tools/power/x86/intel-speed-select/isst-core.c @@ -627,21 +627,8 @@ int isst_pm_qos_config(struct isst_id *id, int enable_clos, int priority_type) int isst_pm_get_clos(struct isst_id *id, int clos, struct isst_clos_config *clos_config) { - unsigned int resp; - int ret; - - ret = isst_send_mbox_command(id->cpu, CONFIG_CLOS, CLOS_PM_CLOS, clos, 0, - &resp); - if (ret) - return ret; - - clos_config->epp = resp & 0x0f; - clos_config->clos_prop_prio = (resp >> 4) & 0x0f; - clos_config->clos_min = (resp >> 8) & 0xff; - clos_config->clos_max = (resp >> 16) & 0xff; - clos_config->clos_desired = (resp >> 24) & 0xff; - - return 0; + CHECK_CB(pm_get_clos); + return isst_ops->pm_get_clos(id, clos, clos_config); } int isst_set_clos(struct isst_id *id, int clos, struct isst_clos_config *clos_config) diff --git a/tools/power/x86/intel-speed-select/isst.h b/tools/power/x86/intel-speed-select/isst.h index 1f60acef0950..99a0d92d36e1 100644 --- a/tools/power/x86/intel-speed-select/isst.h +++ b/tools/power/x86/intel-speed-select/isst.h @@ -201,6 +201,7 @@ struct isst_platform_ops { void (*get_uncore_p0_p1_info)(struct isst_id *id, int config_index, struct isst_pkg_ctdp_level_info *ctdp_level); int (*get_clos_information)(struct isst_id *id, int *enable, int *type); int (*pm_qos_config)(struct isst_id *id, int enable_clos, int priority_type); + int (*pm_get_clos)(struct isst_id *id, int clos, struct isst_clos_config *clos_config); }; extern int is_cpu_in_power_domain(int cpu, struct isst_id *id); From 33dbf360db5fbc09ea1d4962a964d57b5f6004f8 Mon Sep 17 00:00:00 2001 From: Zhang Rui Date: Mon, 8 Aug 2022 22:53:48 +0800 Subject: [PATCH 111/178] tools/power/x86/intel-speed-select: Abstract set_clos Allow platform specific implementation to set CLOS priority setting. No functional changes are expected. Signed-off-by: Zhang Rui [srinivas.pandruvada@linux.intel.com: changelog edits] Signed-off-by: Srinivas Pandruvada --- .../x86/intel-speed-select/isst-core-mbox.c | 25 +++++++++++++++++++ .../power/x86/intel-speed-select/isst-core.c | 22 ++-------------- tools/power/x86/intel-speed-select/isst.h | 1 + 3 files changed, 28 insertions(+), 20 deletions(-) diff --git a/tools/power/x86/intel-speed-select/isst-core-mbox.c b/tools/power/x86/intel-speed-select/isst-core-mbox.c index 6a6298e3a895..3490315034d0 100644 --- a/tools/power/x86/intel-speed-select/isst-core-mbox.c +++ b/tools/power/x86/intel-speed-select/isst-core-mbox.c @@ -732,6 +732,30 @@ static int mbox_pm_get_clos(struct isst_id *id, int clos, struct isst_clos_confi return 0; } +static int mbox_set_clos(struct isst_id *id, int clos, struct isst_clos_config *clos_config) +{ + unsigned int req, resp; + unsigned int param; + int ret; + + req = clos_config->epp & 0x0f; + req |= (clos_config->clos_prop_prio & 0x0f) << 4; + req |= (clos_config->clos_min & 0xff) << 8; + req |= (clos_config->clos_max & 0xff) << 16; + req |= (clos_config->clos_desired & 0xff) << 24; + + param = BIT(MBOX_CMD_WRITE_BIT) | clos; + + ret = isst_send_mbox_command(id->cpu, CONFIG_CLOS, CLOS_PM_CLOS, param, req, + &resp); + if (ret) + return ret; + + debug_printf("cpu:%d CLOS_PM_CLOS param:%x req:%x\n", id->cpu, param, req); + + return 0; +} + static struct isst_platform_ops mbox_ops = { .get_disp_freq_multiplier = mbox_get_disp_freq_multiplier, .get_trl_max_levels = mbox_get_trl_max_levels, @@ -753,6 +777,7 @@ static struct isst_platform_ops mbox_ops = { .get_clos_information = mbox_get_clos_information, .pm_qos_config = mbox_pm_qos_config, .pm_get_clos = mbox_pm_get_clos, + .set_clos = mbox_set_clos, }; struct isst_platform_ops *mbox_get_platform_ops(void) diff --git a/tools/power/x86/intel-speed-select/isst-core.c b/tools/power/x86/intel-speed-select/isst-core.c index 11a07eb4aaf0..a901c63f7355 100644 --- a/tools/power/x86/intel-speed-select/isst-core.c +++ b/tools/power/x86/intel-speed-select/isst-core.c @@ -633,26 +633,8 @@ int isst_pm_get_clos(struct isst_id *id, int clos, struct isst_clos_config *clos int isst_set_clos(struct isst_id *id, int clos, struct isst_clos_config *clos_config) { - unsigned int req, resp; - unsigned int param; - int ret; - - req = clos_config->epp & 0x0f; - req |= (clos_config->clos_prop_prio & 0x0f) << 4; - req |= (clos_config->clos_min & 0xff) << 8; - req |= (clos_config->clos_max & 0xff) << 16; - req |= (clos_config->clos_desired & 0xff) << 24; - - param = BIT(MBOX_CMD_WRITE_BIT) | clos; - - ret = isst_send_mbox_command(id->cpu, CONFIG_CLOS, CLOS_PM_CLOS, param, req, - &resp); - if (ret) - return ret; - - debug_printf("cpu:%d CLOS_PM_CLOS param:%x req:%x\n", id->cpu, param, req); - - return 0; + CHECK_CB(set_clos); + return isst_ops->set_clos(id, clos, clos_config); } int isst_clos_get_assoc_status(struct isst_id *id, int *clos_id) diff --git a/tools/power/x86/intel-speed-select/isst.h b/tools/power/x86/intel-speed-select/isst.h index 99a0d92d36e1..5db3fc5487a6 100644 --- a/tools/power/x86/intel-speed-select/isst.h +++ b/tools/power/x86/intel-speed-select/isst.h @@ -202,6 +202,7 @@ struct isst_platform_ops { int (*get_clos_information)(struct isst_id *id, int *enable, int *type); int (*pm_qos_config)(struct isst_id *id, int enable_clos, int priority_type); int (*pm_get_clos)(struct isst_id *id, int clos, struct isst_clos_config *clos_config); + int (*set_clos)(struct isst_id *id, int clos, struct isst_clos_config *clos_config); }; extern int is_cpu_in_power_domain(int cpu, struct isst_id *id); From b161bbad6db9a1e8d7cada5bf91b21340af7b163 Mon Sep 17 00:00:00 2001 From: Zhang Rui Date: Mon, 8 Aug 2022 22:55:51 +0800 Subject: [PATCH 112/178] tools/power/x86/intel-speed-select: Abstract clos_get_assoc_status Allow platform specific implementation to get per core CLOS setting. No functional changes are expected. Signed-off-by: Zhang Rui [srinivas.pandruvada@linux.intel.com: changelog edits] Signed-off-by: Srinivas Pandruvada --- .../x86/intel-speed-select/isst-core-mbox.c | 21 +++++++++++++++++++ .../power/x86/intel-speed-select/isst-core.c | 19 ++--------------- tools/power/x86/intel-speed-select/isst.h | 1 + 3 files changed, 24 insertions(+), 17 deletions(-) diff --git a/tools/power/x86/intel-speed-select/isst-core-mbox.c b/tools/power/x86/intel-speed-select/isst-core-mbox.c index 3490315034d0..4e86cce92585 100644 --- a/tools/power/x86/intel-speed-select/isst-core-mbox.c +++ b/tools/power/x86/intel-speed-select/isst-core-mbox.c @@ -756,6 +756,26 @@ static int mbox_set_clos(struct isst_id *id, int clos, struct isst_clos_config * return 0; } +static int mbox_clos_get_assoc_status(struct isst_id *id, int *clos_id) +{ + unsigned int resp; + unsigned int param; + int core_id, ret; + + core_id = find_phy_core_num(id->cpu); + param = core_id; + + ret = isst_send_mbox_command(id->cpu, CONFIG_CLOS, CLOS_PQR_ASSOC, param, 0, + &resp); + if (ret) + return ret; + + debug_printf("cpu:%d CLOS_PQR_ASSOC param:%x resp:%x\n", id->cpu, param, + resp); + *clos_id = (resp >> 16) & 0x03; + + return 0; +} static struct isst_platform_ops mbox_ops = { .get_disp_freq_multiplier = mbox_get_disp_freq_multiplier, .get_trl_max_levels = mbox_get_trl_max_levels, @@ -778,6 +798,7 @@ static struct isst_platform_ops mbox_ops = { .pm_qos_config = mbox_pm_qos_config, .pm_get_clos = mbox_pm_get_clos, .set_clos = mbox_set_clos, + .clos_get_assoc_status = mbox_clos_get_assoc_status, }; struct isst_platform_ops *mbox_get_platform_ops(void) diff --git a/tools/power/x86/intel-speed-select/isst-core.c b/tools/power/x86/intel-speed-select/isst-core.c index a901c63f7355..c00622461c62 100644 --- a/tools/power/x86/intel-speed-select/isst-core.c +++ b/tools/power/x86/intel-speed-select/isst-core.c @@ -639,23 +639,8 @@ int isst_set_clos(struct isst_id *id, int clos, struct isst_clos_config *clos_co int isst_clos_get_assoc_status(struct isst_id *id, int *clos_id) { - unsigned int resp; - unsigned int param; - int core_id, ret; - - core_id = find_phy_core_num(id->cpu); - param = core_id; - - ret = isst_send_mbox_command(id->cpu, CONFIG_CLOS, CLOS_PQR_ASSOC, param, 0, - &resp); - if (ret) - return ret; - - debug_printf("cpu:%d CLOS_PQR_ASSOC param:%x resp:%x\n", id->cpu, param, - resp); - *clos_id = (resp >> 16) & 0x03; - - return 0; + CHECK_CB(clos_get_assoc_status); + return isst_ops->clos_get_assoc_status(id, clos_id); } int isst_clos_associate(struct isst_id *id, int clos_id) diff --git a/tools/power/x86/intel-speed-select/isst.h b/tools/power/x86/intel-speed-select/isst.h index 5db3fc5487a6..5794b31beafc 100644 --- a/tools/power/x86/intel-speed-select/isst.h +++ b/tools/power/x86/intel-speed-select/isst.h @@ -203,6 +203,7 @@ struct isst_platform_ops { int (*pm_qos_config)(struct isst_id *id, int enable_clos, int priority_type); int (*pm_get_clos)(struct isst_id *id, int clos, struct isst_clos_config *clos_config); int (*set_clos)(struct isst_id *id, int clos, struct isst_clos_config *clos_config); + int (*clos_get_assoc_status)(struct isst_id *id, int *clos_id); }; extern int is_cpu_in_power_domain(int cpu, struct isst_id *id); From a59a6c0cadd5f0d9a6837b7f62300f7985ea0de3 Mon Sep 17 00:00:00 2001 From: Zhang Rui Date: Mon, 8 Aug 2022 22:57:55 +0800 Subject: [PATCH 113/178] tools/power/x86/intel-speed-select: Abstract clos_associate Allow platform specific implementation to set per core CLOS setting. No functional changes are expected. Signed-off-by: Zhang Rui [srinivas.pandruvada@linux.intel.com: changelog edits] Signed-off-by: Srinivas Pandruvada --- .../x86/intel-speed-select/isst-core-mbox.c | 23 +++++++++++++++++++ .../power/x86/intel-speed-select/isst-core.c | 18 ++------------- tools/power/x86/intel-speed-select/isst.h | 1 + 3 files changed, 26 insertions(+), 16 deletions(-) diff --git a/tools/power/x86/intel-speed-select/isst-core-mbox.c b/tools/power/x86/intel-speed-select/isst-core-mbox.c index 4e86cce92585..ca43cfc48b17 100644 --- a/tools/power/x86/intel-speed-select/isst-core-mbox.c +++ b/tools/power/x86/intel-speed-select/isst-core-mbox.c @@ -776,6 +776,28 @@ static int mbox_clos_get_assoc_status(struct isst_id *id, int *clos_id) return 0; } + +static int mbox_clos_associate(struct isst_id *id, int clos_id) +{ + unsigned int req, resp; + unsigned int param; + int core_id, ret; + + req = (clos_id & 0x03) << 16; + core_id = find_phy_core_num(id->cpu); + param = BIT(MBOX_CMD_WRITE_BIT) | core_id; + + ret = isst_send_mbox_command(id->cpu, CONFIG_CLOS, CLOS_PQR_ASSOC, param, + req, &resp); + if (ret) + return ret; + + debug_printf("cpu:%d CLOS_PQR_ASSOC param:%x req:%x\n", id->cpu, param, + req); + + return 0; +} + static struct isst_platform_ops mbox_ops = { .get_disp_freq_multiplier = mbox_get_disp_freq_multiplier, .get_trl_max_levels = mbox_get_trl_max_levels, @@ -799,6 +821,7 @@ static struct isst_platform_ops mbox_ops = { .pm_get_clos = mbox_pm_get_clos, .set_clos = mbox_set_clos, .clos_get_assoc_status = mbox_clos_get_assoc_status, + .clos_associate = mbox_clos_associate, }; struct isst_platform_ops *mbox_get_platform_ops(void) diff --git a/tools/power/x86/intel-speed-select/isst-core.c b/tools/power/x86/intel-speed-select/isst-core.c index c00622461c62..8ef71a8112be 100644 --- a/tools/power/x86/intel-speed-select/isst-core.c +++ b/tools/power/x86/intel-speed-select/isst-core.c @@ -645,21 +645,7 @@ int isst_clos_get_assoc_status(struct isst_id *id, int *clos_id) int isst_clos_associate(struct isst_id *id, int clos_id) { - unsigned int req, resp; - unsigned int param; - int core_id, ret; + CHECK_CB(clos_associate); + return isst_ops->clos_associate(id, clos_id); - req = (clos_id & 0x03) << 16; - core_id = find_phy_core_num(id->cpu); - param = BIT(MBOX_CMD_WRITE_BIT) | core_id; - - ret = isst_send_mbox_command(id->cpu, CONFIG_CLOS, CLOS_PQR_ASSOC, param, - req, &resp); - if (ret) - return ret; - - debug_printf("cpu:%d CLOS_PQR_ASSOC param:%x req:%x\n", id->cpu, param, - req); - - return 0; } diff --git a/tools/power/x86/intel-speed-select/isst.h b/tools/power/x86/intel-speed-select/isst.h index 5794b31beafc..752991ec7055 100644 --- a/tools/power/x86/intel-speed-select/isst.h +++ b/tools/power/x86/intel-speed-select/isst.h @@ -204,6 +204,7 @@ struct isst_platform_ops { int (*pm_get_clos)(struct isst_id *id, int clos, struct isst_clos_config *clos_config); int (*set_clos)(struct isst_id *id, int clos, struct isst_clos_config *clos_config); int (*clos_get_assoc_status)(struct isst_id *id, int *clos_id); + int (*clos_associate)(struct isst_id *id, int clos_id); }; extern int is_cpu_in_power_domain(int cpu, struct isst_id *id); From 8f54104f7a9548466027e1ee8482b18c2a72656a Mon Sep 17 00:00:00 2001 From: Zhang Rui Date: Mon, 8 Aug 2022 23:19:55 +0800 Subject: [PATCH 114/178] tools/power/x86/intel-speed-select: Abstract read_pm_config Allow platform specific implementation to get SST-CP capability and current state. No functional changes are expected. Signed-off-by: Zhang Rui [srinivas.pandruvada@linux.intel.com: changelog edits] Signed-off-by: Srinivas Pandruvada --- .../x86/intel-speed-select/isst-core-mbox.c | 19 +++++++++++++++++++ .../power/x86/intel-speed-select/isst-core.c | 16 ++-------------- tools/power/x86/intel-speed-select/isst.h | 1 + 3 files changed, 22 insertions(+), 14 deletions(-) diff --git a/tools/power/x86/intel-speed-select/isst-core-mbox.c b/tools/power/x86/intel-speed-select/isst-core-mbox.c index ca43cfc48b17..2169ceddebeb 100644 --- a/tools/power/x86/intel-speed-select/isst-core-mbox.c +++ b/tools/power/x86/intel-speed-select/isst-core-mbox.c @@ -40,6 +40,24 @@ static int mbox_is_punit_valid(struct isst_id *id) return 1; } +static int mbox_read_pm_config(struct isst_id *id, int *cp_state, int *cp_cap) +{ + unsigned int resp; + int ret; + + ret = isst_send_mbox_command(id->cpu, READ_PM_CONFIG, PM_FEATURE, 0, 0, + &resp); + if (ret) + return ret; + + debug_printf("cpu:%d READ_PM_CONFIG resp:%x\n", id->cpu, resp); + + *cp_state = resp & BIT(16); + *cp_cap = resp & BIT(0) ? 1 : 0; + + return 0; +} + static int mbox_get_config_levels(struct isst_id *id, struct isst_pkg_ctdp *pkg_dev) { unsigned int resp; @@ -803,6 +821,7 @@ static struct isst_platform_ops mbox_ops = { .get_trl_max_levels = mbox_get_trl_max_levels, .get_trl_level_name = mbox_get_trl_level_name, .is_punit_valid = mbox_is_punit_valid, + .read_pm_config = mbox_read_pm_config, .get_config_levels = mbox_get_config_levels, .get_ctdp_control = mbox_get_ctdp_control, .get_tdp_info = mbox_get_tdp_info, diff --git a/tools/power/x86/intel-speed-select/isst-core.c b/tools/power/x86/intel-speed-select/isst-core.c index 8ef71a8112be..c31ab8f5f6dc 100644 --- a/tools/power/x86/intel-speed-select/isst-core.c +++ b/tools/power/x86/intel-speed-select/isst-core.c @@ -249,20 +249,8 @@ int isst_send_msr_command(unsigned int cpu, unsigned int msr, int write, int isst_read_pm_config(struct isst_id *id, int *cp_state, int *cp_cap) { - unsigned int resp; - int ret; - - ret = isst_send_mbox_command(id->cpu, READ_PM_CONFIG, PM_FEATURE, 0, 0, - &resp); - if (ret) - return ret; - - debug_printf("cpu:%d READ_PM_CONFIG resp:%x\n", id->cpu, resp); - - *cp_state = resp & BIT(16); - *cp_cap = resp & BIT(0) ? 1 : 0; - - return 0; + CHECK_CB(read_pm_config); + return isst_ops->read_pm_config(id, cp_state, cp_cap); } int isst_get_ctdp_levels(struct isst_id *id, struct isst_pkg_ctdp *pkg_dev) diff --git a/tools/power/x86/intel-speed-select/isst.h b/tools/power/x86/intel-speed-select/isst.h index 752991ec7055..c64af51a44aa 100644 --- a/tools/power/x86/intel-speed-select/isst.h +++ b/tools/power/x86/intel-speed-select/isst.h @@ -186,6 +186,7 @@ struct isst_platform_ops { int (*get_trl_max_levels)(void); char *(*get_trl_level_name)(int level); int (*is_punit_valid)(struct isst_id *id); + int (*read_pm_config)(struct isst_id *id, int *cp_state, int *cp_cap); int (*get_config_levels)(struct isst_id *id, struct isst_pkg_ctdp *pkg_ctdp); int (*get_ctdp_control)(struct isst_id *id, int config_index, struct isst_pkg_ctdp_level_info *ctdp_level); int (*get_tdp_info)(struct isst_id *id, int config_index, struct isst_pkg_ctdp_level_info *ctdp_level); From 73452ccc190a416929ccf7da4061365a5eaff627 Mon Sep 17 00:00:00 2001 From: Zhang Rui Date: Thu, 2 Feb 2023 15:52:42 +0800 Subject: [PATCH 115/178] tools/power/x86/intel-speed-select: Abstract adjust_uncore_freq Allow platform specific implementation to adjust the uncore frequency. No functional changes are expected. Signed-off-by: Zhang Rui [srinivas.pandruvada@linux.intel.com: changelog edits] Signed-off-by: Srinivas Pandruvada --- .../x86/intel-speed-select/isst-config.c | 35 +-------------- .../x86/intel-speed-select/isst-core-mbox.c | 45 +++++++++++++++++-- .../power/x86/intel-speed-select/isst-core.c | 6 +-- tools/power/x86/intel-speed-select/isst.h | 4 +- 4 files changed, 48 insertions(+), 42 deletions(-) diff --git a/tools/power/x86/intel-speed-select/isst-config.c b/tools/power/x86/intel-speed-select/isst-config.c index 71e00d03062f..346f8271b00f 100644 --- a/tools/power/x86/intel-speed-select/isst-config.c +++ b/tools/power/x86/intel-speed-select/isst-config.c @@ -1187,34 +1187,6 @@ static void dump_isst_config(int arg) isst_ctdp_display_information_end(outf); } -static int set_uncore_min_max(struct isst_id *id, int max, int freq) -{ - char buffer[128], freq_str[16]; - int fd, ret, len; - - if (max) - snprintf(buffer, sizeof(buffer), - "/sys/devices/system/cpu/intel_uncore_frequency/package_%02d_die_%02d/max_freq_khz", id->pkg, id->die); - else - snprintf(buffer, sizeof(buffer), - "/sys/devices/system/cpu/intel_uncore_frequency/package_%02d_die_%02d/min_freq_khz", id->pkg, id->die); - - fd = open(buffer, O_WRONLY); - if (fd < 0) - return fd; - - snprintf(freq_str, sizeof(freq_str), "%d", freq); - len = strlen(freq_str); - ret = write(fd, freq_str, len); - if (ret == -1) { - close(fd); - return ret; - } - close(fd); - - return 0; -} - static void adjust_scaling_max_from_base_freq(int cpu); static void set_tdp_level_for_cpu(struct isst_id *id, void *arg1, void *arg2, void *arg3, @@ -1237,12 +1209,7 @@ static void set_tdp_level_for_cpu(struct isst_id *id, void *arg1, void *arg2, vo usleep(2000); /* Adjusting uncore freq */ - isst_get_uncore_p0_p1_info(id, tdp_level, &ctdp_level); - if (ctdp_level.uncore_pm) - set_uncore_min_max(id, 0, ctdp_level.uncore_pm * 100000); - - if (ctdp_level.uncore_p0) - set_uncore_min_max(id, 1, ctdp_level.uncore_p0 * 100000); + isst_adjust_uncore_freq(id, tdp_level, &ctdp_level); fprintf(stderr, "Option is set to online/offline\n"); ctdp_level.core_cpumask_size = diff --git a/tools/power/x86/intel-speed-select/isst-core-mbox.c b/tools/power/x86/intel-speed-select/isst-core-mbox.c index 2169ceddebeb..8e224d154e04 100644 --- a/tools/power/x86/intel-speed-select/isst-core-mbox.c +++ b/tools/power/x86/intel-speed-select/isst-core-mbox.c @@ -120,7 +120,7 @@ static int mbox_get_ctdp_control(struct isst_id *id, int config_index, return 0; } -static void mbox_get_uncore_p0_p1_info(struct isst_id *id, int config_index, +static void _get_uncore_p0_p1_info(struct isst_id *id, int config_index, struct isst_pkg_ctdp_level_info *ctdp_level) { unsigned int resp; @@ -166,6 +166,45 @@ try_uncore_mbox: ctdp_level->uncore_p1); } +static int _set_uncore_min_max(struct isst_id *id, int max, int freq) +{ + char buffer[128], freq_str[16]; + int fd, ret, len; + + if (max) + snprintf(buffer, sizeof(buffer), + "/sys/devices/system/cpu/intel_uncore_frequency/package_%02d_die_%02d/max_freq_khz", id->pkg, id->die); + else + snprintf(buffer, sizeof(buffer), + "/sys/devices/system/cpu/intel_uncore_frequency/package_%02d_die_%02d/min_freq_khz", id->pkg, id->die); + + fd = open(buffer, O_WRONLY); + if (fd < 0) + return fd; + + snprintf(freq_str, sizeof(freq_str), "%d", freq); + len = strlen(freq_str); + ret = write(fd, freq_str, len); + if (ret == -1) { + close(fd); + return ret; + } + close(fd); + + return 0; +} + +static void mbox_adjust_uncore_freq(struct isst_id *id, int config_index, + struct isst_pkg_ctdp_level_info *ctdp_level) +{ + _get_uncore_p0_p1_info(id, config_index, ctdp_level); + if (ctdp_level->uncore_pm) + _set_uncore_min_max(id, 0, ctdp_level->uncore_pm * 100000); + + if (ctdp_level->uncore_p0) + _set_uncore_min_max(id, 1, ctdp_level->uncore_p0 * 100000); +} + static void _get_p1_info(struct isst_id *id, int config_index, struct isst_pkg_ctdp_level_info *ctdp_level) { @@ -250,7 +289,7 @@ static int mbox_get_tdp_info(struct isst_id *id, int config_index, ctdp_level->t_proc_hot = resp & GENMASK(7, 0); - mbox_get_uncore_p0_p1_info(id, config_index, ctdp_level); + _get_uncore_p0_p1_info(id, config_index, ctdp_level); _get_p1_info(id, config_index, ctdp_level); _get_uncore_mem_freq(id, config_index, ctdp_level); @@ -834,7 +873,7 @@ static struct isst_platform_ops mbox_ops = { .get_pbf_info = mbox_get_pbf_info, .set_pbf_fact_status = mbox_set_pbf_fact_status, .get_fact_info = mbox_get_fact_info, - .get_uncore_p0_p1_info = mbox_get_uncore_p0_p1_info, + .adjust_uncore_freq = mbox_adjust_uncore_freq, .get_clos_information = mbox_get_clos_information, .pm_qos_config = mbox_pm_qos_config, .pm_get_clos = mbox_pm_get_clos, diff --git a/tools/power/x86/intel-speed-select/isst-core.c b/tools/power/x86/intel-speed-select/isst-core.c index c31ab8f5f6dc..24b2c041bfe0 100644 --- a/tools/power/x86/intel-speed-select/isst-core.c +++ b/tools/power/x86/intel-speed-select/isst-core.c @@ -492,11 +492,11 @@ void isst_get_process_ctdp_complete(struct isst_id *id, struct isst_pkg_ctdp *pk } } -void isst_get_uncore_p0_p1_info(struct isst_id *id, int config_index, +void isst_adjust_uncore_freq(struct isst_id *id, int config_index, struct isst_pkg_ctdp_level_info *ctdp_level) { - CHECK_CB(get_uncore_p0_p1_info); - return isst_ops->get_uncore_p0_p1_info(id, config_index, ctdp_level); + CHECK_CB(adjust_uncore_freq); + return isst_ops->adjust_uncore_freq(id, config_index, ctdp_level); } int isst_get_process_ctdp(struct isst_id *id, int tdp_level, struct isst_pkg_ctdp *pkg_dev) diff --git a/tools/power/x86/intel-speed-select/isst.h b/tools/power/x86/intel-speed-select/isst.h index c64af51a44aa..8192e683ac92 100644 --- a/tools/power/x86/intel-speed-select/isst.h +++ b/tools/power/x86/intel-speed-select/isst.h @@ -199,7 +199,7 @@ struct isst_platform_ops { int (*get_pbf_info)(struct isst_id *id, int level, struct isst_pbf_info *pbf_info); int (*set_pbf_fact_status)(struct isst_id *id, int pbf, int enable); int (*get_fact_info)(struct isst_id *id, int level, int fact_bucket, struct isst_fact_info *fact_info); - void (*get_uncore_p0_p1_info)(struct isst_id *id, int config_index, struct isst_pkg_ctdp_level_info *ctdp_level); + void (*adjust_uncore_freq)(struct isst_id *id, int config_index, struct isst_pkg_ctdp_level_info *ctdp_level); int (*get_clos_information)(struct isst_id *id, int *enable, int *type); int (*pm_qos_config)(struct isst_id *id, int enable_clos, int priority_type); int (*pm_get_clos)(struct isst_id *id, int clos, struct isst_clos_config *clos_config); @@ -246,7 +246,7 @@ extern int isst_get_ctdp_control(struct isst_id *id, int config_index, struct isst_pkg_ctdp_level_info *ctdp_level); extern int isst_get_coremask_info(struct isst_id *id, int config_index, struct isst_pkg_ctdp_level_info *ctdp_level); -extern void isst_get_uncore_p0_p1_info(struct isst_id *id, int config_index, +extern void isst_adjust_uncore_freq(struct isst_id *id, int config_index, struct isst_pkg_ctdp_level_info *ctdp_level); extern int isst_get_process_ctdp(struct isst_id *id, int tdp_level, struct isst_pkg_ctdp *pkg_dev); From 2b86ed225e657867b405b3e9412e9efefcbe6ffb Mon Sep 17 00:00:00 2001 From: Zhang Rui Date: Sat, 20 Aug 2022 20:14:15 +0800 Subject: [PATCH 116/178] tools/power/x86/intel-speed-select: Move send_mbox_cmd to isst-core-mbox.c After the previous cleanup, there is no user of send_mbox_cmd outside of isst-core-mbox.c. Thus move send_mbox_cmd to isst-core-mbox.c as internal functions. No functional changes are expected. Signed-off-by: Zhang Rui [srinivas.pandruvada@linux.intel.com: changelog edits] Signed-off-by: Srinivas Pandruvada --- .../x86/intel-speed-select/isst-core-mbox.c | 222 +++++++++++++++--- .../power/x86/intel-speed-select/isst-core.c | 160 +------------ 2 files changed, 195 insertions(+), 187 deletions(-) diff --git a/tools/power/x86/intel-speed-select/isst-core-mbox.c b/tools/power/x86/intel-speed-select/isst-core-mbox.c index 8e224d154e04..6eafef925d1a 100644 --- a/tools/power/x86/intel-speed-select/isst-core-mbox.c +++ b/tools/power/x86/intel-speed-select/isst-core-mbox.c @@ -5,6 +5,9 @@ */ #include "isst.h" +static int mbox_delay; +static int mbox_retries = 3; + static int mbox_get_disp_freq_multiplier(void) { return DISP_FREQ_MULTIPLIER; @@ -29,6 +32,20 @@ static char *mbox_get_trl_level_name(int level) } } +static void mbox_update_platform_param(enum isst_platform_param param, int value) +{ + switch (param) { + case ISST_PARAM_MBOX_DELAY: + mbox_delay = value; + break; + case ISST_PARAM_MBOX_RETRIES: + mbox_retries = value; + break; + default: + break; + } +} + static int mbox_is_punit_valid(struct isst_id *id) { if (id->cpu < 0) @@ -40,12 +57,156 @@ static int mbox_is_punit_valid(struct isst_id *id) return 1; } +static int _send_mmio_command(unsigned int cpu, unsigned int reg, int write, + unsigned int *value) +{ + struct isst_if_io_regs io_regs; + const char *pathname = "/dev/isst_interface"; + int cmd; + FILE *outf = get_output_file(); + int fd; + + debug_printf("mmio_cmd cpu:%d reg:%d write:%d\n", cpu, reg, write); + + fd = open(pathname, O_RDWR); + if (fd < 0) + err(-1, "%s open failed", pathname); + + io_regs.req_count = 1; + io_regs.io_reg[0].logical_cpu = cpu; + io_regs.io_reg[0].reg = reg; + cmd = ISST_IF_IO_CMD; + if (write) { + io_regs.io_reg[0].read_write = 1; + io_regs.io_reg[0].value = *value; + } else { + io_regs.io_reg[0].read_write = 0; + } + + if (ioctl(fd, cmd, &io_regs) == -1) { + if (errno == ENOTTY) { + perror("ISST_IF_IO_COMMAND\n"); + fprintf(stderr, "Check presence of kernel modules: isst_if_mmio\n"); + exit(0); + } + fprintf(outf, "Error: mmio_cmd cpu:%d reg:%x read_write:%x\n", + cpu, reg, write); + } else { + if (!write) + *value = io_regs.io_reg[0].value; + + debug_printf( + "mmio_cmd response: cpu:%d reg:%x rd_write:%x resp:%x\n", + cpu, reg, write, *value); + } + + close(fd); + + return 0; +} + +int _send_mbox_command(unsigned int cpu, unsigned char command, + unsigned char sub_command, unsigned int parameter, + unsigned int req_data, unsigned int *resp) +{ + const char *pathname = "/dev/isst_interface"; + int fd, retry; + struct isst_if_mbox_cmds mbox_cmds = { 0 }; + + debug_printf( + "mbox_send: cpu:%d command:%x sub_command:%x parameter:%x req_data:%x\n", + cpu, command, sub_command, parameter, req_data); + + if (!is_skx_based_platform() && command == CONFIG_CLOS && + sub_command != CLOS_PM_QOS_CONFIG) { + unsigned int value; + int write = 0; + int clos_id, core_id, ret = 0; + + debug_printf("CPU %d\n", cpu); + + if (parameter & BIT(MBOX_CMD_WRITE_BIT)) { + value = req_data; + write = 1; + } + + switch (sub_command) { + case CLOS_PQR_ASSOC: + core_id = parameter & 0xff; + ret = _send_mmio_command( + cpu, PQR_ASSOC_OFFSET + core_id * 4, write, + &value); + if (!ret && !write) + *resp = value; + break; + case CLOS_PM_CLOS: + clos_id = parameter & 0x03; + ret = _send_mmio_command( + cpu, PM_CLOS_OFFSET + clos_id * 4, write, + &value); + if (!ret && !write) + *resp = value; + break; + case CLOS_STATUS: + break; + default: + break; + } + return ret; + } + + mbox_cmds.cmd_count = 1; + mbox_cmds.mbox_cmd[0].logical_cpu = cpu; + mbox_cmds.mbox_cmd[0].command = command; + mbox_cmds.mbox_cmd[0].sub_command = sub_command; + mbox_cmds.mbox_cmd[0].parameter = parameter; + mbox_cmds.mbox_cmd[0].req_data = req_data; + + if (mbox_delay) + usleep(mbox_delay * 1000); + + fd = open(pathname, O_RDWR); + if (fd < 0) + err(-1, "%s open failed", pathname); + + retry = mbox_retries; + do { + if (ioctl(fd, ISST_IF_MBOX_COMMAND, &mbox_cmds) == -1) { + if (errno == ENOTTY) { + perror("ISST_IF_MBOX_COMMAND\n"); + fprintf(stderr, "Check presence of kernel modules: isst_if_mbox_pci or isst_if_mbox_msr\n"); + exit(0); + } + debug_printf( + "Error: mbox_cmd cpu:%d command:%x sub_command:%x parameter:%x req_data:%x errorno:%d\n", + cpu, command, sub_command, parameter, req_data, errno); + --retry; + } else { + *resp = mbox_cmds.mbox_cmd[0].resp_data; + debug_printf( + "mbox_cmd response: cpu:%d command:%x sub_command:%x parameter:%x req_data:%x resp:%x\n", + cpu, command, sub_command, parameter, req_data, *resp); + break; + } + } while (retry); + + close(fd); + + if (!retry) { + debug_printf("Failed mbox command even after retries\n"); + return -1; + + } + + return 0; +} + static int mbox_read_pm_config(struct isst_id *id, int *cp_state, int *cp_cap) { unsigned int resp; int ret; - ret = isst_send_mbox_command(id->cpu, READ_PM_CONFIG, PM_FEATURE, 0, 0, + ret = _send_mbox_command(id->cpu, READ_PM_CONFIG, PM_FEATURE, 0, 0, &resp); if (ret) return ret; @@ -63,7 +224,7 @@ static int mbox_get_config_levels(struct isst_id *id, struct isst_pkg_ctdp *pkg_ unsigned int resp; int ret; - ret = isst_send_mbox_command(id->cpu, CONFIG_TDP, + ret = _send_mbox_command(id->cpu, CONFIG_TDP, CONFIG_TDP_GET_LEVELS_INFO, 0, 0, &resp); if (ret) { pkg_dev->levels = 0; @@ -92,7 +253,7 @@ static int mbox_get_ctdp_control(struct isst_id *id, int config_index, unsigned int resp; int ret; - ret = isst_send_mbox_command(id->cpu, CONFIG_TDP, + ret = _send_mbox_command(id->cpu, CONFIG_TDP, CONFIG_TDP_GET_TDP_CONTROL, 0, config_index, &resp); if (ret) @@ -130,7 +291,7 @@ static void _get_uncore_p0_p1_info(struct isst_id *id, int config_index, ctdp_level->uncore_p0 = 0; ctdp_level->uncore_p1 = 0; - ret = isst_send_mbox_command(id->cpu, CONFIG_TDP, + ret = _send_mbox_command(id->cpu, CONFIG_TDP, CONFIG_TDP_GET_RATIO_INFO, 0, (BIT(16) | config_index) , &resp); if (ret) { @@ -149,7 +310,7 @@ static void _get_uncore_p0_p1_info(struct isst_id *id, int config_index, return; try_uncore_mbox: - ret = isst_send_mbox_command(id->cpu, CONFIG_TDP, + ret = _send_mbox_command(id->cpu, CONFIG_TDP, CONFIG_TDP_GET_UNCORE_P0_P1_INFO, 0, config_index, &resp); if (ret) { @@ -210,7 +371,7 @@ static void _get_p1_info(struct isst_id *id, int config_index, { unsigned int resp; int ret; - ret = isst_send_mbox_command(id->cpu, CONFIG_TDP, CONFIG_TDP_GET_P1_INFO, 0, + ret = _send_mbox_command(id->cpu, CONFIG_TDP, CONFIG_TDP_GET_P1_INFO, 0, config_index, &resp); if (ret) { ctdp_level->sse_p1 = 0; @@ -234,7 +395,7 @@ static void _get_uncore_mem_freq(struct isst_id *id, int config_index, unsigned int resp; int ret; - ret = isst_send_mbox_command(id->cpu, CONFIG_TDP, CONFIG_TDP_GET_MEM_FREQ, + ret = _send_mbox_command(id->cpu, CONFIG_TDP, CONFIG_TDP_GET_MEM_FREQ, 0, config_index, &resp); if (ret) { ctdp_level->mem_freq = 0; @@ -267,7 +428,7 @@ static int mbox_get_tdp_info(struct isst_id *id, int config_index, unsigned int resp; int ret; - ret = isst_send_mbox_command(id->cpu, CONFIG_TDP, CONFIG_TDP_GET_TDP_INFO, + ret = _send_mbox_command(id->cpu, CONFIG_TDP, CONFIG_TDP_GET_TDP_INFO, 0, config_index, &resp); if (ret) { isst_display_error_info_message(1, "Invalid level, Can't get TDP information at level", 1, config_index); @@ -282,7 +443,7 @@ static int mbox_get_tdp_info(struct isst_id *id, int config_index, id->cpu, config_index, resp, ctdp_level->tdp_ratio, ctdp_level->pkg_tdp); - ret = isst_send_mbox_command(id->cpu, CONFIG_TDP, CONFIG_TDP_GET_TJMAX_INFO, + ret = _send_mbox_command(id->cpu, CONFIG_TDP, CONFIG_TDP_GET_TJMAX_INFO, 0, config_index, &resp); if (ret) return ret; @@ -306,7 +467,7 @@ static int mbox_get_pwr_info(struct isst_id *id, int config_index, unsigned int resp; int ret; - ret = isst_send_mbox_command(id->cpu, CONFIG_TDP, CONFIG_TDP_GET_PWR_INFO, + ret = _send_mbox_command(id->cpu, CONFIG_TDP, CONFIG_TDP_GET_PWR_INFO, 0, config_index, &resp); if (ret) return ret; @@ -333,7 +494,7 @@ static int mbox_get_coremask_info(struct isst_id *id, int config_index, unsigned long long mask; int cpu_count = 0; - ret = isst_send_mbox_command(id->cpu, CONFIG_TDP, + ret = _send_mbox_command(id->cpu, CONFIG_TDP, CONFIG_TDP_GET_CORE_MASK, 0, (i << 8) | config_index, &resp); if (ret) @@ -362,7 +523,7 @@ static int mbox_get_get_trl(struct isst_id *id, int level, int avx_level, int *t int ret; req = level | (avx_level << 16); - ret = isst_send_mbox_command(id->cpu, CONFIG_TDP, + ret = _send_mbox_command(id->cpu, CONFIG_TDP, CONFIG_TDP_GET_TURBO_LIMIT_RATIOS, 0, req, &resp); if (ret) @@ -378,7 +539,7 @@ static int mbox_get_get_trl(struct isst_id *id, int level, int avx_level, int *t trl[3] = (resp & GENMASK(31, 24)) >> 24; req = level | BIT(8) | (avx_level << 16); - ret = isst_send_mbox_command(id->cpu, CONFIG_TDP, + ret = _send_mbox_command(id->cpu, CONFIG_TDP, CONFIG_TDP_GET_TURBO_LIMIT_RATIOS, 0, req, &resp); if (ret) @@ -438,7 +599,7 @@ static int mbox_set_tdp_level(struct isst_id *id, int tdp_level) } - ret = isst_send_mbox_command(id->cpu, CONFIG_TDP, CONFIG_TDP_SET_LEVEL, 0, + ret = _send_mbox_command(id->cpu, CONFIG_TDP, CONFIG_TDP_SET_LEVEL, 0, tdp_level, &resp); if (ret) { isst_display_error_info_message(1, "Set TDP level failed for level", 1, tdp_level); @@ -461,7 +622,7 @@ static int mbox_get_pbf_info(struct isst_id *id, int level, struct isst_pbf_info unsigned long long mask; int count; - ret = isst_send_mbox_command(id->cpu, CONFIG_TDP, + ret = _send_mbox_command(id->cpu, CONFIG_TDP, CONFIG_TDP_PBF_GET_CORE_MASK_INFO, 0, (i << 8) | level, &resp); if (ret) @@ -479,7 +640,7 @@ static int mbox_get_pbf_info(struct isst_id *id, int level, struct isst_pbf_info } req = level; - ret = isst_send_mbox_command(id->cpu, CONFIG_TDP, + ret = _send_mbox_command(id->cpu, CONFIG_TDP, CONFIG_TDP_PBF_GET_P1HI_P1LO_INFO, 0, req, &resp); if (ret) @@ -492,7 +653,7 @@ static int mbox_get_pbf_info(struct isst_id *id, int level, struct isst_pbf_info pbf_info->p1_high = (resp & GENMASK(15, 8)) >> 8; req = level; - ret = isst_send_mbox_command( + ret = _send_mbox_command( id->cpu, CONFIG_TDP, CONFIG_TDP_PBF_GET_TDP_INFO, 0, req, &resp); if (ret) return ret; @@ -502,7 +663,7 @@ static int mbox_get_pbf_info(struct isst_id *id, int level, struct isst_pbf_info pbf_info->tdp = resp & 0xffff; req = level; - ret = isst_send_mbox_command( + ret = _send_mbox_command( id->cpu, CONFIG_TDP, CONFIG_TDP_PBF_GET_TJ_MAX_INFO, 0, req, &resp); if (ret) return ret; @@ -555,7 +716,7 @@ static int mbox_set_pbf_fact_status(struct isst_id *id, int pbf, int enable) req &= ~BIT(16); } - ret = isst_send_mbox_command(id->cpu, CONFIG_TDP, + ret = _send_mbox_command(id->cpu, CONFIG_TDP, CONFIG_TDP_SET_TDP_CONTROL, 0, req, &resp); if (ret) return ret; @@ -575,7 +736,7 @@ static int _get_fact_bucket_info(struct isst_id *id, int level, for (i = 0; i < 2; ++i) { int j; - ret = isst_send_mbox_command( + ret = _send_mbox_command( id->cpu, CONFIG_TDP, CONFIG_TDP_GET_FACT_HP_TURBO_LIMIT_NUMCORES, 0, (i << 8) | level, &resp); @@ -596,7 +757,7 @@ static int _get_fact_bucket_info(struct isst_id *id, int level, for (i = 0; i < 2; ++i) { int j; - ret = isst_send_mbox_command( + ret = _send_mbox_command( id->cpu, CONFIG_TDP, CONFIG_TDP_GET_FACT_HP_TURBO_LIMIT_RATIOS, 0, (k << 16) | (i << 8) | level, &resp); @@ -622,7 +783,7 @@ static int mbox_get_fact_info(struct isst_id *id, int level, int fact_bucket, st unsigned int resp; int j, ret, print; - ret = isst_send_mbox_command(id->cpu, CONFIG_TDP, + ret = _send_mbox_command(id->cpu, CONFIG_TDP, CONFIG_TDP_GET_FACT_LP_CLIPPING_RATIO, 0, level, &resp); if (ret) @@ -662,7 +823,7 @@ static int mbox_get_clos_information(struct isst_id *id, int *enable, int *type) unsigned int resp; int ret; - ret = isst_send_mbox_command(id->cpu, CONFIG_CLOS, CLOS_PM_QOS_CONFIG, 0, 0, + ret = _send_mbox_command(id->cpu, CONFIG_CLOS, CLOS_PM_QOS_CONFIG, 0, 0, &resp); if (ret) return ret; @@ -692,7 +853,7 @@ static int _write_pm_config(struct isst_id *id, int cp_state) else req = 0; - ret = isst_send_mbox_command(id->cpu, WRITE_PM_CONFIG, PM_FEATURE, 0, req, + ret = _send_mbox_command(id->cpu, WRITE_PM_CONFIG, PM_FEATURE, 0, req, &resp); if (ret) return ret; @@ -735,7 +896,7 @@ static int mbox_pm_qos_config(struct isst_id *id, int enable_clos, int priority_ isst_display_error_info_message(0, "WRITE_PM_CONFIG command failed, ignoring error", 0, 0); } - ret = isst_send_mbox_command(id->cpu, CONFIG_CLOS, CLOS_PM_QOS_CONFIG, 0, 0, + ret = _send_mbox_command(id->cpu, CONFIG_CLOS, CLOS_PM_QOS_CONFIG, 0, 0, &resp); if (ret) { isst_display_error_info_message(1, "CLOS_PM_QOS_CONFIG command failed", 0, 0); @@ -759,7 +920,7 @@ static int mbox_pm_qos_config(struct isst_id *id, int enable_clos, int priority_ else req = req & ~BIT(2); - ret = isst_send_mbox_command(id->cpu, CONFIG_CLOS, CLOS_PM_QOS_CONFIG, + ret = _send_mbox_command(id->cpu, CONFIG_CLOS, CLOS_PM_QOS_CONFIG, BIT(MBOX_CMD_WRITE_BIT), req, &resp); if (ret) return ret; @@ -775,7 +936,7 @@ static int mbox_pm_get_clos(struct isst_id *id, int clos, struct isst_clos_confi unsigned int resp; int ret; - ret = isst_send_mbox_command(id->cpu, CONFIG_CLOS, CLOS_PM_CLOS, clos, 0, + ret = _send_mbox_command(id->cpu, CONFIG_CLOS, CLOS_PM_CLOS, clos, 0, &resp); if (ret) return ret; @@ -803,7 +964,7 @@ static int mbox_set_clos(struct isst_id *id, int clos, struct isst_clos_config * param = BIT(MBOX_CMD_WRITE_BIT) | clos; - ret = isst_send_mbox_command(id->cpu, CONFIG_CLOS, CLOS_PM_CLOS, param, req, + ret = _send_mbox_command(id->cpu, CONFIG_CLOS, CLOS_PM_CLOS, param, req, &resp); if (ret) return ret; @@ -822,7 +983,7 @@ static int mbox_clos_get_assoc_status(struct isst_id *id, int *clos_id) core_id = find_phy_core_num(id->cpu); param = core_id; - ret = isst_send_mbox_command(id->cpu, CONFIG_CLOS, CLOS_PQR_ASSOC, param, 0, + ret = _send_mbox_command(id->cpu, CONFIG_CLOS, CLOS_PQR_ASSOC, param, 0, &resp); if (ret) return ret; @@ -844,7 +1005,7 @@ static int mbox_clos_associate(struct isst_id *id, int clos_id) core_id = find_phy_core_num(id->cpu); param = BIT(MBOX_CMD_WRITE_BIT) | core_id; - ret = isst_send_mbox_command(id->cpu, CONFIG_CLOS, CLOS_PQR_ASSOC, param, + ret = _send_mbox_command(id->cpu, CONFIG_CLOS, CLOS_PQR_ASSOC, param, req, &resp); if (ret) return ret; @@ -859,6 +1020,7 @@ static struct isst_platform_ops mbox_ops = { .get_disp_freq_multiplier = mbox_get_disp_freq_multiplier, .get_trl_max_levels = mbox_get_trl_max_levels, .get_trl_level_name = mbox_get_trl_level_name, + .update_platform_param = mbox_update_platform_param, .is_punit_valid = mbox_is_punit_valid, .read_pm_config = mbox_read_pm_config, .get_config_levels = mbox_get_config_levels, diff --git a/tools/power/x86/intel-speed-select/isst-core.c b/tools/power/x86/intel-speed-select/isst-core.c index 24b2c041bfe0..fcab70ef5704 100644 --- a/tools/power/x86/intel-speed-select/isst-core.c +++ b/tools/power/x86/intel-speed-select/isst-core.c @@ -6,9 +6,6 @@ #include "isst.h" -static int mbox_delay; -static int mbox_retries = 3; - static struct isst_platform_ops *isst_ops; #define CHECK_CB(_name) \ @@ -30,16 +27,9 @@ int isst_set_platform_ops(void) void isst_update_platform_param(enum isst_platform_param param, int value) { - switch (param) { - case ISST_PARAM_MBOX_DELAY: - mbox_delay = value; - break; - case ISST_PARAM_MBOX_RETRIES: - mbox_retries = value; - break; - default: - break; - } + CHECK_CB(update_platform_param); + + isst_ops->update_platform_param(param, value); } int isst_get_disp_freq_multiplier(void) @@ -66,150 +56,6 @@ int isst_is_punit_valid(struct isst_id *id) return isst_ops->is_punit_valid(id); } -static int isst_send_mmio_command(unsigned int cpu, unsigned int reg, int write, - unsigned int *value) -{ - struct isst_if_io_regs io_regs; - const char *pathname = "/dev/isst_interface"; - int cmd; - FILE *outf = get_output_file(); - int fd; - - debug_printf("mmio_cmd cpu:%d reg:%d write:%d\n", cpu, reg, write); - - fd = open(pathname, O_RDWR); - if (fd < 0) - err(-1, "%s open failed", pathname); - - io_regs.req_count = 1; - io_regs.io_reg[0].logical_cpu = cpu; - io_regs.io_reg[0].reg = reg; - cmd = ISST_IF_IO_CMD; - if (write) { - io_regs.io_reg[0].read_write = 1; - io_regs.io_reg[0].value = *value; - } else { - io_regs.io_reg[0].read_write = 0; - } - - if (ioctl(fd, cmd, &io_regs) == -1) { - if (errno == ENOTTY) { - perror("ISST_IF_IO_COMMAND\n"); - fprintf(stderr, "Check presence of kernel modules: isst_if_mmio\n"); - exit(0); - } - fprintf(outf, "Error: mmio_cmd cpu:%d reg:%x read_write:%x\n", - cpu, reg, write); - } else { - if (!write) - *value = io_regs.io_reg[0].value; - - debug_printf( - "mmio_cmd response: cpu:%d reg:%x rd_write:%x resp:%x\n", - cpu, reg, write, *value); - } - - close(fd); - - return 0; -} - -int isst_send_mbox_command(unsigned int cpu, unsigned char command, - unsigned char sub_command, unsigned int parameter, - unsigned int req_data, unsigned int *resp) -{ - const char *pathname = "/dev/isst_interface"; - int fd, retry; - struct isst_if_mbox_cmds mbox_cmds = { 0 }; - - debug_printf( - "mbox_send: cpu:%d command:%x sub_command:%x parameter:%x req_data:%x\n", - cpu, command, sub_command, parameter, req_data); - - if (!is_skx_based_platform() && command == CONFIG_CLOS && - sub_command != CLOS_PM_QOS_CONFIG) { - unsigned int value; - int write = 0; - int clos_id, core_id, ret = 0; - - debug_printf("CPU %d\n", cpu); - - if (parameter & BIT(MBOX_CMD_WRITE_BIT)) { - value = req_data; - write = 1; - } - - switch (sub_command) { - case CLOS_PQR_ASSOC: - core_id = parameter & 0xff; - ret = isst_send_mmio_command( - cpu, PQR_ASSOC_OFFSET + core_id * 4, write, - &value); - if (!ret && !write) - *resp = value; - break; - case CLOS_PM_CLOS: - clos_id = parameter & 0x03; - ret = isst_send_mmio_command( - cpu, PM_CLOS_OFFSET + clos_id * 4, write, - &value); - if (!ret && !write) - *resp = value; - break; - case CLOS_STATUS: - break; - default: - break; - } - return ret; - } - - mbox_cmds.cmd_count = 1; - mbox_cmds.mbox_cmd[0].logical_cpu = cpu; - mbox_cmds.mbox_cmd[0].command = command; - mbox_cmds.mbox_cmd[0].sub_command = sub_command; - mbox_cmds.mbox_cmd[0].parameter = parameter; - mbox_cmds.mbox_cmd[0].req_data = req_data; - - if (mbox_delay) - usleep(mbox_delay * 1000); - - fd = open(pathname, O_RDWR); - if (fd < 0) - err(-1, "%s open failed", pathname); - - retry = mbox_retries; - - do { - if (ioctl(fd, ISST_IF_MBOX_COMMAND, &mbox_cmds) == -1) { - if (errno == ENOTTY) { - perror("ISST_IF_MBOX_COMMAND\n"); - fprintf(stderr, "Check presence of kernel modules: isst_if_mbox_pci or isst_if_mbox_msr\n"); - exit(0); - } - debug_printf( - "Error: mbox_cmd cpu:%d command:%x sub_command:%x parameter:%x req_data:%x errorno:%d\n", - cpu, command, sub_command, parameter, req_data, errno); - --retry; - } else { - *resp = mbox_cmds.mbox_cmd[0].resp_data; - debug_printf( - "mbox_cmd response: cpu:%d command:%x sub_command:%x parameter:%x req_data:%x resp:%x\n", - cpu, command, sub_command, parameter, req_data, *resp); - break; - } - } while (retry); - - close(fd); - - if (!retry) { - debug_printf("Failed mbox command even after retries\n"); - return -1; - - } - return 0; -} - int isst_send_msr_command(unsigned int cpu, unsigned int msr, int write, unsigned long long *req_resp) { From 05aab5b8c1b78c028cd95701e788623de744d1cd Mon Sep 17 00:00:00 2001 From: Zhang Rui Date: Sat, 20 Aug 2022 20:40:48 +0800 Subject: [PATCH 117/178] tools/power/x86/intel-speed-select: Allow api_version based platform callbacks Different api_version suggests different kernel driver used and different interface is used to communication with the hardware. Allow setting platform specific callbacks based on api_version. Currently, all platforms with api_version 1 uses Mbox/MMIO interfaces. No functional changes are expected. Signed-off-by: Zhang Rui [srinivas.pandruvada@linux.intel.com: changelog edits] Signed-off-by: Srinivas Pandruvada --- tools/power/x86/intel-speed-select/isst-config.c | 6 ++++-- tools/power/x86/intel-speed-select/isst-core.c | 11 +++++++++-- tools/power/x86/intel-speed-select/isst.h | 8 ++------ 3 files changed, 15 insertions(+), 10 deletions(-) diff --git a/tools/power/x86/intel-speed-select/isst-config.c b/tools/power/x86/intel-speed-select/isst-config.c index 346f8271b00f..fb83f74f15a4 100644 --- a/tools/power/x86/intel-speed-select/isst-config.c +++ b/tools/power/x86/intel-speed-select/isst-config.c @@ -803,8 +803,10 @@ static int isst_fill_platform_info(void) const char *pathname = "/dev/isst_interface"; int fd; - if (is_clx_n_platform()) + if (is_clx_n_platform()) { + isst_platform_info.api_version = 1; goto set_platform_ops; + } fd = open(pathname, O_RDWR); if (fd < 0) @@ -824,7 +826,7 @@ static int isst_fill_platform_info(void) } set_platform_ops: - if (isst_set_platform_ops()) { + if (isst_set_platform_ops(isst_platform_info.api_version)) { fprintf(stderr, "Failed to set platform callbacks\n"); exit(0); } diff --git a/tools/power/x86/intel-speed-select/isst-core.c b/tools/power/x86/intel-speed-select/isst-core.c index fcab70ef5704..674097289ebf 100644 --- a/tools/power/x86/intel-speed-select/isst-core.c +++ b/tools/power/x86/intel-speed-select/isst-core.c @@ -16,9 +16,16 @@ static struct isst_platform_ops *isst_ops; } \ } while (0) -int isst_set_platform_ops(void) +int isst_set_platform_ops(int api_version) { - isst_ops = mbox_get_platform_ops(); + switch (api_version) { + case 1: + isst_ops = mbox_get_platform_ops(); + break; + default: + isst_ops = NULL; + break; + } if (!isst_ops) return -1; diff --git a/tools/power/x86/intel-speed-select/isst.h b/tools/power/x86/intel-speed-select/isst.h index 8192e683ac92..6839ad2d2b9d 100644 --- a/tools/power/x86/intel-speed-select/isst.h +++ b/tools/power/x86/intel-speed-select/isst.h @@ -185,6 +185,7 @@ struct isst_platform_ops { int (*get_disp_freq_multiplier)(void); int (*get_trl_max_levels)(void); char *(*get_trl_level_name)(int level); + void (*update_platform_param)(enum isst_platform_param param, int value); int (*is_punit_valid)(struct isst_id *id); int (*read_pm_config)(struct isst_id *id, int *cp_state, int *cp_cap); int (*get_config_levels)(struct isst_id *id, struct isst_pkg_ctdp *pkg_ctdp); @@ -226,15 +227,10 @@ extern void set_cpu_mask_from_punit_coremask(struct isst_id *id, size_t core_cpumask_size, cpu_set_t *core_cpumask, int *cpu_cnt); -extern int isst_send_mbox_command(unsigned int cpu, unsigned char command, - unsigned char sub_command, - unsigned int write, - unsigned int req_data, unsigned int *resp); - extern int isst_send_msr_command(unsigned int cpu, unsigned int command, int write, unsigned long long *req_resp); -extern int isst_set_platform_ops(void); +extern int isst_set_platform_ops(int api_version); extern void isst_update_platform_param(enum isst_platform_param, int vale); extern int isst_get_disp_freq_multiplier(void); extern int isst_get_trl_max_levels(void); From 9798768ce9bc4da626091f4f87fc0a8edbee44d5 Mon Sep 17 00:00:00 2001 From: Zhang Rui Date: Tue, 9 Aug 2022 01:56:04 +0800 Subject: [PATCH 118/178] tools/power/x86/intel-speed-select: Introduce is_debug_enabled() Platform specific code also needs to give debug output. Introduce is_debug_enabled() for this purpose. No functional changes are expected. Signed-off-by: Zhang Rui [srinivas.pandruvada@linux.intel.com: changelog edits] Signed-off-by: Srinivas Pandruvada --- tools/power/x86/intel-speed-select/isst-config.c | 5 +++++ tools/power/x86/intel-speed-select/isst.h | 1 + 2 files changed, 6 insertions(+) diff --git a/tools/power/x86/intel-speed-select/isst-config.c b/tools/power/x86/intel-speed-select/isst-config.c index fb83f74f15a4..591fe30a55b0 100644 --- a/tools/power/x86/intel-speed-select/isst-config.c +++ b/tools/power/x86/intel-speed-select/isst-config.c @@ -77,6 +77,11 @@ FILE *get_output_file(void) return outf; } +int is_debug_enabled(void) +{ + return debug_flag; +} + void debug_printf(const char *format, ...) { va_list args; diff --git a/tools/power/x86/intel-speed-select/isst.h b/tools/power/x86/intel-speed-select/isst.h index 6839ad2d2b9d..b3bf989690d6 100644 --- a/tools/power/x86/intel-speed-select/isst.h +++ b/tools/power/x86/intel-speed-select/isst.h @@ -216,6 +216,7 @@ extern int get_max_punit_core_id(struct isst_id *id); /* Common interfaces */ FILE *get_output_file(void); +extern int is_debug_enabled(void); extern void debug_printf(const char *format, ...); extern int out_format_is_json(void); extern void set_isst_id(struct isst_id *id, int cpu); From 20f06c9db22bf45de595a2a5855630b075596217 Mon Sep 17 00:00:00 2001 From: Zhang Rui Date: Tue, 16 Aug 2022 21:20:00 +0800 Subject: [PATCH 119/178] tools/power/x86/intel-speed-select: Support large clos_min/max clos_min/max in TPMI interface is frequency in MHz, thus clos_min/max needs to support larger values. No functional changes are expected. Signed-off-by: Zhang Rui [srinivas.pandruvada@linux.intel.com: changelog edits] Signed-off-by: Srinivas Pandruvada --- tools/power/x86/intel-speed-select/isst.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tools/power/x86/intel-speed-select/isst.h b/tools/power/x86/intel-speed-select/isst.h index b3bf989690d6..b854f4bf795f 100644 --- a/tools/power/x86/intel-speed-select/isst.h +++ b/tools/power/x86/intel-speed-select/isst.h @@ -92,10 +92,10 @@ struct isst_id { }; struct isst_clos_config { + unsigned int clos_min; + unsigned int clos_max; unsigned char epp; unsigned char clos_prop_prio; - unsigned char clos_min; - unsigned char clos_max; unsigned char clos_desired; }; From 887e5be91dd253ff73c0b2644442c0cae5d6dca0 Mon Sep 17 00:00:00 2001 From: Zhang Rui Date: Sat, 20 Aug 2022 20:51:33 +0800 Subject: [PATCH 120/178] tools/power/x86/intel-speed-select: Introduce api_version helper In some cases, the output format may be different with different api_version because of different capabilities or for backward capabilities reason. Introduce api_version() to get the api_version of the platform running. No functional changes are expected. Signed-off-by: Zhang Rui [srinivas.pandruvada@linux.intel.com: changelog edits] Signed-off-by: Srinivas Pandruvada --- tools/power/x86/intel-speed-select/isst-config.c | 5 +++++ tools/power/x86/intel-speed-select/isst.h | 1 + 2 files changed, 6 insertions(+) diff --git a/tools/power/x86/intel-speed-select/isst-config.c b/tools/power/x86/intel-speed-select/isst-config.c index 591fe30a55b0..891693ac1234 100644 --- a/tools/power/x86/intel-speed-select/isst-config.c +++ b/tools/power/x86/intel-speed-select/isst-config.c @@ -166,6 +166,11 @@ static int update_cpu_model(void) return 0; } +int api_version(void) +{ + return isst_platform_info.api_version; +} + /* Open a file, and exit on failure */ static FILE *fopen_or_exit(const char *path, const char *mode) { diff --git a/tools/power/x86/intel-speed-select/isst.h b/tools/power/x86/intel-speed-select/isst.h index b854f4bf795f..3efa7251001f 100644 --- a/tools/power/x86/intel-speed-select/isst.h +++ b/tools/power/x86/intel-speed-select/isst.h @@ -213,6 +213,7 @@ extern int is_cpu_in_power_domain(int cpu, struct isst_id *id); extern int get_topo_max_cpus(void); extern int get_cpu_count(struct isst_id *id); extern int get_max_punit_core_id(struct isst_id *id); +extern int api_version(void); /* Common interfaces */ FILE *get_output_file(void); From a0ca5a097342f78d41e104d8d6bb0742ff0e7330 Mon Sep 17 00:00:00 2001 From: Srinivas Pandruvada Date: Mon, 6 Mar 2023 09:26:28 -0800 Subject: [PATCH 121/178] tools/power/x86/intel-speed-select: Get punit core mapping information Get punit core mapping information using format of MSR 0x54. Based on the API version, decode is done using new format. The new format also include a power domain ID. TPMI SST information is for each power domain. Signed-off-by: Srinivas Pandruvada --- .../x86/intel-speed-select/isst-config.c | 57 +++++++++++++------ 1 file changed, 41 insertions(+), 16 deletions(-) diff --git a/tools/power/x86/intel-speed-select/isst-config.c b/tools/power/x86/intel-speed-select/isst-config.c index 891693ac1234..136da390b4b2 100644 --- a/tools/power/x86/intel-speed-select/isst-config.c +++ b/tools/power/x86/intel-speed-select/isst-config.c @@ -669,6 +669,46 @@ int get_cpu_count(struct isst_id *id) return cpu_cnt[id->pkg][id->die][id->punit]; } +static void update_punit_cpu_info(__u32 physical_cpu, struct _cpu_map *cpu_map) +{ + if (api_version() > 1) { + /* + * MSR 0x54 format + * [15:11] PM_DOMAIN_ID + * [10:3] MODULE_ID (aka IDI_AGENT_ID) + * [2:0] LP_ID (We don't care about these bits we only + * care die and core id + * For Atom: + * [2] Always 0 + * [1:0] core ID within module + * For Core + * [2:1] Always 0 + * [0] thread ID + */ + cpu_map->punit_id = (physical_cpu >> 11) & 0x1f; + cpu_map->punit_cpu_core = (physical_cpu >> 3) & 0xff; + cpu_map->punit_cpu = physical_cpu & 0x7ff; + } else { + int punit_id; + + /* + * MSR 0x53 format + * Format + * Bit 0 – thread ID + * Bit 8:1 – core ID + * Bit 13:9 – punit ID + */ + cpu_map->punit_cpu = physical_cpu & 0x1ff; + cpu_map->punit_cpu_core = (cpu_map->punit_cpu >> 1); // shift to get core id + punit_id = (physical_cpu >> 9) & 0x1f; + + if (punit_id >= MAX_PUNIT_PER_DIE) + punit_id = 0; + + cpu_map->punit_id = punit_id; + } +} + static void create_cpu_map(void) { const char *pathname = "/dev/isst_interface"; @@ -727,24 +767,9 @@ static void create_cpu_map(void) fprintf(outf, "Error: map logical_cpu:%d\n", map.cpu_map[0].logical_cpu); } else { - /* - * Format - * Bit 0 – thread ID - * Bit 8:1 – core ID - * Bit 13:9 – punit ID - */ - cpu_map[i].punit_cpu = map.cpu_map[0].physical_cpu & 0x1ff; - cpu_map[i].punit_cpu_core = (cpu_map[i].punit_cpu >> - 1); // shift to get core id - punit_id = (map.cpu_map[0].physical_cpu >> 9) & 0x1f; - - if (punit_id >= MAX_PUNIT_PER_DIE) - punit_id = 0; - - cpu_map[i].punit_id = punit_id; + update_punit_cpu_info(map.cpu_map[0].physical_cpu, &cpu_map[i]); } } - cpu_map[i].initialized = 1; cpu_cnt[pkg_id][die_id][punit_id]++; From 79554aaa224a3b99f95b9c2843dc904764c32541 Mon Sep 17 00:00:00 2001 From: Zhang Rui Date: Tue, 9 Aug 2022 00:01:00 +0800 Subject: [PATCH 122/178] tools/power/x86/intel-speed-select: Introduce TPMI interface support TPMI (Topology Aware Register and PM Capsule Interface) creates a flexible, extendable and software-PCIe-driver-enumerable MMIO interface for PM features. SST feature is exposed via the TPMI interface on newer Xeon platforms. Kernel TPMI based SST driver provides a series of new IOCTLs for userspace to use. Introduce support for the platforms that do SST control via TPMI interface. Compared with previous platforms, Newer Xeons also supports multi-punit in a package/die, including cpu punit and non-cpu punit. These have already been handled in the generic code. Signed-off-by: Zhang Rui [srinivas.pandruvada@linux.intel.com: changelog edits] Signed-off-by: Srinivas Pandruvada --- tools/power/x86/intel-speed-select/Build | 2 +- .../x86/intel-speed-select/isst-core-tpmi.c | 787 ++++++++++++++++++ .../power/x86/intel-speed-select/isst-core.c | 3 + tools/power/x86/intel-speed-select/isst.h | 3 + 4 files changed, 794 insertions(+), 1 deletion(-) create mode 100644 tools/power/x86/intel-speed-select/isst-core-tpmi.c diff --git a/tools/power/x86/intel-speed-select/Build b/tools/power/x86/intel-speed-select/Build index 32a2493b5233..5a9637e1678c 100644 --- a/tools/power/x86/intel-speed-select/Build +++ b/tools/power/x86/intel-speed-select/Build @@ -1 +1 @@ -intel-speed-select-y += isst-config.o isst-core.o isst-display.o isst-daemon.o hfi-events.o isst-core-mbox.o +intel-speed-select-y += isst-config.o isst-core.o isst-display.o isst-daemon.o hfi-events.o isst-core-mbox.o isst-core-tpmi.o diff --git a/tools/power/x86/intel-speed-select/isst-core-tpmi.c b/tools/power/x86/intel-speed-select/isst-core-tpmi.c new file mode 100644 index 000000000000..19caa9c78d41 --- /dev/null +++ b/tools/power/x86/intel-speed-select/isst-core-tpmi.c @@ -0,0 +1,787 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Intel Speed Select -- Enumerate and control features for TPMI Interface + * Copyright (c) 2022 Intel Corporation. + */ + +#include +#include "isst.h" + +int tpmi_process_ioctl(int ioctl_no, void *info) +{ + const char *pathname = "/dev/isst_interface"; + int fd; + + if (is_debug_enabled()) { + debug_printf("Issue IOCTL: "); + switch (ioctl_no) { + case ISST_IF_CORE_POWER_STATE: + debug_printf("ISST_IF_CORE_POWER_STATE\n"); + break; + case ISST_IF_CLOS_PARAM: + debug_printf("ISST_IF_CLOS_PARAM\n"); + break; + case ISST_IF_CLOS_ASSOC: + debug_printf("ISST_IF_CLOS_ASSOC\n"); + break; + case ISST_IF_PERF_LEVELS: + debug_printf("ISST_IF_PERF_LEVELS\n"); + break; + case ISST_IF_PERF_SET_LEVEL: + debug_printf("ISST_IF_PERF_SET_LEVEL\n"); + break; + case ISST_IF_PERF_SET_FEATURE: + debug_printf("ISST_IF_PERF_SET_FEATURE\n"); + break; + case ISST_IF_GET_PERF_LEVEL_INFO: + debug_printf("ISST_IF_GET_PERF_LEVEL_INFO\n"); + break; + case ISST_IF_GET_PERF_LEVEL_CPU_MASK: + debug_printf("ISST_IF_GET_PERF_LEVEL_CPU_MASK\n"); + break; + case ISST_IF_GET_BASE_FREQ_INFO: + debug_printf("ISST_IF_GET_BASE_FREQ_INFO\n"); + break; + case ISST_IF_GET_BASE_FREQ_CPU_MASK: + debug_printf("ISST_IF_GET_BASE_FREQ_CPU_MASK\n"); + break; + case ISST_IF_GET_TURBO_FREQ_INFO: + debug_printf("ISST_IF_GET_TURBO_FREQ_INFO\n"); + break; + case ISST_IF_COUNT_TPMI_INSTANCES: + debug_printf("ISST_IF_COUNT_TPMI_INSTANCES\n"); + break; + default: + debug_printf("%d\n", ioctl_no); + break; + } + } + + fd = open(pathname, O_RDWR); + if (fd < 0) + return -1; + + if (ioctl(fd, ioctl_no, info) == -1) { + debug_printf("IOCTL %d Failed\n", ioctl_no); + close(fd); + return -1; + } + + close(fd); + + return 0; +} + +static int tpmi_get_disp_freq_multiplier(void) +{ + return 1; +} + +static int tpmi_get_trl_max_levels(void) +{ + return TRL_MAX_LEVELS; +} + +static char *tpmi_get_trl_level_name(int level) +{ + switch (level) { + case 0: + return "level-0"; + case 1: + return "level-1"; + case 2: + return "level-2"; + case 3: + return "level-3"; + case 4: + return "level-4"; + case 5: + return "level-5"; + case 6: + return "level-6"; + case 7: + return "level-7"; + default: + return NULL; + } +} + + +static void tpmi_update_platform_param(enum isst_platform_param param, int value) +{ + /* No params need to be updated for now */ +} + +static int tpmi_is_punit_valid(struct isst_id *id) +{ + struct isst_tpmi_instance_count info; + int ret; + + if (id->punit < 0) + return 0; + + info.socket_id = id->pkg; + ret = tpmi_process_ioctl(ISST_IF_COUNT_TPMI_INSTANCES, &info); + if (ret == -1) + return 0; + + if (info.valid_mask & BIT(id->punit)) + return 1; + + return 0; +} + +static int tpmi_read_pm_config(struct isst_id *id, int *cp_state, int *cp_cap) +{ + struct isst_core_power info; + int ret; + + info.get_set = 0; + info.socket_id = id->pkg; + info.power_domain_id = id->punit; + ret = tpmi_process_ioctl(ISST_IF_CORE_POWER_STATE, &info); + if (ret == -1) + return ret; + + *cp_state = info.enable; + *cp_cap = info.supported; + + return 0; +} + +int tpmi_get_config_levels(struct isst_id *id, struct isst_pkg_ctdp *pkg_dev) +{ + struct isst_perf_level_info info; + int ret; + + info.socket_id = id->pkg; + info.power_domain_id = id->punit; + + ret = tpmi_process_ioctl(ISST_IF_PERF_LEVELS, &info); + if (ret == -1) + return ret; + + pkg_dev->version = info.feature_rev; + pkg_dev->levels = info.max_level; + pkg_dev->locked = info.locked; + pkg_dev->current_level = info.current_level; + pkg_dev->locked = info.locked; + pkg_dev->enabled = info.enabled; + + return 0; +} + +static int tpmi_get_ctdp_control(struct isst_id *id, int config_index, + struct isst_pkg_ctdp_level_info *ctdp_level) +{ + struct isst_core_power core_power_info; + struct isst_perf_level_info info; + int level_mask; + int ret; + + info.socket_id = id->pkg; + info.power_domain_id = id->punit; + + ret = tpmi_process_ioctl(ISST_IF_PERF_LEVELS, &info); + if (ret == -1) + return -1; + + if (config_index != 0xff) + level_mask = 1 << config_index; + else + level_mask = config_index; + + if (!(info.level_mask & level_mask)) + return -1; + + ctdp_level->fact_support = info.sst_tf_support; + ctdp_level->pbf_support = info.sst_bf_support; + ctdp_level->fact_enabled = !!(info.feature_state & BIT(1)); + ctdp_level->pbf_enabled = !!(info.feature_state & BIT(0)); + + core_power_info.get_set = 0; + core_power_info.socket_id = id->pkg; + core_power_info.power_domain_id = id->punit; + + ret = tpmi_process_ioctl(ISST_IF_CORE_POWER_STATE, &core_power_info); + if (ret == -1) + return ret; + + ctdp_level->sst_cp_support = core_power_info.supported; + ctdp_level->sst_cp_enabled = core_power_info.enable; + + debug_printf + ("cpu:%d CONFIG_TDP_GET_TDP_CONTROL fact_support:%d pbf_support: %d fact_enabled:%d pbf_enabled:%d\n", + id->cpu, ctdp_level->fact_support, ctdp_level->pbf_support, + ctdp_level->fact_enabled, ctdp_level->pbf_enabled); + + return 0; +} + +static int tpmi_get_tdp_info(struct isst_id *id, int config_index, + struct isst_pkg_ctdp_level_info *ctdp_level) +{ + struct isst_perf_level_data_info info; + int ret; + + info.socket_id = id->pkg; + info.power_domain_id = id->punit; + info.level = config_index; + + ret = tpmi_process_ioctl(ISST_IF_GET_PERF_LEVEL_INFO, &info); + if (ret == -1) + return ret; + + ctdp_level->pkg_tdp = info.thermal_design_power_w; + ctdp_level->tdp_ratio = info.tdp_ratio; + ctdp_level->sse_p1 = info.base_freq_mhz; + ctdp_level->avx2_p1 = info.base_freq_avx2_mhz; + ctdp_level->avx512_p1 = info.base_freq_avx512_mhz; + ctdp_level->amx_p1 = info.base_freq_amx_mhz; + + ctdp_level->t_proc_hot = info.tjunction_max_c; + ctdp_level->mem_freq = info.max_memory_freq_mhz; + ctdp_level->cooling_type = info.cooling_type; + + ctdp_level->uncore_p0 = info.p0_fabric_freq_mhz; + ctdp_level->uncore_p1 = info.p1_fabric_freq_mhz; + ctdp_level->uncore_pm = info.pm_fabric_freq_mhz; + + debug_printf + ("cpu:%d ctdp:%d CONFIG_TDP_GET_TDP_INFO tdp_ratio:%d pkg_tdp:%d ctdp_level->t_proc_hot:%d\n", + id->cpu, config_index, ctdp_level->tdp_ratio, ctdp_level->pkg_tdp, + ctdp_level->t_proc_hot); + + return 0; +} + +static int tpmi_get_pwr_info(struct isst_id *id, int config_index, + struct isst_pkg_ctdp_level_info *ctdp_level) +{ + /* TBD */ + ctdp_level->pkg_max_power = 0; + ctdp_level->pkg_min_power = 0; + + debug_printf + ("cpu:%d ctdp:%d CONFIG_TDP_GET_PWR_INFO pkg_max_power:%d pkg_min_power:%d\n", + id->cpu, config_index, ctdp_level->pkg_max_power, + ctdp_level->pkg_min_power); + + return 0; +} + +int tpmi_get_coremask_info(struct isst_id *id, int config_index, + struct isst_pkg_ctdp_level_info *ctdp_level) +{ + struct isst_perf_level_cpu_mask info; + int ret, cpu_count; + + info.socket_id = id->pkg; + info.power_domain_id = id->punit; + info.level = config_index; + info.punit_cpu_map = 1; + + ret = tpmi_process_ioctl(ISST_IF_GET_PERF_LEVEL_CPU_MASK, &info); + if (ret == -1) + return ret; + + set_cpu_mask_from_punit_coremask(id, info.mask, + ctdp_level->core_cpumask_size, + ctdp_level->core_cpumask, &cpu_count); + ctdp_level->cpu_count = cpu_count; + + debug_printf("cpu:%d ctdp:%d core_mask ino cpu count:%d\n", + id->cpu, config_index, ctdp_level->cpu_count); + + return 0; +} + +static int tpmi_get_get_trls(struct isst_id *id, int config_index, + struct isst_pkg_ctdp_level_info *ctdp_level) +{ + struct isst_perf_level_data_info info; + int ret, i, j; + + info.socket_id = id->pkg; + info.power_domain_id = id->punit; + info.level = config_index; + + ret = tpmi_process_ioctl(ISST_IF_GET_PERF_LEVEL_INFO, &info); + if (ret == -1) + return ret; + + if (info.max_buckets > TRL_MAX_BUCKETS) + info.max_buckets = TRL_MAX_BUCKETS; + + if (info.max_trl_levels > TRL_MAX_LEVELS) + info.max_trl_levels = TRL_MAX_LEVELS; + + for (i = 0; i < info.max_trl_levels; ++i) + for (j = 0; j < info.max_buckets; ++j) + ctdp_level->trl_ratios[i][j] = info.trl_freq_mhz[i][j]; + + return 0; +} + +static int tpmi_get_get_trl(struct isst_id *id, int level, int config_index, + int *trl) +{ + struct isst_pkg_ctdp_level_info ctdp_level; + int ret, i; + + ret = tpmi_get_get_trls(id, config_index, &ctdp_level); + if (ret) + return ret; + + /* FIX ME: Just return for level 0 */ + for (i = 0; i < 8; ++i) + trl[i] = ctdp_level.trl_ratios[0][i]; + + return 0; +} + +static int tpmi_get_trl_bucket_info(struct isst_id *id, int config_index, + unsigned long long *buckets_info) +{ + struct isst_perf_level_data_info info; + unsigned char *mask = (unsigned char *)buckets_info; + int ret, i; + + info.socket_id = id->pkg; + info.power_domain_id = id->punit; + info.level = config_index; + + ret = tpmi_process_ioctl(ISST_IF_GET_PERF_LEVEL_INFO, &info); + if (ret == -1) + return ret; + + if (info.max_buckets > TRL_MAX_BUCKETS) + info.max_buckets = TRL_MAX_BUCKETS; + + for (i = 0; i < info.max_buckets; ++i) + mask[i] = info.bucket_core_counts[i]; + + debug_printf("cpu:%d TRL bucket info: 0x%llx\n", id->cpu, + *buckets_info); + + return 0; +} + +static int tpmi_set_tdp_level(struct isst_id *id, int tdp_level) +{ + struct isst_perf_level_control info; + int ret; + + info.socket_id = id->pkg; + info.power_domain_id = id->punit; + info.level = tdp_level; + + ret = tpmi_process_ioctl(ISST_IF_PERF_SET_LEVEL, &info); + if (ret == -1) + return ret; + + return 0; +} + +static int _pbf_get_coremask_info(struct isst_id *id, int config_index, + struct isst_pbf_info *pbf_info) +{ + struct isst_perf_level_cpu_mask info; + int ret, cpu_count; + + info.socket_id = id->pkg; + info.power_domain_id = id->punit; + info.level = config_index; + info.punit_cpu_map = 1; + + ret = tpmi_process_ioctl(ISST_IF_GET_BASE_FREQ_CPU_MASK, &info); + if (ret == -1) + return ret; + + set_cpu_mask_from_punit_coremask(id, info.mask, + pbf_info->core_cpumask_size, + pbf_info->core_cpumask, &cpu_count); + + debug_printf("cpu:%d ctdp:%d pbf core_mask info cpu count:%d\n", + id->cpu, config_index, cpu_count); + + return 0; +} + +static int tpmi_get_pbf_info(struct isst_id *id, int level, + struct isst_pbf_info *pbf_info) +{ + struct isst_base_freq_info info; + int ret; + + info.socket_id = id->pkg; + info.power_domain_id = id->punit; + info.level = level; + + ret = tpmi_process_ioctl(ISST_IF_GET_BASE_FREQ_INFO, &info); + if (ret == -1) + return ret; + + pbf_info->p1_low = info.low_base_freq_mhz; + pbf_info->p1_high = info.high_base_freq_mhz; + pbf_info->tdp = info.thermal_design_power_w; + pbf_info->t_prochot = info.tjunction_max_c; + + debug_printf("cpu:%d ctdp:%d pbf info:%d:%d:%d:%d\n", + id->cpu, level, pbf_info->p1_low, pbf_info->p1_high, + pbf_info->tdp, pbf_info->t_prochot); + + return _pbf_get_coremask_info(id, level, pbf_info); +} + +static int tpmi_set_pbf_fact_status(struct isst_id *id, int pbf, int enable) +{ + struct isst_pkg_ctdp pkg_dev; + struct isst_pkg_ctdp_level_info ctdp_level; + int current_level; + struct isst_perf_feature_control info; + int ret; + + ret = isst_get_ctdp_levels(id, &pkg_dev); + if (ret) + debug_printf("cpu:%d No support for dynamic ISST\n", id->cpu); + + current_level = pkg_dev.current_level; + + ret = isst_get_ctdp_control(id, current_level, &ctdp_level); + if (ret) + return ret; + + info.socket_id = id->pkg; + info.power_domain_id = id->punit; + + info.feature = 0; + + if (pbf) { + if (ctdp_level.fact_enabled) + info.feature |= BIT(1); + + if (enable) + info.feature |= BIT(0); + else + info.feature &= ~BIT(0); + } else { + + if (enable && !ctdp_level.sst_cp_enabled) + isst_display_error_info_message(0, + "Make sure to execute before: core-power enable", + 0, 0); + + if (ctdp_level.pbf_enabled) + info.feature |= BIT(0); + + if (enable) + info.feature |= BIT(1); + else + info.feature &= ~BIT(1); + } + + ret = tpmi_process_ioctl(ISST_IF_PERF_SET_FEATURE, &info); + if (ret == -1) + return ret; + + return 0; +} + +static int tpmi_get_fact_info(struct isst_id *id, int level, int fact_bucket, + struct isst_fact_info *fact_info) +{ + struct isst_turbo_freq_info info; + int i, j; + int ret; + + info.socket_id = id->pkg; + info.power_domain_id = id->punit; + info.level = level; + + ret = tpmi_process_ioctl(ISST_IF_GET_TURBO_FREQ_INFO, &info); + if (ret == -1) + return ret; + + for (i = 0; i < info.max_clip_freqs; ++i) + fact_info->lp_ratios[i] = info.lp_clip_freq_mhz[i]; + + if (info.max_buckets > TRL_MAX_BUCKETS) + info.max_buckets = TRL_MAX_BUCKETS; + + if (info.max_trl_levels > TRL_MAX_LEVELS) + info.max_trl_levels = TRL_MAX_LEVELS; + + for (i = 0; i < info.max_trl_levels; ++i) { + for (j = 0; j < info.max_buckets; ++j) + fact_info->bucket_info[j].hp_ratios[i] = + info.trl_freq_mhz[i][j]; + } + + for (i = 0; i < info.max_buckets; ++i) + fact_info->bucket_info[i].hp_cores = info.bucket_core_counts[i]; + + return 0; +} + +static void _set_uncore_min_max(struct isst_id *id, int max, int freq) +{ + DIR *dir; + FILE *filep; + struct dirent *entry; + char buffer[512]; + unsigned int tmp_id; + int ret; + + dir = opendir("/sys/devices/system/cpu/intel_uncore_frequency/"); + if (!dir) + return; + + while ((entry = readdir(dir)) != NULL ) { + /* Check domain_id */ + snprintf(buffer, sizeof(buffer), + "/sys/devices/system/cpu/intel_uncore_frequency/%s/domain_id", entry->d_name); + + filep = fopen(buffer, "r"); + if (!filep) + goto end; + + ret = fscanf(filep, "%u", &tmp_id); + fclose(filep); + if (ret != 1) + goto end; + + if (tmp_id != id->punit) + continue; + + /* Check package_id */ + snprintf(buffer, sizeof(buffer), + "/sys/devices/system/cpu/intel_uncore_frequency/%s/package_id", entry->d_name); + + filep = fopen(buffer, "r"); + if (!filep) + goto end; + + ret = fscanf(filep, "%u", &tmp_id); + fclose(filep); + + if (ret != 1) + goto end; + + if (tmp_id != id->pkg) + continue; + + /* Found the right sysfs path, adjust and quit */ + if (max) + snprintf(buffer, sizeof(buffer), + "/sys/devices/system/cpu/intel_uncore_frequency/%s/max_freq_khz", entry->d_name); + else + snprintf(buffer, sizeof(buffer), + "/sys/devices/system/cpu/intel_uncore_frequency/%s/min_freq_khz", entry->d_name); + + filep = fopen(buffer, "w"); + if (!filep) + goto end; + + fprintf(filep, "%d\n", freq); + fclose(filep); + break; + } + +end: + closedir(dir); +} + +static void tpmi_adjust_uncore_freq(struct isst_id *id, int config_index, + struct isst_pkg_ctdp_level_info *ctdp_level) +{ + struct isst_perf_level_data_info info; + int ret; + + info.socket_id = id->pkg; + info.power_domain_id = id->punit; + info.level = config_index; + + ret = tpmi_process_ioctl(ISST_IF_GET_PERF_LEVEL_INFO, &info); + if (ret == -1) + return; + + ctdp_level->uncore_p0 = info.p0_fabric_freq_mhz; + ctdp_level->uncore_p1 = info.p1_fabric_freq_mhz; + ctdp_level->uncore_pm = info.pm_fabric_freq_mhz; + + if (ctdp_level->uncore_pm) + _set_uncore_min_max(id, 0, ctdp_level->uncore_pm * 100000); + + if (ctdp_level->uncore_p0) + _set_uncore_min_max(id, 1, ctdp_level->uncore_p0 * 100000); + + return; +} + +static int tpmi_get_clos_information(struct isst_id *id, int *enable, int *type) +{ + struct isst_core_power info; + int ret; + + info.get_set = 0; + info.socket_id = id->pkg; + info.power_domain_id = id->punit; + ret = tpmi_process_ioctl(ISST_IF_CORE_POWER_STATE, &info); + if (ret == -1) + return ret; + + *enable = info.enable; + *type = info.priority_type; + + return 0; +} + +static int tpmi_pm_qos_config(struct isst_id *id, int enable_clos, + int priority_type) +{ + struct isst_core_power info; + int ret; + + info.get_set = 1; + info.socket_id = id->pkg; + info.power_domain_id = id->punit; + info.enable = enable_clos; + info.priority_type = priority_type; + ret = tpmi_process_ioctl(ISST_IF_CORE_POWER_STATE, &info); + if (ret == -1) + return ret; + + return 0; +} + +int tpmi_pm_get_clos(struct isst_id *id, int clos, + struct isst_clos_config *clos_config) +{ + struct isst_clos_param info; + int ret; + + info.get_set = 0; + info.socket_id = id->pkg; + info.power_domain_id = id->punit; + info.clos = clos; + + ret = tpmi_process_ioctl(ISST_IF_CLOS_PARAM, &info); + if (ret == -1) + return ret; + + clos_config->epp = 0; + clos_config->clos_prop_prio = info.prop_prio; + clos_config->clos_min = info.min_freq_mhz; + clos_config->clos_max = info.max_freq_mhz; + clos_config->clos_desired = 0; + + debug_printf("cpu:%d clos:%d min:%d max:%d\n", id->cpu, clos, + clos_config->clos_min, clos_config->clos_max); + + return 0; +} + +int tpmi_set_clos(struct isst_id *id, int clos, + struct isst_clos_config *clos_config) +{ + struct isst_clos_param info; + int ret; + + info.get_set = 1; + info.socket_id = id->pkg; + info.power_domain_id = id->punit; + info.clos = clos; + info.prop_prio = clos_config->clos_prop_prio; + + info.min_freq_mhz = clos_config->clos_min; + info.max_freq_mhz = clos_config->clos_max; + + if (info.min_freq_mhz <= 0xff) + info.min_freq_mhz *= 100; + if (info.max_freq_mhz <= 0xff) + info.max_freq_mhz *= 100; + + ret = tpmi_process_ioctl(ISST_IF_CLOS_PARAM, &info); + if (ret == -1) + return ret; + + debug_printf("set cpu:%d clos:%d min:%d max:%d\n", id->cpu, clos, + clos_config->clos_min, clos_config->clos_max); + + return 0; +} + +static int tpmi_clos_get_assoc_status(struct isst_id *id, int *clos_id) +{ + struct isst_if_clos_assoc_cmds assoc_cmds; + int ret; + + assoc_cmds.cmd_count = 1; + assoc_cmds.get_set = 0; + assoc_cmds.punit_cpu_map = 1; + assoc_cmds.assoc_info[0].logical_cpu = find_phy_core_num(id->cpu); + assoc_cmds.assoc_info[0].socket_id = id->pkg; + assoc_cmds.assoc_info[0].power_domain_id = id->punit; + + ret = tpmi_process_ioctl(ISST_IF_CLOS_ASSOC, &assoc_cmds); + if (ret == -1) + return ret; + + *clos_id = assoc_cmds.assoc_info[0].clos; + + return 0; +} + +static int tpmi_clos_associate(struct isst_id *id, int clos_id) +{ + struct isst_if_clos_assoc_cmds assoc_cmds; + int ret; + + assoc_cmds.cmd_count = 1; + assoc_cmds.get_set = 1; + assoc_cmds.punit_cpu_map = 1; + assoc_cmds.assoc_info[0].logical_cpu = find_phy_core_num(id->cpu); + assoc_cmds.assoc_info[0].clos = clos_id; + assoc_cmds.assoc_info[0].socket_id = id->pkg; + assoc_cmds.assoc_info[0].power_domain_id = id->punit; + + ret = tpmi_process_ioctl(ISST_IF_CLOS_ASSOC, &assoc_cmds); + if (ret == -1) + return ret; + + return 0; +} + +static struct isst_platform_ops tpmi_ops = { + .get_disp_freq_multiplier = tpmi_get_disp_freq_multiplier, + .get_trl_max_levels = tpmi_get_trl_max_levels, + .get_trl_level_name = tpmi_get_trl_level_name, + .update_platform_param = tpmi_update_platform_param, + .is_punit_valid = tpmi_is_punit_valid, + .read_pm_config = tpmi_read_pm_config, + .get_config_levels = tpmi_get_config_levels, + .get_ctdp_control = tpmi_get_ctdp_control, + .get_tdp_info = tpmi_get_tdp_info, + .get_pwr_info = tpmi_get_pwr_info, + .get_coremask_info = tpmi_get_coremask_info, + .get_get_trl = tpmi_get_get_trl, + .get_get_trls = tpmi_get_get_trls, + .get_trl_bucket_info = tpmi_get_trl_bucket_info, + .set_tdp_level = tpmi_set_tdp_level, + .get_pbf_info = tpmi_get_pbf_info, + .set_pbf_fact_status = tpmi_set_pbf_fact_status, + .get_fact_info = tpmi_get_fact_info, + .adjust_uncore_freq = tpmi_adjust_uncore_freq, + .get_clos_information = tpmi_get_clos_information, + .pm_qos_config = tpmi_pm_qos_config, + .pm_get_clos = tpmi_pm_get_clos, + .set_clos = tpmi_set_clos, + .clos_get_assoc_status = tpmi_clos_get_assoc_status, + .clos_associate = tpmi_clos_associate, +}; + +struct isst_platform_ops *tpmi_get_platform_ops(void) +{ + return &tpmi_ops; +} diff --git a/tools/power/x86/intel-speed-select/isst-core.c b/tools/power/x86/intel-speed-select/isst-core.c index 674097289ebf..f0b96d7078d3 100644 --- a/tools/power/x86/intel-speed-select/isst-core.c +++ b/tools/power/x86/intel-speed-select/isst-core.c @@ -22,6 +22,9 @@ int isst_set_platform_ops(int api_version) case 1: isst_ops = mbox_get_platform_ops(); break; + case 2: + isst_ops = tpmi_get_platform_ops(); + break; default: isst_ops = NULL; break; diff --git a/tools/power/x86/intel-speed-select/isst.h b/tools/power/x86/intel-speed-select/isst.h index 3efa7251001f..1984cc564fa9 100644 --- a/tools/power/x86/intel-speed-select/isst.h +++ b/tools/power/x86/intel-speed-select/isst.h @@ -143,12 +143,14 @@ struct isst_pkg_ctdp_level_info { int pkg_max_power; int fact; int t_proc_hot; + int cooling_type; int uncore_p0; int uncore_p1; int uncore_pm; int sse_p1; int avx2_p1; int avx512_p1; + int amx_p1; int mem_freq; size_t core_cpumask_size; cpu_set_t *core_cpumask; @@ -311,5 +313,6 @@ extern void hfi_exit(void); /* Interface specific callbacks */ extern struct isst_platform_ops *mbox_get_platform_ops(void); +extern struct isst_platform_ops *tpmi_get_platform_ops(void); #endif From 46de87e39b871f926b429b14bde845412b541841 Mon Sep 17 00:00:00 2001 From: Zhang Rui Date: Wed, 10 Aug 2022 17:14:11 +0800 Subject: [PATCH 123/178] tools/power/x86/intel-speed-select: Display punit info Display punit info for platforms with new api_version. For platforms with old api_version, don't display the punit info to be backward compatible. For example: Intel(R) Speed Select Technology Executing on CPU model:173[0xad] package-0 die-0 powerdomain-0 cpu-0 get-config-current_level:0 package-0 die-0 powerdomain-3 cpu--1 get-config-current_level:0 package-0 die-0 powerdomain-4 cpu--1 get-config-current_level:0 Signed-off-by: Zhang Rui [srinivas.pandruvada@linux.intel.com: changelog edits] Signed-off-by: Srinivas Pandruvada --- .../x86/intel-speed-select/isst-display.c | 39 +++++++++---------- 1 file changed, 18 insertions(+), 21 deletions(-) diff --git a/tools/power/x86/intel-speed-select/isst-display.c b/tools/power/x86/intel-speed-select/isst-display.c index af97ba5dbcc2..959d2d81e13a 100644 --- a/tools/power/x86/intel-speed-select/isst-display.c +++ b/tools/power/x86/intel-speed-select/isst-display.c @@ -169,21 +169,30 @@ static void format_and_print(FILE *outf, int level, char *header, char *value) static int print_package_info(struct isst_id *id, FILE *outf) { char header[256]; + int level = 1; if (out_format_is_json()) { - snprintf(header, sizeof(header), "package-%d:die-%d:cpu-%d", - id->pkg, id->die, id->cpu); - format_and_print(outf, 1, header, NULL); + if (api_version() > 1) + snprintf(header, sizeof(header), "package-%d:die-%d:powerdomain-%d:cpu-%d", + id->pkg, id->die, id->punit, id->cpu); + else + snprintf(header, sizeof(header), "package-%d:die-%d:cpu-%d", + id->pkg, id->die, id->cpu); + format_and_print(outf, level, header, NULL); return 1; } snprintf(header, sizeof(header), "package-%d", id->pkg); - format_and_print(outf, 1, header, NULL); + format_and_print(outf, level++, header, NULL); snprintf(header, sizeof(header), "die-%d", id->die); - format_and_print(outf, 2, header, NULL); + format_and_print(outf, level++, header, NULL); + if (api_version() > 1) { + snprintf(header, sizeof(header), "powerdomain-%d", id->punit); + format_and_print(outf, level++, header, NULL); + } snprintf(header, sizeof(header), "cpu-%d", id->cpu); - format_and_print(outf, 3, header, NULL); + format_and_print(outf, level, header, NULL); - return 3; + return level; } static void _isst_pbf_display_information(struct isst_id *id, FILE *outf, int level, @@ -306,22 +315,10 @@ static void _isst_fact_display_information(struct isst_id *id, FILE *outf, int l void isst_ctdp_display_core_info(struct isst_id *id, FILE *outf, char *prefix, unsigned int val, char *str0, char *str1) { - char header[256]; char value[256]; - int level = 1; + int level = print_package_info(id, outf); - if (out_format_is_json()) { - snprintf(header, sizeof(header), "package-%d:die-%d:cpu-%d", - id->pkg, id->die, id->cpu); - format_and_print(outf, level++, header, NULL); - } else { - snprintf(header, sizeof(header), "package-%d", id->pkg); - format_and_print(outf, level++, header, NULL); - snprintf(header, sizeof(header), "die-%d", id->die); - format_and_print(outf, level++, header, NULL); - snprintf(header, sizeof(header), "cpu-%d", id->cpu); - format_and_print(outf, level++, header, NULL); - } + level++; if (str0 && !val) snprintf(value, sizeof(value), "%s", str0); From 5f319081657cb653b55d2c9ef393fc5cd7aae6d4 Mon Sep 17 00:00:00 2001 From: Zhang Rui Date: Tue, 9 Aug 2022 03:29:23 +0800 Subject: [PATCH 124/178] tools/power/x86/intel-speed-select: Display amx_p1 and cooling_type amx_p1 and cooling_type are newly introduced for TPMI interface. Display amx_p1 and cooling_type info for platforms that support them. Signed-off-by: Zhang Rui Signed-off-by: Srinivas Pandruvada --- tools/power/x86/intel-speed-select/isst-display.c | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/tools/power/x86/intel-speed-select/isst-display.c b/tools/power/x86/intel-speed-select/isst-display.c index 959d2d81e13a..8abb0335e091 100644 --- a/tools/power/x86/intel-speed-select/isst-display.c +++ b/tools/power/x86/intel-speed-select/isst-display.c @@ -421,6 +421,13 @@ void isst_ctdp_display_information(struct isst_id *id, FILE *outf, int tdp_level format_and_print(outf, level + 2, header, value); } + if (api_version() > 1 && ctdp_level->amx_p1) { + snprintf(header, sizeof(header), "base-frequency-amx(MHz)"); + snprintf(value, sizeof(value), "%d", + ctdp_level->amx_p1 * isst_get_disp_freq_multiplier()); + format_and_print(outf, level + 2, header, value); + } + if (ctdp_level->uncore_p1) { snprintf(header, sizeof(header), "uncore-frequency-base(MHz)"); snprintf(value, sizeof(value), "%d", @@ -435,6 +442,13 @@ void isst_ctdp_display_information(struct isst_id *id, FILE *outf, int tdp_level format_and_print(outf, level + 2, header, value); } + if (api_version() > 1) { + snprintf(header, sizeof(header), "cooling_type"); + snprintf(value, sizeof(value), "%d", + ctdp_level->cooling_type); + format_and_print(outf, level + 2, header, value); + } + snprintf(header, sizeof(header), "speed-select-turbo-freq"); if (ctdp_level->fact_support) { From 2e54ba89ec5286d041999e6566ae96267d33b002 Mon Sep 17 00:00:00 2001 From: Zhang Rui Date: Thu, 18 Aug 2022 01:34:55 +0800 Subject: [PATCH 125/178] tools/power/x86/intel-speed-select: Allow display non-cpu power domain info Some power domain may not have CPUs associated, allow displaying information for these non-cpu power domains. Signed-off-by: Zhang Rui Signed-off-by: Srinivas Pandruvada --- tools/power/x86/intel-speed-select/isst-display.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/tools/power/x86/intel-speed-select/isst-display.c b/tools/power/x86/intel-speed-select/isst-display.c index 8abb0335e091..1bfa8e4b39b2 100644 --- a/tools/power/x86/intel-speed-select/isst-display.c +++ b/tools/power/x86/intel-speed-select/isst-display.c @@ -682,8 +682,7 @@ void isst_display_result(struct isst_id *id, FILE *outf, char *feature, char *cm char value[256]; int level = 3; - if (id->cpu >= 0) - level = print_package_info(id, outf); + level = print_package_info(id, outf); snprintf(header, sizeof(header), "%s", feature); format_and_print(outf, level + 1, header, NULL); From 443bf104ef10cfc0d22698613f8ba178427d4538 Mon Sep 17 00:00:00 2001 From: Zhang Rui Date: Thu, 18 Aug 2022 01:45:33 +0800 Subject: [PATCH 126/178] tools/power/x86/intel-speed-select: Prevent cpu clos config for non-cpu power domain Non-cpu power domain does not support cpu clos config. Signed-off-by: Zhang Rui Signed-off-by: Srinivas Pandruvada --- tools/power/x86/intel-speed-select/isst-config.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/tools/power/x86/intel-speed-select/isst-config.c b/tools/power/x86/intel-speed-select/isst-config.c index 136da390b4b2..47431c577668 100644 --- a/tools/power/x86/intel-speed-select/isst-config.c +++ b/tools/power/x86/intel-speed-select/isst-config.c @@ -2077,6 +2077,9 @@ static void dump_clos_config_for_cpu(struct isst_id *id, void *arg1, void *arg2, struct isst_clos_config clos_config; int ret; + if (id->cpu < 0) + return; + ret = isst_pm_get_clos(id, current_clos, &clos_config); if (ret) isst_display_error_info_message(1, "isst_pm_get_clos failed", 0, 0); @@ -2153,6 +2156,9 @@ static void set_clos_config_for_cpu(struct isst_id *id, void *arg1, void *arg2, struct isst_clos_config clos_config; int ret; + if (id->cpu < 0) + return; + clos_config.epp = clos_epp; clos_config.clos_prop_prio = clos_prop_prio; clos_config.clos_min = clos_min; From ca7c5d5b75fdac884d2ad6acd7cb28e3f1dfee9c Mon Sep 17 00:00:00 2001 From: Zhang Rui Date: Thu, 18 Aug 2022 01:51:15 +0800 Subject: [PATCH 127/178] tools/power/x86/intel-speed-select: Show level 0 name for new api_version level 0 name is not shown in some case for backward compatibility reason. No need to keep this quirk for new api_version. Signed-off-by: Zhang Rui Signed-off-by: Srinivas Pandruvada --- tools/power/x86/intel-speed-select/isst-display.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tools/power/x86/intel-speed-select/isst-display.c b/tools/power/x86/intel-speed-select/isst-display.c index 1bfa8e4b39b2..4ad5df30ee83 100644 --- a/tools/power/x86/intel-speed-select/isst-display.c +++ b/tools/power/x86/intel-speed-select/isst-display.c @@ -284,7 +284,7 @@ static void _isst_fact_display_information(struct isst_id *id, FILE *outf, int l for (i = 0; i < trl_max_levels; i++) { if (fact_avx != 0xFF && !(fact_avx & (1 << i))) continue; - if (i == 0) + if (i == 0 && api_version() == 1) snprintf(header, sizeof(header), "high-priority-max-frequency(MHz)"); else @@ -301,7 +301,7 @@ static void _isst_fact_display_information(struct isst_id *id, FILE *outf, int l for (j = 0; j < trl_max_levels; j++) { /* No AVX level name for SSE to be consistent with previous formatting */ - if (j == 0) + if (j == 0 && api_version() == 1) snprintf(header, sizeof(header), "low-priority-max-frequency(MHz)"); else snprintf(header, sizeof(header), "low-priority-max-%s-frequency(MHz)", From b1e9b87b3b5999786685eb882afb99c468af94c1 Mon Sep 17 00:00:00 2001 From: Zhang Rui Date: Thu, 18 Aug 2022 21:18:03 +0800 Subject: [PATCH 128/178] tools/power/x86/intel-speed-select: Display fact info for non-cpu power domain Allow displaying SST-TF info for non-cpu power domain. Signed-off-by: Zhang Rui Signed-off-by: Srinivas Pandruvada --- tools/power/x86/intel-speed-select/isst-display.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tools/power/x86/intel-speed-select/isst-display.c b/tools/power/x86/intel-speed-select/isst-display.c index 4ad5df30ee83..1723e88411f5 100644 --- a/tools/power/x86/intel-speed-select/isst-display.c +++ b/tools/power/x86/intel-speed-select/isst-display.c @@ -253,7 +253,8 @@ static void _isst_fact_display_information(struct isst_id *id, FILE *outf, int l if (fact_bucket != 0xff && fact_bucket != j) continue; - if (!bucket_info[j].hp_cores) + /* core count must be valid for CPU power domain */ + if (!bucket_info[j].hp_cores && id->cpu >= 0) break; print = 1; From c5a295caefa33ce7599d7afbe7a2e9b1bc8d92a2 Mon Sep 17 00:00:00 2001 From: Zhang Rui Date: Thu, 18 Aug 2022 21:21:46 +0800 Subject: [PATCH 129/178] tools/power/x86/intel-speed-select: Hide invalid TRL level TRL levels with Zero ratio values is meaningless. Prevent these TRL levels from being displayed. Signed-off-by: Zhang Rui Signed-off-by: Srinivas Pandruvada --- tools/power/x86/intel-speed-select/isst-display.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/tools/power/x86/intel-speed-select/isst-display.c b/tools/power/x86/intel-speed-select/isst-display.c index 1723e88411f5..d8b789b9656a 100644 --- a/tools/power/x86/intel-speed-select/isst-display.c +++ b/tools/power/x86/intel-speed-select/isst-display.c @@ -505,6 +505,9 @@ void isst_ctdp_display_information(struct isst_id *id, FILE *outf, int tdp_level } for (k = 0; k < trl_max_levels; k++) { + if (!ctdp_level->trl_ratios[k][0]) + continue; + snprintf(header, sizeof(header), "turbo-ratio-limits-%s", isst_get_trl_level_name(k)); format_and_print(outf, level + 2, header, NULL); From c7ff8ff3b29ee031a2194f67b5ceced9b77fdc21 Mon Sep 17 00:00:00 2001 From: Zhang Rui Date: Thu, 18 Aug 2022 21:26:23 +0800 Subject: [PATCH 130/178] tools/power/x86/intel-speed-select: Remove cpu mask display for non-cpu power domain Non CPU power domains will not have any CPUs. So don't display any CPU count or enable mask. Signed-off-by: Zhang Rui [srinivas.pandruvada@linux.intel.com: subject and changelog edits] Signed-off-by: Srinivas Pandruvada --- .../x86/intel-speed-select/isst-display.c | 44 ++++++++++--------- 1 file changed, 23 insertions(+), 21 deletions(-) diff --git a/tools/power/x86/intel-speed-select/isst-display.c b/tools/power/x86/intel-speed-select/isst-display.c index d8b789b9656a..e3573895c8b0 100644 --- a/tools/power/x86/intel-speed-select/isst-display.c +++ b/tools/power/x86/intel-speed-select/isst-display.c @@ -356,31 +356,33 @@ void isst_ctdp_display_information(struct isst_id *id, FILE *outf, int tdp_level ctdp_level->level); format_and_print(outf, level + 1, header, NULL); - snprintf(header, sizeof(header), "cpu-count"); - j = get_cpu_count(id); - snprintf(value, sizeof(value), "%d", j); - format_and_print(outf, level + 2, header, value); - - j = CPU_COUNT_S(ctdp_level->core_cpumask_size, - ctdp_level->core_cpumask); - if (j) { - snprintf(header, sizeof(header), "enable-cpu-count"); + if (id->cpu >= 0) { + snprintf(header, sizeof(header), "cpu-count"); + j = get_cpu_count(id); snprintf(value, sizeof(value), "%d", j); format_and_print(outf, level + 2, header, value); - } - if (ctdp_level->core_cpumask_size) { - snprintf(header, sizeof(header), "enable-cpu-mask"); - printcpumask(sizeof(value), value, - ctdp_level->core_cpumask_size, - ctdp_level->core_cpumask); - format_and_print(outf, level + 2, header, value); + j = CPU_COUNT_S(ctdp_level->core_cpumask_size, + ctdp_level->core_cpumask); + if (j) { + snprintf(header, sizeof(header), "enable-cpu-count"); + snprintf(value, sizeof(value), "%d", j); + format_and_print(outf, level + 2, header, value); + } - snprintf(header, sizeof(header), "enable-cpu-list"); - printcpulist(sizeof(value), value, - ctdp_level->core_cpumask_size, - ctdp_level->core_cpumask); - format_and_print(outf, level + 2, header, value); + if (ctdp_level->core_cpumask_size) { + snprintf(header, sizeof(header), "enable-cpu-mask"); + printcpumask(sizeof(value), value, + ctdp_level->core_cpumask_size, + ctdp_level->core_cpumask); + format_and_print(outf, level + 2, header, value); + + snprintf(header, sizeof(header), "enable-cpu-list"); + printcpulist(sizeof(value), value, + ctdp_level->core_cpumask_size, + ctdp_level->core_cpumask); + format_and_print(outf, level + 2, header, value); + } } snprintf(header, sizeof(header), "thermal-design-power-ratio"); From 14f0cf6cfa53a3a4f2f713b1d54eb71e96ff34cb Mon Sep 17 00:00:00 2001 From: Zhang Rui Date: Thu, 2 Feb 2023 15:55:25 +0800 Subject: [PATCH 131/178] tools/power/x86/intel-speed-select: Avoid setting duplicate tdp level If the new TDP level requetsted is same as the current TDP level, don't call into driver to change level. Signed-off-by: Zhang Rui [srinivas.pandruvada@linux.intel.com: changelog edits] Signed-off-by: Srinivas Pandruvada --- .../x86/intel-speed-select/isst-config.c | 73 +++++++++++-------- 1 file changed, 43 insertions(+), 30 deletions(-) diff --git a/tools/power/x86/intel-speed-select/isst-config.c b/tools/power/x86/intel-speed-select/isst-config.c index 47431c577668..b10c6ecd9777 100644 --- a/tools/power/x86/intel-speed-select/isst-config.c +++ b/tools/power/x86/intel-speed-select/isst-config.c @@ -1229,46 +1229,59 @@ static void adjust_scaling_max_from_base_freq(int cpu); static void set_tdp_level_for_cpu(struct isst_id *id, void *arg1, void *arg2, void *arg3, void *arg4) { + struct isst_pkg_ctdp pkg_dev; int ret; + ret = isst_get_ctdp_levels(id, &pkg_dev); + if (ret) { + isst_display_error_info_message(1, "Get TDP level failed", 0, 0); + isst_ctdp_display_information_end(outf); + exit(1); + } + + if (pkg_dev.current_level == tdp_level) { + debug_printf("TDP level already set. Skipped\n"); + goto display_result; + } + ret = isst_set_tdp_level(id, tdp_level); if (ret) { isst_display_error_info_message(1, "Set TDP level failed", 0, 0); isst_ctdp_display_information_end(outf); exit(1); - } else { - isst_display_result(id, outf, "perf-profile", "set_tdp_level", - ret); - if (force_online_offline) { - struct isst_pkg_ctdp_level_info ctdp_level; + } - /* Wait for updated base frequencies */ - usleep(2000); +display_result: + isst_display_result(id, outf, "perf-profile", "set_tdp_level", ret); + if (force_online_offline) { + struct isst_pkg_ctdp_level_info ctdp_level; - /* Adjusting uncore freq */ - isst_adjust_uncore_freq(id, tdp_level, &ctdp_level); + /* Wait for updated base frequencies */ + usleep(2000); - fprintf(stderr, "Option is set to online/offline\n"); - ctdp_level.core_cpumask_size = - alloc_cpu_set(&ctdp_level.core_cpumask); - ret = isst_get_coremask_info(id, tdp_level, &ctdp_level); - if (ret) { - isst_display_error_info_message(1, "Can't get coremask, online/offline option is ignored", 0, 0); - return; - } - if (ctdp_level.cpu_count) { - int i, max_cpus = get_topo_max_cpus(); - for (i = 0; i < max_cpus; ++i) { - if (!is_cpu_in_power_domain(i, id)) - continue; - if (CPU_ISSET_S(i, ctdp_level.core_cpumask_size, ctdp_level.core_cpumask)) { - fprintf(stderr, "online cpu %d\n", i); - set_cpu_online_offline(i, 1); - adjust_scaling_max_from_base_freq(i); - } else { - fprintf(stderr, "offline cpu %d\n", i); - set_cpu_online_offline(i, 0); - } + /* Adjusting uncore freq */ + isst_adjust_uncore_freq(id, tdp_level, &ctdp_level); + + fprintf(stderr, "Option is set to online/offline\n"); + ctdp_level.core_cpumask_size = + alloc_cpu_set(&ctdp_level.core_cpumask); + ret = isst_get_coremask_info(id, tdp_level, &ctdp_level); + if (ret) { + isst_display_error_info_message(1, "Can't get coremask, online/offline option is ignored", 0, 0); + return; + } + if (ctdp_level.cpu_count) { + int i, max_cpus = get_topo_max_cpus(); + for (i = 0; i < max_cpus; ++i) { + if (!is_cpu_in_power_domain(i, id)) + continue; + if (CPU_ISSET_S(i, ctdp_level.core_cpumask_size, ctdp_level.core_cpumask)) { + fprintf(stderr, "online cpu %d\n", i); + set_cpu_online_offline(i, 1); + adjust_scaling_max_from_base_freq(i); + } else { + fprintf(stderr, "offline cpu %d\n", i); + set_cpu_online_offline(i, 0); } } } From d0e12c46f518620551d719b19b93777dafd0b5e6 Mon Sep 17 00:00:00 2001 From: Zhang Rui Date: Tue, 13 Sep 2022 20:23:19 +0800 Subject: [PATCH 132/178] tools/power/x86/intel-speed-select: Add cpu id check Some operations applies to cpu-power-domain only. Add check for cpu id for these functions. Signed-off-by: Zhang Rui Signed-off-by: Srinivas Pandruvada --- tools/power/x86/intel-speed-select/isst-config.c | 16 ++++++++++++++-- tools/power/x86/intel-speed-select/isst-core.c | 3 +++ 2 files changed, 17 insertions(+), 2 deletions(-) diff --git a/tools/power/x86/intel-speed-select/isst-config.c b/tools/power/x86/intel-speed-select/isst-config.c index b10c6ecd9777..a04005622b32 100644 --- a/tools/power/x86/intel-speed-select/isst-config.c +++ b/tools/power/x86/intel-speed-select/isst-config.c @@ -800,6 +800,9 @@ void set_cpu_mask_from_punit_coremask(struct isst_id *id, unsigned long long cor { int i, cnt = 0; + if (id->cpu < 0) + return; + *cpu_cnt = 0; for (i = 0; i < 64; ++i) { @@ -1253,7 +1256,7 @@ static void set_tdp_level_for_cpu(struct isst_id *id, void *arg1, void *arg2, vo display_result: isst_display_result(id, outf, "perf-profile", "set_tdp_level", ret); - if (force_online_offline) { + if (force_online_offline && id->cpu >= 0) { struct isst_pkg_ctdp_level_info ctdp_level; /* Wait for updated base frequencies */ @@ -1547,6 +1550,9 @@ static void set_scaling_min_to_cpuinfo_max(struct isst_id *id) { int i; + if (id->cpu < 0) + return; + for (i = 0; i < get_topo_max_cpus(); ++i) { if (!is_cpu_in_power_domain(i, id)) continue; @@ -1564,6 +1570,9 @@ static void set_scaling_min_to_cpuinfo_min(struct isst_id *id) { int i; + if (id->cpu < 0) + return; + for (i = 0; i < get_topo_max_cpus(); ++i) { if (!is_cpu_in_power_domain(i, id)) continue; @@ -1643,6 +1652,9 @@ static int set_pbf_core_power(struct isst_id *id) struct isst_pkg_ctdp pkg_dev; int ret; + if (id->cpu < 0) + return 0; + ret = isst_get_ctdp_levels(id, &pkg_dev); if (ret) { debug_printf("isst_get_ctdp_levels failed"); @@ -1888,7 +1900,7 @@ static void set_fact_for_cpu(struct isst_id *id, void *arg1, void *arg2, void *a struct isst_pkg_ctdp pkg_dev; ret = isst_get_ctdp_levels(id, &pkg_dev); - if (!ret) + if (!ret && id->cpu >= 0) ret = isst_set_trl(id, fact_trl); if (ret && auto_mode) isst_pm_qos_config(id, 0, 0); diff --git a/tools/power/x86/intel-speed-select/isst-core.c b/tools/power/x86/intel-speed-select/isst-core.c index f0b96d7078d3..f55fef4c13a7 100644 --- a/tools/power/x86/intel-speed-select/isst-core.c +++ b/tools/power/x86/intel-speed-select/isst-core.c @@ -287,6 +287,9 @@ int isst_set_trl_from_current_tdp(struct isst_id *id, unsigned long long trl) unsigned long long msr_trl; int ret; + if (id->cpu < 0) + return 0; + if (trl) { msr_trl = trl; } else { From 137ba3b13aacf80b64d363dd7cff69c2aed161f0 Mon Sep 17 00:00:00 2001 From: Zhang Rui Date: Sat, 7 Jan 2023 21:45:42 +0800 Subject: [PATCH 133/178] tools/power/x86/intel-speed-select: Fix clos-max display with TPMI I/F Comparing clos_config->clos_max with 255 is broken with TPMI I/F because of different isst_get_disp_freq_multiplier() used. Checking for clos_config->clos_max * isst_get_disp_freq_multiplier() instead. Signed-off-by: Zhang Rui Signed-off-by: Srinivas Pandruvada --- tools/power/x86/intel-speed-select/isst-display.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/power/x86/intel-speed-select/isst-display.c b/tools/power/x86/intel-speed-select/isst-display.c index e3573895c8b0..218fb317f7ad 100644 --- a/tools/power/x86/intel-speed-select/isst-display.c +++ b/tools/power/x86/intel-speed-select/isst-display.c @@ -606,7 +606,7 @@ void isst_clos_display_information(struct isst_id *id, FILE *outf, int clos, format_and_print(outf, level + 2, header, value); snprintf(header, sizeof(header), "clos-max"); - if (clos_config->clos_max == 0xff) + if ((clos_config->clos_max * isst_get_disp_freq_multiplier()) == 25500) snprintf(value, sizeof(value), "Max Turbo frequency"); else snprintf(value, sizeof(value), "%d MHz", clos_config->clos_max * isst_get_disp_freq_multiplier()); From 57797f19d5a78b5a84b7452790f7da22865f6420 Mon Sep 17 00:00:00 2001 From: Srinivas Pandruvada Date: Wed, 22 Feb 2023 03:26:29 -0800 Subject: [PATCH 134/178] tools/power/x86/intel-speed-select: Add missing free cpuset During perf level change cpuset is allocated but not freed. Add free_cpu_set() in success and failure path. Although this is not an issue, as the program will exit after processing of online/offline, but for completeness add the free_cpu_set(). Signed-off-by: Srinivas Pandruvada --- tools/power/x86/intel-speed-select/isst-config.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/tools/power/x86/intel-speed-select/isst-config.c b/tools/power/x86/intel-speed-select/isst-config.c index a04005622b32..a9bd87b4d4ee 100644 --- a/tools/power/x86/intel-speed-select/isst-config.c +++ b/tools/power/x86/intel-speed-select/isst-config.c @@ -1271,7 +1271,7 @@ display_result: ret = isst_get_coremask_info(id, tdp_level, &ctdp_level); if (ret) { isst_display_error_info_message(1, "Can't get coremask, online/offline option is ignored", 0, 0); - return; + goto free_mask; } if (ctdp_level.cpu_count) { int i, max_cpus = get_topo_max_cpus(); @@ -1288,6 +1288,8 @@ display_result: } } } +free_mask: + free_cpu_set(ctdp_level.core_cpumask); } } From 997074df658e444e79a5294b685b77b08a3c414c Mon Sep 17 00:00:00 2001 From: Srinivas Pandruvada Date: Wed, 22 Feb 2023 03:42:19 -0800 Subject: [PATCH 135/178] tools/power/x86/intel-speed-select: Use cgroup v2 isolation On supported systems, it is possiible to isolate CPUs instead of CPU online/offline. This is optional and can be specified using -g option when running as daemon or in combination with -o option for SST-PP level change. CPU isolation doesn't isolate IRQs. So IRQs needs to be moved away from isoolated CPUs. This can be done via IRQ sysfs or irqbalance daemon. The IRQ balance daemon is also capable to parse thermal HFI messages to move IRQs away from CPUS, which are supposed be isolated. But this requires version released after July 2022. Signed-off-by: Srinivas Pandruvada --- .../x86/intel-speed-select/isst-config.c | 155 +++++++++++++++++- .../x86/intel-speed-select/isst-daemon.c | 15 +- tools/power/x86/intel-speed-select/isst.h | 5 + 3 files changed, 173 insertions(+), 2 deletions(-) diff --git a/tools/power/x86/intel-speed-select/isst-config.c b/tools/power/x86/intel-speed-select/isst-config.c index a9bd87b4d4ee..c4d00426cb6a 100644 --- a/tools/power/x86/intel-speed-select/isst-config.c +++ b/tools/power/x86/intel-speed-select/isst-config.c @@ -44,6 +44,7 @@ static int cmd_help; static int force_online_offline; static int auto_mode; static int fact_enable_fail; +static int cgroupv2; /* clos related */ static int current_clos = -1; @@ -834,7 +835,137 @@ int find_phy_core_num(int logical_cpu) return -EINVAL; } +int use_cgroupv2(void) +{ + return cgroupv2; +} +int enable_cpuset_controller(void) +{ + int fd, ret; + + fd = open("/sys/fs/cgroup/cgroup.subtree_control", O_RDWR, 0); + if (fd < 0) { + debug_printf("Can't activate cpuset controller\n"); + debug_printf("Either you are not root user or CGroup v2 is not supported\n"); + return fd; + } + + ret = write(fd, " +cpuset", strlen(" +cpuset")); + close(fd); + + if (ret == -1) { + debug_printf("Can't activate cpuset controller: Write failed\n"); + return ret; + } + + return 0; +} + +int isolate_cpus(struct isst_id *id, int mask_size, cpu_set_t *cpu_mask, int level) +{ + int i, first, curr_index, index, ret, fd; + static char str[512], dir_name[64]; + static char cpuset_cpus[128]; + int str_len = sizeof(str); + DIR *dir; + + snprintf(dir_name, sizeof(dir_name), "/sys/fs/cgroup/%d-%d-%d", id->pkg, id->die, id->punit); + dir = opendir(dir_name); + if (!dir) { + ret = mkdir(dir_name, 0744); + if (ret) { + debug_printf("Can't create dir:%s errno:%d\n", dir_name, errno); + return ret; + } + } + closedir(dir); + + if (!level) { + sprintf(cpuset_cpus, "%s/cpuset.cpus.partition", dir_name); + + fd = open(cpuset_cpus, O_RDWR, 0); + if (fd < 0) { + return fd; + } + + ret = write(fd, "member", strlen("member")); + if (ret == -1) { + printf("Can't update to member\n"); + return ret; + } + + return 0; + } + + if (!CPU_COUNT_S(mask_size, cpu_mask)) { + return -1; + } + + curr_index = 0; + first = 1; + str[0] = '\0'; + for (i = 0; i < get_topo_max_cpus(); ++i) { + if (!is_cpu_in_power_domain(i, id)) + continue; + + if (CPU_ISSET_S(i, mask_size, cpu_mask)) + continue; + + if (!first) { + index = snprintf(&str[curr_index], + str_len - curr_index, ","); + curr_index += index; + if (curr_index >= str_len) + break; + } + index = snprintf(&str[curr_index], str_len - curr_index, "%d", + i); + curr_index += index; + if (curr_index >= str_len) + break; + first = 0; + } + + debug_printf("isolated CPUs list: package:%d curr_index:%d [%s]\n", id->pkg, curr_index ,str); + + snprintf(cpuset_cpus, sizeof(cpuset_cpus), "%s/cpuset.cpus", dir_name); + + fd = open(cpuset_cpus, O_RDWR, 0); + if (fd < 0) { + return fd; + } + + ret = write(fd, str, strlen(str)); + close(fd); + + if (ret == -1) { + debug_printf("Can't activate cpuset controller: Write failed\n"); + return ret; + } + + snprintf(cpuset_cpus, sizeof(cpuset_cpus), "%s/cpuset.cpus.partition", dir_name); + + fd = open(cpuset_cpus, O_RDWR, 0); + if (fd < 0) { + return fd; + } + + ret = write(fd, "isolated", strlen("isolated")); + if (ret == -1) { + debug_printf("Can't update to isolated\n"); + ret = write(fd, "root", strlen("root")); + if (ret == -1) + debug_printf("Can't update to root\n"); + } + + close(fd); + + if (ret < 0) + return ret; + + return 0; +} static int isst_fill_platform_info(void) { @@ -1273,6 +1404,23 @@ display_result: isst_display_error_info_message(1, "Can't get coremask, online/offline option is ignored", 0, 0); goto free_mask; } + + if (use_cgroupv2()) { + int ret; + + fprintf(stderr, "Using cgroup v2 in lieu of online/offline\n"); + ret = enable_cpuset_controller(); + if (ret) + goto use_offline; + + ret = isolate_cpus(id, ctdp_level.core_cpumask_size, ctdp_level.core_cpumask, tdp_level); + if (ret) + goto use_offline; + + goto free_mask; + } + +use_offline: if (ctdp_level.cpu_count) { int i, max_cpus = get_topo_max_cpus(); for (i = 0; i < max_cpus; ++i) { @@ -2787,6 +2935,7 @@ static void usage(void) printf("\t[-b|--oob : Start a daemon to process HFI events for perf profile change from Out of Band agent.\n"); printf("\t[-n|--no-daemon : Don't run as daemon. By default --oob will turn on daemon mode\n"); printf("\t[-w|--delay : Delay for reading config level state change in OOB poll mode.\n"); + printf("\t[-g|--cgroupv2 : Try to use cgroup v2 CPU isolation instead of CPU online/offline.\n"); printf("\nResult format\n"); printf("\tResult display uses a common format for each command:\n"); printf("\tResults are formatted in text/JSON with\n"); @@ -2839,6 +2988,7 @@ static void cmdline(int argc, char **argv) { "oob", no_argument, 0, 'b' }, { "no-daemon", no_argument, 0, 'n' }, { "poll-interval", required_argument, 0, 'w' }, + { "cgroupv2", required_argument, 0, 'g' }, { 0, 0, 0, 0 } }; @@ -2869,7 +3019,7 @@ static void cmdline(int argc, char **argv) goto out; progname = argv[0]; - while ((opt = getopt_long_only(argc, argv, "+c:df:hio:vabw:n", long_options, + while ((opt = getopt_long_only(argc, argv, "+c:df:hio:vabw:ng", long_options, &option_index)) != -1) { switch (opt) { case 'a': @@ -2928,6 +3078,9 @@ static void cmdline(int argc, char **argv) } poll_interval = ret; break; + case 'g': + cgroupv2 = 1; + break; default: usage(); } diff --git a/tools/power/x86/intel-speed-select/isst-daemon.c b/tools/power/x86/intel-speed-select/isst-daemon.c index 6fa2942b5e12..12053fa43542 100644 --- a/tools/power/x86/intel-speed-select/isst-daemon.c +++ b/tools/power/x86/intel-speed-select/isst-daemon.c @@ -83,6 +83,19 @@ void process_level_change(struct isst_id *id) return; } + if (use_cgroupv2()) { + int ret; + + ret = enable_cpuset_controller(); + if (ret) + goto use_offline; + + isolate_cpus(id, ctdp_level.core_cpumask_size, ctdp_level.core_cpumask, pkg_dev.current_level); + + goto free_mask; + } + +use_offline: if (ctdp_level.cpu_count) { int i, max_cpus = get_topo_max_cpus(); for (i = 0; i < max_cpus; ++i) { @@ -97,7 +110,7 @@ void process_level_change(struct isst_id *id) } } } - +free_mask: free_cpu_set(ctdp_level.core_cpumask); } diff --git a/tools/power/x86/intel-speed-select/isst.h b/tools/power/x86/intel-speed-select/isst.h index 1984cc564fa9..6a37c26b1ef7 100644 --- a/tools/power/x86/intel-speed-select/isst.h +++ b/tools/power/x86/intel-speed-select/isst.h @@ -315,4 +315,9 @@ extern void hfi_exit(void); extern struct isst_platform_ops *mbox_get_platform_ops(void); extern struct isst_platform_ops *tpmi_get_platform_ops(void); +/* Cgroup related interface */ +extern int enable_cpuset_controller(void); +extern int isolate_cpus(struct isst_id *id, int mask_size, cpu_set_t *cpu_mask, int level); +extern int use_cgroupv2(void); + #endif From a835ff56dd9ce2dfc53526085ba8efc2807b9bcb Mon Sep 17 00:00:00 2001 From: Srinivas Pandruvada Date: Thu, 23 Feb 2023 05:59:24 -0800 Subject: [PATCH 136/178] tools/power/x86/intel-speed-select: Display AMX base frequency AMX frequency is present in non TPMI platforms also. When platform supports, the value is non zero. So, display AMX base frequency when non zero, irrespective of platform API version. Signed-off-by: Srinivas Pandruvada --- tools/power/x86/intel-speed-select/isst-core-mbox.c | 5 +++-- tools/power/x86/intel-speed-select/isst-display.c | 2 +- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/tools/power/x86/intel-speed-select/isst-core-mbox.c b/tools/power/x86/intel-speed-select/isst-core-mbox.c index 6eafef925d1a..6951a9c31dc5 100644 --- a/tools/power/x86/intel-speed-select/isst-core-mbox.c +++ b/tools/power/x86/intel-speed-select/isst-core-mbox.c @@ -383,10 +383,11 @@ static void _get_p1_info(struct isst_id *id, int config_index, ctdp_level->sse_p1 = resp & GENMASK(7, 0); ctdp_level->avx2_p1 = (resp & GENMASK(15, 8)) >> 8; ctdp_level->avx512_p1 = (resp & GENMASK(23, 16)) >> 16; + ctdp_level->amx_p1 = (resp & GENMASK(31, 24)) >> 24; debug_printf( - "cpu:%d ctdp:%d CONFIG_TDP_GET_P1_INFO resp:%x sse_p1:%d avx2_p1:%d avx512_p1:%d\n", + "cpu:%d ctdp:%d CONFIG_TDP_GET_P1_INFO resp:%x sse_p1:%d avx2_p1:%d avx512_p1:%d amx_p1:%d\n", id->cpu, config_index, resp, ctdp_level->sse_p1, - ctdp_level->avx2_p1, ctdp_level->avx512_p1); + ctdp_level->avx2_p1, ctdp_level->avx512_p1, ctdp_level->amx_p1); } static void _get_uncore_mem_freq(struct isst_id *id, int config_index, diff --git a/tools/power/x86/intel-speed-select/isst-display.c b/tools/power/x86/intel-speed-select/isst-display.c index 218fb317f7ad..0e364b35f093 100644 --- a/tools/power/x86/intel-speed-select/isst-display.c +++ b/tools/power/x86/intel-speed-select/isst-display.c @@ -424,7 +424,7 @@ void isst_ctdp_display_information(struct isst_id *id, FILE *outf, int tdp_level format_and_print(outf, level + 2, header, value); } - if (api_version() > 1 && ctdp_level->amx_p1) { + if (ctdp_level->amx_p1) { snprintf(header, sizeof(header), "base-frequency-amx(MHz)"); snprintf(value, sizeof(value), "%d", ctdp_level->amx_p1 * isst_get_disp_freq_multiplier()); From 1d54b139f43482a5d394f26ce47aa9949dec6e76 Mon Sep 17 00:00:00 2001 From: Srinivas Pandruvada Date: Wed, 22 Feb 2023 22:53:02 -0800 Subject: [PATCH 137/178] tools/power/x86/intel-speed-select: Identify Emerald Rapids There are some differences compared to Sapphire Rapids. So, add a separate API. Signed-off-by: Srinivas Pandruvada --- tools/power/x86/intel-speed-select/isst-config.c | 11 ++++++++++- tools/power/x86/intel-speed-select/isst-core-mbox.c | 2 +- tools/power/x86/intel-speed-select/isst.h | 1 + 3 files changed, 12 insertions(+), 2 deletions(-) diff --git a/tools/power/x86/intel-speed-select/isst-config.c b/tools/power/x86/intel-speed-select/isst-config.c index c4d00426cb6a..2ad8726edc06 100644 --- a/tools/power/x86/intel-speed-select/isst-config.c +++ b/tools/power/x86/intel-speed-select/isst-config.c @@ -114,12 +114,21 @@ int is_skx_based_platform(void) int is_spr_platform(void) { - if (cpu_model == 0x8F || cpu_model == 0xCF) + if (cpu_model == 0x8F) return 1; return 0; } +int is_emr_platform(void) +{ + if (cpu_model == 0xCF) + return 1; + + return 0; +} + + int is_icx_platform(void) { if (cpu_model == 0x6A || cpu_model == 0x6C) diff --git a/tools/power/x86/intel-speed-select/isst-core-mbox.c b/tools/power/x86/intel-speed-select/isst-core-mbox.c index 6951a9c31dc5..c860be30fe5e 100644 --- a/tools/power/x86/intel-speed-select/isst-core-mbox.c +++ b/tools/power/x86/intel-speed-select/isst-core-mbox.c @@ -404,7 +404,7 @@ static void _get_uncore_mem_freq(struct isst_id *id, int config_index, } ctdp_level->mem_freq = resp & GENMASK(7, 0); - if (is_spr_platform()) { + if (is_spr_platform() || is_emr_platform()) { ctdp_level->mem_freq *= 200; } else if (is_icx_platform()) { if (ctdp_level->mem_freq < 7) { diff --git a/tools/power/x86/intel-speed-select/isst.h b/tools/power/x86/intel-speed-select/isst.h index 6a37c26b1ef7..54fc21575d56 100644 --- a/tools/power/x86/intel-speed-select/isst.h +++ b/tools/power/x86/intel-speed-select/isst.h @@ -298,6 +298,7 @@ extern int isst_read_pm_config(struct isst_id *id, int *cp_state, int *cp_cap); extern void isst_display_error_info_message(int error, char *msg, int arg_valid, int arg); extern int is_skx_based_platform(void); extern int is_spr_platform(void); +extern int is_emr_platform(void); extern int is_icx_platform(void); extern void isst_trl_display_information(struct isst_id *id, FILE *outf, unsigned long long trl); From 2c00056f54299983009c13a8790fa004e5337d16 Mon Sep 17 00:00:00 2001 From: Srinivas Pandruvada Date: Thu, 23 Feb 2023 06:36:46 -0800 Subject: [PATCH 138/178] tools/power/x86/intel-speed-select: Change TRL display for Emerald Rapids Emerald Rapids doesn't specify TRL (Turbo Ratio Limits) based instruction types. Instead it specifies 5 TRL levels, which can be anyone of the instruction types. Increase TRL levels to 5 for Emerald Rapids. Also change display to show by level number. Show only non zero level values. Signed-off-by: Srinivas Pandruvada --- .../power/x86/intel-speed-select/isst-core-mbox.c | 15 +++++++++++++++ tools/power/x86/intel-speed-select/isst-display.c | 9 ++++++--- 2 files changed, 21 insertions(+), 3 deletions(-) diff --git a/tools/power/x86/intel-speed-select/isst-core-mbox.c b/tools/power/x86/intel-speed-select/isst-core-mbox.c index c860be30fe5e..24bea57f4ff5 100644 --- a/tools/power/x86/intel-speed-select/isst-core-mbox.c +++ b/tools/power/x86/intel-speed-select/isst-core-mbox.c @@ -8,6 +8,8 @@ static int mbox_delay; static int mbox_retries = 3; +#define MAX_TRL_LEVELS_EMR 5 + static int mbox_get_disp_freq_multiplier(void) { return DISP_FREQ_MULTIPLIER; @@ -15,11 +17,24 @@ static int mbox_get_disp_freq_multiplier(void) static int mbox_get_trl_max_levels(void) { + if (is_emr_platform()) + return MAX_TRL_LEVELS_EMR; + return 3; } static char *mbox_get_trl_level_name(int level) { + if (is_emr_platform()) { + static char level_str[18]; + + if (level >= MAX_TRL_LEVELS_EMR) + return NULL; + + snprintf(level_str, sizeof(level_str), "level-%d", level); + return level_str; + } + switch (level) { case 0: return "sse"; diff --git a/tools/power/x86/intel-speed-select/isst-display.c b/tools/power/x86/intel-speed-select/isst-display.c index 0e364b35f093..0403d42ab1ba 100644 --- a/tools/power/x86/intel-speed-select/isst-display.c +++ b/tools/power/x86/intel-speed-select/isst-display.c @@ -283,9 +283,9 @@ static void _isst_fact_display_information(struct isst_id *id, FILE *outf, int l bucket_info[j].hp_cores); format_and_print(outf, base_level + 2, header, value); for (i = 0; i < trl_max_levels; i++) { - if (fact_avx != 0xFF && !(fact_avx & (1 << i))) + if (!bucket_info[j].hp_ratios[i] || (fact_avx != 0xFF && !(fact_avx & (1 << i)))) continue; - if (i == 0 && api_version() == 1) + if (i == 0 && api_version() == 1 && !is_emr_platform()) snprintf(header, sizeof(header), "high-priority-max-frequency(MHz)"); else @@ -301,8 +301,11 @@ static void _isst_fact_display_information(struct isst_id *id, FILE *outf, int l format_and_print(outf, base_level + 1, header, NULL); for (j = 0; j < trl_max_levels; j++) { + if (!fact_info->lp_ratios[j]) + continue; + /* No AVX level name for SSE to be consistent with previous formatting */ - if (j == 0 && api_version() == 1) + if (j == 0 && api_version() == 1 && !is_emr_platform()) snprintf(header, sizeof(header), "low-priority-max-frequency(MHz)"); else snprintf(header, sizeof(header), "low-priority-max-%s-frequency(MHz)", From 19799d3ae2edec99435b792cfe76b1cba74665fe Mon Sep 17 00:00:00 2001 From: Zhang Rui Date: Mon, 17 Oct 2022 19:34:14 +0800 Subject: [PATCH 139/178] tools/power/x86/intel-speed-select: Update version Update tool and supported API version. This is the first version which supports newer Xeon platforms with TPMI support. Signed-off-by: Zhang Rui [srinivas.pandruvada@linux.intel.com: subject and changelog edits] Signed-off-by: Srinivas Pandruvada --- tools/power/x86/intel-speed-select/isst-config.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tools/power/x86/intel-speed-select/isst-config.c b/tools/power/x86/intel-speed-select/isst-config.c index 2ad8726edc06..2ca0cedd418f 100644 --- a/tools/power/x86/intel-speed-select/isst-config.c +++ b/tools/power/x86/intel-speed-select/isst-config.c @@ -15,9 +15,9 @@ struct process_cmd_struct { int arg; }; -static const char *version_str = "v1.14"; +static const char *version_str = "v1.15"; -static const int supported_api_ver = 1; +static const int supported_api_ver = 2; static struct isst_if_platform_info isst_platform_info; static char *progname; static int debug_flag; From 392cacf2aa10de005e58b68a58012c0c81a100c0 Mon Sep 17 00:00:00 2001 From: Nikita Kravets Date: Tue, 21 Mar 2023 01:55:09 +0300 Subject: [PATCH 140/178] 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 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_ From 67f88ffa6d35d6b4e776f98ea64aab0bc026e2a2 Mon Sep 17 00:00:00 2001 From: Jithu Joseph Date: Tue, 21 Mar 2023 17:33:51 -0700 Subject: [PATCH 141/178] 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(-) 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: From 54c9fcd187dd3bbc151cff9e5be01dda4f23dd0d Mon Sep 17 00:00:00 2001 From: Jithu Joseph Date: Tue, 21 Mar 2023 17:33:52 -0700 Subject: [PATCH 142/178] 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(-) 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); From d847eddf0ee9c7112003becebe53fd8bf10d8671 Mon Sep 17 00:00:00 2001 From: Jithu Joseph Date: Tue, 21 Mar 2023 17:33:53 -0700 Subject: [PATCH 143/178] 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(-) 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; -} From c68e3d473988b9af1f39355be57befb83607d845 Mon Sep 17 00:00:00 2001 From: Jithu Joseph Date: Tue, 21 Mar 2023 17:33:54 -0700 Subject: [PATCH 144/178] x86/include/asm/msr-index.h: Add IFS Array test bits Define MSR bitfields for enumerating support for Array BIST test. Signed-off-by: Jithu Joseph Reviewed-by: Tony Luck Reviewed-by: Hans de Goede Link: https://lore.kernel.org/r/20230322003359.213046-5-jithu.joseph@intel.com Signed-off-by: Hans de Goede --- arch/x86/include/asm/msr-index.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/arch/x86/include/asm/msr-index.h b/arch/x86/include/asm/msr-index.h index ad35355ee43e..3aedae61af4f 100644 --- a/arch/x86/include/asm/msr-index.h +++ b/arch/x86/include/asm/msr-index.h @@ -206,6 +206,8 @@ /* Abbreviated from Intel SDM name IA32_INTEGRITY_CAPABILITIES */ #define MSR_INTEGRITY_CAPS 0x000002d9 +#define MSR_INTEGRITY_CAPS_ARRAY_BIST_BIT 2 +#define MSR_INTEGRITY_CAPS_ARRAY_BIST BIT(MSR_INTEGRITY_CAPS_ARRAY_BIST_BIT) #define MSR_INTEGRITY_CAPS_PERIODIC_BIST_BIT 4 #define MSR_INTEGRITY_CAPS_PERIODIC_BIST BIT(MSR_INTEGRITY_CAPS_PERIODIC_BIST_BIT) From d31bbdf42b46cb8dc81deb48c4bf5234dd63d939 Mon Sep 17 00:00:00 2001 From: Jithu Joseph Date: Tue, 21 Mar 2023 17:33:55 -0700 Subject: [PATCH 145/178] 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(-) 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; From 5210fb4e18806648b5e87ecc206f1b35e1253401 Mon Sep 17 00:00:00 2001 From: Jithu Joseph Date: Tue, 21 Mar 2023 17:33:56 -0700 Subject: [PATCH 146/178] 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(-) 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 +}; From fed696ce13662de882c8708ea7d0664d8e9e178b Mon Sep 17 00:00:00 2001 From: Jithu Joseph Date: Tue, 21 Mar 2023 17:33:57 -0700 Subject: [PATCH 147/178] 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(+) 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; } From 2b965dc05dc14bbe00efa3038aa1a3932ca30880 Mon Sep 17 00:00:00 2001 From: Jithu Joseph Date: Tue, 21 Mar 2023 17:33:58 -0700 Subject: [PATCH 148/178] 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(-) 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: From 3a2f2756c5189e03eeb3bedd04e07e8288f207e2 Mon Sep 17 00:00:00 2001 From: Jithu Joseph Date: Tue, 21 Mar 2023 17:33:59 -0700 Subject: [PATCH 149/178] Documentation/ABI: Update IFS ABI doc Array BIST test doesn't need an IFS test image to operate unlike the SCAN test. Consequently current_batch and image_version files are not applicable for Array BIST IFS device instance, clarify this in the ABI doc. Also given that multiple tests are supported, take the opportunity to generalize descriptions wherever applicable. Signed-off-by: Jithu Joseph Reviewed-by: Tony Luck Link: https://lore.kernel.org/r/20230322003359.213046-10-jithu.joseph@intel.com Reviewed-by: Hans de Goede Signed-off-by: Hans de Goede --- .../ABI/testing/sysfs-platform-intel-ifs | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/Documentation/ABI/testing/sysfs-platform-intel-ifs b/Documentation/ABI/testing/sysfs-platform-intel-ifs index 55991983d0d0..41b4d5b1e21c 100644 --- a/Documentation/ABI/testing/sysfs-platform-intel-ifs +++ b/Documentation/ABI/testing/sysfs-platform-intel-ifs @@ -1,3 +1,7 @@ +Device instance to test mapping +intel_ifs_0 -> Scan Test +intel_ifs_1 -> Array BIST test + What: /sys/devices/virtual/misc/intel_ifs_/run_test Date: Nov 16 2022 KernelVersion: 6.2 @@ -8,6 +12,7 @@ Description: Write to trigger IFS test for one online core. completes the test for the core containing that thread. Example: to test the core containing cpu5: echo 5 > /sys/devices/virtual/misc/intel_ifs_/run_test +Devices: all What: /sys/devices/virtual/misc/intel_ifs_/status Date: Nov 16 2022 @@ -15,21 +20,25 @@ KernelVersion: 6.2 Contact: "Jithu Joseph" Description: The status of the last test. It can be one of "pass", "fail" or "untested". +Devices: all What: /sys/devices/virtual/misc/intel_ifs_/details Date: Nov 16 2022 KernelVersion: 6.2 Contact: "Jithu Joseph" Description: Additional information regarding the last test. The details file reports - the hex value of the SCAN_STATUS MSR. Note that the error_code field + the hex value of the STATUS MSR for this test. Note that the error_code field may contain driver defined software code not defined in the Intel SDM. +Devices: all What: /sys/devices/virtual/misc/intel_ifs_/image_version Date: Nov 16 2022 KernelVersion: 6.2 Contact: "Jithu Joseph" -Description: Version (hexadecimal) of loaded IFS binary image. If no scan image - is loaded reports "none". +Description: Version (hexadecimal) of loaded IFS test image. If no test image + is loaded reports "none". Only present for device instances where a test image + is applicable. +Devices: intel_ifs_0 What: /sys/devices/virtual/misc/intel_ifs_/current_batch Date: Nov 16 2022 @@ -39,3 +48,5 @@ Description: Write a number less than or equal to 0xff to load an IFS test image The number written treated as the 2 digit suffix in the following file name: /lib/firmware/intel/ifs_/ff-mm-ss-02x.scan Reading the file will provide the suffix of the currently loaded IFS test image. + This file is present only for device instances where a test image is applicable. +Devices: intel_ifs_0 From 710ddfbfda939b84794fa3912047bbc6754cbddc Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Sat, 25 Mar 2023 14:08:02 +0300 Subject: [PATCH 150/178] 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(-) 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); From 0de0ab9a659a419d1aa9c2516887e95a51455da0 Mon Sep 17 00:00:00 2001 From: Andrew Kallmeyer Date: Tue, 28 Mar 2023 18:45:58 -0700 Subject: [PATCH 151/178] 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 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_ */ From e82882cdd241b78d305df1441c624fbbbd07af05 Mon Sep 17 00:00:00 2001 From: Gergo Koteles Date: Tue, 28 Mar 2023 18:45:59 -0700 Subject: [PATCH 152/178] 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 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"); From 3e9adae9336f1d530f342e5d5fbfc7d349f25b95 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20Wei=C3=9Fschuh?= Date: Wed, 29 Mar 2023 04:50:25 +0000 Subject: [PATCH 153/178] 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(-) 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; From 01862d01cb36ad36233939c07d6438a2f83dcd46 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Sat, 1 Apr 2023 17:07:35 +0200 Subject: [PATCH 154/178] 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 --- .../platform/x86/x86-android-tablets/lenovo.c | 29 ++++--------------- .../x86-android-tablets/x86-android-tablets.h | 3 +- 2 files changed, 7 insertions(+), 25 deletions(-) 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; From ec5a4565fd6e15ccbe5710a326a2f283c0507389 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Sat, 1 Apr 2023 17:07:36 +0200 Subject: [PATCH 155/178] 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 --- .../platform/x86/x86-android-tablets/lenovo.c | 37 ++++++++----------- 1 file changed, 16 insertions(+), 21 deletions(-) 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", } From c69fec50ec2e3aeea46b13235b72d9d5a1bf095a Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Sat, 1 Apr 2023 17:07:37 +0200 Subject: [PATCH 156/178] 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 --- .../platform/x86/x86-android-tablets/dmi.c | 9 ++ .../platform/x86/x86-android-tablets/lenovo.c | 122 ++++++++++++++++++ .../x86-android-tablets/x86-android-tablets.h | 1 + 3 files changed, 132 insertions(+) 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; From fb131c472df92b2885262e747906c0a4038c82e5 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Tue, 4 Apr 2023 13:19:55 +0200 Subject: [PATCH 157/178] 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(-) 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) From 7bef7eea539dac6182e25e2e8d4a33063d458878 Mon Sep 17 00:00:00 2001 From: Khalil Blaiech Date: Wed, 5 Apr 2023 09:16:29 -0400 Subject: [PATCH 158/178] 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(+) 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 From 4f59630a5ed0a4e7d275bd7e5d253a8f5a425c5a Mon Sep 17 00:00:00 2001 From: Srinivas Pandruvada Date: Thu, 30 Mar 2023 07:59:39 -0700 Subject: [PATCH 159/178] 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 --- .../platform/x86/intel/uncore-frequency/uncore-frequency.c | 7 +++++++ 1 file changed, 7 insertions(+) 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); From aec8298c093f052fc8a86f9411b69b23953b0edb Mon Sep 17 00:00:00 2001 From: Shyam Sundar S K Date: Thu, 6 Apr 2023 22:18:07 +0530 Subject: [PATCH 160/178] 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(-) 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); } From b845772677ea19b8e4c032bc07393ef32de4ee39 Mon Sep 17 00:00:00 2001 From: Mario Limonciello Date: Mon, 10 Apr 2023 00:23:41 +0530 Subject: [PATCH 161/178] 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(+) 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; From 5ec9ee0d464750d72972d5685edf675824e259a1 Mon Sep 17 00:00:00 2001 From: Mario Limonciello Date: Mon, 10 Apr 2023 00:23:42 +0530 Subject: [PATCH 162/178] 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(-) 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) { From 7abc3618b65304d409d9489d77e4a8f047842fb7 Mon Sep 17 00:00:00 2001 From: Mario Limonciello Date: Mon, 10 Apr 2023 00:23:43 +0530 Subject: [PATCH 163/178] 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(-) 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) From 9217bd1d7699f34a01b26ba14ff38c1714ce1185 Mon Sep 17 00:00:00 2001 From: Mario Limonciello Date: Mon, 10 Apr 2023 00:23:44 +0530 Subject: [PATCH 164/178] 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(-) 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) From 310e782a99c7f16fb533a45d8f9c16defefa5aab Mon Sep 17 00:00:00 2001 From: Shyam Sundar S K Date: Mon, 10 Apr 2023 00:23:45 +0530 Subject: [PATCH 165/178] 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(-) 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; } From 8d99129eef8f42377b41c1bacee9f8ce806e9f44 Mon Sep 17 00:00:00 2001 From: Shyam Sundar S K Date: Mon, 10 Apr 2023 00:23:46 +0530 Subject: [PATCH 166/178] 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(-) 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); } } From 336ba968d3e30038c13b03a71f5a672db6c9e303 Mon Sep 17 00:00:00 2001 From: "David E. Box" Date: Sun, 9 Apr 2023 12:25:35 -0700 Subject: [PATCH 167/178] 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(+) 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); } From 5b309e80f457ebb41770e0122766825a78cfc11b Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Mon, 10 Apr 2023 21:35:12 +0200 Subject: [PATCH 168/178] 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(-) 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); From 82c3a0b7f0cb97445eeb8b91b0a37d95f8c2c2a9 Mon Sep 17 00:00:00 2001 From: Liming Sun Date: Tue, 11 Apr 2023 11:39:42 -0400 Subject: [PATCH 169/178] 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 --- .../testing/sysfs-platform-mellanox-bootctl | 7 ++ drivers/platform/mellanox/mlxbf-bootctl.c | 68 +++++++++++++++++++ 2 files changed, 75 insertions(+) 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, From f6e7ac4c35a28aef0be93b32c533ae678ad0b9e7 Mon Sep 17 00:00:00 2001 From: Feng Jiang Date: Wed, 12 Apr 2023 17:37:34 +0800 Subject: [PATCH 170/178] 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(+) 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; } From efebfa80ce24cba9e7af786e9dec911cf9e7b35d Mon Sep 17 00:00:00 2001 From: Shyam Sundar S K Date: Wed, 12 Apr 2023 16:45:00 +0530 Subject: [PATCH 171/178] 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(+) 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) { From 3f581602a22cc5445a66501fa13a80894d83e42a Mon Sep 17 00:00:00 2001 From: "David E. Box" Date: Wed, 12 Apr 2023 18:29:22 -0700 Subject: [PATCH 172/178] 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(-) 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; From 14f6f0e3709a8aa987ae78783d2e4d63f88cc13a Mon Sep 17 00:00:00 2001 From: "David E. Box" Date: Wed, 12 Apr 2023 18:32:30 -0700 Subject: [PATCH 173/178] 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(-) 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 From 3a133f7c51b20d5d330d5424f3ef0dc283992500 Mon Sep 17 00:00:00 2001 From: Daniel Bertalan Date: Fri, 14 Apr 2023 18:02:10 +0000 Subject: [PATCH 174/178] 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(+) 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(); From 02377e983e22396e52d43c4d0adb9bbc85fa9a26 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Sun, 16 Apr 2023 23:28:38 +0200 Subject: [PATCH 175/178] 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(-) 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), From 95b829f89da3fd635b60b9b069395dede17a612c Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Sun, 16 Apr 2023 23:28:39 +0200 Subject: [PATCH 176/178] 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 --- .../platform/x86/x86-android-tablets/lenovo.c | 35 +++++++++++++++++++ 1 file changed, 35 insertions(+) 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 }; From 1d3f7a31aa9671c43e03570edaebcd7e02751c15 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Sun, 16 Apr 2023 23:28:40 +0200 Subject: [PATCH 177/178] 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(+) 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, }; From e578c943e363ff47d08e7b4f5648f3da9db325d8 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Sun, 16 Apr 2023 23:28:41 +0200 Subject: [PATCH 178/178] 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 --- .../platform/x86/x86-android-tablets/lenovo.c | 32 ++++++++++++++++++- 1 file changed, 31 insertions(+), 1 deletion(-) 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;