Files
linux-nv-oot/drivers/platform/tegra/nvadsp/ape_actmon.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

985 lines
25 KiB
C

/*
* 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 <linux/kernel.h>
#include <linux/err.h>
#include <linux/io.h>
#include <linux/clk.h>
#include <linux/interrupt.h>
#include <linux/debugfs.h>
#include <linux/platform_device.h>
#include <linux/platform/tegra/clock.h>
#include <linux/irqchip/tegra-agic.h>
#include <linux/irq.h>
#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;
}