Commit 0553d7b2 authored by Thierry Reding's avatar Thierry Reding

memory: tegra: Support derated timings on Tegra210

Derated timings are used to ensure that the memory chips keep operating
correctly at high temperatures. This adds code to support polling of the
chip operating state when high temperatures are measured on the chip and
change the refresh mode accordingly. Under very high temperatures, the
driver will switch to the derated tables to ensure proper operation of
the memory chips.
Signed-off-by: default avatarThierry Reding <treding@nvidia.com>
parent 9b9d8632
...@@ -1070,6 +1070,9 @@ static void tegra210_emc_r21021_set_clock(struct tegra210_emc *emc, u32 clksrc) ...@@ -1070,6 +1070,9 @@ static void tegra210_emc_r21021_set_clock(struct tegra210_emc *emc, u32 clksrc)
emc_writel(emc, value, offset); emc_writel(emc, value, offset);
} }
/* SW addition: do EMC refresh adjustment here. */
tegra210_emc_adjust_timing(emc, next);
if (dram_type == DRAM_TYPE_LPDDR4) { if (dram_type == DRAM_TYPE_LPDDR4) {
value = (23 << EMC_MRW_MRW_MA_SHIFT) | value = (23 << EMC_MRW_MRW_MA_SHIFT) |
(next->run_clocks & EMC_MRW_MRW_OP_MASK); (next->run_clocks & EMC_MRW_MRW_OP_MASK);
......
This diff is collapsed.
...@@ -13,32 +13,63 @@ static int tegra210_emc_table_device_init(struct reserved_mem *rmem, ...@@ -13,32 +13,63 @@ static int tegra210_emc_table_device_init(struct reserved_mem *rmem,
struct device *dev) struct device *dev)
{ {
struct tegra210_emc *emc = dev_get_drvdata(dev); struct tegra210_emc *emc = dev_get_drvdata(dev);
unsigned int i; struct tegra210_emc_timing *timings;
unsigned int i, count = 0;
emc->timings = memremap(rmem->base, rmem->size, MEMREMAP_WB); timings = memremap(rmem->base, rmem->size, MEMREMAP_WB);
if (!emc->timings) { if (!timings) {
dev_err(dev, "failed to map EMC table\n"); dev_err(dev, "failed to map EMC table\n");
return -ENOMEM; return -ENOMEM;
} }
emc->num_timings = 0; count = 0;
for (i = 0; i < TEGRA_EMC_MAX_FREQS; i++) { for (i = 0; i < TEGRA_EMC_MAX_FREQS; i++) {
if (emc->timings[i].revision == 0) if (timings[i].revision == 0)
break; break;
emc->num_timings++; count++;
} }
/* only the nominal and derated tables are expected */
if (emc->derated) {
dev_warn(dev, "excess EMC table '%s'\n", rmem->name);
goto out;
}
if (emc->nominal) {
if (count != emc->num_timings) {
dev_warn(dev, "%u derated vs. %u nominal entries\n",
count, emc->num_timings);
memunmap(timings);
return -EINVAL;
}
emc->derated = timings;
} else {
emc->num_timings = count;
emc->nominal = timings;
}
out:
/* keep track of which table this is */
rmem->priv = timings;
return 0; return 0;
} }
static void tegra210_emc_table_device_release(struct reserved_mem *rmem, static void tegra210_emc_table_device_release(struct reserved_mem *rmem,
struct device *dev) struct device *dev)
{ {
struct tegra210_emc_timing *timings = rmem->priv;
struct tegra210_emc *emc = dev_get_drvdata(dev); struct tegra210_emc *emc = dev_get_drvdata(dev);
memunmap(emc->timings); if ((emc->nominal && timings != emc->nominal) &&
(emc->derated && timings != emc->derated))
dev_warn(dev, "trying to release unassigned EMC table '%s'\n",
rmem->name);
memunmap(timings);
} }
static const struct reserved_mem_ops tegra210_emc_table_ops = { static const struct reserved_mem_ops tegra210_emc_table_ops = {
......
...@@ -83,6 +83,7 @@ ...@@ -83,6 +83,7 @@
#define EMC_EMRS 0xd0 #define EMC_EMRS 0xd0
#define EMC_EMRS_USE_EMRS_LONG_CNT BIT(26) #define EMC_EMRS_USE_EMRS_LONG_CNT BIT(26)
#define EMC_REF 0xd4 #define EMC_REF 0xd4
#define EMC_REF_REF_CMD BIT(0)
#define EMC_SELF_REF 0xe0 #define EMC_SELF_REF 0xe0
#define EMC_MRW 0xe8 #define EMC_MRW 0xe8
#define EMC_MRW_MRW_OP_SHIFT 0 #define EMC_MRW_MRW_OP_SHIFT 0
...@@ -871,6 +872,13 @@ struct tegra210_emc_timing { ...@@ -871,6 +872,13 @@ struct tegra210_emc_timing {
u32 latency; u32 latency;
}; };
enum tegra210_emc_refresh {
TEGRA210_EMC_REFRESH_NOMINAL = 0,
TEGRA210_EMC_REFRESH_2X,
TEGRA210_EMC_REFRESH_4X,
TEGRA210_EMC_REFRESH_THROTTLE, /* 4x Refresh + derating. */
};
#define DRAM_TYPE_DDR3 0 #define DRAM_TYPE_DDR3 0
#define DRAM_TYPE_LPDDR4 1 #define DRAM_TYPE_LPDDR4 1
#define DRAM_TYPE_LPDDR2 2 #define DRAM_TYPE_LPDDR2 2
...@@ -881,6 +889,12 @@ struct tegra210_emc { ...@@ -881,6 +889,12 @@ struct tegra210_emc {
struct device *dev; struct device *dev;
struct clk *clk; struct clk *clk;
/* nominal EMC frequency table */
struct tegra210_emc_timing *nominal;
/* derated EMC frequency table */
struct tegra210_emc_timing *derated;
/* currently selected table (nominal or derated) */
struct tegra210_emc_timing *timings; struct tegra210_emc_timing *timings;
unsigned int num_timings; unsigned int num_timings;
...@@ -900,6 +914,12 @@ struct tegra210_emc { ...@@ -900,6 +914,12 @@ struct tegra210_emc {
unsigned int training_interval; unsigned int training_interval;
struct timer_list training; struct timer_list training;
enum tegra210_emc_refresh refresh;
unsigned int refresh_poll_interval;
struct timer_list refresh_timer;
unsigned int temperature;
atomic_t refresh_poll;
ktime_t clkchange_time; ktime_t clkchange_time;
int clkchange_delay; int clkchange_delay;
...@@ -909,6 +929,7 @@ struct tegra210_emc { ...@@ -909,6 +929,7 @@ struct tegra210_emc {
struct dentry *root; struct dentry *root;
unsigned long min_rate; unsigned long min_rate;
unsigned long max_rate; unsigned long max_rate;
unsigned int temperature;
} debugfs; } debugfs;
struct tegra210_clk_emc_provider provider; struct tegra210_clk_emc_provider provider;
...@@ -967,6 +988,8 @@ static inline u32 div_o3(u32 a, u32 b) ...@@ -967,6 +988,8 @@ static inline u32 div_o3(u32 a, u32 b)
/* from tegra210-emc-r21021.c */ /* from tegra210-emc-r21021.c */
extern const struct tegra210_emc_sequence tegra210_emc_r21021; extern const struct tegra210_emc_sequence tegra210_emc_r21021;
int tegra210_emc_set_refresh(struct tegra210_emc *emc,
enum tegra210_emc_refresh refresh);
u32 tegra210_emc_mrr_read(struct tegra210_emc *emc, unsigned int chip, u32 tegra210_emc_mrr_read(struct tegra210_emc *emc, unsigned int chip,
unsigned int address); unsigned int address);
void tegra210_emc_do_clock_change(struct tegra210_emc *emc, u32 clksrc); void tegra210_emc_do_clock_change(struct tegra210_emc *emc, u32 clksrc);
...@@ -975,6 +998,8 @@ void tegra210_emc_timing_update(struct tegra210_emc *emc); ...@@ -975,6 +998,8 @@ void tegra210_emc_timing_update(struct tegra210_emc *emc);
u32 tegra210_emc_get_dll_state(struct tegra210_emc_timing *next); u32 tegra210_emc_get_dll_state(struct tegra210_emc_timing *next);
struct tegra210_emc_timing *tegra210_emc_find_timing(struct tegra210_emc *emc, struct tegra210_emc_timing *tegra210_emc_find_timing(struct tegra210_emc *emc,
unsigned long rate); unsigned long rate);
void tegra210_emc_adjust_timing(struct tegra210_emc *emc,
struct tegra210_emc_timing *timing);
int tegra210_emc_wait_for_update(struct tegra210_emc *emc, unsigned int channel, int tegra210_emc_wait_for_update(struct tegra210_emc *emc, unsigned int channel,
unsigned int offset, u32 bit_mask, bool state); unsigned int offset, u32 bit_mask, bool state);
unsigned long tegra210_emc_actual_osc_clocks(u32 in); unsigned long tegra210_emc_actual_osc_clocks(u32 in);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment