Loading drivers/thermal/Makefile +3 −0 Original line number Diff line number Diff line Loading @@ -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 Loading drivers/thermal/of-thermal.c +11 −0 Original line number Diff line number Diff line Loading @@ -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; Loading drivers/thermal/thermal_core.c +2 −0 Original line number Diff line number Diff line Loading @@ -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; Loading @@ -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); Loading drivers/thermal/thermal_core.h +8 −0 Original line number Diff line number Diff line Loading @@ -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, Loading @@ -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__ */ drivers/thermal/thermal_debugfs.c 0 → 100644 +394 −0 Original line number Diff line number Diff line // 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
drivers/thermal/Makefile +3 −0 Original line number Diff line number Diff line Loading @@ -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 Loading
drivers/thermal/of-thermal.c +11 −0 Original line number Diff line number Diff line Loading @@ -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; Loading
drivers/thermal/thermal_core.c +2 −0 Original line number Diff line number Diff line Loading @@ -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; Loading @@ -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); Loading
drivers/thermal/thermal_core.h +8 −0 Original line number Diff line number Diff line Loading @@ -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, Loading @@ -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__ */
drivers/thermal/thermal_debugfs.c 0 → 100644 +394 −0 Original line number Diff line number Diff line // 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); }