kernel_samsung_a34x-permissive/drivers/thermal/ss_thermal_log.c
2024-04-28 15:51:13 +02:00

120 lines
2.5 KiB
C

// SPDX-License-Identifier: GPL-2.0
/*
* ss_thermal_log.c - SAMSUNG Thermal logging.
*
*/
#include <linux/module.h>
#include <linux/device.h>
#include <linux/err.h>
#include <linux/slab.h>
#include <linux/uaccess.h>
#include <linux/thermal.h>
#include <linux/proc_fs.h>
#include <linux/time.h>
#include <linux/sched/clock.h>
/* TODO: move to general 'sec_pm_log' driver?? */
#define PROC_PM_DIR "sec_pm_log"
static struct proc_dir_entry *procfs_power_dir;
/* thermal log */
#define THERMAL_LOG_NAME "thermal_log"
#define THERM_BUF_SIZE SZ_128K
#define MAX_STR_LEN 120
static bool init_done __read_mostly;
static char thermal_log_buf[THERM_BUF_SIZE];
static unsigned int buf_pos;
static unsigned int buf_full;
void ss_thermal_print(const char *fmt, ...)
{
char buf[MAX_STR_LEN] = {0, };
unsigned int len = 0;
va_list args;
u64 time;
unsigned long nsec;
if (!init_done)
return;
/* timestamp */
time = local_clock();
nsec = do_div(time, NSEC_PER_SEC);
len = snprintf(buf, sizeof(buf), "[%6lu.%06ld] ",
(unsigned long)time, nsec / NSEC_PER_USEC);
/* append log */
va_start(args, fmt);
len += vsnprintf(buf + len, MAX_STR_LEN - len, fmt, args);
va_end(args);
/* buffer full, reset position */
if (buf_pos + len + 1 > THERM_BUF_SIZE) {
buf_pos = 0;
buf_full++;
}
buf_pos += scnprintf(&thermal_log_buf[buf_pos], len + 1, "%s", buf);
}
static ssize_t ss_thermal_log_read(struct file *file, char __user *buf,
size_t len, loff_t *offset)
{
loff_t pos = *offset;
ssize_t count = 0;
size_t size = (buf_full > 0) ? THERM_BUF_SIZE : (size_t)buf_pos;
pr_info("%s: pos(%d), full(%d), size(%d)\n", __func__,
buf_pos, buf_full, size);
if (pos >= size)
return 0;
count = min(len, size);
if ((pos + count) > size)
count = size - pos;
if (copy_to_user(buf, thermal_log_buf + pos, count))
return -EFAULT;
*offset += count;
pr_info("%s: done\n", __func__);
return count;
}
static const struct file_operations proc_ss_thermal_log_ops = {
.owner = THIS_MODULE,
.read = ss_thermal_log_read,
};
void ss_thermal_log_init(void)
{
struct proc_dir_entry *entry;
pr_info("%s\n", __func__);
procfs_power_dir = proc_mkdir(PROC_PM_DIR, NULL);
if (unlikely(!procfs_power_dir))
return;
entry = proc_create(THERMAL_LOG_NAME, 0444,
procfs_power_dir,
&proc_ss_thermal_log_ops);
if (unlikely(!entry))
return;
proc_set_size(entry, THERM_BUF_SIZE);
init_done = true;
ss_thermal_print("%s done\n", __func__);
pr_info("%s: done\n", __func__);
}