diff options
Diffstat (limited to 'drivers/acpi/processor_thermal.c')
| -rw-r--r-- | drivers/acpi/processor_thermal.c | 405 |
1 files changed, 141 insertions, 264 deletions
diff --git a/drivers/acpi/processor_thermal.c b/drivers/acpi/processor_thermal.c index dc9817cfb88..e003663b2f8 100644 --- a/drivers/acpi/processor_thermal.c +++ b/drivers/acpi/processor_thermal.c @@ -30,62 +30,15 @@ #include <linux/module.h> #include <linux/init.h> #include <linux/cpufreq.h> -#include <linux/proc_fs.h> -#include <linux/seq_file.h> - +#include <linux/acpi.h> +#include <acpi/processor.h> #include <asm/uaccess.h> -#include <acpi/acpi_bus.h> -#include <acpi/processor.h> -#include <acpi/acpi_drivers.h> +#define PREFIX "ACPI: " -#define ACPI_PROCESSOR_COMPONENT 0x01000000 #define ACPI_PROCESSOR_CLASS "processor" -#define ACPI_PROCESSOR_DRIVER_NAME "ACPI Processor Driver" #define _COMPONENT ACPI_PROCESSOR_COMPONENT -ACPI_MODULE_NAME("acpi_processor") - -/* -------------------------------------------------------------------------- - Limit Interface - -------------------------------------------------------------------------- */ -static int acpi_processor_apply_limit(struct acpi_processor *pr) -{ - int result = 0; - u16 px = 0; - u16 tx = 0; - - ACPI_FUNCTION_TRACE("acpi_processor_apply_limit"); - - if (!pr) - return_VALUE(-EINVAL); - - if (!pr->flags.limit) - return_VALUE(-ENODEV); - - if (pr->flags.throttling) { - if (pr->limit.user.tx > tx) - tx = pr->limit.user.tx; - if (pr->limit.thermal.tx > tx) - tx = pr->limit.thermal.tx; - - result = acpi_processor_set_throttling(pr, tx); - if (result) - goto end; - } - - pr->limit.state.px = px; - pr->limit.state.tx = tx; - - ACPI_DEBUG_PRINT((ACPI_DB_INFO, - "Processor [%d] limit set to (P%d:T%d)\n", pr->id, - pr->limit.state.px, pr->limit.state.tx)); - - end: - if (result) - ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "Unable to set limit\n")); - - return_VALUE(result); -} +ACPI_MODULE_NAME("processor_thermal"); #ifdef CONFIG_CPU_FREQ @@ -95,43 +48,39 @@ static int acpi_processor_apply_limit(struct acpi_processor *pr) * _any_ cpufreq driver and not only the acpi-cpufreq driver. */ -static unsigned int cpufreq_thermal_reduction_pctg[NR_CPUS]; +#define CPUFREQ_THERMAL_MIN_STEP 0 +#define CPUFREQ_THERMAL_MAX_STEP 3 + +static DEFINE_PER_CPU(unsigned int, cpufreq_thermal_reduction_pctg); static unsigned int acpi_thermal_cpufreq_is_init = 0; -static int cpu_has_cpufreq(unsigned int cpu) -{ - struct cpufreq_policy policy; - if (!acpi_thermal_cpufreq_is_init || cpufreq_get_policy(&policy, cpu)) - return 0; - return 1; -} +#define reduction_pctg(cpu) \ + per_cpu(cpufreq_thermal_reduction_pctg, phys_package_first_cpu(cpu)) -static int acpi_thermal_cpufreq_increase(unsigned int 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) { - if (!cpu_has_cpufreq(cpu)) - return -ENODEV; - - if (cpufreq_thermal_reduction_pctg[cpu] < 60) { - cpufreq_thermal_reduction_pctg[cpu] += 20; - cpufreq_update_policy(cpu); - return 0; - } + int i; + int id = topology_physical_package_id(cpu); - return -ERANGE; + for_each_online_cpu(i) + if (topology_physical_package_id(i) == id) + return i; + return 0; } -static int acpi_thermal_cpufreq_decrease(unsigned int cpu) +static int cpu_has_cpufreq(unsigned int cpu) { - if (!cpu_has_cpufreq(cpu)) - return -ENODEV; - - if (cpufreq_thermal_reduction_pctg[cpu] > 20) - cpufreq_thermal_reduction_pctg[cpu] -= 20; - else - cpufreq_thermal_reduction_pctg[cpu] = 0; - cpufreq_update_policy(cpu); - /* We reached max freq again and can leave passive mode */ - return !cpufreq_thermal_reduction_pctg[cpu]; + struct cpufreq_policy policy; + if (!acpi_thermal_cpufreq_is_init || cpufreq_get_policy(&policy, cpu)) + return 0; + return 1; } static int acpi_thermal_cpufreq_notifier(struct notifier_block *nb, @@ -143,9 +92,10 @@ static int acpi_thermal_cpufreq_notifier(struct notifier_block *nb, if (event != CPUFREQ_ADJUST) goto out; - max_freq = - (policy->cpuinfo.max_freq * - (100 - cpufreq_thermal_reduction_pctg[policy->cpu])) / 100; + max_freq = ( + policy->cpuinfo.max_freq * + (100 - reduction_pctg(policy->cpu) * 20) + ) / 100; cpufreq_verify_within_limits(policy, 0, max_freq); @@ -157,12 +107,47 @@ static struct notifier_block acpi_thermal_cpufreq_notifier_block = { .notifier_call = acpi_thermal_cpufreq_notifier, }; -void acpi_thermal_cpufreq_init(void) +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; + + return reduction_pctg(cpu); +} + +static int cpufreq_set_cur_state(unsigned int cpu, int state) { int i; - for (i = 0; i < NR_CPUS; i++) - cpufreq_thermal_reduction_pctg[i] = 0; + if (!cpu_has_cpufreq(cpu)) + return 0; + + 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) { + if (topology_physical_package_id(i) == + topology_physical_package_id(cpu)) + cpufreq_update_policy(i); + } + return 0; +} + +void acpi_thermal_cpufreq_init(void) +{ + int i; i = cpufreq_register_notifier(&acpi_thermal_cpufreq_notifier_block, CPUFREQ_POLICY_NOTIFIER); @@ -181,219 +166,111 @@ void acpi_thermal_cpufreq_exit(void) } #else /* ! CONFIG_CPU_FREQ */ +static int cpufreq_get_max_state(unsigned int cpu) +{ + return 0; +} -static int acpi_thermal_cpufreq_increase(unsigned int cpu) +static int cpufreq_get_cur_state(unsigned int cpu) { - return -ENODEV; + return 0; } -static int acpi_thermal_cpufreq_decrease(unsigned int cpu) + +static int cpufreq_set_cur_state(unsigned int cpu, int state) { - return -ENODEV; + return 0; } #endif -int acpi_processor_set_thermal_limit(acpi_handle handle, int type) +/* thermal cooling device callbacks */ +static int acpi_processor_max_state(struct acpi_processor *pr) { - int result = 0; - struct acpi_processor *pr = NULL; - struct acpi_device *device = NULL; - int tx = 0, max_tx_px = 0; - - ACPI_FUNCTION_TRACE("acpi_processor_set_thermal_limit"); - - if ((type < ACPI_PROCESSOR_LIMIT_NONE) - || (type > ACPI_PROCESSOR_LIMIT_DECREMENT)) - return_VALUE(-EINVAL); - - result = acpi_bus_get_device(handle, &device); - if (result) - return_VALUE(result); - - pr = (struct acpi_processor *)acpi_driver_data(device); - if (!pr) - return_VALUE(-ENODEV); - - /* Thermal limits are always relative to the current Px/Tx state. */ - if (pr->flags.throttling) - pr->limit.thermal.tx = pr->throttling.state; + int max_state = 0; /* - * Our default policy is to only use throttling at the lowest - * performance state. + * There exists four states according to + * cpufreq_thermal_reduction_pctg. 0, 1, 2, 3 */ + max_state += cpufreq_get_max_state(pr->id); + if (pr->flags.throttling) + max_state += (pr->throttling.state_count -1); - tx = pr->limit.thermal.tx; - - switch (type) { - - case ACPI_PROCESSOR_LIMIT_NONE: - do { - result = acpi_thermal_cpufreq_decrease(pr->id); - } while (!result); - tx = 0; - break; - - case ACPI_PROCESSOR_LIMIT_INCREMENT: - /* if going up: P-states first, T-states later */ - - result = acpi_thermal_cpufreq_increase(pr->id); - if (!result) - goto end; - else if (result == -ERANGE) - ACPI_DEBUG_PRINT((ACPI_DB_INFO, - "At maximum performance state\n")); - - if (pr->flags.throttling) { - if (tx == (pr->throttling.state_count - 1)) - ACPI_DEBUG_PRINT((ACPI_DB_INFO, - "At maximum throttling state\n")); - else - tx++; - } - break; - - case ACPI_PROCESSOR_LIMIT_DECREMENT: - /* if going down: T-states first, P-states later */ - - if (pr->flags.throttling) { - if (tx == 0) { - max_tx_px = 1; - ACPI_DEBUG_PRINT((ACPI_DB_INFO, - "At minimum throttling state\n")); - } else { - tx--; - goto end; - } - } - - result = acpi_thermal_cpufreq_decrease(pr->id); - if (result) { - /* - * We only could get -ERANGE, 1 or 0. - * In the first two cases we reached max freq again. - */ - ACPI_DEBUG_PRINT((ACPI_DB_INFO, - "At minimum performance state\n")); - max_tx_px = 1; - } else - max_tx_px = 0; - - break; - } - - end: - if (pr->flags.throttling) { - pr->limit.thermal.px = 0; - pr->limit.thermal.tx = tx; - - result = acpi_processor_apply_limit(pr); - if (result) - ACPI_DEBUG_PRINT((ACPI_DB_ERROR, - "Unable to set thermal limit\n")); - - ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Thermal limit now (P%d:T%d)\n", - pr->limit.thermal.px, pr->limit.thermal.tx)); - } else - result = 0; - if (max_tx_px) - return_VALUE(1); - else - return_VALUE(result); + return max_state; } - -int acpi_processor_get_limit_info(struct acpi_processor *pr) +static int +processor_get_max_state(struct thermal_cooling_device *cdev, + unsigned long *state) { - ACPI_FUNCTION_TRACE("acpi_processor_get_limit_info"); + struct acpi_device *device = cdev->devdata; + struct acpi_processor *pr; - if (!pr) - return_VALUE(-EINVAL); + if (!device) + return -EINVAL; - if (pr->flags.throttling) - pr->flags.limit = 1; + pr = acpi_driver_data(device); + if (!pr) + return -EINVAL; - return_VALUE(0); + *state = acpi_processor_max_state(pr); + return 0; } -/* /proc interface */ - -static int acpi_processor_limit_seq_show(struct seq_file *seq, void *offset) +static int +processor_get_cur_state(struct thermal_cooling_device *cdev, + unsigned long *cur_state) { - struct acpi_processor *pr = (struct acpi_processor *)seq->private; + struct acpi_device *device = cdev->devdata; + struct acpi_processor *pr; - ACPI_FUNCTION_TRACE("acpi_processor_limit_seq_show"); + if (!device) + return -EINVAL; + pr = acpi_driver_data(device); if (!pr) - goto end; - - if (!pr->flags.limit) { - seq_puts(seq, "<not supported>\n"); - goto end; - } + return -EINVAL; - seq_printf(seq, "active limit: P%d:T%d\n" - "user limit: P%d:T%d\n" - "thermal limit: P%d:T%d\n", - pr->limit.state.px, pr->limit.state.tx, - pr->limit.user.px, pr->limit.user.tx, - pr->limit.thermal.px, pr->limit.thermal.tx); - - end: - return_VALUE(0); -} - -static int acpi_processor_limit_open_fs(struct inode *inode, struct file *file) -{ - return single_open(file, acpi_processor_limit_seq_show, - PDE(inode)->data); + *cur_state = cpufreq_get_cur_state(pr->id); + if (pr->flags.throttling) + *cur_state += pr->throttling.state; + return 0; } -ssize_t acpi_processor_write_limit(struct file * file, - const char __user * buffer, - size_t count, loff_t * data) +static int +processor_set_cur_state(struct thermal_cooling_device *cdev, + unsigned long state) { + struct acpi_device *device = cdev->devdata; + struct acpi_processor *pr; int result = 0; - struct seq_file *m = (struct seq_file *)file->private_data; - struct acpi_processor *pr = (struct acpi_processor *)m->private; - char limit_string[25] = { '\0' }; - int px = 0; - int tx = 0; + int max_pstate; - ACPI_FUNCTION_TRACE("acpi_processor_write_limit"); + if (!device) + return -EINVAL; - if (!pr || (count > sizeof(limit_string) - 1)) { - ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "Invalid argument\n")); - return_VALUE(-EINVAL); - } - - if (copy_from_user(limit_string, buffer, count)) { - ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "Invalid data\n")); - return_VALUE(-EFAULT); - } + pr = acpi_driver_data(device); + if (!pr) + return -EINVAL; - limit_string[count] = '\0'; + max_pstate = cpufreq_get_max_state(pr->id); - if (sscanf(limit_string, "%d:%d", &px, &tx) != 2) { - ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "Invalid data format\n")); - return_VALUE(-EINVAL); - } + if (state > acpi_processor_max_state(pr)) + return -EINVAL; - if (pr->flags.throttling) { - if ((tx < 0) || (tx > (pr->throttling.state_count - 1))) { - ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "Invalid tx\n")); - return_VALUE(-EINVAL); - } - pr->limit.user.tx = tx; + if (state <= max_pstate) { + if (pr->flags.throttling && pr->throttling.state) + result = acpi_processor_set_throttling(pr, 0, false); + cpufreq_set_cur_state(pr->id, state); + } else { + cpufreq_set_cur_state(pr->id, max_pstate); + result = acpi_processor_set_throttling(pr, + state - max_pstate, false); } - - result = acpi_processor_apply_limit(pr); - - return_VALUE(count); + return result; } -struct file_operations acpi_processor_limit_fops = { - .open = acpi_processor_limit_open_fs, - .read = seq_read, - .llseek = seq_lseek, - .release = single_release, +const struct thermal_cooling_device_ops processor_cooling_ops = { + .get_max_state = processor_get_max_state, + .get_cur_state = processor_get_cur_state, + .set_cur_state = processor_set_cur_state, }; |
