diff options
Diffstat (limited to 'meta-amd-bsp/recipes-kernel/linux/linux-yocto-5.15/0045-platform-x86-Add-AMD-system-management-interface.patch')
-rw-r--r-- | meta-amd-bsp/recipes-kernel/linux/linux-yocto-5.15/0045-platform-x86-Add-AMD-system-management-interface.patch | 779 |
1 files changed, 779 insertions, 0 deletions
diff --git a/meta-amd-bsp/recipes-kernel/linux/linux-yocto-5.15/0045-platform-x86-Add-AMD-system-management-interface.patch b/meta-amd-bsp/recipes-kernel/linux/linux-yocto-5.15/0045-platform-x86-Add-AMD-system-management-interface.patch new file mode 100644 index 00000000..630803f2 --- /dev/null +++ b/meta-amd-bsp/recipes-kernel/linux/linux-yocto-5.15/0045-platform-x86-Add-AMD-system-management-interface.patch @@ -0,0 +1,779 @@ +From 87807678529f6645b798b5e91cb1a13e8f95b8b2 Mon Sep 17 00:00:00 2001 +From: Suma Hegde <suma.hegde@amd.com> +Date: Tue, 22 Feb 2022 10:35:00 +0530 +Subject: [PATCH 45/86] platform/x86: Add AMD system management interface + +commit 91f410aa679a035e7abdff47daca4418c384c770 upstream + +Recent Fam19h EPYC server line of processors from AMD support system +management functionality via HSMP (Host System Management Port) interface. + +The Host System Management Port (HSMP) is an interface to provide +OS-level software with access to system management functions via a +set of mailbox registers. + +More details on the interface can be found in chapter +"7 Host System Management Port (HSMP)" of the following PPR +https://www.amd.com/system/files/TechDocs/55898_B1_pub_0.50.zip + +This patch adds new amd_hsmp module under the drivers/platforms/x86/ +which creates miscdevice with an IOCTL interface to the user space. +/dev/hsmp is for running the hsmp mailbox commands. + +Signed-off-by: Suma Hegde <suma.hegde@amd.com> +Signed-off-by: Naveen Krishna Chatradhi <nchatrad@amd.com> +Reviewed-by: Carlos Bilbao <carlos.bilbao@amd.com> +Acked-by: Song Liu <song@kernel.org> +Reviewed-by: Nathan Fontenot <nathan.fontenot@amd.com> +Link: https://lore.kernel.org/r/20220222050501.18789-1-nchatrad@amd.com +Reviewed-by: Hans de Goede <hdegoede@redhat.com> +Signed-off-by: Hans de Goede <hdegoede@redhat.com> +Signed-off-by: Zhaolong Zhang <zhaolong.zhang@windriver.com> +--- + .../userspace-api/ioctl/ioctl-number.rst | 2 + + MAINTAINERS | 10 + + arch/x86/include/asm/amd_hsmp.h | 16 + + arch/x86/include/uapi/asm/amd_hsmp.h | 203 +++++++++ + drivers/platform/x86/Kconfig | 13 + + drivers/platform/x86/Makefile | 1 + + drivers/platform/x86/amd_hsmp.c | 425 ++++++++++++++++++ + 7 files changed, 670 insertions(+) + create mode 100644 arch/x86/include/asm/amd_hsmp.h + create mode 100644 arch/x86/include/uapi/asm/amd_hsmp.h + create mode 100644 drivers/platform/x86/amd_hsmp.c + +diff --git a/Documentation/userspace-api/ioctl/ioctl-number.rst b/Documentation/userspace-api/ioctl/ioctl-number.rst +index 6655d929a351..4dc0b46fb022 100644 +--- a/Documentation/userspace-api/ioctl/ioctl-number.rst ++++ b/Documentation/userspace-api/ioctl/ioctl-number.rst +@@ -370,6 +370,8 @@ Code Seq# Include File Comments + <mailto:thomas@winischhofer.net> + 0xF6 all LTTng Linux Trace Toolkit Next Generation + <mailto:mathieu.desnoyers@efficios.com> ++0xF8 all arch/x86/include/uapi/asm/amd_hsmp.h AMD HSMP EPYC system management interface driver ++ <mailto:nchatrad@amd.com> + 0xFD all linux/dm-ioctl.h + 0xFE all linux/isst_if.h + ==== ===== ======================================================= ================================================================ +diff --git a/MAINTAINERS b/MAINTAINERS +index d80625ae4c12..670498533b01 100644 +--- a/MAINTAINERS ++++ b/MAINTAINERS +@@ -980,6 +980,16 @@ L: platform-driver-x86@vger.kernel.org + S: Maintained + F: drivers/platform/x86/amd-pmc.* + ++AMD HSMP DRIVER ++M: Naveen Krishna Chatradhi <naveenkrishna.chatradhi@amd.com> ++R: Carlos Bilbao <carlos.bilbao@amd.com> ++L: platform-driver-x86@vger.kernel.org ++S: Maintained ++F: Documentation/x86/amd_hsmp.rst ++F: arch/x86/include/asm/amd_hsmp.h ++F: arch/x86/include/uapi/asm/amd_hsmp.h ++F: drivers/platform/x86/amd_hsmp.c ++ + AMD POWERPLAY AND SWSMU + M: Evan Quan <evan.quan@amd.com> + L: amd-gfx@lists.freedesktop.org +diff --git a/arch/x86/include/asm/amd_hsmp.h b/arch/x86/include/asm/amd_hsmp.h +new file mode 100644 +index 000000000000..03c2ce3edaf5 +--- /dev/null ++++ b/arch/x86/include/asm/amd_hsmp.h +@@ -0,0 +1,16 @@ ++/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ ++ ++#ifndef _ASM_X86_AMD_HSMP_H_ ++#define _ASM_X86_AMD_HSMP_H_ ++ ++#include <uapi/asm/amd_hsmp.h> ++ ++#if IS_ENABLED(CONFIG_AMD_HSMP) ++int hsmp_send_message(struct hsmp_message *msg); ++#else ++static inline int hsmp_send_message(struct hsmp_message *msg) ++{ ++ return -ENODEV; ++} ++#endif ++#endif /*_ASM_X86_AMD_HSMP_H_*/ +diff --git a/arch/x86/include/uapi/asm/amd_hsmp.h b/arch/x86/include/uapi/asm/amd_hsmp.h +new file mode 100644 +index 000000000000..7ee7ba0d63a3 +--- /dev/null ++++ b/arch/x86/include/uapi/asm/amd_hsmp.h +@@ -0,0 +1,203 @@ ++/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ ++ ++#ifndef _UAPI_ASM_X86_AMD_HSMP_H_ ++#define _UAPI_ASM_X86_AMD_HSMP_H_ ++ ++#include <linux/types.h> ++ ++#pragma pack(4) ++ ++#define HSMP_MAX_MSG_LEN 8 ++ ++/* ++ * HSMP Messages supported ++ */ ++enum hsmp_message_ids { ++ HSMP_TEST = 1, /* 01h Increments input value by 1 */ ++ HSMP_GET_SMU_VER, /* 02h SMU FW version */ ++ HSMP_GET_PROTO_VER, /* 03h HSMP interface version */ ++ HSMP_GET_SOCKET_POWER, /* 04h average package power consumption */ ++ HSMP_SET_SOCKET_POWER_LIMIT, /* 05h Set the socket power limit */ ++ HSMP_GET_SOCKET_POWER_LIMIT, /* 06h Get current socket power limit */ ++ HSMP_GET_SOCKET_POWER_LIMIT_MAX,/* 07h Get maximum socket power value */ ++ HSMP_SET_BOOST_LIMIT, /* 08h Set a core maximum frequency limit */ ++ HSMP_SET_BOOST_LIMIT_SOCKET, /* 09h Set socket maximum frequency level */ ++ HSMP_GET_BOOST_LIMIT, /* 0Ah Get current frequency limit */ ++ HSMP_GET_PROC_HOT, /* 0Bh Get PROCHOT status */ ++ HSMP_SET_XGMI_LINK_WIDTH, /* 0Ch Set max and min width of xGMI Link */ ++ HSMP_SET_DF_PSTATE, /* 0Dh Alter APEnable/Disable messages behavior */ ++ HSMP_SET_AUTO_DF_PSTATE, /* 0Eh Enable DF P-State Performance Boost algorithm */ ++ HSMP_GET_FCLK_MCLK, /* 0Fh Get FCLK and MEMCLK for current socket */ ++ HSMP_GET_CCLK_THROTTLE_LIMIT, /* 10h Get CCLK frequency limit in socket */ ++ HSMP_GET_C0_PERCENT, /* 11h Get average C0 residency in socket */ ++ HSMP_SET_NBIO_DPM_LEVEL, /* 12h Set max/min LCLK DPM Level for a given NBIO */ ++ /* 13h Reserved */ ++ HSMP_GET_DDR_BANDWIDTH = 0x14, /* 14h Get theoretical maximum and current DDR Bandwidth */ ++ HSMP_GET_TEMP_MONITOR, /* 15h Get per-DIMM temperature and refresh rates */ ++ HSMP_MSG_ID_MAX, ++}; ++ ++struct hsmp_message { ++ __u32 msg_id; /* Message ID */ ++ __u16 num_args; /* Number of input argument words in message */ ++ __u16 response_sz; /* Number of expected output/response words */ ++ __u32 args[HSMP_MAX_MSG_LEN]; /* argument/response buffer */ ++ __u16 sock_ind; /* socket number */ ++}; ++ ++enum hsmp_msg_type { ++ HSMP_RSVD = -1, ++ HSMP_SET = 0, ++ HSMP_GET = 1, ++}; ++ ++struct hsmp_msg_desc { ++ int num_args; ++ int response_sz; ++ enum hsmp_msg_type type; ++}; ++ ++/* ++ * User may use these comments as reference, please find the ++ * supported list of messages and message definition in the ++ * HSMP chapter of respective family/model PPR. ++ * ++ * Not supported messages would return -ENOMSG. ++ */ ++static const struct hsmp_msg_desc hsmp_msg_desc_table[] = { ++ /* RESERVED */ ++ {0, 0, HSMP_RSVD}, ++ ++ /* ++ * HSMP_TEST, num_args = 1, response_sz = 1 ++ * input: args[0] = xx ++ * output: args[0] = xx + 1 ++ */ ++ {1, 1, HSMP_GET}, ++ ++ /* ++ * HSMP_GET_SMU_VER, num_args = 0, response_sz = 1 ++ * output: args[0] = smu fw ver ++ */ ++ {0, 1, HSMP_GET}, ++ ++ /* ++ * HSMP_GET_PROTO_VER, num_args = 0, response_sz = 1 ++ * output: args[0] = proto version ++ */ ++ {0, 1, HSMP_GET}, ++ ++ /* ++ * HSMP_GET_SOCKET_POWER, num_args = 0, response_sz = 1 ++ * output: args[0] = socket power in mWatts ++ */ ++ {0, 1, HSMP_GET}, ++ ++ /* ++ * HSMP_SET_SOCKET_POWER_LIMIT, num_args = 1, response_sz = 0 ++ * input: args[0] = power limit value in mWatts ++ */ ++ {1, 0, HSMP_SET}, ++ ++ /* ++ * HSMP_GET_SOCKET_POWER_LIMIT, num_args = 0, response_sz = 1 ++ * output: args[0] = socket power limit value in mWatts ++ */ ++ {0, 1, HSMP_GET}, ++ ++ /* ++ * HSMP_GET_SOCKET_POWER_LIMIT_MAX, num_args = 0, response_sz = 1 ++ * output: args[0] = maximuam socket power limit in mWatts ++ */ ++ {0, 1, HSMP_GET}, ++ ++ /* ++ * HSMP_SET_BOOST_LIMIT, num_args = 1, response_sz = 0 ++ * input: args[0] = apic id[31:16] + boost limit value in MHz[15:0] ++ */ ++ {1, 0, HSMP_SET}, ++ ++ /* ++ * HSMP_SET_BOOST_LIMIT_SOCKET, num_args = 1, response_sz = 0 ++ * input: args[0] = boost limit value in MHz ++ */ ++ {1, 0, HSMP_SET}, ++ ++ /* ++ * HSMP_GET_BOOST_LIMIT, num_args = 1, response_sz = 1 ++ * input: args[0] = apic id ++ * output: args[0] = boost limit value in MHz ++ */ ++ {1, 1, HSMP_GET}, ++ ++ /* ++ * HSMP_GET_PROC_HOT, num_args = 0, response_sz = 1 ++ * output: args[0] = proc hot status ++ */ ++ {0, 1, HSMP_GET}, ++ ++ /* ++ * HSMP_SET_XGMI_LINK_WIDTH, num_args = 1, response_sz = 0 ++ * input: args[0] = min link width[15:8] + max link width[7:0] ++ */ ++ {1, 0, HSMP_SET}, ++ ++ /* ++ * HSMP_SET_DF_PSTATE, num_args = 1, response_sz = 0 ++ * input: args[0] = df pstate[7:0] ++ */ ++ {1, 0, HSMP_SET}, ++ ++ /* HSMP_SET_AUTO_DF_PSTATE, num_args = 0, response_sz = 0 */ ++ {0, 0, HSMP_SET}, ++ ++ /* ++ * HSMP_GET_FCLK_MCLK, num_args = 0, response_sz = 2 ++ * output: args[0] = fclk in MHz, args[1] = mclk in MHz ++ */ ++ {0, 2, HSMP_GET}, ++ ++ /* ++ * HSMP_GET_CCLK_THROTTLE_LIMIT, num_args = 0, response_sz = 1 ++ * output: args[0] = core clock in MHz ++ */ ++ {0, 1, HSMP_GET}, ++ ++ /* ++ * HSMP_GET_C0_PERCENT, num_args = 0, response_sz = 1 ++ * output: args[0] = average c0 residency ++ */ ++ {0, 1, HSMP_GET}, ++ ++ /* ++ * HSMP_SET_NBIO_DPM_LEVEL, num_args = 1, response_sz = 0 ++ * input: args[0] = nbioid[23:16] + max dpm level[15:8] + min dpm level[7:0] ++ */ ++ {1, 0, HSMP_SET}, ++ ++ /* RESERVED message */ ++ {0, 0, HSMP_RSVD}, ++ ++ /* ++ * HSMP_GET_DDR_BANDWIDTH, num_args = 0, response_sz = 1 ++ * output: args[0] = max bw in Gbps[31:20] + utilised bw in Gbps[19:8] + ++ * bw in percentage[7:0] ++ */ ++ {0, 1, HSMP_GET}, ++ ++ /* ++ * HSMP_GET_TEMP_MONITOR, num_args = 0, response_sz = 1 ++ * output: args[0] = temperature in degree celsius. [15:8] integer part + ++ * [7:5] fractional part ++ */ ++ {0, 1, HSMP_GET}, ++}; ++ ++/* Reset to default packing */ ++#pragma pack() ++ ++/* Define unique ioctl command for hsmp msgs using generic _IOWR */ ++#define HSMP_BASE_IOCTL_NR 0xF8 ++#define HSMP_IOCTL_CMD _IOWR(HSMP_BASE_IOCTL_NR, 0, struct hsmp_message) ++ ++#endif /*_ASM_X86_AMD_HSMP_H_*/ +diff --git a/drivers/platform/x86/Kconfig b/drivers/platform/x86/Kconfig +index f1ff003bb14b..9a709e6d2d3e 100644 +--- a/drivers/platform/x86/Kconfig ++++ b/drivers/platform/x86/Kconfig +@@ -182,6 +182,19 @@ config AMD_PMC + If you choose to compile this driver as a module the module will be + called amd-pmc. + ++config AMD_HSMP ++ tristate "AMD HSMP Driver" ++ depends on AMD_NB && X86_64 ++ 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. ++ ++ If you choose to compile this driver as a module the module will be ++ called amd_hsmp. ++ + config ADV_SWBUTTON + tristate "Advantech ACPI Software Button Driver" + depends on ACPI && INPUT +diff --git a/drivers/platform/x86/Makefile b/drivers/platform/x86/Makefile +index 2734a771d1f0..95864ab02045 100644 +--- a/drivers/platform/x86/Makefile ++++ b/drivers/platform/x86/Makefile +@@ -22,6 +22,7 @@ obj-$(CONFIG_ACER_WMI) += acer-wmi.o + + # AMD + obj-$(CONFIG_AMD_PMC) += amd-pmc.o ++obj-$(CONFIG_AMD_HSMP) += amd_hsmp.o + + # Advantech + obj-$(CONFIG_ADV_SWBUTTON) += adv_swbutton.o +diff --git a/drivers/platform/x86/amd_hsmp.c b/drivers/platform/x86/amd_hsmp.c +new file mode 100644 +index 000000000000..a0c54b838c11 +--- /dev/null ++++ b/drivers/platform/x86/amd_hsmp.c +@@ -0,0 +1,425 @@ ++// 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> ++ ++#define DRIVER_NAME "amd_hsmp" ++#define DRIVER_VERSION "1.0" ++ ++/* 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_MSG_ID 0x3B10534 ++#define SMN_HSMP_MSG_RESP 0x3B10980 ++#define SMN_HSMP_MSG_DATA 0x3B109E0 ++ ++#define HSMP_INDEX_REG 0xc4 ++#define HSMP_DATA_REG 0xc8 ++ ++static struct semaphore *hsmp_sem; ++ ++static struct miscdevice hsmp_device; ++ ++static int amd_hsmp_rdwr(struct pci_dev *root, u32 address, ++ u32 *value, bool write) ++{ ++ int ret; ++ ++ ret = pci_write_config_dword(root, HSMP_INDEX_REG, address); ++ if (ret) ++ return ret; ++ ++ ret = (write ? pci_write_config_dword(root, HSMP_DATA_REG, *value) ++ : pci_read_config_dword(root, HSMP_DATA_REG, value)); ++ ++ return ret; ++} ++ ++/* ++ * Send a message to the HSMP port via PCI-e config space registers. ++ * ++ * 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 pci_dev *root, struct hsmp_message *msg) ++{ ++ unsigned long timeout, short_sleep; ++ u32 mbox_status; ++ u32 index; ++ int ret; ++ ++ /* Clear the status register */ ++ mbox_status = HSMP_STATUS_NOT_READY; ++ ret = amd_hsmp_rdwr(root, SMN_HSMP_MSG_RESP, &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(root, SMN_HSMP_MSG_DATA + (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(root, SMN_HSMP_MSG_ID, &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(root, SMN_HSMP_MSG_RESP, &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(root, SMN_HSMP_MSG_DATA + (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 amd_northbridge *nb; ++ int ret; ++ ++ if (!msg) ++ return -EINVAL; ++ ++ nb = node_to_amd_nb(msg->sock_ind); ++ if (!nb || !nb->root) ++ return -ENODEV; ++ ++ ret = validate_message(msg); ++ if (ret) ++ return ret; ++ ++ /* ++ * 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(&hsmp_sem[msg->sock_ind], ++ msecs_to_jiffies(HSMP_MSG_TIMEOUT)); ++ if (ret < 0) ++ return ret; ++ ++ ret = __hsmp_send_message(nb->root, msg); ++ ++ up(&hsmp_sem[msg->sock_ind]); ++ ++ return ret; ++} ++EXPORT_SYMBOL_GPL(hsmp_send_message); ++ ++static int hsmp_test(u16 sock_ind, u32 value) ++{ ++ struct hsmp_message msg = { 0 }; ++ struct amd_northbridge *nb; ++ int ret = -ENODEV; ++ ++ nb = node_to_amd_nb(sock_ind); ++ if (!nb || !nb->root) ++ return ret; ++ ++ /* ++ * 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(nb->root, &msg); ++ if (ret) ++ return ret; ++ ++ /* Check the response value */ ++ if (msg.args[0] != (value + 1)) { ++ pr_err("Socket %d test message failed, Expected 0x%08X, received 0x%08X\n", ++ sock_ind, (value + 1), msg.args[0]); ++ 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, ++}; ++ ++static int hsmp_pltdrv_probe(struct platform_device *pdev) ++{ ++ int i; ++ ++ hsmp_sem = devm_kzalloc(&pdev->dev, ++ (amd_nb_num() * sizeof(struct semaphore)), ++ GFP_KERNEL); ++ if (!hsmp_sem) ++ return -ENOMEM; ++ ++ for (i = 0; i < amd_nb_num(); i++) ++ sema_init(&hsmp_sem[i], 1); ++ ++ hsmp_device.name = "hsmp_cdev"; ++ hsmp_device.minor = MISC_DYNAMIC_MINOR; ++ hsmp_device.fops = &hsmp_fops; ++ hsmp_device.parent = &pdev->dev; ++ hsmp_device.nodename = "hsmp"; ++ hsmp_device.mode = 0644; ++ ++ return misc_register(&hsmp_device); ++} ++ ++static int hsmp_pltdrv_remove(struct platform_device *pdev) ++{ ++ misc_deregister(&hsmp_device); ++ ++ return 0; ++} ++ ++static struct platform_driver amd_hsmp_driver = { ++ .probe = hsmp_pltdrv_probe, ++ .remove = hsmp_pltdrv_remove, ++ .driver = { ++ .name = DRIVER_NAME, ++ }, ++}; ++ ++static struct platform_device *amd_hsmp_platdev; ++ ++static int __init hsmp_plt_init(void) ++{ ++ int ret = -ENODEV; ++ u16 num_sockets; ++ int i; ++ ++ if (boot_cpu_data.x86_vendor != X86_VENDOR_AMD || boot_cpu_data.x86 < 0x19) { ++ pr_err("HSMP is not supported on Family:%x model:%x\n", ++ boot_cpu_data.x86, boot_cpu_data.x86_model); ++ return ret; ++ } ++ ++ /* ++ * 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 ++ */ ++ num_sockets = amd_nb_num(); ++ if (num_sockets == 0) ++ return ret; ++ ++ /* Test the hsmp interface on each socket */ ++ for (i = 0; i < num_sockets; i++) { ++ ret = hsmp_test(i, 0xDEADBEEF); ++ if (ret) { ++ pr_err("HSMP is not supported on Fam:%x model:%x\n", ++ boot_cpu_data.x86, boot_cpu_data.x86_model); ++ pr_err("Or Is HSMP disabled in BIOS ?\n"); ++ return -EOPNOTSUPP; ++ } ++ } ++ ++ ret = platform_driver_register(&amd_hsmp_driver); ++ if (ret) ++ return ret; ++ ++ amd_hsmp_platdev = platform_device_alloc(DRIVER_NAME, -1); ++ if (!amd_hsmp_platdev) { ++ ret = -ENOMEM; ++ goto drv_unregister; ++ } ++ ++ ret = platform_device_add(amd_hsmp_platdev); ++ if (ret) { ++ platform_device_put(amd_hsmp_platdev); ++ goto drv_unregister; ++ } ++ ++ return 0; ++ ++drv_unregister: ++ 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"); +-- +2.37.3 + |