summaryrefslogtreecommitdiff
path: root/arch/arm/mach-tegra/tegra3_emc.c
diff options
context:
space:
mode:
authorAlex Frid <afrid@nvidia.com>2012-02-14 23:00:41 -0800
committerVarun Colbert <vcolbert@nvidia.com>2012-03-13 15:30:21 -0700
commita760dd3c9bb139e1abe65e28d6bc619fbf267d2a (patch)
tree31508e3a678bd28899bf1f8e8f42125352dcc714 /arch/arm/mach-tegra/tegra3_emc.c
parent981e40176c0a9cc9ff4b72397b28a07a429d4e59 (diff)
ARM: tegra: clock: Add LPDDR2 temperature controls
Added interfaces for - reading scaled LPDDR2 temperature from MR4 register - controlling refresh rate according LPDDR2 specification For now, these interfaces are only used by debufs nodes: /sys/kernel/debug/tegra_emc/dram_temperature (read only) /sys/kernel/debug/tegra_emc/over_temp_state (read/write, 0 - set regular low temperature refresh rate, 1 - speed up refresh for high temperature) Bug 939626 Signed-off-by: Alex Frid <afrid@nvidia.com> (cherry picked from commit 373ff7e49235f6e222b42e324b6a2dc9eac633e6) Change-Id: I9cfaaeeab16d5b49acb91824fecc6b0ee8f3cdbb Reviewed-on: http://git-master/r/89349 Tested-by: Aleksandr Frid <afrid@nvidia.com> Reviewed-by: Yu-Huan Hsu <yhsu@nvidia.com>
Diffstat (limited to 'arch/arm/mach-tegra/tegra3_emc.c')
-rw-r--r--arch/arm/mach-tegra/tegra3_emc.c135
1 files changed, 135 insertions, 0 deletions
diff --git a/arch/arm/mach-tegra/tegra3_emc.c b/arch/arm/mach-tegra/tegra3_emc.c
index 23d2d7bf5a2f..746caea11634 100644
--- a/arch/arm/mach-tegra/tegra3_emc.c
+++ b/arch/arm/mach-tegra/tegra3_emc.c
@@ -190,6 +190,7 @@ static int emc_num_burst_regs;
static struct clk_mux_sel tegra_emc_clk_sel[TEGRA_EMC_TABLE_MAX_SIZE];
static struct tegra_emc_table start_timing;
static const struct tegra_emc_table *emc_timing;
+static unsigned long dram_over_temp_state = DRAM_OVER_TEMP_NONE;
static const struct tegra_emc_table *tegra_emc_table;
static int tegra_emc_table_size;
@@ -209,6 +210,8 @@ static struct {
spinlock_t spinlock;
} emc_stats;
+static DEFINE_SPINLOCK(emc_access_lock);
+
static void __iomem *emc_base = IO_ADDRESS(TEGRA_EMC_BASE);
static void __iomem *mc_base = IO_ADDRESS(TEGRA_MC_BASE);
static void __iomem *clk_base = IO_ADDRESS(TEGRA_CLK_RESET_BASE);
@@ -290,6 +293,37 @@ static inline void auto_cal_disable(void)
}
}
+static inline void set_over_temp_timing(
+ const struct tegra_emc_table *next_timing, unsigned long state)
+{
+#define REFRESH_SPEEDUP(val) \
+ do { \
+ val = ((val) & 0xFFFF0000) | (((val) & 0xFFFF) >> 2); \
+ } while (0)
+
+ u32 ref = next_timing->burst_regs[EMC_REFRESH_INDEX];
+ u32 pre_ref = next_timing->burst_regs[EMC_PRE_REFRESH_REQ_CNT_INDEX];
+ u32 dsr_cntrl = next_timing->burst_regs[EMC_DYN_SELF_REF_CONTROL_INDEX];
+
+ switch (state) {
+ case DRAM_OVER_TEMP_NONE:
+ break;
+ case DRAM_OVER_TEMP_REFRESH:
+ REFRESH_SPEEDUP(ref);
+ REFRESH_SPEEDUP(pre_ref);
+ REFRESH_SPEEDUP(dsr_cntrl);
+ break;
+ default:
+ pr_err("%s: Failed to set dram over temp state %lu\n",
+ __func__, state);
+ BUG();
+ }
+
+ __raw_writel(ref, burst_reg_addr[EMC_REFRESH_INDEX]);
+ __raw_writel(pre_ref, burst_reg_addr[EMC_PRE_REFRESH_REQ_CNT_INDEX]);
+ __raw_writel(dsr_cntrl, burst_reg_addr[EMC_DYN_SELF_REF_CONTROL_INDEX]);
+}
+
static inline void set_mc_arbiter_limits(void)
{
u32 reg = mc_readl(MC_EMEM_ARB_OUTSTANDING_REQ);
@@ -523,6 +557,9 @@ static noinline void emc_set_clock(const struct tegra_emc_table *next_timing,
continue;
__raw_writel(next_timing->burst_regs[i], burst_reg_addr[i]);
}
+ if ((dram_type == DRAM_TYPE_LPDDR2) &&
+ (dram_over_temp_state != DRAM_OVER_TEMP_NONE))
+ set_over_temp_timing(next_timing, dram_over_temp_state);
wmb();
barrier();
@@ -669,6 +706,7 @@ int tegra_emc_set_rate(unsigned long rate)
int i;
u32 clk_setting;
const struct tegra_emc_table *last_timing;
+ unsigned long flags;
if (!tegra_emc_table)
return -EINVAL;
@@ -697,10 +735,14 @@ int tegra_emc_set_rate(unsigned long rate)
last_timing = emc_timing;
clk_setting = tegra_emc_clk_sel[i].value;
+
+ spin_lock_irqsave(&emc_access_lock, flags);
emc_set_clock(&tegra_emc_table[i], last_timing, clk_setting);
if (!emc_timing)
emc_cfg_power_restore();
emc_timing = &tegra_emc_table[i];
+ spin_unlock_irqrestore(&emc_access_lock, flags);
+
emc_last_stats_update(i);
pr_debug("%s: rate %lu setting 0x%x\n", __func__, rate, clk_setting);
@@ -1027,6 +1069,70 @@ int tegra_emc_get_dram_type(void)
return dram_type;
}
+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;
+
+ /* FIXME: bit swap decoding */
+ 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;
+ }
+
+ mr4 &= LPDDR2_MR4_TEMP_MASK;
+ spin_unlock_irqrestore(&emc_access_lock, flags);
+ return mr4;
+}
+
+int tegra_emc_set_over_temp_state(unsigned long state)
+{
+ unsigned long flags;
+
+ if (dram_type != DRAM_TYPE_LPDDR2)
+ return -ENODEV;
+
+ spin_lock_irqsave(&emc_access_lock, flags);
+
+ /* Update refresh timing if state changed */
+ if (emc_timing && (dram_over_temp_state != state)) {
+ set_over_temp_timing(emc_timing, state);
+ emc_timing_update();
+ if (state != DRAM_OVER_TEMP_NONE)
+ emc_writel(EMC_REF_FORCE_CMD, EMC_REF);
+ dram_over_temp_state = state;
+ }
+ spin_unlock_irqrestore(&emc_access_lock, flags);
+ return 0;
+}
+
#ifdef CONFIG_DEBUG_FS
static struct dentry *emc_debugfs_root;
@@ -1065,6 +1171,27 @@ 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 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)
+{
+ tegra_emc_set_over_temp_state(val);
+ return 0;
+}
+DEFINE_SIMPLE_ATTRIBUTE(over_temp_state_fops, over_temp_state_get,
+ over_temp_state_set, "%llu\n");
+
static int __init tegra_emc_debug_init(void)
{
if (!tegra_emc_table)
@@ -1078,6 +1205,14 @@ static int __init tegra_emc_debug_init(void)
"stats", S_IRUGO, emc_debugfs_root, NULL, &emc_stats_fops))
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("over_temp_state", S_IRUGO | S_IWUSR,
+ emc_debugfs_root, NULL, &over_temp_state_fops))
+ goto err_out;
+
return 0;
err_out: