Fixed MTP to work with TWRP

This commit is contained in:
awab228 2018-06-19 23:16:04 +02:00
commit f6dfaef42e
50820 changed files with 20846062 additions and 0 deletions

View file

@ -0,0 +1,19 @@
config EXYNOS_THERMAL
tristate "Exynos thermal management unit driver"
depends on ARCH_HAS_BANDGAP && OF
depends on CPU_THERMAL
help
If you say yes here you get support for the TMU (Thermal Management
Unit) driver for SAMSUNG EXYNOS series of SoCs. This driver initialises
the TMU, reports temperature and handles cooling action if defined.
This driver uses the Exynos core thermal APIs and TMU configuration
data from the supported SoCs.
config EXYNOS_THERMAL_CORE
bool "Core thermal framework support for EXYNOS SOCs"
depends on EXYNOS_THERMAL
help
If you say yes here you get support for EXYNOS TMU
(Thermal Management Unit) common registration/unregistration
functions to the core thermal layer and also to use the generic
CPU cooling APIs.

View file

@ -0,0 +1,7 @@
#
# Samsung thermal specific Makefile
#
obj-$(CONFIG_EXYNOS_THERMAL) += exynos_thermal.o
exynos_thermal-y := exynos_tmu.o
exynos_thermal-y += exynos_tmu_data.o
exynos_thermal-$(CONFIG_EXYNOS_THERMAL_CORE) += exynos_thermal_common.o

View file

@ -0,0 +1,590 @@
/*
* linux/drivers/thermal/cpu_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/cpu_cooling.h>
#include <soc/samsung/tmu.h>
#include "exynos_tmu_data.h"
#include <trace/events/exynos.h>
static DEFINE_IDR(cpufreq_idr);
static DEFINE_MUTEX(cooling_cpufreq_lock);
static BLOCKING_NOTIFIER_HEAD(cpu_notifier);
static unsigned int cpufreq_dev_count;
static LIST_HEAD(cpufreq_dev_list);
/**
* 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_cpufreq_lock);
ret = idr_alloc(idr, NULL, 0, 0, GFP_KERNEL);
mutex_unlock(&cooling_cpufreq_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_cpufreq_lock);
idr_remove(idr, id);
mutex_unlock(&cooling_cpufreq_lock);
}
/* Below code defines functions to be used for cpufreq as cooling device */
/**
* is_cpufreq_valid - function to check frequency transitioning capability.
* @cpu: cpu for which check is needed.
*
* This function will check the current state of the system if
* it is capable of changing the frequency for a given @cpu.
*
* Return: 0 if the system is not currently capable of changing
* the frequency of given cpu. !0 in case the frequency is changeable.
*/
static int is_cpufreq_valid(int cpu)
{
struct cpufreq_policy policy;
return (!cpufreq_get_policy(&policy, cpu) && policy.user_policy.governor);
}
enum cpufreq_cooling_property {
GET_LEVEL,
GET_FREQ,
GET_MAXL,
};
/**
* get_property - fetch a property of interest for a give cpu.
* @cpu: cpu 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 cpu 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 cpu, unsigned long input,
unsigned int *output,
enum cpufreq_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 =
cpufreq_frequency_get_table(cpu);
if (!output)
return -EINVAL;
if (!table)
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;
}
/**
* cpufreq_cooling_get_level - for a give cpu, return the cooling level.
* @cpu: cpu 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 cpufreq_cooling_get_level(unsigned int cpu, unsigned int freq)
{
unsigned int val;
if (get_property(cpu, (unsigned long)freq, &val, GET_LEVEL))
return THERMAL_CSTATE_INVALID;
return (unsigned long)val;
}
EXPORT_SYMBOL_GPL(cpufreq_cooling_get_level);
/**
* get_cpu_frequency - get the absolute value of frequency from level.
* @cpu: cpu for which frequency is fetched.
* @level: cooling level
*
* This function matches cooling level with frequency. Based on a cooling level
* of frequency, equals cooling state of cpu cooling device, it will return
* the corresponding frequency.
* e.g level=0 --> 1st MAX FREQ, level=1 ---> 2nd MAX FREQ, .... etc
*
* Return: 0 on error, the corresponding frequency otherwise.
*/
static unsigned int get_cpu_frequency(unsigned int cpu, unsigned long level)
{
int ret = 0;
unsigned int freq;
ret = get_property(cpu, level, &freq, GET_FREQ);
if (ret)
return 0;
return freq;
}
/**
* cpufreq_apply_cooling - function to apply frequency clipping.
* @cpufreq_device: cpufreq_cooling_device pointer containing frequency
* clipping data.
* @cooling_state: value of the cooling state.
*
* Function used to make sure the cpufreq layer is aware of current thermal
* limits. The limits are applied by updating the cpufreq policy.
*
* Return: 0 on success, an error code otherwise (-EINVAL in case wrong
* cooling state).
*/
static int cpufreq_apply_cooling(struct cpufreq_cooling_device *cpufreq_device,
unsigned long cooling_state)
{
unsigned int cpuid, clip_freq;
struct cpumask *mask = &cpufreq_device->allowed_cpus;
unsigned int cpu = cpumask_any(mask);
/* Check if the old cooling action is same as new cooling action */
if (cpufreq_device->cpufreq_state == cooling_state)
return 0;
clip_freq = get_cpu_frequency(cpu, cooling_state);
if (!clip_freq)
return -EINVAL;
cpufreq_device->cpufreq_state = cooling_state;
cpufreq_device->cpufreq_val = clip_freq;
for_each_cpu(cpuid, mask) {
if (is_cpufreq_valid(cpuid))
cpufreq_update_policy(cpuid);
}
return 0;
}
/**
* cpufreq_thermal_notifier - notifier callback for cpufreq policy change.
* @nb: struct notifier_block * with callback info.
* @event: value showing cpufreq event for which this function invoked.
* @data: callback-specific data
*
* Callback to hijack the notification on cpufreq policy transition.
* Every time there is a change in policy, we will intercept and
* update the cpufreq policy with thermal constraints.
*
* Return: 0 (success)
*/
static int cpufreq_thermal_notifier(struct notifier_block *nb,
unsigned long event, void *data)
{
struct cpufreq_policy *policy = data;
unsigned long max_freq = 0;
struct cpufreq_cooling_device *cpufreq_dev;
char *cooling_device_name;
if (event != CPUFREQ_ADJUST)
return 0;
mutex_lock(&cooling_cpufreq_lock);
list_for_each_entry(cpufreq_dev, &cpufreq_dev_list, node) {
if (!cpumask_test_cpu(policy->cpu,
&cpufreq_dev->allowed_cpus))
continue;
if (!cpufreq_dev->cpufreq_val)
cpufreq_dev->cpufreq_val = get_cpu_frequency(
cpumask_any(&cpufreq_dev->allowed_cpus),
cpufreq_dev->cpufreq_state);
max_freq = cpufreq_dev->cpufreq_val;
if (policy->max != max_freq) {
if (policy->cpu)
cooling_device_name = "cluster1";
else
cooling_device_name = "cluster0";
cpufreq_verify_within_limits(policy, 0, max_freq);
exynos_ss_thermal(NULL, 0, cooling_device_name, max_freq);
trace_exynos_thermal(NULL, 0, cooling_device_name, max_freq);
}
}
mutex_unlock(&cooling_cpufreq_lock);
return 0;
}
/* cpufreq cooling device callback functions are defined below */
/**
* cpufreq_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 cpufreq
* max cooling state.
*
* Return: 0 on success, an error code otherwise.
*/
static int cpufreq_get_max_state(struct thermal_cooling_device *cdev,
unsigned long *state)
{
struct cpufreq_cooling_device *cpufreq_device = cdev->devdata;
struct cpumask *mask = &cpufreq_device->allowed_cpus;
unsigned int cpu;
unsigned int count = 0;
int ret;
cpu = cpumask_any(mask);
ret = get_property(cpu, 0, &count, GET_MAXL);
if (count > 0)
*state = count;
return ret;
}
/**
* cpufreq_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 cpufreq
* current cooling state.
*
* Return: 0 on success, an error code otherwise.
*/
static int cpufreq_get_cur_state(struct thermal_cooling_device *cdev,
unsigned long *state)
{
struct cpufreq_cooling_device *cpufreq_device = cdev->devdata;
*state = cpufreq_device->cpufreq_state;
return 0;
}
/**
* cpufreq_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 cpufreq
* current cooling state.
*
* Return: 0 on success, an error code otherwise.
*/
static int cpufreq_set_cur_state(struct thermal_cooling_device *cdev,
unsigned long state)
{
struct cpufreq_cooling_device *cpufreq_device = cdev->devdata;
return cpufreq_apply_cooling(cpufreq_device, state);
}
static enum tmu_noti_state_t cpu_tstate = TMU_NORMAL;
int cpufreq_set_cur_temp(bool suspended, unsigned long temp)
{
enum tmu_noti_state_t tstate;
unsigned int on;
if (suspended || temp < EXYNOS_COLD_TEMP) {
tstate = TMU_COLD;
on = 1;
} else {
tstate = TMU_NORMAL;
on = 0;
}
if (cpu_tstate == tstate)
return 0;
cpu_tstate = tstate;
blocking_notifier_call_chain(&cpu_notifier, TMU_COLD, &on);
return 0;
}
/* Bind cpufreq callbacks to thermal cooling device ops */
static struct thermal_cooling_device_ops const cpufreq_cooling_ops = {
.get_max_state = cpufreq_get_max_state,
.get_cur_state = cpufreq_get_cur_state,
.set_cur_state = cpufreq_set_cur_state,
};
/* Notifier for cpufreq policy change */
static struct notifier_block thermal_cpufreq_notifier_block = {
.notifier_call = cpufreq_thermal_notifier,
};
int exynos_tmu_add_notifier(struct notifier_block *n)
{
return blocking_notifier_chain_register(&cpu_notifier, n);
}
/**
* __cpufreq_cooling_register - helper function to create cpufreq cooling device
* @np: a valid struct device_node to the cooling device device tree node
* @clip_cpus: cpumask of cpus where the frequency constraints will happen.
*
* This interface function registers the cpufreq cooling device with the name
* "thermal-cpufreq-%x". This api can support multiple instances of cpufreq
* 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 *
__cpufreq_cooling_register(struct device_node *np,
const struct cpumask *clip_cpus)
{
struct thermal_cooling_device *cool_dev;
struct cpufreq_cooling_device *cpufreq_dev = NULL;
unsigned int min = 0, max = 0;
char dev_name[THERMAL_NAME_LENGTH];
int ret = 0, i;
struct cpufreq_policy policy;
/* Verify that all the clip cpus have same freq_min, freq_max limit */
for_each_cpu(i, clip_cpus) {
/* continue if cpufreq policy not found and not return error */
if (!cpufreq_get_policy(&policy, i))
continue;
if (min == 0 && max == 0) {
min = policy.cpuinfo.min_freq;
max = policy.cpuinfo.max_freq;
} else {
if (min != policy.cpuinfo.min_freq ||
max != policy.cpuinfo.max_freq)
return ERR_PTR(-EINVAL);
}
}
cpufreq_dev = kzalloc(sizeof(struct cpufreq_cooling_device),
GFP_KERNEL);
if (!cpufreq_dev)
return ERR_PTR(-ENOMEM);
cpumask_copy(&cpufreq_dev->allowed_cpus, clip_cpus);
ret = get_idr(&cpufreq_idr, &cpufreq_dev->id);
if (ret) {
kfree(cpufreq_dev);
return ERR_PTR(-EINVAL);
}
snprintf(dev_name, sizeof(dev_name), "thermal-cpufreq-%d",
cpufreq_dev->id);
cool_dev = thermal_of_cooling_device_register(np, dev_name, cpufreq_dev,
&cpufreq_cooling_ops);
if (IS_ERR(cool_dev)) {
release_idr(&cpufreq_idr, cpufreq_dev->id);
kfree(cpufreq_dev);
return cool_dev;
}
cpufreq_dev->cool_dev = cool_dev;
cpufreq_dev->cpufreq_state = 0;
mutex_lock(&cooling_cpufreq_lock);
/* Register the notifier for first cpufreq cooling device */
if (cpufreq_dev_count == 0)
cpufreq_register_notifier(&thermal_cpufreq_notifier_block,
CPUFREQ_POLICY_NOTIFIER);
cpufreq_dev_count++;
list_add(&cpufreq_dev->node, &cpufreq_dev_list);
mutex_unlock(&cooling_cpufreq_lock);
return cool_dev;
}
/**
* cpufreq_cooling_register - function to create cpufreq cooling device.
* @clip_cpus: cpumask of cpus where the frequency constraints will happen.
*
* This interface function registers the cpufreq cooling device with the name
* "thermal-cpufreq-%x". This api can support multiple instances of cpufreq
* cooling devices.
*
* Return: a valid struct thermal_cooling_device pointer on success,
* on failure, it returns a corresponding ERR_PTR().
*/
struct thermal_cooling_device *
cpufreq_cooling_register(const struct cpumask *clip_cpus)
{
return __cpufreq_cooling_register(NULL, clip_cpus);
}
EXPORT_SYMBOL_GPL(cpufreq_cooling_register);
/**
* of_cpufreq_cooling_register - function to create cpufreq cooling device.
* @np: a valid struct device_node to the cooling device device tree node
* @clip_cpus: cpumask of cpus where the frequency constraints will happen.
*
* This interface function registers the cpufreq cooling device with the name
* "thermal-cpufreq-%x". This api can support multiple instances of cpufreq
* cooling devices. Using this API, the cpufreq 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_cpufreq_cooling_register(struct device_node *np,
const struct cpumask *clip_cpus)
{
if (!np)
return ERR_PTR(-EINVAL);
return __cpufreq_cooling_register(np, clip_cpus);
}
EXPORT_SYMBOL_GPL(of_cpufreq_cooling_register);
/**
* cpufreq_cooling_unregister - function to remove cpufreq cooling device.
* @cdev: thermal cooling device pointer.
*
* This interface function unregisters the "thermal-cpufreq-%x" cooling device.
*/
void cpufreq_cooling_unregister(struct thermal_cooling_device *cdev)
{
struct cpufreq_cooling_device *cpufreq_dev;
if (!cdev)
return;
cpufreq_dev = cdev->devdata;
mutex_lock(&cooling_cpufreq_lock);
list_del(&cpufreq_dev->node);
cpufreq_dev_count--;
/* Unregister the notifier for the last cpufreq cooling device */
if (cpufreq_dev_count == 0)
cpufreq_unregister_notifier(&thermal_cpufreq_notifier_block,
CPUFREQ_POLICY_NOTIFIER);
mutex_unlock(&cooling_cpufreq_lock);
thermal_cooling_device_unregister(cpufreq_dev->cool_dev);
release_idr(&cpufreq_idr, cpufreq_dev->id);
kfree(cpufreq_dev);
}
EXPORT_SYMBOL_GPL(cpufreq_cooling_unregister);

