Resguardo inicial

This commit is contained in:
lastdragon
2026-09-06 12:53:21 -06:00
parent 662efe3c2a
commit ad2088d178
94853 changed files with 43200404 additions and 0 deletions
+83
View File
@@ -0,0 +1,83 @@
# SPDX-License-Identifier: GPL-2.0-only
config TEGRA_MC
bool "NVIDIA Tegra Memory Controller support"
default ARCH_TEGRA
depends on ARCH_TEGRA || (COMPILE_TEST && COMMON_CLK)
select INTERCONNECT
help
This driver supports the Memory Controller (MC) hardware found on
NVIDIA Tegra SoCs.
if TEGRA_MC
config TEGRA20_EMC
tristate "NVIDIA Tegra20 External Memory Controller driver"
default ARCH_TEGRA_2x_SOC
depends on ARCH_TEGRA_2x_SOC || COMPILE_TEST
select DEVFREQ_GOV_SIMPLE_ONDEMAND
select PM_DEVFREQ
select DDR
select TEGRA_EMC_COMMON
help
This driver is for the External Memory Controller (EMC) found on
Tegra20 chips. The EMC controls the external DRAM on the board.
This driver is required to change memory timings / clock rate for
external memory.
config TEGRA30_EMC
tristate "NVIDIA Tegra30 External Memory Controller driver"
default ARCH_TEGRA_3x_SOC
depends on ARCH_TEGRA_3x_SOC || COMPILE_TEST
select PM_OPP
select DDR
select TEGRA_EMC_COMMON
help
This driver is for the External Memory Controller (EMC) found on
Tegra30 chips. The EMC controls the external DRAM on the board.
This driver is required to change memory timings / clock rate for
external memory.
config TEGRA114_EMC
tristate "NVIDIA Tegra114 External Memory Controller driver"
default ARCH_TEGRA_114_SOC
depends on ARCH_TEGRA_114_SOC || COMPILE_TEST
select TEGRA124_CLK_EMC if ARCH_TEGRA
select PM_OPP
select TEGRA_EMC_COMMON
help
This driver is for the External Memory Controller (EMC) found on
Tegra114 chips. The EMC controls the external DRAM on the board.
This driver is required to change memory timings / clock rate for
external memory.
config TEGRA124_EMC
tristate "NVIDIA Tegra124 External Memory Controller driver"
default ARCH_TEGRA_124_SOC
depends on ARCH_TEGRA_124_SOC || COMPILE_TEST
select TEGRA124_CLK_EMC if ARCH_TEGRA
select PM_OPP
select TEGRA_EMC_COMMON
help
This driver is for the External Memory Controller (EMC) found on
Tegra124 chips. The EMC controls the external DRAM on the board.
This driver is required to change memory timings / clock rate for
external memory.
config TEGRA210_EMC_TABLE
bool
depends on ARCH_TEGRA_210_SOC || COMPILE_TEST
config TEGRA210_EMC
tristate "NVIDIA Tegra210 External Memory Controller driver"
depends on ARCH_TEGRA_210_SOC || COMPILE_TEST
select TEGRA210_EMC_TABLE
help
This driver is for the External Memory Controller (EMC) found on
Tegra210 chips. The EMC controls the external DRAM on the board.
This driver is required to change memory timings / clock rate for
external memory.
config TEGRA_EMC_COMMON
tristate
endif
+30
View File
@@ -0,0 +1,30 @@
# SPDX-License-Identifier: GPL-2.0
tegra-mc-y := mc.o
tegra-mc-$(CONFIG_ARCH_TEGRA_2x_SOC) += tegra20.o
tegra-mc-$(CONFIG_ARCH_TEGRA_3x_SOC) += tegra30.o
tegra-mc-$(CONFIG_ARCH_TEGRA_114_SOC) += tegra114.o
tegra-mc-$(CONFIG_ARCH_TEGRA_124_SOC) += tegra124.o
tegra-mc-$(CONFIG_ARCH_TEGRA_132_SOC) += tegra124.o
tegra-mc-$(CONFIG_ARCH_TEGRA_210_SOC) += tegra210.o
tegra-mc-$(CONFIG_ARCH_TEGRA_186_SOC) += tegra186.o
tegra-mc-$(CONFIG_ARCH_TEGRA_194_SOC) += tegra186.o tegra194.o
tegra-mc-$(CONFIG_ARCH_TEGRA_234_SOC) += tegra186.o tegra234.o
tegra-mc-$(CONFIG_ARCH_TEGRA_238_SOC) += tegra186.o tegra234.o tegra238.o
tegra-mc-$(CONFIG_ARCH_TEGRA_264_SOC) += tegra186.o tegra264.o
obj-$(CONFIG_TEGRA_MC) += tegra-mc.o
obj-$(CONFIG_TEGRA_EMC_COMMON) += tegra-emc-common.o
obj-$(CONFIG_TEGRA20_EMC) += tegra20-emc.o
obj-$(CONFIG_TEGRA30_EMC) += tegra30-emc.o
obj-$(CONFIG_TEGRA114_EMC) += tegra114-emc.o
obj-$(CONFIG_TEGRA124_EMC) += tegra124-emc.o
obj-$(CONFIG_TEGRA210_EMC_TABLE) += tegra210-emc-table.o
obj-$(CONFIG_TEGRA210_EMC) += tegra210-emc.o
obj-$(CONFIG_ARCH_TEGRA_186_SOC) += tegra186-emc.o
obj-$(CONFIG_ARCH_TEGRA_194_SOC) += tegra186-emc.o
obj-$(CONFIG_ARCH_TEGRA_234_SOC) += tegra186-emc.o
obj-$(CONFIG_ARCH_TEGRA_264_SOC) += tegra186-emc.o
tegra210-emc-y := tegra210-emc-core.o tegra210-emc-cc-r21021.o
File diff suppressed because it is too large Load Diff
+285
View File
@@ -0,0 +1,285 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Copyright (C) 2014-2026 NVIDIA CORPORATION. All rights reserved.
*/
#ifndef MEMORY_TEGRA_MC_H
#define MEMORY_TEGRA_MC_H
#include <linux/bits.h>
#include <linux/io.h>
#include <linux/types.h>
#include <soc/tegra/mc.h>
#define MC_INTSTATUS 0x00
/* Bit field of MC_INTSTATUS register */
#define MC_INT_DECERR_EMEM BIT(6)
#define MC_INT_INVALID_GART_PAGE BIT(7)
#define MC_INT_SECURITY_VIOLATION BIT(8)
#define MC_INT_ARBITRATION_EMEM BIT(9)
#define MC_INT_INVALID_SMMU_PAGE BIT(10)
#define MC_INT_INVALID_APB_ASID_UPDATE BIT(11)
#define MC_INT_DECERR_VPR BIT(12)
#define MC_INT_SECERR_SEC BIT(13)
#define MC_INT_DECERR_MTS BIT(16)
#define MC_INT_DECERR_GENERALIZED_CARVEOUT BIT(17)
#define MC_INT_DECERR_ROUTE_SANITY BIT(20)
#define MC_INT_DECERR_ROUTE_SANITY_GIC_MSI BIT(21)
#define MC_INTMASK 0x04
#define MC_GART_ERROR_REQ 0x30
#define MC_EMEM_ADR_CFG 0x54
#define MC_EMEM_ADR_CFG_EMEM_NUMDEV BIT(0)
#define MC_DECERR_EMEM_OTHERS_STATUS 0x58
#define MC_SECURITY_VIOLATION_STATUS 0x74
#define MC_EMEM_ARB_CFG 0x90
#define MC_EMEM_ARB_CFG_CYCLES_PER_UPDATE(x) ((x) & 0x1ff)
#define MC_EMEM_ARB_CFG_CYCLES_PER_UPDATE_MASK 0x1ff
#define MC_EMEM_ARB_OUTSTANDING_REQ 0x94
#define MC_EMEM_ARB_OUTSTANDING_REQ_HOLDOFF_OVERRIDE BIT(30)
#define MC_EMEM_ARB_OUTSTANDING_REQ_LIMIT_ENABLE BIT(31)
#define MC_EMEM_ARB_OUTSTANDING_REQ_MAX_MASK 0x1ff
#define MC_EMEM_ARB_TIMING_RCD 0x98
#define MC_EMEM_ARB_TIMING_RP 0x9c
#define MC_EMEM_ARB_TIMING_RC 0xa0
#define MC_EMEM_ARB_TIMING_RAS 0xa4
#define MC_EMEM_ARB_TIMING_FAW 0xa8
#define MC_EMEM_ARB_TIMING_RRD 0xac
#define MC_EMEM_ARB_TIMING_RAP2PRE 0xb0
#define MC_EMEM_ARB_TIMING_WAP2PRE 0xb4
#define MC_EMEM_ARB_TIMING_R2R 0xb8
#define MC_EMEM_ARB_TIMING_W2W 0xbc
#define MC_EMEM_ARB_TIMING_R2W 0xc0
#define MC_EMEM_ARB_TIMING_W2R 0xc4
#define MC_EMEM_ARB_MISC2 0xc8
#define MC_EMEM_ARB_DA_TURNS 0xd0
#define MC_EMEM_ARB_DA_COVERS 0xd4
#define MC_EMEM_ARB_MISC0 0xd8
#define MC_EMEM_ARB_MISC1 0xdc
#define MC_EMEM_ARB_RING1_THROTTLE 0xe0
#define MC_EMEM_ARB_OVERRIDE 0xe8
#define MC_EMEM_ARB_OVERRIDE_EACK_MASK 0x3
#define MC_TIMING_CONTROL_DBG 0xf8
#define MC_TIMING_CONTROL 0xfc
#define MC_TIMING_UPDATE BIT(0)
#define MC_GLOBAL_INTSTATUS 0xf24
/* Bit field of MC_ERR_STATUS_0 register */
#define MC_ERR_STATUS_RW BIT(16)
#define MC_ERR_STATUS_SECURITY BIT(17)
#define MC_ERR_STATUS_NONSECURE BIT(25)
#define MC_ERR_STATUS_WRITABLE BIT(26)
#define MC_ERR_STATUS_READABLE BIT(27)
#define MC_ERR_STATUS_GSC_ADR_HI_MASK 0xffff
#define MC_ERR_STATUS_GSC_ADR_HI_SHIFT 16
#define MC_ERR_STATUS_RT_ADR_HI_SHIFT 15
#define MC_ERR_STATUS_TYPE_SHIFT 28
#define MC_ERR_STATUS_TYPE_INVALID_SMMU_PAGE (0x6 << 28)
#define MC_ERR_STATUS_RT_TYPE_MASK (0xf << 28)
#define MC_ERR_STATUS_RT_TYPE_SHIFT 28
#define MC_ERR_STATUS_ADR_HI_SHIFT 20
#define MC_BROADCAST_CHANNEL ~0
/* Tegra264 specific registers */
/* Registers for MSS HUB */
#define MSS_HUB_GLOBAL_INTSTATUS_0 0x6000
#define MSS_HUBC_INTR BIT(0)
#define MSS_HUB_GLOBAL_MASK 0x7F00
#define MSS_HUB_GLOBAL_SHIFT 8
#define MSS_HUB_HUBC_INTSTATUS_0 0x6008
#define MSS_HUB_INTRSTATUS_0 0x600c
#define MSS_HUB_HUBC_INTMASK_0 0x6010
#define MSS_HUB_HUBC_SCRUB_DONE_INTMASK BIT(0)
#define MSS_HUB_HUBC_INTPRIORITY_0 0x6014
#define MSS_HUB_INTRMASK_0 0x6018
#define MSS_HUB_COALESCER_ERR_INTMASK BIT(0)
#define MSS_HUB_SMMU_BYPASS_ALLOW_ERR_INTMASK BIT(1)
#define MSS_HUB_ILLEGAL_TBUGRP_ID_INTMASK BIT(2)
#define MSS_HUB_MSI_ERR_INTMASK BIT(3)
#define MSS_HUB_POISON_RSP_INTMASK BIT(4)
#define MSS_HUB_RESTRICTED_ACCESS_ERR_INTMASK BIT(5)
#define MSS_HUB_RESERVED_PA_ERR_INTMASK BIT(6)
#define MSS_HUB_INTRPRIORITY_0 0x601c
#define MSS_HUB_SMMU_BYPASS_ALLOW_ERR_STATUS_0 0x6020
#define MSS_HUB_MSI_ERR_STATUS_0 0x6024
#define MSS_HUB_POISON_RSP_STATUS_0 0x6028
#define MSS_HUB_COALESCE_ERR_STATUS_0 0x60e0
#define MSS_HUB_COALESCE_ERR_ADR_HI_0 0x60e4
#define MSS_HUB_COALESCE_ERR_ADR_0 0x60e8
#define MSS_HUB_RESTRICTED_ACCESS_ERR_STATUS_0 0x638c
#define MSS_HUB_RESERVED_PA_ERR_STATUS_0 0x6390
#define MSS_HUB_ILLEGAL_TBUGRP_ID_ERR_STATUS_0 0x63b0
/* Registers for channels */
#define MC_CH_INTSTATUS_0 0x82d4
#define MC_CH_INTMASK_0 0x82d8
#define WCAM_ERR_INTMASK BIT(19)
#define MC_ERR_GENERALIZED_CARVEOUT_STATUS_1_0 0xbc74
/* Registers for MCF */
#define MCF_COMMON_INTSTATUS0_0_0 0xce04
#define MCF_INTSTATUS_0 0xce2c
#define MCF_INTMASK_0 0xce30
#define MCF_INTPRIORITY_0 0xce34
/* Registers for SBS */
#define MSS_SBS_INTSTATUS_0 0xec08
#define MSS_SBS_INTMASK_0 0xec0c
#define MSS_SBS_FILL_FIFO_ISO_OVERFLOW_INTMASK BIT(0)
#define MSS_SBS_FILL_FIFO_SISO_OVERFLOW_INTMASK BIT(1)
#define MSS_SBS_FILL_FIFO_NISO_OVERFLOW_INTMASK BIT(2)
/* Bit field of MC_ERR_ROUTE_SANITY_STATUS_0 register */
#define MC_ERR_ROUTE_SANITY_RW BIT(12)
#define MC_ERR_ROUTE_SANITY_SEC BIT(13)
#define ERR_GENERALIZED_APERTURE_ID_SHIFT 0
#define ERR_GENERALIZED_APERTURE_ID_MASK 0x1F
#define ERR_GENERALIZED_CARVEOUT_APERTURE_ID_SHIFT 5
#define ERR_GENERALIZED_CARVEOUT_APERTURE_ID_MASK 0x1F
static inline u32 tegra_mc_scale_percents(u64 val, unsigned int percents)
{
val = val * percents;
do_div(val, 100);
return min_t(u64, val, U32_MAX);
}
static inline struct tegra_mc *
icc_provider_to_tegra_mc(struct icc_provider *provider)
{
return container_of(provider, struct tegra_mc, provider);
}
static inline u32 mc_ch_readl(const struct tegra_mc *mc, int ch,
unsigned long offset)
{
if (!mc->bcast_ch_regs)
return 0;
if (ch == MC_BROADCAST_CHANNEL)
return readl_relaxed(mc->bcast_ch_regs + offset);
return readl_relaxed(mc->ch_regs[ch] + offset);
}
static inline void mc_ch_writel(const struct tegra_mc *mc, int ch,
u32 value, unsigned long offset)
{
if (!mc->bcast_ch_regs)
return;
if (ch == MC_BROADCAST_CHANNEL)
writel_relaxed(value, mc->bcast_ch_regs + offset);
else
writel_relaxed(value, mc->ch_regs[ch] + offset);
}
static inline u32 mc_readl(const struct tegra_mc *mc, unsigned long offset)
{
return readl_relaxed(mc->regs + offset);
}
static inline void mc_writel(const struct tegra_mc *mc, u32 value,
unsigned long offset)
{
writel_relaxed(value, mc->regs + offset);
}
extern const struct tegra_mc_reset_ops tegra_mc_reset_ops_common;
#ifdef CONFIG_ARCH_TEGRA_2x_SOC
extern const struct tegra_mc_soc tegra20_mc_soc;
#endif
#ifdef CONFIG_ARCH_TEGRA_3x_SOC
extern const struct tegra_mc_soc tegra30_mc_soc;
#endif
#ifdef CONFIG_ARCH_TEGRA_114_SOC
extern const struct tegra_mc_soc tegra114_mc_soc;
#endif
#ifdef CONFIG_ARCH_TEGRA_124_SOC
extern const struct tegra_mc_soc tegra124_mc_soc;
#endif
#ifdef CONFIG_ARCH_TEGRA_132_SOC
extern const struct tegra_mc_soc tegra132_mc_soc;
#endif
#ifdef CONFIG_ARCH_TEGRA_210_SOC
extern const struct tegra_mc_soc tegra210_mc_soc;
#endif
#ifdef CONFIG_ARCH_TEGRA_186_SOC
extern const struct tegra_mc_soc tegra186_mc_soc;
#endif
#ifdef CONFIG_ARCH_TEGRA_194_SOC
extern const struct tegra_mc_soc tegra194_mc_soc;
#endif
#ifdef CONFIG_ARCH_TEGRA_234_SOC
extern const struct tegra_mc_soc tegra234_mc_soc;
#endif
#if defined(CONFIG_ARCH_TEGRA_234_SOC) || defined(CONFIG_ARCH_TEGRA_238_SOC)
extern const struct tegra_mc_icc_ops tegra234_mc_icc_ops;
#endif
#ifdef CONFIG_ARCH_TEGRA_238_SOC
extern const struct tegra_mc_soc tegra238_mc_soc;
#endif
#ifdef CONFIG_ARCH_TEGRA_264_SOC
extern const struct tegra_mc_soc tegra264_mc_soc;
#endif
#if defined(CONFIG_ARCH_TEGRA_3x_SOC) || \
defined(CONFIG_ARCH_TEGRA_114_SOC) || \
defined(CONFIG_ARCH_TEGRA_124_SOC) || \
defined(CONFIG_ARCH_TEGRA_132_SOC) || \
defined(CONFIG_ARCH_TEGRA_210_SOC)
int tegra30_mc_probe(struct tegra_mc *mc);
extern const struct tegra_mc_ops tegra30_mc_ops;
#endif
#if defined(CONFIG_ARCH_TEGRA_186_SOC) || \
defined(CONFIG_ARCH_TEGRA_194_SOC) || \
defined(CONFIG_ARCH_TEGRA_234_SOC) || \
defined(CONFIG_ARCH_TEGRA_238_SOC) || \
defined(CONFIG_ARCH_TEGRA_264_SOC)
extern const struct tegra_mc_ops tegra186_mc_ops;
#endif
irqreturn_t tegra30_mc_handle_irq(int irq, void *data);
extern const irq_handler_t tegra30_mc_irq_handlers[1];
extern const char * const tegra_mc_status_names[32];
extern const char * const tegra20_mc_error_names[8];
/*
* These IDs are for internal use of Tegra ICC drivers. The ID numbers are
* chosen such that they don't conflict with the device-tree ICC node IDs.
*/
#define TEGRA_ICC_MC 1000
#define TEGRA_ICC_EMC 1001
#define TEGRA_ICC_EMEM 1002
#endif /* MEMORY_TEGRA_MC_H */
@@ -0,0 +1,143 @@
// SPDX-License-Identifier: GPL-2.0
#include <linux/device.h>
#include <linux/module.h>
#include <linux/mutex.h>
#include <linux/pm_opp.h>
#include "tegra-emc-common.h"
/**
* tegra_emc_rate_requests_init() - Initialize EMC rate request tracking
* @reqs: struct tegra_emc_rate_requests to initialize.
* @dev: EMC device.
*
* Initializes the rate request tracking state with default state
* (no active requests). Must be called before using @reqs with
* other functions.
*/
void tegra_emc_rate_requests_init(struct tegra_emc_rate_requests *reqs,
struct device *dev)
{
unsigned int i;
mutex_init(&reqs->rate_lock);
reqs->dev = dev;
for (i = 0; i < TEGRA_EMC_RATE_TYPE_MAX; i++) {
reqs->requested_rate[i].min_rate = 0;
reqs->requested_rate[i].max_rate = ULONG_MAX;
}
}
EXPORT_SYMBOL_GPL(tegra_emc_rate_requests_init);
/* Caller must hold reqs->rate_lock. */
static int tegra_emc_request_rate(struct tegra_emc_rate_requests *reqs,
unsigned long new_min_rate,
unsigned long new_max_rate,
enum tegra_emc_rate_request_type type)
{
struct tegra_emc_rate_request *req = reqs->requested_rate;
unsigned long min_rate = 0, max_rate = ULONG_MAX;
unsigned int i;
int err;
lockdep_assert_held(&reqs->rate_lock);
/* select minimum and maximum rates among the requested rates */
for (i = 0; i < TEGRA_EMC_RATE_TYPE_MAX; i++, req++) {
if (i == type) {
min_rate = max(new_min_rate, min_rate);
max_rate = min(new_max_rate, max_rate);
} else {
min_rate = max(req->min_rate, min_rate);
max_rate = min(req->max_rate, max_rate);
}
}
if (min_rate > max_rate) {
dev_err_ratelimited(reqs->dev, "%s: type %u: out of range: %lu %lu\n",
__func__, type, min_rate, max_rate);
return -ERANGE;
}
/*
* EMC rate-changes should go via OPP API because it manages voltage
* changes.
*/
err = dev_pm_opp_set_rate(reqs->dev, min_rate);
if (err)
return err;
reqs->requested_rate[type].min_rate = new_min_rate;
reqs->requested_rate[type].max_rate = new_max_rate;
return 0;
}
/**
* tegra_emc_set_min_rate() - Update minimum rate request for a request type
* @reqs: rate request tracking state
* @rate: new minimum rate in Hz requested by @type
* @type: type of request
*
* Records @rate as the new minimum rate request for @type, recalculates target
* rate based on all requests and applies new rate through the OPP API.
*
* Context: Sleeps. Requests to same @reqs are synchronized via mutex.
*
* Return:
* * %0 - success
* * %-ERANGE - request would cause minimum rate request to be higher than
* maximum rate request
* * other - setting new rate failed
*/
int tegra_emc_set_min_rate(struct tegra_emc_rate_requests *reqs,
unsigned long rate,
enum tegra_emc_rate_request_type type)
{
struct tegra_emc_rate_request *req = &reqs->requested_rate[type];
int ret;
mutex_lock(&reqs->rate_lock);
ret = tegra_emc_request_rate(reqs, rate, req->max_rate, type);
mutex_unlock(&reqs->rate_lock);
return ret;
}
EXPORT_SYMBOL_GPL(tegra_emc_set_min_rate);
/**
* tegra_emc_set_max_rate() - Update maximum rate request for a request type
* @reqs: rate request tracking state
* @rate: new maximum rate in Hz requested by @type
* @type: type of request
*
* Records @rate as the new maximum rate request for @type, recalculates target
* rate based on all requests and applies new rate through the OPP API.
*
* Context: Sleeps. Requests to same @reqs are synchronized via mutex.
*
* Return:
* * %0 - success
* * %-ERANGE - request would cause minimum rate request to be higher than
* maximum rate request
* * other - setting new rate failed
*/
int tegra_emc_set_max_rate(struct tegra_emc_rate_requests *reqs,
unsigned long rate,
enum tegra_emc_rate_request_type type)
{
struct tegra_emc_rate_request *req = &reqs->requested_rate[type];
int ret;
mutex_lock(&reqs->rate_lock);
ret = tegra_emc_request_rate(reqs, req->min_rate, rate, type);
mutex_unlock(&reqs->rate_lock);
return ret;
}
EXPORT_SYMBOL_GPL(tegra_emc_set_max_rate);
MODULE_DESCRIPTION("NVIDIA Tegra EMC common code");
MODULE_LICENSE("GPL");
@@ -0,0 +1,46 @@
/* SPDX-License-Identifier: GPL-2.0 */
#ifndef TEGRA_EMC_COMMON_H
#define TEGRA_EMC_COMMON_H
#include <linux/device.h>
#include <linux/mutex.h>
/**
* enum tegra_emc_rate_request_type - source of rate request
* @TEGRA_EMC_RATE_DEVFREQ: rate requested by devfreq governor
* @TEGRA_EMC_RATE_DEBUG: rate requested through debugfs knobs
* @TEGRA_EMC_RATE_ICC: rate requested by ICC framework
* @TEGRA_EMC_RATE_TYPE_MAX: number of valid request types
*/
enum tegra_emc_rate_request_type {
TEGRA_EMC_RATE_DEVFREQ,
TEGRA_EMC_RATE_DEBUG,
TEGRA_EMC_RATE_ICC,
TEGRA_EMC_RATE_TYPE_MAX,
};
struct tegra_emc_rate_request {
unsigned long min_rate;
unsigned long max_rate;
};
struct tegra_emc_rate_requests {
struct tegra_emc_rate_request requested_rate[TEGRA_EMC_RATE_TYPE_MAX];
/* Protects @requested_rate. */
struct mutex rate_lock;
struct device *dev;
};
void tegra_emc_rate_requests_init(struct tegra_emc_rate_requests *reqs,
struct device *dev);
int tegra_emc_set_min_rate(struct tegra_emc_rate_requests *reqs,
unsigned long rate,
enum tegra_emc_rate_request_type type);
int tegra_emc_set_max_rate(struct tegra_emc_rate_requests *reqs,
unsigned long rate,
enum tegra_emc_rate_request_type type);
#endif /* TEGRA_EMC_COMMON_H */
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,419 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (C) 2019-2025 NVIDIA CORPORATION. All rights reserved.
*/
#include <linux/clk.h>
#include <linux/debugfs.h>
#include <linux/module.h>
#include <linux/of_platform.h>
#include <linux/platform_device.h>
#include <soc/tegra/bpmp.h>
#include "mc.h"
struct tegra186_emc_dvfs {
unsigned long latency;
unsigned long rate;
};
struct tegra186_emc {
struct tegra_bpmp *bpmp;
struct device *dev;
struct clk *clk;
struct clk *clk_dbb;
struct tegra186_emc_dvfs *dvfs;
unsigned int num_dvfs;
struct {
struct dentry *root;
unsigned long min_rate;
unsigned long max_rate;
} debugfs;
struct icc_provider provider;
};
/*
* debugfs interface
*
* The memory controller driver exposes some files in debugfs that can be used
* to control the EMC frequency. The top-level directory can be found here:
*
* /sys/kernel/debug/emc
*
* It contains the following files:
*
* - available_rates: This file contains a list of valid, space-separated
* EMC frequencies.
*
* - min_rate: Writing a value to this file sets the given frequency as the
* floor of the permitted range. If this is higher than the currently
* configured EMC frequency, this will cause the frequency to be
* increased so that it stays within the valid range.
*
* - max_rate: Similarily to the min_rate file, writing a value to this file
* sets the given frequency as the ceiling of the permitted range. If
* the value is lower than the currently configured EMC frequency, this
* will cause the frequency to be decreased so that it stays within the
* valid range.
*/
static bool tegra186_emc_validate_rate(struct tegra186_emc *emc,
unsigned long rate)
{
unsigned int i;
for (i = 0; i < emc->num_dvfs; i++)
if (rate == emc->dvfs[i].rate)
return true;
return false;
}
static int tegra186_emc_debug_available_rates_show(struct seq_file *s,
void *data)
{
struct tegra186_emc *emc = s->private;
const char *prefix = "";
unsigned int i;
for (i = 0; i < emc->num_dvfs; i++) {
seq_printf(s, "%s%lu", prefix, emc->dvfs[i].rate);
prefix = " ";
}
seq_puts(s, "\n");
return 0;
}
DEFINE_SHOW_ATTRIBUTE(tegra186_emc_debug_available_rates);
static int tegra186_emc_debug_min_rate_get(void *data, u64 *rate)
{
struct tegra186_emc *emc = data;
*rate = emc->debugfs.min_rate;
return 0;
}
static int tegra186_emc_debug_min_rate_set(void *data, u64 rate)
{
struct tegra186_emc *emc = data;
int err;
if (!tegra186_emc_validate_rate(emc, rate))
return -EINVAL;
err = clk_set_min_rate(emc->clk, rate);
if (err < 0)
return err;
emc->debugfs.min_rate = rate;
return 0;
}
DEFINE_DEBUGFS_ATTRIBUTE(tegra186_emc_debug_min_rate_fops,
tegra186_emc_debug_min_rate_get,
tegra186_emc_debug_min_rate_set, "%llu\n");
static int tegra186_emc_debug_max_rate_get(void *data, u64 *rate)
{
struct tegra186_emc *emc = data;
*rate = emc->debugfs.max_rate;
return 0;
}
static int tegra186_emc_debug_max_rate_set(void *data, u64 rate)
{
struct tegra186_emc *emc = data;
int err;
if (!tegra186_emc_validate_rate(emc, rate))
return -EINVAL;
err = clk_set_max_rate(emc->clk, rate);
if (err < 0)
return err;
emc->debugfs.max_rate = rate;
return 0;
}
DEFINE_DEBUGFS_ATTRIBUTE(tegra186_emc_debug_max_rate_fops,
tegra186_emc_debug_max_rate_get,
tegra186_emc_debug_max_rate_set, "%llu\n");
static int tegra186_emc_get_emc_dvfs_latency(struct tegra186_emc *emc)
{
struct mrq_emc_dvfs_latency_response response;
struct tegra_bpmp_message msg;
unsigned int i;
int err;
memset(&msg, 0, sizeof(msg));
msg.mrq = MRQ_EMC_DVFS_LATENCY;
msg.tx.data = NULL;
msg.tx.size = 0;
msg.rx.data = &response;
msg.rx.size = sizeof(response);
err = tegra_bpmp_transfer(emc->bpmp, &msg);
if (err < 0) {
dev_err(emc->dev, "failed to EMC DVFS pairs: %d\n", err);
return err;
}
if (msg.rx.ret < 0) {
dev_err(emc->dev, "EMC DVFS MRQ failed: %d (BPMP error code)\n", msg.rx.ret);
return -EINVAL;
}
emc->debugfs.min_rate = ULONG_MAX;
emc->debugfs.max_rate = 0;
emc->num_dvfs = response.num_pairs;
emc->dvfs = devm_kmalloc_array(emc->dev, emc->num_dvfs, sizeof(*emc->dvfs), GFP_KERNEL);
if (!emc->dvfs)
return -ENOMEM;
dev_dbg(emc->dev, "%u DVFS pairs:\n", emc->num_dvfs);
for (i = 0; i < emc->num_dvfs; i++) {
emc->dvfs[i].rate = response.pairs[i].freq * 1000;
emc->dvfs[i].latency = response.pairs[i].latency;
if (emc->dvfs[i].rate < emc->debugfs.min_rate)
emc->debugfs.min_rate = emc->dvfs[i].rate;
if (emc->dvfs[i].rate > emc->debugfs.max_rate)
emc->debugfs.max_rate = emc->dvfs[i].rate;
dev_dbg(emc->dev, " %2u: %lu Hz -> %lu us\n", i,
emc->dvfs[i].rate, emc->dvfs[i].latency);
}
err = clk_set_rate_range(emc->clk, emc->debugfs.min_rate, emc->debugfs.max_rate);
if (err < 0) {
dev_err(emc->dev, "failed to set rate range [%lu-%lu] for %pC\n",
emc->debugfs.min_rate, emc->debugfs.max_rate, emc->clk);
return err;
}
emc->debugfs.root = debugfs_create_dir("emc", NULL);
debugfs_create_file("available_rates", 0444, emc->debugfs.root, emc,
&tegra186_emc_debug_available_rates_fops);
debugfs_create_file("min_rate", 0644, emc->debugfs.root, emc,
&tegra186_emc_debug_min_rate_fops);
debugfs_create_file("max_rate", 0644, emc->debugfs.root, emc,
&tegra186_emc_debug_max_rate_fops);
return 0;
}
/*
* tegra186_emc_icc_set_bw() - Set BW api for EMC provider
* @src: ICC node for External Memory Controller (EMC)
* @dst: ICC node for External Memory (DRAM)
*
* Do nothing here as info to BPMP-FW is now passed in the BW set function
* of the MC driver. BPMP-FW sets the final Freq based on the passed values.
*/
static int tegra186_emc_icc_set_bw(struct icc_node *src, struct icc_node *dst)
{
return 0;
}
static struct icc_node *
tegra186_emc_of_icc_xlate(const struct of_phandle_args *spec, void *data)
{
struct icc_provider *provider = data;
struct icc_node *node;
/* External Memory is the only possible ICC route */
list_for_each_entry(node, &provider->nodes, node_list) {
if (node->id != TEGRA_ICC_EMEM)
continue;
return node;
}
return ERR_PTR(-EPROBE_DEFER);
}
static int tegra186_emc_icc_get_init_bw(struct icc_node *node, u32 *avg, u32 *peak)
{
*avg = 0;
*peak = 0;
return 0;
}
static int tegra186_emc_interconnect_init(struct tegra186_emc *emc)
{
struct icc_node *node;
int err;
emc->provider.dev = emc->dev;
emc->provider.set = tegra186_emc_icc_set_bw;
emc->provider.data = &emc->provider;
emc->provider.aggregate = icc_std_aggregate;
emc->provider.xlate = tegra186_emc_of_icc_xlate;
emc->provider.get_bw = tegra186_emc_icc_get_init_bw;
icc_provider_init(&emc->provider);
/* create External Memory Controller node */
node = icc_node_create(TEGRA_ICC_EMC);
if (IS_ERR(node))
return PTR_ERR(node);
node->name = "External Memory Controller";
icc_node_add(node, &emc->provider);
/* link External Memory Controller to External Memory (DRAM) */
err = icc_link_create(node, TEGRA_ICC_EMEM);
if (err)
goto remove_nodes;
/* create External Memory node */
node = icc_node_create(TEGRA_ICC_EMEM);
if (IS_ERR(node)) {
err = PTR_ERR(node);
goto remove_nodes;
}
node->name = "External Memory (DRAM)";
icc_node_add(node, &emc->provider);
err = icc_provider_register(&emc->provider);
if (err)
goto remove_nodes;
return 0;
remove_nodes:
icc_nodes_remove(&emc->provider);
return dev_err_probe(emc->dev, err, "failed to initialize ICC\n");
}
static int tegra186_emc_probe(struct platform_device *pdev)
{
struct tegra_mc *mc = dev_get_drvdata(pdev->dev.parent);
struct tegra186_emc *emc;
int err;
emc = devm_kzalloc(&pdev->dev, sizeof(*emc), GFP_KERNEL);
if (!emc)
return -ENOMEM;
emc->bpmp = tegra_bpmp_get(&pdev->dev);
if (IS_ERR(emc->bpmp))
return dev_err_probe(&pdev->dev, PTR_ERR(emc->bpmp),
"failed to get BPMP\n");
emc->clk = devm_clk_get(&pdev->dev, "emc");
if (IS_ERR(emc->clk)) {
err = dev_err_probe(&pdev->dev, PTR_ERR(emc->clk),
"failed to get EMC clock\n");
goto put_bpmp;
}
emc->clk_dbb = devm_clk_get_optional_enabled(&pdev->dev, "dbb");
if (IS_ERR(emc->clk_dbb)) {
err = dev_err_probe(&pdev->dev, PTR_ERR(emc->clk_dbb),
"failed to get DBB clock\n");
goto put_bpmp;
}
platform_set_drvdata(pdev, emc);
emc->dev = &pdev->dev;
if (tegra_bpmp_mrq_is_supported(emc->bpmp, MRQ_EMC_DVFS_LATENCY)) {
err = tegra186_emc_get_emc_dvfs_latency(emc);
if (err)
goto put_bpmp;
}
if (mc && mc->soc->icc_ops) {
if (tegra_bpmp_mrq_is_supported(emc->bpmp, MRQ_BWMGR_INT)) {
mc->bwmgr_mrq_supported = true;
/*
* MC driver probe can't get BPMP reference as it gets probed
* earlier than BPMP. So, save the BPMP ref got from the EMC
* DT node in the mc->bpmp and use it in MC's icc_set hook.
*/
mc->bpmp = emc->bpmp;
barrier();
}
/*
* Initialize the ICC even if BPMP-FW doesn't support 'MRQ_BWMGR_INT'.
* Use the flag 'mc->bwmgr_mrq_supported' within MC driver and return
* EINVAL instead of passing the request to BPMP-FW later when the BW
* request is made by client with 'icc_set_bw()' call.
*/
err = tegra186_emc_interconnect_init(emc);
if (err) {
mc->bpmp = NULL;
goto put_bpmp;
}
}
return 0;
put_bpmp:
tegra_bpmp_put(emc->bpmp);
return err;
}
static void tegra186_emc_remove(struct platform_device *pdev)
{
struct tegra_mc *mc = dev_get_drvdata(pdev->dev.parent);
struct tegra186_emc *emc = platform_get_drvdata(pdev);
debugfs_remove_recursive(emc->debugfs.root);
mc->bpmp = NULL;
tegra_bpmp_put(emc->bpmp);
}
static const struct of_device_id tegra186_emc_of_match[] = {
#if defined(CONFIG_ARCH_TEGRA_186_SOC)
{ .compatible = "nvidia,tegra186-emc" },
#endif
#if defined(CONFIG_ARCH_TEGRA_194_SOC)
{ .compatible = "nvidia,tegra194-emc" },
#endif
#if defined(CONFIG_ARCH_TEGRA_234_SOC)
{ .compatible = "nvidia,tegra234-emc" },
#endif
#if defined(CONFIG_ARCH_TEGRA_264_SOC)
{ .compatible = "nvidia,tegra264-emc" },
#endif
{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(of, tegra186_emc_of_match);
static struct platform_driver tegra186_emc_driver = {
.driver = {
.name = "tegra186-emc",
.of_match_table = tegra186_emc_of_match,
.suppress_bind_attrs = true,
.sync_state = icc_sync_state,
},
.probe = tegra186_emc_probe,
.remove = tegra186_emc_remove,
};
module_platform_driver(tegra186_emc_driver);
MODULE_AUTHOR("Thierry Reding <treding@nvidia.com>");
MODULE_DESCRIPTION("NVIDIA Tegra186 External Memory Controller driver");
+927
View File
@@ -0,0 +1,927 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (C) 2017-2026 NVIDIA CORPORATION. All rights reserved.
*/
#include <linux/io.h>
#include <linux/iommu.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/of_platform.h>
#include <linux/platform_device.h>
#include <soc/tegra/mc.h>
#if defined(CONFIG_ARCH_TEGRA_186_SOC)
#include <dt-bindings/memory/tegra186-mc.h>
#endif
#include "mc.h"
#define MC_SID_STREAMID_OVERRIDE_MASK GENMASK(7, 0)
#define MC_SID_STREAMID_SECURITY_WRITE_ACCESS_DISABLED BIT(16)
#define MC_SID_STREAMID_SECURITY_OVERRIDE BIT(8)
static int tegra186_mc_probe(struct tegra_mc *mc)
{
struct platform_device *pdev = to_platform_device(mc->dev);
struct resource *res;
unsigned int i;
char name[8];
int err;
/*
* From Tegra264, the SID region is not present in MC node and BROADCAST is first.
* The common function 'tegra_mc_probe()' already maps first region entry from DT.
* Check if the SID region is present in DT then map BROADCAST. Otherwise, consider
* the first entry mapped in mc probe as the BROADCAST region. This is done to avoid
* mapping the region twice when SID is not present and keep backward compatibility.
*/
res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "sid");
if (res)
mc->bcast_ch_regs = devm_platform_ioremap_resource_byname(pdev, "broadcast");
else
mc->bcast_ch_regs = mc->regs;
if (IS_ERR(mc->bcast_ch_regs)) {
if (PTR_ERR(mc->bcast_ch_regs) == -EINVAL) {
dev_warn(&pdev->dev,
"Broadcast channel is missing, please update your device-tree\n");
mc->bcast_ch_regs = NULL;
goto populate;
}
return PTR_ERR(mc->bcast_ch_regs);
}
mc->ch_regs = devm_kcalloc(mc->dev, mc->soc->num_channels, sizeof(*mc->ch_regs),
GFP_KERNEL);
if (!mc->ch_regs)
return -ENOMEM;
for (i = 0; i < mc->soc->num_channels; i++) {
snprintf(name, sizeof(name), "ch%u", i);
mc->ch_regs[i] = devm_platform_ioremap_resource_byname(pdev, name);
if (IS_ERR(mc->ch_regs[i]))
return PTR_ERR(mc->ch_regs[i]);
}
populate:
err = of_platform_populate(mc->dev->of_node, NULL, NULL, mc->dev);
if (err < 0)
return err;
return 0;
}
static void tegra186_mc_remove(struct tegra_mc *mc)
{
of_platform_depopulate(mc->dev);
}
#if IS_ENABLED(CONFIG_IOMMU_API)
static void tegra186_mc_client_sid_override(struct tegra_mc *mc,
const struct tegra_mc_client *client,
unsigned int sid)
{
u32 value, old;
if (client->regs.sid.security == 0 && client->regs.sid.override == 0)
return;
value = readl(mc->regs + client->regs.sid.security);
if ((value & MC_SID_STREAMID_SECURITY_OVERRIDE) == 0) {
/*
* If the secure firmware has locked this down the override
* for this memory client, there's nothing we can do here.
*/
if (value & MC_SID_STREAMID_SECURITY_WRITE_ACCESS_DISABLED)
return;
/*
* Otherwise, try to set the override itself. Typically the
* secure firmware will never have set this configuration.
* Instead, it will either have disabled write access to
* this field, or it will already have set an explicit
* override itself.
*/
WARN_ON((value & MC_SID_STREAMID_SECURITY_OVERRIDE) == 0);
value |= MC_SID_STREAMID_SECURITY_OVERRIDE;
writel(value, mc->regs + client->regs.sid.security);
}
value = readl(mc->regs + client->regs.sid.override);
old = value & MC_SID_STREAMID_OVERRIDE_MASK;
if (old != sid) {
dev_dbg(mc->dev, "overriding SID %x for %s with %x\n", old,
client->name, sid);
writel(sid, mc->regs + client->regs.sid.override);
}
}
#endif
static int tegra186_mc_probe_device(struct tegra_mc *mc, struct device *dev)
{
#if IS_ENABLED(CONFIG_IOMMU_API)
struct of_phandle_args args;
unsigned int i, index = 0;
u32 sid;
if (!tegra_dev_iommu_get_stream_id(dev, &sid))
return 0;
while (!of_parse_phandle_with_args(dev->of_node, "interconnects", "#interconnect-cells",
index, &args)) {
if (args.np == mc->dev->of_node && args.args_count != 0) {
for (i = 0; i < mc->soc->num_clients; i++) {
const struct tegra_mc_client *client = &mc->soc->clients[i];
if (client->id == args.args[0])
tegra186_mc_client_sid_override(
mc, client,
sid & MC_SID_STREAMID_OVERRIDE_MASK);
}
}
index++;
}
#endif
return 0;
}
static void tegra186_mc_resume(struct tegra_mc *mc)
{
#if IS_ENABLED(CONFIG_IOMMU_API)
unsigned int i;
for (i = 0; i < mc->soc->num_clients; i++) {
const struct tegra_mc_client *client = &mc->soc->clients[i];
tegra186_mc_client_sid_override(mc, client, client->sid);
}
#endif
}
const struct tegra_mc_ops tegra186_mc_ops = {
.probe = tegra186_mc_probe,
.remove = tegra186_mc_remove,
.resume = tegra186_mc_resume,
.probe_device = tegra186_mc_probe_device,
};
#if defined(CONFIG_ARCH_TEGRA_186_SOC)
static const struct tegra_mc_client tegra186_mc_clients[] = {
{
.id = TEGRA186_MEMORY_CLIENT_PTCR,
.name = "ptcr",
.sid = TEGRA186_SID_PASSTHROUGH,
.regs = {
.sid = {
.override = 0x000,
.security = 0x004,
},
},
}, {
.id = TEGRA186_MEMORY_CLIENT_AFIR,
.name = "afir",
.sid = TEGRA186_SID_AFI,
.regs = {
.sid = {
.override = 0x070,
.security = 0x074,
},
},
}, {
.id = TEGRA186_MEMORY_CLIENT_HDAR,
.name = "hdar",
.sid = TEGRA186_SID_HDA,
.regs = {
.sid = {
.override = 0x0a8,
.security = 0x0ac,
},
},
}, {
.id = TEGRA186_MEMORY_CLIENT_HOST1XDMAR,
.name = "host1xdmar",
.sid = TEGRA186_SID_HOST1X,
.regs = {
.sid = {
.override = 0x0b0,
.security = 0x0b4,
},
},
}, {
.id = TEGRA186_MEMORY_CLIENT_NVENCSRD,
.name = "nvencsrd",
.sid = TEGRA186_SID_NVENC,
.regs = {
.sid = {
.override = 0x0e0,
.security = 0x0e4,
},
},
}, {
.id = TEGRA186_MEMORY_CLIENT_SATAR,
.name = "satar",
.sid = TEGRA186_SID_SATA,
.regs = {
.sid = {
.override = 0x0f8,
.security = 0x0fc,
},
},
}, {
.id = TEGRA186_MEMORY_CLIENT_MPCORER,
.name = "mpcorer",
.sid = TEGRA186_SID_PASSTHROUGH,
.regs = {
.sid = {
.override = 0x138,
.security = 0x13c,
},
},
}, {
.id = TEGRA186_MEMORY_CLIENT_NVENCSWR,
.name = "nvencswr",
.sid = TEGRA186_SID_NVENC,
.regs = {
.sid = {
.override = 0x158,
.security = 0x15c,
},
},
}, {
.id = TEGRA186_MEMORY_CLIENT_AFIW,
.name = "afiw",
.sid = TEGRA186_SID_AFI,
.regs = {
.sid = {
.override = 0x188,
.security = 0x18c,
},
},
}, {
.id = TEGRA186_MEMORY_CLIENT_HDAW,
.name = "hdaw",
.sid = TEGRA186_SID_HDA,
.regs = {
.sid = {
.override = 0x1a8,
.security = 0x1ac,
},
},
}, {
.id = TEGRA186_MEMORY_CLIENT_MPCOREW,
.name = "mpcorew",
.sid = TEGRA186_SID_PASSTHROUGH,
.regs = {
.sid = {
.override = 0x1c8,
.security = 0x1cc,
},
},
}, {
.id = TEGRA186_MEMORY_CLIENT_SATAW,
.name = "sataw",
.sid = TEGRA186_SID_SATA,
.regs = {
.sid = {
.override = 0x1e8,
.security = 0x1ec,
},
},
}, {
.id = TEGRA186_MEMORY_CLIENT_ISPRA,
.name = "ispra",
.sid = TEGRA186_SID_ISP,
.regs = {
.sid = {
.override = 0x220,
.security = 0x224,
},
},
}, {
.id = TEGRA186_MEMORY_CLIENT_ISPWA,
.name = "ispwa",
.sid = TEGRA186_SID_ISP,
.regs = {
.sid = {
.override = 0x230,
.security = 0x234,
},
},
}, {
.id = TEGRA186_MEMORY_CLIENT_ISPWB,
.name = "ispwb",
.sid = TEGRA186_SID_ISP,
.regs = {
.sid = {
.override = 0x238,
.security = 0x23c,
},
},
}, {
.id = TEGRA186_MEMORY_CLIENT_XUSB_HOSTR,
.name = "xusb_hostr",
.sid = TEGRA186_SID_XUSB_HOST,
.regs = {
.sid = {
.override = 0x250,
.security = 0x254,
},
},
}, {
.id = TEGRA186_MEMORY_CLIENT_XUSB_HOSTW,
.name = "xusb_hostw",
.sid = TEGRA186_SID_XUSB_HOST,
.regs = {
.sid = {
.override = 0x258,
.security = 0x25c,
},
},
}, {
.id = TEGRA186_MEMORY_CLIENT_XUSB_DEVR,
.name = "xusb_devr",
.sid = TEGRA186_SID_XUSB_DEV,
.regs = {
.sid = {
.override = 0x260,
.security = 0x264,
},
},
}, {
.id = TEGRA186_MEMORY_CLIENT_XUSB_DEVW,
.name = "xusb_devw",
.sid = TEGRA186_SID_XUSB_DEV,
.regs = {
.sid = {
.override = 0x268,
.security = 0x26c,
},
},
}, {
.id = TEGRA186_MEMORY_CLIENT_TSECSRD,
.name = "tsecsrd",
.sid = TEGRA186_SID_TSEC,
.regs = {
.sid = {
.override = 0x2a0,
.security = 0x2a4,
},
},
}, {
.id = TEGRA186_MEMORY_CLIENT_TSECSWR,
.name = "tsecswr",
.sid = TEGRA186_SID_TSEC,
.regs = {
.sid = {
.override = 0x2a8,
.security = 0x2ac,
},
},
}, {
.id = TEGRA186_MEMORY_CLIENT_GPUSRD,
.name = "gpusrd",
.sid = TEGRA186_SID_GPU,
.regs = {
.sid = {
.override = 0x2c0,
.security = 0x2c4,
},
},
}, {
.id = TEGRA186_MEMORY_CLIENT_GPUSWR,
.name = "gpuswr",
.sid = TEGRA186_SID_GPU,
.regs = {
.sid = {
.override = 0x2c8,
.security = 0x2cc,
},
},
}, {
.id = TEGRA186_MEMORY_CLIENT_SDMMCRA,
.name = "sdmmcra",
.sid = TEGRA186_SID_SDMMC1,
.regs = {
.sid = {
.override = 0x300,
.security = 0x304,
},
},
}, {
.id = TEGRA186_MEMORY_CLIENT_SDMMCRAA,
.name = "sdmmcraa",
.sid = TEGRA186_SID_SDMMC2,
.regs = {
.sid = {
.override = 0x308,
.security = 0x30c,
},
},
}, {
.id = TEGRA186_MEMORY_CLIENT_SDMMCR,
.name = "sdmmcr",
.sid = TEGRA186_SID_SDMMC3,
.regs = {
.sid = {
.override = 0x310,
.security = 0x314,
},
},
}, {
.id = TEGRA186_MEMORY_CLIENT_SDMMCRAB,
.name = "sdmmcrab",
.sid = TEGRA186_SID_SDMMC4,
.regs = {
.sid = {
.override = 0x318,
.security = 0x31c,
},
},
}, {
.id = TEGRA186_MEMORY_CLIENT_SDMMCWA,
.name = "sdmmcwa",
.sid = TEGRA186_SID_SDMMC1,
.regs = {
.sid = {
.override = 0x320,
.security = 0x324,
},
},
}, {
.id = TEGRA186_MEMORY_CLIENT_SDMMCWAA,
.name = "sdmmcwaa",
.sid = TEGRA186_SID_SDMMC2,
.regs = {
.sid = {
.override = 0x328,
.security = 0x32c,
},
},
}, {
.id = TEGRA186_MEMORY_CLIENT_SDMMCW,
.name = "sdmmcw",
.sid = TEGRA186_SID_SDMMC3,
.regs = {
.sid = {
.override = 0x330,
.security = 0x334,
},
},
}, {
.id = TEGRA186_MEMORY_CLIENT_SDMMCWAB,
.name = "sdmmcwab",
.sid = TEGRA186_SID_SDMMC4,
.regs = {
.sid = {
.override = 0x338,
.security = 0x33c,
},
},
}, {
.id = TEGRA186_MEMORY_CLIENT_VICSRD,
.name = "vicsrd",
.sid = TEGRA186_SID_VIC,
.regs = {
.sid = {
.override = 0x360,
.security = 0x364,
},
},
}, {
.id = TEGRA186_MEMORY_CLIENT_VICSWR,
.name = "vicswr",
.sid = TEGRA186_SID_VIC,
.regs = {
.sid = {
.override = 0x368,
.security = 0x36c,
},
},
}, {
.id = TEGRA186_MEMORY_CLIENT_VIW,
.name = "viw",
.sid = TEGRA186_SID_VI,
.regs = {
.sid = {
.override = 0x390,
.security = 0x394,
},
},
}, {
.id = TEGRA186_MEMORY_CLIENT_NVDECSRD,
.name = "nvdecsrd",
.sid = TEGRA186_SID_NVDEC,
.regs = {
.sid = {
.override = 0x3c0,
.security = 0x3c4,
},
},
}, {
.id = TEGRA186_MEMORY_CLIENT_NVDECSWR,
.name = "nvdecswr",
.sid = TEGRA186_SID_NVDEC,
.regs = {
.sid = {
.override = 0x3c8,
.security = 0x3cc,
},
},
}, {
.id = TEGRA186_MEMORY_CLIENT_APER,
.name = "aper",
.sid = TEGRA186_SID_APE,
.regs = {
.sid = {
.override = 0x3d0,
.security = 0x3d4,
},
},
}, {
.id = TEGRA186_MEMORY_CLIENT_APEW,
.name = "apew",
.sid = TEGRA186_SID_APE,
.regs = {
.sid = {
.override = 0x3d8,
.security = 0x3dc,
},
},
}, {
.id = TEGRA186_MEMORY_CLIENT_NVJPGSRD,
.name = "nvjpgsrd",
.sid = TEGRA186_SID_NVJPG,
.regs = {
.sid = {
.override = 0x3f0,
.security = 0x3f4,
},
},
}, {
.id = TEGRA186_MEMORY_CLIENT_NVJPGSWR,
.name = "nvjpgswr",
.sid = TEGRA186_SID_NVJPG,
.regs = {
.sid = {
.override = 0x3f8,
.security = 0x3fc,
},
},
}, {
.id = TEGRA186_MEMORY_CLIENT_SESRD,
.name = "sesrd",
.sid = TEGRA186_SID_SE,
.regs = {
.sid = {
.override = 0x400,
.security = 0x404,
},
},
}, {
.id = TEGRA186_MEMORY_CLIENT_SESWR,
.name = "seswr",
.sid = TEGRA186_SID_SE,
.regs = {
.sid = {
.override = 0x408,
.security = 0x40c,
},
},
}, {
.id = TEGRA186_MEMORY_CLIENT_ETRR,
.name = "etrr",
.sid = TEGRA186_SID_ETR,
.regs = {
.sid = {
.override = 0x420,
.security = 0x424,
},
},
}, {
.id = TEGRA186_MEMORY_CLIENT_ETRW,
.name = "etrw",
.sid = TEGRA186_SID_ETR,
.regs = {
.sid = {
.override = 0x428,
.security = 0x42c,
},
},
}, {
.id = TEGRA186_MEMORY_CLIENT_TSECSRDB,
.name = "tsecsrdb",
.sid = TEGRA186_SID_TSECB,
.regs = {
.sid = {
.override = 0x430,
.security = 0x434,
},
},
}, {
.id = TEGRA186_MEMORY_CLIENT_TSECSWRB,
.name = "tsecswrb",
.sid = TEGRA186_SID_TSECB,
.regs = {
.sid = {
.override = 0x438,
.security = 0x43c,
},
},
}, {
.id = TEGRA186_MEMORY_CLIENT_GPUSRD2,
.name = "gpusrd2",
.sid = TEGRA186_SID_GPU,
.regs = {
.sid = {
.override = 0x440,
.security = 0x444,
},
},
}, {
.id = TEGRA186_MEMORY_CLIENT_GPUSWR2,
.name = "gpuswr2",
.sid = TEGRA186_SID_GPU,
.regs = {
.sid = {
.override = 0x448,
.security = 0x44c,
},
},
}, {
.id = TEGRA186_MEMORY_CLIENT_AXISR,
.name = "axisr",
.sid = TEGRA186_SID_GPCDMA_0,
.regs = {
.sid = {
.override = 0x460,
.security = 0x464,
},
},
}, {
.id = TEGRA186_MEMORY_CLIENT_AXISW,
.name = "axisw",
.sid = TEGRA186_SID_GPCDMA_0,
.regs = {
.sid = {
.override = 0x468,
.security = 0x46c,
},
},
}, {
.id = TEGRA186_MEMORY_CLIENT_EQOSR,
.name = "eqosr",
.sid = TEGRA186_SID_EQOS,
.regs = {
.sid = {
.override = 0x470,
.security = 0x474,
},
},
}, {
.id = TEGRA186_MEMORY_CLIENT_EQOSW,
.name = "eqosw",
.sid = TEGRA186_SID_EQOS,
.regs = {
.sid = {
.override = 0x478,
.security = 0x47c,
},
},
}, {
.id = TEGRA186_MEMORY_CLIENT_UFSHCR,
.name = "ufshcr",
.sid = TEGRA186_SID_UFSHC,
.regs = {
.sid = {
.override = 0x480,
.security = 0x484,
},
},
}, {
.id = TEGRA186_MEMORY_CLIENT_UFSHCW,
.name = "ufshcw",
.sid = TEGRA186_SID_UFSHC,
.regs = {
.sid = {
.override = 0x488,
.security = 0x48c,
},
},
}, {
.id = TEGRA186_MEMORY_CLIENT_NVDISPLAYR,
.name = "nvdisplayr",
.sid = TEGRA186_SID_NVDISPLAY,
.regs = {
.sid = {
.override = 0x490,
.security = 0x494,
},
},
}, {
.id = TEGRA186_MEMORY_CLIENT_BPMPR,
.name = "bpmpr",
.sid = TEGRA186_SID_BPMP,
.regs = {
.sid = {
.override = 0x498,
.security = 0x49c,
},
},
}, {
.id = TEGRA186_MEMORY_CLIENT_BPMPW,
.name = "bpmpw",
.sid = TEGRA186_SID_BPMP,
.regs = {
.sid = {
.override = 0x4a0,
.security = 0x4a4,
},
},
}, {
.id = TEGRA186_MEMORY_CLIENT_BPMPDMAR,
.name = "bpmpdmar",
.sid = TEGRA186_SID_BPMP,
.regs = {
.sid = {
.override = 0x4a8,
.security = 0x4ac,
},
},
}, {
.id = TEGRA186_MEMORY_CLIENT_BPMPDMAW,
.name = "bpmpdmaw",
.sid = TEGRA186_SID_BPMP,
.regs = {
.sid = {
.override = 0x4b0,
.security = 0x4b4,
},
},
}, {
.id = TEGRA186_MEMORY_CLIENT_AONR,
.name = "aonr",
.sid = TEGRA186_SID_AON,
.regs = {
.sid = {
.override = 0x4b8,
.security = 0x4bc,
},
},
}, {
.id = TEGRA186_MEMORY_CLIENT_AONW,
.name = "aonw",
.sid = TEGRA186_SID_AON,
.regs = {
.sid = {
.override = 0x4c0,
.security = 0x4c4,
},
},
}, {
.id = TEGRA186_MEMORY_CLIENT_AONDMAR,
.name = "aondmar",
.sid = TEGRA186_SID_AON,
.regs = {
.sid = {
.override = 0x4c8,
.security = 0x4cc,
},
},
}, {
.id = TEGRA186_MEMORY_CLIENT_AONDMAW,
.name = "aondmaw",
.sid = TEGRA186_SID_AON,
.regs = {
.sid = {
.override = 0x4d0,
.security = 0x4d4,
},
},
}, {
.id = TEGRA186_MEMORY_CLIENT_SCER,
.name = "scer",
.sid = TEGRA186_SID_SCE,
.regs = {
.sid = {
.override = 0x4d8,
.security = 0x4dc,
},
},
}, {
.id = TEGRA186_MEMORY_CLIENT_SCEW,
.name = "scew",
.sid = TEGRA186_SID_SCE,
.regs = {
.sid = {
.override = 0x4e0,
.security = 0x4e4,
},
},
}, {
.id = TEGRA186_MEMORY_CLIENT_SCEDMAR,
.name = "scedmar",
.sid = TEGRA186_SID_SCE,
.regs = {
.sid = {
.override = 0x4e8,
.security = 0x4ec,
},
},
}, {
.id = TEGRA186_MEMORY_CLIENT_SCEDMAW,
.name = "scedmaw",
.sid = TEGRA186_SID_SCE,
.regs = {
.sid = {
.override = 0x4f0,
.security = 0x4f4,
},
},
}, {
.id = TEGRA186_MEMORY_CLIENT_APEDMAR,
.name = "apedmar",
.sid = TEGRA186_SID_APE,
.regs = {
.sid = {
.override = 0x4f8,
.security = 0x4fc,
},
},
}, {
.id = TEGRA186_MEMORY_CLIENT_APEDMAW,
.name = "apedmaw",
.sid = TEGRA186_SID_APE,
.regs = {
.sid = {
.override = 0x500,
.security = 0x504,
},
},
}, {
.id = TEGRA186_MEMORY_CLIENT_NVDISPLAYR1,
.name = "nvdisplayr1",
.sid = TEGRA186_SID_NVDISPLAY,
.regs = {
.sid = {
.override = 0x508,
.security = 0x50c,
},
},
}, {
.id = TEGRA186_MEMORY_CLIENT_VICSRD1,
.name = "vicsrd1",
.sid = TEGRA186_SID_VIC,
.regs = {
.sid = {
.override = 0x510,
.security = 0x514,
},
},
}, {
.id = TEGRA186_MEMORY_CLIENT_NVDECSRD1,
.name = "nvdecsrd1",
.sid = TEGRA186_SID_NVDEC,
.regs = {
.sid = {
.override = 0x518,
.security = 0x51c,
},
},
},
};
static const struct tegra_mc_intmask tegra186_mc_intmasks[] = {
{
.reg = MC_INTMASK,
.mask = MC_INT_DECERR_GENERALIZED_CARVEOUT | MC_INT_DECERR_MTS |
MC_INT_SECERR_SEC | MC_INT_DECERR_VPR |
MC_INT_SECURITY_VIOLATION | MC_INT_DECERR_EMEM,
},
};
const struct tegra_mc_soc tegra186_mc_soc = {
.num_clients = ARRAY_SIZE(tegra186_mc_clients),
.clients = tegra186_mc_clients,
.num_address_bits = 40,
.num_channels = 4,
.client_id_mask = 0xff,
.intmasks = tegra186_mc_intmasks,
.num_intmasks = ARRAY_SIZE(tegra186_mc_intmasks),
.ops = &tegra186_mc_ops,
.ch_intmask = 0x0000000f,
.global_intstatus_channel_shift = 0,
.regs = &tegra20_mc_regs,
.handle_irq = tegra30_mc_irq_handlers,
.num_interrupts = ARRAY_SIZE(tegra30_mc_irq_handlers),
.mc_addr_hi_mask = 0x3,
.mc_err_status_type_mask = (0x7 << 28),
};
#endif
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+796
View File
@@ -0,0 +1,796 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (C) 2012-2026 NVIDIA CORPORATION. All rights reserved.
*/
#include <linux/bitfield.h>
#include <linux/delay.h>
#include <linux/device.h>
#include <linux/mutex.h>
#include <linux/of.h>
#include <linux/slab.h>
#include <linux/string.h>
#include <dt-bindings/memory/tegra20-mc.h>
#include "mc.h"
#define MC_STAT_CONTROL 0x90
#define MC_STAT_EMC_CLOCK_LIMIT 0xa0
#define MC_STAT_EMC_CLOCKS 0xa4
#define MC_STAT_EMC_CONTROL_0 0xa8
#define MC_STAT_EMC_CONTROL_1 0xac
#define MC_STAT_EMC_COUNT_0 0xb8
#define MC_STAT_EMC_COUNT_1 0xbc
#define MC_STAT_CONTROL_CLIENT_ID GENMASK(13, 8)
#define MC_STAT_CONTROL_EVENT GENMASK(23, 16)
#define MC_STAT_CONTROL_PRI_EVENT GENMASK(25, 24)
#define MC_STAT_CONTROL_FILTER_CLIENT_ENABLE GENMASK(26, 26)
#define MC_STAT_CONTROL_FILTER_PRI GENMASK(29, 28)
#define MC_STAT_CONTROL_PRI_EVENT_HP 0
#define MC_STAT_CONTROL_PRI_EVENT_TM 1
#define MC_STAT_CONTROL_PRI_EVENT_BW 2
#define MC_STAT_CONTROL_FILTER_PRI_DISABLE 0
#define MC_STAT_CONTROL_FILTER_PRI_NO 1
#define MC_STAT_CONTROL_FILTER_PRI_YES 2
#define MC_STAT_CONTROL_EVENT_QUALIFIED 0
#define MC_STAT_CONTROL_EVENT_ANY_READ 1
#define MC_STAT_CONTROL_EVENT_ANY_WRITE 2
#define MC_STAT_CONTROL_EVENT_RD_WR_CHANGE 3
#define MC_STAT_CONTROL_EVENT_SUCCESSIVE 4
#define MC_STAT_CONTROL_EVENT_ARB_BANK_AA 5
#define MC_STAT_CONTROL_EVENT_ARB_BANK_BB 6
#define MC_STAT_CONTROL_EVENT_PAGE_MISS 7
#define MC_STAT_CONTROL_EVENT_AUTO_PRECHARGE 8
#define EMC_GATHER_RST (0 << 8)
#define EMC_GATHER_CLEAR (1 << 8)
#define EMC_GATHER_DISABLE (2 << 8)
#define EMC_GATHER_ENABLE (3 << 8)
#define MC_STAT_SAMPLE_TIME_USEC 16000
/* we store collected statistics as a fixed point values */
#define MC_FX_FRAC_SCALE 100
static DEFINE_MUTEX(tegra20_mc_stat_lock);
struct tegra20_mc_stat_gather {
unsigned int pri_filter;
unsigned int pri_event;
unsigned int result;
unsigned int client;
unsigned int event;
bool client_enb;
};
struct tegra20_mc_stat {
struct tegra20_mc_stat_gather gather0;
struct tegra20_mc_stat_gather gather1;
unsigned int sample_time_usec;
const struct tegra_mc *mc;
};
struct tegra20_mc_client_stat {
unsigned int events;
unsigned int arb_high_prio;
unsigned int arb_timeout;
unsigned int arb_bandwidth;
unsigned int rd_wr_change;
unsigned int successive;
unsigned int page_miss;
unsigned int auto_precharge;
unsigned int arb_bank_aa;
unsigned int arb_bank_bb;
};
static const struct tegra_mc_client tegra20_mc_clients[] = {
{
.id = 0x00,
.name = "display0a",
}, {
.id = 0x01,
.name = "display0ab",
}, {
.id = 0x02,
.name = "display0b",
}, {
.id = 0x03,
.name = "display0bb",
}, {
.id = 0x04,
.name = "display0c",
}, {
.id = 0x05,
.name = "display0cb",
}, {
.id = 0x06,
.name = "display1b",
}, {
.id = 0x07,
.name = "display1bb",
}, {
.id = 0x08,
.name = "eppup",
}, {
.id = 0x09,
.name = "g2pr",
}, {
.id = 0x0a,
.name = "g2sr",
}, {
.id = 0x0b,
.name = "mpeunifbr",
}, {
.id = 0x0c,
.name = "viruv",
}, {
.id = 0x0d,
.name = "avpcarm7r",
}, {
.id = 0x0e,
.name = "displayhc",
}, {
.id = 0x0f,
.name = "displayhcb",
}, {
.id = 0x10,
.name = "fdcdrd",
}, {
.id = 0x11,
.name = "g2dr",
}, {
.id = 0x12,
.name = "host1xdmar",
}, {
.id = 0x13,
.name = "host1xr",
}, {
.id = 0x14,
.name = "idxsrd",
}, {
.id = 0x15,
.name = "mpcorer",
}, {
.id = 0x16,
.name = "mpe_ipred",
}, {
.id = 0x17,
.name = "mpeamemrd",
}, {
.id = 0x18,
.name = "mpecsrd",
}, {
.id = 0x19,
.name = "ppcsahbdmar",
}, {
.id = 0x1a,
.name = "ppcsahbslvr",
}, {
.id = 0x1b,
.name = "texsrd",
}, {
.id = 0x1c,
.name = "vdebsevr",
}, {
.id = 0x1d,
.name = "vdember",
}, {
.id = 0x1e,
.name = "vdemcer",
}, {
.id = 0x1f,
.name = "vdetper",
}, {
.id = 0x20,
.name = "eppu",
}, {
.id = 0x21,
.name = "eppv",
}, {
.id = 0x22,
.name = "eppy",
}, {
.id = 0x23,
.name = "mpeunifbw",
}, {
.id = 0x24,
.name = "viwsb",
}, {
.id = 0x25,
.name = "viwu",
}, {
.id = 0x26,
.name = "viwv",
}, {
.id = 0x27,
.name = "viwy",
}, {
.id = 0x28,
.name = "g2dw",
}, {
.id = 0x29,
.name = "avpcarm7w",
}, {
.id = 0x2a,
.name = "fdcdwr",
}, {
.id = 0x2b,
.name = "host1xw",
}, {
.id = 0x2c,
.name = "ispw",
}, {
.id = 0x2d,
.name = "mpcorew",
}, {
.id = 0x2e,
.name = "mpecswr",
}, {
.id = 0x2f,
.name = "ppcsahbdmaw",
}, {
.id = 0x30,
.name = "ppcsahbslvw",
}, {
.id = 0x31,
.name = "vdebsevw",
}, {
.id = 0x32,
.name = "vdembew",
}, {
.id = 0x33,
.name = "vdetpmw",
},
};
#define TEGRA20_MC_RESET(_name, _control, _status, _reset, _bit) \
{ \
.name = #_name, \
.id = TEGRA20_MC_RESET_##_name, \
.control = _control, \
.status = _status, \
.reset = _reset, \
.bit = _bit, \
}
static const struct tegra_mc_reset tegra20_mc_resets[] = {
TEGRA20_MC_RESET(AVPC, 0x100, 0x140, 0x104, 0),
TEGRA20_MC_RESET(DC, 0x100, 0x144, 0x104, 1),
TEGRA20_MC_RESET(DCB, 0x100, 0x148, 0x104, 2),
TEGRA20_MC_RESET(EPP, 0x100, 0x14c, 0x104, 3),
TEGRA20_MC_RESET(2D, 0x100, 0x150, 0x104, 4),
TEGRA20_MC_RESET(HC, 0x100, 0x154, 0x104, 5),
TEGRA20_MC_RESET(ISP, 0x100, 0x158, 0x104, 6),
TEGRA20_MC_RESET(MPCORE, 0x100, 0x15c, 0x104, 7),
TEGRA20_MC_RESET(MPEA, 0x100, 0x160, 0x104, 8),
TEGRA20_MC_RESET(MPEB, 0x100, 0x164, 0x104, 9),
TEGRA20_MC_RESET(MPEC, 0x100, 0x168, 0x104, 10),
TEGRA20_MC_RESET(3D, 0x100, 0x16c, 0x104, 11),
TEGRA20_MC_RESET(PPCS, 0x100, 0x170, 0x104, 12),
TEGRA20_MC_RESET(VDE, 0x100, 0x174, 0x104, 13),
TEGRA20_MC_RESET(VI, 0x100, 0x178, 0x104, 14),
};
static int tegra20_mc_hotreset_assert(struct tegra_mc *mc,
const struct tegra_mc_reset *rst)
{
unsigned long flags;
u32 value;
spin_lock_irqsave(&mc->lock, flags);
value = mc_readl(mc, rst->reset);
mc_writel(mc, value & ~BIT(rst->bit), rst->reset);
spin_unlock_irqrestore(&mc->lock, flags);
return 0;
}
static int tegra20_mc_hotreset_deassert(struct tegra_mc *mc,
const struct tegra_mc_reset *rst)
{
unsigned long flags;
u32 value;
spin_lock_irqsave(&mc->lock, flags);
value = mc_readl(mc, rst->reset);
mc_writel(mc, value | BIT(rst->bit), rst->reset);
spin_unlock_irqrestore(&mc->lock, flags);
return 0;
}
static int tegra20_mc_block_dma(struct tegra_mc *mc,
const struct tegra_mc_reset *rst)
{
unsigned long flags;
u32 value;
spin_lock_irqsave(&mc->lock, flags);
value = mc_readl(mc, rst->control) & ~BIT(rst->bit);
mc_writel(mc, value, rst->control);
spin_unlock_irqrestore(&mc->lock, flags);
return 0;
}
static bool tegra20_mc_dma_idling(struct tegra_mc *mc,
const struct tegra_mc_reset *rst)
{
return mc_readl(mc, rst->status) == 0;
}
static int tegra20_mc_reset_status(struct tegra_mc *mc,
const struct tegra_mc_reset *rst)
{
return (mc_readl(mc, rst->reset) & BIT(rst->bit)) == 0;
}
static int tegra20_mc_unblock_dma(struct tegra_mc *mc,
const struct tegra_mc_reset *rst)
{
unsigned long flags;
u32 value;
spin_lock_irqsave(&mc->lock, flags);
value = mc_readl(mc, rst->control) | BIT(rst->bit);
mc_writel(mc, value, rst->control);
spin_unlock_irqrestore(&mc->lock, flags);
return 0;
}
static const struct tegra_mc_reset_ops tegra20_mc_reset_ops = {
.hotreset_assert = tegra20_mc_hotreset_assert,
.hotreset_deassert = tegra20_mc_hotreset_deassert,
.block_dma = tegra20_mc_block_dma,
.dma_idling = tegra20_mc_dma_idling,
.unblock_dma = tegra20_mc_unblock_dma,
.reset_status = tegra20_mc_reset_status,
};
static int tegra20_mc_icc_set(struct icc_node *src, struct icc_node *dst)
{
/*
* It should be possible to tune arbitration knobs here, but the
* default values are known to work well on all devices. Hence
* nothing to do here so far.
*/
return 0;
}
static int tegra20_mc_icc_aggreate(struct icc_node *node, u32 tag, u32 avg_bw,
u32 peak_bw, u32 *agg_avg, u32 *agg_peak)
{
/*
* ISO clients need to reserve extra bandwidth up-front because
* there could be high bandwidth pressure during initial filling
* of the client's FIFO buffers. Secondly, we need to take into
* account impurities of the memory subsystem.
*/
if (tag & TEGRA_MC_ICC_TAG_ISO)
peak_bw = tegra_mc_scale_percents(peak_bw, 300);
*agg_avg += avg_bw;
*agg_peak = max(*agg_peak, peak_bw);
return 0;
}
static struct icc_node_data *
tegra20_mc_of_icc_xlate_extended(const struct of_phandle_args *spec, void *data)
{
struct tegra_mc *mc = icc_provider_to_tegra_mc(data);
unsigned int i, idx = spec->args[0];
struct icc_node_data *ndata;
struct icc_node *node;
list_for_each_entry(node, &mc->provider.nodes, node_list) {
if (node->id != idx)
continue;
ndata = kzalloc_obj(*ndata);
if (!ndata)
return ERR_PTR(-ENOMEM);
ndata->node = node;
/* these clients are isochronous by default */
if (strstarts(node->name, "display") ||
strstarts(node->name, "vi"))
ndata->tag = TEGRA_MC_ICC_TAG_ISO;
else
ndata->tag = TEGRA_MC_ICC_TAG_DEFAULT;
return ndata;
}
for (i = 0; i < mc->soc->num_clients; i++) {
if (mc->soc->clients[i].id == idx)
return ERR_PTR(-EPROBE_DEFER);
}
dev_err(mc->dev, "invalid ICC client ID %u\n", idx);
return ERR_PTR(-EINVAL);
}
static const struct tegra_mc_icc_ops tegra20_mc_icc_ops = {
.xlate_extended = tegra20_mc_of_icc_xlate_extended,
.aggregate = tegra20_mc_icc_aggreate,
.set = tegra20_mc_icc_set,
};
static u32 tegra20_mc_stat_gather_control(const struct tegra20_mc_stat_gather *g)
{
u32 control;
control = FIELD_PREP(MC_STAT_CONTROL_EVENT, g->event);
control |= FIELD_PREP(MC_STAT_CONTROL_CLIENT_ID, g->client);
control |= FIELD_PREP(MC_STAT_CONTROL_PRI_EVENT, g->pri_event);
control |= FIELD_PREP(MC_STAT_CONTROL_FILTER_PRI, g->pri_filter);
control |= FIELD_PREP(MC_STAT_CONTROL_FILTER_CLIENT_ENABLE, g->client_enb);
return control;
}
static void tegra20_mc_stat_gather(struct tegra20_mc_stat *stat)
{
u32 clocks, count0, count1, control_0, control_1;
const struct tegra_mc *mc = stat->mc;
control_0 = tegra20_mc_stat_gather_control(&stat->gather0);
control_1 = tegra20_mc_stat_gather_control(&stat->gather1);
/*
* Reset statistic gathers state, select statistics collection mode
* and set clocks counter saturation limit to maximum.
*/
mc_writel(mc, 0x00000000, MC_STAT_CONTROL);
mc_writel(mc, control_0, MC_STAT_EMC_CONTROL_0);
mc_writel(mc, control_1, MC_STAT_EMC_CONTROL_1);
mc_writel(mc, 0xffffffff, MC_STAT_EMC_CLOCK_LIMIT);
mc_writel(mc, EMC_GATHER_ENABLE, MC_STAT_CONTROL);
fsleep(stat->sample_time_usec);
mc_writel(mc, EMC_GATHER_DISABLE, MC_STAT_CONTROL);
count0 = mc_readl(mc, MC_STAT_EMC_COUNT_0);
count1 = mc_readl(mc, MC_STAT_EMC_COUNT_1);
clocks = mc_readl(mc, MC_STAT_EMC_CLOCKS);
clocks = max(clocks / 100 / MC_FX_FRAC_SCALE, 1u);
stat->gather0.result = DIV_ROUND_UP(count0, clocks);
stat->gather1.result = DIV_ROUND_UP(count1, clocks);
}
static void tegra20_mc_stat_events(const struct tegra_mc *mc,
const struct tegra_mc_client *client0,
const struct tegra_mc_client *client1,
unsigned int pri_filter,
unsigned int pri_event,
unsigned int event,
unsigned int *result0,
unsigned int *result1)
{
struct tegra20_mc_stat stat = {};
stat.gather0.client = client0 ? client0->id : 0;
stat.gather0.pri_filter = pri_filter;
stat.gather0.client_enb = !!client0;
stat.gather0.pri_event = pri_event;
stat.gather0.event = event;
stat.gather1.client = client1 ? client1->id : 0;
stat.gather1.pri_filter = pri_filter;
stat.gather1.client_enb = !!client1;
stat.gather1.pri_event = pri_event;
stat.gather1.event = event;
stat.sample_time_usec = MC_STAT_SAMPLE_TIME_USEC;
stat.mc = mc;
tegra20_mc_stat_gather(&stat);
*result0 = stat.gather0.result;
*result1 = stat.gather1.result;
}
static void tegra20_mc_collect_stats(const struct tegra_mc *mc,
struct tegra20_mc_client_stat *stats)
{
const struct tegra_mc_client *client0, *client1;
unsigned int i;
/* collect memory controller utilization percent for each client */
for (i = 0; i < mc->soc->num_clients; i += 2) {
client0 = &mc->soc->clients[i];
client1 = &mc->soc->clients[i + 1];
if (i + 1 == mc->soc->num_clients)
client1 = NULL;
tegra20_mc_stat_events(mc, client0, client1,
MC_STAT_CONTROL_FILTER_PRI_DISABLE,
MC_STAT_CONTROL_PRI_EVENT_HP,
MC_STAT_CONTROL_EVENT_QUALIFIED,
&stats[i + 0].events,
&stats[i + 1].events);
}
/* collect more info from active clients */
for (i = 0; i < mc->soc->num_clients; i++) {
unsigned int clienta, clientb = mc->soc->num_clients;
for (client0 = NULL; i < mc->soc->num_clients; i++) {
if (stats[i].events) {
client0 = &mc->soc->clients[i];
clienta = i++;
break;
}
}
for (client1 = NULL; i < mc->soc->num_clients; i++) {
if (stats[i].events) {
client1 = &mc->soc->clients[i];
clientb = i;
break;
}
}
if (!client0 && !client1)
break;
tegra20_mc_stat_events(mc, client0, client1,
MC_STAT_CONTROL_FILTER_PRI_YES,
MC_STAT_CONTROL_PRI_EVENT_HP,
MC_STAT_CONTROL_EVENT_QUALIFIED,
&stats[clienta].arb_high_prio,
&stats[clientb].arb_high_prio);
tegra20_mc_stat_events(mc, client0, client1,
MC_STAT_CONTROL_FILTER_PRI_YES,
MC_STAT_CONTROL_PRI_EVENT_TM,
MC_STAT_CONTROL_EVENT_QUALIFIED,
&stats[clienta].arb_timeout,
&stats[clientb].arb_timeout);
tegra20_mc_stat_events(mc, client0, client1,
MC_STAT_CONTROL_FILTER_PRI_YES,
MC_STAT_CONTROL_PRI_EVENT_BW,
MC_STAT_CONTROL_EVENT_QUALIFIED,
&stats[clienta].arb_bandwidth,
&stats[clientb].arb_bandwidth);
tegra20_mc_stat_events(mc, client0, client1,
MC_STAT_CONTROL_FILTER_PRI_DISABLE,
MC_STAT_CONTROL_PRI_EVENT_HP,
MC_STAT_CONTROL_EVENT_RD_WR_CHANGE,
&stats[clienta].rd_wr_change,
&stats[clientb].rd_wr_change);
tegra20_mc_stat_events(mc, client0, client1,
MC_STAT_CONTROL_FILTER_PRI_DISABLE,
MC_STAT_CONTROL_PRI_EVENT_HP,
MC_STAT_CONTROL_EVENT_SUCCESSIVE,
&stats[clienta].successive,
&stats[clientb].successive);
tegra20_mc_stat_events(mc, client0, client1,
MC_STAT_CONTROL_FILTER_PRI_DISABLE,
MC_STAT_CONTROL_PRI_EVENT_HP,
MC_STAT_CONTROL_EVENT_PAGE_MISS,
&stats[clienta].page_miss,
&stats[clientb].page_miss);
}
}
static void tegra20_mc_printf_percents(struct seq_file *s,
const char *fmt,
unsigned int percents_fx)
{
char percents_str[8];
snprintf(percents_str, ARRAY_SIZE(percents_str), "%3u.%02u%%",
percents_fx / MC_FX_FRAC_SCALE, percents_fx % MC_FX_FRAC_SCALE);
seq_printf(s, fmt, percents_str);
}
static int tegra20_mc_stats_show(struct seq_file *s, void *unused)
{
const struct tegra_mc *mc = dev_get_drvdata(s->private);
struct tegra20_mc_client_stat *stats;
unsigned int i;
stats = kzalloc_objs(*stats, mc->soc->num_clients + 1);
if (!stats)
return -ENOMEM;
mutex_lock(&tegra20_mc_stat_lock);
tegra20_mc_collect_stats(mc, stats);
mutex_unlock(&tegra20_mc_stat_lock);
seq_puts(s, "Memory client Events Timeout High priority Bandwidth ARB RW change Successive Page miss\n");
seq_puts(s, "-----------------------------------------------------------------------------------------------------\n");
for (i = 0; i < mc->soc->num_clients; i++) {
seq_printf(s, "%-14s ", mc->soc->clients[i].name);
/* An event is generated when client performs R/W request. */
tegra20_mc_printf_percents(s, "%-9s", stats[i].events);
/*
* An event is generated based on the timeout (TM) signal
* accompanying a request for arbitration.
*/
tegra20_mc_printf_percents(s, "%-10s", stats[i].arb_timeout);
/*
* An event is generated based on the high-priority (HP) signal
* accompanying a request for arbitration.
*/
tegra20_mc_printf_percents(s, "%-16s", stats[i].arb_high_prio);
/*
* An event is generated based on the bandwidth (BW) signal
* accompanying a request for arbitration.
*/
tegra20_mc_printf_percents(s, "%-16s", stats[i].arb_bandwidth);
/*
* An event is generated when the memory controller switches
* between making a read request to making a write request.
*/
tegra20_mc_printf_percents(s, "%-12s", stats[i].rd_wr_change);
/*
* An even generated when the chosen client has wins arbitration
* when it was also the winner at the previous request. If a
* client makes N requests in a row that are honored, SUCCESSIVE
* will be counted (N-1) times. Large values for this event
* imply that if we were patient enough, all of those requests
* could have been coalesced.
*/
tegra20_mc_printf_percents(s, "%-13s", stats[i].successive);
/*
* An event is generated when the memory controller detects a
* page miss for the current request.
*/
tegra20_mc_printf_percents(s, "%-12s\n", stats[i].page_miss);
}
kfree(stats);
return 0;
}
static int tegra20_mc_probe(struct tegra_mc *mc)
{
debugfs_create_devm_seqfile(mc->dev, "stats", mc->debugfs.root,
tegra20_mc_stats_show);
return 0;
}
static irqreturn_t tegra20_mc_handle_irq(int irq, void *data)
{
struct tegra_mc *mc = data;
unsigned long status;
unsigned int bit;
/* mask all interrupts to avoid flooding */
status = mc_readl(mc, MC_INTSTATUS) & mc->soc->intmasks[0].mask;
if (!status)
return IRQ_NONE;
for_each_set_bit(bit, &status, 32) {
const char *error = tegra_mc_status_names[bit];
const char *direction = "read", *secure = "";
const char *client, *desc;
phys_addr_t addr;
u32 value, reg;
u8 id, type;
switch (BIT(bit)) {
case MC_INT_DECERR_EMEM:
reg = MC_DECERR_EMEM_OTHERS_STATUS;
value = mc_readl(mc, reg);
id = value & mc->soc->client_id_mask;
desc = tegra20_mc_error_names[2];
if (value & BIT(31))
direction = "write";
break;
case MC_INT_INVALID_GART_PAGE:
reg = MC_GART_ERROR_REQ;
value = mc_readl(mc, reg);
id = (value >> 1) & mc->soc->client_id_mask;
desc = tegra20_mc_error_names[2];
if (value & BIT(0))
direction = "write";
break;
case MC_INT_SECURITY_VIOLATION:
reg = MC_SECURITY_VIOLATION_STATUS;
value = mc_readl(mc, reg);
id = value & mc->soc->client_id_mask;
type = (value & BIT(30)) ? 4 : 3;
desc = tegra20_mc_error_names[type];
secure = "secure ";
if (value & BIT(31))
direction = "write";
break;
default:
continue;
}
client = mc->soc->clients[id].name;
addr = mc_readl(mc, reg + sizeof(u32));
dev_err_ratelimited(mc->dev, "%s: %s%s @%pa: %s (%s)\n",
client, secure, direction, &addr, error,
desc);
}
/* clear interrupts */
mc_writel(mc, status, MC_INTSTATUS);
return IRQ_HANDLED;
}
static const irq_handler_t tegra20_mc_irq_handlers[] = {
tegra20_mc_handle_irq
};
static const struct tegra_mc_ops tegra20_mc_ops = {
.probe = tegra20_mc_probe,
};
static const struct tegra_mc_intmask tegra20_mc_intmasks[] = {
{
.reg = MC_INTMASK,
.mask = MC_INT_SECURITY_VIOLATION | MC_INT_INVALID_GART_PAGE |
MC_INT_DECERR_EMEM,
},
};
const struct tegra_mc_soc tegra20_mc_soc = {
.clients = tegra20_mc_clients,
.num_clients = ARRAY_SIZE(tegra20_mc_clients),
.num_address_bits = 32,
.client_id_mask = 0x3f,
.intmasks = tegra20_mc_intmasks,
.num_intmasks = ARRAY_SIZE(tegra20_mc_intmasks),
.reset_ops = &tegra20_mc_reset_ops,
.resets = tegra20_mc_resets,
.num_resets = ARRAY_SIZE(tegra20_mc_resets),
.icc_ops = &tegra20_mc_icc_ops,
.ops = &tegra20_mc_ops,
.regs = &tegra20_mc_regs,
.handle_irq = tegra20_mc_irq_handlers,
.num_interrupts = ARRAY_SIZE(tegra20_mc_irq_handlers),
.mc_err_status_type_mask = (0x7 << 28),
};
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,89 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (c) 2020, NVIDIA CORPORATION. All rights reserved.
*/
#include <linux/of_reserved_mem.h>
#include "tegra210-emc.h"
#define TEGRA_EMC_MAX_FREQS 16
static int tegra210_emc_table_device_init(struct reserved_mem *rmem,
struct device *dev)
{
struct tegra210_emc *emc = dev_get_drvdata(dev);
struct tegra210_emc_timing *timings;
unsigned int i, count = 0;
timings = memremap(rmem->base, rmem->size, MEMREMAP_WB);
if (!timings) {
dev_err(dev, "failed to map EMC table\n");
return -ENOMEM;
}
for (i = 0; i < TEGRA_EMC_MAX_FREQS; i++) {
if (timings[i].revision == 0)
break;
count++;
}
/* only the nominal and derated tables are expected */
if (emc->derated) {
dev_warn(dev, "excess EMC table '%s'\n", rmem->name);
goto out;
}
if (emc->nominal) {
if (count != emc->num_timings) {
dev_warn(dev, "%u derated vs. %u nominal entries\n",
count, emc->num_timings);
memunmap(timings);
return -EINVAL;
}
emc->derated = timings;
} else {
emc->num_timings = count;
emc->nominal = timings;
}
out:
/* keep track of which table this is */
rmem->priv = timings;
return 0;
}
static void tegra210_emc_table_device_release(struct reserved_mem *rmem,
struct device *dev)
{
struct tegra210_emc_timing *timings = rmem->priv;
struct tegra210_emc *emc = dev_get_drvdata(dev);
if ((emc->nominal && timings != emc->nominal) &&
(emc->derated && timings != emc->derated))
dev_warn(dev, "trying to release unassigned EMC table '%s'\n",
rmem->name);
memunmap(timings);
}
static int tegra210_emc_table_init(unsigned long node,
struct reserved_mem *rmem)
{
pr_debug("Tegra210 EMC table at %pa, size %lu bytes\n", &rmem->base,
(unsigned long)rmem->size);
return 0;
}
static const struct reserved_mem_ops tegra210_emc_table_ops = {
.node_init = tegra210_emc_table_init,
.device_init = tegra210_emc_table_device_init,
.device_release = tegra210_emc_table_device_release,
};
RESERVEDMEM_OF_DECLARE(tegra210_emc_table, "nvidia,tegra210-emc-table",
&tegra210_emc_table_ops);
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,50 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Copyright (c) 2015-2020, NVIDIA CORPORATION. All rights reserved.
*/
#ifndef TEGRA210_MC_H
#define TEGRA210_MC_H
#include "mc.h"
/* register definitions */
#define MC_LATENCY_ALLOWANCE_AVPC_0 0x2e4
#define MC_LATENCY_ALLOWANCE_HC_0 0x310
#define MC_LATENCY_ALLOWANCE_HC_1 0x314
#define MC_LATENCY_ALLOWANCE_MPCORE_0 0x320
#define MC_LATENCY_ALLOWANCE_NVENC_0 0x328
#define MC_LATENCY_ALLOWANCE_PPCS_0 0x344
#define MC_LATENCY_ALLOWANCE_PPCS_1 0x348
#define MC_LATENCY_ALLOWANCE_ISP2_0 0x370
#define MC_LATENCY_ALLOWANCE_ISP2_1 0x374
#define MC_LATENCY_ALLOWANCE_XUSB_0 0x37c
#define MC_LATENCY_ALLOWANCE_XUSB_1 0x380
#define MC_LATENCY_ALLOWANCE_TSEC_0 0x390
#define MC_LATENCY_ALLOWANCE_VIC_0 0x394
#define MC_LATENCY_ALLOWANCE_VI2_0 0x398
#define MC_LATENCY_ALLOWANCE_GPU_0 0x3ac
#define MC_LATENCY_ALLOWANCE_SDMMCA_0 0x3b8
#define MC_LATENCY_ALLOWANCE_SDMMCAA_0 0x3bc
#define MC_LATENCY_ALLOWANCE_SDMMC_0 0x3c0
#define MC_LATENCY_ALLOWANCE_SDMMCAB_0 0x3c4
#define MC_LATENCY_ALLOWANCE_GPU2_0 0x3e8
#define MC_LATENCY_ALLOWANCE_NVDEC_0 0x3d8
#define MC_MLL_MPCORER_PTSA_RATE 0x44c
#define MC_FTOP_PTSA_RATE 0x50c
#define MC_EMEM_ARB_TIMING_RFCPB 0x6c0
#define MC_EMEM_ARB_TIMING_CCDMW 0x6c4
#define MC_EMEM_ARB_REFPB_HP_CTRL 0x6f0
#define MC_EMEM_ARB_REFPB_BANK_CTRL 0x6f4
#define MC_PTSA_GRANT_DECREMENT 0x960
#define MC_EMEM_ARB_DHYST_CTRL 0xbcc
#define MC_EMEM_ARB_DHYST_TIMEOUT_UTIL_0 0xbd0
#define MC_EMEM_ARB_DHYST_TIMEOUT_UTIL_1 0xbd4
#define MC_EMEM_ARB_DHYST_TIMEOUT_UTIL_2 0xbd8
#define MC_EMEM_ARB_DHYST_TIMEOUT_UTIL_3 0xbdc
#define MC_EMEM_ARB_DHYST_TIMEOUT_UTIL_4 0xbe0
#define MC_EMEM_ARB_DHYST_TIMEOUT_UTIL_5 0xbe4
#define MC_EMEM_ARB_DHYST_TIMEOUT_UTIL_6 0xbe8
#define MC_EMEM_ARB_DHYST_TIMEOUT_UTIL_7 0xbec
#endif
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+391
View File
@@ -0,0 +1,391 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (C) 2026, NVIDIA CORPORATION. All rights reserved.
*/
#include <soc/tegra/mc.h>
#include <dt-bindings/memory/tegra234-mc.h>
#include <dt-bindings/memory/nvidia,tegra238-mc.h>
#include <linux/interconnect.h>
#include <linux/tegra-icc.h>
#include <soc/tegra/bpmp.h>
#include "mc.h"
static const struct tegra_mc_client tegra238_mc_clients[] = {
{
.id = TEGRA234_MEMORY_CLIENT_HDAR,
.name = "hdar",
.bpmp_id = TEGRA_ICC_BPMP_HDA,
.type = TEGRA_ICC_ISO_AUDIO,
.sid = TEGRA238_SID_HDA,
.regs = {
.sid = {
.override = 0xa8,
.security = 0xac,
},
},
}, {
.id = TEGRA234_MEMORY_CLIENT_HDAW,
.name = "hdaw",
.bpmp_id = TEGRA_ICC_BPMP_HDA,
.type = TEGRA_ICC_ISO_AUDIO,
.sid = TEGRA238_SID_HDA,
.regs = {
.sid = {
.override = 0x1a8,
.security = 0x1ac,
},
},
}, {
.id = TEGRA234_MEMORY_CLIENT_SDMMCRAB,
.name = "sdmmcrab",
.bpmp_id = TEGRA_ICC_BPMP_SDMMC_4,
.type = TEGRA_ICC_NISO,
.sid = TEGRA238_SID_SDMMC4A,
.regs = {
.sid = {
.override = 0x318,
.security = 0x31c,
},
},
}, {
.id = TEGRA234_MEMORY_CLIENT_SDMMCWAB,
.name = "sdmmcwab",
.bpmp_id = TEGRA_ICC_BPMP_SDMMC_4,
.type = TEGRA_ICC_NISO,
.sid = TEGRA238_SID_SDMMC4A,
.regs = {
.sid = {
.override = 0x338,
.security = 0x33c,
},
},
}, {
.id = TEGRA234_MEMORY_CLIENT_APER,
.name = "aper",
.bpmp_id = TEGRA_ICC_BPMP_APE,
.type = TEGRA_ICC_ISO_AUDIO,
.sid = TEGRA238_SID_ISO_APE0,
.regs = {
.sid = {
.override = 0x3d0,
.security = 0x3d4,
},
},
}, {
.id = TEGRA234_MEMORY_CLIENT_APEW,
.name = "apew",
.bpmp_id = TEGRA_ICC_BPMP_APE,
.type = TEGRA_ICC_ISO_AUDIO,
.sid = TEGRA238_SID_ISO_APE0,
.regs = {
.sid = {
.override = 0x3d8,
.security = 0x3dc,
},
},
}, {
.id = TEGRA234_MEMORY_CLIENT_NVDISPLAYR,
.name = "nvdisplayr",
.bpmp_id = TEGRA_ICC_BPMP_DISPLAY,
.type = TEGRA_ICC_ISO_DISPLAY,
.sid = TEGRA238_SID_ISO_NVDISPLAY,
.regs = {
.sid = {
.override = 0x490,
.security = 0x494,
},
},
}, {
.id = TEGRA234_MEMORY_CLIENT_NVDISPLAYR1,
.name = "nvdisplayr1",
.bpmp_id = TEGRA_ICC_BPMP_DISPLAY,
.type = TEGRA_ICC_ISO_DISPLAY,
.sid = TEGRA238_SID_ISO_NVDISPLAY,
.regs = {
.sid = {
.override = 0x508,
.security = 0x50c,
},
},
}, {
.id = TEGRA234_MEMORY_CLIENT_BPMPR,
.name = "bpmpr",
.sid = TEGRA238_SID_BPMP,
.regs = {
.sid = {
.override = 0x498,
.security = 0x49c,
},
},
}, {
.id = TEGRA234_MEMORY_CLIENT_BPMPW,
.name = "bpmpw",
.sid = TEGRA238_SID_BPMP,
.regs = {
.sid = {
.override = 0x4a0,
.security = 0x4a4,
},
},
}, {
.id = TEGRA234_MEMORY_CLIENT_BPMPDMAR,
.name = "bpmpdmar",
.sid = TEGRA238_SID_BPMP,
.regs = {
.sid = {
.override = 0x4a8,
.security = 0x4ac,
},
},
}, {
.id = TEGRA234_MEMORY_CLIENT_BPMPDMAW,
.name = "bpmpdmaw",
.sid = TEGRA238_SID_BPMP,
.regs = {
.sid = {
.override = 0x4b0,
.security = 0x4b4,
},
},
}, {
.id = TEGRA234_MEMORY_CLIENT_APEDMAR,
.name = "apedmar",
.bpmp_id = TEGRA_ICC_BPMP_APEDMA,
.type = TEGRA_ICC_ISO_AUDIO,
.sid = TEGRA238_SID_ISO_APE1,
.regs = {
.sid = {
.override = 0x4f8,
.security = 0x4fc,
},
},
}, {
.id = TEGRA234_MEMORY_CLIENT_APEDMAW,
.name = "apedmaw",
.bpmp_id = TEGRA_ICC_BPMP_APEDMA,
.type = TEGRA_ICC_ISO_AUDIO,
.sid = TEGRA238_SID_ISO_APE1,
.regs = {
.sid = {
.override = 0x500,
.security = 0x504,
},
},
}, {
.id = TEGRA234_MEMORY_CLIENT_VICSRD,
.name = "vicsrd",
.bpmp_id = TEGRA_ICC_BPMP_VIC,
.type = TEGRA_ICC_NISO,
.sid = TEGRA238_SID_VIC,
.regs = {
.sid = {
.override = 0x360,
.security = 0x364,
},
},
}, {
.id = TEGRA234_MEMORY_CLIENT_VICSWR,
.name = "vicswr",
.bpmp_id = TEGRA_ICC_BPMP_VIC,
.type = TEGRA_ICC_NISO,
.sid = TEGRA238_SID_VIC,
.regs = {
.sid = {
.override = 0x368,
.security = 0x36c,
},
},
}, {
.id = TEGRA234_MEMORY_CLIENT_NVDECSRD,
.name = "nvdecsrd",
.bpmp_id = TEGRA_ICC_BPMP_NVDEC,
.type = TEGRA_ICC_NISO,
.sid = TEGRA238_SID_NVDEC,
.regs = {
.sid = {
.override = 0x3c0,
.security = 0x3c4,
},
},
}, {
.id = TEGRA234_MEMORY_CLIENT_NVDECSWR,
.name = "nvdecswr",
.bpmp_id = TEGRA_ICC_BPMP_NVDEC,
.type = TEGRA_ICC_NISO,
.sid = TEGRA238_SID_NVDEC,
.regs = {
.sid = {
.override = 0x3c8,
.security = 0x3cc,
},
},
}, {
.id = TEGRA234_MEMORY_CLIENT_NVENCSRD,
.name = "nvencsrd",
.bpmp_id = TEGRA_ICC_BPMP_NVENC,
.type = TEGRA_ICC_NISO,
.sid = TEGRA238_SID_NVENC,
.regs = {
.sid = {
.override = 0xe0,
.security = 0xe4,
},
},
}, {
.id = TEGRA234_MEMORY_CLIENT_NVENCSWR,
.name = "nvencswr",
.bpmp_id = TEGRA_ICC_BPMP_NVENC,
.type = TEGRA_ICC_NISO,
.sid = TEGRA238_SID_NVENC,
.regs = {
.sid = {
.override = 0x158,
.security = 0x15c,
},
},
}, {
.id = TEGRA234_MEMORY_CLIENT_PCIE0R,
.name = "pcie0r",
.bpmp_id = TEGRA_ICC_BPMP_PCIE_0,
.type = TEGRA_ICC_NISO,
.sid = TEGRA238_SID_PCIE0,
.regs = {
.sid = {
.override = 0x6c0,
.security = 0x6c4,
},
},
}, {
.id = TEGRA234_MEMORY_CLIENT_PCIE0W,
.name = "pcie0w",
.bpmp_id = TEGRA_ICC_BPMP_PCIE_0,
.type = TEGRA_ICC_NISO,
.sid = TEGRA238_SID_PCIE0,
.regs = {
.sid = {
.override = 0x6c8,
.security = 0x6cc,
},
},
}, {
.id = TEGRA234_MEMORY_CLIENT_PCIE1R,
.name = "pcie1r",
.bpmp_id = TEGRA_ICC_BPMP_PCIE_1,
.type = TEGRA_ICC_NISO,
.sid = TEGRA238_SID_PCIE1,
.regs = {
.sid = {
.override = 0x6d0,
.security = 0x6d4,
},
},
}, {
.id = TEGRA234_MEMORY_CLIENT_PCIE1W,
.name = "pcie1w",
.bpmp_id = TEGRA_ICC_BPMP_PCIE_1,
.type = TEGRA_ICC_NISO,
.sid = TEGRA238_SID_PCIE1,
.regs = {
.sid = {
.override = 0x6d8,
.security = 0x6dc,
},
},
}, {
.id = TEGRA234_MEMORY_CLIENT_PCIE2AR,
.name = "pcie2ar",
.bpmp_id = TEGRA_ICC_BPMP_PCIE_2,
.type = TEGRA_ICC_NISO,
.sid = TEGRA238_SID_PCIE2,
.regs = {
.sid = {
.override = 0x6e0,
.security = 0x6e4,
},
},
}, {
.id = TEGRA234_MEMORY_CLIENT_PCIE2AW,
.name = "pcie2aw",
.bpmp_id = TEGRA_ICC_BPMP_PCIE_2,
.type = TEGRA_ICC_NISO,
.sid = TEGRA238_SID_PCIE2,
.regs = {
.sid = {
.override = 0x6e8,
.security = 0x6ec,
},
},
}, {
.id = TEGRA234_MEMORY_CLIENT_PCIE3R,
.name = "pcie3r",
.bpmp_id = TEGRA_ICC_BPMP_PCIE_3,
.type = TEGRA_ICC_NISO,
.sid = TEGRA238_SID_PCIE3,
.regs = {
.sid = {
.override = 0x6f0,
.security = 0x6f4,
},
},
}, {
.id = TEGRA234_MEMORY_CLIENT_PCIE3W,
.name = "pcie3w",
.bpmp_id = TEGRA_ICC_BPMP_PCIE_3,
.type = TEGRA_ICC_NISO,
.sid = TEGRA238_SID_PCIE3,
.regs = {
.sid = {
.override = 0x6f8,
.security = 0x6fc,
},
},
}, {
.id = TEGRA_ICC_MC_CPU_CLUSTER0,
.name = "sw_cluster0",
.bpmp_id = TEGRA_ICC_BPMP_CPU_CLUSTER0,
.type = TEGRA_ICC_NISO,
}, {
.id = TEGRA234_MEMORY_CLIENT_NVL1R,
.name = "nvl1r",
.bpmp_id = TEGRA_ICC_BPMP_GPU,
.type = TEGRA_ICC_NISO,
}, {
.id = TEGRA234_MEMORY_CLIENT_NVL1W,
.name = "nvl1w",
.bpmp_id = TEGRA_ICC_BPMP_GPU,
.type = TEGRA_ICC_NISO,
}
};
static const struct tegra_mc_intmask tegra238_mc_intmasks[] = {
{
.reg = MC_INTMASK,
.mask = MC_INT_DECERR_ROUTE_SANITY | MC_INT_DECERR_GENERALIZED_CARVEOUT |
MC_INT_DECERR_MTS | MC_INT_SECERR_SEC | MC_INT_DECERR_VPR |
MC_INT_SECURITY_VIOLATION | MC_INT_DECERR_EMEM,
},
};
const struct tegra_mc_soc tegra238_mc_soc = {
.num_clients = ARRAY_SIZE(tegra238_mc_clients),
.clients = tegra238_mc_clients,
.num_address_bits = 40,
.num_channels = 8,
.client_id_mask = 0x1ff,
.intmasks = tegra238_mc_intmasks,
.num_intmasks = ARRAY_SIZE(tegra238_mc_intmasks),
.has_addr_hi_reg = true,
.ops = &tegra186_mc_ops,
.icc_ops = &tegra234_mc_icc_ops,
.ch_intmask = 0x0000ff00,
.global_intstatus_channel_shift = 8,
.num_carveouts = 32,
.regs = &tegra20_mc_regs,
.handle_irq = tegra30_mc_irq_handlers,
.num_interrupts = ARRAY_SIZE(tegra30_mc_irq_handlers),
.mc_addr_hi_mask = 0x3,
.mc_err_status_type_mask = (0x7 << 28),
};
@@ -0,0 +1,50 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/* Copyright (C) 2025 NVIDIA CORPORATION. All rights reserved. */
#ifndef MEMORY_TEGRA_TEGRA264_BWMGR_H
#define MEMORY_TEGRA_TEGRA264_BWMGR_H
#define TEGRA264_BWMGR_ICC_PRIMARY 1
#define TEGRA264_BWMGR_DEBUG 2
#define TEGRA264_BWMGR_CPU_CLUSTER0 3
#define TEGRA264_BWMGR_CPU_CLUSTER1 4
#define TEGRA264_BWMGR_CPU_CLUSTER2 5
#define TEGRA264_BWMGR_CPU_CLUSTER3 6
#define TEGRA264_BWMGR_CPU_CLUSTER4 7
#define TEGRA264_BWMGR_CPU_CLUSTER5 8
#define TEGRA264_BWMGR_CPU_CLUSTER6 9
#define TEGRA264_BWMGR_CACTMON 10
#define TEGRA264_BWMGR_DISPLAY 11
#define TEGRA264_BWMGR_VI 12
#define TEGRA264_BWMGR_APE 13
#define TEGRA264_BWMGR_VIFAL 14
#define TEGRA264_BWMGR_GPU 15
#define TEGRA264_BWMGR_EQOS 16
#define TEGRA264_BWMGR_PCIE_0 17
#define TEGRA264_BWMGR_PCIE_1 18
#define TEGRA264_BWMGR_PCIE_2 19
#define TEGRA264_BWMGR_PCIE_3 20
#define TEGRA264_BWMGR_PCIE_4 21
#define TEGRA264_BWMGR_PCIE_5 22
#define TEGRA264_BWMGR_SDMMC_1 23
#define TEGRA264_BWMGR_SDMMC_2 24
#define TEGRA264_BWMGR_NVDEC 25
#define TEGRA264_BWMGR_NVENC 26
#define TEGRA264_BWMGR_NVJPG_0 27
#define TEGRA264_BWMGR_NVJPG_1 28
#define TEGRA264_BWMGR_OFAA 29
#define TEGRA264_BWMGR_XUSB_HOST 30
#define TEGRA264_BWMGR_XUSB_DEV 31
#define TEGRA264_BWMGR_TSEC 32
#define TEGRA264_BWMGR_VIC 33
#define TEGRA264_BWMGR_APEDMA 34
#define TEGRA264_BWMGR_SE 35
#define TEGRA264_BWMGR_ISP 36
#define TEGRA264_BWMGR_HDA 37
#define TEGRA264_BWMGR_VI2FAL 38
#define TEGRA264_BWMGR_VI2 39
#define TEGRA264_BWMGR_RCE 40
#define TEGRA264_BWMGR_PVA 41
#define TEGRA264_BWMGR_NVPMODEL 42
#endif
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff