// SPDX-License-Identifier: GPL-2.0-or-later
/*
* processor_throttling . c - Throttling 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
*/
#define pr_fmt(fmt) "ACPI: " fmt
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/init.h>
#include <linux/sched.h>
#include <linux/cpufreq.h>
#include <linux/acpi.h>
#include <linux/uaccess.h>
#include <acpi/processor.h>
#include <asm /io.h>
#ifdef CONFIG_X86
#include <asm /msr.h>
#endif
/* ignore_tpc:
* 0 - > acpi processor driver doesn ' t ignore _ TPC values
* 1 - > acpi processor driver ignores _ TPC values
*/
static int ignore_tpc;
module_param(ignore_tpc, int , 0644 );
MODULE_PARM_DESC(ignore_tpc, "Disable broken BIOS _TPC throttling support" );
struct throttling_tstate {
unsigned int cpu; /* cpu nr */
int target_state; /* target T-state */
};
struct acpi_processor_throttling_arg {
struct acpi_processor *pr;
int target_state;
bool force;
};
#define THROTTLING_PRECHANGE (1 )
#define THROTTLING_POSTCHANGE (2 )
static int acpi_processor_get_throttling(struct acpi_processor *pr);
static int __acpi_processor_set_throttling(struct acpi_processor *pr,
int state, bool force, bool direct);
static int acpi_processor_update_tsd_coord(void )
{
int count_target;
int retval = 0 ;
unsigned int i, j;
cpumask_var_t covered_cpus;
struct acpi_processor *pr, *match_pr;
struct acpi_tsd_package *pdomain, *match_pdomain;
struct acpi_processor_throttling *pthrottling, *match_pthrottling;
if (!zalloc_cpumask_var(&covered_cpus, GFP_KERNEL))
return -ENOMEM;
/*
* Now that we have _ TSD data from all CPUs , lets setup T - state
* coordination between all CPUs .
*/
for_each_possible_cpu(i) {
pr = per_cpu(processors, i);
if (!pr)
continue ;
/* Basic validity check for domain info */
pthrottling = &(pr->throttling);
/*
* If tsd package for one cpu is invalid , the coordination
* among all CPUs is thought as invalid .
* Maybe it is ugly .
*/
if (!pthrottling->tsd_valid_flag) {
retval = -EINVAL;
break ;
}
}
if (retval)
goto err_ret;
for_each_possible_cpu(i) {
pr = per_cpu(processors, i);
if (!pr)
continue ;
if (cpumask_test_cpu(i, covered_cpus))
continue ;
pthrottling = &pr->throttling;
pdomain = &(pthrottling->domain_info);
cpumask_set_cpu(i, pthrottling->shared_cpu_map);
cpumask_set_cpu(i, covered_cpus);
/*
* If the number of processor in the TSD domain is 1 , it is
* unnecessary to parse the coordination for this CPU .
*/
if (pdomain->num_processors <= 1 )
continue ;
/* Validate the Domain info */
count_target = pdomain->num_processors;
for_each_possible_cpu(j) {
if (i == j)
continue ;
match_pr = per_cpu(processors, j);
if (!match_pr)
continue ;
match_pthrottling = &(match_pr->throttling);
match_pdomain = &(match_pthrottling->domain_info);
if (match_pdomain->domain != pdomain->domain)
continue ;
/* Here i and j are in the same domain.
* If two TSD packages have the same domain , they
* should have the same num_porcessors and
* coordination type . Otherwise it will be regarded
* as illegal .
*/
if (match_pdomain->num_processors != count_target) {
retval = -EINVAL;
goto err_ret;
}
if (pdomain->coord_type != match_pdomain->coord_type) {
retval = -EINVAL;
goto err_ret;
}
cpumask_set_cpu(j, covered_cpus);
cpumask_set_cpu(j, pthrottling->shared_cpu_map);
}
for_each_possible_cpu(j) {
if (i == j)
continue ;
match_pr = per_cpu(processors, j);
if (!match_pr)
continue ;
match_pthrottling = &(match_pr->throttling);
match_pdomain = &(match_pthrottling->domain_info);
if (match_pdomain->domain != pdomain->domain)
continue ;
/*
* If some CPUS have the same domain , they
* will have the same shared_cpu_map .
*/
cpumask_copy(match_pthrottling->shared_cpu_map,
pthrottling->shared_cpu_map);
}
}
err_ret:
free_cpumask_var(covered_cpus);
for_each_possible_cpu(i) {
pr = per_cpu(processors, i);
if (!pr)
continue ;
/*
* Assume no coordination on any error parsing domain info .
* The coordination type will be forced as SW_ALL .
*/
if (retval) {
pthrottling = &(pr->throttling);
cpumask_clear(pthrottling->shared_cpu_map);
cpumask_set_cpu(i, pthrottling->shared_cpu_map);
pthrottling->shared_type = DOMAIN_COORD_TYPE_SW_ALL;
}
}
return retval;
}
/*
* Update the T - state coordination after the _ TSD
* data for all cpus is obtained .
*/
void acpi_processor_throttling_init(void )
{
if (acpi_processor_update_tsd_coord())
pr_debug("Assume no T-state coordination\n" );
}
static int acpi_processor_throttling_notifier(unsigned long event, void *data)
{
struct throttling_tstate *p_tstate = data;
struct acpi_processor *pr;
unsigned int cpu;
int target_state;
struct acpi_processor_limit *p_limit;
struct acpi_processor_throttling *p_throttling;
cpu = p_tstate->cpu;
pr = per_cpu(processors, cpu);
if (!pr) {
pr_debug("Invalid pr pointer\n" );
return 0 ;
}
if (!pr->flags.throttling) {
acpi_handle_debug(pr->handle,
"Throttling control unsupported on CPU %d\n" ,
cpu);
return 0 ;
}
target_state = p_tstate->target_state;
p_throttling = &(pr->throttling);
switch (event) {
case THROTTLING_PRECHANGE:
/*
* Prechange event is used to choose one proper t - state ,
* which meets the limits of thermal , user and _ TPC .
*/
p_limit = &pr->limit;
if (p_limit->thermal.tx > target_state)
target_state = p_limit->thermal.tx;
if (p_limit->user.tx > target_state)
target_state = p_limit->user.tx;
if (pr->throttling_platform_limit > target_state)
target_state = pr->throttling_platform_limit;
if (target_state >= p_throttling->state_count) {
pr_warn("Exceed the limit of T-state\n" );
target_state = p_throttling->state_count - 1 ;
}
p_tstate->target_state = target_state;
acpi_handle_debug(pr->handle,
"PreChange Event: target T-state of CPU %d is T%d\n" ,
cpu, target_state);
break ;
case THROTTLING_POSTCHANGE:
/*
* Postchange event is only used to update the
* T - state flag of acpi_processor_throttling .
*/
p_throttling->state = target_state;
acpi_handle_debug(pr->handle,
"PostChange Event: CPU %d is switched to T%d\n" ,
cpu, target_state);
break ;
default :
pr_warn("Unsupported Throttling notifier event\n" );
break ;
}
return 0 ;
}
/*
* _ TPC - Throttling Present Capabilities
*/
static int acpi_processor_get_platform_limit(struct acpi_processor *pr)
{
acpi_status status = 0 ;
unsigned long long tpc = 0 ;
if (!pr)
return -EINVAL;
if (ignore_tpc)
goto end;
status = acpi_evaluate_integer(pr->handle, "_TPC" , NULL, &tpc);
if (ACPI_FAILURE(status)) {
if (status != AE_NOT_FOUND)
acpi_evaluation_failure_warn(pr->handle, "_TPC" , status);
return -ENODEV;
}
end:
pr->throttling_platform_limit = (int )tpc;
return 0 ;
}
int acpi_processor_tstate_has_changed(struct acpi_processor *pr)
{
int result = 0 ;
int throttling_limit;
int current_state;
struct acpi_processor_limit *limit;
int target_state;
if (ignore_tpc)
return 0 ;
result = acpi_processor_get_platform_limit(pr);
if (result) {
/* Throttling Limit is unsupported */
return result;
}
throttling_limit = pr->throttling_platform_limit;
if (throttling_limit >= pr->throttling.state_count) {
/* Uncorrect Throttling Limit */
return -EINVAL;
}
current_state = pr->throttling.state;
if (current_state > throttling_limit) {
/*
* The current state can meet the requirement of
* _ TPC limit . But it is reasonable that OSPM changes
* t - states from high to low for better performance .
* Of course the limit condition of thermal
* and user should be considered .
*/
limit = &pr->limit;
target_state = throttling_limit;
if (limit->thermal.tx > target_state)
target_state = limit->thermal.tx;
if (limit->user.tx > target_state)
target_state = limit->user.tx;
} else if (current_state == throttling_limit) {
/*
* Unnecessary to change the throttling state
*/
return 0 ;
} else {
/*
* If the current state is lower than the limit of _ TPC , it
* will be forced to switch to the throttling state defined
* by throttling_platfor_limit .
* Because the previous state meets with the limit condition
* of thermal and user , it is unnecessary to check it again .
*/
target_state = throttling_limit;
}
return acpi_processor_set_throttling(pr, target_state, false );
}
/*
* This function is used to reevaluate whether the T - state is valid
* after one CPU is onlined / offlined .
* It is noted that it won ' t reevaluate the following properties for
* the T - state .
* 1 . Control method .
* 2 . the number of supported T - state
* 3 . TSD domain
*/
void acpi_processor_reevaluate_tstate(struct acpi_processor *pr,
bool is_dead)
{
int result = 0 ;
if (is_dead) {
/* When one CPU is offline, the T-state throttling
* will be invalidated .
*/
pr->flags.throttling = 0 ;
return ;
}
/* the following is to recheck whether the T-state is valid for
* the online CPU
*/
if (!pr->throttling.state_count) {
/* If the number of T-state is invalid, it is
* invalidated .
*/
pr->flags.throttling = 0 ;
return ;
}
pr->flags.throttling = 1 ;
/* Disable throttling (if enabled). We'll let subsequent
* policy ( e . g . thermal ) decide to lower performance if it
* so chooses , but for now we ' ll crank up the speed .
*/
result = acpi_processor_get_throttling(pr);
if (result)
goto end;
if (pr->throttling.state) {
result = acpi_processor_set_throttling(pr, 0 , false );
if (result)
goto end;
}
end:
if (result)
pr->flags.throttling = 0 ;
}
/*
* _ PTC - Processor Throttling Control ( and status ) register location
*/
static int acpi_processor_get_throttling_control(struct acpi_processor *pr)
{
int result = 0 ;
acpi_status status = 0 ;
struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL };
union acpi_object *ptc = NULL;
union acpi_object obj;
struct acpi_processor_throttling *throttling;
status = acpi_evaluate_object(pr->handle, "_PTC" , NULL, &buffer);
if (ACPI_FAILURE(status)) {
if (status != AE_NOT_FOUND)
acpi_evaluation_failure_warn(pr->handle, "_PTC" , status);
return -ENODEV;
}
ptc = (union acpi_object *)buffer.pointer;
if (!ptc || (ptc->type != ACPI_TYPE_PACKAGE)
|| (ptc->package.count != 2 )) {
pr_err("Invalid _PTC data\n" );
result = -EFAULT;
goto end;
}
/*
* control_register
*/
obj = ptc->package.elements[0 ];
if ((obj.type != ACPI_TYPE_BUFFER)
|| (obj.buffer.length < sizeof (struct acpi_ptc_register))
|| (obj.buffer.pointer == NULL)) {
pr_err("Invalid _PTC data (control_register)\n" );
result = -EFAULT;
goto end;
}
memcpy(&pr->throttling.control_register, obj.buffer.pointer,
sizeof (struct acpi_ptc_register));
/*
* status_register
*/
obj = ptc->package.elements[1 ];
if ((obj.type != ACPI_TYPE_BUFFER)
|| (obj.buffer.length < sizeof (struct acpi_ptc_register))
|| (obj.buffer.pointer == NULL)) {
pr_err("Invalid _PTC data (status_register)\n" );
result = -EFAULT;
goto end;
}
memcpy(&pr->throttling.status_register, obj.buffer.pointer,
sizeof (struct acpi_ptc_register));
throttling = &pr->throttling;
if ((throttling->control_register.bit_width +
throttling->control_register.bit_offset) > 32 ) {
pr_err("Invalid _PTC control register\n" );
result = -EFAULT;
goto end;
}
if ((throttling->status_register.bit_width +
throttling->status_register.bit_offset) > 32 ) {
pr_err("Invalid _PTC status register\n" );
result = -EFAULT;
goto end;
}
end:
kfree(buffer.pointer);
return result;
}
/*
* _ TSS - Throttling Supported States
*/
static int acpi_processor_get_throttling_states(struct acpi_processor *pr)
{
int result = 0 ;
acpi_status status = AE_OK;
struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL };
struct acpi_buffer format = { sizeof ("NNNNN" ), "NNNNN" };
struct acpi_buffer state = { 0 , NULL };
union acpi_object *tss = NULL;
int i;
status = acpi_evaluate_object(pr->handle, "_TSS" , NULL, &buffer);
if (ACPI_FAILURE(status)) {
if (status != AE_NOT_FOUND)
acpi_evaluation_failure_warn(pr->handle, "_TSS" , status);
return -ENODEV;
}
tss = buffer.pointer;
if (!tss || (tss->type != ACPI_TYPE_PACKAGE)) {
pr_err("Invalid _TSS data\n" );
result = -EFAULT;
goto end;
}
acpi_handle_debug(pr->handle, "Found %d throttling states\n" ,
tss->package.count);
pr->throttling.state_count = tss->package.count;
pr->throttling.states_tss =
kmalloc_array(tss->package.count,
sizeof (struct acpi_processor_tx_tss),
GFP_KERNEL);
if (!pr->throttling.states_tss) {
result = -ENOMEM;
goto end;
}
for (i = 0 ; i < pr->throttling.state_count; i++) {
struct acpi_processor_tx_tss *tx =
(struct acpi_processor_tx_tss *)&(pr->throttling.
states_tss[i]);
state.length = sizeof (struct acpi_processor_tx_tss);
state.pointer = tx;
acpi_handle_debug(pr->handle, "Extracting state %d\n" , i);
status = acpi_extract_package(&(tss->package.elements[i]),
&format, &state);
if (ACPI_FAILURE(status)) {
acpi_handle_warn(pr->handle, "Invalid _TSS data: %s\n" ,
acpi_format_exception(status));
result = -EFAULT;
kfree(pr->throttling.states_tss);
goto end;
}
if (!tx->freqpercentage) {
pr_err("Invalid _TSS data: freq is zero\n" );
result = -EFAULT;
kfree(pr->throttling.states_tss);
goto end;
}
}
end:
kfree(buffer.pointer);
return result;
}
/*
* _ TSD - T - State Dependencies
*/
static int acpi_processor_get_tsd(struct acpi_processor *pr)
{
int result = 0 ;
acpi_status status = AE_OK;
struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL };
struct acpi_buffer format = { sizeof ("NNNNN" ), "NNNNN" };
struct acpi_buffer state = { 0 , NULL };
union acpi_object *tsd = NULL;
struct acpi_tsd_package *pdomain;
struct acpi_processor_throttling *pthrottling;
pthrottling = &pr->throttling;
pthrottling->tsd_valid_flag = 0 ;
status = acpi_evaluate_object(pr->handle, "_TSD" , NULL, &buffer);
if (ACPI_FAILURE(status)) {
if (status != AE_NOT_FOUND)
acpi_evaluation_failure_warn(pr->handle, "_TSD" , status);
return -ENODEV;
}
tsd = buffer.pointer;
if (!tsd || (tsd->type != ACPI_TYPE_PACKAGE)) {
pr_err("Invalid _TSD data\n" );
result = -EFAULT;
goto end;
}
if (tsd->package.count != 1 ) {
pr_err("Invalid _TSD data\n" );
result = -EFAULT;
goto end;
}
pdomain = &(pr->throttling.domain_info);
state.length = sizeof (struct acpi_tsd_package);
state.pointer = pdomain;
status = acpi_extract_package(&(tsd->package.elements[0 ]),
&format, &state);
if (ACPI_FAILURE(status)) {
pr_err("Invalid _TSD data\n" );
result = -EFAULT;
goto end;
}
if (pdomain->num_entries != ACPI_TSD_REV0_ENTRIES) {
pr_err("Unknown _TSD:num_entries\n" );
result = -EFAULT;
goto end;
}
if (pdomain->revision != ACPI_TSD_REV0_REVISION) {
pr_err("Unknown _TSD:revision\n" );
result = -EFAULT;
goto end;
}
pthrottling = &pr->throttling;
pthrottling->tsd_valid_flag = 1 ;
pthrottling->shared_type = pdomain->coord_type;
cpumask_set_cpu(pr->id, pthrottling->shared_cpu_map);
/*
* If the coordination type is not defined in ACPI spec ,
* the tsd_valid_flag will be clear and coordination type
* will be forecd as DOMAIN_COORD_TYPE_SW_ALL .
*/
if (pdomain->coord_type != DOMAIN_COORD_TYPE_SW_ALL &&
pdomain->coord_type != DOMAIN_COORD_TYPE_SW_ANY &&
pdomain->coord_type != DOMAIN_COORD_TYPE_HW_ALL) {
pthrottling->tsd_valid_flag = 0 ;
pthrottling->shared_type = DOMAIN_COORD_TYPE_SW_ALL;
}
end:
kfree(buffer.pointer);
return result;
}
/* --------------------------------------------------------------------------
Throttling Control
-------------------------------------------------------------------------- */
static int acpi_processor_get_throttling_fadt(struct acpi_processor *pr)
{
int state = 0 ;
u32 value = 0 ;
u32 duty_mask = 0 ;
u32 duty_value = 0 ;
if (!pr)
return -EINVAL;
if (!pr->flags.throttling)
return -ENODEV;
/*
* We don ' t care about error returns - we just try to mark
* these reserved so that nobody else is confused into thinking
* that this region might be unused . .
*
* ( In particular , allocating the IO range for Cardbus )
*/
request_region(pr->throttling.address, 6 , "ACPI CPU throttle" );
pr->throttling.state = 0 ;
duty_mask = pr->throttling.state_count - 1 ;
duty_mask <<= pr->throttling.duty_offset;
local_irq_disable();
value = inl(pr->throttling.address);
/*
* Compute the current throttling state when throttling is enabled
* ( bit 4 is on ) .
*/
if (value & 0 x10) {
duty_value = value & duty_mask;
duty_value >>= pr->throttling.duty_offset;
if (duty_value)
state = pr->throttling.state_count - duty_value;
}
pr->throttling.state = state;
local_irq_enable();
acpi_handle_debug(pr->handle,
"Throttling state is T%d (%d%% throttling applied)\n" ,
state, pr->throttling.states[state].performance);
return 0 ;
}
#ifdef CONFIG_X86
static int acpi_throttling_rdmsr(u64 *value)
{
u64 msr_high, msr_low;
u64 msr = 0 ;
int ret = -1 ;
if ((this_cpu_read(cpu_info.x86_vendor) != X86_VENDOR_INTEL) ||
!this_cpu_has(X86_FEATURE_ACPI)) {
pr_err("HARDWARE addr space,NOT supported yet\n" );
} else {
msr_low = 0 ;
msr_high = 0 ;
rdmsr_safe(MSR_IA32_THERM_CONTROL,
(u32 *)&msr_low, (u32 *) &msr_high);
msr = (msr_high << 32 ) | msr_low;
*value = (u64) msr;
ret = 0 ;
}
return ret;
}
static int acpi_throttling_wrmsr(u64 value)
{
int ret = -1 ;
u64 msr;
if ((this_cpu_read(cpu_info.x86_vendor) != X86_VENDOR_INTEL) ||
!this_cpu_has(X86_FEATURE_ACPI)) {
pr_err("HARDWARE addr space,NOT supported yet\n" );
} else {
msr = value;
wrmsr_safe(MSR_IA32_THERM_CONTROL,
msr & 0 xffffffff, msr >> 32 );
ret = 0 ;
}
return ret;
}
#else
static int acpi_throttling_rdmsr(u64 *value)
{
pr_err("HARDWARE addr space,NOT supported yet\n" );
return -1 ;
}
static int acpi_throttling_wrmsr(u64 value)
{
pr_err("HARDWARE addr space,NOT supported yet\n" );
return -1 ;
}
#endif
static int acpi_read_throttling_status(struct acpi_processor *pr,
u64 *value)
{
u32 bit_width, bit_offset;
u32 ptc_value;
u64 ptc_mask;
struct acpi_processor_throttling *throttling;
int ret = -1 ;
throttling = &pr->throttling;
switch (throttling->status_register.space_id) {
case ACPI_ADR_SPACE_SYSTEM_IO:
bit_width = throttling->status_register.bit_width;
bit_offset = throttling->status_register.bit_offset;
acpi_os_read_port((acpi_io_address) throttling->status_register.
address, &ptc_value,
(u32) (bit_width + bit_offset));
ptc_mask = (1 << bit_width) - 1 ;
*value = (u64) ((ptc_value >> bit_offset) & ptc_mask);
ret = 0 ;
break ;
case ACPI_ADR_SPACE_FIXED_HARDWARE:
ret = acpi_throttling_rdmsr(value);
break ;
default :
pr_err("Unknown addr space %d\n" ,
(u32) (throttling->status_register.space_id));
}
return ret;
}
static int acpi_write_throttling_state(struct acpi_processor *pr,
u64 value)
{
u32 bit_width, bit_offset;
u64 ptc_value;
u64 ptc_mask;
struct acpi_processor_throttling *throttling;
int ret = -1 ;
throttling = &pr->throttling;
switch (throttling->control_register.space_id) {
case ACPI_ADR_SPACE_SYSTEM_IO:
bit_width = throttling->control_register.bit_width;
bit_offset = throttling->control_register.bit_offset;
ptc_mask = (1 << bit_width) - 1 ;
ptc_value = value & ptc_mask;
acpi_os_write_port((acpi_io_address) throttling->
control_register.address,
(u32) (ptc_value << bit_offset),
(u32) (bit_width + bit_offset));
ret = 0 ;
break ;
case ACPI_ADR_SPACE_FIXED_HARDWARE:
ret = acpi_throttling_wrmsr(value);
break ;
default :
pr_err("Unknown addr space %d\n" ,
(u32) (throttling->control_register.space_id));
}
return ret;
}
static int acpi_get_throttling_state(struct acpi_processor *pr,
u64 value)
{
int i;
for (i = 0 ; i < pr->throttling.state_count; i++) {
struct acpi_processor_tx_tss *tx =
(struct acpi_processor_tx_tss *)&(pr->throttling.
states_tss[i]);
if (tx->control == value)
return i;
}
return -1 ;
}
static int acpi_get_throttling_value(struct acpi_processor *pr,
int state, u64 *value)
{
int ret = -1 ;
if (state >= 0 && state <= pr->throttling.state_count) {
struct acpi_processor_tx_tss *tx =
(struct acpi_processor_tx_tss *)&(pr->throttling.
states_tss[state]);
*value = tx->control;
ret = 0 ;
}
return ret;
}
static int acpi_processor_get_throttling_ptc(struct acpi_processor *pr)
{
int state = 0 ;
int ret;
u64 value;
if (!pr)
return -EINVAL;
if (!pr->flags.throttling)
return -ENODEV;
pr->throttling.state = 0 ;
value = 0 ;
ret = acpi_read_throttling_status(pr, &value);
if (ret >= 0 ) {
state = acpi_get_throttling_state(pr, value);
if (state == -1 ) {
acpi_handle_debug(pr->handle,
"Invalid throttling state, reset\n" );
state = 0 ;
ret = __acpi_processor_set_throttling(pr, state, true ,
true );
if (ret)
return ret;
}
pr->throttling.state = state;
}
return 0 ;
}
static long __acpi_processor_get_throttling(void *data)
{
struct acpi_processor *pr = data;
return pr->throttling.acpi_processor_get_throttling(pr);
}
static int acpi_processor_get_throttling(struct acpi_processor *pr)
{
if (!pr)
return -EINVAL;
if (!pr->flags.throttling)
return -ENODEV;
/*
* This is either called from the CPU hotplug callback of
* processor_driver or via the ACPI probe function . In the latter
* case the CPU is not guaranteed to be online . Both call sites are
* protected against CPU hotplug .
*/
if (!cpu_online(pr->id))
return -ENODEV;
return call_on_cpu(pr->id, __acpi_processor_get_throttling, pr, false );
}
static int acpi_processor_get_fadt_info(struct acpi_processor *pr)
{
int i, step;
if (!pr->throttling.address) {
acpi_handle_debug(pr->handle, "No throttling register\n" );
return -EINVAL;
} else if (!pr->throttling.duty_width) {
acpi_handle_debug(pr->handle, "No throttling states\n" );
return -EINVAL;
}
/* TBD: Support duty_cycle values that span bit 4. */
else if ((pr->throttling.duty_offset + pr->throttling.duty_width) > 4 ) {
pr_warn("duty_cycle spans bit 4\n" );
return -EINVAL;
}
pr->throttling.state_count = 1 << acpi_gbl_FADT.duty_width;
/*
* Compute state values . Note that throttling displays a linear power
* performance relationship ( at 50 % performance the CPU will consume
* 50 % power ) . Values are in 1 / 10 th of a percent to preserve accuracy .
*/
step = (1000 / pr->throttling.state_count);
for (i = 0 ; i < pr->throttling.state_count; i++) {
pr->throttling.states[i].performance = 1000 - step * i;
pr->throttling.states[i].power = 1000 - step * i;
}
return 0 ;
}
static int acpi_processor_set_throttling_fadt(struct acpi_processor *pr,
int state, bool force)
{
u32 value = 0 ;
u32 duty_mask = 0 ;
u32 duty_value = 0 ;
if (!pr)
return -EINVAL;
if ((state < 0 ) || (state > (pr->throttling.state_count - 1 )))
return -EINVAL;
if (!pr->flags.throttling)
return -ENODEV;
if (!force && (state == pr->throttling.state))
return 0 ;
if (state < pr->throttling_platform_limit)
return -EPERM;
/*
* Calculate the duty_value and duty_mask .
*/
if (state) {
duty_value = pr->throttling.state_count - state;
duty_value <<= pr->throttling.duty_offset;
/* Used to clear all duty_value bits */
duty_mask = pr->throttling.state_count - 1 ;
duty_mask <<= acpi_gbl_FADT.duty_offset;
duty_mask = ~duty_mask;
}
local_irq_disable();
/*
* Disable throttling by writing a 0 to bit 4 . Note that we must
* turn it off before you can change the duty_value .
*/
value = inl(pr->throttling.address);
if (value & 0 x10) {
value &= 0 xFFFFFFEF;
outl(value, pr->throttling.address);
}
/*
* Write the new duty_value and then enable throttling . Note
* that a state value of 0 leaves throttling disabled .
*/
if (state) {
value &= duty_mask;
value |= duty_value;
outl(value, pr->throttling.address);
value |= 0 x00000010;
outl(value, pr->throttling.address);
}
pr->throttling.state = state;
local_irq_enable();
acpi_handle_debug(pr->handle,
"Throttling state set to T%d (%d%%)\n" , state,
(pr->throttling.states[state].performance ? pr->
throttling.states[state].performance / 10 : 0 ));
return 0 ;
}
static int acpi_processor_set_throttling_ptc(struct acpi_processor *pr,
int state, bool force)
{
int ret;
u64 value;
if (!pr)
return -EINVAL;
if ((state < 0 ) || (state > (pr->throttling.state_count - 1 )))
return -EINVAL;
if (!pr->flags.throttling)
return -ENODEV;
if (!force && (state == pr->throttling.state))
return 0 ;
if (state < pr->throttling_platform_limit)
return -EPERM;
value = 0 ;
ret = acpi_get_throttling_value(pr, state, &value);
if (ret >= 0 ) {
acpi_write_throttling_state(pr, value);
pr->throttling.state = state;
}
return 0 ;
}
static long acpi_processor_throttling_fn(void *data)
{
struct acpi_processor_throttling_arg *arg = data;
struct acpi_processor *pr = arg->pr;
return pr->throttling.acpi_processor_set_throttling(pr,
arg->target_state, arg->force);
}
static int __acpi_processor_set_throttling(struct acpi_processor *pr,
int state, bool force, bool direct)
{
int ret = 0 ;
unsigned int i;
struct acpi_processor *match_pr;
struct acpi_processor_throttling *p_throttling;
struct acpi_processor_throttling_arg arg;
struct throttling_tstate t_state;
if (!pr)
return -EINVAL;
if (!pr->flags.throttling)
return -ENODEV;
if ((state < 0 ) || (state > (pr->throttling.state_count - 1 )))
return -EINVAL;
if (cpu_is_offline(pr->id)) {
/*
* the cpu pointed by pr - > id is offline . Unnecessary to change
* the throttling state any more .
*/
return -ENODEV;
}
t_state.target_state = state;
p_throttling = &(pr->throttling);
/*
* The throttling notifier will be called for every
* affected cpu in order to get one proper T - state .
* The notifier event is THROTTLING_PRECHANGE .
*/
for_each_cpu_and(i, cpu_online_mask, p_throttling->shared_cpu_map) {
t_state.cpu = i;
acpi_processor_throttling_notifier(THROTTLING_PRECHANGE,
&t_state);
}
/*
* The function of acpi_processor_set_throttling will be called
* to switch T - state . If the coordination type is SW_ALL or HW_ALL ,
* it is necessary to call it for every affected cpu . Otherwise
* it can be called only for the cpu pointed by pr .
*/
if (p_throttling->shared_type == DOMAIN_COORD_TYPE_SW_ANY) {
arg.pr = pr;
arg.target_state = state;
arg.force = force;
ret = call_on_cpu(pr->id, acpi_processor_throttling_fn, &arg,
direct);
} else {
/*
* When the T - state coordination is SW_ALL or HW_ALL ,
* it is necessary to set T - state for every affected
* cpus .
*/
for_each_cpu_and(i, cpu_online_mask,
p_throttling->shared_cpu_map) {
match_pr = per_cpu(processors, i);
/*
* If the pointer is invalid , we will report the
* error message and continue .
*/
if (!match_pr) {
acpi_handle_debug(pr->handle,
"Invalid Pointer for CPU %d\n" , i);
continue ;
}
/*
* If the throttling control is unsupported on CPU i ,
* we will report the error message and continue .
*/
if (!match_pr->flags.throttling) {
acpi_handle_debug(pr->handle,
"Throttling Control unsupported on CPU %d\n" , i);
continue ;
}
arg.pr = match_pr;
arg.target_state = state;
arg.force = force;
ret = call_on_cpu(pr->id, acpi_processor_throttling_fn,
&arg, direct);
}
}
/*
* After the set_throttling is called , the
* throttling notifier is called for every
* affected cpu to update the T - states .
* The notifier event is THROTTLING_POSTCHANGE
*/
for_each_cpu_and(i, cpu_online_mask, p_throttling->shared_cpu_map) {
t_state.cpu = i;
acpi_processor_throttling_notifier(THROTTLING_POSTCHANGE,
&t_state);
}
return ret;
}
int acpi_processor_set_throttling(struct acpi_processor *pr, int state,
bool force)
{
return __acpi_processor_set_throttling(pr, state, force, false );
}
int acpi_processor_get_throttling_info(struct acpi_processor *pr)
{
int result = 0 ;
struct acpi_processor_throttling *pthrottling;
acpi_handle_debug(pr->handle,
"pblk_address[0x%08x] duty_offset[%d] duty_width[%d]\n" ,
pr->throttling.address,
pr->throttling.duty_offset,
pr->throttling.duty_width);
/*
* Evaluate _ PTC , _ TSS and _ TPC
* They must all be present or none of them can be used .
*/
if (acpi_processor_get_throttling_control(pr) ||
acpi_processor_get_throttling_states(pr) ||
acpi_processor_get_platform_limit(pr)) {
pr->throttling.acpi_processor_get_throttling =
&acpi_processor_get_throttling_fadt;
pr->throttling.acpi_processor_set_throttling =
&acpi_processor_set_throttling_fadt;
if (acpi_processor_get_fadt_info(pr))
return 0 ;
} else {
pr->throttling.acpi_processor_get_throttling =
&acpi_processor_get_throttling_ptc;
pr->throttling.acpi_processor_set_throttling =
&acpi_processor_set_throttling_ptc;
}
/*
* If TSD package for one CPU can ' t be parsed successfully , it means
* that this CPU will have no coordination with other CPUs .
*/
if (acpi_processor_get_tsd(pr)) {
pthrottling = &pr->throttling;
pthrottling->tsd_valid_flag = 0 ;
cpumask_set_cpu(pr->id, pthrottling->shared_cpu_map);
pthrottling->shared_type = DOMAIN_COORD_TYPE_SW_ALL;
}
/*
* PIIX4 Errata : We don ' t support throttling on the original PIIX4 .
* This shouldn ' t be an issue as few ( if any ) mobile systems ever
* used this part .
*/
if (errata.piix4.throttle) {
acpi_handle_debug(pr->handle,
"Throttling not supported on PIIX4 A- or B-step\n" );
return 0 ;
}
acpi_handle_debug(pr->handle, "Found %d throttling states\n" ,
pr->throttling.state_count);
pr->flags.throttling = 1 ;
/*
* Disable throttling ( if enabled ) . We ' ll let subsequent policy ( e . g .
* thermal ) decide to lower performance if it so chooses , but for now
* we ' ll crank up the speed .
*/
result = acpi_processor_get_throttling(pr);
if (result)
goto end;
if (pr->throttling.state) {
acpi_handle_debug(pr->handle,
"Disabling throttling (was T%d)\n" ,
pr->throttling.state);
result = acpi_processor_set_throttling(pr, 0 , false );
if (result)
goto end;
}
end:
if (result)
pr->flags.throttling = 0 ;
return result;
}
Messung V0.5 in Prozent C=96 H=89 G=92
¤ Dauer der Verarbeitung: 0.16 Sekunden
(vorverarbeitet am 2026-09-29)
¤
*© Formatika GbR, Deutschland