880 lines
21 KiB
C
880 lines
21 KiB
C
// SPDX-License-Identifier: GPL-2.0
|
|
/*
|
|
* Copyright (c) 2020 MediaTek Inc.
|
|
*/
|
|
|
|
#include <linux/module.h> /* Needed by all modules */
|
|
#include <linux/kernel.h> /* Needed for KERN_ALERT */
|
|
#include <linux/cdev.h>
|
|
#include <linux/device.h>
|
|
#include <linux/delay.h>
|
|
#include <linux/jiffies.h>
|
|
#include <linux/platform_device.h>
|
|
#include <linux/interrupt.h>
|
|
#include <linux/uaccess.h>
|
|
#include <linux/kthread.h>
|
|
|
|
#include <linux/init.h>
|
|
#include <linux/io.h>
|
|
|
|
#ifdef CONFIG_OF
|
|
#include <linux/of.h>
|
|
#include <linux/of_platform.h>
|
|
#endif
|
|
|
|
#include "reviser_drv.h"
|
|
#include "reviser_ioctl.h"
|
|
#include "reviser_cmn.h"
|
|
#include "reviser_hw.h"
|
|
#include "reviser_dbg.h"
|
|
#include "reviser_mem_mgt.h"
|
|
#include "apusys_power.h"
|
|
|
|
/* define */
|
|
#define APUSYS_DRV_NAME "apusys_drv_reviser"
|
|
#define APUSYS_DEV_NAME "apusys_reviser"
|
|
|
|
/* global variable */
|
|
static struct class *reviser_class;
|
|
struct reviser_dev_info *g_reviser_device;
|
|
static struct task_struct *mem_task;
|
|
static int g_ioctl_enable;
|
|
|
|
|
|
/* function declaration */
|
|
static int reviser_open(struct inode *, struct file *);
|
|
static int reviser_release(struct inode *, struct file *);
|
|
static long reviser_ioctl(struct file *filp,
|
|
unsigned int cmd, unsigned long arg);
|
|
static long reviser_compat_ioctl(struct file *, unsigned int, unsigned long);
|
|
|
|
static void reviser_power_on_cb(void *para);
|
|
static void reviser_power_off_cb(void *para);
|
|
|
|
|
|
irqreturn_t reviser_interrupt(int irq, void *private_data)
|
|
{
|
|
|
|
struct reviser_dev_info *reviser_device;
|
|
unsigned long flags;
|
|
irqreturn_t ret = IRQ_NONE;
|
|
|
|
|
|
DEBUG_TAG;
|
|
|
|
|
|
reviser_device = (struct reviser_dev_info *)private_data;
|
|
|
|
if (!reviser_is_power(reviser_device)) {
|
|
//LOG_ERR("Can Not Read when power disable\n");
|
|
return IRQ_NONE;
|
|
}
|
|
|
|
// Check if INT is for reviser
|
|
if (reviser_check_int_valid(reviser_device)) {
|
|
//LOG_ERR("INT NOT triggered by reviser\n");
|
|
return IRQ_NONE;
|
|
}
|
|
|
|
if (!reviser_get_interrupt_offset(private_data)) {
|
|
//reviser_print_remap_table(private_data, NULL);
|
|
//reviser_print_context_ID(private_data, NULL);
|
|
spin_lock_irqsave(&g_reviser_device->lock_dump, flags);
|
|
reviser_device->dump.err_count++;
|
|
spin_unlock_irqrestore(&g_reviser_device->lock_dump, flags);
|
|
ret = IRQ_HANDLED;
|
|
} else {
|
|
//LOG_ERR("INT NOT triggered by reviser\n");
|
|
ret = IRQ_NONE;
|
|
}
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
static int reviser_memory_func(void *arg)
|
|
{
|
|
struct reviser_dev_info *reviser_device;
|
|
|
|
reviser_device = (struct reviser_dev_info *) arg;
|
|
|
|
if (reviser_dram_remap_init(reviser_device)) {
|
|
LOG_ERR("Could not set memory for reviser\n");
|
|
return -ENOMEM;
|
|
}
|
|
LOG_INFO("reviser memory init\n");
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
static const struct file_operations reviser_fops = {
|
|
.open = reviser_open,
|
|
.unlocked_ioctl = reviser_ioctl,
|
|
.release = reviser_release,
|
|
.compat_ioctl = reviser_compat_ioctl,
|
|
};
|
|
|
|
|
|
static void reviser_power_on_cb(void *para)
|
|
{
|
|
unsigned long flags;
|
|
|
|
if (g_reviser_device == NULL) {
|
|
LOG_ERR("Not Found reviser_device\n");
|
|
return;
|
|
}
|
|
spin_lock_irqsave(&g_reviser_device->lock_power, flags);
|
|
g_reviser_device->power = true;
|
|
spin_unlock_irqrestore(&g_reviser_device->lock_power, flags);
|
|
|
|
|
|
reviser_enable_interrupt(g_reviser_device, 1);
|
|
|
|
if (reviser_boundary_init(g_reviser_device, BOUNDARY_APUSYS)) {
|
|
LOG_ERR("Set Boundary Fail\n");
|
|
return;
|
|
}
|
|
if (reviser_set_default_iova(g_reviser_device)) {
|
|
LOG_ERR("Set Default IOVA Fail\n");
|
|
return;
|
|
}
|
|
if (reviser_init_ip()) {
|
|
LOG_ERR("Init IP Fail\n");
|
|
return;
|
|
}
|
|
|
|
}
|
|
|
|
static void reviser_power_off_cb(void *para)
|
|
{
|
|
unsigned long flags;
|
|
|
|
if (g_reviser_device == NULL) {
|
|
LOG_ERR("Not Found reviser_device\n");
|
|
return;
|
|
}
|
|
reviser_enable_interrupt(g_reviser_device, 0);
|
|
|
|
spin_lock_irqsave(&g_reviser_device->lock_power, flags);
|
|
g_reviser_device->power = false;
|
|
spin_unlock_irqrestore(&g_reviser_device->lock_power, flags);
|
|
}
|
|
|
|
static int reviser_open(struct inode *inode, struct file *filp)
|
|
{
|
|
struct reviser_dev_info *reviser_device;
|
|
|
|
DEBUG_TAG;
|
|
reviser_device = container_of(inode->i_cdev,
|
|
struct reviser_dev_info, reviser_cdev);
|
|
|
|
filp->private_data = reviser_device;
|
|
LOG_DEBUG("reviser_device %p\n", reviser_device);
|
|
LOG_DEBUG("filp->private_data %p\n", filp->private_data);
|
|
return 0;
|
|
}
|
|
|
|
static int reviser_release(struct inode *inode, struct file *filp)
|
|
{
|
|
DEBUG_TAG;
|
|
return 0;
|
|
}
|
|
|
|
static int reviser_probe(struct platform_device *pdev)
|
|
{
|
|
//reviser_device_init();
|
|
int ret = 0;
|
|
int irq;
|
|
|
|
struct resource *apusys_reviser_ctl; /* IO mem resources */
|
|
struct resource *apusys_reviser_tcm; /* IO mem resources */
|
|
struct resource *apusys_reviser_vlm; /* IO mem resources */
|
|
struct resource *apusys_reviser_int; /* IO mem resources */
|
|
struct device *dev = &pdev->dev;
|
|
struct reviser_dev_info *reviser_device;
|
|
|
|
struct device_node *power_node;
|
|
struct platform_device *power_pdev;
|
|
|
|
DEBUG_TAG;
|
|
|
|
g_reviser_device = NULL;
|
|
|
|
/* make sure apusys_power driver initiallized before
|
|
* calling apu_power_callback_device_register
|
|
*/
|
|
power_node = of_find_compatible_node(
|
|
NULL, NULL, "mediatek,apusys_power");
|
|
if (!power_node) {
|
|
LOG_ERR("DT,mediatek,apusys_power not found\n");
|
|
return -EINVAL;
|
|
}
|
|
|
|
power_pdev = of_find_device_by_node(power_node);
|
|
|
|
if (!power_pdev || !power_pdev->dev.driver) {
|
|
LOG_DEBUG("Waiting for %s\n",
|
|
power_node->full_name);
|
|
return -EPROBE_DEFER;
|
|
}
|
|
|
|
|
|
reviser_device = devm_kzalloc(dev, sizeof(*reviser_device), GFP_KERNEL);
|
|
if (!reviser_device)
|
|
return -ENOMEM;
|
|
|
|
reviser_device->init_done = false;
|
|
|
|
mutex_init(&reviser_device->mutex_ctxid);
|
|
mutex_init(&reviser_device->mutex_tcm);
|
|
mutex_init(&reviser_device->mutex_vlm_pgtable);
|
|
mutex_init(&reviser_device->mutex_remap);
|
|
mutex_init(&reviser_device->mutex_power);
|
|
init_waitqueue_head(&reviser_device->wait_ctxid);
|
|
init_waitqueue_head(&reviser_device->wait_tcm);
|
|
spin_lock_init(&reviser_device->lock_power);
|
|
spin_lock_init(&reviser_device->lock_dump);
|
|
|
|
g_ioctl_enable = 0;
|
|
reviser_device->dev = &pdev->dev;
|
|
|
|
//memset(&g_reviser_info, 0, sizeof(struct reviser_dev_info));
|
|
/* get major */
|
|
ret = alloc_chrdev_region(&reviser_device->reviser_devt,
|
|
0, 1, APUSYS_DRV_NAME);
|
|
if (ret < 0) {
|
|
LOG_ERR("alloc_chrdev_region failed, %d\n", ret);
|
|
goto out;
|
|
}
|
|
|
|
/* Attatch file operation. */
|
|
cdev_init(&reviser_device->reviser_cdev, &reviser_fops);
|
|
reviser_device->reviser_cdev.owner = THIS_MODULE;
|
|
DEBUG_TAG;
|
|
|
|
/* Add to system */
|
|
ret = cdev_add(&reviser_device->reviser_cdev,
|
|
reviser_device->reviser_devt, 1);
|
|
if (ret < 0) {
|
|
LOG_ERR("Attatch file operation failed, %d\n", ret);
|
|
goto free_chrdev_region;
|
|
}
|
|
|
|
/* Create class register */
|
|
reviser_class = class_create(THIS_MODULE, APUSYS_DRV_NAME);
|
|
if (IS_ERR(reviser_class)) {
|
|
ret = PTR_ERR(reviser_class);
|
|
LOG_ERR("Unable to create class, err = %d\n", ret);
|
|
goto free_cdev_add;
|
|
}
|
|
|
|
dev = device_create(reviser_class, NULL, reviser_device->reviser_devt,
|
|
NULL, APUSYS_DEV_NAME);
|
|
if (IS_ERR(dev)) {
|
|
ret = PTR_ERR(dev);
|
|
LOG_ERR("Failed to create device: /dev/%s, err = %d",
|
|
APUSYS_DEV_NAME, ret);
|
|
goto free_class;
|
|
}
|
|
DEBUG_TAG;
|
|
|
|
apusys_reviser_ctl = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
|
if (!apusys_reviser_ctl) {
|
|
LOG_ERR("invalid address\n");
|
|
ret = -ENODEV;
|
|
goto free_device;
|
|
}
|
|
apusys_reviser_vlm = platform_get_resource(pdev, IORESOURCE_MEM, 1);
|
|
if (!apusys_reviser_vlm) {
|
|
LOG_ERR("invalid address\n");
|
|
ret = -ENODEV;
|
|
goto free_device;
|
|
}
|
|
apusys_reviser_tcm = platform_get_resource(pdev, IORESOURCE_MEM, 2);
|
|
if (!apusys_reviser_tcm) {
|
|
LOG_ERR("invalid address\n");
|
|
ret = -ENODEV;
|
|
goto free_device;
|
|
}
|
|
|
|
apusys_reviser_int = platform_get_resource(pdev, IORESOURCE_MEM, 3);
|
|
if (!apusys_reviser_int) {
|
|
LOG_ERR("invalid address\n");
|
|
ret = -ENODEV;
|
|
goto free_device;
|
|
}
|
|
|
|
LOG_DEBUG("apusys_reviser_ctl->start = %pa\n",
|
|
&apusys_reviser_ctl->start);
|
|
reviser_device->pctrl_top = ioremap_nocache(apusys_reviser_ctl->start,
|
|
apusys_reviser_ctl->end - apusys_reviser_ctl->start + 1);
|
|
if (!reviser_device->pctrl_top) {
|
|
LOG_ERR("Could not allocate iomem\n");
|
|
ret = -EIO;
|
|
goto free_device;
|
|
}
|
|
|
|
LOG_DEBUG("apusys_reviser_vlm->start = %pa\n",
|
|
&apusys_reviser_vlm->start);
|
|
reviser_device->vlm_base =
|
|
ioremap_nocache(apusys_reviser_vlm->start,
|
|
apusys_reviser_vlm->end - apusys_reviser_vlm->start + 1);
|
|
if (!reviser_device->vlm_base) {
|
|
LOG_ERR("Could not allocate iomem\n");
|
|
ret = -EIO;
|
|
goto free_device;
|
|
}
|
|
reviser_device->vlm.iova = apusys_reviser_vlm->start;
|
|
reviser_device->vlm.size = apusys_reviser_vlm->end - apusys_reviser_vlm->start + 1;
|
|
|
|
LOG_DEBUG("apusys_reviser_tcm->start = %pa\n",
|
|
&apusys_reviser_tcm->start);
|
|
LOG_DEBUG("apusys_reviser_tcm->end = %pa\n",
|
|
&apusys_reviser_tcm->end);
|
|
if (apusys_reviser_tcm->end > apusys_reviser_tcm->start) {
|
|
reviser_device->tcm_base =
|
|
ioremap_nocache(apusys_reviser_tcm->start,
|
|
apusys_reviser_tcm->end - apusys_reviser_tcm->start + 1);
|
|
if (!reviser_device->tcm_base) {
|
|
LOG_ERR("Could not allocate iomem\n");
|
|
ret = -EIO;
|
|
goto free_device;
|
|
}
|
|
reviser_device->tcm.size = apusys_reviser_tcm->end - apusys_reviser_tcm->start + 1;
|
|
} else {
|
|
reviser_device->tcm_base = NULL;
|
|
reviser_device->tcm.size = 0;
|
|
}
|
|
reviser_device->tcm.iova = apusys_reviser_tcm->start;
|
|
|
|
LOG_DEBUG("apusys_reviser_int->start = %pa\n",
|
|
&apusys_reviser_int->start);
|
|
reviser_device->int_base =
|
|
ioremap_nocache(apusys_reviser_int->start,
|
|
apusys_reviser_int->end - apusys_reviser_int->start + 1);
|
|
if (!reviser_device->int_base) {
|
|
LOG_ERR("Could not allocate iomem\n");
|
|
ret = -EIO;
|
|
goto free_device;
|
|
}
|
|
|
|
mem_task = kthread_run(reviser_memory_func, reviser_device, "reviser");
|
|
if (mem_task == NULL) {
|
|
LOG_ERR("create kthread(mem) fail\n");
|
|
ret = -ENOMEM;
|
|
goto free_device;
|
|
}
|
|
|
|
irq = platform_get_irq(pdev, 0);
|
|
if (irq < 0) {
|
|
ret = -ENODEV;
|
|
LOG_ERR("platform_get_irq Failed to request irq %d: %d\n",
|
|
irq, ret);
|
|
goto free_map;
|
|
}
|
|
|
|
ret = devm_request_irq(dev, irq, reviser_interrupt,
|
|
IRQF_TRIGGER_HIGH | IRQF_SHARED,
|
|
dev_name(dev),
|
|
reviser_device);
|
|
if (ret < 0) {
|
|
LOG_ERR("devm_request_irq Failed to request irq %d: %d\n",
|
|
irq, ret);
|
|
ret = -ENODEV;
|
|
goto free_map;
|
|
}
|
|
|
|
|
|
|
|
if (reviser_table_init_ctxID(reviser_device)) {
|
|
ret = -EINVAL;
|
|
goto free_map;
|
|
}
|
|
if (reviser_table_init_tcm(reviser_device)) {
|
|
ret = -EINVAL;
|
|
goto free_map;
|
|
}
|
|
if (reviser_table_init_vlm(reviser_device)) {
|
|
ret = -EINVAL;
|
|
goto free_map;
|
|
}
|
|
if (reviser_table_init_remap(reviser_device)) {
|
|
ret = -EINVAL;
|
|
goto free_map;
|
|
}
|
|
reviser_dbg_init(reviser_device);
|
|
|
|
g_reviser_device = reviser_device;
|
|
|
|
ret = apu_power_callback_device_register(REVISOR,
|
|
reviser_power_on_cb, reviser_power_off_cb);
|
|
if (ret) {
|
|
LOG_ERR("apu_power_callback_device_register return error(%d)\n",
|
|
ret);
|
|
ret = -EINVAL;
|
|
goto free_map;
|
|
}
|
|
|
|
apu_power_device_register(REVISER, pdev);
|
|
/* Workaround for power all on mode*/
|
|
//reviser_power_on(NULL);
|
|
|
|
|
|
reviser_device->init_done = true;
|
|
platform_set_drvdata(pdev, reviser_device);
|
|
dev_set_drvdata(dev, reviser_device);
|
|
|
|
|
|
return ret;
|
|
|
|
free_map:
|
|
iounmap(reviser_device->pctrl_top);
|
|
iounmap(reviser_device->vlm_base);
|
|
if (reviser_device->tcm_base)
|
|
iounmap(reviser_device->tcm_base);
|
|
free_device:
|
|
/* Release device */
|
|
device_destroy(reviser_class, reviser_device->reviser_devt);
|
|
|
|
free_class:
|
|
/* Release class */
|
|
class_destroy(reviser_class);
|
|
free_cdev_add:
|
|
/* Release char driver */
|
|
cdev_del(&reviser_device->reviser_cdev);
|
|
|
|
free_chrdev_region:
|
|
unregister_chrdev_region(reviser_device->reviser_devt, 1);
|
|
|
|
out:
|
|
return ret;
|
|
}
|
|
|
|
static int reviser_remove(struct platform_device *pdev)
|
|
{
|
|
struct reviser_dev_info *reviser_device = platform_get_drvdata(pdev);
|
|
|
|
DEBUG_TAG;
|
|
|
|
apu_power_device_unregister(REVISER);
|
|
apu_power_callback_device_unregister(REVISOR);
|
|
|
|
g_reviser_device = NULL;
|
|
|
|
reviser_dbg_destroy(reviser_device);
|
|
reviser_dram_remap_destroy(reviser_device);
|
|
iounmap(reviser_device->pctrl_top);
|
|
|
|
/* Release device */
|
|
device_destroy(reviser_class, reviser_device->reviser_devt);
|
|
|
|
/* Release class */
|
|
if (reviser_class != NULL)
|
|
class_destroy(reviser_class);
|
|
|
|
/* Release char driver */
|
|
cdev_del(&reviser_device->reviser_cdev);
|
|
|
|
unregister_chrdev_region(reviser_device->reviser_devt, 1);
|
|
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int reviser_suspend(struct platform_device *pdev, pm_message_t mesg)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
static int reviser_resume(struct platform_device *pdev)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
static long reviser_ioctl(struct file *filp, unsigned int cmd,
|
|
unsigned long arg)
|
|
{
|
|
int ret = 0;
|
|
struct reviser_dev_info *reviser_device = filp->private_data;
|
|
struct reviser_ioctl_info info;
|
|
unsigned long ctxID = 0;
|
|
struct table_tcm pg_table;
|
|
uint32_t tcm_page_num = 0, tcm_size = 0;
|
|
|
|
if (!g_ioctl_enable)
|
|
return -EINVAL;
|
|
|
|
memset(&info, 0, sizeof(struct reviser_ioctl_info));
|
|
|
|
switch (cmd) {
|
|
case REVISER_IOCTL_SET_BOUNDARY:
|
|
|
|
if (copy_from_user(&info,
|
|
(void *)arg,
|
|
sizeof(struct reviser_ioctl_info))) {
|
|
LOG_ERR("copy info struct fail\n");
|
|
ret = -EINVAL;
|
|
}
|
|
|
|
ret = reviser_set_boundary(
|
|
reviser_device,
|
|
info.bound.type,
|
|
info.bound.index,
|
|
info.bound.boundary);
|
|
if (ret == 0) {
|
|
if (copy_to_user((void *)arg,
|
|
&info,
|
|
sizeof(struct reviser_ioctl_info))) {
|
|
LOG_ERR("copy info to user fail\n");
|
|
ret = -EINVAL;
|
|
}
|
|
}
|
|
break;
|
|
case REVISER_IOCTL_SET_CONTEXT_ID:
|
|
|
|
if (copy_from_user(&info, (void *)arg,
|
|
sizeof(struct reviser_ioctl_info))) {
|
|
LOG_ERR("copy info struct fail\n");
|
|
ret = -EINVAL;
|
|
}
|
|
|
|
ret = reviser_set_context_ID(
|
|
reviser_device,
|
|
(enum REVISER_DEVICE_E) info.contex.type,
|
|
info.contex.index,
|
|
info.contex.ID);
|
|
if (ret == 0) {
|
|
if (copy_to_user((void *)arg, &info,
|
|
sizeof(struct reviser_ioctl_info))) {
|
|
LOG_ERR("copy info to user fail\n");
|
|
ret = -EINVAL;
|
|
}
|
|
}
|
|
break;
|
|
case REVISER_IOCTL_SET_REMAP_TABLE:
|
|
|
|
if (copy_from_user(&info, (void *)arg,
|
|
sizeof(struct reviser_ioctl_info))) {
|
|
LOG_ERR("copy info struct fail\n");
|
|
ret = -EINVAL;
|
|
}
|
|
|
|
ret = reviser_set_remap_table(
|
|
reviser_device, info.table.index,
|
|
info.table.valid, info.table.ID,
|
|
info.table.src_page, info.table.dst_page);
|
|
if (ret == 0) {
|
|
if (copy_to_user(
|
|
(void *)arg, &info,
|
|
sizeof(struct reviser_ioctl_info))) {
|
|
LOG_ERR("copy info to user fail\n");
|
|
ret = -EINVAL;
|
|
}
|
|
}
|
|
break;
|
|
case REVISER_IOCTL_GET_CTXID:
|
|
|
|
if (copy_from_user(&info, (void *)arg,
|
|
sizeof(struct reviser_ioctl_info))) {
|
|
LOG_ERR("copy info struct fail\n");
|
|
ret = -EINVAL;
|
|
}
|
|
|
|
//if (!reviser_table_get_ctxID(reviser_device, &ctxID)) {
|
|
if (!reviser_table_get_ctxID_sync(reviser_device, &ctxID)) {
|
|
LOG_DEBUG("ctxID: %lu\n", ctxID);
|
|
info.contex.ID = ctxID;
|
|
} else {
|
|
ret = -EINVAL;
|
|
}
|
|
|
|
|
|
if (ret == 0) {
|
|
if (copy_to_user(
|
|
(void *)arg, &info,
|
|
sizeof(struct reviser_ioctl_info))) {
|
|
LOG_ERR("copy info to user fail\n");
|
|
ret = -EINVAL;
|
|
}
|
|
}
|
|
break;
|
|
case REVISER_IOCTL_FREE_CTXID:
|
|
|
|
if (copy_from_user(&info, (void *)arg,
|
|
sizeof(struct reviser_ioctl_info))) {
|
|
LOG_ERR("copy info struct fail\n");
|
|
ret = -EINVAL;
|
|
}
|
|
ctxID = info.contex.ID;
|
|
if (reviser_table_free_ctxID(reviser_device, ctxID)) {
|
|
LOG_DEBUG("ctxID: %lu Fail\n", ctxID);
|
|
ret = -EINVAL;
|
|
}
|
|
|
|
if (ret == 0) {
|
|
if (copy_to_user(
|
|
(void *)arg, &info,
|
|
sizeof(struct reviser_ioctl_info))) {
|
|
LOG_ERR("copy info to user fail\n");
|
|
ret = -EINVAL;
|
|
}
|
|
}
|
|
break;
|
|
case REVISER_IOCTL_GET_TCM:
|
|
|
|
if (copy_from_user(&info, (void *)arg,
|
|
sizeof(struct reviser_ioctl_info))) {
|
|
LOG_ERR("copy info struct fail\n");
|
|
ret = -EINVAL;
|
|
}
|
|
memset(&pg_table, 0, sizeof(struct table_tcm));
|
|
|
|
tcm_page_num = DIV_ROUND_UP(info.page.tcm_size, VLM_BANK_SIZE);
|
|
LOG_DEBUG("tcm_size: %x tcm_page_num %d\n",
|
|
info.page.tcm_size, tcm_page_num);
|
|
if (!reviser_table_get_tcm_sync(reviser_device,
|
|
tcm_page_num, &pg_table)) {
|
|
LOG_DEBUG("page_num: %u\n", pg_table.page_num);
|
|
LOG_DEBUG("table_tcm: %lx\n", pg_table.table_tcm[0]);
|
|
|
|
info.page.tcm_num = pg_table.page_num;
|
|
memcpy(info.page.table_tcm,
|
|
pg_table.table_tcm,
|
|
sizeof(unsigned long) *
|
|
BITS_TO_LONGS(TABLE_TCM_MAX));
|
|
|
|
} else {
|
|
LOG_DEBUG("Get TCM Fail\n");
|
|
ret = -EINVAL;
|
|
}
|
|
|
|
|
|
if (ret == 0) {
|
|
if (copy_to_user(
|
|
(void *)arg, &info,
|
|
sizeof(struct reviser_ioctl_info))) {
|
|
LOG_ERR("copy info to user fail\n");
|
|
ret = -EINVAL;
|
|
}
|
|
}
|
|
break;
|
|
case REVISER_IOCTL_FREE_TCM:
|
|
|
|
if (copy_from_user(&info, (void *)arg,
|
|
sizeof(struct reviser_ioctl_info))) {
|
|
LOG_ERR("copy info struct fail\n");
|
|
ret = -EINVAL;
|
|
}
|
|
if (info.page.tcm_num > VLM_TCM_BANK_MAX) {
|
|
LOG_ERR("tcm_num out of range %d\n", info.page.tcm_num);
|
|
ret = -EINVAL;
|
|
}
|
|
if (info.page.tcm_num !=
|
|
bitmap_weight(info.page.table_tcm,
|
|
VLM_TCM_BANK_MAX)) {
|
|
LOG_ERR("tcm_num %d is unequal to table tcm %lx\n",
|
|
info.page.tcm_num,
|
|
info.page.table_tcm[0]);
|
|
ret = -EINVAL;
|
|
}
|
|
|
|
memset(&pg_table, 0, sizeof(struct table_tcm));
|
|
pg_table.page_num = info.page.tcm_num;
|
|
|
|
LOG_DEBUG("info.page.table_tcm: %lx\n", info.page.table_tcm[0]);
|
|
memcpy(pg_table.table_tcm, info.page.table_tcm,
|
|
sizeof(unsigned long) * BITS_TO_LONGS(4));
|
|
LOG_DEBUG("page_num: %u\n", pg_table.page_num);
|
|
LOG_DEBUG("table_tcm: %lx\n", pg_table.table_tcm[0]);
|
|
if (reviser_table_free_tcm(reviser_device, &pg_table)) {
|
|
LOG_DEBUG("Free TCM Fail\n");
|
|
ret = -EINVAL;
|
|
}
|
|
if (ret == 0) {
|
|
if (copy_to_user(
|
|
(void *)arg, &info,
|
|
sizeof(struct reviser_ioctl_info))) {
|
|
LOG_ERR("copy info to user fail\n");
|
|
ret = -EINVAL;
|
|
}
|
|
}
|
|
break;
|
|
case REVISER_IOCTL_GET_VLM:
|
|
|
|
if (copy_from_user(&info, (void *)arg,
|
|
sizeof(struct reviser_ioctl_info))) {
|
|
LOG_ERR("copy info struct fail\n");
|
|
ret = -EINVAL;
|
|
}
|
|
|
|
if (!reviser_table_get_vlm(reviser_device,
|
|
info.page.tcm_size, info.page.force,
|
|
&ctxID, &tcm_size)) {
|
|
LOG_DEBUG("GET VLM : tcm_size: %x\n", tcm_size);
|
|
LOG_DEBUG("GET VLM : ctxID: %lu\n", ctxID);
|
|
info.page.tcm_size = tcm_size;
|
|
info.page.ID = ctxID;
|
|
} else {
|
|
ret = -EINVAL;
|
|
}
|
|
|
|
if (ret == 0) {
|
|
if (copy_to_user(
|
|
(void *)arg, &info,
|
|
sizeof(struct reviser_ioctl_info))) {
|
|
LOG_ERR("copy info to user fail\n");
|
|
ret = -EINVAL;
|
|
}
|
|
}
|
|
break;
|
|
case REVISER_IOCTL_FREE_VLM:
|
|
|
|
if (copy_from_user(&info, (void *)arg,
|
|
sizeof(struct reviser_ioctl_info))) {
|
|
LOG_ERR("copy info struct fail\n");
|
|
ret = -EINVAL;
|
|
}
|
|
|
|
if (reviser_table_free_vlm(reviser_device, info.page.ID)) {
|
|
|
|
LOG_DEBUG("Free VLM : ctxID: %lu\n", ctxID);
|
|
ret = -EINVAL;
|
|
}
|
|
|
|
if (ret == 0) {
|
|
if (copy_to_user(
|
|
(void *)arg, &info,
|
|
sizeof(struct reviser_ioctl_info))) {
|
|
LOG_ERR("copy info to user fail\n");
|
|
ret = -EINVAL;
|
|
}
|
|
}
|
|
break;
|
|
case REVISER_IOCTL_SWAPIN_VLM:
|
|
|
|
if (copy_from_user(&info, (void *)arg,
|
|
sizeof(struct reviser_ioctl_info))) {
|
|
LOG_ERR("copy info struct fail\n");
|
|
ret = -EINVAL;
|
|
}
|
|
if (reviser_table_swapin_vlm(reviser_device, info.page.ID)) {
|
|
LOG_DEBUG("Swapout ctxID Fail\n");
|
|
ret = -EINVAL;
|
|
}
|
|
|
|
|
|
if (ret == 0) {
|
|
if (copy_to_user(
|
|
(void *)arg, &info,
|
|
sizeof(struct reviser_ioctl_info))) {
|
|
LOG_ERR("copy info to user fail\n");
|
|
ret = -EINVAL;
|
|
}
|
|
}
|
|
break;
|
|
case REVISER_IOCTL_SWAPOUT_VLM:
|
|
|
|
if (copy_from_user(&info, (void *)arg,
|
|
sizeof(struct reviser_ioctl_info))) {
|
|
LOG_ERR("copy info struct fail\n");
|
|
ret = -EINVAL;
|
|
}
|
|
if (reviser_table_swapout_vlm(reviser_device, info.page.ID)) {
|
|
ret = -EINVAL;
|
|
LOG_DEBUG("Swapout ctxID Fail\n");
|
|
}
|
|
|
|
if (ret == 0) {
|
|
if (copy_to_user(
|
|
(void *)arg, &info,
|
|
sizeof(struct reviser_ioctl_info))) {
|
|
LOG_ERR("copy info to user fail\n");
|
|
ret = -EINVAL;
|
|
}
|
|
}
|
|
break;
|
|
default:
|
|
ret = -EINVAL;
|
|
break;
|
|
}
|
|
return ret;
|
|
}
|
|
static long reviser_compat_ioctl(struct file *flip, unsigned int cmd,
|
|
unsigned long arg)
|
|
{
|
|
DEBUG_TAG;
|
|
switch (cmd) {
|
|
case REVISER_IOCTL_SET_BOUNDARY:
|
|
case REVISER_IOCTL_SET_CONTEXT_ID:
|
|
case REVISER_IOCTL_SET_REMAP_TABLE:
|
|
case REVISER_IOCTL_GET_CTXID:
|
|
case REVISER_IOCTL_FREE_CTXID:
|
|
case REVISER_IOCTL_GET_TCM:
|
|
case REVISER_IOCTL_FREE_TCM:
|
|
case REVISER_IOCTL_GET_VLM:
|
|
case REVISER_IOCTL_FREE_VLM:
|
|
case REVISER_IOCTL_SWAPIN_VLM:
|
|
case REVISER_IOCTL_SWAPOUT_VLM:
|
|
{
|
|
return flip->f_op->unlocked_ioctl(flip, cmd,
|
|
(unsigned long)compat_ptr(arg));
|
|
}
|
|
default:
|
|
return -ENOIOCTLCMD;
|
|
/*return vpu_ioctl(flip, cmd, arg);*/
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static const struct of_device_id reviser_of_match[] = {
|
|
{.compatible = "mediatek,apusys_reviser",},
|
|
{/* end of list */},
|
|
};
|
|
|
|
static struct platform_driver reviser_driver = {
|
|
.probe = reviser_probe,
|
|
.remove = reviser_remove,
|
|
.suspend = reviser_suspend,
|
|
.resume = reviser_resume,
|
|
//.pm = apusys_pm_qos,
|
|
.driver = {
|
|
.name = APUSYS_DRV_NAME,
|
|
.owner = THIS_MODULE,
|
|
.of_match_table = reviser_of_match,
|
|
},
|
|
};
|
|
|
|
static int __init reviser_init(void)
|
|
{
|
|
int ret = 0;
|
|
//struct device *dev = NULL;
|
|
|
|
|
|
DEBUG_TAG;
|
|
|
|
if (!apusys_power_check()) {
|
|
LOG_ERR("reviser is disabled by apusys\n");
|
|
return -ENODEV;
|
|
}
|
|
|
|
if (platform_driver_register(&reviser_driver)) {
|
|
LOG_ERR("failed to register APUSYS driver");
|
|
return -ENODEV;
|
|
}
|
|
|
|
|
|
return ret;
|
|
}
|
|
|
|
static void __exit reviser_destroy(void)
|
|
{
|
|
platform_driver_unregister(&reviser_driver);
|
|
}
|
|
|
|
module_init(reviser_init);
|
|
module_exit(reviser_destroy);
|
|
MODULE_DESCRIPTION("MTK APUSYS REVISER Driver");
|
|
MODULE_AUTHOR("Yu-Ren Wang");
|
|
MODULE_LICENSE("GPL");
|