1
0
mirror of https://github.com/physwizz/a155-U-u1.git synced 2025-09-26 19:04:54 +00:00
Files
physwizz 99537be4e2 first
2024-03-11 06:53:12 +11:00

1083 lines
24 KiB
C

// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (c) 2020 MediaTek Inc.
*/
#include <linux/platform_device.h>
#include <linux/dma-mapping.h>
#include <asm/mman.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/of_irq.h>
#include <linux/of_platform.h>
#include <linux/interrupt.h>
#include <linux/syscalls.h>
#include <linux/cdev.h>
#include <linux/kthread.h>
#include <linux/fs.h>
#include <linux/sysinfo.h>
#include <linux/seq_file.h>
#include <linux/timer.h>
#include <linux/delay.h>
#include <linux/iommu.h>
#include <linux/debugfs.h>
#include <linux/sysfs.h>
#include <linux/proc_fs.h>
#include <linux/spinlock.h>
#include <linux/sched/signal.h>
#include <mt-plat/mrdump.h>
#include "apusys_core.h"
#include "sw_logger.h"
#define SW_LOGGER_DEV_NAME "apu_sw_logger"
#define BYPASS_IOMMU (0)
DEFINE_SPINLOCK(sw_logger_spinlock);
static u8 g_sw_logger_log_lv;
static struct proc_dir_entry *log_root;
static struct proc_dir_entry *log_devinfo;
static struct proc_dir_entry *log_devattr;
static struct proc_dir_entry *log_seqlog;
static struct proc_dir_entry *log_seqlogL;
static struct sw_logger_seq_data *pSeqData;
static bool startl_first_enter_session;
static struct device *sw_logger_dev;
static struct kobject *root_dir;
static dma_addr_t handle;
static char *sw_log_buf;
static wait_queue_head_t apusys_swlog_wait;
static unsigned int g_log_r_ptr;
static unsigned int g_log_l_r_ptr;
static void *apu_mbox;
static struct mtk_apu *g_apu;
struct sw_log_level_data {
unsigned int level;
};
static struct sw_log_level_data sw_ipi_loglv_data = {IPI_DEBUG_LEVEL};
#define APU_MBOX_BASE (0x19000000)
#define LOG_W_PTR (apu_mbox + 0x40)
#define LOG_R_PTR (apu_mbox + 0x44)
#define LOG_OV_FLG (apu_mbox + 0x4c)
struct sw_logger_seq_data {
uint32_t w_ptr;
uint32_t r_ptr;
uint32_t overflow_flg;
int i;
int is_finished;
char *data;
};
static struct sw_logger_seq_data pSeqData_lock_obj;
/* debug log */
#define PROC_WRITE_BUFSIZE 16
#define CLEAR_LOG_CMD "clear"
static int apusys_debug_dump(struct seq_file *s, void *unused)
{
DBG_LOG_CON(s, "%d\n", g_sw_logger_log_lv);
return 0;
}
static int sw_logger_open(struct inode *inode, struct file *file)
{
return single_open(file, apusys_debug_dump, inode->i_private);
}
static ssize_t show_debuglv(struct file *filp, char __user *buffer,
size_t count, loff_t *ppos)
{
char buf[512];
unsigned int len = 0;
unsigned long flags;
if (sw_log_buf) {
len += scnprintf(buf + len, sizeof(buf) - len,
"uP_sw_logger_log_lv = 0x%x:\n",
sw_ipi_loglv_data.level);
spin_lock_irqsave(&sw_logger_spinlock, flags);
len += scnprintf(buf + len, sizeof(buf) - len,
"w_ptr = %d,r_ptr = %d overflow_flg = %d\n",
ioread32(LOG_W_PTR), ioread32(LOG_R_PTR),
ioread32(LOG_OV_FLG));
spin_unlock_irqrestore(&sw_logger_spinlock, flags);
}
return simple_read_from_buffer(buffer, count, ppos, buf, len);
}
static void sw_logger_buf_invalidate(void)
{
if (!BYPASS_IOMMU)
if (sw_logger_dev)
dma_sync_single_for_cpu(
sw_logger_dev, handle, APU_LOG_SIZE, DMA_FROM_DEVICE);
}
static int sw_logger_buf_alloc(struct device *dev)
{
int ret;
if (!BYPASS_IOMMU) {
ret = dma_set_mask_and_coherent(dev, DMA_BIT_MASK(34));
if (ret) {
dev_info(sw_logger_dev, "%s: dma_set_coherent_mask fail(%d)\n",
__func__, ret);
return -ENOMEM;
}
}
if (!BYPASS_IOMMU) {
sw_log_buf = kmalloc(APU_LOG_SIZE, GFP_KERNEL);
dev_info(sw_logger_dev, "%s: sw_log_buf = 0x%llx\n",
__func__, (uint64_t) sw_log_buf);
if (!sw_log_buf)
return -ENOMEM;
memset(sw_log_buf, 0, APU_LOG_SIZE);
handle = dma_map_single(dev, sw_log_buf, APU_LOG_SIZE,
DMA_FROM_DEVICE);
dev_info(sw_logger_dev, "handle = 0x%llx\n", handle);
if (dma_mapping_error(dev, handle) != 0) {
dev_info(sw_logger_dev, "%s: dma_map_single fail\n", __func__);
kfree(sw_log_buf);
sw_log_buf = NULL;
return -ENOMEM;
}
} else {
sw_log_buf = dma_alloc_coherent(dev, APU_LOG_SIZE,
&handle, GFP_KERNEL);
if (sw_log_buf == NULL || handle == 0) {
dev_info(sw_logger_dev, "%s: dma_alloc_coherent fail\n", __func__);
return -ENOMEM;
}
memset(sw_log_buf, 0, APU_LOG_SIZE);
dev_info(sw_logger_dev, "%s: sw_log_buf = 0x%llx, handle = 0x%llx\n",
__func__, (uint64_t) sw_log_buf, (uint64_t) handle);
}
return 0;
}
static void apu_sw_log_level_ipi_handler(void *data, unsigned int len, void *priv)
{
unsigned int log_level = *(unsigned int *)data;
LOGGER_INFO("log_level = 0x%x (%d)\n", log_level, len);
}
int sw_logger_config_init(struct mtk_apu *apu)
{
int ret;
unsigned long flags;
struct logger_init_info *st_logger_init_info;
if (!apu) {
LOGGER_ERR("invalid argument: apu\n");
return -EINVAL;
}
if (!(apu->conf_buf)) {
LOGGER_ERR("invalid argument: apu->conf_buf\n");
return -EINVAL;
}
/* sw logger not enabled */
if (!apu_mbox)
return 0;
if (!sw_log_buf) {
ret = sw_logger_buf_alloc(sw_logger_dev);
if (ret) {
LOGGER_ERR("%s: sw_logger_buf_alloc fail\n", __func__);
return ret;
}
(void)mrdump_mini_add_extra_file(
(unsigned long)sw_log_buf,
__pa_nodebug(sw_log_buf),
APU_LOG_SIZE, "APUSYS_RV_SW_LOG");
}
spin_lock_irqsave(&sw_logger_spinlock, flags);
/* fixme: if reset ptrs necessary for each power on? */
iowrite32(0, LOG_W_PTR);
iowrite32(0, LOG_R_PTR);
iowrite32(0, LOG_OV_FLG);
g_log_r_ptr = U32_MAX;
g_log_l_r_ptr = U32_MAX;
spin_unlock_irqrestore(&sw_logger_spinlock, flags);
st_logger_init_info = (struct logger_init_info *)
get_apu_config_user_ptr(apu->conf_buf, eLOGGER_INIT_INFO);
st_logger_init_info->iova = handle;
LOGGER_INFO("%s: set st_logger_init_info.iova = 0x%x\n",
__func__, st_logger_init_info->iova);
return 0;
}
EXPORT_SYMBOL(sw_logger_config_init);
int sw_logger_ipi_init(struct mtk_apu *apu)
{
int ret = 0;
/* do nothing if not probed */
if (!apu_mbox)
return 0;
g_apu = apu;
ret = apu_ipi_register(g_apu, APU_IPI_LOG_LEVEL,
apu_sw_log_level_ipi_handler, NULL);
if (ret)
LOGGER_ERR("Fail in apu_sw_log_level_ipi_init\n");
return 0;
}
void sw_logger_ipi_remove(struct mtk_apu *apu)
{
apu_ipi_unregister(apu, APU_IPI_LOG_LEVEL);
}
static ssize_t set_debuglv(struct file *flip,
const char __user *buffer,
size_t count, loff_t *f_pos)
{
char *tmp;
int ret;
unsigned int input = 0;
tmp = kzalloc(count + 1, GFP_KERNEL);
if (!tmp)
return -ENOMEM;
ret = copy_from_user(tmp, buffer, count);
if (ret) {
LOGGER_ERR("copy_from_user failed (%d)\n", ret);
goto out;
}
tmp[count] = '\0';
ret = kstrtouint(tmp, 0, &input);
if (ret) {
LOGGER_ERR("kstrtouint failed (%d)\n", ret);
goto out;
}
LOGGER_INFO("set uP debug lv = 0x%x\n", input);
sw_ipi_loglv_data.level = input;
ret = apu_ipi_send(g_apu, APU_IPI_LOG_LEVEL,
&sw_ipi_loglv_data, sizeof(sw_ipi_loglv_data), 1000);
if (ret)
LOGGER_ERR("Failed for sw_logger log level send.\n");
out:
kfree(tmp);
return count;
}
static const struct proc_ops apusys_debug_fops = {
.proc_open = sw_logger_open,
.proc_read = show_debuglv,
.proc_write = set_debuglv,
.proc_lseek = seq_lseek,
.proc_release = seq_release,
};
static ssize_t show_debugAttr(struct file *filp, char __user *buffer,
size_t count, loff_t *ppos)
{
char buf[512];
unsigned int len = 0;
unsigned long flags;
if (sw_log_buf) {
len += scnprintf(buf + len, sizeof(buf) - len,
"sw_log_buf = 0x%llx\n",
sw_log_buf);
spin_lock_irqsave(&sw_logger_spinlock, flags);
len += scnprintf(buf + len, sizeof(buf) - len,
"w_ptr = %d\n",
ioread32(LOG_W_PTR));
len += scnprintf(buf + len, sizeof(buf) - len,
"r_ptr = %d\n",
ioread32(LOG_R_PTR));
len += scnprintf(buf + len, sizeof(buf) - len,
"overflow_flg = %d\n",
ioread32(LOG_OV_FLG));
spin_unlock_irqrestore(&sw_logger_spinlock, flags);
len += scnprintf(buf + len, sizeof(buf) - len,
"g_sw_logger_log_lv = %d:\n",
g_sw_logger_log_lv);
}
return simple_read_from_buffer(buffer, count, ppos, buf, len);
}
static ssize_t set_debugAttr(struct file *flip,
const char __user *buffer,
size_t count, loff_t *f_pos)
{
char *tmp;
int ret;
unsigned int input = 0;
tmp = kzalloc(count + 1, GFP_KERNEL);
if (!tmp)
return -ENOMEM;
ret = copy_from_user(tmp, buffer, count);
if (ret) {
LOGGER_ERR("copy_from_user failed (%d)\n", ret);
goto out;
}
tmp[count] = '\0';
ret = kstrtouint(tmp, 10, &input);
if (ret) {
LOGGER_ERR("kstrtouint failed (%d)\n", ret);
goto out;
}
LOGGER_INFO("set debug lv = %d\n", input);
if (input <= DEBUG_LOG_DEBUG)
g_sw_logger_log_lv = input;
out:
kfree(tmp);
return count;
}
static const struct proc_ops sw_logger_attr_fops = {
.proc_open = sw_logger_open,
.proc_read = show_debugAttr,
.proc_write = set_debugAttr,
.proc_lseek = seq_lseek,
.proc_release = seq_release,
};
/**
* seq_start() takes a position as an argument and returns an iterator which
* will start reading at that position.
* start->show->next->show...->next->show->next->stop->start->stop
*/
static void *seq_start(struct seq_file *s, loff_t *pos)
{
uint32_t w_ptr, r_ptr, overflow_flg;
unsigned long flags;
if (sw_log_buf == NULL) {
LOGGER_ERR("%s: sw_log_buf == NULL\n", __func__);
return NULL;
}
spin_lock_irqsave(&sw_logger_spinlock, flags);
w_ptr = ioread32(LOG_W_PTR);
if (g_log_r_ptr == U32_MAX) {
/*
* We don't read from the, r_ptr = ioread32(LOG_R_PTR);
* Just move r_ptr next to w_ptr, force dump full log
*/
r_ptr = (w_ptr + LOG_LINE_MAX_LENS) % APU_LOG_SIZE;
} else {
r_ptr = g_log_r_ptr;
}
overflow_flg = ioread32(LOG_OV_FLG);
spin_unlock_irqrestore(&sw_logger_spinlock, flags);
sw_logger_buf_invalidate();
LOGGER_INFO("w_ptr = %d, r_ptr = %d, overflow_flg = %d\n",
w_ptr, r_ptr, overflow_flg);
if (w_ptr == r_ptr && overflow_flg == 0) {
g_log_r_ptr = U32_MAX;
return NULL;
}
if (pSeqData == NULL) {
pSeqData = kzalloc(sizeof(struct sw_logger_seq_data),
GFP_KERNEL);
if (pSeqData != NULL) {
pSeqData->w_ptr = w_ptr;
pSeqData->r_ptr = r_ptr;
pSeqData->overflow_flg = overflow_flg;
if (overflow_flg == 0)
pSeqData->i = r_ptr;
else
pSeqData->i = w_ptr;
pSeqData->is_finished = 0;
}
}
LOGGER_INFO("%s v = 0x%x\n", __func__, pSeqData);
return pSeqData;
}
/**
* seq_start() takes a position as an argument and returns an iterator which
* will start reading at that position.
*/
static void *seq_startl(struct seq_file *s, loff_t *pos)
{
uint32_t w_ptr, r_ptr, overflow_flg;
struct sw_logger_seq_data *pSeqData_lock = &pSeqData_lock_obj;
unsigned long flags;
bool nonblock = false;
if (sw_log_buf == NULL)
return NULL;
if (s->file &&
s->file->f_flags & O_NONBLOCK)
nonblock = true;
spin_lock_irqsave(&sw_logger_spinlock, flags);
w_ptr = ioread32(LOG_W_PTR);
/* mobile logger */
if (nonblock) {
r_ptr = ioread32(LOG_R_PTR);
overflow_flg = ioread32(LOG_OV_FLG);
} else {
/* cat */
if (g_log_l_r_ptr == U32_MAX)
r_ptr = (w_ptr + LOG_LINE_MAX_LENS) % APU_LOG_SIZE;
else
r_ptr = g_log_l_r_ptr;
overflow_flg = 0;
}
spin_unlock_irqrestore(&sw_logger_spinlock, flags);
sw_logger_buf_invalidate();
LOGGER_INFO("w_ptr = %d, r_ptr = %d, overflow_flg = %d\n",
w_ptr, r_ptr, overflow_flg);
/* for ctrl-c to force exit the loop */
while (!signal_pending(current) && w_ptr == r_ptr) {
/* return for mobile logger if nothing to read */
if (w_ptr == r_ptr && nonblock)
return NULL;
usleep_range(10000, 12000);
spin_lock_irqsave(&sw_logger_spinlock, flags);
w_ptr = ioread32(LOG_W_PTR);
/* mobile logger */
if (nonblock) {
r_ptr = ioread32(LOG_R_PTR);
overflow_flg = ioread32(LOG_OV_FLG);
}
spin_unlock_irqrestore(&sw_logger_spinlock, flags);
sw_logger_buf_invalidate();
pSeqData_lock->w_ptr = w_ptr;
pSeqData_lock->r_ptr = r_ptr;
pSeqData_lock->overflow_flg = overflow_flg;
pSeqData_lock->i = r_ptr;
}
if (startl_first_enter_session ||
pSeqData_lock->i == pSeqData_lock->w_ptr) {
startl_first_enter_session = false;
pSeqData_lock->w_ptr = w_ptr;
pSeqData_lock->r_ptr = r_ptr;
pSeqData_lock->overflow_flg = overflow_flg;
pSeqData_lock->i = r_ptr;
}
if (signal_pending(current)) {
startl_first_enter_session = true;
g_log_l_r_ptr = U32_MAX;
return NULL;
}
LOGGER_INFO("%s v = 0x%x\n", __func__, pSeqData_lock);
return pSeqData_lock;
}
/**
* move the iterator forward to the next position in the sequence
*/
static void *seq_next(struct seq_file *s, void *v, loff_t *pos)
{
struct sw_logger_seq_data *pSData = v;
if (pSData == NULL) {
LOGGER_ERR("%s: pSData == NULL\n", __func__);
return NULL;
}
LOGGER_INFO(
"%s in, w_ptr = %d, r_ptr = %d,i = %d, overflow_flg = %d\n",
__func__, pSData->w_ptr, pSData->r_ptr, pSData->i,
pSData->overflow_flg);
pSData->i = (pSData->i + LOG_LINE_MAX_LENS) % APU_LOG_SIZE;
g_log_r_ptr = pSData->i;
/* prevent kernel warning */
*pos = pSData->i;
if (pSData->i != pSData->w_ptr)
return v;
pSData->is_finished = 1;
return NULL;
}
/**
* move the iterator forward to the next position in the sequence
*/
static void *seq_next_lock(struct seq_file *s, void *v, loff_t *pos)
{
struct sw_logger_seq_data *pSData = v;
bool nonblock = false;
if (pSData == NULL) {
LOGGER_ERR("%s: pSData == NULL\n", __func__);
return NULL;
}
if (s->file &&
s->file->f_flags & O_NONBLOCK)
nonblock = true;
LOGGER_INFO(
"%s in, w_ptr = %d, r_ptr = %d, i = %d, overflow_flg = %d\n",
__func__, pSData->w_ptr, pSData->r_ptr, pSData->i,
pSData->overflow_flg);
pSData->i = (pSData->i + LOG_LINE_MAX_LENS) % APU_LOG_SIZE;
/* cat */
if (!nonblock)
g_log_l_r_ptr = pSData->i;
/* prevent kernel warning */
*pos = pSData->i;
if (pSData->i != pSData->w_ptr)
return v;
/* mobile logger */
if (nonblock)
iowrite32(pSData->i, LOG_R_PTR);
return NULL;
}
/**
* stop() is called when iteration is complete (clean up)
*/
static void seq_stop(struct seq_file *s, void *v)
{
unsigned long flags;
LOGGER_INFO("%s v = 0x%x\n", __func__, v);
if (pSeqData != NULL) {
if (pSeqData->is_finished == 1) {
spin_lock_irqsave(&sw_logger_spinlock, flags);
iowrite32(pSeqData->i, LOG_R_PTR);
/* fixme: assume next overflow won't happen
* until next seq_start
*/
iowrite32(0, LOG_OV_FLG);
spin_unlock_irqrestore(&sw_logger_spinlock, flags);
if (v != NULL)
kfree(v);
else {
LOGGER_INFO(" %s free v FAIL!\n", __func__, v);
if (pSeqData != NULL) {
LOGGER_INFO(
"%s free pSeqData = 0x%x\n",
__func__, pSeqData);
kfree(pSeqData);
} else
LOGGER_ERR(
"%s free pSeqData = 0x%x FAIL!\n",
__func__, pSeqData);
}
pSeqData = NULL;
}
}
}
/**
* stop() is called when iteration is complete (clean up)
*/
static void seq_stopl(struct seq_file *s, void *v)
{
LOGGER_INFO("%s v = 0x%x\n", __func__, v);
}
/**
* success return 0, otherwise return error code
*/
static int seq_show(struct seq_file *s, void *v)
{
struct sw_logger_seq_data *pSData = v;
#ifdef SW_LOGGER_DEBUG
unsigned int i;
#else
static unsigned int prevIsBinary = 0;
#endif
LOGGER_INFO("%s in", __func__);
#ifdef SW_LOGGER_DEBUG
if ((sw_log_buf[pSData->i] == 0xA5) &&
(sw_log_buf[pSData->i+1] == 0xA5)) {
seq_printf(s, "dbglog[%d,%d,%d] = ",
pSData->w_ptr, pSData->r_ptr, pSData->i);
for (i = 0; i < LOG_LINE_MAX_LENS; i++)
seq_printf(s, "%02X", sw_log_buf + pSData->i + i);
seq_printf(s, "\n");
} else
seq_printf(s, "dbglog[%d,%d,%d] = %s",
pSData->w_ptr, pSData->r_ptr, pSData->i,
(sw_log_buf + pSData->i));
#else
if ((sw_log_buf[pSData->i] == 0xA5) &&
(sw_log_buf[pSData->i+1] == 0xA5)) {
prevIsBinary = 1;
seq_write(s, sw_log_buf + pSData->i, LOG_LINE_MAX_LENS);
} else {
if (prevIsBinary)
seq_printf(s, "\n");
prevIsBinary = 0;
seq_printf(s, "%s",
sw_log_buf + pSData->i);
}
#endif
return 0;
}
static int seq_showl(struct seq_file *s, void *v)
{
struct sw_logger_seq_data *pSData = v;
#ifdef SW_LOGGER_DEBUG
unsigned int i;
#else
static unsigned int prevIsBinary = 0;
#endif
LOGGER_INFO("%s in: %s", __func__,
sw_log_buf + pSData->i);
#ifdef SW_LOGGER_DEBUG
if (pSData->i != pSData->w_ptr) {
if ((sw_log_buf[pSData->i] == 0xA5) &&
(sw_log_buf[pSData->i+1] == 0xA5)) {
seq_printf(s, "dbglog[%d,%d,%d] = ",
pSData->w_ptr, pSData->r_ptr, pSData->i);
for (i = 0; i < LOG_LINE_MAX_LENS; i++)
seq_printf(s, "%02X", sw_log_buf + pSData->i + i);
seq_printf(s, "\n");
} else
seq_printf(s, "dbglog[%d,%d,%d] = %s",
pSData->w_ptr, pSData->r_ptr, pSData->i,
(sw_log_buf + pSData->i));
}
#else
if (pSData->i != pSData->w_ptr) {
if ((sw_log_buf[pSData->i] == 0xA5) &&
(sw_log_buf[pSData->i+1] == 0xA5)) {
prevIsBinary = 1;
seq_write(s, sw_log_buf + pSData->i, LOG_LINE_MAX_LENS);
} else {
if (prevIsBinary)
seq_printf(s, "\n");
prevIsBinary = 0;
seq_printf(s, "%s",
sw_log_buf + pSData->i);
}
}
#endif
return 0;
}
static unsigned int seq_poll(struct file *file, poll_table *wait)
{
unsigned int ret = 0;
if (!(file->f_mode & FMODE_READ))
return ret;
poll_wait(file, &apusys_swlog_wait, wait);
if (ioread32(LOG_W_PTR) !=
ioread32(LOG_R_PTR))
ret = POLLIN | POLLRDNORM;
return ret;
}
static const struct seq_operations seq_ops = {
.start = seq_start,
.next = seq_next,
.stop = seq_stop,
.show = seq_show
};
static const struct seq_operations seq_ops_lock = {
.start = seq_startl,
.next = seq_next_lock,
.stop = seq_stopl,
.show = seq_showl
};
static int debug_sqopen_lock(struct inode *inode, struct file *file)
{
return seq_open(file, &seq_ops_lock);
}
static int debug_sqopen(struct inode *inode, struct file *file)
{
return seq_open(file, &seq_ops);
}
static void clear_sw_log_buf(void)
{
unsigned long flags;
LOGGER_INFO("in\n");
spin_lock_irqsave(&sw_logger_spinlock, flags);
iowrite32(0, LOG_W_PTR);
iowrite32(0, LOG_R_PTR);
iowrite32(0, LOG_OV_FLG);
memset(sw_log_buf, 0, APU_LOG_SIZE);
spin_unlock_irqrestore(&sw_logger_spinlock, flags);
}
static ssize_t debug_write(struct file *file,
const char __user *buffer, size_t count, loff_t *pos)
{
char buf[PROC_WRITE_BUFSIZE];
if (*pos > 0 || count > PROC_WRITE_BUFSIZE)
return -EFAULT;
if (copy_from_user(buf, buffer, count))
return -EFAULT;
buf[PROC_WRITE_BUFSIZE - 1] = '\0';
LOGGER_INFO("cmd = %s\n", buf);
if (!strncmp(buf, CLEAR_LOG_CMD, strlen(CLEAR_LOG_CMD)))
clear_sw_log_buf();
return count;
}
static const struct proc_ops sw_loggerSeqLog_ops = {
.proc_open = debug_sqopen,
.proc_read = seq_read, // system
.proc_write = debug_write,
.proc_lseek = seq_lseek, // system
.proc_release = seq_release // system
};
static const struct proc_ops sw_loggerSeqLogL_ops = {
.proc_open = debug_sqopen_lock,
.proc_poll = seq_poll,
.proc_read = seq_read, // system
.proc_write = debug_write,
.proc_lseek = seq_lseek, // system
.proc_release = seq_release // system
};
/* must ensure uP no longer print log */
static ssize_t apusys_log_dump(struct file *filep,
struct kobject *kobj, struct bin_attribute *attr,
char *buf, loff_t offset, size_t size)
{
unsigned int length = 0;
uint32_t w_ptr, r_ptr, overflow_flg, i, print_sz;
unsigned long flags;
spin_lock_irqsave(&sw_logger_spinlock, flags);
w_ptr = ioread32(LOG_W_PTR);
r_ptr = ioread32(LOG_R_PTR);
overflow_flg = ioread32(LOG_OV_FLG);
iowrite32(0, LOG_OV_FLG);
spin_unlock_irqrestore(&sw_logger_spinlock, flags);
sw_logger_buf_invalidate();
if (w_ptr == r_ptr && overflow_flg == 0)
return length;
if (overflow_flg == 0)
i = r_ptr;
else
i = w_ptr;
do {
print_sz = (strlen(sw_log_buf + i));
if ((length + print_sz) <= size) {
scnprintf(buf + length, print_sz, "%s", sw_log_buf + i);
/* replace trailing null character with new line
* for log readability
*/
buf[length + print_sz - 1] = '\n';
} else
break;
length += print_sz;
i = (i + LOG_LINE_MAX_LENS) % APU_LOG_SIZE;
iowrite32(i, LOG_R_PTR);
} while (i != w_ptr);
return length;
}
struct bin_attribute bin_attr_apusys_log = {
.attr = {
.name = "apusys_log.txt",
.mode = 0444,
},
.size = 0,
.read = apusys_log_dump,
};
static int sw_logger_create_sysfs(struct device *dev)
{
int ret = 0;
/* create /sys/kernel/apusys_logger */
root_dir = kobject_create_and_add("apusys_logger", kernel_kobj);
if (!root_dir) {
dev_info(sw_logger_dev, "%s kobject_create_and_add fail for apusys_logger, ret %d\n",
__func__, ret);
return -EINVAL;
}
ret = sysfs_create_bin_file(root_dir, &bin_attr_apusys_log);
if (ret)
dev_info(sw_logger_dev, "%s sysfs create fail for apusys_log, ret %d\n",
__func__, ret);
return ret;
}
static void sw_logger_remove_sysfs(struct device *dev)
{
sysfs_remove_bin_file(root_dir, &bin_attr_apusys_log);
kobject_put(root_dir);
}
static void sw_logger_remove_procfs(struct device *dev)
{
remove_proc_entry("log", log_root);
remove_proc_entry("seq_log", log_root);
remove_proc_entry("seq_logl", log_root);
remove_proc_entry("attr", log_root);
remove_proc_entry(APUSYS_LOGGER_DIR, NULL);
}
static int sw_logger_create_procfs(struct device *dev)
{
int ret = 0;
log_root = proc_mkdir(APUSYS_LOGGER_DIR, NULL);
ret = IS_ERR_OR_NULL(log_root);
if (ret) {
LOGGER_ERR("(%d)failed to create apusys_logger dir\n", ret);
goto out;
}
/* create device table info */
log_devinfo = proc_create("log", 0444,
log_root, &apusys_debug_fops);
ret = IS_ERR_OR_NULL(log_devinfo);
if (ret) {
LOGGER_ERR("(%d)failed to create apusys_logger node(devinfo)\n",
ret);
goto out;
}
log_seqlog = proc_create("seq_log", 0444,
log_root, &sw_loggerSeqLog_ops);
ret = IS_ERR_OR_NULL(log_seqlog);
if (ret) {
LOGGER_ERR("(%d)failed to create apusys_logger node(seqlog)\n",
ret);
goto out;
}
log_seqlogL = proc_create("seq_logl", 0444,
log_root, &sw_loggerSeqLogL_ops);
ret = IS_ERR_OR_NULL(log_seqlogL);
if (ret) {
LOGGER_ERR("(%d)failed to create apusys_logger node(seqlogL)\n",
ret);
goto out;
}
log_devattr = proc_create("attr", 0444,
log_root, &sw_logger_attr_fops);
ret = IS_ERR_OR_NULL(log_devattr);
if (ret) {
LOGGER_ERR(
"(%d)failed to create apusys_logger attr node(devinfo)\n",
ret);
goto out;
}
return 0;
out:
sw_logger_remove_procfs(dev);
return ret;
}
static int sw_logger_probe(struct platform_device *pdev)
{
struct resource *res;
struct device *dev = &pdev->dev;
int ret = 0;
dev_info(sw_logger_dev, "%s in", __func__);
sw_logger_dev = dev;
init_waitqueue_head(&apusys_swlog_wait);
ret = sw_logger_create_procfs(dev);
if (ret) {
LOGGER_ERR("%s: sw_logger_create_procfs fail\n", __func__);
goto remove_procfs;
}
ret = sw_logger_create_sysfs(dev);
if (ret) {
LOGGER_ERR("%s: sw_logger_create_sysfs fail\n", __func__);
goto remove_sysfs;
}
res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "apu_mbox");
if (res == NULL) {
dev_info(sw_logger_dev, "%s: apu_mbox get resource fail\n", __func__);
ret = -ENODEV;
goto remove_ioremap;
}
apu_mbox = ioremap(res->start, res->end - res->start + 1);
if (IS_ERR((void const *)apu_mbox)) {
dev_info(sw_logger_dev, "%s: apu_mbox remap base fail\n", __func__);
ret = -ENOMEM;
goto remove_ioremap;
}
dev_info(sw_logger_dev, "apu_sw_logger probe done, sw_log_buf= 0x%p\n",
sw_log_buf);
return 0;
remove_ioremap:
if (apu_mbox != NULL)
iounmap(apu_mbox);
remove_sysfs:
sw_logger_remove_sysfs(dev);
remove_procfs:
sw_logger_remove_procfs(dev);
LOGGER_ERR("apu_sw_logger probe error!!\n");
return ret;
}
static int sw_logger_remove(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
sw_logger_remove_procfs(dev);
sw_logger_remove_sysfs(dev);
if (!BYPASS_IOMMU) {
dma_unmap_single(dev, handle, APU_LOG_SIZE, DMA_FROM_DEVICE);
kfree(sw_log_buf);
} else {
dma_free_coherent(dev, APU_LOG_SIZE,
sw_log_buf, handle);
}
return 0;
}
static const struct of_device_id apusys_sw_logger_of_match[] = {
{ .compatible = "mediatek,apusys_sw_logger"},
{},
};
MODULE_DEVICE_TABLE(of, apusys_sw_logger_of_match);
static struct platform_driver sw_logger_driver = {
.probe = sw_logger_probe,
.remove = sw_logger_remove,
.driver = {
.name = SW_LOGGER_DEV_NAME,
.of_match_table = of_match_ptr(apusys_sw_logger_of_match),
}
};
int sw_logger_init(struct apusys_core_info *info)
{
int ret = 0;
dev_info(sw_logger_dev, "%s in", __func__);
allow_signal(SIGKILL);
sw_log_buf = NULL;
g_sw_logger_log_lv = DEBUG_LOG_WARN;
startl_first_enter_session = true;
ret = platform_driver_register(&sw_logger_driver);
if (ret != 0) {
pr_info("failed to register sw_logger driver");
return -ENODEV;
}
return ret;
}
void sw_logger_exit(void)
{
disallow_signal(SIGKILL);
platform_driver_unregister(&sw_logger_driver);
}