1
0
Fork 0

Merge branches 'thermal-core', 'thermal-soc' and 'thermal-int340x' of .git into next

wifi-calibration
Zhang Rui 2014-12-24 10:38:30 +08:00
commit 7429b1e0d0
19 changed files with 496 additions and 276 deletions

View File

@ -4,6 +4,8 @@
* Copyright (C) 2012 Samsung Electronics Co., Ltd(http://www.samsung.com) * Copyright (C) 2012 Samsung Electronics Co., Ltd(http://www.samsung.com)
* Copyright (C) 2012 Amit Daniel <amit.kachhap@linaro.org> * Copyright (C) 2012 Amit Daniel <amit.kachhap@linaro.org>
* *
* Copyright (C) 2014 Viresh Kumar <viresh.kumar@linaro.org>
*
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
* This program is free software; you can redistribute it and/or modify * 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 * it under the terms of the GNU General Public License as published by
@ -28,6 +30,20 @@
#include <linux/cpu.h> #include <linux/cpu.h>
#include <linux/cpu_cooling.h> #include <linux/cpu_cooling.h>
/*
* Cooling state <-> CPUFreq frequency
*
* Cooling states are translated to frequencies throughout this driver and this
* is the relation between them.
*
* Highest cooling state corresponds to lowest possible frequency.
*
* i.e.
* level 0 --> 1st Max Freq
* level 1 --> 2nd Max Freq
* ...
*/
/** /**
* struct cpufreq_cooling_device - data for cooling device with cpufreq * struct cpufreq_cooling_device - data for cooling device with cpufreq
* @id: unique integer value corresponding to each cpufreq_cooling_device * @id: unique integer value corresponding to each cpufreq_cooling_device
@ -38,26 +54,27 @@
* cooling devices. * cooling devices.
* @cpufreq_val: integer value representing the absolute value of the clipped * @cpufreq_val: integer value representing the absolute value of the clipped
* frequency. * frequency.
* @max_level: maximum cooling level. One less than total number of valid
* cpufreq frequencies.
* @allowed_cpus: all the cpus involved for this cpufreq_cooling_device. * @allowed_cpus: all the cpus involved for this cpufreq_cooling_device.
* @node: list_head to link all cpufreq_cooling_device together. * @node: list_head to link all cpufreq_cooling_device together.
* *
* This structure is required for keeping information of each * This structure is required for keeping information of each registered
* cpufreq_cooling_device registered. In order to prevent corruption of this a * cpufreq_cooling_device.
* mutex lock cooling_cpufreq_lock is used.
*/ */
struct cpufreq_cooling_device { struct cpufreq_cooling_device {
int id; int id;
struct thermal_cooling_device *cool_dev; struct thermal_cooling_device *cool_dev;
unsigned int cpufreq_state; unsigned int cpufreq_state;
unsigned int cpufreq_val; unsigned int cpufreq_val;
unsigned int max_level;
unsigned int *freq_table; /* In descending order */
struct cpumask allowed_cpus; struct cpumask allowed_cpus;
struct list_head node; struct list_head node;
}; };
static DEFINE_IDR(cpufreq_idr); static DEFINE_IDR(cpufreq_idr);
static DEFINE_MUTEX(cooling_cpufreq_lock); static DEFINE_MUTEX(cooling_cpufreq_lock);
static unsigned int cpufreq_dev_count;
static LIST_HEAD(cpufreq_dev_list); static LIST_HEAD(cpufreq_dev_list);
/** /**
@ -99,120 +116,30 @@ static void release_idr(struct idr *idr, int id)
/* Below code defines functions to be used for cpufreq as cooling device */ /* Below code defines functions to be used for cpufreq as cooling device */
/** /**
* is_cpufreq_valid - function to check frequency transitioning capability. * get_level: Find the level for a particular frequency
* @cpu: cpu for which check is needed. * @cpufreq_dev: cpufreq_dev for which the property is required
* @freq: Frequency
* *
* This function will check the current state of the system if * Return: level on success, THERMAL_CSTATE_INVALID on error.
* 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) static unsigned long get_level(struct cpufreq_cooling_device *cpufreq_dev,
unsigned int freq)
{ {
struct cpufreq_policy policy; unsigned long level;
return !cpufreq_get_policy(&policy, cpu); for (level = 0; level <= cpufreq_dev->max_level; level++) {
} if (freq == cpufreq_dev->freq_table[level])
return level;
enum cpufreq_cooling_property { if (freq > cpufreq_dev->freq_table[level])
GET_LEVEL, break;
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 */ return THERMAL_CSTATE_INVALID;
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. * cpufreq_cooling_get_level - for a given cpu, return the cooling level.
* @cpu: cpu for which the level is required * @cpu: cpu for which the level is required
* @freq: the frequency of interest * @freq: the frequency of interest
* *
@ -224,78 +151,22 @@ static int get_property(unsigned int cpu, unsigned long input,
*/ */
unsigned long cpufreq_cooling_get_level(unsigned int cpu, unsigned int freq) unsigned long cpufreq_cooling_get_level(unsigned int cpu, unsigned int freq)
{ {
unsigned int val; struct cpufreq_cooling_device *cpufreq_dev;
if (get_property(cpu, (unsigned long)freq, &val, GET_LEVEL)) mutex_lock(&cooling_cpufreq_lock);
return THERMAL_CSTATE_INVALID; list_for_each_entry(cpufreq_dev, &cpufreq_dev_list, node) {
if (cpumask_test_cpu(cpu, &cpufreq_dev->allowed_cpus)) {
mutex_unlock(&cooling_cpufreq_lock);
return get_level(cpufreq_dev, freq);
}
}
mutex_unlock(&cooling_cpufreq_lock);
return (unsigned long)val; pr_err("%s: cpu:%d not part of any cooling device\n", __func__, cpu);
return THERMAL_CSTATE_INVALID;
} }
EXPORT_SYMBOL_GPL(cpufreq_cooling_get_level); 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. * cpufreq_thermal_notifier - notifier callback for cpufreq policy change.
* @nb: struct notifier_block * with callback info. * @nb: struct notifier_block * with callback info.
@ -324,11 +195,6 @@ static int cpufreq_thermal_notifier(struct notifier_block *nb,
&cpufreq_dev->allowed_cpus)) &cpufreq_dev->allowed_cpus))
continue; 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; max_freq = cpufreq_dev->cpufreq_val;
if (policy->max != max_freq) if (policy->max != max_freq)
@ -355,19 +221,9 @@ static int cpufreq_get_max_state(struct thermal_cooling_device *cdev,
unsigned long *state) unsigned long *state)
{ {
struct cpufreq_cooling_device *cpufreq_device = cdev->devdata; 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); *state = cpufreq_device->max_level;
return 0;
ret = get_property(cpu, 0, &count, GET_MAXL);
if (count > 0)
*state = count;
return ret;
} }
/** /**
@ -404,8 +260,24 @@ static int cpufreq_set_cur_state(struct thermal_cooling_device *cdev,
unsigned long state) unsigned long state)
{ {
struct cpufreq_cooling_device *cpufreq_device = cdev->devdata; struct cpufreq_cooling_device *cpufreq_device = cdev->devdata;
unsigned int cpu = cpumask_any(&cpufreq_device->allowed_cpus);
unsigned int clip_freq;
return cpufreq_apply_cooling(cpufreq_device, state); /* Request state should be less than max_level */
if (WARN_ON(state > cpufreq_device->max_level))
return -EINVAL;
/* Check if the old cooling action is same as new cooling action */
if (cpufreq_device->cpufreq_state == state)
return 0;
clip_freq = cpufreq_device->freq_table[state];
cpufreq_device->cpufreq_state = state;
cpufreq_device->cpufreq_val = clip_freq;
cpufreq_update_policy(cpu);
return 0;
} }
/* Bind cpufreq callbacks to thermal cooling device ops */ /* Bind cpufreq callbacks to thermal cooling device ops */
@ -420,10 +292,25 @@ static struct notifier_block thermal_cpufreq_notifier_block = {
.notifier_call = cpufreq_thermal_notifier, .notifier_call = cpufreq_thermal_notifier,
}; };
static unsigned int find_next_max(struct cpufreq_frequency_table *table,
unsigned int prev_max)
{
struct cpufreq_frequency_table *pos;
unsigned int max = 0;
cpufreq_for_each_valid_entry(pos, table) {
if (pos->frequency > max && pos->frequency < prev_max)
max = pos->frequency;
}
return max;
}
/** /**
* __cpufreq_cooling_register - helper function to create cpufreq cooling device * __cpufreq_cooling_register - helper function to create cpufreq cooling device
* @np: a valid struct device_node to the cooling device device tree node * @np: a valid struct device_node to the cooling device device tree node
* @clip_cpus: cpumask of cpus where the frequency constraints will happen. * @clip_cpus: cpumask of cpus where the frequency constraints will happen.
* Normally this should be same as cpufreq policy->related_cpus.
* *
* This interface function registers the cpufreq cooling device with the name * This interface function registers the cpufreq cooling device with the name
* "thermal-cpufreq-%x". This api can support multiple instances of cpufreq * "thermal-cpufreq-%x". This api can support multiple instances of cpufreq
@ -438,37 +325,42 @@ __cpufreq_cooling_register(struct device_node *np,
const struct cpumask *clip_cpus) const struct cpumask *clip_cpus)
{ {
struct thermal_cooling_device *cool_dev; struct thermal_cooling_device *cool_dev;
struct cpufreq_cooling_device *cpufreq_dev = NULL; struct cpufreq_cooling_device *cpufreq_dev;
unsigned int min = 0, max = 0;
char dev_name[THERMAL_NAME_LENGTH]; char dev_name[THERMAL_NAME_LENGTH];
int ret = 0, i; struct cpufreq_frequency_table *pos, *table;
struct cpufreq_policy policy; unsigned int freq, i;
int ret;
/* Verify that all the clip cpus have same freq_min, freq_max limit */ table = cpufreq_frequency_get_table(cpumask_first(clip_cpus));
for_each_cpu(i, clip_cpus) { if (!table) {
/* continue if cpufreq policy not found and not return error */ pr_debug("%s: CPUFreq table not found\n", __func__);
if (!cpufreq_get_policy(&policy, i)) return ERR_PTR(-EPROBE_DEFER);
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); cpufreq_dev = kzalloc(sizeof(*cpufreq_dev), GFP_KERNEL);
if (!cpufreq_dev) if (!cpufreq_dev)
return ERR_PTR(-ENOMEM); return ERR_PTR(-ENOMEM);
/* Find max levels */
cpufreq_for_each_valid_entry(pos, table)
cpufreq_dev->max_level++;
cpufreq_dev->freq_table = kmalloc(sizeof(*cpufreq_dev->freq_table) *
cpufreq_dev->max_level, GFP_KERNEL);
if (!cpufreq_dev->freq_table) {
cool_dev = ERR_PTR(-ENOMEM);
goto free_cdev;
}
/* max_level is an index, not a counter */
cpufreq_dev->max_level--;
cpumask_copy(&cpufreq_dev->allowed_cpus, clip_cpus); cpumask_copy(&cpufreq_dev->allowed_cpus, clip_cpus);
ret = get_idr(&cpufreq_idr, &cpufreq_dev->id); ret = get_idr(&cpufreq_idr, &cpufreq_dev->id);
if (ret) { if (ret) {
kfree(cpufreq_dev); cool_dev = ERR_PTR(ret);
return ERR_PTR(-EINVAL); goto free_table;
} }
snprintf(dev_name, sizeof(dev_name), "thermal-cpufreq-%d", snprintf(dev_name, sizeof(dev_name), "thermal-cpufreq-%d",
@ -476,24 +368,43 @@ __cpufreq_cooling_register(struct device_node *np,
cool_dev = thermal_of_cooling_device_register(np, dev_name, cpufreq_dev, cool_dev = thermal_of_cooling_device_register(np, dev_name, cpufreq_dev,
&cpufreq_cooling_ops); &cpufreq_cooling_ops);
if (IS_ERR(cool_dev)) { if (IS_ERR(cool_dev))
release_idr(&cpufreq_idr, cpufreq_dev->id); goto remove_idr;
kfree(cpufreq_dev);
return cool_dev; /* Fill freq-table in descending order of frequencies */
for (i = 0, freq = -1; i <= cpufreq_dev->max_level; i++) {
freq = find_next_max(table, freq);
cpufreq_dev->freq_table[i] = freq;
/* Warn for duplicate entries */
if (!freq)
pr_warn("%s: table has duplicate entries\n", __func__);
else
pr_debug("%s: freq:%u KHz\n", __func__, freq);
} }
cpufreq_dev->cpufreq_val = cpufreq_dev->freq_table[0];
cpufreq_dev->cool_dev = cool_dev; cpufreq_dev->cool_dev = cool_dev;
cpufreq_dev->cpufreq_state = 0;
mutex_lock(&cooling_cpufreq_lock); mutex_lock(&cooling_cpufreq_lock);
/* Register the notifier for first cpufreq cooling device */ /* Register the notifier for first cpufreq cooling device */
if (cpufreq_dev_count == 0) if (list_empty(&cpufreq_dev_list))
cpufreq_register_notifier(&thermal_cpufreq_notifier_block, cpufreq_register_notifier(&thermal_cpufreq_notifier_block,
CPUFREQ_POLICY_NOTIFIER); CPUFREQ_POLICY_NOTIFIER);
cpufreq_dev_count++;
list_add(&cpufreq_dev->node, &cpufreq_dev_list); list_add(&cpufreq_dev->node, &cpufreq_dev_list);
mutex_unlock(&cooling_cpufreq_lock); mutex_unlock(&cooling_cpufreq_lock);
return cool_dev;
remove_idr:
release_idr(&cpufreq_idr, cpufreq_dev->id);
free_table:
kfree(cpufreq_dev->freq_table);
free_cdev:
kfree(cpufreq_dev);
return cool_dev; return cool_dev;
} }
@ -555,16 +466,16 @@ void cpufreq_cooling_unregister(struct thermal_cooling_device *cdev)
cpufreq_dev = cdev->devdata; cpufreq_dev = cdev->devdata;
mutex_lock(&cooling_cpufreq_lock); mutex_lock(&cooling_cpufreq_lock);
list_del(&cpufreq_dev->node); list_del(&cpufreq_dev->node);
cpufreq_dev_count--;
/* Unregister the notifier for the last cpufreq cooling device */ /* Unregister the notifier for the last cpufreq cooling device */
if (cpufreq_dev_count == 0) if (list_empty(&cpufreq_dev_list))
cpufreq_unregister_notifier(&thermal_cpufreq_notifier_block, cpufreq_unregister_notifier(&thermal_cpufreq_notifier_block,
CPUFREQ_POLICY_NOTIFIER); CPUFREQ_POLICY_NOTIFIER);
mutex_unlock(&cooling_cpufreq_lock); mutex_unlock(&cooling_cpufreq_lock);
thermal_cooling_device_unregister(cpufreq_dev->cool_dev); thermal_cooling_device_unregister(cpufreq_dev->cool_dev);
release_idr(&cpufreq_idr, cpufreq_dev->id); release_idr(&cpufreq_idr, cpufreq_dev->id);
kfree(cpufreq_dev->freq_table);
kfree(cpufreq_dev); kfree(cpufreq_dev);
} }
EXPORT_SYMBOL_GPL(cpufreq_cooling_unregister); EXPORT_SYMBOL_GPL(cpufreq_cooling_unregister);

