linux/drivers/thermal/cpu_cooling.c
<<
>>
Prefs
   1/*
   2 *  linux/drivers/thermal/cpu_cooling.c
   3 *
   4 *  Copyright (C) 2012  Samsung Electronics Co., Ltd(http://www.samsung.com)
   5 *  Copyright (C) 2012  Amit Daniel <amit.kachhap@linaro.org>
   6 *
   7 * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
   8 *  This program is free software; you can redistribute it and/or modify
   9 *  it under the terms of the GNU General Public License as published by
  10 *  the Free Software Foundation; version 2 of the License.
  11 *
  12 *  This program is distributed in the hope that it will be useful, but
  13 *  WITHOUT ANY WARRANTY; without even the implied warranty of
  14 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  15 *  General Public License for more details.
  16 *
  17 *  You should have received a copy of the GNU General Public License along
  18 *  with this program; if not, write to the Free Software Foundation, Inc.,
  19 *  59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
  20 *
  21 * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  22 */
  23#include <linux/module.h>
  24#include <linux/thermal.h>
  25#include <linux/cpufreq.h>
  26#include <linux/err.h>
  27#include <linux/slab.h>
  28#include <linux/cpu.h>
  29#include <linux/cpu_cooling.h>
  30
  31/**
  32 * struct cpufreq_cooling_device - data for cooling device with cpufreq
  33 * @id: unique integer value corresponding to each cpufreq_cooling_device
  34 *      registered.
  35 * @cool_dev: thermal_cooling_device pointer to keep track of the
  36 *      registered cooling device.
  37 * @cpufreq_state: integer value representing the current state of cpufreq
  38 *      cooling devices.
  39 * @cpufreq_val: integer value representing the absolute value of the clipped
  40 *      frequency.
  41 * @allowed_cpus: all the cpus involved for this cpufreq_cooling_device.
  42 *
  43 * This structure is required for keeping information of each
  44 * cpufreq_cooling_device registered. In order to prevent corruption of this a
  45 * mutex lock cooling_cpufreq_lock is used.
  46 */
  47struct cpufreq_cooling_device {
  48        int id;
  49        struct thermal_cooling_device *cool_dev;
  50        unsigned int cpufreq_state;
  51        unsigned int cpufreq_val;
  52        struct cpumask allowed_cpus;
  53};
  54static DEFINE_IDR(cpufreq_idr);
  55static DEFINE_MUTEX(cooling_cpufreq_lock);
  56
  57static unsigned int cpufreq_dev_count;
  58
  59/* notify_table passes value to the CPUFREQ_ADJUST callback function. */
  60#define NOTIFY_INVALID NULL
  61static struct cpufreq_cooling_device *notify_device;
  62
  63/**
  64 * get_idr - function to get a unique id.
  65 * @idr: struct idr * handle used to create a id.
  66 * @id: int * value generated by this function.
  67 *
  68 * This function will populate @id with an unique
  69 * id, using the idr API.
  70 *
  71 * Return: 0 on success, an error code on failure.
  72 */
  73static int get_idr(struct idr *idr, int *id)
  74{
  75        int ret;
  76
  77        mutex_lock(&cooling_cpufreq_lock);
  78        ret = idr_alloc(idr, NULL, 0, 0, GFP_KERNEL);
  79        mutex_unlock(&cooling_cpufreq_lock);
  80        if (unlikely(ret < 0))
  81                return ret;
  82        *id = ret;
  83
  84        return 0;
  85}
  86
  87/**
  88 * release_idr - function to free the unique id.
  89 * @idr: struct idr * handle used for creating the id.
  90 * @id: int value representing the unique id.
  91 */
  92static void release_idr(struct idr *idr, int id)
  93{
  94        mutex_lock(&cooling_cpufreq_lock);
  95        idr_remove(idr, id);
  96        mutex_unlock(&cooling_cpufreq_lock);
  97}
  98
  99/* Below code defines functions to be used for cpufreq as cooling device */
 100
 101/**
 102 * is_cpufreq_valid - function to check frequency transitioning capability.
 103 * @cpu: cpu for which check is needed.
 104 *
 105 * This function will check the current state of the system if
 106 * it is capable of changing the frequency for a given @cpu.
 107 *
 108 * Return: 0 if the system is not currently capable of changing
 109 * the frequency of given cpu. !0 in case the frequency is changeable.
 110 */
 111static int is_cpufreq_valid(int cpu)
 112{
 113        struct cpufreq_policy policy;
 114
 115        return !cpufreq_get_policy(&policy, cpu);
 116}
 117
 118enum cpufreq_cooling_property {
 119        GET_LEVEL,
 120        GET_FREQ,
 121        GET_MAXL,
 122};
 123
 124/**
 125 * get_property - fetch a property of interest for a give cpu.
 126 * @cpu: cpu for which the property is required
 127 * @input: query parameter
 128 * @output: query return
 129 * @property: type of query (frequency, level, max level)
 130 *
 131 * This is the common function to
 132 * 1. get maximum cpu cooling states
 133 * 2. translate frequency to cooling state
 134 * 3. translate cooling state to frequency
 135 * Note that the code may be not in good shape
 136 * but it is written in this way in order to:
 137 * a) reduce duplicate code as most of the code can be shared.
 138 * b) make sure the logic is consistent when translating between
 139 *    cooling states and frequencies.
 140 *
 141 * Return: 0 on success, -EINVAL when invalid parameters are passed.
 142 */
 143static int get_property(unsigned int cpu, unsigned long input,
 144                        unsigned int *output,
 145                        enum cpufreq_cooling_property property)
 146{
 147        int i, j;
 148        unsigned long max_level = 0, level = 0;
 149        unsigned int freq = CPUFREQ_ENTRY_INVALID;
 150        int descend = -1;
 151        struct cpufreq_frequency_table *table =
 152                                        cpufreq_frequency_get_table(cpu);
 153
 154        if (!output)
 155                return -EINVAL;
 156
 157        if (!table)
 158                return -EINVAL;
 159
 160        for (i = 0; table[i].frequency != CPUFREQ_TABLE_END; i++) {
 161                /* ignore invalid entries */
 162                if (table[i].frequency == CPUFREQ_ENTRY_INVALID)
 163                        continue;
 164
 165                /* ignore duplicate entry */
 166                if (freq == table[i].frequency)
 167                        continue;
 168
 169                /* get the frequency order */
 170                if (freq != CPUFREQ_ENTRY_INVALID && descend == -1)
 171                        descend = !!(freq > table[i].frequency);
 172
 173                freq = table[i].frequency;
 174                max_level++;
 175        }
 176
 177        /* get max level */
 178        if (property == GET_MAXL) {
 179                *output = (unsigned int)max_level;
 180                return 0;
 181        }
 182
 183        if (property == GET_FREQ)
 184                level = descend ? input : (max_level - input - 1);
 185
 186        for (i = 0, j = 0; table[i].frequency != CPUFREQ_TABLE_END; i++) {
 187                /* ignore invalid entry */
 188                if (table[i].frequency == CPUFREQ_ENTRY_INVALID)
 189                        continue;
 190
 191                /* ignore duplicate entry */
 192                if (freq == table[i].frequency)
 193                        continue;
 194
 195                /* now we have a valid frequency entry */
 196                freq = table[i].frequency;
 197
 198                if (property == GET_LEVEL && (unsigned int)input == freq) {
 199                        /* get level by frequency */
 200                        *output = descend ? j : (max_level - j - 1);
 201                        return 0;
 202                }
 203                if (property == GET_FREQ && level == j) {
 204                        /* get frequency by level */
 205                        *output = freq;
 206                        return 0;
 207                }
 208                j++;
 209        }
 210
 211        return -EINVAL;
 212}
 213
 214/**
 215 * cpufreq_cooling_get_level - for a give cpu, return the cooling level.
 216 * @cpu: cpu for which the level is required
 217 * @freq: the frequency of interest
 218 *
 219 * This function will match the cooling level corresponding to the
 220 * requested @freq and return it.
 221 *
 222 * Return: The matched cooling level on success or THERMAL_CSTATE_INVALID
 223 * otherwise.
 224 */
 225unsigned long cpufreq_cooling_get_level(unsigned int cpu, unsigned int freq)
 226{
 227        unsigned int val;
 228
 229        if (get_property(cpu, (unsigned long)freq, &val, GET_LEVEL))
 230                return THERMAL_CSTATE_INVALID;
 231
 232        return (unsigned long)val;
 233}
 234EXPORT_SYMBOL_GPL(cpufreq_cooling_get_level);
 235
 236/**
 237 * get_cpu_frequency - get the absolute value of frequency from level.
 238 * @cpu: cpu for which frequency is fetched.
 239 * @level: cooling level
 240 *
 241 * This function matches cooling level with frequency. Based on a cooling level
 242 * of frequency, equals cooling state of cpu cooling device, it will return
 243 * the corresponding frequency.
 244 *      e.g level=0 --> 1st MAX FREQ, level=1 ---> 2nd MAX FREQ, .... etc
 245 *
 246 * Return: 0 on error, the corresponding frequency otherwise.
 247 */
 248static unsigned int get_cpu_frequency(unsigned int cpu, unsigned long level)
 249{
 250        int ret = 0;
 251        unsigned int freq;
 252
 253        ret = get_property(cpu, level, &freq, GET_FREQ);
 254        if (ret)
 255                return 0;
 256
 257        return freq;
 258}
 259
 260/**
 261 * cpufreq_apply_cooling - function to apply frequency clipping.
 262 * @cpufreq_device: cpufreq_cooling_device pointer containing frequency
 263 *      clipping data.
 264 * @cooling_state: value of the cooling state.
 265 *
 266 * Function used to make sure the cpufreq layer is aware of current thermal
 267 * limits. The limits are applied by updating the cpufreq policy.
 268 *
 269 * Return: 0 on success, an error code otherwise (-EINVAL in case wrong
 270 * cooling state).
 271 */
 272static int cpufreq_apply_cooling(struct cpufreq_cooling_device *cpufreq_device,
 273                                 unsigned long cooling_state)
 274{
 275        unsigned int cpuid, clip_freq;
 276        struct cpumask *mask = &cpufreq_device->allowed_cpus;
 277        unsigned int cpu = cpumask_any(mask);
 278
 279
 280        /* Check if the old cooling action is same as new cooling action */
 281        if (cpufreq_device->cpufreq_state == cooling_state)
 282                return 0;
 283
 284        clip_freq = get_cpu_frequency(cpu, cooling_state);
 285        if (!clip_freq)
 286                return -EINVAL;
 287
 288        cpufreq_device->cpufreq_state = cooling_state;
 289        cpufreq_device->cpufreq_val = clip_freq;
 290        notify_device = cpufreq_device;
 291
 292        for_each_cpu(cpuid, mask) {
 293                if (is_cpufreq_valid(cpuid))
 294                        cpufreq_update_policy(cpuid);
 295        }
 296
 297        notify_device = NOTIFY_INVALID;
 298
 299        return 0;
 300}
 301
 302/**
 303 * cpufreq_thermal_notifier - notifier callback for cpufreq policy change.
 304 * @nb: struct notifier_block * with callback info.
 305 * @event: value showing cpufreq event for which this function invoked.
 306 * @data: callback-specific data
 307 *
 308 * Callback to highjack the notification on cpufreq policy transition.
 309 * Every time there is a change in policy, we will intercept and
 310 * update the cpufreq policy with thermal constraints.
 311 *
 312 * Return: 0 (success)
 313 */
 314static int cpufreq_thermal_notifier(struct notifier_block *nb,
 315                                    unsigned long event, void *data)
 316{
 317        struct cpufreq_policy *policy = data;
 318        unsigned long max_freq = 0;
 319
 320        if (event != CPUFREQ_ADJUST || notify_device == NOTIFY_INVALID)
 321                return 0;
 322
 323        if (cpumask_test_cpu(policy->cpu, &notify_device->allowed_cpus))
 324                max_freq = notify_device->cpufreq_val;
 325        else
 326                return 0;
 327
 328        /* Never exceed user_policy.max */
 329        if (max_freq > policy->user_policy.max)
 330                max_freq = policy->user_policy.max;
 331
 332        if (policy->max != max_freq)
 333                cpufreq_verify_within_limits(policy, 0, max_freq);
 334
 335        return 0;
 336}
 337
 338/* cpufreq cooling device callback functions are defined below */
 339
 340/**
 341 * cpufreq_get_max_state - callback function to get the max cooling state.
 342 * @cdev: thermal cooling device pointer.
 343 * @state: fill this variable with the max cooling state.
 344 *
 345 * Callback for the thermal cooling device to return the cpufreq
 346 * max cooling state.
 347 *
 348 * Return: 0 on success, an error code otherwise.
 349 */
 350static int cpufreq_get_max_state(struct thermal_cooling_device *cdev,
 351                                 unsigned long *state)
 352{
 353        struct cpufreq_cooling_device *cpufreq_device = cdev->devdata;
 354        struct cpumask *mask = &cpufreq_device->allowed_cpus;
 355        unsigned int cpu;
 356        unsigned int count = 0;
 357        int ret;
 358
 359        cpu = cpumask_any(mask);
 360
 361        ret = get_property(cpu, 0, &count, GET_MAXL);
 362
 363        if (count > 0)
 364                *state = count;
 365
 366        return ret;
 367}
 368
 369/**
 370 * cpufreq_get_cur_state - callback function to get the current cooling state.
 371 * @cdev: thermal cooling device pointer.
 372 * @state: fill this variable with the current cooling state.
 373 *
 374 * Callback for the thermal cooling device to return the cpufreq
 375 * current cooling state.
 376 *
 377 * Return: 0 on success, an error code otherwise.
 378 */
 379static int cpufreq_get_cur_state(struct thermal_cooling_device *cdev,
 380                                 unsigned long *state)
 381{
 382        struct cpufreq_cooling_device *cpufreq_device = cdev->devdata;
 383
 384        *state = cpufreq_device->cpufreq_state;
 385
 386        return 0;
 387}
 388
 389/**
 390 * cpufreq_set_cur_state - callback function to set the current cooling state.
 391 * @cdev: thermal cooling device pointer.
 392 * @state: set this variable to the current cooling state.
 393 *
 394 * Callback for the thermal cooling device to change the cpufreq
 395 * current cooling state.
 396 *
 397 * Return: 0 on success, an error code otherwise.
 398 */
 399static int cpufreq_set_cur_state(struct thermal_cooling_device *cdev,
 400                                 unsigned long state)
 401{
 402        struct cpufreq_cooling_device *cpufreq_device = cdev->devdata;
 403
 404        return cpufreq_apply_cooling(cpufreq_device, state);
 405}
 406
 407/* Bind cpufreq callbacks to thermal cooling device ops */
 408static struct thermal_cooling_device_ops const cpufreq_cooling_ops = {
 409        .get_max_state = cpufreq_get_max_state,
 410        .get_cur_state = cpufreq_get_cur_state,
 411        .set_cur_state = cpufreq_set_cur_state,
 412};
 413
 414/* Notifier for cpufreq policy change */
 415static struct notifier_block thermal_cpufreq_notifier_block = {
 416        .notifier_call = cpufreq_thermal_notifier,
 417};
 418
 419/**
 420 * cpufreq_cooling_register - function to create cpufreq cooling device.
 421 * @clip_cpus: cpumask of cpus where the frequency constraints will happen.
 422 *
 423 * This interface function registers the cpufreq cooling device with the name
 424 * "thermal-cpufreq-%x". This api can support multiple instances of cpufreq
 425 * cooling devices.
 426 *
 427 * Return: a valid struct thermal_cooling_device pointer on success,
 428 * on failure, it returns a corresponding ERR_PTR().
 429 */
 430struct thermal_cooling_device *
 431cpufreq_cooling_register(const struct cpumask *clip_cpus)
 432{
 433        struct thermal_cooling_device *cool_dev;
 434        struct cpufreq_cooling_device *cpufreq_dev = NULL;
 435        unsigned int min = 0, max = 0;
 436        char dev_name[THERMAL_NAME_LENGTH];
 437        int ret = 0, i;
 438        struct cpufreq_policy policy;
 439
 440        /* Verify that all the clip cpus have same freq_min, freq_max limit */
 441        for_each_cpu(i, clip_cpus) {
 442                /* continue if cpufreq policy not found and not return error */
 443                if (!cpufreq_get_policy(&policy, i))
 444                        continue;
 445                if (min == 0 && max == 0) {
 446                        min = policy.cpuinfo.min_freq;
 447                        max = policy.cpuinfo.max_freq;
 448                } else {
 449                        if (min != policy.cpuinfo.min_freq ||
 450                            max != policy.cpuinfo.max_freq)
 451                                return ERR_PTR(-EINVAL);
 452                }
 453        }
 454        cpufreq_dev = kzalloc(sizeof(struct cpufreq_cooling_device),
 455                              GFP_KERNEL);
 456        if (!cpufreq_dev)
 457                return ERR_PTR(-ENOMEM);
 458
 459        cpumask_copy(&cpufreq_dev->allowed_cpus, clip_cpus);
 460
 461        ret = get_idr(&cpufreq_idr, &cpufreq_dev->id);
 462        if (ret) {
 463                kfree(cpufreq_dev);
 464                return ERR_PTR(-EINVAL);
 465        }
 466
 467        snprintf(dev_name, sizeof(dev_name), "thermal-cpufreq-%d",
 468                 cpufreq_dev->id);
 469
 470        cool_dev = thermal_cooling_device_register(dev_name, cpufreq_dev,
 471                                                   &cpufreq_cooling_ops);
 472        if (!cool_dev) {
 473                release_idr(&cpufreq_idr, cpufreq_dev->id);
 474                kfree(cpufreq_dev);
 475                return ERR_PTR(-EINVAL);
 476        }
 477        cpufreq_dev->cool_dev = cool_dev;
 478        cpufreq_dev->cpufreq_state = 0;
 479        mutex_lock(&cooling_cpufreq_lock);
 480
 481        /* Register the notifier for first cpufreq cooling device */
 482        if (cpufreq_dev_count == 0)
 483                cpufreq_register_notifier(&thermal_cpufreq_notifier_block,
 484                                          CPUFREQ_POLICY_NOTIFIER);
 485        cpufreq_dev_count++;
 486
 487        mutex_unlock(&cooling_cpufreq_lock);
 488
 489        return cool_dev;
 490}
 491EXPORT_SYMBOL_GPL(cpufreq_cooling_register);
 492
 493/**
 494 * cpufreq_cooling_unregister - function to remove cpufreq cooling device.
 495 * @cdev: thermal cooling device pointer.
 496 *
 497 * This interface function unregisters the "thermal-cpufreq-%x" cooling device.
 498 */
 499void cpufreq_cooling_unregister(struct thermal_cooling_device *cdev)
 500{
 501        struct cpufreq_cooling_device *cpufreq_dev;
 502
 503        if (!cdev)
 504                return;
 505
 506        cpufreq_dev = cdev->devdata;
 507        mutex_lock(&cooling_cpufreq_lock);
 508        cpufreq_dev_count--;
 509
 510        /* Unregister the notifier for the last cpufreq cooling device */
 511        if (cpufreq_dev_count == 0)
 512                cpufreq_unregister_notifier(&thermal_cpufreq_notifier_block,
 513                                            CPUFREQ_POLICY_NOTIFIER);
 514        mutex_unlock(&cooling_cpufreq_lock);
 515
 516        thermal_cooling_device_unregister(cpufreq_dev->cool_dev);
 517        release_idr(&cpufreq_idr, cpufreq_dev->id);
 518        kfree(cpufreq_dev);
 519}
 520EXPORT_SYMBOL_GPL(cpufreq_cooling_unregister);
 521
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.