2019-05-27 08:55:06 +02:00
// SPDX-License-Identifier: GPL-2.0-or-later
2005-04-16 15:20:36 -07:00
/*
* processor_thermal . c - Passive cooling submodule of the ACPI processor driver
*
* Copyright ( C ) 2001 , 2002 Andy Grover < andrew . grover @ intel . com >
* Copyright ( C ) 2001 , 2002 Paul Diefenbaugh < paul . s . diefenbaugh @ intel . com >
* Copyright ( C ) 2004 Dominik Brodowski < linux @ brodo . de >
* Copyright ( C ) 2004 Anil S Keshavamurthy < anil . s . keshavamurthy @ intel . com >
* - Added processor hotplug support
*/
# include <linux/kernel.h>
# include <linux/module.h>
# include <linux/init.h>
# include <linux/cpufreq.h>
2013-12-03 08:49:16 +08:00
# include <linux/acpi.h>
2005-04-16 15:20:36 -07:00
# include <acpi/processor.h>
2016-12-24 11:46:01 -08:00
# include <linux/uaccess.h>
2005-04-16 15:20:36 -07:00
2009-07-28 16:45:54 -04:00
# define PREFIX "ACPI: "
2005-04-16 15:20:36 -07:00
# define ACPI_PROCESSOR_CLASS "processor"
# define _COMPONENT ACPI_PROCESSOR_COMPONENT
2007-02-12 22:42:12 -05:00
ACPI_MODULE_NAME ( " processor_thermal " ) ;
2005-04-16 15:20:36 -07:00
# ifdef CONFIG_CPU_FREQ
/* If a passive cooling situation is detected, primarily CPUfreq is used, as it
* offers ( in most cases ) voltage scaling in addition to frequency scaling , and
* thus a cubic ( instead of linear ) reduction of energy . Also , we allow for
* _any_ cpufreq driver and not only the acpi - cpufreq driver .
*/
2008-01-17 15:51:23 +08:00
# define CPUFREQ_THERMAL_MIN_STEP 0
# define CPUFREQ_THERMAL_MAX_STEP 3
2008-03-05 08:31:29 -08:00
static DEFINE_PER_CPU ( unsigned int , cpufreq_thermal_reduction_pctg ) ;
2005-04-16 15:20:36 -07:00
2012-02-06 08:17:11 -08:00
# define reduction_pctg(cpu) \
per_cpu ( cpufreq_thermal_reduction_pctg , phys_package_first_cpu ( cpu ) )
/*
* Emulate " per package data " using per cpu data ( which should really be
* provided elsewhere )
*
* Note we can lose a CPU on cpu hotunplug , in this case we forget the state
* temporarily . Fortunately that ' s not a big issue here ( I hope )
*/
static int phys_package_first_cpu ( int cpu )
{
int i ;
int id = topology_physical_package_id ( cpu ) ;
for_each_online_cpu ( i )
if ( topology_physical_package_id ( i ) = = id )
return i ;
return 0 ;
}
2005-04-16 15:20:36 -07:00
static int cpu_has_cpufreq ( unsigned int cpu )
{
struct cpufreq_policy policy ;
2019-08-28 14:20:13 +05:30
if ( ! acpi_processor_cpufreq_init | | cpufreq_get_policy ( & policy , cpu ) )
2005-12-21 01:29:00 -05:00
return 0 ;
return 1 ;
2005-04-16 15:20:36 -07:00
}
2008-01-17 15:51:23 +08:00
static int cpufreq_get_max_state ( unsigned int cpu )
{
if ( ! cpu_has_cpufreq ( cpu ) )
return 0 ;
return CPUFREQ_THERMAL_MAX_STEP ;
}
static int cpufreq_get_cur_state ( unsigned int cpu )
{
if ( ! cpu_has_cpufreq ( cpu ) )
return 0 ;
2012-02-06 08:17:11 -08:00
return reduction_pctg ( cpu ) ;
2008-01-17 15:51:23 +08:00
}
static int cpufreq_set_cur_state ( unsigned int cpu , int state )
{
2019-08-28 14:20:13 +05:30
struct cpufreq_policy * policy ;
struct acpi_processor * pr ;
unsigned long max_freq ;
int i , ret ;
2012-02-06 08:17:11 -08:00
2008-01-17 15:51:23 +08:00
if ( ! cpu_has_cpufreq ( cpu ) )
return 0 ;
2012-02-06 08:17:11 -08:00
reduction_pctg ( cpu ) = state ;
/*
* Update all the CPUs in the same package because they all
* contribute to the temperature and often share the same
* frequency .
*/
for_each_online_cpu ( i ) {
2019-08-28 14:20:13 +05:30
if ( topology_physical_package_id ( i ) ! =
2012-02-06 08:17:11 -08:00
topology_physical_package_id ( cpu ) )
2019-08-28 14:20:13 +05:30
continue ;
pr = per_cpu ( processors , i ) ;
if ( unlikely ( ! dev_pm_qos_request_active ( & pr - > thermal_req ) ) )
continue ;
policy = cpufreq_cpu_get ( i ) ;
if ( ! policy )
return - EINVAL ;
max_freq = ( policy - > cpuinfo . max_freq * ( 100 - reduction_pctg ( i ) * 20 ) ) / 100 ;
cpufreq_cpu_put ( policy ) ;
ret = dev_pm_qos_update_request ( & pr - > thermal_req , max_freq ) ;
if ( ret < 0 ) {
pr_warn ( " Failed to update thermal freq constraint: CPU%d (%d) \n " ,
pr - > id , ret ) ;
}
2012-02-06 08:17:11 -08:00
}
2008-01-17 15:51:23 +08:00
return 0 ;
}
2019-08-28 14:20:13 +05:30
void acpi_thermal_cpufreq_init ( int cpu )
2005-08-05 00:44:28 -04:00
{
2019-08-28 14:20:13 +05:30
struct acpi_processor * pr = per_cpu ( processors , cpu ) ;
int ret ;
ret = dev_pm_qos_add_request ( get_cpu_device ( cpu ) ,
& pr - > thermal_req , DEV_PM_QOS_MAX_FREQUENCY ,
INT_MAX ) ;
if ( ret < 0 ) {
pr_err ( " Failed to add freq constraint for CPU%d (%d) \n " , cpu ,
ret ) ;
return ;
}
2005-04-16 15:20:36 -07:00
}
2019-08-28 14:20:13 +05:30
void acpi_thermal_cpufreq_exit ( int cpu )
2005-08-05 00:44:28 -04:00
{
2019-08-28 14:20:13 +05:30
struct acpi_processor * pr = per_cpu ( processors , cpu ) ;
2005-04-16 15:20:36 -07:00
2019-08-28 14:20:13 +05:30
dev_pm_qos_remove_request ( & pr - > thermal_req ) ;
2005-04-16 15:20:36 -07:00
}
2005-08-05 00:44:28 -04:00
# else /* ! CONFIG_CPU_FREQ */
2008-01-17 15:51:23 +08:00
static int cpufreq_get_max_state ( unsigned int cpu )
{
return 0 ;
}
static int cpufreq_get_cur_state ( unsigned int cpu )
{
return 0 ;
}
static int cpufreq_set_cur_state ( unsigned int cpu , int state )
{
return 0 ;
}
2005-04-16 15:20:36 -07:00
# endif
2013-12-05 11:14:16 -07:00
/* thermal cooling device callbacks */
2008-01-17 15:51:23 +08:00
static int acpi_processor_max_state ( struct acpi_processor * pr )
{
int max_state = 0 ;
/*
* There exists four states according to
2013-12-05 11:14:16 -07:00
* cpufreq_thermal_reduction_pctg . 0 , 1 , 2 , 3
2008-01-17 15:51:23 +08:00
*/
max_state + = cpufreq_get_max_state ( pr - > id ) ;
if ( pr - > flags . throttling )
max_state + = ( pr - > throttling . state_count - 1 ) ;
return max_state ;
}
static int
2008-11-27 17:48:13 +00:00
processor_get_max_state ( struct thermal_cooling_device * cdev ,
unsigned long * state )
2008-01-17 15:51:23 +08:00
{
struct acpi_device * device = cdev - > devdata ;
2013-03-25 10:50:06 +00:00
struct acpi_processor * pr ;
2008-01-17 15:51:23 +08:00
2013-03-25 10:50:06 +00:00
if ( ! device )
return - EINVAL ;
pr = acpi_driver_data ( device ) ;
if ( ! pr )
2008-01-17 15:51:23 +08:00
return - EINVAL ;
2008-11-27 17:48:13 +00:00
* state = acpi_processor_max_state ( pr ) ;
return 0 ;
2008-01-17 15:51:23 +08:00
}
static int
2008-11-27 17:48:13 +00:00
processor_get_cur_state ( struct thermal_cooling_device * cdev ,
unsigned long * cur_state )
2008-01-17 15:51:23 +08:00
{
struct acpi_device * device = cdev - > devdata ;
2013-03-25 10:50:06 +00:00
struct acpi_processor * pr ;
2008-01-17 15:51:23 +08:00
2013-03-25 10:50:06 +00:00
if ( ! device )
return - EINVAL ;
pr = acpi_driver_data ( device ) ;
if ( ! pr )
2008-01-17 15:51:23 +08:00
return - EINVAL ;
2008-11-27 17:48:13 +00:00
* cur_state = cpufreq_get_cur_state ( pr - > id ) ;
2008-01-17 15:51:23 +08:00
if ( pr - > flags . throttling )
2008-11-27 17:48:13 +00:00
* cur_state + = pr - > throttling . state ;
return 0 ;
2008-01-17 15:51:23 +08:00
}
static int
2008-11-27 17:48:13 +00:00
processor_set_cur_state ( struct thermal_cooling_device * cdev ,
unsigned long state )
2008-01-17 15:51:23 +08:00
{
struct acpi_device * device = cdev - > devdata ;
2013-03-25 10:50:06 +00:00
struct acpi_processor * pr ;
2008-01-17 15:51:23 +08:00
int result = 0 ;
int max_pstate ;
2013-03-25 10:50:06 +00:00
if ( ! device )
return - EINVAL ;
pr = acpi_driver_data ( device ) ;
if ( ! pr )
2008-01-17 15:51:23 +08:00
return - EINVAL ;
max_pstate = cpufreq_get_max_state ( pr - > id ) ;
if ( state > acpi_processor_max_state ( pr ) )
return - EINVAL ;
if ( state < = max_pstate ) {
if ( pr - > flags . throttling & & pr - > throttling . state )
2009-08-26 14:29:29 -07:00
result = acpi_processor_set_throttling ( pr , 0 , false ) ;
2008-01-17 15:51:23 +08:00
cpufreq_set_cur_state ( pr - > id , state ) ;
} else {
cpufreq_set_cur_state ( pr - > id , max_pstate ) ;
result = acpi_processor_set_throttling ( pr ,
2009-08-26 14:29:29 -07:00
state - max_pstate , false ) ;
2008-01-17 15:51:23 +08:00
}
return result ;
}
2011-06-25 21:07:52 +04:00
const struct thermal_cooling_device_ops processor_cooling_ops = {
2008-01-17 15:51:23 +08:00
. get_max_state = processor_get_max_state ,
. get_cur_state = processor_get_cur_state ,
. set_cur_state = processor_set_cur_state ,
} ;