diff options
63 files changed, 2301 insertions, 524 deletions
diff --git a/Documentation/ABI/testing/debugfs-tpmi b/Documentation/ABI/testing/debugfs-tpmi index 597f0475fe6e..c493a1403d2f 100644 --- a/Documentation/ABI/testing/debugfs-tpmi +++ b/Documentation/ABI/testing/debugfs-tpmi @@ -29,3 +29,12 @@ Example: echo 0,0x20,0xff > mem_write echo 1,64,64 > mem_write Users: Debugging, any user space test suite + +What: /sys/kernel/debug/tpmi-<n>/plr/domain<n>/status +Date: Aug 2024 +KernelVersion: 6.11 +Contact: Tero Kristo <tero.kristo@linux.intel.com> +Description: +Shows the currently active Performance Limit Reasons for die level and the +individual CPUs under the die. The contents of this file are sticky, and +clearing all the statuses can be done by writing "0\n" to this file. diff --git a/Documentation/ABI/testing/sysfs-bus-wmi b/Documentation/ABI/testing/sysfs-bus-wmi new file mode 100644 index 000000000000..aadb35b82198 --- /dev/null +++ b/Documentation/ABI/testing/sysfs-bus-wmi @@ -0,0 +1,81 @@ +What: /sys/bus/wmi/devices/.../driver_override +Date: February 2024 +Contact: Armin Wolf <W_Armin@gmx.de> +Description: + This file allows the driver for a device to be specified which + will override standard ID table matching. + When specified, only a driver with a name matching the value + written to driver_override will have an opportunity to bind + to the device. + The override is specified by writing a string to the + driver_override file (echo wmi-event-dummy > driver_override). + The override may be cleared with an empty string (echo > \ + driver_override) which returns the device to standard matching + rules binding. + Writing to driver_override does not automatically unbind the + device from its current driver or make any attempt to automatically + load the specified driver. If no driver with a matching name is + currently loaded in the kernel, the device will not bind to any + driver. + This also allows devices to opt-out of driver binding using a + driver_override name such as "none". Only a single driver may be + specified in the override, there is no support for parsing delimiters. + +What: /sys/bus/wmi/devices/.../modalias +Date: November 20:15 +Contact: Andy Lutomirski <luto@kernel.org> +Description: + This file contains the MODALIAS value emitted by uevent for a + given WMI device. + + Format: wmi:XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX. + +What: /sys/bus/wmi/devices/.../guid +Date: November 2015 +Contact: Andy Lutomirski <luto@kernel.org> +Description: + This file contains the GUID used to match WMI devices to + compatible WMI drivers. This GUID is not necessarily unique + inside a given machine, it is solely used to identify the + interface exposed by a given WMI device. + +What: /sys/bus/wmi/devices/.../object_id +Date: November 2015 +Contact: Andy Lutomirski <luto@kernel.org> +Description: + This file contains the WMI object ID used internally to construct + the ACPI method names used by non-event WMI devices. It contains + two ASCII letters. + +What: /sys/bus/wmi/devices/.../notify_id +Date: November 2015 +Contact: Andy Lutomirski <luto@kernel.org> +Description: + This file contains the WMI notify ID used internally to map ACPI + events to WMI event devices. It contains two ASCII letters. + +What: /sys/bus/wmi/devices/.../instance_count +Date: November 2015 +Contact: Andy Lutomirski <luto@kernel.org> +Description: + This file contains the number of WMI object instances being + present on a given WMI device. It contains a non-negative + number. + +What: /sys/bus/wmi/devices/.../expensive +Date: November 2015 +Contact: Andy Lutomirski <luto@kernel.org> +Description: + This file contains a boolean flag signaling if interacting with + the given WMI device will consume significant CPU resources. + The WMI driver core will take care of enabling/disabling such + WMI devices. + +What: /sys/bus/wmi/devices/.../setable +Date: May 2017 +Contact: Darren Hart (VMware) <dvhart@infradead.org> +Description: + This file contains a boolean flags signaling the data block + aassociated with the given WMI device is writable. If the + given WMI device is not associated with a data block, then + this file will not exist. diff --git a/Documentation/admin-guide/index.rst b/Documentation/admin-guide/index.rst index 32ea52f1d150..e85b1adf5908 100644 --- a/Documentation/admin-guide/index.rst +++ b/Documentation/admin-guide/index.rst @@ -121,7 +121,6 @@ configure specific aspects of kernel behavior to your liking. parport perf-security pm/index - pmf pnp rapidio RAS/index diff --git a/Documentation/admin-guide/pmf.rst b/Documentation/admin-guide/pmf.rst deleted file mode 100644 index 9ee729ffc19b..000000000000 --- a/Documentation/admin-guide/pmf.rst +++ /dev/null @@ -1,24 +0,0 @@ -.. 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/Documentation/devicetree/bindings/platform/lenovo,yoga-c630-ec.yaml b/Documentation/devicetree/bindings/platform/lenovo,yoga-c630-ec.yaml new file mode 100644 index 000000000000..3180ce1a22d4 --- /dev/null +++ b/Documentation/devicetree/bindings/platform/lenovo,yoga-c630-ec.yaml @@ -0,0 +1,83 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/platform/lenovo,yoga-c630-ec.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Lenovo Yoga C630 Embedded Controller. + +maintainers: + - Bjorn Andersson <andersson@kernel.org> + +description: + The Qualcomm Snapdragon-based Lenovo Yoga C630 has an Embedded Controller + (EC) which handles things such as battery and USB Type-C. This binding + describes the interface, on an I2C bus, to this EC. + +properties: + compatible: + const: lenovo,yoga-c630-ec + + reg: + const: 0x70 + + '#address-cells': + const: 1 + + '#size-cells': + const: 0 + + interrupts: + maxItems: 1 + +patternProperties: + '^connector@[01]$': + $ref: /schemas/connector/usb-connector.yaml# + + properties: + reg: + maxItems: 1 + + unevaluatedProperties: false + +required: + - compatible + - reg + - interrupts + +additionalProperties: false + +examples: + - |+ + #include <dt-bindings/interrupt-controller/irq.h> + i2c1 { + clock-frequency = <400000>; + + #address-cells = <1>; + #size-cells = <0>; + + embedded-controller@70 { + compatible = "lenovo,yoga-c630-ec"; + reg = <0x70>; + + interrupts-extended = <&tlmm 20 IRQ_TYPE_LEVEL_HIGH>; + + #address-cells = <1>; + #size-cells = <0>; + + connector@0 { + compatible = "usb-c-connector"; + reg = <0>; + power-role = "source"; + data-role = "host"; + }; + + connector@1 { + compatible = "usb-c-connector"; + reg = <1>; + power-role = "source"; + data-role = "host"; + }; + }; + }; +... diff --git a/MAINTAINERS b/MAINTAINERS index 811394fe7df0..1cfe0509873d 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -392,6 +392,7 @@ ACPI WMI DRIVER M: Armin Wolf <W_Armin@gmx.de> L: platform-driver-x86@vger.kernel.org S: Maintained +F: Documentation/ABI/testing/sysfs-bus-wmi F: Documentation/driver-api/wmi.rst F: Documentation/wmi/ F: drivers/platform/x86/wmi.c @@ -6194,6 +6195,12 @@ F: Documentation/ABI/obsolete/procfs-i8k F: drivers/hwmon/dell-smm-hwmon.c F: include/uapi/linux/i8k.h +DELL PC DRIVER +M: Lyndon Sanche <lsanche@lyndeno.ca> +L: platform-driver-x86@vger.kernel.org +S: Maintained +F: drivers/platform/x86/dell/dell-pc.c + DELL REMOTE BIOS UPDATE DRIVER M: Stuart Hayes <stuart.w.hayes@gmail.com> L: platform-driver-x86@vger.kernel.org diff --git a/arch/x86/platform/atom/punit_atom_debug.c b/arch/x86/platform/atom/punit_atom_debug.c index 6b9c6deca8ba..44c30ce6360a 100644 --- a/arch/x86/platform/atom/punit_atom_debug.c +++ b/arch/x86/platform/atom/punit_atom_debug.c @@ -165,14 +165,13 @@ static void punit_s2idle_check_register(struct punit_device *punit_device) {} static void punit_s2idle_check_unregister(void) {} #endif -#define X86_MATCH(model, data) \ - X86_MATCH_VENDOR_FAM_MODEL_FEATURE(INTEL, 6, INTEL_FAM6_##model, \ - X86_FEATURE_MWAIT, data) +#define X86_MATCH(vfm, data) \ + X86_MATCH_VFM_FEATURE(vfm, X86_FEATURE_MWAIT, data) static const struct x86_cpu_id intel_punit_cpu_ids[] = { - X86_MATCH(ATOM_SILVERMONT, &punit_device_byt), - X86_MATCH(ATOM_SILVERMONT_MID, &punit_device_tng), - X86_MATCH(ATOM_AIRMONT, &punit_device_cht), + X86_MATCH(INTEL_ATOM_SILVERMONT, &punit_device_byt), + X86_MATCH(INTEL_ATOM_SILVERMONT_MID, &punit_device_tng), + X86_MATCH(INTEL_ATOM_AIRMONT, &punit_device_cht), {} }; MODULE_DEVICE_TABLE(x86cpu, intel_punit_cpu_ids); diff --git a/drivers/platform/Makefile b/drivers/platform/Makefile index bf69cc8d7429..19ac54648586 100644 --- a/drivers/platform/Makefile +++ b/drivers/platform/Makefile @@ -12,4 +12,4 @@ obj-$(CONFIG_GOLDFISH) += goldfish/ obj-$(CONFIG_CHROME_PLATFORMS) += chrome/ obj-$(CONFIG_CZNIC_PLATFORMS) += cznic/ obj-$(CONFIG_SURFACE_PLATFORMS) += surface/ -obj-$(CONFIG_ARM64) += arm64/ +obj-$(CONFIG_ARM64_PLATFORM_DEVICES) += arm64/ diff --git a/drivers/platform/arm64/Kconfig b/drivers/platform/arm64/Kconfig index 8fdca0f8e909..f88395ea3376 100644 --- a/drivers/platform/arm64/Kconfig +++ b/drivers/platform/arm64/Kconfig @@ -18,6 +18,7 @@ if ARM64_PLATFORM_DEVICES config EC_ACER_ASPIRE1 tristate "Acer Aspire 1 Embedded Controller driver" + depends on ARCH_QCOM || COMPILE_TEST depends on I2C depends on DRM depends on POWER_SUPPLY @@ -32,4 +33,20 @@ config EC_ACER_ASPIRE1 laptop where this information is not properly exposed via the standard ACPI devices. +config EC_LENOVO_YOGA_C630 + tristate "Lenovo Yoga C630 Embedded Controller driver" + depends on ARCH_QCOM || COMPILE_TEST + depends on I2C + select AUXILIARY_BUS + help + Driver for the Embedded Controller in the Qualcomm Snapdragon-based + Lenovo Yoga C630, which provides battery and power adapter + information. + + This driver provides battery and AC status support for the mentioned + laptop where this information is not properly exposed via the + standard ACPI devices. + + Say M or Y here to include this support. + endif # ARM64_PLATFORM_DEVICES diff --git a/drivers/platform/arm64/Makefile b/drivers/platform/arm64/Makefile index 4fcc9855579b..b2ae9114fdd8 100644 --- a/drivers/platform/arm64/Makefile +++ b/drivers/platform/arm64/Makefile @@ -6,3 +6,4 @@ # obj-$(CONFIG_EC_ACER_ASPIRE1) += acer-aspire1-ec.o +obj-$(CONFIG_EC_LENOVO_YOGA_C630) += lenovo-yoga-c630.o diff --git a/drivers/platform/arm64/lenovo-yoga-c630.c b/drivers/platform/arm64/lenovo-yoga-c630.c new file mode 100644 index 000000000000..1f05c9a6a89d --- /dev/null +++ b/drivers/platform/arm64/lenovo-yoga-c630.c @@ -0,0 +1,291 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2022-2024, Linaro Ltd + * Authors: + * Bjorn Andersson + * Dmitry Baryshkov + */ +#include <linux/auxiliary_bus.h> +#include <linux/cleanup.h> +#include <linux/device.h> +#include <linux/err.h> +#include <linux/i2c.h> +#include <linux/irqreturn.h> +#include <linux/lockdep.h> +#include <linux/module.h> +#include <linux/mutex.h> +#include <linux/notifier.h> +#include <linux/slab.h> +#include <linux/platform_data/lenovo-yoga-c630.h> + +#define LENOVO_EC_RESPONSE_REG 0x01 +#define LENOVO_EC_REQUEST_REG 0x02 + +#define LENOVO_EC_UCSI_WRITE 0x20 +#define LENOVO_EC_UCSI_READ 0x21 + +#define LENOVO_EC_READ_REG 0xb0 +#define LENOVO_EC_REQUEST_NEXT_EVENT 0x84 + +#define LENOVO_EC_UCSI_VERSION 0x20 + +struct yoga_c630_ec { + struct i2c_client *client; + struct mutex lock; + struct blocking_notifier_head notifier_list; +}; + +static int yoga_c630_ec_request(struct yoga_c630_ec *ec, u8 *req, size_t req_len, + u8 *resp, size_t resp_len) +{ + int ret; + + lockdep_assert_held(&ec->lock); + + ret = i2c_smbus_write_i2c_block_data(ec->client, LENOVO_EC_REQUEST_REG, + req_len, req); + if (ret < 0) + return ret; + + return i2c_smbus_read_i2c_block_data(ec->client, LENOVO_EC_RESPONSE_REG, + resp_len, resp); +} + +int yoga_c630_ec_read8(struct yoga_c630_ec *ec, u8 addr) +{ + u8 req[2] = { LENOVO_EC_READ_REG, }; + int ret; + u8 val; + + guard(mutex)(&ec->lock); + + req[1] = addr; + ret = yoga_c630_ec_request(ec, req, sizeof(req), &val, 1); + if (ret < 0) + return ret; + + return val; +} +EXPORT_SYMBOL_GPL(yoga_c630_ec_read8); + +int yoga_c630_ec_read16(struct yoga_c630_ec *ec, u8 addr) +{ + u8 req[2] = { LENOVO_EC_READ_REG, }; + int ret; + u8 msb; + u8 lsb; + + /* don't overflow the address */ + if (addr == 0xff) + return -EINVAL; + + guard(mutex)(&ec->lock); + + req[1] = addr; + ret = yoga_c630_ec_request(ec, req, sizeof(req), &lsb, 1); + if (ret < 0) + return ret; + + req[1] = addr + 1; + ret = yoga_c630_ec_request(ec, req, sizeof(req), &msb, 1); + if (ret < 0) + return ret; + + return msb << 8 | lsb; +} +EXPORT_SYMBOL_GPL(yoga_c630_ec_read16); + +u16 yoga_c630_ec_ucsi_get_version(struct yoga_c630_ec *ec) +{ + u8 req[3] = { 0xb3, 0xf2, }; + int ret; + u8 msb; + u8 lsb; + + guard(mutex)(&ec->lock); + + req[2] = LENOVO_EC_UCSI_VERSION; + ret = yoga_c630_ec_request(ec, req, sizeof(req), &lsb, 1); + if (ret < 0) + return ret; + + req[2] = LENOVO_EC_UCSI_VERSION + 1; + ret = yoga_c630_ec_request(ec, req, sizeof(req), &msb, 1); + if (ret < 0) + return ret; + + return msb << 8 | lsb; +} +EXPORT_SYMBOL_GPL(yoga_c630_ec_ucsi_get_version); + +int yoga_c630_ec_ucsi_write(struct yoga_c630_ec *ec, + const u8 req[YOGA_C630_UCSI_WRITE_SIZE]) +{ + int ret; + + mutex_lock(&ec->lock); + ret = i2c_smbus_write_i2c_block_data(ec->client, LENOVO_EC_UCSI_WRITE, + YOGA_C630_UCSI_WRITE_SIZE, req); + mutex_unlock(&ec->lock); + + return ret < 0 ? ret : 0; +} +EXPORT_SYMBOL_GPL(yoga_c630_ec_ucsi_write); + +int yoga_c630_ec_ucsi_read(struct yoga_c630_ec *ec, + u8 resp[YOGA_C630_UCSI_READ_SIZE]) +{ + int ret; + + mutex_lock(&ec->lock); + ret = i2c_smbus_read_i2c_block_data(ec->client, LENOVO_EC_UCSI_READ, + YOGA_C630_UCSI_READ_SIZE, resp); + mutex_unlock(&ec->lock); + + return ret < 0 ? ret : 0; +} +EXPORT_SYMBOL_GPL(yoga_c630_ec_ucsi_read); + +static irqreturn_t yoga_c630_ec_thread_intr(int irq, void *data) +{ + u8 req[] = { LENOVO_EC_REQUEST_NEXT_EVENT }; + struct yoga_c630_ec *ec = data; + u8 event; + int ret; + + mutex_lock(&ec->lock); + ret = yoga_c630_ec_request(ec, req, sizeof(req), &event, 1); + mutex_unlock(&ec->lock); + if (ret < 0) + return IRQ_HANDLED; + + blocking_notifier_call_chain(&ec->notifier_list, event, ec); + + return IRQ_HANDLED; +} + +/** + * yoga_c630_ec_register_notify - Register a notifier callback for EC events. + * @ec: Yoga C630 EC + * @nb: Notifier block pointer to register + * + * Return: 0 on success or negative error code. + */ +int yoga_c630_ec_register_notify(struct yoga_c630_ec *ec, struct notifier_block *nb) +{ + return blocking_notifier_chain_register(&ec->notifier_list, nb); +} +EXPORT_SYMBOL_GPL(yoga_c630_ec_register_notify); + +/** + * yoga_c630_ec_unregister_notify - Unregister notifier callback for EC events. + * @ec: Yoga C630 EC + * @nb: Notifier block pointer to unregister + * + * Unregister a notifier callback that was previously registered with + * yoga_c630_ec_register_notify(). + */ +void yoga_c630_ec_unregister_notify(struct yoga_c630_ec *ec, struct notifier_block *nb) +{ + blocking_notifier_chain_unregister(&ec->notifier_list, nb); +} +EXPORT_SYMBOL_GPL(yoga_c630_ec_unregister_notify); + +static void yoga_c630_aux_release(struct device *dev) +{ + struct auxiliary_device *adev = to_auxiliary_dev(dev); + + kfree(adev); +} + +static void yoga_c630_aux_remove(void *data) +{ + struct auxiliary_device *adev = data; + + auxiliary_device_delete(adev); + auxiliary_device_uninit(adev); +} + +static int yoga_c630_aux_init(struct device *parent, const char *name, + struct yoga_c630_ec *ec) +{ + struct auxiliary_device *adev; + int ret; + + adev = kzalloc(sizeof(*adev), GFP_KERNEL); + if (!adev) + return -ENOMEM; + + adev->name = name; + adev->id = 0; + adev->dev.parent = parent; + adev->dev.release = yoga_c630_aux_release; + adev->dev.platform_data = ec; + + ret = auxiliary_device_init(adev); + if (ret) { + kfree(adev); + return ret; + } + + ret = auxiliary_device_add(adev); + if (ret) { + auxiliary_device_uninit(adev); + return ret; + } + + return devm_add_action_or_reset(parent, yoga_c630_aux_remove, adev); +} + +static int yoga_c630_ec_probe(struct i2c_client *client) +{ + struct device *dev = &client->dev; + struct yoga_c630_ec *ec; + int ret; + + ec = devm_kzalloc(dev, sizeof(*ec), GFP_KERNEL); + if (!ec) + return -ENOMEM; + + mutex_init(&ec->lock); + ec->client = client; + BLOCKING_INIT_NOTIFIER_HEAD(&ec->notifier_list); + + ret = devm_request_threaded_irq(dev, client->irq, + NULL, yoga_c630_ec_thread_intr, + IRQF_ONESHOT, "yoga_c630_ec", ec); + if (ret < 0) + return dev_err_probe(dev, ret, "unable to request irq\n"); + + ret = yoga_c630_aux_init(dev, YOGA_C630_DEV_PSY, ec); + if (ret) + return ret; + + return yoga_c630_aux_init(dev, YOGA_C630_DEV_UCSI, ec); +} + + +static const struct of_device_id yoga_c630_ec_of_match[] = { + { .compatible = "lenovo,yoga-c630-ec" }, + {} +}; +MODULE_DEVICE_TABLE(of, yoga_c630_ec_of_match); + +static const struct i2c_device_id yoga_c630_ec_i2c_id_table[] = { + { "yoga-c630-ec", }, + {} +}; +MODULE_DEVICE_TABLE(i2c, yoga_c630_ec_i2c_id_table); + +static struct i2c_driver yoga_c630_ec_i2c_driver = { + .driver = { + .name = "yoga-c630-ec", + .of_match_table = yoga_c630_ec_of_match + }, + .probe = yoga_c630_ec_probe, + .id_table = yoga_c630_ec_i2c_id_table, +}; +module_i2c_driver(yoga_c630_ec_i2c_driver); + +MODULE_DESCRIPTION("Lenovo Yoga C630 Embedded Controller"); +MODULE_LICENSE("GPL"); diff --git a/drivers/platform/x86/amd/pmf/pmf.h b/drivers/platform/x86/amd/pmf/pmf.h index eeedd0c0395a..753d5662c080 100644 --- a/drivers/platform/x86/amd/pmf/pmf.h +++ b/drivers/platform/x86/amd/pmf/pmf.h @@ -12,6 +12,7 @@ #define PMF_H #include <linux/acpi.h> +#include <linux/input.h> #include <linux/platform_profile.h> #define POLICY_BUF_MAX_SZ 0x4b000 @@ -300,6 +301,7 @@ struct amd_pmf_dev { void __iomem *policy_base; bool smart_pc_enabled; u16 pmf_if_version; + struct input_dev *pmf_idev; }; struct apmf_sps_prop_granular_v2 { diff --git a/drivers/platform/x86/amd/pmf/tee-if.c b/drivers/platform/x86/amd/pmf/tee-if.c index b438de4d6bfc..e246367aacee 100644 --- a/drivers/platform/x86/amd/pmf/tee-if.c +++ b/drivers/platform/x86/amd/pmf/tee-if.c @@ -62,18 +62,12 @@ 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) +static void 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; + input_report_key(dev->pmf_idev, event, 1); /* key press */ + input_sync(dev->pmf_idev); + input_report_key(dev->pmf_idev, event, 0); /* key release */ + input_sync(dev->pmf_idev); } static void amd_pmf_apply_policies(struct amd_pmf_dev *dev, struct ta_pmf_enact_result *out) @@ -149,7 +143,20 @@ static void amd_pmf_apply_policies(struct amd_pmf_dev *dev, struct ta_pmf_enact_ break; case PMF_POLICY_SYSTEM_STATE: - amd_pmf_update_uevents(dev, val); + switch (val) { + case 0: + amd_pmf_update_uevents(dev, KEY_SLEEP); + break; + case 1: + amd_pmf_update_uevents(dev, KEY_SUSPEND); + break; + case 2: + amd_pmf_update_uevents(dev, KEY_SCREENLOCK); + break; + default: + dev_err(dev->dev, "Invalid PMF policy system state: %d\n", val); + } + dev_dbg(dev->dev, "update SYSTEM_STATE: %s\n", amd_pmf_uevent_as_str(val)); break; @@ -301,14 +308,9 @@ static ssize_t amd_pmf_get_pb_data(struct file *filp, const char __user *buf, 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)) { - kfree(new_policy_buf); - return -EFAULT; - } + new_policy_buf = memdup_user(buf, length); + if (IS_ERR(new_policy_buf)) + return PTR_ERR(new_policy_buf); kfree(dev->policy_buf); dev->policy_buf = new_policy_buf; @@ -368,6 +370,30 @@ static int amd_pmf_ta_open_session(struct tee_context *ctx, u32 *id) return rc; } +static int amd_pmf_register_input_device(struct amd_pmf_dev *dev) +{ + int err; + + dev->pmf_idev = devm_input_allocate_device(dev->dev); + if (!dev->pmf_idev) + return -ENOMEM; + + dev->pmf_idev->name = "PMF-TA output events"; + dev->pmf_idev->phys = "amd-pmf/input0"; + + input_set_capability(dev->pmf_idev, EV_KEY, KEY_SLEEP); + input_set_capability(dev->pmf_idev, EV_KEY, KEY_SCREENLOCK); + input_set_capability(dev->pmf_idev, EV_KEY, KEY_SUSPEND); + + err = input_register_device(dev->pmf_idev); + if (err) { + dev_err(dev->dev, "Failed to register input device: %d\n", err); + return err; + } + + return 0; +} + static int amd_pmf_tee_init(struct amd_pmf_dev *dev) { u32 size; @@ -475,6 +501,10 @@ int amd_pmf_init_smart_pc(struct amd_pmf_dev *dev) if (pb_side_load) amd_pmf_open_pb(dev, dev->dbgfs_dir); + ret = amd_pmf_register_input_device(dev); + if (ret) + goto error; + return 0; error: @@ -485,6 +515,9 @@ error: void amd_pmf_deinit_smart_pc(struct amd_pmf_dev *dev) { + if (dev->pmf_idev) + input_unregister_device(dev->pmf_idev); + if (pb_side_load && dev->esbin) amd_pmf_remove_pb(dev); diff --git a/drivers/platform/x86/asus-tf103c-dock.c b/drivers/platform/x86/asus-tf103c-dock.c index 8f0f87637c5f..b441d8ca72d3 100644 --- a/drivers/platform/x86/asus-tf103c-dock.c +++ b/drivers/platform/x86/asus-tf103c-dock.c @@ -490,7 +490,7 @@ static void tf103c_dock_enable_touchpad(struct tf103c_dock_data *dock) return; } - strscpy(board_info.type, "elan_i2c", I2C_NAME_SIZE); + strscpy(board_info.type, "elan_i2c"); board_info.addr = TF103C_DOCK_TP_ADDR; board_info.dev_name = TF103C_DOCK_DEV_NAME "-tp"; board_info.irq = dock->tp_irq; @@ -795,7 +795,7 @@ static int tf103c_dock_probe(struct i2c_client *client) */ dock->ec_client = client; - strscpy(board_info.type, "tf103c-dock-intr", I2C_NAME_SIZE); + strscpy(board_info.type, "tf103c-dock-intr"); board_info.addr = TF103C_DOCK_INTR_ADDR; board_info.dev_name = TF103C_DOCK_DEV_NAME "-intr"; @@ -803,7 +803,7 @@ static int tf103c_dock_probe(struct i2c_client *client) if (IS_ERR(dock->intr_client)) return dev_err_probe(dev, PTR_ERR(dock->intr_client), "creating intr client\n"); - strscpy(board_info.type, "tf103c-dock-kbd", I2C_NAME_SIZE); + strscpy(board_info.type, "tf103c-dock-kbd"); board_info.addr = TF103C_DOCK_KBD_ADDR; board_info.dev_name = TF103C_DOCK_DEV_NAME "-kbd"; @@ -846,8 +846,8 @@ static int tf103c_dock_probe(struct i2c_client *client) dock->hid->vendor = 0x0b05; /* USB_VENDOR_ID_ASUSTEK */ dock->hid->product = 0x0103; /* From TF-103-C */ dock->hid->version = 0x0100; /* 1.0 */ - strscpy(dock->hid->name, "Asus TF103C Dock Keyboard", sizeof(dock->hid->name)); - strscpy(dock->hid->phys, dev_name(dev), sizeof(dock->hid->phys)); + strscpy(dock->hid->name, "Asus TF103C Dock Keyboard"); + strscpy(dock->hid->phys, dev_name(dev)); ret = hid_add_device(dock->hid); if (ret) diff --git a/drivers/platform/x86/asus-wmi.c b/drivers/platform/x86/asus-wmi.c index 3f9b6285c9a6..cc735931f97b 100644 --- a/drivers/platform/x86/asus-wmi.c +++ b/drivers/platform/x86/asus-wmi.c @@ -238,6 +238,7 @@ struct asus_wmi { struct led_classdev lightbar_led; int lightbar_led_wk; struct led_classdev micmute_led; + struct led_classdev camera_led; struct workqueue_struct *led_workqueue; struct work_struct tpd_led_work; struct work_struct wlan_led_work; @@ -879,10 +880,14 @@ static ssize_t kbd_rgb_mode_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { - struct asus_wmi *asus = dev_get_drvdata(dev); u32 cmd, mode, r, g, b, speed; + struct led_classdev *led; + struct asus_wmi *asus; int err; + led = dev_get_drvdata(dev); + asus = container_of(led, struct asus_wmi, kbd_led); + if (sscanf(buf, "%d %d %d %d %d %d", &cmd, &mode, &r, &g, &b, &speed) != 6) return -EINVAL; @@ -1642,6 +1647,27 @@ static int micmute_led_set(struct led_classdev *led_cdev, return err < 0 ? err : 0; } +static enum led_brightness camera_led_get(struct led_classdev *led_cdev) +{ + struct asus_wmi *asus; + u32 result; + + asus = container_of(led_cdev, struct asus_wmi, camera_led); + asus_wmi_get_devstate(asus, ASUS_WMI_DEVID_CAMERA_LED, &result); + + return result & ASUS_WMI_DSTS_BRIGHTNESS_MASK; +} + +static int camera_led_set(struct led_classdev *led_cdev, + enum led_brightness brightness) +{ + int state = brightness != LED_OFF; + int err; + + err = asus_wmi_set_devstate(ASUS_WMI_DEVID_CAMERA_LED, state, NULL); + return err < 0 ? err : 0; +} + static void asus_wmi_led_exit(struct asus_wmi *asus) { led_classdev_unregister(&asus->kbd_led); @@ -1649,6 +1675,7 @@ static void asus_wmi_led_exit(struct asus_wmi *asus) led_classdev_unregister(&asus->wlan_led); led_classdev_unregister(&asus->lightbar_led); led_classdev_unregister(&asus->micmute_led); + led_classdev_unregister(&asus->camera_led); if (asus->led_workqueue) destroy_workqueue(asus->led_workqueue); @@ -1740,6 +1767,18 @@ static int asus_wmi_led_init(struct asus_wmi *asus) goto error; } + if (asus_wmi_dev_is_present(asus, ASUS_WMI_DEVID_CAMERA_LED)) { + asus->camera_led.name = "asus::camera"; + asus->camera_led.max_brightness = 1; + asus->camera_led.brightness_get = camera_led_get; + asus->camera_led.brightness_set_blocking = camera_led_set; + + rv = led_classdev_register(&asus->platform_device->dev, + &asus->camera_led); + if (rv) + goto error; + } + error: if (rv) asus_wmi_led_exit(asus); diff --git a/drivers/platform/x86/dell/Kconfig b/drivers/platform/x86/dell/Kconfig index 195a8bf532cc..85a78ef91182 100644 --- a/drivers/platform/x86/dell/Kconfig +++ b/drivers/platform/x86/dell/Kconfig @@ -91,6 +91,19 @@ config DELL_RBTN To compile this driver as a module, choose M here: the module will be called dell-rbtn. +config DELL_PC + tristate "Dell PC Extras" + default m + depends on ACPI + depends on DMI + depends on DELL_SMBIOS + select ACPI_PLATFORM_PROFILE + help + This driver adds support for controlling the fan modes via platform_profile + on supported Dell systems regardless of formfactor. + Module will simply do nothing if thermal management commands are not + supported. + # # The DELL_SMBIOS driver depends on ACPI_WMI and/or DCDBAS if those # backends are selected. The "depends" line prevents a configuration diff --git a/drivers/platform/x86/dell/Makefile b/drivers/platform/x86/dell/Makefile index 8176a257d9c3..79d60f1bf4c1 100644 --- a/drivers/platform/x86/dell/Makefile +++ b/drivers/platform/x86/dell/Makefile @@ -9,6 +9,7 @@ obj-$(CONFIG_DCDBAS) += dcdbas.o obj-$(CONFIG_DELL_LAPTOP) += dell-laptop.o obj-$(CONFIG_DELL_RBTN) += dell-rbtn.o obj-$(CONFIG_DELL_RBU) += dell_rbu.o +obj-$(CONFIG_DELL_PC) += dell-pc.o obj-$(CONFIG_DELL_SMBIOS) += dell-smbios.o dell-smbios-objs := dell-smbios-base.o dell-smbios-$(CONFIG_DELL_SMBIOS_WMI) += dell-smbios-wmi.o diff --git a/drivers/platform/x86/dell/dell-laptop.c b/drivers/platform/x86/dell/dell-laptop.c index 42f7de2b4522..6552dfe491c6 100644 --- a/drivers/platform/x86/dell/dell-laptop.c +++ b/drivers/platform/x86/dell/dell-laptop.c @@ -353,29 +353,6 @@ static const struct dmi_system_id dell_quirks[] __initconst = { { } }; -static void dell_fill_request(struct calling_interface_buffer *buffer, - u32 arg0, u32 arg1, u32 arg2, u32 arg3) -{ - memset(buffer, 0, sizeof(struct calling_interface_buffer)); - buffer->input[0] = arg0; - buffer->input[1] = arg1; - buffer->input[2] = arg2; - buffer->input[3] = arg3; -} - -static int dell_send_request(struct calling_interface_buffer *buffer, - u16 class, u16 select) -{ - int ret; - - buffer->cmd_class = class; - buffer->cmd_select = select; - ret = dell_smbios_call(buffer); - if (ret != 0) - return ret; - return dell_smbios_error(buffer->output[0]); -} - /* * Derived from information in smbios-wireless-ctl: * diff --git a/drivers/platform/x86/dell/dell-pc.c b/drivers/platform/x86/dell/dell-pc.c new file mode 100644 index 000000000000..972385ca1990 --- /dev/null +++ b/drivers/platform/x86/dell/dell-pc.c @@ -0,0 +1,309 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Driver for Dell laptop extras + * + * Copyright (c) Lyndon Sanche <lsanche@lyndeno.ca> + * + * Based on documentation in the libsmbios package: + * Copyright (C) 2005-2014 Dell Inc. + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include <linux/bitfield.h> +#include <linux/bits.h> +#include <linux/dmi.h> +#include <linux/err.h> +#include <linux/init.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/platform_profile.h> +#include <linux/slab.h> + +#include "dell-smbios.h" + +static const struct dmi_system_id dell_device_table[] __initconst = { + { + .ident = "Dell Inc.", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), + }, + }, + { + .ident = "Dell Computer Corporation", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Dell Computer Corporation"), + }, + }, + { } +}; +MODULE_DEVICE_TABLE(dmi, dell_device_table); + +/* Derived from smbios-thermal-ctl + * + * cbClass 17 + * cbSelect 19 + * User Selectable Thermal Tables(USTT) + * cbArg1 determines the function to be performed + * cbArg1 0x0 = Get Thermal Information + * cbRES1 Standard return codes (0, -1, -2) + * cbRES2, byte 0 Bitmap of supported thermal modes. A mode is supported if + * its bit is set to 1 + * Bit 0 Balanced + * Bit 1 Cool Bottom + * Bit 2 Quiet + * Bit 3 Performance + * cbRES2, byte 1 Bitmap of supported Active Acoustic Controller (AAC) modes. + * Each mode corresponds to the supported thermal modes in + * byte 0. A mode is supported if its bit is set to 1. + * Bit 0 AAC (Balanced) + * Bit 1 AAC (Cool Bottom + * Bit 2 AAC (Quiet) + * Bit 3 AAC (Performance) + * cbRes3, byte 0 Current Thermal Mode + * Bit 0 Balanced + * Bit 1 Cool Bottom + * Bit 2 Quiet + * Bit 3 Performanc + * cbRes3, byte 1 AAC Configuration type + * 0 Global (AAC enable/disable applies to all supported USTT modes) + * 1 USTT mode specific + * cbRes3, byte 2 Current Active Acoustic Controller (AAC) Mode + * If AAC Configuration Type is Global, + * 0 AAC mode disabled + * 1 AAC mode enabled + * If AAC Configuration Type is USTT mode specific (multiple bits may be set), + * Bit 0 AAC (Balanced) + * Bit 1 AAC (Cool Bottom + * Bit 2 AAC (Quiet) + * Bit 3 AAC (Performance) + * cbRes3, byte 3 Current Fan Failure Mode + * Bit 0 Minimal Fan Failure (at least one fan has failed, one fan working) + * Bit 1 Catastrophic Fan Failure (all fans have failed) + * + * cbArg1 0x1 (Set Thermal Information), both desired thermal mode and + * desired AAC mode shall be applied + * cbArg2, byte 0 Desired Thermal Mode to set + * (only one bit may be set for this parameter) + * Bit 0 Balanced + * Bit 1 Cool Bottom + * Bit 2 Quiet + * Bit 3 Performance + * cbArg2, byte 1 Desired Active Acoustic Controller (AAC) Mode to set + * If AAC Configuration Type is Global, + * 0 AAC mode disabled + * 1 AAC mode enabled + * If AAC Configuration Type is USTT mode specific + * (multiple bits may be set for this parameter), + * Bit 0 AAC (Balanced) + * Bit 1 AAC (Cool Bottom + * Bit 2 AAC (Quiet) + * Bit 3 AAC (Performance) + */ + +#define DELL_ACC_GET_FIELD GENMASK(19, 16) +#define DELL_ACC_SET_FIELD GENMASK(11, 8) +#define DELL_THERMAL_SUPPORTED GENMASK(3, 0) + +static struct platform_profile_handler *thermal_handler; + +enum thermal_mode_bits { + DELL_BALANCED = BIT(0), + DELL_COOL_BOTTOM = BIT(1), + DELL_QUIET = BIT(2), + DELL_PERFORMANCE = BIT(3), +}; + +static int thermal_get_mode(void) +{ + struct calling_interface_buffer buffer; + int state; + int ret; + + dell_fill_request(&buffer, 0x0, 0, 0, 0); + ret = dell_send_request(&buffer, CLASS_INFO, SELECT_THERMAL_MANAGEMENT); + if (ret) + return ret; + state = buffer.output[2]; + if (state & DELL_BALANCED) + return DELL_BALANCED; + else if (state & DELL_COOL_BOTTOM) + return DELL_COOL_BOTTOM; + else if (state & DELL_QUIET) + return DELL_QUIET; + else if (state & DELL_PERFORMANCE) + return DELL_PERFORMANCE; + else + return -ENXIO; +} + +static int thermal_get_supported_modes(int *supported_bits) +{ + struct calling_interface_buffer buffer; + int ret; + + dell_fill_request(&buffer, 0x0, 0, 0, 0); + ret = dell_send_request(&buffer, CLASS_INFO, SELECT_THERMAL_MANAGEMENT); + /* Thermal function not supported */ + if (ret == -ENXIO) { + *supported_bits = 0; + return 0; + } + if (ret) + return ret; + *supported_bits = FIELD_GET(DELL_THERMAL_SUPPORTED, buffer.output[1]); + return 0; +} + +static int thermal_get_acc_mode(int *acc_mode) +{ + struct calling_interface_buffer buffer; + int ret; + + dell_fill_request(&buffer, 0x0, 0, 0, 0); + ret = dell_send_request(&buffer, CLASS_INFO, SELECT_THERMAL_MANAGEMENT); + if (ret) + return ret; + *acc_mode = FIELD_GET(DELL_ACC_GET_FIELD, buffer.output[3]); + return 0; +} + +static int thermal_set_mode(enum thermal_mode_bits state) +{ + struct calling_interface_buffer buffer; + int ret; + int acc_mode; + + ret = thermal_get_acc_mode(&acc_mode); + if (ret) + return ret; + + dell_fill_request(&buffer, 0x1, FIELD_PREP(DELL_ACC_SET_FIELD, acc_mode) | state, 0, 0); + return dell_send_request(&buffer, CLASS_INFO, SELECT_THERMAL_MANAGEMENT); +} + +static int thermal_platform_profile_set(struct platform_profile_handler *pprof, + enum platform_profile_option profile) +{ + switch (profile) { + case PLATFORM_PROFILE_BALANCED: + return thermal_set_mode(DELL_BALANCED); + case PLATFORM_PROFILE_PERFORMANCE: + return thermal_set_mode(DELL_PERFORMANCE); + case PLATFORM_PROFILE_QUIET: + return thermal_set_mode(DELL_QUIET); + case PLATFORM_PROFILE_COOL: + return thermal_set_mode(DELL_COOL_BOTTOM); + default: + return -EOPNOTSUPP; + } +} + +static int thermal_platform_profile_get(struct platform_profile_handler *pprof, + enum platform_profile_option *profile) +{ + int ret; + + ret = thermal_get_mode(); + if (ret < 0) + return ret; + + switch (ret) { + case DELL_BALANCED: + *profile = PLATFORM_PROFILE_BALANCED; + break; + case DELL_PERFORMANCE: + *profile = PLATFORM_PROFILE_PERFORMANCE; + break; + case DELL_COOL_BOTTOM: + *profile = PLATFORM_PROFILE_COOL; + break; + case DELL_QUIET: + *profile = PLATFORM_PROFILE_QUIET; + break; + default: + return -EINVAL; + } + + return 0; +} + +static int thermal_init(void) +{ + int ret; + int supported_modes; + + /* If thermal commands are not supported, exit without error */ + if (!dell_smbios_class_is_supported(CLASS_INFO)) + return 0; + + /* If thermal modes are not supported, exit without error */ + ret = thermal_get_supported_modes(&supported_modes); + if (ret < 0) + return ret; + if (!supported_modes) + return 0; + + thermal_handler = kzalloc(sizeof(*thermal_handler), GFP_KERNEL); + if (!thermal_handler) + return -ENOMEM; + thermal_handler->profile_get = thermal_platform_profile_get; + thermal_handler->profile_set = thermal_platform_profile_set; + + if (supported_modes & DELL_QUIET) + set_bit(PLATFORM_PROFILE_QUIET, thermal_handler->choices); + if (supported_modes & DELL_COOL_BOTTOM) + set_bit(PLATFORM_PROFILE_COOL, thermal_handler->choices); + if (supported_modes & DELL_BALANCED) + set_bit(PLATFORM_PROFILE_BALANCED, thermal_handler->choices); + if (supported_modes & DELL_PERFORMANCE) + set_bit(PLATFORM_PROFILE_PERFORMANCE, thermal_handler->choices); + + /* Clean up if failed */ + ret = platform_profile_register(thermal_handler); + if (ret) { + kfree(thermal_handler); + thermal_handler = NULL; + } + + return ret; +} + +static void thermal_cleanup(void) +{ + if (thermal_handler) { + platform_profile_remove(); + kfree(thermal_handler); + } +} + +static int __init dell_init(void) +{ + int ret; + + if (!dmi_check_system(dell_device_table)) + return -ENODEV; + + /* Do not fail module if thermal modes not supported, just skip */ + ret = thermal_init(); + if (ret) + goto fail_thermal; + + return 0; + +fail_thermal: + thermal_cleanup(); + return ret; +} + +static void __exit dell_exit(void) +{ + thermal_cleanup(); +} + +module_init(dell_init); +module_exit(dell_exit); + +MODULE_AUTHOR("Lyndon Sanche <lsanche@lyndeno.ca>"); +MODULE_DESCRIPTION("Dell PC driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/platform/x86/dell/dell-smbios-base.c b/drivers/platform/x86/dell/dell-smbios-base.c index b562ed99ec4e..6565fac24cde 100644 --- a/drivers/platform/x86/dell/dell-smbios-base.c +++ b/drivers/platform/x86/dell/dell-smbios-base.c @@ -77,6 +77,7 @@ static struct smbios_call call_blacklist[] = { /* handled by kernel: dell-laptop */ {0x0000, CLASS_INFO, SELECT_RFKILL}, {0x0000, CLASS_KBD_BACKLIGHT, SELECT_KBD_BACKLIGHT}, + {0x0000, CLASS_INFO, SELECT_THERMAL_MANAGEMENT}, }; struct token_range { @@ -320,6 +321,31 @@ out_smbios_call: } EXPORT_SYMBOL_GPL(dell_smbios_call); +void dell_fill_request(struct calling_interface_buffer *buffer, + u32 arg0, u32 arg1, u32 arg2, u32 arg3) +{ + memset(buffer, 0, sizeof(struct calling_interface_buffer)); + buffer->input[0] = arg0; + buffer->input[1] = arg1; + buffer->input[2] = arg2; + buffer->input[3] = arg3; +} +EXPORT_SYMBOL_GPL(dell_fill_request); + +int dell_send_request(struct calling_interface_buffer *buffer, + u16 class, u16 select) +{ + int ret; + + buffer->cmd_class = class; + buffer->cmd_select = select; + ret = dell_smbios_call(buffer); + if (ret != 0) + return ret; + return dell_smbios_error(buffer->output[0]); +} +EXPORT_SYMBOL_GPL(dell_send_request); + struct calling_interface_token *dell_smbios_find_token(int tokenid) { int i; @@ -356,6 +382,15 @@ void dell_laptop_call_notifier(unsigned long action, void *data) } EXPORT_SYMBOL_GPL(dell_laptop_call_notifier); +bool dell_smbios_class_is_supported(u16 class) +{ + /* Classes over 30 always unsupported */ + if (class > 30) + return false; + return da_supported_commands & (1 << class); +} +EXPORT_SYMBOL_GPL(dell_smbios_class_is_supported); + static void __init parse_da_table(const struct dmi_header *dm) { /* Final token is a terminator, so we don't want to copy it */ diff --git a/drivers/platform/x86/dell/dell-smbios.h b/drivers/platform/x86/dell/dell-smbios.h index eb341bf000c6..ea0cc38642a2 100644 --- a/drivers/platform/x86/dell/dell-smbios.h +++ b/drivers/platform/x86/dell/dell-smbios.h @@ -19,6 +19,7 @@ /* Classes and selects used only in kernel drivers */ #define CLASS_KBD_BACKLIGHT 4 #define SELECT_KBD_BACKLIGHT 11 +#define SELECT_THERMAL_MANAGEMENT 19 /* Tokens used in kernel drivers, any of these * should be filtered from userspace access @@ -64,6 +65,11 @@ int dell_smbios_call_filter(struct device *d, struct calling_interface_buffer *buffer); int dell_smbios_call(struct calling_interface_buffer *buffer); +void dell_fill_request(struct calling_interface_buffer *buffer, + u32 arg0, u32 arg1, u32 arg2, u32 arg3); +int dell_send_request(struct calling_interface_buffer *buffer, + u16 class, u16 select); + struct calling_interface_token *dell_smbios_find_token(int tokenid); enum dell_laptop_notifier_actions { @@ -73,6 +79,7 @@ enum dell_laptop_notifier_actions { int dell_laptop_register_notifier(struct notifier_block *nb); int dell_laptop_unregister_notifier(struct notifier_block *nb); void dell_laptop_call_notifier(unsigned long action, void *data); +bool dell_smbios_class_is_supported(u16 class); /* for the supported backends */ #ifdef CONFIG_DELL_SMBIOS_WMI diff --git a/drivers/platform/x86/hp/Kconfig b/drivers/platform/x86/hp/Kconfig index 7fef4f12e498..d776761cd5fd 100644 --- a/drivers/platform/x86/hp/Kconfig +++ b/drivers/platform/x86/hp/Kconfig @@ -40,6 +40,7 @@ config HP_WMI depends on ACPI_WMI depends on INPUT depends on RFKILL || RFKILL = n + select POWER_SUPPLY select INPUT_SPARSEKMAP select ACPI_PLATFORM_PROFILE select HWMON diff --git a/drivers/platform/x86/hp/hp-bioscfg/enum-attributes.c b/drivers/platform/x86/hp/hp-bioscfg/enum-attributes.c index a2402d31c146..c50ad5880503 100644 --- a/drivers/platform/x86/hp/hp-bioscfg/enum-attributes.c +++ b/drivers/platform/x86/hp/hp-bioscfg/enum-attributes.c @@ -52,9 +52,7 @@ static void update_enumeration_value(int instance_id, char *attr_value) { struct enumeration_data *enum_data = &bioscfg_drv.enumeration_data[instance_id]; - strscpy(enum_data->current_value, - attr_value, - sizeof(enum_data->current_value)); + strscpy(enum_data->current_value, attr_value); } ATTRIBUTE_S_COMMON_PROPERTY_SHOW(display_name, enumeration); @@ -174,8 +172,7 @@ static int hp_populate_enumeration_elements_from_package(union acpi_object *enum case VALUE: break; case PATH: - strscpy(enum_data->common.path, str_value, - sizeof(enum_data->common.path)); + strscpy(enum_data->common.path, str_value); break; case IS_READONLY: enum_data->common.is_readonly = int_value; @@ -222,9 +219,7 @@ static int hp_populate_enumeration_elements_from_package(union acpi_object *enum if (ret) return -EINVAL; - strscpy(enum_data->common.prerequisites[reqs], - str_value, - sizeof(enum_data->common.prerequisites[reqs])); + strscpy(enum_data->common.prerequisites[reqs], str_value); kfree(str_value); str_value = NULL; @@ -236,8 +231,7 @@ static int hp_populate_enumeration_elements_from_package(union acpi_object *enum break; case ENUM_CURRENT_VALUE: - strscpy(enum_data->current_value, - str_value, sizeof(enum_data->current_value)); + strscpy(enum_data->current_value, str_value); break; case ENUM_SIZE: if (int_value > MAX_VALUES_SIZE) { @@ -278,9 +272,7 @@ static int hp_populate_enumeration_elements_from_package(union acpi_object *enum * is greater than MAX_VALUES_SIZE */ if (size < MAX_VALUES_SIZE) - strscpy(enum_data->possible_values[pos_values], - str_value, - sizeof(enum_data->possible_values[pos_values])); + strscpy(enum_data->possible_values[pos_values], str_value); kfree(str_value); str_value = NULL; diff --git a/drivers/platform/x86/hp/hp-bioscfg/int-attributes.c b/drivers/platform/x86/hp/hp-bioscfg/int-attributes.c index 86b7ac63fec2..6c7f4d5fa9cb 100644 --- a/drivers/platform/x86/hp/hp-bioscfg/int-attributes.c +++ b/drivers/platform/x86/hp/hp-bioscfg/int-attributes.c @@ -192,8 +192,7 @@ static int hp_populate_integer_elements_from_package(union acpi_object *integer_ integer_data->current_value = int_value; break; case PATH: - strscpy(integer_data->common.path, str_value, - sizeof(integer_data->common.path)); + strscpy(integer_data->common.path, str_value); break; case IS_READONLY: integer_data->common.is_readonly = int_value; @@ -240,9 +239,7 @@ static int hp_populate_integer_elements_from_package(union acpi_object *integer_ if (ret) continue; - strscpy(integer_data->common.prerequisites[reqs], - str_value, - sizeof(integer_data->common.prerequisites[reqs])); + strscpy(integer_data->common.prerequisites[reqs], str_value); kfree(str_value); str_value = NULL; } diff --git a/drivers/platform/x86/hp/hp-bioscfg/order-list-attributes.c b/drivers/platform/x86/hp/hp-bioscfg/order-list-attributes.c index 1ff09dfb7d7e..c6e57bb9d8b7 100644 --- a/drivers/platform/x86/hp/hp-bioscfg/order-list-attributes.c +++ b/drivers/platform/x86/hp/hp-bioscfg/order-list-attributes.c @@ -57,9 +57,7 @@ static void update_ordered_list_value(int instance, char *attr_value) { struct ordered_list_data *ordered_list_data = &bioscfg_drv.ordered_list_data[instance]; - strscpy(ordered_list_data->current_value, - attr_value, - sizeof(ordered_list_data->current_value)); + strscpy(ordered_list_data->current_value, attr_value); } ATTRIBUTE_S_COMMON_PROPERTY_SHOW(display_name, ordered_list); @@ -179,13 +177,11 @@ static int hp_populate_ordered_list_elements_from_package(union acpi_object *ord /* Assign appropriate element value to corresponding field*/ switch (eloc) { case VALUE: - strscpy(ordered_list_data->current_value, - str_value, sizeof(ordered_list_data->current_value)); + strscpy(ordered_list_data->current_value, str_value); replace_char_str(ordered_list_data->current_value, COMMA_SEP, SEMICOLON_SEP); break; case PATH: - strscpy(ordered_list_data->common.path, str_value, - sizeof(ordered_list_data->common.path)); + strscpy(ordered_list_data->common.path, str_value); break; case IS_READONLY: ordered_list_data->common.is_readonly = int_value; @@ -227,9 +223,7 @@ static int hp_populate_ordered_list_elements_from_package(union acpi_object *ord if (ret) continue; - strscpy(ordered_list_data->common.prerequisites[reqs], - str_value, - sizeof(ordered_list_data->common.prerequisites[reqs])); + strscpy(ordered_list_data->common.prerequisites[reqs], str_value); kfree(str_value); str_value = NULL; @@ -271,9 +265,7 @@ static int hp_populate_ordered_list_elements_from_package(union acpi_object *ord part = strsep(&part_tmp, COMMA_SEP); for (olist_elem = 0; olist_elem < MAX_ELEMENTS_SIZE && part; olist_elem++) { - strscpy(ordered_list_data->elements[olist_elem], - part, - sizeof(ordered_list_data->elements[olist_elem])); + strscpy(ordered_list_data->elements[olist_elem], part); part = strsep(&part_tmp, COMMA_SEP); } ordered_list_data->elements_size = olist_elem; diff --git a/drivers/platform/x86/hp/hp-bioscfg/passwdobj-attributes.c b/drivers/platform/x86/hp/hp-bioscfg/passwdobj-attributes.c index f7efe217a4bb..35936c05e45b 100644 --- a/drivers/platform/x86/hp/hp-bioscfg/passwdobj-attributes.c +++ b/drivers/platform/x86/hp/hp-bioscfg/passwdobj-attributes.c @@ -101,13 +101,9 @@ static int store_password_instance(struct kobject *kobj, const char *buf, if (!ret) { if (is_current) - strscpy(bioscfg_drv.password_data[id].current_password, - buf_cp, - sizeof(bioscfg_drv.password_data[id].current_password)); + strscpy(bioscfg_drv.password_data[id].current_password, buf_cp); else - strscpy(bioscfg_drv.password_data[id].new_password, - buf_cp, - sizeof(bioscfg_drv.password_data[id].new_password)); + strscpy(bioscfg_drv.password_data[id].new_password, buf_cp); } kfree(buf_cp); @@ -272,8 +268,7 @@ static int hp_populate_password_elements_from_package(union acpi_object *passwor case VALUE: break; case PATH: - strscpy(password_data->common.path, str_value, - sizeof(password_data->common.path)); + strscpy(password_data->common.path, str_value); break; case IS_READONLY: password_data->common.is_readonly = int_value; @@ -315,9 +310,7 @@ static int hp_populate_password_elements_from_package(union acpi_object *passwor if (ret) break; - strscpy(password_data->common.prerequisites[reqs], - str_value, - sizeof(password_data->common.prerequisites[reqs])); + strscpy(password_data->common.prerequisites[reqs], str_value); kfree(str_value); str_value = NULL; @@ -359,9 +352,7 @@ static int hp_populate_password_elements_from_package(union acpi_object *passwor if (ret) break; - strscpy(password_data->encodings[pos_values], - str_value, - sizeof(password_data->encodings[pos_values])); + strscpy(password_data->encodings[pos_values], str_value); kfree(str_value); str_value = NULL; diff --git a/drivers/platform/x86/hp/hp-bioscfg/spmobj-attributes.c b/drivers/platform/x86/hp/hp-bioscfg/spmobj-attributes.c index 86f90238750c..2b00a14792e9 100644 --- a/drivers/platform/x86/hp/hp-bioscfg/spmobj-attributes.c +++ b/drivers/platform/x86/hp/hp-bioscfg/spmobj-attributes.c @@ -365,8 +365,7 @@ int hp_populate_secure_platform_data(struct kobject *attr_name_kobj) /* Populate data for Secure Platform Management */ bioscfg_drv.spm_data.attr_name_kobj = attr_name_kobj; - strscpy(bioscfg_drv.spm_data.attribute_name, SPM_STR, - sizeof(bioscfg_drv.spm_data.attribute_name)); + strscpy(bioscfg_drv.spm_data.attribute_name, SPM_STR); bioscfg_drv.spm_data.is_enabled = 0; bioscfg_drv.spm_data.mechanism = 0; diff --git a/drivers/platform/x86/hp/hp-bioscfg/string-attributes.c b/drivers/platform/x86/hp/hp-bioscfg/string-attributes.c index f0c20070094d..27758b779b2d 100644 --- a/drivers/platform/x86/hp/hp-bioscfg/string-attributes.c +++ b/drivers/platform/x86/hp/hp-bioscfg/string-attributes.c @@ -50,7 +50,7 @@ static void update_string_value(int instance_id, char *attr_value) struct string_data *string_data = &bioscfg_drv.string_data[instance_id]; /* Write settings to BIOS */ - strscpy(string_data->current_value, attr_value, sizeof(string_data->current_value)); + strscpy(string_data->current_value, attr_value); } /* @@ -178,12 +178,10 @@ static int hp_populate_string_elements_from_package(union acpi_object *string_ob /* Assign appropriate element value to corresponding field*/ switch (eloc) { case VALUE: - strscpy(string_data->current_value, - str_value, sizeof(string_data->current_value)); + strscpy(string_data->current_value, str_value); break; case PATH: - strscpy(string_data->common.path, str_value, - sizeof(string_data->common.path)); + strscpy(string_data->common.path, str_value); break; case IS_READONLY: string_data->common.is_readonly = int_value; @@ -231,9 +229,7 @@ static int hp_populate_string_elements_from_package(union acpi_object *string_ob if (ret) continue; - strscpy(string_data->common.prerequisites[reqs], - str_value, - sizeof(string_data->common.prerequisites[reqs])); + strscpy(string_data->common.prerequisites[reqs], str_value); kfree(str_value); str_value = NULL; } diff --git a/drivers/platform/x86/hp/hp-wmi.c b/drivers/platform/x86/hp/hp-wmi.c index 5fa553023842..876e0a97cee1 100644 --- a/drivers/platform/x86/hp/hp-wmi.c +++ b/drivers/platform/x86/hp/hp-wmi.c @@ -24,6 +24,9 @@ #include <linux/platform_profile.h> #include <linux/hwmon.h> #include <linux/acpi.h> +#include <linux/mutex.h> +#include <linux/cleanup.h> +#include <linux/power_supply.h> #include <linux/rfkill.h> #include <linux/string.h> #include <linux/dmi.h> @@ -42,6 +45,8 @@ MODULE_ALIAS("wmi:5FB7F034-2C63-45E9-BE91-3D44E2C707E4"); #define HP_OMEN_EC_THERMAL_PROFILE_TIMER_OFFSET 0x63 #define HP_OMEN_EC_THERMAL_PROFILE_OFFSET 0x95 +#define ACPI_AC_CLASS "ac_adapter" + #define zero_if_sup(tmp) (zero_insize_support?0:sizeof(tmp)) // use when zero insize is required /* DMI board names of devices that should use the omen specific path for @@ -259,10 +264,18 @@ static const struct key_entry hp_wmi_keymap[] = { { KE_END, 0 } }; +/* + * Mutex for the active_platform_profile variable, + * see omen_powersource_event. + */ +static DEFINE_MUTEX(active_platform_profile_lock); + static struct input_dev *hp_wmi_input_dev; static struct input_dev *camera_shutter_input_dev; static struct platform_device *hp_wmi_platform_dev; static struct platform_profile_handler platform_profile_handler; +static struct notifier_block platform_power_source_nb; +static enum platform_profile_option active_platform_profile; static bool platform_profile_support; static bool zero_insize_support; @@ -1194,8 +1207,7 @@ fail: return err; } -static int platform_profile_omen_get(struct platform_profile_handler *pprof, - enum platform_profile_option *profile) +static int platform_profile_omen_get_ec(enum platform_profile_option *profile) { int tp; @@ -1223,6 +1235,27 @@ static int platform_profile_omen_get(struct platform_profile_handler *pprof, return 0; } +static int platform_profile_omen_get(struct platform_profile_handler *pprof, + enum platform_profile_option *profile) +{ + /* + * We directly return the stored platform profile, as the embedded + * controller will not accept switching to the performance option when + * the conditions are not met (e.g. the laptop is not plugged in). + * + * If we directly return what the EC reports, the platform profile will + * immediately "switch back" to normal mode, which is against the + * expected behaviour from a userspace point of view, as described in + * the Platform Profile Section page of the kernel documentation. + * + * See also omen_powersource_event. + */ + guard(mutex)(&active_platform_profile_lock); + *profile = active_platform_profile; + + return 0; +} + static bool has_omen_thermal_profile_ec_timer(void) { const char *board_name = dmi_get_system_info(DMI_BOARD_NAME); @@ -1245,8 +1278,7 @@ inline int omen_thermal_profile_ec_timer_set(u8 value) return ec_write(HP_OMEN_EC_THERMAL_PROFILE_TIMER_OFFSET, value); } -static int platform_profile_omen_set(struct platform_profile_handler *pprof, - enum platform_profile_option profile) +static int platform_profile_omen_set_ec(enum platform_profile_option profile) { int err, tp, tp_version; enum hp_thermal_profile_omen_flags flags = 0; @@ -1300,6 +1332,22 @@ static int platform_profile_omen_set(struct platform_profile_handler *pprof, return 0; } +static int platform_profile_omen_set(struct platform_profile_handler *pprof, + enum platform_profile_option profile) +{ + int err; + + guard(mutex)(&active_platform_profile_lock); + + err = platform_profile_omen_set_ec(profile); + if (err < 0) + return err; + + active_platform_profile = profile; + + return 0; +} + static int thermal_profile_get(void) { return hp_wmi_read_int(HPWMI_THERMAL_PROFILE_QUERY); @@ -1381,8 +1429,7 @@ static bool is_victus_thermal_profile(void) board_name) >= 0; } -static int platform_profile_victus_get(struct platform_profile_handler *pprof, - enum platform_profile_option *profile) +static int platform_profile_victus_get_ec(enum platform_profile_option *profile) { int tp; @@ -1407,8 +1454,14 @@ static int platform_profile_victus_get(struct platform_profile_handler *pprof, return 0; } -static int platform_profile_victus_set(struct platform_profile_handler *pprof, - enum platform_profile_option profile) +static int platform_profile_victus_get(struct platform_profile_handler *pprof, + enum platform_profile_option *profile) +{ + /* Same behaviour as platform_profile_omen_get */ + return platform_profile_omen_get(pprof, profile); +} + +static int platform_profile_victus_set_ec(enum platform_profile_option profile) { int err, tp; @@ -1433,21 +1486,113 @@ static int platform_profile_victus_set(struct platform_profile_handler *pprof, return 0; } +static int platform_profile_victus_set(struct platform_profile_handler *pprof, + enum platform_profile_option profile) +{ + int err; + + guard(mutex)(&active_platform_profile_lock); + + err = platform_profile_victus_set_ec(profile); + if (err < 0) + return err; + + active_platform_profile = profile; + + return 0; +} + +static int omen_powersource_event(struct notifier_block *nb, + unsigned long value, + void *data) +{ + struct acpi_bus_event *event_entry = data; + enum platform_profile_option actual_profile; + int err; + + if (strcmp(event_entry->device_class, ACPI_AC_CLASS) != 0) + return NOTIFY_DONE; + + pr_debug("Received power source device event\n"); + + guard(mutex)(&active_platform_profile_lock); + + /* + * This handler can only be called on Omen and Victus models, so + * there's no need to call is_victus_thermal_profile() here. + */ + if (is_omen_thermal_profile()) + err = platform_profile_omen_get_ec(&actual_profile); + else + err = platform_profile_victus_get_ec(&actual_profile); + + if (err < 0) { + /* + * Although we failed to get the current platform profile, we + * still want the other event consumers to process it. + */ + pr_warn("Failed to read current platform profile (%d)\n", err); + return NOTIFY_DONE; + } + + /* + * If we're back on AC and that the user-chosen power profile is + * different from what the EC reports, we restore the user-chosen + * one. + */ + if (power_supply_is_system_supplied() <= 0 || + active_platform_profile == actual_profile) { + pr_debug("Platform profile update skipped, conditions unmet\n"); + return NOTIFY_DONE; + } + + if (is_omen_thermal_profile()) + err = platform_profile_omen_set_ec(active_platform_profile); + else + err = platform_profile_victus_set_ec(active_platform_profile); + + if (err < 0) { + pr_warn("Failed to restore platform profile (%d)\n", err); + return NOTIFY_DONE; + } + + return NOTIFY_OK; +} + +static int omen_register_powersource_event_handler(void) +{ + int err; + + platform_power_source_nb.notifier_call = omen_powersource_event; + err = register_acpi_notifier(&platform_power_source_nb); + + if (err < 0) { + pr_warn("Failed to install ACPI power source notify handler\n"); + return err; + } + + return 0; +} + +static inline void omen_unregister_powersource_event_handler(void) +{ + unregister_acpi_notifier(&platform_power_source_nb); +} + static int thermal_profile_setup(void) { int err, tp; if (is_omen_thermal_profile()) { - tp = omen_thermal_profile_get(); - if (tp < 0) - return tp; + err = platform_profile_omen_get_ec(&active_platform_profile); + if (err < 0) + return err; /* * call thermal profile write command to ensure that the * firmware correctly sets the OEM variables */ - - err = omen_thermal_profile_set(tp); + err = platform_profile_omen_set_ec(active_platform_profile); if (err < 0) return err; @@ -1456,15 +1601,15 @@ static int thermal_profile_setup(void) set_bit(PLATFORM_PROFILE_COOL, platform_profile_handler.choices); } else if (is_victus_thermal_profile()) { - tp = omen_thermal_profile_get(); - if (tp < 0) - return tp; + err = platform_profile_victus_get_ec(&active_platform_profile); + if (err < 0) + return err; /* * call thermal profile write command to ensure that the * firmware correctly sets the OEM variables */ - err = omen_thermal_profile_set(tp); + err = platform_profile_victus_set_ec(active_platform_profile); if (err < 0) return err; @@ -1758,6 +1903,12 @@ static int __init hp_wmi_init(void) goto err_unregister_device; } + if (is_omen_thermal_profile() || is_victus_thermal_profile()) { + err = omen_register_powersource_event_handler(); + if (err) + goto err_unregister_device; + } + return 0; err_unregister_device: @@ -1772,6 +1923,9 @@ module_init(hp_wmi_init); static void __exit hp_wmi_exit(void) { + if (is_omen_thermal_profile() || is_victus_thermal_profile()) + omen_unregister_powersource_event_handler(); + if (wmi_has_guid(HPWMI_EVENT_GUID)) hp_wmi_input_destroy(); diff --git a/drivers/platform/x86/ideapad-laptop.c b/drivers/platform/x86/ideapad-laptop.c index fcf13d88fd6e..1ace711f7442 100644 --- a/drivers/platform/x86/ideapad-laptop.c +++ b/drivers/platform/x86/ideapad-laptop.c @@ -13,6 +13,7 @@ #include <linux/bitfield.h> #include <linux/bitops.h> #include <linux/bug.h> +#include <linux/cleanup.h> #include <linux/debugfs.h> #include <linux/device.h> #include <linux/dmi.h> @@ -204,7 +205,7 @@ static int ideapad_shared_init(struct ideapad_private *priv) { int ret; - mutex_lock(&ideapad_shared_mutex); + guard(mutex)(&ideapad_shared_mutex); if (!ideapad_shared) { ideapad_shared = priv; @@ -214,19 +215,15 @@ static int ideapad_shared_init(struct ideapad_private *priv) ret = -EINVAL; } - mutex_unlock(&ideapad_shared_mutex); - return ret; } static void ideapad_shared_exit(struct ideapad_private *priv) { - mutex_lock(&ideapad_shared_mutex); + guard(mutex)(&ideapad_shared_mutex); if (ideapad_shared == priv) ideapad_shared = NULL; - - mutex_unlock(&ideapad_shared_mutex); } /* @@ -840,36 +837,33 @@ static int dytc_profile_set(struct platform_profile_handler *pprof, unsigned long output; int err; - err = mutex_lock_interruptible(&dytc->mutex); - if (err) - return err; - - if (profile == PLATFORM_PROFILE_BALANCED) { - /* To get back to balanced mode we just issue a reset command */ - err = eval_dytc(priv->adev->handle, DYTC_CMD_RESET, NULL); - if (err) - goto unlock; - } else { - int perfmode; - - err = convert_profile_to_dytc(profile, &perfmode); - if (err) - goto unlock; + scoped_guard(mutex_intr, &dytc->mutex) { + if (profile == PLATFORM_PROFILE_BALANCED) { + /* To get back to balanced mode we just issue a reset command */ + err = eval_dytc(priv->adev->handle, DYTC_CMD_RESET, NULL); + if (err) + return err; + } else { + int perfmode; + + err = convert_profile_to_dytc(profile, &perfmode); + if (err) + return err; + + /* Determine if we are in CQL mode. This alters the commands we do */ + err = dytc_cql_command(priv, + DYTC_SET_COMMAND(DYTC_FUNCTION_MMC, perfmode, 1), + &output); + if (err) + return err; + } - /* Determine if we are in CQL mode. This alters the commands we do */ - err = dytc_cql_command(priv, DYTC_SET_COMMAND(DYTC_FUNCTION_MMC, perfmode, 1), - &output); - if (err) - goto unlock; + /* Success - update current profile */ + dytc->current_profile = profile; + return 0; } - /* Success - update current profile */ - dytc->current_profile = profile; - -unlock: - mutex_unlock(&dytc->mutex); - - return err; + return -EINTR; } static void dytc_profile_refresh(struct ideapad_private *priv) @@ -878,9 +872,8 @@ static void dytc_profile_refresh(struct ideapad_private *priv) unsigned long output; int err, perfmode; - mutex_lock(&priv->dytc->mutex); - err = dytc_cql_command(priv, DYTC_CMD_GET, &output); - mutex_unlock(&priv->dytc->mutex); + scoped_guard(mutex, &priv->dytc->mutex) + err = dytc_cql_command(priv, DYTC_CMD_GET, &output); if (err) return; @@ -1809,11 +1802,11 @@ static void ideapad_wmi_notify(struct wmi_device *wdev, union acpi_object *data) struct ideapad_wmi_private *wpriv = dev_get_drvdata(&wdev->dev); struct ideapad_private *priv; - mutex_lock(&ideapad_shared_mutex); + guard(mutex)(&ideapad_shared_mutex); priv = ideapad_shared; if (!priv) - goto unlock; + return; switch (wpriv->event) { case IDEAPAD_WMI_EVENT_ESC: @@ -1847,8 +1840,6 @@ static void ideapad_wmi_notify(struct wmi_device *wdev, union acpi_object *data) break; } -unlock: - mutex_unlock(&ideapad_shared_mutex); } static const struct ideapad_wmi_private ideapad_wmi_context_esc = { diff --git a/drivers/platform/x86/intel/Kconfig b/drivers/platform/x86/intel/Kconfig index e9dc0c021029..ad50bbabec61 100644 --- a/drivers/platform/x86/intel/Kconfig +++ b/drivers/platform/x86/intel/Kconfig @@ -192,10 +192,14 @@ config INTEL_SMARTCONNECT This driver checks to determine whether the device has Intel Smart Connect enabled, and if so disables it. +config INTEL_TPMI_POWER_DOMAINS + tristate + config INTEL_TPMI tristate "Intel Topology Aware Register and PM Capsule Interface (TPMI)" depends on INTEL_VSEC depends on X86_64 + select INTEL_TPMI_POWER_DOMAINS help The Intel Topology Aware Register and PM Capsule Interface (TPMI), provides enumerable MMIO interface for power management features. @@ -205,6 +209,13 @@ config INTEL_TPMI To compile this driver as a module, choose M here: the module will be called intel_vsec_tpmi. +config INTEL_PLR_TPMI + tristate "Intel SoC TPMI Power Limit Reasons driver" + depends on INTEL_TPMI + help + This driver provides the TPMI power limit reasons status information + via debugfs files. + config INTEL_TURBO_MAX_3 bool "Intel Turbo Boost Max Technology 3.0 enumeration driver" depends on X86_64 && SCHED_MC_PRIO diff --git a/drivers/platform/x86/intel/Makefile b/drivers/platform/x86/intel/Makefile index c1d5fe05e3f3..74db065c82d6 100644 --- a/drivers/platform/x86/intel/Makefile +++ b/drivers/platform/x86/intel/Makefile @@ -52,6 +52,10 @@ obj-$(CONFIG_INTEL_PUNIT_IPC) += intel_punit_ipc.o # TPMI drivers intel_vsec_tpmi-y := tpmi.o obj-$(CONFIG_INTEL_TPMI) += intel_vsec_tpmi.o +obj-$(CONFIG_INTEL_PLR_TPMI) += intel_plr_tpmi.o + +intel_tpmi_power_domains-y := tpmi_power_domains.o +obj-$(CONFIG_INTEL_TPMI_POWER_DOMAINS) += intel_tpmi_power_domains.o # Intel Uncore drivers intel-rst-y := rst.o diff --git a/drivers/platform/x86/intel/chtwc_int33fe.c b/drivers/platform/x86/intel/chtwc_int33fe.c index 93f75ba1dafd..11503b1c85f3 100644 --- a/drivers/platform/x86/intel/chtwc_int33fe.c +++ b/drivers/platform/x86/intel/chtwc_int33fe.c @@ -270,7 +270,7 @@ cht_int33fe_register_max17047(struct device *dev, struct cht_int33fe_data *data) } memset(&board_info, 0, sizeof(board_info)); - strscpy(board_info.type, "max17047", I2C_NAME_SIZE); + strscpy(board_info.type, "max17047"); board_info.dev_name = "max17047"; board_info.fwnode = fwnode; data->battery_fg = i2c_acpi_new_device(dev, 1, &board_info); @@ -361,7 +361,7 @@ static int cht_int33fe_typec_probe(struct platform_device *pdev) } memset(&board_info, 0, sizeof(board_info)); - strscpy(board_info.type, "typec_fusb302", I2C_NAME_SIZE); + strscpy(board_info.type, "typec_fusb302"); board_info.dev_name = "fusb302"; board_info.fwnode = fwnode; board_info.irq = fusb302_irq; @@ -381,7 +381,7 @@ static int cht_int33fe_typec_probe(struct platform_device *pdev) memset(&board_info, 0, sizeof(board_info)); board_info.dev_name = "pi3usb30532"; board_info.fwnode = fwnode; - strscpy(board_info.type, "pi3usb30532", I2C_NAME_SIZE); + strscpy(board_info.type, "pi3usb30532"); data->pi3usb30532 = i2c_acpi_new_device(dev, 3, &board_info); if (IS_ERR(data->pi3usb30532)) { diff --git a/drivers/platform/x86/intel/ifs/core.c b/drivers/platform/x86/intel/ifs/core.c index 7b11198d85a1..33412a584836 100644 --- a/drivers/platform/x86/intel/ifs/core.c +++ b/drivers/platform/x86/intel/ifs/core.c @@ -11,16 +11,15 @@ #include "ifs.h" -#define X86_MATCH(model, array_gen) \ - X86_MATCH_VENDOR_FAM_MODEL_FEATURE(INTEL, 6, \ - INTEL_FAM6_##model, X86_FEATURE_CORE_CAPABILITIES, array_gen) +#define X86_MATCH(vfm, array_gen) \ + X86_MATCH_VFM_FEATURE(vfm, X86_FEATURE_CORE_CAPABILITIES, array_gen) static const struct x86_cpu_id ifs_cpu_ids[] __initconst = { - X86_MATCH(SAPPHIRERAPIDS_X, ARRAY_GEN0), - X86_MATCH(EMERALDRAPIDS_X, ARRAY_GEN0), - X86_MATCH(GRANITERAPIDS_X, ARRAY_GEN0), - X86_MATCH(GRANITERAPIDS_D, ARRAY_GEN0), - X86_MATCH(ATOM_CRESTMONT_X, ARRAY_GEN1), + X86_MATCH(INTEL_SAPPHIRERAPIDS_X, ARRAY_GEN0), + X86_MATCH(INTEL_EMERALDRAPIDS_X, ARRAY_GEN0), + X86_MATCH(INTEL_GRANITERAPIDS_X, ARRAY_GEN0), + X86_MATCH(INTEL_GRANITERAPIDS_D, ARRAY_GEN0), + X86_MATCH(INTEL_ATOM_CRESTMONT_X, ARRAY_GEN1), {} }; MODULE_DEVICE_TABLE(x86cpu, ifs_cpu_ids); diff --git a/drivers/platform/x86/intel/intel_plr_tpmi.c b/drivers/platform/x86/intel/intel_plr_tpmi.c new file mode 100644 index 000000000000..69ace6a629bc --- /dev/null +++ b/drivers/platform/x86/intel/intel_plr_tpmi.c @@ -0,0 +1,354 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Performance Limit Reasons via TPMI + * + * Copyright (c) 2024, Intel Corporation. + */ + +#include <linux/array_size.h> +#include <linux/auxiliary_bus.h> +#include <linux/bitfield.h> +#include <linux/bitmap.h> +#include <linux/debugfs.h> +#include <linux/device.h> +#include <linux/err.h> +#include <linux/gfp_types.h> +#include <linux/intel_tpmi.h> +#include <linux/io.h> +#include <linux/iopoll.h> +#include <linux/kstrtox.h> +#include <linux/lockdep.h> +#include <linux/module.h> +#include <linux/mod_devicetable.h> +#include <linux/mutex.h> +#include <linux/seq_file.h> +#include <linux/sprintf.h> +#include <linux/types.h> + +#include "tpmi_power_domains.h" + +#define PLR_HEADER 0x00 +#define PLR_MAILBOX_INTERFACE 0x08 +#define PLR_MAILBOX_DATA 0x10 +#define PLR_DIE_LEVEL 0x18 + +#define PLR_MODULE_ID_MASK GENMASK_ULL(19, 12) +#define PLR_RUN_BUSY BIT_ULL(63) + +#define PLR_COMMAND_WRITE 1 + +#define PLR_INVALID GENMASK_ULL(63, 0) + +#define PLR_TIMEOUT_US 5 +#define PLR_TIMEOUT_MAX_US 1000 + +#define PLR_COARSE_REASON_BITS 32 + +struct tpmi_plr; + +struct tpmi_plr_die { + void __iomem *base; + struct mutex lock; /* Protect access to PLR mailbox */ + int package_id; + int die_id; + struct tpmi_plr *plr; +}; + +struct tpmi_plr { + struct dentry *dbgfs_dir; + struct tpmi_plr_die *die_info; + int num_dies; + struct auxiliary_device *auxdev; +}; + +static const char * const plr_coarse_reasons[] = { + "FREQUENCY", + "CURRENT", + "POWER", + "THERMAL", + "PLATFORM", + "MCP", + "RAS", + "MISC", + "QOS", + "DFC", +}; + +static const char * const plr_fine_reasons[] = { + "FREQUENCY_CDYN0", + "FREQUENCY_CDYN1", + "FREQUENCY_CDYN2", + "FREQUENCY_CDYN3", + "FREQUENCY_CDYN4", + "FREQUENCY_CDYN5", + "FREQUENCY_FCT", + "FREQUENCY_PCS_TRL", + "CURRENT_MTPMAX", + "POWER_FAST_RAPL", + "POWER_PKG_PL1_MSR_TPMI", + "POWER_PKG_PL1_MMIO", + "POWER_PKG_PL1_PCS", + "POWER_PKG_PL2_MSR_TPMI", + "POWER_PKG_PL2_MMIO", + "POWER_PKG_PL2_PCS", + "POWER_PLATFORM_PL1_MSR_TPMI", + "POWER_PLATFORM_PL1_MMIO", + "POWER_PLATFORM_PL1_PCS", + "POWER_PLATFORM_PL2_MSR_TPMI", + "POWER_PLATFORM_PL2_MMIO", + "POWER_PLATFORM_PL2_PCS", + "UNKNOWN(22)", + "THERMAL_PER_CORE", + "DFC_UFS", + "PLATFORM_PROCHOT", + "PLATFORM_HOT_VR", + "UNKNOWN(27)", + "UNKNOWN(28)", + "MISC_PCS_PSTATE", +}; + +static u64 plr_read(struct tpmi_plr_die *plr_die, int offset) +{ + return readq(plr_die->base + offset); +} + +static void plr_write(u64 val, struct tpmi_plr_die *plr_die, int offset) +{ + writeq(val, plr_die->base + offset); +} + +static int plr_read_cpu_status(struct tpmi_plr_die *plr_die, int cpu, + u64 *status) +{ + u64 regval; + int ret; + + lockdep_assert_held(&plr_die->lock); + + regval = FIELD_PREP(PLR_MODULE_ID_MASK, tpmi_get_punit_core_number(cpu)); + regval |= PLR_RUN_BUSY; + + plr_write(regval, plr_die, PLR_MAILBOX_INTERFACE); + + ret = readq_poll_timeout(plr_die->base + PLR_MAILBOX_INTERFACE, regval, + !(regval & PLR_RUN_BUSY), PLR_TIMEOUT_US, + PLR_TIMEOUT_MAX_US); + if (ret) + return ret; + + *status = plr_read(plr_die, PLR_MAILBOX_DATA); + + return 0; +} + +static int plr_clear_cpu_status(struct tpmi_plr_die *plr_die, int cpu) +{ + u64 regval; + + lockdep_assert_held(&plr_die->lock); + + regval = FIELD_PREP(PLR_MODULE_ID_MASK, tpmi_get_punit_core_number(cpu)); + regval |= PLR_RUN_BUSY | PLR_COMMAND_WRITE; + + plr_write(0, plr_die, PLR_MAILBOX_DATA); + + plr_write(regval, plr_die, PLR_MAILBOX_INTERFACE); + + return readq_poll_timeout(plr_die->base + PLR_MAILBOX_INTERFACE, regval, + !(regval & PLR_RUN_BUSY), PLR_TIMEOUT_US, + PLR_TIMEOUT_MAX_US); +} + +static void plr_print_bits(struct seq_file *s, u64 val, int bits) +{ + const unsigned long mask[] = { BITMAP_FROM_U64(val) }; + int bit, index; + + for_each_set_bit(bit, mask, bits) { + const char *str = NULL; + + if (bit < PLR_COARSE_REASON_BITS) { + if (bit < ARRAY_SIZE(plr_coarse_reasons)) + str = plr_coarse_reasons[bit]; + } else { + index = bit - PLR_COARSE_REASON_BITS; + if (index < ARRAY_SIZE(plr_fine_reasons)) + str = plr_fine_reasons[index]; + } + + if (str) + seq_printf(s, " %s", str); + else + seq_printf(s, " UNKNOWN(%d)", bit); + } + + if (!val) + seq_puts(s, " none"); + + seq_putc(s, '\n'); +} + +static int plr_status_show(struct seq_file *s, void *unused) +{ + struct tpmi_plr_die *plr_die = s->private; + int ret; + u64 val; + + val = plr_read(plr_die, PLR_DIE_LEVEL); + seq_puts(s, "cpus"); + plr_print_bits(s, val, 32); + + guard(mutex)(&plr_die->lock); + + for (int cpu = 0; cpu < nr_cpu_ids; cpu++) { + if (plr_die->die_id != tpmi_get_power_domain_id(cpu)) + continue; + + if (plr_die->package_id != topology_physical_package_id(cpu)) + continue; + + seq_printf(s, "cpu%d", cpu); + ret = plr_read_cpu_status(plr_die, cpu, &val); + if (ret) { + dev_err(&plr_die->plr->auxdev->dev, "Failed to read PLR for cpu %d, ret=%d\n", + cpu, ret); + return ret; + } + + plr_print_bits(s, val, 64); + } + + return 0; +} + +static ssize_t plr_status_write(struct file *filp, const char __user *ubuf, + size_t count, loff_t *ppos) +{ + struct seq_file *s = filp->private_data; + struct tpmi_plr_die *plr_die = s->private; + bool val; + int ret; + + ret = kstrtobool_from_user(ubuf, count, &val); + if (ret) + return ret; + + if (val != 0) + return -EINVAL; + + plr_write(0, plr_die, PLR_DIE_LEVEL); + + guard(mutex)(&plr_die->lock); + + for (int cpu = 0; cpu < nr_cpu_ids; cpu++) { + if (plr_die->die_id != tpmi_get_power_domain_id(cpu)) + continue; + + if (plr_die->package_id != topology_physical_package_id(cpu)) + continue; + + plr_clear_cpu_status(plr_die, cpu); + } + + return count; +} +DEFINE_SHOW_STORE_ATTRIBUTE(plr_status); + +static int intel_plr_probe(struct auxiliary_device *auxdev, const struct auxiliary_device_id *id) +{ + struct intel_tpmi_plat_info *plat_info; + struct dentry *dentry; + int i, num_resources; + struct resource *res; + struct tpmi_plr *plr; + void __iomem *base; + char name[16]; + int err; + + plat_info = tpmi_get_platform_data(auxdev); + if (!plat_info) + return dev_err_probe(&auxdev->dev, -EINVAL, "No platform info\n"); + + dentry = tpmi_get_debugfs_dir(auxdev); + if (!dentry) + return dev_err_probe(&auxdev->dev, -ENODEV, "No TPMI debugfs directory.\n"); + + num_resources = tpmi_get_resource_count(auxdev); + if (!num_resources) + return -EINVAL; + + plr = devm_kzalloc(&auxdev->dev, sizeof(*plr), GFP_KERNEL); + if (!plr) + return -ENOMEM; + + plr->die_info = devm_kcalloc(&auxdev->dev, num_resources, sizeof(*plr->die_info), + GFP_KERNEL); + if (!plr->die_info) + return -ENOMEM; + + plr->num_dies = num_resources; + plr->dbgfs_dir = debugfs_create_dir("plr", dentry); + plr->auxdev = auxdev; + + for (i = 0; i < num_resources; i++) { + res = tpmi_get_resource_at_index(auxdev, i); + if (!res) { + err = dev_err_probe(&auxdev->dev, -EINVAL, "No resource\n"); + goto err; + } + + base = devm_ioremap_resource(&auxdev->dev, res); + if (IS_ERR(base)) { + err = PTR_ERR(base); + goto err; + } + + plr->die_info[i].base = base; + plr->die_info[i].package_id = plat_info->package_id; + plr->die_info[i].die_id = i; + plr->die_info[i].plr = plr; + mutex_init(&plr->die_info[i].lock); + + if (plr_read(&plr->die_info[i], PLR_HEADER) == PLR_INVALID) + continue; + + snprintf(name, sizeof(name), "domain%d", i); + + dentry = debugfs_create_dir(name, plr->dbgfs_dir); + debugfs_create_file("status", 0444, dentry, &plr->die_info[i], + &plr_status_fops); + } + + auxiliary_set_drvdata(auxdev, plr); + + return 0; + +err: + debugfs_remove_recursive(plr->dbgfs_dir); + return err; +} + +static void intel_plr_remove(struct auxiliary_device *auxdev) +{ + struct tpmi_plr *plr = auxiliary_get_drvdata(auxdev); + + debugfs_remove_recursive(plr->dbgfs_dir); +} + +static const struct auxiliary_device_id intel_plr_id_table[] = { + { .name = "intel_vsec.tpmi-plr" }, + {} +}; +MODULE_DEVICE_TABLE(auxiliary, intel_plr_id_table); + +static struct auxiliary_driver intel_plr_aux_driver = { + .id_table = intel_plr_id_table, + .remove = intel_plr_remove, + .probe = intel_plr_probe, +}; +module_auxiliary_driver(intel_plr_aux_driver); + +MODULE_IMPORT_NS(INTEL_TPMI); +MODULE_IMPORT_NS(INTEL_TPMI_POWER_DOMAIN); +MODULE_DESCRIPTION("Intel TPMI PLR Driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/platform/x86/intel/pmc/core.c b/drivers/platform/x86/intel/pmc/core.c index 2ad2f8753e5d..01ae71c6df59 100644 --- a/drivers/platform/x86/intel/pmc/core.c +++ b/drivers/platform/x86/intel/pmc/core.c @@ -87,35 +87,26 @@ static int set_etr3(struct pmc_dev *pmcdev) struct pmc *pmc = pmcdev->pmcs[PMC_IDX_MAIN]; const struct pmc_reg_map *map = pmc->map; u32 reg; - int err; if (!map->etr3_offset) return -EOPNOTSUPP; - mutex_lock(&pmcdev->lock); + guard(mutex)(&pmcdev->lock); /* check if CF9 is locked */ reg = pmc_core_reg_read(pmc, map->etr3_offset); - if (reg & ETR3_CF9LOCK) { - err = -EACCES; - goto out_unlock; - } + if (reg & ETR3_CF9LOCK) + return -EACCES; /* write CF9 global reset bit */ reg |= ETR3_CF9GR; pmc_core_reg_write(pmc, map->etr3_offset, reg); reg = pmc_core_reg_read(pmc, map->etr3_offset); - if (!(reg & ETR3_CF9GR)) { - err = -EIO; - goto out_unlock; - } - - err = 0; + if (!(reg & ETR3_CF9GR)) + return -EIO; -out_unlock: - mutex_unlock(&pmcdev->lock); - return err; + return 0; } static umode_t etr3_is_visible(struct kobject *kobj, struct attribute *attr, @@ -127,9 +118,8 @@ static umode_t etr3_is_visible(struct kobject *kobj, const struct pmc_reg_map *map = pmc->map; u32 reg; - mutex_lock(&pmcdev->lock); - reg = pmc_core_reg_read(pmc, map->etr3_offset); - mutex_unlock(&pmcdev->lock); + scoped_guard(mutex, &pmcdev->lock) + reg = pmc_core_reg_read(pmc, map->etr3_offset); return reg & ETR3_CF9LOCK ? attr->mode & (SYSFS_PREALLOC | 0444) : attr->mode; } @@ -145,12 +135,10 @@ static ssize_t etr3_show(struct device *dev, if (!map->etr3_offset) return -EOPNOTSUPP; - mutex_lock(&pmcdev->lock); - - reg = pmc_core_reg_read(pmc, map->etr3_offset); - reg &= ETR3_CF9GR | ETR3_CF9LOCK; - - mutex_unlock(&pmcdev->lock); + scoped_guard(mutex, &pmcdev->lock) { + reg = pmc_core_reg_read(pmc, map->etr3_offset); + reg &= ETR3_CF9GR | ETR3_CF9LOCK; + } return sysfs_emit(buf, "0x%08x", reg); } @@ -257,9 +245,9 @@ static void pmc_core_slps0_display(struct pmc *pmc, struct device *dev, } } -static int pmc_core_lpm_get_arr_size(const struct pmc_bit_map **maps) +static unsigned int pmc_core_lpm_get_arr_size(const struct pmc_bit_map **maps) { - int idx; + unsigned int idx; for (idx = 0; maps[idx]; idx++) ;/* Nothing */ @@ -272,8 +260,8 @@ static void pmc_core_lpm_display(struct pmc *pmc, struct device *dev, const char *str, const struct pmc_bit_map **maps) { - int index, idx, len = 32, bit_mask, arr_size; - u32 *lpm_regs; + unsigned int index, idx, len = 32, arr_size; + u32 bit_mask, *lpm_regs; arr_size = pmc_core_lpm_get_arr_size(maps); lpm_regs = kmalloc_array(arr_size, sizeof(*lpm_regs), GFP_KERNEL); @@ -326,13 +314,13 @@ static void pmc_core_display_map(struct seq_file *s, int index, int idx, int ip, static int pmc_core_ppfear_show(struct seq_file *s, void *unused) { struct pmc_dev *pmcdev = s->private; - int i; + unsigned int i; for (i = 0; i < ARRAY_SIZE(pmcdev->pmcs); ++i) { struct pmc *pmc = pmcdev->pmcs[i]; const struct pmc_bit_map **maps; u8 pf_regs[PPFEAR_MAX_NUM_ENTRIES]; - int index, iter, idx, ip = 0; + unsigned int index, iter, idx, ip = 0; if (!pmc) continue; @@ -391,7 +379,8 @@ static int pmc_core_mphy_pg_show(struct seq_file *s, void *unused) const struct pmc_bit_map *map = pmc->map->mphy_sts; u32 mphy_core_reg_low, mphy_core_reg_high; u32 val_low, val_high; - int index, err = 0; + unsigned int index; + int err = 0; if (pmcdev->pmc_xram_read_bit) { seq_puts(s, "Access denied: please disable PMC_READ_DISABLE setting in BIOS."); @@ -401,20 +390,18 @@ static int pmc_core_mphy_pg_show(struct seq_file *s, void *unused) mphy_core_reg_low = (SPT_PMC_MPHY_CORE_STS_0 << 16); mphy_core_reg_high = (SPT_PMC_MPHY_CORE_STS_1 << 16); - mutex_lock(&pmcdev->lock); + guard(mutex)(&pmcdev->lock); - if (pmc_core_send_msg(pmc, &mphy_core_reg_low) != 0) { - err = -EBUSY; - goto out_unlock; - } + err = pmc_core_send_msg(pmc, &mphy_core_reg_low); + if (err) + return err; msleep(10); val_low = pmc_core_reg_read(pmc, SPT_PMC_MFPMC_OFFSET); - if (pmc_core_send_msg(pmc, &mphy_core_reg_high) != 0) { - err = -EBUSY; - goto out_unlock; - } + err = pmc_core_send_msg(pmc, &mphy_core_reg_high); + if (err) + return err; msleep(10); val_high = pmc_core_reg_read(pmc, SPT_PMC_MFPMC_OFFSET); @@ -433,9 +420,7 @@ static int pmc_core_mphy_pg_show(struct seq_file *s, void *unused) "Power gated"); } -out_unlock: - mutex_unlock(&pmcdev->lock); - return err; + return 0; } DEFINE_SHOW_ATTRIBUTE(pmc_core_mphy_pg); @@ -445,7 +430,8 @@ static int pmc_core_pll_show(struct seq_file *s, void *unused) struct pmc *pmc = pmcdev->pmcs[PMC_IDX_MAIN]; const struct pmc_bit_map *map = pmc->map->pll_sts; u32 mphy_common_reg, val; - int index, err = 0; + unsigned int index; + int err = 0; if (pmcdev->pmc_xram_read_bit) { seq_puts(s, "Access denied: please disable PMC_READ_DISABLE setting in BIOS."); @@ -453,12 +439,11 @@ static int pmc_core_pll_show(struct seq_file *s, void *unused) } mphy_common_reg = (SPT_PMC_MPHY_COM_STS_0 << 16); - mutex_lock(&pmcdev->lock); + guard(mutex)(&pmcdev->lock); - if (pmc_core_send_msg(pmc, &mphy_common_reg) != 0) { - err = -EBUSY; - goto out_unlock; - } + err = pmc_core_send_msg(pmc, &mphy_common_reg); + if (err) + return err; /* Observed PMC HW response latency for MTPMC-MFPMC is ~10 ms */ msleep(10); @@ -470,9 +455,7 @@ static int pmc_core_pll_show(struct seq_file *s, void *unused) map[index].bit_mask & val ? "Active" : "Idle"); } -out_unlock: - mutex_unlock(&pmcdev->lock); - return err; + return 0; } DEFINE_SHOW_ATTRIBUTE(pmc_core_pll); @@ -481,7 +464,8 @@ int pmc_core_send_ltr_ignore(struct pmc_dev *pmcdev, u32 value, int ignore) struct pmc *pmc; const struct pmc_reg_map *map; u32 reg; - int pmc_index, ltr_index; + unsigned int pmc_index; + int ltr_index; ltr_index = value; /* For platforms with multiple pmcs, ltr index value given by user @@ -511,7 +495,7 @@ int pmc_core_send_ltr_ignore(struct pmc_dev *pmcdev, u32 value, int ignore) pr_debug("ltr_ignore for pmc%d: ltr_index:%d\n", pmc_index, ltr_index); - mutex_lock(&pmcdev->lock); + guard(mutex)(&pmcdev->lock); reg = pmc_core_reg_read(pmc, map->ltr_ignore_offset); if (ignore) @@ -520,48 +504,56 @@ int pmc_core_send_ltr_ignore(struct pmc_dev *pmcdev, u32 value, int ignore) reg &= ~BIT(ltr_index); pmc_core_reg_write(pmc, map->ltr_ignore_offset, reg); - mutex_unlock(&pmcdev->lock); - return 0; } -static ssize_t pmc_core_ltr_ignore_write(struct file *file, - const char __user *userbuf, - size_t count, loff_t *ppos) +static ssize_t pmc_core_ltr_write(struct pmc_dev *pmcdev, + const char __user *userbuf, + size_t count, int ignore) { - struct seq_file *s = file->private_data; - struct pmc_dev *pmcdev = s->private; - u32 buf_size, value; + u32 value; int err; - buf_size = min_t(u32, count, 64); - - err = kstrtou32_from_user(userbuf, buf_size, 10, &value); + err = kstrtou32_from_user(userbuf, count, 10, &value); if (err) return err; - err = pmc_core_send_ltr_ignore(pmcdev, value, 1); + err = pmc_core_send_ltr_ignore(pmcdev, value, ignore); + + return err ?: count; +} + +static ssize_t pmc_core_ltr_ignore_write(struct file *file, + const char __user *userbuf, + size_t count, loff_t *ppos) +{ + struct seq_file *s = file->private_data; + struct pmc_dev *pmcdev = s->private; - return err == 0 ? count : err; + return pmc_core_ltr_write(pmcdev, userbuf, count, 1); } static int pmc_core_ltr_ignore_show(struct seq_file *s, void *unused) { return 0; } +DEFINE_SHOW_STORE_ATTRIBUTE(pmc_core_ltr_ignore); -static int pmc_core_ltr_ignore_open(struct inode *inode, struct file *file) +static ssize_t pmc_core_ltr_restore_write(struct file *file, + const char __user *userbuf, + size_t count, loff_t *ppos) { - return single_open(file, pmc_core_ltr_ignore_show, inode->i_private); + struct seq_file *s = file->private_data; + struct pmc_dev *pmcdev = s->private; + + return pmc_core_ltr_write(pmcdev, userbuf, count, 0); } -static const struct file_operations pmc_core_ltr_ignore_ops = { - .open = pmc_core_ltr_ignore_open, - .read = seq_read, - .write = pmc_core_ltr_ignore_write, - .llseek = seq_lseek, - .release = single_release, -}; +static int pmc_core_ltr_restore_show(struct seq_file *s, void *unused) +{ + return 0; +} +DEFINE_SHOW_STORE_ATTRIBUTE(pmc_core_ltr_restore); static void pmc_core_slps0_dbg_latch(struct pmc_dev *pmcdev, bool reset) { @@ -569,10 +561,10 @@ static void pmc_core_slps0_dbg_latch(struct pmc_dev *pmcdev, bool reset) const struct pmc_reg_map *map = pmc->map; u32 fd; - mutex_lock(&pmcdev->lock); + guard(mutex)(&pmcdev->lock); if (!reset && !slps0_dbg_latch) - goto out_unlock; + return; fd = pmc_core_reg_read(pmc, map->slps0_dbg_offset); if (reset) @@ -582,9 +574,6 @@ static void pmc_core_slps0_dbg_latch(struct pmc_dev *pmcdev, bool reset) pmc_core_reg_write(pmc, map->slps0_dbg_offset, fd); slps0_dbg_latch = false; - -out_unlock: - mutex_unlock(&pmcdev->lock); } static int pmc_core_slps0_dbg_show(struct seq_file *s, void *unused) @@ -639,17 +628,29 @@ static int pmc_core_ltr_show(struct seq_file *s, void *unused) u64 decoded_snoop_ltr, decoded_non_snoop_ltr; u32 ltr_raw_data, scale, val; u16 snoop_ltr, nonsnoop_ltr; - int i, index, ltr_index = 0; + unsigned int i, index, ltr_index = 0; for (i = 0; i < ARRAY_SIZE(pmcdev->pmcs); ++i) { - struct pmc *pmc = pmcdev->pmcs[i]; + struct pmc *pmc; const struct pmc_bit_map *map; + u32 ltr_ign_reg; + pmc = pmcdev->pmcs[i]; if (!pmc) continue; + scoped_guard(mutex, &pmcdev->lock) + ltr_ign_reg = pmc_core_reg_read(pmc, pmc->map->ltr_ignore_offset); + map = pmc->map->ltr_show_sts; for (index = 0; map[index].name; index++) { + bool ltr_ign_data; + + if (index > pmc->map->ltr_ignore_max) + ltr_ign_data = false; + else + ltr_ign_data = ltr_ign_reg & BIT(index); + decoded_snoop_ltr = decoded_non_snoop_ltr = 0; ltr_raw_data = pmc_core_reg_read(pmc, map[index].bit_mask); @@ -667,10 +668,10 @@ static int pmc_core_ltr_show(struct seq_file *s, void *unused) decoded_snoop_ltr = val * convert_ltr_scale(scale); } - seq_printf(s, "%d\tPMC%d:%-32s\tLTR: RAW: 0x%-16x\tNon-Snoop(ns): %-16llu\tSnoop(ns): %-16llu\n", + seq_printf(s, "%d\tPMC%d:%-32s\tLTR: RAW: 0x%-16x\tNon-Snoop(ns): %-16llu\tSnoop(ns): %-16llu\tLTR_IGNORE: %d\n", ltr_index, i, map[index].name, ltr_raw_data, decoded_non_snoop_ltr, - decoded_snoop_ltr); + decoded_snoop_ltr, ltr_ign_data); ltr_index++; } } @@ -727,7 +728,8 @@ static int pmc_core_substate_res_show(struct seq_file *s, void *unused) struct pmc *pmc = pmcdev->pmcs[PMC_IDX_MAIN]; const int lpm_adj_x2 = pmc->map->lpm_res_counter_step_x2; u32 offset = pmc->map->lpm_residency_offset; - int i, mode; + unsigned int i; + int mode; seq_printf(s, "%-10s %-15s\n", "Substate", "Residency"); @@ -743,7 +745,7 @@ DEFINE_SHOW_ATTRIBUTE(pmc_core_substate_res); static int pmc_core_substate_sts_regs_show(struct seq_file *s, void *unused) { struct pmc_dev *pmcdev = s->private; - int i; + unsigned int i; for (i = 0; i < ARRAY_SIZE(pmcdev->pmcs); ++i) { struct pmc *pmc = pmcdev->pmcs[i]; @@ -764,7 +766,7 @@ DEFINE_SHOW_ATTRIBUTE(pmc_core_substate_sts_regs); static int pmc_core_substate_l_sts_regs_show(struct seq_file *s, void *unused) { struct pmc_dev *pmcdev = s->private; - int i; + unsigned int i; for (i = 0; i < ARRAY_SIZE(pmcdev->pmcs); ++i) { struct pmc *pmc = pmcdev->pmcs[i]; @@ -785,7 +787,8 @@ DEFINE_SHOW_ATTRIBUTE(pmc_core_substate_l_sts_regs); static void pmc_core_substate_req_header_show(struct seq_file *s, int pmc_index) { struct pmc_dev *pmcdev = s->private; - int i, mode; + unsigned int i; + int mode; seq_printf(s, "%30s |", "Element"); pmc_for_each_mode(i, mode, pmcdev) @@ -799,7 +802,8 @@ static int pmc_core_substate_req_regs_show(struct seq_file *s, void *unused) struct pmc_dev *pmcdev = s->private; u32 sts_offset; u32 *lpm_req_regs; - int num_maps, mp, pmc_index; + unsigned int mp, pmc_index; + int num_maps; for (pmc_index = 0; pmc_index < ARRAY_SIZE(pmcdev->pmcs); ++pmc_index) { struct pmc *pmc = pmcdev->pmcs[pmc_index]; @@ -921,9 +925,10 @@ static int pmc_core_lpm_latch_mode_show(struct seq_file *s, void *unused) { struct pmc_dev *pmcdev = s->private; struct pmc *pmc = pmcdev->pmcs[PMC_IDX_MAIN]; + unsigned int idx; bool c10; u32 reg; - int idx, mode; + int mode; reg = pmc_core_reg_read(pmc, pmc->map->lpm_sts_latch_en_offset); if (reg & LPM_STS_LATCH_MODE) { @@ -955,7 +960,8 @@ static ssize_t pmc_core_lpm_latch_mode_write(struct file *file, struct pmc *pmc = pmcdev->pmcs[PMC_IDX_MAIN]; bool clear = false, c10 = false; unsigned char buf[8]; - int idx, m, mode; + unsigned int idx; + int m, mode; u32 reg; if (count > sizeof(buf) - 1) @@ -987,26 +993,22 @@ static ssize_t pmc_core_lpm_latch_mode_write(struct file *file, } if (clear) { - mutex_lock(&pmcdev->lock); + guard(mutex)(&pmcdev->lock); reg = pmc_core_reg_read(pmc, pmc->map->etr3_offset); reg |= ETR3_CLEAR_LPM_EVENTS; pmc_core_reg_write(pmc, pmc->map->etr3_offset, reg); - mutex_unlock(&pmcdev->lock); - return count; } if (c10) { - mutex_lock(&pmcdev->lock); + guard(mutex)(&pmcdev->lock); reg = pmc_core_reg_read(pmc, pmc->map->lpm_sts_latch_en_offset); reg &= ~LPM_STS_LATCH_MODE; pmc_core_reg_write(pmc, pmc->map->lpm_sts_latch_en_offset, reg); - mutex_unlock(&pmcdev->lock); - return count; } @@ -1015,9 +1017,8 @@ static ssize_t pmc_core_lpm_latch_mode_write(struct file *file, * and clear everything else. */ reg = LPM_STS_LATCH_MODE | BIT(mode); - mutex_lock(&pmcdev->lock); + guard(mutex)(&pmcdev->lock); pmc_core_reg_write(pmc, pmc->map->lpm_sts_latch_en_offset, reg); - mutex_unlock(&pmcdev->lock); return count; } @@ -1028,7 +1029,7 @@ static int pmc_core_pkgc_show(struct seq_file *s, void *unused) struct pmc *pmc = s->private; const struct pmc_bit_map *map = pmc->map->msr_sts; u64 pcstate_count; - int index; + unsigned int index; for (index = 0; map[index].name ; index++) { if (rdmsrl_safe(map[index].bit_mask, &pcstate_count)) @@ -1046,7 +1047,7 @@ DEFINE_SHOW_ATTRIBUTE(pmc_core_pkgc); static bool pmc_core_pri_verify(u32 lpm_pri, u8 *mode_order) { - int i, j; + unsigned int i, j; if (!lpm_pri) return false; @@ -1081,7 +1082,8 @@ void pmc_core_get_low_power_modes(struct pmc_dev *pmcdev) u8 mode_order[LPM_MAX_NUM_MODES]; u32 lpm_pri; u32 lpm_en; - int mode, i, p; + unsigned int i; + int mode, p; /* Use LPM Maps to indicate support for substates */ if (!pmc->map->lpm_num_maps) @@ -1228,7 +1230,9 @@ static void pmc_core_dbgfs_register(struct pmc_dev *pmcdev) pmcdev, &pmc_core_ppfear_fops); debugfs_create_file("ltr_ignore", 0644, dir, pmcdev, - &pmc_core_ltr_ignore_ops); + &pmc_core_ltr_ignore_fops); + + debugfs_create_file("ltr_restore", 0200, dir, pmcdev, &pmc_core_ltr_restore_fops); debugfs_create_file("ltr_show", 0444, dir, pmcdev, &pmc_core_ltr_fops); @@ -1293,29 +1297,29 @@ static void pmc_core_dbgfs_register(struct pmc_dev *pmcdev) } static const struct x86_cpu_id intel_pmc_core_ids[] = { - X86_MATCH_INTEL_FAM6_MODEL(SKYLAKE_L, spt_core_init), - X86_MATCH_INTEL_FAM6_MODEL(SKYLAKE, spt_core_init), - X86_MATCH_INTEL_FAM6_MODEL(KABYLAKE_L, spt_core_init), - X86_MATCH_INTEL_FAM6_MODEL(KABYLAKE, spt_core_init), - X86_MATCH_INTEL_FAM6_MODEL(CANNONLAKE_L, cnp_core_init), - X86_MATCH_INTEL_FAM6_MODEL(ICELAKE_L, icl_core_init), - 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_l_core_init), - X86_MATCH_INTEL_FAM6_MODEL(TIGERLAKE, 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_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_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), - X86_MATCH_INTEL_FAM6_MODEL(ARROWLAKE, arl_core_init), - X86_MATCH_INTEL_FAM6_MODEL(LUNARLAKE_M, lnl_core_init), + X86_MATCH_VFM(INTEL_SKYLAKE_L, spt_core_init), + X86_MATCH_VFM(INTEL_SKYLAKE, spt_core_init), + X86_MATCH_VFM(INTEL_KABYLAKE_L, spt_core_init), + X86_MATCH_VFM(INTEL_KABYLAKE, spt_core_init), + X86_MATCH_VFM(INTEL_CANNONLAKE_L, cnp_core_init), + X86_MATCH_VFM(INTEL_ICELAKE_L, icl_core_init), + X86_MATCH_VFM(INTEL_ICELAKE_NNPI, icl_core_init), + X86_MATCH_VFM(INTEL_COMETLAKE, cnp_core_init), + X86_MATCH_VFM(INTEL_COMETLAKE_L, cnp_core_init), + X86_MATCH_VFM(INTEL_TIGERLAKE_L, tgl_l_core_init), + X86_MATCH_VFM(INTEL_TIGERLAKE, tgl_core_init), + X86_MATCH_VFM(INTEL_ATOM_TREMONT, tgl_l_core_init), + X86_MATCH_VFM(INTEL_ATOM_TREMONT_L, icl_core_init), + X86_MATCH_VFM(INTEL_ROCKETLAKE, tgl_core_init), + X86_MATCH_VFM(INTEL_ALDERLAKE_L, tgl_l_core_init), + X86_MATCH_VFM(INTEL_ATOM_GRACEMONT, tgl_l_core_init), + X86_MATCH_VFM(INTEL_ALDERLAKE, adl_core_init), + X86_MATCH_VFM(INTEL_RAPTORLAKE_P, tgl_l_core_init), + X86_MATCH_VFM(INTEL_RAPTORLAKE, adl_core_init), + X86_MATCH_VFM(INTEL_RAPTORLAKE_S, adl_core_init), + X86_MATCH_VFM(INTEL_METEORLAKE_L, mtl_core_init), + X86_MATCH_VFM(INTEL_ARROWLAKE, arl_core_init), + X86_MATCH_VFM(INTEL_LUNARLAKE_M, lnl_core_init), {} }; @@ -1373,7 +1377,7 @@ static void pmc_core_do_dmi_quirks(struct pmc *pmc) static void pmc_core_clean_structure(struct platform_device *pdev) { struct pmc_dev *pmcdev = platform_get_drvdata(pdev); - int i; + unsigned int i; for (i = 0; i < ARRAY_SIZE(pmcdev->pmcs); ++i) { struct pmc *pmc = pmcdev->pmcs[i]; @@ -1536,7 +1540,7 @@ int pmc_core_resume_common(struct pmc_dev *pmcdev) struct pmc *pmc = pmcdev->pmcs[PMC_IDX_MAIN]; const struct pmc_bit_map **maps = pmc->map->lpm_sts; int offset = pmc->map->lpm_status_offset; - int i; + unsigned int i; /* Check if the syspend used S0ix */ if (pm_suspend_via_firmware()) diff --git a/drivers/platform/x86/intel/pmc/pltdrv.c b/drivers/platform/x86/intel/pmc/pltdrv.c index f2cb87dc2d37..3141d6cbc41b 100644 --- a/drivers/platform/x86/intel/pmc/pltdrv.c +++ b/drivers/platform/x86/intel/pmc/pltdrv.c @@ -35,14 +35,14 @@ static struct platform_device *pmc_core_device; * other list may grow, but this list should not. */ static const struct x86_cpu_id intel_pmc_core_platform_ids[] = { - X86_MATCH_INTEL_FAM6_MODEL(SKYLAKE_L, &pmc_core_device), - X86_MATCH_INTEL_FAM6_MODEL(SKYLAKE, &pmc_core_device), - X86_MATCH_INTEL_FAM6_MODEL(KABYLAKE_L, &pmc_core_device), - X86_MATCH_INTEL_FAM6_MODEL(KABYLAKE, &pmc_core_device), - X86_MATCH_INTEL_FAM6_MODEL(CANNONLAKE_L, &pmc_core_device), - X86_MATCH_INTEL_FAM6_MODEL(ICELAKE_L, &pmc_core_device), - X86_MATCH_INTEL_FAM6_MODEL(COMETLAKE, &pmc_core_device), - X86_MATCH_INTEL_FAM6_MODEL(COMETLAKE_L, &pmc_core_device), + X86_MATCH_VFM(INTEL_SKYLAKE_L, &pmc_core_device), + X86_MATCH_VFM(INTEL_SKYLAKE, &pmc_core_device), + X86_MATCH_VFM(INTEL_KABYLAKE_L, &pmc_core_device), + X86_MATCH_VFM(INTEL_KABYLAKE, &pmc_core_device), + X86_MATCH_VFM(INTEL_CANNONLAKE_L, &pmc_core_device), + X86_MATCH_VFM(INTEL_ICELAKE_L, &pmc_core_device), + X86_MATCH_VFM(INTEL_COMETLAKE, &pmc_core_device), + X86_MATCH_VFM(INTEL_COMETLAKE_L, &pmc_core_device), {} }; MODULE_DEVICE_TABLE(x86cpu, intel_pmc_core_platform_ids); diff --git a/drivers/platform/x86/intel/speed_select_if/isst_if_common.c b/drivers/platform/x86/intel/speed_select_if/isst_if_common.c index 713c0d1fa85f..10e21563fa46 100644 --- a/drivers/platform/x86/intel/speed_select_if/isst_if_common.c +++ b/drivers/platform/x86/intel/speed_select_if/isst_if_common.c @@ -718,14 +718,6 @@ static struct miscdevice isst_if_char_driver = { .fops = &isst_if_char_driver_ops, }; -static const struct x86_cpu_id hpm_cpu_ids[] = { - X86_MATCH_INTEL_FAM6_MODEL(GRANITERAPIDS_D, NULL), - X86_MATCH_INTEL_FAM6_MODEL(GRANITERAPIDS_X, NULL), - X86_MATCH_INTEL_FAM6_MODEL(ATOM_CRESTMONT, NULL), - X86_MATCH_INTEL_FAM6_MODEL(ATOM_CRESTMONT_X, NULL), - {} -}; - static int isst_misc_reg(void) { mutex_lock(&punit_misc_dev_reg_lock); @@ -733,12 +725,6 @@ static int isst_misc_reg(void) goto unlock_exit; if (!misc_usage_count) { - const struct x86_cpu_id *id; - - id = x86_match_cpu(hpm_cpu_ids); - if (id) - isst_hpm_support = true; - misc_device_ret = isst_if_cpu_info_init(); if (misc_device_ret) goto unlock_exit; @@ -786,11 +772,12 @@ static void isst_misc_unreg(void) */ int isst_if_cdev_register(int device_type, struct isst_if_cmd_cb *cb) { - int ret; - if (device_type >= ISST_IF_DEV_MAX) return -EINVAL; + if (device_type < ISST_IF_DEV_TPMI && isst_hpm_support) + return -ENODEV; + mutex_lock(&punit_misc_dev_open_lock); /* Device is already open, we don't want to add new callbacks */ if (misc_device_open) { @@ -805,15 +792,6 @@ int isst_if_cdev_register(int device_type, struct isst_if_cmd_cb *cb) punit_callbacks[device_type].registered = 1; mutex_unlock(&punit_misc_dev_open_lock); - ret = isst_misc_reg(); - if (ret) { - /* - * No need of mutex as the misc device register failed - * as no one can open device yet. Hence no contention. - */ - punit_callbacks[device_type].registered = 0; - return ret; - } return 0; } EXPORT_SYMBOL_GPL(isst_if_cdev_register); @@ -829,7 +807,6 @@ EXPORT_SYMBOL_GPL(isst_if_cdev_register); */ void isst_if_cdev_unregister(int device_type) { - isst_misc_unreg(); mutex_lock(&punit_misc_dev_open_lock); punit_callbacks[device_type].def_ioctl = NULL; punit_callbacks[device_type].registered = 0; @@ -839,5 +816,51 @@ void isst_if_cdev_unregister(int device_type) } EXPORT_SYMBOL_GPL(isst_if_cdev_unregister); +#define SST_HPM_SUPPORTED 0x01 +#define SST_MBOX_SUPPORTED 0x02 + +static const struct x86_cpu_id isst_cpu_ids[] = { + X86_MATCH_VFM(INTEL_ATOM_CRESTMONT, SST_HPM_SUPPORTED), + X86_MATCH_VFM(INTEL_ATOM_CRESTMONT_X, SST_HPM_SUPPORTED), + X86_MATCH_VFM(INTEL_EMERALDRAPIDS_X, 0), + X86_MATCH_VFM(INTEL_GRANITERAPIDS_D, SST_HPM_SUPPORTED), + X86_MATCH_VFM(INTEL_GRANITERAPIDS_X, SST_HPM_SUPPORTED), + X86_MATCH_VFM(INTEL_ICELAKE_D, 0), + X86_MATCH_VFM(INTEL_ICELAKE_X, 0), + X86_MATCH_VFM(INTEL_SAPPHIRERAPIDS_X, 0), + X86_MATCH_VFM(INTEL_SKYLAKE_X, SST_MBOX_SUPPORTED), + {} +}; +MODULE_DEVICE_TABLE(x86cpu, isst_cpu_ids); + +static int __init isst_if_common_init(void) +{ + const struct x86_cpu_id *id; + + id = x86_match_cpu(isst_cpu_ids); + if (!id) + return -ENODEV; + + if (id->driver_data == SST_HPM_SUPPORTED) { + isst_hpm_support = true; + } else if (id->driver_data == SST_MBOX_SUPPORTED) { + u64 data; + + /* Can fail only on some Skylake-X generations */ + if (rdmsrl_safe(MSR_OS_MAILBOX_INTERFACE, &data) || + rdmsrl_safe(MSR_OS_MAILBOX_DATA, &data)) + return -ENODEV; + } + + return isst_misc_reg(); +} +module_init(isst_if_common_init) + +static void __exit isst_if_common_exit(void) +{ + isst_misc_unreg(); +} +module_exit(isst_if_common_exit) + MODULE_DESCRIPTION("ISST common interface module"); MODULE_LICENSE("GPL v2"); diff --git a/drivers/platform/x86/intel/speed_select_if/isst_if_common.h b/drivers/platform/x86/intel/speed_select_if/isst_if_common.h index 1004f2c9cca8..378055fe1d16 100644 --- a/drivers/platform/x86/intel/speed_select_if/isst_if_common.h +++ b/drivers/platform/x86/intel/speed_select_if/isst_if_common.h @@ -16,6 +16,9 @@ #define PCI_DEVICE_ID_INTEL_RAPL_PRIO_DEVID_1 0x3251 #define PCI_DEVICE_ID_INTEL_CFG_MBOX_DEVID_1 0x3259 +#define MSR_OS_MAILBOX_INTERFACE 0xB0 +#define MSR_OS_MAILBOX_DATA 0xB1 + /* * Validate maximum commands in a single request. * This is enough to handle command to every core in one ioctl, or all diff --git a/drivers/platform/x86/intel/speed_select_if/isst_if_mbox_msr.c b/drivers/platform/x86/intel/speed_select_if/isst_if_mbox_msr.c index 1b6eab071068..c4b7af00352b 100644 --- a/drivers/platform/x86/intel/speed_select_if/isst_if_mbox_msr.c +++ b/drivers/platform/x86/intel/speed_select_if/isst_if_mbox_msr.c @@ -21,8 +21,6 @@ #include "isst_if_common.h" -#define MSR_OS_MAILBOX_INTERFACE 0xB0 -#define MSR_OS_MAILBOX_DATA 0xB1 #define MSR_OS_MAILBOX_BUSY_BIT 31 /* @@ -161,7 +159,7 @@ static struct notifier_block isst_pm_nb = { }; static const struct x86_cpu_id isst_if_cpu_ids[] = { - X86_MATCH_INTEL_FAM6_MODEL(SKYLAKE_X, NULL), + X86_MATCH_VFM(INTEL_SKYLAKE_X, NULL), {} }; MODULE_DEVICE_TABLE(x86cpu, isst_if_cpu_ids); diff --git a/drivers/platform/x86/intel/telemetry/debugfs.c b/drivers/platform/x86/intel/telemetry/debugfs.c index 1d4d0fbfd63c..70e5736c44c7 100644 --- a/drivers/platform/x86/intel/telemetry/debugfs.c +++ b/drivers/platform/x86/intel/telemetry/debugfs.c @@ -308,8 +308,8 @@ static struct telemetry_debugfs_conf telem_apl_debugfs_conf = { }; static const struct x86_cpu_id telemetry_debugfs_cpu_ids[] = { - X86_MATCH_INTEL_FAM6_MODEL(ATOM_GOLDMONT, &telem_apl_debugfs_conf), - X86_MATCH_INTEL_FAM6_MODEL(ATOM_GOLDMONT_PLUS, &telem_apl_debugfs_conf), + X86_MATCH_VFM(INTEL_ATOM_GOLDMONT, &telem_apl_debugfs_conf), + X86_MATCH_VFM(INTEL_ATOM_GOLDMONT_PLUS, &telem_apl_debugfs_conf), {} }; MODULE_DEVICE_TABLE(x86cpu, telemetry_debugfs_cpu_ids); diff --git a/drivers/platform/x86/intel/telemetry/pltdrv.c b/drivers/platform/x86/intel/telemetry/pltdrv.c index 06311d0e9451..767a0bc6c7ad 100644 --- a/drivers/platform/x86/intel/telemetry/pltdrv.c +++ b/drivers/platform/x86/intel/telemetry/pltdrv.c @@ -177,8 +177,8 @@ static struct telemetry_plt_config telem_glk_config = { }; static const struct x86_cpu_id telemetry_cpu_ids[] = { - X86_MATCH_INTEL_FAM6_MODEL(ATOM_GOLDMONT, &telem_apl_config), - X86_MATCH_INTEL_FAM6_MODEL(ATOM_GOLDMONT_PLUS, &telem_glk_config), + X86_MATCH_VFM(INTEL_ATOM_GOLDMONT, &telem_apl_config), + X86_MATCH_VFM(INTEL_ATOM_GOLDMONT_PLUS, &telem_glk_config), {} }; diff --git a/drivers/platform/x86/intel/tpmi.c b/drivers/platform/x86/intel/tpmi.c index 6c0cbccd80bb..83e8b1fe53b3 100644 --- a/drivers/platform/x86/intel/tpmi.c +++ b/drivers/platform/x86/intel/tpmi.c @@ -357,6 +357,15 @@ int tpmi_get_feature_status(struct auxiliary_device *auxdev, } EXPORT_SYMBOL_NS_GPL(tpmi_get_feature_status, INTEL_TPMI); +struct dentry *tpmi_get_debugfs_dir(struct auxiliary_device *auxdev) +{ + 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); + + return tpmi_info->dbgfs_dir; +} +EXPORT_SYMBOL_NS_GPL(tpmi_get_debugfs_dir, INTEL_TPMI); + static int tpmi_pfs_dbg_show(struct seq_file *s, void *unused) { struct intel_tpmi_info *tpmi_info = s->private; @@ -577,6 +586,8 @@ static const char *intel_tpmi_name(enum intel_tpmi_id id) return "uncore"; case TPMI_ID_SST: return "sst"; + case TPMI_ID_PLR: + return "plr"; default: return NULL; } diff --git a/drivers/platform/x86/intel/tpmi_power_domains.c b/drivers/platform/x86/intel/tpmi_power_domains.c new file mode 100644 index 000000000000..4eb02553957c --- /dev/null +++ b/drivers/platform/x86/intel/tpmi_power_domains.c @@ -0,0 +1,235 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Mapping of TPMI power domains CPU mapping + * + * Copyright (c) 2024, Intel Corporation. + */ + +#include <linux/bitfield.h> +#include <linux/cleanup.h> +#include <linux/cpuhotplug.h> +#include <linux/cpumask.h> +#include <linux/errno.h> +#include <linux/export.h> +#include <linux/hashtable.h> +#include <linux/module.h> +#include <linux/mutex.h> +#include <linux/overflow.h> +#include <linux/slab.h> +#include <linux/topology.h> +#include <linux/types.h> + +#include <asm/cpu_device_id.h> +#include <asm/intel-family.h> +#include <asm/msr.h> + +#include "tpmi_power_domains.h" + +#define MSR_PM_LOGICAL_ID 0x54 + +/* + * Struct of MSR 0x54 + * [15:11] PM_DOMAIN_ID + * [10:3] MODULE_ID (aka IDI_AGENT_ID) + * [2:0] LP_ID + * For Atom: + * [2] Always 0 + * [1:0] core ID within module + * For Core + * [2:1] Always 0 + * [0] thread ID + */ + +#define LP_ID_MASK GENMASK_ULL(2, 0) +#define MODULE_ID_MASK GENMASK_ULL(10, 3) +#define PM_DOMAIN_ID_MASK GENMASK_ULL(15, 11) + +/** + * struct tpmi_cpu_info - Mapping information for a CPU + * @hnode: Used to add mapping information to hash list + * @linux_cpu: Linux CPU number + * @pkg_id: Package ID of this CPU + * @punit_thread_id: Punit thread id of this CPU + * @punit_core_id: Punit core id + * @punit_domain_id: Power domain id from Punit + * + * Structure to store mapping information for a Linux CPU + * to a Punit core, thread and power domain. + */ +struct tpmi_cpu_info { + struct hlist_node hnode; + int linux_cpu; + u8 pkg_id; + u8 punit_thread_id; + u8 punit_core_id; + u8 punit_domain_id; +}; + +static DEFINE_PER_CPU(struct tpmi_cpu_info, tpmi_cpu_info); + +/* The dynamically assigned cpu hotplug state to free later */ +static enum cpuhp_state tpmi_hp_state __read_mostly; + +#define MAX_POWER_DOMAINS 8 + +static cpumask_t *tpmi_power_domain_mask; + +/* Lock to protect tpmi_power_domain_mask and tpmi_cpu_hash */ +static DEFINE_MUTEX(tpmi_lock); + +static const struct x86_cpu_id tpmi_cpu_ids[] = { + X86_MATCH_VFM(INTEL_GRANITERAPIDS_X, NULL), + X86_MATCH_VFM(INTEL_ATOM_CRESTMONT_X, NULL), + X86_MATCH_VFM(INTEL_ATOM_CRESTMONT, NULL), + X86_MATCH_VFM(INTEL_GRANITERAPIDS_D, NULL), + {} +}; +MODULE_DEVICE_TABLE(x86cpu, tpmi_cpu_ids); + +static DECLARE_HASHTABLE(tpmi_cpu_hash, 8); + +static bool tpmi_domain_is_valid(struct tpmi_cpu_info *info) +{ + return info->pkg_id < topology_max_packages() && + info->punit_domain_id < MAX_POWER_DOMAINS; +} + +int tpmi_get_linux_cpu_number(int package_id, int domain_id, int punit_core_id) +{ + struct tpmi_cpu_info *info; + int ret = -EINVAL; + + guard(mutex)(&tpmi_lock); + hash_for_each_possible(tpmi_cpu_hash, info, hnode, punit_core_id) { + if (info->punit_domain_id == domain_id && info->pkg_id == package_id) { + ret = info->linux_cpu; + break; + } + } + + return ret; +} +EXPORT_SYMBOL_NS_GPL(tpmi_get_linux_cpu_number, INTEL_TPMI_POWER_DOMAIN); + +int tpmi_get_punit_core_number(int cpu_no) +{ + if (cpu_no >= num_possible_cpus()) + return -EINVAL; + + return per_cpu(tpmi_cpu_info, cpu_no).punit_core_id; +} +EXPORT_SYMBOL_NS_GPL(tpmi_get_punit_core_number, INTEL_TPMI_POWER_DOMAIN); + +int tpmi_get_power_domain_id(int cpu_no) +{ + if (cpu_no >= num_possible_cpus()) + return -EINVAL; + + return per_cpu(tpmi_cpu_info, cpu_no).punit_domain_id; +} +EXPORT_SYMBOL_NS_GPL(tpmi_get_power_domain_id, INTEL_TPMI_POWER_DOMAIN); + +cpumask_t *tpmi_get_power_domain_mask(int cpu_no) +{ + struct tpmi_cpu_info *info; + cpumask_t *mask; + int index; + + if (cpu_no >= num_possible_cpus()) + return NULL; + + info = &per_cpu(tpmi_cpu_info, cpu_no); + if (!tpmi_domain_is_valid(info)) + return NULL; + + index = info->pkg_id * MAX_POWER_DOMAINS + info->punit_domain_id; + guard(mutex)(&tpmi_lock); + mask = &tpmi_power_domain_mask[index]; + + return mask; +} +EXPORT_SYMBOL_NS_GPL(tpmi_get_power_domain_mask, INTEL_TPMI_POWER_DOMAIN); + +static int tpmi_get_logical_id(unsigned int cpu, struct tpmi_cpu_info *info) +{ + u64 data; + int ret; + + ret = rdmsrl_safe(MSR_PM_LOGICAL_ID, &data); + if (ret) + return ret; + + info->punit_domain_id = FIELD_GET(PM_DOMAIN_ID_MASK, data); + if (info->punit_domain_id >= MAX_POWER_DOMAINS) + return -EINVAL; + + info->punit_thread_id = FIELD_GET(LP_ID_MASK, data); + info->punit_core_id = FIELD_GET(MODULE_ID_MASK, data); + info->pkg_id = topology_physical_package_id(cpu); + info->linux_cpu = cpu; + + return 0; +} + +static int tpmi_cpu_online(unsigned int cpu) +{ + struct tpmi_cpu_info *info = &per_cpu(tpmi_cpu_info, cpu); + int ret, index; + + /* Don't fail CPU online for some bad mapping of CPUs */ + ret = tpmi_get_logical_id(cpu, info); + if (ret) + return 0; + + index = info->pkg_id * MAX_POWER_DOMAINS + info->punit_domain_id; + + guard(mutex)(&tpmi_lock); + cpumask_set_cpu(cpu, &tpmi_power_domain_mask[index]); + hash_add(tpmi_cpu_hash, &info->hnode, info->punit_core_id); + + return 0; +} + +static int __init tpmi_init(void) +{ + const struct x86_cpu_id *id; + u64 data; + int ret; + + id = x86_match_cpu(tpmi_cpu_ids); + if (!id) + return -ENODEV; + + /* Check for MSR 0x54 presence */ + ret = rdmsrl_safe(MSR_PM_LOGICAL_ID, &data); + if (ret) + return ret; + + tpmi_power_domain_mask = kcalloc(size_mul(topology_max_packages(), MAX_POWER_DOMAINS), + sizeof(*tpmi_power_domain_mask), GFP_KERNEL); + if (!tpmi_power_domain_mask) + return -ENOMEM; + + ret = cpuhp_setup_state(CPUHP_AP_ONLINE_DYN, + "platform/x86/tpmi_power_domains:online", + tpmi_cpu_online, NULL); + if (ret < 0) { + kfree(tpmi_power_domain_mask); + return ret; + } + + tpmi_hp_state = ret; + + return 0; +} +module_init(tpmi_init) + +static void __exit tpmi_exit(void) +{ + cpuhp_remove_state(tpmi_hp_state); + kfree(tpmi_power_domain_mask); +} +module_exit(tpmi_exit) + +MODULE_DESCRIPTION("TPMI Power Domains Mapping"); +MODULE_LICENSE("GPL"); diff --git a/drivers/platform/x86/intel/tpmi_power_domains.h b/drivers/platform/x86/intel/tpmi_power_domains.h new file mode 100644 index 000000000000..e35750dd9273 --- /dev/null +++ b/drivers/platform/x86/intel/tpmi_power_domains.h @@ -0,0 +1,18 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Mapping of TPMI power domain and CPUs + * + * Copyright (c) 2024, Intel Corporation. + */ + +#ifndef _TPMI_POWER_DOMAINS_H_ +#define _TPMI_POWER_DOMAINS_H_ + +#include <linux/cpumask.h> + +int tpmi_get_linux_cpu_number(int package_id, int die_id, int punit_core_id); +int tpmi_get_punit_core_number(int cpu_no); +int tpmi_get_power_domain_id(int cpu_no); +cpumask_t *tpmi_get_power_domain_mask(int cpu_no); + +#endif diff --git a/drivers/platform/x86/intel/turbo_max_3.c b/drivers/platform/x86/intel/turbo_max_3.c index 892140b62898..79a0bcdeffb8 100644 --- a/drivers/platform/x86/intel/turbo_max_3.c +++ b/drivers/platform/x86/intel/turbo_max_3.c @@ -114,8 +114,8 @@ static int itmt_legacy_cpu_online(unsigned int cpu) } static const struct x86_cpu_id itmt_legacy_cpu_ids[] = { - X86_MATCH_INTEL_FAM6_MODEL(BROADWELL_X, NULL), - X86_MATCH_INTEL_FAM6_MODEL(SKYLAKE_X, NULL), + X86_MATCH_VFM(INTEL_BROADWELL_X, NULL), + X86_MATCH_VFM(INTEL_SKYLAKE_X, NULL), {} }; diff --git a/drivers/platform/x86/intel/uncore-frequency/uncore-frequency-common.c b/drivers/platform/x86/intel/uncore-frequency/uncore-frequency-common.c index 33bb58dc3f78..4e880585cbe4 100644 --- a/drivers/platform/x86/intel/uncore-frequency/uncore-frequency-common.c +++ b/drivers/platform/x86/intel/uncore-frequency/uncore-frequency-common.c @@ -19,9 +19,8 @@ static int uncore_instance_count; static DEFINE_IDA(intel_uncore_ida); /* callbacks for actual HW read/write */ -static int (*uncore_read)(struct uncore_data *data, unsigned int *min, unsigned int *max); -static int (*uncore_write)(struct uncore_data *data, unsigned int input, unsigned int min_max); -static int (*uncore_read_freq)(struct uncore_data *data, unsigned int *freq); +static int (*uncore_read)(struct uncore_data *data, unsigned int *value, enum uncore_index index); +static int (*uncore_write)(struct uncore_data *data, unsigned int input, enum uncore_index index); static ssize_t show_domain_id(struct kobject *kobj, struct kobj_attribute *attr, char *buf) { @@ -44,27 +43,22 @@ static ssize_t show_package_id(struct kobject *kobj, struct kobj_attribute *attr return sprintf(buf, "%u\n", data->package_id); } -static ssize_t show_min_max_freq_khz(struct uncore_data *data, - char *buf, int min_max) +static ssize_t show_attr(struct uncore_data *data, char *buf, enum uncore_index index) { - unsigned int min, max; + unsigned int value; int ret; mutex_lock(&uncore_lock); - ret = uncore_read(data, &min, &max); + ret = uncore_read(data, &value, index); mutex_unlock(&uncore_lock); if (ret) return ret; - if (min_max) - return sprintf(buf, "%u\n", max); - - return sprintf(buf, "%u\n", min); + return sprintf(buf, "%u\n", value); } -static ssize_t store_min_max_freq_khz(struct uncore_data *data, - const char *buf, ssize_t count, - int min_max) +static ssize_t store_attr(struct uncore_data *data, const char *buf, ssize_t count, + enum uncore_index index) { unsigned int input; int ret; @@ -73,7 +67,7 @@ static ssize_t store_min_max_freq_khz(struct uncore_data *data, return -EINVAL; mutex_lock(&uncore_lock); - ret = uncore_write(data, input, min_max); + ret = uncore_write(data, input, index); mutex_unlock(&uncore_lock); if (ret) @@ -82,56 +76,32 @@ static ssize_t store_min_max_freq_khz(struct uncore_data *data, return count; } -static ssize_t show_perf_status_freq_khz(struct uncore_data *data, char *buf) -{ - unsigned int freq; - int ret; - - mutex_lock(&uncore_lock); - ret = uncore_read_freq(data, &freq); - mutex_unlock(&uncore_lock); - if (ret) - return ret; - - return sprintf(buf, "%u\n", freq); -} - -#define store_uncore_min_max(name, min_max) \ +#define store_uncore_attr(name, index) \ static ssize_t store_##name(struct kobject *kobj, \ struct kobj_attribute *attr, \ const char *buf, size_t count) \ { \ struct uncore_data *data = container_of(attr, struct uncore_data, name##_kobj_attr);\ \ - return store_min_max_freq_khz(data, buf, count, \ - min_max); \ + return store_attr(data, buf, count, index); \ } -#define show_uncore_min_max(name, min_max) \ +#define show_uncore_attr(name, index) \ static ssize_t show_##name(struct kobject *kobj, \ struct kobj_attribute *attr, char *buf)\ { \ struct uncore_data *data = container_of(attr, struct uncore_data, name##_kobj_attr);\ \ - return show_min_max_freq_khz(data, buf, min_max); \ - } - -#define show_uncore_perf_status(name) \ - static ssize_t show_##name(struct kobject *kobj, \ - struct kobj_attribute *attr, char *buf)\ - { \ - struct uncore_data *data = container_of(attr, struct uncore_data, name##_kobj_attr);\ - \ - return show_perf_status_freq_khz(data, buf); \ + return show_attr(data, buf, index); \ } -store_uncore_min_max(min_freq_khz, 0); -store_uncore_min_max(max_freq_khz, 1); +store_uncore_attr(min_freq_khz, UNCORE_INDEX_MIN_FREQ); +store_uncore_attr(max_freq_khz, UNCORE_INDEX_MAX_FREQ); -show_uncore_min_max(min_freq_khz, 0); -show_uncore_min_max(max_freq_khz, 1); +show_uncore_attr(min_freq_khz, UNCORE_INDEX_MIN_FREQ); +show_uncore_attr(max_freq_khz, UNCORE_INDEX_MAX_FREQ); -show_uncore_perf_status(current_freq_khz); +show_uncore_attr(current_freq_khz, UNCORE_INDEX_CURRENT_FREQ); #define show_uncore_data(member_name) \ static ssize_t show_##member_name(struct kobject *kobj, \ @@ -198,7 +168,7 @@ static int create_attr_group(struct uncore_data *data, char *name) data->uncore_attrs[index++] = &data->initial_min_freq_khz_kobj_attr.attr; data->uncore_attrs[index++] = &data->initial_max_freq_khz_kobj_attr.attr; - ret = uncore_read_freq(data, &freq); + ret = uncore_read(data, &freq, UNCORE_INDEX_CURRENT_FREQ); if (!ret) data->uncore_attrs[index++] = &data->current_freq_khz_kobj_attr.attr; @@ -238,7 +208,8 @@ int uncore_freq_add_entry(struct uncore_data *data, int cpu) sprintf(data->name, "package_%02d_die_%02d", data->package_id, data->die_id); } - uncore_read(data, &data->initial_min_freq_khz, &data->initial_max_freq_khz); + uncore_read(data, &data->initial_min_freq_khz, UNCORE_INDEX_MIN_FREQ); + uncore_read(data, &data->initial_max_freq_khz, UNCORE_INDEX_MAX_FREQ); ret = create_attr_group(data, data->name); if (ret) { @@ -269,15 +240,15 @@ void uncore_freq_remove_die_entry(struct uncore_data *data) } EXPORT_SYMBOL_NS_GPL(uncore_freq_remove_die_entry, INTEL_UNCORE_FREQUENCY); -int uncore_freq_common_init(int (*read_control_freq)(struct uncore_data *data, unsigned int *min, unsigned int *max), - int (*write_control_freq)(struct uncore_data *data, unsigned int input, unsigned int set_max), - int (*read_freq)(struct uncore_data *data, unsigned int *freq)) +int uncore_freq_common_init(int (*read)(struct uncore_data *data, unsigned int *value, + enum uncore_index index), + int (*write)(struct uncore_data *data, unsigned int input, + enum uncore_index index)) { mutex_lock(&uncore_lock); - uncore_read = read_control_freq; - uncore_write = write_control_freq; - uncore_read_freq = read_freq; + uncore_read = read; + uncore_write = write; if (!uncore_root_kobj) { struct device *dev_root = bus_get_dev_root(&cpu_subsys); diff --git a/drivers/platform/x86/intel/uncore-frequency/uncore-frequency-common.h b/drivers/platform/x86/intel/uncore-frequency/uncore-frequency-common.h index 0e5bf507e555..4c245b945e4e 100644 --- a/drivers/platform/x86/intel/uncore-frequency/uncore-frequency-common.h +++ b/drivers/platform/x86/intel/uncore-frequency/uncore-frequency-common.h @@ -66,9 +66,16 @@ struct uncore_data { #define UNCORE_DOMAIN_ID_INVALID -1 -int uncore_freq_common_init(int (*read_control_freq)(struct uncore_data *data, unsigned int *min, unsigned int *max), - int (*write_control_freq)(struct uncore_data *data, unsigned int input, unsigned int min_max), - int (*uncore_read_freq)(struct uncore_data *data, unsigned int *freq)); +enum uncore_index { + UNCORE_INDEX_MIN_FREQ, + UNCORE_INDEX_MAX_FREQ, + UNCORE_INDEX_CURRENT_FREQ, +}; + +int uncore_freq_common_init(int (*read)(struct uncore_data *data, unsigned int *value, + enum uncore_index index), + int (*write)(struct uncore_data *data, unsigned int input, + enum uncore_index index)); void uncore_freq_common_exit(void); int uncore_freq_add_entry(struct uncore_data *data, int cpu); void uncore_freq_remove_die_entry(struct uncore_data *data); 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 bb8e72deb354..9fa3037c03d1 100644 --- a/drivers/platform/x86/intel/uncore-frequency/uncore-frequency-tpmi.c +++ b/drivers/platform/x86/intel/uncore-frequency/uncore-frequency-tpmi.c @@ -69,26 +69,31 @@ struct tpmi_uncore_struct { bool write_blocked; }; -#define UNCORE_GENMASK_MIN_RATIO GENMASK_ULL(21, 15) -#define UNCORE_GENMASK_MAX_RATIO GENMASK_ULL(14, 8) -#define UNCORE_GENMASK_CURRENT_RATIO GENMASK_ULL(6, 0) +/* Bit definitions for STATUS register */ +#define UNCORE_CURRENT_RATIO_MASK GENMASK_ULL(6, 0) + +/* Bit definitions for CONTROL register */ +#define UNCORE_MAX_RATIO_MASK GENMASK_ULL(14, 8) +#define UNCORE_MIN_RATIO_MASK GENMASK_ULL(21, 15) /* Helper function to read MMIO offset for max/min control frequency */ static void read_control_freq(struct tpmi_uncore_cluster_info *cluster_info, - unsigned int *min, unsigned int *max) + unsigned int *value, enum uncore_index index) { u64 control; control = readq(cluster_info->cluster_base + UNCORE_CONTROL_INDEX); - *max = FIELD_GET(UNCORE_GENMASK_MAX_RATIO, control) * UNCORE_FREQ_KHZ_MULTIPLIER; - *min = FIELD_GET(UNCORE_GENMASK_MIN_RATIO, control) * UNCORE_FREQ_KHZ_MULTIPLIER; + if (index == UNCORE_INDEX_MAX_FREQ) + *value = FIELD_GET(UNCORE_MAX_RATIO_MASK, control) * UNCORE_FREQ_KHZ_MULTIPLIER; + else + *value = FIELD_GET(UNCORE_MIN_RATIO_MASK, control) * UNCORE_FREQ_KHZ_MULTIPLIER; } -#define UNCORE_MAX_RATIO FIELD_MAX(UNCORE_GENMASK_MAX_RATIO) +#define UNCORE_MAX_RATIO FIELD_MAX(UNCORE_MAX_RATIO_MASK) -/* Callback for sysfs read for max/min frequencies. Called under mutex locks */ -static int uncore_read_control_freq(struct uncore_data *data, unsigned int *min, - unsigned int *max) +/* Helper for sysfs read for max/min frequencies. Called under mutex locks */ +static int uncore_read_control_freq(struct uncore_data *data, unsigned int *value, + enum uncore_index index) { struct tpmi_uncore_cluster_info *cluster_info; @@ -96,10 +101,11 @@ static int uncore_read_control_freq(struct uncore_data *data, unsigned int *min, if (cluster_info->root_domain) { struct tpmi_uncore_struct *uncore_root = cluster_info->uncore_root; - int i, _min = 0, _max = 0; + unsigned int min, max, v; + int i; - *min = UNCORE_MAX_RATIO * UNCORE_FREQ_KHZ_MULTIPLIER; - *max = 0; + min = UNCORE_MAX_RATIO * UNCORE_FREQ_KHZ_MULTIPLIER; + max = 0; /* * Get the max/min by looking at each cluster. Get the lowest @@ -110,35 +116,41 @@ static int uncore_read_control_freq(struct uncore_data *data, unsigned int *min, for (j = 0; j < uncore_root->pd_info[i].cluster_count; ++j) { read_control_freq(&uncore_root->pd_info[i].cluster_infos[j], - &_min, &_max); - if (*min > _min) - *min = _min; - if (*max < _max) - *max = _max; + &v, index); + if (v < min) + min = v; + if (v > max) + max = v; } } + + if (index == UNCORE_INDEX_MIN_FREQ) + *value = min; + else + *value = max; + return 0; } - read_control_freq(cluster_info, min, max); + read_control_freq(cluster_info, value, index); return 0; } /* Helper function to write MMIO offset for max/min control frequency */ static void write_control_freq(struct tpmi_uncore_cluster_info *cluster_info, unsigned int input, - unsigned int min_max) + unsigned int index) { u64 control; control = readq(cluster_info->cluster_base + UNCORE_CONTROL_INDEX); - if (min_max) { - control &= ~UNCORE_GENMASK_MAX_RATIO; - control |= FIELD_PREP(UNCORE_GENMASK_MAX_RATIO, input); + if (index == UNCORE_INDEX_MAX_FREQ) { + control &= ~UNCORE_MAX_RATIO_MASK; + control |= FIELD_PREP(UNCORE_MAX_RATIO_MASK, input); } else { - control &= ~UNCORE_GENMASK_MIN_RATIO; - control |= FIELD_PREP(UNCORE_GENMASK_MIN_RATIO, input); + control &= ~UNCORE_MIN_RATIO_MASK; + control |= FIELD_PREP(UNCORE_MIN_RATIO_MASK, input); } writeq(control, (cluster_info->cluster_base + UNCORE_CONTROL_INDEX)); @@ -146,7 +158,7 @@ static void write_control_freq(struct tpmi_uncore_cluster_info *cluster_info, un /* Callback for sysfs write for max/min frequencies. Called under mutex locks */ static int uncore_write_control_freq(struct uncore_data *data, unsigned int input, - unsigned int min_max) + enum uncore_index index) { struct tpmi_uncore_cluster_info *cluster_info; struct tpmi_uncore_struct *uncore_root; @@ -171,10 +183,10 @@ static int uncore_write_control_freq(struct uncore_data *data, unsigned int inpu for (j = 0; j < uncore_root->pd_info[i].cluster_count; ++j) write_control_freq(&uncore_root->pd_info[i].cluster_infos[j], - input, min_max); + input, index); } - if (min_max) + if (index == UNCORE_INDEX_MAX_FREQ) uncore_root->max_ratio = input; else uncore_root->min_ratio = input; @@ -182,18 +194,20 @@ static int uncore_write_control_freq(struct uncore_data *data, unsigned int inpu return 0; } - if (min_max && uncore_root->max_ratio && uncore_root->max_ratio < input) + if (index == UNCORE_INDEX_MAX_FREQ && uncore_root->max_ratio && + uncore_root->max_ratio < input) return -EINVAL; - if (!min_max && uncore_root->min_ratio && uncore_root->min_ratio > input) + if (index == UNCORE_INDEX_MIN_FREQ && uncore_root->min_ratio && + uncore_root->min_ratio > input) return -EINVAL; - write_control_freq(cluster_info, input, min_max); + write_control_freq(cluster_info, input, index); return 0; } -/* Callback for sysfs read for the current uncore frequency. Called under mutex locks */ +/* Helper for sysfs read for the current uncore frequency. Called under mutex locks */ static int uncore_read_freq(struct uncore_data *data, unsigned int *freq) { struct tpmi_uncore_cluster_info *cluster_info; @@ -204,11 +218,29 @@ static int uncore_read_freq(struct uncore_data *data, unsigned int *freq) return -ENODATA; status = readq((u8 __iomem *)cluster_info->cluster_base + UNCORE_STATUS_INDEX); - *freq = FIELD_GET(UNCORE_GENMASK_CURRENT_RATIO, status) * UNCORE_FREQ_KHZ_MULTIPLIER; + *freq = FIELD_GET(UNCORE_CURRENT_RATIO_MASK, status) * UNCORE_FREQ_KHZ_MULTIPLIER; return 0; } +/* Callback for sysfs read for TPMI uncore values. Called under mutex locks. */ +static int uncore_read(struct uncore_data *data, unsigned int *value, enum uncore_index index) +{ + switch (index) { + case UNCORE_INDEX_MIN_FREQ: + case UNCORE_INDEX_MAX_FREQ: + return uncore_read_control_freq(data, value, index); + + case UNCORE_INDEX_CURRENT_FREQ: + return uncore_read_freq(data, value); + + default: + break; + } + + return -EOPNOTSUPP; +} + static void remove_cluster_entries(struct tpmi_uncore_struct *tpmi_uncore) { int i; @@ -259,8 +291,7 @@ static int uncore_probe(struct auxiliary_device *auxdev, const struct auxiliary_ return -EINVAL; /* Register callbacks to uncore core */ - ret = uncore_freq_common_init(uncore_read_control_freq, uncore_write_control_freq, - uncore_read_freq); + ret = uncore_freq_common_init(uncore_read, uncore_write_control_freq); if (ret) return ret; diff --git a/drivers/platform/x86/intel/uncore-frequency/uncore-frequency.c b/drivers/platform/x86/intel/uncore-frequency/uncore-frequency.c index b89c0dda9e5d..a450b8a6bcec 100644 --- a/drivers/platform/x86/intel/uncore-frequency/uncore-frequency.c +++ b/drivers/platform/x86/intel/uncore-frequency/uncore-frequency.c @@ -14,6 +14,7 @@ * Author: Srinivas Pandruvada <srinivas.pandruvada@linux.intel.com> */ +#include <linux/bitfield.h> #include <linux/cpu.h> #include <linux/module.h> #include <linux/slab.h> @@ -36,8 +37,13 @@ static enum cpuhp_state uncore_hp_state __read_mostly; #define MSR_UNCORE_PERF_STATUS 0x621 #define UNCORE_FREQ_KHZ_MULTIPLIER 100000 -static int uncore_read_control_freq(struct uncore_data *data, unsigned int *min, - unsigned int *max) +#define UNCORE_MAX_RATIO_MASK GENMASK_ULL(6, 0) +#define UNCORE_MIN_RATIO_MASK GENMASK_ULL(14, 8) + +#define UNCORE_CURRENT_RATIO_MASK GENMASK_ULL(6, 0) + +static int uncore_read_control_freq(struct uncore_data *data, unsigned int *value, + enum uncore_index index) { u64 cap; int ret; @@ -49,20 +55,22 @@ static int uncore_read_control_freq(struct uncore_data *data, unsigned int *min, if (ret) return ret; - *max = (cap & 0x7F) * UNCORE_FREQ_KHZ_MULTIPLIER; - *min = ((cap & GENMASK(14, 8)) >> 8) * UNCORE_FREQ_KHZ_MULTIPLIER; + if (index == UNCORE_INDEX_MAX_FREQ) + *value = FIELD_GET(UNCORE_MAX_RATIO_MASK, cap) * UNCORE_FREQ_KHZ_MULTIPLIER; + else + *value = FIELD_GET(UNCORE_MIN_RATIO_MASK, cap) * UNCORE_FREQ_KHZ_MULTIPLIER; return 0; } static int uncore_write_control_freq(struct uncore_data *data, unsigned int input, - unsigned int min_max) + enum uncore_index index) { int ret; u64 cap; input /= UNCORE_FREQ_KHZ_MULTIPLIER; - if (!input || input > 0x7F) + if (!input || input > FIELD_MAX(UNCORE_MAX_RATIO_MASK)) return -EINVAL; if (data->control_cpu < 0) @@ -72,12 +80,12 @@ static int uncore_write_control_freq(struct uncore_data *data, unsigned int inpu if (ret) return ret; - if (min_max) { - cap &= ~0x7F; - cap |= input; + if (index == UNCORE_INDEX_MAX_FREQ) { + cap &= ~UNCORE_MAX_RATIO_MASK; + cap |= FIELD_PREP(UNCORE_MAX_RATIO_MASK, input); } else { - cap &= ~GENMASK(14, 8); - cap |= (input << 8); + cap &= ~UNCORE_MIN_RATIO_MASK; + cap |= FIELD_PREP(UNCORE_MIN_RATIO_MASK, input); } ret = wrmsrl_on_cpu(data->control_cpu, MSR_UNCORE_RATIO_LIMIT, cap); @@ -101,11 +109,28 @@ static int uncore_read_freq(struct uncore_data *data, unsigned int *freq) if (ret) return ret; - *freq = (ratio & 0x7F) * UNCORE_FREQ_KHZ_MULTIPLIER; + *freq = FIELD_GET(UNCORE_CURRENT_RATIO_MASK, ratio) * UNCORE_FREQ_KHZ_MULTIPLIER; return 0; } +static int uncore_read(struct uncore_data *data, unsigned int *value, enum uncore_index index) +{ + switch (index) { + case UNCORE_INDEX_MIN_FREQ: + case UNCORE_INDEX_MAX_FREQ: + return uncore_read_control_freq(data, value, index); + + case UNCORE_INDEX_CURRENT_FREQ: + return uncore_read_freq(data, value); + + default: + break; + } + + return -EOPNOTSUPP; +} + /* Caller provides protection */ static struct uncore_data *uncore_get_instance(unsigned int cpu) { @@ -197,34 +222,34 @@ static struct notifier_block uncore_pm_nb = { }; static const struct x86_cpu_id intel_uncore_cpu_ids[] = { - X86_MATCH_INTEL_FAM6_MODEL(BROADWELL_G, NULL), - X86_MATCH_INTEL_FAM6_MODEL(BROADWELL_X, NULL), - X86_MATCH_INTEL_FAM6_MODEL(BROADWELL_D, NULL), - X86_MATCH_INTEL_FAM6_MODEL(SKYLAKE_X, NULL), - X86_MATCH_INTEL_FAM6_MODEL(ICELAKE_X, NULL), - 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), - X86_MATCH_INTEL_FAM6_MODEL(RAPTORLAKE_P, NULL), - X86_MATCH_INTEL_FAM6_MODEL(RAPTORLAKE_S, NULL), - X86_MATCH_INTEL_FAM6_MODEL(METEORLAKE, NULL), - X86_MATCH_INTEL_FAM6_MODEL(METEORLAKE_L, NULL), - X86_MATCH_INTEL_FAM6_MODEL(ARROWLAKE, NULL), - X86_MATCH_INTEL_FAM6_MODEL(ARROWLAKE_H, NULL), - X86_MATCH_INTEL_FAM6_MODEL(LUNARLAKE_M, NULL), + X86_MATCH_VFM(INTEL_BROADWELL_G, NULL), + X86_MATCH_VFM(INTEL_BROADWELL_X, NULL), + X86_MATCH_VFM(INTEL_BROADWELL_D, NULL), + X86_MATCH_VFM(INTEL_SKYLAKE_X, NULL), + X86_MATCH_VFM(INTEL_ICELAKE_X, NULL), + X86_MATCH_VFM(INTEL_ICELAKE_D, NULL), + X86_MATCH_VFM(INTEL_SAPPHIRERAPIDS_X, NULL), + X86_MATCH_VFM(INTEL_EMERALDRAPIDS_X, NULL), + X86_MATCH_VFM(INTEL_KABYLAKE, NULL), + X86_MATCH_VFM(INTEL_KABYLAKE_L, NULL), + X86_MATCH_VFM(INTEL_COMETLAKE, NULL), + X86_MATCH_VFM(INTEL_COMETLAKE_L, NULL), + X86_MATCH_VFM(INTEL_CANNONLAKE_L, NULL), + X86_MATCH_VFM(INTEL_ICELAKE, NULL), + X86_MATCH_VFM(INTEL_ICELAKE_L, NULL), + X86_MATCH_VFM(INTEL_ROCKETLAKE, NULL), + X86_MATCH_VFM(INTEL_TIGERLAKE, NULL), + X86_MATCH_VFM(INTEL_TIGERLAKE_L, NULL), + X86_MATCH_VFM(INTEL_ALDERLAKE, NULL), + X86_MATCH_VFM(INTEL_ALDERLAKE_L, NULL), + X86_MATCH_VFM(INTEL_RAPTORLAKE, NULL), + X86_MATCH_VFM(INTEL_RAPTORLAKE_P, NULL), + X86_MATCH_VFM(INTEL_RAPTORLAKE_S, NULL), + X86_MATCH_VFM(INTEL_METEORLAKE, NULL), + X86_MATCH_VFM(INTEL_METEORLAKE_L, NULL), + X86_MATCH_VFM(INTEL_ARROWLAKE, NULL), + X86_MATCH_VFM(INTEL_ARROWLAKE_H, NULL), + X86_MATCH_VFM(INTEL_LUNARLAKE_M, NULL), {} }; MODULE_DEVICE_TABLE(x86cpu, intel_uncore_cpu_ids); @@ -248,8 +273,7 @@ static int __init intel_uncore_init(void) if (!uncore_instances) return -ENOMEM; - ret = uncore_freq_common_init(uncore_read_control_freq, uncore_write_control_freq, - uncore_read_freq); + ret = uncore_freq_common_init(uncore_read, uncore_write_control_freq); if (ret) goto err_free; diff --git a/drivers/platform/x86/intel_ips.c b/drivers/platform/x86/intel_ips.c index 73ec4460a151..c62c3c4ec20a 100644 --- a/drivers/platform/x86/intel_ips.c +++ b/drivers/platform/x86/intel_ips.c @@ -62,6 +62,7 @@ #include <drm/i915_drm.h> #include <asm/msr.h> #include <asm/processor.h> +#include <asm/cpu_device_id.h> #include "intel_ips.h" #include <linux/io-64-nonatomic-lo-hi.h> @@ -1284,7 +1285,7 @@ static struct ips_mcp_limits *ips_detect_cpu(struct ips_driver *ips) struct ips_mcp_limits *limits = NULL; u16 tdp; - if (!(boot_cpu_data.x86 == 6 && boot_cpu_data.x86_model == 37)) { + if (!(boot_cpu_data.x86_vfm == INTEL_WESTMERE)) { dev_info(ips->dev, "Non-IPS CPU detected.\n"); return NULL; } diff --git a/drivers/platform/x86/intel_scu_wdt.c b/drivers/platform/x86/intel_scu_wdt.c index a5031a25632e..d0b6637861d3 100644 --- a/drivers/platform/x86/intel_scu_wdt.c +++ b/drivers/platform/x86/intel_scu_wdt.c @@ -50,7 +50,7 @@ static struct intel_mid_wdt_pdata tangier_pdata = { }; static const struct x86_cpu_id intel_mid_cpu_ids[] = { - X86_MATCH_INTEL_FAM6_MODEL(ATOM_SILVERMONT_MID, &tangier_pdata), + X86_MATCH_VFM(INTEL_ATOM_SILVERMONT_MID, &tangier_pdata), {} }; diff --git a/drivers/platform/x86/p2sb.c b/drivers/platform/x86/p2sb.c index 3bf5d2243491..31f38309b389 100644 --- a/drivers/platform/x86/p2sb.c +++ b/drivers/platform/x86/p2sb.c @@ -24,7 +24,7 @@ #define SPI_DEVFN_GOLDMONT PCI_DEVFN(13, 2) static const struct x86_cpu_id p2sb_cpu_ids[] = { - X86_MATCH_INTEL_FAM6_MODEL(ATOM_GOLDMONT, P2SB_DEVFN_GOLDMONT), + X86_MATCH_VFM(INTEL_ATOM_GOLDMONT, P2SB_DEVFN_GOLDMONT), {} }; diff --git a/drivers/platform/x86/serial-multi-instantiate.c b/drivers/platform/x86/serial-multi-instantiate.c index 97b9c6392230..3be016cfe601 100644 --- a/drivers/platform/x86/serial-multi-instantiate.c +++ b/drivers/platform/x86/serial-multi-instantiate.c @@ -131,7 +131,7 @@ static int smi_spi_probe(struct platform_device *pdev, struct smi *smi, ctlr = spi_dev->controller; - strscpy(spi_dev->modalias, inst_array[i].type, sizeof(spi_dev->modalias)); + strscpy(spi_dev->modalias, inst_array[i].type); ret = smi_get_irq(pdev, adev, &inst_array[i]); if (ret < 0) { @@ -205,7 +205,7 @@ static int smi_i2c_probe(struct platform_device *pdev, struct smi *smi, for (i = 0; i < count && inst_array[i].type; i++) { memset(&board_info, 0, sizeof(board_info)); - strscpy(board_info.type, inst_array[i].type, I2C_NAME_SIZE); + strscpy(board_info.type, inst_array[i].type); snprintf(name, sizeof(name), "%s-%s.%d", dev_name(dev), inst_array[i].type, i); board_info.dev_name = name; diff --git a/drivers/platform/x86/think-lmi.c b/drivers/platform/x86/think-lmi.c index 0f2264bb7577..4cfb53206cb8 100644 --- a/drivers/platform/x86/think-lmi.c +++ b/drivers/platform/x86/think-lmi.c @@ -1508,7 +1508,7 @@ static struct tlmi_pwd_setting *tlmi_create_auth(const char *pwd_type, if (!new_pwd) return NULL; - strscpy(new_pwd->kbdlang, "us", TLMI_LANG_MAXLEN); + strscpy(new_pwd->kbdlang, "us"); new_pwd->encoding = TLMI_ENCODING_ASCII; new_pwd->pwd_type = pwd_type; new_pwd->role = pwd_role; @@ -1582,7 +1582,7 @@ static int tlmi_analyze(void) goto fail_clear_attr; } setting->index = i; - strscpy(setting->display_name, item, TLMI_SETTINGS_MAXLEN); + strscpy(setting->display_name, item); /* If BIOS selections supported, load those */ if (tlmi_priv.can_get_bios_selections) { ret = tlmi_get_bios_selections(setting->display_name, diff --git a/drivers/platform/x86/thinkpad_acpi.c b/drivers/platform/x86/thinkpad_acpi.c index 397b409064c9..f269ca1ff771 100644 --- a/drivers/platform/x86/thinkpad_acpi.c +++ b/drivers/platform/x86/thinkpad_acpi.c @@ -7416,10 +7416,8 @@ static int __init volume_create_alsa_mixer(void) data = card->private_data; data->card = card; - strscpy(card->driver, TPACPI_ALSA_DRVNAME, - sizeof(card->driver)); - strscpy(card->shortname, TPACPI_ALSA_SHRTNAME, - sizeof(card->shortname)); + strscpy(card->driver, TPACPI_ALSA_DRVNAME); + strscpy(card->shortname, TPACPI_ALSA_SHRTNAME); snprintf(card->mixername, sizeof(card->mixername), "ThinkPad EC %s", (thinkpad_id.ec_version_str) ? thinkpad_id.ec_version_str : "(unknown)"); diff --git a/drivers/platform/x86/wmi.c b/drivers/platform/x86/wmi.c index d21f3fa25823..6bfae28b962a 100644 --- a/drivers/platform/x86/wmi.c +++ b/drivers/platform/x86/wmi.c @@ -772,11 +772,39 @@ static ssize_t expensive_show(struct device *dev, } static DEVICE_ATTR_RO(expensive); +static ssize_t driver_override_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct wmi_device *wdev = to_wmi_device(dev); + ssize_t ret; + + device_lock(dev); + ret = sysfs_emit(buf, "%s\n", wdev->driver_override); + device_unlock(dev); + + return ret; +} + +static ssize_t driver_override_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct wmi_device *wdev = to_wmi_device(dev); + int ret; + + ret = driver_set_override(dev, &wdev->driver_override, buf, count); + if (ret < 0) + return ret; + + return count; +} +static DEVICE_ATTR_RW(driver_override); + static struct attribute *wmi_attrs[] = { &dev_attr_modalias.attr, &dev_attr_guid.attr, &dev_attr_instance_count.attr, &dev_attr_expensive.attr, + &dev_attr_driver_override.attr, NULL }; ATTRIBUTE_GROUPS(wmi); @@ -845,6 +873,7 @@ static void wmi_dev_release(struct device *dev) { struct wmi_block *wblock = dev_to_wblock(dev); + kfree(wblock->dev.driver_override); kfree(wblock); } @@ -854,6 +883,10 @@ static int wmi_dev_match(struct device *dev, struct device_driver *driver) struct wmi_block *wblock = dev_to_wblock(dev); const struct wmi_device_id *id = wmi_driver->id_table; + /* When driver_override is set, only bind to the matching driver */ + if (wblock->dev.driver_override) + return !strcmp(wblock->dev.driver_override, driver->name); + if (id == NULL) return 0; diff --git a/include/linux/intel_tpmi.h b/include/linux/intel_tpmi.h index 1e880cb0f454..ff480b47ae64 100644 --- a/include/linux/intel_tpmi.h +++ b/include/linux/intel_tpmi.h @@ -21,6 +21,7 @@ enum intel_tpmi_id { TPMI_ID_PEM = 1, /* Power and Perf excursion Monitor */ TPMI_ID_UNCORE = 2, /* Uncore Frequency Scaling */ TPMI_ID_SST = 5, /* Speed Select Technology */ + TPMI_ID_PLR = 0xc, /* Performance Limit Reasons */ TPMI_CONTROL_ID = 0x80, /* Special ID for getting feature status */ TPMI_INFO_ID = 0x81, /* Special ID for PCI BDF and Package ID information */ }; @@ -53,4 +54,5 @@ struct resource *tpmi_get_resource_at_index(struct auxiliary_device *auxdev, int int tpmi_get_resource_count(struct auxiliary_device *auxdev); int tpmi_get_feature_status(struct auxiliary_device *auxdev, int feature_id, bool *read_blocked, bool *write_blocked); +struct dentry *tpmi_get_debugfs_dir(struct auxiliary_device *auxdev); #endif diff --git a/include/linux/platform_data/lenovo-yoga-c630.h b/include/linux/platform_data/lenovo-yoga-c630.h new file mode 100644 index 000000000000..5d1f9fb33cfc --- /dev/null +++ b/include/linux/platform_data/lenovo-yoga-c630.h @@ -0,0 +1,44 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2022-2024, Linaro Ltd + * Authors: + * Bjorn Andersson + * Dmitry Baryshkov + */ + +#ifndef _LENOVO_YOGA_C630_DATA_H +#define _LENOVO_YOGA_C630_DATA_H + +struct yoga_c630_ec; +struct notifier_block; + +#define YOGA_C630_MOD_NAME "lenovo_yoga_c630" + +#define YOGA_C630_DEV_UCSI "ucsi" +#define YOGA_C630_DEV_PSY "psy" + +int yoga_c630_ec_read8(struct yoga_c630_ec *ec, u8 addr); +int yoga_c630_ec_read16(struct yoga_c630_ec *ec, u8 addr); + +int yoga_c630_ec_register_notify(struct yoga_c630_ec *ec, struct notifier_block *nb); +void yoga_c630_ec_unregister_notify(struct yoga_c630_ec *ec, struct notifier_block *nb); + +#define YOGA_C630_UCSI_WRITE_SIZE 8 +#define YOGA_C630_UCSI_CCI_SIZE 4 +#define YOGA_C630_UCSI_DATA_SIZE 16 +#define YOGA_C630_UCSI_READ_SIZE (YOGA_C630_UCSI_CCI_SIZE + YOGA_C630_UCSI_DATA_SIZE) + +u16 yoga_c630_ec_ucsi_get_version(struct yoga_c630_ec *ec); +int yoga_c630_ec_ucsi_write(struct yoga_c630_ec *ec, + const u8 req[YOGA_C630_UCSI_WRITE_SIZE]); +int yoga_c630_ec_ucsi_read(struct yoga_c630_ec *ec, + u8 resp[YOGA_C630_UCSI_READ_SIZE]); + +#define LENOVO_EC_EVENT_USB 0x20 +#define LENOVO_EC_EVENT_UCSI 0x21 +#define LENOVO_EC_EVENT_HPD 0x22 +#define LENOVO_EC_EVENT_BAT_STATUS 0x24 +#define LENOVO_EC_EVENT_BAT_INFO 0x25 +#define LENOVO_EC_EVENT_BAT_ADPT_STATUS 0x37 + +#endif diff --git a/include/linux/platform_data/x86/asus-wmi.h b/include/linux/platform_data/x86/asus-wmi.h index 3eb5cd6773ad..0aeeae1c1943 100644 --- a/include/linux/platform_data/x86/asus-wmi.h +++ b/include/linux/platform_data/x86/asus-wmi.h @@ -51,6 +51,10 @@ #define ASUS_WMI_DEVID_LED6 0x00020016 #define ASUS_WMI_DEVID_MICMUTE_LED 0x00040017 +/* Disable Camera LED */ +#define ASUS_WMI_DEVID_CAMERA_LED_NEG 0x00060078 /* 0 = on (unused) */ +#define ASUS_WMI_DEVID_CAMERA_LED 0x00060079 /* 1 = on */ + /* Backlight and Brightness */ #define ASUS_WMI_DEVID_ALS_ENABLE 0x00050001 /* Ambient Light Sensor */ #define ASUS_WMI_DEVID_BACKLIGHT 0x00050011 diff --git a/include/linux/wmi.h b/include/linux/wmi.h index 63cca3b58d6d..3275470b5531 100644 --- a/include/linux/wmi.h +++ b/include/linux/wmi.h @@ -16,12 +16,16 @@ * struct wmi_device - WMI device structure * @dev: Device associated with this WMI device * @setable: True for devices implementing the Set Control Method + * @driver_override: Driver name to force a match; do not set directly, + * because core frees it; use driver_set_override() to + * set or clear it. * * This represents WMI devices discovered by the WMI driver core. */ struct wmi_device { struct device dev; bool setable; + const char *driver_override; }; /** diff --git a/tools/power/x86/intel-speed-select/isst-config.c b/tools/power/x86/intel-speed-select/isst-config.c index 5899c27c2e2e..5127be34869e 100644 --- a/tools/power/x86/intel-speed-select/isst-config.c +++ b/tools/power/x86/intel-speed-select/isst-config.c @@ -16,7 +16,7 @@ struct process_cmd_struct { int arg; }; -static const char *version_str = "v1.19"; +static const char *version_str = "v1.20"; static const int supported_api_ver = 3; static struct isst_if_platform_info isst_platform_info; diff --git a/tools/power/x86/intel-speed-select/isst-core.c b/tools/power/x86/intel-speed-select/isst-core.c index 05efffbca3b7..e05561d00458 100644 --- a/tools/power/x86/intel-speed-select/isst-core.c +++ b/tools/power/x86/intel-speed-select/isst-core.c @@ -283,6 +283,8 @@ int isst_set_trl(struct isst_id *id, unsigned long long trl) return 0; } +#define MSR_TRL_FREQ_MULTIPLIER 100 + int isst_set_trl_from_current_tdp(struct isst_id *id, unsigned long long trl) { unsigned long long msr_trl; @@ -310,6 +312,10 @@ int isst_set_trl_from_current_tdp(struct isst_id *id, unsigned long long trl) for (i = 0; i < 8; ++i) { unsigned long long _trl = trl[i]; + /* MSR is always in 100 MHz unit */ + if (isst_get_disp_freq_multiplier() == 1) + _trl /= MSR_TRL_FREQ_MULTIPLIER; + msr_trl |= (_trl << (i * 8)); } } |