platform/tegra: Add Tegra Central Activity Monitor driver

Actmon is a hardware block that can be used to track the activity of
certain hardware units. It can boost EMC clock depending
on the memory traffic among various client. It is called central actmon
as it monitors central activity for example MC activity.

Add central activity monitor driver.

Bug 3625675

Change-Id: I1a5918e7d84bc247f694f53f965c28888b773c91
Signed-off-by: Laxman Dewangan <ldewangan@nvidia.com>
Reviewed-on: https://git-master.nvidia.com/r/c/linux-nv-oot/+/2705395
GVS: Gerrit_Virtual_Submit
This commit is contained in:
Laxman Dewangan
2022-04-30 06:03:48 +00:00
committed by mobile promotions
parent d75309658f
commit 527743a5c3
7 changed files with 1348 additions and 0 deletions

View File

@@ -2,3 +2,4 @@
# Copyright (c) 2022, NVIDIA CORPORATION. All rights reserved.
obj-m += bad.o
obj-m += cactmon.o

View File

@@ -0,0 +1,580 @@
// SPDX-License-Identifier: GPL-2.0
// Copyright (c) 2022, NVIDIA CORPORATION, All rights reserved.
#include <linux/module.h>
#include <linux/of_device.h>
#include <linux/reset.h>
#if IS_ENABLED(CONFIG_INTERCONNECT)
#include <linux/interconnect.h>
#include <dt-bindings/interconnect/tegra_icc_id.h>
#endif
#include <linux/platform/tegra/emc_bwmgr.h>
#include <linux/platform/tegra/actmon_common.h>
#include <linux/platform/tegra/mc_utils.h>
/************ START OF REG DEFINITION **************/
/* Actmon common registers */
#define ACTMON_GLB_CTRL 0x00
#define ACTMON_GLB_INT_EN 0x04
#define ACTMON_GLB_INT_STATUS 0x08
/* Actmon device registers */
/* ACTMON_*_CTRL_0 offset */
#define ACTMON_DEV_CTRL 0x00
/* ACTMON_*_CTRL_0 bit definitions */
#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_PERIODIC_ENB (0x1 << 13)
#define ACTMON_DEV_CTRL_K_VAL_SHIFT 10
#define ACTMON_DEV_CTRL_K_VAL_MASK (0x7 << 10)
/* ACTMON_*_INTR_ENABLE_0 */
#define ACTMON_DEV_INTR_ENB 0x04
/* ACTMON_*_INTR_ENABLE_0 bit definitions */
#define ACTMON_DEV_INTR_UP_WMARK_ENB (0x1 << 31)
#define ACTMON_DEV_INTR_DOWN_WMARK_ENB (0x1 << 30)
#define ACTMON_DEV_INTR_AVG_UP_WMARK_ENB (0x1 << 29)
#define ACTMON_DEV_INTR_AVG_DOWN_WMARK_ENB (0x1 << 28)
/* ACTMON_*_INTR_STAUS_0 */
#define ACTMON_DEV_INTR_STATUS 0x08
/* ACTMON_*_UPPER_WMARK_0 */
#define ACTMON_DEV_UP_WMARK 0x0c
/* ACTMON_*_LOWER_WMARK_0 */
#define ACTMON_DEV_DOWN_WMARK 0x10
/* ACTMON_*_AVG_UPPER_WMARK_0 */
#define ACTMON_DEV_AVG_UP_WMARK 0x14
/* ACTMON_*_AVG_LOWER_WMARK_0 */
#define ACTMON_DEV_AVG_DOWN_WMARK 0x18
/* ACTMON_*_AVG_INIT_AVG_0 */
#define ACTMON_DEV_INIT_AVG 0x1c
/* ACTMON_*_COUNT_0 */
#define ACTMON_DEV_COUNT 0x20
/* ACTMON_*_AVG_COUNT_0 */
#define ACTMON_DEV_AVG_COUNT 0x24
/* ACTMON_*_AVG_COUNT_WEIGHT_0 */
#define ACTMON_DEV_COUNT_WEGHT 0x28
/* ACTMON_*_CUMULATIVE_COUNT_0 */
#define ACTMON_DEV_CUMULATIVE_COUNT 0x2c
/************ END OF REG DEFINITION **************/
/******** start of actmon register operations **********/
static void set_prd(u32 val, void __iomem *base)
{
__raw_writel(val, base + ACTMON_GLB_CTRL);
}
static void set_glb_intr(u32 val, void __iomem *base)
{
__raw_writel(val, base + ACTMON_GLB_INT_EN);
}
static u32 get_glb_intr_st(void __iomem *base)
{
return __raw_readl(base + ACTMON_GLB_INT_STATUS);
}
/******** end of actmon register operations **********/
/*********start of actmon device register operations***********/
static void set_init_avg(u32 val, void __iomem *base)
{
__raw_writel(val, base + ACTMON_DEV_INIT_AVG);
}
static void set_avg_up_wm(u32 val, void __iomem *base)
{
__raw_writel(val, base + ACTMON_DEV_AVG_UP_WMARK);
}
static void set_avg_dn_wm(u32 val, void __iomem *base)
{
__raw_writel(val, base + ACTMON_DEV_AVG_DOWN_WMARK);
}
static void set_dev_up_wm(u32 val, void __iomem *base)
{
__raw_writel(val, base + ACTMON_DEV_UP_WMARK);
}
static void set_dev_dn_wm(u32 val, void __iomem *base)
{
__raw_writel(val, base + ACTMON_DEV_DOWN_WMARK);
}
static void set_cnt_wt(u32 val, void __iomem *base)
{
__raw_writel(val, base + ACTMON_DEV_COUNT_WEGHT);
}
static void set_intr_st(u32 val, void __iomem *base)
{
__raw_writel(val, base + ACTMON_DEV_INTR_STATUS);
}
static u32 get_intr_st(void __iomem *base)
{
return __raw_readl(base + ACTMON_DEV_INTR_STATUS);
}
static void init_dev_cntrl(struct actmon_dev *dev, void __iomem *base)
{
u32 val = 0;
val |= ACTMON_DEV_CTRL_PERIODIC_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);
__raw_writel(val, base + ACTMON_DEV_CTRL);
}
static void enb_dev_intr_all(void __iomem *base)
{
u32 val = 0;
val |= (ACTMON_DEV_INTR_UP_WMARK_ENB |
ACTMON_DEV_INTR_DOWN_WMARK_ENB |
ACTMON_DEV_INTR_AVG_UP_WMARK_ENB |
ACTMON_DEV_INTR_AVG_DOWN_WMARK_ENB);
__raw_writel(val, base + ACTMON_DEV_INTR_ENB);
}
static void enb_dev_intr(u32 val, void __iomem *base)
{
__raw_writel(val, base + ACTMON_DEV_INTR_ENB);
}
static u32 get_dev_intr(void __iomem *base)
{
return __raw_readl(base + ACTMON_DEV_INTR_ENB);
}
static u32 get_avg_cnt(void __iomem *base)
{
return __raw_readl(base + ACTMON_DEV_AVG_COUNT);
}
static u32 get_raw_cnt(void __iomem *base)
{
return __raw_readl(base + ACTMON_DEV_COUNT);
}
static void enb_dev_wm(u32 *val)
{
*val |= (ACTMON_DEV_INTR_UP_WMARK_ENB |
ACTMON_DEV_INTR_DOWN_WMARK_ENB);
}
static void disb_dev_up_wm(u32 *val)
{
*val &= ~ACTMON_DEV_INTR_UP_WMARK_ENB;
}
static void disb_dev_dn_wm(u32 *val)
{
*val &= ~ACTMON_DEV_INTR_DOWN_WMARK_ENB;
}
/*********end of actmon device register operations***********/
static void actmon_dev_reg_ops_init(struct actmon_dev *adev)
{
adev->ops.set_init_avg = set_init_avg;
adev->ops.set_avg_up_wm = set_avg_up_wm;
adev->ops.set_avg_dn_wm = set_avg_dn_wm;
adev->ops.set_dev_up_wm = set_dev_up_wm;
adev->ops.set_dev_dn_wm = set_dev_dn_wm;
adev->ops.set_cnt_wt = set_cnt_wt;
adev->ops.set_intr_st = set_intr_st;
adev->ops.get_intr_st = get_intr_st;
adev->ops.init_dev_cntrl = init_dev_cntrl;
adev->ops.enb_dev_intr_all = enb_dev_intr_all;
adev->ops.enb_dev_intr = enb_dev_intr;
adev->ops.get_dev_intr_enb = get_dev_intr;
adev->ops.get_avg_cnt = get_avg_cnt;
adev->ops.get_raw_cnt = get_raw_cnt;
adev->ops.enb_dev_wm = enb_dev_wm;
adev->ops.disb_dev_up_wm = disb_dev_up_wm;
adev->ops.disb_dev_dn_wm = disb_dev_dn_wm;
}
static unsigned long actmon_dev_get_max_rate(struct actmon_dev *adev)
{
unsigned long rate = 0;
if (!adev->bwmgr_disable)
return tegra_bwmgr_get_max_emc_rate();
if (adev->dram_clk_handle) {
rate = clk_round_rate(adev->dram_clk_handle, ULONG_MAX);
return rate;
}
return 0;
}
static unsigned long actmon_dev_get_rate(struct actmon_dev *adev)
{
unsigned long rate = 0;
if (!adev->bwmgr_disable)
return tegra_bwmgr_get_emc_rate();
if (adev->dram_clk_handle) {
rate = clk_get_rate(adev->dram_clk_handle);
return rate;
}
return 0;
}
static unsigned long actmon_dev_post_change_rate(
struct actmon_dev *adev, void *cclk)
{
struct clk_notifier_data *clk_data = cclk;
return clk_data->new_rate;
}
/* TBD: Use the implementation from mc_utils */
#if IS_ENABLED(CONFIG_INTERCONNECT)
#define BYTES_PER_CLK_PER_CH 4
#define CH_16 16
#define CH_16_BYTES_PER_CLK (BYTES_PER_CLK_PER_CH * CH_16)
unsigned long emc_freq_to_bw(unsigned long freq)
{
return freq * CH_16_BYTES_PER_CLK;
}
#endif
static void icc_set_rate(struct actmon_dev *adev, unsigned long freq)
{
#if IS_ENABLED(CONFIG_INTERCONNECT)
struct icc_path *icc_path_handle = NULL;
u32 floor_bw_kbps = 0;
icc_path_handle = (struct icc_path *)adev->clnt;
floor_bw_kbps = emc_freq_to_bw(freq);
icc_set_bw(icc_path_handle, 0, floor_bw_kbps);
#endif
}
static void actmon_dev_set_rate(struct actmon_dev *adev,
unsigned long freq)
{
struct tegra_bwmgr_client *bwclnt = NULL;
if (!adev->bwmgr_disable) {
bwclnt = (struct tegra_bwmgr_client *)adev->clnt;
tegra_bwmgr_set_emc(bwclnt, freq * 1000,
TEGRA_BWMGR_SET_EMC_FLOOR);
} else {
icc_set_rate(adev, freq);
}
}
static int cactmon_bwmgr_register(
struct actmon_dev *adev, struct platform_device *pdev)
{
struct tegra_bwmgr_client *bwclnt = (struct tegra_bwmgr_client *)
adev->clnt;
struct device *mon_dev = &pdev->dev;
int ret = 0;
bwclnt = tegra_bwmgr_register(TEGRA_BWMGR_CLIENT_MON);
if (IS_ERR_OR_NULL(bwclnt)) {
ret = -ENODEV;
dev_err(mon_dev, "emc bw manager registration failed for %s\n",
adev->dn->name);
return ret;
}
adev->clnt = bwclnt;
return ret;
}
static void cactmon_bwmgr_unregister(
struct actmon_dev *adev, struct platform_device *pdev)
{
struct tegra_bwmgr_client *bwclnt = (struct tegra_bwmgr_client *)
adev->clnt;
struct device *mon_dev = &pdev->dev;
if (bwclnt) {
dev_dbg(mon_dev, "unregistering BW manager for %s\n",
adev->dn->name);
tegra_bwmgr_unregister(bwclnt);
adev->clnt = NULL;
}
}
static int cactmon_icc_register(
struct actmon_dev *adev, struct platform_device *pdev)
{
int ret = 0;
#if IS_ENABLED(CONFIG_INTERCONNECT)
struct icc_path *icc_path_handle = NULL;
struct device *mon_dev = &pdev->dev;
icc_path_handle = icc_get(mon_dev, TEGRA_ICC_CACTMON, TEGRA_ICC_PRIMARY);
if (IS_ERR_OR_NULL(icc_path_handle)) {
ret = -ENODEV;
dev_err(mon_dev, "icc registration failed for %s\n",
adev->dn->name);
return ret;
}
adev->clnt = icc_path_handle;
#endif
return ret;
}
static void cactmon_icc_unregister(
struct actmon_dev *adev, struct platform_device *pdev)
{
#if IS_ENABLED(CONFIG_INTERCONNECT)
struct icc_path *icc_path_handle = (struct icc_path *)
adev->clnt;
struct device *mon_dev = &pdev->dev;
if (icc_path_handle) {
dev_dbg(mon_dev, "unregistering icc for %s\n",
adev->dn->name);
icc_put(icc_path_handle);
adev->clnt = NULL;
}
#endif
}
static int cactmon_register_bw(
struct actmon_dev *adev, struct platform_device *pdev)
{
int ret = 0;
if (adev->bwmgr_disable)
ret = cactmon_icc_register(adev, pdev);
else
ret = cactmon_bwmgr_register(adev, pdev);
return ret;
}
static void cactmon_unregister_bw(
struct actmon_dev *adev, struct platform_device *pdev)
{
if (adev->bwmgr_disable)
cactmon_icc_unregister(adev, pdev);
else
cactmon_bwmgr_unregister(adev, pdev);
}
static int actmon_dev_platform_init(struct actmon_dev *adev,
struct platform_device *pdev)
{
struct tegra_bwmgr_client *bwclnt;
int ret = 0;
ret = cactmon_register_bw(adev, pdev);
if (ret)
goto end;
adev->dev_name = adev->dn->name;
adev->max_freq = actmon_dev_get_max_rate(adev);
if (!adev->bwmgr_disable) {
bwclnt = (struct tegra_bwmgr_client *) adev->clnt;
tegra_bwmgr_set_emc(bwclnt, adev->max_freq,
TEGRA_BWMGR_SET_EMC_FLOOR);
} else {
icc_set_rate(adev, adev->max_freq);
}
adev->max_freq /= 1000;
actmon_dev_reg_ops_init(adev);
adev->actmon_dev_set_rate = actmon_dev_set_rate;
adev->actmon_dev_get_rate = actmon_dev_get_rate;
if (adev->rate_change_nb.notifier_call) {
if (!adev->bwmgr_disable)
ret = tegra_bwmgr_notifier_register(&adev->rate_change_nb);
else
ret = clk_notifier_register(adev->dram_clk_handle, &adev->rate_change_nb);
if (ret) {
pr_err("Failed to register bw manager rate change notifier for %s\n",
adev->dev_name);
return ret;
}
}
adev->actmon_dev_post_change_rate = actmon_dev_post_change_rate;
end:
return ret;
}
static void cactmon_free_resource(
struct actmon_dev *adev, struct platform_device *pdev)
{
int ret = 0;
if (adev->rate_change_nb.notifier_call) {
if (!adev->bwmgr_disable)
ret = tegra_bwmgr_notifier_unregister(&adev->rate_change_nb);
else
ret = clk_notifier_unregister(adev->dram_clk_handle, &adev->rate_change_nb);
if (ret) {
pr_err("Failed to register bw manager rate change notifier for %s\n",
adev->dev_name);
}
}
cactmon_unregister_bw(adev, pdev);
}
static int cactmon_reset_dinit(struct platform_device *pdev)
{
struct actmon_drv_data *actmon = platform_get_drvdata(pdev);
struct device *mon_dev = &pdev->dev;
int ret = -EINVAL;
if (actmon->actmon_rst) {
ret = reset_control_assert(actmon->actmon_rst);
if (ret)
dev_err(mon_dev, "failed to assert actmon\n");
}
return ret;
}
static int cactmon_reset_init(struct platform_device *pdev)
{
struct actmon_drv_data *actmon = platform_get_drvdata(pdev);
struct device *mon_dev = &pdev->dev;
int ret = 0;
actmon->actmon_rst = devm_reset_control_get(mon_dev, "actmon_rst");
if (IS_ERR(actmon->actmon_rst)) {
ret = PTR_ERR(actmon->actmon_rst);
dev_err(mon_dev, "can not get actmon reset%d\n", ret);
}
/* Make actmon out of reset */
ret = reset_control_deassert(actmon->actmon_rst);
if (ret)
dev_err(mon_dev, "failed to deassert actmon\n");
return ret;
}
static int cactmon_clk_disable(struct platform_device *pdev)
{
struct actmon_drv_data *actmon = platform_get_drvdata(pdev);
struct device *mon_dev = &pdev->dev;
int ret = 0;
if (actmon->actmon_clk) {
clk_disable_unprepare(actmon->actmon_clk);
devm_clk_put(mon_dev, actmon->actmon_clk);
actmon->actmon_clk = NULL;
dev_dbg(mon_dev, "actmon clocks disabled\n");
}
return ret;
}
static int cactmon_clk_enable(struct platform_device *pdev)
{
struct actmon_drv_data *actmon = platform_get_drvdata(pdev);
struct device *mon_dev = &pdev->dev;
int ret = 0;
actmon->actmon_clk = devm_clk_get(mon_dev, "actmon");
if (IS_ERR_OR_NULL(actmon->actmon_clk)) {
dev_err(mon_dev, "unable to find actmon clock\n");
ret = PTR_ERR(actmon->actmon_clk);
return ret;
}
ret = clk_prepare_enable(actmon->actmon_clk);
if (ret) {
dev_err(mon_dev, "unable to enable actmon clock\n");
goto end;
}
actmon->freq = clk_get_rate(actmon->actmon_clk) / 1000;
return 0;
end:
devm_clk_put(mon_dev, actmon->actmon_clk);
return ret;
}
static struct actmon_drv_data actmon_data = {
.clock_init = cactmon_clk_enable,
.clock_deinit = cactmon_clk_disable,
.reset_init = cactmon_reset_init,
.reset_deinit = cactmon_reset_dinit,
.dev_free_resource = cactmon_free_resource,
.actmon_dev_platform_init = actmon_dev_platform_init,
.ops.set_sample_prd = set_prd,
.ops.set_glb_intr = set_glb_intr,
.ops.get_glb_intr_st = get_glb_intr_st,
};
static const struct of_device_id tegra_actmon_of_match[] = {
{ .compatible = "nvidia,tegra194-cactmon", .data = &actmon_data, },
{ .compatible = "nvidia,tegra186-cactmon", .data = &actmon_data, },
{ .compatible = "nvidia,tegra234-cactmon", .data = &actmon_data, },
{},
};
static int __init tegra_actmon_probe(struct platform_device *pdev)
{
int ret = 0;
const struct of_device_id *of_id;
struct actmon_drv_data *actmon;
of_id = of_match_node(tegra_actmon_of_match, pdev->dev.of_node);
if (of_id == NULL) {
pr_err("No matching of node\n");
ret = -ENODATA;
return ret;
}
actmon = (struct actmon_drv_data *)of_id->data;
platform_set_drvdata(pdev, actmon);
actmon->pdev = pdev;
ret = tegra_actmon_register(actmon);
return ret;
}
static struct platform_driver tegra19x_actmon_driver __refdata = {
.probe = tegra_actmon_probe,
.remove = tegra_actmon_remove,
.driver = {
.name = "tegra_actmon",
.owner = THIS_MODULE,
.of_match_table = of_match_ptr(tegra_actmon_of_match),
},
};
module_platform_driver(tegra19x_actmon_driver);
MODULE_DESCRIPTION("Central Tegra Activity Monitor");
MODULE_AUTHOR("Laxman Dewangan <ldewangan@nvidia.com>");
MODULE_LICENSE("GPL v2");

View File

@@ -0,0 +1,73 @@
/*
* Copyright (c) 2020-2022, NVIDIA CORPORATION & AFFILIATES. 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 <http://www.gnu.org/licenses/>.
*/
#ifndef _DT_BINDINGS_TEGRA_ICC_ID_H
#define _DT_BINDINGS_TEGRA_ICC_ID_H
/* ICC master node */
#define TEGRA_ICC_PRIMARY 0
#define TEGRA_ICC_DEBUG 1
#define TEGRA_ICC_CPU_CLUSTER0 2
#define TEGRA_ICC_CPU_CLUSTER1 3
#define TEGRA_ICC_CPU_CLUSTER2 4
#define TEGRA_ICC_GPU 5
#define TEGRA_ICC_CACTMON 6
#define TEGRA_ICC_DISPLAY 7
#define TEGRA_ICC_VI 8
#define TEGRA_ICC_EQOS 9
#define TEGRA_ICC_PCIE_0 10
#define TEGRA_ICC_PCIE_1 11
#define TEGRA_ICC_PCIE_2 12
#define TEGRA_ICC_PCIE_3 13
#define TEGRA_ICC_PCIE_4 14
#define TEGRA_ICC_PCIE_5 15
#define TEGRA_ICC_PCIE_6 16
#define TEGRA_ICC_PCIE_7 17
#define TEGRA_ICC_PCIE_8 18
#define TEGRA_ICC_PCIE_9 19
#define TEGRA_ICC_PCIE_10 20
#define TEGRA_ICC_DLA_0 21
#define TEGRA_ICC_DLA_1 22
#define TEGRA_ICC_SDMMC_1 23
#define TEGRA_ICC_SDMMC_2 24
#define TEGRA_ICC_SDMMC_3 25
#define TEGRA_ICC_SDMMC_4 26
#define TEGRA_ICC_NVDEC 27
#define TEGRA_ICC_NVENC 28
#define TEGRA_ICC_NVJPG_0 29
#define TEGRA_ICC_NVJPG_1 30
#define TEGRA_ICC_OFAA 31
#define TEGRA_ICC_XUSB_HOST 32
#define TEGRA_ICC_XUSB_DEV 33
#define TEGRA_ICC_TSEC 34
#define TEGRA_ICC_VIC 35
#define TEGRA_ICC_APE 36
#define TEGRA_ICC_APEDMA 37
#define TEGRA_ICC_SE 38
#define TEGRA_ICC_ISP 39
#define TEGRA_ICC_HDA 40
#define TEGRA_ICC_VIFAL 41
#define TEGRA_ICC_VI2FAL 42
#define TEGRA_ICC_VI2 43
#define TEGRA_ICC_RCE 44
#define TEGRA_ICC_PVA 45
#define TEGRA_ICC_NVPMODEL 46
/* remove later */
#define NV_NVDISPLAYR2MC_SR_ID TEGRA_ICC_DISPLAY
#define TEGRA_ICC_MASTER TEGRA_ICC_PRIMARY
#endif /* _DT_BINDINGS_TEGRA_ICC_ID_H */

