/drivers/thermal/samsung/gpu_cooling.c
C | 460 lines | 213 code | 66 blank | 181 comment | 36 complexity | 134dfd86a31313210f4f839ce3054058 MD5 | raw file
Possible License(s): GPL-2.0
- /*
- * linux/drivers/thermal/gpu_cooling.c
- *
- * Copyright (C) 2012 Samsung Electronics Co., Ltd(http://www.samsung.com)
- * Copyright (C) 2012 Amit Daniel <amit.kachhap@linaro.org>
- *
- * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; version 2 of the License.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
- *
- * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- */
- #include <linux/module.h>
- #include <linux/thermal.h>
- #include <linux/cpufreq.h>
- #include <linux/err.h>
- #include <linux/slab.h>
- #include <linux/cpu.h>
- #include <linux/gpu_cooling.h>
- #include <soc/samsung/tmu.h>
- #include "exynos_tmu_data.h"
- /**
- * struct gpufreq_cooling_device - data for cooling device with gpufreq
- * @id: unique integer value corresponding to each gpufreq_cooling_device
- * registered.
- * @cool_dev: thermal_cooling_device pointer to keep track of the
- * registered cooling device.
- * @gpufreq_state: integer value representing the current state of gpufreq
- * cooling devices.
- * @gpufreq_val: integer value representing the absolute value of the clipped
- * frequency.
- * @allowed_gpus: all the gpus involved for this gpufreq_cooling_device.
- *
- * This structure is required for keeping information of each
- * gpufreq_cooling_device registered. In order to prevent corruption of this a
- * mutex lock cooling_gpu_lock is used.
- */
- struct gpufreq_cooling_device {
- int id;
- struct thermal_cooling_device *cool_dev;
- unsigned long gpufreq_state;
- unsigned int gpufreq_val;
- };
- static DEFINE_IDR(gpufreq_idr);
- static DEFINE_MUTEX(cooling_gpu_lock);
- static BLOCKING_NOTIFIER_HEAD(gpu_notifier);
- static unsigned int gpufreq_dev_count;
- extern struct cpufreq_frequency_table gpu_freq_table[];
- /**
- * get_idr - function to get a unique id.
- * @idr: struct idr * handle used to create a id.
- * @id: int * value generated by this function.
- *
- * This function will populate @id with an unique
- * id, using the idr API.
- *
- * Return: 0 on success, an error code on failure.
- */
- static int get_idr(struct idr *idr, int *id)
- {
- int ret;
- mutex_lock(&cooling_gpu_lock);
- ret = idr_alloc(idr, NULL, 0, 0, GFP_KERNEL);
- mutex_unlock(&cooling_gpu_lock);
- if (unlikely(ret < 0))
- return ret;
- *id = ret;
- return 0;
- }
- /**
- * release_idr - function to free the unique id.
- * @idr: struct idr * handle used for creating the id.
- * @id: int value representing the unique id.
- */
- static void release_idr(struct idr *idr, int id)
- {
- mutex_lock(&cooling_gpu_lock);
- idr_remove(idr, id);
- mutex_unlock(&cooling_gpu_lock);
- }
- /* Below code defines functions to be used for gpufreq as cooling device */
- enum gpufreq_cooling_property {
- GET_LEVEL,
- GET_FREQ,
- GET_MAXL,
- };
- /**
- * get_property - fetch a property of interest for a give gpu.
- * @gpu: gpu for which the property is required
- * @input: query parameter
- * @output: query return
- * @property: type of query (frequency, level, max level)
- *
- * This is the common function to
- * 1. get maximum gpu cooling states
- * 2. translate frequency to cooling state
- * 3. translate cooling state to frequency
- * Note that the code may be not in good shape
- * but it is written in this way in order to:
- * a) reduce duplicate code as most of the code can be shared.
- * b) make sure the logic is consistent when translating between
- * cooling states and frequencies.
- *
- * Return: 0 on success, -EINVAL when invalid parameters are passed.
- */
- static int get_property(unsigned int gpu, unsigned long input,
- unsigned int *output,
- enum gpufreq_cooling_property property)
- {
- int i;
- unsigned long max_level = 0, level = 0;
- unsigned int freq = CPUFREQ_ENTRY_INVALID;
- int descend = -1;
- struct cpufreq_frequency_table *pos, *table =
- gpu_freq_table;
- if (!output)
- return -EINVAL;
- cpufreq_for_each_valid_entry(pos, table) {
- /* ignore duplicate entry */
- if (freq == pos->frequency)
- continue;
- /* get the frequency order */
- if (freq != CPUFREQ_ENTRY_INVALID && descend == -1)
- descend = freq > pos->frequency;
- freq = pos->frequency;
- max_level++;
- }
- /* No valid cpu frequency entry */
- if (max_level == 0)
- return -EINVAL;
- /* max_level is an index, not a counter */
- max_level--;
- /* get max level */
- if (property == GET_MAXL) {
- *output = (unsigned int)max_level;
- return 0;
- }
- if (property == GET_FREQ)
- level = descend ? input : (max_level - input);
- i = 0;
- cpufreq_for_each_valid_entry(pos, table) {
- /* ignore duplicate entry */
- if (freq == pos->frequency)
- continue;
- /* now we have a valid frequency entry */
- freq = pos->frequency;
- if (property == GET_LEVEL && (unsigned int)input == freq) {
- /* get level by frequency */
- *output = descend ? i : (max_level - i);
- return 0;
- }
- if (property == GET_FREQ && level == i) {
- /* get frequency by level */
- *output = freq;
- return 0;
- }
- i++;
- }
- return -EINVAL;
- }
- /**
- * gpufreq_cooling_get_level - for a give gpu, return the cooling level.
- * @gpu: gpu for which the level is required
- * @freq: the frequency of interest
- *
- * This function will match the cooling level corresponding to the
- * requested @freq and return it.
- *
- * Return: The matched cooling level on success or THERMAL_CSTATE_INVALID
- * otherwise.
- */
- unsigned long gpufreq_cooling_get_level(unsigned int gpu, unsigned int freq)
- {
- unsigned int val;
- if (get_property(gpu, (unsigned long)freq, &val, GET_LEVEL))
- return THERMAL_CSTATE_INVALID;
- return (unsigned long)val;
- }
- EXPORT_SYMBOL_GPL(gpufreq_cooling_get_level);
- /**
- * gpufreq_apply_cooling - function to apply frequency clipping.
- * @gpufreq_device: gpufreq_cooling_device pointer containing frequency
- * clipping data.
- * @cooling_state: value of the cooling state.
- *
- * Function used to make sure the gpufreq layer is aware of current thermal
- * limits. The limits are applied by updating the gpufreq policy.
- *
- * Return: 0 on success, an error code otherwise (-EINVAL in case wrong
- * cooling state).
- */
- static int gpufreq_apply_cooling(struct gpufreq_cooling_device *gpufreq_device,
- unsigned long cooling_state)
- {
- /* Check if the old cooling action is same as new cooling action */
- if (gpufreq_device->gpufreq_state == cooling_state)
- return 0;
- gpufreq_device->gpufreq_state = cooling_state;
- blocking_notifier_call_chain(&gpu_notifier, GPU_THROTTLING, &cooling_state);
- return 0;
- }
- /* gpufreq cooling device callback functions are defined below */
- /**
- * gpufreq_get_max_state - callback function to get the max cooling state.
- * @cdev: thermal cooling device pointer.
- * @state: fill this variable with the max cooling state.
- *
- * Callback for the thermal cooling device to return the gpufreq
- * max cooling state.
- *
- * Return: 0 on success, an error code otherwise.
- */
- static int gpufreq_get_max_state(struct thermal_cooling_device *cdev,
- unsigned long *state)
- {
- unsigned int count = 0;
- int ret;
- ret = get_property(0, 0, &count, GET_MAXL);
- if (count > 0)
- *state = count;
- return ret;
- }
- /**
- * gpufreq_get_cur_state - callback function to get the current cooling state.
- * @cdev: thermal cooling device pointer.
- * @state: fill this variable with the current cooling state.
- *
- * Callback for the thermal cooling device to return the gpufreq
- * current cooling state.
- *
- * Return: 0 on success, an error code otherwise.
- */
- static int gpufreq_get_cur_state(struct thermal_cooling_device *cdev,
- unsigned long *state)
- {
- struct gpufreq_cooling_device *gpufreq_device = cdev->devdata;
- *state = gpufreq_device->gpufreq_state;
- return 0;
- }
- /**
- * gpufreq_set_cur_state - callback function to set the current cooling state.
- * @cdev: thermal cooling device pointer.
- * @state: set this variable to the current cooling state.
- *
- * Callback for the thermal cooling device to change the gpufreq
- * current cooling state.
- *
- * Return: 0 on success, an error code otherwise.
- */
- static int gpufreq_set_cur_state(struct thermal_cooling_device *cdev,
- unsigned long state)
- {
- struct gpufreq_cooling_device *gpufreq_device = cdev->devdata;
- return gpufreq_apply_cooling(gpufreq_device, state);
- }
- static enum tmu_noti_state_t gpu_tstate = GPU_COLD;
- int gpufreq_set_cur_temp(bool suspended, unsigned long temp)
- {
- enum tmu_noti_state_t tstate;
- if (temp < EXYNOS_COLD_TEMP)
- tstate = GPU_COLD;
- else
- tstate = GPU_NORMAL;
- if (gpu_tstate == tstate)
- return 0;
- gpu_tstate = tstate;
- blocking_notifier_call_chain(&gpu_notifier, tstate, &tstate);
- return 0;
- }
- /* Bind gpufreq callbacks to thermal cooling device ops */
- static struct thermal_cooling_device_ops const gpufreq_cooling_ops = {
- .get_max_state = gpufreq_get_max_state,
- .get_cur_state = gpufreq_get_cur_state,
- .set_cur_state = gpufreq_set_cur_state,
- };
- int exynos_gpu_add_notifier(struct notifier_block *n)
- {
- return blocking_notifier_chain_register(&gpu_notifier, n);
- }
- /**
- * __gpufreq_cooling_register - helper function to create gpufreq cooling device
- * @np: a valid struct device_node to the cooling device device tree node
- * @clip_gpus: gpumask of gpus where the frequency constraints will happen.
- *
- * This interface function registers the gpufreq cooling device with the name
- * "thermal-gpufreq-%x". This api can support multiple instances of gpufreq
- * cooling devices. It also gives the opportunity to link the cooling device
- * with a device tree node, in order to bind it via the thermal DT code.
- *
- * Return: a valid struct thermal_cooling_device pointer on success,
- * on failure, it returns a corresponding ERR_PTR().
- */
- static struct thermal_cooling_device *
- __gpufreq_cooling_register(struct device_node *np,
- const struct cpumask *clip_gpus)
- {
- struct thermal_cooling_device *cool_dev;
- struct gpufreq_cooling_device *gpufreq_dev = NULL;
- char dev_name[THERMAL_NAME_LENGTH];
- int ret = 0;
- gpufreq_dev = kzalloc(sizeof(struct gpufreq_cooling_device),
- GFP_KERNEL);
- if (!gpufreq_dev)
- return ERR_PTR(-ENOMEM);
- ret = get_idr(&gpufreq_idr, &gpufreq_dev->id);
- if (ret) {
- kfree(gpufreq_dev);
- return ERR_PTR(-EINVAL);
- }
- snprintf(dev_name, sizeof(dev_name), "thermal-gpufreq-%d",
- gpufreq_dev->id);
- cool_dev = thermal_of_cooling_device_register(np, dev_name, gpufreq_dev,
- &gpufreq_cooling_ops);
- if (IS_ERR(cool_dev)) {
- release_idr(&gpufreq_idr, gpufreq_dev->id);
- kfree(gpufreq_dev);
- return cool_dev;
- }
- gpufreq_dev->cool_dev = cool_dev;
- gpufreq_dev->gpufreq_state = 0;
- mutex_lock(&cooling_gpu_lock);
- gpufreq_dev_count++;
- mutex_unlock(&cooling_gpu_lock);
- return cool_dev;
- }
- /**
- * gpufreq_cooling_register - function to create gpufreq cooling device.
- * @clip_gpus: cpumask of gpus where the frequency constraints will happen.
- *
- * This interface function registers the gpufreq cooling device with the name
- * "thermal-gpufreq-%x". This api can support multiple instances of gpufreq
- * cooling devices.
- *
- * Return: a valid struct thermal_cooling_device pointer on success,
- * on failure, it returns a corresponding ERR_PTR().
- */
- struct thermal_cooling_device *
- gpufreq_cooling_register(const struct cpumask *clip_gpus)
- {
- return __gpufreq_cooling_register(NULL, clip_gpus);
- }
- EXPORT_SYMBOL_GPL(gpufreq_cooling_register);
- /**
- * of_gpufreq_cooling_register - function to create gpufreq cooling device.
- * @np: a valid struct device_node to the cooling device device tree node
- * @clip_gpus: cpumask of gpus where the frequency constraints will happen.
- *
- * This interface function registers the gpufreq cooling device with the name
- * "thermal-gpufreq-%x". This api can support multiple instances of gpufreq
- * cooling devices. Using this API, the gpufreq cooling device will be
- * linked to the device tree node provided.
- *
- * Return: a valid struct thermal_cooling_device pointer on success,
- * on failure, it returns a corresponding ERR_PTR().
- */
- struct thermal_cooling_device *
- of_gpufreq_cooling_register(struct device_node *np,
- const struct cpumask *clip_gpus)
- {
- if (!np)
- return ERR_PTR(-EINVAL);
- return __gpufreq_cooling_register(np, clip_gpus);
- }
- EXPORT_SYMBOL_GPL(of_gpufreq_cooling_register);
- /**
- * gpufreq_cooling_unregister - function to remove gpufreq cooling device.
- * @cdev: thermal cooling device pointer.
- *
- * This interface function unregisters the "thermal-gpufreq-%x" cooling device.
- */
- void gpufreq_cooling_unregister(struct thermal_cooling_device *cdev)
- {
- struct gpufreq_cooling_device *gpufreq_dev;
- if (!cdev)
- return;
- gpufreq_dev = cdev->devdata;
- mutex_lock(&cooling_gpu_lock);
- gpufreq_dev_count--;
- mutex_unlock(&cooling_gpu_lock);
- thermal_cooling_device_unregister(gpufreq_dev->cool_dev);
- release_idr(&gpufreq_idr, gpufreq_dev->id);
- kfree(gpufreq_dev);
- }
- EXPORT_SYMBOL_GPL(gpufreq_cooling_unregister);