aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Documentation/cpu-freq/governors.txt16
-rw-r--r--arch/arm/mach-omap2/dpll44xx.c38
-rw-r--r--arch/arm/mach-omap2/emif.c99
-rw-r--r--arch/arm/mach-omap2/include/mach/emif.h2
-rw-r--r--drivers/cpufreq/cpufreq_interactive.c48
5 files changed, 197 insertions, 6 deletions
diff --git a/Documentation/cpu-freq/governors.txt b/Documentation/cpu-freq/governors.txt
index 46abbcb..871ec8e 100644
--- a/Documentation/cpu-freq/governors.txt
+++ b/Documentation/cpu-freq/governors.txt
@@ -225,11 +225,21 @@ frequency before ramping down. This is to ensure that the governor has
seen enough historic cpu load data to determine the appropriate
workload. Default is 80000 uS.
-go_maxspeed_load: The CPU load at which to ramp to max speed. Default
-is 85.
+hispeed_freq: An intermediate "hi speed" at which to initially ramp
+when CPU load hits the value specified in go_hispeed_load. If load
+stays high for the amount of time specified in above_hispeed_delay,
+then speed may be bumped higher. Default is maximum speed.
+
+go_hispeed_load: The CPU load at which to ramp to the intermediate "hi
+speed". Default is 85%.
+
+above_hispeed_delay: Once speed is set to hispeed_freq, wait for this
+long before bumping speed higher in response to continued high load.
+Default is 20000 uS.
timer_rate: Sample rate for reevaluating cpu load when the system is
-not idle. Default is 30000 uS.
+not idle. Default is 20000 uS.
+
2.7 Hotplug
-----------
diff --git a/arch/arm/mach-omap2/dpll44xx.c b/arch/arm/mach-omap2/dpll44xx.c
index 7e509e7..983617f 100644
--- a/arch/arm/mach-omap2/dpll44xx.c
+++ b/arch/arm/mach-omap2/dpll44xx.c
@@ -79,6 +79,35 @@ int omap4_core_dpll_m2_set_rate(struct clk *clk, unsigned long rate)
clkdm_wakeup(l3_emif_clkdm);
/*
+ * Errata ID: i728
+ *
+ * DESCRIPTION:
+ *
+ * If during a small window the following three events occur:
+ *
+ * 1) The EMIF_PWR_MGMT_CTRL[7:4] REG_SR_TIM SR_TIMING counter expires
+ * 2) Frequency change update is requested CM_SHADOW_FREQ_CONFIG1
+ * FREQ_UPDATE set to 1
+ * 3) OCP access is requested
+ *
+ * There will be clock instability on the DDR interface.
+ *
+ * WORKAROUND:
+ *
+ * Prevent event 1) while event 2) is happening.
+ *
+ * Disable the self-refresh when requesting a frequency change.
+ * Before requesting a frequency change, program
+ * EMIF_PWR_MGMT_CTRL[10:8] REG_LP_MODE to 0x0
+ * (omap_emif_frequency_pre_notify)
+ *
+ * When the frequency change is completed, reprogram
+ * EMIF_PWR_MGMT_CTRL[10:8] REG_LP_MODE to 0x2.
+ * (omap_emif_frequency_post_notify)
+ */
+ omap_emif_frequency_pre_notify();
+
+ /*
* Program EMIF timing parameters in EMIF shadow registers
* for targetted DRR clock.
* DDR Clock = core_dpll_m2 / 2
@@ -107,6 +136,9 @@ int omap4_core_dpll_m2_set_rate(struct clk *clk, unsigned long rate)
& OMAP4430_FREQ_UPDATE_MASK) == 0),
MAX_FREQ_UPDATE_TIMEOUT, i);
+ /* Re-enable DDR self refresh */
+ omap_emif_frequency_post_notify();
+
/* Configures MEMIF domain back to HW_WKUP */
clkdm_allow_idle(l3_emif_clkdm);
@@ -150,6 +182,9 @@ int omap4_prcm_freq_update(void)
/* Configures MEMIF domain in SW_WKUP */
clkdm_wakeup(l3_emif_clkdm);
+ /* Disable DDR self refresh (Errata ID: i728) */
+ omap_emif_frequency_pre_notify();
+
/*
* FREQ_UPDATE sequence:
* - DLL_OVERRIDE=0 (DLL lock & code must not be overridden
@@ -167,6 +202,9 @@ int omap4_prcm_freq_update(void)
& OMAP4430_FREQ_UPDATE_MASK) == 0),
MAX_FREQ_UPDATE_TIMEOUT, i);
+ /* Re-enable DDR self refresh */
+ omap_emif_frequency_post_notify();
+
/* Configures MEMIF domain back to HW_WKUP */
clkdm_allow_idle(l3_emif_clkdm);
diff --git a/arch/arm/mach-omap2/emif.c b/arch/arm/mach-omap2/emif.c
index 968bcde..8d41bd1 100644
--- a/arch/arm/mach-omap2/emif.c
+++ b/arch/arm/mach-omap2/emif.c
@@ -38,6 +38,7 @@ struct emif_instance {
void __iomem *base;
u16 irq;
struct platform_device *pdev;
+ bool ddr_refresh_disabled;
};
static struct emif_instance emif[EMIF_NUM_INSTANCES];
static struct emif_regs *emif_curr_regs[EMIF_NUM_INSTANCES];
@@ -534,6 +535,47 @@ static u32 get_ddr_phy_ctrl_1(u32 freq, u8 RL)
}
/*
+ * get_lp_mode - Get the LP Mode of a EMIF instance.
+ *
+ * It returns the REG_LP_MODE of EMIF_PWR_MGMT_CTRL[10:8]
+ * for a EMIF.
+ *
+ */
+static u32 get_lp_mode(u32 emif_nr)
+{
+ u32 temp, lpmode;
+ void __iomem *base = emif[emif_nr].base;
+
+ temp = readl(base + OMAP44XX_EMIF_PWR_MGMT_CTRL);
+ lpmode = (temp & OMAP44XX_REG_LP_MODE_MASK) >>
+ OMAP44XX_REG_LP_MODE_SHIFT;
+
+ return lpmode;
+}
+
+/*
+ * set_lp_mode - Set the LP Mode of a EMIF instance.
+ *
+ * It replaces the REG_LP_MODE of EMIF_PWR_MGMT_CTRL[10:8]
+ * with the new value for a EMIF.
+ *
+ */
+static void set_lp_mode(u32 emif_nr, u32 lpmode)
+{
+ u32 temp;
+ void __iomem *base = emif[emif_nr].base;
+
+ /* Extract current lp mode value */
+ temp = readl(base + OMAP44XX_EMIF_PWR_MGMT_CTRL);
+
+ /* Write out the new lp mode value */
+ temp &= ~OMAP44XX_REG_LP_MODE_MASK;
+ temp |= lpmode << OMAP44XX_REG_LP_MODE_SHIFT;
+ writel(temp, base + OMAP44XX_EMIF_PWR_MGMT_CTRL);
+
+}
+
+/*
* Get the temperature level of the EMIF instance:
* Reads the MR4 register of attached SDRAM parts to find out the temperature
* level. If there are two parts attached(one on each CS), then the temperature
@@ -875,6 +917,8 @@ static int __devinit omap_emif_probe(struct platform_device *pdev)
pr_info("EMIF%d is enabled with IRQ%d\n", id, emif[id].irq);
+ emif[id].ddr_refresh_disabled = false;
+
return 0;
}
@@ -1278,6 +1322,61 @@ int omap_emif_setup_registers(u32 freq, u32 volt_state)
return err;
}
+
+/*
+ * omap_emif_frequency_pre_notify - Disable DDR self refresh of both EMIFs
+ *
+ * It disables the LP mode if the LP mode of EMIFs was LP_MODE_SELF_REFRESH.
+ *
+ * It should be called before any PRCM frequency update sequence.
+ * After the frequency update sequence, omap_emif_frequency_post_notify
+ * should be called to restore the original LP MODE setting of the EMIFs.
+ *
+ */
+void omap_emif_frequency_pre_notify(void)
+{
+ int emif_num;
+
+ for (emif_num = EMIF1; emif_num < EMIF_NUM_INSTANCES; emif_num++) {
+
+ /*
+ * Only disable ddr self-refresh
+ * if ddr self-refresh was enabled
+ */
+ if (likely(LP_MODE_SELF_REFRESH == get_lp_mode(emif_num))) {
+
+ set_lp_mode(emif_num, LP_MODE_DISABLE);
+ emif[emif_num].ddr_refresh_disabled = true;
+ }
+
+ }
+}
+
+/*
+ * omap_emif_frequency_post_notify - Enable DDR self refresh of both EMIFs
+ *
+ * It restores the LP mode of the EMIFs back to LP_MODE_SELF_REFRESH if it
+ * was previously disabled by omap_emif_frequency_pre_notify()
+ *
+ */
+void omap_emif_frequency_post_notify(void)
+{
+ int emif_num;
+
+ for (emif_num = EMIF1; emif_num < EMIF_NUM_INSTANCES; emif_num++) {
+
+ /*
+ * Only re-enable ddr self-refresh
+ * if ddr self-refresh was disabled
+ */
+ if (likely(emif[emif_num].ddr_refresh_disabled)) {
+
+ set_lp_mode(emif_num, LP_MODE_SELF_REFRESH);
+ emif[emif_num].ddr_refresh_disabled = false;
+ }
+ }
+}
+
/*
* omap_emif_setup_device_details - save the SDRAM device details passed
* from the board file
diff --git a/arch/arm/mach-omap2/include/mach/emif.h b/arch/arm/mach-omap2/include/mach/emif.h
index 99a7c69..d7e1eb9 100644
--- a/arch/arm/mach-omap2/include/mach/emif.h
+++ b/arch/arm/mach-omap2/include/mach/emif.h
@@ -258,6 +258,8 @@ struct emif_regs {
int omap_emif_setup_registers(u32 freq,
u32 volt_state);
+void omap_emif_frequency_pre_notify(void);
+void omap_emif_frequency_post_notify(void);
int omap_emif_setup_device_details(
const struct emif_device_details *emif1_devices,
const struct emif_device_details *emif2_devices);
diff --git a/drivers/cpufreq/cpufreq_interactive.c b/drivers/cpufreq/cpufreq_interactive.c
index 42b9e74..c9f348f 100644
--- a/drivers/cpufreq/cpufreq_interactive.c
+++ b/drivers/cpufreq/cpufreq_interactive.c
@@ -66,21 +66,28 @@ static struct mutex set_speed_lock;
static u64 hispeed_freq;
/* Go to hi speed when CPU load at or above this value. */
-#define DEFAULT_GO_HISPEED_LOAD 95
+#define DEFAULT_GO_HISPEED_LOAD 85
static unsigned long go_hispeed_load;
/*
* The minimum amount of time to spend at a frequency before we can ramp down.
*/
-#define DEFAULT_MIN_SAMPLE_TIME 20 * USEC_PER_MSEC
+#define DEFAULT_MIN_SAMPLE_TIME (80 * USEC_PER_MSEC)
static unsigned long min_sample_time;
/*
* The sample rate of the timer used to increase frequency
*/
-#define DEFAULT_TIMER_RATE 20 * USEC_PER_MSEC
+#define DEFAULT_TIMER_RATE (20 * USEC_PER_MSEC)
static unsigned long timer_rate;
+/*
+ * Wait this long before raising speed above hispeed, by default a single
+ * timer interval.
+ */
+#define DEFAULT_ABOVE_HISPEED_DELAY DEFAULT_TIMER_RATE
+static unsigned long above_hispeed_delay_val;
+
static int cpufreq_governor_interactive(struct cpufreq_policy *policy,
unsigned int event);
@@ -174,6 +181,17 @@ static void cpufreq_interactive_timer(unsigned long data)
if (new_freq < hispeed_freq)
new_freq = hispeed_freq;
+
+ if (pcpu->target_freq == hispeed_freq &&
+ new_freq > hispeed_freq &&
+ cputime64_sub(pcpu->timer_run_time,
+ pcpu->target_set_time)
+ < above_hispeed_delay_val) {
+ trace_cpufreq_interactive_notyet(data, cpu_load,
+ pcpu->target_freq,
+ new_freq);
+ goto rearm;
+ }
}
} else {
new_freq = pcpu->policy->max * cpu_load / 100;
@@ -515,6 +533,28 @@ static ssize_t store_min_sample_time(struct kobject *kobj,
static struct global_attr min_sample_time_attr = __ATTR(min_sample_time, 0644,
show_min_sample_time, store_min_sample_time);
+static ssize_t show_above_hispeed_delay(struct kobject *kobj,
+ struct attribute *attr, char *buf)
+{
+ return sprintf(buf, "%lu\n", above_hispeed_delay_val);
+}
+
+static ssize_t store_above_hispeed_delay(struct kobject *kobj,
+ struct attribute *attr,
+ const char *buf, size_t count)
+{
+ int ret;
+ unsigned long val;
+
+ ret = strict_strtoul(buf, 0, &val);
+ if (ret < 0)
+ return ret;
+ above_hispeed_delay_val = val;
+ return count;
+}
+
+define_one_global_rw(above_hispeed_delay);
+
static ssize_t show_timer_rate(struct kobject *kobj,
struct attribute *attr, char *buf)
{
@@ -540,6 +580,7 @@ static struct global_attr timer_rate_attr = __ATTR(timer_rate, 0644,
static struct attribute *interactive_attributes[] = {
&hispeed_freq_attr.attr,
&go_hispeed_load_attr.attr,
+ &above_hispeed_delay.attr,
&min_sample_time_attr.attr,
&timer_rate_attr.attr,
NULL,
@@ -660,6 +701,7 @@ static int __init cpufreq_interactive_init(void)
go_hispeed_load = DEFAULT_GO_HISPEED_LOAD;
min_sample_time = DEFAULT_MIN_SAMPLE_TIME;
+ above_hispeed_delay_val = DEFAULT_ABOVE_HISPEED_DELAY;
timer_rate = DEFAULT_TIMER_RATE;
/* Initalize per-cpu timers */