mirror of
git://nv-tegra.nvidia.com/linux-nv-oot.git
synced 2025-12-24 02:01:36 +03:00
NVADSP driver tries to allocate shared memory for ADSP by requesting the DMA APIs to allocate at address hardcoded in DT. This poses an issue where if kernel has already allocated that memory to a different driver in same iommu group, this memory becomes unavailable and ADSP does not boot Adding support to allocate shared memory for ADSP dynamically to avoid dependencies on other drivers Jira EMA-1213 Change-Id: I1bc8d49f17ec8226d34f3c943cccabef97b2afb6 Signed-off-by: Hariharan Sivaraman <hariharans@nvidia.com> Reviewed-on: https://git-master.nvidia.com/r/2134341 Reviewed-by: Automatic_Commit_Validation_User Reviewed-by: Uday Gupta <udayg@nvidia.com> GVS: Gerrit_Virtual_Submit Tested-by: Niranjan Dighe <ndighe@nvidia.com> Reviewed-by: Nitin Pai <npai@nvidia.com> Reviewed-by: mobile promotions <svcmobile_promotions@nvidia.com> Tested-by: mobile promotions <svcmobile_promotions@nvidia.com>
500 lines
11 KiB
C
500 lines
11 KiB
C
/*
|
|
* dev.c
|
|
*
|
|
* A device driver for ADSP and APE
|
|
*
|
|
* Copyright (C) 2014-2019, 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
|
|
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 */
|
|
|
|
#ifdef CONFIG_PM_SLEEP
|
|
static int nvadsp_suspend(struct device *dev)
|
|
{
|
|
if (pm_runtime_status_suspended(dev))
|
|
return 0;
|
|
|
|
return nvadsp_runtime_suspend(dev);
|
|
}
|
|
|
|
static int nvadsp_resume(struct device *dev)
|
|
{
|
|
if (pm_runtime_status_suspended(dev))
|
|
return 0;
|
|
|
|
return nvadsp_runtime_resume(dev);
|
|
}
|
|
#endif /* CONFIG_PM_SLEEP */
|
|
|
|
static const struct dev_pm_ops nvadsp_pm_ops = {
|
|
SET_NOIRQ_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 = 0,
|
|
.adsp_thread_hwmbox = 0,
|
|
.adsp_irq_hwmbox = 0,
|
|
.adsp_shared_mem_hwmbox = 0,
|
|
.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_shared_mem_hwmbox = 0x18000, /* HWMBOX3 */
|
|
.adsp_thread_hwmbox = 0x20000, /* HWMBOX4 */
|
|
.adsp_state_hwmbox = 0x30000, /* HWMBOX6 */
|
|
.adsp_irq_hwmbox = 0x38000, /* HWMBOX7 */
|
|
.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");
|