root/drivers/thermal/clock_cooling.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. clock_cooling_get_property
  2. clock_cooling_get_level
  3. clock_cooling_get_frequency
  4. clock_cooling_apply
  5. clock_cooling_clock_notifier
  6. clock_cooling_get_max_state
  7. clock_cooling_get_cur_state
  8. clock_cooling_set_cur_state
  9. clock_cooling_register
  10. clock_cooling_unregister

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

/* [<][>][^][v][top][bottom][index][help] */