View File

@ -18,7 +18,6 @@
*/ */
#include <linux/cpu_cooling.h> #include <linux/cpu_cooling.h>
#include <linux/cpufreq.h>
#include <linux/err.h> #include <linux/err.h>
#include <linux/module.h> #include <linux/module.h>
#include <linux/of.h> #include <linux/of.h>
@ -28,18 +27,17 @@
static int db8500_cpufreq_cooling_probe(struct platform_device *pdev) static int db8500_cpufreq_cooling_probe(struct platform_device *pdev)
{ {
struct thermal_cooling_device *cdev; struct thermal_cooling_device *cdev;
struct cpumask mask_val;
/* make sure cpufreq driver has been initialized */
if (!cpufreq_frequency_get_table(0))
return -EPROBE_DEFER;
cpumask_set_cpu(0, &mask_val);
cdev = cpufreq_cooling_register(&mask_val);
cdev = cpufreq_cooling_register(cpu_present_mask);
if (IS_ERR(cdev)) { if (IS_ERR(cdev)) {
dev_err(&pdev->dev, "Failed to register cooling device\n"); int ret = PTR_ERR(cdev);
return PTR_ERR(cdev);
if (ret != -EPROBE_DEFER)
dev_err(&pdev->dev,
"Failed to register cooling device %d\n",
ret);
return ret;
} }
platform_set_drvdata(pdev, cdev); platform_set_drvdata(pdev, cdev);

View File

@ -9,7 +9,6 @@
#include <linux/clk.h> #include <linux/clk.h>
#include <linux/cpu_cooling.h> #include <linux/cpu_cooling.h>
#include <linux/cpufreq.h>
#include <linux/delay.h> #include <linux/delay.h>
#include <linux/device.h> #include <linux/device.h>
#include <linux/init.h> #include <linux/init.h>
@ -454,15 +453,10 @@ static int imx_thermal_probe(struct platform_device *pdev)
const struct of_device_id *of_id = const struct of_device_id *of_id =
of_match_device(of_imx_thermal_match, &pdev->dev); of_match_device(of_imx_thermal_match, &pdev->dev);
struct imx_thermal_data *data; struct imx_thermal_data *data;
struct cpumask clip_cpus;
struct regmap *map; struct regmap *map;
int measure_freq; int measure_freq;
int ret; int ret;
if (!cpufreq_get_current_driver()) {
dev_dbg(&pdev->dev, "no cpufreq driver!");
return -EPROBE_DEFER;
}
data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL); data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL);
if (!data) if (!data)
return -ENOMEM; return -ENOMEM;
@ -516,12 +510,13 @@ static int imx_thermal_probe(struct platform_device *pdev)
regmap_write(map, MISC0 + REG_SET, MISC0_REFTOP_SELBIASOFF); regmap_write(map, MISC0 + REG_SET, MISC0_REFTOP_SELBIASOFF);
regmap_write(map, TEMPSENSE0 + REG_SET, TEMPSENSE0_POWER_DOWN); regmap_write(map, TEMPSENSE0 + REG_SET, TEMPSENSE0_POWER_DOWN);
cpumask_set_cpu(0, &clip_cpus); data->cdev = cpufreq_cooling_register(cpu_present_mask);
data->cdev = cpufreq_cooling_register(&clip_cpus);
if (IS_ERR(data->cdev)) { if (IS_ERR(data->cdev)) {
ret = PTR_ERR(data->cdev); ret = PTR_ERR(data->cdev);
dev_err(&pdev->dev, if (ret != -EPROBE_DEFER)
"failed to register cpufreq cooling device: %d\n", ret); dev_err(&pdev->dev,
"failed to register cpufreq cooling device: %d\n",
ret);
return ret; return ret;
} }

