All of lore.kernel.org
 help / color / mirror / Atom feed
* [RFC 00/14] Tegra210 EMC scaling
@ 2018-09-14 20:02 Peter De Schrijver
  2018-09-14 20:02 ` [RFC 01/14] memory: tegra: mc: Add Tegra210 MC emem registers Peter De Schrijver
                   ` (13 more replies)
  0 siblings, 14 replies; 26+ messages in thread
From: Peter De Schrijver @ 2018-09-14 20:02 UTC (permalink / raw)
  To: vkuruturi, linux-tegra=linux-clk, linux-tegra, thierry.reding,
	jonathanh, devicetree, robh+dt
  Cc: daniel, a.heider, swtcr0, Peter De Schrijver

This series introduces EMC scaling for Tegra210. It's a preliminary version
which hasn't been extensively tested so it may crash your system. The reason
I'm posting it anyway to start discussing the DT binding document. The
problem here is that this binding is also used by firmware which is already
in the field. The firmware needs the data in the DT to perform the initial
training and it also writes the results of this training in the DT which are
then used by this driver. Without this training higher OPPs cannot be used.

Peter De Schrijver (14):
  memory: tegra: mc: Add Tegra210 MC emem registers
  clk: tegra: rename emc timing functions
  clk: tegra: emc: simplify parent matching
  clk: tegra: emc: prepare for Tegra210 parent table
  memory: tegra: mc: Introduce helpers
  memory: tegra: mc: Add support for scaled LA
  memory: tegra: scaled LA register for Tegra210
  clk: tegra: clock changes for emc scaling
  memory: tegra: Add definitions shared by Tegra210 EMC scaling code
  memory: tegra: Add Tegra210 EMC scaling sequence
  memory: tegra: parse DT and costruct timing tables
  memory: tegra: Tegra210 EMC memory driver
  memory: tegra: enable Tegra210 EMC scaling driver
  dt-bindings: tegra: Add Tegra210 EMC binding

 .../memory-controllers/nvidia,tegra210-emc.txt     |  448 ++++
 drivers/clk/tegra/clk-emc.c                        |   88 +-
 drivers/clk/tegra/clk-tegra210.c                   |   53 +-
 drivers/memory/tegra/Kconfig                       |   10 +
 drivers/memory/tegra/Makefile                      |    1 +
 drivers/memory/tegra/mc.c                          |   84 +-
 drivers/memory/tegra/tegra124-emc.c                |    8 +-
 drivers/memory/tegra/tegra210-dt-parse.c           |  363 ++++
 drivers/memory/tegra/tegra210-emc-cc-r21021.c      | 1864 ++++++++++++++++
 drivers/memory/tegra/tegra210-emc-reg.h            | 1879 ++++++++++++++++
 drivers/memory/tegra/tegra210-emc.c                | 2268 ++++++++++++++++++++
 drivers/memory/tegra/tegra210.c                    |   99 +
 include/dt-bindings/clock/tegra210-car.h           |    2 +
 include/soc/tegra/emc.h                            |    8 +-
 include/soc/tegra/mc.h                             |    6 +
 15 files changed, 7119 insertions(+), 62 deletions(-)
 create mode 100644 Documentation/devicetree/bindings/memory-controllers/nvidia,tegra210-emc.txt
 create mode 100644 drivers/memory/tegra/tegra210-dt-parse.c
 create mode 100644 drivers/memory/tegra/tegra210-emc-cc-r21021.c
 create mode 100644 drivers/memory/tegra/tegra210-emc-reg.h
 create mode 100644 drivers/memory/tegra/tegra210-emc.c

-- 
1.9.1

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

* [RFC 01/14] memory: tegra: mc: Add Tegra210 MC emem registers
  2018-09-14 20:02 [RFC 00/14] Tegra210 EMC scaling Peter De Schrijver
@ 2018-09-14 20:02 ` Peter De Schrijver
  2018-09-14 20:02 ` [RFC 02/14] clk: tegra: rename emc timing functions Peter De Schrijver
                   ` (12 subsequent siblings)
  13 siblings, 0 replies; 26+ messages in thread
From: Peter De Schrijver @ 2018-09-14 20:02 UTC (permalink / raw)
  To: vkuruturi, linux-tegra=linux-clk, linux-tegra, thierry.reding,
	jonathanh, devicetree, robh+dt
  Cc: daniel, a.heider, swtcr0, Peter De Schrijver

Signed-off-by: Peter De Schrijver <pdeschrijver@nvidia.com>
---
 drivers/memory/tegra/tegra210.c | 80 +++++++++++++++++++++++++++++++++++++++++
 1 file changed, 80 insertions(+)

diff --git a/drivers/memory/tegra/tegra210.c b/drivers/memory/tegra/tegra210.c
index 5e144ab..aa9dcf0 100644
--- a/drivers/memory/tegra/tegra210.c
+++ b/drivers/memory/tegra/tegra210.c
@@ -15,6 +15,84 @@
 
 #include "mc.h"
 
+#define MC_EMEM_ADR_CFG						0x54
+#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_MISC2					0xc8
+#define MC_EMEM_ARB_RING1_THROTTLE				0xe0
+#define MC_MLL_MPCORER_PTSA_RATE				0x44c
+#define MC_FTOP_PTSA_RATE					0x50c
+#define MC_EMEM_ARB_TIMING_RFCPB				0x6c0
+#define MC_EMEM_ARB_TIMING_CCDMW				0x6c4
+#define MC_EMEM_ARB_REFPB_HP_CTRL				0x6f0
+#define MC_EMEM_ARB_REFPB_BANK_CTRL				0x6f4
+#define MC_PTSA_GRANT_DECREMENT					0x960
+#define MC_EMEM_ARB_DHYST_CTRL					0xbcc
+#define MC_EMEM_ARB_DHYST_TIMEOUT_UTIL_0			0xbd0
+#define MC_EMEM_ARB_DHYST_TIMEOUT_UTIL_1			0xbd4
+#define MC_EMEM_ARB_DHYST_TIMEOUT_UTIL_2			0xbd8
+#define MC_EMEM_ARB_DHYST_TIMEOUT_UTIL_3			0xbdc
+#define MC_EMEM_ARB_DHYST_TIMEOUT_UTIL_4			0xbe0
+#define MC_EMEM_ARB_DHYST_TIMEOUT_UTIL_5			0xbe4
+#define MC_EMEM_ARB_DHYST_TIMEOUT_UTIL_6			0xbe8
+#define MC_EMEM_ARB_DHYST_TIMEOUT_UTIL_7			0xbec
+
+static const unsigned long tegra210_mc_emem_regs[] = {
+	 MC_EMEM_ADR_CFG,
+	 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_MISC2,
+	 MC_EMEM_ARB_RING1_THROTTLE,
+	 MC_MLL_MPCORER_PTSA_RATE,
+	 MC_FTOP_PTSA_RATE,
+	 MC_EMEM_ARB_TIMING_RFCPB,
+	 MC_EMEM_ARB_TIMING_CCDMW,
+	 MC_EMEM_ARB_REFPB_HP_CTRL,
+	 MC_EMEM_ARB_REFPB_BANK_CTRL,
+	 MC_PTSA_GRANT_DECREMENT,
+	 MC_EMEM_ARB_DHYST_CTRL,
+	 MC_EMEM_ARB_DHYST_TIMEOUT_UTIL_0,
+	 MC_EMEM_ARB_DHYST_TIMEOUT_UTIL_1,
+	 MC_EMEM_ARB_DHYST_TIMEOUT_UTIL_2,
+	 MC_EMEM_ARB_DHYST_TIMEOUT_UTIL_3,
+	 MC_EMEM_ARB_DHYST_TIMEOUT_UTIL_4,
+	 MC_EMEM_ARB_DHYST_TIMEOUT_UTIL_5,
+	 MC_EMEM_ARB_DHYST_TIMEOUT_UTIL_6,
+	 MC_EMEM_ARB_DHYST_TIMEOUT_UTIL_7,
+};
+
 static const struct tegra_mc_client tegra210_mc_clients[] = {
 	{
 		.id = 0x00,
@@ -1077,4 +1155,6 @@
 	.atom_size = 64,
 	.client_id_mask = 0xff,
 	.smmu = &tegra210_smmu_soc,
+	.emem_regs = tegra210_mc_emem_regs,
+	.num_emem_regs = ARRAY_SIZE(tegra210_mc_emem_regs),
 };
-- 
1.9.1

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

* [RFC 02/14] clk: tegra: rename emc timing functions
  2018-09-14 20:02 [RFC 00/14] Tegra210 EMC scaling Peter De Schrijver
  2018-09-14 20:02 ` [RFC 01/14] memory: tegra: mc: Add Tegra210 MC emem registers Peter De Schrijver
@ 2018-09-14 20:02 ` Peter De Schrijver
  2018-09-14 20:02 ` [RFC 03/14] clk: tegra: emc: simplify parent matching Peter De Schrijver
                   ` (11 subsequent siblings)
  13 siblings, 0 replies; 26+ messages in thread
From: Peter De Schrijver @ 2018-09-14 20:02 UTC (permalink / raw)
  To: vkuruturi, linux-tegra=linux-clk, linux-tegra, thierry.reding,
	jonathanh, devicetree, robh+dt
  Cc: daniel, a.heider, swtcr0, Peter De Schrijver

Tegra210 emc scaling is similar but sufficiently different to require a new
implementation of the clock driver and the emc timing code. Hence rename
the existing tegra_emc_prepare_timing_change and
tegra_emc_complete_timing_change to tegra124_emc_prepare_timing_change
and tegra124_emc_complete_timing_change because they really only
apply to Tegra124.

Signed-off-by: Peter De Schrijver <pdeschrijver@nvidia.com>
---
 drivers/clk/tegra/clk-emc.c         | 4 ++--
 drivers/memory/tegra/tegra124-emc.c | 8 ++++----
 include/soc/tegra/emc.h             | 8 ++++----
 3 files changed, 10 insertions(+), 10 deletions(-)

diff --git a/drivers/clk/tegra/clk-emc.c b/drivers/clk/tegra/clk-emc.c
index 11a5066..ce41d07 100644
--- a/drivers/clk/tegra/clk-emc.c
+++ b/drivers/clk/tegra/clk-emc.c
@@ -237,7 +237,7 @@ static int emc_set_timing(struct tegra_clk_emc *tegra,
 
 	div = timing->parent_rate / (timing->rate / 2) - 2;
 
-	err = tegra_emc_prepare_timing_change(emc, timing->rate);
+	err = tegra124_emc_prepare_timing_change(emc, timing->rate);
 	if (err)
 		return err;
 
@@ -255,7 +255,7 @@ static int emc_set_timing(struct tegra_clk_emc *tegra,
 
 	spin_unlock_irqrestore(tegra->lock, flags);
 
-	tegra_emc_complete_timing_change(emc, timing->rate);
+	tegra124_emc_complete_timing_change(emc, timing->rate);
 
 	clk_hw_reparent(&tegra->hw, __clk_get_hw(timing->parent));
 	clk_disable_unprepare(tegra->prev_parent);
diff --git a/drivers/memory/tegra/tegra124-emc.c b/drivers/memory/tegra/tegra124-emc.c
index 392dc8d..7140ec8 100644
--- a/drivers/memory/tegra/tegra124-emc.c
+++ b/drivers/memory/tegra/tegra124-emc.c
@@ -562,8 +562,8 @@ static struct emc_timing *tegra_emc_find_timing(struct tegra_emc *emc,
 	return timing;
 }
 
-int tegra_emc_prepare_timing_change(struct tegra_emc *emc,
-				    unsigned long rate)
+int tegra124_emc_prepare_timing_change(struct tegra_emc *emc,
+				       unsigned long rate)
 {
 	struct emc_timing *timing = tegra_emc_find_timing(emc, rate);
 	struct emc_timing *last = &emc->last_timing;
@@ -790,8 +790,8 @@ int tegra_emc_prepare_timing_change(struct tegra_emc *emc,
 	return 0;
 }
 
-void tegra_emc_complete_timing_change(struct tegra_emc *emc,
-				      unsigned long rate)
+void tegra124_emc_complete_timing_change(struct tegra_emc *emc,
+					 unsigned long rate)
 {
 	struct emc_timing *timing = tegra_emc_find_timing(emc, rate);
 	struct emc_timing *last = &emc->last_timing;
diff --git a/include/soc/tegra/emc.h b/include/soc/tegra/emc.h
index f6db33b..625f77d 100644
--- a/include/soc/tegra/emc.h
+++ b/include/soc/tegra/emc.h
@@ -11,9 +11,9 @@
 
 struct tegra_emc;
 
-int tegra_emc_prepare_timing_change(struct tegra_emc *emc,
-				    unsigned long rate);
-void tegra_emc_complete_timing_change(struct tegra_emc *emc,
-				      unsigned long rate);
+int tegra124_emc_prepare_timing_change(struct tegra_emc *emc,
+				       unsigned long rate);
+void tegra124_emc_complete_timing_change(struct tegra_emc *emc,
+					 unsigned long rate);
 
 #endif /* __SOC_TEGRA_EMC_H__ */
-- 
1.9.1

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

* [RFC 03/14] clk: tegra: emc: simplify parent matching
  2018-09-14 20:02 [RFC 00/14] Tegra210 EMC scaling Peter De Schrijver
  2018-09-14 20:02 ` [RFC 01/14] memory: tegra: mc: Add Tegra210 MC emem registers Peter De Schrijver
  2018-09-14 20:02 ` [RFC 02/14] clk: tegra: rename emc timing functions Peter De Schrijver
@ 2018-09-14 20:02 ` Peter De Schrijver
  2018-09-14 20:02 ` [RFC 04/14] clk: tegra: emc: prepare for Tegra210 parent table Peter De Schrijver
                   ` (10 subsequent siblings)
  13 siblings, 0 replies; 26+ messages in thread
From: Peter De Schrijver @ 2018-09-14 20:02 UTC (permalink / raw)
  To: vkuruturi, linux-tegra=linux-clk, linux-tegra, thierry.reding,
	jonathanh, devicetree, robh+dt
  Cc: daniel, a.heider, swtcr0, Peter De Schrijver

We can't change the rate of a parent clock source when that clock source
in use. However pll_m_ud and pll_c_ud are just low jitter paths to the
same clock source, so when comparing clock sources we should treat those
as the same source.

Signed-off-by: Peter De Schrijver <pdeschrijver@nvidia.com>
---
 drivers/clk/tegra/clk-emc.c | 61 ++++++++++++++++++++++++---------------------
 1 file changed, 33 insertions(+), 28 deletions(-)

diff --git a/drivers/clk/tegra/clk-emc.c b/drivers/clk/tegra/clk-emc.c
index ce41d07..e836a9b 100644
--- a/drivers/clk/tegra/clk-emc.c
+++ b/drivers/clk/tegra/clk-emc.c
@@ -49,24 +49,10 @@
 	"pll_c2", "pll_c3", "pll_c_ud"
 };
 
-/*
- * List of clock sources for various parents the EMC clock can have.
- * When we change the timing to a timing with a parent that has the same
- * clock source as the current parent, we must first change to a backup
- * timing that has a different clock source.
- */
-
-#define EMC_SRC_PLL_M 0
-#define EMC_SRC_PLL_C 1
-#define EMC_SRC_PLL_P 2
-#define EMC_SRC_CLK_M 3
-#define EMC_SRC_PLL_C2 4
-#define EMC_SRC_PLL_C3 5
-
-static const char emc_parent_clk_sources[] = {
-	EMC_SRC_PLL_M, EMC_SRC_PLL_C, EMC_SRC_PLL_P, EMC_SRC_CLK_M,
-	EMC_SRC_PLL_M, EMC_SRC_PLL_C2, EMC_SRC_PLL_C3, EMC_SRC_PLL_C
-};
+#define TEGRA124_EMC_PARENT_PLL_M 0
+#define TEGRA124_EMC_PARENT_PLL_M_UD 4
+#define TEGRA124_EMC_PARENT_PLL_C 1
+#define TEGRA124_EMC_PARENT_PLL_C_UD 7
 
 struct emc_timing {
 	unsigned long rate, parent_rate;
@@ -266,6 +252,22 @@ static int emc_set_timing(struct tegra_clk_emc *tegra,
 	return 0;
 }
 
+static int normalize_parent_idx(int parent_idx)
+{
+	switch (parent_idx) {
+	case TEGRA124_EMC_PARENT_PLL_M:
+	case TEGRA124_EMC_PARENT_PLL_M_UD:
+		return TEGRA124_EMC_PARENT_PLL_M;
+
+	case TEGRA124_EMC_PARENT_PLL_C:
+	case TEGRA124_EMC_PARENT_PLL_C_UD:
+		return TEGRA124_EMC_PARENT_PLL_C:;
+
+	default:
+		return parent_idx;
+	}
+}
+
 /*
  * Get backup timing to use as an intermediate step when a change between
  * two timings with the same clock source has been requested. First try to
@@ -275,18 +277,20 @@ static int emc_set_timing(struct tegra_clk_emc *tegra,
 static struct emc_timing *get_backup_timing(struct tegra_clk_emc *tegra,
 					    int timing_index)
 {
-	int i;
+	int i, new_parent_idx;
 	u32 ram_code = tegra_read_ram_code();
 	struct emc_timing *timing;
 
+	new_parent_idx = tegra->timings[timing_index].parent_index;
+	new_parent_idx = normalize_parent_idx(new_parent_idx);
+
 	for (i = timing_index+1; i < tegra->num_timings; i++) {
 		timing = tegra->timings + i;
 		if (timing->ram_code != ram_code)
 			continue;
 
-		if (emc_parent_clk_sources[timing->parent_index] !=
-		    emc_parent_clk_sources[
-		      tegra->timings[timing_index].parent_index])
+		if (normalize_parent_idx(timing->parent_index)
+			!= new_parent_idx)
 			return timing;
 	}
 
@@ -295,9 +299,8 @@ static struct emc_timing *get_backup_timing(struct tegra_clk_emc *tegra,
 		if (timing->ram_code != ram_code)
 			continue;
 
-		if (emc_parent_clk_sources[timing->parent_index] !=
-		    emc_parent_clk_sources[
-		      tegra->timings[timing_index].parent_index])
+		if (normalize_parent_idx(timing->parent_index)
+			!= new_parent_idx)
 			return timing;
 	}
 
@@ -309,7 +312,7 @@ static int emc_set_rate(struct clk_hw *hw, unsigned long rate,
 {
 	struct tegra_clk_emc *tegra;
 	struct emc_timing *timing = NULL;
-	int i, err;
+	int i, err, new_parent_idx, cur_parent_idx;
 	u32 ram_code = tegra_read_ram_code();
 
 	tegra = container_of(hw, struct tegra_clk_emc, hw);
@@ -338,8 +341,10 @@ static int emc_set_rate(struct clk_hw *hw, unsigned long rate,
 		return -EINVAL;
 	}
 
-	if (emc_parent_clk_sources[emc_get_parent(hw)] ==
-	    emc_parent_clk_sources[timing->parent_index] &&
+	cur_parent_idx = normalize_parent_idx(emc_get_parent(hw));
+	new_parent_idx = normalize_parent_idx(timing->parent_index);
+
+	if (cur_parent_idx == new_parent_idx &&
 	    clk_get_rate(timing->parent) != timing->parent_rate) {
 		/*
 		 * Parent clock source not changed but parent rate has changed,
-- 
1.9.1

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

* [RFC 04/14] clk: tegra: emc: prepare for Tegra210 parent table
  2018-09-14 20:02 [RFC 00/14] Tegra210 EMC scaling Peter De Schrijver
                   ` (2 preceding siblings ...)
  2018-09-14 20:02 ` [RFC 03/14] clk: tegra: emc: simplify parent matching Peter De Schrijver
@ 2018-09-14 20:02 ` Peter De Schrijver
  2018-09-14 20:03 ` [RFC 05/14] memory: tegra: mc: Introduce helpers Peter De Schrijver
                   ` (9 subsequent siblings)
  13 siblings, 0 replies; 26+ messages in thread
From: Peter De Schrijver @ 2018-09-14 20:02 UTC (permalink / raw)
  To: vkuruturi, linux-tegra=linux-clk, linux-tegra, thierry.reding,
	jonathanh, devicetree, robh+dt
  Cc: daniel, a.heider, swtcr0, Peter De Schrijver

The set of possible emc parents is different in Tegra124 compared to
Tegra210. Hence make this list a Tegra124 specific table and adjust the
users to allow for other tables.

Signed-off-by: Peter De Schrijver <pdeschrijver@nvidia.com>
---
 drivers/clk/tegra/clk-emc.c | 23 +++++++++++++++--------
 1 file changed, 15 insertions(+), 8 deletions(-)

diff --git a/drivers/clk/tegra/clk-emc.c b/drivers/clk/tegra/clk-emc.c
index e836a9b..84fa806 100644
--- a/drivers/clk/tegra/clk-emc.c
+++ b/drivers/clk/tegra/clk-emc.c
@@ -44,7 +44,7 @@
 #define CLK_SOURCE_EMC_EMC_2X_CLK_SRC(x) (((x) & CLK_SOURCE_EMC_EMC_2X_CLK_SRC_MASK) << \
 					  CLK_SOURCE_EMC_EMC_2X_CLK_SRC_SHIFT)
 
-static const char * const emc_parent_clk_names[] = {
+static const char * const tegra124_emc_parent_clk_names[] = {
 	"pll_m", "pll_c", "pll_p", "clk_m", "pll_m_ud",
 	"pll_c2", "pll_c3", "pll_c_ud"
 };
@@ -376,7 +376,9 @@ static int emc_set_rate(struct clk_hw *hw, unsigned long rate,
 
 static int load_one_timing_from_dt(struct tegra_clk_emc *tegra,
 				   struct emc_timing *timing,
-				   struct device_node *node)
+				   struct device_node *node,
+				   const * char const *parent_names,
+				   int num_parents)
 {
 	int err, i;
 	u32 tmp;
@@ -404,9 +406,9 @@ static int load_one_timing_from_dt(struct tegra_clk_emc *tegra,
 	}
 
 	timing->parent_index = 0xff;
-	for (i = 0; i < ARRAY_SIZE(emc_parent_clk_names); i++) {
-		if (!strcmp(emc_parent_clk_names[i],
-			    __clk_get_name(timing->parent))) {
+	for (i = 0; i < num_parents; i++) {
+		if (!strcmp(parent_names([i],
+			__clk_get_name(timing->parent)))) {
 			timing->parent_index = i;
 			break;
 		}
@@ -436,7 +438,9 @@ static int cmp_timings(const void *_a, const void *_b)
 
 static int load_timings_from_dt(struct tegra_clk_emc *tegra,
 				struct device_node *node,
-				u32 ram_code)
+				u32 ram_code,
+				const * char const *parent_names,
+				int num_parents)
 {
 	struct device_node *child;
 	int child_count = of_get_child_count(node);
@@ -452,7 +456,8 @@ static int load_timings_from_dt(struct tegra_clk_emc *tegra,
 	for_each_child_of_node(node, child) {
 		struct emc_timing *timing = tegra->timings + (i++);
 
-		err = load_one_timing_from_dt(tegra, timing, child);
+		err = load_one_timing_from_dt(tegra, timing, child,
+					      parent_names, num_parents);
 		if (err) {
 			of_node_put(child);
 			return err;
@@ -503,7 +508,9 @@ struct clk *tegra_clk_register_emc(void __iomem *base, struct device_node *np,
 		 * Store timings for all ram codes as we cannot read the
 		 * fuses until the apbmisc driver is loaded.
 		 */
-		err = load_timings_from_dt(tegra, node, node_ram_code);
+		err = load_timings_from_dt(tegra, node, node_ram_code,
+					   tegra124_emc_parents,
+					   ARRAY_SIZE(tegra124_emc_parents));
 		of_node_put(node);
 		if (err)
 			return ERR_PTR(err);
-- 
1.9.1

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

* [RFC 05/14] memory: tegra: mc: Introduce helpers
  2018-09-14 20:02 [RFC 00/14] Tegra210 EMC scaling Peter De Schrijver
                   ` (3 preceding siblings ...)
  2018-09-14 20:02 ` [RFC 04/14] clk: tegra: emc: prepare for Tegra210 parent table Peter De Schrijver
@ 2018-09-14 20:03 ` Peter De Schrijver
  2018-09-14 20:03 ` [RFC 06/14] memory: tegra: mc: Add support for scaled LA Peter De Schrijver
                   ` (8 subsequent siblings)
  13 siblings, 0 replies; 26+ messages in thread
From: Peter De Schrijver @ 2018-09-14 20:03 UTC (permalink / raw)
  To: vkuruturi, linux-tegra=linux-clk, linux-tegra, thierry.reding,
	jonathanh, devicetree, robh+dt
  Cc: daniel, a.heider, swtcr0, Peter De Schrijver

Introduce a few helper functions to lookup a timing for a given rate and
modify MC register field. These will be useful when adding support for
updating the latency allowance (LA) registers when scaling the EMC clock.

Signed-off-by: Peter De Schrijver <pdeschrijver@nvidia.com>
---
 drivers/memory/tegra/mc.c | 30 +++++++++++++++++++++++-------
 1 file changed, 23 insertions(+), 7 deletions(-)

diff --git a/drivers/memory/tegra/mc.c b/drivers/memory/tegra/mc.c
index a4803ac..af4bf29 100644
--- a/drivers/memory/tegra/mc.c
+++ b/drivers/memory/tegra/mc.c
@@ -73,6 +73,17 @@
 };
 MODULE_DEVICE_TABLE(of, tegra_mc_of_match);
 
+static void mc_modifyl(struct tegra_mc *mc, u32 reg, u32 value, u8 shift,
+		       u8 mask)
+{
+	u32 val;
+
+	val = readl(mc->regs + reg);
+	val &= ~(mask << shift);
+	val |= (value & mask) << shift;
+	writel(val, mc->regs + reg);
+}
+
 static int tegra_mc_setup_latency_allowance(struct tegra_mc *mc)
 {
 	unsigned long long tick;
@@ -91,18 +102,14 @@ static int tegra_mc_setup_latency_allowance(struct tegra_mc *mc)
 	/* write latency allowance defaults */
 	for (i = 0; i < mc->soc->num_clients; i++) {
 		const struct tegra_mc_la *la = &mc->soc->clients[i].la;
-		u32 value;
-
-		value = readl(mc->regs + la->reg);
-		value &= ~(la->mask << la->shift);
-		value |= (la->def & la->mask) << la->shift;
-		writel(value, mc->regs + la->reg);
+		mc_modifyl(mc, la->reg, la->def, la->shift, la->mask);
 	}
 
 	return 0;
 }
 
-void tegra_mc_write_emem_configuration(struct tegra_mc *mc, unsigned long rate)
+static struct tegra_mc_timing *find_mc_timing(struct tegra_mc *mc,
+					      unsigned long rate)
 {
 	unsigned int i;
 	struct tegra_mc_timing *timing = NULL;
@@ -114,6 +121,15 @@ void tegra_mc_write_emem_configuration(struct tegra_mc *mc, unsigned long rate)
 		}
 	}
 
+	return timing;
+}
+
+void tegra_mc_write_emem_configuration(struct tegra_mc *mc, unsigned long rate)
+{
+	unsigned int i;
+	struct tegra_mc_timing *timing = NULL;
+
+	timing = find_mc_timing(mc, rate);
 	if (!timing) {
 		dev_err(mc->dev, "no memory timing registered for rate %lu\n",
 			rate);
-- 
1.9.1

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

* [RFC 06/14] memory: tegra: mc: Add support for scaled LA
  2018-09-14 20:02 [RFC 00/14] Tegra210 EMC scaling Peter De Schrijver
                   ` (4 preceding siblings ...)
  2018-09-14 20:03 ` [RFC 05/14] memory: tegra: mc: Introduce helpers Peter De Schrijver
@ 2018-09-14 20:03 ` Peter De Schrijver
  2018-09-14 20:03 ` [RFC 07/14] memory: tegra: scaled LA register for Tegra210 Peter De Schrijver
                   ` (7 subsequent siblings)
  13 siblings, 0 replies; 26+ messages in thread
From: Peter De Schrijver @ 2018-09-14 20:03 UTC (permalink / raw)
  To: vkuruturi, linux-tegra=linux-clk, linux-tegra, thierry.reding,
	jonathanh, devicetree, robh+dt
  Cc: daniel, a.heider, swtcr0, Peter De Schrijver

Besides for each memory client, Tegra210 also has a number of 'scaled latency
allowance' registers. Add support for them.

Signed-off-by: Peter De Schrijver <pdeschrijver@nvidia.com>
---
 drivers/memory/tegra/mc.c | 54 +++++++++++++++++++++++++++++++++++++++++++++++
 include/soc/tegra/mc.h    |  6 ++++++
 2 files changed, 60 insertions(+)

diff --git a/drivers/memory/tegra/mc.c b/drivers/memory/tegra/mc.c
index af4bf29..a7ed8e1 100644
--- a/drivers/memory/tegra/mc.c
+++ b/drivers/memory/tegra/mc.c
@@ -138,6 +138,38 @@ void tegra_mc_write_emem_configuration(struct tegra_mc *mc, unsigned long rate)
 
 	for (i = 0; i < mc->soc->num_emem_regs; ++i)
 		mc_writel(mc, timing->emem_data[i], mc->soc->emem_regs[i]);
+
+}
+
+void tegra_mc_write_scaled_la_configuration(struct tegra_mc *mc,
+					    unsigned long rate)
+{
+	unsigned int i, idx;
+	struct tegra_mc_timing *timing = NULL;
+
+	if (!mc->soc->has_scaled_la)
+		return;
+
+	timing = find_mc_timing(mc, rate);
+	if (!timing) {
+		dev_err(mc->dev, "no memory timing registered for rate %lu\n",
+			rate);
+		return;
+	}
+
+	idx = 0;
+	for (i = 0; i < mc->soc->num_clients; i++) {
+		const struct tegra_mc_la *la = &mc->soc->clients[i].la;
+		mc_modifyl(mc, la->reg, timing->scaled_la_data[idx++],
+			   la->shift, la->mask);
+	}
+
+	for (i = 0; i < mc->soc->num_scaled_la_regs; i++) {
+		mc_modifyl(mc, mc->soc->scaled_la_regs[i],
+			   timing->scaled_la_data[idx++], 0, 0xff);
+		mc_modifyl(mc, mc->soc->scaled_la_regs[i],
+			   timing->scaled_la_data[idx++], 16, 0xff);
+	}
 }
 
 unsigned int tegra_mc_get_emem_device_count(struct tegra_mc *mc)
@@ -181,6 +213,28 @@ static int load_one_timing(struct tegra_mc *mc,
 		return err;
 	}
 
+	if (mc->soc->has_scaled_la) {
+		unsigned int num_scaled_la_regs;
+
+		num_scaled_la_regs = mc->soc->num_clients;
+		/* each scaled LA register has a HI and a LO allowance value */
+		num_scaled_la_regs += mc->soc->num_scaled_la_regs * 2;
+		timing->scaled_la_data = devm_kcalloc(mc->dev,
+			num_scaled_la_regs, sizeof(u8), GFP_KERNEL);
+		if (!timing->scaled_la_data)
+			return -ENOMEM;
+
+		err = of_property_read_u8_array(node,
+				"nvidia,scale-la-configuration",
+				timing->scaled_la_data, num_scaled_la_regs);
+		if (err) {
+			dev_err(mc->dev,
+			 "timing %s: failed to read scaled LA configuration\n",
+			 node->name);
+			return err;
+		}
+	}
+
 	return 0;
 }
 
diff --git a/include/soc/tegra/mc.h b/include/soc/tegra/mc.h
index 44202ff..fce457c 100644
--- a/include/soc/tegra/mc.h
+++ b/include/soc/tegra/mc.h
@@ -24,6 +24,7 @@ struct tegra_mc_timing {
 	unsigned long rate;
 
 	u32 *emem_data;
+	u8 *scaled_la_data;
 };
 
 /* latency allowance */
@@ -93,12 +94,16 @@ struct tegra_mc_soc {
 	const unsigned long *emem_regs;
 	unsigned int num_emem_regs;
 
+	const unsigned long *scaled_la_regs;
+	unsigned int num_scaled_la_regs;
+
 	unsigned int num_address_bits;
 	unsigned int atom_size;
 
 	u8 client_id_mask;
 
 	const struct tegra_smmu_soc *smmu;
+	bool has_scaled_la;
 };
 
 struct tegra_mc {
@@ -117,5 +122,6 @@ struct tegra_mc {
 
 void tegra_mc_write_emem_configuration(struct tegra_mc *mc, unsigned long rate);
 unsigned int tegra_mc_get_emem_device_count(struct tegra_mc *mc);
+void tegra_mc_write_scaled_la_configuration(struct tegra_mc *mc, unsigned long rate);
 
 #endif /* __SOC_TEGRA_MC_H__ */
-- 
1.9.1

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

* [RFC 07/14] memory: tegra: scaled LA register for Tegra210
  2018-09-14 20:02 [RFC 00/14] Tegra210 EMC scaling Peter De Schrijver
                   ` (5 preceding siblings ...)
  2018-09-14 20:03 ` [RFC 06/14] memory: tegra: mc: Add support for scaled LA Peter De Schrijver
@ 2018-09-14 20:03 ` Peter De Schrijver
  2018-09-14 20:03 ` [RFC 08/14] clk: tegra: clock changes for emc scaling Peter De Schrijver
                   ` (6 subsequent siblings)
  13 siblings, 0 replies; 26+ messages in thread
From: Peter De Schrijver @ 2018-09-14 20:03 UTC (permalink / raw)
  To: vkuruturi, linux-tegra=linux-clk, linux-tegra, thierry.reding,
	jonathanh, devicetree, robh+dt
  Cc: daniel, a.heider, swtcr0, Peter De Schrijver

Signed-off-by: Peter De Schrijver <pdeschrijver@nvidia.com>
---
 drivers/memory/tegra/tegra210.c | 19 +++++++++++++++++++
 1 file changed, 19 insertions(+)

diff --git a/drivers/memory/tegra/tegra210.c b/drivers/memory/tegra/tegra210.c
index aa9dcf0..b4dd533 100644
--- a/drivers/memory/tegra/tegra210.c
+++ b/drivers/memory/tegra/tegra210.c
@@ -53,6 +53,13 @@
 #define MC_EMEM_ARB_DHYST_TIMEOUT_UTIL_6			0xbe8
 #define MC_EMEM_ARB_DHYST_TIMEOUT_UTIL_7			0xbec
 
+#define MC_SCALED_LATENCY_ALLOWANCE_DISPLAY0AB			0x694
+#define MC_SCALED_LATENCY_ALLOWANCE_DISPLAY0B			0x698
+#define MC_SCALED_LATENCY_ALLOWANCE_DISPLAY0C			0x6a0
+#define MC_SCALED_LATENCY_ALLOWANCE_DISPLAY0BB			0x69c
+#define MC_SCALED_LATENCY_ALLOWANCE_DISPLAY0CB			0x6a4
+#define MC_SCALED_LATENCY_ALLOWANCE_DISPLAY0A			0x690
+
 static const unsigned long tegra210_mc_emem_regs[] = {
 	 MC_EMEM_ADR_CFG,
 	 MC_EMEM_ARB_CFG,
@@ -93,6 +100,15 @@
 	 MC_EMEM_ARB_DHYST_TIMEOUT_UTIL_7,
 };
 
+static const unsigned long tegra210_scaled_la_regs[] = {
+	 MC_SCALED_LATENCY_ALLOWANCE_DISPLAY0AB,
+	 MC_SCALED_LATENCY_ALLOWANCE_DISPLAY0B,
+	 MC_SCALED_LATENCY_ALLOWANCE_DISPLAY0C,
+	 MC_SCALED_LATENCY_ALLOWANCE_DISPLAY0BB,
+	 MC_SCALED_LATENCY_ALLOWANCE_DISPLAY0CB,
+	 MC_SCALED_LATENCY_ALLOWANCE_DISPLAY0A,
+};
+
 static const struct tegra_mc_client tegra210_mc_clients[] = {
 	{
 		.id = 0x00,
@@ -1157,4 +1173,7 @@
 	.smmu = &tegra210_smmu_soc,
 	.emem_regs = tegra210_mc_emem_regs,
 	.num_emem_regs = ARRAY_SIZE(tegra210_mc_emem_regs),
+	.scaled_la_regs = tegra210_scaled_la_regs,
+	.num_scaled_la_regs = ARRAY_SIZE(tegra210_scaled_la_regs),
+	.has_scaled_la = true,
 };
-- 
1.9.1

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

* [RFC 08/14] clk: tegra: clock changes for emc scaling
  2018-09-14 20:02 [RFC 00/14] Tegra210 EMC scaling Peter De Schrijver
                   ` (6 preceding siblings ...)
  2018-09-14 20:03 ` [RFC 07/14] memory: tegra: scaled LA register for Tegra210 Peter De Schrijver
@ 2018-09-14 20:03 ` Peter De Schrijver
  2018-09-14 20:03 ` [RFC 09/14] memory: tegra: Add definitions shared by Tegra210 EMC scaling code Peter De Schrijver
                   ` (5 subsequent siblings)
  13 siblings, 0 replies; 26+ messages in thread
From: Peter De Schrijver @ 2018-09-14 20:03 UTC (permalink / raw)
  To: vkuruturi, linux-tegra=linux-clk, linux-tegra, thierry.reding,
	jonathanh, devicetree, robh+dt
  Cc: daniel, a.heider, swtcr0, Peter De Schrijver

1) Introduce low jitter paths for pllp and pll_mb used by the EMC scaling code
2) Remove the old emc_mux clock and don't use the common EMC clock definition.
   This will be replaced by a new clock defined in the EMC scaling code
3) Export functions to allow accessing the CAR register required for EMC
   clock scaling. This function will be used to access the CAR register as
   part of the scaling sequence. The scaling sequence may also need to be
   run when the DRAM temperature crosses a certain limit. To avoid
   deadlocks it's easier to keep the whole sequence in one place.

Signed-off-by: Peter De Schrijver <pdeschrijver@nvidia.com>
---
 drivers/clk/tegra/clk-tegra210.c         | 53 ++++++++++++++++++++++++++------
 include/dt-bindings/clock/tegra210-car.h |  2 ++
 2 files changed, 46 insertions(+), 9 deletions(-)

diff --git a/drivers/clk/tegra/clk-tegra210.c b/drivers/clk/tegra/clk-tegra210.c
index 72de0e9..6286b1a 100644
--- a/drivers/clk/tegra/clk-tegra210.c
+++ b/drivers/clk/tegra/clk-tegra210.c
@@ -2368,7 +2368,7 @@ struct utmi_clk_param {
 	[tegra_clk_i2c2] = { .dt_id = TEGRA210_CLK_I2C2, .present = true },
 	[tegra_clk_uartc_8] = { .dt_id = TEGRA210_CLK_UARTC, .present = true },
 	[tegra_clk_mipi_cal] = { .dt_id = TEGRA210_CLK_MIPI_CAL, .present = true },
-	[tegra_clk_emc] = { .dt_id = TEGRA210_CLK_EMC, .present = true },
+	[tegra_clk_emc] = { .dt_id = TEGRA210_CLK_EMC, .present = false },
 	[tegra_clk_usb2] = { .dt_id = TEGRA210_CLK_USB2, .present = true },
 	[tegra_clk_bsev] = { .dt_id = TEGRA210_CLK_BSEV, .present = true },
 	[tegra_clk_uartd_8] = { .dt_id = TEGRA210_CLK_UARTD, .present = true },
@@ -2780,6 +2780,34 @@ void tegra210_put_utmipll_out_iddq(void)
 }
 EXPORT_SYMBOL_GPL(tegra210_put_utmipll_out_iddq);
 
+void tegra210_clk_emc_update_setting(u32 emc_src_value)
+{
+	unsigned long flags = 0;
+
+	spin_lock_irqsave(&emc_lock, flags);
+
+	writel_relaxed(emc_src_value, clk_base + CLK_SOURCE_EMC);
+	readl(clk_base + CLK_SOURCE_EMC);
+
+	spin_unlock_irqrestore(&emc_lock, flags);
+}
+EXPORT_SYMBOL_GPL(tegra210_clk_emc_update_setting);
+
+u32 tegra210_clk_emc_get_setting(void)
+{
+	unsigned long flags = 0;
+	u32 val;
+
+	spin_lock_irqsave(&emc_lock, flags);
+
+	val = readl_relaxed(clk_base + CLK_SOURCE_EMC);
+
+	spin_unlock_irqrestore(&emc_lock, flags);
+
+	return val;
+}
+EXPORT_SYMBOL_GPL(tegra210_clk_emc_get_setting);
+
 static void tegra210_utmi_param_configure(void)
 {
 	u32 reg;
@@ -3016,13 +3044,8 @@ static __init void tegra210_periph_clk_init(void __iomem *clk_base,
 	clk = tegra_clk_register_periph("la", la_parents,
 			ARRAY_SIZE(la_parents), &tegra210_la, clk_base,
 			CLK_SOURCE_LA, 0);
-	/* emc mux */
-	clk = clk_register_mux(NULL, "emc_mux", mux_pllmcp_clkm,
-			       ARRAY_SIZE(mux_pllmcp_clkm), 0,
-			       clk_base + CLK_SOURCE_EMC,
-			       29, 3, 0, &emc_lock);
 
-	clk = tegra_clk_register_mc("mc", "emc_mux", clk_base + CLK_SOURCE_EMC,
+	clk = tegra_clk_register_mc("mc", "emc", clk_base + CLK_SOURCE_EMC,
 				    &emc_lock);
 	clks[TEGRA210_CLK_MC] = clk;
 
@@ -3088,13 +3111,13 @@ static void __init tegra210_pll_init(void __iomem *clk_base,
 
 	/* PLLM */
 	clk = tegra_clk_register_pllm("pll_m", "osc", clk_base, pmc,
-			     CLK_SET_RATE_GATE, &pll_m_params, NULL);
+			     CLK_IS_CRITICAL | CLK_SET_RATE_GATE, &pll_m_params, NULL);
 	clk_register_clkdev(clk, "pll_m", NULL);
 	clks[TEGRA210_CLK_PLL_M] = clk;
 
 	/* PLLMB */
 	clk = tegra_clk_register_pllmb("pll_mb", "osc", clk_base, pmc,
-			     CLK_SET_RATE_GATE, &pll_mb_params, NULL);
+			     CLK_IS_CRITICAL | CLK_SET_RATE_GATE, &pll_mb_params, NULL);
 	clk_register_clkdev(clk, "pll_mb", NULL);
 	clks[TEGRA210_CLK_PLL_MB] = clk;
 
@@ -3104,6 +3127,18 @@ static void __init tegra210_pll_init(void __iomem *clk_base,
 	clk_register_clkdev(clk, "pll_m_ud", NULL);
 	clks[TEGRA210_CLK_PLL_M_UD] = clk;
 
+	/* PLLMB_UD */
+	clk = clk_register_fixed_factor(NULL, "pll_mb_ud", "pll_mb",
+					CLK_SET_RATE_PARENT, 1, 1);
+	clk_register_clkdev(clk, "pll_mb_ud", NULL);
+	clks[TEGRA210_CLK_PLL_MB_UD] = clk;
+
+	/* PLLP_UD */
+	clk = clk_register_fixed_factor(NULL, "pll_p_ud", "pll_p",
+					0, 1, 1);
+	clks[TEGRA210_CLK_PLL_P_UD] = clk;
+
+
 	/* PLLU_VCO */
 	if (!tegra210_init_pllu()) {
 		clk = clk_register_fixed_rate(NULL, "pll_u_vco", "pll_ref", 0,
diff --git a/include/dt-bindings/clock/tegra210-car.h b/include/dt-bindings/clock/tegra210-car.h
index 5df857a..453f814 100644
--- a/include/dt-bindings/clock/tegra210-car.h
+++ b/include/dt-bindings/clock/tegra210-car.h
@@ -396,6 +396,8 @@
 #define TEGRA210_CLK_PLL_M_UD 363
 #define TEGRA210_CLK_PLL_C_UD 364
 #define TEGRA210_CLK_SCLK_MUX 365
+#define TEGRA210_CLK_PLL_MB_UD 366
+#define TEGRA210_CLK_PLL_P_UD 367
 
 #define TEGRA210_CLK_ACLK 370
 
-- 
1.9.1

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

* [RFC 09/14] memory: tegra: Add definitions shared by Tegra210 EMC scaling code
  2018-09-14 20:02 [RFC 00/14] Tegra210 EMC scaling Peter De Schrijver
                   ` (7 preceding siblings ...)
  2018-09-14 20:03 ` [RFC 08/14] clk: tegra: clock changes for emc scaling Peter De Schrijver
@ 2018-09-14 20:03 ` Peter De Schrijver
  2018-09-14 20:03 ` [RFC 10/14] memory: tegra: Add Tegra210 EMC scaling sequence Peter De Schrijver
                   ` (4 subsequent siblings)
  13 siblings, 0 replies; 26+ messages in thread
From: Peter De Schrijver @ 2018-09-14 20:03 UTC (permalink / raw)
  To: vkuruturi, linux-tegra=linux-clk, linux-tegra, thierry.reding,
	jonathanh, devicetree, robh+dt
  Cc: daniel, a.heider, swtcr0, Peter De Schrijver

Signed-off-by: Peter De Schrijver <pdeschrijver@nvidia.com>
---
 drivers/memory/tegra/tegra210-emc-reg.h | 1879 +++++++++++++++++++++++++++++++
 1 file changed, 1879 insertions(+)
 create mode 100644 drivers/memory/tegra/tegra210-emc-reg.h

diff --git a/drivers/memory/tegra/tegra210-emc-reg.h b/drivers/memory/tegra/tegra210-emc-reg.h
new file mode 100644
index 0000000..2bd0be5
--- /dev/null
+++ b/drivers/memory/tegra/tegra210-emc-reg.h
@@ -0,0 +1,1879 @@
+/*
+ * Copyright (c) 2015, NVIDIA CORPORATION.  All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ */
+
+#ifndef _TEGRA210_EMC_REG_H
+#define _TEGRA210_EMC_REG_H
+
+#include <linux/clk.h>
+#include <linux/clk-provider.h>
+#include <linux/of_platform.h>
+
+#define DVFS_FGCG_HIGH_SPEED_THRESHOLD				1000
+#define IOBRICK_DCC_THRESHOLD					2400
+#define DVFS_FGCG_MID_SPEED_THRESHOLD				600
+
+#define TEGRA21_MAX_TABLE_ID_LEN				50
+#define TEGRA_EMC_ISO_USE_FREQ_MAX_NUM				12
+#define PLL_C_DIRECT_FLOOR					333500000
+#define EMC_STATUS_UPDATE_TIMEOUT				1000
+#define TEGRA_EMC_TABLE_MAX_SIZE				16
+
+#define TEGRA_EMC_MODE_REG_17					0x00110000
+#define TEGRA_EMC_MRW_DEV_SHIFT					30
+#define TEGRA_EMC_MRW_DEV1					2
+#define TEGRA_EMC_MRW_DEV2					1
+
+#define EMC_CLK_EMC_2X_CLK_SRC_SHIFT 				29
+#define EMC_CLK_EMC_2X_CLK_SRC_MASK				\
+	(0x7 << EMC_CLK_EMC_2X_CLK_SRC_SHIFT)
+#define EMC_CLK_EMC_2X_CLK_DIVISOR_SHIFT			0
+#define EMC_CLK_EMC_2X_CLK_DIVISOR_MASK				\
+	(0xff << EMC_CLK_EMC_2X_CLK_DIVISOR_SHIFT)
+
+#define EMC_INTSTATUS						0x0
+#define EMC_INTSTATUS_MRR_DIVLD					(0x1 << 5)
+#define EMC_INTSTATUS_CLKCHANGE_COMPLETE			(0x1 << 4)
+
+#define EMC_INTMASK						0x4
+#define EMC_DBG							0x8
+#define EMC_DBG_WRITE_MUX_ACTIVE				(1 << 1)
+#define EMC_DBG_CFG_SWAP_SHIFT					26
+#define EMC_DBG_CFG_SWAP_MASK					\
+	(0x3 <<	EMC_DBG_CFG_SWAP_SHIFT)
+#define EMC_DBG_WRITE_ACTIVE_ONLY				(1 << 30)
+
+#define EMC_CONFIG_SAMPLE_DELAY					0x5f0
+#define EMC_CFG_UPDATE						0x5f4
+#define EMC_CFG_UPDATE_UPDATE_DLL_IN_UPDATE_SHIFT		9
+#define EMC_CFG_UPDATE_UPDATE_DLL_IN_UPDATE_MASK		\
+	(0x3 << EMC_CFG_UPDATE_UPDATE_DLL_IN_UPDATE_SHIFT)
+#define EMC_CFG							0xc
+#define EMC_CFG_DRAM_CLKSTOP_PD					(1 << 31)
+#define EMC_CFG_DRAM_CLKSTOP_SR					(1 << 30)
+#define EMC_CFG_DRAM_ACPD					(1 << 29)
+#define EMC_CFG_DYN_SELF_REF					(1 << 28)
+#define EMC_CFG_REQACT_ASYNC					(1 << 26)
+#define EMC_CFG_AUTO_PRE_WR					(1 << 25)
+#define EMC_CFG_AUTO_PRE_RD					(1 << 24)
+#define EMC_CFG_MAM_PRE_WR					(1 << 23)
+#define EMC_CFG_MAN_PRE_RD					(1 << 22)
+#define EMC_CFG_PERIODIC_QRST					(1 << 21)
+#define EMC_CFG_PERIODIC_QRST_SHIFT				(21)
+#define EMC_CFG_EN_DYNAMIC_PUTERM				(1 << 20)
+#define EMC_CFG_DLY_WR_DQ_HALF_CLOCK				(1 << 19)
+#define EMC_CFG_DSR_VTTGEN_DRV_EN				(1 << 18)
+#define EMC_CFG_EMC2MC_CLK_RATIO				(3 << 16)
+#define EMC_CFG_WAIT_FOR_ISP2B_READY_B4_CC			(1 << 9)
+#define EMC_CFG_WAIT_FOR_VI2_READY_B4_CC			(1 << 8)
+#define EMC_CFG_WAIT_FOR_ISP2_READY_B4_CC			(1 << 7)
+#define EMC_CFG_INVERT_DQM					(1 << 6)
+#define EMC_CFG_WAIT_FOR_DISPLAYB_READY_B4_CC			(1 << 5)
+#define EMC_CFG_WAIT_FOR_DISPLAY_READY_B4_CC			(1 << 4)
+#define EMC_CFG_EMC2PMACRO_CFG_BYPASS_DATAPIPE2			(1 << 3)
+#define EMC_CFG_EMC2PMACRO_CFG_BYPASS_DATAPIPE1			(1 << 2)
+#define EMC_CFG_EMC2PMACRO_CFG_BYPASS_ADDRPIPE			(1 << 1)
+
+#define EMC_ADR_CFG						0x10
+#define EMC_REFCTRL						0x20
+#define EMC_REFCTRL_DEV_SEL_SHIFT				0
+#define EMC_REFCTRL_DEV_SEL_MASK				\
+	(0x3 << EMC_REFCTRL_DEV_SEL_SHIFT)
+#define EMC_REFCTRL_ENABLE					(0x1 << 31)
+#define EMC_REFCTRL_ENABLE_ALL(num)				\
+	(((((num) > 1) ? 0 : 2) << EMC_REFCTRL_DEV_SEL_SHIFT)	\
+	 | EMC_REFCTRL_ENABLE)
+#define EMC_REFCTRL_DISABLE_ALL(num)				\
+	((((num) > 1) ? 0 : 2) << EMC_REFCTRL_DEV_SEL_SHIFT)
+
+#define EMC_PIN							0x24
+#define EMC_PIN_PIN_CKE_PER_DEV					(1 << 2)
+#define EMC_PIN_PIN_CKEB					(1 << 1)
+#define EMC_PIN_PIN_CKE						(1 << 0)
+
+#define EMC_CLK_FORCE_CC_TRIGGER				(1 << 27)
+
+#define EMC_TIMING_CONTROL					0x28
+#define EMC_RC							0x2c
+#define EMC_RFC							0x30
+#define EMC_RFCPB						0x590
+#define EMC_RAS							0x34
+#define EMC_RP							0x38
+#define EMC_R2W							0x3c
+#define EMC_W2R							0x40
+#define EMC_R2P							0x44
+#define EMC_W2P							0x48
+#define EMC_CCDMW						0x5c0
+#define EMC_RD_RCD						0x4c
+#define EMC_WR_RCD						0x50
+#define EMC_RRD							0x54
+#define EMC_REXT						0x58
+#define EMC_WDV							0x5c
+#define EMC_QUSE						0x60
+#define EMC_QRST						0x64
+#define EMC_ISSUE_QRST						0x428
+#define EMC_QSAFE						0x68
+#define EMC_RDV							0x6c
+#define EMC_REFRESH						0x70
+#define EMC_BURST_REFRESH_NUM					0x74
+#define EMC_PDEX2WR						0x78
+#define EMC_PDEX2RD						0x7c
+#define EMC_PDEX2CKE						0x118
+#define EMC_PCHG2PDEN						0x80
+#define EMC_ACT2PDEN						0x84
+#define EMC_AR2PDEN						0x88
+#define EMC_RW2PDEN						0x8c
+#define EMC_CKE2PDEN						0x11c
+#define EMC_TXSR						0x90
+#define EMC_TCKE						0x94
+#define EMC_TFAW						0x98
+#define EMC_TRPAB						0x9c
+#define EMC_TCLKSTABLE						0xa0
+#define EMC_TCLKSTOP						0xa4
+#define EMC_TREFBW						0xa8
+#define EMC_TPPD						0xac
+#define EMC_PDEX2MRR						0xb4
+#define EMC_ODT_WRITE						0xb0
+#define EMC_WEXT						0xb8
+#define EMC_RFC_SLR						0xc0
+#define EMC_MRS_WAIT_CNT2					0xc4
+#define EMC_MRS_WAIT_CNT2_MRS_EXT2_WAIT_CNT_SHIFT		16
+#define EMC_MRS_WAIT_CNT2_MRS_EXT2_WAIT_CNT_MASK		\
+	(0x7ff << EMC_MRS_WAIT_CNT2_MRS_EXT2_WAIT_CNT_SHIFT)
+#define EMC_MRS_WAIT_CNT2_MRS_EXT1_WAIT_CNT_SHIFT		0
+#define EMC_MRS_WAIT_CNT2_MRS_EXT1_WAIT_CNT_MASK		\
+	(0x3ff << EMC_MRS_WAIT_CNT2_MRS_EXT1_WAIT_CNT_SHIFT)
+
+#define EMC_MRS_WAIT_CNT					0xc8
+#define EMC_MRS_WAIT_CNT_SHORT_WAIT_SHIFT			0
+#define EMC_MRS_WAIT_CNT_SHORT_WAIT_MASK			\
+	(0x3FF << EMC_MRS_WAIT_CNT_SHORT_WAIT_SHIFT)
+#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_MRS							0xcc
+#define EMC_MODE_SET_DLL_RESET					(1 << 8)
+#define EMC_MRS_USE_MRS_LONG_CNT				(1 << 26)
+
+#define EMC_EMRS						0xd0
+#define EMC_EMRS_USE_EMRS_LONG_CNT				(1 << 26)
+
+#define EMC_REF							0xd4
+#define EMC_REF_FORCE_CMD					1
+
+#define EMC_PRE							0xd8
+#define EMC_NOP							0xdc
+#define EMC_SELF_REF							0xe0
+#define EMC_SELF_REF_CMD_ENABLED				(1 << 0)
+#define EMC_SELF_REF_ACTIVE_SELF_REF				(1 << 8)
+#define EMC_SELF_REF_DEV_SEL_SHIFT				30
+#define EMC_SELF_REF_DEV_SEL_MASK				\
+	(0x3 << EMC_SELF_REF_DEV_SEL_SHIFT)
+
+#define EMC_DPD							0xe4
+#define EMC_MRW							0xe8
+#define EMC_MRW_MRW_OP_SHIFT					0
+#define EMC_MRW_MRW_OP_MASK					\
+	(0xff << EMC_MRW_MRW_OP_SHIFT)
+#define EMC_MRW_MRW_MA_SHIFT					16
+#define EMC_MRW_MRW_MA_MASK					\
+	(0xff << EMC_MRW_MRW_MA_SHIFT)
+#define EMC_MRW_USE_MRW_LONG_CNT				26
+#define EMC_MRW_USE_MRW_EXT_CNT					27
+#define EMC_MRW_MRW_DEV_SELECTN_SHIFT				30
+#define EMC_MRW_MRW_DEV_SELECTN_MASK				\
+	(0x3 << EMC_MRW_MRW_DEV_SELECTN_SHIFT)
+
+#define EMC_MRR							0xec
+#define EMC_MRR_DEV_SEL_SHIFT					30
+#define EMC_MRR_DEV_SEL_MASK					\
+	(0x3 << EMC_SELF_REF_DEV_SEL_SHIFT)
+#define EMC_MRR_MA_SHIFT					16
+#define EMC_MRR_MA_MASK						\
+	(0xff << EMC_MRR_MA_SHIFT)
+#define EMC_MRR_DATA_SHIFT					0
+#define EMC_MRR_DATA_MASK					\
+	(0xffff << EMC_MRR_DATA_SHIFT)
+#define LPDDR2_MR4_TEMP_SHIFT					0
+#define LPDDR2_MR4_TEMP_MASK					\
+	(0x7 << LPDDR2_MR4_TEMP_SHIFT)
+
+#define EMC_CMDQ						0xf0
+#define EMC_MC2EMCQ						0xf4
+#define EMC_FBIO_SPARE						0x100
+#define EMC_FBIO_CFG5						0x104
+#define EMC_FBIO_CFG5_DRAM_TYPE_SHIFT				0
+#define EMC_FBIO_CFG5_DRAM_TYPE_MASK				\
+	(0x3 <<	EMC_FBIO_CFG5_DRAM_TYPE_SHIFT)
+#define EMC_FBIO_CFG5_CMD_TX_DIS				(1 << 8)
+#define EMC_FBIO_CFG5_CMD_BUS_RETURN_TO_ZERO			(1 << 27)
+
+#define EMC_CFG5_QUSE_MODE_SHIFT				13
+#define EMC_CFG5_QUSE_MODE_MASK					\
+	(0x7 << EMC_CFG5_QUSE_MODE_SHIFT)
+
+#define EMC_CFG_RSV						0x120
+#define EMC_ACPD_CONTROL					0x124
+#define EMC_MPC							0x128
+#define EMC_EMRS2						0x12c
+#define EMC_EMRS2_USE_EMRS2_LONG_CNT				(1 << 26)
+
+#define EMC_EMRS3						0x130
+#define EMC_MRW2						0x134
+#define EMC_MRW3						0x138
+#define EMC_MRW4						0x13c
+#define EMC_MRW5						0x4a0
+#define EMC_MRW6						0x4a4
+#define EMC_MRW7						0x4a8
+#define EMC_MRW8						0x4ac
+#define EMC_MRW9						0x4b0
+#define EMC_MRW10						0x4b4
+#define EMC_MRW11						0x4b8
+#define EMC_MRW12						0x4bc
+#define EMC_MRW13						0x4c0
+#define EMC_MRW14						0x4c4
+#define EMC_MRW15						0x4d0
+#define EMC_CFG_SYNC						0x4d4
+#define EMC_CLKEN_OVERRIDE					0x140
+#define EMC_R2R							0x144
+#define EMC_W2W							0x148
+#define EMC_EINPUT						0x14c
+#define EMC_EINPUT_DURATION 					0x150
+#define EMC_PUTERM_EXTRA					0x154
+#define EMC_TCKESR						0x158
+#define EMC_TPD							0x15c
+#define EMC_STAT_CONTROL					0x160
+#define EMC_STAT_STATUS						0x164
+#define EMC_STAT_DRAM_CLOCK_LIMIT_LO				0x19c
+#define EMC_STAT_DRAM_CLOCK_LIMIT_HI				0x1a0
+#define EMC_STAT_DRAM_CLOCKS_LO					0x1a4
+#define EMC_STAT_DRAM_CLOCKS_HI					0x1a8
+#define EMC_STAT_DRAM_DEV0_ACTIVATE_CNT_LO			0x1ac
+#define EMC_STAT_DRAM_DEV0_ACTIVATE_CNT_HI			0x1b0
+#define EMC_STAT_DRAM_DEV0_READ_CNT_LO				0x1b4
+#define EMC_STAT_DRAM_DEV0_READ_CNT_HI				0x1b8
+#define EMC_STAT_DRAM_DEV0_READ8_CNT_LO				0x1bc
+#define EMC_STAT_DRAM_DEV0_READ8_CNT_HI				0x1c0
+#define EMC_STAT_DRAM_DEV0_WRITE_CNT_LO				0x1c4
+#define EMC_STAT_DRAM_DEV0_WRITE_CNT_HI				0x1c8
+#define EMC_STAT_DRAM_DEV0_WRITE8_CNT_LO			0x1cc
+#define EMC_STAT_DRAM_DEV0_WRITE8_CNT_HI			0x1d0
+#define EMC_STAT_DRAM_DEV0_REF_CNT_LO				0x1d4
+#define EMC_STAT_DRAM_DEV0_REF_CNT_HI				0x1d8
+#define EMC_STAT_DRAM_DEV0_EXTCLKS_CKE_EQ0_NO_BANKS_ACTIVE_CLKS_LO	0x1dc
+#define EMC_STAT_DRAM_DEV0_EXTCLKS_CKE_EQ0_NO_BANKS_ACTIVE_CLKS_HI	0x1e0
+#define EMC_STAT_DRAM_DEV0_CLKSTOP_CKE_EQ0_NO_BANKS_ACTIVE_CLKS_LO	0x1e4
+#define EMC_STAT_DRAM_DEV0_CLKSTOP_CKE_EQ0_NO_BANKS_ACTIVE_CLKS_HI	0x1e8
+#define EMC_STAT_DRAM_DEV0_EXTCLKS_CKE_EQ1_NO_BANKS_ACTIVE_CLKS_LO	0x1ec
+#define EMC_STAT_DRAM_DEV0_EXTCLKS_CKE_EQ1_NO_BANKS_ACTIVE_CLKS_HI	0x1f0
+#define EMC_STAT_DRAM_DEV0_CLKSTOP_CKE_EQ1_NO_BANKS_ACTIVE_CLKS_LO	0x1f4
+#define EMC_STAT_DRAM_DEV0_CLKSTOP_CKE_EQ1_NO_BANKS_ACTIVE_CLKS_HI	0x1f8
+#define EMC_STAT_DRAM_DEV0_EXTCLKS_CKE_EQ0_SOME_BANKS_ACTIVE_CLKS_LO	0x1fc
+#define EMC_STAT_DRAM_DEV0_EXTCLKS_CKE_EQ0_SOME_BANKS_ACTIVE_CLKS_HI	0x200
+#define EMC_STAT_DRAM_DEV0_CLKSTOP_CKE_EQ0_SOME_BANKS_ACTIVE_CLKS_LO	0x204
+#define EMC_STAT_DRAM_DEV0_CLKSTOP_CKE_EQ0_SOME_BANKS_ACTIVE_CLKS_HI	0x208
+#define EMC_STAT_DRAM_DEV0_EXTCLKS_CKE_EQ1_SOME_BANKS_ACTIVE_CLKS_LO	0x20c
+#define EMC_STAT_DRAM_DEV0_EXTCLKS_CKE_EQ1_SOME_BANKS_ACTIVE_CLKS_HI	0x210
+#define EMC_STAT_DRAM_DEV0_CLKSTOP_CKE_EQ1_SOME_BANKS_ACTIVE_CLKS_LO	0x214
+#define EMC_STAT_DRAM_DEV0_CLKSTOP_CKE_EQ1_SOME_BANKS_ACTIVE_CLKS_HI	0x218
+#define EMC_STAT_DRAM_DEV0_SR_CKE_EQ0_CLKS_LO				0x21c
+#define EMC_STAT_DRAM_DEV0_SR_CKE_EQ0_CLKS_HI				0x220
+#define EMC_STAT_DRAM_DEV0_DSR						0x224
+#define EMC_STAT_DRAM_DEV1_ACTIVATE_CNT_LO				0x228
+#define EMC_STAT_DRAM_DEV1_ACTIVATE_CNT_HI				0x22c
+#define EMC_STAT_DRAM_DEV1_READ_CNT_LO					0x230
+#define EMC_STAT_DRAM_DEV1_READ_CNT_HI					0x234
+#define EMC_STAT_DRAM_DEV1_READ8_CNT_LO					0x238
+#define EMC_STAT_DRAM_DEV1_READ8_CNT_HI					0x23c
+#define EMC_STAT_DRAM_DEV1_WRITE_CNT_LO					0x240
+#define EMC_STAT_DRAM_DEV1_WRITE_CNT_HI					0x244
+#define EMC_STAT_DRAM_DEV1_WRITE8_CNT_LO				0x248
+#define EMC_STAT_DRAM_DEV1_WRITE8_CNT_HI				0x24c
+#define EMC_STAT_DRAM_DEV1_REF_CNT_LO					0x250
+#define EMC_STAT_DRAM_DEV1_REF_CNT_HI					0x254
+#define EMC_STAT_DRAM_DEV1_EXTCLKS_CKE_EQ0_NO_BANKS_ACTIVE_CLKS_LO	0x258
+#define EMC_STAT_DRAM_DEV1_EXTCLKS_CKE_EQ0_NO_BANKS_ACTIVE_CLKS_HI	0x25c
+#define EMC_STAT_DRAM_DEV1_CLKSTOP_CKE_EQ0_NO_BANKS_ACTIVE_CLKS_LO	0x260
+#define EMC_STAT_DRAM_DEV1_CLKSTOP_CKE_EQ0_NO_BANKS_ACTIVE_CLKS_HI	0x264
+#define EMC_STAT_DRAM_DEV1_EXTCLKS_CKE_EQ1_NO_BANKS_ACTIVE_CLKS_LO	0x268
+#define EMC_STAT_DRAM_DEV1_EXTCLKS_CKE_EQ1_NO_BANKS_ACTIVE_CLKS_HI	0x26c
+#define EMC_STAT_DRAM_DEV1_CLKSTOP_CKE_EQ1_NO_BANKS_ACTIVE_CLKS_LO	0x270
+#define EMC_STAT_DRAM_DEV1_CLKSTOP_CKE_EQ1_NO_BANKS_ACTIVE_CLKS_HI	0x274
+#define EMC_STAT_DRAM_DEV1_EXTCLKS_CKE_EQ0_SOME_BANKS_ACTIVE_CLKS_LO	0x278
+#define EMC_STAT_DRAM_DEV1_EXTCLKS_CKE_EQ0_SOME_BANKS_ACTIVE_CLKS_HI	0x27c
+#define EMC_STAT_DRAM_DEV1_CLKSTOP_CKE_EQ0_SOME_BANKS_ACTIVE_CLKS_LO	0x280
+#define EMC_STAT_DRAM_DEV1_CLKSTOP_CKE_EQ0_SOME_BANKS_ACTIVE_CLKS_HI	0x284
+#define EMC_STAT_DRAM_DEV1_EXTCLKS_CKE_EQ1_SOME_BANKS_ACTIVE_CLKS_LO	0x288
+#define EMC_STAT_DRAM_DEV1_EXTCLKS_CKE_EQ1_SOME_BANKS_ACTIVE_CLKS_HI	0x28c
+#define EMC_STAT_DRAM_DEV1_CLKSTOP_CKE_EQ1_SOME_BANKS_ACTIVE_CLKS_LO	0x290
+#define EMC_STAT_DRAM_DEV1_CLKSTOP_CKE_EQ1_SOME_BANKS_ACTIVE_CLKS_HI	0x294
+#define EMC_STAT_DRAM_DEV1_SR_CKE_EQ0_CLKS_LO				0x298
+#define EMC_STAT_DRAM_DEV1_SR_CKE_EQ0_CLKS_HI				0x29c
+#define EMC_STAT_DRAM_DEV1_DSR						0x2a0
+#define EMC_STAT_DRAM_IO_EXTCLKS_CKE_EQ0_NO_BANKS_ACTIVE_CLKS_LO	0xc8c
+#define EMC_STAT_DRAM_IO_EXTCLKS_CKE_EQ0_NO_BANKS_ACTIVE_CLKS_HI	0xc90
+#define EMC_STAT_DRAM_IO_CLKSTOP_CKE_EQ0_NO_BANKS_ACTIVE_CLKS_LO	0xc94
+#define EMC_STAT_DRAM_IO_CLKSTOP_CKE_EQ0_NO_BANKS_ACTIVE_CLKS_HI	0xc98
+#define EMC_STAT_DRAM_IO_EXTCLKS_CKE_EQ1_NO_BANKS_ACTIVE_CLKS_LO	0xc9c
+#define EMC_STAT_DRAM_IO_EXTCLKS_CKE_EQ1_NO_BANKS_ACTIVE_CLKS_HI	0xca0
+#define EMC_STAT_DRAM_IO_CLKSTOP_CKE_EQ1_NO_BANKS_ACTIVE_CLKS_LO	0xca4
+#define EMC_STAT_DRAM_IO_CLKSTOP_CKE_EQ1_NO_BANKS_ACTIVE_CLKS_HI	0xca8
+#define EMC_STAT_DRAM_IO_EXTCLKS_CKE_EQ0_SOME_BANKS_ACTIVE_CLKS_LO	0xcac
+#define EMC_STAT_DRAM_IO_EXTCLKS_CKE_EQ0_SOME_BANKS_ACTIVE_CLKS_HI	0xcb0
+#define EMC_STAT_DRAM_IO_CLKSTOP_CKE_EQ0_SOME_BANKS_ACTIVE_CLKS_LO	0xcb4
+#define EMC_STAT_DRAM_IO_CLKSTOP_CKE_EQ0_SOME_BANKS_ACTIVE_CLKS_HI	0xcb8
+#define EMC_STAT_DRAM_IO_EXTCLKS_CKE_EQ1_SOME_BANKS_ACTIVE_CLKS_LO	0xcbc
+#define EMC_STAT_DRAM_IO_EXTCLKS_CKE_EQ1_SOME_BANKS_ACTIVE_CLKS_HI	0xcc0
+#define EMC_STAT_DRAM_IO_CLKSTOP_CKE_EQ1_SOME_BANKS_ACTIVE_CLKS_LO	0xcc4
+#define EMC_STAT_DRAM_IO_CLKSTOP_CKE_EQ1_SOME_BANKS_ACTIVE_CLKS_HI	0xcc8
+#define EMC_STAT_DRAM_IO_SR_CKE_EQ0_CLKS_LO			0xccc
+#define EMC_STAT_DRAM_IO_SR_CKE_EQ0_CLKS_HI			0xcd0
+#define EMC_STAT_DRAM_IO_DSR					0xcd4
+#define EMC_AUTO_CAL_CONFIG					0x2a4
+#define EMC_AUTO_CAL_CONFIG_AUTO_CAL_COMPUTE_START		(1 << 0)
+#define EMC_AUTO_CAL_CONFIG_AUTO_CAL_MEASURE_STALL		(1 << 9)
+#define EMC_AUTO_CAL_CONFIG_AUTO_CAL_UPDATE_STALL		(1 << 10)
+#define EMC_AUTO_CAL_CONFIG_AUTO_CAL_ENABLE			(1 << 29)
+#define EMC_AUTO_CAL_CONFIG_AUTO_CAL_START			(1 << 31)
+
+#define EMC_AUTO_CAL_CONFIG2					0x458
+#define EMC_AUTO_CAL_CONFIG3					0x45c
+#define EMC_AUTO_CAL_CONFIG4					0x5b0
+#define EMC_AUTO_CAL_CONFIG5					0x5b4
+#define EMC_AUTO_CAL_CONFIG6					0x5cc
+#define EMC_AUTO_CAL_CONFIG7					0x574
+#define EMC_AUTO_CAL_CONFIG8					0x2dc
+#define EMC_AUTO_CAL_VREF_SEL_0					0x2f8
+#define EMC_AUTO_CAL_VREF_SEL_1					0x300
+#define EMC_AUTO_CAL_INTERVAL					0x2a8
+#define EMC_AUTO_CAL_STATUS					0x2ac
+#define EMC_AUTO_CAL_STATUS2					0x3d4
+#define EMC_AUTO_CAL_CHANNEL					0x464
+#define EMC_PMACRO_RX_TERM					0xc48
+#define EMC_PMACRO_DQ_TX_DRV					0xc70
+#define EMC_PMACRO_CA_TX_DRV					0xc74
+#define EMC_PMACRO_CMD_TX_DRV					0xc4c
+#define EMC_PMACRO_AUTOCAL_CFG_0				0x700
+#define EMC_PMACRO_AUTOCAL_CFG_1				0x704
+#define EMC_PMACRO_AUTOCAL_CFG_2				0x708
+#define EMC_PMACRO_AUTOCAL_CFG_COMMON				0xc78
+#define EMC_PMACRO_AUTOCAL_CFG_COMMON_E_CAL_BYPASS_DVFS		(1 << 16)
+
+#define EMC_PMACRO_ZCTRL					0xc44
+#define EMC_XM2COMPPADCTRL					0x30c
+#define EMC_XM2COMPPADCTRL_VREF_CAL_ENABLE			(1 << 10)
+
+#define EMC_XM2COMPPADCTRL2					0x578
+#define EMC_XM2COMPPADCTRL3					0x2f4
+#define EMC_COMP_PAD_SW_CTRL					0x57c
+#define EMC_REQ_CTRL						0x2b0
+#define EMC_EMC_STATUS						0x2b4
+#define EMC_EMC_STATUS_MRR_DIVLD				(1 << 20)
+#define EMC_EMC_STATUS_TIMING_UPDATE_STALLED			(1 << 23)
+#define EMC_EMC_STATUS_DRAM_IN_POWERDOWN_SHIFT			4
+#define EMC_EMC_STATUS_DRAM_IN_POWERDOWN_MASK			\
+	(0x3 << EMC_EMC_STATUS_DRAM_IN_POWERDOWN_SHIFT)
+#define EMC_EMC_STATUS_DRAM_IN_SELF_REFRESH_SHIFT		8
+#define EMC_EMC_STATUS_DRAM_IN_SELF_REFRESH_MASK		\
+	(0x3 << EMC_EMC_STATUS_DRAM_IN_SELF_REFRESH_SHIFT)
+
+#define EMC_CFG_2						0x2b8
+#define EMC_CFG_DIG_DLL						0x2bc
+#define EMC_CFG_DIG_DLL_CFG_DLL_EN				(1 << 0)
+#define EMC_CFG_DIG_DLL_CFG_DLL_STALL_ALL_UNTIL_LOCK		(1 << 1)
+#define EMC_CFG_DIG_DLL_CFG_DLL_STALL_ALL_TRAFFIC		(1 << 3)
+#define EMC_CFG_DIG_DLL_CFG_DLL_STALL_RW_UNTIL_LOCK		(1 << 4)
+#define EMC_CFG_DIG_DLL_CFG_DLL_MODE_SHIFT			6
+#define EMC_CFG_DIG_DLL_CFG_DLL_MODE_MASK			\
+	(0x3 << EMC_CFG_DIG_DLL_CFG_DLL_MODE_SHIFT)
+#define EMC_CFG_DIG_DLL_CFG_DLL_LOCK_LIMIT_SHIFT		8
+#define EMC_CFG_DIG_DLL_CFG_DLL_LOCK_LIMIT_MASK			\
+	(0x7 << EMC_CFG_DIG_DLL_CFG_DLL_LOCK_LIMIT_SHIFT)
+
+#define EMC_CFG_DIG_DLL_PERIOD					0x2c0
+#define EMC_DIG_DLL_STATUS					0x2c4
+#define EMC_DIG_DLL_STATUS_DLL_LOCK				(1 << 15)
+#define EMC_DIG_DLL_STATUS_DLL_PRIV_UPDATED			(1 << 17)
+#define EMC_DIG_DLL_STATUS_DLL_OUT_SHIFT			0
+#define EMC_DIG_DLL_STATUS_DLL_OUT_MASK				\
+	(0x7ff << EMC_DIG_DLL_STATUS_DLL_OUT_SHIFT)
+
+#define EMC_CFG_DIG_DLL_1					0x2c8
+#define EMC_RDV_MASK						0x2cc
+#define EMC_WDV_MASK						0x2d0
+#define EMC_RDV_EARLY_MASK					0x2d4
+#define EMC_RDV_EARLY						0x2d8
+#define EMC_WDV_CHK						0x4e0
+#define EMC_ZCAL_INTERVAL					0x2e0
+#define EMC_ZCAL_WAIT_CNT					0x2e4
+#define EMC_ZCAL_WAIT_CNT_ZCAL_WAIT_CNT_MASK			0x7ff
+#define EMC_ZCAL_WAIT_CNT_ZCAL_WAIT_CNT_SHIFT			0
+
+#define EMC_ZCAL_MRW_CMD					0x2e8
+#define EMC_ZQ_CAL						0x2ec
+#define EMC_ZQ_CAL_DEV_SEL_SHIFT				30
+#define EMC_ZQ_CAL_DEV_SEL_MASK					\
+	(0x3 << EMC_SELF_REF_DEV_SEL_SHIFT)
+#define EMC_ZQ_CAL_LONG						(1 << 4)
+#define EMC_ZQ_CAL_ZQ_LATCH_CMD					(1 << 1)
+#define EMC_ZQ_CAL_ZQ_CAL_CMD					(1 << 0)
+#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_SCRATCH0						0x324
+#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_FDPD_CTRL_CMD_NO_RAMP				0x4d8
+#define EMC_FDPD_CTRL_CMD_NO_RAMP_CMD_DPD_NO_RAMP_ENABLE	(1 << 0)
+
+#define EMC_SEL_DPD_CTRL					0x3d8
+#define EMC_SEL_DPD_CTRL_DATA_SEL_DPD_EN			(1 << 8)
+#define EMC_SEL_DPD_CTRL_ODT_SEL_DPD_EN				(1 << 5)
+#define EMC_SEL_DPD_CTRL_RESET_SEL_DPD_EN			(1 << 4)
+#define EMC_SEL_DPD_CTRL_CA_SEL_DPD_EN				(1 << 3)
+#define EMC_SEL_DPD_CTRL_CLK_SEL_DPD_EN				(1 << 2)
+#define EMC_SEL_DPD_CTRL_DDR3_MASK				\
+	((0xf << 2) | (0x1 << 8))
+#define EMC_SEL_DPD_CTRL_MAS					\
+	((0x3 << 2) | (0x1 << 5) | (0x1 << 8))
+
+#define EMC_FDPD_CTRL_DQ					0x310
+#define EMC_FDPD_CTRL_CMD					0x314
+#define EMC_PRE_REFRESH_REQ_CNT					0x3dc
+#define EMC_REFCTRL2						0x580
+#define EMC_FBIO_CFG7						0x584
+#define EMC_FBIO_CFG7_CH0_ENABLE				(1 << 1)
+#define EMC_FBIO_CFG7_CH1_ENABLE				(1 << 2)
+
+#define EMC_DATA_BRLSHFT_0					0x588
+#define EMC_DATA_BRLSHFT_0_RANK0_BYTE7_DATA_BRLSHFT_SHIFT	21
+#define EMC_DATA_BRLSHFT_0_RANK0_BYTE7_DATA_BRLSHFT_MASK	\
+	(0x7 << EMC_DATA_BRLSHFT_0_RANK0_BYTE7_DATA_BRLSHFT_SHIFT)
+#define EMC_DATA_BRLSHFT_0_RANK0_BYTE6_DATA_BRLSHFT_SHIFT	18
+#define EMC_DATA_BRLSHFT_0_RANK0_BYTE6_DATA_BRLSHFT_MASK	\
+	(0x7 << EMC_DATA_BRLSHFT_0_RANK0_BYTE6_DATA_BRLSHFT_SHIFT)
+#define EMC_DATA_BRLSHFT_0_RANK0_BYTE5_DATA_BRLSHFT_SHIFT	15
+#define EMC_DATA_BRLSHFT_0_RANK0_BYTE5_DATA_BRLSHFT_MASK	\
+	(0x7 << EMC_DATA_BRLSHFT_0_RANK0_BYTE5_DATA_BRLSHFT_SHIFT)
+#define EMC_DATA_BRLSHFT_0_RANK0_BYTE4_DATA_BRLSHFT_SHIFT	12
+#define EMC_DATA_BRLSHFT_0_RANK0_BYTE4_DATA_BRLSHFT_MASK	\
+	(0x7 << EMC_DATA_BRLSHFT_0_RANK0_BYTE4_DATA_BRLSHFT_SHIFT)
+#define EMC_DATA_BRLSHFT_0_RANK0_BYTE3_DATA_BRLSHFT_SHIFT	9
+#define EMC_DATA_BRLSHFT_0_RANK0_BYTE3_DATA_BRLSHFT_MASK	\
+	(0x7 << EMC_DATA_BRLSHFT_0_RANK0_BYTE3_DATA_BRLSHFT_SHIFT)
+#define EMC_DATA_BRLSHFT_0_RANK0_BYTE2_DATA_BRLSHFT_SHIFT	6
+#define EMC_DATA_BRLSHFT_0_RANK0_BYTE2_DATA_BRLSHFT_MASK	\
+	(0x7 << EMC_DATA_BRLSHFT_0_RANK0_BYTE2_DATA_BRLSHFT_SHIFT)
+#define EMC_DATA_BRLSHFT_0_RANK0_BYTE1_DATA_BRLSHFT_SHIFT	3
+#define EMC_DATA_BRLSHFT_0_RANK0_BYTE1_DATA_BRLSHFT_MASK	\
+	(0x7 << EMC_DATA_BRLSHFT_0_RANK0_BYTE1_DATA_BRLSHFT_SHIFT)
+#define EMC_DATA_BRLSHFT_0_RANK0_BYTE0_DATA_BRLSHFT_SHIFT	0
+#define EMC_DATA_BRLSHFT_0_RANK0_BYTE0_DATA_BRLSHFT_MASK	\
+	(0x7 << EMC_DATA_BRLSHFT_0_RANK0_BYTE0_DATA_BRLSHFT_SHIFT)
+
+#define EMC_DATA_BRLSHFT_1					0x58c
+#define EMC_DATA_BRLSHFT_1_RANK1_BYTE7_DATA_BRLSHFT_SHIFT	21
+#define EMC_DATA_BRLSHFT_1_RANK1_BYTE7_DATA_BRLSHFT_MASK	\
+	(0x7 << EMC_DATA_BRLSHFT_1_RANK1_BYTE7_DATA_BRLSHFT_SHIFT)
+#define EMC_DATA_BRLSHFT_1_RANK1_BYTE6_DATA_BRLSHFT_SHIFT	18
+#define EMC_DATA_BRLSHFT_1_RANK1_BYTE6_DATA_BRLSHFT_MASK	\
+	(0x7 << EMC_DATA_BRLSHFT_1_RANK1_BYTE6_DATA_BRLSHFT_SHIFT)
+#define EMC_DATA_BRLSHFT_1_RANK1_BYTE5_DATA_BRLSHFT_SHIFT	15
+#define EMC_DATA_BRLSHFT_1_RANK1_BYTE5_DATA_BRLSHFT_MASK	\
+	(0x7 << EMC_DATA_BRLSHFT_1_RANK1_BYTE5_DATA_BRLSHFT_SHIFT)
+#define EMC_DATA_BRLSHFT_1_RANK1_BYTE4_DATA_BRLSHFT_SHIFT	12
+#define EMC_DATA_BRLSHFT_1_RANK1_BYTE4_DATA_BRLSHFT_MASK	\
+	(0x7 << EMC_DATA_BRLSHFT_1_RANK1_BYTE4_DATA_BRLSHFT_SHIFT)
+#define EMC_DATA_BRLSHFT_1_RANK1_BYTE3_DATA_BRLSHFT_SHIFT	9
+#define EMC_DATA_BRLSHFT_1_RANK1_BYTE3_DATA_BRLSHFT_MASK	\
+	(0x7 << EMC_DATA_BRLSHFT_1_RANK1_BYTE3_DATA_BRLSHFT_SHIFT)
+#define EMC_DATA_BRLSHFT_1_RANK1_BYTE2_DATA_BRLSHFT_SHIFT	6
+#define EMC_DATA_BRLSHFT_1_RANK1_BYTE2_DATA_BRLSHFT_MASK	\
+	(0x7 << EMC_DATA_BRLSHFT_1_RANK1_BYTE2_DATA_BRLSHFT_SHIFT)
+#define EMC_DATA_BRLSHFT_1_RANK1_BYTE1_DATA_BRLSHFT_SHIFT	3
+#define EMC_DATA_BRLSHFT_1_RANK1_BYTE1_DATA_BRLSHFT_MASK	\
+	(0x7 << EMC_DATA_BRLSHFT_1_RANK1_BYTE1_DATA_BRLSHFT_SHIFT)
+#define EMC_DATA_BRLSHFT_1_RANK1_BYTE0_DATA_BRLSHFT_SHIFT	0
+#define EMC_DATA_BRLSHFT_1_RANK1_BYTE0_DATA_BRLSHFT_MASK	\
+	(0x7 << EMC_DATA_BRLSHFT_1_RANK1_BYTE0_DATA_BRLSHFT_SHIFT)
+
+#define EMC_DQS_BRLSHFT_0					0x594
+#define EMC_DQS_BRLSHFT_1					0x598
+#define EMC_CMD_BRLSHFT_0					0x59c
+#define EMC_CMD_BRLSHFT_1					0x5a0
+#define EMC_CMD_BRLSHFT_2					0x5a4
+#define EMC_CMD_BRLSHFT_3					0x5a8
+#define EMC_QUSE_BRLSHFT_0					0x5ac
+#define EMC_QUSE_BRLSHFT_1					0x5b8
+#define EMC_QUSE_BRLSHFT_2					0x5bc
+#define EMC_QUSE_BRLSHFT_3					0x5c4
+#define EMC_FBIO_CFG8						0x5c8
+#define EMC_CMD_MAPPING_CMD0_0					0x380
+#define EMC_CMD_MAPPING_CMD0_1					0x384
+#define EMC_CMD_MAPPING_CMD0_2					0x388
+#define EMC_CMD_MAPPING_CMD1_0					0x38c
+#define EMC_CMD_MAPPING_CMD1_1					0x390
+#define EMC_CMD_MAPPING_CMD1_2					0x394
+#define EMC_CMD_MAPPING_CMD2_0					0x398
+#define EMC_CMD_MAPPING_CMD2_1					0x39c
+#define EMC_CMD_MAPPING_CMD2_2					0x3a0
+#define EMC_CMD_MAPPING_CMD3_0					0x3a4
+#define EMC_CMD_MAPPING_CMD3_1					0x3a8
+#define EMC_CMD_MAPPING_CMD3_2					0x3ac
+#define EMC_CMD_MAPPING_BYTE					0x3b0
+#define EMC_DYN_SELF_REF_CONTROL				0x3e0
+#define EMC_TXSRDLL						0x3e4
+#define EMC_CCFIFO_ADDR						0x3e8
+#define EMC_CCFIFO_DATA						0x3ec
+#define EMC_CCFIFO_STATUS					0x3f0
+#define EMC_SWIZZLE_RANK0_BYTE0					0x404
+#define EMC_SWIZZLE_RANK0_BYTE1					0x408
+#define EMC_SWIZZLE_RANK0_BYTE2					0x40c
+#define EMC_SWIZZLE_RANK0_BYTE3					0x410
+#define EMC_SWIZZLE_RANK1_BYTE0					0x418
+#define EMC_SWIZZLE_RANK1_BYTE1					0x41c
+#define EMC_SWIZZLE_RANK1_BYTE2					0x420
+#define EMC_SWIZZLE_RANK1_BYTE3					0x424
+#define EMC_TR_TIMING_0						0x3b4
+#define EMC_TR_CTRL_0						0x3b8
+#define EMC_TR_CTRL_1						0x3bc
+#define EMC_TR_DVFS						0x460
+#define EMC_TR_DVFS_TRAINING_DVFS				(1 << 0)
+
+#define EMC_SWITCH_BACK_CTRL					0x3c0
+#define EMC_TR_RDV						0x3c4
+#define EMC_TR_QPOP						0x3f4
+#define EMC_TR_RDV_MASK						0x3f8
+#define EMC_TR_QSAFE						0x3fc
+#define EMC_TR_QRST						0x400
+#define EMC_IBDLY						0x468
+#define EMC_OBDLY						0x46c
+#define EMC_TXDSRVTTGEN						0x480
+#define EMC_WE_DURATION						0x48c
+#define EMC_WS_DURATION						0x490
+#define EMC_WEV							0x494
+#define EMC_WSV							0x498
+#define EMC_CFG_3						0x49c
+#define EMC_CFG_PIPE_2						0x554
+#define EMC_CFG_PIPE_CLK					0x558
+#define EMC_CFG_PIPE_CLK_CLK_ALWAYS_ON				(1 << 0)
+
+#define EMC_CFG_PIPE_1						0x55c
+#define EMC_CFG_PIPE						0x560
+#define EMC_QPOP						0x564
+#define EMC_QUSE_WIDTH						0x568
+#define EMC_PUTERM_WIDTH					0x56c
+#define EMC_PROTOBIST_CONFIG_ADR_1				0x5d0
+#define EMC_PROTOBIST_CONFIG_ADR_2				0x5d4
+#define EMC_PROTOBIST_MISC					0x5d8
+#define EMC_PROTOBIST_WDATA_LOWER				0x5dc
+#define EMC_PROTOBIST_WDATA_UPPER				0x5e0
+#define EMC_PROTOBIST_RDATA					0x5ec
+#define EMC_DLL_CFG_0						0x5e4
+#define EMC_DLL_CFG_0_DDLLCAL_CTRL_IGNORE_START			(1 << 29)
+#define EMC_DLL_CFG_0_DDLLCAL_CTRL_DUAL_PASS_LOCK		(1 << 28)
+#define EMC_DLL_CFG_0_DDLLCAL_CTRL_STEP_SIZE_SHIFT		24
+#define EMC_DLL_CFG_0_DDLLCAL_CTRL_STEP_SIZE_MASK		\
+	(0xf << EMC_DLL_CFG_0_DDLLCAL_CTRL_STEP_SIZE_SHIFT)
+#define EMC_DLL_CFG_0_DDLLCAL_CTRL_END_COUNT_SHIFT		20
+#define EMC_DLL_CFG_0_DDLLCAL_CTRL_END_COUNT_MASK		\
+	(0xf << EMC_DLL_CFG_0_DDLLCAL_CTRL_END_COUNT_SHIFT)
+#define EMC_DLL_CFG_0_DDLLCAL_CTRL_FILTER_BITS_SHIFT		16
+#define EMC_DLL_CFG_0_DDLLCAL_CTRL_FILTER_BITS_MASK		\
+	(0xf << EMC_DLL_CFG_0_DDLLCAL_CTRL_FILTER_BITS_SHIFT)
+#define EMC_DLL_CFG_0_DDLLCAL_CTRL_SAMPLE_COUNT_SHIFT		12
+#define EMC_DLL_CFG_0_DDLLCAL_CTRL_SAMPLE_COUNT_MASK		\
+	(0xf << EMC_DLL_CFG_0_DDLLCAL_CTRL_SAMPLE_COUNT_SHIFT)
+#define EMC_DLL_CFG_0_DDLLCAL_CTRL_SAMPLE_DELAY_SHIFT		4
+#define EMC_DLL_CFG_0_DDLLCAL_CTRL_SAMPLE_DELAY_MASK		\
+	(0xff << EMC_DLL_CFG_0_DDLLCAL_CTRL_SAMPLE_DELAY_SHIFT)
+#define EMC_DLL_CFG_0_DDLLCAL_UPDATE_CNT_LIMIT_SHIFT		0
+#define EMC_DLL_CFG_0_DDLLCAL_UPDATE_CNT_LIMIT_MASK		\
+	(0xf << EMC_DLL_CFG_0_DDLLCAL_UPDATE_CNT_LIMIT_SHIFT)
+
+#define EMC_DLL_CFG_1						0x5e8
+#define EMC_DLL_CFG_1_DDLLCAL_CTRL_START_TRIM_SHIFT		10
+#define EMC_DLL_CFG_1_DDLLCAL_CTRL_START_TRIM_MASK		\
+	(0x7ff << EMC_DLL_CFG_1_DDLLCAL_CTRL_START_TRIM_SHIFT)
+
+#define EMC_TRAINING_CMD					0xe00
+#define EMC_TRAINING_CMD_PRIME					(1 << 0)
+#define EMC_TRAINING_CMD_CA					(1 << 1)
+#define EMC_TRAINING_CMD_RD					(1 << 2)
+#define EMC_TRAINING_CMD_WR					(1 << 3)
+#define EMC_TRAINING_CMD_QUSE					(1 << 4)
+#define EMC_TRAINING_CMD_CA_VREF				(1 << 5)
+#define EMC_TRAINING_CMD_RD_VREF				(1 << 6)
+#define EMC_TRAINING_CMD_WR_VREF				(1 << 7)
+#define EMC_TRAINING_CMD_QUSE_VREF				(1 << 8)
+#define EMC_TRAINING_CMD_GO					(1 << 31)
+
+#define EMC_TRAINING_CTRL					0xe04
+#define EMC_TRAINING_CTRL_SWAP_RANK				(1 << 14)
+
+#define EMC_TRAINING_STATUS					0xe08
+#define EMC_TRAINING_QUSE_CORS_CTRL				0xe0c
+#define EMC_TRAINING_QUSE_FINE_CTRL				0xe10
+#define EMC_TRAINING_QUSE_CTRL_MISC				0xe14
+#define EMC_TRAINING_WRITE_FINE_CTRL				0xe18
+#define EMC_TRAINING_WRITE_CTRL_MISC				0xe1c
+#define EMC_TRAINING_WRITE_VREF_CTRL				0xe20
+#define EMC_TRAINING_READ_FINE_CTRL				0xe24
+#define EMC_TRAINING_READ_CTRL_MISC				0xe28
+#define EMC_TRAINING_READ_VREF_CTRL				0xe2c
+#define EMC_TRAINING_CA_FINE_CTRL				0xe30
+#define EMC_TRAINING_CA_CTRL_MISC				0xe34
+#define EMC_TRAINING_CA_CTRL_MISC1				0xe38
+#define EMC_TRAINING_CA_VREF_CTRL				0xe3c
+#define EMC_TRAINING_CA_TADR_CTRL				0xe40
+#define EMC_TRAINING_SETTLE					0xe44
+#define EMC_TRAINING_DEBUG_CTRL					0xe48
+#define EMC_TRAINING_DEBUG_DQ0					0xe4c
+#define EMC_TRAINING_DEBUG_DQ1					0xe50
+#define EMC_TRAINING_DEBUG_DQ2					0xe54
+#define EMC_TRAINING_DEBUG_DQ3					0xe58
+#define EMC_TRAINING_MPC					0xe5c
+#define EMC_TRAINING_PATRAM_CTRL				0xe60
+#define EMC_TRAINING_PATRAM_DQ					0xe64
+#define EMC_TRAINING_PATRAM_DMI					0xe68
+#define EMC_TRAINING_VREF_SETTLE				0xe6c
+#define EMC_TRAINING_RW_EYE_CENTER_IB_BYTE0			0xe70
+#define EMC_TRAINING_RW_EYE_CENTER_IB_BYTE1			0xe74
+#define EMC_TRAINING_RW_EYE_CENTER_IB_BYTE2			0xe78
+#define EMC_TRAINING_RW_EYE_CENTER_IB_BYTE3			0xe7c
+#define EMC_TRAINING_RW_EYE_CENTER_IB_MISC			0xe80
+#define EMC_TRAINING_RW_EYE_CENTER_OB_BYTE0			0xe84
+#define EMC_TRAINING_RW_EYE_CENTER_OB_BYTE1			0xe88
+#define EMC_TRAINING_RW_EYE_CENTER_OB_BYTE2			0xe8c
+#define EMC_TRAINING_RW_EYE_CENTER_OB_BYTE3			0xe90
+#define EMC_TRAINING_RW_EYE_CENTER_OB_MISC			0xe94
+#define EMC_TRAINING_RW_OFFSET_IB_BYTE0				0xe98
+#define EMC_TRAINING_RW_OFFSET_IB_BYTE1				0xe9c
+#define EMC_TRAINING_RW_OFFSET_IB_BYTE2				0xea0
+#define EMC_TRAINING_RW_OFFSET_IB_BYTE3				0xea4
+#define EMC_TRAINING_RW_OFFSET_IB_MISC				0xea8
+#define EMC_TRAINING_RW_OFFSET_OB_BYTE0				0xeac
+#define EMC_TRAINING_RW_OFFSET_OB_BYTE1				0xeb0
+#define EMC_TRAINING_RW_OFFSET_OB_BYTE2				0xeb4
+#define EMC_TRAINING_RW_OFFSET_OB_BYTE3				0xeb8
+#define EMC_TRAINING_RW_OFFSET_OB_MISC				0xebc
+#define EMC_TRAINING_OPT_CA_VREF				0xec0
+#define EMC_TRAINING_OPT_DQ_OB_VREF				0xec4
+#define EMC_TRAINING_OPT_DQ_IB_VREF_RANK0			0xec8
+#define EMC_TRAINING_OPT_DQ_IB_VREF_RANK1			0xecc
+#define EMC_TRAINING_QUSE_VREF_CTRL				0xed0
+#define EMC_TRAINING_OPT_DQS_IB_VREF_RANK0			0xed4
+#define EMC_TRAINING_OPT_DQS_IB_VREF_RANK1			0xed8
+#define EMC_TRAINING_DRAMC_TIMING				0xedc
+#define EMC_PMACRO_QUSE_DDLL_RANK0_0				0x600
+#define EMC_PMACRO_QUSE_DDLL_RANK0_1				0x604
+#define EMC_PMACRO_QUSE_DDLL_RANK0_2				0x608
+#define EMC_PMACRO_QUSE_DDLL_RANK0_3				0x60c
+#define EMC_PMACRO_QUSE_DDLL_RANK0_4				0x610
+#define EMC_PMACRO_QUSE_DDLL_RANK0_5				0x614
+#define EMC_PMACRO_QUSE_DDLL_RANK1_0				0x620
+#define EMC_PMACRO_QUSE_DDLL_RANK1_1				0x624
+#define EMC_PMACRO_QUSE_DDLL_RANK1_2				0x628
+#define EMC_PMACRO_QUSE_DDLL_RANK1_3				0x62c
+#define EMC_PMACRO_QUSE_DDLL_RANK1_4				0x630
+#define EMC_PMACRO_QUSE_DDLL_RANK1_5				0x634
+#define EMC_PMACRO_OB_DDLL_LONG_DQ_RANK0_0			0x640
+#define EMC_PMACRO_OB_DDLL_LONG_DQ_RANK0_0_OB_DDLL_LONG_DQ_RANK0_BYTE1_SHIFT \
+	16
+#define EMC_PMACRO_OB_DDLL_LONG_DQ_RANK0_0_OB_DDLL_LONG_DQ_RANK0_BYTE1_MASK  \
+	0x3ff <<							     \
+	EMC_PMACRO_OB_DDLL_LONG_DQ_RANK0_0_OB_DDLL_LONG_DQ_RANK0_BYTE1_SHIFT
+#define EMC_PMACRO_OB_DDLL_LONG_DQ_RANK0_0_OB_DDLL_LONG_DQ_RANK0_BYTE0_SHIFT \
+	0
+#define EMC_PMACRO_OB_DDLL_LONG_DQ_RANK0_0_OB_DDLL_LONG_DQ_RANK0_BYTE0_MASK \
+	0x3ff <<							    \
+	EMC_PMACRO_OB_DDLL_LONG_DQ_RANK0_0_OB_DDLL_LONG_DQ_RANK0_BYTE0_SHIFT
+
+#define EMC_PMACRO_OB_DDLL_LONG_DQ_RANK0_1			0x644
+#define EMC_PMACRO_OB_DDLL_LONG_DQ_RANK0_1_OB_DDLL_LONG_DQ_RANK0_BYTE3_SHIFT \
+	16
+#define EMC_PMACRO_OB_DDLL_LONG_DQ_RANK0_1_OB_DDLL_LONG_DQ_RANK0_BYTE3_MASK  \
+	0x3ff <<							     \
+	EMC_PMACRO_OB_DDLL_LONG_DQ_RANK0_1_OB_DDLL_LONG_DQ_RANK0_BYTE3_SHIFT
+#define EMC_PMACRO_OB_DDLL_LONG_DQ_RANK0_1_OB_DDLL_LONG_DQ_RANK0_BYTE2_SHIFT \
+	0
+#define EMC_PMACRO_OB_DDLL_LONG_DQ_RANK0_1_OB_DDLL_LONG_DQ_RANK0_BYTE2_MASK  \
+	0x3ff <<							     \
+	EMC_PMACRO_OB_DDLL_LONG_DQ_RANK0_1_OB_DDLL_LONG_DQ_RANK0_BYTE2_SHIFT
+
+#define EMC_PMACRO_OB_DDLL_LONG_DQ_RANK0_2			0x648
+#define EMC_PMACRO_OB_DDLL_LONG_DQ_RANK0_2_OB_DDLL_LONG_DQ_RANK0_BYTE5_SHIFT  \
+	16
+#define EMC_PMACRO_OB_DDLL_LONG_DQ_RANK0_2_OB_DDLL_LONG_DQ_RANK0_BYTE5_MASK  \
+	0x3ff <<							     \
+	EMC_PMACRO_OB_DDLL_LONG_DQ_RANK0_2_OB_DDLL_LONG_DQ_RANK0_BYTE5_SHIFT
+#define EMC_PMACRO_OB_DDLL_LONG_DQ_RANK0_2_OB_DDLL_LONG_DQ_RANK0_BYTE4_SHIFT \
+	0
+#define EMC_PMACRO_OB_DDLL_LONG_DQ_RANK0_2_OB_DDLL_LONG_DQ_RANK0_BYTE4_MASK  \
+	0x3ff <<							     \
+	EMC_PMACRO_OB_DDLL_LONG_DQ_RANK0_2_OB_DDLL_LONG_DQ_RANK0_BYTE4_SHIFT
+
+#define EMC_PMACRO_OB_DDLL_LONG_DQ_RANK0_3			0x64c
+#define EMC_PMACRO_OB_DDLL_LONG_DQ_RANK0_3_OB_DDLL_LONG_DQ_RANK0_BYTE7_SHIFT \
+	16
+#define EMC_PMACRO_OB_DDLL_LONG_DQ_RANK0_3_OB_DDLL_LONG_DQ_RANK0_BYTE7_MASK  \
+	0x3ff <<							     \
+	EMC_PMACRO_OB_DDLL_LONG_DQ_RANK0_3_OB_DDLL_LONG_DQ_RANK0_BYTE7_SHIFT
+#define EMC_PMACRO_OB_DDLL_LONG_DQ_RANK0_3_OB_DDLL_LONG_DQ_RANK0_BYTE6_SHIFT \
+	0
+#define EMC_PMACRO_OB_DDLL_LONG_DQ_RANK0_3_OB_DDLL_LONG_DQ_RANK0_BYTE6_MASK  \
+	0x3ff <<							     \
+	EMC_PMACRO_OB_DDLL_LONG_DQ_RANK0_3_OB_DDLL_LONG_DQ_RANK0_BYTE6_SHIFT
+
+#define EMC_PMACRO_OB_DDLL_LONG_DQ_RANK0_4			0x650
+#define EMC_PMACRO_OB_DDLL_LONG_DQ_RANK0_5			0x654
+
+#define EMC_PMACRO_OB_DDLL_LONG_DQ_RANK1_0			0x660
+#define EMC_PMACRO_OB_DDLL_LONG_DQ_RANK1_0_OB_DDLL_LONG_DQ_RANK1_BYTE1_SHIFT \
+	16
+#define EMC_PMACRO_OB_DDLL_LONG_DQ_RANK1_0_OB_DDLL_LONG_DQ_RANK1_BYTE1_MASK  \
+	0x3ff <<							     \
+	EMC_PMACRO_OB_DDLL_LONG_DQ_RANK1_0_OB_DDLL_LONG_DQ_RANK1_BYTE1_SHIFT
+#define EMC_PMACRO_OB_DDLL_LONG_DQ_RANK1_0_OB_DDLL_LONG_DQ_RANK1_BYTE0_SHIFT \
+	0
+#define EMC_PMACRO_OB_DDLL_LONG_DQ_RANK1_0_OB_DDLL_LONG_DQ_RANK1_BYTE0_MASK  \
+	0x3ff <<							     \
+	EMC_PMACRO_OB_DDLL_LONG_DQ_RANK1_0_OB_DDLL_LONG_DQ_RANK1_BYTE0_SHIFT
+
+#define EMC_PMACRO_OB_DDLL_LONG_DQ_RANK1_1			0x664
+#define EMC_PMACRO_OB_DDLL_LONG_DQ_RANK1_1_OB_DDLL_LONG_DQ_RANK1_BYTE3_SHIFT \
+	16
+#define EMC_PMACRO_OB_DDLL_LONG_DQ_RANK1_1_OB_DDLL_LONG_DQ_RANK1_BYTE3_MASK  \
+	0x3ff <<							     \
+	EMC_PMACRO_OB_DDLL_LONG_DQ_RANK1_1_OB_DDLL_LONG_DQ_RANK1_BYTE3_SHIFT
+#define EMC_PMACRO_OB_DDLL_LONG_DQ_RANK1_1_OB_DDLL_LONG_DQ_RANK1_BYTE2_SHIFT \
+	0
+#define EMC_PMACRO_OB_DDLL_LONG_DQ_RANK1_1_OB_DDLL_LONG_DQ_RANK1_BYTE2_MASK  \
+	0x3ff <<							     \
+	EMC_PMACRO_OB_DDLL_LONG_DQ_RANK1_1_OB_DDLL_LONG_DQ_RANK1_BYTE2_SHIFT
+
+#define EMC_PMACRO_OB_DDLL_LONG_DQ_RANK1_2			0x668
+#define EMC_PMACRO_OB_DDLL_LONG_DQ_RANK1_2_OB_DDLL_LONG_DQ_RANK1_BYTE5_SHIFT \
+	16
+#define EMC_PMACRO_OB_DDLL_LONG_DQ_RANK1_2_OB_DDLL_LONG_DQ_RANK1_BYTE5_MASK  \
+	0x3ff <<							     \
+	EMC_PMACRO_OB_DDLL_LONG_DQ_RANK1_2_OB_DDLL_LONG_DQ_RANK1_BYTE5_SHIFT
+#define EMC_PMACRO_OB_DDLL_LONG_DQ_RANK1_2_OB_DDLL_LONG_DQ_RANK1_BYTE4_SHIFT \
+	0
+#define EMC_PMACRO_OB_DDLL_LONG_DQ_RANK1_2_OB_DDLL_LONG_DQ_RANK1_BYTE4_MASK  \
+	0x3ff <<							     \
+	EMC_PMACRO_OB_DDLL_LONG_DQ_RANK1_2_OB_DDLL_LONG_DQ_RANK1_BYTE4_SHIFT
+
+#define EMC_PMACRO_OB_DDLL_LONG_DQ_RANK1_3			0x66c
+#define EMC_PMACRO_OB_DDLL_LONG_DQ_RANK1_3_OB_DDLL_LONG_DQ_RANK1_BYTE7_SHIFT \
+	16
+#define EMC_PMACRO_OB_DDLL_LONG_DQ_RANK1_3_OB_DDLL_LONG_DQ_RANK1_BYTE7_MASK  \
+	0x3ff <<							     \
+	EMC_PMACRO_OB_DDLL_LONG_DQ_RANK1_3_OB_DDLL_LONG_DQ_RANK1_BYTE7_SHIFT
+#define EMC_PMACRO_OB_DDLL_LONG_DQ_RANK1_3_OB_DDLL_LONG_DQ_RANK1_BYTE6_SHIFT \
+	0
+#define EMC_PMACRO_OB_DDLL_LONG_DQ_RANK1_3_OB_DDLL_LONG_DQ_RANK1_BYTE6_MASK  \
+	0x3ff <<							     \
+	EMC_PMACRO_OB_DDLL_LONG_DQ_RANK1_3_OB_DDLL_LONG_DQ_RANK1_BYTE6_SHIFT
+
+#define EMC_PMACRO_OB_DDLL_LONG_DQ_RANK1_4			0x670
+#define EMC_PMACRO_OB_DDLL_LONG_DQ_RANK1_5			0x674
+#define EMC_PMACRO_OB_DDLL_LONG_DQS_RANK0_0			0x680
+#define EMC_PMACRO_OB_DDLL_LONG_DQS_RANK0_1			0x684
+#define EMC_PMACRO_OB_DDLL_LONG_DQS_RANK0_2			0x688
+#define EMC_PMACRO_OB_DDLL_LONG_DQS_RANK0_3			0x68c
+#define EMC_PMACRO_OB_DDLL_LONG_DQS_RANK0_4			0x690
+#define EMC_PMACRO_OB_DDLL_LONG_DQS_RANK0_5			0x694
+#define EMC_PMACRO_OB_DDLL_LONG_DQS_RANK1_0			0x6a0
+#define EMC_PMACRO_OB_DDLL_LONG_DQS_RANK1_1			0x6a4
+#define EMC_PMACRO_OB_DDLL_LONG_DQS_RANK1_2			0x6a8
+#define EMC_PMACRO_OB_DDLL_LONG_DQS_RANK1_3			0x6ac
+#define EMC_PMACRO_OB_DDLL_LONG_DQS_RANK1_4			0x6b0
+#define EMC_PMACRO_OB_DDLL_LONG_DQS_RANK1_5			0x6b4
+#define EMC_PMACRO_IB_DDLL_LONG_DQS_RANK0_0			0x6c0
+#define EMC_PMACRO_IB_DDLL_LONG_DQS_RANK0_1			0x6c4
+#define EMC_PMACRO_IB_DDLL_LONG_DQS_RANK0_2			0x6c8
+#define EMC_PMACRO_IB_DDLL_LONG_DQS_RANK0_3			0x6cc
+#define EMC_PMACRO_IB_DDLL_LONG_DQS_RANK0_4			0x6d0
+#define EMC_PMACRO_IB_DDLL_LONG_DQS_RANK0_5			0x6d4
+#define EMC_PMACRO_IB_DDLL_LONG_DQS_RANK1_0			0x6e0
+#define EMC_PMACRO_IB_DDLL_LONG_DQS_RANK1_1			0x6e4
+#define EMC_PMACRO_IB_DDLL_LONG_DQS_RANK1_2			0x6e8
+#define EMC_PMACRO_IB_DDLL_LONG_DQS_RANK1_3			0x6ec
+#define EMC_PMACRO_IB_DDLL_LONG_DQS_RANK1_4			0x6f0
+#define EMC_PMACRO_IB_DDLL_LONG_DQS_RANK1_5			0x6f4
+#define EMC_PMACRO_TX_PWRD_0					0x720
+#define EMC_PMACRO_TX_PWRD_1					0x724
+#define EMC_PMACRO_TX_PWRD_2					0x728
+#define EMC_PMACRO_TX_PWRD_3					0x72c
+#define EMC_PMACRO_TX_PWRD_4					0x730
+#define EMC_PMACRO_TX_PWRD_5					0x734
+#define EMC_PMACRO_TX_SEL_CLK_SRC_0				0x740
+#define EMC_PMACRO_TX_SEL_CLK_SRC_1				0x744
+#define EMC_PMACRO_TX_SEL_CLK_SRC_3				0x74c
+#define EMC_PMACRO_TX_SEL_CLK_SRC_2				0x748
+#define EMC_PMACRO_TX_SEL_CLK_SRC_4				0x750
+#define EMC_PMACRO_TX_SEL_CLK_SRC_5				0x754
+#define EMC_PMACRO_DDLL_BYPASS					0x760
+#define EMC_PMACRO_DDLL_PWRD_0					0x770
+#define EMC_PMACRO_DDLL_PWRD_1					0x774
+#define EMC_PMACRO_DDLL_PWRD_2					0x778
+#define EMC_PMACRO_CMD_CTRL_0					0x780
+#define EMC_PMACRO_CMD_CTRL_1					0x784
+#define EMC_PMACRO_CMD_CTRL_2					0x788
+#define EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK0_BYTE0_0		0x800
+#define EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK0_BYTE0_1		0x804
+#define EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK0_BYTE0_2		0x808
+#define EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK0_BYTE0_3		0x80c
+#define EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK0_BYTE1_0		0x810
+#define EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK0_BYTE1_1		0x814
+#define EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK0_BYTE1_2		0x818
+#define EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK0_BYTE1_3		0x81c
+#define EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK0_BYTE2_0		0x820
+#define EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK0_BYTE2_1		0x824
+#define EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK0_BYTE2_2		0x828
+#define EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK0_BYTE2_3		0x82c
+#define EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK0_BYTE3_0		0x830
+#define EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK0_BYTE3_1		0x834
+#define EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK0_BYTE3_2		0x838
+#define EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK0_BYTE3_3		0x83c
+#define EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK0_BYTE4_0		0x840
+#define EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK0_BYTE4_1		0x844
+#define EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK0_BYTE4_2		0x848
+#define EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK0_BYTE4_3		0x84c
+#define EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK0_BYTE5_0		0x850
+#define EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK0_BYTE5_1		0x854
+#define EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK0_BYTE5_2		0x858
+#define EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK0_BYTE5_3		0x85c
+#define EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK0_BYTE6_0		0x860
+#define EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK0_BYTE6_1		0x864
+#define EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK0_BYTE6_2		0x868
+#define EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK0_BYTE6_3		0x86c
+#define EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK0_BYTE7_0		0x870
+#define EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK0_BYTE7_1		0x874
+#define EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK0_BYTE7_2		0x878
+#define EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK0_BYTE7_3		0x87c
+#define EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK0_CMD0_0		0x880
+#define EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK0_CMD0_1		0x884
+#define EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK0_CMD0_2		0x888
+#define EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK0_CMD0_3		0x88c
+#define EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK0_CMD1_0		0x890
+#define EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK0_CMD1_1		0x894
+#define EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK0_CMD1_2		0x898
+#define EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK0_CMD1_3		0x89c
+#define EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK0_CMD2_0		0x8a0
+#define EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK0_CMD2_1		0x8a4
+#define EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK0_CMD2_2		0x8a8
+#define EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK0_CMD2_3		0x8ac
+#define EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK0_CMD3_0		0x8b0
+#define EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK0_CMD3_1		0x8b4
+#define EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK0_CMD3_2		0x8b8
+#define EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK0_CMD3_3		0x8bc
+#define EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK1_BYTE0_0		0x900
+#define EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK1_BYTE0_1		0x904
+#define EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK1_BYTE0_2		0x908
+#define EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK1_BYTE0_3		0x90c
+#define EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK1_BYTE1_0		0x910
+#define EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK1_BYTE1_1		0x914
+#define EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK1_BYTE1_2		0x918
+#define EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK1_BYTE1_3		0x91c
+#define EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK1_BYTE2_0		0x920
+#define EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK1_BYTE2_1		0x924
+#define EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK1_BYTE2_2		0x928
+#define EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK1_BYTE2_3		0x92c
+#define EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK1_BYTE3_0		0x930
+#define EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK1_BYTE3_1		0x934
+#define EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK1_BYTE3_2		0x938
+#define EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK1_BYTE3_3		0x93c
+#define EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK1_BYTE4_0		0x940
+#define EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK1_BYTE4_1		0x944
+#define EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK1_BYTE4_2		0x948
+#define EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK1_BYTE4_3		0x94c
+#define EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK1_BYTE5_0		0x950
+#define EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK1_BYTE5_1		0x954
+#define EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK1_BYTE5_2		0x958
+#define EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK1_BYTE5_3		0x95c
+#define EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK1_BYTE6_0		0x960
+#define EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK1_BYTE6_1		0x964
+#define EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK1_BYTE6_2		0x968
+#define EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK1_BYTE6_3		0x96c
+#define EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK1_BYTE7_0		0x970
+#define EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK1_BYTE7_1		0x974
+#define EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK1_BYTE7_2		0x978
+#define EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK1_BYTE7_3		0x97c
+#define EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK1_CMD0_0		0x980
+#define EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK1_CMD0_1		0x984
+#define EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK1_CMD0_2		0x988
+#define EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK1_CMD0_3		0x98c
+#define EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK1_CMD1_0		0x990
+#define EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK1_CMD1_1		0x994
+#define EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK1_CMD1_2		0x998
+#define EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK1_CMD1_3		0x99c
+#define EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK1_CMD2_0		0x9a0
+#define EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK1_CMD2_1		0x9a4
+#define EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK1_CMD2_2		0x9a8
+#define EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK1_CMD2_3		0x9ac
+#define EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK1_CMD3_0		0x9b0
+#define EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK1_CMD3_1		0x9b4
+#define EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK1_CMD3_2		0x9b8
+#define EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK1_CMD3_3		0x9bc
+#define EMC_PMACRO_IB_DDLL_SHORT_DQ_RANK0_BYTE0_0		0xa00
+#define EMC_PMACRO_IB_DDLL_SHORT_DQ_RANK0_BYTE0_1		0xa04
+#define EMC_PMACRO_IB_DDLL_SHORT_DQ_RANK0_BYTE0_2		0xa08
+#define EMC_PMACRO_IB_DDLL_SHORT_DQ_RANK0_BYTE1_0		0xa10
+#define EMC_PMACRO_IB_DDLL_SHORT_DQ_RANK0_BYTE1_1		0xa14
+#define EMC_PMACRO_IB_DDLL_SHORT_DQ_RANK0_BYTE1_2		0xa18
+#define EMC_PMACRO_IB_DDLL_SHORT_DQ_RANK0_BYTE2_0		0xa20
+#define EMC_PMACRO_IB_DDLL_SHORT_DQ_RANK0_BYTE2_1		0xa24
+#define EMC_PMACRO_IB_DDLL_SHORT_DQ_RANK0_BYTE2_2		0xa28
+#define EMC_PMACRO_IB_DDLL_SHORT_DQ_RANK0_BYTE3_0		0xa30
+#define EMC_PMACRO_IB_DDLL_SHORT_DQ_RANK0_BYTE3_1		0xa34
+#define EMC_PMACRO_IB_DDLL_SHORT_DQ_RANK0_BYTE3_2		0xa38
+#define EMC_PMACRO_IB_DDLL_SHORT_DQ_RANK0_BYTE4_0		0xa40
+#define EMC_PMACRO_IB_DDLL_SHORT_DQ_RANK0_BYTE4_1		0xa44
+#define EMC_PMACRO_IB_DDLL_SHORT_DQ_RANK0_BYTE4_2		0xa48
+#define EMC_PMACRO_IB_DDLL_SHORT_DQ_RANK0_BYTE5_0		0xa50
+#define EMC_PMACRO_IB_DDLL_SHORT_DQ_RANK0_BYTE5_1		0xa54
+#define EMC_PMACRO_IB_DDLL_SHORT_DQ_RANK0_BYTE5_2		0xa58
+#define EMC_PMACRO_IB_DDLL_SHORT_DQ_RANK0_BYTE6_0		0xa60
+#define EMC_PMACRO_IB_DDLL_SHORT_DQ_RANK0_BYTE6_1		0xa64
+#define EMC_PMACRO_IB_DDLL_SHORT_DQ_RANK0_BYTE6_2		0xa68
+#define EMC_PMACRO_IB_DDLL_SHORT_DQ_RANK0_BYTE7_0		0xa70
+#define EMC_PMACRO_IB_DDLL_SHORT_DQ_RANK0_BYTE7_1		0xa74
+#define EMC_PMACRO_IB_DDLL_SHORT_DQ_RANK0_BYTE7_2		0xa78
+#define EMC_PMACRO_IB_DDLL_SHORT_DQ_RANK0_CMD0_0		0xa80
+#define EMC_PMACRO_IB_DDLL_SHORT_DQ_RANK0_CMD0_1		0xa84
+#define EMC_PMACRO_IB_DDLL_SHORT_DQ_RANK0_CMD0_2		0xa88
+#define EMC_PMACRO_IB_DDLL_SHORT_DQ_RANK0_CMD1_0		0xa90
+#define EMC_PMACRO_IB_DDLL_SHORT_DQ_RANK0_CMD1_1		0xa94
+#define EMC_PMACRO_IB_DDLL_SHORT_DQ_RANK0_CMD1_2		0xa98
+#define EMC_PMACRO_IB_DDLL_SHORT_DQ_RANK0_CMD2_0		0xaa0
+#define EMC_PMACRO_IB_DDLL_SHORT_DQ_RANK0_CMD2_1		0xaa4
+#define EMC_PMACRO_IB_DDLL_SHORT_DQ_RANK0_CMD2_2		0xaa8
+#define EMC_PMACRO_IB_DDLL_SHORT_DQ_RANK0_CMD3_0		0xab0
+#define EMC_PMACRO_IB_DDLL_SHORT_DQ_RANK0_CMD3_1		0xab4
+#define EMC_PMACRO_IB_DDLL_SHORT_DQ_RANK0_CMD3_2		0xab8
+#define EMC_PMACRO_IB_DDLL_SHORT_DQ_RANK1_BYTE0_0		0xb00
+#define EMC_PMACRO_IB_DDLL_SHORT_DQ_RANK1_BYTE0_1		0xb04
+#define EMC_PMACRO_IB_DDLL_SHORT_DQ_RANK1_BYTE0_2		0xb08
+#define EMC_PMACRO_IB_DDLL_SHORT_DQ_RANK1_BYTE1_0		0xb10
+#define EMC_PMACRO_IB_DDLL_SHORT_DQ_RANK1_BYTE1_1		0xb14
+#define EMC_PMACRO_IB_DDLL_SHORT_DQ_RANK1_BYTE1_2		0xb18
+#define EMC_PMACRO_IB_DDLL_SHORT_DQ_RANK1_BYTE2_0		0xb20
+#define EMC_PMACRO_IB_DDLL_SHORT_DQ_RANK1_BYTE2_1		0xb24
+#define EMC_PMACRO_IB_DDLL_SHORT_DQ_RANK1_BYTE2_2		0xb28
+#define EMC_PMACRO_IB_DDLL_SHORT_DQ_RANK1_BYTE3_0		0xb30
+#define EMC_PMACRO_IB_DDLL_SHORT_DQ_RANK1_BYTE3_1		0xb34
+#define EMC_PMACRO_IB_DDLL_SHORT_DQ_RANK1_BYTE3_2		0xb38
+#define EMC_PMACRO_IB_DDLL_SHORT_DQ_RANK1_BYTE4_0		0xb40
+#define EMC_PMACRO_IB_DDLL_SHORT_DQ_RANK1_BYTE4_1		0xb44
+#define EMC_PMACRO_IB_DDLL_SHORT_DQ_RANK1_BYTE4_2		0xb48
+#define EMC_PMACRO_IB_DDLL_SHORT_DQ_RANK1_BYTE5_0		0xb50
+#define EMC_PMACRO_IB_DDLL_SHORT_DQ_RANK1_BYTE5_1		0xb54
+#define EMC_PMACRO_IB_DDLL_SHORT_DQ_RANK1_BYTE5_2		0xb58
+#define EMC_PMACRO_IB_DDLL_SHORT_DQ_RANK1_BYTE6_0		0xb60
+#define EMC_PMACRO_IB_DDLL_SHORT_DQ_RANK1_BYTE6_1		0xb64
+#define EMC_PMACRO_IB_DDLL_SHORT_DQ_RANK1_BYTE6_2		0xb68
+#define EMC_PMACRO_IB_DDLL_SHORT_DQ_RANK1_BYTE7_0		0xb70
+#define EMC_PMACRO_IB_DDLL_SHORT_DQ_RANK1_BYTE7_1		0xb74
+#define EMC_PMACRO_IB_DDLL_SHORT_DQ_RANK1_BYTE7_2		0xb78
+#define EMC_PMACRO_IB_DDLL_SHORT_DQ_RANK1_CMD0_0		0xb80
+#define EMC_PMACRO_IB_DDLL_SHORT_DQ_RANK1_CMD0_1		0xb84
+#define EMC_PMACRO_IB_DDLL_SHORT_DQ_RANK1_CMD0_2		0xb88
+#define EMC_PMACRO_IB_DDLL_SHORT_DQ_RANK1_CMD1_0		0xb90
+#define EMC_PMACRO_IB_DDLL_SHORT_DQ_RANK1_CMD1_1		0xb94
+#define EMC_PMACRO_IB_DDLL_SHORT_DQ_RANK1_CMD1_2		0xb98
+#define EMC_PMACRO_IB_DDLL_SHORT_DQ_RANK1_CMD2_0		0xba0
+#define EMC_PMACRO_IB_DDLL_SHORT_DQ_RANK1_CMD2_1		0xba4
+#define EMC_PMACRO_IB_DDLL_SHORT_DQ_RANK1_CMD2_2		0xba8
+#define EMC_PMACRO_IB_DDLL_SHORT_DQ_RANK1_CMD3_0		0xbb0
+#define EMC_PMACRO_IB_DDLL_SHORT_DQ_RANK1_CMD3_1		0xbb4
+#define EMC_PMACRO_IB_DDLL_SHORT_DQ_RANK1_CMD3_2		0xbb8
+#define EMC_PMACRO_IB_VREF_DQ_0					0xbe0
+#define EMC_PMACRO_IB_VREF_DQ_1					0xbe4
+#define EMC_PMACRO_IB_VREF_DQ_2					0xbe8
+#define EMC_PMACRO_IB_VREF_DQS_0				0xbf0
+#define EMC_PMACRO_IB_VREF_DQS_1				0xbf4
+#define EMC_PMACRO_IB_VREF_DQS_2				0xbf8
+#define EMC_PMACRO_IB_RXRT					0xcf4
+#define EMC_PMACRO_DDLL_LONG_CMD_0				0xc00
+#define EMC_PMACRO_DDLL_LONG_CMD_1				0xc04
+#define EMC_PMACRO_DDLL_LONG_CMD_2				0xc08
+#define EMC_PMACRO_DDLL_LONG_CMD_3				0xc0c
+#define EMC_PMACRO_DDLL_LONG_CMD_4				0xc10
+#define EMC_PMACRO_DDLL_LONG_CMD_5				0xc14
+#define EMC_PMACRO_DDLL_SHORT_CMD_0				0xc20
+#define EMC_PMACRO_DDLL_SHORT_CMD_1				0xc24
+#define EMC_PMACRO_DDLL_SHORT_CMD_2				0xc28
+#define EMC_PMACRO_CFG_PM_GLOBAL_0				0xc30
+#define EMC_PMACRO_CFG_PM_GLOBAL_0_DISABLE_CFG_BYTE0		(1 << 16)
+#define EMC_PMACRO_CFG_PM_GLOBAL_0_DISABLE_CFG_BYTE1		(1 << 17)
+#define EMC_PMACRO_CFG_PM_GLOBAL_0_DISABLE_CFG_BYTE2		(1 << 18)
+#define EMC_PMACRO_CFG_PM_GLOBAL_0_DISABLE_CFG_BYTE3		(1 << 19)
+#define EMC_PMACRO_CFG_PM_GLOBAL_0_DISABLE_CFG_BYTE4		(1 << 20)
+#define EMC_PMACRO_CFG_PM_GLOBAL_0_DISABLE_CFG_BYTE5		(1 << 21)
+#define EMC_PMACRO_CFG_PM_GLOBAL_0_DISABLE_CFG_BYTE6		(1 << 22)
+#define EMC_PMACRO_CFG_PM_GLOBAL_0_DISABLE_CFG_BYTE7		(1 << 23)
+#define EMC_PMACRO_CFG_PM_GLOBAL_0_DISABLE_CFG_CMD0		(1 << 24)
+#define EMC_PMACRO_CFG_PM_GLOBAL_0_DISABLE_CFG_CMD1		(1 << 25)
+#define EMC_PMACRO_CFG_PM_GLOBAL_0_DISABLE_CFG_CMD2		(1 << 26)
+#define EMC_PMACRO_CFG_PM_GLOBAL_0_DISABLE_CFG_CMD3		(1 << 27)
+
+#define EMC_PMACRO_VTTGEN_CTRL_0				0xc34
+#define EMC_PMACRO_VTTGEN_CTRL_1				0xc38
+#define EMC_PMACRO_VTTGEN_CTRL_2				0xcf0
+#define EMC_PMACRO_BG_BIAS_CTRL_0				0xc3c
+#define EMC_PMACRO_BG_BIAS_CTRL_0_BG_E_PWRD			(1 << 0)
+#define EMC_PMACRO_BG_BIAS_CTRL_0_BG_MODE			(1 << 1)
+#define EMC_PMACRO_BG_BIAS_CTRL_0_BGLP_E_PWRD			(1 << 2)
+
+#define EMC_PMACRO_PAD_CFG_CTRL					0xc40
+#define EMC_PMACRO_CMD_PAD_RX_CTRL				0xc50
+#define EMC_PMACRO_DATA_PAD_RX_CTRL				0xc54
+#define EMC_PMACRO_CMD_RX_TERM_MODE				0xc58
+#define EMC_PMACRO_DATA_RX_TERM_MODE				0xc5c
+#define EMC_PMACRO_DATA_RX_TERM_MODE_DATA_DQSN_RX_TERM_MODE_SHIFT 8
+#define EMC_PMACRO_DATA_RX_TERM_MODE_DATA_DQSN_RX_TERM_MODE_MASK (0x3 << \
+	EMC_PMACRO_DATA_RX_TERM_MODE_DATA_DQSN_RX_TERM_MODE_SHIFT)
+#define EMC_PMACRO_DATA_RX_TERM_MODE_DATA_DQSP_RX_TERM_MODE_SHIFT 4
+#define EMC_PMACRO_DATA_RX_TERM_MODE_DATA_DQSP_RX_TERM_MODE_MASK (0x3 << \
+	EMC_PMACRO_DATA_RX_TERM_MODE_DATA_DQSP_RX_TERM_MODE_SHIFT)
+#define EMC_PMACRO_DATA_RX_TERM_MODE_DATA_DQ_RX_TERM_MODE_SHIFT   0
+#define EMC_PMACRO_DATA_RX_TERM_MODE_DATA_DQ_RX_TERM_MODE_MASK   (0x3 << \
+	EMC_PMACRO_DATA_RX_TERM_MODE_DATA_DQ_RX_TERM_MODE_SHIFT)
+
+#define RX_TERM_MODE							\
+	~(EMC_PMACRO_DATA_RX_TERM_MODE_DATA_DQSN_RX_TERM_MODE_MASK |	\
+	  EMC_PMACRO_DATA_RX_TERM_MODE_DATA_DQSP_RX_TERM_MODE_MASK |	\
+	  EMC_PMACRO_DATA_RX_TERM_MODE_DATA_DQ_RX_TERM_MODE_MASK)
+
+#define EMC_PMACRO_CMD_PAD_TX_CTRL				0xc60
+#define EMC_PMACRO_CMD_PAD_TX_CTRL_CMD_DQ_TX_E_DCC		(1 << 1)
+#define EMC_PMACRO_CMD_PAD_TX_CTRL_CMD_DQSP_TX_E_DCC		(1 << 9)
+#define EMC_PMACRO_CMD_PAD_TX_CTRL_CMD_DQSN_TX_E_DCC		(1 << 16)
+#define EMC_PMACRO_CMD_PAD_TX_CTRL_CMD_CMD_TX_E_DCC		(1 << 24)
+#define EMC_PMACRO_CMD_PAD_TX_CTRL_CMD_DQ_TX_DRVFORCEON		(1 << 26)
+
+#define EMC_PMACRO_DATA_PAD_TX_CTRL				0xc64
+#define EMC_PMACRO_DATA_PAD_TX_CTRL_DATA_DQ_E_IVREF		(1 << 0)
+#define EMC_PMACRO_DATA_PAD_TX_CTRL_DATA_DQ_TX_E_DCC		(1 << 1)
+#define EMC_PMACRO_DATA_PAD_TX_CTRL_DATA_DQS_E_IVREF		(1 << 8)
+#define EMC_PMACRO_DATA_PAD_TX_CTRL_DATA_DQSP_TX_E_DCC		(1 << 9)
+#define EMC_PMACRO_DATA_PAD_TX_CTRL_DATA_DQSN_TX_E_DCC		(1 << 16)
+#define EMC_PMACRO_DATA_PAD_TX_CTRL_DATA_CMD_TX_E_DCC		(1 << 24)
+
+#define EMC_PMACRO_COMMON_PAD_TX_CTRL				0xc68
+#define EMC_PMACRO_BRICK_MAPPING_0				0xc80
+#define EMC_PMACRO_BRICK_MAPPING_1				0xc84
+#define EMC_PMACRO_BRICK_MAPPING_2				0xc88
+#define EMC_PMACRO_DDLLCAL_CAL					0xce0
+#define EMC_PMACRO_DDLL_OFFSET					0xce4
+#define EMC_PMACRO_DDLL_PERIODIC_OFFSET				0xce8
+#define EMC_PMACRO_BRICK_CTRL_RFU1				0x330
+#define EMC_PMACRO_BRICK_CTRL_RFU2				0x334
+#define EMC_PMACRO_CMD_BRICK_CTRL_FDPD				0x318
+#define EMC_PMACRO_DATA_BRICK_CTRL_FDPD				0x31c
+#define EMC_PMACRO_TRAINING_CTRL_0				0xcf8
+#define EMC_PMACRO_TRAINING_CTRL_0_CH0_TRAINING_E_WRPTR		(1 << 3)
+
+#define EMC_PMACRO_TRAINING_CTRL_1				0xcfc
+#define EMC_PMACRO_TRAINING_CTRL_1_CH1_TRAINING_E_WRPTR		(1 << 3)
+
+#define EMC_PMC_SCRATCH1					0x440
+#define EMC_PMC_SCRATCH2					0x444
+#define EMC_PMC_SCRATCH3					0x448
+
+
+#define MC_EMEM_ARB_MISC0_EMC_SAME_FREQ				(1 << 27)
+#define MC_EMEM_ARB_MISC1					0xdc
+#define MC_EMEM_ARB_RING1_THROTTLE				0xe0
+#define MC_EMEM_ARB_RING3_THROTTLE				0xe4
+#define MC_EMEM_ARB_OVERRIDE					0xe8
+#define MC_EMEM_ARB_RSV						0xec
+
+#define MC_CLKEN_OVERRIDE					0xf4
+#define MC_TIMING_CONTROL_DBG					0xf8
+#define MC_TIMING_CONTROL					0xfc
+#define MC_EMEM_ARB_ISOCHRONOUS_0				0x208
+#define MC_EMEM_ARB_ISOCHRONOUS_1				0x20c
+#define MC_EMEM_ARB_ISOCHRONOUS_2				0x210
+#define MC_EMEM_ARB_HYSTERESIS_0_0				0x218
+#define MC_EMEM_ARB_HYSTERESIS_1_0				0x21c
+#define MC_EMEM_ARB_HYSTERESIS_2_0				0x220
+#define MC_EMEM_ARB_HYSTERESIS_3_0				0x224
+
+#define HYST_SATAR						(0x1 << 31)
+#define HYST_PPCSAHBSLVR					(0x1 << 30)
+#define HYST_PPCSAHBDMAR					(0x1 << 29)
+#define HYST_NVENCSRD						(0x1 << 28)
+#define HYST_HOST1XR						(0x1 << 23)
+#define HYST_HOST1XDMAR						(0x1 << 22)
+#define HYST_HDAR						(0x1 << 21)
+#define HYST_DISPLAYHCB						(0x1 << 17)
+#define HYST_DISPLAYHC						(0x1 << 16)
+#define HYST_AVPCARM7R						(0x1 << 15)
+#define HYST_AFIR						(0x1 << 14)
+#define HYST_DISPLAY0CB						(0x1 << 6)
+#define HYST_DISPLAY0C						(0x1 << 5)
+#define HYST_DISPLAY0BB						(0x1 << 4)
+#define HYST_DISPLAY0B						(0x1 << 3)
+#define HYST_DISPLAY0AB						(0x1 << 2)
+#define HYST_DISPLAY0A						(0x1 << 1)
+#define HYST_PTCR						(0x1 << 0)
+
+#define HYST_VDEDBGW						(0x1 << 31)
+#define HYST_VDEBSEVW						(0x1 << 30)
+#define HYST_SATAW						(0x1 << 29)
+#define HYST_PPCSAHBSLVW					(0x1 << 28)
+#define HYST_PPCSAHBDMAW					(0x1 << 27)
+#define HYST_MPCOREW						(0x1 << 25)
+#define HYST_MPCORELPW						(0x1 << 24)
+#define HYST_HOST1XW						(0x1 << 22)
+#define HYST_HDAW						(0x1 << 21)
+#define HYST_AVPCARM7W						(0x1 << 18)
+#define HYST_AFIW						(0x1 << 17)
+#define HYST_NVENCSWR						(0x1 << 11)
+#define HYST_MPCORER						(0x1 << 7)
+#define HYST_MPCORELPR						(0x1 << 6)
+#define HYST_VDETPER						(0x1 << 5)
+#define HYST_VDEMCER						(0x1 << 4)
+#define HYST_VDEMBER						(0x1 << 3)
+#define HYST_VDEBSEVR						(0x1 << 2)
+
+#define HYST_DISPLAYT						(0x1 << 26)
+#define HYST_GPUSWR						(0x1 << 25)
+#define HYST_GPUSRD						(0x1 << 24)
+#define HYST_A9AVPSCW						(0x1 << 23)
+#define HYST_A9AVPSCR						(0x1 << 22)
+#define HYST_TSECSWR						(0x1 << 21)
+#define HYST_TSECSRD						(0x1 << 20)
+#define HYST_ISPWBB						(0x1 << 17)
+#define HYST_ISPWAB						(0x1 << 16)
+#define HYST_ISPRAB						(0x1 << 14)
+#define HYST_XUSB_DEVW						(0x1 << 13)
+#define HYST_XUSB_DEVR						(0x1 << 12)
+#define HYST_XUSB_HOSTW						(0x1 << 11)
+#define HYST_XUSB_HOSTR						(0x1 << 10)
+#define HYST_ISPWB						(0x1 << 7)
+#define HYST_ISPWA						(0x1 << 6)
+#define HYST_ISPRA						(0x1 << 4)
+#define HYST_VDETPMW						(0x1 << 1)
+#define HYST_VDEMBEW						(0x1 << 0)
+
+#define HYST_DISPLAYD						(0x1 << 19)
+#define HYST_VIW						(0x1 << 18)
+#define HYST_VICSWR						(0x1 << 13)
+#define HYST_VICSRD						(0x1 << 12)
+#define HYST_SDMMCWAB						(0x1 << 7)
+#define HYST_SDMMCW						(0x1 << 6)
+#define HYST_SDMMCWAA						(0x1 << 5)
+#define HYST_SDMMCWA						(0x1 << 4)
+#define HYST_SDMMCRAB						(0x1 << 3)
+#define HYST_SDMMCR						(0x1 << 2)
+#define HYST_SDMMCRAA						(0x1 << 1)
+#define HYST_SDMMCRA						(0x1 << 0)8
+
+#define EMC_COPY_TABLE_PARAM_PERIODIC_FIELDS			BIT(0)
+#define EMC_COPY_TABLE_PARAM_TRIM_REGS				BIT(1)
+
+enum {
+	REG_MC,
+	REG_EMC,
+	REG_EMC0,
+	REG_EMC1,
+};
+
+#define BURST_REGS_PER_CH_LIST						\
+{									\
+	DEFINE_REG(REG_EMC0, EMC_MRW10),				\
+	DEFINE_REG(REG_EMC1, EMC_MRW10),				\
+	DEFINE_REG(REG_EMC0, EMC_MRW11),				\
+	DEFINE_REG(REG_EMC1, EMC_MRW11),				\
+	DEFINE_REG(REG_EMC0, EMC_MRW12),				\
+	DEFINE_REG(REG_EMC1, EMC_MRW12),				\
+	DEFINE_REG(REG_EMC0, EMC_MRW13),				\
+	DEFINE_REG(REG_EMC1, EMC_MRW13),				\
+}
+
+#define BURST_REGS_LIST							\
+{									\
+	DEFINE_REG(REG_EMC, EMC_RC),					\
+	DEFINE_REG(REG_EMC, EMC_RFC),					\
+	DEFINE_REG(REG_EMC, EMC_RFCPB),					\
+	DEFINE_REG(REG_EMC, EMC_REFCTRL2),				\
+	DEFINE_REG(REG_EMC, EMC_RFC_SLR),				\
+	DEFINE_REG(REG_EMC, EMC_RAS),					\
+	DEFINE_REG(REG_EMC, EMC_RP),					\
+	DEFINE_REG(REG_EMC, EMC_R2W),					\
+	DEFINE_REG(REG_EMC, EMC_W2R),					\
+	DEFINE_REG(REG_EMC, EMC_R2P),					\
+	DEFINE_REG(REG_EMC, EMC_W2P),					\
+	DEFINE_REG(REG_EMC, EMC_R2R),					\
+	DEFINE_REG(REG_EMC, EMC_TPPD),					\
+	DEFINE_REG(REG_EMC, EMC_CCDMW),					\
+	DEFINE_REG(REG_EMC, EMC_RD_RCD),				\
+	DEFINE_REG(REG_EMC, EMC_WR_RCD),				\
+	DEFINE_REG(REG_EMC, EMC_RRD),					\
+	DEFINE_REG(REG_EMC, EMC_REXT),					\
+	DEFINE_REG(REG_EMC, EMC_WEXT),					\
+	DEFINE_REG(REG_EMC, EMC_WDV_CHK),				\
+	DEFINE_REG(REG_EMC, EMC_WDV),					\
+	DEFINE_REG(REG_EMC, EMC_WSV),					\
+	DEFINE_REG(REG_EMC, EMC_WEV),					\
+	DEFINE_REG(REG_EMC, EMC_WDV_MASK),				\
+	DEFINE_REG(REG_EMC, EMC_WS_DURATION),				\
+	DEFINE_REG(REG_EMC, EMC_WE_DURATION),				\
+	DEFINE_REG(REG_EMC, EMC_QUSE),					\
+	DEFINE_REG(REG_EMC, EMC_QUSE_WIDTH),				\
+	DEFINE_REG(REG_EMC, EMC_IBDLY),					\
+	DEFINE_REG(REG_EMC, EMC_OBDLY),					\
+	DEFINE_REG(REG_EMC, EMC_EINPUT),				\
+	DEFINE_REG(REG_EMC, EMC_MRW6),					\
+	DEFINE_REG(REG_EMC, EMC_EINPUT_DURATION),			\
+	DEFINE_REG(REG_EMC, EMC_PUTERM_EXTRA),				\
+	DEFINE_REG(REG_EMC, EMC_PUTERM_WIDTH),				\
+	DEFINE_REG(REG_EMC, EMC_QRST),					\
+	DEFINE_REG(REG_EMC, EMC_QSAFE),					\
+	DEFINE_REG(REG_EMC, EMC_RDV),					\
+	DEFINE_REG(REG_EMC, EMC_RDV_MASK),				\
+	DEFINE_REG(REG_EMC, EMC_RDV_EARLY),				\
+	DEFINE_REG(REG_EMC, EMC_RDV_EARLY_MASK),			\
+	DEFINE_REG(REG_EMC, EMC_REFRESH),				\
+	DEFINE_REG(REG_EMC, EMC_BURST_REFRESH_NUM),			\
+	DEFINE_REG(REG_EMC, EMC_PRE_REFRESH_REQ_CNT),			\
+	DEFINE_REG(REG_EMC, EMC_PDEX2WR),				\
+	DEFINE_REG(REG_EMC, EMC_PDEX2RD),				\
+	DEFINE_REG(REG_EMC, EMC_PCHG2PDEN),				\
+	DEFINE_REG(REG_EMC, EMC_ACT2PDEN),				\
+	DEFINE_REG(REG_EMC, EMC_AR2PDEN),				\
+	DEFINE_REG(REG_EMC, EMC_RW2PDEN),				\
+	DEFINE_REG(REG_EMC, EMC_CKE2PDEN),				\
+	DEFINE_REG(REG_EMC, EMC_PDEX2CKE),				\
+	DEFINE_REG(REG_EMC, EMC_PDEX2MRR),				\
+	DEFINE_REG(REG_EMC, EMC_TXSR),					\
+	DEFINE_REG(REG_EMC, EMC_TXSRDLL),				\
+	DEFINE_REG(REG_EMC, EMC_TCKE),					\
+	DEFINE_REG(REG_EMC, EMC_TCKESR),				\
+	DEFINE_REG(REG_EMC, EMC_TPD),					\
+	DEFINE_REG(REG_EMC, EMC_TFAW),					\
+	DEFINE_REG(REG_EMC, EMC_TRPAB),					\
+	DEFINE_REG(REG_EMC, EMC_TCLKSTABLE),				\
+	DEFINE_REG(REG_EMC, EMC_TCLKSTOP),				\
+	DEFINE_REG(REG_EMC, EMC_MRW7),					\
+	DEFINE_REG(REG_EMC, EMC_TREFBW),				\
+	DEFINE_REG(REG_EMC, EMC_ODT_WRITE),				\
+	DEFINE_REG(REG_EMC, EMC_FBIO_CFG5),				\
+	DEFINE_REG(REG_EMC, EMC_FBIO_CFG7),				\
+	DEFINE_REG(REG_EMC, EMC_CFG_DIG_DLL),				\
+	DEFINE_REG(REG_EMC, EMC_CFG_DIG_DLL_PERIOD),			\
+	DEFINE_REG(REG_EMC, EMC_PMACRO_IB_RXRT),			\
+	DEFINE_REG(REG_EMC, EMC_CFG_PIPE_1),				\
+	DEFINE_REG(REG_EMC, EMC_CFG_PIPE_2),				\
+	DEFINE_REG(REG_EMC, EMC_PMACRO_QUSE_DDLL_RANK0_4),		\
+	DEFINE_REG(REG_EMC, EMC_PMACRO_QUSE_DDLL_RANK0_5),		\
+	DEFINE_REG(REG_EMC, EMC_PMACRO_QUSE_DDLL_RANK1_4),		\
+	DEFINE_REG(REG_EMC, EMC_PMACRO_QUSE_DDLL_RANK1_5),		\
+	DEFINE_REG(REG_EMC, EMC_MRW8),					\
+	DEFINE_REG(REG_EMC, EMC_PMACRO_OB_DDLL_LONG_DQ_RANK1_4),	\
+	DEFINE_REG(REG_EMC, EMC_PMACRO_OB_DDLL_LONG_DQ_RANK1_5),	\
+	DEFINE_REG(REG_EMC, EMC_PMACRO_OB_DDLL_LONG_DQS_RANK0_0),	\
+	DEFINE_REG(REG_EMC, EMC_PMACRO_OB_DDLL_LONG_DQS_RANK0_1),	\
+	DEFINE_REG(REG_EMC, EMC_PMACRO_OB_DDLL_LONG_DQS_RANK0_2),	\
+	DEFINE_REG(REG_EMC, EMC_PMACRO_OB_DDLL_LONG_DQS_RANK0_3),	\
+	DEFINE_REG(REG_EMC, EMC_PMACRO_OB_DDLL_LONG_DQS_RANK0_4),	\
+	DEFINE_REG(REG_EMC, EMC_PMACRO_OB_DDLL_LONG_DQS_RANK0_5),	\
+	DEFINE_REG(REG_EMC, EMC_PMACRO_OB_DDLL_LONG_DQS_RANK1_0),	\
+	DEFINE_REG(REG_EMC, EMC_PMACRO_OB_DDLL_LONG_DQS_RANK1_1),	\
+	DEFINE_REG(REG_EMC, EMC_PMACRO_OB_DDLL_LONG_DQS_RANK1_2),	\
+	DEFINE_REG(REG_EMC, EMC_PMACRO_OB_DDLL_LONG_DQS_RANK1_3),	\
+	DEFINE_REG(REG_EMC, EMC_PMACRO_OB_DDLL_LONG_DQS_RANK1_4),	\
+	DEFINE_REG(REG_EMC, EMC_PMACRO_OB_DDLL_LONG_DQS_RANK1_5),	\
+	DEFINE_REG(REG_EMC, EMC_PMACRO_DDLL_LONG_CMD_0),		\
+	DEFINE_REG(REG_EMC, EMC_PMACRO_DDLL_LONG_CMD_1),		\
+	DEFINE_REG(REG_EMC, EMC_PMACRO_DDLL_LONG_CMD_2),		\
+	DEFINE_REG(REG_EMC, EMC_PMACRO_DDLL_LONG_CMD_3),		\
+	DEFINE_REG(REG_EMC, EMC_PMACRO_DDLL_LONG_CMD_4),		\
+	DEFINE_REG(REG_EMC, EMC_PMACRO_DDLL_SHORT_CMD_0),		\
+	DEFINE_REG(REG_EMC, EMC_PMACRO_DDLL_SHORT_CMD_1),		\
+	DEFINE_REG(REG_EMC, EMC_PMACRO_DDLL_SHORT_CMD_2),		\
+	DEFINE_REG(REG_EMC, EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK0_BYTE0_3),	\
+	DEFINE_REG(REG_EMC, EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK0_BYTE1_3),	\
+	DEFINE_REG(REG_EMC, EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK0_BYTE2_3),	\
+	DEFINE_REG(REG_EMC, EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK0_BYTE3_3),	\
+	DEFINE_REG(REG_EMC, EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK0_BYTE4_3),	\
+	DEFINE_REG(REG_EMC, EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK0_BYTE5_3),	\
+	DEFINE_REG(REG_EMC, EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK0_BYTE6_3),	\
+	DEFINE_REG(REG_EMC, EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK0_BYTE7_3),	\
+	DEFINE_REG(REG_EMC, EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK0_CMD0_3),	\
+	DEFINE_REG(REG_EMC, EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK0_CMD1_3),	\
+	DEFINE_REG(REG_EMC, EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK0_CMD2_3),	\
+	DEFINE_REG(REG_EMC, EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK0_CMD3_3),	\
+	DEFINE_REG(REG_EMC, EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK1_BYTE0_3),	\
+	DEFINE_REG(REG_EMC, EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK1_BYTE1_3),	\
+	DEFINE_REG(REG_EMC, EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK1_BYTE2_3),	\
+	DEFINE_REG(REG_EMC, EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK1_BYTE3_3),	\
+	DEFINE_REG(REG_EMC, EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK1_BYTE4_3),	\
+	DEFINE_REG(REG_EMC, EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK1_BYTE5_3),	\
+	DEFINE_REG(REG_EMC, EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK1_BYTE6_3),	\
+	DEFINE_REG(REG_EMC, EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK1_BYTE7_3),	\
+	DEFINE_REG(REG_EMC, EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK1_CMD0_0),	\
+	DEFINE_REG(REG_EMC, EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK1_CMD0_1),	\
+	DEFINE_REG(REG_EMC, EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK1_CMD0_2),	\
+	DEFINE_REG(REG_EMC, EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK1_CMD0_3),	\
+	DEFINE_REG(REG_EMC, EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK1_CMD1_0),	\
+	DEFINE_REG(REG_EMC, EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK1_CMD1_1),	\
+	DEFINE_REG(REG_EMC, EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK1_CMD1_2),	\
+	DEFINE_REG(REG_EMC, EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK1_CMD1_3),	\
+	DEFINE_REG(REG_EMC, EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK1_CMD2_0),	\
+	DEFINE_REG(REG_EMC, EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK1_CMD2_1),	\
+	DEFINE_REG(REG_EMC, EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK1_CMD2_2),	\
+	DEFINE_REG(REG_EMC, EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK1_CMD2_3),	\
+	DEFINE_REG(REG_EMC, EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK1_CMD3_0),	\
+	DEFINE_REG(REG_EMC, EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK1_CMD3_1),	\
+	DEFINE_REG(REG_EMC, EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK1_CMD3_2),	\
+	DEFINE_REG(REG_EMC, EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK1_CMD3_3),	\
+	DEFINE_REG(REG_EMC, EMC_TXDSRVTTGEN),				\
+	DEFINE_REG(REG_EMC, EMC_FDPD_CTRL_DQ),				\
+	DEFINE_REG(REG_EMC, EMC_FDPD_CTRL_CMD),				\
+	DEFINE_REG(REG_EMC, EMC_FBIO_SPARE),				\
+	DEFINE_REG(REG_EMC, EMC_ZCAL_INTERVAL),				\
+	DEFINE_REG(REG_EMC, EMC_ZCAL_WAIT_CNT),				\
+	DEFINE_REG(REG_EMC, EMC_MRS_WAIT_CNT),				\
+	DEFINE_REG(REG_EMC, EMC_MRS_WAIT_CNT2),				\
+	DEFINE_REG(REG_EMC, EMC_AUTO_CAL_CHANNEL),			\
+	DEFINE_REG(REG_EMC, EMC_DLL_CFG_0),				\
+	DEFINE_REG(REG_EMC, EMC_DLL_CFG_1),				\
+	DEFINE_REG(REG_EMC, EMC_PMACRO_AUTOCAL_CFG_COMMON),		\
+	DEFINE_REG(REG_EMC, EMC_PMACRO_ZCTRL),				\
+	DEFINE_REG(REG_EMC, EMC_CFG),					\
+	DEFINE_REG(REG_EMC, EMC_CFG_PIPE),				\
+	DEFINE_REG(REG_EMC, EMC_DYN_SELF_REF_CONTROL),			\
+	DEFINE_REG(REG_EMC, EMC_QPOP),					\
+	DEFINE_REG(REG_EMC, EMC_DQS_BRLSHFT_0),				\
+	DEFINE_REG(REG_EMC, EMC_DQS_BRLSHFT_1),				\
+	DEFINE_REG(REG_EMC, EMC_CMD_BRLSHFT_2),				\
+	DEFINE_REG(REG_EMC, EMC_CMD_BRLSHFT_3),				\
+	DEFINE_REG(REG_EMC, EMC_PMACRO_PAD_CFG_CTRL),			\
+	DEFINE_REG(REG_EMC, EMC_PMACRO_DATA_PAD_RX_CTRL),		\
+	DEFINE_REG(REG_EMC, EMC_PMACRO_CMD_PAD_RX_CTRL),		\
+	DEFINE_REG(REG_EMC, EMC_PMACRO_DATA_RX_TERM_MODE),		\
+	DEFINE_REG(REG_EMC, EMC_PMACRO_CMD_RX_TERM_MODE),		\
+	DEFINE_REG(REG_EMC, EMC_PMACRO_CMD_PAD_TX_CTRL),		\
+	DEFINE_REG(REG_EMC, EMC_PMACRO_DATA_PAD_TX_CTRL),		\
+	DEFINE_REG(REG_EMC, EMC_PMACRO_COMMON_PAD_TX_CTRL),		\
+	DEFINE_REG(REG_EMC, EMC_PMACRO_VTTGEN_CTRL_0),			\
+	DEFINE_REG(REG_EMC, EMC_PMACRO_VTTGEN_CTRL_1),			\
+	DEFINE_REG(REG_EMC, EMC_PMACRO_VTTGEN_CTRL_2),			\
+	DEFINE_REG(REG_EMC, EMC_PMACRO_BRICK_CTRL_RFU1),		\
+	DEFINE_REG(REG_EMC, EMC_PMACRO_CMD_BRICK_CTRL_FDPD),		\
+	DEFINE_REG(REG_EMC, EMC_PMACRO_BRICK_CTRL_RFU2),		\
+	DEFINE_REG(REG_EMC, EMC_PMACRO_DATA_BRICK_CTRL_FDPD),		\
+	DEFINE_REG(REG_EMC, EMC_PMACRO_BG_BIAS_CTRL_0),			\
+	DEFINE_REG(REG_EMC, EMC_CFG_3),					\
+	DEFINE_REG(REG_EMC, EMC_PMACRO_TX_PWRD_0),			\
+	DEFINE_REG(REG_EMC, EMC_PMACRO_TX_PWRD_1),			\
+	DEFINE_REG(REG_EMC, EMC_PMACRO_TX_PWRD_2),			\
+	DEFINE_REG(REG_EMC, EMC_PMACRO_TX_PWRD_3),			\
+	DEFINE_REG(REG_EMC, EMC_PMACRO_TX_PWRD_4),			\
+	DEFINE_REG(REG_EMC, EMC_PMACRO_TX_PWRD_5),			\
+	DEFINE_REG(REG_EMC, EMC_CONFIG_SAMPLE_DELAY),			\
+	DEFINE_REG(REG_EMC, EMC_PMACRO_TX_SEL_CLK_SRC_0),		\
+	DEFINE_REG(REG_EMC, EMC_PMACRO_TX_SEL_CLK_SRC_1),		\
+	DEFINE_REG(REG_EMC, EMC_PMACRO_TX_SEL_CLK_SRC_2),		\
+	DEFINE_REG(REG_EMC, EMC_PMACRO_TX_SEL_CLK_SRC_3),		\
+	DEFINE_REG(REG_EMC, EMC_PMACRO_TX_SEL_CLK_SRC_4),		\
+	DEFINE_REG(REG_EMC, EMC_PMACRO_TX_SEL_CLK_SRC_5),		\
+	DEFINE_REG(REG_EMC, EMC_PMACRO_DDLL_BYPASS),			\
+	DEFINE_REG(REG_EMC, EMC_PMACRO_DDLL_PWRD_0),			\
+	DEFINE_REG(REG_EMC, EMC_PMACRO_DDLL_PWRD_1),			\
+	DEFINE_REG(REG_EMC, EMC_PMACRO_DDLL_PWRD_2),			\
+	DEFINE_REG(REG_EMC, EMC_PMACRO_CMD_CTRL_0),			\
+	DEFINE_REG(REG_EMC, EMC_PMACRO_CMD_CTRL_1),			\
+	DEFINE_REG(REG_EMC, EMC_PMACRO_CMD_CTRL_2),			\
+	DEFINE_REG(REG_EMC, EMC_TR_TIMING_0),				\
+	DEFINE_REG(REG_EMC, EMC_TR_DVFS),				\
+	DEFINE_REG(REG_EMC, EMC_TR_CTRL_1),				\
+	DEFINE_REG(REG_EMC, EMC_TR_RDV),				\
+	DEFINE_REG(REG_EMC, EMC_TR_QPOP),				\
+	DEFINE_REG(REG_EMC, EMC_TR_RDV_MASK),				\
+	DEFINE_REG(REG_EMC, EMC_MRW14),					\
+	DEFINE_REG(REG_EMC, EMC_TR_QSAFE),				\
+	DEFINE_REG(REG_EMC, EMC_TR_QRST),				\
+	DEFINE_REG(REG_EMC, EMC_TRAINING_CTRL),				\
+	DEFINE_REG(REG_EMC, EMC_TRAINING_SETTLE),			\
+	DEFINE_REG(REG_EMC, EMC_TRAINING_VREF_SETTLE),			\
+	DEFINE_REG(REG_EMC, EMC_TRAINING_CA_FINE_CTRL),			\
+	DEFINE_REG(REG_EMC, EMC_TRAINING_CA_CTRL_MISC),			\
+	DEFINE_REG(REG_EMC, EMC_TRAINING_CA_CTRL_MISC1),		\
+	DEFINE_REG(REG_EMC, EMC_TRAINING_CA_VREF_CTRL),			\
+	DEFINE_REG(REG_EMC, EMC_TRAINING_QUSE_CORS_CTRL),		\
+	DEFINE_REG(REG_EMC, EMC_TRAINING_QUSE_FINE_CTRL),		\
+	DEFINE_REG(REG_EMC, EMC_TRAINING_QUSE_CTRL_MISC),		\
+	DEFINE_REG(REG_EMC, EMC_TRAINING_QUSE_VREF_CTRL),		\
+	DEFINE_REG(REG_EMC, EMC_TRAINING_READ_FINE_CTRL),		\
+	DEFINE_REG(REG_EMC, EMC_TRAINING_READ_CTRL_MISC),		\
+	DEFINE_REG(REG_EMC, EMC_TRAINING_READ_VREF_CTRL),		\
+	DEFINE_REG(REG_EMC, EMC_TRAINING_WRITE_FINE_CTRL),		\
+	DEFINE_REG(REG_EMC, EMC_TRAINING_WRITE_CTRL_MISC),		\
+	DEFINE_REG(REG_EMC, EMC_TRAINING_WRITE_VREF_CTRL),		\
+	DEFINE_REG(REG_EMC, EMC_TRAINING_MPC),				\
+	DEFINE_REG(REG_EMC, EMC_MRW15),					\
+}
+
+#define TRIM_REGS_PER_CH_LIST						\
+{									\
+	DEFINE_REG(REG_EMC0, EMC_CMD_BRLSHFT_0),			\
+	DEFINE_REG(REG_EMC1, EMC_CMD_BRLSHFT_1),			\
+	DEFINE_REG(REG_EMC0, EMC_DATA_BRLSHFT_0),			\
+	DEFINE_REG(REG_EMC1, EMC_DATA_BRLSHFT_0),			\
+	DEFINE_REG(REG_EMC0, EMC_DATA_BRLSHFT_1),			\
+	DEFINE_REG(REG_EMC1, EMC_DATA_BRLSHFT_1),			\
+	DEFINE_REG(REG_EMC0, EMC_QUSE_BRLSHFT_0),			\
+	DEFINE_REG(REG_EMC1, EMC_QUSE_BRLSHFT_1),			\
+	DEFINE_REG(REG_EMC0, EMC_QUSE_BRLSHFT_2),			\
+	DEFINE_REG(REG_EMC1, EMC_QUSE_BRLSHFT_3),			\
+}
+
+#define TRIM_REGS_LIST							\
+{									\
+	DEFINE_REG(REG_EMC, EMC_PMACRO_IB_DDLL_LONG_DQS_RANK0_0),	\
+	DEFINE_REG(REG_EMC, EMC_PMACRO_IB_DDLL_LONG_DQS_RANK0_1),	\
+	DEFINE_REG(REG_EMC, EMC_PMACRO_IB_DDLL_LONG_DQS_RANK0_2),	\
+	DEFINE_REG(REG_EMC, EMC_PMACRO_IB_DDLL_LONG_DQS_RANK0_3),	\
+	DEFINE_REG(REG_EMC, EMC_PMACRO_IB_DDLL_LONG_DQS_RANK1_0),	\
+	DEFINE_REG(REG_EMC, EMC_PMACRO_IB_DDLL_LONG_DQS_RANK1_1),	\
+	DEFINE_REG(REG_EMC, EMC_PMACRO_IB_DDLL_LONG_DQS_RANK1_2),	\
+	DEFINE_REG(REG_EMC, EMC_PMACRO_IB_DDLL_LONG_DQS_RANK1_3),	\
+	DEFINE_REG(REG_EMC, EMC_PMACRO_IB_DDLL_SHORT_DQ_RANK0_BYTE0_0),	\
+	DEFINE_REG(REG_EMC, EMC_PMACRO_IB_DDLL_SHORT_DQ_RANK0_BYTE0_1),	\
+	DEFINE_REG(REG_EMC, EMC_PMACRO_IB_DDLL_SHORT_DQ_RANK0_BYTE0_2),	\
+	DEFINE_REG(REG_EMC, EMC_PMACRO_IB_DDLL_SHORT_DQ_RANK0_BYTE1_0),	\
+	DEFINE_REG(REG_EMC, EMC_PMACRO_IB_DDLL_SHORT_DQ_RANK0_BYTE1_1),	\
+	DEFINE_REG(REG_EMC, EMC_PMACRO_IB_DDLL_SHORT_DQ_RANK0_BYTE1_2),	\
+	DEFINE_REG(REG_EMC, EMC_PMACRO_IB_DDLL_SHORT_DQ_RANK0_BYTE2_0),	\
+	DEFINE_REG(REG_EMC, EMC_PMACRO_IB_DDLL_SHORT_DQ_RANK0_BYTE2_1),	\
+	DEFINE_REG(REG_EMC, EMC_PMACRO_IB_DDLL_SHORT_DQ_RANK0_BYTE2_2),	\
+	DEFINE_REG(REG_EMC, EMC_PMACRO_IB_DDLL_SHORT_DQ_RANK0_BYTE3_0),	\
+	DEFINE_REG(REG_EMC, EMC_PMACRO_IB_DDLL_SHORT_DQ_RANK0_BYTE3_1),	\
+	DEFINE_REG(REG_EMC, EMC_PMACRO_IB_DDLL_SHORT_DQ_RANK0_BYTE3_2),	\
+	DEFINE_REG(REG_EMC, EMC_PMACRO_IB_DDLL_SHORT_DQ_RANK0_BYTE4_0),	\
+	DEFINE_REG(REG_EMC, EMC_PMACRO_IB_DDLL_SHORT_DQ_RANK0_BYTE4_1),	\
+	DEFINE_REG(REG_EMC, EMC_PMACRO_IB_DDLL_SHORT_DQ_RANK0_BYTE4_2),	\
+	DEFINE_REG(REG_EMC, EMC_PMACRO_IB_DDLL_SHORT_DQ_RANK0_BYTE5_0),	\
+	DEFINE_REG(REG_EMC, EMC_PMACRO_IB_DDLL_SHORT_DQ_RANK0_BYTE5_1),	\
+	DEFINE_REG(REG_EMC, EMC_PMACRO_IB_DDLL_SHORT_DQ_RANK0_BYTE5_2),	\
+	DEFINE_REG(REG_EMC, EMC_PMACRO_IB_DDLL_SHORT_DQ_RANK0_BYTE6_0),	\
+	DEFINE_REG(REG_EMC, EMC_PMACRO_IB_DDLL_SHORT_DQ_RANK0_BYTE6_1),	\
+	DEFINE_REG(REG_EMC, EMC_PMACRO_IB_DDLL_SHORT_DQ_RANK0_BYTE6_2),	\
+	DEFINE_REG(REG_EMC, EMC_PMACRO_IB_DDLL_SHORT_DQ_RANK0_BYTE7_0),	\
+	DEFINE_REG(REG_EMC, EMC_PMACRO_IB_DDLL_SHORT_DQ_RANK0_BYTE7_1),	\
+	DEFINE_REG(REG_EMC, EMC_PMACRO_IB_DDLL_SHORT_DQ_RANK0_BYTE7_2),	\
+	DEFINE_REG(REG_EMC, EMC_PMACRO_IB_DDLL_SHORT_DQ_RANK1_BYTE0_0),	\
+	DEFINE_REG(REG_EMC, EMC_PMACRO_IB_DDLL_SHORT_DQ_RANK1_BYTE0_1),	\
+	DEFINE_REG(REG_EMC, EMC_PMACRO_IB_DDLL_SHORT_DQ_RANK1_BYTE0_2),	\
+	DEFINE_REG(REG_EMC, EMC_PMACRO_IB_DDLL_SHORT_DQ_RANK1_BYTE1_0),	\
+	DEFINE_REG(REG_EMC, EMC_PMACRO_IB_DDLL_SHORT_DQ_RANK1_BYTE1_1),	\
+	DEFINE_REG(REG_EMC, EMC_PMACRO_IB_DDLL_SHORT_DQ_RANK1_BYTE1_2),	\
+	DEFINE_REG(REG_EMC, EMC_PMACRO_IB_DDLL_SHORT_DQ_RANK1_BYTE2_0),	\
+	DEFINE_REG(REG_EMC, EMC_PMACRO_IB_DDLL_SHORT_DQ_RANK1_BYTE2_1),	\
+	DEFINE_REG(REG_EMC, EMC_PMACRO_IB_DDLL_SHORT_DQ_RANK1_BYTE2_2),	\
+	DEFINE_REG(REG_EMC, EMC_PMACRO_IB_DDLL_SHORT_DQ_RANK1_BYTE3_0),	\
+	DEFINE_REG(REG_EMC, EMC_PMACRO_IB_DDLL_SHORT_DQ_RANK1_BYTE3_1),	\
+	DEFINE_REG(REG_EMC, EMC_PMACRO_IB_DDLL_SHORT_DQ_RANK1_BYTE3_2),	\
+	DEFINE_REG(REG_EMC, EMC_PMACRO_IB_DDLL_SHORT_DQ_RANK1_BYTE4_0),	\
+	DEFINE_REG(REG_EMC, EMC_PMACRO_IB_DDLL_SHORT_DQ_RANK1_BYTE4_1),	\
+	DEFINE_REG(REG_EMC, EMC_PMACRO_IB_DDLL_SHORT_DQ_RANK1_BYTE4_2),	\
+	DEFINE_REG(REG_EMC, EMC_PMACRO_IB_DDLL_SHORT_DQ_RANK1_BYTE5_0),	\
+	DEFINE_REG(REG_EMC, EMC_PMACRO_IB_DDLL_SHORT_DQ_RANK1_BYTE5_1),	\
+	DEFINE_REG(REG_EMC, EMC_PMACRO_IB_DDLL_SHORT_DQ_RANK1_BYTE5_2),	\
+	DEFINE_REG(REG_EMC, EMC_PMACRO_IB_DDLL_SHORT_DQ_RANK1_BYTE6_0),	\
+	DEFINE_REG(REG_EMC, EMC_PMACRO_IB_DDLL_SHORT_DQ_RANK1_BYTE6_1),	\
+	DEFINE_REG(REG_EMC, EMC_PMACRO_IB_DDLL_SHORT_DQ_RANK1_BYTE6_2),	\
+	DEFINE_REG(REG_EMC, EMC_PMACRO_IB_DDLL_SHORT_DQ_RANK1_BYTE7_0),	\
+	DEFINE_REG(REG_EMC, EMC_PMACRO_IB_DDLL_SHORT_DQ_RANK1_BYTE7_1),	\
+	DEFINE_REG(REG_EMC, EMC_PMACRO_IB_DDLL_SHORT_DQ_RANK1_BYTE7_2),	\
+	DEFINE_REG(REG_EMC, EMC_PMACRO_IB_VREF_DQS_0),			\
+	DEFINE_REG(REG_EMC, EMC_PMACRO_IB_VREF_DQS_1),			\
+	DEFINE_REG(REG_EMC, EMC_PMACRO_IB_VREF_DQ_0),			\
+	DEFINE_REG(REG_EMC, EMC_PMACRO_IB_VREF_DQ_1),			\
+	DEFINE_REG(REG_EMC, EMC_PMACRO_OB_DDLL_LONG_DQ_RANK0_0),	\
+	DEFINE_REG(REG_EMC, EMC_PMACRO_OB_DDLL_LONG_DQ_RANK0_1),	\
+	DEFINE_REG(REG_EMC, EMC_PMACRO_OB_DDLL_LONG_DQ_RANK0_2),	\
+	DEFINE_REG(REG_EMC, EMC_PMACRO_OB_DDLL_LONG_DQ_RANK0_3),	\
+	DEFINE_REG(REG_EMC, EMC_PMACRO_OB_DDLL_LONG_DQ_RANK0_4),	\
+	DEFINE_REG(REG_EMC, EMC_PMACRO_OB_DDLL_LONG_DQ_RANK0_5),	\
+	DEFINE_REG(REG_EMC, EMC_PMACRO_OB_DDLL_LONG_DQ_RANK1_0),	\
+	DEFINE_REG(REG_EMC, EMC_PMACRO_OB_DDLL_LONG_DQ_RANK1_1),	\
+	DEFINE_REG(REG_EMC, EMC_PMACRO_OB_DDLL_LONG_DQ_RANK1_2),	\
+	DEFINE_REG(REG_EMC, EMC_PMACRO_OB_DDLL_LONG_DQ_RANK1_3),	\
+	DEFINE_REG(REG_EMC, EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK0_BYTE0_0),	\
+	DEFINE_REG(REG_EMC, EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK0_BYTE0_1),	\
+	DEFINE_REG(REG_EMC, EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK0_BYTE0_2),	\
+	DEFINE_REG(REG_EMC, EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK0_BYTE1_0),	\
+	DEFINE_REG(REG_EMC, EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK0_BYTE1_1),	\
+	DEFINE_REG(REG_EMC, EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK0_BYTE1_2),	\
+	DEFINE_REG(REG_EMC, EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK0_BYTE2_0),	\
+	DEFINE_REG(REG_EMC, EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK0_BYTE2_1),	\
+	DEFINE_REG(REG_EMC, EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK0_BYTE2_2),	\
+	DEFINE_REG(REG_EMC, EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK0_BYTE3_0),	\
+	DEFINE_REG(REG_EMC, EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK0_BYTE3_1),	\
+	DEFINE_REG(REG_EMC, EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK0_BYTE3_2),	\
+	DEFINE_REG(REG_EMC, EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK0_BYTE4_0),	\
+	DEFINE_REG(REG_EMC, EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK0_BYTE4_1),	\
+	DEFINE_REG(REG_EMC, EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK0_BYTE4_2),	\
+	DEFINE_REG(REG_EMC, EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK0_BYTE5_0),	\
+	DEFINE_REG(REG_EMC, EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK0_BYTE5_1),	\
+	DEFINE_REG(REG_EMC, EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK0_BYTE5_2),	\
+	DEFINE_REG(REG_EMC, EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK0_BYTE6_0),	\
+	DEFINE_REG(REG_EMC, EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK0_BYTE6_1),	\
+	DEFINE_REG(REG_EMC, EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK0_BYTE6_2),	\
+	DEFINE_REG(REG_EMC, EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK0_BYTE7_0),	\
+	DEFINE_REG(REG_EMC, EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK0_BYTE7_1),	\
+	DEFINE_REG(REG_EMC, EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK0_BYTE7_2),	\
+	DEFINE_REG(REG_EMC, EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK0_CMD0_0),	\
+	DEFINE_REG(REG_EMC, EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK0_CMD0_1),	\
+	DEFINE_REG(REG_EMC, EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK0_CMD0_2),	\
+	DEFINE_REG(REG_EMC, EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK0_CMD1_0),	\
+	DEFINE_REG(REG_EMC, EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK0_CMD1_1),	\
+	DEFINE_REG(REG_EMC, EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK0_CMD1_2),	\
+	DEFINE_REG(REG_EMC, EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK0_CMD2_0),	\
+	DEFINE_REG(REG_EMC, EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK0_CMD2_1),	\
+	DEFINE_REG(REG_EMC, EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK0_CMD2_2),	\
+	DEFINE_REG(REG_EMC, EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK0_CMD3_0),	\
+	DEFINE_REG(REG_EMC, EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK0_CMD3_1),	\
+	DEFINE_REG(REG_EMC, EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK0_CMD3_2),	\
+	DEFINE_REG(REG_EMC, EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK1_BYTE0_0),	\
+	DEFINE_REG(REG_EMC, EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK1_BYTE0_1),	\
+	DEFINE_REG(REG_EMC, EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK1_BYTE0_2),	\
+	DEFINE_REG(REG_EMC, EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK1_BYTE1_0),	\
+	DEFINE_REG(REG_EMC, EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK1_BYTE1_1),	\
+	DEFINE_REG(REG_EMC, EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK1_BYTE1_2),	\
+	DEFINE_REG(REG_EMC, EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK1_BYTE2_0),	\
+	DEFINE_REG(REG_EMC, EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK1_BYTE2_1),	\
+	DEFINE_REG(REG_EMC, EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK1_BYTE2_2),	\
+	DEFINE_REG(REG_EMC, EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK1_BYTE3_0),	\
+	DEFINE_REG(REG_EMC, EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK1_BYTE3_1),	\
+	DEFINE_REG(REG_EMC, EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK1_BYTE3_2),	\
+	DEFINE_REG(REG_EMC, EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK1_BYTE4_0),	\
+	DEFINE_REG(REG_EMC, EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK1_BYTE4_1),	\
+	DEFINE_REG(REG_EMC, EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK1_BYTE4_2),	\
+	DEFINE_REG(REG_EMC, EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK1_BYTE5_0),	\
+	DEFINE_REG(REG_EMC, EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK1_BYTE5_1),	\
+	DEFINE_REG(REG_EMC, EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK1_BYTE5_2),	\
+	DEFINE_REG(REG_EMC, EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK1_BYTE6_0),	\
+	DEFINE_REG(REG_EMC, EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK1_BYTE6_1),	\
+	DEFINE_REG(REG_EMC, EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK1_BYTE6_2),	\
+	DEFINE_REG(REG_EMC, EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK1_BYTE7_0),	\
+	DEFINE_REG(REG_EMC, EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK1_BYTE7_1),	\
+	DEFINE_REG(REG_EMC, EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK1_BYTE7_2),	\
+	DEFINE_REG(REG_EMC, EMC_PMACRO_QUSE_DDLL_RANK0_0),		\
+	DEFINE_REG(REG_EMC, EMC_PMACRO_QUSE_DDLL_RANK0_1),		\
+	DEFINE_REG(REG_EMC, EMC_PMACRO_QUSE_DDLL_RANK0_2),		\
+	DEFINE_REG(REG_EMC, EMC_PMACRO_QUSE_DDLL_RANK0_3),		\
+	DEFINE_REG(REG_EMC, EMC_PMACRO_QUSE_DDLL_RANK1_0),		\
+	DEFINE_REG(REG_EMC, EMC_PMACRO_QUSE_DDLL_RANK1_1),		\
+	DEFINE_REG(REG_EMC, EMC_PMACRO_QUSE_DDLL_RANK1_2),		\
+	DEFINE_REG(REG_EMC, EMC_PMACRO_QUSE_DDLL_RANK1_3),		\
+}
+
+#define VREF_REGS_PER_CH_LIST						\
+{									\
+	DEFINE_REG(REG_EMC0, EMC_TRAINING_OPT_DQS_IB_VREF_RANK0),	\
+	DEFINE_REG(REG_EMC1, EMC_TRAINING_OPT_DQS_IB_VREF_RANK0),	\
+	DEFINE_REG(REG_EMC0, EMC_TRAINING_OPT_DQS_IB_VREF_RANK1),	\
+	DEFINE_REG(REG_EMC1, EMC_TRAINING_OPT_DQS_IB_VREF_RANK1),	\
+}
+
+#define DEFINE_REG(type, reg)	reg##_INDEX
+enum BURST_REGS_LIST;
+enum TRIM_REGS_LIST;
+enum BURST_MC_REGS_LIST;
+enum BURST_UP_DOWN_REGS_LIST;
+#undef DEFINE_REG
+
+#define DEFINE_REG(type, reg)	type##_##reg##_INDEX
+enum BURST_REGS_PER_CH_LIST;
+enum TRIM_REGS_PER_CH_LIST;
+enum VREF_REGS_PER_CH_LIST;
+#undef DEFINE_REG
+
+struct emc_timing {
+	u32 rev;
+	char dvfs_ver[60];
+	u32 rate;
+	u32 min_volt;
+	u32 gpu_min_volt;
+	char clock_src[32];
+	u32 clk_src_emc;
+	u32 needs_training;
+	u32 training_parttern;
+	u32 trained;
+
+	u32 periodic_training;
+	u32 trained_dram_clktree_c0d0u0;
+	u32 trained_dram_clktree_c0d0u1;
+	u32 trained_dram_clktree_c0d1u0;
+	u32 trained_dram_clktree_c0d1u1;
+	u32 trained_dram_clktree_c1d0u0;
+	u32 trained_dram_clktree_c1d0u1;
+	u32 trained_dram_clktree_c1d1u0;
+	u32 trained_dram_clktree_c1d1u1;
+	u32 current_dram_clktree_c0d0u0;
+	u32 current_dram_clktree_c0d0u1;
+	u32 current_dram_clktree_c0d1u0;
+	u32 current_dram_clktree_c0d1u1;
+	u32 current_dram_clktree_c1d0u0;
+	u32 current_dram_clktree_c1d0u1;
+	u32 current_dram_clktree_c1d1u0;
+	u32 current_dram_clktree_c1d1u1;
+	u32 run_clocks;
+	u32 tree_margin;
+
+	u32 num_burst;
+	u32 num_burst_per_ch;
+	u32 num_trim;
+	u32 num_trim_per_ch;
+	u32 num_mc_regs;
+	u32 num_up_down;
+	u32 vref_num;
+	u32 training_mod_num;
+	u32 dram_timing_num;
+
+	u32  ptfv_list[12];
+
+	u32 burst_regs[221];
+	u32 burst_reg_per_ch[8];
+	u32 shadow_regs_ca_train[221];
+	u32 shadow_regs_quse_train[221];
+	u32 shadow_regs_rdwr_train[221];
+
+	u32 trim_regs[138];
+	u32 trim_perch_regs[10];
+
+	u32 vref_perch_regs[4];
+
+	u32 dram_timings[5];
+	u32 training_mod_regs[20];
+	u32 save_restore_mod_regs[12];
+	u32 burst_mc_regs[33];
+	u32 la_scale_regs[24];
+
+	u32 min_mrs_wait;
+	u32 emc_mrw;
+	u32 emc_mrw2;
+	u32 emc_mrw3;
+	u32 emc_mrw4;
+	u32 emc_mrw9;
+	u32 emc_mrs;
+	u32 emc_emrs;
+	u32 emc_emrs2;
+	u32 emc_auto_cal_config;
+	u32 emc_auto_cal_config2;
+	u32 emc_auto_cal_config3;
+	u32 emc_auto_cal_config4;
+	u32 emc_auto_cal_config5;
+	u32 emc_auto_cal_config6;
+	u32 emc_auto_cal_config7;
+	u32 emc_auto_cal_config8;
+	u32 emc_cfg_2;
+	u32 emc_sel_dpd_ctrl;
+	u32 emc_fdpd_ctrl_cmd_no_ramp;
+	u32 dll_clk_src;
+	u32 clk_out_enb_x_0_clk_enb_emc_dll;
+	u32 latency;
+};
+
+struct emc_stats {
+	u64 time_at_clock[TEGRA_EMC_TABLE_MAX_SIZE];
+	int last_sel;
+	u64 last_update;
+	u64 clkchange_count;
+	spinlock_t spinlock;
+};
+
+struct tegra_emc {
+	struct clk_hw hw;
+
+	struct tegra_mc *mc;
+
+	void __iomem *emc_base;
+	void __iomem *emc0_base;
+	void __iomem *emc1_base;
+
+	struct emc_timing *current_timing;
+	struct emc_timing *next_timing;
+	struct emc_timing start_timing;
+
+	struct emc_timing *emc_table;
+	struct emc_timing *emc_table_normal;
+	struct emc_timing *emc_table_derated;
+	
+	unsigned int num_timings;
+
+	int dram_dev_num;
+	u32 dram_type;
+	u32 ram_code;
+	u32 clk_setting;
+
+	struct clk *emc_dll_clk;
+};
+#define to_emc(_hw) container_of(_hw, struct tegra_emc, hw)
+
+struct supported_sequence {
+	u8     table_rev;
+	void (*set_clock)(struct tegra_emc *emc, struct clk *new_parent);
+	u32  (*periodic_compensation)(struct tegra_emc *emc);
+	char  *seq_rev;
+};
+
+enum {
+	DRAM_TYPE_DDR3   = 0,
+	DRAM_TYPE_LPDDR4 = 1,
+	DRAM_TYPE_LPDDR2 = 2,
+	DRAM_TYPE_DDR2 = 3,
+};
+
+enum {
+	DLL_CHANGE_NONE = 0,
+	DLL_CHANGE_ON,
+	DLL_CHANGE_OFF,
+};
+
+enum {
+	DLL_OFF,
+	DLL_ON
+};
+
+enum {
+	AUTO_PD = 0,
+	MAN_SR  = 2
+};
+
+enum {
+	ASSEMBLY = 0,
+	ACTIVE
+};
+
+enum {
+	T_RP = 0,
+	T_FC_LPDDR4,
+	T_RFC,
+	T_PDEX,
+	RL
+};
+
+enum {
+	ONE_RANK = 1,
+	TWO_RANK = 2
+};
+
+enum {
+	SINGLE_CHANNEL = 0,
+	DUAL_CHANNEL
+};
+
+enum {
+	DRAM_DEV_SEL_ALL = 0,
+	DRAM_DEV_SEL_0	 = (2 << 30),
+	DRAM_DEV_SEL_1   = (1 << 30),
+};
+
+enum {
+	EMC_CFG5_QUSE_MODE_NORMAL = 0,
+	EMC_CFG5_QUSE_MODE_ALWAYS_ON,
+	EMC_CFG5_QUSE_MODE_INTERNAL_LPBK,
+	EMC_CFG5_QUSE_MODE_PULSE_INTERN,
+	EMC_CFG5_QUSE_MODE_PULSE_EXTERN,
+	EMC_CFG5_QUSE_MODE_DIRECT_QUSE,
+};
+
+extern void __iomem *clk_base;
+extern unsigned long dram_over_temp_state;
+
+extern struct emc_table *tegra_emc_table;
+extern struct emc_table *tegra_emc_table_normal;
+extern struct emc_table *tegra_emc_table_derated;
+extern int tegra_emc_table_size;
+
+extern u32 burst_regs_per_ch_off[];
+extern u32 burst_regs_off[];
+extern u32 trim_regs_per_ch_off[];
+extern u32 trim_regs_off[];
+extern u32 burst_mc_regs_off[];
+extern u32 la_scale_regs_off[];
+extern u32 vref_regs_per_ch_off[];
+extern u32 burst_regs_per_ch_type[];
+extern u32 trim_regs_per_ch_type[];
+extern u32 vref_regs_per_ch_type[];
+
+u32 tegra210_apply_periodic_compensation_trimmer(
+		struct emc_timing *next_timing, u32 offset);
+void emc_writel(struct tegra_emc *emc, u32 val, unsigned long offset);
+u32  emc_readl(struct tegra_emc *emc, unsigned long offset);
+void emc1_writel(struct tegra_emc *emc, u32 val, unsigned long offset);
+u32  emc1_readl(struct tegra_emc *emc, unsigned long offset);
+void emc_writel_per_ch(struct tegra_emc *emc, u32 val, int type,
+		       unsigned long offset);
+u32 emc_readl_per_ch(struct tegra_emc *emc, int type, unsigned long offset);
+void mc_writel(u32 val, unsigned long offset);
+u32  mc_readl(unsigned long offset);
+void clk_cfg_writel(u32 val);
+u32  clk_cfg_readl(void);
+void ccfifo_writel(struct tegra_emc *emc, u32 val, unsigned long addr,
+		   u32 delay);
+void do_clock_change(struct tegra_emc *emc);
+int wait_for_update(struct tegra_emc *emc, u32 status_reg, u32 bit_mask,
+		    bool updated_state, int chan);
+u32  div_o3(u32 a, u32 b);
+struct emc_timing *emc_get_table(struct tegra_emc *emc,
+				 unsigned long over_temp_state);
+void set_over_temp_timing(struct tegra_emc *emc, struct emc_timing *timing,
+			  unsigned long state);
+void tegra210_update_emc_alt_timing(struct tegra_emc *emc,
+				    struct emc_timing *current_timing);
+u32 tegra210_actual_osc_clocks(u32 in);
+void tegra210_start_periodic_compensation(struct tegra_emc *emc);
+u32 tegra210_dll_prelock(struct tegra_emc *emc, int dvfs_with_training,
+			 struct clk *newparent);
+void tegra210_reset_dram_clktree_values(struct emc_timing *table);
+u32 tegra210_dvfs_power_ramp_up(struct tegra_emc *emc, u32 clk,
+				int flip_backward);
+u32 tegra210_dvfs_power_ramp_down(struct tegra_emc *emc, u32 clk,
+				  int flip_backward);
+void tegra210_dll_disable(struct tegra_emc *emc, int channel_mode);
+void tegra210_dll_enable(struct tegra_emc *emc, int channel_mode);
+void emc_set_shadow_bypass(struct tegra_emc *emc, int set);
+void emc_timing_update(struct tegra_emc *emc, int dual_chan);
+struct emc_timing *get_timing_from_freq(struct tegra_emc *emc,
+					unsigned long rate);
+u32 get_dll_state(struct emc_timing *next_timing);
+
+void emc_set_clock_r21012(struct emc_table *next_timing,
+			  struct emc_table *last_timing,
+			  int training, u32 clksrc);
+void emc_set_clock_r21015(struct emc_table *next_timing,
+			  struct emc_table *last_timing,
+			  int training, u32 clksrc);
+u32  __do_periodic_emc_compensation_r21015(struct emc_table *current_timing);
+void emc_set_clock_r21021(struct tegra_emc *emc, struct clk *new_parent);
+u32  __do_periodic_emc_compensation_r21021(struct tegra_emc *emc);
+
+int tegra_emc_dt_parse_pdata(struct platform_device *pdev,
+			     struct emc_timing **tables,
+			     struct emc_timing **derated_tables,
+			     int *num_entries);
+
+#endif
-- 
1.9.1

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

* [RFC 10/14] memory: tegra: Add Tegra210 EMC scaling sequence
  2018-09-14 20:02 [RFC 00/14] Tegra210 EMC scaling Peter De Schrijver
                   ` (8 preceding siblings ...)
  2018-09-14 20:03 ` [RFC 09/14] memory: tegra: Add definitions shared by Tegra210 EMC scaling code Peter De Schrijver
@ 2018-09-14 20:03 ` Peter De Schrijver
  2018-09-14 20:03 ` [RFC 11/14] memory: tegra: parse DT and costruct timing tables Peter De Schrijver
                   ` (3 subsequent siblings)
  13 siblings, 0 replies; 26+ messages in thread
From: Peter De Schrijver @ 2018-09-14 20:03 UTC (permalink / raw)
  To: vkuruturi, linux-tegra=linux-clk, linux-tegra, thierry.reding,
	jonathanh, devicetree, robh+dt
  Cc: daniel, a.heider, swtcr0, Peter De Schrijver

Historically there have been different sequences to change the EMC clock.
The sequence to be used is specified in the scaling data. However for
the currently supported upstreaming platform, only the most recent sequence
is used so add that in this patch.

Signed-off-by: Peter De Schrijver <pdeschrijver@nvidia.com>
---
 drivers/memory/tegra/tegra210-emc-cc-r21021.c | 1864 +++++++++++++++++++++++++
 1 file changed, 1864 insertions(+)
 create mode 100644 drivers/memory/tegra/tegra210-emc-cc-r21021.c

diff --git a/drivers/memory/tegra/tegra210-emc-cc-r21021.c b/drivers/memory/tegra/tegra210-emc-cc-r21021.c
new file mode 100644
index 0000000..d57f3cc
--- /dev/null
+++ b/drivers/memory/tegra/tegra210-emc-cc-r21021.c
@@ -0,0 +1,1864 @@
+/*
+ * drivers/platform/tegra/tegra21_emc_cc_r21012.c
+ *
+ * Copyright (c) 2014-2015, NVIDIA CORPORATION.  All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ */
+
+#include <linux/kernel.h>
+#include <linux/io.h>
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/of.h>
+#include <soc/tegra/mc.h>
+
+#include "tegra210-emc-reg.h"
+
+#define DVFS_CLOCK_CHANGE_VERSION	21021
+#define EMC_PRELOCK_VERSION		2101
+
+#define emc_cc_dbg(t, ...) pr_debug(__VA_ARGS__)
+
+/*
+ * Enable flags for specifying verbosity.
+ */
+#define INFO            (1 << 0)
+#define STEPS           (1 << 1)
+#define SUB_STEPS       (1 << 2)
+#define PRELOCK         (1 << 3)
+#define PRELOCK_STEPS   (1 << 4)
+#define ACTIVE_EN       (1 << 5)
+#define PRAMP_UP        (1 << 6)
+#define PRAMP_DN        (1 << 7)
+#define EMA_WRITES      (1 << 10)
+#define EMA_UPDATES     (1 << 11)
+#define PER_TRAIN       (1 << 16)
+#define CC_PRINT        (1 << 17)
+#define CCFIFO          (1 << 29)
+#define REGS            (1 << 30)
+#define REG_LISTS       (1 << 31)
+
+enum {
+	DVFS_SEQUENCE = 1,
+	WRITE_TRAINING_SEQUENCE = 2,
+	PERIODIC_TRAINING_SEQUENCE = 3,
+	DVFS_PT1 = 10,
+	DVFS_UPDATE = 11,
+	TRAINING_PT1 = 12,
+	TRAINING_UPDATE = 13,
+	PERIODIC_TRAINING_UPDATE = 14
+};
+
+/*
+ * PTFV defines - basically just indexes into the per table PTFV array.
+ */
+#define PTFV_DQSOSC_MOVAVG_C0D0U0_INDEX		0
+#define PTFV_DQSOSC_MOVAVG_C0D0U1_INDEX		1
+#define PTFV_DQSOSC_MOVAVG_C0D1U0_INDEX		2
+#define PTFV_DQSOSC_MOVAVG_C0D1U1_INDEX		3
+#define PTFV_DQSOSC_MOVAVG_C1D0U0_INDEX		4
+#define PTFV_DQSOSC_MOVAVG_C1D0U1_INDEX		5
+#define PTFV_DQSOSC_MOVAVG_C1D1U0_INDEX		6
+#define PTFV_DQSOSC_MOVAVG_C1D1U1_INDEX		7
+#define PTFV_DVFS_SAMPLES_INDEX			9
+#define PTFV_MOVAVG_WEIGHT_INDEX		10
+#define PTFV_CONFIG_CTRL_INDEX			11
+
+#define PTFV_CONFIG_CTRL_USE_PREVIOUS_EMA	(1 << 0)
+
+/*
+ * Do arithmetic in fixed point.
+ */
+#define MOVAVG_PRECISION_FACTOR		100
+
+/*
+ * The division portion of the average operation.
+ */
+#define __AVERAGE_PTFV(dev)						\
+	({ next_timing->ptfv_list[PTFV_DQSOSC_MOVAVG_ ## dev ## _INDEX] = \
+	   next_timing->ptfv_list[PTFV_DQSOSC_MOVAVG_ ## dev ## _INDEX] / \
+	   next_timing->ptfv_list[PTFV_DVFS_SAMPLES_INDEX]; })
+
+/*
+ * Convert val to fixed point and add it to the temporary average.
+ */
+#define __INCREMENT_PTFV(dev, val)					\
+	({ next_timing->ptfv_list[PTFV_DQSOSC_MOVAVG_ ## dev ## _INDEX] += \
+	   ((val) * MOVAVG_PRECISION_FACTOR); })
+
+/*
+ * Convert a moving average back to integral form and return the value.
+ */
+#define __MOVAVG_AC(timing, dev)					\
+	((timing)->ptfv_list[PTFV_DQSOSC_MOVAVG_ ## dev ## _INDEX] /	\
+	 MOVAVG_PRECISION_FACTOR)
+
+/* Weighted update. */
+#define __WEIGHTED_UPDATE_PTFV(dev, nval)				\
+	do {								\
+		int w = PTFV_MOVAVG_WEIGHT_INDEX;			\
+		int dqs = PTFV_DQSOSC_MOVAVG_ ## dev ## _INDEX;		\
+									\
+		next_timing->ptfv_list[dqs] =				\
+			((nval * MOVAVG_PRECISION_FACTOR) +		\
+			 (next_timing->ptfv_list[dqs] *			\
+			  next_timing->ptfv_list[w])) /			\
+			(next_timing->ptfv_list[w] + 1);		\
+									\
+		emc_cc_dbg(EMA_UPDATES, "%s: (s=%u) EMA: %u\n",		\
+			   __stringify(dev), nval,			\
+			   next_timing->ptfv_list[dqs]);		\
+	} while (0)
+
+/* Access a particular average. */
+#define __MOVAVG(timing, dev)                      \
+	((timing)->ptfv_list[PTFV_DQSOSC_MOVAVG_ ## dev ## _INDEX])
+
+static u32 update_clock_tree_delay(struct tegra_emc *emc,
+				   u32 dram_dev_num, u32 channel_mode, int type)
+{
+	u32 mrr_req = 0, mrr_data = 0;
+	u32 temp0_0 = 0, temp0_1 = 0, temp1_0 = 0, temp1_1 = 0;
+	s32 tdel = 0, tmdel = 0, adel = 0;
+	u32 cval;
+	struct emc_timing *last_timing = emc->current_timing;
+	struct emc_timing *next_timing = emc->next_timing;
+	u32 last_timing_rate_mhz = last_timing->rate / 1000;
+	u32 next_timing_rate_mhz = next_timing->rate / 1000;
+	int dvfs_pt1 = type == DVFS_PT1;
+	int dvfs_update = type == DVFS_UPDATE;
+	int periodic_training_update = type == PERIODIC_TRAINING_UPDATE;
+
+	/*
+	 * Dev0 MSB.
+	 */
+	if (dvfs_pt1 || periodic_training_update) {
+		mrr_req = (2 << EMC_MRR_DEV_SEL_SHIFT) |
+			(19 << EMC_MRR_MA_SHIFT);
+		emc_writel(emc, mrr_req, EMC_MRR);
+
+		WARN(wait_for_update(emc, EMC_EMC_STATUS,
+				     EMC_EMC_STATUS_MRR_DIVLD, 1, REG_EMC),
+		     "Timed out waiting for MRR 19 (ch=0)\n");
+		if (channel_mode == DUAL_CHANNEL)
+			WARN(wait_for_update(emc, EMC_EMC_STATUS,
+					     EMC_EMC_STATUS_MRR_DIVLD, 1, REG_EMC1),
+			     "Timed out waiting for MRR 19 (ch=1)\n");
+
+		mrr_data = (emc_readl(emc, EMC_MRR) & EMC_MRR_DATA_MASK) <<
+			EMC_MRR_DATA_SHIFT;
+
+		temp0_0 = (mrr_data & 0xff) << 8;
+		temp0_1 = mrr_data & 0xff00;
+
+		if (channel_mode == DUAL_CHANNEL) {
+			mrr_data = (emc1_readl(emc, EMC_MRR) & EMC_MRR_DATA_MASK) <<
+				EMC_MRR_DATA_SHIFT;
+			temp1_0 = (mrr_data & 0xff) << 8;
+			temp1_1 = mrr_data & 0xff00;
+		}
+
+		/*
+		 * Dev0 LSB.
+		 */
+		mrr_req = (mrr_req & ~EMC_MRR_MA_MASK) |
+			  (18 << EMC_MRR_MA_SHIFT);
+		emc_writel(emc, mrr_req, EMC_MRR);
+
+		WARN(wait_for_update(emc, EMC_EMC_STATUS,
+				     EMC_EMC_STATUS_MRR_DIVLD, 1, REG_EMC),
+		     "Timed out waiting for MRR 18 (ch=0)\n");
+		if (channel_mode == DUAL_CHANNEL)
+			WARN(wait_for_update(emc, EMC_EMC_STATUS,
+					     EMC_EMC_STATUS_MRR_DIVLD, 1, REG_EMC1),
+			     "Timed out waiting for MRR 18 (ch=1)\n");
+
+		mrr_data = (emc_readl(emc, EMC_MRR) & EMC_MRR_DATA_MASK) <<
+			EMC_MRR_DATA_SHIFT;
+
+		temp0_0 |= mrr_data & 0xff;
+		temp0_1 |= (mrr_data & 0xff00) >> 8;
+
+		if (channel_mode == DUAL_CHANNEL) {
+			mrr_data = (emc1_readl(emc, EMC_MRR) & EMC_MRR_DATA_MASK) <<
+				EMC_MRR_DATA_SHIFT;
+			temp1_0 |= (mrr_data & 0xff);
+			temp1_1 |= (mrr_data & 0xff00) >> 8;
+		}
+	}
+
+	cval = (1000000 * tegra210_actual_osc_clocks(last_timing->run_clocks)) /
+		(last_timing_rate_mhz * 2 * temp0_0);
+
+	if (dvfs_pt1)
+		__INCREMENT_PTFV(C0D0U0, cval);
+	else if (dvfs_update)
+		__AVERAGE_PTFV(C0D0U0);
+	else if (periodic_training_update)
+		__WEIGHTED_UPDATE_PTFV(C0D0U0, cval);
+
+	if (dvfs_update || periodic_training_update) {
+		tdel = next_timing->current_dram_clktree_c0d0u0 -
+			__MOVAVG_AC(next_timing, C0D0U0);
+		tmdel = (tdel < 0) ? -1 * tdel : tdel;
+		adel = tmdel;
+		if (tmdel * 128 * next_timing_rate_mhz / 1000000 >
+		    next_timing->tree_margin)
+			next_timing->current_dram_clktree_c0d0u0 =
+				__MOVAVG_AC(next_timing, C0D0U0);
+	}
+
+	cval = (1000000 * tegra210_actual_osc_clocks(last_timing->run_clocks)) /
+		(last_timing_rate_mhz * 2 * temp0_1);
+
+	if (dvfs_pt1)
+		__INCREMENT_PTFV(C0D0U1, cval);
+	else if (dvfs_update)
+		__AVERAGE_PTFV(C0D0U1);
+	else if (periodic_training_update)
+		__WEIGHTED_UPDATE_PTFV(C0D0U1, cval);
+
+	if (dvfs_update || periodic_training_update) {
+		tdel = next_timing->current_dram_clktree_c0d0u1 -
+			__MOVAVG_AC(next_timing, C0D0U1);
+		tmdel = (tdel < 0) ? -1 * tdel : tdel;
+
+		if (tmdel > adel)
+			adel = tmdel;
+
+		if (tmdel * 128 * next_timing_rate_mhz / 1000000 >
+		    next_timing->tree_margin)
+			next_timing->current_dram_clktree_c0d0u1 =
+				__MOVAVG_AC(next_timing, C0D0U1);
+	}
+
+	if (channel_mode == DUAL_CHANNEL) {
+		cval = (1000000 * tegra210_actual_osc_clocks(last_timing->run_clocks)) /
+			(last_timing_rate_mhz * 2 * temp1_0);
+		if (dvfs_pt1)
+			__INCREMENT_PTFV(C1D0U0, cval);
+		else if (dvfs_update)
+			__AVERAGE_PTFV(C1D0U0);
+		else if (periodic_training_update)
+			__WEIGHTED_UPDATE_PTFV(C1D0U0, cval);
+
+		if (dvfs_update || periodic_training_update) {
+			tdel = next_timing->current_dram_clktree_c1d0u0 -
+				__MOVAVG_AC(next_timing, C1D0U0);
+			tmdel = (tdel < 0) ? -1 * tdel : tdel;
+
+			if (tmdel > adel)
+				adel = tmdel;
+
+			if (tmdel * 128 * next_timing_rate_mhz / 1000000 >
+			    next_timing->tree_margin)
+				next_timing->current_dram_clktree_c1d0u0 =
+					__MOVAVG_AC(next_timing, C1D0U0);
+		}
+
+		cval = (1000000 * tegra210_actual_osc_clocks(last_timing->run_clocks)) /
+			(last_timing_rate_mhz * 2 * temp1_1);
+		if (dvfs_pt1)
+			__INCREMENT_PTFV(C1D0U1, cval);
+		else if (dvfs_update)
+			__AVERAGE_PTFV(C1D0U1);
+		else if (periodic_training_update)
+			__WEIGHTED_UPDATE_PTFV(C1D0U1, cval);
+
+		if (dvfs_update || periodic_training_update) {
+			tdel = next_timing->current_dram_clktree_c1d0u1 -
+				__MOVAVG_AC(next_timing, C1D0U1);
+			tmdel = (tdel < 0) ? -1 * tdel : tdel;
+
+			if (tmdel > adel)
+				adel = tmdel;
+
+			if (tmdel * 128 * next_timing_rate_mhz / 1000000 >
+			    next_timing->tree_margin)
+				next_timing->current_dram_clktree_c1d0u1 =
+					__MOVAVG_AC(next_timing, C1D0U1);
+		}
+	}
+
+	if (emc->dram_dev_num != TWO_RANK)
+		goto done;
+
+	/*
+	 * Dev1 MSB.
+	 */
+	if (dvfs_pt1 || periodic_training_update) {
+		mrr_req = (1 << EMC_MRR_DEV_SEL_SHIFT) |
+			(19 << EMC_MRR_MA_SHIFT);
+		emc_writel(emc, mrr_req, EMC_MRR);
+
+		WARN(wait_for_update(emc, EMC_EMC_STATUS,
+				     EMC_EMC_STATUS_MRR_DIVLD, 1, REG_EMC),
+		     "Timed out waiting for MRR 19 (ch=0)\n");
+		if (channel_mode == DUAL_CHANNEL)
+			WARN(wait_for_update(emc, EMC_EMC_STATUS,
+					     EMC_EMC_STATUS_MRR_DIVLD, 1, REG_EMC1),
+			     "Timed out waiting for MRR 19 (ch=1)\n");
+
+		mrr_data = (emc_readl(emc, EMC_MRR) & EMC_MRR_DATA_MASK) <<
+			EMC_MRR_DATA_SHIFT;
+
+		temp0_0 = (mrr_data & 0xff) << 8;
+		temp0_1 = mrr_data & 0xff00;
+
+		if (channel_mode == DUAL_CHANNEL) {
+			mrr_data = (emc1_readl(emc, EMC_MRR) & EMC_MRR_DATA_MASK) <<
+				EMC_MRR_DATA_SHIFT;
+			temp1_0 = (mrr_data & 0xff) << 8;
+			temp1_1 = mrr_data & 0xff00;
+		}
+
+		/*
+		 * Dev1 LSB.
+		 */
+		mrr_req = (mrr_req & ~EMC_MRR_MA_MASK) |
+			  (18 << EMC_MRR_MA_SHIFT);
+		emc_writel(emc, mrr_req, EMC_MRR);
+
+		WARN(wait_for_update(emc, EMC_EMC_STATUS,
+				     EMC_EMC_STATUS_MRR_DIVLD, 1, REG_EMC),
+		     "Timed out waiting for MRR 18 (ch=0)\n");
+		if (channel_mode == DUAL_CHANNEL)
+			WARN(wait_for_update(emc, EMC_EMC_STATUS,
+					     EMC_EMC_STATUS_MRR_DIVLD, 1, REG_EMC1),
+			     "Timed out waiting for MRR 18 (ch=1)\n");
+
+		mrr_data = (emc_readl(emc, EMC_MRR) & EMC_MRR_DATA_MASK) <<
+			EMC_MRR_DATA_SHIFT;
+
+		temp0_0 |= mrr_data & 0xff;
+		temp0_1 |= (mrr_data & 0xff00) >> 8;
+
+		if (channel_mode == DUAL_CHANNEL) {
+			mrr_data = (emc1_readl(emc, EMC_MRR) & EMC_MRR_DATA_MASK) <<
+				EMC_MRR_DATA_SHIFT;
+			temp1_0 |= (mrr_data & 0xff);
+			temp1_1 |= (mrr_data & 0xff00) >> 8;
+		}
+	}
+
+	cval = (1000000 * tegra210_actual_osc_clocks(last_timing->run_clocks)) /
+		(last_timing_rate_mhz * 2 * temp0_0);
+
+	if (dvfs_pt1)
+		__INCREMENT_PTFV(C0D1U0, cval);
+	else if (dvfs_update)
+		__AVERAGE_PTFV(C0D1U0);
+	else if (periodic_training_update)
+		__WEIGHTED_UPDATE_PTFV(C0D1U0, cval);
+
+	if (dvfs_update || periodic_training_update) {
+		tdel = next_timing->current_dram_clktree_c0d1u0 -
+			__MOVAVG_AC(next_timing, C0D1U0);
+		tmdel = (tdel < 0) ? -1 * tdel : tdel;
+		if (tmdel > adel)
+			adel = tmdel;
+
+		if (tmdel * 128 * next_timing_rate_mhz / 1000000 >
+		    next_timing->tree_margin)
+			next_timing->current_dram_clktree_c0d1u0 =
+				__MOVAVG_AC(next_timing, C0D1U0);
+	}
+
+	cval = (1000000 * tegra210_actual_osc_clocks(last_timing->run_clocks)) /
+		(last_timing_rate_mhz * 2 * temp0_1);
+
+	if (dvfs_pt1)
+		__INCREMENT_PTFV(C0D1U1, cval);
+	else if (dvfs_update)
+		__AVERAGE_PTFV(C0D1U1);
+	else if (periodic_training_update)
+		__WEIGHTED_UPDATE_PTFV(C0D1U1, cval);
+
+	if (dvfs_update || periodic_training_update) {
+		tdel = next_timing->current_dram_clktree_c0d1u1 -
+			__MOVAVG_AC(next_timing, C0D1U1);
+		tmdel = (tdel < 0) ? -1 * tdel : tdel;
+		if (tmdel > adel)
+			adel = tmdel;
+
+		if (tmdel * 128 * next_timing_rate_mhz / 1000000 >
+		    next_timing->tree_margin)
+			next_timing->current_dram_clktree_c0d1u1 =
+				__MOVAVG_AC(next_timing, C0D1U1);
+	}
+
+	if (channel_mode == DUAL_CHANNEL) {
+		cval = (1000000 * tegra210_actual_osc_clocks(last_timing->run_clocks)) /
+			(last_timing_rate_mhz * 2 * temp1_0);
+
+		if (dvfs_pt1)
+			__INCREMENT_PTFV(C1D1U0, cval);
+		else if (dvfs_update)
+			__AVERAGE_PTFV(C1D1U0);
+		else if (periodic_training_update)
+			__WEIGHTED_UPDATE_PTFV(C1D1U0, cval);
+
+		if (dvfs_update || periodic_training_update) {
+			tdel = next_timing->current_dram_clktree_c1d1u0 -
+				__MOVAVG_AC(next_timing, C1D1U0);
+			tmdel = (tdel < 0) ? -1 * tdel : tdel;
+			if (tmdel > adel)
+				adel = tmdel;
+
+			if (tmdel * 128 * next_timing_rate_mhz / 1000000 >
+			    next_timing->tree_margin)
+				next_timing->current_dram_clktree_c1d1u0 =
+					__MOVAVG_AC(next_timing, C1D1U0);
+		}
+
+		cval = (1000000 * tegra210_actual_osc_clocks(last_timing->run_clocks)) /
+			(last_timing_rate_mhz * 2 * temp1_1);
+
+		if (dvfs_pt1)
+			__INCREMENT_PTFV(C1D1U1, cval);
+		else if (dvfs_update)
+			__AVERAGE_PTFV(C1D1U1);
+		else if (periodic_training_update)
+			__WEIGHTED_UPDATE_PTFV(C1D1U1, cval);
+
+		if (dvfs_update || periodic_training_update) {
+			tdel = next_timing->current_dram_clktree_c1d1u1 -
+				__MOVAVG_AC(next_timing, C1D1U1);
+			tmdel = (tdel < 0) ? -1 * tdel : tdel;
+			if (tmdel > adel)
+				adel = tmdel;
+
+			if (tmdel * 128 * next_timing_rate_mhz / 1000000 >
+			    next_timing->tree_margin)
+				next_timing->current_dram_clktree_c1d1u1 =
+					__MOVAVG_AC(next_timing, C1D1U1);
+		}
+	}
+
+done:
+	return adel;
+}
+
+static u32 periodic_compensation_handler(struct tegra_emc *emc, u32 type,
+					 u32 dram_dev_num,
+					 u32 channel_mode,
+					 struct emc_timing *last_timing,
+					 struct emc_timing *next_timing)
+{
+#define __COPY_EMA(nt, lt, dev)						\
+	({ __MOVAVG(nt, dev) = __MOVAVG(lt, dev) *			\
+	   (nt)->ptfv_list[PTFV_DVFS_SAMPLES_INDEX]; })
+
+	u32 i;
+	u32 adel = 0;
+	u32 samples = next_timing->ptfv_list[PTFV_DVFS_SAMPLES_INDEX];
+	u32 delay = 2 + (1000 * tegra210_actual_osc_clocks(last_timing->run_clocks) /
+			 last_timing->rate);
+
+	if (!next_timing->periodic_training)
+		return 0;
+
+	if (type == DVFS_SEQUENCE) {
+		if (last_timing->periodic_training &&
+		    (next_timing->ptfv_list[PTFV_CONFIG_CTRL_INDEX] &
+		     PTFV_CONFIG_CTRL_USE_PREVIOUS_EMA)) {
+			/*
+			 * If the previous frequency was using periodic
+			 * calibration then we can reuse the previous
+			 * frequencies EMA data.
+			 */
+			__COPY_EMA(next_timing, last_timing, C0D0U0);
+			__COPY_EMA(next_timing, last_timing, C0D0U1);
+			__COPY_EMA(next_timing, last_timing, C1D0U0);
+			__COPY_EMA(next_timing, last_timing, C1D0U1);
+			__COPY_EMA(next_timing, last_timing, C0D1U0);
+			__COPY_EMA(next_timing, last_timing, C0D1U1);
+			__COPY_EMA(next_timing, last_timing, C1D1U0);
+			__COPY_EMA(next_timing, last_timing, C1D1U1);
+		} else {
+			/* Reset the EMA.*/
+			__MOVAVG(next_timing, C0D0U0) = 0;
+			__MOVAVG(next_timing, C0D0U1) = 0;
+			__MOVAVG(next_timing, C1D0U0) = 0;
+			__MOVAVG(next_timing, C1D0U1) = 0;
+			__MOVAVG(next_timing, C0D1U0) = 0;
+			__MOVAVG(next_timing, C0D1U1) = 0;
+			__MOVAVG(next_timing, C1D1U0) = 0;
+			__MOVAVG(next_timing, C1D1U1) = 0;
+
+			for (i = 0; i < samples; i++) {
+				tegra210_start_periodic_compensation(emc);
+				udelay(delay);
+
+				/*
+				 * Generate next sample of data.
+				 */
+				adel = update_clock_tree_delay(emc,
+							       emc->dram_dev_num,
+							       channel_mode,
+							       DVFS_PT1);
+			}
+		}
+
+		/* Seems like it should be part of the
+		 * 'if (last_timing->periodic_training)' conditional
+		 * since is already done for the else clause. */
+		adel = update_clock_tree_delay(emc,
+					       emc->dram_dev_num,
+					       channel_mode,
+					       DVFS_UPDATE);
+	}
+
+	if (type == PERIODIC_TRAINING_SEQUENCE) {
+		tegra210_start_periodic_compensation(emc);
+		udelay(delay);
+
+		adel = update_clock_tree_delay(emc,
+					       emc->dram_dev_num,
+					       channel_mode,
+					       PERIODIC_TRAINING_UPDATE);
+	}
+
+	return adel;
+}
+
+u32 __do_periodic_emc_compensation_r21021(struct tegra_emc *emc)
+{
+	u32 channel_mode;
+	u32 emc_cfg, emc_cfg_o;
+	u32 emc_dbg_o;
+	u32 del, i;
+	u32 list[] = {
+		EMC_PMACRO_OB_DDLL_LONG_DQ_RANK0_0,
+		EMC_PMACRO_OB_DDLL_LONG_DQ_RANK0_1,
+		EMC_PMACRO_OB_DDLL_LONG_DQ_RANK0_2,
+		EMC_PMACRO_OB_DDLL_LONG_DQ_RANK0_3,
+		EMC_PMACRO_OB_DDLL_LONG_DQ_RANK1_0,
+		EMC_PMACRO_OB_DDLL_LONG_DQ_RANK1_1,
+		EMC_PMACRO_OB_DDLL_LONG_DQ_RANK1_2,
+		EMC_PMACRO_OB_DDLL_LONG_DQ_RANK1_3,
+		EMC_DATA_BRLSHFT_0,
+		EMC_DATA_BRLSHFT_1
+	};
+	u32 items = ARRAY_SIZE(list);
+	u32 emc_cfg_update;
+	struct emc_timing *current_timing = emc->current_timing;
+
+	if (current_timing->periodic_training) {
+		channel_mode = !!(current_timing->burst_regs[EMC_FBIO_CFG7_INDEX] &
+				  (1 << 2));
+
+		emc_cc_dbg(PER_TRAIN, "Periodic training starting\n");
+
+		emc_dbg_o = emc_readl(emc, EMC_DBG);
+		emc_cfg_o = emc_readl(emc, EMC_CFG);
+		emc_cfg = emc_cfg_o & ~(EMC_CFG_DYN_SELF_REF | EMC_CFG_DRAM_ACPD |
+					EMC_CFG_DRAM_CLKSTOP_PD |
+					EMC_CFG_DRAM_CLKSTOP_PD);
+
+
+		/*
+		 * 1. Power optimizations should be off.
+		 */
+		emc_writel(emc, emc_cfg, EMC_CFG);
+
+		/* Does emc_timing_update() for above changes. */
+		tegra210_dll_disable(emc, channel_mode);
+
+		wait_for_update(emc, EMC_EMC_STATUS,
+				EMC_EMC_STATUS_DRAM_IN_POWERDOWN_MASK, 0, REG_EMC);
+		if (channel_mode)
+			wait_for_update(emc, EMC_EMC_STATUS,
+				EMC_EMC_STATUS_DRAM_IN_POWERDOWN_MASK, 0, REG_EMC1);
+
+		wait_for_update(emc, EMC_EMC_STATUS,
+				EMC_EMC_STATUS_DRAM_IN_SELF_REFRESH_MASK, 0, REG_EMC);
+		if (channel_mode)
+			wait_for_update(emc, EMC_EMC_STATUS,
+				EMC_EMC_STATUS_DRAM_IN_SELF_REFRESH_MASK, 0, REG_EMC1);
+
+		emc_cfg_update = emc_readl(emc, EMC_CFG_UPDATE);
+		emc_writel(emc, (emc_cfg_update &
+			    ~EMC_CFG_UPDATE_UPDATE_DLL_IN_UPDATE_MASK) |
+			   (2 << EMC_CFG_UPDATE_UPDATE_DLL_IN_UPDATE_SHIFT),
+			   EMC_CFG_UPDATE);
+
+		/*
+		 * 2. osc kick off - this assumes training and dvfs have set
+		 *    correct MR23.
+		 */
+		tegra210_start_periodic_compensation(emc);
+
+		/*
+		 * 3. Let dram capture its clock tree delays.
+		 */
+		udelay((tegra210_actual_osc_clocks(current_timing->run_clocks) * 1000) /
+		       current_timing->rate + 1);
+
+		/*
+		 * 4. Check delta wrt previous values (save value if margin
+		 *    exceeds what is set in table).
+		 */
+		del = periodic_compensation_handler(emc, PERIODIC_TRAINING_SEQUENCE,
+						    emc->dram_dev_num,
+						    channel_mode,
+						    current_timing,
+						    current_timing);
+
+		/*
+		 * 5. Apply compensation w.r.t. trained values (if clock tree
+		 *    has drifted more than the set margin).
+		 */
+		if (current_timing->tree_margin <
+		    ((del * 128 * (current_timing->rate / 1000)) / 1000000)) {
+			for (i = 0; i < items; i++) {
+				u32 tmp = tegra210_apply_periodic_compensation_trimmer(
+						       current_timing, list[i]);
+				emc_cc_dbg(EMA_WRITES, "0x%08x <= 0x%08x\n",
+					   list[i], tmp);
+				emc_writel(emc, tmp, list[i]);
+			}
+		}
+
+		emc_writel(emc, emc_cfg_o, EMC_CFG);
+
+		/*
+		 * 6. Timing update actally applies the new trimmers.
+		 */
+		emc_timing_update(emc, channel_mode);
+
+		/* 6.1. Restore the UPDATE_DLL_IN_UPDATE field. */
+		emc_writel(emc, emc_cfg_update, EMC_CFG_UPDATE);
+
+		/* 6.2. Restore the DLL. */
+		tegra210_dll_enable(emc, channel_mode);
+
+		/*
+		 * 7. Copy over the periodic training registers that we updated
+		 *    here to the corresponding derated/non-derated table.
+		 */
+		tegra210_update_emc_alt_timing(emc, current_timing);
+	}
+
+	return 0;
+}
+
+/*
+ * Do the clock change sequence.
+ */
+void emc_set_clock_r21021(struct tegra_emc *emc, struct clk *new_parent)
+{
+	/*
+	 * This is the timing table for the source frequency. It does _not_
+	 * necessarily correspond to the actual timing values in the EMC at the
+	 * moment. If the boot BCT differs from the table then this can happen.
+	 * However, we need it for accessing the dram_timings (which are not
+	 * really registers) array for the current frequency.
+	 */
+	struct emc_timing *fake_timing;
+	struct emc_timing *last_timing = emc->current_timing;
+	struct emc_timing *next_timing = emc->next_timing;
+
+	u32 i, tmp;
+
+	u32 cya_allow_ref_cc = 0, ref_b4_sref_en = 0, cya_issue_pc_ref = 0;
+
+	u32 zqcal_before_cc_cutoff = 2400; /* In picoseconds */
+	u32 ref_delay_mult;
+	u32 ref_delay;
+	s32 zq_latch_dvfs_wait_time;
+	s32 tZQCAL_lpddr4_fc_adj;
+	/* Scaled by x1000 */
+	u32 tFC_lpddr4 = 1000 * next_timing->dram_timings[T_FC_LPDDR4];
+	/* u32 tVRCG_lpddr4 = next_timing->dram_timings[T_FC_LPDDR4]; */
+	u32 tZQCAL_lpddr4 = 1000000;
+
+	u32 dram_type, shared_zq_resistor;
+	u32 channel_mode;
+	u32 is_lpddr3;
+
+	u32 emc_cfg, emc_sel_dpd_ctrl, emc_cfg_reg;
+
+	u32 emc_dbg;
+	u32 emc_zcal_interval;
+	u32 emc_zcal_wait_cnt_old;
+	u32 emc_zcal_wait_cnt_new;
+	u32 emc_dbg_active;
+	u32 zq_op;
+	u32 zcal_wait_time_clocks;
+	u32 zcal_wait_time_ps;
+
+	u32 emc_auto_cal_config;
+	u32 auto_cal_en;
+
+	u32 mr13_catr_enable;
+
+	u32 ramp_up_wait = 0, ramp_down_wait = 0;
+
+	/* In picoseconds. */
+	u32 source_clock_period;
+	u32 destination_clock_period;
+
+	u32 emc_dbg_o;
+	u32 emc_cfg_pipe_clk_o;
+	u32 emc_pin_o;
+
+	u32 mr13_flip_fspwr;
+	u32 mr13_flip_fspop;
+
+	u32 opt_zcal_en_cc;
+	u32 opt_do_sw_qrst = 1;
+	u32 opt_dvfs_mode;
+	u32 opt_dll_mode;
+	u32 opt_cc_short_zcal = 1;
+	u32 opt_short_zcal = 1;
+	u32 save_restore_clkstop_pd = 1;
+
+	u32 prelock_dll_en = 0, dll_out;
+
+	int next_push, next_dq_e_ivref, next_dqs_e_ivref;
+
+	u32 opt_war_200024907;
+	u32 zq_wait_long;
+	u32 zq_wait_short;
+
+	u32 bg_regulator_switch_complete_wait_clks;
+	u32 bg_regulator_mode_change;
+	u32 enable_bglp_regulator;
+	u32 enable_bg_regulator;
+
+	u32 tRTM;
+	u32 RP_war;
+	u32 R2P_war;
+	u32 TRPab_war;
+	s32 nRTP;
+	u32 deltaTWATM;
+	u32 W2P_war;
+	u32 tRPST;
+
+	u32 mrw_req;
+	u32 adel = 0, compensate_trimmer_applicable = 0;
+	u32 next_timing_rate_mhz = next_timing->rate / 1000;
+
+	static u32 fsp_for_next_freq;
+
+	emc_cc_dbg(INFO, "Running clock change.\n");
+
+	fake_timing = get_timing_from_freq(emc, last_timing->rate);
+
+	fsp_for_next_freq = !fsp_for_next_freq;
+
+	dram_type = emc_readl(emc, EMC_FBIO_CFG5) &
+		EMC_FBIO_CFG5_DRAM_TYPE_MASK >> EMC_FBIO_CFG5_DRAM_TYPE_SHIFT;
+	shared_zq_resistor = last_timing->burst_regs[EMC_ZCAL_WAIT_CNT_INDEX] &
+		1 << 31; /* needs def */
+	channel_mode = !!(last_timing->burst_regs[EMC_FBIO_CFG7_INDEX] &
+			  1 << 2); /* needs def */
+	opt_zcal_en_cc = (next_timing->burst_regs[EMC_ZCAL_INTERVAL_INDEX] &&
+			  !last_timing->burst_regs[EMC_ZCAL_INTERVAL_INDEX]) ||
+			  dram_type == DRAM_TYPE_LPDDR4;
+	opt_dll_mode = (dram_type == DRAM_TYPE_DDR3) ?
+		get_dll_state(next_timing) : DLL_OFF;
+	is_lpddr3 = (dram_type == DRAM_TYPE_LPDDR2) &&
+		next_timing->burst_regs[EMC_FBIO_CFG5_INDEX] &
+		1 << 25; /* needs def */
+	opt_war_200024907 = (dram_type == DRAM_TYPE_LPDDR4);
+	opt_dvfs_mode = MAN_SR;
+
+	emc_cfg_reg = emc_readl(emc, EMC_CFG);
+	emc_auto_cal_config = emc_readl(emc, EMC_AUTO_CAL_CONFIG);
+
+	source_clock_period = 1000000000 / last_timing->rate;
+	destination_clock_period = 1000000000 / next_timing->rate;
+
+	tZQCAL_lpddr4_fc_adj = (destination_clock_period >
+				zqcal_before_cc_cutoff) ?
+		tZQCAL_lpddr4 / destination_clock_period :
+		(tZQCAL_lpddr4 - tFC_lpddr4) / destination_clock_period;
+	emc_dbg_o = emc_readl(emc, EMC_DBG);
+	emc_pin_o = emc_readl(emc, EMC_PIN);
+	emc_cfg_pipe_clk_o = emc_readl(emc, EMC_CFG_PIPE_CLK);
+	emc_dbg = emc_dbg_o;
+
+	emc_cfg = next_timing->burst_regs[EMC_CFG_INDEX];
+	emc_cfg &= ~(EMC_CFG_DYN_SELF_REF | EMC_CFG_DRAM_ACPD |
+		     EMC_CFG_DRAM_CLKSTOP_SR | EMC_CFG_DRAM_CLKSTOP_PD);
+	emc_sel_dpd_ctrl = next_timing->emc_sel_dpd_ctrl;
+	emc_sel_dpd_ctrl &= ~(EMC_SEL_DPD_CTRL_CLK_SEL_DPD_EN |
+			      EMC_SEL_DPD_CTRL_CA_SEL_DPD_EN |
+			      EMC_SEL_DPD_CTRL_RESET_SEL_DPD_EN |
+			      EMC_SEL_DPD_CTRL_ODT_SEL_DPD_EN |
+			      EMC_SEL_DPD_CTRL_DATA_SEL_DPD_EN);
+
+	emc_cc_dbg(INFO, "Clock change version: %d\n",
+		   DVFS_CLOCK_CHANGE_VERSION);
+	emc_cc_dbg(INFO, "DRAM type = %d\n", emc->dram_type);
+	emc_cc_dbg(INFO, "DRAM dev #: %d\n", emc->dram_dev_num);
+	emc_cc_dbg(INFO, "DLL clksrc:      0x%08x\n", next_timing->dll_clk_src);
+	emc_cc_dbg(INFO, "last rate: %u, next rate %u\n", last_timing->rate,
+		   next_timing->rate);
+	emc_cc_dbg(INFO, "last period: %u, next period: %u\n",
+		   source_clock_period, destination_clock_period);
+	emc_cc_dbg(INFO, "  shared_zq_resistor: %d\n", !!shared_zq_resistor);
+	emc_cc_dbg(INFO, "  channel_mode: %d\n", channel_mode);
+	emc_cc_dbg(INFO, "  opt_dll_mode: %d\n", opt_dll_mode);
+
+	/* Step 1:
+	 *   Pre DVFS SW sequence.
+	 */
+	emc_cc_dbg(STEPS, "Step 1\n");
+	emc_cc_dbg(STEPS, "Step 1.1: Disable DLL temporarily.\n");
+	tmp = emc_readl(emc, EMC_CFG_DIG_DLL);
+	tmp &= ~EMC_CFG_DIG_DLL_CFG_DLL_EN;
+	emc_writel(emc, tmp, EMC_CFG_DIG_DLL);
+
+	emc_timing_update(emc, channel_mode);
+	wait_for_update(emc, EMC_CFG_DIG_DLL,
+			EMC_CFG_DIG_DLL_CFG_DLL_EN, 0, REG_EMC);
+	if (channel_mode)
+		wait_for_update(emc, EMC_CFG_DIG_DLL,
+				EMC_CFG_DIG_DLL_CFG_DLL_EN, 0, REG_EMC1);
+
+	emc_cc_dbg(STEPS, "Step 1.2: Disable AUTOCAL temporarily.\n");
+	emc_auto_cal_config = next_timing->emc_auto_cal_config;
+	auto_cal_en = emc_auto_cal_config & EMC_AUTO_CAL_CONFIG_AUTO_CAL_ENABLE;
+	emc_auto_cal_config &= ~EMC_AUTO_CAL_CONFIG_AUTO_CAL_START;
+	emc_auto_cal_config |=  EMC_AUTO_CAL_CONFIG_AUTO_CAL_MEASURE_STALL;
+	emc_auto_cal_config |=  EMC_AUTO_CAL_CONFIG_AUTO_CAL_UPDATE_STALL;
+	emc_auto_cal_config |=  auto_cal_en;
+	emc_writel(emc, emc_auto_cal_config, EMC_AUTO_CAL_CONFIG);
+	emc_readl(emc, EMC_AUTO_CAL_CONFIG); /* Flush write. */
+
+	emc_cc_dbg(STEPS, "Step 1.3: Disable other power features.\n");
+	emc_set_shadow_bypass(emc, ACTIVE);
+	emc_writel(emc, emc_cfg, EMC_CFG);
+	emc_writel(emc, emc_sel_dpd_ctrl, EMC_SEL_DPD_CTRL);
+	emc_set_shadow_bypass(emc, ASSEMBLY);
+
+	if (next_timing->periodic_training) {
+		tegra210_reset_dram_clktree_values(next_timing);
+
+		wait_for_update(emc, EMC_EMC_STATUS,
+				EMC_EMC_STATUS_DRAM_IN_POWERDOWN_MASK, 0, REG_EMC);
+		if (channel_mode)
+			wait_for_update(emc, EMC_EMC_STATUS,
+				EMC_EMC_STATUS_DRAM_IN_POWERDOWN_MASK, 0, REG_EMC1);
+
+		wait_for_update(emc, EMC_EMC_STATUS,
+				EMC_EMC_STATUS_DRAM_IN_SELF_REFRESH_MASK, 0, REG_EMC);
+		if (channel_mode)
+			wait_for_update(emc, EMC_EMC_STATUS,
+				EMC_EMC_STATUS_DRAM_IN_SELF_REFRESH_MASK, 0, REG_EMC1);
+
+		tegra210_start_periodic_compensation(emc);
+
+		udelay(((1000 * tegra210_actual_osc_clocks(last_timing->run_clocks)) /
+			last_timing->rate) + 2);
+		adel = periodic_compensation_handler(emc, DVFS_SEQUENCE,
+						     emc->dram_dev_num,
+						     channel_mode,
+						     fake_timing, next_timing);
+		compensate_trimmer_applicable =
+			next_timing->periodic_training &&
+			((adel * 128 * next_timing_rate_mhz) / 1000000) >
+			next_timing->tree_margin;
+	}
+
+	emc_writel(emc, EMC_INTSTATUS_CLKCHANGE_COMPLETE, EMC_INTSTATUS);
+	emc_set_shadow_bypass(emc, ACTIVE);
+	emc_writel(emc, emc_cfg, EMC_CFG);
+	emc_writel(emc, emc_sel_dpd_ctrl, EMC_SEL_DPD_CTRL);
+	emc_writel(emc, emc_cfg_pipe_clk_o | EMC_CFG_PIPE_CLK_CLK_ALWAYS_ON,
+		   EMC_CFG_PIPE_CLK);
+	emc_writel(emc, next_timing->emc_fdpd_ctrl_cmd_no_ramp &
+		   ~EMC_FDPD_CTRL_CMD_NO_RAMP_CMD_DPD_NO_RAMP_ENABLE,
+		   EMC_FDPD_CTRL_CMD_NO_RAMP);
+
+	bg_regulator_mode_change =
+		((next_timing->burst_regs[EMC_PMACRO_BG_BIAS_CTRL_0_INDEX] &
+		  EMC_PMACRO_BG_BIAS_CTRL_0_BGLP_E_PWRD) ^
+		 (last_timing->burst_regs[EMC_PMACRO_BG_BIAS_CTRL_0_INDEX] &
+		  EMC_PMACRO_BG_BIAS_CTRL_0_BGLP_E_PWRD)) ||
+		((next_timing->burst_regs[EMC_PMACRO_BG_BIAS_CTRL_0_INDEX] &
+		  EMC_PMACRO_BG_BIAS_CTRL_0_BG_E_PWRD) ^
+		 (last_timing->burst_regs[EMC_PMACRO_BG_BIAS_CTRL_0_INDEX] &
+		  EMC_PMACRO_BG_BIAS_CTRL_0_BG_E_PWRD));
+	enable_bglp_regulator =
+		(next_timing->burst_regs[EMC_PMACRO_BG_BIAS_CTRL_0_INDEX] &
+		 EMC_PMACRO_BG_BIAS_CTRL_0_BGLP_E_PWRD) == 0;
+	enable_bg_regulator =
+		(next_timing->burst_regs[EMC_PMACRO_BG_BIAS_CTRL_0_INDEX] &
+		 EMC_PMACRO_BG_BIAS_CTRL_0_BG_E_PWRD) == 0;
+
+	if (bg_regulator_mode_change) {
+		if (enable_bg_regulator)
+			emc_writel(emc, last_timing->burst_regs
+				   [EMC_PMACRO_BG_BIAS_CTRL_0_INDEX] &
+				   ~EMC_PMACRO_BG_BIAS_CTRL_0_BG_E_PWRD,
+				   EMC_PMACRO_BG_BIAS_CTRL_0);
+		else
+			emc_writel(emc, last_timing->burst_regs
+				   [EMC_PMACRO_BG_BIAS_CTRL_0_INDEX] &
+				   ~EMC_PMACRO_BG_BIAS_CTRL_0_BGLP_E_PWRD,
+				   EMC_PMACRO_BG_BIAS_CTRL_0);
+
+	}
+
+	/* Check if we need to turn on VREF generator. */
+	if ((((last_timing->burst_regs[EMC_PMACRO_DATA_PAD_TX_CTRL_INDEX] &
+	       EMC_PMACRO_DATA_PAD_TX_CTRL_DATA_DQ_E_IVREF) == 0) &&
+	     ((next_timing->burst_regs[EMC_PMACRO_DATA_PAD_TX_CTRL_INDEX] &
+	       EMC_PMACRO_DATA_PAD_TX_CTRL_DATA_DQ_E_IVREF) == 1)) ||
+	    (((last_timing->burst_regs[EMC_PMACRO_DATA_PAD_TX_CTRL_INDEX] &
+	       EMC_PMACRO_DATA_PAD_TX_CTRL_DATA_DQS_E_IVREF) == 0) &&
+	     ((next_timing->burst_regs[EMC_PMACRO_DATA_PAD_TX_CTRL_INDEX] &
+	       EMC_PMACRO_DATA_PAD_TX_CTRL_DATA_DQS_E_IVREF) == 1))) {
+		u32 pad_tx_ctrl =
+		    next_timing->burst_regs[EMC_PMACRO_DATA_PAD_TX_CTRL_INDEX];
+		u32 last_pad_tx_ctrl =
+		    last_timing->burst_regs[EMC_PMACRO_DATA_PAD_TX_CTRL_INDEX];
+
+		next_dqs_e_ivref = pad_tx_ctrl &
+			EMC_PMACRO_DATA_PAD_TX_CTRL_DATA_DQS_E_IVREF;
+		next_dq_e_ivref = pad_tx_ctrl &
+			EMC_PMACRO_DATA_PAD_TX_CTRL_DATA_DQ_E_IVREF;
+		next_push = (last_pad_tx_ctrl &
+			     ~EMC_PMACRO_DATA_PAD_TX_CTRL_DATA_DQ_E_IVREF &
+			     ~EMC_PMACRO_DATA_PAD_TX_CTRL_DATA_DQS_E_IVREF) |
+			next_dq_e_ivref | next_dqs_e_ivref;
+		emc_writel(emc, next_push, EMC_PMACRO_DATA_PAD_TX_CTRL);
+		udelay(1);
+	} else if (bg_regulator_mode_change) {
+		udelay(1);
+	}
+
+	emc_set_shadow_bypass(emc, ASSEMBLY);
+
+	/* Step 2:
+	 *   Prelock the DLL.
+	 */
+	emc_cc_dbg(STEPS, "Step 2\n");
+	if (next_timing->burst_regs[EMC_CFG_DIG_DLL_INDEX] &
+	    EMC_CFG_DIG_DLL_CFG_DLL_EN) {
+		emc_cc_dbg(INFO, "Prelock enabled for target frequency.\n");
+		dll_out = tegra210_dll_prelock(emc, 0, new_parent);
+		emc_cc_dbg(INFO, "DLL out: 0x%03x\n", dll_out);
+		prelock_dll_en = 1;
+	} else {
+		emc_cc_dbg(INFO, "Disabling DLL for target frequency.\n");
+		tegra210_dll_disable(emc, channel_mode);
+	}
+
+	/* Step 3:
+	 *   Prepare autocal for the clock change.
+	 */
+	emc_cc_dbg(STEPS, "Step 3\n");
+	emc_set_shadow_bypass(emc, ACTIVE);
+	emc_writel(emc, next_timing->emc_auto_cal_config2, EMC_AUTO_CAL_CONFIG2);
+	emc_writel(emc, next_timing->emc_auto_cal_config3, EMC_AUTO_CAL_CONFIG3);
+	emc_writel(emc, next_timing->emc_auto_cal_config4, EMC_AUTO_CAL_CONFIG4);
+	emc_writel(emc, next_timing->emc_auto_cal_config5, EMC_AUTO_CAL_CONFIG5);
+	emc_writel(emc, next_timing->emc_auto_cal_config6, EMC_AUTO_CAL_CONFIG6);
+	emc_writel(emc, next_timing->emc_auto_cal_config7, EMC_AUTO_CAL_CONFIG7);
+	emc_writel(emc, next_timing->emc_auto_cal_config8, EMC_AUTO_CAL_CONFIG8);
+	emc_set_shadow_bypass(emc, ASSEMBLY);
+
+	emc_auto_cal_config |= (EMC_AUTO_CAL_CONFIG_AUTO_CAL_COMPUTE_START |
+				auto_cal_en);
+	emc_writel(emc, emc_auto_cal_config, EMC_AUTO_CAL_CONFIG);
+
+	/* Step 4:
+	 *   Update EMC_CFG. (??)
+	 */
+	emc_cc_dbg(STEPS, "Step 4\n");
+	if (source_clock_period > 50000 && dram_type == DRAM_TYPE_LPDDR4)
+		ccfifo_writel(emc, 1, EMC_SELF_REF, 0);
+	else
+		emc_writel(emc, next_timing->emc_cfg_2, EMC_CFG_2);
+
+	/* Step 5:
+	 *   Prepare reference variables for ZQCAL regs.
+	 */
+	emc_cc_dbg(STEPS, "Step 5\n");
+	emc_zcal_interval = 0;
+	emc_zcal_wait_cnt_old =
+		last_timing->burst_regs[EMC_ZCAL_WAIT_CNT_INDEX];
+	emc_zcal_wait_cnt_new =
+		next_timing->burst_regs[EMC_ZCAL_WAIT_CNT_INDEX];
+	emc_zcal_wait_cnt_old &= ~EMC_ZCAL_WAIT_CNT_ZCAL_WAIT_CNT_MASK;
+	emc_zcal_wait_cnt_new &= ~EMC_ZCAL_WAIT_CNT_ZCAL_WAIT_CNT_MASK;
+
+	if (dram_type == DRAM_TYPE_LPDDR4)
+		zq_wait_long = max((u32)1,
+				 div_o3(1000000, destination_clock_period));
+	else if (dram_type == DRAM_TYPE_LPDDR2 || is_lpddr3)
+		zq_wait_long = max(next_timing->min_mrs_wait,
+				 div_o3(360000, destination_clock_period)) + 4;
+	else if (dram_type == DRAM_TYPE_DDR3)
+		zq_wait_long = max((u32)256,
+				 div_o3(320000, destination_clock_period) + 2);
+	else
+		zq_wait_long = 0;
+
+	if (dram_type == DRAM_TYPE_LPDDR2 || is_lpddr3)
+		zq_wait_short = max(max(next_timing->min_mrs_wait, (u32)6),
+				  div_o3(90000, destination_clock_period)) + 4;
+	else if (dram_type == DRAM_TYPE_DDR3)
+		zq_wait_short = max((u32)64,
+				  div_o3(80000, destination_clock_period)) + 2;
+	else
+		zq_wait_short = 0;
+
+	/* Step 6:
+	 *   Training code - removed.
+	 */
+	emc_cc_dbg(STEPS, "Step 6\n");
+
+	/* Step 7:
+	 *   Program FSP reference registers and send MRWs to new FSPWR.
+	 */
+	emc_cc_dbg(STEPS, "Step 7\n");
+	emc_cc_dbg(SUB_STEPS, "Step 7.1: Bug 200024907 - Patch RP R2P");
+	if (opt_war_200024907) {
+		nRTP = 16;
+		if (source_clock_period >= 1000000/1866) /* 535.91 ps */
+			nRTP = 14;
+		if (source_clock_period >= 1000000/1600) /* 625.00 ps */
+			nRTP = 12;
+		if (source_clock_period >= 1000000/1333) /* 750.19 ps */
+			nRTP = 10;
+		if (source_clock_period >= 1000000/1066) /* 938.09 ps */
+			nRTP = 8;
+
+		deltaTWATM = max_t(u32, div_o3(7500, source_clock_period), 8);
+
+		/*
+		 * Originally there was a + .5 in the tRPST calculation.
+		 * However since we can't do FP in the kernel and the tRTM
+		 * computation was in a floating point ceiling function, adding
+		 * one to tRTP should be ok. There is no other source of non
+		 * integer values, so the result was always going to be
+		 * something for the form: f_ceil(N + .5) = N + 1;
+		 */
+		tRPST = ((last_timing->emc_mrw & 0x80) >> 7);
+		tRTM = fake_timing->dram_timings[RL] +
+			div_o3(3600, source_clock_period) +
+			max_t(u32, div_o3(7500, source_clock_period), 8) +
+			tRPST + 1 + nRTP;
+
+		emc_cc_dbg(INFO, "tRTM = %u, EMC_RP = %u\n", tRTM,
+			   next_timing->burst_regs[EMC_RP_INDEX]);
+
+		if (last_timing->burst_regs[EMC_RP_INDEX] < tRTM) {
+			if (tRTM > (last_timing->burst_regs[EMC_R2P_INDEX] +
+				    last_timing->burst_regs[EMC_RP_INDEX])) {
+				R2P_war = tRTM -
+					last_timing->burst_regs[EMC_RP_INDEX];
+				RP_war = last_timing->burst_regs[EMC_RP_INDEX];
+				TRPab_war =
+				       last_timing->burst_regs[EMC_TRPAB_INDEX];
+				if (R2P_war > 63) {
+					RP_war = R2P_war +
+						last_timing->burst_regs
+						[EMC_RP_INDEX] - 63;
+					if (TRPab_war < RP_war)
+						TRPab_war = RP_war;
+					R2P_war = 63;
+				}
+			} else {
+				R2P_war = last_timing->
+					burst_regs[EMC_R2P_INDEX];
+				RP_war = last_timing->burst_regs[EMC_RP_INDEX];
+				TRPab_war =
+				       last_timing->burst_regs[EMC_TRPAB_INDEX];
+			}
+
+			if (RP_war < deltaTWATM) {
+				W2P_war = last_timing->burst_regs[EMC_W2P_INDEX]
+					+ deltaTWATM - RP_war;
+				if (W2P_war > 63) {
+					RP_war = RP_war + W2P_war - 63;
+					if (TRPab_war < RP_war)
+						TRPab_war = RP_war;
+					W2P_war = 63;
+				}
+			} else {
+				W2P_war =
+					last_timing->burst_regs[EMC_W2P_INDEX];
+			}
+
+			if ((last_timing->burst_regs[EMC_W2P_INDEX] ^
+			     W2P_war) ||
+			    (last_timing->burst_regs[EMC_R2P_INDEX] ^
+			     R2P_war) ||
+			    (last_timing->burst_regs[EMC_RP_INDEX] ^
+			     RP_war) ||
+			    (last_timing->burst_regs[EMC_TRPAB_INDEX] ^
+			     TRPab_war)) {
+				emc_writel(emc, RP_war, EMC_RP);
+				emc_writel(emc, R2P_war, EMC_R2P);
+				emc_writel(emc, W2P_war, EMC_W2P);
+				emc_writel(emc, TRPab_war, EMC_TRPAB);
+			}
+			emc_timing_update(emc, DUAL_CHANNEL);
+		} else {
+			emc_cc_dbg(INFO, "Skipped WAR for bug 200024907\n");
+		}
+	}
+
+	if (!fsp_for_next_freq) {
+		mr13_flip_fspwr = (next_timing->emc_mrw3 & 0xffffff3f) | 0x80;
+		mr13_flip_fspop = (next_timing->emc_mrw3 & 0xffffff3f) | 0x00;
+	} else {
+		mr13_flip_fspwr = (next_timing->emc_mrw3 & 0xffffff3f) | 0x40;
+		mr13_flip_fspop = (next_timing->emc_mrw3 & 0xffffff3f) | 0xc0;
+	}
+
+	mr13_catr_enable = (mr13_flip_fspwr & 0xFFFFFFFE) | 0x01;
+	if (emc->dram_dev_num == TWO_RANK)
+		mr13_catr_enable =
+			(mr13_catr_enable & 0x3fffffff) | 0x80000000;
+
+	if (dram_type == DRAM_TYPE_LPDDR4) {
+		emc_writel(emc, mr13_flip_fspwr, EMC_MRW3);
+		emc_writel(emc, next_timing->emc_mrw, EMC_MRW);
+		emc_writel(emc, next_timing->emc_mrw2, EMC_MRW2);
+	}
+
+	/* Step 8:
+	 *   Program the shadow registers.
+	 */
+	emc_cc_dbg(STEPS, "Step 8\n");
+	emc_cc_dbg(SUB_STEPS, "Writing burst_regs\n");
+	for (i = 0; i < next_timing->num_burst; i++) {
+		u32 var;
+		u32 wval;
+
+		if (!burst_regs_off[i])
+			continue;
+
+		var = burst_regs_off[i];
+		wval = next_timing->burst_regs[i];
+
+		if (dram_type != DRAM_TYPE_LPDDR4 &&
+		    (var == EMC_MRW6      || var == EMC_MRW7 ||
+		     var == EMC_MRW8      || var == EMC_MRW9 ||
+		     var == EMC_MRW10     || var == EMC_MRW11 ||
+		     var == EMC_MRW12     || var == EMC_MRW13 ||
+		     var == EMC_MRW14     || var == EMC_MRW15 ||
+		     var == EMC_TRAINING_CTRL))
+			continue;
+
+		/* Pain... And suffering. */
+		if (var == EMC_CFG) {
+			wval &= ~EMC_CFG_DRAM_ACPD;
+			wval &= ~EMC_CFG_DYN_SELF_REF;
+			if (dram_type == DRAM_TYPE_LPDDR4) {
+				wval &= ~EMC_CFG_DRAM_CLKSTOP_SR;
+				wval &= ~EMC_CFG_DRAM_CLKSTOP_PD;
+			}
+		} else if (var == EMC_MRS_WAIT_CNT &&
+			   dram_type == DRAM_TYPE_LPDDR2 &&
+			   opt_zcal_en_cc && !opt_cc_short_zcal &&
+			   opt_short_zcal) {
+			wval = (wval & ~(EMC_MRS_WAIT_CNT_SHORT_WAIT_MASK <<
+					 EMC_MRS_WAIT_CNT_SHORT_WAIT_SHIFT)) |
+			   ((zq_wait_long & EMC_MRS_WAIT_CNT_SHORT_WAIT_MASK) <<
+			    EMC_MRS_WAIT_CNT_SHORT_WAIT_SHIFT);
+		} else if (var == EMC_ZCAL_WAIT_CNT &&
+			   dram_type == DRAM_TYPE_DDR3 && opt_zcal_en_cc &&
+			   !opt_cc_short_zcal && opt_short_zcal) {
+			wval = (wval & ~(EMC_ZCAL_WAIT_CNT_ZCAL_WAIT_CNT_MASK <<
+				       EMC_ZCAL_WAIT_CNT_ZCAL_WAIT_CNT_SHIFT)) |
+			    ((zq_wait_long &
+			      EMC_ZCAL_WAIT_CNT_ZCAL_WAIT_CNT_MASK) <<
+			      EMC_MRS_WAIT_CNT_SHORT_WAIT_SHIFT);
+		} else if (var == EMC_ZCAL_INTERVAL && opt_zcal_en_cc) {
+			wval = 0; /* EMC_ZCAL_INTERVAL reset value. */
+		} else if (var == EMC_PMACRO_AUTOCAL_CFG_COMMON) {
+			wval |= EMC_PMACRO_AUTOCAL_CFG_COMMON_E_CAL_BYPASS_DVFS;
+		} else if (var == EMC_PMACRO_DATA_PAD_TX_CTRL) {
+			wval &=
+			     ~(EMC_PMACRO_DATA_PAD_TX_CTRL_DATA_DQSP_TX_E_DCC |
+			       EMC_PMACRO_DATA_PAD_TX_CTRL_DATA_DQSN_TX_E_DCC |
+			       EMC_PMACRO_DATA_PAD_TX_CTRL_DATA_DQ_TX_E_DCC |
+			       EMC_PMACRO_DATA_PAD_TX_CTRL_DATA_CMD_TX_E_DCC);
+		} else if (var == EMC_PMACRO_CMD_PAD_TX_CTRL) {
+			wval |= EMC_PMACRO_CMD_PAD_TX_CTRL_CMD_DQ_TX_DRVFORCEON;
+			wval &= ~(EMC_PMACRO_CMD_PAD_TX_CTRL_CMD_DQSP_TX_E_DCC |
+				  EMC_PMACRO_CMD_PAD_TX_CTRL_CMD_DQSN_TX_E_DCC |
+				  EMC_PMACRO_CMD_PAD_TX_CTRL_CMD_DQ_TX_E_DCC |
+				  EMC_PMACRO_CMD_PAD_TX_CTRL_CMD_CMD_TX_E_DCC);
+		} else if (var == EMC_PMACRO_BRICK_CTRL_RFU1) {
+			wval &= 0xf800f800;
+		} else if (var == EMC_PMACRO_COMMON_PAD_TX_CTRL) {
+			wval &= 0xfffffff0;
+		}
+
+		emc_writel(emc, wval, var);
+	}
+
+	/* SW addition: do EMC refresh adjustment here. */
+	set_over_temp_timing(emc, next_timing, dram_over_temp_state);
+
+	if (dram_type == DRAM_TYPE_LPDDR4) {
+		mrw_req = (23 << EMC_MRW_MRW_MA_SHIFT) |
+			(next_timing->run_clocks & EMC_MRW_MRW_OP_MASK);
+		emc_writel(emc, mrw_req, EMC_MRW);
+	}
+
+	/* Per channel burst registers. */
+	emc_cc_dbg(SUB_STEPS, "Writing burst_regs_per_ch\n");
+	for (i = 0; i < next_timing->num_burst_per_ch; i++) {
+		if (!burst_regs_per_ch_off[i])
+			continue;
+
+		if (dram_type != DRAM_TYPE_LPDDR4 &&
+			(burst_regs_per_ch_off[i] == EMC_MRW6 ||
+                        burst_regs_per_ch_off[i] == EMC_MRW7 ||
+                        burst_regs_per_ch_off[i] == EMC_MRW8 ||
+                        burst_regs_per_ch_off[i] == EMC_MRW9 ||
+                        burst_regs_per_ch_off[i] == EMC_MRW10 ||
+                        burst_regs_per_ch_off[i] == EMC_MRW11 ||
+                        burst_regs_per_ch_off[i] == EMC_MRW12 ||
+                        burst_regs_per_ch_off[i] == EMC_MRW13 ||
+                        burst_regs_per_ch_off[i] == EMC_MRW14 ||
+                        burst_regs_per_ch_off[i] == EMC_MRW15))
+			continue;
+
+		/* Filter out second channel if not in DUAL_CHANNEL mode. */
+		if (channel_mode != DUAL_CHANNEL &&
+			burst_regs_per_ch_type[i] >= REG_EMC1)
+			continue;
+
+		emc_cc_dbg(REG_LISTS, "(%u) 0x%08x => 0x%08x\n",
+			   i, next_timing->burst_reg_per_ch[i],
+			   burst_regs_per_ch_off[i]);
+		emc_writel_per_ch(emc, next_timing->burst_reg_per_ch[i],
+			burst_regs_per_ch_type[i], burst_regs_per_ch_off[i]);
+	}
+
+	/* Vref regs. */
+	emc_cc_dbg(SUB_STEPS, "Writing vref_regs\n");
+	for (i = 0; i < next_timing->vref_num; i++) {
+		if (!vref_regs_per_ch_off[i])
+			continue;
+
+		if (channel_mode != DUAL_CHANNEL &&
+			vref_regs_per_ch_type[i] >= REG_EMC1)
+			continue;
+
+		emc_cc_dbg(REG_LISTS, "(%u) 0x%08x => 0x%08x\n",
+			   i, next_timing->vref_perch_regs[i],
+			   vref_regs_per_ch_off[i]);
+		emc_writel_per_ch(emc, next_timing->vref_perch_regs[i],
+				  vref_regs_per_ch_type[i],
+				  vref_regs_per_ch_off[i]);
+	}
+
+	/* Trimmers. */
+	emc_cc_dbg(SUB_STEPS, "Writing trim_regs\n");
+	for (i = 0; i < next_timing->num_trim; i++) {
+		u64 trim_reg;
+
+		if (!trim_regs_off[i])
+			continue;
+
+		trim_reg = trim_regs_off[i];
+		if (compensate_trimmer_applicable &&
+		    (trim_reg == EMC_PMACRO_OB_DDLL_LONG_DQ_RANK0_0 ||
+		     trim_reg == EMC_PMACRO_OB_DDLL_LONG_DQ_RANK0_1 ||
+		     trim_reg == EMC_PMACRO_OB_DDLL_LONG_DQ_RANK0_2 ||
+		     trim_reg == EMC_PMACRO_OB_DDLL_LONG_DQ_RANK0_3 ||
+		     trim_reg == EMC_PMACRO_OB_DDLL_LONG_DQ_RANK1_0 ||
+		     trim_reg == EMC_PMACRO_OB_DDLL_LONG_DQ_RANK1_1 ||
+		     trim_reg == EMC_PMACRO_OB_DDLL_LONG_DQ_RANK1_2 ||
+		     trim_reg == EMC_PMACRO_OB_DDLL_LONG_DQ_RANK1_3 ||
+		     trim_reg == EMC_DATA_BRLSHFT_0 ||
+		     trim_reg == EMC_DATA_BRLSHFT_1)) {
+			u32 reg =
+				tegra210_apply_periodic_compensation_trimmer(next_timing,
+								    trim_reg);
+			emc_cc_dbg(REG_LISTS, "(%u) 0x%08x => 0x%08x\n", i, reg,
+				   trim_regs_off[i]);
+			emc_cc_dbg(EMA_WRITES, "0x%08x <= 0x%08x\n",
+				   (u32)(u64)trim_regs_off[i], reg);
+			emc_writel(emc, reg, trim_regs_off[i]);
+		} else {
+			emc_cc_dbg(REG_LISTS, "(%u) 0x%08x => 0x%08x\n",
+				   i, next_timing->trim_regs[i],
+				   trim_regs_off[i]);
+			emc_writel(emc, next_timing->trim_regs[i],
+				   trim_regs_off[i]);
+		}
+
+	}
+
+	/* Per channel trimmers. */
+	emc_cc_dbg(SUB_STEPS, "Writing trim_regs_per_ch\n");
+	for (i = 0; i < next_timing->num_trim_per_ch; i++) {
+		u32 trim_reg;
+
+		if (!trim_regs_per_ch_off[i])
+			continue;
+
+		if (channel_mode != DUAL_CHANNEL &&
+			trim_regs_per_ch_type[i] >= REG_EMC1)
+			continue;
+
+		trim_reg = trim_regs_per_ch_off[i];
+		if (compensate_trimmer_applicable &&
+		    (trim_reg == EMC_PMACRO_OB_DDLL_LONG_DQ_RANK0_0 ||
+		     trim_reg == EMC_PMACRO_OB_DDLL_LONG_DQ_RANK0_1 ||
+		     trim_reg == EMC_PMACRO_OB_DDLL_LONG_DQ_RANK0_2 ||
+		     trim_reg == EMC_PMACRO_OB_DDLL_LONG_DQ_RANK0_3 ||
+		     trim_reg == EMC_PMACRO_OB_DDLL_LONG_DQ_RANK1_0 ||
+		     trim_reg == EMC_PMACRO_OB_DDLL_LONG_DQ_RANK1_1 ||
+		     trim_reg == EMC_PMACRO_OB_DDLL_LONG_DQ_RANK1_2 ||
+		     trim_reg == EMC_PMACRO_OB_DDLL_LONG_DQ_RANK1_3 ||
+		     trim_reg == EMC_DATA_BRLSHFT_0 ||
+		     trim_reg == EMC_DATA_BRLSHFT_1)) {
+			u32 reg =
+				tegra210_apply_periodic_compensation_trimmer(next_timing,
+							    trim_reg);
+			emc_cc_dbg(REG_LISTS, "(%u) 0x%08x => 0x%08x\n",
+				   i, reg, trim_regs_per_ch_off[i]);
+			emc_cc_dbg(EMA_WRITES, "0x%08x <= 0x%08x\n",
+				   trim_regs_per_ch_off[i], reg);
+			emc_writel_per_ch(emc, reg, trim_regs_per_ch_type[i],
+				     trim_regs_per_ch_off[i]);
+		} else {
+			emc_cc_dbg(REG_LISTS, "(%u) 0x%08x => 0x%08x\n",
+				   i, next_timing->trim_perch_regs[i],
+				   trim_regs_per_ch_off[i]);
+			emc_writel_per_ch(emc, next_timing->trim_perch_regs[i],
+				     trim_regs_per_ch_type[i],
+				     trim_regs_per_ch_off[i]);
+		}
+	}
+
+	emc_cc_dbg(SUB_STEPS, "Writing burst_mc_regs\n");
+	tegra_mc_write_emem_configuration(emc->mc, next_timing->rate);
+	
+	/* Registers to be programmed on the faster clock. */
+	if (next_timing->rate < last_timing->rate) {
+		emc_cc_dbg(SUB_STEPS, "Writing la_scale_regs\n");
+		tegra_mc_write_scaled_la_configuration(emc->mc,
+						       next_timing->rate);
+	}
+	/* Flush all the burst register writes. */
+	wmb();
+
+	/* Step 9:
+	 *   LPDDR4 section A.
+	 */
+	emc_cc_dbg(STEPS, "Step 9\n");
+	if (dram_type == DRAM_TYPE_LPDDR4) {
+		emc_writel(emc, emc_zcal_interval, EMC_ZCAL_INTERVAL);
+		emc_writel(emc, emc_zcal_wait_cnt_new, EMC_ZCAL_WAIT_CNT);
+
+		emc_dbg |= (EMC_DBG_WRITE_MUX_ACTIVE |
+			    EMC_DBG_WRITE_ACTIVE_ONLY);
+
+		emc_writel(emc, emc_dbg, EMC_DBG);
+		emc_writel(emc, emc_zcal_interval, EMC_ZCAL_INTERVAL);
+		emc_writel(emc, emc_dbg_o, EMC_DBG);
+	}
+
+	/* Step 10:
+	 *   LPDDR4 and DDR3 common section.
+	 */
+	emc_cc_dbg(STEPS, "Step 10\n");
+	if (opt_dvfs_mode == MAN_SR || dram_type == DRAM_TYPE_LPDDR4) {
+		if (dram_type == DRAM_TYPE_LPDDR4)
+			ccfifo_writel(emc, 0x101, EMC_SELF_REF, 0);
+		else
+			ccfifo_writel(emc, 0x1, EMC_SELF_REF, 0);
+
+		if (dram_type == DRAM_TYPE_LPDDR4 &&
+		    destination_clock_period <= zqcal_before_cc_cutoff) {
+			ccfifo_writel(emc, mr13_flip_fspwr ^ 0x40, EMC_MRW3, 0);
+			ccfifo_writel(emc, (next_timing->burst_regs[EMC_MRW6_INDEX] &
+				       0xFFFF3F3F) |
+				      (last_timing->burst_regs[EMC_MRW6_INDEX] &
+				       0x0000C0C0), EMC_MRW6, 0);
+			ccfifo_writel(emc,
+				(next_timing->burst_regs[EMC_MRW14_INDEX] &
+				 0xFFFF0707) |
+				(last_timing->burst_regs[EMC_MRW14_INDEX] &
+				 0x00003838), EMC_MRW14, 0);
+
+			if (emc->dram_dev_num == TWO_RANK) {
+				ccfifo_writel(emc,
+				      (next_timing->burst_regs[EMC_MRW7_INDEX] &
+				       0xFFFF3F3F) |
+				      (last_timing->burst_regs[EMC_MRW7_INDEX] &
+				       0x0000C0C0), EMC_MRW7, 0);
+				ccfifo_writel(emc,
+				     (next_timing->burst_regs[EMC_MRW15_INDEX] &
+				      0xFFFF0707) |
+				     (last_timing->burst_regs[EMC_MRW15_INDEX] &
+				      0x00003838), EMC_MRW15, 0);
+			}
+			if (opt_zcal_en_cc) {
+				if (emc->dram_dev_num == ONE_RANK)
+					ccfifo_writel(emc,
+						2 << EMC_ZQ_CAL_DEV_SEL_SHIFT |
+						EMC_ZQ_CAL_ZQ_CAL_CMD,
+						EMC_ZQ_CAL, 0);
+				else if (shared_zq_resistor)
+					ccfifo_writel(emc,
+						2 << EMC_ZQ_CAL_DEV_SEL_SHIFT |
+						EMC_ZQ_CAL_ZQ_CAL_CMD,
+						EMC_ZQ_CAL, 0);
+				else
+					ccfifo_writel(emc, EMC_ZQ_CAL_ZQ_CAL_CMD,
+						     EMC_ZQ_CAL, 0);
+			}
+		}
+	}
+
+	emc_dbg = emc_dbg_o;
+	if (dram_type == DRAM_TYPE_LPDDR4) {
+		ccfifo_writel(emc, mr13_flip_fspop | 0x8, EMC_MRW3,
+			      (1000 * fake_timing->dram_timings[T_RP]) /
+			      source_clock_period);
+		ccfifo_writel(emc, 0, 0, tFC_lpddr4 / source_clock_period);
+	}
+
+	if (dram_type == DRAM_TYPE_LPDDR4 || opt_dvfs_mode != MAN_SR) {
+		u32 t = 30 + (cya_allow_ref_cc ?
+			(4000 * fake_timing->dram_timings[T_RFC]) +
+			((1000 * fake_timing->dram_timings[T_RP]) /
+			 source_clock_period) : 0);
+
+		ccfifo_writel(emc, emc_pin_o & ~(EMC_PIN_PIN_CKE_PER_DEV |
+					    EMC_PIN_PIN_CKEB | EMC_PIN_PIN_CKE),
+			      EMC_PIN, t);
+	}
+
+	ref_delay_mult = 1;
+	ref_b4_sref_en = 0;
+	cya_issue_pc_ref = 0;
+
+	ref_delay_mult += ref_b4_sref_en   ? 1 : 0;
+	ref_delay_mult += cya_allow_ref_cc ? 1 : 0;
+	ref_delay_mult += cya_issue_pc_ref ? 1 : 0;
+	ref_delay = ref_delay_mult *
+		((1000 * fake_timing->dram_timings[T_RP]
+		  / source_clock_period) +
+		 (1000 * fake_timing->dram_timings[T_RFC] /
+		  source_clock_period)) + 20;
+
+	/* Step 11:
+	 *   Ramp down.
+	 */
+	emc_cc_dbg(STEPS, "Step 11\n");
+	ccfifo_writel(emc, 0x0, EMC_CFG_SYNC,
+		      dram_type == DRAM_TYPE_LPDDR4 ? 0 : ref_delay);
+
+	emc_dbg_active = emc_dbg | (EMC_DBG_WRITE_MUX_ACTIVE | /* Redundant. */
+				    EMC_DBG_WRITE_ACTIVE_ONLY);
+	ccfifo_writel(emc, emc_dbg_active, EMC_DBG, 0);
+
+	/* Todo: implement do_dvfs_power_ramp_down */
+	ramp_down_wait = tegra210_dvfs_power_ramp_down(emc,
+						source_clock_period, 0);
+
+	/* Step 12:
+	 *   And finally - trigger the clock change.
+	 */
+	emc_cc_dbg(STEPS, "Step 12\n");
+	ccfifo_writel(emc, 1, EMC_STALL_THEN_EXE_AFTER_CLKCHANGE, 0);
+	emc_dbg_active &= ~EMC_DBG_WRITE_ACTIVE_ONLY;
+	ccfifo_writel(emc, emc_dbg_active, EMC_DBG, 0);
+
+	/* Step 13:
+	 *   Ramp up.
+	 */
+	/* Todo: implement do_dvfs_power_ramp_up(). */
+	emc_cc_dbg(STEPS, "Step 13\n");
+	ramp_up_wait = tegra210_dvfs_power_ramp_up(emc,
+				destination_clock_period, 0);
+	ccfifo_writel(emc, emc_dbg, EMC_DBG, 0);
+
+	/* Step 14:
+	 *   Bringup CKE pins.
+	 */
+	emc_cc_dbg(STEPS, "Step 14\n");
+	if (dram_type == DRAM_TYPE_LPDDR4) {
+		u32 r = emc_pin_o | EMC_PIN_PIN_CKE;
+		if (emc->dram_dev_num == TWO_RANK)
+			ccfifo_writel(emc, r | EMC_PIN_PIN_CKEB |
+				      EMC_PIN_PIN_CKE_PER_DEV, EMC_PIN,
+				      0);
+		else
+			ccfifo_writel(emc, r & ~(EMC_PIN_PIN_CKEB |
+					    EMC_PIN_PIN_CKE_PER_DEV),
+				      EMC_PIN, 0);
+	}
+
+	/* Step 15: (two step 15s ??)
+	 *   Calculate zqlatch wait time; has dependency on ramping times.
+	 */
+	emc_cc_dbg(STEPS, "Step 15\n");
+
+	if (destination_clock_period <= zqcal_before_cc_cutoff) {
+		s32 t = (s32)(ramp_up_wait + ramp_down_wait) /
+			(s32)destination_clock_period;
+		zq_latch_dvfs_wait_time = (s32)tZQCAL_lpddr4_fc_adj - t;
+	} else {
+		zq_latch_dvfs_wait_time = tZQCAL_lpddr4_fc_adj -
+			div_o3(1000 * next_timing->dram_timings[T_PDEX],
+			       destination_clock_period);
+	}
+
+	emc_cc_dbg(INFO, "tZQCAL_lpddr4_fc_adj = %u\n", tZQCAL_lpddr4_fc_adj);
+	emc_cc_dbg(INFO, "destination_clock_period = %u\n",
+		   destination_clock_period);
+	emc_cc_dbg(INFO, "next_timing->dram_timings[T_PDEX] = %u\n",
+		   next_timing->dram_timings[T_PDEX]);
+	emc_cc_dbg(INFO, "zq_latch_dvfs_wait_time = %d\n",
+		   max_t(s32, 0, zq_latch_dvfs_wait_time));
+
+	if (dram_type == DRAM_TYPE_LPDDR4 && opt_zcal_en_cc) {
+		if (emc->dram_dev_num == ONE_RANK) {
+			if (destination_clock_period > zqcal_before_cc_cutoff)
+				ccfifo_writel(emc, 2 << EMC_ZQ_CAL_DEV_SEL_SHIFT |
+				   EMC_ZQ_CAL_ZQ_CAL_CMD, EMC_ZQ_CAL,
+				   div_o3(1000 *
+					  next_timing->dram_timings[T_PDEX],
+					  destination_clock_period));
+			ccfifo_writel(emc, (mr13_flip_fspop & 0xFFFFFFF7) |
+				   0x0C000000, EMC_MRW3,
+				   div_o3(1000 *
+					  next_timing->dram_timings[T_PDEX],
+					  destination_clock_period));
+			ccfifo_writel(emc, 0, EMC_SELF_REF, 0);
+			ccfifo_writel(emc, 0, EMC_REF, 0);
+			ccfifo_writel(emc, 2 << EMC_ZQ_CAL_DEV_SEL_SHIFT |
+				      EMC_ZQ_CAL_ZQ_LATCH_CMD,
+				      EMC_ZQ_CAL,
+				      max_t(s32, 0, zq_latch_dvfs_wait_time));
+		} else if (shared_zq_resistor) {
+			if (destination_clock_period > zqcal_before_cc_cutoff)
+				ccfifo_writel(emc, 2 << EMC_ZQ_CAL_DEV_SEL_SHIFT |
+				   EMC_ZQ_CAL_ZQ_CAL_CMD, EMC_ZQ_CAL,
+				   div_o3(1000 *
+					  next_timing->dram_timings[T_PDEX],
+					  destination_clock_period));
+
+			ccfifo_writel(emc, 2 << EMC_ZQ_CAL_DEV_SEL_SHIFT |
+				  EMC_ZQ_CAL_ZQ_LATCH_CMD, EMC_ZQ_CAL,
+				  max_t(s32, 0, zq_latch_dvfs_wait_time) +
+				  div_o3(1000 *
+					 next_timing->dram_timings[T_PDEX],
+					 destination_clock_period));
+			ccfifo_writel(emc, 1 << EMC_ZQ_CAL_DEV_SEL_SHIFT |
+				      EMC_ZQ_CAL_ZQ_LATCH_CMD,
+				      EMC_ZQ_CAL, 0);
+
+			ccfifo_writel(emc, (mr13_flip_fspop & 0xfffffff7) |
+				      0x0c000000, EMC_MRW3, 0);
+			ccfifo_writel(emc, 0, EMC_SELF_REF, 0);
+			ccfifo_writel(emc, 0, EMC_REF, 0);
+
+			ccfifo_writel(emc, 1 << EMC_ZQ_CAL_DEV_SEL_SHIFT |
+				      EMC_ZQ_CAL_ZQ_LATCH_CMD, EMC_ZQ_CAL,
+				      tZQCAL_lpddr4 / destination_clock_period);
+		} else {
+			if (destination_clock_period > zqcal_before_cc_cutoff) {
+				ccfifo_writel(emc, EMC_ZQ_CAL_ZQ_CAL_CMD, EMC_ZQ_CAL,
+				   div_o3(1000 *
+					  next_timing->dram_timings[T_PDEX],
+					  destination_clock_period));
+			}
+
+			ccfifo_writel(emc, (mr13_flip_fspop & 0xfffffff7) |
+				   0x0c000000, EMC_MRW3,
+				   div_o3(1000 *
+					  next_timing->dram_timings[T_PDEX],
+					  destination_clock_period));
+			ccfifo_writel(emc, 0, EMC_SELF_REF, 0);
+			ccfifo_writel(emc, 0, EMC_REF, 0);
+
+			ccfifo_writel(emc, EMC_ZQ_CAL_ZQ_LATCH_CMD, EMC_ZQ_CAL,
+				      max_t(s32, 0, zq_latch_dvfs_wait_time));
+		}
+	}
+
+	/* WAR: delay for zqlatch */
+	ccfifo_writel(emc, 0, 0, 10);
+
+	/* Step 16:
+	 *   LPDDR4 Conditional Training Kickoff. Removed.
+	 */
+
+	/* Step 17:
+	 *   MANSR exit self refresh.
+	 */
+	emc_cc_dbg(STEPS, "Step 17\n");
+	if (opt_dvfs_mode == MAN_SR && dram_type != DRAM_TYPE_LPDDR4)
+		ccfifo_writel(emc, 0, EMC_SELF_REF, 0);
+
+	/* Step 18:
+	 *   Send MRWs to LPDDR3/DDR3.
+	 */
+	emc_cc_dbg(STEPS, "Step 18\n");
+	if (dram_type == DRAM_TYPE_LPDDR2) {
+		ccfifo_writel(emc, next_timing->emc_mrw2, EMC_MRW2, 0);
+		ccfifo_writel(emc, next_timing->emc_mrw,  EMC_MRW,  0);
+		if (is_lpddr3)
+			ccfifo_writel(emc, next_timing->emc_mrw4, EMC_MRW4, 0);
+	} else if (dram_type == DRAM_TYPE_DDR3) {
+		if (opt_dll_mode == DLL_ON)
+			ccfifo_writel(emc, next_timing->emc_emrs &
+				      ~EMC_EMRS_USE_EMRS_LONG_CNT, EMC_EMRS, 0);
+		ccfifo_writel(emc, next_timing->emc_emrs2 &
+			      ~EMC_EMRS2_USE_EMRS2_LONG_CNT, EMC_EMRS2, 0);
+		ccfifo_writel(emc, next_timing->emc_mrs |
+			      EMC_EMRS_USE_EMRS_LONG_CNT, EMC_MRS, 0);
+	}
+
+	/* Step 19:
+	 *   ZQCAL for LPDDR3/DDR3
+	 */
+	emc_cc_dbg(STEPS, "Step 19\n");
+	if (opt_zcal_en_cc) {
+		if (dram_type == DRAM_TYPE_LPDDR2) {
+			u32 r;
+
+			zq_op = opt_cc_short_zcal  ? 0x56 : 0xAB;
+			zcal_wait_time_ps = opt_cc_short_zcal  ? 90000 : 360000;
+			zcal_wait_time_clocks = div_o3(zcal_wait_time_ps,
+						    destination_clock_period);
+			r = zcal_wait_time_clocks <<
+				EMC_MRS_WAIT_CNT2_MRS_EXT2_WAIT_CNT_SHIFT |
+				zcal_wait_time_clocks <<
+				EMC_MRS_WAIT_CNT2_MRS_EXT1_WAIT_CNT_SHIFT;
+			ccfifo_writel(emc, r, EMC_MRS_WAIT_CNT2, 0);
+			ccfifo_writel(emc, 2 << EMC_MRW_MRW_DEV_SELECTN_SHIFT |
+				      EMC_MRW_USE_MRW_EXT_CNT |
+				      10 << EMC_MRW_MRW_MA_SHIFT |
+				      zq_op << EMC_MRW_MRW_OP_SHIFT,
+				      EMC_MRW, 0);
+			if (emc->dram_dev_num == TWO_RANK) {
+				r = 1 << EMC_MRW_MRW_DEV_SELECTN_SHIFT |
+					EMC_MRW_USE_MRW_EXT_CNT |
+					10 << EMC_MRW_MRW_MA_SHIFT |
+					zq_op << EMC_MRW_MRW_OP_SHIFT;
+				ccfifo_writel(emc, r, EMC_MRW, 0);
+			}
+		} else if (dram_type == DRAM_TYPE_DDR3) {
+			zq_op = opt_cc_short_zcal ? 0 : EMC_ZQ_CAL_LONG;
+			ccfifo_writel(emc, zq_op | 2 << EMC_ZQ_CAL_DEV_SEL_SHIFT |
+				      EMC_ZQ_CAL_ZQ_CAL_CMD, EMC_ZQ_CAL, 0);
+			if (emc->dram_dev_num == TWO_RANK)
+				ccfifo_writel(emc, zq_op |
+					      1 << EMC_ZQ_CAL_DEV_SEL_SHIFT |
+					      EMC_ZQ_CAL_ZQ_CAL_CMD,
+					      EMC_ZQ_CAL, 0);
+		}
+	}
+
+	if (bg_regulator_mode_change) {
+		emc_set_shadow_bypass(emc, ACTIVE);
+		bg_regulator_switch_complete_wait_clks =
+			ramp_up_wait > 1250000 ? 0 :
+			(1250000 - ramp_up_wait) / destination_clock_period;
+		ccfifo_writel(emc, next_timing->burst_regs
+			      [EMC_PMACRO_BG_BIAS_CTRL_0_INDEX],
+			      EMC_PMACRO_BG_BIAS_CTRL_0,
+			      bg_regulator_switch_complete_wait_clks);
+		emc_set_shadow_bypass(emc, ASSEMBLY);
+	}
+
+	/* Step 20:
+	 *   Issue ref and optional QRST.
+	 */
+	emc_cc_dbg(STEPS, "Step 20\n");
+	if (dram_type != DRAM_TYPE_LPDDR4)
+		ccfifo_writel(emc, 0, EMC_REF, 0);
+
+	if (opt_do_sw_qrst) {
+		ccfifo_writel(emc, 1, EMC_ISSUE_QRST, 0);
+		ccfifo_writel(emc, 0, EMC_ISSUE_QRST, 2);
+	}
+
+	/* Step 21:
+	 *   Restore ZCAL and ZCAL interval.
+	 */
+	emc_cc_dbg(STEPS, "Step 21\n");
+	if (save_restore_clkstop_pd || opt_zcal_en_cc) {
+		ccfifo_writel(emc, emc_dbg_o | EMC_DBG_WRITE_MUX_ACTIVE, EMC_DBG, 0);
+		if (opt_zcal_en_cc && dram_type != DRAM_TYPE_LPDDR4)
+			ccfifo_writel(emc, next_timing->
+				      burst_regs[EMC_ZCAL_INTERVAL_INDEX],
+				      EMC_ZCAL_INTERVAL, 0);
+
+		if (save_restore_clkstop_pd)
+			ccfifo_writel(emc, next_timing->burst_regs[EMC_CFG_INDEX] &
+				      ~EMC_CFG_DYN_SELF_REF, EMC_CFG, 0);
+		ccfifo_writel(emc, emc_dbg_o, EMC_DBG, 0);
+	}
+
+	/* Step 22:
+	 *   Restore EMC_CFG_PIPE_CLK.
+	 */
+	emc_cc_dbg(STEPS, "Step 22\n");
+	ccfifo_writel(emc, emc_cfg_pipe_clk_o, EMC_CFG_PIPE_CLK, 0);
+
+	if (bg_regulator_mode_change) {
+		if (enable_bg_regulator)
+			emc_writel(emc, next_timing->burst_regs
+				   [EMC_PMACRO_BG_BIAS_CTRL_0_INDEX] &
+				   ~EMC_PMACRO_BG_BIAS_CTRL_0_BGLP_E_PWRD,
+				   EMC_PMACRO_BG_BIAS_CTRL_0);
+		else
+			emc_writel(emc, next_timing->burst_regs
+				   [EMC_PMACRO_BG_BIAS_CTRL_0_INDEX] &
+				   ~EMC_PMACRO_BG_BIAS_CTRL_0_BG_E_PWRD,
+				   EMC_PMACRO_BG_BIAS_CTRL_0);
+	}
+
+	/* Step 23:
+	 */
+	emc_cc_dbg(STEPS, "Step 23\n");
+
+	/* Fix: rename tmp to something meaningful. */
+	tmp = emc_readl(emc, EMC_CFG_DIG_DLL);
+	tmp |= EMC_CFG_DIG_DLL_CFG_DLL_STALL_ALL_TRAFFIC;
+	tmp &= ~EMC_CFG_DIG_DLL_CFG_DLL_STALL_RW_UNTIL_LOCK;
+	tmp &= ~EMC_CFG_DIG_DLL_CFG_DLL_STALL_ALL_UNTIL_LOCK;
+	tmp &= ~EMC_CFG_DIG_DLL_CFG_DLL_EN;
+	tmp = (tmp & ~EMC_CFG_DIG_DLL_CFG_DLL_MODE_MASK) |
+		(2 << EMC_CFG_DIG_DLL_CFG_DLL_MODE_SHIFT);
+	emc_writel(emc, tmp, EMC_CFG_DIG_DLL);
+
+	do_clock_change(emc);
+	
+	/* Step 24:
+	 *   Save training results. Removed.
+	 */
+
+	/* Step 25:
+	 *   Program MC updown registers.
+	 */
+	emc_cc_dbg(STEPS, "Step 25\n");
+
+	if (next_timing->rate > last_timing->rate)
+		tegra_mc_write_scaled_la_configuration(emc->mc,
+						       next_timing->rate);
+
+	/* Step 26:
+	 *   Restore ZCAL registers.
+	 */
+	emc_cc_dbg(STEPS, "Step 26\n");
+	if (dram_type == DRAM_TYPE_LPDDR4) {
+		emc_set_shadow_bypass(emc, ACTIVE);
+		emc_writel(emc, next_timing->burst_regs[EMC_ZCAL_WAIT_CNT_INDEX],
+			   EMC_ZCAL_WAIT_CNT);
+		emc_writel(emc, next_timing->burst_regs[EMC_ZCAL_INTERVAL_INDEX],
+			   EMC_ZCAL_INTERVAL);
+		emc_set_shadow_bypass(emc, ASSEMBLY);
+	}
+
+	if (dram_type != DRAM_TYPE_LPDDR4 &&
+	    opt_zcal_en_cc && !opt_short_zcal && opt_cc_short_zcal) {
+		udelay(2);
+
+		emc_set_shadow_bypass(emc, ACTIVE);
+		if (dram_type == DRAM_TYPE_LPDDR2)
+			emc_writel(emc, next_timing->
+				  burst_regs[EMC_MRS_WAIT_CNT_INDEX],
+				  EMC_MRS_WAIT_CNT);
+		else if (dram_type == DRAM_TYPE_DDR3)
+			emc_writel(emc, next_timing->
+				   burst_regs[EMC_ZCAL_WAIT_CNT_INDEX],
+				   EMC_ZCAL_WAIT_CNT);
+		emc_set_shadow_bypass(emc, ASSEMBLY);
+	}
+
+	/* Step 27:
+	 *   Restore EMC_CFG, FDPD registers.
+	 */
+	emc_cc_dbg(STEPS, "Step 27\n");
+	emc_set_shadow_bypass(emc, ACTIVE);
+	emc_writel(emc, next_timing->burst_regs[EMC_CFG_INDEX], EMC_CFG);
+	emc_set_shadow_bypass(emc, ASSEMBLY);
+	emc_writel(emc, next_timing->emc_fdpd_ctrl_cmd_no_ramp,
+		   EMC_FDPD_CTRL_CMD_NO_RAMP);
+	emc_writel(emc, next_timing->emc_sel_dpd_ctrl, EMC_SEL_DPD_CTRL);
+
+	/* Step 28:
+	 *   Training recover. Removed.
+	 */
+	emc_cc_dbg(STEPS, "Step 28\n");
+
+	emc_set_shadow_bypass(emc, ACTIVE);
+	emc_writel(emc, next_timing->burst_regs[EMC_PMACRO_AUTOCAL_CFG_COMMON_INDEX],
+		   EMC_PMACRO_AUTOCAL_CFG_COMMON);
+	emc_set_shadow_bypass(emc, ASSEMBLY);
+
+	/* Step 29:
+	 *   Power fix WAR.
+	 */
+	emc_cc_dbg(STEPS, "Step 29\n");
+	emc_writel(emc, EMC_PMACRO_CFG_PM_GLOBAL_0_DISABLE_CFG_BYTE0 |
+		   EMC_PMACRO_CFG_PM_GLOBAL_0_DISABLE_CFG_BYTE1 |
+		   EMC_PMACRO_CFG_PM_GLOBAL_0_DISABLE_CFG_BYTE2 |
+		   EMC_PMACRO_CFG_PM_GLOBAL_0_DISABLE_CFG_BYTE3 |
+		   EMC_PMACRO_CFG_PM_GLOBAL_0_DISABLE_CFG_BYTE4 |
+		   EMC_PMACRO_CFG_PM_GLOBAL_0_DISABLE_CFG_BYTE5 |
+		   EMC_PMACRO_CFG_PM_GLOBAL_0_DISABLE_CFG_BYTE6 |
+		   EMC_PMACRO_CFG_PM_GLOBAL_0_DISABLE_CFG_BYTE7,
+		   EMC_PMACRO_CFG_PM_GLOBAL_0);
+	emc_writel(emc, EMC_PMACRO_TRAINING_CTRL_0_CH0_TRAINING_E_WRPTR,
+		   EMC_PMACRO_TRAINING_CTRL_0);
+	emc_writel(emc, EMC_PMACRO_TRAINING_CTRL_1_CH1_TRAINING_E_WRPTR,
+		   EMC_PMACRO_TRAINING_CTRL_1);
+	emc_writel(emc, 0, EMC_PMACRO_CFG_PM_GLOBAL_0);
+
+	/* Step 30:
+	 *   Re-enable autocal.
+	 */
+	emc_cc_dbg(STEPS, "Step 30: Re-enable DLL and AUTOCAL\n");
+	if (next_timing->burst_regs[EMC_CFG_DIG_DLL_INDEX] &
+	    EMC_CFG_DIG_DLL_CFG_DLL_EN) {
+		tmp = emc_readl(emc, EMC_CFG_DIG_DLL);
+		tmp |=  EMC_CFG_DIG_DLL_CFG_DLL_STALL_ALL_TRAFFIC;
+		tmp |=  EMC_CFG_DIG_DLL_CFG_DLL_EN;
+		tmp &= ~EMC_CFG_DIG_DLL_CFG_DLL_STALL_RW_UNTIL_LOCK;
+		tmp &= ~EMC_CFG_DIG_DLL_CFG_DLL_STALL_ALL_UNTIL_LOCK;
+		tmp =  (tmp & ~EMC_CFG_DIG_DLL_CFG_DLL_MODE_MASK) |
+			(2 << EMC_CFG_DIG_DLL_CFG_DLL_MODE_SHIFT);
+		emc_writel(emc, tmp, EMC_CFG_DIG_DLL);
+		emc_timing_update(emc, channel_mode);
+	}
+
+	emc_auto_cal_config = next_timing->emc_auto_cal_config;
+	emc_writel(emc, emc_auto_cal_config, EMC_AUTO_CAL_CONFIG);
+
+	/* Step 31:
+	 *   Restore FSP to account for switch back. Only needed in training.
+	 */
+	emc_cc_dbg(STEPS, "Step 31\n");
+
+	/* Step 32:
+	 *   [SW] Update the alternative timing (derated vs normal) table with
+	 * the periodic training values computed during the clock change
+	 * pre-amble.
+	 */
+	emc_cc_dbg(STEPS, "Step 32: Update alt timing\n");
+	tegra210_update_emc_alt_timing(emc, next_timing);
+
+	/* Done! Yay. */
+}
-- 
1.9.1

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

* [RFC 11/14] memory: tegra: parse DT and costruct timing tables
  2018-09-14 20:02 [RFC 00/14] Tegra210 EMC scaling Peter De Schrijver
                   ` (9 preceding siblings ...)
  2018-09-14 20:03 ` [RFC 10/14] memory: tegra: Add Tegra210 EMC scaling sequence Peter De Schrijver
@ 2018-09-14 20:03 ` Peter De Schrijver
  2018-09-14 20:03 ` [RFC 12/14] memory: tegra: Tegra210 EMC memory driver Peter De Schrijver
                   ` (2 subsequent siblings)
  13 siblings, 0 replies; 26+ messages in thread
From: Peter De Schrijver @ 2018-09-14 20:03 UTC (permalink / raw)
  To: vkuruturi, linux-tegra=linux-clk, linux-tegra, thierry.reding,
	jonathanh, devicetree, robh+dt
  Cc: daniel, a.heider, swtcr0, Peter De Schrijver

Signed-off-by: Peter De Schrijver <pdeschrijver@nvidia.com>
---
 drivers/memory/tegra/tegra210-dt-parse.c | 363 +++++++++++++++++++++++++++++++
 1 file changed, 363 insertions(+)
 create mode 100644 drivers/memory/tegra/tegra210-dt-parse.c

diff --git a/drivers/memory/tegra/tegra210-dt-parse.c b/drivers/memory/tegra/tegra210-dt-parse.c
new file mode 100644
index 0000000..553663a
--- /dev/null
+++ b/drivers/memory/tegra/tegra210-dt-parse.c
@@ -0,0 +1,363 @@
+/*
+ * drivers/platform/tegra/mc/tegra_emc_dt_parse.c
+ *
+ * Copyright (c) 2013-2017, NVIDIA CORPORATION.  All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/err.h>
+#include <linux/of.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <soc/tegra/fuse.h>
+//#include <soc/tegra/tegra_emc.h>
+
+#include "tegra210-emc-reg.h"
+
+static struct device_node *tegra_emc_ramcode_devnode(
+	struct device_node *np)
+{
+	struct device_node *iter;
+	u32 reg;
+
+	for_each_child_of_node(np, iter) {
+		if (of_property_read_u32(iter, "nvidia,ram-code", &reg))
+			continue;
+		if (reg == tegra_read_ram_code())
+			return of_node_get(iter);
+	}
+
+	return NULL;
+}
+
+static void *tegra_emc_dt_parse_pdata_comp(const char *emc_mode,
+				    const char *comp,
+				    void *pdata,
+				    struct device_node *tnp,
+				    struct platform_device *pdev,
+				    int num_tables, int *table_count)
+{
+#define PNE_U32(node, entry, tbl_entry)					\
+	do {								\
+		int __ret__;						\
+		u32 __tmp__;						\
+									\
+		__ret__ = of_property_read_u32((node), (entry), &__tmp__); \
+		if (__ret__) {						\
+			dev_err(&pdev->dev, "Failed to parse %s in %s: %d\n", \
+				(entry), (node)->full_name, __ret__);	\
+			continue;					\
+		}							\
+									\
+		tables[i].tbl_entry = __tmp__;				\
+	} while (0)
+
+#define PNE_U32_ARRAY(node, entry, tbl_entry, length)			\
+	do {								\
+		int __ret__;						\
+									\
+		__ret__ = of_property_read_u32_array((node), (entry),	\
+						     (tbl_entry), (length)); \
+		if (__ret__) {						\
+			dev_err(&pdev->dev, "Failed to parse %s in %s: %d\n", \
+				(entry), (node)->full_name, __ret__);	\
+			continue;					\
+		}							\
+	} while (0)
+
+	int i = 0, ret = 0;
+	struct device_node *iter;
+	struct emc_timing *tables;
+
+	tables = devm_kzalloc(&pdev->dev,
+			sizeof(*tables) * num_tables, GFP_KERNEL);
+
+	if (!tables) {
+		of_node_put(tnp);
+		return tables;
+	}
+
+	for_each_child_of_node(tnp, iter) {
+		if (of_device_is_compatible(iter, comp)) {
+			const char *source_name;
+			const char *dvfs_ver;
+
+			ret = of_property_read_string(iter,
+					"nvidia,source", &source_name);
+			if (ret) {
+				dev_err(&pdev->dev, "no source name in %s\n",
+						iter->full_name);
+				continue;
+			}
+			strlcpy(tables[i].clock_src, source_name,
+					sizeof(tables[i].clock_src));
+
+			ret = of_property_read_string(iter,
+					"nvidia,dvfs-version", &dvfs_ver);
+			if (ret) {
+				dev_err(&pdev->dev, "no dvfs version in %s\n",
+						iter->full_name);
+				continue;
+			}
+			strlcpy(tables[i].dvfs_ver, dvfs_ver,
+					sizeof(tables[i].dvfs_ver));
+
+			PNE_U32(iter, "nvidia,revision", rev);
+			PNE_U32(iter, "clock-frequency", rate);
+			PNE_U32(iter, "nvidia,emc-min-mv", min_volt);
+			PNE_U32(iter, "nvidia,gk20a-min-mv", gpu_min_volt);
+			PNE_U32(iter, "nvidia,src-sel-reg", clk_src_emc);
+			PNE_U32(iter, "nvidia,burst-regs-num", num_burst);
+			PNE_U32(iter, "nvidia,emc-cfg-2", emc_cfg_2);
+			PNE_U32(iter, "nvidia,emc-sel-dpd-ctrl",
+				emc_sel_dpd_ctrl);
+			PNE_U32(iter, "nvidia,emc-auto-cal-config",
+				emc_auto_cal_config);
+			PNE_U32(iter, "nvidia,emc-auto-cal-config2",
+				emc_auto_cal_config2);
+			PNE_U32(iter, "nvidia,emc-auto-cal-config3",
+				emc_auto_cal_config3);
+			PNE_U32(iter, "nvidia,emc-clock-latency-change",
+				latency);
+			PNE_U32_ARRAY(iter, "nvidia,emc-registers",
+				      tables[i].burst_regs,
+				      tables[i].num_burst);
+
+			PNE_U32(iter, "nvidia,needs-training", needs_training);
+			PNE_U32(iter, "nvidia,trained", trained);
+			if (tables[i].rev < 0x6)
+				goto skip_periodic_training_params;
+			PNE_U32(iter, "nvidia,periodic_training",
+				periodic_training);
+			PNE_U32(iter, "nvidia,trained_dram_clktree_c0d0u0",
+				trained_dram_clktree_c0d0u0);
+			PNE_U32(iter, "nvidia,trained_dram_clktree_c0d0u1",
+				trained_dram_clktree_c0d0u1);
+			PNE_U32(iter, "nvidia,trained_dram_clktree_c0d1u0",
+				trained_dram_clktree_c0d1u0);
+			PNE_U32(iter, "nvidia,trained_dram_clktree_c0d1u1",
+				trained_dram_clktree_c0d1u1);
+			PNE_U32(iter, "nvidia,trained_dram_clktree_c1d0u0",
+				trained_dram_clktree_c1d0u0);
+			PNE_U32(iter, "nvidia,trained_dram_clktree_c1d0u1",
+				trained_dram_clktree_c1d0u1);
+			PNE_U32(iter, "nvidia,trained_dram_clktree_c1d1u0",
+				trained_dram_clktree_c1d1u0);
+			PNE_U32(iter, "nvidia,trained_dram_clktree_c1d1u1",
+				trained_dram_clktree_c1d1u1);
+			PNE_U32(iter, "nvidia,current_dram_clktree_c0d0u0",
+				current_dram_clktree_c0d0u0);
+			PNE_U32(iter, "nvidia,current_dram_clktree_c0d0u1",
+				current_dram_clktree_c0d0u1);
+			PNE_U32(iter, "nvidia,current_dram_clktree_c0d1u0",
+				current_dram_clktree_c0d1u0);
+			PNE_U32(iter, "nvidia,current_dram_clktree_c0d1u1",
+				current_dram_clktree_c0d1u1);
+			PNE_U32(iter, "nvidia,current_dram_clktree_c1d0u0",
+				current_dram_clktree_c1d0u0);
+			PNE_U32(iter, "nvidia,current_dram_clktree_c1d0u1",
+				current_dram_clktree_c1d0u1);
+			PNE_U32(iter, "nvidia,current_dram_clktree_c1d1u0",
+				current_dram_clktree_c1d1u0);
+			PNE_U32(iter, "nvidia,current_dram_clktree_c1d1u1",
+				current_dram_clktree_c1d1u1);
+			PNE_U32(iter, "nvidia,run_clocks", run_clocks);
+			PNE_U32(iter, "nvidia,tree_margin", tree_margin);
+
+skip_periodic_training_params:
+			PNE_U32(iter, "nvidia,burst-regs-per-ch-num",
+				num_burst_per_ch);
+			PNE_U32(iter, "nvidia,trim-regs-num", num_trim);
+			PNE_U32(iter, "nvidia,trim-regs-per-ch-num",
+				num_trim_per_ch);
+			PNE_U32(iter, "nvidia,burst-mc-regs-num",
+				num_mc_regs);
+			PNE_U32(iter, "nvidia,la-scale-regs-num",
+				num_up_down);
+			PNE_U32(iter, "nvidia,vref-regs-num", vref_num);
+			PNE_U32(iter, "nvidia,dram-timing-regs-num",
+				dram_timing_num);
+			PNE_U32(iter, "nvidia,min-mrs-wait", min_mrs_wait);
+			PNE_U32(iter, "nvidia,emc-mrw", emc_mrw);
+			PNE_U32(iter, "nvidia,emc-mrw2", emc_mrw2);
+			PNE_U32(iter, "nvidia,emc-mrw3", emc_mrw3);
+			PNE_U32(iter, "nvidia,emc-mrw4", emc_mrw4);
+			PNE_U32(iter, "nvidia,emc-mrw9", emc_mrw9);
+			PNE_U32(iter, "nvidia,emc-mrs", emc_mrs);
+			PNE_U32(iter, "nvidia,emc-emrs", emc_emrs);
+			PNE_U32(iter, "nvidia,emc-emrs2", emc_emrs2);
+			PNE_U32(iter, "nvidia,emc-auto-cal-config4",
+				emc_auto_cal_config4);
+			PNE_U32(iter, "nvidia,emc-auto-cal-config5",
+				emc_auto_cal_config5);
+			PNE_U32(iter, "nvidia,emc-auto-cal-config6",
+				emc_auto_cal_config6);
+			PNE_U32(iter, "nvidia,emc-auto-cal-config7",
+				emc_auto_cal_config7);
+			PNE_U32(iter, "nvidia,emc-auto-cal-config8",
+				emc_auto_cal_config8);
+			PNE_U32(iter, "nvidia,emc-fdpd-ctrl-cmd-no-ramp",
+				emc_fdpd_ctrl_cmd_no_ramp);
+			PNE_U32(iter, "nvidia,dll-clk-src", dll_clk_src);
+			PNE_U32(iter, "nvidia,clk-out-enb-x-0-clk-enb-emc-dll",
+				clk_out_enb_x_0_clk_enb_emc_dll);
+
+			if (tables[i].rev >= 0x7)
+				PNE_U32_ARRAY(iter, "nvidia,ptfv",
+					      tables[i].ptfv_list,
+					      sizeof(tables[i].ptfv_list)
+							/ sizeof(u32));
+
+			PNE_U32_ARRAY(iter, "nvidia,emc-burst-regs-per-ch",
+				      tables[i].burst_reg_per_ch,
+				      tables[i].num_burst_per_ch);
+			PNE_U32_ARRAY(iter, "nvidia,emc-shadow-regs-ca-train",
+				      tables[i].shadow_regs_ca_train,
+				      tables[i].num_burst);
+			PNE_U32_ARRAY(iter, "nvidia,emc-shadow-regs-quse-train",
+				      tables[i].shadow_regs_quse_train,
+				      tables[i].num_burst);
+			PNE_U32_ARRAY(iter, "nvidia,emc-shadow-regs-rdwr-train",
+				      tables[i].shadow_regs_rdwr_train,
+				      tables[i].num_burst);
+			PNE_U32_ARRAY(iter, "nvidia,emc-trim-regs",
+				      tables[i].trim_regs,
+				      tables[i].num_trim);
+			PNE_U32_ARRAY(iter, "nvidia,emc-trim-regs-per-ch",
+				      tables[i].trim_perch_regs,
+				      tables[i].num_trim_per_ch);
+			PNE_U32_ARRAY(iter, "nvidia,emc-vref-regs",
+				      tables[i].vref_perch_regs,
+				      tables[i].vref_num);
+			PNE_U32_ARRAY(iter, "nvidia,emc-dram-timing-regs",
+				      tables[i].dram_timings,
+				      tables[i].dram_timing_num);
+			PNE_U32_ARRAY(iter, "nvidia,emc-burst-mc-regs",
+				      tables[i].burst_mc_regs,
+				      tables[i].num_mc_regs);
+			PNE_U32_ARRAY(iter, "nvidia,emc-la-scale-regs",
+				      tables[i].la_scale_regs,
+				      tables[i].num_up_down);
+			i++;
+		}
+	}
+	*table_count = i;
+	return tables;
+}
+
+static const struct of_device_id emc_table_match[] = {
+	{
+		.compatible = "nvidia,tegra210-emc-table",
+		.data = "nvidia,tegra210-emc-table-derated",
+	},
+	{
+		.compatible = "nvidia,tegra21-emc-table",
+		.data = "nvidia,tegra21-emc-table-derated",
+	},
+	{ },
+};
+
+int tegra_emc_dt_parse_pdata(struct platform_device *pdev,
+			       struct emc_timing **tables,
+			       struct emc_timing **derated_tables,
+			       int *num_entries)
+{
+	struct device_node *np = pdev->dev.of_node;
+	struct device_node *tnp, *iter;
+	int num_tables, table_count;
+	u32 tegra_bct_strapping;
+	const char *emc_mode = "nvidia,emc-mode-0";
+	struct tegra21_emc_pdata *pdata = NULL;
+	const char *comp = NULL;
+	const char *comp_derated = NULL;
+	const void *prop;
+
+	if (!np) {
+		dev_err(&pdev->dev,
+			"Unable to find external-memory-controller node\n");
+		return -ENODEV;
+	}
+
+	tegra_bct_strapping = tegra_read_ram_code();
+
+	prop = of_get_property(pdev->dev.of_node,
+			       "nvidia,poll_thresh_freq", NULL);
+	if (prop) {
+		unsigned long freq_thresh = (unsigned long)be32_to_cpup(prop);
+
+//		tegra210_emc_mr4_set_freq_thresh(freq_thresh);
+		pr_info("tegra: emc: Using MR4 freq threshold: %lu\n",
+			freq_thresh);
+	}
+
+	if (of_find_property(np, "nvidia,use-ram-code", NULL)) {
+		tnp = tegra_emc_ramcode_devnode(np);
+
+		if (!tnp) {
+			dev_warn(&pdev->dev,
+				"can't find emc table for ram-code 0x%02x\n",
+				tegra_bct_strapping);
+			return -ENODEV;
+		}
+	} else
+		tnp = of_node_get(np);
+
+	num_tables = 0;
+	for_each_child_of_node(tnp, iter) {
+		if (!comp) {
+			const struct of_device_id *m =
+				of_match_node(emc_table_match, iter);
+			if (m) {
+				comp = m->compatible;
+				comp_derated = m->data;
+				num_tables++;
+			}
+			continue;
+		}
+		if (of_device_is_compatible(iter, comp))
+			num_tables++;
+	}
+
+	if (!num_tables) {
+		*tables = NULL;
+		goto out;
+	}
+
+	*tables = tegra_emc_dt_parse_pdata_comp(emc_mode, comp, pdata, tnp,
+						pdev, num_tables, &table_count);
+	*num_entries = table_count;
+
+	/* populate the derated tables */
+	num_tables = 0;
+	for_each_child_of_node(tnp, iter) {
+		if (of_device_is_compatible(iter, comp_derated))
+			num_tables++;
+	}
+
+	if (!num_tables) {
+		*derated_tables = NULL;
+		goto out;
+	}
+
+	*derated_tables = tegra_emc_dt_parse_pdata_comp(emc_mode,
+				comp_derated, pdata, tnp, pdev, num_tables,
+				&table_count);
+
+out:
+	of_node_put(tnp);
+	return 0;
+}
-- 
1.9.1

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

* [RFC 12/14] memory: tegra: Tegra210 EMC memory driver
  2018-09-14 20:02 [RFC 00/14] Tegra210 EMC scaling Peter De Schrijver
                   ` (10 preceding siblings ...)
  2018-09-14 20:03 ` [RFC 11/14] memory: tegra: parse DT and costruct timing tables Peter De Schrijver
@ 2018-09-14 20:03 ` Peter De Schrijver
  2018-09-14 20:03 ` [RFC 13/14] memory: tegra: enable Tegra210 EMC scaling driver Peter De Schrijver
  2018-09-14 20:03 ` [RFC 14/14] dt-bindings: tegra: Add Tegra210 EMC binding Peter De Schrijver
  13 siblings, 0 replies; 26+ messages in thread
From: Peter De Schrijver @ 2018-09-14 20:03 UTC (permalink / raw)
  To: vkuruturi, linux-tegra=linux-clk, linux-tegra, thierry.reding,
	jonathanh, devicetree, robh+dt
  Cc: daniel, a.heider, swtcr0, Peter De Schrijver

Signed-off-by: Peter De Schrijver <pdeschrijver@nvidia.com>
---
 drivers/memory/tegra/Makefile       |    1 +
 drivers/memory/tegra/tegra210-emc.c | 2268 +++++++++++++++++++++++++++++++++++
 2 files changed, 2269 insertions(+)
 create mode 100644 drivers/memory/tegra/tegra210-emc.c

diff --git a/drivers/memory/tegra/Makefile b/drivers/memory/tegra/Makefile
index b44e862..8716e69 100644
--- a/drivers/memory/tegra/Makefile
+++ b/drivers/memory/tegra/Makefile
@@ -10,3 +10,4 @@ tegra-mc-$(CONFIG_ARCH_TEGRA_210_SOC) += tegra210.o
 obj-$(CONFIG_TEGRA_MC) += tegra-mc.o
 
 obj-$(CONFIG_TEGRA124_EMC) += tegra124-emc.o
+obj-$(CONFIG_TEGRA210_EMC) += tegra210-emc.o tegra210-emc-cc-r21021.o tegra210-dt-parse.o
diff --git a/drivers/memory/tegra/tegra210-emc.c b/drivers/memory/tegra/tegra210-emc.c
new file mode 100644
index 0000000..f9ce55c
--- /dev/null
+++ b/drivers/memory/tegra/tegra210-emc.c
@@ -0,0 +1,2268 @@
+/*
+ * Copyright (c) 2015-2017, NVIDIA CORPORATION.  All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ */
+
+#include <linux/clk.h>
+#include <linux/clk/tegra.h>
+#include <linux/debugfs.h>
+#include <linux/delay.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/of_address.h>
+#include <linux/of_platform.h>
+#include <linux/slab.h>
+#include <linux/thermal.h>
+
+#include <soc/tegra/fuse.h>
+#include <soc/tegra/mc.h>
+
+#include "tegra210-emc-reg.h"
+
+#define TEGRA_EMC_TABLE_MAX_SIZE		16
+#define EMC_STATUS_UPDATE_TIMEOUT		1000
+#define TEGRA210_SAVE_RESTORE_MOD_REGS		12
+#define TEGRA_EMC_DEFAULT_CLK_LATENCY_US	2000
+
+#define EMC0_EMC_CMD_BRLSHFT_0_INDEX	0
+#define EMC1_EMC_CMD_BRLSHFT_1_INDEX	1
+#define EMC0_EMC_DATA_BRLSHFT_0_INDEX	2
+#define EMC1_EMC_DATA_BRLSHFT_0_INDEX	3
+#define EMC0_EMC_DATA_BRLSHFT_1_INDEX	4
+#define EMC1_EMC_DATA_BRLSHFT_1_INDEX	5
+#define EMC0_EMC_QUSE_BRLSHFT_0_INDEX	6
+#define EMC1_EMC_QUSE_BRLSHFT_1_INDEX	7
+#define EMC0_EMC_QUSE_BRLSHFT_2_INDEX	8
+#define EMC1_EMC_QUSE_BRLSHFT_3_INDEX	9
+
+#define TRIM_REG(chan, rank, reg, byte)					\
+	((EMC_PMACRO_OB_DDLL_LONG_DQ_RANK ## rank ## _ ## reg ##	\
+	  _OB_DDLL_LONG_DQ_RANK ## rank ## _BYTE ## byte ## _MASK &	\
+	  next_timing->trim_regs[EMC_PMACRO_OB_DDLL_LONG_DQ_RANK ##	\
+				 rank ## _ ## reg ## _INDEX]) >>	\
+	 EMC_PMACRO_OB_DDLL_LONG_DQ_RANK ## rank ## _ ## reg ##		\
+	 _OB_DDLL_LONG_DQ_RANK ## rank ## _BYTE ## byte ## _SHIFT)	\
+	+								\
+	(((EMC_DATA_BRLSHFT_ ## rank ## _RANK ## rank ## _BYTE ##	\
+	   byte ## _DATA_BRLSHFT_MASK &					\
+	   next_timing->trim_perch_regs[EMC ## chan ##			\
+			      _EMC_DATA_BRLSHFT_ ## rank ## _INDEX]) >>	\
+	  EMC_DATA_BRLSHFT_ ## rank ## _RANK ## rank ## _BYTE ##	\
+	  byte ## _DATA_BRLSHFT_SHIFT) * 64)
+
+#define CALC_TEMP(rank, reg, byte1, byte2, n)				\
+	((new[n] << EMC_PMACRO_OB_DDLL_LONG_DQ_RANK ## rank ## _ ##	\
+	  reg ## _OB_DDLL_LONG_DQ_RANK ## rank ## _BYTE ## byte1 ## _SHIFT) & \
+	 EMC_PMACRO_OB_DDLL_LONG_DQ_RANK ## rank ## _ ## reg ##		\
+	 _OB_DDLL_LONG_DQ_RANK ## rank ## _BYTE ## byte1 ## _MASK)	\
+	|								\
+	((new[n + 1] << EMC_PMACRO_OB_DDLL_LONG_DQ_RANK ## rank ## _ ##	\
+	  reg ## _OB_DDLL_LONG_DQ_RANK ## rank ## _BYTE ## byte2 ## _SHIFT) & \
+	 EMC_PMACRO_OB_DDLL_LONG_DQ_RANK ## rank ## _ ## reg ##		\
+	 _OB_DDLL_LONG_DQ_RANK ## rank ## _BYTE ## byte2 ## _MASK)	\
+
+static bool emc_enable = true;
+module_param(emc_enable, bool, 0444);
+static bool emc_force_max_rate;
+module_param(emc_force_max_rate, bool, 0444);
+
+struct emc_sel {
+	struct clk *input;
+	u32 value;
+	unsigned long input_rate;
+
+	struct clk *input_b;
+	u32 value_b;
+	unsigned long input_rate_b;
+};
+
+enum TEGRA_EMC_SOURCE {
+	TEGRA_EMC_SRC_PLLM,
+	TEGRA_EMC_SRC_PLLC,
+	TEGRA_EMC_SRC_PLLP,
+	TEGRA_EMC_SRC_CLKM,
+	TEGRA_EMC_SRC_PLLM_UD,
+	TEGRA_EMC_SRC_PLLMB_UD,
+	TEGRA_EMC_SRC_PLLMB,
+	TEGRA_EMC_SRC_PLLP_UD,
+	TEGRA_EMC_SRC_COUNT,
+};
+
+enum {
+	TEGRA_DRAM_OVER_TEMP_NONE = 0,
+	TEGRA_DRAM_OVER_TEMP_REFRESH_X2,
+	TEGRA_DRAM_OVER_TEMP_REFRESH_X4,
+	TEGRA_DRAM_OVER_TEMP_THROTTLE, /* 4x Refresh + derating. */
+	TEGRA_DRAM_OVER_TEMP_MAX,
+};
+
+#define DEFINE_REG(type, reg) (reg)
+u32 burst_regs_per_ch_off[] = BURST_REGS_PER_CH_LIST;
+u32 burst_regs_off[] = BURST_REGS_LIST;
+u32 trim_regs_per_ch_off[] = TRIM_REGS_PER_CH_LIST;
+u32 trim_regs_off[] = TRIM_REGS_LIST;
+u32 vref_regs_per_ch_off[] = VREF_REGS_PER_CH_LIST;
+#undef DEFINE_REG
+
+#define DEFINE_REG(type, reg) (type)
+u32 burst_regs_per_ch_type[] = BURST_REGS_PER_CH_LIST;
+u32 trim_regs_per_ch_type[] = TRIM_REGS_PER_CH_LIST;
+u32 vref_regs_per_ch_type[] = VREF_REGS_PER_CH_LIST;
+#undef DEFINE_REG
+
+static DEFINE_SPINLOCK(emc_access_lock);
+static ktime_t clkchange_time;
+static int clkchange_delay = 100;
+static int last_rate_idx;
+static u32 current_clksrc;
+static u32 timer_period_mr4 = 1000;
+static u32 timer_period_training = 100;
+static bool tegra_emc_init_done;
+static unsigned long emc_max_rate;
+static const char *emc_parent_names[] = { 
+	[TEGRA_EMC_SRC_PLLM] =  "pll_m",
+	[TEGRA_EMC_SRC_PLLC] =  "pll_c",
+	[TEGRA_EMC_SRC_PLLP] =  "pll_p",
+	[TEGRA_EMC_SRC_CLKM] =  "clk_m",
+	[TEGRA_EMC_SRC_PLLM_UD] = "pll_m_ud",
+	[TEGRA_EMC_SRC_PLLMB_UD] = "pll_mb_ud",
+	[TEGRA_EMC_SRC_PLLMB] = "pll_mb",
+	[TEGRA_EMC_SRC_PLLP_UD] = "pll_p_ud",
+};
+static struct clk_bulk_data emc_clks[] = {
+	[TEGRA_EMC_SRC_PLLM] = { .id = "pll_m", },
+	[TEGRA_EMC_SRC_PLLC] = { .id = "pll_c", },
+	[TEGRA_EMC_SRC_PLLP] = { .id = "pll_p", },
+	[TEGRA_EMC_SRC_CLKM] = { .id = "clk_m", },
+	[TEGRA_EMC_SRC_PLLM_UD] = { .id = "pll_m_ud", },
+	[TEGRA_EMC_SRC_PLLMB_UD] = { .id = "pll_mb_ud", },
+	[TEGRA_EMC_SRC_PLLMB] = { .id = "pll_mb", },
+	[TEGRA_EMC_SRC_PLLP_UD] = { .id = "pll_p_ud", },
+};
+#ifdef CONFIG_PM_SLEEP
+static unsigned long emc_override_rate;
+#endif
+unsigned long dram_over_temp_state = TEGRA_DRAM_OVER_TEMP_NONE;
+static struct emc_stats tegra_emc_stats;
+static struct supported_sequence supported_seqs[] = {
+	{
+		0x7,
+		emc_set_clock_r21021,
+		__do_periodic_emc_compensation_r21021,
+	},
+	{
+		0,
+		NULL,
+		NULL,
+		NULL
+	}
+};
+static struct supported_sequence *seq;
+
+static int emc_get_dram_temperature(struct tegra_emc *emc);
+
+/* MR4 parameters */
+static u32 prev_temp = 0xffffffff; /* i.e -1. */
+static u32 test_mode;
+static int dram_temp_override;
+static unsigned long mr4_freq_threshold;
+static atomic_t mr4_temp_poll;
+static atomic_t mr4_force_poll;
+
+static void emc_mr4_poll(unsigned long nothing);
+static struct timer_list emc_timer_mr4 =
+	TIMER_DEFERRED_INITIALIZER(emc_mr4_poll, 0, 0);
+
+static void emc_train(unsigned long nothing);
+static struct timer_list emc_timer_training =
+	TIMER_INITIALIZER(emc_train, 0, 0);
+
+static u8 tegra210_emc_bw_efficiency = 80;
+static u8 tegra210_emc_iso_share = 100;
+static unsigned long last_iso_bw;
+
+static u32 bw_calc_freqs[] = {
+	5, 10, 20, 30, 40, 60, 80, 100, 120, 140, 160, 180,
+	200, 250, 300, 350, 400, 450, 500, 550, 600, 650, 700
+};
+
+static u32 tegra210_lpddr3_iso_efficiency_os_idle[] = {
+	64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
+	64, 63, 60, 54, 45, 45, 45, 45, 45, 45, 45
+};
+static u32 tegra210_lpddr3_iso_efficiency_general[] = {
+	60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60,
+	60, 59, 59, 58, 57, 56, 55, 54, 54, 54, 54
+};
+
+static u32 tegra210_lpddr4_iso_efficiency_os_idle[] = {
+	56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56,
+	56, 56, 56, 56, 56, 56, 56, 56, 56, 49, 45
+};
+static u32 tegra210_lpddr4_iso_efficiency_general[] = {
+	56, 55, 55, 54, 54, 53, 51, 50, 49, 48, 47, 46,
+	45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45
+};
+
+static u32 tegra210_ddr3_iso_efficiency_os_idle[] = {
+	65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65,
+	65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65
+};
+static u32 tegra210_ddr3_iso_efficiency_general[] = {
+	60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60,
+	60, 59, 59, 58, 57, 56, 55, 54, 54, 54, 54
+};
+
+#if 0
+static u8 iso_share_calc_tegra210_os_idle(unsigned long iso_bw);
+static u8 iso_share_calc_tegra210_general(unsigned long iso_bw);
+
+static struct emc_iso_usage tegra210_emc_iso_usage[] = {
+	{
+		BIT(EMC_USER_DC1),
+		80, iso_share_calc_tegra210_os_idle
+	},
+	{
+		BIT(EMC_USER_DC2),
+		80, iso_share_calc_tegra210_os_idle
+	},
+	{
+		BIT(EMC_USER_DC1) | BIT(EMC_USER_DC2),
+		50, iso_share_calc_tegra210_general
+	},
+	{
+		BIT(EMC_USER_DC1) | BIT(EMC_USER_VI),
+		50, iso_share_calc_tegra210_general
+	},
+	{
+		BIT(EMC_USER_DC1) | BIT(EMC_USER_DC2) | BIT(EMC_USER_VI),
+		50, iso_share_calc_tegra210_general
+	},
+};
+#endif
+
+inline void emc_writel(struct tegra_emc *emc, u32 val, unsigned long offset)
+{
+	writel(val, emc->emc_base + offset);
+}
+
+inline u32 emc_readl(struct tegra_emc *emc, unsigned long offset)
+{
+	return readl(emc->emc_base + offset);
+}
+
+inline void emc1_writel(struct tegra_emc *emc, u32 val, unsigned long offset)
+{
+	writel(val, emc->emc1_base + offset);
+}
+
+inline u32 emc1_readl(struct tegra_emc *emc, unsigned long offset)
+{
+	return readl(emc->emc1_base + offset);
+}
+
+inline void emc_writel_per_ch(struct tegra_emc *emc, u32 val, int type,
+			      unsigned long offset)
+{
+	switch (type) {
+	case REG_EMC:
+	case REG_EMC0:
+		return writel(val, emc->emc_base + offset);
+	case REG_EMC1:
+		return writel(val, emc->emc1_base + offset);
+	}
+}
+
+inline u32 emc_readl_per_ch(struct tegra_emc *emc, int type,
+			    unsigned long offset)
+{
+	u32 val = 0;
+	switch (type) {
+	case REG_EMC:
+	case REG_EMC0:
+		val = readl(emc->emc_base + offset);
+		break;
+	case REG_EMC1:
+		val = readl(emc->emc1_base + offset);
+		break;
+	}
+	return val;
+}
+
+inline void ccfifo_writel(struct tegra_emc *emc, u32 val, unsigned long addr,
+			  u32 delay)
+{
+	writel(val, emc->emc_base + EMC_CCFIFO_DATA);
+	writel((addr & 0xffff) | ((delay & 0x7fff) << 16) | (1 << 31),
+		emc->emc_base + EMC_CCFIFO_ADDR);
+}
+
+static inline u32 emc_src_val(u32 val)
+{
+	return (val & EMC_CLK_EMC_2X_CLK_SRC_MASK) >>
+		EMC_CLK_EMC_2X_CLK_SRC_SHIFT;
+}
+
+static inline u32 emc_div_val(u32 val)
+{
+	return (val & EMC_CLK_EMC_2X_CLK_DIVISOR_MASK) >>
+		EMC_CLK_EMC_2X_CLK_DIVISOR_SHIFT;
+}
+
+
+/*
+ * Tell the dram thermal driver to start/stop polling for the DRAM temperature.
+ * This should be called when the DRAM temp might be hot, for example, if some
+ * other temp sensor is reading very high.
+ */
+static void tegra_emc_mr4_temp_trigger(int do_poll)
+{
+	if (do_poll) {
+		atomic_set(&mr4_temp_poll, 1);
+		mod_timer(&emc_timer_mr4,
+			  jiffies + msecs_to_jiffies(timer_period_mr4));
+	} else {
+		atomic_set(&mr4_temp_poll, 0);
+	}
+}
+
+/*
+ * If the freq is higher than some threshold then poll. Only happens if a
+ * threshold is actually defined.
+ */
+static void tegra_emc_mr4_freq_check(unsigned long freq)
+{
+	if (mr4_freq_threshold && freq >= mr4_freq_threshold)
+		tegra_emc_mr4_temp_trigger(1);
+	else
+		tegra_emc_mr4_temp_trigger(0);
+}
+
+static void emc_timer_training_start(void)
+{
+	mod_timer(&emc_timer_training,
+		  jiffies + msecs_to_jiffies(timer_period_training));
+}
+
+static void emc_timer_training_stop(void)
+{
+	del_timer(&emc_timer_training);
+}
+
+static void emc_set_clock(struct tegra_emc *emc, struct clk *parent)
+{
+	seq->set_clock(emc, parent);
+
+	if (emc->next_timing->periodic_training)
+		emc_timer_training_start();
+	else
+		emc_timer_training_stop();
+	/* EMC freq dependent MR4 polling. */
+	tegra_emc_mr4_freq_check(emc->next_timing->rate);
+}
+
+static int emc_table_lookup(struct tegra_emc *emc, unsigned long rate)
+{
+	int i;
+
+	for (i = 0; i < emc->num_timings; i++) {
+		if (emc->emc_table[i].rate == rate)
+			return i;
+	}
+
+	return -EINVAL;
+}
+
+static inline void emc_get_timing(struct tegra_emc *emc,
+				  struct emc_timing *timing)
+{
+	int i, div;
+	u32 val;
+	unsigned long rate;
+
+	for (i = 0; i < timing->num_burst; i++) {
+		if (burst_regs_off[i])
+			timing->burst_regs[i] = emc_readl(emc,
+							  burst_regs_off[i]);
+		else
+			timing->burst_regs[i] = 0;
+	}
+
+	for (i = 0; i < timing->num_burst_per_ch; i++)
+		timing->burst_reg_per_ch[i] = emc_readl_per_ch(emc,
+			burst_regs_per_ch_type[i], burst_regs_per_ch_off[i]);
+
+	for (i = 0; i < timing->num_trim; i++)
+		timing->trim_regs[i] = emc_readl(emc, trim_regs_off[i]);
+
+	for (i = 0; i < timing->num_trim_per_ch; i++)
+		timing->trim_perch_regs[i] = emc_readl_per_ch(emc,
+			trim_regs_per_ch_type[i], trim_regs_per_ch_off[i]);
+
+	for (i = 0; i < timing->vref_num; i++)
+		timing->vref_perch_regs[i] = emc_readl_per_ch(emc,
+			vref_regs_per_ch_type[i], vref_regs_per_ch_off[i]);
+
+#if 0
+	for (i = 0; i < timing->num_mc_regs; i++)
+		timing->burst_mc_regs[i] = mc_readl(burst_mc_regs_off[i]);
+
+	for (i = 0; i < timing->num_up_down; i++)
+		timing->la_scale_regs[i] = mc_readl(la_scale_regs_off[i]);
+#endif
+
+	
+	val = tegra210_clk_emc_get_setting();
+	rate = clk_get_rate(emc_clks[emc_src_val(val)].clk);
+	div = emc_div_val(val);
+	div += 2;
+	rate *= 2;
+	rate += div - 1;
+	do_div(rate, div);
+	timing->rate = rate / 1000;
+}
+
+void do_clock_change(struct tegra_emc *emc)
+{
+	int err;
+
+	// mc_readl(MC_EMEM_ADR_CFG);
+	emc_readl(emc, EMC_INTSTATUS);	
+
+	tegra210_clk_emc_update_setting(emc->clk_setting);
+
+	err = wait_for_update(emc, EMC_INTSTATUS,
+			      EMC_INTSTATUS_CLKCHANGE_COMPLETE, true, REG_EMC);
+	if (err) {
+		pr_err("%s: clock change completion error: %d", __func__, err);
+		WARN_ON(1);
+	}
+}
+
+int tegra210_emc_set_over_temp_state(struct tegra_emc *emc,
+				     unsigned long state)
+{
+	unsigned long flags;
+	struct emc_timing *current_table;
+	struct emc_timing *new_table;
+
+	if ((emc->dram_type != DRAM_TYPE_LPDDR2 &&
+	     emc->dram_type != DRAM_TYPE_LPDDR4))
+		return -ENODEV;
+
+	if (state > TEGRA_DRAM_OVER_TEMP_THROTTLE)
+		return -EINVAL;
+
+	if (state == dram_over_temp_state)
+		return 0;
+
+	spin_lock_irqsave(&emc_access_lock, flags);
+
+	current_table = emc_get_table(emc, dram_over_temp_state);
+	new_table = emc_get_table(emc, state);
+	dram_over_temp_state = state;
+
+	if (current_table != new_table) {
+		emc->next_timing = &new_table[last_rate_idx];
+		emc->clk_setting |= EMC_CLK_FORCE_CC_TRIGGER;
+		emc_set_clock(emc, emc_clks[emc_src_val(emc->clk_setting)].clk);
+		emc->emc_table = new_table;
+		emc->current_timing = &new_table[last_rate_idx];
+	} else {
+		set_over_temp_timing(emc, emc->current_timing, state);
+		emc_timing_update(emc, 0);
+		if (state != TEGRA_DRAM_OVER_TEMP_NONE)
+			emc_writel(emc, EMC_REF_FORCE_CMD, EMC_REF);
+	}
+
+	spin_unlock_irqrestore(&emc_access_lock, flags);
+
+	return 0;
+}
+
+static void emc_mr4_poll(unsigned long data)
+{
+	int dram_temp;
+	struct tegra_emc *emc = (struct tegra_emc *)data;
+
+	if (!test_mode)
+		dram_temp = emc_get_dram_temperature(emc);
+	else
+		dram_temp = dram_temp_override;
+
+	if (prev_temp == dram_temp)
+		goto reset;
+
+	if (WARN(dram_temp < 0, "Unable to read DRAM temp (MR4)!\n"))
+		goto reset;
+
+	switch (dram_temp) {
+	case 0:
+	case 1:
+	case 2:
+	case 3:
+		/*
+		 * Temp is fine - go back to regular refresh.
+		 */
+		pr_info("Setting nominal refresh + timings.\n");
+		tegra210_emc_set_over_temp_state(emc, TEGRA_DRAM_OVER_TEMP_NONE);
+		break;
+	case 4:
+		pr_info("Enabling 2x refresh.\n");
+		tegra210_emc_set_over_temp_state(emc, TEGRA_DRAM_OVER_TEMP_REFRESH_X2);
+		break;
+	case 5:
+		pr_info("Enabling 4x refresh.\n");
+		tegra210_emc_set_over_temp_state(emc, TEGRA_DRAM_OVER_TEMP_REFRESH_X4);
+		break;
+	case 6:
+		pr_info("Enabling 4x refresh + derating.\n");
+		tegra210_emc_set_over_temp_state(emc, TEGRA_DRAM_OVER_TEMP_THROTTLE);
+		break;
+	default:
+		WARN(1, "%s: Invalid DRAM temp state %d\n",
+		     __func__, dram_temp);
+		break;
+	}
+	prev_temp = dram_temp;
+
+reset:
+	if (atomic_read(&mr4_temp_poll) == 0 &&
+	    atomic_read(&mr4_force_poll) == 0)
+		return;
+
+	if (mod_timer(&emc_timer_mr4,
+		      jiffies + msecs_to_jiffies(timer_period_mr4)))
+		pr_err("Failed to restart timer!!!\n");
+}
+void tegra210_emc_mr4_set_freq_thresh(unsigned long thresh)
+{
+	mr4_freq_threshold = thresh;
+}
+
+static void emc_train(unsigned long data)
+{
+	unsigned long flags;
+	struct tegra_emc *emc = (struct tegra_emc *)data;
+
+	if (!emc->current_timing)
+		return;
+
+	spin_lock_irqsave(&emc_access_lock, flags);
+	if (seq->periodic_compensation)
+		seq->periodic_compensation(emc);
+	spin_unlock_irqrestore(&emc_access_lock, flags);
+
+	mod_timer(&emc_timer_training,
+		  jiffies + msecs_to_jiffies(timer_period_training));
+}
+
+static void tegra210_change_dll_src(struct tegra_emc *emc,
+				    struct clk *newparent)
+{
+
+	clk_set_rate(emc->emc_dll_clk, emc->next_timing->rate);
+	clk_set_parent(emc->emc_dll_clk, newparent);
+	if (emc->next_timing->clk_out_enb_x_0_clk_enb_emc_dll)
+		clk_prepare_enable(emc->emc_dll_clk);
+	else
+		clk_disable_unprepare(emc->emc_dll_clk);
+}
+
+struct emc_timing *get_timing_from_freq(struct tegra_emc *emc,
+				       unsigned long rate)
+{
+	int i;
+
+	for (i = 0; i < emc->num_timings; i++)
+		if (emc->emc_table[i].rate == rate)
+			return &emc->emc_table[i];
+
+	return NULL;
+}
+
+int wait_for_update(struct tegra_emc *emc, u32 status_reg, u32 bit_mask,
+		    bool updated_state, int chan)
+{
+	int i, err = -ETIMEDOUT;
+	u32 reg;
+
+	for (i = 0; i < EMC_STATUS_UPDATE_TIMEOUT; i++) {
+		reg = emc_readl_per_ch(emc, chan, status_reg);
+		if (!!(reg & bit_mask) == updated_state) {
+			err = 0;
+			goto done;
+		}
+		udelay(1);
+	}
+
+done:
+	return err;
+}
+
+void emc_set_shadow_bypass(struct tegra_emc *emc, int set)
+{
+	u32 emc_dbg = emc_readl(emc, EMC_DBG);
+
+	if (set)
+		emc_writel(emc, emc_dbg | EMC_DBG_WRITE_MUX_ACTIVE, EMC_DBG);
+	else
+		emc_writel(emc, emc_dbg & ~EMC_DBG_WRITE_MUX_ACTIVE, EMC_DBG);
+}
+
+u32 get_dll_state(struct emc_timing *next_timing)
+{
+	bool next_dll_enabled;
+
+	next_dll_enabled = !(next_timing->emc_emrs & 0x1);
+	if (next_dll_enabled)
+		return DLL_ON;
+	else
+		return DLL_OFF;
+}
+
+u32 div_o3(u32 a, u32 b)
+{
+	u32 result = a / b;
+
+	if ((b * result) < a)
+		return result + 1;
+	else
+		return result;
+}
+
+void emc_timing_update(struct tegra_emc *emc, int dual_chan)
+{
+	int err = 0;
+
+	emc_writel(emc, 0x1, EMC_TIMING_CONTROL);
+	err |= wait_for_update(emc, EMC_EMC_STATUS,
+			       EMC_EMC_STATUS_TIMING_UPDATE_STALLED, false,
+			       REG_EMC);
+	if (dual_chan)
+		err |= wait_for_update(emc, EMC_EMC_STATUS,
+				       EMC_EMC_STATUS_TIMING_UPDATE_STALLED,
+				       false, REG_EMC1);
+	if (err) {
+		pr_err("%s: timing update error: %d", __func__, err);
+		BUG();
+	}
+}
+
+static void __emc_copy_table_params(struct emc_timing *src,
+				    struct emc_timing *dst, int flags)
+{
+	int i;
+
+	if (flags & EMC_COPY_TABLE_PARAM_PERIODIC_FIELDS) {
+		dst->trained_dram_clktree_c0d0u0 =
+			src->trained_dram_clktree_c0d0u0;
+		dst->trained_dram_clktree_c0d0u1 =
+			src->trained_dram_clktree_c0d0u1;
+		dst->trained_dram_clktree_c0d1u0 =
+			src->trained_dram_clktree_c0d1u0;
+		dst->trained_dram_clktree_c0d1u1 =
+			src->trained_dram_clktree_c0d1u1;
+		dst->trained_dram_clktree_c1d0u0 =
+			src->trained_dram_clktree_c1d0u0;
+		dst->trained_dram_clktree_c1d0u1 =
+			src->trained_dram_clktree_c1d0u1;
+		dst->trained_dram_clktree_c1d1u0 =
+			src->trained_dram_clktree_c1d1u0;
+		dst->trained_dram_clktree_c1d1u1 =
+			src->trained_dram_clktree_c1d1u1;
+		dst->current_dram_clktree_c0d0u0 =
+			src->current_dram_clktree_c0d0u0;
+		dst->current_dram_clktree_c0d0u1 =
+			src->current_dram_clktree_c0d0u1;
+		dst->current_dram_clktree_c0d1u0 =
+			src->current_dram_clktree_c0d1u0;
+		dst->current_dram_clktree_c0d1u1 =
+			src->current_dram_clktree_c0d1u1;
+		dst->current_dram_clktree_c1d0u0 =
+			src->current_dram_clktree_c1d0u0;
+		dst->current_dram_clktree_c1d0u1 =
+			src->current_dram_clktree_c1d0u1;
+		dst->current_dram_clktree_c1d1u0 =
+			src->current_dram_clktree_c1d1u0;
+		dst->current_dram_clktree_c1d1u1 =
+			src->current_dram_clktree_c1d1u1;
+	}
+
+	if (flags & EMC_COPY_TABLE_PARAM_TRIM_REGS) {
+		for (i = 0; i < src->num_trim_per_ch; i++)
+			dst->trim_perch_regs[i] = src->trim_perch_regs[i];
+
+		for (i = 0; i < src->num_trim; i++)
+			dst->trim_regs[i] = src->trim_regs[i];
+
+		for (i = 0; i < src->num_burst_per_ch; i++)
+			dst->burst_reg_per_ch[i] = src->burst_reg_per_ch[i];
+
+		dst->trained = src->trained;
+	}
+}
+
+void tegra210_update_emc_alt_timing(struct tegra_emc *emc,
+				    struct emc_timing *current_timing)
+{
+	struct emc_timing *current_table, *alt_timing;
+	int i;
+
+	if (!emc->emc_table_derated)
+		return;
+
+	current_table = emc_get_table(emc, dram_over_temp_state);
+	i = current_timing - current_table;
+
+	BUG_ON(i < 0 || i > emc->num_timings);
+
+	if (dram_over_temp_state == TEGRA_DRAM_OVER_TEMP_THROTTLE)
+		alt_timing = &emc->emc_table_normal[i];
+	else
+		alt_timing = &emc->emc_table_derated[i];
+
+	__emc_copy_table_params(current_timing, alt_timing,
+				EMC_COPY_TABLE_PARAM_PERIODIC_FIELDS);
+}
+
+u32 tegra210_actual_osc_clocks(u32 in)
+{
+	if (in < 0x40)
+		return in * 16;
+	else if (in < 0x80)
+		return 2048;
+	else if (in < 0xc0)
+		return 4096;
+	else
+		return 8192;
+}
+
+void tegra210_start_periodic_compensation(struct tegra_emc *emc)
+{
+	u32 mpc_req = 0x4b;
+
+	emc_writel(emc, mpc_req, EMC_MPC);
+	mpc_req = emc_readl(emc, EMC_MPC);
+}
+
+u32 tegra210_apply_periodic_compensation_trimmer(
+		struct emc_timing *next_timing, u32 offset)
+{
+	u32 i, temp = 0;
+	u32 next_timing_rate_mhz = next_timing->rate / 1000;
+	s32 tree_delta[4];
+	s32 tree_delta_taps[4];
+	s32 new[] = {
+		TRIM_REG(0, 0, 0, 0),
+		TRIM_REG(0, 0, 0, 1),
+		TRIM_REG(0, 0, 1, 2),
+		TRIM_REG(0, 0, 1, 3),
+
+		TRIM_REG(1, 0, 2, 4),
+		TRIM_REG(1, 0, 2, 5),
+		TRIM_REG(1, 0, 3, 6),
+		TRIM_REG(1, 0, 3, 7),
+
+		TRIM_REG(0, 1, 0, 0),
+		TRIM_REG(0, 1, 0, 1),
+		TRIM_REG(0, 1, 1, 2),
+		TRIM_REG(0, 1, 1, 3),
+
+		TRIM_REG(1, 1, 2, 4),
+		TRIM_REG(1, 1, 2, 5),
+		TRIM_REG(1, 1, 3, 6),
+		TRIM_REG(1, 1, 3, 7)
+	};
+
+	switch (offset) {
+	case EMC_PMACRO_OB_DDLL_LONG_DQ_RANK0_0:
+	case EMC_PMACRO_OB_DDLL_LONG_DQ_RANK0_1:
+	case EMC_PMACRO_OB_DDLL_LONG_DQ_RANK0_2:
+	case EMC_PMACRO_OB_DDLL_LONG_DQ_RANK0_3:
+	case EMC_DATA_BRLSHFT_0:
+		tree_delta[0] = 128 *
+				(next_timing->current_dram_clktree_c0d0u0 -
+				 next_timing->trained_dram_clktree_c0d0u0);
+		tree_delta[1] = 128 *
+				(next_timing->current_dram_clktree_c0d0u1 -
+				 next_timing->trained_dram_clktree_c0d0u1);
+		tree_delta[2] = 128 *
+				(next_timing->current_dram_clktree_c1d0u0 -
+				 next_timing->trained_dram_clktree_c1d0u0);
+		tree_delta[3] = 128 *
+				(next_timing->current_dram_clktree_c1d0u1 -
+				 next_timing->trained_dram_clktree_c1d0u1);
+
+		tree_delta_taps[0] = (tree_delta[0] *
+				     (s32)next_timing_rate_mhz) / 1000000;
+		tree_delta_taps[1] = (tree_delta[1] *
+				     (s32)next_timing_rate_mhz) / 1000000;
+		tree_delta_taps[2] = (tree_delta[2] *
+				     (s32)next_timing_rate_mhz) / 1000000;
+		tree_delta_taps[3] = (tree_delta[3] *
+				     (s32)next_timing_rate_mhz) / 1000000;
+
+		for (i = 0; i < 4; i++) {
+			if ((tree_delta_taps[i] > next_timing->tree_margin) ||
+			    (tree_delta_taps[i] <
+			    (-1 * next_timing->tree_margin))) {
+				new[i * 2] = new[i * 2] + tree_delta_taps[i];
+				new[i * 2 + 1] = new[i * 2 + 1] +
+						 tree_delta_taps[i];
+			}
+		}
+
+		if (offset == EMC_DATA_BRLSHFT_0) {
+			for (i = 0; i < 8; i++)
+				new[i] = new[i] / 64;
+		} else {
+			for (i = 0; i < 8; i++)
+				new[i] = new[i] % 64;
+		}
+		break;
+
+	case EMC_PMACRO_OB_DDLL_LONG_DQ_RANK1_0:
+	case EMC_PMACRO_OB_DDLL_LONG_DQ_RANK1_1:
+	case EMC_PMACRO_OB_DDLL_LONG_DQ_RANK1_2:
+	case EMC_PMACRO_OB_DDLL_LONG_DQ_RANK1_3:
+	case EMC_DATA_BRLSHFT_1:
+		tree_delta[0] = 128 *
+				(next_timing->current_dram_clktree_c0d1u0 -
+				 next_timing->trained_dram_clktree_c0d1u0);
+		tree_delta[1] = 128 *
+				(next_timing->current_dram_clktree_c0d1u1 -
+				 next_timing->trained_dram_clktree_c0d1u1);
+		tree_delta[2] = 128 *
+				(next_timing->current_dram_clktree_c1d1u0 -
+				 next_timing->trained_dram_clktree_c1d1u0);
+		tree_delta[3] = 128 *
+				(next_timing->current_dram_clktree_c1d1u1 -
+				 next_timing->trained_dram_clktree_c1d1u1);
+
+		tree_delta_taps[0] = (tree_delta[0] *
+				     (s32)next_timing_rate_mhz) / 1000000;
+		tree_delta_taps[1] = (tree_delta[1] *
+				     (s32)next_timing_rate_mhz) / 1000000;
+		tree_delta_taps[2] = (tree_delta[2] *
+				     (s32)next_timing_rate_mhz) / 1000000;
+		tree_delta_taps[3] = (tree_delta[3] *
+				     (s32)next_timing_rate_mhz) / 1000000;
+
+		for (i = 0; i < 4; i++) {
+			if ((tree_delta_taps[i] > next_timing->tree_margin) ||
+			    (tree_delta_taps[i] <
+			     (-1 * next_timing->tree_margin))) {
+				new[8 + i * 2] = new[8 + i * 2] +
+						 tree_delta_taps[i];
+				new[8 + i * 2 + 1] = new[8 + i * 2 + 1] +
+						     tree_delta_taps[i];
+			}
+		}
+
+		if (offset == EMC_DATA_BRLSHFT_1) {
+			for (i = 0; i < 8; i++)
+				new[i + 8] = new[i + 8] / 64;
+		} else {
+			for (i = 0; i < 8; i++)
+				new[i + 8] = new[i + 8] % 64;
+		}
+		break;
+	}
+
+	switch (offset) {
+	case EMC_PMACRO_OB_DDLL_LONG_DQ_RANK0_0:
+		temp = CALC_TEMP(0, 0, 0, 1, 0);
+		break;
+	case EMC_PMACRO_OB_DDLL_LONG_DQ_RANK0_1:
+		temp = CALC_TEMP(0, 1, 2, 3, 2);
+		break;
+	case EMC_PMACRO_OB_DDLL_LONG_DQ_RANK0_2:
+		temp = CALC_TEMP(0, 2, 4, 5, 4);
+		break;
+	case EMC_PMACRO_OB_DDLL_LONG_DQ_RANK0_3:
+		temp = CALC_TEMP(0, 3, 6, 7, 6);
+		break;
+	case EMC_PMACRO_OB_DDLL_LONG_DQ_RANK1_0:
+		temp = CALC_TEMP(1, 0, 0, 1, 8);
+		break;
+	case EMC_PMACRO_OB_DDLL_LONG_DQ_RANK1_1:
+		temp = CALC_TEMP(1, 1, 2, 3, 10);
+		break;
+	case EMC_PMACRO_OB_DDLL_LONG_DQ_RANK1_2:
+		temp = CALC_TEMP(1, 2, 4, 5, 12);
+		break;
+	case EMC_PMACRO_OB_DDLL_LONG_DQ_RANK1_3:
+		temp = CALC_TEMP(1, 3, 6, 7, 14);
+		break;
+	case EMC_DATA_BRLSHFT_0:
+		temp = ((new[0] <<
+			 EMC_DATA_BRLSHFT_0_RANK0_BYTE0_DATA_BRLSHFT_SHIFT) &
+			 EMC_DATA_BRLSHFT_0_RANK0_BYTE0_DATA_BRLSHFT_MASK) |
+		       ((new[1] <<
+			 EMC_DATA_BRLSHFT_0_RANK0_BYTE1_DATA_BRLSHFT_SHIFT) &
+			 EMC_DATA_BRLSHFT_0_RANK0_BYTE1_DATA_BRLSHFT_MASK) |
+		       ((new[2] <<
+			 EMC_DATA_BRLSHFT_0_RANK0_BYTE2_DATA_BRLSHFT_SHIFT) &
+			 EMC_DATA_BRLSHFT_0_RANK0_BYTE2_DATA_BRLSHFT_MASK) |
+		       ((new[3] <<
+			 EMC_DATA_BRLSHFT_0_RANK0_BYTE3_DATA_BRLSHFT_SHIFT) &
+			 EMC_DATA_BRLSHFT_0_RANK0_BYTE3_DATA_BRLSHFT_MASK) |
+		       ((new[4] <<
+			 EMC_DATA_BRLSHFT_0_RANK0_BYTE4_DATA_BRLSHFT_SHIFT) &
+			 EMC_DATA_BRLSHFT_0_RANK0_BYTE4_DATA_BRLSHFT_MASK) |
+		       ((new[5] <<
+			 EMC_DATA_BRLSHFT_0_RANK0_BYTE5_DATA_BRLSHFT_SHIFT) &
+			 EMC_DATA_BRLSHFT_0_RANK0_BYTE5_DATA_BRLSHFT_MASK) |
+		       ((new[6] <<
+			 EMC_DATA_BRLSHFT_0_RANK0_BYTE6_DATA_BRLSHFT_SHIFT) &
+			 EMC_DATA_BRLSHFT_0_RANK0_BYTE6_DATA_BRLSHFT_MASK) |
+		       ((new[7] <<
+			 EMC_DATA_BRLSHFT_0_RANK0_BYTE7_DATA_BRLSHFT_SHIFT) &
+			 EMC_DATA_BRLSHFT_0_RANK0_BYTE7_DATA_BRLSHFT_MASK);
+		break;
+	case EMC_DATA_BRLSHFT_1:
+		temp = ((new[8] <<
+			 EMC_DATA_BRLSHFT_1_RANK1_BYTE0_DATA_BRLSHFT_SHIFT) &
+			 EMC_DATA_BRLSHFT_1_RANK1_BYTE0_DATA_BRLSHFT_MASK) |
+		       ((new[9] <<
+			 EMC_DATA_BRLSHFT_1_RANK1_BYTE1_DATA_BRLSHFT_SHIFT) &
+			 EMC_DATA_BRLSHFT_1_RANK1_BYTE1_DATA_BRLSHFT_MASK) |
+		       ((new[10] <<
+			 EMC_DATA_BRLSHFT_1_RANK1_BYTE2_DATA_BRLSHFT_SHIFT) &
+			 EMC_DATA_BRLSHFT_1_RANK1_BYTE2_DATA_BRLSHFT_MASK) |
+		       ((new[11] <<
+			 EMC_DATA_BRLSHFT_1_RANK1_BYTE3_DATA_BRLSHFT_SHIFT) &
+			 EMC_DATA_BRLSHFT_1_RANK1_BYTE3_DATA_BRLSHFT_MASK) |
+		       ((new[12] <<
+			 EMC_DATA_BRLSHFT_1_RANK1_BYTE4_DATA_BRLSHFT_SHIFT) &
+			 EMC_DATA_BRLSHFT_1_RANK1_BYTE4_DATA_BRLSHFT_MASK) |
+		       ((new[13] <<
+			 EMC_DATA_BRLSHFT_1_RANK1_BYTE5_DATA_BRLSHFT_SHIFT) &
+			 EMC_DATA_BRLSHFT_1_RANK1_BYTE5_DATA_BRLSHFT_MASK) |
+		       ((new[14] <<
+			 EMC_DATA_BRLSHFT_1_RANK1_BYTE6_DATA_BRLSHFT_SHIFT) &
+			 EMC_DATA_BRLSHFT_1_RANK1_BYTE6_DATA_BRLSHFT_MASK) |
+		       ((new[15] <<
+			 EMC_DATA_BRLSHFT_1_RANK1_BYTE7_DATA_BRLSHFT_SHIFT) &
+			 EMC_DATA_BRLSHFT_1_RANK1_BYTE7_DATA_BRLSHFT_MASK);
+		break;
+	default:
+		break;
+	}
+
+	return temp;
+}
+
+u32 tegra210_dll_prelock(struct tegra_emc *emc, int dvfs_with_training,
+			 struct clk *newparent)
+{
+	u32 emc_dig_dll_status;
+	u32 dll_locked;
+	u32 dll_out;
+	u32 emc_cfg_dig_dll;
+	u32 emc_dll_cfg_0;
+	u32 emc_dll_cfg_1;
+	u32 ddllcal_ctrl_start_trim_val;
+	u32 dll_en;
+	u32 dual_channel_lpddr4_case;
+	u32 dll_priv_updated;
+
+	dual_channel_lpddr4_case =
+	      !!(emc_readl(emc, EMC_FBIO_CFG7) & EMC_FBIO_CFG7_CH1_ENABLE) &
+	      !!(emc_readl(emc, EMC_FBIO_CFG7) & EMC_FBIO_CFG7_CH0_ENABLE);
+
+	emc_dig_dll_status = 0;
+	dll_locked = 0;
+	dll_out = 0;
+	emc_cfg_dig_dll = 0;
+	emc_dll_cfg_0 = 0;
+	emc_dll_cfg_1 = 0;
+	ddllcal_ctrl_start_trim_val = 0;
+	dll_en = 0;
+
+	emc_cfg_dig_dll = emc_readl(emc, EMC_CFG_DIG_DLL) &
+			  ~EMC_CFG_DIG_DLL_CFG_DLL_LOCK_LIMIT_MASK;
+	emc_cfg_dig_dll |= (3 << EMC_CFG_DIG_DLL_CFG_DLL_LOCK_LIMIT_SHIFT);
+	emc_cfg_dig_dll &= ~EMC_CFG_DIG_DLL_CFG_DLL_EN;
+	emc_cfg_dig_dll &= ~EMC_CFG_DIG_DLL_CFG_DLL_MODE_MASK;
+	emc_cfg_dig_dll |= (3 << EMC_CFG_DIG_DLL_CFG_DLL_MODE_SHIFT);
+	emc_cfg_dig_dll |= EMC_CFG_DIG_DLL_CFG_DLL_STALL_ALL_TRAFFIC;
+	emc_cfg_dig_dll &= ~EMC_CFG_DIG_DLL_CFG_DLL_STALL_RW_UNTIL_LOCK;
+	emc_cfg_dig_dll &= ~EMC_CFG_DIG_DLL_CFG_DLL_STALL_ALL_UNTIL_LOCK;
+
+	emc_writel(emc, emc_cfg_dig_dll, EMC_CFG_DIG_DLL);
+	emc_writel(emc, 1, EMC_TIMING_CONTROL);
+
+	wait_for_update(emc, EMC_EMC_STATUS,
+			EMC_EMC_STATUS_TIMING_UPDATE_STALLED, 0, REG_EMC);
+	if (dual_channel_lpddr4_case)
+		wait_for_update(emc, EMC_EMC_STATUS,
+				EMC_EMC_STATUS_TIMING_UPDATE_STALLED,
+				0, REG_EMC1);
+
+	do {
+		emc_cfg_dig_dll = emc_readl(emc, EMC_CFG_DIG_DLL);
+		dll_en = emc_cfg_dig_dll & EMC_CFG_DIG_DLL_CFG_DLL_EN;
+	} while (dll_en == 1);
+
+	if (dual_channel_lpddr4_case) {
+		do {
+			emc_cfg_dig_dll = emc1_readl(emc, EMC_CFG_DIG_DLL);
+			dll_en = emc_cfg_dig_dll & EMC_CFG_DIG_DLL_CFG_DLL_EN;
+		} while (dll_en == 1);
+	}
+
+	emc_dll_cfg_0 = emc->next_timing->burst_regs[EMC_DLL_CFG_0_INDEX];
+
+	emc_writel(emc, emc_dll_cfg_0, EMC_DLL_CFG_0);
+
+	if (emc->next_timing->rate >= 400000
+		&& emc->next_timing->rate < 600000)
+		ddllcal_ctrl_start_trim_val = 150;
+	else if (emc->next_timing->rate >= 600000
+		&& emc->next_timing->rate < 800000)
+		ddllcal_ctrl_start_trim_val = 100;
+	else if (emc->next_timing->rate >= 800000
+		&& emc->next_timing->rate < 1000000)
+		ddllcal_ctrl_start_trim_val = 70;
+	else if (emc->next_timing->rate >= 1000000
+		&& emc->next_timing->rate < 1200000)
+		ddllcal_ctrl_start_trim_val = 30;
+	else
+		ddllcal_ctrl_start_trim_val = 20;
+
+	emc_dll_cfg_1 = emc_readl(emc, EMC_DLL_CFG_1);
+	emc_dll_cfg_1 &= EMC_DLL_CFG_1_DDLLCAL_CTRL_START_TRIM_MASK;
+	emc_dll_cfg_1 |= ddllcal_ctrl_start_trim_val;
+	emc_writel(emc, emc_dll_cfg_1, EMC_DLL_CFG_1);
+
+	
+	tegra210_change_dll_src(emc, newparent);
+
+	emc_cfg_dig_dll = emc_readl(emc, EMC_CFG_DIG_DLL);
+	emc_cfg_dig_dll |= EMC_CFG_DIG_DLL_CFG_DLL_EN;
+	emc_writel(emc, emc_cfg_dig_dll, EMC_CFG_DIG_DLL);
+
+	emc_timing_update(emc, dual_channel_lpddr4_case ?
+			  DUAL_CHANNEL : SINGLE_CHANNEL);
+
+	do {
+		emc_cfg_dig_dll = emc_readl(emc, EMC_CFG_DIG_DLL);
+		dll_en = emc_cfg_dig_dll & EMC_CFG_DIG_DLL_CFG_DLL_EN;
+	} while (dll_en == 0);
+
+	if (dual_channel_lpddr4_case) {
+		do {
+			emc_cfg_dig_dll = emc1_readl(emc, EMC_CFG_DIG_DLL);
+			dll_en = emc_cfg_dig_dll & EMC_CFG_DIG_DLL_CFG_DLL_EN;
+		} while (dll_en == 0);
+	}
+
+	do {
+		emc_dig_dll_status = emc_readl(emc, EMC_DIG_DLL_STATUS);
+		dll_locked = emc_dig_dll_status & EMC_DIG_DLL_STATUS_DLL_LOCK;
+		dll_priv_updated = emc_dig_dll_status &
+				   EMC_DIG_DLL_STATUS_DLL_PRIV_UPDATED;
+	} while (!dll_locked || !dll_priv_updated);
+
+	emc_dig_dll_status = emc_readl(emc, EMC_DIG_DLL_STATUS);
+	return emc_dig_dll_status & EMC_DIG_DLL_STATUS_DLL_OUT_MASK;
+}
+
+u32 tegra210_dvfs_power_ramp_up(struct tegra_emc *emc, u32 clk,
+				int flip_backward)
+{
+	u32 pmacro_cmd_pad;
+	u32 pmacro_dq_pad;
+	u32 pmacro_rfu1;
+	u32 pmacro_cfg5;
+	u32 pmacro_common_tx;
+	u32 ramp_up_wait = 0;
+
+	if (flip_backward) {
+		pmacro_cmd_pad   = emc->current_timing->
+			burst_regs[EMC_PMACRO_CMD_PAD_TX_CTRL_INDEX];
+		pmacro_dq_pad    = emc->current_timing->
+			burst_regs[EMC_PMACRO_DATA_PAD_TX_CTRL_INDEX];
+		pmacro_rfu1      = emc->current_timing->
+			burst_regs[EMC_PMACRO_BRICK_CTRL_RFU1_INDEX];
+		pmacro_cfg5      = emc->current_timing->
+			burst_regs[EMC_FBIO_CFG5_INDEX];
+		pmacro_common_tx = emc->current_timing->
+			burst_regs[EMC_PMACRO_COMMON_PAD_TX_CTRL_INDEX];
+	} else {
+		pmacro_cmd_pad   = emc->next_timing->
+			burst_regs[EMC_PMACRO_CMD_PAD_TX_CTRL_INDEX];
+		pmacro_dq_pad    = emc->next_timing->
+			burst_regs[EMC_PMACRO_DATA_PAD_TX_CTRL_INDEX];
+		pmacro_rfu1      = emc->next_timing->
+			burst_regs[EMC_PMACRO_BRICK_CTRL_RFU1_INDEX];
+		pmacro_cfg5      = emc->next_timing->
+			burst_regs[EMC_FBIO_CFG5_INDEX];
+		pmacro_common_tx = emc->next_timing->
+			burst_regs[EMC_PMACRO_COMMON_PAD_TX_CTRL_INDEX];
+	}
+	pmacro_cmd_pad |= EMC_PMACRO_CMD_PAD_TX_CTRL_CMD_DQ_TX_DRVFORCEON;
+
+	if (clk < 1000000 / DVFS_FGCG_MID_SPEED_THRESHOLD) {
+		ccfifo_writel(emc, pmacro_common_tx & 0xa,
+			      EMC_PMACRO_COMMON_PAD_TX_CTRL, 0);
+		ccfifo_writel(emc, pmacro_common_tx & 0xf,
+			      EMC_PMACRO_COMMON_PAD_TX_CTRL,
+			      (100000 / clk) + 1);
+		ramp_up_wait += 100000;
+	} else {
+		ccfifo_writel(emc, pmacro_common_tx | 0x8,
+			      EMC_PMACRO_COMMON_PAD_TX_CTRL, 0);
+	}
+
+	if (clk < 1000000 / DVFS_FGCG_HIGH_SPEED_THRESHOLD) {
+		if (clk < 1000000 / IOBRICK_DCC_THRESHOLD) {
+			pmacro_cmd_pad |=
+				EMC_PMACRO_CMD_PAD_TX_CTRL_CMD_DQSP_TX_E_DCC |
+				EMC_PMACRO_CMD_PAD_TX_CTRL_CMD_DQSN_TX_E_DCC;
+			pmacro_cmd_pad &=
+				~(EMC_PMACRO_CMD_PAD_TX_CTRL_CMD_DQ_TX_E_DCC |
+				  EMC_PMACRO_CMD_PAD_TX_CTRL_CMD_CMD_TX_E_DCC);
+			ccfifo_writel(emc, pmacro_cmd_pad,
+				      EMC_PMACRO_CMD_PAD_TX_CTRL,
+				      (100000 / clk) + 1);
+			ramp_up_wait += 100000;
+
+			pmacro_dq_pad |=
+				EMC_PMACRO_DATA_PAD_TX_CTRL_DATA_DQSP_TX_E_DCC |
+				EMC_PMACRO_DATA_PAD_TX_CTRL_DATA_DQSN_TX_E_DCC;
+			pmacro_dq_pad &=
+			       ~(EMC_PMACRO_DATA_PAD_TX_CTRL_DATA_DQ_TX_E_DCC |
+				 EMC_PMACRO_DATA_PAD_TX_CTRL_DATA_CMD_TX_E_DCC);
+			ccfifo_writel(emc, pmacro_dq_pad,
+				      EMC_PMACRO_DATA_PAD_TX_CTRL, 0);
+			ccfifo_writel(emc, pmacro_rfu1 & 0xfe40fe40,
+				      EMC_PMACRO_BRICK_CTRL_RFU1, 0);
+		} else {
+			ccfifo_writel(emc, pmacro_rfu1 & 0xfe40fe40,
+				      EMC_PMACRO_BRICK_CTRL_RFU1,
+				      (100000 / clk) + 1);
+			ramp_up_wait += 100000;
+		}
+
+		ccfifo_writel(emc, pmacro_rfu1 & 0xfeedfeed,
+			      EMC_PMACRO_BRICK_CTRL_RFU1, (100000 / clk) + 1);
+		ramp_up_wait += 100000;
+
+		if (clk < 1000000 / IOBRICK_DCC_THRESHOLD) {
+			pmacro_cmd_pad |=
+				EMC_PMACRO_CMD_PAD_TX_CTRL_CMD_DQSP_TX_E_DCC |
+				EMC_PMACRO_CMD_PAD_TX_CTRL_CMD_DQSN_TX_E_DCC |
+				EMC_PMACRO_CMD_PAD_TX_CTRL_CMD_DQ_TX_E_DCC |
+				EMC_PMACRO_CMD_PAD_TX_CTRL_CMD_CMD_TX_E_DCC;
+			ccfifo_writel(emc, pmacro_cmd_pad,
+				      EMC_PMACRO_CMD_PAD_TX_CTRL,
+				      (100000 / clk) + 1);
+			ramp_up_wait += 100000;
+
+			pmacro_dq_pad |=
+				EMC_PMACRO_DATA_PAD_TX_CTRL_DATA_DQSP_TX_E_DCC |
+				EMC_PMACRO_DATA_PAD_TX_CTRL_DATA_DQSN_TX_E_DCC |
+				EMC_PMACRO_DATA_PAD_TX_CTRL_DATA_DQ_TX_E_DCC |
+				EMC_PMACRO_DATA_PAD_TX_CTRL_DATA_CMD_TX_E_DCC;
+			ccfifo_writel(emc, pmacro_dq_pad,
+				      EMC_PMACRO_DATA_PAD_TX_CTRL, 0);
+			ccfifo_writel(emc, pmacro_rfu1,
+				      EMC_PMACRO_BRICK_CTRL_RFU1, 0);
+		} else {
+			ccfifo_writel(emc, pmacro_rfu1,
+				      EMC_PMACRO_BRICK_CTRL_RFU1,
+				      (100000 / clk) + 1);
+			ramp_up_wait += 100000;
+		}
+
+		ccfifo_writel(emc, pmacro_cfg5 & ~EMC_FBIO_CFG5_CMD_TX_DIS,
+			      EMC_FBIO_CFG5, (100000 / clk) + 10);
+		ramp_up_wait += 100000 + (10 * clk);
+	} else if (clk < 1000000 / DVFS_FGCG_MID_SPEED_THRESHOLD) {
+		ccfifo_writel(emc, pmacro_rfu1 | 0x06000600,
+			      EMC_PMACRO_BRICK_CTRL_RFU1, (100000 / clk) + 1);
+		ccfifo_writel(emc, pmacro_cfg5 & ~EMC_FBIO_CFG5_CMD_TX_DIS,
+			      EMC_FBIO_CFG5, (100000 / clk) + 10);
+		ramp_up_wait += 100000 + 10 * clk;
+	} else {
+		ccfifo_writel(emc, pmacro_rfu1 | 0x00000600,
+			      EMC_PMACRO_BRICK_CTRL_RFU1, 0);
+		ccfifo_writel(emc, pmacro_cfg5 & ~EMC_FBIO_CFG5_CMD_TX_DIS,
+			      EMC_FBIO_CFG5, 12);
+		ramp_up_wait += 12 * clk;
+	}
+
+	pmacro_cmd_pad &= ~EMC_PMACRO_CMD_PAD_TX_CTRL_CMD_DQ_TX_DRVFORCEON;
+	ccfifo_writel(emc, pmacro_cmd_pad, EMC_PMACRO_CMD_PAD_TX_CTRL, 5);
+
+	return ramp_up_wait;
+}
+
+u32 tegra210_dvfs_power_ramp_down(struct tegra_emc *emc, u32 clk,
+				  int flip_backward)
+{
+	u32 ramp_down_wait = 0;
+	u32 pmacro_cmd_pad;
+	u32 pmacro_dq_pad;
+	u32 pmacro_rfu1;
+	u32 pmacro_cfg5;
+	u32 pmacro_common_tx;
+	u32 seq_wait;
+
+	if (flip_backward) {
+		pmacro_cmd_pad   = emc->next_timing->
+			burst_regs[EMC_PMACRO_CMD_PAD_TX_CTRL_INDEX];
+		pmacro_dq_pad    = emc->next_timing->
+			burst_regs[EMC_PMACRO_DATA_PAD_TX_CTRL_INDEX];
+		pmacro_rfu1      = emc->next_timing->
+			burst_regs[EMC_PMACRO_BRICK_CTRL_RFU1_INDEX];
+		pmacro_cfg5      = emc->next_timing->
+			burst_regs[EMC_FBIO_CFG5_INDEX];
+		pmacro_common_tx = emc->next_timing->
+			burst_regs[EMC_PMACRO_COMMON_PAD_TX_CTRL_INDEX];
+	} else {
+		pmacro_cmd_pad   = emc->current_timing->
+			burst_regs[EMC_PMACRO_CMD_PAD_TX_CTRL_INDEX];
+		pmacro_dq_pad    = emc->current_timing->
+			burst_regs[EMC_PMACRO_DATA_PAD_TX_CTRL_INDEX];
+		pmacro_rfu1      = emc->current_timing->
+			burst_regs[EMC_PMACRO_BRICK_CTRL_RFU1_INDEX];
+		pmacro_cfg5      = emc->current_timing->
+			burst_regs[EMC_FBIO_CFG5_INDEX];
+		pmacro_common_tx = emc->current_timing->
+			burst_regs[EMC_PMACRO_COMMON_PAD_TX_CTRL_INDEX];
+	}
+
+	pmacro_cmd_pad |= EMC_PMACRO_CMD_PAD_TX_CTRL_CMD_DQ_TX_DRVFORCEON;
+
+	ccfifo_writel(emc, pmacro_cmd_pad, EMC_PMACRO_CMD_PAD_TX_CTRL, 0);
+	ccfifo_writel(emc, pmacro_cfg5 | EMC_FBIO_CFG5_CMD_TX_DIS,
+			EMC_FBIO_CFG5,
+		      12);
+	ramp_down_wait = 12 * clk;
+
+	seq_wait = (100000 / clk) + 1;
+
+	if (clk < (1000000 / DVFS_FGCG_HIGH_SPEED_THRESHOLD)) {
+		if (clk < (1000000 / IOBRICK_DCC_THRESHOLD)) {
+			pmacro_cmd_pad &=
+				~(EMC_PMACRO_CMD_PAD_TX_CTRL_CMD_DQ_TX_E_DCC |
+				  EMC_PMACRO_CMD_PAD_TX_CTRL_CMD_CMD_TX_E_DCC);
+			pmacro_cmd_pad |=
+				EMC_PMACRO_CMD_PAD_TX_CTRL_CMD_DQSP_TX_E_DCC |
+				EMC_PMACRO_CMD_PAD_TX_CTRL_CMD_DQSN_TX_E_DCC;
+			ccfifo_writel(emc, pmacro_cmd_pad,
+				      EMC_PMACRO_CMD_PAD_TX_CTRL, seq_wait);
+			ramp_down_wait += 100000;
+
+			pmacro_dq_pad &=
+			      ~(EMC_PMACRO_DATA_PAD_TX_CTRL_DATA_DQ_TX_E_DCC |
+				EMC_PMACRO_DATA_PAD_TX_CTRL_DATA_CMD_TX_E_DCC);
+			pmacro_dq_pad |=
+				EMC_PMACRO_DATA_PAD_TX_CTRL_DATA_DQSP_TX_E_DCC |
+				EMC_PMACRO_DATA_PAD_TX_CTRL_DATA_DQSN_TX_E_DCC;
+			ccfifo_writel(emc, pmacro_dq_pad,
+				      EMC_PMACRO_DATA_PAD_TX_CTRL, 0);
+			ccfifo_writel(emc, pmacro_rfu1 & ~0x01120112,
+				      EMC_PMACRO_BRICK_CTRL_RFU1, 0);
+		} else {
+			ccfifo_writel(emc, pmacro_rfu1 & ~0x01120112,
+				      EMC_PMACRO_BRICK_CTRL_RFU1, seq_wait);
+			ramp_down_wait += 100000;
+		}
+
+		ccfifo_writel(emc, pmacro_rfu1 & ~0x01bf01bf,
+			      EMC_PMACRO_BRICK_CTRL_RFU1, seq_wait);
+		ramp_down_wait += 100000;
+
+		if (clk < (1000000 / IOBRICK_DCC_THRESHOLD)) {
+			pmacro_cmd_pad &=
+				~(EMC_PMACRO_CMD_PAD_TX_CTRL_CMD_DQ_TX_E_DCC |
+				  EMC_PMACRO_CMD_PAD_TX_CTRL_CMD_CMD_TX_E_DCC |
+				  EMC_PMACRO_CMD_PAD_TX_CTRL_CMD_DQSP_TX_E_DCC |
+				  EMC_PMACRO_CMD_PAD_TX_CTRL_CMD_DQSN_TX_E_DCC);
+			ccfifo_writel(emc, pmacro_cmd_pad,
+				      EMC_PMACRO_CMD_PAD_TX_CTRL, seq_wait);
+			ramp_down_wait += 100000;
+
+			pmacro_dq_pad &=
+			      ~(EMC_PMACRO_DATA_PAD_TX_CTRL_DATA_DQ_TX_E_DCC |
+				EMC_PMACRO_DATA_PAD_TX_CTRL_DATA_CMD_TX_E_DCC |
+				EMC_PMACRO_DATA_PAD_TX_CTRL_DATA_DQSP_TX_E_DCC |
+				EMC_PMACRO_DATA_PAD_TX_CTRL_DATA_DQSN_TX_E_DCC);
+			ccfifo_writel(emc, pmacro_dq_pad,
+				      EMC_PMACRO_DATA_PAD_TX_CTRL, 0);
+			ccfifo_writel(emc, pmacro_rfu1 & ~0x07ff07ff,
+				      EMC_PMACRO_BRICK_CTRL_RFU1, 0);
+		} else {
+			ccfifo_writel(emc, pmacro_rfu1 & ~0x07ff07ff,
+				      EMC_PMACRO_BRICK_CTRL_RFU1, seq_wait);
+			ramp_down_wait += 100000;
+		}
+	} else {
+		ccfifo_writel(emc, pmacro_rfu1 & ~0xffff07ff,
+			      EMC_PMACRO_BRICK_CTRL_RFU1, seq_wait + 19);
+		ramp_down_wait += 100000 + (20 * clk);
+	}
+
+	if (clk < (1000000 / DVFS_FGCG_MID_SPEED_THRESHOLD)) {
+		ramp_down_wait += 100000;
+		ccfifo_writel(emc, pmacro_common_tx & ~0x5,
+			      EMC_PMACRO_COMMON_PAD_TX_CTRL, seq_wait);
+		ramp_down_wait += 100000;
+		ccfifo_writel(emc, pmacro_common_tx & ~0xf,
+			      EMC_PMACRO_COMMON_PAD_TX_CTRL, seq_wait);
+		ramp_down_wait += 100000;
+		ccfifo_writel(emc, 0, 0, seq_wait);
+		ramp_down_wait += 100000;
+	} else {
+		ccfifo_writel(emc, pmacro_common_tx & ~0xf,
+			      EMC_PMACRO_COMMON_PAD_TX_CTRL, seq_wait);
+	}
+
+	return ramp_down_wait;
+}
+
+void tegra210_reset_dram_clktree_values(struct emc_timing *table)
+{
+	#define __RESET_CLKTREE(TBL, C, D, U)				\
+	TBL->current_dram_clktree_c ## C ## d ## D ## u ## U =		\
+	TBL->trained_dram_clktree_c ## C ## d ## D ## u ## U
+
+	__RESET_CLKTREE(table, 0, 0, 0);
+	__RESET_CLKTREE(table, 0, 0, 1);
+	__RESET_CLKTREE(table, 1, 0, 0);
+	__RESET_CLKTREE(table, 1, 0, 1);
+	__RESET_CLKTREE(table, 1, 1, 0);
+	__RESET_CLKTREE(table, 1, 1, 1);
+}
+
+static void update_dll_control(struct tegra_emc *emc, u32 emc_cfg_dig_dll,
+			       int channel_mode)
+{
+	emc_writel(emc, emc_cfg_dig_dll, EMC_CFG_DIG_DLL);
+	emc_timing_update(emc, channel_mode);
+
+	wait_for_update(emc, EMC_CFG_DIG_DLL, EMC_CFG_DIG_DLL_CFG_DLL_EN,
+			0, REG_EMC);
+	if (channel_mode == DUAL_CHANNEL)
+		wait_for_update(emc, EMC_CFG_DIG_DLL,
+				EMC_CFG_DIG_DLL_CFG_DLL_EN, 0, REG_EMC1);
+}
+
+void tegra210_dll_disable(struct tegra_emc *emc, int channel_mode)
+{
+	u32 emc_cfg_dig_dll;
+
+	emc_cfg_dig_dll = emc_readl(emc, EMC_CFG_DIG_DLL);
+	emc_cfg_dig_dll &= ~EMC_CFG_DIG_DLL_CFG_DLL_EN;
+
+	update_dll_control(emc, emc_cfg_dig_dll, channel_mode);
+}
+
+void tegra210_dll_enable(struct tegra_emc *emc, int channel_mode)
+{
+	u32 emc_cfg_dig_dll;
+
+	emc_cfg_dig_dll = emc_readl(emc, EMC_CFG_DIG_DLL);
+	emc_cfg_dig_dll |= EMC_CFG_DIG_DLL_CFG_DLL_EN;
+
+	update_dll_control(emc, emc_cfg_dig_dll, channel_mode);
+}
+
+#if 0
+void tegra210_emc_timing_invalidate(void)
+{
+	emc_timing = NULL;
+}
+EXPORT_SYMBOL(tegra210_emc_timing_invalidate);
+
+unsigned int tegra210_emc_get_clk_latency(unsigned long rate)
+{
+	int i, index = -1;
+
+	if (!tegra_emc_init_done || !tegra_emc_table_size)
+		return TEGRA_EMC_DEFAULT_CLK_LATENCY_US;
+
+	rate /= 1000;
+	for (i = 0; i < < tegra_emc_table_size; i++) {
+		if (tegra_emc_table[i].rate > rate)
+			break;
+
+		index = i;
+	}
+
+	if (index > 0 && tegra_emc_table[index].latency)
+		return tegra_emc_table[index].latency;
+
+	return TEGRA_EMC_DEFAULT_CLK_LATENCY_US;
+}
+EXPORT_SYMBOL(tegra210_emc_get_clk_latency);
+#endif
+
+static void emc_last_stats_update(int last_sel)
+{
+	unsigned long flags;
+	u64 cur_jiffies = get_jiffies_64();
+
+	spin_lock_irqsave(&tegra_emc_stats.spinlock, flags);
+
+	if (tegra_emc_stats.last_sel < TEGRA_EMC_TABLE_MAX_SIZE)
+		tegra_emc_stats.time_at_clock[tegra_emc_stats.last_sel] =
+			tegra_emc_stats.time_at_clock[tegra_emc_stats.last_sel]
+			+ (cur_jiffies - tegra_emc_stats.last_update);
+
+	tegra_emc_stats.last_update = cur_jiffies;
+
+	if (last_sel < TEGRA_EMC_TABLE_MAX_SIZE) {
+		tegra_emc_stats.clkchange_count++;
+		tegra_emc_stats.last_sel = last_sel;
+	}
+	spin_unlock_irqrestore(&tegra_emc_stats.spinlock, flags);
+}
+
+static struct clk *tegra210_emc_predict_parent(struct emc_timing *timing,
+					       struct clk_hw *hw,
+					       u32 *emc_clk_src_val)
+{
+	int err;
+	u32 src_value, div_value;
+	struct clk *old_parent, *new_parent;
+	unsigned long new_rate, parent_rate;
+	struct tegra_emc *emc = to_emc(hw);
+
+	*emc_clk_src_val = timing->clk_src_emc;
+	div_value = emc_div_val(*emc_clk_src_val);
+	new_rate = (timing->rate * (1 + div_value / 2)) * 1000;
+
+	src_value = emc_src_val(*emc_clk_src_val);
+	new_parent = emc_clks[src_value].clk;
+	if (src_value == TEGRA_EMC_SRC_PLLM ||
+	    src_value == TEGRA_EMC_SRC_PLLM_UD)
+		parent_rate = (timing->rate * (1 + div_value / 2)) * 1000;
+	else
+		parent_rate = clk_get_rate(new_parent);
+
+	if (parent_rate != new_rate) {
+		pr_warn("Tegra EMC: rate %u doesn't match input\n",
+			timing->rate);
+		return ERR_PTR(-EINVAL);
+	}
+
+	old_parent = emc_clks[emc_src_val(emc->clk_setting)].clk;
+	if (parent_rate == clk_get_rate(old_parent))
+		return old_parent;
+
+	if (clk_is_match(new_parent, old_parent)) {
+		switch (src_value) {
+		case TEGRA_EMC_SRC_PLLM:
+			new_parent = emc_clks[TEGRA_EMC_SRC_PLLMB].clk;
+			*emc_clk_src_val &= ~EMC_CLK_EMC_2X_CLK_SRC_MASK;
+			*emc_clk_src_val |=
+			TEGRA_EMC_SRC_PLLMB << EMC_CLK_EMC_2X_CLK_SRC_SHIFT;
+			break;
+		case TEGRA_EMC_SRC_PLLM_UD:
+			new_parent = emc_clks[TEGRA_EMC_SRC_PLLMB_UD].clk;
+			*emc_clk_src_val &= ~EMC_CLK_EMC_2X_CLK_SRC_MASK;
+			*emc_clk_src_val |=
+			TEGRA_EMC_SRC_PLLMB_UD << EMC_CLK_EMC_2X_CLK_SRC_SHIFT;
+			break;
+		default:
+			return ERR_PTR(-EINVAL);
+		}
+	}
+
+	if (parent_rate == clk_get_rate(new_parent)) {
+		err = clk_set_rate(new_parent, parent_rate);
+		if (err)
+			return ERR_PTR(err);
+	}
+
+	return new_parent;
+}
+
+static int clk_emc_set_rate(struct clk_hw *hw, unsigned long rate,
+			    unsigned long parent_rate)
+{
+	int i;
+	unsigned long flags;
+	s64 last_change_delay;
+	struct clk *parent;
+	struct tegra_emc *emc = to_emc(hw);
+
+	if (emc_force_max_rate)
+		rate = emc->emc_table[emc->num_timings - 1].rate * 1000;
+
+	rate /= 1000;
+	if (rate == emc->current_timing->rate)
+		return 0;
+
+	i = emc_table_lookup(emc, rate / 1000);
+
+	if (i < 0)
+		return i;
+
+	if (rate > 204000000 && !emc->emc_table[i].trained)
+		return -EINVAL;
+
+	parent = tegra210_emc_predict_parent(&emc->emc_table[i], hw,
+					     &emc->clk_setting);
+	if (IS_ERR(parent))
+		return PTR_ERR(parent);
+
+	emc->next_timing = &emc->emc_table[i];
+	last_change_delay = ktime_us_delta(ktime_get(), clkchange_time);
+	if ((last_change_delay >= 0) && (last_change_delay < clkchange_delay))
+		udelay(clkchange_delay - (int)last_change_delay);
+
+	spin_lock_irqsave(&emc_access_lock, flags);
+	emc_set_clock(emc, parent);
+	clkchange_time = ktime_get();
+	emc->current_timing = &emc->emc_table[i];
+	last_rate_idx = i;
+	spin_unlock_irqrestore(&emc_access_lock, flags);
+
+	clk_hw_reparent(hw, __clk_get_hw(parent));
+
+	emc_last_stats_update(i);
+
+	return 0;
+}
+
+static inline int bw_calc_get_freq_idx(unsigned long bw)
+{
+	int max_idx = ARRAY_SIZE(bw_calc_freqs) - 1;
+	int idx = (bw > bw_calc_freqs[max_idx] * 1000000) ? max_idx : 0;
+
+	for (; idx < max_idx; idx++) {
+		u32 freq = bw_calc_freqs[idx] * 1000000;
+		if (bw < freq) {
+			if (idx)
+				idx--;
+			break;
+		} else if (bw == freq)
+			break;
+	}
+	return idx;
+}
+
+static u8 clk_emc_get_parent(struct clk_hw *hw)
+{
+	struct tegra_emc *emc = to_emc(hw);
+
+	return emc_src_val(emc->clk_setting);
+}
+
+#if 0
+static u8 iso_share_calc_tegra210_os_idle(unsigned long iso_bw)
+{
+	int freq_idx = bw_calc_get_freq_idx(iso_bw);
+	u8 ret = 60;
+
+	switch (tegra_dram_type) {
+	case DRAM_TYPE_DDR3:
+		ret = tegra210_ddr3_iso_efficiency_os_idle[freq_idx];
+		break;
+	case DRAM_TYPE_LPDDR4:
+		ret = tegra210_lpddr4_iso_efficiency_os_idle[freq_idx];
+		break;
+	case DRAM_TYPE_LPDDR2:
+		ret = tegra210_lpddr3_iso_efficiency_os_idle[freq_idx];
+		break;
+	}
+
+	return ret;
+}
+
+static u8 iso_share_calc_tegra210_general(unsigned long iso_bw)
+{
+	int freq_idx = bw_calc_get_freq_idx(iso_bw);
+	u8 ret = 60;
+
+	switch (tegra_dram_type) {
+	case DRAM_TYPE_DDR3:
+		ret = tegra210_ddr3_iso_efficiency_general[freq_idx];
+		break;
+	case DRAM_TYPE_LPDDR4:
+		ret = tegra210_lpddr4_iso_efficiency_general[freq_idx];
+		break;
+	case DRAM_TYPE_LPDDR2:
+		ret = tegra210_lpddr3_iso_efficiency_general[freq_idx];
+		break;
+	}
+
+	return ret;
+}
+#endif
+
+void set_over_temp_timing(struct tegra_emc *emc, struct emc_timing *timing,
+			  unsigned long state)
+{
+#define REFRESH_X2      1
+#define REFRESH_X4      2
+#define REFRESH_SPEEDUP(val, speedup)					\
+		(val = ((val) & 0xFFFF0000) | (((val) & 0xFFFF) >> (speedup)))
+
+	u32 ref = timing->burst_regs[EMC_REFRESH_INDEX];
+	u32 pre_ref = timing->burst_regs[EMC_PRE_REFRESH_REQ_CNT_INDEX];
+	u32 dsr_cntrl = timing->burst_regs[EMC_DYN_SELF_REF_CONTROL_INDEX];
+
+	switch (state) {
+	case TEGRA_DRAM_OVER_TEMP_NONE:
+	case TEGRA_DRAM_OVER_TEMP_THROTTLE:
+		break;
+	case TEGRA_DRAM_OVER_TEMP_REFRESH_X2:
+		REFRESH_SPEEDUP(ref, REFRESH_X2);
+		REFRESH_SPEEDUP(pre_ref, REFRESH_X2);
+		REFRESH_SPEEDUP(dsr_cntrl, REFRESH_X2);
+		break;
+	case TEGRA_DRAM_OVER_TEMP_REFRESH_X4:
+		REFRESH_SPEEDUP(ref, REFRESH_X4);
+		REFRESH_SPEEDUP(pre_ref, REFRESH_X4);
+		REFRESH_SPEEDUP(dsr_cntrl, REFRESH_X4);
+		break;
+	default:
+	WARN(1, "%s: Failed to set dram over temp state %lu\n",
+		__func__, state);
+	return;
+	}
+
+	emc_writel(emc, ref, burst_regs_off[EMC_REFRESH_INDEX]);
+	emc_writel(emc, pre_ref, burst_regs_off[EMC_PRE_REFRESH_REQ_CNT_INDEX]);
+	emc_writel(emc, dsr_cntrl, burst_regs_off[EMC_DYN_SELF_REF_CONTROL_INDEX]);
+}
+
+static int emc_read_mrr(struct tegra_emc * emc, int dev, int addr)
+{
+	int ret;
+	u32 val, emc_cfg;
+
+	if (emc->dram_type != DRAM_TYPE_LPDDR2 &&
+	    emc->dram_type != DRAM_TYPE_LPDDR4)
+		return -ENODEV;
+
+	ret = wait_for_update(emc, EMC_EMC_STATUS, EMC_EMC_STATUS_MRR_DIVLD,
+			      false, REG_EMC);
+	if (ret)
+		return ret;
+
+	emc_cfg = emc_readl(emc, EMC_CFG);
+	if (emc_cfg & EMC_CFG_DRAM_ACPD) {
+		emc_writel(emc, emc_cfg & ~EMC_CFG_DRAM_ACPD, EMC_CFG);
+		emc_timing_update(emc, 0);
+	}
+
+	val = dev ? DRAM_DEV_SEL_1 : DRAM_DEV_SEL_0;
+	val |= (addr << EMC_MRR_MA_SHIFT) & EMC_MRR_MA_MASK;
+	emc_writel(emc, val, EMC_MRR);
+
+	ret = wait_for_update(emc, EMC_EMC_STATUS, EMC_EMC_STATUS_MRR_DIVLD,
+			      true, REG_EMC);
+	if (emc_cfg & EMC_CFG_DRAM_ACPD) {
+		emc_writel(emc, emc_cfg, EMC_CFG);
+		emc_timing_update(emc, 0);
+	}
+	if (ret)
+		return ret;
+
+	val = emc_readl(emc, EMC_MRR) & EMC_MRR_DATA_MASK;
+	return val;
+}
+
+static int emc_get_dram_temperature(struct tegra_emc *emc)
+{
+	int mr4,mr4_0, mr4_1;
+	unsigned long flags;
+
+	mr4 = mr4_0 = mr4_1 = 0;
+
+	spin_lock_irqsave(&emc_access_lock, flags);
+	mr4_0 = emc_read_mrr(emc, 0, 4);
+	mr4_1 = emc_read_mrr(emc, 1, 4);
+	spin_unlock_irqrestore(&emc_access_lock, flags);
+
+	if (mr4_0 < 0)
+		return mr4_0;
+
+	if (mr4_1 < 0)
+		return mr4_1;
+
+	mr4_0 = (mr4_0 & LPDDR2_MR4_TEMP_MASK) >> LPDDR2_MR4_TEMP_SHIFT;
+	mr4_1 = (mr4_1 & LPDDR2_MR4_TEMP_MASK) >> LPDDR2_MR4_TEMP_SHIFT;
+
+	/* Consider higher temperature of the two DDR Dies */
+	mr4 = (mr4_0 > mr4_1) ? mr4_0 : mr4_1;
+
+	return mr4;
+}
+
+#if 0
+static int emc_get_dram_temp(void *dev, int *temp)
+{
+	int mr4 = emc_get_dram_temperature();
+
+	if (mr4 >= 0)
+		*temp = mr4;
+
+	return 0;
+}
+
+static const struct thermal_zone_of_device_ops dram_therm_ops = {
+	.get_temp = emc_get_dram_temp,
+};
+#endif
+
+struct emc_timing *emc_get_table(struct tegra_emc *emc,
+				 unsigned long over_temp_state)
+{
+	if ((over_temp_state == TEGRA_DRAM_OVER_TEMP_THROTTLE) &&
+	    (emc->emc_table_derated != NULL))
+		return emc->emc_table_derated;
+	else
+		return emc->emc_table_normal;
+}
+
+#ifdef CONFIG_DEBUG_FS
+static int emc_stats_show(struct seq_file *s, void *data)
+{
+	int i;
+	struct tegra_emc *emc = (struct tegra_emc *)data;
+	u32 src;
+
+	emc_last_stats_update(TEGRA_EMC_TABLE_MAX_SIZE);
+
+	seq_printf(s, "%-10s %-10s\n", "rate kHz", "time");
+	for (i = 0; i < emc->num_timings; i++) {
+		if (IS_ERR(tegra210_emc_predict_parent(emc->emc_table+i,
+			   &emc->hw, &src)))
+			continue;
+
+		seq_printf(s, "%-10u %-10llu\n",
+			   emc->emc_table[i].rate,
+			   tegra_emc_stats.time_at_clock[i]);
+	}
+	seq_printf(s, "%-15s %llu\n", "transitions:",
+		   tegra_emc_stats.clkchange_count);
+	seq_printf(s, "%-15s %llu\n", "time-stamp:",
+		   tegra_emc_stats.last_update);
+
+	return 0;
+}
+
+static int emc_stats_open(struct inode *inode, struct file *file)
+{
+	return single_open(file, emc_stats_show, inode->i_private);
+}
+
+static const struct file_operations emc_stats_fops = {
+	.open		= emc_stats_open,
+	.read		= seq_read,
+	.llseek		= seq_lseek,
+	.release	= single_release,
+};
+
+static int efficiency_get(void *data, u64 *val)
+{
+	*val = tegra210_emc_bw_efficiency;
+	return 0;
+}
+
+static int efficiency_set(void *data, u64 val)
+{
+	tegra210_emc_bw_efficiency = (val > 100) ? 100 : val;
+	return 0;
+}
+DEFINE_SIMPLE_ATTRIBUTE(efficiency_fops, efficiency_get,
+			efficiency_set, "%llu\n");
+
+#if 0
+static const char *emc_user_names[] = {
+	"DC1",
+	"DC2",
+	"VI",
+	"MSENC",
+	"2D",
+	"3D",
+	"BB",
+	"VDE",
+	"VI2",
+	"ISPA",
+	"ISPB",
+	"NVDEC",
+	"NVJPG",
+};
+
+static int emc_usage_table_show(struct seq_file *s, void *data)
+{
+	int i, j;
+
+	seq_printf(s, "EMC USAGE\t\tISO SHARE %% @ last bw %lu\n", last_iso_bw);
+
+	for (i = 0; i < ARRAY_SIZE(tegra210_emc_iso_usage); i++) {
+		u32 flags = tegra210_emc_iso_usage[i].emc_usage_flags;
+		u8 share = tegra210_emc_iso_usage[i].iso_usage_share;
+		bool fixed_share = true;
+		bool first = false;
+
+		if (tegra210_emc_iso_usage[i].iso_share_calculator) {
+			share = tegra210_emc_iso_usage[i].iso_share_calculator(
+				last_iso_bw);
+			fixed_share = false;
+		}
+
+		seq_printf(s, "[%d]: ", i);
+		if (!flags) {
+			seq_puts(s, "reserved\n");
+			continue;
+		}
+
+		for (j = 0; j < EMC_USER_NUM; j++) {
+			u32 mask = 0x1 << j;
+			if (!(flags & mask))
+				continue;
+			seq_printf(s, "%s%s", first ? "+" : "",
+				   emc_user_names[j]);
+			first = true;
+		}
+		seq_printf(s, "\r\t\t\t= %d(%s across bw)\n",
+			   share, fixed_share ? "fixed" : "vary");
+	}
+	return 0;
+}
+
+static int emc_usage_table_open(struct inode *inode, struct file *file)
+{
+	return single_open(file, emc_usage_table_show, inode->i_private);
+}
+
+static const struct file_operations emc_usage_table_fops = {
+	.open		= emc_usage_table_open,
+	.read		= seq_read,
+	.llseek		= seq_lseek,
+	.release	= single_release,
+};
+#endif
+
+static int dram_temp_get(void *data, u64 *val)
+{
+	int temp = 0;
+	struct tegra_emc *emc = (struct tegra_emc *)data;
+
+	temp = emc_get_dram_temperature(emc);
+	if (temp < 0)
+		return temp;
+	else
+		*val = temp;
+
+	return 0;
+}
+DEFINE_SIMPLE_ATTRIBUTE(dram_temp_fops, dram_temp_get, NULL,
+	"%lld\n");
+
+static int over_temp_state_get(void *data, u64 *val)
+{
+	*val = dram_over_temp_state;
+	return 0;
+}
+
+static int over_temp_state_set(void *data, u64 val)
+{
+	struct tegra_emc *emc = (struct tegra_emc *)data;
+
+	return tegra210_emc_set_over_temp_state(emc, val);
+}
+DEFINE_SIMPLE_ATTRIBUTE(over_temp_state_fops, over_temp_state_get,
+			over_temp_state_set, "%llu\n");
+
+static int get_mr4_force_poll(void *data, u64 *val)
+{
+	*val = atomic_read(&mr4_force_poll);
+
+	return 0;
+}
+static int set_mr4_force_poll(void *data, u64 val)
+{
+	atomic_set(&mr4_force_poll, (unsigned int)val);
+
+	/* Explicitly wake up the DRAM monitoring thread. */
+	if (atomic_read(&mr4_force_poll))
+		mod_timer(&emc_timer_mr4,
+			  jiffies + msecs_to_jiffies(timer_period_mr4));
+
+	return 0;
+}
+DEFINE_SIMPLE_ATTRIBUTE(mr4_force_poll_fops,
+			get_mr4_force_poll,
+			set_mr4_force_poll, "%llu\n");
+
+static int tegra_emc_debug_init(struct tegra_emc *emc)
+{
+	struct dentry *emc_debugfs_root;
+	struct dentry *dram_therm_debugfs;
+
+	if (!tegra_emc_init_done)
+		return -ENODEV;
+
+	emc_debugfs_root = debugfs_create_dir("tegra_emc", NULL);
+	if (!emc_debugfs_root)
+		return -ENOMEM;
+
+	dram_therm_debugfs = debugfs_create_dir("dram_therm",
+						emc_debugfs_root);
+	if (!dram_therm_debugfs)
+		goto err_out;
+
+	if (!debugfs_create_file("stats", S_IRUGO, emc_debugfs_root, NULL,
+				 &emc_stats_fops))
+		goto err_out;
+
+	if (!debugfs_create_u32("clkchange_delay", S_IRUGO | S_IWUSR,
+				emc_debugfs_root, (u32 *)&clkchange_delay))
+		goto err_out;
+
+	if (!debugfs_create_file("efficiency", S_IRUGO | S_IWUSR,
+				 emc_debugfs_root, NULL, &efficiency_fops))
+		goto err_out;
+
+#if 0
+	if (!debugfs_create_file("emc_usage_table", S_IRUGO, emc_debugfs_root,
+				 NULL, &emc_usage_table_fops))
+		goto err_out;
+#endif
+	if (!debugfs_create_u8("emc_iso_share", S_IRUGO, emc_debugfs_root,
+			       &tegra210_emc_iso_share))
+		goto err_out;
+
+	if (emc->dram_type == DRAM_TYPE_LPDDR2 ||
+		emc->dram_type == DRAM_TYPE_LPDDR4) {
+		if (!debugfs_create_file("dram_temp", S_IRUGO,
+					 emc_debugfs_root, NULL,
+					 &dram_temp_fops))
+			goto err_out;
+		if (!debugfs_create_file("over_temp_state", S_IRUGO | S_IWUSR,
+					 emc_debugfs_root, NULL,
+					 &over_temp_state_fops))
+			goto err_out;
+	}
+
+	/* DRAM thermals. */
+	if (!debugfs_create_u32("timer_period", S_IRUGO | S_IWUSR,
+				dram_therm_debugfs, &timer_period_mr4))
+		goto err_out;
+	if (!debugfs_create_u32("test_mode", S_IRUGO | S_IWUSR,
+				dram_therm_debugfs, &test_mode))
+		goto err_out;
+	if (!debugfs_create_u32("dram_temp_override", S_IRUGO | S_IWUSR,
+				dram_therm_debugfs, &dram_temp_override))
+		goto err_out;
+	if (!debugfs_create_file("force_poll", S_IRUGO | S_IWUSR,
+				 dram_therm_debugfs, NULL,
+				 &mr4_force_poll_fops))
+		goto err_out;
+
+	if (emc->dram_type == DRAM_TYPE_LPDDR4) {
+		if (!debugfs_create_u32("training_timer_period",
+					S_IRUGO | S_IWUSR, emc_debugfs_root,
+					&timer_period_training))
+			goto err_out;
+	}
+
+	return 0;
+
+err_out:
+	debugfs_remove_recursive(emc_debugfs_root);
+	return -ENOMEM;
+}
+
+#endif
+
+static const struct of_device_id mc_match[] = {
+	{ .compatible = "nvidia,tegra210-mc" },
+	{},
+};
+
+static const struct of_device_id car_match[] = {
+	{ .compatible = "nvidia,tegra210-car" },
+	{},
+};
+
+static void emc_copy_table_params(struct emc_timing *src,
+				  struct emc_timing *dst,
+				  int table_size,
+				  int flags)
+{
+	int i;
+
+	for (i = 0; i < table_size; i++)
+		__emc_copy_table_params(&src[i], &dst[i], flags);
+}
+
+static unsigned long clk_emc_recalc_rate(struct clk_hw *hw,
+					 unsigned long parent_rate)
+{
+	struct tegra_emc *emc = to_emc(hw);
+
+	return emc->current_timing->rate * 1000;
+}
+
+static long clk_emc_round_rate(struct clk_hw *hw, unsigned long rate,
+			       unsigned long *prate)
+{
+	struct tegra_emc *emc = to_emc(hw);
+	int i;
+
+	rate /= 1000;
+
+	for (i = 0; i < emc->num_timings; i++) {
+		if (emc->emc_table[i].rate >= rate)
+			return emc->emc_table[i].rate;
+	}
+
+	return emc->emc_table[i - 1].rate;
+}
+
+
+static const struct clk_ops tegra_clk_emc_ops = {
+	.get_parent = clk_emc_get_parent,
+	.recalc_rate = clk_emc_recalc_rate,
+	.round_rate = clk_emc_round_rate,
+	.set_rate = clk_emc_set_rate,
+};
+
+static int tegra210_init_emc_data(struct platform_device *pdev)
+{
+	int i, err, div;
+	unsigned long table_rate;
+	unsigned long current_rate;
+	struct device_node *np;
+	struct platform_device *mc;
+	struct tegra_emc *emc; 
+	struct clk_init_data init;
+	struct clk *clk;
+	struct resource *r;
+	u32 val;
+
+	emc = kzalloc(sizeof(*emc), GFP_KERNEL);
+	if (!emc)
+		return -ENOMEM;
+
+	np = of_parse_phandle(pdev->dev.of_node, "nvidia,memory-controller", 0);
+	if (!np) {
+		dev_err(&pdev->dev, "could not get memory controller\n");
+		kfree(emc);
+		return -ENOENT;
+	}
+
+	mc = of_find_device_by_node(np);
+	of_node_put(np);
+	if (!mc) {
+		kfree(emc);
+		return -ENOENT;
+	}
+
+	emc->mc = platform_get_drvdata(mc);
+	if (!emc->mc) {
+		kfree(emc);
+		return -EPROBE_DEFER;
+	}
+
+	emc->ram_code = tegra_read_ram_code();
+	r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	emc->emc_base = devm_ioremap_resource(&pdev->dev, r);
+	r = platform_get_resource(pdev, IORESOURCE_MEM, 1);
+	emc->emc0_base = devm_ioremap_resource(&pdev->dev, r);
+	r = platform_get_resource(pdev, IORESOURCE_MEM, 2);
+	emc->emc1_base = devm_ioremap_resource(&pdev->dev, r);
+
+	emc->dram_type = (emc_readl(emc, EMC_FBIO_CFG5) &
+			   EMC_FBIO_CFG5_DRAM_TYPE_MASK) >>
+			   EMC_FBIO_CFG5_DRAM_TYPE_SHIFT;
+	if (emc->dram_type != DRAM_TYPE_DDR3 &&
+	    emc->dram_type != DRAM_TYPE_LPDDR2 &&
+	    emc->dram_type != DRAM_TYPE_LPDDR4) {
+		dev_err(&pdev->dev, "DRAM not supported\n");
+		kfree(emc);
+		return -ENODATA;
+	}
+
+	emc->dram_dev_num = tegra_mc_get_emem_device_count(emc->mc);
+
+	tegra_emc_dt_parse_pdata(pdev, &emc->emc_table_normal,
+			&emc->emc_table_derated, &emc->num_timings);
+	if (!emc->num_timings ||
+	    emc->num_timings > TEGRA_EMC_TABLE_MAX_SIZE) {
+		dev_err(&pdev->dev, "Invalid table size %d\n",
+			emc->num_timings);
+		kfree(emc);
+		return -EINVAL;
+	}
+	emc->emc_table = emc->emc_table_normal;
+
+	/*
+	 * Copy trained trimmers from the normal table to the derated
+	 * table for LP4. Bootloader trains only the normal table.
+	 * Trimmers are the same for derated and normal tables.
+	 */
+	if (emc->emc_table_derated && emc->dram_type == DRAM_TYPE_LPDDR4)
+		emc_copy_table_params(emc->emc_table,
+				      emc->emc_table_derated,
+				      emc->num_timings,
+				      EMC_COPY_TABLE_PARAM_PERIODIC_FIELDS |
+				      EMC_COPY_TABLE_PARAM_TRIM_REGS);
+
+	seq = supported_seqs;
+	while (seq->table_rev) {
+		if (seq->table_rev == emc->emc_table[0].rev)
+			break;
+		seq++;
+	}
+	if (!seq->set_clock) {
+		seq = NULL;
+		dev_err(&pdev->dev, "Invalid EMC sequence for table Rev. %d\n",
+			emc->emc_table[0].rev);
+		kfree(emc);
+		return -EINVAL;
+	}
+
+	err = clk_bulk_get(&pdev->dev, ARRAY_SIZE(emc_clks), emc_clks);
+	if (err < 0) {
+		kfree(emc);
+		return -EINVAL;
+	}
+
+	val = tegra210_clk_emc_get_setting();
+	current_rate = clk_get_rate(emc_clks[emc_src_val(val)].clk);
+	div = emc_div_val(val);
+	div += 2;
+	current_rate *= 2;
+	current_rate += div - 1;
+	do_div(current_rate, div);
+	current_rate /=  1000;
+
+	for (i = 0; i < emc->num_timings; i++) {
+		table_rate = emc->emc_table[i].rate;
+		if (!table_rate)
+			continue;
+
+		if (emc_max_rate && table_rate > emc_max_rate)
+			break;
+
+		if (i && ((table_rate <= emc->emc_table[i-1].rate) ||
+		   (emc->emc_table[i].min_volt <
+		    emc->emc_table[i-1].min_volt)))
+			continue;
+
+		if (emc->emc_table[i].rev != emc->emc_table[0].rev)
+			continue;
+
+		if (IS_ERR(tegra210_emc_predict_parent(emc->emc_table+i,
+			    &emc->hw, &val)))
+			continue;
+
+		if (table_rate == current_rate)
+			tegra_emc_stats.last_sel = i;
+	}
+
+	dev_info(&pdev->dev, "validated EMC DFS table\n");
+
+	emc->start_timing.num_burst = emc->emc_table[0].num_burst;
+	emc->start_timing.num_burst_per_ch =
+		emc->emc_table[0].num_burst_per_ch;
+	emc->start_timing.num_trim = emc->emc_table[0].num_trim;
+	emc->start_timing.num_trim_per_ch =
+		emc->emc_table[0].num_trim_per_ch;
+	emc->start_timing.num_mc_regs = emc->emc_table[0].num_mc_regs;
+	emc->start_timing.num_up_down = emc->emc_table[0].num_up_down;
+	emc->start_timing.vref_num = emc->emc_table[0].vref_num;
+
+	init.name = "emc";
+	init.ops = &tegra_clk_emc_ops;
+	init.flags = CLK_IS_CRITICAL;
+	init.parent_names = emc_parent_names;
+	init.num_parents = ARRAY_SIZE(emc_parent_names);
+	emc->hw.init = &init;
+
+	emc_get_timing(emc, &emc->start_timing);
+	emc->current_timing = &emc->start_timing;
+
+	clk = clk_register(NULL, &emc->hw);
+	if (IS_ERR(clk)) {
+		kfree(emc);
+		return PTR_ERR(clk);
+	}
+
+#ifdef CONFIG_DEBUG_FS
+	tegra_emc_debug_init(emc);
+#endif
+	return 0;
+}
+
+static int tegra210_emc_probe(struct platform_device *pdev)
+{
+	int ret;
+
+	ret = tegra210_init_emc_data(pdev);
+	if (ret)
+		return ret;
+
+	tegra_emc_init_done = true;
+
+	return 0;
+}
+
+static struct of_device_id tegra210_emc_of_match[] = {
+	{ .compatible = "nvidia,tegra210-emc", },
+	{ },
+};
+
+static struct platform_driver tegra210_emc_driver = {
+	.driver         = {
+		.name   = "tegra210-emc",
+		.of_match_table = tegra210_emc_of_match,
+	},
+	.probe          = tegra210_emc_probe,
+};
+
+static int __init tegra210_emc_init(void)
+{
+	return platform_driver_register(&tegra210_emc_driver);
+}
+subsys_initcall(tegra210_emc_init);
+
+static int __init tegra210_emc_late_init(void)
+{
+	struct device_node *node;
+	struct platform_device *pdev;
+
+	if (!tegra_emc_init_done)
+		return -ENODEV;
+
+	node = of_find_matching_node(NULL, tegra210_emc_of_match);
+	if (!node) {
+		pr_err("Error finding EMC node.\n");
+		return -EINVAL;
+	}
+
+	pdev = of_find_device_by_node(node);
+	if (!pdev) {
+		pr_err("Error finding EMC device.\n");
+		return -EINVAL;
+	}
+
+//	thermal_zone_of_sensor_register(&pdev->dev, 0, NULL, &dram_therm_ops);
+
+	return 0;
+}
+late_initcall(tegra210_emc_late_init);
+
-- 
1.9.1

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

* [RFC 13/14] memory: tegra: enable Tegra210 EMC scaling driver
  2018-09-14 20:02 [RFC 00/14] Tegra210 EMC scaling Peter De Schrijver
                   ` (11 preceding siblings ...)
  2018-09-14 20:03 ` [RFC 12/14] memory: tegra: Tegra210 EMC memory driver Peter De Schrijver
@ 2018-09-14 20:03 ` Peter De Schrijver
  2018-09-14 20:03 ` [RFC 14/14] dt-bindings: tegra: Add Tegra210 EMC binding Peter De Schrijver
  13 siblings, 0 replies; 26+ messages in thread
From: Peter De Schrijver @ 2018-09-14 20:03 UTC (permalink / raw)
  To: vkuruturi, linux-tegra=linux-clk, linux-tegra, thierry.reding,
	jonathanh, devicetree, robh+dt
  Cc: daniel, a.heider, swtcr0, Peter De Schrijver

Signed-off-by: Peter De Schrijver <pdeschrijver@nvidia.com>
---
 drivers/memory/tegra/Kconfig | 10 ++++++++++
 1 file changed, 10 insertions(+)

diff --git a/drivers/memory/tegra/Kconfig b/drivers/memory/tegra/Kconfig
index 6d74e49..456719a 100644
--- a/drivers/memory/tegra/Kconfig
+++ b/drivers/memory/tegra/Kconfig
@@ -15,3 +15,13 @@ config TEGRA124_EMC
 	  Tegra124 chips. The EMC controls the external DRAM on the board.
 	  This driver is required to change memory timings / clock rate for
 	  external memory.
+
+config TEGRA210_EMC
+	bool "NVIDIA Tegra210 External Memory Controller driver"
+	default y
+	depends on TEGRA_MC && ARCH_TEGRA_210_SOC
+	help
+	  This driver is for the External Memory Controller (EMC) found on
+	  Tegra210 chips. The EMC controls the external DRAM on the board.
+	  This driver is required to change memory timings / clock rate for
+	  external memory.
-- 
1.9.1

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

* [RFC 14/14] dt-bindings: tegra: Add Tegra210 EMC binding
  2018-09-14 20:02 [RFC 00/14] Tegra210 EMC scaling Peter De Schrijver
                   ` (12 preceding siblings ...)
  2018-09-14 20:03 ` [RFC 13/14] memory: tegra: enable Tegra210 EMC scaling driver Peter De Schrijver
@ 2018-09-14 20:03 ` Peter De Schrijver
  2018-09-24 21:04   ` Rob Herring
  13 siblings, 1 reply; 26+ messages in thread
From: Peter De Schrijver @ 2018-09-14 20:03 UTC (permalink / raw)
  To: vkuruturi, linux-tegra=linux-clk, linux-tegra, thierry.reding,
	jonathanh, devicetree, robh+dt
  Cc: daniel, a.heider, swtcr0, Peter De Schrijver

Signed-off-by: Peter De Schrijver <pdeschrijver@nvidia.com>
---
 .../memory-controllers/nvidia,tegra210-emc.txt     | 448 +++++++++++++++++++++
 1 file changed, 448 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/memory-controllers/nvidia,tegra210-emc.txt

diff --git a/Documentation/devicetree/bindings/memory-controllers/nvidia,tegra210-emc.txt b/Documentation/devicetree/bindings/memory-controllers/nvidia,tegra210-emc.txt
new file mode 100644
index 0000000..1c52f47
--- /dev/null
+++ b/Documentation/devicetree/bindings/memory-controllers/nvidia,tegra210-emc.txt
@@ -0,0 +1,448 @@
+NVIDIA Tegra210 SoC EMC (external memory controller)
+====================================================
+
+Required properties :
+- compatible : Should be "nvidia,tegra21-emc", "nvidia,tegra124-emc".
+- reg : physical base address and length of the controller's registers.
+- nvidia,memory-controller : phandle of the MC driver.
+- clocks : phandles of the possible source clocks
+- clock-names : names of the possible source clocks
+
+The node should contain a "emc-table" subnode for each supported RAM type
+(see field RAM_CODE in register PMC_STRAPPING_OPT_A), with its unit address
+being its RAM_CODE.
+
+Required properties for "emc-table" nodes :
+- nvidia,ram-code : Should contain the value of RAM_CODE this timing set is
+used for.
+
+Each "emc-table" node should contain a "emc-table" subnode for every supported
+EMC clock rate. The "emc-table" subnodes should have the clock rate in kHz as
+their unit address.
+
+Required properties for "emc-table" nodes :
+- compatible  "nvidia,tegra21-emc-table", "nvidia,tegra210-emc-table"
+- nvidia,revision : revision of the parameter set used for this node. All
+                    nodes in the same "emc-table" should have the same revision
+- clock-frequency : frequency in kHz
+- nvidia,emc-min-mv : minimum voltage for this OPP
+- nvidia,gk20a-min-mv : minimum GPU voltage for this OPP
+- nvidia,source : clock source to be used for this OPP
+- nvidia,src-sel-reg : value of EMC CAR register to be used for this OPP
+- nvidia,needs-training : 1 if the OPP needs training at boot, 0 otherwise
+- nvidia,trained : 1 if initial training has been done by firmware, 0 otherwise
+- nvidia,periodic_training : 1 if the OPP needs periodic training, 0 otherwise
+- nvidia,trained_dram_clktree_c0d0u0 : training data word
+- nvidia,trained_dram_clktree_c0d0u1 : training data word
+- nvidia,trained_dram_clktree_c0d1u0 : training data word
+- nvidia,trained_dram_clktree_c0d1u1 : training data word
+- nvidia,trained_dram_clktree_c1d0u0 : training data word
+- nvidia,trained_dram_clktree_c1d0u1 : training data word
+- nvidia,trained_dram_clktree_c1d1u0 : training data word
+- nvidia,trained_dram_clktree_c1d1u1 : training data word
+- nvidia,run_clocks : training data
+- nvidia,tree_margin : training data
+- nvidia,burst-regs-num : number of values in nvidia,emc-registers
+- nvidia,burst-regs-per-ch-num : number of values in nvidia,emc-burst-regs-per-ch
+- nvidia,trim-regs-num : number of values in nvidia,emc-trim-regs
+- nvidia,trim-regs-per-ch-num : number of values in nvidia,emc-trim-regs-per-ch
+- nvidia,burst-mc-regs-num : number of values in nvidia,emc-burst-mc-regs
+- nvidia,la-scale-regs-num : number of values in nvidia,emc-la-scale-regs
+- nvidia,vref-regs-num : number of values in nvidia,emc-vref-regs
+- nvidia,dram-timing-regs: number of values in nvidia,emc-dram-timing-regs
+- nvidia,min-mrs-wait : value of the EMC_MRW register
+- nvidia,emc-mrw : value of the EMC_MRW register
+- nvidia,emc-mrw2 : value of the EMC_MRW2 register
+- nvidia,emc-mrw3 : used to determine the value of EMC_MRW3
+- nvidia,emc-mrw4 : value of EMC_MRW4
+- nvidia,ptfv : control data for periodic training
+- nvidia,emc-registers : values for the following registers (See TRM 18.11.2 for register descriptions)
+	 EMC_RC
+	 EMC_RFC
+	 EMC_RFCPB
+	 EMC_REFCTRL2
+	 EMC_RFC_SLR
+	 EMC_RAS
+	 EMC_RP
+	 EMC_R2W
+	 EMC_W2R
+	 EMC_R2P
+	 EMC_W2P
+	 EMC_R2R
+	 EMC_TPPD
+	 EMC_CCDMW
+	 EMC_RD_RCD
+	 EMC_WR_RCD
+	 EMC_RRD
+	 EMC_REXT
+	 EMC_WEXT
+	 EMC_WDV_CHK
+	 EMC_WDV
+	 EMC_WSV
+	 EMC_WEV
+	 EMC_WDV_MASK
+	 EMC_WS_DURATION
+	 EMC_WE_DURATION
+	 EMC_QUSE
+	 EMC_QUSE_WIDTH
+	 EMC_IBDLY
+	 EMC_OBDLY
+	 EMC_EINPUT
+	 EMC_MRW6
+	 EMC_EINPUT_DURATION
+	 EMC_PUTERM_EXTRA
+	 EMC_PUTERM_WIDTH
+	 EMC_QRST
+	 EMC_QSAFE
+	 EMC_RDV
+	 EMC_RDV_MASK
+	 EMC_RDV_EARLY
+	 EMC_RDV_EARLY_MASK
+	 EMC_REFRESH
+	 EMC_BURST_REFRESH_NUM
+	 EMC_PRE_REFRESH_REQ_CNT
+	 EMC_PDEX2WR
+	 EMC_PDEX2RD
+	 EMC_PCHG2PDEN
+	 EMC_ACT2PDEN
+	 EMC_AR2PDEN
+	 EMC_RW2PDEN
+	 EMC_CKE2PDEN
+	 EMC_PDEX2CKE
+	 EMC_PDEX2MRR
+	 EMC_TXSR
+	 EMC_TXSRDLL
+	 EMC_TCKE
+	 EMC_TCKESR
+	 EMC_TPD
+	 EMC_TFAW
+	 EMC_TRPAB
+	 EMC_TCLKSTABLE
+	 EMC_TCLKSTOP
+	 EMC_MRW7
+	 EMC_TREFBW
+	 EMC_ODT_WRITE
+	 EMC_FBIO_CFG5
+	 EMC_FBIO_CFG7
+	 EMC_CFG_DIG_DLL
+	 EMC_CFG_DIG_DLL_PERIOD
+	 EMC_PMACRO_IB_RXRT
+	 EMC_CFG_PIPE_1
+	 EMC_CFG_PIPE_2
+	 EMC_PMACRO_QUSE_DDLL_RANK0_4
+	 EMC_PMACRO_QUSE_DDLL_RANK0_5
+	 EMC_PMACRO_QUSE_DDLL_RANK1_4
+	 EMC_PMACRO_QUSE_DDLL_RANK1_5
+	 EMC_MRW8
+	 EMC_PMACRO_OB_DDLL_LONG_DQ_RANK1_4
+	 EMC_PMACRO_OB_DDLL_LONG_DQ_RANK1_5
+	 EMC_PMACRO_OB_DDLL_LONG_DQS_RANK0_0
+	 EMC_PMACRO_OB_DDLL_LONG_DQS_RANK0_1
+	 EMC_PMACRO_OB_DDLL_LONG_DQS_RANK0_2
+	 EMC_PMACRO_OB_DDLL_LONG_DQS_RANK0_3
+	 EMC_PMACRO_OB_DDLL_LONG_DQS_RANK0_4
+	 EMC_PMACRO_OB_DDLL_LONG_DQS_RANK0_5
+	 EMC_PMACRO_OB_DDLL_LONG_DQS_RANK1_0
+	 EMC_PMACRO_OB_DDLL_LONG_DQS_RANK1_1
+	 EMC_PMACRO_OB_DDLL_LONG_DQS_RANK1_2
+	 EMC_PMACRO_OB_DDLL_LONG_DQS_RANK1_3
+	 EMC_PMACRO_OB_DDLL_LONG_DQS_RANK1_4
+	 EMC_PMACRO_OB_DDLL_LONG_DQS_RANK1_5
+	 EMC_PMACRO_DDLL_LONG_CMD_0
+	 EMC_PMACRO_DDLL_LONG_CMD_1
+	 EMC_PMACRO_DDLL_LONG_CMD_2
+	 EMC_PMACRO_DDLL_LONG_CMD_3
+	 EMC_PMACRO_DDLL_LONG_CMD_4
+	 EMC_PMACRO_DDLL_SHORT_CMD_0
+	 EMC_PMACRO_DDLL_SHORT_CMD_1
+	 EMC_PMACRO_DDLL_SHORT_CMD_2
+	 EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK0_BYTE0_3
+	 EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK0_BYTE1_3
+	 EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK0_BYTE2_3
+	 EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK0_BYTE3_3
+	 EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK0_BYTE4_3
+	 EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK0_BYTE5_3
+	 EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK0_BYTE6_3
+	 EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK0_BYTE7_3
+	 EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK0_CMD0_3
+	 EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK0_CMD1_3
+	 EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK0_CMD2_3
+	 EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK0_CMD3_3
+	 EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK1_BYTE0_3
+	 EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK1_BYTE1_3
+	 EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK1_BYTE2_3
+	 EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK1_BYTE3_3
+	 EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK1_BYTE4_3
+	 EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK1_BYTE5_3
+	 EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK1_BYTE6_3
+	 EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK1_BYTE7_3
+	 EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK1_CMD0_0
+	 EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK1_CMD0_1
+	 EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK1_CMD0_2
+	 EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK1_CMD0_3
+	 EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK1_CMD1_0
+	 EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK1_CMD1_1
+	 EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK1_CMD1_2
+	 EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK1_CMD1_3
+	 EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK1_CMD2_0
+	 EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK1_CMD2_1
+	 EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK1_CMD2_2
+	 EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK1_CMD2_3
+	 EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK1_CMD3_0
+	 EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK1_CMD3_1
+	 EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK1_CMD3_2
+	 EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK1_CMD3_3
+	 EMC_TXDSRVTTGEN
+	 EMC_FDPD_CTRL_DQ
+	 EMC_FDPD_CTRL_CMD
+	 EMC_FBIO_SPARE
+	 EMC_ZCAL_INTERVAL
+	 EMC_ZCAL_WAIT_CNT
+	 EMC_MRS_WAIT_CNT
+	 EMC_MRS_WAIT_CNT2
+	 EMC_AUTO_CAL_CHANNEL
+	 EMC_DLL_CFG_0
+	 EMC_DLL_CFG_1
+	 EMC_PMACRO_AUTOCAL_CFG_COMMON
+	 EMC_PMACRO_ZCTRL
+	 EMC_CFG
+	 EMC_CFG_PIPE
+	 EMC_DYN_SELF_REF_CONTROL
+	 EMC_QPOP
+	 EMC_DQS_BRLSHFT_0
+	 EMC_DQS_BRLSHFT_1
+	 EMC_CMD_BRLSHFT_2
+	 EMC_CMD_BRLSHFT_3
+	 EMC_PMACRO_PAD_CFG_CTRL
+	 EMC_PMACRO_DATA_PAD_RX_CTRL
+	 EMC_PMACRO_CMD_PAD_RX_CTRL
+	 EMC_PMACRO_DATA_RX_TERM_MODE
+	 EMC_PMACRO_CMD_RX_TERM_MODE
+	 EMC_PMACRO_CMD_PAD_TX_CTRL
+	 EMC_PMACRO_DATA_PAD_TX_CTRL
+	 EMC_PMACRO_COMMON_PAD_TX_CTRL
+	 EMC_PMACRO_VTTGEN_CTRL_0
+	 EMC_PMACRO_VTTGEN_CTRL_1
+	 EMC_PMACRO_VTTGEN_CTRL_2
+	 EMC_PMACRO_BRICK_CTRL_RFU1
+	 EMC_PMACRO_CMD_BRICK_CTRL_FDPD
+	 EMC_PMACRO_BRICK_CTRL_RFU2
+	 EMC_PMACRO_DATA_BRICK_CTRL_FDPD
+	 EMC_PMACRO_BG_BIAS_CTRL_0
+	 EMC_CFG_3
+	 EMC_PMACRO_TX_PWRD_0
+	 EMC_PMACRO_TX_PWRD_1
+	 EMC_PMACRO_TX_PWRD_2
+	 EMC_PMACRO_TX_PWRD_3
+	 EMC_PMACRO_TX_PWRD_4
+	 EMC_PMACRO_TX_PWRD_5
+	 EMC_CONFIG_SAMPLE_DELAY
+	 EMC_PMACRO_TX_SEL_CLK_SRC_0
+	 EMC_PMACRO_TX_SEL_CLK_SRC_1
+	 EMC_PMACRO_TX_SEL_CLK_SRC_2
+	 EMC_PMACRO_TX_SEL_CLK_SRC_3
+	 EMC_PMACRO_TX_SEL_CLK_SRC_4
+	 EMC_PMACRO_TX_SEL_CLK_SRC_5
+	 EMC_PMACRO_DDLL_BYPASS
+	 EMC_PMACRO_DDLL_PWRD_0
+	 EMC_PMACRO_DDLL_PWRD_1
+	 EMC_PMACRO_DDLL_PWRD_2
+	 EMC_PMACRO_CMD_CTRL_0
+	 EMC_PMACRO_CMD_CTRL_1
+	 EMC_PMACRO_CMD_CTRL_2
+	 EMC_TR_TIMING_0
+	 EMC_TR_DVFS
+	 EMC_TR_CTRL_1
+	 EMC_TR_RDV
+	 EMC_TR_QPOP
+	 EMC_TR_RDV_MASK
+	 EMC_MRW14
+	 EMC_TR_QSAFE
+	 EMC_TR_QRST
+	 EMC_TRAINING_CTRL
+	 EMC_TRAINING_SETTLE
+	 EMC_TRAINING_VREF_SETTLE
+	 EMC_TRAINING_CA_FINE_CTRL
+	 EMC_TRAINING_CA_CTRL_MISC
+	 EMC_TRAINING_CA_CTRL_MISC1
+	 EMC_TRAINING_CA_VREF_CTRL
+	 EMC_TRAINING_QUSE_CORS_CTRL
+	 EMC_TRAINING_QUSE_FINE_CTRL
+	 EMC_TRAINING_QUSE_CTRL_MISC
+	 EMC_TRAINING_QUSE_VREF_CTRL
+	 EMC_TRAINING_READ_FINE_CTRL
+	 EMC_TRAINING_READ_CTRL_MISC
+	 EMC_TRAINING_READ_VREF_CTRL
+	 EMC_TRAINING_WRITE_FINE_CTRL
+	 EMC_TRAINING_WRITE_CTRL_MISC
+	 EMC_TRAINING_WRITE_VREF_CTRL
+	 EMC_TRAINING_MPC
+	 EMC_MRW15
+- nvidia,emc-burst-regs-per-ch : values for the following registers (See TRM 18.11.2 for register descriptions)
+	 the array containts 2 values for each register, one per channel.
+	 EMC_MRW10
+	 EMC_MRW11
+	 EMC_MRW12
+	 EMC_MRW13
+- nvidia,emc-trim-regs : values for the following registers (See TRM 18.11.2 for register descriptions)
+	 EMC_PMACRO_IB_DDLL_LONG_DQS_RANK0_0
+	 EMC_PMACRO_IB_DDLL_LONG_DQS_RANK0_1
+	 EMC_PMACRO_IB_DDLL_LONG_DQS_RANK0_2
+	 EMC_PMACRO_IB_DDLL_LONG_DQS_RANK0_3
+	 EMC_PMACRO_IB_DDLL_LONG_DQS_RANK1_0
+	 EMC_PMACRO_IB_DDLL_LONG_DQS_RANK1_1
+	 EMC_PMACRO_IB_DDLL_LONG_DQS_RANK1_2
+	 EMC_PMACRO_IB_DDLL_LONG_DQS_RANK1_3
+	 EMC_PMACRO_IB_DDLL_SHORT_DQ_RANK0_BYTE0_0
+	 EMC_PMACRO_IB_DDLL_SHORT_DQ_RANK0_BYTE0_1
+	 EMC_PMACRO_IB_DDLL_SHORT_DQ_RANK0_BYTE0_2
+	 EMC_PMACRO_IB_DDLL_SHORT_DQ_RANK0_BYTE1_0
+	 EMC_PMACRO_IB_DDLL_SHORT_DQ_RANK0_BYTE1_1
+	 EMC_PMACRO_IB_DDLL_SHORT_DQ_RANK0_BYTE1_2
+	 EMC_PMACRO_IB_DDLL_SHORT_DQ_RANK0_BYTE2_0
+	 EMC_PMACRO_IB_DDLL_SHORT_DQ_RANK0_BYTE2_1
+	 EMC_PMACRO_IB_DDLL_SHORT_DQ_RANK0_BYTE2_2
+	 EMC_PMACRO_IB_DDLL_SHORT_DQ_RANK0_BYTE3_0
+	 EMC_PMACRO_IB_DDLL_SHORT_DQ_RANK0_BYTE3_1
+	 EMC_PMACRO_IB_DDLL_SHORT_DQ_RANK0_BYTE3_2
+	 EMC_PMACRO_IB_DDLL_SHORT_DQ_RANK0_BYTE4_0
+	 EMC_PMACRO_IB_DDLL_SHORT_DQ_RANK0_BYTE4_1
+	 EMC_PMACRO_IB_DDLL_SHORT_DQ_RANK0_BYTE4_2
+	 EMC_PMACRO_IB_DDLL_SHORT_DQ_RANK0_BYTE5_0
+	 EMC_PMACRO_IB_DDLL_SHORT_DQ_RANK0_BYTE5_1
+	 EMC_PMACRO_IB_DDLL_SHORT_DQ_RANK0_BYTE5_2
+	 EMC_PMACRO_IB_DDLL_SHORT_DQ_RANK0_BYTE6_0
+	 EMC_PMACRO_IB_DDLL_SHORT_DQ_RANK0_BYTE6_1
+	 EMC_PMACRO_IB_DDLL_SHORT_DQ_RANK0_BYTE6_2
+	 EMC_PMACRO_IB_DDLL_SHORT_DQ_RANK0_BYTE7_0
+	 EMC_PMACRO_IB_DDLL_SHORT_DQ_RANK0_BYTE7_1
+	 EMC_PMACRO_IB_DDLL_SHORT_DQ_RANK0_BYTE7_2
+	 EMC_PMACRO_IB_DDLL_SHORT_DQ_RANK1_BYTE0_0
+	 EMC_PMACRO_IB_DDLL_SHORT_DQ_RANK1_BYTE0_1
+	 EMC_PMACRO_IB_DDLL_SHORT_DQ_RANK1_BYTE0_2
+	 EMC_PMACRO_IB_DDLL_SHORT_DQ_RANK1_BYTE1_0
+	 EMC_PMACRO_IB_DDLL_SHORT_DQ_RANK1_BYTE1_1
+	 EMC_PMACRO_IB_DDLL_SHORT_DQ_RANK1_BYTE1_2
+	 EMC_PMACRO_IB_DDLL_SHORT_DQ_RANK1_BYTE2_0
+	 EMC_PMACRO_IB_DDLL_SHORT_DQ_RANK1_BYTE2_1
+	 EMC_PMACRO_IB_DDLL_SHORT_DQ_RANK1_BYTE2_2
+	 EMC_PMACRO_IB_DDLL_SHORT_DQ_RANK1_BYTE3_0
+	 EMC_PMACRO_IB_DDLL_SHORT_DQ_RANK1_BYTE3_1
+	 EMC_PMACRO_IB_DDLL_SHORT_DQ_RANK1_BYTE3_2
+	 EMC_PMACRO_IB_DDLL_SHORT_DQ_RANK1_BYTE4_0
+	 EMC_PMACRO_IB_DDLL_SHORT_DQ_RANK1_BYTE4_1
+	 EMC_PMACRO_IB_DDLL_SHORT_DQ_RANK1_BYTE4_2
+	 EMC_PMACRO_IB_DDLL_SHORT_DQ_RANK1_BYTE5_0
+	 EMC_PMACRO_IB_DDLL_SHORT_DQ_RANK1_BYTE5_1
+	 EMC_PMACRO_IB_DDLL_SHORT_DQ_RANK1_BYTE5_2
+	 EMC_PMACRO_IB_DDLL_SHORT_DQ_RANK1_BYTE6_0
+	 EMC_PMACRO_IB_DDLL_SHORT_DQ_RANK1_BYTE6_1
+	 EMC_PMACRO_IB_DDLL_SHORT_DQ_RANK1_BYTE6_2
+	 EMC_PMACRO_IB_DDLL_SHORT_DQ_RANK1_BYTE7_0
+	 EMC_PMACRO_IB_DDLL_SHORT_DQ_RANK1_BYTE7_1
+	 EMC_PMACRO_IB_DDLL_SHORT_DQ_RANK1_BYTE7_2
+	 EMC_PMACRO_IB_VREF_DQS_0
+	 EMC_PMACRO_IB_VREF_DQS_1
+	 EMC_PMACRO_IB_VREF_DQ_0
+	 EMC_PMACRO_IB_VREF_DQ_1
+	 EMC_PMACRO_OB_DDLL_LONG_DQ_RANK0_0
+	 EMC_PMACRO_OB_DDLL_LONG_DQ_RANK0_1
+	 EMC_PMACRO_OB_DDLL_LONG_DQ_RANK0_2
+	 EMC_PMACRO_OB_DDLL_LONG_DQ_RANK0_3
+	 EMC_PMACRO_OB_DDLL_LONG_DQ_RANK0_4
+	 EMC_PMACRO_OB_DDLL_LONG_DQ_RANK0_5
+	 EMC_PMACRO_OB_DDLL_LONG_DQ_RANK1_0
+	 EMC_PMACRO_OB_DDLL_LONG_DQ_RANK1_1
+	 EMC_PMACRO_OB_DDLL_LONG_DQ_RANK1_2
+	 EMC_PMACRO_OB_DDLL_LONG_DQ_RANK1_3
+	 EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK0_BYTE0_0
+	 EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK0_BYTE0_1
+	 EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK0_BYTE0_2
+	 EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK0_BYTE1_0
+	 EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK0_BYTE1_1
+	 EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK0_BYTE1_2
+	 EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK0_BYTE2_0
+	 EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK0_BYTE2_1
+	 EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK0_BYTE2_2
+	 EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK0_BYTE3_0
+	 EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK0_BYTE3_1
+	 EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK0_BYTE3_2
+	 EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK0_BYTE4_0
+	 EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK0_BYTE4_1
+	 EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK0_BYTE4_2
+	 EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK0_BYTE5_0
+	 EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK0_BYTE5_1
+	 EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK0_BYTE5_2
+	 EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK0_BYTE6_0
+	 EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK0_BYTE6_1
+	 EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK0_BYTE6_2
+	 EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK0_BYTE7_0
+	 EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK0_BYTE7_1
+	 EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK0_BYTE7_2
+	 EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK0_CMD0_0
+	 EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK0_CMD0_1
+	 EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK0_CMD0_2
+	 EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK0_CMD1_0
+	 EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK0_CMD1_1
+	 EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK0_CMD1_2
+	 EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK0_CMD2_0
+	 EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK0_CMD2_1
+	 EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK0_CMD2_2
+	 EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK0_CMD3_0
+	 EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK0_CMD3_1
+	 EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK0_CMD3_2
+	 EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK1_BYTE0_0
+	 EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK1_BYTE0_1
+	 EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK1_BYTE0_2
+	 EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK1_BYTE1_0
+	 EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK1_BYTE1_1
+	 EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK1_BYTE1_2
+	 EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK1_BYTE2_0
+	 EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK1_BYTE2_1
+	 EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK1_BYTE2_2
+	 EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK1_BYTE3_0
+	 EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK1_BYTE3_1
+	 EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK1_BYTE3_2
+	 EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK1_BYTE4_0
+	 EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK1_BYTE4_1
+	 EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK1_BYTE4_2
+	 EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK1_BYTE5_0
+	 EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK1_BYTE5_1
+	 EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK1_BYTE5_2
+	 EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK1_BYTE6_0
+	 EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK1_BYTE6_1
+	 EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK1_BYTE6_2
+	 EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK1_BYTE7_0
+	 EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK1_BYTE7_1
+	 EMC_PMACRO_OB_DDLL_SHORT_DQ_RANK1_BYTE7_2
+	 EMC_PMACRO_QUSE_DDLL_RANK0_0
+	 EMC_PMACRO_QUSE_DDLL_RANK0_1
+	 EMC_PMACRO_QUSE_DDLL_RANK0_2
+	 EMC_PMACRO_QUSE_DDLL_RANK0_3
+	 EMC_PMACRO_QUSE_DDLL_RANK1_0
+	 EMC_PMACRO_QUSE_DDLL_RANK1_1
+	 EMC_PMACRO_QUSE_DDLL_RANK1_2
+	 EMC_PMACRO_QUSE_DDLL_RANK1_3
+- nvidia,emc-trim-regs-per-ch : values for the following registers (See TRM 18.11.2 for register descriptions)
+	 EMC_CMD_BRLSHFT_0
+	 EMC_CMD_BRLSHFT_1
+	 EMC_DATA_BRLSHFT_0 (channel 0)
+	 EMC_DATA_BRLSHFT_0 (channel 1)
+	 EMC_DATA_BRLSHFT_1 (channel 0)
+	 EMC_DATA_BRLSHFT_1 (channel 1)
+	 EMC_QUSE_BRLSHFT_0
+	 EMC_QUSE_BRLSHFT_1
+	 EMC_QUSE_BRLSHFT_2
+	 EMC_QUSE_BRLSHFT_3
+- nvidia,emc-vref-regs : values for the following registers (See TRM 18.11.2 for register descriptions)
+	 the array containts 2 values for each register, one per channel.
+	 EMC_TRAINING_OPT_DQS_IB_VREF_RANK0
+	 EMC_TRAINING_OPT_DQS_IB_VREF_RANK1
+- nvidia,emc-dram-timing-regs : DRAM timing values. These are not written to
+				registers but used during the sequence.
+	 T_RP : row pre-charge delay
+	 T_FC_LPDDR4 : frequency change time
+	 T_RFC : refresh cycle time
+	 T_PDEX : power down exit delay
+	 RL : mode register read latency
+
-- 
1.9.1

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

* Re: [RFC 14/14] dt-bindings: tegra: Add Tegra210 EMC binding
  2018-09-14 20:03 ` [RFC 14/14] dt-bindings: tegra: Add Tegra210 EMC binding Peter De Schrijver
@ 2018-09-24 21:04   ` Rob Herring
  2018-09-25 12:11     ` Peter De Schrijver
  2018-09-25 13:03       ` Peter De Schrijver
  0 siblings, 2 replies; 26+ messages in thread
From: Rob Herring @ 2018-09-24 21:04 UTC (permalink / raw)
  To: Peter De Schrijver
  Cc: vkuruturi, linux-tegra=linux-clk, linux-tegra, thierry.reding,
	jonathanh, devicetree, daniel, a.heider, swtcr0

On Fri, Sep 14, 2018 at 11:03:09PM +0300, Peter De Schrijver wrote:
> Signed-off-by: Peter De Schrijver <pdeschrijver@nvidia.com>

Needs a commit msg.

> ---
>  .../memory-controllers/nvidia,tegra210-emc.txt     | 448 +++++++++++++++++++++
>  1 file changed, 448 insertions(+)
>  create mode 100644 Documentation/devicetree/bindings/memory-controllers/nvidia,tegra210-emc.txt
> 
> diff --git a/Documentation/devicetree/bindings/memory-controllers/nvidia,tegra210-emc.txt b/Documentation/devicetree/bindings/memory-controllers/nvidia,tegra210-emc.txt
> new file mode 100644
> index 0000000..1c52f47
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/memory-controllers/nvidia,tegra210-emc.txt
> @@ -0,0 +1,448 @@
> +NVIDIA Tegra210 SoC EMC (external memory controller)
> +====================================================
> +
> +Required properties :
> +- compatible : Should be "nvidia,tegra21-emc", "nvidia,tegra124-emc".
> +- reg : physical base address and length of the controller's registers.
> +- nvidia,memory-controller : phandle of the MC driver.

Huh? What is this block then?

> +- clocks : phandles of the possible source clocks
> +- clock-names : names of the possible source clocks
> +
> +The node should contain a "emc-table" subnode for each supported RAM type
> +(see field RAM_CODE in register PMC_STRAPPING_OPT_A), with its unit address
> +being its RAM_CODE.

Unit address is based on reg property.

> +
> +Required properties for "emc-table" nodes :
> +- nvidia,ram-code : Should contain the value of RAM_CODE this timing set is
> +used for.
> +
> +Each "emc-table" node should contain a "emc-table" subnode for every supported
> +EMC clock rate. The "emc-table" subnodes should have the clock rate in kHz as
> +their unit address.
> +
> +Required properties for "emc-table" nodes :

Which emc-table nodes, the child or grand-child nodes?

> +- compatible  "nvidia,tegra21-emc-table", "nvidia,tegra210-emc-table"

> +- nvidia,revision : revision of the parameter set used for this node. All
> +                    nodes in the same "emc-table" should have the same revision
> +- clock-frequency : frequency in kHz
> +- nvidia,emc-min-mv : minimum voltage for this OPP
> +- nvidia,gk20a-min-mv : minimum GPU voltage for this OPP
> +- nvidia,source : clock source to be used for this OPP

Is this memory timings/settings or OPPs? We have a binding for OPPs 
already.

> +- nvidia,src-sel-reg : value of EMC CAR register to be used for this OPP
> +- nvidia,needs-training : 1 if the OPP needs training at boot, 0 otherwise
> +- nvidia,trained : 1 if initial training has been done by firmware, 0 otherwise
> +- nvidia,periodic_training : 1 if the OPP needs periodic training, 0 otherwise
> +- nvidia,trained_dram_clktree_c0d0u0 : training data word
> +- nvidia,trained_dram_clktree_c0d0u1 : training data word

[...]

This is a huge list of properties. For all the things that are memory 
timings, is there really value to defining a property for each setting? 
Perhaps you should just define your own format and either make it a 
separate firmware file or include that file in the dtb.

Rob

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

* Re: [RFC 14/14] dt-bindings: tegra: Add Tegra210 EMC binding
  2018-09-24 21:04   ` Rob Herring
@ 2018-09-25 12:11     ` Peter De Schrijver
  2018-09-25 12:51         ` Peter De Schrijver
  2018-09-25 14:37       ` Rob Herring
  2018-09-25 13:03       ` Peter De Schrijver
  1 sibling, 2 replies; 26+ messages in thread
From: Peter De Schrijver @ 2018-09-25 12:11 UTC (permalink / raw)
  To: Rob Herring
  Cc: vkuruturi, linux-tegra=linux-clk, linux-tegra, thierry.reding,
	jonathanh, devicetree, daniel, a.heider, swtcr0

On Mon, Sep 24, 2018 at 02:04:24PM -0700, Rob Herring wrote:
> On Fri, Sep 14, 2018 at 11:03:09PM +0300, Peter De Schrijver wrote:
> > Signed-off-by: Peter De Schrijver <pdeschrijver@nvidia.com>
> 
> Needs a commit msg.
> 
> > ---
> >  .../memory-controllers/nvidia,tegra210-emc.txt     | 448 +++++++++++++++++++++
> >  1 file changed, 448 insertions(+)
> >  create mode 100644 Documentation/devicetree/bindings/memory-controllers/nvidia,tegra210-emc.txt
> > 
> > diff --git a/Documentation/devicetree/bindings/memory-controllers/nvidia,tegra210-emc.txt b/Documentation/devicetree/bindings/memory-controllers/nvidia,tegra210-emc.txt
> > new file mode 100644
> > index 0000000..1c52f47
> > --- /dev/null
> > +++ b/Documentation/devicetree/bindings/memory-controllers/nvidia,tegra210-emc.txt
> > @@ -0,0 +1,448 @@
> > +NVIDIA Tegra210 SoC EMC (external memory controller)
> > +====================================================
> > +
> > +Required properties :
> > +- compatible : Should be "nvidia,tegra21-emc", "nvidia,tegra124-emc".
> > +- reg : physical base address and length of the controller's registers.
> > +- nvidia,memory-controller : phandle of the MC driver.
> 
> Huh? What is this block then?
> 

This is the EMC. The EMC handles the interface with the external DRAM chips.
The MC aggregates and schedules requests to the EMC. It also handles address
translation. See figure 33 of the TRM.

> > +- clocks : phandles of the possible source clocks
> > +- clock-names : names of the possible source clocks
> > +
> > +The node should contain a "emc-table" subnode for each supported RAM type
> > +(see field RAM_CODE in register PMC_STRAPPING_OPT_A), with its unit address
> > +being its RAM_CODE.
> 
> Unit address is based on reg property.
> 
> > +
> > +Required properties for "emc-table" nodes :
> > +- nvidia,ram-code : Should contain the value of RAM_CODE this timing set is
> > +used for.
> > +
> > +Each "emc-table" node should contain a "emc-table" subnode for every supported
> > +EMC clock rate. The "emc-table" subnodes should have the clock rate in kHz as
> > +their unit address.
> > +
> > +Required properties for "emc-table" nodes :
> 
> Which emc-table nodes, the child or grand-child nodes?
> 

The child "emc-table" node.

> > +- compatible  "nvidia,tegra21-emc-table", "nvidia,tegra210-emc-table"
> 
> > +- nvidia,revision : revision of the parameter set used for this node. All
> > +                    nodes in the same "emc-table" should have the same revision
> > +- clock-frequency : frequency in kHz
> > +- nvidia,emc-min-mv : minimum voltage for this OPP
> > +- nvidia,gk20a-min-mv : minimum GPU voltage for this OPP
> > +- nvidia,source : clock source to be used for this OPP
> 
> Is this memory timings/settings or OPPs? We have a binding for OPPs 
> already.
> 

This is memory timings.

> > +- nvidia,src-sel-reg : value of EMC CAR register to be used for this OPP
> > +- nvidia,needs-training : 1 if the OPP needs training at boot, 0 otherwise
> > +- nvidia,trained : 1 if initial training has been done by firmware, 0 otherwise
> > +- nvidia,periodic_training : 1 if the OPP needs periodic training, 0 otherwise
> > +- nvidia,trained_dram_clktree_c0d0u0 : training data word
> > +- nvidia,trained_dram_clktree_c0d0u1 : training data word
> 
> [...]
> 
> This is a huge list of properties. For all the things that are memory 
> timings, is there really value to defining a property for each setting? 
> Perhaps you should just define your own format and either make it a 
> separate firmware file or include that file in the dtb.

If it's a binary structure, how would this be passed given the bootloader has
to fill in the training information?

Peter.

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

* Re: [RFC 14/14] dt-bindings: tegra: Add Tegra210 EMC binding
  2018-09-25 12:11     ` Peter De Schrijver
@ 2018-09-25 12:51         ` Peter De Schrijver
  2018-09-25 14:37       ` Rob Herring
  1 sibling, 0 replies; 26+ messages in thread
From: Peter De Schrijver @ 2018-09-25 12:51 UTC (permalink / raw)
  To: Rob Herring
  Cc: vkuruturi, linux-clk, linux-tegra, thierry.reding, jonathanh,
	devicetree, daniel, a.heider, swtcr0


Corrected the address of linux-clk in the CC line.

Peter.

On Tue, Sep 25, 2018 at 03:11:07PM +0300, Peter De Schrijver wrote:
> On Mon, Sep 24, 2018 at 02:04:24PM -0700, Rob Herring wrote:
> > On Fri, Sep 14, 2018 at 11:03:09PM +0300, Peter De Schrijver wrote:
> > > Signed-off-by: Peter De Schrijver <pdeschrijver@nvidia.com>
> > 
> > Needs a commit msg.
> > 
> > > ---
> > >  .../memory-controllers/nvidia,tegra210-emc.txt     | 448 +++++++++++++++++++++
> > >  1 file changed, 448 insertions(+)
> > >  create mode 100644 Documentation/devicetree/bindings/memory-controllers/nvidia,tegra210-emc.txt
> > > 
> > > diff --git a/Documentation/devicetree/bindings/memory-controllers/nvidia,tegra210-emc.txt b/Documentation/devicetree/bindings/memory-controllers/nvidia,tegra210-emc.txt
> > > new file mode 100644
> > > index 0000000..1c52f47
> > > --- /dev/null
> > > +++ b/Documentation/devicetree/bindings/memory-controllers/nvidia,tegra210-emc.txt
> > > @@ -0,0 +1,448 @@
> > > +NVIDIA Tegra210 SoC EMC (external memory controller)
> > > +====================================================
> > > +
> > > +Required properties :
> > > +- compatible : Should be "nvidia,tegra21-emc", "nvidia,tegra124-emc".
> > > +- reg : physical base address and length of the controller's registers.
> > > +- nvidia,memory-controller : phandle of the MC driver.
> > 
> > Huh? What is this block then?
> > 
> 
> This is the EMC. The EMC handles the interface with the external DRAM chips.
> The MC aggregates and schedules requests to the EMC. It also handles address
> translation. See figure 33 of the TRM.
> 
> > > +- clocks : phandles of the possible source clocks
> > > +- clock-names : names of the possible source clocks
> > > +
> > > +The node should contain a "emc-table" subnode for each supported RAM type
> > > +(see field RAM_CODE in register PMC_STRAPPING_OPT_A), with its unit address
> > > +being its RAM_CODE.
> > 
> > Unit address is based on reg property.
> > 
> > > +
> > > +Required properties for "emc-table" nodes :
> > > +- nvidia,ram-code : Should contain the value of RAM_CODE this timing set is
> > > +used for.
> > > +
> > > +Each "emc-table" node should contain a "emc-table" subnode for every supported
> > > +EMC clock rate. The "emc-table" subnodes should have the clock rate in kHz as
> > > +their unit address.
> > > +
> > > +Required properties for "emc-table" nodes :
> > 
> > Which emc-table nodes, the child or grand-child nodes?
> > 
> 
> The child "emc-table" node.
> 
> > > +- compatible  "nvidia,tegra21-emc-table", "nvidia,tegra210-emc-table"
> > 
> > > +- nvidia,revision : revision of the parameter set used for this node. All
> > > +                    nodes in the same "emc-table" should have the same revision
> > > +- clock-frequency : frequency in kHz
> > > +- nvidia,emc-min-mv : minimum voltage for this OPP
> > > +- nvidia,gk20a-min-mv : minimum GPU voltage for this OPP
> > > +- nvidia,source : clock source to be used for this OPP
> > 
> > Is this memory timings/settings or OPPs? We have a binding for OPPs 
> > already.
> > 
> 
> This is memory timings.
> 
> > > +- nvidia,src-sel-reg : value of EMC CAR register to be used for this OPP
> > > +- nvidia,needs-training : 1 if the OPP needs training at boot, 0 otherwise
> > > +- nvidia,trained : 1 if initial training has been done by firmware, 0 otherwise
> > > +- nvidia,periodic_training : 1 if the OPP needs periodic training, 0 otherwise
> > > +- nvidia,trained_dram_clktree_c0d0u0 : training data word
> > > +- nvidia,trained_dram_clktree_c0d0u1 : training data word
> > 
> > [...]
> > 
> > This is a huge list of properties. For all the things that are memory 
> > timings, is there really value to defining a property for each setting? 
> > Perhaps you should just define your own format and either make it a 
> > separate firmware file or include that file in the dtb.
> 
> If it's a binary structure, how would this be passed given the bootloader has
> to fill in the training information?
> 
> Peter.

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

* Re: [RFC 14/14] dt-bindings: tegra: Add Tegra210 EMC binding
@ 2018-09-25 12:51         ` Peter De Schrijver
  0 siblings, 0 replies; 26+ messages in thread
From: Peter De Schrijver @ 2018-09-25 12:51 UTC (permalink / raw)
  To: Rob Herring
  Cc: vkuruturi, linux-clk, linux-tegra, thierry.reding, jonathanh,
	devicetree, daniel, a.heider, swtcr0


Corrected the address of linux-clk in the CC line.

Peter.

On Tue, Sep 25, 2018 at 03:11:07PM +0300, Peter De Schrijver wrote:
> On Mon, Sep 24, 2018 at 02:04:24PM -0700, Rob Herring wrote:
> > On Fri, Sep 14, 2018 at 11:03:09PM +0300, Peter De Schrijver wrote:
> > > Signed-off-by: Peter De Schrijver <pdeschrijver@nvidia.com>
> > 
> > Needs a commit msg.
> > 
> > > ---
> > >  .../memory-controllers/nvidia,tegra210-emc.txt     | 448 +++++++++++++++++++++
> > >  1 file changed, 448 insertions(+)
> > >  create mode 100644 Documentation/devicetree/bindings/memory-controllers/nvidia,tegra210-emc.txt
> > > 
> > > diff --git a/Documentation/devicetree/bindings/memory-controllers/nvidia,tegra210-emc.txt b/Documentation/devicetree/bindings/memory-controllers/nvidia,tegra210-emc.txt
> > > new file mode 100644
> > > index 0000000..1c52f47
> > > --- /dev/null
> > > +++ b/Documentation/devicetree/bindings/memory-controllers/nvidia,tegra210-emc.txt
> > > @@ -0,0 +1,448 @@
> > > +NVIDIA Tegra210 SoC EMC (external memory controller)
> > > +====================================================
> > > +
> > > +Required properties :
> > > +- compatible : Should be "nvidia,tegra21-emc", "nvidia,tegra124-emc".
> > > +- reg : physical base address and length of the controller's registers.
> > > +- nvidia,memory-controller : phandle of the MC driver.
> > 
> > Huh? What is this block then?
> > 
> 
> This is the EMC. The EMC handles the interface with the external DRAM chips.
> The MC aggregates and schedules requests to the EMC. It also handles address
> translation. See figure 33 of the TRM.
> 
> > > +- clocks : phandles of the possible source clocks
> > > +- clock-names : names of the possible source clocks
> > > +
> > > +The node should contain a "emc-table" subnode for each supported RAM type
> > > +(see field RAM_CODE in register PMC_STRAPPING_OPT_A), with its unit address
> > > +being its RAM_CODE.
> > 
> > Unit address is based on reg property.
> > 
> > > +
> > > +Required properties for "emc-table" nodes :
> > > +- nvidia,ram-code : Should contain the value of RAM_CODE this timing set is
> > > +used for.
> > > +
> > > +Each "emc-table" node should contain a "emc-table" subnode for every supported
> > > +EMC clock rate. The "emc-table" subnodes should have the clock rate in kHz as
> > > +their unit address.
> > > +
> > > +Required properties for "emc-table" nodes :
> > 
> > Which emc-table nodes, the child or grand-child nodes?
> > 
> 
> The child "emc-table" node.
> 
> > > +- compatible  "nvidia,tegra21-emc-table", "nvidia,tegra210-emc-table"
> > 
> > > +- nvidia,revision : revision of the parameter set used for this node. All
> > > +                    nodes in the same "emc-table" should have the same revision
> > > +- clock-frequency : frequency in kHz
> > > +- nvidia,emc-min-mv : minimum voltage for this OPP
> > > +- nvidia,gk20a-min-mv : minimum GPU voltage for this OPP
> > > +- nvidia,source : clock source to be used for this OPP
> > 
> > Is this memory timings/settings or OPPs? We have a binding for OPPs 
> > already.
> > 
> 
> This is memory timings.
> 
> > > +- nvidia,src-sel-reg : value of EMC CAR register to be used for this OPP
> > > +- nvidia,needs-training : 1 if the OPP needs training at boot, 0 otherwise
> > > +- nvidia,trained : 1 if initial training has been done by firmware, 0 otherwise
> > > +- nvidia,periodic_training : 1 if the OPP needs periodic training, 0 otherwise
> > > +- nvidia,trained_dram_clktree_c0d0u0 : training data word
> > > +- nvidia,trained_dram_clktree_c0d0u1 : training data word
> > 
> > [...]
> > 
> > This is a huge list of properties. For all the things that are memory 
> > timings, is there really value to defining a property for each setting? 
> > Perhaps you should just define your own format and either make it a 
> > separate firmware file or include that file in the dtb.
> 
> If it's a binary structure, how would this be passed given the bootloader has
> to fill in the training information?
> 
> Peter.

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

* Re: [RFC 14/14] dt-bindings: tegra: Add Tegra210 EMC binding
  2018-09-24 21:04   ` Rob Herring
@ 2018-09-25 13:03       ` Peter De Schrijver
  2018-09-25 13:03       ` Peter De Schrijver
  1 sibling, 0 replies; 26+ messages in thread
From: Peter De Schrijver @ 2018-09-25 13:03 UTC (permalink / raw)
  To: Rob Herring
  Cc: vkuruturi, linux-clk, linux-tegra, thierry.reding, jonathanh,
	devicetree, daniel, a.heider, swtcr0

On Mon, Sep 24, 2018 at 02:04:24PM -0700, Rob Herring wrote:
> On Fri, Sep 14, 2018 at 11:03:09PM +0300, Peter De Schrijver wrote:
> > Signed-off-by: Peter De Schrijver <pdeschrijver@nvidia.com>
> 
> Needs a commit msg.
> 
> > ---
> >  .../memory-controllers/nvidia,tegra210-emc.txt     | 448 +++++++++++++++++++++
> >  1 file changed, 448 insertions(+)
> >  create mode 100644 Documentation/devicetree/bindings/memory-controllers/nvidia,tegra210-emc.txt
> > 
> > diff --git a/Documentation/devicetree/bindings/memory-controllers/nvidia,tegra210-emc.txt b/Documentation/devicetree/bindings/memory-controllers/nvidia,tegra210-emc.txt
> > new file mode 100644
> > index 0000000..1c52f47
> > --- /dev/null
> > +++ b/Documentation/devicetree/bindings/memory-controllers/nvidia,tegra210-emc.txt
> > @@ -0,0 +1,448 @@
> > +NVIDIA Tegra210 SoC EMC (external memory controller)
> > +====================================================
> > +
> > +Required properties :
> > +- compatible : Should be "nvidia,tegra21-emc", "nvidia,tegra124-emc".
> > +- reg : physical base address and length of the controller's registers.
> > +- nvidia,memory-controller : phandle of the MC driver.
> 
> Huh? What is this block then?
> 
> > +- clocks : phandles of the possible source clocks
> > +- clock-names : names of the possible source clocks
> > +
> > +The node should contain a "emc-table" subnode for each supported RAM type
> > +(see field RAM_CODE in register PMC_STRAPPING_OPT_A), with its unit address
> > +being its RAM_CODE.
> 
> Unit address is based on reg property.
> 
> > +
> > +Required properties for "emc-table" nodes :
> > +- nvidia,ram-code : Should contain the value of RAM_CODE this timing set is
> > +used for.
> > +
> > +Each "emc-table" node should contain a "emc-table" subnode for every supported
> > +EMC clock rate. The "emc-table" subnodes should have the clock rate in kHz as
> > +their unit address.
> > +
> > +Required properties for "emc-table" nodes :
> 
> Which emc-table nodes, the child or grand-child nodes?
> 
> > +- compatible  "nvidia,tegra21-emc-table", "nvidia,tegra210-emc-table"
> 
> > +- nvidia,revision : revision of the parameter set used for this node. All
> > +                    nodes in the same "emc-table" should have the same revision
> > +- clock-frequency : frequency in kHz
> > +- nvidia,emc-min-mv : minimum voltage for this OPP
> > +- nvidia,gk20a-min-mv : minimum GPU voltage for this OPP
> > +- nvidia,source : clock source to be used for this OPP
> 
> Is this memory timings/settings or OPPs? We have a binding for OPPs 
> already.
> 
> > +- nvidia,src-sel-reg : value of EMC CAR register to be used for this OPP
> > +- nvidia,needs-training : 1 if the OPP needs training at boot, 0 otherwise
> > +- nvidia,trained : 1 if initial training has been done by firmware, 0 otherwise
> > +- nvidia,periodic_training : 1 if the OPP needs periodic training, 0 otherwise
> > +- nvidia,trained_dram_clktree_c0d0u0 : training data word
> > +- nvidia,trained_dram_clktree_c0d0u1 : training data word
> 
> [...]
> 
> This is a huge list of properties. For all the things that are memory 
> timings, is there really value to defining a property for each setting? 
> Perhaps you should just define your own format and either make it a 
> separate firmware file or include that file in the dtb.
> 

The problem with moving to a binary blob is that it will break compatibility
with existing devices (eg. Jetson TX1 and shield TV) because their bootloaders
rely on the existing format.

Peter.

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

* Re: [RFC 14/14] dt-bindings: tegra: Add Tegra210 EMC binding
@ 2018-09-25 13:03       ` Peter De Schrijver
  0 siblings, 0 replies; 26+ messages in thread
From: Peter De Schrijver @ 2018-09-25 13:03 UTC (permalink / raw)
  To: Rob Herring
  Cc: vkuruturi, linux-clk, linux-tegra, thierry.reding, jonathanh,
	devicetree, daniel, a.heider, swtcr0

On Mon, Sep 24, 2018 at 02:04:24PM -0700, Rob Herring wrote:
> On Fri, Sep 14, 2018 at 11:03:09PM +0300, Peter De Schrijver wrote:
> > Signed-off-by: Peter De Schrijver <pdeschrijver@nvidia.com>
> 
> Needs a commit msg.
> 
> > ---
> >  .../memory-controllers/nvidia,tegra210-emc.txt     | 448 +++++++++++++++++++++
> >  1 file changed, 448 insertions(+)
> >  create mode 100644 Documentation/devicetree/bindings/memory-controllers/nvidia,tegra210-emc.txt
> > 
> > diff --git a/Documentation/devicetree/bindings/memory-controllers/nvidia,tegra210-emc.txt b/Documentation/devicetree/bindings/memory-controllers/nvidia,tegra210-emc.txt
> > new file mode 100644
> > index 0000000..1c52f47
> > --- /dev/null
> > +++ b/Documentation/devicetree/bindings/memory-controllers/nvidia,tegra210-emc.txt
> > @@ -0,0 +1,448 @@
> > +NVIDIA Tegra210 SoC EMC (external memory controller)
> > +====================================================
> > +
> > +Required properties :
> > +- compatible : Should be "nvidia,tegra21-emc", "nvidia,tegra124-emc".
> > +- reg : physical base address and length of the controller's registers.
> > +- nvidia,memory-controller : phandle of the MC driver.
> 
> Huh? What is this block then?
> 
> > +- clocks : phandles of the possible source clocks
> > +- clock-names : names of the possible source clocks
> > +
> > +The node should contain a "emc-table" subnode for each supported RAM type
> > +(see field RAM_CODE in register PMC_STRAPPING_OPT_A), with its unit address
> > +being its RAM_CODE.
> 
> Unit address is based on reg property.
> 
> > +
> > +Required properties for "emc-table" nodes :
> > +- nvidia,ram-code : Should contain the value of RAM_CODE this timing set is
> > +used for.
> > +
> > +Each "emc-table" node should contain a "emc-table" subnode for every supported
> > +EMC clock rate. The "emc-table" subnodes should have the clock rate in kHz as
> > +their unit address.
> > +
> > +Required properties for "emc-table" nodes :
> 
> Which emc-table nodes, the child or grand-child nodes?
> 
> > +- compatible  "nvidia,tegra21-emc-table", "nvidia,tegra210-emc-table"
> 
> > +- nvidia,revision : revision of the parameter set used for this node. All
> > +                    nodes in the same "emc-table" should have the same revision
> > +- clock-frequency : frequency in kHz
> > +- nvidia,emc-min-mv : minimum voltage for this OPP
> > +- nvidia,gk20a-min-mv : minimum GPU voltage for this OPP
> > +- nvidia,source : clock source to be used for this OPP
> 
> Is this memory timings/settings or OPPs? We have a binding for OPPs 
> already.
> 
> > +- nvidia,src-sel-reg : value of EMC CAR register to be used for this OPP
> > +- nvidia,needs-training : 1 if the OPP needs training at boot, 0 otherwise
> > +- nvidia,trained : 1 if initial training has been done by firmware, 0 otherwise
> > +- nvidia,periodic_training : 1 if the OPP needs periodic training, 0 otherwise
> > +- nvidia,trained_dram_clktree_c0d0u0 : training data word
> > +- nvidia,trained_dram_clktree_c0d0u1 : training data word
> 
> [...]
> 
> This is a huge list of properties. For all the things that are memory 
> timings, is there really value to defining a property for each setting? 
> Perhaps you should just define your own format and either make it a 
> separate firmware file or include that file in the dtb.
> 

The problem with moving to a binary blob is that it will break compatibility
with existing devices (eg. Jetson TX1 and shield TV) because their bootloaders
rely on the existing format.

Peter.

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

* Re: [RFC 14/14] dt-bindings: tegra: Add Tegra210 EMC binding
  2018-09-25 12:11     ` Peter De Schrijver
  2018-09-25 12:51         ` Peter De Schrijver
@ 2018-09-25 14:37       ` Rob Herring
  2018-09-26  8:14           ` Peter De Schrijver
  1 sibling, 1 reply; 26+ messages in thread
From: Rob Herring @ 2018-09-25 14:37 UTC (permalink / raw)
  To: Peter De Schrijver
  Cc: vkuruturi, linux-tegra, Thierry Reding, Jon Hunter, devicetree,
	daniel, Andre Heider, swtcr0, linux-clk

On Tue, Sep 25, 2018 at 7:11 AM Peter De Schrijver
<pdeschrijver@nvidia.com> wrote:
>
> On Mon, Sep 24, 2018 at 02:04:24PM -0700, Rob Herring wrote:
> > On Fri, Sep 14, 2018 at 11:03:09PM +0300, Peter De Schrijver wrote:
> > > Signed-off-by: Peter De Schrijver <pdeschrijver@nvidia.com>
> >
> > Needs a commit msg.
> >
> > > ---
> > >  .../memory-controllers/nvidia,tegra210-emc.txt     | 448 +++++++++++++++++++++
> > >  1 file changed, 448 insertions(+)
> > >  create mode 100644 Documentation/devicetree/bindings/memory-controllers/nvidia,tegra210-emc.txt
> > >
> > > diff --git a/Documentation/devicetree/bindings/memory-controllers/nvidia,tegra210-emc.txt b/Documentation/devicetree/bindings/memory-controllers/nvidia,tegra210-emc.txt
> > > new file mode 100644
> > > index 0000000..1c52f47
> > > --- /dev/null
> > > +++ b/Documentation/devicetree/bindings/memory-controllers/nvidia,tegra210-emc.txt
> > > @@ -0,0 +1,448 @@
> > > +NVIDIA Tegra210 SoC EMC (external memory controller)
> > > +====================================================
> > > +
> > > +Required properties :
> > > +- compatible : Should be "nvidia,tegra21-emc", "nvidia,tegra124-emc".
> > > +- reg : physical base address and length of the controller's registers.
> > > +- nvidia,memory-controller : phandle of the MC driver.
> >
> > Huh? What is this block then?
> >
>
> This is the EMC. The EMC handles the interface with the external DRAM chips.
> The MC aggregates and schedules requests to the EMC. It also handles address
> translation. See figure 33 of the TRM.

Sorry, but I hardly have time to go read TRM's for every binding.

Just explain what the MC is here and remove 'driver'.

> > > +- clocks : phandles of the possible source clocks
> > > +- clock-names : names of the possible source clocks
> > > +
> > > +The node should contain a "emc-table" subnode for each supported RAM type
> > > +(see field RAM_CODE in register PMC_STRAPPING_OPT_A), with its unit address
> > > +being its RAM_CODE.
> >
> > Unit address is based on reg property.
> >
> > > +
> > > +Required properties for "emc-table" nodes :
> > > +- nvidia,ram-code : Should contain the value of RAM_CODE this timing set is
> > > +used for.
> > > +
> > > +Each "emc-table" node should contain a "emc-table" subnode for every supported
> > > +EMC clock rate. The "emc-table" subnodes should have the clock rate in kHz as
> > > +their unit address.
> > > +
> > > +Required properties for "emc-table" nodes :
> >
> > Which emc-table nodes, the child or grand-child nodes?
> >
>
> The child "emc-table" node.
>
> > > +- compatible  "nvidia,tegra21-emc-table", "nvidia,tegra210-emc-table"
> >
> > > +- nvidia,revision : revision of the parameter set used for this node. All
> > > +                    nodes in the same "emc-table" should have the same revision
> > > +- clock-frequency : frequency in kHz
> > > +- nvidia,emc-min-mv : minimum voltage for this OPP
> > > +- nvidia,gk20a-min-mv : minimum GPU voltage for this OPP
> > > +- nvidia,source : clock source to be used for this OPP
> >
> > Is this memory timings/settings or OPPs? We have a binding for OPPs
> > already.
> >
>
> This is memory timings.
>
> > > +- nvidia,src-sel-reg : value of EMC CAR register to be used for this OPP
> > > +- nvidia,needs-training : 1 if the OPP needs training at boot, 0 otherwise
> > > +- nvidia,trained : 1 if initial training has been done by firmware, 0 otherwise
> > > +- nvidia,periodic_training : 1 if the OPP needs periodic training, 0 otherwise
> > > +- nvidia,trained_dram_clktree_c0d0u0 : training data word
> > > +- nvidia,trained_dram_clktree_c0d0u1 : training data word
> >
> > [...]
> >
> > This is a huge list of properties. For all the things that are memory
> > timings, is there really value to defining a property for each setting?
> > Perhaps you should just define your own format and either make it a
> > separate firmware file or include that file in the dtb.
>
> If it's a binary structure, how would this be passed given the bootloader has
> to fill in the training information?

Perhaps training data makes sense to keep as DT properties, but it is
not all training data, right? Things like a list of register
initialization values do not sound like training data.

I assume the the training data is read back from the h/w, can't the
kernel just do that itself? Or the data is lost after init is
complete?

Rob

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

* Re: [RFC 14/14] dt-bindings: tegra: Add Tegra210 EMC binding
  2018-09-25 13:03       ` Peter De Schrijver
  (?)
@ 2018-09-25 14:45       ` Rob Herring
  -1 siblings, 0 replies; 26+ messages in thread
From: Rob Herring @ 2018-09-25 14:45 UTC (permalink / raw)
  To: Peter De Schrijver
  Cc: vkuruturi, linux-clk, linux-tegra, Thierry Reding, Jon Hunter,
	devicetree, daniel, Andre Heider, swtcr0

On Tue, Sep 25, 2018 at 8:03 AM Peter De Schrijver
<pdeschrijver@nvidia.com> wrote:
>
> On Mon, Sep 24, 2018 at 02:04:24PM -0700, Rob Herring wrote:
> > On Fri, Sep 14, 2018 at 11:03:09PM +0300, Peter De Schrijver wrote:
> > > Signed-off-by: Peter De Schrijver <pdeschrijver@nvidia.com>
> >
> > Needs a commit msg.
> >
> > > ---
> > >  .../memory-controllers/nvidia,tegra210-emc.txt     | 448 +++++++++++++++++++++
> > >  1 file changed, 448 insertions(+)
> > >  create mode 100644 Documentation/devicetree/bindings/memory-controllers/nvidia,tegra210-emc.txt
> > >
> > > diff --git a/Documentation/devicetree/bindings/memory-controllers/nvidia,tegra210-emc.txt b/Documentation/devicetree/bindings/memory-controllers/nvidia,tegra210-emc.txt
> > > new file mode 100644
> > > index 0000000..1c52f47
> > > --- /dev/null
> > > +++ b/Documentation/devicetree/bindings/memory-controllers/nvidia,tegra210-emc.txt
> > > @@ -0,0 +1,448 @@
> > > +NVIDIA Tegra210 SoC EMC (external memory controller)
> > > +====================================================
> > > +
> > > +Required properties :
> > > +- compatible : Should be "nvidia,tegra21-emc", "nvidia,tegra124-emc".
> > > +- reg : physical base address and length of the controller's registers.
> > > +- nvidia,memory-controller : phandle of the MC driver.
> >
> > Huh? What is this block then?
> >
> > > +- clocks : phandles of the possible source clocks
> > > +- clock-names : names of the possible source clocks
> > > +
> > > +The node should contain a "emc-table" subnode for each supported RAM type
> > > +(see field RAM_CODE in register PMC_STRAPPING_OPT_A), with its unit address
> > > +being its RAM_CODE.
> >
> > Unit address is based on reg property.
> >
> > > +
> > > +Required properties for "emc-table" nodes :
> > > +- nvidia,ram-code : Should contain the value of RAM_CODE this timing set is
> > > +used for.
> > > +
> > > +Each "emc-table" node should contain a "emc-table" subnode for every supported
> > > +EMC clock rate. The "emc-table" subnodes should have the clock rate in kHz as
> > > +their unit address.
> > > +
> > > +Required properties for "emc-table" nodes :
> >
> > Which emc-table nodes, the child or grand-child nodes?
> >
> > > +- compatible  "nvidia,tegra21-emc-table", "nvidia,tegra210-emc-table"
> >
> > > +- nvidia,revision : revision of the parameter set used for this node. All
> > > +                    nodes in the same "emc-table" should have the same revision
> > > +- clock-frequency : frequency in kHz
> > > +- nvidia,emc-min-mv : minimum voltage for this OPP
> > > +- nvidia,gk20a-min-mv : minimum GPU voltage for this OPP
> > > +- nvidia,source : clock source to be used for this OPP
> >
> > Is this memory timings/settings or OPPs? We have a binding for OPPs
> > already.
> >
> > > +- nvidia,src-sel-reg : value of EMC CAR register to be used for this OPP
> > > +- nvidia,needs-training : 1 if the OPP needs training at boot, 0 otherwise
> > > +- nvidia,trained : 1 if initial training has been done by firmware, 0 otherwise
> > > +- nvidia,periodic_training : 1 if the OPP needs periodic training, 0 otherwise
> > > +- nvidia,trained_dram_clktree_c0d0u0 : training data word
> > > +- nvidia,trained_dram_clktree_c0d0u1 : training data word
> >
> > [...]
> >
> > This is a huge list of properties. For all the things that are memory
> > timings, is there really value to defining a property for each setting?
> > Perhaps you should just define your own format and either make it a
> > separate firmware file or include that file in the dtb.
> >
>
> The problem with moving to a binary blob is that it will break compatibility
> with existing devices (eg. Jetson TX1 and shield TV) because their bootloaders
> rely on the existing format.

Good to know, but not really my problem that bindings shipped without
review. While this is self-contained and doesn't have any sort of
common binding which could be used instead, can you say that about
*everything* the bootloader depends on.

Rob

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

* Re: [RFC 14/14] dt-bindings: tegra: Add Tegra210 EMC binding
  2018-09-25 14:37       ` Rob Herring
@ 2018-09-26  8:14           ` Peter De Schrijver
  0 siblings, 0 replies; 26+ messages in thread
From: Peter De Schrijver @ 2018-09-26  8:14 UTC (permalink / raw)
  To: Rob Herring
  Cc: vkuruturi, linux-tegra, Thierry Reding, Jon Hunter, devicetree,
	daniel, Andre Heider, swtcr0, linux-clk

On Tue, Sep 25, 2018 at 09:37:59AM -0500, Rob Herring wrote:
> On Tue, Sep 25, 2018 at 7:11 AM Peter De Schrijver
> <pdeschrijver@nvidia.com> wrote:
> >
> > On Mon, Sep 24, 2018 at 02:04:24PM -0700, Rob Herring wrote:
> > > On Fri, Sep 14, 2018 at 11:03:09PM +0300, Peter De Schrijver wrote:
> > > > Signed-off-by: Peter De Schrijver <pdeschrijver@nvidia.com>
> > >
> > > Needs a commit msg.
> > >
> > > > ---
> > > >  .../memory-controllers/nvidia,tegra210-emc.txt     | 448 +++++++++++++++++++++
> > > >  1 file changed, 448 insertions(+)
> > > >  create mode 100644 Documentation/devicetree/bindings/memory-controllers/nvidia,tegra210-emc.txt
> > > >
> > > > diff --git a/Documentation/devicetree/bindings/memory-controllers/nvidia,tegra210-emc.txt b/Documentation/devicetree/bindings/memory-controllers/nvidia,tegra210-emc.txt
> > > > new file mode 100644
> > > > index 0000000..1c52f47
> > > > --- /dev/null
> > > > +++ b/Documentation/devicetree/bindings/memory-controllers/nvidia,tegra210-emc.txt
> > > > @@ -0,0 +1,448 @@
> > > > +NVIDIA Tegra210 SoC EMC (external memory controller)
> > > > +====================================================
> > > > +
> > > > +Required properties :
> > > > +- compatible : Should be "nvidia,tegra21-emc", "nvidia,tegra124-emc".
> > > > +- reg : physical base address and length of the controller's registers.
> > > > +- nvidia,memory-controller : phandle of the MC driver.
> > >
> > > Huh? What is this block then?
> > >
> >
> > This is the EMC. The EMC handles the interface with the external DRAM chips.
> > The MC aggregates and schedules requests to the EMC. It also handles address
> > translation. See figure 33 of the TRM.
> 
> Sorry, but I hardly have time to go read TRM's for every binding.
> 
> Just explain what the MC is here and remove 'driver'.
> 
> > > > +- clocks : phandles of the possible source clocks
> > > > +- clock-names : names of the possible source clocks
> > > > +
> > > > +The node should contain a "emc-table" subnode for each supported RAM type
> > > > +(see field RAM_CODE in register PMC_STRAPPING_OPT_A), with its unit address
> > > > +being its RAM_CODE.
> > >
> > > Unit address is based on reg property.
> > >
> > > > +
> > > > +Required properties for "emc-table" nodes :
> > > > +- nvidia,ram-code : Should contain the value of RAM_CODE this timing set is
> > > > +used for.
> > > > +
> > > > +Each "emc-table" node should contain a "emc-table" subnode for every supported
> > > > +EMC clock rate. The "emc-table" subnodes should have the clock rate in kHz as
> > > > +their unit address.
> > > > +
> > > > +Required properties for "emc-table" nodes :
> > >
> > > Which emc-table nodes, the child or grand-child nodes?
> > >
> >
> > The child "emc-table" node.
> >
> > > > +- compatible  "nvidia,tegra21-emc-table", "nvidia,tegra210-emc-table"
> > >
> > > > +- nvidia,revision : revision of the parameter set used for this node. All
> > > > +                    nodes in the same "emc-table" should have the same revision
> > > > +- clock-frequency : frequency in kHz
> > > > +- nvidia,emc-min-mv : minimum voltage for this OPP
> > > > +- nvidia,gk20a-min-mv : minimum GPU voltage for this OPP
> > > > +- nvidia,source : clock source to be used for this OPP
> > >
> > > Is this memory timings/settings or OPPs? We have a binding for OPPs
> > > already.
> > >
> >
> > This is memory timings.
> >
> > > > +- nvidia,src-sel-reg : value of EMC CAR register to be used for this OPP
> > > > +- nvidia,needs-training : 1 if the OPP needs training at boot, 0 otherwise
> > > > +- nvidia,trained : 1 if initial training has been done by firmware, 0 otherwise
> > > > +- nvidia,periodic_training : 1 if the OPP needs periodic training, 0 otherwise
> > > > +- nvidia,trained_dram_clktree_c0d0u0 : training data word
> > > > +- nvidia,trained_dram_clktree_c0d0u1 : training data word
> > >
> > > [...]
> > >
> > > This is a huge list of properties. For all the things that are memory
> > > timings, is there really value to defining a property for each setting?
> > > Perhaps you should just define your own format and either make it a
> > > separate firmware file or include that file in the dtb.
> >
> > If it's a binary structure, how would this be passed given the bootloader has
> > to fill in the training information?
> 
> Perhaps training data makes sense to keep as DT properties, but it is
> not all training data, right? Things like a list of register
> initialization values do not sound like training data.
> 

No, those are per frequency timing settings, just like we have them for
Tegra124 and previous chips.

> I assume the the training data is read back from the h/w, can't the
> kernel just do that itself? Or the data is lost after init is
> complete?

The data is mostly lost. The training data also depends on the frequency, so
if it could be read back from the h/w, it would only be the data for the last
frequency which was trained.

Peter.

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

* Re: [RFC 14/14] dt-bindings: tegra: Add Tegra210 EMC binding
@ 2018-09-26  8:14           ` Peter De Schrijver
  0 siblings, 0 replies; 26+ messages in thread
From: Peter De Schrijver @ 2018-09-26  8:14 UTC (permalink / raw)
  To: Rob Herring
  Cc: vkuruturi, linux-tegra, Thierry Reding, Jon Hunter, devicetree,
	daniel, Andre Heider, swtcr0, linux-clk

On Tue, Sep 25, 2018 at 09:37:59AM -0500, Rob Herring wrote:
> On Tue, Sep 25, 2018 at 7:11 AM Peter De Schrijver
> <pdeschrijver@nvidia.com> wrote:
> >
> > On Mon, Sep 24, 2018 at 02:04:24PM -0700, Rob Herring wrote:
> > > On Fri, Sep 14, 2018 at 11:03:09PM +0300, Peter De Schrijver wrote:
> > > > Signed-off-by: Peter De Schrijver <pdeschrijver@nvidia.com>
> > >
> > > Needs a commit msg.
> > >
> > > > ---
> > > >  .../memory-controllers/nvidia,tegra210-emc.txt     | 448 +++++++++++++++++++++
> > > >  1 file changed, 448 insertions(+)
> > > >  create mode 100644 Documentation/devicetree/bindings/memory-controllers/nvidia,tegra210-emc.txt
> > > >
> > > > diff --git a/Documentation/devicetree/bindings/memory-controllers/nvidia,tegra210-emc.txt b/Documentation/devicetree/bindings/memory-controllers/nvidia,tegra210-emc.txt
> > > > new file mode 100644
> > > > index 0000000..1c52f47
> > > > --- /dev/null
> > > > +++ b/Documentation/devicetree/bindings/memory-controllers/nvidia,tegra210-emc.txt
> > > > @@ -0,0 +1,448 @@
> > > > +NVIDIA Tegra210 SoC EMC (external memory controller)
> > > > +====================================================
> > > > +
> > > > +Required properties :
> > > > +- compatible : Should be "nvidia,tegra21-emc", "nvidia,tegra124-emc".
> > > > +- reg : physical base address and length of the controller's registers.
> > > > +- nvidia,memory-controller : phandle of the MC driver.
> > >
> > > Huh? What is this block then?
> > >
> >
> > This is the EMC. The EMC handles the interface with the external DRAM chips.
> > The MC aggregates and schedules requests to the EMC. It also handles address
> > translation. See figure 33 of the TRM.
> 
> Sorry, but I hardly have time to go read TRM's for every binding.
> 
> Just explain what the MC is here and remove 'driver'.
> 
> > > > +- clocks : phandles of the possible source clocks
> > > > +- clock-names : names of the possible source clocks
> > > > +
> > > > +The node should contain a "emc-table" subnode for each supported RAM type
> > > > +(see field RAM_CODE in register PMC_STRAPPING_OPT_A), with its unit address
> > > > +being its RAM_CODE.
> > >
> > > Unit address is based on reg property.
> > >
> > > > +
> > > > +Required properties for "emc-table" nodes :
> > > > +- nvidia,ram-code : Should contain the value of RAM_CODE this timing set is
> > > > +used for.
> > > > +
> > > > +Each "emc-table" node should contain a "emc-table" subnode for every supported
> > > > +EMC clock rate. The "emc-table" subnodes should have the clock rate in kHz as
> > > > +their unit address.
> > > > +
> > > > +Required properties for "emc-table" nodes :
> > >
> > > Which emc-table nodes, the child or grand-child nodes?
> > >
> >
> > The child "emc-table" node.
> >
> > > > +- compatible  "nvidia,tegra21-emc-table", "nvidia,tegra210-emc-table"
> > >
> > > > +- nvidia,revision : revision of the parameter set used for this node. All
> > > > +                    nodes in the same "emc-table" should have the same revision
> > > > +- clock-frequency : frequency in kHz
> > > > +- nvidia,emc-min-mv : minimum voltage for this OPP
> > > > +- nvidia,gk20a-min-mv : minimum GPU voltage for this OPP
> > > > +- nvidia,source : clock source to be used for this OPP
> > >
> > > Is this memory timings/settings or OPPs? We have a binding for OPPs
> > > already.
> > >
> >
> > This is memory timings.
> >
> > > > +- nvidia,src-sel-reg : value of EMC CAR register to be used for this OPP
> > > > +- nvidia,needs-training : 1 if the OPP needs training at boot, 0 otherwise
> > > > +- nvidia,trained : 1 if initial training has been done by firmware, 0 otherwise
> > > > +- nvidia,periodic_training : 1 if the OPP needs periodic training, 0 otherwise
> > > > +- nvidia,trained_dram_clktree_c0d0u0 : training data word
> > > > +- nvidia,trained_dram_clktree_c0d0u1 : training data word
> > >
> > > [...]
> > >
> > > This is a huge list of properties. For all the things that are memory
> > > timings, is there really value to defining a property for each setting?
> > > Perhaps you should just define your own format and either make it a
> > > separate firmware file or include that file in the dtb.
> >
> > If it's a binary structure, how would this be passed given the bootloader has
> > to fill in the training information?
> 
> Perhaps training data makes sense to keep as DT properties, but it is
> not all training data, right? Things like a list of register
> initialization values do not sound like training data.
> 

No, those are per frequency timing settings, just like we have them for
Tegra124 and previous chips.

> I assume the the training data is read back from the h/w, can't the
> kernel just do that itself? Or the data is lost after init is
> complete?

The data is mostly lost. The training data also depends on the frequency, so
if it could be read back from the h/w, it would only be the data for the last
frequency which was trained.

Peter.

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

* [RFC 07/14] memory: tegra: scaled LA register for Tegra210
  2018-09-14 21:48 [RFC 00/14] Tegra210 EMC scaling Peter De Schrijver
@ 2018-09-14 21:48 ` Peter De Schrijver
  0 siblings, 0 replies; 26+ messages in thread
From: Peter De Schrijver @ 2018-09-14 21:48 UTC (permalink / raw)
  To: linux-clk; +Cc: Peter De Schrijver

Signed-off-by: Peter De Schrijver <pdeschrijver@nvidia.com>
---
 drivers/memory/tegra/tegra210.c | 19 +++++++++++++++++++
 1 file changed, 19 insertions(+)

diff --git a/drivers/memory/tegra/tegra210.c b/drivers/memory/tegra/tegra210.c
index aa9dcf0..b4dd533 100644
--- a/drivers/memory/tegra/tegra210.c
+++ b/drivers/memory/tegra/tegra210.c
@@ -53,6 +53,13 @@
 #define MC_EMEM_ARB_DHYST_TIMEOUT_UTIL_6			0xbe8
 #define MC_EMEM_ARB_DHYST_TIMEOUT_UTIL_7			0xbec
 
+#define MC_SCALED_LATENCY_ALLOWANCE_DISPLAY0AB			0x694
+#define MC_SCALED_LATENCY_ALLOWANCE_DISPLAY0B			0x698
+#define MC_SCALED_LATENCY_ALLOWANCE_DISPLAY0C			0x6a0
+#define MC_SCALED_LATENCY_ALLOWANCE_DISPLAY0BB			0x69c
+#define MC_SCALED_LATENCY_ALLOWANCE_DISPLAY0CB			0x6a4
+#define MC_SCALED_LATENCY_ALLOWANCE_DISPLAY0A			0x690
+
 static const unsigned long tegra210_mc_emem_regs[] = {
 	 MC_EMEM_ADR_CFG,
 	 MC_EMEM_ARB_CFG,
@@ -93,6 +100,15 @@
 	 MC_EMEM_ARB_DHYST_TIMEOUT_UTIL_7,
 };
 
+static const unsigned long tegra210_scaled_la_regs[] = {
+	 MC_SCALED_LATENCY_ALLOWANCE_DISPLAY0AB,
+	 MC_SCALED_LATENCY_ALLOWANCE_DISPLAY0B,
+	 MC_SCALED_LATENCY_ALLOWANCE_DISPLAY0C,
+	 MC_SCALED_LATENCY_ALLOWANCE_DISPLAY0BB,
+	 MC_SCALED_LATENCY_ALLOWANCE_DISPLAY0CB,
+	 MC_SCALED_LATENCY_ALLOWANCE_DISPLAY0A,
+};
+
 static const struct tegra_mc_client tegra210_mc_clients[] = {
 	{
 		.id = 0x00,
@@ -1157,4 +1173,7 @@
 	.smmu = &tegra210_smmu_soc,
 	.emem_regs = tegra210_mc_emem_regs,
 	.num_emem_regs = ARRAY_SIZE(tegra210_mc_emem_regs),
+	.scaled_la_regs = tegra210_scaled_la_regs,
+	.num_scaled_la_regs = ARRAY_SIZE(tegra210_scaled_la_regs),
+	.has_scaled_la = true,
 };
-- 
1.9.1

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

end of thread, other threads:[~2018-09-26 14:26 UTC | newest]

Thread overview: 26+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2018-09-14 20:02 [RFC 00/14] Tegra210 EMC scaling Peter De Schrijver
2018-09-14 20:02 ` [RFC 01/14] memory: tegra: mc: Add Tegra210 MC emem registers Peter De Schrijver
2018-09-14 20:02 ` [RFC 02/14] clk: tegra: rename emc timing functions Peter De Schrijver
2018-09-14 20:02 ` [RFC 03/14] clk: tegra: emc: simplify parent matching Peter De Schrijver
2018-09-14 20:02 ` [RFC 04/14] clk: tegra: emc: prepare for Tegra210 parent table Peter De Schrijver
2018-09-14 20:03 ` [RFC 05/14] memory: tegra: mc: Introduce helpers Peter De Schrijver
2018-09-14 20:03 ` [RFC 06/14] memory: tegra: mc: Add support for scaled LA Peter De Schrijver
2018-09-14 20:03 ` [RFC 07/14] memory: tegra: scaled LA register for Tegra210 Peter De Schrijver
2018-09-14 20:03 ` [RFC 08/14] clk: tegra: clock changes for emc scaling Peter De Schrijver
2018-09-14 20:03 ` [RFC 09/14] memory: tegra: Add definitions shared by Tegra210 EMC scaling code Peter De Schrijver
2018-09-14 20:03 ` [RFC 10/14] memory: tegra: Add Tegra210 EMC scaling sequence Peter De Schrijver
2018-09-14 20:03 ` [RFC 11/14] memory: tegra: parse DT and costruct timing tables Peter De Schrijver
2018-09-14 20:03 ` [RFC 12/14] memory: tegra: Tegra210 EMC memory driver Peter De Schrijver
2018-09-14 20:03 ` [RFC 13/14] memory: tegra: enable Tegra210 EMC scaling driver Peter De Schrijver
2018-09-14 20:03 ` [RFC 14/14] dt-bindings: tegra: Add Tegra210 EMC binding Peter De Schrijver
2018-09-24 21:04   ` Rob Herring
2018-09-25 12:11     ` Peter De Schrijver
2018-09-25 12:51       ` Peter De Schrijver
2018-09-25 12:51         ` Peter De Schrijver
2018-09-25 14:37       ` Rob Herring
2018-09-26  8:14         ` Peter De Schrijver
2018-09-26  8:14           ` Peter De Schrijver
2018-09-25 13:03     ` Peter De Schrijver
2018-09-25 13:03       ` Peter De Schrijver
2018-09-25 14:45       ` Rob Herring
2018-09-14 21:48 [RFC 00/14] Tegra210 EMC scaling Peter De Schrijver
2018-09-14 21:48 ` [RFC 07/14] memory: tegra: scaled LA register for Tegra210 Peter De Schrijver

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.