diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2024-11-20 14:07:55 -0800 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2024-11-20 14:07:55 -0800 |
commit | fcb3ad4366b9c810cbb9da34c076a9a52d8aa1e0 (patch) | |
tree | 844b7b2bf7c8b5472aef01b55820e4de8b6abddb /drivers/platform/x86 | |
parent | 70e8ef2d6762cecfc868296fa9e0a3848b926efc (diff) | |
parent | c6a2b4fcec5f2d80b0183fae1117f06127584c28 (diff) |
Merge tag 'platform-drivers-x86-v6.13-1' of git://git.kernel.org/pub/scm/linux/kernel/git/pdx86/platform-drivers-x86
Pull x86 platform driver updates from Ilpo Järvinen:
- alienware WMAX thermal interface support
- Split ACPI and platform device based amd/hsmp drivers
- AMD X3D frequency/cache mode switching support
- asus thermal policy fixes
- Disable C1 auto-demotion in suspend to allow entering the deepest
C-states
- Fix volume buttons on Thinkpad X12 Detachable Tablet Gen 1
- Replace intel_scu_ipc "workaround" with 32-bit IO
- Correct *_show() function error handling in panasonic-laptop
- Gemini Lake P2SB devfn correction
- think-lmi Admin/System certificate authentication support
- Disable WMI devices for shutdown, refactoring continues
- Vexia EDU ATLA 10 tablet support
- Surface Pro 9 5G (Arm/QCOM) support
- Misc cleanups / refactoring / improvements
* tag 'platform-drivers-x86-v6.13-1' of git://git.kernel.org/pub/scm/linux/kernel/git/pdx86/platform-drivers-x86: (69 commits)
platform/x86: p2sb: Cache correct PCI bar for P2SB on Gemini Lake
platform/x86: panasonic-laptop: Return errno correctly in show callback
Documentation: alienware-wmi: Describe THERMAL_INFORMATION operation 0x02
alienware-wmi: create_thermal_profile() no longer brute-forces IDs
alienware-wmi: Adds support to Alienware x17 R2
alienware-wmi: extends the list of supported models
alienware-wmi: order alienware_quirks[] alphabetically
platform/x86/intel/pmt: allow user offset for PMT callbacks
platform/x86/amd/hsmp: Change the error type
platform/x86/amd/hsmp: Add new error code and error logs
platform/x86/amd: amd_3d_vcache: Add sysfs ABI documentation
platform/x86/amd: amd_3d_vcache: Add AMD 3D V-Cache optimizer driver
intel-hid: fix volume buttons on Thinkpad X12 Detachable Tablet Gen 1
platform/x86/amd/hsmp: mark hsmp_msg_desc_table[] as maybe_unused
platform/x86: asus-wmi: Use platform_profile_cycle()
platform/x86: asus-wmi: Fix inconsistent use of thermal policies
platform/x86: hp: hp-bioscfg: remove redundant if statement
MAINTAINERS: Update ISHTP ECLITE maintainer entry
platform/x86: x86-android-tablets: Add support for Vexia EDU ATLA 10 tablet
platform/x86: x86-android-tablets: Add support for getting i2c_adapter by PCI parent devname()
...
Diffstat (limited to 'drivers/platform/x86')
81 files changed, 2663 insertions, 1500 deletions
diff --git a/drivers/platform/x86/acer-wmi.c b/drivers/platform/x86/acer-wmi.c index 7169b84ccdb6..d09baa3d3d90 100644 --- a/drivers/platform/x86/acer-wmi.c +++ b/drivers/platform/x86/acer-wmi.c @@ -258,11 +258,6 @@ enum interface_flags { ACER_WMID_v2, }; -#define ACER_DEFAULT_WIRELESS 0 -#define ACER_DEFAULT_BLUETOOTH 0 -#define ACER_DEFAULT_MAILLED 0 -#define ACER_DEFAULT_THREEG 0 - static int max_brightness = 0xF; static int mailled = -1; @@ -2641,7 +2636,7 @@ static struct platform_driver acer_platform_driver = { .pm = &acer_pm, }, .probe = acer_platform_probe, - .remove_new = acer_platform_remove, + .remove = acer_platform_remove, .shutdown = acer_platform_shutdown, }; diff --git a/drivers/platform/x86/adv_swbutton.c b/drivers/platform/x86/adv_swbutton.c index 6b23ba78e028..6fa60f3fc53c 100644 --- a/drivers/platform/x86/adv_swbutton.c +++ b/drivers/platform/x86/adv_swbutton.c @@ -110,7 +110,7 @@ static struct platform_driver adv_swbutton_driver = { .acpi_match_table = button_device_ids, }, .probe = adv_swbutton_probe, - .remove_new = adv_swbutton_remove, + .remove = adv_swbutton_remove, }; module_platform_driver(adv_swbutton_driver); diff --git a/drivers/platform/x86/amd/Kconfig b/drivers/platform/x86/amd/Kconfig index f88682d36447..c3e086ea64fc 100644 --- a/drivers/platform/x86/amd/Kconfig +++ b/drivers/platform/x86/amd/Kconfig @@ -3,21 +3,21 @@ # AMD x86 Platform Specific Drivers # +source "drivers/platform/x86/amd/hsmp/Kconfig" source "drivers/platform/x86/amd/pmf/Kconfig" source "drivers/platform/x86/amd/pmc/Kconfig" -config AMD_HSMP - tristate "AMD HSMP Driver" - depends on AMD_NB && X86_64 && ACPI +config AMD_3D_VCACHE + tristate "AMD 3D V-Cache Performance Optimizer Driver" + depends on 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. - - Host System Management Port (HSMP) interface is a mailbox interface - between the x86 core and the System Management Unit (SMU) firmware. + The driver provides a sysfs interface, enabling the setting of a bias + that alters CPU core reordering. This bias prefers cores with higher + frequencies or larger L3 caches on processors supporting AMD 3D V-Cache + technology. If you choose to compile this driver as a module the module will be - called amd_hsmp. + called amd_3d_vcache. config AMD_WBRF bool "AMD Wifi RF Band mitigations (WBRF)" diff --git a/drivers/platform/x86/amd/Makefile b/drivers/platform/x86/amd/Makefile index dcec0a46f8af..56f62fc9c97b 100644 --- a/drivers/platform/x86/amd/Makefile +++ b/drivers/platform/x86/amd/Makefile @@ -4,8 +4,9 @@ # AMD x86 Platform-Specific Drivers # +obj-$(CONFIG_AMD_3D_VCACHE) += amd_3d_vcache.o +amd_3d_vcache-objs := x3d_vcache.o obj-$(CONFIG_AMD_PMC) += pmc/ -amd_hsmp-y := hsmp.o -obj-$(CONFIG_AMD_HSMP) += amd_hsmp.o +obj-$(CONFIG_AMD_HSMP) += hsmp/ obj-$(CONFIG_AMD_PMF) += pmf/ obj-$(CONFIG_AMD_WBRF) += wbrf.o diff --git a/drivers/platform/x86/amd/hsmp.c b/drivers/platform/x86/amd/hsmp.c deleted file mode 100644 index 8fcf38eed7f0..000000000000 --- a/drivers/platform/x86/amd/hsmp.c +++ /dev/null @@ -1,988 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * AMD HSMP Platform Driver - * Copyright (c) 2022, AMD. - * All Rights Reserved. - * - * This file provides a device implementation for HSMP interface - */ - -#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt - -#include <asm/amd_hsmp.h> -#include <asm/amd_nb.h> -#include <linux/delay.h> -#include <linux/io.h> -#include <linux/miscdevice.h> -#include <linux/module.h> -#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.2" -#define ACPI_HSMP_DEVICE_HID "AMDI0097" - -/* HSMP Status / Error codes */ -#define HSMP_STATUS_NOT_READY 0x00 -#define HSMP_STATUS_OK 0x01 -#define HSMP_ERR_INVALID_MSG 0xFE -#define HSMP_ERR_INVALID_INPUT 0xFF - -/* Timeout in millsec */ -#define HSMP_MSG_TIMEOUT 100 -#define HSMP_SHORT_SLEEP 1 - -#define HSMP_WR true -#define HSMP_RD false - -/* - * To access specific HSMP mailbox register, s/w writes the SMN address of HSMP mailbox - * 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_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 - -#define HSMP_CDEV_NAME "hsmp_cdev" -#define HSMP_DEVNODE_NAME "hsmp" -#define HSMP_METRICS_TABLE_NAME "metrics_bin" - -#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; - u32 proto_ver; - u16 num_sockets; - bool is_acpi_device; - bool is_probed; -}; - -static struct hsmp_plat_device plat_dev; - -static int amd_hsmp_pci_rdwr(struct hsmp_socket *sock, u32 offset, - u32 *value, bool write) -{ - int ret; - - 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(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 - * 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. - * - * Returns 0 for success and populates the requested number of arguments. - * Returns a negative error code for failure. - */ -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(sock, mbinfo->msg_resp_off, &mbox_status, HSMP_WR); - if (ret) { - pr_err("Error %d clearing mailbox status register\n", ret); - return ret; - } - - index = 0; - /* Write any message arguments */ - while (index < msg->num_args) { - 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); - return ret; - } - index++; - } - - /* Write the message ID which starts the operation */ - 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; - } - - /* - * Depending on when the trigger write completes relative to the SMU - * firmware 1 ms cycle, the operation may take from tens of us to 1 ms - * to complete. Some operations may take more. Therefore we will try - * a few short duration sleeps and switch to long sleeps if we don't - * succeed quickly. - */ - short_sleep = jiffies + msecs_to_jiffies(HSMP_SHORT_SLEEP); - timeout = jiffies + msecs_to_jiffies(HSMP_MSG_TIMEOUT); - - while (time_before(jiffies, timeout)) { - 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; - } - - if (mbox_status != HSMP_STATUS_NOT_READY) - break; - if (time_before(jiffies, short_sleep)) - usleep_range(50, 100); - else - usleep_range(1000, 2000); - } - - if (unlikely(mbox_status == HSMP_STATUS_NOT_READY)) { - return -ETIMEDOUT; - } else if (unlikely(mbox_status == HSMP_ERR_INVALID_MSG)) { - return -ENOMSG; - } else if (unlikely(mbox_status == HSMP_ERR_INVALID_INPUT)) { - return -EINVAL; - } else if (unlikely(mbox_status != HSMP_STATUS_OK)) { - pr_err("Message ID %u unknown failure (status = 0x%X)\n", - msg->msg_id, mbox_status); - return -EIO; - } - - /* - * SMU has responded OK. Read response data. - * SMU reads the input arguments from eight 32 bit registers starting - * from SMN_HSMP_MSG_DATA and writes the response data to the same - * SMN_HSMP_MSG_DATA address. - * We copy the response data if any, back to the args[]. - */ - index = 0; - while (index < msg->response_sz) { - 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", - ret, index, msg->msg_id); - break; - } - index++; - } - - return ret; -} - -static int validate_message(struct hsmp_message *msg) -{ - /* msg_id against valid range of message IDs */ - if (msg->msg_id < HSMP_TEST || msg->msg_id >= HSMP_MSG_ID_MAX) - return -ENOMSG; - - /* msg_id is a reserved message ID */ - if (hsmp_msg_desc_table[msg->msg_id].type == HSMP_RSVD) - return -ENOMSG; - - /* num_args and response_sz against the HSMP spec */ - if (msg->num_args != hsmp_msg_desc_table[msg->msg_id].num_args || - msg->response_sz != hsmp_msg_desc_table[msg->msg_id].response_sz) - return -EINVAL; - - return 0; -} - -int hsmp_send_message(struct hsmp_message *msg) -{ - struct hsmp_socket *sock; - int ret; - - if (!msg) - return -EINVAL; - 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. - * In SMP system timeout of 100 millisecs should - * be enough for the previous thread to finish the operation - */ - ret = down_timeout(&sock->hsmp_sem, msecs_to_jiffies(HSMP_MSG_TIMEOUT)); - if (ret < 0) - return ret; - - ret = __hsmp_send_message(sock, msg); - - up(&sock->hsmp_sem); - - return ret; -} -EXPORT_SYMBOL_GPL(hsmp_send_message); - -static int hsmp_test(u16 sock_ind, u32 value) -{ - struct hsmp_message msg = { 0 }; - int ret; - - /* - * Test the hsmp port by performing TEST command. The test message - * takes one argument and returns the value of that argument + 1. - */ - msg.msg_id = HSMP_TEST; - msg.num_args = 1; - msg.response_sz = 1; - msg.args[0] = value; - msg.sock_ind = sock_ind; - - ret = hsmp_send_message(&msg); - if (ret) - return ret; - - /* Check the response value */ - if (msg.args[0] != (value + 1)) { - 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; - } - - return ret; -} - -static long hsmp_ioctl(struct file *fp, unsigned int cmd, unsigned long arg) -{ - int __user *arguser = (int __user *)arg; - struct hsmp_message msg = { 0 }; - int ret; - - if (copy_struct_from_user(&msg, sizeof(msg), arguser, sizeof(struct hsmp_message))) - return -EFAULT; - - /* - * Check msg_id is within the range of supported msg ids - * i.e within the array bounds of hsmp_msg_desc_table - */ - if (msg.msg_id < HSMP_TEST || msg.msg_id >= HSMP_MSG_ID_MAX) - return -ENOMSG; - - switch (fp->f_mode & (FMODE_WRITE | FMODE_READ)) { - case FMODE_WRITE: - /* - * Device is opened in O_WRONLY mode - * Execute only set/configure commands - */ - if (hsmp_msg_desc_table[msg.msg_id].type != HSMP_SET) - return -EINVAL; - break; - case FMODE_READ: - /* - * Device is opened in O_RDONLY mode - * Execute only get/monitor commands - */ - if (hsmp_msg_desc_table[msg.msg_id].type != HSMP_GET) - return -EINVAL; - break; - case FMODE_READ | FMODE_WRITE: - /* - * Device is opened in O_RDWR mode - * Execute both get/monitor and set/configure commands - */ - break; - default: - return -EINVAL; - } - - ret = hsmp_send_message(&msg); - if (ret) - return ret; - - if (hsmp_msg_desc_table[msg.msg_id].response_sz > 0) { - /* Copy results back to user for get/monitor commands */ - if (copy_to_user(arguser, &msg, sizeof(struct hsmp_message))) - return -EFAULT; - } - - return 0; -} - -static const struct file_operations hsmp_fops = { - .owner = THIS_MODULE, - .unlocked_ioctl = hsmp_ioctl, - .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) -{ - struct hsmp_socket *sock = bin_attr->private; - struct hsmp_message msg = { 0 }; - int ret; - - if (!sock) - return -EINVAL; - - /* Do not support lseek(), reads entire metric table */ - if (count < bin_attr->size) { - dev_err(sock->dev, "Wrong buffer size\n"); - return -EINVAL; - } - - msg.msg_id = HSMP_GET_METRIC_TABLE; - msg.sock_ind = sock->sock_ind; - - ret = hsmp_send_message(&msg); - if (ret) - return ret; - memcpy_fromio(buf, sock->metric_tbl_addr, bin_attr->size); - - return bin_attr->size; -} - -static int hsmp_get_tbl_dram_base(u16 sock_ind) -{ - struct hsmp_socket *sock = &plat_dev.sock[sock_ind]; - struct hsmp_message msg = { 0 }; - phys_addr_t dram_addr; - int ret; - - msg.sock_ind = sock_ind; - msg.response_sz = hsmp_msg_desc_table[HSMP_GET_METRIC_TABLE_DRAM_ADDR].response_sz; - msg.msg_id = HSMP_GET_METRIC_TABLE_DRAM_ADDR; - - ret = hsmp_send_message(&msg); - if (ret) - return ret; - - /* - * calculate the metric table DRAM address from lower and upper 32 bits - * sent from SMU and ioremap it to virtual address. - */ - dram_addr = msg.args[0] | ((u64)(msg.args[1]) << 32); - if (!dram_addr) { - dev_err(sock->dev, "Invalid DRAM address for metric table\n"); - return -ENOMEM; - } - sock->metric_tbl_addr = devm_ioremap(sock->dev, dram_addr, - sizeof(struct hsmp_metric_table)); - if (!sock->metric_tbl_addr) { - dev_err(sock->dev, "Failed to ioremap metric table addr\n"); - return -ENOMEM; - } - return 0; -} - -static umode_t hsmp_is_sock_attr_visible(struct kobject *kobj, - struct bin_attribute *battr, int id) -{ - if (plat_dev.proto_ver == HSMP_PROTO_VER6) - return battr->attr.mode; - else - return 0; -} - -static int hsmp_init_metric_tbl_bin_attr(struct bin_attribute **hattrs, u16 sock_ind) -{ - struct bin_attribute *hattr = &plat_dev.sock[sock_ind].hsmp_attr; - - sysfs_bin_attr_init(hattr); - hattr->attr.name = HSMP_METRICS_TABLE_NAME; - hattr->attr.mode = 0444; - hattr->read = hsmp_metric_tbl_read; - hattr->size = sizeof(struct hsmp_metric_table); - hattr->private = &plat_dev.sock[sock_ind]; - hattrs[0] = hattr; - - if (plat_dev.proto_ver == HSMP_PROTO_VER6) - return hsmp_get_tbl_dram_base(sock_ind); - else - return 0; -} - -/* One bin sysfs for metrics table */ -#define NUM_HSMP_ATTRS 1 - -static int hsmp_create_attr_list(struct attribute_group *attr_grp, - struct device *dev, u16 sock_ind) -{ - 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; - u16 i; - - 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(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; - attr_grp->is_bin_visible = hsmp_is_sock_attr_visible; - hsmp_attr_grps[i] = attr_grp; - - hsmp_create_attr_list(attr_grp, dev, i); - } - - return 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(u16 sock_ind) -{ - struct hsmp_message msg = { 0 }; - int ret; - - msg.msg_id = HSMP_GET_PROTO_VER; - msg.sock_ind = sock_ind; - msg.response_sz = hsmp_msg_desc_table[HSMP_GET_PROTO_VER].response_sz; - - ret = hsmp_send_message(&msg); - if (!ret) - plat_dev.proto_ver = msg.args[0]; - - return ret; -} - -static inline bool is_f1a_m0h(void) -{ - if (boot_cpu_data.x86 == 0x1A && boot_cpu_data.x86_model <= 0x0F) - return true; - - 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++) { - 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; - } - } - - 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(sock_ind); - if (ret) { - dev_err(&pdev->dev, "Failed to read HSMP protocol version\n"); - return ret; - } - - 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(&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; - -} - -static void hsmp_pltdrv_remove(struct platform_device *pdev) -{ - /* - * 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 = { - .probe = hsmp_pltdrv_probe, - .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; -} - -/* - * This check is only needed for backward compatibility of previous platforms. - * All new platforms are expected to support ACPI based probing. - */ -static bool legacy_hsmp_support(void) -{ - if (boot_cpu_data.x86_vendor != X86_VENDOR_AMD) - return false; - - switch (boot_cpu_data.x86) { - case 0x19: - switch (boot_cpu_data.x86_model) { - case 0x00 ... 0x1F: - case 0x30 ... 0x3F: - case 0x90 ... 0x9F: - case 0xA0 ... 0xAF: - return true; - default: - return false; - } - case 0x1A: - switch (boot_cpu_data.x86_model) { - case 0x00 ... 0x1F: - return true; - default: - return false; - } - default: - return false; - } - - return false; -} - -static int __init hsmp_plt_init(void) -{ - int ret = -ENODEV; - - /* - * amd_nb_num() returns number of SMN/DF interfaces present in the system - * 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 || plat_dev.num_sockets > MAX_AMD_SOCKETS) - return ret; - - ret = platform_driver_register(&amd_hsmp_driver); - if (ret) - return ret; - - if (!plat_dev.is_acpi_device) { - if (legacy_hsmp_support()) { - /* Not ACPI device, but supports HSMP, register a plat_dev */ - ret = hsmp_plat_dev_register(); - } else { - /* Not ACPI, Does not support HSMP */ - pr_info("HSMP is not supported on Family:%x model:%x\n", - boot_cpu_data.x86, boot_cpu_data.x86_model); - ret = -ENODEV; - } - if (ret) - platform_driver_unregister(&amd_hsmp_driver); - } - - return ret; -} - -static void __exit hsmp_plt_exit(void) -{ - platform_device_unregister(amd_hsmp_platdev); - platform_driver_unregister(&amd_hsmp_driver); -} - -device_initcall(hsmp_plt_init); -module_exit(hsmp_plt_exit); - -MODULE_DESCRIPTION("AMD HSMP Platform Interface Driver"); -MODULE_VERSION(DRIVER_VERSION); -MODULE_LICENSE("GPL v2"); diff --git a/drivers/platform/x86/amd/hsmp/Kconfig b/drivers/platform/x86/amd/hsmp/Kconfig new file mode 100644 index 000000000000..7d10d4462a45 --- /dev/null +++ b/drivers/platform/x86/amd/hsmp/Kconfig @@ -0,0 +1,47 @@ +# SPDX-License-Identifier: GPL-2.0-only +# +# AMD HSMP Driver +# + +config AMD_HSMP + tristate + +menu "AMD HSMP Driver" + depends on AMD_NB || COMPILE_TEST + +config AMD_HSMP_ACPI + tristate "AMD HSMP ACPI device driver" + depends on ACPI + select AMD_HSMP + help + Host System Management Port (HSMP) interface is a mailbox interface + between the x86 core and the System Management Unit (SMU) firmware. + The driver provides a way for user space tools to monitor and manage + system management functionality on EPYC and MI300A server CPUs + from AMD. + + This option supports ACPI based probing. + You may enable this, if your platform BIOS provides an ACPI object + as described in amd_hsmp.rst document. + + If you choose to compile this driver as a module the module will be + called hsmp_acpi. + +config AMD_HSMP_PLAT + tristate "AMD HSMP platform device driver" + select AMD_HSMP + help + Host System Management Port (HSMP) interface is a mailbox interface + between the x86 core and the System Management Unit (SMU) firmware. + The driver provides a way for user space tools to monitor and manage + system management functionality on EPYC and MI300A server CPUs + from AMD. + + This option supports platform device based probing. + You may enable this, if your platform BIOS does not provide + HSMP ACPI object. + + If you choose to compile this driver as a module the module will be + called amd_hsmp. + +endmenu diff --git a/drivers/platform/x86/amd/hsmp/Makefile b/drivers/platform/x86/amd/hsmp/Makefile new file mode 100644 index 000000000000..3175d8885e87 --- /dev/null +++ b/drivers/platform/x86/amd/hsmp/Makefile @@ -0,0 +1,12 @@ +# SPDX-License-Identifier: GPL-2.0 +# +# Makefile for drivers/platform/x86/amd/hsmp +# AMD HSMP Driver +# + +obj-$(CONFIG_AMD_HSMP) += hsmp_common.o +hsmp_common-objs := hsmp.o +obj-$(CONFIG_AMD_HSMP_PLAT) += amd_hsmp.o +amd_hsmp-objs := plat.o +obj-$(CONFIG_AMD_HSMP_ACPI) += hsmp_acpi.o +hsmp_acpi-objs := acpi.o diff --git a/drivers/platform/x86/amd/hsmp/acpi.c b/drivers/platform/x86/amd/hsmp/acpi.c new file mode 100644 index 000000000000..4aa4d66f491a --- /dev/null +++ b/drivers/platform/x86/amd/hsmp/acpi.c @@ -0,0 +1,378 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * AMD HSMP Platform Driver + * Copyright (c) 2024, AMD. + * All Rights Reserved. + * + * This file provides an ACPI based driver implementation for HSMP interface. + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include <asm/amd_hsmp.h> +#include <asm/amd_nb.h> + +#include <linux/acpi.h> +#include <linux/device.h> +#include <linux/dev_printk.h> +#include <linux/ioport.h> +#include <linux/kstrtox.h> +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/sysfs.h> +#include <linux/uuid.h> + +#include <uapi/asm-generic/errno-base.h> + +#include "hsmp.h" + +#define DRIVER_NAME "amd_hsmp" +#define DRIVER_VERSION "2.3" +#define ACPI_HSMP_DEVICE_HID "AMDI0097" + +/* These are the strings specified in ACPI table */ +#define MSG_IDOFF_STR "MsgIdOffset" +#define MSG_ARGOFF_STR "MsgArgOffset" +#define MSG_RESPOFF_STR "MsgRspOffset" + +static struct hsmp_plat_device *hsmp_pdev; + +static int 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); + + return 0; +} + +/* 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 = &hsmp_pdev->sock[sock_ind]; + int ret; + + sock->sock_ind = sock_ind; + sock->dev = dev; + sock->amd_hsmp_rdwr = amd_hsmp_acpi_rdwr; + + sema_init(&sock->hsmp_sem, 1); + + dev_set_drvdata(dev, sock); + + /* 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_acpi_read(struct file *filp, struct kobject *kobj, + struct bin_attribute *bin_attr, char *buf, + loff_t off, size_t count) +{ + struct device *dev = container_of(kobj, struct device, kobj); + struct hsmp_socket *sock = dev_get_drvdata(dev); + + return hsmp_metric_tbl_read(sock, buf, count); +} + +static umode_t hsmp_is_sock_attr_visible(struct kobject *kobj, + struct bin_attribute *battr, int id) +{ + if (hsmp_pdev->proto_ver == HSMP_PROTO_VER6) + return battr->attr.mode; + + return 0; +} + +static int init_acpi(struct device *dev) +{ + u16 sock_ind; + int ret; + + ret = hsmp_get_uid(dev, &sock_ind); + if (ret) + return ret; + if (sock_ind >= hsmp_pdev->num_sockets) + return -EINVAL; + + ret = hsmp_parse_acpi_table(dev, sock_ind); + if (ret) { + dev_err(dev, "Failed to parse ACPI table\n"); + return ret; + } + + /* Test the hsmp interface */ + ret = hsmp_test(sock_ind, 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; + } + + ret = hsmp_cache_proto_ver(sock_ind); + if (ret) { + dev_err(dev, "Failed to read HSMP protocol version\n"); + return ret; + } + + if (hsmp_pdev->proto_ver == HSMP_PROTO_VER6) { + ret = hsmp_get_tbl_dram_base(sock_ind); + if (ret) + dev_err(dev, "Failed to init metric table\n"); + } + + return ret; +} + +static struct bin_attribute hsmp_metric_tbl_attr = { + .attr = { .name = HSMP_METRICS_TABLE_NAME, .mode = 0444}, + .read = hsmp_metric_tbl_acpi_read, + .size = sizeof(struct hsmp_metric_table), +}; + +static struct bin_attribute *hsmp_attr_list[] = { + &hsmp_metric_tbl_attr, + NULL +}; + +static struct attribute_group hsmp_attr_grp = { + .bin_attrs = hsmp_attr_list, + .is_bin_visible = hsmp_is_sock_attr_visible, +}; + +static const struct attribute_group *hsmp_groups[] = { + &hsmp_attr_grp, + NULL +}; + +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_acpi_probe(struct platform_device *pdev) +{ + int ret; + + hsmp_pdev = get_hsmp_pdev(); + if (!hsmp_pdev) + return -ENOMEM; + + if (!hsmp_pdev->is_probed) { + hsmp_pdev->num_sockets = amd_nb_num(); + if (hsmp_pdev->num_sockets == 0 || hsmp_pdev->num_sockets > MAX_AMD_SOCKETS) + return -ENODEV; + + hsmp_pdev->sock = devm_kcalloc(&pdev->dev, hsmp_pdev->num_sockets, + sizeof(*hsmp_pdev->sock), + GFP_KERNEL); + if (!hsmp_pdev->sock) + return -ENOMEM; + } + + ret = init_acpi(&pdev->dev); + if (ret) { + dev_err(&pdev->dev, "Failed to initialize HSMP interface.\n"); + return ret; + } + + if (!hsmp_pdev->is_probed) { + ret = hsmp_misc_register(&pdev->dev); + if (ret) + return ret; + hsmp_pdev->is_probed = true; + } + + return 0; +} + +static void hsmp_acpi_remove(struct platform_device *pdev) +{ + /* + * We register only one misc_device even on multi-socket system. + * So, deregister should happen only once. + */ + if (hsmp_pdev->is_probed) { + hsmp_misc_deregister(); + hsmp_pdev->is_probed = false; + } +} + +static struct platform_driver amd_hsmp_driver = { + .probe = hsmp_acpi_probe, + .remove = hsmp_acpi_remove, + .driver = { + .name = DRIVER_NAME, + .acpi_match_table = amd_hsmp_acpi_ids, + .dev_groups = hsmp_groups, + }, +}; + +module_platform_driver(amd_hsmp_driver); + +MODULE_IMPORT_NS(AMD_HSMP); +MODULE_DESCRIPTION("AMD HSMP Platform Interface Driver"); +MODULE_VERSION(DRIVER_VERSION); +MODULE_LICENSE("GPL"); diff --git a/drivers/platform/x86/amd/hsmp/hsmp.c b/drivers/platform/x86/amd/hsmp/hsmp.c new file mode 100644 index 000000000000..f29dd93fbf0b --- /dev/null +++ b/drivers/platform/x86/amd/hsmp/hsmp.c @@ -0,0 +1,408 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * AMD HSMP Platform Driver + * Copyright (c) 2022, AMD. + * All Rights Reserved. + * + * This file provides a device implementation for HSMP interface + */ + +#include <asm/amd_hsmp.h> +#include <asm/amd_nb.h> + +#include <linux/acpi.h> +#include <linux/delay.h> +#include <linux/device.h> +#include <linux/semaphore.h> +#include <linux/sysfs.h> + +#include "hsmp.h" + +/* HSMP Status / Error codes */ +#define HSMP_STATUS_NOT_READY 0x00 +#define HSMP_STATUS_OK 0x01 +#define HSMP_ERR_INVALID_MSG 0xFE +#define HSMP_ERR_INVALID_INPUT 0xFF +#define HSMP_ERR_PREREQ_NOT_SATISFIED 0xFD +#define HSMP_ERR_SMU_BUSY 0xFC + +/* Timeout in millsec */ +#define HSMP_MSG_TIMEOUT 100 +#define HSMP_SHORT_SLEEP 1 + +#define HSMP_WR true +#define HSMP_RD false + +#define DRIVER_VERSION "2.3" + +static struct hsmp_plat_device hsmp_pdev; + +/* + * 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. + * + * Returns 0 for success and populates the requested number of arguments. + * Returns a negative error code for failure. + */ +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 = sock->amd_hsmp_rdwr(sock, mbinfo->msg_resp_off, &mbox_status, HSMP_WR); + if (ret) { + dev_err(sock->dev, "Error %d clearing mailbox status register\n", ret); + return ret; + } + + index = 0; + /* Write any message arguments */ + while (index < msg->num_args) { + ret = sock->amd_hsmp_rdwr(sock, mbinfo->msg_arg_off + (index << 2), + &msg->args[index], HSMP_WR); + if (ret) { + dev_err(sock->dev, "Error %d writing message argument %d\n", ret, index); + return ret; + } + index++; + } + + /* Write the message ID which starts the operation */ + ret = sock->amd_hsmp_rdwr(sock, mbinfo->msg_id_off, &msg->msg_id, HSMP_WR); + if (ret) { + dev_err(sock->dev, "Error %d writing message ID %u\n", ret, msg->msg_id); + return ret; + } + + /* + * Depending on when the trigger write completes relative to the SMU + * firmware 1 ms cycle, the operation may take from tens of us to 1 ms + * to complete. Some operations may take more. Therefore we will try + * a few short duration sleeps and switch to long sleeps if we don't + * succeed quickly. + */ + short_sleep = jiffies + msecs_to_jiffies(HSMP_SHORT_SLEEP); + timeout = jiffies + msecs_to_jiffies(HSMP_MSG_TIMEOUT); + + while (time_before(jiffies, timeout)) { + ret = sock->amd_hsmp_rdwr(sock, mbinfo->msg_resp_off, &mbox_status, HSMP_RD); + if (ret) { + dev_err(sock->dev, "Error %d reading mailbox status\n", ret); + return ret; + } + + if (mbox_status != HSMP_STATUS_NOT_READY) + break; + if (time_before(jiffies, short_sleep)) + usleep_range(50, 100); + else + usleep_range(1000, 2000); + } + + if (unlikely(mbox_status == HSMP_STATUS_NOT_READY)) { + dev_err(sock->dev, "Message ID 0x%X failure : SMU tmeout (status = 0x%X)\n", + msg->msg_id, mbox_status); + return -ETIMEDOUT; + } else if (unlikely(mbox_status == HSMP_ERR_INVALID_MSG)) { + dev_err(sock->dev, "Message ID 0x%X failure : Invalid message (status = 0x%X)\n", + msg->msg_id, mbox_status); + return -ENOMSG; + } else if (unlikely(mbox_status == HSMP_ERR_INVALID_INPUT)) { + dev_err(sock->dev, "Message ID 0x%X failure : Invalid arguments (status = 0x%X)\n", + msg->msg_id, mbox_status); + return -EINVAL; + } else if (unlikely(mbox_status == HSMP_ERR_PREREQ_NOT_SATISFIED)) { + dev_err(sock->dev, "Message ID 0x%X failure : Prerequisite not satisfied (status = 0x%X)\n", + msg->msg_id, mbox_status); + return -EREMOTEIO; + } else if (unlikely(mbox_status == HSMP_ERR_SMU_BUSY)) { + dev_err(sock->dev, "Message ID 0x%X failure : SMU BUSY (status = 0x%X)\n", + msg->msg_id, mbox_status); + return -EBUSY; + } else if (unlikely(mbox_status != HSMP_STATUS_OK)) { + dev_err(sock->dev, "Message ID 0x%X unknown failure (status = 0x%X)\n", + msg->msg_id, mbox_status); + return -EIO; + } + + /* + * SMU has responded OK. Read response data. + * SMU reads the input arguments from eight 32 bit registers starting + * from SMN_HSMP_MSG_DATA and writes the response data to the same + * SMN_HSMP_MSG_DATA address. + * We copy the response data if any, back to the args[]. + */ + index = 0; + while (index < msg->response_sz) { + ret = sock->amd_hsmp_rdwr(sock, mbinfo->msg_arg_off + (index << 2), + &msg->args[index], HSMP_RD); + if (ret) { + dev_err(sock->dev, "Error %d reading response %u for message ID:%u\n", + ret, index, msg->msg_id); + break; + } + index++; + } + + return ret; +} + +static int validate_message(struct hsmp_message *msg) +{ + /* msg_id against valid range of message IDs */ + if (msg->msg_id < HSMP_TEST || msg->msg_id >= HSMP_MSG_ID_MAX) + return -ENOMSG; + + /* msg_id is a reserved message ID */ + if (hsmp_msg_desc_table[msg->msg_id].type == HSMP_RSVD) + return -ENOMSG; + + /* num_args and response_sz against the HSMP spec */ + if (msg->num_args != hsmp_msg_desc_table[msg->msg_id].num_args || + msg->response_sz != hsmp_msg_desc_table[msg->msg_id].response_sz) + return -EINVAL; + + return 0; +} + +int hsmp_send_message(struct hsmp_message *msg) +{ + struct hsmp_socket *sock; + int ret; + + if (!msg) + return -EINVAL; + ret = validate_message(msg); + if (ret) + return ret; + + if (!hsmp_pdev.sock || msg->sock_ind >= hsmp_pdev.num_sockets) + return -ENODEV; + sock = &hsmp_pdev.sock[msg->sock_ind]; + + /* + * The time taken by smu operation to complete is between + * 10us to 1ms. Sometime it may take more time. + * In SMP system timeout of 100 millisecs should + * be enough for the previous thread to finish the operation + */ + ret = down_timeout(&sock->hsmp_sem, msecs_to_jiffies(HSMP_MSG_TIMEOUT)); + if (ret < 0) + return ret; + + ret = __hsmp_send_message(sock, msg); + + up(&sock->hsmp_sem); + + return ret; +} +EXPORT_SYMBOL_NS_GPL(hsmp_send_message, AMD_HSMP); + +int hsmp_test(u16 sock_ind, u32 value) +{ + struct hsmp_message msg = { 0 }; + int ret; + + /* + * Test the hsmp port by performing TEST command. The test message + * takes one argument and returns the value of that argument + 1. + */ + msg.msg_id = HSMP_TEST; + msg.num_args = 1; + msg.response_sz = 1; + msg.args[0] = value; + msg.sock_ind = sock_ind; + + ret = hsmp_send_message(&msg); + if (ret) + return ret; + + /* Check the response value */ + if (msg.args[0] != (value + 1)) { + dev_err(hsmp_pdev.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; + } + + return ret; +} +EXPORT_SYMBOL_NS_GPL(hsmp_test, AMD_HSMP); + +long hsmp_ioctl(struct file *fp, unsigned int cmd, unsigned long arg) +{ + int __user *arguser = (int __user *)arg; + struct hsmp_message msg = { 0 }; + int ret; + + if (copy_struct_from_user(&msg, sizeof(msg), arguser, sizeof(struct hsmp_message))) + return -EFAULT; + + /* + * Check msg_id is within the range of supported msg ids + * i.e within the array bounds of hsmp_msg_desc_table + */ + if (msg.msg_id < HSMP_TEST || msg.msg_id >= HSMP_MSG_ID_MAX) + return -ENOMSG; + + switch (fp->f_mode & (FMODE_WRITE | FMODE_READ)) { + case FMODE_WRITE: + /* + * Device is opened in O_WRONLY mode + * Execute only set/configure commands + */ + if (hsmp_msg_desc_table[msg.msg_id].type != HSMP_SET) + return -EPERM; + break; + case FMODE_READ: + /* + * Device is opened in O_RDONLY mode + * Execute only get/monitor commands + */ + if (hsmp_msg_desc_table[msg.msg_id].type != HSMP_GET) + return -EPERM; + break; + case FMODE_READ | FMODE_WRITE: + /* + * Device is opened in O_RDWR mode + * Execute both get/monitor and set/configure commands + */ + break; + default: + return -EPERM; + } + + ret = hsmp_send_message(&msg); + if (ret) + return ret; + + if (hsmp_msg_desc_table[msg.msg_id].response_sz > 0) { + /* Copy results back to user for get/monitor commands */ + if (copy_to_user(arguser, &msg, sizeof(struct hsmp_message))) + return -EFAULT; + } + + return 0; +} + +ssize_t hsmp_metric_tbl_read(struct hsmp_socket *sock, char *buf, size_t size) +{ + struct hsmp_message msg = { 0 }; + int ret; + + if (!sock || !buf) + return -EINVAL; + + /* Do not support lseek(), also don't allow more than the size of metric table */ + if (size != sizeof(struct hsmp_metric_table)) { + dev_err(sock->dev, "Wrong buffer size\n"); + return -EINVAL; + } + + msg.msg_id = HSMP_GET_METRIC_TABLE; + msg.sock_ind = sock->sock_ind; + + ret = hsmp_send_message(&msg); + if (ret) + return ret; + memcpy_fromio(buf, sock->metric_tbl_addr, size); + + return size; +} +EXPORT_SYMBOL_NS_GPL(hsmp_metric_tbl_read, AMD_HSMP); + +int hsmp_get_tbl_dram_base(u16 sock_ind) +{ + struct hsmp_socket *sock = &hsmp_pdev.sock[sock_ind]; + struct hsmp_message msg = { 0 }; + phys_addr_t dram_addr; + int ret; + + msg.sock_ind = sock_ind; + msg.response_sz = hsmp_msg_desc_table[HSMP_GET_METRIC_TABLE_DRAM_ADDR].response_sz; + msg.msg_id = HSMP_GET_METRIC_TABLE_DRAM_ADDR; + + ret = hsmp_send_message(&msg); + if (ret) + return ret; + + /* + * calculate the metric table DRAM address from lower and upper 32 bits + * sent from SMU and ioremap it to virtual address. + */ + dram_addr = msg.args[0] | ((u64)(msg.args[1]) << 32); + if (!dram_addr) { + dev_err(sock->dev, "Invalid DRAM address for metric table\n"); + return -ENOMEM; + } + sock->metric_tbl_addr = devm_ioremap(sock->dev, dram_addr, + sizeof(struct hsmp_metric_table)); + if (!sock->metric_tbl_addr) { + dev_err(sock->dev, "Failed to ioremap metric table addr\n"); + return -ENOMEM; + } + return 0; +} +EXPORT_SYMBOL_NS_GPL(hsmp_get_tbl_dram_base, AMD_HSMP); + +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 = sock_ind; + msg.response_sz = hsmp_msg_desc_table[HSMP_GET_PROTO_VER].response_sz; + + ret = hsmp_send_message(&msg); + if (!ret) + hsmp_pdev.proto_ver = msg.args[0]; + + return ret; +} +EXPORT_SYMBOL_NS_GPL(hsmp_cache_proto_ver, AMD_HSMP); + +static const struct file_operations hsmp_fops = { + .owner = THIS_MODULE, + .unlocked_ioctl = hsmp_ioctl, + .compat_ioctl = hsmp_ioctl, +}; + +int hsmp_misc_register(struct device *dev) +{ + hsmp_pdev.mdev.name = HSMP_CDEV_NAME; + hsmp_pdev.mdev.minor = MISC_DYNAMIC_MINOR; + hsmp_pdev.mdev.fops = &hsmp_fops; + hsmp_pdev.mdev.parent = dev; + hsmp_pdev.mdev.nodename = HSMP_DEVNODE_NAME; + hsmp_pdev.mdev.mode = 0644; + + return misc_register(&hsmp_pdev.mdev); +} +EXPORT_SYMBOL_NS_GPL(hsmp_misc_register, AMD_HSMP); + +void hsmp_misc_deregister(void) +{ + misc_deregister(&hsmp_pdev.mdev); +} +EXPORT_SYMBOL_NS_GPL(hsmp_misc_deregister, AMD_HSMP); + +struct hsmp_plat_device *get_hsmp_pdev(void) +{ + return &hsmp_pdev; +} +EXPORT_SYMBOL_NS_GPL(get_hsmp_pdev, AMD_HSMP); + +MODULE_DESCRIPTION("AMD HSMP Common driver"); +MODULE_VERSION(DRIVER_VERSION); +MODULE_LICENSE("GPL"); diff --git a/drivers/platform/x86/amd/hsmp/hsmp.h b/drivers/platform/x86/amd/hsmp/hsmp.h new file mode 100644 index 000000000000..e852f0a947e4 --- /dev/null +++ b/drivers/platform/x86/amd/hsmp/hsmp.h @@ -0,0 +1,66 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * AMD HSMP Platform Driver + * Copyright (c) 2024, AMD. + * All Rights Reserved. + * + * Header file for HSMP driver + */ + +#ifndef HSMP_H +#define HSMP_H + +#include <linux/compiler_types.h> +#include <linux/device.h> +#include <linux/miscdevice.h> +#include <linux/pci.h> +#include <linux/semaphore.h> +#include <linux/sysfs.h> + +#define HSMP_METRICS_TABLE_NAME "metrics_bin" + +#define HSMP_ATTR_GRP_NAME_SIZE 10 + +#define MAX_AMD_SOCKETS 8 + +#define HSMP_CDEV_NAME "hsmp_cdev" +#define HSMP_DEVNODE_NAME "hsmp" + +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; + int (*amd_hsmp_rdwr)(struct hsmp_socket *sock, u32 off, u32 *val, bool rw); +}; + +struct hsmp_plat_device { + struct miscdevice mdev; + struct hsmp_socket *sock; + u32 proto_ver; + u16 num_sockets; + bool is_probed; +}; + +int hsmp_cache_proto_ver(u16 sock_ind); +int hsmp_test(u16 sock_ind, u32 value); +long hsmp_ioctl(struct file *fp, unsigned int cmd, unsigned long arg); +void hsmp_misc_deregister(void); +int hsmp_misc_register(struct device *dev); +int hsmp_get_tbl_dram_base(u16 sock_ind); +ssize_t hsmp_metric_tbl_read(struct hsmp_socket *sock, char *buf, size_t size); +struct hsmp_plat_device *get_hsmp_pdev(void); +#endif /* HSMP_H */ diff --git a/drivers/platform/x86/amd/hsmp/plat.c b/drivers/platform/x86/amd/hsmp/plat.c new file mode 100644 index 000000000000..f8e74c0392ba --- /dev/null +++ b/drivers/platform/x86/amd/hsmp/plat.c @@ -0,0 +1,338 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * AMD HSMP Platform Driver + * Copyright (c) 2024, AMD. + * All Rights Reserved. + * + * This file provides platform device implementations. + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include <asm/amd_hsmp.h> +#include <asm/amd_nb.h> + +#include <linux/device.h> +#include <linux/module.h> +#include <linux/pci.h> +#include <linux/platform_device.h> +#include <linux/sysfs.h> + +#include "hsmp.h" + +#define DRIVER_NAME "amd_hsmp" +#define DRIVER_VERSION "2.3" + +/* + * To access specific HSMP mailbox register, s/w writes the SMN address of HSMP mailbox + * 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_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 + +static struct hsmp_plat_device *hsmp_pdev; + +static int amd_hsmp_pci_rdwr(struct hsmp_socket *sock, u32 offset, + u32 *value, bool write) +{ + int ret; + + 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(sock->root, HSMP_DATA_REG, *value) + : pci_read_config_dword(sock->root, HSMP_DATA_REG, value)); + + return ret; +} + +static ssize_t hsmp_metric_tbl_plat_read(struct file *filp, struct kobject *kobj, + struct bin_attribute *bin_attr, char *buf, + loff_t off, size_t count) +{ + struct hsmp_socket *sock; + u16 sock_ind; + + sock_ind = (uintptr_t)bin_attr->private; + if (sock_ind >= hsmp_pdev->num_sockets) + return -EINVAL; + + sock = &hsmp_pdev->sock[sock_ind]; + + return hsmp_metric_tbl_read(sock, buf, count); +} + +static umode_t hsmp_is_sock_attr_visible(struct kobject *kobj, + struct bin_attribute *battr, int id) +{ + u16 sock_ind; + + sock_ind = (uintptr_t)battr->private; + + if (id == 0 && sock_ind >= hsmp_pdev->num_sockets) + return SYSFS_GROUP_INVISIBLE; + + if (hsmp_pdev->proto_ver == HSMP_PROTO_VER6) + return battr->attr.mode; + + return 0; +} + +/* + * AMD supports maximum of 8 sockets in a system. + * Static array of 8 + 1(for NULL) elements is created below + * to create sysfs groups for sockets. + * is_bin_visible function is used to show / hide the necessary groups. + */ +#define HSMP_BIN_ATTR(index, _list) \ +static struct bin_attribute attr##index = { \ + .attr = { .name = HSMP_METRICS_TABLE_NAME, .mode = 0444}, \ + .private = (void *)index, \ + .read = hsmp_metric_tbl_plat_read, \ + .size = sizeof(struct hsmp_metric_table), \ +}; \ +static struct bin_attribute _list[] = { \ + &attr##index, \ + NULL \ +} + +HSMP_BIN_ATTR(0, *sock0_attr_list); +HSMP_BIN_ATTR(1, *sock1_attr_list); +HSMP_BIN_ATTR(2, *sock2_attr_list); +HSMP_BIN_ATTR(3, *sock3_attr_list); +HSMP_BIN_ATTR(4, *sock4_attr_list); +HSMP_BIN_ATTR(5, *sock5_attr_list); +HSMP_BIN_ATTR(6, *sock6_attr_list); +HSMP_BIN_ATTR(7, *sock7_attr_list); + +#define HSMP_BIN_ATTR_GRP(index, _list, _name) \ +static struct attribute_group sock##index##_attr_grp = { \ + .bin_attrs = _list, \ + .is_bin_visible = hsmp_is_sock_attr_visible, \ + .name = #_name, \ +} + +HSMP_BIN_ATTR_GRP(0, sock0_attr_list, socket0); +HSMP_BIN_ATTR_GRP(1, sock1_attr_list, socket1); +HSMP_BIN_ATTR_GRP(2, sock2_attr_list, socket2); +HSMP_BIN_ATTR_GRP(3, sock3_attr_list, socket3); +HSMP_BIN_ATTR_GRP(4, sock4_attr_list, socket4); +HSMP_BIN_ATTR_GRP(5, sock5_attr_list, socket5); +HSMP_BIN_ATTR_GRP(6, sock6_attr_list, socket6); +HSMP_BIN_ATTR_GRP(7, sock7_attr_list, socket7); + +static const struct attribute_group *hsmp_groups[] = { + &sock0_attr_grp, + &sock1_attr_grp, + &sock2_attr_grp, + &sock3_attr_grp, + &sock4_attr_grp, + &sock5_attr_grp, + &sock6_attr_grp, + &sock7_attr_grp, + NULL +}; + +static inline bool is_f1a_m0h(void) +{ + if (boot_cpu_data.x86 == 0x1A && boot_cpu_data.x86_model <= 0x0F) + return true; + + return false; +} + +static int init_platform_device(struct device *dev) +{ + struct hsmp_socket *sock; + int ret, i; + + for (i = 0; i < hsmp_pdev->num_sockets; i++) { + if (!node_to_amd_nb(i)) + return -ENODEV; + sock = &hsmp_pdev->sock[i]; + sock->root = node_to_amd_nb(i)->root; + sock->sock_ind = i; + sock->dev = dev; + sock->mbinfo.base_addr = SMN_HSMP_BASE; + sock->amd_hsmp_rdwr = amd_hsmp_pci_rdwr; + + /* + * 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; + } + + ret = hsmp_cache_proto_ver(i); + if (ret) { + dev_err(dev, "Failed to read HSMP protocol version\n"); + return ret; + } + + if (hsmp_pdev->proto_ver == HSMP_PROTO_VER6) { + ret = hsmp_get_tbl_dram_base(i); + if (ret) + dev_err(dev, "Failed to init metric table\n"); + } + } + + return 0; +} + +static int hsmp_pltdrv_probe(struct platform_device *pdev) +{ + int ret; + + hsmp_pdev->sock = devm_kcalloc(&pdev->dev, hsmp_pdev->num_sockets, + sizeof(*hsmp_pdev->sock), + GFP_KERNEL); + if (!hsmp_pdev->sock) + return -ENOMEM; + + ret = init_platform_device(&pdev->dev); + if (ret) { + dev_err(&pdev->dev, "Failed to init HSMP mailbox\n"); + return ret; + } + + return hsmp_misc_register(&pdev->dev); +} + +static void hsmp_pltdrv_remove(struct platform_device *pdev) +{ + hsmp_misc_deregister(); +} + +static struct platform_driver amd_hsmp_driver = { + .probe = hsmp_pltdrv_probe, + .remove = hsmp_pltdrv_remove, + .driver = { + .name = DRIVER_NAME, + .dev_groups = hsmp_groups, + }, +}; + +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; +} + +/* + * This check is only needed for backward compatibility of previous platforms. + * All new platforms are expected to support ACPI based probing. + */ +static bool legacy_hsmp_support(void) +{ + if (boot_cpu_data.x86_vendor != X86_VENDOR_AMD) + return false; + + switch (boot_cpu_data.x86) { + case 0x19: + switch (boot_cpu_data.x86_model) { + case 0x00 ... 0x1F: + case 0x30 ... 0x3F: + case 0x90 ... 0x9F: + case 0xA0 ... 0xAF: + return true; + default: + return false; + } + case 0x1A: + switch (boot_cpu_data.x86_model) { + case 0x00 ... 0x1F: + return true; + default: + return false; + } + default: + return false; + } + + return false; +} + +static int __init hsmp_plt_init(void) +{ + int ret = -ENODEV; + + if (!legacy_hsmp_support()) { + pr_info("HSMP is not supported on Family:%x model:%x\n", + boot_cpu_data.x86, boot_cpu_data.x86_model); + return ret; + } + + hsmp_pdev = get_hsmp_pdev(); + if (!hsmp_pdev) + return -ENOMEM; + + /* + * amd_nb_num() returns number of SMN/DF interfaces present in the system + * if we have N SMN/DF interfaces that ideally means N sockets + */ + hsmp_pdev->num_sockets = amd_nb_num(); + if (hsmp_pdev->num_sockets == 0 || hsmp_pdev->num_sockets > MAX_AMD_SOCKETS) + return ret; + + ret = platform_driver_register(&amd_hsmp_driver); + if (ret) + return ret; + + ret = hsmp_plat_dev_register(); + if (ret) + platform_driver_unregister(&amd_hsmp_driver); + + return ret; +} + +static void __exit hsmp_plt_exit(void) +{ + platform_device_unregister(amd_hsmp_platdev); + platform_driver_unregister(&amd_hsmp_driver); +} + +device_initcall(hsmp_plt_init); +module_exit(hsmp_plt_exit); + +MODULE_IMPORT_NS(AMD_HSMP); +MODULE_DESCRIPTION("AMD HSMP Platform Interface Driver"); +MODULE_VERSION(DRIVER_VERSION); +MODULE_LICENSE("GPL"); diff --git a/drivers/platform/x86/amd/pmc/pmc.c b/drivers/platform/x86/amd/pmc/pmc.c index 5669f94c3d06..26b878ee5191 100644 --- a/drivers/platform/x86/amd/pmc/pmc.c +++ b/drivers/platform/x86/amd/pmc/pmc.c @@ -1161,7 +1161,7 @@ static struct platform_driver amd_pmc_driver = { .pm = pm_sleep_ptr(&amd_pmc_pm), }, .probe = amd_pmc_probe, - .remove_new = amd_pmc_remove, + .remove = amd_pmc_remove, }; module_platform_driver(amd_pmc_driver); diff --git a/drivers/platform/x86/amd/pmf/Kconfig b/drivers/platform/x86/amd/pmf/Kconfig index f4fa8bd8bda8..99d67cdbd91e 100644 --- a/drivers/platform/x86/amd/pmf/Kconfig +++ b/drivers/platform/x86/amd/pmf/Kconfig @@ -11,6 +11,7 @@ config AMD_PMF select ACPI_PLATFORM_PROFILE depends on TEE && AMDTEE depends on AMD_SFH_HID + depends on HAS_IOMEM help This driver provides support for the AMD Platform Management Framework. The goal is to enhance end user experience by making AMD PCs smarter, diff --git a/drivers/platform/x86/amd/pmf/acpi.c b/drivers/platform/x86/amd/pmf/acpi.c index d5b496433d69..1b9c7acf0ddf 100644 --- a/drivers/platform/x86/amd/pmf/acpi.c +++ b/drivers/platform/x86/amd/pmf/acpi.c @@ -433,37 +433,29 @@ int apmf_install_handler(struct amd_pmf_dev *pmf_dev) return 0; } -static acpi_status apmf_walk_resources(struct acpi_resource *res, void *data) +int apmf_check_smart_pc(struct amd_pmf_dev *pmf_dev) { - struct amd_pmf_dev *dev = data; + struct platform_device *pdev = to_platform_device(pmf_dev->dev); - switch (res->type) { - case ACPI_RESOURCE_TYPE_ADDRESS64: - dev->policy_addr = res->data.address64.address.minimum; - dev->policy_sz = res->data.address64.address.address_length; - break; - case ACPI_RESOURCE_TYPE_FIXED_MEMORY32: - dev->policy_addr = res->data.fixed_memory32.address; - dev->policy_sz = res->data.fixed_memory32.address_length; - break; - } - - if (!dev->policy_addr || dev->policy_sz > POLICY_BUF_MAX_SZ || dev->policy_sz == 0) { - pr_err("Incorrect Policy params, possibly a SBIOS bug\n"); - return AE_ERROR; + pmf_dev->res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!pmf_dev->res) { + dev_dbg(pmf_dev->dev, "Failed to get I/O memory resource\n"); + return -EINVAL; } - return AE_OK; -} - -int apmf_check_smart_pc(struct amd_pmf_dev *pmf_dev) -{ - acpi_handle ahandle = ACPI_HANDLE(pmf_dev->dev); - acpi_status status; - - status = acpi_walk_resources(ahandle, METHOD_NAME__CRS, apmf_walk_resources, pmf_dev); - if (ACPI_FAILURE(status)) { - dev_dbg(pmf_dev->dev, "acpi_walk_resources failed :%d\n", status); + pmf_dev->policy_addr = pmf_dev->res->start; + /* + * We cannot use resource_size() here because it adds an extra byte to round off the size. + * In the case of PMF ResourceTemplate(), this rounding is already handled within the _CRS. + * Using resource_size() would increase the resource size by 1, causing a mismatch with the + * length field and leading to issues. Therefore, simply use end-start of the ACPI resource + * to obtain the actual length. + */ + pmf_dev->policy_sz = pmf_dev->res->end - pmf_dev->res->start; + + if (!pmf_dev->policy_addr || pmf_dev->policy_sz > POLICY_BUF_MAX_SZ || + pmf_dev->policy_sz == 0) { + dev_err(pmf_dev->dev, "Incorrect policy params, possibly a SBIOS bug\n"); return -EINVAL; } diff --git a/drivers/platform/x86/amd/pmf/core.c b/drivers/platform/x86/amd/pmf/core.c index 347bb43a5f2b..06a97c533cb8 100644 --- a/drivers/platform/x86/amd/pmf/core.c +++ b/drivers/platform/x86/amd/pmf/core.c @@ -430,18 +430,18 @@ static int amd_pmf_probe(struct platform_device *pdev) err = amd_smn_read(0, AMD_PMF_BASE_ADDR_LO, &val); if (err) { - dev_err(dev->dev, "error in reading from 0x%x\n", AMD_PMF_BASE_ADDR_LO); pci_dev_put(rdev); - return pcibios_err_to_errno(err); + return dev_err_probe(dev->dev, pcibios_err_to_errno(err), + "error in reading from 0x%x\n", AMD_PMF_BASE_ADDR_LO); } base_addr_lo = val & AMD_PMF_BASE_ADDR_HI_MASK; err = amd_smn_read(0, AMD_PMF_BASE_ADDR_HI, &val); if (err) { - dev_err(dev->dev, "error in reading from 0x%x\n", AMD_PMF_BASE_ADDR_HI); pci_dev_put(rdev); - return pcibios_err_to_errno(err); + return dev_err_probe(dev->dev, pcibios_err_to_errno(err), + "error in reading from 0x%x\n", AMD_PMF_BASE_ADDR_HI); } base_addr_hi = val & AMD_PMF_BASE_ADDR_LO_MASK; @@ -497,7 +497,7 @@ static struct platform_driver amd_pmf_driver = { .pm = pm_sleep_ptr(&amd_pmf_pm), }, .probe = amd_pmf_probe, - .remove_new = amd_pmf_remove, + .remove = amd_pmf_remove, }; module_platform_driver(amd_pmf_driver); diff --git a/drivers/platform/x86/amd/pmf/pmf.h b/drivers/platform/x86/amd/pmf/pmf.h index 8ce8816da9c1..a79808fda1d8 100644 --- a/drivers/platform/x86/amd/pmf/pmf.h +++ b/drivers/platform/x86/amd/pmf/pmf.h @@ -13,6 +13,7 @@ #include <linux/acpi.h> #include <linux/input.h> +#include <linux/platform_device.h> #include <linux/platform_profile.h> #define POLICY_BUF_MAX_SZ 0x4b000 @@ -355,19 +356,20 @@ struct amd_pmf_dev { /* Smart PC solution builder */ struct dentry *esbin; unsigned char *policy_buf; - u32 policy_sz; + resource_size_t policy_sz; struct tee_context *tee_ctx; struct tee_shm *fw_shm_pool; u32 session_id; void *shbuf; struct delayed_work pb_work; struct pmf_action_table *prev_data; - u64 policy_addr; + resource_size_t policy_addr; void __iomem *policy_base; bool smart_pc_enabled; u16 pmf_if_version; struct input_dev *pmf_idev; size_t mtable_size; + struct resource *res; }; 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 19c27b6e4666..8c88769ea1d8 100644 --- a/drivers/platform/x86/amd/pmf/tee-if.c +++ b/drivers/platform/x86/amd/pmf/tee-if.c @@ -257,7 +257,7 @@ static int amd_pmf_invoke_cmd_init(struct amd_pmf_dev *dev) return -ENODEV; } - dev_dbg(dev->dev, "Policy Binary size: %u bytes\n", dev->policy_sz); + dev_dbg(dev->dev, "Policy Binary size: %llu bytes\n", (unsigned long long)dev->policy_sz); memset(dev->shbuf, 0, dev->policy_sz); ta_sm = dev->shbuf; in = &ta_sm->pmf_input.init_table; @@ -512,9 +512,9 @@ int amd_pmf_init_smart_pc(struct amd_pmf_dev *dev) if (ret) goto error; - dev->policy_base = devm_ioremap(dev->dev, dev->policy_addr, dev->policy_sz); - if (!dev->policy_base) { - ret = -ENOMEM; + dev->policy_base = devm_ioremap_resource(dev->dev, dev->res); + if (IS_ERR(dev->policy_base)) { + ret = PTR_ERR(dev->policy_base); goto error; } diff --git a/drivers/platform/x86/amd/x3d_vcache.c b/drivers/platform/x86/amd/x3d_vcache.c new file mode 100644 index 000000000000..0f6d3c54d879 --- /dev/null +++ b/drivers/platform/x86/amd/x3d_vcache.c @@ -0,0 +1,176 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * AMD 3D V-Cache Performance Optimizer Driver + * + * Copyright (c) 2024, Advanced Micro Devices, Inc. + * All Rights Reserved. + * + * Authors: Basavaraj Natikar <Basavaraj.Natikar@amd.com> + * Perry Yuan <perry.yuan@amd.com> + * Mario Limonciello <mario.limonciello@amd.com> + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include <linux/acpi.h> +#include <linux/array_size.h> +#include <linux/device.h> +#include <linux/errno.h> +#include <linux/module.h> +#include <linux/mutex.h> +#include <linux/platform_device.h> +#include <linux/pm.h> +#include <linux/sysfs.h> +#include <linux/uuid.h> + +static char *x3d_mode = "frequency"; +module_param(x3d_mode, charp, 0); +MODULE_PARM_DESC(x3d_mode, "Initial 3D-VCache mode; 'frequency' (default) or 'cache'"); + +#define DSM_REVISION_ID 0 +#define DSM_SET_X3D_MODE 1 + +static guid_t x3d_guid = GUID_INIT(0xdff8e55f, 0xbcfd, 0x46fb, 0xba, 0x0a, + 0xef, 0xd0, 0x45, 0x0f, 0x34, 0xee); + +enum amd_x3d_mode_type { + MODE_INDEX_FREQ, + MODE_INDEX_CACHE, +}; + +static const char * const amd_x3d_mode_strings[] = { + [MODE_INDEX_FREQ] = "frequency", + [MODE_INDEX_CACHE] = "cache", +}; + +struct amd_x3d_dev { + struct device *dev; + acpi_handle ahandle; + /* To protect x3d mode setting */ + struct mutex lock; + enum amd_x3d_mode_type curr_mode; +}; + +static int amd_x3d_get_mode(struct amd_x3d_dev *data) +{ + guard(mutex)(&data->lock); + + return data->curr_mode; +} + +static int amd_x3d_mode_switch(struct amd_x3d_dev *data, int new_state) +{ + union acpi_object *out, argv; + + guard(mutex)(&data->lock); + argv.type = ACPI_TYPE_INTEGER; + argv.integer.value = new_state; + + out = acpi_evaluate_dsm(data->ahandle, &x3d_guid, DSM_REVISION_ID, + DSM_SET_X3D_MODE, &argv); + if (!out) { + dev_err(data->dev, "failed to evaluate _DSM\n"); + return -EINVAL; + } + + data->curr_mode = new_state; + + kfree(out); + + return 0; +} + +static ssize_t amd_x3d_mode_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct amd_x3d_dev *data = dev_get_drvdata(dev); + int ret; + + ret = sysfs_match_string(amd_x3d_mode_strings, buf); + if (ret < 0) + return ret; + + ret = amd_x3d_mode_switch(data, ret); + if (ret < 0) + return ret; + + return count; +} + +static ssize_t amd_x3d_mode_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct amd_x3d_dev *data = dev_get_drvdata(dev); + int mode = amd_x3d_get_mode(data); + + return sysfs_emit(buf, "%s\n", amd_x3d_mode_strings[mode]); +} +static DEVICE_ATTR_RW(amd_x3d_mode); + +static struct attribute *amd_x3d_attrs[] = { + &dev_attr_amd_x3d_mode.attr, + NULL +}; +ATTRIBUTE_GROUPS(amd_x3d); + +static int amd_x3d_resume_handler(struct device *dev) +{ + struct amd_x3d_dev *data = dev_get_drvdata(dev); + int ret = amd_x3d_get_mode(data); + + return amd_x3d_mode_switch(data, ret); +} + +static DEFINE_SIMPLE_DEV_PM_OPS(amd_x3d_pm, NULL, amd_x3d_resume_handler); + +static const struct acpi_device_id amd_x3d_acpi_ids[] = { + {"AMDI0101"}, + { }, +}; +MODULE_DEVICE_TABLE(acpi, amd_x3d_acpi_ids); + +static int amd_x3d_probe(struct platform_device *pdev) +{ + struct amd_x3d_dev *data; + acpi_handle handle; + int ret; + + handle = ACPI_HANDLE(&pdev->dev); + if (!handle) + return -ENODEV; + + if (!acpi_check_dsm(handle, &x3d_guid, DSM_REVISION_ID, BIT(DSM_SET_X3D_MODE))) + return -ENODEV; + + data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL); + if (!data) + return -ENOMEM; + + data->dev = &pdev->dev; + + ret = devm_mutex_init(data->dev, &data->lock); + if (ret) + return ret; + + data->ahandle = handle; + platform_set_drvdata(pdev, data); + + ret = match_string(amd_x3d_mode_strings, ARRAY_SIZE(amd_x3d_mode_strings), x3d_mode); + if (ret < 0) + return dev_err_probe(&pdev->dev, -EINVAL, "invalid mode %s\n", x3d_mode); + + return amd_x3d_mode_switch(data, ret); +} + +static struct platform_driver amd_3d_vcache_driver = { + .driver = { + .name = "amd_x3d_vcache", + .dev_groups = amd_x3d_groups, + .acpi_match_table = amd_x3d_acpi_ids, + .pm = pm_sleep_ptr(&amd_x3d_pm), + }, + .probe = amd_x3d_probe, +}; +module_platform_driver(amd_3d_vcache_driver); + +MODULE_DESCRIPTION("AMD 3D V-Cache Performance Optimizer Driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/platform/x86/amilo-rfkill.c b/drivers/platform/x86/amilo-rfkill.c index 2423dc91debb..18397c527eab 100644 --- a/drivers/platform/x86/amilo-rfkill.c +++ b/drivers/platform/x86/amilo-rfkill.c @@ -132,10 +132,10 @@ static void amilo_rfkill_remove(struct platform_device *device) static struct platform_driver amilo_rfkill_driver = { .driver = { - .name = KBUILD_MODNAME, + .name = KBUILD_MODNAME, }, - .probe = amilo_rfkill_probe, - .remove_new = amilo_rfkill_remove, + .probe = amilo_rfkill_probe, + .remove = amilo_rfkill_remove, }; static int __init amilo_rfkill_init(void) diff --git a/drivers/platform/x86/asus-laptop.c b/drivers/platform/x86/asus-laptop.c index 9d7e6b712abf..d460dd194f19 100644 --- a/drivers/platform/x86/asus-laptop.c +++ b/drivers/platform/x86/asus-laptop.c @@ -1832,8 +1832,8 @@ static int asus_acpi_add(struct acpi_device *device) if (!asus) return -ENOMEM; asus->handle = device->handle; - strcpy(acpi_device_name(device), ASUS_LAPTOP_DEVICE_NAME); - strcpy(acpi_device_class(device), ASUS_LAPTOP_CLASS); + strscpy(acpi_device_name(device), ASUS_LAPTOP_DEVICE_NAME); + strscpy(acpi_device_class(device), ASUS_LAPTOP_CLASS); device->driver_data = asus; asus->device = device; diff --git a/drivers/platform/x86/asus-wmi.c b/drivers/platform/x86/asus-wmi.c index abdca3f05c5c..ba8b6d028f9f 100644 --- a/drivers/platform/x86/asus-wmi.c +++ b/drivers/platform/x86/asus-wmi.c @@ -3696,10 +3696,28 @@ static int asus_wmi_custom_fan_curve_init(struct asus_wmi *asus) /* Throttle thermal policy ****************************************************/ static int throttle_thermal_policy_write(struct asus_wmi *asus) { - u8 value = asus->throttle_thermal_policy_mode; u32 retval; + u8 value; int err; + if (asus->throttle_thermal_policy_dev == ASUS_WMI_DEVID_THROTTLE_THERMAL_POLICY_VIVO) { + switch (asus->throttle_thermal_policy_mode) { + case ASUS_THROTTLE_THERMAL_POLICY_DEFAULT: + value = ASUS_THROTTLE_THERMAL_POLICY_DEFAULT_VIVO; + break; + case ASUS_THROTTLE_THERMAL_POLICY_OVERBOOST: + value = ASUS_THROTTLE_THERMAL_POLICY_OVERBOOST_VIVO; + break; + case ASUS_THROTTLE_THERMAL_POLICY_SILENT: + value = ASUS_THROTTLE_THERMAL_POLICY_SILENT_VIVO; + break; + default: + return -EINVAL; + } + } else { + value = asus->throttle_thermal_policy_mode; + } + err = asus_wmi_set_devstate(asus->throttle_thermal_policy_dev, value, &retval); @@ -3737,28 +3755,6 @@ static int throttle_thermal_policy_set_default(struct asus_wmi *asus) return throttle_thermal_policy_write(asus); } -static int throttle_thermal_policy_switch_next(struct asus_wmi *asus) -{ - u8 new_mode = asus->throttle_thermal_policy_mode + 1; - int err; - - if (new_mode > PLATFORM_PROFILE_MAX) - new_mode = ASUS_THROTTLE_THERMAL_POLICY_DEFAULT; - - asus->throttle_thermal_policy_mode = new_mode; - err = throttle_thermal_policy_write(asus); - if (err) - return err; - - /* - * Ensure that platform_profile updates userspace with the change to ensure - * that platform_profile and throttle_thermal_policy_mode are in sync. - */ - platform_profile_notify(); - - return 0; -} - static ssize_t throttle_thermal_policy_show(struct device *dev, struct device_attribute *attr, char *buf) { @@ -3804,46 +3800,6 @@ static ssize_t throttle_thermal_policy_store(struct device *dev, static DEVICE_ATTR_RW(throttle_thermal_policy); /* Platform profile ***********************************************************/ -static int asus_wmi_platform_profile_to_vivo(struct asus_wmi *asus, int mode) -{ - bool vivo; - - vivo = asus->throttle_thermal_policy_dev == ASUS_WMI_DEVID_THROTTLE_THERMAL_POLICY_VIVO; - - if (vivo) { - switch (mode) { - case ASUS_THROTTLE_THERMAL_POLICY_DEFAULT: - return ASUS_THROTTLE_THERMAL_POLICY_DEFAULT_VIVO; - case ASUS_THROTTLE_THERMAL_POLICY_OVERBOOST: - return ASUS_THROTTLE_THERMAL_POLICY_OVERBOOST_VIVO; - case ASUS_THROTTLE_THERMAL_POLICY_SILENT: - return ASUS_THROTTLE_THERMAL_POLICY_SILENT_VIVO; - } - } - - return mode; -} - -static int asus_wmi_platform_profile_mode_from_vivo(struct asus_wmi *asus, int mode) -{ - bool vivo; - - vivo = asus->throttle_thermal_policy_dev == ASUS_WMI_DEVID_THROTTLE_THERMAL_POLICY_VIVO; - - if (vivo) { - switch (mode) { - case ASUS_THROTTLE_THERMAL_POLICY_DEFAULT_VIVO: - return ASUS_THROTTLE_THERMAL_POLICY_DEFAULT; - case ASUS_THROTTLE_THERMAL_POLICY_OVERBOOST_VIVO: - return ASUS_THROTTLE_THERMAL_POLICY_OVERBOOST; - case ASUS_THROTTLE_THERMAL_POLICY_SILENT_VIVO: - return ASUS_THROTTLE_THERMAL_POLICY_SILENT; - } - } - - return mode; -} - static int asus_wmi_platform_profile_get(struct platform_profile_handler *pprof, enum platform_profile_option *profile) { @@ -3853,7 +3809,7 @@ static int asus_wmi_platform_profile_get(struct platform_profile_handler *pprof, asus = container_of(pprof, struct asus_wmi, platform_profile_handler); tp = asus->throttle_thermal_policy_mode; - switch (asus_wmi_platform_profile_mode_from_vivo(asus, tp)) { + switch (tp) { case ASUS_THROTTLE_THERMAL_POLICY_DEFAULT: *profile = PLATFORM_PROFILE_BALANCED; break; @@ -3892,7 +3848,7 @@ static int asus_wmi_platform_profile_set(struct platform_profile_handler *pprof, return -EOPNOTSUPP; } - asus->throttle_thermal_policy_mode = asus_wmi_platform_profile_to_vivo(asus, tp); + asus->throttle_thermal_policy_mode = tp; return throttle_thermal_policy_write(asus); } @@ -4323,7 +4279,7 @@ static void asus_wmi_handle_event_code(int code, struct asus_wmi *asus) if (asus->fan_boost_mode_available) fan_boost_mode_switch_next(asus); if (asus->throttle_thermal_policy_dev) - throttle_thermal_policy_switch_next(asus); + platform_profile_cycle(); return; } @@ -5076,7 +5032,7 @@ int __init_or_module asus_wmi_register_driver(struct asus_wmi_driver *driver) return -EBUSY; platform_driver = &driver->platform_driver; - platform_driver->remove_new = asus_wmi_remove; + platform_driver->remove = asus_wmi_remove; platform_driver->driver.owner = driver->owner; platform_driver->driver.name = driver->name; platform_driver->driver.pm = &asus_pm_ops; diff --git a/drivers/platform/x86/barco-p50-gpio.c b/drivers/platform/x86/barco-p50-gpio.c index af566f712057..143d14548565 100644 --- a/drivers/platform/x86/barco-p50-gpio.c +++ b/drivers/platform/x86/barco-p50-gpio.c @@ -385,7 +385,7 @@ static struct platform_driver p50_gpio_driver = { .name = DRIVER_NAME, }, .probe = p50_gpio_probe, - .remove_new = p50_gpio_remove, + .remove = p50_gpio_remove, }; /* Board setup */ diff --git a/drivers/platform/x86/classmate-laptop.c b/drivers/platform/x86/classmate-laptop.c index cb6fce655e35..6b1b8e444e24 100644 --- a/drivers/platform/x86/classmate-laptop.c +++ b/drivers/platform/x86/classmate-laptop.c @@ -12,6 +12,7 @@ #include <linux/backlight.h> #include <linux/input.h> #include <linux/rfkill.h> +#include <linux/sysfs.h> struct cmpc_accel { int sensitivity; @@ -208,7 +209,7 @@ static ssize_t cmpc_accel_sensitivity_show_v4(struct device *dev, inputdev = dev_get_drvdata(&acpi->dev); accel = dev_get_drvdata(&inputdev->dev); - return sprintf(buf, "%d\n", accel->sensitivity); + return sysfs_emit(buf, "%d\n", accel->sensitivity); } static ssize_t cmpc_accel_sensitivity_store_v4(struct device *dev, @@ -257,7 +258,7 @@ static ssize_t cmpc_accel_g_select_show_v4(struct device *dev, inputdev = dev_get_drvdata(&acpi->dev); accel = dev_get_drvdata(&inputdev->dev); - return sprintf(buf, "%d\n", accel->g_select); + return sysfs_emit(buf, "%d\n", accel->g_select); } static ssize_t cmpc_accel_g_select_store_v4(struct device *dev, @@ -550,7 +551,7 @@ static ssize_t cmpc_accel_sensitivity_show(struct device *dev, inputdev = dev_get_drvdata(&acpi->dev); accel = dev_get_drvdata(&inputdev->dev); - return sprintf(buf, "%d\n", accel->sensitivity); + return sysfs_emit(buf, "%d\n", accel->sensitivity); } static ssize_t cmpc_accel_sensitivity_store(struct device *dev, diff --git a/drivers/platform/x86/compal-laptop.c b/drivers/platform/x86/compal-laptop.c index 5546fb189491..58754bc5b5b1 100644 --- a/drivers/platform/x86/compal-laptop.c +++ b/drivers/platform/x86/compal-laptop.c @@ -68,6 +68,7 @@ #include <linux/hwmon.h> #include <linux/hwmon-sysfs.h> #include <linux/power_supply.h> +#include <linux/sysfs.h> #include <linux/fb.h> #include <acpi/video.h> @@ -364,21 +365,21 @@ static const struct rfkill_ops compal_rfkill_ops = { /* Wake_up interface */ -#define SIMPLE_MASKED_STORE_SHOW(NAME, ADDR, MASK) \ -static ssize_t NAME##_show(struct device *dev, \ - struct device_attribute *attr, char *buf) \ -{ \ - return sprintf(buf, "%d\n", ((ec_read_u8(ADDR) & MASK) != 0)); \ -} \ -static ssize_t NAME##_store(struct device *dev, \ - struct device_attribute *attr, const char *buf, size_t count) \ -{ \ - int state; \ - u8 old_val = ec_read_u8(ADDR); \ - if (sscanf(buf, "%d", &state) != 1 || (state < 0 || state > 1)) \ - return -EINVAL; \ - ec_write(ADDR, state ? (old_val | MASK) : (old_val & ~MASK)); \ - return count; \ +#define SIMPLE_MASKED_STORE_SHOW(NAME, ADDR, MASK) \ +static ssize_t NAME##_show(struct device *dev, \ + struct device_attribute *attr, char *buf) \ +{ \ + return sysfs_emit(buf, "%d\n", ((ec_read_u8(ADDR) & MASK) != 0)); \ +} \ +static ssize_t NAME##_store(struct device *dev, \ + struct device_attribute *attr, const char *buf, size_t count) \ +{ \ + int state; \ + u8 old_val = ec_read_u8(ADDR); \ + if (sscanf(buf, "%d", &state) != 1 || (state < 0 || state > 1)) \ + return -EINVAL; \ + ec_write(ADDR, state ? (old_val | MASK) : (old_val & ~MASK)); \ + return count; \ } SIMPLE_MASKED_STORE_SHOW(wake_up_pme, WAKE_UP_ADDR, WAKE_UP_PME) @@ -393,7 +394,7 @@ static ssize_t pwm_enable_show(struct device *dev, struct device_attribute *attr, char *buf) { struct compal_data *data = dev_get_drvdata(dev); - return sprintf(buf, "%d\n", data->pwm_enable); + return sysfs_emit(buf, "%d\n", data->pwm_enable); } static ssize_t pwm_enable_store(struct device *dev, @@ -432,7 +433,7 @@ static ssize_t pwm_show(struct device *dev, struct device_attribute *attr, char *buf) { struct compal_data *data = dev_get_drvdata(dev); - return sprintf(buf, "%hhu\n", data->curr_pwm); + return sysfs_emit(buf, "%hhu\n", data->curr_pwm); } static ssize_t pwm_store(struct device *dev, struct device_attribute *attr, @@ -460,21 +461,21 @@ static ssize_t pwm_store(struct device *dev, struct device_attribute *attr, static ssize_t fan_show(struct device *dev, struct device_attribute *attr, char *buf) { - return sprintf(buf, "%d\n", get_fan_rpm()); + return sysfs_emit(buf, "%d\n", get_fan_rpm()); } /* Temperature interface */ -#define TEMPERATURE_SHOW_TEMP_AND_LABEL(POSTFIX, ADDRESS, LABEL) \ -static ssize_t temp_##POSTFIX(struct device *dev, \ - struct device_attribute *attr, char *buf) \ -{ \ - return sprintf(buf, "%d\n", 1000 * (int)ec_read_s8(ADDRESS)); \ -} \ -static ssize_t label_##POSTFIX(struct device *dev, \ - struct device_attribute *attr, char *buf) \ -{ \ - return sprintf(buf, "%s\n", LABEL); \ +#define TEMPERATURE_SHOW_TEMP_AND_LABEL(POSTFIX, ADDRESS, LABEL) \ +static ssize_t temp_##POSTFIX(struct device *dev, \ + struct device_attribute *attr, char *buf) \ +{ \ + return sysfs_emit(buf, "%d\n", 1000 * (int)ec_read_s8(ADDRESS)); \ +} \ +static ssize_t label_##POSTFIX(struct device *dev, \ + struct device_attribute *attr, char *buf) \ +{ \ + return sysfs_emit(buf, "%s\n", LABEL); \ } /* Labels as in service guide */ @@ -1023,8 +1024,8 @@ static struct platform_driver compal_driver = { .driver = { .name = DRIVER_NAME, }, - .probe = compal_probe, - .remove_new = compal_remove, + .probe = compal_probe, + .remove = compal_remove, }; static int __init compal_init(void) diff --git a/drivers/platform/x86/dell/Kconfig b/drivers/platform/x86/dell/Kconfig index dc21227dd66e..2dddafb3f7fa 100644 --- a/drivers/platform/x86/dell/Kconfig +++ b/drivers/platform/x86/dell/Kconfig @@ -21,6 +21,7 @@ config ALIENWARE_WMI depends on LEDS_CLASS depends on NEW_LEDS depends on ACPI_WMI + select ACPI_PLATFORM_PROFILE help This is a driver for controlling Alienware BIOS driven features. It exposes an interface for controlling the AlienFX diff --git a/drivers/platform/x86/dell/alienware-wmi.c b/drivers/platform/x86/dell/alienware-wmi.c index f5ee62ce1753..77465ed9b449 100644 --- a/drivers/platform/x86/dell/alienware-wmi.c +++ b/drivers/platform/x86/dell/alienware-wmi.c @@ -8,8 +8,11 @@ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt #include <linux/acpi.h> +#include <linux/bitfield.h> +#include <linux/bits.h> #include <linux/module.h> #include <linux/platform_device.h> +#include <linux/platform_profile.h> #include <linux/dmi.h> #include <linux/leds.h> @@ -25,6 +28,13 @@ #define WMAX_METHOD_AMPLIFIER_CABLE 0x6 #define WMAX_METHOD_DEEP_SLEEP_CONTROL 0x0B #define WMAX_METHOD_DEEP_SLEEP_STATUS 0x0C +#define WMAX_METHOD_THERMAL_INFORMATION 0x14 +#define WMAX_METHOD_THERMAL_CONTROL 0x15 +#define WMAX_METHOD_GAME_SHIFT_STATUS 0x25 + +#define WMAX_THERMAL_MODE_GMODE 0xAB + +#define WMAX_FAILURE_CODE 0xFFFFFFFF MODULE_AUTHOR("Mario Limonciello <mario.limonciello@outlook.com>"); MODULE_DESCRIPTION("Alienware special feature control"); @@ -32,6 +42,14 @@ MODULE_LICENSE("GPL"); MODULE_ALIAS("wmi:" LEGACY_CONTROL_GUID); MODULE_ALIAS("wmi:" WMAX_CONTROL_GUID); +static bool force_platform_profile; +module_param_unsafe(force_platform_profile, bool, 0); +MODULE_PARM_DESC(force_platform_profile, "Forces auto-detecting thermal profiles without checking if WMI thermal backend is available"); + +static bool force_gmode; +module_param_unsafe(force_gmode, bool, 0); +MODULE_PARM_DESC(force_gmode, "Forces G-Mode when performance profile is selected"); + enum INTERFACE_FLAGS { LEGACY, WMAX, @@ -49,11 +67,60 @@ enum WMAX_CONTROL_STATES { WMAX_SUSPEND = 3, }; +enum WMAX_THERMAL_INFORMATION_OPERATIONS { + WMAX_OPERATION_SYS_DESCRIPTION = 0x02, + WMAX_OPERATION_LIST_IDS = 0x03, + WMAX_OPERATION_CURRENT_PROFILE = 0x0B, +}; + +enum WMAX_THERMAL_CONTROL_OPERATIONS { + WMAX_OPERATION_ACTIVATE_PROFILE = 0x01, +}; + +enum WMAX_GAME_SHIFT_STATUS_OPERATIONS { + WMAX_OPERATION_TOGGLE_GAME_SHIFT = 0x01, + WMAX_OPERATION_GET_GAME_SHIFT_STATUS = 0x02, +}; + +enum WMAX_THERMAL_TABLES { + WMAX_THERMAL_TABLE_BASIC = 0x90, + WMAX_THERMAL_TABLE_USTT = 0xA0, +}; + +enum wmax_thermal_mode { + THERMAL_MODE_USTT_BALANCED, + THERMAL_MODE_USTT_BALANCED_PERFORMANCE, + THERMAL_MODE_USTT_COOL, + THERMAL_MODE_USTT_QUIET, + THERMAL_MODE_USTT_PERFORMANCE, + THERMAL_MODE_USTT_LOW_POWER, + THERMAL_MODE_BASIC_QUIET, + THERMAL_MODE_BASIC_BALANCED, + THERMAL_MODE_BASIC_BALANCED_PERFORMANCE, + THERMAL_MODE_BASIC_PERFORMANCE, + THERMAL_MODE_LAST, +}; + +static const enum platform_profile_option wmax_mode_to_platform_profile[THERMAL_MODE_LAST] = { + [THERMAL_MODE_USTT_BALANCED] = PLATFORM_PROFILE_BALANCED, + [THERMAL_MODE_USTT_BALANCED_PERFORMANCE] = PLATFORM_PROFILE_BALANCED_PERFORMANCE, + [THERMAL_MODE_USTT_COOL] = PLATFORM_PROFILE_COOL, + [THERMAL_MODE_USTT_QUIET] = PLATFORM_PROFILE_QUIET, + [THERMAL_MODE_USTT_PERFORMANCE] = PLATFORM_PROFILE_PERFORMANCE, + [THERMAL_MODE_USTT_LOW_POWER] = PLATFORM_PROFILE_LOW_POWER, + [THERMAL_MODE_BASIC_QUIET] = PLATFORM_PROFILE_QUIET, + [THERMAL_MODE_BASIC_BALANCED] = PLATFORM_PROFILE_BALANCED, + [THERMAL_MODE_BASIC_BALANCED_PERFORMANCE] = PLATFORM_PROFILE_BALANCED_PERFORMANCE, + [THERMAL_MODE_BASIC_PERFORMANCE] = PLATFORM_PROFILE_PERFORMANCE, +}; + struct quirk_entry { u8 num_zones; u8 hdmi_mux; u8 amplifier; u8 deepslp; + bool thermal; + bool gmode; }; static struct quirk_entry *quirks; @@ -64,6 +131,8 @@ static struct quirk_entry quirk_inspiron5675 = { .hdmi_mux = 0, .amplifier = 0, .deepslp = 0, + .thermal = false, + .gmode = false, }; static struct quirk_entry quirk_unknown = { @@ -71,6 +140,8 @@ static struct quirk_entry quirk_unknown = { .hdmi_mux = 0, .amplifier = 0, .deepslp = 0, + .thermal = false, + .gmode = false, }; static struct quirk_entry quirk_x51_r1_r2 = { @@ -78,6 +149,8 @@ static struct quirk_entry quirk_x51_r1_r2 = { .hdmi_mux = 0, .amplifier = 0, .deepslp = 0, + .thermal = false, + .gmode = false, }; static struct quirk_entry quirk_x51_r3 = { @@ -85,6 +158,8 @@ static struct quirk_entry quirk_x51_r3 = { .hdmi_mux = 0, .amplifier = 1, .deepslp = 0, + .thermal = false, + .gmode = false, }; static struct quirk_entry quirk_asm100 = { @@ -92,6 +167,8 @@ static struct quirk_entry quirk_asm100 = { .hdmi_mux = 1, .amplifier = 0, .deepslp = 0, + .thermal = false, + .gmode = false, }; static struct quirk_entry quirk_asm200 = { @@ -99,6 +176,8 @@ static struct quirk_entry quirk_asm200 = { .hdmi_mux = 1, .amplifier = 0, .deepslp = 1, + .thermal = false, + .gmode = false, }; static struct quirk_entry quirk_asm201 = { @@ -106,6 +185,26 @@ static struct quirk_entry quirk_asm201 = { .hdmi_mux = 1, .amplifier = 1, .deepslp = 1, + .thermal = false, + .gmode = false, +}; + +static struct quirk_entry quirk_g_series = { + .num_zones = 2, + .hdmi_mux = 0, + .amplifier = 0, + .deepslp = 0, + .thermal = true, + .gmode = true, +}; + +static struct quirk_entry quirk_x_series = { + .num_zones = 2, + .hdmi_mux = 0, + .amplifier = 0, + .deepslp = 0, + .thermal = true, + .gmode = false, }; static int __init dmi_matched(const struct dmi_system_id *dmi) @@ -116,68 +215,158 @@ static int __init dmi_matched(const struct dmi_system_id *dmi) static const struct dmi_system_id alienware_quirks[] __initconst = { { - .callback = dmi_matched, - .ident = "Alienware X51 R3", - .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "Alienware"), - DMI_MATCH(DMI_PRODUCT_NAME, "Alienware X51 R3"), - }, - .driver_data = &quirk_x51_r3, - }, + .callback = dmi_matched, + .ident = "Alienware ASM100", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Alienware"), + DMI_MATCH(DMI_PRODUCT_NAME, "ASM100"), + }, + .driver_data = &quirk_asm100, + }, + { + .callback = dmi_matched, + .ident = "Alienware ASM200", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Alienware"), + DMI_MATCH(DMI_PRODUCT_NAME, "ASM200"), + }, + .driver_data = &quirk_asm200, + }, + { + .callback = dmi_matched, + .ident = "Alienware ASM201", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Alienware"), + DMI_MATCH(DMI_PRODUCT_NAME, "ASM201"), + }, + .driver_data = &quirk_asm201, + }, + { + .callback = dmi_matched, + .ident = "Alienware m17 R5", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Alienware"), + DMI_MATCH(DMI_PRODUCT_NAME, "Alienware m17 R5 AMD"), + }, + .driver_data = &quirk_x_series, + }, + { + .callback = dmi_matched, + .ident = "Alienware m18 R2", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Alienware"), + DMI_MATCH(DMI_PRODUCT_NAME, "Alienware m18 R2"), + }, + .driver_data = &quirk_x_series, + }, + { + .callback = dmi_matched, + .ident = "Alienware x15 R1", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Alienware"), + DMI_MATCH(DMI_PRODUCT_NAME, "Alienware x15 R1"), + }, + .driver_data = &quirk_x_series, + }, + { + .callback = dmi_matched, + .ident = "Alienware x17 R2", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Alienware"), + DMI_MATCH(DMI_PRODUCT_NAME, "Alienware x17 R2"), + }, + .driver_data = &quirk_x_series, + }, + { + .callback = dmi_matched, + .ident = "Alienware X51 R1", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Alienware"), + DMI_MATCH(DMI_PRODUCT_NAME, "Alienware X51"), + }, + .driver_data = &quirk_x51_r1_r2, + }, + { + .callback = dmi_matched, + .ident = "Alienware X51 R2", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Alienware"), + DMI_MATCH(DMI_PRODUCT_NAME, "Alienware X51 R2"), + }, + .driver_data = &quirk_x51_r1_r2, + }, { - .callback = dmi_matched, - .ident = "Alienware X51 R2", - .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "Alienware"), - DMI_MATCH(DMI_PRODUCT_NAME, "Alienware X51 R2"), - }, - .driver_data = &quirk_x51_r1_r2, - }, + .callback = dmi_matched, + .ident = "Alienware X51 R3", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Alienware"), + DMI_MATCH(DMI_PRODUCT_NAME, "Alienware X51 R3"), + }, + .driver_data = &quirk_x51_r3, + }, { - .callback = dmi_matched, - .ident = "Alienware X51 R1", - .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "Alienware"), - DMI_MATCH(DMI_PRODUCT_NAME, "Alienware X51"), - }, - .driver_data = &quirk_x51_r1_r2, - }, + .callback = dmi_matched, + .ident = "Dell Inc. G15 5510", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), + DMI_MATCH(DMI_PRODUCT_NAME, "Dell G15 5510"), + }, + .driver_data = &quirk_g_series, + }, { - .callback = dmi_matched, - .ident = "Alienware ASM100", - .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "Alienware"), - DMI_MATCH(DMI_PRODUCT_NAME, "ASM100"), - }, - .driver_data = &quirk_asm100, - }, + .callback = dmi_matched, + .ident = "Dell Inc. G15 5511", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), + DMI_MATCH(DMI_PRODUCT_NAME, "Dell G15 5511"), + }, + .driver_data = &quirk_g_series, + }, { - .callback = dmi_matched, - .ident = "Alienware ASM200", - .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "Alienware"), - DMI_MATCH(DMI_PRODUCT_NAME, "ASM200"), - }, - .driver_data = &quirk_asm200, - }, + .callback = dmi_matched, + .ident = "Dell Inc. G15 5515", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), + DMI_MATCH(DMI_PRODUCT_NAME, "Dell G15 5515"), + }, + .driver_data = &quirk_g_series, + }, { - .callback = dmi_matched, - .ident = "Alienware ASM201", - .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "Alienware"), - DMI_MATCH(DMI_PRODUCT_NAME, "ASM201"), - }, - .driver_data = &quirk_asm201, - }, - { - .callback = dmi_matched, - .ident = "Dell Inc. Inspiron 5675", - .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), - DMI_MATCH(DMI_PRODUCT_NAME, "Inspiron 5675"), - }, - .driver_data = &quirk_inspiron5675, - }, + .callback = dmi_matched, + .ident = "Dell Inc. G3 3500", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), + DMI_MATCH(DMI_PRODUCT_NAME, "G3 3500"), + }, + .driver_data = &quirk_g_series, + }, + { + .callback = dmi_matched, + .ident = "Dell Inc. G3 3590", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), + DMI_MATCH(DMI_PRODUCT_NAME, "G3 3590"), + }, + .driver_data = &quirk_g_series, + }, + { + .callback = dmi_matched, + .ident = "Dell Inc. G5 5500", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), + DMI_MATCH(DMI_PRODUCT_NAME, "G5 5500"), + }, + .driver_data = &quirk_g_series, + }, + { + .callback = dmi_matched, + .ident = "Dell Inc. Inspiron 5675", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), + DMI_MATCH(DMI_PRODUCT_NAME, "Inspiron 5675"), + }, + .driver_data = &quirk_inspiron5675, + }, {} }; @@ -214,15 +403,24 @@ struct wmax_led_args { u8 state; } __packed; +struct wmax_u32_args { + u8 operation; + u8 arg1; + u8 arg2; + u8 arg3; +}; + static struct platform_device *platform_device; static struct device_attribute *zone_dev_attrs; static struct attribute **zone_attrs; static struct platform_zone *zone_data; +static struct platform_profile_handler pp_handler; +static enum wmax_thermal_mode supported_thermal_profiles[PLATFORM_PROFILE_LAST]; static struct platform_driver platform_driver = { .driver = { - .name = "alienware-wmi", - } + .name = "alienware-wmi", + } }; static struct attribute_group zone_attribute_group = { @@ -292,7 +490,7 @@ static int alienware_update_led(struct platform_zone *zone) guid = WMAX_CONTROL_GUID; method_id = WMAX_METHOD_ZONE_CONTROL; - input.length = (acpi_size) sizeof(wmax_basic_args); + input.length = sizeof(wmax_basic_args); input.pointer = &wmax_basic_args; } else { legacy_args.colors = zone->colors; @@ -306,7 +504,7 @@ static int alienware_update_led(struct platform_zone *zone) guid = LEGACY_CONTROL_GUID; method_id = zone->location + 1; - input.length = (acpi_size) sizeof(legacy_args); + input.length = sizeof(legacy_args); input.pointer = &legacy_args; } pr_debug("alienware-wmi: guid %s method %d\n", guid, method_id); @@ -358,7 +556,7 @@ static int wmax_brightness(int brightness) .led_mask = 0xFF, .percentage = brightness, }; - input.length = (acpi_size) sizeof(args); + input.length = sizeof(args); input.pointer = &args; status = wmi_evaluate_method(WMAX_CONTROL_GUID, 0, WMAX_METHOD_BRIGHTNESS, &input, NULL); @@ -500,15 +698,15 @@ static void alienware_zone_exit(struct platform_device *dev) kfree(zone_attrs); } -static acpi_status alienware_wmax_command(struct wmax_basic_args *in_args, - u32 command, int *out_data) +static acpi_status alienware_wmax_command(void *in_args, size_t in_size, + u32 command, u32 *out_data) { acpi_status status; union acpi_object *obj; struct acpi_buffer input; struct acpi_buffer output; - input.length = (acpi_size) sizeof(*in_args); + input.length = in_size; input.pointer = in_args; if (out_data) { output.length = ACPI_ALLOCATE_BUFFER; @@ -541,8 +739,8 @@ static ssize_t show_hdmi_cable(struct device *dev, .arg = 0, }; status = - alienware_wmax_command(&in_args, WMAX_METHOD_HDMI_CABLE, - (u32 *) &out_data); + alienware_wmax_command(&in_args, sizeof(in_args), + WMAX_METHOD_HDMI_CABLE, &out_data); if (ACPI_SUCCESS(status)) { if (out_data == 0) return sysfs_emit(buf, "[unconnected] connected unknown\n"); @@ -562,8 +760,8 @@ static ssize_t show_hdmi_source(struct device *dev, .arg = 0, }; status = - alienware_wmax_command(&in_args, WMAX_METHOD_HDMI_STATUS, - (u32 *) &out_data); + alienware_wmax_command(&in_args, sizeof(in_args), + WMAX_METHOD_HDMI_STATUS, &out_data); if (ACPI_SUCCESS(status)) { if (out_data == 1) @@ -589,7 +787,8 @@ static ssize_t toggle_hdmi_source(struct device *dev, args.arg = 3; pr_debug("alienware-wmi: setting hdmi to %d : %s", args.arg, buf); - status = alienware_wmax_command(&args, WMAX_METHOD_HDMI_SOURCE, NULL); + status = alienware_wmax_command(&args, sizeof(args), + WMAX_METHOD_HDMI_SOURCE, NULL); if (ACPI_FAILURE(status)) pr_err("alienware-wmi: HDMI toggle failed: results: %u\n", @@ -642,8 +841,8 @@ static ssize_t show_amplifier_status(struct device *dev, .arg = 0, }; status = - alienware_wmax_command(&in_args, WMAX_METHOD_AMPLIFIER_CABLE, - (u32 *) &out_data); + alienware_wmax_command(&in_args, sizeof(in_args), + WMAX_METHOD_AMPLIFIER_CABLE, &out_data); if (ACPI_SUCCESS(status)) { if (out_data == 0) return sysfs_emit(buf, "[unconnected] connected unknown\n"); @@ -694,8 +893,8 @@ static ssize_t show_deepsleep_status(struct device *dev, struct wmax_basic_args in_args = { .arg = 0, }; - status = alienware_wmax_command(&in_args, WMAX_METHOD_DEEP_SLEEP_STATUS, - (u32 *) &out_data); + status = alienware_wmax_command(&in_args, sizeof(in_args), + WMAX_METHOD_DEEP_SLEEP_STATUS, &out_data); if (ACPI_SUCCESS(status)) { if (out_data == 0) return sysfs_emit(buf, "[disabled] s5 s5_s4\n"); @@ -723,8 +922,8 @@ static ssize_t toggle_deepsleep(struct device *dev, args.arg = 2; pr_debug("alienware-wmi: setting deep sleep to %d : %s", args.arg, buf); - status = alienware_wmax_command(&args, WMAX_METHOD_DEEP_SLEEP_CONTROL, - NULL); + status = alienware_wmax_command(&args, sizeof(args), + WMAX_METHOD_DEEP_SLEEP_CONTROL, NULL); if (ACPI_FAILURE(status)) pr_err("alienware-wmi: deep sleep control failed: results: %u\n", @@ -760,6 +959,213 @@ static int create_deepsleep(struct platform_device *dev) return ret; } +/* + * Thermal Profile control + * - Provides thermal profile control through the Platform Profile API + */ +#define WMAX_THERMAL_TABLE_MASK GENMASK(7, 4) +#define WMAX_THERMAL_MODE_MASK GENMASK(3, 0) +#define WMAX_SENSOR_ID_MASK BIT(8) + +static bool is_wmax_thermal_code(u32 code) +{ + if (code & WMAX_SENSOR_ID_MASK) + return false; + + if ((code & WMAX_THERMAL_MODE_MASK) >= THERMAL_MODE_LAST) + return false; + + if ((code & WMAX_THERMAL_TABLE_MASK) == WMAX_THERMAL_TABLE_BASIC && + (code & WMAX_THERMAL_MODE_MASK) >= THERMAL_MODE_BASIC_QUIET) + return true; + + if ((code & WMAX_THERMAL_TABLE_MASK) == WMAX_THERMAL_TABLE_USTT && + (code & WMAX_THERMAL_MODE_MASK) <= THERMAL_MODE_USTT_LOW_POWER) + return true; + + return false; +} + +static int wmax_thermal_information(u8 operation, u8 arg, u32 *out_data) +{ + acpi_status status; + struct wmax_u32_args in_args = { + .operation = operation, + .arg1 = arg, + .arg2 = 0, + .arg3 = 0, + }; + + status = alienware_wmax_command(&in_args, sizeof(in_args), + WMAX_METHOD_THERMAL_INFORMATION, + out_data); + + if (ACPI_FAILURE(status)) + return -EIO; + + if (*out_data == WMAX_FAILURE_CODE) + return -EBADRQC; + + return 0; +} + +static int wmax_thermal_control(u8 profile) +{ + acpi_status status; + struct wmax_u32_args in_args = { + .operation = WMAX_OPERATION_ACTIVATE_PROFILE, + .arg1 = profile, + .arg2 = 0, + .arg3 = 0, + }; + u32 out_data; + + status = alienware_wmax_command(&in_args, sizeof(in_args), + WMAX_METHOD_THERMAL_CONTROL, + &out_data); + + if (ACPI_FAILURE(status)) + return -EIO; + + if (out_data == WMAX_FAILURE_CODE) + return -EBADRQC; + + return 0; +} + +static int wmax_game_shift_status(u8 operation, u32 *out_data) +{ + acpi_status status; + struct wmax_u32_args in_args = { + .operation = operation, + .arg1 = 0, + .arg2 = 0, + .arg3 = 0, + }; + + status = alienware_wmax_command(&in_args, sizeof(in_args), + WMAX_METHOD_GAME_SHIFT_STATUS, + out_data); + + if (ACPI_FAILURE(status)) + return -EIO; + + if (*out_data == WMAX_FAILURE_CODE) + return -EOPNOTSUPP; + + return 0; +} + +static int thermal_profile_get(struct platform_profile_handler *pprof, + enum platform_profile_option *profile) +{ + u32 out_data; + int ret; + + ret = wmax_thermal_information(WMAX_OPERATION_CURRENT_PROFILE, + 0, &out_data); + + if (ret < 0) + return ret; + + if (out_data == WMAX_THERMAL_MODE_GMODE) { + *profile = PLATFORM_PROFILE_PERFORMANCE; + return 0; + } + + if (!is_wmax_thermal_code(out_data)) + return -ENODATA; + + out_data &= WMAX_THERMAL_MODE_MASK; + *profile = wmax_mode_to_platform_profile[out_data]; + + return 0; +} + +static int thermal_profile_set(struct platform_profile_handler *pprof, + enum platform_profile_option profile) +{ + if (quirks->gmode) { + u32 gmode_status; + int ret; + + ret = wmax_game_shift_status(WMAX_OPERATION_GET_GAME_SHIFT_STATUS, + &gmode_status); + + if (ret < 0) + return ret; + + if ((profile == PLATFORM_PROFILE_PERFORMANCE && !gmode_status) || + (profile != PLATFORM_PROFILE_PERFORMANCE && gmode_status)) { + ret = wmax_game_shift_status(WMAX_OPERATION_TOGGLE_GAME_SHIFT, + &gmode_status); + + if (ret < 0) + return ret; + } + } + + return wmax_thermal_control(supported_thermal_profiles[profile]); +} + +static int create_thermal_profile(void) +{ + u32 out_data; + u8 sys_desc[4]; + u32 first_mode; + enum wmax_thermal_mode mode; + enum platform_profile_option profile; + int ret; + + ret = wmax_thermal_information(WMAX_OPERATION_SYS_DESCRIPTION, + 0, (u32 *) &sys_desc); + if (ret < 0) + return ret; + + first_mode = sys_desc[0] + sys_desc[1]; + + for (u32 i = 0; i < sys_desc[3]; i++) { + ret = wmax_thermal_information(WMAX_OPERATION_LIST_IDS, + i + first_mode, &out_data); + + if (ret == -EIO) + return ret; + + if (ret == -EBADRQC) + break; + + if (!is_wmax_thermal_code(out_data)) + continue; + + mode = out_data & WMAX_THERMAL_MODE_MASK; + profile = wmax_mode_to_platform_profile[mode]; + supported_thermal_profiles[profile] = out_data; + + set_bit(profile, pp_handler.choices); + } + + if (bitmap_empty(pp_handler.choices, PLATFORM_PROFILE_LAST)) + return -ENODEV; + + if (quirks->gmode) { + supported_thermal_profiles[PLATFORM_PROFILE_PERFORMANCE] = + WMAX_THERMAL_MODE_GMODE; + + set_bit(PLATFORM_PROFILE_PERFORMANCE, pp_handler.choices); + } + + pp_handler.profile_get = thermal_profile_get; + pp_handler.profile_set = thermal_profile_set; + + return platform_profile_register(&pp_handler); +} + +static void remove_thermal_profile(void) +{ + if (quirks->thermal) + platform_profile_remove(); +} + static int __init alienware_wmi_init(void) { int ret; @@ -777,6 +1183,16 @@ static int __init alienware_wmi_init(void) if (quirks == NULL) quirks = &quirk_unknown; + if (force_platform_profile) + quirks->thermal = true; + + if (force_gmode) { + if (quirks->thermal) + quirks->gmode = true; + else + pr_warn("force_gmode requires platform profile support\n"); + } + ret = platform_driver_register(&platform_driver); if (ret) goto fail_platform_driver; @@ -807,6 +1223,12 @@ static int __init alienware_wmi_init(void) goto fail_prep_deepsleep; } + if (quirks->thermal) { + ret = create_thermal_profile(); + if (ret) + goto fail_prep_thermal_profile; + } + ret = alienware_zone_init(platform_device); if (ret) goto fail_prep_zones; @@ -815,6 +1237,8 @@ static int __init alienware_wmi_init(void) fail_prep_zones: alienware_zone_exit(platform_device); + remove_thermal_profile(); +fail_prep_thermal_profile: fail_prep_deepsleep: fail_prep_amplifier: fail_prep_hdmi: @@ -834,6 +1258,7 @@ static void __exit alienware_wmi_exit(void) if (platform_device) { alienware_zone_exit(platform_device); remove_hdmi(platform_device); + remove_thermal_profile(); platform_device_unregister(platform_device); platform_driver_unregister(&platform_driver); } diff --git a/drivers/platform/x86/dell/dcdbas.c b/drivers/platform/x86/dell/dcdbas.c index a60e35056387..0aeb8149c16b 100644 --- a/drivers/platform/x86/dell/dcdbas.c +++ b/drivers/platform/x86/dell/dcdbas.c @@ -29,6 +29,7 @@ #include <linux/smp.h> #include <linux/spinlock.h> #include <linux/string.h> +#include <linux/sysfs.h> #include <linux/types.h> #include <linux/mutex.h> @@ -132,14 +133,14 @@ static ssize_t smi_data_buf_phys_addr_show(struct device *dev, struct device_attribute *attr, char *buf) { - return sprintf(buf, "%x\n", (u32)smi_buf.dma); + return sysfs_emit(buf, "%x\n", (u32)smi_buf.dma); } static ssize_t smi_data_buf_size_show(struct device *dev, struct device_attribute *attr, char *buf) { - return sprintf(buf, "%lu\n", smi_buf.size); + return sysfs_emit(buf, "%lu\n", smi_buf.size); } static ssize_t smi_data_buf_size_store(struct device *dev, @@ -200,7 +201,7 @@ static ssize_t host_control_action_show(struct device *dev, struct device_attribute *attr, char *buf) { - return sprintf(buf, "%u\n", host_control_action); + return sysfs_emit(buf, "%u\n", host_control_action); } static ssize_t host_control_action_store(struct device *dev, @@ -224,7 +225,7 @@ static ssize_t host_control_smi_type_show(struct device *dev, struct device_attribute *attr, char *buf) { - return sprintf(buf, "%u\n", host_control_smi_type); + return sysfs_emit(buf, "%u\n", host_control_smi_type); } static ssize_t host_control_smi_type_store(struct device *dev, @@ -239,7 +240,7 @@ static ssize_t host_control_on_shutdown_show(struct device *dev, struct device_attribute *attr, char *buf) { - return sprintf(buf, "%u\n", host_control_on_shutdown); + return sysfs_emit(buf, "%u\n", host_control_on_shutdown); } static ssize_t host_control_on_shutdown_store(struct device *dev, @@ -709,7 +710,7 @@ static struct platform_driver dcdbas_driver = { .name = DRIVER_NAME, }, .probe = dcdbas_probe, - .remove_new = dcdbas_remove, + .remove = dcdbas_remove, }; static const struct platform_device_info dcdbas_dev_info __initconst = { diff --git a/drivers/platform/x86/dell/dell-smo8800.c b/drivers/platform/x86/dell/dell-smo8800.c index f7ec17c56833..87fe03f23f24 100644 --- a/drivers/platform/x86/dell/dell-smo8800.c +++ b/drivers/platform/x86/dell/dell-smo8800.c @@ -179,7 +179,7 @@ MODULE_DEVICE_TABLE(acpi, smo8800_ids); static struct platform_driver smo8800_driver = { .probe = smo8800_probe, - .remove_new = smo8800_remove, + .remove = smo8800_remove, .driver = { .name = DRIVER_NAME, .acpi_match_table = smo8800_ids, diff --git a/drivers/platform/x86/dell/dell-uart-backlight.c b/drivers/platform/x86/dell/dell-uart-backlight.c index 3995f90add45..6e5dc7e3674f 100644 --- a/drivers/platform/x86/dell/dell-uart-backlight.c +++ b/drivers/platform/x86/dell/dell-uart-backlight.c @@ -393,7 +393,7 @@ static void dell_uart_bl_pdev_remove(struct platform_device *pdev) static struct platform_driver dell_uart_bl_pdev_driver = { .probe = dell_uart_bl_pdev_probe, - .remove_new = dell_uart_bl_pdev_remove, + .remove = dell_uart_bl_pdev_remove, .driver = { .name = "dell-uart-backlight", }, diff --git a/drivers/platform/x86/eeepc-laptop.c b/drivers/platform/x86/eeepc-laptop.c index 03319a80e114..f52fbc4924d4 100644 --- a/drivers/platform/x86/eeepc-laptop.c +++ b/drivers/platform/x86/eeepc-laptop.c @@ -25,6 +25,7 @@ #include <linux/rfkill.h> #include <linux/pci.h> #include <linux/pci_hotplug.h> +#include <linux/sysfs.h> #include <linux/leds.h> #include <linux/dmi.h> #include <acpi/video.h> @@ -285,7 +286,7 @@ static ssize_t show_sys_acpi(struct device *dev, int cm, char *buf) if (value < 0) return -EIO; - return sprintf(buf, "%d\n", value); + return sysfs_emit(buf, "%d\n", value); } #define EEEPC_ACPI_SHOW_FUNC(_name, _cm) \ @@ -361,7 +362,7 @@ static ssize_t cpufv_show(struct device *dev, if (get_cpufv(eeepc, &c)) return -ENODEV; - return sprintf(buf, "%#x\n", (c.num << 8) | c.cur); + return sysfs_emit(buf, "%#x\n", (c.num << 8) | c.cur); } static ssize_t cpufv_store(struct device *dev, @@ -393,7 +394,7 @@ static ssize_t cpufv_disabled_show(struct device *dev, { struct eeepc_laptop *eeepc = dev_get_drvdata(dev); - return sprintf(buf, "%d\n", eeepc->cpufv_disabled); + return sysfs_emit(buf, "%d\n", eeepc->cpufv_disabled); } static ssize_t cpufv_disabled_store(struct device *dev, @@ -1025,7 +1026,7 @@ static ssize_t store_sys_hwmon(void (*set)(int), const char *buf, size_t count) static ssize_t show_sys_hwmon(int (*get)(void), char *buf) { - return sprintf(buf, "%d\n", get()); + return sysfs_emit(buf, "%d\n", get()); } #define EEEPC_SENSOR_SHOW_FUNC(_name, _get) \ diff --git a/drivers/platform/x86/hp/hp-bioscfg/passwdobj-attributes.c b/drivers/platform/x86/hp/hp-bioscfg/passwdobj-attributes.c index 35936c05e45b..187b372123ed 100644 --- a/drivers/platform/x86/hp/hp-bioscfg/passwdobj-attributes.c +++ b/drivers/platform/x86/hp/hp-bioscfg/passwdobj-attributes.c @@ -531,14 +531,9 @@ void hp_exit_password_attributes(void) struct kobject *attr_name_kobj = bioscfg_drv.password_data[instance_id].attr_name_kobj; - if (attr_name_kobj) { - if (!strcmp(attr_name_kobj->name, SETUP_PASSWD)) - sysfs_remove_group(attr_name_kobj, - &password_attr_group); - else - sysfs_remove_group(attr_name_kobj, - &password_attr_group); - } + if (attr_name_kobj) + sysfs_remove_group(attr_name_kobj, + &password_attr_group); } bioscfg_drv.password_instances_count = 0; kfree(bioscfg_drv.password_data); diff --git a/drivers/platform/x86/hp/hp-wmi.c b/drivers/platform/x86/hp/hp-wmi.c index 8c05e0dd2a21..81ccc96ffe40 100644 --- a/drivers/platform/x86/hp/hp-wmi.c +++ b/drivers/platform/x86/hp/hp-wmi.c @@ -1748,7 +1748,7 @@ static struct platform_driver hp_wmi_driver __refdata = { .pm = &hp_wmi_pm_ops, .dev_groups = hp_wmi_groups, }, - .remove_new = __exit_p(hp_wmi_bios_remove), + .remove = __exit_p(hp_wmi_bios_remove), }; static umode_t hp_wmi_hwmon_is_visible(const void *data, diff --git a/drivers/platform/x86/hp/hp_accel.c b/drivers/platform/x86/hp/hp_accel.c index 52535576772a..39a6530f5072 100644 --- a/drivers/platform/x86/hp/hp_accel.c +++ b/drivers/platform/x86/hp/hp_accel.c @@ -372,7 +372,7 @@ static SIMPLE_DEV_PM_OPS(hp_accel_pm, lis3lv02d_suspend, lis3lv02d_resume); /* For the HP MDPS aka 3D Driveguard */ static struct platform_driver lis3lv02d_driver = { .probe = lis3lv02d_probe, - .remove_new = lis3lv02d_remove, + .remove = lis3lv02d_remove, .driver = { .name = "hp_accel", .pm = &hp_accel_pm, diff --git a/drivers/platform/x86/hp/tc1100-wmi.c b/drivers/platform/x86/hp/tc1100-wmi.c index 5298b0f6804f..146716d81442 100644 --- a/drivers/platform/x86/hp/tc1100-wmi.c +++ b/drivers/platform/x86/hp/tc1100-wmi.c @@ -221,7 +221,7 @@ static struct platform_driver tc1100_driver = { .pm = &tc1100_pm_ops, #endif }, - .remove_new = tc1100_remove, + .remove = tc1100_remove, }; static int __init tc1100_init(void) diff --git a/drivers/platform/x86/huawei-wmi.c b/drivers/platform/x86/huawei-wmi.c index d81fd5df4a00..c3772df34679 100644 --- a/drivers/platform/x86/huawei-wmi.c +++ b/drivers/platform/x86/huawei-wmi.c @@ -842,7 +842,7 @@ static struct platform_driver huawei_wmi_driver = { .name = "huawei-wmi", }, .probe = huawei_wmi_probe, - .remove_new = huawei_wmi_remove, + .remove = huawei_wmi_remove, }; static __init int huawei_wmi_init(void) diff --git a/drivers/platform/x86/ideapad-laptop.c b/drivers/platform/x86/ideapad-laptop.c index c908f52ed717..5d2c1f0d1e9f 100644 --- a/drivers/platform/x86/ideapad-laptop.c +++ b/drivers/platform/x86/ideapad-laptop.c @@ -2309,7 +2309,7 @@ MODULE_DEVICE_TABLE(acpi, ideapad_device_ids); static struct platform_driver ideapad_acpi_driver = { .probe = ideapad_acpi_add, - .remove_new = ideapad_acpi_remove, + .remove = ideapad_acpi_remove, .driver = { .name = "ideapad_acpi", .pm = &ideapad_pm, diff --git a/drivers/platform/x86/intel/Makefile b/drivers/platform/x86/intel/Makefile index 74db065c82d6..78acb414e154 100644 --- a/drivers/platform/x86/intel/Makefile +++ b/drivers/platform/x86/intel/Makefile @@ -17,50 +17,40 @@ obj-$(CONFIG_INTEL_UNCORE_FREQ_CONTROL) += uncore-frequency/ # Intel input drivers -intel-hid-y := hid.o -obj-$(CONFIG_INTEL_HID_EVENT) += intel-hid.o -intel-vbtn-y := vbtn.o -obj-$(CONFIG_INTEL_VBTN) += intel-vbtn.o +intel-target-$(CONFIG_INTEL_HID_EVENT) += hid.o +intel-target-$(CONFIG_INTEL_VBTN) += vbtn.o # Intel miscellaneous drivers -obj-$(CONFIG_INTEL_ISHTP_ECLITE) += ishtp_eclite.o -intel_int0002_vgpio-y := int0002_vgpio.o -obj-$(CONFIG_INTEL_INT0002_VGPIO) += intel_int0002_vgpio.o -intel_oaktrail-y := oaktrail.o -obj-$(CONFIG_INTEL_OAKTRAIL) += intel_oaktrail.o -intel_sdsi-y := sdsi.o -obj-$(CONFIG_INTEL_SDSI) += intel_sdsi.o -intel_vsec-y := vsec.o -obj-$(CONFIG_INTEL_VSEC) += intel_vsec.o +intel-target-$(CONFIG_INTEL_INT0002_VGPIO) += int0002_vgpio.o +intel-target-$(CONFIG_INTEL_ISHTP_ECLITE) += ishtp_eclite.o +intel-target-$(CONFIG_INTEL_OAKTRAIL) += oaktrail.o +intel-target-$(CONFIG_INTEL_SDSI) += sdsi.o +intel-target-$(CONFIG_INTEL_VSEC) += vsec.o # Intel PMIC / PMC / P-Unit drivers -intel_bxtwc_tmu-y := bxtwc_tmu.o -obj-$(CONFIG_INTEL_BXTWC_PMIC_TMU) += intel_bxtwc_tmu.o -intel_crystal_cove_charger-y := crystal_cove_charger.o -obj-$(CONFIG_X86_ANDROID_TABLETS) += intel_crystal_cove_charger.o -intel_bytcrc_pwrsrc-y := bytcrc_pwrsrc.o -obj-$(CONFIG_INTEL_BYTCRC_PWRSRC) += intel_bytcrc_pwrsrc.o -intel_chtdc_ti_pwrbtn-y := chtdc_ti_pwrbtn.o -obj-$(CONFIG_INTEL_CHTDC_TI_PWRBTN) += intel_chtdc_ti_pwrbtn.o -intel_chtwc_int33fe-y := chtwc_int33fe.o -obj-$(CONFIG_INTEL_CHTWC_INT33FE) += intel_chtwc_int33fe.o -intel_mrfld_pwrbtn-y := mrfld_pwrbtn.o -obj-$(CONFIG_INTEL_MRFLD_PWRBTN) += intel_mrfld_pwrbtn.o -intel_punit_ipc-y := punit_ipc.o -obj-$(CONFIG_INTEL_PUNIT_IPC) += intel_punit_ipc.o +intel-target-$(CONFIG_INTEL_BYTCRC_PWRSRC) += bytcrc_pwrsrc.o +intel-target-$(CONFIG_INTEL_BXTWC_PMIC_TMU) += bxtwc_tmu.o +intel-target-$(CONFIG_INTEL_CHTDC_TI_PWRBTN) += chtdc_ti_pwrbtn.o +intel-target-$(CONFIG_INTEL_CHTWC_INT33FE) += chtwc_int33fe.o +intel-target-$(CONFIG_X86_ANDROID_TABLETS) += crystal_cove_charger.o +intel-target-$(CONFIG_INTEL_MRFLD_PWRBTN) += mrfld_pwrbtn.o +intel-target-$(CONFIG_INTEL_PUNIT_IPC) += 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-target-$(CONFIG_INTEL_PLR_TPMI) += plr_tpmi.o +intel-target-$(CONFIG_INTEL_TPMI_POWER_DOMAINS) += tpmi_power_domains.o +intel-target-$(CONFIG_INTEL_TPMI) += vsec_tpmi.o # Intel Uncore drivers -intel-rst-y := rst.o -obj-$(CONFIG_INTEL_RST) += intel-rst.o -intel-smartconnect-y := smartconnect.o -obj-$(CONFIG_INTEL_SMARTCONNECT) += intel-smartconnect.o -intel_turbo_max_3-y := turbo_max_3.o -obj-$(CONFIG_INTEL_TURBO_MAX_3) += intel_turbo_max_3.o +intel-target-$(CONFIG_INTEL_RST) += rst.o +intel-target-$(CONFIG_INTEL_SMARTCONNECT) += smartconnect.o +intel-target-$(CONFIG_INTEL_TURBO_MAX_3) += turbo_max_3.o + +# Add 'intel' prefix to each module listed in intel-target-* +define INTEL_OBJ_TARGET +intel-$(1)-y := $(1).o +obj-$(2) += intel-$(1).o +endef + +$(foreach target, $(basename $(intel-target-y)), $(eval $(call INTEL_OBJ_TARGET,$(target),y))) +$(foreach target, $(basename $(intel-target-m)), $(eval $(call INTEL_OBJ_TARGET,$(target),m))) diff --git a/drivers/platform/x86/intel/bxtwc_tmu.c b/drivers/platform/x86/intel/bxtwc_tmu.c index d0e2a3c293b0..1aabd15e2714 100644 --- a/drivers/platform/x86/intel/bxtwc_tmu.c +++ b/drivers/platform/x86/intel/bxtwc_tmu.c @@ -131,7 +131,7 @@ MODULE_DEVICE_TABLE(platform, bxt_wcove_tmu_id_table); static struct platform_driver bxt_wcove_tmu_driver = { .probe = bxt_wcove_tmu_probe, - .remove_new = bxt_wcove_tmu_remove, + .remove = bxt_wcove_tmu_remove, .driver = { .name = "bxt_wcove_tmu", .pm = &bxtwc_tmu_pm_ops, diff --git a/drivers/platform/x86/intel/bytcrc_pwrsrc.c b/drivers/platform/x86/intel/bytcrc_pwrsrc.c index 418b71af27ff..3edc2a9dab38 100644 --- a/drivers/platform/x86/intel/bytcrc_pwrsrc.c +++ b/drivers/platform/x86/intel/bytcrc_pwrsrc.c @@ -167,7 +167,7 @@ static void crc_pwrsrc_remove(struct platform_device *pdev) static struct platform_driver crc_pwrsrc_driver = { .probe = crc_pwrsrc_probe, - .remove_new = crc_pwrsrc_remove, + .remove = crc_pwrsrc_remove, .driver = { .name = "crystal_cove_pwrsrc", }, diff --git a/drivers/platform/x86/intel/chtdc_ti_pwrbtn.c b/drivers/platform/x86/intel/chtdc_ti_pwrbtn.c index 615f8d1a0c68..53f01e198047 100644 --- a/drivers/platform/x86/intel/chtdc_ti_pwrbtn.c +++ b/drivers/platform/x86/intel/chtdc_ti_pwrbtn.c @@ -84,7 +84,7 @@ static struct platform_driver chtdc_ti_pwrbtn_driver = { .name = KBUILD_MODNAME, }, .probe = chtdc_ti_pwrbtn_probe, - .remove_new = chtdc_ti_pwrbtn_remove, + .remove = chtdc_ti_pwrbtn_remove, .id_table = chtdc_ti_pwrbtn_id_table, }; module_platform_driver(chtdc_ti_pwrbtn_driver); diff --git a/drivers/platform/x86/intel/chtwc_int33fe.c b/drivers/platform/x86/intel/chtwc_int33fe.c index 11503b1c85f3..29e8b5432f4c 100644 --- a/drivers/platform/x86/intel/chtwc_int33fe.c +++ b/drivers/platform/x86/intel/chtwc_int33fe.c @@ -427,7 +427,7 @@ static struct platform_driver cht_int33fe_typec_driver = { .acpi_match_table = ACPI_PTR(cht_int33fe_acpi_ids), }, .probe = cht_int33fe_typec_probe, - .remove_new = cht_int33fe_typec_remove, + .remove = cht_int33fe_typec_remove, }; module_platform_driver(cht_int33fe_typec_driver); diff --git a/drivers/platform/x86/intel/hid.c b/drivers/platform/x86/intel/hid.c index 445e7a59beb4..927a2993f616 100644 --- a/drivers/platform/x86/intel/hid.c +++ b/drivers/platform/x86/intel/hid.c @@ -119,6 +119,13 @@ static const struct dmi_system_id button_array_table[] = { }, }, { + .ident = "Lenovo ThinkPad X1 Tablet Gen 1", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"), + DMI_MATCH(DMI_PRODUCT_FAMILY, "ThinkPad X12 Detachable Gen 1"), + }, + }, + { .ident = "Lenovo ThinkPad X1 Tablet Gen 2", .matches = { DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"), @@ -747,7 +754,7 @@ static struct platform_driver intel_hid_pl_driver = { .pm = &intel_hid_pl_pm_ops, }, .probe = intel_hid_probe, - .remove_new = intel_hid_remove, + .remove = intel_hid_remove, }; /* diff --git a/drivers/platform/x86/intel/int0002_vgpio.c b/drivers/platform/x86/intel/int0002_vgpio.c index 527d8fbc7cc1..0cc80603a8a9 100644 --- a/drivers/platform/x86/intel/int0002_vgpio.c +++ b/drivers/platform/x86/intel/int0002_vgpio.c @@ -266,13 +266,13 @@ static const struct acpi_device_id int0002_acpi_ids[] = { MODULE_DEVICE_TABLE(acpi, int0002_acpi_ids); static struct platform_driver int0002_driver = { - .driver = { + .driver = { .name = DRV_NAME, .acpi_match_table = int0002_acpi_ids, .pm = &int0002_pm_ops, }, .probe = int0002_probe, - .remove_new = int0002_remove, + .remove = int0002_remove, }; module_platform_driver(int0002_driver); diff --git a/drivers/platform/x86/intel/int1092/intel_sar.c b/drivers/platform/x86/intel/int1092/intel_sar.c index 6246c066ade2..e526841aff60 100644 --- a/drivers/platform/x86/intel/int1092/intel_sar.c +++ b/drivers/platform/x86/intel/int1092/intel_sar.c @@ -308,7 +308,7 @@ static void sar_remove(struct platform_device *device) static struct platform_driver sar_driver = { .probe = sar_probe, - .remove_new = sar_remove, + .remove = sar_remove, .driver = { .name = DRVNAME, .acpi_match_table = ACPI_PTR(sar_device_ids) diff --git a/drivers/platform/x86/intel/int3472/discrete.c b/drivers/platform/x86/intel/int3472/discrete.c index 3de463c3d13b..d881b2cfcdfc 100644 --- a/drivers/platform/x86/intel/int3472/discrete.c +++ b/drivers/platform/x86/intel/int3472/discrete.c @@ -392,7 +392,7 @@ static struct platform_driver int3472_discrete = { .acpi_match_table = int3472_device_id, }, .probe = skl_int3472_discrete_probe, - .remove_new = skl_int3472_discrete_remove, + .remove = skl_int3472_discrete_remove, }; module_platform_driver(int3472_discrete); diff --git a/drivers/platform/x86/intel/mrfld_pwrbtn.c b/drivers/platform/x86/intel/mrfld_pwrbtn.c index 549a3f586f3b..6c43f801c467 100644 --- a/drivers/platform/x86/intel/mrfld_pwrbtn.c +++ b/drivers/platform/x86/intel/mrfld_pwrbtn.c @@ -97,7 +97,7 @@ static struct platform_driver mrfld_pwrbtn_driver = { .name = "mrfld_bcove_pwrbtn", }, .probe = mrfld_pwrbtn_probe, - .remove_new = mrfld_pwrbtn_remove, + .remove = mrfld_pwrbtn_remove, .id_table = mrfld_pwrbtn_id_table, }; module_platform_driver(mrfld_pwrbtn_driver); diff --git a/drivers/platform/x86/intel/intel_plr_tpmi.c b/drivers/platform/x86/intel/plr_tpmi.c index 69ace6a629bc..69ace6a629bc 100644 --- a/drivers/platform/x86/intel/intel_plr_tpmi.c +++ b/drivers/platform/x86/intel/plr_tpmi.c diff --git a/drivers/platform/x86/intel/pmc/arl.c b/drivers/platform/x86/intel/pmc/arl.c index e10527c4e3e0..05dec4f5019f 100644 --- a/drivers/platform/x86/intel/pmc/arl.c +++ b/drivers/platform/x86/intel/pmc/arl.c @@ -687,9 +687,8 @@ static void arl_d3_fixup(void) static int arl_resume(struct pmc_dev *pmcdev) { arl_d3_fixup(); - pmc_core_send_ltr_ignore(pmcdev, 3, 0); - return pmc_core_resume_common(pmcdev); + return cnl_resume(pmcdev); } int arl_core_init(struct pmc_dev *pmcdev) diff --git a/drivers/platform/x86/intel/pmc/cnp.c b/drivers/platform/x86/intel/pmc/cnp.c index dd72974bf71e..fc5193fdf8a8 100644 --- a/drivers/platform/x86/intel/pmc/cnp.c +++ b/drivers/platform/x86/intel/pmc/cnp.c @@ -8,6 +8,8 @@ * */ +#include <linux/smp.h> +#include <linux/suspend.h> #include "core.h" /* Cannon Lake: PGD PFET Enable Ack Status Register(s) bitmap */ @@ -204,8 +206,57 @@ const struct pmc_reg_map cnp_reg_map = { .etr3_offset = ETR3_OFFSET, }; + +/* + * Disable C1 auto-demotion + * + * Aggressive C1 auto-demotion may lead to failure to enter the deepest C-state + * during suspend-to-idle, causing high power consumption. To prevent this, we + * disable C1 auto-demotion during suspend and re-enable on resume. + * + * Note that, although MSR_PKG_CST_CONFIG_CONTROL has 'package' in its name, it + * is actually a per-core MSR on client platforms, affecting only a single CPU. + * Therefore, it must be configured on all online CPUs. The online cpu mask is + * unchanged during the phase of suspend/resume as user space is frozen. + */ + +static DEFINE_PER_CPU(u64, pkg_cst_config); + +static void disable_c1_auto_demote(void *unused) +{ + int cpunum = smp_processor_id(); + u64 val; + + rdmsrl(MSR_PKG_CST_CONFIG_CONTROL, val); + per_cpu(pkg_cst_config, cpunum) = val; + val &= ~NHM_C1_AUTO_DEMOTE; + wrmsrl(MSR_PKG_CST_CONFIG_CONTROL, val); + + pr_debug("%s: cpu:%d cst %llx\n", __func__, cpunum, val); +} + +static void restore_c1_auto_demote(void *unused) +{ + int cpunum = smp_processor_id(); + + wrmsrl(MSR_PKG_CST_CONFIG_CONTROL, per_cpu(pkg_cst_config, cpunum)); + + pr_debug("%s: cpu:%d cst %llx\n", __func__, cpunum, + per_cpu(pkg_cst_config, cpunum)); +} + +static void s2idle_cpu_quirk(smp_call_func_t func) +{ + if (pm_suspend_via_firmware()) + return; + + on_each_cpu(func, NULL, true); +} + void cnl_suspend(struct pmc_dev *pmcdev) { + s2idle_cpu_quirk(disable_c1_auto_demote); + /* * Due to a hardware limitation, the GBE LTR blocks PC10 * when a cable is attached. To unblock PC10 during suspend, @@ -216,6 +267,8 @@ void cnl_suspend(struct pmc_dev *pmcdev) int cnl_resume(struct pmc_dev *pmcdev) { + s2idle_cpu_quirk(restore_c1_auto_demote); + pmc_core_send_ltr_ignore(pmcdev, 3, 0); return pmc_core_resume_common(pmcdev); diff --git a/drivers/platform/x86/intel/pmc/core.c b/drivers/platform/x86/intel/pmc/core.c index 4e9c8c96c8cc..3e7f99ac8c94 100644 --- a/drivers/platform/x86/intel/pmc/core.c +++ b/drivers/platform/x86/intel/pmc/core.c @@ -1676,7 +1676,7 @@ static struct platform_driver pmc_core_driver = { .dev_groups = pmc_dev_groups, }, .probe = pmc_core_probe, - .remove_new = pmc_core_remove, + .remove = pmc_core_remove, }; module_platform_driver(pmc_core_driver); diff --git a/drivers/platform/x86/intel/pmc/lnl.c b/drivers/platform/x86/intel/pmc/lnl.c index e7a8077d1a3e..be029f12cdf4 100644 --- a/drivers/platform/x86/intel/pmc/lnl.c +++ b/drivers/platform/x86/intel/pmc/lnl.c @@ -546,9 +546,8 @@ static void lnl_d3_fixup(void) static int lnl_resume(struct pmc_dev *pmcdev) { lnl_d3_fixup(); - pmc_core_send_ltr_ignore(pmcdev, 3, 0); - return pmc_core_resume_common(pmcdev); + return cnl_resume(pmcdev); } int lnl_core_init(struct pmc_dev *pmcdev) diff --git a/drivers/platform/x86/intel/pmc/mtl.c b/drivers/platform/x86/intel/pmc/mtl.c index c7d15d864039..02949fed76e9 100644 --- a/drivers/platform/x86/intel/pmc/mtl.c +++ b/drivers/platform/x86/intel/pmc/mtl.c @@ -986,9 +986,8 @@ static void mtl_d3_fixup(void) static int mtl_resume(struct pmc_dev *pmcdev) { mtl_d3_fixup(); - pmc_core_send_ltr_ignore(pmcdev, 3, 0); - return pmc_core_resume_common(pmcdev); + return cnl_resume(pmcdev); } int mtl_core_init(struct pmc_dev *pmcdev) diff --git a/drivers/platform/x86/intel/pmt/class.c b/drivers/platform/x86/intel/pmt/class.c index c04bb7f97a4d..375695cc0d60 100644 --- a/drivers/platform/x86/intel/pmt/class.c +++ b/drivers/platform/x86/intel/pmt/class.c @@ -59,10 +59,12 @@ pmt_memcpy64_fromio(void *to, const u64 __iomem *from, size_t count) } int pmt_telem_read_mmio(struct pci_dev *pdev, struct pmt_callbacks *cb, u32 guid, void *buf, - void __iomem *addr, u32 count) + void __iomem *addr, loff_t off, u32 count) { if (cb && cb->read_telem) - return cb->read_telem(pdev, guid, buf, count); + return cb->read_telem(pdev, guid, buf, off, count); + + addr += off; if (guid == GUID_SPR_PUNIT) /* PUNIT on SPR only supports aligned 64-bit read */ @@ -96,7 +98,7 @@ intel_pmt_read(struct file *filp, struct kobject *kobj, count = entry->size - off; count = pmt_telem_read_mmio(entry->ep->pcidev, entry->cb, entry->header.guid, buf, - entry->base + off, count); + entry->base, off, count); return count; } @@ -207,7 +209,7 @@ static int intel_pmt_populate_entry(struct intel_pmt_entry *entry, /* * Some hardware use a different calculation for the base address * when access_type == ACCESS_LOCAL. On the these systems - * ACCCESS_LOCAL refers to an address in the same BAR as the + * ACCESS_LOCAL refers to an address in the same BAR as the * header but at a fixed offset. But as the header address was * supplied to the driver, we don't know which BAR it was in. * So search for the bar whose range includes the header address. diff --git a/drivers/platform/x86/intel/pmt/class.h b/drivers/platform/x86/intel/pmt/class.h index a267ac964423..b2006d57779d 100644 --- a/drivers/platform/x86/intel/pmt/class.h +++ b/drivers/platform/x86/intel/pmt/class.h @@ -62,7 +62,7 @@ struct intel_pmt_namespace { }; int pmt_telem_read_mmio(struct pci_dev *pdev, struct pmt_callbacks *cb, u32 guid, void *buf, - void __iomem *addr, u32 count); + void __iomem *addr, loff_t off, u32 count); bool intel_pmt_is_early_client_hw(struct device *dev); int intel_pmt_dev_create(struct intel_pmt_entry *entry, struct intel_pmt_namespace *ns, diff --git a/drivers/platform/x86/intel/pmt/telemetry.c b/drivers/platform/x86/intel/pmt/telemetry.c index c9feac859e57..0cea617c6c2e 100644 --- a/drivers/platform/x86/intel/pmt/telemetry.c +++ b/drivers/platform/x86/intel/pmt/telemetry.c @@ -219,7 +219,7 @@ int pmt_telem_read(struct telem_endpoint *ep, u32 id, u64 *data, u32 count) if (offset + NUM_BYTES_QWORD(count) > size) return -EINVAL; - pmt_telem_read_mmio(ep->pcidev, ep->cb, ep->header.guid, data, ep->base + offset, + pmt_telem_read_mmio(ep->pcidev, ep->cb, ep->header.guid, data, ep->base, offset, NUM_BYTES_QWORD(count)); return ep->present ? 0 : -EPIPE; diff --git a/drivers/platform/x86/intel/telemetry/pltdrv.c b/drivers/platform/x86/intel/telemetry/pltdrv.c index 767a0bc6c7ad..9a499efa1e4d 100644 --- a/drivers/platform/x86/intel/telemetry/pltdrv.c +++ b/drivers/platform/x86/intel/telemetry/pltdrv.c @@ -1163,7 +1163,7 @@ static void telemetry_pltdrv_remove(struct platform_device *pdev) static struct platform_driver telemetry_soc_driver = { .probe = telemetry_pltdrv_probe, - .remove_new = telemetry_pltdrv_remove, + .remove = telemetry_pltdrv_remove, .driver = { .name = DRIVER_NAME, }, diff --git a/drivers/platform/x86/intel/vbtn.c b/drivers/platform/x86/intel/vbtn.c index a353e830b65f..232cd12e3c9f 100644 --- a/drivers/platform/x86/intel/vbtn.c +++ b/drivers/platform/x86/intel/vbtn.c @@ -387,7 +387,7 @@ static struct platform_driver intel_vbtn_pl_driver = { .pm = &intel_vbtn_pm_ops, }, .probe = intel_vbtn_probe, - .remove_new = intel_vbtn_remove, + .remove = intel_vbtn_remove, }; static acpi_status __init diff --git a/drivers/platform/x86/intel/vsec.c b/drivers/platform/x86/intel/vsec.c index 7b5cc9993974..9e0f8e38178c 100644 --- a/drivers/platform/x86/intel/vsec.c +++ b/drivers/platform/x86/intel/vsec.c @@ -79,17 +79,13 @@ static void intel_vsec_remove_aux(void *data) auxiliary_device_uninit(data); } -static DEFINE_MUTEX(vsec_ida_lock); - static void intel_vsec_dev_release(struct device *dev) { struct intel_vsec_device *intel_vsec_dev = dev_to_ivdev(dev); xa_erase(&auxdev_array, intel_vsec_dev->id); - mutex_lock(&vsec_ida_lock); ida_free(intel_vsec_dev->ida, intel_vsec_dev->auxdev.id); - mutex_unlock(&vsec_ida_lock); kfree(intel_vsec_dev->resource); kfree(intel_vsec_dev); @@ -113,9 +109,7 @@ int intel_vsec_add_aux(struct pci_dev *pdev, struct device *parent, return ret; } - mutex_lock(&vsec_ida_lock); id = ida_alloc(intel_vsec_dev->ida, GFP_KERNEL); - mutex_unlock(&vsec_ida_lock); if (id < 0) { xa_erase(&auxdev_array, intel_vsec_dev->id); kfree(intel_vsec_dev->resource); diff --git a/drivers/platform/x86/intel/tpmi.c b/drivers/platform/x86/intel/vsec_tpmi.c index 486ddc9b3592..c637e32048a3 100644 --- a/drivers/platform/x86/intel/tpmi.c +++ b/drivers/platform/x86/intel/vsec_tpmi.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0-only /* - * intel-tpmi : Driver to enumerate TPMI features and create devices + * Driver to enumerate TPMI features and create devices * * Copyright (c) 2023, Intel Corporation. * All Rights Reserved. diff --git a/drivers/platform/x86/intel_scu_ipc.c b/drivers/platform/x86/intel_scu_ipc.c index 5b16d29c93d7..3acf6149a9ec 100644 --- a/drivers/platform/x86/intel_scu_ipc.c +++ b/drivers/platform/x86/intel_scu_ipc.c @@ -13,6 +13,7 @@ * along with other APIs. */ +#include <linux/cleanup.h> #include <linux/delay.h> #include <linux/device.h> #include <linux/errno.h> @@ -56,11 +57,11 @@ struct intel_scu_ipc_dev { struct device dev; - struct resource mem; struct module *owner; - int irq; void __iomem *ipc_base; struct completion cmd_complete; + + struct intel_scu_ipc_data data; }; #define IPC_STATUS 0x04 @@ -99,23 +100,21 @@ static struct class intel_scu_ipc_class = { */ struct intel_scu_ipc_dev *intel_scu_ipc_dev_get(void) { - struct intel_scu_ipc_dev *scu = NULL; + guard(mutex)(&ipclock); - mutex_lock(&ipclock); if (ipcdev) { get_device(&ipcdev->dev); /* * Prevent the IPC provider from being unloaded while it * is being used. */ - if (!try_module_get(ipcdev->owner)) - put_device(&ipcdev->dev); - else - scu = ipcdev; + if (try_module_get(ipcdev->owner)) + return ipcdev; + + put_device(&ipcdev->dev); } - mutex_unlock(&ipclock); - return scu; + return NULL; } EXPORT_SYMBOL_GPL(intel_scu_ipc_dev_get); @@ -217,12 +216,6 @@ static inline u8 ipc_read_status(struct intel_scu_ipc_dev *scu) return __raw_readl(scu->ipc_base + IPC_STATUS); } -/* Read ipc byte data */ -static inline u8 ipc_data_readb(struct intel_scu_ipc_dev *scu, u32 offset) -{ - return readb(scu->ipc_base + IPC_READ_BUFFER + offset); -} - /* Read ipc u32 data */ static inline u32 ipc_data_readl(struct intel_scu_ipc_dev *scu, u32 offset) { @@ -262,7 +255,7 @@ static inline int ipc_wait_for_interrupt(struct intel_scu_ipc_dev *scu) static int intel_scu_ipc_check_status(struct intel_scu_ipc_dev *scu) { - return scu->irq > 0 ? ipc_wait_for_interrupt(scu) : busy_loop(scu); + return scu->data.irq > 0 ? ipc_wait_for_interrupt(scu) : busy_loop(scu); } static struct intel_scu_ipc_dev *intel_scu_ipc_get(struct intel_scu_ipc_dev *scu) @@ -295,12 +288,11 @@ static int pwr_reg_rdwr(struct intel_scu_ipc_dev *scu, u16 *addr, u8 *data, memset(cbuf, 0, sizeof(cbuf)); - mutex_lock(&ipclock); + guard(mutex)(&ipclock); + scu = intel_scu_ipc_get(scu); - if (IS_ERR(scu)) { - mutex_unlock(&ipclock); + if (IS_ERR(scu)) return PTR_ERR(scu); - } for (nc = 0; nc < count; nc++, offset += 2) { cbuf[offset] = addr[nc]; @@ -325,14 +317,15 @@ static int pwr_reg_rdwr(struct intel_scu_ipc_dev *scu, u16 *addr, u8 *data, } err = intel_scu_ipc_check_status(scu); - if (!err && id == IPC_CMD_PCNTRL_R) { /* Read rbuf */ - /* Workaround: values are read as 0 without memcpy_fromio */ - memcpy_fromio(cbuf, scu->ipc_base + 0x90, 16); - for (nc = 0; nc < count; nc++) - data[nc] = ipc_data_readb(scu, nc); - } - mutex_unlock(&ipclock); - return err; + if (err) + return err; + + /* Read rbuf */ + for (nc = 0, offset = 0; nc < 4; nc++, offset += 4) + wbuf[nc] = ipc_data_readl(scu, offset); + memcpy(data, wbuf, count); + + return 0; } /** @@ -453,17 +446,15 @@ int intel_scu_ipc_dev_simple_command(struct intel_scu_ipc_dev *scu, int cmd, u32 cmdval; int err; - mutex_lock(&ipclock); + guard(mutex)(&ipclock); + scu = intel_scu_ipc_get(scu); - if (IS_ERR(scu)) { - mutex_unlock(&ipclock); + if (IS_ERR(scu)) return PTR_ERR(scu); - } cmdval = sub << 12 | cmd; ipc_command(scu, cmdval); err = intel_scu_ipc_check_status(scu); - mutex_unlock(&ipclock); if (err) dev_err(&scu->dev, "IPC command %#x failed with %d\n", cmdval, err); return err; @@ -492,18 +483,17 @@ int intel_scu_ipc_dev_command_with_size(struct intel_scu_ipc_dev *scu, int cmd, { size_t outbuflen = DIV_ROUND_UP(outlen, sizeof(u32)); size_t inbuflen = DIV_ROUND_UP(inlen, sizeof(u32)); - u32 cmdval, inbuf[4] = {}; + u32 cmdval, inbuf[4] = {}, outbuf[4] = {}; int i, err; if (inbuflen > 4 || outbuflen > 4) return -EINVAL; - mutex_lock(&ipclock); + guard(mutex)(&ipclock); + scu = intel_scu_ipc_get(scu); - if (IS_ERR(scu)) { - mutex_unlock(&ipclock); + if (IS_ERR(scu)) return PTR_ERR(scu); - } memcpy(inbuf, in, inlen); for (i = 0; i < inbuflen; i++) @@ -512,20 +502,17 @@ int intel_scu_ipc_dev_command_with_size(struct intel_scu_ipc_dev *scu, int cmd, cmdval = (size << 16) | (sub << 12) | cmd; ipc_command(scu, cmdval); err = intel_scu_ipc_check_status(scu); + if (err) { + dev_err(&scu->dev, "IPC command %#x failed with %d\n", cmdval, err); + return err; + } - if (!err) { - u32 outbuf[4] = {}; - - for (i = 0; i < outbuflen; i++) - outbuf[i] = ipc_data_readl(scu, 4 * i); + for (i = 0; i < outbuflen; i++) + outbuf[i] = ipc_data_readl(scu, 4 * i); - memcpy(out, outbuf, outlen); - } + memcpy(out, outbuf, outlen); - mutex_unlock(&ipclock); - if (err) - dev_err(&scu->dev, "IPC command %#x failed with %d\n", cmdval, err); - return err; + return 0; } EXPORT_SYMBOL(intel_scu_ipc_dev_command_with_size); @@ -549,13 +536,13 @@ static irqreturn_t ioc(int irq, void *dev_id) static void intel_scu_ipc_release(struct device *dev) { - struct intel_scu_ipc_dev *scu; + struct intel_scu_ipc_dev *scu = container_of(dev, struct intel_scu_ipc_dev, dev); + struct intel_scu_ipc_data *data = &scu->data; - scu = container_of(dev, struct intel_scu_ipc_dev, dev); - if (scu->irq > 0) - free_irq(scu->irq, scu); + if (data->irq > 0) + free_irq(data->irq, scu); iounmap(scu->ipc_base); - release_mem_region(scu->mem.start, resource_size(&scu->mem)); + release_mem_region(data->mem.start, resource_size(&data->mem)); kfree(scu); } @@ -576,46 +563,44 @@ __intel_scu_ipc_register(struct device *parent, struct module *owner) { int err; + struct intel_scu_ipc_data *data; struct intel_scu_ipc_dev *scu; void __iomem *ipc_base; - mutex_lock(&ipclock); + guard(mutex)(&ipclock); + /* We support only one IPC */ - if (ipcdev) { - err = -EBUSY; - goto err_unlock; - } + if (ipcdev) + return ERR_PTR(-EBUSY); scu = kzalloc(sizeof(*scu), GFP_KERNEL); - if (!scu) { - err = -ENOMEM; - goto err_unlock; - } + if (!scu) + return ERR_PTR(-ENOMEM); scu->owner = owner; scu->dev.parent = parent; scu->dev.class = &intel_scu_ipc_class; scu->dev.release = intel_scu_ipc_release; - if (!request_mem_region(scu_data->mem.start, resource_size(&scu_data->mem), - "intel_scu_ipc")) { + memcpy(&scu->data, scu_data, sizeof(scu->data)); + data = &scu->data; + + if (!request_mem_region(data->mem.start, resource_size(&data->mem), "intel_scu_ipc")) { err = -EBUSY; goto err_free; } - ipc_base = ioremap(scu_data->mem.start, resource_size(&scu_data->mem)); + ipc_base = ioremap(data->mem.start, resource_size(&data->mem)); if (!ipc_base) { err = -ENOMEM; goto err_release; } scu->ipc_base = ipc_base; - scu->mem = scu_data->mem; - scu->irq = scu_data->irq; init_completion(&scu->cmd_complete); - if (scu->irq > 0) { - err = request_irq(scu->irq, ioc, 0, "intel_scu_ipc", scu); + if (data->irq > 0) { + err = request_irq(data->irq, ioc, 0, "intel_scu_ipc", scu); if (err) goto err_unmap; } @@ -628,24 +613,19 @@ __intel_scu_ipc_register(struct device *parent, err = device_register(&scu->dev); if (err) { put_device(&scu->dev); - goto err_unlock; + return ERR_PTR(err); } /* Assign device at last */ ipcdev = scu; - mutex_unlock(&ipclock); - return scu; err_unmap: iounmap(ipc_base); err_release: - release_mem_region(scu_data->mem.start, resource_size(&scu_data->mem)); + release_mem_region(data->mem.start, resource_size(&data->mem)); err_free: kfree(scu); -err_unlock: - mutex_unlock(&ipclock); - return ERR_PTR(err); } EXPORT_SYMBOL_GPL(__intel_scu_ipc_register); @@ -659,12 +639,12 @@ EXPORT_SYMBOL_GPL(__intel_scu_ipc_register); */ void intel_scu_ipc_unregister(struct intel_scu_ipc_dev *scu) { - mutex_lock(&ipclock); + guard(mutex)(&ipclock); + if (!WARN_ON(!ipcdev)) { ipcdev = NULL; device_unregister(&scu->dev); } - mutex_unlock(&ipclock); } EXPORT_SYMBOL_GPL(intel_scu_ipc_unregister); diff --git a/drivers/platform/x86/lenovo-yoga-tab2-pro-1380-fastcharger.c b/drivers/platform/x86/lenovo-yoga-tab2-pro-1380-fastcharger.c index d525bdc8ca9b..d2699ca24f34 100644 --- a/drivers/platform/x86/lenovo-yoga-tab2-pro-1380-fastcharger.c +++ b/drivers/platform/x86/lenovo-yoga-tab2-pro-1380-fastcharger.c @@ -298,7 +298,7 @@ static void yt2_1380_fc_pdev_remove(struct platform_device *pdev) static struct platform_driver yt2_1380_fc_pdev_driver = { .probe = yt2_1380_fc_pdev_probe, - .remove_new = yt2_1380_fc_pdev_remove, + .remove = yt2_1380_fc_pdev_remove, .driver = { .name = YT2_1380_FC_PDEV_NAME, .probe_type = PROBE_PREFER_ASYNCHRONOUS, diff --git a/drivers/platform/x86/lenovo-yogabook.c b/drivers/platform/x86/lenovo-yogabook.c index fd62bf746ebd..31b298dc5046 100644 --- a/drivers/platform/x86/lenovo-yogabook.c +++ b/drivers/platform/x86/lenovo-yogabook.c @@ -536,7 +536,7 @@ static void yogabook_pdev_remove(struct platform_device *pdev) static struct platform_driver yogabook_pdev_driver = { .probe = yogabook_pdev_probe, - .remove_new = yogabook_pdev_remove, + .remove = yogabook_pdev_remove, .driver = { .name = YB_PDEV_NAME, .pm = pm_sleep_ptr(&yogabook_pm_ops), diff --git a/drivers/platform/x86/mlx-platform.c b/drivers/platform/x86/mlx-platform.c index 9d70146fd742..671021cd1f59 100644 --- a/drivers/platform/x86/mlx-platform.c +++ b/drivers/platform/x86/mlx-platform.c @@ -6633,7 +6633,7 @@ static struct platform_driver mlxplat_driver = { .probe_type = PROBE_FORCE_SYNCHRONOUS, }, .probe = mlxplat_probe, - .remove_new = mlxplat_remove, + .remove = mlxplat_remove, }; static int __init mlxplat_init(void) diff --git a/drivers/platform/x86/p2sb.c b/drivers/platform/x86/p2sb.c index 31f38309b389..d51eb0db0626 100644 --- a/drivers/platform/x86/p2sb.c +++ b/drivers/platform/x86/p2sb.c @@ -25,6 +25,7 @@ static const struct x86_cpu_id p2sb_cpu_ids[] = { X86_MATCH_VFM(INTEL_ATOM_GOLDMONT, P2SB_DEVFN_GOLDMONT), + X86_MATCH_VFM(INTEL_ATOM_GOLDMONT_PLUS, P2SB_DEVFN_GOLDMONT), {} }; diff --git a/drivers/platform/x86/panasonic-laptop.c b/drivers/platform/x86/panasonic-laptop.c index 2bf94d0ab324..22ca70eb8227 100644 --- a/drivers/platform/x86/panasonic-laptop.c +++ b/drivers/platform/x86/panasonic-laptop.c @@ -614,8 +614,7 @@ static ssize_t eco_mode_show(struct device *dev, struct device_attribute *attr, result = 1; break; default: - result = -EIO; - break; + return -EIO; } return sysfs_emit(buf, "%u\n", result); } @@ -761,7 +760,12 @@ static ssize_t current_brightness_store(struct device *dev, struct device_attrib static ssize_t cdpower_show(struct device *dev, struct device_attribute *attr, char *buf) { - return sysfs_emit(buf, "%d\n", get_optd_power_state()); + int state = get_optd_power_state(); + + if (state < 0) + return state; + + return sysfs_emit(buf, "%d\n", state); } static ssize_t cdpower_store(struct device *dev, struct device_attribute *attr, diff --git a/drivers/platform/x86/samsung-q10.c b/drivers/platform/x86/samsung-q10.c index 134e2c3d91ca..8160d45f8a23 100644 --- a/drivers/platform/x86/samsung-q10.c +++ b/drivers/platform/x86/samsung-q10.c @@ -78,7 +78,7 @@ static struct platform_driver samsungq10_driver = { .name = KBUILD_MODNAME, }, .probe = samsungq10_probe, - .remove_new = samsungq10_remove, + .remove = samsungq10_remove, }; static struct platform_device *samsungq10_device; diff --git a/drivers/platform/x86/sel3350-platform.c b/drivers/platform/x86/sel3350-platform.c index d09e976e7148..02e2081e2333 100644 --- a/drivers/platform/x86/sel3350-platform.c +++ b/drivers/platform/x86/sel3350-platform.c @@ -235,7 +235,7 @@ MODULE_DEVICE_TABLE(acpi, sel3350_device_ids); static struct platform_driver sel3350_platform_driver = { .probe = sel3350_probe, - .remove_new = sel3350_remove, + .remove = sel3350_remove, .driver = { .name = "sel3350-platform", .acpi_match_table = sel3350_device_ids, diff --git a/drivers/platform/x86/serial-multi-instantiate.c b/drivers/platform/x86/serial-multi-instantiate.c index 7c04cc9e5891..ed6b28505cd6 100644 --- a/drivers/platform/x86/serial-multi-instantiate.c +++ b/drivers/platform/x86/serial-multi-instantiate.c @@ -409,7 +409,7 @@ static struct platform_driver smi_driver = { .acpi_match_table = smi_acpi_ids, }, .probe = smi_probe, - .remove_new = smi_remove, + .remove = smi_remove, }; module_platform_driver(smi_driver); diff --git a/drivers/platform/x86/siemens/simatic-ipc-batt-apollolake.c b/drivers/platform/x86/siemens/simatic-ipc-batt-apollolake.c index 5edc294de6e4..6ff6f3de492b 100644 --- a/drivers/platform/x86/siemens/simatic-ipc-batt-apollolake.c +++ b/drivers/platform/x86/siemens/simatic-ipc-batt-apollolake.c @@ -37,7 +37,7 @@ static int simatic_ipc_batt_apollolake_probe(struct platform_device *pdev) static struct platform_driver simatic_ipc_batt_driver = { .probe = simatic_ipc_batt_apollolake_probe, - .remove_new = simatic_ipc_batt_apollolake_remove, + .remove = simatic_ipc_batt_apollolake_remove, .driver = { .name = KBUILD_MODNAME, }, diff --git a/drivers/platform/x86/siemens/simatic-ipc-batt-elkhartlake.c b/drivers/platform/x86/siemens/simatic-ipc-batt-elkhartlake.c index e6a56d14b505..83f532498c8c 100644 --- a/drivers/platform/x86/siemens/simatic-ipc-batt-elkhartlake.c +++ b/drivers/platform/x86/siemens/simatic-ipc-batt-elkhartlake.c @@ -37,7 +37,7 @@ static int simatic_ipc_batt_elkhartlake_probe(struct platform_device *pdev) static struct platform_driver simatic_ipc_batt_driver = { .probe = simatic_ipc_batt_elkhartlake_probe, - .remove_new = simatic_ipc_batt_elkhartlake_remove, + .remove = simatic_ipc_batt_elkhartlake_remove, .driver = { .name = KBUILD_MODNAME, }, diff --git a/drivers/platform/x86/siemens/simatic-ipc-batt-f7188x.c b/drivers/platform/x86/siemens/simatic-ipc-batt-f7188x.c index f8849d0e48a8..c6a79338f1d0 100644 --- a/drivers/platform/x86/siemens/simatic-ipc-batt-f7188x.c +++ b/drivers/platform/x86/siemens/simatic-ipc-batt-f7188x.c @@ -73,7 +73,7 @@ static int simatic_ipc_batt_f7188x_probe(struct platform_device *pdev) static struct platform_driver simatic_ipc_batt_driver = { .probe = simatic_ipc_batt_f7188x_probe, - .remove_new = simatic_ipc_batt_f7188x_remove, + .remove = simatic_ipc_batt_f7188x_remove, .driver = { .name = KBUILD_MODNAME, }, diff --git a/drivers/platform/x86/siemens/simatic-ipc-batt.c b/drivers/platform/x86/siemens/simatic-ipc-batt.c index d9aff10608cf..7cfe991cba00 100644 --- a/drivers/platform/x86/siemens/simatic-ipc-batt.c +++ b/drivers/platform/x86/siemens/simatic-ipc-batt.c @@ -239,7 +239,7 @@ static int simatic_ipc_batt_io_probe(struct platform_device *pdev) static struct platform_driver simatic_ipc_batt_driver = { .probe = simatic_ipc_batt_io_probe, - .remove_new = simatic_ipc_batt_io_remove, + .remove = simatic_ipc_batt_io_remove, .driver = { .name = KBUILD_MODNAME, }, diff --git a/drivers/platform/x86/think-lmi.c b/drivers/platform/x86/think-lmi.c index 4cfb53206cb8..38de0cb20d77 100644 --- a/drivers/platform/x86/think-lmi.c +++ b/drivers/platform/x86/think-lmi.c @@ -12,6 +12,7 @@ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt #include <linux/acpi.h> +#include <linux/array_size.h> #include <linux/errno.h> #include <linux/fs.h> #include <linux/mutex.h> @@ -169,11 +170,12 @@ MODULE_PARM_DESC(debug_support, "Enable debug command support"); */ #define LENOVO_CERT_THUMBPRINT_GUID "C59119ED-1C0D-4806-A8E9-59AA318176C4" -#define TLMI_POP_PWD BIT(0) /* Supervisor */ -#define TLMI_PAP_PWD BIT(1) /* Power-on */ -#define TLMI_HDD_PWD BIT(2) /* HDD/NVME */ -#define TLMI_SMP_PWD BIT(6) /* System Management */ -#define TLMI_CERT BIT(7) /* Certificate Based */ +#define TLMI_POP_PWD BIT(0) /* Supervisor */ +#define TLMI_PAP_PWD BIT(1) /* Power-on */ +#define TLMI_HDD_PWD BIT(2) /* HDD/NVME */ +#define TLMI_SMP_PWD BIT(6) /* System Management */ +#define TLMI_CERT_SVC BIT(7) /* Admin Certificate Based */ +#define TLMI_CERT_SMC BIT(8) /* System Certificate Based */ static const struct tlmi_err_codes tlmi_errs[] = { {"Success", 0}, @@ -391,7 +393,7 @@ static ssize_t is_enabled_show(struct kobject *kobj, struct kobj_attribute *attr { struct tlmi_pwd_setting *setting = to_tlmi_pwd_setting(kobj); - return sysfs_emit(buf, "%d\n", setting->valid); + return sysfs_emit(buf, "%d\n", setting->pwd_enabled || setting->cert_installed); } static struct kobj_attribute auth_is_pass_set = __ATTR_RO(is_enabled); @@ -469,7 +471,12 @@ static ssize_t new_password_store(struct kobject *kobj, if (ret) goto out; - if (tlmi_priv.pwd_admin->valid) { + /* + * Note admin password is not always required if SMPControl enabled in BIOS, + * So only set if it's configured. + * Let BIOS figure it out - we'll get an error if operation is not permitted + */ + if (tlmi_priv.pwd_admin->pwd_enabled && strlen(tlmi_priv.pwd_admin->password)) { ret = tlmi_opcode_setting("WmiOpcodePasswordAdmin", tlmi_priv.pwd_admin->password); if (ret) @@ -524,6 +531,10 @@ static struct kobj_attribute auth_max_pass_length = __ATTR_RO(max_password_lengt static ssize_t mechanism_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf) { + struct tlmi_pwd_setting *setting = to_tlmi_pwd_setting(kobj); + + if (setting->cert_installed) + return sysfs_emit(buf, "certificate\n"); return sysfs_emit(buf, "password\n"); } static struct kobj_attribute auth_mechanism = __ATTR_RO(mechanism); @@ -644,6 +655,17 @@ static ssize_t level_store(struct kobject *kobj, static struct kobj_attribute auth_level = __ATTR_RW(level); +static char *cert_command(struct tlmi_pwd_setting *setting, const char *arg1, const char *arg2) +{ + /* Prepend with SVC or SMC if multicert supported */ + if (tlmi_priv.pwdcfg.core.password_mode >= TLMI_PWDCFG_MODE_MULTICERT) + return kasprintf(GFP_KERNEL, "%s,%s,%s", + setting == tlmi_priv.pwd_admin ? "SVC" : "SMC", + arg1, arg2); + else + return kasprintf(GFP_KERNEL, "%s,%s", arg1, arg2); +} + static ssize_t cert_thumbprint(char *buf, const char *arg, int count) { const struct acpi_buffer input = { strlen(arg), (char *)arg }; @@ -669,18 +691,35 @@ static ssize_t cert_thumbprint(char *buf, const char *arg, int count) return count; } +static char *thumbtypes[] = {"Md5", "Sha1", "Sha256"}; + static ssize_t certificate_thumbprint_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf) { struct tlmi_pwd_setting *setting = to_tlmi_pwd_setting(kobj); + unsigned int i; int count = 0; + char *wmistr; if (!tlmi_priv.certificate_support || !setting->cert_installed) return -EOPNOTSUPP; - count += cert_thumbprint(buf, "Md5", count); - count += cert_thumbprint(buf, "Sha1", count); - count += cert_thumbprint(buf, "Sha256", count); + for (i = 0; i < ARRAY_SIZE(thumbtypes); i++) { + if (tlmi_priv.pwdcfg.core.password_mode >= TLMI_PWDCFG_MODE_MULTICERT) { + /* Format: 'SVC | SMC, Thumbtype' */ + wmistr = kasprintf(GFP_KERNEL, "%s,%s", + setting == tlmi_priv.pwd_admin ? "SVC" : "SMC", + thumbtypes[i]); + } else { + /* Format: 'Thumbtype' */ + wmistr = kasprintf(GFP_KERNEL, "%s", thumbtypes[i]); + } + if (!wmistr) + return -ENOMEM; + count += cert_thumbprint(buf, wmistr, count); + kfree(wmistr); + } + return count; } @@ -712,7 +751,7 @@ static ssize_t cert_to_password_store(struct kobject *kobj, return -ENOMEM; /* Format: 'Password,Signature' */ - auth_str = kasprintf(GFP_KERNEL, "%s,%s", passwd, setting->signature); + auth_str = cert_command(setting, passwd, setting->signature); if (!auth_str) { kfree_sensitive(passwd); return -ENOMEM; @@ -726,12 +765,19 @@ static ssize_t cert_to_password_store(struct kobject *kobj, static struct kobj_attribute auth_cert_to_password = __ATTR_WO(cert_to_password); +enum cert_install_mode { + TLMI_CERT_INSTALL, + TLMI_CERT_UPDATE, +}; + static ssize_t certificate_store(struct kobject *kobj, struct kobj_attribute *attr, const char *buf, size_t count) { struct tlmi_pwd_setting *setting = to_tlmi_pwd_setting(kobj); + enum cert_install_mode install_mode = TLMI_CERT_INSTALL; char *auth_str, *new_cert; + char *signature; char *guid; int ret; @@ -748,9 +794,9 @@ static ssize_t certificate_store(struct kobject *kobj, return -EACCES; /* Format: 'serial#, signature' */ - auth_str = kasprintf(GFP_KERNEL, "%s,%s", - dmi_get_system_info(DMI_PRODUCT_SERIAL), - setting->signature); + auth_str = cert_command(setting, + dmi_get_system_info(DMI_PRODUCT_SERIAL), + setting->signature); if (!auth_str) return -ENOMEM; @@ -767,24 +813,44 @@ static ssize_t certificate_store(struct kobject *kobj, if (setting->cert_installed) { /* Certificate is installed so this is an update */ - if (!setting->signature || !setting->signature[0]) { + install_mode = TLMI_CERT_UPDATE; + /* If admin account enabled - need to use its signature */ + if (tlmi_priv.pwd_admin->pwd_enabled) + signature = tlmi_priv.pwd_admin->signature; + else + signature = setting->signature; + } else { /* Cert install */ + /* Check if SMC and SVC already installed */ + if ((setting == tlmi_priv.pwd_system) && tlmi_priv.pwd_admin->cert_installed) { + /* This gets treated as a cert update */ + install_mode = TLMI_CERT_UPDATE; + signature = tlmi_priv.pwd_admin->signature; + } else { /* Regular cert install */ + install_mode = TLMI_CERT_INSTALL; + signature = setting->signature; + } + } + + if (install_mode == TLMI_CERT_UPDATE) { + /* This is a certificate update */ + if (!signature || !signature[0]) { kfree(new_cert); return -EACCES; } guid = LENOVO_UPDATE_BIOS_CERT_GUID; /* Format: 'Certificate,Signature' */ - auth_str = kasprintf(GFP_KERNEL, "%s,%s", - new_cert, setting->signature); + auth_str = cert_command(setting, new_cert, signature); } else { /* This is a fresh install */ - if (!setting->valid || !setting->password[0]) { + /* To set admin cert, a password must be enabled */ + if ((setting == tlmi_priv.pwd_admin) && + (!setting->pwd_enabled || !setting->password[0])) { kfree(new_cert); return -EACCES; } guid = LENOVO_SET_BIOS_CERT_GUID; - /* Format: 'Certificate,Admin-password' */ - auth_str = kasprintf(GFP_KERNEL, "%s,%s", - new_cert, setting->password); + /* Format: 'Certificate, password' */ + auth_str = cert_command(setting, new_cert, setting->password); } kfree(new_cert); if (!auth_str) @@ -864,14 +930,19 @@ static umode_t auth_attr_is_visible(struct kobject *kobj, return 0; } - /* We only display certificates on Admin account, if supported */ + /* We only display certificates, if supported */ if (attr == &auth_certificate.attr || attr == &auth_signature.attr || attr == &auth_save_signature.attr || attr == &auth_cert_thumb.attr || attr == &auth_cert_to_password.attr) { - if ((setting == tlmi_priv.pwd_admin) && tlmi_priv.certificate_support) - return attr->mode; + if (tlmi_priv.certificate_support) { + if (setting == tlmi_priv.pwd_admin) + return attr->mode; + if ((tlmi_priv.pwdcfg.core.password_mode >= TLMI_PWDCFG_MODE_MULTICERT) && + (setting == tlmi_priv.pwd_system)) + return attr->mode; + } return 0; } @@ -1019,7 +1090,7 @@ static ssize_t current_value_store(struct kobject *kobj, * Workstation's require the opcode to be set before changing the * attribute. */ - if (tlmi_priv.pwd_admin->valid && tlmi_priv.pwd_admin->password[0]) { + if (tlmi_priv.pwd_admin->pwd_enabled && tlmi_priv.pwd_admin->password[0]) { ret = tlmi_opcode_setting("WmiOpcodePasswordAdmin", tlmi_priv.pwd_admin->password); if (ret) @@ -1042,7 +1113,7 @@ static ssize_t current_value_store(struct kobject *kobj, else ret = tlmi_save_bios_settings(""); } else { /* old non-opcode based authentication method (deprecated) */ - if (tlmi_priv.pwd_admin->valid && tlmi_priv.pwd_admin->password[0]) { + if (tlmi_priv.pwd_admin->pwd_enabled && tlmi_priv.pwd_admin->password[0]) { auth_str = kasprintf(GFP_KERNEL, "%s,%s,%s;", tlmi_priv.pwd_admin->password, encoding_options[tlmi_priv.pwd_admin->encoding], @@ -1215,7 +1286,7 @@ static ssize_t save_settings_store(struct kobject *kobj, struct kobj_attribute * if (ret) goto out; } else if (tlmi_priv.opcode_support) { - if (tlmi_priv.pwd_admin->valid && tlmi_priv.pwd_admin->password[0]) { + if (tlmi_priv.pwd_admin->pwd_enabled && tlmi_priv.pwd_admin->password[0]) { ret = tlmi_opcode_setting("WmiOpcodePasswordAdmin", tlmi_priv.pwd_admin->password); if (ret) @@ -1223,7 +1294,7 @@ static ssize_t save_settings_store(struct kobject *kobj, struct kobj_attribute * } ret = tlmi_save_bios_settings(""); } else { /* old non-opcode based authentication method (deprecated) */ - if (tlmi_priv.pwd_admin->valid && tlmi_priv.pwd_admin->password[0]) { + if (tlmi_priv.pwd_admin->pwd_enabled && tlmi_priv.pwd_admin->password[0]) { auth_str = kasprintf(GFP_KERNEL, "%s,%s,%s;", tlmi_priv.pwd_admin->password, encoding_options[tlmi_priv.pwd_admin->encoding], @@ -1273,7 +1344,7 @@ static ssize_t debug_cmd_store(struct kobject *kobj, struct kobj_attribute *attr if (!new_setting) return -ENOMEM; - if (tlmi_priv.pwd_admin->valid && tlmi_priv.pwd_admin->password[0]) { + if (tlmi_priv.pwd_admin->pwd_enabled && tlmi_priv.pwd_admin->password[0]) { auth_str = kasprintf(GFP_KERNEL, "%s,%s,%s;", tlmi_priv.pwd_admin->password, encoding_options[tlmi_priv.pwd_admin->encoding], @@ -1637,14 +1708,14 @@ static int tlmi_analyze(void) goto fail_clear_attr; if (tlmi_priv.pwdcfg.core.password_state & TLMI_PAP_PWD) - tlmi_priv.pwd_admin->valid = true; + tlmi_priv.pwd_admin->pwd_enabled = true; tlmi_priv.pwd_power = tlmi_create_auth("pop", "power-on"); if (!tlmi_priv.pwd_power) goto fail_clear_attr; if (tlmi_priv.pwdcfg.core.password_state & TLMI_POP_PWD) - tlmi_priv.pwd_power->valid = true; + tlmi_priv.pwd_power->pwd_enabled = true; if (tlmi_priv.opcode_support) { tlmi_priv.pwd_system = tlmi_create_auth("smp", "system"); @@ -1652,7 +1723,7 @@ static int tlmi_analyze(void) goto fail_clear_attr; if (tlmi_priv.pwdcfg.core.password_state & TLMI_SMP_PWD) - tlmi_priv.pwd_system->valid = true; + tlmi_priv.pwd_system->pwd_enabled = true; tlmi_priv.pwd_hdd = tlmi_create_auth("hdd", "hdd"); if (!tlmi_priv.pwd_hdd) @@ -1670,7 +1741,7 @@ static int tlmi_analyze(void) /* Check if PWD is configured and set index to first drive found */ if (tlmi_priv.pwdcfg.ext.hdd_user_password || tlmi_priv.pwdcfg.ext.hdd_master_password) { - tlmi_priv.pwd_hdd->valid = true; + tlmi_priv.pwd_hdd->pwd_enabled = true; if (tlmi_priv.pwdcfg.ext.hdd_master_password) tlmi_priv.pwd_hdd->index = ffs(tlmi_priv.pwdcfg.ext.hdd_master_password) - 1; @@ -1680,7 +1751,7 @@ static int tlmi_analyze(void) } if (tlmi_priv.pwdcfg.ext.nvme_user_password || tlmi_priv.pwdcfg.ext.nvme_master_password) { - tlmi_priv.pwd_nvme->valid = true; + tlmi_priv.pwd_nvme->pwd_enabled = true; if (tlmi_priv.pwdcfg.ext.nvme_master_password) tlmi_priv.pwd_nvme->index = ffs(tlmi_priv.pwdcfg.ext.nvme_master_password) - 1; @@ -1691,10 +1762,12 @@ static int tlmi_analyze(void) } } - if (tlmi_priv.certificate_support && - (tlmi_priv.pwdcfg.core.password_state & TLMI_CERT)) - tlmi_priv.pwd_admin->cert_installed = true; - + if (tlmi_priv.certificate_support) { + tlmi_priv.pwd_admin->cert_installed = + tlmi_priv.pwdcfg.core.password_state & TLMI_CERT_SVC; + tlmi_priv.pwd_system->cert_installed = + tlmi_priv.pwdcfg.core.password_state & TLMI_CERT_SMC; + } return 0; fail_clear_attr: diff --git a/drivers/platform/x86/think-lmi.h b/drivers/platform/x86/think-lmi.h index e1975ffebeb4..f267d8b46957 100644 --- a/drivers/platform/x86/think-lmi.h +++ b/drivers/platform/x86/think-lmi.h @@ -41,6 +41,10 @@ enum save_mode { }; /* password configuration details */ +#define TLMI_PWDCFG_MODE_LEGACY 0 +#define TLMI_PWDCFG_MODE_PASSWORD 1 +#define TLMI_PWDCFG_MODE_MULTICERT 3 + struct tlmi_pwdcfg_core { uint32_t password_mode; uint32_t password_state; @@ -65,7 +69,7 @@ struct tlmi_pwdcfg { /* password setting details */ struct tlmi_pwd_setting { struct kobject kobj; - bool valid; + bool pwd_enabled; char password[TLMI_PWD_BUFSIZE]; const char *pwd_type; const char *role; diff --git a/drivers/platform/x86/wmi.c b/drivers/platform/x86/wmi.c index 3cbe180c3fc0..646370bd6b03 100644 --- a/drivers/platform/x86/wmi.c +++ b/drivers/platform/x86/wmi.c @@ -22,7 +22,6 @@ #include <linux/device.h> #include <linux/init.h> #include <linux/kernel.h> -#include <linux/list.h> #include <linux/module.h> #include <linux/platform_device.h> #include <linux/rwsem.h> @@ -37,8 +36,6 @@ MODULE_AUTHOR("Carlos Corbacho"); MODULE_DESCRIPTION("ACPI-WMI Mapping Driver"); MODULE_LICENSE("GPL"); -static LIST_HEAD(wmi_block_list); - struct guid_block { guid_t guid; union { @@ -63,7 +60,6 @@ enum { /* wmi_block flags */ struct wmi_block { struct wmi_device dev; - struct list_head list; struct guid_block gblock; struct acpi_device *acpi_device; struct rw_semaphore notify_lock; /* Protects notify callback add/remove */ @@ -73,6 +69,10 @@ struct wmi_block { unsigned long flags; }; +struct wmi_guid_count_context { + const guid_t *guid; + int count; +}; /* * If the GUID data block is marked as expensive, we must enable and @@ -91,7 +91,6 @@ static const struct acpi_device_id wmi_device_ids[] = { MODULE_DEVICE_TABLE(acpi, wmi_device_ids); #define dev_to_wblock(__dev) container_of_const(__dev, struct wmi_block, dev.dev) -#define dev_to_wdev(__dev) container_of_const(__dev, struct wmi_device, dev) /* * GUID parsing functions @@ -199,7 +198,7 @@ static struct wmi_device *wmi_find_device_by_guid(const char *guid_string) if (!dev) return ERR_PTR(-ENODEV); - return dev_to_wdev(dev); + return to_wmi_device(dev); } static void wmi_device_put(struct wmi_device *wdev) @@ -654,8 +653,6 @@ char *wmi_get_acpi_device_uid(const char *guid_string) } EXPORT_SYMBOL_GPL(wmi_get_acpi_device_uid); -#define drv_to_wdrv(__drv) container_of_const(__drv, struct wmi_driver, driver) - /* * sysfs interface */ @@ -761,7 +758,7 @@ static DEVICE_ATTR_RO(object_id); static ssize_t setable_show(struct device *dev, struct device_attribute *attr, char *buf) { - struct wmi_device *wdev = dev_to_wdev(dev); + struct wmi_device *wdev = to_wmi_device(dev); return sysfs_emit(buf, "%d\n", (int)wdev->setable); } @@ -803,7 +800,7 @@ static void wmi_dev_release(struct device *dev) static int wmi_dev_match(struct device *dev, const struct device_driver *driver) { - const struct wmi_driver *wmi_driver = drv_to_wdrv(driver); + const struct wmi_driver *wmi_driver = to_wmi_driver(driver); struct wmi_block *wblock = dev_to_wblock(dev); const struct wmi_device_id *id = wmi_driver->id_table; @@ -827,7 +824,7 @@ static int wmi_dev_match(struct device *dev, const struct device_driver *driver) static int wmi_dev_probe(struct device *dev) { struct wmi_block *wblock = dev_to_wblock(dev); - struct wmi_driver *wdriver = drv_to_wdrv(dev->driver); + struct wmi_driver *wdriver = to_wmi_driver(dev->driver); int ret = 0; /* Some older WMI drivers will break if instantiated multiple times, @@ -851,7 +848,7 @@ static int wmi_dev_probe(struct device *dev) dev_warn(dev, "failed to enable device -- probing anyway\n"); if (wdriver->probe) { - ret = wdriver->probe(dev_to_wdev(dev), + ret = wdriver->probe(to_wmi_device(dev), find_guid_context(wblock, wdriver)); if (ret) { if (ACPI_FAILURE(wmi_method_enable(wblock, false))) @@ -871,19 +868,45 @@ static int wmi_dev_probe(struct device *dev) static void wmi_dev_remove(struct device *dev) { struct wmi_block *wblock = dev_to_wblock(dev); - struct wmi_driver *wdriver = drv_to_wdrv(dev->driver); + struct wmi_driver *wdriver = to_wmi_driver(dev->driver); down_write(&wblock->notify_lock); wblock->driver_ready = false; up_write(&wblock->notify_lock); if (wdriver->remove) - wdriver->remove(dev_to_wdev(dev)); + wdriver->remove(to_wmi_device(dev)); if (ACPI_FAILURE(wmi_method_enable(wblock, false))) dev_warn(dev, "failed to disable device\n"); } +static void wmi_dev_shutdown(struct device *dev) +{ + struct wmi_driver *wdriver; + struct wmi_block *wblock; + + if (dev->driver) { + wdriver = to_wmi_driver(dev->driver); + wblock = dev_to_wblock(dev); + + /* + * Some machines return bogus WMI event data when disabling + * the WMI event. Because of this we must prevent the associated + * WMI driver from receiving new WMI events before disabling it. + */ + down_write(&wblock->notify_lock); + wblock->driver_ready = false; + up_write(&wblock->notify_lock); + + if (wdriver->shutdown) + wdriver->shutdown(to_wmi_device(dev)); + + if (ACPI_FAILURE(wmi_method_enable(wblock, false))) + dev_warn(dev, "Failed to disable device\n"); + } +} + static struct class wmi_bus_class = { .name = "wmi_bus", }; @@ -895,6 +918,7 @@ static const struct bus_type wmi_bus_type = { .uevent = wmi_dev_uevent, .probe = wmi_dev_probe, .remove = wmi_dev_remove, + .shutdown = wmi_dev_shutdown, }; static const struct device_type wmi_type_event = { @@ -915,21 +939,30 @@ static const struct device_type wmi_type_data = { .release = wmi_dev_release, }; -/* - * _WDG is a static list that is only parsed at startup, - * so it's safe to count entries without extra protection. - */ +static int wmi_count_guids(struct device *dev, void *data) +{ + struct wmi_guid_count_context *context = data; + struct wmi_block *wblock = dev_to_wblock(dev); + + if (guid_equal(&wblock->gblock.guid, context->guid)) + context->count++; + + return 0; +} + static int guid_count(const guid_t *guid) { - struct wmi_block *wblock; - int count = 0; + struct wmi_guid_count_context context = { + .guid = guid, + .count = 0, + }; + int ret; - list_for_each_entry(wblock, &wmi_block_list, list) { - if (guid_equal(&wblock->gblock.guid, guid)) - count++; - } + ret = bus_for_each_dev(&wmi_bus_type, NULL, &context, wmi_count_guids); + if (ret < 0) + return ret; - return count; + return context.count; } static int wmi_create_device(struct device *wmi_bus_dev, @@ -940,7 +973,7 @@ static int wmi_create_device(struct device *wmi_bus_dev, struct acpi_device_info *info; acpi_handle method_handle; acpi_status status; - uint count; + int count; if (wblock->gblock.flags & ACPI_WMI_EVENT) { wblock->dev.dev.type = &wmi_type_event; @@ -1008,6 +1041,9 @@ static int wmi_create_device(struct device *wmi_bus_dev, wblock->dev.dev.parent = wmi_bus_dev; count = guid_count(&wblock->gblock.guid); + if (count < 0) + return count; + if (count) { dev_set_name(&wblock->dev.dev, "%pUL-%d", &wblock->gblock.guid, count); set_bit(WMI_GUID_DUPLICATED, &wblock->flags); @@ -1093,14 +1129,11 @@ static int parse_wdg(struct device *wmi_bus_dev, struct platform_device *pdev) continue; } - list_add_tail(&wblock->list, &wmi_block_list); - retval = wmi_add_device(pdev, &wblock->dev); if (retval) { dev_err(wmi_bus_dev, "failed to register %pUL\n", &wblock->gblock.guid); - list_del(&wblock->list); put_device(&wblock->dev.dev); } } @@ -1138,7 +1171,7 @@ static int wmi_get_notify_data(struct wmi_block *wblock, union acpi_object **obj static void wmi_notify_driver(struct wmi_block *wblock, union acpi_object *obj) { - struct wmi_driver *driver = drv_to_wdrv(wblock->dev.dev.driver); + struct wmi_driver *driver = to_wmi_driver(wblock->dev.dev.driver); if (!obj && !driver->no_notify_data) { dev_warn(&wblock->dev.dev, "Event contains no event data\n"); @@ -1200,9 +1233,6 @@ static void acpi_wmi_notify_handler(acpi_handle handle, u32 event, void *context static int wmi_remove_device(struct device *dev, void *data) { - struct wmi_block *wblock = dev_to_wblock(dev); - - list_del(&wblock->list); device_unregister(dev); return 0; @@ -1301,7 +1331,7 @@ static struct platform_driver acpi_wmi_driver = { .acpi_match_table = wmi_device_ids, }, .probe = acpi_wmi_probe, - .remove_new = acpi_wmi_remove, + .remove = acpi_wmi_remove, }; static int __init acpi_wmi_init(void) diff --git a/drivers/platform/x86/x86-android-tablets/Kconfig b/drivers/platform/x86/x86-android-tablets/Kconfig index 88d9e8f2ff24..a67bddc43007 100644 --- a/drivers/platform/x86/x86-android-tablets/Kconfig +++ b/drivers/platform/x86/x86-android-tablets/Kconfig @@ -5,7 +5,9 @@ config X86_ANDROID_TABLETS tristate "X86 Android tablet support" - depends on I2C && SPI && SERIAL_DEV_BUS && ACPI && EFI && GPIOLIB && PMIC_OPREGION + depends on I2C && SPI && SERIAL_DEV_BUS + depends on GPIOLIB && PMIC_OPREGION + depends on ACPI && EFI && PCI select NEW_LEDS select LEDS_CLASS help diff --git a/drivers/platform/x86/x86-android-tablets/core.c b/drivers/platform/x86/x86-android-tablets/core.c index ef572b90e06b..4218afcec0e9 100644 --- a/drivers/platform/x86/x86-android-tablets/core.c +++ b/drivers/platform/x86/x86-android-tablets/core.c @@ -11,11 +11,13 @@ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt #include <linux/acpi.h> +#include <linux/device.h> #include <linux/dmi.h> #include <linux/gpio/consumer.h> #include <linux/gpio/machine.h> #include <linux/irq.h> #include <linux/module.h> +#include <linux/pci.h> #include <linux/platform_device.h> #include <linux/serdev.h> #include <linux/string.h> @@ -155,26 +157,66 @@ static struct gpiod_lookup_table * const *gpiod_lookup_tables; static const struct software_node *bat_swnode; static void (*exit_handler)(void); +static struct i2c_adapter * +get_i2c_adap_by_handle(const struct x86_i2c_client_info *client_info) +{ + acpi_handle handle; + acpi_status status; + + status = acpi_get_handle(NULL, client_info->adapter_path, &handle); + if (ACPI_FAILURE(status)) { + pr_err("Error could not get %s handle\n", client_info->adapter_path); + return NULL; + } + + return i2c_acpi_find_adapter_by_handle(handle); +} + +static __init int match_parent(struct device *dev, const void *data) +{ + return dev->parent == data; +} + +static struct i2c_adapter * +get_i2c_adap_by_pci_parent(const struct x86_i2c_client_info *client_info) +{ + struct i2c_adapter *adap = NULL; + struct device *pdev, *adap_dev; + + pdev = bus_find_device_by_name(&pci_bus_type, NULL, client_info->adapter_path); + if (!pdev) { + pr_err("Error could not find %s PCI device\n", client_info->adapter_path); + return NULL; + } + + adap_dev = bus_find_device(&i2c_bus_type, NULL, pdev, match_parent); + if (adap_dev) { + adap = i2c_verify_adapter(adap_dev); + if (!adap) + put_device(adap_dev); + } + + put_device(pdev); + + return adap; +} + static __init int x86_instantiate_i2c_client(const struct x86_dev_info *dev_info, int idx) { const struct x86_i2c_client_info *client_info = &dev_info->i2c_client_info[idx]; struct i2c_board_info board_info = client_info->board_info; struct i2c_adapter *adap; - acpi_handle handle; - acpi_status status; board_info.irq = x86_acpi_irq_helper_get(&client_info->irq_data); if (board_info.irq < 0) return board_info.irq; - status = acpi_get_handle(NULL, client_info->adapter_path, &handle); - if (ACPI_FAILURE(status)) { - pr_err("Error could not get %s handle\n", client_info->adapter_path); - return -ENODEV; - } + if (dev_info->use_pci_devname) + adap = get_i2c_adap_by_pci_parent(client_info); + else + adap = get_i2c_adap_by_handle(client_info); - adap = i2c_acpi_find_adapter_by_handle(handle); if (!adap) { pr_err("error could not get %s adapter\n", client_info->adapter_path); return -ENODEV; @@ -458,7 +500,7 @@ static struct platform_driver x86_android_tablet_driver = { .driver = { .name = KBUILD_MODNAME, }, - .remove_new = x86_android_tablet_remove, + .remove = x86_android_tablet_remove, }; static int __init x86_android_tablet_init(void) diff --git a/drivers/platform/x86/x86-android-tablets/dmi.c b/drivers/platform/x86/x86-android-tablets/dmi.c index 17f6da96aa01..3e5fa3b6e2fd 100644 --- a/drivers/platform/x86/x86-android-tablets/dmi.c +++ b/drivers/platform/x86/x86-android-tablets/dmi.c @@ -180,6 +180,16 @@ const struct dmi_system_id x86_android_tablet_ids[] __initconst = { .driver_data = (void *)&peaq_c1010_info, }, { + /* Vexia Edu Atla 10 tablet 9V version */ + .matches = { + DMI_MATCH(DMI_BOARD_VENDOR, "AMI Corporation"), + DMI_MATCH(DMI_BOARD_NAME, "Aptio CRB"), + /* Above strings are too generic, also match on BIOS date */ + DMI_MATCH(DMI_BIOS_DATE, "08/25/2014"), + }, + .driver_data = (void *)&vexia_edu_atla10_info, + }, + { /* Whitelabel (sold as various brands) TM800A550L */ .matches = { DMI_MATCH(DMI_BOARD_VENDOR, "AMI Corporation"), diff --git a/drivers/platform/x86/x86-android-tablets/other.c b/drivers/platform/x86/x86-android-tablets/other.c index 7db8aa58b907..725948044da4 100644 --- a/drivers/platform/x86/x86-android-tablets/other.c +++ b/drivers/platform/x86/x86-android-tablets/other.c @@ -12,6 +12,7 @@ #include <linux/gpio/machine.h> #include <linux/input.h> #include <linux/leds.h> +#include <linux/pci.h> #include <linux/platform_device.h> #include <linux/pwm.h> @@ -598,6 +599,168 @@ const struct x86_dev_info whitelabel_tm800a550l_info __initconst = { }; /* + * Vexia EDU ATLA 10 tablet, Android 4.2 / 4.4 + Guadalinex Ubuntu tablet + * distributed to schools in the Spanish Andalucía region. + */ +const char * const crystal_cove_pwrsrc_psy[] = { "crystal_cove_pwrsrc" }; + +static const struct property_entry vexia_edu_atla10_ulpmc_props[] = { + PROPERTY_ENTRY_STRING_ARRAY("supplied-from", crystal_cove_pwrsrc_psy), + { } +}; + +const struct software_node vexia_edu_atla10_ulpmc_node = { + .properties = vexia_edu_atla10_ulpmc_props, +}; + +static const char * const vexia_edu_atla10_accel_mount_matrix[] = { + "0", "-1", "0", + "1", "0", "0", + "0", "0", "1" +}; + +static const struct property_entry vexia_edu_atla10_accel_props[] = { + PROPERTY_ENTRY_STRING_ARRAY("mount-matrix", vexia_edu_atla10_accel_mount_matrix), + { } +}; + +static const struct software_node vexia_edu_atla10_accel_node = { + .properties = vexia_edu_atla10_accel_props, +}; + +static const struct property_entry vexia_edu_atla10_touchscreen_props[] = { + PROPERTY_ENTRY_U32("hid-descr-addr", 0x0000), + PROPERTY_ENTRY_U32("post-reset-deassert-delay-ms", 120), + { } +}; + +static const struct software_node vexia_edu_atla10_touchscreen_node = { + .properties = vexia_edu_atla10_touchscreen_props, +}; + +static const struct property_entry vexia_edu_atla10_pmic_props[] = { + PROPERTY_ENTRY_BOOL("linux,register-pwrsrc-power_supply"), + { } +}; + +static const struct software_node vexia_edu_atla10_pmic_node = { + .properties = vexia_edu_atla10_pmic_props, +}; + +static const struct x86_i2c_client_info vexia_edu_atla10_i2c_clients[] __initconst = { + { + /* I2C attached embedded controller, used to access fuel-gauge */ + .board_info = { + .type = "vexia_atla10_ec", + .addr = 0x76, + .dev_name = "ulpmc", + .swnode = &vexia_edu_atla10_ulpmc_node, + }, + .adapter_path = "0000:00:18.1", + }, { + /* RT5642 audio codec */ + .board_info = { + .type = "rt5640", + .addr = 0x1c, + .dev_name = "rt5640", + }, + .adapter_path = "0000:00:18.2", + .irq_data = { + .type = X86_ACPI_IRQ_TYPE_GPIOINT, + .chip = "INT33FC:02", + .index = 4, + .trigger = ACPI_EDGE_SENSITIVE, + .polarity = ACPI_ACTIVE_HIGH, + .con_id = "rt5640_irq", + }, + }, { + /* kxtj21009 accelerometer */ + .board_info = { + .type = "kxtj21009", + .addr = 0x0f, + .dev_name = "kxtj21009", + .swnode = &vexia_edu_atla10_accel_node, + }, + .adapter_path = "0000:00:18.5", + }, { + /* FT5416DQ9 touchscreen controller */ + .board_info = { + .type = "hid-over-i2c", + .addr = 0x38, + .dev_name = "FTSC1000", + .swnode = &vexia_edu_atla10_touchscreen_node, + }, + .adapter_path = "0000:00:18.6", + .irq_data = { + .type = X86_ACPI_IRQ_TYPE_APIC, + .index = 0x45, + .trigger = ACPI_LEVEL_SENSITIVE, + .polarity = ACPI_ACTIVE_HIGH, + }, + }, { + /* Crystal Cove PMIC */ + .board_info = { + .type = "intel_soc_pmic_crc", + .addr = 0x6e, + .dev_name = "intel_soc_pmic_crc", + .swnode = &vexia_edu_atla10_pmic_node, + }, + .adapter_path = "0000:00:18.7", + .irq_data = { + .type = X86_ACPI_IRQ_TYPE_APIC, + .index = 0x43, + .trigger = ACPI_LEVEL_SENSITIVE, + .polarity = ACPI_ACTIVE_HIGH, + }, + } +}; + +static struct gpiod_lookup_table vexia_edu_atla10_ft5416_gpios = { + .dev_id = "i2c-FTSC1000", + .table = { + GPIO_LOOKUP("INT33FC:00", 60, "reset", GPIO_ACTIVE_LOW), + { } + }, +}; + +static struct gpiod_lookup_table * const vexia_edu_atla10_gpios[] = { + &vexia_edu_atla10_ft5416_gpios, + NULL +}; + +static int __init vexia_edu_atla10_init(struct device *dev) +{ + struct pci_dev *pdev; + int ret; + + /* Enable the Wifi module by setting the wifi_enable pin to 1 */ + ret = x86_android_tablet_get_gpiod("INT33FC:02", 20, "wifi_enable", + false, GPIOD_OUT_HIGH, NULL); + if (ret) + return ret; + + /* Reprobe the SDIO controller to enumerate the now enabled Wifi module */ + pdev = pci_get_domain_bus_and_slot(0, 0, PCI_DEVFN(0x11, 0)); + if (!pdev) + return -EPROBE_DEFER; + + ret = device_reprobe(&pdev->dev); + if (ret) + pci_warn(pdev, "Reprobing error: %d\n", ret); + + pci_dev_put(pdev); + return 0; +} + +const struct x86_dev_info vexia_edu_atla10_info __initconst = { + .i2c_client_info = vexia_edu_atla10_i2c_clients, + .i2c_client_count = ARRAY_SIZE(vexia_edu_atla10_i2c_clients), + .gpiod_lookup_tables = vexia_edu_atla10_gpios, + .init = vexia_edu_atla10_init, + .use_pci_devname = true, +}; + +/* * The firmware node for ktd2026 on Xaomi pad2. It composed of a RGB LED node * with three subnodes for each color (B/G/R). The RGB LED node is named * "multi-led" to align with the name in the device tree. diff --git a/drivers/platform/x86/x86-android-tablets/x86-android-tablets.h b/drivers/platform/x86/x86-android-tablets/x86-android-tablets.h index 5517e438c7b6..0fc7e8cff672 100644 --- a/drivers/platform/x86/x86-android-tablets/x86-android-tablets.h +++ b/drivers/platform/x86/x86-android-tablets/x86-android-tablets.h @@ -91,6 +91,7 @@ struct x86_dev_info { int gpio_button_count; int (*init)(struct device *dev); void (*exit)(void); + bool use_pci_devname; }; int x86_android_tablet_get_gpiod(const char *chip, int pin, const char *con_id, @@ -119,6 +120,7 @@ extern const struct x86_dev_info nextbook_ares8_info; extern const struct x86_dev_info nextbook_ares8a_info; extern const struct x86_dev_info peaq_c1010_info; extern const struct x86_dev_info whitelabel_tm800a550l_info; +extern const struct x86_dev_info vexia_edu_atla10_info; extern const struct x86_dev_info xiaomi_mipad2_info; extern const struct dmi_system_id x86_android_tablet_ids[]; diff --git a/drivers/platform/x86/xo1-rfkill.c b/drivers/platform/x86/xo1-rfkill.c index 5fe68296501c..5fedb99b9d94 100644 --- a/drivers/platform/x86/xo1-rfkill.c +++ b/drivers/platform/x86/xo1-rfkill.c @@ -68,7 +68,7 @@ static struct platform_driver xo1_rfkill_driver = { .name = "xo1-rfkill", }, .probe = xo1_rfkill_probe, - .remove_new = xo1_rfkill_remove, + .remove = xo1_rfkill_remove, }; module_platform_driver(xo1_rfkill_driver); |