Files
linux-nv-oot/drivers/platform/tegra/nvadsp/adsp_dfs.c
Mikko Perttunen a37d2fe7e3 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 <mperttunen@nvidia.com>
Change-Id: Ic232ac71a09fe5176247692630db5bc6107573fa
Signed-off-by: Nitin Kumbhar <nkumbhar@nvidia.com>
Reviewed-on: https://git-master.nvidia.com/r/1537316
Reviewed-by: svccoveritychecker <svccoveritychecker@nvidia.com>
GVS: Gerrit_Virtual_Submit
2023-06-20 15:13:15 +00:00

848 lines
19 KiB
C

/*
* 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 <linux/tegra_nvadsp.h>
#include <linux/platform_device.h>
#include <linux/debugfs.h>
#include <linux/clk/tegra.h>
#include <linux/seq_file.h>
#include <asm/cputime.h>
#include <linux/slab.h>
#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;
}