Linux-Clk Archive on lore.kernel.org
 help / color / Atom feed
* [PATCH v10 00/15] memory: tegra: Introduce Tegra30 EMC driver
@ 2019-08-11 21:00 Dmitry Osipenko
  2019-08-11 21:00 ` [PATCH v10 01/15] clk: tegra20/30: Add custom EMC clock implementation Dmitry Osipenko
                   ` (14 more replies)
  0 siblings, 15 replies; 27+ messages in thread
From: Dmitry Osipenko @ 2019-08-11 21:00 UTC (permalink / raw)
  To: Rob Herring, Michael Turquette, Joseph Lo, Thierry Reding,
	Jonathan Hunter, Peter De Schrijver, Prashant Gaikwad,
	Stephen Boyd
  Cc: devicetree, linux-clk, linux-tegra, linux-kernel

Hello,

This series introduces driver for the External Memory Controller (EMC)
found on Tegra30 chips, it 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. The driver was tested using the ACTMON devfreq
driver that performs memory frequency scaling based on memory-usage load.

Changelog:

v10: - Addressed review comments that were made by Rob Herring to v9 by
       dropping unnecessary reg descriptions, specifying valid ranges and
       using boolean type where appropriate in the device-tree patches.

v9: - Fixed memory corruption bug that was uncovered after introducing
      some extra optimizations to the devfreq driver that allows CPU
      to stay longer in the LP2 cpuidle state. The corruption is caused by
      a very late AUTO-REFRESH re-enabling due to a possible schedule on
      waiting for clk-change completion, the re-enabling is now a part of
      "EMC exec-after-clkchange" hardware sequence.

    - Added "type: object" to T124 MC YAML, that was missed in v8 by accident.

v8: - Added two new patches:

        memory: tegra20-emc: Increase handshake timeout
        memory: tegra20-emc: wait_for_completion_timeout() doesn't return error

      Turned out that memory-clk handshake may take much more time under
      some circumstances. The second patch is a minor cleanup. The same
      changes are also applied to the Terga30 EMC driver addition-patch.

      The pattern-properties of YAML bindings gained "type: object", for
      consistency.

v7: - Addressed review comments that were made by Rob Herring to v6 by
      removing old Terga30 Memory Controller binding once it's converted
      to YAML, by using explicit patterns for the sub-nodes and specifying
      min/max clock rates in the YAML.

    - Two patches that were added in v6 are removed from the series:

        clk: tegra20: emc: Add tegra20_clk_emc_on_pllp()
        ARM: tegra30: cpuidle: Don't enter LP2 on CPU0 when EMC runs off PLLP

      Because the problem with the PLLP is resolved now, turned out it was
      a bug in the CPU-suspend code.

    - The "Introduce Tegra30 EMC driver" patch got a fix for the "Same Freq"
      bit typo, it's a bit 27 and not 16.

v6: - Tegra124 Memory Controller binding factored out into standalone
      binding because it requires to specify MC_EMEM_ARB_MISC1 for EMEM
      programming, which is not required for Tegra30. This makes the
      upstream MC registers specification to match downstream exactly,
      easing porting of boards memory timings configuration to upstream.

    - Tegra30/124 Memory Controller binding converted to YAML.

    - Tegra30 External Memory Controller binding now is in YAML format.

    - Added workaround for hanging during LP2 when EMC runs off PLLP on
      Tegra30 in this new patches:

        clk: tegra20: emc: Add tegra20_clk_emc_on_pllp()
        ARM: tegra30: cpuidle: Don't enter LP2 on CPU0 when EMC runs off PLLP

    - Added info message to the Tegra20/30 EMC drivers, telling about
      RAM code and a number of available timings:

        memory: tegra20-emc: Print a brief info message about the timings

v5: - Addressed review comments that were made by Thierry Reding to v4 by
      adding appropriate copyrights to the source code headers and making
      Tegra30 EMC driver to use common Tegra20 CLK API directly instead
      of having a dummy-proxy functions specifically for Tegra30.

    - Addressed review comments that were made by Stephen Boyd to v4 by
      rewording commit message of the "Add custom EMC clock implementation"
      patch and adding clarifying comment (to that patch as well) which
      tells why EMC is a critical clock.

    - Added suspend-resume to Tegra30 EMC driver to error out if EMC driver
      is in a "bad state" as it will likely cause a hang on entering suspend.

    - Dropped patch "tegra20-emc: Replace clk_get_sys with devm_clk_get"
      because the replaced clocks are actually should be removed altogether
      in the "Drop setting EMC rate to max on probe" patch and that was
      missed by an accident.

    - Added "tegra20-emc: Pre-configure debug register" patch which ensures
      that inappropriate HW debug features are disabled at a probe time.
      The same change is also made in the "Introduce Tegra30 EMC driver"
      patch.

    - Added ACKs to the patches from Peter De Schrijver that he gave to v4
      since all of the v5 changes are actually very minor.

v4: - Addressed review comments that were made by Peter De Schrijver to v3
      by adding fence_udelay() after writes in the "Add custom EMC clock
      implementation" patch.

    - Added two new minor patches:

        memory: tegra: Ensure timing control debug features are disabled
        memory: tegra: Consolidate registers definition into one place

      The first one is needed to ensure that EMC driver will work
      properly regardless of hardware configuration left after boot.
      The second patch is just a minor code cleanup.

    - The "Introduce Tegra30 EMC driver" got also few very minor changes.
      Now every possible error case is handled, nothing is ignored.
      The EMC_DBG register is explicitly initialized during probe to be
      on the safe side.

v3: - Addressed review comments that were made by Stephen Boyd to v2 by
      adding explicit typing for the callback variable, by including
      "clk-provider.h" directly in the code and by dropping __clk_lookup
      usage where possible.

    - Added more patches into this series:

        memory: tegra20-emc: Drop setting EMC rate to max on probe
        memory: tegra20-emc: Adapt for clock driver changes
        memory: tegra20-emc: Include io.h instead of iopoll.h
        memory: tegra20-emc: Replace clk_get_sys with devm_clk_get

      Initially I was going to include these patches into other patchset,
      but changed my mind after rearranging things a tad. The "Adapt for
      clock driver changes" patch is directly related to the clock changes
      done in the first patch of this series, the rest are minor cleanups
      that are fine to include here as well.

    - Added some more words to the commit message of "Add binding for NVIDIA
      Tegra30 External Memory Controller" patch, clarifying why common DDR
      timing device-tree form isn't suitable for Tegra30.

    - The Tegra30 EMC driver now explicitly selects the registers access
      mode (EMC_DBG mux), not relying on the setting left from bootloader.

v2: - Added support for changing MC clock diver configuration based on
      Memory Controller (MC) configuration which is part of the memory
      timing.

    - Merged the "Add custom EMC clock implementation" patch into this
      series because the "Introduce Tegra30 EMC driver" patch directly
      depends on it. Please note that Tegra20 EMC driver will need to be
      adapted for the clock changes as well, I'll send out the Tegra20
      patches after this series will be applied because of some other
      dependencies (devfreq) and because the temporary breakage won't
      be critical (driver will just error out on probe).

    - EMC driver now performs MC configuration validation by checking
      that the number of MC / EMC timings matches and that the timings
      rate is the same.

    - EMC driver now supports timings that want to change the MC clock
      configuration.

    - Other minor prettifying changes of the code.

Dmitry Osipenko (15):
  clk: tegra20/30: Add custom EMC clock implementation
  memory: tegra20-emc: Drop setting EMC rate to max on probe
  memory: tegra20-emc: Adapt for clock driver changes
  memory: tegra20-emc: Include io.h instead of iopoll.h
  memory: tegra20-emc: Pre-configure debug register
  memory: tegra20-emc: Print a brief info message about the timings
  memory: tegra20-emc: Increase handshake timeout
  memory: tegra20-emc: wait_for_completion_timeout() doesn't return
    error
  dt-bindings: memory: tegra30: Convert to Tegra124 YAML
  dt-bindings: memory: Add binding for NVIDIA Tegra30 Memory Controller
  dt-bindings: memory: Add binding for NVIDIA Tegra30 External Memory
    Controller
  memory: tegra: Introduce Tegra30 EMC driver
  memory: tegra: Ensure timing control debug features are disabled
  memory: tegra: Consolidate registers definition into common header
  ARM: dts: tegra30: Add External Memory Controller node

 .../nvidia,tegra124-mc.yaml                   |  152 ++
 .../nvidia,tegra30-emc.yaml                   |  336 +++++
 .../memory-controllers/nvidia,tegra30-mc.txt  |  123 --
 .../memory-controllers/nvidia,tegra30-mc.yaml |  167 +++
 arch/arm/boot/dts/tegra30.dtsi                |    9 +
 drivers/clk/tegra/Makefile                    |    2 +
 drivers/clk/tegra/clk-tegra20-emc.c           |  293 ++++
 drivers/clk/tegra/clk-tegra20.c               |   55 +-
 drivers/clk/tegra/clk-tegra30.c               |   38 +-
 drivers/clk/tegra/clk.h                       |    3 +
 drivers/memory/tegra/Kconfig                  |   10 +
 drivers/memory/tegra/Makefile                 |    1 +
 drivers/memory/tegra/mc.c                     |   42 +-
 drivers/memory/tegra/mc.h                     |   74 +-
 drivers/memory/tegra/tegra124.c               |   20 -
 drivers/memory/tegra/tegra20-emc.c            |  134 +-
 drivers/memory/tegra/tegra30-emc.c            | 1232 +++++++++++++++++
 drivers/memory/tegra/tegra30.c                |   23 +
 include/linux/clk/tegra.h                     |   11 +
 include/soc/tegra/mc.h                        |    2 +-
 20 files changed, 2414 insertions(+), 313 deletions(-)
 create mode 100644 Documentation/devicetree/bindings/memory-controllers/nvidia,tegra124-mc.yaml
 create mode 100644 Documentation/devicetree/bindings/memory-controllers/nvidia,tegra30-emc.yaml
 delete mode 100644 Documentation/devicetree/bindings/memory-controllers/nvidia,tegra30-mc.txt
 create mode 100644 Documentation/devicetree/bindings/memory-controllers/nvidia,tegra30-mc.yaml
 create mode 100644 drivers/clk/tegra/clk-tegra20-emc.c
 create mode 100644 drivers/memory/tegra/tegra30-emc.c

-- 
2.22.0


^ permalink raw reply	[flat|nested] 27+ messages in thread

* [PATCH v10 01/15] clk: tegra20/30: Add custom EMC clock implementation
  2019-08-11 21:00 [PATCH v10 00/15] memory: tegra: Introduce Tegra30 EMC driver Dmitry Osipenko
@ 2019-08-11 21:00 ` Dmitry Osipenko
  2019-08-12 23:12   ` Michał Mirosław
  2019-09-10 10:33   ` Stephen Boyd
  2019-08-11 21:00 ` [PATCH v10 02/15] memory: tegra20-emc: Drop setting EMC rate to max on probe Dmitry Osipenko
                   ` (13 subsequent siblings)
  14 siblings, 2 replies; 27+ messages in thread
From: Dmitry Osipenko @ 2019-08-11 21:00 UTC (permalink / raw)
  To: Rob Herring, Michael Turquette, Joseph Lo, Thierry Reding,
	Jonathan Hunter, Peter De Schrijver, Prashant Gaikwad,
	Stephen Boyd
  Cc: devicetree, linux-clk, linux-tegra, linux-kernel

A proper External Memory Controller clock rounding and parent selection
functionality is required by the EMC drivers, it is not available using
the generic clock implementation because only the Memory Controller driver
is aware of what clock rates are actually available for a particular
device. EMC drivers will have to register a Tegra-specific CLK-API
callback which will perform rounding of a requested rate. EMC clock users
won't be able to request EMC clock by getting -EPROBE_DEFER until EMC
driver is probed and the callback is set up.

The functionality is somewhat similar to the clk-emc.c which serves
Tegra124+ SoCs. The later HW generations support more parent clock sources
and the HW configuration / integration with the EMC drivers differs a tad
from the older gens, hence it's not really worth to try to squash
everything into a single source file.

Acked-by: Peter De Schrijver <pdeschrijver@nvidia.com>
Signed-off-by: Dmitry Osipenko <digetx@gmail.com>
---
 drivers/clk/tegra/Makefile          |   2 +
 drivers/clk/tegra/clk-tegra20-emc.c | 293 ++++++++++++++++++++++++++++
 drivers/clk/tegra/clk-tegra20.c     |  55 ++----
 drivers/clk/tegra/clk-tegra30.c     |  38 ++--
 drivers/clk/tegra/clk.h             |   3 +
 include/linux/clk/tegra.h           |  11 ++
 6 files changed, 350 insertions(+), 52 deletions(-)
 create mode 100644 drivers/clk/tegra/clk-tegra20-emc.c

diff --git a/drivers/clk/tegra/Makefile b/drivers/clk/tegra/Makefile
index 4812e45c2214..df966ca06788 100644
--- a/drivers/clk/tegra/Makefile
+++ b/drivers/clk/tegra/Makefile
@@ -17,7 +17,9 @@ obj-y					+= clk-tegra-fixed.o
 obj-y					+= clk-tegra-super-gen4.o
 obj-$(CONFIG_TEGRA_CLK_EMC)		+= clk-emc.o
 obj-$(CONFIG_ARCH_TEGRA_2x_SOC)         += clk-tegra20.o
+obj-$(CONFIG_ARCH_TEGRA_2x_SOC)		+= clk-tegra20-emc.o
 obj-$(CONFIG_ARCH_TEGRA_3x_SOC)         += clk-tegra30.o
+obj-$(CONFIG_ARCH_TEGRA_3x_SOC)		+= clk-tegra20-emc.o
 obj-$(CONFIG_ARCH_TEGRA_114_SOC)	+= clk-tegra114.o
 obj-$(CONFIG_ARCH_TEGRA_124_SOC)	+= clk-tegra124.o
 obj-$(CONFIG_TEGRA_CLK_DFLL)		+= clk-tegra124-dfll-fcpu.o
diff --git a/drivers/clk/tegra/clk-tegra20-emc.c b/drivers/clk/tegra/clk-tegra20-emc.c
new file mode 100644
index 000000000000..03bf0009a33c
--- /dev/null
+++ b/drivers/clk/tegra/clk-tegra20-emc.c
@@ -0,0 +1,293 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Based on drivers/clk/tegra/clk-emc.c
+ * Copyright (c) 2014, NVIDIA CORPORATION.  All rights reserved.
+ *
+ * Author: Dmitry Osipenko <digetx@gmail.com>
+ * Copyright (C) 2019 GRATE-DRIVER project
+ */
+
+#define pr_fmt(fmt)	"tegra-emc-clk: " fmt
+
+#include <linux/bits.h>
+#include <linux/clk-provider.h>
+#include <linux/clk/tegra.h>
+#include <linux/err.h>
+#include <linux/io.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+
+#include "clk.h"
+
+#define CLK_SOURCE_EMC_2X_CLK_DIVISOR_MASK	GENMASK(7, 0)
+#define CLK_SOURCE_EMC_2X_CLK_SRC_MASK		GENMASK(31, 30)
+#define CLK_SOURCE_EMC_2X_CLK_SRC_SHIFT		30
+
+#define MC_EMC_SAME_FREQ	BIT(16)
+#define USE_PLLM_UD		BIT(29)
+
+#define EMC_SRC_PLL_M		0
+#define EMC_SRC_PLL_C		1
+#define EMC_SRC_PLL_P		2
+#define EMC_SRC_CLK_M		3
+
+static const char * const emc_parent_clk_names[] = {
+	"pll_m", "pll_c", "pll_p", "clk_m",
+};
+
+struct tegra_clk_emc {
+	struct clk_hw hw;
+	void __iomem *reg;
+	bool mc_same_freq;
+	bool want_low_jitter;
+
+	tegra20_clk_emc_round_cb *round_cb;
+	void *cb_arg;
+};
+
+static inline struct tegra_clk_emc *to_tegra_clk_emc(struct clk_hw *hw)
+{
+	return container_of(hw, struct tegra_clk_emc, hw);
+}
+
+static unsigned long emc_recalc_rate(struct clk_hw *hw,
+				     unsigned long parent_rate)
+{
+	struct tegra_clk_emc *emc = to_tegra_clk_emc(hw);
+	u32 val, div;
+
+	val = readl_relaxed(emc->reg);
+	div = val & CLK_SOURCE_EMC_2X_CLK_DIVISOR_MASK;
+
+	return DIV_ROUND_UP(parent_rate * 2, div + 2);
+}
+
+static u8 emc_get_parent(struct clk_hw *hw)
+{
+	struct tegra_clk_emc *emc = to_tegra_clk_emc(hw);
+
+	return readl_relaxed(emc->reg) >> CLK_SOURCE_EMC_2X_CLK_SRC_SHIFT;
+}
+
+static int emc_set_parent(struct clk_hw *hw, u8 index)
+{
+	struct tegra_clk_emc *emc = to_tegra_clk_emc(hw);
+	u32 val, div;
+
+	val = readl_relaxed(emc->reg);
+	val &= ~CLK_SOURCE_EMC_2X_CLK_SRC_MASK;
+	val |= index << CLK_SOURCE_EMC_2X_CLK_SRC_SHIFT;
+
+	div = val & CLK_SOURCE_EMC_2X_CLK_DIVISOR_MASK;
+
+	if (index == EMC_SRC_PLL_M && div == 0 && emc->want_low_jitter)
+		val |= USE_PLLM_UD;
+	else
+		val &= ~USE_PLLM_UD;
+
+	if (emc->mc_same_freq)
+		val |= MC_EMC_SAME_FREQ;
+	else
+		val &= ~MC_EMC_SAME_FREQ;
+
+	writel_relaxed(val, emc->reg);
+
+	fence_udelay(1, emc->reg);
+
+	return 0;
+}
+
+static int emc_set_rate(struct clk_hw *hw, unsigned long rate,
+			unsigned long parent_rate)
+{
+	struct tegra_clk_emc *emc = to_tegra_clk_emc(hw);
+	unsigned int index;
+	u32 val, div;
+
+	div = div_frac_get(rate, parent_rate, 8, 1, 0);
+
+	val = readl_relaxed(emc->reg);
+	val &= ~CLK_SOURCE_EMC_2X_CLK_DIVISOR_MASK;
+	val |= div;
+
+	index = val >> CLK_SOURCE_EMC_2X_CLK_SRC_SHIFT;
+
+	if (index == EMC_SRC_PLL_M && div == 0 && emc->want_low_jitter)
+		val |= USE_PLLM_UD;
+	else
+		val &= ~USE_PLLM_UD;
+
+	if (emc->mc_same_freq)
+		val |= MC_EMC_SAME_FREQ;
+	else
+		val &= ~MC_EMC_SAME_FREQ;
+
+	writel_relaxed(val, emc->reg);
+
+	fence_udelay(1, emc->reg);
+
+	return 0;
+}
+
+static int emc_set_rate_and_parent(struct clk_hw *hw,
+				   unsigned long rate,
+				   unsigned long parent_rate,
+				   u8 index)
+{
+	struct tegra_clk_emc *emc = to_tegra_clk_emc(hw);
+	u32 val, div;
+
+	div = div_frac_get(rate, parent_rate, 8, 1, 0);
+
+	val = readl_relaxed(emc->reg);
+
+	val &= ~CLK_SOURCE_EMC_2X_CLK_SRC_MASK;
+	val |= index << CLK_SOURCE_EMC_2X_CLK_SRC_SHIFT;
+
+	val &= ~CLK_SOURCE_EMC_2X_CLK_DIVISOR_MASK;
+	val |= div;
+
+	if (index == EMC_SRC_PLL_M && div == 0 && emc->want_low_jitter)
+		val |= USE_PLLM_UD;
+	else
+		val &= ~USE_PLLM_UD;
+
+	if (emc->mc_same_freq)
+		val |= MC_EMC_SAME_FREQ;
+	else
+		val &= ~MC_EMC_SAME_FREQ;
+
+	writel_relaxed(val, emc->reg);
+
+	fence_udelay(1, emc->reg);
+
+	return 0;
+}
+
+static int emc_determine_rate(struct clk_hw *hw, struct clk_rate_request *req)
+{
+	struct tegra_clk_emc *emc = to_tegra_clk_emc(hw);
+	struct clk_hw *parent_hw;
+	unsigned long divided_rate;
+	unsigned long parent_rate;
+	unsigned int i;
+	long emc_rate;
+	int div;
+
+	emc_rate = emc->round_cb(req->rate, req->min_rate, req->max_rate,
+				 emc->cb_arg);
+	if (emc_rate < 0)
+		return emc_rate;
+
+	for (i = 0; i < ARRAY_SIZE(emc_parent_clk_names); i++) {
+		parent_hw = clk_hw_get_parent_by_index(hw, i);
+
+		if (req->best_parent_hw == parent_hw)
+			parent_rate = req->best_parent_rate;
+		else
+			parent_rate = clk_hw_get_rate(parent_hw);
+
+		if (emc_rate > parent_rate)
+			continue;
+
+		div = div_frac_get(emc_rate, parent_rate, 8, 1, 0);
+		divided_rate = DIV_ROUND_UP(parent_rate * 2, div + 2);
+
+		if (divided_rate != emc_rate)
+			continue;
+
+		req->best_parent_rate = parent_rate;
+		req->best_parent_hw = parent_hw;
+		req->rate = emc_rate;
+		break;
+	}
+
+	if (i == ARRAY_SIZE(emc_parent_clk_names)) {
+		pr_err_once("can't find parent for rate %lu emc_rate %lu\n",
+			    req->rate, emc_rate);
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static const struct clk_ops tegra_clk_emc_ops = {
+	.recalc_rate = emc_recalc_rate,
+	.get_parent = emc_get_parent,
+	.set_parent = emc_set_parent,
+	.set_rate = emc_set_rate,
+	.set_rate_and_parent = emc_set_rate_and_parent,
+	.determine_rate = emc_determine_rate,
+};
+
+void tegra20_clk_set_emc_round_callback(tegra20_clk_emc_round_cb *round_cb,
+					void *cb_arg)
+{
+	struct clk *clk = __clk_lookup("emc");
+	struct tegra_clk_emc *emc;
+	struct clk_hw *hw;
+
+	if (clk) {
+		hw = __clk_get_hw(clk);
+		emc = to_tegra_clk_emc(hw);
+
+		emc->round_cb = round_cb;
+		emc->cb_arg = cb_arg;
+	}
+}
+
+bool tegra20_clk_emc_driver_available(struct clk_hw *emc_hw)
+{
+	return to_tegra_clk_emc(emc_hw)->round_cb != NULL;
+}
+
+struct clk *tegra20_clk_register_emc(void __iomem *ioaddr, bool low_jitter)
+{
+	struct tegra_clk_emc *emc;
+	struct clk_init_data init;
+	struct clk *clk;
+
+	emc = kzalloc(sizeof(*emc), GFP_KERNEL);
+	if (!emc)
+		return NULL;
+
+	/*
+	 * EMC stands for External Memory Controller.
+	 *
+	 * We don't want EMC clock to be disabled ever by gating its
+	 * parent and whatnot because system is busted immediately in that
+	 * case, hence the clock is marked as critical.
+	 */
+	init.name = "emc";
+	init.ops = &tegra_clk_emc_ops;
+	init.flags = CLK_IS_CRITICAL;
+	init.parent_names = emc_parent_clk_names;
+	init.num_parents = ARRAY_SIZE(emc_parent_clk_names);
+
+	emc->reg = ioaddr;
+	emc->hw.init = &init;
+	emc->want_low_jitter = low_jitter;
+
+	clk = clk_register(NULL, &emc->hw);
+	if (IS_ERR(clk)) {
+		kfree(emc);
+		return NULL;
+	}
+
+	return clk;
+}
+
+int tegra20_clk_prepare_emc_mc_same_freq(struct clk *emc_clk, bool same)
+{
+	struct tegra_clk_emc *emc;
+	struct clk_hw *hw;
+
+	if (!emc_clk)
+		return -EINVAL;
+
+	hw = __clk_get_hw(emc_clk);
+	emc = to_tegra_clk_emc(hw);
+	emc->mc_same_freq = same;
+
+	return 0;
+}
diff --git a/drivers/clk/tegra/clk-tegra20.c b/drivers/clk/tegra/clk-tegra20.c
index bcd871134f45..cceefbd67a3b 100644
--- a/drivers/clk/tegra/clk-tegra20.c
+++ b/drivers/clk/tegra/clk-tegra20.c
@@ -130,8 +130,6 @@ static struct cpu_clk_suspend_context {
 static void __iomem *clk_base;
 static void __iomem *pmc_base;
 
-static DEFINE_SPINLOCK(emc_lock);
-
 #define TEGRA_INIT_DATA_MUX(_name, _parents, _offset,	\
 			    _clk_num, _gate_flags, _clk_id)	\
 	TEGRA_INIT_DATA(_name, NULL, NULL, _parents, _offset,	\
@@ -760,7 +758,6 @@ static const char *pwm_parents[] = { "pll_p", "pll_c", "audio", "clk_m",
 static const char *mux_pllpcm_clkm[] = { "pll_p", "pll_c", "pll_m", "clk_m" };
 static const char *mux_pllpdc_clkm[] = { "pll_p", "pll_d_out0", "pll_c",
 					 "clk_m" };
-static const char *mux_pllmcp_clkm[] = { "pll_m", "pll_c", "pll_p", "clk_m" };
 
 static struct tegra_periph_init_data tegra_periph_clk_list[] = {
 	TEGRA_INIT_DATA_MUX("i2s1", i2s1_parents,     CLK_SOURCE_I2S1,   11, TEGRA_PERIPH_ON_APB, TEGRA20_CLK_I2S1),
@@ -787,41 +784,6 @@ 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)
-{
-	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),
-			       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;
-
-	/* 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
-	 * defining 'emc_mux' and 'emc' as distinct clocks.
-	 */
-	clk = tegra_clk_register_divider("emc", "emc_mux",
-				clk_base + CLK_SOURCE_EMC, CLK_IS_CRITICAL,
-				TEGRA_DIVIDER_INT, 0, 8, 1, &emc_lock);
-	clks[TEGRA20_CLK_EMC] = clk;
-}
-
 static void __init tegra20_periph_clk_init(void)
 {
 	struct tegra_periph_init_data *data;
@@ -835,7 +797,13 @@ static void __init tegra20_periph_clk_init(void)
 	clks[TEGRA20_CLK_AC97] = clk;
 
 	/* emc */
-	tegra20_emc_clk_init();
+	clk = tegra20_clk_register_emc(clk_base + CLK_SOURCE_EMC, false);
+
+	clks[TEGRA20_CLK_EMC] = clk;
+
+	clk = tegra_clk_register_mc("mc", "emc", clk_base + CLK_SOURCE_EMC,
+				    NULL);
+	clks[TEGRA20_CLK_MC] = clk;
 
 	/* dsi */
 	clk = tegra_clk_register_periph_gate("dsi", "pll_d", 0, clk_base, 0,
@@ -1115,6 +1083,8 @@ static struct clk *tegra20_clk_src_onecell_get(struct of_phandle_args *clkspec,
 	if (IS_ERR(clk))
 		return clk;
 
+	hw = __clk_get_hw(clk);
+
 	/*
 	 * Tegra20 CDEV1 and CDEV2 clocks are a bit special case, their parent
 	 * clock is created by the pinctrl driver. It is possible for clk user
@@ -1124,13 +1094,16 @@ static struct clk *tegra20_clk_src_onecell_get(struct of_phandle_args *clkspec,
 	 */
 	if (clkspec->args[0] == TEGRA20_CLK_CDEV1 ||
 	    clkspec->args[0] == TEGRA20_CLK_CDEV2) {
-		hw = __clk_get_hw(clk);
-
 		parent_hw = clk_hw_get_parent(hw);
 		if (!parent_hw)
 			return ERR_PTR(-EPROBE_DEFER);
 	}
 
+	if (clkspec->args[0] == TEGRA20_CLK_EMC) {
+		if (!tegra20_clk_emc_driver_available(hw))
+			return ERR_PTR(-EPROBE_DEFER);
+	}
+
 	return clk;
 }
 
diff --git a/drivers/clk/tegra/clk-tegra30.c b/drivers/clk/tegra/clk-tegra30.c
index 7b4c6a488527..95b0e4a16dd5 100644
--- a/drivers/clk/tegra/clk-tegra30.c
+++ b/drivers/clk/tegra/clk-tegra30.c
@@ -151,7 +151,6 @@ static unsigned long input_freq;
 
 static DEFINE_SPINLOCK(cml_lock);
 static DEFINE_SPINLOCK(pll_d_lock);
-static DEFINE_SPINLOCK(emc_lock);
 
 #define TEGRA_INIT_DATA_MUX(_name, _parents, _offset,	\
 			    _clk_num, _gate_flags, _clk_id)	\
@@ -808,7 +807,7 @@ static struct tegra_clk tegra30_clks[tegra_clk_max] __initdata = {
 	[tegra_clk_pll_a] = { .dt_id = TEGRA30_CLK_PLL_A, .present = true },
 	[tegra_clk_pll_a_out0] = { .dt_id = TEGRA30_CLK_PLL_A_OUT0, .present = true },
 	[tegra_clk_cec] = { .dt_id = TEGRA30_CLK_CEC, .present = true },
-	[tegra_clk_emc] = { .dt_id = TEGRA30_CLK_EMC, .present = true },
+	[tegra_clk_emc] = { .dt_id = TEGRA30_CLK_EMC, .present = false },
 };
 
 static const char *pll_e_parents[] = { "pll_ref", "pll_p" };
@@ -995,7 +994,6 @@ static void __init tegra30_super_clk_init(void)
 static const char *mux_pllacp_clkm[] = { "pll_a_out0", "unused", "pll_p",
 					 "clk_m" };
 static const char *mux_pllpcm_clkm[] = { "pll_p", "pll_c", "pll_m", "clk_m" };
-static const char *mux_pllmcp_clkm[] = { "pll_m", "pll_c", "pll_p", "clk_m" };
 static const char *spdif_out_parents[] = { "pll_a_out0", "spdif_2x", "pll_p",
 					   "clk_m" };
 static const char *mux_pllmcpa[] = { "pll_m", "pll_c", "pll_p", "pll_a_out0" };
@@ -1044,14 +1042,12 @@ static void __init tegra30_periph_clk_init(void)
 	clks[TEGRA30_CLK_AFI] = 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 = tegra20_clk_register_emc(clk_base + CLK_SOURCE_EMC, true);
+
+	clks[TEGRA30_CLK_EMC] = clk;
 
-	clk = tegra_clk_register_mc("mc", "emc_mux", clk_base + CLK_SOURCE_EMC,
-				    &emc_lock);
+	clk = tegra_clk_register_mc("mc", "emc", clk_base + CLK_SOURCE_EMC,
+				    NULL);
 	clks[TEGRA30_CLK_MC] = clk;
 
 	/* cml0 */
@@ -1302,6 +1298,26 @@ static struct tegra_audio_clk_info tegra30_audio_plls[] = {
 	{ "pll_a", &pll_a_params, tegra_clk_pll_a, "pll_p_out1" },
 };
 
+static struct clk *tegra30_clk_src_onecell_get(struct of_phandle_args *clkspec,
+					       void *data)
+{
+	struct clk_hw *hw;
+	struct clk *clk;
+
+	clk = of_clk_src_onecell_get(clkspec, data);
+	if (IS_ERR(clk))
+		return clk;
+
+	hw = __clk_get_hw(clk);
+
+	if (clkspec->args[0] == TEGRA30_CLK_EMC) {
+		if (!tegra20_clk_emc_driver_available(hw))
+			return ERR_PTR(-EPROBE_DEFER);
+	}
+
+	return clk;
+}
+
 static void __init tegra30_clock_init(struct device_node *np)
 {
 	struct device_node *node;
@@ -1345,7 +1361,7 @@ static void __init tegra30_clock_init(struct device_node *np)
 
 	tegra_init_dup_clks(tegra_clk_duplicates, clks, TEGRA30_CLK_CLK_MAX);
 
-	tegra_add_of_provider(np, of_clk_src_onecell_get);
+	tegra_add_of_provider(np, tegra30_clk_src_onecell_get);
 	tegra_register_devclks(devclks, ARRAY_SIZE(devclks));
 
 	tegra_clk_apply_init_table = tegra30_clock_apply_init_table;
diff --git a/drivers/clk/tegra/clk.h b/drivers/clk/tegra/clk.h
index a4fbf55930aa..f81c10654aa9 100644
--- a/drivers/clk/tegra/clk.h
+++ b/drivers/clk/tegra/clk.h
@@ -842,4 +842,7 @@ int div_frac_get(unsigned long rate, unsigned parent_rate, u8 width,
 		udelay(delay);		\
 	} while (0)
 
+bool tegra20_clk_emc_driver_available(struct clk_hw *emc_hw);
+struct clk *tegra20_clk_register_emc(void __iomem *ioaddr, bool low_jitter);
+
 #endif /* TEGRA_CLK_H */
diff --git a/include/linux/clk/tegra.h b/include/linux/clk/tegra.h
index cf0f2cb5e109..2b1b35240074 100644
--- a/include/linux/clk/tegra.h
+++ b/include/linux/clk/tegra.h
@@ -132,4 +132,15 @@ extern void tegra210_put_utmipll_in_iddq(void);
 extern void tegra210_put_utmipll_out_iddq(void);
 extern int tegra210_clk_handle_mbist_war(unsigned int id);
 
+struct clk;
+
+typedef long (tegra20_clk_emc_round_cb)(unsigned long rate,
+					unsigned long min_rate,
+					unsigned long max_rate,
+					void *arg);
+
+void tegra20_clk_set_emc_round_callback(tegra20_clk_emc_round_cb *round_cb,
+					void *cb_arg);
+int tegra20_clk_prepare_emc_mc_same_freq(struct clk *emc_clk, bool same);
+
 #endif /* __LINUX_CLK_TEGRA_H_ */
-- 
2.22.0


^ permalink raw reply	[flat|nested] 27+ messages in thread

* [PATCH v10 02/15] memory: tegra20-emc: Drop setting EMC rate to max on probe
  2019-08-11 21:00 [PATCH v10 00/15] memory: tegra: Introduce Tegra30 EMC driver Dmitry Osipenko
  2019-08-11 21:00 ` [PATCH v10 01/15] clk: tegra20/30: Add custom EMC clock implementation Dmitry Osipenko
