288 lines
6.3 KiB
C
288 lines
6.3 KiB
C
// SPDX-License-Identifier: GPL-2.0
|
|
//
|
|
// Copyright (c) 2021 MediaTek Inc.
|
|
|
|
#include <linux/kernel.h>
|
|
#include <linux/module.h>
|
|
#include <linux/cdev.h>
|
|
|
|
#include <linux/dma-mapping.h>
|
|
#include <linux/dma-buf.h>
|
|
#include <linux/irqflags.h>
|
|
#include <linux/timekeeping.h>
|
|
|
|
#include "mtk_ccu_common.h"
|
|
|
|
static inline unsigned int mtk_ccu_mstojiffies(unsigned int Ms)
|
|
{
|
|
return ((Ms * HZ + 512) >> 10);
|
|
}
|
|
|
|
void mtk_ccu_memclr(void *dst, int len)
|
|
{
|
|
int i = 0;
|
|
uint32_t *dstPtr = (uint32_t *)dst;
|
|
|
|
for (i = 0; i < len/4; i++)
|
|
writel(0, dstPtr + i);
|
|
}
|
|
|
|
void mtk_ccu_memcpy(void *dst, const void *src, uint32_t len)
|
|
{
|
|
int i, copy_len;
|
|
uint32_t data = 0;
|
|
uint32_t align_data = 0;
|
|
|
|
for (i = 0; i < len/4; ++i)
|
|
writel(*((uint32_t *)src+i), (uint32_t *)dst+i);
|
|
|
|
if ((len % 4) != 0) {
|
|
copy_len = len & ~(0x3);
|
|
for (i = 0; i < 4; ++i) {
|
|
if (i < (len%4)) {
|
|
data = *((char *)src + copy_len + i);
|
|
align_data += data << (8 * i);
|
|
}
|
|
}
|
|
writel(align_data, (uint32_t *)dst + len/4);
|
|
}
|
|
}
|
|
|
|
struct mtk_ccu_mem_info *mtk_ccu_get_meminfo(struct mtk_ccu *ccu,
|
|
enum mtk_ccu_buffer_type type)
|
|
{
|
|
if (type >= MTK_CCU_BUF_MAX)
|
|
return NULL;
|
|
else
|
|
return &ccu->buffer_handle[type].meminfo;
|
|
}
|
|
|
|
static int mtk_ccu_open(struct inode *inode, struct file *flip)
|
|
{
|
|
struct mtk_ccu *ccu = container_of(inode->i_cdev,
|
|
struct mtk_ccu,
|
|
ccu_cdev);
|
|
|
|
flip->private_data = ccu;
|
|
return 0;
|
|
}
|
|
|
|
static int mtk_ccu_release(struct inode *inode, struct file *flip)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
|
|
static int mtk_ccu_wakeup(struct mtk_ccu *ccu)
|
|
{
|
|
ccu->bWaitCond = true;
|
|
wake_up_interruptible(&ccu->WaitQueueHead);
|
|
return 0;
|
|
}
|
|
|
|
static int mtk_ccu_waitirq(struct mtk_ccu *ccu)
|
|
{
|
|
signed int ret = 0, timeout = 0;
|
|
|
|
timeout = wait_event_interruptible_timeout(
|
|
ccu->WaitQueueHead,
|
|
ccu->bWaitCond,
|
|
mtk_ccu_mstojiffies(100));
|
|
ccu->bWaitCond = false;
|
|
|
|
|
|
if (timeout > 0) {
|
|
dev_info(ccu->dev, "remain time:%d, log_idx: %d\n",
|
|
timeout, ccu->g_LogBufIdx);
|
|
ret = ccu->g_LogBufIdx;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
static long mtk_ccu_ioctl(struct file *flip, unsigned int cmd,
|
|
unsigned long arg)
|
|
{
|
|
int ret = 0;
|
|
int log_idx;
|
|
uint32_t log_level[2] = {0};
|
|
struct mtk_ccu_buffer log_info = {0};
|
|
struct mtk_ccu *ccu = flip->private_data;
|
|
|
|
switch (cmd) {
|
|
case MTK_CCU_IOCTL_SET_LOG_LEVEL:
|
|
{
|
|
|
|
ret = copy_from_user(log_level, (void *)arg, sizeof(log_level));
|
|
if (ret) {
|
|
dev_err(ccu->dev, "set log level failed\n");
|
|
return -EFAULT;
|
|
}
|
|
ccu->log_level = log_level[0];
|
|
ccu->log_taglevel = log_level[1];
|
|
|
|
break;
|
|
}
|
|
case MTK_CCU_IOCTL_WAIT_IRQ:
|
|
{
|
|
log_idx = mtk_ccu_waitirq(ccu);
|
|
if (log_idx < 0) {
|
|
log_info = ccu->log_info[3];
|
|
log_info.buf_idx = log_idx;
|
|
} else if (log_idx > 0) {
|
|
log_info = ccu->log_info[log_idx-1];
|
|
log_info.buf_idx = log_idx;
|
|
}
|
|
log_info.ktime = ccu->ktime;
|
|
log_info.gtick = ccu->gtick;
|
|
ret = copy_to_user((void *)arg, &log_info,
|
|
sizeof(struct mtk_ccu_buffer));
|
|
if (ret) {
|
|
dev_err(ccu->dev, "copy_to_user failed\n");
|
|
ret = -EFAULT;
|
|
}
|
|
break;
|
|
}
|
|
case MTK_CCU_IOCTL_FLUSH_LOG:
|
|
{
|
|
mtk_ccu_wakeup(ccu);
|
|
break;
|
|
}
|
|
|
|
default:
|
|
dev_warn(ccu->dev, "ioctl:No such command!\n");
|
|
ret = -EINVAL;
|
|
break;
|
|
}
|
|
|
|
if (ret != 0) {
|
|
dev_err(ccu->dev, "fail, cmd(%d), cmd_nr(%d)",
|
|
cmd, _IOC_NR(cmd));
|
|
dev_err(ccu->dev, "fail, (process, pid, tgid)=(%s, %d, %d)\n",
|
|
current->comm, current->pid, current->tgid);
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
static int mtk_ccu_mmap(struct file *flip,
|
|
struct vm_area_struct *vma)
|
|
{
|
|
unsigned long length = (vma->vm_end - vma->vm_start);
|
|
struct mtk_ccu *ccu = flip->private_data;
|
|
int ret;
|
|
|
|
if (length > (MTK_CCU_DRAM_LOG_BUF_SIZE * 4))
|
|
return -EINVAL;
|
|
|
|
vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
|
|
|
|
ret = dma_mmap_attrs(ccu->dev, vma, ccu->ext_buf.meminfo.va,
|
|
ccu->ext_buf.meminfo.mva, length, DMA_ATTR_WRITE_COMBINE);
|
|
if (ret)
|
|
dev_err(ccu->dev, "Remapping memory failed, error: %d\n", ret);
|
|
return ret;
|
|
}
|
|
|
|
static const struct file_operations mtk_ccu_fops = {
|
|
.owner = THIS_MODULE,
|
|
.open = mtk_ccu_open,
|
|
.release = mtk_ccu_release,
|
|
.unlocked_ioctl = mtk_ccu_ioctl,
|
|
.mmap = mtk_ccu_mmap,
|
|
};
|
|
|
|
void mtk_ccu_unreg_chardev(struct mtk_ccu *ccu)
|
|
{
|
|
cdev_del(&ccu->ccu_cdev);
|
|
unregister_chrdev_region(ccu->dev_no, 1);
|
|
}
|
|
|
|
int mtk_ccu_reg_chardev(struct mtk_ccu *ccu)
|
|
{
|
|
struct device *dev = ccu->dev;
|
|
int cdev_ret = -1;
|
|
int alloc_ret = -1;
|
|
int ret = 0;
|
|
|
|
alloc_ret = alloc_chrdev_region(&ccu->dev_no, 0, 1, MTK_CCU_DEV_NAME);
|
|
if (alloc_ret) {
|
|
dev_err(dev, "failed to alloc chr_dev region, %d\n", alloc_ret);
|
|
goto ERR;
|
|
}
|
|
|
|
cdev_init(&ccu->ccu_cdev, &mtk_ccu_fops);
|
|
ccu->ccu_cdev.owner = THIS_MODULE;
|
|
cdev_ret = cdev_add(&ccu->ccu_cdev, ccu->dev_no, 1);
|
|
if (cdev_ret) {
|
|
dev_err(dev, "Attach file operation failed, %d\n", cdev_ret);
|
|
goto ERR;
|
|
}
|
|
|
|
ccu->ccu_class = class_create(THIS_MODULE, "ccurprocdrv");
|
|
if (IS_ERR(ccu->ccu_class)) {
|
|
ret = PTR_ERR(ccu->ccu_class);
|
|
dev_err(dev, "Unable to create class, err = %d\n", ret);
|
|
goto ERR;
|
|
}
|
|
ccu->ccu_cdev_dev = device_create(
|
|
ccu->ccu_class, dev, ccu->dev_no, NULL, MTK_CCU_DEV_NAME);
|
|
if (IS_ERR(ccu->ccu_cdev_dev)) {
|
|
ret = PTR_ERR(ccu->ccu_cdev_dev);
|
|
dev_err(ccu->dev,
|
|
"Failed to create device: /dev/%s, err = %d",
|
|
MTK_CCU_DEV_NAME, ret);
|
|
goto ERR;
|
|
}
|
|
|
|
|
|
return 0;
|
|
ERR:
|
|
if (alloc_ret == 0)
|
|
unregister_chrdev_region(ccu->dev_no, 1);
|
|
if (cdev_ret == 0)
|
|
cdev_del(&ccu->ccu_cdev);
|
|
return -EFAULT;
|
|
}
|
|
|
|
void mtk_ccu_ipc_log_handle(uint32_t data, uint32_t len, void *priv)
|
|
{
|
|
struct mtk_ccu *ccu = priv;
|
|
unsigned long flags;
|
|
|
|
if (data > LOG_BUF_IDX_MAX) {
|
|
dev_info(ccu->dev, "BUG: log buf idx %d\n", data);
|
|
return;
|
|
}
|
|
|
|
dev_info(ccu->dev, "got APMCU_FLUSH_LOG:%d\n", data);
|
|
ccu->bWaitCond = true;
|
|
ccu->g_LogBufIdx = (uint32_t)data;
|
|
local_irq_save(flags);
|
|
ccu->ktime = ktime_get_ns();
|
|
ccu->gtick = (uint32_t)arch_timer_read_counter();
|
|
local_irq_restore(flags);
|
|
wake_up_interruptible(&ccu->WaitQueueHead);
|
|
}
|
|
|
|
void mtk_ccu_ipc_assert_handle(uint32_t data, uint32_t len, void *priv)
|
|
{
|
|
struct mtk_ccu *ccu = priv;
|
|
|
|
dev_err(ccu->dev, "got AP_ISR_CCU_ASSERT:%d, para 0x%x\n", data, len);
|
|
ccu->bWaitCond = true;
|
|
ccu->g_LogBufIdx = 0xFFFFFFFF;
|
|
wake_up_interruptible(&ccu->WaitQueueHead);
|
|
}
|
|
|
|
void mtk_ccu_ipc_warning_handle(uint32_t data, uint32_t len, void *priv)
|
|
{
|
|
struct mtk_ccu *ccu = priv;
|
|
|
|
dev_err(ccu->dev, "got AP_ISR_CCU_WARNING:%d\n", data);
|
|
ccu->bWaitCond = true;
|
|
ccu->g_LogBufIdx = -2;
|
|
wake_up_interruptible(&ccu->WaitQueueHead);
|
|
}
|
|
|
|
MODULE_DESCRIPTION("MTK CCU Rproc Driver");
|
|
MODULE_LICENSE("GPL v2");
|