Skip to contentSkip to conversation

Tegra20 External Memory Controller driver

PATCH Superseded Dmitry Osipenko 1 participant 10 messages Jul 19, 2018 – Jul 22, 2018

Subsystems (5)

v4 of 6 Superseded Latest is v6 →

lore
Share & tools
lore
#0 lore raw

v4 cover Tegra20 External Memory Controller driver

Hello,

Couple years ago the Tegra20 EMC driver was removed from the kernel due to incompatible changes in the Tegra's clock driver. This patchset introduces a modernized EMC driver. Currently the sole purpose of the driver is to initialize DRAM frequency to maximum rate during of the kernels boot-up. Later we may consider implementing dynamic memory frequency scaling, utilizing functionality provided by this driver.

Changelog:

v4:
- Fixed "bad of_node_put()" error which was revealed by enabling some extra kernel debug config options.

- The "emc-table" DT nodes are now parsed starting from the "emc" node instead of the DT root.

- Adjusted code comment in the "Turn EMC clock gate into divider" patch as was suggested by Stephen Boyd to the v3.

v3:
- Handle "nvidia,use-ram-code" DT property, its handling was missed in the previous versions.

- Honor "emc-tables" DT node naming which is explicitly specified in the DT binding, also was missed in the previous versions.

- Two new DT binding patches: one adds the EMC clock property, other relocates the binding doc file to the appropriate directory. One new patch that adds EMC clock property to the DTS file.

- Addressed v2 review comments from Thierry Reding. Driver does not preserve backwards compatibility with older device tree binding.

- The PLL_M and PLL_P clocks are kept internal to the driver because after some more considering I couldn't find a really good reason why these clocks should be in the device tree.

- Some minor cleanups and fixes in the drivers code.

v2:
- Minor code cleanups like consistent use of writel_relaxed instead of non-relaxed version, reworded error messages, etc.

- Factored out use_pllm_ud bit checking into a standalone patch for consistency.

Dmitry Osipenko (8):
dt: bindings: tegra20-emc: Document interrupt property
dt: bindings: tegra20-emc: Document clock property
dt: bindings: Move tegra20-emc binding to memory-controllers directory
ARM: dts: tegra20: Add interrupt entry to External Memory Controller
ARM: dts: tegra20: Add clock entry to External Memory Controller
clk: tegra20: Turn EMC clock gate into divider
clk: tegra20: Check whether direct PLLM sourcing is turned off for EMC
memory: tegra: Introduce Tegra20 EMC driver

 .../nvidia,tegra20-emc.txt                    |   4 +
 arch/arm/boot/dts/tegra20.dtsi                |   2 +
 drivers/clk/tegra/clk-tegra20.c               |  46 +-
 drivers/memory/tegra/Kconfig                  |  10 +
 drivers/memory/tegra/Makefile                 |   1 +
 drivers/memory/tegra/tegra20-emc.c            | 575 ++++++++++++++++++
 6 files changed, 628 insertions(+), 10 deletions(-)
 rename Documentation/devicetree/bindings/{arm/tegra => memory-controllers}/nvidia,tegra20-emc.txt (95%)
 create mode 100644 drivers/memory/tegra/tegra20-emc.c
Signature · 1 line
2.18.0
#1 lore raw

PATCH v4 1/8 dt: bindings: tegra20-emc: Document interrupt property

EMC has a dedicated interrupt that is used to notify about completion of HW operations. Document the interrupt property.

---
 .../devicetree/bindings/arm/tegra/nvidia,tegra20-emc.txt        | 2 ++
 1 file changed, 2 insertions(+)
Documentation/devicetree/bindings/arm/tegra/nvidia,tegra20-emc.txt +2 -0
index 4c33b29dc660..a6fe401d0d48 100644
@@ -10,6 +10,7 @@ Properties:
   and chosen using the ramcode board selector. If omitted, only one
   set of tables can be present and said tables will be used
   irrespective of ram-code configuration.
+- interrupts : Should contain EMC General interrupt.
 
 Child device nodes describe the memory settings for different configurations and clock rates.
 
@@ -20,6 +21,7 @@ Example:
 		#size-cells = < 0 >;
 		compatible = "nvidia,tegra20-emc";
 		reg = <0x7000f4000 0x200>;
+		interrupts = <0 78 0x04>;
 	}
 
 
Signature · 1 line
2.18.0
#2 lore raw

PATCH v4 2/8 dt: bindings: tegra20-emc: Document clock property

In reply to Dmitry Osipenko

Embedded memory controller has a corresponding clock, document the clock property.

---
 .../devicetree/bindings/arm/tegra/nvidia,tegra20-emc.txt        | 2 ++
 1 file changed, 2 insertions(+)
Documentation/devicetree/bindings/arm/tegra/nvidia,tegra20-emc.txt +2 -0
index a6fe401d0d48..add95367640b 100644
@@ -11,6 +11,7 @@ Properties:
   set of tables can be present and said tables will be used
   irrespective of ram-code configuration.
 - interrupts : Should contain EMC General interrupt.
+- clocks : Should contain EMC clock.
 
 Child device nodes describe the memory settings for different configurations and clock rates.
 
@@ -22,6 +23,7 @@ Example:
 		compatible = "nvidia,tegra20-emc";
 		reg = <0x7000f4000 0x200>;
 		interrupts = <0 78 0x04>;
+		clocks = <&tegra_car TEGRA20_CLK_EMC>;
 	}
 
 
Signature · 1 line
2.18.0
#3 lore raw

PATCH v4 3/8 dt: bindings: Move tegra20-emc binding to memory-controllers directory

In reply to Dmitry Osipenko

Move tegra20-emc binding to the memory-controllers directory for consistency with the other Tegra memory bindings.

---
 .../{arm/tegra => memory-controllers}/nvidia,tegra20-emc.txt      | 0
 1 file changed, 0 insertions(+), 0 deletions(-)
 rename Documentation/devicetree/bindings/{arm/tegra => memory-controllers}/nvidia,tegra20-emc.txt (100%)
Documentation/devicetree/bindings/memory-controllers/nvidia,tegra20-emc.txt renamed from Documentation/devicetree/bindings/arm/tegra/nvidia,tegra20-emc.txt +0 -0
similarity index 100%
rename from Documentation/devicetree/bindings/arm/tegra/nvidia,tegra20-emc.txt
rename to Documentation/devicetree/bindings/memory-controllers/nvidia,tegra20-emc.txt
Signature · 1 line
2.18.0
#4 lore raw

PATCH v4 4/8 ARM: dts: tegra20: Add interrupt entry to External Memory Controller

In reply to Dmitry Osipenko

Add interrupt entry into the EMC DT node.

---
 arch/arm/boot/dts/tegra20.dtsi | 1 +
 1 file changed, 1 insertion(+)
arch/arm/boot/dts/tegra20.dtsi +1 -0
index 15b73bd377f0..9eb4163a4390 100644
@@ -632,6 +632,7 @@
 	memory-controller@7000f400 {
 		compatible = "nvidia,tegra20-emc";
 		reg = <0x7000f400 0x200>;
+		interrupts = <GIC_SPI 78 IRQ_TYPE_LEVEL_HIGH>;
 		#address-cells = <1>;
 		#size-cells = <0>;
 	};
Signature · 1 line
2.18.0
#5 lore raw

PATCH v4 5/8 ARM: dts: tegra20: Add clock entry to External Memory Controller

In reply to Dmitry Osipenko

Add clock entry into the EMC DT node.

---
 arch/arm/boot/dts/tegra20.dtsi | 1 +
 1 file changed, 1 insertion(+)
arch/arm/boot/dts/tegra20.dtsi +1 -0
index 9eb4163a4390..979f38293fe5 100644
@@ -633,6 +633,7 @@
 		compatible = "nvidia,tegra20-emc";
 		reg = <0x7000f400 0x200>;
 		interrupts = <GIC_SPI 78 IRQ_TYPE_LEVEL_HIGH>;
+		clocks = <&tegra_car TEGRA20_CLK_EMC>;
 		#address-cells = <1>;
 		#size-cells = <0>;
 	};
Signature · 1 line
2.18.0
#6 lore raw

PATCH v4 6/8 clk: tegra20: Turn EMC clock gate into divider

In reply to Dmitry Osipenko

Kernel should never gate the EMC clock as it causes immediate lockup, so removing clk-gate functionality doesn't affect anything. Turning EMC clk gate into divider allows to implement glitch-less EMC scaling, avoiding reparenting to a backup clock.

---
 drivers/clk/tegra/clk-tegra20.c | 36 ++++++++++++++++++++++++---------
 1 file changed, 26 insertions(+), 10 deletions(-)
drivers/clk/tegra/clk-tegra20.c +26 -10
index cc857d4d4a86..ebea97016d58 100644
@@ -578,7 +578,6 @@ static struct tegra_clk tegra20_clks[tegra_clk_max] __initdata = {
 	[tegra_clk_afi] = { .dt_id = TEGRA20_CLK_AFI, .present = true },
 	[tegra_clk_fuse] = { .dt_id = TEGRA20_CLK_FUSE, .present = true },
 	[tegra_clk_kfuse] = { .dt_id = TEGRA20_CLK_KFUSE, .present = true },
-	[tegra_clk_emc] = { .dt_id = TEGRA20_CLK_EMC, .present = true },
 };
 
 static unsigned long tegra20_clk_measure_input_freq(void)
@@ -799,6 +798,31 @@ static struct tegra_periph_init_data tegra_periph_nodiv_clk_list[] = {
 	TEGRA_INIT_DATA_NODIV("disp2",	mux_pllpdc_clkm, CLK_SOURCE_DISP2, 30, 2, 26,  0, TEGRA20_CLK_DISP2),
 };
 
+static void __init tegra20_emc_clk_init(void)
+{
+	struct clk *clk;
+
+	clk = clk_register_mux(NULL, "emc_mux", mux_pllmcp_clkm,
+			       ARRAY_SIZE(mux_pllmcp_clkm),
+			       CLK_SET_RATE_NO_REPARENT,
+			       clk_base + CLK_SOURCE_EMC,
+			       30, 2, 0, &emc_lock);
+
+	clk = tegra_clk_register_mc("mc", "emc_mux", clk_base + CLK_SOURCE_EMC,
+				    &emc_lock);
+	clks[TEGRA20_CLK_MC] = clk;
+
+	/*
+	 * Note that 'emc_mux' source and 'emc' rate shouldn't be changed at
+	 * the same time due to a HW bug, this won't happen because we're
+	 * defining 'emc_mux' and 'emc' as distinct clocks.
+	 */
+	clk = clk_register_divider(NULL, "emc", "emc_mux", CLK_IS_CRITICAL,
+				   clk_base + CLK_SOURCE_EMC, 0, 7,
+				   0, &emc_lock);
+	clks[TEGRA20_CLK_EMC] = clk;
+}
+
 static void __init tegra20_periph_clk_init(void)
 {
 	struct tegra_periph_init_data *data;
@@ -812,15 +836,7 @@ static void __init tegra20_periph_clk_init(void)
 	clks[TEGRA20_CLK_AC97] = clk;
 
 	/* emc */
-	clk = clk_register_mux(NULL, "emc_mux", mux_pllmcp_clkm,
-			       ARRAY_SIZE(mux_pllmcp_clkm),
-			       CLK_SET_RATE_NO_REPARENT,
-			       clk_base + CLK_SOURCE_EMC,
-			       30, 2, 0, &emc_lock);
-
-	clk = tegra_clk_register_mc("mc", "emc_mux", clk_base + CLK_SOURCE_EMC,
-				    &emc_lock);
-	clks[TEGRA20_CLK_MC] = clk;
+	tegra20_emc_clk_init();
 
 	/* dsi */
 	clk = tegra_clk_register_periph_gate("dsi", "pll_d", 0, clk_base, 0,
Signature · 1 line
2.18.0
#7 lore raw

Re: [PATCH v4 6/8] clk: tegra20: Turn EMC clock gate into divider

Quoted 39 lines · On Thursday, 19 July 2018 16:21:30 MSK Dmitry Osipenko wrote:

Kernel should never gate the EMC clock as it causes immediate lockup, so removing clk-gate functionality doesn't affect anything. Turning EMC clk gate into divider allows to implement glitch-less EMC scaling, avoiding reparenting to a backup clock.

---
 drivers/clk/tegra/clk-tegra20.c | 36 ++++++++++++++++++++++++---------
 1 file changed, 26 insertions(+), 10 deletions(-)
(quoted diff) +0 -0

b/drivers/clk/tegra/clk-tegra20.c index cc857d4d4a86..ebea97016d58 100644

drivers/clk/tegra/clk-tegra20.c +0 -0
@@ -578,7 +578,6 @@ static struct tegra_clk tegra20_clks[tegra_clk_max]

__initdata = { [tegra_clk_afi] = { .dt_id = TEGRA20_CLK_AFI, .present = true }, [tegra_clk_fuse] = { .dt_id = TEGRA20_CLK_FUSE, .present = true }, [tegra_clk_kfuse] = { .dt_id = TEGRA20_CLK_KFUSE, .present = true },

drivers/clk/tegra/clk-tegra20.c +12 -1
-	[tegra_clk_emc] = { .dt_id = TEGRA20_CLK_EMC, .present = true }, };
 
 static unsigned long tegra20_clk_measure_input_freq(void)
@@ -799,6 +798,31 @@ static struct tegra_periph_init_data
 tegra_periph_nodiv_clk_list[] = {
 TEGRA_INIT_DATA_NODIV("disp2",	mux_pllpdc_clkm, CLK_SOURCE_DISP2, 30, 2,
 26,  0, TEGRA20_CLK_DISP2), };
 
+static void __init tegra20_emc_clk_init(void)
+{
+	struct clk *clk;
+
+	clk = clk_register_mux(NULL, "emc_mux", mux_pllmcp_clkm,
+			       ARRAY_SIZE(mux_pllmcp_clkm),
+			       CLK_SET_RATE_NO_REPARENT,
+			       clk_base + CLK_SOURCE_EMC,
+			       30, 2, 0, &emc_lock);
+
+	clk = tegra_clk_register_mc("mc", "emc_mux", clk_base + CLK_SOURCE_EMC,
+				    &emc_lock);

drivers/clk/tegra/clk-tegra20.c static struct tegra_periph_init_data · line 823

Quoted 10 lines
drivers/clk/tegra/clk-tegra20.c +10 -0
+	clks[TEGRA20_CLK_MC] = clk;
+
+	/*
+	 * Note that 'emc_mux' source and 'emc' rate shouldn't be changed at
+	 * the same time due to a HW bug, this won't happen because we're
+	 * defining 'emc_mux' and 'emc' as distinct clocks.
+	 */
+	clk = clk_register_divider(NULL, "emc", "emc_mux", CLK_IS_CRITICAL,
+				   clk_base + CLK_SOURCE_EMC, 0, 7,
+				   0, &emc_lock);

Actually this is wrong. The divisor is 8 bits-wide and "lsb denote 0.5x", so this is a 7.1 divider. I'll prepare v5.

#8 lore raw

PATCH v4 7/8 clk: tegra20: Check whether direct PLLM sourcing is turned off for EMC

In reply to Dmitry Osipenko

Ensure that direct PLLM sourcing is turned off for EMC as we don't support that configuration in the clk driver.

---
 drivers/clk/tegra/clk-tegra20.c | 10 ++++++++++
 1 file changed, 10 insertions(+)
drivers/clk/tegra/clk-tegra20.c +10 -0
index ebea97016d58..e1f039aa8ca9 100644
@@ -800,7 +800,9 @@ static struct tegra_periph_init_data tegra_periph_nodiv_clk_list[] = {
 
 static void __init tegra20_emc_clk_init(void)
 {
+	const u32 use_pllm_ud = BIT(29);
 	struct clk *clk;
+	u32 emc_reg;
 
 	clk = clk_register_mux(NULL, "emc_mux", mux_pllmcp_clkm,
 			       ARRAY_SIZE(mux_pllmcp_clkm),
@@ -812,6 +814,14 @@ static void __init tegra20_emc_clk_init(void)
 				    &emc_lock);
 	clks[TEGRA20_CLK_MC] = clk;
 
+	/* un-divided pll_m_out0 is currently unsupported */
+	emc_reg = readl_relaxed(clk_base + CLK_SOURCE_EMC);
+	if (emc_reg & use_pllm_ud) {
+		pr_err("%s: un-divided PllM_out0 used as clock source\n",
+		       __func__);
+		return;
+	}
+
 	/*
 	 * Note that 'emc_mux' source and 'emc' rate shouldn't be changed at
 	 * the same time due to a HW bug, this won't happen because we're
Signature · 1 line
2.18.0
#9 lore raw

PATCH v4 8/8 memory: tegra: Introduce Tegra20 EMC driver

In reply to Dmitry Osipenko

Introduce driver for the External Memory Controller (EMC) found on Tegra20 chips, which controls the external DRAM on the board. The purpose of this driver is to program memory timing for external memory on the EMC clock rate change.

---
 drivers/memory/tegra/Kconfig       |  10 +
 drivers/memory/tegra/Makefile      |   1 +
 drivers/memory/tegra/tegra20-emc.c | 575 +++++++++++++++++++++++++++++
 3 files changed, 586 insertions(+)
 create mode 100644 drivers/memory/tegra/tegra20-emc.c
drivers/memory/tegra/Kconfig +10 -0
index 6d74e499e18d..34e0b70f5c5f 100644
@@ -6,6 +6,16 @@ config TEGRA_MC
 	  This driver supports the Memory Controller (MC) hardware found on
 	  NVIDIA Tegra SoCs.
 
+config TEGRA20_EMC
+	bool "NVIDIA Tegra20 External Memory Controller driver"
+	default y
+	depends on ARCH_TEGRA_2x_SOC
+	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 TEGRA124_EMC
 	bool "NVIDIA Tegra124 External Memory Controller driver"
 	default y
drivers/memory/tegra/Makefile +1 -0
index 94ab16ba075b..3971a6b7c487 100644
@@ -10,5 +10,6 @@ tegra-mc-$(CONFIG_ARCH_TEGRA_210_SOC) += tegra210.o
 
 obj-$(CONFIG_TEGRA_MC) += tegra-mc.o
 
+obj-$(CONFIG_TEGRA20_EMC)  += tegra20-emc.o
 obj-$(CONFIG_TEGRA124_EMC) += tegra124-emc.o
 obj-$(CONFIG_ARCH_TEGRA_186_SOC) += tegra186.o
drivers/memory/tegra/tegra20-emc.c new file +575 -0

575 lines not on this page · raw message

Signature · 1 line
2.18.0