linux/drivers/thermal/clock_cooling.c
<<
>>
Prefs
   1/*
   2 *  drivers/thermal/clock_cooling.c
   3 *
   4 *  Copyright (C) 2014 Eduardo Valentin <edubezval@gmail.com>
   5 *
   6 *  Copyright (C) 2013  Texas Instruments Inc.
   7 *  Contact:  Eduardo Valentin <eduardo.valentin@ti.com>
   8 *
   9 *  Highly based on cpu_cooling.c.
  10 *  Copyright (C) 2012  Samsung Electronics Co., Ltd(http://www.samsung.com)
  11 *  Copyright (C) 2012  Amit Daniel <amit.kachhap@linaro.org>
  12 *
  13 *  This program is free software; you can redistribute it and/or modify
  14 *  it under the terms of the GNU General Public License as published by
  15 *  the Free Software Foundation; version 2 of the License.
  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#include <linux/clk.h>
  23#include <linux/cpufreq.h>
  24#include <linux/device.h>
  25#include <linux/err.h>
  26#include <linux/idr.h>
  27#include <linux/mutex.h>
  28#include <linux/pm_opp.h>
  29#include <linux/slab.h>
  30#include <linux/thermal.h>
  31#include <linux/clock_cooling.h>
  32
  33/**
  34 * struct clock_cooling_device - data for cooling device with clock
  35 * @id: unique integer value corresponding to each clock_cooling_device
  36 *      registered.
  37 * @dev: struct device pointer to the device being used to cool off using
  38 *       clock frequencies.
  39 * @cdev: thermal_cooling_device pointer to keep track of the
  40 *      registered cooling device.
  41 * @clk_rate_change_nb: reference to notifier block used to receive clock
  42 *                      rate changes.
  43 * @freq_table: frequency table used to keep track of available frequencies.
  44 * @clock_state: integer value representing the current state of clock
  45 *      cooling devices.
  46 * @clock_val: integer value representing the absolute value of the clipped
  47 *      frequency.
  48 * @clk: struct clk reference used to enforce clock limits.
  49 * @lock: mutex lock to protect this struct.
  50 *
  51 * This structure is required for keeping information of each
  52 * clock_cooling_device registered. In order to prevent corruption of this a
  53 * mutex @lock is used.
  54 */
  55struct clock_cooling_device {
  56        int id;
  57        struct device *dev;
  58        struct thermal_cooling_device *cdev;
  59        struct notifier_block clk_rate_change_nb;
  60        struct cpufreq_frequency_table *freq_table;
  61        unsigned long clock_state;
  62        unsigned long clock_val;
  63        struct clk *clk;
  64        struct mutex lock; /* lock to protect the content of this struct */
  65};
  66#define to_clock_cooling_device(x) \
  67                container_of(x, struct clock_cooling_device, clk_rate_change_nb)
  68static DEFINE_IDA(clock_ida);
  69
  70/* Below code defines functions to be used for clock as cooling device */
  71
  72enum clock_cooling_property {
  73        GET_LEVEL,
  74        GET_FREQ,
  75        GET_MAXL,
  76};
  77
  78/**
  79 * clock_cooling_get_property - fetch a property of interest for a give cpu.
  80 * @ccdev: clock cooling device reference
  81 * @input: query parameter
  82 * @output: query return
  83 * @property: type of query (frequency, level, max level)
  84 *
  85 * This is the common function to
  86 * 1. get maximum clock cooling states
  87 * 2. translate frequency to cooling state
  88 * 3. translate cooling state to frequency
  89 * Note that the code may be not in good shape
  90 * but it is written in this way in order to:
  91 * a) reduce duplicate code as most of the code can be shared.
  92 * b) make sure the logic is consistent when translating between
  93 *    cooling states and frequencies.
  94 *
  95 * Return: 0 on success, -EINVAL when invalid parameters are passed.
  96 */
  97static int clock_cooling_get_property(struct clock_cooling_device *ccdev,
  98                                      unsigned long input,
  99                                      unsigned long *output,
 100                                      enum clock_cooling_property property)
 101{
 102        int i;
 103        unsigned long max_level = 0, level = 0;
 104        unsigned int freq = CPUFREQ_ENTRY_INVALID;
 105        int descend = -1;
 106        struct cpufreq_frequency_table *pos, *table = ccdev->freq_table;
 107
 108        if (!output)
 109                return -EINVAL;
 110
 111        if (!table)
 112                return -EINVAL;
 113
 114        cpufreq_for_each_valid_entry(pos, table) {
 115                /* ignore duplicate entry */
 116                if (freq == pos->frequency)
 117                        continue;
 118
 119                /* get the frequency order */
 120                if (freq != CPUFREQ_ENTRY_INVALID && descend == -1)
 121                        descend = freq > pos->frequency;
 122
 123                freq = pos->frequency;
 124                max_level++;
 125        }
 126
 127        /* No valid cpu frequency entry */
 128        if (max_level == 0)
 129                return -EINVAL;
 130
 131        /* max_level is an index, not a counter */
 132        max_level--;
 133
 134        /* get max level */
 135        if (property == GET_MAXL) {
 136                *output = max_level;
 137                return 0;
 138        }
 139
 140        if (property == GET_FREQ)
 141                level = descend ? input : (max_level - input);
 142
 143        i = 0;
 144        cpufreq_for_each_valid_entry(pos, table) {
 145                /* ignore duplicate entry */
 146                if (freq == pos->frequency)
 147                        continue;
 148
 149                /* now we have a valid frequency entry */
 150                freq = pos->frequency;
 151
 152                if (property == GET_LEVEL && (unsigned int)input == freq) {
 153                        /* get level by frequency */
 154                        *output = descend ? i : (max_level - i);
 155                        return 0;
 156                }
 157                if (property == GET_FREQ && level == i) {
 158                        /* get frequency by level */
 159                        *output = freq;
 160                        return 0;
 161                }
 162                i++;
 163        }
 164
 165        return -EINVAL;
 166}
 167
 168/**
 169 * clock_cooling_get_level - return the cooling level of given clock cooling.
 170 * @cdev: reference of a thermal cooling device of used as clock cooling device
 171 * @freq: the frequency of interest
 172 *
 173 * This function will match the cooling level corresponding to the
 174 * requested @freq and return it.
 175 *
 176 * Return: The matched cooling level on success or THERMAL_CSTATE_INVALID
 177 * otherwise.
 178 */
 179unsigned long clock_cooling_get_level(struct thermal_cooling_device *cdev,
 180                                      unsigned long freq)
 181{
 182        struct clock_cooling_device *ccdev = cdev->devdata;
 183        unsigned long val;
 184
 185        if (clock_cooling_get_property(ccdev, (unsigned long)freq, &val,
 186                                       GET_LEVEL))
 187                return THERMAL_CSTATE_INVALID;
 188
 189        return val;
 190}
 191EXPORT_SYMBOL_GPL(clock_cooling_get_level);
 192
 193/**
 194 * clock_cooling_get_frequency - get the absolute value of frequency from level.
 195 * @ccdev: clock cooling device reference
 196 * @level: cooling level
 197 *
 198 * This function matches cooling level with frequency. Based on a cooling level
 199 * of frequency, equals cooling state of cpu cooling device, it will return
 200 * the corresponding frequency.
 201 *      e.g level=0 --> 1st MAX FREQ, level=1 ---> 2nd MAX FREQ, .... etc
 202 *
 203 * Return: 0 on error, the corresponding frequency otherwise.
 204 */
 205static unsigned long
 206clock_cooling_get_frequency(struct clock_cooling_device *ccdev,
 207                            unsigned long level)
 208{
 209        int ret = 0;
 210        unsigned long freq;
 211
 212        ret = clock_cooling_get_property(ccdev, level, &freq, GET_FREQ);
 213        if (ret)
 214                return 0;
 215
 216        return freq;
 217}
 218
 219/**
 220 * clock_cooling_apply - function to apply frequency clipping.
 221 * @ccdev: clock_cooling_device pointer containing frequency clipping data.
 222 * @cooling_state: value of the cooling state.
 223 *
 224 * Function used to make sure the clock layer is aware of current thermal
 225 * limits. The limits are applied by updating the clock rate in case it is
 226 * higher than the corresponding frequency based on the requested cooling_state.
 227 *
 228 * Return: 0 on success, an error code otherwise (-EINVAL in case wrong
 229 * cooling state).
 230 */
 231static int clock_cooling_apply(struct clock_cooling_device *ccdev,
 232                               unsigned long cooling_state)
 233{
 234        unsigned long clip_freq, cur_freq;
 235        int ret = 0;
 236
 237        /* Here we write the clipping */
 238        /* Check if the old cooling action is same as new cooling action */
 239        if (ccdev->clock_state == cooling_state)
 240                return 0;
 241
 242        clip_freq = clock_cooling_get_frequency(ccdev, cooling_state);
 243        if (!clip_freq)
 244                return -EINVAL;
 245
 246        cur_freq = clk_get_rate(ccdev->clk);
 247
 248        mutex_lock(&ccdev->lock);
 249        ccdev->clock_state = cooling_state;
 250        ccdev->clock_val = clip_freq;
 251        /* enforce clock level */
 252        if (cur_freq > clip_freq)
 253                ret = clk_set_rate(ccdev->clk, clip_freq);
 254        mutex_unlock(&ccdev->lock);
 255
 256        return ret;
 257}
 258
 259/**
 260 * clock_cooling_clock_notifier - notifier callback on clock rate changes.
 261 * @nb: struct notifier_block * with callback info.
 262 * @event: value showing clock event for which this function invoked.
 263 * @data: callback-specific data
 264 *
 265 * Callback to hijack the notification on clock transition.
 266 * Every time there is a clock change, we intercept all pre change events
 267 * and block the transition in case the new rate infringes thermal limits.
 268 *
 269 * Return: NOTIFY_DONE (success) or NOTIFY_BAD (new_rate > thermal limit).
 270 */
 271static int clock_cooling_clock_notifier(struct notifier_block *nb,
 272                                        unsigned long event, void *data)
 273{
 274        struct clk_notifier_data *ndata = data;
 275        struct clock_cooling_device *ccdev = to_clock_cooling_device(nb);
 276
 277        switch (event) {
 278        case PRE_RATE_CHANGE:
 279                /*
 280                 * checks on current state
 281                 * TODO: current method is not best we can find as it
 282                 * allows possibly voltage transitions, in case DVFS
 283                 * layer is also hijacking clock pre notifications.
 284                 */
 285                if (ndata->new_rate > ccdev->clock_val)
 286                        return NOTIFY_BAD;
 287                /* fall through */
 288        case POST_RATE_CHANGE:
 289        case ABORT_RATE_CHANGE:
 290        default:
 291                return NOTIFY_DONE;
 292        }
 293}
 294
 295/* clock cooling device thermal callback functions are defined below */
 296
 297/**
 298 * clock_cooling_get_max_state - callback function to get the max cooling state.
 299 * @cdev: thermal cooling device pointer.
 300 * @state: fill this variable with the max cooling state.
 301 *
 302 * Callback for the thermal cooling device to return the clock
 303 * max cooling state.
 304 *
 305 * Return: 0 on success, an error code otherwise.
 306 */
 307static int clock_cooling_get_max_state(struct thermal_cooling_device *cdev,
 308                                       unsigned long *state)
 309{
 310        struct clock_cooling_device *ccdev = cdev->devdata;
 311        unsigned long count = 0;
 312        int ret;
 313
 314        ret = clock_cooling_get_property(ccdev, 0, &count, GET_MAXL);
 315        if (!ret)
 316                *state = count;
 317
 318        return ret;
 319}
 320
 321/**
 322 * clock_cooling_get_cur_state - function to get the current cooling state.
 323 * @cdev: thermal cooling device pointer.
 324 * @state: fill this variable with the current cooling state.
 325 *
 326 * Callback for the thermal cooling device to return the clock
 327 * current cooling state.
 328 *
 329 * Return: 0 (success)
 330 */
 331static int clock_cooling_get_cur_state(struct thermal_cooling_device *cdev,
 332                                       unsigned long *state)
 333{
 334        struct clock_cooling_device *ccdev = cdev->devdata;
 335
 336        *state = ccdev->clock_state;
 337
 338        return 0;
 339}
 340
 341/**
 342 * clock_cooling_set_cur_state - function to set the current cooling state.
 343 * @cdev: thermal cooling device pointer.
 344 * @state: set this variable to the current cooling state.
 345 *
 346 * Callback for the thermal cooling device to change the clock cooling
 347 * current cooling state.
 348 *
 349 * Return: 0 on success, an error code otherwise.
 350 */
 351static int clock_cooling_set_cur_state(struct thermal_cooling_device *cdev,
 352                                       unsigned long state)
 353{
 354        struct clock_cooling_device *clock_device = cdev->devdata;
 355
 356        return clock_cooling_apply(clock_device, state);
 357}
 358
 359/* Bind clock callbacks to thermal cooling device ops */
 360static struct thermal_cooling_device_ops const clock_cooling_ops = {
 361        .get_max_state = clock_cooling_get_max_state,
 362        .get_cur_state = clock_cooling_get_cur_state,
 363        .set_cur_state = clock_cooling_set_cur_state,
 364};
 365
 366/**
 367 * clock_cooling_register - function to create clock cooling device.
 368 * @dev: struct device pointer to the device used as clock cooling device.
 369 * @clock_name: string containing the clock used as cooling mechanism.
 370 *
 371 * This interface function registers the clock cooling device with the name
 372 * "thermal-clock-%x". The cooling device is based on clock frequencies.
 373 * The struct device is assumed to be capable of DVFS transitions.
 374 * The OPP layer is used to fetch and fill the available frequencies for
 375 * the referred device. The ordered frequency table is used to control
 376 * the clock cooling device cooling states and to limit clock transitions
 377 * based on the cooling state requested by the thermal framework.
 378 *
 379 * Return: a valid struct thermal_cooling_device pointer on success,
 380 * on failure, it returns a corresponding ERR_PTR().
 381 */
 382struct thermal_cooling_device *
 383clock_cooling_register(struct device *dev, const char *clock_name)
 384{
 385        struct thermal_cooling_device *cdev;
 386        struct clock_cooling_device *ccdev = NULL;
 387        char dev_name[THERMAL_NAME_LENGTH];
 388        int ret = 0;
 389
 390        ccdev = devm_kzalloc(dev, sizeof(*ccdev), GFP_KERNEL);
 391        if (!ccdev)
 392                return ERR_PTR(-ENOMEM);
 393
 394        mutex_init(&ccdev->lock);
 395        ccdev->dev = dev;
 396        ccdev->clk = devm_clk_get(dev, clock_name);
 397        if (IS_ERR(ccdev->clk))
 398                return ERR_CAST(ccdev->clk);
 399
 400        ret = ida_simple_get(&clock_ida, 0, 0, GFP_KERNEL);
 401        if (ret < 0)
 402                return ERR_PTR(ret);
 403        ccdev->id = ret;
 404
 405        snprintf(dev_name, sizeof(dev_name), "thermal-clock-%d", ccdev->id);
 406
 407        cdev = thermal_cooling_device_register(dev_name, ccdev,
 408                                               &clock_cooling_ops);
 409        if (IS_ERR(cdev)) {
 410                ida_simple_remove(&clock_ida, ccdev->id);
 411                return ERR_PTR(-EINVAL);
 412        }
 413        ccdev->cdev = cdev;
 414        ccdev->clk_rate_change_nb.notifier_call = clock_cooling_clock_notifier;
 415
 416        /* Assuming someone has already filled the opp table for this device */
 417        ret = dev_pm_opp_init_cpufreq_table(dev, &ccdev->freq_table);
 418        if (ret) {
 419                ida_simple_remove(&clock_ida, ccdev->id);
 420                return ERR_PTR(ret);
 421        }
 422        ccdev->clock_state = 0;
 423        ccdev->clock_val = clock_cooling_get_frequency(ccdev, 0);
 424
 425        clk_notifier_register(ccdev->clk, &ccdev->clk_rate_change_nb);
 426
 427        return cdev;
 428}
 429EXPORT_SYMBOL_GPL(clock_cooling_register);
 430
 431/**
 432 * clock_cooling_unregister - function to remove clock cooling device.
 433 * @cdev: thermal cooling device pointer.
 434 *
 435 * This interface function unregisters the "thermal-clock-%x" cooling device.
 436 */
 437void clock_cooling_unregister(struct thermal_cooling_device *cdev)
 438{
 439        struct clock_cooling_device *ccdev;
 440
 441        if (!cdev)
 442                return;
 443
 444        ccdev = cdev->devdata;
 445
 446        clk_notifier_unregister(ccdev->clk, &ccdev->clk_rate_change_nb);
 447        dev_pm_opp_free_cpufreq_table(ccdev->dev, &ccdev->freq_table);
 448
 449        thermal_cooling_device_unregister(ccdev->cdev);
 450        ida_simple_remove(&clock_ida, ccdev->id);
 451}
 452EXPORT_SYMBOL_GPL(clock_cooling_unregister);
 453