From a37d2fe7e36926a7745a59fe9f834ca30a116960 Mon Sep 17 00:00:00 2001 From: Mikko Perttunen Date: Fri, 9 Dec 2016 15:06:19 +0200 Subject: [PATCH 001/138] platform: tegra: Import from linux-4.4 Import tegra platform drivers from linux-4.4. These files are introduced or touched by the following commits in linux-4.4: 5798930 arm: mach-tegra: Get rid of apbio.{c,h} 618d424 platform: tegra: enable denver_serr only for t18x c9f681e arm: tegra: get rid of nct header 14cb7cf platform: tegra: nvdumper: Remove code which need NCT 46adb21 platform: tegra: mc: make mc-sid driver scalable and upstreamable b0ea9ac usb: phy: Move header files to include/linux 7d63191 platform: tegra: mc: allow modules to access mc registers 1d5ac46 soc/tegra: Add GPU EDP Management 09166fd soc/tegra: Add CPU EDP Management 7118c16 platform: powergate: tegra: update ISPA name 59cebf1 bwmgr: Add tegra21 specific bwmgr functionality e91ada2 bwmgr: merge dram specific functionality to common code 19fb2fe drivers: platform: tegra: remove TEGRA_21x defconfig b87f6739 platform: nvadsp: fix is_mboxq_full check b11cdba platform: tegra: powergate: do not handle SATA clk 8324487 platform: tegra: mc: remove cl34000094 hacks a77d415 platform: tegra: mc: rename mc-sid.c to tegra-mc-sid.c df6d5a8 platform: tegra: remove support for soctherm e96ddb9 Merge "Merge branch 'dev/praithatha/k4.4_mc_sid' into dev-kernel-4.4" into dev-kernel-4.4 2f69aa5 Merge branch 'dev/praithatha/k4.4_mc_sid' into dev-kernel-4.4 e171bb1 adsp: dfs: override adsp dfs and reinit actmon b7ca294 platform: tegra: mc: Add streamID configuration 457e747 nvdumper: fixed Coverity defects e12c0c0 platform: tegra: Use ARCH_TEGRA_APE for APE PD code 40eba0d platform: tegra: drop flowctrl code 568ad6e central actmon: fix extra interrupt 944bdbf clk: tegra: Correct some nvenc clock names 5c957fe driver: platform: tegra: GR support 5af5163 coverity: Fix the error reported by Coverity 576ea23 tegra-virt-alt: Add adsp virtualization support 7407552 platform: tegra: nvadsp: Fix coverity defect d11b6a0 platform: tegra: nvadsp: Fix coverity defect 6c41ef0 tegra: nvadsp: fix compilation when CONFIG_PM=n 4262e32 adsp: Add virtualization check 85384e2 Fix an uninitialized local value error 44a8d9c tegra: mc: export symbol tegra_set_latency_allowance f699ce7 platform: nvadsp: disable app loading for secload 5ed4b72 drivers: tegra: allow to get "version" from dt 9b6fe36 tegra: add path to allow include 60d96e4 platform: tegra: nvadsp: nvadsp_os_get_version fix 79622a2 drivers: tegra: ape firmware version 27fb719 platform: tegra: iommu: enable linear map setup b77bade platform: nvadsp: fix adsp clock handle 110b085 platform: tegra: Remove code for shared pad control b770e0e bwmgr: pmqos: fix sparse warning dd0400a platform: tegra: remove unused tegra_map_common_io b343db9 platform: tegra: remove Tegra core voltage cap support df9df6b platform/tegra: remove PMC wakeup support codes c669b9a platform/tegra: pmc-iopower: switch to upstream PMC driver 688b6a6 platform/tegra: bootrom-pmc: switch to upstream PMC driver d7d34e6 platform/tegra: remove reboot.c 60b4a47 soc/tegra: pmc: switch to upstream PMC driver 2c9409f bootprof: Separate discontinuous regions e0cb4d2 arm64: tegra186: remove unused register nodes 12c2ba4 fiq reverts 65d8ccb platform: nvadsp: fix clk checks b2eb017 platform: tegra: remove support for auto power detection c403625 tegra: powergate: cleanup code for unsupported platform 589fecb platform: tegra: Removing unsupported platform sources 5f578a8 irqchip: gic: use CPU_PM only when registering d45c1ea platform: tegra: remove nvdump support for t12x/t13x 9b0b6de Revert "Revert "android: fiq_debugger: FIQ glue for ARM64 CPUs"" d5351a1 Revert "Revert "drivers: platform: enumerate fiq_debugger device"" 27af58f Revert "Revert "platform: tegra: clean up fiq_enable() implementation"" 688e514 platform: tegra: Remove support for Tegra clock framework c15896d tegra: denver-knobs: Remove nvmstat_pg stats 019f065 platform: tegra: Remove support for TEGRA_13x_SOC 207db5f drivers: platform: iommu: add linear mapping on lut 1bc0602 denver: hardwood: use device tree to find IRQ number b11f182 isomgr: Apply ISO client's BW limit 132843c platform: tegra: Remove the API tegra_gpio_to_wake() 8a2892d platform: tegra: deprecate APIs to change waketable runtime e2f5924 prod: Add support for masked write in partially prod config e94ac08 platform: tegra: powergate: Remove support for TEGRA_12x_SOC 6b4c4cb platform: tegra: mc: Remove support for TEGRA_12x_SOC 39977fb platform: tegra: Remove drivers for TEGRA_12x_SOC e17670c drivers: platform: fix denver_mca driver 61c6f5e tegra: powergate: add clocks for XUSBB bb10919 tegra: powergate: cleanup clock and reset handling 73b944e tegra: powergate: correct xusbb partition reset ID b3dc4f4 iommu: arm-smmu: add linear mapping support 6c9bcbb platform: tegra: support disabling CONFIG_PM 98d04a5 platform: tegra: remove legacy speedo files c6b9acf platform: tegra: APIs to set prod based on name/index/offset/mask a04d242 platform: tegra: mc: fix build error 1d8c939 platform: tegra: Remove miscellaneous platform specific drivers daab9eb tegra: powergate: shorten some con ids 229a25f platform: tegra: remove tegra common support 3e71442 bwmgr: Remove checks to limit emc_freq 9f3f176 tegra: powergate: use new reset API and use ioremap 11cd9c8 platform: tegra: Disable T210 TCF cpufreq driver 224ecab platform: tegra: Remove the common board support 1813dd1 ivc: fix missing peer notification a56ac67 ivc: fix incorrect counter reset 844c7a1 ivc: Remove nframes power of two check 5a3ec3a bwmgr: Add more information to clients info sysfs 522777c platform: tegra: remove raydium touch support 251660a platform: tegra: remove unneccessary panel file 0915b9a bwmgr: Add API to get core emc rate c66f6bc platform: tegra: Add pmqos emc floor handling a7b51df Add CONFIG_TEGRA_BOND_OUT and disable by default 29cd4ee Stubbed out tegra_periph_reset 3c07fd4 iommu: smmu: Changed the chip ids for 4.4 naming 3d780a1 platform: nvadsp: fix MAXCLKLATENCY offset e470cdd platform: tegra: bwmgr: add a disable knob 1d8e851 tegra: denver-knobs: Use correct CPU id for bgallowed db9711f bwmgr: Add errno to debug print 96ed52e drivers: usb: pmc: rebase pmc driver for kernel-4.4 6510703 tegra: denver-knobs: add tracer_control support 3a285a9 tegra: actmon: missing sysfs_attr_init() 0c83659 tegra: actmon: add sysfs node for emc load 2477286 platform: tegra: pmc: add usb2 sleepwalk APIs 8bcf839 pinctrl: add Tegra21x PADCTL/UPHY driver acaa486 platform: nvadsp: make version func static 6028232 Revert "platform: tegra: clean up fiq_enable() implementation" cc80660 Revert "drivers: platform: enumerate fiq_debugger device" ab2cc4c Revert "android: fiq_debugger: FIQ glue for ARM64 CPUs" 38ff9fd drivers: platform: enumerate fiq_debugger device 1dd509c platform: tegra: clean up fiq_enable() implementation 0a87d11 android: fiq_debugger: FIQ glue for ARM64 CPUs 74ec787 platform: nvadsp: add adsp os version 9bd6a7f platform: tegra: Remove use of is_partition_clk_disabled a56b821 drivers: class 'tegra-firmwares' c859a13 Kconfig: Rename included Kconfigs 804f706 platform: tegra: add kfuse sensing for hdcp 1.x 9b3510f drivers: platform: tegra: only compile tegra_irq_to_wake for Tegra186 fb5394c drivers: platform: tegra: switch powergating driver to CCF 66d0faf tegra: mc: declare tegra_get_chip_id and use in mc eb90d56 drivers: platform: Move DPD IO control into pmc driver 3816cef arm64: enable CCF a196e10 soc/tegra: Add TEGRA148 and TEGRA186 chip id 8354256 drivers: platform: tegra: switch powergate driver to tegra_get_chip_id() 473ce73 platform: tegra: mc: adapt la driver to upstream tegra_get_chip_id() 0f2668b Kconfig: replace source with trysource for external projects 266255a platform: tegra: Add support to find prod setting ac65ac1 platfor: tegra: prod: Do not use kmemleak_not_leak() for managed allocation 96358ea platform: tegra: prod: use devm_ for allocation 53933ed platform: tegra: Add sysfs entry for suspend/resume time bfef5bc drivers: platform: tegra: add tegra_wake_to_gpio() interface 4409ca6 tegra: central_actmon: fix DEBUG_FS=n build 80aa543 platform:tegra:bwmgr: fix build issue. f97d139 tegra: denver-knobs: fix build issue. bcfc89c platform: tegra: prod: Add managed version of tegra_prod_get_from_node 85288f1 platform: tegra: prod: Add support for nested prod nodes. 0be37d5 platform: tegra: prod: Get rid of tegra_prod_release() 753f71d platform: tegra: prod: Remove unused APIs from public header 5b92965 platform: tegra: pmc: Use devm for tegra_prod allocation 21af9cb platform: tegra: prod: Add APIs to managed allocation of prod_list 2d9312b platform: tegra: move definition of tegra_prod_list structure to private 0d1efe1 platform: tegra: prod: Use for_each_available_child_of_node() 3014a93 tegra:nvadsp:fix issue with CONFIG_DEBUG_FS=n edd37fd platform: tegra: prod: Use proper variable name 168ec7b platform: tegra: prod: Fix parsing of prod setting for package/board bc8cd66 platform: tegra: prod: Make allocated memory as kmemleak_not_leak() 380f89f PM/Domains: Remove use of 'need_save' and 'need_restore' fdf13ea platform: nvadsp: change perms of debugfs files bc34a73 nvadsp: console: keep track of app context 62d0040 nvadsp: console: fix app name handling f113a66 platform: nvadsp: export adsp dump sys e0e907b platform: tegra: nvadsp: fix setting boot freqs c7fb6ee Revert "drivers: platform: tegra: add proper config check" c5b1e8b platform:tegra:bwmgr: Fix bwmgr floor aggregation 584b06e platform: tegra: bwmgr: Add Security Engine Client f63d36d tegra: gpo: move gpio-tegra 8171ecb security: Use a common secure monitor interface 1359955 platform: nvadsp: fix unused function build issue aa55e67 platform: tegra: Remove unused functions 6f2d8d8 platform: tegra: Change value of need_save to 'true' 0c28fab Merge "Merge agic changes" into dev-kernel 0174199 platform: nvadsp: Correct AGIC irqs state dumps 0d7fec4 irqchip: Move AGIC Kconfig from nvadsp b7ce47f kernel: change kernel path 7627eeb platform: tegra: mc: fix coverity defects d4afd62 platform: tegra: remove platform emc driver for Tegra210 93e504f platform: tegra: mc: include module.h 4a3d8fa platform: tegra: add thermal throttling config b99e994 platform: tegra: move tegra_fiq_debugger from mach-tegra b89bbf4b tegra: t21x: restore irqs-t21x.h file 3ec5fa9 arm64: Copy over more T210 files 0c3285d platform: tegra: mc: enable latency allowance code compile 80b090f platform: tegra: mc: add T18x PTSA vars 345b7ee tegra: mc: add set LA funcion pointers 26c3314 platform: tegra: Add support for mask with 1s 62e11eb platform: tegra: mc: Don't compile latency_allowance for k4.4 dc9cafc platform: tegra: Add protection to code 55dabeb drivers: platform: tegra: add proper config check 99a65ef platform: tegra: tegra_usb_pmc depends on T210 eae2a6d tegra: denver-knobs: fix seq_printf return value 74467a6 TEMP: drivers: Kconfig: Use source instead of trysource 000acb1 platform: tegra: add missing headers and build fixes e228595 kconfig: add trysource to kernel-t18x 94daaaa drivers: platform: tegra: Initialize drivers/platform/tegra inside kernel-next c5c90b8 drivers: platform: tegra: Add miscellaneous platform specific drivers 3faa2fe drivers: platform: tegra: PTM driver for t12x and t21x 8953022 drivers: platform: tegra: Add kfuse driver 19a844c drivers: platform: tegra: Tegra USB Padctrl driver 4596579 drivers: platform: tegra: Add nvdumper source for platforms a03e4b0 drivers: platform: tegra: Add wakeup related source 363b7ee drivers: platform: tegra: central_actmon: Add common and support for T21x 9fe72d5 drivers: platform: tegra: mc: Add platform specific MC source 6c2b078 drivers: platform: tegra: Adding denver specific drivers 976c8b9 drivers: platform: tegra: Add bootloader drivers a97be5b drivers: platform: tegra: nvadsp: Add platform specific nvadsp drivers b29af75 drivers: platform: tegra: Add powergating drivers 899dddd platform: tegra: Add Tegra Clocks drivers for various platforms Signed-off-by: Mikko Perttunen Change-Id: Ic232ac71a09fe5176247692630db5bc6107573fa Signed-off-by: Nitin Kumbhar Reviewed-on: https://git-master.nvidia.com/r/1537316 Reviewed-by: svccoveritychecker GVS: Gerrit_Virtual_Submit --- drivers/platform/tegra/nvadsp/Kconfig | 80 + drivers/platform/tegra/nvadsp/Makefile | 41 + .../platform/tegra/nvadsp/adsp_console_dbfs.c | 428 +++++ .../platform/tegra/nvadsp/adsp_console_dbfs.h | 31 + .../tegra/nvadsp/adsp_console_ioctl.h | 55 + drivers/platform/tegra/nvadsp/adsp_cpustat.c | 330 ++++ drivers/platform/tegra/nvadsp/adsp_dfs.c | 847 +++++++++ .../tegra/nvadsp/adsp_shared_struct.h | 142 ++ drivers/platform/tegra/nvadsp/adspff.c | 422 +++++ drivers/platform/tegra/nvadsp/adspff.h | 138 ++ drivers/platform/tegra/nvadsp/amc.c | 184 ++ drivers/platform/tegra/nvadsp/amc.h | 58 + drivers/platform/tegra/nvadsp/ape_actmon.c | 984 ++++++++++ drivers/platform/tegra/nvadsp/ape_actmon.h | 86 + drivers/platform/tegra/nvadsp/app.c | 988 ++++++++++ .../platform/tegra/nvadsp/app_loader_linker.c | 960 ++++++++++ drivers/platform/tegra/nvadsp/aram_manager.c | 105 ++ drivers/platform/tegra/nvadsp/aram_manager.h | 30 + drivers/platform/tegra/nvadsp/dev-t21x.c | 319 ++++ drivers/platform/tegra/nvadsp/dev-t21x.h | 52 + drivers/platform/tegra/nvadsp/dev.c | 414 +++++ drivers/platform/tegra/nvadsp/dev.h | 183 ++ .../tegra/nvadsp/dram_app_mem_manager.c | 110 ++ .../tegra/nvadsp/dram_app_mem_manager.h | 30 + drivers/platform/tegra/nvadsp/emc_dfs.c | 465 +++++ .../platform/tegra/nvadsp/hwmailbox-t21x.h | 25 + drivers/platform/tegra/nvadsp/hwmailbox.c | 294 +++ drivers/platform/tegra/nvadsp/hwmailbox.h | 116 ++ drivers/platform/tegra/nvadsp/mailbox.c | 317 ++++ drivers/platform/tegra/nvadsp/mem_manager.c | 323 ++++ drivers/platform/tegra/nvadsp/mem_manager.h | 51 + drivers/platform/tegra/nvadsp/msgq.c | 170 ++ .../platform/tegra/nvadsp/nvadsp_arb_sema.c | 39 + drivers/platform/tegra/nvadsp/nvadsp_dram.c | 67 + .../tegra/nvadsp/nvadsp_shared_sema.c | 41 + drivers/platform/tegra/nvadsp/os-t21x.c | 20 + drivers/platform/tegra/nvadsp/os-t21x.h | 21 + drivers/platform/tegra/nvadsp/os.c | 1619 +++++++++++++++++ drivers/platform/tegra/nvadsp/os.h | 160 ++ 39 files changed, 10745 insertions(+) create mode 100644 drivers/platform/tegra/nvadsp/Kconfig create mode 100644 drivers/platform/tegra/nvadsp/Makefile create mode 100644 drivers/platform/tegra/nvadsp/adsp_console_dbfs.c create mode 100644 drivers/platform/tegra/nvadsp/adsp_console_dbfs.h create mode 100644 drivers/platform/tegra/nvadsp/adsp_console_ioctl.h create mode 100644 drivers/platform/tegra/nvadsp/adsp_cpustat.c create mode 100644 drivers/platform/tegra/nvadsp/adsp_dfs.c create mode 100644 drivers/platform/tegra/nvadsp/adsp_shared_struct.h create mode 100644 drivers/platform/tegra/nvadsp/adspff.c create mode 100644 drivers/platform/tegra/nvadsp/adspff.h create mode 100644 drivers/platform/tegra/nvadsp/amc.c create mode 100644 drivers/platform/tegra/nvadsp/amc.h create mode 100644 drivers/platform/tegra/nvadsp/ape_actmon.c create mode 100644 drivers/platform/tegra/nvadsp/ape_actmon.h create mode 100644 drivers/platform/tegra/nvadsp/app.c create mode 100644 drivers/platform/tegra/nvadsp/app_loader_linker.c create mode 100644 drivers/platform/tegra/nvadsp/aram_manager.c create mode 100644 drivers/platform/tegra/nvadsp/aram_manager.h create mode 100644 drivers/platform/tegra/nvadsp/dev-t21x.c create mode 100644 drivers/platform/tegra/nvadsp/dev-t21x.h create mode 100644 drivers/platform/tegra/nvadsp/dev.c create mode 100644 drivers/platform/tegra/nvadsp/dev.h create mode 100644 drivers/platform/tegra/nvadsp/dram_app_mem_manager.c create mode 100644 drivers/platform/tegra/nvadsp/dram_app_mem_manager.h create mode 100644 drivers/platform/tegra/nvadsp/emc_dfs.c create mode 100644 drivers/platform/tegra/nvadsp/hwmailbox-t21x.h create mode 100644 drivers/platform/tegra/nvadsp/hwmailbox.c create mode 100644 drivers/platform/tegra/nvadsp/hwmailbox.h create mode 100644 drivers/platform/tegra/nvadsp/mailbox.c create mode 100644 drivers/platform/tegra/nvadsp/mem_manager.c create mode 100644 drivers/platform/tegra/nvadsp/mem_manager.h create mode 100644 drivers/platform/tegra/nvadsp/msgq.c create mode 100644 drivers/platform/tegra/nvadsp/nvadsp_arb_sema.c create mode 100644 drivers/platform/tegra/nvadsp/nvadsp_dram.c create mode 100644 drivers/platform/tegra/nvadsp/nvadsp_shared_sema.c create mode 100644 drivers/platform/tegra/nvadsp/os-t21x.c create mode 100644 drivers/platform/tegra/nvadsp/os-t21x.h create mode 100644 drivers/platform/tegra/nvadsp/os.c create mode 100644 drivers/platform/tegra/nvadsp/os.h diff --git a/drivers/platform/tegra/nvadsp/Kconfig b/drivers/platform/tegra/nvadsp/Kconfig new file mode 100644 index 00000000..d7e990c5 --- /dev/null +++ b/drivers/platform/tegra/nvadsp/Kconfig @@ -0,0 +1,80 @@ +config TEGRA_NVADSP + bool "Enable Host ADSP driver" + depends on ARCH_TEGRA_APE + default n + help + Enables support for Host ADSP driver. + + If unsure, say N + +config TEGRA_NVADSP_ON_SMMU + bool "Use SMMU to relocate ADSP" + depends on (TEGRA_IOMMU_SMMU || OF_TEGRA_IOMMU_SMMU) && TEGRA_NVADSP + default n + help + Use SMMU to relocate ADSP OS. + +config TEGRA_ADSP_DFS + bool "Enable ADSP DFS" + depends on TEGRA_NVADSP + default n + help + Enable ADSP dynamic frequency scaling. Use this config + to scale adsp frequency via actmon or set fixed value. + + If unsure, say N + +config TEGRA_ADSP_ACTMON + bool "Enable ADSP ACTMON" + depends on TEGRA_ADSP_DFS + default n + help + Enable ADSP actmon. It converts adsp activty to frequency and + asks adsp dfs to set the adsp frequency. Use it if adsp frequency + to be scaled dynamically by actmon. + + If unsure, say N + +config TEGRA_ADSP_CPUSTAT + bool "Enable ADSP CPUSTAT" + depends on DEBUG_FS && TEGRA_NVADSP && !TEGRA_ADSP_ACTMON + default n + help + Enable ADSP cpu usage measurement using actmon + + If unsure, say N + +config TEGRA_ADSP_FILEIO + bool "Enable ADSP file io" + default n + help + Enable dumping to and reading from file on host from ADSP + + If unsure, say N + +config TEGRA_EMC_APE_DFS + bool "Enable emc dfs due to APE" + depends on ARCH_TEGRA_APE + default n + help + Enable emc dfs due to APE DRAM access + + If unsure, say N + +config TEGRA_ADSP_CONSOLE + bool "Enable ADSP console" + depends on TEGRA_NVADSP + default y + help + Enable ADSP console access + + If unsure, say N + +config MBOX_ACK_HANDLER + bool "Enable mailbox acknowledge handler" + depends on TEGRA_NVADSP + default n + help + Enable mailbox acknowledge handler + + if unsure, say N diff --git a/drivers/platform/tegra/nvadsp/Makefile b/drivers/platform/tegra/nvadsp/Makefile new file mode 100644 index 00000000..da6c1794 --- /dev/null +++ b/drivers/platform/tegra/nvadsp/Makefile @@ -0,0 +1,41 @@ +GCOV_PROFILE := y +ccflags-y += -Werror +ifeq ($(CONFIG_ARCH_TEGRA_18x_SOC),y) +ccflags-y += -I$(srctree)/../t18x/drivers/platform/tegra/nvadsp +endif + +obj-y := nvadsp.o +nvadsp-objs += dev.o os.o app.o app_loader_linker.o\ + amc.o nvadsp_dram.o \ + nvadsp_shared_sema.o nvadsp_arb_sema.o \ + hwmailbox.o mailbox.o msgq.o \ + mem_manager.o aram_manager.o dram_app_mem_manager.o + +ifeq ($(CONFIG_TEGRA_ADSP_DFS),y) +nvadsp-objs += adsp_dfs.o +endif + +ifeq ($(CONFIG_TEGRA_ADSP_ACTMON),y) +nvadsp-objs += ape_actmon.o +endif + +ifeq ($(CONFIG_TEGRA_EMC_APE_DFS),y) +nvadsp-objs += emc_dfs.o +endif + +ifeq ($(CONFIG_TEGRA_ADSP_CONSOLE),y) +nvadsp-objs += adsp_console_dbfs.o +endif + +ifeq ($(CONFIG_TEGRA_ADSP_CPUSTAT),y) +nvadsp-objs += adsp_cpustat.o +endif + +ifeq ($(CONFIG_ARCH_TEGRA_21x_SOC),y) +nvadsp-objs += dev-t21x.o +nvadsp-objs += os-t21x.o +endif + +ifeq ($(CONFIG_TEGRA_ADSP_FILEIO),y) +nvadsp-objs += adspff.o +endif diff --git a/drivers/platform/tegra/nvadsp/adsp_console_dbfs.c b/drivers/platform/tegra/nvadsp/adsp_console_dbfs.c new file mode 100644 index 00000000..0708723e --- /dev/null +++ b/drivers/platform/tegra/nvadsp/adsp_console_dbfs.c @@ -0,0 +1,428 @@ +/* + * adsp_console_dbfs.c + * + * adsp mailbox console driver + * + * Copyright (C) 2014-2016, 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "dev.h" +#include "adsp_console_ioctl.h" +#include "adsp_console_dbfs.h" + +#define USE_RUN_APP_API + +static int open_cnt; + +#define ADSP_APP_CTX_MAX 32 + +static uint64_t adsp_app_ctx_vals[ADSP_APP_CTX_MAX]; + +static int adsp_app_ctx_add(uint64_t ctx) +{ + int i; + + if (ctx == 0) + return -EINVAL; + + for (i = 0; i < ADSP_APP_CTX_MAX; i++) { + if (adsp_app_ctx_vals[i] == 0) { + adsp_app_ctx_vals[i] = ctx; + return 0; + } + } + return -EINVAL; +} + +static int adsp_app_ctx_check(uint64_t ctx) +{ + int i; + + if (ctx == 0) + return -EINVAL; + + for (i = 0; i < ADSP_APP_CTX_MAX; i++) { + if (adsp_app_ctx_vals[i] == ctx) + return 0; + } + return -EINVAL; +} + + +static void adsp_app_ctx_remove(uint64_t ctx) +{ + int i; + + for (i = 0; i < ADSP_APP_CTX_MAX; i++) { + if (adsp_app_ctx_vals[i] == ctx) { + adsp_app_ctx_vals[i] = 0; + return; + } + } +} + +static int adsp_consol_open(struct inode *i, struct file *f) +{ + + int ret; + uint16_t snd_mbox_id = 30; + struct nvadsp_cnsl *console = i->i_private; + struct device *dev = console->dev; + struct platform_device *pdev = to_platform_device(dev); + struct nvadsp_drv_data *drv_data = platform_get_drvdata(pdev); + + if (open_cnt) + return -EBUSY; + open_cnt++; + ret = 0; + f->private_data = console; + if (!drv_data->adsp_os_running) + goto exit_open; + ret = nvadsp_mbox_open(&console->shl_snd_mbox, &snd_mbox_id, + "adsp_send_cnsl", NULL, NULL); + if (!ret) + goto exit_open; + pr_err("adsp_consol: Failed to init adsp_consol send mailbox"); + memset(&console->shl_snd_mbox, 0, sizeof(struct nvadsp_mbox)); + open_cnt--; +exit_open: + return ret; +} +static int adsp_consol_close(struct inode *i, struct file *f) +{ + int ret = 0; + struct nvadsp_cnsl *console = i->i_private; + struct nvadsp_mbox *mbox = &console->shl_snd_mbox; + struct device *dev = console->dev; + struct platform_device *pdev = to_platform_device(dev); + struct nvadsp_drv_data *drv_data = platform_get_drvdata(pdev); + + open_cnt--; + if (!drv_data->adsp_os_running || (0 == mbox->id)) + goto exit_close; + ret = nvadsp_mbox_close(mbox); + if (ret) + pr_err("adsp_consol: Failed to close adsp_consol send mailbox)"); + memset(mbox, 0, sizeof(struct nvadsp_mbox)); +exit_close: + return ret; +} +static long +adsp_consol_ioctl(struct file *f, unsigned int cmd, unsigned long arg) +{ + + int ret = 0; + uint16_t *mid; + uint16_t mbxid = 0; + uint32_t data; + uint64_t ctx2; + nvadsp_app_info_t *app_info; + struct adsp_consol_run_app_arg_t app_args; + struct nvadsp_cnsl *console = f->private_data; + struct nvadsp_mbox *mbox; + struct device *dev = console->dev; + struct platform_device *pdev = to_platform_device(dev); + struct nvadsp_drv_data *drv_data = platform_get_drvdata(pdev); + void __user *uarg = (void __user *)arg; + + if (_IOC_TYPE(cmd) != NV_ADSP_CONSOLE_MAGIC) + return -EFAULT; + + if ((_IOC_NR(cmd) != _IOC_NR(ADSP_CNSL_LOAD)) && + (!drv_data->adsp_os_running)) { + dev_info(dev, "adsp_consol: os not running."); + return -EPERM; + } + + if ((_IOC_NR(cmd) != _IOC_NR(ADSP_CNSL_LOAD)) && + (0 == console->shl_snd_mbox.id)) { + dev_info(dev, "adsp_consol: Mailboxes not open."); + return -EPERM; + } + + switch (_IOC_NR(cmd)) { + case _IOC_NR(ADSP_CNSL_LOAD): + ret = 0; + + if (drv_data->adsp_os_running) + break; + mbxid = 30; + mbox = &console->shl_snd_mbox; + ret = nvadsp_os_load(); + if (ret) { + dev_info(dev, "adsp_consol: Load OS Failed."); + break; + } + ret = nvadsp_os_start(); + if (ret) { + dev_info(dev, "adsp_consol: Start OS Failed."); + break; + } + ret = nvadsp_mbox_open(mbox, &mbxid, + "adsp_send_cnsl", NULL, NULL); + if (!ret) + break; + pr_err("adsp_consol: Failed to init adsp_consol send mailbox"); + memset(mbox, 0, sizeof(struct nvadsp_mbox)); + break; + case _IOC_NR(ADSP_CNSL_RUN_APP): + if (!access_ok(0, uarg, + sizeof(struct adsp_consol_run_app_arg_t))) + return -EACCES; + ret = copy_from_user(&app_args, uarg, + sizeof(app_args)); + if (ret) { + ret = -EACCES; + break; + } + app_args.app_name[NVADSP_NAME_SZ_MAX] = '\0'; + +#ifdef USE_RUN_APP_API + app_args.ctx2 = (uint64_t)nvadsp_run_app(NULL, + app_args.app_name, + (nvadsp_app_args_t *)&app_args.args[0], + NULL, 0, true); + if (!app_args.ctx2) { + dev_info(dev, "adsp_consol: unable to run %s\n", + app_args.app_name); + return -EINVAL; + } + if (adsp_app_ctx_add(app_args.ctx2)) { + dev_info(dev, "adsp_consol: unable to add %s ctx\n", + app_args.app_name); + return -EINVAL; + } +#else + app_args.ctx1 = (uint64_t)nvadsp_app_load(app_args.app_path, + app_args.app_name); + if (!app_args.ctx1) { + dev_info(dev, + "adsp_consol: dynamic app load failed %s\n", + app_args.app_name); + return -EINVAL; + } + if (adsp_app_ctx_add(app_args.ctx1)) { + dev_info(dev, "adsp_consol: unable to add %s ctx\n", + app_args.app_name); + return -EINVAL; + } + + dev_info(dev, "adsp_consol: calling nvadsp_app_init\n"); + app_args.ctx2 = + (uint64_t)nvadsp_app_init((void *)app_args.ctx1, NULL); + if (!app_args.ctx2) { + dev_info(dev, + "adsp_consol: unable to initilize the app\n"); + return -EINVAL; + } + if (adsp_app_ctx_add(app_args.ctx2)) { + dev_info(dev, "adsp_consol: unable to add %s ctx\n", + app_args.app_name); + return -EINVAL; + } + + dev_info(dev, "adsp_consol: calling nvadsp_app_start\n"); + ret = nvadsp_app_start((void *)app_args.ctx2); + if (ret) { + dev_info(dev, "adsp_consol: unable to start the app\n"); + break; + } +#endif + ret = copy_to_user((void __user *) arg, &app_args, + sizeof(struct adsp_consol_run_app_arg_t)); + if (ret) + ret = -EACCES; + break; + case _IOC_NR(ADSP_CNSL_STOP_APP): + if (!access_ok(0, uarg, + sizeof(struct adsp_consol_run_app_arg_t))) + return -EACCES; + ret = copy_from_user(&app_args, uarg, + sizeof(app_args)); + if (ret) { + ret = -EACCES; + break; + } +#ifdef USE_RUN_APP_API + if (!app_args.ctx2) { + ret = -EACCES; + break; + } + if (adsp_app_ctx_check(app_args.ctx2)) { + dev_info(dev, "adsp_consol: unable to check %s ctx\n", + app_args.app_name); + return -EINVAL; + } + + app_args.ctx1 = (uint64_t) + ((nvadsp_app_info_t *)app_args.ctx2)->handle; + + nvadsp_exit_app((nvadsp_app_info_t *)app_args.ctx2, false); + nvadsp_app_unload((const void *)app_args.ctx1); + + adsp_app_ctx_remove(app_args.ctx2); +#else + if ((!app_args.ctx2) || (!app_args.ctx1)) { + ret = -EACCES; + break; + } + + if (adsp_app_ctx_check(app_args.ctx2) || + adsp_app_ctx_check(app_args.ctx1)) { + dev_info(dev, "adsp_consol: unable to check %s ctx\n", + app_args.app_name); + return -EINVAL; + } + + nvadsp_app_deinit((void *)app_args.ctx2); + nvadsp_app_unload((void *)app_args.ctx1); + + adsp_app_ctx_remove(app_args.ctx2); + adsp_app_ctx_remove(app_args.ctx1); +#endif + + break; + case _IOC_NR(ADSP_CNSL_CLR_BUFFER): + break; + case _IOC_NR(ADSP_CNSL_OPN_MBX): + if (!access_ok(0, uarg, sizeof(ctx2))) + return -EACCES; + ret = copy_from_user(&ctx2, uarg, sizeof(ctx2)); + if (ret) { + ret = -EACCES; + break; + } + if (adsp_app_ctx_check(ctx2)) { + dev_info(dev, "adsp_consol: unable to check ctx\n"); + return -EINVAL; + } + + app_info = (nvadsp_app_info_t *)ctx2; + + if (app_info && app_info->mem.shared) { + mid = (short *)(app_info->mem.shared); + dev_info(dev, "adsp_consol: open %x\n", *mid); + mbxid = *mid; + } + + ret = nvadsp_mbox_open(&console->app_mbox, &mbxid, + "app_mbox", NULL, NULL); + if (ret) { + pr_err("adsp_consol: Failed to open app mailbox"); + ret = -EACCES; + } + break; + case _IOC_NR(ADSP_CNSL_CLOSE_MBX): + mbox = &console->app_mbox; + while (!nvadsp_mbox_recv(mbox, &data, 0, 0)) + ; + ret = nvadsp_mbox_close(mbox); + if (ret) + break; + memset(mbox, 0, sizeof(struct nvadsp_mbox)); + break; + case _IOC_NR(ADSP_CNSL_PUT_MBX): + if (!access_ok(0, uarg, + sizeof(uint32_t))) + return -EACCES; + ret = copy_from_user(&data, uarg, + sizeof(uint32_t)); + if (ret) { + ret = -EACCES; + break; + } + ret = nvadsp_mbox_send(&console->app_mbox, data, + NVADSP_MBOX_SMSG, 0, 0); + break; + case _IOC_NR(ADSP_CNSL_GET_MBX): + if (!access_ok(0, uarg, + sizeof(uint32_t))) + return -EACCES; + ret = nvadsp_mbox_recv(&console->app_mbox, &data, 0, 0); + if (ret) + break; + ret = copy_to_user(uarg, &data, + sizeof(uint32_t)); + if (ret) + ret = -EACCES; + break; + case _IOC_NR(ADSP_CNSL_PUT_DATA): + if (!access_ok(0, uarg, + sizeof(struct adsp_consol_run_app_arg_t))) + return -EACCES; + ret = copy_from_user(&data, uarg, sizeof(uint32_t)); + if (ret) { + ret = -EACCES; + break; + } + return nvadsp_mbox_send(&console->shl_snd_mbox, data, + NVADSP_MBOX_SMSG, 0, 0); + break; + default: + dev_info(dev, "adsp_consol: invalid command\n"); + return -EINVAL; + } + return ret; +} + +static const struct file_operations adsp_console_operations = { + .open = adsp_consol_open, + .release = adsp_consol_close, +#ifdef CONFIG_COMPAT + .compat_ioctl = adsp_consol_ioctl, +#endif + .unlocked_ioctl = adsp_consol_ioctl +}; + +int +adsp_create_cnsl(struct dentry *adsp_debugfs_root, struct nvadsp_cnsl *cnsl) +{ + int ret = 0; + + struct device *dev = cnsl->dev; + + if (IS_ERR_OR_NULL(adsp_debugfs_root)) { + ret = -ENOENT; + goto err_out; + } + + if (!debugfs_create_file("adsp_console", S_IRUSR, + adsp_debugfs_root, cnsl, + &adsp_console_operations)) { + dev_err(dev, + "unable to create adsp console debug fs file\n"); + ret = -ENOENT; + goto err_out; + } + + memset(&cnsl->app_mbox, 0, sizeof(cnsl->app_mbox)); + +err_out: + return ret; +} diff --git a/drivers/platform/tegra/nvadsp/adsp_console_dbfs.h b/drivers/platform/tegra/nvadsp/adsp_console_dbfs.h new file mode 100644 index 00000000..add0b159 --- /dev/null +++ b/drivers/platform/tegra/nvadsp/adsp_console_dbfs.h @@ -0,0 +1,31 @@ +/* +* adsp_console_dbfs.h +* +* A header file for adsp console driver +* +* Copyright (C) 2014 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. +* +*/ + +#ifndef ADSP_CNSL_DBFS_H +#define ADSP_CNSL_DBFS_H + +struct nvadsp_cnsl { + struct device *dev; + struct nvadsp_mbox shl_snd_mbox; + struct nvadsp_mbox app_mbox; +}; + +int +adsp_create_cnsl(struct dentry *adsp_debugfs_root, struct nvadsp_cnsl *cnsl); + +#endif /* ADSP_CNSL_DBFS_H */ diff --git a/drivers/platform/tegra/nvadsp/adsp_console_ioctl.h b/drivers/platform/tegra/nvadsp/adsp_console_ioctl.h new file mode 100644 index 00000000..8d0b2d0f --- /dev/null +++ b/drivers/platform/tegra/nvadsp/adsp_console_ioctl.h @@ -0,0 +1,55 @@ +/* + * adsp_console_ioctl.h + * + * A header file for adsp console driver + * + * Copyright (C) 2014-2016, 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. + * + */ + +#ifndef ADSP_CNSL_IOCTL_H +#define ADSP_CNSL_IOCTL_H +#include + +#if !defined(NVADSP_NAME_SZ) +#define NVADSP_NAME_SZ 128 +#endif + +#define NVADSP_NAME_SZ_MAX (NVADSP_NAME_SZ - 1) + +#if !defined(ARGV_SIZE_IN_WORDS) +#define ARGV_SIZE_IN_WORDS 128 +#endif + +#define NV_ADSP_CONSOLE_MAGIC 'q' + +struct adsp_consol_run_app_arg_t { + char app_name[NVADSP_NAME_SZ]; + char app_path[NVADSP_NAME_SZ]; + uint32_t args[ARGV_SIZE_IN_WORDS + 1]; + uint64_t ctx1; + uint64_t ctx2; +}; + +#define ADSP_CNSL_LOAD _IO(NV_ADSP_CONSOLE_MAGIC, 0x01) +#define ADSP_CNSL_CLR_BUFFER _IO(NV_ADSP_CONSOLE_MAGIC, 0x02) +#define ADSP_CNSL_PUT_DATA _IOW(NV_ADSP_CONSOLE_MAGIC, 0x03, uint32_t *) +#define ADSP_CNSL_RUN_APP _IOWR(NV_ADSP_CONSOLE_MAGIC, 0x04,\ + struct adsp_consol_run_app_arg_t *) +#define ADSP_CNSL_STOP_APP _IOWR(NV_ADSP_CONSOLE_MAGIC, 0x05,\ + struct adsp_consol_run_app_arg_t *) +#define ADSP_CNSL_OPN_MBX _IOW(NV_ADSP_CONSOLE_MAGIC, 0x06, void *) +#define ADSP_CNSL_CLOSE_MBX _IO(NV_ADSP_CONSOLE_MAGIC, 0x07) +#define ADSP_CNSL_PUT_MBX _IOW(NV_ADSP_CONSOLE_MAGIC, 0x08, uint32_t *) +#define ADSP_CNSL_GET_MBX _IOR(NV_ADSP_CONSOLE_MAGIC, 0x09, uint32_t *) + +#endif diff --git a/drivers/platform/tegra/nvadsp/adsp_cpustat.c b/drivers/platform/tegra/nvadsp/adsp_cpustat.c new file mode 100644 index 00000000..033a8e50 --- /dev/null +++ b/drivers/platform/tegra/nvadsp/adsp_cpustat.c @@ -0,0 +1,330 @@ +/* + * Copyright (C) 2015-2016, NVIDIA Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "dev.h" + +#define ACTMON_DEV_CTRL 0x00 +#define ACTMON_DEV_CTRL_ENB (0x1 << 31) +#define ACTMON_DEV_CTRL_AT_END_ENB (0x1 << 15) +#define ACTMON_DEV_CTRL_PERIODIC_ENB (0x1 << 13) +#define ACTMON_DEV_CTRL_SAMPLE_PERIOD_VAL_SHIFT (0) +#define ACTMON_DEV_CTRL_SAMPLE_PERIOD_MASK (0xff << 0) + +#define ACTMON_DEV_COUNT 0x18 + +#define ACTMON_DEV_INTR_STATUS 0x20 +#define ACTMON_DEV_INTR_AT_END (0x1 << 27) + +#define ACTMON_DEV_COUNT_WEGHT 0x24 + +#define ACTMON_DEV_SAMPLE_CTRL 0x28 +#define ACTMON_DEV_SAMPLE_CTRL_TICK_65536 (0x1 << 2) +#define ACTMON_DEV_SAMPLE_CTRL_TICK_256 (0x0 << 1) + +#define AMISC_ACTMON_0 0x54 +#define AMISC_ACTMON_CNT_TARGET_ENABLE (0x1 << 31) + +#define ACTMON_REG_OFFSET 0x800 +/* milli second divider as SAMPLE_TICK*/ +#define SAMPLE_MS_DIVIDER 65536 + + +struct adsp_cpustat { + int irq; + struct device *device; + const char *dev_id; + spinlock_t lock; + struct clk *ape_clk; + struct clk *adsp_clk; + unsigned long ape_freq; + unsigned long adsp_freq; + u64 cur_usage; + bool enable; + u64 max_usage; + void __iomem *base; +}; + +static struct adsp_cpustat cpustat; + +static struct adsp_cpustat *cpumon; + +static inline u32 actmon_readl(u32 offset) +{ + return __raw_readl(cpumon->base + offset); +} +static inline void actmon_writel(u32 val, u32 offset) +{ + __raw_writel(val, cpumon->base + offset); +} + +static inline void actmon_wmb(void) +{ + wmb(); +} + +static irqreturn_t adsp_cpustat_isr(int irq, void *dev_id) +{ + u32 val; + unsigned long period, flags; + + spin_lock_irqsave(&cpumon->lock, flags); + val = actmon_readl(ACTMON_DEV_INTR_STATUS); + actmon_writel(val, ACTMON_DEV_INTR_STATUS); + + if (val & ACTMON_DEV_INTR_AT_END) { + period = (255 * SAMPLE_MS_DIVIDER) / cpumon->ape_freq; + + cpumon->cur_usage = + ((u64)actmon_readl(ACTMON_DEV_COUNT) * 100) / (period * cpumon->adsp_freq); + if (cpumon->cur_usage > cpumon->max_usage) + cpumon->max_usage = cpumon->cur_usage; + } + spin_unlock_irqrestore(&cpumon->lock, flags); + + return IRQ_HANDLED; +} + +static void configure_actmon(void) +{ + u32 val; + + /* Set countb weight to 256 */ + actmon_writel(0x100, ACTMON_DEV_COUNT_WEGHT); + + /* Enable periodic sampling */ + val = actmon_readl(ACTMON_DEV_CTRL); + val |= ACTMON_DEV_CTRL_PERIODIC_ENB; + + /* Set sampling period to max i,e, 255 ape clks */ + val &= ~ACTMON_DEV_CTRL_SAMPLE_PERIOD_MASK; + val |= (0xFF << + ACTMON_DEV_CTRL_SAMPLE_PERIOD_VAL_SHIFT) + & ACTMON_DEV_CTRL_SAMPLE_PERIOD_MASK; + + /* Enable the AT_END interrupt */ + val |= ACTMON_DEV_CTRL_AT_END_ENB; + actmon_writel(val, ACTMON_DEV_CTRL); + + actmon_writel(ACTMON_DEV_SAMPLE_CTRL_TICK_65536, + ACTMON_DEV_SAMPLE_CTRL); + actmon_wmb(); +} + +static void adsp_cpustat_enable(void) +{ + u32 val; + unsigned long flags; + + spin_lock_irqsave(&cpumon->lock, flags); + + val = actmon_readl(ACTMON_DEV_CTRL); + val |= ACTMON_DEV_CTRL_ENB; + actmon_writel(val, ACTMON_DEV_CTRL); + actmon_wmb(); + + enable_irq(cpumon->irq); + spin_unlock_irqrestore(&cpumon->lock, flags); +} + +static void adsp_cpustat_disable(void) +{ + u32 val; + unsigned long flags; + + spin_lock_irqsave(&cpumon->lock, flags); + disable_irq(cpumon->irq); + + val = actmon_readl(ACTMON_DEV_CTRL); + val &= ~ACTMON_DEV_CTRL_ENB; + actmon_writel(val, ACTMON_DEV_CTRL); + actmon_writel(0xffffffff, ACTMON_DEV_INTR_STATUS); + actmon_wmb(); + + spin_unlock_irqrestore(&cpumon->lock, flags); +} + +#define RW_MODE (S_IWUSR | S_IRUSR) +#define RO_MODE S_IRUSR + +static int cur_usage_get(void *data, u64 *val) +{ + *val = cpumon->cur_usage; + return 0; +} +DEFINE_SIMPLE_ATTRIBUTE(cur_usage_fops, cur_usage_get, NULL, "%llu\n"); + +static int max_usage_get(void *data, u64 *val) +{ + *val = cpumon->max_usage; + return 0; +} +DEFINE_SIMPLE_ATTRIBUTE(max_usage_fops, max_usage_get, NULL, "%llu\n"); + +static int enable_set(void *data, u64 val) +{ + if (cpumon->enable == (bool)val) + return 0; + cpumon->enable = (bool)val; + + if (cpumon->enable) + adsp_cpustat_enable(); + else + adsp_cpustat_disable(); + return 0; +} + +static int enable_get(void *data, u64 *val) +{ + *val = cpumon->enable; + return 0; +} +DEFINE_SIMPLE_ATTRIBUTE(enable_fops, enable_get, enable_set, "%llu\n"); + +static int cpustat_debugfs_init(struct nvadsp_drv_data *drv) +{ + int ret = -ENOMEM; + struct dentry *d, *dir; + + if (!drv->adsp_debugfs_root) + return ret; + dir = debugfs_create_dir("adsp_cpustat", drv->adsp_debugfs_root); + if (!dir) + return ret; + + d = debugfs_create_file( + "cur_usage", RO_MODE, dir, cpumon, &cur_usage_fops); + if (!d) + return ret; + + d = debugfs_create_file( + "max_usage", RO_MODE, dir, cpumon, &max_usage_fops); + if (!d) + return ret; + + d = debugfs_create_file( + "enable", RW_MODE, dir, cpumon, &enable_fops); + if (!d) + return ret; + + return 0; +} + +int adsp_cpustat_init(struct platform_device *pdev) +{ + struct nvadsp_drv_data *drv = platform_get_drvdata(pdev); + static void __iomem *amisc_base; + u32 val; + int ret = -EINVAL; + + if (drv->cpustat_initialized) + return 0; + + cpumon = &cpustat; + spin_lock_init(&cpumon->lock); + cpumon->base = drv->base_regs[AMISC] + ACTMON_REG_OFFSET; + amisc_base = drv->base_regs[AMISC]; + + cpumon->ape_clk = clk_get_sys(NULL, "adsp.ape"); + if (IS_ERR_OR_NULL(cpumon->ape_clk)) { + dev_err(cpumon->device, "Failed to find adsp.ape clk\n"); + ret = -EINVAL; + goto err_ape_clk; + } + + ret = clk_prepare_enable(cpumon->ape_clk); + if (ret) { + dev_err(cpumon->device, "Failed to enable ape clock\n"); + goto err_ape_enable; + } + cpumon->ape_freq = clk_get_rate(cpumon->ape_clk) / 1000; + + cpumon->adsp_clk = clk_get_sys(NULL, "adsp_cpu"); + if (IS_ERR_OR_NULL(cpumon->adsp_clk)) { + dev_err(cpumon->device, "Failed to find adsp cpu clock\n"); + ret = -EINVAL; + goto err_adsp_clk; + } + + ret = clk_prepare_enable(cpumon->adsp_clk); + if (ret) { + dev_err(cpumon->device, "Failed to enable adsp cpu clock\n"); + goto err_adsp_enable; + } + cpumon->adsp_freq = clk_get_rate(cpumon->adsp_clk) / 1000; + + /* Enable AMISC_ACTMON */ + val = __raw_readl(amisc_base + AMISC_ACTMON_0); + val |= AMISC_ACTMON_CNT_TARGET_ENABLE; + __raw_writel(val, amisc_base + AMISC_ACTMON_0); + + /* Clear all interrupts */ + actmon_writel(0xffffffff, ACTMON_DEV_INTR_STATUS); + + /* One time configuration of actmon regs */ + configure_actmon(); + + cpumon->irq = drv->agic_irqs[ACTMON_VIRQ]; + ret = request_irq(cpumon->irq, adsp_cpustat_isr, + IRQ_TYPE_LEVEL_HIGH, "adsp_actmon", cpumon); + if (ret) { + dev_err(cpumon->device, "Failed irq %d request\n", cpumon->irq); + goto err_irq; + } + + cpustat_debugfs_init(drv); + + drv->cpustat_initialized = true; + + return 0; +err_irq: + clk_disable_unprepare(cpumon->adsp_clk); +err_adsp_enable: + clk_put(cpumon->adsp_clk); +err_adsp_clk: + clk_disable_unprepare(cpumon->ape_clk); +err_ape_enable: + clk_put(cpumon->ape_clk); +err_ape_clk: + return ret; +} + +int adsp_cpustat_exit(struct platform_device *pdev) +{ + status_t ret = 0; + struct nvadsp_drv_data *drv = platform_get_drvdata(pdev); + if (!drv->cpustat_initialized) { + ret = -EINVAL; + goto end; + } + + free_irq(cpumon->irq, cpumon); + clk_disable_unprepare(cpumon->adsp_clk); + clk_put(cpumon->adsp_clk); + clk_put(cpumon->ape_clk); + drv->cpustat_initialized = false; + +end: + return ret; +} diff --git a/drivers/platform/tegra/nvadsp/adsp_dfs.c b/drivers/platform/tegra/nvadsp/adsp_dfs.c new file mode 100644 index 00000000..48ee0326 --- /dev/null +++ b/drivers/platform/tegra/nvadsp/adsp_dfs.c @@ -0,0 +1,847 @@ +/* + * adsp_dfs.c + * + * adsp dynamic frequency scaling + * + * Copyright (C) 2014-2016, 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 +#include +#include +#include +#include +#include +#include + +#include "dev.h" +#include "ape_actmon.h" +#include "os.h" + +#ifndef CONFIG_TEGRA_ADSP_ACTMON +void actmon_rate_change(unsigned long freq, bool override) +{ + +} +#endif + +#define MBOX_TIMEOUT 5000 /* in ms */ +#define HOST_ADSP_DFS_MBOX_ID 3 + +enum adsp_dfs_reply { + ACK, + NACK, +}; + +/* + * Freqency in Hz.The frequency always needs to be a multiple of 12.8 Mhz and + * should be extended with a slab 38.4 Mhz. + */ +static unsigned long adsp_cpu_freq_table[] = { + MIN_ADSP_FREQ, + MIN_ADSP_FREQ * 2, + MIN_ADSP_FREQ * 3, + MIN_ADSP_FREQ * 4, + MIN_ADSP_FREQ * 5, + MIN_ADSP_FREQ * 6, + MIN_ADSP_FREQ * 7, + MIN_ADSP_FREQ * 8, + MIN_ADSP_FREQ * 9, + MIN_ADSP_FREQ * 10, + MIN_ADSP_FREQ * 11, + MIN_ADSP_FREQ * 12, + MIN_ADSP_FREQ * 13, + MIN_ADSP_FREQ * 14, + MIN_ADSP_FREQ * 15, + MIN_ADSP_FREQ * 16, + MIN_ADSP_FREQ * 17, + MIN_ADSP_FREQ * 18, + MIN_ADSP_FREQ * 19, + MIN_ADSP_FREQ * 20, + MIN_ADSP_FREQ * 21, +}; + +struct adsp_dfs_policy { + bool enable; +/* update_freq_flag = TRUE, ADSP ACKed the new freq + * = FALSE, ADSP NACKed the new freq + */ + bool update_freq_flag; + + const char *clk_name; + unsigned long min; /* in kHz */ + unsigned long max; /* in kHz */ + unsigned long cur; /* in kHz */ + unsigned long cpu_min; /* ADSP min freq(KHz). Remain unchanged */ + unsigned long cpu_max; /* ADSP max freq(KHz). Remain unchanged */ + + struct clk *adsp_clk; + struct notifier_block rate_change_nb; + struct nvadsp_mbox mbox; + +#ifdef CONFIG_DEBUG_FS + struct dentry *root; +#endif + unsigned long ovr_freq; +}; + +struct adsp_freq_stats { + struct device *dev; + unsigned long long last_time; + int last_index; + u64 time_in_state[sizeof(adsp_cpu_freq_table) \ + / sizeof(adsp_cpu_freq_table[0])]; + int state_num; +}; + +static struct adsp_dfs_policy *policy; +static struct adsp_freq_stats freq_stats; +static struct device *device; +static struct clk *ape_emc_clk; + + +static DEFINE_MUTEX(policy_mutex); + +static bool is_os_running(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + struct nvadsp_drv_data *drv_data = platform_get_drvdata(pdev); + + if (!drv_data->adsp_os_running) { + dev_dbg(&pdev->dev, "%s: adsp os is not loaded\n", __func__); + return false; + } + return true; +} +/* Expects and returns freq in Hz as table is formmed in terms of Hz */ +static unsigned long adsp_get_target_freq(unsigned long tfreq, int *index) +{ + int i; + int size = sizeof(adsp_cpu_freq_table) / sizeof(adsp_cpu_freq_table[0]); + + if (tfreq <= adsp_cpu_freq_table[0]) { + *index = 0; + return adsp_cpu_freq_table[0]; + } + + if (tfreq >= adsp_cpu_freq_table[size - 1]) { + *index = size - 1; + return adsp_cpu_freq_table[size - 1]; + } + + for (i = 1; i < size; i++) { + if ((tfreq <= adsp_cpu_freq_table[i]) && + (tfreq > adsp_cpu_freq_table[i - 1])) { + *index = i; + return adsp_cpu_freq_table[i]; + } + } + + return 0; +} + +static void adspfreq_stats_update(void) +{ + unsigned long long cur_time; + + cur_time = get_jiffies_64(); + freq_stats.time_in_state[freq_stats.last_index] += cur_time - + freq_stats.last_time; + freq_stats.last_time = cur_time; +} + +/* adsp clock rate change notifier callback */ +static int adsp_dfs_rc_callback( + struct notifier_block *nb, unsigned long rate, void *v) +{ + unsigned long freq = rate / 1000; + int old_index, new_index = 0; + + /* update states */ + adspfreq_stats_update(); + + old_index = freq_stats.last_index; + adsp_get_target_freq(rate, &new_index); + if (old_index != new_index) + freq_stats.last_index = new_index; + + if (policy->ovr_freq && freq == policy->ovr_freq) { + /* Re-init ACTMON when user requested override freq is met */ + actmon_rate_change(freq, true); + policy->ovr_freq = 0; + } else + actmon_rate_change(freq, false); + + return NOTIFY_OK; +}; + +static struct adsp_dfs_policy dfs_policy = { + .enable = 1, + .clk_name = "adsp_cpu", + .rate_change_nb = { + .notifier_call = adsp_dfs_rc_callback, + }, +}; + +/* + * update_freq - update adsp freq and ask adsp to change timer as + * change in adsp freq. + * tfreq - target frequency in KHz + * return - final freq got set. + * - 0, incase of error. + * + * Note - Policy->cur would be updated via rate + * change notifier, when freq is changed in hw + * + */ +static unsigned long update_freq(unsigned long tfreq) +{ + u32 efreq; + int index; + int ret; + unsigned long old_freq; + enum adsp_dfs_reply reply; + struct nvadsp_mbox *mbx = &policy->mbox; + struct nvadsp_drv_data *drv = dev_get_drvdata(device); + + tfreq = adsp_get_target_freq(tfreq * 1000, &index); + if (!tfreq) { + dev_info(device, "unable get the target freq\n"); + return 0; + } + + old_freq = policy->cur; + + if ((tfreq / 1000) == old_freq) { + dev_dbg(device, "old and new target_freq is same\n"); + return 0; + } + + ret = clk_set_rate(policy->adsp_clk, tfreq); + if (ret) { + dev_err(device, "failed to set adsp freq:%d\n", ret); + policy->update_freq_flag = false; + return 0; + } + + efreq = adsp_to_emc_freq(tfreq / 1000); + + if (IS_ENABLED(CONFIG_COMMON_CLK)) { + tegra_bwmgr_set_emc(drv->bwmgr, efreq * 1000, + TEGRA_BWMGR_SET_EMC_FLOOR); + } else { + ret = clk_set_rate(ape_emc_clk, efreq * 1000); + if (ret) { + dev_err(device, "failed to set ape.emc clk:%d\n", ret); + policy->update_freq_flag = false; + goto err_out; + } + } + + dev_dbg(device, "sending change in freq:%lu\n", tfreq); + /* + * Ask adsp to do action upon change in freq. ADSP and Host need to + * maintain the same freq table. + */ + ret = nvadsp_mbox_send(mbx, index, + NVADSP_MBOX_SMSG, true, 100); + if (ret) { + dev_err(device, "%s:host to adsp, mbox_send failure. ret:%d\n", + __func__, ret); + policy->update_freq_flag = false; + goto err_out; + } + + ret = nvadsp_mbox_recv(&policy->mbox, &reply, true, MBOX_TIMEOUT); + if (ret) { + dev_err(device, "%s:host to adsp, mbox_receive failure. ret:%d\n", + __func__, ret); + policy->update_freq_flag = false; + goto err_out; + } + + switch (reply) { + case ACK: + /* Set Update freq flag */ + dev_dbg(device, "adsp freq change status:ACK\n"); + policy->update_freq_flag = true; + break; + case NACK: + /* Set Update freq flag */ + dev_dbg(device, "adsp freq change status:NACK\n"); + policy->update_freq_flag = false; + break; + default: + dev_err(device, "Error: adsp freq change status\n"); + } + + dev_dbg(device, "%s:status received from adsp: %s, tfreq:%lu\n", __func__, + (policy->update_freq_flag == true ? "ACK" : "NACK"), tfreq); + +err_out: + if (!policy->update_freq_flag) { + ret = clk_set_rate(policy->adsp_clk, old_freq * 1000); + if (ret) { + dev_err(device, "failed to resume adsp freq:%lu\n", old_freq); + policy->update_freq_flag = false; + } + + efreq = adsp_to_emc_freq(old_freq / 1000); + if (IS_ENABLED(CONFIG_COMMON_CLK)) { + tegra_bwmgr_set_emc(drv->bwmgr, efreq * 1000, + TEGRA_BWMGR_SET_EMC_FLOOR); + } else { + ret = clk_set_rate(ape_emc_clk, efreq * 1000); + if (ret) { + dev_err(device, + "failed to set ape.emc clk:%d\n", ret); + policy->update_freq_flag = false; + } + } + + tfreq = old_freq; + } + return tfreq / 1000; +} + +/* Set adsp dfs policy min freq(Khz) */ +static int policy_min_set(void *data, u64 val) +{ + int ret = -EINVAL; + unsigned long min = (unsigned long)val; + + if (!is_os_running(device)) + return ret; + + mutex_lock(&policy_mutex); + if (!policy->enable) { + dev_err(device, "adsp dfs policy is not enabled\n"); + goto exit_out; + } + + if (min == policy->min) + goto exit_out; + else if (min < policy->cpu_min) + min = policy->cpu_min; + else if (min >= policy->cpu_max) + min = policy->cpu_max; + + if (min > policy->cur) { + min = update_freq(min); + if (min) + policy->cur = min; + } + + if (min) + policy->min = min; + + ret = 0; +exit_out: + mutex_unlock(&policy_mutex); + return ret; +} + +#ifdef CONFIG_DEBUG_FS + +#define RW_MODE (S_IWUSR | S_IRUSR) +#define RO_MODE S_IRUSR + +/* Get adsp dfs staus: 0: disabled, 1: enabled */ +static int dfs_enable_get(void *data, u64 *val) +{ + mutex_lock(&policy_mutex); + *val = policy->enable; + mutex_unlock(&policy_mutex); + + return 0; +} + +/* Enable/disable adsp dfs */ +static int dfs_enable_set(void *data, u64 val) +{ + mutex_lock(&policy_mutex); + policy->enable = (bool) val; + mutex_unlock(&policy_mutex); + + return 0; +} +DEFINE_SIMPLE_ATTRIBUTE(enable_fops, dfs_enable_get, + dfs_enable_set, "%llu\n"); + +/* Get adsp dfs policy min freq(KHz) */ +static int policy_min_get(void *data, u64 *val) +{ + if (!is_os_running(device)) + return -EINVAL; + + mutex_lock(&policy_mutex); + *val = policy->min; + mutex_unlock(&policy_mutex); + + return 0; +} + +DEFINE_SIMPLE_ATTRIBUTE(min_fops, policy_min_get, + policy_min_set, "%llu\n"); + +/* Get adsp dfs policy max freq(KHz) */ +static int policy_max_get(void *data, u64 *val) +{ + if (!is_os_running(device)) + return -EINVAL; + + mutex_lock(&policy_mutex); + *val = policy->max; + mutex_unlock(&policy_mutex); + return 0; +} + +/* Set adsp dfs policy max freq(KHz) */ +static int policy_max_set(void *data, u64 val) +{ + int ret = -EINVAL; + unsigned long max = (unsigned long)val; + + if (!is_os_running(device)) + return ret; + + mutex_lock(&policy_mutex); + if (!policy->enable) { + dev_err(device, "adsp dfs policy is not enabled\n"); + goto exit_out; + } + + if (!max || ((max > policy->cpu_max) || (max == policy->max))) + goto exit_out; + + else if (max <= policy->cpu_min) + max = policy->cpu_min; + + if (max < policy->cur) + max = update_freq(max); + + if (max) + policy->cur = policy->max = max; + ret = 0; +exit_out: + mutex_unlock(&policy_mutex); + return ret; +} +DEFINE_SIMPLE_ATTRIBUTE(max_fops, policy_max_get, + policy_max_set, "%llu\n"); + +/* Get adsp dfs policy's current freq */ +static int policy_cur_get(void *data, u64 *val) +{ + if (!is_os_running(device)) + return -EINVAL; + + mutex_lock(&policy_mutex); + *val = policy->cur; + mutex_unlock(&policy_mutex); + + return 0; +} + +/* Set adsp dfs policy cur freq(Khz) */ +static int policy_cur_set(void *data, u64 val) +{ + int ret = -EINVAL; + unsigned long cur = (unsigned long)val; + + if (!is_os_running(device)) + return ret; + + mutex_lock(&policy_mutex); + if (policy->enable) { + dev_err(device, "adsp dfs is enabled, should be disabled first\n"); + goto exit_out; + } + + if (!cur || cur == policy->cur) + goto exit_out; + + /* Check tfreq policy sanity */ + if (cur < policy->min) + cur = policy->min; + else if (cur > policy->max) + cur = policy->max; + + cur = update_freq(cur); + if (cur) + policy->cur = cur; + ret = 0; +exit_out: + mutex_unlock(&policy_mutex); + return ret; +} + +DEFINE_SIMPLE_ATTRIBUTE(cur_fops, policy_cur_get, + policy_cur_set, "%llu\n"); + +/* + * Print residency in each freq levels + */ +static void dump_stats_table(struct seq_file *s, struct adsp_freq_stats *fstats) +{ + int i; + + mutex_lock(&policy_mutex); + if (is_os_running(device)) + adspfreq_stats_update(); + + for (i = 0; i < fstats->state_num; i++) { + seq_printf(s, "%lu %llu\n", + (long unsigned int)(adsp_cpu_freq_table[i] / 1000), + cputime64_to_clock_t(fstats->time_in_state[i])); + } + mutex_unlock(&policy_mutex); +} + +static int show_time_in_state(struct seq_file *s, void *data) +{ + struct adsp_freq_stats *fstats = + (struct adsp_freq_stats *) (s->private); + + dump_stats_table(s, fstats); + return 0; +} + +static int stats_open(struct inode *inode, struct file *file) +{ + return single_open(file, show_time_in_state, inode->i_private); +} + +static const struct file_operations time_in_state_fops = { + .open = stats_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + +static int adsp_dfs_debugfs_init(struct platform_device *pdev) +{ + int ret = -ENOMEM; + struct dentry *d, *root; + struct nvadsp_drv_data *drv = platform_get_drvdata(pdev); + + if (!drv->adsp_debugfs_root) + return ret; + + root = debugfs_create_dir("adsp_dfs", drv->adsp_debugfs_root); + if (!root) + return ret; + + policy->root = root; + + d = debugfs_create_file("enable", RW_MODE, root, NULL, + &enable_fops); + if (!d) + goto err_out; + + d = debugfs_create_file("min_freq", RW_MODE, root, NULL, + &min_fops); + if (!d) + goto err_out; + + d = debugfs_create_file("max_freq", RW_MODE, root, + NULL, &max_fops); + if (!d) + goto err_out; + + d = debugfs_create_file("cur_freq", RW_MODE, root, NULL, + &cur_fops); + if (!d) + goto err_out; + + d = debugfs_create_file("time_in_state", RO_MODE, + root, &freq_stats, + &time_in_state_fops); + if (!d) + goto err_out; + + return 0; + +err_out: + debugfs_remove_recursive(root); + policy->root = NULL; + dev_err(&pdev->dev, + "unable to create adsp logger debug fs file\n"); + return ret; +} +#endif + +/* + * Set target freq. + * @params: + * freq: adsp freq in KHz + */ +void adsp_cpu_set_rate(unsigned long freq) +{ + mutex_lock(&policy_mutex); + + if (!policy->enable) { + dev_dbg(device, "adsp dfs policy is not enabled\n"); + goto exit_out; + } + + if (freq < policy->min) + freq = policy->min; + else if (freq > policy->max) + freq = policy->max; + + freq = update_freq(freq); + if (freq) + policy->cur = freq; +exit_out: + mutex_unlock(&policy_mutex); +} + +/* + * Override adsp freq and reinit actmon counters + * + * @params: + * freq: adsp freq in KHz + * return - final freq got set. + * - 0, incase of error. + * + */ +unsigned long adsp_override_freq(unsigned long freq) +{ + int index; + unsigned long ret_freq = 0; + + mutex_lock(&policy_mutex); + + if (freq < policy->min) + freq = policy->min; + else if (freq > policy->max) + freq = policy->max; + + freq = adsp_get_target_freq(freq * 1000, &index); + if (!freq) { + dev_warn(device, "unable get the target freq\n"); + goto exit_out; + } + freq = freq / 1000; /* In KHz */ + + if (freq == policy->cur) { + ret_freq = freq; + goto exit_out; + } + + policy->ovr_freq = freq; + ret_freq = update_freq(freq); + if (ret_freq) + policy->cur = ret_freq; + + if (ret_freq != freq) { + dev_warn(device, "freq override to %lu rejected\n", freq); + policy->ovr_freq = 0; + goto exit_out; + } + +exit_out: + mutex_unlock(&policy_mutex); + return ret_freq; +} + +/* + * Set min ADSP freq. + * + * @params: + * freq: adsp freq in KHz + */ +void adsp_update_dfs_min_rate(unsigned long freq) +{ + policy_min_set(NULL, freq); +} + +/* Enable / disable dynamic freq scaling */ +void adsp_update_dfs(bool val) +{ + mutex_lock(&policy_mutex); + policy->enable = val; + mutex_unlock(&policy_mutex); +} + +/* Should be called after ADSP os is loaded */ +int adsp_dfs_core_init(struct platform_device *pdev) +{ + int size = sizeof(adsp_cpu_freq_table) / sizeof(adsp_cpu_freq_table[0]); + struct nvadsp_drv_data *drv = platform_get_drvdata(pdev); + uint16_t mid = HOST_ADSP_DFS_MBOX_ID; + int ret = 0; + u32 efreq; + + if (drv->dfs_initialized) + return 0; + + device = &pdev->dev; + policy = &dfs_policy; + + if (IS_ENABLED(CONFIG_COMMON_CLK)) + policy->adsp_clk = devm_clk_get(device, "adsp"); + else + policy->adsp_clk = clk_get_sys(NULL, policy->clk_name); + if (IS_ERR_OR_NULL(policy->adsp_clk)) { + dev_err(&pdev->dev, "unable to find adsp clock\n"); + ret = PTR_ERR(policy->adsp_clk); + goto end; + } + + if (IS_ENABLED(CONFIG_COMMON_CLK)) { + drv->bwmgr = tegra_bwmgr_register(TEGRA_BWMGR_CLIENT_APE_ADSP); + if (IS_ERR_OR_NULL(drv->bwmgr)) { + dev_err(&pdev->dev, "unable to register bwmgr\n"); + ret = PTR_ERR(drv->bwmgr); + goto end; + } + } else { + /* Change emc freq as per the adsp to emc lookup table */ + ape_emc_clk = clk_get_sys("ape", "emc"); + if (IS_ERR_OR_NULL(ape_emc_clk)) { + dev_err(device, "unable to find ape.emc clock\n"); + ret = PTR_ERR(ape_emc_clk); + goto end; + } + + ret = clk_prepare_enable(ape_emc_clk); + if (ret) { + dev_err(device, "unable to enable ape.emc clock\n"); + goto end; + } + } + + policy->max = policy->cpu_max = drv->adsp_freq; /* adsp_freq in KHz */ + + policy->min = policy->cpu_min = adsp_cpu_freq_table[0] / 1000; + + policy->cur = clk_get_rate(policy->adsp_clk) / 1000; + + efreq = adsp_to_emc_freq(policy->cur); + + if (IS_ENABLED(CONFIG_COMMON_CLK)) { + tegra_bwmgr_set_emc(drv->bwmgr, efreq * 1000, + TEGRA_BWMGR_SET_EMC_FLOOR); + } else { + ret = clk_set_rate(ape_emc_clk, efreq * 1000); + if (ret) { + dev_err(device, "failed to set ape.emc clk:%d\n", ret); + goto end; + } + } + + adsp_get_target_freq(policy->cur * 1000, &freq_stats.last_index); + freq_stats.last_time = get_jiffies_64(); + freq_stats.state_num = size; + freq_stats.dev = &pdev->dev; + memset(&freq_stats.time_in_state, 0, sizeof(freq_stats.time_in_state)); + + ret = nvadsp_mbox_open(&policy->mbox, &mid, "dfs_comm", NULL, NULL); + if (ret) { + dev_info(&pdev->dev, "unable to open mailbox. ret:%d\n", ret); + goto end; + } + +#if !defined(CONFIG_COMMON_CLK) + if (policy->rate_change_nb.notifier_call) { + /* + * "adsp_cpu" clk is a shared user of parent adsp_cpu_bus clk; + * rate change notification should come from bus clock itself. + */ + struct clk *p = clk_get_parent(policy->adsp_clk); + if (!p) { + dev_err(&pdev->dev, "Failed to find adsp cpu parent clock\n"); + ret = -EINVAL; + goto end; + } + + ret = tegra_register_clk_rate_notifier(p, + &policy->rate_change_nb); + if (ret) { + dev_err(&pdev->dev, "rate change notifier err: %s\n", + policy->clk_name); + nvadsp_mbox_close(&policy->mbox); + goto end; + } + } +#endif + +#ifdef CONFIG_DEBUG_FS + adsp_dfs_debugfs_init(pdev); +#endif + drv->dfs_initialized = true; + + dev_dbg(&pdev->dev, "adsp dfs initialized ....\n"); + return ret; +end: + if (policy->adsp_clk) { + if (IS_ENABLED(CONFIG_COMMON_CLK)) + devm_clk_put(&pdev->dev, policy->adsp_clk); + else + clk_put(policy->adsp_clk); + } + + if (IS_ENABLED(CONFIG_COMMON_CLK) && drv->bwmgr) { + tegra_bwmgr_set_emc(drv->bwmgr, 0, + TEGRA_BWMGR_SET_EMC_FLOOR); + tegra_bwmgr_unregister(drv->bwmgr); + } else if (ape_emc_clk) { + clk_disable_unprepare(ape_emc_clk); + clk_put(ape_emc_clk); + } + + return ret; +} + +int adsp_dfs_core_exit(struct platform_device *pdev) +{ + status_t ret = 0; + struct nvadsp_drv_data *drv = platform_get_drvdata(pdev); + + /* return if dfs is not initialized */ + if (!drv->dfs_initialized) + return -ENODEV; + + ret = nvadsp_mbox_close(&policy->mbox); + if (ret) + dev_info(&pdev->dev, + "adsp dfs exit failed: mbox close error. ret:%d\n", ret); + +#if !defined(CONFIG_COMMON_CLK) + tegra_unregister_clk_rate_notifier(clk_get_parent(policy->adsp_clk), + &policy->rate_change_nb); +#endif + + if (policy->adsp_clk) { + if (IS_ENABLED(CONFIG_COMMON_CLK)) + devm_clk_put(&pdev->dev, policy->adsp_clk); + else + clk_put(policy->adsp_clk); + } + + if (IS_ENABLED(CONFIG_COMMON_CLK) && drv->bwmgr) { + tegra_bwmgr_set_emc(drv->bwmgr, 0, + TEGRA_BWMGR_SET_EMC_FLOOR); + tegra_bwmgr_unregister(drv->bwmgr); + } else if (ape_emc_clk) { + clk_disable_unprepare(ape_emc_clk); + clk_put(ape_emc_clk); + } + + drv->dfs_initialized = false; + dev_dbg(&pdev->dev, "adsp dfs has exited ....\n"); + + return ret; +} diff --git a/drivers/platform/tegra/nvadsp/adsp_shared_struct.h b/drivers/platform/tegra/nvadsp/adsp_shared_struct.h new file mode 100644 index 00000000..f2c2d64e --- /dev/null +++ b/drivers/platform/tegra/nvadsp/adsp_shared_struct.h @@ -0,0 +1,142 @@ +/* + * adsp_shared_struct.h + * + * A header file containing shared data structures shared with ADSP OS + * + * Copyright (C) 2015-2016 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. + * + */ + +#ifndef __ADSP_SHARED_STRUCT +#define __ADSP_SHARED_STRUCT +#include + +#define APP_LOADER_MBOX_ID 1 + +#define ADSP_APP_FLAG_START_ON_BOOT 0x1 + +#define ADSP_OS_LOAD_TIMEOUT 5000 /* 5000 ms */ + +#define DRAM_DEBUG_LOG_SIZE 0x100000 + +#define NVADSP_NAME_SZ 128 + +struct app_mem_size { + uint64_t dram; + uint64_t dram_shared; + uint64_t dram_shared_wc; + uint64_t aram; + uint64_t aram_x; +} __packed; + +struct adsp_shared_app { + char name[NVADSP_NAME_SZ]; + struct app_mem_size mem_size; + int32_t mod_ptr; + int32_t flags; + int32_t dram_data_ptr; + int32_t shared_data_ptr; + int32_t shared_wc_data_ptr; +} __packed; + +/* ADSP app loader message queue */ +struct run_app_instance_data { + uint32_t adsp_mod_ptr; + uint64_t host_ref; + uint32_t adsp_ref; + uint32_t dram_data_ptr; + uint32_t dram_shared_ptr; + uint32_t dram_shared_wc_ptr; + uint32_t aram_ptr; + uint32_t aram_flag; + uint32_t aram_x_ptr; + uint32_t aram_x_flag; + struct app_mem_size mem_size; + nvadsp_app_args_t app_args; + uint32_t stack_size; + uint32_t message; +} __packed; + +struct app_loader_data { + int32_t header[MSGQ_MESSAGE_HEADER_WSIZE]; + struct run_app_instance_data app_init; +} __packed; + +union app_loader_message { + msgq_message_t msgq_msg; + struct app_loader_data data; +} __aligned(4); + +struct adsp_os_message_header { + int32_t header[MSGQ_MESSAGE_HEADER_WSIZE]; + uint32_t message; +} __packed; + +/* ADSP app complete message queue */ +struct app_complete_status_data { + struct adsp_os_message_header header; + uint64_t host_ref; + uint32_t adsp_ref; + int32_t status; +} __packed; + +struct adsp_static_app_data { + struct adsp_os_message_header header; + struct adsp_shared_app shared_app; +} __packed; + +union app_complete_status_message { + msgq_message_t msgq_msg; + struct app_complete_status_data complete_status_data; + struct adsp_static_app_data static_app_data; +} __aligned(4); + + +/*ADSP message pool structure */ +union app_loader_msgq { + msgq_t msgq; + struct { + int32_t header[MSGQ_HEADER_WSIZE]; + int32_t queue[MSGQ_MAX_QUEUE_WSIZE]; + }; +}; + +/* ADSP APP shared message pool */ +struct nvadsp_app_shared_msg_pool { + union app_loader_msgq app_loader_send_message; + union app_loader_msgq app_loader_recv_message; +} __packed; + +/*ADSP shated OS args */ +struct nvadsp_os_args { + int32_t timer_prescalar; + char logger[DRAM_DEBUG_LOG_SIZE]; + uint64_t adsp_freq_hz; + char reserved[128]; +} __packed; + +/* ADSP OS info/status. Keep in sync with firmware. */ +#define MAX_OS_VERSION_BUF 32 +struct nvadsp_os_info { + char version[MAX_OS_VERSION_BUF]; + char reserved[128]; +} __packed; + +/* ADSP OS shared memory */ +struct nvadsp_shared_mem { + struct nvadsp_app_shared_msg_pool app_shared_msg_pool; + struct nvadsp_os_args os_args; + struct nvadsp_os_info os_info; +} __packed; + + +#endif /* __ADSP_SHARED_STRUCT */ diff --git a/drivers/platform/tegra/nvadsp/adspff.c b/drivers/platform/tegra/nvadsp/adspff.c new file mode 100644 index 00000000..57b25ed8 --- /dev/null +++ b/drivers/platform/tegra/nvadsp/adspff.c @@ -0,0 +1,422 @@ +/* + * Copyright (c) 2016, NVIDIA CORPORATION. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope 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 +#include +#include + +#include +#include + +#include + +#include "adspff.h" + +struct file_struct { + struct file *fp; + unsigned long long wr_offset; + unsigned long long rd_offset; +}; + + +static spinlock_t adspff_lock; + +/****************************************************************************** +* Kernel file functions +******************************************************************************/ + +struct file *file_open(const char *path, int flags, int rights) +{ + struct file *filp = NULL; + mm_segment_t oldfs; + int err = 0; + + oldfs = get_fs(); + set_fs(get_ds()); + filp = filp_open(path, flags, rights); + set_fs(oldfs); + if (IS_ERR(filp)) { + err = PTR_ERR(filp); + return NULL; + } + return filp; +} + +void file_close(struct file *file) +{ + filp_close(file, NULL); +} + +int file_write(struct file *file, unsigned long long *offset, + unsigned char *data, unsigned int size) +{ + mm_segment_t oldfs; + int ret = 0; + + oldfs = get_fs(); + set_fs(get_ds()); + + ret = vfs_write(file, data, size, offset); + + set_fs(oldfs); + return ret; +} + +uint32_t file_read(struct file *file, unsigned long long *offset, + unsigned char *data, unsigned int size) +{ + mm_segment_t oldfs; + uint32_t ret = 0; + + oldfs = get_fs(); + set_fs(get_ds()); + + ret = vfs_read(file, data, size, offset); + + set_fs(oldfs); + + return ret; +} + +/****************************************************************************** +* ADSPFF file functions +******************************************************************************/ + +static struct adspff_shared_state_t *adspff; +static struct nvadsp_mbox rx_mbox; + +/** * + * w - open for writing (file need not exist) * + * a - open for appending (file need not exist) * + * r+ - open for reading and writing, start at beginning * + * w+ - open for reading and writing (overwrite file) * + * a+ - open for reading and writing (append if file exists) */ + +void set_flags(union adspff_message_t *m, int *flags) +{ + if (0 == strcmp(m->msg.payload.fopen_msg.modes, "r+")) + *flags = O_RDWR; + + else if (0 == strcmp(m->msg.payload.fopen_msg.modes, "w+")) + *flags = O_CREAT | O_RDWR | O_TRUNC; + + else if (0 == strcmp(m->msg.payload.fopen_msg.modes, "a+")) + *flags = O_APPEND | O_RDWR; + + else if (0 == strcmp(m->msg.payload.fopen_msg.modes, "r")) + *flags = O_RDONLY; + + else if (0 == strcmp(m->msg.payload.fopen_msg.modes, "w")) + *flags = O_CREAT | O_WRONLY | O_TRUNC; + + else if (0 == strcmp(m->msg.payload.fopen_msg.modes, "a")) + *flags = O_CREAT | O_APPEND | O_WRONLY; + + else + *flags = O_CREAT | O_RDWR; +} + +void adspff_fopen(struct work_struct *work) +{ + union adspff_message_t *message; + union adspff_message_t *msg_recv; + int flags = 0, ret = 0; + + struct file_struct *file = kzalloc(sizeof(struct file_struct), GFP_KERNEL); + + message = kzalloc(sizeof(union adspff_message_t), GFP_KERNEL); + msg_recv = kzalloc(sizeof(union adspff_message_t), GFP_KERNEL); + + message->msgq_msg.size = MSGQ_MSG_SIZE(struct fopen_msg_t); + + ret = msgq_dequeue_message(&adspff->msgq_send.msgq, + (msgq_message_t *)message); + + if (ret < 0) { + pr_err("fopen Dequeue failed %d.", ret); + kfree(message); + kfree(msg_recv); + return; + } + + set_flags(message, &flags); + + file->fp = file_open( + (const char *)message->msg.payload.fopen_msg.fname, + flags, S_IRWXU|S_IRWXG|S_IRWXO); + + file->wr_offset = 0; + file->rd_offset = 0; + + msg_recv->msgq_msg.size = MSGQ_MSG_SIZE(struct fopen_recv_msg_t); + msg_recv->msg.payload.fopen_recv_msg.file = (int64_t)file; + + ret = msgq_queue_message(&adspff->msgq_recv.msgq, + (msgq_message_t *)msg_recv); + if (ret < 0) { + pr_err("fopen Enqueue failed %d.", ret); + kfree(message); + kfree(msg_recv); + return; + } + + nvadsp_mbox_send(&rx_mbox, adspff_cmd_fopen_recv, + NVADSP_MBOX_SMSG, 0, 0); + kfree(message); + kfree(msg_recv); +} + +void adspff_fclose(struct work_struct *work) +{ + union adspff_message_t *message; + struct file_struct *file = NULL; + int32_t ret = 0; + + message = kzalloc(sizeof(union adspff_message_t), GFP_KERNEL); + + message->msgq_msg.size = MSGQ_MSG_SIZE(struct fclose_msg_t); + + ret = msgq_dequeue_message(&adspff->msgq_send.msgq, + (msgq_message_t *)message); + + if (ret < 0) { + pr_err("fclose Dequeue failed %d.", ret); + kfree(message); + return; + } + file = (struct file_struct *)message->msg.payload.fclose_msg.file; + if (file) { + file_close(file->fp); + kfree(file); + file = NULL; + } + kfree(message); +} + +void adspff_fwrite(struct work_struct *work) +{ + union adspff_message_t message; + union adspff_message_t *msg_recv; + struct file_struct *file = NULL; + int ret = 0; + uint32_t size = 0; + uint32_t bytes_to_write = 0; + uint32_t bytes_written = 0; + + msg_recv = kzalloc(sizeof(union adspff_message_t), GFP_KERNEL); + msg_recv->msgq_msg.size = MSGQ_MSG_SIZE(struct ack_msg_t); + + message.msgq_msg.size = MSGQ_MSG_SIZE(struct fwrite_msg_t); + ret = msgq_dequeue_message(&adspff->msgq_send.msgq, + (msgq_message_t *)&message); + if (ret < 0) { + pr_err("fwrite Dequeue failed %d.", ret); + return; + } + + file = (struct file_struct *)message.msg.payload.fwrite_msg.file; + size = message.msg.payload.fwrite_msg.size; + + bytes_to_write = ((adspff->write_buf.read_index + size) < ADSPFF_SHARED_BUFFER_SIZE) ? + size : (ADSPFF_SHARED_BUFFER_SIZE - adspff->write_buf.read_index); + ret = file_write(file->fp, &file->wr_offset, + adspff->write_buf.data + adspff->write_buf.read_index, bytes_to_write); + bytes_written += ret; + + if ((size - bytes_to_write) > 0) { + ret = file_write(file->fp, &file->wr_offset, + adspff->write_buf.data, size - bytes_to_write); + bytes_written += ret; + } + + adspff->write_buf.read_index = + (adspff->write_buf.read_index + size) % ADSPFF_SHARED_BUFFER_SIZE; + + /* send ack */ + msg_recv->msg.payload.ack_msg.size = bytes_written; + ret = msgq_queue_message(&adspff->msgq_recv.msgq, + (msgq_message_t *)msg_recv); + + if (ret < 0) { + pr_err("fread Enqueue failed %d.", ret); + kfree(msg_recv); + return; + } + nvadsp_mbox_send(&rx_mbox, adspff_cmd_ack, + NVADSP_MBOX_SMSG, 0, 0); + kfree(msg_recv); +} + +void adspff_fread(struct work_struct *work) +{ + union adspff_message_t *message; + union adspff_message_t *msg_recv; + struct file_struct *file = NULL; + uint32_t bytes_free; + uint32_t wi = adspff->read_buf.write_index; + uint32_t ri = adspff->read_buf.read_index; + uint8_t can_wrap = 0; + uint32_t size = 0, size_read = 0; + int32_t ret = 0; + + if (ri <= wi) { + bytes_free = ADSPFF_SHARED_BUFFER_SIZE - wi + ri - 1; + can_wrap = 1; + } else { + bytes_free = ri - wi - 1; + can_wrap = 0; + } + message = kzalloc(sizeof(union adspff_message_t), GFP_KERNEL); + msg_recv = kzalloc(sizeof(union adspff_message_t), GFP_KERNEL); + + msg_recv->msgq_msg.size = MSGQ_MSG_SIZE(struct ack_msg_t); + message->msgq_msg.size = MSGQ_MSG_SIZE(struct fread_msg_t); + + ret = msgq_dequeue_message(&adspff->msgq_send.msgq, + (msgq_message_t *)message); + + if (ret < 0) { + pr_err("fread Dequeue failed %d.", ret); + kfree(message); + kfree(msg_recv); + return; + } + + file = (struct file_struct *)message->msg.payload.fread_msg.file; + size = message->msg.payload.fread_msg.size; + if (bytes_free < size) { + size_read = 0; + goto send_ack; + } + + if (can_wrap) { + uint32_t bytes_to_read = (size < (ADSPFF_SHARED_BUFFER_SIZE - wi)) ? + size : (ADSPFF_SHARED_BUFFER_SIZE - wi); + ret = file_read(file->fp, &file->rd_offset, + adspff->read_buf.data + wi, bytes_to_read); + size_read = ret; + if (ret < bytes_to_read) + goto send_ack; + if ((size - bytes_to_read) > 0) { + ret = file_read(file->fp, &file->rd_offset, + adspff->read_buf.data, size - bytes_to_read); + size_read += ret; + goto send_ack; + } + } else { + ret = file_read(file->fp, &file->rd_offset, + adspff->read_buf.data + wi, size); + size_read = ret; + goto send_ack; + } +send_ack: + msg_recv->msg.payload.ack_msg.size = size_read; + ret = msgq_queue_message(&adspff->msgq_recv.msgq, + (msgq_message_t *)msg_recv); + + if (ret < 0) { + pr_err("fread Enqueue failed %d.", ret); + kfree(message); + kfree(msg_recv); + return; + } + adspff->read_buf.write_index = + (adspff->read_buf.write_index + size_read) % ADSPFF_SHARED_BUFFER_SIZE; + + nvadsp_mbox_send(&rx_mbox, adspff_cmd_ack, + NVADSP_MBOX_SMSG, 0, 0); + kfree(message); + kfree(msg_recv); +} + +static struct workqueue_struct *adspff_wq; +DECLARE_WORK(fopen_work, adspff_fopen); +DECLARE_WORK(fwrite_work, adspff_fwrite); +DECLARE_WORK(fread_work, adspff_fread); +DECLARE_WORK(fclose_work, adspff_fclose); + +/****************************************************************************** +* ADSP mailbox message handler +******************************************************************************/ + + +static int adspff_msg_handler(uint32_t msg, void *data) +{ + unsigned long flags; + + spin_lock_irqsave(&adspff_lock, flags); + switch (msg) { + case adspff_cmd_fopen: { + queue_work(adspff_wq, &fopen_work); + } + break; + case adspff_cmd_fclose: { + queue_work(adspff_wq, &fclose_work); + } + break; + case adspff_cmd_fwrite: { + queue_work(adspff_wq, &fwrite_work); + } + break; + case adspff_cmd_fread: { + queue_work(adspff_wq, &fread_work); + } + break; + default: + pr_err("Unsupported mbox msg %d.\n", msg); + } + spin_unlock_irqrestore(&adspff_lock, flags); + + return 0; +} + +int adspff_init(void) +{ + int ret = 0; + nvadsp_app_handle_t handle; + nvadsp_app_info_t *app_info; + + handle = nvadsp_app_load("adspff", "adspff.elf"); + if (!handle) + return -1; + + app_info = nvadsp_app_init(handle, NULL); + if (!app_info) { + pr_err("unable to init app adspff\n"); + return -1; + } + + adspff = ADSPFF_SHARED_STATE(app_info->mem.shared); + + ret = nvadsp_mbox_open(&rx_mbox, &adspff->mbox_id, + "adspff", adspff_msg_handler, NULL); + + if (ret < 0) { + pr_err("Failed to open mbox %d", adspff->mbox_id); + return -1; + } + + if (adspff_wq == NULL) + adspff_wq = create_singlethread_workqueue("adspff_wq"); + + spin_lock_init(&adspff_lock); + + return 0; +} + +void adspff_exit(void) +{ + nvadsp_mbox_close(&rx_mbox); + flush_workqueue(adspff_wq); + destroy_workqueue(adspff_wq); +} diff --git a/drivers/platform/tegra/nvadsp/adspff.h b/drivers/platform/tegra/nvadsp/adspff.h new file mode 100644 index 00000000..12715372 --- /dev/null +++ b/drivers/platform/tegra/nvadsp/adspff.h @@ -0,0 +1,138 @@ +/* +* tegra_adspff.h - Shared ADSPFF interface between Tegra ADSP File +* System driver and ADSP side user space code. +* Copyright (c) 2016 NVIDIA Corporation. All rights reserved. +* +* NVIDIA Corporation and its licensors retain all intellectual property +* and proprietary rights in and to this software, related documentation +* and any modifications thereto. Any use, reproduction, disclosure or +* distribution of this software and related documentation without an express +* license agreement from NVIDIA Corporation is strictly prohibited. +*/ + + +#ifndef _TEGRA_ADSPFF_H_ +#define _TEGRA_ADSPFF_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +/****************************************************************************** +* Defines +******************************************************************************/ + + +/* TODO: fine tuning */ +#define ADSPFF_MSG_QUEUE_WSIZE 1024 +#define ADSPFF_WRITE_DATA_SIZE 512 +#define ADSPFF_READ_DATA_SIZE 1024 +#define ADSPFF_SHARED_BUFFER_SIZE (128 * 1024) + +/** + * adspff_mbx_cmd: commands exchanged using mailbox. + * + * @adspff_cmd_fopen: open file on host + * @adspff_cmd_fclose: close file on host + * @adspff_cmd_fwrite: write data in an open file on host + * @adspff_cmd_fread: read data from an open file on host + */ + +enum adspff_mbx_cmd { + adspff_cmd_fopen = 0, + adspff_cmd_fclose, + adspff_cmd_fwrite, + adspff_cmd_fread, + adspff_cmd_fopen_recv, + adspff_cmd_ack, +}; + + +/****************************************************************************** +* Types +******************************************************************************/ + +/* supported message payloads */ +struct fopen_msg_t { + uint8_t fname[250]; + uint8_t modes[2]; +}; + +struct fwrite_msg_t { + int64_t file; + int32_t size; +}; + +struct fread_msg_t { + int64_t file; + int32_t size; +}; + +struct fclose_msg_t { + int64_t file; +}; + +struct fopen_recv_msg_t { + int64_t file; +}; + +struct ack_msg_t { + int32_t size; +}; + +#pragma pack(4) +/* app message definition */ +union adspff_message_t { + msgq_message_t msgq_msg; + struct { + int32_t header[MSGQ_MESSAGE_HEADER_WSIZE]; + union { + struct fopen_msg_t fopen_msg; + struct fwrite_msg_t fwrite_msg; + struct fread_msg_t fread_msg; + struct fclose_msg_t fclose_msg; + struct fopen_recv_msg_t fopen_recv_msg; + struct ack_msg_t ack_msg; + } payload; + } msg; +}; + +/* app queue definition */ +union adspff_msgq_t { + msgq_t msgq; + struct { + int32_t header[MSGQ_HEADER_WSIZE]; + int32_t queue[ADSPFF_MSG_QUEUE_WSIZE]; + } app_msgq; +}; +#pragma pack() + +#define MSGQ_MSG_SIZE(x) \ +(((sizeof(x) + sizeof(int32_t) - 1) & (~(sizeof(int32_t)-1))) >> 2) + + +/** + * ADSPFF state structure shared between ADSP & CPU + */ +typedef struct { + uint32_t write_index; + uint32_t read_index; + uint8_t data[ADSPFF_SHARED_BUFFER_SIZE]; +} adspff_shared_buffer_t; + +struct adspff_shared_state_t { + uint16_t mbox_id; + union adspff_msgq_t msgq_recv; + union adspff_msgq_t msgq_send; + adspff_shared_buffer_t write_buf; + adspff_shared_buffer_t read_buf; +}; + +#define ADSPFF_SHARED_STATE(x) \ +((struct adspff_shared_state_t *)x) + +#ifdef __cplusplus +} +#endif + +#endif /* #ifndef TEGRA_ADSPFF_H_ */ diff --git a/drivers/platform/tegra/nvadsp/amc.c b/drivers/platform/tegra/nvadsp/amc.c new file mode 100644 index 00000000..92803620 --- /dev/null +++ b/drivers/platform/tegra/nvadsp/amc.c @@ -0,0 +1,184 @@ +/* + * amc.c + * + * AMC and ARAM handling + * + * Copyright (C) 2014-2016, 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 +#include +#include + +#include "dev.h" +#include "amc.h" + +static struct platform_device *nvadsp_pdev; +static struct nvadsp_drv_data *nvadsp_drv_data; + +static inline u32 amc_readl(u32 reg) +{ + return readl(nvadsp_drv_data->base_regs[AMC] + reg); +} + +static inline void amc_writel(u32 val, u32 reg) +{ + writel(val, nvadsp_drv_data->base_regs[AMC] + reg); +} + +static void wmemcpy_to_aram(u32 to_aram, const u32 *from_mem, size_t wlen) +{ + u32 base, offset; + + base = to_aram & AMC_ARAM_APERTURE_DATA_LEN; + amc_writel(base, AMC_ARAM_APERTURE_BASE); + + offset = to_aram % AMC_ARAM_APERTURE_DATA_LEN; + + while (wlen--) { + if (offset == AMC_ARAM_APERTURE_DATA_LEN) { + base += AMC_ARAM_APERTURE_DATA_LEN; + amc_writel(base, AMC_ARAM_APERTURE_BASE); + offset = 0; + } + + amc_writel(*from_mem, AMC_ARAM_APERTURE_DATA_START + offset); + from_mem++; + offset += 4; + } +} + +static void wmemcpy_from_aram(u32 *to_mem, const u32 from_aram, size_t wlen) +{ + u32 base, offset; + + base = from_aram & AMC_ARAM_APERTURE_DATA_LEN; + amc_writel(base, AMC_ARAM_APERTURE_BASE); + + offset = from_aram % AMC_ARAM_APERTURE_DATA_LEN; + + while (wlen--) { + if (offset == AMC_ARAM_APERTURE_DATA_LEN) { + base += AMC_ARAM_APERTURE_DATA_LEN; + amc_writel(base, AMC_ARAM_APERTURE_BASE); + offset = 0; + } + + *to_mem = amc_readl(AMC_ARAM_APERTURE_DATA_START + offset); + to_mem++; + offset += 4; + } +} + +int nvadsp_aram_save(struct platform_device *pdev) +{ + struct nvadsp_drv_data *d = platform_get_drvdata(pdev); + + wmemcpy_from_aram(d->state.aram, AMC_ARAM_START, AMC_ARAM_WSIZE); + return 0; +} + +int nvadsp_aram_restore(struct platform_device *pdev) +{ + struct nvadsp_drv_data *ndd = platform_get_drvdata(pdev); + + wmemcpy_to_aram(AMC_ARAM_START, ndd->state.aram, AMC_ARAM_WSIZE); + return 0; +} + +int nvadsp_amc_save(struct platform_device *pdev) +{ + struct nvadsp_drv_data *d = platform_get_drvdata(pdev); + u32 val, offset = 0; + int i = 0; + + offset = 0x0; + val = readl(d->base_regs[AMC] + offset); + d->state.amc_regs[i++] = val; + + offset = 0x8; + val = readl(d->base_regs[AMC] + offset); + d->state.amc_regs[i++] = val; + + return 0; +} + +int nvadsp_amc_restore(struct platform_device *pdev) +{ + struct nvadsp_drv_data *d = platform_get_drvdata(pdev); + u32 val, offset = 0; + int i = 0; + + offset = 0x0; + val = d->state.amc_regs[i++]; + writel(val, d->base_regs[AMC] + offset); + + offset = 0x8; + val = d->state.amc_regs[i++]; + writel(val, d->base_regs[AMC] + offset); + + return 0; +} + +static irqreturn_t nvadsp_amc_error_int_handler(int irq, void *devid) +{ + u32 val, addr, status, intr = 0; + + status = amc_readl(AMC_INT_STATUS); + addr = amc_readl(AMC_ERROR_ADDR); + + if (status & AMC_INT_STATUS_ARAM) { + /* + * Ignore addresses lesser than AMC_ERROR_ADDR_IGNORE (4k) + * as those are spurious ones due a hardware issue. + */ + if (addr > AMC_ERROR_ADDR_IGNORE) + pr_info("nvadsp: invalid ARAM access. address: 0x%x\n", + addr); + + intr |= AMC_INT_INVALID_ARAM_ACCESS; + } + + if (status & AMC_INT_STATUS_REG) { + pr_info("nvadsp: invalid AMC reg access. address: 0x%x\n", + addr); + intr |= AMC_INT_INVALID_REG_ACCESS; + } + + val = amc_readl(AMC_INT_CLR); + val |= intr; + amc_writel(val, AMC_INT_CLR); + + return IRQ_HANDLED; +} + +status_t __init nvadsp_amc_init(struct platform_device *pdev) +{ + struct nvadsp_drv_data *drv = platform_get_drvdata(pdev); + struct device *dev = &pdev->dev; + struct device_node *node = dev->of_node; + int ret = 0; + + nvadsp_pdev = pdev; + nvadsp_drv_data = drv; + + if (!of_device_is_compatible(node, "nvidia,tegra18x-adsp-hv")) { + dev_info(&pdev->dev, "Registering AMC Error Interrupt\n"); + ret = request_irq(drv->agic_irqs[AMC_ERR_VIRQ], + nvadsp_amc_error_int_handler, 0, "AMC error int", pdev); + } + + dev_info(&pdev->dev, "AMC/ARAM initialized.\n"); + + return ret; +} diff --git a/drivers/platform/tegra/nvadsp/amc.h b/drivers/platform/tegra/nvadsp/amc.h new file mode 100644 index 00000000..2bf7d699 --- /dev/null +++ b/drivers/platform/tegra/nvadsp/amc.h @@ -0,0 +1,58 @@ +/* + * amc.h + * + * A header file for AMC/ARAM + * + * Copyright (C) 2014 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. + * + */ + +#ifndef __TEGRA_NVADSP_AMC_H +#define __TEGRA_NVADSP_AMC_H + +#define AMC_CONFIG 0x00 +#define AMC_CONFIG_ALIASING (1 << 0) +#define AMC_CONFIG_CARVEOUT (1 << 1) +#define AMC_CONFIG_ERR_RESP (1 << 2) +#define AMC_INT_STATUS (0x04) +#define AMC_INT_STATUS_ARAM (1 << 0) +#define AMC_INT_STATUS_REG (1 << 1) +#define AMC_INT_MASK 0x08 +#define AMC_INT_SET 0x0C +#define AMC_INT_CLR 0x10 +#define AMC_INT_INVALID_ARAM_ACCESS (1 << 0) +#define AMC_INT_INVALID_REG_ACCESS (1 << 1) +#define AMC_ERROR_ADDR 0x14 + +#define AMC_ERROR_ADDR_IGNORE SZ_4K + +#define AMC_REGS 0x1000 + +#define AMC_ARAM_APERTURE_BASE 0x28 +#define AMC_ARAM_APERTURE_DATA_START 0x800 +#define AMC_ARAM_APERTURE_DATA_LEN 0x800 /* 2KB */ + +#define AMC_ARAM_ALIAS0 0x00400000 +#define AMC_ARAM_ALIAS1 0x00500000 +#define AMC_ARAM_ALIAS2 0x00600000 +#define AMC_ARAM_ALIAS3 0x00700000 + +#define AMC_ARAM_START 0 +#define AMC_ARAM_SIZE SZ_64K +#define AMC_ARAM_WSIZE (AMC_ARAM_SIZE >> 2) + +int nvadsp_aram_save(struct platform_device *pdev); +int nvadsp_aram_restore(struct platform_device *pdev); +int nvadsp_amc_save(struct platform_device *pdev); +int nvadsp_amc_restore(struct platform_device *pdev); + +#endif /* __TEGRA_NVADSP_AMC_H */ diff --git a/drivers/platform/tegra/nvadsp/ape_actmon.c b/drivers/platform/tegra/nvadsp/ape_actmon.c new file mode 100644 index 00000000..3db82adb --- /dev/null +++ b/drivers/platform/tegra/nvadsp/ape_actmon.c @@ -0,0 +1,984 @@ +/* + * Copyright (C) 2014-2016, NVIDIA Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "ape_actmon.h" +#include "dev.h" + +#define ACTMON_DEV_CTRL 0x00 +#define ACTMON_DEV_CTRL_ENB (0x1 << 31) +#define ACTMON_DEV_CTRL_UP_WMARK_NUM_SHIFT 26 +#define ACTMON_DEV_CTRL_UP_WMARK_NUM_MASK (0x7 << 26) +#define ACTMON_DEV_CTRL_DOWN_WMARK_NUM_SHIFT 21 +#define ACTMON_DEV_CTRL_DOWN_WMARK_NUM_MASK (0x7 << 21) +#define ACTMON_DEV_CTRL_UP_WMARK_ENB (0x1 << 19) +#define ACTMON_DEV_CTRL_DOWN_WMARK_ENB (0x1 << 18) +#define ACTMON_DEV_CTRL_AVG_UP_WMARK_ENB (0x1 << 17) +#define ACTMON_DEV_CTRL_AVG_DOWN_WMARK_ENB (0x1 << 16) +#define ACTMON_DEV_CTRL_AT_END_ENB (0x1 << 15) +#define ACTMON_DEV_CTRL_PERIODIC_ENB (0x1 << 13) +#define ACTMON_DEV_CTRL_K_VAL_SHIFT 10 +#define ACTMON_DEV_CTRL_K_VAL_MASK (0x7 << 10) +#define ACTMON_DEV_CTRL_SAMPLE_PERIOD_VAL_SHIFT (0) +#define ACTMON_DEV_CTRL_SAMPLE_PERIOD_MASK (0xff << 0) + +#define ACTMON_DEV_UP_WMARK 0x04 +#define ACTMON_DEV_DOWN_WMARK 0x08 +#define ACTMON_DEV_AVG_UP_WMARK 0x0c +#define ACTMON_DEV_AVG_DOWN_WMARK 0x10 +#define ACTMON_DEV_INIT_AVG 0x14 + +#define ACTMON_DEV_COUNT 0x18 +#define ACTMON_DEV_AVG_COUNT 0x1c + +#define ACTMON_DEV_INTR_STATUS 0x20 +#define ACTMON_DEV_INTR_UP_WMARK (0x1 << 31) +#define ACTMON_DEV_INTR_DOWN_WMARK (0x1 << 30) +#define ACTMON_DEV_INTR_AVG_DOWN_WMARK (0x1 << 29) +#define ACTMON_DEV_INTR_AVG_UP_WMARK (0x1 << 28) + +#define ACTMON_DEV_COUNT_WEGHT 0x24 + +#define ACTMON_DEV_SAMPLE_CTRL 0x28 +#define ACTMON_DEV_SAMPLE_CTRL_TICK_65536 (0x1 << 2) +#define ACTMON_DEV_SAMPLE_CTRL_TICK_256 (0x0 << 1) + +#define AMISC_ACTMON_0 0x54 +#define AMISC_ACTMON_CNT_TARGET_ENABLE (0x1 << 31) +#define ACTMON_DEFAULT_AVG_WINDOW_LOG2 7 +/* 1/10 of % i.e 60 % of max freq */ +#define ACTMON_DEFAULT_AVG_BAND 6 +#define ACTMON_MAX_REG_OFFSET 0x2c +/* TBD: These would come via dts file */ +#define ACTMON_REG_OFFSET 0x800 +/* milli second divider as SAMPLE_TICK*/ +#define SAMPLE_MS_DIVIDER 65536 +/* Sample period in ms */ +#define ACTMON_DEFAULT_SAMPLING_PERIOD 20 +#define AVG_COUNT_THRESHOLD 100000 + +static struct actmon ape_actmon; +static struct actmon *apemon; + +/* APE activity monitor: Samples ADSP activity */ +static struct actmon_dev actmon_dev_adsp = { + .reg = 0x000, + .clk_name = "adsp_cpu", + + /* ADSP suspend activity floor */ + .suspend_freq = 51200, + + /* min step by which we want to boost in case of sudden boost request */ + .boost_freq_step = 51200, + + /* % of boost freq for boosting up */ + .boost_up_coef = 200, + + /* + * % of boost freq for boosting down. Should be boosted down by + * exponential down + */ + .boost_down_coef = 80, + + /* + * % of device freq collected in a sample period set as boost up + * threshold. boost interrupt is generated when actmon_count + * (absolute actmon count in a sample period) + * crosses this threshold consecutively by up_wmark_window. + */ + .boost_up_threshold = 95, + + /* + * % of device freq collected in a sample period set as boost down + * threshold. boost interrupt is generated when actmon_count(raw_count) + * crosses this threshold consecutively by down_wmark_window. + */ + .boost_down_threshold = 80, + + /* + * No of times raw counts hits the up_threshold to generate an + * interrupt + */ + .up_wmark_window = 4, + + /* + * No of times raw counts hits the down_threshold to generate an + * interrupt. + */ + .down_wmark_window = 8, + + /* + * No of samples = 2^ avg_window_log2 for calculating exponential moving + * average. + */ + .avg_window_log2 = ACTMON_DEFAULT_AVG_WINDOW_LOG2, + + /* + * "weight" is used to scale the count to match the device freq + * When 256 adsp active cpu clock are generated, actmon count + * is increamented by 1. Making weight as 256 ensures that 1 adsp active + * clk increaments actmon_count by 1. + * This makes actmon_count exactly reflect active adsp cpu clk + * cycles. + */ + .count_weight = 0x100, + + /* + * FREQ_SAMPLER: samples number of device(adsp) active cycles + * weighted by count_weight to reflect * actmon_count within a + * sample period. + * LOAD_SAMPLER: samples actmon active cycles weighted by + * count_weight to reflect actmon_count within a sample period. + */ + .type = ACTMON_FREQ_SAMPLER, + .state = ACTMON_UNINITIALIZED, +}; + +static struct actmon_dev *actmon_devices[] = { + &actmon_dev_adsp, +}; + +static inline u32 actmon_readl(u32 offset) +{ + return __raw_readl(apemon->base + offset); +} +static inline void actmon_writel(u32 val, u32 offset) +{ + __raw_writel(val, apemon->base + offset); +} +static inline void actmon_wmb(void) +{ + wmb(); +} + +#define offs(x) (dev->reg + x) + +static inline unsigned long do_percent(unsigned long val, unsigned int pct) +{ + return val * pct / 100; +} + +static void actmon_update_sample_period(unsigned long period) +{ + u32 sample_period_in_clks; + u32 val = 0; + + apemon->sampling_period = period; + /* + * sample_period_in_clks <1..255> = (actmon_clk_freq<1..40800> * + * actmon_sample_period <10ms..40ms>) / SAMPLE_MS_DIVIDER(65536) + */ + sample_period_in_clks = (apemon->freq * apemon->sampling_period) / + SAMPLE_MS_DIVIDER; + + val = actmon_readl(ACTMON_DEV_CTRL); + val &= ~ACTMON_DEV_CTRL_SAMPLE_PERIOD_MASK; + val |= (sample_period_in_clks << + ACTMON_DEV_CTRL_SAMPLE_PERIOD_VAL_SHIFT) + & ACTMON_DEV_CTRL_SAMPLE_PERIOD_MASK; + actmon_writel(val, ACTMON_DEV_CTRL); +} + +static inline void actmon_dev_up_wmark_set(struct actmon_dev *dev) +{ + u32 val; + unsigned long freq = (dev->type == ACTMON_FREQ_SAMPLER) ? + dev->cur_freq : apemon->freq; + + val = freq * apemon->sampling_period; + actmon_writel(do_percent(val, dev->boost_up_threshold), + offs(ACTMON_DEV_UP_WMARK)); +} + +static inline void actmon_dev_down_wmark_set(struct actmon_dev *dev) +{ + u32 val; + unsigned long freq = (dev->type == ACTMON_FREQ_SAMPLER) ? + dev->cur_freq : apemon->freq; + + val = freq * apemon->sampling_period; + actmon_writel(do_percent(val, dev->boost_down_threshold), + offs(ACTMON_DEV_DOWN_WMARK)); +} + +static inline void actmon_dev_wmark_set(struct actmon_dev *dev) +{ + u32 val; + unsigned long freq = (dev->type == ACTMON_FREQ_SAMPLER) ? + dev->cur_freq : apemon->freq; + + val = freq * apemon->sampling_period; + + actmon_writel(do_percent(val, dev->boost_up_threshold), + offs(ACTMON_DEV_UP_WMARK)); + actmon_writel(do_percent(val, dev->boost_down_threshold), + offs(ACTMON_DEV_DOWN_WMARK)); +} + +static inline void actmon_dev_avg_wmark_set(struct actmon_dev *dev) +{ + /* + * band: delta from current count to be set for avg upper + * and lower thresholds + */ + u32 band = dev->avg_band_freq * apemon->sampling_period; + u32 avg = dev->avg_count; + + actmon_writel(avg + band, offs(ACTMON_DEV_AVG_UP_WMARK)); + avg = max(avg, band); + actmon_writel(avg - band, offs(ACTMON_DEV_AVG_DOWN_WMARK)); +} + +static unsigned long actmon_dev_avg_freq_get(struct actmon_dev *dev) +{ + u64 val; + + if (dev->type == ACTMON_FREQ_SAMPLER) + return dev->avg_count / apemon->sampling_period; + + val = (u64) dev->avg_count * dev->cur_freq; + do_div(val , apemon->freq * apemon->sampling_period); + return (u32)val; +} + +/* Activity monitor sampling operations */ +static irqreturn_t ape_actmon_dev_isr(int irq, void *dev_id) +{ + u32 val, devval; + unsigned long flags; + struct actmon_dev *dev = (struct actmon_dev *)dev_id; + + spin_lock_irqsave(&dev->lock, flags); + val = actmon_readl(offs(ACTMON_DEV_INTR_STATUS)); + actmon_writel(val, offs(ACTMON_DEV_INTR_STATUS)); /* clr all */ + devval = actmon_readl(offs(ACTMON_DEV_CTRL)); + + if (val & ACTMON_DEV_INTR_AVG_UP_WMARK) { + devval |= (ACTMON_DEV_CTRL_AVG_UP_WMARK_ENB | + ACTMON_DEV_CTRL_AVG_DOWN_WMARK_ENB); + dev->avg_count = actmon_readl(offs(ACTMON_DEV_AVG_COUNT)); + actmon_dev_avg_wmark_set(dev); + } else if (val & ACTMON_DEV_INTR_AVG_DOWN_WMARK) { + devval |= (ACTMON_DEV_CTRL_AVG_UP_WMARK_ENB | + ACTMON_DEV_CTRL_AVG_DOWN_WMARK_ENB); + dev->avg_count = actmon_readl(offs(ACTMON_DEV_AVG_COUNT)); + actmon_dev_avg_wmark_set(dev); + } + + if (val & ACTMON_DEV_INTR_UP_WMARK) { + devval |= (ACTMON_DEV_CTRL_UP_WMARK_ENB | + ACTMON_DEV_CTRL_DOWN_WMARK_ENB); + + dev->boost_freq = dev->boost_freq_step + + do_percent(dev->boost_freq, dev->boost_up_coef); + if (dev->boost_freq >= dev->max_freq) { + dev->boost_freq = dev->max_freq; + devval &= ~ACTMON_DEV_CTRL_UP_WMARK_ENB; + } + } else if (val & ACTMON_DEV_INTR_DOWN_WMARK) { + devval |= (ACTMON_DEV_CTRL_UP_WMARK_ENB | + ACTMON_DEV_CTRL_DOWN_WMARK_ENB); + + dev->boost_freq = + do_percent(dev->boost_freq, dev->boost_down_coef); + if (dev->boost_freq == 0) { + devval &= ~ACTMON_DEV_CTRL_DOWN_WMARK_ENB; + } + } + + actmon_writel(devval, offs(ACTMON_DEV_CTRL)); + actmon_wmb(); + + spin_unlock_irqrestore(&dev->lock, flags); + return IRQ_WAKE_THREAD; +} + +static irqreturn_t ape_actmon_dev_fn(int irq, void *dev_id) +{ + unsigned long flags, freq; + struct actmon_dev *dev = (struct actmon_dev *)dev_id; + + spin_lock_irqsave(&dev->lock, flags); + + if (dev->state != ACTMON_ON) { + spin_unlock_irqrestore(&dev->lock, flags); + return IRQ_HANDLED; + } + + freq = actmon_dev_avg_freq_get(dev); + dev->avg_actv_freq = freq; /* in kHz */ + freq = do_percent(freq, dev->avg_sustain_coef); + freq += dev->boost_freq; + + dev->target_freq = freq; + spin_unlock_irqrestore(&dev->lock, flags); + + dev_dbg(dev->device, "%s(kHz): avg: %lu, boost: %lu, target: %lu, current: %lu\n", + dev->clk_name, dev->avg_actv_freq, dev->boost_freq, dev->target_freq, + dev->cur_freq); + +#if defined(CONFIG_TEGRA_ADSP_DFS) + adsp_cpu_set_rate(freq); +#endif + + return IRQ_HANDLED; +} + +/* Activity monitor configuration and control */ +static void actmon_dev_configure(struct actmon_dev *dev, + unsigned long freq) +{ + u32 val; + + dev->boost_freq = 0; + dev->cur_freq = freq; + dev->target_freq = freq; + dev->avg_actv_freq = freq; + + if (dev->type == ACTMON_FREQ_SAMPLER) { + /* + * max actmon count = (count_weight * adsp_freq (khz) + * sample_period (ms)) / (PULSE_N_CLK+1) + * As Count_weight is set as 256(0x100) and + * (PULSE_N_CLK+1) = 256. both would be + * compensated while coming up max_actmon_count. + * in other word + * max actmon count = ((count_weight * adsp_freq * + * sample_period_reg * SAMPLE_TICK) + * / (ape_freq * (PULSE_N_CLK+1))) + * where - + * sample_period_reg : <1..255> sample period in no of + * actmon clocks per sample + * SAMPLE_TICK : Arbtrary value for ms - 65536, us - 256 + * (PULSE_N_CLK + 1) : 256 - No of adsp "active" clocks to + * increament raw_count/ actmon_count + * by one. + */ + dev->avg_count = dev->cur_freq * apemon->sampling_period; + dev->avg_band_freq = dev->max_freq * + ACTMON_DEFAULT_AVG_BAND / 1000; + } else { + dev->avg_count = apemon->freq * apemon->sampling_period; + dev->avg_band_freq = apemon->freq * + ACTMON_DEFAULT_AVG_BAND / 1000; + } + actmon_writel(dev->avg_count, offs(ACTMON_DEV_INIT_AVG)); + + BUG_ON(!dev->boost_up_threshold); + dev->avg_sustain_coef = 100 * 100 / dev->boost_up_threshold; + actmon_dev_avg_wmark_set(dev); + actmon_dev_wmark_set(dev); + + actmon_writel(dev->count_weight, offs(ACTMON_DEV_COUNT_WEGHT)); + val = actmon_readl(ACTMON_DEV_CTRL); + + val |= (ACTMON_DEV_CTRL_PERIODIC_ENB | + ACTMON_DEV_CTRL_AVG_UP_WMARK_ENB | + ACTMON_DEV_CTRL_AVG_DOWN_WMARK_ENB); + val |= ((dev->avg_window_log2 - 1) << ACTMON_DEV_CTRL_K_VAL_SHIFT) & + ACTMON_DEV_CTRL_K_VAL_MASK; + val |= ((dev->down_wmark_window - 1) << + ACTMON_DEV_CTRL_DOWN_WMARK_NUM_SHIFT) & + ACTMON_DEV_CTRL_DOWN_WMARK_NUM_MASK; + val |= ((dev->up_wmark_window - 1) << + ACTMON_DEV_CTRL_UP_WMARK_NUM_SHIFT) & + ACTMON_DEV_CTRL_UP_WMARK_NUM_MASK; + val |= ACTMON_DEV_CTRL_DOWN_WMARK_ENB | + ACTMON_DEV_CTRL_UP_WMARK_ENB; + + actmon_writel(val, offs(ACTMON_DEV_CTRL)); + actmon_wmb(); +} + +static void actmon_dev_enable(struct actmon_dev *dev) +{ + u32 val; + unsigned long flags; + + spin_lock_irqsave(&dev->lock, flags); + + if (dev->state == ACTMON_OFF) { + dev->state = ACTMON_ON; + + val = actmon_readl(offs(ACTMON_DEV_CTRL)); + val |= ACTMON_DEV_CTRL_ENB; + actmon_writel(val, offs(ACTMON_DEV_CTRL)); + actmon_wmb(); + } + spin_unlock_irqrestore(&dev->lock, flags); +} + +static void actmon_dev_disable(struct actmon_dev *dev) +{ + u32 val; + unsigned long flags; + + spin_lock_irqsave(&dev->lock, flags); + + if (dev->state == ACTMON_ON) { + dev->state = ACTMON_OFF; + + val = actmon_readl(offs(ACTMON_DEV_CTRL)); + val &= ~ACTMON_DEV_CTRL_ENB; + actmon_writel(val, offs(ACTMON_DEV_CTRL)); + actmon_writel(0xffffffff, offs(ACTMON_DEV_INTR_STATUS)); + actmon_wmb(); + } + spin_unlock_irqrestore(&dev->lock, flags); +} +static int actmon_dev_probe(struct actmon_dev *dev) +{ + struct nvadsp_drv_data *drv_data = dev_get_drvdata(dev->device); + int ret; + + dev->irq = drv_data->agic_irqs[ACTMON_VIRQ]; + ret = request_threaded_irq(dev->irq, ape_actmon_dev_isr, + ape_actmon_dev_fn, IRQ_TYPE_LEVEL_HIGH, + dev->clk_name, dev); + if (ret) { + dev_err(dev->device, "Failed irq %d request for %s\n", dev->irq, + dev->clk_name); + goto end; + } + disable_irq(dev->irq); +end: + return ret; +} + +static int actmon_dev_init(struct actmon_dev *dev) +{ + int ret = -EINVAL; + unsigned long freq; + + spin_lock_init(&dev->lock); + + dev->clk = clk_get_sys(NULL, dev->clk_name); + if (IS_ERR_OR_NULL(dev->clk)) { + dev_err(dev->device, "Failed to find %s clock\n", + dev->clk_name); + goto end; + } + + ret = clk_prepare_enable(dev->clk); + if (ret) { + dev_err(dev->device, "unable to enable %s clock\n", + dev->clk_name); + goto err_enable; + } + + dev->max_freq = freq = clk_get_rate(dev->clk) / 1000; + actmon_dev_configure(dev, freq); + + dev->state = ACTMON_OFF; + actmon_dev_enable(dev); + enable_irq(dev->irq); + return 0; + +err_enable: + clk_put(dev->clk); +end: + return ret; +} + +#ifdef CONFIG_DEBUG_FS + +#define RW_MODE (S_IWUSR | S_IRUSR) +#define RO_MODE S_IRUSR + +static struct dentry *clk_debugfs_root; + +static int type_show(struct seq_file *s, void *data) +{ + struct actmon_dev *dev = s->private; + + seq_printf(s, "%s\n", (dev->type == ACTMON_LOAD_SAMPLER) ? + "Load Activity Monitor" : "Frequency Activity Monitor"); + return 0; +} +static int type_open(struct inode *inode, struct file *file) +{ + return single_open(file, type_show, inode->i_private); +} +static const struct file_operations type_fops = { + .open = type_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + +static int actv_get(void *data, u64 *val) +{ + unsigned long flags; + struct actmon_dev *dev = data; + + spin_lock_irqsave(&dev->lock, flags); + *val = actmon_dev_avg_freq_get(dev); + spin_unlock_irqrestore(&dev->lock, flags); + return 0; +} +DEFINE_SIMPLE_ATTRIBUTE(actv_fops, actv_get, NULL, "%llu\n"); + +static int step_get(void *data, u64 *val) +{ + struct actmon_dev *dev = data; + *val = dev->boost_freq_step * 100 / dev->max_freq; + return 0; +} +static int step_set(void *data, u64 val) +{ + unsigned long flags; + struct actmon_dev *dev = data; + + if (val > 100) + val = 100; + + spin_lock_irqsave(&dev->lock, flags); + dev->boost_freq_step = do_percent(dev->max_freq, (unsigned int)val); + spin_unlock_irqrestore(&dev->lock, flags); + return 0; +} +DEFINE_SIMPLE_ATTRIBUTE(step_fops, step_get, step_set, "%llu\n"); + +static int count_weight_get(void *data, u64 *val) +{ + struct actmon_dev *dev = data; + *val = dev->count_weight; + return 0; +} +static int count_weight_set(void *data, u64 val) +{ + unsigned long flags; + struct actmon_dev *dev = data; + + spin_lock_irqsave(&dev->lock, flags); + dev->count_weight = (u32) val; + actmon_writel(dev->count_weight, offs(ACTMON_DEV_COUNT_WEGHT)); + spin_unlock_irqrestore(&dev->lock, flags); + return 0; +} +DEFINE_SIMPLE_ATTRIBUTE(cnt_wt_fops, count_weight_get, + count_weight_set, "%llu\n"); + +static int up_threshold_get(void *data, u64 *val) +{ + struct actmon_dev *dev = data; + *val = dev->boost_up_threshold; + return 0; +} +static int up_threshold_set(void *data, u64 val) +{ + unsigned long flags; + struct actmon_dev *dev = data; + unsigned int up_threshold = (unsigned int)val; + + if (up_threshold > 100) + up_threshold = 100; + + spin_lock_irqsave(&dev->lock, flags); + + if (up_threshold <= dev->boost_down_threshold) + up_threshold = dev->boost_down_threshold; + if (up_threshold) + dev->avg_sustain_coef = 100 * 100 / up_threshold; + dev->boost_up_threshold = up_threshold; + + actmon_dev_up_wmark_set(dev); + actmon_wmb(); + + spin_unlock_irqrestore(&dev->lock, flags); + return 0; +} +DEFINE_SIMPLE_ATTRIBUTE(up_threshold_fops, up_threshold_get, + up_threshold_set, "%llu\n"); + +static int down_threshold_get(void *data, u64 *val) +{ + struct actmon_dev *dev = data; + *val = dev->boost_down_threshold; + return 0; +} +static int down_threshold_set(void *data, u64 val) +{ + unsigned long flags; + struct actmon_dev *dev = data; + unsigned int down_threshold = (unsigned int)val; + + spin_lock_irqsave(&dev->lock, flags); + + if (down_threshold >= dev->boost_up_threshold) + down_threshold = dev->boost_up_threshold; + dev->boost_down_threshold = down_threshold; + + actmon_dev_down_wmark_set(dev); + actmon_wmb(); + + spin_unlock_irqrestore(&dev->lock, flags); + return 0; +} +DEFINE_SIMPLE_ATTRIBUTE(down_threshold_fops, down_threshold_get, + down_threshold_set, "%llu\n"); + +static int state_get(void *data, u64 *val) +{ + struct actmon_dev *dev = data; + *val = dev->state; + return 0; +} +static int state_set(void *data, u64 val) +{ + struct actmon_dev *dev = data; + + if (val) + actmon_dev_enable(dev); + else + actmon_dev_disable(dev); + + return 0; +} +DEFINE_SIMPLE_ATTRIBUTE(state_fops, state_get, state_set, "%llu\n"); + +/* Get period in msec */ +static int period_get(void *data, u64 *val) +{ + *val = apemon->sampling_period; + return 0; +} +/* Set period in msec */ +static int period_set(void *data, u64 val) +{ + int i; + unsigned long flags; + u8 period = (u8)val; + + if (period) { + actmon_update_sample_period(period); + + for (i = 0; i < ARRAY_SIZE(actmon_devices); i++) { + struct actmon_dev *dev = actmon_devices[i]; + spin_lock_irqsave(&dev->lock, flags); + actmon_dev_wmark_set(dev); + spin_unlock_irqrestore(&dev->lock, flags); + } + actmon_wmb(); + return 0; + } + return -EINVAL; +} +DEFINE_SIMPLE_ATTRIBUTE(period_fops, period_get, period_set, "%llu\n"); + + +static int actmon_debugfs_create_dev(struct actmon_dev *dev) +{ + struct dentry *dir, *d; + + if (dev->state == ACTMON_UNINITIALIZED) + return 0; + + dir = debugfs_create_dir(dev->clk_name, clk_debugfs_root); + if (!dir) + return -ENOMEM; + + d = debugfs_create_file( + "actv_type", RO_MODE, dir, dev, &type_fops); + if (!d) + return -ENOMEM; + + d = debugfs_create_file( + "avg_activity", RO_MODE, dir, dev, &actv_fops); + if (!d) + return -ENOMEM; + + d = debugfs_create_file( + "boost_step", RW_MODE, dir, dev, &step_fops); + if (!d) + return -ENOMEM; + + d = debugfs_create_u32( + "boost_rate_dec", RW_MODE, dir, (u32 *)&dev->boost_down_coef); + if (!d) + return -ENOMEM; + + d = debugfs_create_u32( + "boost_rate_inc", RW_MODE, dir, (u32 *)&dev->boost_up_coef); + if (!d) + return -ENOMEM; + + d = debugfs_create_file( + "boost_threshold_dn", RW_MODE, dir, dev, &down_threshold_fops); + if (!d) + return -ENOMEM; + + d = debugfs_create_file( + "boost_threshold_up", RW_MODE, dir, dev, &up_threshold_fops); + if (!d) + return -ENOMEM; + + d = debugfs_create_file( + "state", RW_MODE, dir, dev, &state_fops); + if (!d) + return -ENOMEM; + + d = debugfs_create_file( + "cnt_wt", RW_MODE, dir, dev, &cnt_wt_fops); + if (!d) + return -ENOMEM; + + return 0; +} + +static int actmon_debugfs_init(struct nvadsp_drv_data *drv) +{ + int i; + int ret = -ENOMEM; + struct dentry *d; + + if (!drv->adsp_debugfs_root) + return ret; + d = debugfs_create_dir("adsp_actmon", drv->adsp_debugfs_root); + if (!d) + return ret; + clk_debugfs_root = d; + + d = debugfs_create_file("period", RW_MODE, d, NULL, &period_fops); + if (!d) + goto err_out; + + for (i = 0; i < ARRAY_SIZE(actmon_devices); i++) { + ret = actmon_debugfs_create_dev(actmon_devices[i]); + if (ret) + goto err_out; + } + return 0; + +err_out: + debugfs_remove_recursive(clk_debugfs_root); + return ret; +} + +#endif + +/* freq in KHz */ +void actmon_rate_change(unsigned long freq, bool override) +{ + struct actmon_dev *dev = &actmon_dev_adsp; + unsigned long flags; + + if (override) { + actmon_dev_disable(dev); + spin_lock_irqsave(&dev->lock, flags); + dev->cur_freq = freq; + dev->avg_count = freq * apemon->sampling_period; + actmon_writel(dev->avg_count, offs(ACTMON_DEV_INIT_AVG)); + actmon_dev_avg_wmark_set(dev); + actmon_dev_wmark_set(dev); + actmon_wmb(); + spin_unlock_irqrestore(&dev->lock, flags); + actmon_dev_enable(dev); + } else { + spin_lock_irqsave(&dev->lock, flags); + dev->cur_freq = freq; + if (dev->state == ACTMON_ON) { + actmon_dev_wmark_set(dev); + actmon_wmb(); + } + spin_unlock_irqrestore(&dev->lock, flags); + } + /* change ape rate as half of adsp rate */ + clk_set_rate(apemon->clk, freq * 500); +}; + +int ape_actmon_probe(struct platform_device *pdev) +{ + int ret = 0; + int i; + + for (i = 0; i < ARRAY_SIZE(actmon_devices); i++) { + actmon_devices[i]->device = &pdev->dev; + ret = actmon_dev_probe(actmon_devices[i]); + dev_dbg(&pdev->dev, "%s actmon: %s probe (%d)\n", + actmon_devices[i]->clk_name, ret ? "Failed" : "Completed", ret); + } + return ret; +} + +static int ape_actmon_rc_cb( + struct notifier_block *nb, unsigned long rate, void *v) +{ + struct actmon_dev *dev = &actmon_dev_adsp; + unsigned long flags; + u32 init_cnt; + + if (dev->state != ACTMON_ON) { + dev_dbg(dev->device, "adsp actmon is not ON\n"); + goto exit_out; + } + + actmon_dev_disable(dev); + + spin_lock_irqsave(&dev->lock, flags); + init_cnt = actmon_readl(offs(ACTMON_DEV_AVG_COUNT)); + /* update sample period to maintain number of clock */ + apemon->freq = rate / 1000; /* in KHz */ + actmon_update_sample_period(ACTMON_DEFAULT_SAMPLING_PERIOD); + actmon_writel(init_cnt, offs(ACTMON_DEV_INIT_AVG)); + spin_unlock_irqrestore(&dev->lock, flags); + + actmon_dev_enable(dev); +exit_out: + return NOTIFY_OK; +} +int ape_actmon_init(struct platform_device *pdev) +{ + struct nvadsp_drv_data *drv = platform_get_drvdata(pdev); + static void __iomem *amisc_base; + u32 sample_period_in_clks; + struct clk *p; + u32 val = 0; + int i, ret; + + if (drv->actmon_initialized) + return 0; + + apemon = &ape_actmon; + apemon->base = drv->base_regs[AMISC] + ACTMON_REG_OFFSET; + amisc_base = drv->base_regs[AMISC]; + + apemon->clk = clk_get_sys(NULL, "adsp.ape"); + if (!apemon->clk) { + dev_err(&pdev->dev, "Failed to find actmon clock\n"); + ret = -EINVAL; + goto err_out; + } + + ret = clk_prepare_enable(apemon->clk); + if (ret) { + dev_err(&pdev->dev, "Failed to enable actmon clock\n"); + ret = -EINVAL; + goto err_out; + } + apemon->clk_rc_nb.notifier_call = ape_actmon_rc_cb; + + /* + * "adsp.ape" clk is shared bus user clock and "ape" is bus clock + * but rate change notification should come from bus clock itself. + */ + p = clk_get_parent(apemon->clk); + if (!p) { + dev_err(&pdev->dev, "Failed to find actmon parent clock\n"); + ret = -EINVAL; + goto clk_err_out; + } + + ret = tegra_register_clk_rate_notifier(p, &apemon->clk_rc_nb); + if (ret) { + dev_err(&pdev->dev, "Registration fail: %s rate change notifier for %s\n", + p->name, apemon->clk->name); + goto clk_err_out; + } + apemon->freq = clk_get_rate(apemon->clk) / 1000; /* in KHz */ + + apemon->sampling_period = ACTMON_DEFAULT_SAMPLING_PERIOD; + + /* + * sample period as no of actmon clocks + * Actmon is derived from APE clk. + * suppose APE clk is 204MHz = 204000 KHz and want to calculate + * clocks in 10ms sample + * in 1ms = 204000 cycles + * 10ms = 204000 * 10 APE cycles + * SAMPLE_MS_DIVIDER is an arbitrary number + */ + sample_period_in_clks = (apemon->freq * apemon->sampling_period) + / SAMPLE_MS_DIVIDER; + + /* set ms mode */ + actmon_writel(ACTMON_DEV_SAMPLE_CTRL_TICK_65536, + ACTMON_DEV_SAMPLE_CTRL); + val = actmon_readl(ACTMON_DEV_CTRL); + val &= ~ACTMON_DEV_CTRL_SAMPLE_PERIOD_MASK; + val |= (sample_period_in_clks << + ACTMON_DEV_CTRL_SAMPLE_PERIOD_VAL_SHIFT) + & ACTMON_DEV_CTRL_SAMPLE_PERIOD_MASK; + actmon_writel(val, ACTMON_DEV_CTRL); + + /* Enable AMISC_ACTMON */ + val = __raw_readl(amisc_base + AMISC_ACTMON_0); + val |= AMISC_ACTMON_CNT_TARGET_ENABLE; + __raw_writel(val, amisc_base + AMISC_ACTMON_0); + + actmon_writel(0xffffffff, ACTMON_DEV_INTR_STATUS); /* clr all */ + + for (i = 0; i < ARRAY_SIZE(actmon_devices); i++) { + ret = actmon_dev_init(actmon_devices[i]); + dev_dbg(&pdev->dev, "%s actmon device: %s initialization (%d)\n", + actmon_devices[i]->clk_name, ret ? "Failed" : "Completed", ret); + } + +#ifdef CONFIG_DEBUG_FS + actmon_debugfs_init(drv); +#endif + + drv->actmon_initialized = true; + + dev_dbg(&pdev->dev, "adsp actmon initialized ....\n"); + return 0; +clk_err_out: + if (apemon->clk) + clk_disable_unprepare(apemon->clk); +err_out: + if (apemon->clk) + clk_put(apemon->clk); + return ret; +} + +int ape_actmon_exit(struct platform_device *pdev) +{ + struct nvadsp_drv_data *drv = platform_get_drvdata(pdev); + struct actmon_dev *dev; + status_t ret = 0; + int i; + + /* return if actmon is not initialized */ + if (!drv->actmon_initialized) + return -ENODEV; + + for (i = 0; i < ARRAY_SIZE(actmon_devices); i++) { + dev = actmon_devices[i]; + actmon_dev_disable(dev); + disable_irq(dev->irq); + clk_disable_unprepare(dev->clk); + clk_put(dev->clk); + } + + tegra_unregister_clk_rate_notifier(clk_get_parent(apemon->clk), + &apemon->clk_rc_nb); + + clk_disable_unprepare(apemon->clk); + clk_put(apemon->clk); + + drv->actmon_initialized = false; + + dev_dbg(&pdev->dev, "adsp actmon has exited ....\n"); + + return ret; +} diff --git a/drivers/platform/tegra/nvadsp/ape_actmon.h b/drivers/platform/tegra/nvadsp/ape_actmon.h new file mode 100644 index 00000000..ae7d1723 --- /dev/null +++ b/drivers/platform/tegra/nvadsp/ape_actmon.h @@ -0,0 +1,86 @@ +/* + * Copyright (c) 2014-2016, NVIDIA CORPORATION. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope 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. + */ + +#ifndef __APE_ACTMON_H +#define __APE_ACTMON_H +#include + +enum actmon_type { + ACTMON_LOAD_SAMPLER, + ACTMON_FREQ_SAMPLER, +}; + +enum actmon_state { + ACTMON_UNINITIALIZED = -1, + ACTMON_OFF = 0, + ACTMON_ON = 1, + ACTMON_SUSPENDED = 2, +}; +/* Units: + * - frequency in kHz + * - coefficients, and thresholds in % + * - sampling period in ms + * - window in sample periods (value = setting + 1) + */ +struct actmon_dev { + u32 reg; + int irq; + struct device *device; + + const char *dev_id; + const char *con_id; + const char *clk_name; + struct clk *clk; + + unsigned long max_freq; + unsigned long target_freq; + unsigned long cur_freq; + unsigned long suspend_freq; + + unsigned long avg_actv_freq; + unsigned long avg_band_freq; + unsigned int avg_sustain_coef; + u32 avg_count; + + unsigned long boost_freq; + unsigned long boost_freq_step; + unsigned int boost_up_coef; + unsigned int boost_down_coef; + unsigned int boost_up_threshold; + unsigned int boost_down_threshold; + + u8 up_wmark_window; + u8 down_wmark_window; + u8 avg_window_log2; + u32 count_weight; + + enum actmon_type type; + enum actmon_state state; + enum actmon_state saved_state; + + spinlock_t lock; + +}; + +struct actmon { + struct clk *clk; + unsigned long freq; + unsigned long sampling_period; + struct notifier_block clk_rc_nb; + void __iomem *base; +}; + +int ape_actmon_init(struct platform_device *pdev); +int ape_actmon_exit(struct platform_device *pdev); +void actmon_rate_change(unsigned long freq, bool override); +#endif diff --git a/drivers/platform/tegra/nvadsp/app.c b/drivers/platform/tegra/nvadsp/app.c new file mode 100644 index 00000000..6dc927f8 --- /dev/null +++ b/drivers/platform/tegra/nvadsp/app.c @@ -0,0 +1,988 @@ +/* + * run_app.c + * + * ADSP OS App management + * + * Copyright (C) 2014-2016, 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 +#include +#include +#include +#include +#include +#include +#include +#include + +#include "aram_manager.h" +#include "os.h" +#include "dev.h" +#include "adsp_shared_struct.h" + +#define DYN_APP_EXTN ".elf" + +/* + * structure to hold the list of app binaries loaded and + * its associated instances. +*/ +struct nvadsp_app_service { + char name[NVADSP_NAME_SZ]; + struct list_head node; + int instance; + struct mutex lock; + struct list_head app_head; + const uint32_t token; + const struct app_mem_size *mem_size; + int generated_instance_id; + struct adsp_module *mod; +#ifdef CONFIG_DEBUG_FS + struct dentry *debugfs; +#endif +}; + +/* nvadsp app loader private structure */ +struct nvadsp_app_priv_struct { + struct platform_device *pdev; + struct completion os_load_complete; + struct nvadsp_mbox mbox; + struct list_head service_list; + struct mutex service_lock_list; +#ifdef CONFIG_DEBUG_FS + struct dentry *adsp_app_debugfs_root; +#endif +}; + +static struct nvadsp_app_priv_struct priv; + +static void delete_app_instance(nvadsp_app_info_t *); + +#ifdef CONFIG_DEBUG_FS +static int dump_binary_in_2bytes_app_file_node(struct seq_file *s, void *data) +{ + struct nvadsp_app_service *ser = s->private; + struct adsp_module *mod = ser->mod; + u32 adsp_ptr; + u16 *ptr; + int i; + + adsp_ptr = mod->adsp_module_ptr; + ptr = (u16 *)mod->module_ptr; + for (i = 0; i < mod->size; i += 2) + seq_printf(s, "0x%x : 0x%04x\n", adsp_ptr + i, *(ptr + i)); + + return 0; +} + + +static int dump_binary_in_words_app_file_node(struct seq_file *s, void *data) +{ + struct nvadsp_app_service *ser = s->private; + struct adsp_module *mod = ser->mod; + u32 adsp_ptr; + u32 *ptr; + int i; + + adsp_ptr = mod->adsp_module_ptr; + ptr = (u32 *)mod->module_ptr; + for (i = 0; i < mod->size; i += 4) + seq_printf(s, "0x%x : 0x%08x\n", adsp_ptr + i, *(ptr + i)); + + return 0; +} + +static int host_load_addr_app_file_node(struct seq_file *s, void *data) +{ + struct nvadsp_app_service *ser = s->private; + struct adsp_module *mod = ser->mod; + + seq_printf(s, "%p\n", mod->module_ptr); + + return 0; +} + +static int adsp_load_addr_app_file_node(struct seq_file *s, void *data) +{ + struct nvadsp_app_service *ser = s->private; + struct adsp_module *mod = ser->mod; + + seq_printf(s, "0x%x\n", mod->adsp_module_ptr); + + return 0; +} + +static int size_app_file_node(struct seq_file *s, void *data) +{ + struct nvadsp_app_service *ser = s->private; + struct adsp_module *mod = ser->mod; + + seq_printf(s, "%lu\n", mod->size); + + return 0; +} + +static int dram_app_file_node(struct seq_file *s, void *data) +{ + const struct app_mem_size *mem_size = s->private; + + seq_printf(s, "%llu\n", mem_size->dram); + + return 0; +} + +static int dram_shared_app_file_node(struct seq_file *s, void *data) +{ + const struct app_mem_size *mem_size = s->private; + + seq_printf(s, "%llu\n", mem_size->dram_shared); + + return 0; +} + +static int dram_shared_wc_app_file_node(struct seq_file *s, void *data) +{ + const struct app_mem_size *mem_size = s->private; + + seq_printf(s, "%llu\n", mem_size->dram_shared_wc); + + return 0; +} + +static int aram_app_file_node(struct seq_file *s, void *data) +{ + const struct app_mem_size *mem_size = s->private; + + seq_printf(s, "%llu\n", mem_size->aram); + + return 0; +} + +static int aram_exclusive_app_file_node(struct seq_file *s, void *data) +{ + const struct app_mem_size *mem_size = s->private; + + seq_printf(s, "%llu\n", mem_size->aram_x); + + return 0; +} + +#define ADSP_APP_CREATE_FOLDER(x, root) \ + do {\ + x = debugfs_create_dir(#x, root); \ + if (IS_ERR_OR_NULL(x)) { \ + dev_err(dev, "unable to create app %s folder\n", #x); \ + ret = -ENOENT; \ + goto rm_debug_root; \ + } \ + } while (0) + +#define ADSP_APP_CREATE_FILE(x, priv, root) \ + do { \ + if (IS_ERR_OR_NULL(debugfs_create_file(#x, S_IRUSR, root, \ + priv, &x##_node_operations))) { \ + dev_err(dev, "unable tp create app %s file\n", #x); \ + ret = -ENOENT; \ + goto rm_debug_root; \ + } \ + } while (0) + +#define ADSP_APP_FILE_OPERATION(x) \ +static int x##_open(struct inode *inode, struct file *file) \ +{ \ + return single_open(file, x##_app_file_node, inode->i_private); \ +} \ +\ +static const struct file_operations x##_node_operations = { \ + .open = x##_open, \ + .read = seq_read, \ + .llseek = seq_lseek, \ + .release = single_release, \ +}; + +ADSP_APP_FILE_OPERATION(dump_binary_in_2bytes); +ADSP_APP_FILE_OPERATION(dump_binary_in_words); +ADSP_APP_FILE_OPERATION(host_load_addr); +ADSP_APP_FILE_OPERATION(adsp_load_addr); +ADSP_APP_FILE_OPERATION(size); + +ADSP_APP_FILE_OPERATION(dram); +ADSP_APP_FILE_OPERATION(dram_shared); +ADSP_APP_FILE_OPERATION(dram_shared_wc); +ADSP_APP_FILE_OPERATION(aram); +ADSP_APP_FILE_OPERATION(aram_exclusive); + +static int create_adsp_app_debugfs(struct nvadsp_app_service *ser) +{ + + struct app_mem_size *mem_size = (struct app_mem_size *)ser->mem_size; + struct device *dev = &priv.pdev->dev; + struct dentry *instance_mem_sizes; + struct dentry *root; + int ret = 0; + + root = debugfs_create_dir(ser->name, + priv.adsp_app_debugfs_root); + if (IS_ERR_OR_NULL(root)) { + ret = -EINVAL; + goto err_out; + } + + ADSP_APP_CREATE_FILE(dump_binary_in_2bytes, ser, root); + ADSP_APP_CREATE_FILE(dump_binary_in_words, ser, root); + ADSP_APP_CREATE_FILE(host_load_addr, ser, root); + ADSP_APP_CREATE_FILE(adsp_load_addr, ser, root); + ADSP_APP_CREATE_FILE(size, ser, root); + ADSP_APP_CREATE_FOLDER(instance_mem_sizes, root); + ADSP_APP_CREATE_FILE(dram, mem_size, instance_mem_sizes); + ADSP_APP_CREATE_FILE(dram_shared, mem_size, instance_mem_sizes); + ADSP_APP_CREATE_FILE(dram_shared_wc, mem_size, instance_mem_sizes); + ADSP_APP_CREATE_FILE(aram, mem_size, instance_mem_sizes); + ADSP_APP_CREATE_FILE(aram_exclusive, mem_size, instance_mem_sizes); + + root = ser->debugfs; + return 0; +rm_debug_root: + debugfs_remove_recursive(root); +err_out: + return ret; +} + +static int __init adsp_app_debug_init(struct dentry *root) +{ + priv.adsp_app_debugfs_root = debugfs_create_dir("adsp_apps", root); + return IS_ERR_OR_NULL(priv.adsp_app_debugfs_root) ? -ENOMEM : 0; +} +#endif /* CONFIG_DEBUG_FS */ + +static struct nvadsp_app_service *get_loaded_service(const char *appfile) +{ + struct device *dev = &priv.pdev->dev; + struct nvadsp_app_service *ser; + + list_for_each_entry(ser, &priv.service_list, node) { + if (!strcmp(appfile, ser->name)) { + dev_dbg(dev, "module %s already loaded\n", appfile); + return ser; + } + } + dev_dbg(dev, "module %s will be loaded\n", appfile); + return NULL; +} + +static inline void extract_appname(char *appname, const char *appfile) +{ + char *token = strstr(appfile, DYN_APP_EXTN); + int len = token ? token - appfile : strlen(appfile); + + strncpy(appname, appfile, len); + appname[len] = '\0'; +} + +static nvadsp_app_handle_t app_load(const char *appfile, + struct adsp_shared_app *shared_app, bool dynamic) +{ + struct nvadsp_drv_data *drv_data; + struct device *dev = &priv.pdev->dev; + char appname[NVADSP_NAME_SZ] = { }; + struct nvadsp_app_service *ser; + + drv_data = platform_get_drvdata(priv.pdev); + extract_appname(appname, appfile); + mutex_lock(&priv.service_lock_list); + ser = get_loaded_service(appname); + if (!ser) { + + /* dynamic loading is disabled when running in secure mode */ + if (drv_data->adsp_os_secload && dynamic) + goto err; + dev_dbg(dev, "loading app %s %s\n", appfile, appname); + ser = devm_kzalloc(dev, sizeof(*ser), GFP_KERNEL); + if (!ser) + goto err; + strlcpy(ser->name, appname, NVADSP_NAME_SZ); + + /*load the module in to memory */ + ser->mod = dynamic ? + load_adsp_dynamic_module(appfile, appfile, dev) : + load_adsp_static_module(appfile, shared_app, dev); + if (IS_ERR_OR_NULL(ser->mod)) + goto err_free_service; + ser->mem_size = &ser->mod->mem_size; + + mutex_init(&ser->lock); + INIT_LIST_HEAD(&ser->app_head); + + /* add the app instance service to the list */ + list_add_tail(&ser->node, &priv.service_list); +#ifdef CONFIG_DEBUG_FS + create_adsp_app_debugfs(ser); +#endif + dev_dbg(dev, "loaded app %s\n", ser->name); + } + mutex_unlock(&priv.service_lock_list); + + return ser; + +err_free_service: + devm_kfree(dev, ser); +err: + mutex_unlock(&priv.service_lock_list); + return NULL; +} + + +nvadsp_app_handle_t nvadsp_app_load(const char *appname, const char *appfile) +{ + struct nvadsp_drv_data *drv_data; + + if (IS_ERR_OR_NULL(priv.pdev)) { + pr_err("ADSP Driver is not initialized\n"); + return NULL; + } + + drv_data = platform_get_drvdata(priv.pdev); + + if (!drv_data->adsp_os_running) + return NULL; + + return app_load(appfile, NULL, true); +} +EXPORT_SYMBOL(nvadsp_app_load); + +static void free_instance_memory(nvadsp_app_info_t *app, + const struct app_mem_size *sz) +{ + adsp_app_mem_t *mem = &app->mem; + adsp_app_iova_mem_t *iova_mem = &app->iova_mem; + + if (mem->dram) { + nvadsp_free_coherent(sz->dram, mem->dram, iova_mem->dram); + mem->dram = NULL; + iova_mem->dram = 0; + } + + if (mem->shared) { + nvadsp_free_coherent(sz->dram_shared, mem->shared, + iova_mem->shared); + mem->shared = NULL; + iova_mem->shared = 0; + } + + if (mem->shared_wc) { + nvadsp_free_coherent(sz->dram_shared_wc, mem->shared_wc, + iova_mem->shared_wc); + mem->shared_wc = NULL; + iova_mem->shared_wc = 0; + } + + if (mem->aram_flag) + aram_release(mem->aram); + else if (mem->aram) + nvadsp_free_coherent(sz->aram, mem->aram, iova_mem->aram); + mem->aram = NULL; + iova_mem->aram = 0; + mem->aram_flag = 0; + + if (mem->aram_x_flag) { + aram_release(mem->aram_x); + mem->aram_x = NULL; + iova_mem->aram_x = 0; + mem->aram_flag = 0; + } + +} + +static int create_instance_memory(nvadsp_app_info_t *app, + const struct app_mem_size *sz) +{ + adsp_app_iova_mem_t *iova_mem = &app->iova_mem; + struct device *dev = &priv.pdev->dev; + adsp_app_mem_t *mem = &app->mem; + char name[NVADSP_NAME_SZ]; + void *aram_handle; + dma_addr_t da; + + snprintf(name, NVADSP_NAME_SZ, "%s:%d", app->name, app->instance_id); + + if (sz->dram) { + mem->dram = nvadsp_alloc_coherent(sz->dram, &da, GFP_KERNEL); + iova_mem->dram = (uint32_t)da; + if (!mem->dram) { + dev_err(dev, "app %s dram alloc failed\n", + name); + goto end; + } + dev_dbg(dev, "%s :: mem.dram %p 0x%x\n", name, + mem->dram, iova_mem->dram); + } + + if (sz->dram_shared) { + mem->shared = nvadsp_alloc_coherent(sz->dram_shared, + &da, GFP_KERNEL); + if (!mem->shared) { + dev_err(dev, "app %s shared dram alloc failed\n", + name); + goto end; + } + iova_mem->shared = (uint32_t)da; + dev_dbg(dev, "%s :: mem.shared %p 0x%x\n", name, + mem->shared, iova_mem->shared); + } + + if (sz->dram_shared_wc) { + mem->shared_wc = nvadsp_alloc_coherent(sz->dram_shared_wc, + &da, GFP_KERNEL); + if (!mem->shared_wc) { + dev_err(dev, "app %s shared dram wc alloc failed\n", + name); + goto end; + } + iova_mem->shared_wc = (uint32_t)da; + dev_dbg(dev, "%s :: mem.shared_wc %p 0x%x\n", name, + mem->shared_wc, iova_mem->shared_wc); + } + + if (sz->aram) { + aram_handle = aram_request(name, sz->aram); + if (!IS_ERR_OR_NULL(aram_handle)) { + iova_mem->aram = aram_get_address(aram_handle); + mem->aram = aram_handle; + iova_mem->aram_flag = mem->aram_flag = 1; + dev_dbg(dev, "%s aram %x\n", name, iova_mem->aram); + } else { + dev_dbg(dev, "app %s no ARAM memory ! using DRAM\n", + name); + mem->aram = nvadsp_alloc_coherent(sz->aram, + &da, GFP_KERNEL); + if (!mem->aram) { + iova_mem->aram_flag = mem->aram_flag = 0; + dev_err(dev, + "app %s aram memory alloc failed\n", + name); + goto end; + } + iova_mem->aram = (uint32_t)da; + dev_dbg(dev, "%s :: mem.aram %p 0x%x\n", name, + mem->aram, iova_mem->aram); + } + } + + if (sz->aram_x) { + aram_handle = aram_request(name, sz->aram); + if (!IS_ERR_OR_NULL(aram_handle)) { + iova_mem->aram_x = aram_get_address(aram_handle); + mem->aram_x = aram_handle; + iova_mem->aram_x_flag = mem->aram_x_flag = 1; + dev_dbg(dev, "aram_x %x\n", iova_mem->aram_x); + } else { + iova_mem->aram_x = 0; + iova_mem->aram_x_flag = mem->aram_x_flag = 0; + dev_err(dev, "app %s aram x memory alloc failed\n", + name); + } + } + return 0; + +end: + free_instance_memory(app, sz); + return -ENOMEM; +} + +static void fill_app_instance_data(nvadsp_app_info_t *app, + struct nvadsp_app_service *ser, nvadsp_app_args_t *app_args, + struct run_app_instance_data *data, uint32_t stack_sz) +{ + adsp_app_iova_mem_t *iova_mem = &app->iova_mem; + + data->adsp_mod_ptr = ser->mod->adsp_module_ptr; + /* copy the iova address to adsp so that adsp can access the memory */ + data->dram_data_ptr = iova_mem->dram; + data->dram_shared_ptr = iova_mem->shared; + data->dram_shared_wc_ptr = iova_mem->shared_wc; + data->aram_ptr = iova_mem->aram; + data->aram_flag = iova_mem->aram_flag; + data->aram_x_ptr = iova_mem->aram_x; + data->aram_x_flag = iova_mem->aram_x_flag; + + if (app_args) + memcpy(&data->app_args, app_args, sizeof(nvadsp_app_args_t)); + /* + * app on adsp holds the reference of host app instance to communicate + * back when completed. This way we do not need to iterate through the + * list to find the instance. + */ + data->host_ref = (uint64_t)app; + + /* copy instance mem_size */ + memcpy(&data->mem_size, ser->mem_size, sizeof(struct app_mem_size)); +} + +static nvadsp_app_info_t *create_app_instance(nvadsp_app_handle_t handle, + nvadsp_app_args_t *app_args, struct run_app_instance_data *data, + app_complete_status_notifier notifier, uint32_t stack_size) +{ + struct nvadsp_app_service *ser = (void *)handle; + struct device *dev = &priv.pdev->dev; + nvadsp_app_info_t *app; + int *state; + int *id; + + app = kzalloc(sizeof(*app), GFP_KERNEL); + if (unlikely(!app)) { + dev_err(dev, "cannot allocate memory for app %s instance\n", + ser->name); + goto err_value; + } + /* set the instance name with the app name */ + app->name = ser->name; + /* associate a unique id */ + id = (int *)&app->instance_id; + *id = ser->generated_instance_id++; + /* + * hold the pointer to the service, to dereference later during deinit + */ + app->handle = ser; + + /* create the instance memory required by the app instance */ + if (create_instance_memory(app, ser->mem_size)) { + dev_err(dev, "instance creation failed for app %s:%d\n", + app->name, app->instance_id); + goto free_app; + } + + /* assign the stack that is needed by the app */ + data->stack_size = stack_size; + + /* set the state to INITIALIZED. No need to do it in a spin lock */ + state = (int *)&app->state; + *state = NVADSP_APP_STATE_INITIALIZED; + + /* increment instance count and add the app instance to service list */ + mutex_lock(&ser->lock); + list_add_tail(&app->node, &ser->app_head); + ser->instance++; + mutex_unlock(&ser->lock); + + fill_app_instance_data(app, ser, app_args, data, stack_size); + + init_completion(&app->wait_for_app_start); + init_completion(&app->wait_for_app_complete); + set_app_complete_notifier(app, notifier); + + dev_dbg(dev, "app %s instance %d initilized\n", + app->name, app->instance_id); + dev_dbg(dev, "app %s has %d instances\n", ser->name, ser->instance); + goto end; + +free_app: + kfree(app); +err_value: + app = ERR_PTR(-ENOMEM); +end: + return app; +} + +nvadsp_app_info_t __must_check *nvadsp_app_init(nvadsp_app_handle_t handle, + nvadsp_app_args_t *args) +{ + struct nvadsp_app_shared_msg_pool *msg_pool; + struct nvadsp_shared_mem *shared_mem; + union app_loader_message *message; + struct nvadsp_drv_data *drv_data; + struct app_loader_data *data; + nvadsp_app_info_t *app; + msgq_t *msgq_send; + int *state; + + if (IS_ERR_OR_NULL(priv.pdev)) { + pr_err("ADSP Driver is not initialized\n"); + goto err; + } + + drv_data = platform_get_drvdata(priv.pdev); + + if (!drv_data->adsp_os_running) + goto err; + + if (IS_ERR_OR_NULL(handle)) + goto err; + + message = kzalloc(sizeof(*message), GFP_KERNEL); + if (!message) + goto err; + + shared_mem = drv_data->shared_adsp_os_data; + msg_pool = &shared_mem->app_shared_msg_pool; + msgq_send = &msg_pool->app_loader_send_message.msgq; + data = &message->data; + + app = create_app_instance(handle, args, &data->app_init, NULL, 0); + if (IS_ERR_OR_NULL(app)) { + kfree(message); + goto err; + } + app->priv = data; + data->app_init.message = ADSP_APP_INIT; + + message->msgq_msg.size = MSGQ_MSG_PAYLOAD_WSIZE(*message); + msgq_queue_message(msgq_send, &message->msgq_msg); + + if (app->return_status) { + state = (int *)&app->state; + *state = NVADSP_APP_STATE_STARTED; + } + + nvadsp_mbox_send(&priv.mbox, 0, NVADSP_MBOX_SMSG, false, 0); + + wait_for_completion(&app->wait_for_app_start); + init_completion(&app->wait_for_app_start); + return app; +err: + return ERR_PTR(-ENOMEM); +} +EXPORT_SYMBOL(nvadsp_app_init); + +static int start_app_on_adsp(nvadsp_app_info_t *app, + union app_loader_message *message, bool block) +{ + struct nvadsp_app_shared_msg_pool *msg_pool; + struct device *dev = &priv.pdev->dev; + struct nvadsp_shared_mem *shared_mem; + struct nvadsp_drv_data *drv_data; + msgq_t *msgq_send; + int *state; + + drv_data = platform_get_drvdata(priv.pdev); + shared_mem = drv_data->shared_adsp_os_data; + msg_pool = &shared_mem->app_shared_msg_pool; + msgq_send = &msg_pool->app_loader_send_message.msgq; + + message->msgq_msg.size = MSGQ_MSG_PAYLOAD_WSIZE(*message); + msgq_queue_message(msgq_send, &message->msgq_msg); + + state = (int *)&app->state; + *state = NVADSP_APP_STATE_STARTED; + + nvadsp_mbox_send(&priv.mbox, 0, NVADSP_MBOX_SMSG, false, 0); + + if (block) { + wait_for_completion(&app->wait_for_app_start); + if (app->return_status) { + dev_err(dev, "%s app instance %d failed to start\n", + app->name, app->instance_id); + state = (int *)&app->state; + *state = NVADSP_APP_STATE_INITIALIZED; + } + } + + return app->return_status; +} + +int nvadsp_app_start(nvadsp_app_info_t *app) +{ + union app_loader_message *message = app->priv; + struct app_loader_data *data = &message->data; + struct nvadsp_drv_data *drv_data; + int ret = -EINVAL; + + if (IS_ERR_OR_NULL(app)) + return -EINVAL; + + if (IS_ERR_OR_NULL(priv.pdev)) { + pr_err("ADSP Driver is not initialized\n"); + goto err; + } + + drv_data = platform_get_drvdata(priv.pdev); + + if (!drv_data->adsp_os_running) + goto err; + + data->app_init.message = ADSP_APP_START; + data->app_init.adsp_ref = app->token; + data->app_init.stack_size = app->stack_size; + ret = start_app_on_adsp(app, app->priv, true); +err: + return ret; +} +EXPORT_SYMBOL(nvadsp_app_start); + +nvadsp_app_info_t *nvadsp_run_app(nvadsp_os_handle_t os_handle, + const char *appfile, nvadsp_app_args_t *app_args, + app_complete_status_notifier notifier, uint32_t stack_sz, bool block) +{ + union app_loader_message message = {}; + nvadsp_app_handle_t service_handle; + struct nvadsp_drv_data *drv_data; + nvadsp_app_info_t *info = NULL; + struct app_loader_data *data; + struct device *dev; + int ret; + + if (IS_ERR_OR_NULL(priv.pdev)) { + pr_err("ADSP Driver is not initialized\n"); + info = ERR_PTR(-EINVAL); + goto end; + } + + drv_data = platform_get_drvdata(priv.pdev); + dev = &priv.pdev->dev; + + if (!drv_data->adsp_os_running) + goto end; + + if (IS_ERR_OR_NULL(appfile)) + goto end; + + data = &message.data; + service_handle = app_load(appfile, NULL, true); + if (!service_handle) { + dev_err(dev, "unable to load the app %s\n", appfile); + goto end; + } + + info = create_app_instance(service_handle, app_args, + &data->app_init, notifier, stack_sz); + if (IS_ERR_OR_NULL(info)) { + dev_err(dev, "unable to create instance for app %s\n", appfile); + goto end; + } + data->app_init.message = RUN_ADSP_APP; + + ret = start_app_on_adsp(info, &message, block); + if (ret) { + delete_app_instance(info); + info = NULL; + } +end: + return info; +} +EXPORT_SYMBOL(nvadsp_run_app); + +static void delete_app_instance(nvadsp_app_info_t *app) +{ + struct nvadsp_app_service *ser = + (struct nvadsp_app_service *)app->handle; + struct device *dev = &priv.pdev->dev; + + dev_dbg(dev, "%s:freeing app %s:%d\n", + __func__, app->name, app->instance_id); + + /* update the service app instance manager atomically */ + mutex_lock(&ser->lock); + ser->instance--; + list_del(&app->node); + mutex_unlock(&ser->lock); + + /* free instance memory */ + free_instance_memory(app, ser->mem_size); + kfree(app->priv); + kfree(app); +} + +void nvadsp_exit_app(nvadsp_app_info_t *app, bool terminate) +{ + int *state; + + if (IS_ERR_OR_NULL(priv.pdev)) { + pr_err("ADSP Driver is not initialized\n"); + return; + } + + if (IS_ERR_OR_NULL(app)) + return; + + /* TODO: add termination if possible to kill thread on adsp */ + if (app->state == NVADSP_APP_STATE_STARTED) { + wait_for_completion(&app->wait_for_app_complete); + state = (int *)&app->state; + *state = NVADSP_APP_STATE_INITIALIZED; + } + delete_app_instance(app); +} +EXPORT_SYMBOL(nvadsp_exit_app); + +int nvadsp_app_deinit(nvadsp_app_info_t *app) +{ + nvadsp_exit_app(app, false); + return 0; +} +EXPORT_SYMBOL(nvadsp_app_deinit); + +int nvadsp_app_stop(nvadsp_app_info_t *app) +{ + return -ENOENT; +} +EXPORT_SYMBOL(nvadsp_app_stop); + +void nvadsp_app_unload(nvadsp_app_handle_t handle) +{ + struct nvadsp_drv_data *drv_data; + struct nvadsp_app_service *ser; + struct device *dev; + + if (!priv.pdev) { + pr_err("ADSP Driver is not initialized\n"); + return; + } + + drv_data = platform_get_drvdata(priv.pdev); + dev = &priv.pdev->dev; + + if (!drv_data->adsp_os_running) + return; + + if (IS_ERR_OR_NULL(handle)) + return; + + ser = (struct nvadsp_app_service *)handle; + if (!ser->mod->dynamic) + return; + + mutex_lock(&priv.service_lock_list); + if (ser->instance) { + dev_err(dev, "cannot unload app %s, has instances %d\n", + ser->name, ser->instance); + return; + } + + list_del(&ser->node); +#ifdef CONFIG_DEBUG_FS + debugfs_remove_recursive(ser->debugfs); +#endif + unload_adsp_module(ser->mod); + devm_kfree(dev, ser); + mutex_unlock(&priv.service_lock_list); +} +EXPORT_SYMBOL(nvadsp_app_unload); + +static status_t nvadsp_app_receive_handler(uint32_t msg, void *hdata) +{ + union app_complete_status_message message = { }; + struct nvadsp_app_shared_msg_pool *msg_pool; + struct app_complete_status_data *data; + struct nvadsp_shared_mem *shared_mem; + struct nvadsp_drv_data *drv_data; + struct platform_device *pdev; + nvadsp_app_info_t *app; + struct device *dev; + msgq_t *msgq_recv; + uint32_t *token; + + pdev = hdata; + dev = &pdev->dev; + drv_data = platform_get_drvdata(pdev); + shared_mem = drv_data->shared_adsp_os_data; + msg_pool = &shared_mem->app_shared_msg_pool; + msgq_recv = &msg_pool->app_loader_recv_message.msgq; + data = &message.complete_status_data; + + message.msgq_msg.size = MSGQ_MSG_PAYLOAD_WSIZE(*data); + if (msgq_dequeue_message(msgq_recv, &message.msgq_msg)) { + dev_err(dev, "unable to dequeue app status message\n"); + return 0; + } + + app = (nvadsp_app_info_t *)data->host_ref; + app->return_status = data->status; + app->status_msg = data->header.message; + token = (uint32_t *)&app->token; + *token = data->adsp_ref; + + if (app->complete_status_notifier) { + app->complete_status_notifier(app, + app->status_msg, app->return_status); + } + + switch (data->header.message) { + case ADSP_APP_START_STATUS: + complete_all(&app->wait_for_app_start); + break; + case ADSP_APP_COMPLETE_STATUS: + complete_all(&app->wait_for_app_complete); + break; + } + + return 0; +} + +int load_adsp_static_apps(void) +{ + struct nvadsp_app_shared_msg_pool *msg_pool; + struct nvadsp_shared_mem *shared_mem; + struct nvadsp_drv_data *drv_data; + struct platform_device *pdev; + struct device *dev; + msgq_t *msgq_recv; + + pdev = priv.pdev; + dev = &pdev->dev; + drv_data = platform_get_drvdata(pdev); + shared_mem = drv_data->shared_adsp_os_data; + msg_pool = &shared_mem->app_shared_msg_pool; + msgq_recv = &msg_pool->app_loader_recv_message.msgq; + + while (1) { + union app_complete_status_message message = { }; + struct adsp_static_app_data *data; + struct adsp_shared_app *shared_app; + char *name; + + data = &message.static_app_data; + message.msgq_msg.size = MSGQ_MSG_PAYLOAD_WSIZE(*data); + if (msgq_dequeue_message(msgq_recv, &message.msgq_msg)) { + dev_err(dev, "dequeue of static apps failed\n"); + return -EINVAL; + } + shared_app = &data->shared_app; + name = shared_app->name; + if (!shared_app->mod_ptr) + break; + /* Skip Start on boot apps */ + if (shared_app->flags & ADSP_APP_FLAG_START_ON_BOOT) + continue; + app_load(name, shared_app, false); + } + return 0; +} + +int __init nvadsp_app_module_probe(struct platform_device *pdev) +{ +#ifdef CONFIG_DEBUG_FS + struct nvadsp_drv_data *drv_data = platform_get_drvdata(pdev); +#endif + uint16_t mbox_id = APP_LOADER_MBOX_ID; + struct device *dev = &pdev->dev; + int ret; + + dev_info(dev, "%s\n", __func__); + + ret = nvadsp_mbox_open(&priv.mbox, &mbox_id, + "app_service", nvadsp_app_receive_handler, pdev); + if (ret) { + dev_err(dev, "unable to open mailbox\n"); + goto end; + } + priv.pdev = pdev; + INIT_LIST_HEAD(&priv.service_list); + init_completion(&priv.os_load_complete); + mutex_init(&priv.service_lock_list); + +#ifdef CONFIG_DEBUG_FS + if (adsp_app_debug_init(drv_data->adsp_debugfs_root)) + dev_err(&pdev->dev, "unable to create adsp apps debugfs\n"); +#endif +end: + return ret; +} diff --git a/drivers/platform/tegra/nvadsp/app_loader_linker.c b/drivers/platform/tegra/nvadsp/app_loader_linker.c new file mode 100644 index 00000000..4b380fa1 --- /dev/null +++ b/drivers/platform/tegra/nvadsp/app_loader_linker.c @@ -0,0 +1,960 @@ +/* + * nvadsp_app.c + * + * ADSP OS App management + * + * Copyright (C) 2014-2015 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 +#include +#include +#include +#include +#include +#include +#include + +#include "os.h" +#include "dram_app_mem_manager.h" +#include "adsp_shared_struct.h" + +#ifdef CONFIG_DEBUG_SET_MODULE_RONX +# define debug_align(X) ALIGN(X, PAGE_SIZE) +#else +# define debug_align(X) (X) +#endif + + +#ifndef ARCH_SHF_SMALL +#define ARCH_SHF_SMALL 0 +#endif + +#define BITS_PER_INT 32 +#define INIT_OFFSET_MASK (1U < (BITS_PER_INT-1)) + + +#define HWCAP_SWP (1 << 0) +#define HWCAP_HALF (1 << 1) +#define HWCAP_THUMB (1 << 2) +#define HWCAP_26BIT (1 << 3) /* Play it safe */ +#define HWCAP_FAST_MULT (1 << 4) +#define HWCAP_FPA (1 << 5) +#define HWCAP_VFP (1 << 6) +#define HWCAP_EDSP (1 << 7) +#define HWCAP_JAVA (1 << 8) +#define HWCAP_IWMMXT (1 << 9) +#define HWCAP_CRUNCH (1 << 10) +#define HWCAP_THUMBEE (1 << 11) +#define HWCAP_NEON (1 << 12) +#define HWCAP_VFPv3 (1 << 13) +#define HWCAP_VFPv3D16 (1 << 14) /* also set for VFPv4-D16 */ +#define HWCAP_TLS (1 << 15) +#define HWCAP_VFPv4 (1 << 16) +#define HWCAP_IDIVA (1 << 17) +#define HWCAP_IDIVT (1 << 18) +#define HWCAP_VFPD32 (1 << 19) /* set if VFP has 32 regs (not 16) */ +#define HWCAP_IDIV (HWCAP_IDIVA | HWCAP_IDIVT) + +#define HWCAP_LPAE (1 << 20) +#define HWCAP_EVTSTRM_32 (1 << 21) + + +#define EF_ARM_EABI_MASK 0xff000000 +#define EF_ARM_EABI_UNKNOWN 0x00000000 +#define EF_ARM_EABI_VER1 0x01000000 +#define EF_ARM_EABI_VER2 0x02000000 +#define EF_ARM_EABI_VER3 0x03000000 +#define EF_ARM_EABI_VER4 0x04000000 +#define EF_ARM_EABI_VER5 0x05000000 + +#define EF_ARM_BE8 0x00800000 /* ABI 4,5 */ +#define EF_ARM_LE8 0x00400000 /* ABI 4,5 */ +#define EF_ARM_MAVERICK_FLOAT 0x00000800 /* ABI 0 */ +#define EF_ARM_VFP_FLOAT 0x00000400 /* ABI 0 */ +#define EF_ARM_SOFT_FLOAT 0x00000200 /* ABI 0 */ +#define EF_ARM_OLD_ABI 0x00000100 /* ABI 0 */ +#define EF_ARM_NEW_ABI 0x00000080 /* ABI 0 */ +#define EF_ARM_ALIGN8 0x00000040 /* ABI 0 */ +#define EF_ARM_PIC 0x00000020 /* ABI 0 */ +#define EF_ARM_MAPSYMSFIRST 0x00000010 /* ABI 2 */ +#define EF_ARM_APCS_FLOAT 0x00000010 /* ABI 0, floats in fp regs */ +#define EF_ARM_DYNSYMSUSESEGIDX 0x00000008 /* ABI 2 */ +#define EF_ARM_APCS_26 0x00000008 /* ABI 0 */ +#define EF_ARM_SYMSARESORTED 0x00000004 /* ABI 1,2 */ +#define EF_ARM_INTERWORK 0x00000004 /* ABI 0 */ +#define EF_ARM_HASENTRY 0x00000002 /* All */ +#define EF_ARM_RELEXEC 0x00000001 /* All */ + + +#define R_ARM_NONE 0 +#define R_ARM_PC24 1 +#define R_ARM_ABS32 2 +#define R_ARM_CALL 28 +#define R_ARM_JUMP24 29 +#define R_ARM_TARGET1 38 +#define R_ARM_V4BX 40 +#define R_ARM_PREL31 42 +#define R_ARM_MOVW_ABS_NC 43 +#define R_ARM_MOVT_ABS 44 + +#define R_ARM_THM_CALL 10 +#define R_ARM_THM_JUMP24 30 +#define R_ARM_THM_MOVW_ABS_NC 47 +#define R_ARM_THM_MOVT_ABS 48 + + +struct load_info { + const char *name; + struct elf32_hdr *hdr; + unsigned long len; + struct elf32_shdr *sechdrs; + char *secstrings, *strtab; + unsigned long symoffs, stroffs; + unsigned int num_debug; + bool sig_ok; + struct device *dev; + struct { + unsigned int sym, str, mod, vers, info, pcpu; + } index; +}; + +static int +apply_relocate(const struct load_info *info, Elf32_Shdr *sechdrs, + const char *strtab, unsigned int symindex, + unsigned int relindex, struct adsp_module *module) +{ + Elf32_Shdr *symsec = sechdrs + symindex; + Elf32_Shdr *relsec = sechdrs + relindex; + Elf32_Shdr *dstsec = sechdrs + relsec->sh_info; + Elf32_Rel *rel = (void *)info->hdr + relsec->sh_offset; + struct device *dev = info->dev; + unsigned int i; + + dev_dbg(dev, "the relative section is %s dst %s sym %s\n", + info->secstrings + relsec->sh_name, + info->secstrings + dstsec->sh_name, + info->secstrings + symsec->sh_name); + + for (i = 0; i < relsec->sh_size / sizeof(Elf32_Rel); i++, rel++) { + void *loc; + Elf32_Sym *sym; + const char *symname; + s32 offset; + u32 upper, lower, sign, j1, j2; + uint32_t adsp_loc; + bool switch_mode = false; + int h_bit = 0; + + offset = ELF32_R_SYM(rel->r_info); + if (offset < 0 || (offset > + (symsec->sh_size / sizeof(Elf32_Sym)))) { + dev_err(dev, "%s: section %u reloc %u: bad relocation sym offset\n", + module->name, relindex, i); + return -ENOEXEC; + } + + sym = ((Elf32_Sym *)(module->module_ptr + + symsec->sh_addr)) + offset; + symname = info->strtab + sym->st_name; + + dev_dbg(dev, "%s\n", symname); + + if (rel->r_offset < 0 || + rel->r_offset > dstsec->sh_size - sizeof(u32)) { + dev_err(dev, + "%s: section %u reloc %u sym '%s': out of bounds relocation, offset %d size %u\n", + module->name, relindex, i, symname, + rel->r_offset, dstsec->sh_size); + return -ENOEXEC; + } + + loc = module->module_ptr + dstsec->sh_addr + rel->r_offset; + adsp_loc = module->adsp_module_ptr + + dstsec->sh_addr + rel->r_offset; + dev_dbg(dev, "%p 0x%x\n", loc, adsp_loc); + + if (ELF_ST_BIND(sym->st_info) == STB_WEAK + && sym->st_shndx == SHN_UNDEF) { + dev_dbg(dev, "STB_WEAK %s\n", symname); + continue; + } + + switch (ELF32_R_TYPE(rel->r_info)) { + case R_ARM_NONE: + dev_dbg(dev, "R_ARM_NONE\n"); + /* ignore */ + break; + + case R_ARM_ABS32: + case R_ARM_TARGET1: + dev_dbg(dev, "R_ARM_ABS32\n"); + *(u32 *)loc += sym->st_value; + dev_dbg(dev, "addrs: 0x%x %p values: 0x%x 0x%x\n", + adsp_loc, loc, sym->st_value, + *(u32 *)loc); + break; + + case R_ARM_PC24: + case R_ARM_CALL: + case R_ARM_JUMP24: + dev_dbg(dev, "R_ARM_CALL R_ARM_JUMP24\n"); + offset = (*(u32 *)loc & 0x00ffffff) << 2; + if (offset & 0x02000000) + offset -= 0x04000000; + + offset += sym->st_value - adsp_loc; + if ((ELF32_ST_TYPE(sym->st_info) == STT_FUNC) + && (offset & 3)) { + dev_dbg(dev, "switching the mode from ARM to THUMB\n"); + switch_mode = true; + h_bit = (offset & 2); + dev_dbg(dev, + "%s offset 0x%x hbit %d", + symname, offset, h_bit); + } + + if (offset <= (s32)0xfe000000 || + offset >= (s32)0x02000000) { + dev_err(dev, + "%s: section %u reloc %u sym '%s': relocation %u out of range (%p -> %#x)\n", + module->name, relindex, i, symname, + ELF32_R_TYPE(rel->r_info), loc, + sym->st_value); + return -ENOEXEC; + } + + offset >>= 2; + + *(u32 *)loc &= 0xff000000; + *(u32 *)loc |= offset & 0x00ffffff; + if (switch_mode) { + *(u32 *)loc &= ~(0xff000000); + if (h_bit) + *(u32 *)loc |= 0xfb000000; + else + *(u32 *)loc |= 0xfa000000; + } + + dev_dbg(dev, + "%s address 0x%x instruction 0x%x\n", + symname, adsp_loc, *(u32 *)loc); + break; + + case R_ARM_V4BX: + dev_dbg(dev, "R_ARM_V4BX\n"); + /* Preserve Rm and the condition code. Alter + * other bits to re-code instruction as + * MOV PC,Rm. + */ + *(u32 *)loc &= 0xf000000f; + *(u32 *)loc |= 0x01a0f000; + break; + + case R_ARM_PREL31: + dev_dbg(dev, "R_ARM_PREL31\n"); + offset = *(u32 *)loc + sym->st_value - adsp_loc; + *(u32 *)loc = offset & 0x7fffffff; + break; + + case R_ARM_MOVW_ABS_NC: + case R_ARM_MOVT_ABS: + dev_dbg(dev, "R_ARM_MOVT_ABS\n"); + offset = *(u32 *)loc; + offset = ((offset & 0xf0000) >> 4) | (offset & 0xfff); + offset = (offset ^ 0x8000) - 0x8000; + + offset += sym->st_value; + if (ELF32_R_TYPE(rel->r_info) == R_ARM_MOVT_ABS) + offset >>= 16; + + *(u32 *)loc &= 0xfff0f000; + *(u32 *)loc |= ((offset & 0xf000) << 4) | + (offset & 0x0fff); + break; + + case R_ARM_THM_CALL: + case R_ARM_THM_JUMP24: + dev_dbg(dev, "R_ARM_THM_CALL R_ARM_THM_JUMP24\n"); + upper = *(u16 *)loc; + lower = *(u16 *)(loc + 2); + + /* + * 25 bit signed address range (Thumb-2 BL and B.W + * instructions): + * S:I1:I2:imm10:imm11:0 + * where: + * S = upper[10] = offset[24] + * I1 = ~(J1 ^ S) = offset[23] + * I2 = ~(J2 ^ S) = offset[22] + * imm10 = upper[9:0] = offset[21:12] + * imm11 = lower[10:0] = offset[11:1] + * J1 = lower[13] + * J2 = lower[11] + */ + sign = (upper >> 10) & 1; + j1 = (lower >> 13) & 1; + j2 = (lower >> 11) & 1; + offset = (sign << 24) | ((~(j1 ^ sign) & 1) << 23) | + ((~(j2 ^ sign) & 1) << 22) | + ((upper & 0x03ff) << 12) | + ((lower & 0x07ff) << 1); + if (offset & 0x01000000) + offset -= 0x02000000; + offset += sym->st_value - adsp_loc; + + /* + * For function symbols, only Thumb addresses are + * allowed (no interworking). + * + * For non-function symbols, the destination + * has no specific ARM/Thumb disposition, so + * the branch is resolved under the assumption + * that interworking is not required. + */ + if (ELF32_ST_TYPE(sym->st_info) == STT_FUNC && + !(offset & 1)) { + dev_dbg(dev, + "switching the mode from THUMB to ARM\n"); + switch_mode = true; + offset = ALIGN(offset, 4); + } + + if (offset <= (s32)0xff000000 || + offset >= (s32)0x01000000) { + dev_err(dev, + "%s: section %u reloc %u sym '%s': relocation %u out of range (%p -> %#x)\n", + module->name, relindex, i, symname, + ELF32_R_TYPE(rel->r_info), loc, + sym->st_value); + return -ENOEXEC; + } + + sign = (offset >> 24) & 1; + j1 = sign ^ (~(offset >> 23) & 1); + j2 = sign ^ (~(offset >> 22) & 1); + *(u16 *)loc = (u16)((upper & 0xf800) | (sign << 10) | + ((offset >> 12) & 0x03ff)); + *(u16 *)(loc + 2) = (u16)((lower & 0xd000) | + (j1 << 13) | (j2 << 11) | + ((offset >> 1) & 0x07ff)); + + if (switch_mode) { + lower = *(u16 *)(loc + 2); + lower &= (~(1 << 12)); + *(u16 *)(loc + 2) = lower; + } + + dev_dbg(dev, + "%s address 0x%x upper instruction 0x%x\n", + symname, adsp_loc, *(u16 *)loc); + dev_dbg(dev, + "%s address 0x%x lower instruction 0x%x\n", + symname, adsp_loc, *(u16 *)(loc + 2)); + break; + + case R_ARM_THM_MOVW_ABS_NC: + case R_ARM_THM_MOVT_ABS: + dev_dbg(dev, "in R_ARM_THM_MOVT_ABS\n"); + upper = *(u16 *)loc; + lower = *(u16 *)(loc + 2); + + /* + * MOVT/MOVW instructions encoding in Thumb-2: + * + * i = upper[10] + * imm4 = upper[3:0] + * imm3 = lower[14:12] + * imm8 = lower[7:0] + * + * imm16 = imm4:i:imm3:imm8 + */ + offset = ((upper & 0x000f) << 12) | + ((upper & 0x0400) << 1) | + ((lower & 0x7000) >> 4) | (lower & 0x00ff); + offset = (offset ^ 0x8000) - 0x8000; + offset += sym->st_value; + + if (ELF32_R_TYPE(rel->r_info) == R_ARM_THM_MOVT_ABS) + offset >>= 16; + + *(u16 *)loc = (u16)((upper & 0xfbf0) | + ((offset & 0xf000) >> 12) | + ((offset & 0x0800) >> 1)); + *(u16 *)(loc + 2) = (u16)((lower & 0x8f00) | + ((offset & 0x0700) << 4) | + (offset & 0x00ff)); + break; + + default: + dev_err(dev, "%s: unknown relocation: %u\n", + module->name, ELF32_R_TYPE(rel->r_info)); + return -ENOEXEC; + } + } + return 0; +} + +static int +apply_relocations(struct adsp_module *mod, + const struct load_info *info) +{ + unsigned int i; + int err = 0; + + /* Now do relocations. */ + for (i = 1; i < info->hdr->e_shnum; i++) { + unsigned int infosec = info->sechdrs[i].sh_info; + + /* Not a valid relocation section? */ + if (infosec >= info->hdr->e_shnum) + continue; + + /* Don't bother with non-allocated sections */ + if (!(info->sechdrs[infosec].sh_flags & SHF_ALLOC)) + continue; + + if (info->sechdrs[i].sh_type == SHT_REL) + err = apply_relocate(info, info->sechdrs, info->strtab, + info->index.sym, i, mod); + else if (info->sechdrs[i].sh_type == SHT_RELA) + return -EINVAL; + if (err < 0) + break; + } + return err; +} + +static int +simplify_symbols(struct adsp_module *mod, + const struct load_info *info) +{ + Elf32_Shdr *symsec = &info->sechdrs[info->index.sym]; + Elf32_Sym *sym = mod->module_ptr + symsec->sh_addr; + unsigned int secbase; + unsigned int i; + int ret = 0; + struct global_sym_info *sym_info; + struct device *dev = info->dev; + + for (i = 1; i < symsec->sh_size / sizeof(Elf32_Sym); i++) { + const char *name = info->strtab + sym[i].st_name; + dev_dbg(dev, "%s\n", name); + switch (sym[i].st_shndx) { + case SHN_COMMON: + /* We compiled with -fno-common. These are not + supposed to happen. */ + dev_err(dev, "Common symbol: '%s'\n", name); + dev_err(dev, + "please compile module %s with -fno-common\n", + mod->name); + ret = -ENOEXEC; + goto end; + + case SHN_ABS: + /* Don't need to do anything */ + dev_dbg(dev, "Absolute symbol: 0x%08lx\n", + (long)sym[i].st_value); + break; + + case SHN_UNDEF: + sym_info = find_global_symbol(name); + + /* Ok if resolved. */ + if (sym_info) { + dev_dbg(dev, "SHN_UNDEF sym '%s':0x%x\n", + name, sym_info->addr); + sym[i].st_value = sym_info->addr; + sym[i].st_info = sym_info->info; + break; + } + + if (ELF_ST_BIND(sym[i].st_info) == STB_WEAK) { + dev_dbg(dev, "WEAK SYM %s not resolved\n", + name); + break; + } + + dev_err(dev, "No symbol '%s' found\n", name); + ret = -ENOEXEC; + goto end; + + default: + /* Divert to percpu allocation if a percpu var. */ + dev_dbg(dev, "default\n"); + secbase = info->sechdrs[sym[i].st_shndx].sh_addr; + sym[i].st_value += secbase + mod->adsp_module_ptr; + dev_dbg(dev, "symbol %s is 0x%x\n", + name, sym[i].st_value); + break; + } + } +end: + return ret; +} + +static int move_module(struct adsp_module *mod, struct load_info *info) +{ + struct device *dev = info->dev; + int i; + + mod->handle = dram_app_mem_request(info->name, mod->size); + if (!mod->handle) { + dev_err(dev, "cannot allocate memory for app %s\n", info->name); + return -ENOMEM; + } + mod->adsp_module_ptr = dram_app_mem_get_address(mod->handle); + mod->module_ptr = nvadsp_da_to_va_mappings(mod->adsp_module_ptr, + mod->size); + dev_info(dev, "module %s Load address %p 0x%x\n", info->name, + mod->module_ptr, mod->adsp_module_ptr); + /* Transfer each section which specifies SHF_ALLOC */ + dev_dbg(dev, "final section addresses:\n"); + for (i = 0; i < info->hdr->e_shnum; i++) { + void *dest; + struct elf32_shdr *shdr = &info->sechdrs[i]; + + if (!(shdr->sh_flags & SHF_ALLOC)) + continue; + + if (shdr->sh_entsize & INIT_OFFSET_MASK) { + dev_dbg(dev, "%s %d\n", + info->secstrings + shdr->sh_name, + shdr->sh_entsize); + dest = mod->module_ptr + + (shdr->sh_entsize & ~INIT_OFFSET_MASK); + } else { + dev_dbg(dev, "%s %d\n", + info->secstrings + shdr->sh_name, + shdr->sh_entsize); + dest = mod->module_ptr + shdr->sh_entsize; + } + + if (shdr->sh_type != SHT_NOBITS) + memcpy(dest, + (void *)info->hdr + shdr->sh_offset, + shdr->sh_size); + /* Update sh_addr to point to copy in image. */ + shdr->sh_addr = (uint32_t)(dest - mod->module_ptr); + dev_dbg(dev, "name %s 0x%x %p 0x%x 0x%x\n", + info->secstrings + shdr->sh_name, shdr->sh_addr, + dest, shdr->sh_addr + mod->adsp_module_ptr, + shdr->sh_size); + } + + return 0; +} + +static int get_offset(struct adsp_module *mod, size_t *size, + struct elf32_shdr *sechdr, unsigned int section) +{ + int ret; + + ret = ALIGN(*size, sechdr->sh_addralign ?: 1); + *size = ret + sechdr->sh_size; + return ret; +} + +static bool +is_core_symbol(const struct elf32_sym *src, + const struct elf32_shdr *sechdrs, unsigned int shnum) +{ + const struct elf32_shdr *sec; + + if (src->st_shndx == SHN_UNDEF + || src->st_shndx >= shnum + || !src->st_name) + return false; + + sec = sechdrs + src->st_shndx; + if (!(sec->sh_flags & SHF_ALLOC) +#ifndef CONFIG_KALLSYMS_ALL + || !(sec->sh_flags & SHF_EXECINSTR) +#endif + || (sec->sh_entsize & INIT_OFFSET_MASK)) + return false; + + return true; +} + +static void layout_sections(struct adsp_module *mod, struct load_info *info) +{ + static unsigned long const masks[][2] = { + /* NOTE: all executable code must be the first section + * in this array; otherwise modify the text_size + * finder in the two loops below */ + { SHF_EXECINSTR | SHF_ALLOC, ARCH_SHF_SMALL }, + { SHF_ALLOC, SHF_WRITE | ARCH_SHF_SMALL }, + { SHF_WRITE | SHF_ALLOC, ARCH_SHF_SMALL }, + { ARCH_SHF_SMALL | SHF_ALLOC, 0 } + }; + unsigned int m, i; + struct device *dev = info->dev; + + for (i = 0; i < info->hdr->e_shnum; i++) + info->sechdrs[i].sh_entsize = ~0U; + + dev_dbg(dev, "Core section allocation order:\n"); + for (m = 0; m < ARRAY_SIZE(masks); ++m) { + for (i = 0; i < info->hdr->e_shnum; ++i) { + struct elf32_shdr *s = &info->sechdrs[i]; + const char *sname = info->secstrings + s->sh_name; + + if ((s->sh_flags & masks[m][0]) != masks[m][0] + || (s->sh_flags & masks[m][1]) + || s->sh_entsize != ~0U + || strstarts(sname, ".init")) + continue; + + s->sh_entsize = get_offset(mod, &mod->size, s, i); + dev_dbg(dev, "\t%s %d\n", sname, s->sh_entsize); + } + } + + dev_dbg(dev, "Init section allocation order:\n"); + for (m = 0; m < ARRAY_SIZE(masks); ++m) { + for (i = 0; i < info->hdr->e_shnum; ++i) { + struct elf32_shdr *s = &info->sechdrs[i]; + const char *sname = info->secstrings + s->sh_name; + + if ((s->sh_flags & masks[m][0]) != masks[m][0] + || (s->sh_flags & masks[m][1]) + || s->sh_entsize != ~0U + || !strstarts(sname, ".init")) + continue; + s->sh_entsize = (get_offset(mod, &mod->size, s, i) + | INIT_OFFSET_MASK); + dev_dbg(dev, "\t%s %d\n", sname, s->sh_entsize); + } + } +} + +static int rewrite_section_headers(struct load_info *info) +{ + unsigned int i; + struct device *dev = info->dev; + + /* This should always be true, but let's be sure. */ + info->sechdrs[0].sh_addr = 0; + + for (i = 1; i < info->hdr->e_shnum; i++) { + struct elf32_shdr *shdr = &info->sechdrs[i]; + if (shdr->sh_type != SHT_NOBITS + && info->len < shdr->sh_offset + shdr->sh_size) { + dev_err(dev, "Module len %lu truncated\n", info->len); + return -ENOEXEC; + } + + /* Mark all sections sh_addr with their address in the + temporary image. */ + shdr->sh_addr = shdr->sh_offset; + + } + return 0; +} + + +static struct adsp_module *setup_load_info(struct load_info *info) +{ + unsigned int i; + int err; + struct adsp_module *mod; + struct device *dev = info->dev; + + /* Set up the convenience variables */ + info->sechdrs = (void *)info->hdr + info->hdr->e_shoff; + info->secstrings = (void *)info->hdr + + info->sechdrs[info->hdr->e_shstrndx].sh_offset; + + err = rewrite_section_headers(info); + if (err) + return ERR_PTR(err); + + /* Find internal symbols and strings. */ + for (i = 1; i < info->hdr->e_shnum; i++) { + if (info->sechdrs[i].sh_type == SHT_SYMTAB) { + info->index.sym = i; + info->index.str = info->sechdrs[i].sh_link; + info->strtab = (char *)info->hdr + + info->sechdrs[info->index.str].sh_offset; + break; + } + } + + /* This is temporary: point mod into copy of data. */ + mod = kzalloc(sizeof(struct adsp_module), GFP_KERNEL); + if (!mod) { + dev_err(dev, "Unable to create module\n"); + return ERR_PTR(-ENOMEM); + } + + if (info->index.sym == 0) { + dev_warn(dev, "%s: module has no symbols (stripped?)\n", + mod->name); + kfree(mod); + return ERR_PTR(-ENOEXEC); + } + + return mod; +} + + +static void layout_symtab(struct adsp_module *mod, struct load_info *info) +{ + struct elf32_shdr *symsect = info->sechdrs + info->index.sym; + struct elf32_shdr *strsect = info->sechdrs + info->index.str; + const struct elf32_sym *src; + unsigned int i, nsrc, ndst, strtab_size = 0; + struct device *dev = info->dev; + + /* Put symbol section at end of init part of module. */ + symsect->sh_flags |= SHF_ALLOC; + symsect->sh_entsize = get_offset(mod, &mod->size, symsect, + info->index.sym) | INIT_OFFSET_MASK; + dev_dbg(dev, "\t%s %d\n", info->secstrings + symsect->sh_name, + symsect->sh_entsize); + + src = (void *)info->hdr + symsect->sh_offset; + nsrc = symsect->sh_size / sizeof(*src); + + /* Compute total space required for the core symbols' strtab. */ + for (ndst = i = 0; i < nsrc; i++) { + if (i == 0 || + is_core_symbol(src+i, info->sechdrs, info->hdr->e_shnum)) { + strtab_size += strlen(&info->strtab[src[i].st_name])+1; + ndst++; + } + } + + /* Append room for core symbols at end of core part. */ + info->symoffs = ALIGN(mod->size, symsect->sh_addralign ?: 1); + info->stroffs = mod->size = info->symoffs + ndst * sizeof(Elf32_Sym); + mod->size += strtab_size; + + /* Put string table section at end of init part of module. */ + strsect->sh_flags |= SHF_ALLOC; + strsect->sh_entsize = get_offset(mod, &mod->size, strsect, + info->index.str) | INIT_OFFSET_MASK; + dev_dbg(dev, "\t%s %d\n", + info->secstrings + strsect->sh_name, + symsect->sh_entsize); +} + +static struct adsp_module *layout_and_allocate(struct load_info *info) +{ + /* Module within temporary copy. */ + struct adsp_module *mod; + int err; + + mod = setup_load_info(info); + if (IS_ERR(mod)) + return mod; + + mod->name = info->name; + + /* Determine total sizes, and put offsets in sh_entsize. For now + this is done generically; there doesn't appear to be any + special cases for the architectures. */ + layout_sections(mod, info); + layout_symtab(mod, info); + + /* Allocate and move to the final place */ + err = move_module(mod, info); + if (err) { + /* TODO: need to handle error path more genericly */ + kfree(mod); + return ERR_PTR(err); + } + + return mod; +} + +static int elf_check_arch_arm32(const struct elf32_hdr *x) +{ + unsigned int eflags; + + /* Make sure it's an ARM executable */ + if (x->e_machine != EM_ARM) + return 0; + + /* Make sure the entry address is reasonable */ + if (x->e_entry & 1) { + if (!(elf_hwcap & HWCAP_THUMB)) + return 0; + } else if (x->e_entry & 3) + return 0; + + eflags = x->e_flags; + if ((eflags & EF_ARM_EABI_MASK) == EF_ARM_EABI_UNKNOWN) { + unsigned int flt_fmt; + + /* APCS26 is only allowed if the CPU supports it */ + if ((eflags & EF_ARM_APCS_26) && !(elf_hwcap & HWCAP_26BIT)) + return 0; + + flt_fmt = eflags & (EF_ARM_VFP_FLOAT | EF_ARM_SOFT_FLOAT); + + /* VFP requires the supporting code */ + if (flt_fmt == EF_ARM_VFP_FLOAT && !(elf_hwcap & HWCAP_VFP)) + return 0; + } + return 1; +} + +static int elf_header_check(struct load_info *info) +{ + if (info->len < sizeof(*(info->hdr))) + return -ENOEXEC; + + if (memcmp(info->hdr->e_ident, ELFMAG, SELFMAG) != 0 + || info->hdr->e_type != ET_REL + || !elf_check_arch_arm32(info->hdr) + || info->hdr->e_shentsize != sizeof(Elf32_Shdr)) + return -ENOEXEC; + + if (info->hdr->e_shoff >= info->len + || (info->hdr->e_shnum * sizeof(Elf32_Shdr) > + info->len - info->hdr->e_shoff)) + return -ENOEXEC; + + return 0; +} + +struct adsp_module *load_adsp_static_module(const char *appname, + struct adsp_shared_app *shared_app, struct device *dev) +{ + struct adsp_module *mod = NULL; + + mod = kzalloc(sizeof(struct adsp_module), GFP_KERNEL); + if (!mod) + return NULL; + + memcpy((struct app_mem_size *)&mod->mem_size, + &shared_app->mem_size, sizeof(shared_app->mem_size)); + + mod->adsp_module_ptr = shared_app->mod_ptr; + mod->dynamic = false; + + return mod; +} + +struct adsp_module *load_adsp_dynamic_module(const char *appname, + const char *appfile, struct device *dev) +{ + struct load_info info = { }; + struct adsp_module *mod = NULL; + const struct firmware *fw; + struct elf32_shdr *data_shdr; + struct elf32_shdr *shared_shdr; + struct elf32_shdr *shared_wc_shdr; + struct elf32_shdr *aram_shdr; + struct elf32_shdr *aram_x_shdr; + struct app_mem_size *mem_size; + int ret; + + ret = request_firmware(&fw, appfile, dev); + if (ret < 0) { + dev_err(dev, + "request firmware for %s(%s) failed with %d\n", + appname, appfile, ret); + return ERR_PTR(ret); + } + + info.hdr = (struct elf32_hdr *)fw->data; + info.len = fw->size; + info.dev = dev; + info.name = appname; + + ret = elf_header_check(&info); + if (ret) { + dev_err(dev, + "%s is not an elf file\n", appfile); + goto error_release_fw; + } + + /* Figure out module layout, and allocate all the memory. */ + mod = layout_and_allocate(&info); + if (IS_ERR(mod)) + goto error_release_fw; + + /* update adsp specific sections */ + data_shdr = nvadsp_get_section(fw, ".dram_data"); + shared_shdr = nvadsp_get_section(fw, ".dram_shared"); + shared_wc_shdr = nvadsp_get_section(fw, ".dram_shared_wc"); + aram_shdr = nvadsp_get_section(fw, ".aram_data"); + aram_x_shdr = nvadsp_get_section(fw, ".aram_x_data"); + + mem_size = (void *)&mod->mem_size; + + if (data_shdr) { + dev_dbg(dev, "mem_size.dram_data %d\n", + data_shdr->sh_size); + mem_size->dram = data_shdr->sh_size; + } + + if (shared_shdr) { + dev_dbg(dev, "mem_size.dram_shared %d\n", + shared_shdr->sh_size); + mem_size->dram_shared = + shared_shdr->sh_size; + } + + if (shared_wc_shdr) { + dev_dbg(dev, "shared_wc_shdr->sh_size %d\n", + shared_wc_shdr->sh_size); + mem_size->dram_shared_wc = + shared_wc_shdr->sh_size; + } + + if (aram_shdr) { + dev_dbg(dev, "aram_shdr->sh_size %d\n", aram_shdr->sh_size); + mem_size->aram = aram_shdr->sh_size; + } + + if (aram_x_shdr) { + dev_dbg(dev, + "aram_x_shdr->sh_size %d\n", aram_x_shdr->sh_size); + mem_size->aram_x = aram_x_shdr->sh_size; + } + + /* Fix up syms, so that st_value is a pointer to location. */ + ret = simplify_symbols(mod, &info); + if (ret) { + dev_err(dev, "Unable to simplify symbols\n"); + goto unload_module; + } + + dev_dbg(dev, "applying relocation\n"); + ret = apply_relocations(mod, &info); + if (ret) { + dev_err(dev, "relocation failed\n"); + goto unload_module; + } + + mod->dynamic = true; + +error_release_fw: + release_firmware(fw); + return IS_ERR_VALUE(ret) ? ERR_PTR(ret) : mod; + +unload_module: + unload_adsp_module(mod); + release_firmware(fw); + return ERR_PTR(ret); +} + +void unload_adsp_module(struct adsp_module *mod) +{ + dram_app_mem_release(mod->handle); + kfree(mod); +} diff --git a/drivers/platform/tegra/nvadsp/aram_manager.c b/drivers/platform/tegra/nvadsp/aram_manager.c new file mode 100644 index 00000000..2740e8b2 --- /dev/null +++ b/drivers/platform/tegra/nvadsp/aram_manager.c @@ -0,0 +1,105 @@ +/* + * aram_managerc + * + * ARAM manager + * + * Copyright (C) 2014-2016, 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. + * + */ + +#define pr_fmt(fmt) "%s : %d, " fmt, __func__, __LINE__ + +#include + +#include "aram_manager.h" + +static void *aram_handle; + +static LIST_HEAD(aram_alloc_list); +static LIST_HEAD(aram_free_list); + +void aram_print(void) +{ + mem_print(aram_handle); +} +EXPORT_SYMBOL(aram_print); + +void *aram_request(const char *name, size_t size) +{ + return mem_request(aram_handle, name, size); +} +EXPORT_SYMBOL(aram_request); + +bool aram_release(void *handle) +{ + return mem_release(aram_handle, handle); +} +EXPORT_SYMBOL(aram_release); + +unsigned long aram_get_address(void *handle) +{ + return mem_get_address(handle); +} +EXPORT_SYMBOL(aram_get_address); + +#ifdef CONFIG_DEBUG_FS +static struct dentry *aram_dump_debugfs_file; + +static int aram_dump(struct seq_file *s, void *data) +{ + mem_dump(aram_handle, s); + return 0; +} + +static int aram_dump_open(struct inode *inode, struct file *file) +{ + return single_open(file, aram_dump, inode->i_private); +} + +static const struct file_operations aram_dump_fops = { + .open = aram_dump_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; +#endif + +int aram_init(unsigned long addr, unsigned long size) +{ + aram_handle = create_mem_manager("ARAM", addr, size); + if (IS_ERR(aram_handle)) { + pr_err("ERROR: failed to create aram memory_manager"); + return PTR_ERR(aram_handle); + } + +#ifdef CONFIG_DEBUG_FS + aram_dump_debugfs_file = debugfs_create_file("aram_dump", + S_IRUSR, NULL, NULL, &aram_dump_fops); + if (!aram_dump_debugfs_file) { + pr_err("ERROR: failed to create aram_dump debugfs"); + destroy_mem_manager(aram_handle); + return -ENOMEM; + } +#endif + return 0; +} +EXPORT_SYMBOL(aram_init); + +void aram_exit(void) +{ +#ifdef CONFIG_DEBUG_FS + debugfs_remove(aram_dump_debugfs_file); +#endif + destroy_mem_manager(aram_handle); +} +EXPORT_SYMBOL(aram_exit); + diff --git a/drivers/platform/tegra/nvadsp/aram_manager.h b/drivers/platform/tegra/nvadsp/aram_manager.h new file mode 100644 index 00000000..786c304b --- /dev/null +++ b/drivers/platform/tegra/nvadsp/aram_manager.h @@ -0,0 +1,30 @@ +/* + * Header file for aram manager + * + * Copyright (c) 2014-2015, NVIDIA CORPORATION. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope 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. + */ + +#ifndef __TEGRA_NVADSP_ARAM_MANAGER_H +#define __TEGRA_NVADSP_ARAM_MANAGER_H + +#include "mem_manager.h" + +int aram_init(unsigned long addr, unsigned long size); +void aram_exit(void); + +void *aram_request(const char *name, size_t size); +bool aram_release(void *handle); + +unsigned long aram_get_address(void *handle); +void aram_print(void); + +#endif /* __TEGRA_NVADSP_ARAM_MANAGER_H */ diff --git a/drivers/platform/tegra/nvadsp/dev-t21x.c b/drivers/platform/tegra/nvadsp/dev-t21x.c new file mode 100644 index 00000000..17cbdfab --- /dev/null +++ b/drivers/platform/tegra/nvadsp/dev-t21x.c @@ -0,0 +1,319 @@ +/* + * dev-t21x.c + * + * A device driver for ADSP and APE + * + * Copyright (C) 2014-2016, 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 +#include +#include +#include +#include + +#include "dev.h" +#include "amc.h" + +#ifdef CONFIG_PM +static void nvadsp_clocks_disable(struct platform_device *pdev) +{ + struct nvadsp_drv_data *drv_data = platform_get_drvdata(pdev); + struct device *dev = &pdev->dev; + + if (drv_data->uartape_clk) { + clk_disable_unprepare(drv_data->uartape_clk); + dev_dbg(dev, "uartape clock disabled\n"); + drv_data->uartape_clk = NULL; + } + + if (drv_data->adsp_cpu_clk) { + clk_disable_unprepare(drv_data->adsp_cpu_clk); + dev_dbg(dev, "adsp_cpu clock disabled\n"); + drv_data->adsp_cpu_clk = NULL; + } + + if (drv_data->adsp_clk) { + clk_disable_unprepare(drv_data->adsp_clk); + dev_dbg(dev, "adsp clocks disabled\n"); + drv_data->adsp_clk = NULL; + } + + if (drv_data->ape_clk) { + clk_disable_unprepare(drv_data->ape_clk); + dev_dbg(dev, "ape clock disabled\n"); + drv_data->ape_clk = NULL; + } + + if (drv_data->ape_emc_clk) { + clk_disable_unprepare(drv_data->ape_emc_clk); + dev_dbg(dev, "ape.emc clock disabled\n"); + drv_data->ape_emc_clk = NULL; + } + + + if (drv_data->ahub_clk) { + clk_disable_unprepare(drv_data->ahub_clk); + dev_dbg(dev, "ahub clock disabled\n"); + drv_data->ahub_clk = NULL; + } +} + +static int nvadsp_clocks_enable(struct platform_device *pdev) +{ + struct nvadsp_drv_data *drv_data = platform_get_drvdata(pdev); + struct device *dev = &pdev->dev; + uint32_t val; + int ret = 0; + + drv_data->ahub_clk = clk_get_sys("nvadsp", "ahub"); + if (IS_ERR_OR_NULL(drv_data->ahub_clk)) { + dev_err(dev, "unable to find ahub clock\n"); + ret = PTR_ERR(drv_data->ahub_clk); + goto end; + } + ret = clk_prepare_enable(drv_data->ahub_clk); + if (ret) { + dev_err(dev, "unable to enable ahub clock\n"); + goto end; + } + dev_dbg(dev, "ahub clock enabled\n"); + + drv_data->ape_clk = clk_get_sys(NULL, "adsp.ape"); + if (IS_ERR_OR_NULL(drv_data->ape_clk)) { + dev_err(dev, "unable to find ape clock\n"); + ret = PTR_ERR(drv_data->ape_clk); + goto end; + } + ret = clk_prepare_enable(drv_data->ape_clk); + if (ret) { + dev_err(dev, "unable to enable ape clock\n"); + goto end; + } + dev_dbg(dev, "ape clock enabled\n"); + + drv_data->adsp_clk = clk_get_sys(NULL, "adsp"); + if (IS_ERR_OR_NULL(drv_data->adsp_clk)) { + dev_err(dev, "unable to find adsp clock\n"); + ret = PTR_ERR(drv_data->adsp_clk); + goto end; + } + ret = clk_prepare_enable(drv_data->adsp_clk); + if (ret) { + dev_err(dev, "unable to enable adsp clock\n"); + goto end; + } + + drv_data->adsp_cpu_clk = clk_get_sys(NULL, "adsp_cpu"); + if (IS_ERR_OR_NULL(drv_data->adsp_cpu_clk)) { + dev_err(dev, "unable to find adsp cpu clock\n"); + ret = PTR_ERR(drv_data->adsp_cpu_clk); + goto end; + } + ret = clk_prepare_enable(drv_data->adsp_cpu_clk); + if (ret) { + dev_err(dev, "unable to enable adsp cpu clock\n"); + goto end; + } + dev_dbg(dev, "adsp cpu clock enabled\n"); + + drv_data->ape_emc_clk = clk_get_sys("ape", "emc"); + if (IS_ERR_OR_NULL(drv_data->ape_emc_clk)) { + dev_err(dev, "unable to find ape.emc clock\n"); + ret = PTR_ERR(drv_data->ape_emc_clk); + goto end; + } + + ret = clk_prepare_enable(drv_data->ape_emc_clk); + if (ret) { + dev_err(dev, "unable to enable ape.emc clock\n"); + goto end; + } + dev_dbg(dev, "ape.emc is enabled\n"); + + drv_data->uartape_clk = clk_get_sys("uartape", NULL); + if (IS_ERR_OR_NULL(drv_data->uartape_clk)) { + dev_err(dev, "unable to find uart ape clk\n"); + ret = PTR_ERR(drv_data->uartape_clk); + goto end; + } + ret = clk_prepare_enable(drv_data->uartape_clk); + if (ret) { + dev_err(dev, "unable to enable uartape clock\n"); + goto end; + } + clk_set_rate(drv_data->uartape_clk, UART_BAUD_RATE * 16); + dev_dbg(dev, "uartape clock enabled\n"); + + /* Set MAXCLKLATENCY value before ADSP deasserting reset */ + val = readl(drv_data->base_regs[AMISC] + ADSP_CONFIG); + writel(val | MAXCLKLATENCY, drv_data->base_regs[AMISC] + ADSP_CONFIG); + dev_dbg(dev, "all clocks enabled\n"); + return 0; + end: + nvadsp_clocks_disable(pdev); + return ret; +} + +static inline bool nvadsp_amsic_skip_reg(u32 offset) +{ + if (offset == AMISC_ADSP_L2_REGFILEBASE || + offset == AMISC_SHRD_SMP_STA || + (offset >= AMISC_SEM_REG_START && offset <= AMISC_SEM_REG_END) || + offset == AMISC_TSC || + offset == AMISC_ACTMON_AVG_CNT) { + return true; + } else { + return false; + } +} + +static int nvadsp_amisc_save(struct platform_device *pdev) +{ + struct nvadsp_drv_data *d = platform_get_drvdata(pdev); + u32 val, offset; + int i = 0; + + offset = AMISC_REG_START_OFFSET; + while (offset <= AMISC_REG_MBOX_OFFSET) { + if (nvadsp_amsic_skip_reg(offset)) { + offset += 4; + continue; + } + val = readl(d->base_regs[AMISC] + offset); + d->state.amisc_regs[i++] = val; + offset += 4; + } + + offset = ADSP_ACTMON_REG_START_OFFSET; + while (offset <= ADSP_ACTMON_REG_END_OFFSET) { + if (nvadsp_amsic_skip_reg(offset)) { + offset += 4; + continue; + } + val = readl(d->base_regs[AMISC] + offset); + d->state.amisc_regs[i++] = val; + offset += 4; + } + return 0; +} + +static int nvadsp_amisc_restore(struct platform_device *pdev) +{ + struct nvadsp_drv_data *d = platform_get_drvdata(pdev); + u32 val, offset; + int i = 0; + + offset = AMISC_REG_START_OFFSET; + while (offset <= AMISC_REG_MBOX_OFFSET) { + if (nvadsp_amsic_skip_reg(offset)) { + offset += 4; + continue; + } + val = d->state.amisc_regs[i++]; + writel(val, d->base_regs[AMISC] + offset); + offset += 4; + } + + offset = ADSP_ACTMON_REG_START_OFFSET; + while (offset <= ADSP_ACTMON_REG_END_OFFSET) { + if (nvadsp_amsic_skip_reg(offset)) { + offset += 4; + continue; + } + val = d->state.amisc_regs[i++]; + writel(val, d->base_regs[AMISC] + offset); + offset += 4; + } + return 0; +} + +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 = 0; + + dev_dbg(dev, "restoring adsp base regs\n"); + drv_data->base_regs = drv_data->base_regs_saved; + + dev_dbg(dev, "enabling clocks\n"); + ret = nvadsp_clocks_enable(pdev); + if (ret) { + dev_err(dev, "nvadsp_clocks_enable failed\n"); + goto skip; + } + + if (!drv_data->adsp_os_suspended) { + dev_dbg(dev, "%s: adsp os is not suspended\n", __func__); + goto skip; + } + + dev_dbg(dev, "restoring ape state\n"); + nvadsp_amc_restore(pdev); + nvadsp_aram_restore(pdev); + nvadsp_amisc_restore(pdev); + skip: + 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 = 0; + + if (!drv_data->adsp_os_suspended) { + dev_dbg(dev, "%s: adsp os is not suspended\n", __func__); + goto clocks; + } + + dev_dbg(dev, "saving amsic\n"); + nvadsp_amisc_save(pdev); + + dev_dbg(dev, "saving aram\n"); + nvadsp_aram_save(pdev); + + dev_dbg(dev, "saving amc\n"); + nvadsp_amc_save(pdev); + clocks: + dev_dbg(dev, "disabling clocks\n"); + nvadsp_clocks_disable(pdev); + + dev_dbg(dev, "locking out adsp base regs\n"); + drv_data->base_regs = NULL; + + return ret; +} + +static int __nvadsp_runtime_idle(struct device *dev) +{ + return 0; +} + +int __init nvadsp_pm_init(struct platform_device *pdev) +{ + struct nvadsp_drv_data *drv_data = platform_get_drvdata(pdev); + + drv_data->runtime_suspend = __nvadsp_runtime_suspend; + drv_data->runtime_resume = __nvadsp_runtime_resume; + drv_data->runtime_idle = __nvadsp_runtime_idle; + + return 0; +} +#endif /* CONFIG_PM */ + +int __init nvadsp_reset_init(struct platform_device *pdev) +{ + return 0; +} diff --git a/drivers/platform/tegra/nvadsp/dev-t21x.h b/drivers/platform/tegra/nvadsp/dev-t21x.h new file mode 100644 index 00000000..27bc7e81 --- /dev/null +++ b/drivers/platform/tegra/nvadsp/dev-t21x.h @@ -0,0 +1,52 @@ +/* + * Copyright (C) 2015-2016, 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. + * + */ + +#ifndef __TEGRA_NVADSP_DEV_T21X_H +#define __TEGRA_NVADSP_DEV_T21X_H + +/* + * Note: These enums should be aligned to the regs mentioned in the + * device tree +*/ +enum { + AMC, + AMISC, + ABRIDGE, + UNIT_FPGA_RST, + APE_MAX_REG +}; + +enum { + ADSP_DRAM1, + ADSP_DRAM2, + ADSP_MAX_DRAM_MAP +}; + +/* + * Note: These enums should be aligned to the adsp_mem node mentioned in the + * device tree +*/ +enum adsp_mem_dt { + ADSP_OS_ADDR, + ADSP_OS_SIZE, + ADSP_APP_ADDR, + ADSP_APP_SIZE, + ARAM_ALIAS_0_ADDR, + ARAM_ALIAS_0_SIZE, + ACSR_ADDR, /* ACSR: ADSP CPU SHARED REGION */ + ACSR_SIZE, + ADSP_MEM_END, +}; + +#endif /* __TEGRA_NVADSP_DEV_T21X_H */ diff --git a/drivers/platform/tegra/nvadsp/dev.c b/drivers/platform/tegra/nvadsp/dev.c new file mode 100644 index 00000000..f1756870 --- /dev/null +++ b/drivers/platform/tegra/nvadsp/dev.c @@ -0,0 +1,414 @@ +/* + * dev.c + * + * A device driver for ADSP and APE + * + * Copyright (C) 2014-2016, 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "dev.h" +#include "os.h" +#include "amc.h" +#include "ape_actmon.h" +#include "aram_manager.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; + } + } + + 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); + + 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; + + 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; + adsp_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_amc_init(pdev); + if (ret) + goto err; + + ret = nvadsp_hwmbox_init(pdev); + if (ret) + goto err; + + ret = nvadsp_mbox_init(pdev); + if (ret) + goto err; + +#ifdef CONFIG_TEGRA_EMC_APE_DFS + ret = emc_dfs_init(pdev); + if (ret) + goto err; +#endif + +#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 = aram_init(aram_addr, aram_size); + if (ret) + dev_err(dev, "Failed to init aram\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) +{ +#ifdef CONFIG_TEGRA_EMC_APE_DFS + emc_dfs_exit(); +#endif + 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 const struct of_device_id nvadsp_of_match[] = { + { .compatible = "nvidia,tegra210-adsp", .data = NULL, }, + { .compatible = "nvidia,tegra18x-adsp", .data = NULL, }, + { .compatible = "nvidia,tegra18x-adsp-hv", .data = NULL, }, + {}, +}; +#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"); diff --git a/drivers/platform/tegra/nvadsp/dev.h b/drivers/platform/tegra/nvadsp/dev.h new file mode 100644 index 00000000..df1c4073 --- /dev/null +++ b/drivers/platform/tegra/nvadsp/dev.h @@ -0,0 +1,183 @@ +/* + * dev.h + * + * A header file for Host driver for ADSP and APE + * + * Copyright (C) 2014-2016, 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. + * + */ + +#ifndef __TEGRA_NVADSP_DEV_H +#define __TEGRA_NVADSP_DEV_H + +#include +#include +#include +#include + +#include + +#if defined(CONFIG_ARCH_TEGRA_21x_SOC) +#include "dev-t21x.h" +#else +#include "dev-t18x.h" +#endif /* CONFIG_ARCH_TEGRA_21x_SOC */ + +#include "hwmailbox.h" +#include "amc.h" +#include "os.h" + +enum adsp_unit_fpga_reset { + ADSP_ASSERT, + ADSP_DEASSERT, + ADSP_UNIT_FPGA_RESET_END, +}; + + +#define AMISC_REGS 0x2000 + +#define AMISC_ADSP_L2_REGFILEBASE 0x10 +#define AMISC_SHRD_SMP_STA 0x14 +#define AMISC_SEM_REG_START 0x1c +#define AMISC_SEM_REG_END 0x44 +#define AMISC_TSC 0x48 +#define AMISC_ACTMON_AVG_CNT 0x81c + +#define AMISC_REG_START_OFFSET 0x0 +#define AMISC_REG_MBOX_OFFSET 0x64 +#define ADSP_ACTMON_REG_START_OFFSET 0x800 +#define ADSP_ACTMON_REG_END_OFFSET 0x828 + +enum nvadsp_virqs { + MBOX_SEND_VIRQ, + MBOX_RECV_VIRQ, + WDT_VIRQ, + WFI_VIRQ, + AMC_ERR_VIRQ, + ACTMON_VIRQ, + NVADSP_VIRQ_MAX, +}; + +struct nvadsp_pm_state { + u32 aram[AMC_ARAM_WSIZE]; + uint32_t amc_regs[AMC_REGS]; + uint32_t amisc_regs[AMISC_REGS]; + u32 evp[AMC_EVP_WSIZE]; + void *evp_ptr; +}; + +struct nvadsp_drv_data { + void __iomem **base_regs; + void __iomem **base_regs_saved; + struct platform_device *pdev; + struct resource *dram_region[ADSP_MAX_DRAM_MAP]; + struct hwmbox_queue hwmbox_send_queue; + int hwmbox_send_virq; + int hwmbox_recv_virq; + + struct nvadsp_mbox **mboxes; + unsigned long *mbox_ids; + spinlock_t mbox_lock; + +#ifdef CONFIG_DEBUG_FS + struct dentry *adsp_debugfs_root; +#endif + struct clk *ape_clk; + struct clk *apb2ape_clk; + struct clk *adsp_clk; + struct clk *adsp_cpu_clk; + struct clk *adsp_neon_clk; + struct clk *ape_emc_clk; + struct clk *uartape_clk; + struct clk *ahub_clk; + unsigned long adsp_freq; /* in KHz*/ + unsigned long adsp_freq_hz; /* in Hz*/ + unsigned long ape_freq; /* in KHz*/ + unsigned long ape_emc_freq; /* in KHz*/ + + int (*runtime_suspend)(struct device *dev); + int (*runtime_resume)(struct device *dev); + int (*runtime_idle)(struct device *dev); + int (*assert_adsp)(struct nvadsp_drv_data *drv_data); + int (*deassert_adsp)(struct nvadsp_drv_data *drv_data); + struct reset_control *adspall_rst; + + struct nvadsp_pm_state state; + bool adsp_os_running; + bool adsp_os_suspended; + bool adsp_os_secload; + + void *shared_adsp_os_data; + +#ifdef CONFIG_TEGRA_ADSP_DFS + bool dfs_initialized; +#endif + +#ifdef CONFIG_TEGRA_ADSP_ACTMON + bool actmon_initialized; +#endif + +#ifdef CONFIG_TEGRA_ADSP_CPUSTAT + bool cpustat_initialized; +#endif + +#if defined(CONFIG_TEGRA_ADSP_FILEIO) + bool adspff_init; +#endif + + u32 adsp_mem[ADSP_MEM_END]; + bool adsp_unit_fpga; + u32 unit_fpga_reset[ADSP_UNIT_FPGA_RESET_END]; + int agic_irqs[NVADSP_VIRQ_MAX]; + + struct tegra_bwmgr_client *bwmgr; +}; + +#define ADSP_CONFIG 0x04 +#define MAXCLKLATENCY (3 << 29) +#define UART_BAUD_RATE 9600 + +status_t nvadsp_mbox_init(struct platform_device *pdev); +status_t nvadsp_amc_init(struct platform_device *pdev); + +#ifdef CONFIG_TEGRA_ADSP_DFS +void adsp_cpu_set_rate(unsigned long freq); +int adsp_dfs_core_init(struct platform_device *pdev); +int adsp_dfs_core_exit(struct platform_device *pdev); +u32 adsp_to_emc_freq(u32 adspfreq); +#endif + +#ifdef CONFIG_TEGRA_ADSP_ACTMON +int ape_actmon_probe(struct platform_device *pdev); +#endif + +#ifdef CONFIG_TEGRA_ADSP_CPUSTAT +int adsp_cpustat_init(struct platform_device *pdev); +int adsp_cpustat_exit(struct platform_device *pdev); +#endif + +#if defined(CONFIG_TEGRA_ADSP_FILEIO) +int adspff_init(void); +void adspff_exit(void); +#endif + +#ifdef CONFIG_TEGRA_EMC_APE_DFS +status_t emc_dfs_init(struct platform_device *pdev); +void emc_dfs_exit(void); +#endif + +#ifdef CONFIG_PM +int __init nvadsp_pm_init(struct platform_device *pdev); +#endif +int __init nvadsp_reset_init(struct platform_device *pdev); + +#endif /* __TEGRA_NVADSP_DEV_H */ diff --git a/drivers/platform/tegra/nvadsp/dram_app_mem_manager.c b/drivers/platform/tegra/nvadsp/dram_app_mem_manager.c new file mode 100644 index 00000000..b11049e3 --- /dev/null +++ b/drivers/platform/tegra/nvadsp/dram_app_mem_manager.c @@ -0,0 +1,110 @@ +/* + * dram_app_mem_manager.c + * + * dram app memory manager for allocating memory for text,bss and data + * + * Copyright (C) 2014-2016, 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. + * + */ + +#define pr_fmt(fmt) "%s : %d, " fmt, __func__, __LINE__ + +#include +#include + +#include "dram_app_mem_manager.h" + +#define ALIGN_TO_ADSP_PAGE(x) ALIGN(x, 4096) + +static void *dram_app_mem_handle; + +static LIST_HEAD(dram_app_mem_alloc_list); +static LIST_HEAD(dram_app_mem_free_list); + +void dram_app_mem_print(void) +{ + mem_print(dram_app_mem_handle); +} +EXPORT_SYMBOL(dram_app_mem_print); + +void *dram_app_mem_request(const char *name, size_t size) +{ + return mem_request(dram_app_mem_handle, name, ALIGN_TO_ADSP_PAGE(size)); +} +EXPORT_SYMBOL(dram_app_mem_request); + +bool dram_app_mem_release(void *handle) +{ + return mem_release(dram_app_mem_handle, handle); +} +EXPORT_SYMBOL(dram_app_mem_release); + +unsigned long dram_app_mem_get_address(void *handle) +{ + return mem_get_address(handle); +} +EXPORT_SYMBOL(dram_app_mem_get_address); + +#ifdef CONFIG_DEBUG_FS +static struct dentry *dram_app_mem_dump_debugfs_file; + +static int dram_app_mem_dump(struct seq_file *s, void *data) +{ + mem_dump(dram_app_mem_handle, s); + return 0; +} + +static int dram_app_mem_dump_open(struct inode *inode, struct file *file) +{ + return single_open(file, dram_app_mem_dump, inode->i_private); +} + +static const struct file_operations dram_app_mem_dump_fops = { + .open = dram_app_mem_dump_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; +#endif + +int dram_app_mem_init(unsigned long start, unsigned long size) +{ + dram_app_mem_handle = + create_mem_manager("DRAM_APP_MANAGER", start, size); + if (IS_ERR(dram_app_mem_handle)) { + pr_err("ERROR: failed to create aram memory_manager"); + return PTR_ERR(dram_app_mem_handle); + } + +#ifdef CONFIG_DEBUG_FS + dram_app_mem_dump_debugfs_file = + debugfs_create_file("dram_app_mem_dump", + S_IRUSR, NULL, NULL, &dram_app_mem_dump_fops); + if (!dram_app_mem_dump_debugfs_file) { + pr_err("ERROR: failed to create dram_app_mem_dump debugfs"); + destroy_mem_manager(dram_app_mem_handle); + return -ENOMEM; + } +#endif + return 0; +} +EXPORT_SYMBOL(dram_app_mem_init); + +void dram_app_mem_exit(void) +{ +#ifdef CONFIG_DEBUG_FS + debugfs_remove(dram_app_mem_dump_debugfs_file); +#endif + destroy_mem_manager(dram_app_mem_handle); +} +EXPORT_SYMBOL(dram_app_mem_exit); + diff --git a/drivers/platform/tegra/nvadsp/dram_app_mem_manager.h b/drivers/platform/tegra/nvadsp/dram_app_mem_manager.h new file mode 100644 index 00000000..7f2ca78e --- /dev/null +++ b/drivers/platform/tegra/nvadsp/dram_app_mem_manager.h @@ -0,0 +1,30 @@ +/* + * Header file for dram app memory manager + * + * Copyright (c) 2014-2015, NVIDIA CORPORATION. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope 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. + */ + +#ifndef __TEGRA_NVADSP_DRAM_APP_MEM_MANAGER_H +#define __TEGRA_NVADSP_DRAM_APP_MEM_MANAGER_H + +#include "mem_manager.h" + +int dram_app_mem_init(unsigned long, unsigned long); +void dram_app_mem_exit(void); + +void *dram_app_mem_request(const char *name, size_t size); +bool dram_app_mem_release(void *handle); + +unsigned long dram_app_mem_get_address(void *handle); +void dram_app_mem_print(void); + +#endif /* __TEGRA_NVADSP_DRAM_APP_MEM_MANAGER_H */ diff --git a/drivers/platform/tegra/nvadsp/emc_dfs.c b/drivers/platform/tegra/nvadsp/emc_dfs.c new file mode 100644 index 00000000..0195497c --- /dev/null +++ b/drivers/platform/tegra/nvadsp/emc_dfs.c @@ -0,0 +1,465 @@ +/* + * emc_dfs.c + * + * Emc dynamic frequency scaling due to APE + * + * Copyright (C) 2014-2016, 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 +#include +#include +#include +#include +#include +#include +#include +#include + +#include "dev.h" + +/* Register offsets */ +#define ABRIDGE_STATS_READ_0 0x04 +#define ABRIDGE_STATS_WRITE_0 0x0c +#define ABRIDGE_STATS_CLEAR_0 0x1b +#define ABRIDGE_STATS_HI_0FFSET 0x04 + +/* Sample Period in usecs */ +#define DEFAULT_SAMPLE_PERIOD 500000 +#define INT_SHIFT 32 +#define make64(hi, low) ((((u64)hi) << INT_SHIFT) | (low)) +#define SCALING_DIVIDER 2 +#define BOOST_DOWN_COUNT 2 +#define DEFAULT_BOOST_UP_THRESHOLD 2000000; +#define DEFAULT_BOOST_STEP 2 + +struct emc_dfs_info { + void __iomem *abridge_base; + struct timer_list cnt_timer; + + u64 rd_cnt; + u64 wr_cnt; + bool enable; + u64 avg_cnt; + + unsigned long timer_rate; + ktime_t prev_time; + + u32 dn_count; + u32 boost_dn_count; + + u64 boost_up_threshold; + u8 boost_step; + + struct work_struct clk_set_work; + unsigned long cur_freq; + bool speed_change_flag; + unsigned long max_freq; + + struct clk *emcclk; +}; + +static struct emc_dfs_info global_emc_info; +static struct emc_dfs_info *einfo; +static struct task_struct *speedchange_task; +static spinlock_t speedchange_lock; + +static u64 read64(u32 offset) +{ + u32 low; + u32 hi; + + low = readl(einfo->abridge_base + offset); + hi = readl(einfo->abridge_base + (offset + ABRIDGE_STATS_HI_0FFSET)); + return make64(hi, low); +} +static unsigned long count_to_emcfreq(void) +{ + unsigned long tfreq = 0; + + if (!einfo->avg_cnt) { + if (einfo->dn_count >= einfo->boost_dn_count) { + tfreq = einfo->cur_freq / SCALING_DIVIDER; + einfo->dn_count = 0; + } else + einfo->dn_count++; + } else if (einfo->avg_cnt >= einfo->boost_up_threshold) { + if (einfo->boost_step) + tfreq = einfo->cur_freq * einfo->boost_step; + } + + pr_debug("%s:avg_cnt: %llu current freq(kHz): %lu target freq(kHz): %lu\n", + __func__, einfo->avg_cnt, einfo->cur_freq, tfreq); + + return tfreq; +} + +static int clk_work(void *data) +{ + int ret; + + if (einfo->emcclk && einfo->speed_change_flag && einfo->cur_freq) { + ret = clk_set_rate(einfo->emcclk, einfo->cur_freq * 1000); + if (ret) { + pr_err("failed to set ape.emc freq:%d\n", ret); + BUG_ON(ret); + } + einfo->cur_freq = clk_get_rate(einfo->emcclk) / 1000; + pr_info("ape.emc: setting emc clk: %lu\n", einfo->cur_freq); + } + + mod_timer(&einfo->cnt_timer, + jiffies + usecs_to_jiffies(einfo->timer_rate)); + return 0; +} +static void emc_dfs_timer(unsigned long data) +{ + u64 cur_cnt; + u64 delta_cnt; + u64 prev_cnt; + u64 delta_time; + ktime_t now; + unsigned long target_freq; + unsigned long flags; + + spin_lock_irqsave(&speedchange_lock, flags); + + /* Return if emc dfs is disabled */ + if (!einfo->enable) { + spin_unlock_irqrestore(&speedchange_lock, flags); + return; + } + + prev_cnt = einfo->rd_cnt + einfo->wr_cnt; + + einfo->rd_cnt = read64((u32)ABRIDGE_STATS_READ_0); + einfo->wr_cnt = read64((u32)ABRIDGE_STATS_WRITE_0); + pr_debug("einfo->rd_cnt: %llu einfo->wr_cnt: %llu\n", + einfo->rd_cnt, einfo->wr_cnt); + + cur_cnt = einfo->rd_cnt + einfo->wr_cnt; + delta_cnt = cur_cnt - prev_cnt; + + now = ktime_get(); + + delta_time = ktime_to_ns(ktime_sub(now, einfo->prev_time)); + if (!delta_time) { + pr_err("%s: time interval to calculate emc scaling is zero\n", + __func__); + spin_unlock_irqrestore(&speedchange_lock, flags); + goto exit; + } + + einfo->prev_time = now; + einfo->avg_cnt = delta_cnt / delta_time; + + /* if 0: no scaling is required */ + target_freq = count_to_emcfreq(); + if (!target_freq) { + einfo->speed_change_flag = false; + } else { + einfo->cur_freq = target_freq; + einfo->speed_change_flag = true; + } + + spin_unlock_irqrestore(&speedchange_lock, flags); + pr_info("einfo->avg_cnt: %llu delta_cnt: %llu delta_time %llu emc_freq:%lu\n", + einfo->avg_cnt, delta_cnt, delta_time, einfo->cur_freq); + +exit: + wake_up_process(speedchange_task); +} + +static void emc_dfs_enable(void) +{ + einfo->rd_cnt = read64((u32)ABRIDGE_STATS_READ_0); + einfo->wr_cnt = read64((u32)ABRIDGE_STATS_WRITE_0); + + einfo->prev_time = ktime_get(); + mod_timer(&einfo->cnt_timer, jiffies + 2); +} +static void emc_dfs_disable(void) +{ + einfo->rd_cnt = read64((u32)ABRIDGE_STATS_READ_0); + einfo->wr_cnt = read64((u32)ABRIDGE_STATS_WRITE_0); + + del_timer_sync(&einfo->cnt_timer); +} + +#ifdef CONFIG_DEBUG_FS +static struct dentry *emc_dfs_root; + +#define RW_MODE (S_IWUSR | S_IRUSR) +#define RO_MODE S_IRUSR + +/* Get emc dfs staus: 0: disabled 1:enabled */ +static int dfs_enable_get(void *data, u64 *val) +{ + *val = einfo->enable; + return 0; +} +/* Enable/disable emc dfs */ +static int dfs_enable_set(void *data, u64 val) +{ + einfo->enable = (bool) val; + /* + * If enabling: activate a timer to execute in next 2 jiffies, + * so that emc scaled value takes effect immidiately. + */ + if (einfo->enable) + emc_dfs_enable(); + else + emc_dfs_disable(); + + return 0; +} + +DEFINE_SIMPLE_ATTRIBUTE(enable_fops, dfs_enable_get, + dfs_enable_set, "%llu\n"); + +/* Get emc dfs staus: 0: disabled 1:enabled */ +static int boost_up_threshold_get(void *data, u64 *val) +{ + *val = einfo->boost_up_threshold; + return 0; +} +/* Enable/disable emc dfs */ +static int boost_up_threshold_set(void *data, u64 val) +{ + unsigned long flags; + int ret = 0; + + spin_lock_irqsave(&speedchange_lock, flags); + + if (!einfo->enable) { + pr_info("EMC dfs is not enabled\n"); + ret = -EINVAL; + goto err; + } + + if (val) + einfo->boost_up_threshold = val; + +err: + spin_unlock_irqrestore(&speedchange_lock, flags); + return ret; +} + +DEFINE_SIMPLE_ATTRIBUTE(up_threshold_fops, + boost_up_threshold_get, boost_up_threshold_set, "%llu\n"); + +/* scaling emc freq in multiple of boost factor */ +static int boost_step_get(void *data, u64 *val) +{ + *val = einfo->boost_step; + return 0; +} +/* Set period in usec */ +static int boost_step_set(void *data, u64 val) +{ + unsigned long flags; + int ret = 0; + + spin_lock_irqsave(&speedchange_lock, flags); + + if (!einfo->enable) { + pr_info("EMC dfs is not enabled\n"); + ret = -EINVAL; + goto err; + } + + if (!val) + einfo->boost_step = 1; + else + einfo->boost_step = (u8) val; +err: + spin_unlock_irqrestore(&speedchange_lock, flags); + return ret; +} +DEFINE_SIMPLE_ATTRIBUTE(boost_fops, boost_step_get, + boost_step_set, "%llu\n"); + +/* minimum time after that emc scaling down happens in usec */ +static int boost_down_count_get(void *data, u64 *val) +{ + *val = einfo->boost_dn_count; + return 0; +} +/* Set period in usec */ +static int boost_down_count_set(void *data, u64 val) +{ + unsigned long flags; + int ret = 0; + + spin_lock_irqsave(&speedchange_lock, flags); + + if (!einfo->enable) { + pr_info("EMC dfs is not enabled\n"); + ret = -EINVAL; + goto err; + } + + if (val) + einfo->boost_dn_count = (u32) val; + ret = 0; +err: + spin_unlock_irqrestore(&speedchange_lock, flags); + return ret; +} +DEFINE_SIMPLE_ATTRIBUTE(down_cnt_fops, boost_down_count_get, + boost_down_count_set, "%llu\n"); + +static int period_get(void *data, u64 *val) +{ + *val = einfo->timer_rate; + return 0; +} + +/* Set period in usec */ +static int period_set(void *data, u64 val) +{ + unsigned long flags; + int ret = 0; + + spin_lock_irqsave(&speedchange_lock, flags); + + if (!einfo->enable) { + pr_info("EMC dfs is not enabled\n"); + ret = -EINVAL; + goto err; + } + + if (val) + einfo->timer_rate = (unsigned long)val; + +err: + spin_unlock_irqrestore(&speedchange_lock, flags); + return ret; +} +DEFINE_SIMPLE_ATTRIBUTE(period_fops, period_get, period_set, "%llu\n"); + + +static int emc_dfs_debugfs_init(struct nvadsp_drv_data *drv) +{ + int ret = -ENOMEM; + struct dentry *d; + + if (!drv->adsp_debugfs_root) + return ret; + + emc_dfs_root = debugfs_create_dir("emc_dfs", drv->adsp_debugfs_root); + if (!emc_dfs_root) + goto err_out; + + d = debugfs_create_file("enable", RW_MODE, emc_dfs_root, NULL, + &enable_fops); + if (!d) + goto err_root; + + d = debugfs_create_file("boost_up_threshold", RW_MODE, emc_dfs_root, + NULL, &up_threshold_fops); + if (!d) + goto err_root; + + d = debugfs_create_file("boost_step", RW_MODE, emc_dfs_root, NULL, + &boost_fops); + if (!d) + goto err_root; + + d = debugfs_create_file("boost_down_count", RW_MODE, emc_dfs_root, + NULL, &down_cnt_fops); + if (!d) + goto err_root; + + d = debugfs_create_file("period", RW_MODE, emc_dfs_root, NULL, + &period_fops); + if (!d) + goto err_root; + + return 0; + +err_root: + debugfs_remove_recursive(emc_dfs_root); + +err_out: + return ret; +} + +#endif + +status_t __init emc_dfs_init(struct platform_device *pdev) +{ + struct nvadsp_drv_data *drv = platform_get_drvdata(pdev); + struct sched_param param = { .sched_priority = MAX_RT_PRIO - 1 }; + int ret = 0; + + einfo = &global_emc_info; + einfo->abridge_base = drv->base_regs[ABRIDGE]; + + einfo->emcclk = clk_get_sys("ape", "emc"); + if (IS_ERR_OR_NULL(einfo->emcclk)) { + dev_info(&pdev->dev, "unable to find ape.emc clock\n"); + return PTR_ERR(einfo->emcclk); + } + + einfo->timer_rate = DEFAULT_SAMPLE_PERIOD; + einfo->boost_up_threshold = DEFAULT_BOOST_UP_THRESHOLD; + einfo->boost_step = DEFAULT_BOOST_STEP; + einfo->dn_count = 0; + einfo->boost_dn_count = BOOST_DOWN_COUNT; + einfo->enable = 1; + + einfo->max_freq = clk_round_rate(einfo->emcclk, ULONG_MAX); + ret = clk_set_rate(einfo->emcclk, einfo->max_freq); + if (ret) { + dev_info(&pdev->dev, "failed to set ape.emc freq:%d\n", ret); + return PTR_ERR(einfo->emcclk); + } + einfo->max_freq /= 1000; + einfo->cur_freq = clk_get_rate(einfo->emcclk) / 1000; + if (!einfo->cur_freq) { + dev_info(&pdev->dev, "ape.emc freq is NULL:\n"); + return PTR_ERR(einfo->emcclk); + } + + dev_info(&pdev->dev, "einfo->cur_freq %lu\n", einfo->cur_freq); + + spin_lock_init(&speedchange_lock); + init_timer(&einfo->cnt_timer); + einfo->cnt_timer.function = emc_dfs_timer; + + speedchange_task = kthread_create(clk_work, NULL, "emc_dfs"); + if (IS_ERR(speedchange_task)) + return PTR_ERR(speedchange_task); + + sched_setscheduler_nocheck(speedchange_task, SCHED_FIFO, ¶m); + get_task_struct(speedchange_task); + + /* NB: wake up so the thread does not look hung to the freezer */ + wake_up_process(speedchange_task); + + emc_dfs_enable(); + + dev_info(&pdev->dev, "APE EMC DFS is initialized\n"); + +#ifdef CONFIG_DEBUG_FS + emc_dfs_debugfs_init(drv); +#endif + + return ret; +} +void __exit emc_dfs_exit(void) +{ + kthread_stop(speedchange_task); + put_task_struct(speedchange_task); +} diff --git a/drivers/platform/tegra/nvadsp/hwmailbox-t21x.h b/drivers/platform/tegra/nvadsp/hwmailbox-t21x.h new file mode 100644 index 00000000..597b6b9f --- /dev/null +++ b/drivers/platform/tegra/nvadsp/hwmailbox-t21x.h @@ -0,0 +1,25 @@ +/* + * Copyright (c) 2015, NVIDIA CORPORATION. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope 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. + */ + +#ifndef __HWMAILBOX_T21X_H +#define __HWMAILBOX_T21X_H + +#define HWMB_REG_IDX AMISC + +/* Mailbox register Offsets in AMISC */ +#define HWMBOX0_REG 0x58 +#define HWMBOX1_REG 0X5C +#define HWMBOX2_REG 0x60 +#define HWMBOX3_REG 0x64 + +#endif /* __HWMAILBOX_T21X_H */ diff --git a/drivers/platform/tegra/nvadsp/hwmailbox.c b/drivers/platform/tegra/nvadsp/hwmailbox.c new file mode 100644 index 00000000..57a581eb --- /dev/null +++ b/drivers/platform/tegra/nvadsp/hwmailbox.c @@ -0,0 +1,294 @@ +/* + * Copyright (c) 2014-2016, NVIDIA CORPORATION. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope 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 +#include +#include +#include +#include +#include +#include + +#include "dev.h" + +/* + * Mailbox 0 is for receiving messages + * from ADSP i.e. CPU <-- ADSP. + */ +#define RECV_HWMBOX HWMBOX0_REG +#define INT_RECV_HWMBOX INT_AMISC_MBOX_FULL0 + +/* + * Mailbox 1 is for sending messages + * to ADSP i.e. CPU --> ADSP + */ +#define SEND_HWMBOX HWMBOX1_REG +#define INT_SEND_HWMBOX INT_AMISC_MBOX_EMPTY1 + +static struct platform_device *nvadsp_pdev; +static struct nvadsp_drv_data *nvadsp_drv_data; +/* Initialized to false by default */ +static bool is_hwmbox_busy; +#ifdef CONFIG_MBOX_ACK_HANDLER +static int hwmbox_last_msg; +#endif + + +static inline u32 hwmbox_readl(u32 reg) +{ + return readl(nvadsp_drv_data->base_regs[HWMB_REG_IDX] + reg); +} + +static inline void hwmbox_writel(u32 val, u32 reg) +{ + writel(val, nvadsp_drv_data->base_regs[HWMB_REG_IDX] + reg); +} + + +#define PRINT_HWMBOX(x) \ + dev_info(&nvadsp_pdev->dev, "%s: 0x%x\n", #x, hwmbox_readl(x)) + +void dump_mailbox_regs(void) +{ + dev_info(&nvadsp_pdev->dev, "dumping hwmailbox registers ...\n"); + PRINT_HWMBOX(RECV_HWMBOX); + PRINT_HWMBOX(SEND_HWMBOX); + dev_info(&nvadsp_pdev->dev, "end of dump ....\n"); +} + +static void hwmboxq_init(struct hwmbox_queue *queue) +{ + queue->head = 0; + queue->tail = 0; + queue->count = 0; + init_completion(&queue->comp); + spin_lock_init(&queue->lock); +} + +/* Must be called with queue lock held in non-interrupt context */ +static inline bool +is_hwmboxq_empty(struct hwmbox_queue *queue) +{ + return (queue->count == 0); +} + +/* Must be called with queue lock held in non-interrupt context */ +static inline bool +is_hwmboxq_full(struct hwmbox_queue *queue) +{ + return (queue->count == HWMBOX_QUEUE_SIZE); +} + +/* Must be called with queue lock held in non-interrupt context */ +static status_t hwmboxq_enqueue(struct hwmbox_queue *queue, + uint32_t data) +{ + int ret = 0; + + if (is_hwmboxq_full(queue)) { + ret = -EBUSY; + goto comp; + } + queue->array[queue->tail] = data; + queue->tail = (queue->tail + 1) & HWMBOX_QUEUE_SIZE_MASK; + queue->count++; + + if (is_hwmboxq_full(queue)) + goto comp; + else + goto out; + + comp: + reinit_completion(&queue->comp); + out: + return ret; +} + +status_t nvadsp_hwmbox_send_data(uint16_t mid, uint32_t data, uint32_t flags) +{ + spinlock_t *lock = &nvadsp_drv_data->hwmbox_send_queue.lock; + unsigned long lockflags; + int ret = 0; + + if (flags & NVADSP_MBOX_SMSG) { + data = PREPARE_HWMBOX_SMSG(mid, data); + pr_debug("nvadsp_mbox_send: data: 0x%x\n", data); + } + + /* TODO handle LMSG */ + + spin_lock_irqsave(lock, lockflags); + + if (!is_hwmbox_busy) { + is_hwmbox_busy = true; + pr_debug("nvadsp_mbox_send: empty mailbox. write to mailbox.\n"); +#ifdef CONFIG_MBOX_ACK_HANDLER + hwmbox_last_msg = data; +#endif + hwmbox_writel(data, SEND_HWMBOX); + } else { + pr_debug("nvadsp_mbox_send: enqueue data\n"); + ret = hwmboxq_enqueue(&nvadsp_drv_data->hwmbox_send_queue, + data); + } + spin_unlock_irqrestore(lock, lockflags); + return ret; +} + +/* Must be called with queue lock held in non-interrupt context */ +static status_t hwmboxq_dequeue(struct hwmbox_queue *queue, + uint32_t *data) +{ + int ret = 0; + + if (is_hwmboxq_empty(queue)) { + ret = -EBUSY; + goto out; + } + + if (is_hwmboxq_full(queue)) + complete_all(&nvadsp_drv_data->hwmbox_send_queue.comp); + + *data = queue->array[queue->head]; + queue->head = (queue->head + 1) & HWMBOX_QUEUE_SIZE_MASK; + queue->count--; + + out: + return ret; +} + +static irqreturn_t hwmbox_send_empty_int_handler(int irq, void *devid) +{ + spinlock_t *lock = &nvadsp_drv_data->hwmbox_send_queue.lock; + struct device *dev = &nvadsp_pdev->dev; + unsigned long lockflags; + uint32_t data; + int ret; + + spin_lock_irqsave(lock, lockflags); + + data = hwmbox_readl(SEND_HWMBOX); + if (data != PREPARE_HWMBOX_EMPTY_MSG()) + dev_err(dev, "last mailbox sent failed with 0x%x\n", data); + +#ifdef CONFIG_MBOX_ACK_HANDLER + { + uint16_t last_mboxid = HWMBOX_SMSG_MID(hwmbox_last_msg); + struct nvadsp_mbox *mbox = nvadsp_drv_data->mboxes[last_mboxid]; + + if (mbox) { + nvadsp_mbox_handler_t ack_handler = mbox->ack_handler; + + if (ack_handler) { + uint32_t msg = HWMBOX_SMSG_MSG(hwmbox_last_msg); + + ack_handler(msg, mbox->hdata); + } + } + } +#endif + ret = hwmboxq_dequeue(&nvadsp_drv_data->hwmbox_send_queue, + &data); + if (ret == 0) { +#ifdef CONFIG_MBOX_ACK_HANDLER + hwmbox_last_msg = data; +#endif + hwmbox_writel(data, SEND_HWMBOX); + dev_dbg(dev, "Writing 0x%x to SEND_HWMBOX\n", data); + } else { + is_hwmbox_busy = false; + } + spin_unlock_irqrestore(lock, lockflags); + + return IRQ_HANDLED; +} + +static irqreturn_t hwmbox_recv_full_int_handler(int irq, void *devid) +{ + uint32_t data; + int ret; + + data = hwmbox_readl(RECV_HWMBOX); + hwmbox_writel(PREPARE_HWMBOX_EMPTY_MSG(), RECV_HWMBOX); + + if (IS_HWMBOX_MSG_SMSG(data)) { + uint16_t mboxid = HWMBOX_SMSG_MID(data); + struct nvadsp_mbox *mbox = nvadsp_drv_data->mboxes[mboxid]; + + if (!mbox) { + dev_info(&nvadsp_pdev->dev, + "Failed to get mbox for mboxid: %u\n", + mboxid); + goto out; + } + + if (mbox->handler) { + mbox->handler(HWMBOX_SMSG_MSG(data), mbox->hdata); + } else { + ret = nvadsp_mboxq_enqueue(&mbox->recv_queue, + HWMBOX_SMSG_MSG(data)); + if (ret) { + dev_info(&nvadsp_pdev->dev, + "Failed to deliver msg 0x%x to" + " mbox id %u\n", + HWMBOX_SMSG_MSG(data), mboxid); + goto out; + } + } + } else if (IS_HWMBOX_MSG_LMSG(data)) { + /* TODO */ + } + out: + return IRQ_HANDLED; +} + +int __init nvadsp_hwmbox_init(struct platform_device *pdev) +{ + struct nvadsp_drv_data *drv = platform_get_drvdata(pdev); + int recv_virq, send_virq; + int ret = 0; + + nvadsp_pdev = pdev; + nvadsp_drv_data = drv; + + recv_virq = drv->agic_irqs[MBOX_RECV_VIRQ]; + drv->hwmbox_recv_virq = recv_virq; + + send_virq = drv->agic_irqs[MBOX_SEND_VIRQ]; + drv->hwmbox_send_virq = send_virq; + + ret = request_irq(recv_virq, hwmbox_recv_full_int_handler, + IRQF_TRIGGER_RISING, "hwmbox0_recv_full", pdev); + if (ret) + goto req_recv_virq; + + ret = request_irq(send_virq, hwmbox_send_empty_int_handler, + IRQF_TRIGGER_RISING, + "hwmbox1_send_empty", pdev); + if (ret) + goto req_send_virq; + + hwmboxq_init(&drv->hwmbox_send_queue); + + return ret; + + req_send_virq: + free_irq(recv_virq, pdev); + + req_recv_virq: + irq_dispose_mapping(send_virq); + irq_dispose_mapping(recv_virq); + nvadsp_drv_data = NULL; + nvadsp_pdev = NULL; + return ret; +} diff --git a/drivers/platform/tegra/nvadsp/hwmailbox.h b/drivers/platform/tegra/nvadsp/hwmailbox.h new file mode 100644 index 00000000..1115cfbe --- /dev/null +++ b/drivers/platform/tegra/nvadsp/hwmailbox.h @@ -0,0 +1,116 @@ +/* + * Copyright (c) 2014-2016, NVIDIA CORPORATION. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope 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. + */ + +#ifndef __HWMAILBOX_H +#define __HWMAILBOX_H + +#include +#include +#include +#include + +#if defined(CONFIG_ARCH_TEGRA_21x_SOC) +#include "hwmailbox-t21x.h" +#else +#include "hwmailbox-t18x.h" +#endif /* CONFIG_ARCH_TEGRA_21x_SOC */ + +/* + * The interpretation of hwmailbox content is: + * 31 30 29 0 + * [TAG|TYPE|MESSAGE] + */ +#define HWMBOX_TAG_SHIFT 31 +#define HWMBOX_TAG_MASK 0x1 +#define HWMBOX_TAG_INVALID 0 +#define HWMBOX_TAG_VALID 1 +/* Set Invalid TAG */ +#define SET_HWMBOX_TAG_INVALID (HWMBOX_TAG_INVALID << HWMBOX_TAG_SHIFT) +/* Set Valid TAG */ +#define SET_HWMBOX_TAG_VALID (HWMBOX_TAG_VALID << HWMBOX_TAG_SHIFT) +/* Get current TAG */ +#define HWMBOX_TAG(val) ((val & HWMBOX_TAG_MASK) << HWMBOX_TAG_SHIFT) + +/* + * Mailbox can be used for sending short messages and long messages + */ +#define HWMBOX_MSG_TYPE_SHIFT 30 +#define HWMBOX_MSG_TYPE_MASK 0x1 +#define HWMBOX_MSG_SMSG 0 +#define HWMBOX_MSG_LMSG 1 +/* Set SMSG type */ +#define SET_HWMBOX_MSG_SMSG (HWMBOX_MSG_SMSG << HWMBOX_MSG_TYPE_SHIFT) +/* Set LMSG type */ +#define SET_HWMBOX_MSG_LMSG (HWMBOX_MSG_LMSG << HWMBOX_MSG_TYPE_SHIFT) +/* Get MSG type */ +#define HWMBOX_MSG_TYPE(val) \ + ((val >> HWMBOX_MSG_TYPE_SHIFT) & HWMBOX_MSG_TYPE_MASK) +/* Check if SMSG */ +#define IS_HWMBOX_MSG_SMSG(val) \ + (!((val >> HWMBOX_MSG_TYPE_SHIFT) & HWMBOX_MSG_TYPE_MASK)) +/* Check if LMSG */ +#define IS_HWMBOX_MSG_LMSG(val) \ + ((val >> HWMBOX_MSG_TYPE_SHIFT) & HWMBOX_MSG_TYPE_MASK) + +/* + * The format for a short message is: + * 31 30 29 20 19 0 + * [TAG|TYPE|MBOX ID|SHORT MESSAGE] + * 1b 1b 10bits 20bits + */ +#define HWMBOX_SMSG_SHIFT 0 +#define HWMBOX_SMSG_MASK 0x3FFFFFFF +#define HWMBOX_SMSG(val) ((val >> HWMBOX_SMSG_SHIFT) & HWMBOX_SMSG_MASK) +#define HWMBOX_SMSG_MID_SHIFT 20 +#define HWMBOX_SMSG_MID_MASK 0x3FF +#define HWMBOX_SMSG_MID(val) \ + ((val >> HWMBOX_SMSG_MID_SHIFT) & HWMBOX_SMSG_MID_MASK) +#define HWMBOX_SMSG_MSG_SHIFT 0 +#define HWMBOX_SMSG_MSG_MASK 0xFFFFF +#define HWMBOX_SMSG_MSG(val) \ + ((val >> HWMBOX_SMSG_MSG_SHIFT) & HWMBOX_SMSG_MSG_MASK) +/* Set mailbox id for a short message */ +#define SET_HWMBOX_SMSG_MID(val) \ + ((val & HWMBOX_SMSG_MID_MASK) << HWMBOX_SMSG_MID_SHIFT) +/* Set msg value in a short message */ +#define SET_HWMBOX_SMSG_MSG(val) \ + ((val & HWMBOX_SMSG_MSG_MASK) << HWMBOX_SMSG_MSG_SHIFT) + +/* Prepare a small message with mailbox id and data */ +#define PREPARE_HWMBOX_SMSG(mid, data) (SET_HWMBOX_TAG_VALID | \ + SET_HWMBOX_MSG_SMSG | \ + SET_HWMBOX_SMSG_MID(mid) | \ + SET_HWMBOX_SMSG_MSG(data)) +/* Prepare empty mailbox value */ +#define PREPARE_HWMBOX_EMPTY_MSG() (HWMBOX_TAG_INVALID | 0x0) + +/* + * Queue size must be power of 2 as '&' op + * is being used to manage circular queues + */ +#define HWMBOX_QUEUE_SIZE 1024 +#define HWMBOX_QUEUE_SIZE_MASK (HWMBOX_QUEUE_SIZE - 1) +struct hwmbox_queue { + uint32_t array[HWMBOX_QUEUE_SIZE]; + uint16_t head; + uint16_t tail; + uint16_t count; + struct completion comp; + spinlock_t lock; +}; + +int nvadsp_hwmbox_init(struct platform_device *); +status_t nvadsp_hwmbox_send_data(uint16_t, uint32_t, uint32_t); +void dump_mailbox_regs(void); + +#endif /* __HWMAILBOX_H */ diff --git a/drivers/platform/tegra/nvadsp/mailbox.c b/drivers/platform/tegra/nvadsp/mailbox.c new file mode 100644 index 00000000..17f443a2 --- /dev/null +++ b/drivers/platform/tegra/nvadsp/mailbox.c @@ -0,0 +1,317 @@ +/* + * ADSP mailbox manager + * + * Copyright (c) 2014-2016, NVIDIA CORPORATION. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope 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 "dev.h" + +#define NVADSP_MAILBOX_START 512 +#define NVADSP_MAILBOX_MAX 1024 +#define NVADSP_MAILBOX_OS_MAX 16 + +static struct nvadsp_mbox *nvadsp_mboxes[NVADSP_MAILBOX_MAX]; +static DECLARE_BITMAP(nvadsp_mbox_ids, NVADSP_MAILBOX_MAX); +static struct nvadsp_drv_data *nvadsp_drv_data; + +static inline bool is_mboxq_empty(struct nvadsp_mbox_queue *queue) +{ + return (queue->count == 0); +} + +static inline bool is_mboxq_full(struct nvadsp_mbox_queue *queue) +{ + return (queue->count == NVADSP_MBOX_QUEUE_SIZE); +} + +static void mboxq_init(struct nvadsp_mbox_queue *queue) +{ + queue->head = 0; + queue->tail = 0; + queue->count = 0; + init_completion(&queue->comp); + spin_lock_init(&queue->lock); +} + +static void mboxq_destroy(struct nvadsp_mbox_queue *queue) +{ + if (!is_mboxq_empty(queue)) + pr_info("Mbox queue %p is not empty.\n", queue); + + queue->head = 0; + queue->tail = 0; + queue->count = 0; +} + +static status_t mboxq_enqueue(struct nvadsp_mbox_queue *queue, + uint32_t data) +{ + int ret = 0; + + if (is_mboxq_full(queue)) { + ret = -EINVAL; + goto out; + } + + if (is_mboxq_empty(queue)) + complete_all(&queue->comp); + + queue->array[queue->tail] = data; + queue->tail = (queue->tail + 1) & NVADSP_MBOX_QUEUE_SIZE_MASK; + queue->count++; + out: + return ret; +} + +status_t nvadsp_mboxq_enqueue(struct nvadsp_mbox_queue *queue, + uint32_t data) +{ + return mboxq_enqueue(queue, data); +} + +static status_t mboxq_dequeue(struct nvadsp_mbox_queue *queue, + uint32_t *data) +{ + unsigned long flags; + int ret = 0; + + spin_lock_irqsave(&queue->lock, flags); + if (is_mboxq_empty(queue)) { + ret = -EBUSY; + goto comp; + } + + *data = queue->array[queue->head]; + queue->head = (queue->head + 1) & NVADSP_MBOX_QUEUE_SIZE_MASK; + queue->count--; + + if (is_mboxq_empty(queue)) + goto comp; + else + goto out; + comp: + reinit_completion(&queue->comp); + out: + spin_unlock_irqrestore(&queue->lock, flags); + return ret; +} + +static uint16_t nvadsp_mbox_alloc_mboxid(void) +{ + unsigned long start = NVADSP_MAILBOX_START; + unsigned int nr = 1; + unsigned long align = 0; + uint16_t mid; + + mid = bitmap_find_next_zero_area(nvadsp_drv_data->mbox_ids, + NVADSP_MAILBOX_MAX - 1, + start, nr, align); + + bitmap_set(nvadsp_drv_data->mbox_ids, mid, 1); + return mid; +} + +static status_t nvadsp_mbox_free_mboxid(uint16_t mid) +{ + bitmap_clear(nvadsp_drv_data->mbox_ids, mid, 1); + return 0; +} + +status_t nvadsp_mbox_open(struct nvadsp_mbox *mbox, uint16_t *mid, + const char *name, nvadsp_mbox_handler_t handler, + void *hdata) +{ + unsigned long flags; + int ret = 0; + + if (!nvadsp_drv_data) { + ret = -ENOSYS; + goto err; + } + + spin_lock_irqsave(&nvadsp_drv_data->mbox_lock, flags); + + if (!mbox) { + ret = -EINVAL; + goto out; + } + + if (*mid == 0) { + mbox->id = nvadsp_mbox_alloc_mboxid(); + if (mbox->id >= NVADSP_MAILBOX_MAX) { + ret = -ENOMEM; + mbox->id = 0; + goto out; + } + *mid = mbox->id; + } else { + if (*mid >= NVADSP_MAILBOX_MAX) { + pr_debug("%s: Invalid mailbox %d.\n", + __func__, *mid); + ret = -EINVAL; + goto out; + } + if (nvadsp_drv_data->mboxes[*mid]) { + pr_debug("%s: mailbox %d already opened.\n", + __func__, *mid); + ret = -EINVAL; + goto out; + } + mbox->id = *mid; + } + + strncpy(mbox->name, name, NVADSP_MBOX_NAME_MAX); + mboxq_init(&mbox->recv_queue); + mbox->handler = handler; + mbox->hdata = hdata; + + nvadsp_drv_data->mboxes[mbox->id] = mbox; + out: + spin_unlock_irqrestore(&nvadsp_drv_data->mbox_lock, flags); + err: + return ret; +} +EXPORT_SYMBOL(nvadsp_mbox_open); + +status_t nvadsp_mbox_send(struct nvadsp_mbox *mbox, uint32_t data, + uint32_t flags, bool block, unsigned int timeout) +{ + int ret = 0; + + if (!nvadsp_drv_data) { + ret = -ENOSYS; + goto out; + } + + if (!mbox) { + ret = -EINVAL; + goto out; + } + + retry: + ret = nvadsp_hwmbox_send_data(mbox->id, data, flags); + if (!ret) + goto out; + + if (ret == -EBUSY) { + if (block) { + ret = wait_for_completion_timeout( + &nvadsp_drv_data->hwmbox_send_queue.comp, + msecs_to_jiffies(timeout)); + if (ret) { + block = false; + goto retry; + } else { + ret = -ETIME; + goto out; + } + } else { + pr_debug("Failed to enqueue data 0x%x. ret: %d\n", + data, ret); + } + } else if (ret) { + pr_debug("Failed to enqueue data 0x%x. ret: %d\n", data, ret); + goto out; + } + out: + return ret; +} +EXPORT_SYMBOL(nvadsp_mbox_send); + +status_t nvadsp_mbox_recv(struct nvadsp_mbox *mbox, uint32_t *data, bool block, + unsigned int timeout) +{ + int ret = 0; + + if (!nvadsp_drv_data) { + ret = -ENOSYS; + goto out; + } + + if (!mbox) { + ret = -EINVAL; + goto out; + } + + retry: + ret = mboxq_dequeue(&mbox->recv_queue, data); + if (!ret) + goto out; + + if (ret == -EBUSY) { + if (block) { + ret = wait_for_completion_timeout( + &mbox->recv_queue.comp, + msecs_to_jiffies(timeout)); + if (ret) { + block = false; + goto retry; + } else { + ret = -ETIME; + goto out; + } + } else { + pr_debug("Failed to receive data. ret: %d\n", ret); + } + } else if (ret) { + pr_debug("Failed to receive data. ret: %d\n", ret); + goto out; + } + out: + return ret; +} +EXPORT_SYMBOL(nvadsp_mbox_recv); + +status_t nvadsp_mbox_close(struct nvadsp_mbox *mbox) +{ + unsigned long flags; + int ret = 0; + + if (!nvadsp_drv_data) { + ret = -ENOSYS; + goto err; + } + + spin_lock_irqsave(&nvadsp_drv_data->mbox_lock, flags); + if (!mbox) { + ret = -EINVAL; + goto out; + } + + if (!is_mboxq_empty(&mbox->recv_queue)) { + ret = -EINVAL; + goto out; + } + + nvadsp_mbox_free_mboxid(mbox->id); + mboxq_destroy(&mbox->recv_queue); + nvadsp_drv_data->mboxes[mbox->id] = NULL; + out: + spin_unlock_irqrestore(&nvadsp_drv_data->mbox_lock, flags); + err: + return ret; +} +EXPORT_SYMBOL(nvadsp_mbox_close); + +status_t __init nvadsp_mbox_init(struct platform_device *pdev) +{ + struct nvadsp_drv_data *drv = platform_get_drvdata(pdev); + + drv->mboxes = nvadsp_mboxes; + drv->mbox_ids = nvadsp_mbox_ids; + + spin_lock_init(&drv->mbox_lock); + + nvadsp_drv_data = drv; + + return 0; +} diff --git a/drivers/platform/tegra/nvadsp/mem_manager.c b/drivers/platform/tegra/nvadsp/mem_manager.c new file mode 100644 index 00000000..63a64d90 --- /dev/null +++ b/drivers/platform/tegra/nvadsp/mem_manager.c @@ -0,0 +1,323 @@ +/* + * mem_manager.c + * + * memory manager + * + * Copyright (C) 2014-2015 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. + * + */ + +#define pr_fmt(fmt) "%s : %d, " fmt, __func__, __LINE__ + +#include +#include +#include +#include +#include + +#include "mem_manager.h" + +static void clear_alloc_list(struct mem_manager_info *mm_info); + +void *mem_request(void *mem_handle, const char *name, size_t size) +{ + unsigned long flags; + struct mem_manager_info *mm_info = + (struct mem_manager_info *)mem_handle; + struct mem_chunk *mc_iterator = NULL, *best_match_chunk = NULL; + struct mem_chunk *new_mc = NULL; + + spin_lock_irqsave(&mm_info->lock, flags); + + /* Is mem full? */ + if (list_empty(mm_info->free_list)) { + pr_err("%s : memory full\n", mm_info->name); + spin_unlock_irqrestore(&mm_info->lock, flags); + return ERR_PTR(-ENOMEM); + } + + /* Find the best size match */ + list_for_each_entry(mc_iterator, mm_info->free_list, node) { + if (mc_iterator->size >= size) { + if (best_match_chunk == NULL) + best_match_chunk = mc_iterator; + else if (mc_iterator->size < best_match_chunk->size) + best_match_chunk = mc_iterator; + } + } + + /* Is free node found? */ + if (best_match_chunk == NULL) { + pr_err("%s : no enough memory available\n", mm_info->name); + spin_unlock_irqrestore(&mm_info->lock, flags); + return ERR_PTR(-ENOMEM); + } + + /* Is it exact match? */ + if (best_match_chunk->size == size) { + list_del(&best_match_chunk->node); + list_for_each_entry(mc_iterator, mm_info->alloc_list, node) { + if (best_match_chunk->address < mc_iterator->address) { + list_add_tail(&best_match_chunk->node, + &mc_iterator->node); + strlcpy(best_match_chunk->name, name, + NAME_SIZE); + spin_unlock_irqrestore(&mm_info->lock, flags); + return best_match_chunk; + } + } + list_add(&best_match_chunk->node, mm_info->alloc_list); + strlcpy(best_match_chunk->name, name, NAME_SIZE); + spin_unlock_irqrestore(&mm_info->lock, flags); + return best_match_chunk; + } else { + new_mc = kzalloc(sizeof(struct mem_chunk), GFP_ATOMIC); + if (unlikely(!new_mc)) { + pr_err("failed to allocate memory for mem_chunk\n"); + + spin_unlock_irqrestore(&mm_info->lock, flags); + return ERR_PTR(-ENOMEM); + } + new_mc->address = best_match_chunk->address; + new_mc->size = size; + strlcpy(new_mc->name, name, NAME_SIZE); + best_match_chunk->address += size; + best_match_chunk->size -= size; + list_for_each_entry(mc_iterator, mm_info->alloc_list, node) { + if (new_mc->address < mc_iterator->address) { + list_add_tail(&new_mc->node, + &mc_iterator->node); + spin_unlock_irqrestore(&mm_info->lock, flags); + return new_mc; + } + } + list_add_tail(&new_mc->node, mm_info->alloc_list); + spin_unlock_irqrestore(&mm_info->lock, flags); + return new_mc; + } +} +EXPORT_SYMBOL(mem_request); + +/* + * Find the node with sepcified address and remove it from list + */ +bool mem_release(void *mem_handle, void *handle) +{ + unsigned long flags; + struct mem_manager_info *mm_info = + (struct mem_manager_info *)mem_handle; + struct mem_chunk *mc_curr = NULL, *mc_prev = NULL; + struct mem_chunk *mc_free = (struct mem_chunk *)handle; + + pr_debug(" addr = %lu, size = %lu, name = %s\n", + mc_free->address, mc_free->size, mc_free->name); + + spin_lock_irqsave(&mm_info->lock, flags); + + list_for_each_entry(mc_curr, mm_info->free_list, node) { + if (mc_free->address < mc_curr->address) { + + strlcpy(mc_free->name, "FREE", NAME_SIZE); + + /* adjacent next free node */ + if (mc_curr->address == + (mc_free->address + mc_free->size)) { + + mc_curr->address = mc_free->address; + mc_curr->size += mc_free->size; + list_del(&mc_free->node); + kfree(mc_free); + + /* and adjacent prev free node */ + if ((mc_prev != NULL) && + ((mc_prev->address + mc_prev->size) == + mc_curr->address)) { + + mc_prev->size += mc_curr->size; + list_del(&mc_curr->node); + kfree(mc_curr); + } + } + /* adjacent prev free node */ + else if ((mc_prev != NULL) && + ((mc_prev->address + mc_prev->size) == + mc_free->address)) { + + mc_prev->size += mc_free->size; + list_del(&mc_free->node); + kfree(mc_free); + } else { + list_del(&mc_free->node); + list_add_tail(&mc_free->node, + &mc_curr->node); + } + spin_unlock_irqrestore(&mm_info->lock, flags); + return true; + } + mc_prev = mc_curr; + } + spin_unlock_irqrestore(&mm_info->lock, flags); + return false; +} +EXPORT_SYMBOL(mem_release); + +inline unsigned long mem_get_address(void *handle) +{ + struct mem_chunk *mc = (struct mem_chunk *)handle; + return mc->address; +} +EXPORT_SYMBOL(mem_get_address); + +void mem_print(void *mem_handle) +{ + struct mem_manager_info *mm_info = + (struct mem_manager_info *)mem_handle; + struct mem_chunk *mc_iterator = NULL; + + pr_info("------------------------------------\n"); + pr_info("%s ALLOCATED\n", mm_info->name); + list_for_each_entry(mc_iterator, mm_info->alloc_list, node) { + pr_info(" addr = %lu, size = %lu, name = %s\n", + mc_iterator->address, mc_iterator->size, + mc_iterator->name); + } + + pr_info("%s FREE\n", mm_info->name); + list_for_each_entry(mc_iterator, mm_info->free_list, node) { + pr_info(" addr = %lu, size = %lu, name = %s\n", + mc_iterator->address, mc_iterator->size, + mc_iterator->name); + } + + pr_info("------------------------------------\n"); +} +EXPORT_SYMBOL(mem_print); + +void mem_dump(void *mem_handle, struct seq_file *s) +{ + struct mem_manager_info *mm_info = + (struct mem_manager_info *)mem_handle; + struct mem_chunk *mc_iterator = NULL; + + seq_puts(s, "---------------------------------------\n"); + seq_printf(s, "%s ALLOCATED\n", mm_info->name); + list_for_each_entry(mc_iterator, mm_info->alloc_list, node) { + seq_printf(s, " addr = %lu, size = %lu, name = %s\n", + mc_iterator->address, mc_iterator->size, + mc_iterator->name); + } + + seq_printf(s, "%s FREE\n", mm_info->name); + list_for_each_entry(mc_iterator, mm_info->free_list, node) { + seq_printf(s, " addr = %lu, size = %lu, name = %s\n", + mc_iterator->address, mc_iterator->size, + mc_iterator->name); + } + + seq_puts(s, "---------------------------------------\n"); +} +EXPORT_SYMBOL(mem_dump); + +static void clear_alloc_list(struct mem_manager_info *mm_info) +{ + struct list_head *curr, *next; + struct mem_chunk *mc = NULL; + + list_for_each_safe(curr, next, mm_info->alloc_list) { + mc = list_entry(curr, struct mem_chunk, node); + pr_debug(" addr = %lu, size = %lu, name = %s\n", + mc->address, mc->size, + mc->name); + mem_release(mm_info, mc); + } +} + +void *create_mem_manager(const char *name, unsigned long start_address, + unsigned long size) +{ + void *ret = NULL; + struct mem_chunk *mc; + struct mem_manager_info *mm_info = + kzalloc(sizeof(struct mem_manager_info), GFP_KERNEL); + if (unlikely(!mm_info)) { + pr_err("failed to allocate memory for mem_manager_info\n"); + return ERR_PTR(-ENOMEM); + } + + strlcpy(mm_info->name, name, NAME_SIZE); + + mm_info->alloc_list = kzalloc(sizeof(struct list_head), GFP_KERNEL); + if (unlikely(!mm_info->alloc_list)) { + pr_err("failed to allocate memory for alloc_list\n"); + ret = ERR_PTR(-ENOMEM); + goto free_mm_info; + } + + mm_info->free_list = kzalloc(sizeof(struct list_head), GFP_KERNEL); + if (unlikely(!mm_info->free_list)) { + pr_err("failed to allocate memory for free_list\n"); + ret = ERR_PTR(-ENOMEM); + goto free_alloc_list; + } + + INIT_LIST_HEAD(mm_info->alloc_list); + INIT_LIST_HEAD(mm_info->free_list); + + mm_info->start_address = start_address; + mm_info->size = size; + + /* Add whole memory to free list */ + mc = kzalloc(sizeof(struct mem_chunk), GFP_KERNEL); + if (unlikely(!mc)) { + pr_err("failed to allocate memory for mem_chunk\n"); + ret = ERR_PTR(-ENOMEM); + goto free_free_list; + } + + mc->address = mm_info->start_address; + mc->size = mm_info->size; + strlcpy(mc->name, "FREE", NAME_SIZE); + list_add(&mc->node, mm_info->free_list); + spin_lock_init(&mm_info->lock); + + return (void *)mm_info; + +free_free_list: + kfree(mm_info->free_list); +free_alloc_list: + kfree(mm_info->alloc_list); +free_mm_info: + kfree(mm_info); + + return ret; +} +EXPORT_SYMBOL(create_mem_manager); + +void destroy_mem_manager(void *mem_handle) +{ + struct mem_manager_info *mm_info = + (struct mem_manager_info *)mem_handle; + struct mem_chunk *mc_last = NULL; + + /* Clear all allocated memory */ + clear_alloc_list(mm_info); + + mc_last = list_entry((mm_info->free_list)->next, + struct mem_chunk, node); + list_del(&mc_last->node); + + kfree(mc_last); + kfree(mm_info->alloc_list); + kfree(mm_info->free_list); + kfree(mm_info); +} +EXPORT_SYMBOL(destroy_mem_manager); diff --git a/drivers/platform/tegra/nvadsp/mem_manager.h b/drivers/platform/tegra/nvadsp/mem_manager.h new file mode 100644 index 00000000..6ad04b72 --- /dev/null +++ b/drivers/platform/tegra/nvadsp/mem_manager.h @@ -0,0 +1,51 @@ +/* + * Header file for memory manager + * + * Copyright (c) 2014, NVIDIA CORPORATION. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope 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. + */ + +#ifndef __TEGRA_NVADSP_MEM_MANAGER_H +#define __TEGRA_NVADSP_MEM_MANAGER_H + +#include + +#define NAME_SIZE SZ_16 + +struct mem_chunk { + struct list_head node; + char name[NAME_SIZE]; + unsigned long address; + unsigned long size; +}; + +struct mem_manager_info { + struct list_head *alloc_list; + struct list_head *free_list; + char name[NAME_SIZE]; + unsigned long start_address; + unsigned long size; + spinlock_t lock; +}; + +void *create_mem_manager(const char *name, unsigned long start_address, + unsigned long size); +void destroy_mem_manager(void *mem_handle); + +void *mem_request(void *mem_handle, const char *name, size_t size); +bool mem_release(void *mem_handle, void *handle); + +unsigned long mem_get_address(void *handle); + +void mem_print(void *mem_handle); +void mem_dump(void *mem_handle, struct seq_file *s); + +#endif /* __TEGRA_NVADSP_MEM_MANAGER_H */ diff --git a/drivers/platform/tegra/nvadsp/msgq.c b/drivers/platform/tegra/nvadsp/msgq.c new file mode 100644 index 00000000..427f2fdf --- /dev/null +++ b/drivers/platform/tegra/nvadsp/msgq.c @@ -0,0 +1,170 @@ +/* + * ADSP circular message queue + * + * Copyright (c) 2014-2016, NVIDIA CORPORATION. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope 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 + +#define msgq_wmemcpy(dest, src, words) \ + memcpy(dest, src, (words) * sizeof(int32_t)) + + +/** + * msgq_init - Initialize message queue + * @msgq: pointer to the client message queue + * @size: size of message queue in words + * size will be capped to MSGQ_MAX_WSIZE + * + * This function returns 0 if no error has occurred. + * + * The message queue requires space for the queue to be + * preallocated and should only be initialized once. The queue + * space immediately follows the queue header and begins at + * msgq_t::message_queue. All messages are queued directly with + * no pointer address space translation. + * + * + */ +void msgq_init(msgq_t *msgq, int32_t size) +{ + if (MSGQ_MAX_QUEUE_WSIZE < size) { + /* cap the maximum size */ + pr_info("msgq_init: %d size capped to MSGQ_MAX_QUEUE_WSIZE\n", + size); + size = MSGQ_MAX_QUEUE_WSIZE; + } + + msgq->size = size; + msgq->read_index = 0; + msgq->write_index = 0; +} +EXPORT_SYMBOL(msgq_init); +/** + * msgq_queue_message - Queues a message in the queue + * @msgq: pointer to the client message queue + * @message: Message buffer to copy from + * + * This function returns 0 if no error has occurred. ERR_NO_MEMORY will + * be returned if no space is available in the queue for the + * entire message. On ERR_NO_MEMORY, it may be possible the + * queue size was capped at init time to MSGQ_MAX_WSIZE if an + * unreasonable size was sepecified. + * + * + */ +int32_t msgq_queue_message(msgq_t *msgq, const msgq_message_t *message) +{ + int32_t ret = 0; + if (msgq && message) { + int32_t ri = msgq->read_index; + int32_t wi = msgq->write_index; + bool wrap = ri <= wi; + int32_t *start = msgq->queue; + int32_t *end = &msgq->queue[msgq->size]; + int32_t *first = &msgq->queue[wi]; + int32_t *last = &msgq->queue[ri]; + int32_t qremainder = wrap ? end - first : last - first; + int32_t qsize = wrap ? qremainder + (last - start) : qremainder; + int32_t msize = &message->payload[message->size] - + (int32_t *)message; + + if (qsize <= msize) { + /* don't allow read == write */ + ret = -ENOSPC; + } else if (msize < qremainder) { + msgq_wmemcpy(first, message, msize); + msgq->write_index = wi + MSGQ_MESSAGE_HEADER_WSIZE + + message->size; + } else { + /* message wrapped */ + msgq_wmemcpy(first, message, qremainder); + msgq_wmemcpy(msgq->queue, (int32_t *)message + + qremainder, msize - qremainder); + msgq->write_index = wi + MSGQ_MESSAGE_HEADER_WSIZE + + message->size - msgq->size; + } + } else { + pr_err("NULL: msgq %p message %p\n", msgq, message); + ret = -EFAULT; /* Bad Address */ + } + + return ret; +} +EXPORT_SYMBOL(msgq_queue_message); +/** + * msgq_dequeue_message - Dequeues a message from the queue + * @msgq: pointer to the client message queue + * @message: Message buffer to copy to or + * NULL to discard the current message + * + * This function returns 0 if no error has occurred. + * msgq_message_t::size will be set to the size of the message + * in words. ERR_NO_MEMORY will be returned if the buffer is too small + * for the queued message. ERR_NO_MSG will be returned if there is no + * message in the queue. + * + * + */ +int32_t msgq_dequeue_message(msgq_t *msgq, msgq_message_t *message) +{ + int32_t ret = 0; + int32_t ri; + int32_t wi; + msgq_message_t *msg; + + if (!msgq) { + pr_err("NULL: msgq %p\n", msgq); + return -EFAULT; /* Bad Address */ + } + + ri = msgq->read_index; + wi = msgq->write_index; + msg = (msgq_message_t *)&msgq->queue[msgq->read_index]; + + if (ri == wi) { + /* empty queue */ + if (message) + message->size = 0; + ret = -ENOMSG; + } else if (!message) { + /* no input buffer, discard top message */ + ri += MSGQ_MESSAGE_HEADER_WSIZE + msg->size; + msgq->read_index = ri < msgq->size ? ri : ri - msgq->size; + } else if (message->size < msg->size) { + /* return buffer too small */ + message->size = msg->size; + ret = -ENOSPC; + } else { + /* copy message to the output buffer */ + int32_t msize = MSGQ_MESSAGE_HEADER_WSIZE + msg->size; + int32_t *first = &msgq->queue[msgq->read_index]; + int32_t *end = &msgq->queue[msgq->size]; + int32_t qremainder = end - first; + + if (msize < qremainder) { + msgq_wmemcpy(message, first, msize); + msgq->read_index = ri + MSGQ_MESSAGE_HEADER_WSIZE + + msg->size; + } else { + /* message wrapped */ + msgq_wmemcpy(message, first, qremainder); + msgq_wmemcpy((int32_t *)message + qremainder, + msgq->queue, msize - qremainder); + msgq->read_index = ri + MSGQ_MESSAGE_HEADER_WSIZE + + msg->size - msgq->size; + } + } + + return ret; +} +EXPORT_SYMBOL(msgq_dequeue_message); diff --git a/drivers/platform/tegra/nvadsp/nvadsp_arb_sema.c b/drivers/platform/tegra/nvadsp/nvadsp_arb_sema.c new file mode 100644 index 00000000..13c41cbe --- /dev/null +++ b/drivers/platform/tegra/nvadsp/nvadsp_arb_sema.c @@ -0,0 +1,39 @@ +/* + * nvadsp_arb_sema.c + * + * ADSP Arbitrated Semaphores + * + * Copyright (C) 2014 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 + +nvadsp_arb_sema_t *nvadsp_arb_sema_init(uint8_t nvadsp_arb_sema_id) +{ + return NULL; +} + +status_t nvadsp_arb_sema_destroy(nvadsp_arb_sema_t *sema) +{ + return -ENOENT; +} + +status_t nvadsp_arb_sema_acquire(nvadsp_arb_sema_t *sema) +{ + return -ENOENT; +} + +status_t nvadsp_arb_sema_release(nvadsp_arb_sema_t *sema) +{ + return -ENOENT; +} diff --git a/drivers/platform/tegra/nvadsp/nvadsp_dram.c b/drivers/platform/tegra/nvadsp/nvadsp_dram.c new file mode 100644 index 00000000..beab4e8f --- /dev/null +++ b/drivers/platform/tegra/nvadsp/nvadsp_dram.c @@ -0,0 +1,67 @@ +/* + * nvadsp_dram.c + * + * DRAM Sharing with ADSP + * + * Copyright (C) 2014 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 + +nvadsp_iova_addr_t +nvadsp_dram_map_single(struct device *nvadsp_dev, + void *cpu_addr, size_t size, + nvadsp_data_direction_t direction) +{ + return DMA_ERROR_CODE; +} + +void +nvadsp_dram_unmap_single(struct device *nvadsp_dev, + nvadsp_iova_addr_t iova_addr, size_t size, + nvadsp_data_direction_t direction) +{ + return; +} + +nvadsp_iova_addr_t +nvadsp_dram_map_page(struct device *nvadsp_dev, + struct page *page, unsigned long offset, size_t size, + nvadsp_data_direction_t direction) +{ + return DMA_ERROR_CODE; +} + +void +nvadsp_dram_unmap_page(struct device *nvadsp_dev, + nvadsp_iova_addr_t iova_addr, size_t size, + nvadsp_data_direction_t direction) +{ + return; +} + +void +nvadsp_dram_sync_single_for_cpu(struct device *nvadsp_dev, + nvadsp_iova_addr_t iova_addr, size_t size, + nvadsp_data_direction_t direction) +{ + return; +} + +void +nvadsp_dram_sync_single_for_device(struct device *nvadsp_dev, + nvadsp_iova_addr_t iova_addr, size_t size, + nvadsp_data_direction_t direction) +{ + return; +} diff --git a/drivers/platform/tegra/nvadsp/nvadsp_shared_sema.c b/drivers/platform/tegra/nvadsp/nvadsp_shared_sema.c new file mode 100644 index 00000000..6c591466 --- /dev/null +++ b/drivers/platform/tegra/nvadsp/nvadsp_shared_sema.c @@ -0,0 +1,41 @@ +/* + * nvadsp_shared_sema.c + * + * ADSP Shared Semaphores + * + * Copyright (C) 2014 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 + +nvadsp_shared_sema_t * +nvadsp_shared_sema_init(uint8_t nvadsp_shared_sema_id) +{ + return NULL; +} + +status_t nvadsp_shared_sema_destroy(nvadsp_shared_sema_t *sema) +{ + return -ENOENT; +} + +status_t nvadsp_shared_sema_acquire(nvadsp_shared_sema_t *sema) +{ + return -ENOENT; +} + +status_t nvadsp_shared_sema_release(nvadsp_shared_sema_t *sema) +{ + return -ENOENT; +} + diff --git a/drivers/platform/tegra/nvadsp/os-t21x.c b/drivers/platform/tegra/nvadsp/os-t21x.c new file mode 100644 index 00000000..db1c61bf --- /dev/null +++ b/drivers/platform/tegra/nvadsp/os-t21x.c @@ -0,0 +1,20 @@ +/* + * Copyright (C) 2016, 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 "dev.h" + +int nvadsp_os_init(struct platform_device *pdev) +{ + return 0; +} diff --git a/drivers/platform/tegra/nvadsp/os-t21x.h b/drivers/platform/tegra/nvadsp/os-t21x.h new file mode 100644 index 00000000..e89d5e71 --- /dev/null +++ b/drivers/platform/tegra/nvadsp/os-t21x.h @@ -0,0 +1,21 @@ +/* + * Copyright (C) 2015, 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. + * + */ +#ifndef __TEGRA_NVADSP_OS_T21X_H +#define __TEGRA_NVADSP_OS_T21X_H + +#include + +#define ADSP_WDT_INT INT_ADSP_WDT + +#endif /* __TEGRA_NVADSP_OS_T21X_H */ diff --git a/drivers/platform/tegra/nvadsp/os.c b/drivers/platform/tegra/nvadsp/os.c new file mode 100644 index 00000000..80534612 --- /dev/null +++ b/drivers/platform/tegra/nvadsp/os.c @@ -0,0 +1,1619 @@ +/* + * os.c + * + * ADSP OS management + * Copyright (C) 2011 Texas Instruments, Inc. + * Copyright (C) 2011 Google, Inc. + * + * Copyright (C) 2014-2016 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "ape_actmon.h" +#include "os.h" +#include "dev.h" +#include "dram_app_mem_manager.h" +#include "adsp_console_dbfs.h" +#include "hwmailbox.h" + +#define NVADSP_ELF "adsp.elf" +#define NVADSP_FIRMWARE NVADSP_ELF + +#define MAILBOX_REGION ".mbox_shared_data" +#define DEBUG_RAM_REGION ".debug_mem_logs" + +/* Maximum number of LOAD MAPPINGS supported */ +#define NM_LOAD_MAPPINGS 20 + +#define EOT 0x04 /* End of Transmission */ +#define SOH 0x01 /* Start of Header */ + +#define ADSP_TAG "\n[ADSP OS]" + +#define UART_BAUD_RATE 9600 + +/* Intiialize with FIXED rate, once OS boots up DFS will set required freq */ +#define ADSP_TO_APE_CLK_RATIO 2 +/* 13.5 MHz, should be changed at bringup time */ +#define APE_CLK_FIX_RATE 13500 +/* + * ADSP CLK = APE_CLK * ADSP_TO_APE_CLK_RATIO + * or + * ADSP CLK = APE_CLK >> ADSP_TO_APE_CLK_RATIO + */ +#define ADSP_CLK_FIX_RATE (APE_CLK_FIX_RATE * ADSP_TO_APE_CLK_RATIO) + +/* total number of crashes allowed on adsp */ +#define ALLOWED_CRASHES 1 + +#define DISABLE_MBOX2_FULL_INT 0x0 +#define ENABLE_MBOX2_FULL_INT 0xFFFFFFFF + +#define LOGGER_TIMEOUT 20 /* in ms */ +#define ADSP_WFE_TIMEOUT 5000 /* in ms */ +#define LOGGER_COMPLETE_TIMEOUT 5000 /* in ms */ + +#define DUMP_BUFF 128 + +struct nvadsp_debug_log { + struct device *dev; + char *debug_ram_rdr; + int debug_ram_sz; + int ram_iter; + atomic_t is_opened; + wait_queue_head_t wait_queue; + struct completion complete; +}; + +struct nvadsp_os_data { + void __iomem *unit_fpga_reset_reg; + const struct firmware *os_firmware; + struct platform_device *pdev; + struct global_sym_info *adsp_glo_sym_tbl; + void __iomem *hwmailbox_base; + struct resource **dram_region; + struct nvadsp_debug_log logger; + struct nvadsp_cnsl console; + struct work_struct restart_os_work; + int adsp_num_crashes; + bool adsp_os_fw_loaded; + struct mutex fw_load_lock; + bool os_running; + struct mutex os_run_lock; + dma_addr_t adsp_os_addr; + size_t adsp_os_size; + dma_addr_t app_alloc_addr; + size_t app_size; +}; + +static struct nvadsp_os_data priv; + +struct nvadsp_mappings { + phys_addr_t da; + void *va; + int len; +}; + +static struct nvadsp_mappings adsp_map[NM_LOAD_MAPPINGS]; +static int map_idx; +static struct nvadsp_mbox adsp_com_mbox; + +static DECLARE_COMPLETION(entered_wfi); + +static void __nvadsp_os_stop(bool); + +#ifdef CONFIG_DEBUG_FS +static int adsp_logger_open(struct inode *inode, struct file *file) +{ + struct nvadsp_debug_log *logger = inode->i_private; + struct nvadsp_os_data *os_data; + int ret = -EBUSY; + char *start; + + os_data = container_of(logger, struct nvadsp_os_data, logger); + + /* + * checks if os_opened decrements to zero and if returns true. If true + * then there has been no open. + */ + if (!atomic_dec_and_test(&logger->is_opened)) { + atomic_inc(&logger->is_opened); + goto err_ret; + } + + ret = wait_event_interruptible(logger->wait_queue, + os_data->adsp_os_fw_loaded); + if (ret == -ERESTARTSYS) /* check if interrupted */ + goto err; + + /* loop till writer is initilized with SOH */ + do { + + ret = wait_event_interruptible_timeout(logger->wait_queue, + memchr(logger->debug_ram_rdr, SOH, + logger->debug_ram_sz), + msecs_to_jiffies(LOGGER_TIMEOUT)); + if (ret == -ERESTARTSYS) /* check if interrupted */ + goto err; + + start = memchr(logger->debug_ram_rdr, SOH, + logger->debug_ram_sz); + } while (!start); + + /* maxdiff can be 0, therefore valid */ + logger->ram_iter = start - logger->debug_ram_rdr; + + file->private_data = logger; + return 0; +err: + /* reset to 1 so as to mention the node is free */ + atomic_set(&logger->is_opened, 1); +err_ret: + return ret; +} + + +static int adsp_logger_flush(struct file *file, fl_owner_t id) +{ + struct nvadsp_debug_log *logger = file->private_data; + struct device *dev = logger->dev; + + dev_dbg(dev, "%s\n", __func__); + + /* reset to 1 so as to mention the node is free */ + atomic_set(&logger->is_opened, 1); + return 0; +} + +static int adsp_logger_release(struct inode *inode, struct file *file) +{ + return 0; +} + +static ssize_t adsp_logger_read(struct file *file, char __user *buf, + size_t count, loff_t *ppos) +{ + struct nvadsp_debug_log *logger = file->private_data; + struct device *dev = logger->dev; + ssize_t ret_num_char = 1; + char last_char; + +loop: + last_char = logger->debug_ram_rdr[logger->ram_iter]; + + if ((last_char != EOT) && (last_char != 0)) { +#if CONFIG_ADSP_DRAM_LOG_WITH_TAG + if ((last_char == '\n') || (last_char == '\r')) { + + if (copy_to_user(buf, ADSP_TAG, sizeof(ADSP_TAG) - 1)) { + dev_err(dev, "%s failed\n", __func__); + ret_num_char = -EFAULT; + goto exit; + } + ret_num_char = sizeof(ADSP_TAG) - 1; + + } else +#endif + if (copy_to_user(buf, &last_char, 1)) { + dev_err(dev, "%s failed\n", __func__); + ret_num_char = -EFAULT; + goto exit; + } + + logger->ram_iter = + (logger->ram_iter + 1) % logger->debug_ram_sz; + goto exit; + } + + complete(&logger->complete); + ret_num_char = wait_event_interruptible_timeout(logger->wait_queue, + logger->debug_ram_rdr[logger->ram_iter] != EOT, + msecs_to_jiffies(LOGGER_TIMEOUT)); + if (ret_num_char == -ERESTARTSYS) { + goto exit; + } + goto loop; + +exit: + return ret_num_char; +} + +static const struct file_operations adsp_logger_operations = { + .read = adsp_logger_read, + .open = adsp_logger_open, + .release = adsp_logger_release, + .llseek = generic_file_llseek, + .flush = adsp_logger_flush, +}; + +static int adsp_create_debug_logger(struct dentry *adsp_debugfs_root) +{ + struct nvadsp_debug_log *logger = &priv.logger; + struct device *dev = &priv.pdev->dev; + int ret = 0; + + if (IS_ERR_OR_NULL(adsp_debugfs_root)) { + ret = -ENOENT; + goto err_out; + } + + atomic_set(&logger->is_opened, 1); + init_waitqueue_head(&logger->wait_queue); + init_completion(&logger->complete); + if (!debugfs_create_file("adsp_logger", S_IRUGO, + adsp_debugfs_root, logger, + &adsp_logger_operations)) { + dev_err(dev, "unable to create adsp logger debug fs file\n"); + ret = -ENOENT; + } + +err_out: + return ret; +} +#endif + +bool is_adsp_dram_addr(u64 addr) +{ + int i; + struct resource **dram = priv.dram_region; + + for (i = 0; i < ADSP_MAX_DRAM_MAP; i++) { + if ((dram[i]->start) && (addr >= dram[i]->start) && + (addr <= dram[i]->end)) + return true; + } + return false; +} + +int adsp_add_load_mappings(phys_addr_t pa, void *mapping, int len) +{ + if (map_idx >= NM_LOAD_MAPPINGS) + return -EINVAL; + + adsp_map[map_idx].da = pa; + adsp_map[map_idx].va = mapping; + adsp_map[map_idx].len = len; + map_idx++; + return 0; +} + +void *nvadsp_da_to_va_mappings(u64 da, int len) +{ + void *ptr = NULL; + int i; + + for (i = 0; i < map_idx; i++) { + int offset = da - adsp_map[i].da; + + /* try next carveout if da is too small */ + if (offset < 0) + continue; + + /* try next carveout if da is too large */ + if (offset + len > adsp_map[i].len) + continue; + + ptr = adsp_map[i].va + offset; + break; + } + return ptr; +} +EXPORT_SYMBOL(nvadsp_da_to_va_mappings); + +void *nvadsp_alloc_coherent(size_t size, dma_addr_t *da, gfp_t flags) +{ + struct device *dev; + void *va = NULL; + + if (!priv.pdev) { + pr_err("ADSP Driver is not initialized\n"); + goto end; + } + + dev = &priv.pdev->dev; + va = dma_alloc_coherent(dev, size, da, flags); + if (!va) { + dev_err(dev, "unable to allocate the memory for size %lu\n", + size); + goto end; + } + WARN(!is_adsp_dram_addr(*da), "bus addr %llx beyond %x\n", + *da, UINT_MAX); +end: + return va; +} +EXPORT_SYMBOL(nvadsp_alloc_coherent); + +void nvadsp_free_coherent(size_t size, void *va, dma_addr_t da) +{ + struct device *dev; + + if (!priv.pdev) { + pr_err("ADSP Driver is not initialized\n"); + return; + } + dev = &priv.pdev->dev; + dma_free_coherent(dev, size, va, da); +} +EXPORT_SYMBOL(nvadsp_free_coherent); + +struct elf32_shdr * +nvadsp_get_section(const struct firmware *fw, char *sec_name) +{ + int i; + struct device *dev = &priv.pdev->dev; + const u8 *elf_data = fw->data; + struct elf32_hdr *ehdr = (struct elf32_hdr *)elf_data; + struct elf32_shdr *shdr; + const char *name_table; + + /* look for the resource table and handle it */ + shdr = (struct elf32_shdr *)(elf_data + ehdr->e_shoff); + name_table = elf_data + shdr[ehdr->e_shstrndx].sh_offset; + + for (i = 0; i < ehdr->e_shnum; i++, shdr++) + if (!strcmp(name_table + shdr->sh_name, sec_name)) { + dev_dbg(dev, "found the section %s\n", + name_table + shdr->sh_name); + return shdr; + } + return NULL; +} + +static inline void __maybe_unused dump_global_symbol_table(void) +{ + struct device *dev = &priv.pdev->dev; + struct global_sym_info *table = priv.adsp_glo_sym_tbl; + int num_ent; + int i; + + if (!table) { + dev_err(dev, "no table not created\n"); + return; + } + num_ent = table[0].addr; + dev_info(dev, "total number of entries in global symbol table %d\n", + num_ent); + + pr_info("NAME ADDRESS TYPE\n"); + for (i = 1; i < num_ent; i++) + pr_info("%s %x %s\n", table[i].name, table[i].addr, + ELF32_ST_TYPE(table[i].info) == STT_FUNC ? + "STT_FUNC" : "STT_OBJECT"); +} + +static int +create_global_symbol_table(const struct firmware *fw) +{ + int i; + struct device *dev = &priv.pdev->dev; + struct elf32_shdr *sym_shdr = nvadsp_get_section(fw, ".symtab"); + struct elf32_shdr *str_shdr = nvadsp_get_section(fw, ".strtab"); + const u8 *elf_data = fw->data; + const char *name_table; + /* The first entry stores the number of entries in the array */ + int num_ent = 1; + struct elf32_sym *sym; + struct elf32_sym *last_sym; + + sym = (struct elf32_sym *)(elf_data + sym_shdr->sh_offset); + name_table = elf_data + str_shdr->sh_offset; + + num_ent += sym_shdr->sh_size / sizeof(struct elf32_sym); + priv.adsp_glo_sym_tbl = devm_kzalloc(dev, + sizeof(struct global_sym_info) * num_ent, GFP_KERNEL); + if (!priv.adsp_glo_sym_tbl) + return -ENOMEM; + + last_sym = sym + num_ent; + + for (i = 1; sym < last_sym; sym++) { + unsigned char info = sym->st_info; + unsigned char type = ELF32_ST_TYPE(info); + if ((ELF32_ST_BIND(sym->st_info) == STB_GLOBAL) && + ((type == STT_OBJECT) || (type == STT_FUNC))) { + char *name = priv.adsp_glo_sym_tbl[i].name; + + strlcpy(name, name_table + sym->st_name, SYM_NAME_SZ); + priv.adsp_glo_sym_tbl[i].addr = sym->st_value; + priv.adsp_glo_sym_tbl[i].info = info; + i++; + } + } + priv.adsp_glo_sym_tbl[0].addr = i; + return 0; +} + +struct global_sym_info *find_global_symbol(const char *sym_name) +{ + struct device *dev = &priv.pdev->dev; + struct global_sym_info *table = priv.adsp_glo_sym_tbl; + int num_ent; + int i; + + if (unlikely(!table)) { + dev_err(dev, "symbol table not present\n"); + return NULL; + } + num_ent = table[0].addr; + + for (i = 1; i < num_ent; i++) { + if (!strncmp(table[i].name, sym_name, SYM_NAME_SZ)) + return &table[i]; + } + return NULL; +} + +static void *get_mailbox_shared_region(const struct firmware *fw) +{ + struct device *dev; + struct elf32_shdr *shdr; + int addr; + int size; + + if (!priv.pdev) { + pr_err("ADSP Driver is not initialized\n"); + return ERR_PTR(-EINVAL); + } + + dev = &priv.pdev->dev; + + shdr = nvadsp_get_section(fw, MAILBOX_REGION); + if (!shdr) { + dev_err(dev, "section %s not found\n", MAILBOX_REGION); + return ERR_PTR(-EINVAL); + } + + dev_dbg(dev, "the shared section is present at 0x%x\n", shdr->sh_addr); + addr = shdr->sh_addr; + size = shdr->sh_size; + return nvadsp_da_to_va_mappings(addr, size); +} + +static void copy_io_in_l(void *to, const void *from, int sz) +{ + int i; + for (i = 0; i < sz; i += 4) { + int val = *(int *)(from + i); + writel(val, to + i); + } +} + +static int nvadsp_os_elf_load(const struct firmware *fw) +{ + struct device *dev = &priv.pdev->dev; + struct nvadsp_drv_data *drv_data = platform_get_drvdata(priv.pdev); + struct elf32_hdr *ehdr; + struct elf32_phdr *phdr; + int i, ret = 0; + const u8 *elf_data = fw->data; + + ehdr = (struct elf32_hdr *)elf_data; + phdr = (struct elf32_phdr *)(elf_data + ehdr->e_phoff); + + /* go through the available ELF segments */ + for (i = 0; i < ehdr->e_phnum; i++, phdr++) { + void *va; + u32 da = phdr->p_paddr; + u32 memsz = phdr->p_memsz; + u32 filesz = phdr->p_filesz; + u32 offset = phdr->p_offset; + + if (phdr->p_type != PT_LOAD) + continue; + + dev_dbg(dev, "phdr: type %d da 0x%x memsz 0x%x filesz 0x%x\n", + phdr->p_type, da, memsz, filesz); + + va = nvadsp_da_to_va_mappings(da, filesz); + if (!va) { + dev_err(dev, "no va for da 0x%x filesz 0x%x\n", + da, filesz); + ret = -EINVAL; + break; + } + + if (filesz > memsz) { + dev_err(dev, "bad phdr filesz 0x%x memsz 0x%x\n", + filesz, memsz); + ret = -EINVAL; + break; + } + + if (offset + filesz > fw->size) { + dev_err(dev, "truncated fw: need 0x%x avail 0x%zx\n", + offset + filesz, fw->size); + ret = -EINVAL; + break; + } + + /* put the segment where the remote processor expects it */ + if (filesz) { + if (!is_adsp_dram_addr(da)) { + drv_data->state.evp_ptr = va; + memcpy(drv_data->state.evp, + elf_data + offset, filesz); + } else + memcpy(va, elf_data + offset, filesz); + } + } + + return ret; +} + +static int allocate_memory_for_adsp_os(void) +{ + struct platform_device *pdev = priv.pdev; + struct device *dev = &pdev->dev; +#if defined(CONFIG_TEGRA_NVADSP_ON_SMMU) + dma_addr_t addr; +#else + phys_addr_t addr; +#endif + void *dram_va; + size_t size; + int ret = 0; + + addr = priv.adsp_os_addr; + size = priv.adsp_os_size; +#if defined(CONFIG_TEGRA_NVADSP_ON_SMMU) + dram_va = dma_alloc_at_coherent(dev, size, &addr, GFP_KERNEL); + if (!dram_va) { + dev_err(dev, "unable to allocate SMMU pages\n"); + ret = -ENOMEM; + goto end; + } +#else + dram_va = ioremap_nocache(addr, size); + if (!dram_va) { + dev_err(dev, "remap failed for addr 0x%llx\n", addr); + ret = -ENOMEM; + goto end; + } +#endif + adsp_add_load_mappings(addr, dram_va, size); +end: + return ret; +} + +static void deallocate_memory_for_adsp_os(struct device *dev) +{ +#if defined(CONFIG_TEGRA_NVADSP_ON_SMMU) + void *va = nvadsp_da_to_va_mappings(priv.adsp_os_addr, + priv.adsp_os_size); + dma_free_coherent(dev, priv.adsp_os_addr, va, priv.adsp_os_size); +#endif +} + +static int __nvadsp_os_secload(struct platform_device *pdev) +{ + struct nvadsp_drv_data *drv_data = platform_get_drvdata(pdev); + dma_addr_t addr = drv_data->adsp_mem[ACSR_ADDR]; + size_t size = drv_data->adsp_mem[ACSR_SIZE]; + struct nvadsp_shared_mem *shared_mem; + struct device *dev = &pdev->dev; + void *dram_va; + + dram_va = dma_alloc_at_coherent(dev, size, &addr, GFP_KERNEL); + if (!dram_va) { + dev_err(dev, "unable to allocate shared region\n"); + return -ENOMEM; + } + + shared_mem = dram_va; + drv_data->shared_adsp_os_data = shared_mem; + /* set logger strcuture with required properties */ + priv.logger.debug_ram_rdr = shared_mem->os_args.logger; + priv.logger.debug_ram_sz = sizeof(shared_mem->os_args.logger); + priv.logger.dev = dev; + priv.adsp_os_fw_loaded = true; + +#ifdef CONFIG_DEBUG_FS + wake_up(&priv.logger.wait_queue); +#endif + return 0; +} + +int nvadsp_os_load(void) +{ + struct nvadsp_shared_mem *shared_mem; + struct nvadsp_drv_data *drv_data; + const struct firmware *fw; + struct device *dev; + int ret = 0; + + if (!priv.pdev) { + pr_err("ADSP Driver is not initialized\n"); + ret = -EINVAL; + goto end; + } + + mutex_lock(&priv.fw_load_lock); + if (priv.adsp_os_fw_loaded) + goto end; + + dev = &priv.pdev->dev; + + drv_data = platform_get_drvdata(priv.pdev); + + if (drv_data->adsp_os_secload) { + dev_info(dev, "ADSP OS firmware already loaded\n"); + ret = __nvadsp_os_secload(priv.pdev); + goto end; + } + + ret = request_firmware(&fw, NVADSP_FIRMWARE, dev); + if (ret < 0) { + dev_err(dev, "reqest firmware for %s failed with %d\n", + NVADSP_FIRMWARE, ret); + goto end; + } + + ret = create_global_symbol_table(fw); + if (ret) { + dev_err(dev, "unable to create global symbol table\n"); + goto release_firmware; + } + + ret = allocate_memory_for_adsp_os(); + if (ret) { + dev_err(dev, "unable to allocate memory for adsp os\n"); + goto release_firmware; + } + + shared_mem = get_mailbox_shared_region(fw); + drv_data->shared_adsp_os_data = shared_mem; + /* set logger strcuture with required properties */ + priv.logger.debug_ram_rdr = shared_mem->os_args.logger; + priv.logger.debug_ram_sz = sizeof(shared_mem->os_args.logger); + priv.logger.dev = dev; + + dev_info(dev, "Loading ADSP OS firmware %s\n", NVADSP_FIRMWARE); + + ret = nvadsp_os_elf_load(fw); + if (ret) { + dev_err(dev, "failed to load %s\n", NVADSP_FIRMWARE); + goto deallocate_os_memory; + } + + ret = dram_app_mem_init(priv.app_alloc_addr, priv.app_size); + if (ret) { + dev_err(dev, "Memory allocation dynamic apps failed\n"); + goto deallocate_os_memory; + } + priv.os_firmware = fw; + priv.adsp_os_fw_loaded = true; +#ifdef CONFIG_DEBUG_FS + wake_up(&priv.logger.wait_queue); +#endif + mutex_unlock(&priv.fw_load_lock); + return 0; + +deallocate_os_memory: + deallocate_memory_for_adsp_os(dev); +release_firmware: + release_firmware(fw); +end: + mutex_unlock(&priv.fw_load_lock); + return ret; +} +EXPORT_SYMBOL(nvadsp_os_load); + +/* + * Static adsp freq to emc freq lookup table + * + * arg: + * adspfreq - adsp freq in KHz + * return: + * 0 - min emc freq + * > 0 - expected emc freq at this adsp freq + */ +u32 adsp_to_emc_freq(u32 adspfreq) +{ + /* + * Vote on memory bus frequency based on adsp frequency + * cpu rate is in kHz, emc rate is in Hz + */ + if (adspfreq >= 204800) + return 102000; /* adsp >= 204.8 MHz, emc 102 MHz */ + else + return 0; /* emc min */ +} + +static int nvadsp_set_ape_emc_freq(struct nvadsp_drv_data *drv_data) +{ + unsigned long ape_emc_freq = drv_data->ape_emc_freq * 1000; /* in Hz */ + struct device *dev = &priv.pdev->dev; + int ret; + +#ifdef CONFIG_TEGRA_ADSP_DFS + /* pass adsp freq in KHz. adsp_emc_freq in Hz */ + ape_emc_freq = adsp_to_emc_freq(drv_data->adsp_freq / 1000) * 1000; +#endif + dev_dbg(dev, "requested adsp cpu freq %luKHz", + drv_data->adsp_freq / 1000); + dev_dbg(dev, "ape.emc freq %luHz\n", ape_emc_freq / 1000); + + + if (!ape_emc_freq) + return 0; + + ret = clk_set_rate(drv_data->ape_emc_clk, ape_emc_freq); + + dev_dbg(dev, "ape.emc freq %luKHz\n", + clk_get_rate(drv_data->ape_emc_clk) / 1000); + return ret; +} + +static int nvadsp_set_ape_freq(struct nvadsp_drv_data *drv_data) +{ + unsigned long ape_freq = drv_data->ape_freq * 1000; /* in Hz*/ + struct device *dev = &priv.pdev->dev; + int ret; + +#ifdef CONFIG_TEGRA_ADSP_ACTMON + ape_freq = drv_data->adsp_freq / ADSP_TO_APE_CLK_RATIO; +#endif + dev_dbg(dev, "ape freq %luKHz", ape_freq / 1000); + + if (!ape_freq) + return 0; + + ret = clk_set_rate(drv_data->ape_clk, ape_freq); + + dev_dbg(dev, "ape freq %luKHz\n", + clk_get_rate(drv_data->ape_clk) / 1000); + return ret; +} + +static int set_adsp_clks_and_timer_prescalar(struct nvadsp_drv_data *drv_data) +{ + struct nvadsp_shared_mem *shared_mem = drv_data->shared_adsp_os_data; + struct nvadsp_os_args *os_args = &shared_mem->os_args; + struct device *dev = &priv.pdev->dev; + unsigned long max_adsp_freq; + unsigned long adsp_freq; + u32 max_index; + u32 cur_index; + int ret = 0; + + adsp_freq = drv_data->adsp_freq * 1000; /* in Hz*/ + + max_adsp_freq = clk_round_rate(drv_data->adsp_cpu_clk, + ULONG_MAX); + max_index = max_adsp_freq / MIN_ADSP_FREQ; + cur_index = adsp_freq / MIN_ADSP_FREQ; + + + if (!adsp_freq) + /* Set max adsp boot freq */ + cur_index = max_index; + + if (adsp_freq % MIN_ADSP_FREQ) { + if (cur_index >= max_index) + cur_index = max_index; + else + cur_index++; + } else if (cur_index >= max_index) + cur_index = max_index; + + /* + * timer interval = (prescalar + 1) * (count + 1) / periph_freq + * therefore for 0 count, + * 1 / TIMER_CLK_HZ = (prescalar + 1) / periph_freq + * Hence, prescalar = periph_freq / TIMER_CLK_HZ - 1 + */ + os_args->timer_prescalar = cur_index - 1; + + adsp_freq = cur_index * MIN_ADSP_FREQ; + + ret = clk_set_rate(drv_data->adsp_cpu_clk, adsp_freq); + if (ret) + goto end; + + drv_data->adsp_freq = adsp_freq / 1000; /* adsp_freq in KHz*/ + +end: + dev_dbg(dev, "adsp cpu freq %luKHz\n", + clk_get_rate(drv_data->adsp_cpu_clk) / 1000); + dev_dbg(dev, "timer prescalar %x\n", os_args->timer_prescalar); + + return ret; +} + +static int set_adsp_clks(struct nvadsp_drv_data *drv_data) +{ + struct nvadsp_shared_mem *shared_mem = drv_data->shared_adsp_os_data; + struct nvadsp_os_args *os_args = &shared_mem->os_args; + struct platform_device *pdev = drv_data->pdev; + struct device *dev = &pdev->dev; + unsigned long max_adsp_freq; + unsigned long adsp_freq; + int ret = 0; + + adsp_freq = drv_data->adsp_freq_hz; /* in Hz*/ + max_adsp_freq = clk_round_rate(drv_data->adsp_clk, ULONG_MAX); + + /* Set max adsp boot freq */ + if (!adsp_freq) + adsp_freq = max_adsp_freq; + + ret = clk_set_rate(drv_data->adsp_clk, adsp_freq); + if (ret) { + dev_err(dev, "setting adsp_freq:%luHz failed.\n", adsp_freq); + dev_err(dev, "max_adsp_freq:%luHz\n", max_adsp_freq); + goto end; + } + + drv_data->adsp_freq = adsp_freq / 1000; /* adsp_freq in KHz*/ + drv_data->adsp_freq_hz = adsp_freq; + os_args->adsp_freq_hz = adsp_freq; +end: + dev_dbg(dev, "adsp cpu freq %luKHz\n", + clk_get_rate(drv_data->adsp_clk) / 1000); + return ret; +} + +static int __deassert_adsp(struct nvadsp_drv_data *drv_data) +{ + struct device *dev = &priv.pdev->dev; + + if (drv_data->adsp_unit_fpga) { + dev_info(dev, "De-asserting ADSP UNIT-FPGA\n"); + writel(drv_data->unit_fpga_reset[ADSP_DEASSERT], + priv.unit_fpga_reset_reg); + return 0; + } + + if (drv_data->adsp_clk) { + dev_dbg(dev, "deasserting adsp...\n"); + tegra_periph_reset_deassert(drv_data->adsp_clk); + udelay(200); + return 0; + } + + return -EINVAL; +} + +static int nvadsp_deassert_adsp(struct nvadsp_drv_data *drv_data) +{ + int ret = -EINVAL; + + if (drv_data->deassert_adsp) + ret = drv_data->deassert_adsp(drv_data); + + return ret; +} + +static int __assert_adsp(struct nvadsp_drv_data *drv_data) +{ + struct device *dev = &priv.pdev->dev; + + if (drv_data->adsp_unit_fpga) { + if (drv_data->unit_fpga_reset[ADSP_ASSERT]) { + dev_info(dev, "Asserting ADSP UNIT-FPGA\n"); + writel(drv_data->unit_fpga_reset[ADSP_ASSERT], + priv.unit_fpga_reset_reg); + } + return 0; + } + + if (drv_data->adsp_clk) { + tegra_periph_reset_assert(drv_data->adsp_clk); + udelay(200); + return 0; + } + + return -EINVAL; +} + +static int nvadsp_assert_adsp(struct nvadsp_drv_data *drv_data) +{ + int ret = -EINVAL; + + if (drv_data->assert_adsp) + ret = drv_data->assert_adsp(drv_data); + + return ret; +} + +static int nvadsp_set_boot_freqs(struct nvadsp_drv_data *drv_data) +{ + struct device *dev = &priv.pdev->dev; + struct device_node *node = dev->of_node; + int ret = 0; + + /* on Unit-FPGA do not set clocks, return success */ + if (drv_data->adsp_unit_fpga) + return 0; + + if (of_device_is_compatible(node, "nvidia,tegra210-adsp")) { + if (drv_data->adsp_cpu_clk) { + ret = set_adsp_clks_and_timer_prescalar(drv_data); + if (ret) + goto end; + } else { + ret = -EINVAL; + goto end; + } + } else { + if (drv_data->adsp_clk) { + ret = set_adsp_clks(drv_data); + if (ret) + goto end; + } else { + ret = -EINVAL; + goto end; + } + } + + if (drv_data->ape_clk) { + ret = nvadsp_set_ape_freq(drv_data); + if (ret) + goto end; + } + + if (drv_data->ape_emc_clk) { + ret = nvadsp_set_ape_emc_freq(drv_data); + if (ret) + goto end; + } + +end: + return ret; +} + +static int wait_for_adsp_os_load_complete(void) +{ + struct device *dev = &priv.pdev->dev; + uint32_t data; + status_t ret; + + ret = nvadsp_mbox_recv(&adsp_com_mbox, &data, + true, ADSP_OS_LOAD_TIMEOUT); + if (ret) { + dev_err(dev, "ADSP OS loading timed out\n"); + goto end; + } + dev_dbg(dev, "ADSP has been %s\n", + data == ADSP_OS_BOOT_COMPLETE ? "BOOTED" : "RESUMED"); + + switch (data) { + case ADSP_OS_BOOT_COMPLETE: + ret = load_adsp_static_apps(); + break; + case ADSP_OS_RESUME: + default: + break; + } +end: + return ret; +} + +static int __nvadsp_os_start(void) +{ + struct nvadsp_drv_data *drv_data; + struct device *dev; + int ret = 0; + + dev = &priv.pdev->dev; + drv_data = platform_get_drvdata(priv.pdev); + + + dev_dbg(dev, "ADSP is booting on %s\n", + drv_data->adsp_unit_fpga ? "UNIT-FPGA" : "SILICON"); + + nvadsp_assert_adsp(drv_data); + + if (!drv_data->adsp_os_secload) { + dev_dbg(dev, "Copying EVP...\n"); + copy_io_in_l(drv_data->state.evp_ptr, + drv_data->state.evp, + AMC_EVP_SIZE); + } + + dev_dbg(dev, "Setting freqs\n"); + ret = nvadsp_set_boot_freqs(drv_data); + if (ret) { + dev_err(dev, "failed to set boot freqs\n"); + goto end; + } + + dev_dbg(dev, "De-asserting adsp\n"); + ret = nvadsp_deassert_adsp(drv_data); + if (ret) { + dev_err(dev, "failed to deassert ADSP\n"); + goto end; + } + + dev_dbg(dev, "Waiting for ADSP OS to boot up...\n"); + + ret = wait_for_adsp_os_load_complete(); + if (ret) { + dev_err(dev, "Unable to start ADSP OS\n"); + goto end; + } + dev_dbg(dev, "ADSP OS boot up... Done!\n"); + +#ifdef CONFIG_TEGRA_ADSP_DFS + ret = adsp_dfs_core_init(priv.pdev); + if (ret) { + dev_err(dev, "adsp dfs initialization failed\n"); + goto err; + } +#endif + +#ifdef CONFIG_TEGRA_ADSP_ACTMON + ret = ape_actmon_init(priv.pdev); + if (ret) { + dev_err(dev, "ape actmon initialization failed\n"); + goto err; + } +#endif + +#ifdef CONFIG_TEGRA_ADSP_CPUSTAT + ret = adsp_cpustat_init(priv.pdev); + if (ret) { + dev_err(dev, "adsp cpustat initialisation failed\n"); + goto err; + } +#endif +end: + return ret; + +#if defined(CONFIG_TEGRA_ADSP_DFS) || defined(CONFIG_TEGRA_ADSP_CPUSTAT) +err: + __nvadsp_os_stop(true); + return ret; +#endif +} + +static void dump_adsp_logs(void) +{ + int i = 0; + char buff[DUMP_BUFF] = { }; + int buff_iter = 0; + char last_char; + struct nvadsp_debug_log *logger = &priv.logger; + struct device *dev = &priv.pdev->dev; + char *ptr = logger->debug_ram_rdr; + + dev_err(dev, "Dumping ADSP logs ........\n"); + + for (i = 0; i < logger->debug_ram_sz; i++) { + last_char = *(ptr + i); + if ((last_char != EOT) && (last_char != 0)) { + if ((last_char == '\n') || (last_char == '\r') || + (buff_iter == DUMP_BUFF)) { + dev_err(dev, "[ADSP OS] %s\n", buff); + memset(buff, 0, sizeof(buff)); + buff_iter = 0; + } else { + buff[buff_iter++] = last_char; + } + } + } + dev_err(dev, "End of ADSP log dump .....\n"); +} + +static void print_agic_irq_states(void) +{ + struct device *dev = &priv.pdev->dev; + int i; + + for (i = INT_AGIC_START; i < INT_AGIC_END; i++) { + dev_info(dev, "irq %d is %s and %s\n", i, + tegra_agic_irq_is_pending(i) ? + "pending" : "not pending", + tegra_agic_irq_is_active(i) ? + "active" : "not active"); + } +} + +void dump_adsp_sys(void) +{ + if (!priv.pdev) { + pr_err("ADSP Driver is not initialized\n"); + return; + } + + dump_adsp_logs(); + dump_mailbox_regs(); + print_agic_irq_states(); +} +EXPORT_SYMBOL(dump_adsp_sys); + +int nvadsp_os_start(void) +{ + struct nvadsp_drv_data *drv_data; + struct device *dev; + int ret = 0; + + if (!priv.pdev) { + pr_err("ADSP Driver is not initialized\n"); + ret = -EINVAL; + goto end; + } + + drv_data = platform_get_drvdata(priv.pdev); + dev = &priv.pdev->dev; + + /* check if fw is loaded then start the adsp os */ + if (!priv.adsp_os_fw_loaded) { + dev_err(dev, "Call to nvadsp_os_load not made\n"); + ret = -EINVAL; + goto end; + } + + mutex_lock(&priv.os_run_lock); + /* if adsp is started/running exit gracefully */ + if (priv.os_running) + goto unlock; + +#ifdef CONFIG_PM + ret = pm_runtime_get_sync(&priv.pdev->dev); + if (ret < 0) + goto unlock; +#endif + ret = __nvadsp_os_start(); + if (ret) { + priv.os_running = drv_data->adsp_os_running = false; + /* if start fails call pm suspend of adsp driver */ + dev_err(dev, "adsp failed to boot with ret = %d\n", ret); + dump_adsp_sys(); +#ifdef CONFIG_PM + pm_runtime_put_sync(&priv.pdev->dev); +#endif + goto unlock; + + } + priv.os_running = drv_data->adsp_os_running = true; +#if defined(CONFIG_TEGRA_ADSP_FILEIO) + if (!drv_data->adspff_init) { + ret = adspff_init(); + if (!ret) + drv_data->adspff_init = true; + } +#endif + drv_data->adsp_os_suspended = false; +#ifdef CONFIG_DEBUG_FS + wake_up(&priv.logger.wait_queue); +#endif +unlock: + mutex_unlock(&priv.os_run_lock); +end: + return ret; +} +EXPORT_SYMBOL(nvadsp_os_start); + +static int __nvadsp_os_suspend(void) +{ + struct device *dev = &priv.pdev->dev; + struct nvadsp_drv_data *drv_data; + int ret; + + drv_data = platform_get_drvdata(priv.pdev); + +#ifdef CONFIG_TEGRA_ADSP_ACTMON + ape_actmon_exit(priv.pdev); +#endif + +#ifdef CONFIG_TEGRA_ADSP_DFS + adsp_dfs_core_exit(priv.pdev); +#endif + +#ifdef CONFIG_TEGRA_ADSP_CPUSTAT + adsp_cpustat_exit(priv.pdev); +#endif + + ret = nvadsp_mbox_send(&adsp_com_mbox, ADSP_OS_SUSPEND, + NVADSP_MBOX_SMSG, true, UINT_MAX); + if (ret) { + dev_err(dev, "failed to send with adsp com mbox\n"); + goto out; + } + + dev_dbg(dev, "Waiting for ADSP OS suspend...\n"); + ret = wait_for_completion_interruptible_timeout(&entered_wfi, + msecs_to_jiffies(ADSP_WFE_TIMEOUT)); + if (WARN_ON(ret <= 0)) { + dev_err(dev, "Unable to suspend ADSP OS\n"); + ret = -EINVAL; + goto out; + } + ret = 0; + dev_dbg(dev, "ADSP OS suspended!\n"); + + drv_data->adsp_os_suspended = true; + + nvadsp_assert_adsp(drv_data); + + out: + return ret; +} + +static void __nvadsp_os_stop(bool reload) +{ + const struct firmware *fw = priv.os_firmware; + struct nvadsp_drv_data *drv_data; + struct device *dev; + int err = 0; + + dev = &priv.pdev->dev; + drv_data = platform_get_drvdata(priv.pdev); + +#ifdef CONFIG_TEGRA_ADSP_ACTMON + ape_actmon_exit(priv.pdev); +#endif + +#ifdef CONFIG_TEGRA_ADSP_DFS + adsp_dfs_core_exit(priv.pdev); +#endif + +#ifdef CONFIG_TEGRA_ADSP_CPUSTAT + adsp_cpustat_exit(priv.pdev); +#endif +#if defined(CONFIG_TEGRA_ADSP_FILEIO) + if (drv_data->adspff_init) { + adspff_exit(); + drv_data->adspff_init = false; + } +#endif + + writel(ENABLE_MBOX2_FULL_INT, priv.hwmailbox_base + HWMBOX2_REG); + err = wait_for_completion_interruptible_timeout(&entered_wfi, + msecs_to_jiffies(ADSP_WFE_TIMEOUT)); + writel(DISABLE_MBOX2_FULL_INT, priv.hwmailbox_base + HWMBOX2_REG); + + /* + * ADSP needs to be in WFI/WFE state to properly reset it. + * However, when ADSPOS is getting stopped on error path, + * it cannot gaurantee that ADSP is in WFI/WFE state. + * Reset it in either case. On failure, whole APE reset is + * required (happens on next APE power domain cycle). + */ + nvadsp_assert_adsp(drv_data); + + /* Don't reload ADSPOS if ADSP state is not WFI/WFE */ + if (WARN_ON(err <= 0)) { + dev_err(dev, "ADSP is unable to enter wfi state\n"); + goto end; + } + + if (reload && !drv_data->adsp_os_secload) { + struct nvadsp_debug_log *logger = &priv.logger; + +#ifdef CONFIG_DEBUG_FS + wake_up(&logger->wait_queue); + /* wait for LOGGER_TIMEOUT to complete filling the buffer */ + wait_for_completion_interruptible_timeout(&logger->complete, + msecs_to_jiffies(LOGGER_COMPLETE_TIMEOUT)); +#endif + /* + * move ram iterator to 0, since after restart the iterator + * will be pointing to initial position of start. + */ + logger->debug_ram_rdr[0] = EOT; + logger->ram_iter = 0; + /* load a fresh copy of adsp.elf */ + if (nvadsp_os_elf_load(fw)) + dev_err(dev, "failed to reload %s\n", NVADSP_FIRMWARE); + } + + end: + return; +} + + +void nvadsp_os_stop(void) +{ + struct nvadsp_drv_data *drv_data; + struct device *dev; + + if (!priv.pdev) { + pr_err("ADSP Driver is not initialized\n"); + return; + } + + dev = &priv.pdev->dev; + drv_data = platform_get_drvdata(priv.pdev); + + mutex_lock(&priv.os_run_lock); + /* check if os is running else exit */ + if (!priv.os_running) + goto end; + + __nvadsp_os_stop(true); + + priv.os_running = drv_data->adsp_os_running = false; + +#ifdef CONFIG_PM + if (pm_runtime_put_sync(dev)) + dev_err(dev, "failed in pm_runtime_put_sync\n"); +#endif +end: + mutex_unlock(&priv.os_run_lock); +} +EXPORT_SYMBOL(nvadsp_os_stop); + +int nvadsp_os_suspend(void) +{ + struct nvadsp_drv_data *drv_data; + int ret = -EINVAL; + + if (!priv.pdev) { + pr_err("ADSP Driver is not initialized\n"); + goto end; + } + + /* + * No os suspend/stop on linsim as + * APE can be reset only once. + */ + if (tegra_platform_is_linsim()) + goto end; + + drv_data = platform_get_drvdata(priv.pdev); + + mutex_lock(&priv.os_run_lock); + /* check if os is running else exit */ + if (!priv.os_running) { + ret = 0; + goto unlock; + } + ret = __nvadsp_os_suspend(); + if (!ret) { +#ifdef CONFIG_PM + struct device *dev = &priv.pdev->dev; + ret = pm_runtime_put_sync(&priv.pdev->dev); + if (ret) + dev_err(dev, "failed in pm_runtime_put_sync\n"); +#endif + priv.os_running = drv_data->adsp_os_running = false; + } else { + dev_err(&priv.pdev->dev, "suspend failed with %d\n", ret); + dump_adsp_sys(); + } +unlock: + mutex_unlock(&priv.os_run_lock); +end: + return ret; +} +EXPORT_SYMBOL(nvadsp_os_suspend); + +static void nvadsp_os_restart(struct work_struct *work) +{ + struct nvadsp_os_data *data = + container_of(work, struct nvadsp_os_data, restart_os_work); + struct nvadsp_drv_data *drv_data = platform_get_drvdata(data->pdev); + int wdt_virq = drv_data->agic_irqs[WDT_VIRQ]; + struct device *dev = &data->pdev->dev; + + disable_irq(wdt_virq); + dump_adsp_sys(); + nvadsp_os_stop(); + + if (tegra_agic_irq_is_active(ADSP_WDT_INT)) { + dev_info(dev, "wdt interrupt is active hence clearing\n"); + tegra_agic_clear_active(ADSP_WDT_INT); + } + + if (tegra_agic_irq_is_pending(ADSP_WDT_INT)) { + dev_info(dev, "wdt interrupt is pending hence clearing\n"); + tegra_agic_clear_pending(ADSP_WDT_INT); + } + + dev_info(dev, "wdt interrupt is not pending or active...enabling\n"); + enable_irq(wdt_virq); + + data->adsp_num_crashes++; + if (data->adsp_num_crashes >= ALLOWED_CRASHES) { + /* making pdev NULL so that externally start is not called */ + priv.pdev = NULL; + dev_crit(dev, "ADSP has crashed too many times(%d)\n", + data->adsp_num_crashes); + return; + } + + if (nvadsp_os_start()) + dev_crit(dev, "Unable to restart ADSP OS\n"); +} + +static irqreturn_t adsp_wfi_handler(int irq, void *arg) +{ + struct nvadsp_os_data *data = arg; + struct device *dev = &data->pdev->dev; + + dev_dbg(dev, "%s\n", __func__); + complete(&entered_wfi); + + return IRQ_HANDLED; +} + +static irqreturn_t adsp_wdt_handler(int irq, void *arg) +{ + struct nvadsp_os_data *data = arg; + struct nvadsp_drv_data *drv_data; + struct device *dev = &data->pdev->dev; + + drv_data = platform_get_drvdata(data->pdev); + if (!drv_data->adsp_unit_fpga) { + dev_crit(dev, "ADSP OS Hanged or Crashed! Restarting...\n"); + schedule_work(&data->restart_os_work); + } else { + dev_crit(dev, "ADSP OS Hanged or Crashed!\n"); + } + return IRQ_HANDLED; +} + +void nvadsp_get_os_version(char *buf, int buf_size) +{ + struct nvadsp_drv_data *drv_data; + struct nvadsp_shared_mem *shared_mem; + struct nvadsp_os_info *os_info; + + memset(buf, 0, buf_size); + + if (!priv.pdev) + return; + + drv_data = platform_get_drvdata(priv.pdev); + shared_mem = drv_data->shared_adsp_os_data; + if (shared_mem) { + os_info = &shared_mem->os_info; + strlcpy(buf, os_info->version, buf_size); + } else { + strlcpy(buf, "unavailable", buf_size); + } +} +EXPORT_SYMBOL(nvadsp_get_os_version); + +#ifdef CONFIG_DEBUG_FS +static int show_os_version(struct seq_file *s, void *data) +{ + char ver_buf[MAX_OS_VERSION_BUF] = ""; + + nvadsp_get_os_version(ver_buf, MAX_OS_VERSION_BUF); + seq_printf(s, "version=\"%s\"\n", ver_buf); + + return 0; +} + +static int os_version_open(struct inode *inode, struct file *file) +{ + return single_open(file, show_os_version, inode->i_private); +} + +static const struct file_operations version_fops = { + .open = os_version_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + +#define RO_MODE S_IRUSR + +static int adsp_create_os_version(struct dentry *adsp_debugfs_root) +{ + struct device *dev = &priv.pdev->dev; + struct dentry *d; + + d = debugfs_create_file("adspos_version", RO_MODE, adsp_debugfs_root, + NULL, &version_fops); + if (!d) { + dev_err(dev, "failed to create adsp_version\n"); + return -EINVAL; + } + return 0; +} +#endif + +int __init nvadsp_os_probe(struct platform_device *pdev) +{ + struct nvadsp_drv_data *drv_data = platform_get_drvdata(pdev); + int wdt_virq = drv_data->agic_irqs[WDT_VIRQ]; + int wfi_virq = drv_data->agic_irqs[WFI_VIRQ]; + uint16_t com_mid = ADSP_COM_MBOX_ID; + struct device *dev = &pdev->dev; + struct device_node *node = dev->of_node; + int ret = 0; + + priv.unit_fpga_reset_reg = drv_data->base_regs[UNIT_FPGA_RST]; + priv.hwmailbox_base = drv_data->base_regs[HWMB_REG_IDX]; + priv.dram_region = drv_data->dram_region; + + priv.adsp_os_addr = drv_data->adsp_mem[ADSP_OS_ADDR]; + priv.adsp_os_size = drv_data->adsp_mem[ADSP_OS_SIZE]; + priv.app_alloc_addr = drv_data->adsp_mem[ADSP_APP_ADDR]; + priv.app_size = drv_data->adsp_mem[ADSP_APP_SIZE]; + + ret = devm_request_irq(dev, wdt_virq, adsp_wdt_handler, + IRQF_TRIGGER_RISING, "adsp watchdog", &priv); + if (ret) { + dev_err(dev, "failed to get adsp watchdog interrupt\n"); + goto end; + } + + ret = devm_request_irq(dev, wfi_virq, adsp_wfi_handler, + IRQF_TRIGGER_RISING, "adsp wfi", &priv); + if (ret) { + dev_err(dev, "cannot request for wfi interrupt\n"); + goto end; + } + + writel(DISABLE_MBOX2_FULL_INT, priv.hwmailbox_base + HWMBOX2_REG); + + if (of_device_is_compatible(dev->of_node, "nvidia,tegra210-adsp")) { + drv_data->assert_adsp = __assert_adsp; + drv_data->deassert_adsp = __deassert_adsp; + } + + if (!of_device_is_compatible(node, "nvidia,tegra18x-adsp-hv")) { + ret = nvadsp_os_init(pdev); + if (ret) { + dev_err(dev, "failed to init os\n"); + goto end; + } + } + + ret = nvadsp_mbox_open(&adsp_com_mbox, &com_mid, "adsp_com_mbox", + NULL, NULL); + if (ret) { + dev_err(dev, "failed to open adsp com mbox\n"); + goto end; + } + + INIT_WORK(&priv.restart_os_work, nvadsp_os_restart); + mutex_init(&priv.fw_load_lock); + mutex_init(&priv.os_run_lock); + + priv.pdev = pdev; +#ifdef CONFIG_DEBUG_FS + priv.logger.dev = &pdev->dev; + if (adsp_create_debug_logger(drv_data->adsp_debugfs_root)) + dev_err(dev, "unable to create adsp debug logger file\n"); + +#ifdef CONFIG_TEGRA_ADSP_CONSOLE + priv.console.dev = &pdev->dev; + if (adsp_create_cnsl(drv_data->adsp_debugfs_root, &priv.console)) + dev_err(dev, "unable to create adsp console file\n"); +#endif /* CONFIG_TEGRA_ADSP_CONSOLE */ + + if (adsp_create_os_version(drv_data->adsp_debugfs_root)) + dev_err(dev, "unable to create adsp_version file\n"); + +#endif /* CONFIG_DEBUG_FS */ + +end: + return ret; +} diff --git a/drivers/platform/tegra/nvadsp/os.h b/drivers/platform/tegra/nvadsp/os.h new file mode 100644 index 00000000..343ef0de --- /dev/null +++ b/drivers/platform/tegra/nvadsp/os.h @@ -0,0 +1,160 @@ +/* + * os.h + * + * A header file containing data structures shared with ADSP OS + * + * Copyright (C) 2014-2016 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. + * + */ +#ifndef __TEGRA_NVADSP_OS_H +#define __TEGRA_NVADSP_OS_H + +#include +#include "adsp_shared_struct.h" + +#if defined(CONFIG_ARCH_TEGRA_21x_SOC) +#include "os-t21x.h" +#else +#include "os-t18x.h" +#endif /* CONFIG_ARCH_TEGRA_21x_SOC */ + +#define CONFIG_ADSP_DRAM_LOG_WITH_TAG 1 +/* enable profiling of load init start */ +#define RECORD_STATS 0 + +#define SYM_NAME_SZ 128 + +#define AMC_EVP_RESET_VEC_0 0x700 +#define AMC_EVP_UNDEF_VEC_0 0x704 +#define AMC_EVP_SWI_VEC_0 0x708 +#define AMC_EVP_PREFETCH_ABORT_VEC_0 0x70c +#define AMC_EVP_DATA_ABORT_VEC_0 0x710 +#define AMC_EVP_RSVD_VEC_0 0x714 +#define AMC_EVP_IRQ_VEC_0 0x718 +#define AMC_EVP_FIQ_VEC_0 0x71c +#define AMC_EVP_RESET_ADDR_0 0x720 +#define AMC_EVP_UNDEF_ADDR_0 0x724 +#define AMC_EVP_SWI_ADDR_0 0x728 +#define AMC_EVP_PREFETCH_ABORT_ADDR_0 0x72c +#define AMC_EVP_DATA_ABORT_ADDR_0 0x730 +#define AMC_EVP_RSVD_ADDR_0 0x734 +#define AMC_EVP_IRQ_ADDR_0 0x738 +#define AMC_EVP_FIQ_ADDR_0 0x73c + +#define AMC_EVP_SIZE (AMC_EVP_FIQ_ADDR_0 - AMC_EVP_RESET_VEC_0 + 4) +#define AMC_EVP_WSIZE (AMC_EVP_SIZE >> 2) + +#define OS_LOAD_TIMEOUT 5000 /* ms */ +#define ADSP_COM_MBOX_ID 2 + +#define MIN_ADSP_FREQ 38400000lu /* in Hz */ + +enum adsp_os_cmd { + ADSP_OS_BOOT_COMPLETE, + ADSP_OS_SUSPEND, + ADSP_OS_RESUME, +}; + +#if RECORD_STATS +#define RECORD_STAT(x) \ + (x = ktime_to_ns(ktime_get()) - x) +#define EQUATE_STAT(x, y) \ + (x = y) +#define RECORD_TIMESTAMP(x) \ + (x = nvadsp_get_timestamp_counter()) +#else +#define RECORD_STAT(x) +#define EQUATE_STAT(x, y) +#define RECORD_TIMESTAMP(x) +#endif + +/** + * struct global_sym_info - Global Symbol information required by app loader. + * @name: Name of the symbol + * @addr: Address of the symbol + * @info: Type and binding attributes + */ +struct global_sym_info { + char name[SYM_NAME_SZ]; + uint32_t addr; + unsigned char info; +}; + +struct adsp_module { + const char *name; + void *handle; + void *module_ptr; + uint32_t adsp_module_ptr; + size_t size; + const struct app_mem_size mem_size; + bool dynamic; +}; + +struct app_load_stats { + s64 ns_time_load; + s64 ns_time_service_parse; + s64 ns_time_module_load; + s64 ns_time_req_firmware; + s64 ns_time_layout; + s64 ns_time_native_load; + s64 ns_time_load_mbox_send_time; + s64 ns_time_load_wait_time; + s64 ns_time_native_load_complete; + u64 ns_time_adsp_map; + u64 ns_time_adsp_app_load; + u64 ns_time_adsp_send_status; + u64 adsp_receive_timestamp; + u64 host_send_timestamp; + u64 host_receive_timestamp; +}; + +struct app_init_stats { + s64 ns_time_app_init; + s64 ns_time_app_alloc; + s64 ns_time_instance_memory; + s64 ns_time_native_call; + u64 ns_time_adsp_app_init; + u64 ns_time_adsp_mem_instance_map; + u64 ns_time_adsp_init_call; + u64 ns_time_adsp_send_status; + u64 adsp_receive_timestamp; +}; + +struct app_start_stats { + s64 ns_time_app_start; + s64 ns_time_native_call; + s64 ns_time_adsp_app_start; + u64 ns_time_app_thread_creation; + u64 ns_time_app_thread_detach; + u64 ns_time_app_thread_resume; + u64 ns_time_adsp_send_status; + u64 adsp_receive_timestamp; +}; + +int nvadsp_os_probe(struct platform_device *); +int nvadsp_os_init(struct platform_device *pdev); +int nvadsp_app_module_probe(struct platform_device *); +int adsp_add_load_mappings(phys_addr_t, void *, int); +struct elf32_shdr *nvadsp_get_section(const struct firmware *, char *); +struct global_sym_info *find_global_symbol(const char *); +void update_nvadsp_app_shared_ptr(void *); + +struct adsp_module *load_adsp_dynamic_module(const char *, const char *, + struct device *); +struct adsp_module *load_adsp_static_module(const char *, + struct adsp_shared_app *, struct device *); +void unload_adsp_module(struct adsp_module *); + +int allocate_memory_from_adsp(void **, unsigned int); +bool is_adsp_dram_addr(u64); +int load_adsp_static_apps(void); +#endif /* __TEGRA_NVADSP_OS_H */ From 34470e65b4cd80f35d84fad7d7dd9890182e26a4 Mon Sep 17 00:00:00 2001 From: Ajay Nandakumar Date: Fri, 1 Apr 2016 15:45:18 +0530 Subject: [PATCH 002/138] platform: nvadsp: check evp address before copying When ADSP OS is loaded from adsp.elf, it loads two sections : adsp os and the vector table. The vector table is copied to a local buffer before writing to the EVP registers. There is a probability that, if the adsp.elf is hacked / modified by an attacker, it can write to other APE register spaces. Hence, the destination address obtained from the elf is checked with evp base before copying. Also, this fixes out of array-bound writing to evp buffer. Bug 1684844 Change-Id: I8981dc5a5db8e0c0653ff46a1576df2c82e420be Signed-off-by: Ajay Nandakumar Reviewed-on: http://git-master/r/1118936 (cherry picked from commit 9f7120e03e66b5f6e2bf67f09063da20945be238) Reviewed-on: http://git-master/r/1458894 Signed-off-by: Nitin Kumbhar Reviewed-on: https://git-master.nvidia.com/r/1537317 Reviewed-by: svccoveritychecker GVS: Gerrit_Virtual_Submit --- drivers/platform/tegra/nvadsp/dev.c | 14 ++++++++++++++ drivers/platform/tegra/nvadsp/dev.h | 9 ++++++++- drivers/platform/tegra/nvadsp/os.c | 16 ++++++++++++---- 3 files changed, 34 insertions(+), 5 deletions(-) diff --git a/drivers/platform/tegra/nvadsp/dev.c b/drivers/platform/tegra/nvadsp/dev.c index f1756870..5366e156 100644 --- a/drivers/platform/tegra/nvadsp/dev.c +++ b/drivers/platform/tegra/nvadsp/dev.c @@ -159,6 +159,15 @@ static int __init nvadsp_parse_dt(struct platform_device *pdev) } } + 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"); @@ -178,6 +187,11 @@ static int __init nvadsp_parse_dt(struct platform_device *pdev) } 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; } diff --git a/drivers/platform/tegra/nvadsp/dev.h b/drivers/platform/tegra/nvadsp/dev.h index df1c4073..adc96fa0 100644 --- a/drivers/platform/tegra/nvadsp/dev.h +++ b/drivers/platform/tegra/nvadsp/dev.h @@ -36,6 +36,12 @@ #include "amc.h" #include "os.h" +enum adsp_evp_dt { + ADSP_EVP_BASE, + ADSP_EVP_SIZE, + ADSP_EVP_END, +}; + enum adsp_unit_fpga_reset { ADSP_ASSERT, ADSP_DEASSERT, @@ -71,7 +77,7 @@ struct nvadsp_pm_state { u32 aram[AMC_ARAM_WSIZE]; uint32_t amc_regs[AMC_REGS]; uint32_t amisc_regs[AMISC_REGS]; - u32 evp[AMC_EVP_WSIZE]; + u32 *evp; void *evp_ptr; }; @@ -140,6 +146,7 @@ struct nvadsp_drv_data { int agic_irqs[NVADSP_VIRQ_MAX]; struct tegra_bwmgr_client *bwmgr; + u32 evp_base[ADSP_EVP_END]; }; #define ADSP_CONFIG 0x04 diff --git a/drivers/platform/tegra/nvadsp/os.c b/drivers/platform/tegra/nvadsp/os.c index 80534612..4c972196 100644 --- a/drivers/platform/tegra/nvadsp/os.c +++ b/drivers/platform/tegra/nvadsp/os.c @@ -559,12 +559,20 @@ static int nvadsp_os_elf_load(const struct firmware *fw) /* put the segment where the remote processor expects it */ if (filesz) { - if (!is_adsp_dram_addr(da)) { + if (is_adsp_dram_addr(da)) + memcpy(va, elf_data + offset, filesz); + else if ((da == drv_data->evp_base[ADSP_EVP_BASE]) && + (filesz == drv_data->evp_base[ADSP_EVP_SIZE])) { + drv_data->state.evp_ptr = va; memcpy(drv_data->state.evp, - elf_data + offset, filesz); - } else - memcpy(va, elf_data + offset, filesz); + elf_data + offset, filesz); + } else { + dev_err(dev, "can't load mem pa:0x%x va:%p\n", + da, va); + ret = -EINVAL; + break; + } } } From 828e9d448f1e07399e24421311e0d385ed371e7c Mon Sep 17 00:00:00 2001 From: Ajay Nandakumar Date: Mon, 24 Apr 2017 16:35:45 +0530 Subject: [PATCH 003/138] platform: nvadsp: Fix pointer to error conversion Fixing the issue of convertion from pointer to error conversion for integer value in load_adsp_dynamic_module. Bug 200270956 Change-Id: I50fb4f4a4b0cf607045f8037d116affcdd1a6b3b Signed-off-by: Ajay Nandakumar Reviewed-on: http://git-master/r/1468501 Signed-off-by: Nitin Kumbhar Reviewed-on: https://git-master.nvidia.com/r/1537318 Reviewed-by: svccoveritychecker GVS: Gerrit_Virtual_Submit --- drivers/platform/tegra/nvadsp/app_loader_linker.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/platform/tegra/nvadsp/app_loader_linker.c b/drivers/platform/tegra/nvadsp/app_loader_linker.c index 4b380fa1..61e076b5 100644 --- a/drivers/platform/tegra/nvadsp/app_loader_linker.c +++ b/drivers/platform/tegra/nvadsp/app_loader_linker.c @@ -945,7 +945,7 @@ struct adsp_module *load_adsp_dynamic_module(const char *appname, error_release_fw: release_firmware(fw); - return IS_ERR_VALUE(ret) ? ERR_PTR(ret) : mod; + return ret ? ERR_PTR(ret) : mod; unload_module: unload_adsp_module(mod); From 915f2bfa4dec132e98f7f51d01e3922218059e45 Mon Sep 17 00:00:00 2001 From: Nitin Kumbhar Date: Fri, 6 Jan 2017 14:36:34 +0530 Subject: [PATCH 004/138] Platform: nvadsp: Unify ADSP across chips Unifying ADSP driver across t186, t210. This removes the use of chip specific configs. Based on: platform: nvadsp: Enable ADSP in T210 for 4.4 nvadsp: Add nvadsp_os_init for t18x HV config tegra: t210: nvadsp: move hwmbox to chip data platform: nvadsp: Add callbacks to chip data platform: nvadsp: Use WDT irq from chip data soc: adsp: use soc/tegra/chip-id.h for soc header tegra: nvadsp: remove clk periph reset assert/deassert tegra: nvadsp: use ACLK to manage ADSP clk on t186 tegra: nvadsp: dfs: fix tfreq in error path platform: tegra: adsp: update rpm error handling platform: nvadsp: export symbols for audio driver Bug 200272977 Jira EMA-373 Bug 200257350 Bug 200124772 Bug 200289390 Change-Id: Iab20f54a48c67febd6b4ccfaf2e89e5b264e0f5a Signed-off-by: Ajay Nandakumar Reviewed-on: http://git-master/r/1468351 Signed-off-by: Nitin Kumbhar Reviewed-on: https://git-master.nvidia.com/r/1537319 Reviewed-by: svccoveritychecker GVS: Gerrit_Virtual_Submit --- drivers/platform/tegra/nvadsp/Makefile | 12 +- drivers/platform/tegra/nvadsp/acast.c | 144 ++++++++++ drivers/platform/tegra/nvadsp/adsp_dfs.c | 151 +++++++--- .../platform/tegra/nvadsp/app_loader_linker.c | 22 +- drivers/platform/tegra/nvadsp/dev-t18x.c | 267 ++++++++++++++++++ drivers/platform/tegra/nvadsp/dev-t18x.h | 23 ++ drivers/platform/tegra/nvadsp/dev-t21x.c | 112 ++++---- drivers/platform/tegra/nvadsp/dev-t21x.h | 38 +-- drivers/platform/tegra/nvadsp/dev.c | 64 ++++- drivers/platform/tegra/nvadsp/dev.h | 98 ++++++- .../platform/tegra/nvadsp/hwmailbox-t21x.h | 25 -- drivers/platform/tegra/nvadsp/hwmailbox.c | 64 +++-- drivers/platform/tegra/nvadsp/hwmailbox.h | 11 +- drivers/platform/tegra/nvadsp/os-t18x.c | 51 ++++ drivers/platform/tegra/nvadsp/os-t21x.c | 5 +- drivers/platform/tegra/nvadsp/os-t21x.h | 21 -- drivers/platform/tegra/nvadsp/os.c | 133 +++++---- drivers/platform/tegra/nvadsp/os.h | 19 +- 18 files changed, 941 insertions(+), 319 deletions(-) create mode 100644 drivers/platform/tegra/nvadsp/acast.c create mode 100644 drivers/platform/tegra/nvadsp/dev-t18x.c create mode 100644 drivers/platform/tegra/nvadsp/dev-t18x.h delete mode 100644 drivers/platform/tegra/nvadsp/hwmailbox-t21x.h create mode 100644 drivers/platform/tegra/nvadsp/os-t18x.c diff --git a/drivers/platform/tegra/nvadsp/Makefile b/drivers/platform/tegra/nvadsp/Makefile index da6c1794..87c8cd37 100644 --- a/drivers/platform/tegra/nvadsp/Makefile +++ b/drivers/platform/tegra/nvadsp/Makefile @@ -1,15 +1,14 @@ GCOV_PROFILE := y ccflags-y += -Werror -ifeq ($(CONFIG_ARCH_TEGRA_18x_SOC),y) -ccflags-y += -I$(srctree)/../t18x/drivers/platform/tegra/nvadsp -endif obj-y := nvadsp.o nvadsp-objs += dev.o os.o app.o app_loader_linker.o\ amc.o nvadsp_dram.o \ nvadsp_shared_sema.o nvadsp_arb_sema.o \ hwmailbox.o mailbox.o msgq.o \ - mem_manager.o aram_manager.o dram_app_mem_manager.o + mem_manager.o aram_manager.o dram_app_mem_manager.o \ + dev-t21x.o os-t21x.o dev-t18x.o os-t18x.o acast.o + ifeq ($(CONFIG_TEGRA_ADSP_DFS),y) nvadsp-objs += adsp_dfs.o @@ -31,11 +30,6 @@ ifeq ($(CONFIG_TEGRA_ADSP_CPUSTAT),y) nvadsp-objs += adsp_cpustat.o endif -ifeq ($(CONFIG_ARCH_TEGRA_21x_SOC),y) -nvadsp-objs += dev-t21x.o -nvadsp-objs += os-t21x.o -endif - ifeq ($(CONFIG_TEGRA_ADSP_FILEIO),y) nvadsp-objs += adspff.o endif diff --git a/drivers/platform/tegra/nvadsp/acast.c b/drivers/platform/tegra/nvadsp/acast.c new file mode 100644 index 00000000..5342caec --- /dev/null +++ b/drivers/platform/tegra/nvadsp/acast.c @@ -0,0 +1,144 @@ +/* + * Copyright (C) 2016-2017 NVIDIA CORPORATION. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope 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. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include +#include +#include "dev-t18x.h" + +#define AST_CONTROL 0x000 +#define AST_STREAMID_CTL_0 0x020 +#define AST_STREAMID_CTL_1 0x024 +#define AST_RGN_SLAVE_BASE_LO 0x100 +#define AST_RGN_SLAVE_BASE_HI 0x104 +#define AST_RGN_MASK_BASE_LO 0x108 +#define AST_RGN_MASK_BASE_HI 0x10c +#define AST_RGN_MASTER_BASE_LO 0x110 +#define AST_RGN_MASTER_BASE_HI 0x114 +#define AST_RGN_CONTOL 0x118 + +#define AST_PAGE_MASK (~0xFFF) +#define AST_LO_SHIFT 32 +#define AST_LO_MASK 0xFFFFFFFF +#define AST_PHY_SID_IDX 0 +#define AST_APE_SID_IDX 1 +#define AST_NS (1 << 3) +#define AST_VMINDEX(IDX) (IDX << 15) +#define AST_RGN_ENABLE (1 << 0) +#define AST_RGN_OFFSET 0x20 + +struct acast_region { + u64 rgn; + u64 rgn_ctrl; + u64 slave; + u64 size; + u64 master; +}; + +#define ACAST_REGIONS 1 +#define ACAST_BASE 0x02994000 +#define ACAST_SIZE 0x1FFF + +#define ACAST_GLOBAL_CTRL_VAL 0x07b80009 +#define ACAST_STREAMID_CTL_0_VAL 0x00007f01 +#define ACAST_STREAMID_CTL_1_VAL 0x00001e01 + +static struct acast_region acast_regions[ACAST_REGIONS] = { + { + 0x2, + 0x00008008, + 0x40000000, + 0x20000000, + 0x40000000, + }, +}; + +static void __iomem *acast_base; + +static inline void acast_write(void __iomem *acast, u32 reg, u32 val) +{ + writel(val, acast + reg); +} + +static inline u32 __maybe_unused acast_read(void __iomem *acast, u32 reg) +{ + return readl(acast + reg); +} + +static inline u32 acast_rgn_reg(u32 rgn, u32 reg) +{ + return rgn * AST_RGN_OFFSET + reg; +} + +static void tegra18x_acast_map(void __iomem *acast, u64 rgn, u64 rgn_ctrl, + u64 slave, u64 size, u64 master) +{ + u32 val; + + val = (slave & AST_LO_MASK) | AST_RGN_ENABLE; + acast_write(acast, + acast_rgn_reg(rgn, AST_RGN_SLAVE_BASE_LO), val); + val = slave >> AST_LO_SHIFT; + acast_write(acast, + acast_rgn_reg(rgn, AST_RGN_SLAVE_BASE_HI), val); + + val = master & AST_LO_MASK; + acast_write(acast, + acast_rgn_reg(rgn, AST_RGN_MASTER_BASE_LO), val); + val = master >> AST_LO_SHIFT; + acast_write(acast, + acast_rgn_reg(rgn, AST_RGN_MASTER_BASE_HI), val); + + val = ((size - 1) & AST_PAGE_MASK) & AST_LO_MASK; + acast_write(acast, + acast_rgn_reg(rgn, AST_RGN_MASK_BASE_LO), val); + val = (size - 1) >> AST_LO_SHIFT; + acast_write(acast, + acast_rgn_reg(rgn, AST_RGN_MASK_BASE_HI), val); + + acast_write(acast, + acast_rgn_reg(rgn, AST_RGN_CONTOL), rgn_ctrl); +} + +int nvadsp_acast_init(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + int i; + + if (!acast_base) { + acast_base = devm_ioremap_nocache(dev, ACAST_BASE, ACAST_SIZE); + if (IS_ERR_OR_NULL(acast_base)) { + dev_err(dev, "failed to map ACAST\n"); + return PTR_ERR(acast_base); + } + } + + for (i = 0; i < ACAST_REGIONS; i++) { + tegra18x_acast_map(acast_base, + acast_regions[i].rgn, + acast_regions[i].rgn_ctrl, + acast_regions[i].slave, + acast_regions[i].size, + acast_regions[i].master); + + dev_dbg(dev, "i:%d rgn:0x%llx rgn_ctrl:0x%llx ", + i, acast_regions[i].rgn, acast_regions[i].rgn_ctrl); + dev_dbg(dev, "slave:0x%llx size:0x%llx master:0x%llx\n", + acast_regions[i].slave, acast_regions[i].size, + acast_regions[i].master); + } + + return 0; +} diff --git a/drivers/platform/tegra/nvadsp/adsp_dfs.c b/drivers/platform/tegra/nvadsp/adsp_dfs.c index 48ee0326..fca2623c 100644 --- a/drivers/platform/tegra/nvadsp/adsp_dfs.c +++ b/drivers/platform/tegra/nvadsp/adsp_dfs.c @@ -3,7 +3,7 @@ * * adsp dynamic frequency scaling * - * Copyright (C) 2014-2016, NVIDIA Corporation. All rights reserved. + * Copyright (C) 2014-2017, 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 @@ -86,6 +86,7 @@ struct adsp_dfs_policy { unsigned long cpu_max; /* ADSP max freq(KHz). Remain unchanged */ struct clk *adsp_clk; + struct clk *aclk_clk; struct notifier_block rate_change_nb; struct nvadsp_mbox mbox; @@ -123,6 +124,66 @@ static bool is_os_running(struct device *dev) } return true; } + +static int adsp_clk_get(struct adsp_dfs_policy *policy) +{ + struct device_node *node = device->of_node; + int ret = 0; + + if (IS_ENABLED(CONFIG_COMMON_CLK)) + policy->adsp_clk = devm_clk_get(device, "adsp"); + else + policy->adsp_clk = clk_get_sys(NULL, policy->clk_name); + + if (IS_ERR_OR_NULL(policy->adsp_clk)) { + dev_err(device, "unable to find adsp clock\n"); + ret = PTR_ERR(policy->adsp_clk); + } + + if (!of_device_is_compatible(node, "nvidia,tegra210-adsp")) { + policy->aclk_clk = devm_clk_get(device, "aclk"); + + if (IS_ERR_OR_NULL(policy->aclk_clk)) { + dev_err(device, "unable to find aclk clock\n"); + ret = PTR_ERR(policy->aclk_clk); + } + } + + return ret; +} + +static void adsp_clk_put(struct adsp_dfs_policy *policy) +{ + if (policy->adsp_clk) { + if (IS_ENABLED(CONFIG_COMMON_CLK)) + devm_clk_put(device, policy->adsp_clk); + else + clk_put(policy->adsp_clk); + } + + if (policy->aclk_clk) + devm_clk_put(device, policy->aclk_clk); +} + +static int adsp_clk_set_rate(struct adsp_dfs_policy *policy, + unsigned long freq_hz) +{ + struct device_node *node = device->of_node; + int ret; + + if (of_device_is_compatible(node, "nvidia,tegra210-adsp")) + ret = clk_set_rate(policy->adsp_clk, freq_hz); + else + ret = clk_set_rate(policy->aclk_clk, freq_hz); + + return ret; +} + +static unsigned long adsp_clk_get_rate(struct adsp_dfs_policy *policy) +{ + return clk_get_rate(policy->adsp_clk); +} + /* Expects and returns freq in Hz as table is formmed in terms of Hz */ static unsigned long adsp_get_target_freq(unsigned long tfreq, int *index) { @@ -196,7 +257,7 @@ static struct adsp_dfs_policy dfs_policy = { /* * update_freq - update adsp freq and ask adsp to change timer as * change in adsp freq. - * tfreq - target frequency in KHz + * freq_khz - target frequency in KHz * return - final freq got set. * - 0, incase of error. * @@ -204,37 +265,38 @@ static struct adsp_dfs_policy dfs_policy = { * change notifier, when freq is changed in hw * */ -static unsigned long update_freq(unsigned long tfreq) +static unsigned long update_freq(unsigned long freq_khz) { u32 efreq; int index; int ret; - unsigned long old_freq; + unsigned long tfreq_hz, old_freq_khz; enum adsp_dfs_reply reply; struct nvadsp_mbox *mbx = &policy->mbox; struct nvadsp_drv_data *drv = dev_get_drvdata(device); - tfreq = adsp_get_target_freq(tfreq * 1000, &index); - if (!tfreq) { + tfreq_hz = adsp_get_target_freq(freq_khz * 1000, &index); + if (!tfreq_hz) { dev_info(device, "unable get the target freq\n"); return 0; } - old_freq = policy->cur; + old_freq_khz = policy->cur; - if ((tfreq / 1000) == old_freq) { + if ((tfreq_hz / 1000) == old_freq_khz) { dev_dbg(device, "old and new target_freq is same\n"); return 0; } - ret = clk_set_rate(policy->adsp_clk, tfreq); + ret = adsp_clk_set_rate(policy, tfreq_hz); if (ret) { - dev_err(device, "failed to set adsp freq:%d\n", ret); + dev_err(device, "failed to set adsp freq:%luhz err:%d\n", + tfreq_hz, ret); policy->update_freq_flag = false; return 0; } - efreq = adsp_to_emc_freq(tfreq / 1000); + efreq = adsp_to_emc_freq(tfreq_hz / 1000); if (IS_ENABLED(CONFIG_COMMON_CLK)) { tegra_bwmgr_set_emc(drv->bwmgr, efreq * 1000, @@ -248,7 +310,7 @@ static unsigned long update_freq(unsigned long tfreq) } } - dev_dbg(device, "sending change in freq:%lu\n", tfreq); + dev_dbg(device, "sending change in freq(hz):%lu\n", tfreq_hz); /* * Ask adsp to do action upon change in freq. ADSP and Host need to * maintain the same freq table. @@ -285,18 +347,21 @@ static unsigned long update_freq(unsigned long tfreq) dev_err(device, "Error: adsp freq change status\n"); } - dev_dbg(device, "%s:status received from adsp: %s, tfreq:%lu\n", __func__, - (policy->update_freq_flag == true ? "ACK" : "NACK"), tfreq); + dev_dbg(device, "%s:status received from adsp: %s, tfreq(hz):%lu\n", + __func__, + policy->update_freq_flag == true ? "ACK" : "NACK", + tfreq_hz); err_out: if (!policy->update_freq_flag) { - ret = clk_set_rate(policy->adsp_clk, old_freq * 1000); + ret = adsp_clk_set_rate(policy, old_freq_khz * 1000); if (ret) { - dev_err(device, "failed to resume adsp freq:%lu\n", old_freq); + dev_err(device, "failed to resume adsp freq(khz):%lu\n", + old_freq_khz); policy->update_freq_flag = false; } - efreq = adsp_to_emc_freq(old_freq / 1000); + efreq = adsp_to_emc_freq(old_freq_khz); if (IS_ENABLED(CONFIG_COMMON_CLK)) { tegra_bwmgr_set_emc(drv->bwmgr, efreq * 1000, TEGRA_BWMGR_SET_EMC_FLOOR); @@ -309,9 +374,9 @@ err_out: } } - tfreq = old_freq; + tfreq_hz = old_freq_khz * 1000; } - return tfreq / 1000; + return tfreq_hz / 1000; } /* Set adsp dfs policy min freq(Khz) */ @@ -612,17 +677,19 @@ exit_out: * * @params: * freq: adsp freq in KHz - * return - final freq got set. - * - 0, incase of error. + * return - final freq set + * - 0 incase of error * */ -unsigned long adsp_override_freq(unsigned long freq) +unsigned long adsp_override_freq(unsigned long req_freq_khz) { + unsigned long ret_freq = 0, freq; int index; - unsigned long ret_freq = 0; mutex_lock(&policy_mutex); + freq = req_freq_khz; + if (freq < policy->min) freq = policy->min; else if (freq > policy->max) @@ -630,7 +697,9 @@ unsigned long adsp_override_freq(unsigned long freq) freq = adsp_get_target_freq(freq * 1000, &index); if (!freq) { - dev_warn(device, "unable get the target freq\n"); + dev_warn(device, + "req freq:%lukhz. unable get the target freq.\n", + req_freq_khz); goto exit_out; } freq = freq / 1000; /* In KHz */ @@ -646,7 +715,9 @@ unsigned long adsp_override_freq(unsigned long freq) policy->cur = ret_freq; if (ret_freq != freq) { - dev_warn(device, "freq override to %lu rejected\n", freq); + dev_warn(device, + "req freq:%lukhz. freq override to %lukhz rejected.\n", + req_freq_khz, freq); policy->ovr_freq = 0; goto exit_out; } @@ -655,6 +726,7 @@ exit_out: mutex_unlock(&policy_mutex); return ret_freq; } +EXPORT_SYMBOL(adsp_override_freq); /* * Set min ADSP freq. @@ -666,6 +738,7 @@ void adsp_update_dfs_min_rate(unsigned long freq) { policy_min_set(NULL, freq); } +EXPORT_SYMBOL(adsp_update_dfs_min_rate); /* Enable / disable dynamic freq scaling */ void adsp_update_dfs(bool val) @@ -690,15 +763,9 @@ int adsp_dfs_core_init(struct platform_device *pdev) device = &pdev->dev; policy = &dfs_policy; - if (IS_ENABLED(CONFIG_COMMON_CLK)) - policy->adsp_clk = devm_clk_get(device, "adsp"); - else - policy->adsp_clk = clk_get_sys(NULL, policy->clk_name); - if (IS_ERR_OR_NULL(policy->adsp_clk)) { - dev_err(&pdev->dev, "unable to find adsp clock\n"); - ret = PTR_ERR(policy->adsp_clk); + ret = adsp_clk_get(policy); + if (ret) goto end; - } if (IS_ENABLED(CONFIG_COMMON_CLK)) { drv->bwmgr = tegra_bwmgr_register(TEGRA_BWMGR_CLIENT_APE_ADSP); @@ -727,7 +794,7 @@ int adsp_dfs_core_init(struct platform_device *pdev) policy->min = policy->cpu_min = adsp_cpu_freq_table[0] / 1000; - policy->cur = clk_get_rate(policy->adsp_clk) / 1000; + policy->cur = adsp_clk_get_rate(policy) / 1000; efreq = adsp_to_emc_freq(policy->cur); @@ -786,12 +853,8 @@ int adsp_dfs_core_init(struct platform_device *pdev) dev_dbg(&pdev->dev, "adsp dfs initialized ....\n"); return ret; end: - if (policy->adsp_clk) { - if (IS_ENABLED(CONFIG_COMMON_CLK)) - devm_clk_put(&pdev->dev, policy->adsp_clk); - else - clk_put(policy->adsp_clk); - } + + adsp_clk_put(policy); if (IS_ENABLED(CONFIG_COMMON_CLK) && drv->bwmgr) { tegra_bwmgr_set_emc(drv->bwmgr, 0, @@ -823,13 +886,7 @@ int adsp_dfs_core_exit(struct platform_device *pdev) tegra_unregister_clk_rate_notifier(clk_get_parent(policy->adsp_clk), &policy->rate_change_nb); #endif - - if (policy->adsp_clk) { - if (IS_ENABLED(CONFIG_COMMON_CLK)) - devm_clk_put(&pdev->dev, policy->adsp_clk); - else - clk_put(policy->adsp_clk); - } + adsp_clk_put(policy); if (IS_ENABLED(CONFIG_COMMON_CLK) && drv->bwmgr) { tegra_bwmgr_set_emc(drv->bwmgr, 0, diff --git a/drivers/platform/tegra/nvadsp/app_loader_linker.c b/drivers/platform/tegra/nvadsp/app_loader_linker.c index 61e076b5..396a3563 100644 --- a/drivers/platform/tegra/nvadsp/app_loader_linker.c +++ b/drivers/platform/tegra/nvadsp/app_loader_linker.c @@ -3,7 +3,7 @@ * * ADSP OS App management * - * Copyright (C) 2014-2015 NVIDIA Corporation. All rights reserved. + * Copyright (C) 2014-2017 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 @@ -860,6 +860,7 @@ struct adsp_module *load_adsp_dynamic_module(const char *appname, struct elf32_shdr *aram_shdr; struct elf32_shdr *aram_x_shdr; struct app_mem_size *mem_size; + void *buf; int ret; ret = request_firmware(&fw, appfile, dev); @@ -870,7 +871,13 @@ struct adsp_module *load_adsp_dynamic_module(const char *appname, return ERR_PTR(ret); } - info.hdr = (struct elf32_hdr *)fw->data; + buf = kzalloc(fw->size, GFP_KERNEL); + if (!buf) + goto release_firmware; + + memcpy(buf, fw->data, fw->size); + + info.hdr = (struct elf32_hdr *)buf; info.len = fw->size; info.dev = dev; info.name = appname; @@ -879,13 +886,13 @@ struct adsp_module *load_adsp_dynamic_module(const char *appname, if (ret) { dev_err(dev, "%s is not an elf file\n", appfile); - goto error_release_fw; + goto error_free_memory; } /* Figure out module layout, and allocate all the memory. */ mod = layout_and_allocate(&info); if (IS_ERR(mod)) - goto error_release_fw; + goto error_free_memory; /* update adsp specific sections */ data_shdr = nvadsp_get_section(fw, ".dram_data"); @@ -943,11 +950,14 @@ struct adsp_module *load_adsp_dynamic_module(const char *appname, mod->dynamic = true; -error_release_fw: + error_free_memory: + kfree(buf); + release_firmware: release_firmware(fw); return ret ? ERR_PTR(ret) : mod; -unload_module: + unload_module: + kfree(buf); unload_adsp_module(mod); release_firmware(fw); return ERR_PTR(ret); diff --git a/drivers/platform/tegra/nvadsp/dev-t18x.c b/drivers/platform/tegra/nvadsp/dev-t18x.c new file mode 100644 index 00000000..90eefef6 --- /dev/null +++ b/drivers/platform/tegra/nvadsp/dev-t18x.c @@ -0,0 +1,267 @@ +/* + * Copyright (c) 2015-2017, 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 +#include +#include +#include + +#include + +#include "dev.h" +#include "dev-t18x.h" + +#ifdef CONFIG_PM +static int nvadsp_t18x_clocks_disable(struct platform_device *pdev) +{ + struct nvadsp_drv_data *drv_data = platform_get_drvdata(pdev); + struct device *dev = &pdev->dev; + + if (drv_data->adsp_clk) { + clk_disable_unprepare(drv_data->adsp_clk); + dev_dbg(dev, "adsp clocks disabled\n"); + drv_data->adsp_clk = NULL; + } + + if (drv_data->aclk_clk) { + clk_disable_unprepare(drv_data->aclk_clk); + dev_dbg(dev, "aclk clock disabled\n"); + drv_data->aclk_clk = NULL; + } + + if (drv_data->adsp_neon_clk) { + clk_disable_unprepare(drv_data->adsp_neon_clk); + dev_dbg(dev, "adsp_neon clocks disabled\n"); + drv_data->adsp_neon_clk = NULL; + } + + if (drv_data->ape_clk) { + clk_disable_unprepare(drv_data->ape_clk); + dev_dbg(dev, "ape clock disabled\n"); + drv_data->ape_clk = NULL; + } + + if (drv_data->apb2ape_clk) { + clk_disable_unprepare(drv_data->apb2ape_clk); + dev_dbg(dev, "apb2ape clock disabled\n"); + drv_data->apb2ape_clk = NULL; + } + + if (drv_data->ape_emc_clk) { + clk_disable_unprepare(drv_data->ape_emc_clk); + dev_dbg(dev, "ape.emc clock disabled\n"); + drv_data->ape_emc_clk = NULL; + } + return 0; +} + +static int nvadsp_t18x_clocks_enable(struct platform_device *pdev) +{ + struct nvadsp_drv_data *drv_data = platform_get_drvdata(pdev); + struct device *dev = &pdev->dev; + int ret = 0; + + drv_data->ape_clk = devm_clk_get(dev, "adsp.ape"); + if (IS_ERR_OR_NULL(drv_data->ape_clk)) { + dev_err(dev, "unable to find adsp.ape clock\n"); + ret = PTR_ERR(drv_data->ape_clk); + goto end; + } + ret = clk_prepare_enable(drv_data->ape_clk); + if (ret) { + dev_err(dev, "unable to enable adsp.ape clock\n"); + goto end; + } + dev_dbg(dev, "adsp.ape clock enabled\n"); + + drv_data->adsp_clk = devm_clk_get(dev, "adsp"); + if (IS_ERR_OR_NULL(drv_data->adsp_clk)) { + dev_err(dev, "unable to find adsp clock\n"); + ret = PTR_ERR(drv_data->adsp_clk); + goto end; + } + ret = clk_prepare_enable(drv_data->adsp_clk); + if (ret) { + dev_err(dev, "unable to enable adsp clock\n"); + goto end; + } + + drv_data->aclk_clk = devm_clk_get(dev, "aclk"); + if (IS_ERR_OR_NULL(drv_data->aclk_clk)) { + dev_err(dev, "unable to find aclk clock\n"); + ret = PTR_ERR(drv_data->aclk_clk); + goto end; + } + ret = clk_prepare_enable(drv_data->aclk_clk); + if (ret) { + dev_err(dev, "unable to enable aclk clock\n"); + goto end; + } + + drv_data->adsp_neon_clk = devm_clk_get(dev, "adspneon"); + if (IS_ERR_OR_NULL(drv_data->adsp_neon_clk)) { + dev_err(dev, "unable to find adsp neon clock\n"); + ret = PTR_ERR(drv_data->adsp_neon_clk); + goto end; + } + ret = clk_prepare_enable(drv_data->adsp_neon_clk); + if (ret) { + dev_err(dev, "unable to enable adsp neon clock\n"); + goto end; + } + dev_dbg(dev, "adsp neon clock enabled\n"); + + drv_data->ape_emc_clk = devm_clk_get(dev, "adsp.emc"); + if (IS_ERR_OR_NULL(drv_data->ape_emc_clk)) { + dev_err(dev, "unable to find adsp.emc clock\n"); + ret = PTR_ERR(drv_data->ape_emc_clk); + goto end; + } + ret = clk_prepare_enable(drv_data->ape_emc_clk); + if (ret) { + dev_err(dev, "unable to enable adsp.emc clock\n"); + goto end; + } + dev_dbg(dev, "ape.emc is enabled\n"); + + drv_data->apb2ape_clk = devm_clk_get(dev, "adsp.apb2ape"); + if (IS_ERR_OR_NULL(drv_data->apb2ape_clk)) { + dev_err(dev, "unable to find adsp.apb2ape clk\n"); + ret = PTR_ERR(drv_data->apb2ape_clk); + goto end; + } + ret = clk_prepare_enable(drv_data->apb2ape_clk); + if (ret) { + dev_err(dev, "unable to enable adsp.apb2ape clock\n"); + goto end; + } + dev_dbg(dev, "adsp.apb2ape clock enabled\n"); + + dev_dbg(dev, "all clocks enabled\n"); + return 0; + end: + nvadsp_t18x_clocks_disable(pdev); + return ret; +} + +static int __nvadsp_t18x_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; + + dev_dbg(dev, "at %s:%d\n", __func__, __LINE__); + + ret = nvadsp_t18x_clocks_enable(pdev); + if (ret) { + dev_dbg(dev, "failed in nvadsp_t18x_clocks_enable\n"); + return ret; + } + + if (!drv_data->adsp_os_secload) { + ret = nvadsp_acast_init(pdev); + if (ret) { + dev_err(dev, "failed in nvadsp_acast_init\n"); + return ret; + } + } + + return ret; +} + +static int __nvadsp_t18x_runtime_suspend(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + + dev_dbg(dev, "at %s:%d\n", __func__, __LINE__); + + return nvadsp_t18x_clocks_disable(pdev); +} + +static int __nvadsp_t18x_runtime_idle(struct device *dev) +{ + dev_dbg(dev, "at %s:%d\n", __func__, __LINE__); + return 0; +} + +int nvadsp_pm_t18x_init(struct platform_device *pdev) +{ + struct nvadsp_drv_data *d = platform_get_drvdata(pdev); + struct device *dev = &pdev->dev; + + dev_dbg(dev, "at %s:%d\n", __func__, __LINE__); + + d->runtime_suspend = __nvadsp_t18x_runtime_suspend; + d->runtime_resume = __nvadsp_t18x_runtime_resume; + d->runtime_idle = __nvadsp_t18x_runtime_idle; + + return 0; +} +#endif /* CONFIG_PM */ + +static int __assert_t18x_adsp(struct nvadsp_drv_data *d) +{ + struct platform_device *pdev = d->pdev; + struct device *dev = &pdev->dev; + int ret = 0; + + /* + * The ADSP_ALL reset in BPMP-FW is overloaded to assert + * all 7 resets i.e. ADSP, ADSPINTF, ADSPDBG, ADSPNEON, + * ADSPPERIPH, ADSPSCU and ADSPWDT resets. So resetting + * only ADSP reset is sufficient to reset all ADSP sub-modules. + */ + ret = reset_control_assert(d->adspall_rst); + if (ret) + dev_err(dev, "failed to assert adsp\n"); + + return ret; +} + +static int __deassert_t18x_adsp(struct nvadsp_drv_data *d) +{ + struct platform_device *pdev = d->pdev; + struct device *dev = &pdev->dev; + int ret = 0; + + /* + * The ADSP_ALL reset in BPMP-FW is overloaded to de-assert + * all 7 resets i.e. ADSP, ADSPINTF, ADSPDBG, ADSPNEON, ADSPPERIPH, + * ADSPSCU and ADSPWDT resets. The BPMP-FW also takes care + * of specific de-assert sequence and delays between them. + * So de-resetting only ADSP reset is sufficient to de-reset + * all ADSP sub-modules. + */ + ret = reset_control_deassert(d->adspall_rst); + if (ret) + dev_err(dev, "failed to deassert adsp\n"); + + return ret; +} + +int nvadsp_reset_t18x_init(struct platform_device *pdev) +{ + struct nvadsp_drv_data *d = platform_get_drvdata(pdev); + struct device *dev = &pdev->dev; + int ret = 0; + + d->assert_adsp = __assert_t18x_adsp; + d->deassert_adsp = __deassert_t18x_adsp; + d->adspall_rst = devm_reset_control_get(dev, "adspall"); + if (IS_ERR(d->adspall_rst)) { + dev_err(dev, "can not get adspall reset\n"); + ret = PTR_ERR(d->adspall_rst); + } + return ret; +} diff --git a/drivers/platform/tegra/nvadsp/dev-t18x.h b/drivers/platform/tegra/nvadsp/dev-t18x.h new file mode 100644 index 00000000..b4b216de --- /dev/null +++ b/drivers/platform/tegra/nvadsp/dev-t18x.h @@ -0,0 +1,23 @@ +/* + * Copyright (C) 2015-2017, 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. + * + */ + +#ifndef __TEGRA_NVADSP_DEV_T18X_H +#define __TEGRA_NVADSP_DEV_T18X_H + +int nvadsp_acast_init(struct platform_device *pdev); +int nvadsp_reset_t18x_init(struct platform_device *pdev); +int nvadsp_os_t18x_init(struct platform_device *pdev); +int nvadsp_pm_t18x_init(struct platform_device *pdev); + +#endif /* __TEGRA_NVADSP_DEV_T18X_H */ diff --git a/drivers/platform/tegra/nvadsp/dev-t21x.c b/drivers/platform/tegra/nvadsp/dev-t21x.c index 17cbdfab..1326190c 100644 --- a/drivers/platform/tegra/nvadsp/dev-t21x.c +++ b/drivers/platform/tegra/nvadsp/dev-t21x.c @@ -3,7 +3,7 @@ * * A device driver for ADSP and APE * - * Copyright (C) 2014-2016, NVIDIA Corporation. All rights reserved. + * Copyright (C) 2014-2017, 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 @@ -21,6 +21,7 @@ #include #include #include +#include #include "dev.h" #include "amc.h" @@ -31,22 +32,17 @@ static void nvadsp_clocks_disable(struct platform_device *pdev) struct nvadsp_drv_data *drv_data = platform_get_drvdata(pdev); struct device *dev = &pdev->dev; - if (drv_data->uartape_clk) { - clk_disable_unprepare(drv_data->uartape_clk); - dev_dbg(dev, "uartape clock disabled\n"); - drv_data->uartape_clk = NULL; - } - - if (drv_data->adsp_cpu_clk) { - clk_disable_unprepare(drv_data->adsp_cpu_clk); - dev_dbg(dev, "adsp_cpu clock disabled\n"); - drv_data->adsp_cpu_clk = NULL; - } - if (drv_data->adsp_clk) { clk_disable_unprepare(drv_data->adsp_clk); dev_dbg(dev, "adsp clocks disabled\n"); drv_data->adsp_clk = NULL; + drv_data->adsp_cpu_clk = NULL; + } + + if (drv_data->adsp_neon_clk) { + clk_disable_unprepare(drv_data->adsp_neon_clk); + dev_info(dev, "adsp_neon clocks disabled\n"); + drv_data->adsp_neon_clk = NULL; } if (drv_data->ape_clk) { @@ -55,54 +51,39 @@ static void nvadsp_clocks_disable(struct platform_device *pdev) drv_data->ape_clk = NULL; } + if (drv_data->apb2ape_clk) { + clk_disable_unprepare(drv_data->apb2ape_clk); + dev_info(dev, "apb2ape clock disabled\n"); + drv_data->apb2ape_clk = NULL; + } + if (drv_data->ape_emc_clk) { clk_disable_unprepare(drv_data->ape_emc_clk); dev_dbg(dev, "ape.emc clock disabled\n"); drv_data->ape_emc_clk = NULL; } - - - if (drv_data->ahub_clk) { - clk_disable_unprepare(drv_data->ahub_clk); - dev_dbg(dev, "ahub clock disabled\n"); - drv_data->ahub_clk = NULL; - } } static int nvadsp_clocks_enable(struct platform_device *pdev) { struct nvadsp_drv_data *drv_data = platform_get_drvdata(pdev); struct device *dev = &pdev->dev; - uint32_t val; int ret = 0; - drv_data->ahub_clk = clk_get_sys("nvadsp", "ahub"); - if (IS_ERR_OR_NULL(drv_data->ahub_clk)) { - dev_err(dev, "unable to find ahub clock\n"); - ret = PTR_ERR(drv_data->ahub_clk); - goto end; - } - ret = clk_prepare_enable(drv_data->ahub_clk); - if (ret) { - dev_err(dev, "unable to enable ahub clock\n"); - goto end; - } - dev_dbg(dev, "ahub clock enabled\n"); - - drv_data->ape_clk = clk_get_sys(NULL, "adsp.ape"); + drv_data->ape_clk = devm_clk_get(dev, "adsp.ape"); if (IS_ERR_OR_NULL(drv_data->ape_clk)) { - dev_err(dev, "unable to find ape clock\n"); + dev_err(dev, "unable to find adsp.ape clock\n"); ret = PTR_ERR(drv_data->ape_clk); goto end; } ret = clk_prepare_enable(drv_data->ape_clk); if (ret) { - dev_err(dev, "unable to enable ape clock\n"); + dev_err(dev, "unable to enable adsp.ape clock\n"); goto end; } dev_dbg(dev, "ape clock enabled\n"); - drv_data->adsp_clk = clk_get_sys(NULL, "adsp"); + drv_data->adsp_clk = devm_clk_get(dev, "adsp"); if (IS_ERR_OR_NULL(drv_data->adsp_clk)) { dev_err(dev, "unable to find adsp clock\n"); ret = PTR_ERR(drv_data->adsp_clk); @@ -113,51 +94,50 @@ static int nvadsp_clocks_enable(struct platform_device *pdev) dev_err(dev, "unable to enable adsp clock\n"); goto end; } + drv_data->adsp_cpu_clk = drv_data->adsp_clk; - drv_data->adsp_cpu_clk = clk_get_sys(NULL, "adsp_cpu"); - if (IS_ERR_OR_NULL(drv_data->adsp_cpu_clk)) { - dev_err(dev, "unable to find adsp cpu clock\n"); - ret = PTR_ERR(drv_data->adsp_cpu_clk); + drv_data->adsp_neon_clk = devm_clk_get(dev, "adspneon"); + if (IS_ERR_OR_NULL(drv_data->adsp_neon_clk)) { + dev_err(dev, "unable to find adsp neon clock\n"); + ret = PTR_ERR(drv_data->adsp_neon_clk); goto end; } - ret = clk_prepare_enable(drv_data->adsp_cpu_clk); + ret = clk_prepare_enable(drv_data->adsp_neon_clk); if (ret) { - dev_err(dev, "unable to enable adsp cpu clock\n"); + dev_err(dev, "unable to enable adsp neon clock\n"); goto end; } dev_dbg(dev, "adsp cpu clock enabled\n"); - drv_data->ape_emc_clk = clk_get_sys("ape", "emc"); + drv_data->ape_emc_clk = devm_clk_get(dev, "adsp.emc"); if (IS_ERR_OR_NULL(drv_data->ape_emc_clk)) { - dev_err(dev, "unable to find ape.emc clock\n"); + dev_err(dev, "unable to find adsp.emc clock\n"); ret = PTR_ERR(drv_data->ape_emc_clk); goto end; } ret = clk_prepare_enable(drv_data->ape_emc_clk); if (ret) { - dev_err(dev, "unable to enable ape.emc clock\n"); + dev_err(dev, "unable to enable adsp.emc clock\n"); goto end; } dev_dbg(dev, "ape.emc is enabled\n"); - drv_data->uartape_clk = clk_get_sys("uartape", NULL); - if (IS_ERR_OR_NULL(drv_data->uartape_clk)) { - dev_err(dev, "unable to find uart ape clk\n"); - ret = PTR_ERR(drv_data->uartape_clk); + drv_data->apb2ape_clk = devm_clk_get(dev, "adsp.apb2ape"); + if (IS_ERR_OR_NULL(drv_data->apb2ape_clk)) { + dev_err(dev, "unable to find adsp.apb2ape clk\n"); + ret = PTR_ERR(drv_data->apb2ape_clk); goto end; } - ret = clk_prepare_enable(drv_data->uartape_clk); + ret = clk_prepare_enable(drv_data->apb2ape_clk); if (ret) { - dev_err(dev, "unable to enable uartape clock\n"); + dev_err(dev, "unable to enable adsp.apb2ape clock\n"); goto end; } - clk_set_rate(drv_data->uartape_clk, UART_BAUD_RATE * 16); - dev_dbg(dev, "uartape clock enabled\n"); - /* Set MAXCLKLATENCY value before ADSP deasserting reset */ - val = readl(drv_data->base_regs[AMISC] + ADSP_CONFIG); - writel(val | MAXCLKLATENCY, drv_data->base_regs[AMISC] + ADSP_CONFIG); + /* AHUB clock, UART clock is not being enabled as UART by default is + * disabled on t210 + */ dev_dbg(dev, "all clocks enabled\n"); return 0; end: @@ -301,7 +281,7 @@ static int __nvadsp_runtime_idle(struct device *dev) return 0; } -int __init nvadsp_pm_init(struct platform_device *pdev) +int nvadsp_pm_t21x_init(struct platform_device *pdev) { struct nvadsp_drv_data *drv_data = platform_get_drvdata(pdev); @@ -313,7 +293,17 @@ int __init nvadsp_pm_init(struct platform_device *pdev) } #endif /* CONFIG_PM */ -int __init nvadsp_reset_init(struct platform_device *pdev) +int nvadsp_reset_t21x_init(struct platform_device *pdev) { - return 0; + struct nvadsp_drv_data *drv_data = platform_get_drvdata(pdev); + struct device *dev = &pdev->dev; + int ret = 0; + + drv_data->adspall_rst = devm_reset_control_get(dev, "adspall"); + if (IS_ERR_OR_NULL(drv_data->adspall_rst)) { + ret = PTR_ERR(drv_data->adspall_rst); + dev_err(dev, "unable to get adspall reset %d\n", ret); + } + + return ret; } diff --git a/drivers/platform/tegra/nvadsp/dev-t21x.h b/drivers/platform/tegra/nvadsp/dev-t21x.h index 27bc7e81..1f4c2352 100644 --- a/drivers/platform/tegra/nvadsp/dev-t21x.h +++ b/drivers/platform/tegra/nvadsp/dev-t21x.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2015-2016, NVIDIA Corporation. All rights reserved. + * Copyright (C) 2015-2017, 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 @@ -15,38 +15,8 @@ #ifndef __TEGRA_NVADSP_DEV_T21X_H #define __TEGRA_NVADSP_DEV_T21X_H -/* - * Note: These enums should be aligned to the regs mentioned in the - * device tree -*/ -enum { - AMC, - AMISC, - ABRIDGE, - UNIT_FPGA_RST, - APE_MAX_REG -}; - -enum { - ADSP_DRAM1, - ADSP_DRAM2, - ADSP_MAX_DRAM_MAP -}; - -/* - * Note: These enums should be aligned to the adsp_mem node mentioned in the - * device tree -*/ -enum adsp_mem_dt { - ADSP_OS_ADDR, - ADSP_OS_SIZE, - ADSP_APP_ADDR, - ADSP_APP_SIZE, - ARAM_ALIAS_0_ADDR, - ARAM_ALIAS_0_SIZE, - ACSR_ADDR, /* ACSR: ADSP CPU SHARED REGION */ - ACSR_SIZE, - ADSP_MEM_END, -}; +int nvadsp_reset_t21x_init(struct platform_device *pdev); +int nvadsp_os_t21x_init(struct platform_device *pdev); +int nvadsp_pm_t21x_init(struct platform_device *pdev); #endif /* __TEGRA_NVADSP_DEV_T21X_H */ diff --git a/drivers/platform/tegra/nvadsp/dev.c b/drivers/platform/tegra/nvadsp/dev.c index 5366e156..feef42b4 100644 --- a/drivers/platform/tegra/nvadsp/dev.c +++ b/drivers/platform/tegra/nvadsp/dev.c @@ -3,7 +3,7 @@ * * A device driver for ADSP and APE * - * Copyright (C) 2014-2016, NVIDIA Corporation. All rights reserved. + * Copyright (C) 2014-2017, 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 @@ -27,12 +27,13 @@ #include #include #include -#include +#include #include #include #include #include #include +#include #include "dev.h" #include "os.h" @@ -40,6 +41,9 @@ #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 @@ -220,6 +224,7 @@ static int __init nvadsp_probe(struct platform_device *pdev) 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) @@ -390,11 +395,58 @@ static int nvadsp_remove(struct platform_device *pdev) } #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, + }, + .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, + }, + .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 = NULL, }, - { .compatible = "nvidia,tegra18x-adsp", .data = NULL, }, - { .compatible = "nvidia,tegra18x-adsp-hv", .data = NULL, }, - {}, + { + .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 diff --git a/drivers/platform/tegra/nvadsp/dev.h b/drivers/platform/tegra/nvadsp/dev.h index adc96fa0..c31ef749 100644 --- a/drivers/platform/tegra/nvadsp/dev.h +++ b/drivers/platform/tegra/nvadsp/dev.h @@ -3,7 +3,7 @@ * * A header file for Host driver for ADSP and APE * - * Copyright (C) 2014-2016, NVIDIA Corporation. All rights reserved. + * Copyright (C) 2014-2017, 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 @@ -26,15 +26,43 @@ #include -#if defined(CONFIG_ARCH_TEGRA_21x_SOC) -#include "dev-t21x.h" -#else -#include "dev-t18x.h" -#endif /* CONFIG_ARCH_TEGRA_21x_SOC */ - #include "hwmailbox.h" #include "amc.h" -#include "os.h" + +/* + * Note: These enums should be aligned to the regs mentioned in the + * device tree +*/ +enum { + AMC, + AMISC, + ABRIDGE, + UNIT_FPGA_RST, + AHSP, + APE_MAX_REG +}; + +enum { + ADSP_DRAM1, + ADSP_DRAM2, + ADSP_MAX_DRAM_MAP +}; + +/* + * Note: These enums should be aligned to the adsp_mem node mentioned in the + * device tree +*/ +enum adsp_mem_dt { + ADSP_OS_ADDR, + ADSP_OS_SIZE, + ADSP_APP_ADDR, + ADSP_APP_SIZE, + ARAM_ALIAS_0_ADDR, + ARAM_ALIAS_0_SIZE, + ACSR_ADDR, /* ACSR: ADSP CPU SHARED REGION */ + ACSR_SIZE, + ADSP_MEM_END, +}; enum adsp_evp_dt { ADSP_EVP_BASE, @@ -81,6 +109,37 @@ struct nvadsp_pm_state { void *evp_ptr; }; +struct nvadsp_hwmb { + u32 reg_idx; + u32 hwmbox0_reg; + u32 hwmbox1_reg; + u32 hwmbox2_reg; + u32 hwmbox3_reg; + u32 hwmbox4_reg; + u32 hwmbox5_reg; + u32 hwmbox6_reg; + u32 hwmbox7_reg; +}; + + +typedef int (*reset_init) (struct platform_device *pdev); +typedef int (*os_init) (struct platform_device *pdev); +#ifdef CONFIG_PM +typedef int (*pm_init) (struct platform_device *pdev); +#endif + +struct nvadsp_chipdata { + struct nvadsp_hwmb hwmb; + reset_init reset_init; + os_init os_init; +#ifdef CONFIG_PM + pm_init pm_init; +#endif + int wdt_irq; + int start_irq; + int end_irq; +}; + struct nvadsp_drv_data { void __iomem **base_regs; void __iomem **base_regs_saved; @@ -100,6 +159,7 @@ struct nvadsp_drv_data { struct clk *ape_clk; struct clk *apb2ape_clk; struct clk *adsp_clk; + struct clk *aclk_clk; struct clk *adsp_cpu_clk; struct clk *adsp_neon_clk; struct clk *ape_emc_clk; @@ -147,6 +207,8 @@ struct nvadsp_drv_data { struct tegra_bwmgr_client *bwmgr; u32 evp_base[ADSP_EVP_END]; + + const struct nvadsp_chipdata *chip_data; }; #define ADSP_CONFIG 0x04 @@ -183,8 +245,24 @@ void emc_dfs_exit(void); #endif #ifdef CONFIG_PM -int __init nvadsp_pm_init(struct platform_device *pdev); +static inline int __init nvadsp_pm_init(struct platform_device *pdev) +{ + struct nvadsp_drv_data *drv_data = platform_get_drvdata(pdev); + + if (drv_data->chip_data->pm_init) + return drv_data->chip_data->pm_init(pdev); + + return -EINVAL; +} #endif -int __init nvadsp_reset_init(struct platform_device *pdev); +static inline int __init nvadsp_reset_init(struct platform_device *pdev) +{ + struct nvadsp_drv_data *drv_data = platform_get_drvdata(pdev); + + if (drv_data->chip_data->reset_init) + return drv_data->chip_data->reset_init(pdev); + + return -EINVAL; +} #endif /* __TEGRA_NVADSP_DEV_H */ diff --git a/drivers/platform/tegra/nvadsp/hwmailbox-t21x.h b/drivers/platform/tegra/nvadsp/hwmailbox-t21x.h deleted file mode 100644 index 597b6b9f..00000000 --- a/drivers/platform/tegra/nvadsp/hwmailbox-t21x.h +++ /dev/null @@ -1,25 +0,0 @@ -/* - * Copyright (c) 2015, NVIDIA CORPORATION. All rights reserved. - * - * This program is free software; you can redistribute it and/or modify it - * under the terms and conditions of the GNU General Public License, - * version 2, as published by the Free Software Foundation. - * - * This program is distributed in the hope 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. - */ - -#ifndef __HWMAILBOX_T21X_H -#define __HWMAILBOX_T21X_H - -#define HWMB_REG_IDX AMISC - -/* Mailbox register Offsets in AMISC */ -#define HWMBOX0_REG 0x58 -#define HWMBOX1_REG 0X5C -#define HWMBOX2_REG 0x60 -#define HWMBOX3_REG 0x64 - -#endif /* __HWMAILBOX_T21X_H */ diff --git a/drivers/platform/tegra/nvadsp/hwmailbox.c b/drivers/platform/tegra/nvadsp/hwmailbox.c index 57a581eb..ebb70b8c 100644 --- a/drivers/platform/tegra/nvadsp/hwmailbox.c +++ b/drivers/platform/tegra/nvadsp/hwmailbox.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014-2016, NVIDIA CORPORATION. All rights reserved. + * Copyright (c) 2014-2017, NVIDIA CORPORATION. All rights reserved. * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU General Public License, @@ -21,19 +21,6 @@ #include "dev.h" -/* - * Mailbox 0 is for receiving messages - * from ADSP i.e. CPU <-- ADSP. - */ -#define RECV_HWMBOX HWMBOX0_REG -#define INT_RECV_HWMBOX INT_AMISC_MBOX_FULL0 - -/* - * Mailbox 1 is for sending messages - * to ADSP i.e. CPU --> ADSP - */ -#define SEND_HWMBOX HWMBOX1_REG -#define INT_SEND_HWMBOX INT_AMISC_MBOX_EMPTY1 static struct platform_device *nvadsp_pdev; static struct nvadsp_drv_data *nvadsp_drv_data; @@ -43,15 +30,42 @@ static bool is_hwmbox_busy; static int hwmbox_last_msg; #endif +/* + * Mailbox 0 is for receiving messages + * from ADSP i.e. CPU <-- ADSP. + */ +#define INT_RECV_HWMBOX INT_AMISC_MBOX_FULL0 -static inline u32 hwmbox_readl(u32 reg) +static inline u32 recv_hwmbox(void) { - return readl(nvadsp_drv_data->base_regs[HWMB_REG_IDX] + reg); + return nvadsp_drv_data->chip_data->hwmb.hwmbox0_reg; } -static inline void hwmbox_writel(u32 val, u32 reg) +/* + * Mailbox 1 is for sending messages + * to ADSP i.e. CPU --> ADSP + */ +#define INT_SEND_HWMBOX INT_AMISC_MBOX_EMPTY1 + +static inline u32 send_hwmbox(void) { - writel(val, nvadsp_drv_data->base_regs[HWMB_REG_IDX] + reg); + return nvadsp_drv_data->chip_data->hwmb.hwmbox1_reg; +} + + +u32 hwmb_reg_idx(void) +{ + return nvadsp_drv_data->chip_data->hwmb.reg_idx; +} + +u32 hwmbox_readl(u32 reg) +{ + return readl(nvadsp_drv_data->base_regs[hwmb_reg_idx()] + reg); +} + +void hwmbox_writel(u32 val, u32 reg) +{ + writel(val, nvadsp_drv_data->base_regs[hwmb_reg_idx()] + reg); } @@ -61,8 +75,8 @@ static inline void hwmbox_writel(u32 val, u32 reg) void dump_mailbox_regs(void) { dev_info(&nvadsp_pdev->dev, "dumping hwmailbox registers ...\n"); - PRINT_HWMBOX(RECV_HWMBOX); - PRINT_HWMBOX(SEND_HWMBOX); + PRINT_HWMBOX(recv_hwmbox()); + PRINT_HWMBOX(send_hwmbox()); dev_info(&nvadsp_pdev->dev, "end of dump ....\n"); } @@ -135,7 +149,7 @@ status_t nvadsp_hwmbox_send_data(uint16_t mid, uint32_t data, uint32_t flags) #ifdef CONFIG_MBOX_ACK_HANDLER hwmbox_last_msg = data; #endif - hwmbox_writel(data, SEND_HWMBOX); + hwmbox_writel(data, send_hwmbox()); } else { pr_debug("nvadsp_mbox_send: enqueue data\n"); ret = hwmboxq_enqueue(&nvadsp_drv_data->hwmbox_send_queue, @@ -177,7 +191,7 @@ static irqreturn_t hwmbox_send_empty_int_handler(int irq, void *devid) spin_lock_irqsave(lock, lockflags); - data = hwmbox_readl(SEND_HWMBOX); + data = hwmbox_readl(send_hwmbox()); if (data != PREPARE_HWMBOX_EMPTY_MSG()) dev_err(dev, "last mailbox sent failed with 0x%x\n", data); @@ -203,7 +217,7 @@ static irqreturn_t hwmbox_send_empty_int_handler(int irq, void *devid) #ifdef CONFIG_MBOX_ACK_HANDLER hwmbox_last_msg = data; #endif - hwmbox_writel(data, SEND_HWMBOX); + hwmbox_writel(data, send_hwmbox()); dev_dbg(dev, "Writing 0x%x to SEND_HWMBOX\n", data); } else { is_hwmbox_busy = false; @@ -218,8 +232,8 @@ static irqreturn_t hwmbox_recv_full_int_handler(int irq, void *devid) uint32_t data; int ret; - data = hwmbox_readl(RECV_HWMBOX); - hwmbox_writel(PREPARE_HWMBOX_EMPTY_MSG(), RECV_HWMBOX); + data = hwmbox_readl(recv_hwmbox()); + hwmbox_writel(PREPARE_HWMBOX_EMPTY_MSG(), recv_hwmbox()); if (IS_HWMBOX_MSG_SMSG(data)) { uint16_t mboxid = HWMBOX_SMSG_MID(data); diff --git a/drivers/platform/tegra/nvadsp/hwmailbox.h b/drivers/platform/tegra/nvadsp/hwmailbox.h index 1115cfbe..2071a83e 100644 --- a/drivers/platform/tegra/nvadsp/hwmailbox.h +++ b/drivers/platform/tegra/nvadsp/hwmailbox.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014-2016, NVIDIA CORPORATION. All rights reserved. + * Copyright (c) 2014-2017, NVIDIA CORPORATION. All rights reserved. * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU General Public License, @@ -19,12 +19,6 @@ #include #include -#if defined(CONFIG_ARCH_TEGRA_21x_SOC) -#include "hwmailbox-t21x.h" -#else -#include "hwmailbox-t18x.h" -#endif /* CONFIG_ARCH_TEGRA_21x_SOC */ - /* * The interpretation of hwmailbox content is: * 31 30 29 0 @@ -109,6 +103,9 @@ struct hwmbox_queue { spinlock_t lock; }; +u32 hwmb_reg_idx(void); +u32 hwmbox_readl(u32 reg); +void hwmbox_writel(u32 val, u32 reg); int nvadsp_hwmbox_init(struct platform_device *); status_t nvadsp_hwmbox_send_data(uint16_t, uint32_t, uint32_t); void dump_mailbox_regs(void); diff --git a/drivers/platform/tegra/nvadsp/os-t18x.c b/drivers/platform/tegra/nvadsp/os-t18x.c new file mode 100644 index 00000000..a46e9d20 --- /dev/null +++ b/drivers/platform/tegra/nvadsp/os-t18x.c @@ -0,0 +1,51 @@ +/* + * Copyright (C) 2015-2017, 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 +#include +#include +#include + +#include "dev.h" + +static void nvadsp_dbell_handler(void *data) +{ + struct platform_device *pdev = data; + struct device *dev = &pdev->dev; + + dev_info(dev, "APE DBELL handler\n"); +} + + +int nvadsp_os_t18x_init(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct nvadsp_drv_data *drv_data = platform_get_drvdata(pdev); + struct device_node *node = dev->of_node; + int ret; + + if (of_device_is_compatible(node, "nvidia,tegra18x-adsp-hv")) { + hwmbox_writel(1, drv_data->chip_data->hwmb.hwmbox5_reg); + return 0; + } + + ret = tegra_hsp_db_add_handler(HSP_MASTER_APE, + nvadsp_dbell_handler, pdev); + if (ret) { + dev_err(dev, "failed to add HSP_MASTER_APE DB handler\n"); + goto end; + } + end: + return ret; +} diff --git a/drivers/platform/tegra/nvadsp/os-t21x.c b/drivers/platform/tegra/nvadsp/os-t21x.c index db1c61bf..a445d5a0 100644 --- a/drivers/platform/tegra/nvadsp/os-t21x.c +++ b/drivers/platform/tegra/nvadsp/os-t21x.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2016, NVIDIA Corporation. All rights reserved. + * Copyright (C) 2016-2017, 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 @@ -13,8 +13,9 @@ */ #include "dev.h" +#include "dev-t21x.h" -int nvadsp_os_init(struct platform_device *pdev) +int nvadsp_os_t21x_init(struct platform_device *pdev) { return 0; } diff --git a/drivers/platform/tegra/nvadsp/os-t21x.h b/drivers/platform/tegra/nvadsp/os-t21x.h index e89d5e71..e69de29b 100644 --- a/drivers/platform/tegra/nvadsp/os-t21x.h +++ b/drivers/platform/tegra/nvadsp/os-t21x.h @@ -1,21 +0,0 @@ -/* - * Copyright (C) 2015, 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. - * - */ -#ifndef __TEGRA_NVADSP_OS_T21X_H -#define __TEGRA_NVADSP_OS_T21X_H - -#include - -#define ADSP_WDT_INT INT_ADSP_WDT - -#endif /* __TEGRA_NVADSP_OS_T21X_H */ diff --git a/drivers/platform/tegra/nvadsp/os.c b/drivers/platform/tegra/nvadsp/os.c index 4c972196..7fbf7b15 100644 --- a/drivers/platform/tegra/nvadsp/os.c +++ b/drivers/platform/tegra/nvadsp/os.c @@ -5,7 +5,7 @@ * Copyright (C) 2011 Texas Instruments, Inc. * Copyright (C) 2011 Google, Inc. * - * Copyright (C) 2014-2016 NVIDIA Corporation. All rights reserved. + * Copyright (C) 2014-2017, 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 @@ -27,7 +27,7 @@ #include #include #include -#include +#include #include #include #include @@ -36,6 +36,8 @@ #include #include #include +#include +#include #include @@ -804,7 +806,7 @@ static int nvadsp_set_ape_freq(struct nvadsp_drv_data *drv_data) return ret; } -static int set_adsp_clks_and_timer_prescalar(struct nvadsp_drv_data *drv_data) +static int nvadsp_t210_set_clks_and_prescalar(struct nvadsp_drv_data *drv_data) { struct nvadsp_shared_mem *shared_mem = drv_data->shared_adsp_os_data; struct nvadsp_os_args *os_args = &shared_mem->os_args; @@ -859,7 +861,7 @@ end: return ret; } -static int set_adsp_clks(struct nvadsp_drv_data *drv_data) +static int nvadsp_set_adsp_clks(struct nvadsp_drv_data *drv_data) { struct nvadsp_shared_mem *shared_mem = drv_data->shared_adsp_os_data; struct nvadsp_os_args *os_args = &shared_mem->os_args; @@ -870,13 +872,16 @@ static int set_adsp_clks(struct nvadsp_drv_data *drv_data) int ret = 0; adsp_freq = drv_data->adsp_freq_hz; /* in Hz*/ - max_adsp_freq = clk_round_rate(drv_data->adsp_clk, ULONG_MAX); + + /* round rate shall be used with adsp parent clk i.e. aclk */ + max_adsp_freq = clk_round_rate(drv_data->aclk_clk, ULONG_MAX); /* Set max adsp boot freq */ if (!adsp_freq) adsp_freq = max_adsp_freq; - ret = clk_set_rate(drv_data->adsp_clk, adsp_freq); + /* set rate shall be used with adsp parent clk i.e. aclk */ + ret = clk_set_rate(drv_data->aclk_clk, adsp_freq); if (ret) { dev_err(dev, "setting adsp_freq:%luHz failed.\n", adsp_freq); dev_err(dev, "max_adsp_freq:%luHz\n", max_adsp_freq); @@ -892,25 +897,25 @@ end: return ret; } -static int __deassert_adsp(struct nvadsp_drv_data *drv_data) +static int __deassert_adsp(struct nvadsp_drv_data *d) { - struct device *dev = &priv.pdev->dev; + struct platform_device *pdev = d->pdev; + struct device *dev = &pdev->dev; + int ret = 0; - if (drv_data->adsp_unit_fpga) { - dev_info(dev, "De-asserting ADSP UNIT-FPGA\n"); - writel(drv_data->unit_fpga_reset[ADSP_DEASSERT], - priv.unit_fpga_reset_reg); - return 0; - } + /* + * The ADSP_ALL reset in BPMP-FW is overloaded to de-assert + * all 7 resets i.e. ADSP, ADSPINTF, ADSPDBG, ADSPNEON, ADSPPERIPH, + * ADSPSCU and ADSPWDT resets. The BPMP-FW also takes care + * of specific de-assert sequence and delays between them. + * So de-resetting only ADSP reset is sufficient to de-reset + * all ADSP sub-modules. + */ + ret = reset_control_deassert(d->adspall_rst); + if (ret) + dev_err(dev, "failed to deassert adsp\n"); - if (drv_data->adsp_clk) { - dev_dbg(dev, "deasserting adsp...\n"); - tegra_periph_reset_deassert(drv_data->adsp_clk); - udelay(200); - return 0; - } - - return -EINVAL; + return ret; } static int nvadsp_deassert_adsp(struct nvadsp_drv_data *drv_data) @@ -923,26 +928,23 @@ static int nvadsp_deassert_adsp(struct nvadsp_drv_data *drv_data) return ret; } -static int __assert_adsp(struct nvadsp_drv_data *drv_data) +static int __assert_adsp(struct nvadsp_drv_data *d) { - struct device *dev = &priv.pdev->dev; + struct platform_device *pdev = d->pdev; + struct device *dev = &pdev->dev; + int ret = 0; - if (drv_data->adsp_unit_fpga) { - if (drv_data->unit_fpga_reset[ADSP_ASSERT]) { - dev_info(dev, "Asserting ADSP UNIT-FPGA\n"); - writel(drv_data->unit_fpga_reset[ADSP_ASSERT], - priv.unit_fpga_reset_reg); - } - return 0; - } + /* + * The ADSP_ALL reset in BPMP-FW is overloaded to assert + * all 7 resets i.e. ADSP, ADSPINTF, ADSPDBG, ADSPNEON, + * ADSPPERIPH, ADSPSCU and ADSPWDT resets. So resetting + * only ADSP reset is sufficient to reset all ADSP sub-modules. + */ + ret = reset_control_assert(d->adspall_rst); + if (ret) + dev_err(dev, "failed to assert adsp\n"); - if (drv_data->adsp_clk) { - tegra_periph_reset_assert(drv_data->adsp_clk); - udelay(200); - return 0; - } - - return -EINVAL; + return ret; } static int nvadsp_assert_adsp(struct nvadsp_drv_data *drv_data) @@ -967,7 +969,7 @@ static int nvadsp_set_boot_freqs(struct nvadsp_drv_data *drv_data) if (of_device_is_compatible(node, "nvidia,tegra210-adsp")) { if (drv_data->adsp_cpu_clk) { - ret = set_adsp_clks_and_timer_prescalar(drv_data); + ret = nvadsp_t210_set_clks_and_prescalar(drv_data); if (ret) goto end; } else { @@ -976,7 +978,7 @@ static int nvadsp_set_boot_freqs(struct nvadsp_drv_data *drv_data) } } else { if (drv_data->adsp_clk) { - ret = set_adsp_clks(drv_data); + ret = nvadsp_set_adsp_clks(drv_data); if (ret) goto end; } else { @@ -1136,10 +1138,13 @@ static void dump_adsp_logs(void) static void print_agic_irq_states(void) { + struct nvadsp_drv_data *drv_data = platform_get_drvdata(priv.pdev); + int start_irq = drv_data->chip_data->start_irq; + int end_irq = drv_data->chip_data->end_irq; struct device *dev = &priv.pdev->dev; int i; - for (i = INT_AGIC_START; i < INT_AGIC_END; i++) { + for (i = start_irq; i < end_irq; i++) { dev_info(dev, "irq %d is %s and %s\n", i, tegra_agic_irq_is_pending(i) ? "pending" : "not pending", @@ -1298,10 +1303,12 @@ static void __nvadsp_os_stop(bool reload) } #endif - writel(ENABLE_MBOX2_FULL_INT, priv.hwmailbox_base + HWMBOX2_REG); + writel(ENABLE_MBOX2_FULL_INT, + priv.hwmailbox_base + drv_data->chip_data->hwmb.hwmbox2_reg); err = wait_for_completion_interruptible_timeout(&entered_wfi, msecs_to_jiffies(ADSP_WFE_TIMEOUT)); - writel(DISABLE_MBOX2_FULL_INT, priv.hwmailbox_base + HWMBOX2_REG); + writel(DISABLE_MBOX2_FULL_INT, + priv.hwmailbox_base + drv_data->chip_data->hwmb.hwmbox2_reg); /* * ADSP needs to be in WFI/WFE state to properly reset it. @@ -1366,7 +1373,7 @@ void nvadsp_os_stop(void) priv.os_running = drv_data->adsp_os_running = false; #ifdef CONFIG_PM - if (pm_runtime_put_sync(dev)) + if (pm_runtime_put_sync(dev) < 0) dev_err(dev, "failed in pm_runtime_put_sync\n"); #endif end: @@ -1404,7 +1411,7 @@ int nvadsp_os_suspend(void) #ifdef CONFIG_PM struct device *dev = &priv.pdev->dev; ret = pm_runtime_put_sync(&priv.pdev->dev); - if (ret) + if (ret < 0) dev_err(dev, "failed in pm_runtime_put_sync\n"); #endif priv.os_running = drv_data->adsp_os_running = false; @@ -1425,20 +1432,21 @@ static void nvadsp_os_restart(struct work_struct *work) container_of(work, struct nvadsp_os_data, restart_os_work); struct nvadsp_drv_data *drv_data = platform_get_drvdata(data->pdev); int wdt_virq = drv_data->agic_irqs[WDT_VIRQ]; + int wdt_irq = drv_data->chip_data->wdt_irq; struct device *dev = &data->pdev->dev; disable_irq(wdt_virq); dump_adsp_sys(); nvadsp_os_stop(); - if (tegra_agic_irq_is_active(ADSP_WDT_INT)) { + if (tegra_agic_irq_is_active(wdt_irq)) { dev_info(dev, "wdt interrupt is active hence clearing\n"); - tegra_agic_clear_active(ADSP_WDT_INT); + tegra_agic_clear_active(wdt_irq); } - if (tegra_agic_irq_is_pending(ADSP_WDT_INT)) { + if (tegra_agic_irq_is_pending(wdt_irq)) { dev_info(dev, "wdt interrupt is pending hence clearing\n"); - tegra_agic_clear_pending(ADSP_WDT_INT); + tegra_agic_clear_pending(wdt_irq); } dev_info(dev, "wdt interrupt is not pending or active...enabling\n"); @@ -1546,6 +1554,13 @@ static int adsp_create_os_version(struct dentry *adsp_debugfs_root) } #endif +static ssize_t tegrafw_read_adsp(struct device *dev, + char *data, size_t size) +{ + nvadsp_get_os_version(data, size); + return strlen(data); +} + int __init nvadsp_os_probe(struct platform_device *pdev) { struct nvadsp_drv_data *drv_data = platform_get_drvdata(pdev); @@ -1553,11 +1568,10 @@ int __init nvadsp_os_probe(struct platform_device *pdev) int wfi_virq = drv_data->agic_irqs[WFI_VIRQ]; uint16_t com_mid = ADSP_COM_MBOX_ID; struct device *dev = &pdev->dev; - struct device_node *node = dev->of_node; int ret = 0; priv.unit_fpga_reset_reg = drv_data->base_regs[UNIT_FPGA_RST]; - priv.hwmailbox_base = drv_data->base_regs[HWMB_REG_IDX]; + priv.hwmailbox_base = drv_data->base_regs[hwmb_reg_idx()]; priv.dram_region = drv_data->dram_region; priv.adsp_os_addr = drv_data->adsp_mem[ADSP_OS_ADDR]; @@ -1579,19 +1593,18 @@ int __init nvadsp_os_probe(struct platform_device *pdev) goto end; } - writel(DISABLE_MBOX2_FULL_INT, priv.hwmailbox_base + HWMBOX2_REG); + writel(DISABLE_MBOX2_FULL_INT, + priv.hwmailbox_base + drv_data->chip_data->hwmb.hwmbox2_reg); if (of_device_is_compatible(dev->of_node, "nvidia,tegra210-adsp")) { drv_data->assert_adsp = __assert_adsp; drv_data->deassert_adsp = __deassert_adsp; } - if (!of_device_is_compatible(node, "nvidia,tegra18x-adsp-hv")) { - ret = nvadsp_os_init(pdev); - if (ret) { - dev_err(dev, "failed to init os\n"); - goto end; - } + ret = nvadsp_os_init(pdev); + if (ret) { + dev_err(dev, "failed to init os\n"); + goto end; } ret = nvadsp_mbox_open(&adsp_com_mbox, &com_mid, "adsp_com_mbox", @@ -1622,6 +1635,8 @@ int __init nvadsp_os_probe(struct platform_device *pdev) #endif /* CONFIG_DEBUG_FS */ + devm_tegrafw_register(dev, "APE", TFW_DONT_CACHE, + tegrafw_read_adsp, NULL); end: return ret; } diff --git a/drivers/platform/tegra/nvadsp/os.h b/drivers/platform/tegra/nvadsp/os.h index 343ef0de..0b84f8cf 100644 --- a/drivers/platform/tegra/nvadsp/os.h +++ b/drivers/platform/tegra/nvadsp/os.h @@ -3,7 +3,7 @@ * * A header file containing data structures shared with ADSP OS * - * Copyright (C) 2014-2016 NVIDIA Corporation. All rights reserved. + * Copyright (C) 2014-2017 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 @@ -21,11 +21,7 @@ #include #include "adsp_shared_struct.h" -#if defined(CONFIG_ARCH_TEGRA_21x_SOC) -#include "os-t21x.h" -#else -#include "os-t18x.h" -#endif /* CONFIG_ARCH_TEGRA_21x_SOC */ +#include "dev.h" #define CONFIG_ADSP_DRAM_LOG_WITH_TAG 1 /* enable profiling of load init start */ @@ -140,8 +136,17 @@ struct app_start_stats { u64 adsp_receive_timestamp; }; +static inline int nvadsp_os_init(struct platform_device *pdev) +{ + struct nvadsp_drv_data *drv_data = platform_get_drvdata(pdev); + + if (drv_data->chip_data->os_init) + return drv_data->chip_data->os_init(pdev); + + return -EINVAL; +} + int nvadsp_os_probe(struct platform_device *); -int nvadsp_os_init(struct platform_device *pdev); int nvadsp_app_module_probe(struct platform_device *); int adsp_add_load_mappings(phys_addr_t, void *, int); struct elf32_shdr *nvadsp_get_section(const struct firmware *, char *); From 49aad0ca789fdee8732107ca89143aa67f46d3d7 Mon Sep 17 00:00:00 2001 From: Ajay Nandakumar Date: Tue, 27 Dec 2016 13:51:55 +0530 Subject: [PATCH 005/138] platform: nvadsp: Decrease size of shared memory Decrease the size of app message(send/receive) queue from 16KB from 4096 bytes. This is to reduce the foot print of adsp. Decreasing size of ADSP log buffer from 1MB to 16 KB. This helps reducing the amount of memory used by ADSP. Based on: platform: nvadsp: Decrease size of app msg queue platform: nvadsp: Reduce the size of adsp logger Bug 1774702 Bug 200270956 Change-Id: I3b4b5d28d2fe05ad9fe2b1247327497ca63767fe Signed-off-by: Ajay Nandakumar Reviewed-on: http://git-master/r/1468350 Signed-off-by: Nitin Kumbhar Reviewed-on: https://git-master.nvidia.com/r/1537320 Reviewed-by: Automatic_Commit_Validation_User Reviewed-by: svccoveritychecker GVS: Gerrit_Virtual_Submit --- drivers/platform/tegra/nvadsp/adsp_shared_struct.h | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/drivers/platform/tegra/nvadsp/adsp_shared_struct.h b/drivers/platform/tegra/nvadsp/adsp_shared_struct.h index f2c2d64e..db0bdf15 100644 --- a/drivers/platform/tegra/nvadsp/adsp_shared_struct.h +++ b/drivers/platform/tegra/nvadsp/adsp_shared_struct.h @@ -3,7 +3,7 @@ * * A header file containing shared data structures shared with ADSP OS * - * Copyright (C) 2015-2016 NVIDIA Corporation. All rights reserved. + * Copyright (C) 2015-2017 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 @@ -26,7 +26,7 @@ #define ADSP_OS_LOAD_TIMEOUT 5000 /* 5000 ms */ -#define DRAM_DEBUG_LOG_SIZE 0x100000 +#define DRAM_DEBUG_LOG_SIZE 0x4000 /* 16 KB */ #define NVADSP_NAME_SZ 128 @@ -102,11 +102,16 @@ union app_complete_status_message { /*ADSP message pool structure */ +#define ADSP_MAX_MSGQ_SIZE 4096 +#define ADSP_MAX_MSGQ_WSIZE (ADSP_MAX_MSGQ_SIZE / sizeof(int32_t)) +#define ADSP_MSGQ_MAX_QUEUE_WSIZE \ + (ADSP_MAX_MSGQ_WSIZE - (int32_t)MSGQ_HEADER_WSIZE) + union app_loader_msgq { msgq_t msgq; struct { int32_t header[MSGQ_HEADER_WSIZE]; - int32_t queue[MSGQ_MAX_QUEUE_WSIZE]; + int32_t queue[ADSP_MSGQ_MAX_QUEUE_WSIZE]; }; }; From 396966153abf2d77cb13628b378349f2010f3b36 Mon Sep 17 00:00:00 2001 From: Ajay Nandakumar Date: Mon, 24 Apr 2017 16:38:57 +0530 Subject: [PATCH 006/138] platform: nvadsp: remove dependency ARCH_TEGRA_APE Removing the dependency of ARCH_TEGRA_APE on 4.9 as this is uneedded CONFIG and APE can be enabled and disabled through the DT available. Bug 200270956 Change-Id: I5529e7359f7659baa9efd8781365721ec4105789 Signed-off-by: Ajay Nandakumar Reviewed-on: http://git-master/r/1468502 Signed-off-by: Nitin Kumbhar Reviewed-on: https://git-master.nvidia.com/r/1537322 Reviewed-by: Automatic_Commit_Validation_User Reviewed-by: svccoveritychecker GVS: Gerrit_Virtual_Submit --- drivers/platform/tegra/nvadsp/Kconfig | 2 -- 1 file changed, 2 deletions(-) diff --git a/drivers/platform/tegra/nvadsp/Kconfig b/drivers/platform/tegra/nvadsp/Kconfig index d7e990c5..6df39035 100644 --- a/drivers/platform/tegra/nvadsp/Kconfig +++ b/drivers/platform/tegra/nvadsp/Kconfig @@ -1,6 +1,5 @@ config TEGRA_NVADSP bool "Enable Host ADSP driver" - depends on ARCH_TEGRA_APE default n help Enables support for Host ADSP driver. @@ -54,7 +53,6 @@ config TEGRA_ADSP_FILEIO config TEGRA_EMC_APE_DFS bool "Enable emc dfs due to APE" - depends on ARCH_TEGRA_APE default n help Enable emc dfs due to APE DRAM access From 3d27131a1122580013dc3f2f05f2bfe9073df298 Mon Sep 17 00:00:00 2001 From: Ajay Nandakumar Date: Wed, 26 Apr 2017 14:09:23 +0530 Subject: [PATCH 007/138] platform: nvadsp: Add dependency on GIC_PM Add dependency on GIC_PM as ADSP needs APIs from gic pm driver. Bug 200270956 Change-Id: Ic3c30f366f93f3267bdb1a9634b4c5371006f7b8 Signed-off-by: Ajay Nandakumar Reviewed-on: http://git-master/r/1470306 Signed-off-by: Nitin Kumbhar Reviewed-on: https://git-master.nvidia.com/r/1537323 Reviewed-by: Automatic_Commit_Validation_User Reviewed-by: svccoveritychecker GVS: Gerrit_Virtual_Submit --- drivers/platform/tegra/nvadsp/Kconfig | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/platform/tegra/nvadsp/Kconfig b/drivers/platform/tegra/nvadsp/Kconfig index 6df39035..b6fb0f67 100644 --- a/drivers/platform/tegra/nvadsp/Kconfig +++ b/drivers/platform/tegra/nvadsp/Kconfig @@ -1,6 +1,7 @@ config TEGRA_NVADSP bool "Enable Host ADSP driver" default n + depends on ARM_GIC_PM help Enables support for Host ADSP driver. From 6e0764338a34a552ec64e27e7411a639eb7eb05a Mon Sep 17 00:00:00 2001 From: Ajay Nandakumar Date: Mon, 24 Apr 2017 16:41:16 +0530 Subject: [PATCH 008/138] platform: nvadsp: Select FIQ Enable FIQ when ADSP driver is enabled. An FIQ interrupt is a highest priorty interrupt and used when ADSP crashes to put ADSP in WFI. The ADSP needs to be put in WFI, so that all transcations on the bus are completed. Bug 200270956 Change-Id: Ifb177ba9e0486ff7b4761d95f68b09265510c55f Signed-off-by: Ajay Nandakumar Reviewed-on: http://git-master/r/1468503 Signed-off-by: Nitin Kumbhar Reviewed-on: https://git-master.nvidia.com/r/1537324 Reviewed-by: svccoveritychecker GVS: Gerrit_Virtual_Submit --- drivers/platform/tegra/nvadsp/Kconfig | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/platform/tegra/nvadsp/Kconfig b/drivers/platform/tegra/nvadsp/Kconfig index b6fb0f67..37f4b8eb 100644 --- a/drivers/platform/tegra/nvadsp/Kconfig +++ b/drivers/platform/tegra/nvadsp/Kconfig @@ -2,6 +2,7 @@ config TEGRA_NVADSP bool "Enable Host ADSP driver" default n depends on ARM_GIC_PM + select FIQ help Enables support for Host ADSP driver. From 2a016f531d6f099322017163376cae907cf64a46 Mon Sep 17 00:00:00 2001 From: Nitin Kumbhar Date: Wed, 3 May 2017 14:39:13 +0530 Subject: [PATCH 009/138] drivers: nvadsp: fix sparse warnings Fix following sparse warnings by including header dev-t21x.h, which has function declarations, in dev-t21x.c. dev-t21x.c:284:5: warning: symbol 'nvadsp_pm_t21x_init' was not declared. Should it be static? dev-t21x.c:296:5: warning: symbol 'nvadsp_reset_t21x_init' was not declared. Should it be static? Bug 200299572 Change-Id: I5f16da0852ab9083fba7395cfbd66ec012014663 Signed-off-by: Nitin Kumbhar Reviewed-on: http://git-master/r/1474440 Signed-off-by: Nitin Kumbhar Reviewed-on: https://git-master.nvidia.com/r/1537325 Reviewed-by: svccoveritychecker GVS: Gerrit_Virtual_Submit Reviewed-by: Amit Sharma (SW-TEGRA) --- drivers/platform/tegra/nvadsp/dev-t21x.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/platform/tegra/nvadsp/dev-t21x.c b/drivers/platform/tegra/nvadsp/dev-t21x.c index 1326190c..c36dc5f0 100644 --- a/drivers/platform/tegra/nvadsp/dev-t21x.c +++ b/drivers/platform/tegra/nvadsp/dev-t21x.c @@ -25,6 +25,7 @@ #include "dev.h" #include "amc.h" +#include "dev-t21x.h" #ifdef CONFIG_PM static void nvadsp_clocks_disable(struct platform_device *pdev) From 816abce5a93dc4ee93d277f40311a1b2147b9d99 Mon Sep 17 00:00:00 2001 From: Ajay Nandakumar Date: Wed, 10 May 2017 11:42:40 +0530 Subject: [PATCH 010/138] platform: nvadsp: move request/free irqs calls The request irqs are moved just before starting ADSP and freed when adsp is suspended/stopped. This is since the new agic driver is based on a device-driver model and requires all interrupts to be freed before it could suspend. Bug 200270956 Change-Id: I8ecd05ebe52020f11be79b9a1da37a85fed432ac Signed-off-by: Ajay Nandakumar Reviewed-on: http://git-master/r/1478838 Signed-off-by: Nitin Kumbhar Reviewed-on: https://git-master.nvidia.com/r/1537326 Reviewed-by: Automatic_Commit_Validation_User Reviewed-by: svccoveritychecker GVS: Gerrit_Virtual_Submit --- drivers/platform/tegra/nvadsp/amc.c | 30 ++++-- drivers/platform/tegra/nvadsp/dev.c | 5 - drivers/platform/tegra/nvadsp/dev.h | 6 +- drivers/platform/tegra/nvadsp/hwmailbox.c | 71 ++++++++------ drivers/platform/tegra/nvadsp/hwmailbox.h | 3 + drivers/platform/tegra/nvadsp/os.c | 108 ++++++++++++++++++---- 6 files changed, 157 insertions(+), 66 deletions(-) diff --git a/drivers/platform/tegra/nvadsp/amc.c b/drivers/platform/tegra/nvadsp/amc.c index 92803620..1d37d4f2 100644 --- a/drivers/platform/tegra/nvadsp/amc.c +++ b/drivers/platform/tegra/nvadsp/amc.c @@ -3,7 +3,7 @@ * * AMC and ARAM handling * - * Copyright (C) 2014-2016, NVIDIA Corporation. All rights reserved. + * Copyright (C) 2014-2017, 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 @@ -162,23 +162,33 @@ static irqreturn_t nvadsp_amc_error_int_handler(int irq, void *devid) return IRQ_HANDLED; } -status_t __init nvadsp_amc_init(struct platform_device *pdev) +void nvadsp_free_amc_interrupts(struct platform_device *pdev) { struct nvadsp_drv_data *drv = platform_get_drvdata(pdev); struct device *dev = &pdev->dev; - struct device_node *node = dev->of_node; + struct device_node *node; + + node = dev->of_node; + + if (!of_device_is_compatible(node, "nvidia,tegra18x-adsp-hv")) + devm_free_irq(dev, drv->agic_irqs[AMC_ERR_VIRQ], pdev); +} + +int nvadsp_setup_amc_interrupts(struct platform_device *pdev) +{ + struct nvadsp_drv_data *drv = platform_get_drvdata(pdev); + struct device *dev = &pdev->dev; + struct device_node *node; int ret = 0; + node = dev->of_node; nvadsp_pdev = pdev; nvadsp_drv_data = drv; - if (!of_device_is_compatible(node, "nvidia,tegra18x-adsp-hv")) { - dev_info(&pdev->dev, "Registering AMC Error Interrupt\n"); - ret = request_irq(drv->agic_irqs[AMC_ERR_VIRQ], - nvadsp_amc_error_int_handler, 0, "AMC error int", pdev); - } - - dev_info(&pdev->dev, "AMC/ARAM initialized.\n"); + if (!of_device_is_compatible(node, "nvidia,tegra18x-adsp-hv")) + ret = devm_request_irq(dev, drv->agic_irqs[AMC_ERR_VIRQ], + nvadsp_amc_error_int_handler, 0, + "AMC error int", pdev); return ret; } diff --git a/drivers/platform/tegra/nvadsp/dev.c b/drivers/platform/tegra/nvadsp/dev.c index feef42b4..0738f56c 100644 --- a/drivers/platform/tegra/nvadsp/dev.c +++ b/drivers/platform/tegra/nvadsp/dev.c @@ -322,11 +322,6 @@ static int __init nvadsp_probe(struct platform_device *pdev) if (ret < 0) goto out; #endif - - ret = nvadsp_amc_init(pdev); - if (ret) - goto err; - ret = nvadsp_hwmbox_init(pdev); if (ret) goto err; diff --git a/drivers/platform/tegra/nvadsp/dev.h b/drivers/platform/tegra/nvadsp/dev.h index c31ef749..653c93d9 100644 --- a/drivers/platform/tegra/nvadsp/dev.h +++ b/drivers/platform/tegra/nvadsp/dev.h @@ -146,8 +146,6 @@ struct nvadsp_drv_data { struct platform_device *pdev; struct resource *dram_region[ADSP_MAX_DRAM_MAP]; struct hwmbox_queue hwmbox_send_queue; - int hwmbox_send_virq; - int hwmbox_recv_virq; struct nvadsp_mbox **mboxes; unsigned long *mbox_ids; @@ -216,7 +214,9 @@ struct nvadsp_drv_data { #define UART_BAUD_RATE 9600 status_t nvadsp_mbox_init(struct platform_device *pdev); -status_t nvadsp_amc_init(struct platform_device *pdev); + +int nvadsp_setup_amc_interrupts(struct platform_device *pdev); +void nvadsp_free_amc_interrupts(struct platform_device *pdev); #ifdef CONFIG_TEGRA_ADSP_DFS void adsp_cpu_set_rate(unsigned long freq); diff --git a/drivers/platform/tegra/nvadsp/hwmailbox.c b/drivers/platform/tegra/nvadsp/hwmailbox.c index ebb70b8c..2b3b1240 100644 --- a/drivers/platform/tegra/nvadsp/hwmailbox.c +++ b/drivers/platform/tegra/nvadsp/hwmailbox.c @@ -266,43 +266,58 @@ static irqreturn_t hwmbox_recv_full_int_handler(int irq, void *devid) return IRQ_HANDLED; } +void nvadsp_free_hwmbox_interrupts(struct platform_device *pdev) +{ + + struct nvadsp_drv_data *drv = platform_get_drvdata(pdev); + struct device *dev = &pdev->dev; + int recv_virq, send_virq; + + recv_virq = drv->agic_irqs[MBOX_RECV_VIRQ]; + send_virq = drv->agic_irqs[MBOX_SEND_VIRQ]; + + devm_free_irq(dev, recv_virq, pdev); + devm_free_irq(dev, send_virq, pdev); +} + +int nvadsp_setup_hwmbox_interrupts(struct platform_device *pdev) +{ + struct nvadsp_drv_data *drv = platform_get_drvdata(pdev); + struct device *dev = &pdev->dev; + int recv_virq, send_virq; + int ret; + + recv_virq = drv->agic_irqs[MBOX_RECV_VIRQ]; + send_virq = drv->agic_irqs[MBOX_SEND_VIRQ]; + + ret = devm_request_irq(dev, recv_virq, hwmbox_recv_full_int_handler, + IRQF_TRIGGER_RISING, "hwmbox0_recv_full", pdev); + if (ret) + goto err; + + ret = devm_request_irq(dev, send_virq, hwmbox_send_empty_int_handler, + IRQF_TRIGGER_RISING, + "hwmbox1_send_empty", pdev); + if (ret) + goto free_interrupts; + + return ret; + + free_interrupts: + nvadsp_free_hwmbox_interrupts(pdev); + err: + return ret; +} + int __init nvadsp_hwmbox_init(struct platform_device *pdev) { struct nvadsp_drv_data *drv = platform_get_drvdata(pdev); - int recv_virq, send_virq; int ret = 0; nvadsp_pdev = pdev; nvadsp_drv_data = drv; - recv_virq = drv->agic_irqs[MBOX_RECV_VIRQ]; - drv->hwmbox_recv_virq = recv_virq; - - send_virq = drv->agic_irqs[MBOX_SEND_VIRQ]; - drv->hwmbox_send_virq = send_virq; - - ret = request_irq(recv_virq, hwmbox_recv_full_int_handler, - IRQF_TRIGGER_RISING, "hwmbox0_recv_full", pdev); - if (ret) - goto req_recv_virq; - - ret = request_irq(send_virq, hwmbox_send_empty_int_handler, - IRQF_TRIGGER_RISING, - "hwmbox1_send_empty", pdev); - if (ret) - goto req_send_virq; - hwmboxq_init(&drv->hwmbox_send_queue); return ret; - - req_send_virq: - free_irq(recv_virq, pdev); - - req_recv_virq: - irq_dispose_mapping(send_virq); - irq_dispose_mapping(recv_virq); - nvadsp_drv_data = NULL; - nvadsp_pdev = NULL; - return ret; } diff --git a/drivers/platform/tegra/nvadsp/hwmailbox.h b/drivers/platform/tegra/nvadsp/hwmailbox.h index 2071a83e..b0792961 100644 --- a/drivers/platform/tegra/nvadsp/hwmailbox.h +++ b/drivers/platform/tegra/nvadsp/hwmailbox.h @@ -110,4 +110,7 @@ int nvadsp_hwmbox_init(struct platform_device *); status_t nvadsp_hwmbox_send_data(uint16_t, uint32_t, uint32_t); void dump_mailbox_regs(void); +int nvadsp_setup_hwmbox_interrupts(struct platform_device *pdev); +void nvadsp_free_hwmbox_interrupts(struct platform_device *pdev); + #endif /* __HWMAILBOX_H */ diff --git a/drivers/platform/tegra/nvadsp/os.c b/drivers/platform/tegra/nvadsp/os.c index 7fbf7b15..8093878d 100644 --- a/drivers/platform/tegra/nvadsp/os.c +++ b/drivers/platform/tegra/nvadsp/os.c @@ -133,6 +133,8 @@ static struct nvadsp_mbox adsp_com_mbox; static DECLARE_COMPLETION(entered_wfi); static void __nvadsp_os_stop(bool); +static irqreturn_t adsp_wdt_handler(int irq, void *arg); +static irqreturn_t adsp_wfi_handler(int irq, void *arg); #ifdef CONFIG_DEBUG_FS static int adsp_logger_open(struct inode *inode, struct file *file) @@ -1166,6 +1168,83 @@ void dump_adsp_sys(void) } EXPORT_SYMBOL(dump_adsp_sys); +static void nvadsp_free_os_interrupts(struct nvadsp_os_data *priv) +{ + struct nvadsp_drv_data *drv_data = platform_get_drvdata(priv->pdev); + int wdt_virq = drv_data->agic_irqs[WDT_VIRQ]; + int wfi_virq = drv_data->agic_irqs[WFI_VIRQ]; + struct device *dev = &priv->pdev->dev; + + devm_free_irq(dev, wdt_virq, priv); + devm_free_irq(dev, wfi_virq, priv); +} + +static int nvadsp_setup_os_interrupts(struct nvadsp_os_data *priv) +{ + struct nvadsp_drv_data *drv_data = platform_get_drvdata(priv->pdev); + int wdt_virq = drv_data->agic_irqs[WDT_VIRQ]; + int wfi_virq = drv_data->agic_irqs[WFI_VIRQ]; + struct device *dev = &priv->pdev->dev; + int ret; + + ret = devm_request_irq(dev, wdt_virq, adsp_wdt_handler, + IRQF_TRIGGER_RISING, "adsp watchdog", priv); + if (ret) { + dev_err(dev, "failed to get adsp watchdog interrupt\n"); + goto end; + } + + ret = devm_request_irq(dev, wfi_virq, adsp_wfi_handler, + IRQF_TRIGGER_RISING, "adsp wfi", priv); + if (ret) { + dev_err(dev, "cannot request for wfi interrupt\n"); + goto free_interrupts; + } + + writel(DISABLE_MBOX2_FULL_INT, + priv->hwmailbox_base + drv_data->chip_data->hwmb.hwmbox2_reg); + + end: + + return ret; + + free_interrupts: + nvadsp_free_os_interrupts(priv); + return ret; +} + +static void free_interrupts(struct nvadsp_os_data *priv) +{ + nvadsp_free_os_interrupts(priv); + nvadsp_free_hwmbox_interrupts(priv->pdev); + nvadsp_free_amc_interrupts(priv->pdev); +} + +static int setup_interrupts(struct nvadsp_os_data *priv) +{ + int ret; + + ret = nvadsp_setup_os_interrupts(priv); + if (ret) + goto err; + + ret = nvadsp_setup_hwmbox_interrupts(priv->pdev); + if (ret) + goto free_os_interrupts; + ret = nvadsp_setup_amc_interrupts(priv->pdev); + if (ret) + goto free_hwmbox_interrupts; + + return ret; + + free_hwmbox_interrupts: + nvadsp_free_hwmbox_interrupts(priv->pdev); + free_os_interrupts: + nvadsp_free_os_interrupts(priv); + err: + return ret; +} + int nvadsp_os_start(void) { struct nvadsp_drv_data *drv_data; @@ -1198,12 +1277,17 @@ int nvadsp_os_start(void) if (ret < 0) goto unlock; #endif + ret = setup_interrupts(&priv); + if (ret < 0) + goto unlock; + ret = __nvadsp_os_start(); if (ret) { priv.os_running = drv_data->adsp_os_running = false; /* if start fails call pm suspend of adsp driver */ dev_err(dev, "adsp failed to boot with ret = %d\n", ret); dump_adsp_sys(); + free_interrupts(&priv); #ifdef CONFIG_PM pm_runtime_put_sync(&priv.pdev->dev); #endif @@ -1372,6 +1456,7 @@ void nvadsp_os_stop(void) priv.os_running = drv_data->adsp_os_running = false; + free_interrupts(&priv); #ifdef CONFIG_PM if (pm_runtime_put_sync(dev) < 0) dev_err(dev, "failed in pm_runtime_put_sync\n"); @@ -1410,6 +1495,8 @@ int nvadsp_os_suspend(void) if (!ret) { #ifdef CONFIG_PM struct device *dev = &priv.pdev->dev; + + free_interrupts(&priv); ret = pm_runtime_put_sync(&priv.pdev->dev); if (ret < 0) dev_err(dev, "failed in pm_runtime_put_sync\n"); @@ -1465,7 +1552,7 @@ static void nvadsp_os_restart(struct work_struct *work) dev_crit(dev, "Unable to restart ADSP OS\n"); } -static irqreturn_t adsp_wfi_handler(int irq, void *arg) +static irqreturn_t adsp_wfi_handler(int irq, void *arg) { struct nvadsp_os_data *data = arg; struct device *dev = &data->pdev->dev; @@ -1564,8 +1651,6 @@ static ssize_t tegrafw_read_adsp(struct device *dev, int __init nvadsp_os_probe(struct platform_device *pdev) { struct nvadsp_drv_data *drv_data = platform_get_drvdata(pdev); - int wdt_virq = drv_data->agic_irqs[WDT_VIRQ]; - int wfi_virq = drv_data->agic_irqs[WFI_VIRQ]; uint16_t com_mid = ADSP_COM_MBOX_ID; struct device *dev = &pdev->dev; int ret = 0; @@ -1579,23 +1664,6 @@ int __init nvadsp_os_probe(struct platform_device *pdev) priv.app_alloc_addr = drv_data->adsp_mem[ADSP_APP_ADDR]; priv.app_size = drv_data->adsp_mem[ADSP_APP_SIZE]; - ret = devm_request_irq(dev, wdt_virq, adsp_wdt_handler, - IRQF_TRIGGER_RISING, "adsp watchdog", &priv); - if (ret) { - dev_err(dev, "failed to get adsp watchdog interrupt\n"); - goto end; - } - - ret = devm_request_irq(dev, wfi_virq, adsp_wfi_handler, - IRQF_TRIGGER_RISING, "adsp wfi", &priv); - if (ret) { - dev_err(dev, "cannot request for wfi interrupt\n"); - goto end; - } - - writel(DISABLE_MBOX2_FULL_INT, - priv.hwmailbox_base + drv_data->chip_data->hwmb.hwmbox2_reg); - if (of_device_is_compatible(dev->of_node, "nvidia,tegra210-adsp")) { drv_data->assert_adsp = __assert_adsp; drv_data->deassert_adsp = __deassert_adsp; From 808af08cb90b493582b8a65cb3c18327635204ea Mon Sep 17 00:00:00 2001 From: Nitin Kumbhar Date: Fri, 30 Sep 2016 14:45:55 +0530 Subject: [PATCH 011/138] nvadsp: dump adsp state from hwmbox reg The state of ADSP is logged by writing to a mailbox register. Adding support to kernel driver to dump ADSP state when crash happens. Jira EMA-404 Bug 1901511 Bug 1893324 Bug 200239577 Change-Id: Ia2beb84a7d5bc871f7062bd6a661333af0b0e24d Signed-off-by: Hariharan Sivaraman Reviewed-on: http://git-master/r/1229864 (cherry picked from commit 0027e7a51f6134b4a7b332c17f7c7c26ac727ebc) Signed-off-by: Nitin Kumbhar Reviewed-on: https://git-master.nvidia.com/r/1537327 Reviewed-by: svccoveritychecker GVS: Gerrit_Virtual_Submit --- drivers/platform/tegra/nvadsp/dev.c | 2 + drivers/platform/tegra/nvadsp/dev.h | 1 + drivers/platform/tegra/nvadsp/log_state.h | 65 ++++++++++++ drivers/platform/tegra/nvadsp/os.c | 122 ++++++++++++++++++++++ 4 files changed, 190 insertions(+) create mode 100644 drivers/platform/tegra/nvadsp/log_state.h diff --git a/drivers/platform/tegra/nvadsp/dev.c b/drivers/platform/tegra/nvadsp/dev.c index 0738f56c..5203538d 100644 --- a/drivers/platform/tegra/nvadsp/dev.c +++ b/drivers/platform/tegra/nvadsp/dev.c @@ -398,6 +398,7 @@ static struct nvadsp_chipdata tegra210_adsp_chipdata = { .hwmbox2_reg = 0x60, .hwmbox3_reg = 0x64, }, + .adsp_state_hwmbox = -1, .reset_init = nvadsp_reset_t21x_init, .os_init = nvadsp_os_t21x_init, #ifdef CONFIG_PM @@ -420,6 +421,7 @@ static struct nvadsp_chipdata tegrat18x_adsp_chipdata = { .hwmbox6_reg = 0X30000, .hwmbox7_reg = 0X38000, }, + .adsp_state_hwmbox = 0x30000, .reset_init = nvadsp_reset_t18x_init, .os_init = nvadsp_os_t18x_init, #ifdef CONFIG_PM diff --git a/drivers/platform/tegra/nvadsp/dev.h b/drivers/platform/tegra/nvadsp/dev.h index 653c93d9..75cb5ffe 100644 --- a/drivers/platform/tegra/nvadsp/dev.h +++ b/drivers/platform/tegra/nvadsp/dev.h @@ -130,6 +130,7 @@ typedef int (*pm_init) (struct platform_device *pdev); struct nvadsp_chipdata { struct nvadsp_hwmb hwmb; + u32 adsp_state_hwmbox; reset_init reset_init; os_init os_init; #ifdef CONFIG_PM diff --git a/drivers/platform/tegra/nvadsp/log_state.h b/drivers/platform/tegra/nvadsp/log_state.h new file mode 100644 index 00000000..e80142a8 --- /dev/null +++ b/drivers/platform/tegra/nvadsp/log_state.h @@ -0,0 +1,65 @@ +/* + * Copyright (C) 2017, 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. + * + */ + +#ifndef __LOG_STATE_H +#define __LOG_STATE_H + +#ifdef __ASSEMBLER__ +#define ENUM_START +#define ENUM_VALUE(key, value) .equ key, value +#define ENUM_END(typename) +#else +#define ENUM_START typedef enum { +#define ENUM_VALUE(key, value) key = value, +#define ENUM_END(typename) } typename; +#endif + +#define STATE_LOG_MASK 0x7FFFFFFF + +ENUM_START + +ENUM_VALUE(ADSP_LOADER_MAIN_ENTRY, 0x1) +ENUM_VALUE(ADSP_LOADER_MAIN_CACHE_DISABLE_COMPLETE, 0x2) +ENUM_VALUE(ADSP_LOADER_MAIN_CONFIGURE_MMU_COMPLETE, 0x3) +ENUM_VALUE(ADSP_LOADER_MAIN_CACHE_ENABLE_COMPLETE, 0x4) +ENUM_VALUE(ADSP_LOADER_MAIN_FPU_ENABLE_COMPLETE, 0x5) +ENUM_VALUE(ADSP_LOADER_MAIN_DECOMPRESSION_COMPLETE, 0x6) +ENUM_VALUE(ADSP_LOADER_MAIN_EXIT, 0x7) + +ENUM_VALUE(ADSP_START_ENTRY_AT_RESET, 0x101) +ENUM_VALUE(ADSP_START_CPU_EARLY_INIT, 0x102) +ENUM_VALUE(ADSP_START_FIRST_BOOT, 0x103) +ENUM_VALUE(ADSP_START_LK_MAIN_ENTRY, 0x104) + +ENUM_VALUE(ADSP_LK_MAIN_ENTRY, 0x201) +ENUM_VALUE(ADSP_LK_MAIN_EARLY_THREAD_INIT_COMPLETE, 0x202) +ENUM_VALUE(ADSP_LK_MAIN_EARLY_ARCH_INIT_COMPLETE, 0x203) +ENUM_VALUE(ADSP_LK_MAIN_EARLY_PLATFORM_INIT_COMPLETE, 0x204) +ENUM_VALUE(ADSP_LK_MAIN_EARLY_TARGET_INIT_COMPLETE, 0x205) +ENUM_VALUE(ADSP_LK_MAIN_CONSTRUCTOR_INIT_COMPLETE, 0x206) +ENUM_VALUE(ADSP_LK_MAIN_HEAP_INIT_COMPLETE, 0x207) +ENUM_VALUE(ADSP_LK_MAIN_KERNEL_INIT_COMPLETE, 0x208) +ENUM_VALUE(ADSP_LK_MAIN_CPU_RESUME_ENTRY, 0x209) + +ENUM_VALUE(ADSP_BOOTSTRAP2_ARCH_INIT_COMPLETE, 0x301) +ENUM_VALUE(ADSP_BOOTSTRAP2_PLATFORM_INIT_COMPLETE, 0x302) +ENUM_VALUE(ADSP_BOOTSTRAP2_TARGET_INIT_COMPLETE, 0x303) +ENUM_VALUE(ADSP_BOOTSTRAP2_APP_MODULE_INIT_COMPLETE, 0x304) +ENUM_VALUE(ADSP_BOOTSTRAP2_APP_INIT_COMPLETE, 0x305) +ENUM_VALUE(ADSP_BOOTSTRAP2_STATIC_APP_INIT_COMPLETE, 0x306) +ENUM_VALUE(ADSP_BOOTSTRAP2_OS_LOAD_COMPLETE, 0x307) + +ENUM_END(adsp_state) + +#endif diff --git a/drivers/platform/tegra/nvadsp/os.c b/drivers/platform/tegra/nvadsp/os.c index 8093878d..04536fea 100644 --- a/drivers/platform/tegra/nvadsp/os.c +++ b/drivers/platform/tegra/nvadsp/os.c @@ -47,6 +47,7 @@ #include "dram_app_mem_manager.h" #include "adsp_console_dbfs.h" #include "hwmailbox.h" +#include "log_state.h" #define NVADSP_ELF "adsp.elf" #define NVADSP_FIRMWARE NVADSP_ELF @@ -1155,6 +1156,126 @@ static void print_agic_irq_states(void) } } +static void get_adsp_state(void) +{ + uint32_t val; + struct nvadsp_drv_data *drv_data; + struct device *dev; + char *msg; + + if (!priv.pdev) { + pr_err("ADSP Driver is not initialized\n"); + return; + } + + drv_data = platform_get_drvdata(priv.pdev); + dev = &priv.pdev->dev; + + if (drv_data->chip_data->adsp_state_hwmbox == -1) { + dev_info(dev, "%s: No state hwmbox available\n", __func__); + return; + } + + val = hwmbox_readl(drv_data->chip_data->adsp_state_hwmbox); + dev_info(dev, "%s: adsp state hwmbox value: 0x%X\n", __func__, val); + + switch (val) { + + case ADSP_LOADER_MAIN_ENTRY: + msg = "loader_main: entry to loader_main"; + break; + case ADSP_LOADER_MAIN_CACHE_DISABLE_COMPLETE: + msg = "loader_main: Cache has been disabled"; + break; + case ADSP_LOADER_MAIN_CONFIGURE_MMU_COMPLETE: + msg = "loader_main: MMU configuration is complete"; + break; + case ADSP_LOADER_MAIN_CACHE_ENABLE_COMPLETE: + msg = "loader_main: Cache has been enabled"; + break; + case ADSP_LOADER_MAIN_FPU_ENABLE_COMPLETE: + msg = "loader_main: FPU has been enabled"; + break; + case ADSP_LOADER_MAIN_DECOMPRESSION_COMPLETE: + msg = "loader_main: ADSP FW decompression is complete"; + break; + case ADSP_LOADER_MAIN_EXIT: + msg = "loader_main: exiting loader_main function"; + break; + + case ADSP_START_ENTRY_AT_RESET: + msg = "start: ADSP is at reset"; + break; + case ADSP_START_CPU_EARLY_INIT: + msg = "start: ADSP to do cpu_early_init"; + break; + case ADSP_START_FIRST_BOOT: + msg = "start: ADSP is booting for first time," + "initializing DATA and clearing BSS"; + break; + case ADSP_START_LK_MAIN_ENTRY: + msg = "start: ADSP about to enter lk_main"; + break; + + case ADSP_LK_MAIN_ENTRY: + msg = "lk_main: entry to lk_main"; + break; + case ADSP_LK_MAIN_EARLY_THREAD_INIT_COMPLETE: + msg = "lk_main: early_thread_init has been completed"; + break; + case ADSP_LK_MAIN_EARLY_ARCH_INIT_COMPLETE: + msg = "lk_main: early_arch_init has been completed"; + break; + case ADSP_LK_MAIN_EARLY_PLATFORM_INIT_COMPLETE: + msg = "lk_main: early_platform_init has been completed"; + break; + case ADSP_LK_MAIN_EARLY_TARGET_INIT_COMPLETE: + msg = "lk_main: early_target_init has been completed"; + break; + case ADSP_LK_MAIN_CONSTRUCTOR_INIT_COMPLETE: + msg = "lk_main: constructors has been called"; + break; + case ADSP_LK_MAIN_HEAP_INIT_COMPLETE: + msg = "lk_main: heap has been initialized"; + break; + case ADSP_LK_MAIN_KERNEL_INIT_COMPLETE: + msg = "lk_main: ADSP kernel has been initialized"; + break; + case ADSP_LK_MAIN_CPU_RESUME_ENTRY: + msg = "lk_main: ADSP is about to resume from suspend"; + break; + + case ADSP_BOOTSTRAP2_ARCH_INIT_COMPLETE: + msg = "bootstrap2: ADSP arch_init is complete"; + break; + case ADSP_BOOTSTRAP2_PLATFORM_INIT_COMPLETE: + msg = "bootstrap2: platform has been initialized"; + break; + case ADSP_BOOTSTRAP2_TARGET_INIT_COMPLETE: + msg = "bootstrap2: target has been initialized"; + break; + case ADSP_BOOTSTRAP2_APP_MODULE_INIT_COMPLETE: + msg = "bootstrap2: APP modules initialized"; + break; + case ADSP_BOOTSTRAP2_APP_INIT_COMPLETE: + msg = "bootstrap2: APP init is complete"; + break; + case ADSP_BOOTSTRAP2_STATIC_APP_INIT_COMPLETE: + msg = "bootstrap2: Static apps has been initialized"; + break; + case ADSP_BOOTSTRAP2_OS_LOAD_COMPLETE: + msg = "bootstrap2: ADSP OS successfully loaded"; + break; + + default: + msg = "Unrecognized ADSP state!!"; + break; + } + + dev_info(dev, "%s: %s\n", __func__, msg); +} + + void dump_adsp_sys(void) { if (!priv.pdev) { @@ -1164,6 +1285,7 @@ void dump_adsp_sys(void) dump_adsp_logs(); dump_mailbox_regs(); + get_adsp_state(); print_agic_irq_states(); } EXPORT_SYMBOL(dump_adsp_sys); From 7e75728df70816e65d41c00b81b270d0ffe2b69d Mon Sep 17 00:00:00 2001 From: Nitin Kumbhar Date: Wed, 19 Apr 2017 16:52:43 +0530 Subject: [PATCH 012/138] platform: tegra: nvadsp: add adma reg dump Add ADMA regs as part of dump_adsp_sys() which is called on irrecoverable errors on ADSP. Bug 200295526 Change-Id: Iacb315d6b188d1b98085e59a86c74212246308da Signed-off-by: Nitin Kumbhar Reviewed-on: http://git-master/r/1466422 (cherry picked from commit 4df02685ef33a9b3058d63bc1c09ceb4a726aa70) Reviewed-on: http://git-master/r/1465573 (cherry picked from commit 83fa5779125c024dd0101215c4d37df8e89a9619) Reviewed-on: https://git-master/r/1513584 Reviewed-on: https://git-master.nvidia.com/r/1537328 Reviewed-by: Automatic_Commit_Validation_User Reviewed-by: svccoveritychecker GVS: Gerrit_Virtual_Submit --- drivers/platform/tegra/nvadsp/os.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/drivers/platform/tegra/nvadsp/os.c b/drivers/platform/tegra/nvadsp/os.c index 04536fea..02a8264f 100644 --- a/drivers/platform/tegra/nvadsp/os.c +++ b/drivers/platform/tegra/nvadsp/os.c @@ -49,6 +49,10 @@ #include "hwmailbox.h" #include "log_state.h" +void tegra_adma_dump_ch_reg(void) +{ +} + #define NVADSP_ELF "adsp.elf" #define NVADSP_FIRMWARE NVADSP_ELF @@ -1286,6 +1290,7 @@ void dump_adsp_sys(void) dump_adsp_logs(); dump_mailbox_regs(); get_adsp_state(); + tegra_adma_dump_ch_reg(); print_agic_irq_states(); } EXPORT_SYMBOL(dump_adsp_sys); From 9df95f35c0edee3045d9cef3136633490741a710 Mon Sep 17 00:00:00 2001 From: Nitin Kumbhar Date: Thu, 4 May 2017 20:50:27 +0530 Subject: [PATCH 013/138] drivers: nvadsp: dump current thread and last irq Dump name (first 4 chars) of current thread and irq number of last interrupt handled from hwmbox reg 4 and 7 respectively. Bug 200295526 Change-Id: I4b53521cbfece2177e3cf6ed80b559b58e1d1c0a Signed-off-by: Nitin Kumbhar Reviewed-on: http://git-master/r/1475487 (cherry picked from commit 64a799ae7d8e8f8783212fb4c28209cf760b38a7) Reviewed-on: http://git-master/r/1478825 (cherry picked from commit cab60f6977fc8070e4206db01e2a2d444860c90c) Reviewed-on: https://git-master/r/1513585 Reviewed-on: https://git-master.nvidia.com/r/1537329 Reviewed-by: Automatic_Commit_Validation_User Reviewed-by: svccoveritychecker GVS: Gerrit_Virtual_Submit --- drivers/platform/tegra/nvadsp/dev.c | 4 ++++ drivers/platform/tegra/nvadsp/dev.h | 4 +++- drivers/platform/tegra/nvadsp/hwmailbox.c | 1 - drivers/platform/tegra/nvadsp/os.c | 22 +++++++++++++++++++++- 4 files changed, 28 insertions(+), 3 deletions(-) diff --git a/drivers/platform/tegra/nvadsp/dev.c b/drivers/platform/tegra/nvadsp/dev.c index 5203538d..45b55b4f 100644 --- a/drivers/platform/tegra/nvadsp/dev.c +++ b/drivers/platform/tegra/nvadsp/dev.c @@ -399,6 +399,8 @@ static struct nvadsp_chipdata tegra210_adsp_chipdata = { .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 @@ -422,6 +424,8 @@ static struct nvadsp_chipdata tegrat18x_adsp_chipdata = { .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 diff --git a/drivers/platform/tegra/nvadsp/dev.h b/drivers/platform/tegra/nvadsp/dev.h index 75cb5ffe..0198f87d 100644 --- a/drivers/platform/tegra/nvadsp/dev.h +++ b/drivers/platform/tegra/nvadsp/dev.h @@ -130,7 +130,9 @@ typedef int (*pm_init) (struct platform_device *pdev); struct nvadsp_chipdata { struct nvadsp_hwmb hwmb; - u32 adsp_state_hwmbox; + u32 adsp_state_hwmbox; + u32 adsp_thread_hwmbox; + u32 adsp_irq_hwmbox; reset_init reset_init; os_init os_init; #ifdef CONFIG_PM diff --git a/drivers/platform/tegra/nvadsp/hwmailbox.c b/drivers/platform/tegra/nvadsp/hwmailbox.c index 2b3b1240..9ae54b8b 100644 --- a/drivers/platform/tegra/nvadsp/hwmailbox.c +++ b/drivers/platform/tegra/nvadsp/hwmailbox.c @@ -77,7 +77,6 @@ void dump_mailbox_regs(void) dev_info(&nvadsp_pdev->dev, "dumping hwmailbox registers ...\n"); PRINT_HWMBOX(recv_hwmbox()); PRINT_HWMBOX(send_hwmbox()); - dev_info(&nvadsp_pdev->dev, "end of dump ....\n"); } static void hwmboxq_init(struct hwmbox_queue *queue) diff --git a/drivers/platform/tegra/nvadsp/os.c b/drivers/platform/tegra/nvadsp/os.c index 02a8264f..7bce10a7 100644 --- a/drivers/platform/tegra/nvadsp/os.c +++ b/drivers/platform/tegra/nvadsp/os.c @@ -1160,11 +1160,25 @@ static void print_agic_irq_states(void) } } +static void dump_thread_name(struct platform_device *pdev, u32 val) +{ + dev_info(&pdev->dev, "%s: adsp current thread: %c%c%c%c\n", + __func__, + (val >> 24) & 0xFF, (val >> 16) & 0xFF, + (val >> 8) & 0xFF, (val >> 0) & 0xFF); +} + +static void dump_irq_num(struct platform_device *pdev, u32 val) +{ + dev_info(&pdev->dev, "%s: adsp current/last irq : %d\n", + __func__, val); +} + static void get_adsp_state(void) { - uint32_t val; struct nvadsp_drv_data *drv_data; struct device *dev; + uint32_t val; char *msg; if (!priv.pdev) { @@ -1277,6 +1291,12 @@ static void get_adsp_state(void) } dev_info(dev, "%s: %s\n", __func__, msg); + + val = hwmbox_readl(drv_data->chip_data->adsp_thread_hwmbox); + dump_thread_name(priv.pdev, val); + + val = hwmbox_readl(drv_data->chip_data->adsp_irq_hwmbox); + dump_irq_num(priv.pdev, val); } From c196dc0fff35ddb321f36334d11c679e0da5f3e3 Mon Sep 17 00:00:00 2001 From: Nitin Kumbhar Date: Thu, 4 May 2017 17:05:11 +0530 Subject: [PATCH 014/138] drivers: nvadsp: dump msgs for logs from suspend Add messages for states logged during ADSP suspend and resume path. Bug 200295526 Change-Id: I2b039744e6abfd8d30b6b78ec4f9c4acda7a13aa Signed-off-by: Nitin Kumbhar Reviewed-on: http://git-master/r/1475428 (cherry picked from commit e5d9528576285ceffd89f7a8c962ad57140cd3c9) Reviewed-on: http://git-master/r/1478824 (cherry picked from commit 332a011b8a97cc1ce788500b1dcbce6e08a7fe83) Reviewed-on: https://git-master/r/1513586 Reviewed-on: https://git-master.nvidia.com/r/1537330 Reviewed-by: Automatic_Commit_Validation_User Reviewed-by: svccoveritychecker GVS: Gerrit_Virtual_Submit --- drivers/platform/tegra/nvadsp/log_state.h | 15 ++++++++ drivers/platform/tegra/nvadsp/os.c | 43 ++++++++++++++++++++++- 2 files changed, 57 insertions(+), 1 deletion(-) diff --git a/drivers/platform/tegra/nvadsp/log_state.h b/drivers/platform/tegra/nvadsp/log_state.h index e80142a8..ab8563f6 100644 --- a/drivers/platform/tegra/nvadsp/log_state.h +++ b/drivers/platform/tegra/nvadsp/log_state.h @@ -60,6 +60,21 @@ ENUM_VALUE(ADSP_BOOTSTRAP2_APP_INIT_COMPLETE, 0x305) ENUM_VALUE(ADSP_BOOTSTRAP2_STATIC_APP_INIT_COMPLETE, 0x306) ENUM_VALUE(ADSP_BOOTSTRAP2_OS_LOAD_COMPLETE, 0x307) +ENUM_VALUE(ADSP_SUSPEND_BEGINS, 0x320) +ENUM_VALUE(ADSP_SUSPEND_MBX_SEND_COMPLETE, 0x321) +ENUM_VALUE(ADSP_SUSPEND_DISABLED_TIMERS, 0x322) +ENUM_VALUE(ADSP_SUSPEND_DISABLED_INTS, 0x323) +ENUM_VALUE(ADSP_SUSPEND_ARAM_SAVED, 0x324) +ENUM_VALUE(ADSP_SUSPEND_AMC_SAVED, 0x325) +ENUM_VALUE(ADSP_SUSPEND_AMISC_SAVED, 0x326) +ENUM_VALUE(ADSP_SUSPEND_L1_CACHE_DISABLED, 0x327) +ENUM_VALUE(ADSP_SUSPEND_L2_CACHE_DISABLED, 0x328) +ENUM_VALUE(ADSP_RESUME_ADSP, 0x330) +ENUM_VALUE(ADSP_RESUME_AMISC_RESTORED, 0x331) +ENUM_VALUE(ADSP_RESUME_AMC_RESTORED, 0x332) +ENUM_VALUE(ADSP_RESUME_ARAM_RESTORED, 0x333) +ENUM_VALUE(ADSP_RESUME_COMPLETE, 0x334) + ENUM_END(adsp_state) #endif diff --git a/drivers/platform/tegra/nvadsp/os.c b/drivers/platform/tegra/nvadsp/os.c index 7bce10a7..6c35176f 100644 --- a/drivers/platform/tegra/nvadsp/os.c +++ b/drivers/platform/tegra/nvadsp/os.c @@ -1284,7 +1284,48 @@ static void get_adsp_state(void) case ADSP_BOOTSTRAP2_OS_LOAD_COMPLETE: msg = "bootstrap2: ADSP OS successfully loaded"; break; - + case ADSP_SUSPEND_BEGINS: + msg = "suspend: begins"; + break; + case ADSP_SUSPEND_MBX_SEND_COMPLETE: + msg = "suspend: mbox send complete"; + break; + case ADSP_SUSPEND_DISABLED_TIMERS: + msg = "suspend: timers disabled"; + break; + case ADSP_SUSPEND_DISABLED_INTS: + msg = "suspend: interrupts disabled"; + break; + case ADSP_SUSPEND_ARAM_SAVED: + msg = "suspend: aram saved"; + break; + case ADSP_SUSPEND_AMC_SAVED: + msg = "suspend: amc saved"; + break; + case ADSP_SUSPEND_AMISC_SAVED: + msg = "suspend: amisc saved"; + break; + case ADSP_SUSPEND_L1_CACHE_DISABLED: + msg = "suspend: l1 cache disabled"; + break; + case ADSP_SUSPEND_L2_CACHE_DISABLED: + msg = "suspend: l2 cache disabled"; + break; + case ADSP_RESUME_ADSP: + msg = "resume: beings"; + break; + case ADSP_RESUME_AMISC_RESTORED: + msg = "resume: amisc restored"; + break; + case ADSP_RESUME_AMC_RESTORED: + msg = "resume: amc restored"; + break; + case ADSP_RESUME_ARAM_RESTORED: + msg = "resume: aram restored"; + break; + case ADSP_RESUME_COMPLETE: + msg = "resume: complete"; + break; default: msg = "Unrecognized ADSP state!!"; break; From 412366151b9e4e80c7d8b6b098267af02c3f2375 Mon Sep 17 00:00:00 2001 From: Nitin Kumbhar Date: Tue, 6 Jun 2017 16:17:33 +0530 Subject: [PATCH 015/138] platform: nvadsp: dump mailbox queue on error Dump mailbox queue, if it's not empty, when mailbox is getting closed. Also, change errornos in mailbox APIs such that those are unique in that function. Bug 200295526 Change-Id: I69e83146c049bb51d7cac5e954183c6c7cc97956 Signed-off-by: Nitin Kumbhar Reviewed-on: http://git-master/r/1496890 (cherry picked from commit dec73d1e0ab5c6ccddf043bd4ae7ce65d1d478e2) Reviewed-on: https://git-master/r/1513587 Reviewed-on: https://git-master.nvidia.com/r/1537331 Reviewed-by: Automatic_Commit_Validation_User Reviewed-by: svccoveritychecker GVS: Gerrit_Virtual_Submit --- drivers/platform/tegra/nvadsp/mailbox.c | 33 ++++++++++++++++++++++--- 1 file changed, 29 insertions(+), 4 deletions(-) diff --git a/drivers/platform/tegra/nvadsp/mailbox.c b/drivers/platform/tegra/nvadsp/mailbox.c index 17f443a2..8c2d2c9a 100644 --- a/drivers/platform/tegra/nvadsp/mailbox.c +++ b/drivers/platform/tegra/nvadsp/mailbox.c @@ -1,7 +1,7 @@ /* * ADSP mailbox manager * - * Copyright (c) 2014-2016, NVIDIA CORPORATION. All rights reserved. + * Copyright (c) 2014-2017, NVIDIA CORPORATION. All rights reserved. * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU General Public License, @@ -105,6 +105,30 @@ static status_t mboxq_dequeue(struct nvadsp_mbox_queue *queue, return ret; } +static void mboxq_dump(struct nvadsp_mbox_queue *queue) +{ + unsigned long flags; + uint16_t head, count; + uint32_t data; + + spin_lock_irqsave(&queue->lock, flags); + + count = queue->count; + pr_info("nvadsp: queue %p count:%d\n", queue, count); + + pr_info("nvadsp: queue data: "); + head = queue->head; + while (count) { + data = queue->array[head]; + head = (head + 1) & NVADSP_MBOX_QUEUE_SIZE_MASK; + count--; + pr_info("0x%x ", data); + } + pr_info(" dumped\n"); + + spin_unlock_irqrestore(&queue->lock, flags); +} + static uint16_t nvadsp_mbox_alloc_mboxid(void) { unsigned long start = NVADSP_MAILBOX_START; @@ -157,13 +181,13 @@ status_t nvadsp_mbox_open(struct nvadsp_mbox *mbox, uint16_t *mid, if (*mid >= NVADSP_MAILBOX_MAX) { pr_debug("%s: Invalid mailbox %d.\n", __func__, *mid); - ret = -EINVAL; + ret = -ERANGE; goto out; } if (nvadsp_drv_data->mboxes[*mid]) { pr_debug("%s: mailbox %d already opened.\n", __func__, *mid); - ret = -EINVAL; + ret = -EADDRINUSE; goto out; } mbox->id = *mid; @@ -288,7 +312,8 @@ status_t nvadsp_mbox_close(struct nvadsp_mbox *mbox) } if (!is_mboxq_empty(&mbox->recv_queue)) { - ret = -EINVAL; + ret = -ENOTEMPTY; + mboxq_dump(&mbox->recv_queue); goto out; } From da4867a93cce79eedd00891fc8d329a591a9f32a Mon Sep 17 00:00:00 2001 From: Ajay Nandakumar Date: Wed, 17 May 2017 12:30:54 +0530 Subject: [PATCH 016/138] drivers: nvadsp: Add msgs for logs from wfi Add messages for states logged during wfi entry and exit. This messages would be useful when ADSP fails to receive wfi interrupts. Bug 1893324 Change-Id: I3f7ade24b3d0b7bf2e5fe35d1d380ed51de38b70 Signed-off-by: Ajay Nandakumar Reviewed-on: http://git-master/r/1483708 (cherry picked from commit ea6a7d1566e57c23099ed09c06f64967d67d26da) Signed-off-by: Nitin Kumbhar Reviewed-on: https://git-master/r/1513588 Reviewed-on: https://git-master.nvidia.com/r/1537332 Reviewed-by: Automatic_Commit_Validation_User Reviewed-by: svccoveritychecker GVS: Gerrit_Virtual_Submit --- drivers/platform/tegra/nvadsp/log_state.h | 2 ++ drivers/platform/tegra/nvadsp/os.c | 6 ++++++ 2 files changed, 8 insertions(+) diff --git a/drivers/platform/tegra/nvadsp/log_state.h b/drivers/platform/tegra/nvadsp/log_state.h index ab8563f6..abb4180a 100644 --- a/drivers/platform/tegra/nvadsp/log_state.h +++ b/drivers/platform/tegra/nvadsp/log_state.h @@ -74,6 +74,8 @@ ENUM_VALUE(ADSP_RESUME_AMISC_RESTORED, 0x331) ENUM_VALUE(ADSP_RESUME_AMC_RESTORED, 0x332) ENUM_VALUE(ADSP_RESUME_ARAM_RESTORED, 0x333) ENUM_VALUE(ADSP_RESUME_COMPLETE, 0x334) +ENUM_VALUE(ADSP_WFI_ENTER, 0x335) +ENUM_VALUE(ADSP_WFI_EXIT, 0x336) ENUM_END(adsp_state) diff --git a/drivers/platform/tegra/nvadsp/os.c b/drivers/platform/tegra/nvadsp/os.c index 6c35176f..9dd072ef 100644 --- a/drivers/platform/tegra/nvadsp/os.c +++ b/drivers/platform/tegra/nvadsp/os.c @@ -1326,6 +1326,12 @@ static void get_adsp_state(void) case ADSP_RESUME_COMPLETE: msg = "resume: complete"; break; + case ADSP_WFI_ENTER: + msg = "WFI: Entering WFI"; + break; + case ADSP_WFI_EXIT: + msg = "WFI: Exiting WFI, Failed to Enter"; + break; default: msg = "Unrecognized ADSP state!!"; break; From 5711c0800c8c4590aa95c1bd8833fc21469b8bc0 Mon Sep 17 00:00:00 2001 From: Nitin Kumbhar Date: Tue, 6 Jun 2017 17:52:05 +0530 Subject: [PATCH 017/138] platform: nvadsp: add dfs comm logs Add messages for logs added on adsp for dfs mailbox communication. Bug 200295526 Change-Id: I19048f755578359a673d164be3d4f5601cb82634 Signed-off-by: Nitin Kumbhar Reviewed-on: http://git-master/r/1496891 (cherry picked from commit 06fec301a0d744108ac890a87bd187efd956a688) Reviewed-on: https://git-master/r/1513589 Reviewed-on: https://git-master.nvidia.com/r/1537333 Reviewed-by: Automatic_Commit_Validation_User Reviewed-by: svccoveritychecker GVS: Gerrit_Virtual_Submit --- drivers/platform/tegra/nvadsp/log_state.h | 2 ++ drivers/platform/tegra/nvadsp/os.c | 6 ++++++ 2 files changed, 8 insertions(+) diff --git a/drivers/platform/tegra/nvadsp/log_state.h b/drivers/platform/tegra/nvadsp/log_state.h index abb4180a..581c1d92 100644 --- a/drivers/platform/tegra/nvadsp/log_state.h +++ b/drivers/platform/tegra/nvadsp/log_state.h @@ -76,6 +76,8 @@ ENUM_VALUE(ADSP_RESUME_ARAM_RESTORED, 0x333) ENUM_VALUE(ADSP_RESUME_COMPLETE, 0x334) ENUM_VALUE(ADSP_WFI_ENTER, 0x335) ENUM_VALUE(ADSP_WFI_EXIT, 0x336) +ENUM_VALUE(ADSP_DFS_MBOX_RECV, 0x337) +ENUM_VALUE(ADSP_DFS_MBOX_SENT, 0x338) ENUM_END(adsp_state) diff --git a/drivers/platform/tegra/nvadsp/os.c b/drivers/platform/tegra/nvadsp/os.c index 9dd072ef..6678217c 100644 --- a/drivers/platform/tegra/nvadsp/os.c +++ b/drivers/platform/tegra/nvadsp/os.c @@ -1332,6 +1332,12 @@ static void get_adsp_state(void) case ADSP_WFI_EXIT: msg = "WFI: Exiting WFI, Failed to Enter"; break; + case ADSP_DFS_MBOX_RECV: + msg = "DFS: mbox received"; + break; + case ADSP_DFS_MBOX_SENT: + msg = "DFS: mbox sent"; + break; default: msg = "Unrecognized ADSP state!!"; break; From a3d5511deac4d1d790ac5adb7ce2c217919f55a8 Mon Sep 17 00:00:00 2001 From: Nitin Kumbhar Date: Sun, 2 Jul 2017 14:30:44 +0530 Subject: [PATCH 018/138] platform: nvadsp: use smem for adsp freq update Instead of using mailbox to communicate change in adsp cpu freq, update it in adsp os args from ccplex-adsp shared memory. This adsp freq update is needed by EDF scheduler for scheduling EDF threads. The EDF scheduler gets current adsp freq from shared memory instead of scheduler callback. However, for APE1 (i.e. chips = T21x), timer prescalar must still be updated through mailbox each time adsp cpu freq is changed. Bug 200322504 Change-Id: I26f14f5c4f5c2369d67dfcd73dc6b9664df2ca26 Signed-off-by: Nitin Kumbhar Reviewed-on: https://git-master/r/1512069 (cherry picked from commit 76e8a4c19de2fa5f0d91ebefac68aa0b864cb0eb) Reviewed-on: https://git-master/r/1512864 (cherry picked from commit 3100911879f9ea9930a515f6d55257caefcef869) Reviewed-on: https://git-master.nvidia.com/r/1517749 Reviewed-on: https://git-master.nvidia.com/r/1537334 Reviewed-by: Automatic_Commit_Validation_User Reviewed-by: svccoveritychecker GVS: Gerrit_Virtual_Submit --- drivers/platform/tegra/nvadsp/adsp_dfs.c | 134 ++++++++++++++--------- drivers/platform/tegra/nvadsp/os.c | 6 + 2 files changed, 87 insertions(+), 53 deletions(-) diff --git a/drivers/platform/tegra/nvadsp/adsp_dfs.c b/drivers/platform/tegra/nvadsp/adsp_dfs.c index fca2623c..c97f598a 100644 --- a/drivers/platform/tegra/nvadsp/adsp_dfs.c +++ b/drivers/platform/tegra/nvadsp/adsp_dfs.c @@ -254,61 +254,11 @@ static struct adsp_dfs_policy dfs_policy = { }, }; -/* - * update_freq - update adsp freq and ask adsp to change timer as - * change in adsp freq. - * freq_khz - target frequency in KHz - * return - final freq got set. - * - 0, incase of error. - * - * Note - Policy->cur would be updated via rate - * change notifier, when freq is changed in hw - * - */ -static unsigned long update_freq(unsigned long freq_khz) +static int adsp_update_freq_handshake(unsigned long tfreq_hz, int index) { - u32 efreq; - int index; - int ret; - unsigned long tfreq_hz, old_freq_khz; - enum adsp_dfs_reply reply; struct nvadsp_mbox *mbx = &policy->mbox; - struct nvadsp_drv_data *drv = dev_get_drvdata(device); - - tfreq_hz = adsp_get_target_freq(freq_khz * 1000, &index); - if (!tfreq_hz) { - dev_info(device, "unable get the target freq\n"); - return 0; - } - - old_freq_khz = policy->cur; - - if ((tfreq_hz / 1000) == old_freq_khz) { - dev_dbg(device, "old and new target_freq is same\n"); - return 0; - } - - ret = adsp_clk_set_rate(policy, tfreq_hz); - if (ret) { - dev_err(device, "failed to set adsp freq:%luhz err:%d\n", - tfreq_hz, ret); - policy->update_freq_flag = false; - return 0; - } - - efreq = adsp_to_emc_freq(tfreq_hz / 1000); - - if (IS_ENABLED(CONFIG_COMMON_CLK)) { - tegra_bwmgr_set_emc(drv->bwmgr, efreq * 1000, - TEGRA_BWMGR_SET_EMC_FLOOR); - } else { - ret = clk_set_rate(ape_emc_clk, efreq * 1000); - if (ret) { - dev_err(device, "failed to set ape.emc clk:%d\n", ret); - policy->update_freq_flag = false; - goto err_out; - } - } + enum adsp_dfs_reply reply; + int ret; dev_dbg(device, "sending change in freq(hz):%lu\n", tfreq_hz); /* @@ -351,6 +301,84 @@ static unsigned long update_freq(unsigned long freq_khz) __func__, policy->update_freq_flag == true ? "ACK" : "NACK", tfreq_hz); +err_out: + return ret; +} + +/* + * update_freq - update adsp freq and ask adsp to change timer as + * change in adsp freq. + * freq_khz - target frequency in KHz + * return - final freq got set. + * - 0, incase of error. + * + * Note - Policy->cur would be updated via rate + * change notifier, when freq is changed in hw + * + */ +static unsigned long update_freq(unsigned long freq_khz) +{ + struct nvadsp_drv_data *drv = dev_get_drvdata(device); + unsigned long tfreq_hz, old_freq_khz; + u32 efreq; + int index; + int ret; + + tfreq_hz = adsp_get_target_freq(freq_khz * 1000, &index); + if (!tfreq_hz) { + dev_info(device, "unable get the target freq\n"); + return 0; + } + + old_freq_khz = policy->cur; + + if ((tfreq_hz / 1000) == old_freq_khz) { + dev_dbg(device, "old and new target_freq is same\n"); + return 0; + } + + ret = adsp_clk_set_rate(policy, tfreq_hz); + if (ret) { + dev_err(device, "failed to set adsp freq:%luhz err:%d\n", + tfreq_hz, ret); + policy->update_freq_flag = false; + return 0; + } + + efreq = adsp_to_emc_freq(tfreq_hz / 1000); + + if (IS_ENABLED(CONFIG_COMMON_CLK)) { + tegra_bwmgr_set_emc(drv->bwmgr, efreq * 1000, + TEGRA_BWMGR_SET_EMC_FLOOR); + } else { + ret = clk_set_rate(ape_emc_clk, efreq * 1000); + if (ret) { + dev_err(device, "failed to set ape.emc clk:%d\n", ret); + policy->update_freq_flag = false; + goto err_out; + } + } + + /* + * On tegra > t210, as os_args->adsp_freq_hz is used to know adsp cpu + * clk rate and there is no need to set up timer prescalar. So skip + * communicating adsp cpu clk rate update to adspos using mbox + */ + if (!of_device_is_compatible(device->of_node, "nvidia,tegra210-adsp")) + policy->update_freq_flag = true; + else + adsp_update_freq_handshake(tfreq_hz, index); + + /* + * Use os_args->adsp_freq_hz to update adsp cpu clk rate + * for adspos firmware, which uses this shared variable + * to get the clk rate for EDF, etc. + */ + if (policy->update_freq_flag) { + struct nvadsp_shared_mem *sm = drv->shared_adsp_os_data; + + sm->os_args.adsp_freq_hz = tfreq_hz; + } err_out: if (!policy->update_freq_flag) { diff --git a/drivers/platform/tegra/nvadsp/os.c b/drivers/platform/tegra/nvadsp/os.c index 6678217c..ff406fa8 100644 --- a/drivers/platform/tegra/nvadsp/os.c +++ b/drivers/platform/tegra/nvadsp/os.c @@ -859,6 +859,10 @@ static int nvadsp_t210_set_clks_and_prescalar(struct nvadsp_drv_data *drv_data) goto end; drv_data->adsp_freq = adsp_freq / 1000; /* adsp_freq in KHz*/ + drv_data->adsp_freq_hz = adsp_freq; + + /* adspos uses os_args->adsp_freq_hz for EDF */ + os_args->adsp_freq_hz = adsp_freq; end: dev_dbg(dev, "adsp cpu freq %luKHz\n", @@ -897,6 +901,8 @@ static int nvadsp_set_adsp_clks(struct nvadsp_drv_data *drv_data) drv_data->adsp_freq = adsp_freq / 1000; /* adsp_freq in KHz*/ drv_data->adsp_freq_hz = adsp_freq; + + /* adspos uses os_args->adsp_freq_hz for EDF */ os_args->adsp_freq_hz = adsp_freq; end: dev_dbg(dev, "adsp cpu freq %luKHz\n", From 71b56ecaa2701f59dc857fa34946cb1cbeed95d6 Mon Sep 17 00:00:00 2001 From: Ajay Nandakumar Date: Tue, 11 Jul 2017 14:49:41 +0530 Subject: [PATCH 019/138] platform: nvadsp: make WFI wait non-interruptible Making the "WFI wait" from interruptible to non-interruptible. This is done because there is a probalbilty between the thread making a call to nvadsp_os_suspend / nvadsp_os_start getting signaled or having a pending signal before ADSP enters WFI and the CPU getting the WFI interrupt.This results in wait_for_completion_interruptible_timeout() returning -ERESTARTSYS, due to which nvadsp_os_suspend treats it as failure although ADSP has entered WFI and the WFI interrupt has been received on the CCPLEX. Since this is a race it is not seen often. Also, reducing the timeouts for WFI and logger wait to 800 and 500 ms respectively. The timeouts have been reduced because during suspend there are only a few register writes and logger has been reduced from 1 MB to 16 KB. Bug 200317530 Bug 1893324 Change-Id: I40b3894f284763c0e4cc45647e1adf2cd074d701 Signed-off-by: Ajay Nandakumar Reviewed-on: https://git-master.nvidia.com/r/1517838 Signed-off-by: Nitin Kumbhar Reviewed-on: https://git-master.nvidia.com/r/1537335 Reviewed-by: Automatic_Commit_Validation_User Reviewed-by: svccoveritychecker GVS: Gerrit_Virtual_Submit --- drivers/platform/tegra/nvadsp/os.c | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/drivers/platform/tegra/nvadsp/os.c b/drivers/platform/tegra/nvadsp/os.c index ff406fa8..583d5255 100644 --- a/drivers/platform/tegra/nvadsp/os.c +++ b/drivers/platform/tegra/nvadsp/os.c @@ -87,8 +87,8 @@ void tegra_adma_dump_ch_reg(void) #define ENABLE_MBOX2_FULL_INT 0xFFFFFFFF #define LOGGER_TIMEOUT 20 /* in ms */ -#define ADSP_WFE_TIMEOUT 5000 /* in ms */ -#define LOGGER_COMPLETE_TIMEOUT 5000 /* in ms */ +#define ADSP_WFI_TIMEOUT 800 /* in ms */ +#define LOGGER_COMPLETE_TIMEOUT 500 /* in ms */ #define DUMP_BUFF 128 @@ -1547,11 +1547,11 @@ static int __nvadsp_os_suspend(void) } dev_dbg(dev, "Waiting for ADSP OS suspend...\n"); - ret = wait_for_completion_interruptible_timeout(&entered_wfi, - msecs_to_jiffies(ADSP_WFE_TIMEOUT)); + ret = wait_for_completion_timeout(&entered_wfi, + msecs_to_jiffies(ADSP_WFI_TIMEOUT)); if (WARN_ON(ret <= 0)) { - dev_err(dev, "Unable to suspend ADSP OS\n"); - ret = -EINVAL; + dev_err(dev, "Unable to suspend ADSP OS err = %d\n", ret); + ret = (ret < 0) ? ret : -ETIMEDOUT; goto out; } ret = 0; @@ -1595,8 +1595,8 @@ static void __nvadsp_os_stop(bool reload) writel(ENABLE_MBOX2_FULL_INT, priv.hwmailbox_base + drv_data->chip_data->hwmb.hwmbox2_reg); - err = wait_for_completion_interruptible_timeout(&entered_wfi, - msecs_to_jiffies(ADSP_WFE_TIMEOUT)); + err = wait_for_completion_timeout(&entered_wfi, + msecs_to_jiffies(ADSP_WFI_TIMEOUT)); writel(DISABLE_MBOX2_FULL_INT, priv.hwmailbox_base + drv_data->chip_data->hwmb.hwmbox2_reg); @@ -1611,7 +1611,8 @@ static void __nvadsp_os_stop(bool reload) /* Don't reload ADSPOS if ADSP state is not WFI/WFE */ if (WARN_ON(err <= 0)) { - dev_err(dev, "ADSP is unable to enter wfi state\n"); + dev_err(dev, "%s: unable to enter wfi state err = %d\n", + __func__, err); goto end; } @@ -1621,7 +1622,7 @@ static void __nvadsp_os_stop(bool reload) #ifdef CONFIG_DEBUG_FS wake_up(&logger->wait_queue); /* wait for LOGGER_TIMEOUT to complete filling the buffer */ - wait_for_completion_interruptible_timeout(&logger->complete, + wait_for_completion_timeout(&logger->complete, msecs_to_jiffies(LOGGER_COMPLETE_TIMEOUT)); #endif /* From 5e4dff249fc641bb912cd2762d6a19395e8e2e45 Mon Sep 17 00:00:00 2001 From: William Pierce Date: Thu, 18 May 2017 10:11:31 -0700 Subject: [PATCH 020/138] tegra: linsim removal Removed multiple tegra_platform_is_linsim if statements because of linsim deprecation. Bug 1903831 Change-Id: Id8dd87c9d4ab633175ef3f697fec5cf33dfc4077 Signed-off-by: William Pierce Reviewed-on: https://git-master.nvidia.com/r/1485008 Signed-off-by: Nitin Kumbhar Reviewed-on: https://git-master.nvidia.com/r/1537336 Reviewed-by: Automatic_Commit_Validation_User Reviewed-by: svccoveritychecker GVS: Gerrit_Virtual_Submit --- drivers/platform/tegra/nvadsp/os.c | 7 ------- 1 file changed, 7 deletions(-) diff --git a/drivers/platform/tegra/nvadsp/os.c b/drivers/platform/tegra/nvadsp/os.c index 583d5255..ad383742 100644 --- a/drivers/platform/tegra/nvadsp/os.c +++ b/drivers/platform/tegra/nvadsp/os.c @@ -1683,13 +1683,6 @@ int nvadsp_os_suspend(void) goto end; } - /* - * No os suspend/stop on linsim as - * APE can be reset only once. - */ - if (tegra_platform_is_linsim()) - goto end; - drv_data = platform_get_drvdata(priv.pdev); mutex_lock(&priv.os_run_lock); From 0081a6e97c11d862507584c9e708d721d68ae3c1 Mon Sep 17 00:00:00 2001 From: Nitin Kumbhar Date: Fri, 21 Jul 2017 17:23:18 +0530 Subject: [PATCH 021/138] platform: nvadsp: dfs: add os status checks Add checks for adsp os running state in adsp_override_freq() and update_freq(). If adsp os is not running state then communication with ADSP for freq update will not work. This could result in failure in updating ADSP CPU frequency. Bug 200295526 Bug 200322504 Change-Id: Iea25f542b080ac22faf562f35e82ceede43bbc83 Signed-off-by: Nitin Kumbhar Reviewed-on: https://git-master.nvidia.com/r/1526340 (cherry picked from commit 2aea120c7c1385e14f36e5fa707594e3ab6e2294) Reviewed-on: https://git-master.nvidia.com/r/1527565 Reviewed-on: https://git-master.nvidia.com/r/1537337 Reviewed-by: Automatic_Commit_Validation_User Reviewed-by: svccoveritychecker GVS: Gerrit_Virtual_Submit --- drivers/platform/tegra/nvadsp/adsp_dfs.c | 22 +++++++++++++++++++--- 1 file changed, 19 insertions(+), 3 deletions(-) diff --git a/drivers/platform/tegra/nvadsp/adsp_dfs.c b/drivers/platform/tegra/nvadsp/adsp_dfs.c index c97f598a..9e3974ce 100644 --- a/drivers/platform/tegra/nvadsp/adsp_dfs.c +++ b/drivers/platform/tegra/nvadsp/adsp_dfs.c @@ -115,8 +115,14 @@ static DEFINE_MUTEX(policy_mutex); static bool is_os_running(struct device *dev) { - struct platform_device *pdev = to_platform_device(dev); - struct nvadsp_drv_data *drv_data = platform_get_drvdata(pdev); + struct platform_device *pdev; + struct nvadsp_drv_data *drv_data; + + if (!dev) + return false; + + pdev = to_platform_device(dev); + drv_data = platform_get_drvdata(pdev); if (!drv_data->adsp_os_running) { dev_dbg(&pdev->dev, "%s: adsp os is not loaded\n", __func__); @@ -324,9 +330,14 @@ static unsigned long update_freq(unsigned long freq_khz) int index; int ret; + if (!is_os_running(device)) { + dev_err(device, "adsp os is not running\n"); + return 0; + } + tfreq_hz = adsp_get_target_freq(freq_khz * 1000, &index); if (!tfreq_hz) { - dev_info(device, "unable get the target freq\n"); + dev_err(device, "unable get the target freq\n"); return 0; } @@ -714,6 +725,11 @@ unsigned long adsp_override_freq(unsigned long req_freq_khz) unsigned long ret_freq = 0, freq; int index; + if (!is_os_running(device)) { + pr_err("%s: adsp os is not in running state.\n", __func__); + return 0; + } + mutex_lock(&policy_mutex); freq = req_freq_khz; From a82a2e94de1f307cd5db290097bdc654a45e0bcd Mon Sep 17 00:00:00 2001 From: Nitin Kumbhar Date: Tue, 25 Jul 2017 07:50:28 -0700 Subject: [PATCH 022/138] platform: nvadsp: dfs: update freq table Update adsp cpu frequency table to be specific to a chip to reflect supported frequencies. Bug 200295526 Bug 200322504 Change-Id: I538cbe5280212cc6cd54198d2f3d8e4f78664045 Signed-off-by: Nitin Kumbhar Reviewed-on: https://git-master.nvidia.com/r/1526341 (cherry picked from commit ae50e01f2e14c9b9280c4d8d9ef712ef6f38add4) Reviewed-on: https://git-master.nvidia.com/r/1527566 Reviewed-on: https://git-master.nvidia.com/r/1537338 Reviewed-by: Automatic_Commit_Validation_User Reviewed-by: svccoveritychecker GVS: Gerrit_Virtual_Submit --- drivers/platform/tegra/nvadsp/adsp_dfs.c | 48 +++++++++++++++++++++--- 1 file changed, 43 insertions(+), 5 deletions(-) diff --git a/drivers/platform/tegra/nvadsp/adsp_dfs.c b/drivers/platform/tegra/nvadsp/adsp_dfs.c index 9e3974ce..9ce0cdd4 100644 --- a/drivers/platform/tegra/nvadsp/adsp_dfs.c +++ b/drivers/platform/tegra/nvadsp/adsp_dfs.c @@ -47,7 +47,7 @@ enum adsp_dfs_reply { * Freqency in Hz.The frequency always needs to be a multiple of 12.8 Mhz and * should be extended with a slab 38.4 Mhz. */ -static unsigned long adsp_cpu_freq_table[] = { +static unsigned long adsp_cpu_freq_table_t21x[] = { MIN_ADSP_FREQ, MIN_ADSP_FREQ * 2, MIN_ADSP_FREQ * 3, @@ -71,6 +71,18 @@ static unsigned long adsp_cpu_freq_table[] = { MIN_ADSP_FREQ * 21, }; +/* + * Frequency in Hz. + */ +static unsigned long adsp_cpu_freq_table_t18x[] = { + 150000000lu, + 300000000lu, + 600000000lu, +}; + +static unsigned long *adsp_cpu_freq_table; +static int adsp_cpu_freq_table_size; + struct adsp_dfs_policy { bool enable; /* update_freq_flag = TRUE, ADSP ACKed the new freq @@ -96,12 +108,17 @@ struct adsp_dfs_policy { unsigned long ovr_freq; }; + + +#define MAX_SIZE(x, y) (x > y ? x : y) +#define TIME_IN_STATE_SIZE MAX_SIZE(ARRAY_SIZE(adsp_cpu_freq_table_t21x), \ + ARRAY_SIZE(adsp_cpu_freq_table_t18x)) struct adsp_freq_stats { struct device *dev; unsigned long long last_time; int last_index; - u64 time_in_state[sizeof(adsp_cpu_freq_table) \ - / sizeof(adsp_cpu_freq_table[0])]; + u64 time_in_state[TIME_IN_STATE_SIZE]; + int state_num; }; @@ -190,11 +207,28 @@ static unsigned long adsp_clk_get_rate(struct adsp_dfs_policy *policy) return clk_get_rate(policy->adsp_clk); } +static void adsp_cpu_freq_table_setup(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct device_node *node = dev->of_node; + + if (adsp_cpu_freq_table) + return; + + if (of_device_is_compatible(node, "nvidia,tegra210-adsp")) { + adsp_cpu_freq_table = adsp_cpu_freq_table_t21x; + adsp_cpu_freq_table_size = ARRAY_SIZE(adsp_cpu_freq_table_t21x); + } else { + adsp_cpu_freq_table = adsp_cpu_freq_table_t18x; + adsp_cpu_freq_table_size = ARRAY_SIZE(adsp_cpu_freq_table_t18x); + } +} + /* Expects and returns freq in Hz as table is formmed in terms of Hz */ static unsigned long adsp_get_target_freq(unsigned long tfreq, int *index) { int i; - int size = sizeof(adsp_cpu_freq_table) / sizeof(adsp_cpu_freq_table[0]); + int size = adsp_cpu_freq_table_size; if (tfreq <= adsp_cpu_freq_table[0]) { *index = 0; @@ -795,7 +829,7 @@ void adsp_update_dfs(bool val) /* Should be called after ADSP os is loaded */ int adsp_dfs_core_init(struct platform_device *pdev) { - int size = sizeof(adsp_cpu_freq_table) / sizeof(adsp_cpu_freq_table[0]); + int size = adsp_cpu_freq_table_size; struct nvadsp_drv_data *drv = platform_get_drvdata(pdev); uint16_t mid = HOST_ADSP_DFS_MBOX_ID; int ret = 0; @@ -807,6 +841,10 @@ int adsp_dfs_core_init(struct platform_device *pdev) device = &pdev->dev; policy = &dfs_policy; + /* Set up adsp cpu freq table as per chip */ + if (!adsp_cpu_freq_table) + adsp_cpu_freq_table_setup(pdev); + ret = adsp_clk_get(policy); if (ret) goto end; From a9dd5adae33a862caf315f3e01e3a3c10cb6e954 Mon Sep 17 00:00:00 2001 From: Sameer Pujar Date: Thu, 18 May 2017 18:23:41 +0530 Subject: [PATCH 023/138] drivers: nvadsp: support for adma regdump callback With adma driver as module, following compilation error is seen "undefined reference to tegra_adma_dump_ch_reg". In the current patch, adsp code exports a function to set regdump function call back. This function is used by adsp audio driver to set the function pointer to tegra_adma_dump_ch_reg. This is done in tegra210_adsp_init(). Bug 200289390 Reviewed-on: http://git-master/r/1483001 Change-Id: I1ef259cf3b988a41720fb6706d39d9bd6f86aac3 Signed-off-by: Sameer Pujar (cherry picked from commit 36ccba1262928a6ce8b0f4fff1d4ad2591c4305d) Reviewed-on: https://git-master.nvidia.com/r/1528158 Signed-off-by: Nitin Kumbhar Reviewed-on: https://git-master.nvidia.com/r/1537339 Reviewed-by: Automatic_Commit_Validation_User Reviewed-by: svccoveritychecker GVS: Gerrit_Virtual_Submit --- drivers/platform/tegra/nvadsp/os.c | 21 ++++++++++++++++----- 1 file changed, 16 insertions(+), 5 deletions(-) diff --git a/drivers/platform/tegra/nvadsp/os.c b/drivers/platform/tegra/nvadsp/os.c index ad383742..a4f95784 100644 --- a/drivers/platform/tegra/nvadsp/os.c +++ b/drivers/platform/tegra/nvadsp/os.c @@ -49,10 +49,6 @@ #include "hwmailbox.h" #include "log_state.h" -void tegra_adma_dump_ch_reg(void) -{ -} - #define NVADSP_ELF "adsp.elf" #define NVADSP_FIRMWARE NVADSP_ELF @@ -141,6 +137,12 @@ static void __nvadsp_os_stop(bool); static irqreturn_t adsp_wdt_handler(int irq, void *arg); static irqreturn_t adsp_wfi_handler(int irq, void *arg); +/* + * set by adsp audio driver through exported api nvadsp_set_adma_dump_reg + * used to dump adma registers incase of failures for debug + */ +void (*nvadsp_tegra_adma_dump_ch_reg)(void) = NULL; + #ifdef CONFIG_DEBUG_FS static int adsp_logger_open(struct inode *inode, struct file *file) { @@ -1369,7 +1371,8 @@ void dump_adsp_sys(void) dump_adsp_logs(); dump_mailbox_regs(); get_adsp_state(); - tegra_adma_dump_ch_reg(); + if (nvadsp_tegra_adma_dump_ch_reg) + (*nvadsp_tegra_adma_dump_ch_reg)(); print_agic_irq_states(); } EXPORT_SYMBOL(dump_adsp_sys); @@ -1451,6 +1454,14 @@ static int setup_interrupts(struct nvadsp_os_data *priv) return ret; } +void nvadsp_set_adma_dump_reg(void (*cb_adma_regdump)(void)) +{ + nvadsp_tegra_adma_dump_ch_reg = cb_adma_regdump; + pr_info("%s: callback for adma reg dump is sent to %p\n", + __func__, nvadsp_tegra_adma_dump_ch_reg); +} +EXPORT_SYMBOL(nvadsp_set_adma_dump_reg); + int nvadsp_os_start(void) { struct nvadsp_drv_data *drv_data; From 303185c87a0c097ab4f844b2da5ae8c7318a2134 Mon Sep 17 00:00:00 2001 From: Nitin Kumbhar Date: Fri, 11 Aug 2017 02:08:22 -0700 Subject: [PATCH 024/138] platform: nvadsp: rename Makefile and Kconfig Rename Makefile and Kconfig as Makefile.nvidia and Kconfig.nvidia respectively. This is done to allow use of downstream nvadsp driver in kernels like K4.4, K4.9, etc. Bug 200325731 Change-Id: I96269398089700677bdf919519295f80b40e7848 Signed-off-by: Nitin Kumbhar Reviewed-on: https://git-master.nvidia.com/r/1537418 Reviewed-by: Sachin Nikam Reviewed-by: svccoveritychecker Reviewed-by: Ajay Nandakumar M GVS: Gerrit_Virtual_Submit --- drivers/platform/tegra/nvadsp/{Kconfig => Kconfig.nvidia} | 0 drivers/platform/tegra/nvadsp/{Makefile => Makefile.nvidia} | 0 2 files changed, 0 insertions(+), 0 deletions(-) rename drivers/platform/tegra/nvadsp/{Kconfig => Kconfig.nvidia} (100%) rename drivers/platform/tegra/nvadsp/{Makefile => Makefile.nvidia} (100%) diff --git a/drivers/platform/tegra/nvadsp/Kconfig b/drivers/platform/tegra/nvadsp/Kconfig.nvidia similarity index 100% rename from drivers/platform/tegra/nvadsp/Kconfig rename to drivers/platform/tegra/nvadsp/Kconfig.nvidia diff --git a/drivers/platform/tegra/nvadsp/Makefile b/drivers/platform/tegra/nvadsp/Makefile.nvidia similarity index 100% rename from drivers/platform/tegra/nvadsp/Makefile rename to drivers/platform/tegra/nvadsp/Makefile.nvidia From 9b73ee3d3ebc1a91963aae88078e18b20f70ad86 Mon Sep 17 00:00:00 2001 From: Nitin Kumbhar Date: Fri, 11 Aug 2017 03:25:00 -0700 Subject: [PATCH 025/138] platform: nvadsp: kconfig: select ARM_GIC_PM dep Select ARM_GIC_PM by default as K4.9 nvadsp driver needs GIC PM feature. For K4.4 there is no dependency and no such config so it will be ignored. Bug 200325731 Change-Id: I05735a7f5e2ae6f31df6938a45b89dc83150c7ec Signed-off-by: Nitin Kumbhar Reviewed-on: https://git-master.nvidia.com/r/1537419 Reviewed-by: Automatic_Commit_Validation_User GVS: Gerrit_Virtual_Submit Reviewed-by: svccoveritychecker Reviewed-by: Bharat Nihalani Reviewed-by: Ajay Nandakumar M Reviewed-by: Sachin Nikam --- drivers/platform/tegra/nvadsp/Kconfig.nvidia | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/platform/tegra/nvadsp/Kconfig.nvidia b/drivers/platform/tegra/nvadsp/Kconfig.nvidia index 37f4b8eb..4f3fa700 100644 --- a/drivers/platform/tegra/nvadsp/Kconfig.nvidia +++ b/drivers/platform/tegra/nvadsp/Kconfig.nvidia @@ -1,7 +1,7 @@ config TEGRA_NVADSP bool "Enable Host ADSP driver" default n - depends on ARM_GIC_PM + select ARM_GIC_PM select FIQ help Enables support for Host ADSP driver. From 3fbe739b6f1d5ffa1f3bc2c0fbf9bc90c2ec45d7 Mon Sep 17 00:00:00 2001 From: Ajay Nandakumar Date: Fri, 14 Jul 2017 11:06:44 +0530 Subject: [PATCH 026/138] platform: nvadsp: adsp_cpu_abus to set freq on t21x Using adsp_cpu_abus which is a virtual clk (ADSP_CPU_ABUS clock) to set frequency on t21x platform. Earlier the physical ADSP clk was used which only provides a constant frequency. Bug 1955157 Change-Id: Ifcf42b50a543a95e934e651e68056cb1c8d6c654 Signed-off-by: Ajay Nandakumar Reviewed-on: https://git-master.nvidia.com/r/1520261 (cherry picked from commit c1c8d713cd723b20f890cfbd205e38a90007e847) Reviewed-on: https://git-master.nvidia.com/r/1542708 Reviewed-by: Nitin Kumbhar Reviewed-by: svccoveritychecker Reviewed-by: svc-mobile-coverity GVS: Gerrit_Virtual_Submit Reviewed-by: Bharat Nihalani --- drivers/platform/tegra/nvadsp/adsp_dfs.c | 28 ++++++++++++++---------- drivers/platform/tegra/nvadsp/dev-t21x.c | 20 +++++++++++++++-- drivers/platform/tegra/nvadsp/dev.h | 2 +- drivers/platform/tegra/nvadsp/os.c | 8 +++---- 4 files changed, 39 insertions(+), 19 deletions(-) diff --git a/drivers/platform/tegra/nvadsp/adsp_dfs.c b/drivers/platform/tegra/nvadsp/adsp_dfs.c index 9ce0cdd4..a12dc4f6 100644 --- a/drivers/platform/tegra/nvadsp/adsp_dfs.c +++ b/drivers/platform/tegra/nvadsp/adsp_dfs.c @@ -99,6 +99,7 @@ struct adsp_dfs_policy { struct clk *adsp_clk; struct clk *aclk_clk; + struct clk *adsp_cpu_abus_clk; struct notifier_block rate_change_nb; struct nvadsp_mbox mbox; @@ -153,11 +154,7 @@ static int adsp_clk_get(struct adsp_dfs_policy *policy) struct device_node *node = device->of_node; int ret = 0; - if (IS_ENABLED(CONFIG_COMMON_CLK)) - policy->adsp_clk = devm_clk_get(device, "adsp"); - else - policy->adsp_clk = clk_get_sys(NULL, policy->clk_name); - + policy->adsp_clk = devm_clk_get(device, "adsp"); if (IS_ERR_OR_NULL(policy->adsp_clk)) { dev_err(device, "unable to find adsp clock\n"); ret = PTR_ERR(policy->adsp_clk); @@ -170,6 +167,14 @@ static int adsp_clk_get(struct adsp_dfs_policy *policy) dev_err(device, "unable to find aclk clock\n"); ret = PTR_ERR(policy->aclk_clk); } + } else { + policy->adsp_cpu_abus_clk = + devm_clk_get(device, "adsp_cpu_abus"); + + if (IS_ERR_OR_NULL(policy->adsp_cpu_abus_clk)) { + dev_err(device, "unable to find adsp cpu abus clock\n"); + ret = PTR_ERR(policy->adsp_cpu_abus_clk); + } } return ret; @@ -177,12 +182,11 @@ static int adsp_clk_get(struct adsp_dfs_policy *policy) static void adsp_clk_put(struct adsp_dfs_policy *policy) { - if (policy->adsp_clk) { - if (IS_ENABLED(CONFIG_COMMON_CLK)) - devm_clk_put(device, policy->adsp_clk); - else - clk_put(policy->adsp_clk); - } + if (policy->adsp_cpu_abus_clk) + devm_clk_put(device, policy->adsp_cpu_abus_clk); + + if (policy->adsp_clk) + devm_clk_put(device, policy->adsp_clk); if (policy->aclk_clk) devm_clk_put(device, policy->aclk_clk); @@ -195,7 +199,7 @@ static int adsp_clk_set_rate(struct adsp_dfs_policy *policy, int ret; if (of_device_is_compatible(node, "nvidia,tegra210-adsp")) - ret = clk_set_rate(policy->adsp_clk, freq_hz); + ret = clk_set_rate(policy->adsp_cpu_abus_clk, freq_hz); else ret = clk_set_rate(policy->aclk_clk, freq_hz); diff --git a/drivers/platform/tegra/nvadsp/dev-t21x.c b/drivers/platform/tegra/nvadsp/dev-t21x.c index c36dc5f0..b165415d 100644 --- a/drivers/platform/tegra/nvadsp/dev-t21x.c +++ b/drivers/platform/tegra/nvadsp/dev-t21x.c @@ -37,7 +37,12 @@ static void nvadsp_clocks_disable(struct platform_device *pdev) clk_disable_unprepare(drv_data->adsp_clk); dev_dbg(dev, "adsp clocks disabled\n"); drv_data->adsp_clk = NULL; - drv_data->adsp_cpu_clk = NULL; + } + + if (drv_data->adsp_cpu_abus_clk) { + clk_disable_unprepare(drv_data->adsp_cpu_abus_clk); + dev_dbg(dev, "adsp cpu abus clock disabled\n"); + drv_data->adsp_cpu_abus_clk = NULL; } if (drv_data->adsp_neon_clk) { @@ -95,7 +100,18 @@ static int nvadsp_clocks_enable(struct platform_device *pdev) dev_err(dev, "unable to enable adsp clock\n"); goto end; } - drv_data->adsp_cpu_clk = drv_data->adsp_clk; + + drv_data->adsp_cpu_abus_clk = devm_clk_get(dev, "adsp_cpu_abus"); + if (IS_ERR_OR_NULL(drv_data->adsp_cpu_abus_clk)) { + dev_err(dev, "unable to find adsp cpu abus clock\n"); + ret = PTR_ERR(drv_data->adsp_cpu_abus_clk); + goto end; + } + ret = clk_prepare_enable(drv_data->adsp_cpu_abus_clk); + if (ret) { + dev_err(dev, "unable to enable adsp cpu abus clock\n"); + goto end; + } drv_data->adsp_neon_clk = devm_clk_get(dev, "adspneon"); if (IS_ERR_OR_NULL(drv_data->adsp_neon_clk)) { diff --git a/drivers/platform/tegra/nvadsp/dev.h b/drivers/platform/tegra/nvadsp/dev.h index 0198f87d..2de943eb 100644 --- a/drivers/platform/tegra/nvadsp/dev.h +++ b/drivers/platform/tegra/nvadsp/dev.h @@ -161,7 +161,7 @@ struct nvadsp_drv_data { struct clk *apb2ape_clk; struct clk *adsp_clk; struct clk *aclk_clk; - struct clk *adsp_cpu_clk; + struct clk *adsp_cpu_abus_clk; struct clk *adsp_neon_clk; struct clk *ape_emc_clk; struct clk *uartape_clk; diff --git a/drivers/platform/tegra/nvadsp/os.c b/drivers/platform/tegra/nvadsp/os.c index a4f95784..3b731ea6 100644 --- a/drivers/platform/tegra/nvadsp/os.c +++ b/drivers/platform/tegra/nvadsp/os.c @@ -828,7 +828,7 @@ static int nvadsp_t210_set_clks_and_prescalar(struct nvadsp_drv_data *drv_data) adsp_freq = drv_data->adsp_freq * 1000; /* in Hz*/ - max_adsp_freq = clk_round_rate(drv_data->adsp_cpu_clk, + max_adsp_freq = clk_round_rate(drv_data->adsp_cpu_abus_clk, ULONG_MAX); max_index = max_adsp_freq / MIN_ADSP_FREQ; cur_index = adsp_freq / MIN_ADSP_FREQ; @@ -856,7 +856,7 @@ static int nvadsp_t210_set_clks_and_prescalar(struct nvadsp_drv_data *drv_data) adsp_freq = cur_index * MIN_ADSP_FREQ; - ret = clk_set_rate(drv_data->adsp_cpu_clk, adsp_freq); + ret = clk_set_rate(drv_data->adsp_cpu_abus_clk, adsp_freq); if (ret) goto end; @@ -868,7 +868,7 @@ static int nvadsp_t210_set_clks_and_prescalar(struct nvadsp_drv_data *drv_data) end: dev_dbg(dev, "adsp cpu freq %luKHz\n", - clk_get_rate(drv_data->adsp_cpu_clk) / 1000); + clk_get_rate(drv_data->adsp_cpu_abus_clk) / 1000); dev_dbg(dev, "timer prescalar %x\n", os_args->timer_prescalar); return ret; @@ -983,7 +983,7 @@ static int nvadsp_set_boot_freqs(struct nvadsp_drv_data *drv_data) return 0; if (of_device_is_compatible(node, "nvidia,tegra210-adsp")) { - if (drv_data->adsp_cpu_clk) { + if (drv_data->adsp_cpu_abus_clk) { ret = nvadsp_t210_set_clks_and_prescalar(drv_data); if (ret) goto end; From 12be717e8ca30e76522173622f90d85f23552bd2 Mon Sep 17 00:00:00 2001 From: Ajay Nandakumar Date: Tue, 27 Jun 2017 12:32:01 +0530 Subject: [PATCH 027/138] platform: nvadsp: Silence debug t21x clock prints Moving debug clock logs from dev_info to dev_dbg as it creates a huge number of prints as ADSP is frequently suspended and resumed. Change-Id: I923ea079f74f1de659e3c8626d437cb0a22802bb Signed-off-by: Ajay Nandakumar Reviewed-on: https://git-master/r/1509154 (cherry picked from commit 3e9b288c245789f0a09cc7d9ba5a0ef2a7d74a4c) Reviewed-on: https://git-master.nvidia.com/r/1543825 Reviewed-by: Automatic_Commit_Validation_User Reviewed-by: svc-mobile-coverity Reviewed-by: svccoveritychecker GVS: Gerrit_Virtual_Submit Reviewed-by: Sachin Nikam Tested-by: Sachin Nikam --- drivers/platform/tegra/nvadsp/dev-t21x.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/platform/tegra/nvadsp/dev-t21x.c b/drivers/platform/tegra/nvadsp/dev-t21x.c index b165415d..bbcc186d 100644 --- a/drivers/platform/tegra/nvadsp/dev-t21x.c +++ b/drivers/platform/tegra/nvadsp/dev-t21x.c @@ -47,7 +47,7 @@ static void nvadsp_clocks_disable(struct platform_device *pdev) if (drv_data->adsp_neon_clk) { clk_disable_unprepare(drv_data->adsp_neon_clk); - dev_info(dev, "adsp_neon clocks disabled\n"); + dev_dbg(dev, "adsp_neon clocks disabled\n"); drv_data->adsp_neon_clk = NULL; } @@ -59,7 +59,7 @@ static void nvadsp_clocks_disable(struct platform_device *pdev) if (drv_data->apb2ape_clk) { clk_disable_unprepare(drv_data->apb2ape_clk); - dev_info(dev, "apb2ape clock disabled\n"); + dev_dbg(dev, "apb2ape clock disabled\n"); drv_data->apb2ape_clk = NULL; } From 3fb218dbaad107ee8906cb8d163d80b8afa1247f Mon Sep 17 00:00:00 2001 From: Nitin Kumbhar Date: Thu, 24 Aug 2017 04:25:43 -0700 Subject: [PATCH 028/138] platform: nvadsp: rename aram manager apis As aram manager functions are made available through tegra_nvadsp.h header, rename those to make those specific to nvadsp. Bug 200326188 Change-Id: Ib10d78f6f74600bb2771151ca38792f6f81858f1 Signed-off-by: Nitin Kumbhar Reviewed-on: https://git-master.nvidia.com/r/1545002 Reviewed-by: Automatic_Commit_Validation_User Reviewed-by: Ajay Nandakumar M Reviewed-by: svc-mobile-coverity Reviewed-by: svccoveritychecker GVS: Gerrit_Virtual_Submit Reviewed-by: Sachin Nikam --- drivers/platform/tegra/nvadsp/app.c | 14 ++++----- drivers/platform/tegra/nvadsp/aram_manager.c | 33 ++++++++++---------- drivers/platform/tegra/nvadsp/aram_manager.h | 13 ++------ drivers/platform/tegra/nvadsp/dev.c | 4 +-- 4 files changed, 28 insertions(+), 36 deletions(-) diff --git a/drivers/platform/tegra/nvadsp/app.c b/drivers/platform/tegra/nvadsp/app.c index 6dc927f8..4b1acb1c 100644 --- a/drivers/platform/tegra/nvadsp/app.c +++ b/drivers/platform/tegra/nvadsp/app.c @@ -3,7 +3,7 @@ * * ADSP OS App management * - * Copyright (C) 2014-2016, NVIDIA Corporation. All rights reserved. + * Copyright (C) 2014-2017, 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 @@ -387,7 +387,7 @@ static void free_instance_memory(nvadsp_app_info_t *app, } if (mem->aram_flag) - aram_release(mem->aram); + nvadsp_aram_release(mem->aram); else if (mem->aram) nvadsp_free_coherent(sz->aram, mem->aram, iova_mem->aram); mem->aram = NULL; @@ -395,7 +395,7 @@ static void free_instance_memory(nvadsp_app_info_t *app, mem->aram_flag = 0; if (mem->aram_x_flag) { - aram_release(mem->aram_x); + nvadsp_aram_release(mem->aram_x); mem->aram_x = NULL; iova_mem->aram_x = 0; mem->aram_flag = 0; @@ -454,9 +454,9 @@ static int create_instance_memory(nvadsp_app_info_t *app, } if (sz->aram) { - aram_handle = aram_request(name, sz->aram); + aram_handle = nvadsp_aram_request(name, sz->aram); if (!IS_ERR_OR_NULL(aram_handle)) { - iova_mem->aram = aram_get_address(aram_handle); + iova_mem->aram = nvadsp_aram_get_address(aram_handle); mem->aram = aram_handle; iova_mem->aram_flag = mem->aram_flag = 1; dev_dbg(dev, "%s aram %x\n", name, iova_mem->aram); @@ -479,9 +479,9 @@ static int create_instance_memory(nvadsp_app_info_t *app, } if (sz->aram_x) { - aram_handle = aram_request(name, sz->aram); + aram_handle = nvadsp_aram_request(name, sz->aram); if (!IS_ERR_OR_NULL(aram_handle)) { - iova_mem->aram_x = aram_get_address(aram_handle); + iova_mem->aram_x = nvadsp_aram_get_address(aram_handle); mem->aram_x = aram_handle; iova_mem->aram_x_flag = mem->aram_x_flag = 1; dev_dbg(dev, "aram_x %x\n", iova_mem->aram_x); diff --git a/drivers/platform/tegra/nvadsp/aram_manager.c b/drivers/platform/tegra/nvadsp/aram_manager.c index 2740e8b2..4b28ae95 100644 --- a/drivers/platform/tegra/nvadsp/aram_manager.c +++ b/drivers/platform/tegra/nvadsp/aram_manager.c @@ -3,7 +3,7 @@ * * ARAM manager * - * Copyright (C) 2014-2016, NVIDIA Corporation. All rights reserved. + * Copyright (C) 2014-2017, 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 @@ -19,6 +19,7 @@ #define pr_fmt(fmt) "%s : %d, " fmt, __func__, __LINE__ #include +#include #include "aram_manager.h" @@ -27,53 +28,53 @@ static void *aram_handle; static LIST_HEAD(aram_alloc_list); static LIST_HEAD(aram_free_list); -void aram_print(void) +void nvadsp_aram_print(void) { mem_print(aram_handle); } -EXPORT_SYMBOL(aram_print); +EXPORT_SYMBOL(nvadsp_aram_print); -void *aram_request(const char *name, size_t size) +void *nvadsp_aram_request(const char *name, size_t size) { return mem_request(aram_handle, name, size); } -EXPORT_SYMBOL(aram_request); +EXPORT_SYMBOL(nvadsp_aram_request); -bool aram_release(void *handle) +bool nvadsp_aram_release(void *handle) { return mem_release(aram_handle, handle); } -EXPORT_SYMBOL(aram_release); +EXPORT_SYMBOL(nvadsp_aram_release); -unsigned long aram_get_address(void *handle) +unsigned long nvadsp_aram_get_address(void *handle) { return mem_get_address(handle); } -EXPORT_SYMBOL(aram_get_address); +EXPORT_SYMBOL(nvadsp_aram_get_address); #ifdef CONFIG_DEBUG_FS static struct dentry *aram_dump_debugfs_file; -static int aram_dump(struct seq_file *s, void *data) +static int nvadsp_aram_dump(struct seq_file *s, void *data) { mem_dump(aram_handle, s); return 0; } -static int aram_dump_open(struct inode *inode, struct file *file) +static int nvadsp_aram_dump_open(struct inode *inode, struct file *file) { - return single_open(file, aram_dump, inode->i_private); + return single_open(file, nvadsp_aram_dump, inode->i_private); } static const struct file_operations aram_dump_fops = { - .open = aram_dump_open, + .open = nvadsp_aram_dump_open, .read = seq_read, .llseek = seq_lseek, .release = single_release, }; #endif -int aram_init(unsigned long addr, unsigned long size) +int nvadsp_aram_init(unsigned long addr, unsigned long size) { aram_handle = create_mem_manager("ARAM", addr, size); if (IS_ERR(aram_handle)) { @@ -92,14 +93,12 @@ int aram_init(unsigned long addr, unsigned long size) #endif return 0; } -EXPORT_SYMBOL(aram_init); -void aram_exit(void) +void nvadsp_aram_exit(void) { #ifdef CONFIG_DEBUG_FS debugfs_remove(aram_dump_debugfs_file); #endif destroy_mem_manager(aram_handle); } -EXPORT_SYMBOL(aram_exit); diff --git a/drivers/platform/tegra/nvadsp/aram_manager.h b/drivers/platform/tegra/nvadsp/aram_manager.h index 786c304b..18e8f887 100644 --- a/drivers/platform/tegra/nvadsp/aram_manager.h +++ b/drivers/platform/tegra/nvadsp/aram_manager.h @@ -1,7 +1,7 @@ /* * Header file for aram manager * - * Copyright (c) 2014-2015, NVIDIA CORPORATION. All rights reserved. + * Copyright (c) 2014-2017, NVIDIA CORPORATION. All rights reserved. * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU General Public License, @@ -18,13 +18,6 @@ #include "mem_manager.h" -int aram_init(unsigned long addr, unsigned long size); -void aram_exit(void); - -void *aram_request(const char *name, size_t size); -bool aram_release(void *handle); - -unsigned long aram_get_address(void *handle); -void aram_print(void); - +int nvadsp_aram_init(unsigned long addr, unsigned long size); +void nvadsp_aram_exit(void); #endif /* __TEGRA_NVADSP_ARAM_MANAGER_H */ diff --git a/drivers/platform/tegra/nvadsp/dev.c b/drivers/platform/tegra/nvadsp/dev.c index 45b55b4f..621618ac 100644 --- a/drivers/platform/tegra/nvadsp/dev.c +++ b/drivers/platform/tegra/nvadsp/dev.c @@ -357,7 +357,7 @@ static int __init nvadsp_probe(struct platform_device *pdev) aram_addr = drv_data->adsp_mem[ARAM_ALIAS_0_ADDR]; aram_size = drv_data->adsp_mem[ARAM_ALIAS_0_SIZE]; - ret = aram_init(aram_addr, aram_size); + ret = nvadsp_aram_init(aram_addr, aram_size); if (ret) dev_err(dev, "Failed to init aram\n"); err: @@ -375,7 +375,7 @@ static int nvadsp_remove(struct platform_device *pdev) #ifdef CONFIG_TEGRA_EMC_APE_DFS emc_dfs_exit(); #endif - aram_exit(); + nvadsp_aram_exit(); pm_runtime_disable(&pdev->dev); From 02f9bb7fd002a8d41604f6a89767ff701988aac2 Mon Sep 17 00:00:00 2001 From: Gaurav Tendolkar Date: Thu, 6 Jul 2017 14:54:29 +0530 Subject: [PATCH 029/138] nvadsp: add debugfs file for adsp crash detection Added file adsp_health on which userspace app can poll to check for ADSP crash or hang Change-Id: If2265cf245759809da233ae366a3fbcb7f42d4b6 Signed-off-by: Gaurav Tendolkar Reviewed-on: https://git-master.nvidia.com/r/1514462 (cherry picked from commit fb8c6a3119a6ceb4dc6a19bd548737d279f7cba6) Signed-off-by: Hariharan Sivaraman Reviewed-on: https://git-master.nvidia.com/r/1558929 Reviewed-by: svc-mobile-coverity Reviewed-by: Automatic_Commit_Validation_User Reviewed-by: svccoveritychecker GVS: Gerrit_Virtual_Submit Reviewed-by: Nitin Kumbhar --- drivers/platform/tegra/nvadsp/dev.h | 3 +++ drivers/platform/tegra/nvadsp/os.c | 42 +++++++++++++++++++++++++++++ 2 files changed, 45 insertions(+) diff --git a/drivers/platform/tegra/nvadsp/dev.h b/drivers/platform/tegra/nvadsp/dev.h index 2de943eb..831b0527 100644 --- a/drivers/platform/tegra/nvadsp/dev.h +++ b/drivers/platform/tegra/nvadsp/dev.h @@ -201,6 +201,9 @@ struct nvadsp_drv_data { bool adspff_init; #endif + wait_queue_head_t adsp_health_waitq; + bool adsp_crashed; + u32 adsp_mem[ADSP_MEM_END]; bool adsp_unit_fpga; u32 unit_fpga_reset[ADSP_UNIT_FPGA_RESET_END]; diff --git a/drivers/platform/tegra/nvadsp/os.c b/drivers/platform/tegra/nvadsp/os.c index 3b731ea6..40e6cbcd 100644 --- a/drivers/platform/tegra/nvadsp/os.c +++ b/drivers/platform/tegra/nvadsp/os.c @@ -38,6 +38,7 @@ #include #include #include +#include #include @@ -1781,6 +1782,10 @@ static irqreturn_t adsp_wdt_handler(int irq, void *arg) struct device *dev = &data->pdev->dev; drv_data = platform_get_drvdata(data->pdev); + + drv_data->adsp_crashed = true; + wake_up_interruptible(&drv_data->adsp_health_waitq); + if (!drv_data->adsp_unit_fpga) { dev_crit(dev, "ADSP OS Hanged or Crashed! Restarting...\n"); schedule_work(&data->restart_os_work); @@ -1850,6 +1855,37 @@ static int adsp_create_os_version(struct dentry *adsp_debugfs_root) } return 0; } + +static unsigned int adsp_health_poll(struct file *file, + poll_table *wait) +{ + struct nvadsp_drv_data *drv_data = platform_get_drvdata(priv.pdev); + + poll_wait(file, &drv_data->adsp_health_waitq, wait); + + if (drv_data->adsp_crashed) + return POLLIN | POLLRDNORM; + + return 0; +} + +static const struct file_operations adsp_health_fops = { + .poll = adsp_health_poll, +}; + +static int adsp_create_adsp_health(struct dentry *adsp_debugfs_root) +{ + struct device *dev = &priv.pdev->dev; + struct dentry *d; + + d = debugfs_create_file("adsp_health", RO_MODE, adsp_debugfs_root, + NULL, &adsp_health_fops); + if (!d) { + dev_err(dev, "failed to create adsp_health\n"); + return -EINVAL; + } + return 0; +} #endif static ssize_t tegrafw_read_adsp(struct device *dev, @@ -1912,6 +1948,12 @@ int __init nvadsp_os_probe(struct platform_device *pdev) if (adsp_create_os_version(drv_data->adsp_debugfs_root)) dev_err(dev, "unable to create adsp_version file\n"); + if (adsp_create_adsp_health(drv_data->adsp_debugfs_root)) + dev_err(dev, "unable to create adsp_health file\n"); + + drv_data->adsp_crashed = false; + init_waitqueue_head(&drv_data->adsp_health_waitq); + #endif /* CONFIG_DEBUG_FS */ devm_tegrafw_register(dev, "APE", TFW_DONT_CACHE, From c6fc11080f25785db2bab19c222ee20d4dfbff72 Mon Sep 17 00:00:00 2001 From: Sameer Pujar Date: Wed, 4 Oct 2017 12:30:32 +0530 Subject: [PATCH 030/138] nvadsp: fix compilation warning. This patch fixes following warning, warning: symbol 'nvadsp_tegra_adma_dump_ch_reg' was not declared Bug 200299572 Change-Id: I6d4b06b73d7d8928a08c975c305bf76d8ac58568 Signed-off-by: Sameer Pujar Reviewed-on: https://git-master.nvidia.com/r/1572940 Reviewed-by: Automatic_Commit_Validation_User Reviewed-by: svc-mobile-coverity Reviewed-by: svccoveritychecker GVS: Gerrit_Virtual_Submit Reviewed-by: Sachin Nikam --- drivers/platform/tegra/nvadsp/os.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/platform/tegra/nvadsp/os.c b/drivers/platform/tegra/nvadsp/os.c index 40e6cbcd..eb9f9d9b 100644 --- a/drivers/platform/tegra/nvadsp/os.c +++ b/drivers/platform/tegra/nvadsp/os.c @@ -142,7 +142,7 @@ static irqreturn_t adsp_wfi_handler(int irq, void *arg); * set by adsp audio driver through exported api nvadsp_set_adma_dump_reg * used to dump adma registers incase of failures for debug */ -void (*nvadsp_tegra_adma_dump_ch_reg)(void) = NULL; +static void (*nvadsp_tegra_adma_dump_ch_reg)(void); #ifdef CONFIG_DEBUG_FS static int adsp_logger_open(struct inode *inode, struct file *file) From 1138e7fbc74cc60cb902d318cc9321bafdc5c5d7 Mon Sep 17 00:00:00 2001 From: Stephen Warren Date: Thu, 14 Sep 2017 10:51:18 -0600 Subject: [PATCH 031/138] nvidia: use kernel overlay features Update all Kconfig files and Makefiles to rely on the kernel overlay feature. In particular, don't include any Kconfig files or Makefiles from other overlays. -I directives in CFLAGS are not yet cleaned up. Bug 1978395 Change-Id: I425d37d55f8ea61fb3a082a1504f994ff30cec03 Signed-off-by: Stephen Warren Reviewed-on: https://git-master.nvidia.com/r/1561187 Reviewed-by: Terje Bergstrom GVS: Gerrit_Virtual_Submit Reviewed-by: mobile promotions Tested-by: mobile promotions --- drivers/platform/tegra/nvadsp/{Kconfig.nvidia => Kconfig} | 0 drivers/platform/tegra/nvadsp/{Makefile.nvidia => Makefile} | 0 2 files changed, 0 insertions(+), 0 deletions(-) rename drivers/platform/tegra/nvadsp/{Kconfig.nvidia => Kconfig} (100%) rename drivers/platform/tegra/nvadsp/{Makefile.nvidia => Makefile} (100%) diff --git a/drivers/platform/tegra/nvadsp/Kconfig.nvidia b/drivers/platform/tegra/nvadsp/Kconfig similarity index 100% rename from drivers/platform/tegra/nvadsp/Kconfig.nvidia rename to drivers/platform/tegra/nvadsp/Kconfig diff --git a/drivers/platform/tegra/nvadsp/Makefile.nvidia b/drivers/platform/tegra/nvadsp/Makefile similarity index 100% rename from drivers/platform/tegra/nvadsp/Makefile.nvidia rename to drivers/platform/tegra/nvadsp/Makefile From 12dc199ff378376c837ba119c5a3a74d16f5f7b9 Mon Sep 17 00:00:00 2001 From: Prateek Patel Date: Sun, 24 Sep 2017 23:28:33 -0700 Subject: [PATCH 032/138] platform: tegra: adsp: remove emc clocks With common clock framework, the shared emc clks have been replaced by a bandwidth manager. Hence, the shared clocks for t210 on 4.9 are deprecated. bug 200345548 Change-Id: I09638ffef2635cabefbb560f2d686eb306f4fd78 Signed-off-by: Prateek Patel Reviewed-on: https://git-master.nvidia.com/r/1568428 GVS: Gerrit_Virtual_Submit Reviewed-by: svc-mobile-coverity Reviewed-by: Nitin Kumbhar Reviewed-by: mobile promotions Tested-by: mobile promotions --- drivers/platform/tegra/nvadsp/dev-t21x.c | 20 --------------- drivers/platform/tegra/nvadsp/os.c | 31 ------------------------ 2 files changed, 51 deletions(-) diff --git a/drivers/platform/tegra/nvadsp/dev-t21x.c b/drivers/platform/tegra/nvadsp/dev-t21x.c index bbcc186d..6ae797e2 100644 --- a/drivers/platform/tegra/nvadsp/dev-t21x.c +++ b/drivers/platform/tegra/nvadsp/dev-t21x.c @@ -62,12 +62,6 @@ static void nvadsp_clocks_disable(struct platform_device *pdev) dev_dbg(dev, "apb2ape clock disabled\n"); drv_data->apb2ape_clk = NULL; } - - if (drv_data->ape_emc_clk) { - clk_disable_unprepare(drv_data->ape_emc_clk); - dev_dbg(dev, "ape.emc clock disabled\n"); - drv_data->ape_emc_clk = NULL; - } } static int nvadsp_clocks_enable(struct platform_device *pdev) @@ -126,20 +120,6 @@ static int nvadsp_clocks_enable(struct platform_device *pdev) } dev_dbg(dev, "adsp cpu clock enabled\n"); - drv_data->ape_emc_clk = devm_clk_get(dev, "adsp.emc"); - if (IS_ERR_OR_NULL(drv_data->ape_emc_clk)) { - dev_err(dev, "unable to find adsp.emc clock\n"); - ret = PTR_ERR(drv_data->ape_emc_clk); - goto end; - } - - ret = clk_prepare_enable(drv_data->ape_emc_clk); - if (ret) { - dev_err(dev, "unable to enable adsp.emc clock\n"); - goto end; - } - dev_dbg(dev, "ape.emc is enabled\n"); - drv_data->apb2ape_clk = devm_clk_get(dev, "adsp.apb2ape"); if (IS_ERR_OR_NULL(drv_data->apb2ape_clk)) { dev_err(dev, "unable to find adsp.apb2ape clk\n"); diff --git a/drivers/platform/tegra/nvadsp/os.c b/drivers/platform/tegra/nvadsp/os.c index eb9f9d9b..99acab8f 100644 --- a/drivers/platform/tegra/nvadsp/os.c +++ b/drivers/platform/tegra/nvadsp/os.c @@ -770,31 +770,6 @@ u32 adsp_to_emc_freq(u32 adspfreq) return 0; /* emc min */ } -static int nvadsp_set_ape_emc_freq(struct nvadsp_drv_data *drv_data) -{ - unsigned long ape_emc_freq = drv_data->ape_emc_freq * 1000; /* in Hz */ - struct device *dev = &priv.pdev->dev; - int ret; - -#ifdef CONFIG_TEGRA_ADSP_DFS - /* pass adsp freq in KHz. adsp_emc_freq in Hz */ - ape_emc_freq = adsp_to_emc_freq(drv_data->adsp_freq / 1000) * 1000; -#endif - dev_dbg(dev, "requested adsp cpu freq %luKHz", - drv_data->adsp_freq / 1000); - dev_dbg(dev, "ape.emc freq %luHz\n", ape_emc_freq / 1000); - - - if (!ape_emc_freq) - return 0; - - ret = clk_set_rate(drv_data->ape_emc_clk, ape_emc_freq); - - dev_dbg(dev, "ape.emc freq %luKHz\n", - clk_get_rate(drv_data->ape_emc_clk) / 1000); - return ret; -} - static int nvadsp_set_ape_freq(struct nvadsp_drv_data *drv_data) { unsigned long ape_freq = drv_data->ape_freq * 1000; /* in Hz*/ @@ -1009,12 +984,6 @@ static int nvadsp_set_boot_freqs(struct nvadsp_drv_data *drv_data) goto end; } - if (drv_data->ape_emc_clk) { - ret = nvadsp_set_ape_emc_freq(drv_data); - if (ret) - goto end; - } - end: return ret; } From b3a66c2476e9883d9c07ee33e86e01b2e607a39c Mon Sep 17 00:00:00 2001 From: Hariharan Sivaraman Date: Thu, 28 Apr 2016 16:17:26 +0530 Subject: [PATCH 033/138] tegra: nvadsp: Include adsp usage functionality Adds functionality to calculate adsp cpu usage Inits, loads, starts and resume adsp_lpthread app Bug 1727014 Change-Id: Icaf7a1ba9e91bff19d7fbc894bfdd0f884971d6c Signed-off-by: Hariharan Sivaraman Reviewed-on: http://git-master/r/1134173 (cherry picked from commit 256bd7180cefa523a9e480be315fbf47b9c8de4f) Signed-off-by: Hariharan Sivaraman Reviewed-on: https://git-master.nvidia.com/r/1564162 Reviewed-by: svc-mobile-coverity GVS: Gerrit_Virtual_Submit Reviewed-by: Ajay Nandakumar M Reviewed-by: Nitin Pai Reviewed-by: mobile promotions Tested-by: mobile promotions --- drivers/platform/tegra/nvadsp/Kconfig | 11 + drivers/platform/tegra/nvadsp/Makefile | 5 + drivers/platform/tegra/nvadsp/adsp_lpthread.c | 120 +++++++++ .../tegra/nvadsp/adsp_lpthread_proc.c | 232 ++++++++++++++++++ drivers/platform/tegra/nvadsp/dev.h | 17 +- drivers/platform/tegra/nvadsp/os.c | 21 ++ 6 files changed, 405 insertions(+), 1 deletion(-) create mode 100644 drivers/platform/tegra/nvadsp/adsp_lpthread.c create mode 100644 drivers/platform/tegra/nvadsp/adsp_lpthread_proc.c diff --git a/drivers/platform/tegra/nvadsp/Kconfig b/drivers/platform/tegra/nvadsp/Kconfig index 4f3fa700..0af19781 100644 --- a/drivers/platform/tegra/nvadsp/Kconfig +++ b/drivers/platform/tegra/nvadsp/Kconfig @@ -53,6 +53,17 @@ config TEGRA_ADSP_FILEIO If unsure, say N +config TEGRA_ADSP_LPTHREAD + bool "Enable ADSP usage calc by lpthread" + depends on DEBUG_FS + default n + help + Enable calculation of ADSP usage by running a low priority + thread in background whenever OS is not suspended. Can be + enable or disabled by echo to adsp_usage file. + + If unsure, say N + config TEGRA_EMC_APE_DFS bool "Enable emc dfs due to APE" default n diff --git a/drivers/platform/tegra/nvadsp/Makefile b/drivers/platform/tegra/nvadsp/Makefile index 87c8cd37..dd3173d1 100644 --- a/drivers/platform/tegra/nvadsp/Makefile +++ b/drivers/platform/tegra/nvadsp/Makefile @@ -33,3 +33,8 @@ endif ifeq ($(CONFIG_TEGRA_ADSP_FILEIO),y) nvadsp-objs += adspff.o endif + +ifeq ($(CONFIG_TEGRA_ADSP_LPTHREAD),y) +nvadsp-objs += adsp_lpthread.o +nvadsp-objs += adsp_lpthread_proc.o +endif diff --git a/drivers/platform/tegra/nvadsp/adsp_lpthread.c b/drivers/platform/tegra/nvadsp/adsp_lpthread.c new file mode 100644 index 00000000..013f086d --- /dev/null +++ b/drivers/platform/tegra/nvadsp/adsp_lpthread.c @@ -0,0 +1,120 @@ +/* + * Copyright (c) 2016-2017, NVIDIA CORPORATION. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope 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 +#include +#include + +#include +#include + +#include + +#include "dev.h" + +/* #define VERBOSE_OUTPUT_LPTHREAD */ + +struct adsp_lpthread_shared_state_t { + uint16_t mbox_id; +}; + +enum adsp_lpthread_mbx_cmd { + adsp_lpthread_cmd_resume = 0, + adsp_lpthread_cmd_pause, + adsp_lpthread_cmd_close, +}; + +static struct nvadsp_mbox mbox; +static struct adsp_lpthread_shared_state_t *adsp_lpthread; + +int adsp_lpthread_init(bool is_adsp_suspended) +{ + nvadsp_app_handle_t handle; + nvadsp_app_info_t *app_info; + int ret; + +#ifdef VERBOSE_OUTPUT_LPTHREAD + pr_info("ADSP_LPTHREAD_INIT(): %d\n", (int)is_adsp_suspended); +#endif + + handle = nvadsp_app_load("adsp_lpthread", "adsp_lpthread.elf"); + if (!handle) + return -1; + + app_info = nvadsp_app_init(handle, NULL); + if (!app_info) { + pr_info("unable to init app adsp_lpthread\n"); + return -2; + } + + ret = nvadsp_app_start(app_info); + if (ret) { + pr_info("unable to start app adsp_lpthread\n"); + return -3; + } + + adsp_lpthread = (struct adsp_lpthread_shared_state_t *)app_info->mem.shared; + ret = nvadsp_mbox_open(&mbox, &adsp_lpthread->mbox_id, "adsp_lpthread", NULL, NULL); + if (ret) { + pr_info("Failed to open mbox %d", adsp_lpthread->mbox_id); + return -4; + } + + /* Start timer is adsp is not in suspended state */ + if (!is_adsp_suspended) { +#ifdef VERBOSE_OUTPUT_LPTHREAD + pr_info("Attempting resume() from init()\n"); +#endif + ret = adsp_lpthread_resume(); + return ret; + } + + return 0; +} + +int adsp_lpthread_resume(void) +{ + int ret; +#ifdef VERBOSE_OUTPUT_LPTHREAD + pr_info("ADSP_LPTHREAD_RESUME()\n"); +#endif + ret = nvadsp_mbox_send(&mbox, adsp_lpthread_cmd_resume, NVADSP_MBOX_SMSG, 0, 0); + if (ret) + pr_info("nvadsp_mbox_send() in adsp_lpthread_resume() failed: %d, ret = %d\n", adsp_lpthread->mbox_id, ret); + return ret; +} + +int adsp_lpthread_pause(void) +{ + int ret; +#ifdef VERBOSE_OUTPUT_LPTHREAD + pr_info("ADSP_LPTHREAD_PAUSE()\n"); +#endif + ret = nvadsp_mbox_send(&mbox, adsp_lpthread_cmd_pause, NVADSP_MBOX_SMSG, 0, 0); + if (ret) + pr_info("nvadsp_mbox_send() in adsp_lpthread_pause() failed: %d, ret = %d\n", adsp_lpthread->mbox_id, ret); + return ret; +} + +int adsp_lpthread_exit(void) +{ + int ret; +#ifdef VERBOSE_OUTPUT_LPTHREAD + pr_info("ADSP_LPTHREAD_EXIT()\n"); +#endif + ret = nvadsp_mbox_send(&mbox, adsp_lpthread_cmd_close, NVADSP_MBOX_SMSG, 0, 0); + if (ret) + pr_info("nvadsp_mbox_send() in adsp_lpthread_exit() failed: %d, ret = %d\n", adsp_lpthread->mbox_id, ret); + nvadsp_mbox_close(&mbox); + return ret; +} diff --git a/drivers/platform/tegra/nvadsp/adsp_lpthread_proc.c b/drivers/platform/tegra/nvadsp/adsp_lpthread_proc.c new file mode 100644 index 00000000..467b92f2 --- /dev/null +++ b/drivers/platform/tegra/nvadsp/adsp_lpthread_proc.c @@ -0,0 +1,232 @@ +/* + * Copyright (c) 2016-2017, NVIDIA CORPORATION. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "dev.h" + +#define RW_MODE (S_IWUSR | S_IRUGO) + +/* #define VERBOSE_OUTPUT_LPTHREAD */ + +struct adsp_lpthread { + bool lpthread_initialized; + bool adsp_os_suspended; + bool lpthread_to_be_paused; + bool lpthread_to_be_resumed; + bool lpthread_to_be_closed; + bool lpthread_init_and_closed; +}; + +static struct adsp_lpthread lpthread_obj; +static struct adsp_lpthread *lpthread; + +static int adsp_usage_set(void *data, u64 val) +{ + int ret = 0; + switch (val) { + case 1: + if (lpthread->adsp_os_suspended && !lpthread->lpthread_initialized) { + if (lpthread->lpthread_init_and_closed) { + lpthread->lpthread_init_and_closed = false; + lpthread->lpthread_initialized = true; + lpthread->lpthread_to_be_resumed = true; + pr_info("App already initialized\n"); + break; + } +#ifdef VERBOSE_OUTPUT_LPTHREAD + pr_info("Starting os\n"); +#endif + if (nvadsp_os_start()) { + pr_info("Unable to restart os\n"); + break; + } + ret = adsp_lpthread_init(true); + lpthread->lpthread_initialized = true; +#ifdef VERBOSE_OUTPUT_LPTHREAD + pr_info("Initialized lpthread. Suspending OS\n"); +#endif + if (nvadsp_os_suspend()) { + pr_info("Unable to restart os\n"); + break; + } + lpthread->lpthread_to_be_resumed = true; + } else if (!lpthread->lpthread_initialized) { + if (lpthread->lpthread_init_and_closed) { + lpthread->lpthread_init_and_closed = false; + lpthread->lpthread_initialized = true; + lpthread->lpthread_to_be_resumed = true; + pr_info("App already initialized\n"); + break; + } + ret = adsp_lpthread_init(lpthread->adsp_os_suspended); + lpthread->lpthread_initialized = true; + } else if (lpthread->adsp_os_suspended) { +#ifdef VERBOSE_OUTPUT_LPTHREAD + pr_info("App to be resumed\n"); +#endif + lpthread->lpthread_to_be_resumed = true; + lpthread->lpthread_to_be_paused = false; + ret = 0; + } else { + ret = adsp_lpthread_resume(); + } + break; + case 2: + if (lpthread->adsp_os_suspended && lpthread->lpthread_initialized) { +#ifdef VERBOSE_OUTPUT_LPTHREAD + pr_info("App to be paused\n"); +#endif + lpthread->lpthread_to_be_resumed = false; + lpthread->lpthread_to_be_paused = true; + ret = 0; + } else if (lpthread->lpthread_initialized) { + ret = adsp_lpthread_pause(); + } else { + pr_info("App not initialized. echo 1 > adsp_usage to init\n"); + ret = 0; + } + break; + case 0: + if (lpthread->adsp_os_suspended && lpthread->lpthread_initialized) { +#ifdef VERBOSE_OUTPUT_LPTHREAD + pr_info("App to be closed\n"); +#endif + lpthread->lpthread_to_be_closed = true; + lpthread->lpthread_init_and_closed = true; + lpthread->lpthread_initialized = false; + lpthread->lpthread_to_be_resumed = false; + ret = 0; + } else if (lpthread->lpthread_initialized) { + ret = adsp_lpthread_exit(); + lpthread->lpthread_initialized = false; + } else { + pr_info("App not initialized. echo 1 > adsp_usage to init\n"); + ret = 0; + } + break; + default: + pr_info("Invalid input\n"); + pr_info("echo 1 > adsp_usage to init/resume\n"); + pr_info("echo 2 > adsp_usage to pause\n"); + pr_info("echo 0 > adsp_usage to stop\n"); + ret = 0; + } + return ret; +} + +static int adsp_usage_get(void *data, u64 *val) +{ + return 0; +} + +DEFINE_SIMPLE_ATTRIBUTE(adsp_usage_fops, adsp_usage_get, adsp_usage_set, "%llu\n"); + +static int lpthread_debugfs_init(struct nvadsp_drv_data *drv) +{ + int ret = -ENOMEM; + struct dentry *d, *dir; + + if (!drv->adsp_debugfs_root) + return ret; + dir = debugfs_create_dir("adsp_lpthread", drv->adsp_debugfs_root); + if (!dir) + return ret; + + d = debugfs_create_file( + "adsp_usage", RW_MODE, dir, NULL, &adsp_usage_fops); + if (!d) + return ret; + + return 0; +} + +int adsp_lpthread_debugfs_init(struct platform_device *pdev) +{ + struct nvadsp_drv_data *drv = platform_get_drvdata(pdev); + int ret = -EINVAL; + + lpthread = &lpthread_obj; + + ret = lpthread_debugfs_init(drv); + if (!ret) + pr_info(" lpthread_debugfs_init() ret = %d\n", ret); + + drv->lpthread_initialized = true; + lpthread->adsp_os_suspended = false; + + return 0; +} + +int adsp_lpthread_debugfs_exit(struct platform_device *pdev) +{ + status_t ret = 0; + struct nvadsp_drv_data *drv = platform_get_drvdata(pdev); + + if (!drv->lpthread_initialized) + ret = -EINVAL; + drv->lpthread_initialized = false; + + return ret; +} + +int adsp_lpthread_debugfs_set_suspend(bool is_suspended) +{ + lpthread->adsp_os_suspended = is_suspended; +#ifdef VERBOSE_OUTPUT_LPTHREAD + pr_info("ADSP_OS_SUSPENDED = %d\n", (int)is_suspended); +#endif + return 0; +} + +int adsp_lpthread_debugfs_callback(void) +{ + int ret = 0; + + if (lpthread->lpthread_initialized) { + if (lpthread->lpthread_to_be_resumed) { + lpthread->lpthread_to_be_resumed = false; + ret = adsp_lpthread_resume(); + } else if (lpthread->lpthread_to_be_paused) { + lpthread->lpthread_to_be_paused = false; + ret = adsp_lpthread_pause(); + } + return ret; + } + + if (lpthread->lpthread_to_be_closed) { + lpthread->lpthread_to_be_closed = false; + lpthread->lpthread_init_and_closed = false; + ret = adsp_lpthread_exit(); + return ret; + } + +#ifdef VERBOSE_OUTPUT_LPTHREAD + pr_info("to_be_paused = %d\n", (int)lpthread->lpthread_to_be_paused); + pr_info("to_be_resumed = %d\n", (int)lpthread->lpthread_to_be_resumed); + pr_info("to_be_closed = %d\n", (int)lpthread->lpthread_to_be_closed); + pr_info("init_and_closed = %d\n", (int)lpthread->lpthread_init_and_closed); + pr_info("initialized = %d\n", (int)lpthread->lpthread_initialized); +#endif + + return ret; +} diff --git a/drivers/platform/tegra/nvadsp/dev.h b/drivers/platform/tegra/nvadsp/dev.h index 831b0527..ebe84505 100644 --- a/drivers/platform/tegra/nvadsp/dev.h +++ b/drivers/platform/tegra/nvadsp/dev.h @@ -76,7 +76,6 @@ enum adsp_unit_fpga_reset { ADSP_UNIT_FPGA_RESET_END, }; - #define AMISC_REGS 0x2000 #define AMISC_ADSP_L2_REGFILEBASE 0x10 @@ -201,6 +200,10 @@ struct nvadsp_drv_data { bool adspff_init; #endif +#ifdef CONFIG_TEGRA_ADSP_LPTHREAD + bool lpthread_initialized; +#endif + wait_queue_head_t adsp_health_waitq; bool adsp_crashed; @@ -271,4 +274,16 @@ static inline int __init nvadsp_reset_init(struct platform_device *pdev) return -EINVAL; } +#ifdef CONFIG_TEGRA_ADSP_LPTHREAD +int adsp_lpthread_init(bool is_adsp_suspended); +int adsp_lpthread_resume(void); +int adsp_lpthread_pause(void); +int adsp_lpthread_exit(void); + +int adsp_lpthread_debugfs_init(struct platform_device *pdev); +int adsp_lpthread_debugfs_exit(struct platform_device *pdev); +int adsp_lpthread_debugfs_set_suspend(bool is_suspended); +int adsp_lpthread_debugfs_callback(void); +#endif + #endif /* __TEGRA_NVADSP_DEV_H */ diff --git a/drivers/platform/tegra/nvadsp/os.c b/drivers/platform/tegra/nvadsp/os.c index 99acab8f..adba72f9 100644 --- a/drivers/platform/tegra/nvadsp/os.c +++ b/drivers/platform/tegra/nvadsp/os.c @@ -1489,10 +1489,27 @@ int nvadsp_os_start(void) drv_data->adspff_init = true; } #endif + +#ifdef CONFIG_TEGRA_ADSP_LPTHREAD + if (drv_data->adsp_os_suspended == false) { + ret = adsp_lpthread_debugfs_init(priv.pdev); + if (ret) + dev_err(dev, "adsp_lpthread_debugfs_init() failed with ret = %d\n", ret); + else + dev_err(dev, "adsp_lpthread_debugfs_init() success with ret = %d\n", ret); + } +#endif + drv_data->adsp_os_suspended = false; #ifdef CONFIG_DEBUG_FS wake_up(&priv.logger.wait_queue); #endif + +#ifdef CONFIG_TEGRA_ADSP_LPTHREAD + adsp_lpthread_debugfs_set_suspend(drv_data->adsp_os_suspended); + adsp_lpthread_debugfs_callback(); +#endif + unlock: mutex_unlock(&priv.os_run_lock); end: @@ -1540,6 +1557,10 @@ static int __nvadsp_os_suspend(void) drv_data->adsp_os_suspended = true; +#ifdef CONFIG_TEGRA_ADSP_LPTHREAD + adsp_lpthread_debugfs_set_suspend(drv_data->adsp_os_suspended); +#endif + nvadsp_assert_adsp(drv_data); out: From f9f361c265cf52384c404dfd64c1a60965ec9ea4 Mon Sep 17 00:00:00 2001 From: Hariharan Sivaraman Date: Thu, 5 May 2016 12:39:31 +0530 Subject: [PATCH 034/138] tegra: nvadsp: adsp usage app changes Combining adsp_lpthread_proc.c and adsp_lpthread.c under one file adsp_lpthread.c Bug 1727014 Change-Id: I45ab5e5a49fce29f151b81706fee8e9f3baffeef Signed-off-by: Hariharan Sivaraman Reviewed-on: http://git-master/r/1141833 (cherry picked from commit 8e9632c3bba854eb91fe406b1f67a1941da95292) Signed-off-by: Hariharan Sivaraman Reviewed-on: https://git-master.nvidia.com/r/1564175 GVS: Gerrit_Virtual_Submit Reviewed-by: Ajay Nandakumar M Reviewed-by: Nitin Pai Reviewed-by: mobile promotions Tested-by: mobile promotions --- drivers/platform/tegra/nvadsp/Makefile | 1 - drivers/platform/tegra/nvadsp/adsp_lpthread.c | 207 ++++++++++++++++ .../tegra/nvadsp/adsp_lpthread_proc.c | 232 ------------------ 3 files changed, 207 insertions(+), 233 deletions(-) delete mode 100644 drivers/platform/tegra/nvadsp/adsp_lpthread_proc.c diff --git a/drivers/platform/tegra/nvadsp/Makefile b/drivers/platform/tegra/nvadsp/Makefile index dd3173d1..f40c0331 100644 --- a/drivers/platform/tegra/nvadsp/Makefile +++ b/drivers/platform/tegra/nvadsp/Makefile @@ -36,5 +36,4 @@ endif ifeq ($(CONFIG_TEGRA_ADSP_LPTHREAD),y) nvadsp-objs += adsp_lpthread.o -nvadsp-objs += adsp_lpthread_proc.o endif diff --git a/drivers/platform/tegra/nvadsp/adsp_lpthread.c b/drivers/platform/tegra/nvadsp/adsp_lpthread.c index 013f086d..d305771d 100644 --- a/drivers/platform/tegra/nvadsp/adsp_lpthread.c +++ b/drivers/platform/tegra/nvadsp/adsp_lpthread.c @@ -17,11 +17,15 @@ #include #include +#include +#include #include #include "dev.h" +#define RW_MODE (S_IWUSR | S_IRUGO) + /* #define VERBOSE_OUTPUT_LPTHREAD */ struct adsp_lpthread_shared_state_t { @@ -34,6 +38,18 @@ enum adsp_lpthread_mbx_cmd { adsp_lpthread_cmd_close, }; +struct adsp_lpthread { + bool lpthread_initialized; + bool adsp_os_suspended; + bool lpthread_to_be_paused; + bool lpthread_to_be_resumed; + bool lpthread_to_be_closed; + bool lpthread_init_and_closed; +}; + +static struct adsp_lpthread lpthread_obj; +static struct adsp_lpthread *lpthread; + static struct nvadsp_mbox mbox; static struct adsp_lpthread_shared_state_t *adsp_lpthread; @@ -118,3 +134,194 @@ int adsp_lpthread_exit(void) nvadsp_mbox_close(&mbox); return ret; } + +static int adsp_usage_set(void *data, u64 val) +{ + int ret = 0; + switch (val) { + case 1: + if (lpthread->adsp_os_suspended && !lpthread->lpthread_initialized) { + if (lpthread->lpthread_init_and_closed) { + lpthread->lpthread_init_and_closed = false; + lpthread->lpthread_initialized = true; + lpthread->lpthread_to_be_resumed = true; + pr_info("App already initialized\n"); + break; + } +#ifdef VERBOSE_OUTPUT_LPTHREAD + pr_info("Starting os\n"); +#endif + if (nvadsp_os_start()) { + pr_info("Unable to restart os\n"); + break; + } + ret = adsp_lpthread_init(true); + lpthread->lpthread_initialized = true; +#ifdef VERBOSE_OUTPUT_LPTHREAD + pr_info("Initialized lpthread. Suspending OS\n"); +#endif + if (nvadsp_os_suspend()) { + pr_info("Unable to restart os\n"); + break; + } + lpthread->lpthread_to_be_resumed = true; + } else if (!lpthread->lpthread_initialized) { + if (lpthread->lpthread_init_and_closed) { + lpthread->lpthread_init_and_closed = false; + lpthread->lpthread_initialized = true; + lpthread->lpthread_to_be_resumed = true; + pr_info("App already initialized\n"); + break; + } + ret = adsp_lpthread_init(lpthread->adsp_os_suspended); + lpthread->lpthread_initialized = true; + } else if (lpthread->adsp_os_suspended) { +#ifdef VERBOSE_OUTPUT_LPTHREAD + pr_info("App to be resumed\n"); +#endif + lpthread->lpthread_to_be_resumed = true; + lpthread->lpthread_to_be_paused = false; + ret = 0; + } else { + ret = adsp_lpthread_resume(); + } + break; + case 2: + if (lpthread->adsp_os_suspended && lpthread->lpthread_initialized) { +#ifdef VERBOSE_OUTPUT_LPTHREAD + pr_info("App to be paused\n"); +#endif + lpthread->lpthread_to_be_resumed = false; + lpthread->lpthread_to_be_paused = true; + ret = 0; + } else if (lpthread->lpthread_initialized) { + ret = adsp_lpthread_pause(); + } else { + pr_info("App not initialized. echo 1 > adsp_usage to init\n"); + ret = 0; + } + break; + case 0: + if (lpthread->adsp_os_suspended && lpthread->lpthread_initialized) { +#ifdef VERBOSE_OUTPUT_LPTHREAD + pr_info("App to be closed\n"); +#endif + lpthread->lpthread_to_be_closed = true; + lpthread->lpthread_init_and_closed = true; + lpthread->lpthread_initialized = false; + lpthread->lpthread_to_be_resumed = false; + ret = 0; + } else if (lpthread->lpthread_initialized) { + ret = adsp_lpthread_exit(); + lpthread->lpthread_initialized = false; + } else { + pr_info("App not initialized. echo 1 > adsp_usage to init\n"); + ret = 0; + } + break; + default: + pr_info("Invalid input\n"); + pr_info("echo 1 > adsp_usage to init/resume\n"); + pr_info("echo 2 > adsp_usage to pause\n"); + pr_info("echo 0 > adsp_usage to stop\n"); + ret = 0; + } + return ret; +} + +static int adsp_usage_get(void *data, u64 *val) +{ + return 0; +} + +DEFINE_SIMPLE_ATTRIBUTE(adsp_usage_fops, adsp_usage_get, adsp_usage_set, "%llu\n"); + +static int lpthread_debugfs_init(struct nvadsp_drv_data *drv) +{ + int ret = -ENOMEM; + struct dentry *d, *dir; + + if (!drv->adsp_debugfs_root) + return ret; + dir = debugfs_create_dir("adsp_lpthread", drv->adsp_debugfs_root); + if (!dir) + return ret; + + d = debugfs_create_file( + "adsp_usage", RW_MODE, dir, NULL, &adsp_usage_fops); + if (!d) + return ret; + + return 0; +} + +int adsp_lpthread_debugfs_init(struct platform_device *pdev) +{ + struct nvadsp_drv_data *drv = platform_get_drvdata(pdev); + int ret = -EINVAL; + + lpthread = &lpthread_obj; + + ret = lpthread_debugfs_init(drv); + if (!ret) + pr_info(" lpthread_debugfs_init() ret = %d\n", ret); + + drv->lpthread_initialized = true; + lpthread->adsp_os_suspended = false; + + return 0; +} + +int adsp_lpthread_debugfs_exit(struct platform_device *pdev) +{ + status_t ret = 0; + struct nvadsp_drv_data *drv = platform_get_drvdata(pdev); + + if (!drv->lpthread_initialized) + ret = -EINVAL; + drv->lpthread_initialized = false; + + return ret; +} + +int adsp_lpthread_debugfs_set_suspend(bool is_suspended) +{ + lpthread->adsp_os_suspended = is_suspended; +#ifdef VERBOSE_OUTPUT_LPTHREAD + pr_info("ADSP_OS_SUSPENDED = %d\n", (int)is_suspended); +#endif + return 0; +} + +int adsp_lpthread_debugfs_callback(void) +{ + int ret = 0; + + if (lpthread->lpthread_initialized) { + if (lpthread->lpthread_to_be_resumed) { + lpthread->lpthread_to_be_resumed = false; + ret = adsp_lpthread_resume(); + } else if (lpthread->lpthread_to_be_paused) { + lpthread->lpthread_to_be_paused = false; + ret = adsp_lpthread_pause(); + } + return ret; + } + + if (lpthread->lpthread_to_be_closed) { + lpthread->lpthread_to_be_closed = false; + lpthread->lpthread_init_and_closed = false; + ret = adsp_lpthread_exit(); + return ret; + } + +#ifdef VERBOSE_OUTPUT_LPTHREAD + pr_info("to_be_paused = %d\n", (int)lpthread->lpthread_to_be_paused); + pr_info("to_be_resumed = %d\n", (int)lpthread->lpthread_to_be_resumed); + pr_info("to_be_closed = %d\n", (int)lpthread->lpthread_to_be_closed); + pr_info("init_and_closed = %d\n", (int)lpthread->lpthread_init_and_closed); + pr_info("initialized = %d\n", (int)lpthread->lpthread_initialized); +#endif + + return ret; +} diff --git a/drivers/platform/tegra/nvadsp/adsp_lpthread_proc.c b/drivers/platform/tegra/nvadsp/adsp_lpthread_proc.c deleted file mode 100644 index 467b92f2..00000000 --- a/drivers/platform/tegra/nvadsp/adsp_lpthread_proc.c +++ /dev/null @@ -1,232 +0,0 @@ -/* - * Copyright (c) 2016-2017, NVIDIA CORPORATION. All rights reserved. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; version 2 of the License. - * - * 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 -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "dev.h" - -#define RW_MODE (S_IWUSR | S_IRUGO) - -/* #define VERBOSE_OUTPUT_LPTHREAD */ - -struct adsp_lpthread { - bool lpthread_initialized; - bool adsp_os_suspended; - bool lpthread_to_be_paused; - bool lpthread_to_be_resumed; - bool lpthread_to_be_closed; - bool lpthread_init_and_closed; -}; - -static struct adsp_lpthread lpthread_obj; -static struct adsp_lpthread *lpthread; - -static int adsp_usage_set(void *data, u64 val) -{ - int ret = 0; - switch (val) { - case 1: - if (lpthread->adsp_os_suspended && !lpthread->lpthread_initialized) { - if (lpthread->lpthread_init_and_closed) { - lpthread->lpthread_init_and_closed = false; - lpthread->lpthread_initialized = true; - lpthread->lpthread_to_be_resumed = true; - pr_info("App already initialized\n"); - break; - } -#ifdef VERBOSE_OUTPUT_LPTHREAD - pr_info("Starting os\n"); -#endif - if (nvadsp_os_start()) { - pr_info("Unable to restart os\n"); - break; - } - ret = adsp_lpthread_init(true); - lpthread->lpthread_initialized = true; -#ifdef VERBOSE_OUTPUT_LPTHREAD - pr_info("Initialized lpthread. Suspending OS\n"); -#endif - if (nvadsp_os_suspend()) { - pr_info("Unable to restart os\n"); - break; - } - lpthread->lpthread_to_be_resumed = true; - } else if (!lpthread->lpthread_initialized) { - if (lpthread->lpthread_init_and_closed) { - lpthread->lpthread_init_and_closed = false; - lpthread->lpthread_initialized = true; - lpthread->lpthread_to_be_resumed = true; - pr_info("App already initialized\n"); - break; - } - ret = adsp_lpthread_init(lpthread->adsp_os_suspended); - lpthread->lpthread_initialized = true; - } else if (lpthread->adsp_os_suspended) { -#ifdef VERBOSE_OUTPUT_LPTHREAD - pr_info("App to be resumed\n"); -#endif - lpthread->lpthread_to_be_resumed = true; - lpthread->lpthread_to_be_paused = false; - ret = 0; - } else { - ret = adsp_lpthread_resume(); - } - break; - case 2: - if (lpthread->adsp_os_suspended && lpthread->lpthread_initialized) { -#ifdef VERBOSE_OUTPUT_LPTHREAD - pr_info("App to be paused\n"); -#endif - lpthread->lpthread_to_be_resumed = false; - lpthread->lpthread_to_be_paused = true; - ret = 0; - } else if (lpthread->lpthread_initialized) { - ret = adsp_lpthread_pause(); - } else { - pr_info("App not initialized. echo 1 > adsp_usage to init\n"); - ret = 0; - } - break; - case 0: - if (lpthread->adsp_os_suspended && lpthread->lpthread_initialized) { -#ifdef VERBOSE_OUTPUT_LPTHREAD - pr_info("App to be closed\n"); -#endif - lpthread->lpthread_to_be_closed = true; - lpthread->lpthread_init_and_closed = true; - lpthread->lpthread_initialized = false; - lpthread->lpthread_to_be_resumed = false; - ret = 0; - } else if (lpthread->lpthread_initialized) { - ret = adsp_lpthread_exit(); - lpthread->lpthread_initialized = false; - } else { - pr_info("App not initialized. echo 1 > adsp_usage to init\n"); - ret = 0; - } - break; - default: - pr_info("Invalid input\n"); - pr_info("echo 1 > adsp_usage to init/resume\n"); - pr_info("echo 2 > adsp_usage to pause\n"); - pr_info("echo 0 > adsp_usage to stop\n"); - ret = 0; - } - return ret; -} - -static int adsp_usage_get(void *data, u64 *val) -{ - return 0; -} - -DEFINE_SIMPLE_ATTRIBUTE(adsp_usage_fops, adsp_usage_get, adsp_usage_set, "%llu\n"); - -static int lpthread_debugfs_init(struct nvadsp_drv_data *drv) -{ - int ret = -ENOMEM; - struct dentry *d, *dir; - - if (!drv->adsp_debugfs_root) - return ret; - dir = debugfs_create_dir("adsp_lpthread", drv->adsp_debugfs_root); - if (!dir) - return ret; - - d = debugfs_create_file( - "adsp_usage", RW_MODE, dir, NULL, &adsp_usage_fops); - if (!d) - return ret; - - return 0; -} - -int adsp_lpthread_debugfs_init(struct platform_device *pdev) -{ - struct nvadsp_drv_data *drv = platform_get_drvdata(pdev); - int ret = -EINVAL; - - lpthread = &lpthread_obj; - - ret = lpthread_debugfs_init(drv); - if (!ret) - pr_info(" lpthread_debugfs_init() ret = %d\n", ret); - - drv->lpthread_initialized = true; - lpthread->adsp_os_suspended = false; - - return 0; -} - -int adsp_lpthread_debugfs_exit(struct platform_device *pdev) -{ - status_t ret = 0; - struct nvadsp_drv_data *drv = platform_get_drvdata(pdev); - - if (!drv->lpthread_initialized) - ret = -EINVAL; - drv->lpthread_initialized = false; - - return ret; -} - -int adsp_lpthread_debugfs_set_suspend(bool is_suspended) -{ - lpthread->adsp_os_suspended = is_suspended; -#ifdef VERBOSE_OUTPUT_LPTHREAD - pr_info("ADSP_OS_SUSPENDED = %d\n", (int)is_suspended); -#endif - return 0; -} - -int adsp_lpthread_debugfs_callback(void) -{ - int ret = 0; - - if (lpthread->lpthread_initialized) { - if (lpthread->lpthread_to_be_resumed) { - lpthread->lpthread_to_be_resumed = false; - ret = adsp_lpthread_resume(); - } else if (lpthread->lpthread_to_be_paused) { - lpthread->lpthread_to_be_paused = false; - ret = adsp_lpthread_pause(); - } - return ret; - } - - if (lpthread->lpthread_to_be_closed) { - lpthread->lpthread_to_be_closed = false; - lpthread->lpthread_init_and_closed = false; - ret = adsp_lpthread_exit(); - return ret; - } - -#ifdef VERBOSE_OUTPUT_LPTHREAD - pr_info("to_be_paused = %d\n", (int)lpthread->lpthread_to_be_paused); - pr_info("to_be_resumed = %d\n", (int)lpthread->lpthread_to_be_resumed); - pr_info("to_be_closed = %d\n", (int)lpthread->lpthread_to_be_closed); - pr_info("init_and_closed = %d\n", (int)lpthread->lpthread_init_and_closed); - pr_info("initialized = %d\n", (int)lpthread->lpthread_initialized); -#endif - - return ret; -} From d593fa42f0ac8e9b8c86c38340c821a33a89ff00 Mon Sep 17 00:00:00 2001 From: Hariharan Sivaraman Date: Fri, 6 May 2016 15:22:20 +0530 Subject: [PATCH 035/138] tegra: nvadsp: disable suspend when adsp usage log Prevents adsp os from getting suspended when adsp_usage is being logged Checks whether usage app is running and skips suspend accordingly Bug 1727014 Change-Id: Iafd800d3d18d6292bee5d3376cec59dbb4557f2e Signed-off-by: Hariharan Sivaraman Reviewed-on: http://git-master/r/1142669 (cherry picked from commit 04660b149f1455f1e5e77f75aeb652a9dceae55f) Signed-off-by: Hariharan Sivaraman Reviewed-on: https://git-master.nvidia.com/r/1564184 Reviewed-by: Ajay Nandakumar M Reviewed-by: svc-mobile-coverity GVS: Gerrit_Virtual_Submit Reviewed-by: Nitin Pai Reviewed-by: mobile promotions Tested-by: mobile promotions --- drivers/platform/tegra/nvadsp/adsp_lpthread.c | 144 ++++++------------ drivers/platform/tegra/nvadsp/dev.h | 2 +- drivers/platform/tegra/nvadsp/os.c | 9 +- 3 files changed, 57 insertions(+), 98 deletions(-) diff --git a/drivers/platform/tegra/nvadsp/adsp_lpthread.c b/drivers/platform/tegra/nvadsp/adsp_lpthread.c index d305771d..eed20eb2 100644 --- a/drivers/platform/tegra/nvadsp/adsp_lpthread.c +++ b/drivers/platform/tegra/nvadsp/adsp_lpthread.c @@ -41,10 +41,9 @@ enum adsp_lpthread_mbx_cmd { struct adsp_lpthread { bool lpthread_initialized; bool adsp_os_suspended; - bool lpthread_to_be_paused; - bool lpthread_to_be_resumed; - bool lpthread_to_be_closed; - bool lpthread_init_and_closed; + bool lpthread_paused; + bool lpthread_resumed; + bool lpthread_closed; }; static struct adsp_lpthread lpthread_obj; @@ -101,6 +100,7 @@ int adsp_lpthread_init(bool is_adsp_suspended) int adsp_lpthread_resume(void) { int ret; + #ifdef VERBOSE_OUTPUT_LPTHREAD pr_info("ADSP_LPTHREAD_RESUME()\n"); #endif @@ -113,6 +113,7 @@ int adsp_lpthread_resume(void) int adsp_lpthread_pause(void) { int ret; + #ifdef VERBOSE_OUTPUT_LPTHREAD pr_info("ADSP_LPTHREAD_PAUSE()\n"); #endif @@ -125,6 +126,7 @@ int adsp_lpthread_pause(void) int adsp_lpthread_exit(void) { int ret; + #ifdef VERBOSE_OUTPUT_LPTHREAD pr_info("ADSP_LPTHREAD_EXIT()\n"); #endif @@ -140,84 +142,54 @@ static int adsp_usage_set(void *data, u64 val) int ret = 0; switch (val) { case 1: + if (lpthread->lpthread_initialized && lpthread->lpthread_resumed) { + pr_info("App already running\n"); + pr_info("echo 2 > adsp_usage to pause\n"); + pr_info("echo 0 > adsp_usage to stop\n"); + break; + } if (lpthread->adsp_os_suspended && !lpthread->lpthread_initialized) { - if (lpthread->lpthread_init_and_closed) { - lpthread->lpthread_init_and_closed = false; - lpthread->lpthread_initialized = true; - lpthread->lpthread_to_be_resumed = true; - pr_info("App already initialized\n"); - break; - } -#ifdef VERBOSE_OUTPUT_LPTHREAD - pr_info("Starting os\n"); -#endif + pr_info("Starting ADSP OS\n"); if (nvadsp_os_start()) { - pr_info("Unable to restart os\n"); - break; - } - ret = adsp_lpthread_init(true); - lpthread->lpthread_initialized = true; -#ifdef VERBOSE_OUTPUT_LPTHREAD - pr_info("Initialized lpthread. Suspending OS\n"); -#endif - if (nvadsp_os_suspend()) { - pr_info("Unable to restart os\n"); - break; - } - lpthread->lpthread_to_be_resumed = true; - } else if (!lpthread->lpthread_initialized) { - if (lpthread->lpthread_init_and_closed) { - lpthread->lpthread_init_and_closed = false; - lpthread->lpthread_initialized = true; - lpthread->lpthread_to_be_resumed = true; - pr_info("App already initialized\n"); + pr_info("Unable to start OS\n"); break; } + lpthread->adsp_os_suspended = false; ret = adsp_lpthread_init(lpthread->adsp_os_suspended); + pr_info("Initializing lpthread\n"); + lpthread->lpthread_initialized = true; + } else if (!lpthread->lpthread_initialized) { + ret = adsp_lpthread_init(lpthread->adsp_os_suspended); + pr_info("Initializing lpthread\n"); lpthread->lpthread_initialized = true; - } else if (lpthread->adsp_os_suspended) { -#ifdef VERBOSE_OUTPUT_LPTHREAD - pr_info("App to be resumed\n"); -#endif - lpthread->lpthread_to_be_resumed = true; - lpthread->lpthread_to_be_paused = false; - ret = 0; } else { ret = adsp_lpthread_resume(); + pr_info("Resuming lpthread\n"); } + lpthread->lpthread_resumed = true; + lpthread->lpthread_paused = false; + lpthread->lpthread_closed = false; break; case 2: - if (lpthread->adsp_os_suspended && lpthread->lpthread_initialized) { -#ifdef VERBOSE_OUTPUT_LPTHREAD - pr_info("App to be paused\n"); -#endif - lpthread->lpthread_to_be_resumed = false; - lpthread->lpthread_to_be_paused = true; - ret = 0; - } else if (lpthread->lpthread_initialized) { - ret = adsp_lpthread_pause(); - } else { + if (!lpthread->lpthread_initialized) { pr_info("App not initialized. echo 1 > adsp_usage to init\n"); - ret = 0; + break; } + ret = adsp_lpthread_pause(); + lpthread->lpthread_resumed = false; + lpthread->lpthread_paused = true; + lpthread->lpthread_closed = false; break; case 0: - if (lpthread->adsp_os_suspended && lpthread->lpthread_initialized) { -#ifdef VERBOSE_OUTPUT_LPTHREAD - pr_info("App to be closed\n"); -#endif - lpthread->lpthread_to_be_closed = true; - lpthread->lpthread_init_and_closed = true; - lpthread->lpthread_initialized = false; - lpthread->lpthread_to_be_resumed = false; - ret = 0; - } else if (lpthread->lpthread_initialized) { - ret = adsp_lpthread_exit(); - lpthread->lpthread_initialized = false; - } else { + if (!lpthread->lpthread_initialized) { pr_info("App not initialized. echo 1 > adsp_usage to init\n"); - ret = 0; + break; } + ret = adsp_lpthread_exit(); + lpthread->lpthread_resumed = false; + lpthread->lpthread_paused = false; + lpthread->lpthread_closed = true; + lpthread->lpthread_initialized = false; break; default: pr_info("Invalid input\n"); @@ -231,7 +203,12 @@ static int adsp_usage_set(void *data, u64 val) static int adsp_usage_get(void *data, u64 *val) { - return 0; + if (lpthread->lpthread_initialized && lpthread->lpthread_resumed) + return 1; + else if (lpthread->lpthread_initialized && lpthread->lpthread_paused) + return 2; + else + return 0; } DEFINE_SIMPLE_ATTRIBUTE(adsp_usage_fops, adsp_usage_get, adsp_usage_set, "%llu\n"); @@ -293,35 +270,10 @@ int adsp_lpthread_debugfs_set_suspend(bool is_suspended) return 0; } -int adsp_lpthread_debugfs_callback(void) +int adsp_lpthread_get_state(void) { - int ret = 0; - - if (lpthread->lpthread_initialized) { - if (lpthread->lpthread_to_be_resumed) { - lpthread->lpthread_to_be_resumed = false; - ret = adsp_lpthread_resume(); - } else if (lpthread->lpthread_to_be_paused) { - lpthread->lpthread_to_be_paused = false; - ret = adsp_lpthread_pause(); - } - return ret; - } - - if (lpthread->lpthread_to_be_closed) { - lpthread->lpthread_to_be_closed = false; - lpthread->lpthread_init_and_closed = false; - ret = adsp_lpthread_exit(); - return ret; - } - -#ifdef VERBOSE_OUTPUT_LPTHREAD - pr_info("to_be_paused = %d\n", (int)lpthread->lpthread_to_be_paused); - pr_info("to_be_resumed = %d\n", (int)lpthread->lpthread_to_be_resumed); - pr_info("to_be_closed = %d\n", (int)lpthread->lpthread_to_be_closed); - pr_info("init_and_closed = %d\n", (int)lpthread->lpthread_init_and_closed); - pr_info("initialized = %d\n", (int)lpthread->lpthread_initialized); -#endif - - return ret; + if (lpthread->lpthread_initialized && lpthread->lpthread_resumed) + return 1; + else + return 0; } diff --git a/drivers/platform/tegra/nvadsp/dev.h b/drivers/platform/tegra/nvadsp/dev.h index ebe84505..b8458280 100644 --- a/drivers/platform/tegra/nvadsp/dev.h +++ b/drivers/platform/tegra/nvadsp/dev.h @@ -279,11 +279,11 @@ int adsp_lpthread_init(bool is_adsp_suspended); int adsp_lpthread_resume(void); int adsp_lpthread_pause(void); int adsp_lpthread_exit(void); +int adsp_lpthread_get_state(void); int adsp_lpthread_debugfs_init(struct platform_device *pdev); int adsp_lpthread_debugfs_exit(struct platform_device *pdev); int adsp_lpthread_debugfs_set_suspend(bool is_suspended); -int adsp_lpthread_debugfs_callback(void); #endif #endif /* __TEGRA_NVADSP_DEV_H */ diff --git a/drivers/platform/tegra/nvadsp/os.c b/drivers/platform/tegra/nvadsp/os.c index adba72f9..f31562ed 100644 --- a/drivers/platform/tegra/nvadsp/os.c +++ b/drivers/platform/tegra/nvadsp/os.c @@ -1507,7 +1507,6 @@ int nvadsp_os_start(void) #ifdef CONFIG_TEGRA_ADSP_LPTHREAD adsp_lpthread_debugfs_set_suspend(drv_data->adsp_os_suspended); - adsp_lpthread_debugfs_callback(); #endif unlock: @@ -1685,6 +1684,14 @@ int nvadsp_os_suspend(void) goto end; } +#ifdef CONFIG_TEGRA_ADSP_LPTHREAD + if (adsp_lpthread_get_state()) { + dev_err(&priv.pdev->dev, "Adsp usage being calculated. Not suspending adsp\n"); + ret = 0; + goto end; + } +#endif + drv_data = platform_get_drvdata(priv.pdev); mutex_lock(&priv.os_run_lock); From 2e9feef7358c86ce312a0aea1bf58acd8a9e74cc Mon Sep 17 00:00:00 2001 From: Hariharan Sivaraman Date: Wed, 25 May 2016 14:43:22 +0530 Subject: [PATCH 036/138] nvadsp: os: remove adsp_lpthread kernel print Removing adsp_lpthread_debugfs_init() success print since it causes kernel warning test to fail Bug 1727014 Change-Id: I5297be9ed656b1872c5e3088f5a9e2e4a29d7a8b Signed-off-by: Hariharan Sivaraman Reviewed-on: http://git-master/r/1153287 (cherry picked from commit 8371d226c11ec602c858ff8a50d0d43e51dc0a48) Reviewed-on: https://git-master.nvidia.com/r/1558947 Reviewed-by: svc-mobile-coverity GVS: Gerrit_Virtual_Submit Reviewed-by: Ajay Nandakumar M Reviewed-by: Nitin Pai Reviewed-by: mobile promotions Tested-by: mobile promotions --- drivers/platform/tegra/nvadsp/os.c | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/drivers/platform/tegra/nvadsp/os.c b/drivers/platform/tegra/nvadsp/os.c index f31562ed..031485ba 100644 --- a/drivers/platform/tegra/nvadsp/os.c +++ b/drivers/platform/tegra/nvadsp/os.c @@ -1492,11 +1492,9 @@ int nvadsp_os_start(void) #ifdef CONFIG_TEGRA_ADSP_LPTHREAD if (drv_data->adsp_os_suspended == false) { - ret = adsp_lpthread_debugfs_init(priv.pdev); - if (ret) - dev_err(dev, "adsp_lpthread_debugfs_init() failed with ret = %d\n", ret); - else - dev_err(dev, "adsp_lpthread_debugfs_init() success with ret = %d\n", ret); + ret = adsp_lpthread_debugfs_init(priv.pdev); + if (ret) + dev_err(dev, "adsp_lpthread_debugfs_init failed ret = %d\n", ret); } #endif From a82f3515d6a5b414bc73c8ae97e7acfbe4d96759 Mon Sep 17 00:00:00 2001 From: Hariharan Sivaraman Date: Thu, 28 Apr 2016 16:17:26 +0530 Subject: [PATCH 037/138] tegra: nvadsp: adsp_lpthread patch cleanup Patch to cleanup adsp_lpthread code - used enumerators instead of macros - Added unload of app during lpthread exit - Removed sprinkled print statements - Added comments for readability Bug 1727014 Jira EMA-841 Jira EMA-854 Change-Id: Ifb3a1e60760013a52187a7ed54de27d35ff88c33 Signed-off-by: Hariharan Sivaraman Reviewed-on: https://git-master.nvidia.com/r/1564217 Reviewed-by: Automatic_Commit_Validation_User Reviewed-by: svc-mobile-coverity GVS: Gerrit_Virtual_Submit Reviewed-by: Ajay Nandakumar M Reviewed-by: Nitin Pai Reviewed-by: mobile promotions Tested-by: mobile promotions --- drivers/platform/tegra/nvadsp/adsp_lpthread.c | 162 +++++++++++------- drivers/platform/tegra/nvadsp/os.c | 2 +- 2 files changed, 99 insertions(+), 65 deletions(-) diff --git a/drivers/platform/tegra/nvadsp/adsp_lpthread.c b/drivers/platform/tegra/nvadsp/adsp_lpthread.c index eed20eb2..dfb4ba47 100644 --- a/drivers/platform/tegra/nvadsp/adsp_lpthread.c +++ b/drivers/platform/tegra/nvadsp/adsp_lpthread.c @@ -11,31 +11,34 @@ * more details. */ -#include #include #include +#include #include #include #include #include - #include #include "dev.h" #define RW_MODE (S_IWUSR | S_IRUGO) -/* #define VERBOSE_OUTPUT_LPTHREAD */ +enum adsp_lpthread_state { + ADSP_LPTHREAD_STOP, + ADSP_LPTHREAD_START, + ADSP_LPTHREAD_PAUSE, +}; struct adsp_lpthread_shared_state_t { uint16_t mbox_id; }; enum adsp_lpthread_mbx_cmd { - adsp_lpthread_cmd_resume = 0, - adsp_lpthread_cmd_pause, - adsp_lpthread_cmd_close, + ADSP_LPTHREAD_CMD_RESUME = 0, + ADSP_LPTHREAD_CMD_PAUSE, + ADSP_LPTHREAD_CMD_CLOSE, }; struct adsp_lpthread { @@ -44,6 +47,8 @@ struct adsp_lpthread { bool lpthread_paused; bool lpthread_resumed; bool lpthread_closed; + nvadsp_app_handle_t app_handle; + nvadsp_app_info_t *app_info; }; static struct adsp_lpthread lpthread_obj; @@ -52,44 +57,44 @@ static struct adsp_lpthread *lpthread; static struct nvadsp_mbox mbox; static struct adsp_lpthread_shared_state_t *adsp_lpthread; +/* Initialize adsp_lpthread app and mailbox */ int adsp_lpthread_init(bool is_adsp_suspended) { nvadsp_app_handle_t handle; nvadsp_app_info_t *app_info; int ret; -#ifdef VERBOSE_OUTPUT_LPTHREAD - pr_info("ADSP_LPTHREAD_INIT(): %d\n", (int)is_adsp_suspended); -#endif - handle = nvadsp_app_load("adsp_lpthread", "adsp_lpthread.elf"); if (!handle) return -1; app_info = nvadsp_app_init(handle, NULL); if (!app_info) { - pr_info("unable to init app adsp_lpthread\n"); - return -2; + pr_err("unable to init app adsp_lpthread\n"); + return -1; } ret = nvadsp_app_start(app_info); if (ret) { - pr_info("unable to start app adsp_lpthread\n"); - return -3; + pr_err("unable to start app adsp_lpthread\n"); + return -1; } - adsp_lpthread = (struct adsp_lpthread_shared_state_t *)app_info->mem.shared; - ret = nvadsp_mbox_open(&mbox, &adsp_lpthread->mbox_id, "adsp_lpthread", NULL, NULL); + lpthread->app_info = app_info; + lpthread->app_handle = handle; + + adsp_lpthread = + (struct adsp_lpthread_shared_state_t *)app_info->mem.shared; + ret = nvadsp_mbox_open(&mbox, &adsp_lpthread->mbox_id, + "adsp_lpthread", NULL, NULL); if (ret) { - pr_info("Failed to open mbox %d", adsp_lpthread->mbox_id); - return -4; + pr_err("Failed to open mbox %d for adsp_lpthread app", + adsp_lpthread->mbox_id); + return -1; } /* Start timer is adsp is not in suspended state */ if (!is_adsp_suspended) { -#ifdef VERBOSE_OUTPUT_LPTHREAD - pr_info("Attempting resume() from init()\n"); -#endif ret = adsp_lpthread_resume(); return ret; } @@ -101,12 +106,12 @@ int adsp_lpthread_resume(void) { int ret; -#ifdef VERBOSE_OUTPUT_LPTHREAD - pr_info("ADSP_LPTHREAD_RESUME()\n"); -#endif - ret = nvadsp_mbox_send(&mbox, adsp_lpthread_cmd_resume, NVADSP_MBOX_SMSG, 0, 0); + ret = nvadsp_mbox_send(&mbox, ADSP_LPTHREAD_CMD_RESUME, + NVADSP_MBOX_SMSG, 0, 0); if (ret) - pr_info("nvadsp_mbox_send() in adsp_lpthread_resume() failed: %d, ret = %d\n", adsp_lpthread->mbox_id, ret); + pr_err("%s: nvadsp_mbox_send() failed: %d, ret = %d\n", + __func__, adsp_lpthread->mbox_id, ret); + return ret; } @@ -114,12 +119,12 @@ int adsp_lpthread_pause(void) { int ret; -#ifdef VERBOSE_OUTPUT_LPTHREAD - pr_info("ADSP_LPTHREAD_PAUSE()\n"); -#endif - ret = nvadsp_mbox_send(&mbox, adsp_lpthread_cmd_pause, NVADSP_MBOX_SMSG, 0, 0); + ret = nvadsp_mbox_send(&mbox, ADSP_LPTHREAD_CMD_PAUSE, + NVADSP_MBOX_SMSG, 0, 0); if (ret) - pr_info("nvadsp_mbox_send() in adsp_lpthread_pause() failed: %d, ret = %d\n", adsp_lpthread->mbox_id, ret); + pr_err("%s: nvadsp_mbox_send() failed: %d, ret = %d\n", + __func__, adsp_lpthread->mbox_id, ret); + return ret; } @@ -127,31 +132,42 @@ int adsp_lpthread_exit(void) { int ret; -#ifdef VERBOSE_OUTPUT_LPTHREAD - pr_info("ADSP_LPTHREAD_EXIT()\n"); -#endif - ret = nvadsp_mbox_send(&mbox, adsp_lpthread_cmd_close, NVADSP_MBOX_SMSG, 0, 0); + ret = nvadsp_mbox_send(&mbox, ADSP_LPTHREAD_CMD_CLOSE, + NVADSP_MBOX_SMSG, 0, 0); if (ret) - pr_info("nvadsp_mbox_send() in adsp_lpthread_exit() failed: %d, ret = %d\n", adsp_lpthread->mbox_id, ret); + pr_err("%s: nvadsp_mbox_send() failed: %d, ret = %d\n", + __func__, adsp_lpthread->mbox_id, ret); + nvadsp_mbox_close(&mbox); + + nvadsp_exit_app((nvadsp_app_info_t *)lpthread->app_info, false); + + nvadsp_app_unload((const void *)lpthread->app_handle); + return ret; } static int adsp_usage_set(void *data, u64 val) { int ret = 0; + switch (val) { - case 1: - if (lpthread->lpthread_initialized && lpthread->lpthread_resumed) { - pr_info("App already running\n"); - pr_info("echo 2 > adsp_usage to pause\n"); - pr_info("echo 0 > adsp_usage to stop\n"); + + case ADSP_LPTHREAD_START: + if (lpthread->lpthread_initialized && + lpthread->lpthread_resumed) { + pr_info("ADSP Usage App already running\n"); + pr_info("echo %d > adsp_usage to pause\n", + ADSP_LPTHREAD_PAUSE); + pr_info("echo %d > adsp_usage to stop\n", + ADSP_LPTHREAD_STOP); break; } - if (lpthread->adsp_os_suspended && !lpthread->lpthread_initialized) { + if (lpthread->adsp_os_suspended && + !lpthread->lpthread_initialized) { pr_info("Starting ADSP OS\n"); if (nvadsp_os_start()) { - pr_info("Unable to start OS\n"); + pr_err("Unable to start OS\n"); break; } lpthread->adsp_os_suspended = false; @@ -170,32 +186,44 @@ static int adsp_usage_set(void *data, u64 val) lpthread->lpthread_paused = false; lpthread->lpthread_closed = false; break; - case 2: + + case ADSP_LPTHREAD_PAUSE: if (!lpthread->lpthread_initialized) { - pr_info("App not initialized. echo 1 > adsp_usage to init\n"); + pr_info("ADSP Usage App not initialized\n"); + pr_info("echo %d > adsp_usage to init\n", + ADSP_LPTHREAD_START); break; } + pr_info("Pausing lpthread\n"); ret = adsp_lpthread_pause(); lpthread->lpthread_resumed = false; lpthread->lpthread_paused = true; lpthread->lpthread_closed = false; break; - case 0: + + case ADSP_LPTHREAD_STOP: if (!lpthread->lpthread_initialized) { - pr_info("App not initialized. echo 1 > adsp_usage to init\n"); + pr_info("ADSP Usage App not initialized\n"); + pr_info("echo %d > adsp_usage to init\n", + ADSP_LPTHREAD_START); break; } + pr_info("Exiting lpthread\n"); ret = adsp_lpthread_exit(); lpthread->lpthread_resumed = false; lpthread->lpthread_paused = false; lpthread->lpthread_closed = true; lpthread->lpthread_initialized = false; break; + default: - pr_info("Invalid input\n"); - pr_info("echo 1 > adsp_usage to init/resume\n"); - pr_info("echo 2 > adsp_usage to pause\n"); - pr_info("echo 0 > adsp_usage to stop\n"); + pr_err("ADSP Usage App: Invalid input\n"); + pr_err("echo %d > adsp_usage to init/resume\n", + ADSP_LPTHREAD_START); + pr_err("echo %d > adsp_usage to pause\n", + ADSP_LPTHREAD_PAUSE); + pr_err("echo %d > adsp_usage to stop\n", + ADSP_LPTHREAD_STOP); ret = 0; } return ret; @@ -204,14 +232,16 @@ static int adsp_usage_set(void *data, u64 val) static int adsp_usage_get(void *data, u64 *val) { if (lpthread->lpthread_initialized && lpthread->lpthread_resumed) - return 1; - else if (lpthread->lpthread_initialized && lpthread->lpthread_paused) - return 2; - else - return 0; + return ADSP_LPTHREAD_START; + + if (lpthread->lpthread_initialized && lpthread->lpthread_paused) + return ADSP_LPTHREAD_PAUSE; + + return ADSP_LPTHREAD_STOP; } -DEFINE_SIMPLE_ATTRIBUTE(adsp_usage_fops, adsp_usage_get, adsp_usage_set, "%llu\n"); +DEFINE_SIMPLE_ATTRIBUTE(adsp_usage_fops, + adsp_usage_get, adsp_usage_set, "%llu\n"); static int lpthread_debugfs_init(struct nvadsp_drv_data *drv) { @@ -220,16 +250,23 @@ static int lpthread_debugfs_init(struct nvadsp_drv_data *drv) if (!drv->adsp_debugfs_root) return ret; - dir = debugfs_create_dir("adsp_lpthread", drv->adsp_debugfs_root); + + dir = debugfs_create_dir("adsp_lpthread", + drv->adsp_debugfs_root); if (!dir) return ret; d = debugfs_create_file( "adsp_usage", RW_MODE, dir, NULL, &adsp_usage_fops); if (!d) - return ret; + goto err; return 0; + +err: + debugfs_remove_recursive(dir); + pr_err("unable to create adsp lpthread debug file\n"); + return -ENOMEM; } int adsp_lpthread_debugfs_init(struct platform_device *pdev) @@ -240,8 +277,8 @@ int adsp_lpthread_debugfs_init(struct platform_device *pdev) lpthread = &lpthread_obj; ret = lpthread_debugfs_init(drv); - if (!ret) - pr_info(" lpthread_debugfs_init() ret = %d\n", ret); + if (ret) + pr_err("lpthread_debugfs_init() ret = %d\n", ret); drv->lpthread_initialized = true; lpthread->adsp_os_suspended = false; @@ -264,9 +301,6 @@ int adsp_lpthread_debugfs_exit(struct platform_device *pdev) int adsp_lpthread_debugfs_set_suspend(bool is_suspended) { lpthread->adsp_os_suspended = is_suspended; -#ifdef VERBOSE_OUTPUT_LPTHREAD - pr_info("ADSP_OS_SUSPENDED = %d\n", (int)is_suspended); -#endif return 0; } diff --git a/drivers/platform/tegra/nvadsp/os.c b/drivers/platform/tegra/nvadsp/os.c index 031485ba..5e8f6b64 100644 --- a/drivers/platform/tegra/nvadsp/os.c +++ b/drivers/platform/tegra/nvadsp/os.c @@ -1491,7 +1491,7 @@ int nvadsp_os_start(void) #endif #ifdef CONFIG_TEGRA_ADSP_LPTHREAD - if (drv_data->adsp_os_suspended == false) { + if (!drv_data->lpthread_initialized) { ret = adsp_lpthread_debugfs_init(priv.pdev); if (ret) dev_err(dev, "adsp_lpthread_debugfs_init failed ret = %d\n", ret); From d25d71e53f4e17218169765819ea60b14a93297d Mon Sep 17 00:00:00 2001 From: Prateek Patel Date: Tue, 14 Nov 2017 04:55:15 -0800 Subject: [PATCH 038/138] platform: tegra: adsp: use bwmgr to set emc freq Shared clocks on 4.9 have been deprecated. This has been replaced by tegra bandwidth managers. bug 200359628 Change-Id: Ib23ad57d36c501c16ade33d27d846f0d24217b34 Signed-off-by: Prateek Patel Reviewed-on: https://git-master.nvidia.com/r/1597879 Reviewed-by: Automatic_Commit_Validation_User Reviewed-by: svc-mobile-coverity Reviewed-by: Bharat Nihalani GVS: Gerrit_Virtual_Submit Reviewed-by: Sachin Nikam Reviewed-by: mobile promotions Tested-by: mobile promotions --- drivers/platform/tegra/nvadsp/adsp_dfs.c | 147 +++-------------------- drivers/platform/tegra/nvadsp/dev-t18x.c | 18 --- drivers/platform/tegra/nvadsp/dev.c | 27 +++-- drivers/platform/tegra/nvadsp/dev.h | 1 - drivers/platform/tegra/nvadsp/os.c | 40 +++++- 5 files changed, 75 insertions(+), 158 deletions(-) diff --git a/drivers/platform/tegra/nvadsp/adsp_dfs.c b/drivers/platform/tegra/nvadsp/adsp_dfs.c index a12dc4f6..09d23c9b 100644 --- a/drivers/platform/tegra/nvadsp/adsp_dfs.c +++ b/drivers/platform/tegra/nvadsp/adsp_dfs.c @@ -100,7 +100,6 @@ struct adsp_dfs_policy { struct clk *adsp_clk; struct clk *aclk_clk; struct clk *adsp_cpu_abus_clk; - struct notifier_block rate_change_nb; struct nvadsp_mbox mbox; #ifdef CONFIG_DEBUG_FS @@ -126,7 +125,6 @@ struct adsp_freq_stats { static struct adsp_dfs_policy *policy; static struct adsp_freq_stats freq_stats; static struct device *device; -static struct clk *ape_emc_clk; static DEFINE_MUTEX(policy_mutex); @@ -265,37 +263,9 @@ static void adspfreq_stats_update(void) freq_stats.last_time = cur_time; } -/* adsp clock rate change notifier callback */ -static int adsp_dfs_rc_callback( - struct notifier_block *nb, unsigned long rate, void *v) -{ - unsigned long freq = rate / 1000; - int old_index, new_index = 0; - - /* update states */ - adspfreq_stats_update(); - - old_index = freq_stats.last_index; - adsp_get_target_freq(rate, &new_index); - if (old_index != new_index) - freq_stats.last_index = new_index; - - if (policy->ovr_freq && freq == policy->ovr_freq) { - /* Re-init ACTMON when user requested override freq is met */ - actmon_rate_change(freq, true); - policy->ovr_freq = 0; - } else - actmon_rate_change(freq, false); - - return NOTIFY_OK; -}; - static struct adsp_dfs_policy dfs_policy = { .enable = 1, .clk_name = "adsp_cpu", - .rate_change_nb = { - .notifier_call = adsp_dfs_rc_callback, - }, }; static int adsp_update_freq_handshake(unsigned long tfreq_hz, int index) @@ -396,16 +366,12 @@ static unsigned long update_freq(unsigned long freq_khz) efreq = adsp_to_emc_freq(tfreq_hz / 1000); - if (IS_ENABLED(CONFIG_COMMON_CLK)) { - tegra_bwmgr_set_emc(drv->bwmgr, efreq * 1000, - TEGRA_BWMGR_SET_EMC_FLOOR); - } else { - ret = clk_set_rate(ape_emc_clk, efreq * 1000); - if (ret) { - dev_err(device, "failed to set ape.emc clk:%d\n", ret); - policy->update_freq_flag = false; - goto err_out; - } + ret = tegra_bwmgr_set_emc(drv->bwmgr, efreq * 1000, + TEGRA_BWMGR_SET_EMC_FLOOR); + if (ret) { + dev_err(device, "failed to set emc freq rate:%d\n", ret); + policy->update_freq_flag = false; + goto err_out; } /* @@ -439,18 +405,14 @@ err_out: } efreq = adsp_to_emc_freq(old_freq_khz); - if (IS_ENABLED(CONFIG_COMMON_CLK)) { - tegra_bwmgr_set_emc(drv->bwmgr, efreq * 1000, - TEGRA_BWMGR_SET_EMC_FLOOR); - } else { - ret = clk_set_rate(ape_emc_clk, efreq * 1000); - if (ret) { - dev_err(device, - "failed to set ape.emc clk:%d\n", ret); - policy->update_freq_flag = false; - } - } + ret = tegra_bwmgr_set_emc(drv->bwmgr, efreq * 1000, + TEGRA_BWMGR_SET_EMC_FLOOR); + if (ret) { + dev_err(device, "failed to set emc freq rate:%d\n", + ret); + policy->update_freq_flag = false; + } tfreq_hz = old_freq_khz * 1000; } return tfreq_hz / 1000; @@ -853,29 +815,6 @@ int adsp_dfs_core_init(struct platform_device *pdev) if (ret) goto end; - if (IS_ENABLED(CONFIG_COMMON_CLK)) { - drv->bwmgr = tegra_bwmgr_register(TEGRA_BWMGR_CLIENT_APE_ADSP); - if (IS_ERR_OR_NULL(drv->bwmgr)) { - dev_err(&pdev->dev, "unable to register bwmgr\n"); - ret = PTR_ERR(drv->bwmgr); - goto end; - } - } else { - /* Change emc freq as per the adsp to emc lookup table */ - ape_emc_clk = clk_get_sys("ape", "emc"); - if (IS_ERR_OR_NULL(ape_emc_clk)) { - dev_err(device, "unable to find ape.emc clock\n"); - ret = PTR_ERR(ape_emc_clk); - goto end; - } - - ret = clk_prepare_enable(ape_emc_clk); - if (ret) { - dev_err(device, "unable to enable ape.emc clock\n"); - goto end; - } - } - policy->max = policy->cpu_max = drv->adsp_freq; /* adsp_freq in KHz */ policy->min = policy->cpu_min = adsp_cpu_freq_table[0] / 1000; @@ -884,15 +823,11 @@ int adsp_dfs_core_init(struct platform_device *pdev) efreq = adsp_to_emc_freq(policy->cur); - if (IS_ENABLED(CONFIG_COMMON_CLK)) { - tegra_bwmgr_set_emc(drv->bwmgr, efreq * 1000, - TEGRA_BWMGR_SET_EMC_FLOOR); - } else { - ret = clk_set_rate(ape_emc_clk, efreq * 1000); - if (ret) { - dev_err(device, "failed to set ape.emc clk:%d\n", ret); - goto end; - } + ret = tegra_bwmgr_set_emc(drv->bwmgr, efreq * 1000, + TEGRA_BWMGR_SET_EMC_FLOOR); + if (ret) { + dev_err(device, "failed to set emc freq rate:%d\n", ret); + goto end; } adsp_get_target_freq(policy->cur * 1000, &freq_stats.last_index); @@ -907,30 +842,6 @@ int adsp_dfs_core_init(struct platform_device *pdev) goto end; } -#if !defined(CONFIG_COMMON_CLK) - if (policy->rate_change_nb.notifier_call) { - /* - * "adsp_cpu" clk is a shared user of parent adsp_cpu_bus clk; - * rate change notification should come from bus clock itself. - */ - struct clk *p = clk_get_parent(policy->adsp_clk); - if (!p) { - dev_err(&pdev->dev, "Failed to find adsp cpu parent clock\n"); - ret = -EINVAL; - goto end; - } - - ret = tegra_register_clk_rate_notifier(p, - &policy->rate_change_nb); - if (ret) { - dev_err(&pdev->dev, "rate change notifier err: %s\n", - policy->clk_name); - nvadsp_mbox_close(&policy->mbox); - goto end; - } - } -#endif - #ifdef CONFIG_DEBUG_FS adsp_dfs_debugfs_init(pdev); #endif @@ -942,15 +853,6 @@ end: adsp_clk_put(policy); - if (IS_ENABLED(CONFIG_COMMON_CLK) && drv->bwmgr) { - tegra_bwmgr_set_emc(drv->bwmgr, 0, - TEGRA_BWMGR_SET_EMC_FLOOR); - tegra_bwmgr_unregister(drv->bwmgr); - } else if (ape_emc_clk) { - clk_disable_unprepare(ape_emc_clk); - clk_put(ape_emc_clk); - } - return ret; } @@ -968,21 +870,8 @@ int adsp_dfs_core_exit(struct platform_device *pdev) dev_info(&pdev->dev, "adsp dfs exit failed: mbox close error. ret:%d\n", ret); -#if !defined(CONFIG_COMMON_CLK) - tegra_unregister_clk_rate_notifier(clk_get_parent(policy->adsp_clk), - &policy->rate_change_nb); -#endif adsp_clk_put(policy); - if (IS_ENABLED(CONFIG_COMMON_CLK) && drv->bwmgr) { - tegra_bwmgr_set_emc(drv->bwmgr, 0, - TEGRA_BWMGR_SET_EMC_FLOOR); - tegra_bwmgr_unregister(drv->bwmgr); - } else if (ape_emc_clk) { - clk_disable_unprepare(ape_emc_clk); - clk_put(ape_emc_clk); - } - drv->dfs_initialized = false; dev_dbg(&pdev->dev, "adsp dfs has exited ....\n"); diff --git a/drivers/platform/tegra/nvadsp/dev-t18x.c b/drivers/platform/tegra/nvadsp/dev-t18x.c index 90eefef6..8aa8e744 100644 --- a/drivers/platform/tegra/nvadsp/dev-t18x.c +++ b/drivers/platform/tegra/nvadsp/dev-t18x.c @@ -58,11 +58,6 @@ static int nvadsp_t18x_clocks_disable(struct platform_device *pdev) drv_data->apb2ape_clk = NULL; } - if (drv_data->ape_emc_clk) { - clk_disable_unprepare(drv_data->ape_emc_clk); - dev_dbg(dev, "ape.emc clock disabled\n"); - drv_data->ape_emc_clk = NULL; - } return 0; } @@ -122,19 +117,6 @@ static int nvadsp_t18x_clocks_enable(struct platform_device *pdev) } dev_dbg(dev, "adsp neon clock enabled\n"); - drv_data->ape_emc_clk = devm_clk_get(dev, "adsp.emc"); - if (IS_ERR_OR_NULL(drv_data->ape_emc_clk)) { - dev_err(dev, "unable to find adsp.emc clock\n"); - ret = PTR_ERR(drv_data->ape_emc_clk); - goto end; - } - ret = clk_prepare_enable(drv_data->ape_emc_clk); - if (ret) { - dev_err(dev, "unable to enable adsp.emc clock\n"); - goto end; - } - dev_dbg(dev, "ape.emc is enabled\n"); - drv_data->apb2ape_clk = devm_clk_get(dev, "adsp.apb2ape"); if (IS_ERR_OR_NULL(drv_data->apb2ape_clk)) { dev_err(dev, "unable to find adsp.apb2ape clk\n"); diff --git a/drivers/platform/tegra/nvadsp/dev.c b/drivers/platform/tegra/nvadsp/dev.c index 621618ac..03cb86d4 100644 --- a/drivers/platform/tegra/nvadsp/dev.c +++ b/drivers/platform/tegra/nvadsp/dev.c @@ -330,17 +330,12 @@ static int __init nvadsp_probe(struct platform_device *pdev) if (ret) goto err; -#ifdef CONFIG_TEGRA_EMC_APE_DFS - ret = emc_dfs_init(pdev); - if (ret) - goto err; -#endif - #ifdef CONFIG_TEGRA_ADSP_ACTMON ret = ape_actmon_probe(pdev); if (ret) goto err; #endif + ret = nvadsp_os_probe(pdev); if (ret) goto err; @@ -360,6 +355,11 @@ static int __init nvadsp_probe(struct platform_device *pdev) 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); @@ -372,9 +372,18 @@ out: static int nvadsp_remove(struct platform_device *pdev) { -#ifdef CONFIG_TEGRA_EMC_APE_DFS - emc_dfs_exit(); -#endif + 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); diff --git a/drivers/platform/tegra/nvadsp/dev.h b/drivers/platform/tegra/nvadsp/dev.h index b8458280..91d64749 100644 --- a/drivers/platform/tegra/nvadsp/dev.h +++ b/drivers/platform/tegra/nvadsp/dev.h @@ -162,7 +162,6 @@ struct nvadsp_drv_data { struct clk *aclk_clk; struct clk *adsp_cpu_abus_clk; struct clk *adsp_neon_clk; - struct clk *ape_emc_clk; struct clk *uartape_clk; struct clk *ahub_clk; unsigned long adsp_freq; /* in KHz*/ diff --git a/drivers/platform/tegra/nvadsp/os.c b/drivers/platform/tegra/nvadsp/os.c index 5e8f6b64..99c088b8 100644 --- a/drivers/platform/tegra/nvadsp/os.c +++ b/drivers/platform/tegra/nvadsp/os.c @@ -770,6 +770,40 @@ u32 adsp_to_emc_freq(u32 adspfreq) return 0; /* emc min */ } +static int nvadsp_set_ape_emc_freq(struct nvadsp_drv_data *drv_data) +{ + unsigned long ape_emc_freq; + struct device *dev = &priv.pdev->dev; + int ret; + +#ifdef CONFIG_TEGRA_ADSP_DFS + /* pass adsp freq in KHz. adsp_emc_freq in Hz */ + ape_emc_freq = adsp_to_emc_freq(drv_data->adsp_freq / 1000) * 1000; +#else + ape_emc_freq = drv_data->ape_emc_freq * 1000; /* in Hz */ +#endif + dev_dbg(dev, "requested adsp cpu freq %luKHz", + drv_data->adsp_freq / 1000); + dev_dbg(dev, "emc freq %luHz\n", ape_emc_freq / 1000); + + /* + * ape_emc_freq is not required to set if adsp_freq + * is lesser than 204.8 MHz + */ + + if (!ape_emc_freq) + return 0; + + ret = tegra_bwmgr_set_emc(drv_data->bwmgr, ape_emc_freq * 1000, + TEGRA_BWMGR_SET_EMC_FLOOR); + if (ret) + dev_err(dev, "failed to set emc freq rate:%d\n", ret); + dev_dbg(dev, "ape.emc freq %luKHz\n", + tegra_bwmgr_get_emc_rate() / 1000); + + return ret; +} + static int nvadsp_set_ape_freq(struct nvadsp_drv_data *drv_data) { unsigned long ape_freq = drv_data->ape_freq * 1000; /* in Hz*/ @@ -983,7 +1017,11 @@ static int nvadsp_set_boot_freqs(struct nvadsp_drv_data *drv_data) if (ret) goto end; } - + if (drv_data->bwmgr) { + ret = nvadsp_set_ape_emc_freq(drv_data); + if (ret) + goto end; + } end: return ret; } From 765e2695ddd811ffaac3991c1686fe992597f64a Mon Sep 17 00:00:00 2001 From: Prateek Patel Date: Wed, 29 Nov 2017 23:51:13 -0800 Subject: [PATCH 039/138] platform: tegra: adsp: remove arbitrated semaphore removing ADSP arbitrated semaphores since they are not part of t18x and not in use in t21x. bug 200326188 Change-Id: I52ef7e47c98ae0c5b9a95eed4b10161de2aff5dc Signed-off-by: Prateek Patel Reviewed-on: https://git-master.nvidia.com/r/1607937 GVS: Gerrit_Virtual_Submit Reviewed-by: Sachin Nikam Reviewed-by: mobile promotions Tested-by: mobile promotions --- drivers/platform/tegra/nvadsp/Makefile | 2 +- .../platform/tegra/nvadsp/nvadsp_arb_sema.c | 39 ------------------- 2 files changed, 1 insertion(+), 40 deletions(-) delete mode 100644 drivers/platform/tegra/nvadsp/nvadsp_arb_sema.c diff --git a/drivers/platform/tegra/nvadsp/Makefile b/drivers/platform/tegra/nvadsp/Makefile index f40c0331..5cd35bda 100644 --- a/drivers/platform/tegra/nvadsp/Makefile +++ b/drivers/platform/tegra/nvadsp/Makefile @@ -4,7 +4,7 @@ ccflags-y += -Werror obj-y := nvadsp.o nvadsp-objs += dev.o os.o app.o app_loader_linker.o\ amc.o nvadsp_dram.o \ - nvadsp_shared_sema.o nvadsp_arb_sema.o \ + nvadsp_shared_sema.o \ hwmailbox.o mailbox.o msgq.o \ mem_manager.o aram_manager.o dram_app_mem_manager.o \ dev-t21x.o os-t21x.o dev-t18x.o os-t18x.o acast.o diff --git a/drivers/platform/tegra/nvadsp/nvadsp_arb_sema.c b/drivers/platform/tegra/nvadsp/nvadsp_arb_sema.c deleted file mode 100644 index 13c41cbe..00000000 --- a/drivers/platform/tegra/nvadsp/nvadsp_arb_sema.c +++ /dev/null @@ -1,39 +0,0 @@ -/* - * nvadsp_arb_sema.c - * - * ADSP Arbitrated Semaphores - * - * Copyright (C) 2014 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 - -nvadsp_arb_sema_t *nvadsp_arb_sema_init(uint8_t nvadsp_arb_sema_id) -{ - return NULL; -} - -status_t nvadsp_arb_sema_destroy(nvadsp_arb_sema_t *sema) -{ - return -ENOENT; -} - -status_t nvadsp_arb_sema_acquire(nvadsp_arb_sema_t *sema) -{ - return -ENOENT; -} - -status_t nvadsp_arb_sema_release(nvadsp_arb_sema_t *sema) -{ - return -ENOENT; -} From 68dd548b9e55d8af968c5e5237735ac43423f960 Mon Sep 17 00:00:00 2001 From: Prateek Patel Date: Fri, 1 Dec 2017 00:40:54 -0800 Subject: [PATCH 040/138] platform: tegra: adsp: remove nvadsp_dram.c removing dummy functionality of DRAM sharing with ADSP, which are not in use bug 200326188 Change-Id: I613a65aa39c3bcd548ca001d29e7112798b438d6 Signed-off-by: Prateek Patel Reviewed-on: https://git-master.nvidia.com/r/1608868 Reviewed-by: Sachin Nikam GVS: Gerrit_Virtual_Submit Reviewed-by: mobile promotions Tested-by: mobile promotions --- drivers/platform/tegra/nvadsp/Makefile | 3 +- drivers/platform/tegra/nvadsp/nvadsp_dram.c | 67 --------------------- 2 files changed, 1 insertion(+), 69 deletions(-) delete mode 100644 drivers/platform/tegra/nvadsp/nvadsp_dram.c diff --git a/drivers/platform/tegra/nvadsp/Makefile b/drivers/platform/tegra/nvadsp/Makefile index 5cd35bda..fec9a585 100644 --- a/drivers/platform/tegra/nvadsp/Makefile +++ b/drivers/platform/tegra/nvadsp/Makefile @@ -3,8 +3,7 @@ ccflags-y += -Werror obj-y := nvadsp.o nvadsp-objs += dev.o os.o app.o app_loader_linker.o\ - amc.o nvadsp_dram.o \ - nvadsp_shared_sema.o \ + amc.o nvadsp_shared_sema.o \ hwmailbox.o mailbox.o msgq.o \ mem_manager.o aram_manager.o dram_app_mem_manager.o \ dev-t21x.o os-t21x.o dev-t18x.o os-t18x.o acast.o diff --git a/drivers/platform/tegra/nvadsp/nvadsp_dram.c b/drivers/platform/tegra/nvadsp/nvadsp_dram.c deleted file mode 100644 index beab4e8f..00000000 --- a/drivers/platform/tegra/nvadsp/nvadsp_dram.c +++ /dev/null @@ -1,67 +0,0 @@ -/* - * nvadsp_dram.c - * - * DRAM Sharing with ADSP - * - * Copyright (C) 2014 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 - -nvadsp_iova_addr_t -nvadsp_dram_map_single(struct device *nvadsp_dev, - void *cpu_addr, size_t size, - nvadsp_data_direction_t direction) -{ - return DMA_ERROR_CODE; -} - -void -nvadsp_dram_unmap_single(struct device *nvadsp_dev, - nvadsp_iova_addr_t iova_addr, size_t size, - nvadsp_data_direction_t direction) -{ - return; -} - -nvadsp_iova_addr_t -nvadsp_dram_map_page(struct device *nvadsp_dev, - struct page *page, unsigned long offset, size_t size, - nvadsp_data_direction_t direction) -{ - return DMA_ERROR_CODE; -} - -void -nvadsp_dram_unmap_page(struct device *nvadsp_dev, - nvadsp_iova_addr_t iova_addr, size_t size, - nvadsp_data_direction_t direction) -{ - return; -} - -void -nvadsp_dram_sync_single_for_cpu(struct device *nvadsp_dev, - nvadsp_iova_addr_t iova_addr, size_t size, - nvadsp_data_direction_t direction) -{ - return; -} - -void -nvadsp_dram_sync_single_for_device(struct device *nvadsp_dev, - nvadsp_iova_addr_t iova_addr, size_t size, - nvadsp_data_direction_t direction) -{ - return; -} From 9de7361de1ef95566a242bd76e5c7e8433812d71 Mon Sep 17 00:00:00 2001 From: Prateek Patel Date: Thu, 30 Nov 2017 04:27:03 -0800 Subject: [PATCH 041/138] platform: tegra: adsp: share exception data adding ability for ADSP to share data when an exception occurs on the ADSP. The data is shared through the shared memory across the host. adding shared exception data prints on host side for debugging. bug 1868578 Change-Id: I6645e8ae6ab73e5c9df8374f583dacbed4615536 Signed-off-by: Prateek Patel Reviewed-on: https://git-master.nvidia.com/r/1611260 Reviewed-by: Ajay Nandakumar M Reviewed-by: Automatic_Commit_Validation_User Reviewed-by: Bharat Nihalani --- .../tegra/nvadsp/adsp_shared_struct.h | 29 ++++++++ drivers/platform/tegra/nvadsp/os.c | 68 +++++++++++++++++++ drivers/platform/tegra/nvadsp/os.h | 11 +++ 3 files changed, 108 insertions(+) diff --git a/drivers/platform/tegra/nvadsp/adsp_shared_struct.h b/drivers/platform/tegra/nvadsp/adsp_shared_struct.h index db0bdf15..bbfaef80 100644 --- a/drivers/platform/tegra/nvadsp/adsp_shared_struct.h +++ b/drivers/platform/tegra/nvadsp/adsp_shared_struct.h @@ -129,6 +129,34 @@ struct nvadsp_os_args { char reserved[128]; } __packed; +/* ARM MODE REGS */ +struct arm_mode_regs_shared { + uint32_t fiq_r13, fiq_r14; + uint32_t irq_r13, irq_r14; + uint32_t svc_r13, svc_r14; + uint32_t abt_r13, abt_r14; + uint32_t und_r13, und_r14; + uint32_t sys_r13, sys_r14; +} __packed; + +/* ARM FAULT FRAME */ +struct arm_fault_frame_shared { + uint32_t spsr; + uint32_t usp; + uint32_t ulr; + uint32_t r[13]; + uint32_t pc; +} __packed; + +/* ADSP ARM EXCEPTION CONTEXT */ +struct nvadsp_exception_context { + struct arm_fault_frame_shared frame; + struct arm_mode_regs_shared regs; + uint32_t stack_addr; + uint32_t stack_dump[32]; + uint32_t exception_reason; +} __packed; + /* ADSP OS info/status. Keep in sync with firmware. */ #define MAX_OS_VERSION_BUF 32 struct nvadsp_os_info { @@ -141,6 +169,7 @@ struct nvadsp_shared_mem { struct nvadsp_app_shared_msg_pool app_shared_msg_pool; struct nvadsp_os_args os_args; struct nvadsp_os_info os_info; + struct nvadsp_exception_context exception_context; } __packed; diff --git a/drivers/platform/tegra/nvadsp/os.c b/drivers/platform/tegra/nvadsp/os.c index 99c088b8..b51bae0a 100644 --- a/drivers/platform/tegra/nvadsp/os.c +++ b/drivers/platform/tegra/nvadsp/os.c @@ -1176,6 +1176,72 @@ static void print_agic_irq_states(void) } } +static void print_arm_mode_regs(void) +{ + struct nvadsp_exception_context *excep_context; + struct arm_fault_frame_shared *shared_frame; + struct arm_mode_regs_shared *shared_regs; + struct nvadsp_shared_mem *shared_mem; + struct device *dev = &priv.pdev->dev; + struct nvadsp_drv_data *drv_data; + + drv_data = platform_get_drvdata(priv.pdev); + shared_mem = drv_data->shared_adsp_os_data; + excep_context = &shared_mem->exception_context; + shared_frame = &excep_context->frame; + shared_regs = &excep_context->regs; + + dev_err(dev, "dumping arm mode register data...\n"); + dev_err(dev, "%c fiq r13 0x%08x r14 0x%08x\n", + ((shared_frame->spsr & MODE_MASK) == MODE_FIQ) ? '*' : ' ', + shared_regs->fiq_r13, shared_regs->fiq_r14); + dev_err(dev, "%c irq r13 0x%08x r14 0x%08x\n", + ((shared_frame->spsr & MODE_MASK) == MODE_IRQ) ? '*' : ' ', + shared_regs->irq_r13, shared_regs->irq_r14); + dev_err(dev, "%c svc r13 0x%08x r14 0x%08x\n", + ((shared_frame->spsr & MODE_MASK) == MODE_SVC) ? '*' : ' ', + shared_regs->svc_r13, shared_regs->svc_r14); + dev_err(dev, "%c und r13 0x%08x r14 0x%08x\n", + ((shared_frame->spsr & MODE_MASK) == MODE_UND) ? '*' : ' ', + shared_regs->und_r13, shared_regs->und_r14); + dev_err(dev, "%c sys r13 0x%08x r14 0x%08x\n", + ((shared_frame->spsr & MODE_MASK) == MODE_SYS) ? '*' : ' ', + shared_regs->sys_r13, shared_regs->sys_r14); + dev_err(dev, "%c abt r13 0x%08x r14 0x%08x\n", + ((shared_frame->spsr & MODE_MASK) == MODE_ABT) ? '*' : ' ', + shared_regs->abt_r13, shared_regs->abt_r14); +} + +static void print_arm_fault_frame(void) +{ + struct nvadsp_exception_context *excep_context; + struct arm_fault_frame_shared *shared_frame; + struct nvadsp_shared_mem *shared_mem; + struct device *dev = &priv.pdev->dev; + struct nvadsp_drv_data *drv_data; + + drv_data = platform_get_drvdata(priv.pdev); + shared_mem = drv_data->shared_adsp_os_data; + excep_context = &shared_mem->exception_context; + shared_frame = &excep_context->frame; + + dev_err(dev, "dumping fault frame...\n"); + dev_err(dev, "r0 0x%08x r1 0x%08x r2 0x%08x r3 0x%08x\n", + shared_frame->r[0], shared_frame->r[1], shared_frame->r[2], + shared_frame->r[3]); + dev_err(dev, "r4 0x%08x r5 0x%08x r6 0x%08x r7 0x%08x\n", + shared_frame->r[4], shared_frame->r[5], shared_frame->r[6], + shared_frame->r[7]); + dev_err(dev, "r8 0x%08x r9 0x%08x r10 0x%08x r11 0x%08x\n", + shared_frame->r[8], shared_frame->r[9], shared_frame->r[10], + shared_frame->r[11]); + dev_err(dev, "r12 0x%08x usp 0x%08x ulr 0x%08x pc 0x%08x\n", + shared_frame->r[12], shared_frame->usp, shared_frame->ulr, + shared_frame->pc); + dev_err(dev, "spsr 0x%08x\n", shared_frame->spsr); + +} + static void dump_thread_name(struct platform_device *pdev, u32 val) { dev_info(&pdev->dev, "%s: adsp current thread: %c%c%c%c\n", @@ -1378,6 +1444,8 @@ void dump_adsp_sys(void) dump_adsp_logs(); dump_mailbox_regs(); + print_arm_fault_frame(); + print_arm_mode_regs(); get_adsp_state(); if (nvadsp_tegra_adma_dump_ch_reg) (*nvadsp_tegra_adma_dump_ch_reg)(); diff --git a/drivers/platform/tegra/nvadsp/os.h b/drivers/platform/tegra/nvadsp/os.h index 0b84f8cf..1878955a 100644 --- a/drivers/platform/tegra/nvadsp/os.h +++ b/drivers/platform/tegra/nvadsp/os.h @@ -54,6 +54,17 @@ #define MIN_ADSP_FREQ 38400000lu /* in Hz */ +/* macros used to find the current mode of ADSP */ +#define MODE_MASK 0x1f +#define MODE_USR 0x10 +#define MODE_FIQ 0x11 +#define MODE_IRQ 0x12 +#define MODE_SVC 0x13 +#define MODE_MON 0x16 +#define MODE_ABT 0x17 +#define MODE_UND 0x1b +#define MODE_SYS 0x1f + enum adsp_os_cmd { ADSP_OS_BOOT_COMPLETE, ADSP_OS_SUSPEND, From b3ff5cc42305d20af3d9e4ffa31b2b59c769b780 Mon Sep 17 00:00:00 2001 From: Ian Chang Date: Tue, 5 Dec 2017 10:16:15 +0800 Subject: [PATCH 042/138] adsp_dfs: define adspfreq_stats_update conditionally Define function adspfreq_stats_update only if CONFIG_DEBUG_FS is enabled. Otherwise, this function is unused, which triggers a warning and may be treated as an error. Change-Id: Ia0d6d004dc74ce942db7579810297c469d8ff44f Signed-off-by: Ian Chang Reviewed-on: https://git-master.nvidia.com/r/1610762 (cherry picked from commit ed13d72d6b99b93095600aad0e02aae6eaf44e8c) Reviewed-on: https://git-master.nvidia.com/r/1626906 Reviewed-by: svc-mobile-coverity GVS: Gerrit_Virtual_Submit Reviewed-by: Sachin Nikam Reviewed-by: mobile promotions Tested-by: mobile promotions --- drivers/platform/tegra/nvadsp/adsp_dfs.c | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/drivers/platform/tegra/nvadsp/adsp_dfs.c b/drivers/platform/tegra/nvadsp/adsp_dfs.c index 09d23c9b..85b240bf 100644 --- a/drivers/platform/tegra/nvadsp/adsp_dfs.c +++ b/drivers/platform/tegra/nvadsp/adsp_dfs.c @@ -253,16 +253,6 @@ static unsigned long adsp_get_target_freq(unsigned long tfreq, int *index) return 0; } -static void adspfreq_stats_update(void) -{ - unsigned long long cur_time; - - cur_time = get_jiffies_64(); - freq_stats.time_in_state[freq_stats.last_index] += cur_time - - freq_stats.last_time; - freq_stats.last_time = cur_time; -} - static struct adsp_dfs_policy dfs_policy = { .enable = 1, .clk_name = "adsp_cpu", @@ -593,6 +583,16 @@ exit_out: DEFINE_SIMPLE_ATTRIBUTE(cur_fops, policy_cur_get, policy_cur_set, "%llu\n"); +static void adspfreq_stats_update(void) +{ + unsigned long long cur_time; + + cur_time = get_jiffies_64(); + freq_stats.time_in_state[freq_stats.last_index] += cur_time - + freq_stats.last_time; + freq_stats.last_time = cur_time; +} + /* * Print residency in each freq levels */ From 562a14e11b01007547537658aa9fc32e65ca4bed Mon Sep 17 00:00:00 2001 From: Prateek Patel Date: Fri, 15 Dec 2017 07:12:19 -0800 Subject: [PATCH 043/138] platform: tegra: adsp: add dynamic_app_support adding dynamic_app_support flag for the dynamic apps and setting it to true if load from "adsp.elf" bug 1833670 Change-Id: Ic68611e140f6bc0392a4fb153436d196bf0bda1c Signed-off-by: Prateek Patel Reviewed-on: https://git-master.nvidia.com/r/1618738 Reviewed-by: Automatic_Commit_Validation_User GVS: Gerrit_Virtual_Submit Reviewed-by: Sachin Nikam Reviewed-by: Ajay Nandakumar M Reviewed-by: mobile promotions Tested-by: mobile promotions --- drivers/platform/tegra/nvadsp/adsp_shared_struct.h | 5 +++-- drivers/platform/tegra/nvadsp/os.c | 5 ++++- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/drivers/platform/tegra/nvadsp/adsp_shared_struct.h b/drivers/platform/tegra/nvadsp/adsp_shared_struct.h index bbfaef80..fcc7b643 100644 --- a/drivers/platform/tegra/nvadsp/adsp_shared_struct.h +++ b/drivers/platform/tegra/nvadsp/adsp_shared_struct.h @@ -3,7 +3,7 @@ * * A header file containing shared data structures shared with ADSP OS * - * Copyright (C) 2015-2017 NVIDIA Corporation. All rights reserved. + * Copyright (C) 2015-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 @@ -126,7 +126,8 @@ struct nvadsp_os_args { int32_t timer_prescalar; char logger[DRAM_DEBUG_LOG_SIZE]; uint64_t adsp_freq_hz; - char reserved[128]; + uint32_t dynamic_app_support; + char reserved[124]; } __packed; /* ARM MODE REGS */ diff --git a/drivers/platform/tegra/nvadsp/os.c b/drivers/platform/tegra/nvadsp/os.c index b51bae0a..f87703a6 100644 --- a/drivers/platform/tegra/nvadsp/os.c +++ b/drivers/platform/tegra/nvadsp/os.c @@ -5,7 +5,7 @@ * Copyright (C) 2011 Texas Instruments, Inc. * Copyright (C) 2011 Google, Inc. * - * Copyright (C) 2014-2017, NVIDIA Corporation. All rights reserved. + * 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 @@ -651,6 +651,7 @@ static int __nvadsp_os_secload(struct platform_device *pdev) } shared_mem = dram_va; + shared_mem->os_args.dynamic_app_support = 0; drv_data->shared_adsp_os_data = shared_mem; /* set logger strcuture with required properties */ priv.logger.debug_ram_rdr = shared_mem->os_args.logger; @@ -726,6 +727,8 @@ int nvadsp_os_load(void) goto deallocate_os_memory; } + shared_mem->os_args.dynamic_app_support = 1; + ret = dram_app_mem_init(priv.app_alloc_addr, priv.app_size); if (ret) { dev_err(dev, "Memory allocation dynamic apps failed\n"); From c79dd8ba507c379828d6a303a67b067cf4e0bb40 Mon Sep 17 00:00:00 2001 From: David Gilhooley Date: Fri, 15 Dec 2017 09:59:17 -0800 Subject: [PATCH 044/138] platform: nvadsp: prevent speculative load related leak Data can be speculatively loaded from memory and stay in cache even when bound check fails. This can lead to unintended information disclosure via side-channel analysis. To mitigate this problem, insert speculation barrier. bug 2039126 CVE-2017-5753 Change-Id: I5a745320b64bf6689cf8ac4b713cf1b32f662a23 Signed-off-by: David Gilhooley Reviewed-on: https://git-master.nvidia.com/r/1640352 Reviewed-by: Bharat Nihalani Reviewed-by: Ajay Nandakumar M Reviewed-by: Sachin Nikam Tested-by: Sachin Nikam GVS: Gerrit_Virtual_Submit Reviewed-by: mobile promotions Tested-by: mobile promotions --- drivers/platform/tegra/nvadsp/mailbox.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/platform/tegra/nvadsp/mailbox.c b/drivers/platform/tegra/nvadsp/mailbox.c index 8c2d2c9a..06e2159c 100644 --- a/drivers/platform/tegra/nvadsp/mailbox.c +++ b/drivers/platform/tegra/nvadsp/mailbox.c @@ -1,7 +1,7 @@ /* * ADSP mailbox manager * - * Copyright (c) 2014-2017, NVIDIA CORPORATION. All rights reserved. + * Copyright (c) 2014-2018, NVIDIA CORPORATION. All rights reserved. * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU General Public License, @@ -14,6 +14,7 @@ */ #include "dev.h" +#include #define NVADSP_MAILBOX_START 512 #define NVADSP_MAILBOX_MAX 1024 @@ -184,6 +185,7 @@ status_t nvadsp_mbox_open(struct nvadsp_mbox *mbox, uint16_t *mid, ret = -ERANGE; goto out; } + speculation_barrier(); if (nvadsp_drv_data->mboxes[*mid]) { pr_debug("%s: mailbox %d already opened.\n", __func__, *mid); From 8f2612d56d40aa646084dfe49e1dabba934da927 Mon Sep 17 00:00:00 2001 From: Ajay Nandakumar Date: Wed, 24 Jan 2018 02:38:44 -0800 Subject: [PATCH 045/138] platform: nvadsp: Ability to get chip id on ADSP Adding ability for ADSP OS to get chip id. This can be used to enable/disable features. Bug 200372198 Change-Id: I9f872065bcffa714bf1e2added7c41c3b228cebc Signed-off-by: Ajay Nandakumar Reviewed-on: https://git-master.nvidia.com/r/1645257 Reviewed-by: Automatic_Commit_Validation_User Reviewed-by: svc-mobile-coverity GVS: Gerrit_Virtual_Submit Reviewed-by: Bharat Nihalani Reviewed-by: mobile promotions Tested-by: mobile promotions --- .../tegra/nvadsp/adsp_shared_struct.h | 3 +- drivers/platform/tegra/nvadsp/os.c | 116 ++++++++++-------- 2 files changed, 70 insertions(+), 49 deletions(-) diff --git a/drivers/platform/tegra/nvadsp/adsp_shared_struct.h b/drivers/platform/tegra/nvadsp/adsp_shared_struct.h index fcc7b643..95e43494 100644 --- a/drivers/platform/tegra/nvadsp/adsp_shared_struct.h +++ b/drivers/platform/tegra/nvadsp/adsp_shared_struct.h @@ -127,7 +127,8 @@ struct nvadsp_os_args { char logger[DRAM_DEBUG_LOG_SIZE]; uint64_t adsp_freq_hz; uint32_t dynamic_app_support; - char reserved[124]; + uint32_t chip_id; + char reserved[120]; } __packed; /* ARM MODE REGS */ diff --git a/drivers/platform/tegra/nvadsp/os.c b/drivers/platform/tegra/nvadsp/os.c index f87703a6..1df4d194 100644 --- a/drivers/platform/tegra/nvadsp/os.c +++ b/drivers/platform/tegra/nvadsp/os.c @@ -42,6 +42,8 @@ #include +#include + #include "ape_actmon.h" #include "os.h" #include "dev.h" @@ -635,12 +637,34 @@ static void deallocate_memory_for_adsp_os(struct device *dev) #endif } +static void nvadsp_set_shared_mem(struct platform_device *pdev, + struct nvadsp_shared_mem *shared_mem, + uint32_t dynamic_app_support) +{ + struct nvadsp_drv_data *drv_data = platform_get_drvdata(pdev); + struct device *dev = &pdev->dev; + struct nvadsp_os_args *os_args; + enum tegra_chipid chip_id; + + shared_mem->os_args.dynamic_app_support = dynamic_app_support; + /* set logger strcuture with required properties */ + priv.logger.debug_ram_rdr = shared_mem->os_args.logger; + priv.logger.debug_ram_sz = sizeof(shared_mem->os_args.logger); + priv.logger.dev = dev; + priv.adsp_os_fw_loaded = true; + + chip_id = tegra_get_chipid(); + os_args = &shared_mem->os_args; + os_args->chip_id = chip_id; + + drv_data->shared_adsp_os_data = shared_mem; +} + static int __nvadsp_os_secload(struct platform_device *pdev) { struct nvadsp_drv_data *drv_data = platform_get_drvdata(pdev); dma_addr_t addr = drv_data->adsp_mem[ACSR_ADDR]; size_t size = drv_data->adsp_mem[ACSR_SIZE]; - struct nvadsp_shared_mem *shared_mem; struct device *dev = &pdev->dev; void *dram_va; @@ -650,49 +674,18 @@ static int __nvadsp_os_secload(struct platform_device *pdev) return -ENOMEM; } - shared_mem = dram_va; - shared_mem->os_args.dynamic_app_support = 0; - drv_data->shared_adsp_os_data = shared_mem; - /* set logger strcuture with required properties */ - priv.logger.debug_ram_rdr = shared_mem->os_args.logger; - priv.logger.debug_ram_sz = sizeof(shared_mem->os_args.logger); - priv.logger.dev = dev; - priv.adsp_os_fw_loaded = true; + nvadsp_set_shared_mem(pdev, dram_va, 0); -#ifdef CONFIG_DEBUG_FS - wake_up(&priv.logger.wait_queue); -#endif return 0; } -int nvadsp_os_load(void) +static int nvadsp_firmware_load(struct platform_device *pdev) { struct nvadsp_shared_mem *shared_mem; - struct nvadsp_drv_data *drv_data; + struct device *dev = &pdev->dev; const struct firmware *fw; - struct device *dev; int ret = 0; - if (!priv.pdev) { - pr_err("ADSP Driver is not initialized\n"); - ret = -EINVAL; - goto end; - } - - mutex_lock(&priv.fw_load_lock); - if (priv.adsp_os_fw_loaded) - goto end; - - dev = &priv.pdev->dev; - - drv_data = platform_get_drvdata(priv.pdev); - - if (drv_data->adsp_os_secload) { - dev_info(dev, "ADSP OS firmware already loaded\n"); - ret = __nvadsp_os_secload(priv.pdev); - goto end; - } - ret = request_firmware(&fw, NVADSP_FIRMWARE, dev); if (ret < 0) { dev_err(dev, "reqest firmware for %s failed with %d\n", @@ -712,13 +705,6 @@ int nvadsp_os_load(void) goto release_firmware; } - shared_mem = get_mailbox_shared_region(fw); - drv_data->shared_adsp_os_data = shared_mem; - /* set logger strcuture with required properties */ - priv.logger.debug_ram_rdr = shared_mem->os_args.logger; - priv.logger.debug_ram_sz = sizeof(shared_mem->os_args.logger); - priv.logger.dev = dev; - dev_info(dev, "Loading ADSP OS firmware %s\n", NVADSP_FIRMWARE); ret = nvadsp_os_elf_load(fw); @@ -727,7 +713,8 @@ int nvadsp_os_load(void) goto deallocate_os_memory; } - shared_mem->os_args.dynamic_app_support = 1; + shared_mem = get_mailbox_shared_region(fw); + nvadsp_set_shared_mem(pdev, shared_mem, 1); ret = dram_app_mem_init(priv.app_alloc_addr, priv.app_size); if (ret) { @@ -735,17 +722,50 @@ int nvadsp_os_load(void) goto deallocate_os_memory; } priv.os_firmware = fw; - priv.adsp_os_fw_loaded = true; -#ifdef CONFIG_DEBUG_FS - wake_up(&priv.logger.wait_queue); -#endif - mutex_unlock(&priv.fw_load_lock); + return 0; deallocate_os_memory: deallocate_memory_for_adsp_os(dev); release_firmware: release_firmware(fw); +end: + return ret; + +} + +int nvadsp_os_load(void) +{ + struct nvadsp_drv_data *drv_data; + struct device *dev; + int ret = 0; + + if (!priv.pdev) { + pr_err("ADSP Driver is not initialized\n"); + ret = -EINVAL; + goto end; + } + + mutex_lock(&priv.fw_load_lock); + if (priv.adsp_os_fw_loaded) + goto end; + + drv_data = platform_get_drvdata(priv.pdev); + dev = &priv.pdev->dev; + + if (drv_data->adsp_os_secload) { + dev_info(dev, "ADSP OS firmware already loaded\n"); + ret = __nvadsp_os_secload(priv.pdev); + } else { + ret = nvadsp_firmware_load(priv.pdev); + } + + if (ret == 0) { + priv.adsp_os_fw_loaded = true; +#ifdef CONFIG_DEBUG_FS + wake_up(&priv.logger.wait_queue); +#endif + } end: mutex_unlock(&priv.fw_load_lock); return ret; From 3079cc865b64d351fb137d1b2752697d77a43373 Mon Sep 17 00:00:00 2001 From: Bharat Nihalani Date: Sun, 4 Feb 2018 22:46:39 -0800 Subject: [PATCH 046/138] Revert "platform: nvadsp: Ability to get chip id on ADSP" This reverts commit baa2e9cd86a446e085091494be4247d2a4b64a33 since it causes ADSP tests to fail on T18x and T19x platforms. Bug 2057870 Change-Id: Ibfc1ec5dfcd678bf608598edc1f118a452d072e3 Signed-off-by: Bharat Nihalani Reviewed-on: https://git-master.nvidia.com/r/1651935 Reviewed-by: svc-mobile-coverity --- .../tegra/nvadsp/adsp_shared_struct.h | 3 +- drivers/platform/tegra/nvadsp/os.c | 116 ++++++++---------- 2 files changed, 49 insertions(+), 70 deletions(-) diff --git a/drivers/platform/tegra/nvadsp/adsp_shared_struct.h b/drivers/platform/tegra/nvadsp/adsp_shared_struct.h index 95e43494..fcc7b643 100644 --- a/drivers/platform/tegra/nvadsp/adsp_shared_struct.h +++ b/drivers/platform/tegra/nvadsp/adsp_shared_struct.h @@ -127,8 +127,7 @@ struct nvadsp_os_args { char logger[DRAM_DEBUG_LOG_SIZE]; uint64_t adsp_freq_hz; uint32_t dynamic_app_support; - uint32_t chip_id; - char reserved[120]; + char reserved[124]; } __packed; /* ARM MODE REGS */ diff --git a/drivers/platform/tegra/nvadsp/os.c b/drivers/platform/tegra/nvadsp/os.c index 1df4d194..f87703a6 100644 --- a/drivers/platform/tegra/nvadsp/os.c +++ b/drivers/platform/tegra/nvadsp/os.c @@ -42,8 +42,6 @@ #include -#include - #include "ape_actmon.h" #include "os.h" #include "dev.h" @@ -637,34 +635,12 @@ static void deallocate_memory_for_adsp_os(struct device *dev) #endif } -static void nvadsp_set_shared_mem(struct platform_device *pdev, - struct nvadsp_shared_mem *shared_mem, - uint32_t dynamic_app_support) -{ - struct nvadsp_drv_data *drv_data = platform_get_drvdata(pdev); - struct device *dev = &pdev->dev; - struct nvadsp_os_args *os_args; - enum tegra_chipid chip_id; - - shared_mem->os_args.dynamic_app_support = dynamic_app_support; - /* set logger strcuture with required properties */ - priv.logger.debug_ram_rdr = shared_mem->os_args.logger; - priv.logger.debug_ram_sz = sizeof(shared_mem->os_args.logger); - priv.logger.dev = dev; - priv.adsp_os_fw_loaded = true; - - chip_id = tegra_get_chipid(); - os_args = &shared_mem->os_args; - os_args->chip_id = chip_id; - - drv_data->shared_adsp_os_data = shared_mem; -} - static int __nvadsp_os_secload(struct platform_device *pdev) { struct nvadsp_drv_data *drv_data = platform_get_drvdata(pdev); dma_addr_t addr = drv_data->adsp_mem[ACSR_ADDR]; size_t size = drv_data->adsp_mem[ACSR_SIZE]; + struct nvadsp_shared_mem *shared_mem; struct device *dev = &pdev->dev; void *dram_va; @@ -674,18 +650,49 @@ static int __nvadsp_os_secload(struct platform_device *pdev) return -ENOMEM; } - nvadsp_set_shared_mem(pdev, dram_va, 0); + shared_mem = dram_va; + shared_mem->os_args.dynamic_app_support = 0; + drv_data->shared_adsp_os_data = shared_mem; + /* set logger strcuture with required properties */ + priv.logger.debug_ram_rdr = shared_mem->os_args.logger; + priv.logger.debug_ram_sz = sizeof(shared_mem->os_args.logger); + priv.logger.dev = dev; + priv.adsp_os_fw_loaded = true; +#ifdef CONFIG_DEBUG_FS + wake_up(&priv.logger.wait_queue); +#endif return 0; } -static int nvadsp_firmware_load(struct platform_device *pdev) +int nvadsp_os_load(void) { struct nvadsp_shared_mem *shared_mem; - struct device *dev = &pdev->dev; + struct nvadsp_drv_data *drv_data; const struct firmware *fw; + struct device *dev; int ret = 0; + if (!priv.pdev) { + pr_err("ADSP Driver is not initialized\n"); + ret = -EINVAL; + goto end; + } + + mutex_lock(&priv.fw_load_lock); + if (priv.adsp_os_fw_loaded) + goto end; + + dev = &priv.pdev->dev; + + drv_data = platform_get_drvdata(priv.pdev); + + if (drv_data->adsp_os_secload) { + dev_info(dev, "ADSP OS firmware already loaded\n"); + ret = __nvadsp_os_secload(priv.pdev); + goto end; + } + ret = request_firmware(&fw, NVADSP_FIRMWARE, dev); if (ret < 0) { dev_err(dev, "reqest firmware for %s failed with %d\n", @@ -705,6 +712,13 @@ static int nvadsp_firmware_load(struct platform_device *pdev) goto release_firmware; } + shared_mem = get_mailbox_shared_region(fw); + drv_data->shared_adsp_os_data = shared_mem; + /* set logger strcuture with required properties */ + priv.logger.debug_ram_rdr = shared_mem->os_args.logger; + priv.logger.debug_ram_sz = sizeof(shared_mem->os_args.logger); + priv.logger.dev = dev; + dev_info(dev, "Loading ADSP OS firmware %s\n", NVADSP_FIRMWARE); ret = nvadsp_os_elf_load(fw); @@ -713,8 +727,7 @@ static int nvadsp_firmware_load(struct platform_device *pdev) goto deallocate_os_memory; } - shared_mem = get_mailbox_shared_region(fw); - nvadsp_set_shared_mem(pdev, shared_mem, 1); + shared_mem->os_args.dynamic_app_support = 1; ret = dram_app_mem_init(priv.app_alloc_addr, priv.app_size); if (ret) { @@ -722,50 +735,17 @@ static int nvadsp_firmware_load(struct platform_device *pdev) goto deallocate_os_memory; } priv.os_firmware = fw; - + priv.adsp_os_fw_loaded = true; +#ifdef CONFIG_DEBUG_FS + wake_up(&priv.logger.wait_queue); +#endif + mutex_unlock(&priv.fw_load_lock); return 0; deallocate_os_memory: deallocate_memory_for_adsp_os(dev); release_firmware: release_firmware(fw); -end: - return ret; - -} - -int nvadsp_os_load(void) -{ - struct nvadsp_drv_data *drv_data; - struct device *dev; - int ret = 0; - - if (!priv.pdev) { - pr_err("ADSP Driver is not initialized\n"); - ret = -EINVAL; - goto end; - } - - mutex_lock(&priv.fw_load_lock); - if (priv.adsp_os_fw_loaded) - goto end; - - drv_data = platform_get_drvdata(priv.pdev); - dev = &priv.pdev->dev; - - if (drv_data->adsp_os_secload) { - dev_info(dev, "ADSP OS firmware already loaded\n"); - ret = __nvadsp_os_secload(priv.pdev); - } else { - ret = nvadsp_firmware_load(priv.pdev); - } - - if (ret == 0) { - priv.adsp_os_fw_loaded = true; -#ifdef CONFIG_DEBUG_FS - wake_up(&priv.logger.wait_queue); -#endif - } end: mutex_unlock(&priv.fw_load_lock); return ret; From 07d0f698f2d9b7c05bcb22719d1a1b9cca540f51 Mon Sep 17 00:00:00 2001 From: Ajay Nandakumar M Date: Mon, 5 Feb 2018 22:38:15 -0800 Subject: [PATCH 047/138] Revert "Revert "platform: nvadsp: Ability to get chip id on ADSP"" This reverts commit 45f17000bcd1a32f4b6d35a11af476c4dc152060 and restores change https://git-master.nvidia.com/r/1645257. Bug 200372198 Change-Id: I3798ac0ba7432cc50df18dde65705145f62c0616 Signed-off-by: Ajay Nandakumar Reviewed-on: https://git-master.nvidia.com/r/1652521 GVS: Gerrit_Virtual_Submit Reviewed-by: Bharat Nihalani Reviewed-by: mobile promotions Tested-by: mobile promotions --- .../tegra/nvadsp/adsp_shared_struct.h | 3 +- drivers/platform/tegra/nvadsp/os.c | 116 ++++++++++-------- 2 files changed, 70 insertions(+), 49 deletions(-) diff --git a/drivers/platform/tegra/nvadsp/adsp_shared_struct.h b/drivers/platform/tegra/nvadsp/adsp_shared_struct.h index fcc7b643..95e43494 100644 --- a/drivers/platform/tegra/nvadsp/adsp_shared_struct.h +++ b/drivers/platform/tegra/nvadsp/adsp_shared_struct.h @@ -127,7 +127,8 @@ struct nvadsp_os_args { char logger[DRAM_DEBUG_LOG_SIZE]; uint64_t adsp_freq_hz; uint32_t dynamic_app_support; - char reserved[124]; + uint32_t chip_id; + char reserved[120]; } __packed; /* ARM MODE REGS */ diff --git a/drivers/platform/tegra/nvadsp/os.c b/drivers/platform/tegra/nvadsp/os.c index f87703a6..1df4d194 100644 --- a/drivers/platform/tegra/nvadsp/os.c +++ b/drivers/platform/tegra/nvadsp/os.c @@ -42,6 +42,8 @@ #include +#include + #include "ape_actmon.h" #include "os.h" #include "dev.h" @@ -635,12 +637,34 @@ static void deallocate_memory_for_adsp_os(struct device *dev) #endif } +static void nvadsp_set_shared_mem(struct platform_device *pdev, + struct nvadsp_shared_mem *shared_mem, + uint32_t dynamic_app_support) +{ + struct nvadsp_drv_data *drv_data = platform_get_drvdata(pdev); + struct device *dev = &pdev->dev; + struct nvadsp_os_args *os_args; + enum tegra_chipid chip_id; + + shared_mem->os_args.dynamic_app_support = dynamic_app_support; + /* set logger strcuture with required properties */ + priv.logger.debug_ram_rdr = shared_mem->os_args.logger; + priv.logger.debug_ram_sz = sizeof(shared_mem->os_args.logger); + priv.logger.dev = dev; + priv.adsp_os_fw_loaded = true; + + chip_id = tegra_get_chipid(); + os_args = &shared_mem->os_args; + os_args->chip_id = chip_id; + + drv_data->shared_adsp_os_data = shared_mem; +} + static int __nvadsp_os_secload(struct platform_device *pdev) { struct nvadsp_drv_data *drv_data = platform_get_drvdata(pdev); dma_addr_t addr = drv_data->adsp_mem[ACSR_ADDR]; size_t size = drv_data->adsp_mem[ACSR_SIZE]; - struct nvadsp_shared_mem *shared_mem; struct device *dev = &pdev->dev; void *dram_va; @@ -650,49 +674,18 @@ static int __nvadsp_os_secload(struct platform_device *pdev) return -ENOMEM; } - shared_mem = dram_va; - shared_mem->os_args.dynamic_app_support = 0; - drv_data->shared_adsp_os_data = shared_mem; - /* set logger strcuture with required properties */ - priv.logger.debug_ram_rdr = shared_mem->os_args.logger; - priv.logger.debug_ram_sz = sizeof(shared_mem->os_args.logger); - priv.logger.dev = dev; - priv.adsp_os_fw_loaded = true; + nvadsp_set_shared_mem(pdev, dram_va, 0); -#ifdef CONFIG_DEBUG_FS - wake_up(&priv.logger.wait_queue); -#endif return 0; } -int nvadsp_os_load(void) +static int nvadsp_firmware_load(struct platform_device *pdev) { struct nvadsp_shared_mem *shared_mem; - struct nvadsp_drv_data *drv_data; + struct device *dev = &pdev->dev; const struct firmware *fw; - struct device *dev; int ret = 0; - if (!priv.pdev) { - pr_err("ADSP Driver is not initialized\n"); - ret = -EINVAL; - goto end; - } - - mutex_lock(&priv.fw_load_lock); - if (priv.adsp_os_fw_loaded) - goto end; - - dev = &priv.pdev->dev; - - drv_data = platform_get_drvdata(priv.pdev); - - if (drv_data->adsp_os_secload) { - dev_info(dev, "ADSP OS firmware already loaded\n"); - ret = __nvadsp_os_secload(priv.pdev); - goto end; - } - ret = request_firmware(&fw, NVADSP_FIRMWARE, dev); if (ret < 0) { dev_err(dev, "reqest firmware for %s failed with %d\n", @@ -712,13 +705,6 @@ int nvadsp_os_load(void) goto release_firmware; } - shared_mem = get_mailbox_shared_region(fw); - drv_data->shared_adsp_os_data = shared_mem; - /* set logger strcuture with required properties */ - priv.logger.debug_ram_rdr = shared_mem->os_args.logger; - priv.logger.debug_ram_sz = sizeof(shared_mem->os_args.logger); - priv.logger.dev = dev; - dev_info(dev, "Loading ADSP OS firmware %s\n", NVADSP_FIRMWARE); ret = nvadsp_os_elf_load(fw); @@ -727,7 +713,8 @@ int nvadsp_os_load(void) goto deallocate_os_memory; } - shared_mem->os_args.dynamic_app_support = 1; + shared_mem = get_mailbox_shared_region(fw); + nvadsp_set_shared_mem(pdev, shared_mem, 1); ret = dram_app_mem_init(priv.app_alloc_addr, priv.app_size); if (ret) { @@ -735,17 +722,50 @@ int nvadsp_os_load(void) goto deallocate_os_memory; } priv.os_firmware = fw; - priv.adsp_os_fw_loaded = true; -#ifdef CONFIG_DEBUG_FS - wake_up(&priv.logger.wait_queue); -#endif - mutex_unlock(&priv.fw_load_lock); + return 0; deallocate_os_memory: deallocate_memory_for_adsp_os(dev); release_firmware: release_firmware(fw); +end: + return ret; + +} + +int nvadsp_os_load(void) +{ + struct nvadsp_drv_data *drv_data; + struct device *dev; + int ret = 0; + + if (!priv.pdev) { + pr_err("ADSP Driver is not initialized\n"); + ret = -EINVAL; + goto end; + } + + mutex_lock(&priv.fw_load_lock); + if (priv.adsp_os_fw_loaded) + goto end; + + drv_data = platform_get_drvdata(priv.pdev); + dev = &priv.pdev->dev; + + if (drv_data->adsp_os_secload) { + dev_info(dev, "ADSP OS firmware already loaded\n"); + ret = __nvadsp_os_secload(priv.pdev); + } else { + ret = nvadsp_firmware_load(priv.pdev); + } + + if (ret == 0) { + priv.adsp_os_fw_loaded = true; +#ifdef CONFIG_DEBUG_FS + wake_up(&priv.logger.wait_queue); +#endif + } end: mutex_unlock(&priv.fw_load_lock); return ret; From e0ecbe2746f1ae8176d1d2c83151f73d51ed3615 Mon Sep 17 00:00:00 2001 From: Dipesh Gandhi Date: Fri, 12 Jan 2018 10:19:11 +0530 Subject: [PATCH 048/138] platform: nvadsp: print app version info Print each app's version info string in the app's sysfs node Jira EMA-841 Bug 1819427 Bug 200381729 Change-Id: I2cbb7e13a4a351100812dcf63f9f62b8268c874f Signed-off-by: Gaurav Tendolkar Reviewed-on: http://git-master/r/1246459 (cherry picked from commit 1ccaba7f30c1b663fef0082b0686e1a3ba65e484) Signed-off-by: Hariharan Sivaraman Reviewed-on: https://git-master.nvidia.com/r/1640900 (cherry picked from commit e81e6dd69db817a351f09befd86ac5612eb003f3) Reviewed-on: https://git-master.nvidia.com/r/1652453 Reviewed-by: svcboomerang Tested-by: svcboomerang --- drivers/platform/tegra/nvadsp/adsp_shared_struct.h | 1 + drivers/platform/tegra/nvadsp/app.c | 12 ++++++++++++ drivers/platform/tegra/nvadsp/app_loader_linker.c | 1 + drivers/platform/tegra/nvadsp/os.h | 1 + 4 files changed, 15 insertions(+) diff --git a/drivers/platform/tegra/nvadsp/adsp_shared_struct.h b/drivers/platform/tegra/nvadsp/adsp_shared_struct.h index 95e43494..5859a04d 100644 --- a/drivers/platform/tegra/nvadsp/adsp_shared_struct.h +++ b/drivers/platform/tegra/nvadsp/adsp_shared_struct.h @@ -46,6 +46,7 @@ struct adsp_shared_app { int32_t dram_data_ptr; int32_t shared_data_ptr; int32_t shared_wc_data_ptr; + char version[16]; } __packed; /* ADSP app loader message queue */ diff --git a/drivers/platform/tegra/nvadsp/app.c b/drivers/platform/tegra/nvadsp/app.c index 4b1acb1c..6268ce79 100644 --- a/drivers/platform/tegra/nvadsp/app.c +++ b/drivers/platform/tegra/nvadsp/app.c @@ -132,6 +132,16 @@ static int size_app_file_node(struct seq_file *s, void *data) return 0; } +static int version_app_file_node(struct seq_file *s, void *data) +{ + struct nvadsp_app_service *ser = s->private; + struct adsp_module *mod = ser->mod; + + seq_printf(s, "%s\n", strcmp(mod->version, "") ? mod->version : "unavailable"); + + return 0; +} + static int dram_app_file_node(struct seq_file *s, void *data) { const struct app_mem_size *mem_size = s->private; @@ -215,6 +225,7 @@ ADSP_APP_FILE_OPERATION(dump_binary_in_words); ADSP_APP_FILE_OPERATION(host_load_addr); ADSP_APP_FILE_OPERATION(adsp_load_addr); ADSP_APP_FILE_OPERATION(size); +ADSP_APP_FILE_OPERATION(version); ADSP_APP_FILE_OPERATION(dram); ADSP_APP_FILE_OPERATION(dram_shared); @@ -243,6 +254,7 @@ static int create_adsp_app_debugfs(struct nvadsp_app_service *ser) ADSP_APP_CREATE_FILE(host_load_addr, ser, root); ADSP_APP_CREATE_FILE(adsp_load_addr, ser, root); ADSP_APP_CREATE_FILE(size, ser, root); + ADSP_APP_CREATE_FILE(version, ser, root); ADSP_APP_CREATE_FOLDER(instance_mem_sizes, root); ADSP_APP_CREATE_FILE(dram, mem_size, instance_mem_sizes); ADSP_APP_CREATE_FILE(dram_shared, mem_size, instance_mem_sizes); diff --git a/drivers/platform/tegra/nvadsp/app_loader_linker.c b/drivers/platform/tegra/nvadsp/app_loader_linker.c index 396a3563..3e64bdd9 100644 --- a/drivers/platform/tegra/nvadsp/app_loader_linker.c +++ b/drivers/platform/tegra/nvadsp/app_loader_linker.c @@ -844,6 +844,7 @@ struct adsp_module *load_adsp_static_module(const char *appname, mod->adsp_module_ptr = shared_app->mod_ptr; mod->dynamic = false; + memcpy(mod->version, shared_app->version, sizeof(shared_app->version)); return mod; } diff --git a/drivers/platform/tegra/nvadsp/os.h b/drivers/platform/tegra/nvadsp/os.h index 1878955a..624b94c9 100644 --- a/drivers/platform/tegra/nvadsp/os.h +++ b/drivers/platform/tegra/nvadsp/os.h @@ -104,6 +104,7 @@ struct adsp_module { size_t size; const struct app_mem_size mem_size; bool dynamic; + char version[16]; }; struct app_load_stats { From 5c63aad7c299d27cf5ba6d63b005ee0a71c8fe86 Mon Sep 17 00:00:00 2001 From: Ajay Nandakumar Date: Tue, 6 Feb 2018 17:54:03 +0530 Subject: [PATCH 049/138] platform: nvadsp: do not export da to va mappings 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 Reviewed-on: https://git-master.nvidia.com/r/1652771 Reviewed-by: mobile promotions Tested-by: mobile promotions --- drivers/platform/tegra/nvadsp/dev.c | 4 ++-- drivers/platform/tegra/nvadsp/os.c | 5 ++--- drivers/platform/tegra/nvadsp/os.h | 5 +++-- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/drivers/platform/tegra/nvadsp/dev.c b/drivers/platform/tegra/nvadsp/dev.c index 03cb86d4..6560297b 100644 --- a/drivers/platform/tegra/nvadsp/dev.c +++ b/drivers/platform/tegra/nvadsp/dev.c @@ -3,7 +3,7 @@ * * A device driver for ADSP and APE * - * Copyright (C) 2014-2017, NVIDIA Corporation. All rights reserved. + * 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 @@ -282,7 +282,7 @@ static int __init nvadsp_probe(struct platform_device *pdev) goto out; } drv_data->base_regs[iter] = base; - adsp_add_load_mappings(res->start, base, + nvadsp_add_load_mappings(res->start, base, resource_size(res)); } diff --git a/drivers/platform/tegra/nvadsp/os.c b/drivers/platform/tegra/nvadsp/os.c index 1df4d194..7befb63b 100644 --- a/drivers/platform/tegra/nvadsp/os.c +++ b/drivers/platform/tegra/nvadsp/os.c @@ -309,7 +309,7 @@ bool is_adsp_dram_addr(u64 addr) return false; } -int adsp_add_load_mappings(phys_addr_t pa, void *mapping, int len) +int nvadsp_add_load_mappings(phys_addr_t pa, void *mapping, int len) { if (map_idx >= NM_LOAD_MAPPINGS) return -EINVAL; @@ -342,7 +342,6 @@ void *nvadsp_da_to_va_mappings(u64 da, int len) } return ptr; } -EXPORT_SYMBOL(nvadsp_da_to_va_mappings); void *nvadsp_alloc_coherent(size_t size, dma_addr_t *da, gfp_t flags) { @@ -623,7 +622,7 @@ static int allocate_memory_for_adsp_os(void) goto end; } #endif - adsp_add_load_mappings(addr, dram_va, size); + nvadsp_add_load_mappings(addr, dram_va, size); end: return ret; } diff --git a/drivers/platform/tegra/nvadsp/os.h b/drivers/platform/tegra/nvadsp/os.h index 624b94c9..e89503c7 100644 --- a/drivers/platform/tegra/nvadsp/os.h +++ b/drivers/platform/tegra/nvadsp/os.h @@ -3,7 +3,7 @@ * * A header file containing data structures shared with ADSP OS * - * Copyright (C) 2014-2017 NVIDIA Corporation. All rights reserved. + * 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 @@ -160,7 +160,8 @@ static inline int nvadsp_os_init(struct platform_device *pdev) int nvadsp_os_probe(struct platform_device *); int nvadsp_app_module_probe(struct platform_device *); -int adsp_add_load_mappings(phys_addr_t, void *, int); +void *nvadsp_da_to_va_mappings(u64 da, int len); +int nvadsp_add_load_mappings(phys_addr_t pa, void *mapping, int len); struct elf32_shdr *nvadsp_get_section(const struct firmware *, char *); struct global_sym_info *find_global_symbol(const char *); void update_nvadsp_app_shared_ptr(void *); From 168fe1fdab38b463065c334150f2de7448d56195 Mon Sep 17 00:00:00 2001 From: Ajay Nandakumar Date: Tue, 6 Feb 2018 18:03:08 +0530 Subject: [PATCH 050/138] platform: tegra: nvadsp: remove export symbols Remove export symbols for APIs that are not exposed and not used by other modules except ADSP driver. Bug 200381256 Change-Id: Ibe11e5992e5a93b7a5698a6158d9d27e4f55d453 Signed-off-by: Ajay Nandakumar Reviewed-on: https://git-master.nvidia.com/r/1652773 Reviewed-by: svc-mobile-coverity Reviewed-by: Automatic_Commit_Validation_User GVS: Gerrit_Virtual_Submit Reviewed-by: Sachin Nikam Reviewed-by: mobile promotions Tested-by: mobile promotions --- drivers/platform/tegra/nvadsp/aram_manager.c | 6 +----- drivers/platform/tegra/nvadsp/dram_app_mem_manager.c | 8 +------- drivers/platform/tegra/nvadsp/mem_manager.c | 9 +-------- 3 files changed, 3 insertions(+), 20 deletions(-) diff --git a/drivers/platform/tegra/nvadsp/aram_manager.c b/drivers/platform/tegra/nvadsp/aram_manager.c index 4b28ae95..62182c7a 100644 --- a/drivers/platform/tegra/nvadsp/aram_manager.c +++ b/drivers/platform/tegra/nvadsp/aram_manager.c @@ -3,7 +3,7 @@ * * ARAM manager * - * Copyright (C) 2014-2017, NVIDIA Corporation. All rights reserved. + * 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 @@ -32,25 +32,21 @@ void nvadsp_aram_print(void) { mem_print(aram_handle); } -EXPORT_SYMBOL(nvadsp_aram_print); void *nvadsp_aram_request(const char *name, size_t size) { return mem_request(aram_handle, name, size); } -EXPORT_SYMBOL(nvadsp_aram_request); bool nvadsp_aram_release(void *handle) { return mem_release(aram_handle, handle); } -EXPORT_SYMBOL(nvadsp_aram_release); unsigned long nvadsp_aram_get_address(void *handle) { return mem_get_address(handle); } -EXPORT_SYMBOL(nvadsp_aram_get_address); #ifdef CONFIG_DEBUG_FS static struct dentry *aram_dump_debugfs_file; diff --git a/drivers/platform/tegra/nvadsp/dram_app_mem_manager.c b/drivers/platform/tegra/nvadsp/dram_app_mem_manager.c index b11049e3..ca809526 100644 --- a/drivers/platform/tegra/nvadsp/dram_app_mem_manager.c +++ b/drivers/platform/tegra/nvadsp/dram_app_mem_manager.c @@ -3,7 +3,7 @@ * * dram app memory manager for allocating memory for text,bss and data * - * Copyright (C) 2014-2016, NVIDIA Corporation. All rights reserved. + * 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 @@ -34,25 +34,21 @@ void dram_app_mem_print(void) { mem_print(dram_app_mem_handle); } -EXPORT_SYMBOL(dram_app_mem_print); void *dram_app_mem_request(const char *name, size_t size) { return mem_request(dram_app_mem_handle, name, ALIGN_TO_ADSP_PAGE(size)); } -EXPORT_SYMBOL(dram_app_mem_request); bool dram_app_mem_release(void *handle) { return mem_release(dram_app_mem_handle, handle); } -EXPORT_SYMBOL(dram_app_mem_release); unsigned long dram_app_mem_get_address(void *handle) { return mem_get_address(handle); } -EXPORT_SYMBOL(dram_app_mem_get_address); #ifdef CONFIG_DEBUG_FS static struct dentry *dram_app_mem_dump_debugfs_file; @@ -97,7 +93,6 @@ int dram_app_mem_init(unsigned long start, unsigned long size) #endif return 0; } -EXPORT_SYMBOL(dram_app_mem_init); void dram_app_mem_exit(void) { @@ -106,5 +101,4 @@ void dram_app_mem_exit(void) #endif destroy_mem_manager(dram_app_mem_handle); } -EXPORT_SYMBOL(dram_app_mem_exit); diff --git a/drivers/platform/tegra/nvadsp/mem_manager.c b/drivers/platform/tegra/nvadsp/mem_manager.c index 63a64d90..f0dc582e 100644 --- a/drivers/platform/tegra/nvadsp/mem_manager.c +++ b/drivers/platform/tegra/nvadsp/mem_manager.c @@ -3,7 +3,7 @@ * * memory manager * - * Copyright (C) 2014-2015 NVIDIA Corporation. All rights reserved. + * 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 @@ -105,7 +105,6 @@ void *mem_request(void *mem_handle, const char *name, size_t size) return new_mc; } } -EXPORT_SYMBOL(mem_request); /* * Find the node with sepcified address and remove it from list @@ -168,14 +167,12 @@ bool mem_release(void *mem_handle, void *handle) spin_unlock_irqrestore(&mm_info->lock, flags); return false; } -EXPORT_SYMBOL(mem_release); inline unsigned long mem_get_address(void *handle) { struct mem_chunk *mc = (struct mem_chunk *)handle; return mc->address; } -EXPORT_SYMBOL(mem_get_address); void mem_print(void *mem_handle) { @@ -200,7 +197,6 @@ void mem_print(void *mem_handle) pr_info("------------------------------------\n"); } -EXPORT_SYMBOL(mem_print); void mem_dump(void *mem_handle, struct seq_file *s) { @@ -225,7 +221,6 @@ void mem_dump(void *mem_handle, struct seq_file *s) seq_puts(s, "---------------------------------------\n"); } -EXPORT_SYMBOL(mem_dump); static void clear_alloc_list(struct mem_manager_info *mm_info) { @@ -300,7 +295,6 @@ free_mm_info: return ret; } -EXPORT_SYMBOL(create_mem_manager); void destroy_mem_manager(void *mem_handle) { @@ -320,4 +314,3 @@ void destroy_mem_manager(void *mem_handle) kfree(mm_info->free_list); kfree(mm_info); } -EXPORT_SYMBOL(destroy_mem_manager); From c8f7a26db70838c77a34da62cfeb3e5b2f4dbf65 Mon Sep 17 00:00:00 2001 From: Ajay Nandakumar Date: Thu, 22 Feb 2018 23:43:44 -0800 Subject: [PATCH 051/138] platform: nvadsp: Reduce blocking time of logger The logger currently blocks in open for finding SOH within the circular buffer. This blocking is infinite. However, the SOH is written in buffer for sure when adsp boots. Hence, added a check to see if in the past if adsp has been started and then search for SOH else return an error. Also, the blocking takes place in the read path where the logger waits in the position where it gets EOT and loops back. This is not desirable due to recent changes in debugfs and hence now the read path returns back with an ASCII value of BELL which will not effect `cat` from writing anything on the console. Bug 1997011 Change-Id: Ia6e990bcdc1af5c328c24f350af941126beda07a Signed-off-by: Ajay Nandakumar Reviewed-on: https://git-master.nvidia.com/r/1662918 Reviewed-by: svc-mobile-coverity GVS: Gerrit_Virtual_Submit Reviewed-by: Sachin Nikam Reviewed-by: Timo Alho Reviewed-by: mobile promotions Tested-by: mobile promotions --- drivers/platform/tegra/nvadsp/os.c | 45 ++++++++++++++++++++---------- 1 file changed, 31 insertions(+), 14 deletions(-) diff --git a/drivers/platform/tegra/nvadsp/os.c b/drivers/platform/tegra/nvadsp/os.c index 7befb63b..7eb038af 100644 --- a/drivers/platform/tegra/nvadsp/os.c +++ b/drivers/platform/tegra/nvadsp/os.c @@ -63,6 +63,7 @@ #define EOT 0x04 /* End of Transmission */ #define SOH 0x01 /* Start of Header */ +#define BELL 0x07 /* Bell character */ #define ADSP_TAG "\n[ADSP OS]" @@ -85,10 +86,12 @@ #define DISABLE_MBOX2_FULL_INT 0x0 #define ENABLE_MBOX2_FULL_INT 0xFFFFFFFF -#define LOGGER_TIMEOUT 20 /* in ms */ +#define LOGGER_TIMEOUT 1 /* in ms */ #define ADSP_WFI_TIMEOUT 800 /* in ms */ #define LOGGER_COMPLETE_TIMEOUT 500 /* in ms */ +#define SEARCH_SOH_RETRY 2 + #define DUMP_BUFF 128 struct nvadsp_debug_log { @@ -120,6 +123,7 @@ struct nvadsp_os_data { size_t adsp_os_size; dma_addr_t app_alloc_addr; size_t app_size; + int num_start; /* registers number of time start called */ }; static struct nvadsp_os_data priv; @@ -150,11 +154,16 @@ static void (*nvadsp_tegra_adma_dump_ch_reg)(void); static int adsp_logger_open(struct inode *inode, struct file *file) { struct nvadsp_debug_log *logger = inode->i_private; - struct nvadsp_os_data *os_data; int ret = -EBUSY; char *start; + int i; - os_data = container_of(logger, struct nvadsp_os_data, logger); + mutex_lock(&priv.os_run_lock); + if (!priv.num_start) { + mutex_unlock(&priv.os_run_lock); + goto err_ret; + } + mutex_unlock(&priv.os_run_lock); /* * checks if os_opened decrements to zero and if returns true. If true @@ -165,13 +174,8 @@ static int adsp_logger_open(struct inode *inode, struct file *file) goto err_ret; } - ret = wait_event_interruptible(logger->wait_queue, - os_data->adsp_os_fw_loaded); - if (ret == -ERESTARTSYS) /* check if interrupted */ - goto err; - /* loop till writer is initilized with SOH */ - do { + for (i = 0; i < SEARCH_SOH_RETRY; i++) { ret = wait_event_interruptible_timeout(logger->wait_queue, memchr(logger->debug_ram_rdr, SOH, @@ -182,7 +186,14 @@ static int adsp_logger_open(struct inode *inode, struct file *file) start = memchr(logger->debug_ram_rdr, SOH, logger->debug_ram_sz); - } while (!start); + if (start) + break; + } + + if (i == SEARCH_SOH_RETRY) { + ret = -EINVAL; + goto err; + } /* maxdiff can be 0, therefore valid */ logger->ram_iter = start - logger->debug_ram_rdr; @@ -222,7 +233,6 @@ static ssize_t adsp_logger_read(struct file *file, char __user *buf, ssize_t ret_num_char = 1; char last_char; -loop: last_char = logger->debug_ram_rdr[logger->ram_iter]; if ((last_char != EOT) && (last_char != 0)) { @@ -230,7 +240,7 @@ loop: if ((last_char == '\n') || (last_char == '\r')) { if (copy_to_user(buf, ADSP_TAG, sizeof(ADSP_TAG) - 1)) { - dev_err(dev, "%s failed\n", __func__); + dev_err(dev, "%s failed in copying tag\n", __func__); ret_num_char = -EFAULT; goto exit; } @@ -239,7 +249,7 @@ loop: } else #endif if (copy_to_user(buf, &last_char, 1)) { - dev_err(dev, "%s failed\n", __func__); + dev_err(dev, "%s failed in copying character\n", __func__); ret_num_char = -EFAULT; goto exit; } @@ -256,8 +266,14 @@ loop: if (ret_num_char == -ERESTARTSYS) { goto exit; } - goto loop; + last_char = BELL; + if (copy_to_user(buf, &last_char, 1)) { + dev_err(dev, "%s failed in copying bell character\n", __func__); + ret_num_char = -EFAULT; + goto exit; + } + ret_num_char = 1; exit: return ret_num_char; } @@ -1610,6 +1626,7 @@ int nvadsp_os_start(void) } priv.os_running = drv_data->adsp_os_running = true; + priv.num_start++; #if defined(CONFIG_TEGRA_ADSP_FILEIO) if (!drv_data->adspff_init) { ret = adspff_init(); From c844c3a97366e913752e6099b91de6670f752415 Mon Sep 17 00:00:00 2001 From: Dipesh Gandhi Date: Tue, 6 Mar 2018 13:47:55 +0530 Subject: [PATCH 052/138] nvadsp: false trigger for adsp boot Change add spinlock protection for message enqueue to avoid possible race condition between enqueue and dequeue of ADSP OS message. Bug 2031075 Change-Id: Iad243fdf034db89a61e32411a8b03c817c19d81e Signed-off-by: Dipesh Gandhi Reviewed-on: https://git-master.nvidia.com/r/1613124 (cherry picked from commit 3d146a3ba4dbe4c1375d64d425ff0b9be7f7c272) Reviewed-on: https://git-master.nvidia.com/r/1669402 Reviewed-by: Bharat Nihalani Tested-by: Bharat Nihalani Reviewed-by: svc-mobile-coverity Reviewed-by: Automatic_Commit_Validation_User GVS: Gerrit_Virtual_Submit Tested-by: Ajay Nandakumar M Reviewed-by: mobile promotions Tested-by: mobile promotions --- drivers/platform/tegra/nvadsp/mailbox.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/platform/tegra/nvadsp/mailbox.c b/drivers/platform/tegra/nvadsp/mailbox.c index 06e2159c..753265a7 100644 --- a/drivers/platform/tegra/nvadsp/mailbox.c +++ b/drivers/platform/tegra/nvadsp/mailbox.c @@ -56,19 +56,21 @@ static void mboxq_destroy(struct nvadsp_mbox_queue *queue) static status_t mboxq_enqueue(struct nvadsp_mbox_queue *queue, uint32_t data) { + unsigned long flags; int ret = 0; if (is_mboxq_full(queue)) { ret = -EINVAL; goto out; } - + spin_lock_irqsave(&queue->lock, flags); if (is_mboxq_empty(queue)) complete_all(&queue->comp); queue->array[queue->tail] = data; queue->tail = (queue->tail + 1) & NVADSP_MBOX_QUEUE_SIZE_MASK; queue->count++; + spin_unlock_irqrestore(&queue->lock, flags); out: return ret; } From d56b0972b07c3f70456dee48c620a1469931ffd7 Mon Sep 17 00:00:00 2001 From: David Gilhooley Date: Thu, 29 Mar 2018 14:27:38 -0700 Subject: [PATCH 053/138] platform: nvadsp: prevent speculative load related leak Data can be speculatively loaded from memory and stay in cache even when bound check fails. This can lead to unintended information disclosure via side-channel analysis. To mitigate this problem, use array_index_nospec. Bug 2060857 CVE-2017-5753 Change-Id: I3b79ab2df0cff5eb7f94f8056cfdfb98ac69037a Signed-off-by: David Gilhooley Reviewed-on: https://git-master.nvidia.com/r/1684649 Reviewed-by: Bo Yan Reviewed-by: svc-mobile-coverity GVS: Gerrit_Virtual_Submit Reviewed-by: mobile promotions Tested-by: mobile promotions --- drivers/platform/tegra/nvadsp/mailbox.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/drivers/platform/tegra/nvadsp/mailbox.c b/drivers/platform/tegra/nvadsp/mailbox.c index 753265a7..3b197a6e 100644 --- a/drivers/platform/tegra/nvadsp/mailbox.c +++ b/drivers/platform/tegra/nvadsp/mailbox.c @@ -14,6 +14,7 @@ */ #include "dev.h" +#include #include #define NVADSP_MAILBOX_START 512 @@ -187,7 +188,9 @@ status_t nvadsp_mbox_open(struct nvadsp_mbox *mbox, uint16_t *mid, ret = -ERANGE; goto out; } - speculation_barrier(); + + *mid = array_index_nospec(*mid, NVADSP_MAILBOX_MAX); + if (nvadsp_drv_data->mboxes[*mid]) { pr_debug("%s: mailbox %d already opened.\n", __func__, *mid); From ca09364356570dd70d0338ca46d8d20f5ee75694 Mon Sep 17 00:00:00 2001 From: Dipesh Gandhi Date: Wed, 2 May 2018 16:19:38 +0530 Subject: [PATCH 054/138] nvadsp: clock cleanup for audio devices Change cleans up ape, apb2ape clocks from nvadsp device. These clocks will be controlled by parent aconnect bus device as for any audio use case to work we need these clocks. PM_RUNTIME on nvadsp will call runtime for aconnect which will enable these minimal required clocks. Jira EMA-641 Change-Id: I2f452ed1f28fc2ee79bd0f7eac686aeccd8d9513 Signed-off-by: Dipesh Gandhi Reviewed-on: https://git-master.nvidia.com/r/1688359 Reviewed-by: Automatic_Commit_Validation_User Reviewed-by: Uday Gupta Reviewed-by: svc-mobile-coverity GVS: Gerrit_Virtual_Submit Reviewed-by: Ajay Nandakumar M Reviewed-by: Sachin Nikam Reviewed-by: mobile promotions Tested-by: mobile promotions --- drivers/platform/tegra/nvadsp/dev-t18x.c | 47 ++++-------------------- 1 file changed, 7 insertions(+), 40 deletions(-) diff --git a/drivers/platform/tegra/nvadsp/dev-t18x.c b/drivers/platform/tegra/nvadsp/dev-t18x.c index 8aa8e744..d002e840 100644 --- a/drivers/platform/tegra/nvadsp/dev-t18x.c +++ b/drivers/platform/tegra/nvadsp/dev-t18x.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015-2017, NVIDIA Corporation. All rights reserved. + * Copyright (c) 2015-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 @@ -28,6 +28,9 @@ static int nvadsp_t18x_clocks_disable(struct platform_device *pdev) struct nvadsp_drv_data *drv_data = platform_get_drvdata(pdev); struct device *dev = &pdev->dev; + /* APE and APB2APE clocks which are required by NVADSP are controlled + * from parent ACONNECT bus driver + */ if (drv_data->adsp_clk) { clk_disable_unprepare(drv_data->adsp_clk); dev_dbg(dev, "adsp clocks disabled\n"); @@ -46,18 +49,6 @@ static int nvadsp_t18x_clocks_disable(struct platform_device *pdev) drv_data->adsp_neon_clk = NULL; } - if (drv_data->ape_clk) { - clk_disable_unprepare(drv_data->ape_clk); - dev_dbg(dev, "ape clock disabled\n"); - drv_data->ape_clk = NULL; - } - - if (drv_data->apb2ape_clk) { - clk_disable_unprepare(drv_data->apb2ape_clk); - dev_dbg(dev, "apb2ape clock disabled\n"); - drv_data->apb2ape_clk = NULL; - } - return 0; } @@ -66,20 +57,9 @@ static int nvadsp_t18x_clocks_enable(struct platform_device *pdev) struct nvadsp_drv_data *drv_data = platform_get_drvdata(pdev); struct device *dev = &pdev->dev; int ret = 0; - - drv_data->ape_clk = devm_clk_get(dev, "adsp.ape"); - if (IS_ERR_OR_NULL(drv_data->ape_clk)) { - dev_err(dev, "unable to find adsp.ape clock\n"); - ret = PTR_ERR(drv_data->ape_clk); - goto end; - } - ret = clk_prepare_enable(drv_data->ape_clk); - if (ret) { - dev_err(dev, "unable to enable adsp.ape clock\n"); - goto end; - } - dev_dbg(dev, "adsp.ape clock enabled\n"); - + /* APE and APB2APE clocks which are required by NVADSP are controlled + * from parent ACONNECT bus driver + */ drv_data->adsp_clk = devm_clk_get(dev, "adsp"); if (IS_ERR_OR_NULL(drv_data->adsp_clk)) { dev_err(dev, "unable to find adsp clock\n"); @@ -117,19 +97,6 @@ static int nvadsp_t18x_clocks_enable(struct platform_device *pdev) } dev_dbg(dev, "adsp neon clock enabled\n"); - drv_data->apb2ape_clk = devm_clk_get(dev, "adsp.apb2ape"); - if (IS_ERR_OR_NULL(drv_data->apb2ape_clk)) { - dev_err(dev, "unable to find adsp.apb2ape clk\n"); - ret = PTR_ERR(drv_data->apb2ape_clk); - goto end; - } - ret = clk_prepare_enable(drv_data->apb2ape_clk); - if (ret) { - dev_err(dev, "unable to enable adsp.apb2ape clock\n"); - goto end; - } - dev_dbg(dev, "adsp.apb2ape clock enabled\n"); - dev_dbg(dev, "all clocks enabled\n"); return 0; end: From 159d4e9e34f415db29e760df1bceb1553e510ef6 Mon Sep 17 00:00:00 2001 From: Uday Gupta Date: Sun, 20 May 2018 10:25:36 +0530 Subject: [PATCH 055/138] nvadsp: Increase static app queue size Increase static app init queue size between ADSP and CCPLEX to handle more APPs. Bug 2131818 Change-Id: Id8b376f312c3a3f04ec82abe69ec62994fcc0254 Signed-off-by: Uday Gupta Reviewed-on: https://git-master.nvidia.com/r/1726937 Tested-by: Hariharan Sivaraman Reviewed-by: Ajay Nandakumar M Reviewed-by: Automatic_Commit_Validation_User Reviewed-by: Dipesh Gandhi GVS: Gerrit_Virtual_Submit Reviewed-by: Nitin Pai Reviewed-by: mobile promotions Tested-by: mobile promotions --- drivers/platform/tegra/nvadsp/adsp_shared_struct.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/platform/tegra/nvadsp/adsp_shared_struct.h b/drivers/platform/tegra/nvadsp/adsp_shared_struct.h index 5859a04d..d8628f64 100644 --- a/drivers/platform/tegra/nvadsp/adsp_shared_struct.h +++ b/drivers/platform/tegra/nvadsp/adsp_shared_struct.h @@ -103,7 +103,7 @@ union app_complete_status_message { /*ADSP message pool structure */ -#define ADSP_MAX_MSGQ_SIZE 4096 +#define ADSP_MAX_MSGQ_SIZE 8192 #define ADSP_MAX_MSGQ_WSIZE (ADSP_MAX_MSGQ_SIZE / sizeof(int32_t)) #define ADSP_MSGQ_MAX_QUEUE_WSIZE \ (ADSP_MAX_MSGQ_WSIZE - (int32_t)MSGQ_HEADER_WSIZE) From beddbe937e36a96232ab7735d75e1ce2791ad5c7 Mon Sep 17 00:00:00 2001 From: Dara Ramesh Date: Thu, 7 Jun 2018 14:57:17 +0530 Subject: [PATCH 056/138] adsp: updated standard include path & clock_t api cputime64_to_clock_t is deprecated on kernel 4.14 as it is an architecture defined type so changing cputime64 to nsecs. Bug 200406253 Change-Id: I16bac09da698ca1b15130be69fb7c58c70d3ce94 Signed-off-by: Dara Ramesh Reviewed-on: https://git-master.nvidia.com/r/1742429 Reviewed-by: svc-mobile-coverity GVS: Gerrit_Virtual_Submit Reviewed-by: Ajay Nandakumar M Reviewed-by: Sachin Nikam Reviewed-by: mobile promotions Tested-by: mobile promotions --- drivers/platform/tegra/nvadsp/adsp_console_dbfs.c | 4 ++-- drivers/platform/tegra/nvadsp/adsp_dfs.c | 9 +++++++-- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/drivers/platform/tegra/nvadsp/adsp_console_dbfs.c b/drivers/platform/tegra/nvadsp/adsp_console_dbfs.c index 0708723e..4a23ba65 100644 --- a/drivers/platform/tegra/nvadsp/adsp_console_dbfs.c +++ b/drivers/platform/tegra/nvadsp/adsp_console_dbfs.c @@ -3,7 +3,7 @@ * * adsp mailbox console driver * - * Copyright (C) 2014-2016, NVIDIA Corporation. All rights reserved. + * 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 @@ -30,7 +30,7 @@ #include #include -#include +#include #include "dev.h" #include "adsp_console_ioctl.h" diff --git a/drivers/platform/tegra/nvadsp/adsp_dfs.c b/drivers/platform/tegra/nvadsp/adsp_dfs.c index 85b240bf..7fdb61ad 100644 --- a/drivers/platform/tegra/nvadsp/adsp_dfs.c +++ b/drivers/platform/tegra/nvadsp/adsp_dfs.c @@ -3,7 +3,7 @@ * * adsp dynamic frequency scaling * - * Copyright (C) 2014-2017, NVIDIA Corporation. All rights reserved. + * 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 @@ -21,7 +21,12 @@ #include #include #include +#include +#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 14, 0) #include +#else +#include +#endif #include #include "dev.h" @@ -607,7 +612,7 @@ static void dump_stats_table(struct seq_file *s, struct adsp_freq_stats *fstats) for (i = 0; i < fstats->state_num; i++) { seq_printf(s, "%lu %llu\n", (long unsigned int)(adsp_cpu_freq_table[i] / 1000), - cputime64_to_clock_t(fstats->time_in_state[i])); + nsec_to_clock_t(fstats->time_in_state[i])); } mutex_unlock(&policy_mutex); } From 6b338bab499f6da43b7713e379437588b4854feb Mon Sep 17 00:00:00 2001 From: Dara Ramesh Date: Wed, 20 Jun 2018 15:30:29 +0530 Subject: [PATCH 057/138] adsp: add dma_set_mask parse Add dma-mask parsing in device tree for adsp driver to start allocations start from the specified address and downwards. This will allows adsp binaries are placed at top of the IOVA space Bug 200385990 Bug 200406253 Change-Id: Id37579a78e6d788cfd722505a23e90f249cee5eb Signed-off-by: Dara Ramesh Reviewed-on: https://git-master.nvidia.com/r/1755675 Reviewed-by: Ajay Nandakumar M Reviewed-by: svc-mobile-coverity Reviewed-by: Dipesh Gandhi GVS: Gerrit_Virtual_Submit Reviewed-by: Ravindra Lokhande Reviewed-by: mobile promotions Tested-by: mobile promotions --- drivers/platform/tegra/nvadsp/os.c | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/drivers/platform/tegra/nvadsp/os.c b/drivers/platform/tegra/nvadsp/os.c index 7eb038af..66bc322b 100644 --- a/drivers/platform/tegra/nvadsp/os.c +++ b/drivers/platform/tegra/nvadsp/os.c @@ -39,6 +39,7 @@ #include #include #include +#include #include @@ -2038,8 +2039,11 @@ static ssize_t tegrafw_read_adsp(struct device *dev, int __init nvadsp_os_probe(struct platform_device *pdev) { struct nvadsp_drv_data *drv_data = platform_get_drvdata(pdev); - uint16_t com_mid = ADSP_COM_MBOX_ID; struct device *dev = &pdev->dev; +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 14, 0) + uint64_t dma_mask; +#endif + uint16_t com_mid = ADSP_COM_MBOX_ID; int ret = 0; priv.unit_fpga_reset_reg = drv_data->base_regs[UNIT_FPGA_RST]; @@ -2056,6 +2060,15 @@ int __init nvadsp_os_probe(struct platform_device *pdev) drv_data->deassert_adsp = __deassert_adsp; } +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 14, 0) + ret = of_property_read_u64(dev->of_node, "dma-mask", &dma_mask); + if (ret) { + dev_err(&pdev->dev, "Missing property dma-mask\n"); + goto end; + } else { + dma_set_mask_and_coherent(&pdev->dev, dma_mask); + } +#endif ret = nvadsp_os_init(pdev); if (ret) { dev_err(dev, "failed to init os\n"); From 385dc6327520f618a31c279be8d244f44530753c Mon Sep 17 00:00:00 2001 From: Hariharan Sivaraman Date: Mon, 8 May 2017 15:06:03 +0530 Subject: [PATCH 058/138] nvadsp: Add ADMA page info in hwmbox For virtualized configurations, add DMA page info for ADSP to read via HWMBOX. Remaining DMA pages will be unmapped by ADSP OS Jira EMA-414 Jira EMA-415 Change-Id: I67b7b2069aca07948e63436367d80a2baeaaf6ae Signed-off-by: Hariharan Sivaraman Reviewed-on: https://git-master/r/1477304 (cherry picked from commit 93f338df31af5894935754aac23bb003168f9ea0) Signed-off-by: Hariharan Sivaraman Reviewed-on: https://git-master.nvidia.com/r/1567625 Reviewed-by: Viraj Karandikar Reviewed-by: svc-mobile-coverity GVS: Gerrit_Virtual_Submit Reviewed-by: Nitin Pai Reviewed-by: mobile promotions Tested-by: mobile promotions --- drivers/platform/tegra/nvadsp/os-t18x.c | 56 +++++++++++++++++++++++-- drivers/platform/tegra/nvadsp/os.h | 21 +++++++++- 2 files changed, 73 insertions(+), 4 deletions(-) diff --git a/drivers/platform/tegra/nvadsp/os-t18x.c b/drivers/platform/tegra/nvadsp/os-t18x.c index a46e9d20..c575331e 100644 --- a/drivers/platform/tegra/nvadsp/os-t18x.c +++ b/drivers/platform/tegra/nvadsp/os-t18x.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2015-2017, NVIDIA Corporation. All rights reserved. + * Copyright (C) 2015-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 @@ -18,6 +18,7 @@ #include #include "dev.h" +#include "os.h" static void nvadsp_dbell_handler(void *data) { @@ -28,15 +29,64 @@ static void nvadsp_dbell_handler(void *data) } +/* Function to return the ADMA page number (0 indexed) used by guest */ +static int tegra_adma_query_dma_page(void) +{ + struct device_node *np = NULL; + int adma_page = 0, ret = 0, i = 0; + + static const char *compatible[] = { + "nvidia,tegra210-adma", + "nvidia,tegra210-adma-hv", + "nvidia,tegra186-adma", + "nvidia,tegra194-adma-hv", + NULL, + }; + + for (i = 0; compatible[i] != NULL; i++) { + np = of_find_compatible_node(NULL, NULL, compatible[i]); + if (np == NULL) + continue; + + /* + * In DT, "adma-page" property is 1 indexed + * If property is present, update return value to be 0 indexed + * If property is absent, return default value as page 0 + */ + ret = of_property_read_u32(np, "adma-page", &adma_page); + if (ret == 0) + adma_page = adma_page - 1; + + break; + } + + pr_info("%s: adma-page %d\n", __func__, adma_page); + return adma_page; +} + int nvadsp_os_t18x_init(struct platform_device *pdev) { struct device *dev = &pdev->dev; struct nvadsp_drv_data *drv_data = platform_get_drvdata(pdev); struct device_node *node = dev->of_node; - int ret; + int ret, adma_ch_page, val = 0; if (of_device_is_compatible(node, "nvidia,tegra18x-adsp-hv")) { - hwmbox_writel(1, drv_data->chip_data->hwmb.hwmbox5_reg); + + adma_ch_page = tegra_adma_query_dma_page(); + + /* Set ADSP to do decompression again */ + val = ADSP_CONFIG_DECOMPRESS_EN << ADSP_CONFIG_DECOMPRESS_SHIFT; + + /* Set ADSP to know its virtualized configuration */ + val = val | (ADSP_CONFIG_VIRT_EN << ADSP_CONFIG_VIRT_SHIFT); + + /* Encode DMA Page Bits with DMA page information */ + val = val | (adma_ch_page << ADSP_CONFIG_DMA_PAGE_SHIFT); + + /* Write to HWMBOX5 */ + hwmbox_writel(val, drv_data->chip_data->hwmb.hwmbox5_reg); + return 0; } diff --git a/drivers/platform/tegra/nvadsp/os.h b/drivers/platform/tegra/nvadsp/os.h index e89503c7..b4bf0b82 100644 --- a/drivers/platform/tegra/nvadsp/os.h +++ b/drivers/platform/tegra/nvadsp/os.h @@ -65,6 +65,25 @@ #define MODE_UND 0x1b #define MODE_SYS 0x1f +/* + * ADSP OS Config + * + * DECOMPRESS (Bit 0) : Set if ADSP FW needs to be decompressed + * VIRT CONFIG (Bit 1) : Set if virtualized configuration + * DMA PAGE (Bits 7:4) : Contains DMA page information + */ + +#define ADSP_CONFIG_DECOMPRESS_SHIFT 0 +#define ADSP_CONFIG_DECOMPRESS_EN 1 +#define ADSP_CONFIG_DECOMPRESS_MASK (1 << ADSP_CONFIG_DECOMPRESS_SHIFT) + +#define ADSP_CONFIG_VIRT_SHIFT 1 +#define ADSP_CONFIG_VIRT_EN 1 +#define ADSP_CONFIG_VIRT_MASK (1 << ADSP_CONFIG_VIRT_SHIFT) + +#define ADSP_CONFIG_DMA_PAGE_SHIFT 4 +#define ADSP_CONFIG_DMA_PAGE_MASK (0xF << ADSP_CONFIG_DMA_PAGE_SHIFT) + enum adsp_os_cmd { ADSP_OS_BOOT_COMPLETE, ADSP_OS_SUSPEND, @@ -175,4 +194,4 @@ void unload_adsp_module(struct adsp_module *); int allocate_memory_from_adsp(void **, unsigned int); bool is_adsp_dram_addr(u64); int load_adsp_static_apps(void); -#endif /* __TEGRA_NVADSP_OS_H */ +#endif /* __TEGRA_NVADSP_OS_H */ \ No newline at end of file From 0d5e6bc927661b84c0c962cad571daf92ade6b2a Mon Sep 17 00:00:00 2001 From: Mika Andersson Date: Wed, 4 Jul 2018 13:46:46 +0200 Subject: [PATCH 059/138] platform: nvadsp: Check input buf-size in adsp logger Adsp logger's read incorrectly expected the (remaining) size of the user-buf to be at least ADSP_TAG - 1 chars. That has been corrected. For simplicity there is no state machine introduced to make sure the ADSP_TAG is always complete in the log. In the case the tag needs to be added to user-buf without sufficient size it gets simply truncated. Bug: 2108805 Change-Id: Ifd137980be521ca3c0932248353f10ea0e92692a Signed-off-by: Mika Andersson Reviewed-on: https://git-master.nvidia.com/r/1770559 Reviewed-by: svc-mobile-coverity Reviewed-by: Uday Gupta Reviewed-by: Viraj Karandikar Reviewed-by: Ajay Nandakumar M Reviewed-by: Sandeep Trasi GVS: Gerrit_Virtual_Submit Reviewed-by: mobile promotions Tested-by: mobile promotions --- drivers/platform/tegra/nvadsp/os.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/drivers/platform/tegra/nvadsp/os.c b/drivers/platform/tegra/nvadsp/os.c index 66bc322b..11fec3a4 100644 --- a/drivers/platform/tegra/nvadsp/os.c +++ b/drivers/platform/tegra/nvadsp/os.c @@ -239,13 +239,14 @@ static ssize_t adsp_logger_read(struct file *file, char __user *buf, if ((last_char != EOT) && (last_char != 0)) { #if CONFIG_ADSP_DRAM_LOG_WITH_TAG if ((last_char == '\n') || (last_char == '\r')) { + size_t num_char = min(count, sizeof(ADSP_TAG) - 1); - if (copy_to_user(buf, ADSP_TAG, sizeof(ADSP_TAG) - 1)) { + if (copy_to_user(buf, ADSP_TAG, num_char)) { dev_err(dev, "%s failed in copying tag\n", __func__); ret_num_char = -EFAULT; goto exit; } - ret_num_char = sizeof(ADSP_TAG) - 1; + ret_num_char = num_char; } else #endif From 7d7819bcb243e352551e586840ad1f43ff7c8dd9 Mon Sep 17 00:00:00 2001 From: Uday Gupta Date: Fri, 22 Jun 2018 14:13:05 +0530 Subject: [PATCH 060/138] nvadsp: Add get file size support to adspff APP Add adsp_fsize support Bug 2060675 Change-Id: I288d09c290eab940547c76654fa5708efc40724d Signed-off-by: Hariharan Sivaraman Reviewed-on: https://git-master.nvidia.com/r/1775909 GVS: Gerrit_Virtual_Submit Tested-by: Uday Gupta Reviewed-by: Swati Sachdeva Reviewed-by: Viraj Karandikar Reviewed-by: Nitin Pai Reviewed-by: mobile promotions Tested-by: mobile promotions --- drivers/platform/tegra/nvadsp/adspff.c | 64 +++++++++++++++++++++++++- drivers/platform/tegra/nvadsp/adspff.h | 8 +++- 2 files changed, 70 insertions(+), 2 deletions(-) diff --git a/drivers/platform/tegra/nvadsp/adspff.c b/drivers/platform/tegra/nvadsp/adspff.c index 57b25ed8..210dd65e 100644 --- a/drivers/platform/tegra/nvadsp/adspff.c +++ b/drivers/platform/tegra/nvadsp/adspff.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, NVIDIA CORPORATION. All rights reserved. + * Copyright (c) 2016-2018, NVIDIA CORPORATION. All rights reserved. * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU General Public License, @@ -88,6 +88,23 @@ uint32_t file_read(struct file *file, unsigned long long *offset, return ret; } +uint32_t file_size(struct file *file) +{ + mm_segment_t oldfs; + uint32_t size = 0; + + oldfs = get_fs(); + set_fs(get_ds()); + + size = vfs_llseek(file, 0, SEEK_END); + + vfs_llseek(file, 0, SEEK_SET); + + set_fs(oldfs); + + return size; +} + /****************************************************************************** * ADSPFF file functions ******************************************************************************/ @@ -203,6 +220,46 @@ void adspff_fclose(struct work_struct *work) kfree(message); } +void adspff_fsize(struct work_struct *work) +{ + union adspff_message_t *msg_recv; + union adspff_message_t message; + struct file_struct *file = NULL; + int32_t ret = 0; + uint32_t size = 0; + + msg_recv = kzalloc(sizeof(union adspff_message_t), GFP_KERNEL); + msg_recv->msgq_msg.size = MSGQ_MSG_SIZE(struct ack_msg_t); + + message.msgq_msg.size = MSGQ_MSG_SIZE(struct fsize_msg_t); + ret = msgq_dequeue_message(&adspff->msgq_send.msgq, + (msgq_message_t *)&message); + + if (ret < 0) { + pr_err("fsize Dequeue failed %d.", ret); + kfree(msg_recv); + return; + } + file = (struct file_struct *)message.msg.payload.fsize_msg.file; + if (file) { + size = file_size(file->fp); + } + + /* send ack */ + msg_recv->msg.payload.ack_msg.size = size; + ret = msgq_queue_message(&adspff->msgq_recv.msgq, + (msgq_message_t *)msg_recv); + + if (ret < 0) { + pr_err("fsize Enqueue failed %d.", ret); + kfree(msg_recv); + return; + } + nvadsp_mbox_send(&rx_mbox, adspff_cmd_ack, + NVADSP_MBOX_SMSG, 0, 0); + kfree(msg_recv); +} + void adspff_fwrite(struct work_struct *work) { union adspff_message_t message; @@ -344,6 +401,7 @@ DECLARE_WORK(fopen_work, adspff_fopen); DECLARE_WORK(fwrite_work, adspff_fwrite); DECLARE_WORK(fread_work, adspff_fread); DECLARE_WORK(fclose_work, adspff_fclose); +DECLARE_WORK(fsize_work, adspff_fsize); /****************************************************************************** * ADSP mailbox message handler @@ -372,6 +430,10 @@ static int adspff_msg_handler(uint32_t msg, void *data) queue_work(adspff_wq, &fread_work); } break; + case adspff_cmd_fsize: { + queue_work(adspff_wq, &fsize_work); + } + break; default: pr_err("Unsupported mbox msg %d.\n", msg); } diff --git a/drivers/platform/tegra/nvadsp/adspff.h b/drivers/platform/tegra/nvadsp/adspff.h index 12715372..3caf7919 100644 --- a/drivers/platform/tegra/nvadsp/adspff.h +++ b/drivers/platform/tegra/nvadsp/adspff.h @@ -1,7 +1,7 @@ /* * tegra_adspff.h - Shared ADSPFF interface between Tegra ADSP File * System driver and ADSP side user space code. -* Copyright (c) 2016 NVIDIA Corporation. All rights reserved. +* Copyright (c) 2016-2018 NVIDIA Corporation. All rights reserved. * * NVIDIA Corporation and its licensors retain all intellectual property * and proprietary rights in and to this software, related documentation @@ -45,6 +45,7 @@ enum adspff_mbx_cmd { adspff_cmd_fread, adspff_cmd_fopen_recv, adspff_cmd_ack, + adspff_cmd_fsize, }; @@ -76,6 +77,10 @@ struct fopen_recv_msg_t { int64_t file; }; +struct fsize_msg_t { + int64_t file; +}; + struct ack_msg_t { int32_t size; }; @@ -93,6 +98,7 @@ union adspff_message_t { struct fclose_msg_t fclose_msg; struct fopen_recv_msg_t fopen_recv_msg; struct ack_msg_t ack_msg; + struct fsize_msg_t fsize_msg; } payload; } msg; }; From 6a3e481f365c85e3acae61ed0b405f8177a0ee99 Mon Sep 17 00:00:00 2001 From: Dipesh Gandhi Date: Mon, 12 Feb 2018 18:21:37 +0530 Subject: [PATCH 061/138] nvadsp: prevent race in os_queue for app Change add spinlock to nvadsp app start and init functions for os_queue write protection. It make sure only one app from different context can update queue at given time. Also added some debug info to adsp driver for providing extra info for debugging purpose. Bug 2060866 Signed-off-by: Dipesh Gandhi Change-Id: If1bbfdb233d2af984f62bb02ed515eec1ab8a492 Reviewed-on: https://git-master.nvidia.com/r/1655974 (cherry picked from commit 7edeb242aba4cabc517b6e270b254777e3ea66ea) Reviewed-on: https://git-master.nvidia.com/r/1775133 Reviewed-by: svc-mobile-coverity Reviewed-by: Hariharan Sivaraman Reviewed-by: Viraj Karandikar GVS: Gerrit_Virtual_Submit Reviewed-by: Nitin Pai Reviewed-by: mobile promotions Tested-by: mobile promotions --- drivers/platform/tegra/nvadsp/app.c | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/drivers/platform/tegra/nvadsp/app.c b/drivers/platform/tegra/nvadsp/app.c index 6268ce79..6fafdb80 100644 --- a/drivers/platform/tegra/nvadsp/app.c +++ b/drivers/platform/tegra/nvadsp/app.c @@ -3,7 +3,7 @@ * * ADSP OS App management * - * Copyright (C) 2014-2017, NVIDIA Corporation. All rights reserved. + * 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 @@ -616,6 +616,7 @@ nvadsp_app_info_t __must_check *nvadsp_app_init(nvadsp_app_handle_t handle, nvadsp_app_info_t *app; msgq_t *msgq_send; int *state; + unsigned long flags; if (IS_ERR_OR_NULL(priv.pdev)) { pr_err("ADSP Driver is not initialized\n"); @@ -648,7 +649,10 @@ nvadsp_app_info_t __must_check *nvadsp_app_init(nvadsp_app_handle_t handle, data->app_init.message = ADSP_APP_INIT; message->msgq_msg.size = MSGQ_MSG_PAYLOAD_WSIZE(*message); + + spin_lock_irqsave(&drv_data->mbox_lock, flags); msgq_queue_message(msgq_send, &message->msgq_msg); + spin_unlock_irqrestore(&drv_data->mbox_lock, flags); if (app->return_status) { state = (int *)&app->state; @@ -674,6 +678,7 @@ static int start_app_on_adsp(nvadsp_app_info_t *app, struct nvadsp_drv_data *drv_data; msgq_t *msgq_send; int *state; + unsigned long flags; drv_data = platform_get_drvdata(priv.pdev); shared_mem = drv_data->shared_adsp_os_data; @@ -681,7 +686,10 @@ static int start_app_on_adsp(nvadsp_app_info_t *app, msgq_send = &msg_pool->app_loader_send_message.msgq; message->msgq_msg.size = MSGQ_MSG_PAYLOAD_WSIZE(*message); + + spin_lock_irqsave(&drv_data->mbox_lock, flags); msgq_queue_message(msgq_send, &message->msgq_msg); + spin_unlock_irqrestore(&drv_data->mbox_lock, flags); state = (int *)&app->state; *state = NVADSP_APP_STATE_STARTED; From e23ed464ef938f76504084913670dea9bd671c6b Mon Sep 17 00:00:00 2001 From: Dipesh Gandhi Date: Fri, 6 Apr 2018 14:52:17 +0530 Subject: [PATCH 062/138] nvadsp: adsp suspend/resume handling Change adds pm ops in nvadsp driver to support suspend/resume functionality. Without the change, if playback/capture is active when system suspend occurs, the nvadsp driver is never suspended and hence the clocks are not disabled. In terms of power sequence device driver registered as late_suspend are suspended first followed by noirq pm ops registred devices. adsp_audio device node is registered for late suspend. To make sure adsp_audio suspend is called first nvadsp is registered under noirq pm ops Jira EMA-641 Change-Id: I3a6c3d75cbafc9e25771c4207113db1cd8066d09 Signed-off-by: Dipesh Gandhi Reviewed-on: https://git-master.nvidia.com/r/1689900 Reviewed-by: Ajay Nandakumar M Reviewed-by: svc-mobile-coverity Reviewed-by: Uday Gupta Reviewed-by: Automatic_Commit_Validation_User Reviewed-by: Jinyoung Park GVS: Gerrit_Virtual_Submit Reviewed-by: Ravindra Lokhande Reviewed-by: Sachin Nikam Reviewed-by: mobile promotions Tested-by: mobile promotions --- drivers/platform/tegra/nvadsp/dev.c | 31 ++++++++++++++++++----------- 1 file changed, 19 insertions(+), 12 deletions(-) diff --git a/drivers/platform/tegra/nvadsp/dev.c b/drivers/platform/tegra/nvadsp/dev.c index 6560297b..93b3f4e8 100644 --- a/drivers/platform/tegra/nvadsp/dev.c +++ b/drivers/platform/tegra/nvadsp/dev.c @@ -56,17 +56,6 @@ static int __init adsp_debug_init(struct nvadsp_drv_data *drv_data) } #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) @@ -106,8 +95,26 @@ static int nvadsp_runtime_idle(struct device *dev) } #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_SYSTEM_SLEEP_PM_OPS(nvadsp_suspend, nvadsp_resume) + SET_NOIRQ_SYSTEM_SLEEP_PM_OPS(nvadsp_suspend, nvadsp_resume) SET_RUNTIME_PM_OPS(nvadsp_runtime_suspend, nvadsp_runtime_resume, nvadsp_runtime_idle) }; From 989d49990e0dc0b826758ab441e6af7c51a3aaf6 Mon Sep 17 00:00:00 2001 From: Niranjan Dighe Date: Thu, 18 Oct 2018 16:28:32 +0530 Subject: [PATCH 063/138] nvadsp: adspff: Fix kernel crash if file not found Check file->fp for NULL before returning the file handle to ADSP in adspff_fopen, which otherwise results in a kernel crash on successive file operations using the same file handle if the requested file does not exist. Jira: EMA-1152 Change-Id: I307cc82dd3a2135039b819d1f746ee8164568110 Signed-off-by: Niranjan Dighe Reviewed-on: https://git-master.nvidia.com/r/1929809 Reviewed-by: Automatic_Commit_Validation_User GVS: Gerrit_Virtual_Submit Reviewed-by: Uday Gupta Reviewed-by: Hariharan Sivaraman Reviewed-by: Nitin Pai Reviewed-by: mobile promotions Tested-by: mobile promotions --- drivers/platform/tegra/nvadsp/adspff.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/drivers/platform/tegra/nvadsp/adspff.c b/drivers/platform/tegra/nvadsp/adspff.c index 210dd65e..f76d5354 100644 --- a/drivers/platform/tegra/nvadsp/adspff.c +++ b/drivers/platform/tegra/nvadsp/adspff.c @@ -11,6 +11,8 @@ * more details. */ +#define pr_fmt(fmt) "adspff: " fmt + #include #include #include @@ -175,6 +177,13 @@ void adspff_fopen(struct work_struct *work) file->wr_offset = 0; file->rd_offset = 0; + if (!(file->fp)) { + kfree(file); + file = NULL; + pr_err("File not found - %s\n", + (const char *) message->msg.payload.fopen_msg.fname); + } + msg_recv->msgq_msg.size = MSGQ_MSG_SIZE(struct fopen_recv_msg_t); msg_recv->msg.payload.fopen_recv_msg.file = (int64_t)file; From ee8de73c70be25d0a4339b9c6ac582b074e86103 Mon Sep 17 00:00:00 2001 From: Hariharan Sivaraman Date: Fri, 21 Dec 2018 16:22:56 +0530 Subject: [PATCH 064/138] nvadsp: Clear HWBOX0 for ADSP guest reset handling During ADSP playback, if HWMBOX0 is high and guest reset is done, after reset ADSP is unable to communicate with kernel driver since the interrupt line remains High thereby not generating interrupt (edge triggered). Jira EMA-1178 Change-Id: Ifb1283c7286ed944f243e7d92dfe4db40bcd0ba2 Signed-off-by: Hariharan Sivaraman Reviewed-on: https://git-master.nvidia.com/r/1978017 Reviewed-by: Uday Gupta Reviewed-by: svc-mobile-coverity Reviewed-by: svc-mobile-misra Reviewed-by: Automatic_Commit_Validation_User GVS: Gerrit_Virtual_Submit Reviewed-by: Dipesh Gandhi Reviewed-by: Sachin Nikam Reviewed-by: mobile promotions Tested-by: mobile promotions --- drivers/platform/tegra/nvadsp/os-t18x.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/platform/tegra/nvadsp/os-t18x.c b/drivers/platform/tegra/nvadsp/os-t18x.c index c575331e..0d5e6919 100644 --- a/drivers/platform/tegra/nvadsp/os-t18x.c +++ b/drivers/platform/tegra/nvadsp/os-t18x.c @@ -87,6 +87,9 @@ int nvadsp_os_t18x_init(struct platform_device *pdev) /* Write to HWMBOX5 */ hwmbox_writel(val, drv_data->chip_data->hwmb.hwmbox5_reg); + /* Clear HWMBOX0 for ADSP Guest reset handling */ + hwmbox_writel(0, drv_data->chip_data->hwmb.hwmbox0_reg); + return 0; } From 99fa48e09c04ab512e309c69011f321b2c7903c4 Mon Sep 17 00:00:00 2001 From: Anuj Gangwar Date: Fri, 23 Nov 2018 10:43:17 +0530 Subject: [PATCH 065/138] include: uapi: move adsp user-interface header Move the driver adsp_console_ioctl.h user-interface headers from drivers/platform/tegra/nvadsp/adsp_console_ioctl.h to include/uapi/misc/ Change the path for above headers in the dependent files. Bug 2062672 Change-Id: I7337c0fc03a75101b4276773368a7c4eccd25960 Signed-off-by: Anuj Gangwar Reviewed-on: https://git-master.nvidia.com/r/1956612 Reviewed-by: svc-mobile-coverity Reviewed-by: svc-mobile-misra GVS: Gerrit_Virtual_Submit Reviewed-by: Stephen Warren Reviewed-by: Bibek Basu Reviewed-by: mobile promotions Tested-by: mobile promotions --- .../platform/tegra/nvadsp/adsp_console_dbfs.c | 4 +- .../tegra/nvadsp/adsp_console_ioctl.h | 55 ------------------- 2 files changed, 2 insertions(+), 57 deletions(-) delete mode 100644 drivers/platform/tegra/nvadsp/adsp_console_ioctl.h diff --git a/drivers/platform/tegra/nvadsp/adsp_console_dbfs.c b/drivers/platform/tegra/nvadsp/adsp_console_dbfs.c index 4a23ba65..64cfc3df 100644 --- a/drivers/platform/tegra/nvadsp/adsp_console_dbfs.c +++ b/drivers/platform/tegra/nvadsp/adsp_console_dbfs.c @@ -3,7 +3,7 @@ * * adsp mailbox console driver * - * Copyright (C) 2014-2018, NVIDIA Corporation. All rights reserved. + * 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 @@ -29,11 +29,11 @@ #include #include #include +#include #include #include "dev.h" -#include "adsp_console_ioctl.h" #include "adsp_console_dbfs.h" #define USE_RUN_APP_API diff --git a/drivers/platform/tegra/nvadsp/adsp_console_ioctl.h b/drivers/platform/tegra/nvadsp/adsp_console_ioctl.h deleted file mode 100644 index 8d0b2d0f..00000000 --- a/drivers/platform/tegra/nvadsp/adsp_console_ioctl.h +++ /dev/null @@ -1,55 +0,0 @@ -/* - * adsp_console_ioctl.h - * - * A header file for adsp console driver - * - * Copyright (C) 2014-2016, 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. - * - */ - -#ifndef ADSP_CNSL_IOCTL_H -#define ADSP_CNSL_IOCTL_H -#include - -#if !defined(NVADSP_NAME_SZ) -#define NVADSP_NAME_SZ 128 -#endif - -#define NVADSP_NAME_SZ_MAX (NVADSP_NAME_SZ - 1) - -#if !defined(ARGV_SIZE_IN_WORDS) -#define ARGV_SIZE_IN_WORDS 128 -#endif - -#define NV_ADSP_CONSOLE_MAGIC 'q' - -struct adsp_consol_run_app_arg_t { - char app_name[NVADSP_NAME_SZ]; - char app_path[NVADSP_NAME_SZ]; - uint32_t args[ARGV_SIZE_IN_WORDS + 1]; - uint64_t ctx1; - uint64_t ctx2; -}; - -#define ADSP_CNSL_LOAD _IO(NV_ADSP_CONSOLE_MAGIC, 0x01) -#define ADSP_CNSL_CLR_BUFFER _IO(NV_ADSP_CONSOLE_MAGIC, 0x02) -#define ADSP_CNSL_PUT_DATA _IOW(NV_ADSP_CONSOLE_MAGIC, 0x03, uint32_t *) -#define ADSP_CNSL_RUN_APP _IOWR(NV_ADSP_CONSOLE_MAGIC, 0x04,\ - struct adsp_consol_run_app_arg_t *) -#define ADSP_CNSL_STOP_APP _IOWR(NV_ADSP_CONSOLE_MAGIC, 0x05,\ - struct adsp_consol_run_app_arg_t *) -#define ADSP_CNSL_OPN_MBX _IOW(NV_ADSP_CONSOLE_MAGIC, 0x06, void *) -#define ADSP_CNSL_CLOSE_MBX _IO(NV_ADSP_CONSOLE_MAGIC, 0x07) -#define ADSP_CNSL_PUT_MBX _IOW(NV_ADSP_CONSOLE_MAGIC, 0x08, uint32_t *) -#define ADSP_CNSL_GET_MBX _IOR(NV_ADSP_CONSOLE_MAGIC, 0x09, uint32_t *) - -#endif From 27c5e1cba3ed9ac562352e9c0761eb93bb8381fc Mon Sep 17 00:00:00 2001 From: Niranjan Dighe Date: Fri, 1 Mar 2019 16:10:07 +0530 Subject: [PATCH 066/138] adsp: fix tlv read write issues Change fixes problems caused in tlv read and write synchronization with other messages - Add separate spinlock completions for tlv read - Add separate spinlock completions for tlv write - Add separate ack for raw (tlv) messages - Print msgq indexes when queue or dequeue fails Bug 2420196 Change-Id: I405e3a9939ec804dc65875d900ba635d2dc15ee3 Signed-off-by: Hariharan Sivaraman Reviewed-on: https://git-master.nvidia.com/r/1975072 (cherry picked from commit 1df184d908c84c8f4e15b48cdd9ada90da313169) Signed-off-by: Niranjan Dighe Reviewed-on: https://git-master.nvidia.com/r/1986443 Reviewed-by: svc-mobile-coverity GVS: Gerrit_Virtual_Submit Reviewed-by: Uday Gupta Reviewed-by: Nitin Pai Reviewed-by: mobile promotions Tested-by: mobile promotions --- drivers/platform/tegra/nvadsp/msgq.c | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/drivers/platform/tegra/nvadsp/msgq.c b/drivers/platform/tegra/nvadsp/msgq.c index 427f2fdf..1060eec2 100644 --- a/drivers/platform/tegra/nvadsp/msgq.c +++ b/drivers/platform/tegra/nvadsp/msgq.c @@ -1,7 +1,7 @@ /* * ADSP circular message queue * - * Copyright (c) 2014-2016, NVIDIA CORPORATION. All rights reserved. + * Copyright (c) 2014-2019, NVIDIA CORPORATION. All rights reserved. * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU General Public License, @@ -65,6 +65,7 @@ EXPORT_SYMBOL(msgq_init); int32_t msgq_queue_message(msgq_t *msgq, const msgq_message_t *message) { int32_t ret = 0; + if (msgq && message) { int32_t ri = msgq->read_index; int32_t wi = msgq->write_index; @@ -80,6 +81,9 @@ int32_t msgq_queue_message(msgq_t *msgq, const msgq_message_t *message) if (qsize <= msize) { /* don't allow read == write */ + pr_err("%s failed: msgq ri: %d, wi %d, msg size %d\n", + __func__, msgq->read_index, + msgq->write_index, message->size); ret = -ENOSPC; } else if (msize < qremainder) { msgq_wmemcpy(first, message, msize); @@ -135,6 +139,8 @@ int32_t msgq_dequeue_message(msgq_t *msgq, msgq_message_t *message) /* empty queue */ if (message) message->size = 0; + pr_err("%s failed: msgq ri: %d, wi %d; NO MSG\n", + __func__, msgq->read_index, msgq->write_index); ret = -ENOMSG; } else if (!message) { /* no input buffer, discard top message */ @@ -142,6 +148,8 @@ int32_t msgq_dequeue_message(msgq_t *msgq, msgq_message_t *message) msgq->read_index = ri < msgq->size ? ri : ri - msgq->size; } else if (message->size < msg->size) { /* return buffer too small */ + pr_err("%s failed: msgq ri: %d, wi %d, NO SPACE\n", + __func__, msgq->read_index, msgq->write_index); message->size = msg->size; ret = -ENOSPC; } else { From 9b6d089a5e9a3d4306f4ded1744eaa6255932ac0 Mon Sep 17 00:00:00 2001 From: Nicolin Chen Date: Wed, 27 Mar 2019 17:37:39 -0700 Subject: [PATCH 067/138] platform: nvadsp: Deprecate dma_alloc_at_coherent The dma_alloc_at_coherent is implemented with a hack in the iommu core code, which is unlikely able to get upstream. So this patch removes its callers in the nvadsp code, and then adds an alternative helper function to do something similar. The dma_alloc_at_coherent was used to allocate a DMA buffer and to map it to a specified iova address. And this can be done by remapping a normal DMA buffer to the iova address. Bug 200444660 Change-Id: Ic90aec550daa29df69d97c4b230c8f9cc1519166 Signed-off-by: Nicolin Chen Reviewed-on: https://git-master.nvidia.com/r/2083356 Reviewed-by: Krishna Reddy Reviewed-by: Automatic_Commit_Validation_User Reviewed-by: mobile promotions Tested-by: mobile promotions --- drivers/platform/tegra/nvadsp/dev.h | 6 +- drivers/platform/tegra/nvadsp/os.c | 108 +++++++++++++++++++++++++++- 2 files changed, 110 insertions(+), 4 deletions(-) diff --git a/drivers/platform/tegra/nvadsp/dev.h b/drivers/platform/tegra/nvadsp/dev.h index 91d64749..ac68a480 100644 --- a/drivers/platform/tegra/nvadsp/dev.h +++ b/drivers/platform/tegra/nvadsp/dev.h @@ -3,7 +3,7 @@ * * A header file for Host driver for ADSP and APE * - * Copyright (C) 2014-2017, NVIDIA Corporation. All rights reserved. + * 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 @@ -22,6 +22,7 @@ #include #include #include +#include #include #include @@ -214,6 +215,9 @@ struct nvadsp_drv_data { struct tegra_bwmgr_client *bwmgr; u32 evp_base[ADSP_EVP_END]; + /* Used to reserve iova */ + struct iova_domain iovad; + const struct nvadsp_chipdata *chip_data; }; diff --git a/drivers/platform/tegra/nvadsp/os.c b/drivers/platform/tegra/nvadsp/os.c index 11fec3a4..d2edc106 100644 --- a/drivers/platform/tegra/nvadsp/os.c +++ b/drivers/platform/tegra/nvadsp/os.c @@ -5,7 +5,7 @@ * Copyright (C) 2011 Texas Instruments, Inc. * Copyright (C) 2011 Google, Inc. * - * Copyright (C) 2014-2018, NVIDIA Corporation. All rights reserved. + * 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 @@ -22,6 +22,7 @@ #include #include #include +#include #include #include #include @@ -610,6 +611,88 @@ static int nvadsp_os_elf_load(const struct firmware *fw) return ret; } +/** + * Allocate a dma buffer and map it to a specified iova + * Return valid cpu virtual address on success or NULL on failure + */ +static void *nvadsp_dma_alloc_and_map_at(struct platform_device *pdev, + size_t size, dma_addr_t iova, + gfp_t flags) +{ + struct iommu_domain *domain = iommu_get_domain_for_dev(&pdev->dev); + struct nvadsp_drv_data *drv_data = platform_get_drvdata(pdev); + unsigned long shift = __ffs(domain->pgsize_bitmap); + unsigned long pg_size = 1UL << shift; + unsigned long mp_size = pg_size; + struct device *dev = &pdev->dev; + dma_addr_t tmp_iova, offset; + phys_addr_t pa, pa_new; + struct iova *iova_pfn; + void *cpu_va; + int ret; + + /* Reserve iova range */ + iova_pfn = alloc_iova(&drv_data->iovad, size >> shift, + (iova + size - pg_size) >> shift, false); + if (!iova_pfn || (iova_pfn->pfn_lo << shift) != iova) { + dev_err(dev, "failed to reserve iova at 0x%llx size 0x%lx\n", + iova, size); + return NULL; + } + + /* Allocate a memory first and get a tmp_iova */ + cpu_va = dma_alloc_coherent(dev, size, &tmp_iova, flags); + if (!cpu_va) + goto fail_dma_alloc; + + /* Luckily hitting the target iova, no need to remap */ + if (tmp_iova == iova) + return cpu_va; + + /* Use tmp_iova to remap non-contiguous pages to the desired iova */ + for (offset = 0; offset < size; offset += mp_size) { + dma_addr_t cur_iova = tmp_iova + offset; + + mp_size = pg_size; + pa = iommu_iova_to_phys(domain, cur_iova); + /* Checking if next physical addresses are contiguous */ + for ( ; offset + mp_size < size; mp_size += pg_size) { + pa_new = iommu_iova_to_phys(domain, cur_iova + mp_size); + if (pa + mp_size != pa_new) + break; + } + + /* Remap the contiguous physical addresses together */ + ret = iommu_map(domain, iova + offset, pa, mp_size, + IOMMU_READ | IOMMU_WRITE); + if (ret) { + dev_err(dev, "failed to map pa %llx va %llx size %lx\n", + pa, iova + offset, mp_size); + goto fail_map; + } + + /* Verify if the new iova is correctly mapped */ + if (pa != iommu_iova_to_phys(domain, iova + offset)) { + dev_err(dev, "mismatched pa 0x%llx <-> 0x%llx\n", + pa, iommu_iova_to_phys(domain, iova + offset)); + goto fail_map; + } + } + + /* Unmap the tmp_iova since target iova is linked */ + iommu_unmap(domain, tmp_iova, size); + + return cpu_va; + +fail_map: + iommu_unmap(domain, iova, offset); + dma_free_coherent(dev, size, cpu_va, tmp_iova); +fail_dma_alloc: + __free_iova(&drv_data->iovad, iova_pfn); + + return NULL; +} + static int allocate_memory_for_adsp_os(void) { struct platform_device *pdev = priv.pdev; @@ -626,7 +709,7 @@ static int allocate_memory_for_adsp_os(void) addr = priv.adsp_os_addr; size = priv.adsp_os_size; #if defined(CONFIG_TEGRA_NVADSP_ON_SMMU) - dram_va = dma_alloc_at_coherent(dev, size, &addr, GFP_KERNEL); + dram_va = nvadsp_dma_alloc_and_map_at(pdev, size, addr, GFP_KERNEL); if (!dram_va) { dev_err(dev, "unable to allocate SMMU pages\n"); ret = -ENOMEM; @@ -648,9 +731,13 @@ end: static void deallocate_memory_for_adsp_os(struct device *dev) { #if defined(CONFIG_TEGRA_NVADSP_ON_SMMU) + struct nvadsp_drv_data *drv_data = platform_get_drvdata(priv.pdev); + struct iova_domain *iovad = &drv_data->iovad; void *va = nvadsp_da_to_va_mappings(priv.adsp_os_addr, priv.adsp_os_size); dma_free_coherent(dev, priv.adsp_os_addr, va, priv.adsp_os_size); + free_iova(iovad, iova_pfn(iovad, priv.adsp_os_addr)); + put_iova_domain(iovad); #endif } @@ -685,7 +772,7 @@ static int __nvadsp_os_secload(struct platform_device *pdev) struct device *dev = &pdev->dev; void *dram_va; - dram_va = dma_alloc_at_coherent(dev, size, &addr, GFP_KERNEL); + dram_va = nvadsp_dma_alloc_and_map_at(pdev, size, addr, GFP_KERNEL); if (!dram_va) { dev_err(dev, "unable to allocate shared region\n"); return -ENOMEM; @@ -756,6 +843,9 @@ int nvadsp_os_load(void) struct nvadsp_drv_data *drv_data; struct device *dev; int ret = 0; +#ifdef CONFIG_TEGRA_NVADSP_ON_SMMU + u32 addr, size; +#endif if (!priv.pdev) { pr_err("ADSP Driver is not initialized\n"); @@ -770,6 +860,18 @@ int nvadsp_os_load(void) drv_data = platform_get_drvdata(priv.pdev); dev = &priv.pdev->dev; +#ifdef CONFIG_TEGRA_NVADSP_ON_SMMU + if (drv_data->adsp_os_secload) { + addr = drv_data->adsp_mem[ACSR_ADDR]; + size = drv_data->adsp_mem[ACSR_SIZE]; + } else { + addr = drv_data->adsp_mem[ADSP_OS_ADDR]; + size = drv_data->adsp_mem[ADSP_OS_SIZE]; + } + init_iova_domain(&drv_data->iovad, PAGE_SIZE, + addr >> PAGE_SHIFT, (addr + size) >> PAGE_SHIFT); +#endif + if (drv_data->adsp_os_secload) { dev_info(dev, "ADSP OS firmware already loaded\n"); ret = __nvadsp_os_secload(priv.pdev); From df7a7fb5ab9b4f4a8c05688b8726fc19bd322c3c Mon Sep 17 00:00:00 2001 From: Nicolin Chen Date: Mon, 15 Apr 2019 11:47:21 -0700 Subject: [PATCH 068/138] platform: nvadsp: Fix mismatched mutex lock/unlock The function hasn't grabbed the mutex_lock yet at first condition check. So this patch changes the exit path to return directly instead of jumping to the mutex_unlock. Bug 200444660 Change-Id: I122fc9cd145a5de932b4ae55d88c878231926072 Signed-off-by: Nicolin Chen Reviewed-on: https://git-master.nvidia.com/r/2098217 Reviewed-by: Automatic_Commit_Validation_User GVS: Gerrit_Virtual_Submit Reviewed-by: Krishna Reddy Reviewed-by: mobile promotions Tested-by: mobile promotions --- drivers/platform/tegra/nvadsp/os.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/platform/tegra/nvadsp/os.c b/drivers/platform/tegra/nvadsp/os.c index d2edc106..e91f9540 100644 --- a/drivers/platform/tegra/nvadsp/os.c +++ b/drivers/platform/tegra/nvadsp/os.c @@ -849,8 +849,7 @@ int nvadsp_os_load(void) if (!priv.pdev) { pr_err("ADSP Driver is not initialized\n"); - ret = -EINVAL; - goto end; + return -EINVAL; } mutex_lock(&priv.fw_load_lock); From 5759b9d6d437eacbab474a076e332d1edab10009 Mon Sep 17 00:00:00 2001 From: Nicolin Chen Date: Mon, 15 Apr 2019 17:29:13 -0700 Subject: [PATCH 069/138] platform: nvadsp: Fix dma_free_coherent() call The parameters of dma_free_coherent() should be in order of: 1) Device pointer 2) Size 3) CPU virtual address 4) Device address However, the dma_free_coherent() here mixes with the size with the device address which will trigger kernel bugs. So this patch fixes the bug by exchanging those two. Bug 200444660 Change-Id: Ib0b7855be3868908a4fd59beb938f8bec9e6daee Signed-off-by: Nicolin Chen Reviewed-on: https://git-master.nvidia.com/r/2098218 Reviewed-by: Automatic_Commit_Validation_User GVS: Gerrit_Virtual_Submit Reviewed-by: Krishna Reddy Reviewed-by: mobile promotions Tested-by: mobile promotions --- drivers/platform/tegra/nvadsp/os.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/platform/tegra/nvadsp/os.c b/drivers/platform/tegra/nvadsp/os.c index e91f9540..5996973b 100644 --- a/drivers/platform/tegra/nvadsp/os.c +++ b/drivers/platform/tegra/nvadsp/os.c @@ -735,7 +735,7 @@ static void deallocate_memory_for_adsp_os(struct device *dev) struct iova_domain *iovad = &drv_data->iovad; void *va = nvadsp_da_to_va_mappings(priv.adsp_os_addr, priv.adsp_os_size); - dma_free_coherent(dev, priv.adsp_os_addr, va, priv.adsp_os_size); + dma_free_coherent(dev, priv.adsp_os_size, va, priv.adsp_os_addr); free_iova(iovad, iova_pfn(iovad, priv.adsp_os_addr)); put_iova_domain(iovad); #endif From 65f4df1dc48cd1b5899c3ef04e91a8eddbe5bf4b Mon Sep 17 00:00:00 2001 From: Nicolin Chen Date: Fri, 12 Apr 2019 16:16:36 -0700 Subject: [PATCH 070/138] platform: nvadsp: Drop KERNEL_VERSION check The dma-mask could be applied to an older kernel as well. And this is essential for nvadsp driver to use the newer nvadsp_dma_alloc_and_map_at() function. Though downstream kernel 4.9 still has dma_alloc_at_coherent() support, it would be cleaner to have nvadsp code using a unified way instead of adding older dma_alloc_at_coherent() back with another KERNEL_VERSION check. Bug 200444660 Change-Id: I3968155c7439789a398ca499e70598ce1385737a Signed-off-by: Nicolin Chen Reviewed-on: https://git-master.nvidia.com/r/2096705 Reviewed-by: Automatic_Commit_Validation_User GVS: Gerrit_Virtual_Submit Reviewed-by: Krishna Reddy Reviewed-by: mobile promotions Tested-by: mobile promotions --- drivers/platform/tegra/nvadsp/os.c | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/drivers/platform/tegra/nvadsp/os.c b/drivers/platform/tegra/nvadsp/os.c index 5996973b..55ec92d5 100644 --- a/drivers/platform/tegra/nvadsp/os.c +++ b/drivers/platform/tegra/nvadsp/os.c @@ -2142,9 +2142,7 @@ int __init nvadsp_os_probe(struct platform_device *pdev) { struct nvadsp_drv_data *drv_data = platform_get_drvdata(pdev); struct device *dev = &pdev->dev; -#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 14, 0) uint64_t dma_mask; -#endif uint16_t com_mid = ADSP_COM_MBOX_ID; int ret = 0; @@ -2162,7 +2160,6 @@ int __init nvadsp_os_probe(struct platform_device *pdev) drv_data->deassert_adsp = __deassert_adsp; } -#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 14, 0) ret = of_property_read_u64(dev->of_node, "dma-mask", &dma_mask); if (ret) { dev_err(&pdev->dev, "Missing property dma-mask\n"); @@ -2170,7 +2167,7 @@ int __init nvadsp_os_probe(struct platform_device *pdev) } else { dma_set_mask_and_coherent(&pdev->dev, dma_mask); } -#endif + ret = nvadsp_os_init(pdev); if (ret) { dev_err(dev, "failed to init os\n"); From d059cdbc44dbf93ddde427410e4361f908e92cbc Mon Sep 17 00:00:00 2001 From: Nicolin Chen Date: Mon, 15 Apr 2019 13:11:23 -0700 Subject: [PATCH 071/138] platform: nvadsp: Remove iova lucky check The logic of the nvadsp_dma_alloc_and_map_at() function is to firstly reserve the target iova and secondly allocate a memory with another dynamical iova. Since the target iova is reserved, the dynamical iova won't be possible to be identical. And this lucky check lacks of an exit routine either. So this patch just drops it. Bug 200444660 Change-Id: I5c45e72b1e7315dacd8708fc8facf6bda41c51e5 Signed-off-by: Nicolin Chen Reviewed-on: https://git-master.nvidia.com/r/2098219 Reviewed-by: Automatic_Commit_Validation_User GVS: Gerrit_Virtual_Submit Reviewed-by: Krishna Reddy Reviewed-by: mobile promotions Tested-by: mobile promotions --- drivers/platform/tegra/nvadsp/os.c | 4 ---- 1 file changed, 4 deletions(-) diff --git a/drivers/platform/tegra/nvadsp/os.c b/drivers/platform/tegra/nvadsp/os.c index 55ec92d5..fd402397 100644 --- a/drivers/platform/tegra/nvadsp/os.c +++ b/drivers/platform/tegra/nvadsp/os.c @@ -645,10 +645,6 @@ static void *nvadsp_dma_alloc_and_map_at(struct platform_device *pdev, if (!cpu_va) goto fail_dma_alloc; - /* Luckily hitting the target iova, no need to remap */ - if (tmp_iova == iova) - return cpu_va; - /* Use tmp_iova to remap non-contiguous pages to the desired iova */ for (offset = 0; offset < size; offset += mp_size) { dma_addr_t cur_iova = tmp_iova + offset; From 767700e13df25f9ab6a9b25d1aa1c353fb50376e Mon Sep 17 00:00:00 2001 From: Nicolin Chen Date: Fri, 12 Apr 2019 13:13:43 -0700 Subject: [PATCH 072/138] platform: nvadsp: Fix iova reservation There is no point in managing iovad that's not binded to device's IOMMU domain. So this patch just removes iovad managing code and does a correct iommu_dma_alloc_iova() to reserve the iova. A difficulty of using iommu_dma_alloc_iova() is that we mostly do not use aligned addresses for the target iova, but IOMMU core aligns the iova address with the size by power of two due to cache friendly. So the trick here is to align the start address and to allocate a larger iova region that would cover the target iova -- the excessive part will be freed during iommu_dma_free_iova() by IOMMU core. Note that the dma_free_coherent() does both iommu_unmap and iommu_dma_free_iova, so there's no need of an extra free_iova() in the exit routine. However, for the remap part, the tmp_iova is allocated and mapped, so it should be unmapped and freed. So this patch also adds a free() of tmp_iova in the nvadsp_dma_alloc_at_map() function. Bug 200444660 Change-Id: I54126c2bd9ff3dd688aaa5c6f9befaad620ab548 Signed-off-by: Nicolin Chen Reviewed-on: https://git-master.nvidia.com/r/2096704 Reviewed-by: Automatic_Commit_Validation_User GVS: Gerrit_Virtual_Submit Reviewed-by: Krishna Reddy Reviewed-by: mobile promotions Tested-by: mobile promotions --- drivers/platform/tegra/nvadsp/dev.h | 4 --- drivers/platform/tegra/nvadsp/os.c | 50 ++++++++++++----------------- 2 files changed, 21 insertions(+), 33 deletions(-) diff --git a/drivers/platform/tegra/nvadsp/dev.h b/drivers/platform/tegra/nvadsp/dev.h index ac68a480..dbdcbdbf 100644 --- a/drivers/platform/tegra/nvadsp/dev.h +++ b/drivers/platform/tegra/nvadsp/dev.h @@ -22,7 +22,6 @@ #include #include #include -#include #include #include @@ -215,9 +214,6 @@ struct nvadsp_drv_data { struct tegra_bwmgr_client *bwmgr; u32 evp_base[ADSP_EVP_END]; - /* Used to reserve iova */ - struct iova_domain iovad; - const struct nvadsp_chipdata *chip_data; }; diff --git a/drivers/platform/tegra/nvadsp/os.c b/drivers/platform/tegra/nvadsp/os.c index fd402397..73deff89 100644 --- a/drivers/platform/tegra/nvadsp/os.c +++ b/drivers/platform/tegra/nvadsp/os.c @@ -25,6 +25,7 @@ #include #include #include +#include #include #include #include @@ -620,26 +621,35 @@ static void *nvadsp_dma_alloc_and_map_at(struct platform_device *pdev, gfp_t flags) { struct iommu_domain *domain = iommu_get_domain_for_dev(&pdev->dev); - struct nvadsp_drv_data *drv_data = platform_get_drvdata(pdev); + unsigned long align_mask = ~0UL << fls_long(size - 1); unsigned long shift = __ffs(domain->pgsize_bitmap); unsigned long pg_size = 1UL << shift; unsigned long mp_size = pg_size; struct device *dev = &pdev->dev; + dma_addr_t aligned_iova = iova & align_mask; + dma_addr_t end = iova + size; dma_addr_t tmp_iova, offset; phys_addr_t pa, pa_new; - struct iova *iova_pfn; void *cpu_va; int ret; - /* Reserve iova range */ - iova_pfn = alloc_iova(&drv_data->iovad, size >> shift, - (iova + size - pg_size) >> shift, false); - if (!iova_pfn || (iova_pfn->pfn_lo << shift) != iova) { - dev_err(dev, "failed to reserve iova at 0x%llx size 0x%lx\n", - iova, size); + /* + * Reserve iova range using aligned size: adsp memory might not start + * from an aligned address by power of 2, while iommu_dma_alloc_iova() + * would shift the allocation off the target iova so as to align start + * address by power of 2. To prevent this shifting, use aligned size. + * It might allocate an excessive iova region but it would be handled + * by IOMMU core during iommu_dma_free_iova(). + */ + tmp_iova = iommu_dma_alloc_iova(dev, end - aligned_iova, end - pg_size); + if (tmp_iova != aligned_iova) { + dev_err(dev, "failed to reserve iova range [%llx, %llx]\n", + aligned_iova, end); return NULL; } + dev_dbg(dev, "Reserved iova region [%llx, %llx]\n", aligned_iova, end); + /* Allocate a memory first and get a tmp_iova */ cpu_va = dma_alloc_coherent(dev, size, &tmp_iova, flags); if (!cpu_va) @@ -675,8 +685,9 @@ static void *nvadsp_dma_alloc_and_map_at(struct platform_device *pdev, } } - /* Unmap the tmp_iova since target iova is linked */ + /* Unmap and free the tmp_iova since target iova is linked */ iommu_unmap(domain, tmp_iova, size); + iommu_dma_free_iova(dev, tmp_iova, size); return cpu_va; @@ -684,7 +695,7 @@ fail_map: iommu_unmap(domain, iova, offset); dma_free_coherent(dev, size, cpu_va, tmp_iova); fail_dma_alloc: - __free_iova(&drv_data->iovad, iova_pfn); + iommu_dma_free_iova(dev, end - aligned_iova, end - pg_size); return NULL; } @@ -727,13 +738,9 @@ end: static void deallocate_memory_for_adsp_os(struct device *dev) { #if defined(CONFIG_TEGRA_NVADSP_ON_SMMU) - struct nvadsp_drv_data *drv_data = platform_get_drvdata(priv.pdev); - struct iova_domain *iovad = &drv_data->iovad; void *va = nvadsp_da_to_va_mappings(priv.adsp_os_addr, priv.adsp_os_size); dma_free_coherent(dev, priv.adsp_os_size, va, priv.adsp_os_addr); - free_iova(iovad, iova_pfn(iovad, priv.adsp_os_addr)); - put_iova_domain(iovad); #endif } @@ -839,9 +846,6 @@ int nvadsp_os_load(void) struct nvadsp_drv_data *drv_data; struct device *dev; int ret = 0; -#ifdef CONFIG_TEGRA_NVADSP_ON_SMMU - u32 addr, size; -#endif if (!priv.pdev) { pr_err("ADSP Driver is not initialized\n"); @@ -855,18 +859,6 @@ int nvadsp_os_load(void) drv_data = platform_get_drvdata(priv.pdev); dev = &priv.pdev->dev; -#ifdef CONFIG_TEGRA_NVADSP_ON_SMMU - if (drv_data->adsp_os_secload) { - addr = drv_data->adsp_mem[ACSR_ADDR]; - size = drv_data->adsp_mem[ACSR_SIZE]; - } else { - addr = drv_data->adsp_mem[ADSP_OS_ADDR]; - size = drv_data->adsp_mem[ADSP_OS_SIZE]; - } - init_iova_domain(&drv_data->iovad, PAGE_SIZE, - addr >> PAGE_SHIFT, (addr + size) >> PAGE_SHIFT); -#endif - if (drv_data->adsp_os_secload) { dev_info(dev, "ADSP OS firmware already loaded\n"); ret = __nvadsp_os_secload(priv.pdev); From dfc3347d287dbe4b6ee972b7dc6c23c083e92ca6 Mon Sep 17 00:00:00 2001 From: Hariharan Sivaraman Date: Tue, 7 May 2019 15:31:33 +0530 Subject: [PATCH 073/138] adsp: add support for ivc based adsp reset control For virtualized configurations, ADSP reset will be owned only by Audio server. - Moving tegra-virt-alt-ivc to drivers/.../nvaudio_ivc/ to enable Audio IVC to be built as part of Image - Adding support in nvadsp driver to communicate with Audio server for asserting or deasserting ADSP. Jira EMA-1208 Change-Id: I6773c35edf2c646aa9fd7ec0aa5eceb992dffa35 Signed-off-by: Hariharan Sivaraman Reviewed-on: https://git-master.nvidia.com/r/2113661 Reviewed-by: Uday Gupta GVS: Gerrit_Virtual_Submit Reviewed-by: Mohan Kumar D Reviewed-by: Nitin Pai Reviewed-by: mobile promotions Tested-by: mobile promotions --- drivers/platform/tegra/nvadsp/Makefile | 4 ++ drivers/platform/tegra/nvadsp/dev-t18x.c | 71 +++++++++++++++++++++++- 2 files changed, 74 insertions(+), 1 deletion(-) diff --git a/drivers/platform/tegra/nvadsp/Makefile b/drivers/platform/tegra/nvadsp/Makefile index fec9a585..948e9eff 100644 --- a/drivers/platform/tegra/nvadsp/Makefile +++ b/drivers/platform/tegra/nvadsp/Makefile @@ -36,3 +36,7 @@ endif ifeq ($(CONFIG_TEGRA_ADSP_LPTHREAD),y) nvadsp-objs += adsp_lpthread.o endif + +ifeq ($(CONFIG_TEGRA_VIRT_AUDIO_IVC),y) +ccflags-y += -I$(srctree.nvidia)/drivers/platform/tegra/nvaudio_ivc/ +endif diff --git a/drivers/platform/tegra/nvadsp/dev-t18x.c b/drivers/platform/tegra/nvadsp/dev-t18x.c index d002e840..0f417a33 100644 --- a/drivers/platform/tegra/nvadsp/dev-t18x.c +++ b/drivers/platform/tegra/nvadsp/dev-t18x.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015-2018, NVIDIA Corporation. All rights reserved. + * Copyright (c) 2015-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 @@ -18,6 +18,12 @@ #include #include +#include + +#ifdef CONFIG_TEGRA_VIRT_AUDIO_IVC +#include "tegra_virt_alt_ivc_common.h" +#include "tegra_virt_alt_ivc.h" +#endif #include "dev.h" #include "dev-t18x.h" @@ -199,12 +205,75 @@ static int __deassert_t18x_adsp(struct nvadsp_drv_data *d) return ret; } +#ifdef CONFIG_TEGRA_VIRT_AUDIO_IVC +static int __virt_assert_t18x_adsp(struct nvadsp_drv_data *d) +{ + int err; + struct nvaudio_ivc_msg msg; + struct nvaudio_ivc_ctxt *hivc_client = nvaudio_get_ivc_alloc_ctxt(); + + if (!hivc_client) { + pr_err("%s: Failed to allocate IVC context\n", __func__); + return -ENODEV; + } + + memset(&msg, 0, sizeof(struct nvaudio_ivc_msg)); + msg.cmd = NVAUDIO_ADSP_RESET; + msg.params.adsp_reset_info.reset_req = ASSERT; + msg.ack_required = true; + + err = nvaudio_ivc_send_receive(hivc_client, + &msg, + sizeof(struct nvaudio_ivc_msg)); + if (err < 0) + pr_err("%s: error on ivc_send_receive\n", __func__); + + return 0; +} + +static int __virt_deassert_t18x_adsp(struct nvadsp_drv_data *d) +{ + int err; + struct nvaudio_ivc_msg msg; + struct nvaudio_ivc_ctxt *hivc_client = nvaudio_get_ivc_alloc_ctxt(); + + if (!hivc_client) { + pr_err("%s: Failed to allocate IVC context\n", __func__); + return -ENODEV; + } + + memset(&msg, 0, sizeof(struct nvaudio_ivc_msg)); + msg.cmd = NVAUDIO_ADSP_RESET; + msg.params.adsp_reset_info.reset_req = DEASSERT; + msg.ack_required = true; + + err = nvaudio_ivc_send_receive(hivc_client, + &msg, + sizeof(struct nvaudio_ivc_msg)); + if (err < 0) + pr_err("%s: error on ivc_send_receive\n", __func__); + + return 0; +} +#endif + int nvadsp_reset_t18x_init(struct platform_device *pdev) { struct nvadsp_drv_data *d = platform_get_drvdata(pdev); struct device *dev = &pdev->dev; int ret = 0; +#ifdef CONFIG_TEGRA_VIRT_AUDIO_IVC + struct device_node *node = dev->of_node; + + if (of_device_is_compatible(node, "nvidia,tegra18x-adsp-hv")) { + d->assert_adsp = __virt_assert_t18x_adsp; + d->deassert_adsp = __virt_deassert_t18x_adsp; + d->adspall_rst = NULL; + return 0; + } +#endif + d->assert_adsp = __assert_t18x_adsp; d->deassert_adsp = __deassert_t18x_adsp; d->adspall_rst = devm_reset_control_get(dev, "adspall"); From cb8fd9beb2ba305b36fb7f071a47a94dc42920ae Mon Sep 17 00:00:00 2001 From: Hariharan Sivaraman Date: Mon, 4 Mar 2019 16:18:00 +0530 Subject: [PATCH 074/138] nvadsp: allocate adsp shared memory dynamically 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 Reviewed-on: https://git-master.nvidia.com/r/2134341 Reviewed-by: Automatic_Commit_Validation_User Reviewed-by: Uday Gupta GVS: Gerrit_Virtual_Submit Tested-by: Niranjan Dighe Reviewed-by: Nitin Pai Reviewed-by: mobile promotions Tested-by: mobile promotions --- drivers/platform/tegra/nvadsp/dev.c | 16 +++++++++------- drivers/platform/tegra/nvadsp/dev.h | 2 ++ drivers/platform/tegra/nvadsp/os.c | 28 +++++++++++++++++++++++----- 3 files changed, 34 insertions(+), 12 deletions(-) diff --git a/drivers/platform/tegra/nvadsp/dev.c b/drivers/platform/tegra/nvadsp/dev.c index 93b3f4e8..82d50103 100644 --- a/drivers/platform/tegra/nvadsp/dev.c +++ b/drivers/platform/tegra/nvadsp/dev.c @@ -3,7 +3,7 @@ * * A device driver for ADSP and APE * - * Copyright (C) 2014-2018, NVIDIA Corporation. All rights reserved. + * 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 @@ -414,9 +414,10 @@ static struct nvadsp_chipdata tegra210_adsp_chipdata = { .hwmbox2_reg = 0x60, .hwmbox3_reg = 0x64, }, - .adsp_state_hwmbox = -1, - .adsp_thread_hwmbox = -1, - .adsp_irq_hwmbox = -1, + .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 @@ -439,9 +440,10 @@ static struct nvadsp_chipdata tegrat18x_adsp_chipdata = { .hwmbox6_reg = 0X30000, .hwmbox7_reg = 0X38000, }, - .adsp_state_hwmbox = 0x30000, - .adsp_thread_hwmbox = 0x20000, - .adsp_irq_hwmbox = 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 diff --git a/drivers/platform/tegra/nvadsp/dev.h b/drivers/platform/tegra/nvadsp/dev.h index dbdcbdbf..a11e7e9a 100644 --- a/drivers/platform/tegra/nvadsp/dev.h +++ b/drivers/platform/tegra/nvadsp/dev.h @@ -132,6 +132,7 @@ struct nvadsp_chipdata { u32 adsp_state_hwmbox; u32 adsp_thread_hwmbox; u32 adsp_irq_hwmbox; + u32 adsp_shared_mem_hwmbox; reset_init reset_init; os_init os_init; #ifdef CONFIG_PM @@ -182,6 +183,7 @@ struct nvadsp_drv_data { bool adsp_os_secload; void *shared_adsp_os_data; + dma_addr_t shared_adsp_os_data_iova; #ifdef CONFIG_TEGRA_ADSP_DFS bool dfs_initialized; diff --git a/drivers/platform/tegra/nvadsp/os.c b/drivers/platform/tegra/nvadsp/os.c index 73deff89..c6ae939e 100644 --- a/drivers/platform/tegra/nvadsp/os.c +++ b/drivers/platform/tegra/nvadsp/os.c @@ -775,12 +775,22 @@ static int __nvadsp_os_secload(struct platform_device *pdev) struct device *dev = &pdev->dev; void *dram_va; - dram_va = nvadsp_dma_alloc_and_map_at(pdev, size, addr, GFP_KERNEL); - if (!dram_va) { - dev_err(dev, "unable to allocate shared region\n"); - return -ENOMEM; + if (drv_data->chip_data->adsp_shared_mem_hwmbox != 0) { + dram_va = nvadsp_alloc_coherent(size, &addr, GFP_KERNEL); + if (dram_va == NULL) { + dev_err(dev, "unable to allocate shared region\n"); + return -ENOMEM; + } + } else { + dram_va = nvadsp_dma_alloc_and_map_at(pdev, size, addr, + GFP_KERNEL); + if (dram_va == NULL) { + dev_err(dev, "unable to allocate shared region\n"); + return -ENOMEM; + } } + drv_data->shared_adsp_os_data_iova = addr; nvadsp_set_shared_mem(pdev, dram_va, 0); return 0; @@ -1400,7 +1410,7 @@ static void get_adsp_state(void) drv_data = platform_get_drvdata(priv.pdev); dev = &priv.pdev->dev; - if (drv_data->chip_data->adsp_state_hwmbox == -1) { + if (drv_data->chip_data->adsp_state_hwmbox == 0) { dev_info(dev, "%s: No state hwmbox available\n", __func__); return; } @@ -1672,6 +1682,7 @@ int nvadsp_os_start(void) struct nvadsp_drv_data *drv_data; struct device *dev; int ret = 0; + static int cold_start = 1; if (!priv.pdev) { pr_err("ADSP Driver is not initialized\n"); @@ -1703,6 +1714,13 @@ int nvadsp_os_start(void) if (ret < 0) goto unlock; + if (cold_start && drv_data->chip_data->adsp_shared_mem_hwmbox != 0) { + hwmbox_writel((uint32_t)drv_data->shared_adsp_os_data_iova, + drv_data->chip_data->adsp_shared_mem_hwmbox); + /* Write ACSR base address only once */ + cold_start = 0; + } + ret = __nvadsp_os_start(); if (ret) { priv.os_running = drv_data->adsp_os_running = false; From 857731124c24616aa754bf2edb109a6b73f2e6ce Mon Sep 17 00:00:00 2001 From: Gaurav Tendolkar Date: Fri, 3 Mar 2017 19:29:08 +0530 Subject: [PATCH 075/138] nvadsp: adspff: use kthread to schedule file io - Workqueue scheduling latency causes issues for adsp apps requiring real time behaviour. Hence use RT kthread to schedule the file io functions - file open with O_TRUNC flag may take long time Hence we open the file just once and just reset the read/write pointers in a file close call - Expose sysfs entry to actually close all files opened by kernel Bug 1846449 Jira EMA-847 Change-Id: I3c130b36e5f5f5a0e65ddb4c44efc8754fb006d2 Signed-off-by: Gaurav Tendolkar Reviewed-on: http://git-master/r/1314984 (cherry picked from commit 00f5d0b48aa0df9a720c6fd15b4cffd827408375) Signed-off-by: Hariharan Sivaraman Reviewed-on: https://git-master.nvidia.com/r/2029203 (cherry picked from commit 6760f7d61a589b90a8c156a38e4453b518a20b05) Reviewed-on: https://git-master.nvidia.com/r/2030551 (cherry picked from commit c136844cd4147c25478fe39a15122a1f43553079) Reviewed-on: https://git-master.nvidia.com/r/2150778 Reviewed-by: Automatic_Commit_Validation_User Reviewed-by: Niranjan Dighe GVS: Gerrit_Virtual_Submit Reviewed-by: Nitin Pai Reviewed-by: mobile promotions Tested-by: mobile promotions --- drivers/platform/tegra/nvadsp/adspff.c | 291 ++++++++++++++++++++----- drivers/platform/tegra/nvadsp/adspff.h | 5 +- drivers/platform/tegra/nvadsp/dev.h | 2 +- drivers/platform/tegra/nvadsp/os.c | 2 +- 4 files changed, 240 insertions(+), 60 deletions(-) diff --git a/drivers/platform/tegra/nvadsp/adspff.c b/drivers/platform/tegra/nvadsp/adspff.c index f76d5354..eafb693f 100644 --- a/drivers/platform/tegra/nvadsp/adspff.c +++ b/drivers/platform/tegra/nvadsp/adspff.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016-2018, NVIDIA CORPORATION. All rights reserved. + * Copyright (c) 2016-2019, NVIDIA CORPORATION. All rights reserved. * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU General Public License, @@ -18,18 +18,32 @@ #include #include -#include +#include +#include +#include +#include +#include +#include +#include +#include #include #include "adspff.h" +#include "dev.h" + + +#define ADSPFF_MAX_OPEN_FILES (4) struct file_struct { struct file *fp; + uint8_t file_name[ADSPFF_MAX_FILENAME_SIZE]; + unsigned int flags; unsigned long long wr_offset; unsigned long long rd_offset; }; +static struct file_struct adspff_open_files[ADSPFF_MAX_OPEN_FILES]; static spinlock_t adspff_lock; @@ -121,7 +135,7 @@ static struct nvadsp_mbox rx_mbox; * w+ - open for reading and writing (overwrite file) * * a+ - open for reading and writing (append if file exists) */ -void set_flags(union adspff_message_t *m, int *flags) +void set_flags(union adspff_message_t *m, unsigned int *flags) { if (0 == strcmp(m->msg.payload.fopen_msg.modes, "r+")) *flags = O_RDWR; @@ -145,16 +159,57 @@ void set_flags(union adspff_message_t *m, int *flags) *flags = O_CREAT | O_RDWR; } -void adspff_fopen(struct work_struct *work) +/* + * checks if file is already opened + * if yes, then returns the struct file_struct for the file + * if no, thgen returns first available struct file_struct + * if ADSPFF_MAX_OPEN_FILES alreadu open, returns NULL + */ +static struct file_struct *check_file_opened(const char *path) +{ + struct file_struct *file; + int idx; + + /* assuming files opened by ADSP will + * never be actually closed in kernel + */ + for (idx = 0; idx < ADSPFF_MAX_OPEN_FILES; idx++) { + file = &adspff_open_files[idx]; + if (!file->fp) + break; + if (!strncmp(path, file->file_name, + ADSPFF_MAX_FILENAME_SIZE)) { + break; + } + } + + if (idx == ADSPFF_MAX_OPEN_FILES) { + pr_err("adspff: %d files already opened\n", + ADSPFF_MAX_OPEN_FILES); + file = NULL; + } + return file; +} + +void adspff_fopen(void) { union adspff_message_t *message; union adspff_message_t *msg_recv; - int flags = 0, ret = 0; + unsigned int flags = 0; + int ret = 0; + struct file_struct *file; - struct file_struct *file = kzalloc(sizeof(struct file_struct), GFP_KERNEL); message = kzalloc(sizeof(union adspff_message_t), GFP_KERNEL); + if (!message) + return; + + msg_recv = kzalloc(sizeof(union adspff_message_t), GFP_KERNEL); + if (!msg_recv) { + kfree(message); + return; + } message->msgq_msg.size = MSGQ_MSG_SIZE(struct fopen_msg_t); @@ -168,14 +223,24 @@ void adspff_fopen(struct work_struct *work) return; } - set_flags(message, &flags); + file = check_file_opened(message->msg.payload.fopen_msg.fname); + if (file && !file->fp) { + /* open a new file */ + set_flags(message, &flags); + pr_info("adspff: opening file %s\n", + message->msg.payload.fopen_msg.fname); - file->fp = file_open( - (const char *)message->msg.payload.fopen_msg.fname, - flags, S_IRWXU|S_IRWXG|S_IRWXO); + file->fp = file_open( + (const char *)message->msg.payload.fopen_msg.fname, + flags, 0777); /* S_IRWXU | S_IRWXG | S_IRWXO */ - file->wr_offset = 0; - file->rd_offset = 0; + file->wr_offset = 0; + file->rd_offset = 0; + memcpy(file->file_name, + message->msg.payload.fopen_msg.fname, + ADSPFF_MAX_FILENAME_SIZE); + file->flags = flags; + } if (!(file->fp)) { kfree(file); @@ -202,13 +267,25 @@ void adspff_fopen(struct work_struct *work) kfree(msg_recv); } -void adspff_fclose(struct work_struct *work) +static inline unsigned int is_read_file(struct file_struct *file) +{ + return file->flags & (O_RDONLY | O_RDWR); +} + +static inline unsigned int is_write_file(struct file_struct *file) +{ + return file->flags & (O_WRONLY | O_RDWR); +} + +void adspff_fclose(void) { union adspff_message_t *message; struct file_struct *file = NULL; int32_t ret = 0; message = kzalloc(sizeof(union adspff_message_t), GFP_KERNEL); + if (!message) + return; message->msgq_msg.size = MSGQ_MSG_SIZE(struct fclose_msg_t); @@ -220,16 +297,20 @@ void adspff_fclose(struct work_struct *work) kfree(message); return; } + file = (struct file_struct *)message->msg.payload.fclose_msg.file; if (file) { - file_close(file->fp); - kfree(file); - file = NULL; + if ((file->flags & O_APPEND) == 0) { + if (is_read_file(file)) + file->rd_offset = 0; + if (is_write_file(file)) + file->wr_offset = 0; + } } kfree(message); } -void adspff_fsize(struct work_struct *work) +void adspff_fsize(void) { union adspff_message_t *msg_recv; union adspff_message_t message; @@ -269,7 +350,7 @@ void adspff_fsize(struct work_struct *work) kfree(msg_recv); } -void adspff_fwrite(struct work_struct *work) +void adspff_fwrite(void) { union adspff_message_t message; union adspff_message_t *msg_recv; @@ -280,6 +361,9 @@ void adspff_fwrite(struct work_struct *work) uint32_t bytes_written = 0; msg_recv = kzalloc(sizeof(union adspff_message_t), GFP_KERNEL); + if (!msg_recv) + return; + msg_recv->msgq_msg.size = MSGQ_MSG_SIZE(struct ack_msg_t); message.msgq_msg.size = MSGQ_MSG_SIZE(struct fwrite_msg_t); @@ -314,7 +398,7 @@ void adspff_fwrite(struct work_struct *work) (msgq_message_t *)msg_recv); if (ret < 0) { - pr_err("fread Enqueue failed %d.", ret); + pr_err("adspff: fwrite Enqueue failed %d.", ret); kfree(msg_recv); return; } @@ -323,7 +407,7 @@ void adspff_fwrite(struct work_struct *work) kfree(msg_recv); } -void adspff_fread(struct work_struct *work) +void adspff_fread(void) { union adspff_message_t *message; union adspff_message_t *msg_recv; @@ -343,7 +427,14 @@ void adspff_fread(struct work_struct *work) can_wrap = 0; } message = kzalloc(sizeof(union adspff_message_t), GFP_KERNEL); + if (!message) + return; + msg_recv = kzalloc(sizeof(union adspff_message_t), GFP_KERNEL); + if (!msg_recv) { + kfree(message); + return; + } msg_recv->msgq_msg.size = MSGQ_MSG_SIZE(struct ack_msg_t); message->msgq_msg.size = MSGQ_MSG_SIZE(struct fread_msg_t); @@ -405,12 +496,64 @@ send_ack: kfree(msg_recv); } -static struct workqueue_struct *adspff_wq; -DECLARE_WORK(fopen_work, adspff_fopen); -DECLARE_WORK(fwrite_work, adspff_fwrite); -DECLARE_WORK(fread_work, adspff_fread); -DECLARE_WORK(fclose_work, adspff_fclose); -DECLARE_WORK(fsize_work, adspff_fsize); + +static const struct sched_param param = { + .sched_priority = 1, +}; +static struct task_struct *adspff_kthread; +static DECLARE_COMPLETION(adspff_kthread_completion); +static struct list_head adspff_kthread_msgq_head; + +struct adspff_kthread_msg { + uint32_t msg_id; + struct list_head list; +}; + + +static int adspff_kthread_fn(void *data) +{ + int ret = 0; + struct adspff_kthread_msg *kmsg; + + while (1) { + if (kthread_should_stop()) + do_exit(ret); + + /* Wait for 10 secs or until woken up (earlier) */ + if (!wait_for_completion_timeout(&adspff_kthread_completion, + msecs_to_jiffies(10 * 1000))) + continue; /* Timeout */ + + if (!list_empty(&adspff_kthread_msgq_head)) { + kmsg = list_first_entry(&adspff_kthread_msgq_head, + struct adspff_kthread_msg, list); + switch (kmsg->msg_id) { + case adspff_cmd_fopen: + adspff_fopen(); + break; + case adspff_cmd_fclose: + adspff_fclose(); + break; + case adspff_cmd_fwrite: + adspff_fwrite(); + break; + case adspff_cmd_fread: + adspff_fread(); + break; + case adspff_cmd_fsize: + adspff_fsize(); + break; + default: + pr_warn("adspff: kthread unsupported msg %d\n", + kmsg->msg_id); + } + list_del(&kmsg->list); + kfree(kmsg); + } + } + + do_exit(ret); +} /****************************************************************************** * ADSP mailbox message handler @@ -420,42 +563,67 @@ DECLARE_WORK(fsize_work, adspff_fsize); static int adspff_msg_handler(uint32_t msg, void *data) { unsigned long flags; + struct adspff_kthread_msg *kmsg; spin_lock_irqsave(&adspff_lock, flags); - switch (msg) { - case adspff_cmd_fopen: { - queue_work(adspff_wq, &fopen_work); - } - break; - case adspff_cmd_fclose: { - queue_work(adspff_wq, &fclose_work); - } - break; - case adspff_cmd_fwrite: { - queue_work(adspff_wq, &fwrite_work); - } - break; - case adspff_cmd_fread: { - queue_work(adspff_wq, &fread_work); - } - break; - case adspff_cmd_fsize: { - queue_work(adspff_wq, &fsize_work); - } - break; - default: - pr_err("Unsupported mbox msg %d.\n", msg); + kmsg = kzalloc(sizeof(*kmsg), GFP_ATOMIC); + if (!kmsg) { + spin_unlock_irqrestore(&adspff_lock, flags); + return -ENOMEM; } + + kmsg->msg_id = msg; + list_add_tail(&kmsg->list, &adspff_kthread_msgq_head); + complete(&adspff_kthread_completion); spin_unlock_irqrestore(&adspff_lock, flags); return 0; } -int adspff_init(void) +static int adspff_set(void *data, u64 val) +{ + int i; + struct file_struct *file; + + if (val != 1) + return 0; + for (i = 0; i < ADSPFF_MAX_OPEN_FILES; i++) { + file = &adspff_open_files[i]; + if (file->fp) + file_close(file->fp); + } + memset(adspff_open_files, 0, sizeof(adspff_open_files)); + + return 0; +} +DEFINE_SIMPLE_ATTRIBUTE(adspff_fops, NULL, adspff_set, "%llu\n"); + +static int adspff_debugfs_init(struct nvadsp_drv_data *drv) +{ + int ret = -ENOMEM; + struct dentry *d, *dir; + + if (!drv->adsp_debugfs_root) + return ret; + dir = debugfs_create_dir("adspff", drv->adsp_debugfs_root); + if (!dir) + return ret; + + d = debugfs_create_file( + "close_files", 0200, /* S_IWUSR */ + dir, NULL, &adspff_fops); + if (!d) + return ret; + + return 0; +} + +int adspff_init(struct platform_device *pdev) { int ret = 0; nvadsp_app_handle_t handle; nvadsp_app_info_t *app_info; + struct nvadsp_drv_data *drv = platform_get_drvdata(pdev); handle = nvadsp_app_load("adspff", "adspff.elf"); if (!handle) @@ -477,17 +645,28 @@ int adspff_init(void) return -1; } - if (adspff_wq == NULL) - adspff_wq = create_singlethread_workqueue("adspff_wq"); - spin_lock_init(&adspff_lock); - return 0; + memset(&adspff_open_files, 0, sizeof(adspff_open_files)); + + ret = adspff_debugfs_init(drv); + if (ret) + pr_warn("adspff: failed to create debugfs entry\n"); + + INIT_LIST_HEAD(&adspff_kthread_msgq_head); + + adspff_kthread = kthread_create(adspff_kthread_fn, + NULL, "adspp_kthread"); + sched_setscheduler(adspff_kthread, SCHED_FIFO, ¶m); + get_task_struct(adspff_kthread); + wake_up_process(adspff_kthread); + + return ret; } void adspff_exit(void) { nvadsp_mbox_close(&rx_mbox); - flush_workqueue(adspff_wq); - destroy_workqueue(adspff_wq); + kthread_stop(adspff_kthread); + put_task_struct(adspff_kthread); } diff --git a/drivers/platform/tegra/nvadsp/adspff.h b/drivers/platform/tegra/nvadsp/adspff.h index 3caf7919..f281f222 100644 --- a/drivers/platform/tegra/nvadsp/adspff.h +++ b/drivers/platform/tegra/nvadsp/adspff.h @@ -1,7 +1,7 @@ /* * tegra_adspff.h - Shared ADSPFF interface between Tegra ADSP File * System driver and ADSP side user space code. -* Copyright (c) 2016-2018 NVIDIA Corporation. All rights reserved. +* Copyright (c) 2016-2019 NVIDIA Corporation. All rights reserved. * * NVIDIA Corporation and its licensors retain all intellectual property * and proprietary rights in and to this software, related documentation @@ -28,6 +28,7 @@ extern "C" { #define ADSPFF_WRITE_DATA_SIZE 512 #define ADSPFF_READ_DATA_SIZE 1024 #define ADSPFF_SHARED_BUFFER_SIZE (128 * 1024) +#define ADSPFF_MAX_FILENAME_SIZE (250) /** * adspff_mbx_cmd: commands exchanged using mailbox. @@ -55,7 +56,7 @@ enum adspff_mbx_cmd { /* supported message payloads */ struct fopen_msg_t { - uint8_t fname[250]; + uint8_t fname[ADSPFF_MAX_FILENAME_SIZE]; uint8_t modes[2]; }; diff --git a/drivers/platform/tegra/nvadsp/dev.h b/drivers/platform/tegra/nvadsp/dev.h index a11e7e9a..35c58931 100644 --- a/drivers/platform/tegra/nvadsp/dev.h +++ b/drivers/platform/tegra/nvadsp/dev.h @@ -245,7 +245,7 @@ int adsp_cpustat_exit(struct platform_device *pdev); #endif #if defined(CONFIG_TEGRA_ADSP_FILEIO) -int adspff_init(void); +int adspff_init(struct platform_device *pdev); void adspff_exit(void); #endif diff --git a/drivers/platform/tegra/nvadsp/os.c b/drivers/platform/tegra/nvadsp/os.c index c6ae939e..ed4c8e59 100644 --- a/drivers/platform/tegra/nvadsp/os.c +++ b/drivers/platform/tegra/nvadsp/os.c @@ -1738,7 +1738,7 @@ int nvadsp_os_start(void) priv.num_start++; #if defined(CONFIG_TEGRA_ADSP_FILEIO) if (!drv_data->adspff_init) { - ret = adspff_init(); + ret = adspff_init(priv.pdev); if (!ret) drv_data->adspff_init = true; } From b82520e6d39b2d11962b4b7f58325168d82242e9 Mon Sep 17 00:00:00 2001 From: Shashank Verma Date: Thu, 23 Mar 2017 19:56:42 +0530 Subject: [PATCH 076/138] nvadsp: adspff: add spinlock across list_del Added spinlock across list_del to prevent race between itself and list_add_tail. Bug 1846449 Jira EMA-847 Change-Id: Icc27b0d4926659a04bff3ddb02ec0c11ed1e0a70 Signed-off-by: Shashank Verma Reviewed-on: http://git-master/r/1326930 (cherry picked from commit f097894d0382f79c100bf7bd75c7f923eb20085c) Signed-off-by: Hariharan Sivaraman Reviewed-on: https://git-master.nvidia.com/r/2029205 (cherry picked from commit a7ca3766153a0c5068fbc0dff7391426a948e79a) Reviewed-on: https://git-master.nvidia.com/r/2030553 (cherry picked from commit 2806ac36bc24af1796e875edc4a6678e2db86f97) Reviewed-on: https://git-master.nvidia.com/r/2150779 Reviewed-by: Automatic_Commit_Validation_User Reviewed-by: Niranjan Dighe Reviewed-by: Dipesh Gandhi GVS: Gerrit_Virtual_Submit Reviewed-by: Nitin Pai Reviewed-by: mobile promotions Tested-by: mobile promotions --- drivers/platform/tegra/nvadsp/adspff.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/platform/tegra/nvadsp/adspff.c b/drivers/platform/tegra/nvadsp/adspff.c index eafb693f..6b9558c2 100644 --- a/drivers/platform/tegra/nvadsp/adspff.c +++ b/drivers/platform/tegra/nvadsp/adspff.c @@ -514,6 +514,7 @@ static int adspff_kthread_fn(void *data) { int ret = 0; struct adspff_kthread_msg *kmsg; + unsigned long flags; while (1) { if (kthread_should_stop()) @@ -547,7 +548,9 @@ static int adspff_kthread_fn(void *data) pr_warn("adspff: kthread unsupported msg %d\n", kmsg->msg_id); } + spin_lock_irqsave(&adspff_lock, flags); list_del(&kmsg->list); + spin_unlock_irqrestore(&adspff_lock, flags); kfree(kmsg); } } From 891627113797fffd5481cd467c5e1663e9c1abc2 Mon Sep 17 00:00:00 2001 From: Niranjan Dighe Date: Mon, 22 Apr 2019 19:59:17 +0530 Subject: [PATCH 077/138] nvadsp: adspff: fix issues with kthread impl - Implement file list rather than fixed length array - Fix issues related to file mode handling - Change file not found validation with respect to commit c136844cd414 ("nvadsp: adspff: use kthread to schedule file io") Bug 2538512 Change-Id: I1cd8fa065887effa6073d963df62f6e1bb877a1d Signed-off-by: Niranjan Dighe Reviewed-on: https://git-master.nvidia.com/r/2102696 (cherry picked from commit ea7d4ea78a22b7d8675966f2b2f85343c488d059) Reviewed-on: https://git-master.nvidia.com/r/2150780 Tested-by: Hariharan Sivaraman Reviewed-by: Automatic_Commit_Validation_User GVS: Gerrit_Virtual_Submit Reviewed-by: Nitin Pai Reviewed-by: mobile promotions Tested-by: mobile promotions --- drivers/platform/tegra/nvadsp/adspff.c | 56 +++++++++++++++++--------- drivers/platform/tegra/nvadsp/adspff.h | 2 +- 2 files changed, 38 insertions(+), 20 deletions(-) diff --git a/drivers/platform/tegra/nvadsp/adspff.c b/drivers/platform/tegra/nvadsp/adspff.c index 6b9558c2..2c0c82e9 100644 --- a/drivers/platform/tegra/nvadsp/adspff.c +++ b/drivers/platform/tegra/nvadsp/adspff.c @@ -26,6 +26,7 @@ #include #include #include +#include #include @@ -33,7 +34,7 @@ #include "dev.h" -#define ADSPFF_MAX_OPEN_FILES (4) +#define ADSPFF_MAX_OPEN_FILES (32) struct file_struct { struct file *fp; @@ -41,11 +42,12 @@ struct file_struct { unsigned int flags; unsigned long long wr_offset; unsigned long long rd_offset; + struct list_head list; }; -static struct file_struct adspff_open_files[ADSPFF_MAX_OPEN_FILES]; - +static struct list_head file_list; static spinlock_t adspff_lock; +static int open_count; /****************************************************************************** * Kernel file functions @@ -162,31 +164,40 @@ void set_flags(union adspff_message_t *m, unsigned int *flags) /* * checks if file is already opened * if yes, then returns the struct file_struct for the file - * if no, thgen returns first available struct file_struct - * if ADSPFF_MAX_OPEN_FILES alreadu open, returns NULL + * if no, then allocates a file_struct and adds to the list + * and returns the pointer to the newly allocated file_struct + * if ADSPFF_MAX_OPEN_FILES already open, returns NULL */ static struct file_struct *check_file_opened(const char *path) { - struct file_struct *file; - int idx; + struct file_struct *file = NULL; + struct list_head *pos; /* assuming files opened by ADSP will * never be actually closed in kernel */ - for (idx = 0; idx < ADSPFF_MAX_OPEN_FILES; idx++) { - file = &adspff_open_files[idx]; + list_for_each(pos, &file_list) { + file = list_entry(pos, struct file_struct, list); if (!file->fp) break; if (!strncmp(path, file->file_name, ADSPFF_MAX_FILENAME_SIZE)) { break; } + file = NULL; } - if (idx == ADSPFF_MAX_OPEN_FILES) { + if (file != NULL) + return file; + + if (open_count == ADSPFF_MAX_OPEN_FILES) { pr_err("adspff: %d files already opened\n", ADSPFF_MAX_OPEN_FILES); file = NULL; + } else { + file = kzalloc(sizeof(*file), GFP_KERNEL); + open_count++; + list_add_tail(&file->list, &file_list); } return file; } @@ -242,8 +253,7 @@ void adspff_fopen(void) file->flags = flags; } - if (!(file->fp)) { - kfree(file); + if (file && !file->fp) { file = NULL; pr_err("File not found - %s\n", (const char *) message->msg.payload.fopen_msg.fname); @@ -256,6 +266,12 @@ void adspff_fopen(void) (msgq_message_t *)msg_recv); if (ret < 0) { pr_err("fopen Enqueue failed %d.", ret); + + if (file) { + file_close(file->fp); + file->fp = NULL; + } + kfree(message); kfree(msg_recv); return; @@ -269,7 +285,7 @@ void adspff_fopen(void) static inline unsigned int is_read_file(struct file_struct *file) { - return file->flags & (O_RDONLY | O_RDWR); + return ((!file->flags) || (file->flags & O_RDWR)); } static inline unsigned int is_write_file(struct file_struct *file) @@ -585,17 +601,20 @@ static int adspff_msg_handler(uint32_t msg, void *data) static int adspff_set(void *data, u64 val) { - int i; struct file_struct *file; + struct list_head *pos, *n; if (val != 1) return 0; - for (i = 0; i < ADSPFF_MAX_OPEN_FILES; i++) { - file = &adspff_open_files[i]; + list_for_each_safe(pos, n, &file_list) { + file = list_entry(pos, struct file_struct, list); + list_del(pos); if (file->fp) file_close(file->fp); + kfree(file); } - memset(adspff_open_files, 0, sizeof(adspff_open_files)); + + open_count = 0; return 0; } @@ -650,13 +669,12 @@ int adspff_init(struct platform_device *pdev) spin_lock_init(&adspff_lock); - memset(&adspff_open_files, 0, sizeof(adspff_open_files)); - ret = adspff_debugfs_init(drv); if (ret) pr_warn("adspff: failed to create debugfs entry\n"); INIT_LIST_HEAD(&adspff_kthread_msgq_head); + INIT_LIST_HEAD(&file_list); adspff_kthread = kthread_create(adspff_kthread_fn, NULL, "adspp_kthread"); diff --git a/drivers/platform/tegra/nvadsp/adspff.h b/drivers/platform/tegra/nvadsp/adspff.h index f281f222..05cd40ee 100644 --- a/drivers/platform/tegra/nvadsp/adspff.h +++ b/drivers/platform/tegra/nvadsp/adspff.h @@ -57,7 +57,7 @@ enum adspff_mbx_cmd { /* supported message payloads */ struct fopen_msg_t { uint8_t fname[ADSPFF_MAX_FILENAME_SIZE]; - uint8_t modes[2]; + uint8_t modes[3]; }; struct fwrite_msg_t { From 15961fe4f88fd7779926e5472a226de3214ad80e Mon Sep 17 00:00:00 2001 From: Niranjan Dighe Date: Tue, 16 Jul 2019 12:45:20 +0530 Subject: [PATCH 078/138] nvadsp: logger: reset is_opened flag on file close adsp_logger file could not be re-opened after closing and open call always returned -EBUSY. This was because logger->is_opened was never reset in the file close path. Setting it to 0 in .release callback solves the problem. Jira EMA-1321 Change-Id: I42834fa7af19bacc4326cd32745996b6f4e47686 Signed-off-by: Niranjan Dighe Reviewed-on: https://git-master.nvidia.com/r/2153912 Reviewed-by: mobile promotions Tested-by: mobile promotions --- drivers/platform/tegra/nvadsp/os.c | 23 ++++++++++++----------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/drivers/platform/tegra/nvadsp/os.c b/drivers/platform/tegra/nvadsp/os.c index ed4c8e59..6e43ef87 100644 --- a/drivers/platform/tegra/nvadsp/os.c +++ b/drivers/platform/tegra/nvadsp/os.c @@ -169,13 +169,11 @@ static int adsp_logger_open(struct inode *inode, struct file *file) mutex_unlock(&priv.os_run_lock); /* - * checks if os_opened decrements to zero and if returns true. If true - * then there has been no open. - */ - if (!atomic_dec_and_test(&logger->is_opened)) { - atomic_inc(&logger->is_opened); + * checks if is_opened is 0, if yes, set 1 and proceed, + * else return -EBUSY + */ + if (atomic_cmpxchg(&logger->is_opened, 0, 1)) goto err_ret; - } /* loop till writer is initilized with SOH */ for (i = 0; i < SEARCH_SOH_RETRY; i++) { @@ -204,8 +202,8 @@ static int adsp_logger_open(struct inode *inode, struct file *file) file->private_data = logger; return 0; err: - /* reset to 1 so as to mention the node is free */ - atomic_set(&logger->is_opened, 1); + /* reset to 0 so as to mention the node is free */ + atomic_set(&logger->is_opened, 0); err_ret: return ret; } @@ -218,13 +216,16 @@ static int adsp_logger_flush(struct file *file, fl_owner_t id) dev_dbg(dev, "%s\n", __func__); - /* reset to 1 so as to mention the node is free */ - atomic_set(&logger->is_opened, 1); + /* reset to 0 so as to mention the node is free */ + atomic_set(&logger->is_opened, 0); return 0; } static int adsp_logger_release(struct inode *inode, struct file *file) { + struct nvadsp_debug_log *logger = inode->i_private; + + atomic_set(&logger->is_opened, 0); return 0; } @@ -301,7 +302,7 @@ static int adsp_create_debug_logger(struct dentry *adsp_debugfs_root) goto err_out; } - atomic_set(&logger->is_opened, 1); + atomic_set(&logger->is_opened, 0); init_waitqueue_head(&logger->wait_queue); init_completion(&logger->complete); if (!debugfs_create_file("adsp_logger", S_IRUGO, From a1a7e31c6145b79296d5ac1c40deafe42701d93a Mon Sep 17 00:00:00 2001 From: Niranjan Dighe Date: Fri, 2 Aug 2019 11:50:31 +0530 Subject: [PATCH 079/138] Revert "adsp: add dma_set_mask parse" This reverts commit 4c531e24d2a08eaef19ea5e366374d0aab6b58bd. Jira EMA-1251 Change-Id: I3de386b442ecf3beca938db0725bbe3e673d15c1 Signed-off-by: Niranjan Dighe Reviewed-on: https://git-master.nvidia.com/r/2166541 Reviewed-by: Uday Gupta Reviewed-by: Automatic_Commit_Validation_User Reviewed-by: Dipesh Gandhi Reviewed-by: Mohan Kumar D GVS: Gerrit_Virtual_Submit Reviewed-by: Nitin Pai Reviewed-by: mobile promotions Tested-by: mobile promotions --- drivers/platform/tegra/nvadsp/os.c | 12 +----------- 1 file changed, 1 insertion(+), 11 deletions(-) diff --git a/drivers/platform/tegra/nvadsp/os.c b/drivers/platform/tegra/nvadsp/os.c index 6e43ef87..64df48e0 100644 --- a/drivers/platform/tegra/nvadsp/os.c +++ b/drivers/platform/tegra/nvadsp/os.c @@ -41,7 +41,6 @@ #include #include #include -#include #include @@ -2148,9 +2147,8 @@ static ssize_t tegrafw_read_adsp(struct device *dev, int __init nvadsp_os_probe(struct platform_device *pdev) { struct nvadsp_drv_data *drv_data = platform_get_drvdata(pdev); - struct device *dev = &pdev->dev; - uint64_t dma_mask; uint16_t com_mid = ADSP_COM_MBOX_ID; + struct device *dev = &pdev->dev; int ret = 0; priv.unit_fpga_reset_reg = drv_data->base_regs[UNIT_FPGA_RST]; @@ -2167,14 +2165,6 @@ int __init nvadsp_os_probe(struct platform_device *pdev) drv_data->deassert_adsp = __deassert_adsp; } - ret = of_property_read_u64(dev->of_node, "dma-mask", &dma_mask); - if (ret) { - dev_err(&pdev->dev, "Missing property dma-mask\n"); - goto end; - } else { - dma_set_mask_and_coherent(&pdev->dev, dma_mask); - } - ret = nvadsp_os_init(pdev); if (ret) { dev_err(dev, "failed to init os\n"); From 1dd46a95c77a3267c50a53b6e0f7d53728fd5f24 Mon Sep 17 00:00:00 2001 From: Dipesh Gandhi Date: Wed, 18 Sep 2019 13:46:03 +0530 Subject: [PATCH 080/138] ASoC: tegra-alt: remove redundant drivers - remove unused code - update hypervisor check condition Bug 200552920 Change-Id: Ie2ca3542d8dc50191f402c9d4c96d56e4c89db7a Signed-off-by: Dipesh Gandhi Reviewed-on: https://git-master.nvidia.com/r/2200432 Reviewed-by: Uday Gupta Tested-by: Uday Gupta Reviewed-by: Mohan Kumar D GVS: Gerrit_Virtual_Submit Reviewed-by: Nitin Pai Reviewed-by: mobile promotions Tested-by: mobile promotions --- drivers/platform/tegra/nvadsp/amc.c | 7 ++++--- drivers/platform/tegra/nvadsp/dev-t18x.c | 5 ++--- drivers/platform/tegra/nvadsp/dev.c | 3 --- drivers/platform/tegra/nvadsp/os-t18x.c | 6 +++--- 4 files changed, 9 insertions(+), 12 deletions(-) diff --git a/drivers/platform/tegra/nvadsp/amc.c b/drivers/platform/tegra/nvadsp/amc.c index 1d37d4f2..7743f509 100644 --- a/drivers/platform/tegra/nvadsp/amc.c +++ b/drivers/platform/tegra/nvadsp/amc.c @@ -3,7 +3,7 @@ * * AMC and ARAM handling * - * Copyright (C) 2014-2017, NVIDIA Corporation. All rights reserved. + * 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 @@ -19,6 +19,7 @@ #include #include #include +#include #include "dev.h" #include "amc.h" @@ -170,7 +171,7 @@ void nvadsp_free_amc_interrupts(struct platform_device *pdev) node = dev->of_node; - if (!of_device_is_compatible(node, "nvidia,tegra18x-adsp-hv")) + if (!is_tegra_hypervisor_mode()) devm_free_irq(dev, drv->agic_irqs[AMC_ERR_VIRQ], pdev); } @@ -185,7 +186,7 @@ int nvadsp_setup_amc_interrupts(struct platform_device *pdev) nvadsp_pdev = pdev; nvadsp_drv_data = drv; - if (!of_device_is_compatible(node, "nvidia,tegra18x-adsp-hv")) + if (!is_tegra_hypervisor_mode()) ret = devm_request_irq(dev, drv->agic_irqs[AMC_ERR_VIRQ], nvadsp_amc_error_int_handler, 0, "AMC error int", pdev); diff --git a/drivers/platform/tegra/nvadsp/dev-t18x.c b/drivers/platform/tegra/nvadsp/dev-t18x.c index 0f417a33..b64e053d 100644 --- a/drivers/platform/tegra/nvadsp/dev-t18x.c +++ b/drivers/platform/tegra/nvadsp/dev-t18x.c @@ -11,7 +11,7 @@ * GNU General Public License for more details. * */ - +#include #include #include #include @@ -264,9 +264,8 @@ int nvadsp_reset_t18x_init(struct platform_device *pdev) int ret = 0; #ifdef CONFIG_TEGRA_VIRT_AUDIO_IVC - struct device_node *node = dev->of_node; - if (of_device_is_compatible(node, "nvidia,tegra18x-adsp-hv")) { + if (is_tegra_hypervisor_mode()) { d->assert_adsp = __virt_assert_t18x_adsp; d->deassert_adsp = __virt_deassert_t18x_adsp; d->adspall_rst = NULL; diff --git a/drivers/platform/tegra/nvadsp/dev.c b/drivers/platform/tegra/nvadsp/dev.c index 82d50103..4447aeb6 100644 --- a/drivers/platform/tegra/nvadsp/dev.c +++ b/drivers/platform/tegra/nvadsp/dev.c @@ -461,9 +461,6 @@ static const struct of_device_id nvadsp_of_match[] = { }, { .compatible = "nvidia,tegra18x-adsp", .data = &tegrat18x_adsp_chipdata, - }, { - .compatible = "nvidia,tegra18x-adsp-hv", - .data = &tegrat18x_adsp_chipdata, }, { }, }; diff --git a/drivers/platform/tegra/nvadsp/os-t18x.c b/drivers/platform/tegra/nvadsp/os-t18x.c index 0d5e6919..1f561a86 100644 --- a/drivers/platform/tegra/nvadsp/os-t18x.c +++ b/drivers/platform/tegra/nvadsp/os-t18x.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2015-2018, NVIDIA Corporation. All rights reserved. + * Copyright (C) 2015-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 @@ -12,6 +12,7 @@ * */ +#include #include #include #include @@ -68,10 +69,9 @@ int nvadsp_os_t18x_init(struct platform_device *pdev) { struct device *dev = &pdev->dev; struct nvadsp_drv_data *drv_data = platform_get_drvdata(pdev); - struct device_node *node = dev->of_node; int ret, adma_ch_page, val = 0; - if (of_device_is_compatible(node, "nvidia,tegra18x-adsp-hv")) { + if (is_tegra_hypervisor_mode()) { adma_ch_page = tegra_adma_query_dma_page(); From dddab770debc102e04b63673cb477a58ea9abb27 Mon Sep 17 00:00:00 2001 From: Asha Talambedu Date: Fri, 11 Oct 2019 12:32:05 +0530 Subject: [PATCH 081/138] platform: tegra: Make ADSP CPU driver a LKM -Made necessary Kconfig and makefile changes required to make ADSP CPU driver a loadable kernel module. -Had to replace nsec_to_clock_t api with functions that are available as exported symbols as nsec_to_clock_t is not an exported symbol Bug 200560561 Change-Id: Ia2fa069d9bb234b57f19cbf919c9e6d7567f3d7b Signed-off-by: Asha Talambedu Reviewed-on: https://git-master.nvidia.com/r/2215613 (cherry picked from commit 519a4618c452e3a62550d4fdf3ec5b52acf8b9c8) Reviewed-on: https://git-master.nvidia.com/r/2228337 Reviewed-by: Automatic_Commit_Validation_User Reviewed-by: Bharat Nihalani GVS: Gerrit_Virtual_Submit Reviewed-by: mobile promotions Tested-by: mobile promotions --- drivers/platform/tegra/nvadsp/Kconfig | 6 ++++-- drivers/platform/tegra/nvadsp/Makefile | 2 +- drivers/platform/tegra/nvadsp/adsp_dfs.c | 5 +++-- 3 files changed, 8 insertions(+), 5 deletions(-) diff --git a/drivers/platform/tegra/nvadsp/Kconfig b/drivers/platform/tegra/nvadsp/Kconfig index 0af19781..71f2d907 100644 --- a/drivers/platform/tegra/nvadsp/Kconfig +++ b/drivers/platform/tegra/nvadsp/Kconfig @@ -1,5 +1,5 @@ config TEGRA_NVADSP - bool "Enable Host ADSP driver" + tristate "Enable Host ADSP driver" default n select ARM_GIC_PM select FIQ @@ -47,6 +47,7 @@ config TEGRA_ADSP_CPUSTAT config TEGRA_ADSP_FILEIO bool "Enable ADSP file io" + depends on TEGRA_NVADSP default n help Enable dumping to and reading from file on host from ADSP @@ -55,7 +56,7 @@ config TEGRA_ADSP_FILEIO config TEGRA_ADSP_LPTHREAD bool "Enable ADSP usage calc by lpthread" - depends on DEBUG_FS + depends on DEBUG_FS && TEGRA_NVADSP default n help Enable calculation of ADSP usage by running a low priority @@ -66,6 +67,7 @@ config TEGRA_ADSP_LPTHREAD config TEGRA_EMC_APE_DFS bool "Enable emc dfs due to APE" + depends on TEGRA_NVADSP default n help Enable emc dfs due to APE DRAM access diff --git a/drivers/platform/tegra/nvadsp/Makefile b/drivers/platform/tegra/nvadsp/Makefile index 948e9eff..087db77e 100644 --- a/drivers/platform/tegra/nvadsp/Makefile +++ b/drivers/platform/tegra/nvadsp/Makefile @@ -1,7 +1,7 @@ GCOV_PROFILE := y ccflags-y += -Werror -obj-y := nvadsp.o +obj-$(CONFIG_TEGRA_NVADSP) := nvadsp.o nvadsp-objs += dev.o os.o app.o app_loader_linker.o\ amc.o nvadsp_shared_sema.o \ hwmailbox.o mailbox.o msgq.o \ diff --git a/drivers/platform/tegra/nvadsp/adsp_dfs.c b/drivers/platform/tegra/nvadsp/adsp_dfs.c index 7fdb61ad..6e9f6884 100644 --- a/drivers/platform/tegra/nvadsp/adsp_dfs.c +++ b/drivers/platform/tegra/nvadsp/adsp_dfs.c @@ -3,7 +3,7 @@ * * adsp dynamic frequency scaling * - * Copyright (C) 2014-2018, NVIDIA Corporation. All rights reserved. + * 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 @@ -610,9 +610,10 @@ static void dump_stats_table(struct seq_file *s, struct adsp_freq_stats *fstats) adspfreq_stats_update(); for (i = 0; i < fstats->state_num; i++) { + u64 jiffies64 = nsecs_to_jiffies64(fstats->time_in_state[i]); seq_printf(s, "%lu %llu\n", (long unsigned int)(adsp_cpu_freq_table[i] / 1000), - nsec_to_clock_t(fstats->time_in_state[i])); + jiffies_64_to_clock_t(jiffies64)); } mutex_unlock(&policy_mutex); } From b72af873f42eec58afa7203c231c7932a5dc8eea Mon Sep 17 00:00:00 2001 From: Dipesh Gandhi Date: Mon, 16 Sep 2019 15:53:22 +0530 Subject: [PATCH 082/138] ASoC: tegra-virt-alt: lpthread init via amixer Change exposes lpthread/adsp usage trigger via amixer. This is needed to make sound card aware of lpthread state. Without this STR adsp feature breaks as currently lpthread is written in such a manner which does not allow adsp to suspend. Bug 200552183 Change-Id: I2cc1e0fd805a982686bbd034b2c6e094b56df23b Signed-off-by: Dipesh Gandhi Reviewed-on: https://git-master.nvidia.com/r/2198693 (cherry picked from commit e9bd14fd6eb0305cc583092752e6bdccc9c5e76a) Reviewed-on: https://git-master.nvidia.com/r/2250202 Reviewed-by: Automatic_Commit_Validation_User Reviewed-by: Swati Sachdeva GVS: Gerrit_Virtual_Submit Reviewed-by: Uday Gupta Reviewed-by: Nitin Pai Reviewed-by: mobile promotions Tested-by: mobile promotions --- drivers/platform/tegra/nvadsp/adsp_lpthread.c | 81 +++---------------- drivers/platform/tegra/nvadsp/dev.h | 8 +- drivers/platform/tegra/nvadsp/os.c | 17 ++-- 3 files changed, 21 insertions(+), 85 deletions(-) diff --git a/drivers/platform/tegra/nvadsp/adsp_lpthread.c b/drivers/platform/tegra/nvadsp/adsp_lpthread.c index dfb4ba47..8a827a29 100644 --- a/drivers/platform/tegra/nvadsp/adsp_lpthread.c +++ b/drivers/platform/tegra/nvadsp/adsp_lpthread.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016-2017, NVIDIA CORPORATION. All rights reserved. + * Copyright (c) 2016-2019, NVIDIA CORPORATION. All rights reserved. * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU General Public License, @@ -128,7 +128,7 @@ int adsp_lpthread_pause(void) return ret; } -int adsp_lpthread_exit(void) +int adsp_lpthread_uninit(void) { int ret; @@ -147,7 +147,7 @@ int adsp_lpthread_exit(void) return ret; } -static int adsp_usage_set(void *data, u64 val) +int adsp_usage_set(unsigned int val) { int ret = 0; @@ -157,24 +157,10 @@ static int adsp_usage_set(void *data, u64 val) if (lpthread->lpthread_initialized && lpthread->lpthread_resumed) { pr_info("ADSP Usage App already running\n"); - pr_info("echo %d > adsp_usage to pause\n", - ADSP_LPTHREAD_PAUSE); - pr_info("echo %d > adsp_usage to stop\n", - ADSP_LPTHREAD_STOP); break; } - if (lpthread->adsp_os_suspended && - !lpthread->lpthread_initialized) { - pr_info("Starting ADSP OS\n"); - if (nvadsp_os_start()) { - pr_err("Unable to start OS\n"); - break; - } - lpthread->adsp_os_suspended = false; - ret = adsp_lpthread_init(lpthread->adsp_os_suspended); - pr_info("Initializing lpthread\n"); - lpthread->lpthread_initialized = true; - } else if (!lpthread->lpthread_initialized) { + + if (!lpthread->lpthread_initialized) { ret = adsp_lpthread_init(lpthread->adsp_os_suspended); pr_info("Initializing lpthread\n"); lpthread->lpthread_initialized = true; @@ -190,8 +176,6 @@ static int adsp_usage_set(void *data, u64 val) case ADSP_LPTHREAD_PAUSE: if (!lpthread->lpthread_initialized) { pr_info("ADSP Usage App not initialized\n"); - pr_info("echo %d > adsp_usage to init\n", - ADSP_LPTHREAD_START); break; } pr_info("Pausing lpthread\n"); @@ -204,12 +188,10 @@ static int adsp_usage_set(void *data, u64 val) case ADSP_LPTHREAD_STOP: if (!lpthread->lpthread_initialized) { pr_info("ADSP Usage App not initialized\n"); - pr_info("echo %d > adsp_usage to init\n", - ADSP_LPTHREAD_START); break; } pr_info("Exiting lpthread\n"); - ret = adsp_lpthread_exit(); + ret = adsp_lpthread_uninit(); lpthread->lpthread_resumed = false; lpthread->lpthread_paused = false; lpthread->lpthread_closed = true; @@ -218,18 +200,12 @@ static int adsp_usage_set(void *data, u64 val) default: pr_err("ADSP Usage App: Invalid input\n"); - pr_err("echo %d > adsp_usage to init/resume\n", - ADSP_LPTHREAD_START); - pr_err("echo %d > adsp_usage to pause\n", - ADSP_LPTHREAD_PAUSE); - pr_err("echo %d > adsp_usage to stop\n", - ADSP_LPTHREAD_STOP); ret = 0; } return ret; } - -static int adsp_usage_get(void *data, u64 *val) +EXPORT_SYMBOL(adsp_usage_set); +unsigned int adsp_usage_get(void) { if (lpthread->lpthread_initialized && lpthread->lpthread_resumed) return ADSP_LPTHREAD_START; @@ -239,54 +215,21 @@ static int adsp_usage_get(void *data, u64 *val) return ADSP_LPTHREAD_STOP; } +EXPORT_SYMBOL(adsp_usage_get); -DEFINE_SIMPLE_ATTRIBUTE(adsp_usage_fops, - adsp_usage_get, adsp_usage_set, "%llu\n"); - -static int lpthread_debugfs_init(struct nvadsp_drv_data *drv) -{ - int ret = -ENOMEM; - struct dentry *d, *dir; - - if (!drv->adsp_debugfs_root) - return ret; - - dir = debugfs_create_dir("adsp_lpthread", - drv->adsp_debugfs_root); - if (!dir) - return ret; - - d = debugfs_create_file( - "adsp_usage", RW_MODE, dir, NULL, &adsp_usage_fops); - if (!d) - goto err; - - return 0; - -err: - debugfs_remove_recursive(dir); - pr_err("unable to create adsp lpthread debug file\n"); - return -ENOMEM; -} - -int adsp_lpthread_debugfs_init(struct platform_device *pdev) +int adsp_lpthread_entry(struct platform_device *pdev) { struct nvadsp_drv_data *drv = platform_get_drvdata(pdev); - int ret = -EINVAL; lpthread = &lpthread_obj; - ret = lpthread_debugfs_init(drv); - if (ret) - pr_err("lpthread_debugfs_init() ret = %d\n", ret); - drv->lpthread_initialized = true; lpthread->adsp_os_suspended = false; return 0; } -int adsp_lpthread_debugfs_exit(struct platform_device *pdev) +int adsp_lpthread_exit(struct platform_device *pdev) { status_t ret = 0; struct nvadsp_drv_data *drv = platform_get_drvdata(pdev); @@ -298,7 +241,7 @@ int adsp_lpthread_debugfs_exit(struct platform_device *pdev) return ret; } -int adsp_lpthread_debugfs_set_suspend(bool is_suspended) +int adsp_lpthread_set_suspend(bool is_suspended) { lpthread->adsp_os_suspended = is_suspended; return 0; diff --git a/drivers/platform/tegra/nvadsp/dev.h b/drivers/platform/tegra/nvadsp/dev.h index 35c58931..75bfc448 100644 --- a/drivers/platform/tegra/nvadsp/dev.h +++ b/drivers/platform/tegra/nvadsp/dev.h @@ -279,12 +279,12 @@ static inline int __init nvadsp_reset_init(struct platform_device *pdev) int adsp_lpthread_init(bool is_adsp_suspended); int adsp_lpthread_resume(void); int adsp_lpthread_pause(void); -int adsp_lpthread_exit(void); +int adsp_lpthread_uninit(void); int adsp_lpthread_get_state(void); -int adsp_lpthread_debugfs_init(struct platform_device *pdev); -int adsp_lpthread_debugfs_exit(struct platform_device *pdev); -int adsp_lpthread_debugfs_set_suspend(bool is_suspended); +int adsp_lpthread_entry(struct platform_device *pdev); +int adsp_lpthread_exit(struct platform_device *pdev); +int adsp_lpthread_set_suspend(bool is_suspended); #endif #endif /* __TEGRA_NVADSP_DEV_H */ diff --git a/drivers/platform/tegra/nvadsp/os.c b/drivers/platform/tegra/nvadsp/os.c index 64df48e0..86bf8aae 100644 --- a/drivers/platform/tegra/nvadsp/os.c +++ b/drivers/platform/tegra/nvadsp/os.c @@ -1746,9 +1746,10 @@ int nvadsp_os_start(void) #ifdef CONFIG_TEGRA_ADSP_LPTHREAD if (!drv_data->lpthread_initialized) { - ret = adsp_lpthread_debugfs_init(priv.pdev); + ret = adsp_lpthread_entry(priv.pdev); if (ret) - dev_err(dev, "adsp_lpthread_debugfs_init failed ret = %d\n", ret); + dev_err(dev, "adsp_lpthread_entry failed ret = %d\n", + ret); } #endif @@ -1758,7 +1759,7 @@ int nvadsp_os_start(void) #endif #ifdef CONFIG_TEGRA_ADSP_LPTHREAD - adsp_lpthread_debugfs_set_suspend(drv_data->adsp_os_suspended); + adsp_lpthread_set_suspend(drv_data->adsp_os_suspended); #endif unlock: @@ -1809,7 +1810,7 @@ static int __nvadsp_os_suspend(void) drv_data->adsp_os_suspended = true; #ifdef CONFIG_TEGRA_ADSP_LPTHREAD - adsp_lpthread_debugfs_set_suspend(drv_data->adsp_os_suspended); + adsp_lpthread_set_suspend(drv_data->adsp_os_suspended); #endif nvadsp_assert_adsp(drv_data); @@ -1936,14 +1937,6 @@ int nvadsp_os_suspend(void) goto end; } -#ifdef CONFIG_TEGRA_ADSP_LPTHREAD - if (adsp_lpthread_get_state()) { - dev_err(&priv.pdev->dev, "Adsp usage being calculated. Not suspending adsp\n"); - ret = 0; - goto end; - } -#endif - drv_data = platform_get_drvdata(priv.pdev); mutex_lock(&priv.os_run_lock); From 0f6dc526558d3c714918e9d9845ed9ebae4904db Mon Sep 17 00:00:00 2001 From: Swati Sachdeva Date: Fri, 25 Oct 2019 16:28:13 +0530 Subject: [PATCH 083/138] nvadsp: Fixing adsp_ff_exit function - ADSP thread was waiting on a sempaphore to wake up - In case of shutdown when kthread_stop is called, kthread_should_stop - will not be called if thread is not woken up - Solution: Replace semaphore with wait_queue and use - wait_event_interruptible - wake up the thread if either there is a message in the list or - kthread_should_stop is true Bug 2739934 Bug 200560194 Bug 200463529 Change-Id: Iea04e0100248f554076aaed8627d40f5b96df2bf Signed-off-by: Uday Gupta Reviewed-on: https://git-master.nvidia.com/r/2232868 (cherry picked from commit a3a81a984a933a8f580154b76098119427f5e19e) Signed-off-by: Dipesh Gandhi Reviewed-on: https://git-master.nvidia.com/r/2238825 Reviewed-by: Automatic_Commit_Validation_User GVS: Gerrit_Virtual_Submit Reviewed-by: Swati Sachdeva Reviewed-by: Nitin Pai Reviewed-by: mobile promotions Tested-by: mobile promotions --- drivers/platform/tegra/nvadsp/adspff.c | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/drivers/platform/tegra/nvadsp/adspff.c b/drivers/platform/tegra/nvadsp/adspff.c index 2c0c82e9..0655c4a3 100644 --- a/drivers/platform/tegra/nvadsp/adspff.c +++ b/drivers/platform/tegra/nvadsp/adspff.c @@ -517,8 +517,8 @@ static const struct sched_param param = { .sched_priority = 1, }; static struct task_struct *adspff_kthread; -static DECLARE_COMPLETION(adspff_kthread_completion); static struct list_head adspff_kthread_msgq_head; +static wait_queue_head_t wait_queue; struct adspff_kthread_msg { uint32_t msg_id; @@ -533,13 +533,12 @@ static int adspff_kthread_fn(void *data) unsigned long flags; while (1) { - if (kthread_should_stop()) - do_exit(ret); - /* Wait for 10 secs or until woken up (earlier) */ - if (!wait_for_completion_timeout(&adspff_kthread_completion, - msecs_to_jiffies(10 * 1000))) - continue; /* Timeout */ + ret = wait_event_interruptible(wait_queue, kthread_should_stop() + || !list_empty(&adspff_kthread_msgq_head)); + + if (kthread_should_stop()) + do_exit(0); if (!list_empty(&adspff_kthread_msgq_head)) { kmsg = list_first_entry(&adspff_kthread_msgq_head, @@ -593,7 +592,7 @@ static int adspff_msg_handler(uint32_t msg, void *data) kmsg->msg_id = msg; list_add_tail(&kmsg->list, &adspff_kthread_msgq_head); - complete(&adspff_kthread_completion); + wake_up(&wait_queue); spin_unlock_irqrestore(&adspff_lock, flags); return 0; @@ -676,6 +675,8 @@ int adspff_init(struct platform_device *pdev) INIT_LIST_HEAD(&adspff_kthread_msgq_head); INIT_LIST_HEAD(&file_list); + // kthread inIt + init_waitqueue_head(&wait_queue); adspff_kthread = kthread_create(adspff_kthread_fn, NULL, "adspp_kthread"); sched_setscheduler(adspff_kthread, SCHED_FIFO, ¶m); From b24ad07d958fc2cbe42712f8bc7fdd34f8f27d62 Mon Sep 17 00:00:00 2001 From: Asha Talambedu Date: Thu, 5 Dec 2019 01:46:07 +0530 Subject: [PATCH 084/138] nvadsp: Remove symbol table creation for l4t Removed symbol table for L4T platform that has symbol stripped adsp-fw Bug 200536088 Change-Id: I18187479c796c98ce2dcc5865e1175dd9d7c9f5e Signed-off-by: Asha Talambedu (cherry picked from commit 4089e712d554081a69f34e60a01459cb19e1978c) Reviewed-on: https://git-master.nvidia.com/r/c/linux-nvidia/+/2308814 Reviewed-by: Automatic_Commit_Validation_User Reviewed-by: automaticguardword Reviewed-by: Mohan Kumar D Reviewed-by: Sharad Gupta Reviewed-by: Viswanath L Reviewed-by: mobile promotions Tested-by: mobile promotions GVS: Gerrit_Virtual_Submit --- drivers/platform/tegra/nvadsp/os.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/drivers/platform/tegra/nvadsp/os.c b/drivers/platform/tegra/nvadsp/os.c index 86bf8aae..5f4c70c4 100644 --- a/drivers/platform/tegra/nvadsp/os.c +++ b/drivers/platform/tegra/nvadsp/os.c @@ -5,7 +5,7 @@ * Copyright (C) 2011 Texas Instruments, Inc. * Copyright (C) 2011 Google, Inc. * - * Copyright (C) 2014-2019, NVIDIA Corporation. All rights reserved. + * Copyright (C) 2014-2020, 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 @@ -446,7 +446,7 @@ static inline void __maybe_unused dump_global_symbol_table(void) } static int -create_global_symbol_table(const struct firmware *fw) +__maybe_unused create_global_symbol_table(const struct firmware *fw) { int i; struct device *dev = &priv.pdev->dev; @@ -487,7 +487,7 @@ create_global_symbol_table(const struct firmware *fw) return 0; } -struct global_sym_info *find_global_symbol(const char *sym_name) +struct global_sym_info * __maybe_unused find_global_symbol(const char *sym_name) { struct device *dev = &priv.pdev->dev; struct global_sym_info *table = priv.adsp_glo_sym_tbl; @@ -809,13 +809,13 @@ static int nvadsp_firmware_load(struct platform_device *pdev) NVADSP_FIRMWARE, ret); goto end; } - +#ifdef CONFIG_ANDROID ret = create_global_symbol_table(fw); if (ret) { dev_err(dev, "unable to create global symbol table\n"); goto release_firmware; } - +#endif ret = allocate_memory_for_adsp_os(); if (ret) { dev_err(dev, "unable to allocate memory for adsp os\n"); From a617e73405640d2cb7c3f8253d7099f3646d3b1b Mon Sep 17 00:00:00 2001 From: Sameer Pujar Date: Thu, 2 Apr 2020 17:16:47 +0530 Subject: [PATCH 085/138] nvadsp: build dependency on HSP driver ADSP driver has build dependency over HSP driver. Currently on 5.4 CONFIG_TEGRA_HSP is not yet enabled and it is causing build break. This can be addressed by including corresponding driver calls under above config check. Bug 200593718 Change-Id: I4847f8ac251c9cb2bd96fbdecbf29b4200aca371 Signed-off-by: Sameer Pujar Reviewed-on: https://git-master.nvidia.com/r/c/linux-nvidia/+/2323013 Reviewed-by: automaticguardword Reviewed-by: Mohan Kumar D Reviewed-by: Dipesh Gandhi Reviewed-by: Sharad Gupta Reviewed-by: mobile promotions GVS: Gerrit_Virtual_Submit Tested-by: mobile promotions --- drivers/platform/tegra/nvadsp/os-t18x.c | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/drivers/platform/tegra/nvadsp/os-t18x.c b/drivers/platform/tegra/nvadsp/os-t18x.c index 1f561a86..29f8dd3b 100644 --- a/drivers/platform/tegra/nvadsp/os-t18x.c +++ b/drivers/platform/tegra/nvadsp/os-t18x.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2015-2019, NVIDIA Corporation. All rights reserved. + * Copyright (C) 2015-2020, 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 @@ -21,6 +21,7 @@ #include "dev.h" #include "os.h" +#if IS_ENABLED(CONFIG_TEGRA_HSP) static void nvadsp_dbell_handler(void *data) { struct platform_device *pdev = data; @@ -28,6 +29,7 @@ static void nvadsp_dbell_handler(void *data) dev_info(dev, "APE DBELL handler\n"); } +#endif /* Function to return the ADMA page number (0 indexed) used by guest */ @@ -67,9 +69,8 @@ static int tegra_adma_query_dma_page(void) int nvadsp_os_t18x_init(struct platform_device *pdev) { - struct device *dev = &pdev->dev; struct nvadsp_drv_data *drv_data = platform_get_drvdata(pdev); - int ret, adma_ch_page, val = 0; + int ret = 0, adma_ch_page, val = 0; if (is_tegra_hypervisor_mode()) { @@ -93,12 +94,13 @@ int nvadsp_os_t18x_init(struct platform_device *pdev) return 0; } +#if IS_ENABLED(CONFIG_TEGRA_HSP) ret = tegra_hsp_db_add_handler(HSP_MASTER_APE, nvadsp_dbell_handler, pdev); - if (ret) { - dev_err(dev, "failed to add HSP_MASTER_APE DB handler\n"); - goto end; - } - end: + if (ret) + dev_err(&pdev->dev, + "failed to add HSP_MASTER_APE DB handler\n"); +#endif + return ret; } From 003fec905dd8776901ff996351bac13031e559b2 Mon Sep 17 00:00:00 2001 From: Sameer Pujar Date: Thu, 2 Apr 2020 16:53:02 +0530 Subject: [PATCH 086/138] nvadsp: fix 5.4 build error related to access_ok() access_ok() macro has changed in 5.4 kernel and it does not accept the same number of arguments any more. To fix the build error and to make it work for all the supported kernels, a new local macro ACCESS_OK() is introduced which can further make kernel specific calls. Bug 200593718 Change-Id: I71f766401ec185859b4185f2e4b4f2bb914965fc Signed-off-by: Sameer Pujar Reviewed-on: https://git-master.nvidia.com/r/c/linux-nvidia/+/2323014 Reviewed-by: automaticguardword Reviewed-by: Mohan Kumar D Reviewed-by: Sharad Gupta Reviewed-by: mobile promotions GVS: Gerrit_Virtual_Submit Tested-by: mobile promotions --- .../platform/tegra/nvadsp/adsp_console_dbfs.c | 25 ++++++++++--------- 1 file changed, 13 insertions(+), 12 deletions(-) diff --git a/drivers/platform/tegra/nvadsp/adsp_console_dbfs.c b/drivers/platform/tegra/nvadsp/adsp_console_dbfs.c index 64cfc3df..ee3a270b 100644 --- a/drivers/platform/tegra/nvadsp/adsp_console_dbfs.c +++ b/drivers/platform/tegra/nvadsp/adsp_console_dbfs.c @@ -3,7 +3,7 @@ * * adsp mailbox console driver * - * Copyright (C) 2014-2019, NVIDIA Corporation. All rights reserved. + * Copyright (C) 2014-2020, 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 @@ -44,6 +44,12 @@ static int open_cnt; static uint64_t adsp_app_ctx_vals[ADSP_APP_CTX_MAX]; +#if LINUX_VERSION_CODE < KERNEL_VERSION(5, 4, 0) +#define ACCESS_OK(addr, size) access_ok(0, addr, size) +#else +#define ACCESS_OK(addr, size) access_ok(addr, size) +#endif + static int adsp_app_ctx_add(uint64_t ctx) { int i; @@ -192,8 +198,7 @@ adsp_consol_ioctl(struct file *f, unsigned int cmd, unsigned long arg) memset(mbox, 0, sizeof(struct nvadsp_mbox)); break; case _IOC_NR(ADSP_CNSL_RUN_APP): - if (!access_ok(0, uarg, - sizeof(struct adsp_consol_run_app_arg_t))) + if (!ACCESS_OK(uarg, sizeof(struct adsp_consol_run_app_arg_t))) return -EACCES; ret = copy_from_user(&app_args, uarg, sizeof(app_args)); @@ -260,8 +265,7 @@ adsp_consol_ioctl(struct file *f, unsigned int cmd, unsigned long arg) ret = -EACCES; break; case _IOC_NR(ADSP_CNSL_STOP_APP): - if (!access_ok(0, uarg, - sizeof(struct adsp_consol_run_app_arg_t))) + if (!ACCESS_OK(uarg, sizeof(struct adsp_consol_run_app_arg_t))) return -EACCES; ret = copy_from_user(&app_args, uarg, sizeof(app_args)); @@ -311,7 +315,7 @@ adsp_consol_ioctl(struct file *f, unsigned int cmd, unsigned long arg) case _IOC_NR(ADSP_CNSL_CLR_BUFFER): break; case _IOC_NR(ADSP_CNSL_OPN_MBX): - if (!access_ok(0, uarg, sizeof(ctx2))) + if (!ACCESS_OK(uarg, sizeof(ctx2))) return -EACCES; ret = copy_from_user(&ctx2, uarg, sizeof(ctx2)); if (ret) { @@ -348,8 +352,7 @@ adsp_consol_ioctl(struct file *f, unsigned int cmd, unsigned long arg) memset(mbox, 0, sizeof(struct nvadsp_mbox)); break; case _IOC_NR(ADSP_CNSL_PUT_MBX): - if (!access_ok(0, uarg, - sizeof(uint32_t))) + if (!ACCESS_OK(uarg, sizeof(uint32_t))) return -EACCES; ret = copy_from_user(&data, uarg, sizeof(uint32_t)); @@ -361,8 +364,7 @@ adsp_consol_ioctl(struct file *f, unsigned int cmd, unsigned long arg) NVADSP_MBOX_SMSG, 0, 0); break; case _IOC_NR(ADSP_CNSL_GET_MBX): - if (!access_ok(0, uarg, - sizeof(uint32_t))) + if (!ACCESS_OK(uarg, sizeof(uint32_t))) return -EACCES; ret = nvadsp_mbox_recv(&console->app_mbox, &data, 0, 0); if (ret) @@ -373,8 +375,7 @@ adsp_consol_ioctl(struct file *f, unsigned int cmd, unsigned long arg) ret = -EACCES; break; case _IOC_NR(ADSP_CNSL_PUT_DATA): - if (!access_ok(0, uarg, - sizeof(struct adsp_consol_run_app_arg_t))) + if (!ACCESS_OK(uarg, sizeof(struct adsp_consol_run_app_arg_t))) return -EACCES; ret = copy_from_user(&data, uarg, sizeof(uint32_t)); if (ret) { From 6cd83e4d26cb2f9d2a294c51eb5026dabb2e99a7 Mon Sep 17 00:00:00 2001 From: Sameer Pujar Date: Mon, 27 Apr 2020 13:45:19 +0530 Subject: [PATCH 087/138] nvadsp: don't use tegra-pd for kernel 5.4 onwards The usage of Tegra power-domains is deprecated from kernel-5.4 and instead generic power-domains should be used. Current driver code is common across multiple kernel versions and to maintain backward compatibility kernel version checks are added. Bug 200593718 Change-Id: Ia49c0bab0d320de2d4a3a23e7e675242f56dbff7 Signed-off-by: Sameer Pujar Reviewed-on: https://git-master.nvidia.com/r/c/linux-nvidia/+/2334795 Reviewed-by: automaticguardword Reviewed-by: Mohan Kumar D Reviewed-by: Dipesh Gandhi Reviewed-by: Sharad Gupta Reviewed-by: mobile promotions GVS: Gerrit_Virtual_Submit Tested-by: mobile promotions --- drivers/platform/tegra/nvadsp/dev.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/drivers/platform/tegra/nvadsp/dev.c b/drivers/platform/tegra/nvadsp/dev.c index 4447aeb6..ead43e69 100644 --- a/drivers/platform/tegra/nvadsp/dev.c +++ b/drivers/platform/tegra/nvadsp/dev.c @@ -3,7 +3,7 @@ * * A device driver for ADSP and APE * - * Copyright (C) 2014-2019, NVIDIA Corporation. All rights reserved. + * Copyright (C) 2014-2020, 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 @@ -29,7 +29,10 @@ #include #include #include +#include +#if KERNEL_VERSION(5, 4, 0) > LINUX_VERSION_CODE #include +#endif #include #include #include @@ -321,7 +324,9 @@ static int __init nvadsp_probe(struct platform_device *pdev) nvadsp_drv_data = drv_data; #ifdef CONFIG_PM +#if KERNEL_VERSION(5, 4, 0) > LINUX_VERSION_CODE tegra_pd_add_device(dev); +#endif pm_runtime_enable(dev); @@ -400,7 +405,9 @@ static int nvadsp_remove(struct platform_device *pdev) nvadsp_runtime_suspend(&pdev->dev); #endif +#if KERNEL_VERSION(5, 4, 0) > LINUX_VERSION_CODE tegra_pd_remove_device(&pdev->dev); +#endif return 0; } From ceb4b9f02ee5af1756cea548b3dd76b07846bc65 Mon Sep 17 00:00:00 2001 From: Bitan Biswas Date: Fri, 1 May 2020 14:33:35 -0700 Subject: [PATCH 088/138] kernel: nvidia: use fuse.h instead of chip-id.h in k5.4 bug 200591811 Change-Id: If72122efdeda154f435f79da8f2cd9cad644d380 Signed-off-by: Bitan Biswas Reviewed-on: https://git-master.nvidia.com/r/c/linux-nvidia/+/2337930 Tested-by: mobile promotions Reviewed-by: mobile promotions --- drivers/platform/tegra/nvadsp/amc.c | 7 ++++++- drivers/platform/tegra/nvadsp/dev-t18x.c | 7 ++++++- drivers/platform/tegra/nvadsp/dev.c | 5 +++++ drivers/platform/tegra/nvadsp/os-t18x.c | 5 +++++ drivers/platform/tegra/nvadsp/os.c | 5 +++++ 5 files changed, 27 insertions(+), 2 deletions(-) diff --git a/drivers/platform/tegra/nvadsp/amc.c b/drivers/platform/tegra/nvadsp/amc.c index 7743f509..bbc1a5ab 100644 --- a/drivers/platform/tegra/nvadsp/amc.c +++ b/drivers/platform/tegra/nvadsp/amc.c @@ -3,7 +3,7 @@ * * AMC and ARAM handling * - * Copyright (C) 2014-2019, NVIDIA Corporation. All rights reserved. + * Copyright (C) 2014-2020, 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 @@ -19,7 +19,12 @@ #include #include #include +#include +#if KERNEL_VERSION(4, 15, 0) > LINUX_VERSION_CODE #include +#else +#include +#endif #include "dev.h" #include "amc.h" diff --git a/drivers/platform/tegra/nvadsp/dev-t18x.c b/drivers/platform/tegra/nvadsp/dev-t18x.c index b64e053d..c3bd9318 100644 --- a/drivers/platform/tegra/nvadsp/dev-t18x.c +++ b/drivers/platform/tegra/nvadsp/dev-t18x.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015-2019, NVIDIA Corporation. All rights reserved. + * Copyright (c) 2015-2020, 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 @@ -11,7 +11,12 @@ * GNU General Public License for more details. * */ +#include +#if KERNEL_VERSION(4, 15, 0) > LINUX_VERSION_CODE #include +#else +#include +#endif #include #include #include diff --git a/drivers/platform/tegra/nvadsp/dev.c b/drivers/platform/tegra/nvadsp/dev.c index ead43e69..b59e6979 100644 --- a/drivers/platform/tegra/nvadsp/dev.c +++ b/drivers/platform/tegra/nvadsp/dev.c @@ -27,7 +27,12 @@ #include #include #include +#include +#if KERNEL_VERSION(4, 15, 0) > LINUX_VERSION_CODE #include +#else +#include +#endif #include #include #if KERNEL_VERSION(5, 4, 0) > LINUX_VERSION_CODE diff --git a/drivers/platform/tegra/nvadsp/os-t18x.c b/drivers/platform/tegra/nvadsp/os-t18x.c index 29f8dd3b..eec5e333 100644 --- a/drivers/platform/tegra/nvadsp/os-t18x.c +++ b/drivers/platform/tegra/nvadsp/os-t18x.c @@ -12,7 +12,12 @@ * */ +#include +#if KERNEL_VERSION(4, 15, 0) > LINUX_VERSION_CODE #include +#else +#include +#endif #include #include #include diff --git a/drivers/platform/tegra/nvadsp/os.c b/drivers/platform/tegra/nvadsp/os.c index 5f4c70c4..129bf6a4 100644 --- a/drivers/platform/tegra/nvadsp/os.c +++ b/drivers/platform/tegra/nvadsp/os.c @@ -29,7 +29,12 @@ #include #include #include +#include +#if KERNEL_VERSION(4, 15, 0) > LINUX_VERSION_CODE #include +#else +#include +#endif #include #include #include From 29efd1bd1b7e2838b937a437989ed09f2e2e5453 Mon Sep 17 00:00:00 2001 From: Sameer Pujar Date: Fri, 8 May 2020 22:39:54 +0530 Subject: [PATCH 089/138] nvadsp: fix build errors due to chip-id.h 5.4 kernel no longer uses chip-id.h and instead fuse.h is used. Currently this causes build issues in ADSP driver code and hence related drivers are updated to make use of references as per fuse.h for 5.4 kernel. The issue is not seen on TOT because ADSP is not yet enabled. Bug 200593718 Change-Id: I82af6a4f02ce64da4b20875a28f8ef9488b92e54 Signed-off-by: Sameer Pujar Reviewed-on: https://git-master.nvidia.com/r/c/linux-nvidia/+/2341652 Reviewed-by: automaticguardword Reviewed-by: Automatic_Commit_Validation_User Reviewed-by: Bitan Biswas Reviewed-by: Mohan Kumar D Reviewed-by: mobile promotions Tested-by: mobile promotions GVS: Gerrit_Virtual_Submit --- drivers/platform/tegra/nvadsp/adsp_shared_struct.h | 4 ++-- drivers/platform/tegra/nvadsp/os.c | 6 ++---- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/drivers/platform/tegra/nvadsp/adsp_shared_struct.h b/drivers/platform/tegra/nvadsp/adsp_shared_struct.h index d8628f64..6c0dd462 100644 --- a/drivers/platform/tegra/nvadsp/adsp_shared_struct.h +++ b/drivers/platform/tegra/nvadsp/adsp_shared_struct.h @@ -3,7 +3,7 @@ * * A header file containing shared data structures shared with ADSP OS * - * Copyright (C) 2015-2018 NVIDIA Corporation. All rights reserved. + * Copyright (C) 2015-2020 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 @@ -128,7 +128,7 @@ struct nvadsp_os_args { char logger[DRAM_DEBUG_LOG_SIZE]; uint64_t adsp_freq_hz; uint32_t dynamic_app_support; - uint32_t chip_id; + u8 chip_id; char reserved[120]; } __packed; diff --git a/drivers/platform/tegra/nvadsp/os.c b/drivers/platform/tegra/nvadsp/os.c index 129bf6a4..2157028b 100644 --- a/drivers/platform/tegra/nvadsp/os.c +++ b/drivers/platform/tegra/nvadsp/os.c @@ -49,8 +49,6 @@ #include -#include - #include "ape_actmon.h" #include "os.h" #include "dev.h" @@ -756,7 +754,7 @@ static void nvadsp_set_shared_mem(struct platform_device *pdev, struct nvadsp_drv_data *drv_data = platform_get_drvdata(pdev); struct device *dev = &pdev->dev; struct nvadsp_os_args *os_args; - enum tegra_chipid chip_id; + u8 chip_id; shared_mem->os_args.dynamic_app_support = dynamic_app_support; /* set logger strcuture with required properties */ @@ -765,7 +763,7 @@ static void nvadsp_set_shared_mem(struct platform_device *pdev, priv.logger.dev = dev; priv.adsp_os_fw_loaded = true; - chip_id = tegra_get_chipid(); + chip_id = tegra_get_chip_id(); os_args = &shared_mem->os_args; os_args->chip_id = chip_id; From bff1871f76eacf3ab265072b6a627dbf2bf446f6 Mon Sep 17 00:00:00 2001 From: Sameer Pujar Date: Thu, 2 Apr 2020 15:59:40 +0530 Subject: [PATCH 090/138] nvadsp: fix build error related to counter On 5.4 kernel, function arch_counter_get_cntvct() is not available in arch_timer.h and this causes build error. This patch fixes the issue by using __arch_counter_get_cntvct_stable() which provides similar functionality. Bug 200593718 Change-Id: I64732dc3a11effba0f05afb3f9cbd50351047ee8 Signed-off-by: Sameer Pujar Reviewed-on: https://git-master.nvidia.com/r/c/linux-nvidia/+/2341653 Reviewed-by: automaticguardword Reviewed-by: Mohan Kumar D Reviewed-by: Sharad Gupta Reviewed-by: mobile promotions GVS: Gerrit_Virtual_Submit Tested-by: mobile promotions --- drivers/platform/tegra/nvadsp/dev.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/drivers/platform/tegra/nvadsp/dev.c b/drivers/platform/tegra/nvadsp/dev.c index b59e6979..21997ae1 100644 --- a/drivers/platform/tegra/nvadsp/dev.c +++ b/drivers/platform/tegra/nvadsp/dev.c @@ -34,7 +34,6 @@ #include #endif #include -#include #if KERNEL_VERSION(5, 4, 0) > LINUX_VERSION_CODE #include #endif @@ -129,7 +128,11 @@ static const struct dev_pm_ops nvadsp_pm_ops = { uint64_t nvadsp_get_timestamp_counter(void) { +#if KERNEL_VERSION(5, 4, 0) > LINUX_VERSION_CODE return arch_counter_get_cntvct(); +#else + return __arch_counter_get_cntvct_stable(); +#endif } EXPORT_SYMBOL(nvadsp_get_timestamp_counter); From 68ea0dc35bd9522f9d0ee5f629ddfe0821abacdb Mon Sep 17 00:00:00 2001 From: Sameer Pujar Date: Sun, 10 May 2020 10:06:15 +0530 Subject: [PATCH 091/138] nvadsp: fix build error due to elf_hwcap Build error is seen on 5.4 kernel due to 'elf_hwcap' not being available. Commit aec0bff757c9 ("arm64: HWCAP: encapsulate elf_hwcap") in upstream removes direct expose of 'elf_hwcap' and instead provides a helper function cpu_get_elf_hwcap() for it. To fix build error this patch includes 'asm/hwcap.h' which works well for all supported kernel versions in downstream. The macro ELF_HWCAP, from this header, can be used for this purpose. * For kernel < 5.4 : ELF_HWCAP --> elf_hwcap * For kernel >= 5.4 : ELF_HWCAP --> cpu_get_elf_hwcap() Bug 200593718 Change-Id: I3ef3527be5972d6f6ba6ab6ef6dc50936dff73fd Signed-off-by: Sameer Pujar Reviewed-on: https://git-master.nvidia.com/r/c/linux-nvidia/+/2341654 Reviewed-by: automaticguardword Reviewed-by: Automatic_Commit_Validation_User Reviewed-by: Mohan Kumar D Reviewed-by: Sharad Gupta Reviewed-by: mobile promotions GVS: Gerrit_Virtual_Submit Tested-by: mobile promotions --- drivers/platform/tegra/nvadsp/app_loader_linker.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/drivers/platform/tegra/nvadsp/app_loader_linker.c b/drivers/platform/tegra/nvadsp/app_loader_linker.c index 3e64bdd9..303d6993 100644 --- a/drivers/platform/tegra/nvadsp/app_loader_linker.c +++ b/drivers/platform/tegra/nvadsp/app_loader_linker.c @@ -3,7 +3,7 @@ * * ADSP OS App management * - * Copyright (C) 2014-2017 NVIDIA Corporation. All rights reserved. + * Copyright (C) 2014-2020 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 @@ -24,6 +24,7 @@ #include #include #include +#include #include "os.h" #include "dram_app_mem_manager.h" @@ -789,7 +790,7 @@ static int elf_check_arch_arm32(const struct elf32_hdr *x) /* Make sure the entry address is reasonable */ if (x->e_entry & 1) { - if (!(elf_hwcap & HWCAP_THUMB)) + if (!(ELF_HWCAP & HWCAP_THUMB)) return 0; } else if (x->e_entry & 3) return 0; @@ -799,13 +800,13 @@ static int elf_check_arch_arm32(const struct elf32_hdr *x) unsigned int flt_fmt; /* APCS26 is only allowed if the CPU supports it */ - if ((eflags & EF_ARM_APCS_26) && !(elf_hwcap & HWCAP_26BIT)) + if ((eflags & EF_ARM_APCS_26) && !(ELF_HWCAP & HWCAP_26BIT)) return 0; flt_fmt = eflags & (EF_ARM_VFP_FLOAT | EF_ARM_SOFT_FLOAT); /* VFP requires the supporting code */ - if (flt_fmt == EF_ARM_VFP_FLOAT && !(elf_hwcap & HWCAP_VFP)) + if (flt_fmt == EF_ARM_VFP_FLOAT && !(ELF_HWCAP & HWCAP_VFP)) return 0; } return 1; From 134ade9bf2e672ec82357ea1ce685f30f27ff217 Mon Sep 17 00:00:00 2001 From: Sameer Pujar Date: Fri, 15 May 2020 16:29:00 +0530 Subject: [PATCH 092/138] nvadsp: request clk_round_rate() for 600 MHz On 5.4 kernel clk_round_rate(), for ACLK with ULONG_MAX request, is returning minimum possible value for ACLK and this is causing a failure with ADSP OS load. The issue is found in BPMP where there is a problem with data mis-alignment. Till this gets addressed a TEMP change is provided here to unblock ADSP audio related work. This can be reverted once original BPMP fix is merged. Bug 200593718 Change-Id: I1965986770424f2a8560e011ca892b700cdc0258 Signed-off-by: Sameer Pujar Reviewed-on: https://git-master.nvidia.com/r/c/linux-nvidia/+/2345133 Reviewed-by: automaticguardword Reviewed-by: Dipesh Gandhi Reviewed-by: Uday Gupta Reviewed-by: Mohan Kumar D Reviewed-by: Sharad Gupta Reviewed-by: mobile promotions Tested-by: mobile promotions GVS: Gerrit_Virtual_Submit --- drivers/platform/tegra/nvadsp/os.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/platform/tegra/nvadsp/os.c b/drivers/platform/tegra/nvadsp/os.c index 2157028b..a7b56ae3 100644 --- a/drivers/platform/tegra/nvadsp/os.c +++ b/drivers/platform/tegra/nvadsp/os.c @@ -1039,7 +1039,11 @@ static int nvadsp_set_adsp_clks(struct nvadsp_drv_data *drv_data) adsp_freq = drv_data->adsp_freq_hz; /* in Hz*/ /* round rate shall be used with adsp parent clk i.e. aclk */ +#if KERNEL_VERSION(5, 4, 0) > LINUX_VERSION_CODE max_adsp_freq = clk_round_rate(drv_data->aclk_clk, ULONG_MAX); +#else + max_adsp_freq = clk_round_rate(drv_data->aclk_clk, 600000000); +#endif /* Set max adsp boot freq */ if (!adsp_freq) From c880772741ec5f447c8ff2e80a12386cc24a0e1e Mon Sep 17 00:00:00 2001 From: Bitan Biswas Date: Tue, 19 May 2020 13:47:28 -0700 Subject: [PATCH 093/138] nvidia: drivers: fix linux-5.7-rc5 build Fix build linux-5.7-rc5 errors including following: - change timespec to timespec64. replace getnstimeofday with ktime_get_ts64 - replace usage of macro FIELD_SIZEOF with sizeof_field in ethtool.c nvethernet and eqos files. - support 2 arguments for of_get_phy_mode call bug 200617764 Change-Id: I46067d7d36d08ee9556b2722e9ccec707b8853d4 Signed-off-by: Bitan Biswas Reviewed-on: https://git-master.nvidia.com/r/c/linux-nvidia/+/2347244 Tested-by: mobile promotions Reviewed-by: mobile promotions --- drivers/platform/tegra/nvadsp/acast.c | 2 +- drivers/platform/tegra/nvadsp/os.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/platform/tegra/nvadsp/acast.c b/drivers/platform/tegra/nvadsp/acast.c index 5342caec..0fab3403 100644 --- a/drivers/platform/tegra/nvadsp/acast.c +++ b/drivers/platform/tegra/nvadsp/acast.c @@ -118,7 +118,7 @@ int nvadsp_acast_init(struct platform_device *pdev) int i; if (!acast_base) { - acast_base = devm_ioremap_nocache(dev, ACAST_BASE, ACAST_SIZE); + acast_base = devm_ioremap(dev, ACAST_BASE, ACAST_SIZE); if (IS_ERR_OR_NULL(acast_base)) { dev_err(dev, "failed to map ACAST\n"); return PTR_ERR(acast_base); diff --git a/drivers/platform/tegra/nvadsp/os.c b/drivers/platform/tegra/nvadsp/os.c index a7b56ae3..c55e1757 100644 --- a/drivers/platform/tegra/nvadsp/os.c +++ b/drivers/platform/tegra/nvadsp/os.c @@ -726,7 +726,7 @@ static int allocate_memory_for_adsp_os(void) goto end; } #else - dram_va = ioremap_nocache(addr, size); + dram_va = ioremap(addr, size); if (!dram_va) { dev_err(dev, "remap failed for addr 0x%llx\n", addr); ret = -ENOMEM; From 08391815ee19036720c057c5ecb140dd303be399 Mon Sep 17 00:00:00 2001 From: Sameer Pujar Date: Wed, 3 Jun 2020 08:24:58 +0530 Subject: [PATCH 094/138] Revert "[TEMP] nvadsp: request clk_round_rate() for 600 MHz" This reverts commit 9786bb772a32456102f4de622c9ce3ee35784920. The original commit earlier was done to avoid a clock issue in BPMP as a temporary fix. Since now the root cause is addressed in BPMP it can be reverted. Bug 200617376 Change-Id: I64df2f33e1fd294c151ae4f7b39e2d9ea321b5c6 Signed-off-by: Sameer Pujar Reviewed-on: https://git-master.nvidia.com/r/c/linux-nvidia/+/2354898 Reviewed-by: automaticguardword Reviewed-by: Mohan Kumar D Reviewed-by: Uday Gupta Reviewed-by: Sivaram Nair Reviewed-by: mobile promotions Tested-by: mobile promotions GVS: Gerrit_Virtual_Submit --- drivers/platform/tegra/nvadsp/os.c | 4 ---- 1 file changed, 4 deletions(-) diff --git a/drivers/platform/tegra/nvadsp/os.c b/drivers/platform/tegra/nvadsp/os.c index c55e1757..5a2dee80 100644 --- a/drivers/platform/tegra/nvadsp/os.c +++ b/drivers/platform/tegra/nvadsp/os.c @@ -1039,11 +1039,7 @@ static int nvadsp_set_adsp_clks(struct nvadsp_drv_data *drv_data) adsp_freq = drv_data->adsp_freq_hz; /* in Hz*/ /* round rate shall be used with adsp parent clk i.e. aclk */ -#if KERNEL_VERSION(5, 4, 0) > LINUX_VERSION_CODE max_adsp_freq = clk_round_rate(drv_data->aclk_clk, ULONG_MAX); -#else - max_adsp_freq = clk_round_rate(drv_data->aclk_clk, 600000000); -#endif /* Set max adsp boot freq */ if (!adsp_freq) From 4614098993e810a9dcc7fdf60ce46ce05bc8e16a Mon Sep 17 00:00:00 2001 From: Sameer Pujar Date: Fri, 11 Sep 2020 20:52:13 +0530 Subject: [PATCH 095/138] nvadsp: fix build errors on v5.9 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Following build errors are seen when ADSP configs CONFIG_TEGRA_ADSP_FILEIO and CONFIG_TEGRA_ADSP_LPTHREAD are enabled. * Error due to "segment.h" adsp_lpthread.c:14:10: fatal error: asm/segment.h: No such file or directory adspff.c:17:10: fatal error: asm/segment.h: No such file or directory (asm/segment.h inclusion is removed as asm/uaccess.h already takes care of required dependencies) * Error due to unavailable structure "sched_param" adspff.c:517:21: error: variable ‘param’ has initializer but incomplete type adspff.c:518:3: error: ‘const struct sched_param’ has no member named ‘sched_priority’ (Replace with to fix this build issue) * Error due to unavailable "get_ds()" adspff.c:63:9: error: implicit declaration of function ‘get_ds’; did you mean ‘get_fs’? [-Werror=implicit-function-declaration] (Fixed by replacing get_ds() with KERNEL_DS. The get_ds() support is removed from upstream kernel) Bug 200593718 Bug 200657500 Change-Id: I8b9cbed5ee42f34cf3731ea2d2f06aa760c28358 Signed-off-by: Sameer Pujar Reviewed-on: https://git-master.nvidia.com/r/c/linux-nvidia/+/2412299 Reviewed-by: automaticguardword Reviewed-by: Mohan Kumar D Reviewed-by: Dipesh Gandhi Reviewed-by: Sharad Gupta Reviewed-by: mobile promotions Tested-by: mobile promotions GVS: Gerrit_Virtual_Submit --- drivers/platform/tegra/nvadsp/adsp_lpthread.c | 3 +-- drivers/platform/tegra/nvadsp/adspff.c | 13 ++++++------- 2 files changed, 7 insertions(+), 9 deletions(-) diff --git a/drivers/platform/tegra/nvadsp/adsp_lpthread.c b/drivers/platform/tegra/nvadsp/adsp_lpthread.c index 8a827a29..3646a70e 100644 --- a/drivers/platform/tegra/nvadsp/adsp_lpthread.c +++ b/drivers/platform/tegra/nvadsp/adsp_lpthread.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016-2019, NVIDIA CORPORATION. All rights reserved. + * Copyright (c) 2016-2020, NVIDIA CORPORATION. All rights reserved. * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU General Public License, @@ -11,7 +11,6 @@ * more details. */ -#include #include #include diff --git a/drivers/platform/tegra/nvadsp/adspff.c b/drivers/platform/tegra/nvadsp/adspff.c index 0655c4a3..e7199e98 100644 --- a/drivers/platform/tegra/nvadsp/adspff.c +++ b/drivers/platform/tegra/nvadsp/adspff.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016-2019, NVIDIA CORPORATION. All rights reserved. + * Copyright (c) 2016-2020, NVIDIA CORPORATION. All rights reserved. * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU General Public License, @@ -14,7 +14,6 @@ #define pr_fmt(fmt) "adspff: " fmt #include -#include #include #include @@ -22,13 +21,13 @@ #include #include #include -#include #include #include #include #include #include +#include #include "adspff.h" #include "dev.h" @@ -60,7 +59,7 @@ struct file *file_open(const char *path, int flags, int rights) int err = 0; oldfs = get_fs(); - set_fs(get_ds()); + set_fs(KERNEL_DS); filp = filp_open(path, flags, rights); set_fs(oldfs); if (IS_ERR(filp)) { @@ -82,7 +81,7 @@ int file_write(struct file *file, unsigned long long *offset, int ret = 0; oldfs = get_fs(); - set_fs(get_ds()); + set_fs(KERNEL_DS); ret = vfs_write(file, data, size, offset); @@ -97,7 +96,7 @@ uint32_t file_read(struct file *file, unsigned long long *offset, uint32_t ret = 0; oldfs = get_fs(); - set_fs(get_ds()); + set_fs(KERNEL_DS); ret = vfs_read(file, data, size, offset); @@ -112,7 +111,7 @@ uint32_t file_size(struct file *file) uint32_t size = 0; oldfs = get_fs(); - set_fs(get_ds()); + set_fs(KERNEL_DS); size = vfs_llseek(file, 0, SEEK_END); From 86b6ccfd04820e1b460832855ebc9960e1fc4c70 Mon Sep 17 00:00:00 2001 From: Sameer Pujar Date: Mon, 21 Sep 2020 12:30:32 +0530 Subject: [PATCH 096/138] nvadsp: fix v5.9-rc4 build error MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit After v5.9-rc4 merge in dev-main-5.9 many build errors are seen with ADSP config CONFIG_TEGRA_ADSP_LPTHREAD. Some of these build errors are listed below. * kernel/nvidia/drivers/platform/tegra/nvadsp/adsp_lpthread.c:15:0: kernel-5.9/include/linux/uaccess.h: In function ‘force_uaccess_begin’: kernel-5.9/include/linux/uaccess.h:20:2: error: implicit declaration of function ‘set_fs’; did you mean ‘get_fs’? [-Werror=implicit-function-declaration] set_fs(USER_DS); ^~~~~~ * kernel-5.9/arch/arm64/include/asm/uaccess.h: In function ‘__uaccess_mask_ptr’: kernel-5.9/arch/arm64/include/asm/uaccess.h:240:41: error: invalid type argument of ‘->’ (have ‘int’) : "r" (ptr), "r" (current_thread_info()->addr_limit), ^~ ... The errors appear to be related to 'uaccess.h' header and following inclusion order is causing build failures for some reason. Checked with other source files with below and same behavior is seen. #include #include ... Issue goes away after replacing 'asm/uaccess.h' with 'linux/uaccess.h'. It should be noted that latter header by default includes the former one. For consistency this is done for other relevant ADSP files too. Bug 200657500 Change-Id: I7ce98c46327a825711c4fa9ab791afc65b1cbf1b Signed-off-by: Sameer Pujar Reviewed-on: https://git-master.nvidia.com/r/c/linux-nvidia/+/2417265 Reviewed-by: automaticguardword Reviewed-by: Automatic_Commit_Validation_User Reviewed-by: Mohan Kumar D Reviewed-by: Sachin Nikam Reviewed-by: Uday Gupta Reviewed-by: Sharad Gupta Reviewed-by: mobile promotions Tested-by: mobile promotions GVS: Gerrit_Virtual_Submit --- drivers/platform/tegra/nvadsp/adsp_lpthread.c | 2 +- drivers/platform/tegra/nvadsp/adspff.c | 2 +- drivers/platform/tegra/nvadsp/os.c | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/platform/tegra/nvadsp/adsp_lpthread.c b/drivers/platform/tegra/nvadsp/adsp_lpthread.c index 3646a70e..5caa72ef 100644 --- a/drivers/platform/tegra/nvadsp/adsp_lpthread.c +++ b/drivers/platform/tegra/nvadsp/adsp_lpthread.c @@ -11,7 +11,7 @@ * more details. */ -#include +#include #include #include diff --git a/drivers/platform/tegra/nvadsp/adspff.c b/drivers/platform/tegra/nvadsp/adspff.c index e7199e98..206ca7b9 100644 --- a/drivers/platform/tegra/nvadsp/adspff.c +++ b/drivers/platform/tegra/nvadsp/adspff.c @@ -14,7 +14,7 @@ #define pr_fmt(fmt) "adspff: " fmt #include -#include +#include #include #include diff --git a/drivers/platform/tegra/nvadsp/os.c b/drivers/platform/tegra/nvadsp/os.c index 5a2dee80..f860d479 100644 --- a/drivers/platform/tegra/nvadsp/os.c +++ b/drivers/platform/tegra/nvadsp/os.c @@ -47,7 +47,7 @@ #include #include -#include +#include #include "ape_actmon.h" #include "os.h" From 3b69d4961c3d3cf8d26461e7ca8e442247d33e68 Mon Sep 17 00:00:00 2001 From: Sameer Pujar Date: Tue, 22 Sep 2020 18:46:19 +0530 Subject: [PATCH 097/138] nvadsp: Convert to set_sched_fifo*() for v5.9 While enabling 'CONFIG_TEGRA_ADSP_FILEIO' following build error is seen with v5.9-rc4 build. ERROR: modpost: "sched_setscheduler" [drivers/platform/tegra/ nvadsp/nvadsp.ko] undefined! This happens because sched_setscheduler() export is removed with commit 616d91b68cd5 ("sched: Remove sched_setscheduler*() EXPORTs") in upstream. The reason for removal is below. Upstream commit 7318d4cc14c8 ("sched: Provide sched_set_fifo()") provides sched_set_*() functions and suggests to convert existing sched_setscheduler_*() calls to sched_set_*(). As per this following updates are made for ADSP driver to work for v5.9. - Use sched_set_fifo() whenever a high priority is required. (However by default priority is set to MAX_RT/2) - Use sched_set_fifo_low() whenever priority needs to be above SCHED_NORMAL. Since it is a recent change in upstream kernel above changes are not applicable for previous kernels (v4.14 and v4.9). Hence these can continue to use existing calls. Thus above changes are protected under kernel version checks. Bug 200657500 Change-Id: I763fb7d1461e23e1eeb26d7c49a295172450e54d Signed-off-by: Sameer Pujar Reviewed-on: https://git-master.nvidia.com/r/c/linux-nvidia/+/2417919 Reviewed-by: automaticguardword Reviewed-by: Uday Gupta Reviewed-by: Mohan Kumar D Reviewed-by: Bitan Biswas Reviewed-by: mobile promotions GVS: Gerrit_Virtual_Submit Tested-by: mobile promotions --- drivers/platform/tegra/nvadsp/adspff.c | 9 ++++++++- drivers/platform/tegra/nvadsp/emc_dfs.c | 9 ++++++++- 2 files changed, 16 insertions(+), 2 deletions(-) diff --git a/drivers/platform/tegra/nvadsp/adspff.c b/drivers/platform/tegra/nvadsp/adspff.c index 206ca7b9..79eaccd5 100644 --- a/drivers/platform/tegra/nvadsp/adspff.c +++ b/drivers/platform/tegra/nvadsp/adspff.c @@ -511,10 +511,11 @@ send_ack: kfree(msg_recv); } - +#if KERNEL_VERSION(5, 9, 0) > LINUX_VERSION_CODE static const struct sched_param param = { .sched_priority = 1, }; +#endif static struct task_struct *adspff_kthread; static struct list_head adspff_kthread_msgq_head; static wait_queue_head_t wait_queue; @@ -678,7 +679,13 @@ int adspff_init(struct platform_device *pdev) init_waitqueue_head(&wait_queue); adspff_kthread = kthread_create(adspff_kthread_fn, NULL, "adspp_kthread"); + +#if KERNEL_VERSION(5, 9, 0) > LINUX_VERSION_CODE sched_setscheduler(adspff_kthread, SCHED_FIFO, ¶m); +#else + sched_set_fifo_low(adspff_kthread); +#endif + get_task_struct(adspff_kthread); wake_up_process(adspff_kthread); diff --git a/drivers/platform/tegra/nvadsp/emc_dfs.c b/drivers/platform/tegra/nvadsp/emc_dfs.c index 0195497c..e0964e78 100644 --- a/drivers/platform/tegra/nvadsp/emc_dfs.c +++ b/drivers/platform/tegra/nvadsp/emc_dfs.c @@ -3,7 +3,7 @@ * * Emc dynamic frequency scaling due to APE * - * Copyright (C) 2014-2016, NVIDIA Corporation. All rights reserved. + * Copyright (C) 2014-2020, 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 @@ -400,7 +400,9 @@ err_out: status_t __init emc_dfs_init(struct platform_device *pdev) { struct nvadsp_drv_data *drv = platform_get_drvdata(pdev); +#if KERNEL_VERSION(5, 9, 0) > LINUX_VERSION_CODE struct sched_param param = { .sched_priority = MAX_RT_PRIO - 1 }; +#endif int ret = 0; einfo = &global_emc_info; @@ -442,7 +444,12 @@ status_t __init emc_dfs_init(struct platform_device *pdev) if (IS_ERR(speedchange_task)) return PTR_ERR(speedchange_task); +#if KERNEL_VERSION(5, 9, 0) > LINUX_VERSION_CODE sched_setscheduler_nocheck(speedchange_task, SCHED_FIFO, ¶m); +#else + sched_set_fifo(speedchange_task); +#endif + get_task_struct(speedchange_task); /* NB: wake up so the thread does not look hung to the freezer */ From 4464d2600746d958685b172e3b2d6a1bc3aed74f Mon Sep 17 00:00:00 2001 From: Mohan Kumar Date: Wed, 16 Sep 2020 12:47:36 +0530 Subject: [PATCH 098/138] nvadsp: Add Interconnect support for ADSP Add Interconnect API support for ADSP Memory bandwidth requirement handling. From Kernel 4.14 and beyond - On T23X and newer platforms, the interconnects driver will be initialized and calls to BWMGR will be stubbed out. - On T194 and earlier platforms, BWMGR driver will be initialized and calls to interconnect framework will be stubbed out. Added code check with K5.9 as mc_utils.h header is present from K5.9 build For Kernel 4.9 and earlier - T23x will not be supported. - T194 and earlier platforms will use BWMGR. Interconnect framework is not available. JIRA TAS-1060 Change-Id: Ie0b4040f374a3c6102cb7aff2506d8cf2f0eab69 Signed-off-by: Mohan Kumar Reviewed-on: https://git-master.nvidia.com/r/c/linux-nvidia/+/2414369 Reviewed-by: Viswanath L Reviewed-by: Sameer Pujar Reviewed-by: Sharad Gupta Reviewed-by: mobile promotions GVS: Gerrit_Virtual_Submit Tested-by: mobile promotions --- drivers/platform/tegra/nvadsp/adsp_dfs.c | 22 ++----- drivers/platform/tegra/nvadsp/dev.c | 84 ++++++++++++++++++++---- drivers/platform/tegra/nvadsp/dev.h | 14 +++- drivers/platform/tegra/nvadsp/os.c | 5 +- 4 files changed, 91 insertions(+), 34 deletions(-) diff --git a/drivers/platform/tegra/nvadsp/adsp_dfs.c b/drivers/platform/tegra/nvadsp/adsp_dfs.c index 6e9f6884..3f3dd243 100644 --- a/drivers/platform/tegra/nvadsp/adsp_dfs.c +++ b/drivers/platform/tegra/nvadsp/adsp_dfs.c @@ -3,7 +3,7 @@ * * adsp dynamic frequency scaling * - * Copyright (C) 2014-2019, NVIDIA Corporation. All rights reserved. + * Copyright (C) 2014-2020, 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 @@ -361,10 +361,8 @@ static unsigned long update_freq(unsigned long freq_khz) efreq = adsp_to_emc_freq(tfreq_hz / 1000); - ret = tegra_bwmgr_set_emc(drv->bwmgr, efreq * 1000, - TEGRA_BWMGR_SET_EMC_FLOOR); + ret = nvadsp_set_bw(drv, efreq); if (ret) { - dev_err(device, "failed to set emc freq rate:%d\n", ret); policy->update_freq_flag = false; goto err_out; } @@ -401,13 +399,10 @@ err_out: efreq = adsp_to_emc_freq(old_freq_khz); - ret = tegra_bwmgr_set_emc(drv->bwmgr, efreq * 1000, - TEGRA_BWMGR_SET_EMC_FLOOR); - if (ret) { - dev_err(device, "failed to set emc freq rate:%d\n", - ret); + ret = nvadsp_set_bw(drv, efreq); + if (ret) policy->update_freq_flag = false; - } + tfreq_hz = old_freq_khz * 1000; } return tfreq_hz / 1000; @@ -829,12 +824,9 @@ int adsp_dfs_core_init(struct platform_device *pdev) efreq = adsp_to_emc_freq(policy->cur); - ret = tegra_bwmgr_set_emc(drv->bwmgr, efreq * 1000, - TEGRA_BWMGR_SET_EMC_FLOOR); - if (ret) { - dev_err(device, "failed to set emc freq rate:%d\n", ret); + ret = nvadsp_set_bw(drv, efreq); + if (ret) goto end; - } adsp_get_target_freq(policy->cur * 1000, &freq_stats.last_index); freq_stats.last_time = get_jiffies_64(); diff --git a/drivers/platform/tegra/nvadsp/dev.c b/drivers/platform/tegra/nvadsp/dev.c index 21997ae1..58114d70 100644 --- a/drivers/platform/tegra/nvadsp/dev.c +++ b/drivers/platform/tegra/nvadsp/dev.c @@ -136,6 +136,73 @@ uint64_t nvadsp_get_timestamp_counter(void) } EXPORT_SYMBOL(nvadsp_get_timestamp_counter); +int nvadsp_set_bw(struct nvadsp_drv_data *drv_data, u32 efreq) +{ + int ret = -EINVAL; + + if (drv_data->bwmgr) + ret = tegra_bwmgr_set_emc(drv_data->bwmgr, efreq * 1000, + TEGRA_BWMGR_SET_EMC_FLOOR); +#if KERNEL_VERSION(5, 9, 0) <= LINUX_VERSION_CODE + else if (drv_data->icc_path_handle) + ret = icc_set_bw(drv_data->icc_path_handle, 0, + (unsigned long)FREQ2ICC(efreq * 1000)); +#endif + if (ret) + dev_err(&drv_data->pdev->dev, + "failed to set emc freq rate:%d\n", ret); + + return ret; +} + +static void nvadsp_bw_register(struct nvadsp_drv_data *drv_data) +{ + struct device *dev = &drv_data->pdev->dev; + + switch (tegra_get_chip_id()) { + case TEGRA210: + case TEGRA186: + case TEGRA194: + drv_data->bwmgr = tegra_bwmgr_register( + TEGRA_BWMGR_CLIENT_APE_ADSP); + if (IS_ERR(drv_data->bwmgr)) { + dev_err(dev, "unable to register bwmgr\n"); + drv_data->bwmgr = NULL; + } + break; + default: +#if KERNEL_VERSION(5, 9, 0) <= LINUX_VERSION_CODE + /* Interconnect Support */ + drv_data->icc_path_handle = icc_get(dev, TEGRA_ICC_APE, + TEGRA_ICC_PRIMARY); + if (IS_ERR(drv_data->icc_path_handle)) { + dev_err(dev, + "%s: Failed to register Interconnect. err=%ld\n", + __func__, PTR_ERR(drv_data->icc_path_handle)); + drv_data->icc_path_handle = NULL; + } +#endif + break; + } +} + +static void nvadsp_bw_unregister(struct nvadsp_drv_data *drv_data) +{ + nvadsp_set_bw(drv_data, 0); + + if (drv_data->bwmgr) { + tegra_bwmgr_unregister(drv_data->bwmgr); + drv_data->bwmgr = NULL; + } + +#if KERNEL_VERSION(5, 9, 0) <= LINUX_VERSION_CODE + if (drv_data->icc_path_handle) { + icc_put(drv_data->icc_path_handle); + drv_data->icc_path_handle = NULL; + } +#endif +} + static void __init nvadsp_parse_clk_entries(struct platform_device *pdev) { struct nvadsp_drv_data *drv_data = platform_get_drvdata(pdev); @@ -376,10 +443,7 @@ static int __init nvadsp_probe(struct platform_device *pdev) 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"); + nvadsp_bw_register(drv_data); err: #ifdef CONFIG_PM ret = pm_runtime_put_sync(dev); @@ -393,17 +457,9 @@ out: 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_bw_unregister(drv_data); + nvadsp_aram_exit(); pm_runtime_disable(&pdev->dev); diff --git a/drivers/platform/tegra/nvadsp/dev.h b/drivers/platform/tegra/nvadsp/dev.h index 75bfc448..10a5a125 100644 --- a/drivers/platform/tegra/nvadsp/dev.h +++ b/drivers/platform/tegra/nvadsp/dev.h @@ -3,7 +3,7 @@ * * A header file for Host driver for ADSP and APE * - * Copyright (C) 2014-2019, NVIDIA Corporation. All rights reserved. + * Copyright (C) 2014-2020, 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 @@ -25,6 +25,11 @@ #include #include +#if KERNEL_VERSION(5, 9, 0) <= LINUX_VERSION_CODE +#include +#include +#include +#endif #include "hwmailbox.h" #include "amc.h" @@ -89,6 +94,9 @@ enum adsp_unit_fpga_reset { #define AMISC_REG_MBOX_OFFSET 0x64 #define ADSP_ACTMON_REG_START_OFFSET 0x800 #define ADSP_ACTMON_REG_END_OFFSET 0x828 +#if KERNEL_VERSION(5, 9, 0) <= LINUX_VERSION_CODE +#define FREQ2ICC(x) (Bps_to_icc(emc_freq_to_bw(x))) +#endif enum nvadsp_virqs { MBOX_SEND_VIRQ, @@ -214,6 +222,9 @@ struct nvadsp_drv_data { int agic_irqs[NVADSP_VIRQ_MAX]; struct tegra_bwmgr_client *bwmgr; +#if KERNEL_VERSION(5, 9, 0) <= LINUX_VERSION_CODE + struct icc_path *icc_path_handle; /* icc_path handle handle */ +#endif u32 evp_base[ADSP_EVP_END]; const struct nvadsp_chipdata *chip_data; @@ -227,6 +238,7 @@ status_t nvadsp_mbox_init(struct platform_device *pdev); int nvadsp_setup_amc_interrupts(struct platform_device *pdev); void nvadsp_free_amc_interrupts(struct platform_device *pdev); +int nvadsp_set_bw(struct nvadsp_drv_data *drv, u32 efreq); #ifdef CONFIG_TEGRA_ADSP_DFS void adsp_cpu_set_rate(unsigned long freq); diff --git a/drivers/platform/tegra/nvadsp/os.c b/drivers/platform/tegra/nvadsp/os.c index f860d479..d6eb7e85 100644 --- a/drivers/platform/tegra/nvadsp/os.c +++ b/drivers/platform/tegra/nvadsp/os.c @@ -936,10 +936,7 @@ static int nvadsp_set_ape_emc_freq(struct nvadsp_drv_data *drv_data) if (!ape_emc_freq) return 0; - ret = tegra_bwmgr_set_emc(drv_data->bwmgr, ape_emc_freq * 1000, - TEGRA_BWMGR_SET_EMC_FLOOR); - if (ret) - dev_err(dev, "failed to set emc freq rate:%d\n", ret); + ret = nvadsp_set_bw(drv_data, ape_emc_freq); dev_dbg(dev, "ape.emc freq %luKHz\n", tegra_bwmgr_get_emc_rate() / 1000); From 56135207fa9e3a1aae77f68b1c11d46f06810d5b Mon Sep 17 00:00:00 2001 From: Bitan Biswas Date: Tue, 23 Mar 2021 05:52:38 -0700 Subject: [PATCH 099/138] platform: tegra: nvadsp: fix external build Fix build errors in external build due to unavailable headers bug 200705253 Change-Id: Ic4627681a9dee0e47d2da57d4247274b150928f5 Signed-off-by: Bitan Biswas Reviewed-on: https://git-master.nvidia.com/r/c/linux-nvidia/+/2503918 Reviewed-by: svcacv Reviewed-by: Sameer Pujar Reviewed-by: Ajay Nandakumar M Reviewed-by: Manish Bhardwaj Reviewed-by: mobile promotions Tested-by: mobile promotions GVS: Gerrit_Virtual_Submit --- drivers/platform/tegra/nvadsp/dev.c | 4 +++- drivers/platform/tegra/nvadsp/dev.h | 8 +++++++- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/drivers/platform/tegra/nvadsp/dev.c b/drivers/platform/tegra/nvadsp/dev.c index 58114d70..193a7176 100644 --- a/drivers/platform/tegra/nvadsp/dev.c +++ b/drivers/platform/tegra/nvadsp/dev.c @@ -3,7 +3,7 @@ * * A device driver for ADSP and APE * - * Copyright (C) 2014-2020, NVIDIA Corporation. All rights reserved. + * Copyright (C) 2014-2021, 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 @@ -173,8 +173,10 @@ static void nvadsp_bw_register(struct nvadsp_drv_data *drv_data) default: #if KERNEL_VERSION(5, 9, 0) <= LINUX_VERSION_CODE /* Interconnect Support */ +#ifdef CONFIG_ARCH_TEGRA_23x_SOC drv_data->icc_path_handle = icc_get(dev, TEGRA_ICC_APE, TEGRA_ICC_PRIMARY); +#endif if (IS_ERR(drv_data->icc_path_handle)) { dev_err(dev, "%s: Failed to register Interconnect. err=%ld\n", diff --git a/drivers/platform/tegra/nvadsp/dev.h b/drivers/platform/tegra/nvadsp/dev.h index 10a5a125..f5a07029 100644 --- a/drivers/platform/tegra/nvadsp/dev.h +++ b/drivers/platform/tegra/nvadsp/dev.h @@ -3,7 +3,7 @@ * * A header file for Host driver for ADSP and APE * - * Copyright (C) 2014-2020, NVIDIA Corporation. All rights reserved. + * Copyright (C) 2014-2021, 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 @@ -26,8 +26,10 @@ #include #if KERNEL_VERSION(5, 9, 0) <= LINUX_VERSION_CODE +#ifdef CONFIG_ARCH_TEGRA_23x_SOC #include #include +#endif #include #endif @@ -95,7 +97,11 @@ enum adsp_unit_fpga_reset { #define ADSP_ACTMON_REG_START_OFFSET 0x800 #define ADSP_ACTMON_REG_END_OFFSET 0x828 #if KERNEL_VERSION(5, 9, 0) <= LINUX_VERSION_CODE +#ifdef CONFIG_ARCH_TEGRA_23x_SOC #define FREQ2ICC(x) (Bps_to_icc(emc_freq_to_bw(x))) +#else +#define FREQ2ICC(x) 0UL +#endif #endif enum nvadsp_virqs { From 5190a1db72af28efa956381fe70bffdd77620ae6 Mon Sep 17 00:00:00 2001 From: Ajay Nandakumar Date: Tue, 4 May 2021 17:52:45 +0530 Subject: [PATCH 100/138] nvadsp: change chip_id type in shared struct The chip_id type in shared struct is u8 where as across the adsp firmwares the chip_id type is of type uint32_t. This causes shared structure sizes to be different causing firmwares and causes different address of the shared struct variables when accessed from the Host and the adsp firmware. Bug 200692666 Change-Id: I6ad23f021f64458d87596b9d6c47c9df38e3689b Signed-off-by: Ajay Nandakumar Reviewed-on: https://git-master.nvidia.com/r/c/linux-nvidia/+/2524414 Reviewed-by: Sachin Nikam Reviewed-by: Nikesh Oswal Reviewed-by: mobile promotions GVS: Gerrit_Virtual_Submit Tested-by: mobile promotions --- drivers/platform/tegra/nvadsp/adsp_shared_struct.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/platform/tegra/nvadsp/adsp_shared_struct.h b/drivers/platform/tegra/nvadsp/adsp_shared_struct.h index 6c0dd462..7a08b8c0 100644 --- a/drivers/platform/tegra/nvadsp/adsp_shared_struct.h +++ b/drivers/platform/tegra/nvadsp/adsp_shared_struct.h @@ -3,7 +3,7 @@ * * A header file containing shared data structures shared with ADSP OS * - * Copyright (C) 2015-2020 NVIDIA Corporation. All rights reserved. + * Copyright (C) 2015-2021 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 @@ -128,7 +128,7 @@ struct nvadsp_os_args { char logger[DRAM_DEBUG_LOG_SIZE]; uint64_t adsp_freq_hz; uint32_t dynamic_app_support; - u8 chip_id; + uint32_t chip_id; char reserved[120]; } __packed; From 8f504dc84eda57146a15d9f23ec2322171b866d1 Mon Sep 17 00:00:00 2001 From: Asha Talambedu Date: Thu, 13 May 2021 16:02:50 +0530 Subject: [PATCH 101/138] nvadsp: Write chip id to h/w mailbox1 Need to communicate chip id info so that ast settings can be enabled only for t186/t194 from ADSP. Hence, HWMBOX1 is used to communicate the same Bug 200684491 Change-Id: If7ae5f58fc410fd7cbb19c9e0129bead8740e88b Signed-off-by: Asha Talambedu Reviewed-on: https://git-master.nvidia.com/r/c/linux-nvidia/+/2529141 Reviewed-by: svcacv Reviewed-by: Sameer Pujar Reviewed-by: svc_kernel_abi Reviewed-by: Sharad Gupta Reviewed-by: Viswanath L Reviewed-by: mobile promotions Tested-by: mobile promotions GVS: Gerrit_Virtual_Submit --- drivers/platform/tegra/nvadsp/os.c | 25 ++++++++++++++++++++++++- 1 file changed, 24 insertions(+), 1 deletion(-) diff --git a/drivers/platform/tegra/nvadsp/os.c b/drivers/platform/tegra/nvadsp/os.c index d6eb7e85..8aee7110 100644 --- a/drivers/platform/tegra/nvadsp/os.c +++ b/drivers/platform/tegra/nvadsp/os.c @@ -5,7 +5,7 @@ * Copyright (C) 2011 Texas Instruments, Inc. * Copyright (C) 2011 Google, Inc. * - * Copyright (C) 2014-2020, NVIDIA Corporation. All rights reserved. + * Copyright (C) 2014-2021, 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 @@ -765,6 +765,9 @@ static void nvadsp_set_shared_mem(struct platform_device *pdev, chip_id = tegra_get_chip_id(); os_args = &shared_mem->os_args; + /* Chip id info is communicated twice to ADSP + * TODO::clean up the redundant comm. + */ os_args->chip_id = chip_id; drv_data->shared_adsp_os_data = shared_mem; @@ -1683,6 +1686,7 @@ int nvadsp_os_start(void) struct device *dev; int ret = 0; static int cold_start = 1; + u8 chip_id; if (!priv.pdev) { pr_err("ADSP Driver is not initialized\n"); @@ -1721,6 +1725,25 @@ int nvadsp_os_start(void) cold_start = 0; } + if (drv_data->chip_data->hwmb.hwmbox1_reg != 0) { + chip_id = tegra_get_chip_id(); + /* Write chip id info to HWMBOX1 to enable ast config + * later for t186/t196 + */ + if (chip_id != 0) { + hwmbox_writel((uint32_t)chip_id, + drv_data->chip_data->hwmb.hwmbox1_reg); + } else { + dev_err(dev, "chip id is NULL\n"); + ret = -EINVAL; + free_interrupts(&priv); +#ifdef CONFIG_PM + pm_runtime_put_sync(&priv.pdev->dev); +#endif + goto unlock; + } + } + ret = __nvadsp_os_start(); if (ret) { priv.os_running = drv_data->adsp_os_running = false; From fc0faf10ad011da518042ec8691bf37172407df8 Mon Sep 17 00:00:00 2001 From: Viswanath L Date: Wed, 14 Jul 2021 23:09:07 +0530 Subject: [PATCH 102/138] nvadsp: Set alignment for shared structs Shared structures nvadsp_app_shared_msg_pool and nvadsp_shared_mem are aligned using #pragma pack(8) in order to match the alignment on ADSP side. Bug 200729844 Change-Id: I541a8a26894e9c5e78f56fa687ca59b905342f23 Signed-off-by: Viswanath L Reviewed-on: https://git-master.nvidia.com/r/c/linux-nvidia/+/2559105 Reviewed-by: svcacv Reviewed-by: svc_kernel_abi Reviewed-by: Niranjan Dighe Reviewed-by: Sharad Gupta Reviewed-by: mobile promotions Tested-by: mobile promotions GVS: Gerrit_Virtual_Submit --- drivers/platform/tegra/nvadsp/adsp_shared_struct.h | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/drivers/platform/tegra/nvadsp/adsp_shared_struct.h b/drivers/platform/tegra/nvadsp/adsp_shared_struct.h index 7a08b8c0..629e3b8d 100644 --- a/drivers/platform/tegra/nvadsp/adsp_shared_struct.h +++ b/drivers/platform/tegra/nvadsp/adsp_shared_struct.h @@ -117,10 +117,12 @@ union app_loader_msgq { }; /* ADSP APP shared message pool */ +#pragma pack(8) struct nvadsp_app_shared_msg_pool { union app_loader_msgq app_loader_send_message; union app_loader_msgq app_loader_recv_message; -} __packed; +}; +#pragma pack() /*ADSP shated OS args */ struct nvadsp_os_args { @@ -168,12 +170,14 @@ struct nvadsp_os_info { } __packed; /* ADSP OS shared memory */ +#pragma pack(8) struct nvadsp_shared_mem { struct nvadsp_app_shared_msg_pool app_shared_msg_pool; struct nvadsp_os_args os_args; struct nvadsp_os_info os_info; struct nvadsp_exception_context exception_context; -} __packed; +}; +#pragma pack() #endif /* __ADSP_SHARED_STRUCT */ From 7aae76754e37d057794620353e6022b0288aeb99 Mon Sep 17 00:00:00 2001 From: Sahil Mukund Patki Date: Tue, 3 Aug 2021 12:11:31 +0000 Subject: [PATCH 103/138] platform: tegra: nvadsp: Fix debugfs compilation error Fix compilation errors in nvadsp driver seen when disabling CONFIG_DEBUG_FS Bug 200755555 Change-Id: Id749b2af4aeb199a58997ea788273b9af31c1682 Signed-off-by: Sahil Mukund Patki Reviewed-on: https://git-master.nvidia.com/r/c/linux-nvidia/+/2569775 Reviewed-by: svc_kernel_abi Reviewed-by: Nikesh Oswal Reviewed-by: Bharat Nihalani Reviewed-by: mobile promotions Tested-by: mobile promotions GVS: Gerrit_Virtual_Submit --- drivers/platform/tegra/nvadsp/adspff.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/drivers/platform/tegra/nvadsp/adspff.c b/drivers/platform/tegra/nvadsp/adspff.c index 79eaccd5..487f0e34 100644 --- a/drivers/platform/tegra/nvadsp/adspff.c +++ b/drivers/platform/tegra/nvadsp/adspff.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016-2020, NVIDIA CORPORATION. All rights reserved. + * Copyright (c) 2016-2021, NVIDIA CORPORATION. All rights reserved. * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU General Public License, @@ -619,6 +619,7 @@ static int adspff_set(void *data, u64 val) } DEFINE_SIMPLE_ATTRIBUTE(adspff_fops, NULL, adspff_set, "%llu\n"); +#ifdef CONFIG_DEBUG_FS static int adspff_debugfs_init(struct nvadsp_drv_data *drv) { int ret = -ENOMEM; @@ -638,13 +639,17 @@ static int adspff_debugfs_init(struct nvadsp_drv_data *drv) return 0; } +#endif int adspff_init(struct platform_device *pdev) { int ret = 0; nvadsp_app_handle_t handle; nvadsp_app_info_t *app_info; + +#ifdef CONFIG_DEBUG_FS struct nvadsp_drv_data *drv = platform_get_drvdata(pdev); +#endif handle = nvadsp_app_load("adspff", "adspff.elf"); if (!handle) @@ -668,9 +673,11 @@ int adspff_init(struct platform_device *pdev) spin_lock_init(&adspff_lock); +#ifdef CONFIG_DEBUG_FS ret = adspff_debugfs_init(drv); if (ret) pr_warn("adspff: failed to create debugfs entry\n"); +#endif INIT_LIST_HEAD(&adspff_kthread_msgq_head); INIT_LIST_HEAD(&file_list); From 8bbf49a3861670aaf71ab97e6f1fe99117aec666 Mon Sep 17 00:00:00 2001 From: Viswanath L Date: Tue, 19 Oct 2021 06:52:33 +0000 Subject: [PATCH 104/138] nvadsp: Move struct element for alignment Simulator platforms do not support unaligned access, so element adsp_freq_hz in struct nvadsp_os_args is reorganized to make it aligned. Bug 200745795 Bug 200746669 Change-Id: I053756a52b8baf3ec04cdbc9eb82878fd6531a62 Signed-off-by: Viswanath L Reviewed-on: https://git-master.nvidia.com/r/c/linux-nvidia/+/2612812 Reviewed-by: svcacv Reviewed-by: svc_kernel_abi Reviewed-by: Sharad Gupta Reviewed-by: mobile promotions GVS: Gerrit_Virtual_Submit Tested-by: mobile promotions --- drivers/platform/tegra/nvadsp/adsp_shared_struct.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/platform/tegra/nvadsp/adsp_shared_struct.h b/drivers/platform/tegra/nvadsp/adsp_shared_struct.h index 629e3b8d..099173e1 100644 --- a/drivers/platform/tegra/nvadsp/adsp_shared_struct.h +++ b/drivers/platform/tegra/nvadsp/adsp_shared_struct.h @@ -126,9 +126,9 @@ struct nvadsp_app_shared_msg_pool { /*ADSP shated OS args */ struct nvadsp_os_args { + uint64_t adsp_freq_hz; int32_t timer_prescalar; char logger[DRAM_DEBUG_LOG_SIZE]; - uint64_t adsp_freq_hz; uint32_t dynamic_app_support; uint32_t chip_id; char reserved[120]; From 6360f074cf3d8a55b46094b2c2faa2bae7042c08 Mon Sep 17 00:00:00 2001 From: Viswanath L Date: Wed, 20 Oct 2021 07:53:46 +0000 Subject: [PATCH 105/138] nvadsp: Add backdoor boot via CO memory Support is added for backdoor boot via CO memory (i.e. no SMMU). To use this mode, 'nvidia,adsp_os_secload' property should be removed and reserved-memory:adspos_carveout node enabled. ACAST mapping in this mode should span the exact CO reserved region. Bug 200745795 Bug 200746669 Change-Id: I6bbb84d3278db8f69e60933d63a900c2797fc425 Signed-off-by: Viswanath L Reviewed-on: https://git-master.nvidia.com/r/c/linux-nvidia/+/2613521 Reviewed-by: svcacv Reviewed-by: svc_kernel_abi Reviewed-by: Sharad Gupta Reviewed-by: mobile promotions GVS: Gerrit_Virtual_Submit Tested-by: mobile promotions --- drivers/platform/tegra/nvadsp/dev.c | 31 +++++++++++++++++++++++++++ drivers/platform/tegra/nvadsp/dev.h | 3 +++ drivers/platform/tegra/nvadsp/os.c | 33 ++++++++++++++++++++++++++--- 3 files changed, 64 insertions(+), 3 deletions(-) diff --git a/drivers/platform/tegra/nvadsp/dev.c b/drivers/platform/tegra/nvadsp/dev.c index 193a7176..5e8c07c4 100644 --- a/drivers/platform/tegra/nvadsp/dev.c +++ b/drivers/platform/tegra/nvadsp/dev.c @@ -205,6 +205,34 @@ static void nvadsp_bw_unregister(struct nvadsp_drv_data *drv_data) #endif } +static int __init nvadsp_parse_co_mem(struct platform_device *pdev) +{ + struct nvadsp_drv_data *drv_data = platform_get_drvdata(pdev); + struct device *dev = &pdev->dev; + struct device_node *node; + int err = 0; + + node = of_parse_phandle(dev->of_node, "nvidia,adsp_co", 0); + if (!node) + return 0; + + if (!of_device_is_available(node)) + goto exit; + + err = of_address_to_resource(node, 0, &drv_data->co_mem); + if (err) { + dev_err(dev, "cannot get adsp CO memory (%d)\n", err); + goto exit; + } + + drv_data->adsp_mem[ADSP_OS_SIZE] = resource_size(&drv_data->co_mem); + +exit: + of_node_put(node); + + return err; +} + static void __init nvadsp_parse_clk_entries(struct platform_device *pdev) { struct nvadsp_drv_data *drv_data = platform_get_drvdata(pdev); @@ -278,6 +306,9 @@ static int __init nvadsp_parse_dt(struct platform_device *pdev) } nvadsp_parse_clk_entries(pdev); + if (nvadsp_parse_co_mem(pdev)) + return -ENOMEM; + drv_data->state.evp = devm_kzalloc(dev, drv_data->evp_base[ADSP_EVP_SIZE], GFP_KERNEL); if (!drv_data->state.evp) diff --git a/drivers/platform/tegra/nvadsp/dev.h b/drivers/platform/tegra/nvadsp/dev.h index f5a07029..a4426643 100644 --- a/drivers/platform/tegra/nvadsp/dev.h +++ b/drivers/platform/tegra/nvadsp/dev.h @@ -234,6 +234,9 @@ struct nvadsp_drv_data { u32 evp_base[ADSP_EVP_END]; const struct nvadsp_chipdata *chip_data; + + /* CO mem in backdoor boot */ + struct resource co_mem; }; #define ADSP_CONFIG 0x04 diff --git a/drivers/platform/tegra/nvadsp/os.c b/drivers/platform/tegra/nvadsp/os.c index 8aee7110..b803a2ff 100644 --- a/drivers/platform/tegra/nvadsp/os.c +++ b/drivers/platform/tegra/nvadsp/os.c @@ -706,7 +706,9 @@ fail_dma_alloc: static int allocate_memory_for_adsp_os(void) { struct platform_device *pdev = priv.pdev; + struct nvadsp_drv_data *drv_data = platform_get_drvdata(pdev); struct device *dev = &pdev->dev; + struct resource *co_mem = &drv_data->co_mem; #if defined(CONFIG_TEGRA_NVADSP_ON_SMMU) dma_addr_t addr; #else @@ -718,6 +720,19 @@ static int allocate_memory_for_adsp_os(void) addr = priv.adsp_os_addr; size = priv.adsp_os_size; + + if (co_mem->start) { + dram_va = devm_memremap(dev, co_mem->start, + size, MEMREMAP_WT); + if (IS_ERR(dram_va)) { + dev_err(dev, "unable to map CO mem: %pR\n", co_mem); + ret = -ENOMEM; + goto end; + } + dev_info(dev, "Mapped CO mem: %pR\n", co_mem); + goto map_and_end; + } + #if defined(CONFIG_TEGRA_NVADSP_ON_SMMU) dram_va = nvadsp_dma_alloc_and_map_at(pdev, size, addr, GFP_KERNEL); if (!dram_va) { @@ -733,16 +748,28 @@ static int allocate_memory_for_adsp_os(void) goto end; } #endif + +map_and_end: nvadsp_add_load_mappings(addr, dram_va, size); end: return ret; } -static void deallocate_memory_for_adsp_os(struct device *dev) +static void deallocate_memory_for_adsp_os(void) { -#if defined(CONFIG_TEGRA_NVADSP_ON_SMMU) + struct platform_device *pdev = priv.pdev; + struct nvadsp_drv_data *drv_data = platform_get_drvdata(pdev); + struct device *dev = &pdev->dev; + void *va = nvadsp_da_to_va_mappings(priv.adsp_os_addr, priv.adsp_os_size); + + if (drv_data->co_mem.start) { + devm_memunmap(dev, va); + return; + } + +#if defined(CONFIG_TEGRA_NVADSP_ON_SMMU) dma_free_coherent(dev, priv.adsp_os_size, va, priv.adsp_os_addr); #endif } @@ -849,7 +876,7 @@ static int nvadsp_firmware_load(struct platform_device *pdev) return 0; deallocate_os_memory: - deallocate_memory_for_adsp_os(dev); + deallocate_memory_for_adsp_os(); release_firmware: release_firmware(fw); end: From bb93b07676cff8ae8d6467b471d728f1db51053e Mon Sep 17 00:00:00 2001 From: Viswanath L Date: Wed, 20 Oct 2021 09:03:44 +0000 Subject: [PATCH 106/138] nvadsp: Pass shared mem via MBX in backdoor boot In backdoor boot if shared memory region is not defined in the ADSP FW then assume it to be placed at the start of OS memory and communicate the same via MBX. In case MBX is not available then the boot is aborted. Bug 200745795 Bug 200746669 Change-Id: I15cd4f3f07dd91c2700479906d19b9219814e2f6 Signed-off-by: Viswanath L Reviewed-on: https://git-master.nvidia.com/r/c/linux-nvidia/+/2613565 Reviewed-by: svcacv Reviewed-by: svc_kernel_abi Reviewed-by: Sharad Gupta Reviewed-by: mobile promotions GVS: Gerrit_Virtual_Submit Tested-by: mobile promotions --- drivers/platform/tegra/nvadsp/os.c | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/drivers/platform/tegra/nvadsp/os.c b/drivers/platform/tegra/nvadsp/os.c index b803a2ff..913a0b3d 100644 --- a/drivers/platform/tegra/nvadsp/os.c +++ b/drivers/platform/tegra/nvadsp/os.c @@ -526,7 +526,7 @@ static void *get_mailbox_shared_region(const struct firmware *fw) shdr = nvadsp_get_section(fw, MAILBOX_REGION); if (!shdr) { - dev_err(dev, "section %s not found\n", MAILBOX_REGION); + dev_dbg(dev, "section %s not found\n", MAILBOX_REGION); return ERR_PTR(-EINVAL); } @@ -832,6 +832,7 @@ static int __nvadsp_os_secload(struct platform_device *pdev) static int nvadsp_firmware_load(struct platform_device *pdev) { struct nvadsp_shared_mem *shared_mem; + struct nvadsp_drv_data *drv_data = platform_get_drvdata(pdev); struct device *dev = &pdev->dev; const struct firmware *fw; int ret = 0; @@ -864,6 +865,21 @@ static int nvadsp_firmware_load(struct platform_device *pdev) } shared_mem = get_mailbox_shared_region(fw); + if (IS_ERR(shared_mem)) { + if (drv_data->chip_data->adsp_shared_mem_hwmbox != 0) { + /* + * If FW is not explicitly defining a shared memory + * region then assume it to be placed at the start + * of OS memory and communicate the same via MBX + */ + drv_data->shared_adsp_os_data_iova = priv.adsp_os_addr; + shared_mem = nvadsp_da_to_va_mappings( + priv.adsp_os_addr, priv.adsp_os_size); + } else { + dev_err(dev, "failed to locate shared memory\n"); + goto deallocate_os_memory; + } + } nvadsp_set_shared_mem(pdev, shared_mem, 1); ret = dram_app_mem_init(priv.app_alloc_addr, priv.app_size); From f33770d313664e311e09c253318a574ac7a9f9e5 Mon Sep 17 00:00:00 2001 From: Asha Talambedu Date: Tue, 16 Nov 2021 16:42:51 +0530 Subject: [PATCH 107/138] tegra: nvadsp: Timeout added on wait for app start In cold boot stress test, it is observed that adsp OS hung intermittently(1/276). Due to which 1) app init of adspff is not able to complete as it does not receive start ack from adsp 2) wdt handler is triggered as adsp is hung for more than 10 seconds. When poweroff is issued in such case, the alsactl save/restore job which is part of shutdown sequence is hung as its unable to acquire lock held by host adsp audio driver fn that initiated nvadsp_os_start. Therefore, timeout is added while waiting for app init. If app init fails to happen within timeout duration, error status is received by nvadsp_os_start which inturn is received by host side adsp audio driver. This ensures that lock is released in case of adsff_init failure in nvadsp_os_start function so that shutdown can proceed without issue. Bug 3391964 Change-Id: Ieca54fe9dd21bf9de70d781cfaceb5ffe83809ef Signed-off-by: Asha Talambedu Reviewed-on: https://git-master.nvidia.com/r/c/linux-nvidia/+/2626892 Tested-by: mobile promotions Reviewed-by: svc_kernel_abi Reviewed-by: svcacv Reviewed-by: Dara Ramesh Reviewed-by: Sharad Gupta Reviewed-by: mobile promotions GVS: Gerrit_Virtual_Submit --- drivers/platform/tegra/nvadsp/app.c | 12 +++++++++--- drivers/platform/tegra/nvadsp/os.c | 13 ++++++++++++- 2 files changed, 21 insertions(+), 4 deletions(-) diff --git a/drivers/platform/tegra/nvadsp/app.c b/drivers/platform/tegra/nvadsp/app.c index 6fafdb80..12634b76 100644 --- a/drivers/platform/tegra/nvadsp/app.c +++ b/drivers/platform/tegra/nvadsp/app.c @@ -3,7 +3,7 @@ * * ADSP OS App management * - * Copyright (C) 2014-2018, NVIDIA Corporation. All rights reserved. + * Copyright (C) 2014-2021, 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 @@ -32,6 +32,7 @@ #include "adsp_shared_struct.h" #define DYN_APP_EXTN ".elf" +#define ADSP_APP_INIT_TIMEOUT 2000 /* in ms */ /* * structure to hold the list of app binaries loaded and @@ -616,7 +617,7 @@ nvadsp_app_info_t __must_check *nvadsp_app_init(nvadsp_app_handle_t handle, nvadsp_app_info_t *app; msgq_t *msgq_send; int *state; - unsigned long flags; + unsigned long flags, ret = 0; if (IS_ERR_OR_NULL(priv.pdev)) { pr_err("ADSP Driver is not initialized\n"); @@ -661,7 +662,12 @@ nvadsp_app_info_t __must_check *nvadsp_app_init(nvadsp_app_handle_t handle, nvadsp_mbox_send(&priv.mbox, 0, NVADSP_MBOX_SMSG, false, 0); - wait_for_completion(&app->wait_for_app_start); + ret = wait_for_completion_timeout(&app->wait_for_app_start, + msecs_to_jiffies(ADSP_APP_INIT_TIMEOUT)); + if (!ret) { + delete_app_instance(app); + return NULL; + } init_completion(&app->wait_for_app_start); return app; err: diff --git a/drivers/platform/tegra/nvadsp/os.c b/drivers/platform/tegra/nvadsp/os.c index 913a0b3d..ab465b8d 100644 --- a/drivers/platform/tegra/nvadsp/os.c +++ b/drivers/platform/tegra/nvadsp/os.c @@ -1805,7 +1805,18 @@ int nvadsp_os_start(void) #if defined(CONFIG_TEGRA_ADSP_FILEIO) if (!drv_data->adspff_init) { ret = adspff_init(priv.pdev); - if (!ret) + if (ret) { + priv.os_running = drv_data->adsp_os_running = false; + dev_err(dev, + "adsp boot failed at adspff init with ret = %d", + ret); + dump_adsp_sys(); + free_interrupts(&priv); +#ifdef CONFIG_PM + pm_runtime_put_sync(&priv.pdev->dev); +#endif + goto unlock; + } else drv_data->adspff_init = true; } #endif From 3c9d6d8db127caa923dc099597989eb778b40cca Mon Sep 17 00:00:00 2001 From: Viswanath L Date: Thu, 18 Nov 2021 07:54:37 +0000 Subject: [PATCH 108/138] nvadsp: Add support for APE_TKE reset Add support for APE_TKE assert/deassert alongside ADSP assert/deassert. This reset may be specified in the DT with string "ape_tke". Bug 200736921 Bug 200773359 Change-Id: I3331ec1572057661ea62dc5269288575a0f0364b Signed-off-by: Viswanath L Reviewed-on: https://git-master.nvidia.com/r/c/linux-nvidia/+/2627992 Tested-by: mobile promotions Reviewed-by: Dara Ramesh Reviewed-by: Sharad Gupta Reviewed-by: svc_kernel_abi Reviewed-by: mobile promotions GVS: Gerrit_Virtual_Submit --- drivers/platform/tegra/nvadsp/dev-t18x.c | 31 ++++++++++++++++++++++-- drivers/platform/tegra/nvadsp/dev.h | 1 + 2 files changed, 30 insertions(+), 2 deletions(-) diff --git a/drivers/platform/tegra/nvadsp/dev-t18x.c b/drivers/platform/tegra/nvadsp/dev-t18x.c index c3bd9318..e6e40b4a 100644 --- a/drivers/platform/tegra/nvadsp/dev-t18x.c +++ b/drivers/platform/tegra/nvadsp/dev-t18x.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015-2020, NVIDIA Corporation. All rights reserved. + * Copyright (c) 2015-2021, 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 @@ -183,9 +183,19 @@ static int __assert_t18x_adsp(struct nvadsp_drv_data *d) * only ADSP reset is sufficient to reset all ADSP sub-modules. */ ret = reset_control_assert(d->adspall_rst); - if (ret) + if (ret) { dev_err(dev, "failed to assert adsp\n"); + goto end; + } + /* APE_TKE reset */ + if (d->ape_tke_rst) { + ret = reset_control_assert(d->ape_tke_rst); + if (ret) + dev_err(dev, "failed to assert ape_tke\n"); + } + +end: return ret; } @@ -195,6 +205,15 @@ static int __deassert_t18x_adsp(struct nvadsp_drv_data *d) struct device *dev = &pdev->dev; int ret = 0; + /* APE_TKE reset */ + if (d->ape_tke_rst) { + ret = reset_control_deassert(d->ape_tke_rst); + if (ret) { + dev_err(dev, "failed to deassert ape_tke\n"); + goto end; + } + } + /* * The ADSP_ALL reset in BPMP-FW is overloaded to de-assert * all 7 resets i.e. ADSP, ADSPINTF, ADSPDBG, ADSPNEON, ADSPPERIPH, @@ -207,6 +226,7 @@ static int __deassert_t18x_adsp(struct nvadsp_drv_data *d) if (ret) dev_err(dev, "failed to deassert adsp\n"); +end: return ret; } @@ -284,6 +304,13 @@ int nvadsp_reset_t18x_init(struct platform_device *pdev) if (IS_ERR(d->adspall_rst)) { dev_err(dev, "can not get adspall reset\n"); ret = PTR_ERR(d->adspall_rst); + goto end; } + + d->ape_tke_rst = devm_reset_control_get(dev, "ape_tke"); + if (IS_ERR(d->ape_tke_rst)) + d->ape_tke_rst = NULL; + +end: return ret; } diff --git a/drivers/platform/tegra/nvadsp/dev.h b/drivers/platform/tegra/nvadsp/dev.h index a4426643..ab0e1b67 100644 --- a/drivers/platform/tegra/nvadsp/dev.h +++ b/drivers/platform/tegra/nvadsp/dev.h @@ -190,6 +190,7 @@ struct nvadsp_drv_data { int (*assert_adsp)(struct nvadsp_drv_data *drv_data); int (*deassert_adsp)(struct nvadsp_drv_data *drv_data); struct reset_control *adspall_rst; + struct reset_control *ape_tke_rst; struct nvadsp_pm_state state; bool adsp_os_running; From 8036a9525f65908082f5e112b71a5c1a746fd16c Mon Sep 17 00:00:00 2001 From: Viswanath L Date: Mon, 8 Nov 2021 06:52:38 +0000 Subject: [PATCH 109/138] nvadsp: Pass platform info to ADSP Provision is made to pass the Tegra platform information to ADSP ("nvidia,tegra_platform" DT property, as per enum tegra_platform); this will be useful to convey whether the platform is CMODEL, in which case the ADSP may take CMODEL specific paths, like using HSP shared interrupts. Upper 16 bits of nvadsp_os_args.chip_id is used to convey the platform information; this may be improved in future as a separate structure member. Bug 200745795 Bug 200746669 Change-Id: Ibd9423eebb06fc4d243c132ab1cb7c6dc558ba5e Signed-off-by: Viswanath L Reviewed-on: https://git-master.nvidia.com/r/c/linux-nvidia/+/2622156 Reviewed-by: svcacv Reviewed-by: svc_kernel_abi Reviewed-by: Mohan Kumar D Reviewed-by: Asha Talambedu Reviewed-by: Sharad Gupta Reviewed-by: mobile promotions GVS: Gerrit_Virtual_Submit Tested-by: mobile promotions --- drivers/platform/tegra/nvadsp/dev.c | 3 +++ drivers/platform/tegra/nvadsp/dev.h | 3 +++ drivers/platform/tegra/nvadsp/os.c | 7 +++++++ 3 files changed, 13 insertions(+) diff --git a/drivers/platform/tegra/nvadsp/dev.c b/drivers/platform/tegra/nvadsp/dev.c index 5e8c07c4..a9f505b0 100644 --- a/drivers/platform/tegra/nvadsp/dev.c +++ b/drivers/platform/tegra/nvadsp/dev.c @@ -293,6 +293,9 @@ static int __init nvadsp_parse_dt(struct platform_device *pdev) drv_data->adsp_os_secload = of_property_read_bool(dev->of_node, "nvidia,adsp_os_secload"); + of_property_read_u32(dev->of_node, "nvidia,tegra_platform", + &drv_data->tegra_platform); + 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, diff --git a/drivers/platform/tegra/nvadsp/dev.h b/drivers/platform/tegra/nvadsp/dev.h index ab0e1b67..f05cd10d 100644 --- a/drivers/platform/tegra/nvadsp/dev.h +++ b/drivers/platform/tegra/nvadsp/dev.h @@ -238,6 +238,9 @@ struct nvadsp_drv_data { /* CO mem in backdoor boot */ struct resource co_mem; + + /* enum tegra_platform */ + u32 tegra_platform; }; #define ADSP_CONFIG 0x04 diff --git a/drivers/platform/tegra/nvadsp/os.c b/drivers/platform/tegra/nvadsp/os.c index ab465b8d..da1356ba 100644 --- a/drivers/platform/tegra/nvadsp/os.c +++ b/drivers/platform/tegra/nvadsp/os.c @@ -797,6 +797,13 @@ static void nvadsp_set_shared_mem(struct platform_device *pdev, */ os_args->chip_id = chip_id; + /* + * Tegra platform is encoded in the upper 16 bits + * of chip_id; can be improved to make this a + * separate member in nvadsp_os_args + */ + os_args->chip_id |= (drv_data->tegra_platform << 16); + drv_data->shared_adsp_os_data = shared_mem; } From ac60258ffda579918e40e2d5c3aba363c690a41a Mon Sep 17 00:00:00 2001 From: Viswanath L Date: Mon, 8 Nov 2021 16:35:16 +0000 Subject: [PATCH 110/138] nvadsp: Add provision to configure timeout Provision is added to specify the OS load timeout from DT, using property "nvidia,adsp_load_timeout". Bug 200745795 Bug 200746669 Change-Id: Ie868a1528ad9d39200a0359f6a9d54a2832b0547 Signed-off-by: Viswanath L Reviewed-on: https://git-master.nvidia.com/r/c/linux-nvidia/+/2622380 Reviewed-by: svcacv Reviewed-by: svc_kernel_abi Reviewed-by: Mohan Kumar D Reviewed-by: Sharad Gupta Reviewed-by: mobile promotions GVS: Gerrit_Virtual_Submit Tested-by: mobile promotions --- drivers/platform/tegra/nvadsp/dev.c | 3 +++ drivers/platform/tegra/nvadsp/dev.h | 3 +++ drivers/platform/tegra/nvadsp/os.c | 9 +++++++-- 3 files changed, 13 insertions(+), 2 deletions(-) diff --git a/drivers/platform/tegra/nvadsp/dev.c b/drivers/platform/tegra/nvadsp/dev.c index a9f505b0..cd3a5ed9 100644 --- a/drivers/platform/tegra/nvadsp/dev.c +++ b/drivers/platform/tegra/nvadsp/dev.c @@ -296,6 +296,9 @@ static int __init nvadsp_parse_dt(struct platform_device *pdev) of_property_read_u32(dev->of_node, "nvidia,tegra_platform", &drv_data->tegra_platform); + of_property_read_u32(dev->of_node, "nvidia,adsp_load_timeout", + &drv_data->adsp_load_timeout); + 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, diff --git a/drivers/platform/tegra/nvadsp/dev.h b/drivers/platform/tegra/nvadsp/dev.h index f05cd10d..de77c9f9 100644 --- a/drivers/platform/tegra/nvadsp/dev.h +++ b/drivers/platform/tegra/nvadsp/dev.h @@ -241,6 +241,9 @@ struct nvadsp_drv_data { /* enum tegra_platform */ u32 tegra_platform; + + /* "nvidia,adsp_load_timeout" (in ms) */ + u32 adsp_load_timeout; }; #define ADSP_CONFIG 0x04 diff --git a/drivers/platform/tegra/nvadsp/os.c b/drivers/platform/tegra/nvadsp/os.c index da1356ba..122e56cd 100644 --- a/drivers/platform/tegra/nvadsp/os.c +++ b/drivers/platform/tegra/nvadsp/os.c @@ -1221,11 +1221,16 @@ end: static int wait_for_adsp_os_load_complete(void) { struct device *dev = &priv.pdev->dev; - uint32_t data; + struct nvadsp_drv_data *drv_data = platform_get_drvdata(priv.pdev); + uint32_t timeout, data; status_t ret; + timeout = drv_data->adsp_load_timeout; + if (!timeout) + timeout = ADSP_OS_LOAD_TIMEOUT; + ret = nvadsp_mbox_recv(&adsp_com_mbox, &data, - true, ADSP_OS_LOAD_TIMEOUT); + true, timeout); if (ret) { dev_err(dev, "ADSP OS loading timed out\n"); goto end; From cadec4698683cf5122909bcff97e50c1cc492f38 Mon Sep 17 00:00:00 2001 From: Viswanath L Date: Thu, 9 Dec 2021 22:58:33 +0530 Subject: [PATCH 111/138] nvadsp: AMC error WAR under chip flag WAR for spurious AMC error when accessing address < 0x1000 is placed under a flag in chip data. Bug 200747371 Bug 200773359 Change-Id: I6299fe754efbf2f4d6a99252f71ed3140bade1ba Signed-off-by: Viswanath L Reviewed-on: https://git-master.nvidia.com/r/c/linux-nvidia/+/2639578 Reviewed-by: svcacv Reviewed-by: svc_kernel_abi Reviewed-by: Sharad Gupta Reviewed-by: mobile promotions GVS: Gerrit_Virtual_Submit Tested-by: mobile promotions --- drivers/platform/tegra/nvadsp/amc.c | 5 +++-- drivers/platform/tegra/nvadsp/dev.c | 4 ++++ drivers/platform/tegra/nvadsp/dev.h | 2 ++ 3 files changed, 9 insertions(+), 2 deletions(-) diff --git a/drivers/platform/tegra/nvadsp/amc.c b/drivers/platform/tegra/nvadsp/amc.c index bbc1a5ab..64db3d0c 100644 --- a/drivers/platform/tegra/nvadsp/amc.c +++ b/drivers/platform/tegra/nvadsp/amc.c @@ -3,7 +3,7 @@ * * AMC and ARAM handling * - * Copyright (C) 2014-2020, NVIDIA Corporation. All rights reserved. + * Copyright (C) 2014-2021, 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 @@ -148,7 +148,8 @@ static irqreturn_t nvadsp_amc_error_int_handler(int irq, void *devid) * Ignore addresses lesser than AMC_ERROR_ADDR_IGNORE (4k) * as those are spurious ones due a hardware issue. */ - if (addr > AMC_ERROR_ADDR_IGNORE) + if (!(nvadsp_drv_data->chip_data->amc_err_war) || + (addr > AMC_ERROR_ADDR_IGNORE)) pr_info("nvadsp: invalid ARAM access. address: 0x%x\n", addr); diff --git a/drivers/platform/tegra/nvadsp/dev.c b/drivers/platform/tegra/nvadsp/dev.c index cd3a5ed9..3d050b2d 100644 --- a/drivers/platform/tegra/nvadsp/dev.c +++ b/drivers/platform/tegra/nvadsp/dev.c @@ -536,6 +536,8 @@ static struct nvadsp_chipdata tegra210_adsp_chipdata = { .wdt_irq = INT_T210_ADSP_WDT, .start_irq = INT_T210_AGIC_START, .end_irq = INT_T210_AGIC_END, + + .amc_err_war = true, }; static struct nvadsp_chipdata tegrat18x_adsp_chipdata = { @@ -562,6 +564,8 @@ static struct nvadsp_chipdata tegrat18x_adsp_chipdata = { .wdt_irq = INT_T18x_ATKE_WDT_IRQ, .start_irq = INT_T18x_AGIC_START, .end_irq = INT_T18x_AGIC_END, + + .amc_err_war = true, }; static const struct of_device_id nvadsp_of_match[] = { diff --git a/drivers/platform/tegra/nvadsp/dev.h b/drivers/platform/tegra/nvadsp/dev.h index de77c9f9..1e91746c 100644 --- a/drivers/platform/tegra/nvadsp/dev.h +++ b/drivers/platform/tegra/nvadsp/dev.h @@ -155,6 +155,8 @@ struct nvadsp_chipdata { int wdt_irq; int start_irq; int end_irq; + + bool amc_err_war; }; struct nvadsp_drv_data { From f5feadcb431a5bfc56a89432c539b0524d3a76c3 Mon Sep 17 00:00:00 2001 From: Viswanath L Date: Tue, 21 Dec 2021 11:41:11 +0530 Subject: [PATCH 112/138] nvadsp: ACAST settings from DT ACAST settings are expected to be done by the driver in backdoor boot. Provision is added to read the ACAST address ranges and the SMMU stream ID from DT. ACAST settings are done either for physical memory, or for SMMU mapped memory, as per the backdoor boot configuration. Also, call to ACAST init function is moved from runtime resume to device probe. Bug 200745795 Bug 200746669 Bug 200773359 Change-Id: Ia53820968ce4d48966c39d72b4c2106be99815fa Signed-off-by: Viswanath L Reviewed-on: https://git-master.nvidia.com/r/c/linux-nvidia/+/2644989 Reviewed-by: svc_kernel_abi Reviewed-by: Mohan Kumar D Reviewed-by: Sharad Gupta Reviewed-by: mobile promotions GVS: Gerrit_Virtual_Submit Tested-by: mobile promotions --- drivers/platform/tegra/nvadsp/acast.c | 166 +++++++++++++++++------ drivers/platform/tegra/nvadsp/dev-t18x.c | 9 -- drivers/platform/tegra/nvadsp/dev-t18x.h | 4 +- drivers/platform/tegra/nvadsp/dev.c | 8 ++ drivers/platform/tegra/nvadsp/dev.h | 12 ++ 5 files changed, 150 insertions(+), 49 deletions(-) diff --git a/drivers/platform/tegra/nvadsp/acast.c b/drivers/platform/tegra/nvadsp/acast.c index 0fab3403..061cacfa 100644 --- a/drivers/platform/tegra/nvadsp/acast.c +++ b/drivers/platform/tegra/nvadsp/acast.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2016-2017 NVIDIA CORPORATION. All rights reserved. + * Copyright (C) 2016-2021 NVIDIA CORPORATION. All rights reserved. * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU General Public License, @@ -15,7 +15,9 @@ */ #include +#include #include +#include "dev.h" #include "dev-t18x.h" #define AST_CONTROL 0x000 @@ -35,44 +37,50 @@ #define AST_PHY_SID_IDX 0 #define AST_APE_SID_IDX 1 #define AST_NS (1 << 3) +#define AST_CARVEOUTID(ID) (ID << 5) #define AST_VMINDEX(IDX) (IDX << 15) +#define AST_PHSICAL(PHY) (PHY << 19) +#define AST_STREAMID(ID) (ID << 8) +#define AST_VMINDEX_ENABLE (1 << 0) #define AST_RGN_ENABLE (1 << 0) #define AST_RGN_OFFSET 0x20 struct acast_region { - u64 rgn; - u64 rgn_ctrl; + u32 rgn; + u32 rgn_ctrl; + u32 strmid_reg; + u32 strmid_ctrl; u64 slave; u64 size; u64 master; }; -#define ACAST_REGIONS 1 -#define ACAST_BASE 0x02994000 -#define ACAST_SIZE 0x1FFF +#define NUM_MAX_ACAST 2 -#define ACAST_GLOBAL_CTRL_VAL 0x07b80009 -#define ACAST_STREAMID_CTL_0_VAL 0x00007f01 -#define ACAST_STREAMID_CTL_1_VAL 0x00001e01 +#define ACAST_RGN_PHY 0x0 +#define ACAST_RGN_CTL_PHY (AST_PHSICAL(1) | AST_CARVEOUTID(0x7)) -static struct acast_region acast_regions[ACAST_REGIONS] = { - { - 0x2, - 0x00008008, - 0x40000000, - 0x20000000, - 0x40000000, - }, -}; +#define ACAST_RGN_VM 0x2 +#define ACAST_VMINDEX 1 +#define ACAST_RGN_CTL_VM(IDX) AST_VMINDEX(IDX) +#define ACAST_SID_REG_EVAL(IDX) AST_STREAMID_CTL_##IDX +#define ACAST_STRMID_REG(IDX) ACAST_SID_REG_EVAL(IDX) -static void __iomem *acast_base; +#if KERNEL_VERSION(4, 14, 0) > LINUX_VERSION_CODE +/* Older kernels do not have this function, so stubbing it */ +static inline int of_property_read_u64_index(const struct device_node *np, + const char *propname, u32 index, u64 *out_value) +{ + return -ENOSYS; +} +#endif static inline void acast_write(void __iomem *acast, u32 reg, u32 val) { writel(val, acast + reg); } -static inline u32 __maybe_unused acast_read(void __iomem *acast, u32 reg) +static inline u32 acast_read(void __iomem *acast, u32 reg) { return readl(acast + reg); } @@ -82,18 +90,12 @@ static inline u32 acast_rgn_reg(u32 rgn, u32 reg) return rgn * AST_RGN_OFFSET + reg; } -static void tegra18x_acast_map(void __iomem *acast, u64 rgn, u64 rgn_ctrl, +static void tegra18x_acast_map(void __iomem *acast, u32 rgn, u32 rgn_ctrl, + u32 strmid_reg, u32 strmid_ctrl, u64 slave, u64 size, u64 master) { u32 val; - val = (slave & AST_LO_MASK) | AST_RGN_ENABLE; - acast_write(acast, - acast_rgn_reg(rgn, AST_RGN_SLAVE_BASE_LO), val); - val = slave >> AST_LO_SHIFT; - acast_write(acast, - acast_rgn_reg(rgn, AST_RGN_SLAVE_BASE_HI), val); - val = master & AST_LO_MASK; acast_write(acast, acast_rgn_reg(rgn, AST_RGN_MASTER_BASE_LO), val); @@ -108,33 +110,51 @@ static void tegra18x_acast_map(void __iomem *acast, u64 rgn, u64 rgn_ctrl, acast_write(acast, acast_rgn_reg(rgn, AST_RGN_MASK_BASE_HI), val); + val = acast_read(acast, acast_rgn_reg(rgn, AST_RGN_CONTOL)); + val |= rgn_ctrl; acast_write(acast, - acast_rgn_reg(rgn, AST_RGN_CONTOL), rgn_ctrl); + acast_rgn_reg(rgn, AST_RGN_CONTOL), val); + + if (strmid_reg) + acast_write(acast, strmid_reg, strmid_ctrl); + + val = slave >> AST_LO_SHIFT; + acast_write(acast, + acast_rgn_reg(rgn, AST_RGN_SLAVE_BASE_HI), val); + val = (slave & AST_LO_MASK) | AST_RGN_ENABLE; + acast_write(acast, + acast_rgn_reg(rgn, AST_RGN_SLAVE_BASE_LO), val); } -int nvadsp_acast_init(struct platform_device *pdev) +static int tegra18x_acast_init(struct platform_device *pdev, + uint32_t acast_addr, uint32_t acast_size, + struct acast_region *acast_regions, uint32_t num_regions) { struct device *dev = &pdev->dev; + void __iomem *acast_base; int i; - if (!acast_base) { - acast_base = devm_ioremap(dev, ACAST_BASE, ACAST_SIZE); - if (IS_ERR_OR_NULL(acast_base)) { - dev_err(dev, "failed to map ACAST\n"); - return PTR_ERR(acast_base); - } + acast_base = devm_ioremap(dev, acast_addr, acast_size); + if (IS_ERR_OR_NULL(acast_base)) { + dev_err(dev, "failed to map ACAST 0x%x\n", acast_addr); + return PTR_ERR(acast_base); } - for (i = 0; i < ACAST_REGIONS; i++) { + for (i = 0; i < num_regions; i++) { tegra18x_acast_map(acast_base, acast_regions[i].rgn, acast_regions[i].rgn_ctrl, + acast_regions[i].strmid_reg, + acast_regions[i].strmid_ctrl, acast_regions[i].slave, acast_regions[i].size, acast_regions[i].master); - dev_dbg(dev, "i:%d rgn:0x%llx rgn_ctrl:0x%llx ", + dev_dbg(dev, "i:%d rgn:0x%x rgn_ctrl:0x%x ", i, acast_regions[i].rgn, acast_regions[i].rgn_ctrl); + dev_dbg(dev, "strmid_reg:0x%x strmid_ctrl:0x%x ", + acast_regions[i].strmid_reg, + acast_regions[i].strmid_ctrl); dev_dbg(dev, "slave:0x%llx size:0x%llx master:0x%llx\n", acast_regions[i].slave, acast_regions[i].size, acast_regions[i].master); @@ -142,3 +162,73 @@ int nvadsp_acast_init(struct platform_device *pdev) return 0; } + +int nvadsp_acast_t18x_init(struct platform_device *pdev) +{ + struct nvadsp_drv_data *drv_data = platform_get_drvdata(pdev); + struct device *dev = &pdev->dev; + struct resource *co_mem = &drv_data->co_mem; + uint32_t acast_addr, acast_size; + int iter, num_acast = 0, ret = 0; + struct acast_region acast_config; + + if (co_mem->start) { + acast_config.rgn = ACAST_RGN_PHY; + acast_config.rgn_ctrl = ACAST_RGN_CTL_PHY; + acast_config.strmid_reg = 0; + acast_config.strmid_ctrl = 0; + acast_config.slave = drv_data->adsp_mem[ADSP_OS_ADDR]; + acast_config.size = drv_data->adsp_mem[ADSP_OS_SIZE]; + acast_config.master = co_mem->start; + } else { + uint32_t stream_id; + uint64_t iommu_addr_start, iommu_addr_end; + + if (of_property_read_u32_index(dev->of_node, + "iommus", 1, &stream_id)) { + dev_warn(dev, "no SMMU stream ID found\n"); + goto exit; + } + if (of_property_read_u64_index(dev->of_node, + "iommu-resv-regions", 1, &iommu_addr_start)) { + dev_warn(dev, "no IOMMU reserved region\n"); + goto exit; + } + if (of_property_read_u64_index(dev->of_node, + "iommu-resv-regions", 2, &iommu_addr_end)) { + dev_warn(dev, "no IOMMU reserved region\n"); + goto exit; + } + + acast_config.rgn = ACAST_RGN_VM; + acast_config.rgn_ctrl = ACAST_RGN_CTL_VM(ACAST_VMINDEX); + acast_config.strmid_reg = ACAST_STRMID_REG(ACAST_VMINDEX); + acast_config.strmid_ctrl = AST_STREAMID(stream_id) | + AST_VMINDEX_ENABLE; + acast_config.slave = iommu_addr_start; + acast_config.size = (iommu_addr_end - acast_config.slave); + acast_config.master = iommu_addr_start; + } + + for (iter = 0; iter < (NUM_MAX_ACAST * 2); iter += 2) { + if (of_property_read_u32_index(dev->of_node, + "nvidia,acast_config", iter, &acast_addr)) + continue; + if (of_property_read_u32_index(dev->of_node, + "nvidia,acast_config", (iter + 1), &acast_size)) + continue; + + ret = tegra18x_acast_init(pdev, acast_addr, acast_size, + &acast_config, 1); + if (ret) + goto exit; + + num_acast++; + } + + if (num_acast == 0) + dev_warn(dev, "no ACAST configurations found\n"); + +exit: + return ret; +} diff --git a/drivers/platform/tegra/nvadsp/dev-t18x.c b/drivers/platform/tegra/nvadsp/dev-t18x.c index e6e40b4a..2e70ebd0 100644 --- a/drivers/platform/tegra/nvadsp/dev-t18x.c +++ b/drivers/platform/tegra/nvadsp/dev-t18x.c @@ -118,7 +118,6 @@ static int nvadsp_t18x_clocks_enable(struct platform_device *pdev) static int __nvadsp_t18x_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; dev_dbg(dev, "at %s:%d\n", __func__, __LINE__); @@ -129,14 +128,6 @@ static int __nvadsp_t18x_runtime_resume(struct device *dev) return ret; } - if (!drv_data->adsp_os_secload) { - ret = nvadsp_acast_init(pdev); - if (ret) { - dev_err(dev, "failed in nvadsp_acast_init\n"); - return ret; - } - } - return ret; } diff --git a/drivers/platform/tegra/nvadsp/dev-t18x.h b/drivers/platform/tegra/nvadsp/dev-t18x.h index b4b216de..fe0dd8fb 100644 --- a/drivers/platform/tegra/nvadsp/dev-t18x.h +++ b/drivers/platform/tegra/nvadsp/dev-t18x.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2015-2017, NVIDIA Corporation. All rights reserved. + * Copyright (C) 2015-2021, 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 @@ -15,7 +15,7 @@ #ifndef __TEGRA_NVADSP_DEV_T18X_H #define __TEGRA_NVADSP_DEV_T18X_H -int nvadsp_acast_init(struct platform_device *pdev); +int nvadsp_acast_t18x_init(struct platform_device *pdev); int nvadsp_reset_t18x_init(struct platform_device *pdev); int nvadsp_os_t18x_init(struct platform_device *pdev); int nvadsp_pm_t18x_init(struct platform_device *pdev); diff --git a/drivers/platform/tegra/nvadsp/dev.c b/drivers/platform/tegra/nvadsp/dev.c index 3d050b2d..667f8e3e 100644 --- a/drivers/platform/tegra/nvadsp/dev.c +++ b/drivers/platform/tegra/nvadsp/dev.c @@ -483,6 +483,13 @@ static int __init nvadsp_probe(struct platform_device *pdev) dev_err(dev, "Failed to init aram\n"); nvadsp_bw_register(drv_data); + + if (!drv_data->adsp_os_secload) { + ret = nvadsp_acast_init(pdev); + if (ret) + goto err; + } + err: #ifdef CONFIG_PM ret = pm_runtime_put_sync(dev); @@ -556,6 +563,7 @@ static struct nvadsp_chipdata tegrat18x_adsp_chipdata = { .adsp_thread_hwmbox = 0x20000, /* HWMBOX4 */ .adsp_state_hwmbox = 0x30000, /* HWMBOX6 */ .adsp_irq_hwmbox = 0x38000, /* HWMBOX7 */ + .acast_init = nvadsp_acast_t18x_init, .reset_init = nvadsp_reset_t18x_init, .os_init = nvadsp_os_t18x_init, #ifdef CONFIG_PM diff --git a/drivers/platform/tegra/nvadsp/dev.h b/drivers/platform/tegra/nvadsp/dev.h index 1e91746c..8fc8aee9 100644 --- a/drivers/platform/tegra/nvadsp/dev.h +++ b/drivers/platform/tegra/nvadsp/dev.h @@ -135,6 +135,7 @@ struct nvadsp_hwmb { }; +typedef int (*acast_init) (struct platform_device *pdev); typedef int (*reset_init) (struct platform_device *pdev); typedef int (*os_init) (struct platform_device *pdev); #ifdef CONFIG_PM @@ -147,6 +148,7 @@ struct nvadsp_chipdata { u32 adsp_thread_hwmbox; u32 adsp_irq_hwmbox; u32 adsp_shared_mem_hwmbox; + acast_init acast_init; reset_init reset_init; os_init os_init; #ifdef CONFIG_PM @@ -305,6 +307,16 @@ static inline int __init nvadsp_reset_init(struct platform_device *pdev) return -EINVAL; } +static inline int __init nvadsp_acast_init(struct platform_device *pdev) +{ + struct nvadsp_drv_data *drv_data = platform_get_drvdata(pdev); + + if (drv_data->chip_data->acast_init) + return drv_data->chip_data->acast_init(pdev); + + return 0; +} + #ifdef CONFIG_TEGRA_ADSP_LPTHREAD int adsp_lpthread_init(bool is_adsp_suspended); int adsp_lpthread_resume(void); From 0b2760e995a1e20aacf356a4aa092a83caef1d6e Mon Sep 17 00:00:00 2001 From: Akash Kollipara Date: Tue, 14 Dec 2021 16:09:54 +0530 Subject: [PATCH 113/138] nvadsp: Handles adspff init failure - Avoids adspff init in non-secure boot mode - Avoids boot failure if adspff.elf is absent on secure boot mode Bug 200773359 Bug 200746669 Change-Id: Ib62ce4ecae3434b7c7fde57e8735620976d2a653 Signed-off-by: Akash Kollipara Reviewed-on: https://git-master.nvidia.com/r/c/linux-nvidia/+/2641776 Tested-by: mobile promotions Reviewed-by: Asha Talambedu Reviewed-by: Sharad Gupta Reviewed-by: svcacv Reviewed-by: svc_kernel_abi Reviewed-by: mobile promotions GVS: Gerrit_Virtual_Submit --- drivers/platform/tegra/nvadsp/adspff.c | 5 +++-- drivers/platform/tegra/nvadsp/os.c | 29 +++++++++++++++----------- 2 files changed, 20 insertions(+), 14 deletions(-) diff --git a/drivers/platform/tegra/nvadsp/adspff.c b/drivers/platform/tegra/nvadsp/adspff.c index 487f0e34..bf9148c9 100644 --- a/drivers/platform/tegra/nvadsp/adspff.c +++ b/drivers/platform/tegra/nvadsp/adspff.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016-2021, NVIDIA CORPORATION. All rights reserved. + * Copyright (c) 2016-2022, NVIDIA CORPORATION. All rights reserved. * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU General Public License, @@ -15,6 +15,7 @@ #include #include +#include #include #include @@ -653,7 +654,7 @@ int adspff_init(struct platform_device *pdev) handle = nvadsp_app_load("adspff", "adspff.elf"); if (!handle) - return -1; + return -ENOENT; app_info = nvadsp_app_init(handle, NULL); if (!app_info) { diff --git a/drivers/platform/tegra/nvadsp/os.c b/drivers/platform/tegra/nvadsp/os.c index 122e56cd..709b5c9c 100644 --- a/drivers/platform/tegra/nvadsp/os.c +++ b/drivers/platform/tegra/nvadsp/os.c @@ -5,7 +5,7 @@ * Copyright (C) 2011 Texas Instruments, Inc. * Copyright (C) 2011 Google, Inc. * - * Copyright (C) 2014-2021, NVIDIA Corporation. All rights reserved. + * Copyright (C) 2014-2022, 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 @@ -20,6 +20,7 @@ #include #include +#include #include #include #include @@ -1815,19 +1816,23 @@ int nvadsp_os_start(void) priv.os_running = drv_data->adsp_os_running = true; priv.num_start++; #if defined(CONFIG_TEGRA_ADSP_FILEIO) - if (!drv_data->adspff_init) { - ret = adspff_init(priv.pdev); - if (ret) { - priv.os_running = drv_data->adsp_os_running = false; - dev_err(dev, - "adsp boot failed at adspff init with ret = %d", - ret); - dump_adsp_sys(); - free_interrupts(&priv); + if ((drv_data->adsp_os_secload) && (!drv_data->adspff_init)) { + int adspff_status = adspff_init(priv.pdev); + + if (adspff_status) { + if (adspff_status != -ENOENT) { + priv.os_running = drv_data->adsp_os_running = false; + dev_err(dev, + "adsp boot failed at adspff init with ret = %d", + adspff_status); + dump_adsp_sys(); + free_interrupts(&priv); #ifdef CONFIG_PM - pm_runtime_put_sync(&priv.pdev->dev); + pm_runtime_put_sync(&priv.pdev->dev); #endif - goto unlock; + ret = adspff_status; + goto unlock; + } } else drv_data->adspff_init = true; } From 5efd53d8fb96aafb548686b2eefb17745a79e906 Mon Sep 17 00:00:00 2001 From: Prateek Patel Date: Thu, 6 Jan 2022 10:16:18 +0000 Subject: [PATCH 114/138] drivers: adsp: fix check_return Coverity defect Defect: check_return: Calling of_property_read_u32 without checking return value. Fix the defect by validating the return value. CID 10129674 Bug 3461002 Change-Id: Idd236fccabc23b789d57c29c126e09391df022dc Signed-off-by: Prateek Patel Reviewed-on: https://git-master.nvidia.com/r/c/linux-nvidia/+/2650556 Reviewed-by: svcacv Reviewed-by: svc_kernel_abi Reviewed-by: Sachin Nikam Reviewed-by: mobile promotions GVS: Gerrit_Virtual_Submit Tested-by: mobile promotions --- drivers/platform/tegra/nvadsp/dev.c | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/drivers/platform/tegra/nvadsp/dev.c b/drivers/platform/tegra/nvadsp/dev.c index 667f8e3e..29a5a579 100644 --- a/drivers/platform/tegra/nvadsp/dev.c +++ b/drivers/platform/tegra/nvadsp/dev.c @@ -3,7 +3,7 @@ * * A device driver for ADSP and APE * - * Copyright (C) 2014-2021, NVIDIA Corporation. All rights reserved. + * Copyright (C) 2014-2022, 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 @@ -293,11 +293,13 @@ static int __init nvadsp_parse_dt(struct platform_device *pdev) drv_data->adsp_os_secload = of_property_read_bool(dev->of_node, "nvidia,adsp_os_secload"); - of_property_read_u32(dev->of_node, "nvidia,tegra_platform", - &drv_data->tegra_platform); + if (of_property_read_u32(dev->of_node, "nvidia,tegra_platform", + &drv_data->tegra_platform)) + dev_dbg(dev, "tegra_platform dt not found\n"); - of_property_read_u32(dev->of_node, "nvidia,adsp_load_timeout", - &drv_data->adsp_load_timeout); + if (of_property_read_u32(dev->of_node, "nvidia,adsp_load_timeout", + &drv_data->adsp_load_timeout)) + dev_dbg(dev, "adsp_load_timeout dt not found\n"); if (drv_data->adsp_unit_fpga) { for (iter = 0; iter < ADSP_UNIT_FPGA_RESET_END; iter++) { From 35393d7615c325d06a595cbbe3dc633991519d06 Mon Sep 17 00:00:00 2001 From: Prateek Patel Date: Wed, 12 Jan 2022 09:06:28 +0000 Subject: [PATCH 115/138] drivers: adsp: fix resource leak Coverity defect: Variable msg_recv going out of scope leaks the storage it points to. Fix the defect by freeing msg_recv before return. CID 31655 Bug 3461002 Change-Id: I350191943d342d33b1d07155ddcc2d875a78f7af Signed-off-by: Prateek Patel Reviewed-on: https://git-master.nvidia.com/r/c/linux-nvidia/+/2653460 Reviewed-by: svcacv Reviewed-by: svc_kernel_abi Reviewed-by: Sachin Nikam Reviewed-by: mobile promotions GVS: Gerrit_Virtual_Submit Tested-by: mobile promotions --- drivers/platform/tegra/nvadsp/adspff.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/platform/tegra/nvadsp/adspff.c b/drivers/platform/tegra/nvadsp/adspff.c index bf9148c9..7d971788 100644 --- a/drivers/platform/tegra/nvadsp/adspff.c +++ b/drivers/platform/tegra/nvadsp/adspff.c @@ -387,6 +387,7 @@ void adspff_fwrite(void) (msgq_message_t *)&message); if (ret < 0) { pr_err("fwrite Dequeue failed %d.", ret); + kfree(msg_recv); return; } From a99d68a86e0459d1b2219dccfc66076c46bee9e9 Mon Sep 17 00:00:00 2001 From: Viswanath L Date: Wed, 2 Feb 2022 09:00:48 +0000 Subject: [PATCH 116/138] nvadsp: Add provision to specify ADSP ELF name Provision is added to specify ADSP ELF name from DT, using prop 'nvidia,adsp_elf'. This will be picked from /lib/firmware/ when ADSP boots in backdoor mode. Default name remains "adsp.elf", if the prop is not specified. Bug 200746669 Bug 200773359 Change-Id: Ie5a4ba60fa2c049b7895967326a325257a65bb82 Signed-off-by: Viswanath L Reviewed-on: https://git-master.nvidia.com/r/c/linux-nvidia/+/2662909 Reviewed-by: Mohan Kumar D Reviewed-by: svcacv Reviewed-by: Asha Talambedu Reviewed-by: Dipesh Gandhi Reviewed-by: svc_kernel_abi Reviewed-by: Sharad Gupta GVS: Gerrit_Virtual_Submit --- drivers/platform/tegra/nvadsp/dev.c | 7 +++++++ drivers/platform/tegra/nvadsp/dev.h | 8 +++++++- drivers/platform/tegra/nvadsp/os.c | 14 ++++++-------- 3 files changed, 20 insertions(+), 9 deletions(-) diff --git a/drivers/platform/tegra/nvadsp/dev.c b/drivers/platform/tegra/nvadsp/dev.c index 29a5a579..7f718cda 100644 --- a/drivers/platform/tegra/nvadsp/dev.c +++ b/drivers/platform/tegra/nvadsp/dev.c @@ -263,6 +263,7 @@ 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; + const char *adsp_elf; u32 *adsp_reset; u32 *adsp_mem; int iter; @@ -287,6 +288,12 @@ static int __init nvadsp_parse_dt(struct platform_device *pdev) } } + if (!of_property_read_string(dev->of_node, + "nvidia,adsp_elf", &adsp_elf)) + strcpy(drv_data->adsp_elf, adsp_elf); + else + strcpy(drv_data->adsp_elf, NVADSP_ELF); + drv_data->adsp_unit_fpga = of_property_read_bool(dev->of_node, "nvidia,adsp_unit_fpga"); diff --git a/drivers/platform/tegra/nvadsp/dev.h b/drivers/platform/tegra/nvadsp/dev.h index 8fc8aee9..76adfb2c 100644 --- a/drivers/platform/tegra/nvadsp/dev.h +++ b/drivers/platform/tegra/nvadsp/dev.h @@ -3,7 +3,7 @@ * * A header file for Host driver for ADSP and APE * - * Copyright (C) 2014-2021, NVIDIA Corporation. All rights reserved. + * Copyright (C) 2014-2022, 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 @@ -104,6 +104,9 @@ enum adsp_unit_fpga_reset { #endif #endif +#define NVADSP_ELF "adsp.elf" +#define MAX_FW_STR 30 + enum nvadsp_virqs { MBOX_SEND_VIRQ, MBOX_RECV_VIRQ, @@ -248,6 +251,9 @@ struct nvadsp_drv_data { /* "nvidia,adsp_load_timeout" (in ms) */ u32 adsp_load_timeout; + + /* "nvidia,adsp_elf" (FW for backdoor boot) */ + char adsp_elf[MAX_FW_STR]; }; #define ADSP_CONFIG 0x04 diff --git a/drivers/platform/tegra/nvadsp/os.c b/drivers/platform/tegra/nvadsp/os.c index 709b5c9c..793e6506 100644 --- a/drivers/platform/tegra/nvadsp/os.c +++ b/drivers/platform/tegra/nvadsp/os.c @@ -58,9 +58,6 @@ #include "hwmailbox.h" #include "log_state.h" -#define NVADSP_ELF "adsp.elf" -#define NVADSP_FIRMWARE NVADSP_ELF - #define MAILBOX_REGION ".mbox_shared_data" #define DEBUG_RAM_REGION ".debug_mem_logs" @@ -845,10 +842,10 @@ static int nvadsp_firmware_load(struct platform_device *pdev) const struct firmware *fw; int ret = 0; - ret = request_firmware(&fw, NVADSP_FIRMWARE, dev); + ret = request_firmware(&fw, drv_data->adsp_elf, dev); if (ret < 0) { dev_err(dev, "reqest firmware for %s failed with %d\n", - NVADSP_FIRMWARE, ret); + drv_data->adsp_elf, ret); goto end; } #ifdef CONFIG_ANDROID @@ -864,11 +861,11 @@ static int nvadsp_firmware_load(struct platform_device *pdev) goto release_firmware; } - dev_info(dev, "Loading ADSP OS firmware %s\n", NVADSP_FIRMWARE); + dev_info(dev, "Loading ADSP OS firmware %s\n", drv_data->adsp_elf); ret = nvadsp_os_elf_load(fw); if (ret) { - dev_err(dev, "failed to load %s\n", NVADSP_FIRMWARE); + dev_err(dev, "failed to load %s\n", drv_data->adsp_elf); goto deallocate_os_memory; } @@ -1981,7 +1978,8 @@ static void __nvadsp_os_stop(bool reload) logger->ram_iter = 0; /* load a fresh copy of adsp.elf */ if (nvadsp_os_elf_load(fw)) - dev_err(dev, "failed to reload %s\n", NVADSP_FIRMWARE); + dev_err(dev, "failed to reload %s\n", + drv_data->adsp_elf); } end: From 7835064bc30a8d734aef7b127ac6b0bc9f554f65 Mon Sep 17 00:00:00 2001 From: Viswanath L Date: Mon, 7 Feb 2022 13:01:13 +0530 Subject: [PATCH 117/138] nvadsp: Add err check in ADSP ELF string copy Error check is added to prevent out-of-bound access when copying ADSP ELF string from DT. Fixes Coverity defect CID 10132209. Bug 3461002 Bug 200746669 Bug 200773359 Change-Id: I56beabcf8d78aed560cde523dee0429acd784dc9 Signed-off-by: Viswanath L Reviewed-on: https://git-master.nvidia.com/r/c/linux-nvidia/+/2664843 Reviewed-by: svcacv Reviewed-by: svc-mobile-coverity Reviewed-by: svc-mobile-cert Reviewed-by: svc_kernel_abi Reviewed-by: Sachin Nikam GVS: Gerrit_Virtual_Submit --- drivers/platform/tegra/nvadsp/dev.c | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/drivers/platform/tegra/nvadsp/dev.c b/drivers/platform/tegra/nvadsp/dev.c index 7f718cda..53eac2db 100644 --- a/drivers/platform/tegra/nvadsp/dev.c +++ b/drivers/platform/tegra/nvadsp/dev.c @@ -289,9 +289,14 @@ static int __init nvadsp_parse_dt(struct platform_device *pdev) } if (!of_property_read_string(dev->of_node, - "nvidia,adsp_elf", &adsp_elf)) - strcpy(drv_data->adsp_elf, adsp_elf); - else + "nvidia,adsp_elf", &adsp_elf)) { + if (strlen(adsp_elf) < MAX_FW_STR) + strcpy(drv_data->adsp_elf, adsp_elf); + else { + dev_err(dev, "invalid string in nvidia,adsp_elf\n"); + return -EINVAL; + } + } else strcpy(drv_data->adsp_elf, NVADSP_ELF); drv_data->adsp_unit_fpga = of_property_read_bool(dev->of_node, From f90d551e16220b8a6b8159188d8471d3c94b81e8 Mon Sep 17 00:00:00 2001 From: Viswanath L Date: Tue, 22 Feb 2022 07:16:02 +0000 Subject: [PATCH 118/138] drivers: nvadsp: Disable MBX empty intr at config MBOX empty interrupt line is high by default; configuring AGIC line in this state is undefined behaviour. Fix this by disabling the interrupt at source and enabling it only after unmasking in AGIC. Immediately upon unmasking one empty interrupt will be raised, which must be ignored. Bug 3432474 Change-Id: I03a26f061bb28b616626bb07153b0263540d7bd9 Signed-off-by: Viswanath L Reviewed-on: https://git-master.nvidia.com/r/c/linux-nvidia/+/2672095 Reviewed-by: svcacv Reviewed-by: svc-mobile-coverity Reviewed-by: svc-mobile-cert Reviewed-by: svc_kernel_abi Reviewed-by: Dipesh Gandhi Reviewed-by: Sharad Gupta GVS: Gerrit_Virtual_Submit --- drivers/platform/tegra/nvadsp/dev.c | 1 + drivers/platform/tegra/nvadsp/dev.h | 1 + drivers/platform/tegra/nvadsp/hwmailbox.c | 10 +++++++++- 3 files changed, 11 insertions(+), 1 deletion(-) diff --git a/drivers/platform/tegra/nvadsp/dev.c b/drivers/platform/tegra/nvadsp/dev.c index 53eac2db..fe5aecd2 100644 --- a/drivers/platform/tegra/nvadsp/dev.c +++ b/drivers/platform/tegra/nvadsp/dev.c @@ -572,6 +572,7 @@ static struct nvadsp_chipdata tegrat18x_adsp_chipdata = { .hwmbox5_reg = 0X28000, .hwmbox6_reg = 0X30000, .hwmbox7_reg = 0X38000, + .empty_int_ie = 0x8, }, .adsp_shared_mem_hwmbox = 0x18000, /* HWMBOX3 */ .adsp_thread_hwmbox = 0x20000, /* HWMBOX4 */ diff --git a/drivers/platform/tegra/nvadsp/dev.h b/drivers/platform/tegra/nvadsp/dev.h index 76adfb2c..f7254358 100644 --- a/drivers/platform/tegra/nvadsp/dev.h +++ b/drivers/platform/tegra/nvadsp/dev.h @@ -135,6 +135,7 @@ struct nvadsp_hwmb { u32 hwmbox5_reg; u32 hwmbox6_reg; u32 hwmbox7_reg; + u32 empty_int_ie; }; diff --git a/drivers/platform/tegra/nvadsp/hwmailbox.c b/drivers/platform/tegra/nvadsp/hwmailbox.c index 9ae54b8b..c6d90820 100644 --- a/drivers/platform/tegra/nvadsp/hwmailbox.c +++ b/drivers/platform/tegra/nvadsp/hwmailbox.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014-2017, NVIDIA CORPORATION. All rights reserved. + * Copyright (c) 2014-2022, NVIDIA CORPORATION. All rights reserved. * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU General Public License, @@ -188,6 +188,9 @@ static irqreturn_t hwmbox_send_empty_int_handler(int irq, void *devid) uint32_t data; int ret; + if (!is_hwmbox_busy) + return IRQ_HANDLED; + spin_lock_irqsave(lock, lockflags); data = hwmbox_readl(send_hwmbox()); @@ -283,6 +286,7 @@ int nvadsp_setup_hwmbox_interrupts(struct platform_device *pdev) { struct nvadsp_drv_data *drv = platform_get_drvdata(pdev); struct device *dev = &pdev->dev; + u32 empty_int_ie = drv->chip_data->hwmb.empty_int_ie; int recv_virq, send_virq; int ret; @@ -294,9 +298,13 @@ int nvadsp_setup_hwmbox_interrupts(struct platform_device *pdev) if (ret) goto err; + if (empty_int_ie) + hwmbox_writel(0x0, send_hwmbox() + empty_int_ie); ret = devm_request_irq(dev, send_virq, hwmbox_send_empty_int_handler, IRQF_TRIGGER_RISING, "hwmbox1_send_empty", pdev); + if (empty_int_ie) + hwmbox_writel(0x1, send_hwmbox() + empty_int_ie); if (ret) goto free_interrupts; From 3ea7c9344cfd82b85cfa9802e4a4142694c28d24 Mon Sep 17 00:00:00 2001 From: Sharad gupta Date: Fri, 18 Feb 2022 15:14:08 +0530 Subject: [PATCH 119/138] nvadsp: os: CERT-C Fixes Uploaded fix to address coverity issues. 1. Fix for string assignment to a char ptr (const) 2. Unsigned int type variable to hold AGIC IRQ number 3. Misc. : added error handling for null pointers Bug 3512545 CID : 407176, 339372, 426068, 349597, 408235, 468432, 425451, 339564, 340901, 360199, 482718, 338388, 434000, 488442, 355983, 437791, 444590, 409920, 423102, 416511, 426540, 392115, 451458, 355042, 443648, 420343, 423532, 488763, 476874, 404805, 380630, 358854, 485375, 483303, 462651, 371708, 465040, 447093, 406990, 335646, 437343, 467012, 378419, 428324, 354351, 460155, 415702, 376737, 390977, 414736, 411426, 393560, 490887 Change-Id: I5c847fa02438931e95107d06333014fb802c8207 Signed-off-by: Sharad Gupta Reviewed-on: https://git-master.nvidia.com/r/c/linux-nvidia/+/2672112 Tested-by: mobile promotions Reviewed-by: mobile promotions --- drivers/platform/tegra/nvadsp/dev.h | 2 +- drivers/platform/tegra/nvadsp/os.c | 36 +++++++++++++++++++++-------- 2 files changed, 28 insertions(+), 10 deletions(-) diff --git a/drivers/platform/tegra/nvadsp/dev.h b/drivers/platform/tegra/nvadsp/dev.h index f7254358..f0102949 100644 --- a/drivers/platform/tegra/nvadsp/dev.h +++ b/drivers/platform/tegra/nvadsp/dev.h @@ -234,7 +234,7 @@ struct nvadsp_drv_data { u32 adsp_mem[ADSP_MEM_END]; bool adsp_unit_fpga; u32 unit_fpga_reset[ADSP_UNIT_FPGA_RESET_END]; - int agic_irqs[NVADSP_VIRQ_MAX]; + u32 agic_irqs[NVADSP_VIRQ_MAX]; struct tegra_bwmgr_client *bwmgr; #if KERNEL_VERSION(5, 9, 0) <= LINUX_VERSION_CODE diff --git a/drivers/platform/tegra/nvadsp/os.c b/drivers/platform/tegra/nvadsp/os.c index 793e6506..7a472532 100644 --- a/drivers/platform/tegra/nvadsp/os.c +++ b/drivers/platform/tegra/nvadsp/os.c @@ -446,6 +446,7 @@ static inline void __maybe_unused dump_global_symbol_table(void) "STT_FUNC" : "STT_OBJECT"); } +#ifdef CONFIG_ANDROID static int __maybe_unused create_global_symbol_table(const struct firmware *fw) { @@ -460,6 +461,11 @@ __maybe_unused create_global_symbol_table(const struct firmware *fw) struct elf32_sym *sym; struct elf32_sym *last_sym; + if (!sym_shdr || !str_shdr) { + dev_dbg(dev, "section symtab/strtab not found!\n"); + return -EINVAL; + } + sym = (struct elf32_sym *)(elf_data + sym_shdr->sh_offset); name_table = elf_data + str_shdr->sh_offset; @@ -487,6 +493,7 @@ __maybe_unused create_global_symbol_table(const struct firmware *fw) priv.adsp_glo_sym_tbl[0].addr = i; return 0; } +#endif /* CONFIG_ANDROID */ struct global_sym_info * __maybe_unused find_global_symbol(const char *sym_name) { @@ -623,9 +630,6 @@ static void *nvadsp_dma_alloc_and_map_at(struct platform_device *pdev, { struct iommu_domain *domain = iommu_get_domain_for_dev(&pdev->dev); unsigned long align_mask = ~0UL << fls_long(size - 1); - unsigned long shift = __ffs(domain->pgsize_bitmap); - unsigned long pg_size = 1UL << shift; - unsigned long mp_size = pg_size; struct device *dev = &pdev->dev; dma_addr_t aligned_iova = iova & align_mask; dma_addr_t end = iova + size; @@ -633,6 +637,16 @@ static void *nvadsp_dma_alloc_and_map_at(struct platform_device *pdev, phys_addr_t pa, pa_new; void *cpu_va; int ret; + unsigned long shift = 0, pg_size = 0, mp_size = 0; + + if (!domain) { + dev_err(dev, "Unable to get iommu_domain\n"); + return NULL; + } + + shift = __ffs(domain->pgsize_bitmap); + pg_size = 1UL << shift; + mp_size = pg_size; /* * Reserve iova range using aligned size: adsp memory might not start @@ -880,6 +894,10 @@ static int nvadsp_firmware_load(struct platform_device *pdev) drv_data->shared_adsp_os_data_iova = priv.adsp_os_addr; shared_mem = nvadsp_da_to_va_mappings( priv.adsp_os_addr, priv.adsp_os_size); + if (!shared_mem) { + dev_err(dev, "Failed to get VA for ADSP OS\n"); + goto deallocate_os_memory; + } } else { dev_err(dev, "failed to locate shared memory\n"); goto deallocate_os_memory; @@ -1456,7 +1474,7 @@ static void get_adsp_state(void) struct nvadsp_drv_data *drv_data; struct device *dev; uint32_t val; - char *msg; + const char *msg; if (!priv.pdev) { pr_err("ADSP Driver is not initialized\n"); @@ -1651,8 +1669,8 @@ EXPORT_SYMBOL(dump_adsp_sys); static void nvadsp_free_os_interrupts(struct nvadsp_os_data *priv) { struct nvadsp_drv_data *drv_data = platform_get_drvdata(priv->pdev); - int wdt_virq = drv_data->agic_irqs[WDT_VIRQ]; - int wfi_virq = drv_data->agic_irqs[WFI_VIRQ]; + unsigned int wdt_virq = drv_data->agic_irqs[WDT_VIRQ]; + unsigned int wfi_virq = drv_data->agic_irqs[WFI_VIRQ]; struct device *dev = &priv->pdev->dev; devm_free_irq(dev, wdt_virq, priv); @@ -1662,8 +1680,8 @@ static void nvadsp_free_os_interrupts(struct nvadsp_os_data *priv) static int nvadsp_setup_os_interrupts(struct nvadsp_os_data *priv) { struct nvadsp_drv_data *drv_data = platform_get_drvdata(priv->pdev); - int wdt_virq = drv_data->agic_irqs[WDT_VIRQ]; - int wfi_virq = drv_data->agic_irqs[WFI_VIRQ]; + unsigned int wdt_virq = drv_data->agic_irqs[WDT_VIRQ]; + unsigned int wfi_virq = drv_data->agic_irqs[WFI_VIRQ]; struct device *dev = &priv->pdev->dev; int ret; @@ -2064,7 +2082,7 @@ static void nvadsp_os_restart(struct work_struct *work) struct nvadsp_os_data *data = container_of(work, struct nvadsp_os_data, restart_os_work); struct nvadsp_drv_data *drv_data = platform_get_drvdata(data->pdev); - int wdt_virq = drv_data->agic_irqs[WDT_VIRQ]; + unsigned int wdt_virq = drv_data->agic_irqs[WDT_VIRQ]; int wdt_irq = drv_data->chip_data->wdt_irq; struct device *dev = &data->pdev->dev; From 77a7ea2c88cfae6a518819fe4a4aae4338670719 Mon Sep 17 00:00:00 2001 From: Viswanath L Date: Mon, 28 Feb 2022 15:30:48 +0530 Subject: [PATCH 120/138] nvadsp: Skip ACAST setting if already set It may be useful at times to boot ADSP via backdoor, even while system boots via front door. MB2 sets the ACAST in front door, so ACAST setting from nvadsp driver is skipped if that region is already enabled. Above scheme will work for ADSP backdoor boot via SMMU mapped memory, but not for physical memory, as the underlying carveout memory is different between frontdoor and backdoor. Bug 200745826 Change-Id: I718da97e3f06eb86b3e40efab91275f2d5958dd4 Signed-off-by: Viswanath L Reviewed-on: https://git-master.nvidia.com/r/c/linux-nvidia/+/2675786 Reviewed-by: svcacv Reviewed-by: svc_kernel_abi Reviewed-by: Dara Ramesh GVS: Gerrit_Virtual_Submit --- drivers/platform/tegra/nvadsp/acast.c | 24 +++++++++++++++--------- 1 file changed, 15 insertions(+), 9 deletions(-) diff --git a/drivers/platform/tegra/nvadsp/acast.c b/drivers/platform/tegra/nvadsp/acast.c index 061cacfa..f8de7f40 100644 --- a/drivers/platform/tegra/nvadsp/acast.c +++ b/drivers/platform/tegra/nvadsp/acast.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2016-2021 NVIDIA CORPORATION. All rights reserved. + * Copyright (C) 2016-2022 NVIDIA CORPORATION. All rights reserved. * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU General Public License, @@ -29,7 +29,7 @@ #define AST_RGN_MASK_BASE_HI 0x10c #define AST_RGN_MASTER_BASE_LO 0x110 #define AST_RGN_MASTER_BASE_HI 0x114 -#define AST_RGN_CONTOL 0x118 +#define AST_RGN_CONTROL 0x118 #define AST_PAGE_MASK (~0xFFF) #define AST_LO_SHIFT 32 @@ -90,12 +90,19 @@ static inline u32 acast_rgn_reg(u32 rgn, u32 reg) return rgn * AST_RGN_OFFSET + reg; } -static void tegra18x_acast_map(void __iomem *acast, u32 rgn, u32 rgn_ctrl, +static void tegra18x_acast_map(struct device *dev, + void __iomem *acast, u32 rgn, u32 rgn_ctrl, u32 strmid_reg, u32 strmid_ctrl, u64 slave, u64 size, u64 master) { u32 val; + val = acast_read(acast, acast_rgn_reg(rgn, AST_RGN_SLAVE_BASE_LO)); + if (val & AST_RGN_ENABLE) { + dev_warn(dev, "ACAST rgn %u already mapped...skipping\n", rgn); + return; + } + val = master & AST_LO_MASK; acast_write(acast, acast_rgn_reg(rgn, AST_RGN_MASTER_BASE_LO), val); @@ -110,10 +117,10 @@ static void tegra18x_acast_map(void __iomem *acast, u32 rgn, u32 rgn_ctrl, acast_write(acast, acast_rgn_reg(rgn, AST_RGN_MASK_BASE_HI), val); - val = acast_read(acast, acast_rgn_reg(rgn, AST_RGN_CONTOL)); + val = acast_read(acast, acast_rgn_reg(rgn, AST_RGN_CONTROL)); val |= rgn_ctrl; acast_write(acast, - acast_rgn_reg(rgn, AST_RGN_CONTOL), val); + acast_rgn_reg(rgn, AST_RGN_CONTROL), val); if (strmid_reg) acast_write(acast, strmid_reg, strmid_ctrl); @@ -126,11 +133,10 @@ static void tegra18x_acast_map(void __iomem *acast, u32 rgn, u32 rgn_ctrl, acast_rgn_reg(rgn, AST_RGN_SLAVE_BASE_LO), val); } -static int tegra18x_acast_init(struct platform_device *pdev, +static int tegra18x_acast_init(struct device *dev, uint32_t acast_addr, uint32_t acast_size, struct acast_region *acast_regions, uint32_t num_regions) { - struct device *dev = &pdev->dev; void __iomem *acast_base; int i; @@ -141,7 +147,7 @@ static int tegra18x_acast_init(struct platform_device *pdev, } for (i = 0; i < num_regions; i++) { - tegra18x_acast_map(acast_base, + tegra18x_acast_map(dev, acast_base, acast_regions[i].rgn, acast_regions[i].rgn_ctrl, acast_regions[i].strmid_reg, @@ -218,7 +224,7 @@ int nvadsp_acast_t18x_init(struct platform_device *pdev) "nvidia,acast_config", (iter + 1), &acast_size)) continue; - ret = tegra18x_acast_init(pdev, acast_addr, acast_size, + ret = tegra18x_acast_init(dev, acast_addr, acast_size, &acast_config, 1); if (ret) goto exit; From 5616ba82b164acf79bd71ac3874a1453957cf412 Mon Sep 17 00:00:00 2001 From: Viswanath L Date: Tue, 1 Mar 2022 21:00:39 +0530 Subject: [PATCH 121/138] nvadsp: Fix uninitialized access Access module name from initialized location. CID: 490698 Bug 3512545 Change-Id: I8b6ece000342b424a3b0e1b0a29fb423d6801f16 Signed-off-by: Viswanath L Reviewed-on: https://git-master.nvidia.com/r/c/linux-nvidia/+/2676344 Reviewed-by: svcacv Reviewed-by: svc_kernel_abi Reviewed-by: svc-mobile-coverity Reviewed-by: svc-mobile-cert Reviewed-by: Uday Gupta Reviewed-by: Sachin Nikam GVS: Gerrit_Virtual_Submit --- drivers/platform/tegra/nvadsp/app_loader_linker.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/platform/tegra/nvadsp/app_loader_linker.c b/drivers/platform/tegra/nvadsp/app_loader_linker.c index 303d6993..083a0d4a 100644 --- a/drivers/platform/tegra/nvadsp/app_loader_linker.c +++ b/drivers/platform/tegra/nvadsp/app_loader_linker.c @@ -3,7 +3,7 @@ * * ADSP OS App management * - * Copyright (C) 2014-2020 NVIDIA Corporation. All rights reserved. + * Copyright (C) 2014-2022 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 @@ -701,7 +701,7 @@ static struct adsp_module *setup_load_info(struct load_info *info) if (info->index.sym == 0) { dev_warn(dev, "%s: module has no symbols (stripped?)\n", - mod->name); + info->name); kfree(mod); return ERR_PTR(-ENOEXEC); } From 2204b3fe109d2b655cb8abd00716bd6e0e73ac8e Mon Sep 17 00:00:00 2001 From: Viswanath L Date: Tue, 1 Mar 2022 23:39:25 +0530 Subject: [PATCH 122/138] nvadsp: Use array bound in loop Use array bound in loop rather than depend upon a NULL element to mark the end. CID: 490319 Bug 3512545 Change-Id: I65c7eeecd41e8cfa3c35e2bbaa059b272b154d70 Signed-off-by: Viswanath L Reviewed-on: https://git-master.nvidia.com/r/c/linux-nvidia/+/2676345 Reviewed-by: svcacv Reviewed-by: svc_kernel_abi Reviewed-by: svc-mobile-coverity Reviewed-by: svc-mobile-cert Reviewed-by: Uday Gupta Reviewed-by: Sachin Nikam GVS: Gerrit_Virtual_Submit --- drivers/platform/tegra/nvadsp/os-t18x.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/drivers/platform/tegra/nvadsp/os-t18x.c b/drivers/platform/tegra/nvadsp/os-t18x.c index eec5e333..f05b8e98 100644 --- a/drivers/platform/tegra/nvadsp/os-t18x.c +++ b/drivers/platform/tegra/nvadsp/os-t18x.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2015-2020, NVIDIA Corporation. All rights reserved. + * Copyright (C) 2015-2022, 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 @@ -48,10 +48,9 @@ static int tegra_adma_query_dma_page(void) "nvidia,tegra210-adma-hv", "nvidia,tegra186-adma", "nvidia,tegra194-adma-hv", - NULL, }; - for (i = 0; compatible[i] != NULL; i++) { + for (i = 0; i < ARRAY_SIZE(compatible); i++) { np = of_find_compatible_node(NULL, NULL, compatible[i]); if (np == NULL) continue; From 9b526e03db947b00962bc1acb6adf96b21b3ce75 Mon Sep 17 00:00:00 2001 From: Sharad Gupta Date: Wed, 9 Mar 2022 23:13:51 +0530 Subject: [PATCH 123/138] nvadsp: CERT-C Fixes Fixed for L1 issues : Return value check for snprintf NULL pointer check using IS_ERR_OR_NULL Bug 3512545 CID 407176 CID 429671 CID 471352 Signed-off-by: Sharad Gupta Change-Id: Ib03339db8dc669b6eff448941fac62b2feabf7bc Reviewed-on: https://git-master.nvidia.com/r/c/linux-nvidia/+/2679048 Reviewed-by: svcacv Reviewed-by: Akash Kollipara Reviewed-by: Sachin Nikam Reviewed-by: Viswanath L Reviewed-by: svc-mobile-coverity Reviewed-by: svc-mobile-cert Reviewed-by: svc_kernel_abi GVS: Gerrit_Virtual_Submit --- drivers/platform/tegra/nvadsp/app.c | 9 +++++++-- drivers/platform/tegra/nvadsp/app_loader_linker.c | 2 +- drivers/platform/tegra/nvadsp/os.c | 4 ++-- 3 files changed, 10 insertions(+), 5 deletions(-) diff --git a/drivers/platform/tegra/nvadsp/app.c b/drivers/platform/tegra/nvadsp/app.c index 12634b76..38dac101 100644 --- a/drivers/platform/tegra/nvadsp/app.c +++ b/drivers/platform/tegra/nvadsp/app.c @@ -3,7 +3,7 @@ * * ADSP OS App management * - * Copyright (C) 2014-2021, NVIDIA Corporation. All rights reserved. + * Copyright (C) 2014-2022, 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 @@ -425,8 +425,13 @@ static int create_instance_memory(nvadsp_app_info_t *app, char name[NVADSP_NAME_SZ]; void *aram_handle; dma_addr_t da; + int ret; - snprintf(name, NVADSP_NAME_SZ, "%s:%d", app->name, app->instance_id); + ret = snprintf(name, NVADSP_NAME_SZ, "%s:%d", app->name, app->instance_id); + if (ret < 0 || ret >= NVADSP_NAME_SZ) { + dev_err(dev, "Invalid App name %s\n", app->name); + return -EINVAL; + } if (sz->dram) { mem->dram = nvadsp_alloc_coherent(sz->dram, &da, GFP_KERNEL); diff --git a/drivers/platform/tegra/nvadsp/app_loader_linker.c b/drivers/platform/tegra/nvadsp/app_loader_linker.c index 083a0d4a..cfd8d193 100644 --- a/drivers/platform/tegra/nvadsp/app_loader_linker.c +++ b/drivers/platform/tegra/nvadsp/app_loader_linker.c @@ -893,7 +893,7 @@ struct adsp_module *load_adsp_dynamic_module(const char *appname, /* Figure out module layout, and allocate all the memory. */ mod = layout_and_allocate(&info); - if (IS_ERR(mod)) + if (IS_ERR_OR_NULL(mod)) goto error_free_memory; /* update adsp specific sections */ diff --git a/drivers/platform/tegra/nvadsp/os.c b/drivers/platform/tegra/nvadsp/os.c index 7a472532..7bb55ee4 100644 --- a/drivers/platform/tegra/nvadsp/os.c +++ b/drivers/platform/tegra/nvadsp/os.c @@ -332,7 +332,7 @@ bool is_adsp_dram_addr(u64 addr) int nvadsp_add_load_mappings(phys_addr_t pa, void *mapping, int len) { - if (map_idx >= NM_LOAD_MAPPINGS) + if (map_idx < 0 || map_idx >= NM_LOAD_MAPPINGS) return -EINVAL; adsp_map[map_idx].da = pa; @@ -884,7 +884,7 @@ static int nvadsp_firmware_load(struct platform_device *pdev) } shared_mem = get_mailbox_shared_region(fw); - if (IS_ERR(shared_mem)) { + if (IS_ERR_OR_NULL(shared_mem)) { if (drv_data->chip_data->adsp_shared_mem_hwmbox != 0) { /* * If FW is not explicitly defining a shared memory From 19cf0a1232c3767ce0641ddb0701cc3e3e41cd89 Mon Sep 17 00:00:00 2001 From: Uday Gupta Date: Fri, 28 Jan 2022 13:30:10 +0530 Subject: [PATCH 124/138] nvadsp: Use HWMBOX5 for ADSP OS decompression There is a decompress_done flag in the adsp OS firmware that was earlier used to indicate that decompress is done at the adsp os cold boot However this flag cannot be part of firmware as fw needs to authenticated at the sc7 resume as well and hence the fw should not have modifications On fw side, the flag is removed. Instead the same information is preserved across sc7 cycles via HWMBOX5 and this driver enables the decompress bit to indicate the same to adsp at cold start Bug 3491011 Change-Id: I77d1ff6defdf1228f4cd4278cf5bb667df51fad1 Signed-off-by: Uday Gupta Reviewed-on: https://git-master.nvidia.com/r/c/linux-nvidia/+/2660706 Tested-by: Asha Talambedu Reviewed-by: svcacv Reviewed-by: Asha Talambedu Reviewed-by: svc_kernel_abi Reviewed-by: Sharad Gupta Reviewed-by: Viswanath L GVS: Gerrit_Virtual_Submit --- drivers/platform/tegra/nvadsp/dev.c | 2 ++ drivers/platform/tegra/nvadsp/dev.h | 1 + drivers/platform/tegra/nvadsp/os-t18x.c | 2 +- drivers/platform/tegra/nvadsp/os.c | 20 +++++++++++++++++--- 4 files changed, 21 insertions(+), 4 deletions(-) diff --git a/drivers/platform/tegra/nvadsp/dev.c b/drivers/platform/tegra/nvadsp/dev.c index fe5aecd2..838eb9f8 100644 --- a/drivers/platform/tegra/nvadsp/dev.c +++ b/drivers/platform/tegra/nvadsp/dev.c @@ -549,6 +549,7 @@ static struct nvadsp_chipdata tegra210_adsp_chipdata = { .adsp_thread_hwmbox = 0, .adsp_irq_hwmbox = 0, .adsp_shared_mem_hwmbox = 0, + .adsp_os_config_hwmbox = 0, .reset_init = nvadsp_reset_t21x_init, .os_init = nvadsp_os_t21x_init, #ifdef CONFIG_PM @@ -576,6 +577,7 @@ static struct nvadsp_chipdata tegrat18x_adsp_chipdata = { }, .adsp_shared_mem_hwmbox = 0x18000, /* HWMBOX3 */ .adsp_thread_hwmbox = 0x20000, /* HWMBOX4 */ + .adsp_os_config_hwmbox = 0X28000, /*HWMBOX5 */ .adsp_state_hwmbox = 0x30000, /* HWMBOX6 */ .adsp_irq_hwmbox = 0x38000, /* HWMBOX7 */ .acast_init = nvadsp_acast_t18x_init, diff --git a/drivers/platform/tegra/nvadsp/dev.h b/drivers/platform/tegra/nvadsp/dev.h index f0102949..e82c6ba0 100644 --- a/drivers/platform/tegra/nvadsp/dev.h +++ b/drivers/platform/tegra/nvadsp/dev.h @@ -152,6 +152,7 @@ struct nvadsp_chipdata { u32 adsp_thread_hwmbox; u32 adsp_irq_hwmbox; u32 adsp_shared_mem_hwmbox; + u32 adsp_os_config_hwmbox; acast_init acast_init; reset_init reset_init; os_init os_init; diff --git a/drivers/platform/tegra/nvadsp/os-t18x.c b/drivers/platform/tegra/nvadsp/os-t18x.c index f05b8e98..604e9641 100644 --- a/drivers/platform/tegra/nvadsp/os-t18x.c +++ b/drivers/platform/tegra/nvadsp/os-t18x.c @@ -90,7 +90,7 @@ int nvadsp_os_t18x_init(struct platform_device *pdev) val = val | (adma_ch_page << ADSP_CONFIG_DMA_PAGE_SHIFT); /* Write to HWMBOX5 */ - hwmbox_writel(val, drv_data->chip_data->hwmb.hwmbox5_reg); + hwmbox_writel(val, drv_data->chip_data->adsp_os_config_hwmbox); /* Clear HWMBOX0 for ADSP Guest reset handling */ hwmbox_writel(0, drv_data->chip_data->hwmb.hwmbox0_reg); diff --git a/drivers/platform/tegra/nvadsp/os.c b/drivers/platform/tegra/nvadsp/os.c index 7bb55ee4..ce84cdf8 100644 --- a/drivers/platform/tegra/nvadsp/os.c +++ b/drivers/platform/tegra/nvadsp/os.c @@ -1789,10 +1789,24 @@ int nvadsp_os_start(void) if (ret < 0) goto unlock; - if (cold_start && drv_data->chip_data->adsp_shared_mem_hwmbox != 0) { - hwmbox_writel((uint32_t)drv_data->shared_adsp_os_data_iova, + if (cold_start) { + if (drv_data->chip_data->adsp_shared_mem_hwmbox != 0) + hwmbox_writel( + (uint32_t)drv_data->shared_adsp_os_data_iova, drv_data->chip_data->adsp_shared_mem_hwmbox); - /* Write ACSR base address only once */ + + if (!is_tegra_hypervisor_mode() && + drv_data->chip_data->adsp_os_config_hwmbox != 0) { + /* Set ADSP to do decompression */ + uint32_t val = (ADSP_CONFIG_DECOMPRESS_EN << + ADSP_CONFIG_DECOMPRESS_SHIFT); + + /* Write to HWMBOX5 */ + hwmbox_writel(val, + drv_data->chip_data->adsp_os_config_hwmbox); + } + + /* Write ACSR base address and decompr enable flag only once */ cold_start = 0; } From 3fa96a4417bea38b24eb22958ec4479a10f1e564 Mon Sep 17 00:00:00 2001 From: Niranjan Dighe Date: Mon, 21 Mar 2022 23:28:58 +0530 Subject: [PATCH 125/138] drivers: platform: nvadsp: Fix sparse issues Fix multiple instances of the following issues - - warning: incorrect type in argument 2 (different address spaces) - warning: symbol 'file_size' was not declared. Should it be static? Bug 3528414 Change-Id: I2cbda8dbfc98b134f36ec3c291a5147d96c6ff82 Signed-off-by: Niranjan Dighe Reviewed-on: https://git-master.nvidia.com/r/c/linux-nvidia/+/2684747 Tested-by: mobile promotions Reviewed-by: mobile promotions --- drivers/platform/tegra/nvadsp/adspff.c | 26 ++++++++++++------------- drivers/platform/tegra/nvadsp/os-t18x.c | 1 + drivers/platform/tegra/nvadsp/os.c | 4 +++- 3 files changed, 17 insertions(+), 14 deletions(-) diff --git a/drivers/platform/tegra/nvadsp/adspff.c b/drivers/platform/tegra/nvadsp/adspff.c index 7d971788..11c10959 100644 --- a/drivers/platform/tegra/nvadsp/adspff.c +++ b/drivers/platform/tegra/nvadsp/adspff.c @@ -53,7 +53,7 @@ static int open_count; * Kernel file functions ******************************************************************************/ -struct file *file_open(const char *path, int flags, int rights) +static struct file *file_open(const char *path, int flags, int rights) { struct file *filp = NULL; mm_segment_t oldfs; @@ -70,12 +70,12 @@ struct file *file_open(const char *path, int flags, int rights) return filp; } -void file_close(struct file *file) +static void file_close(struct file *file) { filp_close(file, NULL); } -int file_write(struct file *file, unsigned long long *offset, +static int file_write(struct file *file, unsigned long long *offset, unsigned char *data, unsigned int size) { mm_segment_t oldfs; @@ -84,13 +84,13 @@ int file_write(struct file *file, unsigned long long *offset, oldfs = get_fs(); set_fs(KERNEL_DS); - ret = vfs_write(file, data, size, offset); + ret = vfs_write(file, (const char __user *)data, size, offset); set_fs(oldfs); return ret; } -uint32_t file_read(struct file *file, unsigned long long *offset, +static uint32_t file_read(struct file *file, unsigned long long *offset, unsigned char *data, unsigned int size) { mm_segment_t oldfs; @@ -99,14 +99,14 @@ uint32_t file_read(struct file *file, unsigned long long *offset, oldfs = get_fs(); set_fs(KERNEL_DS); - ret = vfs_read(file, data, size, offset); + ret = vfs_read(file, (char __user *)data, size, offset); set_fs(oldfs); return ret; } -uint32_t file_size(struct file *file) +static uint32_t file_size(struct file *file) { mm_segment_t oldfs; uint32_t size = 0; @@ -137,7 +137,7 @@ static struct nvadsp_mbox rx_mbox; * w+ - open for reading and writing (overwrite file) * * a+ - open for reading and writing (append if file exists) */ -void set_flags(union adspff_message_t *m, unsigned int *flags) +static void set_flags(union adspff_message_t *m, unsigned int *flags) { if (0 == strcmp(m->msg.payload.fopen_msg.modes, "r+")) *flags = O_RDWR; @@ -202,7 +202,7 @@ static struct file_struct *check_file_opened(const char *path) return file; } -void adspff_fopen(void) +static void adspff_fopen(void) { union adspff_message_t *message; union adspff_message_t *msg_recv; @@ -293,7 +293,7 @@ static inline unsigned int is_write_file(struct file_struct *file) return file->flags & (O_WRONLY | O_RDWR); } -void adspff_fclose(void) +static void adspff_fclose(void) { union adspff_message_t *message; struct file_struct *file = NULL; @@ -326,7 +326,7 @@ void adspff_fclose(void) kfree(message); } -void adspff_fsize(void) +static void adspff_fsize(void) { union adspff_message_t *msg_recv; union adspff_message_t message; @@ -366,7 +366,7 @@ void adspff_fsize(void) kfree(msg_recv); } -void adspff_fwrite(void) +static void adspff_fwrite(void) { union adspff_message_t message; union adspff_message_t *msg_recv; @@ -424,7 +424,7 @@ void adspff_fwrite(void) kfree(msg_recv); } -void adspff_fread(void) +static void adspff_fread(void) { union adspff_message_t *message; union adspff_message_t *msg_recv; diff --git a/drivers/platform/tegra/nvadsp/os-t18x.c b/drivers/platform/tegra/nvadsp/os-t18x.c index 604e9641..c850892f 100644 --- a/drivers/platform/tegra/nvadsp/os-t18x.c +++ b/drivers/platform/tegra/nvadsp/os-t18x.c @@ -25,6 +25,7 @@ #include "dev.h" #include "os.h" +#include "dev-t18x.h" #if IS_ENABLED(CONFIG_TEGRA_HSP) static void nvadsp_dbell_handler(void *data) diff --git a/drivers/platform/tegra/nvadsp/os.c b/drivers/platform/tegra/nvadsp/os.c index ce84cdf8..ec05b69c 100644 --- a/drivers/platform/tegra/nvadsp/os.c +++ b/drivers/platform/tegra/nvadsp/os.c @@ -546,7 +546,7 @@ static void copy_io_in_l(void *to, const void *from, int sz) int i; for (i = 0; i < sz; i += 4) { int val = *(int *)(from + i); - writel(val, to + i); + *(int *)(to + i) = val; } } @@ -969,6 +969,7 @@ EXPORT_SYMBOL(nvadsp_os_load); * 0 - min emc freq * > 0 - expected emc freq at this adsp freq */ +#ifdef CONFIG_TEGRA_ADSP_DFS u32 adsp_to_emc_freq(u32 adspfreq) { /* @@ -980,6 +981,7 @@ u32 adsp_to_emc_freq(u32 adspfreq) else return 0; /* emc min */ } +#endif static int nvadsp_set_ape_emc_freq(struct nvadsp_drv_data *drv_data) { From 11c878a96eec206b86d6edaad34727e6a00c2b36 Mon Sep 17 00:00:00 2001 From: Akash Kollipara Date: Wed, 16 Mar 2022 14:10:57 +0530 Subject: [PATCH 126/138] audio: tegra: nvadsp: Fixed typecast warnings - Fixed typecast warning - Updated datatypes to make it compliant with kernel version > 5.10.0 Bug 3528414 Change-Id: Ief5cac399c35786e3f53dbe3199f70881e730df3 Signed-off-by: Akash Kollipara Reviewed-on: https://git-master.nvidia.com/r/c/linux-nvidia/+/2682497 Reviewed-by: svc-mobile-coverity Reviewed-by: svc-mobile-cert Reviewed-by: Viswanath L Reviewed-by: svc_kernel_abi Reviewed-by: Ketan Patil Reviewed-by: Sharad Gupta GVS: Gerrit_Virtual_Submit --- drivers/platform/tegra/nvadsp/dev.c | 2 +- drivers/platform/tegra/nvadsp/os.c | 9 +++++++-- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/drivers/platform/tegra/nvadsp/dev.c b/drivers/platform/tegra/nvadsp/dev.c index 838eb9f8..72f7fc8b 100644 --- a/drivers/platform/tegra/nvadsp/dev.c +++ b/drivers/platform/tegra/nvadsp/dev.c @@ -420,7 +420,7 @@ static int __init nvadsp_probe(struct platform_device *pdev) goto out; } drv_data->base_regs[iter] = base; - nvadsp_add_load_mappings(res->start, base, + nvadsp_add_load_mappings(res->start, (void __force *)base, resource_size(res)); } diff --git a/drivers/platform/tegra/nvadsp/os.c b/drivers/platform/tegra/nvadsp/os.c index ec05b69c..ea66ab6e 100644 --- a/drivers/platform/tegra/nvadsp/os.c +++ b/drivers/platform/tegra/nvadsp/os.c @@ -545,8 +545,8 @@ static void copy_io_in_l(void *to, const void *from, int sz) { int i; for (i = 0; i < sz; i += 4) { - int val = *(int *)(from + i); - *(int *)(to + i) = val; + u32 val = *(u32 *)(from + i); + writel(val, (void __iomem *)(to + i)); } } @@ -2224,8 +2224,13 @@ static int adsp_create_os_version(struct dentry *adsp_debugfs_root) return 0; } +#if KERNEL_VERSION(5, 10, 0) > LINUX_VERSION_CODE static unsigned int adsp_health_poll(struct file *file, poll_table *wait) +#else +static __poll_t adsp_health_poll(struct file *file, + poll_table *wait) +#endif { struct nvadsp_drv_data *drv_data = platform_get_drvdata(priv.pdev); From b1977843d8f4b5ae122cc87dfce75fe3c1c7ef78 Mon Sep 17 00:00:00 2001 From: Asha Talambedu Date: Thu, 24 Mar 2022 12:18:13 +0530 Subject: [PATCH 127/138] nvadsp: Remove MBOX2 related logic Replacing mbox2 usage with the help of existing mbox used for os related communication with host cpu so that the mbox2 can be used in 128 bit mbox usage Bug 3581290 Change-Id: Iba750b6cedcc2e7b0c5ab1548e3614d77aaeb729 Signed-off-by: Asha Talambedu Reviewed-on: https://git-master.nvidia.com/r/c/linux-nvidia/+/2686392 Reviewed-by: svcacv Reviewed-by: svc_kernel_abi Reviewed-by: Viswanath L Reviewed-by: Dara Ramesh GVS: Gerrit_Virtual_Submit --- drivers/platform/tegra/nvadsp/os.c | 15 +++++---------- drivers/platform/tegra/nvadsp/os.h | 5 +++-- 2 files changed, 8 insertions(+), 12 deletions(-) diff --git a/drivers/platform/tegra/nvadsp/os.c b/drivers/platform/tegra/nvadsp/os.c index ea66ab6e..20221edb 100644 --- a/drivers/platform/tegra/nvadsp/os.c +++ b/drivers/platform/tegra/nvadsp/os.c @@ -86,9 +86,6 @@ /* total number of crashes allowed on adsp */ #define ALLOWED_CRASHES 1 -#define DISABLE_MBOX2_FULL_INT 0x0 -#define ENABLE_MBOX2_FULL_INT 0xFFFFFFFF - #define LOGGER_TIMEOUT 1 /* in ms */ #define ADSP_WFI_TIMEOUT 800 /* in ms */ #define LOGGER_COMPLETE_TIMEOUT 500 /* in ms */ @@ -1701,9 +1698,6 @@ static int nvadsp_setup_os_interrupts(struct nvadsp_os_data *priv) goto free_interrupts; } - writel(DISABLE_MBOX2_FULL_INT, - priv->hwmailbox_base + drv_data->chip_data->hwmb.hwmbox2_reg); - end: return ret; @@ -1972,12 +1966,13 @@ static void __nvadsp_os_stop(bool reload) } #endif - writel(ENABLE_MBOX2_FULL_INT, - priv.hwmailbox_base + drv_data->chip_data->hwmb.hwmbox2_reg); + err = nvadsp_mbox_send(&adsp_com_mbox, + ADSP_OS_STOP, + NVADSP_MBOX_SMSG, true, UINT_MAX); + if (err) + dev_err(dev, "failed to send stop msg to adsp\n"); err = wait_for_completion_timeout(&entered_wfi, msecs_to_jiffies(ADSP_WFI_TIMEOUT)); - writel(DISABLE_MBOX2_FULL_INT, - priv.hwmailbox_base + drv_data->chip_data->hwmb.hwmbox2_reg); /* * ADSP needs to be in WFI/WFE state to properly reset it. diff --git a/drivers/platform/tegra/nvadsp/os.h b/drivers/platform/tegra/nvadsp/os.h index b4bf0b82..2f8e4b89 100644 --- a/drivers/platform/tegra/nvadsp/os.h +++ b/drivers/platform/tegra/nvadsp/os.h @@ -3,7 +3,7 @@ * * A header file containing data structures shared with ADSP OS * - * Copyright (C) 2014-2018 NVIDIA Corporation. All rights reserved. + * Copyright (C) 2014-2022 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 @@ -88,6 +88,7 @@ enum adsp_os_cmd { ADSP_OS_BOOT_COMPLETE, ADSP_OS_SUSPEND, ADSP_OS_RESUME, + ADSP_OS_STOP, }; #if RECORD_STATS @@ -194,4 +195,4 @@ void unload_adsp_module(struct adsp_module *); int allocate_memory_from_adsp(void **, unsigned int); bool is_adsp_dram_addr(u64); int load_adsp_static_apps(void); -#endif /* __TEGRA_NVADSP_OS_H */ \ No newline at end of file +#endif /* __TEGRA_NVADSP_OS_H */ From 2b0e81df35bd8e1efc13069225141c7a9a99cbc1 Mon Sep 17 00:00:00 2001 From: dramesh Date: Wed, 20 Apr 2022 12:41:22 +0530 Subject: [PATCH 128/138] audio: nvadsp: copy shared memory iova address on Non-Secure FW load, incase FW explicitly defined a shared memory region then ensure IOVA data populated correctly into shared_adsp_os_data_iova. Bug 3592962 Change-Id: I6c40f1c691f37fc14f6752759b97942e28c76f43 Signed-off-by: dramesh Reviewed-on: https://git-master.nvidia.com/r/c/linux-nvidia/+/2700117 Reviewed-by: svcacv Reviewed-by: Viswanath L Reviewed-by: svc-mobile-coverity Reviewed-by: svc-mobile-cert Reviewed-by: svc_kernel_abi GVS: Gerrit_Virtual_Submit --- drivers/platform/tegra/nvadsp/os.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/platform/tegra/nvadsp/os.c b/drivers/platform/tegra/nvadsp/os.c index 20221edb..4c00eeb5 100644 --- a/drivers/platform/tegra/nvadsp/os.c +++ b/drivers/platform/tegra/nvadsp/os.c @@ -515,6 +515,7 @@ struct global_sym_info * __maybe_unused find_global_symbol(const char *sym_name) static void *get_mailbox_shared_region(const struct firmware *fw) { struct device *dev; + struct nvadsp_drv_data *drv_data = platform_get_drvdata(priv.pdev); struct elf32_shdr *shdr; int addr; int size; @@ -535,6 +536,7 @@ static void *get_mailbox_shared_region(const struct firmware *fw) dev_dbg(dev, "the shared section is present at 0x%x\n", shdr->sh_addr); addr = shdr->sh_addr; size = shdr->sh_size; + drv_data->shared_adsp_os_data_iova = addr; return nvadsp_da_to_va_mappings(addr, size); } From 3325cdc0cb1de724895e021a1627edfc0e752b74 Mon Sep 17 00:00:00 2001 From: Viswanath L Date: Thu, 31 Mar 2022 11:42:03 +0530 Subject: [PATCH 129/138] nvadsp: Disable AMC error WAR for T239 Spurious AMC error when accessing address < 0x1000 is fixed in T239, hence the WAR is disabled for "nvidia,tegra239-adsp". Bug 200747371 Bug 3580398 Change-Id: I943f4c4c2fa8250b15e6ee73607bb6ecb9d760a3 Signed-off-by: Viswanath L Reviewed-on: https://git-master.nvidia.com/r/c/linux-nvidia/+/2715544 (cherry picked from commit 095ceb378a0e3aa9e54cc1742c59adf897bc1b14) Reviewed-on: https://git-master.nvidia.com/r/c/linux-nvidia/+/2690528 Reviewed-by: svcacv Reviewed-by: svc_kernel_abi Reviewed-by: Sharad Gupta GVS: Gerrit_Virtual_Submit --- drivers/platform/tegra/nvadsp/dev.c | 34 +++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/drivers/platform/tegra/nvadsp/dev.c b/drivers/platform/tegra/nvadsp/dev.c index 72f7fc8b..e55fe57a 100644 --- a/drivers/platform/tegra/nvadsp/dev.c +++ b/drivers/platform/tegra/nvadsp/dev.c @@ -593,6 +593,37 @@ static struct nvadsp_chipdata tegrat18x_adsp_chipdata = { .amc_err_war = true, }; +static struct nvadsp_chipdata tegra239_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, + .empty_int_ie = 0x8, + }, + .adsp_shared_mem_hwmbox = 0x18000, /* HWMBOX3 */ + .adsp_thread_hwmbox = 0x20000, /* HWMBOX4 */ + .adsp_os_config_hwmbox = 0X28000, /* HWMBOX5 */ + .adsp_state_hwmbox = 0x30000, /* HWMBOX6 */ + .adsp_irq_hwmbox = 0x38000, /* HWMBOX7 */ + .acast_init = nvadsp_acast_t18x_init, + .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, + + .amc_err_war = false, +}; + static const struct of_device_id nvadsp_of_match[] = { { .compatible = "nvidia,tegra210-adsp", @@ -600,6 +631,9 @@ static const struct of_device_id nvadsp_of_match[] = { }, { .compatible = "nvidia,tegra18x-adsp", .data = &tegrat18x_adsp_chipdata, + }, { + .compatible = "nvidia,tegra239-adsp", + .data = &tegra239_adsp_chipdata, }, { }, }; From 9bfe26c72d9865d4921f279059b17458beb5fbc2 Mon Sep 17 00:00:00 2001 From: Viswanath L Date: Mon, 30 May 2022 12:57:25 +0000 Subject: [PATCH 130/138] nvadsp: Add chip ID major rev in T239 Extend chip ID in T239 with major revision so that the value (=0x239) is distinct from T234. Bug 200688972 Bug 3660611 Change-Id: If00da32ade7aa88f3dc6ba7f59038e5b4710a677 Signed-off-by: Viswanath L Reviewed-on: https://git-master.nvidia.com/r/c/linux-nvidia/+/2720497 (cherry picked from commit 246f43a203714c97b0df7e22e82d976c126b8267) Reviewed-on: https://git-master.nvidia.com/r/c/linux-nvidia/+/2725862 Reviewed-by: Sharad Gupta Reviewed-by: svc_kernel_abi GVS: Gerrit_Virtual_Submit --- drivers/platform/tegra/nvadsp/dev.c | 3 +++ drivers/platform/tegra/nvadsp/dev.h | 1 + drivers/platform/tegra/nvadsp/os.c | 11 +++++++++-- 3 files changed, 13 insertions(+), 2 deletions(-) diff --git a/drivers/platform/tegra/nvadsp/dev.c b/drivers/platform/tegra/nvadsp/dev.c index e55fe57a..9c63befc 100644 --- a/drivers/platform/tegra/nvadsp/dev.c +++ b/drivers/platform/tegra/nvadsp/dev.c @@ -622,6 +622,9 @@ static struct nvadsp_chipdata tegra239_adsp_chipdata = { .end_irq = INT_T18x_AGIC_END, .amc_err_war = false, + + /* Populate Chip ID Major Revision as well */ + .chipid_ext = true, }; static const struct of_device_id nvadsp_of_match[] = { diff --git a/drivers/platform/tegra/nvadsp/dev.h b/drivers/platform/tegra/nvadsp/dev.h index e82c6ba0..bba98892 100644 --- a/drivers/platform/tegra/nvadsp/dev.h +++ b/drivers/platform/tegra/nvadsp/dev.h @@ -164,6 +164,7 @@ struct nvadsp_chipdata { int end_irq; bool amc_err_war; + bool chipid_ext; }; struct nvadsp_drv_data { diff --git a/drivers/platform/tegra/nvadsp/os.c b/drivers/platform/tegra/nvadsp/os.c index 4c00eeb5..65753962 100644 --- a/drivers/platform/tegra/nvadsp/os.c +++ b/drivers/platform/tegra/nvadsp/os.c @@ -134,6 +134,10 @@ struct nvadsp_mappings { int len; }; +#if KERNEL_VERSION(4, 15, 0) > LINUX_VERSION_CODE +static inline u8 tegra_get_major_rev(void) { return 0; } +#endif + static struct nvadsp_mappings adsp_map[NM_LOAD_MAPPINGS]; static int map_idx; static struct nvadsp_mbox adsp_com_mbox; @@ -792,7 +796,7 @@ static void nvadsp_set_shared_mem(struct platform_device *pdev, struct nvadsp_drv_data *drv_data = platform_get_drvdata(pdev); struct device *dev = &pdev->dev; struct nvadsp_os_args *os_args; - u8 chip_id; + u32 chip_id; shared_mem->os_args.dynamic_app_support = dynamic_app_support; /* set logger strcuture with required properties */ @@ -801,7 +805,10 @@ static void nvadsp_set_shared_mem(struct platform_device *pdev, priv.logger.dev = dev; priv.adsp_os_fw_loaded = true; - chip_id = tegra_get_chip_id(); + chip_id = (u32)tegra_get_chip_id(); + if (drv_data->chip_data->chipid_ext) + chip_id = (chip_id << 4) | tegra_get_major_rev(); + os_args = &shared_mem->os_args; /* Chip id info is communicated twice to ADSP * TODO::clean up the redundant comm. From e6c266d6bfea429ea8bdb989640d7f0d80644a5a Mon Sep 17 00:00:00 2001 From: Viswanath L Date: Fri, 22 Apr 2022 09:37:28 +0000 Subject: [PATCH 131/138] nvadsp: Add support for multiple FW Support is added for launching multiple FW on ADSP (multiple AMP, or combination of SMP and AMP). CONFIG_TEGRA_ADSP_MULTIPLE_FW will need to be enabled. Shared memory is communicated via the respective AHSP for the core. For front door boot MB2 would have loaded the FW for all the cores. Carveout allocation should be set as necessary (4x4 MB = 16 MB). Backdoor boot via SMMU is also supported. Individual core FW for AMP cores are loaded first, followed by core-0 FW at the end. CCPLEX<->ADSP communication is limited to AHSP0, so only core-0 (SMP or AMP) will be accessible for command interface or signalling. Bug 200745833 Change-Id: Ibfddd463de1ecada6fd47944ca12ef0444cd269f Signed-off-by: Viswanath L Reviewed-on: https://git-master.nvidia.com/r/c/linux-nvidia/+/2722603 (cherry picked from commit 1e08636df25690017b8d7e1818eda210fef6da8b) Reviewed-on: https://git-master.nvidia.com/r/c/linux-nvidia/+/2701452 Tested-by: mobile promotions Reviewed-by: mobile promotions --- drivers/platform/tegra/nvadsp/os.c | 182 ++++++++++++++++++++++++++++- 1 file changed, 181 insertions(+), 1 deletion(-) diff --git a/drivers/platform/tegra/nvadsp/os.c b/drivers/platform/tegra/nvadsp/os.c index 65753962..b5ed6216 100644 --- a/drivers/platform/tegra/nvadsp/os.c +++ b/drivers/platform/tegra/nvadsp/os.c @@ -929,6 +929,167 @@ end: } +#ifdef CONFIG_TEGRA_ADSP_MULTIPLE_FW + +#define MFW_MAX_OTHER_CORES 3 +dma_addr_t mfw_smem_iova[MFW_MAX_OTHER_CORES]; +void *mfw_hsp_va[MFW_MAX_OTHER_CORES]; + +static int nvadsp_load_multi_fw(struct platform_device *pdev) +{ + struct nvadsp_drv_data *drv_data = platform_get_drvdata(pdev); + struct device *dev = &pdev->dev; + int i, j, ret; + void *dram_va, *hsp_va; + dma_addr_t shrd_mem_iova; + struct device_node *hsp_node; + struct resource hsp_inst; + u32 hsp_int, hsp_int_targ; + + hsp_node = of_get_child_by_name(dev->of_node, "ahsp_sm_multi"); + if (!hsp_node) { + dev_err(dev, "missing ahsp_sm addr\n"); + return -ENOENT; + } + + for (i = 0; !of_address_to_resource(hsp_node, i, &hsp_inst); i++) { + if (i >= MFW_MAX_OTHER_CORES) { + dev_err(dev, "core out of bound\n"); + break; + } + + if (drv_data->adsp_os_secload) { + /* For front door boot MB2 would have loaded + * the FW for all the cores; only shared + * memory neeeds to be allocated and set + */ + size_t size = drv_data->adsp_mem[ACSR_SIZE]; + + dram_va = nvadsp_alloc_coherent( + size, &shrd_mem_iova, GFP_KERNEL); + if (!dram_va) { + dev_err(dev, + "mem alloc failed for adsp %d\n", i); + continue; + } + } else { + const struct firmware *fw; + const char *adsp_elf; + u32 os_mem, os_size; + + ret = of_property_read_string_index( + dev->of_node, "nvidia,adsp_elf_multi", + i, &adsp_elf); + if (ret) { + dev_err(dev, "err reading adsp FW %d: %d\n", + (i + 1), ret); + continue; + } + + if (!strcmp(adsp_elf, "")) + continue; + + ret = request_firmware(&fw, adsp_elf, dev); + if (ret < 0) { + dev_err(dev, "request FW failed for %s: %d\n", + adsp_elf, ret); + continue; + } + + os_size = drv_data->adsp_mem[ADSP_OS_SIZE]; + os_mem = drv_data->adsp_mem[ADSP_OS_ADDR] + + ((i + 1) * os_size); + +#if defined(CONFIG_TEGRA_NVADSP_ON_SMMU) + dram_va = nvadsp_dma_alloc_and_map_at(pdev, + (size_t)os_size, (dma_addr_t)os_mem, + GFP_KERNEL); + if (!dram_va) { + dev_err(dev, + "dma_alloc failed for 0x%x\n", os_mem); + continue; + } +#else + dram_va = ioremap((phys_addr_t)os_mem, (size_t)os_size); + if (!dram_va) { + dev_err(dev, + "remap failed for addr 0x%x\n", os_mem); + continue; + } +#endif + + nvadsp_add_load_mappings((phys_addr_t)os_mem, + dram_va, (size_t)os_size); + + dev_info(dev, "Loading ADSP OS firmware %s\n", adsp_elf); + ret = nvadsp_os_elf_load(fw); + if (ret) { + dev_err(dev, "failed to load %s\n", adsp_elf); + continue; + } + + /* Shared mem is at the start of OS memory */ + shrd_mem_iova = (dma_addr_t)os_mem; + } + + nvadsp_set_shared_mem(pdev, dram_va, 0); + + /* Store shared mem IOVA for writing into MBOX (for ADSP) */ + hsp_va = devm_ioremap_resource(dev, &hsp_inst); + if (IS_ERR(hsp_va)) { + dev_err(dev, "ioremap failed for HSP %d\n", (i + 1)); + continue; + } + mfw_smem_iova[i] = shrd_mem_iova; + mfw_hsp_va[i] = hsp_va; + + /* + * Interrupt routing of AHSP1-3 is only for the + * sake of completion; CCPLEX<->ADSP communication + * is limited to AHSP0, i.e. ADSP core-0 + */ + for (j = 0; j < 8; j += 2) { + if (of_property_read_u32_index(hsp_node, + "nvidia,ahsp_sm_interrupts", + (i * 8) + j, &hsp_int)) { + dev_err(dev, + "no HSP int config for core %d\n", + (i + 1)); + break; + } + if (of_property_read_u32_index(hsp_node, + "nvidia,ahsp_sm_interrupts", + (i * 8) + j + 1, &hsp_int_targ)) { + dev_err(dev, + "no HSP int_targ config for core %d\n", + (i + 1)); + break; + } + + /* + * DT definition decrements SPI IRQs + * by 32, so restore the same here + */ + ret = tegra_agic_route_interrupt(hsp_int + 32, + hsp_int_targ); + if (ret) { + dev_err(dev, + "HSP routing for core %d failed: %d\n", + (i + 1), ret); + break; + } + } + + if (j == 8) + dev_info(dev, "Setup done for core %d FW\n", (i + 1)); + } + + of_node_put(hsp_node); + + return 0; +} +#endif // CONFIG_TEGRA_ADSP_MULTIPLE_FW + int nvadsp_os_load(void) { struct nvadsp_drv_data *drv_data; @@ -947,6 +1108,11 @@ int nvadsp_os_load(void) drv_data = platform_get_drvdata(priv.pdev); dev = &priv.pdev->dev; +#ifdef CONFIG_TEGRA_ADSP_MULTIPLE_FW + dev_info(dev, "Loading multiple ADSP FW....\n"); + nvadsp_load_multi_fw(priv.pdev); +#endif // CONFIG_TEGRA_ADSP_MULTIPLE_FW + if (drv_data->adsp_os_secload) { dev_info(dev, "ADSP OS firmware already loaded\n"); ret = __nvadsp_os_secload(priv.pdev); @@ -1795,10 +1961,24 @@ int nvadsp_os_start(void) goto unlock; if (cold_start) { - if (drv_data->chip_data->adsp_shared_mem_hwmbox != 0) + if (drv_data->chip_data->adsp_shared_mem_hwmbox != 0) { +#ifdef CONFIG_TEGRA_ADSP_MULTIPLE_FW + int i; + for (i = 0; i < MFW_MAX_OTHER_CORES; i++) { + if (mfw_hsp_va[i]) { + writel((uint32_t)mfw_smem_iova[i], + mfw_hsp_va[i] + + drv_data->chip_data-> + adsp_shared_mem_hwmbox + ); + } + } +#endif // CONFIG_TEGRA_ADSP_MULTIPLE_FW + hwmbox_writel( (uint32_t)drv_data->shared_adsp_os_data_iova, drv_data->chip_data->adsp_shared_mem_hwmbox); + } if (!is_tegra_hypervisor_mode() && drv_data->chip_data->adsp_os_config_hwmbox != 0) { From 4bcb44fbf9bcf7cf87bcfaa7a01337db4fb41306 Mon Sep 17 00:00:00 2001 From: Niranjan Dighe Date: Tue, 30 Nov 2021 10:58:45 +0530 Subject: [PATCH 132/138] nvadsp: adspff: Handle kthread creation failure kthread_create call may return ERR_PTR(-ENOMEM) or ERR_PTR(-EINTR) in case of memory allocation failure or if the newly created thread receives a fatal signal. Handle the conditions by returning and not attempting to wake up the thread. Bug 200688338 Change-Id: Ibde3a847202476f52fca38d145091c540ace19c4 Signed-off-by: Niranjan Dighe Reviewed-on: https://git-master.nvidia.com/r/c/linux-nvidia/+/2633986 (cherry picked from commit 4d382d925befd445cddfc10c556d7c06b980b920) Reviewed-on: https://git-master.nvidia.com/r/c/linux-nvidia/+/2729780 Reviewed-by: svcacv Reviewed-by: svc-mobile-coverity Reviewed-by: svc-mobile-cert Reviewed-by: svc_kernel_abi Reviewed-by: Nitin Pai GVS: Gerrit_Virtual_Submit Tested-by: Uday Gupta --- drivers/platform/tegra/nvadsp/adspff.c | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/drivers/platform/tegra/nvadsp/adspff.c b/drivers/platform/tegra/nvadsp/adspff.c index 11c10959..cbc13fcd 100644 --- a/drivers/platform/tegra/nvadsp/adspff.c +++ b/drivers/platform/tegra/nvadsp/adspff.c @@ -663,6 +663,16 @@ int adspff_init(struct platform_device *pdev) return -1; } + adspff_kthread = kthread_create(adspff_kthread_fn, + NULL, "adspp_kthread"); + if ((adspff_kthread == ERR_PTR(-ENOMEM)) || + (adspff_kthread == ERR_PTR(-EINTR))) { + pr_err("adspff kthread_create failed, error = %s\n", + (adspff_kthread == ERR_PTR(-ENOMEM)) ? + "-ENOMEM" : "-EINTR"); + return -1; + } + adspff = ADSPFF_SHARED_STATE(app_info->mem.shared); ret = nvadsp_mbox_open(&rx_mbox, &adspff->mbox_id, @@ -684,10 +694,7 @@ int adspff_init(struct platform_device *pdev) INIT_LIST_HEAD(&adspff_kthread_msgq_head); INIT_LIST_HEAD(&file_list); - // kthread inIt init_waitqueue_head(&wait_queue); - adspff_kthread = kthread_create(adspff_kthread_fn, - NULL, "adspp_kthread"); #if KERNEL_VERSION(5, 9, 0) > LINUX_VERSION_CODE sched_setscheduler(adspff_kthread, SCHED_FIFO, ¶m); From b7dc4e8302139a60913b843199961fd4c734b149 Mon Sep 17 00:00:00 2001 From: Uday Gupta Date: Sun, 12 Sep 2021 16:45:19 +0530 Subject: [PATCH 133/138] nvadsp: Add more error logs and fix crash - Change adds more error logs in case of APP init failure. - Also potentially fixes the crash issue Bug 3374437 Bug 3498407 Change-Id: If6baf6e2e11250815cff4a6b8a2abe553e893e34 Signed-off-by: Uday Gupta Reviewed-on: https://git-master.nvidia.com/r/c/linux-nvidia/+/2593021 (cherry picked from commit b051fae2faffafbffed99b94914bbd9bc370240f) Reviewed-on: https://git-master.nvidia.com/r/c/linux-nvidia/+/2731778 Reviewed-by: svc_kernel_abi Reviewed-by: Swati Sachdeva Reviewed-by: Nitin Pai GVS: Gerrit_Virtual_Submit --- drivers/platform/tegra/nvadsp/adsp_lpthread.c | 4 ++-- drivers/platform/tegra/nvadsp/adspff.c | 2 +- drivers/platform/tegra/nvadsp/app.c | 13 ++++++++++--- drivers/platform/tegra/nvadsp/mailbox.c | 8 ++++++-- 4 files changed, 19 insertions(+), 8 deletions(-) diff --git a/drivers/platform/tegra/nvadsp/adsp_lpthread.c b/drivers/platform/tegra/nvadsp/adsp_lpthread.c index 5caa72ef..f71122ec 100644 --- a/drivers/platform/tegra/nvadsp/adsp_lpthread.c +++ b/drivers/platform/tegra/nvadsp/adsp_lpthread.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016-2020, NVIDIA CORPORATION. All rights reserved. + * Copyright (c) 2016-2022, NVIDIA CORPORATION. All rights reserved. * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU General Public License, @@ -68,7 +68,7 @@ int adsp_lpthread_init(bool is_adsp_suspended) return -1; app_info = nvadsp_app_init(handle, NULL); - if (!app_info) { + if (IS_ERR_OR_NULL(app_info)) { pr_err("unable to init app adsp_lpthread\n"); return -1; } diff --git a/drivers/platform/tegra/nvadsp/adspff.c b/drivers/platform/tegra/nvadsp/adspff.c index cbc13fcd..acca6cf4 100644 --- a/drivers/platform/tegra/nvadsp/adspff.c +++ b/drivers/platform/tegra/nvadsp/adspff.c @@ -658,7 +658,7 @@ int adspff_init(struct platform_device *pdev) return -ENOENT; app_info = nvadsp_app_init(handle, NULL); - if (!app_info) { + if (IS_ERR_OR_NULL(app_info)) { pr_err("unable to init app adspff\n"); return -1; } diff --git a/drivers/platform/tegra/nvadsp/app.c b/drivers/platform/tegra/nvadsp/app.c index 38dac101..911f24a1 100644 --- a/drivers/platform/tegra/nvadsp/app.c +++ b/drivers/platform/tegra/nvadsp/app.c @@ -631,15 +631,21 @@ nvadsp_app_info_t __must_check *nvadsp_app_init(nvadsp_app_handle_t handle, drv_data = platform_get_drvdata(priv.pdev); - if (!drv_data->adsp_os_running) + if (!drv_data->adsp_os_running) { + pr_err("ADSP is not running\n"); goto err; + } - if (IS_ERR_OR_NULL(handle)) + if (IS_ERR_OR_NULL(handle)) { + pr_err("ADSP APP handle is NULL\n"); goto err; + } message = kzalloc(sizeof(*message), GFP_KERNEL); - if (!message) + if (!message) { + pr_err("Failed to allocate memory for ADSP msg\n"); goto err; + } shared_mem = drv_data->shared_adsp_os_data; msg_pool = &shared_mem->app_shared_msg_pool; @@ -648,6 +654,7 @@ nvadsp_app_info_t __must_check *nvadsp_app_init(nvadsp_app_handle_t handle, app = create_app_instance(handle, args, &data->app_init, NULL, 0); if (IS_ERR_OR_NULL(app)) { + pr_err("Failed to create APP instance\n"); kfree(message); goto err; } diff --git a/drivers/platform/tegra/nvadsp/mailbox.c b/drivers/platform/tegra/nvadsp/mailbox.c index 3b197a6e..94afbd43 100644 --- a/drivers/platform/tegra/nvadsp/mailbox.c +++ b/drivers/platform/tegra/nvadsp/mailbox.c @@ -1,7 +1,7 @@ /* * ADSP mailbox manager * - * Copyright (c) 2014-2018, NVIDIA CORPORATION. All rights reserved. + * Copyright (c) 2014-2022, NVIDIA CORPORATION. All rights reserved. * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU General Public License, @@ -219,11 +219,13 @@ status_t nvadsp_mbox_send(struct nvadsp_mbox *mbox, uint32_t data, int ret = 0; if (!nvadsp_drv_data) { + pr_err("ADSP drv_data is NULL\n"); ret = -ENOSYS; goto out; } if (!mbox) { + pr_err("ADSP MBOX is NULL\n"); ret = -EINVAL; goto out; } @@ -239,9 +241,11 @@ status_t nvadsp_mbox_send(struct nvadsp_mbox *mbox, uint32_t data, &nvadsp_drv_data->hwmbox_send_queue.comp, msecs_to_jiffies(timeout)); if (ret) { + pr_warn("ADSP HWMBOX send retry\n"); block = false; goto retry; } else { + pr_err("ADSP wait for completion timed out\n"); ret = -ETIME; goto out; } @@ -250,7 +254,7 @@ status_t nvadsp_mbox_send(struct nvadsp_mbox *mbox, uint32_t data, data, ret); } } else if (ret) { - pr_debug("Failed to enqueue data 0x%x. ret: %d\n", data, ret); + pr_warn("Failed to enqueue data 0x%x. ret: %d\n", data, ret); goto out; } out: From 913a409fe5ae318afa5127c52a8c967aef2cc206 Mon Sep 17 00:00:00 2001 From: Uday Gupta Date: Thu, 30 Jun 2022 14:37:32 +0000 Subject: [PATCH 134/138] nvadsp: Don't use ICC in HV - tegra_icc node is disabled in HV config - In HV config do not call icc api to register with BW Manager. Bug 3602082 Change-Id: Ic6d476e3e605fe6b5addc6439045920a10b34e48 Signed-off-by: Uday Gupta Reviewed-on: https://git-master.nvidia.com/r/c/linux-nvidia/+/2737892 Reviewed-by: svcacv Reviewed-by: svc_kernel_abi Reviewed-by: Niranjan Dighe Reviewed-by: Dipesh Gandhi Reviewed-by: Nitin Pai GVS: Gerrit_Virtual_Submit --- drivers/platform/tegra/nvadsp/dev.c | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/drivers/platform/tegra/nvadsp/dev.c b/drivers/platform/tegra/nvadsp/dev.c index 9c63befc..6f8f8abf 100644 --- a/drivers/platform/tegra/nvadsp/dev.c +++ b/drivers/platform/tegra/nvadsp/dev.c @@ -172,16 +172,18 @@ static void nvadsp_bw_register(struct nvadsp_drv_data *drv_data) break; default: #if KERNEL_VERSION(5, 9, 0) <= LINUX_VERSION_CODE - /* Interconnect Support */ + if (!is_tegra_hypervisor_mode()) { + /* Interconnect Support */ #ifdef CONFIG_ARCH_TEGRA_23x_SOC - drv_data->icc_path_handle = icc_get(dev, TEGRA_ICC_APE, + drv_data->icc_path_handle = icc_get(dev, TEGRA_ICC_APE, TEGRA_ICC_PRIMARY); #endif - if (IS_ERR(drv_data->icc_path_handle)) { - dev_err(dev, - "%s: Failed to register Interconnect. err=%ld\n", - __func__, PTR_ERR(drv_data->icc_path_handle)); - drv_data->icc_path_handle = NULL; + if (IS_ERR(drv_data->icc_path_handle)) { + dev_err(dev, + "%s: Failed to register Interconnect err=%ld\n", + __func__, PTR_ERR(drv_data->icc_path_handle)); + drv_data->icc_path_handle = NULL; + } } #endif break; From 3a6d714aa732c5f8019ce36e1a6ae0313214da78 Mon Sep 17 00:00:00 2001 From: Akash Kollipara Date: Mon, 11 Apr 2022 13:31:40 +0530 Subject: [PATCH 135/138] nvadsp: Added new commands support to adsp console - Added suspend, reusme and stop abilities to adsp console Bug 3596981 Change-Id: I867a85130bb2abf7feb5a3a78ebb2b343272a32a Signed-off-by: Akash Kollipara Reviewed-on: https://git-master.nvidia.com/r/c/linux-nvidia/+/2723711 (cherry picked from commit 046f4f8874a1fc8c7af88c44ad08114e489f3302) Reviewed-on: https://git-master.nvidia.com/r/c/linux-nvidia/+/2695477 Reviewed-by: svc-mobile-coverity Reviewed-by: svc-mobile-cert Reviewed-by: Viswanath L Reviewed-by: Ravindra Lokhande GVS: Gerrit_Virtual_Submit --- .../platform/tegra/nvadsp/adsp_console_dbfs.c | 20 ++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/drivers/platform/tegra/nvadsp/adsp_console_dbfs.c b/drivers/platform/tegra/nvadsp/adsp_console_dbfs.c index ee3a270b..c45bd112 100644 --- a/drivers/platform/tegra/nvadsp/adsp_console_dbfs.c +++ b/drivers/platform/tegra/nvadsp/adsp_console_dbfs.c @@ -3,7 +3,7 @@ * * adsp mailbox console driver * - * Copyright (C) 2014-2020, NVIDIA Corporation. All rights reserved. + * Copyright (C) 2014-2022, 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 @@ -161,6 +161,7 @@ adsp_consol_ioctl(struct file *f, unsigned int cmd, unsigned long arg) return -EFAULT; if ((_IOC_NR(cmd) != _IOC_NR(ADSP_CNSL_LOAD)) && + (_IOC_NR(cmd) != _IOC_NR(ADSP_CNSL_RESUME)) && (!drv_data->adsp_os_running)) { dev_info(dev, "adsp_consol: os not running."); return -EPERM; @@ -197,6 +198,23 @@ adsp_consol_ioctl(struct file *f, unsigned int cmd, unsigned long arg) pr_err("adsp_consol: Failed to init adsp_consol send mailbox"); memset(mbox, 0, sizeof(struct nvadsp_mbox)); break; + case _IOC_NR(ADSP_CNSL_SUSPEND): + ret = nvadsp_os_suspend(); + if (ret) + dev_info(dev, "adsp_consol: OS Suspend Failed."); + break; + case _IOC_NR(ADSP_CNSL_STOP): + nvadsp_os_stop(); + break; + case _IOC_NR(ADSP_CNSL_RESUME): + if (!drv_data->adsp_os_suspended) { + dev_info(dev, "adsp_consol: OS is not suspended to perform resume."); + break; + } + ret = nvadsp_os_start(); + if (ret) + dev_info(dev, "adsp_consol: OS Resume Failed."); + break; case _IOC_NR(ADSP_CNSL_RUN_APP): if (!ACCESS_OK(uarg, sizeof(struct adsp_consol_run_app_arg_t))) return -EACCES; From 358498746ad741273f378cf904eceec7a8d048ac Mon Sep 17 00:00:00 2001 From: Akash Kollipara Date: Fri, 1 Jul 2022 15:00:25 +0530 Subject: [PATCH 136/138] nvadsp: app: Dispatch apps on sec-cores - Updated nvadsp_run_app to accept core ID for pinning app - For legacy api, apps are pinned to core 0 Bug 3664974 Signed-off-by: Akash Kollipara Change-Id: I8f3bcc6a77bb2f912675cfd5af131cdbd36c417c Reviewed-on: https://git-master.nvidia.com/r/c/linux-nvidia/+/2738421 (cherry picked from commit 12bcba564e9a570218eab944d4ee8e64074c6be4) Reviewed-on: https://git-master.nvidia.com/r/c/linux-nvidia/+/2747911 Reviewed-by: svc-mobile-coverity Reviewed-by: svc-mobile-cert Reviewed-by: Viswanath L Reviewed-by: Ravindra Lokhande GVS: Gerrit_Virtual_Submit --- drivers/platform/tegra/nvadsp/adsp_console_dbfs.c | 4 +++- drivers/platform/tegra/nvadsp/adsp_shared_struct.h | 3 ++- drivers/platform/tegra/nvadsp/app.c | 13 +++++++++---- 3 files changed, 14 insertions(+), 6 deletions(-) diff --git a/drivers/platform/tegra/nvadsp/adsp_console_dbfs.c b/drivers/platform/tegra/nvadsp/adsp_console_dbfs.c index c45bd112..ff5d6cdb 100644 --- a/drivers/platform/tegra/nvadsp/adsp_console_dbfs.c +++ b/drivers/platform/tegra/nvadsp/adsp_console_dbfs.c @@ -224,13 +224,15 @@ adsp_consol_ioctl(struct file *f, unsigned int cmd, unsigned long arg) ret = -EACCES; break; } + + dev_info(dev, "Core ID: %d\n", app_args.core_id); app_args.app_name[NVADSP_NAME_SZ_MAX] = '\0'; #ifdef USE_RUN_APP_API app_args.ctx2 = (uint64_t)nvadsp_run_app(NULL, app_args.app_name, (nvadsp_app_args_t *)&app_args.args[0], - NULL, 0, true); + NULL, 0, app_args.core_id, true); if (!app_args.ctx2) { dev_info(dev, "adsp_consol: unable to run %s\n", app_args.app_name); diff --git a/drivers/platform/tegra/nvadsp/adsp_shared_struct.h b/drivers/platform/tegra/nvadsp/adsp_shared_struct.h index 099173e1..b7615a7e 100644 --- a/drivers/platform/tegra/nvadsp/adsp_shared_struct.h +++ b/drivers/platform/tegra/nvadsp/adsp_shared_struct.h @@ -3,7 +3,7 @@ * * A header file containing shared data structures shared with ADSP OS * - * Copyright (C) 2015-2021 NVIDIA Corporation. All rights reserved. + * Copyright (C) 2015-2022 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 @@ -64,6 +64,7 @@ struct run_app_instance_data { struct app_mem_size mem_size; nvadsp_app_args_t app_args; uint32_t stack_size; + uint32_t core_id; uint32_t message; } __packed; diff --git a/drivers/platform/tegra/nvadsp/app.c b/drivers/platform/tegra/nvadsp/app.c index 911f24a1..3cfb5f37 100644 --- a/drivers/platform/tegra/nvadsp/app.c +++ b/drivers/platform/tegra/nvadsp/app.c @@ -548,7 +548,8 @@ static void fill_app_instance_data(nvadsp_app_info_t *app, static nvadsp_app_info_t *create_app_instance(nvadsp_app_handle_t handle, nvadsp_app_args_t *app_args, struct run_app_instance_data *data, - app_complete_status_notifier notifier, uint32_t stack_size) + app_complete_status_notifier notifier, uint32_t stack_size, + uint32_t core_id) { struct nvadsp_app_service *ser = (void *)handle; struct device *dev = &priv.pdev->dev; @@ -581,6 +582,8 @@ static nvadsp_app_info_t *create_app_instance(nvadsp_app_handle_t handle, /* assign the stack that is needed by the app */ data->stack_size = stack_size; + /* assign the core that is needed by the app */ + data->core_id = core_id; /* set the state to INITIALIZED. No need to do it in a spin lock */ state = (int *)&app->state; @@ -652,7 +655,8 @@ nvadsp_app_info_t __must_check *nvadsp_app_init(nvadsp_app_handle_t handle, msgq_send = &msg_pool->app_loader_send_message.msgq; data = &message->data; - app = create_app_instance(handle, args, &data->app_init, NULL, 0); + /* Pinning app to core 0 by default */ + app = create_app_instance(handle, args, &data->app_init, NULL, 0, 0); if (IS_ERR_OR_NULL(app)) { pr_err("Failed to create APP instance\n"); kfree(message); @@ -758,7 +762,8 @@ EXPORT_SYMBOL(nvadsp_app_start); nvadsp_app_info_t *nvadsp_run_app(nvadsp_os_handle_t os_handle, const char *appfile, nvadsp_app_args_t *app_args, - app_complete_status_notifier notifier, uint32_t stack_sz, bool block) + app_complete_status_notifier notifier, uint32_t stack_sz, + uint32_t core_id, bool block) { union app_loader_message message = {}; nvadsp_app_handle_t service_handle; @@ -791,7 +796,7 @@ nvadsp_app_info_t *nvadsp_run_app(nvadsp_os_handle_t os_handle, } info = create_app_instance(service_handle, app_args, - &data->app_init, notifier, stack_sz); + &data->app_init, notifier, stack_sz, core_id); if (IS_ERR_OR_NULL(info)) { dev_err(dev, "unable to create instance for app %s\n", appfile); goto end; From 83ee116f728cdd5ebd086a4830189e2665209e3a Mon Sep 17 00:00:00 2001 From: Viswanath L Date: Thu, 11 Aug 2022 10:31:34 +0000 Subject: [PATCH 137/138] nvadsp: Check ADSP_STATUS before reset in suspend Check L2_IDLE and L2_CLKSTOPPED in AMISC_ADSP_STATUS_0 before asserting ADSP reset in suspend flow. Standby mode in ADSP L2CC Power Control register should be enabled so that L2C clock is stopped after all cores enter WFI. Bug 3700834 Change-Id: I4b89771968dd8b72dfea9920c9125562f8dfa92d Signed-off-by: Viswanath L Reviewed-on: https://git-master.nvidia.com/r/c/linux-nvidia/+/2759621 (cherry picked from commit 7b819575670eb1e06e510a840977101cae3367be) Reviewed-on: https://git-master.nvidia.com/r/c/linux-nvidia/+/2765174 Reviewed-by: Uday Gupta Reviewed-by: Sharad Gupta Tested-by: Uday Gupta GVS: Gerrit_Virtual_Submit --- drivers/platform/tegra/nvadsp/amisc.h | 23 +++++++++++++++++++++++ drivers/platform/tegra/nvadsp/os.c | 24 +++++++++++++++++++++++- 2 files changed, 46 insertions(+), 1 deletion(-) create mode 100644 drivers/platform/tegra/nvadsp/amisc.h diff --git a/drivers/platform/tegra/nvadsp/amisc.h b/drivers/platform/tegra/nvadsp/amisc.h new file mode 100644 index 00000000..b127bb5a --- /dev/null +++ b/drivers/platform/tegra/nvadsp/amisc.h @@ -0,0 +1,23 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * amisc.h - AMISC register access + * + * Copyright (c) 2022 NVIDIA CORPORATION. All rights reserved. + * + */ + +#ifndef __TEGRA_NVADSP_AMISC_H +#define __TEGRA_NVADSP_AMISC_H + +#include "dev.h" + +#define AMISC_ADSP_STATUS (0x14) +#define AMISC_ADSP_L2_CLKSTOPPED (1 << 30) +#define AMISC_ADSP_L2_IDLE (1 << 31) + +static inline u32 amisc_readl(struct nvadsp_drv_data *drv_data, u32 reg) +{ + return readl(drv_data->base_regs[AMISC] + reg); +} + +#endif /* __TEGRA_NVADSP_AMISC_H */ diff --git a/drivers/platform/tegra/nvadsp/os.c b/drivers/platform/tegra/nvadsp/os.c index b5ed6216..df6c2beb 100644 --- a/drivers/platform/tegra/nvadsp/os.c +++ b/drivers/platform/tegra/nvadsp/os.c @@ -50,6 +50,7 @@ #include +#include "amisc.h" #include "ape_actmon.h" #include "os.h" #include "dev.h" @@ -2081,7 +2082,8 @@ static int __nvadsp_os_suspend(void) { struct device *dev = &priv.pdev->dev; struct nvadsp_drv_data *drv_data; - int ret; + int ret, cnt = 0; + u32 adsp_status; drv_data = platform_get_drvdata(priv.pdev); @@ -2112,6 +2114,26 @@ static int __nvadsp_os_suspend(void) ret = (ret < 0) ? ret : -ETIMEDOUT; goto out; } + + /* + * Check L2_IDLE and L2_CLKSTOPPED in ADSP_STATUS + * NOTE: Standby mode in ADSP L2CC Power Control + * register should be enabled for this + */ + do { + adsp_status = amisc_readl(drv_data, AMISC_ADSP_STATUS); + if ((adsp_status & AMISC_ADSP_L2_IDLE) && + (adsp_status & AMISC_ADSP_L2_CLKSTOPPED)) + break; + cnt++; + mdelay(1); + } while (cnt < 5); + if (cnt >= 5) { + dev_err(dev, "ADSP L2C clock not halted: 0x%x\n", adsp_status); + ret = -EDEADLK; + goto out; + } + ret = 0; dev_dbg(dev, "ADSP OS suspended!\n"); From d40e2df1b27c2c1df088f457c1c183eac3f2478e Mon Sep 17 00:00:00 2001 From: Bharat Nihalani Date: Fri, 2 Dec 2022 17:15:08 +0530 Subject: [PATCH 138/138] drivers: Use runtime debugfs check When kernel command line debugfs=off is specified instead of disabling CONFIG_DEBUG_FS in defconfig to disable Debug-FS, debugfs functions like debugfs_create_dir will fail. Use function debugfs_initialized() to check if debugfs functionality is enabled before calling any debugfs functions. This allows us to by-pass debugfs initialization if debugfs is not enabled. Also, there is no need to protect debugfs related code under CONFIG_DEBUG_FS, as stub functions for all debugfs APIs are defined when CONFIG_DEBUG_FS is disabled. Bug 3752450 Change-Id: I1aa2c46bc822da54fdc87504ac75c91845e02c12 Signed-off-by: Bharat Nihalani Reviewed-on: https://git-master.nvidia.com/r/c/linux-nvidia/+/2820666 Reviewed-by: svc_kernel_abi Reviewed-by: svc-mobile-coverity Reviewed-by: svc-mobile-cert Reviewed-by: Suresh Venkatachalam Reviewed-by: Dara Ramesh GVS: Gerrit_Virtual_Submit --- drivers/platform/tegra/nvadsp/aram_manager.c | 22 +++++++---------- .../tegra/nvadsp/dram_app_mem_manager.c | 24 ++++++++----------- 2 files changed, 19 insertions(+), 27 deletions(-) diff --git a/drivers/platform/tegra/nvadsp/aram_manager.c b/drivers/platform/tegra/nvadsp/aram_manager.c index 62182c7a..e575037b 100644 --- a/drivers/platform/tegra/nvadsp/aram_manager.c +++ b/drivers/platform/tegra/nvadsp/aram_manager.c @@ -3,7 +3,7 @@ * * ARAM manager * - * Copyright (C) 2014-2018, NVIDIA Corporation. All rights reserved. + * Copyright (C) 2014-2022, 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 @@ -48,7 +48,6 @@ unsigned long nvadsp_aram_get_address(void *handle) return mem_get_address(handle); } -#ifdef CONFIG_DEBUG_FS static struct dentry *aram_dump_debugfs_file; static int nvadsp_aram_dump(struct seq_file *s, void *data) @@ -68,7 +67,6 @@ static const struct file_operations aram_dump_fops = { .llseek = seq_lseek, .release = single_release, }; -#endif int nvadsp_aram_init(unsigned long addr, unsigned long size) { @@ -78,23 +76,21 @@ int nvadsp_aram_init(unsigned long addr, unsigned long size) return PTR_ERR(aram_handle); } -#ifdef CONFIG_DEBUG_FS - aram_dump_debugfs_file = debugfs_create_file("aram_dump", - S_IRUSR, NULL, NULL, &aram_dump_fops); - if (!aram_dump_debugfs_file) { - pr_err("ERROR: failed to create aram_dump debugfs"); - destroy_mem_manager(aram_handle); - return -ENOMEM; + if (debugfs_initialized()) { + aram_dump_debugfs_file = debugfs_create_file("aram_dump", + S_IRUSR, NULL, NULL, &aram_dump_fops); + if (!aram_dump_debugfs_file) { + pr_err("ERROR: failed to create aram_dump debugfs"); + destroy_mem_manager(aram_handle); + return -ENOMEM; + } } -#endif return 0; } void nvadsp_aram_exit(void) { -#ifdef CONFIG_DEBUG_FS debugfs_remove(aram_dump_debugfs_file); -#endif destroy_mem_manager(aram_handle); } diff --git a/drivers/platform/tegra/nvadsp/dram_app_mem_manager.c b/drivers/platform/tegra/nvadsp/dram_app_mem_manager.c index ca809526..df45f019 100644 --- a/drivers/platform/tegra/nvadsp/dram_app_mem_manager.c +++ b/drivers/platform/tegra/nvadsp/dram_app_mem_manager.c @@ -3,7 +3,7 @@ * * dram app memory manager for allocating memory for text,bss and data * - * Copyright (C) 2014-2018, NVIDIA Corporation. All rights reserved. + * Copyright (C) 2014-2022, 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 @@ -50,7 +50,6 @@ unsigned long dram_app_mem_get_address(void *handle) return mem_get_address(handle); } -#ifdef CONFIG_DEBUG_FS static struct dentry *dram_app_mem_dump_debugfs_file; static int dram_app_mem_dump(struct seq_file *s, void *data) @@ -70,7 +69,6 @@ static const struct file_operations dram_app_mem_dump_fops = { .llseek = seq_lseek, .release = single_release, }; -#endif int dram_app_mem_init(unsigned long start, unsigned long size) { @@ -81,24 +79,22 @@ int dram_app_mem_init(unsigned long start, unsigned long size) return PTR_ERR(dram_app_mem_handle); } -#ifdef CONFIG_DEBUG_FS - dram_app_mem_dump_debugfs_file = - debugfs_create_file("dram_app_mem_dump", - S_IRUSR, NULL, NULL, &dram_app_mem_dump_fops); - if (!dram_app_mem_dump_debugfs_file) { - pr_err("ERROR: failed to create dram_app_mem_dump debugfs"); - destroy_mem_manager(dram_app_mem_handle); - return -ENOMEM; + if (debugfs_initialized()) { + dram_app_mem_dump_debugfs_file = + debugfs_create_file("dram_app_mem_dump", + S_IRUSR, NULL, NULL, &dram_app_mem_dump_fops); + if (!dram_app_mem_dump_debugfs_file) { + pr_err("ERROR: failed to create dram_app_mem_dump debugfs"); + destroy_mem_manager(dram_app_mem_handle); + return -ENOMEM; + } } -#endif return 0; } void dram_app_mem_exit(void) { -#ifdef CONFIG_DEBUG_FS debugfs_remove(dram_app_mem_dump_debugfs_file); -#endif destroy_mem_manager(dram_app_mem_handle); }