View File

@@ -0,0 +1,198 @@
// SPDX-License-Identifier: GPL-2.0
// Copyright (c) 2022, NVIDIA CORPORATION, All rights reserved.
#ifndef ACTMON_COMMON_H
#include <linux/io.h>
/* START: These device register offsets have common value across socs */
#define ACTMON_CMN_DEV_CTRL 0x00
#define ACTMON_CMN_DEV_CTRL_ENB (0x1 << 31)
#define ACTMON_CMN_DEV_CTRL_UP_WMARK_NUM_SHIFT 26
#define ACTMON_CMN_DEV_CTRL_UP_WMARK_NUM_MASK (0x7 << 26)
#define ACTMON_CMN_DEV_CTRL_DOWN_WMARK_NUM_SHIFT 21
#define ACTMON_CMN_DEV_CTRL_DOWN_WMARK_NUM_MASK (0x7 << 21)
/* Common dev interrupt status bits across socs */
#define ACTMON_CMN_DEV_INTR_UP_WMARK (0x1 << 31)
#define ACTMON_CMN_DEV_INTR_DOWN_WMARK (0x1 << 30)
/* END: common device regs across socs */
#define ACTMON_DEFAULT_AVG_WINDOW_LOG2 7
#define ACTMON_DEFAULT_AVG_BAND 6 /* 1/10 of % */
#define ACTMON_DEFAULT_SAMPLING_PERIOD 7
#define DEFAULT_SUSPEND_FREQ 204000
#define DEFAULT_BOOST_UP_COEF 200
#define DEFAULT_BOOST_DOWN_COEF 50
#define DEFAULT_BOOST_UP_THRESHOLD 30
#define DEFAULT_BOOST_DOWN_THRESHOLD 20
#define DEFAULT_UP_WMARK_WINDOW 3
#define DEFAULT_DOWN_WMARK_WINDOW 2
#define DEFAULT_EWMA_COEF_K 6
#define DEFAULT_COUNT_WEIGHT 0x53
#define FREQ_SAMPLER 1
#define LOAD_SAMPLER 0
#define DEFAULT_ACTMON_TYPE FREQ_SAMPLER
/* Maximum frequency EMC is running at when sourced from PLLP. This is
* really a short-cut, but it is true for all Tegra3 platforms
*/
#define EMC_PLLP_FREQ_MAX 204000
enum actmon_devices { /* Should match with device sequence in dt */
MC_ALL, /* instance 1 */
MC_CPU, /* instance 0 */
MAX_DEVICES,
};
enum actmon_type {
ACTMON_LOAD_SAMPLER,
ACTMON_FREQ_SAMPLER,
};
enum actmon_state {
ACTMON_UNINITIALIZED = -1,
ACTMON_OFF = 0,
ACTMON_ON = 1,
ACTMON_SUSPENDED = 2,
};
struct actmon_dev;
struct actmon_drv_data;
struct dev_reg_ops {
void (*set_init_avg)(u32 value, void __iomem *base);
void (*set_avg_up_wm)(u32 value, void __iomem *base);
void (*set_avg_dn_wm)(u32 value, void __iomem *base);
void (*set_dev_up_wm)(u32 value, void __iomem *base);
void (*set_dev_dn_wm)(u32 value, void __iomem *base);
void (*enb_dev_wm)(u32 *value);
void (*disb_dev_up_wm)(u32 *value);
void (*disb_dev_dn_wm)(u32 *value);
void (*set_intr_st)(u32 value, void __iomem *base);
void (*init_dev_cntrl)(struct actmon_dev *dev, void __iomem *base);
void (*enb_dev_intr)(u32 value, void __iomem *base);
void (*enb_dev_intr_all)(void __iomem *base);
void (*set_cnt_wt)(u32 value, void __iomem *base);
u32 (*get_intr_st)(void __iomem *base);
u32 (*get_dev_intr_enb)(void __iomem *base);
u32 (*get_dev_intr)(void __iomem *base);
u32 (*get_raw_cnt)(void __iomem *base);
u32 (*get_avg_cnt)(void __iomem *base);
u32 (*get_cum_cnt)(void __iomem *base);
};
/* Units:
* - frequency in kHz
* - coefficients, and thresholds in %
* - sampling period in ms
* - window in sample periods (value = setting + 1)
*/
struct actmon_dev {
struct device_node *dn;
u32 reg_offs;
u32 glb_status_irq_mask;
const char *dev_name;
const char *con_id;
void *clnt;
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;
u32 avg_dependency_threshold;
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;
bool bwmgr_disable;
struct clk *dram_clk_handle;
struct dev_reg_ops ops;
void (*actmon_dev_set_rate)(struct actmon_dev *adev, unsigned long freq);
unsigned long (*actmon_dev_get_rate)(struct actmon_dev *adev);
unsigned long (*actmon_dev_post_change_rate)(struct actmon_dev *adev,
void *v);
void (*actmon_dev_clk_enable)(struct actmon_dev *adev);
spinlock_t lock;
struct notifier_block rate_change_nb;
struct kobj_attribute avgact_attr;
};
struct actmon_reg_ops {
void (*set_sample_prd)(u32 value, void __iomem *base);
void (*set_glb_intr)(u32 value, void __iomem *base);
u32 (*get_glb_intr_st)(void __iomem *base);
};
struct actmon_drv_data {
void __iomem *base;
struct platform_device *pdev;
int virq;
struct clk *actmon_clk;
u8 sample_period;
unsigned long freq;
struct reset_control *actmon_rst;
struct actmon_dev devices[MAX_DEVICES];
int (*actmon_dev_platform_init)(struct actmon_dev *adev,
struct platform_device *pdev);
int (*clock_init)(struct platform_device *pdev);
int (*clock_deinit)(struct platform_device *pdev);
int (*reset_init)(struct platform_device *pdev);
int (*reset_deinit)(struct platform_device *pdev);
void (*dev_free_resource)(struct actmon_dev *adev,
struct platform_device *pdev);
struct actmon_reg_ops ops;
#ifdef CONFIG_DEBUG_FS
struct dentry *dfs_root;
#endif
struct kobject *actmon_kobj;
};
static inline void actmon_wmb(void)
{
dsb(sy);
}
static inline u32 actmon_dev_readl(void __iomem *base,
u32 offset)
{
return __raw_readl(base + offset);
}
static inline void actmon_dev_writel(void __iomem *base, u32
offset, u32 val)
{
__raw_writel(val, base + offset);
}
#if defined(CONFIG_TEGRA_CENTRAL_ACTMON)
int tegra_actmon_register(struct actmon_drv_data *actmon);
int tegra_actmon_remove(struct platform_device *pdev);
#else
static inline int tegra_actmon_register(struct actmon_drv_data *actmon)
{
return -EINVAL;
}
static inline int tegra_actmon_remove(struct platform_device *pdev)
{
return -EINVAL;
}
#endif
#endif /* ACTMON_COMMON_H */

