mirror of
https://mirrors.bfsu.edu.cn/git/linux.git
synced 2024-11-11 21:38:32 +08:00
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:
commit
0a8e73301d
@ -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>;
|
||||
|
||||
|
@ -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>;
|
||||
}
|
||||
|
||||
|
@ -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>;
|
||||
};
|
@ -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>;
|
||||
};
|
@ -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>;
|
||||
|
||||
|
@ -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",
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
@ -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));
|
||||
|
@ -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,
|
||||
};
|
||||
|
@ -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));
|
||||
|
@ -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);
|
||||
|
Loading…
Reference in New Issue
Block a user