mirror of
https://github.com/physwizz/a155-U-u1.git
synced 2025-09-26 19:04:54 +00:00
403 lines
8.7 KiB
C
403 lines
8.7 KiB
C
// SPDX-License-Identifier: GPL-2.0
|
|
/*
|
|
* Copyright (c) 2019 MediaTek Inc.
|
|
*/
|
|
#include <linux/module.h>
|
|
#include <linux/slab.h>
|
|
#include <linux/cdev.h>
|
|
#include <linux/of_device.h>
|
|
#include <linux/types.h>
|
|
#include <linux/dma-direct.h>
|
|
|
|
#include <apusys_device.h>
|
|
|
|
#include <common/mdla_driver.h>
|
|
#include <common/mdla_device.h>
|
|
#include <common/mdla_power_ctrl.h>
|
|
#include <common/mdla_cmd_proc.h>
|
|
#include <common/mdla_ioctl.h>
|
|
|
|
#include <utilities/mdla_profile.h>
|
|
#include <utilities/mdla_util.h>
|
|
|
|
static struct apusys_device *apusys_dev_mdla;
|
|
static struct apusys_device *apusys_dev_mdla_rt;
|
|
|
|
static bool apusys_mdla_rt_support(void)
|
|
{
|
|
return DEVICE_MDLA != DEVICE_MDLA_RT;
|
|
}
|
|
|
|
static int apusys_mdla_handler(int type, void *hnd, struct apusys_device *dev)
|
|
{
|
|
int ret = 0;
|
|
struct mdla_dev *mdla_info;
|
|
struct apusys_cmd_handle *cmd_hnd;
|
|
|
|
if (unlikely(!dev || !(dev->private)))
|
|
return -EINVAL;
|
|
|
|
mdla_info = (struct mdla_dev *)dev->private;
|
|
|
|
if (dev->dev_type != DEVICE_MDLA)
|
|
return -EINVAL;
|
|
|
|
if (unlikely(core_id_is_invalid(mdla_info->mdla_id)))
|
|
return -EINVAL;
|
|
|
|
switch (type) {
|
|
case APUSYS_CMD_POWERON:
|
|
ret = mdla_pwr_ops_get()->on(mdla_info->mdla_id, true);
|
|
break;
|
|
case APUSYS_CMD_POWERDOWN:
|
|
ret = mdla_pwr_ops_get()->off(mdla_info->mdla_id, 0, true);
|
|
break;
|
|
case APUSYS_CMD_RESUME:
|
|
break;
|
|
case APUSYS_CMD_SUSPEND:
|
|
ret = mdla_pwr_ops_get()->off(mdla_info->mdla_id, 1, true);
|
|
break;
|
|
case APUSYS_CMD_EXECUTE:
|
|
cmd_hnd = hnd;
|
|
|
|
if (unlikely(!cmd_hnd || cmd_hnd->num_cmdbufs < MIN_CMDBUF_NUM))
|
|
return -ENODEV;
|
|
|
|
if (unlikely(!cmd_hnd->cmdbufs[CMD_INFO_IDX].kva))
|
|
return -EINVAL;
|
|
|
|
ret = mdla_cmd_ops_get()->run_sync(
|
|
(struct mdla_run_cmd_sync *)cmd_hnd
|
|
->cmdbufs[CMD_INFO_IDX]
|
|
.kva,
|
|
mdla_info, cmd_hnd, MDLA_LOW_PRIORITY);
|
|
break;
|
|
case APUSYS_CMD_PREEMPT:
|
|
ret = -EINVAL;
|
|
break;
|
|
default:
|
|
ret = -EINVAL;
|
|
break;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int apusys_mdla_rt_handler(int type, void *hnd,
|
|
struct apusys_device *dev)
|
|
{
|
|
int ret;
|
|
struct apusys_cmd_handle *cmd_hnd;
|
|
struct mdla_dev *mdla_info;
|
|
|
|
if (type != APUSYS_CMD_EXECUTE)
|
|
return 0;
|
|
|
|
if (unlikely(dev->dev_type != DEVICE_MDLA_RT))
|
|
return -EINVAL;
|
|
|
|
if (unlikely(!hnd || !dev || !(dev->private)))
|
|
return -EINVAL;
|
|
|
|
cmd_hnd = hnd;
|
|
mdla_info = (struct mdla_dev *)dev->private;
|
|
|
|
if (unlikely(cmd_hnd->num_cmdbufs < MIN_CMDBUF_NUM))
|
|
return -ENODEV;
|
|
|
|
if (unlikely(!cmd_hnd->cmdbufs[CMD_INFO_IDX].kva))
|
|
return -EINVAL;
|
|
|
|
if (unlikely(core_id_is_invalid(mdla_info->mdla_id)))
|
|
return -EINVAL;
|
|
|
|
ret = mdla_cmd_ops_get()->run_sync(
|
|
(struct mdla_run_cmd_sync *)cmd_hnd->cmdbufs[CMD_INFO_IDX].kva,
|
|
mdla_info, cmd_hnd, MDLA_HIGH_PRIORITY);
|
|
|
|
return ret;
|
|
}
|
|
|
|
#define MDLA_DEVICE_NAME "mdlactl"
|
|
#define MDLA_CLASS_NAME "mdla"
|
|
|
|
static dev_t mdlactl_dev_num;
|
|
static struct cdev *mdlactl_cdev;
|
|
static struct class *mdlactl_class;
|
|
static struct device *mdlactl_device;
|
|
|
|
static void mdla_drv_unregister_char_dev(void)
|
|
{
|
|
cdev_del(mdlactl_cdev);
|
|
unregister_chrdev_region(mdlactl_dev_num, 1);
|
|
}
|
|
|
|
static int mdla_drv_register_char_dev(struct device *dev)
|
|
{
|
|
int ret = 0;
|
|
|
|
/* Register a range of char device numbers */
|
|
ret = alloc_chrdev_region(&mdlactl_dev_num, 0, 1, MDLA_DEVICE_NAME);
|
|
if (ret < 0) {
|
|
dev_info(dev, "alloc_chrdev_region failed, %d\n", ret);
|
|
goto out;
|
|
}
|
|
|
|
/* Allocate a char device structure */
|
|
mdlactl_cdev = cdev_alloc();
|
|
if (!mdlactl_cdev) {
|
|
dev_info(dev, "cdev_alloc failed\n");
|
|
ret = -ENOMEM;
|
|
goto err_cdev;
|
|
}
|
|
|
|
cdev_init(mdlactl_cdev, mdla_ioctl_get_fops());
|
|
|
|
mdlactl_cdev->owner = THIS_MODULE;
|
|
|
|
/* Add a char device to the system */
|
|
ret = cdev_add(mdlactl_cdev, mdlactl_dev_num, 1);
|
|
if (ret < 0) {
|
|
dev_info(dev, "Attatch file operation failed, %d\n", ret);
|
|
goto err_cdev_add;
|
|
}
|
|
|
|
dev_info(dev, "Registered cdev with major/minor number %d\n",
|
|
mdlactl_dev_num);
|
|
|
|
return 0;
|
|
|
|
err_cdev_add:
|
|
cdev_del(mdlactl_cdev);
|
|
err_cdev:
|
|
unregister_chrdev_region(mdlactl_dev_num, 1);
|
|
out:
|
|
return ret;
|
|
}
|
|
|
|
int mdla_drv_create_device_node(struct device *dev)
|
|
{
|
|
int ret = 0;
|
|
|
|
if (mdlactl_cdev) {
|
|
ret = -1;
|
|
dev_info(dev, "%s() Has registered character device!\n",
|
|
__func__);
|
|
goto out;
|
|
}
|
|
|
|
/* 1. Register character driver */
|
|
ret = mdla_drv_register_char_dev(dev);
|
|
if (ret < 0)
|
|
goto out;
|
|
|
|
/* 2. Create a class structure. It's used in calls to device_create() */
|
|
mdlactl_class = class_create(THIS_MODULE, MDLA_CLASS_NAME);
|
|
if (IS_ERR(mdlactl_class)) {
|
|
dev_info(dev, "Failed to register device class\n");
|
|
ret = PTR_ERR(mdlactl_class);
|
|
goto err_class;
|
|
}
|
|
|
|
/* 3. Creates a device and registers it with sysfs */
|
|
mdlactl_device = device_create(mdlactl_class, NULL, mdlactl_dev_num,
|
|
NULL, MDLA_DEVICE_NAME);
|
|
if (IS_ERR(mdlactl_device)) {
|
|
dev_info(dev, "Failed to create the device\n");
|
|
ret = PTR_ERR(mdlactl_device);
|
|
goto err_devive;
|
|
}
|
|
|
|
/* 4. Init DMA from of */
|
|
of_dma_configure(mdlactl_device, NULL, true);
|
|
|
|
/* 5. Set DMA mask */
|
|
ret = dma_get_mask(mdlactl_device);
|
|
if (ret < 0 || ret != DMA_BIT_MASK(32)) {
|
|
ret = dma_set_mask_and_coherent(mdlactl_device,
|
|
DMA_BIT_MASK(32));
|
|
if (ret)
|
|
dev_info(dev, "MDLA: set DMA mask failed: %d\n", ret);
|
|
}
|
|
|
|
return 0;
|
|
|
|
err_devive:
|
|
class_destroy(mdlactl_class);
|
|
err_class:
|
|
mdla_drv_unregister_char_dev();
|
|
out:
|
|
return ret;
|
|
}
|
|
|
|
void mdla_drv_destroy_device_node(void)
|
|
{
|
|
if (mdlactl_device) {
|
|
device_destroy(mdlactl_class, mdlactl_dev_num);
|
|
mdlactl_device = NULL;
|
|
}
|
|
|
|
if (mdlactl_class) {
|
|
class_destroy(mdlactl_class);
|
|
mdlactl_class = NULL;
|
|
}
|
|
|
|
if (mdlactl_cdev) {
|
|
mdla_drv_unregister_char_dev();
|
|
mdlactl_cdev = NULL;
|
|
}
|
|
}
|
|
|
|
static int mdla_probe(struct platform_device *pdev)
|
|
{
|
|
int i, ret = 0;
|
|
struct device *dev = &pdev->dev;
|
|
struct apusys_device *adev_mdla;
|
|
struct apusys_device *adev_mdla_rt;
|
|
struct mdla_dev *mdev;
|
|
|
|
if (mdla_pwr_apusys_disabled())
|
|
return -1;
|
|
|
|
/* Initialize platform to allocate mdla devices first. */
|
|
ret = mdla_util_plat_init(pdev);
|
|
|
|
if (ret < 0) {
|
|
dev_info(dev, "platform init failed\n");
|
|
return -EINVAL;
|
|
} else if (ret == 1) {
|
|
dev_info(dev, "%s: uP version done\n", __func__);
|
|
return 0;
|
|
}
|
|
|
|
apusys_dev_mdla = kcalloc(mdla_util_get_core_num(),
|
|
sizeof(struct apusys_device), GFP_KERNEL);
|
|
if (!apusys_dev_mdla) {
|
|
ret = -ENOMEM;
|
|
goto err;
|
|
}
|
|
|
|
apusys_dev_mdla_rt = kcalloc(mdla_util_get_core_num(),
|
|
sizeof(struct apusys_device), GFP_KERNEL);
|
|
if (!apusys_dev_mdla_rt) {
|
|
ret = -ENOMEM;
|
|
goto err_dev_mdla_rt;
|
|
}
|
|
|
|
for_each_mdla_core(i) {
|
|
mdev = mdla_get_device(i);
|
|
adev_mdla = &apusys_dev_mdla[i];
|
|
adev_mdla_rt = &apusys_dev_mdla_rt[i];
|
|
|
|
adev_mdla->dev_type = DEVICE_MDLA;
|
|
adev_mdla->preempt_type = APUSYS_PREEMPT_NONE;
|
|
adev_mdla->private = mdev;
|
|
adev_mdla->send_cmd = apusys_mdla_handler;
|
|
|
|
ret = apusys_register_device(adev_mdla);
|
|
if (ret) {
|
|
dev_info(dev, "register apusys mdla %d fail\n", i);
|
|
goto err_apusys;
|
|
}
|
|
|
|
if (!apusys_mdla_rt_support())
|
|
continue;
|
|
|
|
adev_mdla_rt->dev_type = DEVICE_MDLA_RT;
|
|
adev_mdla_rt->preempt_type = APUSYS_PREEMPT_NONE;
|
|
adev_mdla_rt->private = mdev;
|
|
adev_mdla_rt->send_cmd = apusys_mdla_rt_handler;
|
|
|
|
ret = apusys_register_device(adev_mdla_rt);
|
|
|
|
if (ret) {
|
|
dev_info(dev, "register apusys mdla RT %d fail\n", i);
|
|
apusys_unregister_device(adev_mdla);
|
|
goto err_apusys;
|
|
}
|
|
}
|
|
|
|
dev_info(dev, "%s: done\n", __func__);
|
|
|
|
return 0;
|
|
|
|
err_apusys:
|
|
for (i = i - 1; i >= 0; i--) {
|
|
apusys_unregister_device(&apusys_dev_mdla[i]);
|
|
if (apusys_mdla_rt_support())
|
|
apusys_unregister_device(&apusys_dev_mdla_rt[i]);
|
|
}
|
|
kfree(apusys_dev_mdla_rt);
|
|
err_dev_mdla_rt:
|
|
kfree(apusys_dev_mdla);
|
|
err:
|
|
mdla_util_plat_deinit(pdev);
|
|
return ret;
|
|
}
|
|
|
|
static int mdla_remove(struct platform_device *pdev)
|
|
{
|
|
int i;
|
|
|
|
if (mdla_pwr_apusys_disabled())
|
|
return 0;
|
|
|
|
dev_info(&pdev->dev, "%s start -\n", __func__);
|
|
|
|
for_each_mdla_core(i) {
|
|
apusys_unregister_device(&apusys_dev_mdla[i]);
|
|
if (apusys_mdla_rt_support())
|
|
apusys_unregister_device(&apusys_dev_mdla_rt[i]);
|
|
}
|
|
|
|
kfree(apusys_dev_mdla_rt);
|
|
kfree(apusys_dev_mdla);
|
|
|
|
mdla_util_plat_deinit(pdev);
|
|
|
|
platform_set_drvdata(pdev, NULL);
|
|
|
|
dev_info(&pdev->dev, "%s done -\n", __func__);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int mdla_resume(struct platform_device *pdev)
|
|
{
|
|
dev_info(&pdev->dev, "%s()\n", __func__);
|
|
return 0;
|
|
}
|
|
|
|
static int mdla_suspend(struct platform_device *pdev, pm_message_t mesg)
|
|
{
|
|
int i;
|
|
|
|
for_each_mdla_core(i) mdla_pwr_ops_get()->off(i, 1, true);
|
|
|
|
dev_info(&pdev->dev, "%s()\n", __func__);
|
|
return 0;
|
|
}
|
|
|
|
static struct platform_driver mdla_driver = {
|
|
.driver = {
|
|
.name = DRIVER_NAME,
|
|
.owner = THIS_MODULE,
|
|
},
|
|
.probe = mdla_probe,
|
|
.remove = mdla_remove,
|
|
.suspend = mdla_suspend,
|
|
.resume = mdla_resume,
|
|
};
|
|
|
|
int mdla_drv_init(void)
|
|
{
|
|
mdla_driver.driver.of_match_table = mdla_util_get_device_id();
|
|
|
|
return platform_driver_register(&mdla_driver);
|
|
}
|
|
|
|
void mdla_drv_exit(void)
|
|
{
|
|
platform_driver_unregister(&mdla_driver);
|
|
}
|