View File

@@ -0,0 +1,381 @@
// SPDX-License-Identifier: GPL-2.0
// Copyright (c) 2022, NVIDIA CORPORATION, All rights reserved.
#ifndef __EMC_BWMGR_H
#define __EMC_BWMGR_H
#include <linux/clk.h>
#include <linux/device.h>
#include <linux/of_address.h>
#include <linux/platform/tegra/iso_client.h>
#include <soc/tegra/bpmp-abi.h>
/* keep in sync with tegra_bwmgr_client_names */
enum tegra_bwmgr_client_id {
TEGRA_BWMGR_CLIENT_CPU_CLUSTER_0,
TEGRA_BWMGR_CLIENT_CPU_CLUSTER_1,
TEGRA_BWMGR_CLIENT_CPU_CLUSTER_2,
TEGRA_BWMGR_CLIENT_CPU_CLUSTER_3,
TEGRA_BWMGR_CLIENT_DISP0,
TEGRA_BWMGR_CLIENT_DISP1,
TEGRA_BWMGR_CLIENT_DISP2,
TEGRA_BWMGR_CLIENT_DISP1_LA_EMC,
TEGRA_BWMGR_CLIENT_DISP2_LA_EMC,
TEGRA_BWMGR_CLIENT_USBD,
TEGRA_BWMGR_CLIENT_XHCI,
TEGRA_BWMGR_CLIENT_SDMMC1,
TEGRA_BWMGR_CLIENT_SDMMC2,
TEGRA_BWMGR_CLIENT_SDMMC3,
TEGRA_BWMGR_CLIENT_SDMMC4,
TEGRA_BWMGR_CLIENT_MON,
TEGRA_BWMGR_CLIENT_GPU,
TEGRA_BWMGR_CLIENT_MSENC,
TEGRA_BWMGR_CLIENT_NVENC1,
TEGRA_BWMGR_CLIENT_NVJPG,
TEGRA_BWMGR_CLIENT_NVDEC,
TEGRA_BWMGR_CLIENT_NVDEC1,
TEGRA_BWMGR_CLIENT_TSEC,
TEGRA_BWMGR_CLIENT_TSECB,
TEGRA_BWMGR_CLIENT_VI,
TEGRA_BWMGR_CLIENT_ISPA,
TEGRA_BWMGR_CLIENT_ISPB,
TEGRA_BWMGR_CLIENT_CAMERA,
TEGRA_BWMGR_CLIENT_CAMERA_NON_ISO,
TEGRA_BWMGR_CLIENT_CAMRTC,
TEGRA_BWMGR_CLIENT_ISOMGR,
TEGRA_BWMGR_CLIENT_THERMAL_CAP,
TEGRA_BWMGR_CLIENT_VIC,
TEGRA_BWMGR_CLIENT_APE_ADSP,
TEGRA_BWMGR_CLIENT_APE_ADMA,
TEGRA_BWMGR_CLIENT_PCIE,
TEGRA_BWMGR_CLIENT_PCIE_1,
TEGRA_BWMGR_CLIENT_PCIE_2,
TEGRA_BWMGR_CLIENT_PCIE_3,
TEGRA_BWMGR_CLIENT_PCIE_4,
TEGRA_BWMGR_CLIENT_PCIE_5,
TEGRA_BWMGR_CLIENT_BBC_0,
TEGRA_BWMGR_CLIENT_EQOS,
TEGRA_BWMGR_CLIENT_SE0,
TEGRA_BWMGR_CLIENT_SE1,
TEGRA_BWMGR_CLIENT_SE2,
TEGRA_BWMGR_CLIENT_SE3,
TEGRA_BWMGR_CLIENT_SE4,
TEGRA_BWMGR_CLIENT_NVPMODEL,
TEGRA_BWMGR_CLIENT_DEBUG,
TEGRA_BWMGR_CLIENT_DLA0,
TEGRA_BWMGR_CLIENT_DLA1,
TEGRA_BWMGR_CLIENT_COUNT /* Should always be last */
};
enum tegra_bwmgr_request_type {
TEGRA_BWMGR_SET_EMC_FLOOR, /* lower bound */
TEGRA_BWMGR_SET_EMC_CAP, /* upper bound */
TEGRA_BWMGR_SET_EMC_ISO_CAP, /* upper bound that affects ISO Bw */
TEGRA_BWMGR_SET_EMC_SHARED_BW, /* shared bw request */
TEGRA_BWMGR_SET_EMC_SHARED_BW_ISO, /* for use by ISO Mgr only */
TEGRA_BWMGR_SET_EMC_REQ_COUNT /* Should always be last */
};
enum bwmgr_dram_types {
DRAM_TYPE_NONE,
DRAM_TYPE_LPDDR4_16CH_ECC,
DRAM_TYPE_LPDDR4_8CH_ECC,
DRAM_TYPE_LPDDR4_4CH_ECC,
DRAM_TYPE_LPDDR4_2CH_ECC,
DRAM_TYPE_LPDDR4_16CH,
DRAM_TYPE_LPDDR4_8CH,
DRAM_TYPE_LPDDR4_4CH,
DRAM_TYPE_LPDDR4_2CH,
DRAM_TYPE_LPDDR3_2CH,
DRAM_TYPE_DDR3_2CH
};
extern u8 bwmgr_dram_efficiency;
extern u8 bwmgr_dram_num_channels;
/* flag to determine supported memory and channel configuration */
extern u8 bwmgr_dram_config_supported;
extern u32 *bwmgr_dram_iso_eff_table;
extern u32 *bwmgr_dram_noniso_eff_table;
extern u32 *bwmgr_max_nvdis_bw_reqd;
extern u32 *bwmgr_max_vi_bw_reqd;
extern int *bwmgr_slope;
extern u32 *bwmgr_vi_bw_reqd_offset;
extern int bwmgr_iso_bw_percentage;
extern enum bwmgr_dram_types bwmgr_dram_type;
extern int emc_to_dram_freq_factor;
extern struct mrq_emc_dvfs_latency_response bwmgr_emc_dvfs;
struct tegra_bwmgr_client;
struct bwmgr_ops {
unsigned long (*freq_to_bw)(unsigned long freq);
unsigned long (*bw_to_freq)(unsigned long bw);
u32 (*dvfs_latency)(u32 ufreq);
unsigned long (*bwmgr_apply_efficiency)(
unsigned long total_bw, unsigned long iso_bw,
unsigned long max_rate, u64 usage_flags,
unsigned long *iso_bw_min, unsigned long iso_bw_nvdis,
unsigned long iso_bw_vi);
unsigned long (*get_best_iso_freq)(long iso_bw,
long iso_bw_nvdis, long iso_bw_vi);
void (*update_efficiency)(unsigned long dram_refresh_rate);
u32 (*get_max_iso_bw)(enum tegra_iso_client client);
};
struct bwmgr_ops *bwmgr_eff_init_t21x(void);
struct bwmgr_ops *bwmgr_eff_init_t18x(void);
struct bwmgr_ops *bwmgr_eff_init_t19x(void);
#if defined(CONFIG_TEGRA_BWMGR)
/**
* tegra_bwmgr_register - register an EMC Bandwidth Manager client.
* Also see tegra_bwmgr_unregister().
* @client client id from tegra_bwmgr_client_id
*
* Returns a valid handle on successful registration, NULL on error.
*/
struct tegra_bwmgr_client *tegra_bwmgr_register(
enum tegra_bwmgr_client_id client);
/**
* tegra_bwmgr_unregister - unregister an EMC Bandwidth Manager client.
* Callers should match register/unregister calls.
* Persistence of old requests across
* register/unregister calls is undefined.
* Also see tegra_bwmgr_set_emc()
*
* @handle handle acquired during tegra_bwmgr_register
*/
void tegra_bwmgr_unregister(struct tegra_bwmgr_client *handle);
/**
* tegra_bwmgr_get_dram_num_channels - get the number of DRAM channels
*
* Returns the number of DRAM channels that are configured on the underlying
* platform.
*/
u8 tegra_bwmgr_get_dram_num_channels(void);
/**
* tegra_bwmgr_get_emc_rate - get the current EMC rate.
*
* Returns current memory clock rate in Hz.
*/
unsigned long tegra_bwmgr_get_emc_rate(void);
/**
* tegra_bwmgr_get_max_emc_rate - get the max EMC rate.
*
* Returns the max memory clock rate in Hz.
*/
unsigned long tegra_bwmgr_get_max_emc_rate(void);
/**
* tegra_bwmgr_get_core_emc_rate - get the actual emc frequency calculated
* using the dram frequency and emc_to_dram
* conversion factor.
*
* Returns the core emc rate in Hz.
*/
unsigned long tegra_bwmgr_get_core_emc_rate(void);
/**
* tegra_bwmgr_round_rate - round up to next EMC rate which can be provided
*
* @bw Input rate
*
* Returns the next higher rate from the Input rate that EMC can run at.
*/
unsigned long tegra_bwmgr_round_rate(unsigned long bw);
/**
* tegra_bwmgr_set_emc - request to bwmgr to set an EMC rate parameter.
* Actual clock rate depends on aggregation of
* requests by all clients. If needed, use
* tegra_bwmgr_get_emc_rate() to get the rate after
* a tegra_bwmgr_set_emc() call.
*
* Call tegra_bwmgr_set_emc() with same request type and
* val = 0 to clear request.
*
* @handle handle acquired during tegra_bwmgr_register
* @val value to be set in Hz, 0 to clear old request of the same type
* @req chosen type from tegra_bwmgr_request_type
*
* Returns success (0) or negative errno.
*/
int tegra_bwmgr_set_emc(struct tegra_bwmgr_client *handle, unsigned long val,
enum tegra_bwmgr_request_type req);
/**
* tegra_bwmgr_get_client_info - outputs the value previously set with
* tegra_bwmgr_set_emc or 0 if no value has been set.
*
* @handle handle acquired during tegra_bwmgr_register
* @out_val bandwidth value in Hz
* @req chosen type from tegra_bwmgr_request_type
*
* Returns success (0) or negative errno.
*/
int tegra_bwmgr_get_client_info(struct tegra_bwmgr_client *handle,
unsigned long *out_val,
enum tegra_bwmgr_request_type req);
/**
* tegra_bwmgr_notifier_register - register a notifier callback when
* emc rate changes. Must be called from non-atomic
* context. The callback must not call any bwmgr API.
* @nb linux notifier block
*
* Returns success (0) or negative errno.
*/
int tegra_bwmgr_notifier_register(struct notifier_block *nb);
/**
* tegra_bwmgr_notifier_unregister - unregister a notifier callback.
* @nb linux notifier block
*
* Returns success (0) or negative errno.
*/
int tegra_bwmgr_notifier_unregister(struct notifier_block *nb);
/*
* Initialize bwmgr.
* This api would be called by .init_machine during boot.
* bwmgr clients, don't call this api.
*/
int __init bwmgr_init(void);
void __exit bwmgr_exit(void);
#else /* CONFIG_TEGRA_BWMGR */
static inline struct tegra_bwmgr_client *tegra_bwmgr_register(
enum tegra_bwmgr_client_id client_id)
{
static int i;
/* return a dummy handle to allow client to function
* as if bwmgr were enabled.
*/
return (struct tegra_bwmgr_client *) &i;
}
static inline void tegra_bwmgr_unregister(struct tegra_bwmgr_client *handle) {}
static inline int bwmgr_init(void)
{
return 0;
}
static inline void bwmgr_exit(void) {}
static inline u8 tegra_bwmgr_get_dram_num_channels(void)
{
return 0;
}
static inline unsigned long tegra_bwmgr_get_emc_rate(void)
{
static struct clk *bwmgr_emc_clk;
struct device_node *dn;
if (!bwmgr_emc_clk) {
dn = of_find_compatible_node(NULL, NULL, "nvidia,bwmgr");
if (dn == NULL) {
pr_err("bwmgr: dt node not found.\n");
return 0;
}
bwmgr_emc_clk = of_clk_get(dn, 0);
if (IS_ERR_OR_NULL(bwmgr_emc_clk)) {
pr_err("bwmgr: couldn't find emc clock.\n");
bwmgr_emc_clk = NULL;
WARN_ON(true);
return 0;
}
}
return clk_get_rate(bwmgr_emc_clk);
}
static inline unsigned long tegra_bwmgr_get_max_emc_rate(void)
{
static struct clk *bwmgr_emc_clk;
struct device_node *dn;
if (!bwmgr_emc_clk) {
dn = of_find_compatible_node(NULL, NULL, "nvidia,bwmgr");
if (dn == NULL) {
pr_err("bwmgr: dt node not found.\n");
return 0;
}
bwmgr_emc_clk = of_clk_get(dn, 0);
if (IS_ERR_OR_NULL(bwmgr_emc_clk)) {
pr_err("bwmgr: couldn't find emc clock.\n");
bwmgr_emc_clk = NULL;
WARN_ON(true);
return 0;
}
}
/* Use LONG_MAX as clk_round_rate treats rate argument as signed */
return clk_round_rate(bwmgr_emc_clk, LONG_MAX);
}
static inline unsigned long tegra_bwmgr_get_core_emc_rate(void)
{
return 0;
}
static inline unsigned long tegra_bwmgr_round_rate(unsigned long bw)
{
static struct clk *bwmgr_emc_clk;
struct device_node *dn;
if (!bwmgr_emc_clk) {
dn = of_find_compatible_node(NULL, NULL, "nvidia,bwmgr");
if (dn == NULL) {
pr_err("bwmgr: dt node not found.\n");
return 0;
}
bwmgr_emc_clk = of_clk_get(dn, 0);
if (IS_ERR_OR_NULL(bwmgr_emc_clk)) {
pr_err("bwmgr: couldn't find emc clock.\n");
bwmgr_emc_clk = NULL;
WARN_ON(true);
return 0;
}
}
return clk_round_rate(bwmgr_emc_clk, bw);
}
static inline int tegra_bwmgr_set_emc(struct tegra_bwmgr_client *handle,
unsigned long val, enum tegra_bwmgr_request_type req)
{
return 0;
}
static inline int tegra_bwmgr_get_client_info(struct tegra_bwmgr_client *handle,
unsigned long *out_val,
enum tegra_bwmgr_request_type req)
{
if (!out_val)
return -EINVAL;
*out_val = 0;
return 0;
}
static inline int tegra_bwmgr_notifier_register(struct notifier_block *nb)
{
return 0;
}
static inline int tegra_bwmgr_notifier_unregister(struct notifier_block *nb)
{
return 0;
}
#endif /* CONFIG_TEGRA_BWMGR */
#endif /* __EMC_BWMGR_H */

