// SPDX-License-Identifier: GPL-2.0 /* * Copyright (c) 2020 MediaTek Inc. */ #include /* Needed by all modules */ #include #include #include #include #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");