kernel-brax3-ubuntu-touch/drivers/misc/mediatek/apusys/reviser/2.0/common/reviser_drv.c
erascape f319b992b1 kernel-5.15: Initial import brax3 UT kernel
* halium configs enabled

Signed-off-by: erascape <erascape@proton.me>
2025-09-23 15:17:10 +00:00

725 lines
16 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>
#include <linux/rpmsg.h>
#include "reviser_plat.h"
#include "reviser_device.h"
#if IS_ENABLED(CONFIG_OF)
#include <linux/of.h>
#include <linux/of_platform.h>
#endif
#include "apusys_core.h"
#include "apu.h"
#include "apu_config.h"
#include "reviser_drv.h"
#include "reviser_cmn.h"
#include "reviser_dbg.h"
#include "reviser_table_mgt.h"
#include "apusys_power.h"
#include "reviser_mem.h"
#include "reviser_power.h"
#include "reviser_hw_mgt.h"
#include "reviser_remote.h"
#include "reviser_remote_cmd.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_rdv;
static struct task_struct *mem_task;
static struct apusys_core_info *g_apusys;
/* function declaration */
static int reviser_open(struct inode *, struct file *);
static int reviser_release(struct inode *, struct file *);
static int reviser_memory_func(void *arg);
static int reviser_init_para(struct reviser_dev_info *rdv);
static int reviser_get_addr(struct platform_device *pdev, void **reg, int num,
unsigned int *base, unsigned int *size);
static int reviser_map_dts(struct platform_device *pdev);
static int reviser_unmap_dts(struct platform_device *pdev);
static int reviser_create_node(struct platform_device *pdev);
static int reviser_delete_node(void *drvinfo);
static void reviser_power_on_cb(void *para);
static void reviser_power_off_cb(void *para);
static int reviser_memory_func(void *arg)
{
struct reviser_dev_info *rdv;
rdv = (struct reviser_dev_info *) arg;
if (reviser_dram_remap_init(rdv)) {
LOG_ERR("Could not set memory for reviser\n");
return -ENOMEM;
}
LOG_INFO("reviser memory init\n");
return 0;
}
static int reviser_rprmsg_memory_func(void *arg)
{
struct reviser_dev_info *rdv;
int ret = 0;
uint32_t i;
rdv = (struct reviser_dev_info *) arg;
ret = reviser_remote_handshake(rdv, NULL);
if (ret) {
LOG_ERR("Remote Handshake fail %d\n", ret);
goto out;
}
ret = reviser_memory_func(arg);
if (ret) {
LOG_ERR("reviser memory fail\n");
goto out;
}
for (i = 0; i < rdv->plat.dram_max; i++) {
ret = reviser_remote_set_hw_default_iova(rdv, i, rdv->plat.dram[i]);
if (ret) {
LOG_ERR("reviser_remote_set_hw_default_iova fail %d\n", ret);
goto out;
}
}
LOG_INFO("reviser memory init\n");
out:
return ret;
}
int reviser_set_init_info(struct mtk_apu *apu)
{
struct reviser_dev_info *rdv;
struct reviser_init_info *rv_info;
int i = 0;
rdv = g_rdv;
if (!rdv) {
LOG_ERR("No reviser_dev_info!\n");
return -EINVAL;
}
rv_info = (struct reviser_init_info *)
get_apu_config_user_ptr(apu->conf_buf, eREVISER_INIT_INFO);
memset((void *)rv_info, 0, sizeof(struct reviser_init_info));
// if (reviser_dram_remap_init(rdv)) {
// LOG_ERR("Could not set memory for reviser\n");
// return -ENOMEM;
// }
rv_info->boundary = rdv->plat.boundary;
for (i = 0; i < rdv->plat.dram_max; i++)
rv_info->dram[i] = rdv->plat.dram[i];
LOG_INFO("reviser info init\n");
return 0;
}
EXPORT_SYMBOL(reviser_set_init_info);
static void reviser_power_on_cb(void *para)
{
unsigned long flags;
if (g_rdv == NULL) {
LOG_ERR("Not Found rdv\n");
return;
}
spin_lock_irqsave(&g_rdv->lock.lock_power, flags);
g_rdv->power.power = true;
spin_unlock_irqrestore(&g_rdv->lock.lock_power, flags);
reviser_mgt_set_int(g_rdv, 1);
if (reviser_mgt_set_boundary(g_rdv, g_rdv->plat.boundary)) {
LOG_ERR("Set Boundary Fail\n");
return;
}
reviser_mgt_dmp_boundary(g_rdv, NULL);
if (reviser_mgt_set_default(g_rdv)) {
LOG_ERR("Set Default IOVA Fail\n");
return;
}
LOG_INFO("reviser power-on callback Done\n");
}
static void reviser_power_off_cb(void *para)
{
unsigned long flags;
if (g_rdv == NULL) {
LOG_ERR("Not Found rdv\n");
return;
}
reviser_mgt_set_int(g_rdv, 0);
spin_lock_irqsave(&g_rdv->lock.lock_power, flags);
g_rdv->power.power = false;
spin_unlock_irqrestore(&g_rdv->lock.lock_power, flags);
LOG_INFO("reviser power-off callback Done\n");
}
static const struct file_operations reviser_fops = {
.open = reviser_open,
.release = reviser_release,
};
static int reviser_open(struct inode *inode, struct file *filp)
{
struct reviser_dev_info *rdv;
DEBUG_TAG;
rdv = container_of(inode->i_cdev,
struct reviser_dev_info, reviser_cdev);
filp->private_data = rdv;
LOG_DBG_RVR_FLW("rdv %p\n", rdv);
LOG_DBG_RVR_FLW("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_init_para(struct reviser_dev_info *rdv)
{
mutex_init(&rdv->lock.mutex_ctx);
mutex_init(&rdv->lock.mutex_tcm);
mutex_init(&rdv->lock.mutex_ctx_pgt);
mutex_init(&rdv->lock.mutex_remap);
mutex_init(&rdv->lock.mutex_power);
init_waitqueue_head(&rdv->lock.wait_ctx);
init_waitqueue_head(&rdv->lock.wait_tcm);
spin_lock_init(&rdv->lock.lock_power);
spin_lock_init(&rdv->lock.lock_dump);
return 0;
}
static int reviser_get_addr(struct platform_device *pdev, void **reg, int num,
unsigned int *base, unsigned int *size)
{
struct resource *res;
res = platform_get_resource(pdev, IORESOURCE_MEM, num);
if (!res) {
dev_info(&pdev->dev, "invalid address (num = %d)\n", num);
return -ENODEV;
}
if (res->start > res->end) {
*base = res->start;
*size = 0;
*reg = NULL;
dev_info(&pdev->dev,
"(num = %d) at 0x%08lx map 0x%08lx base:0x%08lx size:0x%08lx\n",
num, (unsigned long __force)res->start,
(unsigned long __force)res->end, *base, *size);
return -ENOMEM;
}
*reg = ioremap(res->start, res->end - res->start + 1);
if (*reg == 0) {
dev_info(&pdev->dev,
"could not allocate iomem (num = %d)\n", num);
return -EIO;
}
*base = res->start;
*size = res->end - res->start + 1;
dev_info(&pdev->dev,
"(num = %d) at 0x%08lx map 0x%08lx base:0x%08lx size:0x%08lx\n",
num, (unsigned long __force)res->start,
(unsigned long __force)res->end, *base, *size);
return 0;
}
static int reviser_map_dts(struct platform_device *pdev)
{
int ret = 0;
struct reviser_dev_info *rdv = platform_get_drvdata(pdev);
uint32_t dram_offset = 0;
uint32_t slb_size = 0;
struct device_node *slb_node;
DEBUG_TAG;
if (!rdv) {
LOG_ERR("No reviser_dev_info!\n");
return -EINVAL;
}
if (reviser_get_addr(pdev, &rdv->rsc.ctrl.base, 0,
&rdv->rsc.ctrl.addr, &rdv->rsc.ctrl.size)) {
LOG_ERR("invalid address\n");
ret = -ENODEV;
goto out;
}
if (reviser_get_addr(pdev, &rdv->rsc.vlm.base, 1,
&rdv->rsc.vlm.addr, &rdv->rsc.vlm.size)) {
LOG_ERR("invalid address\n");
ret = -ENODEV;
goto free_ctrl;
}
rdv->plat.vlm_addr = rdv->rsc.vlm.addr;
rdv->plat.vlm_size = rdv->rsc.vlm.size;
rdv->plat.vlm_bank_max = rdv->plat.vlm_size / rdv->plat.bank_size;
ret = reviser_get_addr(pdev, &rdv->rsc.pool[REVSIER_POOL_TCM].base,
2, &rdv->rsc.pool[REVSIER_POOL_TCM].addr,
&rdv->rsc.pool[REVSIER_POOL_TCM].size);
if (ret && (ret != -ENOMEM)) {
LOG_ERR("invalid address\n");
ret = -ENODEV;
goto free_vlm;
} else {
ret = 0;
LOG_INFO("TCM Empty\n");
}
rdv->plat.vlm_bank_max = rdv->plat.vlm_size / rdv->plat.bank_size;
rdv->plat.pool_addr[REVSIER_POOL_TCM] = rdv->rsc.pool[REVSIER_POOL_TCM].addr;
rdv->plat.pool_size[REVSIER_POOL_TCM] = rdv->rsc.pool[REVSIER_POOL_TCM].size;
rdv->plat.pool_bank_max[REVSIER_POOL_TCM] =
rdv->plat.pool_size[REVSIER_POOL_TCM] / rdv->plat.bank_size;
rdv->plat.pool_max++;
if (reviser_get_addr(pdev, &rdv->rsc.isr.base, 3,
&rdv->rsc.isr.addr, &rdv->rsc.isr.size)) {
LOG_ERR("invalid address\n");
ret = -ENODEV;
goto free_tcm;
}
rdv->rsc.dram.addr = rdv->plat.dram[0];
LOG_DBG_RVR_FLW("rdv->rsc.dram.addr %x\n", rdv->rsc.dram.addr);
if (of_property_read_u32(pdev->dev.of_node,
"boundary", &rdv->plat.boundary)) {
LOG_ERR("Invalid boundary %d\n", ret);
goto free_int;
}
LOG_DBG_RVR_FLW("boundary: %08xh\n", rdv->plat.boundary);
if (of_property_read_u32(pdev->dev.of_node,
"default-dram", &dram_offset)) {
LOG_ERR("Invalid dram_offset %d\n", ret);
goto free_int;
}
LOG_DBG_RVR_FLW("dram_offset: %08xh\n", dram_offset);
if (dram_offset > 0)
rdv->plat.fix_dram = dram_offset;
else
rdv->plat.fix_dram = 0;
slb_node = of_find_compatible_node(
NULL, NULL, "mediatek,mtk-slbc");
if (slb_node) {
of_property_read_u32(slb_node,
"apu", &slb_size);
rdv->rsc.pool[REVSIER_POOL_SLBS].size = slb_size;
rdv->plat.pool_type[REVSIER_POOL_SLBS] = REVISER_MEM_TYPE_SLBS;
rdv->plat.pool_size[REVSIER_POOL_SLBS] = rdv->rsc.pool[REVSIER_POOL_SLBS].size;
rdv->plat.pool_max++;
LOG_INFO("APU-slb size: 0x%x\n", rdv->plat.pool_size[REVSIER_POOL_SLBS]);
}
return ret;
free_int:
iounmap(rdv->rsc.isr.base);
free_tcm:
if (!rdv->rsc.pool[0].base)
iounmap(rdv->rsc.pool[0].base);
free_vlm:
iounmap(rdv->rsc.vlm.base);
free_ctrl:
iounmap(rdv->rsc.ctrl.base);
out:
return ret;
}
static int reviser_unmap_dts(struct platform_device *pdev)
{
int ret = 0;
struct reviser_dev_info *rdv = platform_get_drvdata(pdev);
DEBUG_TAG;
if (!rdv) {
LOG_ERR("No reviser_dev_info!\n");
return -EINVAL;
}
if (!rdv->rsc.pool[0].base)
iounmap(rdv->rsc.pool[0].base);
iounmap(rdv->rsc.vlm.base);
iounmap(rdv->rsc.ctrl.base);
return ret;
}
static int reviser_create_node(struct platform_device *pdev)
{
int ret = 0;
struct device *dev = &pdev->dev;
struct reviser_dev_info *rdv = platform_get_drvdata(pdev);
if (!rdv) {
LOG_ERR("No reviser_dev_info!\n");
return -EINVAL;
}
/* get major */
ret = alloc_chrdev_region(&rdv->reviser_devt,
0, 1, APUSYS_DRV_NAME);
if (ret < 0) {
LOG_ERR("alloc_chrdev_region failed, %d\n", ret);
goto out;
}
/* Attach file operation. */
cdev_init(&rdv->reviser_cdev, &reviser_fops);
rdv->reviser_cdev.owner = THIS_MODULE;
/* Add to system */
ret = cdev_add(&rdv->reviser_cdev,
rdv->reviser_devt, 1);
if (ret < 0) {
LOG_ERR("Attach 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, rdv->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;
}
return 0;
free_class:
/* Release class */
class_destroy(reviser_class);
free_cdev_add:
/* Release char driver */
cdev_del(&rdv->reviser_cdev);
free_chrdev_region:
unregister_chrdev_region(rdv->reviser_devt, 1);
out:
return ret;
}
static int reviser_delete_node(void *drvinfo)
{
int ret = 0;
struct reviser_dev_info *rdv = NULL;
DEBUG_TAG;
if (drvinfo == NULL) {
LOG_ERR("invalid argument\n");
return -EINVAL;
}
rdv = (struct reviser_dev_info *)drvinfo;
device_destroy(reviser_class, rdv->reviser_devt);
class_destroy(reviser_class);
cdev_del(&rdv->reviser_cdev);
unregister_chrdev_region(rdv->reviser_devt, 1);
return ret;
}
static int reviser_probe(struct platform_device *pdev)
{
//reviser_device_init();
int ret = 0;
struct device *dev = &pdev->dev;
struct reviser_dev_info *rdv;
DEBUG_TAG;
g_rdv = NULL;
mem_task = NULL;
rdv = devm_kzalloc(dev, sizeof(*rdv), GFP_KERNEL);
if (!rdv)
return -ENOMEM;
rdv->dev = &pdev->dev;
platform_set_drvdata(pdev, rdv);
dev_set_drvdata(dev, rdv);
rdv->init_done = false;
reviser_init_para(rdv);
if (reviser_plat_init(pdev)) {
dev_info(dev, "platform init failed\n");
return -ENODEV;
}
//memset(&g_reviser_info, 0, sizeof(struct reviser_dev_info));
if (reviser_create_node(pdev)) {
LOG_ERR("reviser_create_node fail\n");
return -ENODEV;
}
if (reviser_map_dts(pdev)) {
LOG_ERR("reviser_map_dts fail\n");
ret = -ENODEV;
goto free_node;
}
if (rdv->plat.fix_dram) {
mem_task = kthread_run(reviser_memory_func, rdv, "reviser");
if (mem_task == NULL) {
LOG_ERR("create kthread(mem) fail\n");
ret = -ENOMEM;
goto free_map;
}
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_dbg;
}
apu_power_device_register(REVISER, pdev);
}
if (reviser_table_init(rdv)) {
LOG_ERR("table init fail\n");
ret = -EINVAL;
goto free_map;
}
reviser_dbg_init(rdv, g_apusys->dbg_root);
g_rdv = rdv;
/* Workaround for power all on mode*/
//reviser_power_on(rdv);
rdv->init_done = true;
LOG_INFO("probe done\n");
return ret;
free_dbg:
reviser_dbg_destroy(rdv);
free_map:
reviser_unmap_dts(pdev);
free_node:
reviser_delete_node(rdv);
return ret;
}
static int reviser_remove(struct platform_device *pdev)
{
struct reviser_dev_info *rdv = platform_get_drvdata(pdev);
DEBUG_TAG;
if (rdv->plat.fix_dram) {
apu_power_device_unregister(REVISER);
apu_power_callback_device_unregister(REVISOR);
}
reviser_table_uninit(rdv);
reviser_dbg_destroy(rdv);
if (mem_task) {
reviser_dram_remap_destroy(rdv);
mem_task = NULL;
}
reviser_unmap_dts(pdev);
reviser_delete_node(rdv);
g_rdv = NULL;
LOG_INFO("remove done\n");
return 0;
}
static struct platform_driver reviser_driver = {
.probe = reviser_probe,
.remove = reviser_remove,
.driver = {
.name = APUSYS_DRV_NAME,
.owner = THIS_MODULE,
},
};
static int reviser_rpmsg_cb(struct rpmsg_device *rpdev, void *data,
int len, void *priv, u32 src)
{
int ret = 0;
//LOG_INFO("reviser len=%d, priv=%p, src=%d\n", len, priv, src);
ret = reviser_remote_rx_cb(data, len);
return ret;
}
static int reviser_rpmsg_probe(struct rpmsg_device *rpdev)
{
struct reviser_dev_info *rdv;
int ret = 0;
LOG_INFO("name=%s, src=%d\n", rpdev->id.name, rpdev->src);
if (!g_rdv) {
LOG_ERR("No Reviser Driver Init\n");
return -ENODEV;
}
rdv = g_rdv;
rdv->rpdev = rpdev;
rdv->power.power = true;
reviser_remote_init();
mem_task = kthread_run(reviser_rprmsg_memory_func, rdv, "reviser");
if (mem_task == NULL) {
LOG_ERR("create kthread(mem) fail\n");
ret = -ENOMEM;
goto out;
}
dev_set_drvdata(&rpdev->dev, rdv);
/* Workaround for power all on mode*/
//reviser_power_on(rdv);
LOG_INFO("Done\n");
out:
return ret;
}
static void reviser_rpmsg_remove(struct rpmsg_device *rpdev)
{
reviser_remote_exit();
LOG_INFO("Done\n");
}
static const struct of_device_id reviser_rpmsg_of_match[] = {
{ .compatible = "mediatek,apu-reviser-rpmsg", },
{ },
};
static struct rpmsg_driver reviser_rpmsg_driver = {
.drv = {
.name = "apu-reviser-rpmsg",
.of_match_table = reviser_rpmsg_of_match,
},
.probe = reviser_rpmsg_probe,
.remove = reviser_rpmsg_remove,
.callback = reviser_rpmsg_cb,
};
int reviser_init(struct apusys_core_info *info)
{
int ret = 0;
//struct device *dev = NULL;
DEBUG_TAG;
g_apusys = info;
reviser_driver.driver.of_match_table = reviser_get_of_device_id();
if (platform_driver_register(&reviser_driver)) {
LOG_ERR("failed to register APUSYS driver");
return -ENODEV;
}
if (register_rpmsg_driver(&reviser_rpmsg_driver)) {
LOG_ERR("failed to register RMPSG driver");
return -ENODEV;
}
return ret;
}
void reviser_exit(void)
{
unregister_rpmsg_driver(&reviser_rpmsg_driver);
platform_driver_unregister(&reviser_driver);
}