linux/drivers/acpi/processor_thermal.c
<<
>>
Prefs
   1/*
   2 * processor_thermal.c - Passive cooling submodule of the ACPI processor driver
   3 *
   4 *  Copyright (C) 2001, 2002 Andy Grover <andrew.grover@intel.com>
   5 *  Copyright (C) 2001, 2002 Paul Diefenbaugh <paul.s.diefenbaugh@intel.com>
   6 *  Copyright (C) 2004       Dominik Brodowski <linux@brodo.de>
   7 *  Copyright (C) 2004  Anil S Keshavamurthy <anil.s.keshavamurthy@intel.com>
   8 *                      - Added processor hotplug support
   9 *
  10 * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  11 *
  12 *  This program is free software; you can redistribute it and/or modify
  13 *  it under the terms of the GNU General Public License as published by
  14 *  the Free Software Foundation; either version 2 of the License, or (at
  15 *  your option) any later version.
  16 *
  17 *  This program is distributed in the hope that it will be useful, but
  18 *  WITHOUT ANY WARRANTY; without even the implied warranty of
  19 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  20 *  General Public License for more details.
  21 *
  22 *  You should have received a copy of the GNU General Public License along
  23 *  with this program; if not, write to the Free Software Foundation, Inc.,
  24 *  59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
  25 *
  26 * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  27 */
  28
  29#include <linux/kernel.h>
  30#include <linux/module.h>
  31#include <linux/init.h>
  32#include <linux/cpufreq.h>
  33
  34#include <asm/uaccess.h>
  35
  36#include <acpi/acpi_bus.h>
  37#include <acpi/processor.h>
  38#include <acpi/acpi_drivers.h>
  39
  40#define PREFIX "ACPI: "
  41
  42#define ACPI_PROCESSOR_CLASS            "processor"
  43#define _COMPONENT              ACPI_PROCESSOR_COMPONENT
  44ACPI_MODULE_NAME("processor_thermal");
  45
  46#ifdef CONFIG_CPU_FREQ
  47
  48/* If a passive cooling situation is detected, primarily CPUfreq is used, as it
  49 * offers (in most cases) voltage scaling in addition to frequency scaling, and
  50 * thus a cubic (instead of linear) reduction of energy. Also, we allow for
  51 * _any_ cpufreq driver and not only the acpi-cpufreq driver.
  52 */
  53
  54#define CPUFREQ_THERMAL_MIN_STEP 0
  55#define CPUFREQ_THERMAL_MAX_STEP 3
  56
  57static DEFINE_PER_CPU(unsigned int, cpufreq_thermal_reduction_pctg);
  58static unsigned int acpi_thermal_cpufreq_is_init = 0;
  59
  60#define reduction_pctg(cpu) \
  61        per_cpu(cpufreq_thermal_reduction_pctg, phys_package_first_cpu(cpu))
  62
  63/*
  64 * Emulate "per package data" using per cpu data (which should really be
  65 * provided elsewhere)
  66 *
  67 * Note we can lose a CPU on cpu hotunplug, in this case we forget the state
  68 * temporarily. Fortunately that's not a big issue here (I hope)
  69 */
  70static int phys_package_first_cpu(int cpu)
  71{
  72        int i;
  73        int id = topology_physical_package_id(cpu);
  74
  75        for_each_online_cpu(i)
  76                if (topology_physical_package_id(i) == id)
  77                        return i;
  78        return 0;
  79}
  80
  81static int cpu_has_cpufreq(unsigned int cpu)
  82{
  83        struct cpufreq_policy policy;
  84        if (!acpi_thermal_cpufreq_is_init || cpufreq_get_policy(&policy, cpu))
  85                return 0;
  86        return 1;
  87}
  88
  89static int acpi_thermal_cpufreq_notifier(struct notifier_block *nb,
  90                                         unsigned long event, void *data)
  91{
  92        struct cpufreq_policy *policy = data;
  93        unsigned long max_freq = 0;
  94
  95        if (event != CPUFREQ_ADJUST)
  96                goto out;
  97
  98        max_freq = (
  99            policy->cpuinfo.max_freq *
 100            (100 - reduction_pctg(policy->cpu) * 20)
 101        ) / 100;
 102
 103        cpufreq_verify_within_limits(policy, 0, max_freq);
 104
 105      out:
 106        return 0;
 107}
 108
 109static struct notifier_block acpi_thermal_cpufreq_notifier_block = {
 110        .notifier_call = acpi_thermal_cpufreq_notifier,
 111};
 112
 113static int cpufreq_get_max_state(unsigned int cpu)
 114{
 115        if (!cpu_has_cpufreq(cpu))
 116                return 0;
 117
 118        return CPUFREQ_THERMAL_MAX_STEP;
 119}
 120
 121static int cpufreq_get_cur_state(unsigned int cpu)
 122{
 123        if (!cpu_has_cpufreq(cpu))
 124                return 0;
 125
 126        return reduction_pctg(cpu);
 127}
 128
 129static int cpufreq_set_cur_state(unsigned int cpu, int state)
 130{
 131        int i;
 132
 133        if (!cpu_has_cpufreq(cpu))
 134                return 0;
 135
 136        reduction_pctg(cpu) = state;
 137
 138        /*
 139         * Update all the CPUs in the same package because they all
 140         * contribute to the temperature and often share the same
 141         * frequency.
 142         */
 143        for_each_online_cpu(i) {
 144                if (topology_physical_package_id(i) ==
 145                    topology_physical_package_id(cpu))
 146                        cpufreq_update_policy(i);
 147        }
 148        return 0;
 149}
 150
 151void acpi_thermal_cpufreq_init(void)
 152{
 153        int i;
 154
 155        i = cpufreq_register_notifier(&acpi_thermal_cpufreq_notifier_block,
 156                                      CPUFREQ_POLICY_NOTIFIER);
 157        if (!i)
 158                acpi_thermal_cpufreq_is_init = 1;
 159}
 160
 161void acpi_thermal_cpufreq_exit(void)
 162{
 163        if (acpi_thermal_cpufreq_is_init)
 164                cpufreq_unregister_notifier
 165                    (&acpi_thermal_cpufreq_notifier_block,
 166                     CPUFREQ_POLICY_NOTIFIER);
 167
 168        acpi_thermal_cpufreq_is_init = 0;
 169}
 170
 171#else                           /* ! CONFIG_CPU_FREQ */
 172static int cpufreq_get_max_state(unsigned int cpu)
 173{
 174        return 0;
 175}
 176
 177static int cpufreq_get_cur_state(unsigned int cpu)
 178{
 179        return 0;
 180}
 181
 182static int cpufreq_set_cur_state(unsigned int cpu, int state)
 183{
 184        return 0;
 185}
 186
 187#endif
 188
 189int acpi_processor_get_limit_info(struct acpi_processor *pr)
 190{
 191
 192        if (!pr)
 193                return -EINVAL;
 194
 195        if (pr->flags.throttling)
 196                pr->flags.limit = 1;
 197
 198        return 0;
 199}
 200
 201/* thermal coolign device callbacks */
 202static int acpi_processor_max_state(struct acpi_processor *pr)
 203{
 204        int max_state = 0;
 205
 206        /*
 207         * There exists four states according to
 208         * cpufreq_thermal_reduction_ptg. 0, 1, 2, 3
 209         */
 210        max_state += cpufreq_get_max_state(pr->id);
 211        if (pr->flags.throttling)
 212                max_state += (pr->throttling.state_count -1);
 213
 214        return max_state;
 215}
 216static int
 217processor_get_max_state(struct thermal_cooling_device *cdev,
 218                        unsigned long *state)
 219{
 220        struct acpi_device *device = cdev->devdata;
 221        struct acpi_processor *pr;
 222
 223        if (!device)
 224                return -EINVAL;
 225
 226        pr = acpi_driver_data(device);
 227        if (!pr)
 228                return -EINVAL;
 229
 230        *state = acpi_processor_max_state(pr);
 231        return 0;
 232}
 233
 234static int
 235processor_get_cur_state(struct thermal_cooling_device *cdev,
 236                        unsigned long *cur_state)
 237{
 238        struct acpi_device *device = cdev->devdata;
 239        struct acpi_processor *pr;
 240
 241        if (!device)
 242                return -EINVAL;
 243
 244        pr = acpi_driver_data(device);
 245        if (!pr)
 246                return -EINVAL;
 247
 248        *cur_state = cpufreq_get_cur_state(pr->id);
 249        if (pr->flags.throttling)
 250                *cur_state += pr->throttling.state;
 251        return 0;
 252}
 253
 254static int
 255processor_set_cur_state(struct thermal_cooling_device *cdev,
 256                        unsigned long state)
 257{
 258        struct acpi_device *device = cdev->devdata;
 259        struct acpi_processor *pr;
 260        int result = 0;
 261        int max_pstate;
 262
 263        if (!device)
 264                return -EINVAL;
 265
 266        pr = acpi_driver_data(device);
 267        if (!pr)
 268                return -EINVAL;
 269
 270        max_pstate = cpufreq_get_max_state(pr->id);
 271
 272        if (state > acpi_processor_max_state(pr))
 273                return -EINVAL;
 274
 275        if (state <= max_pstate) {
 276                if (pr->flags.throttling && pr->throttling.state)
 277                        result = acpi_processor_set_throttling(pr, 0, false);
 278                cpufreq_set_cur_state(pr->id, state);
 279        } else {
 280                cpufreq_set_cur_state(pr->id, max_pstate);
 281                result = acpi_processor_set_throttling(pr,
 282                                state - max_pstate, false);
 283        }
 284        return result;
 285}
 286
 287const struct thermal_cooling_device_ops processor_cooling_ops = {
 288        .get_max_state = processor_get_max_state,
 289        .get_cur_state = processor_get_cur_state,
 290        .set_cur_state = processor_set_cur_state,
 291};
 292
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.