summaryrefslogtreecommitdiff
path: root/drivers/platform/x86
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2024-11-20 14:07:55 -0800
committerLinus Torvalds <torvalds@linux-foundation.org>2024-11-20 14:07:55 -0800
commitfcb3ad4366b9c810cbb9da34c076a9a52d8aa1e0 (patch)
tree844b7b2bf7c8b5472aef01b55820e4de8b6abddb /drivers/platform/x86
parent70e8ef2d6762cecfc868296fa9e0a3848b926efc (diff)
parentc6a2b4fcec5f2d80b0183fae1117f06127584c28 (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')
-rw-r--r--drivers/platform/x86/acer-wmi.c7
-rw-r--r--drivers/platform/x86/adv_swbutton.c2
-rw-r--r--drivers/platform/x86/amd/Kconfig18
-rw-r--r--drivers/platform/x86/amd/Makefile5
-rw-r--r--drivers/platform/x86/amd/hsmp.c988
-rw-r--r--drivers/platform/x86/amd/hsmp/Kconfig47
-rw-r--r--drivers/platform/x86/amd/hsmp/Makefile12
-rw-r--r--drivers/platform/x86/amd/hsmp/acpi.c378
-rw-r--r--drivers/platform/x86/amd/hsmp/hsmp.c408
-rw-r--r--drivers/platform/x86/amd/hsmp/hsmp.h66
-rw-r--r--drivers/platform/x86/amd/hsmp/plat.c338
-rw-r--r--drivers/platform/x86/amd/pmc/pmc.c2
-rw-r--r--drivers/platform/x86/amd/pmf/Kconfig1
-rw-r--r--drivers/platform/x86/amd/pmf/acpi.c46
-rw-r--r--drivers/platform/x86/amd/pmf/core.c10
-rw-r--r--drivers/platform/x86/amd/pmf/pmf.h6
-rw-r--r--drivers/platform/x86/amd/pmf/tee-if.c8
-rw-r--r--drivers/platform/x86/amd/x3d_vcache.c176
-rw-r--r--drivers/platform/x86/amilo-rfkill.c6
-rw-r--r--drivers/platform/x86/asus-laptop.c4
-rw-r--r--drivers/platform/x86/asus-wmi.c90
-rw-r--r--drivers/platform/x86/barco-p50-gpio.c2
-rw-r--r--drivers/platform/x86/classmate-laptop.c7
-rw-r--r--drivers/platform/x86/compal-laptop.c61
-rw-r--r--drivers/platform/x86/dell/Kconfig1
-rw-r--r--drivers/platform/x86/dell/alienware-wmi.c577
-rw-r--r--drivers/platform/x86/dell/dcdbas.c13
-rw-r--r--drivers/platform/x86/dell/dell-smo8800.c2
-rw-r--r--drivers/platform/x86/dell/dell-uart-backlight.c2
-rw-r--r--drivers/platform/x86/eeepc-laptop.c9
-rw-r--r--drivers/platform/x86/hp/hp-bioscfg/passwdobj-attributes.c11
-rw-r--r--drivers/platform/x86/hp/hp-wmi.c2
-rw-r--r--drivers/platform/x86/hp/hp_accel.c2
-rw-r--r--drivers/platform/x86/hp/tc1100-wmi.c2
-rw-r--r--drivers/platform/x86/huawei-wmi.c2
-rw-r--r--drivers/platform/x86/ideapad-laptop.c2
-rw-r--r--drivers/platform/x86/intel/Makefile68
-rw-r--r--drivers/platform/x86/intel/bxtwc_tmu.c2
-rw-r--r--drivers/platform/x86/intel/bytcrc_pwrsrc.c2
-rw-r--r--drivers/platform/x86/intel/chtdc_ti_pwrbtn.c2
-rw-r--r--drivers/platform/x86/intel/chtwc_int33fe.c2
-rw-r--r--drivers/platform/x86/intel/hid.c9
-rw-r--r--drivers/platform/x86/intel/int0002_vgpio.c4
-rw-r--r--drivers/platform/x86/intel/int1092/intel_sar.c2
-rw-r--r--drivers/platform/x86/intel/int3472/discrete.c2
-rw-r--r--drivers/platform/x86/intel/mrfld_pwrbtn.c2
-rw-r--r--drivers/platform/x86/intel/plr_tpmi.c (renamed from drivers/platform/x86/intel/intel_plr_tpmi.c)0
-rw-r--r--drivers/platform/x86/intel/pmc/arl.c3
-rw-r--r--drivers/platform/x86/intel/pmc/cnp.c53
-rw-r--r--drivers/platform/x86/intel/pmc/core.c2
-rw-r--r--drivers/platform/x86/intel/pmc/lnl.c3
-rw-r--r--drivers/platform/x86/intel/pmc/mtl.c3
-rw-r--r--drivers/platform/x86/intel/pmt/class.c10
-rw-r--r--drivers/platform/x86/intel/pmt/class.h2
-rw-r--r--drivers/platform/x86/intel/pmt/telemetry.c2
-rw-r--r--drivers/platform/x86/intel/telemetry/pltdrv.c2
-rw-r--r--drivers/platform/x86/intel/vbtn.c2
-rw-r--r--drivers/platform/x86/intel/vsec.c6
-rw-r--r--drivers/platform/x86/intel/vsec_tpmi.c (renamed from drivers/platform/x86/intel/tpmi.c)2
-rw-r--r--drivers/platform/x86/intel_scu_ipc.c140
-rw-r--r--drivers/platform/x86/lenovo-yoga-tab2-pro-1380-fastcharger.c2
-rw-r--r--drivers/platform/x86/lenovo-yogabook.c2
-rw-r--r--drivers/platform/x86/mlx-platform.c2
-rw-r--r--drivers/platform/x86/p2sb.c1
-rw-r--r--drivers/platform/x86/panasonic-laptop.c10
-rw-r--r--drivers/platform/x86/samsung-q10.c2
-rw-r--r--drivers/platform/x86/sel3350-platform.c2
-rw-r--r--drivers/platform/x86/serial-multi-instantiate.c2
-rw-r--r--drivers/platform/x86/siemens/simatic-ipc-batt-apollolake.c2
-rw-r--r--drivers/platform/x86/siemens/simatic-ipc-batt-elkhartlake.c2
-rw-r--r--drivers/platform/x86/siemens/simatic-ipc-batt-f7188x.c2
-rw-r--r--drivers/platform/x86/siemens/simatic-ipc-batt.c2
-rw-r--r--drivers/platform/x86/think-lmi.c149
-rw-r--r--drivers/platform/x86/think-lmi.h6
-rw-r--r--drivers/platform/x86/wmi.c98
-rw-r--r--drivers/platform/x86/x86-android-tablets/Kconfig4
-rw-r--r--drivers/platform/x86/x86-android-tablets/core.c60
-rw-r--r--drivers/platform/x86/x86-android-tablets/dmi.c10
-rw-r--r--drivers/platform/x86/x86-android-tablets/other.c163
-rw-r--r--drivers/platform/x86/x86-android-tablets/x86-android-tablets.h2
-rw-r--r--drivers/platform/x86/xo1-rfkill.c2
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);