mirror of
https://github.com/BobTheBlinker/android_kernel_motorola_sm6375.git
synced 2026-10-07 04:12:04 -04:00
drivers: thermal: Add support to change thermal zone configuration
Add debug file system node to configure the thermal zone parameters dynamically. This node will use the same format that is used to print the configuration of the thermal zone. Change-Id: I4fd8cdafcb689f4d986738864e31177401f9bc47 Signed-off-by: Ram Chandrasekar <rkumbako@codeaurora.org>
This commit is contained in:
parent
6ea589d54a
commit
e0cb6023dc
5 changed files with 418 additions and 0 deletions
|
|
@ -27,6 +27,9 @@ thermal_sys-$(CONFIG_CLOCK_THERMAL) += clock_cooling.o
|
|||
# devfreq cooling
|
||||
thermal_sys-$(CONFIG_DEVFREQ_THERMAL) += devfreq_cooling.o
|
||||
|
||||
# debugfs support
|
||||
thermal_sys-$(CONFIG_QTI_THERMAL) += thermal_debugfs.o
|
||||
|
||||
# platform thermal drivers
|
||||
obj-y += broadcom/
|
||||
obj-$(CONFIG_THERMAL_MMIO) += thermal_mmio.o
|
||||
|
|
|
|||
|
|
@ -466,8 +466,19 @@ static int of_thermal_set_mode(struct thermal_zone_device *tz,
|
|||
mutex_lock(&tz->lock);
|
||||
|
||||
if (mode == THERMAL_DEVICE_ENABLED) {
|
||||
#ifdef CONFIG_QTI_THERMAL
|
||||
if (!tz->polling_delay)
|
||||
tz->polling_delay = data->polling_delay;
|
||||
else
|
||||
data->polling_delay = tz->polling_delay;
|
||||
if (!tz->passive_delay)
|
||||
tz->passive_delay = data->passive_delay;
|
||||
else
|
||||
data->passive_delay = tz->passive_delay;
|
||||
#else
|
||||
tz->polling_delay = data->polling_delay;
|
||||
tz->passive_delay = data->passive_delay;
|
||||
#endif
|
||||
} else {
|
||||
tz->polling_delay = 0;
|
||||
tz->passive_delay = 0;
|
||||
|
|
|
|||
|
|
@ -1799,6 +1799,7 @@ static int __init thermal_init(void)
|
|||
if (result)
|
||||
pr_warn("Thermal: Can not register suspend notifier, return %d\n",
|
||||
result);
|
||||
thermal_debug_init();
|
||||
|
||||
return 0;
|
||||
|
||||
|
|
@ -1824,6 +1825,7 @@ static void thermal_exit(void)
|
|||
destroy_workqueue(thermal_passive_wq);
|
||||
genetlink_exit();
|
||||
class_unregister(&thermal_class);
|
||||
thermal_debug_exit();
|
||||
thermal_unregister_governors();
|
||||
ida_destroy(&thermal_tz_ida);
|
||||
ida_destroy(&thermal_cdev_ida);
|
||||
|
|
|
|||
|
|
@ -126,6 +126,8 @@ void of_thermal_handle_trip(struct device *dev,
|
|||
void of_thermal_handle_trip_temp(struct device *dev,
|
||||
struct thermal_zone_device *tz,
|
||||
int trip_temp);
|
||||
int thermal_debug_init(void);
|
||||
void thermal_debug_exit(void);
|
||||
#else
|
||||
static inline int of_thermal_aggregate_trip(struct device *dev,
|
||||
struct thermal_zone_device *tz,
|
||||
|
|
@ -141,6 +143,12 @@ static inline
|
|||
void of_thermal_handle_trip_temp(struct device *dev,
|
||||
struct thermal_zone_device *tz, int trip_temp)
|
||||
{ }
|
||||
static inline int thermal_debug_init(void)
|
||||
{
|
||||
return -ENODEV;
|
||||
}
|
||||
static inline void thermal_debug_exit(void)
|
||||
{ }
|
||||
#endif
|
||||
|
||||
#endif /* __THERMAL_CORE_H__ */
|
||||
|
|
|
|||
394
drivers/thermal/thermal_debugfs.c
Normal file
394
drivers/thermal/thermal_debugfs.c
Normal file
|
|
@ -0,0 +1,394 @@
|
|||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/*
|
||||
* Copyright (c) 2020, The Linux Foundation. All rights reserved.
|
||||
*/
|
||||
|
||||
#include <linux/debugfs.h>
|
||||
#include <linux/uaccess.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/string.h>
|
||||
|
||||
#include "thermal_core.h"
|
||||
|
||||
#define SPACE_DELIMITER " \t"
|
||||
|
||||
static struct dentry *thermal_debugfs_parent;
|
||||
static struct dentry *thermal_debugfs_config;
|
||||
|
||||
static int fetch_cdev(struct thermal_zone_device *tz, char *dev_token,
|
||||
char *upper_lim_token, char *lower_lim_token, int trip)
|
||||
{
|
||||
unsigned long upper_limit, lower_limit;
|
||||
char cdev_name[THERMAL_NAME_LENGTH] = "";
|
||||
char limit_str[THERMAL_NAME_LENGTH] = "";
|
||||
struct thermal_instance *instance;
|
||||
bool match_found = false;
|
||||
|
||||
dev_token = strim(dev_token);
|
||||
if (sscanf(dev_token, "%20[^ +\n\t]", cdev_name) != 1)
|
||||
return -EINVAL;
|
||||
list_for_each_entry(instance, &tz->thermal_instances, tz_node) {
|
||||
if (!instance->cdev || instance->trip != trip)
|
||||
continue;
|
||||
|
||||
if (strcmp(instance->cdev->type, cdev_name))
|
||||
continue;
|
||||
match_found = true;
|
||||
break;
|
||||
}
|
||||
if (!match_found)
|
||||
return -ENODEV;
|
||||
if (upper_lim_token) {
|
||||
if (sscanf(upper_lim_token, "%20[^ +\n\t]", limit_str) != 1)
|
||||
return -EINVAL;
|
||||
if (kstrtoul(limit_str, 0, &upper_limit))
|
||||
return -EINVAL;
|
||||
instance->upper = upper_limit;
|
||||
}
|
||||
if (lower_lim_token) {
|
||||
if (sscanf(lower_lim_token, "%20[^ +\n\t]", limit_str) != 1)
|
||||
return -EINVAL;
|
||||
if (kstrtoul(limit_str, 0, &lower_limit))
|
||||
return -EINVAL;
|
||||
instance->lower = lower_limit;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int fetch_and_update_cdev(struct thermal_zone_device *tz, int trip,
|
||||
char **dev_buf, char *dev_buf_end,
|
||||
char **up_buf, char *up_buf_end,
|
||||
char **low_buf, char *low_buf_end)
|
||||
{
|
||||
int ret;
|
||||
char *dev_token, *upper_lim_token, *lower_lim_token, *dev1_token;
|
||||
|
||||
dev_token = strsep(dev_buf, SPACE_DELIMITER);
|
||||
if (*up_buf) {
|
||||
upper_lim_token = strsep(up_buf, SPACE_DELIMITER);
|
||||
upper_lim_token = strnchr(*up_buf, up_buf_end - *up_buf,
|
||||
' ');
|
||||
if (upper_lim_token && upper_lim_token < up_buf_end)
|
||||
up_buf_end = upper_lim_token;
|
||||
}
|
||||
if (*low_buf) {
|
||||
lower_lim_token = strsep(low_buf, SPACE_DELIMITER);
|
||||
lower_lim_token = strnchr(*low_buf, low_buf_end - *low_buf,
|
||||
' ');
|
||||
if (lower_lim_token && lower_lim_token < low_buf_end)
|
||||
low_buf_end = lower_lim_token;
|
||||
}
|
||||
if (dev_token && *dev_buf < dev_buf_end) {
|
||||
do {
|
||||
ret = fetch_cdev(tz, *dev_buf, *up_buf,
|
||||
*low_buf, trip);
|
||||
if (ret)
|
||||
return ret;
|
||||
dev1_token = strnchr(*dev_buf, dev_buf_end - *dev_buf,
|
||||
'+');
|
||||
if (!dev1_token || dev1_token >= dev_buf_end)
|
||||
break;
|
||||
dev1_token = strsep(dev_buf, "+");
|
||||
if (*up_buf) {
|
||||
upper_lim_token = strnchr(*up_buf,
|
||||
up_buf_end - *up_buf,
|
||||
'+');
|
||||
if (!upper_lim_token ||
|
||||
upper_lim_token >= up_buf_end)
|
||||
return -EINVAL;
|
||||
upper_lim_token = strsep(up_buf, "+");
|
||||
}
|
||||
if (*low_buf) {
|
||||
lower_lim_token = strnchr(*low_buf,
|
||||
low_buf_end - *low_buf,
|
||||
'+');
|
||||
if (!lower_lim_token ||
|
||||
lower_lim_token >= low_buf_end)
|
||||
return -EINVAL;
|
||||
lower_lim_token = strsep(low_buf, "+");
|
||||
}
|
||||
} while (dev1_token);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int fetch_and_update_threshold(struct thermal_zone_device *tz, int trip,
|
||||
char **temp_buf, char *temp_buf_end, bool is_trip)
|
||||
{
|
||||
int ret, trip_temp;
|
||||
char *tripT_token;
|
||||
|
||||
tripT_token = strsep(temp_buf, SPACE_DELIMITER);
|
||||
if (tripT_token && *temp_buf < temp_buf_end) {
|
||||
if (kstrtoint(*temp_buf, 0, &trip_temp))
|
||||
return -EINVAL;
|
||||
if (is_trip)
|
||||
ret = tz->ops->set_trip_temp(tz, trip, trip_temp);
|
||||
else
|
||||
ret = tz->ops->set_trip_hyst(tz, trip, trip_temp);
|
||||
if (ret)
|
||||
return ret;
|
||||
} else {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int parse_threshold(struct thermal_zone_device *tz, char *buf_ptr,
|
||||
size_t count)
|
||||
{
|
||||
char *trip_buf_end = NULL, *temp_buf_end = NULL, *hyst_buf_end = NULL;
|
||||
char *trip_buf = NULL, *temp_buf = NULL, *hyst_buf = NULL;
|
||||
char *dev_buf_end = NULL, *up_buf_end = NULL, *low_buf_end = NULL;
|
||||
char *dev_buf = NULL, *up_buf = NULL, *low_buf = NULL;
|
||||
char *buf = buf_ptr, *token = NULL;
|
||||
int ret;
|
||||
|
||||
trip_buf = strnstr(buf, "trip", count);
|
||||
trip_buf_end = strnchr(trip_buf, buf_ptr + count - trip_buf, '\n');
|
||||
if (!trip_buf_end)
|
||||
return -EINVAL;
|
||||
|
||||
temp_buf = strnstr(buf, "trip_threshold", count);
|
||||
if (!temp_buf)
|
||||
goto eval_device;
|
||||
if (!tz->ops->set_trip_temp)
|
||||
return -EPERM;
|
||||
|
||||
temp_buf_end = strnchr(temp_buf, buf_ptr + count - temp_buf, '\n');
|
||||
if (!temp_buf_end)
|
||||
return -EINVAL;
|
||||
|
||||
hyst_buf = strnstr(buf, "trip_threshold_clr", count);
|
||||
if (hyst_buf) {
|
||||
if (!tz->ops->set_trip_hyst)
|
||||
return -EPERM;
|
||||
hyst_buf_end = strnchr(hyst_buf,
|
||||
buf_ptr + count - hyst_buf, '\n');
|
||||
if (!hyst_buf_end)
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
eval_device:
|
||||
dev_buf = strnstr(buf, "device", count);
|
||||
if (!dev_buf) {
|
||||
if (!temp_buf)
|
||||
return -EINVAL;
|
||||
goto parse_config;
|
||||
}
|
||||
dev_buf_end = strnchr(dev_buf, buf_ptr + count - dev_buf, '\n');
|
||||
if (!dev_buf_end)
|
||||
return -EINVAL;
|
||||
|
||||
up_buf = strnstr(buf, "upper_limit", count);
|
||||
if (!up_buf)
|
||||
return -EINVAL;
|
||||
up_buf_end = strnchr(up_buf, buf_ptr + count - up_buf, '\n');
|
||||
if (!up_buf_end)
|
||||
return -EINVAL;
|
||||
|
||||
low_buf = strnstr(buf, "lower_limit", count);
|
||||
if (!low_buf)
|
||||
return -EINVAL;
|
||||
low_buf_end = strnchr(low_buf, buf_ptr + count - low_buf, '\n');
|
||||
if (!low_buf_end)
|
||||
return -EINVAL;
|
||||
parse_config:
|
||||
*trip_buf_end = '\0';
|
||||
if (temp_buf_end)
|
||||
*temp_buf_end = '\0';
|
||||
if (hyst_buf_end)
|
||||
*hyst_buf_end = '\0';
|
||||
if (dev_buf_end)
|
||||
*dev_buf_end = '\0';
|
||||
if (up_buf_end)
|
||||
*up_buf_end = '\0';
|
||||
if (low_buf_end)
|
||||
*low_buf_end = '\0';
|
||||
token = strnchr(trip_buf, trip_buf_end - trip_buf, ' ');
|
||||
while (token && token < trip_buf_end) {
|
||||
int trip;
|
||||
|
||||
token = strsep((char **)&trip_buf, " ");
|
||||
if (kstrtoint(trip_buf, 0, &trip))
|
||||
return -EINVAL;
|
||||
|
||||
if (temp_buf) {
|
||||
ret = fetch_and_update_threshold(tz, trip, &temp_buf,
|
||||
temp_buf_end, true);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (hyst_buf) {
|
||||
ret = fetch_and_update_threshold(tz, trip, &hyst_buf,
|
||||
hyst_buf_end, false);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (dev_buf) {
|
||||
ret = fetch_and_update_cdev(tz, trip, &dev_buf,
|
||||
dev_buf_end, &up_buf, up_buf_end,
|
||||
&low_buf, low_buf_end);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
token = strnchr(trip_buf, trip_buf_end - trip_buf, ' ');
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int parse_delay(struct thermal_zone_device *tz, char *buf,
|
||||
size_t count, bool is_polling)
|
||||
{
|
||||
int delay = 0;
|
||||
|
||||
if (is_polling) {
|
||||
if (sscanf(buf, "polling_delay %d", &delay) != 1)
|
||||
return -EINVAL;
|
||||
tz->polling_delay = delay;
|
||||
} else {
|
||||
if (sscanf(buf, "passive_polling_delay %d", &delay) != 1)
|
||||
return -EINVAL;
|
||||
tz->passive_delay = delay;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int parse_config(struct thermal_zone_device *tz, char *buf_ptr,
|
||||
size_t buf_ct)
|
||||
{
|
||||
char *buf, *buf_end, *next_buf, *curr_buf;
|
||||
int count, ret = 0;
|
||||
enum thermal_device_mode mode = THERMAL_DEVICE_ENABLED;
|
||||
|
||||
if (tz->ops->get_mode) {
|
||||
ret = tz->ops->get_mode(tz, &mode);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
if (tz->ops->set_mode) {
|
||||
ret = tz->ops->set_mode(tz, THERMAL_DEVICE_DISABLED);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
mutex_lock(&tz->lock);
|
||||
buf = kzalloc(sizeof(char) * (buf_ct + 1), GFP_KERNEL);
|
||||
if (!buf) {
|
||||
ret = -ENOMEM;
|
||||
goto parse_exit;
|
||||
}
|
||||
strlcpy(buf, buf_ptr, (buf_ct + 1));
|
||||
buf_end = buf + buf_ct;
|
||||
if (strnstr(buf, "trip", buf_ct)) {
|
||||
ret = parse_threshold(tz, buf, buf_ct);
|
||||
if (ret)
|
||||
goto parse_exit;
|
||||
}
|
||||
strlcpy(buf, buf_ptr, (buf_ct + 1));
|
||||
curr_buf = next_buf = buf;
|
||||
strsep(&next_buf, "\n");
|
||||
while (curr_buf && curr_buf < buf_end) {
|
||||
if (next_buf)
|
||||
count = next_buf - curr_buf;
|
||||
else
|
||||
count = buf_end - curr_buf + 1;
|
||||
if (strnstr(curr_buf, "passive_polling_delay", count))
|
||||
ret = parse_delay(tz, curr_buf, count, false);
|
||||
else if (strnstr(curr_buf, "polling_delay", count))
|
||||
ret = parse_delay(tz, curr_buf, count, true);
|
||||
|
||||
if (ret)
|
||||
goto parse_exit;
|
||||
|
||||
curr_buf = next_buf;
|
||||
if (next_buf < buf_end)
|
||||
strsep(&next_buf, "\n");
|
||||
else
|
||||
next_buf = NULL;
|
||||
}
|
||||
parse_exit:
|
||||
mutex_unlock(&tz->lock);
|
||||
if (mode == THERMAL_DEVICE_ENABLED && tz->ops->set_mode)
|
||||
tz->ops->set_mode(tz, THERMAL_DEVICE_ENABLED);
|
||||
kfree(buf);
|
||||
return ret ? ret : buf_ct;
|
||||
}
|
||||
|
||||
static ssize_t thermal_dbgfs_config_write(struct file *file,
|
||||
const char __user *user_buf, size_t count, loff_t *ppos)
|
||||
{
|
||||
struct thermal_zone_device *tz = NULL;
|
||||
char *sensor_buf = NULL, sensor_name[THERMAL_NAME_LENGTH] = "", *buf;
|
||||
int ret = -EINVAL;
|
||||
|
||||
buf = kzalloc(sizeof(char) * (count + 1), GFP_KERNEL);
|
||||
if (!buf)
|
||||
return -ENOMEM;
|
||||
if (copy_from_user(buf, user_buf, count)) {
|
||||
ret = -EFAULT;
|
||||
goto config_exit;
|
||||
}
|
||||
sensor_buf = strnstr(buf, "sensor", count);
|
||||
if (sensor_buf) {
|
||||
if (sscanf(sensor_buf, "sensor %20[^\n\t ]",
|
||||
sensor_name) != 1) {
|
||||
pr_err("sensor name not found\n");
|
||||
ret = -EINVAL;
|
||||
goto config_exit;
|
||||
}
|
||||
tz = thermal_zone_get_zone_by_name((const char *)sensor_name);
|
||||
if (IS_ERR(tz)) {
|
||||
ret = PTR_ERR(tz);
|
||||
pr_err("No thermal zone for sensor:%s. err:%d\n",
|
||||
sensor_name, ret);
|
||||
goto config_exit;
|
||||
}
|
||||
ret = parse_config(tz, buf, count);
|
||||
}
|
||||
|
||||
config_exit:
|
||||
kfree(buf);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static const struct file_operations thermal_dbgfs_config_fops = {
|
||||
.write = thermal_dbgfs_config_write,
|
||||
};
|
||||
|
||||
int thermal_debug_init(void)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
thermal_debugfs_parent = debugfs_create_dir("thermal", NULL);
|
||||
if (IS_ERR_OR_NULL(thermal_debugfs_parent)) {
|
||||
ret = PTR_ERR(thermal_debugfs_parent);
|
||||
pr_err("Error creating thermal debugfs directory. err:%d\n",
|
||||
ret);
|
||||
return ret;
|
||||
}
|
||||
thermal_debugfs_config = debugfs_create_file("config", 0200,
|
||||
thermal_debugfs_parent, NULL,
|
||||
&thermal_dbgfs_config_fops);
|
||||
if (IS_ERR_OR_NULL(thermal_debugfs_config)) {
|
||||
ret = PTR_ERR(thermal_debugfs_config);
|
||||
pr_err("Error creating thermal config debugfs. err:%d\n",
|
||||
ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
void thermal_debug_exit(void)
|
||||
{
|
||||
debugfs_remove_recursive(thermal_debugfs_parent);
|
||||
}
|
||||
Loading…
Reference in a new issue