View file

@ -0,0 +1,641 @@
/*
* exynos_thermal_common.c - Samsung EXYNOS common thermal file
*
* Copyright (C) 2013 Samsung Electronics
* Amit Daniel Kachhap <amit.daniel@samsung.com>
*
* 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; either version 2 of the License, or
* (at your option) any later version.
*
* 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/cpu_cooling.h>
#include <linux/gpu_cooling.h>
#include <linux/isp_cooling.h>
#include <linux/err.h>
#include <linux/slab.h>
#include <linux/suspend.h>
#include <linux/thermal.h>
#include <linux/pm_qos.h>
#include <linux/cpufreq.h>
#include <soc/samsung/cpufreq.h>
#include "exynos_thermal_common.h"
unsigned long cpu_max_temp[2];
struct exynos_thermal_zone {
enum thermal_device_mode mode;
struct thermal_zone_device *therm_dev;
struct thermal_cooling_device *cool_dev[MAX_COOLING_DEVICE];
unsigned int cool_dev_size;
struct platform_device *exynos4_dev;
struct thermal_sensor_conf *sensor_conf;
bool bind;
};
static DEFINE_MUTEX (thermal_suspend_lock);
static bool suspended;
static bool is_cpu_hotplugged_out;
/* Get mode callback functions for thermal zone */
static int exynos_get_mode(struct thermal_zone_device *thermal,
enum thermal_device_mode *mode)
{
struct exynos_thermal_zone *th_zone = thermal->devdata;
if (th_zone)
*mode = th_zone->mode;
return 0;
}
/* Set mode callback functions for thermal zone */
static int exynos_set_mode(struct thermal_zone_device *thermal,
enum thermal_device_mode mode)
{
struct exynos_thermal_zone *th_zone = thermal->devdata;
if (!th_zone) {
dev_err(&thermal->device,
"thermal zone not registered\n");
return 0;
}
mutex_lock(&thermal->lock);
if (mode == THERMAL_DEVICE_ENABLED &&
!th_zone->sensor_conf->trip_data.trigger_falling)
thermal->polling_delay = IDLE_INTERVAL;
else
thermal->polling_delay = 0;
mutex_unlock(&thermal->lock);
th_zone->mode = mode;
thermal_zone_device_update(thermal);
dev_dbg(th_zone->sensor_conf->dev,
"thermal polling set for duration=%d msec\n",
thermal->polling_delay);
return 0;
}
/* Get trip type callback functions for thermal zone */
static int exynos_get_trip_type(struct thermal_zone_device *thermal, int trip,
enum thermal_trip_type *type)
{
struct exynos_thermal_zone *th_zone = thermal->devdata;
int max_trip = th_zone->sensor_conf->trip_data.trip_count;
int trip_type;
if (trip < 0 || trip >= max_trip)
return -EINVAL;
trip_type = th_zone->sensor_conf->trip_data.trip_type[trip];
if (trip_type == SW_TRIP)
*type = THERMAL_TRIP_CRITICAL;
else if (trip_type == THROTTLE_ACTIVE)
*type = THERMAL_TRIP_ACTIVE;
else if (trip_type == THROTTLE_PASSIVE)
*type = THERMAL_TRIP_PASSIVE;
else
return -EINVAL;
return 0;
}
/* Get trip temperature callback functions for thermal zone */
static int exynos_get_trip_temp(struct thermal_zone_device *thermal, int trip,
unsigned long *temp)
{
struct exynos_thermal_zone *th_zone = thermal->devdata;
int max_trip = th_zone->sensor_conf->trip_data.trip_count;
if (trip < 0 || trip >= max_trip)
return -EINVAL;
*temp = th_zone->sensor_conf->trip_data.trip_val[trip];
/* convert the temperature into millicelsius */
*temp = *temp * MCELSIUS;
return 0;
}
/* Get critical temperature callback functions for thermal zone */
static int exynos_get_crit_temp(struct thermal_zone_device *thermal,
unsigned long *temp)
{
struct exynos_thermal_zone *th_zone = thermal->devdata;
int max_trip = th_zone->sensor_conf->trip_data.trip_count;
/* Get the temp of highest trip*/
return exynos_get_trip_temp(thermal, max_trip - 1, temp);
}
/* Bind callback functions for thermal zone */
static int exynos_bind(struct thermal_zone_device *thermal,
struct thermal_cooling_device *cdev)
{
int ret = 0, i, tab_size, level;
struct freq_clip_table *tab_ptr, *clip_data;
struct exynos_thermal_zone *th_zone = thermal->devdata;
struct thermal_sensor_conf *data = th_zone->sensor_conf;
enum thermal_trip_type type;
struct cpufreq_policy policy;
#ifdef CONFIG_GPU_THERMAL
int gpu_max_freq, gpu_min_freq;
#endif
tab_ptr = (struct freq_clip_table *)data->cooling_data.freq_data;
tab_size = data->cooling_data.freq_clip_count;
if (tab_ptr == NULL || tab_size == 0)
return 0;
/* find the cooling device registered*/
for (i = 0; i < th_zone->cool_dev_size; i++)
if (cdev == th_zone->cool_dev[i])
break;
/* No matching cooling device */
if (i == th_zone->cool_dev_size)
return 0;
/* Bind the thermal zone to the cpufreq cooling device */
for (i = 0; i < tab_size; i++) {
clip_data = (struct freq_clip_table *)&(tab_ptr[i]);
if(data->d_type == CLUSTER0 || data->d_type == CLUSTER1) {
ret = cpufreq_get_policy(&policy, data->d_type);
if (ret)
return -EINVAL;
if (clip_data->freq_clip_max > policy.max) {
pr_warn("%s: Throttling freq(%d) is greater than policy max(%d)\n", __func__, clip_data->freq_clip_max, policy.max);
clip_data->freq_clip_max = policy.max;
} else if (clip_data->freq_clip_max < policy.min){
pr_warn("%s: Throttling freq(%d) is lower than policy min(%d)\n", __func__, clip_data->freq_clip_max, policy.min);
clip_data->freq_clip_max = policy.min;
}
}
#ifdef CONFIG_GPU_THERMAL
if(data->d_type == GPU) {
gpu_max_freq = gpu_dvfs_get_max_freq() * 1000;
gpu_min_freq = gpu_dvfs_get_min_freq() * 1000;
if(gpu_max_freq > 0 && gpu_min_freq > 0) {
if (clip_data->freq_clip_max > gpu_max_freq) {
pr_warn("%s: GPU Throttling freq(%d) is greater than max(%d)\n", __func__, clip_data->freq_clip_max, gpu_max_freq);
clip_data->freq_clip_max = gpu_max_freq;
} else if (clip_data->freq_clip_max < gpu_min_freq){
pr_warn("%s: GPU Throttling freq(%d) is lower than min(%d)\n", __func__, clip_data->freq_clip_max, gpu_min_freq);
clip_data->freq_clip_max = gpu_min_freq;
}
}
}
#endif
if (data->d_type == CLUSTER0)
level = cpufreq_cooling_get_level(0, clip_data->freq_clip_max);
else if (data->d_type == CLUSTER1)
level = cpufreq_cooling_get_level(4, clip_data->freq_clip_max);
else if (data->d_type == GPU)
level = gpufreq_cooling_get_level(0, clip_data->freq_clip_max);
else if (data->d_type == ISP)
level = isp_cooling_get_fps(0, clip_data->freq_clip_max);
else
level = (int)THERMAL_CSTATE_INVALID;
if (level == THERMAL_CSTATE_INVALID) {
pr_warn("Bind fail is occurred because level is invalid.\n");
return 0;
}
exynos_get_trip_type(thermal, i, &type);
switch (GET_ZONE(type)) {
case MONITOR_ZONE:
case WARN_ZONE:
if (thermal_zone_bind_cooling_device(thermal, i, cdev,
level, 0)) {
dev_err(data->dev,
"error unbinding cdev inst=%d\n", i);
ret = -EINVAL;
}
th_zone->bind = true;
break;
default:
ret = -EINVAL;
}
}
return ret;
}
/* Unbind callback functions for thermal zone */
static int exynos_unbind(struct thermal_zone_device *thermal,
struct thermal_cooling_device *cdev)
{
int ret = 0, i, tab_size;
struct exynos_thermal_zone *th_zone = thermal->devdata;
struct thermal_sensor_conf *data = th_zone->sensor_conf;
enum thermal_trip_type type;
if (th_zone->bind == false)
return 0;
tab_size = data->cooling_data.freq_clip_count;
if (tab_size == 0)
return 0;
/* find the cooling device registered*/
for (i = 0; i < th_zone->cool_dev_size; i++)
if (cdev == th_zone->cool_dev[i])
break;
/* No matching cooling device */
if (i == th_zone->cool_dev_size)
return 0;
/* Bind the thermal zone to the cpufreq cooling device */
for (i = 0; i < tab_size; i++) {
exynos_get_trip_type(thermal, i, &type);
switch (GET_ZONE(type)) {
case MONITOR_ZONE:
case WARN_ZONE:
if (thermal_zone_unbind_cooling_device(thermal, i,
cdev)) {
dev_err(data->dev,
"error unbinding cdev inst=%d\n", i);
ret = -EINVAL;
}
th_zone->bind = false;
break;
default:
ret = -EINVAL;
}
}
return ret;
}
#ifdef CONFIG_CPU_THERMAL
extern int cpufreq_set_cur_temp(bool suspended, unsigned long temp);
#else
static inline int cpufreq_set_cur_temp(bool suspended, unsigned long temp) { return 0; }
#endif
#ifdef CONFIG_GPU_THERMAL
extern int gpufreq_set_cur_temp(bool suspended, unsigned long temp);
#else
static inline int gpufreq_set_cur_temp(bool suspended, unsigned long temp) { return 0; }
#endif
#ifdef CONFIG_ISP_THERMAL
extern int isp_set_cur_temp(bool suspended, unsigned long temp);
#else
static inline int isp_set_cur_temp(bool suspended, unsigned long temp) { return 0; }
#endif
/* Get temperature callback functions for thermal zone */
static int exynos_get_temp(struct thermal_zone_device *thermal,
unsigned long *temp)
{
struct exynos_thermal_zone *th_zone = thermal->devdata;
void *data;
unsigned long max_temp;
if (!th_zone->sensor_conf) {
dev_err(&thermal->device,
"Temperature sensor not initialised\n");
return -EINVAL;
}
data = th_zone->sensor_conf->driver_data;
*temp = th_zone->sensor_conf->read_temperature(data);
/* convert the temperature into millicelsius */
*temp = *temp * MCELSIUS;
mutex_lock(&thermal_suspend_lock);
if (th_zone->sensor_conf->d_type == CLUSTER0 || th_zone->sensor_conf->d_type == CLUSTER1) {
cpu_max_temp[th_zone->sensor_conf->d_type] = *temp;
max_temp = max(cpu_max_temp[CLUSTER0], cpu_max_temp[CLUSTER1]);
cpufreq_set_cur_temp(suspended, max_temp / 1000);
} else if (th_zone->sensor_conf->d_type == GPU)
gpufreq_set_cur_temp(suspended, *temp / 1000);
else if (th_zone->sensor_conf->d_type == ISP)
isp_set_cur_temp(suspended, *temp / 1000);
mutex_unlock(&thermal_suspend_lock);
return 0;
}
/* Get temperature callback functions for thermal zone */
static int exynos_set_emul_temp(struct thermal_zone_device *thermal,
unsigned long temp)
{
void *data;
int ret = -EINVAL;
struct exynos_thermal_zone *th_zone = thermal->devdata;
if (!th_zone->sensor_conf) {
dev_err(&thermal->device,
"Temperature sensor not initialised\n");
return -EINVAL;
}
data = th_zone->sensor_conf->driver_data;
if (th_zone->sensor_conf->write_emul_temp)
ret = th_zone->sensor_conf->write_emul_temp(data, temp);
return ret;
}
/* Get the temperature trend */
static int exynos_get_trend(struct thermal_zone_device *thermal,
int trip, enum thermal_trend *trend)
{
int ret;
unsigned long trip_temp;
ret = exynos_get_trip_temp(thermal, trip, &trip_temp);
if (ret < 0)
return ret;
if (thermal->temperature >= trip_temp)
*trend = THERMAL_TREND_RAISE_FULL;
else
*trend = THERMAL_TREND_DROP_FULL;
return 0;
}
struct pm_qos_request thermal_cpu_hotplug_request;
static int exynos_throttle_cpu_hotplug(struct thermal_zone_device *thermal)
{
struct exynos_thermal_zone *th_zone = thermal->devdata;
struct thermal_sensor_conf *data = th_zone->sensor_conf;
struct cpufreq_cooling_device *cpufreq_device = (struct cpufreq_cooling_device *)th_zone->cool_dev[0]->devdata;
int ret = 0;
int cur_temp = 0;
if (!thermal->temperature)
return -EINVAL;
cur_temp = thermal->temperature / MCELSIUS;
if (is_cpu_hotplugged_out) {
if (cur_temp < data->hotplug_in_threshold) {
/*
* If current temperature is lower than low threshold,
* call cluster1_cores_hotplug(false) for hotplugged out cpus.
*/
pm_qos_update_request(&thermal_cpu_hotplug_request, NR_CPUS);
is_cpu_hotplugged_out = false;
cpufreq_device->cpufreq_state = 0;
}
} else {
if (cur_temp >= data->hotplug_out_threshold) {
/*
* If current temperature is higher than high threshold,
* call cluster1_cores_hotplug(true) to hold temperature down.
*/
pm_qos_update_request(&thermal_cpu_hotplug_request, NR_CLUST1_CPUS);
is_cpu_hotplugged_out = true;
}
}
return ret;
}
/* Operation callback functions for thermal zone */
static struct thermal_zone_device_ops exynos_dev_ops = {
.bind = exynos_bind,
.unbind = exynos_unbind,
.get_temp = exynos_get_temp,
.set_emul_temp = exynos_set_emul_temp,
.get_trend = exynos_get_trend,
.get_mode = exynos_get_mode,
.set_mode = exynos_set_mode,
.get_trip_type = exynos_get_trip_type,
.get_trip_temp = exynos_get_trip_temp,
.get_crit_temp = exynos_get_crit_temp,
};
/* Operation callback functions for thermal zone */
static struct thermal_zone_device_ops exynos_dev_hotplug_ops = {
.bind = exynos_bind,
.unbind = exynos_unbind,
.get_temp = exynos_get_temp,
.set_emul_temp = exynos_set_emul_temp,
.get_trend = exynos_get_trend,
.get_mode = exynos_get_mode,
.set_mode = exynos_set_mode,
.get_trip_type = exynos_get_trip_type,
.get_trip_temp = exynos_get_trip_temp,
.get_crit_temp = exynos_get_crit_temp,
.throttle_cpu_hotplug = exynos_throttle_cpu_hotplug,
};
/*
* This function may be called from interrupt based temperature sensor
* when threshold is changed.
*/
void exynos_report_trigger(struct thermal_sensor_conf *conf)
{
unsigned int i;
char data[10];
char *envp[] = { data, NULL };
struct exynos_thermal_zone *th_zone;
if (!conf || !conf->pzone_data) {
pr_err("Invalid temperature sensor configuration data\n");
return;
}
th_zone = conf->pzone_data;
if (th_zone->bind == false) {
for (i = 0; i < th_zone->cool_dev_size; i++) {
if (!th_zone->cool_dev[i])
continue;
exynos_bind(th_zone->therm_dev,
th_zone->cool_dev[i]);
}
}
thermal_zone_device_update(th_zone->therm_dev);
mutex_lock(&th_zone->therm_dev->lock);
/* Find the level for which trip happened */
for (i = 0; i < th_zone->sensor_conf->trip_data.trip_count; i++) {
if (th_zone->therm_dev->last_temperature <
th_zone->sensor_conf->trip_data.trip_val[i] * MCELSIUS)
break;
}
if (th_zone->mode == THERMAL_DEVICE_ENABLED &&
!th_zone->sensor_conf->trip_data.trigger_falling) {
if (i > 0)
th_zone->therm_dev->polling_delay = ACTIVE_INTERVAL;
else
th_zone->therm_dev->polling_delay = IDLE_INTERVAL;
}
snprintf(data, sizeof(data), "%u", i);
kobject_uevent_env(&th_zone->therm_dev->device.kobj, KOBJ_CHANGE, envp);
mutex_unlock(&th_zone->therm_dev->lock);
}
static int exynos_pm_notifier(struct notifier_block *notifier,
unsigned long event, void *v)
{
switch (event) {
case PM_SUSPEND_PREPARE:
mutex_lock(&thermal_suspend_lock);
suspended = true;
cpufreq_set_cur_temp(suspended, 0);
gpufreq_set_cur_temp(suspended, 0);
isp_set_cur_temp(suspended, 0);
mutex_unlock(&thermal_suspend_lock);
break;
case PM_POST_SUSPEND:
mutex_lock(&thermal_suspend_lock);
suspended = false;
mutex_unlock(&thermal_suspend_lock);
break;
}
return NOTIFY_OK;
}
static struct notifier_block exynos_tmu_pm_notifier = {
.notifier_call = exynos_pm_notifier,
};
/* Register with the in-kernel thermal management */
int exynos_register_thermal(struct thermal_sensor_conf *sensor_conf)
{
int ret, cpu;
struct cpumask mask_val;
struct exynos_thermal_zone *th_zone;
struct thermal_zone_device_ops *dev_ops;
if (!sensor_conf || !sensor_conf->read_temperature) {
pr_err("Temperature sensor not initialised\n");
return -EINVAL;
}
th_zone = devm_kzalloc(sensor_conf->dev,
sizeof(struct exynos_thermal_zone), GFP_KERNEL);
if (!th_zone)
return -ENOMEM;
th_zone->sensor_conf = sensor_conf;
cpumask_clear(&mask_val);
for_each_possible_cpu(cpu) {
if (cpu_topology[cpu].cluster_id == sensor_conf->id) {
cpumask_copy(&mask_val, topology_core_cpumask(cpu));
break;
}
}
/*
* TODO: 1) Handle multiple cooling devices in a thermal zone
* 2) Add a flag/name in cooling info to map to specific
* sensor
*/
if (sensor_conf->cooling_data.freq_clip_count > 0) {
if (sensor_conf->d_type == CLUSTER0 || sensor_conf->d_type == CLUSTER1) {
th_zone->cool_dev[th_zone->cool_dev_size] =
cpufreq_cooling_register(&mask_val);
} else if (sensor_conf->d_type == GPU) {
th_zone->cool_dev[th_zone->cool_dev_size] =
gpufreq_cooling_register(&mask_val);
} else if (sensor_conf->d_type == ISP) {
th_zone->cool_dev[th_zone->cool_dev_size] =
isp_cooling_register(&mask_val);
}
if (IS_ERR(th_zone->cool_dev[th_zone->cool_dev_size])) {
dev_err(sensor_conf->dev,
"Failed to register cpufreq cooling device\n");
ret = -EINVAL;
goto err_unregister;
}
th_zone->cool_dev_size++;
}
/* Add hotplug function ops */
if (sensor_conf->hotplug_enable) {
dev_ops = &exynos_dev_hotplug_ops;
pm_qos_add_request(&thermal_cpu_hotplug_request, PM_QOS_CPU_ONLINE_MAX,
PM_QOS_CPU_ONLINE_MAX_DEFAULT_VALUE);
} else
dev_ops = &exynos_dev_ops;
th_zone->therm_dev = thermal_zone_device_register(
sensor_conf->name, sensor_conf->trip_data.trip_count,
0, th_zone, dev_ops, NULL, 0,
sensor_conf->trip_data.trigger_falling ? 0 :
IDLE_INTERVAL);
if (IS_ERR(th_zone->therm_dev)) {
dev_err(sensor_conf->dev,
"Failed to register thermal zone device\n");
ret = PTR_ERR(th_zone->therm_dev);
goto err_unregister;
}
th_zone->mode = THERMAL_DEVICE_ENABLED;
sensor_conf->pzone_data = th_zone;
if (sensor_conf->id == 0)
register_pm_notifier(&exynos_tmu_pm_notifier);
dev_info(sensor_conf->dev,
"Exynos: Thermal zone(%s) registered\n", sensor_conf->name);
return 0;
err_unregister:
exynos_unregister_thermal(sensor_conf);
return ret;
}
/* Un-Register with the in-kernel thermal management */
void exynos_unregister_thermal(struct thermal_sensor_conf *sensor_conf)
{
int i;
struct exynos_thermal_zone *th_zone;
if (!sensor_conf || !sensor_conf->pzone_data) {
pr_err("Invalid temperature sensor configuration data\n");
return;
}
th_zone = sensor_conf->pzone_data;
thermal_zone_device_unregister(th_zone->therm_dev);
for (i = 0; i < th_zone->cool_dev_size; i++) {
if (sensor_conf->d_type == CLUSTER0 || sensor_conf->d_type == CLUSTER1)
cpufreq_cooling_unregister(th_zone->cool_dev[i]);
else if (sensor_conf->d_type == GPU)
gpufreq_cooling_unregister(th_zone->cool_dev[i]);
else if (sensor_conf->d_type == ISP)
isp_cooling_unregister(th_zone->cool_dev[i]);
}
if (sensor_conf->id == 0)
unregister_pm_notifier(&exynos_tmu_pm_notifier);
dev_info(sensor_conf->dev,
"Exynos: Kernel Thermal management unregistered\n");
}