View File

@ -1,4 +1,5 @@
obj-$(CONFIG_INT340X_THERMAL) += int3400_thermal.o obj-$(CONFIG_INT340X_THERMAL) += int3400_thermal.o
obj-$(CONFIG_INT340X_THERMAL) += int3402_thermal.o obj-$(CONFIG_INT340X_THERMAL) += int3402_thermal.o
obj-$(CONFIG_INT340X_THERMAL) += int3403_thermal.o obj-$(CONFIG_INT340X_THERMAL) += int3403_thermal.o
obj-$(CONFIG_INT340X_THERMAL) += processor_thermal_device.o
obj-$(CONFIG_ACPI_THERMAL_REL) += acpi_thermal_rel.o obj-$(CONFIG_ACPI_THERMAL_REL) += acpi_thermal_rel.o

View File

@ -82,7 +82,7 @@ int acpi_parse_trt(acpi_handle handle, int *trt_count, struct trt **trtp,
struct acpi_buffer trt_format = { sizeof("RRNNNNNN"), "RRNNNNNN" }; struct acpi_buffer trt_format = { sizeof("RRNNNNNN"), "RRNNNNNN" };
if (!acpi_has_method(handle, "_TRT")) if (!acpi_has_method(handle, "_TRT"))
return 0; return -ENODEV;
status = acpi_evaluate_object(handle, "_TRT", NULL, &buffer); status = acpi_evaluate_object(handle, "_TRT", NULL, &buffer);
if (ACPI_FAILURE(status)) if (ACPI_FAILURE(status))
@ -167,7 +167,7 @@ int acpi_parse_art(acpi_handle handle, int *art_count, struct art **artp,
sizeof("RRNNNNNNNNNNN"), "RRNNNNNNNNNNN" }; sizeof("RRNNNNNNNNNNN"), "RRNNNNNNNNNNN" };
if (!acpi_has_method(handle, "_ART")) if (!acpi_has_method(handle, "_ART"))
return 0; return -ENODEV;
status = acpi_evaluate_object(handle, "_ART", NULL, &buffer); status = acpi_evaluate_object(handle, "_ART", NULL, &buffer);
if (ACPI_FAILURE(status)) if (ACPI_FAILURE(status))
@ -321,8 +321,8 @@ static long acpi_thermal_rel_ioctl(struct file *f, unsigned int cmd,
unsigned long length = 0; unsigned long length = 0;
int count = 0; int count = 0;
char __user *arg = (void __user *)__arg; char __user *arg = (void __user *)__arg;
struct trt *trts; struct trt *trts = NULL;
struct art *arts; struct art *arts = NULL;
switch (cmd) { switch (cmd) {
case ACPI_THERMAL_GET_TRT_COUNT: case ACPI_THERMAL_GET_TRT_COUNT:

View File

@ -335,7 +335,6 @@ static struct platform_driver int3400_thermal_driver = {
.remove = int3400_thermal_remove, .remove = int3400_thermal_remove,
.driver = { .driver = {
.name = "int3400 thermal", .name = "int3400 thermal",
.owner = THIS_MODULE,
.acpi_match_table = ACPI_PTR(int3400_thermal_match), .acpi_match_table = ACPI_PTR(int3400_thermal_match),
}, },
}; };

View File

@ -231,7 +231,6 @@ static struct platform_driver int3402_thermal_driver = {
.remove = int3402_thermal_remove, .remove = int3402_thermal_remove,
.driver = { .driver = {
.name = "int3402 thermal", .name = "int3402 thermal",
.owner = THIS_MODULE,
.acpi_match_table = int3402_thermal_match, .acpi_match_table = int3402_thermal_match,
}, },
}; };

View File

@ -301,6 +301,8 @@ static int int3403_sensor_remove(struct int3403_priv *priv)
{ {
struct int3403_sensor *obj = priv->priv; struct int3403_sensor *obj = priv->priv;
acpi_remove_notify_handler(priv->adev->handle,
ACPI_DEVICE_NOTIFY, int3403_notify);
thermal_zone_device_unregister(obj->tzone); thermal_zone_device_unregister(obj->tzone);
return 0; return 0;
} }
@ -369,6 +371,7 @@ static int int3403_cdev_add(struct int3403_priv *priv)
p = buf.pointer; p = buf.pointer;
if (!p || (p->type != ACPI_TYPE_PACKAGE)) { if (!p || (p->type != ACPI_TYPE_PACKAGE)) {
printk(KERN_WARNING "Invalid PPSS data\n"); printk(KERN_WARNING "Invalid PPSS data\n");
kfree(buf.pointer);
return -EFAULT; return -EFAULT;
} }
@ -381,6 +384,7 @@ static int int3403_cdev_add(struct int3403_priv *priv)
priv->priv = obj; priv->priv = obj;
kfree(buf.pointer);
/* TODO: add ACPI notification support */ /* TODO: add ACPI notification support */
return result; return result;

View File

@ -0,0 +1,309 @@
/*
* processor_thermal_device.c
* Copyright (c) 2014, Intel Corporation.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope 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.
*
*/
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/pci.h>
#include <linux/platform_device.h>
#include <linux/acpi.h>
/* Broadwell-U/HSB thermal reporting device */
#define PCI_DEVICE_ID_PROC_BDW_THERMAL 0x1603
#define PCI_DEVICE_ID_PROC_HSB_THERMAL 0x0A03
/* Braswell thermal reporting device */
#define PCI_DEVICE_ID_PROC_BSW_THERMAL 0x22DC
struct power_config {
u32 index;
u32 min_uw;
u32 max_uw;
u32 tmin_us;
u32 tmax_us;
u32 step_uw;
};
struct proc_thermal_device {
struct device *dev;
struct acpi_device *adev;
struct power_config power_limits[2];
};
enum proc_thermal_emum_mode_type {
PROC_THERMAL_NONE,
PROC_THERMAL_PCI,
PROC_THERMAL_PLATFORM_DEV
};
/*
* We can have only one type of enumeration, PCI or Platform,
* not both. So we don't need instance specific data.
*/
static enum proc_thermal_emum_mode_type proc_thermal_emum_mode =
PROC_THERMAL_NONE;
#define POWER_LIMIT_SHOW(index, suffix) \
static ssize_t power_limit_##index##_##suffix##_show(struct device *dev, \
struct device_attribute *attr, \
char *buf) \
{ \
struct pci_dev *pci_dev; \
struct platform_device *pdev; \
struct proc_thermal_device *proc_dev; \
\
if (proc_thermal_emum_mode == PROC_THERMAL_PLATFORM_DEV) { \
pdev = to_platform_device(dev); \
proc_dev = platform_get_drvdata(pdev); \
} else { \
pci_dev = to_pci_dev(dev); \
proc_dev = pci_get_drvdata(pci_dev); \
} \
return sprintf(buf, "%lu\n",\
(unsigned long)proc_dev->power_limits[index].suffix * 1000); \
}
POWER_LIMIT_SHOW(0, min_uw)
POWER_LIMIT_SHOW(0, max_uw)
POWER_LIMIT_SHOW(0, step_uw)
POWER_LIMIT_SHOW(0, tmin_us)
POWER_LIMIT_SHOW(0, tmax_us)
POWER_LIMIT_SHOW(1, min_uw)
POWER_LIMIT_SHOW(1, max_uw)
POWER_LIMIT_SHOW(1, step_uw)
POWER_LIMIT_SHOW(1, tmin_us)
POWER_LIMIT_SHOW(1, tmax_us)
static DEVICE_ATTR_RO(power_limit_0_min_uw);
static DEVICE_ATTR_RO(power_limit_0_max_uw);
static DEVICE_ATTR_RO(power_limit_0_step_uw);
static DEVICE_ATTR_RO(power_limit_0_tmin_us);
static DEVICE_ATTR_RO(power_limit_0_tmax_us);
static DEVICE_ATTR_RO(power_limit_1_min_uw);
static DEVICE_ATTR_RO(power_limit_1_max_uw);
static DEVICE_ATTR_RO(power_limit_1_step_uw);
static DEVICE_ATTR_RO(power_limit_1_tmin_us);
static DEVICE_ATTR_RO(power_limit_1_tmax_us);
static struct attribute *power_limit_attrs[] = {
&dev_attr_power_limit_0_min_uw.attr,
&dev_attr_power_limit_1_min_uw.attr,
&dev_attr_power_limit_0_max_uw.attr,
&dev_attr_power_limit_1_max_uw.attr,
&dev_attr_power_limit_0_step_uw.attr,
&dev_attr_power_limit_1_step_uw.attr,
&dev_attr_power_limit_0_tmin_us.attr,
&dev_attr_power_limit_1_tmin_us.attr,
&dev_attr_power_limit_0_tmax_us.attr,
&dev_attr_power_limit_1_tmax_us.attr,
NULL
};
static struct attribute_group power_limit_attribute_group = {
.attrs = power_limit_attrs,
.name = "power_limits"
};
static int proc_thermal_add(struct device *dev,
struct proc_thermal_device **priv)
{
struct proc_thermal_device *proc_priv;
struct acpi_device *adev;
acpi_status status;
struct acpi_buffer buf = { ACPI_ALLOCATE_BUFFER, NULL };
union acpi_object *elements, *ppcc;
union acpi_object *p;
int i;
int ret;
adev = ACPI_COMPANION(dev);
status = acpi_evaluate_object(adev->handle, "PPCC", NULL, &buf);
if (ACPI_FAILURE(status))
return -ENODEV;
p = buf.pointer;
if (!p || (p->type != ACPI_TYPE_PACKAGE)) {
dev_err(dev, "Invalid PPCC data\n");
ret = -EFAULT;
goto free_buffer;
}
if (!p->package.count) {
dev_err(dev, "Invalid PPCC package size\n");
ret = -EFAULT;
goto free_buffer;
}
proc_priv = devm_kzalloc(dev, sizeof(*proc_priv), GFP_KERNEL);
if (!proc_priv) {
ret = -ENOMEM;
goto free_buffer;
}
proc_priv->dev = dev;
proc_priv->adev = adev;
for (i = 0; i < min((int)p->package.count - 1, 2); ++i) {
elements = &(p->package.elements[i+1]);
if (elements->type != ACPI_TYPE_PACKAGE ||
elements->package.count != 6) {
ret = -EFAULT;
goto free_buffer;
}
ppcc = elements->package.elements;
proc_priv->power_limits[i].index = ppcc[0].integer.value;
proc_priv->power_limits[i].min_uw = ppcc[1].integer.value;
proc_priv->power_limits[i].max_uw = ppcc[2].integer.value;
proc_priv->power_limits[i].tmin_us = ppcc[3].integer.value;
proc_priv->power_limits[i].tmax_us = ppcc[4].integer.value;
proc_priv->power_limits[i].step_uw = ppcc[5].integer.value;
}
*priv = proc_priv;
ret = sysfs_create_group(&dev->kobj,
&power_limit_attribute_group);
free_buffer:
kfree(buf.pointer);
return ret;
}
void proc_thermal_remove(struct proc_thermal_device *proc_priv)
{
sysfs_remove_group(&proc_priv->dev->kobj,
&power_limit_attribute_group);
}
static int int3401_add(struct platform_device *pdev)
{
struct proc_thermal_device *proc_priv;
int ret;
if (proc_thermal_emum_mode == PROC_THERMAL_PCI) {
dev_err(&pdev->dev, "error: enumerated as PCI dev\n");
return -ENODEV;
}
ret = proc_thermal_add(&pdev->dev, &proc_priv);
if (ret)
return ret;
platform_set_drvdata(pdev, proc_priv);
proc_thermal_emum_mode = PROC_THERMAL_PLATFORM_DEV;
return 0;
}
static int int3401_remove(struct platform_device *pdev)
{
proc_thermal_remove(platform_get_drvdata(pdev));
return 0;
}
static int proc_thermal_pci_probe(struct pci_dev *pdev,
const struct pci_device_id *unused)
{
struct proc_thermal_device *proc_priv;
int ret;
if (proc_thermal_emum_mode == PROC_THERMAL_PLATFORM_DEV) {
dev_err(&pdev->dev, "error: enumerated as platform dev\n");
return -ENODEV;
}
ret = pci_enable_device(pdev);
if (ret < 0) {
dev_err(&pdev->dev, "error: could not enable device\n");
return ret;
}
ret = proc_thermal_add(&pdev->dev, &proc_priv);
if (ret) {
pci_disable_device(pdev);
return ret;
}
pci_set_drvdata(pdev, proc_priv);
proc_thermal_emum_mode = PROC_THERMAL_PCI;
return 0;
}
static void proc_thermal_pci_remove(struct pci_dev *pdev)
{
proc_thermal_remove(pci_get_drvdata(pdev));
pci_disable_device(pdev);
}
static const struct pci_device_id proc_thermal_pci_ids[] = {
{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_PROC_BDW_THERMAL)},
{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_PROC_HSB_THERMAL)},
{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_PROC_BSW_THERMAL)},
{ 0, },
};
MODULE_DEVICE_TABLE(pci, proc_thermal_pci_ids);
static struct pci_driver proc_thermal_pci_driver = {
.name = "proc_thermal",
.probe = proc_thermal_pci_probe,
.remove = proc_thermal_pci_remove,
.id_table = proc_thermal_pci_ids,
};
static const struct acpi_device_id int3401_device_ids[] = {
{"INT3401", 0},
{"", 0},
};
MODULE_DEVICE_TABLE(acpi, int3401_device_ids);
static struct platform_driver int3401_driver = {
.probe = int3401_add,
.remove = int3401_remove,
.driver = {
.name = "int3401 thermal",
.acpi_match_table = int3401_device_ids,
},
};
static int __init proc_thermal_init(void)
{
int ret;
ret = platform_driver_register(&int3401_driver);
if (ret)
return ret;
ret = pci_register_driver(&proc_thermal_pci_driver);
return ret;
}
static void __exit proc_thermal_exit(void)
{
platform_driver_unregister(&int3401_driver);
pci_unregister_driver(&proc_thermal_pci_driver);
}
module_init(proc_thermal_init);
module_exit(proc_thermal_exit);
MODULE_AUTHOR("Srinivas Pandruvada <srinivas.pandruvada@linux.intel.com>");
MODULE_DESCRIPTION("Processor Thermal Reporting Device Driver");
MODULE_LICENSE("GPL v2");

