linux/drivers/powercap/intel_rapl_msr.c
Zhang Rui abcfaeb3f5 intel_rapl: Fix module autoloading issue
intel_rapl driver used to have a list of cpuids, which is used to
1. check if the processor support RAPL MSRs
2. do some cpu model specific setting
3. module autoloading

Now, the cpu model specific setting are moved to intel_rapl_common.c as
part of the common code, because the setup is also needed by RAPL MMIO
interface on those platforms.
But removing the cpuid list from intel_rapl MSR interface driver results
in that the driver can not be loaded automatically.

Maintaining another copy of the cpuid list in intel_rapl_msr.c does not make
sense because it increases the complexity when enabling RAPL support on a
new cpu model.

Fix the problem by creating an "intel_rapl_msr" platform device in the
common code, and make RAPL MSR interface driver (intel_rapl_msr.c) probe the
platform device directly.

Reviewed-by: Pandruvada, Srinivas <srinivas.pandruvada@intel.com>
Tested-by: Pandruvada, Srinivas <srinivas.pandruvada@intel.com>
Signed-off-by: Zhang Rui <rui.zhang@intel.com>
Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
2019-07-11 15:08:58 +02:00

184 lines
4.6 KiB
C

// SPDX-License-Identifier: GPL-2.0-only
/*
* Intel Running Average Power Limit (RAPL) Driver via MSR interface
* Copyright (c) 2019, Intel Corporation.
*/
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/list.h>
#include <linux/types.h>
#include <linux/device.h>
#include <linux/slab.h>
#include <linux/log2.h>
#include <linux/bitmap.h>
#include <linux/delay.h>
#include <linux/sysfs.h>
#include <linux/cpu.h>
#include <linux/powercap.h>
#include <linux/suspend.h>
#include <linux/intel_rapl.h>
#include <linux/processor.h>
#include <linux/platform_device.h>
#include <asm/iosf_mbi.h>
#include <asm/cpu_device_id.h>
#include <asm/intel-family.h>
/* Local defines */
#define MSR_PLATFORM_POWER_LIMIT 0x0000065C
/* private data for RAPL MSR Interface */
static struct rapl_if_priv rapl_msr_priv = {
.reg_unit = MSR_RAPL_POWER_UNIT,
.regs[RAPL_DOMAIN_PACKAGE] = {
MSR_PKG_POWER_LIMIT, MSR_PKG_ENERGY_STATUS, MSR_PKG_PERF_STATUS, 0, MSR_PKG_POWER_INFO },
.regs[RAPL_DOMAIN_PP0] = {
MSR_PP0_POWER_LIMIT, MSR_PP0_ENERGY_STATUS, 0, MSR_PP0_POLICY, 0 },
.regs[RAPL_DOMAIN_PP1] = {
MSR_PP1_POWER_LIMIT, MSR_PP1_ENERGY_STATUS, 0, MSR_PP1_POLICY, 0 },
.regs[RAPL_DOMAIN_DRAM] = {
MSR_DRAM_POWER_LIMIT, MSR_DRAM_ENERGY_STATUS, MSR_DRAM_PERF_STATUS, 0, MSR_DRAM_POWER_INFO },
.regs[RAPL_DOMAIN_PLATFORM] = {
MSR_PLATFORM_POWER_LIMIT, MSR_PLATFORM_ENERGY_STATUS, 0, 0, 0},
.limits[RAPL_DOMAIN_PACKAGE] = 2,
};
/* Handles CPU hotplug on multi-socket systems.
* If a CPU goes online as the first CPU of the physical package
* we add the RAPL package to the system. Similarly, when the last
* CPU of the package is removed, we remove the RAPL package and its
* associated domains. Cooling devices are handled accordingly at
* per-domain level.
*/
static int rapl_cpu_online(unsigned int cpu)
{
struct rapl_package *rp;
rp = rapl_find_package_domain(cpu, &rapl_msr_priv);
if (!rp) {
rp = rapl_add_package(cpu, &rapl_msr_priv);
if (IS_ERR(rp))
return PTR_ERR(rp);
}
cpumask_set_cpu(cpu, &rp->cpumask);
return 0;
}
static int rapl_cpu_down_prep(unsigned int cpu)
{
struct rapl_package *rp;
int lead_cpu;
rp = rapl_find_package_domain(cpu, &rapl_msr_priv);
if (!rp)
return 0;
cpumask_clear_cpu(cpu, &rp->cpumask);
lead_cpu = cpumask_first(&rp->cpumask);
if (lead_cpu >= nr_cpu_ids)
rapl_remove_package(rp);
else if (rp->lead_cpu == cpu)
rp->lead_cpu = lead_cpu;
return 0;
}
static int rapl_msr_read_raw(int cpu, struct reg_action *ra)
{
u32 msr = (u32)ra->reg;
if (rdmsrl_safe_on_cpu(cpu, msr, &ra->value)) {
pr_debug("failed to read msr 0x%x on cpu %d\n", msr, cpu);
return -EIO;
}
ra->value &= ra->mask;
return 0;
}
static void rapl_msr_update_func(void *info)
{
struct reg_action *ra = info;
u32 msr = (u32)ra->reg;
u64 val;
ra->err = rdmsrl_safe(msr, &val);
if (ra->err)
return;
val &= ~ra->mask;
val |= ra->value;
ra->err = wrmsrl_safe(msr, val);
}
static int rapl_msr_write_raw(int cpu, struct reg_action *ra)
{
int ret;
ret = smp_call_function_single(cpu, rapl_msr_update_func, ra, 1);
if (WARN_ON_ONCE(ret))
return ret;
return ra->err;
}
static int rapl_msr_probe(struct platform_device *pdev)
{
int ret;
rapl_msr_priv.read_raw = rapl_msr_read_raw;
rapl_msr_priv.write_raw = rapl_msr_write_raw;
rapl_msr_priv.control_type = powercap_register_control_type(NULL, "intel-rapl", NULL);
if (IS_ERR(rapl_msr_priv.control_type)) {
pr_debug("failed to register powercap control_type.\n");
return PTR_ERR(rapl_msr_priv.control_type);
}
ret = cpuhp_setup_state(CPUHP_AP_ONLINE_DYN, "powercap/rapl:online",
rapl_cpu_online, rapl_cpu_down_prep);
if (ret < 0)
goto out;
rapl_msr_priv.pcap_rapl_online = ret;
/* Don't bail out if PSys is not supported */
rapl_add_platform_domain(&rapl_msr_priv);
return 0;
out:
if (ret)
powercap_unregister_control_type(rapl_msr_priv.control_type);
return ret;
}
static int rapl_msr_remove(struct platform_device *pdev)
{
cpuhp_remove_state(rapl_msr_priv.pcap_rapl_online);
rapl_remove_platform_domain(&rapl_msr_priv);
powercap_unregister_control_type(rapl_msr_priv.control_type);
return 0;
}
static const struct platform_device_id rapl_msr_ids[] = {
{ .name = "intel_rapl_msr", },
{}
};
MODULE_DEVICE_TABLE(platform, rapl_msr_ids);
static struct platform_driver intel_rapl_msr_driver = {
.probe = rapl_msr_probe,
.remove = rapl_msr_remove,
.id_table = rapl_msr_ids,
.driver = {
.name = "intel_rapl_msr",
},
};
module_platform_driver(intel_rapl_msr_driver);
MODULE_DESCRIPTION("Driver for Intel RAPL (Running Average Power Limit) control via MSR interface");
MODULE_AUTHOR("Zhang Rui <rui.zhang@intel.com>");
MODULE_LICENSE("GPL v2");