mirror of
git://nv-tegra.nvidia.com/linux-nv-oot.git
synced 2025-12-22 17:25:35 +03:00
nvadsp_da_to_va_mappings API provides the ability to get the virtual address from the physical address. This API does not need to be exposed as there are no other modules using it and prevent prevent speculative load related leak. Bug 200381256 Change-Id: Ic94497ca40402101e4246b914f00e030551b0c4c Signed-off-by: Ajay Nandakumar <anandakumarm@nvidia.com> Reviewed-on: https://git-master.nvidia.com/r/1652771 Reviewed-by: mobile promotions <svcmobile_promotions@nvidia.com> Tested-by: mobile promotions <svcmobile_promotions@nvidia.com>
491 lines
11 KiB
C
491 lines
11 KiB
C
/*
|
|
* dev.c
|
|
*
|
|
* A device driver for ADSP and APE
|
|
*
|
|
* Copyright (C) 2014-2018, NVIDIA Corporation. All rights reserved.
|
|
*
|
|
* This software is licensed under the terms of the GNU General Public
|
|
* License version 2, as published by the Free Software Foundation, and
|
|
* may be copied, distributed, and modified under those terms.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
*/
|
|
|
|
#include <linux/platform_device.h>
|
|
#include <linux/fs.h>
|
|
#include <linux/platform_device.h>
|
|
#include <linux/miscdevice.h>
|
|
#include <linux/pm.h>
|
|
#include <linux/of.h>
|
|
#include <linux/of_device.h>
|
|
#include <linux/module.h>
|
|
#include <linux/moduleparam.h>
|
|
#include <linux/io.h>
|
|
#include <linux/tegra_nvadsp.h>
|
|
#include <soc/tegra/chip-id.h>
|
|
#include <linux/pm_runtime.h>
|
|
#include <linux/tegra_pm_domains.h>
|
|
#include <linux/clk/tegra.h>
|
|
#include <linux/delay.h>
|
|
#include <asm/arch_timer.h>
|
|
#include <linux/irqchip/tegra-agic.h>
|
|
|
|
#include "dev.h"
|
|
#include "os.h"
|
|
#include "amc.h"
|
|
#include "ape_actmon.h"
|
|
#include "aram_manager.h"
|
|
|
|
#include "dev-t21x.h"
|
|
#include "dev-t18x.h"
|
|
|
|
static struct nvadsp_drv_data *nvadsp_drv_data;
|
|
|
|
#ifdef CONFIG_DEBUG_FS
|
|
static int __init adsp_debug_init(struct nvadsp_drv_data *drv_data)
|
|
{
|
|
drv_data->adsp_debugfs_root = debugfs_create_dir("tegra_ape", NULL);
|
|
if (!drv_data->adsp_debugfs_root)
|
|
return -ENOMEM;
|
|
return 0;
|
|
}
|
|
#endif /* CONFIG_DEBUG_FS */
|
|
|
|
#ifdef CONFIG_PM_SLEEP
|
|
static int nvadsp_suspend(struct device *dev)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
static int nvadsp_resume(struct device *dev)
|
|
{
|
|
return 0;
|
|
}
|
|
#endif /* CONFIG_PM_SLEEP */
|
|
|
|
#ifdef CONFIG_PM
|
|
static int nvadsp_runtime_resume(struct device *dev)
|
|
{
|
|
struct platform_device *pdev = to_platform_device(dev);
|
|
struct nvadsp_drv_data *drv_data = platform_get_drvdata(pdev);
|
|
int ret = -EINVAL;
|
|
|
|
if (drv_data->runtime_resume)
|
|
ret = drv_data->runtime_resume(dev);
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int nvadsp_runtime_suspend(struct device *dev)
|
|
{
|
|
struct platform_device *pdev = to_platform_device(dev);
|
|
struct nvadsp_drv_data *drv_data = platform_get_drvdata(pdev);
|
|
int ret = -EINVAL;
|
|
|
|
if (drv_data->runtime_suspend)
|
|
ret = drv_data->runtime_suspend(dev);
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int nvadsp_runtime_idle(struct device *dev)
|
|
{
|
|
struct platform_device *pdev = to_platform_device(dev);
|
|
struct nvadsp_drv_data *drv_data = platform_get_drvdata(pdev);
|
|
int ret = 0;
|
|
|
|
if (drv_data->runtime_idle)
|
|
ret = drv_data->runtime_idle(dev);
|
|
|
|
return ret;
|
|
}
|
|
#endif /* CONFIG_PM */
|
|
|
|
static const struct dev_pm_ops nvadsp_pm_ops = {
|
|
SET_SYSTEM_SLEEP_PM_OPS(nvadsp_suspend, nvadsp_resume)
|
|
SET_RUNTIME_PM_OPS(nvadsp_runtime_suspend, nvadsp_runtime_resume,
|
|
nvadsp_runtime_idle)
|
|
};
|
|
|
|
uint64_t nvadsp_get_timestamp_counter(void)
|
|
{
|
|
return arch_counter_get_cntvct();
|
|
}
|
|
EXPORT_SYMBOL(nvadsp_get_timestamp_counter);
|
|
|
|
static void __init nvadsp_parse_clk_entries(struct platform_device *pdev)
|
|
{
|
|
struct nvadsp_drv_data *drv_data = platform_get_drvdata(pdev);
|
|
struct device *dev = &pdev->dev;
|
|
u32 val32 = 0;
|
|
|
|
|
|
/* Optional properties, should come from platform dt files */
|
|
if (of_property_read_u32(dev->of_node, "nvidia,adsp_freq", &val32))
|
|
dev_dbg(dev, "adsp_freq dt not found\n");
|
|
else {
|
|
drv_data->adsp_freq = val32;
|
|
drv_data->adsp_freq_hz = val32 * 1000;
|
|
}
|
|
|
|
if (of_property_read_u32(dev->of_node, "nvidia,ape_freq", &val32))
|
|
dev_dbg(dev, "ape_freq dt not found\n");
|
|
else
|
|
drv_data->ape_freq = val32;
|
|
|
|
if (of_property_read_u32(dev->of_node, "nvidia,ape_emc_freq", &val32))
|
|
dev_dbg(dev, "ape_emc_freq dt not found\n");
|
|
else
|
|
drv_data->ape_emc_freq = val32;
|
|
}
|
|
|
|
static int __init nvadsp_parse_dt(struct platform_device *pdev)
|
|
{
|
|
struct nvadsp_drv_data *drv_data = platform_get_drvdata(pdev);
|
|
struct device *dev = &pdev->dev;
|
|
u32 *adsp_reset;
|
|
u32 *adsp_mem;
|
|
int iter;
|
|
|
|
adsp_reset = drv_data->unit_fpga_reset;
|
|
adsp_mem = drv_data->adsp_mem;
|
|
|
|
for (iter = 0; iter < ADSP_MEM_END; iter++) {
|
|
if (of_property_read_u32_index(dev->of_node, "nvidia,adsp_mem",
|
|
iter, &adsp_mem[iter])) {
|
|
dev_err(dev, "adsp memory dt %d not found\n", iter);
|
|
return -EINVAL;
|
|
}
|
|
}
|
|
|
|
for (iter = 0; iter < ADSP_EVP_END; iter++) {
|
|
if (of_property_read_u32_index(dev->of_node,
|
|
"nvidia,adsp-evp-base",
|
|
iter, &drv_data->evp_base[iter])) {
|
|
dev_err(dev, "adsp memory dt %d not found\n", iter);
|
|
return -EINVAL;
|
|
}
|
|
}
|
|
|
|
drv_data->adsp_unit_fpga = of_property_read_bool(dev->of_node,
|
|
"nvidia,adsp_unit_fpga");
|
|
|
|
drv_data->adsp_os_secload = of_property_read_bool(dev->of_node,
|
|
"nvidia,adsp_os_secload");
|
|
|
|
if (drv_data->adsp_unit_fpga) {
|
|
for (iter = 0; iter < ADSP_UNIT_FPGA_RESET_END; iter++) {
|
|
if (of_property_read_u32_index(dev->of_node,
|
|
"nvidia,adsp_unit_fpga_reset", iter,
|
|
&adsp_reset[iter])) {
|
|
dev_err(dev, "adsp reset dt %d not found\n",
|
|
iter);
|
|
return -EINVAL;
|
|
}
|
|
}
|
|
}
|
|
nvadsp_parse_clk_entries(pdev);
|
|
|
|
drv_data->state.evp = devm_kzalloc(dev,
|
|
drv_data->evp_base[ADSP_EVP_SIZE], GFP_KERNEL);
|
|
if (!drv_data->state.evp)
|
|
return -ENOMEM;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int __init nvadsp_probe(struct platform_device *pdev)
|
|
{
|
|
struct nvadsp_drv_data *drv_data;
|
|
struct device *dev = &pdev->dev;
|
|
struct resource *res = NULL;
|
|
void __iomem *base = NULL;
|
|
uint32_t aram_addr;
|
|
uint32_t aram_size;
|
|
int dram_iter;
|
|
int irq_iter;
|
|
int ret = 0;
|
|
int iter;
|
|
|
|
dev_info(dev, "in probe()...\n");
|
|
|
|
drv_data = devm_kzalloc(dev, sizeof(*drv_data),
|
|
GFP_KERNEL);
|
|
if (!drv_data) {
|
|
dev_err(&pdev->dev, "Failed to allocate driver data");
|
|
ret = -ENOMEM;
|
|
goto out;
|
|
}
|
|
|
|
platform_set_drvdata(pdev, drv_data);
|
|
drv_data->pdev = pdev;
|
|
drv_data->chip_data = of_device_get_match_data(dev);
|
|
|
|
ret = nvadsp_parse_dt(pdev);
|
|
if (ret)
|
|
goto out;
|
|
|
|
#ifdef CONFIG_PM
|
|
ret = nvadsp_pm_init(pdev);
|
|
if (ret) {
|
|
dev_err(dev, "Failed in pm init");
|
|
goto out;
|
|
}
|
|
#endif
|
|
|
|
#ifdef CONFIG_DEBUG_FS
|
|
if (adsp_debug_init(drv_data))
|
|
dev_err(dev,
|
|
"unable to create tegra_ape debug fs directory\n");
|
|
#endif
|
|
|
|
drv_data->base_regs =
|
|
devm_kzalloc(dev, sizeof(void *) * APE_MAX_REG,
|
|
GFP_KERNEL);
|
|
if (!drv_data->base_regs) {
|
|
dev_err(dev, "Failed to allocate regs");
|
|
ret = -ENOMEM;
|
|
goto out;
|
|
}
|
|
|
|
for (iter = 0; iter < APE_MAX_REG; iter++) {
|
|
res = platform_get_resource(pdev, IORESOURCE_MEM, iter);
|
|
if (!res) {
|
|
dev_err(dev,
|
|
"Failed to get resource with ID %d\n",
|
|
iter);
|
|
ret = -EINVAL;
|
|
goto out;
|
|
}
|
|
|
|
if (!drv_data->adsp_unit_fpga && iter == UNIT_FPGA_RST)
|
|
continue;
|
|
|
|
/*
|
|
* skip if the particular module is not present in a
|
|
* generation, for which the register start address
|
|
* is made 0 from dt.
|
|
*/
|
|
if (res->start == 0)
|
|
continue;
|
|
|
|
base = devm_ioremap_resource(dev, res);
|
|
if (IS_ERR(base)) {
|
|
dev_err(dev, "Failed to iomap resource reg[%d]\n",
|
|
iter);
|
|
ret = PTR_ERR(base);
|
|
goto out;
|
|
}
|
|
drv_data->base_regs[iter] = base;
|
|
nvadsp_add_load_mappings(res->start, base,
|
|
resource_size(res));
|
|
}
|
|
|
|
drv_data->base_regs_saved = drv_data->base_regs;
|
|
|
|
for (dram_iter = 0; dram_iter < ADSP_MAX_DRAM_MAP; dram_iter++) {
|
|
res = platform_get_resource(pdev, IORESOURCE_MEM, iter++);
|
|
if (!res) {
|
|
dev_err(dev,
|
|
"Failed to get DRAM map with ID %d\n", iter);
|
|
ret = -EINVAL;
|
|
goto out;
|
|
}
|
|
|
|
drv_data->dram_region[dram_iter] = res;
|
|
}
|
|
|
|
for (irq_iter = 0; irq_iter < NVADSP_VIRQ_MAX; irq_iter++) {
|
|
res = platform_get_resource(pdev, IORESOURCE_IRQ, irq_iter);
|
|
if (!res) {
|
|
dev_err(dev, "Failed to get irq number for index %d\n",
|
|
irq_iter);
|
|
ret = -EINVAL;
|
|
goto out;
|
|
}
|
|
drv_data->agic_irqs[irq_iter] = res->start;
|
|
}
|
|
|
|
nvadsp_drv_data = drv_data;
|
|
|
|
#ifdef CONFIG_PM
|
|
tegra_pd_add_device(dev);
|
|
|
|
pm_runtime_enable(dev);
|
|
|
|
ret = pm_runtime_get_sync(dev);
|
|
if (ret < 0)
|
|
goto out;
|
|
#endif
|
|
ret = nvadsp_hwmbox_init(pdev);
|
|
if (ret)
|
|
goto err;
|
|
|
|
ret = nvadsp_mbox_init(pdev);
|
|
if (ret)
|
|
goto err;
|
|
|
|
#ifdef CONFIG_TEGRA_ADSP_ACTMON
|
|
ret = ape_actmon_probe(pdev);
|
|
if (ret)
|
|
goto err;
|
|
#endif
|
|
|
|
ret = nvadsp_os_probe(pdev);
|
|
if (ret)
|
|
goto err;
|
|
|
|
ret = nvadsp_reset_init(pdev);
|
|
if (ret) {
|
|
dev_err(dev, "Failed initialize resets\n");
|
|
goto err;
|
|
}
|
|
|
|
ret = nvadsp_app_module_probe(pdev);
|
|
if (ret)
|
|
goto err;
|
|
|
|
aram_addr = drv_data->adsp_mem[ARAM_ALIAS_0_ADDR];
|
|
aram_size = drv_data->adsp_mem[ARAM_ALIAS_0_SIZE];
|
|
ret = nvadsp_aram_init(aram_addr, aram_size);
|
|
if (ret)
|
|
dev_err(dev, "Failed to init aram\n");
|
|
|
|
drv_data->bwmgr = tegra_bwmgr_register(TEGRA_BWMGR_CLIENT_APE_ADSP);
|
|
ret = IS_ERR_OR_NULL(drv_data->bwmgr);
|
|
if (ret)
|
|
dev_err(&pdev->dev, "unable to register bwmgr\n");
|
|
err:
|
|
#ifdef CONFIG_PM
|
|
ret = pm_runtime_put_sync(dev);
|
|
if (ret < 0)
|
|
dev_err(dev, "pm_runtime_put_sync failed\n");
|
|
#endif
|
|
out:
|
|
return ret;
|
|
}
|
|
|
|
static int nvadsp_remove(struct platform_device *pdev)
|
|
{
|
|
struct nvadsp_drv_data *drv_data = platform_get_drvdata(pdev);
|
|
int err;
|
|
|
|
if (drv_data->bwmgr) {
|
|
err = tegra_bwmgr_set_emc(drv_data->bwmgr, 0,
|
|
TEGRA_BWMGR_SET_EMC_FLOOR);
|
|
if (err) {
|
|
dev_err(&pdev->dev, "failed to set emc freq rate:%d\n",
|
|
err);
|
|
}
|
|
tegra_bwmgr_unregister(drv_data->bwmgr);
|
|
}
|
|
nvadsp_aram_exit();
|
|
|
|
pm_runtime_disable(&pdev->dev);
|
|
|
|
#ifdef CONFIG_PM
|
|
if (!pm_runtime_status_suspended(&pdev->dev))
|
|
nvadsp_runtime_suspend(&pdev->dev);
|
|
#endif
|
|
|
|
tegra_pd_remove_device(&pdev->dev);
|
|
|
|
return 0;
|
|
}
|
|
|
|
#ifdef CONFIG_OF
|
|
static struct nvadsp_chipdata tegra210_adsp_chipdata = {
|
|
.hwmb = {
|
|
.reg_idx = AMISC,
|
|
.hwmbox0_reg = 0x58,
|
|
.hwmbox1_reg = 0X5C,
|
|
.hwmbox2_reg = 0x60,
|
|
.hwmbox3_reg = 0x64,
|
|
},
|
|
.adsp_state_hwmbox = -1,
|
|
.adsp_thread_hwmbox = -1,
|
|
.adsp_irq_hwmbox = -1,
|
|
.reset_init = nvadsp_reset_t21x_init,
|
|
.os_init = nvadsp_os_t21x_init,
|
|
#ifdef CONFIG_PM
|
|
.pm_init = nvadsp_pm_t21x_init,
|
|
#endif
|
|
.wdt_irq = INT_T210_ADSP_WDT,
|
|
.start_irq = INT_T210_AGIC_START,
|
|
.end_irq = INT_T210_AGIC_END,
|
|
};
|
|
|
|
static struct nvadsp_chipdata tegrat18x_adsp_chipdata = {
|
|
.hwmb = {
|
|
.reg_idx = AHSP,
|
|
.hwmbox0_reg = 0x00000,
|
|
.hwmbox1_reg = 0X08000,
|
|
.hwmbox2_reg = 0X10000,
|
|
.hwmbox3_reg = 0X18000,
|
|
.hwmbox4_reg = 0X20000,
|
|
.hwmbox5_reg = 0X28000,
|
|
.hwmbox6_reg = 0X30000,
|
|
.hwmbox7_reg = 0X38000,
|
|
},
|
|
.adsp_state_hwmbox = 0x30000,
|
|
.adsp_thread_hwmbox = 0x20000,
|
|
.adsp_irq_hwmbox = 0x38000,
|
|
.reset_init = nvadsp_reset_t18x_init,
|
|
.os_init = nvadsp_os_t18x_init,
|
|
#ifdef CONFIG_PM
|
|
.pm_init = nvadsp_pm_t18x_init,
|
|
#endif
|
|
.wdt_irq = INT_T18x_ATKE_WDT_IRQ,
|
|
.start_irq = INT_T18x_AGIC_START,
|
|
.end_irq = INT_T18x_AGIC_END,
|
|
};
|
|
|
|
static const struct of_device_id nvadsp_of_match[] = {
|
|
{
|
|
.compatible = "nvidia,tegra210-adsp",
|
|
.data = &tegra210_adsp_chipdata,
|
|
}, {
|
|
.compatible = "nvidia,tegra18x-adsp",
|
|
.data = &tegrat18x_adsp_chipdata,
|
|
}, {
|
|
.compatible = "nvidia,tegra18x-adsp-hv",
|
|
.data = &tegrat18x_adsp_chipdata,
|
|
}, {
|
|
},
|
|
};
|
|
#endif
|
|
|
|
static struct platform_driver nvadsp_driver __refdata = {
|
|
.driver = {
|
|
.name = "nvadsp",
|
|
.owner = THIS_MODULE,
|
|
.pm = &nvadsp_pm_ops,
|
|
.of_match_table = of_match_ptr(nvadsp_of_match),
|
|
},
|
|
.probe = nvadsp_probe,
|
|
.remove = nvadsp_remove,
|
|
};
|
|
|
|
static int __init nvadsp_init(void)
|
|
{
|
|
return platform_driver_register(&nvadsp_driver);
|
|
}
|
|
|
|
static void __exit nvadsp_exit(void)
|
|
{
|
|
platform_driver_unregister(&nvadsp_driver);
|
|
}
|
|
|
|
module_init(nvadsp_init);
|
|
module_exit(nvadsp_exit);
|
|
|
|
MODULE_AUTHOR("NVIDIA");
|
|
MODULE_DESCRIPTION("Tegra Host ADSP Driver");
|
|
MODULE_VERSION("1.0");
|
|
MODULE_LICENSE("Dual BSD/GPL");
|