View file

@ -0,0 +1,134 @@
/*
* exynos_thermal_common.h - Samsung EXYNOS common header file
*
* Copyright (C) 2013 Samsung Electronics
* Amit Daniel Kachhap <amit.daniel@samsung.com>
*
* 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; either version 2 of the License, or
* (at your option) any later version.
*
* 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
*
*/
#ifndef _EXYNOS_THERMAL_COMMON_H
#define _EXYNOS_THERMAL_COMMON_H
#include <linux/types.h>
/* In-kernel thermal framework related macros & definations */
#define SENSOR_NAME_LEN 16
#define MAX_TRIP_COUNT 8
#define MAX_COOLING_DEVICE 4
#define MAX_TRIMINFO_CTRL_REG 2
#define ACTIVE_INTERVAL 300
#define IDLE_INTERVAL 500
#define MCELSIUS 1000
/* CPU Zone information */
#define PANIC_ZONE 4
#define WARN_ZONE 3
#define MONITOR_ZONE 2
#define SAFE_ZONE 1
#define GET_ZONE(trip) (trip + 2)
#define GET_TRIP(zone) (zone - 2)
/* Bit type */
#define TYPE_8BIT_MASK (0xFF)
#define TYPE_9BIT_MASK (0x1FF)
enum trigger_type {
THROTTLE_ACTIVE = 1,
THROTTLE_PASSIVE,
SW_TRIP,
HW_TRIP,
};
enum dev_type {
CLUSTER0,
CLUSTER1,
GPU,
ISP,
};
/**
* struct freq_clip_table
* @freq_clip_max: maximum frequency allowed for this cooling state.
* @temp_level: Temperature level at which the temperature clipping will
* happen.
* @mask_val: cpumask of the allowed cpu's where the clipping will take place.
*
* This structure is required to be filled and passed to the
* cpufreq_cooling_unregister function.
*/
struct freq_clip_table {
unsigned int freq_clip_max;
unsigned int temp_level;
const struct cpumask *mask_val;
};
struct thermal_trip_point_conf {
int trip_val[MAX_TRIP_COUNT];
int trip_type[MAX_TRIP_COUNT];
int trip_count;
unsigned char trigger_falling;
};
struct thermal_cooling_conf {
struct freq_clip_table freq_data[MAX_TRIP_COUNT];
int freq_clip_count;
};
struct thermal_sensor_conf {
char name[SENSOR_NAME_LEN];
int (*read_temperature)(void *data);
int (*write_emul_temp)(void *drv_data, unsigned long temp);
struct thermal_trip_point_conf trip_data;
struct thermal_cooling_conf cooling_data;
void *driver_data;
void *pzone_data;
struct device *dev;
enum dev_type d_type;
int id;
bool hotplug_enable;
int count;
int hotplug_in_threshold;
int hotplug_out_threshold;
};
/*Functions used exynos based thermal sensor driver*/
#ifdef CONFIG_EXYNOS_THERMAL_CORE
void exynos_unregister_thermal(struct thermal_sensor_conf *sensor_conf);
int exynos_register_thermal(struct thermal_sensor_conf *sensor_conf);
void exynos_report_trigger(struct thermal_sensor_conf *sensor_conf);
#else
static inline void
exynos_unregister_thermal(struct thermal_sensor_conf *sensor_conf) { return; }
static inline int
exynos_register_thermal(struct thermal_sensor_conf *sensor_conf) { return 0; }
static inline void
exynos_report_trigger(struct thermal_sensor_conf *sensor_conf) { return; }
#endif /* CONFIG_EXYNOS_THERMAL_CORE */
#if defined(CONFIG_GPU_THERMAL) && defined(CONFIG_MALI_DVFS)
extern int gpu_dvfs_get_max_freq(void);
extern int gpu_dvfs_get_min_freq(void);
#else
static inline int gpu_dvfs_get_max_freq(void) { return -1; };
static inline int gpu_dvfs_get_min_freq(void) { return -1; };
#endif
#endif /* _EXYNOS_THERMAL_COMMON_H */

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,323 @@
/*
* exynos_tmu.h - Samsung EXYNOS TMU (Thermal Management Unit)
*
* Copyright (C) 2011 Samsung Electronics
* Donggeun Kim <dg77.kim@samsung.com>
* Amit Daniel Kachhap <amit.daniel@samsung.com>
*
* 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; either version 2 of the License, or
* (at your option) any later version.
*
* 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
*/
#ifndef _EXYNOS_TMU_H
#define _EXYNOS_TMU_H
#include <linux/cpu_cooling.h>
#include "exynos_thermal_common.h"
#define FIRST_SENSOR 0
enum calibration_type {
TYPE_ONE_POINT_TRIMMING,
TYPE_ONE_POINT_TRIMMING_25,
TYPE_ONE_POINT_TRIMMING_85,
TYPE_TWO_POINT_TRIMMING,
TYPE_NONE,
};
enum calibration_mode {
SW_MODE,
HW_MODE,
};
enum soc_type {
SOC_ARCH_EXYNOS3250 = 1,
SOC_ARCH_EXYNOS4210,
SOC_ARCH_EXYNOS4412,
SOC_ARCH_EXYNOS5250,
SOC_ARCH_EXYNOS5260,
SOC_ARCH_EXYNOS5420_TRIMINFO,
SOC_ARCH_EXYNOS5440,
SOC_ARCH_EXYNOS7580,
SOC_ARCH_EXYNOS7870,
SOC_ARCH_EXYNOS7570,
SOC_ARCH_EXYNOS8890,
};
/**
* EXYNOS TMU supported features.
* TMU_SUPPORT_EMULATION - This features is used to set user defined
* temperature to the TMU controller.
* TMU_SUPPORT_MULTI_INST - This features denotes that the soc
* has many instances of TMU.
* TMU_SUPPORT_TRIM_RELOAD - This features shows that trimming can
* be reloaded.
* TMU_SUPPORT_FALLING_TRIP - This features shows that interrupt can
* be registered for falling trips also.
* TMU_SUPPORT_READY_STATUS - This feature tells that the TMU current
* state(active/idle) can be checked.
* TMU_SUPPORT_EMUL_TIME - This features allows to set next temp emulation
* sample time.
* TMU_SUPPORT_ADDRESS_MULTIPLE - This feature tells that the different TMU
* sensors shares some common registers.
* TMU_SUPPORT - macro to compare the above features with the supplied.
*/
#define TMU_SUPPORT_EMULATION BIT(0)
#define TMU_SUPPORT_MULTI_INST BIT(1)
#define TMU_SUPPORT_TRIM_RELOAD BIT(2)
#define TMU_SUPPORT_FALLING_TRIP BIT(3)
#define TMU_SUPPORT_READY_STATUS BIT(4)
#define TMU_SUPPORT_EMUL_TIME BIT(5)
#define TMU_SUPPORT_ADDRESS_MULTIPLE BIT(6)
#define TMU_SUPPORTS(a, b) (a->features & TMU_SUPPORT_ ## b)
/**
* struct exynos_tmu_register - register descriptors to access registers and
* bitfields. The register validity, offsets and bitfield values may vary
* slightly across different exynos SOC's.
* @triminfo_data: register containing 2 pont trimming data
* @triminfo_ctrl: trim info controller register.
* @triminfo_ctrl_count: the number of trim info controller register.
* @triminfo_85_shift: shift bit of the 85 C trim value in triminfo_data reg.
* @tmu_ctrl: TMU main controller register.
* @tmu_ctrl1: TMU main controller register.
* @test_mux_addr_shift: shift bits of test mux address.
* @therm_trip_mode_shift: shift bits of tripping mode in tmu_ctrl register.
* @therm_trip_mode_mask: mask bits of tripping mode in tmu_ctrl register.
* @therm_trip_en_shift: shift bits of tripping enable in tmu_ctrl register.
* @lpi0_mode_en_shift: shift bits of LIPI0 mode enable bit in tmu_ctrl1 register.
* @tmu_status: register drescribing the TMU status.
* @tmu_cur_temp: register containing the current temperature of the TMU.
* @threshold_temp: register containing the base threshold level.
* @threshold_th0: Register containing first set of rising levels.
* @threshold_th1: Register containing second set of rising levels.
* @threshold_th2: Register containing third set of rising levels.
* @threshold_th3: Register containing fourth set of rising levels.
* @threshold_th3_l0_shift: shift bits of level0 threshold temperature.
* @tmu_inten: register containing the different threshold interrupt
enable bits.
* @inten_rise0_shift: shift bits of rising 0 interrupt bits.
* @inten_rise1_shift: shift bits of rising 1 interrupt bits.
* @inten_rise2_shift: shift bits of rising 2 interrupt bits.
* @inten_rise3_shift: shift bits of rising 3 interrupt bits.
* @inten_rise4_shift: shift bits of rising 4 interrupt bits.
* @inten_rise5_shift: shift bits of rising 5 interrupt bits.
* @inten_rise6_shift: shift bits of rising 6 interrupt bits.
* @inten_rise7_shift: shift bits of rising 7 interrupt bits.
* @inten_fall0_shift: shift bits of falling 0 interrupt bits.
* @inten_fall1_shift: shift bits of falling 1 interrupt bits.
* @inten_fall2_shift: shift bits of falling 2 interrupt bits.
* @inten_fall3_shift: shift bits of falling 3 interrupt bits.
* @inten_fall4_shift: shift bits of falling 4 interrupt bits.
* @inten_fall5_shift: shift bits of falling 5 interrupt bits.
* @inten_fall6_shift: shift bits of falling 6 interrupt bits.
* @inten_fall7_shift: shift bits of falling 7 interrupt bits.
* @tmu_intstat: Register containing the interrupt status values.
* @tmu_intclear: Register for clearing the raised interrupt status.
* @emul_con: TMU emulation controller register.
* @emul_temp_shift: shift bits of emulation temperature.
* @emul_time_shift: shift bits of emulation time.
* @tmu_irqstatus: register to find which TMU generated interrupts.
* @tmu_pmin: register to get/set the Pmin value.
*/
struct exynos_tmu_registers {
u32 triminfo_data;
u32 calib_sel_shift;
u32 calib_sel_mask;
u32 triminfo_ctrl[MAX_TRIMINFO_CTRL_REG];
u32 triminfo_ctrl_count;
u32 triminfo_85_shift;
u32 tmu_ctrl;
u32 tmu_ctrl1;
u32 test_mux_addr_shift;
/* vref and slope otp value */
u32 buf_vref_otp_reg;
u32 buf_vref_otp_shift;
u32 buf_vref_otp_mask;
u32 buf_slope_otp_reg;
u32 buf_slope_otp_shift;
u32 buf_slope_otp_mask;
u32 therm_trip_mode_shift;
u32 therm_trip_mode_mask;
u32 therm_trip_en_shift;
u32 lpi0_mode_en_shift;
u32 tmu_status;
u32 valid_mask;
u32 valid_p0_shift;
u32 valid_p1_shift;
u32 valid_p2_shift;
u32 valid_p3_shift;
u32 valid_p4_shift;
u32 tmu_cur_temp;
u32 threshold_temp;
u32 threshold_th0;
u32 threshold_th1;
u32 threshold_th2;
u32 threshold_th3;
u32 threshold_th3_l0_shift;
u32 threshold_th4;
u32 threshold_th5;
u32 threshold_th6;
u32 threshold_th7;
u32 tmu_inten;
u32 tmu_intstat;
u32 inten_rise0_shift;
u32 inten_rise1_shift;
u32 inten_rise2_shift;
u32 inten_rise3_shift;
u32 inten_rise4_shift;
u32 inten_rise5_shift;
u32 inten_rise6_shift;
u32 inten_rise7_shift;
u32 inten_fall0_shift;
u32 inten_fall1_shift;
u32 inten_fall2_shift;
u32 inten_fall3_shift;
u32 inten_fall4_shift;
u32 inten_fall5_shift;
u32 inten_fall6_shift;
u32 inten_fall7_shift;
u32 tmu_intclear;
u32 emul_con;
u32 emul_temp_shift;
u32 emul_time_shift;
u32 tmu_irqstatus;
u32 tmu_pmin;
};
/**
* struct exynos_tmu_platform_data
* @threshold: basic temperature for generating interrupt
* 25 <= threshold <= 125 [unit: degree Celsius]
* @threshold_falling: differntial value for setting threshold
* of temperature falling interrupt.
* @trigger_levels: array for each interrupt levels
* [unit: degree Celsius]
* 0: temperature for trigger_level0 interrupt
* condition for trigger_level0 interrupt:
* current temperature > threshold + trigger_levels[0]
* 1: temperature for trigger_level1 interrupt
* condition for trigger_level1 interrupt:
* current temperature > threshold + trigger_levels[1]
* 2: temperature for trigger_level2 interrupt
* condition for trigger_level2 interrupt:
* current temperature > threshold + trigger_levels[2]
* 3: temperature for trigger_level3 interrupt
* condition for trigger_level3 interrupt:
* current temperature > threshold + trigger_levels[3]
* @trigger_type: defines the type of trigger. Possible values are,
* THROTTLE_ACTIVE trigger type
* THROTTLE_PASSIVE trigger type
* SW_TRIP trigger type
* HW_TRIP
* @trigger_enable[]: array to denote which trigger levels are enabled.
* 1 = enable trigger_level[] interrupt,
* 0 = disable trigger_level[] interrupt
* @max_trigger_level: max trigger level supported by the TMU
* @non_hw_trigger_levels: number of defined non-hardware trigger levels
* @gain: gain of amplifier in the positive-TC generator block
* 0 < gain <= 15
* @reference_voltage: reference voltage of amplifier
* in the positive-TC generator block
* 0 < reference_voltage <= 31
* @noise_cancel_mode: noise cancellation mode
* 000, 100, 101, 110 and 111 can be different modes
* @type: determines the type of SOC
* @efuse_value: platform defined fuse value
* @min_efuse_value: minimum valid trimming data
* @max_efuse_value: maximum valid trimming data
* @first_point_trim: temp value of the first point trimming
* @second_point_trim: temp value of the second point trimming
* @default_temp_offset: default temperature offset in case of no trimming
* @test_mux; information if SoC supports test MUX
* @triminfo_reload: reload value to read TRIMINFO register
* @cal_type: calibration type for temperature
* @freq_clip_table: Table representing frequency reduction percentage.
* @freq_tab_count: Count of the above table as frequency reduction may
* applicable to only some of the trigger levels.
* @registers: Pointer to structure containing all the TMU controller registers
* and bitfields shifts and masks.
* @features: a bitfield value indicating the features supported in SOC like
* emulation, multi instance etc
*
* This structure is required for configuration of exynos_tmu driver.
*/
struct exynos_tmu_platform_data {
char *tmu_name;
int ect_hotplug_flag;
int ect_hotplug_interval;
u32 temp_mask;
u8 threshold;
u8 threshold_falling;
u8 trigger_levels[MAX_TRIP_COUNT];
enum trigger_type trigger_type[MAX_TRIP_COUNT];
bool trigger_enable[MAX_TRIP_COUNT];
u8 max_trigger_level;
u8 non_hw_trigger_levels;
u8 default_ptat_cont;
u8 gain;
u8 reference_voltage;
u8 noise_cancel_mode;
u32 efuse_value;
u32 min_efuse_value;
u32 max_efuse_value;
u8 first_point_trim;
u8 second_point_trim;
u8 default_temp_offset;
u8 test_mux;
u8 triminfo_reload[MAX_TRIMINFO_CTRL_REG];
bool hotplug_enable;
u32 hotplug_in_threshold;
u32 hotplug_out_threshold;
enum calibration_type cal_type;
enum soc_type type;
enum dev_type d_type;
struct freq_clip_table freq_tab[MAX_TRIP_COUNT];
unsigned int freq_tab_count;
const struct exynos_tmu_registers *registers;
unsigned int features;
u8 sensor_type;
};
/**
* struct exynos_tmu_init_data
* @tmu_count: number of TMU instances.
* @tmu_data: platform data of all TMU instances.
* This structure is required to store data for multi-instance exynos tmu
* driver.
*/
struct exynos_tmu_init_data {
int tmu_count;
struct exynos_tmu_platform_data tmu_data[];
};
#endif /* _EXYNOS_TMU_H */

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,361 @@
/*
* exynos_tmu_data.h - Samsung EXYNOS tmu data header file
*
* Copyright (C) 2013 Samsung Electronics
* Amit Daniel Kachhap <amit.daniel@samsung.com>
*
* 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; either version 2 of the License, or
* (at your option) any later version.
*
* 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
*
*/
#ifndef _EXYNOS_TMU_DATA_H
#define _EXYNOS_TMU_DATA_H
/* Exynos generic registers */
#define EXYNOS_TMU_REG_TRIMINFO 0x0
#define EXYNOS_TMU_REG_TRIMINFO1 0x4
#define EXYNOS_TMU_REG_CONTROL 0x20
#define EXYNOS_TMU_REG_CONTROL1 0x24
#define EXYNOS_TMU_REG_STATUS 0x28
#define EXYNOS_TMU_REG_CURRENT_TEMP 0x40
#define EXYNOS_TMU_REG_INTEN 0x70
#define EXYNOS_TMU_REG_INTSTAT 0x74
#define EXYNOS_TMU_REG_INTCLEAR 0x78
#define EXYNOS_TMU_REF_VOLTAGE_OTP_SHIFT 18
#define EXYNOS_TMU_REF_VOLTAGE_OTP_MASK 0x1F
#define EXYNOS_TMU_REF_VOLTAGE_OTP_MASK_3BIT 0x7
#define EXYNOS_TMU_REF_VOLTAGE_SHIFT 24
#define EXYNOS_TMU_REF_VOLTAGE_MASK 0x1f
#define EXYNOS_TMU_BUF_SLOPE_SEL_OTP_MASK 0xf
#define EXYNOS_TMU_BUF_SLOPE_SEL_OTP_MASK_3BIT 0x7
#define EXYNOS_TMU_BUF_SLOPE_SEL_OTP_SHIFT 18
#define EXYNOS_TMU_BUF_SLOPE_SEL_MASK 0xf
#define EXYNOS_TMU_BUF_SLOPE_SEL_SHIFT 8
#define EXYNOS_TMU_CORE_EN_SHIFT 0
#define EXYNOS_TMU_MUX_ADDR_SHIFT 20
#define EXYNOS_TMU_MUX_ADDR_MASK 0x5
#define EXYNOS_TMU_PTAT_CON_SHIFT 20
#define EXYNOS_TMU_PTAT_CON_MASK 0x7
#define EXYNOS_TMU_BUF_CONT_SHIFT 12
#define EXYNOS_TMU_BUF_CONT_MASK 0xf
/* Exynos3250 specific registers */
#define EXYNOS_TMU_TRIMINFO_CON1 0x10
/* Exynos4210 specific registers */
#define EXYNOS4210_TMU_REG_THRESHOLD_TEMP 0x44
#define EXYNOS4210_TMU_REG_TRIG_LEVEL0 0x50
/* Exynos5250, Exynos4412, Exynos3250 specific registers */
#define EXYNOS_TMU_TRIMINFO_CON2 0x14
#define EXYNOS_THD_TEMP_RISE 0x50
#define EXYNOS_THD_TEMP_FALL 0x54
#define EXYNOS_EMUL_CON 0x80
#define EXYNOS_TRIMINFO_25_SHIFT 0
#define EXYNOS_TRIMINFO_85_SHIFT 8
#define EXYNOS_TMU_TRIP_MODE_SHIFT 13
#define EXYNOS_TMU_TRIP_MODE_MASK 0x7
#define EXYNOS_TMU_THERM_TRIP_EN_SHIFT 12
#define EXYNOS_TMU_INTEN_RISE0_SHIFT 0
#define EXYNOS_TMU_INTEN_RISE1_SHIFT 4
#define EXYNOS_TMU_INTEN_RISE2_SHIFT 8
#define EXYNOS_TMU_INTEN_RISE3_SHIFT 12
#define EXYNOS_TMU_INTEN_FALL0_SHIFT 16
#define EXYNOS_EMUL_TIME 0x1
#define EXYNOS_EMUL_TIME_MASK 0xffff
#define EXYNOS_EMUL_TIME_SHIFT 16
#define EXYNOS_EMUL_DATA_SHIFT 8
#define EXYNOS_EMUL_DATA_MASK 0xFF
#define EXYNOS_EMUL_ENABLE 0x1
#define EXYNOS_MAX_TRIGGER_PER_REG 8
/* Exynos5260 specific */
#define EXYNOS5260_TMU_REG_INTEN 0xC0
#define EXYNOS5260_TMU_REG_INTSTAT 0xC4
#define EXYNOS5260_TMU_REG_INTCLEAR 0xC8
#define EXYNOS5260_EMUL_CON 0x100
/* Exynos4412 specific */
#define EXYNOS4412_MUX_ADDR_VALUE 6
#define EXYNOS4412_MUX_ADDR_SHIFT 20
/*exynos5440 specific registers*/
#define EXYNOS5440_TMU_S0_7_TRIM 0x000
#define EXYNOS5440_TMU_S0_7_CTRL 0x020
#define EXYNOS5440_TMU_S0_7_DEBUG 0x040
#define EXYNOS5440_TMU_S0_7_STATUS 0x060
#define EXYNOS5440_TMU_S0_7_TEMP 0x0f0
#define EXYNOS5440_TMU_S0_7_TH0 0x110
#define EXYNOS5440_TMU_S0_7_TH1 0x130
#define EXYNOS5440_TMU_S0_7_TH2 0x150
#define EXYNOS5440_TMU_S0_7_IRQEN 0x210
#define EXYNOS5440_TMU_S0_7_IRQ 0x230
/* exynos5440 common registers */
#define EXYNOS5440_TMU_IRQ_STATUS 0x000
#define EXYNOS5440_TMU_PMIN 0x004
#define EXYNOS5440_TMU_INTEN_RISE0_SHIFT 0
#define EXYNOS5440_TMU_INTEN_RISE1_SHIFT 1
#define EXYNOS5440_TMU_INTEN_RISE2_SHIFT 2
#define EXYNOS5440_TMU_INTEN_RISE3_SHIFT 3
#define EXYNOS5440_TMU_INTEN_FALL0_SHIFT 4
#define EXYNOS5440_TMU_TH_RISE4_SHIFT 24
#define EXYNOS5440_EFUSE_SWAP_OFFSET 8
#if defined(CONFIG_SOC_EXYNOS3250)
extern struct exynos_tmu_init_data const exynos3250_default_tmu_data;
#define EXYNOS3250_TMU_DRV_DATA (&exynos3250_default_tmu_data)
#else
#define EXYNOS3250_TMU_DRV_DATA (NULL)
#endif
/*exynos7580 specific registers*/
#define EXYNOS7580_TMU_RISE3_0 0x50
#define EXYNOS7580_TMU_RISE7_4 0x54
#define EXYNOS7580_TMU_TH_HW_TRIP_SHIFT 24
#define EXYNOS7580_TMU_FALL3_0 0x60
#define EXYNOS7580_TMU_FALL7_4 0x64
#define EXYNOS7580_TMU_REG_INTEN 0xC0
#define EXYNOS7580_TMU_REG_INTCLEAR 0xC8
#define EXYNOS7580_EMUL_CON 0x110
#define EXYNOS7580_TMU_LPI0_MODE_EN_SHIFT 10
#define EXYNOS7580_TMU_VALID_P0 15
#define EXYNOS7580_TMU_INTEN_RISE0_SHIFT 0
#define EXYNOS7580_TMU_INTEN_RISE1_SHIFT 1
#define EXYNOS7580_TMU_INTEN_RISE2_SHIFT 2
#define EXYNOS7580_TMU_INTEN_RISE3_SHIFT 3
#define EXYNOS7580_TMU_INTEN_RISE4_SHIFT 4
#define EXYNOS7580_TMU_INTEN_RISE5_SHIFT 5
#define EXYNOS7580_TMU_INTEN_RISE6_SHIFT 6
#define EXYNOS7580_TMU_INTEN_RISE7_SHIFT 7
#define EXYNOS7580_TMU_INTEN_FALL0_SHIFT 16
#define EXYNOS7580_TMU_INTEN_FALL1_SHIFT 17
#define EXYNOS7580_TMU_INTEN_FALL2_SHIFT 18
#define EXYNOS7580_TMU_INTEN_FALL3_SHIFT 19
#define EXYNOS7580_TMU_INTEN_FALL4_SHIFT 20
#define EXYNOS7580_TMU_INTEN_FALL5_SHIFT 21
#define EXYNOS7580_TMU_INTEN_FALL6_SHIFT 22
#define EXYNOS7580_TMU_INTEN_FALL7_SHIFT 23
#define EXYNOS7580_TMU_RISE_INT_MASK 0xff
#define EXYNOS7580_TMU_RISE_INT_SHIFT 0
#define EXYNOS7580_TMU_FALL_INT_MASK 0xff
#define EXYNOS7580_TMU_FALL_INT_SHIFT 16
/* Define sensor type */
#define EXYNOS_TEM1456X 0x1
#define EXYNOS_TEM1455X 0x2
/*TEM1456X specific registers*/
#define EXYNOS_TEM1456X_TMU_TEMP_MASK 0x1ff
#define EXYNOS_TEM1456X_TRIMINFO_25_SHIFT 9
#define EXYNOS_TEM1456X_TRIMINFO_85_SHIFT 9
#define EXYNOS_TEM1456X_CALIB_SEL_SHIFT 23
#define EXYNOS_TEM1456X_CALIB_SEL_MASK 1
#define EXYNOS_TEM1456X_TMU_LPI0_MODE_EN_SHIFT 10
#define EXYNOS_TEM1456X_TMU_RISE6_7 0x50
#define EXYNOS_TEM1456X_TMU_RISE4_5 0x54
#define EXYNOS_TEM1456X_TMU_RISE2_3 0x58
#define EXYNOS_TEM1456X_TMU_RISE0_1 0x5C
#define EXYNOS_TEM1456X_TMU_TH_HW_TRIP_SHIFT 24
#define EXYNOS_TEM1456X_TMU_FALL6_7 0x60
#define EXYNOS_TEM1456X_TMU_FALL4_5 0x64
#define EXYNOS_TEM1456X_TMU_FALL2_3 0x68
#define EXYNOS_TEM1456X_TMU_FALL0_1 0x6C
#define EXYNOS_TEM1456X_TMU_REG_INTEN 0x110
#define EXYNOS_TEM1456X_TMU_REG_INTCLEAR 0x118
#define EXYNOS_TEM1456X_EMUL_CON 0x160
#define EXYNOS_TEM1456X_EMUL_DATA_SHIFT 7
#define EXYNOS_TEM1456X_EMUL_DATA_MASK 0x1FF
#define EXYNOS_TEM1456X_TMU_LPI0_MODE_EN_SHIFT 10
#define EXYNOS_TEM1456X_TMU_VALID_P0 15
#define EXYNOS_TEM1456X_TMU_INTEN_RISE0_SHIFT 0
#define EXYNOS_TEM1456X_TMU_INTEN_RISE1_SHIFT 1
#define EXYNOS_TEM1456X_TMU_INTEN_RISE2_SHIFT 2
#define EXYNOS_TEM1456X_TMU_INTEN_RISE3_SHIFT 3
#define EXYNOS_TEM1456X_TMU_INTEN_RISE4_SHIFT 4
#define EXYNOS_TEM1456X_TMU_INTEN_RISE5_SHIFT 5
#define EXYNOS_TEM1456X_TMU_INTEN_RISE6_SHIFT 6
#define EXYNOS_TEM1456X_TMU_INTEN_RISE7_SHIFT 7
#define EXYNOS_TEM1456X_TMU_INTEN_FALL0_SHIFT 16
#define EXYNOS_TEM1456X_TMU_INTEN_FALL1_SHIFT 17
#define EXYNOS_TEM1456X_TMU_INTEN_FALL2_SHIFT 18
#define EXYNOS_TEM1456X_TMU_INTEN_FALL3_SHIFT 19
#define EXYNOS_TEM1456X_TMU_INTEN_FALL4_SHIFT 20
#define EXYNOS_TEM1456X_TMU_INTEN_FALL5_SHIFT 21
#define EXYNOS_TEM1456X_TMU_INTEN_FALL6_SHIFT 22
#define EXYNOS_TEM1456X_TMU_INTEN_FALL7_SHIFT 23
#define EXYNOS_TEM1456X_TMU_RISE_INT_MASK 0xff
#define EXYNOS_TEM1456X_TMU_RISE_INT_SHIFT 0
#define EXYNOS_TEM1456X_TMU_FALL_INT_MASK 0xff
#define EXYNOS_TEM1456X_TMU_FALL_INT_SHIFT 16
#define EXYNOS_TEM1456X_VALID_MASK 0x1
#define EXYNOS_TEM1456X_VALID_P0_SHIFT 12
#define EXYNOS_TEM1456X_VALID_P1_SHIFT 13
#define EXYNOS_TEM1456X_VALID_P2_SHIFT 14
#define EXYNOS_TEM1456X_VALID_P3_SHIFT 15
#define EXYNOS_TEM1456X_VALID_P4_SHIFT 16
/*TEM1455X specific registers*/
#define EXYNOS_TEM1455X_MUX_ADDR_VALUE 0x0
#define EXYNOS_TEM1455X_TMU_TEMP_MASK 0x1ff
#define EXYNOS_TEM1455X_TRIMINFO_25_SHIFT 0
#define EXYNOS_TEM1455X_TRIMINFO_85_SHIFT 9
#define EXYNOS_TEM1455X_CALIB_SEL_SHIFT 23
#define EXYNOS_TEM1455X_CALIB_SEL_MASK 1
#define EXYNOS_TEM1455X_TMU_LPI0_MODE_EN_SHIFT 10
#define EXYNOS_TEM1455X_TMU_RISE6_7 0x50
#define EXYNOS_TEM1455X_TMU_RISE4_5 0x54
#define EXYNOS_TEM1455X_TMU_RISE2_3 0x58
#define EXYNOS_TEM1455X_TMU_RISE0_1 0x5C
#define EXYNOS_TEM1455X_TMU_FALL6_7 0x60
#define EXYNOS_TEM1455X_TMU_FALL4_5 0x64
#define EXYNOS_TEM1455X_TMU_FALL2_3 0x68
#define EXYNOS_TEM1455X_TMU_FALL0_1 0x6C
#define EXYNOS_TEM1455X_TMU_REG_INTEN 0x110
#define EXYNOS_TEM1455X_TMU_REG_INTPEND 0x118
#define EXYNOS_TEM1455X_TMU_REG_INTCLEAR 0x118
#define EXYNOS_TEM1455X_EMUL_CON 0x160
#define EXYNOS_TEM1455X_EMUL_DATA_SHIFT 7
#define EXYNOS_TEM1455X_EMUL_DATA_MASK 0x1FF
#define EXYNOS_TEM1455X_TMU_LPI0_MODE_EN_SHIFT 10
#define EXYNOS_TEM1455X_TMU_VALID_P0 15
#define EXYNOS_TEM1455X_TMU_INTEN_RISE0_SHIFT 0
#define EXYNOS_TEM1455X_TMU_INTEN_RISE1_SHIFT 1
#define EXYNOS_TEM1455X_TMU_INTEN_RISE2_SHIFT 2
#define EXYNOS_TEM1455X_TMU_INTEN_RISE3_SHIFT 3
#define EXYNOS_TEM1455X_TMU_INTEN_RISE4_SHIFT 4
#define EXYNOS_TEM1455X_TMU_INTEN_RISE5_SHIFT 5
#define EXYNOS_TEM1455X_TMU_INTEN_RISE6_SHIFT 6
#define EXYNOS_TEM1455X_TMU_INTEN_RISE7_SHIFT 7
#define EXYNOS_TEM1455X_TMU_INTEN_FALL0_SHIFT 16
#define EXYNOS_TEM1455X_TMU_INTEN_FALL1_SHIFT 17
#define EXYNOS_TEM1455X_TMU_INTEN_FALL2_SHIFT 18
#define EXYNOS_TEM1455X_TMU_INTEN_FALL3_SHIFT 19
#define EXYNOS_TEM1455X_TMU_INTEN_FALL4_SHIFT 20
#define EXYNOS_TEM1455X_TMU_INTEN_FALL5_SHIFT 21
#define EXYNOS_TEM1455X_TMU_INTEN_FALL6_SHIFT 22
#define EXYNOS_TEM1455X_TMU_INTEN_FALL7_SHIFT 23
#define EXYNOS_TEM1455X_TMU_RISE_INT_MASK 0xff
#define EXYNOS_TEM1455X_TMU_RISE_INT_SHIFT 0
#define EXYNOS_TEM1455X_TMU_FALL_INT_MASK 0xff
#define EXYNOS_TEM1455X_TMU_FALL_INT_SHIFT 16
#define EXYNOS_TEM1455X_VALID_MASK 0x1
#define EXYNOS_TEM1455X_VALID_P0_SHIFT 12
#define EXYNOS_TEM1455X_VALID_P1_SHIFT 13
#define EXYNOS_TEM1455X_VALID_P2_SHIFT 14
#define EXYNOS_TEM1455X_VALID_P3_SHIFT 15
#define EXYNOS_TEM1455X_VALID_P4_SHIFT 16
/* Define Exynos Global constant value */
#define EXYNOS_MAX_TEMP 125
#define EXYNOS_MIN_TEMP 10
#define EXYNOS_COLD_TEMP 15
#if defined(CONFIG_CPU_EXYNOS4210)
extern struct exynos_tmu_init_data const exynos4210_default_tmu_data;
#define EXYNOS4210_TMU_DRV_DATA (&exynos4210_default_tmu_data)
#else
#define EXYNOS4210_TMU_DRV_DATA (NULL)
#endif
#if defined(CONFIG_SOC_EXYNOS4412)
extern struct exynos_tmu_init_data const exynos4412_default_tmu_data;
#define EXYNOS4412_TMU_DRV_DATA (&exynos4412_default_tmu_data)
#else
#define EXYNOS4412_TMU_DRV_DATA (NULL)
#endif
#if defined(CONFIG_SOC_EXYNOS5250)
extern struct exynos_tmu_init_data const exynos5250_default_tmu_data;
#define EXYNOS5250_TMU_DRV_DATA (&exynos5250_default_tmu_data)
#else
#define EXYNOS5250_TMU_DRV_DATA (NULL)
#endif
#if defined(CONFIG_SOC_EXYNOS5260)
extern struct exynos_tmu_init_data const exynos5260_default_tmu_data;
#define EXYNOS5260_TMU_DRV_DATA (&exynos5260_default_tmu_data)
#else
#define EXYNOS5260_TMU_DRV_DATA (NULL)
#endif
#if defined(CONFIG_SOC_EXYNOS5420)
extern struct exynos_tmu_init_data const exynos5420_default_tmu_data;
#define EXYNOS5420_TMU_DRV_DATA (&exynos5420_default_tmu_data)
#else
#define EXYNOS5420_TMU_DRV_DATA (NULL)
#endif
#if defined(CONFIG_SOC_EXYNOS5440)
extern struct exynos_tmu_init_data const exynos5440_default_tmu_data;
#define EXYNOS5440_TMU_DRV_DATA (&exynos5440_default_tmu_data)
#else
#define EXYNOS5440_TMU_DRV_DATA (NULL)
#endif
#if defined(CONFIG_SOC_EXYNOS7580)
extern struct exynos_tmu_init_data const exynos7580_default_tmu_data;
#define EXYNOS7580_TMU_DRV_DATA (&exynos7580_default_tmu_data)
#else
#define EXYNOS7580_TMU_DRV_DATA (NULL)
#endif
#if defined(CONFIG_SOC_EXYNOS8890)
extern struct exynos_tmu_init_data exynos8890_default_tmu_data;
#define EXYNOS8890_TMU_DRV_DATA (&exynos8890_default_tmu_data)
#else
#define EXYNOS8890_TMU_DRV_DATA (NULL)
#endif
#if defined(CONFIG_SOC_EXYNOS7870)
extern struct exynos_tmu_init_data exynos7870_default_tmu_data;
#define EXYNOS7870_TMU_DRV_DATA (&exynos7870_default_tmu_data)
#else
#define EXYNOS7870_TMU_DRV_DATA (NULL)
#endif
#if defined(CONFIG_SOC_EXYNOS7570)
extern struct exynos_tmu_init_data exynos7570_default_tmu_data;
#define EXYNOS7570_TMU_DRV_DATA (&exynos7570_default_tmu_data)
#else
#define EXYNOS7570_TMU_DRV_DATA (NULL)
#endif
#endif /*_EXYNOS_TMU_DATA_H*/

