diff --git a/drivers/platform/tegra/Makefile b/drivers/platform/tegra/Makefile index 47ebd653..18551301 100644 --- a/drivers/platform/tegra/Makefile +++ b/drivers/platform/tegra/Makefile @@ -2,3 +2,4 @@ # Copyright (c) 2022, NVIDIA CORPORATION. All rights reserved. obj-m += bad.o +obj-m += cactmon.o diff --git a/drivers/platform/tegra/cactmon.c b/drivers/platform/tegra/cactmon.c new file mode 100644 index 00000000..c4cd32c2 --- /dev/null +++ b/drivers/platform/tegra/cactmon.c @@ -0,0 +1,580 @@ +// SPDX-License-Identifier: GPL-2.0 +// Copyright (c) 2022, NVIDIA CORPORATION, All rights reserved. + +#include +#include +#include +#if IS_ENABLED(CONFIG_INTERCONNECT) +#include +#include +#endif +#include +#include + +#include + +/************ 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 "); +MODULE_LICENSE("GPL v2"); + diff --git a/include/dt-bindings/interconnect/tegra_icc_id.h b/include/dt-bindings/interconnect/tegra_icc_id.h new file mode 100644 index 00000000..1a8e0622 --- /dev/null +++ b/include/dt-bindings/interconnect/tegra_icc_id.h @@ -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 . + */ + +#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 */ diff --git a/include/linux/platform/tegra/actmon_common.h b/include/linux/platform/tegra/actmon_common.h new file mode 100644 index 00000000..b9896d30 --- /dev/null +++ b/include/linux/platform/tegra/actmon_common.h @@ -0,0 +1,198 @@ +// SPDX-License-Identifier: GPL-2.0 +// Copyright (c) 2022, NVIDIA CORPORATION, All rights reserved. + +#ifndef ACTMON_COMMON_H + +#include + +/* 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 */ diff --git a/include/linux/platform/tegra/emc_bwmgr.h b/include/linux/platform/tegra/emc_bwmgr.h new file mode 100644 index 00000000..9e8b5bad --- /dev/null +++ b/include/linux/platform/tegra/emc_bwmgr.h @@ -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 +#include +#include +#include +#include + +/* 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 */ diff --git a/include/linux/platform/tegra/iso_client.h b/include/linux/platform/tegra/iso_client.h new file mode 100644 index 00000000..2de3c3f7 --- /dev/null +++ b/include/linux/platform/tegra/iso_client.h @@ -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 */ diff --git a/include/linux/platform/tegra/mc_utils.h b/include/linux/platform/tegra/mc_utils.h new file mode 100644 index 00000000..609083cb --- /dev/null +++ b/include/linux/platform/tegra/mc_utils.h @@ -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 */