From f25d34646bd01505a0989ca67bc9a37390cae755 Mon Sep 17 00:00:00 2001 From: Armin Wolf Date: Fri, 3 Nov 2023 19:25:23 +0100 Subject: platform/x86: wmi: Add wmidev_block_set() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Currently, WMI drivers have to use the deprecated GUID-based interface when setting data blocks. This prevents those drivers from fully moving away from this interface. Provide wmidev_block_set() so drivers using wmi_set_block() can fully migrate to the modern bus-based interface. Tested with a custom SSDT from the Intel Slim Bootloader project. Signed-off-by: Armin Wolf Link: https://lore.kernel.org/r/20231103182526.3524-1-W_Armin@gmx.de Reviewed-by: Ilpo Järvinen Reviewed-by: Hans de Goede Signed-off-by: Hans de Goede --- drivers/platform/x86/wmi.c | 64 ++++++++++++++++++++++++---------------------- include/linux/wmi.h | 2 ++ 2 files changed, 36 insertions(+), 30 deletions(-) diff --git a/drivers/platform/x86/wmi.c b/drivers/platform/x86/wmi.c index 5c27b4aa9690..9d9a050e7086 100644 --- a/drivers/platform/x86/wmi.c +++ b/drivers/platform/x86/wmi.c @@ -536,41 +536,50 @@ EXPORT_SYMBOL_GPL(wmidev_block_query); * * Return: acpi_status signaling success or error. */ -acpi_status wmi_set_block(const char *guid_string, u8 instance, - const struct acpi_buffer *in) +acpi_status wmi_set_block(const char *guid_string, u8 instance, const struct acpi_buffer *in) { - struct wmi_block *wblock; - struct guid_block *block; struct wmi_device *wdev; - acpi_handle handle; - struct acpi_object_list input; - union acpi_object params[2]; - char method[WMI_ACPI_METHOD_NAME_SIZE]; acpi_status status; - if (!in) - return AE_BAD_DATA; - wdev = wmi_find_device_by_guid(guid_string); if (IS_ERR(wdev)) return AE_ERROR; - wblock = container_of(wdev, struct wmi_block, dev); - block = &wblock->gblock; - handle = wblock->acpi_device->handle; + status = wmidev_block_set(wdev, instance, in); + wmi_device_put(wdev); - if (block->instance_count <= instance) { - status = AE_BAD_PARAMETER; + return status; +} +EXPORT_SYMBOL_GPL(wmi_set_block); - goto err_wdev_put; - } +/** + * wmidev_block_set - Write to a WMI block + * @wdev: A wmi bus device from a driver + * @instance: Instance index + * @in: Buffer containing new values for the data block + * + * Write contents of the input buffer to an ACPI-WMI data block. + * + * Return: acpi_status signaling success or error. + */ +acpi_status wmidev_block_set(struct wmi_device *wdev, u8 instance, const struct acpi_buffer *in) +{ + struct wmi_block *wblock = container_of(wdev, struct wmi_block, dev); + acpi_handle handle = wblock->acpi_device->handle; + struct guid_block *block = &wblock->gblock; + char method[WMI_ACPI_METHOD_NAME_SIZE]; + struct acpi_object_list input; + union acpi_object params[2]; - /* Check GUID is a data block */ - if (block->flags & (ACPI_WMI_EVENT | ACPI_WMI_METHOD)) { - status = AE_ERROR; + if (!in) + return AE_BAD_DATA; - goto err_wdev_put; - } + if (block->instance_count <= instance) + return AE_BAD_PARAMETER; + + /* Check GUID is a data block */ + if (block->flags & (ACPI_WMI_EVENT | ACPI_WMI_METHOD)) + return AE_ERROR; input.count = 2; input.pointer = params; @@ -582,14 +591,9 @@ acpi_status wmi_set_block(const char *guid_string, u8 instance, get_acpi_method_name(wblock, 'S', method); - status = acpi_evaluate_object(handle, method, &input, NULL); - -err_wdev_put: - wmi_device_put(wdev); - - return status; + return acpi_evaluate_object(handle, method, &input, NULL); } -EXPORT_SYMBOL_GPL(wmi_set_block); +EXPORT_SYMBOL_GPL(wmidev_block_set); static void wmi_dump_wdg(const struct guid_block *g) { diff --git a/include/linux/wmi.h b/include/linux/wmi.h index 763bd382cf2d..207544968268 100644 --- a/include/linux/wmi.h +++ b/include/linux/wmi.h @@ -35,6 +35,8 @@ extern acpi_status wmidev_evaluate_method(struct wmi_device *wdev, extern union acpi_object *wmidev_block_query(struct wmi_device *wdev, u8 instance); +acpi_status wmidev_block_set(struct wmi_device *wdev, u8 instance, const struct acpi_buffer *in); + u8 wmidev_instance_count(struct wmi_device *wdev); extern int set_required_buffer_size(struct wmi_device *wdev, u64 length); -- cgit From 7275bf3e09578e1761157e7683f2e898c5c235a6 Mon Sep 17 00:00:00 2001 From: Armin Wolf Date: Fri, 3 Nov 2023 19:25:24 +0100 Subject: platform/x86: wmi: Add to_wmi_device() helper macro MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add a helper macro for WMI drivers to cast a device to the corresponding WMI device. This should replace some boilerplate code. Signed-off-by: Armin Wolf Link: https://lore.kernel.org/r/20231103182526.3524-2-W_Armin@gmx.de Reviewed-by: Ilpo Järvinen Reviewed-by: Hans de Goede Signed-off-by: Hans de Goede --- include/linux/wmi.h | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/include/linux/wmi.h b/include/linux/wmi.h index 207544968268..8a643c39fcce 100644 --- a/include/linux/wmi.h +++ b/include/linux/wmi.h @@ -27,6 +27,14 @@ struct wmi_device { bool setable; }; +/** + * to_wmi_device() - Helper macro to cast a device to a wmi_device + * @device: device struct + * + * Cast a struct device to a struct wmi_device. + */ +#define to_wmi_device(device) container_of(device, struct wmi_device, dev) + extern acpi_status wmidev_evaluate_method(struct wmi_device *wdev, u8 instance, u32 method_id, const struct acpi_buffer *in, -- cgit From 75c487fcb69c981f9bd21f91e6e3b8b2080d7ab0 Mon Sep 17 00:00:00 2001 From: Armin Wolf Date: Fri, 3 Nov 2023 19:25:25 +0100 Subject: platform/x86: intel-wmi-sbl-fw-update: Use bus-based WMI interface MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Currently, the driver was still using the deprecated GUID-based interface to query/set data blocks. Use the modern bus-based interface for this. Tested with a custom SSDT from the Intel Slim Bootloader project. Reviewed-by: Jithu Joseph Signed-off-by: Armin Wolf Link: https://lore.kernel.org/r/20231103182526.3524-3-W_Armin@gmx.de Reviewed-by: Ilpo Järvinen Reviewed-by: Hans de Goede Signed-off-by: Hans de Goede --- drivers/platform/x86/intel/wmi/sbl-fw-update.c | 13 ++++--------- drivers/platform/x86/wmi.c | 1 + 2 files changed, 5 insertions(+), 9 deletions(-) diff --git a/drivers/platform/x86/intel/wmi/sbl-fw-update.c b/drivers/platform/x86/intel/wmi/sbl-fw-update.c index 3c86e0108a24..9cf5ed0f8dc2 100644 --- a/drivers/platform/x86/intel/wmi/sbl-fw-update.c +++ b/drivers/platform/x86/intel/wmi/sbl-fw-update.c @@ -25,18 +25,13 @@ static int get_fwu_request(struct device *dev, u32 *out) { - struct acpi_buffer result = {ACPI_ALLOCATE_BUFFER, NULL}; union acpi_object *obj; - acpi_status status; - status = wmi_query_block(INTEL_WMI_SBL_GUID, 0, &result); - if (ACPI_FAILURE(status)) { - dev_err(dev, "wmi_query_block failed\n"); + obj = wmidev_block_query(to_wmi_device(dev), 0); + if (!obj) return -ENODEV; - } - obj = (union acpi_object *)result.pointer; - if (!obj || obj->type != ACPI_TYPE_INTEGER) { + if (obj->type != ACPI_TYPE_INTEGER) { dev_warn(dev, "wmi_query_block returned invalid value\n"); kfree(obj); return -EINVAL; @@ -58,7 +53,7 @@ static int set_fwu_request(struct device *dev, u32 in) input.length = sizeof(u32); input.pointer = &value; - status = wmi_set_block(INTEL_WMI_SBL_GUID, 0, &input); + status = wmidev_block_set(to_wmi_device(dev), 0, &input); if (ACPI_FAILURE(status)) { dev_err(dev, "wmi_set_block failed\n"); return -ENODEV; diff --git a/drivers/platform/x86/wmi.c b/drivers/platform/x86/wmi.c index 9d9a050e7086..4c4effc883ae 100644 --- a/drivers/platform/x86/wmi.c +++ b/drivers/platform/x86/wmi.c @@ -106,6 +106,7 @@ MODULE_DEVICE_TABLE(acpi, wmi_device_ids); static const char * const allow_duplicates[] = { "05901221-D566-11D1-B2F0-00A0C9062910", /* wmi-bmof */ "8A42EA14-4F2A-FD45-6422-0087F7A7E608", /* dell-wmi-ddv */ + "44FADEB1-B204-40F2-8581-394BBDC1B651", /* intel-wmi-sbl-fw-update */ NULL }; -- cgit From 2340f12023efa7dc256f496d85d2411ca47cb9a2 Mon Sep 17 00:00:00 2001 From: Armin Wolf Date: Fri, 3 Nov 2023 19:25:26 +0100 Subject: platform/x86/intel/wmi: thunderbolt: Use bus-based WMI interface MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Currently, the driver still uses the legacy GUID-based interface to invoke WMI methods. Use the modern bus-based interface instead. Tested on a Lenovo E51-80. Signed-off-by: Armin Wolf Link: https://lore.kernel.org/r/20231103182526.3524-4-W_Armin@gmx.de Reviewed-by: Ilpo Järvinen Reviewed-by: Hans de Goede Signed-off-by: Hans de Goede --- drivers/platform/x86/intel/wmi/thunderbolt.c | 3 +-- drivers/platform/x86/wmi.c | 1 + 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/platform/x86/intel/wmi/thunderbolt.c b/drivers/platform/x86/intel/wmi/thunderbolt.c index fc333ff82d1e..e2ad3f46f356 100644 --- a/drivers/platform/x86/intel/wmi/thunderbolt.c +++ b/drivers/platform/x86/intel/wmi/thunderbolt.c @@ -32,8 +32,7 @@ static ssize_t force_power_store(struct device *dev, mode = hex_to_bin(buf[0]); dev_dbg(dev, "force_power: storing %#x\n", mode); if (mode == 0 || mode == 1) { - status = wmi_evaluate_method(INTEL_WMI_THUNDERBOLT_GUID, 0, 1, - &input, NULL); + status = wmidev_evaluate_method(to_wmi_device(dev), 0, 1, &input, NULL); if (ACPI_FAILURE(status)) { dev_dbg(dev, "force_power: failed to evaluate ACPI method\n"); return -ENODEV; diff --git a/drivers/platform/x86/wmi.c b/drivers/platform/x86/wmi.c index 4c4effc883ae..cb7e74f2b009 100644 --- a/drivers/platform/x86/wmi.c +++ b/drivers/platform/x86/wmi.c @@ -107,6 +107,7 @@ static const char * const allow_duplicates[] = { "05901221-D566-11D1-B2F0-00A0C9062910", /* wmi-bmof */ "8A42EA14-4F2A-FD45-6422-0087F7A7E608", /* dell-wmi-ddv */ "44FADEB1-B204-40F2-8581-394BBDC1B651", /* intel-wmi-sbl-fw-update */ + "86CCFD48-205E-4A77-9C48-2021CBEDE341", /* intel-wmi-thunderbolt */ NULL }; -- cgit From 57eb82ff34e3e3dfa95a80c40ef5a4764c833ec6 Mon Sep 17 00:00:00 2001 From: Jules Irenge Date: Fri, 3 Nov 2023 23:54:08 +0000 Subject: platform/mellanox: mlxbf-tmfifo: Remove unnecessary bool conversion MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This commit fixes coccinelle warning in macro function IS_VRING_DROP() which complains conversion to bool not needed here. Signed-off-by: Jules Irenge Link: https://lore.kernel.org/r/ZUWIIKbz4vukl8qb@octinomon Reviewed-by: Ilpo Järvinen Reviewed-by: Hans de Goede Signed-off-by: Hans de Goede --- drivers/platform/mellanox/mlxbf-tmfifo.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/platform/mellanox/mlxbf-tmfifo.c b/drivers/platform/mellanox/mlxbf-tmfifo.c index 5c683b4eaf10..ed16ec422a7b 100644 --- a/drivers/platform/mellanox/mlxbf-tmfifo.c +++ b/drivers/platform/mellanox/mlxbf-tmfifo.c @@ -91,7 +91,7 @@ struct mlxbf_tmfifo_vring { /* Check whether vring is in drop mode. */ #define IS_VRING_DROP(_r) ({ \ typeof(_r) (r) = (_r); \ - (r->desc_head == &r->drop_desc ? true : false); }) + r->desc_head == &r->drop_desc; }) /* A stub length to drop maximum length packet. */ #define VRING_DROP_DESC_MAX_LEN GENMASK(15, 0) -- cgit From 8d437a0b68c175ed591322e53b7e1f91094abfd5 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Sat, 4 Nov 2023 21:58:25 +0100 Subject: ACPI: scan: Add LNXVIDEO HID to ignore_serial_bus_ids[] The I2C-core already has filtering to skip i2c_client instantiation for LNXVIDEO acpi_device-s with I2cSerialBus resources, since LNXVIDEO devices are not i2c_client-s and are handled by the acpi_video driver. This filtering was added to i2c-core-acpi.c in commit 3a4991a9864c ("i2c: acpi: Do not create i2c-clients for LNXVIDEO ACPI devices"). Now a similar problem has shown up where the SPI-core is instantiating an unwanted SPI-device for a SpiSerialBus resource under a LNXVIDEO acpi_device. On a Lenovo Yoga Tab 3 YT3-X90F this unwanted SPI-device instanstantiation causes the SPI-device instanstantiation for the WM5102 audio codec to fail with: [ 21.988441] pxa2xx-spi 8086228E:00: chipselect 0 already in use Instead of duplicating the I2C-core filtering in the SPI-core code, push the filtering of SerialBus resources under LNXVIDEO acpi_device-s up into the ACPI-core by adding the LNXVIDEO HID to ignore_serial_bus_ids[]. Note the filtering in the I2C-core i2c_acpi_do_lookup() function is still necessary because this not only impacts i2c_client instantiation but it also makes the I2C-core ignore the I2cSerialBus resource when checking what the maximum speed is the I2C bus supports, which is still necessary. Acked-by: Rafael J. Wysocki Signed-off-by: Hans de Goede Link: https://lore.kernel.org/r/20231104205828.63139-1-hdegoede@redhat.com --- drivers/acpi/scan.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/acpi/scan.c b/drivers/acpi/scan.c index fa5dd71a80fa..46a9238c72c6 100644 --- a/drivers/acpi/scan.c +++ b/drivers/acpi/scan.c @@ -1727,6 +1727,7 @@ static bool acpi_device_enumeration_by_parent(struct acpi_device *device) * Some ACPI devs contain SerialBus resources even though they are not * attached to a serial bus at all. */ + {ACPI_VIDEO_HID, }, {"MSHW0028", }, /* * HIDs of device with an UartSerialBusV2 resource for which userspace -- cgit From 70505ea6de24093136103cedcf2deeb85891ed6c Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Sat, 4 Nov 2023 21:58:26 +0100 Subject: platform/x86: x86-android-tablets: Add support for SPI device instantiation Some x86 Android tablets have SPI devices which are not properly described in their DSDT. Add support for instantiating SPI devices. Signed-off-by: Hans de Goede Link: https://lore.kernel.org/r/20231104205828.63139-2-hdegoede@redhat.com --- drivers/platform/x86/x86-android-tablets/core.c | 62 ++++++++++++++++++++++ .../x86/x86-android-tablets/x86-android-tablets.h | 9 ++++ 2 files changed, 71 insertions(+) diff --git a/drivers/platform/x86/x86-android-tablets/core.c b/drivers/platform/x86/x86-android-tablets/core.c index b55957bde034..6a5975ac3286 100644 --- a/drivers/platform/x86/x86-android-tablets/core.c +++ b/drivers/platform/x86/x86-android-tablets/core.c @@ -141,9 +141,11 @@ int x86_acpi_irq_helper_get(const struct x86_acpi_irq_data *data) } static int i2c_client_count; +static int spi_dev_count; static int pdev_count; static int serdev_count; static struct i2c_client **i2c_clients; +static struct spi_device **spi_devs; static struct platform_device **pdevs; static struct serdev_device **serdevs; static struct gpio_keys_button *buttons; @@ -185,6 +187,46 @@ static __init int x86_instantiate_i2c_client(const struct x86_dev_info *dev_info return 0; } +static __init int x86_instantiate_spi_dev(const struct x86_dev_info *dev_info, int idx) +{ + const struct x86_spi_dev_info *spi_dev_info = &dev_info->spi_dev_info[idx]; + struct spi_board_info board_info = spi_dev_info->board_info; + struct spi_controller *controller; + struct acpi_device *adev; + acpi_handle handle; + acpi_status status; + + board_info.irq = x86_acpi_irq_helper_get(&spi_dev_info->irq_data); + if (board_info.irq < 0) + return board_info.irq; + + status = acpi_get_handle(NULL, spi_dev_info->ctrl_path, &handle); + if (ACPI_FAILURE(status)) { + pr_err("Error could not get %s handle\n", spi_dev_info->ctrl_path); + return -ENODEV; + } + + adev = acpi_fetch_acpi_dev(handle); + if (!adev) { + pr_err("Error could not get adev for %s\n", spi_dev_info->ctrl_path); + return -ENODEV; + } + + controller = acpi_spi_find_controller_by_adev(adev); + if (!controller) { + pr_err("Error could not get SPI controller for %s\n", spi_dev_info->ctrl_path); + return -ENODEV; + } + + spi_devs[idx] = spi_new_device(controller, &board_info); + put_device(&controller->dev); + if (IS_ERR(spi_devs[idx])) + return dev_err_probe(&controller->dev, PTR_ERR(spi_devs[idx]), + "creating SPI-device %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; @@ -263,6 +305,11 @@ static void x86_android_tablet_remove(struct platform_device *pdev) kfree(pdevs); kfree(buttons); + for (i = 0; i < spi_dev_count; i++) + spi_unregister_device(spi_devs[i]); + + kfree(spi_devs); + for (i = 0; i < i2c_client_count; i++) i2c_unregister_device(i2c_clients[i]); @@ -333,6 +380,21 @@ static __init int x86_android_tablet_probe(struct platform_device *pdev) } } + spi_devs = kcalloc(dev_info->spi_dev_count, sizeof(*spi_devs), GFP_KERNEL); + if (!spi_devs) { + x86_android_tablet_remove(pdev); + return -ENOMEM; + } + + spi_dev_count = dev_info->spi_dev_count; + for (i = 0; i < spi_dev_count; i++) { + ret = x86_instantiate_spi_dev(dev_info, i); + if (ret < 0) { + x86_android_tablet_remove(pdev); + return ret; + } + } + /* + 1 to make space for (optional) gpio_keys_button pdev */ pdevs = kcalloc(dev_info->pdev_count + 1, sizeof(*pdevs), GFP_KERNEL); if (!pdevs) { 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 9d2fb7fded6d..49fed9410adb 100644 --- a/drivers/platform/x86/x86-android-tablets/x86-android-tablets.h +++ b/drivers/platform/x86/x86-android-tablets/x86-android-tablets.h @@ -14,6 +14,7 @@ #include #include #include +#include struct gpio_desc; struct gpiod_lookup_table; @@ -48,6 +49,12 @@ struct x86_i2c_client_info { struct x86_acpi_irq_data irq_data; }; +struct x86_spi_dev_info { + struct spi_board_info board_info; + char *ctrl_path; + struct x86_acpi_irq_data irq_data; +}; + struct x86_serdev_info { const char *ctrl_hid; const char *ctrl_uid; @@ -72,10 +79,12 @@ struct x86_dev_info { 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 x86_spi_dev_info *spi_dev_info; const struct platform_device_info *pdev_info; const struct x86_serdev_info *serdev_info; const struct x86_gpio_button *gpio_button; int i2c_client_count; + int spi_dev_count; int pdev_count; int serdev_count; int gpio_button_count; -- cgit From 115779bf6abef3161c72311614a16d06d7216213 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Sat, 4 Nov 2023 21:58:27 +0100 Subject: platform/x86: x86-android-tablets: Add audio codec info for Lenovo Yoga Tab 3 Pro YT3-X90F The SPI attached WM5102 codec on the Lenovo Yoga Tab 3 Pro YT3-X90F is not described in the ACPI tables. Add info to instantiate the SPI device for the codec manually. Signed-off-by: Hans de Goede Link: https://lore.kernel.org/r/20231104205828.63139-3-hdegoede@redhat.com --- drivers/platform/x86/x86-android-tablets/lenovo.c | 98 +++++++++++++++++++++++ 1 file changed, 98 insertions(+) diff --git a/drivers/platform/x86/x86-android-tablets/lenovo.c b/drivers/platform/x86/x86-android-tablets/lenovo.c index c1e68211283f..0bc6a74b8beb 100644 --- a/drivers/platform/x86/x86-android-tablets/lenovo.c +++ b/drivers/platform/x86/x86-android-tablets/lenovo.c @@ -12,6 +12,8 @@ #include #include +#include +#include #include #include #include @@ -659,6 +661,88 @@ static const struct x86_i2c_client_info lenovo_yt3_i2c_clients[] __initconst = { } }; +/* + * The AOSP 3.5 mm Headset: Accessory Specification gives the following values: + * Function A Play/Pause: 0 ohm + * Function D Voice assistant: 135 ohm + * Function B Volume Up 240 ohm + * Function C Volume Down 470 ohm + * Minimum Mic DC resistance 1000 ohm + * Minimum Ear speaker impedance 16 ohm + * Note the first max value below must be less then the min. speaker impedance, + * to allow CTIA/OMTP detection to work. The other max values are the closest + * value from extcon-arizona.c:arizona_micd_levels halfway 2 button resistances. + */ +static const struct arizona_micd_range arizona_micd_aosp_ranges[] = { + { .max = 11, .key = KEY_PLAYPAUSE }, + { .max = 186, .key = KEY_VOICECOMMAND }, + { .max = 348, .key = KEY_VOLUMEUP }, + { .max = 752, .key = KEY_VOLUMEDOWN }, +}; + +/* YT3 WM5102 arizona_micd_config comes from Android kernel sources */ +static struct arizona_micd_config lenovo_yt3_wm5102_micd_config[] = { + { 0, 1, 0 }, + { ARIZONA_ACCDET_SRC, 2, 1 }, +}; + +static struct arizona_pdata lenovo_yt3_wm5102_pdata = { + .irq_flags = IRQF_TRIGGER_LOW, + .micd_detect_debounce = 200, + .micd_ranges = arizona_micd_aosp_ranges, + .num_micd_ranges = ARRAY_SIZE(arizona_micd_aosp_ranges), + .hpdet_channel = ARIZONA_ACCDET_MODE_HPL, + + /* Below settings come from Android kernel sources */ + .micd_bias_start_time = 1, + .micd_rate = 6, + .micd_configs = lenovo_yt3_wm5102_micd_config, + .num_micd_configs = ARRAY_SIZE(lenovo_yt3_wm5102_micd_config), + .micbias = { + [0] = { /* MICBIAS1 */ + .mV = 2800, + .ext_cap = 1, + .discharge = 1, + .soft_start = 0, + .bypass = 0, + }, + [1] = { /* MICBIAS2 */ + .mV = 2800, + .ext_cap = 1, + .discharge = 1, + .soft_start = 0, + .bypass = 0, + }, + [2] = { /* MICBIAS2 */ + .mV = 2800, + .ext_cap = 1, + .discharge = 1, + .soft_start = 0, + .bypass = 0, + }, + }, +}; + +static const struct x86_spi_dev_info lenovo_yt3_spi_devs[] __initconst = { + { + /* WM5102 codec */ + .board_info = { + .modalias = "wm5102", + .platform_data = &lenovo_yt3_wm5102_pdata, + .max_speed_hz = 5000000, + }, + .ctrl_path = "\\_SB_.PCI0.SPI1", + .irq_data = { + .type = X86_ACPI_IRQ_TYPE_GPIOINT, + .chip = "INT33FF:00", + .index = 91, + .trigger = ACPI_LEVEL_SENSITIVE, + .polarity = ACPI_ACTIVE_LOW, + .con_id = "wm5102_irq", + }, + } +}; + static int __init lenovo_yt3_init(void) { int ret; @@ -702,14 +786,28 @@ static struct gpiod_lookup_table lenovo_yt3_hideep_gpios = { }, }; +static struct gpiod_lookup_table lenovo_yt3_wm5102_gpios = { + .dev_id = "spi1.0", + .table = { + GPIO_LOOKUP("INT33FF:00", 75, "wlf,spkvdd-ena", GPIO_ACTIVE_HIGH), + GPIO_LOOKUP("INT33FF:00", 81, "wlf,ldoena", GPIO_ACTIVE_HIGH), + GPIO_LOOKUP("INT33FF:00", 82, "reset", GPIO_ACTIVE_HIGH), + GPIO_LOOKUP("arizona", 2, "wlf,micd-pol", GPIO_ACTIVE_HIGH), + { } + }, +}; + static struct gpiod_lookup_table * const lenovo_yt3_gpios[] = { &lenovo_yt3_hideep_gpios, + &lenovo_yt3_wm5102_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), + .spi_dev_info = lenovo_yt3_spi_devs, + .spi_dev_count = ARRAY_SIZE(lenovo_yt3_spi_devs), .gpiod_lookup_tables = lenovo_yt3_gpios, .init = lenovo_yt3_init, }; -- cgit From 93ec6f222c680cef282a2e94fc42a130d34179b2 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Sat, 4 Nov 2023 21:58:28 +0100 Subject: platform/x86: x86-android-tablets: Fix backlight ctrl for Lenovo Yoga Tab 3 Pro YT3-X90F Fix the maximum brightness being much too low on the Yoga Tab 3 Pro. The LP8557 backlight controller can either be configured to multiply its PWM input and the I2C register set level (requiring both to be at 100% for 100% output); or to only take the I2C register set level into account. Multiplying the 2 levels is useful because this will turn off the backlight when the panel goes off and turns off its PWM output. But on the YT3-X90F the panel's PWM output defaults to a duty-cycle of much less then 100%, severely limiting max brightness. In this case the LP8557 should be configured to only take the I2C register into account and the i915 driver must turn off the backlight separately using a VBT MIPI sequence to turn off the backlight. Signed-off-by: Hans de Goede Link: https://lore.kernel.org/r/20231104205828.63139-4-hdegoede@redhat.com --- drivers/platform/x86/x86-android-tablets/lenovo.c | 26 +++++++++++++++++++---- 1 file changed, 22 insertions(+), 4 deletions(-) diff --git a/drivers/platform/x86/x86-android-tablets/lenovo.c b/drivers/platform/x86/x86-android-tablets/lenovo.c index 0bc6a74b8beb..f1c66a61bfc5 100644 --- a/drivers/platform/x86/x86-android-tablets/lenovo.c +++ b/drivers/platform/x86/x86-android-tablets/lenovo.c @@ -34,12 +34,30 @@ * * 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. + * + * The LP8557 can either be configured to multiply its PWM input and + * the I2C register set level (requiring both to be at 100% for 100% output); + * or to only take the I2C register set level into account. + * + * Multiplying the 2 levels is useful because this will turn off the backlight + * when the panel goes off and turns off its PWM output. + * + * But on some models the panel's PWM output defaults to a duty-cycle of + * much less then 100%, severely limiting max brightness. In this case + * the LP8557 should be configured to only take the I2C register into + * account and the i915 driver must turn off the panel and the backlight + * separately using e.g. VBT MIPI sequences to turn off the backlight. */ -static struct lp855x_platform_data lenovo_lp8557_pdata = { +static struct lp855x_platform_data lenovo_lp8557_pwm_and_reg_pdata = { .device_control = 0x86, .initial_brightness = 128, }; +static struct lp855x_platform_data lenovo_lp8557_reg_only_pdata = { + .device_control = 0x85, + .initial_brightness = 128, +}; + /* Lenovo Yoga Book X90F / X90L's Android factory img has everything hardcoded */ static const struct property_entry lenovo_yb1_x90_wacom_props[] = { @@ -122,7 +140,7 @@ static const struct x86_i2c_client_info lenovo_yb1_x90_i2c_clients[] __initconst .type = "lp8557", .addr = 0x2c, .dev_name = "lp8557", - .platform_data = &lenovo_lp8557_pdata, + .platform_data = &lenovo_lp8557_pwm_and_reg_pdata, }, .adapter_path = "\\_SB_.PCI0.I2C4", }, { @@ -358,7 +376,7 @@ static struct x86_i2c_client_info lenovo_yoga_tab2_830_1050_i2c_clients[] __init .type = "lp8557", .addr = 0x2c, .dev_name = "lp8557", - .platform_data = &lenovo_lp8557_pdata, + .platform_data = &lenovo_lp8557_pwm_and_reg_pdata, }, .adapter_path = "\\_SB_.I2C3", }, @@ -655,7 +673,7 @@ static const struct x86_i2c_client_info lenovo_yt3_i2c_clients[] __initconst = { .type = "lp8557", .addr = 0x2c, .dev_name = "lp8557", - .platform_data = &lenovo_lp8557_pdata, + .platform_data = &lenovo_lp8557_reg_only_pdata, }, .adapter_path = "\\_SB_.PCI0.I2C1", } -- cgit From 3ecb4d85461a34323a849769030841533fcd0395 Mon Sep 17 00:00:00 2001 From: Christophe JAILLET Date: Sun, 12 Nov 2023 08:44:15 +0100 Subject: platform/x86/dell: alienware-wmi: Use kasprintf() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Use kasprintf() instead of hand writing it. This saves the need of an intermediate buffer. Signed-off-by: Christophe JAILLET Link: https://lore.kernel.org/r/f2b2c9e5d80550e480a627c1b2139d5cc9472ffa.1699775015.git.christophe.jaillet@wanadoo.fr Reviewed-by: Ilpo Järvinen Reviewed-by: Hans de Goede Signed-off-by: Hans de Goede --- drivers/platform/x86/dell/alienware-wmi.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/drivers/platform/x86/dell/alienware-wmi.c b/drivers/platform/x86/dell/alienware-wmi.c index a9477e5432e4..f5ee62ce1753 100644 --- a/drivers/platform/x86/dell/alienware-wmi.c +++ b/drivers/platform/x86/dell/alienware-wmi.c @@ -429,7 +429,6 @@ static DEVICE_ATTR(lighting_control_state, 0644, show_control_state, static int alienware_zone_init(struct platform_device *dev) { u8 zone; - char buffer[10]; char *name; if (interface == WMAX) { @@ -466,8 +465,7 @@ static int alienware_zone_init(struct platform_device *dev) return -ENOMEM; for (zone = 0; zone < quirks->num_zones; zone++) { - sprintf(buffer, "zone%02hhX", zone); - name = kstrdup(buffer, GFP_KERNEL); + name = kasprintf(GFP_KERNEL, "zone%02hhX", zone); if (name == NULL) return 1; sysfs_attr_init(&zone_dev_attrs[zone].attr); -- cgit From 6bb5153dfbaf88fa4b40ef50b706d2fb186ed92e Mon Sep 17 00:00:00 2001 From: SungHwan Jung Date: Fri, 24 Nov 2023 18:41:18 +0900 Subject: platform/x86: acer-wmi: Add platform profile and mode key support for Predator PHN16-71 The Acer Predator PHN16-71 has the mode key that is used to rotate thermal modes or toggle turbo mode with predator sense app (ver. 4) on windows. This patch includes platform profile and the mode key support for the device and also includes a small fix for "WMI_gaming_execute_u64" function. Signed-off-by: SungHwan Jung Link: https://lore.kernel.org/r/20231124094122.100707-2-onenowy@gmail.com Reviewed-by: Hans de Goede Signed-off-by: Hans de Goede --- drivers/platform/x86/Kconfig | 1 + drivers/platform/x86/acer-wmi.c | 271 +++++++++++++++++++++++++++++++++++++++- 2 files changed, 267 insertions(+), 5 deletions(-) diff --git a/drivers/platform/x86/Kconfig b/drivers/platform/x86/Kconfig index 7e69fdaccdd5..dbde7e018dc1 100644 --- a/drivers/platform/x86/Kconfig +++ b/drivers/platform/x86/Kconfig @@ -181,6 +181,7 @@ config ACER_WMI select INPUT_SPARSEKMAP select LEDS_CLASS select NEW_LEDS + select ACPI_PLATFORM_PROFILE help This is a driver for newer Acer (and Wistron) laptops. It adds wireless radio and bluetooth control, and on some laptops, diff --git a/drivers/platform/x86/acer-wmi.c b/drivers/platform/x86/acer-wmi.c index 0e472aa9bf41..55fedc2e656e 100644 --- a/drivers/platform/x86/acer-wmi.c +++ b/drivers/platform/x86/acer-wmi.c @@ -20,6 +20,7 @@ #include #include #include +#include #include #include #include @@ -62,9 +63,12 @@ MODULE_LICENSE("GPL"); #define ACER_WMID_SET_GAMING_LED_METHODID 2 #define ACER_WMID_GET_GAMING_LED_METHODID 4 +#define ACER_WMID_GET_GAMING_SYS_INFO_METHODID 5 #define ACER_WMID_SET_GAMING_FAN_BEHAVIOR 14 #define ACER_WMID_SET_GAMING_MISC_SETTING_METHODID 22 +#define ACER_PREDATOR_V4_THERMAL_PROFILE_EC_OFFSET 0x54 + /* * Acer ACPI method GUIDs */ @@ -90,6 +94,10 @@ enum acer_wmi_event_ids { WMID_GAMING_TURBO_KEY_EVENT = 0x7, }; +enum acer_wmi_predator_v4_sys_info_command { + ACER_WMID_CMD_GET_PREDATOR_V4_BAT_STATUS = 0x02, +}; + static const struct key_entry acer_wmi_keymap[] __initconst = { {KE_KEY, 0x01, {KEY_WLAN} }, /* WiFi */ {KE_KEY, 0x03, {KEY_WLAN} }, /* WiFi */ @@ -229,9 +237,10 @@ struct hotkey_function_type_aa { #define ACER_CAP_THREEG BIT(4) #define ACER_CAP_SET_FUNCTION_MODE BIT(5) #define ACER_CAP_KBD_DOCK BIT(6) -#define ACER_CAP_TURBO_OC BIT(7) -#define ACER_CAP_TURBO_LED BIT(8) -#define ACER_CAP_TURBO_FAN BIT(9) +#define ACER_CAP_TURBO_OC BIT(7) +#define ACER_CAP_TURBO_LED BIT(8) +#define ACER_CAP_TURBO_FAN BIT(9) +#define ACER_CAP_PLATFORM_PROFILE BIT(10) /* * Interface type flags @@ -259,6 +268,7 @@ static bool ec_raw_mode; static bool has_type_aa; static u16 commun_func_bitmap; static u8 commun_fn_key_number; +static bool cycle_gaming_thermal_profile = true; module_param(mailled, int, 0444); module_param(brightness, int, 0444); @@ -266,12 +276,15 @@ module_param(threeg, int, 0444); module_param(force_series, int, 0444); module_param(force_caps, int, 0444); module_param(ec_raw_mode, bool, 0444); +module_param(cycle_gaming_thermal_profile, bool, 0644); MODULE_PARM_DESC(mailled, "Set initial state of Mail LED"); MODULE_PARM_DESC(brightness, "Set initial LCD backlight brightness"); MODULE_PARM_DESC(threeg, "Set initial state of 3G hardware"); MODULE_PARM_DESC(force_series, "Force a different laptop series"); MODULE_PARM_DESC(force_caps, "Force the capability bitmask to this value"); MODULE_PARM_DESC(ec_raw_mode, "Enable EC raw mode"); +MODULE_PARM_DESC(cycle_gaming_thermal_profile, + "Set thermal mode key in cycle mode. Disabling it sets the mode key in turbo toggle mode"); struct acer_data { int mailled; @@ -321,6 +334,7 @@ struct quirk_entry { u8 turbo; u8 cpu_fans; u8 gpu_fans; + u8 predator_v4; }; static struct quirk_entry *quirks; @@ -336,6 +350,9 @@ static void __init set_quirks(void) if (quirks->turbo) interface->capability |= ACER_CAP_TURBO_OC | ACER_CAP_TURBO_LED | ACER_CAP_TURBO_FAN; + + if (quirks->predator_v4) + interface->capability |= ACER_CAP_PLATFORM_PROFILE; } static int __init dmi_matched(const struct dmi_system_id *dmi) @@ -370,6 +387,10 @@ static struct quirk_entry quirk_acer_predator_ph315_53 = { .gpu_fans = 1, }; +static struct quirk_entry quirk_acer_predator_v4 = { + .predator_v4 = 1, +}; + /* This AMW0 laptop has no bluetooth */ static struct quirk_entry quirk_medion_md_98300 = { .wireless = 1, @@ -546,6 +567,15 @@ static const struct dmi_system_id acer_quirks[] __initconst = { }, .driver_data = &quirk_acer_predator_ph315_53, }, + { + .callback = dmi_matched, + .ident = "Acer Predator PHN16-71", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Acer"), + DMI_MATCH(DMI_PRODUCT_NAME, "Predator PHN16-71"), + }, + .driver_data = &quirk_acer_predator_v4, + }, { .callback = set_force_caps, .ident = "Acer Aspire Switch 10E SW3-016", @@ -659,6 +689,31 @@ static const struct dmi_system_id non_acer_quirks[] __initconst = { {} }; +static struct platform_profile_handler platform_profile_handler; +static bool platform_profile_support; + +/* + * The profile used before turbo mode. This variable is needed for + * returning from turbo mode when the mode key is in toggle mode. + */ +static int last_non_turbo_profile; + +enum acer_predator_v4_thermal_profile_ec { + ACER_PREDATOR_V4_THERMAL_PROFILE_ECO = 0x04, + ACER_PREDATOR_V4_THERMAL_PROFILE_TURBO = 0x03, + ACER_PREDATOR_V4_THERMAL_PROFILE_PERFORMANCE = 0x02, + ACER_PREDATOR_V4_THERMAL_PROFILE_QUIET = 0x01, + ACER_PREDATOR_V4_THERMAL_PROFILE_BALANCED = 0x00, +}; + +enum acer_predator_v4_thermal_profile_wmi { + ACER_PREDATOR_V4_THERMAL_PROFILE_ECO_WMI = 0x060B, + ACER_PREDATOR_V4_THERMAL_PROFILE_TURBO_WMI = 0x050B, + ACER_PREDATOR_V4_THERMAL_PROFILE_PERFORMANCE_WMI = 0x040B, + ACER_PREDATOR_V4_THERMAL_PROFILE_QUIET_WMI = 0x0B, + ACER_PREDATOR_V4_THERMAL_PROFILE_BALANCED_WMI = 0x010B, +}; + /* Find which quirks are needed for a particular vendor/ model pair */ static void __init find_quirks(void) { @@ -1339,7 +1394,7 @@ WMI_gaming_execute_u64(u32 method_id, u64 in, u64 *out) struct acpi_buffer input = { (acpi_size) sizeof(u64), (void *)(&in) }; struct acpi_buffer result = { ACPI_ALLOCATE_BUFFER, NULL }; union acpi_object *obj; - u32 tmp = 0; + u64 tmp = 0; acpi_status status; status = wmi_evaluate_method(WMID_GUID4, 0, method_id, &input, &result); @@ -1698,6 +1753,199 @@ static int acer_toggle_turbo(void) return turbo_led_state; } +static int +acer_predator_v4_platform_profile_get(struct platform_profile_handler *pprof, + enum platform_profile_option *profile) +{ + u8 tp; + int err; + + err = ec_read(ACER_PREDATOR_V4_THERMAL_PROFILE_EC_OFFSET, &tp); + + if (err < 0) + return err; + + switch (tp) { + case ACER_PREDATOR_V4_THERMAL_PROFILE_TURBO: + *profile = PLATFORM_PROFILE_PERFORMANCE; + break; + case ACER_PREDATOR_V4_THERMAL_PROFILE_PERFORMANCE: + *profile = PLATFORM_PROFILE_BALANCED_PERFORMANCE; + break; + case ACER_PREDATOR_V4_THERMAL_PROFILE_BALANCED: + *profile = PLATFORM_PROFILE_BALANCED; + break; + case ACER_PREDATOR_V4_THERMAL_PROFILE_QUIET: + *profile = PLATFORM_PROFILE_QUIET; + break; + case ACER_PREDATOR_V4_THERMAL_PROFILE_ECO: + *profile = PLATFORM_PROFILE_LOW_POWER; + break; + default: + return -EOPNOTSUPP; + } + + return 0; +} + +static int +acer_predator_v4_platform_profile_set(struct platform_profile_handler *pprof, + enum platform_profile_option profile) +{ + int tp; + acpi_status status; + + switch (profile) { + case PLATFORM_PROFILE_PERFORMANCE: + tp = ACER_PREDATOR_V4_THERMAL_PROFILE_TURBO_WMI; + break; + case PLATFORM_PROFILE_BALANCED_PERFORMANCE: + tp = ACER_PREDATOR_V4_THERMAL_PROFILE_PERFORMANCE_WMI; + break; + case PLATFORM_PROFILE_BALANCED: + tp = ACER_PREDATOR_V4_THERMAL_PROFILE_BALANCED_WMI; + break; + case PLATFORM_PROFILE_QUIET: + tp = ACER_PREDATOR_V4_THERMAL_PROFILE_QUIET_WMI; + break; + case PLATFORM_PROFILE_LOW_POWER: + tp = ACER_PREDATOR_V4_THERMAL_PROFILE_ECO_WMI; + break; + default: + return -EOPNOTSUPP; + } + + status = WMI_gaming_execute_u64( + ACER_WMID_SET_GAMING_MISC_SETTING_METHODID, tp, NULL); + + if (ACPI_FAILURE(status)) + return -EIO; + + if (tp != ACER_PREDATOR_V4_THERMAL_PROFILE_TURBO_WMI) + last_non_turbo_profile = tp; + + return 0; +} + +static int acer_platform_profile_setup(void) +{ + if (quirks->predator_v4) { + int err; + + platform_profile_handler.profile_get = + acer_predator_v4_platform_profile_get; + platform_profile_handler.profile_set = + acer_predator_v4_platform_profile_set; + + set_bit(PLATFORM_PROFILE_PERFORMANCE, + platform_profile_handler.choices); + set_bit(PLATFORM_PROFILE_BALANCED_PERFORMANCE, + platform_profile_handler.choices); + set_bit(PLATFORM_PROFILE_BALANCED, + platform_profile_handler.choices); + set_bit(PLATFORM_PROFILE_QUIET, + platform_profile_handler.choices); + set_bit(PLATFORM_PROFILE_LOW_POWER, + platform_profile_handler.choices); + + err = platform_profile_register(&platform_profile_handler); + if (err) + return err; + + platform_profile_support = true; + + /* Set default non-turbo profile */ + last_non_turbo_profile = + ACER_PREDATOR_V4_THERMAL_PROFILE_BALANCED_WMI; + } + return 0; +} + +static int acer_thermal_profile_change(void) +{ + /* + * This mode key can rotate each mode or toggle turbo mode. + * On battery, only ECO and BALANCED mode are available. + */ + if (quirks->predator_v4) { + u8 current_tp; + int tp, err; + u64 on_AC; + acpi_status status; + + err = ec_read(ACER_PREDATOR_V4_THERMAL_PROFILE_EC_OFFSET, + ¤t_tp); + + if (err < 0) + return err; + + /* Check power source */ + status = WMI_gaming_execute_u64( + ACER_WMID_GET_GAMING_SYS_INFO_METHODID, + ACER_WMID_CMD_GET_PREDATOR_V4_BAT_STATUS, &on_AC); + + if (ACPI_FAILURE(status)) + return -EIO; + + switch (current_tp) { + case ACER_PREDATOR_V4_THERMAL_PROFILE_TURBO: + if (!on_AC) + tp = ACER_PREDATOR_V4_THERMAL_PROFILE_BALANCED_WMI; + else if (cycle_gaming_thermal_profile) + tp = ACER_PREDATOR_V4_THERMAL_PROFILE_ECO_WMI; + else + tp = last_non_turbo_profile; + break; + case ACER_PREDATOR_V4_THERMAL_PROFILE_PERFORMANCE: + if (!on_AC) + tp = ACER_PREDATOR_V4_THERMAL_PROFILE_BALANCED_WMI; + else + tp = ACER_PREDATOR_V4_THERMAL_PROFILE_TURBO_WMI; + break; + case ACER_PREDATOR_V4_THERMAL_PROFILE_BALANCED: + if (!on_AC) + tp = ACER_PREDATOR_V4_THERMAL_PROFILE_ECO_WMI; + else if (cycle_gaming_thermal_profile) + tp = ACER_PREDATOR_V4_THERMAL_PROFILE_PERFORMANCE_WMI; + else + tp = ACER_PREDATOR_V4_THERMAL_PROFILE_TURBO_WMI; + break; + case ACER_PREDATOR_V4_THERMAL_PROFILE_QUIET: + if (!on_AC) + tp = ACER_PREDATOR_V4_THERMAL_PROFILE_BALANCED_WMI; + else if (cycle_gaming_thermal_profile) + tp = ACER_PREDATOR_V4_THERMAL_PROFILE_BALANCED_WMI; + else + tp = ACER_PREDATOR_V4_THERMAL_PROFILE_TURBO_WMI; + break; + case ACER_PREDATOR_V4_THERMAL_PROFILE_ECO: + if (!on_AC) + tp = ACER_PREDATOR_V4_THERMAL_PROFILE_BALANCED_WMI; + else if (cycle_gaming_thermal_profile) + tp = ACER_PREDATOR_V4_THERMAL_PROFILE_QUIET_WMI; + else + tp = ACER_PREDATOR_V4_THERMAL_PROFILE_TURBO_WMI; + break; + default: + return -EOPNOTSUPP; + } + + status = WMI_gaming_execute_u64( + ACER_WMID_SET_GAMING_MISC_SETTING_METHODID, tp, NULL); + + if (ACPI_FAILURE(status)) + return -EIO; + + /* Store non-turbo profile for turbo mode toggle*/ + if (tp != ACER_PREDATOR_V4_THERMAL_PROFILE_TURBO_WMI) + last_non_turbo_profile = tp; + + platform_profile_notify(); + } + + return 0; +} + /* * Switch series keyboard dock status */ @@ -1997,6 +2245,8 @@ static void acer_wmi_notify(u32 value, void *context) case WMID_GAMING_TURBO_KEY_EVENT: if (return_value.key_num == 0x4) acer_toggle_turbo(); + if (return_value.key_num == 0x5 && has_cap(ACER_CAP_PLATFORM_PROFILE)) + acer_thermal_profile_change(); break; default: pr_warn("Unknown function number - %d - %d\n", @@ -2245,8 +2495,16 @@ static int acer_platform_probe(struct platform_device *device) if (err) goto error_rfkill; - return err; + if (has_cap(ACER_CAP_PLATFORM_PROFILE)) { + err = acer_platform_profile_setup(); + if (err) + goto error_platform_profile; + } + + return 0; +error_platform_profile: + acer_rfkill_exit(); error_rfkill: if (has_cap(ACER_CAP_BRIGHTNESS)) acer_backlight_exit(); @@ -2265,6 +2523,9 @@ static void acer_platform_remove(struct platform_device *device) acer_backlight_exit(); acer_rfkill_exit(); + + if (platform_profile_support) + platform_profile_remove(); } #ifdef CONFIG_PM_SLEEP -- cgit From c0ff2c397e84795816816ae8a32fd1104156d0f6 Mon Sep 17 00:00:00 2001 From: SungHwan Jung Date: Fri, 24 Nov 2023 18:41:20 +0900 Subject: platform/x86: acer-wmi: Depend on ACPI_VIDEO instead of selecting it "select ACPI_VIDEO" cause recursive dependency when "depends on HWMON" is added: drivers/hwmon/Kconfig:6:error: recursive dependency detected! drivers/hwmon/Kconfig:6: symbol HWMON is selected by EEEPC_LAPTOP drivers/platform/x86/Kconfig:326: symbol EEEPC_LAPTOP depends on ACPI_VIDEO drivers/acpi/Kconfig:208: symbol ACPI_VIDEO is selected by ACER_WMI drivers/platform/x86/Kconfig:173: symbol ACER_WMI depends on HWMON Replace the select with depends on to avoid this problem when the next patch in this series adds "depends on HWMON". There is a stub defined for the used acpi_video_get_backlight_type() function when ACPI_VIDEO is not set, so use: depends on ACPI_VIDEO || ACPI_VIDEO = n Signed-off-by: SungHwan Jung Link: https://lore.kernel.org/r/20231124094122.100707-4-onenowy@gmail.com Reviewed-by: Hans de Goede Signed-off-by: Hans de Goede --- drivers/platform/x86/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/platform/x86/Kconfig b/drivers/platform/x86/Kconfig index dbde7e018dc1..14059335f19d 100644 --- a/drivers/platform/x86/Kconfig +++ b/drivers/platform/x86/Kconfig @@ -177,7 +177,7 @@ config ACER_WMI depends on INPUT depends on RFKILL || RFKILL = n depends on ACPI_WMI - select ACPI_VIDEO + depends on ACPI_VIDEO || ACPI_VIDEO = n select INPUT_SPARSEKMAP select LEDS_CLASS select NEW_LEDS -- cgit From 446dd8efa94ca80a8b91fbe907364001ed1b3d85 Mon Sep 17 00:00:00 2001 From: SungHwan Jung Date: Fri, 24 Nov 2023 18:41:19 +0900 Subject: platform/x86: acer-wmi: add fan speed monitoring for Predator PHN16-71 Support CPU and GPU fan speed monitoring through WMI for Predator PHN16-71. Signed-off-by: SungHwan Jung Link: https://lore.kernel.org/r/20231124094122.100707-3-onenowy@gmail.com Reviewed-by: Hans de Goede Signed-off-by: Hans de Goede --- drivers/platform/x86/Kconfig | 1 + drivers/platform/x86/acer-wmi.c | 108 +++++++++++++++++++++++++++++++++++++++- 2 files changed, 108 insertions(+), 1 deletion(-) diff --git a/drivers/platform/x86/Kconfig b/drivers/platform/x86/Kconfig index 14059335f19d..7ddb8cc3d56f 100644 --- a/drivers/platform/x86/Kconfig +++ b/drivers/platform/x86/Kconfig @@ -178,6 +178,7 @@ config ACER_WMI depends on RFKILL || RFKILL = n depends on ACPI_WMI depends on ACPI_VIDEO || ACPI_VIDEO = n + depends on HWMON select INPUT_SPARSEKMAP select LEDS_CLASS select NEW_LEDS diff --git a/drivers/platform/x86/acer-wmi.c b/drivers/platform/x86/acer-wmi.c index 55fedc2e656e..88b826e88ebd 100644 --- a/drivers/platform/x86/acer-wmi.c +++ b/drivers/platform/x86/acer-wmi.c @@ -30,6 +30,8 @@ #include #include #include +#include +#include MODULE_AUTHOR("Carlos Corbacho"); MODULE_DESCRIPTION("Acer Laptop WMI Extras Driver"); @@ -69,6 +71,8 @@ MODULE_LICENSE("GPL"); #define ACER_PREDATOR_V4_THERMAL_PROFILE_EC_OFFSET 0x54 +#define ACER_PREDATOR_V4_FAN_SPEED_READ_BIT_MASK GENMASK(20, 8) + /* * Acer ACPI method GUIDs */ @@ -96,6 +100,8 @@ enum acer_wmi_event_ids { enum acer_wmi_predator_v4_sys_info_command { ACER_WMID_CMD_GET_PREDATOR_V4_BAT_STATUS = 0x02, + ACER_WMID_CMD_GET_PREDATOR_V4_CPU_FAN_SPEED = 0x0201, + ACER_WMID_CMD_GET_PREDATOR_V4_GPU_FAN_SPEED = 0x0601, }; static const struct key_entry acer_wmi_keymap[] __initconst = { @@ -241,6 +247,7 @@ struct hotkey_function_type_aa { #define ACER_CAP_TURBO_LED BIT(8) #define ACER_CAP_TURBO_FAN BIT(9) #define ACER_CAP_PLATFORM_PROFILE BIT(10) +#define ACER_CAP_FAN_SPEED_READ BIT(11) /* * Interface type flags @@ -352,7 +359,8 @@ static void __init set_quirks(void) | ACER_CAP_TURBO_FAN; if (quirks->predator_v4) - interface->capability |= ACER_CAP_PLATFORM_PROFILE; + interface->capability |= ACER_CAP_PLATFORM_PROFILE | + ACER_CAP_FAN_SPEED_READ; } static int __init dmi_matched(const struct dmi_system_id *dmi) @@ -1718,6 +1726,26 @@ static int acer_gsensor_event(void) return 0; } +static int acer_get_fan_speed(int fan) +{ + if (quirks->predator_v4) { + acpi_status status; + u64 fanspeed; + + status = WMI_gaming_execute_u64( + ACER_WMID_GET_GAMING_SYS_INFO_METHODID, + fan == 0 ? ACER_WMID_CMD_GET_PREDATOR_V4_CPU_FAN_SPEED : + ACER_WMID_CMD_GET_PREDATOR_V4_GPU_FAN_SPEED, + &fanspeed); + + if (ACPI_FAILURE(status)) + return -EIO; + + return FIELD_GET(ACER_PREDATOR_V4_FAN_SPEED_READ_BIT_MASK, fanspeed); + } + return -EOPNOTSUPP; +} + /* * Predator series turbo button */ @@ -2472,6 +2500,8 @@ static u32 get_wmid_devices(void) return devices; } +static int acer_wmi_hwmon_init(void); + /* * Platform device */ @@ -2501,8 +2531,17 @@ static int acer_platform_probe(struct platform_device *device) goto error_platform_profile; } + if (has_cap(ACER_CAP_FAN_SPEED_READ)) { + err = acer_wmi_hwmon_init(); + if (err) + goto error_hwmon; + } + return 0; +error_hwmon: + if (platform_profile_support) + platform_profile_remove(); error_platform_profile: acer_rfkill_exit(); error_rfkill: @@ -2612,6 +2651,73 @@ static void __init create_debugfs(void) &interface->debug.wmid_devices); } +static umode_t acer_wmi_hwmon_is_visible(const void *data, + enum hwmon_sensor_types type, u32 attr, + int channel) +{ + switch (type) { + case hwmon_fan: + if (acer_get_fan_speed(channel) >= 0) + return 0444; + break; + default: + return 0; + } + + return 0; +} + +static int acer_wmi_hwmon_read(struct device *dev, enum hwmon_sensor_types type, + u32 attr, int channel, long *val) +{ + int ret; + + switch (type) { + case hwmon_fan: + ret = acer_get_fan_speed(channel); + if (ret < 0) + return ret; + *val = ret; + break; + default: + return -EOPNOTSUPP; + } + + return 0; +} + +static const struct hwmon_channel_info *const acer_wmi_hwmon_info[] = { + HWMON_CHANNEL_INFO(fan, HWMON_F_INPUT, HWMON_F_INPUT), NULL +}; + +static const struct hwmon_ops acer_wmi_hwmon_ops = { + .read = acer_wmi_hwmon_read, + .is_visible = acer_wmi_hwmon_is_visible, +}; + +static const struct hwmon_chip_info acer_wmi_hwmon_chip_info = { + .ops = &acer_wmi_hwmon_ops, + .info = acer_wmi_hwmon_info, +}; + +static int acer_wmi_hwmon_init(void) +{ + struct device *dev = &acer_platform_device->dev; + struct device *hwmon; + + hwmon = devm_hwmon_device_register_with_info(dev, "acer", + &acer_platform_driver, + &acer_wmi_hwmon_chip_info, + NULL); + + if (IS_ERR(hwmon)) { + dev_err(dev, "Could not register acer hwmon device\n"); + return PTR_ERR(hwmon); + } + + return 0; +} + static int __init acer_wmi_init(void) { int err; -- cgit From 3799b5d2323dffde5e2ba60b5bfbd8e6d4bea28e Mon Sep 17 00:00:00 2001 From: Edson Juliano Drosdeck Date: Mon, 16 Oct 2023 16:13:49 -0300 Subject: platform/x86: asus-laptop: remove redundant braces in if statements Adhere to Linux kernel coding style. Reported by checkpatch: WARNING: braces {} are not necessary for single statement blocks Signed-off-by: Edson Juliano Drosdeck Link: https://lore.kernel.org/r/20231016191349.3856-1-edson.drosdeck@gmail.com Signed-off-by: Hans de Goede --- drivers/platform/x86/asus-laptop.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/platform/x86/asus-laptop.c b/drivers/platform/x86/asus-laptop.c index 761029f39314..bf03ea1b1274 100644 --- a/drivers/platform/x86/asus-laptop.c +++ b/drivers/platform/x86/asus-laptop.c @@ -1816,9 +1816,8 @@ static void asus_dmi_check(void) return; /* On L1400B WLED control the sound card, don't mess with it ... */ - if (strncmp(model, "L1400B", 6) == 0) { + if (strncmp(model, "L1400B", 6) == 0) wlan_status = -1; - } } static bool asus_device_present; -- cgit From d9cd21d441c8c7c22ef448d23f1d6f5fa698b7f0 Mon Sep 17 00:00:00 2001 From: Henry Shi Date: Fri, 24 Nov 2023 15:03:34 -0500 Subject: platform/x86: Add Silicom Platform Driver MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add Silicom platform (silicom-platform) Linux driver for Swisscom Business Box (Swisscom BB) as well as Cordoba family products. This platform driver provides support for various functions via the Linux LED framework, GPIO framework, Hardware Monitoring (HWMON) and device attributes. Signed-off-by: Henry Shi Reviewed-by: Ilpo Järvinen Link: https://lore.kernel.org/r/20231124200334.5318-1-henryshi2018@gmail.com Signed-off-by: Hans de Goede --- Documentation/ABI/testing/sysfs-platform-silicom | 29 + drivers/platform/x86/Kconfig | 15 + drivers/platform/x86/Makefile | 3 + drivers/platform/x86/silicom-platform.c | 1004 ++++++++++++++++++++++ 4 files changed, 1051 insertions(+) create mode 100644 Documentation/ABI/testing/sysfs-platform-silicom create mode 100644 drivers/platform/x86/silicom-platform.c diff --git a/Documentation/ABI/testing/sysfs-platform-silicom b/Documentation/ABI/testing/sysfs-platform-silicom new file mode 100644 index 000000000000..2288b3665d16 --- /dev/null +++ b/Documentation/ABI/testing/sysfs-platform-silicom @@ -0,0 +1,29 @@ +What: /sys/devices/platform/silicom-platform/uc_version +Date: November 2023 +KernelVersion: 6.7 +Contact: Henry Shi +Description: + This file allows to read microcontroller firmware + version of current platform. + +What: /sys/devices/platform/silicom-platform/power_cycle +Date: November 2023 +KernelVersion: 6.7 +Contact: Henry Shi + This file allow user to power cycle the platform. + Default value is 0; when set to 1, it powers down + the platform, waits 5 seconds, then powers on the + device. It returns to default value after power cycle. + + 0 - default value. + +What: /sys/devices/platform/silicom-platform/efuse_status +Date: November 2023 +KernelVersion: 6.7 +Contact: Henry Shi +Description: + This file is read only. It returns the current + OTP status: + + 0 - not programmed. + 1 - programmed. diff --git a/drivers/platform/x86/Kconfig b/drivers/platform/x86/Kconfig index 7ddb8cc3d56f..5c6dbbf0235c 100644 --- a/drivers/platform/x86/Kconfig +++ b/drivers/platform/x86/Kconfig @@ -1089,6 +1089,21 @@ config INTEL_SCU_IPC_UTIL source "drivers/platform/x86/siemens/Kconfig" +config SILICOM_PLATFORM + tristate "Silicom Edge Networking device support" + depends on HWMON + depends on GPIOLIB + depends on LEDS_CLASS_MULTICOLOR + help + This option enables support for the LEDs/GPIO/etc downstream of the + embedded controller on Silicom "Cordoba" hardware and derivatives. + + This platform driver provides support for various functions via + the Linux LED framework, GPIO framework, Hardware Monitoring (HWMON) + and device attributes. + + If you have a Silicom network appliance, say Y or M here. + config WINMATE_FM07_KEYS tristate "Winmate FM07/FM07P front-panel keys driver" depends on INPUT diff --git a/drivers/platform/x86/Makefile b/drivers/platform/x86/Makefile index c7a18e95ad8c..1de432e8861e 100644 --- a/drivers/platform/x86/Makefile +++ b/drivers/platform/x86/Makefile @@ -136,6 +136,9 @@ obj-$(CONFIG_X86_INTEL_LPSS) += pmc_atom.o # Siemens Simatic Industrial PCs obj-$(CONFIG_SIEMENS_SIMATIC_IPC) += siemens/ +# Silicom +obj-$(CONFIG_SILICOM_PLATFORM) += silicom-platform.o + # Winmate obj-$(CONFIG_WINMATE_FM07_KEYS) += winmate-fm07-keys.o diff --git a/drivers/platform/x86/silicom-platform.c b/drivers/platform/x86/silicom-platform.c new file mode 100644 index 000000000000..84b92b3f9f4b --- /dev/null +++ b/drivers/platform/x86/silicom-platform.c @@ -0,0 +1,1004 @@ +// SPDX-License-Identifier: GPL-2.0+ +// +// silicom-platform.c - Silicom MEC170x platform driver +// +// Copyright (C) 2023 Henry Shi +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#define MEC_POWER_CYCLE_ADDR 0x24 +#define MEC_EFUSE_LSB_ADDR 0x28 +#define MEC_GPIO_IN_POS 0x08 +#define MEC_IO_BASE 0x0800 +#define MEC_IO_LEN 0x8 +#define IO_REG_BANK 0x0 +#define DEFAULT_CHAN_LO 0 +#define DEFAULT_CHAN_HI 0 +#define DEFAULT_CHAN_LO_T 0xc +#define MEC_ADDR (MEC_IO_BASE + 0x02) +#define EC_ADDR_LSB MEC_ADDR +#define SILICOM_MEC_MAGIC 0x5a + +#define MEC_PORT_CHANNEL_MASK GENMASK(2, 0) +#define MEC_PORT_DWORD_OFFSET GENMASK(31, 3) +#define MEC_DATA_OFFSET_MASK GENMASK(1, 0) +#define MEC_PORT_OFFSET_MASK GENMASK(7, 2) + +#define MEC_TEMP_LOC GENMASK(31, 16) +#define MEC_VERSION_LOC GENMASK(15, 8) +#define MEC_VERSION_MAJOR GENMASK(15, 14) +#define MEC_VERSION_MINOR GENMASK(13, 8) + +#define EC_ADDR_MSB (MEC_IO_BASE + 0x3) +#define MEC_DATA_OFFSET(offset) (MEC_IO_BASE + 0x04 + (offset)) + +#define OFFSET_BIT_TO_CHANNEL(off, bit) ((((off) + 0x014) << 3) | (bit)) +#define CHANNEL_TO_OFFSET(chan) (((chan) >> 3) - 0x14) + +static DEFINE_MUTEX(mec_io_mutex); +static unsigned int efuse_status; +static unsigned int mec_uc_version; +static unsigned int power_cycle; + +static const struct hwmon_channel_info *silicom_fan_control_info[] = { + HWMON_CHANNEL_INFO(fan, HWMON_F_INPUT | HWMON_F_LABEL), + HWMON_CHANNEL_INFO(temp, HWMON_T_INPUT | HWMON_T_LABEL), + NULL +}; + +struct silicom_platform_info { + int io_base; + int io_len; + struct led_classdev_mc *led_info; + struct gpio_chip *gpiochip; + u8 *gpio_channels; + u16 ngpio; +}; + +static const char * const plat_0222_gpio_names[] = { + "AUTOM0_SFP_TX_FAULT", + "SLOT2_LED_OUT", + "SIM_M2_SLOT2_B_DET", + "SIM_M2_SLOT2_A_DET", + "SLOT1_LED_OUT", + "SIM_M2_SLOT1_B_DET", + "SIM_M2_SLOT1_A_DET", + "SLOT0_LED_OUT", + "WAN_SFP0_RX_LOS", + "WAN_SFP0_PRSNT_N", + "WAN_SFP0_TX_FAULT", + "AUTOM1_SFP_RX_LOS", + "AUTOM1_SFP_PRSNT_N", + "AUTOM1_SFP_TX_FAULT", + "AUTOM0_SFP_RX_LOS", + "AUTOM0_SFP_PRSNT_N", + "WAN_SFP1_RX_LOS", + "WAN_SFP1_PRSNT_N", + "WAN_SFP1_TX_FAULT", + "SIM_M2_SLOT1_MUX_SEL", + "W_DISABLE_M2_SLOT1_N", + "W_DISABLE_MPCIE_SLOT0_N", + "W_DISABLE_M2_SLOT0_N", + "BT_COMMAND_MODE", + "WAN_SFP1_TX_DISABLE", + "WAN_SFP0_TX_DISABLE", + "AUTOM1_SFP_TX_DISABLE", + "AUTOM0_SFP_TX_DISABLE", + "SIM_M2_SLOT2_MUX_SEL", + "W_DISABLE_M2_SLOT2_N", + "RST_CTL_M2_SLOT_1_N", + "RST_CTL_M2_SLOT_2_N", + "PM_USB_PWR_EN_BOT", + "PM_USB_PWR_EN_TOP", +}; + +static u8 plat_0222_gpio_channels[] = { + OFFSET_BIT_TO_CHANNEL(0x00, 0), + OFFSET_BIT_TO_CHANNEL(0x00, 1), + OFFSET_BIT_TO_CHANNEL(0x00, 2), + OFFSET_BIT_TO_CHANNEL(0x00, 3), + OFFSET_BIT_TO_CHANNEL(0x00, 4), + OFFSET_BIT_TO_CHANNEL(0x00, 5), + OFFSET_BIT_TO_CHANNEL(0x00, 6), + OFFSET_BIT_TO_CHANNEL(0x00, 7), + OFFSET_BIT_TO_CHANNEL(0x01, 0), + OFFSET_BIT_TO_CHANNEL(0x01, 1), + OFFSET_BIT_TO_CHANNEL(0x01, 2), + OFFSET_BIT_TO_CHANNEL(0x01, 3), + OFFSET_BIT_TO_CHANNEL(0x01, 4), + OFFSET_BIT_TO_CHANNEL(0x01, 5), + OFFSET_BIT_TO_CHANNEL(0x01, 6), + OFFSET_BIT_TO_CHANNEL(0x01, 7), + OFFSET_BIT_TO_CHANNEL(0x02, 0), + OFFSET_BIT_TO_CHANNEL(0x02, 1), + OFFSET_BIT_TO_CHANNEL(0x02, 2), + OFFSET_BIT_TO_CHANNEL(0x09, 0), + OFFSET_BIT_TO_CHANNEL(0x09, 1), + OFFSET_BIT_TO_CHANNEL(0x09, 2), + OFFSET_BIT_TO_CHANNEL(0x09, 3), + OFFSET_BIT_TO_CHANNEL(0x0a, 0), + OFFSET_BIT_TO_CHANNEL(0x0a, 1), + OFFSET_BIT_TO_CHANNEL(0x0a, 2), + OFFSET_BIT_TO_CHANNEL(0x0a, 3), + OFFSET_BIT_TO_CHANNEL(0x0a, 4), + OFFSET_BIT_TO_CHANNEL(0x0a, 5), + OFFSET_BIT_TO_CHANNEL(0x0a, 6), + OFFSET_BIT_TO_CHANNEL(0x0b, 0), + OFFSET_BIT_TO_CHANNEL(0x0b, 1), + OFFSET_BIT_TO_CHANNEL(0x0b, 2), + OFFSET_BIT_TO_CHANNEL(0x0b, 3), +}; + +static struct platform_device *silicom_platform_dev; +static struct led_classdev_mc *silicom_led_info __initdata; +static struct gpio_chip *silicom_gpiochip __initdata; +static u8 *silicom_gpio_channels __initdata; + +static int silicom_mec_port_get(unsigned int offset) +{ + unsigned short mec_data_addr; + unsigned short mec_port_addr; + u8 reg; + + mec_data_addr = FIELD_GET(MEC_PORT_DWORD_OFFSET, offset) & MEC_DATA_OFFSET_MASK; + mec_port_addr = FIELD_GET(MEC_PORT_DWORD_OFFSET, offset) & MEC_PORT_OFFSET_MASK; + + mutex_lock(&mec_io_mutex); + outb(mec_port_addr, MEC_ADDR); + reg = inb(MEC_DATA_OFFSET(mec_data_addr)); + mutex_unlock(&mec_io_mutex); + + return (reg >> (offset & MEC_PORT_CHANNEL_MASK)) & 0x01; +} + +static enum led_brightness silicom_mec_led_get(int channel) +{ + /* Outputs are active low */ + return silicom_mec_port_get(channel) ? LED_OFF : LED_ON; +} + +static void silicom_mec_port_set(int channel, int on) +{ + + unsigned short mec_data_addr; + unsigned short mec_port_addr; + u8 reg; + + mec_data_addr = FIELD_GET(MEC_PORT_DWORD_OFFSET, channel) & MEC_DATA_OFFSET_MASK; + mec_port_addr = FIELD_GET(MEC_PORT_DWORD_OFFSET, channel) & MEC_PORT_OFFSET_MASK; + + mutex_lock(&mec_io_mutex); + outb(mec_port_addr, MEC_ADDR); + reg = inb(MEC_DATA_OFFSET(mec_data_addr)); + /* Outputs are active low, so clear the bit for on, or set it for off */ + if (on) + reg &= ~(1 << (channel & MEC_PORT_CHANNEL_MASK)); + else + reg |= 1 << (channel & MEC_PORT_CHANNEL_MASK); + outb(reg, MEC_DATA_OFFSET(mec_data_addr)); + mutex_unlock(&mec_io_mutex); +} + +static enum led_brightness silicom_mec_led_mc_brightness_get(struct led_classdev *led_cdev) +{ + struct led_classdev_mc *mc_cdev = lcdev_to_mccdev(led_cdev); + enum led_brightness brightness = LED_OFF; + int i; + + for (i = 0; i < mc_cdev->num_colors; i++) { + mc_cdev->subled_info[i].brightness = + silicom_mec_led_get(mc_cdev->subled_info[i].channel); + /* Mark the overall brightness as LED_ON if any of the subleds are on */ + if (mc_cdev->subled_info[i].brightness != LED_OFF) + brightness = LED_ON; + } + + return brightness; +} + +static void silicom_mec_led_mc_brightness_set(struct led_classdev *led_cdev, + enum led_brightness brightness) +{ + struct led_classdev_mc *mc_cdev = lcdev_to_mccdev(led_cdev); + int i; + + led_mc_calc_color_components(mc_cdev, brightness); + for (i = 0; i < mc_cdev->num_colors; i++) { + silicom_mec_port_set(mc_cdev->subled_info[i].channel, + mc_cdev->subled_info[i].brightness); + } +} + +static int silicom_gpio_get_direction(struct gpio_chip *gc, + unsigned int offset) +{ + u8 *channels = gpiochip_get_data(gc); + + /* Input registers have offsets between [0x00, 0x07] */ + if (CHANNEL_TO_OFFSET(channels[offset]) < MEC_GPIO_IN_POS) + return GPIO_LINE_DIRECTION_IN; + + return GPIO_LINE_DIRECTION_OUT; +} + +static int silicom_gpio_direction_input(struct gpio_chip *gc, + unsigned int offset) +{ + int direction = silicom_gpio_get_direction(gc, offset); + + return direction == GPIO_LINE_DIRECTION_IN ? 0 : -EINVAL; +} + +static void silicom_gpio_set(struct gpio_chip *gc, + unsigned int offset, + int value) +{ + int direction = silicom_gpio_get_direction(gc, offset); + u8 *channels = gpiochip_get_data(gc); + int channel = channels[offset]; + + if (direction == GPIO_LINE_DIRECTION_IN) + return; + + if (value) + silicom_mec_port_set(channel, 0); + else if (value == 0) + silicom_mec_port_set(channel, 1); + else + pr_err("Wrong argument value: %d\n", value); +} + +static int silicom_gpio_direction_output(struct gpio_chip *gc, + unsigned int offset, + int value) +{ + int direction = silicom_gpio_get_direction(gc, offset); + + if (direction == GPIO_LINE_DIRECTION_IN) + return -EINVAL; + + silicom_gpio_set(gc, offset, value); + + return 0; +} + +static int silicom_gpio_get(struct gpio_chip *gc, unsigned int offset) +{ + u8 *channels = gpiochip_get_data(gc); + int channel = channels[offset]; + + return silicom_mec_port_get(channel); +} + +static struct mc_subled plat_0222_wan_mc_subled_info[] __initdata = { + { + .color_index = LED_COLOR_ID_WHITE, + .brightness = 1, + .intensity = 0, + .channel = OFFSET_BIT_TO_CHANNEL(0x0c, 7), + }, + { + .color_index = LED_COLOR_ID_YELLOW, + .brightness = 1, + .intensity = 0, + .channel = OFFSET_BIT_TO_CHANNEL(0x0c, 6), + }, + { + .color_index = LED_COLOR_ID_RED, + .brightness = 1, + .intensity = 0, + .channel = OFFSET_BIT_TO_CHANNEL(0x0c, 5), + }, +}; + +static struct mc_subled plat_0222_sys_mc_subled_info[] __initdata = { + { + .color_index = LED_COLOR_ID_WHITE, + .brightness = 1, + .intensity = 0, + .channel = OFFSET_BIT_TO_CHANNEL(0x0c, 4), + }, + { + .color_index = LED_COLOR_ID_AMBER, + .brightness = 1, + .intensity = 0, + .channel = OFFSET_BIT_TO_CHANNEL(0x0c, 3), + }, + { + .color_index = LED_COLOR_ID_RED, + .brightness = 1, + .intensity = 0, + .channel = OFFSET_BIT_TO_CHANNEL(0x0c, 2), + }, +}; + +static struct mc_subled plat_0222_stat1_mc_subled_info[] __initdata = { + { + .color_index = LED_COLOR_ID_RED, + .brightness = 1, + .intensity = 0, + .channel = OFFSET_BIT_TO_CHANNEL(0x0c, 1), + }, + { + .color_index = LED_COLOR_ID_GREEN, + .brightness = 1, + .intensity = 0, + .channel = OFFSET_BIT_TO_CHANNEL(0x0c, 0), + }, + { + .color_index = LED_COLOR_ID_BLUE, + .brightness = 1, + .intensity = 0, + .channel = OFFSET_BIT_TO_CHANNEL(0x0d, 7), + }, + { + .color_index = LED_COLOR_ID_YELLOW, + .brightness = 1, + .intensity = 0, + .channel = OFFSET_BIT_TO_CHANNEL(0x0d, 6), + }, +}; + +static struct mc_subled plat_0222_stat2_mc_subled_info[] __initdata = { + { + .color_index = LED_COLOR_ID_RED, + .brightness = 1, + .intensity = 0, + .channel = OFFSET_BIT_TO_CHANNEL(0x0d, 5), + }, + { + .color_index = LED_COLOR_ID_GREEN, + .brightness = 1, + .intensity = 0, + .channel = OFFSET_BIT_TO_CHANNEL(0x0d, 4), + }, + { + .color_index = LED_COLOR_ID_BLUE, + .brightness = 1, + .intensity = 0, + .channel = OFFSET_BIT_TO_CHANNEL(0x0d, 3), + }, + { + .color_index = LED_COLOR_ID_YELLOW, + .brightness = 1, + .intensity = 0, + .channel = OFFSET_BIT_TO_CHANNEL(0x0d, 2), + }, +}; + +static struct mc_subled plat_0222_stat3_mc_subled_info[] __initdata = { + { + .color_index = LED_COLOR_ID_RED, + .brightness = 1, + .intensity = 0, + .channel = OFFSET_BIT_TO_CHANNEL(0x0d, 1), + }, + { + .color_index = LED_COLOR_ID_GREEN, + .brightness = 1, + .intensity = 0, + .channel = OFFSET_BIT_TO_CHANNEL(0x0d, 0), + }, + { + .color_index = LED_COLOR_ID_BLUE, + .brightness = 1, + .intensity = 0, + .channel = OFFSET_BIT_TO_CHANNEL(0x0e, 1), + }, + { + .color_index = LED_COLOR_ID_YELLOW, + .brightness = 1, + .intensity = 0, + .channel = OFFSET_BIT_TO_CHANNEL(0x0e, 0), + }, +}; + +static struct led_classdev_mc plat_0222_mc_led_info[] __initdata = { + { + .led_cdev = { + .name = "platled::wan", + .brightness = 0, + .max_brightness = 1, + .brightness_set = silicom_mec_led_mc_brightness_set, + .brightness_get = silicom_mec_led_mc_brightness_get, + }, + .num_colors = ARRAY_SIZE(plat_0222_wan_mc_subled_info), + .subled_info = plat_0222_wan_mc_subled_info, + }, + { + .led_cdev = { + .name = "platled::sys", + .brightness = 0, + .max_brightness = 1, + .brightness_set = silicom_mec_led_mc_brightness_set, + .brightness_get = silicom_mec_led_mc_brightness_get, + }, + .num_colors = ARRAY_SIZE(plat_0222_sys_mc_subled_info), + .subled_info = plat_0222_sys_mc_subled_info, + }, + { + .led_cdev = { + .name = "platled::stat1", + .brightness = 0, + .max_brightness = 1, + .brightness_set = silicom_mec_led_mc_brightness_set, + .brightness_get = silicom_mec_led_mc_brightness_get, + }, + .num_colors = ARRAY_SIZE(plat_0222_stat1_mc_subled_info), + .subled_info = plat_0222_stat1_mc_subled_info, + }, + { + .led_cdev = { + .name = "platled::stat2", + .brightness = 0, + .max_brightness = 1, + .brightness_set = silicom_mec_led_mc_brightness_set, + .brightness_get = silicom_mec_led_mc_brightness_get, + }, + .num_colors = ARRAY_SIZE(plat_0222_stat2_mc_subled_info), + .subled_info = plat_0222_stat2_mc_subled_info, + }, + { + .led_cdev = { + .name = "platled::stat3", + .brightness = 0, + .max_brightness = 1, + .brightness_set = silicom_mec_led_mc_brightness_set, + .brightness_get = silicom_mec_led_mc_brightness_get, + }, + .num_colors = ARRAY_SIZE(plat_0222_stat3_mc_subled_info), + .subled_info = plat_0222_stat3_mc_subled_info, + }, + { }, +}; + +static struct gpio_chip silicom_gpio_chip = { + .label = "silicom-gpio", + .get_direction = silicom_gpio_get_direction, + .direction_input = silicom_gpio_direction_input, + .direction_output = silicom_gpio_direction_output, + .get = silicom_gpio_get, + .set = silicom_gpio_set, + .base = -1, + .ngpio = ARRAY_SIZE(plat_0222_gpio_channels), + .names = plat_0222_gpio_names, + /* + * We're using a mutex to protect the indirect access, so we can sleep + * if the lock blocks + */ + .can_sleep = true, +}; + +static struct silicom_platform_info silicom_plat_0222_cordoba_info __initdata = { + .io_base = MEC_IO_BASE, + .io_len = MEC_IO_LEN, + .led_info = plat_0222_mc_led_info, + .gpiochip = &silicom_gpio_chip, + .gpio_channels = plat_0222_gpio_channels, + /* + * The original generic cordoba does not have the last 4 outputs of the + * plat_0222 variant, the rest are the same, so use the same longer list, + * but ignore the last entries here + */ + .ngpio = ARRAY_SIZE(plat_0222_gpio_channels), + +}; + +static struct mc_subled cordoba_fp_left_mc_subled_info[] __initdata = { + { + .color_index = LED_COLOR_ID_RED, + .brightness = 1, + .intensity = 0, + .channel = OFFSET_BIT_TO_CHANNEL(0x08, 6), + }, + { + .color_index = LED_COLOR_ID_GREEN, + .brightness = 1, + .intensity = 0, + .channel = OFFSET_BIT_TO_CHANNEL(0x08, 5), + }, + { + .color_index = LED_COLOR_ID_BLUE, + .brightness = 1, + .intensity = 0, + .channel = OFFSET_BIT_TO_CHANNEL(0x09, 7), + }, + { + .color_index = LED_COLOR_ID_AMBER, + .brightness = 1, + .intensity = 0, + .channel = OFFSET_BIT_TO_CHANNEL(0x09, 4), + }, +}; + +static struct mc_subled cordoba_fp_center_mc_subled_info[] __initdata = { + { + .color_index = LED_COLOR_ID_RED, + .brightness = 1, + .intensity = 0, + .channel = OFFSET_BIT_TO_CHANNEL(0x08, 7), + }, + { + .color_index = LED_COLOR_ID_GREEN, + .brightness = 1, + .intensity = 0, + .channel = OFFSET_BIT_TO_CHANNEL(0x08, 4), + }, + { + .color_index = LED_COLOR_ID_BLUE, + .brightness = 1, + .intensity = 0, + .channel = OFFSET_BIT_TO_CHANNEL(0x08, 3), + }, + { + .color_index = LED_COLOR_ID_AMBER, + .brightness = 1, + .intensity = 0, + .channel = OFFSET_BIT_TO_CHANNEL(0x09, 6), + }, +}; + +static struct mc_subled cordoba_fp_right_mc_subled_info[] __initdata = { + { + .color_index = LED_COLOR_ID_RED, + .brightness = 1, + .intensity = 0, + .channel = OFFSET_BIT_TO_CHANNEL(0x08, 2), + }, + { + .color_index = LED_COLOR_ID_GREEN, + .brightness = 1, + .intensity = 0, + .channel = OFFSET_BIT_TO_CHANNEL(0x08, 1), + }, + { + .color_index = LED_COLOR_ID_BLUE, + .brightness = 1, + .intensity = 0, + .channel = OFFSET_BIT_TO_CHANNEL(0x08, 0), + }, + { + .color_index = LED_COLOR_ID_AMBER, + .brightness = 1, + .intensity = 0, + .channel = OFFSET_BIT_TO_CHANNEL(0x09, 5), + }, +}; + +static struct led_classdev_mc cordoba_mc_led_info[] __initdata = { + { + .led_cdev = { + .name = "platled::fp_left", + .brightness = 0, + .max_brightness = 1, + .brightness_set = silicom_mec_led_mc_brightness_set, + .brightness_get = silicom_mec_led_mc_brightness_get, + }, + .num_colors = ARRAY_SIZE(cordoba_fp_left_mc_subled_info), + .subled_info = cordoba_fp_left_mc_subled_info, + }, + { + .led_cdev = { + .name = "platled::fp_center", + .brightness = 0, + .max_brightness = 1, + .brightness_set = silicom_mec_led_mc_brightness_set, + .brightness_get = silicom_mec_led_mc_brightness_get, + }, + .num_colors = ARRAY_SIZE(cordoba_fp_center_mc_subled_info), + .subled_info = cordoba_fp_center_mc_subled_info, + }, + { + .led_cdev = { + .name = "platled::fp_right", + .brightness = 0, + .max_brightness = 1, + .brightness_set = silicom_mec_led_mc_brightness_set, + .brightness_get = silicom_mec_led_mc_brightness_get, + }, + .num_colors = ARRAY_SIZE(cordoba_fp_right_mc_subled_info), + .subled_info = cordoba_fp_right_mc_subled_info, + }, + { }, +}; + +static struct silicom_platform_info silicom_generic_cordoba_info __initdata = { + .io_base = MEC_IO_BASE, + .io_len = MEC_IO_LEN, + .led_info = cordoba_mc_led_info, + .gpiochip = &silicom_gpio_chip, + .gpio_channels = plat_0222_gpio_channels, + .ngpio = ARRAY_SIZE(plat_0222_gpio_channels), +}; + +/* + * sysfs interface + */ +static ssize_t efuse_status_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + u32 reg; + + mutex_lock(&mec_io_mutex); + /* Select memory region */ + outb(IO_REG_BANK, EC_ADDR_MSB); + outb(MEC_EFUSE_LSB_ADDR, EC_ADDR_LSB); + + /* Get current data from the address */ + reg = inl(MEC_DATA_OFFSET(DEFAULT_CHAN_LO)); + mutex_unlock(&mec_io_mutex); + + efuse_status = reg & 0x1; + + return sysfs_emit(buf, "%u\n", efuse_status); +} +static DEVICE_ATTR_RO(efuse_status); + +static ssize_t uc_version_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + int uc_version; + u32 reg; + + mutex_lock(&mec_io_mutex); + outb(IO_REG_BANK, EC_ADDR_MSB); + outb(DEFAULT_CHAN_LO, EC_ADDR_LSB); + + reg = inl(MEC_DATA_OFFSET(DEFAULT_CHAN_LO)); + mutex_unlock(&mec_io_mutex); + uc_version = FIELD_GET(MEC_VERSION_LOC, reg); + if (uc_version >= 192) + return -EINVAL; + + uc_version = FIELD_GET(MEC_VERSION_MAJOR, reg) * 100 + + FIELD_GET(MEC_VERSION_MINOR, reg); + + mec_uc_version = uc_version; + + return sysfs_emit(buf, "%u\n", mec_uc_version); +} +static DEVICE_ATTR_RO(uc_version); + +static ssize_t power_cycle_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + return sysfs_emit(buf, "%u\n", power_cycle); +} + +static void powercycle_uc(void) +{ + /* Select memory region */ + outb(IO_REG_BANK, EC_ADDR_MSB); + outb(MEC_POWER_CYCLE_ADDR, EC_ADDR_LSB); + + /* Set to 1 for current data from the address */ + outb(1, MEC_DATA_OFFSET(DEFAULT_CHAN_LO)); +} + +static ssize_t power_cycle_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + int rc; + unsigned int power_cycle_cmd; + + rc = kstrtou32(buf, 0, &power_cycle_cmd); + if (rc) + return -EINVAL; + + if (power_cycle_cmd > 0) { + mutex_lock(&mec_io_mutex); + power_cycle = power_cycle_cmd; + powercycle_uc(); + mutex_unlock(&mec_io_mutex); + } + + return count; +} +static DEVICE_ATTR_RW(power_cycle); + +static struct attribute *silicom_attrs[] = { + &dev_attr_efuse_status.attr, + &dev_attr_uc_version.attr, + &dev_attr_power_cycle.attr, + NULL, +}; +ATTRIBUTE_GROUPS(silicom); + +static struct platform_driver silicom_platform_driver = { + .driver = { + .name = "silicom-platform", + .dev_groups = silicom_groups, + }, +}; + +static int __init silicom_mc_leds_register(struct device *dev, + const struct led_classdev_mc *mc_leds) +{ + int size = sizeof(struct mc_subled); + struct led_classdev_mc *led; + int i, err; + + for (i = 0; mc_leds[i].led_cdev.name; i++) { + + led = devm_kzalloc(dev, sizeof(*led), GFP_KERNEL); + if (!led) + return -ENOMEM; + memcpy(led, &mc_leds[i], sizeof(*led)); + + led->subled_info = devm_kzalloc(dev, led->num_colors * size, GFP_KERNEL); + if (!led->subled_info) + return -ENOMEM; + memcpy(led->subled_info, mc_leds[i].subled_info, led->num_colors * size); + + err = devm_led_classdev_multicolor_register(dev, led); + if (err) + return err; + } + + return 0; +} + +static u32 rpm_get(void) +{ + u32 reg; + + mutex_lock(&mec_io_mutex); + /* Select memory region */ + outb(IO_REG_BANK, EC_ADDR_MSB); + outb(DEFAULT_CHAN_LO_T, EC_ADDR_LSB); + reg = inw(MEC_DATA_OFFSET(DEFAULT_CHAN_LO)); + mutex_unlock(&mec_io_mutex); + + return reg; +} + +static u32 temp_get(void) +{ + u32 reg; + + mutex_lock(&mec_io_mutex); + /* Select memory region */ + outb(IO_REG_BANK, EC_ADDR_MSB); + outb(DEFAULT_CHAN_LO_T, EC_ADDR_LSB); + reg = inl(MEC_DATA_OFFSET(DEFAULT_CHAN_LO)); + mutex_unlock(&mec_io_mutex); + + return FIELD_GET(MEC_TEMP_LOC, reg) * 100; +} + +static umode_t silicom_fan_control_fan_is_visible(const u32 attr) +{ + switch (attr) { + case hwmon_fan_input: + case hwmon_fan_label: + return 0444; + default: + return 0; + } +} + +static umode_t silicom_fan_control_temp_is_visible(const u32 attr) +{ + switch (attr) { + case hwmon_temp_input: + case hwmon_temp_label: + return 0444; + default: + return 0; + } +} + +static int silicom_fan_control_read_fan(struct device *dev, u32 attr, long *val) +{ + switch (attr) { + case hwmon_fan_input: + *val = rpm_get(); + return 0; + default: + return -EOPNOTSUPP; + } +} + +static int silicom_fan_control_read_temp(struct device *dev, u32 attr, long *val) +{ + switch (attr) { + case hwmon_temp_input: + *val = temp_get(); + return 0; + default: + return -EOPNOTSUPP; + } +} + +static umode_t silicom_fan_control_is_visible(const void *data, + enum hwmon_sensor_types type, + u32 attr, int channel) +{ + switch (type) { + case hwmon_fan: + return silicom_fan_control_fan_is_visible(attr); + case hwmon_temp: + return silicom_fan_control_temp_is_visible(attr); + default: + return 0; + } +} + +static int silicom_fan_control_read(struct device *dev, + enum hwmon_sensor_types type, + u32 attr, int channel, + long *val) +{ + switch (type) { + case hwmon_fan: + return silicom_fan_control_read_fan(dev, attr, val); + case hwmon_temp: + return silicom_fan_control_read_temp(dev, attr, val); + default: + return -EOPNOTSUPP; + } +} + +static int silicom_fan_control_read_labels(struct device *dev, + enum hwmon_sensor_types type, + u32 attr, int channel, + const char **str) +{ + switch (type) { + case hwmon_fan: + *str = "Silicom_platfomr: Fan Speed"; + return 0; + case hwmon_temp: + *str = "Silicom_platform: Thermostat Sensor"; + return 0; + default: + return -EOPNOTSUPP; + } +} + +static const struct hwmon_ops silicom_fan_control_hwmon_ops = { + .is_visible = silicom_fan_control_is_visible, + .read = silicom_fan_control_read, + .read_string = silicom_fan_control_read_labels, +}; + +static const struct hwmon_chip_info silicom_chip_info = { + .ops = &silicom_fan_control_hwmon_ops, + .info = silicom_fan_control_info, +}; + +static int __init silicom_platform_probe(struct platform_device *device) +{ + struct device *hwmon_dev; + u8 magic, ver; + int err; + + if (!devm_request_region(&device->dev, MEC_IO_BASE, MEC_IO_LEN, "mec")) { + dev_err(&device->dev, "couldn't reserve MEC io ports\n"); + return -EBUSY; + } + + /* Sanity check magic number read for EC */ + outb(IO_REG_BANK, MEC_ADDR); + magic = inb(MEC_DATA_OFFSET(DEFAULT_CHAN_LO)); + ver = inb(MEC_DATA_OFFSET(DEFAULT_CHAN_HI)); + dev_dbg(&device->dev, "EC magic 0x%02x, version 0x%02x\n", magic, ver); + + if (magic != SILICOM_MEC_MAGIC) { + dev_err(&device->dev, "Bad EC magic 0x%02x!\n", magic); + return -ENODEV; + } + + err = silicom_mc_leds_register(&device->dev, silicom_led_info); + if (err) { + dev_err(&device->dev, "Failed to register LEDs\n"); + return err; + } + + err = devm_gpiochip_add_data(&device->dev, silicom_gpiochip, + silicom_gpio_channels); + if (err) { + dev_err(&device->dev, "Failed to register gpiochip: %d\n", err); + return err; + } + + hwmon_dev = devm_hwmon_device_register_with_info(&device->dev, "silicom_fan", NULL, + &silicom_chip_info, NULL); + err = PTR_ERR_OR_ZERO(hwmon_dev); + if (err) { + dev_err(&device->dev, "Failed to register hwmon_dev: %d\n", err); + return err; + } + + return err; +} + +static int __init silicom_platform_info_init(const struct dmi_system_id *id) +{ + struct silicom_platform_info *info = id->driver_data; + + silicom_led_info = info->led_info; + silicom_gpio_channels = info->gpio_channels; + silicom_gpiochip = info->gpiochip; + silicom_gpiochip->ngpio = info->ngpio; + + return 1; +} + +static const struct dmi_system_id silicom_dmi_ids[] __initconst = { + { + .callback = silicom_platform_info_init, + .ident = "Silicom Cordoba (Generic)", + .matches = { + DMI_MATCH(DMI_BOARD_VENDOR, "Silicom"), + DMI_MATCH(DMI_BOARD_NAME, "80300-0214-G"), + }, + .driver_data = &silicom_generic_cordoba_info, + }, + { + .callback = silicom_platform_info_init, + .ident = "Silicom Cordoba (Generic)", + .matches = { + DMI_MATCH(DMI_BOARD_VENDOR, "Silicom"), + DMI_MATCH(DMI_BOARD_NAME, "80500-0214-G"), + }, + .driver_data = &silicom_generic_cordoba_info, + }, + { + .callback = silicom_platform_info_init, + .ident = "Silicom Cordoba (plat_0222)", + .matches = { + DMI_MATCH(DMI_BOARD_VENDOR, "Silicom"), + DMI_MATCH(DMI_BOARD_NAME, "80300-0222-G"), + }, + .driver_data = &silicom_plat_0222_cordoba_info, + }, + { }, +}; +MODULE_DEVICE_TABLE(dmi, silicom_dmi_ids); + +static int __init silicom_platform_init(void) +{ + if (!dmi_check_system(silicom_dmi_ids)) { + pr_err("No DMI match for this platform\n"); + return -ENODEV; + } + silicom_platform_dev = platform_create_bundle(&silicom_platform_driver, + silicom_platform_probe, + NULL, 0, NULL, 0); + + return PTR_ERR_OR_ZERO(silicom_platform_dev); +} + +static void __exit silicom_platform_exit(void) +{ + platform_device_unregister(silicom_platform_dev); + platform_driver_unregister(&silicom_platform_driver); +} + +module_init(silicom_platform_init); +module_exit(silicom_platform_exit); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Henry Shi "); +MODULE_DESCRIPTION("Platform driver for Silicom network appliances"); -- cgit From 8cbcc1dbf8a62c730fadd60de761e0658547a589 Mon Sep 17 00:00:00 2001 From: "David E. Box" Date: Wed, 29 Nov 2023 14:21:13 -0800 Subject: platform/x86/intel/vsec: Fix xa_alloc memory leak MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Commit 936874b77dd0 ("platform/x86/intel/vsec: Add PCI error recovery support to Intel PMT") added an xarray to track the list of vsec devices to be recovered after a PCI error. But it did not provide cleanup for the list leading to a memory leak that was caught by kmemleak. Do xa_alloc() before devm_add_action_or_reset() so that the list may be cleaned up with xa_erase() in the release function. Fixes: 936874b77dd0 ("platform/x86/intel/vsec: Add PCI error recovery support to Intel PMT") Signed-off-by: David E. Box Reviewed-by: Ilpo Järvinen Link: https://lore.kernel.org/r/20231129222132.2331261-2-david.e.box@linux.intel.com [hdegoede@redhat.com: Add missing xa_erase() on error-exit Signed-off-by: Hans de Goede --- drivers/platform/x86/intel/vsec.c | 25 +++++++++++++++---------- drivers/platform/x86/intel/vsec.h | 1 + 2 files changed, 16 insertions(+), 10 deletions(-) diff --git a/drivers/platform/x86/intel/vsec.c b/drivers/platform/x86/intel/vsec.c index c1f9e4471b28..343ab6a82c01 100644 --- a/drivers/platform/x86/intel/vsec.c +++ b/drivers/platform/x86/intel/vsec.c @@ -120,6 +120,8 @@ static void intel_vsec_dev_release(struct device *dev) { struct intel_vsec_device *intel_vsec_dev = dev_to_ivdev(dev); + xa_erase(&auxdev_array, intel_vsec_dev->id); + mutex_lock(&vsec_ida_lock); ida_free(intel_vsec_dev->ida, intel_vsec_dev->auxdev.id); mutex_unlock(&vsec_ida_lock); @@ -135,19 +137,28 @@ int intel_vsec_add_aux(struct pci_dev *pdev, struct device *parent, struct auxiliary_device *auxdev = &intel_vsec_dev->auxdev; int ret, id; - mutex_lock(&vsec_ida_lock); - ret = ida_alloc(intel_vsec_dev->ida, GFP_KERNEL); - mutex_unlock(&vsec_ida_lock); + ret = xa_alloc(&auxdev_array, &intel_vsec_dev->id, intel_vsec_dev, + PMT_XA_LIMIT, GFP_KERNEL); if (ret < 0) { kfree(intel_vsec_dev->resource); kfree(intel_vsec_dev); return ret; } + mutex_lock(&vsec_ida_lock); + id = ida_alloc(intel_vsec_dev->ida, GFP_KERNEL); + mutex_unlock(&vsec_ida_lock); + if (id < 0) { + xa_erase(&auxdev_array, intel_vsec_dev->id); + kfree(intel_vsec_dev->resource); + kfree(intel_vsec_dev); + return id; + } + if (!parent) parent = &pdev->dev; - auxdev->id = ret; + auxdev->id = id; auxdev->name = name; auxdev->dev.parent = parent; auxdev->dev.release = intel_vsec_dev_release; @@ -169,12 +180,6 @@ int intel_vsec_add_aux(struct pci_dev *pdev, struct device *parent, if (ret < 0) return ret; - /* Add auxdev to list */ - ret = xa_alloc(&auxdev_array, &id, intel_vsec_dev, PMT_XA_LIMIT, - GFP_KERNEL); - if (ret) - return ret; - return 0; } EXPORT_SYMBOL_NS_GPL(intel_vsec_add_aux, INTEL_VSEC); diff --git a/drivers/platform/x86/intel/vsec.h b/drivers/platform/x86/intel/vsec.h index 0fd042c171ba..0a6201b4a0e9 100644 --- a/drivers/platform/x86/intel/vsec.h +++ b/drivers/platform/x86/intel/vsec.h @@ -45,6 +45,7 @@ struct intel_vsec_device { struct ida *ida; struct intel_vsec_platform_info *info; int num_resources; + int id; /* xa */ void *priv_data; size_t priv_data_size; }; -- cgit From ace7b6f00870cea56460df335606e35ace3c07ac Mon Sep 17 00:00:00 2001 From: "David E. Box" Date: Wed, 29 Nov 2023 14:21:14 -0800 Subject: platform/x86/intel/vsec: Remove unnecessary return MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In intel_vsec_add_aux(), just return from the last call to devm_add_action_or_reset() instead of checking its return value. Suggested-by: Ilpo Järvinen Signed-off-by: David E. Box Reviewed-by: Ilpo Järvinen Link: https://lore.kernel.org/r/20231129222132.2331261-3-david.e.box@linux.intel.com Signed-off-by: Hans de Goede --- 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 343ab6a82c01..25017227a0a6 100644 --- a/drivers/platform/x86/intel/vsec.c +++ b/drivers/platform/x86/intel/vsec.c @@ -175,12 +175,8 @@ int intel_vsec_add_aux(struct pci_dev *pdev, struct device *parent, return ret; } - ret = devm_add_action_or_reset(parent, intel_vsec_remove_aux, + return devm_add_action_or_reset(parent, intel_vsec_remove_aux, auxdev); - if (ret < 0) - return ret; - - return 0; } EXPORT_SYMBOL_NS_GPL(intel_vsec_add_aux, INTEL_VSEC); -- cgit From dbc01b0c86a7b23ffd06e14a84591500b04591ed Mon Sep 17 00:00:00 2001 From: "David E. Box" Date: Wed, 29 Nov 2023 14:21:15 -0800 Subject: platform/x86/intel/vsec: Move structures to header MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In preparation for exporting an API to register Intel Vendor Specific Extended Capabilities (VSEC) from other drivers, move needed structures to the header file. Signed-off-by: David E. Box Reviewed-by: Ilpo Järvinen Link: https://lore.kernel.org/r/20231129222132.2331261-4-david.e.box@linux.intel.com Signed-off-by: Hans de Goede --- drivers/platform/x86/intel/vsec.c | 35 ----------------------------------- drivers/platform/x86/intel/vsec.h | 36 ++++++++++++++++++++++++++++++++++++ 2 files changed, 36 insertions(+), 35 deletions(-) diff --git a/drivers/platform/x86/intel/vsec.c b/drivers/platform/x86/intel/vsec.c index 25017227a0a6..4dc490fd4a5b 100644 --- a/drivers/platform/x86/intel/vsec.c +++ b/drivers/platform/x86/intel/vsec.c @@ -24,13 +24,6 @@ #include "vsec.h" -/* Intel DVSEC offsets */ -#define INTEL_DVSEC_ENTRIES 0xA -#define INTEL_DVSEC_SIZE 0xB -#define INTEL_DVSEC_TABLE 0xC -#define INTEL_DVSEC_TABLE_BAR(x) ((x) & GENMASK(2, 0)) -#define INTEL_DVSEC_TABLE_OFFSET(x) ((x) & GENMASK(31, 3)) -#define TABLE_OFFSET_SHIFT 3 #define PMT_XA_START 0 #define PMT_XA_MAX INT_MAX #define PMT_XA_LIMIT XA_LIMIT(PMT_XA_START, PMT_XA_MAX) @@ -39,34 +32,6 @@ static DEFINE_IDA(intel_vsec_ida); static DEFINE_IDA(intel_vsec_sdsi_ida); static DEFINE_XARRAY_ALLOC(auxdev_array); -/** - * struct intel_vsec_header - Common fields of Intel VSEC and DVSEC registers. - * @rev: Revision ID of the VSEC/DVSEC register space - * @length: Length of the VSEC/DVSEC register space - * @id: ID of the feature - * @num_entries: Number of instances of the feature - * @entry_size: Size of the discovery table for each feature - * @tbir: BAR containing the discovery tables - * @offset: BAR offset of start of the first discovery table - */ -struct intel_vsec_header { - u8 rev; - u16 length; - u16 id; - u8 num_entries; - u8 entry_size; - u8 tbir; - u32 offset; -}; - -enum intel_vsec_id { - VSEC_ID_TELEMETRY = 2, - VSEC_ID_WATCHER = 3, - VSEC_ID_CRASHLOG = 4, - VSEC_ID_SDSI = 65, - VSEC_ID_TPMI = 66, -}; - static const char *intel_vsec_name(enum intel_vsec_id id) { switch (id) { diff --git a/drivers/platform/x86/intel/vsec.h b/drivers/platform/x86/intel/vsec.h index 0a6201b4a0e9..c242c07ea69c 100644 --- a/drivers/platform/x86/intel/vsec.h +++ b/drivers/platform/x86/intel/vsec.h @@ -11,9 +11,45 @@ #define VSEC_CAP_SDSI BIT(3) #define VSEC_CAP_TPMI BIT(4) +/* Intel DVSEC offsets */ +#define INTEL_DVSEC_ENTRIES 0xA +#define INTEL_DVSEC_SIZE 0xB +#define INTEL_DVSEC_TABLE 0xC +#define INTEL_DVSEC_TABLE_BAR(x) ((x) & GENMASK(2, 0)) +#define INTEL_DVSEC_TABLE_OFFSET(x) ((x) & GENMASK(31, 3)) +#define TABLE_OFFSET_SHIFT 3 + struct pci_dev; struct resource; +enum intel_vsec_id { + VSEC_ID_TELEMETRY = 2, + VSEC_ID_WATCHER = 3, + VSEC_ID_CRASHLOG = 4, + VSEC_ID_SDSI = 65, + VSEC_ID_TPMI = 66, +}; + +/** + * struct intel_vsec_header - Common fields of Intel VSEC and DVSEC registers. + * @rev: Revision ID of the VSEC/DVSEC register space + * @length: Length of the VSEC/DVSEC register space + * @id: ID of the feature + * @num_entries: Number of instances of the feature + * @entry_size: Size of the discovery table for each feature + * @tbir: BAR containing the discovery tables + * @offset: BAR offset of start of the first discovery table + */ +struct intel_vsec_header { + u8 rev; + u16 length; + u16 id; + u8 num_entries; + u8 entry_size; + u8 tbir; + u32 offset; +}; + enum intel_vsec_quirks { /* Watcher feature not supported */ VSEC_QUIRK_NO_WATCHER = BIT(0), -- cgit From 0a0a52abaa65b844afde3d7229c209a8cddc5a07 Mon Sep 17 00:00:00 2001 From: "David E. Box" Date: Wed, 29 Nov 2023 14:21:16 -0800 Subject: platform/x86/intel/vsec: remove platform_info from vsec device structure MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In preparation for exporting an API to register Intel Vendor Specific Extended Capabilities (VSEC) from other drivers, remove the pointer to platform_info from intel_vsec_device. This prevents a potential page fault when auxiliary drivers probe and attempt to dereference this pointer to access the needed quirks field. Instead, just add the quirks to intel_vsec_device. Signed-off-by: David E. Box Reviewed-by: Ilpo Järvinen Link: https://lore.kernel.org/r/20231129222132.2331261-5-david.e.box@linux.intel.com Signed-off-by: Hans de Goede --- drivers/platform/x86/intel/pmt/class.c | 2 +- drivers/platform/x86/intel/vsec.c | 2 +- drivers/platform/x86/intel/vsec.h | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/platform/x86/intel/pmt/class.c b/drivers/platform/x86/intel/pmt/class.c index f32a233470de..2ad91d2fd954 100644 --- a/drivers/platform/x86/intel/pmt/class.c +++ b/drivers/platform/x86/intel/pmt/class.c @@ -31,7 +31,7 @@ bool intel_pmt_is_early_client_hw(struct device *dev) * differences from the server platforms (which use the Out Of Band * Management Services Module OOBMSM). */ - return !!(ivdev->info->quirks & VSEC_QUIRK_EARLY_HW); + return !!(ivdev->quirks & VSEC_QUIRK_EARLY_HW); } EXPORT_SYMBOL_NS_GPL(intel_pmt_is_early_client_hw, INTEL_PMT); diff --git a/drivers/platform/x86/intel/vsec.c b/drivers/platform/x86/intel/vsec.c index 4dc490fd4a5b..bcdc727c4cc3 100644 --- a/drivers/platform/x86/intel/vsec.c +++ b/drivers/platform/x86/intel/vsec.c @@ -194,7 +194,7 @@ static int intel_vsec_add_dev(struct pci_dev *pdev, struct intel_vsec_header *he intel_vsec_dev->pcidev = pdev; intel_vsec_dev->resource = res; intel_vsec_dev->num_resources = header->num_entries; - intel_vsec_dev->info = info; + intel_vsec_dev->quirks = info->quirks; if (header->id == VSEC_ID_SDSI) intel_vsec_dev->ida = &intel_vsec_sdsi_ida; diff --git a/drivers/platform/x86/intel/vsec.h b/drivers/platform/x86/intel/vsec.h index c242c07ea69c..8b9fad170503 100644 --- a/drivers/platform/x86/intel/vsec.h +++ b/drivers/platform/x86/intel/vsec.h @@ -79,11 +79,11 @@ struct intel_vsec_device { struct pci_dev *pcidev; struct resource *resource; struct ida *ida; - struct intel_vsec_platform_info *info; int num_resources; int id; /* xa */ void *priv_data; size_t priv_data_size; + unsigned long quirks; }; int intel_vsec_add_aux(struct pci_dev *pdev, struct device *parent, -- cgit From 1d1b4770d4b661ecdf899c314ce406b9840c0c22 Mon Sep 17 00:00:00 2001 From: "David E. Box" Date: Wed, 29 Nov 2023 14:21:17 -0800 Subject: platform/x86/intel/vsec: Use cleanup.h MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Use cleanup.h helpers to handle cleanup of resources in intel_vsec_add_dev() after failures. Signed-off-by: David E. Box Reviewed-by: Ilpo Järvinen Link: https://lore.kernel.org/r/20231129222132.2331261-6-david.e.box@linux.intel.com Signed-off-by: Hans de Goede --- drivers/platform/x86/intel/vsec.c | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/drivers/platform/x86/intel/vsec.c b/drivers/platform/x86/intel/vsec.c index bcdc727c4cc3..6b0e7363397a 100644 --- a/drivers/platform/x86/intel/vsec.c +++ b/drivers/platform/x86/intel/vsec.c @@ -15,6 +15,7 @@ #include #include +#include #include #include #include @@ -148,8 +149,9 @@ EXPORT_SYMBOL_NS_GPL(intel_vsec_add_aux, INTEL_VSEC); static int intel_vsec_add_dev(struct pci_dev *pdev, struct intel_vsec_header *header, struct intel_vsec_platform_info *info) { - struct intel_vsec_device *intel_vsec_dev; - struct resource *res, *tmp; + struct intel_vsec_device __free(kfree) *intel_vsec_dev = NULL; + struct resource __free(kfree) *res = NULL; + struct resource *tmp; unsigned long quirks = info->quirks; int i; @@ -171,10 +173,8 @@ static int intel_vsec_add_dev(struct pci_dev *pdev, struct intel_vsec_header *he return -ENOMEM; res = kcalloc(header->num_entries, sizeof(*res), GFP_KERNEL); - if (!res) { - kfree(intel_vsec_dev); + if (!res) return -ENOMEM; - } if (quirks & VSEC_QUIRK_TABLE_SHIFT) header->offset >>= TABLE_OFFSET_SHIFT; @@ -192,7 +192,7 @@ static int intel_vsec_add_dev(struct pci_dev *pdev, struct intel_vsec_header *he } intel_vsec_dev->pcidev = pdev; - intel_vsec_dev->resource = res; + intel_vsec_dev->resource = no_free_ptr(res); intel_vsec_dev->num_resources = header->num_entries; intel_vsec_dev->quirks = info->quirks; @@ -201,7 +201,11 @@ static int intel_vsec_add_dev(struct pci_dev *pdev, struct intel_vsec_header *he else intel_vsec_dev->ida = &intel_vsec_ida; - return intel_vsec_add_aux(pdev, NULL, intel_vsec_dev, + /* + * Pass the ownership of intel_vsec_dev and resource within it to + * intel_vsec_add_aux() + */ + return intel_vsec_add_aux(pdev, NULL, no_free_ptr(intel_vsec_dev), intel_vsec_name(header->id)); } -- cgit From 6dfc2514acee37e30ce59f1f25b1f8f6aa7c1b08 Mon Sep 17 00:00:00 2001 From: "David E. Box" Date: Wed, 29 Nov 2023 14:21:18 -0800 Subject: platform/x86/intel/vsec: Assign auxdev parent by argument MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Instead of checking for a NULL parent argument in intel_vsec_add_aux() and then assigning it to the probed device, remove this check and just pass the device in the call. Since this function is exported, return -EINVAL if the parent is not specified. Signed-off-by: David E. Box Reviewed-by: Ilpo Järvinen Link: https://lore.kernel.org/r/20231129222132.2331261-7-david.e.box@linux.intel.com Signed-off-by: Hans de Goede --- drivers/platform/x86/intel/vsec.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/platform/x86/intel/vsec.c b/drivers/platform/x86/intel/vsec.c index 6b0e7363397a..bcfb5d480ebd 100644 --- a/drivers/platform/x86/intel/vsec.c +++ b/drivers/platform/x86/intel/vsec.c @@ -103,6 +103,9 @@ int intel_vsec_add_aux(struct pci_dev *pdev, struct device *parent, struct auxiliary_device *auxdev = &intel_vsec_dev->auxdev; int ret, id; + if (!parent) + return -EINVAL; + ret = xa_alloc(&auxdev_array, &intel_vsec_dev->id, intel_vsec_dev, PMT_XA_LIMIT, GFP_KERNEL); if (ret < 0) { @@ -121,9 +124,6 @@ int intel_vsec_add_aux(struct pci_dev *pdev, struct device *parent, return id; } - if (!parent) - parent = &pdev->dev; - auxdev->id = id; auxdev->name = name; auxdev->dev.parent = parent; @@ -205,7 +205,7 @@ static int intel_vsec_add_dev(struct pci_dev *pdev, struct intel_vsec_header *he * Pass the ownership of intel_vsec_dev and resource within it to * intel_vsec_add_aux() */ - return intel_vsec_add_aux(pdev, NULL, no_free_ptr(intel_vsec_dev), + return intel_vsec_add_aux(pdev, &pdev->dev, no_free_ptr(intel_vsec_dev), intel_vsec_name(header->id)); } -- cgit From 4edbd117ba3f7beacfb439aad60e8a5de77114b4 Mon Sep 17 00:00:00 2001 From: Gayatri Kammela Date: Wed, 29 Nov 2023 14:21:19 -0800 Subject: platform/x86/intel/vsec: Add intel_vsec_register MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add and export intel_vsec_register() to allow the registration of Intel extended capabilities from other drivers. Add check to look for memory conflicts before registering a new capability. Since the vsec provider may not be a PCI device, add a parent field to intel_vsec_platform_info() to allow specifying the parent device for device managed cleanup. Signed-off-by: Gayatri Kammela Signed-off-by: David E. Box Reviewed-by: Ilpo Järvinen Link: https://lore.kernel.org/r/20231129222132.2331261-8-david.e.box@linux.intel.com Signed-off-by: Hans de Goede --- drivers/platform/x86/intel/vsec.c | 24 +++++++++++++++++++++++- drivers/platform/x86/intel/vsec.h | 4 ++++ 2 files changed, 27 insertions(+), 1 deletion(-) diff --git a/drivers/platform/x86/intel/vsec.c b/drivers/platform/x86/intel/vsec.c index bcfb5d480ebd..5568d6236bd6 100644 --- a/drivers/platform/x86/intel/vsec.c +++ b/drivers/platform/x86/intel/vsec.c @@ -152,9 +152,15 @@ static int intel_vsec_add_dev(struct pci_dev *pdev, struct intel_vsec_header *he struct intel_vsec_device __free(kfree) *intel_vsec_dev = NULL; struct resource __free(kfree) *res = NULL; struct resource *tmp; + struct device *parent; unsigned long quirks = info->quirks; int i; + if (info->parent) + parent = info->parent; + else + parent = &pdev->dev; + if (!intel_vsec_supported(header->id, info->caps)) return -EINVAL; @@ -189,6 +195,12 @@ static int intel_vsec_add_dev(struct pci_dev *pdev, struct intel_vsec_header *he header->offset + i * (header->entry_size * sizeof(u32)); tmp->end = tmp->start + (header->entry_size * sizeof(u32)) - 1; tmp->flags = IORESOURCE_MEM; + + /* Check resource is not in use */ + if (!request_mem_region(tmp->start, resource_size(tmp), "")) + return -EBUSY; + + release_mem_region(tmp->start, resource_size(tmp)); } intel_vsec_dev->pcidev = pdev; @@ -205,7 +217,7 @@ static int intel_vsec_add_dev(struct pci_dev *pdev, struct intel_vsec_header *he * Pass the ownership of intel_vsec_dev and resource within it to * intel_vsec_add_aux() */ - return intel_vsec_add_aux(pdev, &pdev->dev, no_free_ptr(intel_vsec_dev), + return intel_vsec_add_aux(pdev, parent, no_free_ptr(intel_vsec_dev), intel_vsec_name(header->id)); } @@ -323,6 +335,16 @@ static bool intel_vsec_walk_vsec(struct pci_dev *pdev, return have_devices; } +void intel_vsec_register(struct pci_dev *pdev, + struct intel_vsec_platform_info *info) +{ + if (!pdev || !info) + return; + + intel_vsec_walk_header(pdev, info); +} +EXPORT_SYMBOL_NS_GPL(intel_vsec_register, INTEL_VSEC); + static int intel_vsec_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id) { struct intel_vsec_platform_info *info; diff --git a/drivers/platform/x86/intel/vsec.h b/drivers/platform/x86/intel/vsec.h index 8b9fad170503..bb8b6452df70 100644 --- a/drivers/platform/x86/intel/vsec.h +++ b/drivers/platform/x86/intel/vsec.h @@ -69,6 +69,7 @@ enum intel_vsec_quirks { /* Platform specific data */ struct intel_vsec_platform_info { + struct device *parent; struct intel_vsec_header **headers; unsigned long caps; unsigned long quirks; @@ -99,4 +100,7 @@ static inline struct intel_vsec_device *auxdev_to_ivdev(struct auxiliary_device { return container_of(auxdev, struct intel_vsec_device, auxdev); } + +void intel_vsec_register(struct pci_dev *pdev, + struct intel_vsec_platform_info *info); #endif -- cgit From e97ec7f621fbfdce07bf1b98a26883ee19281747 Mon Sep 17 00:00:00 2001 From: "David E. Box" Date: Wed, 29 Nov 2023 14:21:20 -0800 Subject: platform/x86/intel/vsec: Add base address field MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Some devices may emulate PCI VSEC capabilities in MMIO. In such cases the BAR is not readable from a config space. Provide a field for drivers to indicate the base address to be used. Signed-off-by: David E. Box Reviewed-by: Ilpo Järvinen Link: https://lore.kernel.org/r/20231129222132.2331261-9-david.e.box@linux.intel.com Signed-off-by: Hans de Goede --- drivers/platform/x86/intel/pmt/class.c | 14 +++++++++++--- drivers/platform/x86/intel/vsec.c | 10 ++++++++-- drivers/platform/x86/intel/vsec.h | 2 ++ 3 files changed, 21 insertions(+), 5 deletions(-) diff --git a/drivers/platform/x86/intel/pmt/class.c b/drivers/platform/x86/intel/pmt/class.c index 2ad91d2fd954..32608baaa56c 100644 --- a/drivers/platform/x86/intel/pmt/class.c +++ b/drivers/platform/x86/intel/pmt/class.c @@ -160,10 +160,11 @@ static struct class intel_pmt_class = { static int intel_pmt_populate_entry(struct intel_pmt_entry *entry, struct intel_pmt_header *header, - struct device *dev, + struct intel_vsec_device *ivdev, struct resource *disc_res) { - struct pci_dev *pci_dev = to_pci_dev(dev->parent); + struct pci_dev *pci_dev = ivdev->pcidev; + struct device *dev = &ivdev->auxdev.dev; u8 bir; /* @@ -215,6 +216,13 @@ static int intel_pmt_populate_entry(struct intel_pmt_entry *entry, break; case ACCESS_BARID: + /* Use the provided base address if it exists */ + if (ivdev->base_addr) { + entry->base_addr = ivdev->base_addr + + GET_ADDRESS(header->base_offset); + break; + } + /* * If another BAR was specified then the base offset * represents the offset within that BAR. SO retrieve the @@ -319,7 +327,7 @@ int intel_pmt_dev_create(struct intel_pmt_entry *entry, struct intel_pmt_namespa if (ret) return ret; - ret = intel_pmt_populate_entry(entry, &header, dev, disc_res); + ret = intel_pmt_populate_entry(entry, &header, intel_vsec_dev, disc_res); if (ret) return ret; diff --git a/drivers/platform/x86/intel/vsec.c b/drivers/platform/x86/intel/vsec.c index 5568d6236bd6..b68586731e45 100644 --- a/drivers/platform/x86/intel/vsec.c +++ b/drivers/platform/x86/intel/vsec.c @@ -154,6 +154,7 @@ static int intel_vsec_add_dev(struct pci_dev *pdev, struct intel_vsec_header *he struct resource *tmp; struct device *parent; unsigned long quirks = info->quirks; + u64 base_addr; int i; if (info->parent) @@ -185,14 +186,18 @@ static int intel_vsec_add_dev(struct pci_dev *pdev, struct intel_vsec_header *he if (quirks & VSEC_QUIRK_TABLE_SHIFT) header->offset >>= TABLE_OFFSET_SHIFT; + if (info->base_addr) + base_addr = info->base_addr; + else + base_addr = pdev->resource[header->tbir].start; + /* * The DVSEC/VSEC contains the starting offset and count for a block of * discovery tables. Create a resource array of these tables to the * auxiliary device driver. */ for (i = 0, tmp = res; i < header->num_entries; i++, tmp++) { - tmp->start = pdev->resource[header->tbir].start + - header->offset + i * (header->entry_size * sizeof(u32)); + tmp->start = base_addr + header->offset + i * (header->entry_size * sizeof(u32)); tmp->end = tmp->start + (header->entry_size * sizeof(u32)) - 1; tmp->flags = IORESOURCE_MEM; @@ -207,6 +212,7 @@ static int intel_vsec_add_dev(struct pci_dev *pdev, struct intel_vsec_header *he intel_vsec_dev->resource = no_free_ptr(res); intel_vsec_dev->num_resources = header->num_entries; intel_vsec_dev->quirks = info->quirks; + intel_vsec_dev->base_addr = info->base_addr; if (header->id == VSEC_ID_SDSI) intel_vsec_dev->ida = &intel_vsec_sdsi_ida; diff --git a/drivers/platform/x86/intel/vsec.h b/drivers/platform/x86/intel/vsec.h index bb8b6452df70..e23e76129691 100644 --- a/drivers/platform/x86/intel/vsec.h +++ b/drivers/platform/x86/intel/vsec.h @@ -73,6 +73,7 @@ struct intel_vsec_platform_info { struct intel_vsec_header **headers; unsigned long caps; unsigned long quirks; + u64 base_addr; }; struct intel_vsec_device { @@ -85,6 +86,7 @@ struct intel_vsec_device { void *priv_data; size_t priv_data_size; unsigned long quirks; + u64 base_addr; }; int intel_vsec_add_aux(struct pci_dev *pdev, struct device *parent, -- cgit From 4d1b7efee3fc703c64bacc37c4824888c5f26e8b Mon Sep 17 00:00:00 2001 From: "David E. Box" Date: Wed, 29 Nov 2023 14:21:21 -0800 Subject: platform/x86/intel/pmt: Add header to struct intel_pmt_entry MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The PMT header is passed to several functions. Instead, store the header in struct intel_pmt_entry which is also passed to these functions and shorten the argument list. This simplifies the calls in preparation for later changes. While here also perform a newline cleanup. Signed-off-by: David E. Box Reviewed-by: Ilpo Järvinen Link: https://lore.kernel.org/r/20231129222132.2331261-10-david.e.box@linux.intel.com Signed-off-by: Hans de Goede --- drivers/platform/x86/intel/pmt/class.c | 8 +++----- drivers/platform/x86/intel/pmt/class.h | 16 ++++++++-------- drivers/platform/x86/intel/pmt/crashlog.c | 2 +- drivers/platform/x86/intel/pmt/telemetry.c | 2 +- 4 files changed, 13 insertions(+), 15 deletions(-) diff --git a/drivers/platform/x86/intel/pmt/class.c b/drivers/platform/x86/intel/pmt/class.c index 32608baaa56c..142a24e3727d 100644 --- a/drivers/platform/x86/intel/pmt/class.c +++ b/drivers/platform/x86/intel/pmt/class.c @@ -159,12 +159,12 @@ static struct class intel_pmt_class = { }; static int intel_pmt_populate_entry(struct intel_pmt_entry *entry, - struct intel_pmt_header *header, struct intel_vsec_device *ivdev, struct resource *disc_res) { struct pci_dev *pci_dev = ivdev->pcidev; struct device *dev = &ivdev->auxdev.dev; + struct intel_pmt_header *header = &entry->header; u8 bir; /* @@ -313,7 +313,6 @@ int intel_pmt_dev_create(struct intel_pmt_entry *entry, struct intel_pmt_namespa struct intel_vsec_device *intel_vsec_dev, int idx) { struct device *dev = &intel_vsec_dev->auxdev.dev; - struct intel_pmt_header header; struct resource *disc_res; int ret; @@ -323,16 +322,15 @@ int intel_pmt_dev_create(struct intel_pmt_entry *entry, struct intel_pmt_namespa if (IS_ERR(entry->disc_table)) return PTR_ERR(entry->disc_table); - ret = ns->pmt_header_decode(entry, &header, dev); + ret = ns->pmt_header_decode(entry, dev); if (ret) return ret; - ret = intel_pmt_populate_entry(entry, &header, intel_vsec_dev, disc_res); + ret = intel_pmt_populate_entry(entry, intel_vsec_dev, disc_res); if (ret) return ret; return intel_pmt_dev_register(entry, ns, dev); - } EXPORT_SYMBOL_NS_GPL(intel_pmt_dev_create, INTEL_PMT); diff --git a/drivers/platform/x86/intel/pmt/class.h b/drivers/platform/x86/intel/pmt/class.h index db11d58867ce..e477a19f6700 100644 --- a/drivers/platform/x86/intel/pmt/class.h +++ b/drivers/platform/x86/intel/pmt/class.h @@ -18,7 +18,15 @@ #define GET_BIR(v) ((v) & GENMASK(2, 0)) #define GET_ADDRESS(v) ((v) & GENMASK(31, 3)) +struct intel_pmt_header { + u32 base_offset; + u32 size; + u32 guid; + u8 access_type; +}; + struct intel_pmt_entry { + struct intel_pmt_header header; struct bin_attribute pmt_bin_attr; struct kobject *kobj; void __iomem *disc_table; @@ -29,19 +37,11 @@ struct intel_pmt_entry { int devid; }; -struct intel_pmt_header { - u32 base_offset; - u32 size; - u32 guid; - u8 access_type; -}; - struct intel_pmt_namespace { const char *name; struct xarray *xa; const struct attribute_group *attr_grp; int (*pmt_header_decode)(struct intel_pmt_entry *entry, - struct intel_pmt_header *header, struct device *dev); }; diff --git a/drivers/platform/x86/intel/pmt/crashlog.c b/drivers/platform/x86/intel/pmt/crashlog.c index bbb3d61d09f4..4014c02cafdb 100644 --- a/drivers/platform/x86/intel/pmt/crashlog.c +++ b/drivers/platform/x86/intel/pmt/crashlog.c @@ -223,10 +223,10 @@ static const struct attribute_group pmt_crashlog_group = { }; static int pmt_crashlog_header_decode(struct intel_pmt_entry *entry, - struct intel_pmt_header *header, struct device *dev) { void __iomem *disc_table = entry->disc_table; + struct intel_pmt_header *header = &entry->header; struct crashlog_entry *crashlog; if (!pmt_crashlog_supported(entry)) diff --git a/drivers/platform/x86/intel/pmt/telemetry.c b/drivers/platform/x86/intel/pmt/telemetry.c index 39cbc87cc28a..f86080e8bebd 100644 --- a/drivers/platform/x86/intel/pmt/telemetry.c +++ b/drivers/platform/x86/intel/pmt/telemetry.c @@ -58,10 +58,10 @@ static bool pmt_telem_region_overlaps(struct intel_pmt_entry *entry, } static int pmt_telem_header_decode(struct intel_pmt_entry *entry, - struct intel_pmt_header *header, struct device *dev) { void __iomem *disc_table = entry->disc_table; + struct intel_pmt_header *header = &entry->header; if (pmt_telem_region_overlaps(entry, dev)) return 1; -- cgit From 416eeb2e1fc7b60ab0c7ced26ab966dd7733357d Mon Sep 17 00:00:00 2001 From: "David E. Box" Date: Wed, 29 Nov 2023 14:21:22 -0800 Subject: platform/x86/intel/pmt: telemetry: Export API to read telemetry MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Export symbols to allow access to Intel PMT Telemetry data on available devices. Provides APIs to search, register, and read telemetry using a kref managed pointer that serves as a handle to a telemetry endpoint. To simplify searching for present devices, have the IDA start at 1 instead of 0 so that 0 can be used to indicate end of search. Signed-off-by: David E. Box Reviewed-by: Ilpo Järvinen Link: https://lore.kernel.org/r/20231129222132.2331261-11-david.e.box@linux.intel.com Signed-off-by: Hans de Goede --- drivers/platform/x86/intel/pmt/class.c | 21 +++- drivers/platform/x86/intel/pmt/class.h | 14 +++ drivers/platform/x86/intel/pmt/telemetry.c | 191 ++++++++++++++++++++++++++++- drivers/platform/x86/intel/pmt/telemetry.h | 126 +++++++++++++++++++ 4 files changed, 344 insertions(+), 8 deletions(-) create mode 100644 drivers/platform/x86/intel/pmt/telemetry.h diff --git a/drivers/platform/x86/intel/pmt/class.c b/drivers/platform/x86/intel/pmt/class.c index 142a24e3727d..4b53940a64e2 100644 --- a/drivers/platform/x86/intel/pmt/class.c +++ b/drivers/platform/x86/intel/pmt/class.c @@ -17,7 +17,7 @@ #include "../vsec.h" #include "class.h" -#define PMT_XA_START 0 +#define PMT_XA_START 1 #define PMT_XA_MAX INT_MAX #define PMT_XA_LIMIT XA_LIMIT(PMT_XA_START, PMT_XA_MAX) #define GUID_SPR_PUNIT 0x9956f43f @@ -247,6 +247,7 @@ static int intel_pmt_dev_register(struct intel_pmt_entry *entry, struct intel_pmt_namespace *ns, struct device *parent) { + struct intel_vsec_device *ivdev = dev_to_ivdev(parent); struct resource res = {0}; struct device *dev; int ret; @@ -270,7 +271,7 @@ static int intel_pmt_dev_register(struct intel_pmt_entry *entry, if (ns->attr_grp) { ret = sysfs_create_group(entry->kobj, ns->attr_grp); if (ret) - goto fail_sysfs; + goto fail_sysfs_create_group; } /* if size is 0 assume no data buffer, so no file needed */ @@ -295,13 +296,23 @@ static int intel_pmt_dev_register(struct intel_pmt_entry *entry, entry->pmt_bin_attr.size = entry->size; ret = sysfs_create_bin_file(&dev->kobj, &entry->pmt_bin_attr); - if (!ret) - return 0; + if (ret) + goto fail_ioremap; + + if (ns->pmt_add_endpoint) { + ret = ns->pmt_add_endpoint(entry, ivdev->pcidev); + if (ret) + goto fail_add_endpoint; + } + + return 0; +fail_add_endpoint: + sysfs_remove_bin_file(entry->kobj, &entry->pmt_bin_attr); fail_ioremap: if (ns->attr_grp) sysfs_remove_group(entry->kobj, ns->attr_grp); -fail_sysfs: +fail_sysfs_create_group: device_unregister(dev); fail_dev_create: xa_erase(ns->xa, entry->devid); diff --git a/drivers/platform/x86/intel/pmt/class.h b/drivers/platform/x86/intel/pmt/class.h index e477a19f6700..d23c63b73ab7 100644 --- a/drivers/platform/x86/intel/pmt/class.h +++ b/drivers/platform/x86/intel/pmt/class.h @@ -9,6 +9,7 @@ #include #include "../vsec.h" +#include "telemetry.h" /* PMT access types */ #define ACCESS_BARID 2 @@ -18,6 +19,16 @@ #define GET_BIR(v) ((v) & GENMASK(2, 0)) #define GET_ADDRESS(v) ((v) & GENMASK(31, 3)) +struct pci_dev; + +struct telem_endpoint { + struct pci_dev *pcidev; + struct telem_header header; + void __iomem *base; + bool present; + struct kref kref; +}; + struct intel_pmt_header { u32 base_offset; u32 size; @@ -26,6 +37,7 @@ struct intel_pmt_header { }; struct intel_pmt_entry { + struct telem_endpoint *ep; struct intel_pmt_header header; struct bin_attribute pmt_bin_attr; struct kobject *kobj; @@ -43,6 +55,8 @@ struct intel_pmt_namespace { const struct attribute_group *attr_grp; int (*pmt_header_decode)(struct intel_pmt_entry *entry, struct device *dev); + int (*pmt_add_endpoint)(struct intel_pmt_entry *entry, + struct pci_dev *pdev); }; bool intel_pmt_is_early_client_hw(struct device *dev); diff --git a/drivers/platform/x86/intel/pmt/telemetry.c b/drivers/platform/x86/intel/pmt/telemetry.c index f86080e8bebd..09258564dfc4 100644 --- a/drivers/platform/x86/intel/pmt/telemetry.c +++ b/drivers/platform/x86/intel/pmt/telemetry.c @@ -30,6 +30,15 @@ /* Used by client hardware to identify a fixed telemetry entry*/ #define TELEM_CLIENT_FIXED_BLOCK_GUID 0x10000000 +#define NUM_BYTES_QWORD(v) ((v) << 3) +#define SAMPLE_ID_OFFSET(v) ((v) << 3) + +#define NUM_BYTES_DWORD(v) ((v) << 2) +#define SAMPLE_ID_OFFSET32(v) ((v) << 2) + +/* Protects access to the xarray of telemetry endpoint handles */ +static DEFINE_MUTEX(ep_lock); + enum telem_type { TELEM_TYPE_PUNIT = 0, TELEM_TYPE_CRASHLOG, @@ -84,21 +93,195 @@ static int pmt_telem_header_decode(struct intel_pmt_entry *entry, return 0; } +static int pmt_telem_add_endpoint(struct intel_pmt_entry *entry, + struct pci_dev *pdev) +{ + struct telem_endpoint *ep; + + /* Endpoint lifetimes are managed by kref, not devres */ + entry->ep = kzalloc(sizeof(*(entry->ep)), GFP_KERNEL); + if (!entry->ep) + return -ENOMEM; + + ep = entry->ep; + ep->pcidev = pdev; + ep->header.access_type = entry->header.access_type; + ep->header.guid = entry->header.guid; + ep->header.base_offset = entry->header.base_offset; + ep->header.size = entry->header.size; + ep->base = entry->base; + ep->present = true; + + kref_init(&ep->kref); + + return 0; +} + static DEFINE_XARRAY_ALLOC(telem_array); static struct intel_pmt_namespace pmt_telem_ns = { .name = "telem", .xa = &telem_array, .pmt_header_decode = pmt_telem_header_decode, + .pmt_add_endpoint = pmt_telem_add_endpoint, }; +/* Called when all users unregister and the device is removed */ +static void pmt_telem_ep_release(struct kref *kref) +{ + struct telem_endpoint *ep; + + ep = container_of(kref, struct telem_endpoint, kref); + kfree(ep); +} + +unsigned long pmt_telem_get_next_endpoint(unsigned long start) +{ + struct intel_pmt_entry *entry; + unsigned long found_idx; + + mutex_lock(&ep_lock); + xa_for_each_start(&telem_array, found_idx, entry, start) { + /* + * Return first found index after start. + * 0 is not valid id. + */ + if (found_idx > start) + break; + } + mutex_unlock(&ep_lock); + + return found_idx == start ? 0 : found_idx; +} +EXPORT_SYMBOL_NS_GPL(pmt_telem_get_next_endpoint, INTEL_PMT_TELEMETRY); + +struct telem_endpoint *pmt_telem_register_endpoint(int devid) +{ + struct intel_pmt_entry *entry; + unsigned long index = devid; + + mutex_lock(&ep_lock); + entry = xa_find(&telem_array, &index, index, XA_PRESENT); + if (!entry) { + mutex_unlock(&ep_lock); + return ERR_PTR(-ENXIO); + } + + kref_get(&entry->ep->kref); + mutex_unlock(&ep_lock); + + return entry->ep; +} +EXPORT_SYMBOL_NS_GPL(pmt_telem_register_endpoint, INTEL_PMT_TELEMETRY); + +void pmt_telem_unregister_endpoint(struct telem_endpoint *ep) +{ + kref_put(&ep->kref, pmt_telem_ep_release); +} +EXPORT_SYMBOL_NS_GPL(pmt_telem_unregister_endpoint, INTEL_PMT_TELEMETRY); + +int pmt_telem_get_endpoint_info(int devid, struct telem_endpoint_info *info) +{ + struct intel_pmt_entry *entry; + unsigned long index = devid; + int err = 0; + + if (!info) + return -EINVAL; + + mutex_lock(&ep_lock); + entry = xa_find(&telem_array, &index, index, XA_PRESENT); + if (!entry) { + err = -ENXIO; + goto unlock; + } + + info->pdev = entry->ep->pcidev; + info->header = entry->ep->header; + +unlock: + mutex_unlock(&ep_lock); + return err; + +} +EXPORT_SYMBOL_NS_GPL(pmt_telem_get_endpoint_info, INTEL_PMT_TELEMETRY); + +int pmt_telem_read(struct telem_endpoint *ep, u32 id, u64 *data, u32 count) +{ + u32 offset, size; + + if (!ep->present) + return -ENODEV; + + offset = SAMPLE_ID_OFFSET(id); + size = ep->header.size; + + if (offset + NUM_BYTES_QWORD(count) > size) + return -EINVAL; + + memcpy_fromio(data, ep->base + offset, NUM_BYTES_QWORD(count)); + + return ep->present ? 0 : -EPIPE; +} +EXPORT_SYMBOL_NS_GPL(pmt_telem_read, INTEL_PMT_TELEMETRY); + +int pmt_telem_read32(struct telem_endpoint *ep, u32 id, u32 *data, u32 count) +{ + u32 offset, size; + + if (!ep->present) + return -ENODEV; + + offset = SAMPLE_ID_OFFSET32(id); + size = ep->header.size; + + if (offset + NUM_BYTES_DWORD(count) > size) + return -EINVAL; + + memcpy_fromio(data, ep->base + offset, NUM_BYTES_DWORD(count)); + + return ep->present ? 0 : -EPIPE; +} +EXPORT_SYMBOL_NS_GPL(pmt_telem_read32, INTEL_PMT_TELEMETRY); + +struct telem_endpoint * +pmt_telem_find_and_register_endpoint(struct pci_dev *pcidev, u32 guid, u16 pos) +{ + int devid = 0; + int inst = 0; + int err = 0; + + while ((devid = pmt_telem_get_next_endpoint(devid))) { + struct telem_endpoint_info ep_info; + + err = pmt_telem_get_endpoint_info(devid, &ep_info); + if (err) + return ERR_PTR(err); + + if (ep_info.header.guid == guid && ep_info.pdev == pcidev) { + if (inst == pos) + return pmt_telem_register_endpoint(devid); + ++inst; + } + } + + return ERR_PTR(-ENXIO); +} +EXPORT_SYMBOL_NS_GPL(pmt_telem_find_and_register_endpoint, INTEL_PMT_TELEMETRY); + static void pmt_telem_remove(struct auxiliary_device *auxdev) { struct pmt_telem_priv *priv = auxiliary_get_drvdata(auxdev); int i; - for (i = 0; i < priv->num_entries; i++) - intel_pmt_dev_destroy(&priv->entry[i], &pmt_telem_ns); -} + mutex_lock(&ep_lock); + for (i = 0; i < priv->num_entries; i++) { + struct intel_pmt_entry *entry = &priv->entry[i]; + + kref_put(&entry->ep->kref, pmt_telem_ep_release); + intel_pmt_dev_destroy(entry, &pmt_telem_ns); + } + mutex_unlock(&ep_lock); +}; static int pmt_telem_probe(struct auxiliary_device *auxdev, const struct auxiliary_device_id *id) { @@ -117,7 +300,9 @@ static int pmt_telem_probe(struct auxiliary_device *auxdev, const struct auxilia for (i = 0; i < intel_vsec_dev->num_resources; i++) { struct intel_pmt_entry *entry = &priv->entry[priv->num_entries]; + mutex_lock(&ep_lock); ret = intel_pmt_dev_create(entry, &pmt_telem_ns, intel_vsec_dev, i); + mutex_unlock(&ep_lock); if (ret < 0) goto abort_probe; if (ret) diff --git a/drivers/platform/x86/intel/pmt/telemetry.h b/drivers/platform/x86/intel/pmt/telemetry.h new file mode 100644 index 000000000000..d45af5512b4e --- /dev/null +++ b/drivers/platform/x86/intel/pmt/telemetry.h @@ -0,0 +1,126 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef _TELEMETRY_H +#define _TELEMETRY_H + +/* Telemetry types */ +#define PMT_TELEM_TELEMETRY 0 +#define PMT_TELEM_CRASHLOG 1 + +struct telem_endpoint; +struct pci_dev; + +struct telem_header { + u8 access_type; + u16 size; + u32 guid; + u32 base_offset; +}; + +struct telem_endpoint_info { + struct pci_dev *pdev; + struct telem_header header; +}; + +/** + * pmt_telem_get_next_endpoint() - Get next device id for a telemetry endpoint + * @start: starting devid to look from + * + * This functions can be used in a while loop predicate to retrieve the devid + * of all available telemetry endpoints. Functions pmt_telem_get_next_endpoint() + * and pmt_telem_register_endpoint() can be used inside of the loop to examine + * endpoint info and register to receive a pointer to the endpoint. The pointer + * is then usable in the telemetry read calls to access the telemetry data. + * + * Return: + * * devid - devid of the next present endpoint from start + * * 0 - when no more endpoints are present after start + */ +unsigned long pmt_telem_get_next_endpoint(unsigned long start); + +/** + * pmt_telem_register_endpoint() - Register a telemetry endpoint + * @devid: device id/handle of the telemetry endpoint + * + * Increments the kref usage counter for the endpoint. + * + * Return: + * * endpoint - On success returns pointer to the telemetry endpoint + * * -ENXIO - telemetry endpoint not found + */ +struct telem_endpoint *pmt_telem_register_endpoint(int devid); + +/** + * pmt_telem_unregister_endpoint() - Unregister a telemetry endpoint + * @ep: ep structure to populate. + * + * Decrements the kref usage counter for the endpoint. + */ +void pmt_telem_unregister_endpoint(struct telem_endpoint *ep); + +/** + * pmt_telem_get_endpoint_info() - Get info for an endpoint from its devid + * @devid: device id/handle of the telemetry endpoint + * @info: Endpoint info structure to be populated + * + * Return: + * * 0 - Success + * * -ENXIO - telemetry endpoint not found for the devid + * * -EINVAL - @info is NULL + */ +int pmt_telem_get_endpoint_info(int devid, struct telem_endpoint_info *info); + +/** + * pmt_telem_find_and_register_endpoint() - Get a telemetry endpoint from + * pci_dev device, guid and pos + * @pdev: PCI device inside the Intel vsec + * @guid: GUID of the telemetry space + * @pos: Instance of the guid + * + * Return: + * * endpoint - On success returns pointer to the telemetry endpoint + * * -ENXIO - telemetry endpoint not found + */ +struct telem_endpoint *pmt_telem_find_and_register_endpoint(struct pci_dev *pcidev, + u32 guid, u16 pos); + +/** + * pmt_telem_read() - Read qwords from counter sram using sample id + * @ep: Telemetry endpoint to be read + * @id: The beginning sample id of the metric(s) to be read + * @data: Allocated qword buffer + * @count: Number of qwords requested + * + * Callers must ensure reads are aligned. When the call returns -ENODEV, + * the device has been removed and callers should unregister the telemetry + * endpoint. + * + * Return: + * * 0 - Success + * * -ENODEV - The device is not present. + * * -EINVAL - The offset is out bounds + * * -EPIPE - The device was removed during the read. Data written + * but should be considered invalid. + */ +int pmt_telem_read(struct telem_endpoint *ep, u32 id, u64 *data, u32 count); + +/** + * pmt_telem_read32() - Read qwords from counter sram using sample id + * @ep: Telemetry endpoint to be read + * @id: The beginning sample id of the metric(s) to be read + * @data: Allocated dword buffer + * @count: Number of dwords requested + * + * Callers must ensure reads are aligned. When the call returns -ENODEV, + * the device has been removed and callers should unregister the telemetry + * endpoint. + * + * Return: + * * 0 - Success + * * -ENODEV - The device is not present. + * * -EINVAL - The offset is out bounds + * * -EPIPE - The device was removed during the read. Data written + * but should be considered invalid. + */ +int pmt_telem_read32(struct telem_endpoint *ep, u32 id, u32 *data, u32 count); + +#endif -- cgit From 2e35e3aa9f10ea430468207c3dd9dc33ba1afc33 Mon Sep 17 00:00:00 2001 From: Xi Pardee Date: Wed, 29 Nov 2023 14:21:23 -0800 Subject: platform/x86:intel/pmc: Call pmc_get_low_power_modes from platform init MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In order to setup a table of low power mode requirements for Meteor Lake, pmc_core_get_low_power_modes() will need to be run from platform init code so that the enabled modes are known, allowing the use of the pmc_for_each_mode helper. Make the function global and call it from the platform init code. Signed-off-by: Xi Pardee Signed-off-by: David E. Box Reviewed-by: Ilpo Järvinen Link: https://lore.kernel.org/r/20231129222132.2331261-12-david.e.box@linux.intel.com Signed-off-by: Hans de Goede --- drivers/platform/x86/intel/pmc/adl.c | 2 ++ drivers/platform/x86/intel/pmc/cnp.c | 2 ++ drivers/platform/x86/intel/pmc/core.c | 7 +++---- drivers/platform/x86/intel/pmc/core.h | 1 + drivers/platform/x86/intel/pmc/icl.c | 10 +++++++++- drivers/platform/x86/intel/pmc/mtl.c | 4 +++- drivers/platform/x86/intel/pmc/spt.c | 10 +++++++++- drivers/platform/x86/intel/pmc/tgl.c | 1 + 8 files changed, 30 insertions(+), 7 deletions(-) diff --git a/drivers/platform/x86/intel/pmc/adl.c b/drivers/platform/x86/intel/pmc/adl.c index 5006008e01be..64c492391ede 100644 --- a/drivers/platform/x86/intel/pmc/adl.c +++ b/drivers/platform/x86/intel/pmc/adl.c @@ -319,6 +319,8 @@ int adl_core_init(struct pmc_dev *pmcdev) if (ret) return ret; + pmc_core_get_low_power_modes(pmcdev); + /* Due to a hardware limitation, the GBE LTR blocks PC10 * when a cable is attached. Tell the PMC to ignore it. */ diff --git a/drivers/platform/x86/intel/pmc/cnp.c b/drivers/platform/x86/intel/pmc/cnp.c index 420aaa1d7c76..59298f184d0e 100644 --- a/drivers/platform/x86/intel/pmc/cnp.c +++ b/drivers/platform/x86/intel/pmc/cnp.c @@ -214,6 +214,8 @@ int cnp_core_init(struct pmc_dev *pmcdev) if (ret) return ret; + pmc_core_get_low_power_modes(pmcdev); + /* Due to a hardware limitation, the GBE LTR blocks PC10 * when a cable is attached. Tell the PMC to ignore it. */ diff --git a/drivers/platform/x86/intel/pmc/core.c b/drivers/platform/x86/intel/pmc/core.c index 84c175b9721a..3894119d61b0 100644 --- a/drivers/platform/x86/intel/pmc/core.c +++ b/drivers/platform/x86/intel/pmc/core.c @@ -966,9 +966,8 @@ static bool pmc_core_pri_verify(u32 lpm_pri, u8 *mode_order) return true; } -static void pmc_core_get_low_power_modes(struct platform_device *pdev) +void pmc_core_get_low_power_modes(struct pmc_dev *pmcdev) { - struct pmc_dev *pmcdev = platform_get_drvdata(pdev); struct pmc *pmc = pmcdev->pmcs[PMC_IDX_MAIN]; u8 pri_order[LPM_MAX_NUM_MODES] = LPM_DEFAULT_PRI; u8 mode_order[LPM_MAX_NUM_MODES]; @@ -1000,7 +999,8 @@ static void pmc_core_get_low_power_modes(struct platform_device *pdev) for (mode = 0; mode < LPM_MAX_NUM_MODES; mode++) pri_order[mode_order[mode]] = mode; else - dev_warn(&pdev->dev, "Assuming a default substate order for this platform\n"); + dev_warn(&pmcdev->pdev->dev, + "Assuming a default substate order for this platform\n"); /* * Loop through all modes from lowest to highest priority, @@ -1250,7 +1250,6 @@ static int pmc_core_probe(struct platform_device *pdev) } pmcdev->pmc_xram_read_bit = pmc_core_check_read_lock_bit(primary_pmc); - pmc_core_get_low_power_modes(pdev); pmc_core_do_dmi_quirks(primary_pmc); pmc_core_dbgfs_register(pmcdev); diff --git a/drivers/platform/x86/intel/pmc/core.h b/drivers/platform/x86/intel/pmc/core.h index 0729f593c6a7..ccf24e0f5e50 100644 --- a/drivers/platform/x86/intel/pmc/core.h +++ b/drivers/platform/x86/intel/pmc/core.h @@ -490,6 +490,7 @@ extern int pmc_core_send_ltr_ignore(struct pmc_dev *pmcdev, u32 value); int pmc_core_resume_common(struct pmc_dev *pmcdev); int get_primary_reg_base(struct pmc *pmc); +extern void pmc_core_get_low_power_modes(struct pmc_dev *pmcdev); extern void pmc_core_ssram_init(struct pmc_dev *pmcdev); diff --git a/drivers/platform/x86/intel/pmc/icl.c b/drivers/platform/x86/intel/pmc/icl.c index d08e3174230d..71b0fd6cb7d8 100644 --- a/drivers/platform/x86/intel/pmc/icl.c +++ b/drivers/platform/x86/intel/pmc/icl.c @@ -53,7 +53,15 @@ const struct pmc_reg_map icl_reg_map = { int icl_core_init(struct pmc_dev *pmcdev) { struct pmc *pmc = pmcdev->pmcs[PMC_IDX_MAIN]; + int ret; pmc->map = &icl_reg_map; - return get_primary_reg_base(pmc); + + ret = get_primary_reg_base(pmc); + if (ret) + return ret; + + pmc_core_get_low_power_modes(pmcdev); + + return ret; } diff --git a/drivers/platform/x86/intel/pmc/mtl.c b/drivers/platform/x86/intel/pmc/mtl.c index 2204bc666980..c3b5f4fe01d1 100644 --- a/drivers/platform/x86/intel/pmc/mtl.c +++ b/drivers/platform/x86/intel/pmc/mtl.c @@ -985,7 +985,7 @@ static int mtl_resume(struct pmc_dev *pmcdev) int mtl_core_init(struct pmc_dev *pmcdev) { struct pmc *pmc = pmcdev->pmcs[PMC_IDX_SOC]; - int ret = 0; + int ret; mtl_d3_fixup(); @@ -1002,6 +1002,8 @@ int mtl_core_init(struct pmc_dev *pmcdev) return ret; } + pmc_core_get_low_power_modes(pmcdev); + /* Due to a hardware limitation, the GBE LTR blocks PC10 * when a cable is attached. Tell the PMC to ignore it. */ diff --git a/drivers/platform/x86/intel/pmc/spt.c b/drivers/platform/x86/intel/pmc/spt.c index 4b6f5cbda16c..ab993a69e33e 100644 --- a/drivers/platform/x86/intel/pmc/spt.c +++ b/drivers/platform/x86/intel/pmc/spt.c @@ -137,7 +137,15 @@ const struct pmc_reg_map spt_reg_map = { int spt_core_init(struct pmc_dev *pmcdev) { struct pmc *pmc = pmcdev->pmcs[PMC_IDX_MAIN]; + int ret; pmc->map = &spt_reg_map; - return get_primary_reg_base(pmc); + + ret = get_primary_reg_base(pmc); + if (ret) + return ret; + + pmc_core_get_low_power_modes(pmcdev); + + return ret; } diff --git a/drivers/platform/x86/intel/pmc/tgl.c b/drivers/platform/x86/intel/pmc/tgl.c index 2449940102db..d5f1d2223c5a 100644 --- a/drivers/platform/x86/intel/pmc/tgl.c +++ b/drivers/platform/x86/intel/pmc/tgl.c @@ -263,6 +263,7 @@ int tgl_core_init(struct pmc_dev *pmcdev) if (ret) return ret; + pmc_core_get_low_power_modes(pmcdev); pmc_core_get_tgl_lpm_reqs(pmcdev->pdev); /* Due to a hardware limitation, the GBE LTR blocks PC10 * when a cable is attached. Tell the PMC to ignore it. -- cgit From 9512920a6be57af191ab2849b3ec393b8e92530a Mon Sep 17 00:00:00 2001 From: "David E. Box" Date: Wed, 29 Nov 2023 14:21:24 -0800 Subject: platform/x86/intel/pmc: Allow pmc_core_ssram_init to fail MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Currently, if the PMC SSRAM initialization fails, no error is returned and the only indication is that a PMC device has not been created. Instead, allow an error to be returned and handled directly by the caller. Signed-off-by: David E. Box Reviewed-by: Ilpo Järvinen Link: https://lore.kernel.org/r/20231129222132.2331261-13-david.e.box@linux.intel.com Signed-off-by: Hans de Goede --- drivers/platform/x86/intel/pmc/core.h | 2 +- drivers/platform/x86/intel/pmc/core_ssram.c | 21 +++++++++++++-------- 2 files changed, 14 insertions(+), 9 deletions(-) diff --git a/drivers/platform/x86/intel/pmc/core.h b/drivers/platform/x86/intel/pmc/core.h index ccf24e0f5e50..edaa70067e41 100644 --- a/drivers/platform/x86/intel/pmc/core.h +++ b/drivers/platform/x86/intel/pmc/core.h @@ -492,7 +492,7 @@ int pmc_core_resume_common(struct pmc_dev *pmcdev); int get_primary_reg_base(struct pmc *pmc); extern void pmc_core_get_low_power_modes(struct pmc_dev *pmcdev); -extern void pmc_core_ssram_init(struct pmc_dev *pmcdev); +extern int pmc_core_ssram_init(struct pmc_dev *pmcdev); int spt_core_init(struct pmc_dev *pmcdev); int cnp_core_init(struct pmc_dev *pmcdev); diff --git a/drivers/platform/x86/intel/pmc/core_ssram.c b/drivers/platform/x86/intel/pmc/core_ssram.c index 13fa16f0d52e..815950713e25 100644 --- a/drivers/platform/x86/intel/pmc/core_ssram.c +++ b/drivers/platform/x86/intel/pmc/core_ssram.c @@ -35,20 +35,20 @@ static inline u64 get_base(void __iomem *addr, u32 offset) return lo_hi_readq(addr + offset) & GENMASK_ULL(63, 3); } -static void +static int pmc_core_pmc_add(struct pmc_dev *pmcdev, u64 pwrm_base, const struct pmc_reg_map *reg_map, int pmc_index) { struct pmc *pmc = pmcdev->pmcs[pmc_index]; if (!pwrm_base) - return; + return -ENODEV; /* Memory for primary PMC has been allocated in core.c */ if (!pmc) { pmc = devm_kzalloc(&pmcdev->pdev->dev, sizeof(*pmc), GFP_KERNEL); if (!pmc) - return; + return -ENOMEM; } pmc->map = reg_map; @@ -57,10 +57,12 @@ pmc_core_pmc_add(struct pmc_dev *pmcdev, u64 pwrm_base, if (!pmc->regbase) { devm_kfree(&pmcdev->pdev->dev, pmc); - return; + return -ENOMEM; } pmcdev->pmcs[pmc_index] = pmc; + + return 0; } static void @@ -96,7 +98,7 @@ pmc_core_ssram_get_pmc(struct pmc_dev *pmcdev, void __iomem *ssram, u32 offset, iounmap(ssram); } -void pmc_core_ssram_init(struct pmc_dev *pmcdev) +int pmc_core_ssram_init(struct pmc_dev *pmcdev) { void __iomem *ssram; struct pci_dev *pcidev; @@ -105,7 +107,7 @@ void pmc_core_ssram_init(struct pmc_dev *pmcdev) pcidev = pci_get_domain_bus_and_slot(0, 0, PCI_DEVFN(20, 2)); if (!pcidev) - goto out; + return -ENODEV; ret = pcim_enable_device(pcidev); if (ret) @@ -123,11 +125,14 @@ void pmc_core_ssram_init(struct pmc_dev *pmcdev) pmc_core_ssram_get_pmc(pmcdev, ssram, SSRAM_PCH_OFFSET, PMC_IDX_PCH); iounmap(ssram); -out: - return; + + return 0; disable_dev: + pmcdev->ssram_pcidev = NULL; pci_disable_device(pcidev); release_dev: pci_dev_put(pcidev); + + return ret; } -- cgit From a01486dc4bb17de976c6d0a4b1ad5f8106525dfb Mon Sep 17 00:00:00 2001 From: "David E. Box" Date: Wed, 29 Nov 2023 14:21:25 -0800 Subject: platform/x86/intel/pmc: Cleanup SSRAM discovery MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Clean up the code handling SSRAM discovery. Handle all resource allocation and cleanup in pmc_core_ssram_get_pmc(). Return the error status from this function but only fail the init if we fail to discover the primary PMC. Signed-off-by: David E. Box Reviewed-by: Ilpo Järvinen Link: https://lore.kernel.org/r/20231129222132.2331261-14-david.e.box@linux.intel.com Signed-off-by: Hans de Goede --- drivers/platform/x86/intel/pmc/core_ssram.c | 62 +++++++++++++++-------------- 1 file changed, 33 insertions(+), 29 deletions(-) diff --git a/drivers/platform/x86/intel/pmc/core_ssram.c b/drivers/platform/x86/intel/pmc/core_ssram.c index 815950713e25..c1b984255571 100644 --- a/drivers/platform/x86/intel/pmc/core_ssram.c +++ b/drivers/platform/x86/intel/pmc/core_ssram.c @@ -8,6 +8,7 @@ * */ +#include #include #include @@ -21,6 +22,8 @@ #define SSRAM_IOE_OFFSET 0x68 #define SSRAM_DEVID_OFFSET 0x70 +DEFINE_FREE(pmc_core_iounmap, void __iomem *, iounmap(_T)); + static const struct pmc_reg_map *pmc_core_find_regmap(struct pmc_info *list, u16 devid) { for (; list->map; ++list) @@ -65,44 +68,49 @@ pmc_core_pmc_add(struct pmc_dev *pmcdev, u64 pwrm_base, return 0; } -static void -pmc_core_ssram_get_pmc(struct pmc_dev *pmcdev, void __iomem *ssram, u32 offset, - int pmc_idx) +static int +pmc_core_ssram_get_pmc(struct pmc_dev *pmcdev, int pmc_idx, u32 offset) { - u64 pwrm_base; + struct pci_dev *ssram_pcidev = pmcdev->ssram_pcidev; + void __iomem __free(pmc_core_iounmap) *tmp_ssram = NULL; + void __iomem __free(pmc_core_iounmap) *ssram = NULL; + const struct pmc_reg_map *map; + u64 ssram_base, pwrm_base; u16 devid; - if (pmc_idx != PMC_IDX_SOC) { - u64 ssram_base = get_base(ssram, offset); + if (!pmcdev->regmap_list) + return -ENOENT; - if (!ssram_base) - return; + ssram_base = ssram_pcidev->resource[0].start; + tmp_ssram = ioremap(ssram_base, SSRAM_HDR_SIZE); + if (pmc_idx != PMC_IDX_MAIN) { + /* + * The secondary PMC BARS (which are behind hidden PCI devices) + * are read from fixed offsets in MMIO of the primary PMC BAR. + */ + ssram_base = get_base(tmp_ssram, offset); ssram = ioremap(ssram_base, SSRAM_HDR_SIZE); if (!ssram) - return; + return -ENOMEM; + + } else { + ssram = no_free_ptr(tmp_ssram); } pwrm_base = get_base(ssram, SSRAM_PWRM_OFFSET); devid = readw(ssram + SSRAM_DEVID_OFFSET); - if (pmcdev->regmap_list) { - const struct pmc_reg_map *map; - - map = pmc_core_find_regmap(pmcdev->regmap_list, devid); - if (map) - pmc_core_pmc_add(pmcdev, pwrm_base, map, pmc_idx); - } + map = pmc_core_find_regmap(pmcdev->regmap_list, devid); + if (!map) + return -ENODEV; - if (pmc_idx != PMC_IDX_SOC) - iounmap(ssram); + return pmc_core_pmc_add(pmcdev, pwrm_base, map, PMC_IDX_MAIN); } int pmc_core_ssram_init(struct pmc_dev *pmcdev) { - void __iomem *ssram; struct pci_dev *pcidev; - u64 ssram_base; int ret; pcidev = pci_get_domain_bus_and_slot(0, 0, PCI_DEVFN(20, 2)); @@ -113,18 +121,14 @@ int pmc_core_ssram_init(struct pmc_dev *pmcdev) if (ret) goto release_dev; - ssram_base = pcidev->resource[0].start; - ssram = ioremap(ssram_base, SSRAM_HDR_SIZE); - if (!ssram) - goto disable_dev; - pmcdev->ssram_pcidev = pcidev; - pmc_core_ssram_get_pmc(pmcdev, ssram, 0, PMC_IDX_SOC); - pmc_core_ssram_get_pmc(pmcdev, ssram, SSRAM_IOE_OFFSET, PMC_IDX_IOE); - pmc_core_ssram_get_pmc(pmcdev, ssram, SSRAM_PCH_OFFSET, PMC_IDX_PCH); + ret = pmc_core_ssram_get_pmc(pmcdev, PMC_IDX_MAIN, 0); + if (ret) + goto disable_dev; - iounmap(ssram); + pmc_core_ssram_get_pmc(pmcdev, PMC_IDX_IOE, SSRAM_IOE_OFFSET); + pmc_core_ssram_get_pmc(pmcdev, PMC_IDX_PCH, SSRAM_PCH_OFFSET); return 0; -- cgit From 642dd26f58d91f4bb2e2fcaaf178bbc35369b73a Mon Sep 17 00:00:00 2001 From: "David E. Box" Date: Wed, 29 Nov 2023 14:21:26 -0800 Subject: platform/x86/intel/pmc/mtl: Use return value from pmc_core_ssram_init() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Instead of checking for a NULL regbase, use the return value from pmc_core_ssram_init() to check if PMC discovery was successful. If not, use the legacy enumeration method (which only works for the primary PMC). Signed-off-by: David E. Box Reviewed-by: Ilpo Järvinen Link: https://lore.kernel.org/r/20231129222132.2331261-15-david.e.box@linux.intel.com Signed-off-by: Hans de Goede --- drivers/platform/x86/intel/pmc/mtl.c | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/drivers/platform/x86/intel/pmc/mtl.c b/drivers/platform/x86/intel/pmc/mtl.c index c3b5f4fe01d1..d1d3d33fb4b8 100644 --- a/drivers/platform/x86/intel/pmc/mtl.c +++ b/drivers/platform/x86/intel/pmc/mtl.c @@ -990,12 +990,16 @@ int mtl_core_init(struct pmc_dev *pmcdev) mtl_d3_fixup(); pmcdev->resume = mtl_resume; - pmcdev->regmap_list = mtl_pmc_info_list; - pmc_core_ssram_init(pmcdev); - /* If regbase not assigned, set map and discover using legacy method */ - if (!pmc->regbase) { + /* + * If ssram init fails use legacy method to at least get the + * primary PMC + */ + ret = pmc_core_ssram_init(pmcdev); + if (ret) { + dev_warn(&pmcdev->pdev->dev, + "ssram init failed, %d, using legacy init\n", ret); pmc->map = &mtl_socm_reg_map; ret = get_primary_reg_base(pmc); if (ret) -- cgit From 104f74943f4830f3a65fb96565b89014c882db85 Mon Sep 17 00:00:00 2001 From: "David E. Box" Date: Wed, 29 Nov 2023 14:21:27 -0800 Subject: platform/x86/intel/pmc: Find and register PMC telemetry entries MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The PMC SSRAM device contains counters that are structured in Intel Platform Monitoring Technology (PMT) telemetry regions. Look for and register these telemetry regions from the driver so that they may be read using the Intel PMT ABI. Signed-off-by: David E. Box Reviewed-by: Ilpo Järvinen Link: https://lore.kernel.org/r/20231129222132.2331261-16-david.e.box@linux.intel.com Signed-off-by: Hans de Goede --- drivers/platform/x86/intel/pmc/Kconfig | 1 + drivers/platform/x86/intel/pmc/core_ssram.c | 49 +++++++++++++++++++++++++++++ 2 files changed, 50 insertions(+) diff --git a/drivers/platform/x86/intel/pmc/Kconfig b/drivers/platform/x86/intel/pmc/Kconfig index b526597e4deb..d2f651fbec2c 100644 --- a/drivers/platform/x86/intel/pmc/Kconfig +++ b/drivers/platform/x86/intel/pmc/Kconfig @@ -7,6 +7,7 @@ config INTEL_PMC_CORE tristate "Intel PMC Core driver" depends on PCI depends on ACPI + depends on INTEL_PMT_TELEMETRY help The Intel Platform Controller Hub for Intel Core SoCs provides access to Power Management Controller registers via various interfaces. This diff --git a/drivers/platform/x86/intel/pmc/core_ssram.c b/drivers/platform/x86/intel/pmc/core_ssram.c index c1b984255571..9ca720f9cbb2 100644 --- a/drivers/platform/x86/intel/pmc/core_ssram.c +++ b/drivers/platform/x86/intel/pmc/core_ssram.c @@ -13,6 +13,8 @@ #include #include "core.h" +#include "../vsec.h" +#include "../pmt/telemetry.h" #define SSRAM_HDR_SIZE 0x100 #define SSRAM_PWRM_OFFSET 0x14 @@ -24,6 +26,49 @@ DEFINE_FREE(pmc_core_iounmap, void __iomem *, iounmap(_T)); +static void +pmc_add_pmt(struct pmc_dev *pmcdev, u64 ssram_base, void __iomem *ssram) +{ + struct pci_dev *pcidev = pmcdev->ssram_pcidev; + struct intel_vsec_platform_info info = {}; + struct intel_vsec_header *headers[2] = {}; + struct intel_vsec_header header; + void __iomem *dvsec; + u32 dvsec_offset; + u32 table, hdr; + + ssram = ioremap(ssram_base, SSRAM_HDR_SIZE); + if (!ssram) + return; + + dvsec_offset = readl(ssram + SSRAM_DVSEC_OFFSET); + iounmap(ssram); + + dvsec = ioremap(ssram_base + dvsec_offset, SSRAM_DVSEC_SIZE); + if (!dvsec) + return; + + hdr = readl(dvsec + PCI_DVSEC_HEADER1); + header.id = readw(dvsec + PCI_DVSEC_HEADER2); + header.rev = PCI_DVSEC_HEADER1_REV(hdr); + header.length = PCI_DVSEC_HEADER1_LEN(hdr); + header.num_entries = readb(dvsec + INTEL_DVSEC_ENTRIES); + header.entry_size = readb(dvsec + INTEL_DVSEC_SIZE); + + table = readl(dvsec + INTEL_DVSEC_TABLE); + header.tbir = INTEL_DVSEC_TABLE_BAR(table); + header.offset = INTEL_DVSEC_TABLE_OFFSET(table); + iounmap(dvsec); + + headers[0] = &header; + info.caps = VSEC_CAP_TELEMETRY; + info.headers = headers; + info.base_addr = ssram_base; + info.parent = &pmcdev->pdev->dev; + + intel_vsec_register(pcidev, &info); +} + static const struct pmc_reg_map *pmc_core_find_regmap(struct pmc_info *list, u16 devid) { for (; list->map; ++list) @@ -101,6 +146,9 @@ pmc_core_ssram_get_pmc(struct pmc_dev *pmcdev, int pmc_idx, u32 offset) pwrm_base = get_base(ssram, SSRAM_PWRM_OFFSET); devid = readw(ssram + SSRAM_DEVID_OFFSET); + /* Find and register and PMC telemetry entries */ + pmc_add_pmt(pmcdev, ssram_base, ssram); + map = pmc_core_find_regmap(pmcdev->regmap_list, devid); if (!map) return -ENODEV; @@ -140,3 +188,4 @@ release_dev: return ret; } +MODULE_IMPORT_NS(INTEL_VSEC); -- cgit From 0f601dec1856d675a1251e25e858d8f1cb0b8026 Mon Sep 17 00:00:00 2001 From: Rajvi Jingar Date: Wed, 29 Nov 2023 14:21:28 -0800 Subject: platform/x86/intel/pmc: Display LPM requirements for multiple PMCs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update the substate_requirements attribute to display the requirements for all the PMCs on a package. Signed-off-by: Rajvi Jingar Signed-off-by: David E. Box Reviewed-by: Ilpo Järvinen Link: https://lore.kernel.org/r/20231129222132.2331261-17-david.e.box@linux.intel.com Signed-off-by: Hans de Goede --- drivers/platform/x86/intel/pmc/core.c | 123 +++++++++++++++++++--------------- 1 file changed, 68 insertions(+), 55 deletions(-) diff --git a/drivers/platform/x86/intel/pmc/core.c b/drivers/platform/x86/intel/pmc/core.c index 3894119d61b0..9d3a1b6ef622 100644 --- a/drivers/platform/x86/intel/pmc/core.c +++ b/drivers/platform/x86/intel/pmc/core.c @@ -728,7 +728,7 @@ static int pmc_core_substate_l_sts_regs_show(struct seq_file *s, void *unused) } DEFINE_SHOW_ATTRIBUTE(pmc_core_substate_l_sts_regs); -static void pmc_core_substate_req_header_show(struct seq_file *s) +static void pmc_core_substate_req_header_show(struct seq_file *s, int pmc_index) { struct pmc_dev *pmcdev = s->private; int i, mode; @@ -743,68 +743,81 @@ static void pmc_core_substate_req_header_show(struct seq_file *s) static int pmc_core_substate_req_regs_show(struct seq_file *s, void *unused) { struct pmc_dev *pmcdev = s->private; - struct pmc *pmc = pmcdev->pmcs[PMC_IDX_MAIN]; - const struct pmc_bit_map **maps = pmc->map->lpm_sts; - const struct pmc_bit_map *map; - const int num_maps = pmc->map->lpm_num_maps; - u32 sts_offset = pmc->map->lpm_status_offset; - u32 *lpm_req_regs = pmc->lpm_req_regs; - int mp; + u32 sts_offset; + u32 *lpm_req_regs; + int num_maps, mp, pmc_index; - /* Display the header */ - pmc_core_substate_req_header_show(s); + for (pmc_index = 0; pmc_index < ARRAY_SIZE(pmcdev->pmcs); ++pmc_index) { + struct pmc *pmc = pmcdev->pmcs[pmc_index]; + const struct pmc_bit_map **maps; - /* Loop over maps */ - for (mp = 0; mp < num_maps; mp++) { - u32 req_mask = 0; - u32 lpm_status; - int mode, idx, i, len = 32; + if (!pmc) + continue; + + maps = pmc->map->lpm_sts; + num_maps = pmc->map->lpm_num_maps; + sts_offset = pmc->map->lpm_status_offset; + lpm_req_regs = pmc->lpm_req_regs; /* - * Capture the requirements and create a mask so that we only - * show an element if it's required for at least one of the - * enabled low power modes + * When there are multiple PMCs, though the PMC may exist, the + * requirement register discovery could have failed so check + * before accessing. */ - pmc_for_each_mode(idx, mode, pmcdev) - req_mask |= lpm_req_regs[mp + (mode * num_maps)]; - - /* Get the last latched status for this map */ - lpm_status = pmc_core_reg_read(pmc, sts_offset + (mp * 4)); - - /* Loop over elements in this map */ - map = maps[mp]; - for (i = 0; map[i].name && i < len; i++) { - u32 bit_mask = map[i].bit_mask; - - if (!(bit_mask & req_mask)) - /* - * Not required for any enabled states - * so don't display - */ - continue; - - /* Display the element name in the first column */ - seq_printf(s, "%30s |", map[i].name); - - /* Loop over the enabled states and display if required */ - pmc_for_each_mode(idx, mode, pmcdev) { - if (lpm_req_regs[mp + (mode * num_maps)] & bit_mask) - seq_printf(s, " %9s |", - "Required"); - else - seq_printf(s, " %9s |", " "); - } - - /* In Status column, show the last captured state of this agent */ - if (lpm_status & bit_mask) - seq_printf(s, " %9s |", "Yes"); - else - seq_printf(s, " %9s |", " "); + if (!lpm_req_regs) + continue; - seq_puts(s, "\n"); + /* Display the header */ + pmc_core_substate_req_header_show(s, pmc_index); + + /* Loop over maps */ + for (mp = 0; mp < num_maps; mp++) { + u32 req_mask = 0; + u32 lpm_status; + const struct pmc_bit_map *map; + int mode, idx, i, len = 32; + + /* + * Capture the requirements and create a mask so that we only + * show an element if it's required for at least one of the + * enabled low power modes + */ + pmc_for_each_mode(idx, mode, pmcdev) + req_mask |= lpm_req_regs[mp + (mode * num_maps)]; + + /* Get the last latched status for this map */ + lpm_status = pmc_core_reg_read(pmc, sts_offset + (mp * 4)); + + /* Loop over elements in this map */ + map = maps[mp]; + for (i = 0; map[i].name && i < len; i++) { + u32 bit_mask = map[i].bit_mask; + + if (!(bit_mask & req_mask)) { + /* + * Not required for any enabled states + * so don't display + */ + continue; + } + + /* Display the element name in the first column */ + seq_printf(s, "pmc%d: %26s |", pmc_index, map[i].name); + + /* Loop over the enabled states and display if required */ + pmc_for_each_mode(idx, mode, pmcdev) { + bool required = lpm_req_regs[mp + (mode * num_maps)] & + bit_mask; + seq_printf(s, " %9s |", required ? "Required" : " "); + } + + /* In Status column, show the last captured state of this agent */ + seq_printf(s, " %9s |", lpm_status & bit_mask ? "Yes" : " "); + + seq_puts(s, "\n"); + } } } - return 0; } DEFINE_SHOW_ATTRIBUTE(pmc_core_substate_req_regs); -- cgit From 4d621c3f02ba71cb8ed48b7c32ecb0910000cc28 Mon Sep 17 00:00:00 2001 From: Xi Pardee Date: Wed, 29 Nov 2023 14:21:29 -0800 Subject: platform/x86/intel/pmc: Retrieve LPM information using Intel PMT MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit On supported platforms, the low power mode (LPM) requirements for entering each idle substate are described in Platform Monitoring Technology (PMT) telemetry entries. Provide a function for platform code to attempt to find and read the requirements from the telemetry entries. Signed-off-by: Xi Pardee Signed-off-by: David E. Box Reviewed-by: Ilpo Järvinen Link: https://lore.kernel.org/r/20231129222132.2331261-18-david.e.box@linux.intel.com Signed-off-by: Hans de Goede --- drivers/platform/x86/intel/pmc/core.h | 3 + drivers/platform/x86/intel/pmc/core_ssram.c | 135 ++++++++++++++++++++++++++++ 2 files changed, 138 insertions(+) diff --git a/drivers/platform/x86/intel/pmc/core.h b/drivers/platform/x86/intel/pmc/core.h index edaa70067e41..85b6f6ae4995 100644 --- a/drivers/platform/x86/intel/pmc/core.h +++ b/drivers/platform/x86/intel/pmc/core.h @@ -320,6 +320,7 @@ struct pmc_reg_map { const u32 lpm_status_offset; const u32 lpm_live_status_offset; const u32 etr3_offset; + const u8 *lpm_reg_index; }; /** @@ -329,6 +330,7 @@ struct pmc_reg_map { * specific attributes */ struct pmc_info { + u32 guid; u16 devid; const struct pmc_reg_map *map; }; @@ -486,6 +488,7 @@ extern const struct pmc_bit_map *mtl_ioem_lpm_maps[]; extern const struct pmc_reg_map mtl_ioem_reg_map; extern void pmc_core_get_tgl_lpm_reqs(struct platform_device *pdev); +extern int pmc_core_ssram_get_lpm_reqs(struct pmc_dev *pmcdev); extern int pmc_core_send_ltr_ignore(struct pmc_dev *pmcdev, u32 value); int pmc_core_resume_common(struct pmc_dev *pmcdev); diff --git a/drivers/platform/x86/intel/pmc/core_ssram.c b/drivers/platform/x86/intel/pmc/core_ssram.c index 9ca720f9cbb2..3501c7bd6b33 100644 --- a/drivers/platform/x86/intel/pmc/core_ssram.c +++ b/drivers/platform/x86/intel/pmc/core_ssram.c @@ -24,8 +24,142 @@ #define SSRAM_IOE_OFFSET 0x68 #define SSRAM_DEVID_OFFSET 0x70 +/* PCH query */ +#define LPM_HEADER_OFFSET 1 +#define LPM_REG_COUNT 28 +#define LPM_MODE_OFFSET 1 + DEFINE_FREE(pmc_core_iounmap, void __iomem *, iounmap(_T)); +static u32 pmc_core_find_guid(struct pmc_info *list, const struct pmc_reg_map *map) +{ + for (; list->map; ++list) + if (list->map == map) + return list->guid; + + return 0; +} + +static int pmc_core_get_lpm_req(struct pmc_dev *pmcdev, struct pmc *pmc) +{ + struct telem_endpoint *ep; + const u8 *lpm_indices; + int num_maps, mode_offset = 0; + int ret, mode, i; + int lpm_size; + u32 guid; + + lpm_indices = pmc->map->lpm_reg_index; + num_maps = pmc->map->lpm_num_maps; + lpm_size = LPM_MAX_NUM_MODES * num_maps; + + guid = pmc_core_find_guid(pmcdev->regmap_list, pmc->map); + if (!guid) + return -ENXIO; + + ep = pmt_telem_find_and_register_endpoint(pmcdev->ssram_pcidev, guid, 0); + if (IS_ERR(ep)) { + dev_dbg(&pmcdev->pdev->dev, "couldn't get telem endpoint %ld", + PTR_ERR(ep)); + return -EPROBE_DEFER; + } + + pmc->lpm_req_regs = devm_kzalloc(&pmcdev->pdev->dev, + lpm_size * sizeof(u32), + GFP_KERNEL); + if (!pmc->lpm_req_regs) { + ret = -ENOMEM; + goto unregister_ep; + } + + /* + * PMC Low Power Mode (LPM) table + * + * In telemetry space, the LPM table contains a 4 byte header followed + * by 8 consecutive mode blocks (one for each LPM mode). Each block + * has a 4 byte header followed by a set of registers that describe the + * IP state requirements for the given mode. The IP mapping is platform + * specific but the same for each block, making for easy analysis. + * Platforms only use a subset of the space to track the requirements + * for their IPs. Callers provide the requirement registers they use as + * a list of indices. Each requirement register is associated with an + * IP map that's maintained by the caller. + * + * Header + * +----+----------------------------+----------------------------+ + * | 0 | REVISION | ENABLED MODES | + * +----+--------------+-------------+-------------+--------------+ + * + * Low Power Mode 0 Block + * +----+--------------+-------------+-------------+--------------+ + * | 1 | SUB ID | SIZE | MAJOR | MINOR | + * +----+--------------+-------------+-------------+--------------+ + * | 2 | LPM0 Requirements 0 | + * +----+---------------------------------------------------------+ + * | | ... | + * +----+---------------------------------------------------------+ + * | 29 | LPM0 Requirements 27 | + * +----+---------------------------------------------------------+ + * + * ... + * + * Low Power Mode 7 Block + * +----+--------------+-------------+-------------+--------------+ + * | | SUB ID | SIZE | MAJOR | MINOR | + * +----+--------------+-------------+-------------+--------------+ + * | 60 | LPM7 Requirements 0 | + * +----+---------------------------------------------------------+ + * | | ... | + * +----+---------------------------------------------------------+ + * | 87 | LPM7 Requirements 27 | + * +----+---------------------------------------------------------+ + * + */ + mode_offset = LPM_HEADER_OFFSET + LPM_MODE_OFFSET; + pmc_for_each_mode(i, mode, pmcdev) { + u32 *req_offset = pmc->lpm_req_regs + (mode * num_maps); + int m; + + for (m = 0; m < num_maps; m++) { + u8 sample_id = lpm_indices[m] + mode_offset; + + ret = pmt_telem_read32(ep, sample_id, req_offset, 1); + if (ret) { + dev_err(&pmcdev->pdev->dev, + "couldn't read Low Power Mode requirements: %d\n", ret); + devm_kfree(&pmcdev->pdev->dev, pmc->lpm_req_regs); + goto unregister_ep; + } + ++req_offset; + } + mode_offset += LPM_REG_COUNT + LPM_MODE_OFFSET; + } + +unregister_ep: + pmt_telem_unregister_endpoint(ep); + + return ret; +} + +int pmc_core_ssram_get_lpm_reqs(struct pmc_dev *pmcdev) +{ + int ret, i; + + if (!pmcdev->ssram_pcidev) + return -ENODEV; + + for (i = 0; i < ARRAY_SIZE(pmcdev->pmcs); ++i) { + if (!pmcdev->pmcs[i]) + continue; + + ret = pmc_core_get_lpm_req(pmcdev, pmcdev->pmcs[i]); + if (ret) + return ret; + } + + return 0; +} + static void pmc_add_pmt(struct pmc_dev *pmcdev, u64 ssram_base, void __iomem *ssram) { @@ -189,3 +323,4 @@ release_dev: return ret; } MODULE_IMPORT_NS(INTEL_VSEC); +MODULE_IMPORT_NS(INTEL_PMT_TELEMETRY); -- cgit From 935b8211a31a52c82150b2b83c8428859393860d Mon Sep 17 00:00:00 2001 From: Xi Pardee Date: Wed, 29 Nov 2023 14:21:30 -0800 Subject: platform/x86/intel/pmc: Read low power mode requirements for MTL-M and MTL-P MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add support to read the low power mode requirements for Meteor Lake M and Meteor Lake P. Signed-off-by: Xi Pardee Signed-off-by: David E. Box Reviewed-by: Ilpo Järvinen Link: https://lore.kernel.org/r/20231129222132.2331261-19-david.e.box@linux.intel.com Signed-off-by: Hans de Goede --- drivers/platform/x86/intel/pmc/mtl.c | 39 +++++++++++++++++++++++++++++------- 1 file changed, 32 insertions(+), 7 deletions(-) diff --git a/drivers/platform/x86/intel/pmc/mtl.c b/drivers/platform/x86/intel/pmc/mtl.c index d1d3d33fb4b8..7ceeae507f4c 100644 --- a/drivers/platform/x86/intel/pmc/mtl.c +++ b/drivers/platform/x86/intel/pmc/mtl.c @@ -11,6 +11,13 @@ #include #include "core.h" +/* PMC SSRAM PMT Telemetry GUIDS */ +#define SOCP_LPM_REQ_GUID 0x2625030 +#define IOEM_LPM_REQ_GUID 0x4357464 +#define IOEP_LPM_REQ_GUID 0x5077612 + +static const u8 MTL_LPM_REG_INDEX[] = {0, 4, 5, 6, 8, 9, 10, 11, 12, 13, 14, 15, 16, 20}; + /* * Die Mapping to Product. * Product SOCDie IOEDie PCHDie @@ -465,6 +472,7 @@ const struct pmc_reg_map mtl_socm_reg_map = { .lpm_sts = mtl_socm_lpm_maps, .lpm_status_offset = MTL_LPM_STATUS_OFFSET, .lpm_live_status_offset = MTL_LPM_LIVE_STATUS_OFFSET, + .lpm_reg_index = MTL_LPM_REG_INDEX, }; const struct pmc_bit_map mtl_ioep_pfear_map[] = { @@ -782,6 +790,13 @@ const struct pmc_reg_map mtl_ioep_reg_map = { .ltr_show_sts = mtl_ioep_ltr_show_map, .ltr_ignore_offset = CNP_PMC_LTR_IGNORE_OFFSET, .ltr_ignore_max = ADL_NUM_IP_IGN_ALLOWED, + .lpm_num_maps = ADL_LPM_NUM_MAPS, + .lpm_res_counter_step_x2 = TGL_PMC_LPM_RES_COUNTER_STEP_X2, + .lpm_residency_offset = MTL_LPM_RESIDENCY_OFFSET, + .lpm_priority_offset = MTL_LPM_PRI_OFFSET, + .lpm_en_offset = MTL_LPM_EN_OFFSET, + .lpm_sts_latch_en_offset = MTL_LPM_STATUS_LATCH_EN_OFFSET, + .lpm_reg_index = MTL_LPM_REG_INDEX, }; const struct pmc_bit_map mtl_ioem_pfear_map[] = { @@ -922,6 +937,13 @@ const struct pmc_reg_map mtl_ioem_reg_map = { .ltr_show_sts = mtl_ioep_ltr_show_map, .ltr_ignore_offset = CNP_PMC_LTR_IGNORE_OFFSET, .ltr_ignore_max = ADL_NUM_IP_IGN_ALLOWED, + .lpm_sts_latch_en_offset = MTL_LPM_STATUS_LATCH_EN_OFFSET, + .lpm_num_maps = ADL_LPM_NUM_MAPS, + .lpm_priority_offset = MTL_LPM_PRI_OFFSET, + .lpm_en_offset = MTL_LPM_EN_OFFSET, + .lpm_res_counter_step_x2 = TGL_PMC_LPM_RES_COUNTER_STEP_X2, + .lpm_residency_offset = MTL_LPM_RESIDENCY_OFFSET, + .lpm_reg_index = MTL_LPM_REG_INDEX, }; #define PMC_DEVID_SOCM 0x7e7f @@ -929,16 +951,19 @@ const struct pmc_reg_map mtl_ioem_reg_map = { #define PMC_DEVID_IOEM 0x7ebf static struct pmc_info mtl_pmc_info_list[] = { { - .devid = PMC_DEVID_SOCM, - .map = &mtl_socm_reg_map, + .guid = SOCP_LPM_REQ_GUID, + .devid = PMC_DEVID_SOCM, + .map = &mtl_socm_reg_map, }, { - .devid = PMC_DEVID_IOEP, - .map = &mtl_ioep_reg_map, + .guid = IOEP_LPM_REQ_GUID, + .devid = PMC_DEVID_IOEP, + .map = &mtl_ioep_reg_map, }, { - .devid = PMC_DEVID_IOEM, - .map = &mtl_ioem_reg_map + .guid = IOEM_LPM_REQ_GUID, + .devid = PMC_DEVID_IOEM, + .map = &mtl_ioem_reg_map }, {} }; @@ -1014,5 +1039,5 @@ int mtl_core_init(struct pmc_dev *pmcdev) dev_dbg(&pmcdev->pdev->dev, "ignoring GBE LTR\n"); pmc_core_send_ltr_ignore(pmcdev, 3); - return 0; + return pmc_core_ssram_get_lpm_reqs(pmcdev); } -- cgit From 3621df43b07d9a32e18309de569f43a8b6453966 Mon Sep 17 00:00:00 2001 From: "David E. Box" Date: Wed, 29 Nov 2023 14:21:31 -0800 Subject: platform/x86/intel/pmc: Add debug attribute for Die C6 counter MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add a "die_c6_us_show" debugfs attribute. Reads the counter value using Intel Platform Monitoring Technology (PMT) driver API. This counter is useful for determining the idle residency of CPUs in the compute tile. Also adds a missing forward declaration for punit_ep which was declared in an earlier upstream commit but only used for the first time in this one. Signed-off-by: David E. Box Reviewed-by: Ilpo Järvinen Link: https://lore.kernel.org/r/20231129222132.2331261-20-david.e.box@linux.intel.com Signed-off-by: Hans de Goede --- drivers/platform/x86/intel/pmc/core.c | 55 +++++++++++++++++++++++++++++++++++ drivers/platform/x86/intel/pmc/core.h | 4 +++ 2 files changed, 59 insertions(+) diff --git a/drivers/platform/x86/intel/pmc/core.c b/drivers/platform/x86/intel/pmc/core.c index 9d3a1b6ef622..983e3a8f4910 100644 --- a/drivers/platform/x86/intel/pmc/core.c +++ b/drivers/platform/x86/intel/pmc/core.c @@ -20,6 +20,7 @@ #include #include #include +#include #include #include @@ -27,6 +28,7 @@ #include #include "core.h" +#include "../pmt/telemetry.h" /* Maximum number of modes supported by platfoms that has low power mode capability */ const char *pmc_lpm_modes[] = { @@ -822,6 +824,47 @@ static int pmc_core_substate_req_regs_show(struct seq_file *s, void *unused) } DEFINE_SHOW_ATTRIBUTE(pmc_core_substate_req_regs); +static unsigned int pmc_core_get_crystal_freq(void) +{ + unsigned int eax_denominator, ebx_numerator, ecx_hz, edx; + + if (boot_cpu_data.cpuid_level < 0x15) + return 0; + + eax_denominator = ebx_numerator = ecx_hz = edx = 0; + + /* CPUID 15H TSC/Crystal ratio, plus optionally Crystal Hz */ + cpuid(0x15, &eax_denominator, &ebx_numerator, &ecx_hz, &edx); + + if (ebx_numerator == 0 || eax_denominator == 0) + return 0; + + return ecx_hz; +} + +static int pmc_core_die_c6_us_show(struct seq_file *s, void *unused) +{ + struct pmc_dev *pmcdev = s->private; + u64 die_c6_res, count; + int ret; + + if (!pmcdev->crystal_freq) { + dev_warn_once(&pmcdev->pdev->dev, "Crystal frequency unavailable\n"); + return -ENXIO; + } + + ret = pmt_telem_read(pmcdev->punit_ep, pmcdev->die_c6_offset, + &count, 1); + if (ret) + return ret; + + die_c6_res = div64_u64(count * HZ_PER_MHZ, pmcdev->crystal_freq); + seq_printf(s, "%llu\n", die_c6_res); + + return 0; +} +DEFINE_SHOW_ATTRIBUTE(pmc_core_die_c6_us); + static int pmc_core_lpm_latch_mode_show(struct seq_file *s, void *unused) { struct pmc_dev *pmcdev = s->private; @@ -1118,6 +1161,12 @@ static void pmc_core_dbgfs_register(struct pmc_dev *pmcdev) pmcdev->dbgfs_dir, pmcdev, &pmc_core_substate_req_regs_fops); } + + if (pmcdev->has_die_c6) { + debugfs_create_file("die_c6_us_show", 0444, + pmcdev->dbgfs_dir, pmcdev, + &pmc_core_die_c6_us_fops); + } } static const struct x86_cpu_id intel_pmc_core_ids[] = { @@ -1212,6 +1261,10 @@ static void pmc_core_clean_structure(struct platform_device *pdev) pci_dev_put(pmcdev->ssram_pcidev); pci_disable_device(pmcdev->ssram_pcidev); } + + if (pmcdev->punit_ep) + pmt_telem_unregister_endpoint(pmcdev->punit_ep); + platform_set_drvdata(pdev, NULL); mutex_destroy(&pmcdev->lock); } @@ -1232,6 +1285,8 @@ static int pmc_core_probe(struct platform_device *pdev) if (!pmcdev) return -ENOMEM; + pmcdev->crystal_freq = pmc_core_get_crystal_freq(); + platform_set_drvdata(pdev, pmcdev); pmcdev->pdev = pdev; diff --git a/drivers/platform/x86/intel/pmc/core.h b/drivers/platform/x86/intel/pmc/core.h index 85b6f6ae4995..6d7673145f90 100644 --- a/drivers/platform/x86/intel/pmc/core.h +++ b/drivers/platform/x86/intel/pmc/core.h @@ -16,6 +16,8 @@ #include #include +struct telem_endpoint; + #define SLP_S0_RES_COUNTER_MASK GENMASK(31, 0) #define PMC_BASE_ADDR_DEFAULT 0xFE000000 @@ -357,6 +359,7 @@ struct pmc { * @devs: pointer to an array of pmc pointers * @pdev: pointer to platform_device struct * @ssram_pcidev: pointer to pci device struct for the PMC SSRAM + * @crystal_freq: crystal frequency from cpuid * @dbgfs_dir: path to debugfs interface * @pmc_xram_read_bit: flag to indicate whether PMC XRAM shadow registers * used to read MPHY PG and PLL status are available @@ -374,6 +377,7 @@ struct pmc_dev { struct dentry *dbgfs_dir; struct platform_device *pdev; struct pci_dev *ssram_pcidev; + unsigned int crystal_freq; int pmc_xram_read_bit; struct mutex lock; /* generic mutex lock for PMC Core */ -- cgit From 6e79648553818bb21021ccf72ae27f4508844818 Mon Sep 17 00:00:00 2001 From: "David E. Box" Date: Wed, 29 Nov 2023 14:21:32 -0800 Subject: platform/x86/intel/pmc: Show Die C6 counter on Meteor Lake MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Expose the Die C6 counter on Meteor Lake. Signed-off-by: David E. Box Reviewed-by: Ilpo Järvinen Link: https://lore.kernel.org/r/20231129222132.2331261-21-david.e.box@linux.intel.com Signed-off-by: Hans de Goede --- drivers/platform/x86/intel/pmc/mtl.c | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/drivers/platform/x86/intel/pmc/mtl.c b/drivers/platform/x86/intel/pmc/mtl.c index 7ceeae507f4c..38c2f946ec23 100644 --- a/drivers/platform/x86/intel/pmc/mtl.c +++ b/drivers/platform/x86/intel/pmc/mtl.c @@ -10,12 +10,17 @@ #include #include "core.h" +#include "../pmt/telemetry.h" /* PMC SSRAM PMT Telemetry GUIDS */ #define SOCP_LPM_REQ_GUID 0x2625030 #define IOEM_LPM_REQ_GUID 0x4357464 #define IOEP_LPM_REQ_GUID 0x5077612 +/* Die C6 from PUNIT telemetry */ +#define MTL_PMT_DMU_DIE_C6_OFFSET 15 +#define MTL_PMT_DMU_GUID 0x1A067102 + static const u8 MTL_LPM_REG_INDEX[] = {0, 4, 5, 6, 8, 9, 10, 11, 12, 13, 14, 15, 16, 20}; /* @@ -968,6 +973,32 @@ static struct pmc_info mtl_pmc_info_list[] = { {} }; +static void mtl_punit_pmt_init(struct pmc_dev *pmcdev) +{ + struct telem_endpoint *ep; + struct pci_dev *pcidev; + + pcidev = pci_get_domain_bus_and_slot(0, 0, PCI_DEVFN(10, 0)); + if (!pcidev) { + dev_err(&pmcdev->pdev->dev, "PUNIT PMT device not found.\n"); + return; + } + + ep = pmt_telem_find_and_register_endpoint(pcidev, MTL_PMT_DMU_GUID, 0); + if (IS_ERR(ep)) { + dev_err(&pmcdev->pdev->dev, + "pmc_core: couldn't get DMU telem endpoint, %ld\n", + PTR_ERR(ep)); + return; + } + + pci_dev_put(pcidev); + pmcdev->punit_ep = ep; + + pmcdev->has_die_c6 = true; + pmcdev->die_c6_offset = MTL_PMT_DMU_DIE_C6_OFFSET; +} + #define MTL_GNA_PCI_DEV 0x7e4c #define MTL_IPU_PCI_DEV 0x7d19 #define MTL_VPU_PCI_DEV 0x7d1d @@ -1032,6 +1063,7 @@ int mtl_core_init(struct pmc_dev *pmcdev) } pmc_core_get_low_power_modes(pmcdev); + mtl_punit_pmt_init(pmcdev); /* Due to a hardware limitation, the GBE LTR blocks PC10 * when a cable is attached. Tell the PMC to ignore it. -- cgit From 35ddd61cf023b5deb2b7e9f1627abef053281c0a Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Mon, 4 Dec 2023 15:29:29 +0300 Subject: platform/x86: x86-android-tablets: Fix an IS_ERR() vs NULL check in probe The spi_new_device() function returns NULL on error, it doesn't return error pointers. Fixes: 70505ea6de24 ("platform/x86: x86-android-tablets: Add support for SPI device instantiation") Signed-off-by: Dan Carpenter Link: https://lore.kernel.org/r/4b1b2395-c7c5-44a4-b0b0-6d091c7f46a2@moroto.mountain Reviewed-by: Hans de Goede Signed-off-by: Hans de Goede --- drivers/platform/x86/x86-android-tablets/core.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/platform/x86/x86-android-tablets/core.c b/drivers/platform/x86/x86-android-tablets/core.c index 6a5975ac3286..f8221a15575b 100644 --- a/drivers/platform/x86/x86-android-tablets/core.c +++ b/drivers/platform/x86/x86-android-tablets/core.c @@ -220,8 +220,8 @@ static __init int x86_instantiate_spi_dev(const struct x86_dev_info *dev_info, i spi_devs[idx] = spi_new_device(controller, &board_info); put_device(&controller->dev); - if (IS_ERR(spi_devs[idx])) - return dev_err_probe(&controller->dev, PTR_ERR(spi_devs[idx]), + if (!spi_devs[idx]) + return dev_err_probe(&controller->dev, -ENOMEM, "creating SPI-device %d\n", idx); return 0; -- cgit From f2a2d85a9374b771474aeefe4b27cc725f0cfcb0 Mon Sep 17 00:00:00 2001 From: Uwe Kleine-König Date: Mon, 4 Dec 2023 22:52:11 +0100 Subject: platform/x86: asus-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 ignored (apart from emitting a warning) 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. Eventually after all drivers are converted, .remove_new() will be renamed to .remove(). 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/639b9ffc18422fe59125893bd7909e8a73cffb72.1701726190.git.u.kleine-koenig@pengutronix.de Reviewed-by: Hans de Goede Signed-off-by: Hans de Goede --- drivers/platform/x86/asus-wmi.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/drivers/platform/x86/asus-wmi.c b/drivers/platform/x86/asus-wmi.c index 6a79f16233ab..ca3e64c37f1e 100644 --- a/drivers/platform/x86/asus-wmi.c +++ b/drivers/platform/x86/asus-wmi.c @@ -4598,7 +4598,7 @@ fail_platform: return err; } -static int asus_wmi_remove(struct platform_device *device) +static void asus_wmi_remove(struct platform_device *device) { struct asus_wmi *asus; @@ -4619,7 +4619,6 @@ static int asus_wmi_remove(struct platform_device *device) platform_profile_remove(); kfree(asus); - return 0; } /* Platform driver - hibernate/resume callbacks *******************************/ @@ -4741,7 +4740,7 @@ int __init_or_module asus_wmi_register_driver(struct asus_wmi_driver *driver) return -EBUSY; platform_driver = &driver->platform_driver; - platform_driver->remove = asus_wmi_remove; + platform_driver->remove_new = asus_wmi_remove; platform_driver->driver.owner = driver->owner; platform_driver->driver.name = driver->name; platform_driver->driver.pm = &asus_pm_ops; -- cgit From 3df692169e8486fc3dd91fcd5ea81c27a0bac033 Mon Sep 17 00:00:00 2001 From: Uwe Kleine-König Date: Mon, 4 Dec 2023 22:52:12 +0100 Subject: platform/x86: hp-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 ignored (apart from emitting a warning) 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. Eventually after all drivers are converted, .remove_new() will be renamed to .remove(). 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/a6b074b7ee37f3682da4b3f39ea40af97add64c2.1701726190.git.u.kleine-koenig@pengutronix.de Reviewed-by: Hans de Goede Signed-off-by: Hans de Goede --- drivers/platform/x86/hp/hp-wmi.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/drivers/platform/x86/hp/hp-wmi.c b/drivers/platform/x86/hp/hp-wmi.c index 8ebb7be52ee7..e536604225c5 100644 --- a/drivers/platform/x86/hp/hp-wmi.c +++ b/drivers/platform/x86/hp/hp-wmi.c @@ -1478,7 +1478,7 @@ static int __init hp_wmi_bios_setup(struct platform_device *device) return 0; } -static int __exit hp_wmi_bios_remove(struct platform_device *device) +static void __exit hp_wmi_bios_remove(struct platform_device *device) { int i; @@ -1502,8 +1502,6 @@ static int __exit hp_wmi_bios_remove(struct platform_device *device) if (platform_profile_support) platform_profile_remove(); - - return 0; } static int hp_wmi_resume_handler(struct device *device) @@ -1560,7 +1558,7 @@ static struct platform_driver hp_wmi_driver __refdata = { .pm = &hp_wmi_pm_ops, .dev_groups = hp_wmi_groups, }, - .remove = __exit_p(hp_wmi_bios_remove), + .remove_new = __exit_p(hp_wmi_bios_remove), }; static umode_t hp_wmi_hwmon_is_visible(const void *data, -- cgit From 7973be94724464222ae0b1860a25be04ab7b0132 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Fri, 8 Dec 2023 18:52:38 +0200 Subject: clk: x86: lpss-atom: Drop unneeded 'extern' in the header MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 'extern' for the functions is not needed, drop it. Signed-off-by: Andy Shevchenko Reviewed-by: Ilpo Järvinen Link: https://lore.kernel.org/r/20231208165238.3309058-1-andriy.shevchenko@linux.intel.com Reviewed-by: Hans de Goede Signed-off-by: Hans de Goede --- include/linux/platform_data/x86/clk-lpss.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/linux/platform_data/x86/clk-lpss.h b/include/linux/platform_data/x86/clk-lpss.h index 41df326583f9..7f132029316a 100644 --- a/include/linux/platform_data/x86/clk-lpss.h +++ b/include/linux/platform_data/x86/clk-lpss.h @@ -15,6 +15,6 @@ struct lpss_clk_data { struct clk *clk; }; -extern int lpss_atom_clk_init(void); +int lpss_atom_clk_init(void); #endif /* __CLK_LPSS_H */ -- cgit From b87434f2e6fe81362d2ac57f3aba45ba89a11399 Mon Sep 17 00:00:00 2001 From: Srinivas Pandruvada Date: Mon, 4 Dec 2023 14:17:36 -0800 Subject: platform/x86/intel/tpmi: Don't create devices for disabled features MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit If some TPMI features are disabled, don't create auxiliary devices. In this way feature drivers will not load. While creating auxiliary devices, call tpmi_read_feature_status() to check feature state and return if the feature is disabled without creating a device. Signed-off-by: Srinivas Pandruvada Reviewed-by: Hans de Goede Reviewed-by: Ilpo Järvinen Link: https://lore.kernel.org/r/20231204221740.3645130-2-srinivas.pandruvada@linux.intel.com Signed-off-by: Hans de Goede --- drivers/platform/x86/intel/tpmi.c | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/drivers/platform/x86/intel/tpmi.c b/drivers/platform/x86/intel/tpmi.c index 311abcac894a..09972e2b8a06 100644 --- a/drivers/platform/x86/intel/tpmi.c +++ b/drivers/platform/x86/intel/tpmi.c @@ -598,9 +598,21 @@ static int tpmi_create_device(struct intel_tpmi_info *tpmi_info, struct intel_vsec_device *vsec_dev = tpmi_info->vsec_dev; char feature_id_name[TPMI_FEATURE_NAME_LEN]; struct intel_vsec_device *feature_vsec_dev; + struct tpmi_feature_state feature_state; struct resource *res, *tmp; const char *name; - int i; + int i, ret; + + ret = tpmi_read_feature_status(tpmi_info, pfs->pfs_header.tpmi_id, &feature_state); + if (ret) + return ret; + + /* + * If not enabled, continue to look at other features in the PFS, so return -EOPNOTSUPP. + * This will not cause failure of loading of this driver. + */ + if (!feature_state.enabled) + return -EOPNOTSUPP; name = intel_tpmi_name(pfs->pfs_header.tpmi_id); if (!name) -- cgit From 72dd14d241e1c6e241fc5b265746c59f306c6aa3 Mon Sep 17 00:00:00 2001 From: Srinivas Pandruvada Date: Mon, 4 Dec 2023 14:17:37 -0800 Subject: platform/x86/intel/tpmi: Modify external interface to get read/write state MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Modify the external interface tpmi_get_feature_status() to get read and write blocked instead of locked and disabled. Since auxiliary device is not created when disabled, no use of returning disabled state. Also locked state is not useful as feature driver can't use locked state in a meaningful way. Using read and write state, feature driver can decide which operations to restrict for that feature. Signed-off-by: Srinivas Pandruvada Reviewed-by: Ilpo Järvinen Link: https://lore.kernel.org/r/20231204221740.3645130-3-srinivas.pandruvada@linux.intel.com Signed-off-by: Hans de Goede --- drivers/platform/x86/intel/tpmi.c | 8 ++++---- include/linux/intel_tpmi.h | 5 ++--- 2 files changed, 6 insertions(+), 7 deletions(-) diff --git a/drivers/platform/x86/intel/tpmi.c b/drivers/platform/x86/intel/tpmi.c index 09972e2b8a06..92c5c3a90d53 100644 --- a/drivers/platform/x86/intel/tpmi.c +++ b/drivers/platform/x86/intel/tpmi.c @@ -345,8 +345,8 @@ err_unlock: return ret; } -int tpmi_get_feature_status(struct auxiliary_device *auxdev, int feature_id, - int *locked, int *disabled) +int tpmi_get_feature_status(struct auxiliary_device *auxdev, + int feature_id, bool *read_blocked, bool *write_blocked) { struct intel_vsec_device *intel_vsec_dev = dev_to_ivdev(auxdev->dev.parent); struct intel_tpmi_info *tpmi_info = auxiliary_get_drvdata(&intel_vsec_dev->auxdev); @@ -357,8 +357,8 @@ int tpmi_get_feature_status(struct auxiliary_device *auxdev, int feature_id, if (ret) return ret; - *locked = feature_state.locked; - *disabled = !feature_state.enabled; + *read_blocked = feature_state.read_blocked; + *write_blocked = feature_state.write_blocked; return 0; } diff --git a/include/linux/intel_tpmi.h b/include/linux/intel_tpmi.h index ee07393445f9..4f89c5bd8663 100644 --- a/include/linux/intel_tpmi.h +++ b/include/linux/intel_tpmi.h @@ -32,7 +32,6 @@ struct intel_tpmi_plat_info { struct intel_tpmi_plat_info *tpmi_get_platform_data(struct auxiliary_device *auxdev); struct resource *tpmi_get_resource_at_index(struct auxiliary_device *auxdev, int index); int tpmi_get_resource_count(struct auxiliary_device *auxdev); - -int tpmi_get_feature_status(struct auxiliary_device *auxdev, int feature_id, int *locked, - int *disabled); +int tpmi_get_feature_status(struct auxiliary_device *auxdev, int feature_id, bool *read_blocked, + bool *write_blocked); #endif -- cgit From 046d7be6210e7f870e53eb38fd410237e9d1d88f Mon Sep 17 00:00:00 2001 From: Srinivas Pandruvada Date: Mon, 4 Dec 2023 14:17:38 -0800 Subject: platform/x86/intel/tpmi: Move TPMI ID definition MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Move TPMI ID definitions to common include file. In this way other feature drivers don't have to redefine. Signed-off-by: Srinivas Pandruvada Reviewed-by: Ilpo Järvinen Link: https://lore.kernel.org/r/20231204221740.3645130-4-srinivas.pandruvada@linux.intel.com Signed-off-by: Hans de Goede --- drivers/platform/x86/intel/tpmi.c | 13 ------------- include/linux/intel_tpmi.h | 13 +++++++++++++ 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/drivers/platform/x86/intel/tpmi.c b/drivers/platform/x86/intel/tpmi.c index 92c5c3a90d53..e73cdea67fff 100644 --- a/drivers/platform/x86/intel/tpmi.c +++ b/drivers/platform/x86/intel/tpmi.c @@ -170,19 +170,6 @@ struct tpmi_feature_state { u32 locked:1; } __packed; -/* - * List of supported TMPI IDs. - * Some TMPI IDs are not used by Linux, so the numbers are not consecutive. - */ -enum intel_tpmi_id { - TPMI_ID_RAPL = 0, /* Running Average Power Limit */ - TPMI_ID_PEM = 1, /* Power and Perf excursion Monitor */ - TPMI_ID_UNCORE = 2, /* Uncore Frequency Scaling */ - TPMI_ID_SST = 5, /* Speed Select Technology */ - TPMI_CONTROL_ID = 0x80, /* Special ID for getting feature status */ - TPMI_INFO_ID = 0x81, /* Special ID for PCI BDF and Package ID information */ -}; - /* * The size from hardware is in u32 units. This size is from a trusted hardware, * but better to verify for pre silicon platforms. Set size to 0, when invalid. diff --git a/include/linux/intel_tpmi.h b/include/linux/intel_tpmi.h index 4f89c5bd8663..a3529b962be6 100644 --- a/include/linux/intel_tpmi.h +++ b/include/linux/intel_tpmi.h @@ -12,6 +12,19 @@ #define TPMI_MINOR_VERSION(val) FIELD_GET(GENMASK(4, 0), val) #define TPMI_MAJOR_VERSION(val) FIELD_GET(GENMASK(7, 5), val) +/* + * List of supported TMPI IDs. + * Some TMPI IDs are not used by Linux, so the numbers are not consecutive. + */ +enum intel_tpmi_id { + TPMI_ID_RAPL = 0, /* Running Average Power Limit */ + TPMI_ID_PEM = 1, /* Power and Perf excursion Monitor */ + TPMI_ID_UNCORE = 2, /* Uncore Frequency Scaling */ + TPMI_ID_SST = 5, /* Speed Select Technology */ + TPMI_CONTROL_ID = 0x80, /* Special ID for getting feature status */ + TPMI_INFO_ID = 0x81, /* Special ID for PCI BDF and Package ID information */ +}; + /** * struct intel_tpmi_plat_info - Platform information for a TPMI device instance * @package_id: CPU Package id -- cgit From 8bed9ff7dbcce4d1a436f7839be48c6fd5fac0ce Mon Sep 17 00:00:00 2001 From: Srinivas Pandruvada Date: Mon, 4 Dec 2023 14:17:39 -0800 Subject: platform/x86: ISST: Process read/write blocked feature status MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When a feature is read blocked, don't continue to read SST information and register with SST core. When the feature is write blocked, continue to offer read interface for SST parameters, but don't allow any operation to change state. A state change results from SST level change, feature change or class of service change. Signed-off-by: Srinivas Pandruvada Reviewed-by: Hans de Goede Reviewed-by: Ilpo Järvinen Link: https://lore.kernel.org/r/20231204221740.3645130-5-srinivas.pandruvada@linux.intel.com Signed-off-by: Hans de Goede --- .../x86/intel/speed_select_if/isst_tpmi_core.c | 25 ++++++++++++++++++++++ 1 file changed, 25 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 0b6d2c864437..2662fbbddf0c 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 @@ -234,6 +234,7 @@ struct perf_level { * @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 + * @write_blocked: Write operation is blocked, so can't change SST state * * 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 @@ -259,6 +260,7 @@ struct tpmi_per_power_domain_info { u64 saved_clos_configs[4]; u64 saved_clos_assocs[4]; u64 saved_pp_control; + bool write_blocked; }; /** @@ -515,6 +517,9 @@ static long isst_if_clos_param(void __user *argp) return -EINVAL; if (clos_param.get_set) { + if (power_domain_info->write_blocked) + return -EPERM; + _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, @@ -602,6 +607,9 @@ static long isst_if_clos_assoc(void __user *argp) power_domain_info = &sst_inst->power_domain_info[punit_id]; + if (assoc_cmds.get_set && power_domain_info->write_blocked) + return -EPERM; + 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; @@ -752,6 +760,9 @@ static int isst_if_set_perf_level(void __user *argp) if (!power_domain_info) return -EINVAL; + if (power_domain_info->write_blocked) + return -EPERM; + if (!(power_domain_info->pp_header.allowed_level_mask & BIT(perf_level.level))) return -EINVAL; @@ -809,6 +820,9 @@ static int isst_if_set_perf_feature(void __user *argp) if (!power_domain_info) return -EINVAL; + if (power_domain_info->write_blocked) + return -EPERM; + _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) @@ -1257,11 +1271,21 @@ static long isst_if_def_ioctl(struct file *file, unsigned int cmd, int tpmi_sst_dev_add(struct auxiliary_device *auxdev) { + bool read_blocked = 0, write_blocked = 0; struct intel_tpmi_plat_info *plat_info; struct tpmi_sst_struct *tpmi_sst; int i, ret, pkg = 0, inst = 0; int num_resources; + ret = tpmi_get_feature_status(auxdev, TPMI_ID_SST, &read_blocked, &write_blocked); + if (ret) + dev_info(&auxdev->dev, "Can't read feature status: ignoring read/write blocked status\n"); + + if (read_blocked) { + dev_info(&auxdev->dev, "Firmware has blocked reads, exiting\n"); + return -ENODEV; + } + plat_info = tpmi_get_platform_data(auxdev); if (!plat_info) { dev_err(&auxdev->dev, "No platform info\n"); @@ -1306,6 +1330,7 @@ int tpmi_sst_dev_add(struct auxiliary_device *auxdev) 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].write_blocked = write_blocked; 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); -- cgit From b06458d1b1cbb99635c7bb4f9a4f4c4cef2ed984 Mon Sep 17 00:00:00 2001 From: Srinivas Pandruvada Date: Mon, 4 Dec 2023 14:17:40 -0800 Subject: platform/x86/intel-uncore-freq: Process read/write blocked feature status MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When a feature is read blocked, don't continue to read uncore information and register with uncore core. When the feature is write blocked, continue to offer read interface but block setting uncore limits. Signed-off-by: Srinivas Pandruvada Reviewed-by: Ilpo Järvinen Link: https://lore.kernel.org/r/20231204221740.3645130-6-srinivas.pandruvada@linux.intel.com Signed-off-by: Hans de Goede --- .../x86/intel/uncore-frequency/uncore-frequency-tpmi.c | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/drivers/platform/x86/intel/uncore-frequency/uncore-frequency-tpmi.c b/drivers/platform/x86/intel/uncore-frequency/uncore-frequency-tpmi.c index 4fb790552c47..bd75d61ff8a6 100644 --- a/drivers/platform/x86/intel/uncore-frequency/uncore-frequency-tpmi.c +++ b/drivers/platform/x86/intel/uncore-frequency/uncore-frequency-tpmi.c @@ -66,6 +66,7 @@ struct tpmi_uncore_struct { int min_ratio; struct tpmi_uncore_power_domain_info *pd_info; struct tpmi_uncore_cluster_info root_cluster; + bool write_blocked; }; #define UNCORE_GENMASK_MIN_RATIO GENMASK_ULL(21, 15) @@ -157,6 +158,9 @@ static int uncore_write_control_freq(struct uncore_data *data, unsigned int inpu cluster_info = container_of(data, struct tpmi_uncore_cluster_info, uncore_data); uncore_root = cluster_info->uncore_root; + if (uncore_root->write_blocked) + return -EPERM; + /* Update each cluster in a package */ if (cluster_info->root_domain) { struct tpmi_uncore_struct *uncore_root = cluster_info->uncore_root; @@ -233,11 +237,21 @@ static void remove_cluster_entries(struct tpmi_uncore_struct *tpmi_uncore) static int uncore_probe(struct auxiliary_device *auxdev, const struct auxiliary_device_id *id) { + bool read_blocked = 0, write_blocked = 0; struct intel_tpmi_plat_info *plat_info; struct tpmi_uncore_struct *tpmi_uncore; int ret, i, pkg = 0; int num_resources; + ret = tpmi_get_feature_status(auxdev, TPMI_ID_UNCORE, &read_blocked, &write_blocked); + if (ret) + dev_info(&auxdev->dev, "Can't read feature status: ignoring blocked status\n"); + + if (read_blocked) { + dev_info(&auxdev->dev, "Firmware has blocked reads, exiting\n"); + return -ENODEV; + } + /* Get number of power domains, which is equal to number of resources */ num_resources = tpmi_get_resource_count(auxdev); if (!num_resources) @@ -266,6 +280,7 @@ static int uncore_probe(struct auxiliary_device *auxdev, const struct auxiliary_ } tpmi_uncore->power_domain_count = num_resources; + tpmi_uncore->write_blocked = write_blocked; /* Get the package ID from the TPMI core */ plat_info = tpmi_get_platform_data(auxdev); -- cgit From 682b43a049c898d060bb1bd7af4cac0d91b3594d Mon Sep 17 00:00:00 2001 From: Ilpo Järvinen Date: Fri, 8 Dec 2023 15:48:45 +0200 Subject: platform/x86: ips: Remove unused debug code MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Remove unused debug code inside #if 0 ... #endif. Signed-off-by: Ilpo Järvinen Link: https://lore.kernel.org/r/20231208134845.3900-1-ilpo.jarvinen@linux.intel.com Reviewed-by: Hans de Goede Signed-off-by: Hans de Goede --- drivers/platform/x86/intel_ips.c | 33 --------------------------------- 1 file changed, 33 deletions(-) diff --git a/drivers/platform/x86/intel_ips.c b/drivers/platform/x86/intel_ips.c index 4dfdbfca6841..e26e7e14c44c 100644 --- a/drivers/platform/x86/intel_ips.c +++ b/drivers/platform/x86/intel_ips.c @@ -1105,39 +1105,6 @@ static int ips_monitor(void *data) return 0; } -#if 0 -#define THM_DUMPW(reg) \ - { \ - u16 val = thm_readw(reg); \ - dev_dbg(ips->dev, #reg ": 0x%04x\n", val); \ - } -#define THM_DUMPL(reg) \ - { \ - u32 val = thm_readl(reg); \ - dev_dbg(ips->dev, #reg ": 0x%08x\n", val); \ - } -#define THM_DUMPQ(reg) \ - { \ - u64 val = thm_readq(reg); \ - dev_dbg(ips->dev, #reg ": 0x%016x\n", val); \ - } - -static void dump_thermal_info(struct ips_driver *ips) -{ - u16 ptl; - - ptl = thm_readw(THM_PTL); - dev_dbg(ips->dev, "Processor temp limit: %d\n", ptl); - - THM_DUMPW(THM_CTA); - THM_DUMPW(THM_TRC); - THM_DUMPW(THM_CTV1); - THM_DUMPL(THM_STS); - THM_DUMPW(THM_PTV); - THM_DUMPQ(THM_MGTV); -} -#endif - /** * ips_irq_handler - handle temperature triggers and other IPS events * @irq: irq number -- cgit From f763fd73b181c7c5117e0d8a4da3dd4237737b86 Mon Sep 17 00:00:00 2001 From: Armin Wolf Date: Sun, 10 Dec 2023 21:24:39 +0100 Subject: platform/x86: wmi: Remove debug_dump_wdg module param MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The functionality of dumping WDG entries is better provided by userspace tools like "fwts wmi", which also does not suffer from garbled printk output caused by pr_cont(). Reviewed-by: Ilpo Järvinen Signed-off-by: Armin Wolf Link: https://lore.kernel.org/r/20231210202443.646427-2-W_Armin@gmx.de Reviewed-by: Hans de Goede Signed-off-by: Hans de Goede --- drivers/platform/x86/wmi.c | 31 ------------------------------- 1 file changed, 31 deletions(-) diff --git a/drivers/platform/x86/wmi.c b/drivers/platform/x86/wmi.c index 4f94e4b117f1..e8019bc19b4f 100644 --- a/drivers/platform/x86/wmi.c +++ b/drivers/platform/x86/wmi.c @@ -90,11 +90,6 @@ module_param(debug_event, bool, 0444); MODULE_PARM_DESC(debug_event, "Log WMI Events [0/1]"); -static bool debug_dump_wdg; -module_param(debug_dump_wdg, bool, 0444); -MODULE_PARM_DESC(debug_dump_wdg, - "Dump available WMI interfaces [0/1]"); - static const struct acpi_device_id wmi_device_ids[] = { {"PNP0C14", 0}, {"pnp0c14", 0}, @@ -597,29 +592,6 @@ acpi_status wmidev_block_set(struct wmi_device *wdev, u8 instance, const struct } EXPORT_SYMBOL_GPL(wmidev_block_set); -static void wmi_dump_wdg(const struct guid_block *g) -{ - pr_info("%pUL:\n", &g->guid); - if (g->flags & ACPI_WMI_EVENT) - pr_info("\tnotify_id: 0x%02X\n", g->notify_id); - else - pr_info("\tobject_id: %2pE\n", g->object_id); - pr_info("\tinstance_count: %d\n", g->instance_count); - pr_info("\tflags: %#x", g->flags); - if (g->flags) { - if (g->flags & ACPI_WMI_EXPENSIVE) - pr_cont(" ACPI_WMI_EXPENSIVE"); - if (g->flags & ACPI_WMI_METHOD) - pr_cont(" ACPI_WMI_METHOD"); - if (g->flags & ACPI_WMI_STRING) - pr_cont(" ACPI_WMI_STRING"); - if (g->flags & ACPI_WMI_EVENT) - pr_cont(" ACPI_WMI_EVENT"); - } - pr_cont("\n"); - -} - static void wmi_notify_debug(u32 value, void *context) { struct acpi_buffer response = { ACPI_ALLOCATE_BUFFER, NULL }; @@ -1343,9 +1315,6 @@ static int parse_wdg(struct device *wmi_bus_dev, struct platform_device *pdev) total = obj->buffer.length / sizeof(struct guid_block); for (i = 0; i < total; i++) { - if (debug_dump_wdg) - wmi_dump_wdg(&gblock[i]); - if (!gblock[i].instance_count) { dev_info(wmi_bus_dev, FW_INFO "%pUL has zero instances\n", &gblock[i].guid); continue; -- cgit From ed72a2b50b755327f411f8199c4120d8bc7687cf Mon Sep 17 00:00:00 2001 From: Armin Wolf Date: Sun, 10 Dec 2023 21:24:40 +0100 Subject: platform/x86: wmi: Remove debug_event module param MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Users can already listen to ACPI WMI events through the ACPI netlink interface. The old wmi_notify_debug() interface also uses the deprecated GUID-based interface. Remove it to make the event handling code more readable. Reviewed-by: Ilpo Järvinen Signed-off-by: Armin Wolf Link: https://lore.kernel.org/r/20231210202443.646427-3-W_Armin@gmx.de Reviewed-by: Hans de Goede Signed-off-by: Hans de Goede --- drivers/platform/x86/wmi.c | 74 +++++----------------------------------------- 1 file changed, 7 insertions(+), 67 deletions(-) diff --git a/drivers/platform/x86/wmi.c b/drivers/platform/x86/wmi.c index e8019bc19b4f..7df5b5ee7983 100644 --- a/drivers/platform/x86/wmi.c +++ b/drivers/platform/x86/wmi.c @@ -85,11 +85,6 @@ struct wmi_block { #define ACPI_WMI_STRING BIT(2) /* GUID takes & returns a string */ #define ACPI_WMI_EVENT BIT(3) /* GUID is an event */ -static bool debug_event; -module_param(debug_event, bool, 0444); -MODULE_PARM_DESC(debug_event, - "Log WMI Events [0/1]"); - static const struct acpi_device_id wmi_device_ids[] = { {"PNP0C14", 0}, {"pnp0c14", 0}, @@ -592,42 +587,6 @@ acpi_status wmidev_block_set(struct wmi_device *wdev, u8 instance, const struct } EXPORT_SYMBOL_GPL(wmidev_block_set); -static void wmi_notify_debug(u32 value, void *context) -{ - struct acpi_buffer response = { ACPI_ALLOCATE_BUFFER, NULL }; - union acpi_object *obj; - acpi_status status; - - status = wmi_get_event_data(value, &response); - if (status != AE_OK) { - pr_info("bad event status 0x%x\n", status); - return; - } - - obj = response.pointer; - if (!obj) - return; - - pr_info("DEBUG: event 0x%02X ", value); - switch (obj->type) { - case ACPI_TYPE_BUFFER: - pr_cont("BUFFER_TYPE - length %u\n", obj->buffer.length); - break; - case ACPI_TYPE_STRING: - pr_cont("STRING_TYPE - %s\n", obj->string.pointer); - break; - case ACPI_TYPE_INTEGER: - pr_cont("INTEGER_TYPE - %llu\n", obj->integer.value); - break; - case ACPI_TYPE_PACKAGE: - pr_cont("PACKAGE_TYPE - %u elements\n", obj->package.count); - break; - default: - pr_cont("object type 0x%X\n", obj->type); - } - kfree(obj); -} - /** * wmi_install_notify_handler - Register handler for WMI events (deprecated) * @guid: 36 char string of the form fa50ff2b-f2e8-45de-83fa-65417f2f49ba @@ -656,8 +615,7 @@ acpi_status wmi_install_notify_handler(const char *guid, acpi_status wmi_status; if (guid_equal(&block->gblock.guid, &guid_input)) { - if (block->handler && - block->handler != wmi_notify_debug) + if (block->handler) return AE_ALREADY_ACQUIRED; block->handler = handler; @@ -698,22 +656,14 @@ acpi_status wmi_remove_notify_handler(const char *guid) acpi_status wmi_status; if (guid_equal(&block->gblock.guid, &guid_input)) { - if (!block->handler || - block->handler == wmi_notify_debug) + if (!block->handler) return AE_NULL_ENTRY; - if (debug_event) { - block->handler = wmi_notify_debug; - status = AE_OK; - } else { - wmi_status = wmi_method_enable(block, false); - block->handler = NULL; - block->handler_data = NULL; - if ((wmi_status != AE_OK) || - ((wmi_status == AE_OK) && - (status == AE_NOT_EXIST))) - status = wmi_status; - } + wmi_status = wmi_method_enable(block, false); + block->handler = NULL; + block->handler_data = NULL; + if (wmi_status != AE_OK || (wmi_status == AE_OK && status == AE_NOT_EXIST)) + status = wmi_status; } } @@ -1340,17 +1290,10 @@ static int parse_wdg(struct device *wmi_bus_dev, struct platform_device *pdev) list_add_tail(&wblock->list, &wmi_block_list); - if (debug_event) { - wblock->handler = wmi_notify_debug; - wmi_method_enable(wblock, true); - } - retval = wmi_add_device(pdev, &wblock->dev); if (retval) { dev_err(wmi_bus_dev, "failed to register %pUL\n", &wblock->gblock.guid); - if (debug_event) - wmi_method_enable(wblock, false); list_del(&wblock->list); put_device(&wblock->dev.dev); @@ -1445,9 +1388,6 @@ static void acpi_wmi_notify_handler(acpi_handle handle, u32 event, wblock->handler(event, wblock->handler_data); } - if (debug_event) - pr_info("DEBUG: GUID %pUL event 0x%02X\n", &wblock->gblock.guid, event); - acpi_bus_generate_netlink_event( wblock->acpi_device->pnp.device_class, dev_name(&wblock->dev.dev), -- cgit From ba358964cb8f24f28d543c6ce18a4af64961ab62 Mon Sep 17 00:00:00 2001 From: Armin Wolf Date: Sun, 10 Dec 2023 21:24:41 +0100 Subject: platform/x86: dell-smbios-wmi: Use devm_get_free_pages() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Use devres version of __get_free_pages() to simplify the error handling code. Signed-off-by: Armin Wolf Reviewed-by: Ilpo Järvinen Link: https://lore.kernel.org/r/20231210202443.646427-4-W_Armin@gmx.de Reviewed-by: Hans de Goede Signed-off-by: Hans de Goede --- drivers/platform/x86/dell/dell-smbios-wmi.c | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/drivers/platform/x86/dell/dell-smbios-wmi.c b/drivers/platform/x86/dell/dell-smbios-wmi.c index 931cc50136de..7eb7c61bb27d 100644 --- a/drivers/platform/x86/dell/dell-smbios-wmi.c +++ b/drivers/platform/x86/dell/dell-smbios-wmi.c @@ -6,6 +6,7 @@ */ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt +#include #include #include #include @@ -183,7 +184,7 @@ static int dell_smbios_wmi_probe(struct wmi_device *wdev, const void *context) return ret; count = get_order(priv->req_buf_size); - priv->buf = (void *)__get_free_pages(GFP_KERNEL, count); + priv->buf = (void *)devm_get_free_pages(&wdev->dev, GFP_KERNEL, count); if (!priv->buf) return -ENOMEM; @@ -191,7 +192,7 @@ static int dell_smbios_wmi_probe(struct wmi_device *wdev, const void *context) wdev->dev.id = 1; ret = dell_smbios_register_device(&wdev->dev, &dell_smbios_wmi_call); if (ret) - goto fail_register; + return ret; priv->wdev = wdev; dev_set_drvdata(&wdev->dev, priv); @@ -200,24 +201,17 @@ static int dell_smbios_wmi_probe(struct wmi_device *wdev, const void *context) mutex_unlock(&list_mutex); return 0; - -fail_register: - free_pages((unsigned long)priv->buf, count); - return ret; } static void dell_smbios_wmi_remove(struct wmi_device *wdev) { struct wmi_smbios_priv *priv = dev_get_drvdata(&wdev->dev); - int count; mutex_lock(&call_mutex); mutex_lock(&list_mutex); list_del(&priv->list); mutex_unlock(&list_mutex); dell_smbios_unregister_device(&wdev->dev); - count = get_order(priv->req_buf_size); - free_pages((unsigned long)priv->buf, count); mutex_unlock(&call_mutex); } -- cgit From 93885e85a77f25a1de57d47572a00633717fe1d4 Mon Sep 17 00:00:00 2001 From: Armin Wolf Date: Sun, 10 Dec 2023 21:24:42 +0100 Subject: platform/x86: dell-smbios-wmi: Stop using WMI chardev The WMI chardev API will be removed in the near future. Reimplement the necessary bits used by this driver so that userspace software depending on it does no break. Signed-off-by: Armin Wolf Link: https://lore.kernel.org/r/20231210202443.646427-5-W_Armin@gmx.de Reviewed-by: Hans de Goede Signed-off-by: Hans de Goede --- drivers/platform/x86/dell/dell-smbios-wmi.c | 161 ++++++++++++++++++++-------- 1 file changed, 117 insertions(+), 44 deletions(-) diff --git a/drivers/platform/x86/dell/dell-smbios-wmi.c b/drivers/platform/x86/dell/dell-smbios-wmi.c index 7eb7c61bb27d..ae9012549560 100644 --- a/drivers/platform/x86/dell/dell-smbios-wmi.c +++ b/drivers/platform/x86/dell/dell-smbios-wmi.c @@ -8,11 +8,14 @@ #include #include +#include #include +#include #include #include #include #include +#include #include "dell-smbios.h" #include "dell-wmi-descriptor.h" @@ -33,7 +36,8 @@ struct wmi_smbios_priv { struct list_head list; struct wmi_device *wdev; struct device *child; - u32 req_buf_size; + u64 req_buf_size; + struct miscdevice char_dev; }; static LIST_HEAD(wmi_list); @@ -109,48 +113,115 @@ out_wmi_call: return ret; } -static long dell_smbios_wmi_filter(struct wmi_device *wdev, unsigned int cmd, - struct wmi_ioctl_buffer *arg) +static int dell_smbios_wmi_open(struct inode *inode, struct file *filp) { struct wmi_smbios_priv *priv; - int ret = 0; - - switch (cmd) { - case DELL_WMI_SMBIOS_CMD: - mutex_lock(&call_mutex); - priv = dev_get_drvdata(&wdev->dev); - if (!priv) { - ret = -ENODEV; - goto fail_smbios_cmd; - } - memcpy(priv->buf, arg, priv->req_buf_size); - if (dell_smbios_call_filter(&wdev->dev, &priv->buf->std)) { - dev_err(&wdev->dev, "Invalid call %d/%d:%8x\n", - priv->buf->std.cmd_class, - priv->buf->std.cmd_select, - priv->buf->std.input[0]); - ret = -EFAULT; - goto fail_smbios_cmd; - } - ret = run_smbios_call(priv->wdev); - if (ret) - goto fail_smbios_cmd; - memcpy(arg, priv->buf, priv->req_buf_size); -fail_smbios_cmd: - mutex_unlock(&call_mutex); - break; - default: - ret = -ENOIOCTLCMD; + + priv = container_of(filp->private_data, struct wmi_smbios_priv, char_dev); + filp->private_data = priv; + + return nonseekable_open(inode, filp); +} + +static ssize_t dell_smbios_wmi_read(struct file *filp, char __user *buffer, size_t length, + loff_t *offset) +{ + struct wmi_smbios_priv *priv = filp->private_data; + + return simple_read_from_buffer(buffer, length, offset, &priv->req_buf_size, + sizeof(priv->req_buf_size)); +} + +static long dell_smbios_wmi_do_ioctl(struct wmi_smbios_priv *priv, + struct dell_wmi_smbios_buffer __user *arg) +{ + long ret; + + if (get_user(priv->buf->length, &arg->length)) + return -EFAULT; + + if (priv->buf->length < priv->req_buf_size) + return -EINVAL; + + /* if it's too big, warn, driver will only use what is needed */ + if (priv->buf->length > priv->req_buf_size) + dev_err(&priv->wdev->dev, "Buffer %llu is bigger than required %llu\n", + priv->buf->length, priv->req_buf_size); + + if (copy_from_user(priv->buf, arg, priv->req_buf_size)) + return -EFAULT; + + if (dell_smbios_call_filter(&priv->wdev->dev, &priv->buf->std)) { + dev_err(&priv->wdev->dev, "Invalid call %d/%d:%8x\n", + priv->buf->std.cmd_class, + priv->buf->std.cmd_select, + priv->buf->std.input[0]); + + return -EINVAL; } + + ret = run_smbios_call(priv->wdev); + if (ret) + return ret; + + if (copy_to_user(arg, priv->buf, priv->req_buf_size)) + return -EFAULT; + + return 0; +} + +static long dell_smbios_wmi_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) +{ + struct dell_wmi_smbios_buffer __user *input = (struct dell_wmi_smbios_buffer __user *)arg; + struct wmi_smbios_priv *priv = filp->private_data; + long ret; + + if (cmd != DELL_WMI_SMBIOS_CMD) + return -ENOIOCTLCMD; + + mutex_lock(&call_mutex); + ret = dell_smbios_wmi_do_ioctl(priv, input); + mutex_unlock(&call_mutex); + return ret; } +static const struct file_operations dell_smbios_wmi_fops = { + .owner = THIS_MODULE, + .open = dell_smbios_wmi_open, + .read = dell_smbios_wmi_read, + .unlocked_ioctl = dell_smbios_wmi_ioctl, + .compat_ioctl = compat_ptr_ioctl, +}; + +static void dell_smbios_wmi_unregister_chardev(void *data) +{ + struct miscdevice *char_dev = data; + + misc_deregister(char_dev); +} + +static int dell_smbios_wmi_register_chardev(struct wmi_smbios_priv *priv) +{ + int ret; + + priv->char_dev.minor = MISC_DYNAMIC_MINOR; + priv->char_dev.name = "wmi/dell-smbios"; + priv->char_dev.fops = &dell_smbios_wmi_fops; + priv->char_dev.mode = 0444; + + ret = misc_register(&priv->char_dev); + if (ret < 0) + return ret; + + return devm_add_action_or_reset(&priv->wdev->dev, dell_smbios_wmi_unregister_chardev, + &priv->char_dev); +} + static int dell_smbios_wmi_probe(struct wmi_device *wdev, const void *context) { - struct wmi_driver *wdriver = - container_of(wdev->dev.driver, struct wmi_driver, driver); struct wmi_smbios_priv *priv; - u32 hotfix; + u32 buffer_size, hotfix; int count; int ret; @@ -163,39 +234,42 @@ static int dell_smbios_wmi_probe(struct wmi_device *wdev, const void *context) if (!priv) return -ENOMEM; + priv->wdev = wdev; + dev_set_drvdata(&wdev->dev, priv); + /* WMI buffer size will be either 4k or 32k depending on machine */ - if (!dell_wmi_get_size(&priv->req_buf_size)) + if (!dell_wmi_get_size(&buffer_size)) return -EPROBE_DEFER; + priv->req_buf_size = buffer_size; + /* some SMBIOS calls fail unless BIOS contains hotfix */ if (!dell_wmi_get_hotfix(&hotfix)) return -EPROBE_DEFER; - if (!hotfix) { + + if (!hotfix) dev_warn(&wdev->dev, "WMI SMBIOS userspace interface not supported(%u), try upgrading to a newer BIOS\n", hotfix); - wdriver->filter_callback = NULL; - } /* add in the length object we will use internally with ioctl */ priv->req_buf_size += sizeof(u64); - ret = set_required_buffer_size(wdev, priv->req_buf_size); - if (ret) - return ret; count = get_order(priv->req_buf_size); priv->buf = (void *)devm_get_free_pages(&wdev->dev, GFP_KERNEL, count); if (!priv->buf) return -ENOMEM; + ret = dell_smbios_wmi_register_chardev(priv); + if (ret) + return ret; + /* ID is used by dell-smbios to set priority of drivers */ wdev->dev.id = 1; ret = dell_smbios_register_device(&wdev->dev, &dell_smbios_wmi_call); if (ret) return ret; - priv->wdev = wdev; - dev_set_drvdata(&wdev->dev, priv); mutex_lock(&list_mutex); list_add_tail(&priv->list, &wmi_list); mutex_unlock(&list_mutex); @@ -250,7 +324,6 @@ static struct wmi_driver dell_smbios_wmi_driver = { .probe = dell_smbios_wmi_probe, .remove = dell_smbios_wmi_remove, .id_table = dell_smbios_wmi_id_table, - .filter_callback = dell_smbios_wmi_filter, }; int init_dell_smbios_wmi(void) -- cgit From 704af3a40747e395b67892127943e6ffd5e2b642 Mon Sep 17 00:00:00 2001 From: Armin Wolf Date: Sun, 10 Dec 2023 21:24:43 +0100 Subject: platform/x86: wmi: Remove chardev interface MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The design of the WMI chardev interface is broken: - it assumes that WMI drivers are not instantiated twice - it offers next to no abstractions, the WMI driver gets a raw byte buffer - it is only used by a single driver, something which is unlikely to change Since the only user (dell-smbios-wmi) has been migrated to his own ioctl interface, remove it. Reviewed-by: Ilpo Järvinen Signed-off-by: Armin Wolf Link: https://lore.kernel.org/r/20231210202443.646427-6-W_Armin@gmx.de Reviewed-by: Hans de Goede Signed-off-by: Hans de Goede --- drivers/platform/x86/wmi.c | 180 ++------------------------------------------- include/linux/wmi.h | 8 -- 2 files changed, 5 insertions(+), 183 deletions(-) diff --git a/drivers/platform/x86/wmi.c b/drivers/platform/x86/wmi.c index 7df5b5ee7983..7303702290e5 100644 --- a/drivers/platform/x86/wmi.c +++ b/drivers/platform/x86/wmi.c @@ -23,17 +23,14 @@ #include #include #include -#include #include #include #include #include #include -#include #include #include #include -#include MODULE_AUTHOR("Carlos Corbacho"); MODULE_DESCRIPTION("ACPI-WMI Mapping Driver"); @@ -66,12 +63,9 @@ struct wmi_block { struct wmi_device dev; struct list_head list; struct guid_block gblock; - struct miscdevice char_dev; - struct mutex char_mutex; struct acpi_device *acpi_device; wmi_notify_handler handler; void *handler_data; - u64 req_buf_size; unsigned long flags; }; @@ -256,26 +250,6 @@ static void wmi_device_put(struct wmi_device *wdev) * Exported WMI functions */ -/** - * set_required_buffer_size - Sets the buffer size needed for performing IOCTL - * @wdev: A wmi bus device from a driver - * @length: Required buffer size - * - * Allocates memory needed for buffer, stores the buffer size in that memory. - * - * Return: 0 on success or a negative error code for failure. - */ -int set_required_buffer_size(struct wmi_device *wdev, u64 length) -{ - struct wmi_block *wblock; - - wblock = container_of(wdev, struct wmi_block, dev); - wblock->req_buf_size = length; - - return 0; -} -EXPORT_SYMBOL_GPL(set_required_buffer_size); - /** * wmi_instance_count - Get number of WMI object instances * @guid_string: 36 char string of the form fa50ff2b-f2e8-45de-83fa-65417f2f49ba @@ -884,111 +858,12 @@ static int wmi_dev_match(struct device *dev, struct device_driver *driver) return 0; } -static int wmi_char_open(struct inode *inode, struct file *filp) -{ - /* - * The miscdevice already stores a pointer to itself - * inside filp->private_data - */ - struct wmi_block *wblock = container_of(filp->private_data, struct wmi_block, char_dev); - - filp->private_data = wblock; - - return nonseekable_open(inode, filp); -} - -static ssize_t wmi_char_read(struct file *filp, char __user *buffer, - size_t length, loff_t *offset) -{ - struct wmi_block *wblock = filp->private_data; - - return simple_read_from_buffer(buffer, length, offset, - &wblock->req_buf_size, - sizeof(wblock->req_buf_size)); -} - -static long wmi_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) -{ - struct wmi_ioctl_buffer __user *input = - (struct wmi_ioctl_buffer __user *) arg; - struct wmi_block *wblock = filp->private_data; - struct wmi_ioctl_buffer *buf; - struct wmi_driver *wdriver; - int ret; - - if (_IOC_TYPE(cmd) != WMI_IOC) - return -ENOTTY; - - /* make sure we're not calling a higher instance than exists*/ - if (_IOC_NR(cmd) >= wblock->gblock.instance_count) - return -EINVAL; - - mutex_lock(&wblock->char_mutex); - buf = wblock->handler_data; - if (get_user(buf->length, &input->length)) { - dev_dbg(&wblock->dev.dev, "Read length from user failed\n"); - ret = -EFAULT; - goto out_ioctl; - } - /* if it's too small, abort */ - if (buf->length < wblock->req_buf_size) { - dev_err(&wblock->dev.dev, - "Buffer %lld too small, need at least %lld\n", - buf->length, wblock->req_buf_size); - ret = -EINVAL; - goto out_ioctl; - } - /* if it's too big, warn, driver will only use what is needed */ - if (buf->length > wblock->req_buf_size) - dev_warn(&wblock->dev.dev, - "Buffer %lld is bigger than required %lld\n", - buf->length, wblock->req_buf_size); - - /* copy the structure from userspace */ - if (copy_from_user(buf, input, wblock->req_buf_size)) { - dev_dbg(&wblock->dev.dev, "Copy %llu from user failed\n", - wblock->req_buf_size); - ret = -EFAULT; - goto out_ioctl; - } - - /* let the driver do any filtering and do the call */ - wdriver = drv_to_wdrv(wblock->dev.dev.driver); - if (!try_module_get(wdriver->driver.owner)) { - ret = -EBUSY; - goto out_ioctl; - } - ret = wdriver->filter_callback(&wblock->dev, cmd, buf); - module_put(wdriver->driver.owner); - if (ret) - goto out_ioctl; - - /* return the result (only up to our internal buffer size) */ - if (copy_to_user(input, buf, wblock->req_buf_size)) { - dev_dbg(&wblock->dev.dev, "Copy %llu to user failed\n", - wblock->req_buf_size); - ret = -EFAULT; - } - -out_ioctl: - mutex_unlock(&wblock->char_mutex); - return ret; -} - -static const struct file_operations wmi_fops = { - .owner = THIS_MODULE, - .read = wmi_char_read, - .open = wmi_char_open, - .unlocked_ioctl = wmi_ioctl, - .compat_ioctl = compat_ptr_ioctl, -}; static int wmi_dev_probe(struct device *dev) { struct wmi_block *wblock = dev_to_wblock(dev); struct wmi_driver *wdriver = drv_to_wdrv(dev->driver); int ret = 0; - char *buf; if (ACPI_FAILURE(wmi_method_enable(wblock, true))) dev_warn(dev, "failed to enable device -- probing anyway\n"); @@ -996,55 +871,17 @@ static int wmi_dev_probe(struct device *dev) if (wdriver->probe) { ret = wdriver->probe(dev_to_wdev(dev), find_guid_context(wblock, wdriver)); - if (ret != 0) - goto probe_failure; - } - - /* driver wants a character device made */ - if (wdriver->filter_callback) { - /* check that required buffer size declared by driver or MOF */ - if (!wblock->req_buf_size) { - dev_err(&wblock->dev.dev, - "Required buffer size not set\n"); - ret = -EINVAL; - goto probe_failure; - } + if (!ret) { + if (ACPI_FAILURE(wmi_method_enable(wblock, false))) + dev_warn(dev, "Failed to disable device\n"); - wblock->handler_data = kmalloc(wblock->req_buf_size, - GFP_KERNEL); - if (!wblock->handler_data) { - ret = -ENOMEM; - goto probe_failure; - } - - buf = kasprintf(GFP_KERNEL, "wmi/%s", wdriver->driver.name); - if (!buf) { - ret = -ENOMEM; - goto probe_string_failure; - } - wblock->char_dev.minor = MISC_DYNAMIC_MINOR; - wblock->char_dev.name = buf; - wblock->char_dev.fops = &wmi_fops; - wblock->char_dev.mode = 0444; - ret = misc_register(&wblock->char_dev); - if (ret) { - dev_warn(dev, "failed to register char dev: %d\n", ret); - ret = -ENOMEM; - goto probe_misc_failure; + return ret; } } set_bit(WMI_PROBED, &wblock->flags); - return 0; -probe_misc_failure: - kfree(buf); -probe_string_failure: - kfree(wblock->handler_data); -probe_failure: - if (ACPI_FAILURE(wmi_method_enable(wblock, false))) - dev_warn(dev, "failed to disable device\n"); - return ret; + return 0; } static void wmi_dev_remove(struct device *dev) @@ -1054,12 +891,6 @@ static void wmi_dev_remove(struct device *dev) clear_bit(WMI_PROBED, &wblock->flags); - if (wdriver->filter_callback) { - misc_deregister(&wblock->char_dev); - kfree(wblock->char_dev.name); - kfree(wblock->handler_data); - } - if (wdriver->remove) wdriver->remove(dev_to_wdev(dev)); @@ -1131,7 +962,6 @@ static int wmi_create_device(struct device *wmi_bus_dev, if (wblock->gblock.flags & ACPI_WMI_METHOD) { wblock->dev.dev.type = &wmi_type_method; - mutex_init(&wblock->char_mutex); goto out_init; } diff --git a/include/linux/wmi.h b/include/linux/wmi.h index 8a643c39fcce..50f7f1e4fd4f 100644 --- a/include/linux/wmi.h +++ b/include/linux/wmi.h @@ -11,7 +11,6 @@ #include #include #include -#include /** * struct wmi_device - WMI device structure @@ -47,8 +46,6 @@ acpi_status wmidev_block_set(struct wmi_device *wdev, u8 instance, const struct u8 wmidev_instance_count(struct wmi_device *wdev); -extern int set_required_buffer_size(struct wmi_device *wdev, u64 length); - /** * struct wmi_driver - WMI driver structure * @driver: Driver model structure @@ -57,11 +54,8 @@ extern int set_required_buffer_size(struct wmi_device *wdev, u64 length); * @probe: Callback for device binding * @remove: Callback for device unbinding * @notify: Callback for receiving WMI events - * @filter_callback: Callback for filtering device IOCTLs * * This represents WMI drivers which handle WMI devices. - * @filter_callback is only necessary for drivers which - * want to set up a WMI IOCTL interface. */ struct wmi_driver { struct device_driver driver; @@ -71,8 +65,6 @@ struct wmi_driver { int (*probe)(struct wmi_device *wdev, const void *context); void (*remove)(struct wmi_device *wdev); void (*notify)(struct wmi_device *device, union acpi_object *data); - long (*filter_callback)(struct wmi_device *wdev, unsigned int cmd, - struct wmi_ioctl_buffer *arg); }; extern int __must_check __wmi_driver_register(struct wmi_driver *driver, -- cgit From 2128f3cca5a2e7ab4d1ffb16c0e0431c3a0106a1 Mon Sep 17 00:00:00 2001 From: Ma Jun Date: Mon, 11 Dec 2023 18:06:22 +0800 Subject: Documentation/driver-api: Add document about WBRF mechanism Add documentation about AMD's Wifi band RFI mitigation (WBRF) mechanism explaining the theory and how it is used. Signed-off-by: Ma Jun Reviewed-by: Hans de Goede Reviewed-by: Mario Limonciello Signed-off-by: Hans de Goede --- Documentation/driver-api/index.rst | 1 + Documentation/driver-api/wbrf.rst | 78 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 79 insertions(+) create mode 100644 Documentation/driver-api/wbrf.rst diff --git a/Documentation/driver-api/index.rst b/Documentation/driver-api/index.rst index f549a68951d7..8bc4ebe7a36f 100644 --- a/Documentation/driver-api/index.rst +++ b/Documentation/driver-api/index.rst @@ -115,6 +115,7 @@ available subsections can be seen below. hte/index wmi dpll + wbrf .. only:: subproject and html diff --git a/Documentation/driver-api/wbrf.rst b/Documentation/driver-api/wbrf.rst new file mode 100644 index 000000000000..f48bfa029813 --- /dev/null +++ b/Documentation/driver-api/wbrf.rst @@ -0,0 +1,78 @@ +.. SPDX-License-Identifier: GPL-2.0-or-later + +================================= +WBRF - Wifi Band RFI Mitigations +================================= + +Due to electrical and mechanical constraints in certain platform designs +there may be likely interference of relatively high-powered harmonics of +the GPU memory clocks with local radio module frequency bands used by +certain Wifi bands. + +To mitigate possible RFI interference producers can advertise the +frequencies in use and consumers can use this information to avoid using +these frequencies for sensitive features. + +When a platform is known to have this issue with any contained devices, +the platform designer will advertise the availability of this feature via +ACPI devices with a device specific method (_DSM). +* Producers with this _DSM will be able to advertise the frequencies in use. +* Consumers with this _DSM will be able to register for notifications of +frequencies in use. + +Some general terms +================== + +Producer: such component who can produce high-powered radio frequency +Consumer: such component who can adjust its in-use frequency in +response to the radio frequencies of other components to mitigate the +possible RFI. + +To make the mechanism function, those producers should notify active use +of their particular frequencies so that other consumers can make relative +internal adjustments as necessary to avoid this resonance. + +ACPI interface +============== + +Although initially used by for wifi + dGPU use cases, the ACPI interface +can be scaled to any type of device that a platform designer discovers +can cause interference. + +The GUID used for the _DSM is 7B7656CF-DC3D-4C1C-83E9-66E721DE3070. + +3 functions are available in this _DSM: + +* 0: discover # of functions available +* 1: record RF bands in use +* 2: retrieve RF bands in use + +Driver programming interface +============================ + +.. kernel-doc:: drivers/platform/x86/amd/wbrf.c + +Sample Usage +============= + +The expected flow for the producers: +1. During probe, call `acpi_amd_wbrf_supported_producer` to check if WBRF +can be enabled for the device. +2. On using some frequency band, call `acpi_amd_wbrf_add_remove` with 'add' +param to get other consumers properly notified. +3. Or on stopping using some frequency band, call +`acpi_amd_wbrf_add_remove` with 'remove' param to get other consumers notified. + +The expected flow for the consumers: +1. During probe, call `acpi_amd_wbrf_supported_consumer` to check if WBRF +can be enabled for the device. +2. Call `amd_wbrf_register_notifier` to register for notification +of frequency band change(add or remove) from other producers. +3. Call the `amd_wbrf_retrieve_freq_band` initally to retrieve +current active frequency bands considering some producers may broadcast +such information before the consumer is up. +4. On receiving a notification for frequency band change, run +`amd_wbrf_retrieve_freq_band` again to retrieve the latest +active frequency bands. +5. During driver cleanup, call `amd_wbrf_unregister_notifier` to +unregister the notifier. -- cgit From 58e82a62669da52e688f4a8b89922c1839bf1001 Mon Sep 17 00:00:00 2001 From: Ma Jun Date: Mon, 11 Dec 2023 18:06:23 +0800 Subject: platform/x86/amd: Add support for AMD ACPI based Wifi band RFI mitigation feature Due to electrical and mechanical constraints in certain platform designs there may be likely interference of relatively high-powered harmonics of the (G-)DDR memory clocks with local radio module frequency bands used by Wifi 6/6e/7. To mitigate this, AMD has introduced a mechanism that devices can use to notify active use of particular frequencies so that other devices can make relative internal adjustments as necessary to avoid this resonance. Co-developed-by: Evan Quan Signed-off-by: Evan Quan Signed-off-by: Ma Jun Reviewed-by: Mario Limonciello Reviewed-by: Hans de Goede Signed-off-by: Hans de Goede --- drivers/platform/x86/amd/Kconfig | 14 ++ drivers/platform/x86/amd/Makefile | 1 + drivers/platform/x86/amd/wbrf.c | 317 ++++++++++++++++++++++++++++++++++++++ include/linux/acpi_amd_wbrf.h | 91 +++++++++++ 4 files changed, 423 insertions(+) create mode 100644 drivers/platform/x86/amd/wbrf.c create mode 100644 include/linux/acpi_amd_wbrf.h diff --git a/drivers/platform/x86/amd/Kconfig b/drivers/platform/x86/amd/Kconfig index 55f3a2fc6aec..54753213cc61 100644 --- a/drivers/platform/x86/amd/Kconfig +++ b/drivers/platform/x86/amd/Kconfig @@ -18,3 +18,17 @@ config AMD_HSMP If you choose to compile this driver as a module the module will be called amd_hsmp. + +config AMD_WBRF + bool "AMD Wifi RF Band mitigations (WBRF)" + depends on ACPI + help + WBRF(Wifi Band RFI mitigation) mechanism allows Wifi drivers + to notify the frequencies they are using so that other hardware + can be reconfigured to avoid harmonic conflicts. + + AMD provides an ACPI based mechanism to support WBRF on platform with + appropriate underlying support. + + This mechanism will only be activated on platforms that advertise a + need for it. diff --git a/drivers/platform/x86/amd/Makefile b/drivers/platform/x86/amd/Makefile index f04932b7a7d1..dcec0a46f8af 100644 --- a/drivers/platform/x86/amd/Makefile +++ b/drivers/platform/x86/amd/Makefile @@ -8,3 +8,4 @@ obj-$(CONFIG_AMD_PMC) += pmc/ amd_hsmp-y := hsmp.o obj-$(CONFIG_AMD_HSMP) += amd_hsmp.o obj-$(CONFIG_AMD_PMF) += pmf/ +obj-$(CONFIG_AMD_WBRF) += wbrf.o diff --git a/drivers/platform/x86/amd/wbrf.c b/drivers/platform/x86/amd/wbrf.c new file mode 100644 index 000000000000..dd197b3aebe0 --- /dev/null +++ b/drivers/platform/x86/amd/wbrf.c @@ -0,0 +1,317 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Wifi Frequency Band Manage Interface + * Copyright (C) 2023 Advanced Micro Devices + */ + +#include +#include + +/* + * Functions bit vector for WBRF method + * + * Bit 0: WBRF supported. + * Bit 1: Function 1 (Add / Remove frequency) is supported. + * Bit 2: Function 2 (Get frequency list) is supported. + */ +#define WBRF_ENABLED 0x0 +#define WBRF_RECORD 0x1 +#define WBRF_RETRIEVE 0x2 + +#define WBRF_REVISION 0x1 + +/* + * The data structure used for WBRF_RETRIEVE is not naturally aligned. + * And unfortunately the design has been settled down. + */ +struct amd_wbrf_ranges_out { + u32 num_of_ranges; + struct freq_band_range band_list[MAX_NUM_OF_WBRF_RANGES]; +} __packed; + +static const guid_t wifi_acpi_dsm_guid = + GUID_INIT(0x7b7656cf, 0xdc3d, 0x4c1c, + 0x83, 0xe9, 0x66, 0xe7, 0x21, 0xde, 0x30, 0x70); + +/* + * Used to notify consumer (amdgpu driver currently) about + * the wifi frequency is change. + */ +static BLOCKING_NOTIFIER_HEAD(wbrf_chain_head); + +static int wbrf_record(struct acpi_device *adev, uint8_t action, struct wbrf_ranges_in_out *in) +{ + union acpi_object argv4; + union acpi_object *tmp; + union acpi_object *obj; + u32 num_of_ranges = 0; + u32 num_of_elements; + u32 arg_idx = 0; + int ret; + u32 i; + + if (!in) + return -EINVAL; + + for (i = 0; i < ARRAY_SIZE(in->band_list); i++) { + if (in->band_list[i].start && in->band_list[i].end) + num_of_ranges++; + } + + /* + * The num_of_ranges value in the "in" object supplied by + * the caller is required to be equal to the number of + * entries in the band_list array in there. + */ + if (num_of_ranges != in->num_of_ranges) + return -EINVAL; + + /* + * Every input frequency band comes with two end points(start/end) + * and each is accounted as an element. Meanwhile the range count + * and action type are accounted as an element each. + * So, the total element count = 2 * num_of_ranges + 1 + 1. + */ + num_of_elements = 2 * num_of_ranges + 2; + + tmp = kcalloc(num_of_elements, sizeof(*tmp), GFP_KERNEL); + if (!tmp) + return -ENOMEM; + + argv4.package.type = ACPI_TYPE_PACKAGE; + argv4.package.count = num_of_elements; + argv4.package.elements = tmp; + + /* save the number of ranges*/ + tmp[0].integer.type = ACPI_TYPE_INTEGER; + tmp[0].integer.value = num_of_ranges; + + /* save the action(WBRF_RECORD_ADD/REMOVE/RETRIEVE) */ + tmp[1].integer.type = ACPI_TYPE_INTEGER; + tmp[1].integer.value = action; + + arg_idx = 2; + for (i = 0; i < ARRAY_SIZE(in->band_list); i++) { + if (!in->band_list[i].start || !in->band_list[i].end) + continue; + + tmp[arg_idx].integer.type = ACPI_TYPE_INTEGER; + tmp[arg_idx++].integer.value = in->band_list[i].start; + tmp[arg_idx].integer.type = ACPI_TYPE_INTEGER; + tmp[arg_idx++].integer.value = in->band_list[i].end; + } + + obj = acpi_evaluate_dsm(adev->handle, &wifi_acpi_dsm_guid, + WBRF_REVISION, WBRF_RECORD, &argv4); + + if (!obj) + return -EINVAL; + + if (obj->type != ACPI_TYPE_INTEGER) { + ret = -EINVAL; + goto out; + } + + ret = obj->integer.value; + if (ret) + ret = -EINVAL; + +out: + ACPI_FREE(obj); + kfree(tmp); + + return ret; +} + +/** + * acpi_amd_wbrf_add_remove - add or remove the frequency band the device is using + * + * @dev: device pointer + * @action: remove or add the frequency band into bios + * @in: input structure containing the frequency band the device is using + * + * Broadcast to other consumers the frequency band the device starts + * to use. Underneath the surface the information is cached into an + * internal buffer first. Then a notification is sent to all those + * registered consumers. So then they can retrieve that buffer to + * know the latest active frequency bands. Consumers that haven't + * yet been registered can retrieve the information from the cache + * when they register. + * + * Return: + * 0 for success add/remove wifi frequency band. + * Returns a negative error code for failure. + */ +int acpi_amd_wbrf_add_remove(struct device *dev, uint8_t action, struct wbrf_ranges_in_out *in) +{ + struct acpi_device *adev; + int ret; + + adev = ACPI_COMPANION(dev); + if (!adev) + return -ENODEV; + + ret = wbrf_record(adev, action, in); + if (ret) + return ret; + + blocking_notifier_call_chain(&wbrf_chain_head, WBRF_CHANGED, NULL); + + return 0; +} +EXPORT_SYMBOL_GPL(acpi_amd_wbrf_add_remove); + +/** + * acpi_amd_wbrf_supported_producer - determine if the WBRF can be enabled + * for the device as a producer + * + * @dev: device pointer + * + * Check if the platform equipped with necessary implementations to + * support WBRF for the device as a producer. + * + * Return: + * true if WBRF is supported, otherwise returns false + */ +bool acpi_amd_wbrf_supported_producer(struct device *dev) +{ + struct acpi_device *adev; + + adev = ACPI_COMPANION(dev); + if (!adev) + return false; + + return acpi_check_dsm(adev->handle, &wifi_acpi_dsm_guid, + WBRF_REVISION, BIT(WBRF_RECORD)); +} +EXPORT_SYMBOL_GPL(acpi_amd_wbrf_supported_producer); + +/** + * acpi_amd_wbrf_supported_consumer - determine if the WBRF can be enabled + * for the device as a consumer + * + * @dev: device pointer + * + * Determine if the platform equipped with necessary implementations to + * support WBRF for the device as a consumer. + * + * Return: + * true if WBRF is supported, otherwise returns false. + */ +bool acpi_amd_wbrf_supported_consumer(struct device *dev) +{ + struct acpi_device *adev; + + adev = ACPI_COMPANION(dev); + if (!adev) + return false; + + return acpi_check_dsm(adev->handle, &wifi_acpi_dsm_guid, + WBRF_REVISION, BIT(WBRF_RETRIEVE)); +} +EXPORT_SYMBOL_GPL(acpi_amd_wbrf_supported_consumer); + +/** + * amd_wbrf_retrieve_freq_band - retrieve current active frequency bands + * + * @dev: device pointer + * @out: output structure containing all the active frequency bands + * + * Retrieve the current active frequency bands which were broadcasted + * by other producers. The consumer who calls this API should take + * proper actions if any of the frequency band may cause RFI with its + * own frequency band used. + * + * Return: + * 0 for getting wifi freq band successfully. + * Returns a negative error code for failure. + */ +int amd_wbrf_retrieve_freq_band(struct device *dev, struct wbrf_ranges_in_out *out) +{ + struct amd_wbrf_ranges_out acpi_out = {0}; + struct acpi_device *adev; + union acpi_object *obj; + union acpi_object param; + int ret = 0; + + adev = ACPI_COMPANION(dev); + if (!adev) + return -ENODEV; + + param.type = ACPI_TYPE_STRING; + param.string.length = 0; + param.string.pointer = NULL; + + obj = acpi_evaluate_dsm(adev->handle, &wifi_acpi_dsm_guid, + WBRF_REVISION, WBRF_RETRIEVE, ¶m); + if (!obj) + return -EINVAL; + + /* + * The return buffer is with variable length and the format below: + * number_of_entries(1 DWORD): Number of entries + * start_freq of 1st entry(1 QWORD): Start frequency of the 1st entry + * end_freq of 1st entry(1 QWORD): End frequency of the 1st entry + * ... + * ... + * start_freq of the last entry(1 QWORD) + * end_freq of the last entry(1 QWORD) + * + * Thus the buffer length is determined by the number of entries. + * - For zero entry scenario, the buffer length will be 4 bytes. + * - For one entry scenario, the buffer length will be 20 bytes. + */ + if (obj->buffer.length > sizeof(acpi_out) || obj->buffer.length < 4) { + dev_err(dev, "Wrong sized WBRT information"); + ret = -EINVAL; + goto out; + } + memcpy(&acpi_out, obj->buffer.pointer, obj->buffer.length); + + out->num_of_ranges = acpi_out.num_of_ranges; + memcpy(out->band_list, acpi_out.band_list, sizeof(acpi_out.band_list)); + +out: + ACPI_FREE(obj); + return ret; +} +EXPORT_SYMBOL_GPL(amd_wbrf_retrieve_freq_band); + +/** + * amd_wbrf_register_notifier - register for notifications of frequency + * band update + * + * @nb: driver notifier block + * + * The consumer should register itself via this API so that it can get + * notified on the frequency band updates from other producers. + * + * Return: + * 0 for registering a consumer driver successfully. + * Returns a negative error code for failure. + */ +int amd_wbrf_register_notifier(struct notifier_block *nb) +{ + return blocking_notifier_chain_register(&wbrf_chain_head, nb); +} +EXPORT_SYMBOL_GPL(amd_wbrf_register_notifier); + +/** + * amd_wbrf_unregister_notifier - unregister for notifications of + * frequency band update + * + * @nb: driver notifier block + * + * The consumer should call this API when it is longer interested with + * the frequency band updates from other producers. Usually, this should + * be performed during driver cleanup. + * + * Return: + * 0 for unregistering a consumer driver. + * Returns a negative error code for failure. + */ +int amd_wbrf_unregister_notifier(struct notifier_block *nb) +{ + return blocking_notifier_chain_unregister(&wbrf_chain_head, nb); +} +EXPORT_SYMBOL_GPL(amd_wbrf_unregister_notifier); diff --git a/include/linux/acpi_amd_wbrf.h b/include/linux/acpi_amd_wbrf.h new file mode 100644 index 000000000000..898f31d536d4 --- /dev/null +++ b/include/linux/acpi_amd_wbrf.h @@ -0,0 +1,91 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Wifi Band Exclusion Interface (AMD ACPI Implementation) + * Copyright (C) 2023 Advanced Micro Devices + */ + +#ifndef _ACPI_AMD_WBRF_H +#define _ACPI_AMD_WBRF_H + +#include +#include + +/* The maximum number of frequency band ranges */ +#define MAX_NUM_OF_WBRF_RANGES 11 + +/* Record actions */ +#define WBRF_RECORD_ADD 0x0 +#define WBRF_RECORD_REMOVE 0x1 + +/** + * struct freq_band_range - Wifi frequency band range definition + * @start: start frequency point (in Hz) + * @end: end frequency point (in Hz) + */ +struct freq_band_range { + u64 start; + u64 end; +}; + +/** + * struct wbrf_ranges_in_out - wbrf ranges info + * @num_of_ranges: total number of band ranges in this struct + * @band_list: array of Wifi band ranges + */ +struct wbrf_ranges_in_out { + u64 num_of_ranges; + struct freq_band_range band_list[MAX_NUM_OF_WBRF_RANGES]; +}; + +/** + * enum wbrf_notifier_actions - wbrf notifier actions index + * @WBRF_CHANGED: there was some frequency band updates. The consumers + * should retrieve the latest active frequency bands. + */ +enum wbrf_notifier_actions { + WBRF_CHANGED, +}; + +#if IS_ENABLED(CONFIG_AMD_WBRF) +bool acpi_amd_wbrf_supported_producer(struct device *dev); +int acpi_amd_wbrf_add_remove(struct device *dev, uint8_t action, struct wbrf_ranges_in_out *in); +bool acpi_amd_wbrf_supported_consumer(struct device *dev); +int amd_wbrf_retrieve_freq_band(struct device *dev, struct wbrf_ranges_in_out *out); +int amd_wbrf_register_notifier(struct notifier_block *nb); +int amd_wbrf_unregister_notifier(struct notifier_block *nb); +#else +static inline +bool acpi_amd_wbrf_supported_consumer(struct device *dev) +{ + return false; +} + +static inline +int acpi_amd_wbrf_add_remove(struct device *dev, uint8_t action, struct wbrf_ranges_in_out *in) +{ + return -ENODEV; +} + +static inline +bool acpi_amd_wbrf_supported_producer(struct device *dev) +{ + return false; +} +static inline +int amd_wbrf_retrieve_freq_band(struct device *dev, struct wbrf_ranges_in_out *out) +{ + return -ENODEV; +} +static inline +int amd_wbrf_register_notifier(struct notifier_block *nb) +{ + return -ENODEV; +} +static inline +int amd_wbrf_unregister_notifier(struct notifier_block *nb) +{ + return -ENODEV; +} +#endif /* CONFIG_AMD_WBRF */ + +#endif /* _ACPI_AMD_WBRF_H */ -- cgit From ac052d8c08f9da225bea09c7e71527831368462b Mon Sep 17 00:00:00 2001 From: Shyam Sundar S K Date: Tue, 12 Dec 2023 07:16:54 +0530 Subject: platform/x86/amd/pmf: Add PMF TEE interface AMD PMF driver loads the PMF TA (Trusted Application) into the AMD ASP's (AMD Security Processor) TEE (Trusted Execution Environment). PMF Trusted Application is a secured firmware placed under /lib/firmware/amdtee gets loaded only when the TEE environment is initialized. Add the initial code path to build these pipes. Reviewed-by: Mario Limonciello Signed-off-by: Shyam Sundar S K Link: https://lore.kernel.org/r/20231212014705.2017474-2-Shyam-sundar.S-k@amd.com Signed-off-by: Hans de Goede --- drivers/platform/x86/amd/pmf/Kconfig | 1 + drivers/platform/x86/amd/pmf/Makefile | 3 +- drivers/platform/x86/amd/pmf/core.c | 10 ++-- drivers/platform/x86/amd/pmf/pmf.h | 16 ++++++ drivers/platform/x86/amd/pmf/tee-if.c | 105 ++++++++++++++++++++++++++++++++++ 5 files changed, 130 insertions(+), 5 deletions(-) create mode 100644 drivers/platform/x86/amd/pmf/tee-if.c diff --git a/drivers/platform/x86/amd/pmf/Kconfig b/drivers/platform/x86/amd/pmf/Kconfig index 3064bc8ea167..32a029e8db80 100644 --- a/drivers/platform/x86/amd/pmf/Kconfig +++ b/drivers/platform/x86/amd/pmf/Kconfig @@ -9,6 +9,7 @@ config AMD_PMF depends on POWER_SUPPLY depends on AMD_NB select ACPI_PLATFORM_PROFILE + depends on TEE help This driver provides support for the AMD Platform Management Framework. The goal is to enhance end user experience by making AMD PCs smarter, diff --git a/drivers/platform/x86/amd/pmf/Makefile b/drivers/platform/x86/amd/pmf/Makefile index fdededf54392..d2746ee7369f 100644 --- a/drivers/platform/x86/amd/pmf/Makefile +++ b/drivers/platform/x86/amd/pmf/Makefile @@ -6,4 +6,5 @@ obj-$(CONFIG_AMD_PMF) += amd-pmf.o amd-pmf-objs := core.o acpi.o sps.o \ - auto-mode.o cnqf.o + auto-mode.o cnqf.o \ + tee-if.o diff --git a/drivers/platform/x86/amd/pmf/core.c b/drivers/platform/x86/amd/pmf/core.c index 78ed3ee22555..ec92d1cc0dac 100644 --- a/drivers/platform/x86/amd/pmf/core.c +++ b/drivers/platform/x86/amd/pmf/core.c @@ -309,13 +309,13 @@ static void amd_pmf_init_features(struct amd_pmf_dev *dev) dev_dbg(dev->dev, "SPS enabled and Platform Profiles registered\n"); } - /* Enable Auto Mode */ - if (is_apmf_func_supported(dev, APMF_FUNC_AUTO_MODE)) { + if (!amd_pmf_init_smart_pc(dev)) { + dev_dbg(dev->dev, "Smart PC Solution Enabled\n"); + } else if (is_apmf_func_supported(dev, APMF_FUNC_AUTO_MODE)) { amd_pmf_init_auto_mode(dev); dev_dbg(dev->dev, "Auto Mode Init done\n"); } else if (is_apmf_func_supported(dev, APMF_FUNC_DYN_SLIDER_AC) || is_apmf_func_supported(dev, APMF_FUNC_DYN_SLIDER_DC)) { - /* Enable Cool n Quiet Framework (CnQF) */ ret = amd_pmf_init_cnqf(dev); if (ret) dev_warn(dev->dev, "CnQF Init failed\n"); @@ -330,7 +330,9 @@ static void amd_pmf_deinit_features(struct amd_pmf_dev *dev) amd_pmf_deinit_sps(dev); } - if (is_apmf_func_supported(dev, APMF_FUNC_AUTO_MODE)) { + if (!dev->smart_pc_enabled) { + amd_pmf_deinit_smart_pc(dev); + } else if (is_apmf_func_supported(dev, APMF_FUNC_AUTO_MODE)) { amd_pmf_deinit_auto_mode(dev); } else if (is_apmf_func_supported(dev, APMF_FUNC_DYN_SLIDER_AC) || is_apmf_func_supported(dev, APMF_FUNC_DYN_SLIDER_DC)) { diff --git a/drivers/platform/x86/amd/pmf/pmf.h b/drivers/platform/x86/amd/pmf/pmf.h index deba88e6e4c8..bd40458937ba 100644 --- a/drivers/platform/x86/amd/pmf/pmf.h +++ b/drivers/platform/x86/amd/pmf/pmf.h @@ -179,6 +179,12 @@ struct amd_pmf_dev { bool cnqf_enabled; bool cnqf_supported; struct notifier_block pwr_src_notifier; + /* Smart PC solution builder */ + struct tee_context *tee_ctx; + struct tee_shm *fw_shm_pool; + u32 session_id; + void *shbuf; + bool smart_pc_enabled; }; struct apmf_sps_prop_granular { @@ -389,6 +395,13 @@ struct apmf_dyn_slider_output { struct apmf_cnqf_power_set ps[APMF_CNQF_MAX]; } __packed; +struct ta_pmf_shared_memory { + int command_id; + int resp_id; + u32 pmf_result; + u32 if_version; +}; + /* Core Layer */ int apmf_acpi_init(struct amd_pmf_dev *pmf_dev); void apmf_acpi_deinit(struct amd_pmf_dev *pmf_dev); @@ -433,4 +446,7 @@ void amd_pmf_deinit_cnqf(struct amd_pmf_dev *dev); int amd_pmf_trans_cnqf(struct amd_pmf_dev *dev, int socket_power, ktime_t time_lapsed_ms); extern const struct attribute_group cnqf_feature_attribute_group; +/* Smart PC builder Layer */ +int amd_pmf_init_smart_pc(struct amd_pmf_dev *dev); +void amd_pmf_deinit_smart_pc(struct amd_pmf_dev *dev); #endif /* PMF_H */ diff --git a/drivers/platform/x86/amd/pmf/tee-if.c b/drivers/platform/x86/amd/pmf/tee-if.c new file mode 100644 index 000000000000..6ec8c3726624 --- /dev/null +++ b/drivers/platform/x86/amd/pmf/tee-if.c @@ -0,0 +1,105 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * AMD Platform Management Framework Driver - TEE Interface + * + * Copyright (c) 2023, Advanced Micro Devices, Inc. + * All Rights Reserved. + * + * Author: Shyam Sundar S K + */ + +#include +#include +#include "pmf.h" + +#define MAX_TEE_PARAM 4 +static const uuid_t amd_pmf_ta_uuid = UUID_INIT(0x6fd93b77, 0x3fb8, 0x524d, + 0xb1, 0x2d, 0xc5, 0x29, 0xb1, 0x3d, 0x85, 0x43); + +static int amd_pmf_amdtee_ta_match(struct tee_ioctl_version_data *ver, const void *data) +{ + return ver->impl_id == TEE_IMPL_ID_AMDTEE; +} + +static int amd_pmf_ta_open_session(struct tee_context *ctx, u32 *id) +{ + struct tee_ioctl_open_session_arg sess_arg = {}; + int rc; + + export_uuid(sess_arg.uuid, &amd_pmf_ta_uuid); + sess_arg.clnt_login = TEE_IOCTL_LOGIN_PUBLIC; + sess_arg.num_params = 0; + + rc = tee_client_open_session(ctx, &sess_arg, NULL); + if (rc < 0 || sess_arg.ret != 0) { + pr_err("Failed to open TEE session err:%#x, rc:%d\n", sess_arg.ret, rc); + return rc; + } + + *id = sess_arg.session; + + return rc; +} + +static int amd_pmf_tee_init(struct amd_pmf_dev *dev) +{ + u32 size; + int ret; + + dev->tee_ctx = tee_client_open_context(NULL, amd_pmf_amdtee_ta_match, NULL, NULL); + if (IS_ERR(dev->tee_ctx)) { + dev_err(dev->dev, "Failed to open TEE context\n"); + return PTR_ERR(dev->tee_ctx); + } + + ret = amd_pmf_ta_open_session(dev->tee_ctx, &dev->session_id); + if (ret) { + dev_err(dev->dev, "Failed to open TA session (%d)\n", ret); + ret = -EINVAL; + goto out_ctx; + } + + size = sizeof(struct ta_pmf_shared_memory); + dev->fw_shm_pool = tee_shm_alloc_kernel_buf(dev->tee_ctx, size); + if (IS_ERR(dev->fw_shm_pool)) { + dev_err(dev->dev, "Failed to alloc TEE shared memory\n"); + ret = PTR_ERR(dev->fw_shm_pool); + goto out_sess; + } + + dev->shbuf = tee_shm_get_va(dev->fw_shm_pool, 0); + if (IS_ERR(dev->shbuf)) { + dev_err(dev->dev, "Failed to get TEE virtual address\n"); + ret = PTR_ERR(dev->shbuf); + goto out_shm; + } + dev_dbg(dev->dev, "TEE init done\n"); + + return 0; + +out_shm: + tee_shm_free(dev->fw_shm_pool); +out_sess: + tee_client_close_session(dev->tee_ctx, dev->session_id); +out_ctx: + tee_client_close_context(dev->tee_ctx); + + return ret; +} + +static void amd_pmf_tee_deinit(struct amd_pmf_dev *dev) +{ + tee_shm_free(dev->fw_shm_pool); + tee_client_close_session(dev->tee_ctx, dev->session_id); + tee_client_close_context(dev->tee_ctx); +} + +int amd_pmf_init_smart_pc(struct amd_pmf_dev *dev) +{ + return amd_pmf_tee_init(dev); +} + +void amd_pmf_deinit_smart_pc(struct amd_pmf_dev *dev) +{ + amd_pmf_tee_deinit(dev); +} -- cgit From ae82cef7d9c53cad0852d2d79d430b210432a025 Mon Sep 17 00:00:00 2001 From: Shyam Sundar S K Date: Tue, 12 Dec 2023 07:16:55 +0530 Subject: platform/x86/amd/pmf: Add support for PMF-TA interaction PMF TA (Trusted Application) loads via the TEE environment into the AMD ASP. PMF-TA supports two commands: 1) Init: Initialize the TA with the PMF Smart PC policy binary and start the policy engine. A policy is a combination of inputs and outputs, where; - the inputs are the changing dynamics of the system like the user behaviour, system heuristics etc. - the outputs, which are the actions to be set on the system which lead to better power management and enhanced user experience. PMF driver acts as a central manager in this case to supply the inputs required to the TA (either by getting the information from the other kernel subsystems or from userland) 2) Enact: Enact the output actions from the TA. The action could be applying a new thermal limit to boost/throttle the power limits or change system behavior. Reviewed-by: Mario Limonciello Signed-off-by: Shyam Sundar S K Link: https://lore.kernel.org/r/20231212014705.2017474-3-Shyam-sundar.S-k@amd.com Signed-off-by: Hans de Goede --- drivers/platform/x86/amd/pmf/pmf.h | 10 ++++ drivers/platform/x86/amd/pmf/tee-if.c | 97 ++++++++++++++++++++++++++++++++++- 2 files changed, 106 insertions(+), 1 deletion(-) diff --git a/drivers/platform/x86/amd/pmf/pmf.h b/drivers/platform/x86/amd/pmf/pmf.h index bd40458937ba..a24e34e42032 100644 --- a/drivers/platform/x86/amd/pmf/pmf.h +++ b/drivers/platform/x86/amd/pmf/pmf.h @@ -59,6 +59,9 @@ #define ARG_NONE 0 #define AVG_SAMPLE_SIZE 3 +/* TA macros */ +#define PMF_TA_IF_VERSION_MAJOR 1 + /* AMD PMF BIOS interfaces */ struct apmf_verify_interface { u16 size; @@ -184,6 +187,7 @@ struct amd_pmf_dev { struct tee_shm *fw_shm_pool; u32 session_id; void *shbuf; + struct delayed_work pb_work; bool smart_pc_enabled; }; @@ -395,6 +399,12 @@ struct apmf_dyn_slider_output { struct apmf_cnqf_power_set ps[APMF_CNQF_MAX]; } __packed; +/* Command ids for TA communication */ +enum ta_pmf_command { + TA_PMF_COMMAND_POLICY_BUILDER_INITIALIZE, + TA_PMF_COMMAND_POLICY_BUILDER_ENACT_POLICIES, +}; + struct ta_pmf_shared_memory { int command_id; int resp_id; diff --git a/drivers/platform/x86/amd/pmf/tee-if.c b/drivers/platform/x86/amd/pmf/tee-if.c index 6ec8c3726624..4036f435f1e2 100644 --- a/drivers/platform/x86/amd/pmf/tee-if.c +++ b/drivers/platform/x86/amd/pmf/tee-if.c @@ -13,9 +13,96 @@ #include "pmf.h" #define MAX_TEE_PARAM 4 + +/* Policy binary actions sampling frequency (in ms) */ +static int pb_actions_ms = MSEC_PER_SEC; +#ifdef CONFIG_AMD_PMF_DEBUG +module_param(pb_actions_ms, int, 0644); +MODULE_PARM_DESC(pb_actions_ms, "Policy binary actions sampling frequency (default = 1000ms)"); +#endif + static const uuid_t amd_pmf_ta_uuid = UUID_INIT(0x6fd93b77, 0x3fb8, 0x524d, 0xb1, 0x2d, 0xc5, 0x29, 0xb1, 0x3d, 0x85, 0x43); +static void amd_pmf_prepare_args(struct amd_pmf_dev *dev, int cmd, + struct tee_ioctl_invoke_arg *arg, + struct tee_param *param) +{ + memset(arg, 0, sizeof(*arg)); + memset(param, 0, MAX_TEE_PARAM * sizeof(*param)); + + arg->func = cmd; + arg->session = dev->session_id; + arg->num_params = MAX_TEE_PARAM; + + /* Fill invoke cmd params */ + param[0].u.memref.size = sizeof(struct ta_pmf_shared_memory); + param[0].attr = TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_INOUT; + param[0].u.memref.shm = dev->fw_shm_pool; + param[0].u.memref.shm_offs = 0; +} + +static int amd_pmf_invoke_cmd_enact(struct amd_pmf_dev *dev) +{ + struct ta_pmf_shared_memory *ta_sm = NULL; + struct tee_param param[MAX_TEE_PARAM]; + struct tee_ioctl_invoke_arg arg; + int ret = 0; + + if (!dev->tee_ctx) + return -ENODEV; + + ta_sm = dev->shbuf; + memset(ta_sm, 0, sizeof(*ta_sm)); + ta_sm->command_id = TA_PMF_COMMAND_POLICY_BUILDER_ENACT_POLICIES; + ta_sm->if_version = PMF_TA_IF_VERSION_MAJOR; + + amd_pmf_prepare_args(dev, TA_PMF_COMMAND_POLICY_BUILDER_ENACT_POLICIES, &arg, param); + + ret = tee_client_invoke_func(dev->tee_ctx, &arg, param); + if (ret < 0 || arg.ret != 0) { + dev_err(dev->dev, "TEE enact cmd failed. err: %x, ret:%d\n", arg.ret, ret); + return ret; + } + + return 0; +} + +static int amd_pmf_invoke_cmd_init(struct amd_pmf_dev *dev) +{ + struct ta_pmf_shared_memory *ta_sm = NULL; + struct tee_param param[MAX_TEE_PARAM]; + struct tee_ioctl_invoke_arg arg; + int ret = 0; + + if (!dev->tee_ctx) { + dev_err(dev->dev, "Failed to get TEE context\n"); + return -ENODEV; + } + + ta_sm = dev->shbuf; + ta_sm->command_id = TA_PMF_COMMAND_POLICY_BUILDER_INITIALIZE; + ta_sm->if_version = PMF_TA_IF_VERSION_MAJOR; + + amd_pmf_prepare_args(dev, TA_PMF_COMMAND_POLICY_BUILDER_INITIALIZE, &arg, param); + + ret = tee_client_invoke_func(dev->tee_ctx, &arg, param); + if (ret < 0 || arg.ret != 0) { + dev_err(dev->dev, "Failed to invoke TEE init cmd. err: %x, ret:%d\n", arg.ret, ret); + return ret; + } + + return ta_sm->pmf_result; +} + +static void amd_pmf_invoke_cmd(struct work_struct *work) +{ + struct amd_pmf_dev *dev = container_of(work, struct amd_pmf_dev, pb_work.work); + + amd_pmf_invoke_cmd_enact(dev); + schedule_delayed_work(&dev->pb_work, msecs_to_jiffies(pb_actions_ms)); +} + static int amd_pmf_amdtee_ta_match(struct tee_ioctl_version_data *ver, const void *data) { return ver->impl_id == TEE_IMPL_ID_AMDTEE; @@ -96,10 +183,18 @@ static void amd_pmf_tee_deinit(struct amd_pmf_dev *dev) int amd_pmf_init_smart_pc(struct amd_pmf_dev *dev) { - return amd_pmf_tee_init(dev); + int ret; + + ret = amd_pmf_tee_init(dev); + if (ret) + return ret; + + INIT_DELAYED_WORK(&dev->pb_work, amd_pmf_invoke_cmd); + return 0; } void amd_pmf_deinit_smart_pc(struct amd_pmf_dev *dev) { + cancel_delayed_work_sync(&dev->pb_work); amd_pmf_tee_deinit(dev); } -- cgit From 2b3a7f06caaf1aa7379cc0233462799852fcd8b4 Mon Sep 17 00:00:00 2001 From: Shyam Sundar S K Date: Tue, 12 Dec 2023 07:16:56 +0530 Subject: platform/x86/amd/pmf: Change return type of amd_pmf_set_dram_addr() In the current code, the metrics table information was required only for auto-mode or CnQF at a given time. Hence keeping the return type of amd_pmf_set_dram_addr() as static made sense. But with the addition of Smart PC builder feature, the metrics table information has to be shared by the Smart PC also and this feature resides outside of core.c. To make amd_pmf_set_dram_addr() visible outside of core.c make it as a non-static function and move the allocation of memory for metrics table from amd_pmf_init_metrics_table() to amd_pmf_set_dram_addr() as amd_pmf_set_dram_addr() is the common function to set the DRAM address. Add a suspend handler that can free up the allocated memory for getting the metrics table information. Reviewed-by: Mario Limonciello Signed-off-by: Shyam Sundar S K Link: https://lore.kernel.org/r/20231212014705.2017474-4-Shyam-sundar.S-k@amd.com Signed-off-by: Hans de Goede --- drivers/platform/x86/amd/pmf/core.c | 39 ++++++++++++++++++++++++++++--------- drivers/platform/x86/amd/pmf/pmf.h | 1 + 2 files changed, 31 insertions(+), 9 deletions(-) diff --git a/drivers/platform/x86/amd/pmf/core.c b/drivers/platform/x86/amd/pmf/core.c index ec92d1cc0dac..9953619a830b 100644 --- a/drivers/platform/x86/amd/pmf/core.c +++ b/drivers/platform/x86/amd/pmf/core.c @@ -251,29 +251,37 @@ static const struct pci_device_id pmf_pci_ids[] = { { } }; -static void amd_pmf_set_dram_addr(struct amd_pmf_dev *dev) +int amd_pmf_set_dram_addr(struct amd_pmf_dev *dev, bool alloc_buffer) { u64 phys_addr; u32 hi, low; + /* Get Metrics Table Address */ + if (alloc_buffer) { + dev->buf = kzalloc(sizeof(dev->m_table), GFP_KERNEL); + if (!dev->buf) + return -ENOMEM; + } + phys_addr = virt_to_phys(dev->buf); hi = phys_addr >> 32; low = phys_addr & GENMASK(31, 0); amd_pmf_send_cmd(dev, SET_DRAM_ADDR_HIGH, 0, hi, NULL); amd_pmf_send_cmd(dev, SET_DRAM_ADDR_LOW, 0, low, NULL); + + return 0; } int amd_pmf_init_metrics_table(struct amd_pmf_dev *dev) { - /* Get Metrics Table Address */ - dev->buf = kzalloc(sizeof(dev->m_table), GFP_KERNEL); - if (!dev->buf) - return -ENOMEM; + int ret; INIT_DELAYED_WORK(&dev->work_buffer, amd_pmf_get_metrics); - amd_pmf_set_dram_addr(dev); + ret = amd_pmf_set_dram_addr(dev, true); + if (ret) + return ret; /* * Start collecting the metrics data after a small delay @@ -284,17 +292,30 @@ int amd_pmf_init_metrics_table(struct amd_pmf_dev *dev) return 0; } +static int amd_pmf_suspend_handler(struct device *dev) +{ + struct amd_pmf_dev *pdev = dev_get_drvdata(dev); + + kfree(pdev->buf); + + return 0; +} + static int amd_pmf_resume_handler(struct device *dev) { struct amd_pmf_dev *pdev = dev_get_drvdata(dev); + int ret; - if (pdev->buf) - amd_pmf_set_dram_addr(pdev); + if (pdev->buf) { + ret = amd_pmf_set_dram_addr(pdev, false); + if (ret) + return ret; + } return 0; } -static DEFINE_SIMPLE_DEV_PM_OPS(amd_pmf_pm, NULL, amd_pmf_resume_handler); +static DEFINE_SIMPLE_DEV_PM_OPS(amd_pmf_pm, amd_pmf_suspend_handler, amd_pmf_resume_handler); static void amd_pmf_init_features(struct amd_pmf_dev *dev) { diff --git a/drivers/platform/x86/amd/pmf/pmf.h b/drivers/platform/x86/amd/pmf/pmf.h index a24e34e42032..6c1aba5d2027 100644 --- a/drivers/platform/x86/amd/pmf/pmf.h +++ b/drivers/platform/x86/amd/pmf/pmf.h @@ -421,6 +421,7 @@ int amd_pmf_init_metrics_table(struct amd_pmf_dev *dev); int amd_pmf_get_power_source(void); int apmf_install_handler(struct amd_pmf_dev *pmf_dev); int apmf_os_power_slider_update(struct amd_pmf_dev *dev, u8 flag); +int amd_pmf_set_dram_addr(struct amd_pmf_dev *dev, bool alloc_buffer); /* SPS Layer */ int amd_pmf_get_pprof_modes(struct amd_pmf_dev *pmf); -- cgit From 7c45534afa4435c9fceeeb8ca33c0fdc269c2240 Mon Sep 17 00:00:00 2001 From: Shyam Sundar S K Date: Tue, 12 Dec 2023 07:16:57 +0530 Subject: platform/x86/amd/pmf: Add support for PMF Policy Binary PMF Policy binary is a encrypted and signed binary that will be part of the BIOS. PMF driver via the ACPI interface checks the existence of Smart PC bit. If the advertised bit is found, PMF driver walks the acpi namespace to find out the policy binary size and the address which has to be passed to the TA during the TA init sequence. The policy binary is comprised of inputs (or the events) and outputs (or the actions). With the PMF ecosystem, OEMs generate the policy binary (or could be multiple binaries) that contains a supported set of inputs and outputs which could be specifically carved out for each usage segment (or for each user also) that could influence the system behavior either by enriching the user experience or/and boost/throttle power limits. Once the TA init command succeeds, the PMF driver sends the changing events in the current environment to the TA for a constant sampling frequency time (the event here could be a lid close or open) and if the policy binary has corresponding action built within it, the TA sends the action for it in the subsequent enact command. If the inputs sent to the TA has no output defined in the policy binary generated by OEMs, there will be no action to be performed by the PMF driver. Example policies: 1) if slider is performance ; set the SPL to 40W Here PMF driver registers with the platform profile interface and when the slider position is changed, PMF driver lets the TA know about this. TA sends back an action to update the Sustained Power Limit (SPL). PMF driver updates this limit via the PMFW mailbox. 2) if user_away ; then lock the system Here PMF driver hooks to the AMD SFH driver to know the user presence and send the inputs to TA and if the condition is met, the TA sends the action of locking the system. PMF driver generates a uevent and based on the udev rule in the userland the system gets locked with systemctl. The intent here is to provide the OEM's to make a policy to lock the system when the user is away ; but the userland can make a choice to ignore it. The OEMs will have an utility to create numerous such policies and the policies shall be reviewed by AMD before signing and encrypting them. Policies are shared between operating systems to have seemless user experience. Since all this action has to happen via the "amdtee" driver, currently there is no caller for it in the kernel which can load the amdtee driver. Without amdtee driver loading onto the system the "tee" calls shall fail from the PMF driver. Hence an explicit MODULE_SOFTDEP has been added to address this. Signed-off-by: Shyam Sundar S K Link: https://lore.kernel.org/r/20231212014705.2017474-5-Shyam-sundar.S-k@amd.com Signed-off-by: Hans de Goede --- drivers/platform/x86/amd/pmf/Kconfig | 2 +- drivers/platform/x86/amd/pmf/acpi.c | 37 ++++++++ drivers/platform/x86/amd/pmf/core.c | 1 + drivers/platform/x86/amd/pmf/pmf.h | 141 ++++++++++++++++++++++++++++++ drivers/platform/x86/amd/pmf/tee-if.c | 158 +++++++++++++++++++++++++++++++++- 5 files changed, 336 insertions(+), 3 deletions(-) diff --git a/drivers/platform/x86/amd/pmf/Kconfig b/drivers/platform/x86/amd/pmf/Kconfig index 32a029e8db80..f246252bddd8 100644 --- a/drivers/platform/x86/amd/pmf/Kconfig +++ b/drivers/platform/x86/amd/pmf/Kconfig @@ -9,7 +9,7 @@ config AMD_PMF depends on POWER_SUPPLY depends on AMD_NB select ACPI_PLATFORM_PROFILE - depends on TEE + depends on TEE && AMDTEE help This driver provides support for the AMD Platform Management Framework. The goal is to enhance end user experience by making AMD PCs smarter, diff --git a/drivers/platform/x86/amd/pmf/acpi.c b/drivers/platform/x86/amd/pmf/acpi.c index 3fc5e4547d9f..4ec7957eb707 100644 --- a/drivers/platform/x86/amd/pmf/acpi.c +++ b/drivers/platform/x86/amd/pmf/acpi.c @@ -286,6 +286,43 @@ int apmf_install_handler(struct amd_pmf_dev *pmf_dev) return 0; } +static acpi_status apmf_walk_resources(struct acpi_resource *res, void *data) +{ + struct amd_pmf_dev *dev = data; + + switch (res->type) { + case ACPI_RESOURCE_TYPE_ADDRESS64: + dev->policy_addr = res->data.address64.address.minimum; + dev->policy_sz = res->data.address64.address.address_length; + break; + case ACPI_RESOURCE_TYPE_FIXED_MEMORY32: + dev->policy_addr = res->data.fixed_memory32.address; + dev->policy_sz = res->data.fixed_memory32.address_length; + break; + } + + if (!dev->policy_addr || dev->policy_sz > POLICY_BUF_MAX_SZ || dev->policy_sz == 0) { + pr_err("Incorrect Policy params, possibly a SBIOS bug\n"); + return AE_ERROR; + } + + return AE_OK; +} + +int apmf_check_smart_pc(struct amd_pmf_dev *pmf_dev) +{ + acpi_handle ahandle = ACPI_HANDLE(pmf_dev->dev); + acpi_status status; + + status = acpi_walk_resources(ahandle, METHOD_NAME__CRS, apmf_walk_resources, pmf_dev); + if (ACPI_FAILURE(status)) { + dev_err(pmf_dev->dev, "acpi_walk_resources failed :%d\n", status); + return -EINVAL; + } + + return 0; +} + void apmf_acpi_deinit(struct amd_pmf_dev *pmf_dev) { acpi_handle ahandle = ACPI_HANDLE(pmf_dev->dev); diff --git a/drivers/platform/x86/amd/pmf/core.c b/drivers/platform/x86/amd/pmf/core.c index 9953619a830b..c10d40b33667 100644 --- a/drivers/platform/x86/amd/pmf/core.c +++ b/drivers/platform/x86/amd/pmf/core.c @@ -471,3 +471,4 @@ module_platform_driver(amd_pmf_driver); MODULE_LICENSE("GPL"); MODULE_DESCRIPTION("AMD Platform Management Framework Driver"); +MODULE_SOFTDEP("pre: amdtee"); diff --git a/drivers/platform/x86/amd/pmf/pmf.h b/drivers/platform/x86/amd/pmf/pmf.h index 6c1aba5d2027..031e6d3ebd4d 100644 --- a/drivers/platform/x86/amd/pmf/pmf.h +++ b/drivers/platform/x86/amd/pmf/pmf.h @@ -14,6 +14,11 @@ #include #include +#define POLICY_BUF_MAX_SZ 0x4b000 +#define POLICY_SIGN_COOKIE 0x31535024 +#define POLICY_COOKIE_OFFSET 0x10 +#define POLICY_COOKIE_LEN 0x14 + /* APMF Functions */ #define APMF_FUNC_VERIFY_INTERFACE 0 #define APMF_FUNC_GET_SYS_PARAMS 1 @@ -59,8 +64,21 @@ #define ARG_NONE 0 #define AVG_SAMPLE_SIZE 3 +/* Policy Actions */ +#define PMF_POLICY_SPL 2 +#define PMF_POLICY_SPPT 3 +#define PMF_POLICY_FPPT 4 +#define PMF_POLICY_SPPT_APU_ONLY 5 +#define PMF_POLICY_STT_MIN 6 +#define PMF_POLICY_STT_SKINTEMP_APU 7 +#define PMF_POLICY_STT_SKINTEMP_HS2 8 + /* TA macros */ #define PMF_TA_IF_VERSION_MAJOR 1 +#define TA_PMF_ACTION_MAX 32 +#define TA_PMF_UNDO_MAX 8 +#define TA_OUTPUT_RESERVED_MEM 906 +#define MAX_OPERATION_PARAMS 4 /* AMD PMF BIOS interfaces */ struct apmf_verify_interface { @@ -183,11 +201,16 @@ struct amd_pmf_dev { bool cnqf_supported; struct notifier_block pwr_src_notifier; /* Smart PC solution builder */ + unsigned char *policy_buf; + u32 policy_sz; struct tee_context *tee_ctx; struct tee_shm *fw_shm_pool; u32 session_id; void *shbuf; struct delayed_work pb_work; + struct pmf_action_table *prev_data; + u64 policy_addr; + void *policy_base; bool smart_pc_enabled; }; @@ -399,17 +422,134 @@ struct apmf_dyn_slider_output { struct apmf_cnqf_power_set ps[APMF_CNQF_MAX]; } __packed; +enum smart_pc_status { + PMF_SMART_PC_ENABLED, + PMF_SMART_PC_DISABLED, +}; + +/* Smart PC - TA internals */ +enum ta_slider { + TA_BEST_BATTERY, + TA_BETTER_BATTERY, + TA_BETTER_PERFORMANCE, + TA_BEST_PERFORMANCE, + TA_MAX, +}; + /* Command ids for TA communication */ enum ta_pmf_command { TA_PMF_COMMAND_POLICY_BUILDER_INITIALIZE, TA_PMF_COMMAND_POLICY_BUILDER_ENACT_POLICIES, }; +enum ta_pmf_error_type { + TA_PMF_TYPE_SUCCESS, + TA_PMF_ERROR_TYPE_GENERIC, + TA_PMF_ERROR_TYPE_CRYPTO, + TA_PMF_ERROR_TYPE_CRYPTO_VALIDATE, + TA_PMF_ERROR_TYPE_CRYPTO_VERIFY_OEM, + TA_PMF_ERROR_TYPE_POLICY_BUILDER, + TA_PMF_ERROR_TYPE_PB_CONVERT, + TA_PMF_ERROR_TYPE_PB_SETUP, + TA_PMF_ERROR_TYPE_PB_ENACT, + TA_PMF_ERROR_TYPE_ASD_GET_DEVICE_INFO, + TA_PMF_ERROR_TYPE_ASD_GET_DEVICE_PCIE_INFO, + TA_PMF_ERROR_TYPE_SYS_DRV_FW_VALIDATION, + TA_PMF_ERROR_TYPE_MAX, +}; + +struct pmf_action_table { + u32 spl; /* in mW */ + u32 sppt; /* in mW */ + u32 sppt_apuonly; /* in mW */ + u32 fppt; /* in mW */ + u32 stt_minlimit; /* in mW */ + u32 stt_skintemp_apu; /* in C */ + u32 stt_skintemp_hs2; /* in C */ +}; + +/* Input conditions */ +struct ta_pmf_condition_info { + u32 power_source; + u32 bat_percentage; + u32 power_slider; + u32 lid_state; + bool user_present; + u32 rsvd1[2]; + u32 monitor_count; + u32 rsvd2[2]; + u32 bat_design; + u32 full_charge_capacity; + int drain_rate; + bool user_engaged; + u32 device_state; + u32 socket_power; + u32 skin_temperature; + u32 rsvd3[5]; + u32 ambient_light; + u32 length; + u32 avg_c0residency; + u32 max_c0residency; + u32 s0i3_entry; + u32 gfx_busy; + u32 rsvd4[7]; + bool camera_state; + u32 workload_type; + u32 display_type; + u32 display_state; + u32 rsvd5[150]; +}; + +struct ta_pmf_load_policy_table { + u32 table_size; + u8 table[POLICY_BUF_MAX_SZ]; +}; + +/* TA initialization params */ +struct ta_pmf_init_table { + u32 frequency; /* SMU sampling frequency */ + bool validate; + bool sku_check; + bool metadata_macrocheck; + struct ta_pmf_load_policy_table policies_table; +}; + +/* Everything the TA needs to Enact Policies */ +struct ta_pmf_enact_table { + struct ta_pmf_condition_info ev_info; + u32 name; +}; + +struct ta_pmf_action { + u32 action_index; + u32 value; +}; + +/* Output actions from TA */ +struct ta_pmf_enact_result { + u32 actions_count; + struct ta_pmf_action actions_list[TA_PMF_ACTION_MAX]; + u32 undo_count; + struct ta_pmf_action undo_list[TA_PMF_UNDO_MAX]; +}; + +union ta_pmf_input { + struct ta_pmf_enact_table enact_table; + struct ta_pmf_init_table init_table; +}; + +union ta_pmf_output { + struct ta_pmf_enact_result policy_apply_table; + u32 rsvd[TA_OUTPUT_RESERVED_MEM]; +}; + struct ta_pmf_shared_memory { int command_id; int resp_id; u32 pmf_result; u32 if_version; + union ta_pmf_output pmf_output; + union ta_pmf_input pmf_input; }; /* Core Layer */ @@ -460,4 +600,5 @@ extern const struct attribute_group cnqf_feature_attribute_group; /* Smart PC builder Layer */ int amd_pmf_init_smart_pc(struct amd_pmf_dev *dev); void amd_pmf_deinit_smart_pc(struct amd_pmf_dev *dev); +int apmf_check_smart_pc(struct amd_pmf_dev *pmf_dev); #endif /* PMF_H */ diff --git a/drivers/platform/x86/amd/pmf/tee-if.c b/drivers/platform/x86/amd/pmf/tee-if.c index 4036f435f1e2..f99dc79ebb40 100644 --- a/drivers/platform/x86/amd/pmf/tee-if.c +++ b/drivers/platform/x86/amd/pmf/tee-if.c @@ -42,9 +42,77 @@ static void amd_pmf_prepare_args(struct amd_pmf_dev *dev, int cmd, param[0].u.memref.shm_offs = 0; } +static void amd_pmf_apply_policies(struct amd_pmf_dev *dev, struct ta_pmf_enact_result *out) +{ + u32 val; + int idx; + + for (idx = 0; idx < out->actions_count; idx++) { + val = out->actions_list[idx].value; + switch (out->actions_list[idx].action_index) { + case PMF_POLICY_SPL: + if (dev->prev_data->spl != val) { + amd_pmf_send_cmd(dev, SET_SPL, false, val, NULL); + dev_dbg(dev->dev, "update SPL: %u\n", val); + dev->prev_data->spl = val; + } + break; + + case PMF_POLICY_SPPT: + if (dev->prev_data->sppt != val) { + amd_pmf_send_cmd(dev, SET_SPPT, false, val, NULL); + dev_dbg(dev->dev, "update SPPT: %u\n", val); + dev->prev_data->sppt = val; + } + break; + + case PMF_POLICY_FPPT: + if (dev->prev_data->fppt != val) { + amd_pmf_send_cmd(dev, SET_FPPT, false, val, NULL); + dev_dbg(dev->dev, "update FPPT: %u\n", val); + dev->prev_data->fppt = val; + } + break; + + case PMF_POLICY_SPPT_APU_ONLY: + if (dev->prev_data->sppt_apuonly != val) { + amd_pmf_send_cmd(dev, SET_SPPT_APU_ONLY, false, val, NULL); + dev_dbg(dev->dev, "update SPPT_APU_ONLY: %u\n", val); + dev->prev_data->sppt_apuonly = val; + } + break; + + case PMF_POLICY_STT_MIN: + if (dev->prev_data->stt_minlimit != val) { + amd_pmf_send_cmd(dev, SET_STT_MIN_LIMIT, false, val, NULL); + dev_dbg(dev->dev, "update STT_MIN: %u\n", val); + dev->prev_data->stt_minlimit = val; + } + break; + + case PMF_POLICY_STT_SKINTEMP_APU: + if (dev->prev_data->stt_skintemp_apu != val) { + amd_pmf_send_cmd(dev, SET_STT_LIMIT_APU, false, val, NULL); + dev_dbg(dev->dev, "update STT_SKINTEMP_APU: %u\n", val); + dev->prev_data->stt_skintemp_apu = val; + } + break; + + case PMF_POLICY_STT_SKINTEMP_HS2: + if (dev->prev_data->stt_skintemp_hs2 != val) { + amd_pmf_send_cmd(dev, SET_STT_LIMIT_HS2, false, val, NULL); + dev_dbg(dev->dev, "update STT_SKINTEMP_HS2: %u\n", val); + dev->prev_data->stt_skintemp_hs2 = val; + } + break; + } + } +} + static int amd_pmf_invoke_cmd_enact(struct amd_pmf_dev *dev) { struct ta_pmf_shared_memory *ta_sm = NULL; + struct ta_pmf_enact_result *out = NULL; struct tee_param param[MAX_TEE_PARAM]; struct tee_ioctl_invoke_arg arg; int ret = 0; @@ -52,7 +120,10 @@ static int amd_pmf_invoke_cmd_enact(struct amd_pmf_dev *dev) if (!dev->tee_ctx) return -ENODEV; + memset(dev->shbuf, 0, dev->policy_sz); ta_sm = dev->shbuf; + out = &ta_sm->pmf_output.policy_apply_table; + memset(ta_sm, 0, sizeof(*ta_sm)); ta_sm->command_id = TA_PMF_COMMAND_POLICY_BUILDER_ENACT_POLICIES; ta_sm->if_version = PMF_TA_IF_VERSION_MAJOR; @@ -65,6 +136,12 @@ static int amd_pmf_invoke_cmd_enact(struct amd_pmf_dev *dev) return ret; } + if (ta_sm->pmf_result == TA_PMF_TYPE_SUCCESS && out->actions_count) { + dev_dbg(dev->dev, "action count:%u result:%x\n", out->actions_count, + ta_sm->pmf_result); + amd_pmf_apply_policies(dev, out); + } + return 0; } @@ -72,6 +149,7 @@ static int amd_pmf_invoke_cmd_init(struct amd_pmf_dev *dev) { struct ta_pmf_shared_memory *ta_sm = NULL; struct tee_param param[MAX_TEE_PARAM]; + struct ta_pmf_init_table *in = NULL; struct tee_ioctl_invoke_arg arg; int ret = 0; @@ -80,10 +158,21 @@ static int amd_pmf_invoke_cmd_init(struct amd_pmf_dev *dev) return -ENODEV; } + dev_dbg(dev->dev, "Policy Binary size: %u bytes\n", dev->policy_sz); + memset(dev->shbuf, 0, dev->policy_sz); ta_sm = dev->shbuf; + in = &ta_sm->pmf_input.init_table; + ta_sm->command_id = TA_PMF_COMMAND_POLICY_BUILDER_INITIALIZE; ta_sm->if_version = PMF_TA_IF_VERSION_MAJOR; + in->metadata_macrocheck = false; + in->sku_check = false; + in->validate = true; + in->frequency = pb_actions_ms; + in->policies_table.table_size = dev->policy_sz; + + memcpy(in->policies_table.table, dev->policy_buf, dev->policy_sz); amd_pmf_prepare_args(dev, TA_PMF_COMMAND_POLICY_BUILDER_INITIALIZE, &arg, param); ret = tee_client_invoke_func(dev->tee_ctx, &arg, param); @@ -103,6 +192,52 @@ static void amd_pmf_invoke_cmd(struct work_struct *work) schedule_delayed_work(&dev->pb_work, msecs_to_jiffies(pb_actions_ms)); } +static int amd_pmf_start_policy_engine(struct amd_pmf_dev *dev) +{ + u32 cookie, length; + int res; + + cookie = readl(dev->policy_buf + POLICY_COOKIE_OFFSET); + length = readl(dev->policy_buf + POLICY_COOKIE_LEN); + + if (cookie != POLICY_SIGN_COOKIE || !length) + return -EINVAL; + + /* Update the actual length */ + dev->policy_sz = length + 512; + res = amd_pmf_invoke_cmd_init(dev); + if (res == TA_PMF_TYPE_SUCCESS) { + /* Now its safe to announce that smart pc is enabled */ + dev->smart_pc_enabled = PMF_SMART_PC_ENABLED; + /* + * Start collecting the data from TA FW after a small delay + * or else, we might end up getting stale values. + */ + schedule_delayed_work(&dev->pb_work, msecs_to_jiffies(pb_actions_ms * 3)); + } else { + dev_err(dev->dev, "ta invoke cmd init failed err: %x\n", res); + dev->smart_pc_enabled = PMF_SMART_PC_DISABLED; + return res; + } + + return 0; +} + +static int amd_pmf_get_bios_buffer(struct amd_pmf_dev *dev) +{ + dev->policy_buf = kzalloc(dev->policy_sz, GFP_KERNEL); + if (!dev->policy_buf) + return -ENOMEM; + + dev->policy_base = devm_ioremap(dev->dev, dev->policy_addr, dev->policy_sz); + if (!dev->policy_base) + return -ENOMEM; + + memcpy(dev->policy_buf, dev->policy_base, dev->policy_sz); + + return amd_pmf_start_policy_engine(dev); +} + static int amd_pmf_amdtee_ta_match(struct tee_ioctl_version_data *ver, const void *data) { return ver->impl_id == TEE_IMPL_ID_AMDTEE; @@ -146,7 +281,7 @@ static int amd_pmf_tee_init(struct amd_pmf_dev *dev) goto out_ctx; } - size = sizeof(struct ta_pmf_shared_memory); + size = sizeof(struct ta_pmf_shared_memory) + dev->policy_sz; dev->fw_shm_pool = tee_shm_alloc_kernel_buf(dev->tee_ctx, size); if (IS_ERR(dev->fw_shm_pool)) { dev_err(dev->dev, "Failed to alloc TEE shared memory\n"); @@ -185,16 +320,35 @@ int amd_pmf_init_smart_pc(struct amd_pmf_dev *dev) { int ret; + ret = apmf_check_smart_pc(dev); + if (ret) { + /* + * Lets not return from here if Smart PC bit is not advertised in + * the BIOS. This way, there will be some amount of power savings + * to the user with static slider (if enabled). + */ + dev_info(dev->dev, "PMF Smart PC not advertised in BIOS!:%d\n", ret); + return -ENODEV; + } + ret = amd_pmf_tee_init(dev); if (ret) return ret; INIT_DELAYED_WORK(&dev->pb_work, amd_pmf_invoke_cmd); - return 0; + amd_pmf_set_dram_addr(dev, true); + amd_pmf_get_bios_buffer(dev); + dev->prev_data = kzalloc(sizeof(*dev->prev_data), GFP_KERNEL); + if (!dev->prev_data) + return -ENOMEM; + + return dev->smart_pc_enabled; } void amd_pmf_deinit_smart_pc(struct amd_pmf_dev *dev) { + kfree(dev->prev_data); + kfree(dev->policy_buf); cancel_delayed_work_sync(&dev->pb_work); amd_pmf_tee_deinit(dev); } -- cgit From c7af165372a8612eae08dbbab787d1d84d7f0384 Mon Sep 17 00:00:00 2001 From: Shyam Sundar S K Date: Tue, 12 Dec 2023 07:16:58 +0530 Subject: platform/x86/amd/pmf: change amd_pmf_init_features() call sequence To sideload pmf policy binaries, the Smart PC Solution Builder provides a debugfs file called "update_policy"; that gets created under a new debugfs directory called "pb" and this new directory has to be associated with existing parent directory for PMF driver called "amd_pmf". In the current code structure, amd_pmf_dbgfs_register() is called after amd_pmf_init_features(). This will not help when the Smart PC builder feature has to be assoicated to the parent directory. Hence change the order of amd_pmf_dbgfs_register() and call it before amd_pmf_init_features() so that when the Smart PC init happens, it has the parent debugfs directory to get itself hooked. Reviewed-by: Mario Limonciello Signed-off-by: Shyam Sundar S K Link: https://lore.kernel.org/r/20231212014705.2017474-6-Shyam-sundar.S-k@amd.com Signed-off-by: Hans de Goede --- drivers/platform/x86/amd/pmf/core.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/platform/x86/amd/pmf/core.c b/drivers/platform/x86/amd/pmf/core.c index c10d40b33667..feaa09f5b35a 100644 --- a/drivers/platform/x86/amd/pmf/core.c +++ b/drivers/platform/x86/amd/pmf/core.c @@ -431,9 +431,9 @@ static int amd_pmf_probe(struct platform_device *pdev) apmf_acpi_init(dev); platform_set_drvdata(pdev, dev); + amd_pmf_dbgfs_register(dev); amd_pmf_init_features(dev); apmf_install_handler(dev); - amd_pmf_dbgfs_register(dev); dev_info(dev->dev, "registered PMF device successfully\n"); -- cgit From f4627dfd0e1924ad31c6476c0fc2308cfe12b561 Mon Sep 17 00:00:00 2001 From: Shyam Sundar S K Date: Tue, 12 Dec 2023 07:16:59 +0530 Subject: platform/x86/amd/pmf: Add support to get inputs from other subsystems MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit PMF driver sends changing inputs from each subystem to TA for evaluating the conditions in the policy binary. Add initial support of plumbing in the PMF driver for Smart PC to get information from other subsystems in the kernel. Reviewed-by: Mario Limonciello Reviewed-by: Ilpo Järvinen Signed-off-by: Shyam Sundar S K Link: https://lore.kernel.org/r/20231212014705.2017474-7-Shyam-sundar.S-k@amd.com Signed-off-by: Hans de Goede --- drivers/platform/x86/amd/pmf/Makefile | 2 +- drivers/platform/x86/amd/pmf/pmf.h | 18 +++++ drivers/platform/x86/amd/pmf/spc.c | 122 ++++++++++++++++++++++++++++++++++ drivers/platform/x86/amd/pmf/tee-if.c | 3 + 4 files changed, 144 insertions(+), 1 deletion(-) create mode 100644 drivers/platform/x86/amd/pmf/spc.c diff --git a/drivers/platform/x86/amd/pmf/Makefile b/drivers/platform/x86/amd/pmf/Makefile index d2746ee7369f..6b26e48ce8ad 100644 --- a/drivers/platform/x86/amd/pmf/Makefile +++ b/drivers/platform/x86/amd/pmf/Makefile @@ -7,4 +7,4 @@ obj-$(CONFIG_AMD_PMF) += amd-pmf.o amd-pmf-objs := core.o acpi.o sps.o \ auto-mode.o cnqf.o \ - tee-if.o + tee-if.o spc.o diff --git a/drivers/platform/x86/amd/pmf/pmf.h b/drivers/platform/x86/amd/pmf/pmf.h index 031e6d3ebd4d..4da51eb28b6f 100644 --- a/drivers/platform/x86/amd/pmf/pmf.h +++ b/drivers/platform/x86/amd/pmf/pmf.h @@ -150,6 +150,21 @@ struct smu_pmf_metrics { u16 infra_gfx_maxfreq; /* in MHz */ u16 skin_temp; /* in centi-Celsius */ u16 device_state; + u16 curtemp; /* in centi-Celsius */ + u16 filter_alpha_value; + u16 avg_gfx_clkfrequency; + u16 avg_fclk_frequency; + u16 avg_gfx_activity; + u16 avg_socclk_frequency; + u16 avg_vclk_frequency; + u16 avg_vcn_activity; + u16 avg_dram_reads; + u16 avg_dram_writes; + u16 avg_socket_power; + u16 avg_core_power[2]; + u16 avg_core_c0residency[16]; + u16 spare1; + u32 metrics_counter; } __packed; enum amd_stt_skin_temp { @@ -601,4 +616,7 @@ extern const struct attribute_group cnqf_feature_attribute_group; int amd_pmf_init_smart_pc(struct amd_pmf_dev *dev); void amd_pmf_deinit_smart_pc(struct amd_pmf_dev *dev); int apmf_check_smart_pc(struct amd_pmf_dev *pmf_dev); + +/* Smart PC - TA interfaces */ +void amd_pmf_populate_ta_inputs(struct amd_pmf_dev *dev, struct ta_pmf_enact_table *in); #endif /* PMF_H */ diff --git a/drivers/platform/x86/amd/pmf/spc.c b/drivers/platform/x86/amd/pmf/spc.c new file mode 100644 index 000000000000..351efcbe83c4 --- /dev/null +++ b/drivers/platform/x86/amd/pmf/spc.c @@ -0,0 +1,122 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * AMD Platform Management Framework Driver - Smart PC Capabilities + * + * Copyright (c) 2023, Advanced Micro Devices, Inc. + * All Rights Reserved. + * + * Authors: Shyam Sundar S K + * Patil Rajesh Reddy + */ + +#include +#include +#include +#include "pmf.h" + +static void amd_pmf_get_smu_info(struct amd_pmf_dev *dev, struct ta_pmf_enact_table *in) +{ + u16 max, avg = 0; + int i; + + memset(dev->buf, 0, sizeof(dev->m_table)); + amd_pmf_send_cmd(dev, SET_TRANSFER_TABLE, 0, 7, NULL); + memcpy(&dev->m_table, dev->buf, sizeof(dev->m_table)); + + in->ev_info.socket_power = dev->m_table.apu_power + dev->m_table.dgpu_power; + in->ev_info.skin_temperature = dev->m_table.skin_temp; + + /* Get the avg and max C0 residency of all the cores */ + max = dev->m_table.avg_core_c0residency[0]; + for (i = 0; i < ARRAY_SIZE(dev->m_table.avg_core_c0residency); i++) { + avg += dev->m_table.avg_core_c0residency[i]; + if (dev->m_table.avg_core_c0residency[i] > max) + max = dev->m_table.avg_core_c0residency[i]; + } + + avg = DIV_ROUND_CLOSEST(avg, ARRAY_SIZE(dev->m_table.avg_core_c0residency)); + in->ev_info.avg_c0residency = avg; + in->ev_info.max_c0residency = max; + in->ev_info.gfx_busy = dev->m_table.avg_gfx_activity; +} + +static const char * const pmf_battery_supply_name[] = { + "BATT", + "BAT0", +}; + +static int amd_pmf_get_battery_prop(enum power_supply_property prop) +{ + union power_supply_propval value; + struct power_supply *psy; + int i, ret; + + for (i = 0; i < ARRAY_SIZE(pmf_battery_supply_name); i++) { + psy = power_supply_get_by_name(pmf_battery_supply_name[i]); + if (!psy) + continue; + + ret = power_supply_get_property(psy, prop, &value); + if (ret) { + power_supply_put(psy); + return ret; + } + } + + return value.intval; +} + +static int amd_pmf_get_battery_info(struct amd_pmf_dev *dev, struct ta_pmf_enact_table *in) +{ + int val; + + val = amd_pmf_get_battery_prop(POWER_SUPPLY_PROP_PRESENT); + if (val < 0) + return val; + if (val != 1) + return -ENODEV; + + in->ev_info.bat_percentage = amd_pmf_get_battery_prop(POWER_SUPPLY_PROP_CAPACITY); + /* all values in mWh metrics */ + in->ev_info.bat_design = amd_pmf_get_battery_prop(POWER_SUPPLY_PROP_ENERGY_FULL_DESIGN) / + MILLIWATT_PER_WATT; + in->ev_info.full_charge_capacity = amd_pmf_get_battery_prop(POWER_SUPPLY_PROP_ENERGY_FULL) / + MILLIWATT_PER_WATT; + in->ev_info.drain_rate = amd_pmf_get_battery_prop(POWER_SUPPLY_PROP_POWER_NOW) / + MILLIWATT_PER_WATT; + + return 0; +} + +static int amd_pmf_get_slider_info(struct amd_pmf_dev *dev, struct ta_pmf_enact_table *in) +{ + int val; + + switch (dev->current_profile) { + case PLATFORM_PROFILE_PERFORMANCE: + val = TA_BEST_PERFORMANCE; + break; + case PLATFORM_PROFILE_BALANCED: + val = TA_BETTER_PERFORMANCE; + break; + case PLATFORM_PROFILE_LOW_POWER: + val = TA_BEST_BATTERY; + break; + default: + dev_err(dev->dev, "Unknown Platform Profile.\n"); + return -EOPNOTSUPP; + } + in->ev_info.power_slider = val; + + return 0; +} + +void amd_pmf_populate_ta_inputs(struct amd_pmf_dev *dev, struct ta_pmf_enact_table *in) +{ + /* TA side lid open is 1 and close is 0, hence the ! here */ + in->ev_info.lid_state = !acpi_lid_open(); + in->ev_info.power_source = amd_pmf_get_power_source(); + amd_pmf_get_smu_info(dev, in); + amd_pmf_get_battery_info(dev, in); + amd_pmf_get_slider_info(dev, in); +} diff --git a/drivers/platform/x86/amd/pmf/tee-if.c b/drivers/platform/x86/amd/pmf/tee-if.c index f99dc79ebb40..e96db406e91b 100644 --- a/drivers/platform/x86/amd/pmf/tee-if.c +++ b/drivers/platform/x86/amd/pmf/tee-if.c @@ -113,6 +113,7 @@ static int amd_pmf_invoke_cmd_enact(struct amd_pmf_dev *dev) { struct ta_pmf_shared_memory *ta_sm = NULL; struct ta_pmf_enact_result *out = NULL; + struct ta_pmf_enact_table *in = NULL; struct tee_param param[MAX_TEE_PARAM]; struct tee_ioctl_invoke_arg arg; int ret = 0; @@ -123,11 +124,13 @@ static int amd_pmf_invoke_cmd_enact(struct amd_pmf_dev *dev) memset(dev->shbuf, 0, dev->policy_sz); ta_sm = dev->shbuf; out = &ta_sm->pmf_output.policy_apply_table; + in = &ta_sm->pmf_input.enact_table; memset(ta_sm, 0, sizeof(*ta_sm)); ta_sm->command_id = TA_PMF_COMMAND_POLICY_BUILDER_ENACT_POLICIES; ta_sm->if_version = PMF_TA_IF_VERSION_MAJOR; + amd_pmf_populate_ta_inputs(dev, in); amd_pmf_prepare_args(dev, TA_PMF_COMMAND_POLICY_BUILDER_ENACT_POLICIES, &arg, param); ret = tee_client_invoke_func(dev->tee_ctx, &arg, param); -- cgit From c3b40930a214545919d1385b8aa71cb665904571 Mon Sep 17 00:00:00 2001 From: Shyam Sundar S K Date: Tue, 12 Dec 2023 07:17:00 +0530 Subject: platform/x86/amd/pmf: Add support update p3t limit P3T (Peak Package Power Limit) is a metric within the SMU controller that can influence the power limits. Add support from the driver to update P3T limits accordingly. Reviewed-by: Mario Limonciello Signed-off-by: Shyam Sundar S K Link: https://lore.kernel.org/r/20231212014705.2017474-8-Shyam-sundar.S-k@amd.com Signed-off-by: Hans de Goede --- drivers/platform/x86/amd/pmf/pmf.h | 3 +++ drivers/platform/x86/amd/pmf/tee-if.c | 8 ++++++++ 2 files changed, 11 insertions(+) diff --git a/drivers/platform/x86/amd/pmf/pmf.h b/drivers/platform/x86/amd/pmf/pmf.h index 4da51eb28b6f..37bf1c701361 100644 --- a/drivers/platform/x86/amd/pmf/pmf.h +++ b/drivers/platform/x86/amd/pmf/pmf.h @@ -49,6 +49,7 @@ #define GET_STT_MIN_LIMIT 0x1F #define GET_STT_LIMIT_APU 0x20 #define GET_STT_LIMIT_HS2 0x21 +#define SET_P3T 0x23 /* P3T: Peak Package Power Limit */ /* OS slider update notification */ #define DC_BEST_PERF 0 @@ -72,6 +73,7 @@ #define PMF_POLICY_STT_MIN 6 #define PMF_POLICY_STT_SKINTEMP_APU 7 #define PMF_POLICY_STT_SKINTEMP_HS2 8 +#define PMF_POLICY_P3T 38 /* TA macros */ #define PMF_TA_IF_VERSION_MAJOR 1 @@ -481,6 +483,7 @@ struct pmf_action_table { u32 stt_minlimit; /* in mW */ u32 stt_skintemp_apu; /* in C */ u32 stt_skintemp_hs2; /* in C */ + u32 p3t_limit; /* in mW */ }; /* Input conditions */ diff --git a/drivers/platform/x86/amd/pmf/tee-if.c b/drivers/platform/x86/amd/pmf/tee-if.c index e96db406e91b..bf8cb98d41ec 100644 --- a/drivers/platform/x86/amd/pmf/tee-if.c +++ b/drivers/platform/x86/amd/pmf/tee-if.c @@ -105,6 +105,14 @@ static void amd_pmf_apply_policies(struct amd_pmf_dev *dev, struct ta_pmf_enact_ dev->prev_data->stt_skintemp_hs2 = val; } break; + + case PMF_POLICY_P3T: + if (dev->prev_data->p3t_limit != val) { + amd_pmf_send_cmd(dev, SET_P3T, false, val, NULL); + dev_dbg(dev->dev, "update P3T: %u\n", val); + dev->prev_data->p3t_limit = val; + } + break; } } } -- cgit From d0ba7ad438dfed944232cf8c96141ae5057605ee Mon Sep 17 00:00:00 2001 From: Shyam Sundar S K Date: Tue, 12 Dec 2023 07:17:01 +0530 Subject: platform/x86/amd/pmf: Add support to update system state PMF driver based on the output actions from the TA can request to update the system states like entering s0i3, lock screen etc. by generating an uevent. Based on the udev rules set in the userspace the event id matching the uevent shall get updated accordingly using the systemctl. Sample udev rules under Documentation/admin-guide/pmf.rst. Reviewed-by: Mario Limonciello Signed-off-by: Shyam Sundar S K Link: https://lore.kernel.org/r/20231212014705.2017474-9-Shyam-sundar.S-k@amd.com Signed-off-by: Hans de Goede --- Documentation/admin-guide/index.rst | 1 + Documentation/admin-guide/pmf.rst | 24 ++++++++++++++++++++++++ drivers/platform/x86/amd/pmf/pmf.h | 9 +++++++++ drivers/platform/x86/amd/pmf/tee-if.c | 34 ++++++++++++++++++++++++++++++++++ 4 files changed, 68 insertions(+) create mode 100644 Documentation/admin-guide/pmf.rst diff --git a/Documentation/admin-guide/index.rst b/Documentation/admin-guide/index.rst index 43ea35613dfc..fb40a1f6f79e 100644 --- a/Documentation/admin-guide/index.rst +++ b/Documentation/admin-guide/index.rst @@ -119,6 +119,7 @@ configure specific aspects of kernel behavior to your liking. parport perf-security pm/index + pmf pnp rapidio ras diff --git a/Documentation/admin-guide/pmf.rst b/Documentation/admin-guide/pmf.rst new file mode 100644 index 000000000000..9ee729ffc19b --- /dev/null +++ b/Documentation/admin-guide/pmf.rst @@ -0,0 +1,24 @@ +.. SPDX-License-Identifier: GPL-2.0 + +Set udev rules for PMF Smart PC Builder +--------------------------------------- + +AMD PMF(Platform Management Framework) Smart PC Solution builder has to set the system states +like S0i3, Screen lock, hibernate etc, based on the output actions provided by the PMF +TA (Trusted Application). + +In order for this to work the PMF driver generates a uevent for userspace to react to. Below are +sample udev rules that can facilitate this experience when a machine has PMF Smart PC solution builder +enabled. + +Please add the following line(s) to +``/etc/udev/rules.d/99-local.rules``:: + + DRIVERS=="amd-pmf", ACTION=="change", ENV{EVENT_ID}=="0", RUN+="/usr/bin/systemctl suspend" + DRIVERS=="amd-pmf", ACTION=="change", ENV{EVENT_ID}=="1", RUN+="/usr/bin/systemctl hibernate" + DRIVERS=="amd-pmf", ACTION=="change", ENV{EVENT_ID}=="2", RUN+="/bin/loginctl lock-sessions" + +EVENT_ID values: +0= Put the system to S0i3/S2Idle +1= Put the system to hibernate +2= Lock the screen diff --git a/drivers/platform/x86/amd/pmf/pmf.h b/drivers/platform/x86/amd/pmf/pmf.h index 37bf1c701361..50f98c398727 100644 --- a/drivers/platform/x86/amd/pmf/pmf.h +++ b/drivers/platform/x86/amd/pmf/pmf.h @@ -73,6 +73,7 @@ #define PMF_POLICY_STT_MIN 6 #define PMF_POLICY_STT_SKINTEMP_APU 7 #define PMF_POLICY_STT_SKINTEMP_HS2 8 +#define PMF_POLICY_SYSTEM_STATE 9 #define PMF_POLICY_P3T 38 /* TA macros */ @@ -445,6 +446,13 @@ enum smart_pc_status { }; /* Smart PC - TA internals */ +enum system_state { + SYSTEM_STATE_S0i3, + SYSTEM_STATE_S4, + SYSTEM_STATE_SCREEN_LOCK, + SYSTEM_STATE_MAX, +}; + enum ta_slider { TA_BEST_BATTERY, TA_BETTER_BATTERY, @@ -476,6 +484,7 @@ enum ta_pmf_error_type { }; struct pmf_action_table { + enum system_state system_state; u32 spl; /* in mW */ u32 sppt; /* in mW */ u32 sppt_apuonly; /* in mW */ diff --git a/drivers/platform/x86/amd/pmf/tee-if.c b/drivers/platform/x86/amd/pmf/tee-if.c index bf8cb98d41ec..8811631c7be5 100644 --- a/drivers/platform/x86/amd/pmf/tee-if.c +++ b/drivers/platform/x86/amd/pmf/tee-if.c @@ -24,6 +24,20 @@ MODULE_PARM_DESC(pb_actions_ms, "Policy binary actions sampling frequency (defau static const uuid_t amd_pmf_ta_uuid = UUID_INIT(0x6fd93b77, 0x3fb8, 0x524d, 0xb1, 0x2d, 0xc5, 0x29, 0xb1, 0x3d, 0x85, 0x43); +static const char *amd_pmf_uevent_as_str(unsigned int state) +{ + switch (state) { + case SYSTEM_STATE_S0i3: + return "S0i3"; + case SYSTEM_STATE_S4: + return "S4"; + case SYSTEM_STATE_SCREEN_LOCK: + return "SCREEN_LOCK"; + default: + return "Unknown Smart PC event"; + } +} + static void amd_pmf_prepare_args(struct amd_pmf_dev *dev, int cmd, struct tee_ioctl_invoke_arg *arg, struct tee_param *param) @@ -42,6 +56,20 @@ static void amd_pmf_prepare_args(struct amd_pmf_dev *dev, int cmd, param[0].u.memref.shm_offs = 0; } +static int amd_pmf_update_uevents(struct amd_pmf_dev *dev, u16 event) +{ + char *envp[2] = {}; + + envp[0] = kasprintf(GFP_KERNEL, "EVENT_ID=%d", event); + if (!envp[0]) + return -EINVAL; + + kobject_uevent_env(&dev->dev->kobj, KOBJ_CHANGE, envp); + + kfree(envp[0]); + return 0; +} + static void amd_pmf_apply_policies(struct amd_pmf_dev *dev, struct ta_pmf_enact_result *out) { u32 val; @@ -113,6 +141,12 @@ static void amd_pmf_apply_policies(struct amd_pmf_dev *dev, struct ta_pmf_enact_ dev->prev_data->p3t_limit = val; } break; + + case PMF_POLICY_SYSTEM_STATE: + amd_pmf_update_uevents(dev, val); + dev_dbg(dev->dev, "update SYSTEM_STATE: %s\n", + amd_pmf_uevent_as_str(val)); + break; } } } -- cgit From 4984dbb60789ccb8674708446431f3bc0dc73100 Mon Sep 17 00:00:00 2001 From: Shyam Sundar S K Date: Tue, 12 Dec 2023 07:17:02 +0530 Subject: platform/x86/amd/pmf: Make source_as_str() as non-static Add amd_pmf prefix to source_as_str() function, so that the function name does not look generic. As this is a helper function make it as non-static so that it can be reused across multiple PMF features. Reviewed-by: Mario Limonciello Signed-off-by: Shyam Sundar S K Link: https://lore.kernel.org/r/20231212014705.2017474-10-Shyam-sundar.S-k@amd.com Signed-off-by: Hans de Goede --- drivers/platform/x86/amd/pmf/pmf.h | 1 + drivers/platform/x86/amd/pmf/sps.c | 5 +++-- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/drivers/platform/x86/amd/pmf/pmf.h b/drivers/platform/x86/amd/pmf/pmf.h index 50f98c398727..25e369477f86 100644 --- a/drivers/platform/x86/amd/pmf/pmf.h +++ b/drivers/platform/x86/amd/pmf/pmf.h @@ -600,6 +600,7 @@ int apmf_get_static_slider_granular(struct amd_pmf_dev *pdev, struct apmf_static_slider_granular_output *output); bool is_pprof_balanced(struct amd_pmf_dev *pmf); int amd_pmf_power_slider_update_event(struct amd_pmf_dev *dev); +const char *amd_pmf_source_as_str(unsigned int state); int apmf_update_fan_idx(struct amd_pmf_dev *pdev, bool manual, u32 idx); diff --git a/drivers/platform/x86/amd/pmf/sps.c b/drivers/platform/x86/amd/pmf/sps.c index a70e67749be3..33e23e25c8b1 100644 --- a/drivers/platform/x86/amd/pmf/sps.c +++ b/drivers/platform/x86/amd/pmf/sps.c @@ -27,7 +27,7 @@ static const char *slider_as_str(unsigned int state) } } -static const char *source_as_str(unsigned int state) +const char *amd_pmf_source_as_str(unsigned int state) { switch (state) { case POWER_SOURCE_AC: @@ -47,7 +47,8 @@ static void amd_pmf_dump_sps_defaults(struct amd_pmf_static_slider_granular *dat for (i = 0; i < POWER_SOURCE_MAX; i++) { for (j = 0; j < POWER_MODE_MAX; j++) { - pr_debug("--- Source:%s Mode:%s ---\n", source_as_str(i), slider_as_str(j)); + pr_debug("--- Source:%s Mode:%s ---\n", amd_pmf_source_as_str(i), + slider_as_str(j)); pr_debug("SPL: %u mW\n", data->prop[i][j].spl); pr_debug("SPPT: %u mW\n", data->prop[i][j].sppt); pr_debug("SPPT_ApuOnly: %u mW\n", data->prop[i][j].sppt_apu_only); -- cgit From 69e76c5af973854556625a8e156a39d1edbe8d6f Mon Sep 17 00:00:00 2001 From: Shyam Sundar S K Date: Tue, 12 Dec 2023 07:17:03 +0530 Subject: platform/x86/amd/pmf: Add facility to dump TA inputs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit PMF driver sends constant inputs to TA which its gets via the other subsystems in the kernel. To debug certain TA issues knowing what inputs being sent to TA becomes critical. Add debug facility to the driver which can isolate Smart PC and TA related issues. Also, make source_as_str() as non-static function as this helper is required outside of sps.c file. Reviewed-by: Mario Limonciello Reviewed-by: Ilpo Järvinen Signed-off-by: Shyam Sundar S K Link: https://lore.kernel.org/r/20231212014705.2017474-11-Shyam-sundar.S-k@amd.com Signed-off-by: Hans de Goede --- drivers/platform/x86/amd/pmf/pmf.h | 3 +++ drivers/platform/x86/amd/pmf/spc.c | 36 +++++++++++++++++++++++++++++++++++ drivers/platform/x86/amd/pmf/tee-if.c | 1 + 3 files changed, 40 insertions(+) diff --git a/drivers/platform/x86/amd/pmf/pmf.h b/drivers/platform/x86/amd/pmf/pmf.h index 25e369477f86..55cd2b301bbb 100644 --- a/drivers/platform/x86/amd/pmf/pmf.h +++ b/drivers/platform/x86/amd/pmf/pmf.h @@ -602,6 +602,7 @@ bool is_pprof_balanced(struct amd_pmf_dev *pmf); int amd_pmf_power_slider_update_event(struct amd_pmf_dev *dev); const char *amd_pmf_source_as_str(unsigned int state); +const char *amd_pmf_source_as_str(unsigned int state); int apmf_update_fan_idx(struct amd_pmf_dev *pdev, bool manual, u32 idx); int amd_pmf_set_sps_power_limits(struct amd_pmf_dev *pmf); @@ -632,4 +633,6 @@ int apmf_check_smart_pc(struct amd_pmf_dev *pmf_dev); /* Smart PC - TA interfaces */ void amd_pmf_populate_ta_inputs(struct amd_pmf_dev *dev, struct ta_pmf_enact_table *in); +void amd_pmf_dump_ta_inputs(struct amd_pmf_dev *dev, struct ta_pmf_enact_table *in); + #endif /* PMF_H */ diff --git a/drivers/platform/x86/amd/pmf/spc.c b/drivers/platform/x86/amd/pmf/spc.c index 351efcbe83c4..a0423942f771 100644 --- a/drivers/platform/x86/amd/pmf/spc.c +++ b/drivers/platform/x86/amd/pmf/spc.c @@ -14,6 +14,42 @@ #include #include "pmf.h" +#ifdef CONFIG_AMD_PMF_DEBUG +static const char *ta_slider_as_str(unsigned int state) +{ + switch (state) { + case TA_BEST_PERFORMANCE: + return "PERFORMANCE"; + case TA_BETTER_PERFORMANCE: + return "BALANCED"; + case TA_BEST_BATTERY: + return "POWER_SAVER"; + default: + return "Unknown TA Slider State"; + } +} + +void amd_pmf_dump_ta_inputs(struct amd_pmf_dev *dev, struct ta_pmf_enact_table *in) +{ + dev_dbg(dev->dev, "==== TA inputs START ====\n"); + dev_dbg(dev->dev, "Slider State: %s\n", ta_slider_as_str(in->ev_info.power_slider)); + dev_dbg(dev->dev, "Power Source: %s\n", amd_pmf_source_as_str(in->ev_info.power_source)); + dev_dbg(dev->dev, "Battery Percentage: %u\n", in->ev_info.bat_percentage); + dev_dbg(dev->dev, "Designed Battery Capacity: %u\n", in->ev_info.bat_design); + dev_dbg(dev->dev, "Fully Charged Capacity: %u\n", in->ev_info.full_charge_capacity); + dev_dbg(dev->dev, "Drain Rate: %d\n", in->ev_info.drain_rate); + dev_dbg(dev->dev, "Socket Power: %u\n", in->ev_info.socket_power); + dev_dbg(dev->dev, "Skin Temperature: %u\n", in->ev_info.skin_temperature); + dev_dbg(dev->dev, "Avg C0 Residency: %u\n", in->ev_info.avg_c0residency); + dev_dbg(dev->dev, "Max C0 Residency: %u\n", in->ev_info.max_c0residency); + dev_dbg(dev->dev, "GFX Busy: %u\n", in->ev_info.gfx_busy); + dev_dbg(dev->dev, "LID State: %s\n", in->ev_info.lid_state ? "close" : "open"); + dev_dbg(dev->dev, "==== TA inputs END ====\n"); +} +#else +void amd_pmf_dump_ta_inputs(struct amd_pmf_dev *dev, struct ta_pmf_enact_table *in) {} +#endif + static void amd_pmf_get_smu_info(struct amd_pmf_dev *dev, struct ta_pmf_enact_table *in) { u16 max, avg = 0; diff --git a/drivers/platform/x86/amd/pmf/tee-if.c b/drivers/platform/x86/amd/pmf/tee-if.c index 8811631c7be5..38b75198cc3f 100644 --- a/drivers/platform/x86/amd/pmf/tee-if.c +++ b/drivers/platform/x86/amd/pmf/tee-if.c @@ -182,6 +182,7 @@ static int amd_pmf_invoke_cmd_enact(struct amd_pmf_dev *dev) } if (ta_sm->pmf_result == TA_PMF_TYPE_SUCCESS && out->actions_count) { + amd_pmf_dump_ta_inputs(dev, in); dev_dbg(dev->dev, "action count:%u result:%x\n", out->actions_count, ta_sm->pmf_result); amd_pmf_apply_policies(dev, out); -- cgit From 10817f28e5337e5ddb873c8431d4db8d93712587 Mon Sep 17 00:00:00 2001 From: Shyam Sundar S K Date: Tue, 12 Dec 2023 07:17:04 +0530 Subject: platform/x86/amd/pmf: Add capability to sideload of policy binary A policy binary is OS agnostic, and the same policies are expected to work across the OSes. At times it becomes difficult to debug when the policies inside the policy binaries starts to misbehave. Add a way to sideload such policies independently to debug them via a debugfs entry. Reviewed-by: Mario Limonciello Signed-off-by: Shyam Sundar S K Link: https://lore.kernel.org/r/20231212014705.2017474-12-Shyam-sundar.S-k@amd.com Reviewed-by: Hans de Goede Signed-off-by: Hans de Goede --- drivers/platform/x86/amd/pmf/pmf.h | 1 + drivers/platform/x86/amd/pmf/tee-if.c | 63 +++++++++++++++++++++++++++++++++++ 2 files changed, 64 insertions(+) diff --git a/drivers/platform/x86/amd/pmf/pmf.h b/drivers/platform/x86/amd/pmf/pmf.h index 55cd2b301bbb..16999c5b334f 100644 --- a/drivers/platform/x86/amd/pmf/pmf.h +++ b/drivers/platform/x86/amd/pmf/pmf.h @@ -219,6 +219,7 @@ struct amd_pmf_dev { bool cnqf_supported; struct notifier_block pwr_src_notifier; /* Smart PC solution builder */ + struct dentry *esbin; unsigned char *policy_buf; u32 policy_sz; struct tee_context *tee_ctx; diff --git a/drivers/platform/x86/amd/pmf/tee-if.c b/drivers/platform/x86/amd/pmf/tee-if.c index 38b75198cc3f..72248407138f 100644 --- a/drivers/platform/x86/amd/pmf/tee-if.c +++ b/drivers/platform/x86/amd/pmf/tee-if.c @@ -8,6 +8,7 @@ * Author: Shyam Sundar S K */ +#include #include #include #include "pmf.h" @@ -16,9 +17,14 @@ /* Policy binary actions sampling frequency (in ms) */ static int pb_actions_ms = MSEC_PER_SEC; +/* Sideload policy binaries to debug policy failures */ +static bool pb_side_load; + #ifdef CONFIG_AMD_PMF_DEBUG module_param(pb_actions_ms, int, 0644); MODULE_PARM_DESC(pb_actions_ms, "Policy binary actions sampling frequency (default = 1000ms)"); +module_param(pb_side_load, bool, 0444); +MODULE_PARM_DESC(pb_side_load, "Sideload policy binaries debug policy failures"); #endif static const uuid_t amd_pmf_ta_uuid = UUID_INIT(0x6fd93b77, 0x3fb8, 0x524d, @@ -269,6 +275,57 @@ static int amd_pmf_start_policy_engine(struct amd_pmf_dev *dev) return 0; } +#ifdef CONFIG_AMD_PMF_DEBUG +static ssize_t amd_pmf_get_pb_data(struct file *filp, const char __user *buf, + size_t length, loff_t *pos) +{ + struct amd_pmf_dev *dev = filp->private_data; + unsigned char *new_policy_buf; + int ret; + + /* Policy binary size cannot exceed POLICY_BUF_MAX_SZ */ + if (length > POLICY_BUF_MAX_SZ || length == 0) + return -EINVAL; + + /* re-alloc to the new buffer length of the policy binary */ + new_policy_buf = kzalloc(length, GFP_KERNEL); + if (!new_policy_buf) + return -ENOMEM; + + if (copy_from_user(new_policy_buf, buf, length)) + return -EFAULT; + + kfree(dev->policy_buf); + dev->policy_buf = new_policy_buf; + dev->policy_sz = length; + + ret = amd_pmf_start_policy_engine(dev); + if (ret) + return -EINVAL; + + return length; +} + +static const struct file_operations pb_fops = { + .write = amd_pmf_get_pb_data, + .open = simple_open, +}; + +static void amd_pmf_open_pb(struct amd_pmf_dev *dev, struct dentry *debugfs_root) +{ + dev->esbin = debugfs_create_dir("pb", debugfs_root); + debugfs_create_file("update_policy", 0644, dev->esbin, dev, &pb_fops); +} + +static void amd_pmf_remove_pb(struct amd_pmf_dev *dev) +{ + debugfs_remove_recursive(dev->esbin); +} +#else +static void amd_pmf_open_pb(struct amd_pmf_dev *dev, struct dentry *debugfs_root) {} +static void amd_pmf_remove_pb(struct amd_pmf_dev *dev) {} +#endif + static int amd_pmf_get_bios_buffer(struct amd_pmf_dev *dev) { dev->policy_buf = kzalloc(dev->policy_sz, GFP_KERNEL); @@ -281,6 +338,9 @@ static int amd_pmf_get_bios_buffer(struct amd_pmf_dev *dev) memcpy(dev->policy_buf, dev->policy_base, dev->policy_sz); + if (pb_side_load) + amd_pmf_open_pb(dev, dev->dbgfs_dir); + return amd_pmf_start_policy_engine(dev); } @@ -393,6 +453,9 @@ int amd_pmf_init_smart_pc(struct amd_pmf_dev *dev) void amd_pmf_deinit_smart_pc(struct amd_pmf_dev *dev) { + if (pb_side_load) + amd_pmf_remove_pb(dev); + kfree(dev->prev_data); kfree(dev->policy_buf); cancel_delayed_work_sync(&dev->pb_work); -- cgit From f533fa142258024dfe9a8fcba1a28d25a3cbe51b Mon Sep 17 00:00:00 2001 From: Shyam Sundar S K Date: Tue, 12 Dec 2023 07:17:05 +0530 Subject: platform/x86/amd/pmf: dump policy binary data MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Sometimes policy binary retrieved from the BIOS maybe incorrect that can end up in failing to enable the Smart PC solution feature. Use print_hex_dump_debug() to dump the policy binary in hex, so that we debug the issues related to the binary even before sending that to TA. Reviewed-by: Mario Limonciello Reviewed-by: Ilpo Järvinen Signed-off-by: Shyam Sundar S K Link: https://lore.kernel.org/r/20231212014705.2017474-13-Shyam-sundar.S-k@amd.com Signed-off-by: Hans de Goede --- drivers/platform/x86/amd/pmf/tee-if.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/drivers/platform/x86/amd/pmf/tee-if.c b/drivers/platform/x86/amd/pmf/tee-if.c index 72248407138f..502ce93d5cdd 100644 --- a/drivers/platform/x86/amd/pmf/tee-if.c +++ b/drivers/platform/x86/amd/pmf/tee-if.c @@ -276,6 +276,12 @@ static int amd_pmf_start_policy_engine(struct amd_pmf_dev *dev) } #ifdef CONFIG_AMD_PMF_DEBUG +static void amd_pmf_hex_dump_pb(struct amd_pmf_dev *dev) +{ + print_hex_dump_debug("(pb): ", DUMP_PREFIX_OFFSET, 16, 1, dev->policy_buf, + dev->policy_sz, false); +} + static ssize_t amd_pmf_get_pb_data(struct file *filp, const char __user *buf, size_t length, loff_t *pos) { @@ -299,6 +305,7 @@ static ssize_t amd_pmf_get_pb_data(struct file *filp, const char __user *buf, dev->policy_buf = new_policy_buf; dev->policy_sz = length; + amd_pmf_hex_dump_pb(dev); ret = amd_pmf_start_policy_engine(dev); if (ret) return -EINVAL; @@ -324,6 +331,7 @@ static void amd_pmf_remove_pb(struct amd_pmf_dev *dev) #else static void amd_pmf_open_pb(struct amd_pmf_dev *dev, struct dentry *debugfs_root) {} static void amd_pmf_remove_pb(struct amd_pmf_dev *dev) {} +static void amd_pmf_hex_dump_pb(struct amd_pmf_dev *dev) {} #endif static int amd_pmf_get_bios_buffer(struct amd_pmf_dev *dev) @@ -338,6 +346,7 @@ static int amd_pmf_get_bios_buffer(struct amd_pmf_dev *dev) memcpy(dev->policy_buf, dev->policy_base, dev->policy_sz); + amd_pmf_hex_dump_pb(dev); if (pb_side_load) amd_pmf_open_pb(dev, dev->dbgfs_dir); -- cgit From b23ae451d7b1ddb10536fa10f5dc9842ef66f387 Mon Sep 17 00:00:00 2001 From: Colin Ian King Date: Fri, 15 Dec 2023 11:27:46 +0000 Subject: platform/x86: silicom-platform: Fix spelling mistake "platfomr" -> "platform" MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit There is a spelling mistake in a literal string. Fix it. Signed-off-by: Colin Ian King Reviewed-by: Ilpo Järvinen Link: https://lore.kernel.org/r/20231215112746.13752-1-colin.i.king@gmail.com Signed-off-by: Hans de Goede --- drivers/platform/x86/silicom-platform.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/platform/x86/silicom-platform.c b/drivers/platform/x86/silicom-platform.c index 84b92b3f9f4b..6ce43ccb3112 100644 --- a/drivers/platform/x86/silicom-platform.c +++ b/drivers/platform/x86/silicom-platform.c @@ -866,7 +866,7 @@ static int silicom_fan_control_read_labels(struct device *dev, { switch (type) { case hwmon_fan: - *str = "Silicom_platfomr: Fan Speed"; + *str = "Silicom_platform: Fan Speed"; return 0; case hwmon_temp: *str = "Silicom_platform: Thermostat Sensor"; -- cgit From 784a00474633aa7ff4940b3adf74d900e54f6a36 Mon Sep 17 00:00:00 2001 From: Rajvi Jingar Date: Fri, 15 Dec 2023 16:51:46 -0800 Subject: platform/x86/intel/vsec: Add support for Lunar Lake M MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add Lunar Lake M PMT telemetry support. Signed-off-by: Rajvi Jingar Reviewed-by: Ilpo Järvinen Link: https://lore.kernel.org/r/20231216005146.1735455-1-rajvi.jingar@linux.intel.com Reviewed-by: Hans de Goede Signed-off-by: Hans de Goede --- drivers/platform/x86/intel/vsec.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/drivers/platform/x86/intel/vsec.c b/drivers/platform/x86/intel/vsec.c index b68586731e45..778eb0aa3479 100644 --- a/drivers/platform/x86/intel/vsec.c +++ b/drivers/platform/x86/intel/vsec.c @@ -419,6 +419,11 @@ static const struct intel_vsec_platform_info tgl_info = { .quirks = VSEC_QUIRK_TABLE_SHIFT | VSEC_QUIRK_EARLY_HW, }; +/* LNL info */ +static const struct intel_vsec_platform_info lnl_info = { + .caps = VSEC_CAP_TELEMETRY | VSEC_CAP_WATCHER, +}; + #define PCI_DEVICE_ID_INTEL_VSEC_ADL 0x467d #define PCI_DEVICE_ID_INTEL_VSEC_DG1 0x490e #define PCI_DEVICE_ID_INTEL_VSEC_MTL_M 0x7d0d @@ -426,6 +431,7 @@ static const struct intel_vsec_platform_info tgl_info = { #define PCI_DEVICE_ID_INTEL_VSEC_OOBMSM 0x09a7 #define PCI_DEVICE_ID_INTEL_VSEC_RPL 0xa77d #define PCI_DEVICE_ID_INTEL_VSEC_TGL 0x9a0d +#define PCI_DEVICE_ID_INTEL_VSEC_LNL_M 0x647d static const struct pci_device_id intel_vsec_pci_ids[] = { { PCI_DEVICE_DATA(INTEL, VSEC_ADL, &tgl_info) }, { PCI_DEVICE_DATA(INTEL, VSEC_DG1, &dg1_info) }, @@ -434,6 +440,7 @@ static const struct pci_device_id intel_vsec_pci_ids[] = { { PCI_DEVICE_DATA(INTEL, VSEC_OOBMSM, &oobmsm_info) }, { PCI_DEVICE_DATA(INTEL, VSEC_RPL, &tgl_info) }, { PCI_DEVICE_DATA(INTEL, VSEC_TGL, &tgl_info) }, + { PCI_DEVICE_DATA(INTEL, VSEC_LNL_M, &lnl_info) }, { } }; MODULE_DEVICE_TABLE(pci, intel_vsec_pci_ids); -- cgit From a92d3078244891c1bc4dc2112ae58b416875d296 Mon Sep 17 00:00:00 2001 From: Rajvi Jingar Date: Fri, 15 Dec 2023 17:17:02 -0800 Subject: platform/x86/intel/pmc: Fix in pmc_core_ssram_get_pmc() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Passing PMC_IDX_MAIN in pmc_core_pmc_add() adds only primary pmc to pmcdev. Use pmc_idx instead to add all available pmcs. Fixes: a01486dc4bb1 ("platform/x86/intel/pmc: Cleanup SSRAM discovery") Signed-off-by: Rajvi Jingar Reviewed-by: Ilpo Järvinen Link: https://lore.kernel.org/r/20231216011702.1976408-1-rajvi.jingar@linux.intel.com Signed-off-by: Hans de Goede --- drivers/platform/x86/intel/pmc/core_ssram.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/platform/x86/intel/pmc/core_ssram.c b/drivers/platform/x86/intel/pmc/core_ssram.c index 3501c7bd6b33..55e54207987c 100644 --- a/drivers/platform/x86/intel/pmc/core_ssram.c +++ b/drivers/platform/x86/intel/pmc/core_ssram.c @@ -287,7 +287,7 @@ pmc_core_ssram_get_pmc(struct pmc_dev *pmcdev, int pmc_idx, u32 offset) if (!map) return -ENODEV; - return pmc_core_pmc_add(pmcdev, pwrm_base, map, PMC_IDX_MAIN); + return pmc_core_pmc_add(pmcdev, pwrm_base, map, pmc_idx); } int pmc_core_ssram_init(struct pmc_dev *pmcdev) -- cgit From 41dd6822949ec6e83416a0e245f32a726110056a Mon Sep 17 00:00:00 2001 From: Armin Wolf Date: Mon, 18 Dec 2023 20:24:15 +0100 Subject: platform/x86: wmi: Remove unused variable in address space handler MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The variable "i" is always zero and only used in shift operations. Remove it to make the code more readable. Reviewed-by: Ilpo Järvinen Reviewed-by: Hans de Goede Signed-off-by: Armin Wolf Link: https://lore.kernel.org/r/20231218192420.305411-2-W_Armin@gmx.de Signed-off-by: Hans de Goede --- drivers/platform/x86/wmi.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/platform/x86/wmi.c b/drivers/platform/x86/wmi.c index 7303702290e5..906d3a2831ae 100644 --- a/drivers/platform/x86/wmi.c +++ b/drivers/platform/x86/wmi.c @@ -1144,7 +1144,7 @@ acpi_wmi_ec_space_handler(u32 function, acpi_physical_address address, u32 bits, u64 *value, void *handler_context, void *region_context) { - int result = 0, i = 0; + int result = 0; u8 temp = 0; if ((address > 0xFF) || !value) @@ -1158,9 +1158,9 @@ acpi_wmi_ec_space_handler(u32 function, acpi_physical_address address, if (function == ACPI_READ) { result = ec_read(address, &temp); - (*value) |= ((u64)temp) << i; + *value = temp; } else { - temp = 0xff & ((*value) >> i); + temp = 0xff & *value; result = ec_write(address, temp); } -- cgit From 22574e17626391ad969af9a13aaa58a1b37ad384 Mon Sep 17 00:00:00 2001 From: Armin Wolf Date: Mon, 18 Dec 2023 20:24:16 +0100 Subject: platform/x86: wmi: Remove ACPI handlers after WMI devices When removing the ACPI notify/address space handlers, the WMI devices are still active and might still depend on ACPI EC access or WMI events. Fix this by removing the ACPI handlers after all WMI devices associated with an ACPI device have been removed. Reviewed-by: Hans de Goede Signed-off-by: Armin Wolf Link: https://lore.kernel.org/r/20231218192420.305411-3-W_Armin@gmx.de Signed-off-by: Hans de Goede --- drivers/platform/x86/wmi.c | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/drivers/platform/x86/wmi.c b/drivers/platform/x86/wmi.c index 906d3a2831ae..2120c13e1676 100644 --- a/drivers/platform/x86/wmi.c +++ b/drivers/platform/x86/wmi.c @@ -1239,13 +1239,12 @@ static void acpi_wmi_remove(struct platform_device *device) struct acpi_device *acpi_device = ACPI_COMPANION(&device->dev); struct device *wmi_bus_device = dev_get_drvdata(&device->dev); - acpi_remove_notify_handler(acpi_device->handle, ACPI_ALL_NOTIFY, - acpi_wmi_notify_handler); - acpi_remove_address_space_handler(acpi_device->handle, - ACPI_ADR_SPACE_EC, &acpi_wmi_ec_space_handler); - device_for_each_child_reverse(wmi_bus_device, NULL, wmi_remove_device); device_unregister(wmi_bus_device); + + acpi_remove_notify_handler(acpi_device->handle, ACPI_ALL_NOTIFY, acpi_wmi_notify_handler); + acpi_remove_address_space_handler(acpi_device->handle, ACPI_ADR_SPACE_EC, + &acpi_wmi_ec_space_handler); } static int acpi_wmi_probe(struct platform_device *device) -- cgit From 08e7f4d61d3f043f32e35c224898d0869f8b1530 Mon Sep 17 00:00:00 2001 From: Armin Wolf Date: Mon, 18 Dec 2023 20:24:17 +0100 Subject: platform/x86: wmi: Use devres for resource handling Use devres for cleaning up the ACPI handlers and the WMI bus device to simplify the error handling. Reviewed-by: Hans de Goede Signed-off-by: Armin Wolf Link: https://lore.kernel.org/r/20231218192420.305411-4-W_Armin@gmx.de Signed-off-by: Hans de Goede --- drivers/platform/x86/wmi.c | 58 ++++++++++++++++++++++++++++------------------ 1 file changed, 35 insertions(+), 23 deletions(-) diff --git a/drivers/platform/x86/wmi.c b/drivers/platform/x86/wmi.c index 2120c13e1676..4bc5da70c1b0 100644 --- a/drivers/platform/x86/wmi.c +++ b/drivers/platform/x86/wmi.c @@ -1236,17 +1236,33 @@ static int wmi_remove_device(struct device *dev, void *data) static void acpi_wmi_remove(struct platform_device *device) { - struct acpi_device *acpi_device = ACPI_COMPANION(&device->dev); struct device *wmi_bus_device = dev_get_drvdata(&device->dev); device_for_each_child_reverse(wmi_bus_device, NULL, wmi_remove_device); - device_unregister(wmi_bus_device); +} + +static void acpi_wmi_remove_notify_handler(void *data) +{ + struct acpi_device *acpi_device = data; acpi_remove_notify_handler(acpi_device->handle, ACPI_ALL_NOTIFY, acpi_wmi_notify_handler); +} + +static void acpi_wmi_remove_address_space_handler(void *data) +{ + struct acpi_device *acpi_device = data; + acpi_remove_address_space_handler(acpi_device->handle, ACPI_ADR_SPACE_EC, &acpi_wmi_ec_space_handler); } +static void acpi_wmi_remove_bus_device(void *data) +{ + struct device *wmi_bus_dev = data; + + device_unregister(wmi_bus_dev); +} + static int acpi_wmi_probe(struct platform_device *device) { struct acpi_device *acpi_device; @@ -1268,6 +1284,10 @@ static int acpi_wmi_probe(struct platform_device *device) dev_err(&device->dev, "Error installing EC region handler\n"); return -ENODEV; } + error = devm_add_action_or_reset(&device->dev, acpi_wmi_remove_address_space_handler, + acpi_device); + if (error < 0) + return error; status = acpi_install_notify_handler(acpi_device->handle, ACPI_ALL_NOTIFY, @@ -1275,39 +1295,31 @@ static int acpi_wmi_probe(struct platform_device *device) NULL); if (ACPI_FAILURE(status)) { dev_err(&device->dev, "Error installing notify handler\n"); - error = -ENODEV; - goto err_remove_ec_handler; + return -ENODEV; } + error = devm_add_action_or_reset(&device->dev, acpi_wmi_remove_notify_handler, + acpi_device); + if (error < 0) + return error; wmi_bus_dev = device_create(&wmi_bus_class, &device->dev, MKDEV(0, 0), NULL, "wmi_bus-%s", dev_name(&device->dev)); - if (IS_ERR(wmi_bus_dev)) { - error = PTR_ERR(wmi_bus_dev); - goto err_remove_notify_handler; - } + if (IS_ERR(wmi_bus_dev)) + return PTR_ERR(wmi_bus_dev); + + error = devm_add_action_or_reset(&device->dev, acpi_wmi_remove_bus_device, wmi_bus_dev); + if (error < 0) + return error; + dev_set_drvdata(&device->dev, wmi_bus_dev); error = parse_wdg(wmi_bus_dev, device); if (error) { pr_err("Failed to parse WDG method\n"); - goto err_remove_busdev; + return error; } return 0; - -err_remove_busdev: - device_unregister(wmi_bus_dev); - -err_remove_notify_handler: - acpi_remove_notify_handler(acpi_device->handle, ACPI_ALL_NOTIFY, - acpi_wmi_notify_handler); - -err_remove_ec_handler: - acpi_remove_address_space_handler(acpi_device->handle, - ACPI_ADR_SPACE_EC, - &acpi_wmi_ec_space_handler); - - return error; } int __must_check __wmi_driver_register(struct wmi_driver *driver, -- cgit From 095fa72a19f13b15629611d52447cb17ce223bcd Mon Sep 17 00:00:00 2001 From: Armin Wolf Date: Mon, 18 Dec 2023 20:24:18 +0100 Subject: platform/x86: wmi: Create WMI bus device first Create the WMI bus device first so that it can be used by the ACPI handlers. Reviewed-by: Hans de Goede Signed-off-by: Armin Wolf Link: https://lore.kernel.org/r/20231218192420.305411-5-W_Armin@gmx.de Signed-off-by: Hans de Goede --- drivers/platform/x86/wmi.c | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/drivers/platform/x86/wmi.c b/drivers/platform/x86/wmi.c index 4bc5da70c1b0..e2bfdc61c4ce 100644 --- a/drivers/platform/x86/wmi.c +++ b/drivers/platform/x86/wmi.c @@ -1276,6 +1276,17 @@ static int acpi_wmi_probe(struct platform_device *device) return -ENODEV; } + wmi_bus_dev = device_create(&wmi_bus_class, &device->dev, MKDEV(0, 0), NULL, "wmi_bus-%s", + dev_name(&device->dev)); + if (IS_ERR(wmi_bus_dev)) + return PTR_ERR(wmi_bus_dev); + + error = devm_add_action_or_reset(&device->dev, acpi_wmi_remove_bus_device, wmi_bus_dev); + if (error < 0) + return error; + + dev_set_drvdata(&device->dev, wmi_bus_dev); + status = acpi_install_address_space_handler(acpi_device->handle, ACPI_ADR_SPACE_EC, &acpi_wmi_ec_space_handler, @@ -1302,17 +1313,6 @@ static int acpi_wmi_probe(struct platform_device *device) if (error < 0) return error; - wmi_bus_dev = device_create(&wmi_bus_class, &device->dev, MKDEV(0, 0), - NULL, "wmi_bus-%s", dev_name(&device->dev)); - if (IS_ERR(wmi_bus_dev)) - return PTR_ERR(wmi_bus_dev); - - error = devm_add_action_or_reset(&device->dev, acpi_wmi_remove_bus_device, wmi_bus_dev); - if (error < 0) - return error; - - dev_set_drvdata(&device->dev, wmi_bus_dev); - error = parse_wdg(wmi_bus_dev, device); if (error) { pr_err("Failed to parse WDG method\n"); -- cgit From 2c933755eaaa82fffe0201f376713fa2070b9428 Mon Sep 17 00:00:00 2001 From: Armin Wolf Date: Mon, 18 Dec 2023 20:24:19 +0100 Subject: platform/x86: wmi: Decouple ACPI notify handler from wmi_block_list Currently, the ACPI notify handler searches all WMI devices for a matching WMI event device. This is inefficient since only WMI devices associated with the notified ACPI device need to be searched. Use the WMI bus device and device_for_each_child() to search for a matching WMI event device instead. Reviewed-by: Hans de Goede Signed-off-by: Armin Wolf Link: https://lore.kernel.org/r/20231218192420.305411-6-W_Armin@gmx.de Signed-off-by: Hans de Goede --- drivers/platform/x86/wmi.c | 46 ++++++++++++++++++++-------------------------- 1 file changed, 20 insertions(+), 26 deletions(-) diff --git a/drivers/platform/x86/wmi.c b/drivers/platform/x86/wmi.c index e2bfdc61c4ce..559a99ebc624 100644 --- a/drivers/platform/x86/wmi.c +++ b/drivers/platform/x86/wmi.c @@ -1176,24 +1176,13 @@ acpi_wmi_ec_space_handler(u32 function, acpi_physical_address address, } } -static void acpi_wmi_notify_handler(acpi_handle handle, u32 event, - void *context) +static int wmi_notify_device(struct device *dev, void *data) { - struct wmi_block *wblock = NULL, *iter; - - list_for_each_entry(iter, &wmi_block_list, list) { - struct guid_block *block = &iter->gblock; - - if (iter->acpi_device->handle == handle && - (block->flags & ACPI_WMI_EVENT) && - (block->notify_id == event)) { - wblock = iter; - break; - } - } + struct wmi_block *wblock = dev_to_wblock(dev); + u32 *event = data; - if (!wblock) - return; + if (!(wblock->gblock.flags & ACPI_WMI_EVENT && wblock->gblock.notify_id == *event)) + return 0; /* If a driver is bound, then notify the driver. */ if (test_bit(WMI_PROBED, &wblock->flags) && wblock->dev.dev.driver) { @@ -1205,7 +1194,7 @@ static void acpi_wmi_notify_handler(acpi_handle handle, u32 event, status = get_event_data(wblock, &evdata); if (ACPI_FAILURE(status)) { dev_warn(&wblock->dev.dev, "failed to get event data\n"); - return; + return -EIO; } } @@ -1215,13 +1204,20 @@ static void acpi_wmi_notify_handler(acpi_handle handle, u32 event, kfree(evdata.pointer); } else if (wblock->handler) { /* Legacy handler */ - wblock->handler(event, wblock->handler_data); + wblock->handler(*event, wblock->handler_data); } - acpi_bus_generate_netlink_event( - wblock->acpi_device->pnp.device_class, - dev_name(&wblock->dev.dev), - event, 0); + acpi_bus_generate_netlink_event(wblock->acpi_device->pnp.device_class, + dev_name(&wblock->dev.dev), *event, 0); + + return -EBUSY; +} + +static void acpi_wmi_notify_handler(acpi_handle handle, u32 event, void *context) +{ + struct device *wmi_bus_dev = context; + + device_for_each_child(wmi_bus_dev, &event, wmi_notify_device); } static int wmi_remove_device(struct device *dev, void *data) @@ -1300,10 +1296,8 @@ static int acpi_wmi_probe(struct platform_device *device) if (error < 0) return error; - status = acpi_install_notify_handler(acpi_device->handle, - ACPI_ALL_NOTIFY, - acpi_wmi_notify_handler, - NULL); + status = acpi_install_notify_handler(acpi_device->handle, ACPI_ALL_NOTIFY, + acpi_wmi_notify_handler, wmi_bus_dev); if (ACPI_FAILURE(status)) { dev_err(&device->dev, "Error installing notify handler\n"); return -ENODEV; -- cgit From bd142914f805b88dcb15acaab9fbc9bea666dd32 Mon Sep 17 00:00:00 2001 From: Armin Wolf Date: Mon, 18 Dec 2023 20:24:20 +0100 Subject: platform/x86: wmi: Simplify get_subobj_info() All callers who call get_subobj_info() with **info being NULL should better use acpi_has_method() instead. Convert the only caller who does this to acpi_has_method() to drop the dummy info handling. Reviewed-by: Hans de Goede Signed-off-by: Armin Wolf Link: https://lore.kernel.org/r/20231218192420.305411-7-W_Armin@gmx.de Signed-off-by: Hans de Goede --- drivers/platform/x86/wmi.c | 16 +++++----------- 1 file changed, 5 insertions(+), 11 deletions(-) diff --git a/drivers/platform/x86/wmi.c b/drivers/platform/x86/wmi.c index 559a99ebc624..a7cfcbf92432 100644 --- a/drivers/platform/x86/wmi.c +++ b/drivers/platform/x86/wmi.c @@ -132,23 +132,19 @@ static const void *find_guid_context(struct wmi_block *wblock, static int get_subobj_info(acpi_handle handle, const char *pathname, struct acpi_device_info **info) { - struct acpi_device_info *dummy_info, **info_ptr; acpi_handle subobj_handle; acpi_status status; - status = acpi_get_handle(handle, (char *)pathname, &subobj_handle); + status = acpi_get_handle(handle, pathname, &subobj_handle); if (status == AE_NOT_FOUND) return -ENOENT; - else if (ACPI_FAILURE(status)) - return -EIO; - info_ptr = info ? info : &dummy_info; - status = acpi_get_object_info(subobj_handle, info_ptr); if (ACPI_FAILURE(status)) return -EIO; - if (!info) - kfree(dummy_info); + status = acpi_get_object_info(subobj_handle, info); + if (ACPI_FAILURE(status)) + return -EIO; return 0; } @@ -998,9 +994,7 @@ static int wmi_create_device(struct device *wmi_bus_dev, kfree(info); get_acpi_method_name(wblock, 'S', method); - result = get_subobj_info(device->handle, method, NULL); - - if (result == 0) + if (acpi_has_method(device->handle, method)) wblock->dev.setable = true; out_init: -- cgit From 1f5e56c9f6cc92c45d27adfe78fb54c716fed2e2 Mon Sep 17 00:00:00 2001 From: Rajvi Jingar Date: Mon, 18 Dec 2023 20:22:09 -0800 Subject: platform/x86/intel/pmc: Fix in mtl_punit_pmt_init() pci_get_domain_bus_and_slot() increases the reference count on the pci device that is used to register the endpoint. In case of failure in registration, decrease reference count using pci_dev_put(pcidev) before returning. Fixes: 6e7964855381 ("platform/x86/intel/pmc: Show Die C6 counter on Meteor Lake") Signed-off-by: Rajvi Jingar Signed-off-by: David E. Box Link: https://lore.kernel.org/r/20231219042216.2592029-1-rajvi.jingar@linux.intel.com Reviewed-by: Hans de Goede Signed-off-by: Hans de Goede --- drivers/platform/x86/intel/pmc/mtl.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/platform/x86/intel/pmc/mtl.c b/drivers/platform/x86/intel/pmc/mtl.c index 38c2f946ec23..fb59dffccf28 100644 --- a/drivers/platform/x86/intel/pmc/mtl.c +++ b/drivers/platform/x86/intel/pmc/mtl.c @@ -985,6 +985,7 @@ static void mtl_punit_pmt_init(struct pmc_dev *pmcdev) } ep = pmt_telem_find_and_register_endpoint(pcidev, MTL_PMT_DMU_GUID, 0); + pci_dev_put(pcidev); if (IS_ERR(ep)) { dev_err(&pmcdev->pdev->dev, "pmc_core: couldn't get DMU telem endpoint, %ld\n", @@ -992,7 +993,6 @@ static void mtl_punit_pmt_init(struct pmc_dev *pmcdev) return; } - pci_dev_put(pcidev); pmcdev->punit_ep = ep; pmcdev->has_die_c6 = true; -- cgit From b6258fa2c7b3dd23e362801410f171567d0d16af Mon Sep 17 00:00:00 2001 From: Rajvi Jingar Date: Mon, 18 Dec 2023 20:22:10 -0800 Subject: platform/x86/intel/pmc: Add PSON residency counter Tiger Lake platform onwards, devices have the capability to track the duration of time that their Power Supply Units (PSUs) are turned off during S0ix. This patch adds a debugfs file `pson_residency_usec` to provide access to this counter. Signed-off-by: Michael Bottini Signed-off-by: Rajvi Jingar Signed-off-by: David E. Box Link: https://lore.kernel.org/r/20231219042216.2592029-2-rajvi.jingar@linux.intel.com Reviewed-by: Hans de Goede Signed-off-by: Hans de Goede --- drivers/platform/x86/intel/pmc/core.c | 37 +++++++++++++++++++++++++++++++++++ drivers/platform/x86/intel/pmc/core.h | 2 ++ 2 files changed, 39 insertions(+) diff --git a/drivers/platform/x86/intel/pmc/core.c b/drivers/platform/x86/intel/pmc/core.c index 983e3a8f4910..91e5e500eb41 100644 --- a/drivers/platform/x86/intel/pmc/core.c +++ b/drivers/platform/x86/intel/pmc/core.c @@ -208,6 +208,20 @@ static int pmc_core_dev_state_get(void *data, u64 *val) DEFINE_DEBUGFS_ATTRIBUTE(pmc_core_dev_state, pmc_core_dev_state_get, NULL, "%llu\n"); +static int pmc_core_pson_residency_get(void *data, u64 *val) +{ + struct pmc *pmc = data; + const struct pmc_reg_map *map = pmc->map; + u32 value; + + value = pmc_core_reg_read(pmc, map->pson_residency_offset); + *val = (u64)value * map->pson_residency_counter_step; + + return 0; +} + +DEFINE_DEBUGFS_ATTRIBUTE(pmc_core_pson_residency, pmc_core_pson_residency_get, NULL, "%llu\n"); + static int pmc_core_check_read_lock_bit(struct pmc *pmc) { u32 value; @@ -1092,6 +1106,24 @@ int get_primary_reg_base(struct pmc *pmc) return 0; } +static bool pmc_core_is_pson_residency_enabled(struct pmc_dev *pmcdev) +{ + struct platform_device *pdev = pmcdev->pdev; + struct acpi_device *adev = ACPI_COMPANION(&pdev->dev); + u8 val; + + if (!adev) + return false; + + if (fwnode_property_read_u8(acpi_fwnode_handle(adev), + "intel-cec-pson-switching-enabled-in-s0", + &val)) + return false; + + return val == 1; +} + + static void pmc_core_dbgfs_unregister(struct pmc_dev *pmcdev) { debugfs_remove_recursive(pmcdev->dbgfs_dir); @@ -1162,6 +1194,11 @@ static void pmc_core_dbgfs_register(struct pmc_dev *pmcdev) &pmc_core_substate_req_regs_fops); } + if (primary_pmc->map->pson_residency_offset && pmc_core_is_pson_residency_enabled(pmcdev)) { + debugfs_create_file("pson_residency_usec", 0444, + pmcdev->dbgfs_dir, primary_pmc, &pmc_core_pson_residency); + } + if (pmcdev->has_die_c6) { debugfs_create_file("die_c6_us_show", 0444, pmcdev->dbgfs_dir, pmcdev, diff --git a/drivers/platform/x86/intel/pmc/core.h b/drivers/platform/x86/intel/pmc/core.h index 6d7673145f90..91cb34a6505c 100644 --- a/drivers/platform/x86/intel/pmc/core.h +++ b/drivers/platform/x86/intel/pmc/core.h @@ -323,6 +323,8 @@ struct pmc_reg_map { const u32 lpm_live_status_offset; const u32 etr3_offset; const u8 *lpm_reg_index; + const u32 pson_residency_offset; + const u32 pson_residency_counter_step; }; /** -- cgit From 544f7b7f651cf5745f3a1f3d28b298ee2b128eb1 Mon Sep 17 00:00:00 2001 From: Rajvi Jingar Date: Mon, 18 Dec 2023 20:22:11 -0800 Subject: platform/x86/intel/pmc: Add regmap for Tiger Lake H PCH Tiger Lake H PCH is same as Tiger Lake LP PCH from the driver perspective with the addition of the PSON residency counter. Add regmap for TGP H to add PSON register offsets for Tiger Lake H PCH. Signed-off-by: Rajvi Jingar Link: https://lore.kernel.org/r/20231219042216.2592029-3-rajvi.jingar@linux.intel.com Reviewed-by: Hans de Goede Signed-off-by: Hans de Goede --- drivers/platform/x86/intel/pmc/core.c | 10 ++++---- drivers/platform/x86/intel/pmc/core.h | 6 +++++ drivers/platform/x86/intel/pmc/tgl.c | 48 ++++++++++++++++++++++++++++++++++- 3 files changed, 58 insertions(+), 6 deletions(-) diff --git a/drivers/platform/x86/intel/pmc/core.c b/drivers/platform/x86/intel/pmc/core.c index 91e5e500eb41..e95105ad1243 100644 --- a/drivers/platform/x86/intel/pmc/core.c +++ b/drivers/platform/x86/intel/pmc/core.c @@ -1216,15 +1216,15 @@ static const struct x86_cpu_id intel_pmc_core_ids[] = { X86_MATCH_INTEL_FAM6_MODEL(ICELAKE_NNPI, icl_core_init), X86_MATCH_INTEL_FAM6_MODEL(COMETLAKE, cnp_core_init), X86_MATCH_INTEL_FAM6_MODEL(COMETLAKE_L, cnp_core_init), - X86_MATCH_INTEL_FAM6_MODEL(TIGERLAKE_L, tgl_core_init), + X86_MATCH_INTEL_FAM6_MODEL(TIGERLAKE_L, tgl_l_core_init), X86_MATCH_INTEL_FAM6_MODEL(TIGERLAKE, tgl_core_init), - X86_MATCH_INTEL_FAM6_MODEL(ATOM_TREMONT, tgl_core_init), + X86_MATCH_INTEL_FAM6_MODEL(ATOM_TREMONT, tgl_l_core_init), X86_MATCH_INTEL_FAM6_MODEL(ATOM_TREMONT_L, icl_core_init), X86_MATCH_INTEL_FAM6_MODEL(ROCKETLAKE, tgl_core_init), - X86_MATCH_INTEL_FAM6_MODEL(ALDERLAKE_L, tgl_core_init), - X86_MATCH_INTEL_FAM6_MODEL(ATOM_GRACEMONT, tgl_core_init), + X86_MATCH_INTEL_FAM6_MODEL(ALDERLAKE_L, tgl_l_core_init), + X86_MATCH_INTEL_FAM6_MODEL(ATOM_GRACEMONT, tgl_l_core_init), X86_MATCH_INTEL_FAM6_MODEL(ALDERLAKE, adl_core_init), - X86_MATCH_INTEL_FAM6_MODEL(RAPTORLAKE_P, tgl_core_init), + X86_MATCH_INTEL_FAM6_MODEL(RAPTORLAKE_P, tgl_l_core_init), X86_MATCH_INTEL_FAM6_MODEL(RAPTORLAKE, adl_core_init), X86_MATCH_INTEL_FAM6_MODEL(RAPTORLAKE_S, adl_core_init), X86_MATCH_INTEL_FAM6_MODEL(METEORLAKE_L, mtl_core_init), diff --git a/drivers/platform/x86/intel/pmc/core.h b/drivers/platform/x86/intel/pmc/core.h index 91cb34a6505c..d09962940ad6 100644 --- a/drivers/platform/x86/intel/pmc/core.h +++ b/drivers/platform/x86/intel/pmc/core.h @@ -223,6 +223,10 @@ enum ppfear_regs { #define TGL_LPM_PRI_OFFSET 0x1C7C #define TGL_LPM_NUM_MAPS 6 +/* Tigerlake PSON residency register */ +#define TGL_PSON_RESIDENCY_OFFSET 0x18f8 +#define TGL_PSON_RES_COUNTER_STEP 0x7A + /* Extended Test Mode Register 3 (CNL and later) */ #define ETR3_OFFSET 0x1048 #define ETR3_CF9GR BIT(20) @@ -507,6 +511,8 @@ int spt_core_init(struct pmc_dev *pmcdev); int cnp_core_init(struct pmc_dev *pmcdev); int icl_core_init(struct pmc_dev *pmcdev); int tgl_core_init(struct pmc_dev *pmcdev); +int tgl_l_core_init(struct pmc_dev *pmcdev); +int tgl_core_generic_init(struct pmc_dev *pmcdev, int pch_tp); int adl_core_init(struct pmc_dev *pmcdev); int mtl_core_init(struct pmc_dev *pmcdev); diff --git a/drivers/platform/x86/intel/pmc/tgl.c b/drivers/platform/x86/intel/pmc/tgl.c index d5f1d2223c5a..91fd725951e5 100644 --- a/drivers/platform/x86/intel/pmc/tgl.c +++ b/drivers/platform/x86/intel/pmc/tgl.c @@ -13,6 +13,11 @@ #define ACPI_S0IX_DSM_UUID "57a6512e-3979-4e9d-9708-ff13b2508972" #define ACPI_GET_LOW_MODE_REGISTERS 1 +enum pch_type { + PCH_H, + PCH_LP +}; + const struct pmc_bit_map tgl_pfear_map[] = { {"PSF9", BIT(0)}, {"RES_66", BIT(1)}, @@ -205,6 +210,33 @@ const struct pmc_reg_map tgl_reg_map = { .etr3_offset = ETR3_OFFSET, }; +const struct pmc_reg_map tgl_h_reg_map = { + .pfear_sts = ext_tgl_pfear_map, + .slp_s0_offset = CNP_PMC_SLP_S0_RES_COUNTER_OFFSET, + .slp_s0_res_counter_step = TGL_PMC_SLP_S0_RES_COUNTER_STEP, + .ltr_show_sts = cnp_ltr_show_map, + .msr_sts = msr_map, + .ltr_ignore_offset = CNP_PMC_LTR_IGNORE_OFFSET, + .regmap_length = CNP_PMC_MMIO_REG_LEN, + .ppfear0_offset = CNP_PMC_HOST_PPFEAR0A, + .ppfear_buckets = ICL_PPFEAR_NUM_ENTRIES, + .pm_cfg_offset = CNP_PMC_PM_CFG_OFFSET, + .pm_read_disable_bit = CNP_PMC_READ_DISABLE_BIT, + .ltr_ignore_max = TGL_NUM_IP_IGN_ALLOWED, + .lpm_num_maps = TGL_LPM_NUM_MAPS, + .lpm_res_counter_step_x2 = TGL_PMC_LPM_RES_COUNTER_STEP_X2, + .lpm_sts_latch_en_offset = TGL_LPM_STS_LATCH_EN_OFFSET, + .lpm_en_offset = TGL_LPM_EN_OFFSET, + .lpm_priority_offset = TGL_LPM_PRI_OFFSET, + .lpm_residency_offset = TGL_LPM_RESIDENCY_OFFSET, + .lpm_sts = tgl_lpm_maps, + .lpm_status_offset = TGL_LPM_STATUS_OFFSET, + .lpm_live_status_offset = TGL_LPM_LIVE_STATUS_OFFSET, + .etr3_offset = ETR3_OFFSET, + .pson_residency_offset = TGL_PSON_RESIDENCY_OFFSET, + .pson_residency_counter_step = TGL_PSON_RES_COUNTER_STEP, +}; + void pmc_core_get_tgl_lpm_reqs(struct platform_device *pdev) { struct pmc_dev *pmcdev = platform_get_drvdata(pdev); @@ -253,12 +285,26 @@ free_acpi_obj: ACPI_FREE(out_obj); } +int tgl_l_core_init(struct pmc_dev *pmcdev) +{ + return tgl_core_generic_init(pmcdev, PCH_LP); +} + int tgl_core_init(struct pmc_dev *pmcdev) +{ + return tgl_core_generic_init(pmcdev, PCH_H); +} + +int tgl_core_generic_init(struct pmc_dev *pmcdev, int pch_tp) { struct pmc *pmc = pmcdev->pmcs[PMC_IDX_MAIN]; int ret; - pmc->map = &tgl_reg_map; + if (pch_tp == PCH_H) + pmc->map = &tgl_h_reg_map; + else + pmc->map = &tgl_reg_map; + ret = get_primary_reg_base(pmc); if (ret) return ret; -- cgit From d873f380525c502904737f592008d509cff20c78 Mon Sep 17 00:00:00 2001 From: Rajvi Jingar Date: Mon, 18 Dec 2023 20:22:12 -0800 Subject: platform/x86/intel/pmc: Add PSON residency counter for Alder Lake Add PSON register offsets for Alder Lake PCH that provides an access to PSON residency counter. Signed-off-by: Rajvi Jingar Link: https://lore.kernel.org/r/20231219042216.2592029-4-rajvi.jingar@linux.intel.com Reviewed-by: Hans de Goede Signed-off-by: Hans de Goede --- drivers/platform/x86/intel/pmc/adl.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/platform/x86/intel/pmc/adl.c b/drivers/platform/x86/intel/pmc/adl.c index 64c492391ede..882f2d5d8937 100644 --- a/drivers/platform/x86/intel/pmc/adl.c +++ b/drivers/platform/x86/intel/pmc/adl.c @@ -307,6 +307,8 @@ const struct pmc_reg_map adl_reg_map = { .lpm_sts = adl_lpm_maps, .lpm_status_offset = ADL_LPM_STATUS_OFFSET, .lpm_live_status_offset = ADL_LPM_LIVE_STATUS_OFFSET, + .pson_residency_offset = TGL_PSON_RESIDENCY_OFFSET, + .pson_residency_counter_step = TGL_PSON_RES_COUNTER_STEP, }; int adl_core_init(struct pmc_dev *pmcdev) -- cgit From d79c3c82ee82cc99ffde4c4f5fe69db35bcfb733 Mon Sep 17 00:00:00 2001 From: Rajvi Jingar Date: Mon, 18 Dec 2023 20:22:13 -0800 Subject: platform/x86/intel/pmc: Move common code to core.c Functions like mtl_set_device_d3() and mtl_punit_pmt_init() were added for Meteor Lake. To be able to use them in Arrow Lake and future platforms, move them to core.c. Also, to support different guids, add guid argument in pmc_core_punit_pmt_init() and to support different PCI function numbers, add func arg in pmc_core_ssram_init(). Signed-off-by: Rajvi Jingar Link: https://lore.kernel.org/r/20231219042216.2592029-5-rajvi.jingar@linux.intel.com Reviewed-by: Hans de Goede Signed-off-by: Hans de Goede --- drivers/platform/x86/intel/pmc/core.c | 45 ++++++++++++++++++++++ drivers/platform/x86/intel/pmc/core.h | 8 +++- drivers/platform/x86/intel/pmc/core_ssram.c | 4 +- drivers/platform/x86/intel/pmc/mtl.c | 60 +++-------------------------- 4 files changed, 60 insertions(+), 57 deletions(-) diff --git a/drivers/platform/x86/intel/pmc/core.c b/drivers/platform/x86/intel/pmc/core.c index e95105ad1243..ac446b0f2192 100644 --- a/drivers/platform/x86/intel/pmc/core.c +++ b/drivers/platform/x86/intel/pmc/core.c @@ -1106,6 +1106,51 @@ int get_primary_reg_base(struct pmc *pmc) return 0; } +void pmc_core_punit_pmt_init(struct pmc_dev *pmcdev, u32 guid) +{ + struct telem_endpoint *ep; + struct pci_dev *pcidev; + + pcidev = pci_get_domain_bus_and_slot(0, 0, PCI_DEVFN(10, 0)); + if (!pcidev) { + dev_err(&pmcdev->pdev->dev, "PUNIT PMT device not found."); + return; + } + + ep = pmt_telem_find_and_register_endpoint(pcidev, guid, 0); + pci_dev_put(pcidev); + if (IS_ERR(ep)) { + dev_err(&pmcdev->pdev->dev, + "pmc_core: couldn't get DMU telem endpoint %ld", + PTR_ERR(ep)); + return; + } + + pmcdev->punit_ep = ep; + + pmcdev->has_die_c6 = true; + pmcdev->die_c6_offset = MTL_PMT_DMU_DIE_C6_OFFSET; +} + +void pmc_core_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); + } +} + static bool pmc_core_is_pson_residency_enabled(struct pmc_dev *pmcdev) { struct platform_device *pdev = pmcdev->pdev; diff --git a/drivers/platform/x86/intel/pmc/core.h b/drivers/platform/x86/intel/pmc/core.h index d09962940ad6..e678a1205514 100644 --- a/drivers/platform/x86/intel/pmc/core.h +++ b/drivers/platform/x86/intel/pmc/core.h @@ -268,6 +268,10 @@ enum ppfear_regs { #define MTL_SOCM_PPFEAR_NUM_ENTRIES 8 #define MTL_IOE_PPFEAR_NUM_ENTRIES 10 +/* Die C6 from PUNIT telemetry */ +#define MTL_PMT_DMU_DIE_C6_OFFSET 15 +#define MTL_PMT_DMU_GUID 0x1A067102 + extern const char *pmc_lpm_modes[]; struct pmc_bit_map { @@ -504,8 +508,10 @@ extern int pmc_core_send_ltr_ignore(struct pmc_dev *pmcdev, u32 value); int pmc_core_resume_common(struct pmc_dev *pmcdev); int get_primary_reg_base(struct pmc *pmc); extern void pmc_core_get_low_power_modes(struct pmc_dev *pmcdev); +extern void pmc_core_punit_pmt_init(struct pmc_dev *pmcdev, u32 guid); +extern void pmc_core_set_device_d3(unsigned int device); -extern int pmc_core_ssram_init(struct pmc_dev *pmcdev); +extern int pmc_core_ssram_init(struct pmc_dev *pmcdev, int func); int spt_core_init(struct pmc_dev *pmcdev); int cnp_core_init(struct pmc_dev *pmcdev); diff --git a/drivers/platform/x86/intel/pmc/core_ssram.c b/drivers/platform/x86/intel/pmc/core_ssram.c index 55e54207987c..1bde86c54eb9 100644 --- a/drivers/platform/x86/intel/pmc/core_ssram.c +++ b/drivers/platform/x86/intel/pmc/core_ssram.c @@ -290,12 +290,12 @@ pmc_core_ssram_get_pmc(struct pmc_dev *pmcdev, int pmc_idx, u32 offset) return pmc_core_pmc_add(pmcdev, pwrm_base, map, pmc_idx); } -int pmc_core_ssram_init(struct pmc_dev *pmcdev) +int pmc_core_ssram_init(struct pmc_dev *pmcdev, int func) { struct pci_dev *pcidev; int ret; - pcidev = pci_get_domain_bus_and_slot(0, 0, PCI_DEVFN(20, 2)); + pcidev = pci_get_domain_bus_and_slot(0, 0, PCI_DEVFN(20, func)); if (!pcidev) return -ENODEV; diff --git a/drivers/platform/x86/intel/pmc/mtl.c b/drivers/platform/x86/intel/pmc/mtl.c index fb59dffccf28..d2470f800298 100644 --- a/drivers/platform/x86/intel/pmc/mtl.c +++ b/drivers/platform/x86/intel/pmc/mtl.c @@ -17,10 +17,6 @@ #define IOEM_LPM_REQ_GUID 0x4357464 #define IOEP_LPM_REQ_GUID 0x5077612 -/* Die C6 from PUNIT telemetry */ -#define MTL_PMT_DMU_DIE_C6_OFFSET 15 -#define MTL_PMT_DMU_GUID 0x1A067102 - static const u8 MTL_LPM_REG_INDEX[] = {0, 4, 5, 6, 8, 9, 10, 11, 12, 13, 14, 15, 16, 20}; /* @@ -973,63 +969,18 @@ static struct pmc_info mtl_pmc_info_list[] = { {} }; -static void mtl_punit_pmt_init(struct pmc_dev *pmcdev) -{ - struct telem_endpoint *ep; - struct pci_dev *pcidev; - - pcidev = pci_get_domain_bus_and_slot(0, 0, PCI_DEVFN(10, 0)); - if (!pcidev) { - dev_err(&pmcdev->pdev->dev, "PUNIT PMT device not found.\n"); - return; - } - - ep = pmt_telem_find_and_register_endpoint(pcidev, MTL_PMT_DMU_GUID, 0); - pci_dev_put(pcidev); - if (IS_ERR(ep)) { - dev_err(&pmcdev->pdev->dev, - "pmc_core: couldn't get DMU telem endpoint, %ld\n", - PTR_ERR(ep)); - return; - } - - pmcdev->punit_ep = ep; - - pmcdev->has_die_c6 = true; - pmcdev->die_c6_offset = MTL_PMT_DMU_DIE_C6_OFFSET; -} - #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); - } -} - /* * Set power state of select devices that do not have drivers to D3 * so that they do not block Package C entry. */ static void mtl_d3_fixup(void) { - mtl_set_device_d3(MTL_GNA_PCI_DEV); - mtl_set_device_d3(MTL_IPU_PCI_DEV); - mtl_set_device_d3(MTL_VPU_PCI_DEV); + pmc_core_set_device_d3(MTL_GNA_PCI_DEV); + pmc_core_set_device_d3(MTL_IPU_PCI_DEV); + pmc_core_set_device_d3(MTL_VPU_PCI_DEV); } static int mtl_resume(struct pmc_dev *pmcdev) @@ -1042,6 +993,7 @@ int mtl_core_init(struct pmc_dev *pmcdev) { struct pmc *pmc = pmcdev->pmcs[PMC_IDX_SOC]; int ret; + int func = 2; mtl_d3_fixup(); @@ -1052,7 +1004,7 @@ int mtl_core_init(struct pmc_dev *pmcdev) * If ssram init fails use legacy method to at least get the * primary PMC */ - ret = pmc_core_ssram_init(pmcdev); + ret = pmc_core_ssram_init(pmcdev, func); if (ret) { dev_warn(&pmcdev->pdev->dev, "ssram init failed, %d, using legacy init\n", ret); @@ -1063,7 +1015,7 @@ int mtl_core_init(struct pmc_dev *pmcdev) } pmc_core_get_low_power_modes(pmcdev); - mtl_punit_pmt_init(pmcdev); + pmc_core_punit_pmt_init(pmcdev, MTL_PMT_DMU_GUID); /* Due to a hardware limitation, the GBE LTR blocks PC10 * when a cable is attached. Tell the PMC to ignore it. -- cgit From 1d62ada48d41d72d72232585eed0f3e1136ae1fb Mon Sep 17 00:00:00 2001 From: Rajvi Jingar Date: Mon, 18 Dec 2023 20:22:14 -0800 Subject: platform/x86/intel/pmc: Add ssram_init flag in PMC discovery in Meteor Lake If PMC discovery using pmc_core_ssram_init() was unsuccessful for the Meteor Lake platform, the legacy enumeration method is used. In this case pci device struct for the PMC SSRAM is not available and pmc_core_ssram_get_lpm_reqs() will not work. Add ssram_init flag to indicate if the PMC SSRAM initialization was successful or not. Call pmc_core_ssram_get_lpm_reqs() only if the ssram_init flag is set to true. Signed-off-by: Rajvi Jingar Link: https://lore.kernel.org/r/20231219042216.2592029-6-rajvi.jingar@linux.intel.com Reviewed-by: Hans de Goede Signed-off-by: Hans de Goede --- drivers/platform/x86/intel/pmc/mtl.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/drivers/platform/x86/intel/pmc/mtl.c b/drivers/platform/x86/intel/pmc/mtl.c index d2470f800298..e75431325dda 100644 --- a/drivers/platform/x86/intel/pmc/mtl.c +++ b/drivers/platform/x86/intel/pmc/mtl.c @@ -994,6 +994,7 @@ int mtl_core_init(struct pmc_dev *pmcdev) struct pmc *pmc = pmcdev->pmcs[PMC_IDX_SOC]; int ret; int func = 2; + bool ssram_init = true; mtl_d3_fixup(); @@ -1006,6 +1007,7 @@ int mtl_core_init(struct pmc_dev *pmcdev) */ ret = pmc_core_ssram_init(pmcdev, func); if (ret) { + ssram_init = false; dev_warn(&pmcdev->pdev->dev, "ssram init failed, %d, using legacy init\n", ret); pmc->map = &mtl_socm_reg_map; @@ -1023,5 +1025,8 @@ int mtl_core_init(struct pmc_dev *pmcdev) dev_dbg(&pmcdev->pdev->dev, "ignoring GBE LTR\n"); pmc_core_send_ltr_ignore(pmcdev, 3); - return pmc_core_ssram_get_lpm_reqs(pmcdev); + if (ssram_init) + return pmc_core_ssram_get_lpm_reqs(pmcdev); + + return 0; } -- cgit From 83f168a1a4375c653d6d2383fd6ce5b404d031da Mon Sep 17 00:00:00 2001 From: Rajvi Jingar Date: Mon, 18 Dec 2023 20:22:15 -0800 Subject: platform/x86/intel/pmc: Add Arrow Lake S support to intel_pmc_core driver Add Arrow Lake S support in intel_pmc_core driver Signed-off-by: Rajvi Jingar Link: https://lore.kernel.org/r/20231219042216.2592029-7-rajvi.jingar@linux.intel.com Reviewed-by: Hans de Goede Signed-off-by: Hans de Goede --- drivers/platform/x86/intel/pmc/Makefile | 2 +- drivers/platform/x86/intel/pmc/arl.c | 726 ++++++++++++++++++++++++++++++++ drivers/platform/x86/intel/pmc/core.c | 1 + drivers/platform/x86/intel/pmc/core.h | 38 ++ 4 files changed, 766 insertions(+), 1 deletion(-) create mode 100644 drivers/platform/x86/intel/pmc/arl.c diff --git a/drivers/platform/x86/intel/pmc/Makefile b/drivers/platform/x86/intel/pmc/Makefile index 3a4cf1cbc1ca..74655e176178 100644 --- a/drivers/platform/x86/intel/pmc/Makefile +++ b/drivers/platform/x86/intel/pmc/Makefile @@ -4,7 +4,7 @@ # intel_pmc_core-y := core.o core_ssram.o spt.o cnp.o \ - icl.o tgl.o adl.o mtl.o + icl.o tgl.o adl.o mtl.o arl.o obj-$(CONFIG_INTEL_PMC_CORE) += intel_pmc_core.o intel_pmc_core_pltdrv-y := pltdrv.o obj-$(CONFIG_INTEL_PMC_CORE) += intel_pmc_core_pltdrv.o diff --git a/drivers/platform/x86/intel/pmc/arl.c b/drivers/platform/x86/intel/pmc/arl.c new file mode 100644 index 000000000000..e6190247f5df --- /dev/null +++ b/drivers/platform/x86/intel/pmc/arl.c @@ -0,0 +1,726 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * This file contains platform specific structure definitions + * and init function used by Meteor Lake PCH. + * + * Copyright (c) 2022, Intel Corporation. + * All Rights Reserved. + * + */ + +#include +#include "core.h" +#include "../pmt/telemetry.h" + +/* PMC SSRAM PMT Telemetry GUID */ +#define IOEP_LPM_REQ_GUID 0x5077612 +#define SOCS_LPM_REQ_GUID 0x8478657 +#define PCHS_LPM_REQ_GUID 0x9684572 + +static const u8 ARL_LPM_REG_INDEX[] = {0, 4, 5, 6, 8, 9, 10, 11, 12, 13, 14, 15, 16, 20}; + +const struct pmc_bit_map arl_socs_ltr_show_map[] = { + {"SOUTHPORT_A", CNP_PMC_LTR_SPA}, + {"SOUTHPORT_B", CNP_PMC_LTR_SPB}, + {"SATA", CNP_PMC_LTR_SATA}, + {"GIGABIT_ETHERNET", CNP_PMC_LTR_GBE}, + {"XHCI", CNP_PMC_LTR_XHCI}, + {"SOUTHPORT_F", ADL_PMC_LTR_SPF}, + {"ME", CNP_PMC_LTR_ME}, + /* EVA is Enterprise Value Add, doesn't really exist on PCH */ + {"SATA1", CNP_PMC_LTR_EVA}, + {"SOUTHPORT_C", CNP_PMC_LTR_SPC}, + {"HD_AUDIO", CNP_PMC_LTR_AZ}, + {"CNV", CNP_PMC_LTR_CNV}, + {"LPSS", CNP_PMC_LTR_LPSS}, + {"SOUTHPORT_D", CNP_PMC_LTR_SPD}, + {"SOUTHPORT_E", CNP_PMC_LTR_SPE}, + {"SATA2", CNP_PMC_LTR_CAM}, + {"ESPI", CNP_PMC_LTR_ESPI}, + {"SCC", CNP_PMC_LTR_SCC}, + {"ISH", CNP_PMC_LTR_ISH}, + {"UFSX2", CNP_PMC_LTR_UFSX2}, + {"EMMC", CNP_PMC_LTR_EMMC}, + /* + * Check intel_pmc_core_ids[] users of cnp_reg_map for + * a list of core SoCs using this. + */ + {"WIGIG", ICL_PMC_LTR_WIGIG}, + {"THC0", TGL_PMC_LTR_THC0}, + {"THC1", TGL_PMC_LTR_THC1}, + {"SOUTHPORT_G", MTL_PMC_LTR_SPG}, + {"Reserved", ARL_SOCS_PMC_LTR_RESERVED}, + {"IOE_PMC", MTL_PMC_LTR_IOE_PMC}, + {"DMI3", ARL_PMC_LTR_DMI3}, + + /* Below two cannot be used for LTR_IGNORE */ + {"CURRENT_PLATFORM", CNP_PMC_LTR_CUR_PLT}, + {"AGGREGATED_SYSTEM", CNP_PMC_LTR_CUR_ASLT}, + {} +}; + +const struct pmc_bit_map arl_socs_clocksource_status_map[] = { + {"AON2_OFF_STS", BIT(0)}, + {"AON3_OFF_STS", BIT(1)}, + {"AON4_OFF_STS", BIT(2)}, + {"AON5_OFF_STS", BIT(3)}, + {"AON1_OFF_STS", BIT(4)}, + {"XTAL_LVM_OFF_STS", BIT(5)}, + {"AON3_SPL_OFF_STS", BIT(9)}, + {"DMI3FPW_0_PLL_OFF_STS", BIT(10)}, + {"DMI3FPW_1_PLL_OFF_STS", BIT(11)}, + {"G5X16FPW_0_PLL_OFF_STS", BIT(14)}, + {"G5X16FPW_1_PLL_OFF_STS", BIT(15)}, + {"G5X16FPW_2_PLL_OFF_STS", BIT(16)}, + {"XTAL_AGGR_OFF_STS", BIT(17)}, + {"USB2_PLL_OFF_STS", BIT(18)}, + {"G5X16FPW_3_PLL_OFF_STS", BIT(19)}, + {"BCLK_EXT_INJ_CLK_OFF_STS", BIT(20)}, + {"PHY_OC_EXT_INJ_CLK_OFF_STS", BIT(21)}, + {"FILTER_PLL_OFF_STS", BIT(22)}, + {"FABRIC_PLL_OFF_STS", BIT(25)}, + {"SOC_PLL_OFF_STS", BIT(26)}, + {"PCIEFAB_PLL_OFF_STS", BIT(27)}, + {"REF_PLL_OFF_STS", BIT(28)}, + {"GENLOCK_FILTER_PLL_OFF_STS", BIT(30)}, + {"RTC_PLL_OFF_STS", BIT(31)}, + {} +}; + +const struct pmc_bit_map arl_socs_power_gating_status_0_map[] = { + {"PMC_PGD0_PG_STS", BIT(0)}, + {"DMI_PGD0_PG_STS", BIT(1)}, + {"ESPISPI_PGD0_PG_STS", BIT(2)}, + {"XHCI_PGD0_PG_STS", BIT(3)}, + {"SPA_PGD0_PG_STS", BIT(4)}, + {"SPB_PGD0_PG_STS", BIT(5)}, + {"SPC_PGD0_PG_STS", BIT(6)}, + {"GBE_PGD0_PG_STS", BIT(7)}, + {"SATA_PGD0_PG_STS", BIT(8)}, + {"FIACPCB_P5x16_PGD0_PG_STS", BIT(9)}, + {"G5x16FPW_PGD0_PG_STS", BIT(10)}, + {"FIA_D_PGD0_PG_STS", BIT(11)}, + {"MPFPW2_PGD0_PG_STS", BIT(12)}, + {"SPD_PGD0_PG_STS", BIT(13)}, + {"LPSS_PGD0_PG_STS", BIT(14)}, + {"LPC_PGD0_PG_STS", BIT(15)}, + {"SMB_PGD0_PG_STS", BIT(16)}, + {"ISH_PGD0_PG_STS", BIT(17)}, + {"P2S_PGD0_PG_STS", BIT(18)}, + {"NPK_PGD0_PG_STS", BIT(19)}, + {"DMI3FPW_PGD0_PG_STS", BIT(20)}, + {"GBETSN1_PGD0_PG_STS", BIT(21)}, + {"FUSE_PGD0_PG_STS", BIT(22)}, + {"FIACPCB_D_PGD0_PG_STS", BIT(23)}, + {"FUSEGPSB_PGD0_PG_STS", BIT(24)}, + {"XDCI_PGD0_PG_STS", BIT(25)}, + {"EXI_PGD0_PG_STS", BIT(26)}, + {"CSE_PGD0_PG_STS", BIT(27)}, + {"KVMCC_PGD0_PG_STS", BIT(28)}, + {"PMT_PGD0_PG_STS", BIT(29)}, + {"CLINK_PGD0_PG_STS", BIT(30)}, + {"PTIO_PGD0_PG_STS", BIT(31)}, + {} +}; + +const struct pmc_bit_map arl_socs_power_gating_status_1_map[] = { + {"USBR0_PGD0_PG_STS", BIT(0)}, + {"SUSRAM_PGD0_PG_STS", BIT(1)}, + {"SMT1_PGD0_PG_STS", BIT(2)}, + {"FIACPCB_U_PGD0_PG_STS", BIT(3)}, + {"SMS2_PGD0_PG_STS", BIT(4)}, + {"SMS1_PGD0_PG_STS", BIT(5)}, + {"CSMERTC_PGD0_PG_STS", BIT(6)}, + {"CSMEPSF_PGD0_PG_STS", BIT(7)}, + {"SBR0_PGD0_PG_STS", BIT(8)}, + {"SBR1_PGD0_PG_STS", BIT(9)}, + {"SBR2_PGD0_PG_STS", BIT(10)}, + {"SBR3_PGD0_PG_STS", BIT(11)}, + {"MPFPW1_PGD0_PG_STS", BIT(12)}, + {"SBR5_PGD0_PG_STS", BIT(13)}, + {"FIA_X_PGD0_PG_STS", BIT(14)}, + {"FIACPCB_X_PGD0_PG_STS", BIT(15)}, + {"SBRG_PGD0_PG_STS", BIT(16)}, + {"SOC_D2D_PGD1_PG_STS", BIT(17)}, + {"PSF4_PGD0_PG_STS", BIT(18)}, + {"CNVI_PGD0_PG_STS", BIT(19)}, + {"UFSX2_PGD0_PG_STS", BIT(20)}, + {"ENDBG_PGD0_PG_STS", BIT(21)}, + {"DBG_PSF_PGD0_PG_STS", BIT(22)}, + {"SBR6_PGD0_PG_STS", BIT(23)}, + {"SOC_D2D_PGD2_PG_STS", BIT(24)}, + {"NPK_PGD1_PG_STS", BIT(25)}, + {"DMI3_PGD0_PG_STS", BIT(26)}, + {"DBG_SBR_PGD0_PG_STS", BIT(27)}, + {"SOC_D2D_PGD0_PG_STS", BIT(28)}, + {"PSF6_PGD0_PG_STS", BIT(29)}, + {"PSF7_PGD0_PG_STS", BIT(30)}, + {"MPFPW3_PGD0_PG_STS", BIT(31)}, + {} +}; + +const struct pmc_bit_map arl_socs_power_gating_status_2_map[] = { + {"PSF8_PGD0_PG_STS", BIT(0)}, + {"FIA_PGD0_PG_STS", BIT(1)}, + {"SOC_D2D_PGD3_PG_STS", BIT(2)}, + {"FIA_U_PGD0_PG_STS", BIT(3)}, + {"TAM_PGD0_PG_STS", BIT(4)}, + {"GBETSN_PGD0_PG_STS", BIT(5)}, + {"TBTLSX_PGD0_PG_STS", BIT(6)}, + {"THC0_PGD0_PG_STS", BIT(7)}, + {"THC1_PGD0_PG_STS", BIT(8)}, + {"PMC_PGD1_PG_STS", BIT(9)}, + {"FIA_P5x16_PGD0_PG_STS", BIT(10)}, + {"GNA_PGD0_PG_STS", BIT(11)}, + {"ACE_PGD0_PG_STS", BIT(12)}, + {"ACE_PGD1_PG_STS", BIT(13)}, + {"ACE_PGD2_PG_STS", BIT(14)}, + {"ACE_PGD3_PG_STS", BIT(15)}, + {"ACE_PGD4_PG_STS", BIT(16)}, + {"ACE_PGD5_PG_STS", BIT(17)}, + {"ACE_PGD6_PG_STS", BIT(18)}, + {"ACE_PGD7_PG_STS", BIT(19)}, + {"ACE_PGD8_PG_STS", BIT(20)}, + {"FIA_PGS_PGD0_PG_STS", BIT(21)}, + {"FIACPCB_PGS_PGD0_PG_STS", BIT(22)}, + {"FUSEPMSB_PGD0_PG_STS", BIT(23)}, + {} +}; + +const struct pmc_bit_map arl_socs_d3_status_2_map[] = { + {"CSMERTC_D3_STS", BIT(1)}, + {"SUSRAM_D3_STS", BIT(2)}, + {"CSE_D3_STS", BIT(4)}, + {"KVMCC_D3_STS", BIT(5)}, + {"USBR0_D3_STS", BIT(6)}, + {"ISH_D3_STS", BIT(7)}, + {"SMT1_D3_STS", BIT(8)}, + {"SMT2_D3_STS", BIT(9)}, + {"SMT3_D3_STS", BIT(10)}, + {"GNA_D3_STS", BIT(12)}, + {"CLINK_D3_STS", BIT(14)}, + {"PTIO_D3_STS", BIT(16)}, + {"PMT_D3_STS", BIT(17)}, + {"SMS1_D3_STS", BIT(18)}, + {"SMS2_D3_STS", BIT(19)}, + {} +}; + +const struct pmc_bit_map arl_socs_d3_status_3_map[] = { + {"GBETSN_D3_STS", BIT(13)}, + {"THC0_D3_STS", BIT(14)}, + {"THC1_D3_STS", BIT(15)}, + {"ACE_D3_STS", BIT(23)}, + {} +}; + +const struct pmc_bit_map arl_socs_vnn_req_status_3_map[] = { + {"DTS0_VNN_REQ_STS", BIT(7)}, + {"GPIOCOM5_VNN_REQ_STS", BIT(11)}, + {} +}; + +const struct pmc_bit_map *arl_socs_lpm_maps[] = { + arl_socs_clocksource_status_map, + arl_socs_power_gating_status_0_map, + arl_socs_power_gating_status_1_map, + arl_socs_power_gating_status_2_map, + mtl_socm_d3_status_0_map, + mtl_socm_d3_status_1_map, + arl_socs_d3_status_2_map, + arl_socs_d3_status_3_map, + mtl_socm_vnn_req_status_0_map, + mtl_socm_vnn_req_status_1_map, + mtl_socm_vnn_req_status_2_map, + arl_socs_vnn_req_status_3_map, + mtl_socm_vnn_misc_status_map, + mtl_socm_signal_status_map, + NULL +}; + +const struct pmc_bit_map arl_socs_pfear_map[] = { + {"RSVD64", BIT(0)}, + {"RSVD65", BIT(1)}, + {"RSVD66", BIT(2)}, + {"RSVD67", BIT(3)}, + {"RSVD68", BIT(4)}, + {"GBETSN", BIT(5)}, + {"TBTLSX", BIT(6)}, + {} +}; + +const struct pmc_bit_map *ext_arl_socs_pfear_map[] = { + mtl_socm_pfear_map, + arl_socs_pfear_map, + NULL +}; + +const struct pmc_reg_map arl_socs_reg_map = { + .pfear_sts = ext_arl_socs_pfear_map, + .ppfear_buckets = ARL_SOCS_PPFEAR_NUM_ENTRIES, + .pm_read_disable_bit = CNP_PMC_READ_DISABLE_BIT, + .lpm_sts = arl_socs_lpm_maps, + .ltr_ignore_max = ARL_SOCS_NUM_IP_IGN_ALLOWED, + .ltr_show_sts = arl_socs_ltr_show_map, + .slp_s0_offset = CNP_PMC_SLP_S0_RES_COUNTER_OFFSET, + .slp_s0_res_counter_step = TGL_PMC_SLP_S0_RES_COUNTER_STEP, + .lpm_res_counter_step_x2 = TGL_PMC_LPM_RES_COUNTER_STEP_X2, + .msr_sts = msr_map, + .ltr_ignore_offset = CNP_PMC_LTR_IGNORE_OFFSET, + .regmap_length = MTL_SOC_PMC_MMIO_REG_LEN, + .ppfear0_offset = CNP_PMC_HOST_PPFEAR0A, + .pm_cfg_offset = CNP_PMC_PM_CFG_OFFSET, + .lpm_priority_offset = MTL_LPM_PRI_OFFSET, + .lpm_en_offset = MTL_LPM_EN_OFFSET, + .lpm_residency_offset = MTL_LPM_RESIDENCY_OFFSET, + .lpm_status_offset = MTL_LPM_STATUS_OFFSET, + .lpm_sts_latch_en_offset = MTL_LPM_STATUS_LATCH_EN_OFFSET, + .lpm_live_status_offset = MTL_LPM_LIVE_STATUS_OFFSET, + .lpm_num_maps = ADL_LPM_NUM_MAPS, + .lpm_reg_index = ARL_LPM_REG_INDEX, + .etr3_offset = ETR3_OFFSET, + .pson_residency_offset = TGL_PSON_RESIDENCY_OFFSET, + .pson_residency_counter_step = TGL_PSON_RES_COUNTER_STEP, +}; + +const struct pmc_bit_map arl_pchs_ltr_show_map[] = { + {"SOUTHPORT_A", CNP_PMC_LTR_SPA}, + {"SOUTHPORT_B", CNP_PMC_LTR_SPB}, + {"SATA", CNP_PMC_LTR_SATA}, + {"GIGABIT_ETHERNET", CNP_PMC_LTR_GBE}, + {"XHCI", CNP_PMC_LTR_XHCI}, + {"SOUTHPORT_F", ADL_PMC_LTR_SPF}, + {"ME", CNP_PMC_LTR_ME}, + /* EVA is Enterprise Value Add, doesn't really exist on PCH */ + {"SATA1", CNP_PMC_LTR_EVA}, + {"SOUTHPORT_C", CNP_PMC_LTR_SPC}, + {"HD_AUDIO", CNP_PMC_LTR_AZ}, + {"CNV", CNP_PMC_LTR_CNV}, + {"LPSS", CNP_PMC_LTR_LPSS}, + {"SOUTHPORT_D", CNP_PMC_LTR_SPD}, + {"SOUTHPORT_E", CNP_PMC_LTR_SPE}, + {"SATA2", CNP_PMC_LTR_CAM}, + {"ESPI", CNP_PMC_LTR_ESPI}, + {"SCC", CNP_PMC_LTR_SCC}, + {"ISH", CNP_PMC_LTR_ISH}, + {"UFSX2", CNP_PMC_LTR_UFSX2}, + {"EMMC", CNP_PMC_LTR_EMMC}, + /* + * Check intel_pmc_core_ids[] users of cnp_reg_map for + * a list of core SoCs using this. + */ + {"WIGIG", ICL_PMC_LTR_WIGIG}, + {"THC0", TGL_PMC_LTR_THC0}, + {"THC1", TGL_PMC_LTR_THC1}, + {"SOUTHPORT_G", MTL_PMC_LTR_SPG}, + {"ESE", MTL_PMC_LTR_ESE}, + {"IOE_PMC", MTL_PMC_LTR_IOE_PMC}, + {"DMI3", ARL_PMC_LTR_DMI3}, + + /* Below two cannot be used for LTR_IGNORE */ + {"CURRENT_PLATFORM", CNP_PMC_LTR_CUR_PLT}, + {"AGGREGATED_SYSTEM", CNP_PMC_LTR_CUR_ASLT}, + {} +}; + +const struct pmc_bit_map arl_pchs_clocksource_status_map[] = { + {"AON2_OFF_STS", BIT(0)}, + {"AON3_OFF_STS", BIT(1)}, + {"AON4_OFF_STS", BIT(2)}, + {"AON2_SPL_OFF_STS", BIT(3)}, + {"AONL_OFF_STS", BIT(4)}, + {"XTAL_LVM_OFF_STS", BIT(5)}, + {"AON5_ACRO_OFF_STS", BIT(6)}, + {"AON6_ACRO_OFF_STS", BIT(7)}, + {"USB3_PLL_OFF_STS", BIT(8)}, + {"ACRO_OFF_STS", BIT(9)}, + {"AUDIO_PLL_OFF_STS", BIT(10)}, + {"MAIN_CRO_OFF_STS", BIT(11)}, + {"MAIN_DIVIDER_OFF_STS", BIT(12)}, + {"REF_PLL_NON_OC_OFF_STS", BIT(13)}, + {"DMI_PLL_OFF_STS", BIT(14)}, + {"PHY_EXT_INJ_OFF_STS", BIT(15)}, + {"AON6_MCRO_OFF_STS", BIT(16)}, + {"XTAL_AGGR_OFF_STS", BIT(17)}, + {"USB2_PLL_OFF_STS", BIT(18)}, + {"TSN0_PLL_OFF_STS", BIT(19)}, + {"TSN1_PLL_OFF_STS", BIT(20)}, + {"GBE_PLL_OFF_STS", BIT(21)}, + {"SATA_PLL_OFF_STS", BIT(22)}, + {"PCIE0_PLL_OFF_STS", BIT(23)}, + {"PCIE1_PLL_OFF_STS", BIT(24)}, + {"PCIE2_PLL_OFF_STS", BIT(26)}, + {"PCIE3_PLL_OFF_STS", BIT(27)}, + {"REF_PLL_OFF_STS", BIT(28)}, + {"PCIE4_PLL_OFF_STS", BIT(29)}, + {"PCIE5_PLL_OFF_STS", BIT(30)}, + {"REF38P4_PLL_OFF_STS", BIT(31)}, + {} +}; + +const struct pmc_bit_map arl_pchs_power_gating_status_0_map[] = { + {"PMC_PGD0_PG_STS", BIT(0)}, + {"DMI_PGD0_PG_STS", BIT(1)}, + {"ESPISPI_PGD0_PG_STS", BIT(2)}, + {"XHCI_PGD0_PG_STS", BIT(3)}, + {"SPA_PGD0_PG_STS", BIT(4)}, + {"SPB_PGD0_PG_STS", BIT(5)}, + {"SPC_PGD0_PG_STS", BIT(6)}, + {"GBE_PGD0_PG_STS", BIT(7)}, + {"SATA_PGD0_PG_STS", BIT(8)}, + {"FIA_X_PGD0_PG_STS", BIT(9)}, + {"MPFPW4_PGD0_PG_STS", BIT(10)}, + {"EAH_PGD0_PG_STS", BIT(11)}, + {"MPFPW1_PGD0_PG_STS", BIT(12)}, + {"SPD_PGD0_PG_STS", BIT(13)}, + {"LPSS_PGD0_PG_STS", BIT(14)}, + {"LPC_PGD0_PG_STS", BIT(15)}, + {"SMB_PGD0_PG_STS", BIT(16)}, + {"ISH_PGD0_PG_STS", BIT(17)}, + {"P2S_PGD0_PG_STS", BIT(18)}, + {"NPK_PGD0_PG_STS", BIT(19)}, + {"U3FPW1_PGD0_PG_STS", BIT(20)}, + {"PECI_PGD0_PG_STS", BIT(21)}, + {"FUSE_PGD0_PG_STS", BIT(22)}, + {"SBR8_PGD0_PG_STS", BIT(23)}, + {"EXE_PGD0_PG_STS", BIT(24)}, + {"XDCI_PGD0_PG_STS", BIT(25)}, + {"EXI_PGD0_PG_STS", BIT(26)}, + {"CSE_PGD0_PG_STS", BIT(27)}, + {"KVMCC_PGD0_PG_STS", BIT(28)}, + {"PMT_PGD0_PG_STS", BIT(29)}, + {"CLINK_PGD0_PG_STS", BIT(30)}, + {"PTIO_PGD0_PG_STS", BIT(31)}, + {} +}; + +const struct pmc_bit_map arl_pchs_power_gating_status_1_map[] = { + {"USBR0_PGD0_PG_STS", BIT(0)}, + {"SUSRAM_PGD0_PG_STS", BIT(1)}, + {"SMT1_PGD0_PG_STS", BIT(2)}, + {"SMT4_PGD0_PG_STS", BIT(3)}, + {"SMS2_PGD0_PG_STS", BIT(4)}, + {"SMS1_PGD0_PG_STS", BIT(5)}, + {"CSMERTC_PGD0_PG_STS", BIT(6)}, + {"CSMEPSF_PGD0_PG_STS", BIT(7)}, + {"SBR0_PGD0_PG_STS", BIT(8)}, + {"SBR1_PGD0_PG_STS", BIT(9)}, + {"SBR2_PGD0_PG_STS", BIT(10)}, + {"SBR3_PGD0_PG_STS", BIT(11)}, + {"SBR4_PGD0_PG_STS", BIT(12)}, + {"SBR5_PGD0_PG_STS", BIT(13)}, + {"MPFPW3_PGD0_PG_STS", BIT(14)}, + {"PSF1_PGD0_PG_STS", BIT(15)}, + {"PSF2_PGD0_PG_STS", BIT(16)}, + {"PSF3_PGD0_PG_STS", BIT(17)}, + {"PSF4_PGD0_PG_STS", BIT(18)}, + {"CNVI_PGD0_PG_STS", BIT(19)}, + {"DMI3_PGD0_PG_STS", BIT(20)}, + {"ENDBG_PGD0_PG_STS", BIT(21)}, + {"DBG_SBR_PGD0_PG_STS", BIT(22)}, + {"SBR6_PGD0_PG_STS", BIT(23)}, + {"SBR7_PGD0_PG_STS", BIT(24)}, + {"NPK_PGD1_PG_STS", BIT(25)}, + {"U3FPW3_PGD0_PG_STS", BIT(26)}, + {"MPFPW2_PGD0_PG_STS", BIT(27)}, + {"MPFPW7_PGD0_PG_STS", BIT(28)}, + {"GBETSN1_PGD0_PG_STS", BIT(29)}, + {"PSF7_PGD0_PG_STS", BIT(30)}, + {"FIA2_PGD0_PG_STS", BIT(31)}, + {} +}; + +const struct pmc_bit_map arl_pchs_power_gating_status_2_map[] = { + {"U3FPW2_PGD0_PG_STS", BIT(0)}, + {"FIA_PGD0_PG_STS", BIT(1)}, + {"FIACPCB_X_PGD0_PG_STS", BIT(2)}, + {"FIA1_PGD0_PG_STS", BIT(3)}, + {"TAM_PGD0_PG_STS", BIT(4)}, + {"GBETSN_PGD0_PG_STS", BIT(5)}, + {"SBR9_PGD0_PG_STS", BIT(6)}, + {"THC0_PGD0_PG_STS", BIT(7)}, + {"THC1_PGD0_PG_STS", BIT(8)}, + {"PMC_PGD1_PG_STS", BIT(9)}, + {"DBC_PGD0_PG_STS", BIT(10)}, + {"DBG_PSF_PGD0_PG_STS", BIT(11)}, + {"SPF_PGD0_PG_STS", BIT(12)}, + {"ACE_PGD0_PG_STS", BIT(13)}, + {"ACE_PGD1_PG_STS", BIT(14)}, + {"ACE_PGD2_PG_STS", BIT(15)}, + {"ACE_PGD3_PG_STS", BIT(16)}, + {"ACE_PGD4_PG_STS", BIT(17)}, + {"ACE_PGD5_PG_STS", BIT(18)}, + {"ACE_PGD6_PG_STS", BIT(19)}, + {"ACE_PGD7_PG_STS", BIT(20)}, + {"SPE_PGD0_PG_STS", BIT(21)}, + {"MPFPW5_PG_STS", BIT(22)}, + {} +}; + +const struct pmc_bit_map arl_pchs_d3_status_0_map[] = { + {"SPF_D3_STS", BIT(0)}, + {"LPSS_D3_STS", BIT(3)}, + {"XDCI_D3_STS", BIT(4)}, + {"XHCI_D3_STS", BIT(5)}, + {"SPA_D3_STS", BIT(12)}, + {"SPB_D3_STS", BIT(13)}, + {"SPC_D3_STS", BIT(14)}, + {"SPD_D3_STS", BIT(15)}, + {"SPE_D3_STS", BIT(16)}, + {"ESPISPI_D3_STS", BIT(18)}, + {"SATA_D3_STS", BIT(20)}, + {"PSTH_D3_STS", BIT(21)}, + {"DMI_D3_STS", BIT(22)}, + {} +}; + +const struct pmc_bit_map arl_pchs_d3_status_1_map[] = { + {"GBETSN1_D3_STS", BIT(14)}, + {"GBE_D3_STS", BIT(19)}, + {"ITSS_D3_STS", BIT(23)}, + {"P2S_D3_STS", BIT(24)}, + {"CNVI_D3_STS", BIT(27)}, + {} +}; + +const struct pmc_bit_map arl_pchs_d3_status_2_map[] = { + {"CSMERTC_D3_STS", BIT(1)}, + {"SUSRAM_D3_STS", BIT(2)}, + {"CSE_D3_STS", BIT(4)}, + {"KVMCC_D3_STS", BIT(5)}, + {"USBR0_D3_STS", BIT(6)}, + {"ISH_D3_STS", BIT(7)}, + {"SMT1_D3_STS", BIT(8)}, + {"SMT2_D3_STS", BIT(9)}, + {"SMT3_D3_STS", BIT(10)}, + {"SMT4_D3_STS", BIT(11)}, + {"SMT5_D3_STS", BIT(12)}, + {"SMT6_D3_STS", BIT(13)}, + {"CLINK_D3_STS", BIT(14)}, + {"PTIO_D3_STS", BIT(16)}, + {"PMT_D3_STS", BIT(17)}, + {"SMS1_D3_STS", BIT(18)}, + {"SMS2_D3_STS", BIT(19)}, + {} +}; + +const struct pmc_bit_map arl_pchs_d3_status_3_map[] = { + {"ESE_D3_STS", BIT(3)}, + {"GBETSN_D3_STS", BIT(13)}, + {"THC0_D3_STS", BIT(14)}, + {"THC1_D3_STS", BIT(15)}, + {"ACE_D3_STS", BIT(23)}, + {} +}; + +const struct pmc_bit_map arl_pchs_vnn_req_status_0_map[] = { + {"FIA_VNN_REQ_STS", BIT(17)}, + {"ESPISPI_VNN_REQ_STS", BIT(18)}, + {} +}; + +const struct pmc_bit_map arl_pchs_vnn_req_status_1_map[] = { + {"NPK_VNN_REQ_STS", BIT(4)}, + {"DFXAGG_VNN_REQ_STS", BIT(8)}, + {"EXI_VNN_REQ_STS", BIT(9)}, + {"GBE_VNN_REQ_STS", BIT(19)}, + {"SMB_VNN_REQ_STS", BIT(25)}, + {"LPC_VNN_REQ_STS", BIT(26)}, + {"CNVI_VNN_REQ_STS", BIT(27)}, + {} +}; + +const struct pmc_bit_map arl_pchs_vnn_req_status_2_map[] = { + {"FIA2_VNN_REQ_STS", BIT(0)}, + {"CSMERTC_VNN_REQ_STS", BIT(1)}, + {"CSE_VNN_REQ_STS", BIT(4)}, + {"ISH_VNN_REQ_STS", BIT(7)}, + {"SMT1_VNN_REQ_STS", BIT(8)}, + {"SMT4_VNN_REQ_STS", BIT(11)}, + {"CLINK_VNN_REQ_STS", BIT(14)}, + {"SMS1_VNN_REQ_STS", BIT(18)}, + {"SMS2_VNN_REQ_STS", BIT(19)}, + {"GPIOCOM4_VNN_REQ_STS", BIT(20)}, + {"GPIOCOM3_VNN_REQ_STS", BIT(21)}, + {"GPIOCOM2_VNN_REQ_STS", BIT(22)}, + {"GPIOCOM1_VNN_REQ_STS", BIT(23)}, + {"GPIOCOM0_VNN_REQ_STS", BIT(24)}, + {} +}; + +const struct pmc_bit_map arl_pchs_vnn_req_status_3_map[] = { + {"ESE_VNN_REQ_STS", BIT(3)}, + {"DTS0_VNN_REQ_STS", BIT(7)}, + {"GPIOCOM5_VNN_REQ_STS", BIT(11)}, + {"FIA1_VNN_REQ_STS", BIT(12)}, + {} +}; + +const struct pmc_bit_map arl_pchs_vnn_misc_status_map[] = { + {"CPU_C10_REQ_STS", BIT(0)}, + {"TS_OFF_REQ_STS", BIT(1)}, + {"PNDE_MET_REQ_STS", BIT(2)}, + {"PCIE_DEEP_PM_REQ_STS", BIT(3)}, + {"FW_THROTTLE_ALLOWED_REQ_STS", BIT(4)}, + {"ISH_VNNAON_REQ_STS", BIT(7)}, + {"IOE_COND_MET_S02I2_0_REQ_STS", BIT(8)}, + {"IOE_COND_MET_S02I2_1_REQ_STS", BIT(9)}, + {"IOE_COND_MET_S02I2_2_REQ_STS", BIT(10)}, + {"PLT_GREATER_REQ_STS", BIT(11)}, + {"PMC_IDLE_FB_OCP_REQ_STS", BIT(13)}, + {"PM_SYNC_STATES_REQ_STS", BIT(14)}, + {"EA_REQ_STS", BIT(15)}, + {"DMI_CLKREQ_B_REQ_STS", BIT(16)}, + {"BRK_EV_EN_REQ_STS", BIT(17)}, + {"AUTO_DEMO_EN_REQ_STS", BIT(18)}, + {"ITSS_CLK_SRC_REQ_STS", BIT(19)}, + {"ARC_IDLE_REQ_STS", BIT(21)}, + {"DMI_IN_REQ_STS", BIT(22)}, + {"FIA_DEEP_PM_REQ_STS", BIT(23)}, + {"XDCI_ATTACHED_REQ_STS", BIT(24)}, + {"ARC_INTERRUPT_WAKE_REQ_STS", BIT(25)}, + {"PRE_WAKE0_REQ_STS", BIT(27)}, + {"PRE_WAKE1_REQ_STS", BIT(28)}, + {"PRE_WAKE2_EN_REQ_STS", BIT(29)}, + {"CNVI_V1P05_REQ_STS", BIT(31)}, + {} +}; + +const struct pmc_bit_map arl_pchs_signal_status_map[] = { + {"LSX_Wake0_STS", BIT(0)}, + {"LSX_Wake1_STS", BIT(1)}, + {"LSX_Wake2_STS", BIT(2)}, + {"LSX_Wake3_STS", BIT(3)}, + {"LSX_Wake4_STS", BIT(4)}, + {"LSX_Wake5_STS", BIT(5)}, + {"LSX_Wake6_STS", BIT(6)}, + {"LSX_Wake7_STS", BIT(7)}, + {"Int_Timer_SS_Wake0_STS", BIT(8)}, + {"Int_Timer_SS_Wake1_STS", BIT(9)}, + {"Int_Timer_SS_Wake0_STS", BIT(10)}, + {"Int_Timer_SS_Wake1_STS", BIT(11)}, + {"Int_Timer_SS_Wake2_STS", BIT(12)}, + {"Int_Timer_SS_Wake3_STS", BIT(13)}, + {"Int_Timer_SS_Wake4_STS", BIT(14)}, + {"Int_Timer_SS_Wake5_STS", BIT(15)}, + {} +}; + +const struct pmc_bit_map *arl_pchs_lpm_maps[] = { + arl_pchs_clocksource_status_map, + arl_pchs_power_gating_status_0_map, + arl_pchs_power_gating_status_1_map, + arl_pchs_power_gating_status_2_map, + arl_pchs_d3_status_0_map, + arl_pchs_d3_status_1_map, + arl_pchs_d3_status_2_map, + arl_pchs_d3_status_3_map, + arl_pchs_vnn_req_status_0_map, + arl_pchs_vnn_req_status_1_map, + arl_pchs_vnn_req_status_2_map, + arl_pchs_vnn_req_status_3_map, + arl_pchs_vnn_misc_status_map, + arl_pchs_signal_status_map, + NULL +}; + +const struct pmc_reg_map arl_pchs_reg_map = { + .pfear_sts = ext_arl_socs_pfear_map, + .ppfear_buckets = ARL_SOCS_PPFEAR_NUM_ENTRIES, + .pm_read_disable_bit = CNP_PMC_READ_DISABLE_BIT, + .ltr_ignore_max = ARL_SOCS_NUM_IP_IGN_ALLOWED, + .lpm_sts = arl_pchs_lpm_maps, + .ltr_show_sts = arl_pchs_ltr_show_map, + .slp_s0_offset = CNP_PMC_SLP_S0_RES_COUNTER_OFFSET, + .slp_s0_res_counter_step = TGL_PMC_SLP_S0_RES_COUNTER_STEP, + .lpm_res_counter_step_x2 = TGL_PMC_LPM_RES_COUNTER_STEP_X2, + .msr_sts = msr_map, + .ltr_ignore_offset = CNP_PMC_LTR_IGNORE_OFFSET, + .regmap_length = ARL_PCH_PMC_MMIO_REG_LEN, + .ppfear0_offset = CNP_PMC_HOST_PPFEAR0A, + .pm_cfg_offset = CNP_PMC_PM_CFG_OFFSET, + .lpm_priority_offset = MTL_LPM_PRI_OFFSET, + .lpm_en_offset = MTL_LPM_EN_OFFSET, + .lpm_residency_offset = MTL_LPM_RESIDENCY_OFFSET, + .lpm_status_offset = MTL_LPM_STATUS_OFFSET, + .lpm_sts_latch_en_offset = MTL_LPM_STATUS_LATCH_EN_OFFSET, + .lpm_live_status_offset = MTL_LPM_LIVE_STATUS_OFFSET, + .lpm_num_maps = ADL_LPM_NUM_MAPS, + .lpm_reg_index = ARL_LPM_REG_INDEX, + .etr3_offset = ETR3_OFFSET, +}; + +#define PMC_DEVID_SOCS 0xae7f +#define PMC_DEVID_IOEP 0x7ecf +#define PMC_DEVID_PCHS 0x7f27 +static struct pmc_info arl_pmc_info_list[] = { + { + .guid = IOEP_LPM_REQ_GUID, + .devid = PMC_DEVID_IOEP, + .map = &mtl_ioep_reg_map, + }, + { + .guid = SOCS_LPM_REQ_GUID, + .devid = PMC_DEVID_SOCS, + .map = &arl_socs_reg_map, + }, + { + .guid = PCHS_LPM_REQ_GUID, + .devid = PMC_DEVID_PCHS, + .map = &arl_pchs_reg_map, + }, + {} +}; + +#define ARL_NPU_PCI_DEV 0xad1d +/* + * Set power state of select devices that do not have drivers to D3 + * so that they do not block Package C entry. + */ +static void arl_d3_fixup(void) +{ + pmc_core_set_device_d3(ARL_NPU_PCI_DEV); +} + +static int arl_resume(struct pmc_dev *pmcdev) +{ + arl_d3_fixup(); + return pmc_core_resume_common(pmcdev); +} + +int arl_core_init(struct pmc_dev *pmcdev) +{ + struct pmc *pmc = pmcdev->pmcs[PMC_IDX_SOC]; + int ret; + int func = 0; + bool ssram_init = true; + + arl_d3_fixup(); + pmcdev->resume = arl_resume; + pmcdev->regmap_list = arl_pmc_info_list; + + /* + * If ssram init fails use legacy method to at least get the + * primary PMC + */ + ret = pmc_core_ssram_init(pmcdev, func); + if (ret) { + ssram_init = false; + pmc->map = &arl_socs_reg_map; + + ret = get_primary_reg_base(pmc); + if (ret) + return ret; + } + + pmc_core_get_low_power_modes(pmcdev); + pmc_core_punit_pmt_init(pmcdev, ARL_PMT_DMU_GUID); + + if (ssram_init) { + ret = pmc_core_ssram_get_lpm_reqs(pmcdev); + if (ret) + return ret; + } + + return 0; +} diff --git a/drivers/platform/x86/intel/pmc/core.c b/drivers/platform/x86/intel/pmc/core.c index ac446b0f2192..67eecef7a54f 100644 --- a/drivers/platform/x86/intel/pmc/core.c +++ b/drivers/platform/x86/intel/pmc/core.c @@ -1273,6 +1273,7 @@ static const struct x86_cpu_id intel_pmc_core_ids[] = { X86_MATCH_INTEL_FAM6_MODEL(RAPTORLAKE, adl_core_init), X86_MATCH_INTEL_FAM6_MODEL(RAPTORLAKE_S, adl_core_init), X86_MATCH_INTEL_FAM6_MODEL(METEORLAKE_L, mtl_core_init), + X86_MATCH_INTEL_FAM6_MODEL(ARROWLAKE, arl_core_init), {} }; diff --git a/drivers/platform/x86/intel/pmc/core.h b/drivers/platform/x86/intel/pmc/core.h index e678a1205514..f9c2fee22e44 100644 --- a/drivers/platform/x86/intel/pmc/core.h +++ b/drivers/platform/x86/intel/pmc/core.h @@ -263,14 +263,20 @@ enum ppfear_regs { #define MTL_SOCM_NUM_IP_IGN_ALLOWED 25 #define MTL_SOC_PMC_MMIO_REG_LEN 0x2708 #define MTL_PMC_LTR_SPG 0x1B74 +#define ARL_SOCS_PMC_LTR_RESERVED 0x1B88 +#define ARL_SOCS_NUM_IP_IGN_ALLOWED 26 +#define ARL_PMC_LTR_DMI3 0x1BE4 +#define ARL_PCH_PMC_MMIO_REG_LEN 0x2720 /* Meteor Lake PGD PFET Enable Ack Status */ #define MTL_SOCM_PPFEAR_NUM_ENTRIES 8 #define MTL_IOE_PPFEAR_NUM_ENTRIES 10 +#define ARL_SOCS_PPFEAR_NUM_ENTRIES 9 /* Die C6 from PUNIT telemetry */ #define MTL_PMT_DMU_DIE_C6_OFFSET 15 #define MTL_PMT_DMU_GUID 0x1A067102 +#define ARL_PMT_DMU_GUID 0x1A06A000 extern const char *pmc_lpm_modes[]; @@ -501,6 +507,37 @@ extern const struct pmc_bit_map mtl_ioem_vnn_req_status_1_map[]; extern const struct pmc_bit_map *mtl_ioem_lpm_maps[]; extern const struct pmc_reg_map mtl_ioem_reg_map; +/* ARL */ +extern const struct pmc_bit_map arl_socs_ltr_show_map[]; +extern const struct pmc_bit_map arl_socs_clocksource_status_map[]; +extern const struct pmc_bit_map arl_socs_power_gating_status_0_map[]; +extern const struct pmc_bit_map arl_socs_power_gating_status_1_map[]; +extern const struct pmc_bit_map arl_socs_power_gating_status_2_map[]; +extern const struct pmc_bit_map arl_socs_d3_status_2_map[]; +extern const struct pmc_bit_map arl_socs_d3_status_3_map[]; +extern const struct pmc_bit_map arl_socs_vnn_req_status_3_map[]; +extern const struct pmc_bit_map *arl_socs_lpm_maps[]; +extern const struct pmc_bit_map arl_socs_pfear_map[]; +extern const struct pmc_bit_map *ext_arl_socs_pfear_map[]; +extern const struct pmc_reg_map arl_socs_reg_map; +extern const struct pmc_bit_map arl_pchs_ltr_show_map[]; +extern const struct pmc_bit_map arl_pchs_clocksource_status_map[]; +extern const struct pmc_bit_map arl_pchs_power_gating_status_0_map[]; +extern const struct pmc_bit_map arl_pchs_power_gating_status_1_map[]; +extern const struct pmc_bit_map arl_pchs_power_gating_status_2_map[]; +extern const struct pmc_bit_map arl_pchs_d3_status_0_map[]; +extern const struct pmc_bit_map arl_pchs_d3_status_1_map[]; +extern const struct pmc_bit_map arl_pchs_d3_status_2_map[]; +extern const struct pmc_bit_map arl_pchs_d3_status_3_map[]; +extern const struct pmc_bit_map arl_pchs_vnn_req_status_0_map[]; +extern const struct pmc_bit_map arl_pchs_vnn_req_status_1_map[]; +extern const struct pmc_bit_map arl_pchs_vnn_req_status_2_map[]; +extern const struct pmc_bit_map arl_pchs_vnn_req_status_3_map[]; +extern const struct pmc_bit_map arl_pchs_vnn_misc_status_map[]; +extern const struct pmc_bit_map arl_pchs_signal_status_map[]; +extern const struct pmc_bit_map *arl_pchs_lpm_maps[]; +extern const struct pmc_reg_map arl_pchs_reg_map; + extern void pmc_core_get_tgl_lpm_reqs(struct platform_device *pdev); extern int pmc_core_ssram_get_lpm_reqs(struct pmc_dev *pmcdev); extern int pmc_core_send_ltr_ignore(struct pmc_dev *pmcdev, u32 value); @@ -521,6 +558,7 @@ int tgl_l_core_init(struct pmc_dev *pmcdev); int tgl_core_generic_init(struct pmc_dev *pmcdev, int pch_tp); int adl_core_init(struct pmc_dev *pmcdev); int mtl_core_init(struct pmc_dev *pmcdev); +int arl_core_init(struct pmc_dev *pmcdev); #define pmc_for_each_mode(i, mode, pmcdev) \ for (i = 0, mode = pmcdev->lpm_en_modes[i]; \ -- cgit From 3748dfdae2a6bedc64ec7d2b17c9a58dc01c2700 Mon Sep 17 00:00:00 2001 From: Rajvi Jingar Date: Mon, 18 Dec 2023 20:22:16 -0800 Subject: platform/x86/intel/pmc: Add Lunar Lake M support to intel_pmc_core driver Add Lunar Lake M support in intel_pmc_core driver Signed-off-by: Rajvi Jingar Link: https://lore.kernel.org/r/20231219042216.2592029-8-rajvi.jingar@linux.intel.com Reviewed-by: Hans de Goede Signed-off-by: Hans de Goede --- drivers/platform/x86/intel/pmc/Makefile | 2 +- drivers/platform/x86/intel/pmc/core.c | 1 + drivers/platform/x86/intel/pmc/core.h | 26 ++ drivers/platform/x86/intel/pmc/lnl.c | 546 ++++++++++++++++++++++++++++++++ 4 files changed, 574 insertions(+), 1 deletion(-) create mode 100644 drivers/platform/x86/intel/pmc/lnl.c diff --git a/drivers/platform/x86/intel/pmc/Makefile b/drivers/platform/x86/intel/pmc/Makefile index 74655e176178..389e5419dadf 100644 --- a/drivers/platform/x86/intel/pmc/Makefile +++ b/drivers/platform/x86/intel/pmc/Makefile @@ -4,7 +4,7 @@ # intel_pmc_core-y := core.o core_ssram.o spt.o cnp.o \ - icl.o tgl.o adl.o mtl.o arl.o + icl.o tgl.o adl.o mtl.o arl.o lnl.o obj-$(CONFIG_INTEL_PMC_CORE) += intel_pmc_core.o intel_pmc_core_pltdrv-y := pltdrv.o obj-$(CONFIG_INTEL_PMC_CORE) += intel_pmc_core_pltdrv.o diff --git a/drivers/platform/x86/intel/pmc/core.c b/drivers/platform/x86/intel/pmc/core.c index 67eecef7a54f..cdaab728e942 100644 --- a/drivers/platform/x86/intel/pmc/core.c +++ b/drivers/platform/x86/intel/pmc/core.c @@ -1274,6 +1274,7 @@ static const struct x86_cpu_id intel_pmc_core_ids[] = { X86_MATCH_INTEL_FAM6_MODEL(RAPTORLAKE_S, adl_core_init), X86_MATCH_INTEL_FAM6_MODEL(METEORLAKE_L, mtl_core_init), X86_MATCH_INTEL_FAM6_MODEL(ARROWLAKE, arl_core_init), + X86_MATCH_INTEL_FAM6_MODEL(LUNARLAKE_M, lnl_core_init), {} }; diff --git a/drivers/platform/x86/intel/pmc/core.h b/drivers/platform/x86/intel/pmc/core.h index f9c2fee22e44..2891d8d04fad 100644 --- a/drivers/platform/x86/intel/pmc/core.h +++ b/drivers/platform/x86/intel/pmc/core.h @@ -278,6 +278,11 @@ enum ppfear_regs { #define MTL_PMT_DMU_GUID 0x1A067102 #define ARL_PMT_DMU_GUID 0x1A06A000 +#define LNL_PMC_MMIO_REG_LEN 0x2708 +#define LNL_PMC_LTR_OSSE 0x1B88 +#define LNL_NUM_IP_IGN_ALLOWED 27 +#define LNL_PPFEAR_NUM_ENTRIES 12 + extern const char *pmc_lpm_modes[]; struct pmc_bit_map { @@ -506,6 +511,26 @@ extern const struct pmc_bit_map mtl_ioem_power_gating_status_1_map[]; extern const struct pmc_bit_map mtl_ioem_vnn_req_status_1_map[]; extern const struct pmc_bit_map *mtl_ioem_lpm_maps[]; extern const struct pmc_reg_map mtl_ioem_reg_map; +extern const struct pmc_reg_map lnl_socm_reg_map; + +/* LNL */ +extern const struct pmc_bit_map lnl_ltr_show_map[]; +extern const struct pmc_bit_map lnl_clocksource_status_map[]; +extern const struct pmc_bit_map lnl_power_gating_status_0_map[]; +extern const struct pmc_bit_map lnl_power_gating_status_1_map[]; +extern const struct pmc_bit_map lnl_power_gating_status_2_map[]; +extern const struct pmc_bit_map lnl_d3_status_0_map[]; +extern const struct pmc_bit_map lnl_d3_status_1_map[]; +extern const struct pmc_bit_map lnl_d3_status_2_map[]; +extern const struct pmc_bit_map lnl_d3_status_3_map[]; +extern const struct pmc_bit_map lnl_vnn_req_status_0_map[]; +extern const struct pmc_bit_map lnl_vnn_req_status_1_map[]; +extern const struct pmc_bit_map lnl_vnn_req_status_2_map[]; +extern const struct pmc_bit_map lnl_vnn_req_status_3_map[]; +extern const struct pmc_bit_map lnl_vnn_misc_status_map[]; +extern const struct pmc_bit_map *lnl_lpm_maps[]; +extern const struct pmc_bit_map lnl_pfear_map[]; +extern const struct pmc_bit_map *ext_lnl_pfear_map[]; /* ARL */ extern const struct pmc_bit_map arl_socs_ltr_show_map[]; @@ -559,6 +584,7 @@ int tgl_core_generic_init(struct pmc_dev *pmcdev, int pch_tp); int adl_core_init(struct pmc_dev *pmcdev); int mtl_core_init(struct pmc_dev *pmcdev); int arl_core_init(struct pmc_dev *pmcdev); +int lnl_core_init(struct pmc_dev *pmcdev); #define pmc_for_each_mode(i, mode, pmcdev) \ for (i = 0, mode = pmcdev->lpm_en_modes[i]; \ diff --git a/drivers/platform/x86/intel/pmc/lnl.c b/drivers/platform/x86/intel/pmc/lnl.c new file mode 100644 index 000000000000..842f9cca0392 --- /dev/null +++ b/drivers/platform/x86/intel/pmc/lnl.c @@ -0,0 +1,546 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * This file contains platform specific structure definitions + * and init function used by Meteor Lake PCH. + * + * Copyright (c) 2022, Intel Corporation. + * All Rights Reserved. + * + */ + +#include +#include + +#include "core.h" + +#define SOCM_LPM_REQ_GUID 0x11594920 + +#define PMC_DEVID_SOCM 0xa87f + +static const u8 LNL_LPM_REG_INDEX[] = {0, 4, 5, 6, 8, 9, 10, 11, 12, 13, 14, 15, 16, 20}; + +static struct pmc_info lnl_pmc_info_list[] = { + { + .guid = SOCM_LPM_REQ_GUID, + .devid = PMC_DEVID_SOCM, + .map = &lnl_socm_reg_map, + }, + {} +}; + +const struct pmc_bit_map lnl_ltr_show_map[] = { + {"SOUTHPORT_A", CNP_PMC_LTR_SPA}, + {"SOUTHPORT_B", CNP_PMC_LTR_SPB}, + {"SATA", CNP_PMC_LTR_SATA}, + {"GIGABIT_ETHERNET", CNP_PMC_LTR_GBE}, + {"XHCI", CNP_PMC_LTR_XHCI}, + {"SOUTHPORT_F", ADL_PMC_LTR_SPF}, + {"ME", CNP_PMC_LTR_ME}, + /* EVA is Enterprise Value Add, doesn't really exist on PCH */ + {"SATA1", CNP_PMC_LTR_EVA}, + {"SOUTHPORT_C", CNP_PMC_LTR_SPC}, + {"HD_AUDIO", CNP_PMC_LTR_AZ}, + {"CNV", CNP_PMC_LTR_CNV}, + {"LPSS", CNP_PMC_LTR_LPSS}, + {"SOUTHPORT_D", CNP_PMC_LTR_SPD}, + {"SOUTHPORT_E", CNP_PMC_LTR_SPE}, + {"SATA2", CNP_PMC_LTR_CAM}, + {"ESPI", CNP_PMC_LTR_ESPI}, + {"SCC", CNP_PMC_LTR_SCC}, + {"ISH", CNP_PMC_LTR_ISH}, + {"UFSX2", CNP_PMC_LTR_UFSX2}, + {"EMMC", CNP_PMC_LTR_EMMC}, + /* + * Check intel_pmc_core_ids[] users of cnp_reg_map for + * a list of core SoCs using this. + */ + {"WIGIG", ICL_PMC_LTR_WIGIG}, + {"THC0", TGL_PMC_LTR_THC0}, + {"THC1", TGL_PMC_LTR_THC1}, + {"SOUTHPORT_G", CNP_PMC_LTR_RESERVED}, + + {"ESE", MTL_PMC_LTR_ESE}, + {"IOE_PMC", MTL_PMC_LTR_IOE_PMC}, + {"DMI3", ARL_PMC_LTR_DMI3}, + {"OSSE", LNL_PMC_LTR_OSSE}, + + /* Below two cannot be used for LTR_IGNORE */ + {"CURRENT_PLATFORM", CNP_PMC_LTR_CUR_PLT}, + {"AGGREGATED_SYSTEM", CNP_PMC_LTR_CUR_ASLT}, + {} +}; + +const struct pmc_bit_map lnl_power_gating_status_0_map[] = { + {"PMC_PGD0_PG_STS", BIT(0)}, + {"FUSE_OSSE_PGD0_PG_STS", BIT(1)}, + {"ESPISPI_PGD0_PG_STS", BIT(2)}, + {"XHCI_PGD0_PG_STS", BIT(3)}, + {"SPA_PGD0_PG_STS", BIT(4)}, + {"SPB_PGD0_PG_STS", BIT(5)}, + {"SPR16B0_PGD0_PG_STS", BIT(6)}, + {"GBE_PGD0_PG_STS", BIT(7)}, + {"SBR8B7_PGD0_PG_STS", BIT(8)}, + {"SBR8B6_PGD0_PG_STS", BIT(9)}, + {"SBR16B1_PGD0_PG_STS", BIT(10)}, + {"SBR8B8_PGD0_PG_STS", BIT(11)}, + {"ESE_PGD3_PG_STS", BIT(12)}, + {"D2D_DISP_PGD0_PG_STS", BIT(13)}, + {"LPSS_PGD0_PG_STS", BIT(14)}, + {"LPC_PGD0_PG_STS", BIT(15)}, + {"SMB_PGD0_PG_STS", BIT(16)}, + {"ISH_PGD0_PG_STS", BIT(17)}, + {"SBR8B2_PGD0_PG_STS", BIT(18)}, + {"NPK_PGD0_PG_STS", BIT(19)}, + {"D2D_NOC_PGD0_PG_STS", BIT(20)}, + {"SAFSS_PGD0_PG_STS", BIT(21)}, + {"FUSE_PGD0_PG_STS", BIT(22)}, + {"D2D_DISP_PGD1_PG_STS", BIT(23)}, + {"MPFPW1_PGD0_PG_STS", BIT(24)}, + {"XDCI_PGD0_PG_STS", BIT(25)}, + {"EXI_PGD0_PG_STS", BIT(26)}, + {"CSE_PGD0_PG_STS", BIT(27)}, + {"KVMCC_PGD0_PG_STS", BIT(28)}, + {"PMT_PGD0_PG_STS", BIT(29)}, + {"CLINK_PGD0_PG_STS", BIT(30)}, + {"PTIO_PGD0_PG_STS", BIT(31)}, + {} +}; + +const struct pmc_bit_map lnl_power_gating_status_1_map[] = { + {"USBR0_PGD0_PG_STS", BIT(0)}, + {"SUSRAM_PGD0_PG_STS", BIT(1)}, + {"SMT1_PGD0_PG_STS", BIT(2)}, + {"U3FPW1_PGD0_PG_STS", BIT(3)}, + {"SMS2_PGD0_PG_STS", BIT(4)}, + {"SMS1_PGD0_PG_STS", BIT(5)}, + {"CSMERTC_PGD0_PG_STS", BIT(6)}, + {"CSMEPSF_PGD0_PG_STS", BIT(7)}, + {"FIA_PG_PGD0_PG_STS", BIT(8)}, + {"SBR16B4_PGD0_PG_STS", BIT(9)}, + {"P2SB8B_PGD0_PG_STS", BIT(10)}, + {"DBG_SBR_PGD0_PG_STS", BIT(11)}, + {"SBR8B9_PGD0_PG_STS", BIT(12)}, + {"OSSE_SMT1_PGD0_PG_STS", BIT(13)}, + {"SBR8B10_PGD0_PG_STS", BIT(14)}, + {"SBR16B3_PGD0_PG_STS", BIT(15)}, + {"G5FPW1_PGD0_PG_STS", BIT(16)}, + {"SBRG_PGD0_PG_STS", BIT(17)}, + {"PSF4_PGD0_PG_STS", BIT(18)}, + {"CNVI_PGD0_PG_STS", BIT(19)}, + {"USFX2_PGD0_PG_STS", BIT(20)}, + {"ENDBG_PGD0_PG_STS", BIT(21)}, + {"FIACPCB_P5X4_PGD0_PG_STS", BIT(22)}, + {"SBR8B3_PGD0_PG_STS", BIT(23)}, + {"SBR8B0_PGD0_PG_STS", BIT(24)}, + {"NPK_PGD1_PG_STS", BIT(25)}, + {"OSSE_HOTHAM_PGD0_PG_STS", BIT(26)}, + {"D2D_NOC_PGD2_PG_STS", BIT(27)}, + {"SBR8B1_PGD0_PG_STS", BIT(28)}, + {"PSF6_PGD0_PG_STS", BIT(29)}, + {"PSF7_PGD0_PG_STS", BIT(30)}, + {"FIA_U_PGD0_PG_STS", BIT(31)}, + {} +}; + +const struct pmc_bit_map lnl_power_gating_status_2_map[] = { + {"PSF8_PGD0_PG_STS", BIT(0)}, + {"SBR16B2_PGD0_PG_STS", BIT(1)}, + {"D2D_IPU_PGD0_PG_STS", BIT(2)}, + {"FIACPCB_U_PGD0_PG_STS", BIT(3)}, + {"TAM_PGD0_PG_STS", BIT(4)}, + {"D2D_NOC_PGD1_PG_STS", BIT(5)}, + {"TBTLSX_PGD0_PG_STS", BIT(6)}, + {"THC0_PGD0_PG_STS", BIT(7)}, + {"THC1_PGD0_PG_STS", BIT(8)}, + {"PMC_PGD0_PG_STS", BIT(9)}, + {"SBR8B5_PGD0_PG_STS", BIT(10)}, + {"UFSPW1_PGD0_PG_STS", BIT(11)}, + {"DBC_PGD0_PG_STS", BIT(12)}, + {"TCSS_PGD0_PG_STS", BIT(13)}, + {"FIA_P5X4_PGD0_PG_STS", BIT(14)}, + {"DISP_PGA_PGD0_PG_STS", BIT(15)}, + {"DISP_PSF_PGD0_PG_STS", BIT(16)}, + {"PSF0_PGD0_PG_STS", BIT(17)}, + {"P2SB16B_PGD0_PG_STS", BIT(18)}, + {"ACE_PGD0_PG_STS", BIT(19)}, + {"ACE_PGD1_PG_STS", BIT(20)}, + {"ACE_PGD2_PG_STS", BIT(21)}, + {"ACE_PGD3_PG_STS", BIT(22)}, + {"ACE_PGD4_PG_STS", BIT(23)}, + {"ACE_PGD5_PG_STS", BIT(24)}, + {"ACE_PGD6_PG_STS", BIT(25)}, + {"ACE_PGD7_PG_STS", BIT(26)}, + {"ACE_PGD8_PG_STS", BIT(27)}, + {"ACE_PGD9_PG_STS", BIT(28)}, + {"ACE_PGD10_PG_STS", BIT(29)}, + {"FIACPCB_PG_PGD0_PG_STS", BIT(30)}, + {"OSSE_PGD0_PG_STS", BIT(31)}, + {} +}; + +const struct pmc_bit_map lnl_d3_status_0_map[] = { + {"LPSS_D3_STS", BIT(3)}, + {"XDCI_D3_STS", BIT(4)}, + {"XHCI_D3_STS", BIT(5)}, + {"SPA_D3_STS", BIT(12)}, + {"SPB_D3_STS", BIT(13)}, + {"OSSE_D3_STS", BIT(15)}, + {"ESPISPI_D3_STS", BIT(18)}, + {"PSTH_D3_STS", BIT(21)}, + {} +}; + +const struct pmc_bit_map lnl_d3_status_1_map[] = { + {"OSSE_SMT1_D3_STS", BIT(7)}, + {"GBE_D3_STS", BIT(19)}, + {"ITSS_D3_STS", BIT(23)}, + {"CNVI_D3_STS", BIT(27)}, + {"UFSX2_D3_STS", BIT(28)}, + {"OSSE_HOTHAM_D3_STS", BIT(31)}, + {} +}; + +const struct pmc_bit_map lnl_d3_status_2_map[] = { + {"ESE_D3_STS", BIT(0)}, + {"CSMERTC_D3_STS", BIT(1)}, + {"SUSRAM_D3_STS", BIT(2)}, + {"CSE_D3_STS", BIT(4)}, + {"KVMCC_D3_STS", BIT(5)}, + {"USBR0_D3_STS", BIT(6)}, + {"ISH_D3_STS", BIT(7)}, + {"SMT1_D3_STS", BIT(8)}, + {"SMT2_D3_STS", BIT(9)}, + {"SMT3_D3_STS", BIT(10)}, + {"OSSE_SMT2_D3_STS", BIT(13)}, + {"CLINK_D3_STS", BIT(14)}, + {"PTIO_D3_STS", BIT(16)}, + {"PMT_D3_STS", BIT(17)}, + {"SMS1_D3_STS", BIT(18)}, + {"SMS2_D3_STS", BIT(19)}, + {} +}; + +const struct pmc_bit_map lnl_d3_status_3_map[] = { + {"THC0_D3_STS", BIT(14)}, + {"THC1_D3_STS", BIT(15)}, + {"OSSE_SMT3_D3_STS", BIT(21)}, + {"ACE_D3_STS", BIT(23)}, + {} +}; + +const struct pmc_bit_map lnl_vnn_req_status_0_map[] = { + {"LPSS_VNN_REQ_STS", BIT(3)}, + {"OSSE_VNN_REQ_STS", BIT(15)}, + {"ESPISPI_VNN_REQ_STS", BIT(18)}, + {} +}; + +const struct pmc_bit_map lnl_vnn_req_status_1_map[] = { + {"NPK_VNN_REQ_STS", BIT(4)}, + {"OSSE_SMT1_VNN_REQ_STS", BIT(7)}, + {"DFXAGG_VNN_REQ_STS", BIT(8)}, + {"EXI_VNN_REQ_STS", BIT(9)}, + {"P2D_VNN_REQ_STS", BIT(18)}, + {"GBE_VNN_REQ_STS", BIT(19)}, + {"SMB_VNN_REQ_STS", BIT(25)}, + {"LPC_VNN_REQ_STS", BIT(26)}, + {} +}; + +const struct pmc_bit_map lnl_vnn_req_status_2_map[] = { + {"eSE_VNN_REQ_STS", BIT(0)}, + {"CSMERTC_VNN_REQ_STS", BIT(1)}, + {"CSE_VNN_REQ_STS", BIT(4)}, + {"ISH_VNN_REQ_STS", BIT(7)}, + {"SMT1_VNN_REQ_STS", BIT(8)}, + {"CLINK_VNN_REQ_STS", BIT(14)}, + {"SMS1_VNN_REQ_STS", BIT(18)}, + {"SMS2_VNN_REQ_STS", BIT(19)}, + {"GPIOCOM4_VNN_REQ_STS", BIT(20)}, + {"GPIOCOM3_VNN_REQ_STS", BIT(21)}, + {"GPIOCOM2_VNN_REQ_STS", BIT(22)}, + {"GPIOCOM1_VNN_REQ_STS", BIT(23)}, + {"GPIOCOM0_VNN_REQ_STS", BIT(24)}, + {} +}; + +const struct pmc_bit_map lnl_vnn_req_status_3_map[] = { + {"DISP_SHIM_VNN_REQ_STS", BIT(2)}, + {"DTS0_VNN_REQ_STS", BIT(7)}, + {"GPIOCOM5_VNN_REQ_STS", BIT(11)}, + {} +}; + +const struct pmc_bit_map lnl_vnn_misc_status_map[] = { + {"CPU_C10_REQ_STS", BIT(0)}, + {"TS_OFF_REQ_STS", BIT(1)}, + {"PNDE_MET_REQ_STS", BIT(2)}, + {"PCIE_DEEP_PM_REQ_STS", BIT(3)}, + {"PMC_CLK_THROTTLE_EN_REQ_STS", BIT(4)}, + {"NPK_VNNAON_REQ_STS", BIT(5)}, + {"VNN_SOC_REQ_STS", BIT(6)}, + {"ISH_VNNAON_REQ_STS", BIT(7)}, + {"D2D_NOC_CFI_QACTIVE_REQ_STS", BIT(8)}, + {"D2D_NOC_GPSB_QACTIVE_REQ_STS", BIT(9)}, + {"D2D_NOC_IPU_QACTIVE_REQ_STS", BIT(10)}, + {"PLT_GREATER_REQ_STS", BIT(11)}, + {"PCIE_CLKREQ_REQ_STS", BIT(12)}, + {"PMC_IDLE_FB_OCP_REQ_STS", BIT(13)}, + {"PM_SYNC_STATES_REQ_STS", BIT(14)}, + {"EA_REQ_STS", BIT(15)}, + {"MPHY_CORE_OFF_REQ_STS", BIT(16)}, + {"BRK_EV_EN_REQ_STS", BIT(17)}, + {"AUTO_DEMO_EN_REQ_STS", BIT(18)}, + {"ITSS_CLK_SRC_REQ_STS", BIT(19)}, + {"LPC_CLK_SRC_REQ_STS", BIT(20)}, + {"ARC_IDLE_REQ_STS", BIT(21)}, + {"MPHY_SUS_REQ_STS", BIT(22)}, + {"FIA_DEEP_PM_REQ_STS", BIT(23)}, + {"UXD_CONNECTED_REQ_STS", BIT(24)}, + {"ARC_INTERRUPT_WAKE_REQ_STS", BIT(25)}, + {"D2D_NOC_DISP_DDI_QACTIVE_REQ_STS", BIT(26)}, + {"PRE_WAKE0_REQ_STS", BIT(27)}, + {"PRE_WAKE1_REQ_STS", BIT(28)}, + {"PRE_WAKE2_EN_REQ_STS", BIT(29)}, + {"WOV_REQ_STS", BIT(30)}, + {"D2D_NOC_DISP_EDP_QACTIVE_REQ_STS_31", BIT(31)}, + {} +}; + +const struct pmc_bit_map lnl_clocksource_status_map[] = { + {"AON2_OFF_STS", BIT(0)}, + {"AON3_OFF_STS", BIT(1)}, + {"AON4_OFF_STS", BIT(2)}, + {"AON5_OFF_STS", BIT(3)}, + {"AON1_OFF_STS", BIT(4)}, + {"MPFPW1_0_PLL_OFF_STS", BIT(6)}, + {"USB3_PLL_OFF_STS", BIT(8)}, + {"AON3_SPL_OFF_STS", BIT(9)}, + {"G5FPW1_PLL_OFF_STS", BIT(15)}, + {"XTAL_AGGR_OFF_STS", BIT(17)}, + {"USB2_PLL_OFF_STS", BIT(18)}, + {"SAF_PLL_OFF_STS", BIT(19)}, + {"SE_TCSS_PLL_OFF_STS", BIT(20)}, + {"DDI_PLL_OFF_STS", BIT(21)}, + {"FILTER_PLL_OFF_STS", BIT(22)}, + {"ACE_PLL_OFF_STS", BIT(24)}, + {"FABRIC_PLL_OFF_STS", BIT(25)}, + {"SOC_PLL_OFF_STS", BIT(26)}, + {"REF_OFF_STS", BIT(28)}, + {"IMG_OFF_STS", BIT(29)}, + {"RTC_PLL_OFF_STS", BIT(31)}, + {} +}; + +const struct pmc_bit_map *lnl_lpm_maps[] = { + lnl_clocksource_status_map, + lnl_power_gating_status_0_map, + lnl_power_gating_status_1_map, + lnl_power_gating_status_2_map, + lnl_d3_status_0_map, + lnl_d3_status_1_map, + lnl_d3_status_2_map, + lnl_d3_status_3_map, + lnl_vnn_req_status_0_map, + lnl_vnn_req_status_1_map, + lnl_vnn_req_status_2_map, + lnl_vnn_req_status_3_map, + lnl_vnn_misc_status_map, + mtl_socm_signal_status_map, + NULL +}; + +const struct pmc_bit_map lnl_pfear_map[] = { + {"PMC_0", BIT(0)}, + {"FUSE_OSSE", BIT(1)}, + {"ESPISPI", BIT(2)}, + {"XHCI", BIT(3)}, + {"SPA", BIT(4)}, + {"SPB", BIT(5)}, + {"SBR16B0", BIT(6)}, + {"GBE", BIT(7)}, + + {"SBR8B7", BIT(0)}, + {"SBR8B6", BIT(1)}, + {"SBR16B1", BIT(1)}, + {"SBR8B8", BIT(2)}, + {"ESE", BIT(3)}, + {"SBR8B10", BIT(4)}, + {"D2D_DISP_0", BIT(5)}, + {"LPSS", BIT(6)}, + {"LPC", BIT(7)}, + + {"SMB", BIT(0)}, + {"ISH", BIT(1)}, + {"SBR8B2", BIT(2)}, + {"NPK_0", BIT(3)}, + {"D2D_NOC_0", BIT(4)}, + {"SAFSS", BIT(5)}, + {"FUSE", BIT(6)}, + {"D2D_DISP_1", BIT(7)}, + + {"MPFPW1", BIT(0)}, + {"XDCI", BIT(1)}, + {"EXI", BIT(2)}, + {"CSE", BIT(3)}, + {"KVMCC", BIT(4)}, + {"PMT", BIT(5)}, + {"CLINK", BIT(6)}, + {"PTIO", BIT(7)}, + + {"USBR", BIT(0)}, + {"SUSRAM", BIT(1)}, + {"SMT1", BIT(2)}, + {"U3FPW1", BIT(3)}, + {"SMS2", BIT(4)}, + {"SMS1", BIT(5)}, + {"CSMERTC", BIT(6)}, + {"CSMEPSF", BIT(7)}, + + {"FIA_PG", BIT(0)}, + {"SBR16B4", BIT(1)}, + {"P2SB8B", BIT(2)}, + {"DBG_SBR", BIT(3)}, + {"SBR8B9", BIT(4)}, + {"OSSE_SMT1", BIT(5)}, + {"SBR8B10", BIT(6)}, + {"SBR16B3", BIT(7)}, + + {"G5FPW1", BIT(0)}, + {"SBRG", BIT(1)}, + {"PSF4", BIT(2)}, + {"CNVI", BIT(3)}, + {"UFSX2", BIT(4)}, + {"ENDBG", BIT(5)}, + {"FIACPCB_P5X4", BIT(6)}, + {"SBR8B3", BIT(7)}, + + {"SBR8B0", BIT(0)}, + {"NPK_1", BIT(1)}, + {"OSSE_HOTHAM", BIT(2)}, + {"D2D_NOC_2", BIT(3)}, + {"SBR8B1", BIT(4)}, + {"PSF6", BIT(5)}, + {"PSF7", BIT(6)}, + {"FIA_U", BIT(7)}, + + {"PSF8", BIT(0)}, + {"SBR16B2", BIT(1)}, + {"D2D_IPU", BIT(2)}, + {"FIACPCB_U", BIT(3)}, + {"TAM", BIT(4)}, + {"D2D_NOC_1", BIT(5)}, + {"TBTLSX", BIT(6)}, + {"THC0", BIT(7)}, + + {"THC1", BIT(0)}, + {"PMC_1", BIT(1)}, + {"SBR8B5", BIT(2)}, + {"UFSPW1", BIT(3)}, + {"DBC", BIT(4)}, + {"TCSS", BIT(5)}, + {"FIA_P5X4", BIT(6)}, + {"DISP_PGA", BIT(7)}, + + {"DBG_PSF", BIT(0)}, + {"PSF0", BIT(1)}, + {"P2SB16B", BIT(2)}, + {"ACE0", BIT(3)}, + {"ACE1", BIT(4)}, + {"ACE2", BIT(5)}, + {"ACE3", BIT(6)}, + {"ACE4", BIT(7)}, + + {"ACE5", BIT(0)}, + {"ACE6", BIT(1)}, + {"ACE7", BIT(2)}, + {"ACE8", BIT(3)}, + {"ACE9", BIT(4)}, + {"ACE10", BIT(5)}, + {"FIACPCB", BIT(6)}, + {"OSSE", BIT(7)}, + {} +}; + +const struct pmc_bit_map *ext_lnl_pfear_map[] = { + lnl_pfear_map, + NULL +}; + +const struct pmc_reg_map lnl_socm_reg_map = { + .pfear_sts = ext_lnl_pfear_map, + .slp_s0_offset = CNP_PMC_SLP_S0_RES_COUNTER_OFFSET, + .slp_s0_res_counter_step = TGL_PMC_SLP_S0_RES_COUNTER_STEP, + .ltr_show_sts = lnl_ltr_show_map, + .msr_sts = msr_map, + .ltr_ignore_offset = CNP_PMC_LTR_IGNORE_OFFSET, + .regmap_length = LNL_PMC_MMIO_REG_LEN, + .ppfear0_offset = CNP_PMC_HOST_PPFEAR0A, + .ppfear_buckets = LNL_PPFEAR_NUM_ENTRIES, + .pm_cfg_offset = CNP_PMC_PM_CFG_OFFSET, + .pm_read_disable_bit = CNP_PMC_READ_DISABLE_BIT, + .ltr_ignore_max = LNL_NUM_IP_IGN_ALLOWED, + .lpm_num_maps = ADL_LPM_NUM_MAPS, + .lpm_res_counter_step_x2 = TGL_PMC_LPM_RES_COUNTER_STEP_X2, + .etr3_offset = ETR3_OFFSET, + .lpm_sts_latch_en_offset = MTL_LPM_STATUS_LATCH_EN_OFFSET, + .lpm_priority_offset = MTL_LPM_PRI_OFFSET, + .lpm_en_offset = MTL_LPM_EN_OFFSET, + .lpm_residency_offset = MTL_LPM_RESIDENCY_OFFSET, + .lpm_sts = lnl_lpm_maps, + .lpm_status_offset = MTL_LPM_STATUS_OFFSET, + .lpm_live_status_offset = MTL_LPM_LIVE_STATUS_OFFSET, + .lpm_reg_index = LNL_LPM_REG_INDEX, +}; + +#define LNL_NPU_PCI_DEV 0x643e +#define LNL_IPU_PCI_DEV 0x645d + +/* + * Set power state of select devices that do not have drivers to D3 + * so that they do not block Package C entry. + */ +static void lnl_d3_fixup(void) +{ + pmc_core_set_device_d3(LNL_IPU_PCI_DEV); + pmc_core_set_device_d3(LNL_NPU_PCI_DEV); +} + +static int lnl_resume(struct pmc_dev *pmcdev) +{ + lnl_d3_fixup(); + return pmc_core_resume_common(pmcdev); +} + +int lnl_core_init(struct pmc_dev *pmcdev) +{ + int ret; + int func = 2; + bool ssram_init = true; + struct pmc *pmc = pmcdev->pmcs[PMC_IDX_SOC]; + + lnl_d3_fixup(); + + pmcdev->resume = lnl_resume; + pmcdev->regmap_list = lnl_pmc_info_list; + ret = pmc_core_ssram_init(pmcdev, func); + + /* If regbase not assigned, set map and discover using legacy method */ + if (ret) { + ssram_init = false; + pmc->map = &lnl_socm_reg_map; + ret = get_primary_reg_base(pmc); + if (ret) + return ret; + } + + pmc_core_get_low_power_modes(pmcdev); + + if (ssram_init) { + ret = pmc_core_ssram_get_lpm_reqs(pmcdev); + if (ret) + return ret; + } + + return 0; +} -- cgit From 810bad6e055cdda9a72ff15f59fe497f5ae81606 Mon Sep 17 00:00:00 2001 From: Tanzir Hasan Date: Tue, 19 Dec 2023 22:10:52 +0000 Subject: platform/x86: hp-bioscfg: Removed needless asm-generic asm-generic/posix-types.h is obtained through bioscfg.h so there is no need to include it. It is also an asm-generic file which should be avoided if possible. Suggest-by: Al Viro Signed-off-by: Tanzir Hasan Reviewed-by: Nick Desaulniers Tested-by: Nick Desaulniers Link: https://lore.kernel.org/r/20231219-hp-password-v1-1-052fe7b6b7f1@google.com Signed-off-by: Hans de Goede --- drivers/platform/x86/hp/hp-bioscfg/passwdobj-attributes.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/platform/x86/hp/hp-bioscfg/passwdobj-attributes.c b/drivers/platform/x86/hp/hp-bioscfg/passwdobj-attributes.c index 03d0188804ba..f7efe217a4bb 100644 --- a/drivers/platform/x86/hp/hp-bioscfg/passwdobj-attributes.c +++ b/drivers/platform/x86/hp/hp-bioscfg/passwdobj-attributes.c @@ -7,7 +7,6 @@ */ #include "bioscfg.h" -#include GET_INSTANCE_ID(password); /* -- cgit From ef67575ac92142ba949ae628241dffc3d9acf56e Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Fri, 22 Dec 2023 16:44:53 +0200 Subject: platform/x86: Remove "X86 PLATFORM DRIVERS - ARCH" from MAINTAINERS It seems traffic there is quite low and changes are often not related to PDx86 anyhow. Besides that I have a lot of other stuff to do, I'm rearly pay attention on these emails. Doesn't seem Daren to be active either. With this in mind, remove (stale) section. Note, it might be make sense to actually move that folder under PDx86 umbrella (in MAINTAINERS) if people find it suitable. That will reduce burden on arch/x86 maintenance. Signed-off-by: Andy Shevchenko Link: https://lore.kernel.org/r/20231222144453.2888706-1-andriy.shevchenko@linux.intel.com Reviewed-by: Hans de Goede Signed-off-by: Hans de Goede --- MAINTAINERS | 9 --------- 1 file changed, 9 deletions(-) diff --git a/MAINTAINERS b/MAINTAINERS index e972ce5f54e7..ef7ddcb17146 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -23659,15 +23659,6 @@ F: drivers/platform/olpc/ F: drivers/platform/x86/ F: include/linux/platform_data/x86/ -X86 PLATFORM DRIVERS - ARCH -R: Darren Hart -R: Andy Shevchenko -L: platform-driver-x86@vger.kernel.org -L: x86@kernel.org -S: Maintained -T: git git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip.git x86/core -F: arch/x86/platform - X86 PLATFORM UV HPE SUPERDOME FLEX M: Steve Wahl R: Justin Ernst -- cgit From 27f2b08735c90d0f6bd5888edb58bbce7fcf22a8 Mon Sep 17 00:00:00 2001 From: Srinivas Pandruvada Date: Fri, 22 Dec 2023 12:39:57 -0800 Subject: platform/x86: intel-uncore-freq: Add additional client processors Add support for client processors starting from Kaby Lake. Signed-off-by: Srinivas Pandruvada Link: https://lore.kernel.org/r/20231222203957.1348043-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 | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/drivers/platform/x86/intel/uncore-frequency/uncore-frequency.c b/drivers/platform/x86/intel/uncore-frequency/uncore-frequency.c index a3b25253b6fd..a5e0f5c22179 100644 --- a/drivers/platform/x86/intel/uncore-frequency/uncore-frequency.c +++ b/drivers/platform/x86/intel/uncore-frequency/uncore-frequency.c @@ -205,6 +205,16 @@ 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(KABYLAKE, NULL), + X86_MATCH_INTEL_FAM6_MODEL(KABYLAKE_L, NULL), + X86_MATCH_INTEL_FAM6_MODEL(COMETLAKE, NULL), + X86_MATCH_INTEL_FAM6_MODEL(COMETLAKE_L, NULL), + X86_MATCH_INTEL_FAM6_MODEL(CANNONLAKE_L, NULL), + X86_MATCH_INTEL_FAM6_MODEL(ICELAKE, NULL), + X86_MATCH_INTEL_FAM6_MODEL(ICELAKE_L, NULL), + X86_MATCH_INTEL_FAM6_MODEL(ROCKETLAKE, NULL), + X86_MATCH_INTEL_FAM6_MODEL(TIGERLAKE, NULL), + X86_MATCH_INTEL_FAM6_MODEL(TIGERLAKE_L, NULL), X86_MATCH_INTEL_FAM6_MODEL(ALDERLAKE, NULL), X86_MATCH_INTEL_FAM6_MODEL(ALDERLAKE_L, NULL), X86_MATCH_INTEL_FAM6_MODEL(RAPTORLAKE, NULL), @@ -212,6 +222,9 @@ static const struct x86_cpu_id intel_uncore_cpu_ids[] = { X86_MATCH_INTEL_FAM6_MODEL(RAPTORLAKE_S, NULL), X86_MATCH_INTEL_FAM6_MODEL(METEORLAKE, NULL), X86_MATCH_INTEL_FAM6_MODEL(METEORLAKE_L, NULL), + X86_MATCH_INTEL_FAM6_MODEL(ARROWLAKE, NULL), + X86_MATCH_INTEL_FAM6_MODEL(ARROWLAKE_H, NULL), + X86_MATCH_INTEL_FAM6_MODEL(LUNARLAKE_M, NULL), {} }; MODULE_DEVICE_TABLE(x86cpu, intel_uncore_cpu_ids); -- cgit From 10ed9ee0af5a6cab8b36b301865417a288179b06 Mon Sep 17 00:00:00 2001 From: "David E. Box" Date: Fri, 22 Dec 2023 19:25:46 -0800 Subject: platform/x86/intel/pmc/arl: Add GBE LTR ignore during suspend Add the GBE LTR ignore suspend time fix for Arrow Lake. Fixes: f34dcf397286 ("platform/x86/intel/pmc: Add Arrow Lake S support to intel_pmc_core driver") Signed-off-by: David E. Box Link: https://lore.kernel.org/r/20231223032548.1680738-7-david.e.box@linux.intel.com Reviewed-by: Hans de Goede Signed-off-by: Hans de Goede --- drivers/platform/x86/intel/pmc/arl.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/platform/x86/intel/pmc/arl.c b/drivers/platform/x86/intel/pmc/arl.c index e6190247f5df..683ae828276b 100644 --- a/drivers/platform/x86/intel/pmc/arl.c +++ b/drivers/platform/x86/intel/pmc/arl.c @@ -685,6 +685,8 @@ static void arl_d3_fixup(void) static int arl_resume(struct pmc_dev *pmcdev) { arl_d3_fixup(); + pmc_core_send_ltr_ignore(pmcdev, 3, 0); + return pmc_core_resume_common(pmcdev); } @@ -696,6 +698,7 @@ int arl_core_init(struct pmc_dev *pmcdev) bool ssram_init = true; arl_d3_fixup(); + pmcdev->suspend = cnl_suspend; pmcdev->resume = arl_resume; pmcdev->regmap_list = arl_pmc_info_list; -- cgit From 6f9fac5535ba2038063c656f0afb496d7f87bcc1 Mon Sep 17 00:00:00 2001 From: "David E. Box" Date: Fri, 22 Dec 2023 19:25:47 -0800 Subject: platform/x86/intel/pmc/lnl: Add GBE LTR ignore during suspend Add the GBE LTR ignore suspend time fix for Lunar Lake. Fixes: 119652b855e6 ("platform/x86/intel/pmc: Add Lunar Lake M support to intel_pmc_core driver") Signed-off-by: David E. Box Link: https://lore.kernel.org/r/20231223032548.1680738-8-david.e.box@linux.intel.com Reviewed-by: Hans de Goede Signed-off-by: Hans de Goede --- drivers/platform/x86/intel/pmc/lnl.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/platform/x86/intel/pmc/lnl.c b/drivers/platform/x86/intel/pmc/lnl.c index 842f9cca0392..abad17cdd3d7 100644 --- a/drivers/platform/x86/intel/pmc/lnl.c +++ b/drivers/platform/x86/intel/pmc/lnl.c @@ -509,6 +509,8 @@ static void lnl_d3_fixup(void) static int lnl_resume(struct pmc_dev *pmcdev) { lnl_d3_fixup(); + pmc_core_send_ltr_ignore(pmcdev, 3, 0); + return pmc_core_resume_common(pmcdev); } @@ -521,6 +523,7 @@ int lnl_core_init(struct pmc_dev *pmcdev) lnl_d3_fixup(); + pmcdev->suspend = cnl_suspend; pmcdev->resume = lnl_resume; pmcdev->regmap_list = lnl_pmc_info_list; ret = pmc_core_ssram_init(pmcdev, func); -- cgit From 669f157fd7ad987dd5beba46576ec357f4d6c686 Mon Sep 17 00:00:00 2001 From: "David E. Box" Date: Fri, 22 Dec 2023 19:25:48 -0800 Subject: platform/x86/intel/pmc: Add missing extern Add missing extern for tgl_h_reg_map. Fixes sparse warning: drivers/platform/x86/intel/pmc/tgl.c:213:26: warning: symbol 'tgl_h_reg_map' was not declared. Should it be static? Fixes: 544f7b7f651c ("platform/x86/intel/pmc: Add regmap for Tiger Lake H PCH") Signed-off-by: David E. Box Link: https://lore.kernel.org/r/20231223032548.1680738-9-david.e.box@linux.intel.com Reviewed-by: Hans de Goede Signed-off-by: Hans de Goede --- drivers/platform/x86/intel/pmc/core.h | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/platform/x86/intel/pmc/core.h b/drivers/platform/x86/intel/pmc/core.h index e2826e1ef90f..54137faaae2b 100644 --- a/drivers/platform/x86/intel/pmc/core.h +++ b/drivers/platform/x86/intel/pmc/core.h @@ -452,6 +452,7 @@ extern const struct pmc_bit_map tgl_vnn_misc_status_map[]; extern const struct pmc_bit_map tgl_signal_status_map[]; extern const struct pmc_bit_map *tgl_lpm_maps[]; extern const struct pmc_reg_map tgl_reg_map; +extern const struct pmc_reg_map tgl_h_reg_map; extern const struct pmc_bit_map adl_pfear_map[]; extern const struct pmc_bit_map *ext_adl_pfear_map[]; extern const struct pmc_bit_map adl_ltr_show_map[]; -- cgit From 87d8f1ee1d4070571095859d310f6951440f288c Mon Sep 17 00:00:00 2001 From: Randy Dunlap Date: Sat, 23 Dec 2023 11:43:19 -0800 Subject: platform/x86: wmi: linux/wmi.h: fix Excess kernel-doc description warning MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Remove the "private:" comment to prevent the kernel-doc warning: include/linux/wmi.h:27: warning: Excess struct member 'setable' description in 'wmi_device' Either a struct member is documented (via kernel-doc) or it's private, but not both. Fixes: b4cc979588ee ("platform/x86: wmi: Add kernel doc comments") Signed-off-by: Randy Dunlap Cc: Armin Wolf Cc: Hans de Goede Cc: Ilpo Järvinen Cc: platform-driver-x86@vger.kernel.org Reviewed-by: Armin Wolf Link: https://lore.kernel.org/r/20231223194321.23084-1-rdunlap@infradead.org Signed-off-by: Hans de Goede --- include/linux/wmi.h | 2 -- 1 file changed, 2 deletions(-) diff --git a/include/linux/wmi.h b/include/linux/wmi.h index 50f7f1e4fd4f..686291b87852 100644 --- a/include/linux/wmi.h +++ b/include/linux/wmi.h @@ -21,8 +21,6 @@ */ struct wmi_device { struct device dev; - - /* private: used by the WMI driver core */ bool setable; }; -- cgit From 9dd3f1ef40d01fdfe69c3ada28b1f201a7207f6e Mon Sep 17 00:00:00 2001 From: Markus Elfring Date: Thu, 28 Dec 2023 09:30:54 +0100 Subject: platform/x86/amd/pmf: Return directly after a failed apmf_if_call() in apmf_sbios_heartbeat_notify() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The kfree() function was called in one case by the apmf_sbios_heartbeat_notify() function during error handling even if the passed variable contained a null pointer. This issue was detected by using the Coccinelle software. * Thus return directly after a call of the function “apmf_if_call” failed at the beginning. * Delete the label “out” which became unnecessary with this refactoring. Signed-off-by: Markus Elfring Link: https://lore.kernel.org/r/362ee824-fc53-4e19-9529-8b621657635b@web.de Reviewed-by: Hans de Goede Signed-off-by: Hans de Goede --- drivers/platform/x86/amd/pmf/acpi.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/drivers/platform/x86/amd/pmf/acpi.c b/drivers/platform/x86/amd/pmf/acpi.c index 4ec7957eb707..eb367e38c5ab 100644 --- a/drivers/platform/x86/amd/pmf/acpi.c +++ b/drivers/platform/x86/amd/pmf/acpi.c @@ -135,11 +135,9 @@ static void apmf_sbios_heartbeat_notify(struct work_struct *work) dev_dbg(dev->dev, "Sending heartbeat to SBIOS\n"); info = apmf_if_call(dev, APMF_FUNC_SBIOS_HEARTBEAT, NULL); if (!info) - goto out; + return; schedule_delayed_work(&dev->heart_beat, msecs_to_jiffies(dev->hb_interval * 1000)); - -out: kfree(info); } -- cgit From 4527898e300ce65e9ac5d5a05e086a07e212753c Mon Sep 17 00:00:00 2001 From: Markus Elfring Date: Thu, 28 Dec 2023 10:48:16 +0100 Subject: platform/x86/amd/pmf: Return a status code only as a constant in two functions Return a status code without storing it in an intermediate variable. Signed-off-by: Markus Elfring Link: https://lore.kernel.org/r/0d0c4876-37d7-4bee-912e-56324495454f@web.de Reviewed-by: Hans de Goede Signed-off-by: Hans de Goede --- drivers/platform/x86/amd/pmf/acpi.c | 15 +++++---------- 1 file changed, 5 insertions(+), 10 deletions(-) diff --git a/drivers/platform/x86/amd/pmf/acpi.c b/drivers/platform/x86/amd/pmf/acpi.c index eb367e38c5ab..f2eb07ef855a 100644 --- a/drivers/platform/x86/amd/pmf/acpi.c +++ b/drivers/platform/x86/amd/pmf/acpi.c @@ -111,7 +111,6 @@ int apmf_os_power_slider_update(struct amd_pmf_dev *pdev, u8 event) struct os_power_slider args; struct acpi_buffer params; union acpi_object *info; - int err = 0; args.size = sizeof(args); args.slider_event = event; @@ -121,10 +120,10 @@ int apmf_os_power_slider_update(struct amd_pmf_dev *pdev, u8 event) info = apmf_if_call(pdev, APMF_FUNC_OS_POWER_SLIDER_UPDATE, ¶ms); if (!info) - err = -EIO; + return -EIO; kfree(info); - return err; + return 0; } static void apmf_sbios_heartbeat_notify(struct work_struct *work) @@ -146,7 +145,6 @@ int apmf_update_fan_idx(struct amd_pmf_dev *pdev, bool manual, u32 idx) union acpi_object *info; struct apmf_fan_idx args; struct acpi_buffer params; - int err = 0; args.size = sizeof(args); args.fan_ctl_mode = manual; @@ -156,14 +154,11 @@ int apmf_update_fan_idx(struct amd_pmf_dev *pdev, bool manual, u32 idx) params.pointer = (void *)&args; info = apmf_if_call(pdev, APMF_FUNC_SET_FAN_IDX, ¶ms); - if (!info) { - err = -EIO; - goto out; - } + if (!info) + return -EIO; -out: kfree(info); - return err; + return 0; } int apmf_get_auto_mode_def(struct amd_pmf_dev *pdev, struct apmf_auto_mode *data) -- cgit From 9efa2a04ce7c827bf265c8963b38553306a8225e Mon Sep 17 00:00:00 2001 From: Shyam Sundar S K Date: Thu, 28 Dec 2023 15:50:59 +0530 Subject: platform/x86/amd/pmc: Send OS_HINT command for AMDI000A platform MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit To initiate the HW deep state transistion the OS_HINT command has to be sent the PMFW. Add this support to the platforms that has AMDI000A support. Signed-off-by: Sanket Goswami Signed-off-by: Shyam Sundar S K Reviewed-by: Ilpo Järvinen Link: https://lore.kernel.org/r/20231228102104.1785383-2-Shyam-sundar.S-k@amd.com Signed-off-by: Hans de Goede --- drivers/platform/x86/amd/pmc/pmc.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/platform/x86/amd/pmc/pmc.c b/drivers/platform/x86/amd/pmc/pmc.c index 864c8cc2f8a3..5e962f395a69 100644 --- a/drivers/platform/x86/amd/pmc/pmc.c +++ b/drivers/platform/x86/amd/pmc/pmc.c @@ -751,6 +751,7 @@ static int amd_pmc_get_os_hint(struct amd_pmc_dev *dev) case AMD_CPU_ID_YC: case AMD_CPU_ID_CB: case AMD_CPU_ID_PS: + case PCI_DEVICE_ID_AMD_1AH_M20H_ROOT: return MSG_OS_HINT_RN; } return -EINVAL; -- cgit From d8fb50fe6c5c3632079a1642afdd87d30e0d1bbe Mon Sep 17 00:00:00 2001 From: Shyam Sundar S K Date: Thu, 28 Dec 2023 15:51:00 +0530 Subject: platform/x86/amd/pmc: Add VPE information for AMDI000A platform MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Latest AMD SoCs has VPE (Video Processing Engine) IP block and the statistics related to this IP can be obtained as a part of metrics table information that the PMFW propogates. Add this support for 1Ah family series. Signed-off-by: Sanket Goswami Signed-off-by: Shyam Sundar S K Reviewed-by: Ilpo Järvinen Link: https://lore.kernel.org/r/20231228102104.1785383-3-Shyam-sundar.S-k@amd.com Signed-off-by: Hans de Goede --- drivers/platform/x86/amd/pmc/pmc.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/drivers/platform/x86/amd/pmc/pmc.c b/drivers/platform/x86/amd/pmc/pmc.c index 5e962f395a69..6826e26a7650 100644 --- a/drivers/platform/x86/amd/pmc/pmc.c +++ b/drivers/platform/x86/amd/pmc/pmc.c @@ -145,6 +145,7 @@ static const struct amd_pmc_bit_map soc15_ip_blk[] = { {"JPEG", BIT(18)}, {"IPU", BIT(19)}, {"UMSCH", BIT(20)}, + {"VPE", BIT(21)}, {} }; @@ -355,6 +356,10 @@ static void amd_pmc_get_ip_info(struct amd_pmc_dev *dev) dev->num_ips = 21; dev->s2d_msg_id = 0x85; break; + case PCI_DEVICE_ID_AMD_1AH_M20H_ROOT: + dev->num_ips = 22; + dev->s2d_msg_id = 0xDE; + break; } } -- cgit From 13313c135266ed62d33f43281fb22289865064fd Mon Sep 17 00:00:00 2001 From: Shyam Sundar S K Date: Thu, 28 Dec 2023 15:51:01 +0530 Subject: platform/x86/amd/pmc: call amd_pmc_get_ip_info() during driver probe MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In the current code, amd_pmc_get_ip_info() is being called from amd_pmc_s2d_init() and that code block gets enabled only when the STB is being enabled. But the information from amd_pmc_get_ip_info() will be required outside of STB usecase. Hence move this call into driver probe sequence. Signed-off-by: Sanket Goswami Signed-off-by: Shyam Sundar S K Reviewed-by: Ilpo Järvinen Link: https://lore.kernel.org/r/20231228102104.1785383-4-Shyam-sundar.S-k@amd.com Signed-off-by: Hans de Goede --- drivers/platform/x86/amd/pmc/pmc.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/platform/x86/amd/pmc/pmc.c b/drivers/platform/x86/amd/pmc/pmc.c index 6826e26a7650..bc52229b155e 100644 --- a/drivers/platform/x86/amd/pmc/pmc.c +++ b/drivers/platform/x86/amd/pmc/pmc.c @@ -973,9 +973,6 @@ static int amd_pmc_s2d_init(struct amd_pmc_dev *dev) /* Spill to DRAM feature uses separate SMU message port */ dev->msg_port = 1; - /* Get num of IP blocks within the SoC */ - amd_pmc_get_ip_info(dev); - amd_pmc_send_cmd(dev, S2D_TELEMETRY_SIZE, &size, dev->s2d_msg_id, true); if (size != S2D_TELEMETRY_BYTES_MAX) return -EIO; @@ -1083,6 +1080,9 @@ static int amd_pmc_probe(struct platform_device *pdev) mutex_init(&dev->lock); + /* Get num of IP blocks within the SoC */ + amd_pmc_get_ip_info(dev); + if (enable_stb && amd_pmc_is_stb_supported(dev)) { err = amd_pmc_s2d_init(dev); if (err) -- cgit From 9ae57d88609d2077a8340d316fc9849a601844f9 Mon Sep 17 00:00:00 2001 From: Shyam Sundar S K Date: Thu, 28 Dec 2023 15:51:02 +0530 Subject: platform/x86/amd/pmc: Add idlemask support for 1Ah family MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Idlemask is an indication of each IP block current state (i.e. whether it is running or idle) during s2idle transistion. The newer 1Ah family supports this feature, add it to the support list. Signed-off-by: Sanket Goswami Signed-off-by: Shyam Sundar S K Reviewed-by: Ilpo Järvinen Link: https://lore.kernel.org/r/20231228102104.1785383-5-Shyam-sundar.S-k@amd.com Signed-off-by: Hans de Goede --- drivers/platform/x86/amd/pmc/pmc.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/platform/x86/amd/pmc/pmc.c b/drivers/platform/x86/amd/pmc/pmc.c index bc52229b155e..ceb2b5677f78 100644 --- a/drivers/platform/x86/amd/pmc/pmc.c +++ b/drivers/platform/x86/amd/pmc/pmc.c @@ -38,6 +38,7 @@ /* PMC Scratch Registers */ #define AMD_PMC_SCRATCH_REG_CZN 0x94 #define AMD_PMC_SCRATCH_REG_YC 0xD14 +#define AMD_PMC_SCRATCH_REG_1AH 0xF14 /* STB Registers */ #define AMD_PMC_STB_PMI_0 0x03E30600 @@ -593,6 +594,9 @@ static int amd_pmc_idlemask_read(struct amd_pmc_dev *pdev, struct device *dev, case AMD_CPU_ID_PS: val = amd_pmc_reg_read(pdev, AMD_PMC_SCRATCH_REG_YC); break; + case PCI_DEVICE_ID_AMD_1AH_M20H_ROOT: + val = amd_pmc_reg_read(pdev, AMD_PMC_SCRATCH_REG_1AH); + break; default: return -EINVAL; } -- cgit From d33e992ec87a7a7b1839fac38573d2c0721569f9 Mon Sep 17 00:00:00 2001 From: Shyam Sundar S K Date: Thu, 28 Dec 2023 15:51:03 +0530 Subject: platform/x86/amd/pmc: Add 1Ah family series to STB support list MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit AMD newer platforms, (AMDI000A or family 1Ah series) also supports the STB functionality. Add this to amd_pmc_is_stb_supported(). Signed-off-by: Sanket Goswami Signed-off-by: Shyam Sundar S K Reviewed-by: Ilpo Järvinen Link: https://lore.kernel.org/r/20231228102104.1785383-6-Shyam-sundar.S-k@amd.com Signed-off-by: Hans de Goede --- drivers/platform/x86/amd/pmc/pmc.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/platform/x86/amd/pmc/pmc.c b/drivers/platform/x86/amd/pmc/pmc.c index ceb2b5677f78..3845cbd17f71 100644 --- a/drivers/platform/x86/amd/pmc/pmc.c +++ b/drivers/platform/x86/amd/pmc/pmc.c @@ -627,6 +627,7 @@ static bool amd_pmc_is_stb_supported(struct amd_pmc_dev *dev) case AMD_CPU_ID_YC: case AMD_CPU_ID_CB: case AMD_CPU_ID_PS: + case PCI_DEVICE_ID_AMD_1AH_M20H_ROOT: return true; default: return false; -- cgit From 236f7d8034ff401d02fa6d74bae494a2b54e1834 Mon Sep 17 00:00:00 2001 From: Shyam Sundar S K Date: Thu, 28 Dec 2023 15:51:04 +0530 Subject: platform/x86/amd/pmc: Modify SMU message port for latest AMD platform MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The latest platforms use a different SMU message port(0x938) from the one currently being used (0x538). Make code changes to adapt to this new information. Signed-off-by: Sanket Goswami Signed-off-by: Shyam Sundar S K Reviewed-by: Ilpo Järvinen Link: https://lore.kernel.org/r/20231228102104.1785383-7-Shyam-sundar.S-k@amd.com Signed-off-by: Hans de Goede --- drivers/platform/x86/amd/pmc/pmc.c | 8 +++++--- drivers/platform/x86/amd/pmc/pmc.h | 1 + 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/drivers/platform/x86/amd/pmc/pmc.c b/drivers/platform/x86/amd/pmc/pmc.c index 3845cbd17f71..108e12fd580f 100644 --- a/drivers/platform/x86/amd/pmc/pmc.c +++ b/drivers/platform/x86/amd/pmc/pmc.c @@ -31,7 +31,6 @@ #include "pmc.h" /* SMU communication registers */ -#define AMD_PMC_REGISTER_MESSAGE 0x538 #define AMD_PMC_REGISTER_RESPONSE 0x980 #define AMD_PMC_REGISTER_ARGUMENT 0x9BC @@ -352,14 +351,17 @@ static void amd_pmc_get_ip_info(struct amd_pmc_dev *dev) case AMD_CPU_ID_CB: dev->num_ips = 12; dev->s2d_msg_id = 0xBE; + dev->smu_msg = 0x538; break; case AMD_CPU_ID_PS: dev->num_ips = 21; dev->s2d_msg_id = 0x85; + dev->smu_msg = 0x538; break; case PCI_DEVICE_ID_AMD_1AH_M20H_ROOT: dev->num_ips = 22; dev->s2d_msg_id = 0xDE; + dev->smu_msg = 0x938; break; } } @@ -663,7 +665,7 @@ static void amd_pmc_dump_registers(struct amd_pmc_dev *dev) argument = AMD_S2D_REGISTER_ARGUMENT; response = AMD_S2D_REGISTER_RESPONSE; } else { - message = AMD_PMC_REGISTER_MESSAGE; + message = dev->smu_msg; argument = AMD_PMC_REGISTER_ARGUMENT; response = AMD_PMC_REGISTER_RESPONSE; } @@ -690,7 +692,7 @@ static int amd_pmc_send_cmd(struct amd_pmc_dev *dev, u32 arg, u32 *data, u8 msg, argument = AMD_S2D_REGISTER_ARGUMENT; response = AMD_S2D_REGISTER_RESPONSE; } else { - message = AMD_PMC_REGISTER_MESSAGE; + message = dev->smu_msg; argument = AMD_PMC_REGISTER_ARGUMENT; response = AMD_PMC_REGISTER_RESPONSE; } diff --git a/drivers/platform/x86/amd/pmc/pmc.h b/drivers/platform/x86/amd/pmc/pmc.h index b4794f118739..827eef65e133 100644 --- a/drivers/platform/x86/amd/pmc/pmc.h +++ b/drivers/platform/x86/amd/pmc/pmc.h @@ -26,6 +26,7 @@ struct amd_pmc_dev { u32 dram_size; u32 num_ips; u32 s2d_msg_id; + u32 smu_msg; /* SMU version information */ u8 smu_program; u8 major; -- cgit