View file

@ -0,0 +1,460 @@
/*
* 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 int 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 (suspended || 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);

View file

@ -0,0 +1,459 @@
/*
* linux/drivers/thermal/isp_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/err.h>
#include <linux/slab.h>
#include <linux/cpu.h>
#include <linux/isp_cooling.h>
#include <soc/samsung/tmu.h>
#include "exynos_tmu_data.h"
/**
* struct isp_cooling_device - data for cooling device with isp
* @id: unique integer value corresponding to each isp_cooling_device
* registered.
* @cool_dev: thermal_cooling_device pointer to keep track of the
* registered cooling device.
* @isp_state: integer value representing the current state of isp
* cooling devices.
* @isp_val: integer value representing the absolute value of the clipped
* fps.
* @allowed_isp: all the isp involved for this isp_cooling_device.
*
* This structure is required for keeping information of each
* isp_cooling_device registered. In order to prevent corruption of this a
* mutex lock cooling_isp_lock is used.
*/
struct isp_cooling_device {
int id;
struct thermal_cooling_device *cool_dev;
unsigned int isp_state;
unsigned int isp_val;
};
static DEFINE_IDR(isp_idr);
static DEFINE_MUTEX(cooling_isp_lock);
static BLOCKING_NOTIFIER_HEAD(isp_notifier);
static unsigned int isp_dev_count;
extern struct isp_fps_table isp_fps_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_isp_lock);
ret = idr_alloc(idr, NULL, 0, 0, GFP_KERNEL);
mutex_unlock(&cooling_isp_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_isp_lock);
idr_remove(idr, id);
mutex_unlock(&cooling_isp_lock);
}
/* Below code defines functions to be used for isp as cooling device */
enum isp_cooling_property {
GET_LEVEL,
GET_FREQ,
GET_MAXL,
};
/**
* get_property - fetch a property of interest for a give isp.
* @isp: isp for which the property is required
* @input: query parameter
* @output: query return
* @property: type of query (fps, level, max level)
*
* This is the common function to
* 1. get maximum isp cooling states
* 2. translate fps to cooling state
* 3. translate cooling state to fps
* 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 fps.
*
* Return: 0 on success, -EINVAL when invalid parameters are passed.
*/
static int get_property(unsigned int isp, unsigned long input,
unsigned int *output,
enum isp_cooling_property property)
{
int i;
unsigned long max_level = 0, level = 0;
unsigned int fps = ISP_FPS_ENTRY_INVALID;
int descend = -1;
struct isp_fps_table *pos, *table =
isp_fps_table;
if (!output)
return -EINVAL;
if (!table)
return -EINVAL;
isp_fps_for_each_valid_entry(pos, table) {
/* ignore duplicate entry */
if (fps == pos->fps)
continue;
/* get the fps order */
if (fps != ISP_FPS_ENTRY_INVALID && descend == -1)
descend = fps > pos->fps;
fps = pos->fps;
max_level++;
}
/* No valid cpu fps 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;
}
i = 0;
isp_fps_for_each_valid_entry(pos, table) {
/* ignore duplicate entry */
if (fps == pos->fps)
continue;
/* now we have a valid fps entry */
fps = pos->fps;
if (property == GET_LEVEL && (unsigned int)input == fps) {
/* get level by fps */
*output = descend ? i : (max_level - i);
return 0;
}
if (property == GET_FREQ && level == i) {
/* get fps by level */
*output = fps;
return 0;
}
i++;
}
return -EINVAL;
}
/**
* isp_cooling_get_level - for a give isp, return the cooling level.
* @isp: isp for which the level is required
* @fps: the fps of interest
*
* This function will match the cooling level corresponding to the
* requested @fps and return it.
*
* Return: The matched cooling level on success or THERMAL_CSTATE_INVALID
* otherwise.
*/
unsigned long isp_cooling_get_fps(unsigned int isp, unsigned int fps)
{
unsigned int val;
if (get_property(isp, (unsigned long)fps, &val, GET_LEVEL))
return THERMAL_CSTATE_INVALID;
return (unsigned long)val;
}
EXPORT_SYMBOL_GPL(isp_cooling_get_fps);
/**
* isp_apply_cooling - function to apply fps clipping.
* @isp_device: isp_cooling_device pointer containing fps
* clipping data.
* @cooling_state: value of the cooling state.
*
* Function used to make sure the isp layer is aware of current thermal
* limits. The limits are applied by updating the isp policy.
*
* Return: 0 on success, an error code otherwise (-EINVAL in case wrong
* cooling state).
*/
static int isp_apply_cooling(struct isp_cooling_device *isp_device,
unsigned long cooling_state)
{
/* Check if the old cooling action is same as new cooling action */
if (isp_device->isp_state == cooling_state)
return 0;
isp_device->isp_state = cooling_state;
blocking_notifier_call_chain(&isp_notifier, cooling_state, &cooling_state);
return 0;
}
/* isp cooling device callback functions are defined below */
/**
* isp_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 isp
* max cooling state.
*
* Return: 0 on success, an error code otherwise.
*/
static int isp_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;
}
/**
* isp_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 isp
* current cooling state.
*
* Return: 0 on success, an error code otherwise.
*/
static int isp_get_cur_state(struct thermal_cooling_device *cdev,
unsigned long *state)
{
struct isp_cooling_device *isp_device = cdev->devdata;
*state = isp_device->isp_state;
return 0;
}
/**
* isp_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 isp
* current cooling state.
*
* Return: 0 on success, an error code otherwise.
*/
static int isp_set_cur_state(struct thermal_cooling_device *cdev,
unsigned long state)
{
struct isp_cooling_device *isp_device = cdev->devdata;
return isp_apply_cooling(isp_device, state);
}
static enum tmu_noti_state_t isp_tstate = ISP_COLD;
int isp_set_cur_temp(bool suspended, unsigned long temp)
{
enum tmu_noti_state_t tstate;
if (suspended || temp < EXYNOS_COLD_TEMP)
tstate = ISP_COLD;
else
tstate = ISP_NORMAL;
if (isp_tstate == tstate)
return 0;
isp_tstate = tstate;
blocking_notifier_call_chain(&isp_notifier, tstate, &tstate);
return 0;
}
/* Bind isp callbacks to thermal cooling device ops */
static struct thermal_cooling_device_ops const isp_cooling_ops = {
.get_max_state = isp_get_max_state,
.get_cur_state = isp_get_cur_state,
.set_cur_state = isp_set_cur_state,
};
int exynos_tmu_isp_add_notifier(struct notifier_block *n)
{
return blocking_notifier_chain_register(&isp_notifier, n);
}
/**
* __isp_cooling_register - helper function to create isp cooling device
* @np: a valid struct device_node to the cooling device device tree node
* @clip_isp: ispmask of isp where the fps constraints will happen.
*
* This interface function registers the isp cooling device with the name
* "thermal-isp-%x". This api can support multiple instances of isp
* 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 *
__isp_cooling_register(struct device_node *np,
const struct cpumask *clip_isp)
{
struct thermal_cooling_device *cool_dev;
struct isp_cooling_device *isp_dev = NULL;
char dev_name[THERMAL_NAME_LENGTH];
int ret = 0;
isp_dev = kzalloc(sizeof(struct isp_cooling_device),
GFP_KERNEL);
if (!isp_dev)
return ERR_PTR(-ENOMEM);
ret = get_idr(&isp_idr, &isp_dev->id);
if (ret) {
kfree(isp_dev);
return ERR_PTR(-EINVAL);
}
snprintf(dev_name, sizeof(dev_name), "thermal-isp-%d",
isp_dev->id);
cool_dev = thermal_of_cooling_device_register(np, dev_name, isp_dev,
&isp_cooling_ops);
if (IS_ERR(cool_dev)) {
release_idr(&isp_idr, isp_dev->id);
kfree(isp_dev);
return cool_dev;
}
isp_dev->cool_dev = cool_dev;
isp_dev->isp_state = 0;
mutex_lock(&cooling_isp_lock);
isp_dev_count++;
mutex_unlock(&cooling_isp_lock);
return cool_dev;
}
/**
* isp_cooling_register - function to create isp cooling device.
* @clip_isp: cpumask of gpus where the fps constraints will happen.
*
* This interface function registers the isp cooling device with the name
* "thermal-isp-%x". This api can support multiple instances of isp
* cooling devices.
*
* Return: a valid struct thermal_cooling_device pointer on success,
* on failure, it returns a corresponding ERR_PTR().
*/
struct thermal_cooling_device *
isp_cooling_register(const struct cpumask *clip_isp)
{
return __isp_cooling_register(NULL, clip_isp);
}
EXPORT_SYMBOL_GPL(isp_cooling_register);
/**
* of_isp_cooling_register - function to create isp cooling device.
* @np: a valid struct device_node to the cooling device device tree node
* @clip_isp: cpumask of gpus where the fps constraints will happen.
*
* This interface function registers the isp cooling device with the name
* "thermal-isp-%x". This api can support multiple instances of isp
* cooling devices. Using this API, the isp 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_isp_cooling_register(struct device_node *np,
const struct cpumask *clip_isp)
{
if (!np)
return ERR_PTR(-EINVAL);
return __isp_cooling_register(np, clip_isp);
}
EXPORT_SYMBOL_GPL(of_isp_cooling_register);
/**
* isp_cooling_unregister - function to remove isp cooling device.
* @cdev: thermal cooling device pointer.
*
* This interface function unregisters the "thermal-isp-%x" cooling device.
*/
void isp_cooling_unregister(struct thermal_cooling_device *cdev)
{
struct isp_cooling_device *isp_dev;
if (!cdev)
return;
isp_dev = cdev->devdata;
mutex_lock(&cooling_isp_lock);
isp_dev_count--;
mutex_unlock(&cooling_isp_lock);
thermal_cooling_device_unregister(isp_dev->cool_dev);
release_idr(&isp_idr, isp_dev->id);
kfree(isp_dev);
}
EXPORT_SYMBOL_GPL(isp_cooling_unregister);