kernel_samsung_a34x-permissive/drivers/misc/mediatek/sspm/sspm_logger_impl.c

257 lines
5.4 KiB
C
Raw Normal View History

// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (c) 2019 MediaTek Inc.
*/
#include <linux/types.h>
#include <linux/module.h> /* needed by all modules */
#include <linux/init.h> /* needed by module macros */
#include <linux/fs.h> /* needed by file_operations* */
#include <linux/device.h> /* needed by device_* */
#include <linux/vmalloc.h> /* needed by kmalloc */
#include <linux/uaccess.h> /* needed by copy_to_user */
#include <linux/fs.h> /* needed by file_operations* */
#include <linux/slab.h> /* needed by kmalloc */
#include <linux/poll.h> /* needed by poll */
#include <linux/sched.h>
#include <linux/interrupt.h>
#include <linux/suspend.h>
#include <linux/timer.h>
#include <linux/ioport.h>
#include <linux/io.h>
#include <linux/types.h>
#include <linux/atomic.h>
#include "sspm_define.h"
#include "sspm_ipi.h"
#include "sspm_sysfs.h"
#include "sspm_logger.h"
#ifdef SSPM_PLT_LOGGER_BUF_LEN
/* use platform-defined buffer length */
#define BUF_LEN SSPM_PLT_LOGGER_BUF_LEN
#else
/* otherwise use default buffer length */
#define BUF_LEN (1 * 1024 * 1024)
#endif
#define LBUF_LEN (4 * 1024)
#define SSPM_TIMER_TIMEOUT (1 * HZ) /* 1 seconds*/
struct log_ctrl_s {
unsigned int base;
unsigned int size;
unsigned int enable;
unsigned int info_ofs;
unsigned int buff_ofs;
unsigned int buff_size;
};
struct buffer_info_s {
unsigned int r_pos;
unsigned int w_pos;
};
static unsigned int sspm_logger_inited;
static struct log_ctrl_s *log_ctl;
static struct buffer_info_s *buf_info, *lbuf_info;
static struct timer_list sspm_log_timer;
static DEFINE_MUTEX(sspm_log_mutex);
static inline void sspm_log_timer_add(void)
{
if (sspm_log_timer.expires == 0) {
sspm_log_timer.expires = jiffies + SSPM_TIMER_TIMEOUT;
add_timer(&sspm_log_timer);
}
}
static void sspm_log_timeout(struct timer_list *unused)
{
if (buf_info->r_pos != buf_info->w_pos) {
sspm_log_if_wake();
sspm_log_timer.expires = 0;
} else {
sspm_log_timer_add();
}
}
ssize_t sspm_log_read(char __user *data, size_t len)
{
unsigned long w_pos, r_pos, datalen;
char *buf, *tmp_buf;
if (!sspm_logger_inited)
return 0;
datalen = 0;
mutex_lock(&sspm_log_mutex);
r_pos = buf_info->r_pos;
w_pos = buf_info->w_pos;
if (r_pos == w_pos)
goto error;
if (r_pos > w_pos)
datalen = BUF_LEN - r_pos; /* not wrap */
else
datalen = w_pos - r_pos;
if (datalen > len)
datalen = len;
buf = ((char *) log_ctl) + log_ctl->buff_ofs + r_pos;
tmp_buf = kmalloc((size_t)len, GFP_KERNEL);
len = datalen;
if (tmp_buf) {
memcpy_fromio(tmp_buf, buf, len);
if (copy_to_user(data, tmp_buf, len))
pr_debug("sspm logger: copy data failed !!!\n");
kfree(tmp_buf);
} else {
pr_debug("sspm logger: create log buffer failed !!!\n");
goto error;
}
r_pos += datalen;
if (r_pos >= BUF_LEN)
r_pos -= BUF_LEN;
buf_info->r_pos = r_pos;
error:
mutex_unlock(&sspm_log_mutex);
return datalen;
}
unsigned int sspm_log_poll(void)
{
if (!sspm_logger_inited)
return 0;
if (buf_info->r_pos != buf_info->w_pos)
return POLLIN | POLLRDNORM;
sspm_log_timer_add();
return 0;
}
static unsigned int sspm_log_enable_set(unsigned int enable)
{
struct plt_ipi_data_s ipi_data;
int ret, ackdata;
if (sspm_logger_inited) {
ipi_data.cmd = PLT_LOG_ENABLE;
ipi_data.u.logger.enable = enable;
ret = sspm_ipi_send_sync(IPI_ID_PLATFORM, IPI_OPT_WAIT,
&ipi_data, sizeof(ipi_data) / MBOX_SLOT_SIZE, &ackdata, 1);
if (ret != 0) {
pr_err("SSPM: logger IPI fail ret=%d\n", ret);
goto error;
}
if (enable != ackdata) {
pr_err("SSPM: %s fail ret=%d\n", __func__, ackdata);
goto error;
}
log_ctl->enable = enable;
}
error:
return 0;
}
static ssize_t sspm_mobile_log_show(struct device *kobj,
struct device_attribute *attr, char *buf)
{
unsigned int stat;
stat = (sspm_logger_inited && log_ctl->enable) ? 1 : 0;
return snprintf(buf, PAGE_SIZE, "SSPM mobile log is %s\n",
(stat == 0x1) ? "enabled" : "disabled");
}
static ssize_t sspm_mobile_log_store(struct device *kobj,
struct device_attribute *attr, const char *buf, size_t n)
{
unsigned int enable;
if (kstrtouint(buf, 0, &enable) != 0)
return -EINVAL;
mutex_lock(&sspm_log_mutex);
sspm_log_enable_set(enable);
mutex_unlock(&sspm_log_mutex);
return n;
}
static DEVICE_ATTR_RW(sspm_mobile_log);
unsigned int __init sspm_logger_init(phys_addr_t start, phys_addr_t limit)
{
unsigned int last_ofs;
last_ofs = 0;
log_ctl = (struct log_ctrl_s *)(uintptr_t) start;
log_ctl->base = PLT_LOG_ENABLE; /* magic */
log_ctl->enable = 0;
log_ctl->size = sizeof(*log_ctl);
last_ofs += log_ctl->size;
log_ctl->info_ofs = last_ofs;
last_ofs += sizeof(*buf_info);
last_ofs = roundup(last_ofs, 4);
log_ctl->buff_ofs = last_ofs;
log_ctl->buff_size = BUF_LEN;
buf_info = (struct buffer_info_s *) (((unsigned char *) log_ctl) +
log_ctl->info_ofs);
buf_info->r_pos = 0;
buf_info->w_pos = 0;
last_ofs += log_ctl->buff_size;
if (last_ofs >= limit) {
pr_err("SSPM:%s() initial fail, last_ofs=%u, limit=%u\n",
__func__, last_ofs, (unsigned int) limit);
goto error;
}
return last_ofs;
error:
sspm_logger_inited = 0;
log_ctl = NULL;
buf_info = lbuf_info = NULL;
return 0;
}
int __init sspm_logger_init_done(void)
{
int ret;
if (log_ctl) {
ret = sspm_sysfs_create_file(&dev_attr_sspm_mobile_log);
if (ret)
return ret;
timer_setup(&sspm_log_timer, &sspm_log_timeout, 0);
sspm_log_timer.expires = 0;
sspm_logger_inited = 1;
}
return 0;
}