diff options
author | Alex Frid <afrid@nvidia.com> | 2012-02-14 23:00:41 -0800 |
---|---|---|
committer | Varun Colbert <vcolbert@nvidia.com> | 2012-03-13 15:30:21 -0700 |
commit | a760dd3c9bb139e1abe65e28d6bc619fbf267d2a (patch) | |
tree | 31508e3a678bd28899bf1f8e8f42125352dcc714 /arch/arm/mach-tegra/tegra3_emc.c | |
parent | 981e40176c0a9cc9ff4b72397b28a07a429d4e59 (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.c | 135 |
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: |