Memory controller drivers for v5.13 - Tegra SoC

1. Few cleanups.
 2. Add debug statistics to Tegra20 memory controller.
 3. Update bindings and convert to dtschema.  This update is not
    backwards compatible (ABI break) however the broken part was added
    recently (v5.11) and there are no users of it yet.
 -----BEGIN PGP SIGNATURE-----
 
 iQJEBAABCgAuFiEE3dJiKD0RGyM7briowTdm5oaLg9cFAmBt2ZcQHGtyemtAa2Vy
 bmVsLm9yZwAKCRDBN2bmhouD12sfEACQYS2TjPwHXmo9hv+fKoYAq4qQ/NOL7YAY
 pN1k42RsDa28/KwoToFkD2WFNpDppSVHVQYjLMjBhjwh7A2x8DOFufm4kOVuhOxI
 u9BAo8iasKh+gmrdLgHcWNXkm881V4mFwSYR+FLQCvrF+1O+CFc1xJ3l9CMMKAvI
 ukJBwriJ2HFWnWXiv0xRTJkjhO8/hK+AuGHLeU0EhskFUIYhaPOm8lQ75KLx6axU
 LFh5lv56zPZcXihWKU1Au3S4Wxb/FEmqkL5HVgqkNEaZYRZH8JhIR2cV5iUukA2o
 ZdWPXaSNtHn7vXAXI+VjRj10+l+qYqktSXv9ukOmd/ofQN6aYDzb0HdPM/QBw/Kz
 VNSK2bPe912oN8a9nD2rePqS/Ny8+AZz0mLkmUbVmhJWuD483pLHZAhK1zyl2Toe
 Zj5l4hSFTUwjr4UBLBlKZmBmz3sYyYUT9k/PDYU+AX52uUPLAdC0piyoEVKZxVDB
 P3XUdAZlhde/BKwVpyBNCDoWgD4SEJfSw7BOYbfVvIyaOBqxh8foMX1C+xCfaxW2
 /dYCDtdjfqSWr2rwiNNtgA8XqiFKeJwthez6xsUHvI7qxPHRRhFCZlLUk0EK9X5h
 hPyESPu01BuXzjp4w4rIX3K4t2ikBDXXOqrdlRr/8C2/Xzk6XQ4VOk/pqFk47hWk
 oJJ3Jg+Nfw==
 =bbW2
 -----END PGP SIGNATURE-----

Merge tag 'memory-controller-drv-tegra-5.13' of git://git.kernel.org/pub/scm/linux/kernel/git/krzk/linux-mem-ctrl into arm/drivers

Memory controller drivers for v5.13 - Tegra SoC

1. Few cleanups.
2. Add debug statistics to Tegra20 memory controller.
3. Update bindings and convert to dtschema.  This update is not
   backwards compatible (ABI break) however the broken part was added
   recently (v5.11) and there are no users of it yet.

* tag 'memory-controller-drv-tegra-5.13' of git://git.kernel.org/pub/scm/linux/kernel/git/krzk/linux-mem-ctrl:
  dt-bindings: memory: tegra20: mc: Convert to schema
  dt-bindings: memory: tegra124: emc: Replace core regulator with power domain
  dt-bindings: memory: tegra30: emc: Replace core regulator with power domain
  dt-bindings: memory: tegra20: emc: Replace core regulator with power domain
  memory: tegra: Print out info-level once per driver probe
  memory: tegra20: Protect debug code with a lock
  memory: tegra20: Correct comment to MC_STAT registers writes
  memory: tegra20: Add debug statistics
  memory: tegra: replace DEFINE_SIMPLE_ATTRIBUTE with DEFINE_DEBUGFS_ATTRIBUTE

Link: https://lore.kernel.org/r/20210407161333.73013-2-krzysztof.kozlowski@canonical.com
Signed-off-by: Arnd Bergmann <arnd@arndb.de>
This commit is contained in:
Arnd Bergmann 2021-04-08 17:47:49 +02:00
commit 0a8e73301d
12 changed files with 466 additions and 77 deletions

View File

@ -37,9 +37,10 @@ properties:
description:
phandle of the memory controller node
core-supply:
power-domains:
maxItems: 1
description:
Phandle of voltage regulator of the SoC "core" power domain.
Phandle of the SoC "core" power domain.
operating-points-v2:
description:
@ -370,7 +371,7 @@ examples:
nvidia,memory-controller = <&mc>;
operating-points-v2 = <&dvfs_opp_table>;
core-supply = <&vdd_core>;
power-domains = <&domain>;
#interconnect-cells = <0>;

View File

@ -23,7 +23,7 @@ For each opp entry in 'operating-points-v2' table:
matches, the OPP gets enabled.
Optional properties:
- core-supply: Phandle of voltage regulator of the SoC "core" power domain.
- power-domains: Phandle of the SoC "core" power domain.
Child device nodes describe the memory settings for different configurations and clock rates.
@ -48,7 +48,7 @@ Example:
interrupts = <0 78 0x04>;
clocks = <&tegra_car TEGRA20_CLK_EMC>;
nvidia,memory-controller = <&mc>;
core-supply = <&core_vdd_reg>;
power-domains = <&domain>;
operating-points-v2 = <&opp_table>;
}

View File

@ -1,40 +0,0 @@
NVIDIA Tegra20 MC(Memory Controller)
Required properties:
- compatible : "nvidia,tegra20-mc-gart"
- reg : Should contain 2 register ranges: physical base address and length of
the controller's registers and the GART aperture respectively.
- clocks: Must contain an entry for each entry in clock-names.
See ../clocks/clock-bindings.txt for details.
- clock-names: Must include the following entries:
- mc: the module's clock input
- interrupts : Should contain MC General interrupt.
- #reset-cells : Should be 1. This cell represents memory client module ID.
The assignments may be found in header file <dt-bindings/memory/tegra20-mc.h>
or in the TRM documentation.
- #iommu-cells: Should be 0. This cell represents the number of cells in an
IOMMU specifier needed to encode an address. GART supports only a single
address space that is shared by all devices, therefore no additional
information needed for the address encoding.
- #interconnect-cells : Should be 1. This cell represents memory client.
The assignments may be found in header file <dt-bindings/memory/tegra20-mc.h>.
Example:
mc: memory-controller@7000f000 {
compatible = "nvidia,tegra20-mc-gart";
reg = <0x7000f000 0x400 /* controller registers */
0x58000000 0x02000000>; /* GART aperture */
clocks = <&tegra_car TEGRA20_CLK_MC>;
clock-names = "mc";
interrupts = <GIC_SPI 77 0x04>;
#reset-cells = <1>;
#iommu-cells = <0>;
#interconnect-cells = <1>;
};
video-codec@6001a000 {
compatible = "nvidia,tegra20-vde";
...
resets = <&mc TEGRA20_MC_RESET_VDE>;
iommus = <&mc>;
};

View File

@ -0,0 +1,79 @@
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/memory-controllers/nvidia,tegra20-mc.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: NVIDIA Tegra20 SoC Memory Controller
maintainers:
- Dmitry Osipenko <digetx@gmail.com>
- Jon Hunter <jonathanh@nvidia.com>
- Thierry Reding <thierry.reding@gmail.com>
description: |
The Tegra20 Memory Controller merges request streams from various client
interfaces into request stream(s) for the various memory target devices,
and returns response data to the various clients. The Memory Controller
has a configurable arbitration algorithm to allow the user to fine-tune
performance among the various clients.
Tegra20 Memory Controller includes the GART (Graphics Address Relocation
Table) which allows Memory Controller to provide a linear view of a
fragmented memory pages.
properties:
compatible:
const: nvidia,tegra20-mc-gart
reg:
items:
- description: controller registers
- description: GART registers
clocks:
maxItems: 1
clock-names:
items:
- const: mc
interrupts:
maxItems: 1
"#reset-cells":
const: 1
"#iommu-cells":
const: 0
"#interconnect-cells":
const: 1
required:
- compatible
- reg
- interrupts
- clocks
- clock-names
- "#reset-cells"
- "#iommu-cells"
- "#interconnect-cells"
additionalProperties: false
examples:
- |
memory-controller@7000f000 {
compatible = "nvidia,tegra20-mc-gart";
reg = <0x7000f000 0x400>, /* Controller registers */
<0x58000000 0x02000000>; /* GART aperture */
clocks = <&clock_controller 32>;
clock-names = "mc";
interrupts = <0 77 4>;
#iommu-cells = <0>;
#reset-cells = <1>;
#interconnect-cells = <1>;
};

View File

@ -39,9 +39,10 @@ properties:
description:
Phandle of the Memory Controller node.
core-supply:
power-domains:
maxItems: 1
description:
Phandle of voltage regulator of the SoC "core" power domain.
Phandle of the SoC "core" power domain.
operating-points-v2:
description:
@ -241,7 +242,7 @@ examples:
nvidia,memory-controller = <&mc>;
operating-points-v2 = <&dvfs_opp_table>;
core-supply = <&vdd_core>;
power-domains = <&domain>;
#interconnect-cells = <0>;

View File

@ -827,6 +827,15 @@ static int tegra_mc_probe(struct platform_device *pdev)
return err;
}
mc->debugfs.root = debugfs_create_dir("mc", NULL);
if (mc->soc->init) {
err = mc->soc->init(mc);
if (err < 0)
dev_err(&pdev->dev, "failed to initialize SoC driver: %d\n",
err);
}
err = tegra_mc_reset_setup(mc);
if (err < 0)
dev_err(&pdev->dev, "failed to register reset controller: %d\n",

View File

@ -92,12 +92,12 @@ icc_provider_to_tegra_mc(struct icc_provider *provider)
return container_of(provider, struct tegra_mc, provider);
}
static inline u32 mc_readl(struct tegra_mc *mc, unsigned long 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(struct tegra_mc *mc, u32 value,
static inline void mc_writel(const struct tegra_mc *mc, u32 value,
unsigned long offset)
{
writel_relaxed(value, mc->regs + offset);

View File

@ -905,7 +905,7 @@ static int emc_init(struct tegra_emc *emc)
else
emc->dram_bus_width = 32;
dev_info(emc->dev, "%ubit DRAM bus\n", emc->dram_bus_width);
dev_info_once(emc->dev, "%ubit DRAM bus\n", emc->dram_bus_width);
emc->dram_type &= EMC_FBIO_CFG5_DRAM_TYPE_MASK;
emc->dram_type >>= EMC_FBIO_CFG5_DRAM_TYPE_SHIFT;
@ -1204,7 +1204,7 @@ static int tegra_emc_debug_min_rate_set(void *data, u64 rate)
return 0;
}
DEFINE_SIMPLE_ATTRIBUTE(tegra_emc_debug_min_rate_fops,
DEFINE_DEBUGFS_ATTRIBUTE(tegra_emc_debug_min_rate_fops,
tegra_emc_debug_min_rate_get,
tegra_emc_debug_min_rate_set, "%llu\n");
@ -1234,7 +1234,7 @@ static int tegra_emc_debug_max_rate_set(void *data, u64 rate)
return 0;
}
DEFINE_SIMPLE_ATTRIBUTE(tegra_emc_debug_max_rate_fops,
DEFINE_DEBUGFS_ATTRIBUTE(tegra_emc_debug_max_rate_fops,
tegra_emc_debug_max_rate_get,
tegra_emc_debug_max_rate_set, "%llu\n");
@ -1419,8 +1419,8 @@ static int tegra_emc_opp_table_init(struct tegra_emc *emc)
goto put_hw_table;
}
dev_info(emc->dev, "OPP HW ver. 0x%x, current clock rate %lu MHz\n",
hw_version, clk_get_rate(emc->clk) / 1000000);
dev_info_once(emc->dev, "OPP HW ver. 0x%x, current clock rate %lu MHz\n",
hw_version, clk_get_rate(emc->clk) / 1000000);
/* first dummy rate-set initializes voltage state */
err = dev_pm_opp_set_rate(emc->dev, clk_get_rate(emc->clk));
@ -1475,9 +1475,9 @@ static int tegra_emc_probe(struct platform_device *pdev)
if (err)
return err;
} else {
dev_info(&pdev->dev,
"no memory timings for RAM code %u found in DT\n",
ram_code);
dev_info_once(&pdev->dev,
"no memory timings for RAM code %u found in DT\n",
ram_code);
}
err = emc_init(emc);

View File

@ -411,12 +411,12 @@ static int tegra_emc_load_timings_from_dt(struct tegra_emc *emc,
sort(emc->timings, emc->num_timings, sizeof(*timing), cmp_timings,
NULL);
dev_info(emc->dev,
"got %u timings for RAM code %u (min %luMHz max %luMHz)\n",
emc->num_timings,
tegra_read_ram_code(),
emc->timings[0].rate / 1000000,
emc->timings[emc->num_timings - 1].rate / 1000000);
dev_info_once(emc->dev,
"got %u timings for RAM code %u (min %luMHz max %luMHz)\n",
emc->num_timings,
tegra_read_ram_code(),
emc->timings[0].rate / 1000000,
emc->timings[emc->num_timings - 1].rate / 1000000);
return 0;
}
@ -429,7 +429,7 @@ tegra_emc_find_node_by_ram_code(struct device *dev)
int err;
if (of_get_child_count(dev->of_node) == 0) {
dev_info(dev, "device-tree doesn't have memory timings\n");
dev_info_once(dev, "device-tree doesn't have memory timings\n");
return NULL;
}
@ -496,7 +496,7 @@ static int emc_setup_hw(struct tegra_emc *emc)
else
emc->dram_bus_width = 32;
dev_info(emc->dev, "%ubit DRAM bus\n", emc->dram_bus_width);
dev_info_once(emc->dev, "%ubit DRAM bus\n", emc->dram_bus_width);
return 0;
}
@ -931,8 +931,8 @@ static int tegra_emc_opp_table_init(struct tegra_emc *emc)
goto put_hw_table;
}
dev_info(emc->dev, "OPP HW ver. 0x%x, current clock rate %lu MHz\n",
hw_version, clk_get_rate(emc->clk) / 1000000);
dev_info_once(emc->dev, "OPP HW ver. 0x%x, current clock rate %lu MHz\n",
hw_version, clk_get_rate(emc->clk) / 1000000);
/* first dummy rate-set initializes voltage state */
err = dev_pm_opp_set_rate(emc->dev, clk_get_rate(emc->clk));

