1
0
mirror of https://github.com/physwizz/a155-U-u1.git synced 2025-09-26 19:04:54 +00:00
Files
physwizz 99537be4e2 first
2024-03-11 06:53:12 +11:00

603 lines
14 KiB
C

// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (c) 2020 MediaTek Inc.
*/
#include <linux/remoteproc.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/firmware.h>
#include <linux/delay.h>
#include <linux/pagemap.h>
#include <linux/dma-mapping.h>
#include <linux/memremap.h>
#include <linux/of_address.h>
#include <linux/io.h>
#include <linux/iommu.h>
#include <linux/time64.h>
#include <linux/interrupt.h>
#include <linux/device.h>
#include <linux/sched/clock.h>
#include <linux/of_device.h>
#include <linux/pm_runtime.h>
#include <mt-plat/aee.h>
#include "apu.h"
#include "apu_debug.h"
#include "apu_excep.h"
#include "apu_config.h"
#include "apusys_core.h"
struct mtk_apu *g_apu_struct;
uint32_t g_apu_log;
static void *apu_da_to_va(struct rproc *rproc, u64 da, size_t len, bool *is_iomem)
{
void *ptr = NULL;
struct mtk_apu *apu = (struct mtk_apu *)rproc->priv;
if (da < DRAM_OFFSET + CODE_BUF_SIZE) {
ptr = apu->code_buf + (da - DRAM_OFFSET);
dev_info(apu->dev, "%s: (DRAM): da = 0x%llx, len = 0x%x\n",
__func__, da, len);
} else if (da >= TCM_OFFSET && da < TCM_OFFSET + TCM_SIZE) {
ptr = apu->md32_tcm + (da - TCM_OFFSET);
dev_info(apu->dev, "%s: (TCM): da = 0x%llx, len = 0x%x\n",
__func__, da, len);
}
return ptr;
}
static int __apu_run(struct rproc *rproc)
{
struct mtk_apu *apu = (struct mtk_apu *)rproc->priv;
struct mtk_apu_hw_ops *hw_ops = &apu->platdata->ops;
struct device *dev = apu->dev;
struct apu_run *run = &apu->run;
struct timespec64 begin, end, delta;
int ret;
if (!hw_ops->start) {
WARN_ON(1);
return -EINVAL;
}
hw_ops->power_on(apu);
hw_ops->start(apu);
/* check if boot success */
ktime_get_ts64(&begin);
ret = wait_event_interruptible_timeout(
run->wq,
run->signaled,
msecs_to_jiffies(10000));
ktime_get_ts64(&end);
if (ret == 0) {
dev_info(dev, "APU initialization timeout!!\n");
ret = -ETIME;
apusys_rv_aee_warn("APUSYS_RV", "APUSYS_RV_BOOT_TIMEOUT");
goto stop;
}
if (ret == -ERESTARTSYS)
dev_info(dev, "wait APU interrupted by a signal!!\n");
delta = timespec64_sub(end, begin);
dev_info(dev,
"APU uP boot done. boot time: %llu s, %llu ns. fw_ver: %s\n",
(uint64_t)delta.tv_sec, (uint64_t)delta.tv_nsec, run->fw_ver);
return 0;
stop:
if (!hw_ops->stop) {
WARN_ON(1);
return -EINVAL;
}
hw_ops->stop(apu);
return ret;
}
static int apu_start(struct rproc *rproc)
{
struct mtk_apu *apu = (struct mtk_apu *)rproc->priv;
dev_info(apu->dev, "%s: try to boot uP\n", __func__);
return __apu_run(rproc);
}
static int apu_attach(struct rproc *rproc)
{
struct mtk_apu *apu = (struct mtk_apu *)rproc->priv;
dev_info(apu->dev, "%s: try to boot uP\n", __func__);
return __apu_run(rproc);
}
static int apu_stop(struct rproc *rproc)
{
struct mtk_apu *apu = (struct mtk_apu *)rproc->priv;
struct mtk_apu_hw_ops *hw_ops = &apu->platdata->ops;
if (!hw_ops->stop) {
WARN_ON(1);
return -EINVAL;
}
hw_ops->stop(apu);
return 0;
}
static const struct rproc_ops apu_ops = {
.start = apu_start,
.stop = apu_stop,
.attach = apu_attach,
.da_to_va = apu_da_to_va,
};
static void apu_dram_boot_remove(struct mtk_apu *apu)
{
void *domain = iommu_get_domain_for_dev(apu->dev);
u32 boundary = (u32) upper_32_bits(apu->code_da);
u64 iova = CODE_BUF_DA | ((u64) boundary << 32);
if (apu->platdata->flags & F_SECURE_BOOT)
return;
if (apu->platdata->flags & F_PRELOAD_FIRMWARE) {
if (domain != NULL)
iommu_unmap(domain, APU_SEC_FW_IOVA, apu->apusys_sec_mem_size);
} else if ((apu->platdata->flags & F_BYPASS_IOMMU) == 0)
if (domain != NULL)
iommu_unmap(domain, iova, CODE_BUF_SIZE);
if ((apu->platdata->flags & F_PRELOAD_FIRMWARE) == 0)
dma_free_coherent(apu->dev, CODE_BUF_SIZE, apu->code_buf, apu->code_da);
}
static int apu_dram_boot_init(struct mtk_apu *apu)
{
struct device *dev = apu->dev;
int ret = 0;
int map_sg_sz = 0;
void *domain;
struct sg_table sgt;
phys_addr_t pa;
u32 boundary;
u64 iova;
if (apu->platdata->flags & F_SECURE_BOOT)
return 0;
if ((apu->platdata->flags & F_BYPASS_IOMMU) == 0) {
domain = iommu_get_domain_for_dev(apu->dev);
if (domain == NULL) {
dev_info(dev, "%s: iommu_get_domain_for_dev fail\n", __func__);
return -ENOMEM;
}
}
if (apu->platdata->flags & F_PRELOAD_FIRMWARE &&
(apu->platdata->flags & F_BYPASS_IOMMU) == 0) {
apu->code_buf = (void *) apu->apu_sec_mem_base +
apu->apusys_sec_info->up_code_buf_ofs;
apu->code_da = APU_SEC_FW_IOVA;
/* Map reserved code buffer to APU_SEC_FW_IOVA */
ret = iommu_map(domain, APU_SEC_FW_IOVA,
apu->apusys_sec_mem_start,
apu->apusys_sec_mem_size, IOMMU_READ|IOMMU_WRITE);
if (ret) {
dev_info(dev, "%s: iommu_map fail(%d)\n", __func__, ret);
return ret;
}
dev_info(dev, "%s: iommu_map done\n", __func__);
return ret;
}
/* Allocate code buffer */
apu->code_buf = dma_alloc_coherent(apu->dev, CODE_BUF_SIZE,
&apu->code_da, GFP_KERNEL);
if (apu->code_buf == NULL || apu->code_da == 0) {
dev_info(dev, "%s: dma_alloc_coherent fail\n", __func__);
return -ENOMEM;
}
memset(apu->code_buf, 0, CODE_BUF_SIZE);
boundary = (u32) upper_32_bits(apu->code_da);
iova = CODE_BUF_DA | ((u64) boundary << 32);
dev_info(dev, "%s: boundary = %u, iova = 0x%llx\n",
__func__, boundary, iova);
if ((apu->platdata->flags & F_BYPASS_IOMMU) == 0) {
sgt.sgl = NULL;
/* Convert IOVA to sgtable */
ret = dma_get_sgtable(apu->dev, &sgt, apu->code_buf,
apu->code_da, CODE_BUF_SIZE);
if (ret < 0 || sgt.sgl == NULL) {
dev_info(dev, "get sgtable fail\n");
return -EINVAL;
}
dev_info(dev, "%s: sgt.nents = %d, sgt.orig_nents = %d\n",
__func__, sgt.nents, sgt.orig_nents);
/* Map sg_list to MD32_BOOT_ADDR */
map_sg_sz = iommu_map_sg(domain, iova, sgt.sgl,
sgt.nents, IOMMU_READ|IOMMU_WRITE);
dev_info(dev, "%s: sgt.nents = %d, sgt.orig_nents = %d\n",
__func__, sgt.nents, sgt.orig_nents);
dev_info(dev, "%s: map_sg_sz = %d\n", __func__, map_sg_sz);
if (map_sg_sz != CODE_BUF_SIZE)
dev_info(dev, "%s: iommu_map_sg fail(%d)\n", __func__, ret);
pa = iommu_iova_to_phys(domain,
iova + CODE_BUF_SIZE - SZ_4K);
dev_info(dev, "%s: pa = 0x%llx\n",
__func__, (uint64_t) pa);
if (!pa) // pa should not be null
dev_info(dev, "%s: check pa fail(0x%llx)\n",
__func__, (uint64_t) pa);
}
dev_info(dev, "%s: apu->code_buf = 0x%llx, apu->code_da = 0x%llx\n",
__func__, (uint64_t) apu->code_buf, (uint64_t) apu->code_da);
return ret;
}
static int apu_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct device_node *np = dev->of_node;
struct device_node *apusys_sec_mem_node,
*apusys_aee_coredump_mem_node;
struct rproc *rproc;
struct mtk_apu *apu;
struct mtk_apu_platdata *data;
struct mtk_apu_hw_ops *hw_ops;
char *fw_name = "mrv.elf";
int ret = 0;
uint32_t up_code_buf_sz;
dev_info(dev, "%s: enter\n", __func__);
data = (struct mtk_apu_platdata *)of_device_get_match_data(dev);
if (!data) {
dev_info(dev, "%s: of_device_get_match_data fail\n", __func__);
return -EINVAL;
}
dev_info(dev, "%s: platdata flags=0x%08x\n", __func__, data->flags);
hw_ops = &data->ops;
rproc = rproc_alloc(dev,
np->name,
&apu_ops,
fw_name,
sizeof(struct mtk_apu));
if (!rproc) {
dev_info(dev, "unable to allocate remoteproc\n");
return -ENOMEM;
}
/*
* auto boot off
* make sure the firmware is in /vendor/firmware/mrv.elf
* before boot up.
*
* use below command to run uP:
* echo start > /sys/class/remoteproc/remoteproc0/state
*/
if (data->flags & F_AUTO_BOOT)
rproc->auto_boot = true;
else
rproc->auto_boot = false;
if (data->flags & F_DEBUG_LOG_ON)
g_apu_log = 1;
else
g_apu_log = 0;
apu = (struct mtk_apu *)rproc->priv;
g_apu_struct = apu;
dev_info(dev, "%s: apu=%p\n", __func__, apu);
apu->rproc = rproc;
apu->pdev = pdev;
apu->dev = dev;
apu->platdata = data;
platform_set_drvdata(pdev, apu);
spin_lock_init(&apu->reg_lock);
if (apu->platdata->flags & F_PRELOAD_FIRMWARE) {
/* prevent MPU violation when F_SECURE_BOOT is enabled */
if ((apu->platdata->flags & F_SECURE_BOOT) == 0) {
apusys_sec_mem_node = of_find_compatible_node(NULL, NULL,
"mediatek,apu_apusys-rv_secure");
if (!apusys_sec_mem_node) {
apu_drv_debug("DT,mediatek,apu_apusys-rv_secure not found\n");
ret = -EINVAL;
goto out_free_rproc;
}
of_property_read_u64_index(apusys_sec_mem_node, "reg", 0,
&(apu->apusys_sec_mem_start));
of_property_read_u64_index(apusys_sec_mem_node, "reg", 1,
&(apu->apusys_sec_mem_size));
apu_drv_debug("%s: start = 0x%llx, size = 0x%llx\n",
apusys_sec_mem_node->full_name, apu->apusys_sec_mem_start,
apu->apusys_sec_mem_size);
apu->apu_sec_mem_base = memremap(apu->apusys_sec_mem_start,
apu->apusys_sec_mem_size, MEMREMAP_WC);
ret = of_property_read_u32(np, "up_code_buf_sz",
&up_code_buf_sz);
if (ret) {
dev_info(dev, "parsing up_code_buf_sz error: %d\n", ret);
ret = -EINVAL;
goto out_free_rproc;
}
apu->apusys_sec_info = (struct apusys_secure_info_t *)
(apu->apu_sec_mem_base + up_code_buf_sz);
apu_drv_debug("up_fw_ofs = 0x%x, up_fw_sz = 0x%x\n",
apu->apusys_sec_info->up_fw_ofs,
apu->apusys_sec_info->up_fw_sz);
apu_drv_debug("up_xfile_ofs = 0x%x, up_xfile_sz = 0x%x\n",
apu->apusys_sec_info->up_xfile_ofs,
apu->apusys_sec_info->up_xfile_sz);
}
apusys_aee_coredump_mem_node = of_find_compatible_node(NULL, NULL,
"mediatek,apu_apusys-rv_aee-coredump");
if (!apusys_aee_coredump_mem_node) {
dev_info(dev, "DT,mediatek,apu_apusys-rv_aee-coredump not found\n");
ret = -EINVAL;
goto out_free_rproc;
}
of_property_read_u64_index(apusys_aee_coredump_mem_node, "reg", 0,
&(apu->apusys_aee_coredump_mem_start));
of_property_read_u64_index(apusys_aee_coredump_mem_node, "reg", 1,
&(apu->apusys_aee_coredump_mem_size));
apu_drv_debug("%s: start = 0x%llx, size = 0x%llx\n",
apusys_aee_coredump_mem_node->full_name,
apu->apusys_aee_coredump_mem_start,
apu->apusys_aee_coredump_mem_size);
apu->apu_aee_coredump_mem_base =
ioremap_cache(apu->apusys_aee_coredump_mem_start,
apu->apusys_aee_coredump_mem_size);
apu->apusys_aee_coredump_info = (struct apusys_aee_coredump_info_t *)
apu->apu_aee_coredump_mem_base;
}
apu_drv_debug("before pm_runtime_enable\n");
pm_runtime_enable(&pdev->dev);
/*
* CAUTION !
* this line will cause rpm refcnt of apu_top +2
* apusys_rv -> iommu0 -> apu_top
* apusys_rv -> iommu1 -> apu_top
*/
pm_runtime_get_sync(&pdev->dev);
if (data->flags & F_AUTO_BOOT) {
ret = hw_ops->power_init(apu);
if (ret) {
pm_runtime_put_sync(&pdev->dev);
goto out_free_rproc;
}
}
if (!hw_ops->apu_memmap_init) {
pm_runtime_put_sync(&pdev->dev);
if (data->flags & F_AUTO_BOOT)
pm_runtime_put_sync(apu->power_dev);
WARN_ON(1);
goto out_free_rproc;
}
ret = hw_ops->apu_memmap_init(apu);
if (ret)
goto remove_apu_memmap;
ret = apu_mem_init(apu);
if (ret)
goto remove_apu_mem;
ret = apu_config_setup(apu);
if (ret)
goto remove_apu_config_setup;
ret = apu_coredump_init(apu);
if (ret)
goto remove_apu_coredump;
ret = apu_dram_boot_init(apu);
if (ret)
goto remove_apu_dram_boot;
ret = apu_ipi_init(pdev, apu);
if (ret)
goto remove_apu_ipi;
if (data->flags & F_AUTO_BOOT) {
ret = apu_deepidle_init(apu);
if (ret < 0)
goto remove_apu_deepidle;
}
ret = apu_debug_init(apu);
if (ret)
goto remove_apu_debug;
ret = apu_timesync_init(apu);
if (ret)
goto remove_apu_timesync;
ret = apu_procfs_init(pdev);
if (ret)
goto remove_apu_procfs;
ret = apu_excep_init(pdev, apu);
if (ret < 0)
goto remove_apu_excep;
if (data->flags & F_PRELOAD_FIRMWARE)
rproc->state = RPROC_DETACHED;
ret = rproc_add(rproc);
if (ret < 0) {
dev_info(dev, "boot fail ret:%d\n", ret);
goto remove_apu_excep;
}
/* to avoid running state being changed through rproc sysfs */
if ((data->flags & F_PRELOAD_FIRMWARE) && (data->flags & F_AUTO_BOOT))
rproc->state = RPROC_DETACHED;
if (hw_ops->init) {
ret = hw_ops->init(apu);
if (ret)
goto del_rproc;
}
pm_runtime_put_sync(&pdev->dev);
if (data->flags & F_AUTO_BOOT)
pm_runtime_put_sync(apu->power_dev);
return 0;
del_rproc:
rproc_del(rproc);
remove_apu_excep:
apu_excep_remove(pdev, apu);
remove_apu_procfs:
apu_procfs_remove(pdev);
remove_apu_timesync:
apu_timesync_remove(apu);
remove_apu_debug:
apu_debug_remove(apu);
remove_apu_deepidle:
apu_deepidle_exit(apu);
remove_apu_ipi:
apu_ipi_remove(apu);
remove_apu_dram_boot:
apu_dram_boot_remove(apu);
remove_apu_coredump:
apu_coredump_remove(apu);
remove_apu_config_setup:
apu_config_remove(apu);
remove_apu_mem:
apu_mem_remove(apu);
remove_apu_memmap:
pm_runtime_put_sync(&pdev->dev);
if (data->flags & F_AUTO_BOOT)
pm_runtime_put_sync(apu->power_dev);
if (!hw_ops->apu_memmap_remove) {
WARN_ON(1);
return -EINVAL;
}
hw_ops->apu_memmap_remove(apu);
out_free_rproc:
rproc_free(rproc);
return ret;
}
static int apu_remove(struct platform_device *pdev)
{
struct mtk_apu *apu = platform_get_drvdata(pdev);
struct mtk_apu_hw_ops *hw_ops = &apu->platdata->ops;
if (hw_ops->exit)
hw_ops->exit(apu);
rproc_del(apu->rproc);
apu_deepidle_exit(apu);
apu_excep_remove(pdev, apu);
apu_procfs_remove(pdev);
apu_timesync_remove(apu);
apu_debug_remove(apu);
apu_ipi_remove(apu);
apu_dram_boot_remove(apu);
apu_coredump_remove(apu);
apu_config_remove(apu);
apu_mem_remove(apu);
if (!hw_ops->apu_memmap_remove) {
WARN_ON(1);
return -EINVAL;
}
hw_ops->apu_memmap_remove(apu);
rproc_free(apu->rproc);
return 0;
}
#ifndef MT6879_APUSYS_RV_PLAT_DATA
const struct mtk_apu_platdata mt6879_platdata;
#endif
#ifndef MT6893_APUSYS_RV_PLAT_DATA
const struct mtk_apu_platdata mt6893_platdata;
#endif
#ifndef MT6895_APUSYS_RV_PLAT_DATA
const struct mtk_apu_platdata mt6895_platdata;
#endif
#ifndef MT6983_APUSYS_RV_PLAT_DATA
const struct mtk_apu_platdata mt6983_platdata;
#endif
static const struct of_device_id mtk_apu_of_match[] = {
{ .compatible = "mediatek,mt6879-apusys_rv", .data = &mt6879_platdata},
{ .compatible = "mediatek,mt6893-apusys_rv", .data = &mt6893_platdata},
{ .compatible = "mediatek,mt6895-apusys_rv", .data = &mt6895_platdata},
{ .compatible = "mediatek,mt6983-apusys_rv", .data = &mt6983_platdata},
{},
};
static struct platform_driver mtk_apu_driver = {
.probe = apu_probe,
.remove = apu_remove,
.driver = {
.name = "mtk-apu",
.of_match_table = of_match_ptr(mtk_apu_of_match),
},
};
int apu_rproc_init(struct apusys_core_info *info)
{
int ret;
ret = platform_driver_register(&mtk_apu_driver);
if (ret)
pr_info("failed to register mtk_apu_driver\n");
return ret;
}
void apu_rproc_exit(void)
{
platform_driver_unregister(&mtk_apu_driver);
}