unplugged-kernel/drivers/misc/mediatek/apusys/midware/1.1/mdw_drv.c

510 lines
10 KiB
C

// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (c) 2020 MediaTek Inc.
*/
#include <linux/module.h> /* Needed by all modules */
#include <linux/miscdevice.h>
#include <linux/platform_device.h>
#include <linux/uaccess.h>
#include <linux/slab.h>
#include "apusys_power.h"
#include "apusys_drv.h"
#include "mdw_dbg.h"
#include "mdw_cmn.h"
#include "mdw_cmd.h"
#include "mdw_mem.h"
#include "mdw_usr.h"
#include "mdw_rsc.h"
#include "mdw_sched.h"
#include "mdw_tag.h"
#include "mdw_sysfs.h"
/* define */
#define APUSYS_DEV_NAME "apusys"
/* global variable */
struct device *mdw_device;
struct miscdevice mdw_miscdev;
/* function declaration */
static int mdw_open(struct inode *, struct file *);
static int mdw_release(struct inode *, struct file *);
static long mdw_ioctl(struct file *, unsigned int, unsigned long);
static long mdw_compat_ioctl(struct file *, unsigned int, unsigned long);
static const struct file_operations mdw_fops = {
.open = mdw_open,
.unlocked_ioctl = mdw_ioctl,
.compat_ioctl = mdw_compat_ioctl,
.release = mdw_release,
};
static int mdw_open(struct inode *inode, struct file *filp)
{
struct mdw_usr *u;
u = mdw_usr_create();
if (!u)
return -ENOMEM;
filp->private_data = u;
return 0;
}
static int mdw_release(struct inode *inode, struct file *filp)
{
struct mdw_usr *u;
u = filp->private_data;
mdw_usr_put(u);
return 0;
}
static int mdw_probe(struct platform_device *pdev)
{
int ret = 0;
mdw_drv_info("+\n");
if (!apusys_power_check()) {
mdw_drv_err("apusys disable\n");
return -ENODEV;
}
mdw_device = &pdev->dev;
/* register misc device */
memset(&mdw_miscdev, 0, sizeof(mdw_miscdev));
mdw_miscdev.minor = MISC_DYNAMIC_MINOR;
mdw_miscdev.name = APUSYS_DEV_NAME;
mdw_miscdev.fops = &mdw_fops;
ret = misc_register(&mdw_miscdev);
if (ret) {
mdw_drv_err("register misc device fail(%d)\n", ret);
mdw_device = NULL;
goto out;
}
mdw_dbg_init();
mdw_sysfs_init(mdw_miscdev.this_device);
mdw_tag_init();
mdw_mem_init();
mdw_rsc_init();
mdw_usr_init();
mdw_drv_info("-\n");
out:
return ret;
}
static int mdw_remove(struct platform_device *pdev)
{
mdw_drv_info("+\n");
mdw_usr_exit();
mdw_rsc_exit();
mdw_mem_exit();
mdw_tag_exit();
mdw_sysfs_exit();
mdw_dbg_exit();
misc_deregister(&mdw_miscdev);
mdw_device = NULL;
mdw_drv_info("-\n");
return 0;
}
static int mdw_suspend(struct platform_device *pdev, pm_message_t mesg)
{
return mdw_sched_pause();
}
static int mdw_resume(struct platform_device *pdev)
{
mdw_sched_restart();
return 0;
}
static int mdw_handshake(struct apusys_ioctl_hs *hs)
{
int ret = 0;
hs->magic_num = mdw_cmd_get_magic();
hs->cmd_version = mdw_cmd_get_ver();
switch (hs->type) {
case APUSYS_HANDSHAKE_BEGIN:
hs->begin.mem_support = mdw_mem_get_support();
hs->begin.dev_support = mdw_rsc_get_dev_bmp();
hs->begin.dev_type_max = 64;
if (hs->begin.mem_support & (1UL << APUSYS_MEM_VLM)) {
mdw_mem_get_vlm(&hs->begin.vlm_start,
&hs->begin.vlm_size);
} else {
hs->begin.vlm_start = 0;
hs->begin.vlm_size = 0;
}
mdw_drv_debug("support dev(0x%llx)mem(0x%x/0x%x/%u)\n",
hs->begin.dev_support,
hs->begin.mem_support,
hs->begin.vlm_start,
hs->begin.vlm_size);
break;
case APUSYS_HANDSHAKE_QUERY_DEV:
hs->dev.num = mdw_rsc_get_dev_num(hs->dev.type);
if (hs->dev.num <= 0)
ret = -ENODEV;
mdw_drv_debug("dev(%u) num(%u)\n", hs->dev.type, hs->dev.num);
break;
default:
mdw_drv_debug("wrong handshake type(%d)\n",
hs->type);
ret = -EINVAL;
break;
}
return ret;
}
static long mdw_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
{
int ret = 0;
struct apusys_mem um;
struct apusys_ioctl_cmd ucmd;
struct apusys_ioctl_hs hs;
struct mdw_usr *u;
struct apusys_ioctl_power upwr;
struct apusys_ioctl_ucmd uc;
struct apusys_ioctl_sec us;
u = (struct mdw_usr *)filp->private_data;
switch (cmd) {
case APUSYS_IOCTL_HANDSHAKE:
/* handshaking, pass kernel apusys information */
if (copy_from_user(&hs, (void *)arg,
sizeof(struct apusys_ioctl_hs))) {
mdw_drv_err("copy handshake struct fail\n");
ret = -EINVAL;
goto out;
}
ret = mdw_handshake(&hs);
if (ret)
goto out;
if (copy_to_user((void *)arg, &hs,
sizeof(struct apusys_ioctl_hs))) {
mdw_drv_err("handshake with user fail\n");
ret = -EINVAL;
}
break;
case APUSYS_IOCTL_MEM_ALLOC:
mdw_drv_warn("not support mem alloc\n");
ret = -EINVAL;
break;
case APUSYS_IOCTL_MEM_IMPORT:
if (copy_from_user(&um, (void *)arg,
sizeof(struct apusys_mem))) {
mdw_drv_err("copy mem struct fail\n");
ret = -EINVAL;
goto out;
}
ret = mdw_usr_mem_import(&um, u);
if (ret)
goto out;
if (copy_to_user((void *)arg, &um,
sizeof(struct apusys_mem))) {
mdw_drv_err("copy mem struct to u fail\n");
ret = -EINVAL;
}
break;
case APUSYS_IOCTL_MEM_MAP:
if (copy_from_user(&um, (void *)arg,
sizeof(struct apusys_mem))) {
mdw_drv_err("copy mem struct fail\n");
ret = -EINVAL;
goto out;
}
ret = mdw_usr_mem_map(&um, u);
if (ret)
goto out;
if (copy_to_user((void *)arg, &um,
sizeof(struct apusys_mem))) {
mdw_drv_err("copy mem struct to u fail\n");
ret = -EINVAL;
}
break;
case APUSYS_IOCTL_MEM_FREE:
case APUSYS_IOCTL_MEM_UNIMPORT:
case APUSYS_IOCTL_MEM_UNMAP:
if (copy_from_user(&um, (void *)arg,
sizeof(struct apusys_mem))) {
mdw_drv_err("copy mem struct fail\n");
ret = -EINVAL;
goto out;
}
ret = mdw_usr_mem_free(&um, u);
break;
case APUSYS_IOCTL_RUN_CMD_SYNC:
if (copy_from_user(&ucmd, (void *)arg,
sizeof(struct apusys_ioctl_cmd))) {
mdw_drv_err("copy cmd struct fail\n");
ret = -EINVAL;
goto out;
}
ret = mdw_usr_run_cmd_sync(u, &ucmd);
break;
case APUSYS_IOCTL_RUN_CMD_ASYNC:
if (copy_from_user(&ucmd, (void *)arg,
sizeof(struct apusys_ioctl_cmd))) {
mdw_drv_err("copy cmd struct fail\n");
ret = -EINVAL;
goto out;
}
ret = mdw_usr_run_cmd_async(u, &ucmd);
if (copy_to_user((void *)arg, &ucmd,
sizeof(struct apusys_ioctl_cmd))) {
mdw_drv_err("copy cmd struct to u fail\n");
ret = -EINVAL;
}
break;
case APUSYS_IOCTL_WAIT_CMD:
if (copy_from_user(&ucmd, (void *)arg,
sizeof(struct apusys_ioctl_cmd))) {
mdw_drv_err("copy cmd struct fail\n");
ret = -EINVAL;
goto out;
}
ret = mdw_usr_wait_cmd(u, &ucmd);
break;
case APUSYS_IOCTL_SET_POWER:
ret = copy_from_user(&upwr, (void *)arg,
sizeof(struct apusys_ioctl_power));
if (ret) {
mdw_drv_err("copy power struct fail\n");
goto out;
}
ret = mdw_usr_set_pwr(&upwr);
break;
case APUSYS_IOCTL_DEVICE_ALLOC:
ret = -EINVAL;
break;
case APUSYS_IOCTL_DEVICE_FREE:
ret = -EINVAL;
break;
case APUSYS_IOCTL_FW_LOAD:
ret = -EINVAL;
mdw_drv_warn("not support fw load\n");
break;
case APUSYS_IOCTL_FW_UNLOAD:
ret = -EINVAL;
mdw_drv_warn("not suppot fw unload\n");
break;
case APUSYS_IOCTL_USER_CMD:
ret = copy_from_user(&uc, (void *)arg,
sizeof(struct apusys_ioctl_ucmd));
if (ret) {
mdw_drv_err("copy ucmd struct fail\n");
goto out;
}
ret = mdw_usr_ucmd(&uc);
break;
case APUSYS_IOCTL_SEC_DEVICE_LOCK:
ret = copy_from_user(&us, (void *)arg,
sizeof(struct apusys_ioctl_sec));
if (ret) {
mdw_drv_err("copy sec struct fail\n");
goto out;
}
ret = mdw_usr_dev_sec_alloc(us.dev_type, u);
break;
case APUSYS_IOCTL_SEC_DEVICE_UNLOCK:
ret = copy_from_user(&us, (void *)arg,
sizeof(struct apusys_ioctl_sec));
if (ret) {
mdw_drv_err("copy sec struct fail\n");
goto out;
}
if (us.dev_type >= APUSYS_DEVICE_MAX || us.dev_type < 0) {
mdw_drv_err("invalid sec type(%d)\n", us.dev_type);
ret = -EINVAL;
goto out;
}
ret = mdw_usr_dev_sec_free(us.dev_type, u);
break;
default:
ret = -EINVAL;
break;
}
out:
mdw_flw_debug("cmd(%u)ret(%d)\n", _IOC_NR(cmd), ret);
return ret;
}
static long mdw_compat_ioctl(struct file *flip, unsigned int cmd,
unsigned long arg)
{
switch (cmd) {
case APUSYS_IOCTL_HANDSHAKE:
case APUSYS_IOCTL_MEM_ALLOC:
case APUSYS_IOCTL_MEM_FREE:
case APUSYS_IOCTL_MEM_IMPORT:
case APUSYS_IOCTL_MEM_UNIMPORT:
case APUSYS_IOCTL_MEM_CTL:
case APUSYS_IOCTL_RUN_CMD_SYNC:
case APUSYS_IOCTL_RUN_CMD_ASYNC:
case APUSYS_IOCTL_WAIT_CMD:
case APUSYS_IOCTL_SET_POWER:
case APUSYS_IOCTL_DEVICE_ALLOC:
case APUSYS_IOCTL_DEVICE_FREE:
case APUSYS_IOCTL_FW_LOAD:
case APUSYS_IOCTL_FW_UNLOAD:
case APUSYS_IOCTL_USER_CMD:
case APUSYS_IOCTL_SEC_DEVICE_LOCK:
case APUSYS_IOCTL_SEC_DEVICE_UNLOCK:
case APUSYS_IOCTL_MEM_MAP:
case APUSYS_IOCTL_MEM_UNMAP:
{
return flip->f_op->unlocked_ioctl(flip, cmd,
(unsigned long)compat_ptr(arg));
}
default:
return -ENOIOCTLCMD;
/*return vpu_ioctl(flip, cmd, arg);*/
}
return 0;
}
#ifdef CONFIG_PM
static int mdw_pm_suspend(struct device *device)
{
struct platform_device *pdev = to_platform_device(device);
WARN_ON(pdev == NULL);
return mdw_suspend(pdev, PMSG_SUSPEND);
}
static int mdw_pm_resume(struct device *device)
{
struct platform_device *pdev = to_platform_device(device);
WARN_ON(pdev == NULL);
return mdw_resume(pdev);
}
static int mdw_pm_restore_noirq(struct device *device)
{
return 0;
}
static const struct dev_pm_ops mdw_pm_ops = {
.suspend = mdw_pm_suspend,
.resume = mdw_pm_resume,
.freeze = mdw_pm_suspend,
.thaw = mdw_pm_resume,
.poweroff = mdw_pm_suspend,
.restore = mdw_pm_resume,
.restore_noirq = mdw_pm_restore_noirq,
};
#endif
static struct platform_driver mdw_drv = {
.probe = mdw_probe,
.remove = mdw_remove,
.suspend = mdw_suspend,
.resume = mdw_resume,
.driver = {
.name = APUSYS_DEV_NAME,
.owner = THIS_MODULE,
#ifdef CONFIG_PM
.pm = &mdw_pm_ops,
#endif
},
};
static void mdw_dev_release(struct device *dev)
{
}
static struct platform_device mdw_dev = {
.name = APUSYS_DEV_NAME,
.id = -1,
.dev = {
.release = mdw_dev_release,
},
};
static int __init mdw_init(void)
{
if (!apusys_power_check()) {
mdw_drv_err("apusys disable\n");
return -ENODEV;
}
if (platform_driver_register(&mdw_drv)) {
mdw_drv_err("failed to register apusys midware driver");
return -ENODEV;
}
if (platform_device_register(&mdw_dev)) {
mdw_drv_err("failed to register apusys midware device");
platform_driver_unregister(&mdw_drv);
return -ENODEV;
}
return 0;
}
static void __exit mdw_exit(void)
{
platform_driver_unregister(&mdw_drv);
platform_device_unregister(&mdw_dev);
}
module_init(mdw_init);
module_exit(mdw_exit);
MODULE_DESCRIPTION("MTK APUSys Middleware Driver");
MODULE_AUTHOR("SPT1");
MODULE_LICENSE("GPL");