Files
android_kernel_samsung_sm8750/drivers/thermal/qcom/thermal_config.c
2025-08-12 22:16:57 +02:00

342 lines
9.0 KiB
C
Executable File

// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (c) 2021-2024, Qualcomm Innovation Center, Inc. All rights reserved.
*/
#define pr_fmt(fmt) "%s:%s " fmt, KBUILD_MODNAME, __func__
#include <linux/debugfs.h>
#include <linux/uaccess.h>
#include <linux/device.h>
#include <linux/module.h>
#include <linux/err.h>
#include <linux/slab.h>
#include <linux/string.h>
#include "../thermal_core.h"
static struct dentry *thermal_debugfs_parent;
static struct dentry *thermal_debugfs_config;
#define UINT_MAX_CHARACTER 11
static char tzone_sensor_name[THERMAL_NAME_LENGTH] = "";
int buffer_overflow_check(char *buf, int offset, int buf_size)
{
if ((buf == NULL) || (offset >= buf_size) || (strlen(buf) >= buf_size))
return -EINVAL;
return 0;
}
static int fetch_and_populate_trip_data(char *buf, struct thermal_zone_device *tz,
int idx, int offset, size_t size, bool is_hyst)
{
int trip_temp;
if (!is_hyst)
trip_temp = tz->trips[idx].temperature;
else
trip_temp = tz->trips[idx].hysteresis;
offset += scnprintf(buf + offset, size - offset, "%d ", trip_temp);
return offset;
}
static int fetch_and_populate_trips(char *config_buf, struct thermal_zone_device *tz,
int offset)
{
size_t buf_size = 0;
int i = 0, ret = 0;
int buf1_offset = 0, buf2_offset = 0;
char *buf_temp = NULL, *buf_hyst = NULL;
buf_size = tz->num_trips * UINT_MAX_CHARACTER;
buf_temp = kzalloc(buf_size, GFP_KERNEL);
buf_hyst = kzalloc(buf_size, GFP_KERNEL);
if (!buf_hyst || !buf_temp) {
kfree(buf_temp);
kfree(buf_hyst);
return -ENOMEM;
}
for (i = 0; i < tz->num_trips; i++) {
ret = fetch_and_populate_trip_data(buf_temp, tz, i, buf1_offset,
buf_size, false);
if (ret < 0)
goto config_exit;
buf1_offset = ret;
if (!tz->trips)
continue;
ret = fetch_and_populate_trip_data(buf_hyst, tz, i, buf2_offset,
buf_size, true);
if (ret < 0)
goto config_exit;
buf2_offset = ret;
}
ret = buffer_overflow_check(buf_temp, offset, PAGE_SIZE-offset);
if (ret)
goto config_exit;
offset += scnprintf(config_buf + offset, PAGE_SIZE - offset,
"%*s%s\n", -15, "set_temp", buf_temp);
if (buf2_offset) {
ret = buffer_overflow_check(buf_hyst, offset, PAGE_SIZE-offset);
if (ret)
goto config_exit;
offset += scnprintf(config_buf + offset, PAGE_SIZE - offset,
"%*s%s\n", -15, "clr_temp", buf_hyst);
}
config_exit:
kfree(buf_temp);
kfree(buf_hyst);
return (ret < 0) ? ret : offset;
}
static int fetch_and_populate_cdevs(char *config_buf, struct thermal_zone_device *tz,
int offset)
{
int ret = 0, i = 0;
int buf_size = 0, buf_offset = 0, buf1_offset = 0, buf2_offset = 0;
char *buf_cdev = NULL, *buf_cdev_upper = NULL, *buf_cdev_lower = NULL;
struct thermal_instance *instance;
mutex_lock(&tz->lock);
list_for_each_entry(instance, &tz->thermal_instances, tz_node) {
if (instance->cdev)
buf_size++;
}
if (!buf_size) {
mutex_unlock(&tz->lock);
return offset;
}
buf_size = (buf_size + tz->num_trips) * THERMAL_NAME_LENGTH;
buf_cdev = kzalloc(buf_size, GFP_KERNEL);
buf_cdev_upper = kzalloc(buf_size, GFP_KERNEL);
buf_cdev_lower = kzalloc(buf_size, GFP_KERNEL);
if (!buf_cdev || !buf_cdev_upper || !buf_cdev_lower) {
mutex_unlock(&tz->lock);
kfree(buf_cdev);
kfree(buf_cdev_upper);
kfree(buf_cdev_lower);
return -ENOMEM;
}
for (i = 0; i < tz->num_trips; i++) {
bool first_entry = true;
bool no_cdevs = true;
list_for_each_entry(instance, &tz->thermal_instances, tz_node) {
if (!instance->cdev || instance->trip != &tz->trips[i])
continue;
no_cdevs = false;
if (first_entry) {
first_entry = false;
buf_offset += scnprintf(
buf_cdev + buf_offset,
buf_size - buf_offset,
" %s", instance->cdev->type);
buf1_offset += scnprintf(
buf_cdev_upper + buf1_offset,
buf_size - buf1_offset,
" %ld", instance->upper);
buf2_offset += scnprintf(
buf_cdev_lower + buf2_offset,
buf_size - buf2_offset,
" %ld", instance->lower);
} else {
buf_offset += scnprintf(
buf_cdev + buf_offset,
buf_size - buf_offset,
"+%s", instance->cdev->type);
buf1_offset += scnprintf(
buf_cdev_upper + buf1_offset,
buf_size - buf1_offset,
"+%ld", instance->upper);
buf2_offset += scnprintf(
buf_cdev_lower + buf2_offset,
buf_size - buf2_offset,
"+%ld", instance->lower);
}
}
if (no_cdevs) {
buf_offset += scnprintf(
buf_cdev + buf_offset,
buf_size - buf_offset,
" %s", "-");
buf1_offset += scnprintf(
buf_cdev_upper + buf1_offset,
buf_size - buf1_offset,
" %s", "-");
buf2_offset += scnprintf(
buf_cdev_lower + buf2_offset,
buf_size - buf2_offset,
" %s", "-");
}
}
mutex_unlock(&tz->lock);
ret = buffer_overflow_check(buf_cdev, offset, PAGE_SIZE - offset);
if (ret)
goto config_exit;
offset += scnprintf(config_buf + offset, PAGE_SIZE - offset,
"%*s%s\n", -14, "device", buf_cdev);
ret = buffer_overflow_check(buf_cdev_upper, offset, PAGE_SIZE-offset);
if (ret)
goto config_exit;
offset += scnprintf(config_buf + offset, PAGE_SIZE - offset,
"%*s%s\n", -14, "upper_limit", buf_cdev_upper);
ret = buffer_overflow_check(buf_cdev_lower, offset, PAGE_SIZE-offset);
if (ret)
goto config_exit;
offset += scnprintf(config_buf + offset, PAGE_SIZE - offset,
"%*s%s\n", -14, "lower_limit", buf_cdev_lower);
config_exit:
kfree(buf_cdev);
kfree(buf_cdev_upper);
kfree(buf_cdev_lower);
return (ret < 0) ? ret : offset;
}
ssize_t thermal_dbgfs_config_read(struct file *file, char __user *buf,
size_t count, loff_t *ppos)
{
struct thermal_zone_device *tz = NULL;
int offset = 0, buf_count = 0, ret;
char *config_buf = NULL;
tz = thermal_zone_get_zone_by_name((const char *)tzone_sensor_name);
if (IS_ERR(tz)) {
ret = PTR_ERR(tz);
pr_err("No thermal zone for sensor:%s. err:%d\n",
tzone_sensor_name, ret);
return ret;
}
config_buf = kzalloc(sizeof(char) * PAGE_SIZE, GFP_KERNEL);
if (!config_buf)
return -ENOMEM;
offset += scnprintf(config_buf + offset, PAGE_SIZE - offset, "%*s%s\n",
-15, "sensor", tz->type);
offset += scnprintf(config_buf + offset, PAGE_SIZE - offset, "%*s%s\n",
-15, "algo_type", tz->governor->name);
offset += scnprintf(config_buf + offset, PAGE_SIZE - offset, "%*s%s\n",
-15, "mode",
(tz->mode == THERMAL_DEVICE_DISABLED) ? "disabled" : "enabled");
offset += scnprintf(config_buf + offset, PAGE_SIZE - offset, "%*s%d\n",
-15, "polling_delay",
jiffies_to_msecs(tz->polling_delay_jiffies));
offset += scnprintf(config_buf + offset, PAGE_SIZE - offset, "%*s%d\n",
-15, "passive_delay",
jiffies_to_msecs(tz->passive_delay_jiffies));
if (!tz->num_trips || !tz->trips) {
if (offset >= PAGE_SIZE) {
pr_err("%s sensor config rule length is more than buffer size\n",
tz->type);
ret = -ENOMEM;
goto config_exit;
}
config_buf[offset] = '\0';
ret = simple_read_from_buffer(buf, count, ppos, config_buf, offset);
kfree(config_buf);
return ret;
}
ret = fetch_and_populate_trips(config_buf, tz, offset);
if (ret < 0)
goto config_exit;
offset = ret;
ret = fetch_and_populate_cdevs(config_buf, tz, offset);
if (ret < 0)
goto config_exit;
offset = ret;
if (offset >= PAGE_SIZE) {
pr_err("%s sensor config rule length is more than buffer size\n",
tz->type);
ret = -ENOMEM;
goto config_exit;
}
config_buf[offset] = '\0';
buf_count = simple_read_from_buffer(buf, count, ppos, config_buf, offset);
config_exit:
kfree(config_buf);
return (ret < 0) ? ret : buf_count;
}
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_name[THERMAL_NAME_LENGTH] = "";
if (!count || (count > THERMAL_NAME_LENGTH))
return -EINVAL;
if (copy_from_user(sensor_name, user_buf, count))
return -EFAULT;
if (sscanf(sensor_name, "%19[^\n\t ]", tzone_sensor_name) != 1)
return -EINVAL;
tz = thermal_zone_get_zone_by_name((const char *)tzone_sensor_name);
if (IS_ERR(tz)) {
pr_err("No thermal zone for sensor:%s. err:%ld\n",
tzone_sensor_name, PTR_ERR(tz));
return PTR_ERR(tz);
}
return count;
}
static const struct file_operations thermal_dbgfs_config_fops = {
.write = thermal_dbgfs_config_write,
.read = thermal_dbgfs_config_read,
};
static int thermal_config_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", 0600,
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;
}
static void thermal_config_exit(void)
{
debugfs_remove_recursive(thermal_debugfs_parent);
}
module_init(thermal_config_init);
module_exit(thermal_config_exit);
MODULE_DESCRIPTION("Thermal Zone config debug driver");
MODULE_LICENSE("GPL");