diff options
-rw-r--r-- | Documentation/admin-guide/laptops/thinkpad-acpi.rst | 7 | ||||
-rw-r--r-- | drivers/platform/mellanox/mlxbf-bootctl.c | 14 | ||||
-rw-r--r-- | drivers/platform/surface/surface_aggregator_registry.c | 7 | ||||
-rw-r--r-- | drivers/platform/x86/Kconfig | 6 | ||||
-rw-r--r-- | drivers/platform/x86/amd/Kconfig | 2 | ||||
-rw-r--r-- | drivers/platform/x86/amd/hsmp.c | 584 | ||||
-rw-r--r-- | drivers/platform/x86/asus-wmi.c | 1 | ||||
-rw-r--r-- | drivers/platform/x86/dell/Kconfig | 3 | ||||
-rw-r--r-- | drivers/platform/x86/dell/dell-laptop.c | 2 | ||||
-rw-r--r-- | drivers/platform/x86/dell/dell-wmi-privacy.c | 1 | ||||
-rw-r--r-- | drivers/platform/x86/hp/hp-wmi.c | 71 | ||||
-rw-r--r-- | drivers/platform/x86/huawei-wmi.c | 1 | ||||
-rw-r--r-- | drivers/platform/x86/ibm_rtl.c | 2 | ||||
-rw-r--r-- | drivers/platform/x86/intel/ifs/load.c | 2 | ||||
-rw-r--r-- | drivers/platform/x86/intel/ifs/runtest.c | 101 | ||||
-rw-r--r-- | drivers/platform/x86/silicom-platform.c | 7 | ||||
-rw-r--r-- | drivers/platform/x86/thinkpad_acpi.c | 29 | ||||
-rw-r--r-- | drivers/platform/x86/wmi.c | 61 | ||||
-rw-r--r-- | include/trace/events/intel_ifs.h | 12 |
19 files changed, 669 insertions, 244 deletions
diff --git a/Documentation/admin-guide/laptops/thinkpad-acpi.rst b/Documentation/admin-guide/laptops/thinkpad-acpi.rst index 98d304010170..7f674a6cfa8a 100644 --- a/Documentation/admin-guide/laptops/thinkpad-acpi.rst +++ b/Documentation/admin-guide/laptops/thinkpad-acpi.rst @@ -444,7 +444,9 @@ event code Key Notes 0x1008 0x07 FN+F8 IBM: toggle screen expand Lenovo: configure UltraNav, - or toggle screen expand + or toggle screen expand. + On newer platforms (2024+) + replaced by 0x131f (see below) 0x1009 0x08 FN+F9 - @@ -504,6 +506,9 @@ event code Key Notes 0x1019 0x18 unknown +0x131f ... FN+F8 Platform Mode change. + Implemented in driver. + ... ... ... 0x1020 0x1F unknown diff --git a/drivers/platform/mellanox/mlxbf-bootctl.c b/drivers/platform/mellanox/mlxbf-bootctl.c index c1aef3a8fb2d..dd5f370c3168 100644 --- a/drivers/platform/mellanox/mlxbf-bootctl.c +++ b/drivers/platform/mellanox/mlxbf-bootctl.c @@ -463,7 +463,7 @@ static ssize_t large_icm_show(struct device *dev, if (res.a0) return -EPERM; - return snprintf(buf, PAGE_SIZE, "0x%lx", res.a1); + return sysfs_emit(buf, "0x%lx", res.a1); } static ssize_t large_icm_store(struct device *dev, @@ -581,7 +581,7 @@ static ssize_t opn_show(struct device *dev, } mutex_unlock(&mfg_ops_lock); - return snprintf(buf, PAGE_SIZE, "%s", (char *)opn_data); + return sysfs_emit(buf, "%s", (char *)opn_data); } static ssize_t opn_store(struct device *dev, @@ -632,7 +632,7 @@ static ssize_t sku_show(struct device *dev, } mutex_unlock(&mfg_ops_lock); - return snprintf(buf, PAGE_SIZE, "%s", (char *)sku_data); + return sysfs_emit(buf, "%s", (char *)sku_data); } static ssize_t sku_store(struct device *dev, @@ -683,7 +683,7 @@ static ssize_t modl_show(struct device *dev, } mutex_unlock(&mfg_ops_lock); - return snprintf(buf, PAGE_SIZE, "%s", (char *)modl_data); + return sysfs_emit(buf, "%s", (char *)modl_data); } static ssize_t modl_store(struct device *dev, @@ -734,7 +734,7 @@ static ssize_t sn_show(struct device *dev, } mutex_unlock(&mfg_ops_lock); - return snprintf(buf, PAGE_SIZE, "%s", (char *)sn_data); + return sysfs_emit(buf, "%s", (char *)sn_data); } static ssize_t sn_store(struct device *dev, @@ -785,7 +785,7 @@ static ssize_t uuid_show(struct device *dev, } mutex_unlock(&mfg_ops_lock); - return snprintf(buf, PAGE_SIZE, "%s", (char *)uuid_data); + return sysfs_emit(buf, "%s", (char *)uuid_data); } static ssize_t uuid_store(struct device *dev, @@ -836,7 +836,7 @@ static ssize_t rev_show(struct device *dev, } mutex_unlock(&mfg_ops_lock); - return snprintf(buf, PAGE_SIZE, "%s", (char *)rev_data); + return sysfs_emit(buf, "%s", (char *)rev_data); } static ssize_t rev_store(struct device *dev, diff --git a/drivers/platform/surface/surface_aggregator_registry.c b/drivers/platform/surface/surface_aggregator_registry.c index aeb3feae40ff..035d6b4105cd 100644 --- a/drivers/platform/surface/surface_aggregator_registry.c +++ b/drivers/platform/surface/surface_aggregator_registry.c @@ -74,6 +74,12 @@ static const struct software_node ssam_node_tmp_pprof = { .parent = &ssam_node_root, }; +/* Fan speed function. */ +static const struct software_node ssam_node_fan_speed = { + .name = "ssam:01:05:01:01:01", + .parent = &ssam_node_root, +}; + /* Tablet-mode switch via KIP subsystem. */ static const struct software_node ssam_node_kip_tablet_switch = { .name = "ssam:01:0e:01:00:01", @@ -305,6 +311,7 @@ static const struct software_node *ssam_node_group_sp9[] = { &ssam_node_bat_ac, &ssam_node_bat_main, &ssam_node_tmp_pprof, + &ssam_node_fan_speed, &ssam_node_pos_tablet_switch, &ssam_node_hid_kip_keyboard, &ssam_node_hid_kip_penstash, diff --git a/drivers/platform/x86/Kconfig b/drivers/platform/x86/Kconfig index bdd302274b9a..6dbd40e2aeda 100644 --- a/drivers/platform/x86/Kconfig +++ b/drivers/platform/x86/Kconfig @@ -56,8 +56,6 @@ config HUAWEI_WMI depends on INPUT select INPUT_SPARSEKMAP select LEDS_CLASS - select LEDS_TRIGGERS - select LEDS_TRIGGER_AUDIO select NEW_LEDS help This driver provides support for Huawei WMI hotkeys, battery charge @@ -269,8 +267,6 @@ config ASUS_WMI select INPUT_SPARSEKMAP select LEDS_CLASS select NEW_LEDS - select LEDS_TRIGGERS - select LEDS_TRIGGER_AUDIO select ACPI_PLATFORM_PROFILE help Say Y here if you have a WMI aware Asus laptop (like Eee PCs or new @@ -507,8 +503,6 @@ config THINKPAD_ACPI select NVRAM select NEW_LEDS select LEDS_CLASS - select LEDS_TRIGGERS - select LEDS_TRIGGER_AUDIO help This is a driver for the IBM and Lenovo ThinkPad laptops. It adds support for Fn-Fx key combinations, Bluetooth control, video diff --git a/drivers/platform/x86/amd/Kconfig b/drivers/platform/x86/amd/Kconfig index 54753213cc61..f88682d36447 100644 --- a/drivers/platform/x86/amd/Kconfig +++ b/drivers/platform/x86/amd/Kconfig @@ -8,7 +8,7 @@ source "drivers/platform/x86/amd/pmc/Kconfig" config AMD_HSMP tristate "AMD HSMP Driver" - depends on AMD_NB && X86_64 + depends on AMD_NB && X86_64 && ACPI help The driver provides a way for user space tools to monitor and manage system management functionality on EPYC server CPUs from AMD. diff --git a/drivers/platform/x86/amd/hsmp.c b/drivers/platform/x86/amd/hsmp.c index b55d80e29139..1927be901108 100644 --- a/drivers/platform/x86/amd/hsmp.c +++ b/drivers/platform/x86/amd/hsmp.c @@ -18,9 +18,11 @@ #include <linux/pci.h> #include <linux/platform_device.h> #include <linux/semaphore.h> +#include <linux/acpi.h> #define DRIVER_NAME "amd_hsmp" -#define DRIVER_VERSION "2.0" +#define DRIVER_VERSION "2.2" +#define ACPI_HSMP_DEVICE_HID "AMDI0097" /* HSMP Status / Error codes */ #define HSMP_STATUS_NOT_READY 0x00 @@ -40,9 +42,11 @@ * register into the SMN_INDEX register, and reads/writes the SMN_DATA reg. * Below are required SMN address for HSMP Mailbox register offsets in SMU address space */ -#define SMN_HSMP_MSG_ID 0x3B10534 -#define SMN_HSMP_MSG_RESP 0x3B10980 -#define SMN_HSMP_MSG_DATA 0x3B109E0 +#define SMN_HSMP_BASE 0x3B00000 +#define SMN_HSMP_MSG_ID 0x0010534 +#define SMN_HSMP_MSG_ID_F1A_M0H 0x0010934 +#define SMN_HSMP_MSG_RESP 0x0010980 +#define SMN_HSMP_MSG_DATA 0x00109E0 #define HSMP_INDEX_REG 0xc4 #define HSMP_DATA_REG 0xc8 @@ -53,41 +57,86 @@ #define HSMP_ATTR_GRP_NAME_SIZE 10 +/* These are the strings specified in ACPI table */ +#define MSG_IDOFF_STR "MsgIdOffset" +#define MSG_ARGOFF_STR "MsgArgOffset" +#define MSG_RESPOFF_STR "MsgRspOffset" + +#define MAX_AMD_SOCKETS 8 + +struct hsmp_mbaddr_info { + u32 base_addr; + u32 msg_id_off; + u32 msg_resp_off; + u32 msg_arg_off; + u32 size; +}; + struct hsmp_socket { struct bin_attribute hsmp_attr; + struct hsmp_mbaddr_info mbinfo; void __iomem *metric_tbl_addr; + void __iomem *virt_base_addr; struct semaphore hsmp_sem; char name[HSMP_ATTR_GRP_NAME_SIZE]; + struct pci_dev *root; + struct device *dev; u16 sock_ind; }; struct hsmp_plat_device { struct miscdevice hsmp_device; struct hsmp_socket *sock; - struct device *dev; u32 proto_ver; u16 num_sockets; + bool is_acpi_device; + bool is_probed; }; static struct hsmp_plat_device plat_dev; -static int amd_hsmp_rdwr(struct pci_dev *root, u32 address, - u32 *value, bool write) +static int amd_hsmp_pci_rdwr(struct hsmp_socket *sock, u32 offset, + u32 *value, bool write) { int ret; - ret = pci_write_config_dword(root, HSMP_INDEX_REG, address); + if (!sock->root) + return -ENODEV; + + ret = pci_write_config_dword(sock->root, HSMP_INDEX_REG, + sock->mbinfo.base_addr + offset); if (ret) return ret; - ret = (write ? pci_write_config_dword(root, HSMP_DATA_REG, *value) - : pci_read_config_dword(root, HSMP_DATA_REG, value)); + ret = (write ? pci_write_config_dword(sock->root, HSMP_DATA_REG, *value) + : pci_read_config_dword(sock->root, HSMP_DATA_REG, value)); return ret; } +static void amd_hsmp_acpi_rdwr(struct hsmp_socket *sock, u32 offset, + u32 *value, bool write) +{ + if (write) + iowrite32(*value, sock->virt_base_addr + offset); + else + *value = ioread32(sock->virt_base_addr + offset); +} + +static int amd_hsmp_rdwr(struct hsmp_socket *sock, u32 offset, + u32 *value, bool write) +{ + if (plat_dev.is_acpi_device) + amd_hsmp_acpi_rdwr(sock, offset, value, write); + else + return amd_hsmp_pci_rdwr(sock, offset, value, write); + + return 0; +} + /* - * Send a message to the HSMP port via PCI-e config space registers. + * Send a message to the HSMP port via PCI-e config space registers + * or by writing to MMIO space. * * The caller is expected to zero out any unused arguments. * If a response is expected, the number of response words should be greater than 0. @@ -95,16 +144,19 @@ static int amd_hsmp_rdwr(struct pci_dev *root, u32 address, * Returns 0 for success and populates the requested number of arguments. * Returns a negative error code for failure. */ -static int __hsmp_send_message(struct pci_dev *root, struct hsmp_message *msg) +static int __hsmp_send_message(struct hsmp_socket *sock, struct hsmp_message *msg) { + struct hsmp_mbaddr_info *mbinfo; unsigned long timeout, short_sleep; u32 mbox_status; u32 index; int ret; + mbinfo = &sock->mbinfo; + /* Clear the status register */ mbox_status = HSMP_STATUS_NOT_READY; - ret = amd_hsmp_rdwr(root, SMN_HSMP_MSG_RESP, &mbox_status, HSMP_WR); + ret = amd_hsmp_rdwr(sock, mbinfo->msg_resp_off, &mbox_status, HSMP_WR); if (ret) { pr_err("Error %d clearing mailbox status register\n", ret); return ret; @@ -113,7 +165,7 @@ static int __hsmp_send_message(struct pci_dev *root, struct hsmp_message *msg) index = 0; /* Write any message arguments */ while (index < msg->num_args) { - ret = amd_hsmp_rdwr(root, SMN_HSMP_MSG_DATA + (index << 2), + ret = amd_hsmp_rdwr(sock, mbinfo->msg_arg_off + (index << 2), &msg->args[index], HSMP_WR); if (ret) { pr_err("Error %d writing message argument %d\n", ret, index); @@ -123,7 +175,7 @@ static int __hsmp_send_message(struct pci_dev *root, struct hsmp_message *msg) } /* Write the message ID which starts the operation */ - ret = amd_hsmp_rdwr(root, SMN_HSMP_MSG_ID, &msg->msg_id, HSMP_WR); + ret = amd_hsmp_rdwr(sock, mbinfo->msg_id_off, &msg->msg_id, HSMP_WR); if (ret) { pr_err("Error %d writing message ID %u\n", ret, msg->msg_id); return ret; @@ -140,7 +192,7 @@ static int __hsmp_send_message(struct pci_dev *root, struct hsmp_message *msg) timeout = jiffies + msecs_to_jiffies(HSMP_MSG_TIMEOUT); while (time_before(jiffies, timeout)) { - ret = amd_hsmp_rdwr(root, SMN_HSMP_MSG_RESP, &mbox_status, HSMP_RD); + ret = amd_hsmp_rdwr(sock, mbinfo->msg_resp_off, &mbox_status, HSMP_RD); if (ret) { pr_err("Error %d reading mailbox status\n", ret); return ret; @@ -175,7 +227,7 @@ static int __hsmp_send_message(struct pci_dev *root, struct hsmp_message *msg) */ index = 0; while (index < msg->response_sz) { - ret = amd_hsmp_rdwr(root, SMN_HSMP_MSG_DATA + (index << 2), + ret = amd_hsmp_rdwr(sock, mbinfo->msg_arg_off + (index << 2), &msg->args[index], HSMP_RD); if (ret) { pr_err("Error %d reading response %u for message ID:%u\n", @@ -208,21 +260,19 @@ static int validate_message(struct hsmp_message *msg) int hsmp_send_message(struct hsmp_message *msg) { - struct hsmp_socket *sock = &plat_dev.sock[msg->sock_ind]; - struct amd_northbridge *nb; + struct hsmp_socket *sock; int ret; if (!msg) return -EINVAL; - - nb = node_to_amd_nb(msg->sock_ind); - if (!nb || !nb->root) - return -ENODEV; - ret = validate_message(msg); if (ret) return ret; + if (!plat_dev.sock || msg->sock_ind >= plat_dev.num_sockets) + return -ENODEV; + sock = &plat_dev.sock[msg->sock_ind]; + /* * The time taken by smu operation to complete is between * 10us to 1ms. Sometime it may take more time. @@ -233,7 +283,7 @@ int hsmp_send_message(struct hsmp_message *msg) if (ret < 0) return ret; - ret = __hsmp_send_message(nb->root, msg); + ret = __hsmp_send_message(sock, msg); up(&sock->hsmp_sem); @@ -244,12 +294,7 @@ EXPORT_SYMBOL_GPL(hsmp_send_message); static int hsmp_test(u16 sock_ind, u32 value) { struct hsmp_message msg = { 0 }; - struct amd_northbridge *nb; - int ret = -ENODEV; - - nb = node_to_amd_nb(sock_ind); - if (!nb || !nb->root) - return ret; + int ret; /* * Test the hsmp port by performing TEST command. The test message @@ -261,14 +306,15 @@ static int hsmp_test(u16 sock_ind, u32 value) msg.args[0] = value; msg.sock_ind = sock_ind; - ret = __hsmp_send_message(nb->root, &msg); + ret = hsmp_send_message(&msg); if (ret) return ret; /* Check the response value */ if (msg.args[0] != (value + 1)) { - pr_err("Socket %d test message failed, Expected 0x%08X, received 0x%08X\n", - sock_ind, (value + 1), msg.args[0]); + dev_err(plat_dev.sock[sock_ind].dev, + "Socket %d test message failed, Expected 0x%08X, received 0x%08X\n", + sock_ind, (value + 1), msg.args[0]); return -EBADE; } @@ -337,6 +383,181 @@ static const struct file_operations hsmp_fops = { .compat_ioctl = hsmp_ioctl, }; +/* This is the UUID used for HSMP */ +static const guid_t acpi_hsmp_uuid = GUID_INIT(0xb74d619d, 0x5707, 0x48bd, + 0xa6, 0x9f, 0x4e, 0xa2, + 0x87, 0x1f, 0xc2, 0xf6); + +static inline bool is_acpi_hsmp_uuid(union acpi_object *obj) +{ + if (obj->type == ACPI_TYPE_BUFFER && obj->buffer.length == UUID_SIZE) + return guid_equal((guid_t *)obj->buffer.pointer, &acpi_hsmp_uuid); + + return false; +} + +static inline int hsmp_get_uid(struct device *dev, u16 *sock_ind) +{ + char *uid; + + /* + * UID (ID00, ID01..IDXX) is used for differentiating sockets, + * read it and strip the "ID" part of it and convert the remaining + * bytes to integer. + */ + uid = acpi_device_uid(ACPI_COMPANION(dev)); + + return kstrtou16(uid + 2, 10, sock_ind); +} + +static acpi_status hsmp_resource(struct acpi_resource *res, void *data) +{ + struct hsmp_socket *sock = data; + struct resource r; + + switch (res->type) { + case ACPI_RESOURCE_TYPE_FIXED_MEMORY32: + if (!acpi_dev_resource_memory(res, &r)) + return AE_ERROR; + if (!r.start || r.end < r.start || !(r.flags & IORESOURCE_MEM_WRITEABLE)) + return AE_ERROR; + sock->mbinfo.base_addr = r.start; + sock->mbinfo.size = resource_size(&r); + break; + case ACPI_RESOURCE_TYPE_END_TAG: + break; + default: + return AE_ERROR; + } + + return AE_OK; +} + +static int hsmp_read_acpi_dsd(struct hsmp_socket *sock) +{ + struct acpi_buffer buf = { ACPI_ALLOCATE_BUFFER, NULL }; + union acpi_object *guid, *mailbox_package; + union acpi_object *dsd; + acpi_status status; + int ret = 0; + int j; + + status = acpi_evaluate_object_typed(ACPI_HANDLE(sock->dev), "_DSD", NULL, + &buf, ACPI_TYPE_PACKAGE); + if (ACPI_FAILURE(status)) { + dev_err(sock->dev, "Failed to read mailbox reg offsets from DSD table, err: %s\n", + acpi_format_exception(status)); + return -ENODEV; + } + + dsd = buf.pointer; + + /* HSMP _DSD property should contain 2 objects. + * 1. guid which is an acpi object of type ACPI_TYPE_BUFFER + * 2. mailbox which is an acpi object of type ACPI_TYPE_PACKAGE + * This mailbox object contains 3 more acpi objects of type + * ACPI_TYPE_PACKAGE for holding msgid, msgresp, msgarg offsets + * these packages inturn contain 2 acpi objects of type + * ACPI_TYPE_STRING and ACPI_TYPE_INTEGER + */ + if (!dsd || dsd->type != ACPI_TYPE_PACKAGE || dsd->package.count != 2) { + ret = -EINVAL; + goto free_buf; + } + + guid = &dsd->package.elements[0]; + mailbox_package = &dsd->package.elements[1]; + if (!is_acpi_hsmp_uuid(guid) || mailbox_package->type != ACPI_TYPE_PACKAGE) { + dev_err(sock->dev, "Invalid hsmp _DSD table data\n"); + ret = -EINVAL; + goto free_buf; + } + + for (j = 0; j < mailbox_package->package.count; j++) { + union acpi_object *msgobj, *msgstr, *msgint; + + msgobj = &mailbox_package->package.elements[j]; + msgstr = &msgobj->package.elements[0]; + msgint = &msgobj->package.elements[1]; + + /* package should have 1 string and 1 integer object */ + if (msgobj->type != ACPI_TYPE_PACKAGE || + msgstr->type != ACPI_TYPE_STRING || + msgint->type != ACPI_TYPE_INTEGER) { + ret = -EINVAL; + goto free_buf; + } + + if (!strncmp(msgstr->string.pointer, MSG_IDOFF_STR, + msgstr->string.length)) { + sock->mbinfo.msg_id_off = msgint->integer.value; + } else if (!strncmp(msgstr->string.pointer, MSG_RESPOFF_STR, + msgstr->string.length)) { + sock->mbinfo.msg_resp_off = msgint->integer.value; + } else if (!strncmp(msgstr->string.pointer, MSG_ARGOFF_STR, + msgstr->string.length)) { + sock->mbinfo.msg_arg_off = msgint->integer.value; + } else { + ret = -ENOENT; + goto free_buf; + } + } + + if (!sock->mbinfo.msg_id_off || !sock->mbinfo.msg_resp_off || + !sock->mbinfo.msg_arg_off) + ret = -EINVAL; + +free_buf: + ACPI_FREE(buf.pointer); + return ret; +} + +static int hsmp_read_acpi_crs(struct hsmp_socket *sock) +{ + acpi_status status; + + status = acpi_walk_resources(ACPI_HANDLE(sock->dev), METHOD_NAME__CRS, + hsmp_resource, sock); + if (ACPI_FAILURE(status)) { + dev_err(sock->dev, "Failed to look up MP1 base address from CRS method, err: %s\n", + acpi_format_exception(status)); + return -EINVAL; + } + if (!sock->mbinfo.base_addr || !sock->mbinfo.size) + return -EINVAL; + + /* The mapped region should be un cached */ + sock->virt_base_addr = devm_ioremap_uc(sock->dev, sock->mbinfo.base_addr, + sock->mbinfo.size); + if (!sock->virt_base_addr) { + dev_err(sock->dev, "Failed to ioremap MP1 base address\n"); + return -ENOMEM; + } + + return 0; +} + +/* Parse the ACPI table to read the data */ +static int hsmp_parse_acpi_table(struct device *dev, u16 sock_ind) +{ + struct hsmp_socket *sock = &plat_dev.sock[sock_ind]; + int ret; + + sock->sock_ind = sock_ind; + sock->dev = dev; + plat_dev.is_acpi_device = true; + + sema_init(&sock->hsmp_sem, 1); + + /* Read MP1 base address from CRS method */ + ret = hsmp_read_acpi_crs(sock); + if (ret) + return ret; + + /* Read mailbox offsets from DSD table */ + return hsmp_read_acpi_dsd(sock); +} + static ssize_t hsmp_metric_tbl_read(struct file *filp, struct kobject *kobj, struct bin_attribute *bin_attr, char *buf, loff_t off, size_t count) @@ -345,14 +566,12 @@ static ssize_t hsmp_metric_tbl_read(struct file *filp, struct kobject *kobj, struct hsmp_message msg = { 0 }; int ret; - /* Do not support lseek(), reads entire metric table */ - if (count < bin_attr->size) { - dev_err(plat_dev.dev, "Wrong buffer size\n"); + if (!sock) return -EINVAL; - } - if (!sock) { - dev_err(plat_dev.dev, "Failed to read attribute private data\n"); + /* Do not support lseek(), reads entire metric table */ + if (count < bin_attr->size) { + dev_err(sock->dev, "Wrong buffer size\n"); return -EINVAL; } @@ -388,13 +607,13 @@ static int hsmp_get_tbl_dram_base(u16 sock_ind) */ dram_addr = msg.args[0] | ((u64)(msg.args[1]) << 32); if (!dram_addr) { - dev_err(plat_dev.dev, "Invalid DRAM address for metric table\n"); + dev_err(sock->dev, "Invalid DRAM address for metric table\n"); return -ENOMEM; } - sock->metric_tbl_addr = devm_ioremap(plat_dev.dev, dram_addr, + sock->metric_tbl_addr = devm_ioremap(sock->dev, dram_addr, sizeof(struct hsmp_metric_table)); if (!sock->metric_tbl_addr) { - dev_err(plat_dev.dev, "Failed to ioremap metric table addr\n"); + dev_err(sock->dev, "Failed to ioremap metric table addr\n"); return -ENOMEM; } return 0; @@ -422,65 +641,91 @@ static int hsmp_init_metric_tbl_bin_attr(struct bin_attribute **hattrs, u16 sock hattrs[0] = hattr; if (plat_dev.proto_ver == HSMP_PROTO_VER6) - return (hsmp_get_tbl_dram_base(sock_ind)); + return hsmp_get_tbl_dram_base(sock_ind); else return 0; } -/* One bin sysfs for metrics table*/ +/* One bin sysfs for metrics table */ #define NUM_HSMP_ATTRS 1 -static int hsmp_create_sysfs_interface(void) +static int hsmp_create_attr_list(struct attribute_group *attr_grp, + struct device *dev, u16 sock_ind) { - const struct attribute_group **hsmp_attr_grps; struct bin_attribute **hsmp_bin_attrs; + + /* Null terminated list of attributes */ + hsmp_bin_attrs = devm_kcalloc(dev, NUM_HSMP_ATTRS + 1, + sizeof(*hsmp_bin_attrs), + GFP_KERNEL); + if (!hsmp_bin_attrs) + return -ENOMEM; + + attr_grp->bin_attrs = hsmp_bin_attrs; + + return hsmp_init_metric_tbl_bin_attr(hsmp_bin_attrs, sock_ind); +} + +static int hsmp_create_non_acpi_sysfs_if(struct device *dev) +{ + const struct attribute_group **hsmp_attr_grps; struct attribute_group *attr_grp; - int ret; u16 i; - /* String formatting is currently limited to u8 sockets */ - if (WARN_ON(plat_dev.num_sockets > U8_MAX)) - return -ERANGE; - - hsmp_attr_grps = devm_kzalloc(plat_dev.dev, sizeof(struct attribute_group *) * - (plat_dev.num_sockets + 1), GFP_KERNEL); + hsmp_attr_grps = devm_kcalloc(dev, plat_dev.num_sockets + 1, + sizeof(*hsmp_attr_grps), + GFP_KERNEL); if (!hsmp_attr_grps) return -ENOMEM; /* Create a sysfs directory for each socket */ for (i = 0; i < plat_dev.num_sockets; i++) { - attr_grp = devm_kzalloc(plat_dev.dev, sizeof(struct attribute_group), GFP_KERNEL); + attr_grp = devm_kzalloc(dev, sizeof(struct attribute_group), + GFP_KERNEL); if (!attr_grp) return -ENOMEM; snprintf(plat_dev.sock[i].name, HSMP_ATTR_GRP_NAME_SIZE, "socket%u", (u8)i); - attr_grp->name = plat_dev.sock[i].name; - - /* Null terminated list of attributes */ - hsmp_bin_attrs = devm_kzalloc(plat_dev.dev, sizeof(struct bin_attribute *) * - (NUM_HSMP_ATTRS + 1), GFP_KERNEL); - if (!hsmp_bin_attrs) - return -ENOMEM; - - attr_grp->bin_attrs = hsmp_bin_attrs; + attr_grp->name = plat_dev.sock[i].name; attr_grp->is_bin_visible = hsmp_is_sock_attr_visible; hsmp_attr_grps[i] = attr_grp; - /* Now create the leaf nodes */ - ret = hsmp_init_metric_tbl_bin_attr(hsmp_bin_attrs, i); - if (ret) - return ret; + hsmp_create_attr_list(attr_grp, dev, i); } - return devm_device_add_groups(plat_dev.dev, hsmp_attr_grps); + + return devm_device_add_groups(dev, hsmp_attr_grps); +} + +static int hsmp_create_acpi_sysfs_if(struct device *dev) +{ + struct attribute_group *attr_grp; + u16 sock_ind; + int ret; + + attr_grp = devm_kzalloc(dev, sizeof(struct attribute_group), GFP_KERNEL); + if (!attr_grp) + return -ENOMEM; + + attr_grp->is_bin_visible = hsmp_is_sock_attr_visible; + + ret = hsmp_get_uid(dev, &sock_ind); + if (ret) + return ret; + + ret = hsmp_create_attr_list(attr_grp, dev, sock_ind); + if (ret) + return ret; + + return devm_device_add_group(dev, attr_grp); } -static int hsmp_cache_proto_ver(void) +static int hsmp_cache_proto_ver(u16 sock_ind) { struct hsmp_message msg = { 0 }; int ret; msg.msg_id = HSMP_GET_PROTO_VER; - msg.sock_ind = 0; + msg.sock_ind = sock_ind; msg.response_sz = hsmp_msg_desc_table[HSMP_GET_PROTO_VER].response_sz; ret = hsmp_send_message(&msg); @@ -490,45 +735,150 @@ static int hsmp_cache_proto_ver(void) return ret; } -static int hsmp_pltdrv_probe(struct platform_device *pdev) +static inline bool is_f1a_m0h(void) { - int ret, i; + if (boot_cpu_data.x86 == 0x1A && boot_cpu_data.x86_model <= 0x0F) + return true; - plat_dev.sock = devm_kzalloc(&pdev->dev, - (plat_dev.num_sockets * sizeof(struct hsmp_socket)), - GFP_KERNEL); - if (!plat_dev.sock) - return -ENOMEM; - plat_dev.dev = &pdev->dev; + return false; +} + +static int init_platform_device(struct device *dev) +{ + struct hsmp_socket *sock; + int ret, i; for (i = 0; i < plat_dev.num_sockets; i++) { - sema_init(&plat_dev.sock[i].hsmp_sem, 1); - plat_dev.sock[i].sock_ind = i; + if (!node_to_amd_nb(i)) + return -ENODEV; + sock = &plat_dev.sock[i]; + sock->root = node_to_amd_nb(i)->root; + sock->sock_ind = i; + sock->dev = dev; + sock->mbinfo.base_addr = SMN_HSMP_BASE; + + /* + * This is a transitional change from non-ACPI to ACPI, only + * family 0x1A, model 0x00 platform is supported for both ACPI and non-ACPI. + */ + if (is_f1a_m0h()) + sock->mbinfo.msg_id_off = SMN_HSMP_MSG_ID_F1A_M0H; + else + sock->mbinfo.msg_id_off = SMN_HSMP_MSG_ID; + + sock->mbinfo.msg_resp_off = SMN_HSMP_MSG_RESP; + sock->mbinfo.msg_arg_off = SMN_HSMP_MSG_DATA; + sema_init(&sock->hsmp_sem, 1); + + /* Test the hsmp interface on each socket */ + ret = hsmp_test(i, 0xDEADBEEF); + if (ret) { + dev_err(dev, "HSMP test message failed on Fam:%x model:%x\n", + boot_cpu_data.x86, boot_cpu_data.x86_model); + dev_err(dev, "Is HSMP disabled in BIOS ?\n"); + return ret; + } } - plat_dev.hsmp_device.name = HSMP_CDEV_NAME; - plat_dev.hsmp_device.minor = MISC_DYNAMIC_MINOR; - plat_dev.hsmp_device.fops = &hsmp_fops; - plat_dev.hsmp_device.parent = &pdev->dev; - plat_dev.hsmp_device.nodename = HSMP_DEVNODE_NAME; - plat_dev.hsmp_device.mode = 0644; + return 0; +} + +static const struct acpi_device_id amd_hsmp_acpi_ids[] = { + {ACPI_HSMP_DEVICE_HID, 0}, + {} +}; +MODULE_DEVICE_TABLE(acpi, amd_hsmp_acpi_ids); + +static int hsmp_pltdrv_probe(struct platform_device *pdev) +{ + struct acpi_device *adev; + u16 sock_ind = 0; + int ret; + + /* + * On ACPI supported BIOS, there is an ACPI HSMP device added for + * each socket, so the per socket probing, but the memory allocated for + * sockets should be contiguous to access it as an array, + * Hence allocate memory for all the sockets at once instead of allocating + * on each probe. + */ + if (!plat_dev.is_probed) { + plat_dev.sock = devm_kcalloc(&pdev->dev, plat_dev.num_sockets, + sizeof(*plat_dev.sock), + GFP_KERNEL); + if (!plat_dev.sock) + return -ENOMEM; + } + adev = ACPI_COMPANION(&pdev->dev); + if (adev && !acpi_match_device_ids(adev, amd_hsmp_acpi_ids)) { + ret = hsmp_get_uid(&pdev->dev, &sock_ind); + if (ret) + return ret; + if (sock_ind >= plat_dev.num_sockets) + return -EINVAL; + ret = hsmp_parse_acpi_table(&pdev->dev, sock_ind); + if (ret) { + dev_err(&pdev->dev, "Failed to parse ACPI table\n"); + return ret; + } + /* Test the hsmp interface */ + ret = hsmp_test(sock_ind, 0xDEADBEEF); + if (ret) { + dev_err(&pdev->dev, "HSMP test message failed on Fam:%x model:%x\n", + boot_cpu_data.x86, boot_cpu_data.x86_model); + dev_err(&pdev->dev, "Is HSMP disabled in BIOS ?\n"); + return ret; + } + } else { + ret = init_platform_device(&pdev->dev); + if (ret) { + dev_err(&pdev->dev, "Failed to init HSMP mailbox\n"); + return ret; + } + } - ret = hsmp_cache_proto_ver(); + ret = hsmp_cache_proto_ver(sock_ind); if (ret) { - dev_err(plat_dev.dev, "Failed to read HSMP protocol version\n"); + dev_err(&pdev->dev, "Failed to read HSMP protocol version\n"); return ret; } - ret = hsmp_create_sysfs_interface(); + if (plat_dev.is_acpi_device) + ret = hsmp_create_acpi_sysfs_if(&pdev->dev); + else + ret = hsmp_create_non_acpi_sysfs_if(&pdev->dev); if (ret) - dev_err(plat_dev.dev, "Failed to create HSMP sysfs interface\n"); + dev_err(&pdev->dev, "Failed to create HSMP sysfs interface\n"); + + if (!plat_dev.is_probed) { + plat_dev.hsmp_device.name = HSMP_CDEV_NAME; + plat_dev.hsmp_device.minor = MISC_DYNAMIC_MINOR; + plat_dev.hsmp_device.fops = &hsmp_fops; + plat_dev.hsmp_device.parent = &pdev->dev; + plat_dev.hsmp_device.nodename = HSMP_DEVNODE_NAME; + plat_dev.hsmp_device.mode = 0644; + + ret = misc_register(&plat_dev.hsmp_device); + if (ret) + return ret; + + plat_dev.is_probed = true; + } + + return 0; - return misc_register(&plat_dev.hsmp_device); } static void hsmp_pltdrv_remove(struct platform_device *pdev) { - misc_deregister(&plat_dev.hsmp_device); + /* + * We register only one misc_device even on multi socket system. + * So, deregister should happen only once. + */ + if (plat_dev.is_probed) { + misc_deregister(&plat_dev.hsmp_device); + plat_dev.is_probed = false; + } } static struct platform_driver amd_hsmp_driver = { @@ -536,15 +886,30 @@ static struct platform_driver amd_hsmp_driver = { .remove_new = hsmp_pltdrv_remove, .driver = { .name = DRIVER_NAME, + .acpi_match_table = amd_hsmp_acpi_ids, }, }; static struct platform_device *amd_hsmp_platdev; +static int hsmp_plat_dev_register(void) +{ + int ret; + + amd_hsmp_platdev = platform_device_alloc(DRIVER_NAME, PLATFORM_DEVID_NONE); + if (!amd_hsmp_platdev) + return -ENOMEM; + + ret = platform_device_add(amd_hsmp_platdev); + if (ret) + platform_device_put(amd_hsmp_platdev); + + return ret; +} + static int __init hsmp_plt_init(void) { int ret = -ENODEV; - int i; if (boot_cpu_data.x86_vendor != X86_VENDOR_AMD || boot_cpu_data.x86 < 0x19) { pr_err("HSMP is not supported on Family:%x model:%x\n", @@ -557,40 +922,19 @@ static int __init hsmp_plt_init(void) * if we have N SMN/DF interfaces that ideally means N sockets */ plat_dev.num_sockets = amd_nb_num(); - if (plat_dev.num_sockets == 0) + if (plat_dev.num_sockets == 0 || plat_dev.num_sockets > MAX_AMD_SOCKETS) return ret; - /* Test the hsmp interface on each socket */ - for (i = 0; i < plat_dev.num_sockets; i++) { - ret = hsmp_test(i, 0xDEADBEEF); - if (ret) { - pr_err("HSMP test message failed on Fam:%x model:%x\n", - boot_cpu_data.x86, boot_cpu_data.x86_model); - pr_err("Is HSMP disabled in BIOS ?\n"); - return ret; - } - } - ret = platform_driver_register(&amd_hsmp_driver); if (ret) return ret; - amd_hsmp_platdev = platform_device_alloc(DRIVER_NAME, PLATFORM_DEVID_NONE); - if (!amd_hsmp_platdev) { - ret = -ENOMEM; - goto drv_unregister; - } - - ret = platform_device_add(amd_hsmp_platdev); - if (ret) { - platform_device_put(amd_hsmp_platdev); - goto drv_unregister; + if (!plat_dev.is_acpi_device) { + ret = hsmp_plat_dev_register(); + if (ret) + platform_driver_unregister(&amd_hsmp_driver); } - return 0; - -drv_unregister: - platform_driver_unregister(&amd_hsmp_driver); return ret; } diff --git a/drivers/platform/x86/asus-wmi.c b/drivers/platform/x86/asus-wmi.c index 18be35fdb381..21dee425ea6f 100644 --- a/drivers/platform/x86/asus-wmi.c +++ b/drivers/platform/x86/asus-wmi.c @@ -1620,7 +1620,6 @@ static int asus_wmi_led_init(struct asus_wmi *asus) if (asus_wmi_dev_is_present(asus, ASUS_WMI_DEVID_MICMUTE_LED)) { asus->micmute_led.name = "platform::micmute"; asus->micmute_led.max_brightness = 1; - asus->micmute_led.brightness = ledtrig_audio_get(LED_AUDIO_MICMUTE); asus->micmute_led.brightness_set_blocking = micmute_led_set; asus->micmute_led.default_trigger = "audio-micmute"; diff --git a/drivers/platform/x86/dell/Kconfig b/drivers/platform/x86/dell/Kconfig index e712df67fa6b..bd9f445974cc 100644 --- a/drivers/platform/x86/dell/Kconfig +++ b/drivers/platform/x86/dell/Kconfig @@ -57,8 +57,6 @@ config DELL_LAPTOP select POWER_SUPPLY select LEDS_CLASS select NEW_LEDS - select LEDS_TRIGGERS - select LEDS_TRIGGER_AUDIO help This driver adds support for rfkill and backlight control to Dell laptops (except for some models covered by the Compal driver). @@ -165,7 +163,6 @@ config DELL_WMI config DELL_WMI_PRIVACY bool "Dell WMI Hardware Privacy Support" - depends on LEDS_TRIGGER_AUDIO = y || DELL_WMI = LEDS_TRIGGER_AUDIO depends on DELL_WMI help This option adds integration with the "Dell Hardware Privacy" diff --git a/drivers/platform/x86/dell/dell-laptop.c b/drivers/platform/x86/dell/dell-laptop.c index 6586438356de..42f7de2b4522 100644 --- a/drivers/platform/x86/dell/dell-laptop.c +++ b/drivers/platform/x86/dell/dell-laptop.c @@ -2252,7 +2252,6 @@ static int __init dell_init(void) if (dell_smbios_find_token(GLOBAL_MIC_MUTE_DISABLE) && dell_smbios_find_token(GLOBAL_MIC_MUTE_ENABLE) && !dell_privacy_has_mic_mute()) { - micmute_led_cdev.brightness = ledtrig_audio_get(LED_AUDIO_MICMUTE); ret = led_classdev_register(&platform_device->dev, &micmute_led_cdev); if (ret < 0) goto fail_led; @@ -2261,7 +2260,6 @@ static int __init dell_init(void) if (dell_smbios_find_token(GLOBAL_MUTE_DISABLE) && dell_smbios_find_token(GLOBAL_MUTE_ENABLE)) { - mute_led_cdev.brightness = ledtrig_audio_get(LED_AUDIO_MUTE); ret = led_classdev_register(&platform_device->dev, &mute_led_cdev); if (ret < 0) goto fail_backlight; diff --git a/drivers/platform/x86/dell/dell-wmi-privacy.c b/drivers/platform/x86/dell/dell-wmi-privacy.c index c517bd45dd32..4d94603f7785 100644 --- a/drivers/platform/x86/dell/dell-wmi-privacy.c +++ b/drivers/platform/x86/dell/dell-wmi-privacy.c @@ -288,7 +288,6 @@ static int dell_privacy_leds_setup(struct device *dev) priv->cdev.max_brightness = 1; priv->cdev.brightness_set_blocking = dell_privacy_micmute_led_set; priv->cdev.default_trigger = "audio-micmute"; - priv->cdev.brightness = ledtrig_audio_get(LED_AUDIO_MICMUTE); return devm_led_classdev_register(dev, &priv->cdev); } diff --git a/drivers/platform/x86/hp/hp-wmi.c b/drivers/platform/x86/hp/hp-wmi.c index e536604225c5..630519c08617 100644 --- a/drivers/platform/x86/hp/hp-wmi.c +++ b/drivers/platform/x86/hp/hp-wmi.c @@ -29,15 +29,19 @@ #include <linux/dmi.h> MODULE_AUTHOR("Matthew Garrett <[email protected]>"); -MODULE_DESCRIPTION("HP laptop WMI hotkeys driver"); +MODULE_DESCRIPTION("HP laptop WMI driver"); MODULE_LICENSE("GPL"); MODULE_ALIAS("wmi:95F24279-4D7B-4334-9387-ACCDC67EF61C"); -MODULE_ALIAS("wmi:5FB7F034-2C63-45e9-BE91-3D44E2C707E4"); +MODULE_ALIAS("wmi:5FB7F034-2C63-45E9-BE91-3D44E2C707E4"); #define HPWMI_EVENT_GUID "95F24279-4D7B-4334-9387-ACCDC67EF61C" -#define HPWMI_BIOS_GUID "5FB7F034-2C63-45e9-BE91-3D44E2C707E4" +#define HPWMI_BIOS_GUID "5FB7F034-2C63-45E9-BE91-3D44E2C707E4" + +#define HP_OMEN_EC_THERMAL_PROFILE_FLAGS_OFFSET 0x62 +#define HP_OMEN_EC_THERMAL_PROFILE_TIMER_OFFSET 0x63 #define HP_OMEN_EC_THERMAL_PROFILE_OFFSET 0x95 + #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 @@ -55,17 +59,25 @@ static const char * const omen_thermal_profile_boards[] = { "874A", "8603", "8604", "8748", "886B", "886C", "878A", "878B", "878C", "88C8", "88CB", "8786", "8787", "8788", "88D1", "88D2", "88F4", "88FD", "88F5", "88F6", "88F7", "88FE", "88FF", "8900", "8901", "8902", "8912", - "8917", "8918", "8949", "894A", "89EB" + "8917", "8918", "8949", "894A", "89EB", "8BAD", "8A42" }; /* DMI Board names of Omen laptops that are specifically set to be thermal * profile version 0 by the Omen Command Center app, regardless of what * the get system design information WMI call returns */ -static const char *const omen_thermal_profile_force_v0_boards[] = { +static const char * const omen_thermal_profile_force_v0_boards[] = { "8607", "8746", "8747", "8749", "874A", "8748" }; +/* DMI board names of Omen laptops that have a thermal profile timer which will + * cause the embedded controller to set the thermal profile back to + * "balanced" when reaching zero. + */ +static const char * const omen_timed_thermal_profile_boards[] = { + "8BAD", "8A42" +}; + /* DMI Board names of Victus laptops */ static const char * const victus_thermal_profile_boards[] = { "8A25" @@ -182,6 +194,12 @@ enum hp_thermal_profile_omen_v1 { HP_OMEN_V1_THERMAL_PROFILE_COOL = 0x50, }; +enum hp_thermal_profile_omen_flags { + HP_OMEN_EC_FLAGS_TURBO = 0x04, + HP_OMEN_EC_FLAGS_NOTIMER = 0x02, + HP_OMEN_EC_FLAGS_JUSTSET = 0x01, +}; + enum hp_thermal_profile_victus { HP_VICTUS_THERMAL_PROFILE_DEFAULT = 0x00, HP_VICTUS_THERMAL_PROFILE_PERFORMANCE = 0x01, @@ -449,7 +467,11 @@ static int hp_wmi_get_tablet_mode(void) static int omen_thermal_profile_set(int mode) { - char buffer[2] = {0, mode}; + /* The Omen Control Center actively sets the first byte of the buffer to + * 255, so let's mimic this behaviour to be as close as possible to + * the original software. + */ + char buffer[2] = {-1, mode}; int ret; ret = hp_wmi_perform_query(HPWMI_SET_PERFORMANCE_MODE, HPWMI_GM, @@ -1201,10 +1223,33 @@ static int platform_profile_omen_get(struct platform_profile_handler *pprof, return 0; } +static bool has_omen_thermal_profile_ec_timer(void) +{ + const char *board_name = dmi_get_system_info(DMI_BOARD_NAME); + + if (!board_name) + return false; + + return match_string(omen_timed_thermal_profile_boards, + ARRAY_SIZE(omen_timed_thermal_profile_boards), + board_name) >= 0; +} + +inline int omen_thermal_profile_ec_flags_set(enum hp_thermal_profile_omen_flags flags) +{ + return ec_write(HP_OMEN_EC_THERMAL_PROFILE_FLAGS_OFFSET, flags); +} + +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) { int err, tp, tp_version; + enum hp_thermal_profile_omen_flags flags = 0; tp_version = omen_get_thermal_policy_version(); @@ -1238,6 +1283,20 @@ static int platform_profile_omen_set(struct platform_profile_handler *pprof, if (err < 0) return err; + if (has_omen_thermal_profile_ec_timer()) { + err = omen_thermal_profile_ec_timer_set(0); + if (err < 0) + return err; + + if (profile == PLATFORM_PROFILE_PERFORMANCE) + flags = HP_OMEN_EC_FLAGS_NOTIMER | + HP_OMEN_EC_FLAGS_TURBO; + + err = omen_thermal_profile_ec_flags_set(flags); + if (err < 0) + return err; + } + return 0; } diff --git a/drivers/platform/x86/huawei-wmi.c b/drivers/platform/x86/huawei-wmi.c index 0ef1c46b617b..dde139c69945 100644 --- a/drivers/platform/x86/huawei-wmi.c +++ b/drivers/platform/x86/huawei-wmi.c @@ -310,7 +310,6 @@ static void huawei_wmi_leds_setup(struct device *dev) huawei->cdev.max_brightness = 1; huawei->cdev.brightness_set_blocking = &huawei_wmi_micmute_led_set; huawei->cdev.default_trigger = "audio-micmute"; - huawei->cdev.brightness = ledtrig_audio_get(LED_AUDIO_MICMUTE); huawei->cdev.dev = dev; huawei->cdev.flags = LED_CORE_SUSPENDRESUME; diff --git a/drivers/platform/x86/ibm_rtl.c b/drivers/platform/x86/ibm_rtl.c index 2ab7d9ac542d..1d4bbae115f1 100644 --- a/drivers/platform/x86/ibm_rtl.c +++ b/drivers/platform/x86/ibm_rtl.c @@ -179,7 +179,7 @@ static ssize_t rtl_set_state(struct device *dev, return ret; } -static struct bus_type rtl_subsys = { +static const struct bus_type rtl_subsys = { .name = "ibm_rtl", .dev_name = "ibm_rtl", }; diff --git a/drivers/platform/x86/intel/ifs/load.c b/drivers/platform/x86/intel/ifs/load.c index 2cf3b4a8813f..584c44387e10 100644 --- a/drivers/platform/x86/intel/ifs/load.c +++ b/drivers/platform/x86/intel/ifs/load.c @@ -383,7 +383,7 @@ int ifs_load_firmware(struct device *dev) unsigned int expected_size; const struct firmware *fw; char scan_path[64]; - int ret = -EINVAL; + int ret; snprintf(scan_path, sizeof(scan_path), "intel/ifs_%d/%02x-%02x-%02x-%02x.scan", test->test_num, boot_cpu_data.x86, boot_cpu_data.x86_model, diff --git a/drivers/platform/x86/intel/ifs/runtest.c b/drivers/platform/x86/intel/ifs/runtest.c index 13ecd55c6668..95b4b71fab53 100644 --- a/drivers/platform/x86/intel/ifs/runtest.c +++ b/drivers/platform/x86/intel/ifs/runtest.c @@ -23,6 +23,12 @@ /* Max retries on the same chunk */ #define MAX_IFS_RETRIES 5 +struct run_params { + struct ifs_data *ifsd; + union ifs_scan *activate; + union ifs_status status; +}; + /* * Number of TSC cycles that a logical CPU will wait for the other * logical CPU on the core in the WRMSR(ACTIVATE_SCAN). @@ -134,19 +140,56 @@ static bool can_restart(union ifs_status status) return false; } +#define SPINUNIT 100 /* 100 nsec */ +static atomic_t array_cpus_in; +static atomic_t scan_cpus_in; + +/* + * Simplified cpu sibling rendezvous loop based on microcode loader __wait_for_cpus() + */ +static void wait_for_sibling_cpu(atomic_t *t, long long timeout) +{ + int cpu = smp_processor_id(); + const struct cpumask *smt_mask = cpu_smt_mask(cpu); + int all_cpus = cpumask_weight(smt_mask); + + atomic_inc(t); + while (atomic_read(t) < all_cpus) { + if (timeout < SPINUNIT) + return; + ndelay(SPINUNIT); + timeout -= SPINUNIT; + touch_nmi_watchdog(); + } +} + /* * Execute the scan. Called "simultaneously" on all threads of a core * at high priority using the stop_cpus mechanism. */ static int doscan(void *data) { - int cpu = smp_processor_id(); - u64 *msrs = data; + int cpu = smp_processor_id(), start, stop; + struct run_params *params = data; + union ifs_status status; + struct ifs_data *ifsd; int first; + ifsd = params->ifsd; + + if (ifsd->generation) { + start = params->activate->gen2.start; + stop = params->activate->gen2.stop; + } else { + start = params->activate->gen0.start; + stop = params->activate->gen0.stop; + } + /* Only the first logical CPU on a core reports result */ first = cpumask_first(cpu_smt_mask(cpu)); + wait_for_sibling_cpu(&scan_cpus_in, NSEC_PER_SEC); + /* * This WRMSR will wait for other HT threads to also write * to this MSR (at most for activate.delay cycles). Then it @@ -155,12 +198,14 @@ static int doscan(void *data) * take up to 200 milliseconds (in the case where all chunks * are processed in a single pass) before it retires. */ - wrmsrl(MSR_ACTIVATE_SCAN, msrs[0]); + wrmsrl(MSR_ACTIVATE_SCAN, params->activate->data); + rdmsrl(MSR_SCAN_STATUS, status.data); - if (cpu == first) { - /* Pass back the result of the scan */ - rdmsrl(MSR_SCAN_STATUS, msrs[1]); - } + trace_ifs_status(ifsd->cur_batch, start, stop, status.data); + + /* Pass back the result of the scan */ + if (cpu == first) + params->status = status; return 0; } @@ -179,7 +224,7 @@ static void ifs_test_core(int cpu, struct device *dev) struct ifs_data *ifsd; int to_start, to_stop; int status_chunk; - u64 msrvals[2]; + struct run_params params; int retries; ifsd = ifs_get_data(dev); @@ -190,6 +235,8 @@ static void ifs_test_core(int cpu, struct device *dev) to_start = 0; to_stop = ifsd->valid_chunks - 1; + params.ifsd = ifs_get_data(dev); + if (ifsd->generation) { activate.gen2.start = to_start; activate.gen2.stop = to_stop; @@ -207,12 +254,11 @@ static void ifs_test_core(int cpu, struct device *dev) break; } - msrvals[0] = activate.data; - stop_core_cpuslocked(cpu, doscan, msrvals); - - status.data = msrvals[1]; + params.activate = &activate; + atomic_set(&scan_cpus_in, 0); + stop_core_cpuslocked(cpu, doscan, ¶ms); - trace_ifs_status(cpu, to_start, to_stop, status.data); + status = params.status; /* Some cases can be retried, give up for others */ if (!can_restart(status)) @@ -250,34 +296,14 @@ static void ifs_test_core(int cpu, struct device *dev) } } -#define SPINUNIT 100 /* 100 nsec */ -static atomic_t array_cpus_out; - -/* - * Simplified cpu sibling rendezvous loop based on microcode loader __wait_for_cpus() - */ -static void wait_for_sibling_cpu(atomic_t *t, long long timeout) -{ - int cpu = smp_processor_id(); - const struct cpumask *smt_mask = cpu_smt_mask(cpu); - int all_cpus = cpumask_weight(smt_mask); - - atomic_inc(t); - while (atomic_read(t) < all_cpus) { - if (timeout < SPINUNIT) - return; - ndelay(SPINUNIT); - timeout -= SPINUNIT; - touch_nmi_watchdog(); - } -} - static int do_array_test(void *data) { union ifs_array *command = data; int cpu = smp_processor_id(); int first; + wait_for_sibling_cpu(&array_cpus_in, NSEC_PER_SEC); + /* * Only one logical CPU on a core needs to trigger the Array test via MSR write. */ @@ -289,9 +315,6 @@ static int do_array_test(void *data) rdmsrl(MSR_ARRAY_BIST, command->data); } - /* Tests complete faster if the sibling is spinning here */ - wait_for_sibling_cpu(&array_cpus_out, NSEC_PER_SEC); - return 0; } @@ -312,7 +335,7 @@ static void ifs_array_test_core(int cpu, struct device *dev) timed_out = true; break; } - atomic_set(&array_cpus_out, 0); + atomic_set(&array_cpus_in, 0); stop_core_cpuslocked(cpu, do_array_test, &command); if (command.ctrl_result) diff --git a/drivers/platform/x86/silicom-platform.c b/drivers/platform/x86/silicom-platform.c index 6ce43ccb3112..c0910af16a3a 100644 --- a/drivers/platform/x86/silicom-platform.c +++ b/drivers/platform/x86/silicom-platform.c @@ -256,12 +256,7 @@ static void silicom_gpio_set(struct gpio_chip *gc, if (direction == GPIO_LINE_DIRECTION_IN) return; - if (value) - silicom_mec_port_set(channel, 0); - else if (value == 0) - silicom_mec_port_set(channel, 1); - else - pr_err("Wrong argument value: %d\n", value); + silicom_mec_port_set(channel, !value); } static int silicom_gpio_direction_output(struct gpio_chip *gc, diff --git a/drivers/platform/x86/thinkpad_acpi.c b/drivers/platform/x86/thinkpad_acpi.c index c4895e9bc714..897d4cd9e5f4 100644 --- a/drivers/platform/x86/thinkpad_acpi.c +++ b/drivers/platform/x86/thinkpad_acpi.c @@ -166,6 +166,7 @@ enum tpacpi_hkey_event_t { TP_HKEY_EV_VOL_MUTE = 0x1017, /* Mixer output mute */ TP_HKEY_EV_PRIVACYGUARD_TOGGLE = 0x130f, /* Toggle priv.guard on/off */ TP_HKEY_EV_AMT_TOGGLE = 0x131a, /* Toggle AMT on/off */ + TP_HKEY_EV_PROFILE_TOGGLE = 0x131f, /* Toggle platform profile */ /* Reasons for waking up from S3/S4 */ TP_HKEY_EV_WKUP_S3_UNDOCK = 0x2304, /* undock requested, S3 */ @@ -3731,6 +3732,7 @@ static bool hotkey_notify_extended_hotkey(const u32 hkey) switch (hkey) { case TP_HKEY_EV_PRIVACYGUARD_TOGGLE: case TP_HKEY_EV_AMT_TOGGLE: + case TP_HKEY_EV_PROFILE_TOGGLE: tpacpi_driver_event(hkey); return true; } @@ -6208,17 +6210,15 @@ static int thermal_get_sensor(int idx, s32 *value) static int thermal_get_sensors(struct ibm_thermal_sensors_struct *s) { - int res, i; - int n; - - n = 8; - i = 0; + int res, i, n; if (!s) return -EINVAL; if (thermal_read_mode == TPACPI_THERMAL_TPEC_16) n = 16; + else + n = 8; for (i = 0 ; i < n; i++) { res = thermal_get_sensor(i, &s->temp[i]); @@ -9285,7 +9285,6 @@ static int mute_led_init(struct ibm_init_struct *iibm) continue; } - mute_led_cdev[i].brightness = ledtrig_audio_get(i); err = led_classdev_register(&tpacpi_pdev->dev, &mute_led_cdev[i]); if (err < 0) { while (i--) @@ -11118,7 +11117,23 @@ static void tpacpi_driver_event(const unsigned int hkey_event) else dytc_control_amt(!dytc_amt_active); } - + if (hkey_event == TP_HKEY_EV_PROFILE_TOGGLE) { + switch (dytc_current_profile) { + case PLATFORM_PROFILE_LOW_POWER: + dytc_profile_set(NULL, PLATFORM_PROFILE_BALANCED); + break; + case PLATFORM_PROFILE_BALANCED: + dytc_profile_set(NULL, PLATFORM_PROFILE_PERFORMANCE); + break; + case PLATFORM_PROFILE_PERFORMANCE: + dytc_profile_set(NULL, PLATFORM_PROFILE_LOW_POWER); + break; + default: + pr_warn("Profile HKEY unexpected profile %d", dytc_current_profile); + } + /* Notify user space the profile changed */ + platform_profile_notify(); + } } static void hotkey_driver_event(const unsigned int scancode) diff --git a/drivers/platform/x86/wmi.c b/drivers/platform/x86/wmi.c index 3c288e8f404b..b83c0f0ddd5c 100644 --- a/drivers/platform/x86/wmi.c +++ b/drivers/platform/x86/wmi.c @@ -132,26 +132,6 @@ static const void *find_guid_context(struct wmi_block *wblock, return NULL; } -static int get_subobj_info(acpi_handle handle, const char *pathname, - struct acpi_device_info **info) -{ - acpi_handle subobj_handle; - acpi_status status; - - status = acpi_get_handle(handle, pathname, &subobj_handle); - if (status == AE_NOT_FOUND) - return -ENOENT; - - if (ACPI_FAILURE(status)) - return -EIO; - - status = acpi_get_object_info(subobj_handle, info); - if (ACPI_FAILURE(status)) - return -EIO; - - return 0; -} - static acpi_status wmi_method_enable(struct wmi_block *wblock, bool enable) { struct guid_block *block; @@ -232,7 +212,7 @@ static int wmidev_match_notify_id(struct device *dev, const void *data) return 0; } -static struct bus_type wmi_bus_type; +static const struct bus_type wmi_bus_type; static struct wmi_device *wmi_find_device_by_guid(const char *guid_string) { @@ -931,7 +911,7 @@ static struct class wmi_bus_class = { .name = "wmi_bus", }; -static struct bus_type wmi_bus_type = { +static const struct bus_type wmi_bus_type = { .name = "wmi", .dev_groups = wmi_groups, .match = wmi_dev_match, @@ -979,9 +959,10 @@ static int wmi_create_device(struct device *wmi_bus_dev, struct wmi_block *wblock, struct acpi_device *device) { - struct acpi_device_info *info; char method[WMI_ACPI_METHOD_NAME_SIZE]; - int result; + struct acpi_device_info *info; + acpi_handle method_handle; + acpi_status status; uint count; if (wblock->gblock.flags & ACPI_WMI_EVENT) { @@ -990,6 +971,15 @@ static int wmi_create_device(struct device *wmi_bus_dev, } if (wblock->gblock.flags & ACPI_WMI_METHOD) { + get_acpi_method_name(wblock, 'M', method); + if (!acpi_has_method(device->handle, method)) { + dev_warn(wmi_bus_dev, + FW_BUG "%s method block execution control method not found\n", + method); + + return -ENXIO; + } + wblock->dev.dev.type = &wmi_type_method; goto out_init; } @@ -1000,15 +990,19 @@ static int wmi_create_device(struct device *wmi_bus_dev, * we ignore this data block. */ get_acpi_method_name(wblock, 'Q', method); - result = get_subobj_info(device->handle, method, &info); - - if (result) { + status = acpi_get_handle(device->handle, method, &method_handle); + if (ACPI_FAILURE(status)) { dev_warn(wmi_bus_dev, - "%s data block query control method not found\n", + FW_BUG "%s data block query control method not found\n", method); - return result; + + return -ENXIO; } + status = acpi_get_object_info(method_handle, &info); + if (ACPI_FAILURE(status)) + return -EIO; + wblock->dev.dev.type = &wmi_type_data; /* @@ -1133,10 +1127,8 @@ static int parse_wdg(struct device *wmi_bus_dev, struct platform_device *pdev) continue; wblock = kzalloc(sizeof(*wblock), GFP_KERNEL); - if (!wblock) { - dev_err(wmi_bus_dev, "Failed to allocate %pUL\n", &gblock[i].guid); + if (!wblock) continue; - } wblock->acpi_device = device; wblock->gblock = gblock[i]; @@ -1245,8 +1237,7 @@ static int wmi_notify_device(struct device *dev, void *data) } up_read(&wblock->notify_lock); - acpi_bus_generate_netlink_event(wblock->acpi_device->pnp.device_class, - dev_name(&wblock->dev.dev), *event, 0); + acpi_bus_generate_netlink_event("wmi", acpi_dev_name(wblock->acpi_device), *event, 0); return -EBUSY; } @@ -1347,7 +1338,7 @@ static int acpi_wmi_probe(struct platform_device *device) error = parse_wdg(wmi_bus_dev, device); if (error) { - pr_err("Failed to parse WDG method\n"); + dev_err(&device->dev, "Failed to parse _WDG method\n"); return error; } diff --git a/include/trace/events/intel_ifs.h b/include/trace/events/intel_ifs.h index af0af3f1d9b7..8ce2de120f2d 100644 --- a/include/trace/events/intel_ifs.h +++ b/include/trace/events/intel_ifs.h @@ -10,26 +10,26 @@ TRACE_EVENT(ifs_status, - TP_PROTO(int cpu, int start, int stop, u64 status), + TP_PROTO(int batch, int start, int stop, u64 status), - TP_ARGS(cpu, start, stop, status), + TP_ARGS(batch, start, stop, status), TP_STRUCT__entry( + __field( int, batch ) __field( u64, status ) - __field( int, cpu ) __field( u16, start ) __field( u16, stop ) ), TP_fast_assign( - __entry->cpu = cpu; + __entry->batch = batch; __entry->start = start; __entry->stop = stop; __entry->status = status; ), - TP_printk("cpu: %d, start: %.4x, stop: %.4x, status: %.16llx", - __entry->cpu, + TP_printk("batch: %.2d, start: %.4x, stop: %.4x, status: %.16llx", + __entry->batch, __entry->start, __entry->stop, __entry->status) |