mirror of
https://github.com/torvalds/linux.git
synced 2026-07-27 09:36:22 +02:00
memory: tegra: Implement EMEM regs and ICC ops for Tegra114
Prepare Internal Memory Controller for introduction of External Memory Controller. Signed-off-by: Svyatoslav Ryhel <clamor95@gmail.com> Reviewed-by: Mikko Perttunen <mperttunen@nvidia.com> Link: https://patch.msgid.link/20260427070312.81679-3-clamor95@gmail.com Signed-off-by: Krzysztof Kozlowski <krzk@kernel.org>
This commit is contained in:
parent
b42834da6f
commit
750fed9230
|
|
@ -3,6 +3,7 @@
|
|||
* Copyright (C) 2014-2026 NVIDIA CORPORATION. All rights reserved.
|
||||
*/
|
||||
|
||||
#include <linux/device.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/mm.h>
|
||||
|
||||
|
|
@ -1101,6 +1102,195 @@ static const struct tegra_mc_reset tegra114_mc_resets[] = {
|
|||
TEGRA114_MC_RESET(VI, 0x200, 0x204, 17),
|
||||
};
|
||||
|
||||
static void tegra114_mc_tune_client_latency(struct tegra_mc *mc,
|
||||
const struct tegra_mc_client *client,
|
||||
unsigned int bandwidth_mbytes_sec)
|
||||
{
|
||||
u32 arb_tolerance_compensation_nsec, arb_tolerance_compensation_div;
|
||||
unsigned int fifo_size = client->fifo_size;
|
||||
u32 arb_nsec, la_ticks, value;
|
||||
|
||||
/* see 20.3.1.1 Client Configuration in Tegra4 TRM v01p */
|
||||
if (bandwidth_mbytes_sec)
|
||||
arb_nsec = fifo_size * NSEC_PER_USEC / bandwidth_mbytes_sec;
|
||||
else
|
||||
arb_nsec = U32_MAX;
|
||||
|
||||
/*
|
||||
* Latency allowness should be set with consideration for the module's
|
||||
* latency tolerance and internal buffering capabilities.
|
||||
*
|
||||
* Display memory clients use isochronous transfers and have very low
|
||||
* tolerance to a belated transfers. Hence we need to compensate the
|
||||
* memory arbitration imperfection for them in order to prevent FIFO
|
||||
* underflow condition when memory bus is busy.
|
||||
*
|
||||
* VI clients also need a stronger compensation.
|
||||
*/
|
||||
switch (client->swgroup) {
|
||||
case TEGRA_SWGROUP_MPCORE:
|
||||
case TEGRA_SWGROUP_PTC:
|
||||
/*
|
||||
* We always want lower latency for these clients, hence
|
||||
* don't touch them.
|
||||
*/
|
||||
return;
|
||||
|
||||
case TEGRA_SWGROUP_DC:
|
||||
case TEGRA_SWGROUP_DCB:
|
||||
arb_tolerance_compensation_nsec = 1050;
|
||||
arb_tolerance_compensation_div = 2;
|
||||
break;
|
||||
|
||||
case TEGRA_SWGROUP_VI:
|
||||
arb_tolerance_compensation_nsec = 1050;
|
||||
arb_tolerance_compensation_div = 1;
|
||||
break;
|
||||
|
||||
default:
|
||||
arb_tolerance_compensation_nsec = 150;
|
||||
arb_tolerance_compensation_div = 1;
|
||||
break;
|
||||
}
|
||||
|
||||
if (arb_nsec > arb_tolerance_compensation_nsec)
|
||||
arb_nsec -= arb_tolerance_compensation_nsec;
|
||||
else
|
||||
arb_nsec = 0;
|
||||
|
||||
arb_nsec /= arb_tolerance_compensation_div;
|
||||
|
||||
/*
|
||||
* Latency allowance is a number of ticks a request from a particular
|
||||
* client may wait in the EMEM arbiter before it becomes a high-priority
|
||||
* request.
|
||||
*/
|
||||
la_ticks = arb_nsec / mc->tick;
|
||||
la_ticks = min(la_ticks, client->regs.la.mask);
|
||||
|
||||
value = mc_readl(mc, client->regs.la.reg);
|
||||
value &= ~(client->regs.la.mask << client->regs.la.shift);
|
||||
value |= la_ticks << client->regs.la.shift;
|
||||
mc_writel(mc, value, client->regs.la.reg);
|
||||
}
|
||||
|
||||
static int tegra114_mc_icc_set(struct icc_node *src, struct icc_node *dst)
|
||||
{
|
||||
struct tegra_mc *mc = icc_provider_to_tegra_mc(src->provider);
|
||||
const struct tegra_mc_client *client = &mc->soc->clients[src->id];
|
||||
u64 peak_bandwidth = icc_units_to_bps(src->peak_bw);
|
||||
|
||||
/*
|
||||
* Skip pre-initialization that is done by icc_node_add(), which sets
|
||||
* bandwidth to maximum for all clients before drivers are loaded.
|
||||
*
|
||||
* This doesn't make sense for us because we don't have drivers for all
|
||||
* clients and it's okay to keep configuration left from bootloader
|
||||
* during boot, at least for today.
|
||||
*/
|
||||
if (src == dst)
|
||||
return 0;
|
||||
|
||||
/* convert bytes/sec to megabytes/sec */
|
||||
do_div(peak_bandwidth, 1000000);
|
||||
|
||||
tegra114_mc_tune_client_latency(mc, client, peak_bandwidth);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int tegra114_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, 400);
|
||||
|
||||
*agg_avg += avg_bw;
|
||||
*agg_peak = max(*agg_peak, peak_bw);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct icc_node_data *
|
||||
tegra114_mc_of_icc_xlate_extended(const struct of_phandle_args *spec, void *data)
|
||||
{
|
||||
struct tegra_mc *mc = icc_provider_to_tegra_mc(data);
|
||||
const struct tegra_mc_client *client;
|
||||
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);
|
||||
|
||||
client = &mc->soc->clients[idx];
|
||||
ndata->node = node;
|
||||
|
||||
switch (client->swgroup) {
|
||||
case TEGRA_SWGROUP_DC:
|
||||
case TEGRA_SWGROUP_DCB:
|
||||
case TEGRA_SWGROUP_PTC:
|
||||
case TEGRA_SWGROUP_VI:
|
||||
/* these clients are isochronous by default */
|
||||
ndata->tag = TEGRA_MC_ICC_TAG_ISO;
|
||||
break;
|
||||
|
||||
default:
|
||||
ndata->tag = TEGRA_MC_ICC_TAG_DEFAULT;
|
||||
break;
|
||||
}
|
||||
|
||||
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 tegra114_mc_icc_ops = {
|
||||
.xlate_extended = tegra114_mc_of_icc_xlate_extended,
|
||||
.aggregate = tegra114_mc_icc_aggreate,
|
||||
.set = tegra114_mc_icc_set,
|
||||
};
|
||||
|
||||
static const unsigned long tegra114_mc_emem_regs[] = {
|
||||
MC_EMEM_ARB_CFG,
|
||||
MC_EMEM_ARB_OUTSTANDING_REQ,
|
||||
MC_EMEM_ARB_TIMING_RCD,
|
||||
MC_EMEM_ARB_TIMING_RP,
|
||||
MC_EMEM_ARB_TIMING_RC,
|
||||
MC_EMEM_ARB_TIMING_RAS,
|
||||
MC_EMEM_ARB_TIMING_FAW,
|
||||
MC_EMEM_ARB_TIMING_RRD,
|
||||
MC_EMEM_ARB_TIMING_RAP2PRE,
|
||||
MC_EMEM_ARB_TIMING_WAP2PRE,
|
||||
MC_EMEM_ARB_TIMING_R2R,
|
||||
MC_EMEM_ARB_TIMING_W2W,
|
||||
MC_EMEM_ARB_TIMING_R2W,
|
||||
MC_EMEM_ARB_TIMING_W2R,
|
||||
MC_EMEM_ARB_DA_TURNS,
|
||||
MC_EMEM_ARB_DA_COVERS,
|
||||
MC_EMEM_ARB_MISC0,
|
||||
MC_EMEM_ARB_RING1_THROTTLE,
|
||||
};
|
||||
|
||||
static const struct tegra_mc_intmask tegra114_mc_intmasks[] = {
|
||||
{
|
||||
.reg = MC_INTMASK,
|
||||
|
|
@ -1116,11 +1306,14 @@ const struct tegra_mc_soc tegra114_mc_soc = {
|
|||
.atom_size = 32,
|
||||
.client_id_mask = 0x7f,
|
||||
.smmu = &tegra114_smmu_soc,
|
||||
.emem_regs = tegra114_mc_emem_regs,
|
||||
.num_emem_regs = ARRAY_SIZE(tegra114_mc_emem_regs),
|
||||
.intmasks = tegra114_mc_intmasks,
|
||||
.num_intmasks = ARRAY_SIZE(tegra114_mc_intmasks),
|
||||
.reset_ops = &tegra_mc_reset_ops_common,
|
||||
.resets = tegra114_mc_resets,
|
||||
.num_resets = ARRAY_SIZE(tegra114_mc_resets),
|
||||
.icc_ops = &tegra114_mc_icc_ops,
|
||||
.ops = &tegra30_mc_ops,
|
||||
.regs = &tegra20_mc_regs,
|
||||
.handle_irq = tegra30_mc_irq_handlers,
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user