View File

@ -3,6 +3,9 @@
* Copyright (C) 2012 NVIDIA CORPORATION. All rights reserved.
*/
#include <linux/bitfield.h>
#include <linux/delay.h>
#include <linux/mutex.h>
#include <linux/of_device.h>
#include <linux/slab.h>
#include <linux/string.h>
@ -11,6 +14,79 @@
#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,
@ -356,6 +432,261 @@ static const struct tegra_mc_icc_ops tegra20_mc_icc_ops = {
.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 = kcalloc(mc->soc->num_clients + 1, sizeof(*stats), GFP_KERNEL);
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_init(struct tegra_mc *mc)
{
debugfs_create_devm_seqfile(mc->dev, "stats", mc->debugfs.root,
tegra20_mc_stats_show);
return 0;
}
const struct tegra_mc_soc tegra20_mc_soc = {
.clients = tegra20_mc_clients,
.num_clients = ARRAY_SIZE(tegra20_mc_clients),
@ -367,4 +698,5 @@ const struct tegra_mc_soc tegra20_mc_soc = {
.resets = tegra20_mc_resets,
.num_resets = ARRAY_SIZE(tegra20_mc_resets),
.icc_ops = &tegra20_mc_icc_ops,
.init = tegra20_mc_init,
};

View File

@ -998,12 +998,12 @@ static int emc_load_timings_from_dt(struct tegra_emc *emc,
if (err)
return err;
dev_info(emc->dev,
"got %u timings for RAM code %u (min %luMHz max %luMHz)\n",
emc->num_timings,
tegra_read_ram_code(),
emc->timings[0].rate / 1000000,
emc->timings[emc->num_timings - 1].rate / 1000000);
dev_info_once(emc->dev,
"got %u timings for RAM code %u (min %luMHz max %luMHz)\n",
emc->num_timings,
tegra_read_ram_code(),
emc->timings[0].rate / 1000000,
emc->timings[emc->num_timings - 1].rate / 1000000);
return 0;
}
@ -1015,7 +1015,7 @@ static struct device_node *emc_find_node_by_ram_code(struct device *dev)
int err;
if (of_get_child_count(dev->of_node) == 0) {
dev_info(dev, "device-tree doesn't have memory timings\n");
dev_info_once(dev, "device-tree doesn't have memory timings\n");
return NULL;
}
@ -1503,8 +1503,8 @@ static int tegra_emc_opp_table_init(struct tegra_emc *emc)
goto put_hw_table;
}
dev_info(emc->dev, "OPP HW ver. 0x%x, current clock rate %lu MHz\n",
hw_version, clk_get_rate(emc->clk) / 1000000);
dev_info_once(emc->dev, "OPP HW ver. 0x%x, current clock rate %lu MHz\n",
hw_version, clk_get_rate(emc->clk) / 1000000);
/* first dummy rate-set initializes voltage state */
err = dev_pm_opp_set_rate(emc->dev, clk_get_rate(emc->clk));

View File

@ -7,6 +7,7 @@
#define __SOC_TEGRA_MC_H__
#include <linux/bits.h>
#include <linux/debugfs.h>
#include <linux/err.h>
#include <linux/interconnect-provider.h>
#include <linux/reset-controller.h>
@ -175,6 +176,8 @@ struct tegra_mc_soc {
unsigned int num_resets;
const struct tegra_mc_icc_ops *icc_ops;
int (*init)(struct tegra_mc *mc);
};
struct tegra_mc {
@ -196,6 +199,10 @@ struct tegra_mc {
struct icc_provider provider;
spinlock_t lock;
struct {
struct dentry *root;
} debugfs;
};
int tegra_mc_write_emem_configuration(struct tegra_mc *mc, unsigned long rate);