View File

@@ -0,0 +1,22 @@
// SPDX-License-Identifier: GPL-2.0
// Copyright (c) 2022, NVIDIA CORPORATION, All rights reserved.
#ifndef _INCLUDE_MACH_ISO_CLIENT_H
#define _INCLUDE_MACH_ISO_CLIENT_H
enum tegra_iso_client {
TEGRA_ISO_CLIENT_DISP_0,
TEGRA_ISO_CLIENT_DISP_1,
TEGRA_ISO_CLIENT_DISP_2,
TEGRA_ISO_CLIENT_VI_0,
TEGRA_ISO_CLIENT_VI_1,
TEGRA_ISO_CLIENT_ISP_A,
TEGRA_ISO_CLIENT_ISP_B,
TEGRA_ISO_CLIENT_BBC_0,
TEGRA_ISO_CLIENT_TEGRA_CAMERA,
TEGRA_ISO_CLIENT_APE_ADMA,
TEGRA_ISO_CLIENT_EQOS,
TEGRA_ISO_CLIENT_COUNT
};
#endif /* _INCLUDE_MACH_ISO_CLIENT_H */

View File

@@ -0,0 +1,93 @@
// SPDX-License-Identifier: GPL-2.0
// Copyright (c) 2022, NVIDIA CORPORATION, All rights reserved.
#ifndef __TEGRA_MC_UTILS_H
#define __TEGRA_MC_UTILS_H
enum dram_types {
DRAM_TYPE_INVAL,
DRAM_TYPE_LPDDR5_16CH_ECC_1RANK,
DRAM_TYPE_LPDDR5_16CH_ECC_2RANK,
DRAM_TYPE_LPDDR5_8CH_ECC_1RANK,
DRAM_TYPE_LPDDR5_8CH_ECC_2RANK,
DRAM_TYPE_LPDDR5_4CH_ECC_1RANK,
DRAM_TYPE_LPDDR5_4CH_ECC_2RANK,
DRAM_TYPE_LPDDR5_16CH_1RANK,
DRAM_TYPE_LPDDR5_16CH_2RANK,
DRAM_TYPE_LPDDR5_8CH_1RANK,
DRAM_TYPE_LPDDR5_8CH_2RANK,
DRAM_TYPE_LPDDR5_4CH_1RANK,
DRAM_TYPE_LPDDR5_4CH_2RANK,
DRAM_TYPE_LPDDR4_16CH_ECC_1RANK,
DRAM_TYPE_LPDDR4_16CH_ECC_2RANK,
DRAM_TYPE_LPDDR4_8CH_ECC_1RANK,
DRAM_TYPE_LPDDR4_8CH_ECC_2RANK,
DRAM_TYPE_LPDDR4_4CH_ECC_1RANK,
DRAM_TYPE_LPDDR4_4CH_ECC_2RANK,
DRAM_TYPE_LPDDR4_16CH_1RANK,
DRAM_TYPE_LPDDR4_16CH_2RANK,
DRAM_TYPE_LPDDR4_8CH_1RANK,
DRAM_TYPE_LPDDR4_8CH_2RANK,
DRAM_TYPE_LPDDR4_4CH_1RANK,
DRAM_TYPE_LPDDR4_4CH_2RANK,
};
void tegra_mc_utils_init(void);
/*
* Utility API to convert the given frequency to Bandwidth.
*
* @freq Frequency to convert. It can be in any unit - the resulting Bandwidth
* will be in the same unit as passed. E.g KHz leads to KBps and Hz
* leads to Bps.
*
* Converts EMC clock frequency into theoretical BW. This
* does not account for a realistic utilization of the EMC bus. That is the
* various overheads (refresh, bank commands, etc) that a real system sees
* are not computed.
*
* Return: Converted Bandwidth.
*/
unsigned long emc_freq_to_bw(unsigned long freq);
/*
* Utility API to convert the given Bandwidth to frequency.
*
* @bw Bandwidth to convert. It can be in any unit - the resulting frequency
* will be in the same unit as passed. E.g KBps leads to KHz and Bps leads
* to Hz.
*
* Converts BW into theoretical EMC clock frequency.
*
* Return: Converted Frequency.
*/
unsigned long emc_bw_to_freq(unsigned long bw);
/*
* Return dram types or dram configuration.
*
* Return dram configuration based upon ecc/channel/Rank
*
* Return: dram type.
*/
enum dram_types tegra_dram_types(void);
/*
* Return Number of channels of dram.
*
* Return number of dram channels
*
* Return: dram channels.
*/
u8 get_dram_num_channels(void);
/*
* Return mc_clk from dram_clk.
*
* Return DRAM clock in MHZ to MC clk in MHz.
*
* dram_clk: dram clk in MHz
* Return: mc clk in MHz.
*/
unsigned long dram_clk_to_mc_clk(unsigned long dram_clk);
#endif /* __TEGRA_MC_UTILS_H */