View File

@ -688,6 +688,7 @@ static const struct x86_cpu_id intel_powerclamp_ids[] = {
{ X86_VENDOR_INTEL, 6, 0x45}, { X86_VENDOR_INTEL, 6, 0x45},
{ X86_VENDOR_INTEL, 6, 0x46}, { X86_VENDOR_INTEL, 6, 0x46},
{ X86_VENDOR_INTEL, 6, 0x4c}, { X86_VENDOR_INTEL, 6, 0x4c},
{ X86_VENDOR_INTEL, 6, 0x56},
{} {}
}; };
MODULE_DEVICE_TABLE(x86cpu, intel_powerclamp_ids); MODULE_DEVICE_TABLE(x86cpu, intel_powerclamp_ids);

View File

@ -677,7 +677,6 @@ static SIMPLE_DEV_PM_OPS(rockchip_thermal_pm_ops,
static struct platform_driver rockchip_thermal_driver = { static struct platform_driver rockchip_thermal_driver = {
.driver = { .driver = {
.name = "rockchip-thermal", .name = "rockchip-thermal",
.owner = THIS_MODULE,
.pm = &rockchip_thermal_pm_ops, .pm = &rockchip_thermal_pm_ops,
.of_match_table = of_rockchip_thermal_match, .of_match_table = of_rockchip_thermal_match,
}, },

View File

@ -1,6 +1,6 @@
config EXYNOS_THERMAL config EXYNOS_THERMAL
tristate "Exynos thermal management unit driver" tristate "Exynos thermal management unit driver"
depends on ARCH_HAS_BANDGAP && OF depends on OF
help help
If you say yes here you get support for the TMU (Thermal Management If you say yes here you get support for the TMU (Thermal Management
Unit) driver for SAMSUNG EXYNOS series of SoCs. This driver initialises Unit) driver for SAMSUNG EXYNOS series of SoCs. This driver initialises

View File

@ -347,7 +347,6 @@ void exynos_report_trigger(struct thermal_sensor_conf *conf)
int exynos_register_thermal(struct thermal_sensor_conf *sensor_conf) int exynos_register_thermal(struct thermal_sensor_conf *sensor_conf)
{ {
int ret; int ret;
struct cpumask mask_val;
struct exynos_thermal_zone *th_zone; struct exynos_thermal_zone *th_zone;
if (!sensor_conf || !sensor_conf->read_temperature) { if (!sensor_conf || !sensor_conf->read_temperature) {
@ -367,13 +366,14 @@ int exynos_register_thermal(struct thermal_sensor_conf *sensor_conf)
* sensor * sensor
*/ */
if (sensor_conf->cooling_data.freq_clip_count > 0) { if (sensor_conf->cooling_data.freq_clip_count > 0) {
cpumask_set_cpu(0, &mask_val);
th_zone->cool_dev[th_zone->cool_dev_size] = th_zone->cool_dev[th_zone->cool_dev_size] =
cpufreq_cooling_register(&mask_val); cpufreq_cooling_register(cpu_present_mask);
if (IS_ERR(th_zone->cool_dev[th_zone->cool_dev_size])) { if (IS_ERR(th_zone->cool_dev[th_zone->cool_dev_size])) {
dev_err(sensor_conf->dev, ret = PTR_ERR(th_zone->cool_dev[th_zone->cool_dev_size]);
"Failed to register cpufreq cooling device\n"); if (ret != -EPROBE_DEFER)
ret = -EINVAL; dev_err(sensor_conf->dev,
"Failed to register cpufreq cooling device: %d\n",
ret);
goto err_unregister; goto err_unregister;
} }
th_zone->cool_dev_size++; th_zone->cool_dev_size++;

View File

@ -927,7 +927,10 @@ static int exynos_tmu_probe(struct platform_device *pdev)
/* Register the sensor with thermal management interface */ /* Register the sensor with thermal management interface */
ret = exynos_register_thermal(sensor_conf); ret = exynos_register_thermal(sensor_conf);
if (ret) { if (ret) {
dev_err(&pdev->dev, "Failed to register thermal interface\n"); if (ret != -EPROBE_DEFER)
dev_err(&pdev->dev,
"Failed to register thermal interface: %d\n",
ret);
goto err_clk; goto err_clk;
} }
data->reg_conf = sensor_conf; data->reg_conf = sensor_conf;

View File

@ -930,7 +930,7 @@ int thermal_zone_bind_cooling_device(struct thermal_zone_device *tz,
struct thermal_zone_device *pos1; struct thermal_zone_device *pos1;
struct thermal_cooling_device *pos2; struct thermal_cooling_device *pos2;
unsigned long max_state; unsigned long max_state;
int result; int result, ret;
if (trip >= tz->trips || (trip < 0 && trip != THERMAL_TRIPS_NONE)) if (trip >= tz->trips || (trip < 0 && trip != THERMAL_TRIPS_NONE))
return -EINVAL; return -EINVAL;
@ -947,7 +947,9 @@ int thermal_zone_bind_cooling_device(struct thermal_zone_device *tz,
if (tz != pos1 || cdev != pos2) if (tz != pos1 || cdev != pos2)
return -EINVAL; return -EINVAL;
cdev->ops->get_max_state(cdev, &max_state); ret = cdev->ops->get_max_state(cdev, &max_state);
if (ret)
return ret;
/* lower default 0, upper default max_state */ /* lower default 0, upper default max_state */
lower = lower == THERMAL_NO_LIMIT ? 0 : lower; lower = lower == THERMAL_NO_LIMIT ? 0 : lower;

View File

@ -28,7 +28,6 @@
#include <linux/kernel.h> #include <linux/kernel.h>
#include <linux/workqueue.h> #include <linux/workqueue.h>
#include <linux/thermal.h> #include <linux/thermal.h>
#include <linux/cpufreq.h>
#include <linux/cpumask.h> #include <linux/cpumask.h>
#include <linux/cpu_cooling.h> #include <linux/cpu_cooling.h>
#include <linux/of.h> #include <linux/of.h>
@ -407,17 +406,17 @@ int ti_thermal_register_cpu_cooling(struct ti_bandgap *bgp, int id)
if (!data) if (!data)
return -EINVAL; return -EINVAL;
if (!cpufreq_get_current_driver()) {
dev_dbg(bgp->dev, "no cpufreq driver yet\n");
return -EPROBE_DEFER;
}
/* Register cooling device */ /* Register cooling device */
data->cool_dev = cpufreq_cooling_register(cpu_present_mask); data->cool_dev = cpufreq_cooling_register(cpu_present_mask);
if (IS_ERR(data->cool_dev)) { if (IS_ERR(data->cool_dev)) {
dev_err(bgp->dev, int ret = PTR_ERR(data->cool_dev);
"Failed to register cpufreq cooling device\n");
return PTR_ERR(data->cool_dev); if (ret != -EPROBE_DEFER)
dev_err(bgp->dev,
"Failed to register cpu cooling device %d\n",
ret);
return ret;
} }
ti_bandgap_set_sensor_data(bgp, id, data); ti_bandgap_set_sensor_data(bgp, id, data);

View File

@ -11,7 +11,7 @@
#define _DT_BINDINGS_THERMAL_THERMAL_H #define _DT_BINDINGS_THERMAL_THERMAL_H
/* On cooling devices upper and lower limits */ /* On cooling devices upper and lower limits */
#define THERMAL_NO_LIMIT (-1UL) #define THERMAL_NO_LIMIT (~0)
#endif #endif

View File

@ -50,7 +50,7 @@ static inline struct thermal_cooling_device *
of_cpufreq_cooling_register(struct device_node *np, of_cpufreq_cooling_register(struct device_node *np,
const struct cpumask *clip_cpus) const struct cpumask *clip_cpus)
{ {
return NULL; return ERR_PTR(-ENOSYS);
} }
#endif #endif
@ -65,13 +65,13 @@ unsigned long cpufreq_cooling_get_level(unsigned int cpu, unsigned int freq);
static inline struct thermal_cooling_device * static inline struct thermal_cooling_device *
cpufreq_cooling_register(const struct cpumask *clip_cpus) cpufreq_cooling_register(const struct cpumask *clip_cpus)
{ {
return NULL; return ERR_PTR(-ENOSYS);
} }
static inline struct thermal_cooling_device * static inline struct thermal_cooling_device *
of_cpufreq_cooling_register(struct device_node *np, of_cpufreq_cooling_register(struct device_node *np,
const struct cpumask *clip_cpus) const struct cpumask *clip_cpus)
{ {
return NULL; return ERR_PTR(-ENOSYS);
} }
static inline static inline
void cpufreq_cooling_unregister(struct thermal_cooling_device *cdev) void cpufreq_cooling_unregister(struct thermal_cooling_device *cdev)

View File

@ -38,7 +38,7 @@
#define THERMAL_CSTATE_INVALID -1UL #define THERMAL_CSTATE_INVALID -1UL
/* No upper/lower limit requirement */ /* No upper/lower limit requirement */
#define THERMAL_NO_LIMIT THERMAL_CSTATE_INVALID #define THERMAL_NO_LIMIT ((u32)~0)
/* Unit conversion macros */ /* Unit conversion macros */
#define KELVIN_TO_CELSIUS(t) (long)(((long)t-2732 >= 0) ? \ #define KELVIN_TO_CELSIUS(t) (long)(((long)t-2732 >= 0) ? \