ARM: Tegra11: Add support for EMC bindings
[linux-2.6.git] / arch / arm / mach-tegra / tegra11_emc.c
index 3c88891..bc3e21d 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * arch/arm/mach-tegra/tegra11_emc.c
  *
- * Copyright (C) 2011-2012 NVIDIA Corporation
+ * Copyright (c) 2011-2013, 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
@@ -22,6 +22,7 @@
 #include <linux/clk.h>
 #include <linux/err.h>
 #include <linux/io.h>
+#include <linux/of.h>
 #include <linux/module.h>
 #include <linux/delay.h>
 #include <linux/platform_device.h>
@@ -36,7 +37,9 @@
 
 #include "clock.h"
 #include "dvfs.h"
+#include "board.h"
 #include "tegra11_emc.h"
+#include "fuse.h"
 
 #ifdef CONFIG_TEGRA_EMC_SCALING_ENABLE
 static bool emc_enable = true;
@@ -45,16 +48,22 @@ static bool emc_enable;
 #endif
 module_param(emc_enable, bool, 0644);
 
+u8 tegra_emc_bw_efficiency = 100;
+
 #define PLL_C_DIRECT_FLOOR             333500000
 #define EMC_STATUS_UPDATE_TIMEOUT      100
 #define TEGRA_EMC_TABLE_MAX_SIZE       16
 
+enum {
+       DLL_CHANGE_NONE = 0,
+       DLL_CHANGE_ON,
+       DLL_CHANGE_OFF,
+};
 
 #define EMC_CLK_DIV_SHIFT              0
-#define EMC_CLK_DIV_MAX_VALUE          0xFF
 #define EMC_CLK_DIV_MASK               (0xFF << EMC_CLK_DIV_SHIFT)
 #define EMC_CLK_SOURCE_SHIFT           29
-#define EMC_CLK_SOURCE_MAX_VALUE       3
+#define EMC_CLK_SOURCE_MASK            (0x7 << EMC_CLK_SOURCE_SHIFT)
 #define EMC_CLK_LOW_JITTER_ENABLE      (0x1 << 31)
 #define        EMC_CLK_MC_SAME_FREQ            (0x1 << 16)
 
@@ -62,24 +71,185 @@ module_param(emc_enable, bool, 0644);
 #define BURST_REG_LIST \
        DEFINE_REG(TEGRA_EMC_BASE, EMC_RC),                     \
        DEFINE_REG(TEGRA_EMC_BASE, EMC_RFC),                    \
+       DEFINE_REG(TEGRA_EMC_BASE, EMC_RFC_SLR),                \
        DEFINE_REG(TEGRA_EMC_BASE, EMC_RAS),                    \
        DEFINE_REG(TEGRA_EMC_BASE, EMC_RP),                     \
-                                                               \
-       DEFINE_REG(TEGRA_MC_BASE, MC_EMEM_ARB_MISC0),
+       DEFINE_REG(TEGRA_EMC_BASE, EMC_R2W),                    \
+       DEFINE_REG(TEGRA_EMC_BASE, EMC_W2R),                    \
+       DEFINE_REG(TEGRA_EMC_BASE, EMC_R2P),                    \
+       DEFINE_REG(TEGRA_EMC_BASE, EMC_W2P),                    \
+       DEFINE_REG(TEGRA_EMC_BASE, EMC_RD_RCD),                 \
+       DEFINE_REG(TEGRA_EMC_BASE, EMC_WR_RCD),                 \
+       DEFINE_REG(TEGRA_EMC_BASE, EMC_RRD),                    \
+       DEFINE_REG(TEGRA_EMC_BASE, EMC_REXT),                   \
+       DEFINE_REG(TEGRA_EMC_BASE, EMC_WEXT),                   \
+       DEFINE_REG(TEGRA_EMC_BASE, EMC_WDV),                    \
+       DEFINE_REG(TEGRA_EMC_BASE, EMC_WDV_MASK),               \
+       DEFINE_REG(TEGRA_EMC_BASE, EMC_IBDLY),                  \
+       DEFINE_REG(TEGRA_EMC_BASE, EMC_PUTERM_EXTRA),           \
+       DEFINE_REG(TEGRA_EMC_BASE, EMC_CDB_CNTL_2),             \
+       DEFINE_REG(TEGRA_EMC_BASE, EMC_QRST),                   \
+       DEFINE_REG(TEGRA_EMC_BASE, EMC_RDV_MASK),               \
+       DEFINE_REG(TEGRA_EMC_BASE, EMC_REFRESH),                \
+       DEFINE_REG(TEGRA_EMC_BASE, EMC_BURST_REFRESH_NUM),      \
+       DEFINE_REG(TEGRA_EMC_BASE, EMC_PRE_REFRESH_REQ_CNT),    \
+       DEFINE_REG(TEGRA_EMC_BASE, EMC_PDEX2WR),                \
+       DEFINE_REG(TEGRA_EMC_BASE, EMC_PDEX2RD),                \
+       DEFINE_REG(TEGRA_EMC_BASE, EMC_PCHG2PDEN),              \
+       DEFINE_REG(TEGRA_EMC_BASE, EMC_ACT2PDEN),               \
+       DEFINE_REG(TEGRA_EMC_BASE, EMC_AR2PDEN),                \
+       DEFINE_REG(TEGRA_EMC_BASE, EMC_RW2PDEN),                \
+       DEFINE_REG(TEGRA_EMC_BASE, EMC_TXSR),                   \
+       DEFINE_REG(TEGRA_EMC_BASE, EMC_TXSRDLL),                \
+       DEFINE_REG(TEGRA_EMC_BASE, EMC_TCKE),                   \
+       DEFINE_REG(TEGRA_EMC_BASE, EMC_TCKESR),                 \
+       DEFINE_REG(TEGRA_EMC_BASE, EMC_TPD),                    \
+       DEFINE_REG(TEGRA_EMC_BASE, EMC_TFAW),                   \
+       DEFINE_REG(TEGRA_EMC_BASE, EMC_TRPAB),                  \
+       DEFINE_REG(TEGRA_EMC_BASE, EMC_TCLKSTABLE),             \
+       DEFINE_REG(TEGRA_EMC_BASE, EMC_TCLKSTOP),               \
+       DEFINE_REG(TEGRA_EMC_BASE, EMC_TREFBW),                 \
+       DEFINE_REG(TEGRA_EMC_BASE, EMC_QUSE_EXTRA),             \
+       DEFINE_REG(TEGRA_EMC_BASE, EMC_ODT_WRITE),              \
+       DEFINE_REG(TEGRA_EMC_BASE, EMC_ODT_READ),               \
+       DEFINE_REG(TEGRA_EMC_BASE, EMC_FBIO_CFG5),              \
+       DEFINE_REG(TEGRA_EMC_BASE, EMC_CFG_DIG_DLL),            \
+       DEFINE_REG(TEGRA_EMC_BASE, EMC_CFG_DIG_DLL_PERIOD),     \
+       DEFINE_REG(TEGRA_EMC_BASE, EMC_DLL_XFORM_DQS4),         \
+       DEFINE_REG(TEGRA_EMC_BASE, EMC_DLL_XFORM_DQS5),         \
+       DEFINE_REG(TEGRA_EMC_BASE, EMC_DLL_XFORM_DQS6),         \
+       DEFINE_REG(TEGRA_EMC_BASE, EMC_DLL_XFORM_DQS7),         \
+       DEFINE_REG(TEGRA_EMC_BASE, EMC_DLL_XFORM_QUSE4),        \
+       DEFINE_REG(TEGRA_EMC_BASE, EMC_DLL_XFORM_QUSE5),        \
+       DEFINE_REG(TEGRA_EMC_BASE, EMC_DLL_XFORM_QUSE6),        \
+       DEFINE_REG(TEGRA_EMC_BASE, EMC_DLL_XFORM_QUSE7),        \
+       DEFINE_REG(TEGRA_EMC_BASE, EMC_DLI_TRIM_TXDQS4),        \
+       DEFINE_REG(TEGRA_EMC_BASE, EMC_DLI_TRIM_TXDQS5),        \
+       DEFINE_REG(TEGRA_EMC_BASE, EMC_DLI_TRIM_TXDQS6),        \
+       DEFINE_REG(TEGRA_EMC_BASE, EMC_DLI_TRIM_TXDQS7),        \
+       DEFINE_REG(TEGRA_EMC_BASE, EMC_XM2CMDPADCTRL),          \
+       DEFINE_REG(TEGRA_EMC_BASE, EMC_XM2CMDPADCTRL4),         \
+       DEFINE_REG(TEGRA_EMC_BASE, EMC_XM2DQSPADCTRL2),         \
+       DEFINE_REG(TEGRA_EMC_BASE, EMC_XM2DQPADCTRL2),          \
+       DEFINE_REG(TEGRA_EMC_BASE, EMC_XM2CLKPADCTRL),          \
+       DEFINE_REG(TEGRA_EMC_BASE, EMC_XM2COMPPADCTRL),         \
+       DEFINE_REG(TEGRA_EMC_BASE, EMC_XM2VTTGENPADCTRL),       \
+       DEFINE_REG(TEGRA_EMC_BASE, EMC_XM2VTTGENPADCTRL2),      \
+       DEFINE_REG(TEGRA_EMC_BASE, EMC_DSR_VTTGEN_DRV),         \
+       DEFINE_REG(TEGRA_EMC_BASE, EMC_TXDSRVTTGEN),            \
+       DEFINE_REG(TEGRA_EMC_BASE, EMC_FBIO_SPARE),             \
+       DEFINE_REG(TEGRA_EMC_BASE, EMC_CTT_TERM_CTRL),          \
+       DEFINE_REG(TEGRA_EMC_BASE, EMC_ZCAL_INTERVAL),          \
+       DEFINE_REG(TEGRA_EMC_BASE, EMC_ZCAL_WAIT_CNT),          \
+       DEFINE_REG(TEGRA_EMC_BASE, EMC_MRS_WAIT_CNT),           \
+       DEFINE_REG(TEGRA_EMC_BASE, EMC_MRS_WAIT_CNT2),          \
+       DEFINE_REG(TEGRA_EMC_BASE, EMC_AUTO_CAL_CONFIG2),       \
+       DEFINE_REG(TEGRA_EMC_BASE, EMC_AUTO_CAL_CONFIG3),       \
+       DEFINE_REG(TEGRA_EMC_BASE, EMC_CTT),                    \
+       DEFINE_REG(TEGRA_EMC_BASE, EMC_CTT_DURATION),           \
+       DEFINE_REG(TEGRA_EMC_BASE, EMC_DYN_SELF_REF_CONTROL),   \
+       DEFINE_REG(TEGRA_EMC_BASE, EMC_CA_TRAINING_TIMING_CNTL1),       \
+       DEFINE_REG(TEGRA_EMC_BASE, EMC_CA_TRAINING_TIMING_CNTL2),       \
+                                                                       \
+       DEFINE_REG(TEGRA_MC_BASE, MC_EMEM_ARB_CFG),             \
+       DEFINE_REG(TEGRA_MC_BASE, MC_EMEM_ARB_OUTSTANDING_REQ), \
+       DEFINE_REG(TEGRA_MC_BASE, MC_EMEM_ARB_TIMING_RCD),      \
+       DEFINE_REG(TEGRA_MC_BASE, MC_EMEM_ARB_TIMING_RP),       \
+       DEFINE_REG(TEGRA_MC_BASE, MC_EMEM_ARB_TIMING_RC),       \
+       DEFINE_REG(TEGRA_MC_BASE, MC_EMEM_ARB_TIMING_RAS),      \
+       DEFINE_REG(TEGRA_MC_BASE, MC_EMEM_ARB_TIMING_FAW),      \
+       DEFINE_REG(TEGRA_MC_BASE, MC_EMEM_ARB_TIMING_RRD),      \
+       DEFINE_REG(TEGRA_MC_BASE, MC_EMEM_ARB_TIMING_RAP2PRE),  \
+       DEFINE_REG(TEGRA_MC_BASE, MC_EMEM_ARB_TIMING_WAP2PRE),  \
+       DEFINE_REG(TEGRA_MC_BASE, MC_EMEM_ARB_TIMING_R2R),      \
+       DEFINE_REG(TEGRA_MC_BASE, MC_EMEM_ARB_TIMING_W2W),      \
+       DEFINE_REG(TEGRA_MC_BASE, MC_EMEM_ARB_TIMING_R2W),      \
+       DEFINE_REG(TEGRA_MC_BASE, MC_EMEM_ARB_TIMING_W2R),      \
+       DEFINE_REG(TEGRA_MC_BASE, MC_EMEM_ARB_DA_TURNS),        \
+       DEFINE_REG(TEGRA_MC_BASE, MC_EMEM_ARB_DA_COVERS),       \
+       DEFINE_REG(TEGRA_MC_BASE, MC_EMEM_ARB_MISC0),           \
+       DEFINE_REG(TEGRA_MC_BASE, MC_EMEM_ARB_RING1_THROTTLE),  \
+       DEFINE_REG(TEGRA_EMC_BASE, EMC_SEL_DPD_CTRL),
+
+#define BURST_UP_DOWN_REG_LIST \
+       DEFINE_REG(TEGRA_MC_BASE, MC_PTSA_GRANT_DECREMENT),     \
+       DEFINE_REG(TEGRA_MC_BASE, MC_LATENCY_ALLOWANCE_G2_0),   \
+       DEFINE_REG(TEGRA_MC_BASE, MC_LATENCY_ALLOWANCE_G2_1),   \
+       DEFINE_REG(TEGRA_MC_BASE, MC_LATENCY_ALLOWANCE_NV_0),   \
+       DEFINE_REG(TEGRA_MC_BASE, MC_LATENCY_ALLOWANCE_NV2_0),  \
+       DEFINE_REG(TEGRA_MC_BASE, MC_LATENCY_ALLOWANCE_NV_2),   \
+       DEFINE_REG(TEGRA_MC_BASE, MC_LATENCY_ALLOWANCE_NV_1),   \
+       DEFINE_REG(TEGRA_MC_BASE, MC_LATENCY_ALLOWANCE_NV2_1),  \
+       DEFINE_REG(TEGRA_MC_BASE, MC_LATENCY_ALLOWANCE_NV_3),   \
+       DEFINE_REG(TEGRA_MC_BASE, MC_LATENCY_ALLOWANCE_EPP_0),  \
+       DEFINE_REG(TEGRA_MC_BASE, MC_LATENCY_ALLOWANCE_EPP_1),
+
+#define EMC_TRIMMERS_REG_LIST \
+       DEFINE_REG(0, EMC_CDB_CNTL_1),                          \
+       DEFINE_REG(0, EMC_FBIO_CFG6),                           \
+       DEFINE_REG(0, EMC_QUSE),                                \
+       DEFINE_REG(0, EMC_EINPUT),                              \
+       DEFINE_REG(0, EMC_EINPUT_DURATION),                     \
+       DEFINE_REG(0, EMC_DLL_XFORM_DQS0),                      \
+       DEFINE_REG(0, EMC_QSAFE),                               \
+       DEFINE_REG(0, EMC_DLL_XFORM_QUSE0),                     \
+       DEFINE_REG(0, EMC_RDV),                                 \
+       DEFINE_REG(0, EMC_XM2DQSPADCTRL4),                      \
+       DEFINE_REG(0, EMC_XM2DQSPADCTRL3),                      \
+       DEFINE_REG(0, EMC_DLL_XFORM_DQ0),                       \
+       DEFINE_REG(0, EMC_AUTO_CAL_CONFIG),                     \
+       DEFINE_REG(0, EMC_DLL_XFORM_ADDR0),                     \
+       DEFINE_REG(0, EMC_XM2CLKPADCTRL2),                      \
+       DEFINE_REG(0, EMC_DLI_TRIM_TXDQS0),                     \
+       DEFINE_REG(0, EMC_DLL_XFORM_ADDR1),                     \
+       DEFINE_REG(0, EMC_DLL_XFORM_ADDR2),                     \
+       DEFINE_REG(0, EMC_DLL_XFORM_DQS1),                      \
+       DEFINE_REG(0, EMC_DLL_XFORM_DQS2),                      \
+       DEFINE_REG(0, EMC_DLL_XFORM_DQS3),                      \
+       DEFINE_REG(0, EMC_DLL_XFORM_DQ1),                       \
+       DEFINE_REG(0, EMC_DLL_XFORM_DQ2),                       \
+       DEFINE_REG(0, EMC_DLL_XFORM_DQ3),                       \
+       DEFINE_REG(0, EMC_DLI_TRIM_TXDQS1),                     \
+       DEFINE_REG(0, EMC_DLI_TRIM_TXDQS2),                     \
+       DEFINE_REG(0, EMC_DLI_TRIM_TXDQS3),                     \
+       DEFINE_REG(0, EMC_DLL_XFORM_QUSE1),                     \
+       DEFINE_REG(0, EMC_DLL_XFORM_QUSE2),                     \
+       DEFINE_REG(0, EMC_DLL_XFORM_QUSE3),
+
 
 #define DEFINE_REG(base, reg) ((base) ? (IO_ADDRESS((base)) + (reg)) : 0)
 static const void __iomem *burst_reg_addr[TEGRA11_EMC_MAX_NUM_REGS] = {
        BURST_REG_LIST
 };
+#ifndef EMULATE_CLOCK_SWITCH
+static const void __iomem *burst_up_down_reg_addr[TEGRA11_EMC_MAX_NUM_REGS] = {
+       BURST_UP_DOWN_REG_LIST
+};
+#endif
+#undef DEFINE_REG
+
+
+#define DEFINE_REG(base, reg) (reg)
+#ifndef EMULATE_CLOCK_SWITCH
+static const u32 emc_trimmer_offs[TEGRA11_EMC_MAX_NUM_REGS] = {
+       EMC_TRIMMERS_REG_LIST
+};
+#endif
 #undef DEFINE_REG
 
+
 #define DEFINE_REG(base, reg)  reg##_INDEX
 enum {
        BURST_REG_LIST
 };
 #undef DEFINE_REG
 
-static int emc_num_burst_regs;
+#define DEFINE_REG(base, reg)  reg##_TRIM_INDEX
+enum {
+       EMC_TRIMMERS_REG_LIST
+};
+#undef DEFINE_REG
+
 
 struct emc_sel {
        struct clk      *input;
@@ -93,6 +263,7 @@ static const struct tegra11_emc_table *emc_timing;
 static ktime_t clkchange_time;
 static int clkchange_delay = 100;
 
+static const u32 *dram_to_soc_bit_map;
 static const struct tegra11_emc_table *tegra_emc_table;
 static int tegra_emc_table_size;
 
@@ -112,12 +283,23 @@ static struct {
 static DEFINE_SPINLOCK(emc_access_lock);
 
 static void __iomem *emc_base = IO_ADDRESS(TEGRA_EMC_BASE);
+static void __iomem *emc0_base = IO_ADDRESS(TEGRA_EMC0_BASE);
+static void __iomem *emc1_base = IO_ADDRESS(TEGRA_EMC1_BASE);
 static void __iomem *mc_base = IO_ADDRESS(TEGRA_MC_BASE);
+static void __iomem *clk_base = IO_ADDRESS(TEGRA_CLK_RESET_BASE);
 
 static inline void emc_writel(u32 val, unsigned long addr)
 {
        writel(val, (u32)emc_base + addr);
 }
+static inline void emc0_writel(u32 val, unsigned long addr)
+{
+       writel(val, (u32)emc0_base + addr);
+}
+static inline void emc1_writel(u32 val, unsigned long addr)
+{
+       writel(val, (u32)emc1_base + addr);
+}
 static inline u32 emc_readl(unsigned long addr)
 {
        return readl((u32)emc_base + addr);
@@ -131,6 +313,20 @@ static inline u32 mc_readl(unsigned long addr)
        return readl((u32)mc_base + addr);
 }
 
+static inline void ccfifo_writel(u32 val, unsigned long addr)
+{
+       writel(val, (u32)emc_base + EMC_CCFIFO_DATA);
+       writel(addr, (u32)emc_base + EMC_CCFIFO_ADDR);
+}
+
+static int last_round_idx;
+static inline int get_start_idx(unsigned long rate)
+{
+       if (tegra_emc_table[last_round_idx].rate == rate)
+               return last_round_idx;
+       return 0;
+}
+
 static void emc_last_stats_update(int last_sel)
 {
        unsigned long flags;
@@ -152,33 +348,336 @@ static void emc_last_stats_update(int last_sel)
        spin_unlock_irqrestore(&emc_stats.spinlock, flags);
 }
 
+static int wait_for_update(u32 status_reg, u32 bit_mask, bool updated_state)
+{
+       int i;
+       for (i = 0; i < EMC_STATUS_UPDATE_TIMEOUT; i++) {
+               if (!!(emc_readl(status_reg) & bit_mask) == updated_state)
+                       return 0;
+               udelay(1);
+       }
+       return -ETIMEDOUT;
+}
+
+static inline void emc_timing_update(void)
+{
+       int err;
+
+       emc_writel(0x1, EMC_TIMING_CONTROL);
+       err = wait_for_update(EMC_STATUS,
+                             EMC_STATUS_TIMING_UPDATE_STALLED, false);
+       if (err) {
+               pr_err("%s: timing update error: %d", __func__, err);
+               BUG();
+       }
+}
+
+static inline void auto_cal_disable(void)
+{
+       int err;
+
+       emc_writel(0, EMC_AUTO_CAL_INTERVAL);
+       err = wait_for_update(EMC_AUTO_CAL_STATUS,
+                             EMC_AUTO_CAL_STATUS_ACTIVE, false);
+       if (err) {
+               pr_err("%s: disable auto-cal error: %d", __func__, err);
+               BUG();
+       }
+}
+
+static inline bool dqs_preset(const struct tegra11_emc_table *next_timing,
+                             const struct tegra11_emc_table *last_timing)
+{
+       bool ret = false;
+
+#define DQS_SET(reg, bit)                                                    \
+       do {                                                                  \
+               if ((next_timing->burst_regs[EMC_##reg##_INDEX] &             \
+                    EMC_##reg##_##bit##_ENABLE) &&                           \
+                   (!(last_timing->burst_regs[EMC_##reg##_INDEX] &           \
+                      EMC_##reg##_##bit##_ENABLE)))   {                      \
+                       emc_writel(last_timing->burst_regs[EMC_##reg##_INDEX] \
+                                  | EMC_##reg##_##bit##_ENABLE, EMC_##reg);  \
+                       ret = true;                                           \
+               }                                                             \
+       } while (0)
+
+
+#define DQS_SET_TRIM(reg, bit, ch)                                            \
+       do {                                                                   \
+               if ((next_timing->emc_trimmers_##ch[EMC_##reg##_TRIM_INDEX]    \
+                    & EMC_##reg##_##bit##_ENABLE) &&                          \
+                   (!(last_timing->emc_trimmers_##ch[EMC_##reg##_TRIM_INDEX]  \
+                      & EMC_##reg##_##bit##_ENABLE)))   {                     \
+                       emc##ch##_writel(last_timing->emc_trimmers_##ch[EMC_##reg##_TRIM_INDEX] \
+                                  | EMC_##reg##_##bit##_ENABLE, EMC_##reg);   \
+                       ret = true;                                            \
+               }                                                              \
+       } while (0)
+
+       DQS_SET(XM2DQSPADCTRL2, VREF);
+
+       return ret;
+}
+
+static inline void overwrite_mrs_wait_cnt(
+       const struct tegra11_emc_table *next_timing,
+       bool zcal_long)
+{
+       u32 reg;
+       u32 cnt = 512;
+
+       /* For ddr3 when DLL is re-started: overwrite EMC DFS table settings
+          for MRS_WAIT_LONG with maximum of MRS_WAIT_SHORT settings and
+          expected operation length. Reduce the latter by the overlapping
+          zq-calibration, if any */
+       if (zcal_long)
+               cnt -= dram_dev_num * 256;
+
+       reg = (next_timing->burst_regs[EMC_MRS_WAIT_CNT_INDEX] &
+               EMC_MRS_WAIT_CNT_SHORT_WAIT_MASK) >>
+               EMC_MRS_WAIT_CNT_SHORT_WAIT_SHIFT;
+       if (cnt < reg)
+               cnt = reg;
+
+       reg = (next_timing->burst_regs[EMC_MRS_WAIT_CNT_INDEX] &
+               (~EMC_MRS_WAIT_CNT_LONG_WAIT_MASK));
+       reg |= (cnt << EMC_MRS_WAIT_CNT_LONG_WAIT_SHIFT) &
+               EMC_MRS_WAIT_CNT_LONG_WAIT_MASK;
+
+       emc_writel(reg, EMC_MRS_WAIT_CNT);
+}
+
+static inline int get_dll_change(const struct tegra11_emc_table *next_timing,
+                                const struct tegra11_emc_table *last_timing)
+{
+       bool next_dll_enabled = !(next_timing->emc_mode_1 & 0x1);
+       bool last_dll_enabled = !(last_timing->emc_mode_1 & 0x1);
+
+       if (next_dll_enabled == last_dll_enabled)
+               return DLL_CHANGE_NONE;
+       else if (next_dll_enabled)
+               return DLL_CHANGE_ON;
+       else
+               return DLL_CHANGE_OFF;
+}
+
+static inline void set_dram_mode(const struct tegra11_emc_table *next_timing,
+                                const struct tegra11_emc_table *last_timing,
+                                int dll_change)
+{
+       if (dram_type == DRAM_TYPE_DDR3) {
+               /* first mode_1, then mode_2, then mode_reset*/
+               if (next_timing->emc_mode_1 != last_timing->emc_mode_1)
+                       ccfifo_writel(next_timing->emc_mode_1, EMC_EMRS);
+               if (next_timing->emc_mode_2 != last_timing->emc_mode_2)
+                       ccfifo_writel(next_timing->emc_mode_2, EMC_EMRS2);
+
+               if ((next_timing->emc_mode_reset !=
+                    last_timing->emc_mode_reset) ||
+                   (dll_change == DLL_CHANGE_ON)) {
+                       u32 reg = next_timing->emc_mode_reset &
+                               (~EMC_MODE_SET_DLL_RESET);
+                       if (dll_change == DLL_CHANGE_ON) {
+                               reg |= EMC_MODE_SET_DLL_RESET;
+                               reg |= EMC_MODE_SET_LONG_CNT;
+                       }
+                       ccfifo_writel(reg, EMC_MRS);
+               }
+       } else {
+               /* first mode_2, then mode_1; mode_reset is not applicable */
+               if (next_timing->emc_mode_2 != last_timing->emc_mode_2)
+                       ccfifo_writel(next_timing->emc_mode_2, EMC_MRW2);
+               if (next_timing->emc_mode_1 != last_timing->emc_mode_1)
+                       ccfifo_writel(next_timing->emc_mode_1, EMC_MRW);
+               if (next_timing->emc_mode_4 != last_timing->emc_mode_4)
+                       ccfifo_writel(next_timing->emc_mode_4, EMC_MRW4);
+       }
+}
+
+static inline void do_clock_change(u32 clk_setting)
+{
+       int err;
+
+       mc_readl(MC_EMEM_ADR_CFG);      /* completes prev writes */
+       writel(clk_setting, (u32)clk_base + emc->reg);
+       readl((u32)clk_base + emc->reg);/* completes prev write */
+
+       err = wait_for_update(EMC_INTSTATUS,
+                             EMC_INTSTATUS_CLKCHANGE_COMPLETE, true);
+       if (err) {
+               pr_err("%s: clock change completion error: %d", __func__, err);
+               BUG();
+       }
+}
+
 static noinline void emc_set_clock(const struct tegra11_emc_table *next_timing,
                                   const struct tegra11_emc_table *last_timing,
                                   u32 clk_setting)
 {
+#ifndef EMULATE_CLOCK_SWITCH
+       int i, dll_change, pre_wait;
+       bool dyn_sref_enabled, zcal_long;
+
+       u32 emc_cfg_reg = emc_readl(EMC_CFG);
+
+       dyn_sref_enabled = emc_cfg_reg & EMC_CFG_DYN_SREF_ENABLE;
+       dll_change = get_dll_change(next_timing, last_timing);
+       zcal_long = (next_timing->burst_regs[EMC_ZCAL_INTERVAL_INDEX] != 0) &&
+               (last_timing->burst_regs[EMC_ZCAL_INTERVAL_INDEX] == 0);
+
+       /* FIXME: remove steps enumeration below? */
+
+       /* 1. clear clkchange_complete interrupts */
+       emc_writel(EMC_INTSTATUS_CLKCHANGE_COMPLETE, EMC_INTSTATUS);
+
+       /* 2. disable dynamic self-refresh and preset dqs vref, then wait for
+          possible self-refresh entry/exit and/or dqs vref settled - waiting
+          before the clock change decreases worst case change stall time */
+       pre_wait = 0;
+       if (dyn_sref_enabled) {
+               emc_cfg_reg &= ~EMC_CFG_DYN_SREF_ENABLE;
+               emc_writel(emc_cfg_reg, EMC_CFG);
+               pre_wait = 5;           /* 5us+ for self-refresh entry/exit */
+       }
+
+       /* 2.5 check dq/dqs vref delay */
+       if (dqs_preset(next_timing, last_timing)) {
+               if (pre_wait < 3)
+                       pre_wait = 3;   /* 3us+ for dqs vref settled */
+       }
+       if (pre_wait) {
+               emc_timing_update();
+               udelay(pre_wait);
+       }
+
+       /* 3. disable auto-cal if vref mode is switching - removed */
+
+       /* 4. program burst shadow registers */
+       for (i = 0; i < next_timing->burst_regs_num; i++) {
+               if (!burst_reg_addr[i])
+                       continue;
+               __raw_writel(next_timing->burst_regs[i], burst_reg_addr[i]);
+       }
+       for (i = 0; i < next_timing->emc_trimmers_num; i++) {
+               __raw_writel(next_timing->emc_trimmers_0[i],
+                       (u32)emc0_base + emc_trimmer_offs[i]);
+               __raw_writel(next_timing->emc_trimmers_1[i],
+                       (u32)emc1_base + emc_trimmer_offs[i]);
+       }
+       emc_cfg_reg &= ~EMC_CFG_UPDATE_MASK;
+       emc_cfg_reg |= next_timing->emc_cfg & EMC_CFG_UPDATE_MASK;
+       emc_writel(emc_cfg_reg, EMC_CFG);
+       wmb();
+       barrier();
+
+       /* 4.1 On ddr3 when DLL is re-started predict MRS long wait count and
+          overwrite DFS table setting */
+       if ((dram_type == DRAM_TYPE_DDR3) && (dll_change == DLL_CHANGE_ON))
+               overwrite_mrs_wait_cnt(next_timing, zcal_long);
+
+       /* 5.2 disable auto-refresh to save time after clock change */
+       emc_writel(EMC_REFCTRL_DISABLE_ALL(dram_dev_num), EMC_REFCTRL);
+
+       /* 6. turn Off dll and enter self-refresh on DDR3 */
+       if (dram_type == DRAM_TYPE_DDR3) {
+               if (dll_change == DLL_CHANGE_OFF)
+                       ccfifo_writel(next_timing->emc_mode_1, EMC_EMRS);
+               ccfifo_writel(DRAM_BROADCAST(dram_dev_num) |
+                             EMC_SELF_REF_CMD_ENABLED, EMC_SELF_REF);
+       }
+
+       /* 7. flow control marker 2 */
+       ccfifo_writel(1, EMC_STALL_THEN_EXE_AFTER_CLKCHANGE);
+
+       /* 8. exit self-refresh on DDR3 */
+       if (dram_type == DRAM_TYPE_DDR3)
+               ccfifo_writel(DRAM_BROADCAST(dram_dev_num), EMC_SELF_REF);
+
+       /* 9. set dram mode registers */
+       set_dram_mode(next_timing, last_timing, dll_change);
+
+       /* 10. issue zcal command if turning zcal On */
+       if (zcal_long) {
+               ccfifo_writel(EMC_ZQ_CAL_LONG_CMD_DEV0, EMC_ZQ_CAL);
+               if (dram_dev_num > 1)
+                       ccfifo_writel(EMC_ZQ_CAL_LONG_CMD_DEV1, EMC_ZQ_CAL);
+       }
+
+       /* 10.1 dummy write to RO register to remove stall after change */
+       ccfifo_writel(0, EMC_CCFIFO_STATUS);
+
+       /* 11.5 program burst_up_down registers if emc rate is going down */
+       if (next_timing->rate < last_timing->rate) {
+               for (i = 0; i < next_timing->burst_up_down_regs_num; i++)
+                       __raw_writel(next_timing->burst_up_down_regs[i],
+                               burst_up_down_reg_addr[i]);
+               wmb();
+       }
+
+       /* 12-14. read any MC register to ensure the programming is done
+          change EMC clock source register wait for clk change completion */
+       do_clock_change(clk_setting);
+
+       /* 14.1 re-enable auto-refresh */
+       emc_writel(EMC_REFCTRL_ENABLE_ALL(dram_dev_num), EMC_REFCTRL);
+
+       /* 14.2 program burst_up_down registers if emc rate is going up */
+       if (next_timing->rate > last_timing->rate) {
+               for (i = 0; i < next_timing->burst_up_down_regs_num; i++)
+                       __raw_writel(next_timing->burst_up_down_regs[i],
+                               burst_up_down_reg_addr[i]);
+               wmb();
+       }
+
+       /* 15. restore auto-cal - removed */
+
+       /* 16. restore dynamic self-refresh */
+       if (next_timing->emc_cfg & EMC_CFG_DYN_SREF_ENABLE) {
+               emc_cfg_reg |= EMC_CFG_DYN_SREF_ENABLE;
+               emc_writel(emc_cfg_reg, EMC_CFG);
+       }
+
+       /* 17. set zcal wait count */
+       if (zcal_long)
+               emc_writel(next_timing->emc_zcal_cnt_long, EMC_ZCAL_WAIT_CNT);
+
+       /* 18. update restored timing */
+       udelay(2);
+       emc_timing_update();
+#else
        /* FIXME: implement */
        pr_info("tegra11_emc: Configuring EMC rate %lu (setting: 0x%x)\n",
                next_timing->rate, clk_setting);
+#endif
 }
 
 static inline void emc_get_timing(struct tegra11_emc_table *timing)
 {
        int i;
 
-       for (i = 0; i < emc_num_burst_regs; i++) {
+       /* burst and trimmers updates depends on previous state; burst_up_down
+          are stateless */
+       for (i = 0; i < timing->burst_regs_num; i++) {
                if (burst_reg_addr[i])
                        timing->burst_regs[i] = __raw_readl(burst_reg_addr[i]);
                else
                        timing->burst_regs[i] = 0;
        }
+       for (i = 0; i < timing->emc_trimmers_num; i++) {
+               timing->emc_trimmers_0[i] =
+                       __raw_readl((u32)emc0_base + emc_trimmer_offs[i]);
+               timing->emc_trimmers_1[i] =
+                       __raw_readl((u32)emc1_base + emc_trimmer_offs[i]);
+       }
        timing->emc_acal_interval = 0;
        timing->emc_zcal_cnt_long = 0;
        timing->emc_mode_reset = 0;
        timing->emc_mode_1 = 0;
        timing->emc_mode_2 = 0;
-       timing->emc_periodic_qrst = (emc_readl(EMC_CFG) &
-                                    EMC_CFG_PERIODIC_QRST) ? 1 : 0;
-       timing->rate = clk_get_rate_locked(emc);
+       timing->emc_mode_4 = 0;
+       timing->emc_cfg = emc_readl(EMC_CFG);
+       timing->rate = clk_get_rate_locked(emc) / 1000;
 }
 
 /* The EMC registers have shadow registers. When the EMC clock is updated
@@ -202,7 +701,8 @@ int tegra_emc_set_rate(unsigned long rate)
        /* Table entries specify rate in kHz */
        rate = rate / 1000;
 
-       for (i = 0; i < tegra_emc_table_size; i++) {
+       i = get_start_idx(rate);
+       for (; i < tegra_emc_table_size; i++) {
                if (tegra_emc_clk_sel[i].input == NULL)
                        continue;       /* invalid entry */
 
@@ -241,9 +741,10 @@ int tegra_emc_set_rate(unsigned long rate)
        return 0;
 }
 
-long tegra_emc_round_rate(unsigned long rate)
+long tegra_emc_round_rate_updown(unsigned long rate, bool up)
 {
        int i;
+       unsigned long table_rate;
 
        if (!tegra_emc_table)
                return clk_get_rate_locked(emc); /* no table - no rate change */
@@ -256,14 +757,20 @@ long tegra_emc_round_rate(unsigned long rate)
        /* Table entries specify rate in kHz */
        rate = rate / 1000;
 
-       for (i = 0; i < tegra_emc_table_size; i++) {
+       i = get_start_idx(rate);
+       for (; i < tegra_emc_table_size; i++) {
                if (tegra_emc_clk_sel[i].input == NULL)
                        continue;       /* invalid entry */
 
-               if (tegra_emc_table[i].rate >= rate) {
-                       pr_debug("%s: using %lu\n",
-                                __func__, tegra_emc_table[i].rate);
-                       return tegra_emc_table[i].rate * 1000;
+               table_rate = tegra_emc_table[i].rate;
+               if (table_rate >= rate) {
+                       if (!up && i && (table_rate > rate)) {
+                               i--;
+                               table_rate = tegra_emc_table[i].rate;
+                       }
+                       pr_debug("%s: using %lu\n", __func__, table_rate);
+                       last_round_idx = i;
+                       return table_rate * 1000;
                }
        }
 
@@ -287,7 +794,8 @@ struct clk *tegra_emc_predict_parent(unsigned long rate, u32 *div_value)
        /* Table entries specify rate in kHz */
        rate = rate / 1000;
 
-       for (i = 0; i < tegra_emc_table_size; i++) {
+       i = get_start_idx(rate);
+       for (; i < tegra_emc_table_size; i++) {
                if (tegra_emc_table[i].rate == rate) {
                        struct clk *p = tegra_emc_clk_sel[i].input;
 
@@ -310,7 +818,7 @@ bool tegra_emc_is_parent_ready(unsigned long rate, struct clk **parent,
        struct clk *p = NULL;
        unsigned long p_rate = 0;
 
-       if (!tegra_emc_table || !emc_enable)
+       if (!tegra_emc_table)
                return true;
 
        pr_debug("%s: %lu\n", __func__, rate);
@@ -318,7 +826,8 @@ bool tegra_emc_is_parent_ready(unsigned long rate, struct clk **parent,
        /* Table entries specify rate in kHz */
        rate = rate / 1000;
 
-       for (i = 0; i < tegra_emc_table_size; i++) {
+       i = get_start_idx(rate);
+       for (; i < tegra_emc_table_size; i++) {
                if (tegra_emc_table[i].rate == rate) {
                        p = tegra_emc_clk_sel[i].input;
                        if (!p)
@@ -335,6 +844,20 @@ bool tegra_emc_is_parent_ready(unsigned long rate, struct clk **parent,
        if (!p)
                return true;
 
+#ifdef CONFIG_TEGRA_PLLM_SCALED
+       /*
+        * Table match found, but parent is not ready - check if backup entry
+        * was found during initialization, and return the respective backup
+        * rate
+        */
+       if (emc->shared_bus_backup.input &&
+           (emc->shared_bus_backup.input != p)) {
+               *parent = p;
+               *parent_rate = p_rate;
+               *backup_rate = emc->shared_bus_backup.bus_rate;
+               return false;
+       }
+#else
        /*
         * Table match found, but parent is not ready - continue search
         * for backup rate: min rate above requested that has different
@@ -354,76 +877,97 @@ bool tegra_emc_is_parent_ready(unsigned long rate, struct clk **parent,
                        return false;
                }
        }
-
+#endif
        /* Parent is not ready, and no backup found */
        *backup_rate = -EINVAL;
        return false;
 }
 
+static inline const struct clk_mux_sel *get_emc_input(u32 val)
+{
+       const struct clk_mux_sel *sel;
+
+       for (sel = emc->inputs; sel->input != NULL; sel++) {
+               if (sel->value == val)
+                       break;
+       }
+       return sel;
+}
+
 static int find_matching_input(const struct tegra11_emc_table *table,
                        struct clk *pll_c, struct emc_sel *emc_clk_sel)
 {
-       u32 div_value = 0;
+       u32 div_value = (table->src_sel_reg & EMC_CLK_DIV_MASK) >>
+               EMC_CLK_DIV_SHIFT;
+       u32 src_value = (table->src_sel_reg & EMC_CLK_SOURCE_MASK) >>
+               EMC_CLK_SOURCE_SHIFT;
        unsigned long input_rate = 0;
        unsigned long table_rate = table->rate * 1000; /* table rate in kHz */
-       struct clk *src = tegra_get_clock_by_name(table->src_name);
-       const struct clk_mux_sel *sel;
+       const struct clk_mux_sel *sel = get_emc_input(src_value);
 
-       for (sel = emc->inputs; sel->input != NULL; sel++) {
-               if (sel->input != src)
-                       continue;
-               /*
-                * PLLC is a scalable source. For rates below PLL_C_DIRECT_FLOOR
-                * configure PLLC at double rate and set 1:2 divider, otherwise
-                * configure PLLC at target rate with divider 1:1.
-                */
-               if (src == pll_c) {
-#ifdef CONFIG_TEGRA_DUAL_CBUS
-                       if (table_rate < PLL_C_DIRECT_FLOOR) {
-                               input_rate = 2 * table_rate;
-                               div_value = 2;
-                       } else {
-                               input_rate = table_rate;
-                               div_value = 0;
-                       }
-                       break;
+#ifdef CONFIG_TEGRA_PLLM_SCALED
+       struct clk *scalable_pll = emc->parent; /* pll_m is a boot parent */
 #else
-                       continue;       /* pll_c is used for cbus - skip */
+       struct clk *scalable_pll = pll_c;
 #endif
-               }
+       pr_info_once("tegra: %s is selected as scalable EMC clock source\n",
+                    scalable_pll->name);
 
-               /*
-                * All other clock sources are fixed rate sources, and must
-                * run at rate that is an exact multiple of the target.
-                */
-               input_rate = clk_get_rate(src);
-
-               if ((input_rate >= table_rate) &&
-                    (input_rate % table_rate == 0)) {
-                       div_value = 2 * input_rate / table_rate - 2;
-                       break;
-               }
+       if (div_value & 0x1) {
+               pr_warn("tegra: invalid odd divider for EMC rate %lu\n",
+                       table_rate);
+               return -EINVAL;
        }
-
-       if (!sel->input || (sel->value > EMC_CLK_SOURCE_MAX_VALUE) ||
-           (div_value > EMC_CLK_DIV_MAX_VALUE)) {
+       if (!sel->input) {
                pr_warn("tegra: no matching input found for EMC rate %lu\n",
                        table_rate);
                return -EINVAL;
        }
+       if (div_value && (table->src_sel_reg & EMC_CLK_LOW_JITTER_ENABLE)) {
+               pr_warn("tegra: invalid LJ path for EMC rate %lu\n",
+                       table_rate);
+               return -EINVAL;
+       }
+       if (!(table->src_sel_reg & EMC_CLK_MC_SAME_FREQ) !=
+           !(MC_EMEM_ARB_MISC0_EMC_SAME_FREQ &
+             table->burst_regs[MC_EMEM_ARB_MISC0_INDEX])) {
+               pr_warn("tegra: ambiguous EMC to MC ratio for EMC rate %lu\n",
+                       table_rate);
+               return -EINVAL;
+       }
 
-       emc_clk_sel->input = sel->input;
-       emc_clk_sel->input_rate = input_rate;
+#ifndef CONFIG_TEGRA_DUAL_CBUS
+       if (sel->input == pll_c) {
+               pr_warn("tegra: %s is cbus source: no EMC rate %lu support\n",
+                       sel->input->name, table_rate);
+               return -EINVAL;
+       }
+#endif
 
-       /* Get ready emc clock selection settings for this table rate */
-       emc_clk_sel->value = sel->value << EMC_CLK_SOURCE_SHIFT;
-       emc_clk_sel->value |= (div_value << EMC_CLK_DIV_SHIFT);
-       if ((div_value == 0) && (emc_clk_sel->input == emc->parent))
-               emc_clk_sel->value |= EMC_CLK_LOW_JITTER_ENABLE;
+       if (sel->input == scalable_pll) {
+               input_rate = table_rate * (1 + div_value / 2);
+       } else {
+               /* all other sources are fixed, must exactly match the rate */
+               input_rate = clk_get_rate(sel->input);
+               if (input_rate != (table_rate * (1 + div_value / 2))) {
+                       pr_warn("tegra: EMC rate %lu does not match %s rate %lu\n",
+                               table_rate, sel->input->name, input_rate);
+                       return -EINVAL;
+               }
+       }
 
-       if (MC_EMEM_ARB_MISC0_EMC_SAME_FREQ &
-           table->burst_regs[MC_EMEM_ARB_MISC0_INDEX])
-               emc_clk_sel->value |= EMC_CLK_MC_SAME_FREQ;
+#ifdef CONFIG_TEGRA_PLLM_SCALED
+               if (sel->input == pll_c) {
+                       /* maybe overwritten in a loop - end up at max rate
+                          from pll_c */
+                       emc->shared_bus_backup.input = pll_c;
+                       emc->shared_bus_backup.bus_rate = table_rate;
+               }
+#endif
+       /* Get ready emc clock selection settings for this table rate */
+       emc_clk_sel->input = sel->input;
+       emc_clk_sel->input_rate = input_rate;
+       emc_clk_sel->value = table->src_sel_reg;
 
        return 0;
 }
@@ -454,11 +998,295 @@ static void adjust_emc_dvfs_table(const struct tegra11_emc_table *table,
        }
 }
 
+#ifdef CONFIG_TEGRA_PLLM_SCALED
+/* When pll_m is scaled, pll_c must provide backup rate;
+   if not - remove rates that require pll_m scaling */
+static int purge_emc_table(unsigned long max_rate)
+{
+       int i;
+       int ret = 0;
+
+       if (emc->shared_bus_backup.input)
+               return ret;
+
+       pr_warn("tegra: selected pll_m scaling option but no backup source:\n");
+       pr_warn("       removed not supported entries from the table:\n");
+
+       /* made all entries with non matching rate invalid */
+       for (i = 0; i < tegra_emc_table_size; i++) {
+               struct emc_sel *sel = &tegra_emc_clk_sel[i];
+               if (sel->input) {
+                       if (clk_get_rate(sel->input) != sel->input_rate) {
+                               pr_warn("       EMC rate %lu\n",
+                                       tegra_emc_table[i].rate * 1000);
+                               sel->input = NULL;
+                               sel->input_rate = 0;
+                               sel->value = 0;
+                               if (max_rate == tegra_emc_table[i].rate)
+                                       ret = -EINVAL;
+                       }
+               }
+       }
+       return ret;
+}
+#else
+/* When pll_m is fixed @ max EMC rate, it always provides backup for pll_c */
+#define purge_emc_table(max_rate) (0)
+#endif
+
+#ifdef CONFIG_OF
+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(np, "nvidia,ram-code", &reg))
+                       continue;
+               if (reg == tegra_bct_strapping)
+                       return of_node_get(iter);
+       }
+
+       return NULL;
+}
+
+static struct tegra11_emc_pdata *tegra_emc_dt_parse_pdata(
+               struct platform_device *pdev)
+{
+       struct device_node *np = pdev->dev.of_node;
+       struct device_node *tnp, *iter;
+       struct tegra11_emc_pdata *pdata;
+       int ret, i, num_tables;
+
+       if (!np)
+               return NULL;
+
+       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);
+       } else
+               tnp = of_node_get(np);
+
+       if (!tnp)
+               return NULL;
+
+       num_tables = 0;
+       for_each_child_of_node(tnp, iter)
+               if (of_device_is_compatible(iter, "nvidia,tegra11-emc-table"))
+                       num_tables++;
+
+       if (!num_tables) {
+               pdata = NULL;
+               goto out;
+       }
+
+       pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL);
+       pdata->tables = devm_kzalloc(&pdev->dev,
+                               sizeof(*pdata->tables) * num_tables,
+                                       GFP_KERNEL);
+
+       i = 0;
+       for_each_child_of_node(tnp, iter) {
+               u32 u;
+               const char *source_name;
+
+               ret = of_property_read_u32(iter, "nvidia,revision", &u);
+               if (ret) {
+                       dev_err(&pdev->dev, "no revision in %s\n",
+                               iter->full_name);
+                       continue;
+               }
+               pdata->tables[i].rev = u;
+
+               ret = of_property_read_u32(iter, "clock-frequency", &u);
+               if (ret) {
+                       dev_err(&pdev->dev, "no clock-frequency in %s\n",
+                               iter->full_name);
+                       continue;
+               }
+               pdata->tables[i].rate = u;
+
+               ret = of_property_read_u32(iter, "nvidia,emc-min-mv", &u);
+               if (ret) {
+                       dev_err(&pdev->dev, "no emc-min-mv in %s\n",
+                               iter->full_name);
+                       continue;
+               }
+               pdata->tables[i].emc_min_mv = u;
+
+               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;
+               }
+               pdata->tables[i].src_name = source_name;
+
+               ret = of_property_read_u32(iter, "nvidia,src-sel-reg", &u);
+               if (ret) {
+                       dev_err(&pdev->dev, "no src-sel-reg in %s\n",
+                               iter->full_name);
+                       continue;
+               }
+               pdata->tables[i].src_sel_reg = u;
+
+               ret = of_property_read_u32(iter, "nvidia,burst-regs-num", &u);
+               if (ret) {
+                       dev_err(&pdev->dev, "no burst-regs-num in %s\n",
+                               iter->full_name);
+                       continue;
+               }
+               pdata->tables[i].burst_regs_num = u;
+
+               ret = of_property_read_u32(iter, "nvidia,emc-trimmers-num", &u);
+               if (ret) {
+                       dev_err(&pdev->dev, "no emc-trimmers-num in %s\n",
+                               iter->full_name);
+                       continue;
+               }
+               pdata->tables[i].emc_trimmers_num = u;
+
+               ret = of_property_read_u32(iter,
+                                       "nvidia,burst-up-down-regs-num", &u);
+               if (ret) {
+                       dev_err(&pdev->dev, "no burst-up-down-regs-num in %s\n",
+                               iter->full_name);
+                       continue;
+               }
+               pdata->tables[i].burst_up_down_regs_num = u;
+
+               ret = of_property_read_u32_array(iter, "nvidia,emc-registers",
+                                       pdata->tables[i].burst_regs,
+                                       pdata->tables[i].burst_regs_num);
+               if (ret) {
+                       dev_err(&pdev->dev,
+                               "malformed emc-registers property in %s\n",
+                               iter->full_name);
+                       continue;
+               }
+
+               ret = of_property_read_u32_array(iter, "nvidia,emc-trimmers-0",
+                                       pdata->tables[i].emc_trimmers_0,
+                                       pdata->tables[i].emc_trimmers_num);
+               if (ret) {
+                       dev_err(&pdev->dev,
+                               "malformed emc-trimmers-0 property in %s\n",
+                               iter->full_name);
+                       continue;
+               }
+
+               ret = of_property_read_u32_array(iter, "nvidia,emc-trimmers-1",
+                                       pdata->tables[i].emc_trimmers_1,
+                                       pdata->tables[i].emc_trimmers_num);
+               if (ret) {
+                       dev_err(&pdev->dev,
+                               "malformed emc-trimmers-1 property in %s\n",
+                               iter->full_name);
+                       continue;
+               }
+
+               ret = of_property_read_u32_array(iter,
+                               "nvidia,emc-burst-up-down-regs",
+                               pdata->tables[i].burst_up_down_regs,
+                               pdata->tables[i].burst_up_down_regs_num);
+               if (ret) {
+                       dev_err(&pdev->dev,
+                               "malformed emc-burst-up-down-regs property in %s\n",
+                               iter->full_name);
+                       continue;
+               }
+
+               ret = of_property_read_u32(iter, "nvidia,emc-zcal-cnt-long",
+                                       &pdata->tables[i].emc_zcal_cnt_long);
+               if (ret) {
+                       dev_err(&pdev->dev,
+                               "malformed emc-zcal-cnt-long property in %s\n",
+                               iter->full_name);
+                       continue;
+               }
+
+               ret = of_property_read_u32(iter, "nvidia,emc-acal-interval",
+                                       &pdata->tables[i].emc_acal_interval);
+               if (ret) {
+                       dev_err(&pdev->dev,
+                               "malformed emc-acal-interval property in %s\n",
+                               iter->full_name);
+                       continue;
+               }
+
+               ret = of_property_read_u32(iter, "nvidia,emc-cfg",
+                                       &pdata->tables[i].emc_cfg);
+               if (ret) {
+                       dev_err(&pdev->dev,
+                               "malformed emc-cfg property in %s\n",
+                               iter->full_name);
+                       continue;
+               }
+
+               ret = of_property_read_u32(iter, "nvidia,emc-mode-reset",
+                                       &pdata->tables[i].emc_mode_reset);
+               if (ret) {
+                       dev_err(&pdev->dev,
+                               "malformed emc-mode-reset property in %s\n",
+                               iter->full_name);
+                       continue;
+               }
+
+               ret = of_property_read_u32(iter, "nvidia,emc-mode-1",
+                                       &pdata->tables[i].emc_mode_1);
+               if (ret) {
+                       dev_err(&pdev->dev,
+                               "malformed emc-mode-1 property in %s\n",
+                               iter->full_name);
+                       continue;
+               }
+
+               ret = of_property_read_u32(iter, "nvidia,emc-mode-2",
+                                       &pdata->tables[i].emc_mode_2);
+               if (ret) {
+                       dev_err(&pdev->dev,
+                               "malformed emc-mode-2 property in %s\n",
+                               iter->full_name);
+                       continue;
+               }
+
+               ret = of_property_read_u32(iter, "nvidia,emc-mode-4",
+                                       &pdata->tables[i].emc_mode_4);
+               if (ret) {
+                       dev_err(&pdev->dev,
+                               "malformed emc-mode-4 property in %s\n",
+                               iter->full_name);
+                       continue;
+               }
+
+               of_property_read_u32(iter, "nvidia,emc-clock-latency-change",
+                                       &pdata->tables[i].clock_change_latency);
+               i++;
+       }
+       pdata->num_tables = i;
+
+out:
+       of_node_put(tnp);
+       return pdata;
+}
+#else
+static struct tegra_emc_pdata *tegra_emc_dt_parse_pdata(
+                                       struct platform_device *pdev)
+{
+       return NULL;
+}
+#endif
+
 static int init_emc_table(const struct tegra11_emc_table *table, int table_size)
 {
        int i, mv;
        u32 reg;
        bool max_entry = false;
+       bool emc_max_dvfs_sel = get_emc_max_dvfs();
        unsigned long boot_rate, max_rate;
        struct clk *pll_c = tegra_get_clock_by_name("pll_c");
 
@@ -467,9 +1295,6 @@ static int init_emc_table(const struct tegra11_emc_table *table, int table_size)
        emc_stats.last_update = get_jiffies_64();
        emc_stats.last_sel = TEGRA_EMC_TABLE_MAX_SIZE;
 
-       boot_rate = clk_get_rate(emc) / 1000;
-       max_rate = clk_get_max_rate(emc) / 1000;
-
        if ((dram_type != DRAM_TYPE_DDR3) && (dram_type != DRAM_TYPE_LPDDR2)) {
                pr_err("tegra: not supported DRAM type %u\n", dram_type);
                return -ENODATA;
@@ -486,10 +1311,16 @@ static int init_emc_table(const struct tegra11_emc_table *table, int table_size)
                return -ENODATA;
        }
 
+       boot_rate = clk_get_rate(emc) / 1000;
+       max_rate = clk_get_rate(emc->parent) / 1000;
+
        tegra_emc_table_size = min(table_size, TEGRA_EMC_TABLE_MAX_SIZE);
        switch (table[0].rev) {
        case 0x40:
-               emc_num_burst_regs = 105; /* FIXME: actual number */
+       case 0x41:
+       case 0x42:
+               start_timing.burst_regs_num = table[0].burst_regs_num;
+               start_timing.emc_trimmers_num = table[0].emc_trimmers_num;
                break;
        default:
                pr_err("tegra: invalid EMC DFS table: unknown rev 0x%x\n",
@@ -515,8 +1346,17 @@ static int init_emc_table(const struct tegra11_emc_table *table, int table_size)
                if (table_rate == boot_rate)
                        emc_stats.last_sel = i;
 
-               if (table_rate == max_rate)
+               if (emc_max_dvfs_sel) {
+                       /* EMC max rate = max table entry above boot pll_m */
+                       if (table_rate >= max_rate) {
+                               max_rate = table_rate;
+                               max_entry = true;
+                       }
+               } else if (table_rate == max_rate) {
+                       /* EMC max rate = boot pll_m rate */
                        max_entry = true;
+                       break;
+               }
        }
 
        /* Validate EMC rate and voltage limits */
@@ -528,6 +1368,16 @@ static int init_emc_table(const struct tegra11_emc_table *table, int table_size)
 
        tegra_emc_table = table;
 
+       /*
+        * Purge rates that cannot be reached because table does not specify
+        * proper backup source. If maximum rate was purged, fall back on boot
+        * pll_m rate as maximum limit. In any case propagate new maximum limit
+        * down stream to shared users, and check it against nominal voltage.
+        */
+       if (purge_emc_table(max_rate))
+               max_rate = clk_get_rate(emc->parent) / 1000;
+       tegra_init_max_rate(emc, max_rate * 1000);
+
        if (emc->dvfs) {
                adjust_emc_dvfs_table(tegra_emc_table, tegra_emc_table_size);
                mv = tegra_dvfs_predict_millivolts(emc, max_rate * 1000);
@@ -555,6 +1405,9 @@ static int __devinit tegra11_emc_probe(struct platform_device *pdev)
        struct tegra11_emc_pdata *pdata;
        struct resource *res;
 
+       if (tegra_emc_table)
+               return -EINVAL;
+
        res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
        if (!res) {
                dev_err(&pdev->dev, "missing register base\n");
@@ -562,6 +1415,10 @@ static int __devinit tegra11_emc_probe(struct platform_device *pdev)
        }
 
        pdata = pdev->dev.platform_data;
+
+       if (!pdata)
+               pdata = (struct tegra11_emc_pdata *)tegra_emc_dt_parse_pdata(pdev);
+
        if (!pdata) {
                dev_err(&pdev->dev, "missing platform data\n");
                return -ENODATA;
@@ -570,17 +1427,34 @@ static int __devinit tegra11_emc_probe(struct platform_device *pdev)
        return init_emc_table(pdata->tables, pdata->num_tables);
 }
 
+static struct of_device_id tegra11_emc_of_match[] __devinitdata = {
+       { .compatible = "nvidia,tegra11-emc", },
+       { },
+};
+
 static struct platform_driver tegra11_emc_driver = {
        .driver         = {
                .name   = "tegra-emc",
                .owner  = THIS_MODULE,
+               .of_match_table = tegra11_emc_of_match,
        },
        .probe          = tegra11_emc_probe,
 };
 
+static struct emc_iso_usage tegra11_emc_iso_usage[] = {
+       { BIT(EMC_USER_DC),                     80 },
+       { BIT(EMC_USER_DC) | BIT(EMC_USER_VI),  45 },
+};
+
 int __init tegra11_emc_init(void)
 {
-       return platform_driver_register(&tegra11_emc_driver);
+       int ret = platform_driver_register(&tegra11_emc_driver);
+       if (!ret) {
+               tegra_clk_preset_emc_monitor();
+               tegra_emc_iso_usage_table_init(tegra11_emc_iso_usage,
+                       ARRAY_SIZE(tegra11_emc_iso_usage));
+       }
+       return ret;
 }
 
 void tegra_emc_timing_invalidate(void)
@@ -603,6 +1477,72 @@ int tegra_emc_get_dram_type(void)
        return dram_type;
 }
 
+static u32 soc_to_dram_bit_swap(u32 soc_val, u32 dram_mask, u32 dram_shift)
+{
+       int bit;
+       u32 dram_val = 0;
+
+       /* tegra clocks definitions use shifted mask always */
+       if (!dram_to_soc_bit_map)
+               return soc_val & dram_mask;
+
+       for (bit = dram_shift; bit < 32; bit++) {
+               u32 dram_bit_mask = 0x1 << bit;
+               u32 soc_bit_mask = dram_to_soc_bit_map[bit];
+
+               if (!(dram_bit_mask & dram_mask))
+                       break;
+
+               if (soc_bit_mask & soc_val)
+                       dram_val |= dram_bit_mask;
+       }
+
+       return dram_val;
+}
+
+static int emc_read_mrr(int dev, int addr)
+{
+       int ret;
+       u32 val;
+
+       if (dram_type != DRAM_TYPE_LPDDR2)
+               return -ENODEV;
+
+       ret = wait_for_update(EMC_STATUS, EMC_STATUS_MRR_DIVLD, false);
+       if (ret)
+               return ret;
+
+       val = dev ? DRAM_DEV_SEL_1 : DRAM_DEV_SEL_0;
+       val |= (addr << EMC_MRR_MA_SHIFT) & EMC_MRR_MA_MASK;
+       emc_writel(val, EMC_MRR);
+
+       ret = wait_for_update(EMC_STATUS, EMC_STATUS_MRR_DIVLD, true);
+       if (ret)
+               return ret;
+
+       val = emc_readl(EMC_MRR) & EMC_MRR_DATA_MASK;
+       return val;
+}
+
+int tegra_emc_get_dram_temperature(void)
+{
+       int mr4;
+       unsigned long flags;
+
+       spin_lock_irqsave(&emc_access_lock, flags);
+
+       mr4 = emc_read_mrr(0, 4);
+       if (IS_ERR_VALUE(mr4)) {
+               spin_unlock_irqrestore(&emc_access_lock, flags);
+               return mr4;
+       }
+       spin_unlock_irqrestore(&emc_access_lock, flags);
+
+       mr4 = soc_to_dram_bit_swap(
+               mr4, LPDDR2_MR4_TEMP_MASK, LPDDR2_MR4_TEMP_SHIFT);
+       return mr4;
+}
+
 #ifdef CONFIG_DEBUG_FS
 
 static struct dentry *emc_debugfs_root;
@@ -641,6 +1581,30 @@ static const struct file_operations emc_stats_fops = {
        .release        = single_release,
 };
 
+static int dram_temperature_get(void *data, u64 *val)
+{
+       *val = tegra_emc_get_dram_temperature();
+       return 0;
+}
+DEFINE_SIMPLE_ATTRIBUTE(dram_temperature_fops, dram_temperature_get,
+                       NULL, "%lld\n");
+
+static int efficiency_get(void *data, u64 *val)
+{
+       *val = tegra_emc_bw_efficiency;
+       return 0;
+}
+static int efficiency_set(void *data, u64 val)
+{
+       tegra_emc_bw_efficiency = (val > 100) ? 100 : val;
+       if (emc)
+               tegra_clk_shared_bus_update(emc);
+
+       return 0;
+}
+DEFINE_SIMPLE_ATTRIBUTE(efficiency_fops, efficiency_get,
+                       efficiency_set, "%llu\n");
+
 static int __init tegra_emc_debug_init(void)
 {
        if (!tegra_emc_table)
@@ -658,6 +1622,17 @@ static int __init tegra_emc_debug_init(void)
                emc_debugfs_root, (u32 *)&clkchange_delay))
                goto err_out;
 
+       if (!debugfs_create_file("dram_temperature", S_IRUGO, emc_debugfs_root,
+                                NULL, &dram_temperature_fops))
+               goto err_out;
+
+       if (!debugfs_create_file("efficiency", S_IRUGO | S_IWUSR,
+                                emc_debugfs_root, NULL, &efficiency_fops))
+               goto err_out;
+
+       if (tegra_emc_iso_usage_debugfs_init(emc_debugfs_root))
+               goto err_out;
+
        return 0;
 
 err_out: