2019-07-10 16:44:30 +03:00
// 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>
2019-07-10 16:44:34 +03:00
# include <linux/platform_device.h>
2019-07-10 16:44:30 +03:00
# 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 } ,
2019-07-10 16:44:32 +03:00
. limits [ RAPL_DOMAIN_PACKAGE ] = 2 ,
2019-07-10 16:44:30 +03:00
} ;
/* 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 )
{
2019-07-10 16:44:31 +03:00
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 ) ;
2019-07-10 16:44:30 +03:00
return - EIO ;
}
ra - > value & = ra - > mask ;
return 0 ;
}
static void rapl_msr_update_func ( void * info )
{
struct reg_action * ra = info ;
2019-07-10 16:44:31 +03:00
u32 msr = ( u32 ) ra - > reg ;
2019-07-10 16:44:30 +03:00
u64 val ;
2019-07-10 16:44:31 +03:00
ra - > err = rdmsrl_safe ( msr , & val ) ;
2019-07-10 16:44:30 +03:00
if ( ra - > err )
return ;
val & = ~ ra - > mask ;
val | = ra - > value ;
2019-07-10 16:44:31 +03:00
ra - > err = wrmsrl_safe ( msr , val ) ;
2019-07-10 16:44:30 +03:00
}
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 ;
}
2019-07-10 16:44:34 +03:00
static int rapl_msr_probe ( struct platform_device * pdev )
2019-07-10 16:44:30 +03:00
{
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 ;
}
2019-07-10 16:44:34 +03:00
static int rapl_msr_remove ( struct platform_device * pdev )
2019-07-10 16:44:30 +03:00
{
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 ) ;
2019-07-10 16:44:34 +03:00
return 0 ;
2019-07-10 16:44:30 +03:00
}
2019-07-10 16:44:34 +03:00
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 ) ;
2019-07-10 16:44:30 +03:00
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 " ) ;