@ 2019-08-11 21:00 ` Dmitry Osipenko
  2019-08-11 21:00 ` [PATCH v10 03/15] memory: tegra20-emc: Adapt for clock driver changes Dmitry Osipenko
                   ` (12 subsequent siblings)
  14 siblings, 0 replies; 27+ messages in thread
From: Dmitry Osipenko @ 2019-08-11 21:00 UTC (permalink / raw)
  To: Rob Herring, Michael Turquette, Joseph Lo, Thierry Reding,
	Jonathan Hunter, Peter De Schrijver, Prashant Gaikwad,
	Stephen Boyd
  Cc: devicetree, linux-clk, linux-tegra, linux-kernel

The memory frequency scaling will be managed by tegra20-devfreq driver
and PM QoS once all the prerequisite patches will get upstreamed.
The parent clock is now managed by the clock driver and we also should
assume that PLLM rate can't be changed on some devices (Galaxy Tab 10.1
for example). Altogether there is no point in touching of clock's rate
from the EMC driver.

Acked-by: Peter De Schrijver <pdeschrijver@nvidia.com>
Signed-off-by: Dmitry Osipenko <digetx@gmail.com>
---
 drivers/memory/tegra/tegra20-emc.c | 78 +-----------------------------
 1 file changed, 1 insertion(+), 77 deletions(-)

diff --git a/drivers/memory/tegra/tegra20-emc.c b/drivers/memory/tegra/tegra20-emc.c
index 9ee5bef49e47..da8fa592b071 100644
--- a/drivers/memory/tegra/tegra20-emc.c
+++ b/drivers/memory/tegra/tegra20-emc.c
@@ -137,9 +137,6 @@ struct tegra_emc {
 	struct device *dev;
 	struct completion clk_handshake_complete;
 	struct notifier_block clk_nb;
-	struct clk *backup_clk;
-	struct clk *emc_mux;
-	struct clk *pll_m;
 	struct clk *clk;
 	void __iomem *regs;
 
@@ -424,41 +421,6 @@ static int emc_setup_hw(struct tegra_emc *emc)
 	return 0;
 }
 
-static int emc_init(struct tegra_emc *emc, unsigned long rate)
-{
-	int err;
-
-	err = clk_set_parent(emc->emc_mux, emc->backup_clk);
-	if (err) {
-		dev_err(emc->dev,
-			"failed to reparent to backup source: %d\n", err);
-		return err;
-	}
-
-	err = clk_set_rate(emc->pll_m, rate);
-	if (err) {
-		dev_err(emc->dev,
-			"failed to change pll_m rate: %d\n", err);
-		return err;
-	}
-
-	err = clk_set_parent(emc->emc_mux, emc->pll_m);
-	if (err) {
-		dev_err(emc->dev,
-			"failed to reparent to pll_m: %d\n", err);
-		return err;
-	}
-
-	err = clk_set_rate(emc->clk, rate);
-	if (err) {
-		dev_err(emc->dev,
-			"failed to change emc rate: %d\n", err);
-		return err;
-	}
-
-	return 0;
-}
-
 static int tegra_emc_probe(struct platform_device *pdev)
 {
 	struct device_node *np;
@@ -522,52 +484,14 @@ static int tegra_emc_probe(struct platform_device *pdev)
 		return err;
 	}
 
-	emc->pll_m = clk_get_sys(NULL, "pll_m");
-	if (IS_ERR(emc->pll_m)) {
-		err = PTR_ERR(emc->pll_m);
-		dev_err(&pdev->dev, "failed to get pll_m clock: %d\n", err);
-		return err;
-	}
-
-	emc->backup_clk = clk_get_sys(NULL, "pll_p");
-	if (IS_ERR(emc->backup_clk)) {
-		err = PTR_ERR(emc->backup_clk);
-		dev_err(&pdev->dev, "failed to get pll_p clock: %d\n", err);
-		goto put_pll_m;
-	}
-
-	emc->emc_mux = clk_get_parent(emc->clk);
-	if (IS_ERR(emc->emc_mux)) {
-		err = PTR_ERR(emc->emc_mux);
-		dev_err(&pdev->dev, "failed to get emc_mux clock: %d\n", err);
-		goto put_backup;
-	}
-
 	err = clk_notifier_register(emc->clk, &emc->clk_nb);
 	if (err) {
 		dev_err(&pdev->dev, "failed to register clk notifier: %d\n",
 			err);
-		goto put_backup;
-	}
-
-	/* set DRAM clock rate to maximum */
-	err = emc_init(emc, emc->timings[emc->num_timings - 1].rate);
-	if (err) {
-		dev_err(&pdev->dev, "failed to initialize EMC clock rate: %d\n",
-			err);
-		goto unreg_notifier;
+		return err;
 	}
 
 	return 0;
-
-unreg_notifier:
-	clk_notifier_unregister(emc->clk, &emc->clk_nb);
-put_backup:
-	clk_put(emc->backup_clk);
-put_pll_m:
-	clk_put(emc->pll_m);
-
-	return err;
 }
 
 static const struct of_device_id tegra_emc_of_match[] = {
-- 
2.22.0


^ permalink raw reply	[flat|nested] 27+ messages in thread

* [PATCH v10 03/15] memory: tegra20-emc: Adapt for clock driver changes
  2019-08-11 21:00 [PATCH v10 00/15] memory: tegra: Introduce Tegra30 EMC driver Dmitry Osipenko
  2019-08-11 21:00 ` [PATCH v10 01/15] clk: tegra20/30: Add custom EMC clock implementation Dmitry Osipenko
  2019-08-11 21:00 ` [PATCH v10 02/15] memory: tegra20-emc: Drop setting EMC rate to max on probe Dmitry Osipenko
@ 2019-08-11 21:00 ` Dmitry Osipenko
  2019-08-11 21:00 ` [PATCH v10 04/15] memory: tegra20-emc: Include io.h instead of iopoll.h Dmitry Osipenko
                   ` (11 subsequent siblings)
  14 siblings, 0 replies; 27+ messages in thread
From: Dmitry Osipenko @ 2019-08-11 21:00 UTC (permalink / raw)
  To: Rob Herring, Michael Turquette, Joseph Lo, Thierry Reding,
	Jonathan Hunter, Peter De Schrijver, Prashant Gaikwad,
	Stephen Boyd
  Cc: devicetree, linux-clk, linux-tegra, linux-kernel

Now Terga20 and Tegra30 EMC drivers should provide clock-rounding
functionality using the new Tegra-CLK driver API.

Acked-by: Peter De Schrijver <pdeschrijver@nvidia.com>
Signed-off-by: Dmitry Osipenko <digetx@gmail.com>
---
 drivers/memory/tegra/tegra20-emc.c | 50 ++++++++++++++++++++++++++++--
 1 file changed, 48 insertions(+), 2 deletions(-)

diff --git a/drivers/memory/tegra/tegra20-emc.c b/drivers/memory/tegra/tegra20-emc.c
index da8fa592b071..b519f02b0ee9 100644
--- a/drivers/memory/tegra/tegra20-emc.c
+++ b/drivers/memory/tegra/tegra20-emc.c
@@ -6,6 +6,7 @@
  */
 
 #include <linux/clk.h>
+#include <linux/clk/tegra.h>
 #include <linux/completion.h>
 #include <linux/err.h>
 #include <linux/interrupt.h>
@@ -421,6 +422,44 @@ static int emc_setup_hw(struct tegra_emc *emc)
 	return 0;
 }
 
+static long emc_round_rate(unsigned long rate,
+			   unsigned long min_rate,
+			   unsigned long max_rate,
+			   void *arg)
+{
+	struct emc_timing *timing = NULL;
+	struct tegra_emc *emc = arg;
+	unsigned int i;
+
+	min_rate = min(min_rate, emc->timings[emc->num_timings - 1].rate);
+
+	for (i = 0; i < emc->num_timings; i++) {
+		if (emc->timings[i].rate < rate && i != emc->num_timings - 1)
+			continue;
+
+		if (emc->timings[i].rate > max_rate) {
+			i = max(i, 1u) - 1;
+
+			if (emc->timings[i].rate < min_rate)
+				break;
+		}
+
+		if (emc->timings[i].rate < min_rate)
+			continue;
+
+		timing = &emc->timings[i];
+		break;
+	}
+
+	if (!timing) {
+		dev_err(emc->dev, "no timing for rate %lu min %lu max %lu\n",
+			rate, min_rate, max_rate);
+		return -EINVAL;
+	}
+
+	return timing->rate;
+}
+
 static int tegra_emc_probe(struct platform_device *pdev)
 {
 	struct device_node *np;
@@ -477,21 +516,28 @@ static int tegra_emc_probe(struct platform_device *pdev)
 		return err;
 	}
 
+	tegra20_clk_set_emc_round_callback(emc_round_rate, emc);
+
 	emc->clk = devm_clk_get(&pdev->dev, "emc");
 	if (IS_ERR(emc->clk)) {
 		err = PTR_ERR(emc->clk);
 		dev_err(&pdev->dev, "failed to get emc clock: %d\n", err);
-		return err;
+		goto unset_cb;
 	}
 
 	err = clk_notifier_register(emc->clk, &emc->clk_nb);
 	if (err) {
 		dev_err(&pdev->dev, "failed to register clk notifier: %d\n",
 			err);
-		return err;
+		goto unset_cb;
 	}
 
 	return 0;
+
+unset_cb:
+	tegra20_clk_set_emc_round_callback(NULL, NULL);
+
+	return err;
 }
 
 static const struct of_device_id tegra_emc_of_match[] = {
-- 
2.22.0


^ permalink raw reply	[flat|nested] 27+ messages in thread

* [PATCH v10 04/15] memory: tegra20-emc: Include io.h instead of iopoll.h
  2019-08-11 21:00 [PATCH v10 00/15] memory: tegra: Introduce Tegra30 EMC driver Dmitry Osipenko
                   ` (2 preceding siblings ...)
  2019-08-11 21:00 ` [PATCH v10 03/15] memory: tegra20-emc: Adapt for clock driver changes Dmitry Osipenko
@ 2019-08-11 21:00 ` Dmitry Osipenko
  2019-08-11 21:00 ` [PATCH v10 05/15] memory: tegra20-emc: Pre-configure debug register Dmitry Osipenko
                   ` (10 subsequent siblings)
  14 siblings, 0 replies; 27+ messages in thread
From: Dmitry Osipenko @ 2019-08-11 21:00 UTC (permalink / raw)
  To: Rob Herring, Michael Turquette, Joseph Lo, Thierry Reding,
	Jonathan Hunter, Peter De Schrijver, Prashant Gaikwad,
	Stephen Boyd
  Cc: devicetree, linux-clk, linux-tegra, linux-kernel

The register polling code was gone, but the included header change was
missed. Fix it up for consistency.

Acked-by: Peter De Schrijver <pdeschrijver@nvidia.com>
Signed-off-by: Dmitry Osipenko <digetx@gmail.com>
---
 drivers/memory/tegra/tegra20-emc.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/drivers/memory/tegra/tegra20-emc.c b/drivers/memory/tegra/tegra20-emc.c
index b519f02b0ee9..1ce351dd5461 100644
--- a/drivers/memory/tegra/tegra20-emc.c
+++ b/drivers/memory/tegra/tegra20-emc.c
@@ -10,7 +10,7 @@
 #include <linux/completion.h>
 #include <linux/err.h>
 #include <linux/interrupt.h>
-#include <linux/iopoll.h>
+#include <linux/io.h>
 #include <linux/kernel.h>
 #include <linux/module.h>
 #include <linux/of.h>
-- 
2.22.0


^ permalink raw reply	[flat|nested] 27+ messages in thread

* [PATCH v10 05/15] memory: tegra20-emc: Pre-configure debug register
  2019-08-11 21:00 [PATCH v10 00/15] memory: tegra: Introduce Tegra30 EMC driver Dmitry Osipenko
                   ` (3 preceding siblings ...)
  2019-08-11 21:00 ` [PATCH v10 04/15] memory: tegra20-emc: Include io.h instead of iopoll.h Dmitry Osipenko
@ 2019-08-11 21:00 ` Dmitry Osipenko
  2019-08-11 21:00 ` [PATCH v10 06/15] memory: tegra20-emc: Print a brief info message about the timings Dmitry Osipenko
                   ` (9 subsequent siblings)
  14 siblings, 0 replies; 27+ messages in thread
From: Dmitry Osipenko @ 2019-08-11 21:00 UTC (permalink / raw)
  To: Rob Herring, Michael Turquette, Joseph Lo, Thierry Reding,
	Jonathan Hunter, Peter De Schrijver, Prashant Gaikwad,
	Stephen Boyd
  Cc: devicetree, linux-clk, linux-tegra, linux-kernel

The driver expects certain debug features to be disabled in order to
work properly. Let's disable them explicitly for consistency and to not
rely on a boot state.

Acked-by: Peter De Schrijver <pdeschrijver@nvidia.com>
Signed-off-by: Dmitry Osipenko <digetx@gmail.com>
---
 drivers/memory/tegra/tegra20-emc.c | 17 ++++++++++++++++-
 1 file changed, 16 insertions(+), 1 deletion(-)

diff --git a/drivers/memory/tegra/tegra20-emc.c b/drivers/memory/tegra/tegra20-emc.c
index 1ce351dd5461..85c24f285fd4 100644
--- a/drivers/memory/tegra/tegra20-emc.c
+++ b/drivers/memory/tegra/tegra20-emc.c
@@ -22,6 +22,7 @@
 
 #define EMC_INTSTATUS				0x000
 #define EMC_INTMASK				0x004
+#define EMC_DBG					0x008
 #define EMC_TIMING_CONTROL			0x028
 #define EMC_RC					0x02c
 #define EMC_RFC					0x030
@@ -80,6 +81,12 @@
 #define EMC_REFRESH_OVERFLOW_INT		BIT(3)
 #define EMC_CLKCHANGE_COMPLETE_INT		BIT(4)
 
+#define EMC_DBG_READ_MUX_ASSEMBLY		BIT(0)
+#define EMC_DBG_WRITE_MUX_ACTIVE		BIT(1)
+#define EMC_DBG_FORCE_UPDATE			BIT(2)
+#define EMC_DBG_READ_DQM_CTRL			BIT(9)
+#define EMC_DBG_CFG_PRIORITY			BIT(24)
+
 static const u16 emc_timing_registers[] = {
 	EMC_RC,
 	EMC_RFC,
@@ -396,7 +403,7 @@ tegra_emc_find_node_by_ram_code(struct device *dev)
 static int emc_setup_hw(struct tegra_emc *emc)
 {
 	u32 intmask = EMC_REFRESH_OVERFLOW_INT | EMC_CLKCHANGE_COMPLETE_INT;
-	u32 emc_cfg;
+	u32 emc_cfg, emc_dbg;
 
 	emc_cfg = readl_relaxed(emc->regs + EMC_CFG_2);
 
@@ -419,6 +426,14 @@ static int emc_setup_hw(struct tegra_emc *emc)
 	writel_relaxed(intmask, emc->regs + EMC_INTMASK);
 	writel_relaxed(intmask, emc->regs + EMC_INTSTATUS);
 
+	/* ensure that unwanted debug features are disabled */
+	emc_dbg = readl_relaxed(emc->regs + EMC_DBG);
+	emc_dbg |= EMC_DBG_CFG_PRIORITY;
+	emc_dbg &= ~EMC_DBG_READ_MUX_ASSEMBLY;
+	emc_dbg &= ~EMC_DBG_WRITE_MUX_ACTIVE;
+	emc_dbg &= ~EMC_DBG_FORCE_UPDATE;
+	writel_relaxed(emc_dbg, emc->regs + EMC_DBG);
+
 	return 0;
 }
 
-- 
2.22.0


^ permalink raw reply	[flat|nested] 27+ messages in thread

* [PATCH v10 06/15] memory: tegra20-emc: Print a brief info message about the timings
  2019-08-11 21:00 [PATCH v10 00/15] memory: tegra: Introduce Tegra30 EMC driver Dmitry Osipenko
                   ` (4 preceding siblings ...)
  2019-08-11 21:00 ` [PATCH v10 05/15] memory: tegra20-emc: Pre-configure debug register Dmitry Osipenko
@ 2019-08-11 21:00 ` Dmitry Osipenko
  2019-08-11 21:00 ` [PATCH v10 07/15] memory: tegra20-emc: Increase handshake timeout Dmitry Osipenko
                   ` (8 subsequent siblings)
  14 siblings, 0 replies; 27+ messages in thread
From: Dmitry Osipenko @ 2019-08-11 21:00 UTC (permalink / raw)
  To: Rob Herring, Michael Turquette, Joseph Lo, Thierry Reding,
	Jonathan Hunter, Peter De Schrijver, Prashant Gaikwad,
	Stephen Boyd
  Cc: devicetree, linux-clk, linux-tegra, linux-kernel

During boot print how many memory timings got the driver and what's the
RAM code. This is a very useful information when something is wrong with
boards memory timing.

Suggested-by: Marc Dietrich <marvin24@gmx.de>
Signed-off-by: Dmitry Osipenko <digetx@gmail.com>
---
 drivers/memory/tegra/tegra20-emc.c | 7 +++++++
 1 file changed, 7 insertions(+)

diff --git a/drivers/memory/tegra/tegra20-emc.c b/drivers/memory/tegra/tegra20-emc.c
index 85c24f285fd4..25a6aad6a7a9 100644
--- a/drivers/memory/tegra/tegra20-emc.c
+++ b/drivers/memory/tegra/tegra20-emc.c
@@ -368,6 +368,13 @@ 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);
+
 	return 0;
 }
 
-- 
2.22.0


^ permalink raw reply	[flat|nested] 27+ messages in thread

* [PATCH v10 07/15] memory: tegra20-emc: Increase handshake timeout
  2019-08-11 21:00 [PATCH v10 00/15] memory: tegra: Introduce Tegra30 EMC driver Dmitry Osipenko
                   ` (5 preceding siblings ...)
  2019-08-11 21:00 ` [PATCH v10 06/15] memory: tegra20-emc: Print a brief info message about the timings Dmitry Osipenko
@ 2019-08-11 21:00 ` Dmitry Osipenko
  2019-08-11 21:00 ` [PATCH v10 08/15] memory: tegra20-emc: wait_for_completion_timeout() doesn't return error Dmitry Osipenko
                   ` (7 subsequent siblings)
  14 siblings, 0 replies; 27+ messages in thread
From: Dmitry Osipenko @ 2019-08-11 21:00 UTC (permalink / raw)
  To: Rob Herring, Michael Turquette, Joseph Lo, Thierry Reding,
	Jonathan Hunter, Peter De Schrijver, Prashant Gaikwad,
	Stephen Boyd
  Cc: devicetree, linux-clk, linux-tegra, linux-kernel

Turned out that it could take over a millisecond under some circumstances,
like running on a very low CPU/memory frequency. TRM says that handshake
happens when there is a "safe" moment, but not explains exactly what that
moment is. Apparently at least memory should be idling and thus the low
frequency should be a reasonable cause for a longer handshake delay.

Signed-off-by: Dmitry Osipenko <digetx@gmail.com>
---
 drivers/memory/tegra/tegra20-emc.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/drivers/memory/tegra/tegra20-emc.c b/drivers/memory/tegra/tegra20-emc.c
index 25a6aad6a7a9..da75efc632c7 100644
--- a/drivers/memory/tegra/tegra20-emc.c
+++ b/drivers/memory/tegra/tegra20-emc.c
@@ -236,7 +236,7 @@ static int emc_complete_timing_change(struct tegra_emc *emc, bool flush)
 	}
 
 	timeout = wait_for_completion_timeout(&emc->clk_handshake_complete,
-					      usecs_to_jiffies(100));
+					      msecs_to_jiffies(100));
 	if (timeout == 0) {
 		dev_err(emc->dev, "EMC-CAR handshake failed\n");
 		return -EIO;
-- 
2.22.0


^ permalink raw reply	[flat|nested] 27+ messages in thread

* [PATCH v10 08/15] memory: tegra20-emc: wait_for_completion_timeout() doesn't return error
  2019-08-11 21:00 [PATCH v10 00/15] memory: tegra: Introduce Tegra30 EMC driver Dmitry Osipenko
                   ` (6 preceding siblings ...)
  2019-08-11 21:00 ` [PATCH v10 07/15] memory: tegra20-emc: Increase handshake timeout Dmitry Osipenko
@ 2019-08-11 21:00 ` Dmitry Osipenko
  2019-08-11 21:00 ` [PATCH v10 09/15] dt-bindings: memory: tegra30: Convert to Tegra124 YAML Dmitry Osipenko
                   ` (6 subsequent siblings)
  14 siblings, 0 replies; 27+ messages in thread
From: Dmitry Osipenko @ 2019-08-11 21:00 UTC (permalink / raw)
  To: Rob Herring, Michael Turquette, Joseph Lo, Thierry Reding,
	Jonathan Hunter, Peter De Schrijver, Prashant Gaikwad,
	Stephen Boyd
  Cc: devicetree, linux-clk, linux-tegra, linux-kernel

The "interruptible" variant may error out, the "uninterruptible" not.

Signed-off-by: Dmitry Osipenko <digetx@gmail.com>
---
 drivers/memory/tegra/tegra20-emc.c | 6 +-----
 1 file changed, 1 insertion(+), 5 deletions(-)

diff --git a/drivers/memory/tegra/tegra20-emc.c b/drivers/memory/tegra/tegra20-emc.c
index da75efc632c7..1b23b1c34476 100644
--- a/drivers/memory/tegra/tegra20-emc.c
+++ b/drivers/memory/tegra/tegra20-emc.c
@@ -224,7 +224,7 @@ static int emc_prepare_timing_change(struct tegra_emc *emc, unsigned long rate)
 
 static int emc_complete_timing_change(struct tegra_emc *emc, bool flush)
 {
-	long timeout;
+	unsigned long timeout;
 
 	dev_dbg(emc->dev, "%s: flush %d\n", __func__, flush);
 
@@ -240,10 +240,6 @@ static int emc_complete_timing_change(struct tegra_emc *emc, bool flush)
 	if (timeout == 0) {
 		dev_err(emc->dev, "EMC-CAR handshake failed\n");
 		return -EIO;
-	} else if (timeout < 0) {
-		dev_err(emc->dev, "failed to wait for EMC-CAR handshake: %ld\n",
-			timeout);
-		return timeout;
 	}
 
 	return 0;
-- 
2.22.0


^ permalink raw reply	[flat|nested] 27+ messages in thread

* [PATCH v10 09/15] dt-bindings: memory: tegra30: Convert to Tegra124 YAML
  2019-08-11 21:00 [PATCH v10 00/15] memory: tegra: Introduce Tegra30 EMC driver Dmitry Osipenko
                   ` (7 preceding siblings ...)
  2019-08-11 21:00 ` [PATCH v10 08/15] memory: tegra20-emc: wait_for_completion_timeout() doesn't return error Dmitry Osipenko
@ 2019-08-11 21:00 ` Dmitry Osipenko
  2019-08-12 19:53   ` Rob Herring
  2019-08-11 21:00 ` [PATCH v10 10/15] dt-bindings: memory: Add binding for NVIDIA Tegra30 Memory Controller Dmitry Osipenko
                   ` (5 subsequent siblings)
  14 siblings, 1 reply; 27+ messages in thread
From: Dmitry Osipenko @ 2019-08-11 21:00 UTC (permalink / raw)
  To: Rob Herring, Michael Turquette, Joseph Lo, Thierry Reding,
	Jonathan Hunter, Peter De Schrijver, Prashant Gaikwad,
	Stephen Boyd
  Cc: devicetree, linux-clk, linux-tegra, linux-kernel

The Tegra30 binding will actually differ from the Tegra124 a tad, in
particular the EMEM configuration description. Hence rename the binding
to Tegra124 during of the conversion to YAML.

Reviewed-by: Rob Herring <robh@kernel.org>
Signed-off-by: Dmitry Osipenko <digetx@gmail.com>
---
 .../nvidia,tegra124-mc.yaml                   | 152 ++++++++++++++++++
 .../memory-controllers/nvidia,tegra30-mc.txt  | 123 --------------
 2 files changed, 152 insertions(+), 123 deletions(-)
 create mode 100644 Documentation/devicetree/bindings/memory-controllers/nvidia,tegra124-mc.yaml
 delete mode 100644 Documentation/devicetree/bindings/memory-controllers/nvidia,tegra30-mc.txt

diff --git a/Documentation/devicetree/bindings/memory-controllers/nvidia,tegra124-mc.yaml b/Documentation/devicetree/bindings/memory-controllers/nvidia,tegra124-mc.yaml
new file mode 100644
index 000000000000..30d9fb193d7f
--- /dev/null
+++ b/Documentation/devicetree/bindings/memory-controllers/nvidia,tegra124-mc.yaml
@@ -0,0 +1,152 @@
+# SPDX-License-Identifier: (GPL-2.0)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/memory-controllers/nvidia,tegra124-mc.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: NVIDIA Tegra124 SoC Memory Controller
+
+maintainers:
+  - Jon Hunter <jonathanh@nvidia.com>
+  - Thierry Reding <thierry.reding@gmail.com>
+
+description: |
+  Tegra124 SoC features a hybrid 2x32-bit / 1x64-bit memory controller.
+  These are interleaved to provide high performance with the load shared across
+  two memory channels. The Tegra124 Memory Controller handles memory requests
+  from internal clients and arbitrates among them to allocate memory bandwidth
+  for DDR3L and LPDDR3 SDRAMs.
+
+properties:
+  compatible:
+    const: nvidia,tegra124-mc
+
+  reg:
+    maxItems: 1
+
+  clocks:
+    maxItems: 1
+
+  clock-names:
+    items:
+      - const: mc
+
+  interrupts:
+    maxItems: 1
+
+  "#reset-cells":
+    const: 1
+
+  "#iommu-cells":
+    const: 1
+
+patternProperties:
+  "^emc-timings-[0-9]+$":
+    type: object
+    properties:
+      nvidia,ram-code:
+        $ref: /schemas/types.yaml#/definitions/uint32
+        description:
+          Value of RAM_CODE this timing set is used for.
+
+    patternProperties:
+      "^timing-[0-9]+$":
+        type: object
+        properties:
+          clock-frequency:
+            description:
+              Memory clock rate in Hz.
+            minimum: 1000000
+            maximum: 1066000000
+
+          nvidia,emem-configuration:
+            $ref: /schemas/types.yaml#/definitions/uint32-array
+            description: |
+              Values to be written to the EMEM register block. See section
+              "15.6.1 MC Registers" in the TRM.
+            items:
+              - description: MC_EMEM_ARB_CFG
+              - description: MC_EMEM_ARB_OUTSTANDING_REQ
+              - description: MC_EMEM_ARB_TIMING_RCD
+              - description: MC_EMEM_ARB_TIMING_RP
+              - description: MC_EMEM_ARB_TIMING_RC
+              - description: MC_EMEM_ARB_TIMING_RAS
+              - description: MC_EMEM_ARB_TIMING_FAW
+              - description: MC_EMEM_ARB_TIMING_RRD
+              - description: MC_EMEM_ARB_TIMING_RAP2PRE
+              - description: MC_EMEM_ARB_TIMING_WAP2PRE
+              - description: MC_EMEM_ARB_TIMING_R2R
+              - description: MC_EMEM_ARB_TIMING_W2W
+              - description: MC_EMEM_ARB_TIMING_R2W
+              - description: MC_EMEM_ARB_TIMING_W2R
+              - description: MC_EMEM_ARB_DA_TURNS
+              - description: MC_EMEM_ARB_DA_COVERS
+              - description: MC_EMEM_ARB_MISC0
+              - description: MC_EMEM_ARB_MISC1
+              - description: MC_EMEM_ARB_RING1_THROTTLE
+
+        required:
+          - clock-frequency
+          - nvidia,emem-configuration
+
+        additionalProperties: false
+
+    required:
+      - nvidia,ram-code
+
+    additionalProperties: false
+
+required:
+  - compatible
+  - reg
+  - interrupts
+  - clocks
+  - clock-names
+  - "#reset-cells"
+  - "#iommu-cells"
+
+additionalProperties: false
+
+examples:
+  - |
+    memory-controller@70019000 {
+        compatible = "nvidia,tegra124-mc";
+        reg = <0x0 0x70019000 0x0 0x1000>;
+        clocks = <&tegra_car 32>;
+        clock-names = "mc";
+
+        interrupts = <0 77 4>;
+
+        #iommu-cells = <1>;
+        #reset-cells = <1>;
+
+        emc-timings-3 {
+            nvidia,ram-code = <3>;
+
+            timing-12750000 {
+                clock-frequency = <12750000>;
+
+                nvidia,emem-configuration = <
+                    0x40040001 /* MC_EMEM_ARB_CFG */
+                    0x8000000a /* MC_EMEM_ARB_OUTSTANDING_REQ */
+                    0x00000001 /* MC_EMEM_ARB_TIMING_RCD */
+                    0x00000001 /* MC_EMEM_ARB_TIMING_RP */
+                    0x00000002 /* MC_EMEM_ARB_TIMING_RC */
+                    0x00000000 /* MC_EMEM_ARB_TIMING_RAS */
+                    0x00000002 /* MC_EMEM_ARB_TIMING_FAW */
+                    0x00000001 /* MC_EMEM_ARB_TIMING_RRD */
+                    0x00000002 /* MC_EMEM_ARB_TIMING_RAP2PRE */
+                    0x00000008 /* MC_EMEM_ARB_TIMING_WAP2PRE */
+                    0x00000003 /* MC_EMEM_ARB_TIMING_R2R */
+                    0x00000002 /* MC_EMEM_ARB_TIMING_W2W */
+                    0x00000003 /* MC_EMEM_ARB_TIMING_R2W */
+                    0x00000006 /* MC_EMEM_ARB_TIMING_W2R */
+                    0x06030203 /* MC_EMEM_ARB_DA_TURNS */
+                    0x000a0402 /* MC_EMEM_ARB_DA_COVERS */
+                    0x77e30303 /* MC_EMEM_ARB_MISC0 */
+                    0x70000f03 /* MC_EMEM_ARB_MISC1 */
+                    0x001f0000 /* MC_EMEM_ARB_RING1_THROTTLE */
+                >;
+            };
+        };
+    };
diff --git a/Documentation/devicetree/bindings/memory-controllers/nvidia,tegra30-mc.txt b/Documentation/devicetree/bindings/memory-controllers/nvidia,tegra30-mc.txt
deleted file mode 100644
index a878b5908a4d..000000000000
--- a/Documentation/devicetree/bindings/memory-controllers/nvidia,tegra30-mc.txt
+++ /dev/null
@@ -1,123 +0,0 @@
-NVIDIA Tegra Memory Controller device tree bindings
-===================================================
-
-memory-controller node
-----------------------
-
-Required properties:
-- compatible: Should be "nvidia,tegra<chip>-mc"
-- reg: Physical base address and length of the controller's registers.
-- 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: The interrupt outputs from the controller.
-- #reset-cells : Should be 1. This cell represents memory client module ID.
-  The assignments may be found in header file <dt-bindings/memory/tegra30-mc.h>
-  or in the TRM documentation.
-
-Required properties for Tegra30, Tegra114, Tegra124, Tegra132 and Tegra210:
-- #iommu-cells: Should be 1. The single cell of the IOMMU specifier defines
-  the SWGROUP of the master.
-
-This device implements an IOMMU that complies with the generic IOMMU binding.
-See ../iommu/iommu.txt for details.
-
-emc-timings subnode
--------------------
-
-The node should contain a "emc-timings" subnode for each supported RAM type (see field RAM_CODE in
-register PMC_STRAPPING_OPT_A).
-
-Required properties for "emc-timings" nodes :
-- nvidia,ram-code : Should contain the value of RAM_CODE this timing set is used for.
-
-timing subnode
---------------
-
-Each "emc-timings" node should contain a subnode for every supported EMC clock rate.
-
-Required properties for timing nodes :
-- clock-frequency : Should contain the memory clock rate in Hz.
-- nvidia,emem-configuration : Values to be written to the EMEM register block. For the Tegra124 SoC
-(see section "15.6.1 MC Registers" in the TRM), these are the registers whose values need to be
-specified, according to the board documentation:
-
-	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_MISC1
-	MC_EMEM_ARB_RING1_THROTTLE
-
-Example SoC include file:
-
-/ {
-	mc: memory-controller@70019000 {
-		compatible = "nvidia,tegra124-mc";
-		reg = <0x0 0x70019000 0x0 0x1000>;
-		clocks = <&tegra_car TEGRA124_CLK_MC>;
-		clock-names = "mc";
-
-		interrupts = <GIC_SPI 77 IRQ_TYPE_LEVEL_HIGH>;
-
-		#iommu-cells = <1>;
-		#reset-cells = <1>;
-	};
-
-	sdhci@700b0000 {
-		compatible = "nvidia,tegra124-sdhci";
-		...
-		iommus = <&mc TEGRA_SWGROUP_SDMMC1A>;
-		resets = <&mc TEGRA124_MC_RESET_SDMMC1>;
-	};
-};
-
-Example board file:
-
-/ {
-	memory-controller@70019000 {
-		emc-timings-3 {
-			nvidia,ram-code = <3>;
-
-			timing-12750000 {
-				clock-frequency = <12750000>;
-
-				nvidia,emem-configuration = <
-					0x40040001 /* MC_EMEM_ARB_CFG */
-					0x8000000a /* MC_EMEM_ARB_OUTSTANDING_REQ */
-					0x00000001 /* MC_EMEM_ARB_TIMING_RCD */
-					0x00000001 /* MC_EMEM_ARB_TIMING_RP */
-					0x00000002 /* MC_EMEM_ARB_TIMING_RC */
-					0x00000000 /* MC_EMEM_ARB_TIMING_RAS */
-					0x00000002 /* MC_EMEM_ARB_TIMING_FAW */
-					0x00000001 /* MC_EMEM_ARB_TIMING_RRD */
-					0x00000002 /* MC_EMEM_ARB_TIMING_RAP2PRE */
-					0x00000008 /* MC_EMEM_ARB_TIMING_WAP2PRE */
-					0x00000003 /* MC_EMEM_ARB_TIMING_R2R */
-					0x00000002 /* MC_EMEM_ARB_TIMING_W2W */
-					0x00000003 /* MC_EMEM_ARB_TIMING_R2W */
-					0x00000006 /* MC_EMEM_ARB_TIMING_W2R */
-					0x06030203 /* MC_EMEM_ARB_DA_TURNS */
-					0x000a0402 /* MC_EMEM_ARB_DA_COVERS */
-					0x77e30303 /* MC_EMEM_ARB_MISC0 */
-					0x70000f03 /* MC_EMEM_ARB_MISC1 */
-					0x001f0000 /* MC_EMEM_ARB_RING1_THROTTLE */
-				>;
-			};
-		};
-	};
-};
-- 
2.22.0


^ permalink raw reply	[flat|nested] 27+ messages in thread

* [PATCH v10 10/15] dt-bindings: memory: Add binding for NVIDIA Tegra30 Memory Controller
  2019-08-11 21:00 [PATCH v10 00/15] memory: tegra: Introduce Tegra30 EMC driver Dmitry Osipenko
                   ` (8 preceding siblings ...)
  2019-08-11 21:00 ` [PATCH v10 09/15] dt-bindings: memory: tegra30: Convert to Tegra124 YAML Dmitry Osipenko
@ 2019-08-11 21:00 ` Dmitry Osipenko
  2019-08-12 19:55   ` Rob Herring
  2019-08-11 21:00 ` [PATCH v10 11/15] dt-bindings: memory: Add binding for NVIDIA Tegra30 External " Dmitry Osipenko
                   ` (4 subsequent siblings)
  14 siblings, 1 reply; 27+ messages in thread
From: Dmitry Osipenko @ 2019-08-11 21:00 UTC (permalink / raw)
  To: Rob Herring, Michael Turquette, Joseph Lo, Thierry Reding,
	Jonathan Hunter, Peter De Schrijver, Prashant Gaikwad,
	Stephen Boyd
  Cc: devicetree, linux-clk, linux-tegra, linux-kernel

Add binding for the NVIDIA Tegra30 SoC Memory Controller.

Signed-off-by: Dmitry Osipenko <digetx@gmail.com>
---
 .../memory-controllers/nvidia,tegra30-mc.yaml | 167 ++++++++++++++++++
 1 file changed, 167 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/memory-controllers/nvidia,tegra30-mc.yaml

diff --git a/Documentation/devicetree/bindings/memory-controllers/nvidia,tegra30-mc.yaml b/Documentation/devicetree/bindings/memory-controllers/nvidia,tegra30-mc.yaml
new file mode 100644
index 000000000000..84fd57bcf0dc
--- /dev/null
+++ b/Documentation/devicetree/bindings/memory-controllers/nvidia,tegra30-mc.yaml
@@ -0,0 +1,167 @@
+# SPDX-License-Identifier: (GPL-2.0)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/memory-controllers/nvidia,tegra30-mc.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: NVIDIA Tegra30 SoC Memory Controller
+
+maintainers:
+  - Dmitry Osipenko <digetx@gmail.com>
+  - Jon Hunter <jonathanh@nvidia.com>
+  - Thierry Reding <thierry.reding@gmail.com>
+
+description: |
+  Tegra30 Memory Controller architecturally consists of the following parts:
+
+    Arbitration Domains, which can handle a single request or response per
+    clock from a group of clients. Typically, a system has a single Arbitration
+    Domain, but an implementation may divide the client space into multiple
+    Arbitration Domains to increase the effective system bandwidth.
+
+    Protocol Arbiter, which manage a related pool of memory devices. A system
+    may have a single Protocol Arbiter or multiple Protocol Arbiters.
+
+    Memory Crossbar, which routes request and responses between Arbitration
+    Domains and Protocol Arbiters. In the simplest version of the system, the
+    Memory Crossbar is just a pass through between a single Arbitration Domain
+    and a single Protocol Arbiter.
+
+    Global Resources, which include things like configuration registers which
+    are shared across the Memory Subsystem.
+
+  The Tegra30 Memory Controller handles memory requests from internal clients
+  and arbitrates among them to allocate memory bandwidth for DDR3L and LPDDR2
+  SDRAMs.
+
+properties:
+  compatible:
+    const: nvidia,tegra30-mc
+
+  reg:
+    maxItems: 1
+
+  clocks:
+    maxItems: 1
+
+  clock-names:
+    items:
+      - const: mc
+
+  interrupts:
+    maxItems: 1
+
+  "#reset-cells":
+    const: 1
+
+  "#iommu-cells":
+    const: 1
+
+patternProperties:
+  "^emc-timings-[0-9]+$":
+    type: object
+    properties:
+      nvidia,ram-code:
+        $ref: /schemas/types.yaml#/definitions/uint32
+        description:
+          Value of RAM_CODE this timing set is used for.
+
+    patternProperties:
+      "^timing-[0-9]+$":
+        type: object
+        properties:
+          clock-frequency:
+            description:
+              Memory clock rate in Hz.
+            minimum: 1000000
+            maximum: 900000000
+
+          nvidia,emem-configuration:
+            $ref: /schemas/types.yaml#/definitions/uint32-array
+            description: |
+              Values to be written to the EMEM register block. See section
+              "18.13.1 MC Registers" in the TRM.
+            items:
+              - description: MC_EMEM_ARB_CFG
+              - description: MC_EMEM_ARB_OUTSTANDING_REQ
+              - description: MC_EMEM_ARB_TIMING_RCD
+              - description: MC_EMEM_ARB_TIMING_RP
+              - description: MC_EMEM_ARB_TIMING_RC
+              - description: MC_EMEM_ARB_TIMING_RAS
+              - description: MC_EMEM_ARB_TIMING_FAW
+              - description: MC_EMEM_ARB_TIMING_RRD
+              - description: MC_EMEM_ARB_TIMING_RAP2PRE
+              - description: MC_EMEM_ARB_TIMING_WAP2PRE
+              - description: MC_EMEM_ARB_TIMING_R2R
+              - description: MC_EMEM_ARB_TIMING_W2W
+              - description: MC_EMEM_ARB_TIMING_R2W
+              - description: MC_EMEM_ARB_TIMING_W2R
+              - description: MC_EMEM_ARB_DA_TURNS
+              - description: MC_EMEM_ARB_DA_COVERS
+              - description: MC_EMEM_ARB_MISC0
+              - description: MC_EMEM_ARB_RING1_THROTTLE
+
+        required:
+          - clock-frequency
+          - nvidia,emem-configuration
+
+        additionalProperties: false
+
+    required:
+      - nvidia,ram-code
+
+    additionalProperties: false
+
+required:
+  - compatible
+  - reg
+  - interrupts
+  - clocks
+  - clock-names
+  - "#reset-cells"
+  - "#iommu-cells"
+
+additionalProperties: false
+
+examples:
+  - |
+    memory-controller@7000f000 {
+        compatible = "nvidia,tegra30-mc";
+        reg = <0x7000f000 0x400>;
+        clocks = <&tegra_car 32>;
+        clock-names = "mc";
+
+        interrupts = <0 77 4>;
+
+        #iommu-cells = <1>;
+        #reset-cells = <1>;
+
+        emc-timings-1 {
+            nvidia,ram-code = <1>;
+
+            timing-667000000 {
+                clock-frequency = <667000000>;
+
+                nvidia,emem-configuration = <
+                    0x0000000a /* MC_EMEM_ARB_CFG */
+                    0xc0000079 /* MC_EMEM_ARB_OUTSTANDING_REQ */
+                    0x00000003 /* MC_EMEM_ARB_TIMING_RCD */
+                    0x00000004 /* MC_EMEM_ARB_TIMING_RP */
+                    0x00000010 /* MC_EMEM_ARB_TIMING_RC */
+                    0x0000000b /* MC_EMEM_ARB_TIMING_RAS */
+                    0x0000000a /* MC_EMEM_ARB_TIMING_FAW */
+                    0x00000001 /* MC_EMEM_ARB_TIMING_RRD */
+                    0x00000003 /* MC_EMEM_ARB_TIMING_RAP2PRE */
+                    0x0000000b /* MC_EMEM_ARB_TIMING_WAP2PRE */
+                    0x00000002 /* MC_EMEM_ARB_TIMING_R2R */
+                    0x00000002 /* MC_EMEM_ARB_TIMING_W2W */
+                    0x00000004 /* MC_EMEM_ARB_TIMING_R2W */
+                    0x00000008 /* MC_EMEM_ARB_TIMING_W2R */
+                    0x08040202 /* MC_EMEM_ARB_DA_TURNS */
+                    0x00130b10 /* MC_EMEM_ARB_DA_COVERS */
+                    0x70ea1f11 /* MC_EMEM_ARB_MISC0 */
+                    0x001f0000 /* MC_EMEM_ARB_RING1_THROTTLE */
+                >;
+            };
+        };
+    };
-- 
2.22.0


^ permalink raw reply	[flat|nested] 27+ messages in thread

* [PATCH v10 11/15] dt-bindings: memory: Add binding for NVIDIA Tegra30 External Memory Controller
  2019-08-11 21:00 [PATCH v10 00/15] memory: tegra: Introduce Tegra30 EMC driver Dmitry Osipenko
                   ` (9 preceding siblings ...)
  2019-08-11 21:00 ` [PATCH v10 10/15] dt-bindings: memory: Add binding for NVIDIA Tegra30 Memory Controller Dmitry Osipenko
@ 2019-08-11 21:00 ` " Dmitry Osipenko
  2019-08-12 19:56   ` Rob Herring
  2019-08-11 21:00 ` [PATCH v10 12/15] memory: tegra: Introduce Tegra30 EMC driver Dmitry Osipenko
                   ` (3 subsequent siblings)
  14 siblings, 1 reply; 27+ messages in thread
From: Dmitry Osipenko @ 2019-08-11 21:00 UTC (permalink / raw)
  To: Rob Herring, Michael Turquette, Joseph Lo, Thierry Reding,
	Jonathan Hunter, Peter De Schrijver, Prashant Gaikwad,
	Stephen Boyd
  Cc: devicetree, linux-clk, linux-tegra, linux-kernel

Add device-tree binding for NVIDIA Tegra30 External Memory Controller.
The binding is based on the Tegra124 EMC binding since hardware is
similar, although there are couple significant differences.

Note that the memory timing description is given in a platform-specific
form because there is no detailed information on how to convert a
typical-common DDR timing into the register values. The timing format is
borrowed from downstream kernel, hence there is no hurdle in regards to
upstreaming of memory timings for the boards.

Acked-by: Peter De Schrijver <pdeschrijver@nvidia.com>
Signed-off-by: Dmitry Osipenko <digetx@gmail.com>
---
 .../nvidia,tegra30-emc.yaml                   | 336 ++++++++++++++++++
 1 file changed, 336 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/memory-controllers/nvidia,tegra30-emc.yaml

diff --git a/Documentation/devicetree/bindings/memory-controllers/nvidia,tegra30-emc.yaml b/Documentation/devicetree/bindings/memory-controllers/nvidia,tegra30-emc.yaml
new file mode 100644
index 000000000000..02c70c776414
--- /dev/null
+++ b/Documentation/devicetree/bindings/memory-controllers/nvidia,tegra30-emc.yaml
@@ -0,0 +1,336 @@
+# SPDX-License-Identifier: (GPL-2.0)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/memory-controllers/nvidia,tegra30-emc.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: NVIDIA Tegra30 SoC External Memory Controller
+
+maintainers:
+  - Dmitry Osipenko <digetx@gmail.com>
+  - Jon Hunter <jonathanh@nvidia.com>
+  - Thierry Reding <thierry.reding@gmail.com>
+
+description: |
+  The EMC interfaces with the off-chip SDRAM to service the request stream
+  sent from Memory Controller. The EMC also has various performance-affecting
+  settings beyond the obvious SDRAM configuration parameters and initialization
+  settings. Tegra30 EMC supports multiple JEDEC standard protocols: LPDDR2,
+  LPDDR3, and DDR3.
+
+properties:
+  compatible:
+    const: nvidia,tegra30-emc
+
+  reg:
+    maxItems: 1
+
+  clocks:
+    maxItems: 1
+
+  interrupts:
+    maxItems: 1
+
+  nvidia,memory-controller:
+    $ref: /schemas/types.yaml#/definitions/phandle
+    description:
+      Phandle of the Memory Controller node.
+
+patternProperties:
+  "^emc-timings-[0-9]+$":
+    type: object
+    properties:
+      nvidia,ram-code:
+        $ref: /schemas/types.yaml#/definitions/uint32
+        description:
+          Value of RAM_CODE this timing set is used for.
+
+    patternProperties:
+      "^timing-[0-9]+$":
+        type: object
+        properties:
+          clock-frequency:
+            description:
+              Memory clock rate in Hz.
+            minimum: 1000000
+            maximum: 900000000
+
+          nvidia,emc-auto-cal-interval:
+            $ref: /schemas/types.yaml#/definitions/uint32
+            description:
+              Pad calibration interval in microseconds.
+            minimum: 0
+            maximum: 2097151
+
+          nvidia,emc-mode-1:
+            $ref: /schemas/types.yaml#/definitions/uint32
+            description:
+              Mode Register 1.
+
+          nvidia,emc-mode-2:
+            $ref: /schemas/types.yaml#/definitions/uint32
+            description:
+              Mode Register 2.
+
+          nvidia,emc-mode-reset:
+            $ref: /schemas/types.yaml#/definitions/uint32
+            description:
+              Mode Register 0.
+
+          nvidia,emc-zcal-cnt-long:
+            $ref: /schemas/types.yaml#/definitions/uint32
+            description:
+              Number of EMC clocks to wait before issuing any commands after
+              sending ZCAL_MRW_CMD.
+            minimum: 0
+            maximum: 1023
+
+          nvidia,emc-cfg-dyn-self-ref:
+            type: boolean
+            description:
+              Dynamic self-refresh enabled.
+
+          nvidia,emc-cfg-periodic-qrst:
+            type: boolean
+            description:
+              FBIO "read" FIFO periodic resetting enabled.
+
+          nvidia,emc-configuration:
+            $ref: /schemas/types.yaml#/definitions/uint32-array
+            description:
+              EMC timing characterization data. These are the registers
+              (see section "18.13.2 EMC Registers" in the TRM) whose values
+              need to be specified, according to the board documentation.
+            items:
+              - description: EMC_RC
+              - description: EMC_RFC
+              - description: EMC_RAS
+              - description: EMC_RP
+              - description: EMC_R2W
+              - description: EMC_W2R
+              - description: EMC_R2P
+              - description: EMC_W2P
+              - description: EMC_RD_RCD
+              - description: EMC_WR_RCD
+              - description: EMC_RRD
+              - description: EMC_REXT
+              - description: EMC_WEXT
+              - description: EMC_WDV
+              - description: EMC_QUSE
+              - description: EMC_QRST
+              - description: EMC_QSAFE
+              - description: EMC_RDV
+              - description: EMC_REFRESH
+              - description: EMC_BURST_REFRESH_NUM
+              - description: EMC_PRE_REFRESH_REQ_CNT
+              - description: EMC_PDEX2WR
+              - description: EMC_PDEX2RD
+              - description: EMC_PCHG2PDEN
+              - description: EMC_ACT2PDEN
+              - description: EMC_AR2PDEN
+              - description: EMC_RW2PDEN
+              - description: EMC_TXSR
+              - description: EMC_TXSRDLL
+              - description: EMC_TCKE
+              - description: EMC_TFAW
+              - description: EMC_TRPAB
+              - description: EMC_TCLKSTABLE
+              - description: EMC_TCLKSTOP
+              - description: EMC_TREFBW
+              - description: EMC_QUSE_EXTRA
+              - description: EMC_FBIO_CFG6
+              - description: EMC_ODT_WRITE
+              - description: EMC_ODT_READ
+              - description: EMC_FBIO_CFG5
+              - description: EMC_CFG_DIG_DLL
+              - description: EMC_CFG_DIG_DLL_PERIOD
+              - description: EMC_DLL_XFORM_DQS0
+              - description: EMC_DLL_XFORM_DQS1
+              - description: EMC_DLL_XFORM_DQS2
+              - description: EMC_DLL_XFORM_DQS3
+              - description: EMC_DLL_XFORM_DQS4
+              - description: EMC_DLL_XFORM_DQS5
+              - description: EMC_DLL_XFORM_DQS6
+              - description: EMC_DLL_XFORM_DQS7
+              - description: EMC_DLL_XFORM_QUSE0
+              - description: EMC_DLL_XFORM_QUSE1
+              - description: EMC_DLL_XFORM_QUSE2
+              - description: EMC_DLL_XFORM_QUSE3
+              - description: EMC_DLL_XFORM_QUSE4
+              - description: EMC_DLL_XFORM_QUSE5
+              - description: EMC_DLL_XFORM_QUSE6
+              - description: EMC_DLL_XFORM_QUSE7
+              - description: EMC_DLI_TRIM_TXDQS0
+              - description: EMC_DLI_TRIM_TXDQS1
+              - description: EMC_DLI_TRIM_TXDQS2
+              - description: EMC_DLI_TRIM_TXDQS3
+              - description: EMC_DLI_TRIM_TXDQS4
+              - description: EMC_DLI_TRIM_TXDQS5
+              - description: EMC_DLI_TRIM_TXDQS6
+              - description: EMC_DLI_TRIM_TXDQS7
+              - description: EMC_DLL_XFORM_DQ0
+              - description: EMC_DLL_XFORM_DQ1
+              - description: EMC_DLL_XFORM_DQ2
+              - description: EMC_DLL_XFORM_DQ3
+              - description: EMC_XM2CMDPADCTRL
+              - description: EMC_XM2DQSPADCTRL2
+              - description: EMC_XM2DQPADCTRL2
+              - description: EMC_XM2CLKPADCTRL
+              - description: EMC_XM2COMPPADCTRL
+              - description: EMC_XM2VTTGENPADCTRL
+              - description: EMC_XM2VTTGENPADCTRL2
+              - description: EMC_XM2QUSEPADCTRL
+              - description: EMC_XM2DQSPADCTRL3
+              - description: EMC_CTT_TERM_CTRL
+              - description: EMC_ZCAL_INTERVAL
+              - description: EMC_ZCAL_WAIT_CNT
+              - description: EMC_MRS_WAIT_CNT
+              - description: EMC_AUTO_CAL_CONFIG
+              - description: EMC_CTT
+              - description: EMC_CTT_DURATION
+              - description: EMC_DYN_SELF_REF_CONTROL
+              - description: EMC_FBIO_SPARE
+              - description: EMC_CFG_RSV
+
+        required:
+          - clock-frequency
+          - nvidia,emc-auto-cal-interval
+          - nvidia,emc-mode-1
+          - nvidia,emc-mode-2
+          - nvidia,emc-mode-reset
+          - nvidia,emc-zcal-cnt-long
+          - nvidia,emc-configuration
+
+        additionalProperties: false
+
+    required:
+      - nvidia,ram-code
+
+    additionalProperties: false
+
+required:
+  - compatible
+  - reg
+  - interrupts
+  - clocks
+  - nvidia,memory-controller
+
+additionalProperties: false
+
+examples:
+  - |
+    external-memory-controller@7000f400 {
+        compatible = "nvidia,tegra30-emc";
+        reg = <0x7000f400 0x400>;
+        interrupts = <0 78 4>;
+        clocks = <&tegra_car 57>;
+
+        nvidia,memory-controller = <&mc>;
+
+        emc-timings-1 {
+            nvidia,ram-code = <1>;
+
+            timing-667000000 {
+                clock-frequency = <667000000>;
+
+                nvidia,emc-auto-cal-interval = <0x001fffff>;
+                nvidia,emc-mode-1 = <0x80100002>;
+                nvidia,emc-mode-2 = <0x80200018>;
+                nvidia,emc-mode-reset = <0x80000b71>;
+                nvidia,emc-zcal-cnt-long = <0x00000040>;
+                nvidia,emc-cfg-periodic-qrst;
+
+                nvidia,emc-configuration = <
+                    0x00000020 /* EMC_RC */
+                    0x0000006a /* EMC_RFC */
+                    0x00000017 /* EMC_RAS */
+                    0x00000007 /* EMC_RP */
+                    0x00000005 /* EMC_R2W */
+                    0x0000000c /* EMC_W2R */
+                    0x00000003 /* EMC_R2P */
+                    0x00000011 /* EMC_W2P */
+                    0x00000007 /* EMC_RD_RCD */
+                    0x00000007 /* EMC_WR_RCD */
+                    0x00000002 /* EMC_RRD */
+                    0x00000001 /* EMC_REXT */
+                    0x00000000 /* EMC_WEXT */
+                    0x00000007 /* EMC_WDV */
+                    0x0000000a /* EMC_QUSE */
+                    0x00000009 /* EMC_QRST */
+                    0x0000000b /* EMC_QSAFE */
+                    0x00000011 /* EMC_RDV */
+                    0x00001412 /* EMC_REFRESH */
+                    0x00000000 /* EMC_BURST_REFRESH_NUM */
+                    0x00000504 /* EMC_PRE_REFRESH_REQ_CNT */
+                    0x00000002 /* EMC_PDEX2WR */
+                    0x0000000e /* EMC_PDEX2RD */
+                    0x00000001 /* EMC_PCHG2PDEN */
+                    0x00000000 /* EMC_ACT2PDEN */
+                    0x0000000c /* EMC_AR2PDEN */
+                    0x00000016 /* EMC_RW2PDEN */
+                    0x00000072 /* EMC_TXSR */
+                    0x00000200 /* EMC_TXSRDLL */
+                    0x00000005 /* EMC_TCKE */
+                    0x00000015 /* EMC_TFAW */
+                    0x00000000 /* EMC_TRPAB */
+                    0x00000006 /* EMC_TCLKSTABLE */
+                    0x00000007 /* EMC_TCLKSTOP */
+                    0x00001453 /* EMC_TREFBW */
+                    0x0000000b /* EMC_QUSE_EXTRA */
+                    0x00000006 /* EMC_FBIO_CFG6 */
+                    0x00000000 /* EMC_ODT_WRITE */
+                    0x00000000 /* EMC_ODT_READ */
+                    0x00005088 /* EMC_FBIO_CFG5 */
+                    0xf00b0191 /* EMC_CFG_DIG_DLL */
+                    0x00008000 /* EMC_CFG_DIG_DLL_PERIOD */
+                    0x00000008 /* EMC_DLL_XFORM_DQS0 */
+                    0x00000008 /* EMC_DLL_XFORM_DQS1 */
+                    0x00000008 /* EMC_DLL_XFORM_DQS2 */
+                    0x00000008 /* EMC_DLL_XFORM_DQS3 */
+                    0x0000000a /* EMC_DLL_XFORM_DQS4 */
+                    0x0000000a /* EMC_DLL_XFORM_DQS5 */
+                    0x0000000a /* EMC_DLL_XFORM_DQS6 */
+                    0x0000000a /* EMC_DLL_XFORM_DQS7 */
+                    0x00018000 /* EMC_DLL_XFORM_QUSE0 */
+                    0x00018000 /* EMC_DLL_XFORM_QUSE1 */
+                    0x00018000 /* EMC_DLL_XFORM_QUSE2 */
+                    0x00018000 /* EMC_DLL_XFORM_QUSE3 */
+                    0x00000000 /* EMC_DLL_XFORM_QUSE4 */
+                    0x00000000 /* EMC_DLL_XFORM_QUSE5 */
+                    0x00000000 /* EMC_DLL_XFORM_QUSE6 */
+                    0x00000000 /* EMC_DLL_XFORM_QUSE7 */
+                    0x00000000 /* EMC_DLI_TRIM_TXDQS0 */
+                    0x00000000 /* EMC_DLI_TRIM_TXDQS1 */
+                    0x00000000 /* EMC_DLI_TRIM_TXDQS2 */
+                    0x00000000 /* EMC_DLI_TRIM_TXDQS3 */
+                    0x00000000 /* EMC_DLI_TRIM_TXDQS4 */
+                    0x00000000 /* EMC_DLI_TRIM_TXDQS5 */
+                    0x00000000 /* EMC_DLI_TRIM_TXDQS6 */
+                    0x00000000 /* EMC_DLI_TRIM_TXDQS7 */
+                    0x0000000a /* EMC_DLL_XFORM_DQ0 */
+                    0x0000000a /* EMC_DLL_XFORM_DQ1 */
+                    0x0000000a /* EMC_DLL_XFORM_DQ2 */
+                    0x0000000a /* EMC_DLL_XFORM_DQ3 */
+                    0x000002a0 /* EMC_XM2CMDPADCTRL */
+                    0x0800013d /* EMC_XM2DQSPADCTRL2 */
+                    0x22220000 /* EMC_XM2DQPADCTRL2 */
+                    0x77fff884 /* EMC_XM2CLKPADCTRL */
+                    0x01f1f501 /* EMC_XM2COMPPADCTRL */
+                    0x07077404 /* EMC_XM2VTTGENPADCTRL */
+                    0x54000000 /* EMC_XM2VTTGENPADCTRL2 */
+                    0x080001e8 /* EMC_XM2QUSEPADCTRL */
+                    0x0c000021 /* EMC_XM2DQSPADCTRL3 */
+                    0x00000802 /* EMC_CTT_TERM_CTRL */
+                    0x00020000 /* EMC_ZCAL_INTERVAL */
+                    0x00000100 /* EMC_ZCAL_WAIT_CNT */
+                    0x0155000c /* EMC_MRS_WAIT_CNT */
+                    0xa0f10000 /* EMC_AUTO_CAL_CONFIG */
+                    0x00000000 /* EMC_CTT */
+                    0x00000000 /* EMC_CTT_DURATION */
+                    0x800028a5 /* EMC_DYN_SELF_REF_CONTROL */
+                    0xe8000000 /* EMC_FBIO_SPARE */
+                    0xff00ff49 /* EMC_CFG_RSV */
+                >;
+            };
+        };
+    };
-- 
2.22.0


^ permalink raw reply	[flat|nested] 27+ messages in thread

* [PATCH v10 12/15] memory: tegra: Introduce Tegra30 EMC driver
  2019-08-11 21:00 [PATCH v10 00/15] memory: tegra: Introduce Tegra30 EMC driver Dmitry Osipenko
                   ` (10 preceding siblings ...)
  2019-08-11 21:00 ` [PATCH v10 11/15] dt-bindings: memory: Add binding for NVIDIA Tegra30 External " Dmitry Osipenko
@ 2019-08-11 21:00 ` Dmitry Osipenko
  2019-10-05 16:28   ` Peter Geis
  2019-08-11 21:00 ` [PATCH v10 13/15] memory: tegra: Ensure timing control debug features are disabled Dmitry Osipenko
                   ` (2 subsequent siblings)
  14 siblings, 1 reply; 27+ messages in thread
From: Dmitry Osipenko @ 2019-08-11 21:00 UTC (permalink / raw)
  To: Rob Herring, Michael Turquette, Joseph Lo, Thierry Reding,
	Jonathan Hunter, Peter De Schrijver, Prashant Gaikwad,
	Stephen Boyd
  Cc: devicetree, linux-clk, linux-tegra, linux-kernel

Introduce driver for the External Memory Controller (EMC) found on Tegra30
chips, it 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.

Acked-by: Peter De Schrijver <pdeschrijver@nvidia.com>
Signed-off-by: Dmitry Osipenko <digetx@gmail.com>
---
 drivers/memory/tegra/Kconfig       |   10 +
 drivers/memory/tegra/Makefile      |    1 +
 drivers/memory/tegra/mc.c          |    9 +-
 drivers/memory/tegra/mc.h          |   30 +-
 drivers/memory/tegra/tegra30-emc.c | 1232 ++++++++++++++++++++++++++++
 drivers/memory/tegra/tegra30.c     |   42 +
 include/soc/tegra/mc.h             |    2 +-
 7 files changed, 1311 insertions(+), 15 deletions(-)
 create mode 100644 drivers/memory/tegra/tegra30-emc.c

diff --git a/drivers/memory/tegra/Kconfig b/drivers/memory/tegra/Kconfig
index 4680124ddcab..fbfbaada61a2 100644
--- a/drivers/memory/tegra/Kconfig
+++ b/drivers/memory/tegra/Kconfig
@@ -17,6 +17,16 @@ config TEGRA20_EMC
 	  This driver is required to change memory timings / clock rate for
 	  external memory.
 
+config TEGRA30_EMC
+	bool "NVIDIA Tegra30 External Memory Controller driver"
+	default y
+	depends on TEGRA_MC && ARCH_TEGRA_3x_SOC
+	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 TEGRA124_EMC
 	bool "NVIDIA Tegra124 External Memory Controller driver"
 	default y
diff --git a/drivers/memory/tegra/Makefile b/drivers/memory/tegra/Makefile
index 3971a6b7c487..3d23c4261104 100644
--- a/drivers/memory/tegra/Makefile
+++ b/drivers/memory/tegra/Makefile
@@ -11,5 +11,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_TEGRA30_EMC)  += tegra30-emc.o
 obj-$(CONFIG_TEGRA124_EMC) += tegra124-emc.o
 obj-$(CONFIG_ARCH_TEGRA_186_SOC) += tegra186.o
diff --git a/drivers/memory/tegra/mc.c b/drivers/memory/tegra/mc.c
index 3d8d322511c5..43819e8df95c 100644
--- a/drivers/memory/tegra/mc.c
+++ b/drivers/memory/tegra/mc.c
@@ -48,9 +48,6 @@
 #define MC_EMEM_ADR_CFG 0x54
 #define MC_EMEM_ADR_CFG_EMEM_NUMDEV BIT(0)
 
-#define MC_TIMING_CONTROL		0xfc
-#define MC_TIMING_UPDATE		BIT(0)
-
 static const struct of_device_id tegra_mc_of_match[] = {
 #ifdef CONFIG_ARCH_TEGRA_2x_SOC
 	{ .compatible = "nvidia,tegra20-mc-gart", .data = &tegra20_mc_soc },
@@ -307,7 +304,7 @@ static int tegra_mc_setup_latency_allowance(struct tegra_mc *mc)
 	return 0;
 }
 
-void tegra_mc_write_emem_configuration(struct tegra_mc *mc, unsigned long rate)
+int tegra_mc_write_emem_configuration(struct tegra_mc *mc, unsigned long rate)
 {
 	unsigned int i;
 	struct tegra_mc_timing *timing = NULL;
@@ -322,11 +319,13 @@ void tegra_mc_write_emem_configuration(struct tegra_mc *mc, unsigned long rate)
 	if (!timing) {
 		dev_err(mc->dev, "no memory timing registered for rate %lu\n",
 			rate);
-		return;
+		return -EINVAL;
 	}
 
 	for (i = 0; i < mc->soc->num_emem_regs; ++i)
 		mc_writel(mc, timing->emem_data[i], mc->soc->emem_regs[i]);
+
+	return 0;
 }
 
 unsigned int tegra_mc_get_emem_device_count(struct tegra_mc *mc)
diff --git a/drivers/memory/tegra/mc.h b/drivers/memory/tegra/mc.h
index f9353494b708..410efc4d7e7b 100644
--- a/drivers/memory/tegra/mc.h
+++ b/drivers/memory/tegra/mc.h
@@ -6,20 +6,32 @@
 #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_INT_DECERR_MTS (1 << 16)
-#define MC_INT_SECERR_SEC (1 << 13)
-#define MC_INT_DECERR_VPR (1 << 12)
-#define MC_INT_INVALID_APB_ASID_UPDATE (1 << 11)
-#define MC_INT_INVALID_SMMU_PAGE (1 << 10)
-#define MC_INT_ARBITRATION_EMEM (1 << 9)
-#define MC_INT_SECURITY_VIOLATION (1 << 8)
-#define MC_INT_INVALID_GART_PAGE (1 << 7)
-#define MC_INT_DECERR_EMEM (1 << 6)
+#define MC_INT_DECERR_MTS				BIT(16)
+#define MC_INT_SECERR_SEC				BIT(13)
+#define MC_INT_DECERR_VPR				BIT(12)
+#define MC_INT_INVALID_APB_ASID_UPDATE			BIT(11)
+#define MC_INT_INVALID_SMMU_PAGE			BIT(10)
+#define MC_INT_ARBITRATION_EMEM				BIT(9)
+#define MC_INT_SECURITY_VIOLATION			BIT(8)
+#define MC_INT_INVALID_GART_PAGE			BIT(7)
+#define MC_INT_DECERR_EMEM				BIT(6)
+
+#define MC_EMEM_ARB_OUTSTANDING_REQ			0x94
+#define MC_EMEM_ARB_OUTSTANDING_REQ_MAX_MASK		0x1ff
+#define MC_EMEM_ARB_OUTSTANDING_REQ_HOLDOFF_OVERRIDE	BIT(30)
+#define MC_EMEM_ARB_OUTSTANDING_REQ_LIMIT_ENABLE	BIT(31)
+
+#define MC_EMEM_ARB_OVERRIDE				0xe8
+#define MC_EMEM_ARB_OVERRIDE_EACK_MASK			0x3
+
+#define MC_TIMING_CONTROL				0xfc
+#define MC_TIMING_UPDATE				BIT(0)
 
 static inline u32 mc_readl(struct tegra_mc *mc, unsigned long offset)
 {
diff --git a/drivers/memory/tegra/tegra30-emc.c b/drivers/memory/tegra/tegra30-emc.c
new file mode 100644
index 000000000000..6929980bf907
--- /dev/null
+++ b/drivers/memory/tegra/tegra30-emc.c
@@ -0,0 +1,1232 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Tegra30 External Memory Controller driver
+ *
+ * Based on downstream driver from NVIDIA and tegra124-emc.c
+ * Copyright (C) 2011-2014 NVIDIA Corporation
+ *
+ * Author: Dmitry Osipenko <digetx@gmail.com>
+ * Copyright (C) 2019 GRATE-DRIVER project
+ */
+
+#include <linux/clk.h>
+#include <linux/clk/tegra.h>
+#include <linux/completion.h>
+#include <linux/delay.h>
+#include <linux/err.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/iopoll.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/of_platform.h>
+#include <linux/platform_device.h>
+#include <linux/sort.h>
+#include <linux/types.h>
+
+#include <soc/tegra/fuse.h>
+
+#include "mc.h"
+
+#define EMC_INTSTATUS				0x000
+#define EMC_INTMASK				0x004
+#define EMC_DBG					0x008
+#define EMC_CFG					0x00c
+#define EMC_REFCTRL				0x020
+#define EMC_TIMING_CONTROL			0x028
+#define EMC_RC					0x02c
+#define EMC_RFC					0x030
+#define EMC_RAS					0x034
+#define EMC_RP					0x038
+#define EMC_R2W					0x03c
+#define EMC_W2R					0x040
+#define EMC_R2P					0x044
+#define EMC_W2P					0x048
+#define EMC_RD_RCD				0x04c
+#define EMC_WR_RCD				0x050
+#define EMC_RRD					0x054
+#define EMC_REXT				0x058
+#define EMC_WDV					0x05c
+#define EMC_QUSE				0x060
+#define EMC_QRST				0x064
+#define EMC_QSAFE				0x068
+#define EMC_RDV					0x06c
+#define EMC_REFRESH				0x070
+#define EMC_BURST_REFRESH_NUM			0x074
+#define EMC_PDEX2WR				0x078
+#define EMC_PDEX2RD				0x07c
+#define EMC_PCHG2PDEN				0x080
+#define EMC_ACT2PDEN				0x084
+#define EMC_AR2PDEN				0x088
+#define EMC_RW2PDEN				0x08c
+#define EMC_TXSR				0x090
+#define EMC_TCKE				0x094
+#define EMC_TFAW				0x098
+#define EMC_TRPAB				0x09c
+#define EMC_TCLKSTABLE				0x0a0
+#define EMC_TCLKSTOP				0x0a4
+#define EMC_TREFBW				0x0a8
+#define EMC_QUSE_EXTRA				0x0ac
+#define EMC_ODT_WRITE				0x0b0
+#define EMC_ODT_READ				0x0b4
+#define EMC_WEXT				0x0b8
+#define EMC_CTT					0x0bc
+#define EMC_MRS_WAIT_CNT			0x0c8
+#define EMC_MRS					0x0cc
+#define EMC_EMRS				0x0d0
+#define EMC_SELF_REF				0x0e0
+#define EMC_MRW					0x0e8
+#define EMC_XM2DQSPADCTRL3			0x0f8
+#define EMC_FBIO_SPARE				0x100
+#define EMC_FBIO_CFG5				0x104
+#define EMC_FBIO_CFG6				0x114
+#define EMC_CFG_RSV				0x120
+#define EMC_AUTO_CAL_CONFIG			0x2a4
+#define EMC_AUTO_CAL_INTERVAL			0x2a8
+#define EMC_AUTO_CAL_STATUS			0x2ac
+#define EMC_STATUS				0x2b4
+#define EMC_CFG_2				0x2b8
+#define EMC_CFG_DIG_DLL				0x2bc
+#define EMC_CFG_DIG_DLL_PERIOD			0x2c0
+#define EMC_CTT_DURATION			0x2d8
+#define EMC_CTT_TERM_CTRL			0x2dc
+#define EMC_ZCAL_INTERVAL			0x2e0
+#define EMC_ZCAL_WAIT_CNT			0x2e4
+#define EMC_ZQ_CAL				0x2ec
+#define EMC_XM2CMDPADCTRL			0x2f0
+#define EMC_XM2DQSPADCTRL2			0x2fc
+#define EMC_XM2DQPADCTRL2			0x304
+#define EMC_XM2CLKPADCTRL			0x308
+#define EMC_XM2COMPPADCTRL			0x30c
+#define EMC_XM2VTTGENPADCTRL			0x310
+#define EMC_XM2VTTGENPADCTRL2			0x314
+#define EMC_XM2QUSEPADCTRL			0x318
+#define EMC_DLL_XFORM_DQS0			0x328
+#define EMC_DLL_XFORM_DQS1			0x32c
+#define EMC_DLL_XFORM_DQS2			0x330
+#define EMC_DLL_XFORM_DQS3			0x334
+#define EMC_DLL_XFORM_DQS4			0x338
+#define EMC_DLL_XFORM_DQS5			0x33c
+#define EMC_DLL_XFORM_DQS6			0x340
+#define EMC_DLL_XFORM_DQS7			0x344
+#define EMC_DLL_XFORM_QUSE0			0x348
+#define EMC_DLL_XFORM_QUSE1			0x34c
+#define EMC_DLL_XFORM_QUSE2			0x350
+#define EMC_DLL_XFORM_QUSE3			0x354
+#define EMC_DLL_XFORM_QUSE4			0x358
+#define EMC_DLL_XFORM_QUSE5			0x35c
+#define EMC_DLL_XFORM_QUSE6			0x360
+#define EMC_DLL_XFORM_QUSE7			0x364
+#define EMC_DLL_XFORM_DQ0			0x368
+#define EMC_DLL_XFORM_DQ1			0x36c
+#define EMC_DLL_XFORM_DQ2			0x370
+#define EMC_DLL_XFORM_DQ3			0x374
+#define EMC_DLI_TRIM_TXDQS0			0x3a8
+#define EMC_DLI_TRIM_TXDQS1			0x3ac
+#define EMC_DLI_TRIM_TXDQS2			0x3b0
+#define EMC_DLI_TRIM_TXDQS3			0x3b4
+#define EMC_DLI_TRIM_TXDQS4			0x3b8
+#define EMC_DLI_TRIM_TXDQS5			0x3bc
+#define EMC_DLI_TRIM_TXDQS6			0x3c0
+#define EMC_DLI_TRIM_TXDQS7			0x3c4
+#define EMC_STALL_THEN_EXE_BEFORE_CLKCHANGE	0x3c8
+#define EMC_STALL_THEN_EXE_AFTER_CLKCHANGE	0x3cc
+#define EMC_UNSTALL_RW_AFTER_CLKCHANGE		0x3d0
+#define EMC_SEL_DPD_CTRL			0x3d8
+#define EMC_PRE_REFRESH_REQ_CNT			0x3dc
+#define EMC_DYN_SELF_REF_CONTROL		0x3e0
+#define EMC_TXSRDLL				0x3e4
+
+#define EMC_STATUS_TIMING_UPDATE_STALLED	BIT(23)
+
+#define EMC_MODE_SET_DLL_RESET			BIT(8)
+#define EMC_MODE_SET_LONG_CNT			BIT(26)
+
+#define EMC_SELF_REF_CMD_ENABLED		BIT(0)
+
+#define DRAM_DEV_SEL_ALL			(0 << 30)
+#define DRAM_DEV_SEL_0				(2 << 30)
+#define DRAM_DEV_SEL_1				(1 << 30)
+#define DRAM_BROADCAST(num) \
+	((num) > 1 ? DRAM_DEV_SEL_ALL : DRAM_DEV_SEL_0)
+
+#define EMC_ZQ_CAL_CMD				BIT(0)
+#define EMC_ZQ_CAL_LONG				BIT(4)
+#define EMC_ZQ_CAL_LONG_CMD_DEV0 \
+	(DRAM_DEV_SEL_0 | EMC_ZQ_CAL_LONG | EMC_ZQ_CAL_CMD)
+#define EMC_ZQ_CAL_LONG_CMD_DEV1 \
+	(DRAM_DEV_SEL_1 | EMC_ZQ_CAL_LONG | EMC_ZQ_CAL_CMD)
+
+#define EMC_DBG_READ_MUX_ASSEMBLY		BIT(0)
+#define EMC_DBG_WRITE_MUX_ACTIVE		BIT(1)
+#define EMC_DBG_FORCE_UPDATE			BIT(2)
+#define EMC_DBG_CFG_PRIORITY			BIT(24)
+
+#define EMC_CFG5_QUSE_MODE_SHIFT		13
+#define EMC_CFG5_QUSE_MODE_MASK			(7 << EMC_CFG5_QUSE_MODE_SHIFT)
+
+#define EMC_CFG5_QUSE_MODE_INTERNAL_LPBK	2
+#define EMC_CFG5_QUSE_MODE_PULSE_INTERN		3
+
+#define EMC_SEL_DPD_CTRL_QUSE_DPD_ENABLE	BIT(9)
+
+#define EMC_XM2COMPPADCTRL_VREF_CAL_ENABLE	BIT(10)
+
+#define EMC_XM2QUSEPADCTRL_IVREF_ENABLE		BIT(4)
+
+#define EMC_XM2DQSPADCTRL2_VREF_ENABLE		BIT(5)
+#define EMC_XM2DQSPADCTRL3_VREF_ENABLE		BIT(5)
+
+#define EMC_AUTO_CAL_STATUS_ACTIVE		BIT(31)
+
+#define	EMC_FBIO_CFG5_DRAM_TYPE_MASK		0x3
+
+#define EMC_MRS_WAIT_CNT_SHORT_WAIT_MASK	0x3ff
+#define EMC_MRS_WAIT_CNT_LONG_WAIT_SHIFT	16
+#define EMC_MRS_WAIT_CNT_LONG_WAIT_MASK \
+	(0x3ff << EMC_MRS_WAIT_CNT_LONG_WAIT_SHIFT)
+
+#define EMC_REFCTRL_DEV_SEL_MASK		0x3
+#define EMC_REFCTRL_ENABLE			BIT(31)
+#define EMC_REFCTRL_ENABLE_ALL(num) \
+	(((num) > 1 ? 0 : 2) | EMC_REFCTRL_ENABLE)
+#define EMC_REFCTRL_DISABLE_ALL(num)		((num) > 1 ? 0 : 2)
+
+#define EMC_CFG_PERIODIC_QRST			BIT(21)
+#define EMC_CFG_DYN_SREF_ENABLE			BIT(28)
+
+#define EMC_CLKCHANGE_REQ_ENABLE		BIT(0)
+#define EMC_CLKCHANGE_PD_ENABLE			BIT(1)
+#define EMC_CLKCHANGE_SR_ENABLE			BIT(2)
+
+#define EMC_TIMING_UPDATE			BIT(0)
+
+#define EMC_REFRESH_OVERFLOW_INT		BIT(3)
+#define EMC_CLKCHANGE_COMPLETE_INT		BIT(4)
+
+enum emc_dram_type {
+	DRAM_TYPE_DDR3,
+	DRAM_TYPE_DDR1,
+	DRAM_TYPE_LPDDR2,
+	DRAM_TYPE_DDR2,
+};
+
+enum emc_dll_change {
+	DLL_CHANGE_NONE,
+	DLL_CHANGE_ON,
+	DLL_CHANGE_OFF
+};
+
+static const u16 emc_timing_registers[] = {
+	[0] = EMC_RC,
+	[1] = EMC_RFC,
+	[2] = EMC_RAS,
+	[3] = EMC_RP,
+	[4] = EMC_R2W,
+	[5] = EMC_W2R,
+	[6] = EMC_R2P,
+	[7] = EMC_W2P,
+	[8] = EMC_RD_RCD,
+	[9] = EMC_WR_RCD,
+	[10] = EMC_RRD,
+	[11] = EMC_REXT,
+	[12] = EMC_WEXT,
+	[13] = EMC_WDV,
+	[14] = EMC_QUSE,
+	[15] = EMC_QRST,
+	[16] = EMC_QSAFE,
+	[17] = EMC_RDV,
+	[18] = EMC_REFRESH,
+	[19] = EMC_BURST_REFRESH_NUM,
+	[20] = EMC_PRE_REFRESH_REQ_CNT,
+	[21] = EMC_PDEX2WR,
+	[22] = EMC_PDEX2RD,
+	[23] = EMC_PCHG2PDEN,
+	[24] = EMC_ACT2PDEN,
+	[25] = EMC_AR2PDEN,
+	[26] = EMC_RW2PDEN,
+	[27] = EMC_TXSR,
+	[28] = EMC_TXSRDLL,
+	[29] = EMC_TCKE,
+	[30] = EMC_TFAW,
+	[31] = EMC_TRPAB,
+	[32] = EMC_TCLKSTABLE,
+	[33] = EMC_TCLKSTOP,
+	[34] = EMC_TREFBW,
+	[35] = EMC_QUSE_EXTRA,
+	[36] = EMC_FBIO_CFG6,
+	[37] = EMC_ODT_WRITE,
+	[38] = EMC_ODT_READ,
+	[39] = EMC_FBIO_CFG5,
+	[40] = EMC_CFG_DIG_DLL,
+	[41] = EMC_CFG_DIG_DLL_PERIOD,
+	[42] = EMC_DLL_XFORM_DQS0,
+	[43] = EMC_DLL_XFORM_DQS1,
+	[44] = EMC_DLL_XFORM_DQS2,
+	[45] = EMC_DLL_XFORM_DQS3,
+	[46] = EMC_DLL_XFORM_DQS4,
+	[47] = EMC_DLL_XFORM_DQS5,
+	[48] = EMC_DLL_XFORM_DQS6,
+	[49] = EMC_DLL_XFORM_DQS7,
+	[50] = EMC_DLL_XFORM_QUSE0,
+	[51] = EMC_DLL_XFORM_QUSE1,
+	[52] = EMC_DLL_XFORM_QUSE2,
+	[53] = EMC_DLL_XFORM_QUSE3,
+	[54] = EMC_DLL_XFORM_QUSE4,
+	[55] = EMC_DLL_XFORM_QUSE5,
+	[56] = EMC_DLL_XFORM_QUSE6,
+	[57] = EMC_DLL_XFORM_QUSE7,
+	[58] = EMC_DLI_TRIM_TXDQS0,
+	[59] = EMC_DLI_TRIM_TXDQS1,
+	[60] = EMC_DLI_TRIM_TXDQS2,
+	[61] = EMC_DLI_TRIM_TXDQS3,
+	[62] = EMC_DLI_TRIM_TXDQS4,
+	[63] = EMC_DLI_TRIM_TXDQS5,
+	[64] = EMC_DLI_TRIM_TXDQS6,
+	[65] = EMC_DLI_TRIM_TXDQS7,
+	[66] = EMC_DLL_XFORM_DQ0,
+	[67] = EMC_DLL_XFORM_DQ1,
+	[68] = EMC_DLL_XFORM_DQ2,
+	[69] = EMC_DLL_XFORM_DQ3,
+	[70] = EMC_XM2CMDPADCTRL,
+	[71] = EMC_XM2DQSPADCTRL2,
+	[72] = EMC_XM2DQPADCTRL2,
+	[73] = EMC_XM2CLKPADCTRL,
+	[74] = EMC_XM2COMPPADCTRL,
+	[75] = EMC_XM2VTTGENPADCTRL,
+	[76] = EMC_XM2VTTGENPADCTRL2,
+	[77] = EMC_XM2QUSEPADCTRL,
+	[78] = EMC_XM2DQSPADCTRL3,
+	[79] = EMC_CTT_TERM_CTRL,
+	[80] = EMC_ZCAL_INTERVAL,
+	[81] = EMC_ZCAL_WAIT_CNT,
+	[82] = EMC_MRS_WAIT_CNT,
+	[83] = EMC_AUTO_CAL_CONFIG,
+	[84] = EMC_CTT,
+	[85] = EMC_CTT_DURATION,
+	[86] = EMC_DYN_SELF_REF_CONTROL,
+	[87] = EMC_FBIO_SPARE,
+	[88] = EMC_CFG_RSV,
+};
+
+struct emc_timing {
+	unsigned long rate;
+
+	u32 data[ARRAY_SIZE(emc_timing_registers)];
+
+	u32 emc_auto_cal_interval;
+	u32 emc_mode_1;
+	u32 emc_mode_2;
+	u32 emc_mode_reset;
+	u32 emc_zcal_cnt_long;
+	bool emc_cfg_periodic_qrst;
+	bool emc_cfg_dyn_self_ref;
+};
+
+struct tegra_emc {
+	struct device *dev;
+	struct tegra_mc *mc;
+	struct completion clk_handshake_complete;
+	struct notifier_block clk_nb;
+	struct clk *clk;
+	void __iomem *regs;
+	unsigned int irq;
+
+	struct emc_timing *timings;
+	unsigned int num_timings;
+
+	u32 mc_override;
+	u32 emc_cfg;
+
+	u32 emc_mode_1;
+	u32 emc_mode_2;
+	u32 emc_mode_reset;
+
+	bool vref_cal_toggle : 1;
+	bool zcal_long : 1;
+	bool dll_on : 1;
+	bool prepared : 1;
+	bool bad_state : 1;
+};
+
+static irqreturn_t tegra_emc_isr(int irq, void *data)
+{
+	struct tegra_emc *emc = data;
+	u32 intmask = EMC_REFRESH_OVERFLOW_INT | EMC_CLKCHANGE_COMPLETE_INT;
+	u32 status;
+
+	status = readl_relaxed(emc->regs + EMC_INTSTATUS) & intmask;
+	if (!status)
+		return IRQ_NONE;
+
+	/* notify about EMC-CAR handshake completion */
+	if (status & EMC_CLKCHANGE_COMPLETE_INT)
+		complete(&emc->clk_handshake_complete);
+
+	/* notify about HW problem */
+	if (status & EMC_REFRESH_OVERFLOW_INT)
+		dev_err_ratelimited(emc->dev,
+				    "refresh request overflow timeout\n");
+
+	/* clear interrupts */
+	writel_relaxed(status, emc->regs + EMC_INTSTATUS);
+
+	return IRQ_HANDLED;
+}
+
+static struct emc_timing *emc_find_timing(struct tegra_emc *emc,
+					  unsigned long rate)
+{
+	struct emc_timing *timing = NULL;
+	unsigned int i;
+
+	for (i = 0; i < emc->num_timings; i++) {
+		if (emc->timings[i].rate >= rate) {
+			timing = &emc->timings[i];
+			break;
+		}
+	}
+
+	if (!timing) {
+		dev_err(emc->dev, "no timing for rate %lu\n", rate);
+		return NULL;
+	}
+
+	return timing;
+}
+
+static bool emc_dqs_preset(struct tegra_emc *emc, struct emc_timing *timing,
+			   bool *schmitt_to_vref)
+{
+	bool preset = false;
+	u32 val;
+
+	if (timing->data[71] & EMC_XM2DQSPADCTRL2_VREF_ENABLE) {
+		val = readl_relaxed(emc->regs + EMC_XM2DQSPADCTRL2);
+
+		if (!(val & EMC_XM2DQSPADCTRL2_VREF_ENABLE)) {
+			val |= EMC_XM2DQSPADCTRL2_VREF_ENABLE;
+			writel_relaxed(val, emc->regs + EMC_XM2DQSPADCTRL2);
+
+			preset = true;
+		}
+	}
+
+	if (timing->data[78] & EMC_XM2DQSPADCTRL3_VREF_ENABLE) {
+		val = readl_relaxed(emc->regs + EMC_XM2DQSPADCTRL3);
+
+		if (!(val & EMC_XM2DQSPADCTRL3_VREF_ENABLE)) {
+			val |= EMC_XM2DQSPADCTRL3_VREF_ENABLE;
+			writel_relaxed(val, emc->regs + EMC_XM2DQSPADCTRL3);
+
+			preset = true;
+		}
+	}
+
+	if (timing->data[77] & EMC_XM2QUSEPADCTRL_IVREF_ENABLE) {
+		val = readl_relaxed(emc->regs + EMC_XM2QUSEPADCTRL);
+
+		if (!(val & EMC_XM2QUSEPADCTRL_IVREF_ENABLE)) {
+			val |= EMC_XM2QUSEPADCTRL_IVREF_ENABLE;
+			writel_relaxed(val, emc->regs + EMC_XM2QUSEPADCTRL);
+
+			*schmitt_to_vref = true;
+			preset = true;
+		}
+	}
+
+	return preset;
+}
+
+static int emc_seq_update_timing(struct tegra_emc *emc)
+{
+	u32 val;
+	int err;
+
+	writel_relaxed(EMC_TIMING_UPDATE, emc->regs + EMC_TIMING_CONTROL);
+
+	err = readl_relaxed_poll_timeout_atomic(emc->regs + EMC_STATUS, val,
+				!(val & EMC_STATUS_TIMING_UPDATE_STALLED),
+				1, 200);
+	if (err) {
+		dev_err(emc->dev, "failed to update timing: %d\n", err);
+		return err;
+	}
+
+	return 0;
+}
+
+static int emc_prepare_mc_clk_cfg(struct tegra_emc *emc, unsigned long rate)
+{
+	struct tegra_mc *mc = emc->mc;
+	unsigned int misc0_index = 16;
+	unsigned int i;
+	bool same;
+
+	for (i = 0; i < mc->num_timings; i++) {
+		if (mc->timings[i].rate != rate)
+			continue;
+
+		if (mc->timings[i].emem_data[misc0_index] & BIT(27))
+			same = true;
+		else
+			same = false;
+
+		return tegra20_clk_prepare_emc_mc_same_freq(emc->clk, same);
+	}
+
+	return -EINVAL;
+}
+
+static int emc_prepare_timing_change(struct tegra_emc *emc, unsigned long rate)
+{
+	struct emc_timing *timing = emc_find_timing(emc, rate);
+	enum emc_dll_change dll_change;
+	enum emc_dram_type dram_type;
+	bool schmitt_to_vref = false;
+	unsigned int pre_wait = 0;
+	bool qrst_used = false;
+	unsigned int dram_num;
+	unsigned int i;
+	u32 fbio_cfg5;
+	u32 emc_dbg;
+	u32 val;
+	int err;
+
+	if (!timing || emc->bad_state)
+		return -EINVAL;
+
+	dev_dbg(emc->dev, "%s: using timing rate %lu for requested rate %lu\n",
+		__func__, timing->rate, rate);
+
+	emc->bad_state = true;
+
+	err = emc_prepare_mc_clk_cfg(emc, rate);
+	if (err) {
+		dev_err(emc->dev, "mc clock preparation failed: %d\n", err);
+		return err;
+	}
+
+	emc->vref_cal_toggle = false;
+	emc->mc_override = mc_readl(emc->mc, MC_EMEM_ARB_OVERRIDE);
+	emc->emc_cfg = readl_relaxed(emc->regs + EMC_CFG);
+	emc_dbg = readl_relaxed(emc->regs + EMC_DBG);
+
+	if (emc->dll_on == !!(timing->emc_mode_1 & 0x1))
+		dll_change = DLL_CHANGE_NONE;
+	else if (timing->emc_mode_1 & 0x1)
+		dll_change = DLL_CHANGE_ON;
+	else
+		dll_change = DLL_CHANGE_OFF;
+
+	emc->dll_on = !!(timing->emc_mode_1 & 0x1);
+
+	if (timing->data[80] && !readl_relaxed(emc->regs + EMC_ZCAL_INTERVAL))
+		emc->zcal_long = true;
+	else
+		emc->zcal_long = false;
+
+	fbio_cfg5 = readl_relaxed(emc->regs + EMC_FBIO_CFG5);
+	dram_type = fbio_cfg5 & EMC_FBIO_CFG5_DRAM_TYPE_MASK;
+
+	dram_num = tegra_mc_get_emem_device_count(emc->mc);
+
+	/* disable dynamic self-refresh */
+	if (emc->emc_cfg & EMC_CFG_DYN_SREF_ENABLE) {
+		emc->emc_cfg &= ~EMC_CFG_DYN_SREF_ENABLE;
+		writel_relaxed(emc->emc_cfg, emc->regs + EMC_CFG);
+
+		pre_wait = 5;
+	}
+
+	/* update MC arbiter settings */
+	val = mc_readl(emc->mc, MC_EMEM_ARB_OUTSTANDING_REQ);
+	if (!(val & MC_EMEM_ARB_OUTSTANDING_REQ_HOLDOFF_OVERRIDE) ||
+	    ((val & MC_EMEM_ARB_OUTSTANDING_REQ_MAX_MASK) > 0x50)) {
+
+		val = MC_EMEM_ARB_OUTSTANDING_REQ_LIMIT_ENABLE |
+		      MC_EMEM_ARB_OUTSTANDING_REQ_HOLDOFF_OVERRIDE | 0x50;
+		mc_writel(emc->mc, val, MC_EMEM_ARB_OUTSTANDING_REQ);
+		mc_writel(emc->mc, MC_TIMING_UPDATE, MC_TIMING_CONTROL);
+	}
+
+	if (emc->mc_override & MC_EMEM_ARB_OVERRIDE_EACK_MASK)
+		mc_writel(emc->mc,
+			  emc->mc_override & ~MC_EMEM_ARB_OVERRIDE_EACK_MASK,
+			  MC_EMEM_ARB_OVERRIDE);
+
+	/* check DQ/DQS VREF delay */
+	if (emc_dqs_preset(emc, timing, &schmitt_to_vref)) {
+		if (pre_wait < 3)
+			pre_wait = 3;
+	}
+
+	if (pre_wait) {
+		err = emc_seq_update_timing(emc);
+		if (err)
+			return err;
+
+		udelay(pre_wait);
+	}
+
+	/* disable auto-calibration if VREF mode is switching */
+	if (timing->emc_auto_cal_interval) {
+		val = readl_relaxed(emc->regs + EMC_XM2COMPPADCTRL);
+		val ^= timing->data[74];
+
+		if (val & EMC_XM2COMPPADCTRL_VREF_CAL_ENABLE) {
+			writel_relaxed(0, emc->regs + EMC_AUTO_CAL_INTERVAL);
+
+			err = readl_relaxed_poll_timeout_atomic(
+				emc->regs + EMC_AUTO_CAL_STATUS, val,
+				!(val & EMC_AUTO_CAL_STATUS_ACTIVE), 1, 300);
+			if (err) {
+				dev_err(emc->dev,
+					"failed to disable auto-cal: %d\n",
+					err);
+				return err;
+			}
+
+			emc->vref_cal_toggle = true;
+		}
+	}
+
+	/* program shadow registers */
+	for (i = 0; i < ARRAY_SIZE(timing->data); i++) {
+		/* EMC_XM2CLKPADCTRL should be programmed separately */
+		if (i != 73)
+			writel_relaxed(timing->data[i],
+				       emc->regs + emc_timing_registers[i]);
+	}
+
+	err = tegra_mc_write_emem_configuration(emc->mc, timing->rate);
+	if (err)
+		return err;
+
+	/* DDR3: predict MRS long wait count */
+	if (dram_type == DRAM_TYPE_DDR3 && dll_change == DLL_CHANGE_ON) {
+		u32 cnt = 512;
+
+		if (emc->zcal_long)
+			cnt -= dram_num * 256;
+
+		val = timing->data[82] & EMC_MRS_WAIT_CNT_SHORT_WAIT_MASK;
+		if (cnt < val)
+			cnt = val;
+
+		val = timing->data[82] & ~EMC_MRS_WAIT_CNT_LONG_WAIT_MASK;
+		val |= (cnt << EMC_MRS_WAIT_CNT_LONG_WAIT_SHIFT) &
+			EMC_MRS_WAIT_CNT_LONG_WAIT_MASK;
+
+		writel_relaxed(val, emc->regs + EMC_MRS_WAIT_CNT);
+	}
+
+	/* disable interrupt since read access is prohibited after stalling */
+	disable_irq(emc->irq);
+
+	/* this read also completes the writes */
+	val = readl_relaxed(emc->regs + EMC_SEL_DPD_CTRL);
+
+	if (!(val & EMC_SEL_DPD_CTRL_QUSE_DPD_ENABLE) && schmitt_to_vref) {
+		u32 cur_mode, new_mode;
+
+		cur_mode = fbio_cfg5 & EMC_CFG5_QUSE_MODE_MASK;
+		cur_mode >>= EMC_CFG5_QUSE_MODE_SHIFT;
+
+		new_mode = timing->data[39] & EMC_CFG5_QUSE_MODE_MASK;
+		new_mode >>= EMC_CFG5_QUSE_MODE_SHIFT;
+
+		if ((cur_mode != EMC_CFG5_QUSE_MODE_PULSE_INTERN &&
+		     cur_mode != EMC_CFG5_QUSE_MODE_INTERNAL_LPBK) ||
+		    (new_mode != EMC_CFG5_QUSE_MODE_PULSE_INTERN &&
+		     new_mode != EMC_CFG5_QUSE_MODE_INTERNAL_LPBK))
+			qrst_used = true;
+	}
+
+	/* flow control marker 1 */
+	writel_relaxed(0x1, emc->regs + EMC_STALL_THEN_EXE_BEFORE_CLKCHANGE);
+
+	/* enable periodic reset */
+	if (qrst_used) {
+		writel_relaxed(emc_dbg | EMC_DBG_WRITE_MUX_ACTIVE,
+			       emc->regs + EMC_DBG);
+		writel_relaxed(emc->emc_cfg | EMC_CFG_PERIODIC_QRST,
+			       emc->regs + EMC_CFG);
+		writel_relaxed(emc_dbg, emc->regs + EMC_DBG);
+	}
+
+	/* disable auto-refresh to save time after clock change */
+	writel_relaxed(EMC_REFCTRL_DISABLE_ALL(dram_num),
+		       emc->regs + EMC_REFCTRL);
+
+	/* turn off DLL and enter self-refresh on DDR3 */
+	if (dram_type == DRAM_TYPE_DDR3) {
+		if (dll_change == DLL_CHANGE_OFF)
+			writel_relaxed(timing->emc_mode_1,
+				       emc->regs + EMC_EMRS);
+
+		writel_relaxed(DRAM_BROADCAST(dram_num) |
+			       EMC_SELF_REF_CMD_ENABLED,
+			       emc->regs + EMC_SELF_REF);
+	}
+
+	/* flow control marker 2 */
+	writel_relaxed(0x1, emc->regs + EMC_STALL_THEN_EXE_AFTER_CLKCHANGE);
+
+	/* enable write-active MUX, update unshadowed pad control */
+	writel_relaxed(emc_dbg | EMC_DBG_WRITE_MUX_ACTIVE, emc->regs + EMC_DBG);
+	writel_relaxed(timing->data[73], emc->regs + EMC_XM2CLKPADCTRL);
+
+	/* restore periodic QRST and disable write-active MUX */
+	val = !!(emc->emc_cfg & EMC_CFG_PERIODIC_QRST);
+	if (qrst_used || timing->emc_cfg_periodic_qrst != val) {
+		if (timing->emc_cfg_periodic_qrst)
+			emc->emc_cfg |= EMC_CFG_PERIODIC_QRST;
+		else
+			emc->emc_cfg &= ~EMC_CFG_PERIODIC_QRST;
+
+		writel_relaxed(emc->emc_cfg, emc->regs + EMC_CFG);
+	}
+	writel_relaxed(emc_dbg, emc->regs + EMC_DBG);
+
+	/* exit self-refresh on DDR3 */
+	if (dram_type == DRAM_TYPE_DDR3)
+		writel_relaxed(DRAM_BROADCAST(dram_num),
+			       emc->regs + EMC_SELF_REF);
+
+	/* set DRAM-mode registers */
+	if (dram_type == DRAM_TYPE_DDR3) {
+		if (timing->emc_mode_1 != emc->emc_mode_1)
+			writel_relaxed(timing->emc_mode_1,
+				       emc->regs + EMC_EMRS);
+
+		if (timing->emc_mode_2 != emc->emc_mode_2)
+			writel_relaxed(timing->emc_mode_2,
+				       emc->regs + EMC_EMRS);
+
+		if (timing->emc_mode_reset != emc->emc_mode_reset ||
+		    dll_change == DLL_CHANGE_ON) {
+			val = timing->emc_mode_reset;
+			if (dll_change == DLL_CHANGE_ON) {
+				val |= EMC_MODE_SET_DLL_RESET;
+				val |= EMC_MODE_SET_LONG_CNT;
+			} else {
+				val &= ~EMC_MODE_SET_DLL_RESET;
+			}
+			writel_relaxed(val, emc->regs + EMC_MRS);
+		}
+	} else {
+		if (timing->emc_mode_2 != emc->emc_mode_2)
+			writel_relaxed(timing->emc_mode_2,
+				       emc->regs + EMC_MRW);
+
+		if (timing->emc_mode_1 != emc->emc_mode_1)
+			writel_relaxed(timing->emc_mode_1,
+				       emc->regs + EMC_MRW);
+	}
+
+	emc->emc_mode_1 = timing->emc_mode_1;
+	emc->emc_mode_2 = timing->emc_mode_2;
+	emc->emc_mode_reset = timing->emc_mode_reset;
+
+	/* issue ZCAL command if turning ZCAL on */
+	if (emc->zcal_long) {
+		writel_relaxed(EMC_ZQ_CAL_LONG_CMD_DEV0,
+			       emc->regs + EMC_ZQ_CAL);
+
+		if (dram_num > 1)
+			writel_relaxed(EMC_ZQ_CAL_LONG_CMD_DEV1,
+				       emc->regs + EMC_ZQ_CAL);
+	}
+
+	/* re-enable auto-refresh */
+	writel_relaxed(EMC_REFCTRL_ENABLE_ALL(dram_num),
+		       emc->regs + EMC_REFCTRL);
+
+	/* flow control marker 3 */
+	writel_relaxed(0x1, emc->regs + EMC_UNSTALL_RW_AFTER_CLKCHANGE);
+
+	reinit_completion(&emc->clk_handshake_complete);
+
+	/* interrupt can be re-enabled now */
+	enable_irq(emc->irq);
+
+	emc->bad_state = false;
+	emc->prepared = true;
+
+	return 0;
+}
+
+static int emc_complete_timing_change(struct tegra_emc *emc,
+				      unsigned long rate)
+{
+	struct emc_timing *timing = emc_find_timing(emc, rate);
+	unsigned long timeout;
+	int ret;
+
+	timeout = wait_for_completion_timeout(&emc->clk_handshake_complete,
+					      msecs_to_jiffies(100));
+	if (timeout == 0) {
+		dev_err(emc->dev, "emc-car handshake failed\n");
+		emc->bad_state = true;
+		return -EIO;
+	}
+
+	/* restore auto-calibration */
+	if (emc->vref_cal_toggle)
+		writel_relaxed(timing->emc_auto_cal_interval,
+			       emc->regs + EMC_AUTO_CAL_INTERVAL);
+
+	/* restore dynamic self-refresh */
+	if (timing->emc_cfg_dyn_self_ref) {
+		emc->emc_cfg |= EMC_CFG_DYN_SREF_ENABLE;
+		writel_relaxed(emc->emc_cfg, emc->regs + EMC_CFG);
+	}
+
+	/* set number of clocks to wait after each ZQ command */
+	if (emc->zcal_long)
+		writel_relaxed(timing->emc_zcal_cnt_long,
+			       emc->regs + EMC_ZCAL_WAIT_CNT);
+
+	udelay(2);
+	/* update restored timing */
+	ret = emc_seq_update_timing(emc);
+	if (ret)
+		emc->bad_state = true;
+
+	/* restore early ACK */
+	mc_writel(emc->mc, emc->mc_override, MC_EMEM_ARB_OVERRIDE);
+
+	emc->prepared = false;
+
+	return ret;
+}
+
+static int emc_unprepare_timing_change(struct tegra_emc *emc,
+				       unsigned long rate)
+{
+	if (emc->prepared && !emc->bad_state) {
+		/* shouldn't ever happen in practice */
+		dev_err(emc->dev, "timing configuration can't be reverted\n");
+		emc->bad_state = true;
+	}
+
+	return 0;
+}
+
+static int emc_clk_change_notify(struct notifier_block *nb,
+				 unsigned long msg, void *data)
+{
+	struct tegra_emc *emc = container_of(nb, struct tegra_emc, clk_nb);
+	struct clk_notifier_data *cnd = data;
+	int err;
+
+	switch (msg) {
+	case PRE_RATE_CHANGE:
+		err = emc_prepare_timing_change(emc, cnd->new_rate);
+		break;
+
+	case ABORT_RATE_CHANGE:
+		err = emc_unprepare_timing_change(emc, cnd->old_rate);
+		break;
+
+	case POST_RATE_CHANGE:
+		err = emc_complete_timing_change(emc, cnd->new_rate);
+		break;
+
+	default:
+		return NOTIFY_DONE;
+	}
+
+	return notifier_from_errno(err);
+}
+
+static int load_one_timing_from_dt(struct tegra_emc *emc,
+				   struct emc_timing *timing,
+				   struct device_node *node)
+{
+	u32 value;
+	int err;
+
+	err = of_property_read_u32(node, "clock-frequency", &value);
+	if (err) {
+		dev_err(emc->dev, "timing %pOF: failed to read rate: %d\n",
+			node, err);
+		return err;
+	}
+
+	timing->rate = value;
+
+	err = of_property_read_u32_array(node, "nvidia,emc-configuration",
+					 timing->data,
+					 ARRAY_SIZE(emc_timing_registers));
+	if (err) {
+		dev_err(emc->dev,
+			"timing %pOF: failed to read emc timing data: %d\n",
+			node, err);
+		return err;
+	}
+
+#define EMC_READ_BOOL(prop, dtprop) \
+	timing->prop = of_property_read_bool(node, dtprop);
+
+#define EMC_READ_U32(prop, dtprop) \
+	err = of_property_read_u32(node, dtprop, &timing->prop); \
+	if (err) { \
+		dev_err(emc->dev, \
+			"timing %pOFn: failed to read " #prop ": %d\n", \
+			node, err); \
+		return err; \
+	}
+
+	EMC_READ_U32(emc_auto_cal_interval, "nvidia,emc-auto-cal-interval")
+	EMC_READ_U32(emc_mode_1, "nvidia,emc-mode-1")
+	EMC_READ_U32(emc_mode_2, "nvidia,emc-mode-2")
+	EMC_READ_U32(emc_mode_reset, "nvidia,emc-mode-reset")
+	EMC_READ_U32(emc_zcal_cnt_long, "nvidia,emc-zcal-cnt-long")
+	EMC_READ_BOOL(emc_cfg_dyn_self_ref, "nvidia,emc-cfg-dyn-self-ref")
+	EMC_READ_BOOL(emc_cfg_periodic_qrst, "nvidia,emc-cfg-periodic-qrst")
+
+#undef EMC_READ_U32
+#undef EMC_READ_BOOL
+
+	dev_dbg(emc->dev, "%s: %pOF: rate %lu\n", __func__, node, timing->rate);
+
+	return 0;
+}
+
+static int cmp_timings(const void *_a, const void *_b)
+{
+	const struct emc_timing *a = _a;
+	const struct emc_timing *b = _b;
+
+	if (a->rate < b->rate)
+		return -1;
+
+	if (a->rate > b->rate)
+		return 1;
+
+	return 0;
+}
+
+static int emc_check_mc_timings(struct tegra_emc *emc)
+{
+	struct tegra_mc *mc = emc->mc;
+	unsigned int i;
+
+	if (emc->num_timings != mc->num_timings) {
+		dev_err(emc->dev, "emc/mc timings number mismatch: %u %u\n",
+			emc->num_timings, mc->num_timings);
+		return -EINVAL;
+	}
+
+	for (i = 0; i < mc->num_timings; i++) {
+		if (emc->timings[i].rate != mc->timings[i].rate) {
+			dev_err(emc->dev,
+				"emc/mc timing rate mismatch: %lu %lu\n",
+				emc->timings[i].rate, mc->timings[i].rate);
+			return -EINVAL;
+		}
+	}
+
+	return 0;
+}
+
+static int emc_load_timings_from_dt(struct tegra_emc *emc,
+				    struct device_node *node)
+{
+	struct device_node *child;
+	struct emc_timing *timing;
+	int child_count;
+	int err;
+
+	child_count = of_get_child_count(node);
+	if (!child_count) {
+		dev_err(emc->dev, "no memory timings in: %pOF\n", node);
+		return -EINVAL;
+	}
+
+	emc->timings = devm_kcalloc(emc->dev, child_count, sizeof(*timing),
+				    GFP_KERNEL);
+	if (!emc->timings)
+		return -ENOMEM;
+
+	emc->num_timings = child_count;
+	timing = emc->timings;
+
+	for_each_child_of_node(node, child) {
+		err = load_one_timing_from_dt(emc, timing++, child);
+		if (err) {
+			of_node_put(child);
+			return err;
+		}
+	}
+
+	sort(emc->timings, emc->num_timings, sizeof(*timing), cmp_timings,
+	     NULL);
+
+	err = emc_check_mc_timings(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);
+
+	return 0;
+}
+
+static struct device_node *emc_find_node_by_ram_code(struct device *dev)
+{
+	struct device_node *np;
+	u32 value, ram_code;
+	int err;
+
+	ram_code = tegra_read_ram_code();
+
+	for_each_child_of_node(dev->of_node, np) {
+		err = of_property_read_u32(np, "nvidia,ram-code", &value);
+		if (err || value != ram_code)
+			continue;
+
+		return np;
+	}
+
+	dev_err(dev, "no memory timings for RAM code %u found in device-tree\n",
+		ram_code);
+
+	return NULL;
+}
+
+static int emc_setup_hw(struct tegra_emc *emc)
+{
+	u32 intmask = EMC_REFRESH_OVERFLOW_INT | EMC_CLKCHANGE_COMPLETE_INT;
+	u32 fbio_cfg5, emc_cfg, emc_dbg;
+	enum emc_dram_type dram_type;
+
+	fbio_cfg5 = readl_relaxed(emc->regs + EMC_FBIO_CFG5);
+	dram_type = fbio_cfg5 & EMC_FBIO_CFG5_DRAM_TYPE_MASK;
+
+	emc_cfg = readl_relaxed(emc->regs + EMC_CFG_2);
+
+	/* enable EMC and CAR to handshake on PLL divider/source changes */
+	emc_cfg |= EMC_CLKCHANGE_REQ_ENABLE;
+
+	/* configure clock change mode accordingly to DRAM type */
+	switch (dram_type) {
+	case DRAM_TYPE_LPDDR2:
+		emc_cfg |= EMC_CLKCHANGE_PD_ENABLE;
+		emc_cfg &= ~EMC_CLKCHANGE_SR_ENABLE;
+		break;
+
+	default:
+		emc_cfg &= ~EMC_CLKCHANGE_SR_ENABLE;
+		emc_cfg &= ~EMC_CLKCHANGE_PD_ENABLE;
+		break;
+	}
+
+	writel_relaxed(emc_cfg, emc->regs + EMC_CFG_2);
+
+	/* initialize interrupt */
+	writel_relaxed(intmask, emc->regs + EMC_INTMASK);
+	writel_relaxed(0xffffffff, emc->regs + EMC_INTSTATUS);
+
+	/* ensure that unwanted debug features are disabled */
+	emc_dbg = readl_relaxed(emc->regs + EMC_DBG);
+	emc_dbg |= EMC_DBG_CFG_PRIORITY;
+	emc_dbg &= ~EMC_DBG_READ_MUX_ASSEMBLY;
+	emc_dbg &= ~EMC_DBG_WRITE_MUX_ACTIVE;
+	emc_dbg &= ~EMC_DBG_FORCE_UPDATE;
+	writel_relaxed(emc_dbg, emc->regs + EMC_DBG);
+
+	return 0;
+}
+
+static long emc_round_rate(unsigned long rate,
+			   unsigned long min_rate,
+			   unsigned long max_rate,
+			   void *arg)
+{
+	struct emc_timing *timing = NULL;
+	struct tegra_emc *emc = arg;
+	unsigned int i;
+
+	min_rate = min(min_rate, emc->timings[emc->num_timings - 1].rate);
+
+	for (i = 0; i < emc->num_timings; i++) {
+		if (emc->timings[i].rate < rate && i != emc->num_timings - 1)
+			continue;
+
+		if (emc->timings[i].rate > max_rate) {
+			i = max(i, 1u) - 1;
+
+			if (emc->timings[i].rate < min_rate)
+				break;
+		}
+
+		if (emc->timings[i].rate < min_rate)
+			continue;
+
+		timing = &emc->timings[i];
+		break;
+	}
+
+	if (!timing) {
+		dev_err(emc->dev, "no timing for rate %lu min %lu max %lu\n",
+			rate, min_rate, max_rate);
+		return -EINVAL;
+	}
+
+	return timing->rate;
+}
+
+static int tegra_emc_probe(struct platform_device *pdev)
+{
+	struct platform_device *mc;
+	struct device_node *np;
+	struct tegra_emc *emc;
+	int err;
+
+	if (of_get_child_count(pdev->dev.of_node) == 0) {
+		dev_info(&pdev->dev,
+			 "device-tree node doesn't have memory timings\n");
+		return 0;
+	}
+
+	np = of_parse_phandle(pdev->dev.of_node, "nvidia,memory-controller", 0);
+	if (!np) {
+		dev_err(&pdev->dev, "could not get memory controller node\n");
+		return -ENOENT;
+	}
+
+	mc = of_find_device_by_node(np);
+	of_node_put(np);
+	if (!mc)
+		return -ENOENT;
+
+	np = emc_find_node_by_ram_code(&pdev->dev);
+	if (!np)
+		return -EINVAL;
+
+	emc = devm_kzalloc(&pdev->dev, sizeof(*emc), GFP_KERNEL);
+	if (!emc) {
+		of_node_put(np);
+		return -ENOMEM;
+	}
+
+	emc->mc = platform_get_drvdata(mc);
+	if (!emc->mc)
+		return -EPROBE_DEFER;
+
+	init_completion(&emc->clk_handshake_complete);
+	emc->clk_nb.notifier_call = emc_clk_change_notify;
+	emc->dev = &pdev->dev;
+
+	err = emc_load_timings_from_dt(emc, np);
+	of_node_put(np);
+	if (err)
+		return err;
+
+	emc->regs = devm_platform_ioremap_resource(pdev, 0);
+	if (IS_ERR(emc->regs))
+		return PTR_ERR(emc->regs);
+
+	err = emc_setup_hw(emc);
+	if (err)
+		return err;
+
+	err = platform_get_irq(pdev, 0);
+	if (err < 0) {
+		dev_err(&pdev->dev, "interrupt not specified: %d\n", err);
+		return err;
+	}
+	emc->irq = err;
+
+	err = devm_request_irq(&pdev->dev, emc->irq, tegra_emc_isr, 0,
+			       dev_name(&pdev->dev), emc);
+	if (err) {
+		dev_err(&pdev->dev, "failed to request irq: %d\n", err);
+		return err;
+	}
+
+	tegra20_clk_set_emc_round_callback(emc_round_rate, emc);
+
+	emc->clk = devm_clk_get(&pdev->dev, "emc");
+	if (IS_ERR(emc->clk)) {
+		err = PTR_ERR(emc->clk);
+		dev_err(&pdev->dev, "failed to get emc clock: %d\n", err);
+		goto unset_cb;
+	}
+
+	err = clk_notifier_register(emc->clk, &emc->clk_nb);
+	if (err) {
+		dev_err(&pdev->dev, "failed to register clk notifier: %d\n",
+			err);
+		goto unset_cb;
+	}
+
+	platform_set_drvdata(pdev, emc);
+
+	return 0;
+
+unset_cb:
+	tegra20_clk_set_emc_round_callback(NULL, NULL);
+
+	return err;
+}
+
+static int tegra_emc_suspend(struct device *dev)
+{
+	struct tegra_emc *emc = dev_get_drvdata(dev);
+
+	/*
+	 * Suspending in a bad state will hang machine. The "prepared" var
+	 * shall be always false here unless it's a kernel bug that caused
+	 * suspending in a wrong order.
+	 */
+	if (WARN_ON(emc->prepared) || emc->bad_state)
+		return -EINVAL;
+
+	emc->bad_state = true;
+
+	return 0;
+}
+
+static int tegra_emc_resume(struct device *dev)
+{
+	struct tegra_emc *emc = dev_get_drvdata(dev);
+
+	emc_setup_hw(emc);
+	emc->bad_state = false;
+
+	return 0;
+}
+
+static const struct dev_pm_ops tegra_emc_pm_ops = {
+	.suspend = tegra_emc_suspend,
+	.resume = tegra_emc_resume,
+};
+
+static const struct of_device_id tegra_emc_of_match[] = {
+	{ .compatible = "nvidia,tegra30-emc", },
+	{},
+};
+
+static struct platform_driver tegra_emc_driver = {
+	.probe = tegra_emc_probe,
+	.driver = {
+		.name = "tegra30-emc",
+		.of_match_table = tegra_emc_of_match,
+		.pm = &tegra_emc_pm_ops,
+		.suppress_bind_attrs = true,
+	},
+};
+
+static int __init tegra_emc_init(void)
+{
+	return platform_driver_register(&tegra_emc_driver);
+}
+subsys_initcall(tegra_emc_init);
diff --git a/drivers/memory/tegra/tegra30.c b/drivers/memory/tegra/tegra30.c
index 14788fc2f9e8..b1226d3f067f 100644
--- a/drivers/memory/tegra/tegra30.c
+++ b/drivers/memory/tegra/tegra30.c
@@ -10,6 +10,46 @@
 
 #include "mc.h"
 
+#define MC_EMEM_ARB_CFG				0x90
+#define MC_EMEM_ARB_OUTSTANDING_REQ		0x94
+#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_DA_TURNS			0xd0
+#define MC_EMEM_ARB_DA_COVERS			0xd4
+#define MC_EMEM_ARB_MISC0			0xd8
+#define MC_EMEM_ARB_RING1_THROTTLE		0xe0
+
+static const unsigned long tegra30_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_client tegra30_mc_clients[] = {
 	{
 		.id = 0x00,
@@ -994,6 +1034,8 @@ const struct tegra_mc_soc tegra30_mc_soc = {
 	.atom_size = 16,
 	.client_id_mask = 0x7f,
 	.smmu = &tegra30_smmu_soc,
+	.emem_regs = tegra30_mc_emem_regs,
+	.num_emem_regs = ARRAY_SIZE(tegra30_mc_emem_regs),
 	.intmask = MC_INT_INVALID_SMMU_PAGE | MC_INT_SECURITY_VIOLATION |
 		   MC_INT_DECERR_EMEM,
 	.reset_ops = &tegra_mc_reset_ops_common,
diff --git a/include/soc/tegra/mc.h b/include/soc/tegra/mc.h
index 16e2c2fb5f6c..1238e35653d1 100644
--- a/include/soc/tegra/mc.h
+++ b/include/soc/tegra/mc.h
@@ -181,7 +181,7 @@ struct tegra_mc {
 	spinlock_t lock;
 };
 
-void tegra_mc_write_emem_configuration(struct tegra_mc *mc, unsigned long rate);
+int tegra_mc_write_emem_configuration(struct tegra_mc *mc, unsigned long rate);
 unsigned int tegra_mc_get_emem_device_count(struct tegra_mc *mc);
 
 #endif /* __SOC_TEGRA_MC_H__ */
-- 
2.22.0


^ permalink raw reply	[flat|nested] 27+ messages in thread

* [PATCH v10 13/15] memory: tegra: Ensure timing control debug features are disabled
  2019-08-11 21:00 [PATCH v10 00/15] memory: tegra: Introduce Tegra30 EMC driver Dmitry Osipenko
                   ` (11 preceding siblings ...)
  2019-08-11 21:00 ` [PATCH v10 12/15] memory: tegra: Introduce Tegra30 EMC driver Dmitry Osipenko
@ 2019-08-11 21:00 ` Dmitry Osipenko
  2019-08-11 21:00 ` [PATCH v10 14/15] memory: tegra: Consolidate registers definition into common header Dmitry Osipenko
  2019-08-11 21:00 ` [PATCH v10 15/15] ARM: dts: tegra30: Add External Memory Controller node Dmitry Osipenko
  14 siblings, 0 replies; 27+ messages in thread
From: Dmitry Osipenko @ 2019-08-11 21:00 UTC (permalink / raw)
  To: Rob Herring, Michael Turquette, Joseph Lo, Thierry Reding,
	Jonathan Hunter, Peter De Schrijver, Prashant Gaikwad,
	Stephen Boyd
  Cc: devicetree, linux-clk, linux-tegra, linux-kernel

Timing control debug features should be disabled at a boot time, but you
never now and hence it's better to disable them explicitly because some of
those features are crucial for the driver to do a proper thing.

Acked-by: Peter De Schrijver <pdeschrijver@nvidia.com>
Signed-off-by: Dmitry Osipenko <digetx@gmail.com>
---
 drivers/memory/tegra/mc.c | 3 +++
 drivers/memory/tegra/mc.h | 2 ++
 2 files changed, 5 insertions(+)

diff --git a/drivers/memory/tegra/mc.c b/drivers/memory/tegra/mc.c
index 43819e8df95c..1bad7f238881 100644
--- a/drivers/memory/tegra/mc.c
+++ b/drivers/memory/tegra/mc.c
@@ -657,6 +657,9 @@ static int tegra_mc_probe(struct platform_device *pdev)
 	} else
 #endif
 	{
+		/* ensure that debug features are disabled */
+		mc_writel(mc, 0x00000000, MC_TIMING_CONTROL_DBG);
+
 		err = tegra_mc_setup_latency_allowance(mc);
 		if (err < 0) {
 			dev_err(&pdev->dev,
diff --git a/drivers/memory/tegra/mc.h b/drivers/memory/tegra/mc.h
index 410efc4d7e7b..cd52628c2b96 100644
--- a/drivers/memory/tegra/mc.h
+++ b/drivers/memory/tegra/mc.h
@@ -30,6 +30,8 @@
 #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)
 
-- 
2.22.0


^ permalink raw reply	[flat|nested] 27+ messages in thread

* [PATCH v10 14/15] memory: tegra: Consolidate registers definition into common header
  2019-08-11 21:00 [PATCH v10 00/15] memory: tegra: Introduce Tegra30 EMC driver Dmitry Osipenko
                   ` (12 preceding siblings ...)
  2019-08-11 21:00 ` [PATCH v10 13/15] memory: tegra: Ensure timing control debug features are disabled Dmitry Osipenko
@ 2019-08-11 21:00 ` Dmitry Osipenko
  2019-08-11 21:00 ` [PATCH v10 15/15] ARM: dts: tegra30: Add External Memory Controller node Dmitry Osipenko
  14 siblings, 0 replies; 27+ messages in thread
From: Dmitry Osipenko @ 2019-08-11 21:00 UTC (permalink / raw)
  To: Rob Herring, Michael Turquette, Joseph Lo, Thierry Reding,
	Jonathan Hunter, Peter De Schrijver, Prashant Gaikwad,
	Stephen Boyd
  Cc: devicetree, linux-clk, linux-tegra, linux-kernel

The Memory Controller registers definition is sparse and duplicated,
let's consolidate everything into a common place for consistency.

Acked-by: Peter De Schrijver <pdeschrijver@nvidia.com>
Signed-off-by: Dmitry Osipenko <digetx@gmail.com>
---
 drivers/memory/tegra/mc.c       | 30 -------------------
 drivers/memory/tegra/mc.h       | 52 +++++++++++++++++++++++++++++----
 drivers/memory/tegra/tegra124.c | 20 -------------
 drivers/memory/tegra/tegra30.c  | 19 ------------
 4 files changed, 47 insertions(+), 74 deletions(-)

diff --git a/drivers/memory/tegra/mc.c b/drivers/memory/tegra/mc.c
index 1bad7f238881..955f1d3f6b6a 100644
--- a/drivers/memory/tegra/mc.c
+++ b/drivers/memory/tegra/mc.c
@@ -18,36 +18,6 @@
 
 #include "mc.h"
 
-#define MC_INTSTATUS 0x000
-
-#define MC_INTMASK 0x004
-
-#define MC_ERR_STATUS 0x08
-#define  MC_ERR_STATUS_TYPE_SHIFT 28
-#define  MC_ERR_STATUS_TYPE_INVALID_SMMU_PAGE (6 << MC_ERR_STATUS_TYPE_SHIFT)
-#define  MC_ERR_STATUS_TYPE_MASK (0x7 << MC_ERR_STATUS_TYPE_SHIFT)
-#define  MC_ERR_STATUS_READABLE (1 << 27)
-#define  MC_ERR_STATUS_WRITABLE (1 << 26)
-#define  MC_ERR_STATUS_NONSECURE (1 << 25)
-#define  MC_ERR_STATUS_ADR_HI_SHIFT 20
-#define  MC_ERR_STATUS_ADR_HI_MASK 0x3
-#define  MC_ERR_STATUS_SECURITY (1 << 17)
-#define  MC_ERR_STATUS_RW (1 << 16)
-
-#define MC_ERR_ADR 0x0c
-
-#define MC_GART_ERROR_REQ		0x30
-#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) << 0)
-#define  MC_EMEM_ARB_CFG_CYCLES_PER_UPDATE_MASK	0x1ff
-#define MC_EMEM_ARB_MISC0 0xd8
-
-#define MC_EMEM_ADR_CFG 0x54
-#define MC_EMEM_ADR_CFG_EMEM_NUMDEV BIT(0)
-
 static const struct of_device_id tegra_mc_of_match[] = {
 #ifdef CONFIG_ARCH_TEGRA_2x_SOC
 	{ .compatible = "nvidia,tegra20-mc-gart", .data = &tegra20_mc_soc },
diff --git a/drivers/memory/tegra/mc.h b/drivers/memory/tegra/mc.h
index cd52628c2b96..957c6eb74ff9 100644
--- a/drivers/memory/tegra/mc.h
+++ b/drivers/memory/tegra/mc.h
@@ -12,6 +12,37 @@
 
 #include <soc/tegra/mc.h>
 
+#define MC_INTSTATUS					0x00
+#define MC_INTMASK					0x04
+#define MC_ERR_STATUS					0x08
+#define MC_ERR_ADR					0x0c
+#define MC_GART_ERROR_REQ				0x30
+#define MC_EMEM_ADR_CFG					0x54
+#define MC_DECERR_EMEM_OTHERS_STATUS			0x58
+#define MC_SECURITY_VIOLATION_STATUS			0x74
+#define MC_EMEM_ARB_CFG					0x90
+#define MC_EMEM_ARB_OUTSTANDING_REQ			0x94
+#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_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_TIMING_CONTROL_DBG				0xf8
+#define MC_TIMING_CONTROL				0xfc
+
 #define MC_INT_DECERR_MTS				BIT(16)
 #define MC_INT_SECERR_SEC				BIT(13)
 #define MC_INT_DECERR_VPR				BIT(12)
@@ -22,17 +53,28 @@
 #define MC_INT_INVALID_GART_PAGE			BIT(7)
 #define MC_INT_DECERR_EMEM				BIT(6)
 
-#define MC_EMEM_ARB_OUTSTANDING_REQ			0x94
+#define MC_ERR_STATUS_TYPE_SHIFT			28
+#define MC_ERR_STATUS_TYPE_INVALID_SMMU_PAGE		(0x6 << 28)
+#define MC_ERR_STATUS_TYPE_MASK				(0x7 << 28)
+#define MC_ERR_STATUS_READABLE				BIT(27)
+#define MC_ERR_STATUS_WRITABLE				BIT(26)
+#define MC_ERR_STATUS_NONSECURE				BIT(25)
+#define MC_ERR_STATUS_ADR_HI_SHIFT			20
+#define MC_ERR_STATUS_ADR_HI_MASK			0x3
+#define MC_ERR_STATUS_SECURITY				BIT(17)
+#define MC_ERR_STATUS_RW				BIT(16)
+
+#define MC_EMEM_ADR_CFG_EMEM_NUMDEV			BIT(0)
+
+#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_MAX_MASK		0x1ff
 #define MC_EMEM_ARB_OUTSTANDING_REQ_HOLDOFF_OVERRIDE	BIT(30)
 #define MC_EMEM_ARB_OUTSTANDING_REQ_LIMIT_ENABLE	BIT(31)
 
-#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)
 
 static inline u32 mc_readl(struct tegra_mc *mc, unsigned long offset)
diff --git a/drivers/memory/tegra/tegra124.c b/drivers/memory/tegra/tegra124.c
index 5d0ccb2be206..4d80c81a4581 100644
--- a/drivers/memory/tegra/tegra124.c
+++ b/drivers/memory/tegra/tegra124.c
@@ -10,26 +10,6 @@
 
 #include "mc.h"
 
-#define MC_EMEM_ARB_CFG				0x90
-#define MC_EMEM_ARB_OUTSTANDING_REQ		0x94
-#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_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
-
 static const struct tegra_mc_client tegra124_mc_clients[] = {
 	{
 		.id = 0x00,
diff --git a/drivers/memory/tegra/tegra30.c b/drivers/memory/tegra/tegra30.c
index b1226d3f067f..fdc0b342cf80 100644
--- a/drivers/memory/tegra/tegra30.c
+++ b/drivers/memory/tegra/tegra30.c
@@ -10,25 +10,6 @@
 
 #include "mc.h"
 
-#define MC_EMEM_ARB_CFG				0x90
-#define MC_EMEM_ARB_OUTSTANDING_REQ		0x94
-#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_DA_TURNS			0xd0
-#define MC_EMEM_ARB_DA_COVERS			0xd4
-#define MC_EMEM_ARB_MISC0			0xd8
-#define MC_EMEM_ARB_RING1_THROTTLE		0xe0
-
 static const unsigned long tegra30_mc_emem_regs[] = {
 	MC_EMEM_ARB_CFG,
 	MC_EMEM_ARB_OUTSTANDING_REQ,
-- 
2.22.0


^ permalink raw reply	[flat|nested] 27+ messages in thread

* [PATCH v10 15/15] ARM: dts: tegra30: Add External Memory Controller node
  2019-08-11 21:00 [PATCH v10 00/15] memory: tegra: Introduce Tegra30 EMC driver Dmitry Osipenko
                   ` (13 preceding siblings ...)
  2019-08-11 21:00 ` [PATCH v10 14/15] memory: tegra: Consolidate registers definition into common header Dmitry Osipenko
@ 2019-08-11 21:00 ` Dmitry Osipenko
  14 siblings, 0 replies; 27+ messages in thread
From: Dmitry Osipenko @ 2019-08-11 21:00 UTC (permalink / raw)
  To: Rob Herring, Michael Turquette, Joseph Lo, Thierry Reding,
	Jonathan Hunter, Peter De Schrijver, Prashant Gaikwad,
	Stephen Boyd
  Cc: devicetree, linux-clk, linux-tegra, linux-kernel

Add External Memory Controller node to the device-tree.

Acked-by: Peter De Schrijver <pdeschrijver@nvidia.com>
Signed-off-by: Dmitry Osipenko <digetx@gmail.com>
---
 arch/arm/boot/dts/tegra30.dtsi | 9 +++++++++
 1 file changed, 9 insertions(+)

diff --git a/arch/arm/boot/dts/tegra30.dtsi b/arch/arm/boot/dts/tegra30.dtsi
index e074258d4518..8355264e2265 100644
--- a/arch/arm/boot/dts/tegra30.dtsi
+++ b/arch/arm/boot/dts/tegra30.dtsi
@@ -732,6 +732,15 @@
 		#reset-cells = <1>;
 	};
 
+	memory-controller@7000f400 {
+		compatible = "nvidia,tegra30-emc";
+		reg = <0x7000f400 0x400>;
+		interrupts = <GIC_SPI 78 IRQ_TYPE_LEVEL_HIGH>;
+		clocks = <&tegra_car TEGRA30_CLK_EMC>;
+
+		nvidia,memory-controller = <&mc>;
+	};
+
 	fuse@7000f800 {
 		compatible = "nvidia,tegra30-efuse";
 		reg = <0x7000f800 0x400>;
-- 
2.22.0


^ permalink raw reply	[flat|nested] 27+ messages in thread

* Re: [PATCH v10 09/15] dt-bindings: memory: tegra30: Convert to Tegra124 YAML
  2019-08-11 21:00 ` [PATCH v10 09/15] dt-bindings: memory: tegra30: Convert to Tegra124 YAML Dmitry Osipenko
@ 2019-08-12 19:53   ` Rob Herring
  2019-08-12 19:54     ` Rob Herring
  0 siblings, 1 reply; 27+ messages in thread
From: Rob Herring @ 2019-08-12 19:53 UTC (permalink / raw)
  To: Dmitry Osipenko
  Cc: Michael Turquette, Joseph Lo, Thierry Reding, Jonathan Hunter,
	Peter De Schrijver, Prashant Gaikwad, Stephen Boyd, devicetree,
	linux-clk, linux-tegra, linux-kernel

On Sun, Aug 11, 2019 at 3:01 PM Dmitry Osipenko <digetx@gmail.com> wrote:
>
> The Tegra30 binding will actually differ from the Tegra124 a tad, in
> particular the EMEM configuration description. Hence rename the binding
> to Tegra124 during of the conversion to YAML.
>
> Reviewed-by: Rob Herring <robh@kernel.org>
> Signed-off-by: Dmitry Osipenko <digetx@gmail.com>
> ---
>  .../nvidia,tegra124-mc.yaml                   | 152 ++++++++++++++++++
>  .../memory-controllers/nvidia,tegra30-mc.txt  | 123 --------------
>  2 files changed, 152 insertions(+), 123 deletions(-)
>  create mode 100644 Documentation/devicetree/bindings/memory-controllers/nvidia,tegra124-mc.yaml
>  delete mode 100644 Documentation/devicetree/bindings/memory-controllers/nvidia,tegra30-mc.txt

Reviewed-by: Rob Herring <robh@kernel.org>

^ permalink raw reply	[flat|nested] 27+ messages in thread

* Re: [PATCH v10 09/15] dt-bindings: memory: tegra30: Convert to Tegra124 YAML
  2019-08-12 19:53   ` Rob Herring
@ 2019-08-12 19:54     ` Rob Herring
  2019-08-12 20:19       ` Dmitry Osipenko
  0 siblings, 1 reply; 27+ messages in thread
From: Rob Herring @ 2019-08-12 19:54 UTC (permalink / raw)
  To: Dmitry Osipenko
  Cc: Michael Turquette, Joseph Lo, Thierry Reding, Jonathan Hunter,
	Peter De Schrijver, Prashant Gaikwad, Stephen Boyd, devicetree,
	linux-clk, linux-tegra, linux-kernel

On Mon, Aug 12, 2019 at 1:53 PM Rob Herring <robh+dt@kernel.org> wrote:
>
> On Sun, Aug 11, 2019 at 3:01 PM Dmitry Osipenko <digetx@gmail.com> wrote:
> >
> > The Tegra30 binding will actually differ from the Tegra124 a tad, in
> > particular the EMEM configuration description. Hence rename the binding
> > to Tegra124 during of the conversion to YAML.
> >
> > Reviewed-by: Rob Herring <robh@kernel.org>
> > Signed-off-by: Dmitry Osipenko <digetx@gmail.com>
> > ---
> >  .../nvidia,tegra124-mc.yaml                   | 152 ++++++++++++++++++
> >  .../memory-controllers/nvidia,tegra30-mc.txt  | 123 --------------
> >  2 files changed, 152 insertions(+), 123 deletions(-)
> >  create mode 100644 Documentation/devicetree/bindings/memory-controllers/nvidia,tegra124-mc.yaml
> >  delete mode 100644 Documentation/devicetree/bindings/memory-controllers/nvidia,tegra30-mc.txt
>
> Reviewed-by: Rob Herring <robh@kernel.org>

Wrong patch, but still R-by... :)

^ permalink raw reply	[flat|nested] 27+ messages in thread

* Re: [PATCH v10 10/15] dt-bindings: memory: Add binding for NVIDIA Tegra30 Memory Controller
  2019-08-11 21:00 ` [PATCH v10 10/15] dt-bindings: memory: Add binding for NVIDIA Tegra30 Memory Controller Dmitry Osipenko
@ 2019-08-12 19:55   ` Rob Herring
  0 siblings, 0 replies; 27+ messages in thread
From: Rob Herring @ 2019-08-12 19:55 UTC (permalink / raw)
  To: Dmitry Osipenko
  Cc: Michael Turquette, Joseph Lo, Thierry Reding, Jonathan Hunter,
	Peter De Schrijver, Prashant Gaikwad, Stephen Boyd, devicetree,
	linux-clk, linux-tegra, linux-kernel

On Sun, Aug 11, 2019 at 3:01 PM Dmitry Osipenko <digetx@gmail.com> wrote:
>
> Add binding for the NVIDIA Tegra30 SoC Memory Controller.
>
> Signed-off-by: Dmitry Osipenko <digetx@gmail.com>
> ---
>  .../memory-controllers/nvidia,tegra30-mc.yaml | 167 ++++++++++++++++++
>  1 file changed, 167 insertions(+)
>  create mode 100644 Documentation/devicetree/bindings/memory-controllers/nvidia,tegra30-mc.yaml

Reviewed-by: Rob Herring <robh@kernel.org>

^ permalink raw reply	[flat|nested] 27+ messages in thread

* Re: [PATCH v10 11/15] dt-bindings: memory: Add binding for NVIDIA Tegra30 External Memory Controller
  2019-08-11 21:00 ` [PATCH v10 11/15] dt-bindings: memory: Add binding for NVIDIA Tegra30 External " Dmitry Osipenko
@ 2019-08-12 19:56   ` Rob Herring
  0 siblings, 0 replies; 27+ messages in thread
From: Rob Herring @ 2019-08-12 19:56 UTC (permalink / raw)
  To: Dmitry Osipenko
  Cc: Michael Turquette, Joseph Lo, Thierry Reding, Jonathan Hunter,
	Peter De Schrijver, Prashant Gaikwad, Stephen Boyd, devicetree,
	linux-clk, linux-tegra, linux-kernel

On Sun, Aug 11, 2019 at 3:01 PM Dmitry Osipenko <digetx@gmail.com> wrote:
>
> Add device-tree binding for NVIDIA Tegra30 External Memory Controller.
> The binding is based on the Tegra124 EMC binding since hardware is
> similar, although there are couple significant differences.
>
> Note that the memory timing description is given in a platform-specific
> form because there is no detailed information on how to convert a
> typical-common DDR timing into the register values. The timing format is
> borrowed from downstream kernel, hence there is no hurdle in regards to
> upstreaming of memory timings for the boards.
>
> Acked-by: Peter De Schrijver <pdeschrijver@nvidia.com>
> Signed-off-by: Dmitry Osipenko <digetx@gmail.com>
> ---
>  .../nvidia,tegra30-emc.yaml                   | 336 ++++++++++++++++++
>  1 file changed, 336 insertions(+)
>  create mode 100644 Documentation/devicetree/bindings/memory-controllers/nvidia,tegra30-emc.yaml

Reviewed-by: Rob Herring <robh@kernel.org>

^ permalink raw reply	[flat|nested] 27+ messages in thread

* Re: [PATCH v10 09/15] dt-bindings: memory: tegra30: Convert to Tegra124 YAML
  2019-08-12 19:54     ` Rob Herring
@ 2019-08-12 20:19       ` Dmitry Osipenko
  0 siblings, 0 replies; 27+ messages in thread
From: Dmitry Osipenko @ 2019-08-12 20:19 UTC (permalink / raw)
  To: Rob Herring
  Cc: Michael Turquette, Joseph Lo, Thierry Reding, Jonathan Hunter,
	Peter De Schrijver, Prashant Gaikwad, Stephen Boyd, devicetree,
	linux-clk, linux-tegra, linux-kernel

12.08.2019 22:54, Rob Herring пишет:
> On Mon, Aug 12, 2019 at 1:53 PM Rob Herring <robh+dt@kernel.org> wrote:
>>
>> On Sun, Aug 11, 2019 at 3:01 PM Dmitry Osipenko <digetx@gmail.com> wrote:
>>>
>>> The Tegra30 binding will actually differ from the Tegra124 a tad, in
>>> particular the EMEM configuration description. Hence rename the binding
>>> to Tegra124 during of the conversion to YAML.
>>>
>>> Reviewed-by: Rob Herring <robh@kernel.org>
>>> Signed-off-by: Dmitry Osipenko <digetx@gmail.com>
>>> ---
>>>  .../nvidia,tegra124-mc.yaml                   | 152 ++++++++++++++++++
>>>  .../memory-controllers/nvidia,tegra30-mc.txt  | 123 --------------
>>>  2 files changed, 152 insertions(+), 123 deletions(-)
>>>  create mode 100644 Documentation/devicetree/bindings/memory-controllers/nvidia,tegra124-mc.yaml
>>>  delete mode 100644 Documentation/devicetree/bindings/memory-controllers/nvidia,tegra30-mc.txt
>>
>> Reviewed-by: Rob Herring <robh@kernel.org>
> 
> Wrong patch, but still R-by... :)
> 

Thanks!

^ permalink raw reply	[flat|nested] 27+ messages in thread

* Re: [PATCH v10 01/15] clk: tegra20/30: Add custom EMC clock implementation
  2019-08-11 21:00 ` [PATCH v10 01/15] clk: tegra20/30: Add custom EMC clock implementation Dmitry Osipenko
@ 2019-08-12 23:12   ` Michał Mirosław
  2019-08-13  2:36     ` Dmitry Osipenko
  2019-09-10 10:33   ` Stephen Boyd
  1 sibling, 1 reply; 27+ messages in thread
From: Michał Mirosław @ 2019-08-12 23:12 UTC (permalink / raw)
  To: Dmitry Osipenko
  Cc: Rob Herring, Michael Turquette, Joseph Lo, Thierry Reding,
	Jonathan Hunter, Peter De Schrijver, Prashant Gaikwad,
	Stephen Boyd, devicetree, linux-clk, linux-tegra, linux-kernel

On Mon, Aug 12, 2019 at 12:00:29AM +0300, Dmitry Osipenko wrote:
> A proper External Memory Controller clock rounding and parent selection
> functionality is required by the EMC drivers, it is not available using
> the generic clock implementation because only the Memory Controller driver
> is aware of what clock rates are actually available for a particular
> device. EMC drivers will have to register a Tegra-specific CLK-API
> callback which will perform rounding of a requested rate. EMC clock users
> won't be able to request EMC clock by getting -EPROBE_DEFER until EMC
> driver is probed and the callback is set up.
[...]
> diff --git a/drivers/clk/tegra/Makefile b/drivers/clk/tegra/Makefile
> index 4812e45c2214..df966ca06788 100644
> --- a/drivers/clk/tegra/Makefile
> +++ b/drivers/clk/tegra/Makefile
> @@ -17,7 +17,9 @@ obj-y					+= clk-tegra-fixed.o
>  obj-y					+= clk-tegra-super-gen4.o
>  obj-$(CONFIG_TEGRA_CLK_EMC)		+= clk-emc.o
>  obj-$(CONFIG_ARCH_TEGRA_2x_SOC)         += clk-tegra20.o
> +obj-$(CONFIG_ARCH_TEGRA_2x_SOC)		+= clk-tegra20-emc.o
>  obj-$(CONFIG_ARCH_TEGRA_3x_SOC)         += clk-tegra30.o
> +obj-$(CONFIG_ARCH_TEGRA_3x_SOC)		+= clk-tegra20-emc.o
>  obj-$(CONFIG_ARCH_TEGRA_114_SOC)	+= clk-tegra114.o
>  obj-$(CONFIG_ARCH_TEGRA_124_SOC)	+= clk-tegra124.o
>  obj-$(CONFIG_TEGRA_CLK_DFLL)		+= clk-tegra124-dfll-fcpu.o

Doesn't it complain when both CONFIG_ARCH_TEGRA_2x_SOC and
CONFIG_ARCH_TEGRA_3x_SOC are enabled at the same time?

Best Regards,
Micha³ Miros³aw

^ permalink raw reply	[flat|nested] 27+ messages in thread

* Re: [PATCH v10 01/15] clk: tegra20/30: Add custom EMC clock implementation
  2019-08-12 23:12   ` Michał Mirosław
@ 2019-08-13  2:36     ` Dmitry Osipenko
  2019-08-21 16:46       ` Thierry Reding
  0 siblings, 1 reply; 27+ messages in thread
From: Dmitry Osipenko @ 2019-08-13  2:36 UTC (permalink / raw)
  To: Michał Mirosław
  Cc: Rob Herring, Michael Turquette, Joseph Lo, Thierry Reding,
	Jonathan Hunter, Peter De Schrijver, Prashant Gaikwad,
	Stephen Boyd, devicetree, linux-clk, linux-tegra, linux-kernel

13.08.2019 2:12, Michał Mirosław пишет:
> On Mon, Aug 12, 2019 at 12:00:29AM +0300, Dmitry Osipenko wrote:
>> A proper External Memory Controller clock rounding and parent selection
>> functionality is required by the EMC drivers, it is not available using
>> the generic clock implementation because only the Memory Controller driver
>> is aware of what clock rates are actually available for a particular
>> device. EMC drivers will have to register a Tegra-specific CLK-API
>> callback which will perform rounding of a requested rate. EMC clock users
>> won't be able to request EMC clock by getting -EPROBE_DEFER until EMC
>> driver is probed and the callback is set up.
> [...]
>> diff --git a/drivers/clk/tegra/Makefile b/drivers/clk/tegra/Makefile
>> index 4812e45c2214..df966ca06788 100644
>> --- a/drivers/clk/tegra/Makefile
>> +++ b/drivers/clk/tegra/Makefile
>> @@ -17,7 +17,9 @@ obj-y					+= clk-tegra-fixed.o
>>  obj-y					+= clk-tegra-super-gen4.o
>>  obj-$(CONFIG_TEGRA_CLK_EMC)		+= clk-emc.o
>>  obj-$(CONFIG_ARCH_TEGRA_2x_SOC)         += clk-tegra20.o
>> +obj-$(CONFIG_ARCH_TEGRA_2x_SOC)		+= clk-tegra20-emc.o
>>  obj-$(CONFIG_ARCH_TEGRA_3x_SOC)         += clk-tegra30.o
>> +obj-$(CONFIG_ARCH_TEGRA_3x_SOC)		+= clk-tegra20-emc.o
>>  obj-$(CONFIG_ARCH_TEGRA_114_SOC)	+= clk-tegra114.o
>>  obj-$(CONFIG_ARCH_TEGRA_124_SOC)	+= clk-tegra124.o
>>  obj-$(CONFIG_TEGRA_CLK_DFLL)		+= clk-tegra124-dfll-fcpu.o
> 
> Doesn't it complain when both CONFIG_ARCH_TEGRA_2x_SOC and
> CONFIG_ARCH_TEGRA_3x_SOC are enabled at the same time?

No, at least not with my toolchain setup. Are you getting some warning?

^ permalink raw reply	[flat|nested] 27+ messages in thread

* Re: [PATCH v10 01/15] clk: tegra20/30: Add custom EMC clock implementation
  2019-08-13  2:36     ` Dmitry Osipenko
@ 2019-08-21 16:46       ` Thierry Reding
  0 siblings, 0 replies; 27+ messages in thread
From: Thierry Reding @ 2019-08-21 16:46 UTC (permalink / raw)
  To: Dmitry Osipenko
  Cc: Michał Mirosław, Rob Herring, Michael Turquette,
	Joseph Lo, Jonathan Hunter, Peter De Schrijver, Prashant Gaikwad,
	Stephen Boyd, devicetree, linux-clk, linux-tegra, linux-kernel

[-- Attachment #1: Type: text/plain, Size: 1868 bytes --]

On Tue, Aug 13, 2019 at 05:36:41AM +0300, Dmitry Osipenko wrote:
> 13.08.2019 2:12, Michał Mirosław пишет:
> > On Mon, Aug 12, 2019 at 12:00:29AM +0300, Dmitry Osipenko wrote:
> >> A proper External Memory Controller clock rounding and parent selection
> >> functionality is required by the EMC drivers, it is not available using
> >> the generic clock implementation because only the Memory Controller driver
> >> is aware of what clock rates are actually available for a particular
> >> device. EMC drivers will have to register a Tegra-specific CLK-API
> >> callback which will perform rounding of a requested rate. EMC clock users
> >> won't be able to request EMC clock by getting -EPROBE_DEFER until EMC
> >> driver is probed and the callback is set up.
> > [...]
> >> diff --git a/drivers/clk/tegra/Makefile b/drivers/clk/tegra/Makefile
> >> index 4812e45c2214..df966ca06788 100644
> >> --- a/drivers/clk/tegra/Makefile
> >> +++ b/drivers/clk/tegra/Makefile
> >> @@ -17,7 +17,9 @@ obj-y					+= clk-tegra-fixed.o
> >>  obj-y					+= clk-tegra-super-gen4.o
> >>  obj-$(CONFIG_TEGRA_CLK_EMC)		+= clk-emc.o
> >>  obj-$(CONFIG_ARCH_TEGRA_2x_SOC)         += clk-tegra20.o
> >> +obj-$(CONFIG_ARCH_TEGRA_2x_SOC)		+= clk-tegra20-emc.o
> >>  obj-$(CONFIG_ARCH_TEGRA_3x_SOC)         += clk-tegra30.o
> >> +obj-$(CONFIG_ARCH_TEGRA_3x_SOC)		+= clk-tegra20-emc.o
> >>  obj-$(CONFIG_ARCH_TEGRA_114_SOC)	+= clk-tegra114.o
> >>  obj-$(CONFIG_ARCH_TEGRA_124_SOC)	+= clk-tegra124.o
> >>  obj-$(CONFIG_TEGRA_CLK_DFLL)		+= clk-tegra124-dfll-fcpu.o
> > 
> > Doesn't it complain when both CONFIG_ARCH_TEGRA_2x_SOC and
> > CONFIG_ARCH_TEGRA_3x_SOC are enabled at the same time?
> 
> No, at least not with my toolchain setup. Are you getting some warning?

Kbuild actually filters out duplicates to facilitate this kind of
construct.

Thierry

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]

^ permalink raw reply	[flat|nested] 27+ messages in thread

* Re: [PATCH v10 01/15] clk: tegra20/30: Add custom EMC clock implementation
  2019-08-11 21:00 ` [PATCH v10 01/15] clk: tegra20/30: Add custom EMC clock implementation Dmitry Osipenko
  2019-08-12 23:12   ` Michał Mirosław
@ 2019-09-10 10:33   ` Stephen Boyd
  1 sibling, 0 replies; 27+ messages in thread
From: Stephen Boyd @ 2019-09-10 10:33 UTC (permalink / raw)
  To: Dmitry Osipenko, Jonathan Hunter, Joseph Lo, Michael Turquette,
	Peter De Schrijver, Prashant Gaikwad, Rob Herring,
	Thierry Reding
  Cc: devicetree, linux-clk, linux-tegra, linux-kernel

Quoting Dmitry Osipenko (2019-08-11 14:00:29)
> A proper External Memory Controller clock rounding and parent selection
> functionality is required by the EMC drivers, it is not available using
> the generic clock implementation because only the Memory Controller driver
> is aware of what clock rates are actually available for a particular
> device. EMC drivers will have to register a Tegra-specific CLK-API
> callback which will perform rounding of a requested rate. EMC clock users
> won't be able to request EMC clock by getting -EPROBE_DEFER until EMC
> driver is probed and the callback is set up.
> 
> The functionality is somewhat similar to the clk-emc.c which serves
> Tegra124+ SoCs. The later HW generations support more parent clock sources
> and the HW configuration / integration with the EMC drivers differs a tad
> from the older gens, hence it's not really worth to try to squash
> everything into a single source file.
> 
> Acked-by: Peter De Schrijver <pdeschrijver@nvidia.com>
> Signed-off-by: Dmitry Osipenko <digetx@gmail.com>
> ---

Acked-by: Stephen Boyd <sboyd@kernel.org>


^ permalink raw reply	[flat|nested] 27+ messages in thread

* Re: [PATCH v10 12/15] memory: tegra: Introduce Tegra30 EMC driver
  2019-08-11 21:00 ` [PATCH v10 12/15] memory: tegra: Introduce Tegra30 EMC driver Dmitry Osipenko
@ 2019-10-05 16:28   ` Peter Geis
  2019-10-09  8:52     ` Dmitry Osipenko
  0 siblings, 1 reply; 27+ messages in thread
From: Peter Geis @ 2019-10-05 16:28 UTC (permalink / raw)
  To: Dmitry Osipenko
  Cc: Rob Herring, Michael Turquette, Joseph Lo, Thierry Reding,
	Jonathan Hunter, Peter De Schrijver, Prashant Gaikwad,
	Stephen Boyd, devicetree, linux-clk, linux-tegra, linux-kernel

Tested on the Ouya (tegra30).

Tested-by: Peter Geis <pgwipeout@gmail.com>

On Sun, Aug 11, 2019 at 5:02 PM Dmitry Osipenko <digetx@gmail.com> wrote:
>
> Introduce driver for the External Memory Controller (EMC) found on Tegra30
> chips, it 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.
>
> Acked-by: Peter De Schrijver <pdeschrijver@nvidia.com>
> Signed-off-by: Dmitry Osipenko <digetx@gmail.com>
> ---
>  drivers/memory/tegra/Kconfig       |   10 +
>  drivers/memory/tegra/Makefile      |    1 +
>  drivers/memory/tegra/mc.c          |    9 +-
>  drivers/memory/tegra/mc.h          |   30 +-
>  drivers/memory/tegra/tegra30-emc.c | 1232 ++++++++++++++++++++++++++++
>  drivers/memory/tegra/tegra30.c     |   42 +
>  include/soc/tegra/mc.h             |    2 +-
>  7 files changed, 1311 insertions(+), 15 deletions(-)
>  create mode 100644 drivers/memory/tegra/tegra30-emc.c
>
> diff --git a/drivers/memory/tegra/Kconfig b/drivers/memory/tegra/Kconfig
> index 4680124ddcab..fbfbaada61a2 100644
> --- a/drivers/memory/tegra/Kconfig
> +++ b/drivers/memory/tegra/Kconfig
> @@ -17,6 +17,16 @@ config TEGRA20_EMC
>           This driver is required to change memory timings / clock rate for
>           external memory.
>
> +config TEGRA30_EMC
> +       bool "NVIDIA Tegra30 External Memory Controller driver"
> +       default y
> +       depends on TEGRA_MC && ARCH_TEGRA_3x_SOC
> +       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 TEGRA124_EMC
>         bool "NVIDIA Tegra124 External Memory Controller driver"
>         default y
> diff --git a/drivers/memory/tegra/Makefile b/drivers/memory/tegra/Makefile
> index 3971a6b7c487..3d23c4261104 100644
> --- a/drivers/memory/tegra/Makefile
> +++ b/drivers/memory/tegra/Makefile
> @@ -11,5 +11,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_TEGRA30_EMC)  += tegra30-emc.o
>  obj-$(CONFIG_TEGRA124_EMC) += tegra124-emc.o
>  obj-$(CONFIG_ARCH_TEGRA_186_SOC) += tegra186.o
> diff --git a/drivers/memory/tegra/mc.c b/drivers/memory/tegra/mc.c
> index 3d8d322511c5..43819e8df95c 100644
> --- a/drivers/memory/tegra/mc.c
> +++ b/drivers/memory/tegra/mc.c
> @@ -48,9 +48,6 @@
>  #define MC_EMEM_ADR_CFG 0x54
>  #define MC_EMEM_ADR_CFG_EMEM_NUMDEV BIT(0)
>
> -#define MC_TIMING_CONTROL              0xfc
> -#define MC_TIMING_UPDATE               BIT(0)
> -
>  static const struct of_device_id tegra_mc_of_match[] = {
>  #ifdef CONFIG_ARCH_TEGRA_2x_SOC
>         { .compatible = "nvidia,tegra20-mc-gart", .data = &tegra20_mc_soc },
> @@ -307,7 +304,7 @@ static int tegra_mc_setup_latency_allowance(struct tegra_mc *mc)
>         return 0;
>  }
>
> -void tegra_mc_write_emem_configuration(struct tegra_mc *mc, unsigned long rate)
> +int tegra_mc_write_emem_configuration(struct tegra_mc *mc, unsigned long rate)
>  {
>         unsigned int i;
>         struct tegra_mc_timing *timing = NULL;
> @@ -322,11 +319,13 @@ void tegra_mc_write_emem_configuration(struct tegra_mc *mc, unsigned long rate)
>         if (!timing) {
>                 dev_err(mc->dev, "no memory timing registered for rate %lu\n",
>                         rate);
> -               return;
> +               return -EINVAL;
>         }
>
>         for (i = 0; i < mc->soc->num_emem_regs; ++i)
>                 mc_writel(mc, timing->emem_data[i], mc->soc->emem_regs[i]);
> +
> +       return 0;
>  }
>
>  unsigned int tegra_mc_get_emem_device_count(struct tegra_mc *mc)
> diff --git a/drivers/memory/tegra/mc.h b/drivers/memory/tegra/mc.h
> index f9353494b708..410efc4d7e7b 100644
> --- a/drivers/memory/tegra/mc.h
> +++ b/drivers/memory/tegra/mc.h
> @@ -6,20 +6,32 @@
>  #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_INT_DECERR_MTS (1 << 16)
> -#define MC_INT_SECERR_SEC (1 << 13)
> -#define MC_INT_DECERR_VPR (1 << 12)
> -#define MC_INT_INVALID_APB_ASID_UPDATE (1 << 11)
> -#define MC_INT_INVALID_SMMU_PAGE (1 << 10)
> -#define MC_INT_ARBITRATION_EMEM (1 << 9)
> -#define MC_INT_SECURITY_VIOLATION (1 << 8)
> -#define MC_INT_INVALID_GART_PAGE (1 << 7)
> -#define MC_INT_DECERR_EMEM (1 << 6)
> +#define MC_INT_DECERR_MTS                              BIT(16)
> +#define MC_INT_SECERR_SEC                              BIT(13)
> +#define MC_INT_DECERR_VPR                              BIT(12)
> +#define MC_INT_INVALID_APB_ASID_UPDATE                 BIT(11)
> +#define MC_INT_INVALID_SMMU_PAGE                       BIT(10)
> +#define MC_INT_ARBITRATION_EMEM                                BIT(9)
> +#define MC_INT_SECURITY_VIOLATION                      BIT(8)
> +#define MC_INT_INVALID_GART_PAGE                       BIT(7)
> +#define MC_INT_DECERR_EMEM                             BIT(6)
> +
> +#define MC_EMEM_ARB_OUTSTANDING_REQ                    0x94
> +#define MC_EMEM_ARB_OUTSTANDING_REQ_MAX_MASK           0x1ff
> +#define MC_EMEM_ARB_OUTSTANDING_REQ_HOLDOFF_OVERRIDE   BIT(30)
> +#define MC_EMEM_ARB_OUTSTANDING_REQ_LIMIT_ENABLE       BIT(31)
> +
> +#define MC_EMEM_ARB_OVERRIDE                           0xe8
> +#define MC_EMEM_ARB_OVERRIDE_EACK_MASK                 0x3
> +
> +#define MC_TIMING_CONTROL                              0xfc
> +#define MC_TIMING_UPDATE                               BIT(0)
>
>  static inline u32 mc_readl(struct tegra_mc *mc, unsigned long offset)
>  {
> diff --git a/drivers/memory/tegra/tegra30-emc.c b/drivers/memory/tegra/tegra30-emc.c
> new file mode 100644
> index 000000000000..6929980bf907
> --- /dev/null
> +++ b/drivers/memory/tegra/tegra30-emc.c
> @@ -0,0 +1,1232 @@
> +// SPDX-License-Identifier: GPL-2.0+
> +/*
> + * Tegra30 External Memory Controller driver
> + *
> + * Based on downstream driver from NVIDIA and tegra124-emc.c
> + * Copyright (C) 2011-2014 NVIDIA Corporation
> + *
> + * Author: Dmitry Osipenko <digetx@gmail.com>
> + * Copyright (C) 2019 GRATE-DRIVER project
> + */
> +
> +#include <linux/clk.h>
> +#include <linux/clk/tegra.h>
> +#include <linux/completion.h>
> +#include <linux/delay.h>
> +#include <linux/err.h>
> +#include <linux/interrupt.h>
> +#include <linux/io.h>
> +#include <linux/iopoll.h>
> +#include <linux/kernel.h>
> +#include <linux/module.h>
> +#include <linux/of_platform.h>
> +#include <linux/platform_device.h>
> +#include <linux/sort.h>
> +#include <linux/types.h>
> +
> +#include <soc/tegra/fuse.h>
> +
> +#include "mc.h"
> +
> +#define EMC_INTSTATUS                          0x000
> +#define EMC_INTMASK                            0x004
> +#define EMC_DBG                                        0x008
> +#define EMC_CFG                                        0x00c
> +#define EMC_REFCTRL                            0x020
> +#define EMC_TIMING_CONTROL                     0x028
> +#define EMC_RC                                 0x02c
> +#define EMC_RFC                                        0x030
> +#define EMC_RAS                                        0x034
> +#define EMC_RP                                 0x038
> +#define EMC_R2W                                        0x03c
> +#define EMC_W2R                                        0x040
> +#define EMC_R2P                                        0x044
> +#define EMC_W2P                                        0x048
> +#define EMC_RD_RCD                             0x04c
> +#define EMC_WR_RCD                             0x050
> +#define EMC_RRD                                        0x054
> +#define EMC_REXT                               0x058
> +#define EMC_WDV                                        0x05c
> +#define EMC_QUSE                               0x060
> +#define EMC_QRST                               0x064
> +#define EMC_QSAFE                              0x068
> +#define EMC_RDV                                        0x06c
> +#define EMC_REFRESH                            0x070
> +#define EMC_BURST_REFRESH_NUM                  0x074
> +#define EMC_PDEX2WR                            0x078
> +#define EMC_PDEX2RD                            0x07c
> +#define EMC_PCHG2PDEN                          0x080
> +#define EMC_ACT2PDEN                           0x084
> +#define EMC_AR2PDEN                            0x088
> +#define EMC_RW2PDEN                            0x08c
> +#define EMC_TXSR                               0x090
> +#define EMC_TCKE                               0x094
> +#define EMC_TFAW                               0x098
> +#define EMC_TRPAB                              0x09c
> +#define EMC_TCLKSTABLE                         0x0a0
> +#define EMC_TCLKSTOP                           0x0a4
> +#define EMC_TREFBW                             0x0a8
> +#define EMC_QUSE_EXTRA                         0x0ac
> +#define EMC_ODT_WRITE                          0x0b0
> +#define EMC_ODT_READ                           0x0b4
> +#define EMC_WEXT                               0x0b8
> +#define EMC_CTT                                        0x0bc
> +#define EMC_MRS_WAIT_CNT                       0x0c8
> +#define EMC_MRS                                        0x0cc
> +#define EMC_EMRS                               0x0d0
> +#define EMC_SELF_REF                           0x0e0
> +#define EMC_MRW                                        0x0e8
> +#define EMC_XM2DQSPADCTRL3                     0x0f8
> +#define EMC_FBIO_SPARE                         0x100
> +#define EMC_FBIO_CFG5                          0x104
> +#define EMC_FBIO_CFG6                          0x114
> +#define EMC_CFG_RSV                            0x120
> +#define EMC_AUTO_CAL_CONFIG                    0x2a4
> +#define EMC_AUTO_CAL_INTERVAL                  0x2a8
> +#define EMC_AUTO_CAL_STATUS                    0x2ac
> +#define EMC_STATUS                             0x2b4
> +#define EMC_CFG_2                              0x2b8
> +#define EMC_CFG_DIG_DLL                                0x2bc
> +#define EMC_CFG_DIG_DLL_PERIOD                 0x2c0
> +#define EMC_CTT_DURATION                       0x2d8
> +#define EMC_CTT_TERM_CTRL                      0x2dc
> +#define EMC_ZCAL_INTERVAL                      0x2e0
> +#define EMC_ZCAL_WAIT_CNT                      0x2e4
> +#define EMC_ZQ_CAL                             0x2ec
> +#define EMC_XM2CMDPADCTRL                      0x2f0
> +#define EMC_XM2DQSPADCTRL2                     0x2fc
> +#define EMC_XM2DQPADCTRL2                      0x304
> +#define EMC_XM2CLKPADCTRL                      0x308
> +#define EMC_XM2COMPPADCTRL                     0x30c
> +#define EMC_XM2VTTGENPADCTRL                   0x310
> +#define EMC_XM2VTTGENPADCTRL2                  0x314
> +#define EMC_XM2QUSEPADCTRL                     0x318
> +#define EMC_DLL_XFORM_DQS0                     0x328
> +#define EMC_DLL_XFORM_DQS1                     0x32c
> +#define EMC_DLL_XFORM_DQS2                     0x330
> +#define EMC_DLL_XFORM_DQS3                     0x334
> +#define EMC_DLL_XFORM_DQS4                     0x338
> +#define EMC_DLL_XFORM_DQS5                     0x33c
> +#define EMC_DLL_XFORM_DQS6                     0x340
> +#define EMC_DLL_XFORM_DQS7                     0x344
> +#define EMC_DLL_XFORM_QUSE0                    0x348
> +#define EMC_DLL_XFORM_QUSE1                    0x34c
> +#define EMC_DLL_XFORM_QUSE2                    0x350
> +#define EMC_DLL_XFORM_QUSE3                    0x354
> +#define EMC_DLL_XFORM_QUSE4                    0x358
> +#define EMC_DLL_XFORM_QUSE5                    0x35c
> +#define EMC_DLL_XFORM_QUSE6                    0x360
> +#define EMC_DLL_XFORM_QUSE7                    0x364
> +#define EMC_DLL_XFORM_DQ0                      0x368
> +#define EMC_DLL_XFORM_DQ1                      0x36c
> +#define EMC_DLL_XFORM_DQ2                      0x370
> +#define EMC_DLL_XFORM_DQ3                      0x374
> +#define EMC_DLI_TRIM_TXDQS0                    0x3a8
> +#define EMC_DLI_TRIM_TXDQS1                    0x3ac
> +#define EMC_DLI_TRIM_TXDQS2                    0x3b0
> +#define EMC_DLI_TRIM_TXDQS3                    0x3b4
> +#define EMC_DLI_TRIM_TXDQS4                    0x3b8
> +#define EMC_DLI_TRIM_TXDQS5                    0x3bc
> +#define EMC_DLI_TRIM_TXDQS6                    0x3c0
> +#define EMC_DLI_TRIM_TXDQS7                    0x3c4
> +#define EMC_STALL_THEN_EXE_BEFORE_CLKCHANGE    0x3c8
> +#define EMC_STALL_THEN_EXE_AFTER_CLKCHANGE     0x3cc
> +#define EMC_UNSTALL_RW_AFTER_CLKCHANGE         0x3d0
> +#define EMC_SEL_DPD_CTRL                       0x3d8
> +#define EMC_PRE_REFRESH_REQ_CNT                        0x3dc
> +#define EMC_DYN_SELF_REF_CONTROL               0x3e0
> +#define EMC_TXSRDLL                            0x3e4
> +
> +#define EMC_STATUS_TIMING_UPDATE_STALLED       BIT(23)
> +
> +#define EMC_MODE_SET_DLL_RESET                 BIT(8)
> +#define EMC_MODE_SET_LONG_CNT                  BIT(26)
> +
> +#define EMC_SELF_REF_CMD_ENABLED               BIT(0)
> +
> +#define DRAM_DEV_SEL_ALL                       (0 << 30)
> +#define DRAM_DEV_SEL_0                         (2 << 30)
> +#define DRAM_DEV_SEL_1                         (1 << 30)
> +#define DRAM_BROADCAST(num) \
> +       ((num) > 1 ? DRAM_DEV_SEL_ALL : DRAM_DEV_SEL_0)
> +
> +#define EMC_ZQ_CAL_CMD                         BIT(0)
> +#define EMC_ZQ_CAL_LONG                                BIT(4)
> +#define EMC_ZQ_CAL_LONG_CMD_DEV0 \
> +       (DRAM_DEV_SEL_0 | EMC_ZQ_CAL_LONG | EMC_ZQ_CAL_CMD)
> +#define EMC_ZQ_CAL_LONG_CMD_DEV1 \
> +       (DRAM_DEV_SEL_1 | EMC_ZQ_CAL_LONG | EMC_ZQ_CAL_CMD)
> +
> +#define EMC_DBG_READ_MUX_ASSEMBLY              BIT(0)
> +#define EMC_DBG_WRITE_MUX_ACTIVE               BIT(1)
> +#define EMC_DBG_FORCE_UPDATE                   BIT(2)
> +#define EMC_DBG_CFG_PRIORITY                   BIT(24)
> +
> +#define EMC_CFG5_QUSE_MODE_SHIFT               13
> +#define EMC_CFG5_QUSE_MODE_MASK                        (7 << EMC_CFG5_QUSE_MODE_SHIFT)
> +
> +#define EMC_CFG5_QUSE_MODE_INTERNAL_LPBK       2
> +#define EMC_CFG5_QUSE_MODE_PULSE_INTERN                3
> +
> +#define EMC_SEL_DPD_CTRL_QUSE_DPD_ENABLE       BIT(9)
> +
> +#define EMC_XM2COMPPADCTRL_VREF_CAL_ENABLE     BIT(10)
> +
> +#define EMC_XM2QUSEPADCTRL_IVREF_ENABLE                BIT(4)
> +
> +#define EMC_XM2DQSPADCTRL2_VREF_ENABLE         BIT(5)
> +#define EMC_XM2DQSPADCTRL3_VREF_ENABLE         BIT(5)
> +
> +#define EMC_AUTO_CAL_STATUS_ACTIVE             BIT(31)
> +
> +#define        EMC_FBIO_CFG5_DRAM_TYPE_MASK            0x3
> +
> +#define EMC_MRS_WAIT_CNT_SHORT_WAIT_MASK       0x3ff
> +#define EMC_MRS_WAIT_CNT_LONG_WAIT_SHIFT       16
> +#define EMC_MRS_WAIT_CNT_LONG_WAIT_MASK \
> +       (0x3ff << EMC_MRS_WAIT_CNT_LONG_WAIT_SHIFT)
> +
> +#define EMC_REFCTRL_DEV_SEL_MASK               0x3
> +#define EMC_REFCTRL_ENABLE                     BIT(31)
> +#define EMC_REFCTRL_ENABLE_ALL(num) \
> +       (((num) > 1 ? 0 : 2) | EMC_REFCTRL_ENABLE)
> +#define EMC_REFCTRL_DISABLE_ALL(num)           ((num) > 1 ? 0 : 2)
> +
> +#define EMC_CFG_PERIODIC_QRST                  BIT(21)
> +#define EMC_CFG_DYN_SREF_ENABLE                        BIT(28)
> +
> +#define EMC_CLKCHANGE_REQ_ENABLE               BIT(0)
> +#define EMC_CLKCHANGE_PD_ENABLE                        BIT(1)
> +#define EMC_CLKCHANGE_SR_ENABLE                        BIT(2)
> +
> +#define EMC_TIMING_UPDATE                      BIT(0)
> +
> +#define EMC_REFRESH_OVERFLOW_INT               BIT(3)
> +#define EMC_CLKCHANGE_COMPLETE_INT             BIT(4)
> +
> +enum emc_dram_type {
> +       DRAM_TYPE_DDR3,
> +       DRAM_TYPE_DDR1,
> +       DRAM_TYPE_LPDDR2,
> +       DRAM_TYPE_DDR2,
> +};
> +
> +enum emc_dll_change {
> +       DLL_CHANGE_NONE,
> +       DLL_CHANGE_ON,
> +       DLL_CHANGE_OFF
> +};
> +
> +static const u16 emc_timing_registers[] = {
> +       [0] = EMC_RC,
> +       [1] = EMC_RFC,
> +       [2] = EMC_RAS,
> +       [3] = EMC_RP,
> +       [4] = EMC_R2W,
> +       [5] = EMC_W2R,
> +       [6] = EMC_R2P,
> +       [7] = EMC_W2P,
> +       [8] = EMC_RD_RCD,
> +       [9] = EMC_WR_RCD,
> +       [10] = EMC_RRD,
> +       [11] = EMC_REXT,
> +       [12] = EMC_WEXT,
> +       [13] = EMC_WDV,
> +       [14] = EMC_QUSE,
> +       [15] = EMC_QRST,
> +       [16] = EMC_QSAFE,
> +       [17] = EMC_RDV,
> +       [18] = EMC_REFRESH,
> +       [19] = EMC_BURST_REFRESH_NUM,
> +       [20] = EMC_PRE_REFRESH_REQ_CNT,
> +       [21] = EMC_PDEX2WR,
> +       [22] = EMC_PDEX2RD,
> +       [23] = EMC_PCHG2PDEN,
> +       [24] = EMC_ACT2PDEN,
> +       [25] = EMC_AR2PDEN,
> +       [26] = EMC_RW2PDEN,
> +       [27] = EMC_TXSR,
> +       [28] = EMC_TXSRDLL,
> +       [29] = EMC_TCKE,
> +       [30] = EMC_TFAW,
> +       [31] = EMC_TRPAB,
> +       [32] = EMC_TCLKSTABLE,
> +       [33] = EMC_TCLKSTOP,
> +       [34] = EMC_TREFBW,
> +       [35] = EMC_QUSE_EXTRA,
> +       [36] = EMC_FBIO_CFG6,
> +       [37] = EMC_ODT_WRITE,
> +       [38] = EMC_ODT_READ,
> +       [39] = EMC_FBIO_CFG5,
> +       [40] = EMC_CFG_DIG_DLL,
> +       [41] = EMC_CFG_DIG_DLL_PERIOD,
> +       [42] = EMC_DLL_XFORM_DQS0,
> +       [43] = EMC_DLL_XFORM_DQS1,
> +       [44] = EMC_DLL_XFORM_DQS2,
> +       [45] = EMC_DLL_XFORM_DQS3,
> +       [46] = EMC_DLL_XFORM_DQS4,
> +       [47] = EMC_DLL_XFORM_DQS5,
> +       [48] = EMC_DLL_XFORM_DQS6,
> +       [49] = EMC_DLL_XFORM_DQS7,
> +       [50] = EMC_DLL_XFORM_QUSE0,
> +       [51] = EMC_DLL_XFORM_QUSE1,
> +       [52] = EMC_DLL_XFORM_QUSE2,
> +       [53] = EMC_DLL_XFORM_QUSE3,
> +       [54] = EMC_DLL_XFORM_QUSE4,
> +       [55] = EMC_DLL_XFORM_QUSE5,
> +       [56] = EMC_DLL_XFORM_QUSE6,
> +       [57] = EMC_DLL_XFORM_QUSE7,
> +       [58] = EMC_DLI_TRIM_TXDQS0,
> +       [59] = EMC_DLI_TRIM_TXDQS1,
> +       [60] = EMC_DLI_TRIM_TXDQS2,
> +       [61] = EMC_DLI_TRIM_TXDQS3,
> +       [62] = EMC_DLI_TRIM_TXDQS4,
> +       [63] = EMC_DLI_TRIM_TXDQS5,
> +       [64] = EMC_DLI_TRIM_TXDQS6,
> +       [65] = EMC_DLI_TRIM_TXDQS7,
> +       [66] = EMC_DLL_XFORM_DQ0,
> +       [67] = EMC_DLL_XFORM_DQ1,
> +       [68] = EMC_DLL_XFORM_DQ2,
> +       [69] = EMC_DLL_XFORM_DQ3,
> +       [70] = EMC_XM2CMDPADCTRL,
> +       [71] = EMC_XM2DQSPADCTRL2,
> +       [72] = EMC_XM2DQPADCTRL2,
> +       [73] = EMC_XM2CLKPADCTRL,
> +       [74] = EMC_XM2COMPPADCTRL,
> +       [75] = EMC_XM2VTTGENPADCTRL,
> +       [76] = EMC_XM2VTTGENPADCTRL2,
> +       [77] = EMC_XM2QUSEPADCTRL,
> +       [78] = EMC_XM2DQSPADCTRL3,
> +       [79] = EMC_CTT_TERM_CTRL,
> +       [80] = EMC_ZCAL_INTERVAL,
> +       [81] = EMC_ZCAL_WAIT_CNT,
> +       [82] = EMC_MRS_WAIT_CNT,
> +       [83] = EMC_AUTO_CAL_CONFIG,
> +       [84] = EMC_CTT,
> +       [85] = EMC_CTT_DURATION,
> +       [86] = EMC_DYN_SELF_REF_CONTROL,
> +       [87] = EMC_FBIO_SPARE,
> +       [88] = EMC_CFG_RSV,
> +};
> +
> +struct emc_timing {
> +       unsigned long rate;
> +
> +       u32 data[ARRAY_SIZE(emc_timing_registers)];
> +
> +       u32 emc_auto_cal_interval;
> +       u32 emc_mode_1;
> +       u32 emc_mode_2;
> +       u32 emc_mode_reset;
> +       u32 emc_zcal_cnt_long;
> +       bool emc_cfg_periodic_qrst;
> +       bool emc_cfg_dyn_self_ref;
> +};
> +
> +struct tegra_emc {
> +       struct device *dev;
> +       struct tegra_mc *mc;
> +       struct completion clk_handshake_complete;
> +       struct notifier_block clk_nb;
> +       struct clk *clk;
> +       void __iomem *regs;
> +       unsigned int irq;
> +
> +       struct emc_timing *timings;
> +       unsigned int num_timings;
> +
> +       u32 mc_override;
> +       u32 emc_cfg;
> +
> +       u32 emc_mode_1;
> +       u32 emc_mode_2;
> +       u32 emc_mode_reset;
> +
> +       bool vref_cal_toggle : 1;
> +       bool zcal_long : 1;
> +       bool dll_on : 1;
> +       bool prepared : 1;
> +       bool bad_state : 1;
> +};
> +
> +static irqreturn_t tegra_emc_isr(int irq, void *data)
> +{
> +       struct tegra_emc *emc = data;
> +       u32 intmask = EMC_REFRESH_OVERFLOW_INT | EMC_CLKCHANGE_COMPLETE_INT;
> +       u32 status;
> +
> +       status = readl_relaxed(emc->regs + EMC_INTSTATUS) & intmask;
> +       if (!status)
> +               return IRQ_NONE;
> +
> +       /* notify about EMC-CAR handshake completion */
> +       if (status & EMC_CLKCHANGE_COMPLETE_INT)
> +               complete(&emc->clk_handshake_complete);
> +
> +       /* notify about HW problem */
> +       if (status & EMC_REFRESH_OVERFLOW_INT)
> +               dev_err_ratelimited(emc->dev,
> +                                   "refresh request overflow timeout\n");
> +
> +       /* clear interrupts */
> +       writel_relaxed(status, emc->regs + EMC_INTSTATUS);
> +
> +       return IRQ_HANDLED;
> +}
> +
> +static struct emc_timing *emc_find_timing(struct tegra_emc *emc,
> +                                         unsigned long rate)
> +{
> +       struct emc_timing *timing = NULL;
> +       unsigned int i;
> +
> +       for (i = 0; i < emc->num_timings; i++) {
> +               if (emc->timings[i].rate >= rate) {
> +                       timing = &emc->timings[i];
> +                       break;
> +               }
> +       }
> +
> +       if (!timing) {
> +               dev_err(emc->dev, "no timing for rate %lu\n", rate);
> +               return NULL;
> +       }
> +
> +       return timing;
> +}
> +
> +static bool emc_dqs_preset(struct tegra_emc *emc, struct emc_timing *timing,
> +                          bool *schmitt_to_vref)
> +{
> +       bool preset = false;
> +       u32 val;
> +
> +       if (timing->data[71] & EMC_XM2DQSPADCTRL2_VREF_ENABLE) {
> +               val = readl_relaxed(emc->regs + EMC_XM2DQSPADCTRL2);
> +
> +               if (!(val & EMC_XM2DQSPADCTRL2_VREF_ENABLE)) {
> +                       val |= EMC_XM2DQSPADCTRL2_VREF_ENABLE;
> +                       writel_relaxed(val, emc->regs + EMC_XM2DQSPADCTRL2);
> +
> +                       preset = true;
> +               }
> +       }
> +
> +       if (timing->data[78] & EMC_XM2DQSPADCTRL3_VREF_ENABLE) {
> +               val = readl_relaxed(emc->regs + EMC_XM2DQSPADCTRL3);
> +
> +               if (!(val & EMC_XM2DQSPADCTRL3_VREF_ENABLE)) {
> +                       val |= EMC_XM2DQSPADCTRL3_VREF_ENABLE;
> +                       writel_relaxed(val, emc->regs + EMC_XM2DQSPADCTRL3);
> +
> +                       preset = true;
> +               }
> +       }
> +
> +       if (timing->data[77] & EMC_XM2QUSEPADCTRL_IVREF_ENABLE) {
> +               val = readl_relaxed(emc->regs + EMC_XM2QUSEPADCTRL);
> +
> +               if (!(val & EMC_XM2QUSEPADCTRL_IVREF_ENABLE)) {
> +                       val |= EMC_XM2QUSEPADCTRL_IVREF_ENABLE;
> +                       writel_relaxed(val, emc->regs + EMC_XM2QUSEPADCTRL);
> +
> +                       *schmitt_to_vref = true;
> +                       preset = true;
> +               }
> +       }
> +
> +       return preset;
> +}
> +
> +static int emc_seq_update_timing(struct tegra_emc *emc)
> +{
> +       u32 val;
> +       int err;
> +
> +       writel_relaxed(EMC_TIMING_UPDATE, emc->regs + EMC_TIMING_CONTROL);
> +
> +       err = readl_relaxed_poll_timeout_atomic(emc->regs + EMC_STATUS, val,
> +                               !(val & EMC_STATUS_TIMING_UPDATE_STALLED),
> +                               1, 200);
> +       if (err) {
> +               dev_err(emc->dev, "failed to update timing: %d\n", err);
> +               return err;
> +       }
> +
> +       return 0;
> +}
> +
> +static int emc_prepare_mc_clk_cfg(struct tegra_emc *emc, unsigned long rate)
> +{
> +       struct tegra_mc *mc = emc->mc;
> +       unsigned int misc0_index = 16;
> +       unsigned int i;
> +       bool same;
> +
> +       for (i = 0; i < mc->num_timings; i++) {
> +               if (mc->timings[i].rate != rate)
> +                       continue;
> +
> +               if (mc->timings[i].emem_data[misc0_index] & BIT(27))
> +                       same = true;
> +               else
> +                       same = false;
> +
> +               return tegra20_clk_prepare_emc_mc_same_freq(emc->clk, same);
> +       }
> +
> +       return -EINVAL;
> +}
> +
> +static int emc_prepare_timing_change(struct tegra_emc *emc, unsigned long rate)
> +{
> +       struct emc_timing *timing = emc_find_timing(emc, rate);
> +       enum emc_dll_change dll_change;
> +       enum emc_dram_type dram_type;
> +       bool schmitt_to_vref = false;
> +       unsigned int pre_wait = 0;
> +       bool qrst_used = false;
> +       unsigned int dram_num;
> +       unsigned int i;
> +       u32 fbio_cfg5;
> +       u32 emc_dbg;
> +       u32 val;
> +       int err;
> +
> +       if (!timing || emc->bad_state)
> +               return -EINVAL;
> +
> +       dev_dbg(emc->dev, "%s: using timing rate %lu for requested rate %lu\n",
> +               __func__, timing->rate, rate);
> +
> +       emc->bad_state = true;
> +
> +       err = emc_prepare_mc_clk_cfg(emc, rate);
> +       if (err) {
> +               dev_err(emc->dev, "mc clock preparation failed: %d\n", err);
> +               return err;
> +       }
> +
> +       emc->vref_cal_toggle = false;
> +       emc->mc_override = mc_readl(emc->mc, MC_EMEM_ARB_OVERRIDE);
> +       emc->emc_cfg = readl_relaxed(emc->regs + EMC_CFG);
> +       emc_dbg = readl_relaxed(emc->regs + EMC_DBG);
> +
> +       if (emc->dll_on == !!(timing->emc_mode_1 & 0x1))
> +               dll_change = DLL_CHANGE_NONE;
> +       else if (timing->emc_mode_1 & 0x1)
> +               dll_change = DLL_CHANGE_ON;
> +       else
> +               dll_change = DLL_CHANGE_OFF;
> +
> +       emc->dll_on = !!(timing->emc_mode_1 & 0x1);
> +
> +       if (timing->data[80] && !readl_relaxed(emc->regs + EMC_ZCAL_INTERVAL))
> +               emc->zcal_long = true;
> +       else
> +               emc->zcal_long = false;
> +
> +       fbio_cfg5 = readl_relaxed(emc->regs + EMC_FBIO_CFG5);
> +       dram_type = fbio_cfg5 & EMC_FBIO_CFG5_DRAM_TYPE_MASK;
> +
> +       dram_num = tegra_mc_get_emem_device_count(emc->mc);
> +
> +       /* disable dynamic self-refresh */
> +       if (emc->emc_cfg & EMC_CFG_DYN_SREF_ENABLE) {
> +               emc->emc_cfg &= ~EMC_CFG_DYN_SREF_ENABLE;
> +               writel_relaxed(emc->emc_cfg, emc->regs + EMC_CFG);
> +
> +               pre_wait = 5;
> +       }
> +
> +       /* update MC arbiter settings */
> +       val = mc_readl(emc->mc, MC_EMEM_ARB_OUTSTANDING_REQ);
> +       if (!(val & MC_EMEM_ARB_OUTSTANDING_REQ_HOLDOFF_OVERRIDE) ||
> +           ((val & MC_EMEM_ARB_OUTSTANDING_REQ_MAX_MASK) > 0x50)) {
> +
> +               val = MC_EMEM_ARB_OUTSTANDING_REQ_LIMIT_ENABLE |
> +                     MC_EMEM_ARB_OUTSTANDING_REQ_HOLDOFF_OVERRIDE | 0x50;
> +               mc_writel(emc->mc, val, MC_EMEM_ARB_OUTSTANDING_REQ);
> +               mc_writel(emc->mc, MC_TIMING_UPDATE, MC_TIMING_CONTROL);
> +       }
> +
> +       if (emc->mc_override & MC_EMEM_ARB_OVERRIDE_EACK_MASK)
> +               mc_writel(emc->mc,
> +                         emc->mc_override & ~MC_EMEM_ARB_OVERRIDE_EACK_MASK,
> +                         MC_EMEM_ARB_OVERRIDE);
> +
> +       /* check DQ/DQS VREF delay */
> +       if (emc_dqs_preset(emc, timing, &schmitt_to_vref)) {
> +               if (pre_wait < 3)
> +                       pre_wait = 3;
> +       }
> +
> +       if (pre_wait) {
> +               err = emc_seq_update_timing(emc);
> +               if (err)
> +                       return err;
> +
> +               udelay(pre_wait);
> +       }
> +
> +       /* disable auto-calibration if VREF mode is switching */
> +       if (timing->emc_auto_cal_interval) {
> +               val = readl_relaxed(emc->regs + EMC_XM2COMPPADCTRL);
> +               val ^= timing->data[74];
> +
> +               if (val & EMC_XM2COMPPADCTRL_VREF_CAL_ENABLE) {
> +                       writel_relaxed(0, emc->regs + EMC_AUTO_CAL_INTERVAL);
> +
> +                       err = readl_relaxed_poll_timeout_atomic(
> +                               emc->regs + EMC_AUTO_CAL_STATUS, val,
> +                               !(val & EMC_AUTO_CAL_STATUS_ACTIVE), 1, 300);
> +                       if (err) {
> +                               dev_err(emc->dev,
> +                                       "failed to disable auto-cal: %d\n",
> +                                       err);
> +                               return err;
> +                       }
> +
> +                       emc->vref_cal_toggle = true;
> +               }
> +       }
> +
> +       /* program shadow registers */
> +       for (i = 0; i < ARRAY_SIZE(timing->data); i++) {
> +               /* EMC_XM2CLKPADCTRL should be programmed separately */
> +               if (i != 73)
> +                       writel_relaxed(timing->data[i],
> +                                      emc->regs + emc_timing_registers[i]);
> +       }
> +
> +       err = tegra_mc_write_emem_configuration(emc->mc, timing->rate);
> +       if (err)
> +               return err;
> +
> +       /* DDR3: predict MRS long wait count */
> +       if (dram_type == DRAM_TYPE_DDR3 && dll_change == DLL_CHANGE_ON) {
> +               u32 cnt = 512;
> +
> +               if (emc->zcal_long)
> +                       cnt -= dram_num * 256;
> +
> +               val = timing->data[82] & EMC_MRS_WAIT_CNT_SHORT_WAIT_MASK;
> +               if (cnt < val)
> +                       cnt = val;
> +
> +               val = timing->data[82] & ~EMC_MRS_WAIT_CNT_LONG_WAIT_MASK;
> +               val |= (cnt << EMC_MRS_WAIT_CNT_LONG_WAIT_SHIFT) &
> +                       EMC_MRS_WAIT_CNT_LONG_WAIT_MASK;
> +
> +               writel_relaxed(val, emc->regs + EMC_MRS_WAIT_CNT);
> +       }
> +
> +       /* disable interrupt since read access is prohibited after stalling */
> +       disable_irq(emc->irq);
> +
> +       /* this read also completes the writes */
> +       val = readl_relaxed(emc->regs + EMC_SEL_DPD_CTRL);
> +
> +       if (!(val & EMC_SEL_DPD_CTRL_QUSE_DPD_ENABLE) && schmitt_to_vref) {
> +               u32 cur_mode, new_mode;
> +
> +               cur_mode = fbio_cfg5 & EMC_CFG5_QUSE_MODE_MASK;
> +               cur_mode >>= EMC_CFG5_QUSE_MODE_SHIFT;
> +
> +               new_mode = timing->data[39] & EMC_CFG5_QUSE_MODE_MASK;
> +               new_mode >>= EMC_CFG5_QUSE_MODE_SHIFT;
> +
> +               if ((cur_mode != EMC_CFG5_QUSE_MODE_PULSE_INTERN &&
> +                    cur_mode != EMC_CFG5_QUSE_MODE_INTERNAL_LPBK) ||
> +                   (new_mode != EMC_CFG5_QUSE_MODE_PULSE_INTERN &&
> +                    new_mode != EMC_CFG5_QUSE_MODE_INTERNAL_LPBK))
> +                       qrst_used = true;
> +       }
> +
> +       /* flow control marker 1 */
> +       writel_relaxed(0x1, emc->regs + EMC_STALL_THEN_EXE_BEFORE_CLKCHANGE);
> +
> +       /* enable periodic reset */
> +       if (qrst_used) {
> +               writel_relaxed(emc_dbg | EMC_DBG_WRITE_MUX_ACTIVE,
> +                              emc->regs + EMC_DBG);
> +               writel_relaxed(emc->emc_cfg | EMC_CFG_PERIODIC_QRST,
> +                              emc->regs + EMC_CFG);
> +               writel_relaxed(emc_dbg, emc->regs + EMC_DBG);
> +       }
> +
> +       /* disable auto-refresh to save time after clock change */
> +       writel_relaxed(EMC_REFCTRL_DISABLE_ALL(dram_num),
> +                      emc->regs + EMC_REFCTRL);
> +
> +       /* turn off DLL and enter self-refresh on DDR3 */
> +       if (dram_type == DRAM_TYPE_DDR3) {
> +               if (dll_change == DLL_CHANGE_OFF)
> +                       writel_relaxed(timing->emc_mode_1,
> +                                      emc->regs + EMC_EMRS);
> +
> +               writel_relaxed(DRAM_BROADCAST(dram_num) |
> +                              EMC_SELF_REF_CMD_ENABLED,
> +                              emc->regs + EMC_SELF_REF);
> +       }
> +
> +       /* flow control marker 2 */
> +       writel_relaxed(0x1, emc->regs + EMC_STALL_THEN_EXE_AFTER_CLKCHANGE);
> +
> +       /* enable write-active MUX, update unshadowed pad control */
> +       writel_relaxed(emc_dbg | EMC_DBG_WRITE_MUX_ACTIVE, emc->regs + EMC_DBG);
> +       writel_relaxed(timing->data[73], emc->regs + EMC_XM2CLKPADCTRL);
> +
> +       /* restore periodic QRST and disable write-active MUX */
> +       val = !!(emc->emc_cfg & EMC_CFG_PERIODIC_QRST);
> +       if (qrst_used || timing->emc_cfg_periodic_qrst != val) {
> +               if (timing->emc_cfg_periodic_qrst)
> +                       emc->emc_cfg |= EMC_CFG_PERIODIC_QRST;
> +               else
> +                       emc->emc_cfg &= ~EMC_CFG_PERIODIC_QRST;
> +
> +               writel_relaxed(emc->emc_cfg, emc->regs + EMC_CFG);
> +       }
> +       writel_relaxed(emc_dbg, emc->regs + EMC_DBG);
> +
> +       /* exit self-refresh on DDR3 */
> +       if (dram_type == DRAM_TYPE_DDR3)
> +               writel_relaxed(DRAM_BROADCAST(dram_num),
> +                              emc->regs + EMC_SELF_REF);
> +
> +       /* set DRAM-mode registers */
> +       if (dram_type == DRAM_TYPE_DDR3) {
> +               if (timing->emc_mode_1 != emc->emc_mode_1)
> +                       writel_relaxed(timing->emc_mode_1,
> +                                      emc->regs + EMC_EMRS);
> +
> +               if (timing->emc_mode_2 != emc->emc_mode_2)
> +                       writel_relaxed(timing->emc_mode_2,
> +                                      emc->regs + EMC_EMRS);
> +
> +               if (timing->emc_mode_reset != emc->emc_mode_reset ||
> +                   dll_change == DLL_CHANGE_ON) {
> +                       val = timing->emc_mode_reset;
> +                       if (dll_change == DLL_CHANGE_ON) {
> +                               val |= EMC_MODE_SET_DLL_RESET;
> +                               val |= EMC_MODE_SET_LONG_CNT;
> +                       } else {
> +                               val &= ~EMC_MODE_SET_DLL_RESET;
> +                       }
> +                       writel_relaxed(val, emc->regs + EMC_MRS);
> +               }
> +       } else {
> +               if (timing->emc_mode_2 != emc->emc_mode_2)
> +                       writel_relaxed(timing->emc_mode_2,
> +                                      emc->regs + EMC_MRW);
> +
> +               if (timing->emc_mode_1 != emc->emc_mode_1)
> +                       writel_relaxed(timing->emc_mode_1,
> +                                      emc->regs + EMC_MRW);
> +       }
> +
> +       emc->emc_mode_1 = timing->emc_mode_1;
> +       emc->emc_mode_2 = timing->emc_mode_2;
> +       emc->emc_mode_reset = timing->emc_mode_reset;
> +
> +       /* issue ZCAL command if turning ZCAL on */
> +       if (emc->zcal_long) {
> +               writel_relaxed(EMC_ZQ_CAL_LONG_CMD_DEV0,
> +                              emc->regs + EMC_ZQ_CAL);
> +
> +               if (dram_num > 1)
> +                       writel_relaxed(EMC_ZQ_CAL_LONG_CMD_DEV1,
> +                                      emc->regs + EMC_ZQ_CAL);
> +       }
> +
> +       /* re-enable auto-refresh */
> +       writel_relaxed(EMC_REFCTRL_ENABLE_ALL(dram_num),
> +                      emc->regs + EMC_REFCTRL);
> +
> +       /* flow control marker 3 */
> +       writel_relaxed(0x1, emc->regs + EMC_UNSTALL_RW_AFTER_CLKCHANGE);
> +
> +       reinit_completion(&emc->clk_handshake_complete);
> +
> +       /* interrupt can be re-enabled now */
> +       enable_irq(emc->irq);
> +
> +       emc->bad_state = false;
> +       emc->prepared = true;
> +
> +       return 0;
> +}
> +
> +static int emc_complete_timing_change(struct tegra_emc *emc,
> +                                     unsigned long rate)
> +{
> +       struct emc_timing *timing = emc_find_timing(emc, rate);
> +       unsigned long timeout;
> +       int ret;
> +
> +       timeout = wait_for_completion_timeout(&emc->clk_handshake_complete,
> +                                             msecs_to_jiffies(100));
> +       if (timeout == 0) {
> +               dev_err(emc->dev, "emc-car handshake failed\n");
> +               emc->bad_state = true;
> +               return -EIO;
> +       }
> +
> +       /* restore auto-calibration */
> +       if (emc->vref_cal_toggle)
> +               writel_relaxed(timing->emc_auto_cal_interval,
> +                              emc->regs + EMC_AUTO_CAL_INTERVAL);
> +
> +       /* restore dynamic self-refresh */
> +       if (timing->emc_cfg_dyn_self_ref) {
> +               emc->emc_cfg |= EMC_CFG_DYN_SREF_ENABLE;
> +               writel_relaxed(emc->emc_cfg, emc->regs + EMC_CFG);
> +       }
> +
> +       /* set number of clocks to wait after each ZQ command */
> +       if (emc->zcal_long)
> +               writel_relaxed(timing->emc_zcal_cnt_long,
> +                              emc->regs + EMC_ZCAL_WAIT_CNT);
> +
> +       udelay(2);
> +       /* update restored timing */
> +       ret = emc_seq_update_timing(emc);
> +       if (ret)
> +               emc->bad_state = true;
> +
> +       /* restore early ACK */
> +       mc_writel(emc->mc, emc->mc_override, MC_EMEM_ARB_OVERRIDE);
> +
> +       emc->prepared = false;
> +
> +       return ret;
> +}
> +
> +static int emc_unprepare_timing_change(struct tegra_emc *emc,
> +                                      unsigned long rate)
> +{
> +       if (emc->prepared && !emc->bad_state) {
> +               /* shouldn't ever happen in practice */
> +               dev_err(emc->dev, "timing configuration can't be reverted\n");
> +               emc->bad_state = true;
> +       }
> +
> +       return 0;
> +}
> +
> +static int emc_clk_change_notify(struct notifier_block *nb,
> +                                unsigned long msg, void *data)
> +{
> +       struct tegra_emc *emc = container_of(nb, struct tegra_emc, clk_nb);
> +       struct clk_notifier_data *cnd = data;
> +       int err;
> +
> +       switch (msg) {
> +       case PRE_RATE_CHANGE:
> +               err = emc_prepare_timing_change(emc, cnd->new_rate);
> +               break;
> +
> +       case ABORT_RATE_CHANGE:
> +               err = emc_unprepare_timing_change(emc, cnd->old_rate);
> +               break;
> +
> +       case POST_RATE_CHANGE:
> +               err = emc_complete_timing_change(emc, cnd->new_rate);
> +               break;
> +
> +       default:
> +               return NOTIFY_DONE;
> +       }
> +
> +       return notifier_from_errno(err);
> +}
> +
> +static int load_one_timing_from_dt(struct tegra_emc *emc,
> +                                  struct emc_timing *timing,
> +                                  struct device_node *node)
> +{
> +       u32 value;
> +       int err;
> +
> +       err = of_property_read_u32(node, "clock-frequency", &value);
> +       if (err) {
> +               dev_err(emc->dev, "timing %pOF: failed to read rate: %d\n",
> +                       node, err);
> +               return err;
> +       }
> +
> +       timing->rate = value;
> +
> +       err = of_property_read_u32_array(node, "nvidia,emc-configuration",
> +                                        timing->data,
> +                                        ARRAY_SIZE(emc_timing_registers));
> +       if (err) {
> +               dev_err(emc->dev,
> +                       "timing %pOF: failed to read emc timing data: %d\n",
> +                       node, err);
> +               return err;
> +       }
> +
> +#define EMC_READ_BOOL(prop, dtprop) \
> +       timing->prop = of_property_read_bool(node, dtprop);
> +
> +#define EMC_READ_U32(prop, dtprop) \
> +       err = of_property_read_u32(node, dtprop, &timing->prop); \
> +       if (err) { \
> +               dev_err(emc->dev, \
> +                       "timing %pOFn: failed to read " #prop ": %d\n", \
> +                       node, err); \
> +               return err; \
> +       }
> +
> +       EMC_READ_U32(emc_auto_cal_interval, "nvidia,emc-auto-cal-interval")
> +       EMC_READ_U32(emc_mode_1, "nvidia,emc-mode-1")
> +       EMC_READ_U32(emc_mode_2, "nvidia,emc-mode-2")
> +       EMC_READ_U32(emc_mode_reset, "nvidia,emc-mode-reset")
> +       EMC_READ_U32(emc_zcal_cnt_long, "nvidia,emc-zcal-cnt-long")
> +       EMC_READ_BOOL(emc_cfg_dyn_self_ref, "nvidia,emc-cfg-dyn-self-ref")
> +       EMC_READ_BOOL(emc_cfg_periodic_qrst, "nvidia,emc-cfg-periodic-qrst")
> +
> +#undef EMC_READ_U32
> +#undef EMC_READ_BOOL
> +
> +       dev_dbg(emc->dev, "%s: %pOF: rate %lu\n", __func__, node, timing->rate);
> +
> +       return 0;
> +}
> +
> +static int cmp_timings(const void *_a, const void *_b)
> +{
> +       const struct emc_timing *a = _a;
> +       const struct emc_timing *b = _b;
> +
> +       if (a->rate < b->rate)
> +               return -1;
> +
> +       if (a->rate > b->rate)
> +               return 1;
> +
> +       return 0;
> +}
> +
> +static int emc_check_mc_timings(struct tegra_emc *emc)
> +{
> +       struct tegra_mc *mc = emc->mc;
> +       unsigned int i;
> +
> +       if (emc->num_timings != mc->num_timings) {
> +               dev_err(emc->dev, "emc/mc timings number mismatch: %u %u\n",
> +                       emc->num_timings, mc->num_timings);
> +               return -EINVAL;
> +       }
> +
> +       for (i = 0; i < mc->num_timings; i++) {
> +               if (emc->timings[i].rate != mc->timings[i].rate) {
> +                       dev_err(emc->dev,
> +                               "emc/mc timing rate mismatch: %lu %lu\n",
> +                               emc->timings[i].rate, mc->timings[i].rate);
> +                       return -EINVAL;
> +               }
> +       }
> +
> +       return 0;
> +}
> +
> +static int emc_load_timings_from_dt(struct tegra_emc *emc,
> +                                   struct device_node *node)
> +{
> +       struct device_node *child;
> +       struct emc_timing *timing;
> +       int child_count;
> +       int err;
> +
> +       child_count = of_get_child_count(node);
> +       if (!child_count) {
> +               dev_err(emc->dev, "no memory timings in: %pOF\n", node);
> +               return -EINVAL;
> +       }
> +
> +       emc->timings = devm_kcalloc(emc->dev, child_count, sizeof(*timing),
> +                                   GFP_KERNEL);
> +       if (!emc->timings)
> +               return -ENOMEM;
> +
> +       emc->num_timings = child_count;
> +       timing = emc->timings;
> +
> +       for_each_child_of_node(node, child) {
> +               err = load_one_timing_from_dt(emc, timing++, child);
> +               if (err) {
> +                       of_node_put(child);
> +                       return err;
> +               }
> +       }
> +
> +       sort(emc->timings, emc->num_timings, sizeof(*timing), cmp_timings,
> +            NULL);
> +
> +       err = emc_check_mc_timings(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);
> +
> +       return 0;
> +}
> +
> +static struct device_node *emc_find_node_by_ram_code(struct device *dev)
> +{
> +       struct device_node *np;
> +       u32 value, ram_code;
> +       int err;
> +
> +       ram_code = tegra_read_ram_code();
> +
> +       for_each_child_of_node(dev->of_node, np) {
> +               err = of_property_read_u32(np, "nvidia,ram-code", &value);
> +               if (err || value != ram_code)
> +                       continue;
> +
> +               return np;
> +       }
> +
> +       dev_err(dev, "no memory timings for RAM code %u found in device-tree\n",
> +               ram_code);
> +
> +       return NULL;
> +}
> +
> +static int emc_setup_hw(struct tegra_emc *emc)
> +{
> +       u32 intmask = EMC_REFRESH_OVERFLOW_INT | EMC_CLKCHANGE_COMPLETE_INT;
> +       u32 fbio_cfg5, emc_cfg, emc_dbg;
> +       enum emc_dram_type dram_type;
> +
> +       fbio_cfg5 = readl_relaxed(emc->regs + EMC_FBIO_CFG5);
> +       dram_type = fbio_cfg5 & EMC_FBIO_CFG5_DRAM_TYPE_MASK;
> +
> +       emc_cfg = readl_relaxed(emc->regs + EMC_CFG_2);
> +
> +       /* enable EMC and CAR to handshake on PLL divider/source changes */
> +       emc_cfg |= EMC_CLKCHANGE_REQ_ENABLE;
> +
> +       /* configure clock change mode accordingly to DRAM type */
> +       switch (dram_type) {
> +       case DRAM_TYPE_LPDDR2:
> +               emc_cfg |= EMC_CLKCHANGE_PD_ENABLE;
> +               emc_cfg &= ~EMC_CLKCHANGE_SR_ENABLE;
> +               break;
> +
> +       default:
> +               emc_cfg &= ~EMC_CLKCHANGE_SR_ENABLE;
> +               emc_cfg &= ~EMC_CLKCHANGE_PD_ENABLE;
> +               break;
> +       }
> +
> +       writel_relaxed(emc_cfg, emc->regs + EMC_CFG_2);
> +
> +       /* initialize interrupt */
> +       writel_relaxed(intmask, emc->regs + EMC_INTMASK);
> +       writel_relaxed(0xffffffff, emc->regs + EMC_INTSTATUS);
> +
> +       /* ensure that unwanted debug features are disabled */
> +       emc_dbg = readl_relaxed(emc->regs + EMC_DBG);
> +       emc_dbg |= EMC_DBG_CFG_PRIORITY;
> +       emc_dbg &= ~EMC_DBG_READ_MUX_ASSEMBLY;
> +       emc_dbg &= ~EMC_DBG_WRITE_MUX_ACTIVE;
> +       emc_dbg &= ~EMC_DBG_FORCE_UPDATE;
> +       writel_relaxed(emc_dbg, emc->regs + EMC_DBG);
> +
> +       return 0;
> +}
> +
> +static long emc_round_rate(unsigned long rate,
> +                          unsigned long min_rate,
> +                          unsigned long max_rate,
> +                          void *arg)
> +{
> +       struct emc_timing *timing = NULL;
> +       struct tegra_emc *emc = arg;
> +       unsigned int i;
> +
> +       min_rate = min(min_rate, emc->timings[emc->num_timings - 1].rate);
> +
> +       for (i = 0; i < emc->num_timings; i++) {
> +               if (emc->timings[i].rate < rate && i != emc->num_timings - 1)
> +                       continue;
> +
> +               if (emc->timings[i].rate > max_rate) {
> +                       i = max(i, 1u) - 1;
> +
> +                       if (emc->timings[i].rate < min_rate)
> +                               break;
> +               }
> +
> +               if (emc->timings[i].rate < min_rate)
> +                       continue;
> +
> +               timing = &emc->timings[i];
> +               break;
> +       }
> +
> +       if (!timing) {
> +               dev_err(emc->dev, "no timing for rate %lu min %lu max %lu\n",
> +                       rate, min_rate, max_rate);
> +               return -EINVAL;
> +       }
> +
> +       return timing->rate;
> +}
> +
> +static int tegra_emc_probe(struct platform_device *pdev)
> +{
> +       struct platform_device *mc;
> +       struct device_node *np;
> +       struct tegra_emc *emc;
> +       int err;
> +
> +       if (of_get_child_count(pdev->dev.of_node) == 0) {
> +               dev_info(&pdev->dev,
> +                        "device-tree node doesn't have memory timings\n");
> +               return 0;
> +       }
> +
> +       np = of_parse_phandle(pdev->dev.of_node, "nvidia,memory-controller", 0);
> +       if (!np) {
> +               dev_err(&pdev->dev, "could not get memory controller node\n");
> +               return -ENOENT;
> +       }
> +
> +       mc = of_find_device_by_node(np);
> +       of_node_put(np);
> +       if (!mc)
> +               return -ENOENT;
> +
> +       np = emc_find_node_by_ram_code(&pdev->dev);
> +       if (!np)
> +               return -EINVAL;
> +
> +       emc = devm_kzalloc(&pdev->dev, sizeof(*emc), GFP_KERNEL);
> +       if (!emc) {
> +               of_node_put(np);
> +               return -ENOMEM;
> +       }
> +
> +       emc->mc = platform_get_drvdata(mc);
> +       if (!emc->mc)
> +               return -EPROBE_DEFER;
> +
> +       init_completion(&emc->clk_handshake_complete);
> +       emc->clk_nb.notifier_call = emc_clk_change_notify;
> +       emc->dev = &pdev->dev;
> +
> +       err = emc_load_timings_from_dt(emc, np);
> +       of_node_put(np);
> +       if (err)
> +               return err;
> +
> +       emc->regs = devm_platform_ioremap_resource(pdev, 0);
> +       if (IS_ERR(emc->regs))
> +               return PTR_ERR(emc->regs);
> +
> +       err = emc_setup_hw(emc);
> +       if (err)
> +               return err;
> +
> +       err = platform_get_irq(pdev, 0);
> +       if (err < 0) {
> +               dev_err(&pdev->dev, "interrupt not specified: %d\n", err);
> +               return err;
> +       }
> +       emc->irq = err;
> +
> +       err = devm_request_irq(&pdev->dev, emc->irq, tegra_emc_isr, 0,
> +                              dev_name(&pdev->dev), emc);
> +       if (err) {
> +               dev_err(&pdev->dev, "failed to request irq: %d\n", err);
> +               return err;
> +       }
> +
> +       tegra20_clk_set_emc_round_callback(emc_round_rate, emc);
> +
> +       emc->clk = devm_clk_get(&pdev->dev, "emc");
> +       if (IS_ERR(emc->clk)) {
> +               err = PTR_ERR(emc->clk);
> +               dev_err(&pdev->dev, "failed to get emc clock: %d\n", err);
> +               goto unset_cb;
> +       }
> +
> +       err = clk_notifier_register(emc->clk, &emc->clk_nb);
> +       if (err) {
> +               dev_err(&pdev->dev, "failed to register clk notifier: %d\n",
> +                       err);
> +               goto unset_cb;
> +       }
> +
> +       platform_set_drvdata(pdev, emc);
> +
> +       return 0;
> +
> +unset_cb:
> +       tegra20_clk_set_emc_round_callback(NULL, NULL);
> +
> +       return err;
> +}
> +
> +static int tegra_emc_suspend(struct device *dev)
> +{
> +       struct tegra_emc *emc = dev_get_drvdata(dev);
> +
> +       /*
> +        * Suspending in a bad state will hang machine. The "prepared" var
> +        * shall be always false here unless it's a kernel bug that caused
> +        * suspending in a wrong order.
> +        */
> +       if (WARN_ON(emc->prepared) || emc->bad_state)
> +               return -EINVAL;
> +
> +       emc->bad_state = true;
> +
> +       return 0;
> +}
> +
> +static int tegra_emc_resume(struct device *dev)
> +{
> +       struct tegra_emc *emc = dev_get_drvdata(dev);
> +
> +       emc_setup_hw(emc);
> +       emc->bad_state = false;
> +
> +       return 0;
> +}
> +
> +static const struct dev_pm_ops tegra_emc_pm_ops = {
> +       .suspend = tegra_emc_suspend,
> +       .resume = tegra_emc_resume,
> +};
> +
> +static const struct of_device_id tegra_emc_of_match[] = {
> +       { .compatible = "nvidia,tegra30-emc", },
> +       {},
> +};
> +
> +static struct platform_driver tegra_emc_driver = {
> +       .probe = tegra_emc_probe,
> +       .driver = {
> +               .name = "tegra30-emc",
> +               .of_match_table = tegra_emc_of_match,
> +               .pm = &tegra_emc_pm_ops,
> +               .suppress_bind_attrs = true,
> +       },
> +};
> +
> +static int __init tegra_emc_init(void)
> +{
> +       return platform_driver_register(&tegra_emc_driver);
> +}
> +subsys_initcall(tegra_emc_init);
> diff --git a/drivers/memory/tegra/tegra30.c b/drivers/memory/tegra/tegra30.c
> index 14788fc2f9e8..b1226d3f067f 100644
> --- a/drivers/memory/tegra/tegra30.c
> +++ b/drivers/memory/tegra/tegra30.c
> @@ -10,6 +10,46 @@
>
>  #include "mc.h"
>
> +#define MC_EMEM_ARB_CFG                                0x90
> +#define MC_EMEM_ARB_OUTSTANDING_REQ            0x94
> +#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_DA_TURNS                   0xd0
> +#define MC_EMEM_ARB_DA_COVERS                  0xd4
> +#define MC_EMEM_ARB_MISC0                      0xd8
> +#define MC_EMEM_ARB_RING1_THROTTLE             0xe0
> +
> +static const unsigned long tegra30_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_client tegra30_mc_clients[] = {
>         {
>                 .id = 0x00,
> @@ -994,6 +1034,8 @@ const struct tegra_mc_soc tegra30_mc_soc = {
>         .atom_size = 16,
>         .client_id_mask = 0x7f,
>         .smmu = &tegra30_smmu_soc,
> +       .emem_regs = tegra30_mc_emem_regs,
> +       .num_emem_regs = ARRAY_SIZE(tegra30_mc_emem_regs),
>         .intmask = MC_INT_INVALID_SMMU_PAGE | MC_INT_SECURITY_VIOLATION |
>                    MC_INT_DECERR_EMEM,
>         .reset_ops = &tegra_mc_reset_ops_common,
> diff --git a/include/soc/tegra/mc.h b/include/soc/tegra/mc.h
> index 16e2c2fb5f6c..1238e35653d1 100644
> --- a/include/soc/tegra/mc.h
> +++ b/include/soc/tegra/mc.h
> @@ -181,7 +181,7 @@ struct tegra_mc {
>         spinlock_t lock;
>  };
>
> -void tegra_mc_write_emem_configuration(struct tegra_mc *mc, unsigned long rate);
> +int tegra_mc_write_emem_configuration(struct tegra_mc *mc, unsigned long rate);
>  unsigned int tegra_mc_get_emem_device_count(struct tegra_mc *mc);
>
>  #endif /* __SOC_TEGRA_MC_H__ */
> --
> 2.22.0
>

^ permalink raw reply	[flat|nested] 27+ messages in thread

* Re: [PATCH v10 12/15] memory: tegra: Introduce Tegra30 EMC driver
  2019-10-05 16:28   ` Peter Geis
@ 2019-10-09  8:52     ` Dmitry Osipenko
  0 siblings, 0 replies; 27+ messages in thread
From: Dmitry Osipenko @ 2019-10-09  8:52 UTC (permalink / raw)
  To: Peter Geis, Thierry Reding
  Cc: Rob Herring, Michael Turquette, Joseph Lo, Jonathan Hunter,
	Peter De Schrijver, Prashant Gaikwad, Stephen Boyd, devicetree,
	linux-clk, linux-tegra, linux-kernel

05.10.2019 19:28, Peter Geis пишет:
> Tested on the Ouya (tegra30).
> 
> Tested-by: Peter Geis <pgwipeout@gmail.com>
> 
> On Sun, Aug 11, 2019 at 5:02 PM Dmitry Osipenko <digetx@gmail.com> wrote:
>>
>> Introduce driver for the External Memory Controller (EMC) found on Tegra30
>> chips, it 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.
>>
>> Acked-by: Peter De Schrijver <pdeschrijver@nvidia.com>
>> Signed-off-by: Dmitry Osipenko <digetx@gmail.com>
>> ---

Peter, thank you very much for the testing!

Thierry, could you please pick up this series and other relevant patches
for 5.5? Thanks in advance!

^ permalink raw reply	[flat|nested] 27+ messages in thread

end of thread, back to index

Thread overview: 27+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2019-08-11 21:00 [PATCH v10 00/15] memory: tegra: Introduce Tegra30 EMC driver Dmitry Osipenko
2019-08-11 21:00 ` [PATCH v10 01/15] clk: tegra20/30: Add custom EMC clock implementation Dmitry Osipenko
2019-08-12 23:12   ` Michał Mirosław
2019-08-13  2:36     ` Dmitry Osipenko
2019-08-21 16:46       ` Thierry Reding
2019-09-10 10:33   ` Stephen Boyd
2019-08-11 21:00 ` [PATCH v10 02/15] memory: tegra20-emc: Drop setting EMC rate to max on probe Dmitry Osipenko
2019-08-11 21:00 ` [PATCH v10 03/15] memory: tegra20-emc: Adapt for clock driver changes Dmitry Osipenko
2019-08-11 21:00 ` [PATCH v10 04/15] memory: tegra20-emc: Include io.h instead of iopoll.h Dmitry Osipenko
2019-08-11 21:00 ` [PATCH v10 05/15] memory: tegra20-emc: Pre-configure debug register Dmitry Osipenko
2019-08-11 21:00 ` [PATCH v10 06/15] memory: tegra20-emc: Print a brief info message about the timings Dmitry Osipenko
2019-08-11 21:00 ` [PATCH v10 07/15] memory: tegra20-emc: Increase handshake timeout Dmitry Osipenko
2019-08-11 21:00 ` [PATCH v10 08/15] memory: tegra20-emc: wait_for_completion_timeout() doesn't return error Dmitry Osipenko
2019-08-11 21:00 ` [PATCH v10 09/15] dt-bindings: memory: tegra30: Convert to Tegra124 YAML Dmitry Osipenko
2019-08-12 19:53   ` Rob Herring
2019-08-12 19:54     ` Rob Herring
2019-08-12 20:19       ` Dmitry Osipenko
2019-08-11 21:00 ` [PATCH v10 10/15] dt-bindings: memory: Add binding for NVIDIA Tegra30 Memory Controller Dmitry Osipenko
2019-08-12 19:55   ` Rob Herring
2019-08-11 21:00 ` [PATCH v10 11/15] dt-bindings: memory: Add binding for NVIDIA Tegra30 External " Dmitry Osipenko
2019-08-12 19:56   ` Rob Herring
2019-08-11 21:00 ` [PATCH v10 12/15] memory: tegra: Introduce Tegra30 EMC driver Dmitry Osipenko
2019-10-05 16:28   ` Peter Geis
2019-10-09  8:52     ` Dmitry Osipenko
2019-08-11 21:00 ` [PATCH v10 13/15] memory: tegra: Ensure timing control debug features are disabled Dmitry Osipenko
2019-08-11 21:00 ` [PATCH v10 14/15] memory: tegra: Consolidate registers definition into common header Dmitry Osipenko
2019-08-11 21:00 ` [PATCH v10 15/15] ARM: dts: tegra30: Add External Memory Controller node Dmitry Osipenko

Linux-Clk Archive on lore.kernel.org

Archives are clonable:
	git clone --mirror https://lore.kernel.org/linux-clk/0 linux-clk/git/0.git

	# If you have public-inbox 1.1+ installed, you may
	# initialize and index your mirror using the following commands:
	public-inbox-init -V2 linux-clk linux-clk/ https://lore.kernel.org/linux-clk \
		linux-clk@vger.kernel.org linux-clk@archiver.kernel.org
	public-inbox-index linux-clk

Example config snippet for mirrors

Newsgroup available over NNTP:
	nntp://nntp.lore.kernel.org/org.kernel.vger.linux-clk


AGPL code for this site: git clone https://public-inbox.org/ public-inbox