mirror of
https://github.com/Fishwaldo/Star64_linux.git
synced 2025-07-01 11:21:51 +00:00
Merge branch 'next' of git://git.kernel.org/pub/scm/linux/kernel/git/rzhang/linux
Pull thermal management update from Zhang Rui: - Fix race condition in imx_thermal_probe() (Mikhail Lappo) - Add cooling device's statistics in sysfs (Viresh Kumar) * 'next' of git://git.kernel.org/pub/scm/linux/kernel/git/rzhang/linux: thermal: Add cooling device's statistics in sysfs thermal: imx: Fix race condition in imx_thermal_probe()
This commit is contained in:
commit
ba2b137d10
8 changed files with 283 additions and 5 deletions
|
@ -255,6 +255,7 @@ temperature) and throttle appropriate devices.
|
||||||
2. sysfs attributes structure
|
2. sysfs attributes structure
|
||||||
|
|
||||||
RO read only value
|
RO read only value
|
||||||
|
WO write only value
|
||||||
RW read/write value
|
RW read/write value
|
||||||
|
|
||||||
Thermal sysfs attributes will be represented under /sys/class/thermal.
|
Thermal sysfs attributes will be represented under /sys/class/thermal.
|
||||||
|
@ -286,6 +287,11 @@ Thermal cooling device sys I/F, created once it's registered:
|
||||||
|---type: Type of the cooling device(processor/fan/...)
|
|---type: Type of the cooling device(processor/fan/...)
|
||||||
|---max_state: Maximum cooling state of the cooling device
|
|---max_state: Maximum cooling state of the cooling device
|
||||||
|---cur_state: Current cooling state of the cooling device
|
|---cur_state: Current cooling state of the cooling device
|
||||||
|
|---stats: Directory containing cooling device's statistics
|
||||||
|
|---stats/reset: Writing any value resets the statistics
|
||||||
|
|---stats/time_in_state_ms: Time (msec) spent in various cooling states
|
||||||
|
|---stats/total_trans: Total number of times cooling state is changed
|
||||||
|
|---stats/trans_table: Cooing state transition table
|
||||||
|
|
||||||
|
|
||||||
Then next two dynamic attributes are created/removed in pairs. They represent
|
Then next two dynamic attributes are created/removed in pairs. They represent
|
||||||
|
@ -490,6 +496,31 @@ cur_state
|
||||||
- cur_state == max_state means the maximum cooling.
|
- cur_state == max_state means the maximum cooling.
|
||||||
RW, Required
|
RW, Required
|
||||||
|
|
||||||
|
stats/reset
|
||||||
|
Writing any value resets the cooling device's statistics.
|
||||||
|
WO, Required
|
||||||
|
|
||||||
|
stats/time_in_state_ms:
|
||||||
|
The amount of time spent by the cooling device in various cooling
|
||||||
|
states. The output will have "<state> <time>" pair in each line, which
|
||||||
|
will mean this cooling device spent <time> msec of time at <state>.
|
||||||
|
Output will have one line for each of the supported states. usertime
|
||||||
|
units here is 10mS (similar to other time exported in /proc).
|
||||||
|
RO, Required
|
||||||
|
|
||||||
|
stats/total_trans:
|
||||||
|
A single positive value showing the total number of times the state of a
|
||||||
|
cooling device is changed.
|
||||||
|
RO, Required
|
||||||
|
|
||||||
|
stats/trans_table:
|
||||||
|
This gives fine grained information about all the cooling state
|
||||||
|
transitions. The cat output here is a two dimensional matrix, where an
|
||||||
|
entry <i,j> (row i, column j) represents the number of transitions from
|
||||||
|
State_i to State_j. If the transition table is bigger than PAGE_SIZE,
|
||||||
|
reading this will return an -EFBIG error.
|
||||||
|
RO, Required
|
||||||
|
|
||||||
3. A simple implementation
|
3. A simple implementation
|
||||||
|
|
||||||
ACPI thermal zone may support multiple trip points like critical, hot,
|
ACPI thermal zone may support multiple trip points like critical, hot,
|
||||||
|
|
|
@ -15,6 +15,13 @@ menuconfig THERMAL
|
||||||
|
|
||||||
if THERMAL
|
if THERMAL
|
||||||
|
|
||||||
|
config THERMAL_STATISTICS
|
||||||
|
bool "Thermal state transition statistics"
|
||||||
|
help
|
||||||
|
Export thermal state transition statistics information through sysfs.
|
||||||
|
|
||||||
|
If in doubt, say N.
|
||||||
|
|
||||||
config THERMAL_EMERGENCY_POWEROFF_DELAY_MS
|
config THERMAL_EMERGENCY_POWEROFF_DELAY_MS
|
||||||
int "Emergency poweroff delay in milli-seconds"
|
int "Emergency poweroff delay in milli-seconds"
|
||||||
depends on THERMAL
|
depends on THERMAL
|
||||||
|
|
|
@ -637,6 +637,9 @@ static int imx_thermal_probe(struct platform_device *pdev)
|
||||||
regmap_write(map, TEMPSENSE0 + REG_CLR, TEMPSENSE0_POWER_DOWN);
|
regmap_write(map, TEMPSENSE0 + REG_CLR, TEMPSENSE0_POWER_DOWN);
|
||||||
regmap_write(map, TEMPSENSE0 + REG_SET, TEMPSENSE0_MEASURE_TEMP);
|
regmap_write(map, TEMPSENSE0 + REG_SET, TEMPSENSE0_MEASURE_TEMP);
|
||||||
|
|
||||||
|
data->irq_enabled = true;
|
||||||
|
data->mode = THERMAL_DEVICE_ENABLED;
|
||||||
|
|
||||||
ret = devm_request_threaded_irq(&pdev->dev, data->irq,
|
ret = devm_request_threaded_irq(&pdev->dev, data->irq,
|
||||||
imx_thermal_alarm_irq, imx_thermal_alarm_irq_thread,
|
imx_thermal_alarm_irq, imx_thermal_alarm_irq_thread,
|
||||||
0, "imx_thermal", data);
|
0, "imx_thermal", data);
|
||||||
|
@ -649,9 +652,6 @@ static int imx_thermal_probe(struct platform_device *pdev)
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
data->irq_enabled = true;
|
|
||||||
data->mode = THERMAL_DEVICE_ENABLED;
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -972,8 +972,8 @@ __thermal_cooling_device_register(struct device_node *np,
|
||||||
cdev->ops = ops;
|
cdev->ops = ops;
|
||||||
cdev->updated = false;
|
cdev->updated = false;
|
||||||
cdev->device.class = &thermal_class;
|
cdev->device.class = &thermal_class;
|
||||||
thermal_cooling_device_setup_sysfs(cdev);
|
|
||||||
cdev->devdata = devdata;
|
cdev->devdata = devdata;
|
||||||
|
thermal_cooling_device_setup_sysfs(cdev);
|
||||||
dev_set_name(&cdev->device, "cooling_device%d", cdev->id);
|
dev_set_name(&cdev->device, "cooling_device%d", cdev->id);
|
||||||
result = device_register(&cdev->device);
|
result = device_register(&cdev->device);
|
||||||
if (result) {
|
if (result) {
|
||||||
|
@ -1106,6 +1106,7 @@ void thermal_cooling_device_unregister(struct thermal_cooling_device *cdev)
|
||||||
|
|
||||||
ida_simple_remove(&thermal_cdev_ida, cdev->id);
|
ida_simple_remove(&thermal_cdev_ida, cdev->id);
|
||||||
device_unregister(&cdev->device);
|
device_unregister(&cdev->device);
|
||||||
|
thermal_cooling_device_destroy_sysfs(cdev);
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL_GPL(thermal_cooling_device_unregister);
|
EXPORT_SYMBOL_GPL(thermal_cooling_device_unregister);
|
||||||
|
|
||||||
|
|
|
@ -73,6 +73,7 @@ int thermal_build_list_of_policies(char *buf);
|
||||||
int thermal_zone_create_device_groups(struct thermal_zone_device *, int);
|
int thermal_zone_create_device_groups(struct thermal_zone_device *, int);
|
||||||
void thermal_zone_destroy_device_groups(struct thermal_zone_device *);
|
void thermal_zone_destroy_device_groups(struct thermal_zone_device *);
|
||||||
void thermal_cooling_device_setup_sysfs(struct thermal_cooling_device *);
|
void thermal_cooling_device_setup_sysfs(struct thermal_cooling_device *);
|
||||||
|
void thermal_cooling_device_destroy_sysfs(struct thermal_cooling_device *cdev);
|
||||||
/* used only at binding time */
|
/* used only at binding time */
|
||||||
ssize_t
|
ssize_t
|
||||||
thermal_cooling_device_trip_point_show(struct device *,
|
thermal_cooling_device_trip_point_show(struct device *,
|
||||||
|
@ -84,6 +85,15 @@ ssize_t thermal_cooling_device_weight_store(struct device *,
|
||||||
struct device_attribute *,
|
struct device_attribute *,
|
||||||
const char *, size_t);
|
const char *, size_t);
|
||||||
|
|
||||||
|
#ifdef CONFIG_THERMAL_STATISTICS
|
||||||
|
void thermal_cooling_device_stats_update(struct thermal_cooling_device *cdev,
|
||||||
|
unsigned long new_state);
|
||||||
|
#else
|
||||||
|
static inline void
|
||||||
|
thermal_cooling_device_stats_update(struct thermal_cooling_device *cdev,
|
||||||
|
unsigned long new_state) {}
|
||||||
|
#endif /* CONFIG_THERMAL_STATISTICS */
|
||||||
|
|
||||||
#ifdef CONFIG_THERMAL_GOV_STEP_WISE
|
#ifdef CONFIG_THERMAL_GOV_STEP_WISE
|
||||||
int thermal_gov_step_wise_register(void);
|
int thermal_gov_step_wise_register(void);
|
||||||
void thermal_gov_step_wise_unregister(void);
|
void thermal_gov_step_wise_unregister(void);
|
||||||
|
|
|
@ -187,7 +187,10 @@ void thermal_cdev_update(struct thermal_cooling_device *cdev)
|
||||||
if (instance->target > target)
|
if (instance->target > target)
|
||||||
target = instance->target;
|
target = instance->target;
|
||||||
}
|
}
|
||||||
cdev->ops->set_cur_state(cdev, target);
|
|
||||||
|
if (!cdev->ops->set_cur_state(cdev, target))
|
||||||
|
thermal_cooling_device_stats_update(cdev, target);
|
||||||
|
|
||||||
cdev->updated = true;
|
cdev->updated = true;
|
||||||
mutex_unlock(&cdev->lock);
|
mutex_unlock(&cdev->lock);
|
||||||
trace_cdev_update(cdev, target);
|
trace_cdev_update(cdev, target);
|
||||||
|
|
|
@ -20,6 +20,7 @@
|
||||||
#include <linux/err.h>
|
#include <linux/err.h>
|
||||||
#include <linux/slab.h>
|
#include <linux/slab.h>
|
||||||
#include <linux/string.h>
|
#include <linux/string.h>
|
||||||
|
#include <linux/jiffies.h>
|
||||||
|
|
||||||
#include "thermal_core.h"
|
#include "thermal_core.h"
|
||||||
|
|
||||||
|
@ -721,6 +722,7 @@ thermal_cooling_device_cur_state_store(struct device *dev,
|
||||||
result = cdev->ops->set_cur_state(cdev, state);
|
result = cdev->ops->set_cur_state(cdev, state);
|
||||||
if (result)
|
if (result)
|
||||||
return result;
|
return result;
|
||||||
|
thermal_cooling_device_stats_update(cdev, state);
|
||||||
return count;
|
return count;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -745,14 +747,237 @@ static const struct attribute_group cooling_device_attr_group = {
|
||||||
|
|
||||||
static const struct attribute_group *cooling_device_attr_groups[] = {
|
static const struct attribute_group *cooling_device_attr_groups[] = {
|
||||||
&cooling_device_attr_group,
|
&cooling_device_attr_group,
|
||||||
|
NULL, /* Space allocated for cooling_device_stats_attr_group */
|
||||||
NULL,
|
NULL,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
#ifdef CONFIG_THERMAL_STATISTICS
|
||||||
|
struct cooling_dev_stats {
|
||||||
|
spinlock_t lock;
|
||||||
|
unsigned int total_trans;
|
||||||
|
unsigned long state;
|
||||||
|
unsigned long max_states;
|
||||||
|
ktime_t last_time;
|
||||||
|
ktime_t *time_in_state;
|
||||||
|
unsigned int *trans_table;
|
||||||
|
};
|
||||||
|
|
||||||
|
static void update_time_in_state(struct cooling_dev_stats *stats)
|
||||||
|
{
|
||||||
|
ktime_t now = ktime_get(), delta;
|
||||||
|
|
||||||
|
delta = ktime_sub(now, stats->last_time);
|
||||||
|
stats->time_in_state[stats->state] =
|
||||||
|
ktime_add(stats->time_in_state[stats->state], delta);
|
||||||
|
stats->last_time = now;
|
||||||
|
}
|
||||||
|
|
||||||
|
void thermal_cooling_device_stats_update(struct thermal_cooling_device *cdev,
|
||||||
|
unsigned long new_state)
|
||||||
|
{
|
||||||
|
struct cooling_dev_stats *stats = cdev->stats;
|
||||||
|
|
||||||
|
spin_lock(&stats->lock);
|
||||||
|
|
||||||
|
if (stats->state == new_state)
|
||||||
|
goto unlock;
|
||||||
|
|
||||||
|
update_time_in_state(stats);
|
||||||
|
stats->trans_table[stats->state * stats->max_states + new_state]++;
|
||||||
|
stats->state = new_state;
|
||||||
|
stats->total_trans++;
|
||||||
|
|
||||||
|
unlock:
|
||||||
|
spin_unlock(&stats->lock);
|
||||||
|
}
|
||||||
|
|
||||||
|
static ssize_t
|
||||||
|
thermal_cooling_device_total_trans_show(struct device *dev,
|
||||||
|
struct device_attribute *attr,
|
||||||
|
char *buf)
|
||||||
|
{
|
||||||
|
struct thermal_cooling_device *cdev = to_cooling_device(dev);
|
||||||
|
struct cooling_dev_stats *stats = cdev->stats;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
spin_lock(&stats->lock);
|
||||||
|
ret = sprintf(buf, "%u\n", stats->total_trans);
|
||||||
|
spin_unlock(&stats->lock);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static ssize_t
|
||||||
|
thermal_cooling_device_time_in_state_show(struct device *dev,
|
||||||
|
struct device_attribute *attr,
|
||||||
|
char *buf)
|
||||||
|
{
|
||||||
|
struct thermal_cooling_device *cdev = to_cooling_device(dev);
|
||||||
|
struct cooling_dev_stats *stats = cdev->stats;
|
||||||
|
ssize_t len = 0;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
spin_lock(&stats->lock);
|
||||||
|
update_time_in_state(stats);
|
||||||
|
|
||||||
|
for (i = 0; i < stats->max_states; i++) {
|
||||||
|
len += sprintf(buf + len, "state%u\t%llu\n", i,
|
||||||
|
ktime_to_ms(stats->time_in_state[i]));
|
||||||
|
}
|
||||||
|
spin_unlock(&stats->lock);
|
||||||
|
|
||||||
|
return len;
|
||||||
|
}
|
||||||
|
|
||||||
|
static ssize_t
|
||||||
|
thermal_cooling_device_reset_store(struct device *dev,
|
||||||
|
struct device_attribute *attr,
|
||||||
|
const char *buf, size_t count)
|
||||||
|
{
|
||||||
|
struct thermal_cooling_device *cdev = to_cooling_device(dev);
|
||||||
|
struct cooling_dev_stats *stats = cdev->stats;
|
||||||
|
int i, states = stats->max_states;
|
||||||
|
|
||||||
|
spin_lock(&stats->lock);
|
||||||
|
|
||||||
|
stats->total_trans = 0;
|
||||||
|
stats->last_time = ktime_get();
|
||||||
|
memset(stats->trans_table, 0,
|
||||||
|
states * states * sizeof(*stats->trans_table));
|
||||||
|
|
||||||
|
for (i = 0; i < stats->max_states; i++)
|
||||||
|
stats->time_in_state[i] = ktime_set(0, 0);
|
||||||
|
|
||||||
|
spin_unlock(&stats->lock);
|
||||||
|
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
|
||||||
|
static ssize_t
|
||||||
|
thermal_cooling_device_trans_table_show(struct device *dev,
|
||||||
|
struct device_attribute *attr,
|
||||||
|
char *buf)
|
||||||
|
{
|
||||||
|
struct thermal_cooling_device *cdev = to_cooling_device(dev);
|
||||||
|
struct cooling_dev_stats *stats = cdev->stats;
|
||||||
|
ssize_t len = 0;
|
||||||
|
int i, j;
|
||||||
|
|
||||||
|
len += snprintf(buf + len, PAGE_SIZE - len, " From : To\n");
|
||||||
|
len += snprintf(buf + len, PAGE_SIZE - len, " : ");
|
||||||
|
for (i = 0; i < stats->max_states; i++) {
|
||||||
|
if (len >= PAGE_SIZE)
|
||||||
|
break;
|
||||||
|
len += snprintf(buf + len, PAGE_SIZE - len, "state%2u ", i);
|
||||||
|
}
|
||||||
|
if (len >= PAGE_SIZE)
|
||||||
|
return PAGE_SIZE;
|
||||||
|
|
||||||
|
len += snprintf(buf + len, PAGE_SIZE - len, "\n");
|
||||||
|
|
||||||
|
for (i = 0; i < stats->max_states; i++) {
|
||||||
|
if (len >= PAGE_SIZE)
|
||||||
|
break;
|
||||||
|
|
||||||
|
len += snprintf(buf + len, PAGE_SIZE - len, "state%2u:", i);
|
||||||
|
|
||||||
|
for (j = 0; j < stats->max_states; j++) {
|
||||||
|
if (len >= PAGE_SIZE)
|
||||||
|
break;
|
||||||
|
len += snprintf(buf + len, PAGE_SIZE - len, "%8u ",
|
||||||
|
stats->trans_table[i * stats->max_states + j]);
|
||||||
|
}
|
||||||
|
if (len >= PAGE_SIZE)
|
||||||
|
break;
|
||||||
|
len += snprintf(buf + len, PAGE_SIZE - len, "\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (len >= PAGE_SIZE) {
|
||||||
|
pr_warn_once("Thermal transition table exceeds PAGE_SIZE. Disabling\n");
|
||||||
|
return -EFBIG;
|
||||||
|
}
|
||||||
|
return len;
|
||||||
|
}
|
||||||
|
|
||||||
|
static DEVICE_ATTR(total_trans, 0444, thermal_cooling_device_total_trans_show,
|
||||||
|
NULL);
|
||||||
|
static DEVICE_ATTR(time_in_state_ms, 0444,
|
||||||
|
thermal_cooling_device_time_in_state_show, NULL);
|
||||||
|
static DEVICE_ATTR(reset, 0200, NULL, thermal_cooling_device_reset_store);
|
||||||
|
static DEVICE_ATTR(trans_table, 0444,
|
||||||
|
thermal_cooling_device_trans_table_show, NULL);
|
||||||
|
|
||||||
|
static struct attribute *cooling_device_stats_attrs[] = {
|
||||||
|
&dev_attr_total_trans.attr,
|
||||||
|
&dev_attr_time_in_state_ms.attr,
|
||||||
|
&dev_attr_reset.attr,
|
||||||
|
&dev_attr_trans_table.attr,
|
||||||
|
NULL
|
||||||
|
};
|
||||||
|
|
||||||
|
static const struct attribute_group cooling_device_stats_attr_group = {
|
||||||
|
.attrs = cooling_device_stats_attrs,
|
||||||
|
.name = "stats"
|
||||||
|
};
|
||||||
|
|
||||||
|
static void cooling_device_stats_setup(struct thermal_cooling_device *cdev)
|
||||||
|
{
|
||||||
|
struct cooling_dev_stats *stats;
|
||||||
|
unsigned long states;
|
||||||
|
int var;
|
||||||
|
|
||||||
|
if (cdev->ops->get_max_state(cdev, &states))
|
||||||
|
return;
|
||||||
|
|
||||||
|
states++; /* Total number of states is highest state + 1 */
|
||||||
|
|
||||||
|
var = sizeof(*stats);
|
||||||
|
var += sizeof(*stats->time_in_state) * states;
|
||||||
|
var += sizeof(*stats->trans_table) * states * states;
|
||||||
|
|
||||||
|
stats = kzalloc(var, GFP_KERNEL);
|
||||||
|
if (!stats)
|
||||||
|
return;
|
||||||
|
|
||||||
|
stats->time_in_state = (ktime_t *)(stats + 1);
|
||||||
|
stats->trans_table = (unsigned int *)(stats->time_in_state + states);
|
||||||
|
cdev->stats = stats;
|
||||||
|
stats->last_time = ktime_get();
|
||||||
|
stats->max_states = states;
|
||||||
|
|
||||||
|
spin_lock_init(&stats->lock);
|
||||||
|
|
||||||
|
/* Fill the empty slot left in cooling_device_attr_groups */
|
||||||
|
var = ARRAY_SIZE(cooling_device_attr_groups) - 2;
|
||||||
|
cooling_device_attr_groups[var] = &cooling_device_stats_attr_group;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void cooling_device_stats_destroy(struct thermal_cooling_device *cdev)
|
||||||
|
{
|
||||||
|
kfree(cdev->stats);
|
||||||
|
cdev->stats = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
#else
|
||||||
|
|
||||||
|
static inline void
|
||||||
|
cooling_device_stats_setup(struct thermal_cooling_device *cdev) {}
|
||||||
|
static inline void
|
||||||
|
cooling_device_stats_destroy(struct thermal_cooling_device *cdev) {}
|
||||||
|
|
||||||
|
#endif /* CONFIG_THERMAL_STATISTICS */
|
||||||
|
|
||||||
void thermal_cooling_device_setup_sysfs(struct thermal_cooling_device *cdev)
|
void thermal_cooling_device_setup_sysfs(struct thermal_cooling_device *cdev)
|
||||||
{
|
{
|
||||||
|
cooling_device_stats_setup(cdev);
|
||||||
cdev->device.groups = cooling_device_attr_groups;
|
cdev->device.groups = cooling_device_attr_groups;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void thermal_cooling_device_destroy_sysfs(struct thermal_cooling_device *cdev)
|
||||||
|
{
|
||||||
|
cooling_device_stats_destroy(cdev);
|
||||||
|
}
|
||||||
|
|
||||||
/* these helper will be used only at the time of bindig */
|
/* these helper will be used only at the time of bindig */
|
||||||
ssize_t
|
ssize_t
|
||||||
thermal_cooling_device_trip_point_show(struct device *dev,
|
thermal_cooling_device_trip_point_show(struct device *dev,
|
||||||
|
|
|
@ -148,6 +148,7 @@ struct thermal_cooling_device {
|
||||||
struct device device;
|
struct device device;
|
||||||
struct device_node *np;
|
struct device_node *np;
|
||||||
void *devdata;
|
void *devdata;
|
||||||
|
void *stats;
|
||||||
const struct thermal_cooling_device_ops *ops;
|
const struct thermal_cooling_device_ops *ops;
|
||||||
bool updated; /* true if the cooling device does not need update */
|
bool updated; /* true if the cooling device does not need update */
|
||||||
struct mutex lock; /* protect thermal_instances list */
|
struct mutex lock; /* protect thermal_instances list */
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue