1
0
mirror of https://github.com/physwizz/a155-U-u1.git synced 2024-11-19 13:27:49 +00:00
a155-U-u1/kernel-5.10/drivers/misc/mediatek/mcupm/v2/mcupm_logger.c
2024-03-11 06:53:12 +11:00

313 lines
6.0 KiB
C

// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (c) 2020 MediaTek Inc.
*/
/* MCUPM LOGGER */
#include <linux/io.h>
#include <linux/slab.h> /* needed by kmalloc */
#include <linux/timer.h>
#include <linux/miscdevice.h> /* needed by miscdevice* */
#include <linux/mutex.h>
#include <linux/of.h>
#include <linux/poll.h>
#include "mcupm_driver.h"
#include "mcupm_ipi_id.h"
extern int mcupm_plt_ackdata;
static wait_queue_head_t logwait;
static unsigned int mcupm_logger_inited;
static struct log_ctrl_s *log_ctl;
static struct buffer_info_s *buf_info, *lbuf_info;
static struct timer_list mcupm_log_timer;
static DEFINE_MUTEX(mcupm_log_mutex);
static ssize_t mcupm_log_if_read(struct file *file, char __user *data,
size_t len, loff_t *ppos)
{
ssize_t ret;
/* pr_debug("[MCUPM] mcupm_log_if_read\n"); */
ret = 0;
if (access_ok(data, len))
ret = mcupm_log_read(data, len);
return ret;
}
static int mcupm_log_if_open(struct inode *inode, struct file *file)
{
/* pr_debug("[MCUPM] mcupm_log_if_open\n"); */
return nonseekable_open(inode, file);
}
static unsigned int mcupm_log_if_poll(struct file *file, poll_table *wait)
{
unsigned int ret = 0;
/* pr_debug("[MCUPM] mcupm_log_if_poll\n"); */
if (!(file->f_mode & FMODE_READ))
return ret;
poll_wait(file, &logwait, wait);
ret = mcupm_log_poll();
return ret;
}
void mcupm_log_if_wake(void)
{
wake_up(&logwait);
}
static inline void mcupm_log_timer_add(void)
{
if (mcupm_log_timer.expires == 0) {
mcupm_log_timer.expires = jiffies + MCUPM_TIMER_TIMEOUT;
add_timer(&mcupm_log_timer);
}
}
static void mcupm_log_timeout(struct timer_list *timer)
{
if (buf_info->r_pos != buf_info->w_pos) {
mcupm_log_if_wake();
mcupm_log_timer.expires = 0;
} else {
mcupm_log_timer_add();
}
}
ssize_t mcupm_log_read(char __user *data, size_t len)
{
unsigned long w_pos, r_pos, datalen;
char *buf, *tmp_buf;
if (!mcupm_logger_inited)
return 0;
datalen = 0;
mutex_lock(&mcupm_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("mcupm logger: copy data failed !!!\n");
kfree(tmp_buf);
} else {
pr_debug("mcupm 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(&mcupm_log_mutex);
return datalen;
}
unsigned int mcupm_log_poll(void)
{
if (!mcupm_logger_inited)
return 0;
if (buf_info->r_pos != buf_info->w_pos)
return POLLIN | POLLRDNORM;
mcupm_log_timer_add();
return 0;
}
static const struct file_operations mcupm_log_file_ops = {
.owner = THIS_MODULE,
.read = mcupm_log_if_read,
.open = mcupm_log_if_open,
.poll = mcupm_log_if_poll,
};
static struct miscdevice mcupm_log_device = {
.minor = MISC_DYNAMIC_MINOR,
.name = "mcupm",
.fops = &mcupm_log_file_ops
};
int mcupm_sysfs_create_file(struct device_attribute *attr)
{
return device_create_file(mcupm_log_device.this_device, attr);
}
int mcupm_sysfs_init(void)
{
int ret;
init_waitqueue_head(&logwait);
ret = misc_register(&mcupm_log_device);
if (unlikely(ret != 0))
return ret;
return 0;
}
static unsigned int mcupm_log_enable_set(unsigned int enable)
{
struct mcupm_ipi_data_s ipi_data;
int ret = 0;
if (mcupm_logger_inited) {
ipi_data.cmd = MCUPM_PLT_LOG_ENABLE;
ipi_data.u.logger.enable = enable ? ENABLE : DISABLE;
mcupm_plt_ackdata = -1;
ret = mtk_ipi_send_compl(&mcupm_ipidev, CH_S_PLATFORM,
IPI_SEND_WAIT, &ipi_data,
sizeof(struct mcupm_ipi_data_s) / MCUPM_MBOX_SLOT_SIZE,
2000);
if (ret) {
pr_debug("MCUPM: logger IPI fail ret=%d\n", ret);
goto error;
}
if (enable != mcupm_plt_ackdata) {
pr_debug("MCUPM: %s fail enable=%d ackdata=%d\n",
__func__, enable, mcupm_plt_ackdata);
goto error;
}
log_ctl->enable = enable;
pr_info("MCUPM: logger IPI success ret=%d, ackdata = %d\n",
ret, mcupm_plt_ackdata);
}
error:
return 0;
}
static ssize_t mcupm_mobile_log_show(struct device *kobj,
struct device_attribute *attr, char *buf)
{
unsigned int stat;
stat = (mcupm_logger_inited && log_ctl->enable) ? 1 : 0;
return snprintf(buf, PAGE_SIZE, "MCUPM mobile log is %s\n",
(stat == 0x1) ? "enabled" : "disabled");
}
static ssize_t mcupm_mobile_log_store(struct device *kobj,
struct device_attribute *attr, const char *buf, size_t n)
{
unsigned int enable = 0;
if (kstrtouint(buf, 0, &enable) != 0)
return -EINVAL;
mutex_lock(&mcupm_log_mutex);
mcupm_log_enable_set(enable);
mutex_unlock(&mcupm_log_mutex);
return n;
}
DEVICE_ATTR_RW(mcupm_mobile_log);
unsigned int mcupm_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 = MCUPM_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_debug("MCUPM:%s() initial fail, last_ofs=%u, limit=%u\n",
__func__, last_ofs, (unsigned int) limit);
goto error;
}
return last_ofs;
error:
mcupm_logger_inited = 0;
log_ctl = NULL;
buf_info = lbuf_info = NULL;
return 0;
}
int mcupm_logger_init_done(void)
{
int ret;
if (log_ctl) {
ret = mcupm_sysfs_create_file(&dev_attr_mcupm_mobile_log);
if (unlikely(ret != 0))
return ret;
timer_setup(&mcupm_log_timer, &mcupm_log_timeout, 0);
mcupm_log_timer.expires = 0;
mcupm_logger_inited = 1;
}
return 0;
}