mirror of
git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2025-09-04 20:19:47 +08:00
x86/platform changes for v6.17:
This tree adds support for the AMD hardware feedback interface (HFI), by Perry Yuan. Signed-off-by: Ingo Molnar <mingo@kernel.org> -----BEGIN PGP SIGNATURE----- iQJFBAABCgAvFiEEBpT5eoXrXCwVQwEKEnMQ0APhK1gFAmiIdwERHG1pbmdvQGtl cm5lbC5vcmcACgkQEnMQ0APhK1gYIRAAueF4R6gXv2luxwZc5xDHUp0z3W2nkFWB mpD329mqewy79fPusGRMjwxwuT7/EzIIfVkjOAi21RnwkoU/L6kBdIsn/ZGMTUpG +8zFnjBsdAb2Nm27LZ+W6nTxHn+ZiMs1U2AUqYC+F/sTtILA7LKt0aufgRrw028I aZKHOvHoYRYxgQEgcaCjBXXyUsw4zetIV8i2fj+rflutXL5tLSSgg9Pyh/dNU8JX MfWHxC2J79mYc8sLj2Pc0H6wuMPDCZ7uzBq/uieuZjhDouSdV9oDFoc3bNwYpxZI 7CBqg8qcnDY3HFnbT7Hwv9O0+Kbl3+714N5P1J0Lb1+ovaRazdLCA9jenRu+lXIp 7OehfOQoxw0XbWfESvEsSmMEjiyxIbcuwyu9X+9asY8bybD21s1gzbQYOGcBD4sf YjbGObnGqqS2GWe6LayU6HKs6kMWQfS+8xLlvWtnT4KNPwVOT1D41Fiy8lYQMXd1 MFX1vRxLdtMtjVrotq4UmENdK/t7fd2BRvDw3wOjSfNynIf55XFRuqR7RTCLQivJ Z8ZF390kb1JSmBJdNZ1Mj4XUT5fTcimLra2qSTZ2dTDO23b0IiTlgoDP1LLbYYDL lRGfMViwO4Kinm/nerzELv/VAFMFm/DvHmWuGj/vaRTS3r7iGb3+A5SFe4JS0Utz BWoqrbrAWCg= =h01B -----END PGP SIGNATURE----- Merge tag 'x86-platform-2025-07-29' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip Pull x86 platform updates from Ingo Molnar: "This adds support for the AMD hardware feedback interface (HFI), by Perry Yuan" * tag 'x86-platform-2025-07-29' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip: x86/itmt: Add debugfs file to show core priorities platform/x86/amd: hfi: Add debugfs support platform/x86/amd: hfi: Set ITMT priority from ranking data cpufreq/amd-pstate: Disable preferred cores on designs with workload classification x86/process: Clear hardware feedback history for AMD processors platform/x86: hfi: Add power management callback platform/x86: hfi: Add online and offline callback support platform/x86: hfi: Init per-cpu scores for each class platform/x86: hfi: Parse CPU core ranking data from shared memory platform/x86: hfi: Introduce AMD Hardware Feedback Interface Driver x86/msr-index: Add AMD workload classification MSRs MAINTAINERS: Add maintainer entry for AMD Hardware Feedback Driver Documentation/x86: Add AMD Hardware Feedback Interface documentation
This commit is contained in:
commit
98e8f2c0e0
133
Documentation/arch/x86/amd-hfi.rst
Normal file
133
Documentation/arch/x86/amd-hfi.rst
Normal file
@ -0,0 +1,133 @@
|
|||||||
|
.. SPDX-License-Identifier: GPL-2.0
|
||||||
|
|
||||||
|
======================================================================
|
||||||
|
Hardware Feedback Interface For Hetero Core Scheduling On AMD Platform
|
||||||
|
======================================================================
|
||||||
|
|
||||||
|
:Copyright: 2025 Advanced Micro Devices, Inc. All Rights Reserved.
|
||||||
|
|
||||||
|
:Author: Perry Yuan <perry.yuan@amd.com>
|
||||||
|
:Author: Mario Limonciello <mario.limonciello@amd.com>
|
||||||
|
|
||||||
|
Overview
|
||||||
|
--------
|
||||||
|
|
||||||
|
AMD Heterogeneous Core implementations are comprised of more than one
|
||||||
|
architectural class and CPUs are comprised of cores of various efficiency and
|
||||||
|
power capabilities: performance-oriented *classic cores* and power-efficient
|
||||||
|
*dense cores*. As such, power management strategies must be designed to
|
||||||
|
accommodate the complexities introduced by incorporating different core types.
|
||||||
|
Heterogeneous systems can also extend to more than two architectural classes
|
||||||
|
as well. The purpose of the scheduling feedback mechanism is to provide
|
||||||
|
information to the operating system scheduler in real time such that the
|
||||||
|
scheduler can direct threads to the optimal core.
|
||||||
|
|
||||||
|
The goal of AMD's heterogeneous architecture is to attain power benefit by
|
||||||
|
sending background threads to the dense cores while sending high priority
|
||||||
|
threads to the classic cores. From a performance perspective, sending
|
||||||
|
background threads to dense cores can free up power headroom and allow the
|
||||||
|
classic cores to optimally service demanding threads. Furthermore, the area
|
||||||
|
optimized nature of the dense cores allows for an increasing number of
|
||||||
|
physical cores. This improved core density will have positive multithreaded
|
||||||
|
performance impact.
|
||||||
|
|
||||||
|
AMD Heterogeneous Core Driver
|
||||||
|
-----------------------------
|
||||||
|
|
||||||
|
The ``amd_hfi`` driver delivers the operating system a performance and energy
|
||||||
|
efficiency capability data for each CPU in the system. The scheduler can use
|
||||||
|
the ranking data from the HFI driver to make task placement decisions.
|
||||||
|
|
||||||
|
Thread Classification and Ranking Table Interaction
|
||||||
|
----------------------------------------------------
|
||||||
|
|
||||||
|
The thread classification is used to select into a ranking table that
|
||||||
|
describes an efficiency and performance ranking for each classification.
|
||||||
|
|
||||||
|
Threads are classified during runtime into enumerated classes. The classes
|
||||||
|
represent thread performance/power characteristics that may benefit from
|
||||||
|
special scheduling behaviors. The below table depicts an example of thread
|
||||||
|
classification and a preference where a given thread should be scheduled
|
||||||
|
based on its thread class. The real time thread classification is consumed
|
||||||
|
by the operating system and is used to inform the scheduler of where the
|
||||||
|
thread should be placed.
|
||||||
|
|
||||||
|
Thread Classification Example Table
|
||||||
|
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
+----------+----------------+-------------------------------+---------------------+---------+
|
||||||
|
| class ID | Classification | Preferred scheduling behavior | Preemption priority | Counter |
|
||||||
|
+----------+----------------+-------------------------------+---------------------+---------+
|
||||||
|
| 0 | Default | Performant | Highest | |
|
||||||
|
+----------+----------------+-------------------------------+---------------------+---------+
|
||||||
|
| 1 | Non-scalable | Efficient | Lowest | PMCx1A1 |
|
||||||
|
+----------+----------------+-------------------------------+---------------------+---------+
|
||||||
|
| 2 | I/O bound | Efficient | Lowest | PMCx044 |
|
||||||
|
+----------+----------------+-------------------------------+---------------------+---------+
|
||||||
|
|
||||||
|
Thread classification is performed by the hardware each time that the thread is switched out.
|
||||||
|
Threads that don't meet any hardware specified criteria are classified as "default".
|
||||||
|
|
||||||
|
AMD Hardware Feedback Interface
|
||||||
|
--------------------------------
|
||||||
|
|
||||||
|
The Hardware Feedback Interface provides to the operating system information
|
||||||
|
about the performance and energy efficiency of each CPU in the system. Each
|
||||||
|
capability is given as a unit-less quantity in the range [0-255]. A higher
|
||||||
|
performance value indicates higher performance capability, and a higher
|
||||||
|
efficiency value indicates more efficiency. Energy efficiency and performance
|
||||||
|
are reported in separate capabilities in the shared memory based ranking table.
|
||||||
|
|
||||||
|
These capabilities may change at runtime as a result of changes in the
|
||||||
|
operating conditions of the system or the action of external factors.
|
||||||
|
Power Management firmware is responsible for detecting events that require
|
||||||
|
a reordering of the performance and efficiency ranking. Table updates happen
|
||||||
|
relatively infrequently and occur on the time scale of seconds or more.
|
||||||
|
|
||||||
|
The following events trigger a table update:
|
||||||
|
* Thermal Stress Events
|
||||||
|
* Silent Compute
|
||||||
|
* Extreme Low Battery Scenarios
|
||||||
|
|
||||||
|
The kernel or a userspace policy daemon can use these capabilities to modify
|
||||||
|
task placement decisions. For instance, if either the performance or energy
|
||||||
|
capabilities of a given logical processor becomes zero, it is an indication
|
||||||
|
that the hardware recommends to the operating system to not schedule any tasks
|
||||||
|
on that processor for performance or energy efficiency reasons, respectively.
|
||||||
|
|
||||||
|
Implementation details for Linux
|
||||||
|
--------------------------------
|
||||||
|
|
||||||
|
The implementation of threads scheduling consists of the following steps:
|
||||||
|
|
||||||
|
1. A thread is spawned and scheduled to the ideal core using the default
|
||||||
|
heterogeneous scheduling policy.
|
||||||
|
2. The processor profiles thread execution and assigns an enumerated
|
||||||
|
classification ID.
|
||||||
|
This classification is communicated to the OS via logical processor
|
||||||
|
scope MSR.
|
||||||
|
3. During the thread context switch out the operating system consumes the
|
||||||
|
workload (WL) classification which resides in a logical processor scope MSR.
|
||||||
|
4. The OS triggers the hardware to clear its history by writing to an MSR,
|
||||||
|
after consuming the WL classification and before switching in the new thread.
|
||||||
|
5. If due to the classification, ranking table, and processor availability,
|
||||||
|
the thread is not on its ideal processor, the OS will then consider
|
||||||
|
scheduling the thread on its ideal processor (if available).
|
||||||
|
|
||||||
|
Ranking Table
|
||||||
|
-------------
|
||||||
|
The ranking table is a shared memory region that is used to communicate the
|
||||||
|
performance and energy efficiency capabilities of each CPU in the system.
|
||||||
|
|
||||||
|
The ranking table design includes rankings for each APIC ID in the system and
|
||||||
|
rankings both for performance and efficiency for each workload classification.
|
||||||
|
|
||||||
|
.. kernel-doc:: drivers/platform/x86/amd/hfi/hfi.c
|
||||||
|
:doc: amd_shmem_info
|
||||||
|
|
||||||
|
Ranking Table update
|
||||||
|
---------------------------
|
||||||
|
The power management firmware issues an platform interrupt after updating the
|
||||||
|
ranking table and is ready for the operating system to consume it. CPUs receive
|
||||||
|
such interrupt and read new ranking table from shared memory which PCCT table
|
||||||
|
has provided, then ``amd_hfi`` driver parses the new table to provide new
|
||||||
|
consume data for scheduling decisions.
|
@ -28,6 +28,7 @@ x86-specific Documentation
|
|||||||
amd-debugging
|
amd-debugging
|
||||||
amd-memory-encryption
|
amd-memory-encryption
|
||||||
amd_hsmp
|
amd_hsmp
|
||||||
|
amd-hfi
|
||||||
tdx
|
tdx
|
||||||
pti
|
pti
|
||||||
mds
|
mds
|
||||||
|
@ -1115,6 +1115,15 @@ F: arch/x86/include/asm/amd/hsmp.h
|
|||||||
F: arch/x86/include/uapi/asm/amd_hsmp.h
|
F: arch/x86/include/uapi/asm/amd_hsmp.h
|
||||||
F: drivers/platform/x86/amd/hsmp/
|
F: drivers/platform/x86/amd/hsmp/
|
||||||
|
|
||||||
|
AMD HETERO CORE HARDWARE FEEDBACK DRIVER
|
||||||
|
M: Mario Limonciello <mario.limonciello@amd.com>
|
||||||
|
R: Perry Yuan <perry.yuan@amd.com>
|
||||||
|
L: platform-driver-x86@vger.kernel.org
|
||||||
|
S: Supported
|
||||||
|
B: https://gitlab.freedesktop.org/drm/amd/-/issues
|
||||||
|
F: Documentation/arch/x86/amd-hfi.rst
|
||||||
|
F: drivers/platform/x86/amd/hfi/
|
||||||
|
|
||||||
AMD IOMMU (AMD-VI)
|
AMD IOMMU (AMD-VI)
|
||||||
M: Joerg Roedel <joro@8bytes.org>
|
M: Joerg Roedel <joro@8bytes.org>
|
||||||
R: Suravee Suthikulpanit <suravee.suthikulpanit@amd.com>
|
R: Suravee Suthikulpanit <suravee.suthikulpanit@amd.com>
|
||||||
|
@ -733,6 +733,11 @@
|
|||||||
#define MSR_AMD64_PERF_CNTR_GLOBAL_CTL 0xc0000301
|
#define MSR_AMD64_PERF_CNTR_GLOBAL_CTL 0xc0000301
|
||||||
#define MSR_AMD64_PERF_CNTR_GLOBAL_STATUS_CLR 0xc0000302
|
#define MSR_AMD64_PERF_CNTR_GLOBAL_STATUS_CLR 0xc0000302
|
||||||
|
|
||||||
|
/* AMD Hardware Feedback Support MSRs */
|
||||||
|
#define MSR_AMD_WORKLOAD_CLASS_CONFIG 0xc0000500
|
||||||
|
#define MSR_AMD_WORKLOAD_CLASS_ID 0xc0000501
|
||||||
|
#define MSR_AMD_WORKLOAD_HRST 0xc0000502
|
||||||
|
|
||||||
/* AMD Last Branch Record MSRs */
|
/* AMD Last Branch Record MSRs */
|
||||||
#define MSR_AMD64_LBR_SELECT 0xc000010e
|
#define MSR_AMD64_LBR_SELECT 0xc000010e
|
||||||
|
|
||||||
|
@ -59,6 +59,18 @@ static ssize_t sched_itmt_enabled_write(struct file *filp,
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int sched_core_priority_show(struct seq_file *s, void *unused)
|
||||||
|
{
|
||||||
|
int cpu;
|
||||||
|
|
||||||
|
seq_puts(s, "CPU #\tPriority\n");
|
||||||
|
for_each_possible_cpu(cpu)
|
||||||
|
seq_printf(s, "%d\t%d\n", cpu, arch_asym_cpu_priority(cpu));
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
DEFINE_SHOW_ATTRIBUTE(sched_core_priority);
|
||||||
|
|
||||||
static const struct file_operations dfs_sched_itmt_fops = {
|
static const struct file_operations dfs_sched_itmt_fops = {
|
||||||
.read = debugfs_read_file_bool,
|
.read = debugfs_read_file_bool,
|
||||||
.write = sched_itmt_enabled_write,
|
.write = sched_itmt_enabled_write,
|
||||||
@ -67,6 +79,7 @@ static const struct file_operations dfs_sched_itmt_fops = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
static struct dentry *dfs_sched_itmt;
|
static struct dentry *dfs_sched_itmt;
|
||||||
|
static struct dentry *dfs_sched_core_prio;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* sched_set_itmt_support() - Indicate platform supports ITMT
|
* sched_set_itmt_support() - Indicate platform supports ITMT
|
||||||
@ -102,6 +115,14 @@ int sched_set_itmt_support(void)
|
|||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
dfs_sched_core_prio = debugfs_create_file("sched_core_priority", 0644,
|
||||||
|
arch_debugfs_dir, NULL,
|
||||||
|
&sched_core_priority_fops);
|
||||||
|
if (IS_ERR_OR_NULL(dfs_sched_core_prio)) {
|
||||||
|
dfs_sched_core_prio = NULL;
|
||||||
|
return -ENOMEM;
|
||||||
|
}
|
||||||
|
|
||||||
sched_itmt_capable = true;
|
sched_itmt_capable = true;
|
||||||
|
|
||||||
sysctl_sched_itmt_enabled = 1;
|
sysctl_sched_itmt_enabled = 1;
|
||||||
@ -133,6 +154,8 @@ void sched_clear_itmt_support(void)
|
|||||||
|
|
||||||
debugfs_remove(dfs_sched_itmt);
|
debugfs_remove(dfs_sched_itmt);
|
||||||
dfs_sched_itmt = NULL;
|
dfs_sched_itmt = NULL;
|
||||||
|
debugfs_remove(dfs_sched_core_prio);
|
||||||
|
dfs_sched_core_prio = NULL;
|
||||||
|
|
||||||
if (sysctl_sched_itmt_enabled) {
|
if (sysctl_sched_itmt_enabled) {
|
||||||
/* disable sched_itmt if we are no longer ITMT capable */
|
/* disable sched_itmt if we are no longer ITMT capable */
|
||||||
|
@ -707,6 +707,10 @@ __switch_to(struct task_struct *prev_p, struct task_struct *next_p)
|
|||||||
/* Load the Intel cache allocation PQR MSR. */
|
/* Load the Intel cache allocation PQR MSR. */
|
||||||
resctrl_arch_sched_in(next_p);
|
resctrl_arch_sched_in(next_p);
|
||||||
|
|
||||||
|
/* Reset hw history on AMD CPUs */
|
||||||
|
if (cpu_feature_enabled(X86_FEATURE_AMD_WORKLOAD_CLASS))
|
||||||
|
wrmsrl(MSR_AMD_WORKLOAD_HRST, 0x1);
|
||||||
|
|
||||||
return prev_p;
|
return prev_p;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -826,6 +826,13 @@ static void amd_pstate_init_prefcore(struct amd_cpudata *cpudata)
|
|||||||
if (!amd_pstate_prefcore)
|
if (!amd_pstate_prefcore)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
/* should use amd-hfi instead */
|
||||||
|
if (cpu_feature_enabled(X86_FEATURE_AMD_WORKLOAD_CLASS) &&
|
||||||
|
IS_ENABLED(CONFIG_AMD_HFI)) {
|
||||||
|
amd_pstate_prefcore = false;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
cpudata->hw_prefcore = true;
|
cpudata->hw_prefcore = true;
|
||||||
|
|
||||||
/* Priorities must be initialized before ITMT support can be toggled on. */
|
/* Priorities must be initialized before ITMT support can be toggled on. */
|
||||||
|
@ -6,6 +6,7 @@
|
|||||||
source "drivers/platform/x86/amd/hsmp/Kconfig"
|
source "drivers/platform/x86/amd/hsmp/Kconfig"
|
||||||
source "drivers/platform/x86/amd/pmf/Kconfig"
|
source "drivers/platform/x86/amd/pmf/Kconfig"
|
||||||
source "drivers/platform/x86/amd/pmc/Kconfig"
|
source "drivers/platform/x86/amd/pmc/Kconfig"
|
||||||
|
source "drivers/platform/x86/amd/hfi/Kconfig"
|
||||||
|
|
||||||
config AMD_3D_VCACHE
|
config AMD_3D_VCACHE
|
||||||
tristate "AMD 3D V-Cache Performance Optimizer Driver"
|
tristate "AMD 3D V-Cache Performance Optimizer Driver"
|
||||||
|
@ -11,3 +11,4 @@ obj-$(CONFIG_AMD_HSMP) += hsmp/
|
|||||||
obj-$(CONFIG_AMD_PMF) += pmf/
|
obj-$(CONFIG_AMD_PMF) += pmf/
|
||||||
obj-$(CONFIG_AMD_WBRF) += wbrf.o
|
obj-$(CONFIG_AMD_WBRF) += wbrf.o
|
||||||
obj-$(CONFIG_AMD_ISP_PLATFORM) += amd_isp4.o
|
obj-$(CONFIG_AMD_ISP_PLATFORM) += amd_isp4.o
|
||||||
|
obj-$(CONFIG_AMD_HFI) += hfi/
|
||||||
|
18
drivers/platform/x86/amd/hfi/Kconfig
Normal file
18
drivers/platform/x86/amd/hfi/Kconfig
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
# SPDX-License-Identifier: GPL-2.0-only
|
||||||
|
#
|
||||||
|
# AMD Hardware Feedback Interface Driver
|
||||||
|
#
|
||||||
|
|
||||||
|
config AMD_HFI
|
||||||
|
bool "AMD Hetero Core Hardware Feedback Driver"
|
||||||
|
depends on ACPI
|
||||||
|
depends on CPU_SUP_AMD
|
||||||
|
depends on SCHED_MC_PRIO
|
||||||
|
help
|
||||||
|
Select this option to enable the AMD Heterogeneous Core Hardware
|
||||||
|
Feedback Interface. If selected, hardware provides runtime thread
|
||||||
|
classification guidance to the operating system on the performance and
|
||||||
|
energy efficiency capabilities of each heterogeneous CPU core. These
|
||||||
|
capabilities may vary due to the inherent differences in the core types
|
||||||
|
and can also change as a result of variations in the operating
|
||||||
|
conditions of the system such as power and thermal limits.
|
7
drivers/platform/x86/amd/hfi/Makefile
Normal file
7
drivers/platform/x86/amd/hfi/Makefile
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
# SPDX-License-Identifier: GPL-2.0
|
||||||
|
#
|
||||||
|
# AMD Hardware Feedback Interface Driver
|
||||||
|
#
|
||||||
|
|
||||||
|
obj-$(CONFIG_AMD_HFI) += amd_hfi.o
|
||||||
|
amd_hfi-objs := hfi.o
|
551
drivers/platform/x86/amd/hfi/hfi.c
Normal file
551
drivers/platform/x86/amd/hfi/hfi.c
Normal file
@ -0,0 +1,551 @@
|
|||||||
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
/*
|
||||||
|
* AMD Hardware Feedback Interface Driver
|
||||||
|
*
|
||||||
|
* Copyright (C) 2025 Advanced Micro Devices, Inc. All Rights Reserved.
|
||||||
|
*
|
||||||
|
* Authors: Perry Yuan <Perry.Yuan@amd.com>
|
||||||
|
* Mario Limonciello <mario.limonciello@amd.com>
|
||||||
|
*/
|
||||||
|
|
||||||
|
#define pr_fmt(fmt) "amd-hfi: " fmt
|
||||||
|
|
||||||
|
#include <linux/acpi.h>
|
||||||
|
#include <linux/cpu.h>
|
||||||
|
#include <linux/cpumask.h>
|
||||||
|
#include <linux/debugfs.h>
|
||||||
|
#include <linux/gfp.h>
|
||||||
|
#include <linux/init.h>
|
||||||
|
#include <linux/io.h>
|
||||||
|
#include <linux/kernel.h>
|
||||||
|
#include <linux/module.h>
|
||||||
|
#include <linux/mailbox_client.h>
|
||||||
|
#include <linux/mutex.h>
|
||||||
|
#include <linux/percpu-defs.h>
|
||||||
|
#include <linux/platform_device.h>
|
||||||
|
#include <linux/smp.h>
|
||||||
|
#include <linux/topology.h>
|
||||||
|
#include <linux/workqueue.h>
|
||||||
|
|
||||||
|
#include <asm/cpu_device_id.h>
|
||||||
|
|
||||||
|
#include <acpi/pcc.h>
|
||||||
|
#include <acpi/cppc_acpi.h>
|
||||||
|
|
||||||
|
#define AMD_HFI_DRIVER "amd_hfi"
|
||||||
|
#define AMD_HFI_MAILBOX_COUNT 1
|
||||||
|
#define AMD_HETERO_RANKING_TABLE_VER 2
|
||||||
|
|
||||||
|
#define AMD_HETERO_CPUID_27 0x80000027
|
||||||
|
|
||||||
|
static struct platform_device *device;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* struct amd_shmem_info - Shared memory table for AMD HFI
|
||||||
|
*
|
||||||
|
* @header: The PCCT table header including signature, length flags and command.
|
||||||
|
* @version_number: Version number of the table
|
||||||
|
* @n_logical_processors: Number of logical processors
|
||||||
|
* @n_capabilities: Number of ranking dimensions (performance, efficiency, etc)
|
||||||
|
* @table_update_context: Command being sent over the subspace
|
||||||
|
* @n_bitmaps: Number of 32-bit bitmaps to enumerate all the APIC IDs
|
||||||
|
* This is based on the maximum APIC ID enumerated in the system
|
||||||
|
* @reserved: 24 bit spare
|
||||||
|
* @table_data: Bit Map(s) of enabled logical processors
|
||||||
|
* Followed by the ranking data for each logical processor
|
||||||
|
*/
|
||||||
|
struct amd_shmem_info {
|
||||||
|
struct acpi_pcct_ext_pcc_shared_memory header;
|
||||||
|
u32 version_number :8,
|
||||||
|
n_logical_processors :8,
|
||||||
|
n_capabilities :8,
|
||||||
|
table_update_context :8;
|
||||||
|
u32 n_bitmaps :8,
|
||||||
|
reserved :24;
|
||||||
|
u32 table_data[];
|
||||||
|
};
|
||||||
|
|
||||||
|
struct amd_hfi_data {
|
||||||
|
const char *name;
|
||||||
|
struct device *dev;
|
||||||
|
|
||||||
|
/* PCCT table related */
|
||||||
|
struct pcc_mbox_chan *pcc_chan;
|
||||||
|
void __iomem *pcc_comm_addr;
|
||||||
|
struct acpi_subtable_header *pcct_entry;
|
||||||
|
struct amd_shmem_info *shmem;
|
||||||
|
|
||||||
|
struct dentry *dbgfs_dir;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* struct amd_hfi_classes - HFI class capabilities per CPU
|
||||||
|
* @perf: Performance capability
|
||||||
|
* @eff: Power efficiency capability
|
||||||
|
*
|
||||||
|
* Capabilities of a logical processor in the ranking table. These capabilities
|
||||||
|
* are unitless and specific to each HFI class.
|
||||||
|
*/
|
||||||
|
struct amd_hfi_classes {
|
||||||
|
u32 perf;
|
||||||
|
u32 eff;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* struct amd_hfi_cpuinfo - HFI workload class info per CPU
|
||||||
|
* @cpu: CPU index
|
||||||
|
* @apic_id: APIC id of the current CPU
|
||||||
|
* @cpus: mask of CPUs associated with amd_hfi_cpuinfo
|
||||||
|
* @class_index: workload class ID index
|
||||||
|
* @nr_class: max number of workload class supported
|
||||||
|
* @ipcc_scores: ipcc scores for each class
|
||||||
|
* @amd_hfi_classes: current CPU workload class ranking data
|
||||||
|
*
|
||||||
|
* Parameters of a logical processor linked with hardware feedback class.
|
||||||
|
*/
|
||||||
|
struct amd_hfi_cpuinfo {
|
||||||
|
int cpu;
|
||||||
|
u32 apic_id;
|
||||||
|
cpumask_var_t cpus;
|
||||||
|
s16 class_index;
|
||||||
|
u8 nr_class;
|
||||||
|
int *ipcc_scores;
|
||||||
|
struct amd_hfi_classes *amd_hfi_classes;
|
||||||
|
};
|
||||||
|
|
||||||
|
static DEFINE_PER_CPU(struct amd_hfi_cpuinfo, amd_hfi_cpuinfo) = {.class_index = -1};
|
||||||
|
|
||||||
|
static DEFINE_MUTEX(hfi_cpuinfo_lock);
|
||||||
|
|
||||||
|
static void amd_hfi_sched_itmt_work(struct work_struct *work)
|
||||||
|
{
|
||||||
|
sched_set_itmt_support();
|
||||||
|
}
|
||||||
|
static DECLARE_WORK(sched_amd_hfi_itmt_work, amd_hfi_sched_itmt_work);
|
||||||
|
|
||||||
|
static int find_cpu_index_by_apicid(unsigned int target_apicid)
|
||||||
|
{
|
||||||
|
int cpu_index;
|
||||||
|
|
||||||
|
for_each_possible_cpu(cpu_index) {
|
||||||
|
struct cpuinfo_x86 *info = &cpu_data(cpu_index);
|
||||||
|
|
||||||
|
if (info->topo.apicid == target_apicid) {
|
||||||
|
pr_debug("match APIC id %u for CPU index: %d\n",
|
||||||
|
info->topo.apicid, cpu_index);
|
||||||
|
return cpu_index;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return -ENODEV;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int amd_hfi_fill_metadata(struct amd_hfi_data *amd_hfi_data)
|
||||||
|
{
|
||||||
|
struct acpi_pcct_ext_pcc_slave *pcct_ext =
|
||||||
|
(struct acpi_pcct_ext_pcc_slave *)amd_hfi_data->pcct_entry;
|
||||||
|
void __iomem *pcc_comm_addr;
|
||||||
|
u32 apic_start = 0;
|
||||||
|
|
||||||
|
pcc_comm_addr = acpi_os_ioremap(amd_hfi_data->pcc_chan->shmem_base_addr,
|
||||||
|
amd_hfi_data->pcc_chan->shmem_size);
|
||||||
|
if (!pcc_comm_addr) {
|
||||||
|
dev_err(amd_hfi_data->dev, "failed to ioremap PCC common region mem\n");
|
||||||
|
return -ENOMEM;
|
||||||
|
}
|
||||||
|
|
||||||
|
memcpy_fromio(amd_hfi_data->shmem, pcc_comm_addr, pcct_ext->length);
|
||||||
|
iounmap(pcc_comm_addr);
|
||||||
|
|
||||||
|
if (amd_hfi_data->shmem->header.signature != PCC_SIGNATURE) {
|
||||||
|
dev_err(amd_hfi_data->dev, "invalid signature in shared memory\n");
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
if (amd_hfi_data->shmem->version_number != AMD_HETERO_RANKING_TABLE_VER) {
|
||||||
|
dev_err(amd_hfi_data->dev, "invalid version %d\n",
|
||||||
|
amd_hfi_data->shmem->version_number);
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (unsigned int i = 0; i < amd_hfi_data->shmem->n_bitmaps; i++) {
|
||||||
|
u32 bitmap = amd_hfi_data->shmem->table_data[i];
|
||||||
|
|
||||||
|
for (unsigned int j = 0; j < BITS_PER_TYPE(u32); j++) {
|
||||||
|
u32 apic_id = i * BITS_PER_TYPE(u32) + j;
|
||||||
|
struct amd_hfi_cpuinfo *info;
|
||||||
|
int cpu_index, apic_index;
|
||||||
|
|
||||||
|
if (!(bitmap & BIT(j)))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
cpu_index = find_cpu_index_by_apicid(apic_id);
|
||||||
|
if (cpu_index < 0) {
|
||||||
|
dev_warn(amd_hfi_data->dev, "APIC ID %u not found\n", apic_id);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
info = per_cpu_ptr(&amd_hfi_cpuinfo, cpu_index);
|
||||||
|
info->apic_id = apic_id;
|
||||||
|
|
||||||
|
/* Fill the ranking data for each logical processor */
|
||||||
|
info = per_cpu_ptr(&amd_hfi_cpuinfo, cpu_index);
|
||||||
|
apic_index = apic_start * info->nr_class * 2;
|
||||||
|
for (unsigned int k = 0; k < info->nr_class; k++) {
|
||||||
|
u32 *table = amd_hfi_data->shmem->table_data +
|
||||||
|
amd_hfi_data->shmem->n_bitmaps +
|
||||||
|
i * info->nr_class;
|
||||||
|
|
||||||
|
info->amd_hfi_classes[k].eff = table[apic_index + 2 * k];
|
||||||
|
info->amd_hfi_classes[k].perf = table[apic_index + 2 * k + 1];
|
||||||
|
}
|
||||||
|
apic_start++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int amd_hfi_alloc_class_data(struct platform_device *pdev)
|
||||||
|
{
|
||||||
|
struct amd_hfi_cpuinfo *hfi_cpuinfo;
|
||||||
|
struct device *dev = &pdev->dev;
|
||||||
|
u32 nr_class_id;
|
||||||
|
int idx;
|
||||||
|
|
||||||
|
nr_class_id = cpuid_eax(AMD_HETERO_CPUID_27);
|
||||||
|
if (nr_class_id > 255) {
|
||||||
|
dev_err(dev, "number of supported classes too large: %d\n",
|
||||||
|
nr_class_id);
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
for_each_possible_cpu(idx) {
|
||||||
|
struct amd_hfi_classes *classes;
|
||||||
|
int *ipcc_scores;
|
||||||
|
|
||||||
|
classes = devm_kcalloc(dev,
|
||||||
|
nr_class_id,
|
||||||
|
sizeof(struct amd_hfi_classes),
|
||||||
|
GFP_KERNEL);
|
||||||
|
if (!classes)
|
||||||
|
return -ENOMEM;
|
||||||
|
ipcc_scores = devm_kcalloc(dev, nr_class_id, sizeof(int), GFP_KERNEL);
|
||||||
|
if (!ipcc_scores)
|
||||||
|
return -ENOMEM;
|
||||||
|
hfi_cpuinfo = per_cpu_ptr(&amd_hfi_cpuinfo, idx);
|
||||||
|
hfi_cpuinfo->amd_hfi_classes = classes;
|
||||||
|
hfi_cpuinfo->ipcc_scores = ipcc_scores;
|
||||||
|
hfi_cpuinfo->nr_class = nr_class_id;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void amd_hfi_remove(struct platform_device *pdev)
|
||||||
|
{
|
||||||
|
struct amd_hfi_data *dev = platform_get_drvdata(pdev);
|
||||||
|
|
||||||
|
debugfs_remove_recursive(dev->dbgfs_dir);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int amd_set_hfi_ipcc_score(struct amd_hfi_cpuinfo *hfi_cpuinfo, int cpu)
|
||||||
|
{
|
||||||
|
for (int i = 0; i < hfi_cpuinfo->nr_class; i++)
|
||||||
|
WRITE_ONCE(hfi_cpuinfo->ipcc_scores[i],
|
||||||
|
hfi_cpuinfo->amd_hfi_classes[i].perf);
|
||||||
|
|
||||||
|
sched_set_itmt_core_prio(hfi_cpuinfo->ipcc_scores[0], cpu);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int amd_hfi_set_state(unsigned int cpu, bool state)
|
||||||
|
{
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
ret = wrmsrq_on_cpu(cpu, MSR_AMD_WORKLOAD_CLASS_CONFIG, state ? 1 : 0);
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
return wrmsrq_on_cpu(cpu, MSR_AMD_WORKLOAD_HRST, 0x1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* amd_hfi_online() - Enable workload classification on @cpu
|
||||||
|
* @cpu: CPU in which the workload classification will be enabled
|
||||||
|
*
|
||||||
|
* Return: 0 on success, negative error code on failure.
|
||||||
|
*/
|
||||||
|
static int amd_hfi_online(unsigned int cpu)
|
||||||
|
{
|
||||||
|
struct amd_hfi_cpuinfo *hfi_info = per_cpu_ptr(&amd_hfi_cpuinfo, cpu);
|
||||||
|
struct amd_hfi_classes *hfi_classes;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
if (WARN_ON_ONCE(!hfi_info))
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Check if @cpu as an associated, initialized and ranking data must
|
||||||
|
* be filled.
|
||||||
|
*/
|
||||||
|
hfi_classes = hfi_info->amd_hfi_classes;
|
||||||
|
if (!hfi_classes)
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
guard(mutex)(&hfi_cpuinfo_lock);
|
||||||
|
|
||||||
|
if (!zalloc_cpumask_var(&hfi_info->cpus, GFP_KERNEL))
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
cpumask_set_cpu(cpu, hfi_info->cpus);
|
||||||
|
|
||||||
|
ret = amd_hfi_set_state(cpu, true);
|
||||||
|
if (ret)
|
||||||
|
pr_err("WCT enable failed for CPU %u\n", cpu);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* amd_hfi_offline() - Disable workload classification on @cpu
|
||||||
|
* @cpu: CPU in which the workload classification will be disabled
|
||||||
|
*
|
||||||
|
* Remove @cpu from those covered by its HFI instance.
|
||||||
|
*
|
||||||
|
* Return: 0 on success, negative error code on failure
|
||||||
|
*/
|
||||||
|
static int amd_hfi_offline(unsigned int cpu)
|
||||||
|
{
|
||||||
|
struct amd_hfi_cpuinfo *hfi_info = &per_cpu(amd_hfi_cpuinfo, cpu);
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
if (WARN_ON_ONCE(!hfi_info))
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
guard(mutex)(&hfi_cpuinfo_lock);
|
||||||
|
|
||||||
|
ret = amd_hfi_set_state(cpu, false);
|
||||||
|
if (ret)
|
||||||
|
pr_err("WCT disable failed for CPU %u\n", cpu);
|
||||||
|
|
||||||
|
free_cpumask_var(hfi_info->cpus);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int update_hfi_ipcc_scores(void)
|
||||||
|
{
|
||||||
|
int cpu;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
for_each_possible_cpu(cpu) {
|
||||||
|
struct amd_hfi_cpuinfo *hfi_cpuinfo = per_cpu_ptr(&amd_hfi_cpuinfo, cpu);
|
||||||
|
|
||||||
|
ret = amd_set_hfi_ipcc_score(hfi_cpuinfo, cpu);
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int amd_hfi_metadata_parser(struct platform_device *pdev,
|
||||||
|
struct amd_hfi_data *amd_hfi_data)
|
||||||
|
{
|
||||||
|
struct acpi_pcct_ext_pcc_slave *pcct_ext;
|
||||||
|
struct acpi_subtable_header *pcct_entry;
|
||||||
|
struct mbox_chan *pcc_mbox_channels;
|
||||||
|
struct acpi_table_header *pcct_tbl;
|
||||||
|
struct pcc_mbox_chan *pcc_chan;
|
||||||
|
acpi_status status;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
pcc_mbox_channels = devm_kcalloc(&pdev->dev, AMD_HFI_MAILBOX_COUNT,
|
||||||
|
sizeof(*pcc_mbox_channels), GFP_KERNEL);
|
||||||
|
if (!pcc_mbox_channels)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
pcc_chan = devm_kcalloc(&pdev->dev, AMD_HFI_MAILBOX_COUNT,
|
||||||
|
sizeof(*pcc_chan), GFP_KERNEL);
|
||||||
|
if (!pcc_chan)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
status = acpi_get_table(ACPI_SIG_PCCT, 0, &pcct_tbl);
|
||||||
|
if (ACPI_FAILURE(status) || !pcct_tbl)
|
||||||
|
return -ENODEV;
|
||||||
|
|
||||||
|
/* get pointer to the first PCC subspace entry */
|
||||||
|
pcct_entry = (struct acpi_subtable_header *) (
|
||||||
|
(unsigned long)pcct_tbl + sizeof(struct acpi_table_pcct));
|
||||||
|
|
||||||
|
pcc_chan->mchan = &pcc_mbox_channels[0];
|
||||||
|
|
||||||
|
amd_hfi_data->pcc_chan = pcc_chan;
|
||||||
|
amd_hfi_data->pcct_entry = pcct_entry;
|
||||||
|
pcct_ext = (struct acpi_pcct_ext_pcc_slave *)pcct_entry;
|
||||||
|
|
||||||
|
if (pcct_ext->length <= 0)
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
amd_hfi_data->shmem = devm_kzalloc(amd_hfi_data->dev, pcct_ext->length, GFP_KERNEL);
|
||||||
|
if (!amd_hfi_data->shmem)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
pcc_chan->shmem_base_addr = pcct_ext->base_address;
|
||||||
|
pcc_chan->shmem_size = pcct_ext->length;
|
||||||
|
|
||||||
|
/* parse the shared memory info from the PCCT table */
|
||||||
|
ret = amd_hfi_fill_metadata(amd_hfi_data);
|
||||||
|
|
||||||
|
acpi_put_table(pcct_tbl);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int class_capabilities_show(struct seq_file *s, void *unused)
|
||||||
|
{
|
||||||
|
u32 cpu, idx;
|
||||||
|
|
||||||
|
seq_puts(s, "CPU #\tWLC\tPerf\tEff\n");
|
||||||
|
for_each_possible_cpu(cpu) {
|
||||||
|
struct amd_hfi_cpuinfo *hfi_cpuinfo = per_cpu_ptr(&amd_hfi_cpuinfo, cpu);
|
||||||
|
|
||||||
|
seq_printf(s, "%d", cpu);
|
||||||
|
for (idx = 0; idx < hfi_cpuinfo->nr_class; idx++) {
|
||||||
|
seq_printf(s, "\t%u\t%u\t%u\n", idx,
|
||||||
|
hfi_cpuinfo->amd_hfi_classes[idx].perf,
|
||||||
|
hfi_cpuinfo->amd_hfi_classes[idx].eff);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
DEFINE_SHOW_ATTRIBUTE(class_capabilities);
|
||||||
|
|
||||||
|
static int amd_hfi_pm_resume(struct device *dev)
|
||||||
|
{
|
||||||
|
int ret, cpu;
|
||||||
|
|
||||||
|
for_each_online_cpu(cpu) {
|
||||||
|
ret = amd_hfi_set_state(cpu, true);
|
||||||
|
if (ret < 0) {
|
||||||
|
dev_err(dev, "failed to enable workload class config: %d\n", ret);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int amd_hfi_pm_suspend(struct device *dev)
|
||||||
|
{
|
||||||
|
int ret, cpu;
|
||||||
|
|
||||||
|
for_each_online_cpu(cpu) {
|
||||||
|
ret = amd_hfi_set_state(cpu, false);
|
||||||
|
if (ret < 0) {
|
||||||
|
dev_err(dev, "failed to disable workload class config: %d\n", ret);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static DEFINE_SIMPLE_DEV_PM_OPS(amd_hfi_pm_ops, amd_hfi_pm_suspend, amd_hfi_pm_resume);
|
||||||
|
|
||||||
|
static const struct acpi_device_id amd_hfi_platform_match[] = {
|
||||||
|
{"AMDI0104", 0},
|
||||||
|
{ }
|
||||||
|
};
|
||||||
|
MODULE_DEVICE_TABLE(acpi, amd_hfi_platform_match);
|
||||||
|
|
||||||
|
static int amd_hfi_probe(struct platform_device *pdev)
|
||||||
|
{
|
||||||
|
struct amd_hfi_data *amd_hfi_data;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
if (!acpi_match_device(amd_hfi_platform_match, &pdev->dev))
|
||||||
|
return -ENODEV;
|
||||||
|
|
||||||
|
amd_hfi_data = devm_kzalloc(&pdev->dev, sizeof(*amd_hfi_data), GFP_KERNEL);
|
||||||
|
if (!amd_hfi_data)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
amd_hfi_data->dev = &pdev->dev;
|
||||||
|
platform_set_drvdata(pdev, amd_hfi_data);
|
||||||
|
|
||||||
|
ret = amd_hfi_alloc_class_data(pdev);
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
ret = amd_hfi_metadata_parser(pdev, amd_hfi_data);
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
ret = update_hfi_ipcc_scores();
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Tasks will already be running at the time this happens. This is
|
||||||
|
* OK because rankings will be adjusted by the callbacks.
|
||||||
|
*/
|
||||||
|
ret = cpuhp_setup_state(CPUHP_AP_ONLINE_DYN, "x86/amd_hfi:online",
|
||||||
|
amd_hfi_online, amd_hfi_offline);
|
||||||
|
if (ret < 0)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
schedule_work(&sched_amd_hfi_itmt_work);
|
||||||
|
|
||||||
|
amd_hfi_data->dbgfs_dir = debugfs_create_dir("amd_hfi", arch_debugfs_dir);
|
||||||
|
debugfs_create_file("class_capabilities", 0644, amd_hfi_data->dbgfs_dir, pdev,
|
||||||
|
&class_capabilities_fops);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct platform_driver amd_hfi_driver = {
|
||||||
|
.driver = {
|
||||||
|
.name = AMD_HFI_DRIVER,
|
||||||
|
.owner = THIS_MODULE,
|
||||||
|
.pm = &amd_hfi_pm_ops,
|
||||||
|
.acpi_match_table = ACPI_PTR(amd_hfi_platform_match),
|
||||||
|
},
|
||||||
|
.probe = amd_hfi_probe,
|
||||||
|
.remove = amd_hfi_remove,
|
||||||
|
};
|
||||||
|
|
||||||
|
static int __init amd_hfi_init(void)
|
||||||
|
{
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
if (acpi_disabled ||
|
||||||
|
!cpu_feature_enabled(X86_FEATURE_AMD_HTR_CORES) ||
|
||||||
|
!cpu_feature_enabled(X86_FEATURE_AMD_WORKLOAD_CLASS))
|
||||||
|
return -ENODEV;
|
||||||
|
|
||||||
|
device = platform_device_register_simple(AMD_HFI_DRIVER, -1, NULL, 0);
|
||||||
|
if (IS_ERR(device)) {
|
||||||
|
pr_err("unable to register HFI platform device\n");
|
||||||
|
return PTR_ERR(device);
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = platform_driver_register(&amd_hfi_driver);
|
||||||
|
if (ret)
|
||||||
|
pr_err("failed to register HFI driver\n");
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static __exit void amd_hfi_exit(void)
|
||||||
|
{
|
||||||
|
platform_driver_unregister(&amd_hfi_driver);
|
||||||
|
platform_device_unregister(device);
|
||||||
|
}
|
||||||
|
module_init(amd_hfi_init);
|
||||||
|
module_exit(amd_hfi_exit);
|
||||||
|
|
||||||
|
MODULE_LICENSE("GPL");
|
||||||
|
MODULE_DESCRIPTION("AMD Hardware Feedback Interface Driver");
|
Loading…
Reference in New Issue
Block a user