From 4b90a24492ff445d435d005c16eda3b43d214dfb Mon Sep 17 00:00:00 2001 From: =?utf8?q?=E9=BB=84=E6=B6=9B?= Date: Fri, 26 Aug 2011 20:26:58 +0800 Subject: [PATCH] rk29: support DDR frequency scaling --- arch/arm/mach-rk29/Kconfig | 3 + arch/arm/mach-rk29/clock.c | 5 +- arch/arm/mach-rk29/cpufreq.c | 157 +++++++++++++++++++++++++++++++++-- 3 files changed, 157 insertions(+), 8 deletions(-) diff --git a/arch/arm/mach-rk29/Kconfig b/arch/arm/mach-rk29/Kconfig index a5c0611fe1a5..24c3ebbbc26e 100755 --- a/arch/arm/mach-rk29/Kconfig +++ b/arch/arm/mach-rk29/Kconfig @@ -157,6 +157,9 @@ config DDR_SDRAM_FREQ int "DDR SDRAM frequence (in MHz)" default 400 +config DDR_FREQ + bool "Enable DDR frequency scaling (EXPERIMENTAL)" + config DDR_RECONFIG bool "Enable dynamic DDR reconfiguration (EXPERIMENTAL)" diff --git a/arch/arm/mach-rk29/clock.c b/arch/arm/mach-rk29/clock.c index 41822edd7b6b..facedb0ea984 100755 --- a/arch/arm/mach-rk29/clock.c +++ b/arch/arm/mach-rk29/clock.c @@ -2654,7 +2654,6 @@ static void __init clk_enable_init_clocks(void) clk_enable_nolock(&clk_dma1); clk_enable_nolock(&clk_emem); clk_enable_nolock(&clk_intmem); - clk_enable_nolock(&clk_ddr); clk_enable_nolock(&clk_debug); clk_enable_nolock(&clk_tpiu); clk_enable_nolock(&clk_jtag); @@ -2715,7 +2714,7 @@ void __init rk29_clock_init2(enum periph_pll ppll_rate, enum codec_pll cpll_rate printk(KERN_INFO "Clocking rate (apll/dpll/cpll/gpll/core/aclk_cpu/hclk_cpu/pclk_cpu/aclk_periph/hclk_periph/pclk_periph): %ld/%ld/%ld/%ld/%ld/%ld/%ld/%ld/%ld/%ld/%ld MHz", arm_pll_clk.rate / MHZ, ddr_pll_clk.rate / MHZ, codec_pll_clk.rate / MHZ, general_pll_clk.rate / MHZ, clk_core.rate / MHZ, aclk_cpu.rate / MHZ, hclk_cpu.rate / MHZ, pclk_cpu.rate / MHZ, aclk_periph.rate / MHZ, hclk_periph.rate / MHZ, pclk_periph.rate / MHZ); - printk(KERN_CONT " (20110812)\n"); + printk(KERN_CONT " (20110826)\n"); preset_lpj = loops_per_jiffy; } @@ -2789,6 +2788,8 @@ static void dump_clock(struct seq_file *s, struct clk *clk, int deep) case CRU_GENERAL_MODE_SLOW27: seq_printf(s, "slow27 "); break; } if (cru_readl(CRU_GPLL_CON) & PLL_BYPASS) seq_printf(s, "bypass "); + } else if (clk == &clk_ddr) { + rate = clk->recalc(clk); } if (rate >= MHZ) { diff --git a/arch/arm/mach-rk29/cpufreq.c b/arch/arm/mach-rk29/cpufreq.c index 75b42465d50b..5001606d06ed 100755 --- a/arch/arm/mach-rk29/cpufreq.c +++ b/arch/arm/mach-rk29/cpufreq.c @@ -27,7 +27,9 @@ #include #include #include +#include #include +#include #include <../../../drivers/video/rk29_fb.h> #define MHZ (1000*1000) @@ -51,6 +53,7 @@ static struct cpufreq_frequency_table default_freq_table[] = { static struct cpufreq_frequency_table *freq_table = default_freq_table; static struct clk *arm_clk; static struct clk *ddr_clk; +static unsigned long ddr_max_rate; static DEFINE_MUTEX(mutex); #ifdef CONFIG_REGULATOR @@ -81,8 +84,9 @@ enum { DEBUG_CHANGE = 1U << 0, DEBUG_TEMP = 1U << 1, DEBUG_DISP = 1U << 2, + DEBUG_DDR = 1U << 3, }; -static uint debug_mask = DEBUG_CHANGE; +static uint debug_mask = DEBUG_CHANGE | DEBUG_DDR; module_param(debug_mask, uint, 0644); #define dprintk(mask, fmt, ...) do { if (mask & debug_mask) printk(KERN_DEBUG pr_fmt(fmt), ##__VA_ARGS__); } while (0) @@ -126,6 +130,12 @@ module_param(limit_index_1008, int, 0444); static inline bool aclk_limit(void) { return false; } #endif +#ifdef CONFIG_DDR_FREQ +static void rk29_cpufreq_change_ddr_freq(unsigned long mhz); +#else +static inline void rk29_cpufreq_change_ddr_freq(unsigned long mhz) {} +#endif + static bool rk29_cpufreq_is_ondemand_policy(struct cpufreq_policy *policy) { char c = 0; @@ -240,16 +250,13 @@ static void rk29_cpufreq_limit_by_temp(struct cpufreq_policy *policy, unsigned i #ifdef CONFIG_RK29_CPU_FREQ_LIMIT_BY_DISP static void rk29_cpufreq_limit_by_disp(int *index) { - unsigned long ddr_rate; unsigned int frequency = freq_table[*index].frequency; int new_index = -1; if (!aclk_limit()) return; - ddr_rate = clk_get_rate(ddr_clk); - - if (ddr_rate < 492 * MHZ) { + if (ddr_max_rate < 492 * MHZ) { if (limit_index_816 >= 0 && frequency > 816 * KHZ) new_index = limit_index_816; } else { @@ -274,6 +281,7 @@ static int rk29_cpufreq_do_target(struct cpufreq_policy *policy, unsigned int ta const struct cpufreq_frequency_table *freq; int err = 0; bool force = relation & CPUFREQ_FORCE_CHANGE; + unsigned long new_arm_rate; relation &= ~CPUFREQ_FORCE_CHANGE; @@ -310,6 +318,7 @@ static int rk29_cpufreq_do_target(struct cpufreq_policy *policy, unsigned int ta freqs.new = freq->frequency; freqs.cpu = 0; new_vcore_uV = freq->index; + new_arm_rate = freqs.new * KHZ; dprintk(DEBUG_CHANGE, "%d Hz r %d(%c) selected %d Hz (%d uV)\n", target_freq, relation, relation & CPUFREQ_RELATION_H ? 'H' : 'L', freq->frequency, new_vcore_uV); @@ -330,6 +339,7 @@ static int rk29_cpufreq_do_target(struct cpufreq_policy *policy, unsigned int ta cpufreq_notify_transition(&freqs, CPUFREQ_PRECHANGE); dprintk(DEBUG_CHANGE, "pre change\n"); clk_set_rate(arm_clk, freqs.new * KHZ + aclk_limit()); + rk29_cpufreq_change_ddr_freq(0); dprintk(DEBUG_CHANGE, "post change\n"); freqs.new = clk_get_rate(arm_clk) / KHZ; cpufreq_notify_transition(&freqs, CPUFREQ_POSTCHANGE); @@ -448,6 +458,119 @@ static struct notifier_block rk29_cpufreq_fb_notifier = { }; #endif +#ifdef CONFIG_DDR_FREQ +static struct clk *ddr_pll_clk; +static struct clk *aclk_lcdc; +static bool ddr_pll_can_change; +static bool aclk_lcdc_disabled; +static bool disable_ddr_freq; +module_param(ddr_pll_can_change, bool, 0644); +module_param(aclk_lcdc_disabled, bool, 0644); +module_param(disable_ddr_freq, bool, 0644); +static unsigned long ddr_max_mhz; +static unsigned long ddr_min_mhz; +module_param(ddr_max_mhz, ulong, 0644); +module_param(ddr_min_mhz, ulong, 0644); +#define DDR_ARM_RATE (408 * MHZ) + +static void rk29_cpufreq_change_ddr_freq(unsigned long mhz) +{ + unsigned long flags; + unsigned long ddr_rate, arm_rate; + bool changed = false; + + if (disable_ddr_freq) + return; + + if (DEBUG_DDR & debug_mask) { + unsigned long _mhz = mhz; + ddr_rate = clk_get_rate(ddr_clk); + if (!mhz && ddr_pll_can_change && aclk_lcdc_disabled) { + arm_rate = clk_get_rate(arm_clk); + if (arm_rate <= DDR_ARM_RATE && ddr_rate == ddr_max_rate) + _mhz = ddr_min_mhz; + else if (arm_rate > DDR_ARM_RATE && ddr_rate < ddr_max_rate) + _mhz = ddr_max_mhz; + } + if (_mhz) { + unsigned long hz = _mhz * MHZ; + if (hz != ddr_rate) + dprintk(DEBUG_DDR, "ddr %lu -> %lu Hz\n", ddr_rate, hz); + } + } + + local_irq_save(flags); + ddr_rate = clk_get_rate(ddr_clk); + if (!mhz && ddr_pll_can_change && aclk_lcdc_disabled) { + arm_rate = clk_get_rate(arm_clk); + if (arm_rate <= DDR_ARM_RATE && ddr_rate == ddr_max_rate) + mhz = ddr_min_mhz; + else if (arm_rate > DDR_ARM_RATE && ddr_rate < ddr_max_rate) + mhz = ddr_max_mhz; + } + if (mhz && (mhz * MHZ) != ddr_rate) { + ddr_change_freq(mhz); + changed = true; + } + local_irq_restore(flags); + + if (changed) + dprintk(DEBUG_DDR, "ok, got %lu Hz\n", clk_get_rate(ddr_clk)); +} + +static int rk29_cpufreq_ddr_pll_notifier_event(struct notifier_block *this, + unsigned long event, void *ptr) +{ + switch (event) { + case CLK_PRE_ENABLE: + ddr_pll_can_change = false; + break; + case CLK_ABORT_ENABLE: + case CLK_POST_DISABLE: + ddr_pll_can_change = true; + break; + default: + return NOTIFY_DONE; + } + + if (!disable_ddr_freq) { + dprintk(DEBUG_DDR, "event: %lu ddr_pll_can_change: %d\n", event, ddr_pll_can_change); + rk29_cpufreq_change_ddr_freq(ddr_pll_can_change ? 0 : ddr_max_mhz); + } + return NOTIFY_OK; +} + +static struct notifier_block rk29_cpufreq_ddr_pll_notifier = { + .notifier_call = rk29_cpufreq_ddr_pll_notifier_event, +}; + +static int rk29_cpufreq_aclk_lcdc_notifier_event(struct notifier_block *this, + unsigned long event, void *ptr) +{ + switch (event) { + case CLK_PRE_ENABLE: + aclk_lcdc_disabled = false; + break; + case CLK_ABORT_ENABLE: + case CLK_POST_DISABLE: + aclk_lcdc_disabled = true; + break; + default: + return NOTIFY_DONE; + } + + if (!disable_ddr_freq) { + dprintk(DEBUG_DDR, "event: %lu aclk_lcdc_disabled: %d\n", event, aclk_lcdc_disabled); + rk29_cpufreq_change_ddr_freq(aclk_lcdc_disabled ? 0 : ddr_max_mhz); + } + return NOTIFY_OK; +} + +static struct notifier_block rk29_cpufreq_aclk_lcdc_notifier = { + .notifier_call = rk29_cpufreq_aclk_lcdc_notifier_event, +}; +#endif + static int rk29_cpufreq_init(struct cpufreq_policy *policy) { if (policy->cpu != 0) @@ -468,6 +591,24 @@ static int rk29_cpufreq_init(struct cpufreq_policy *policy) ddr_clk = NULL; return err; } + ddr_max_rate = clk_get_rate(ddr_clk); + +#ifdef CONFIG_DDR_FREQ + ddr_max_mhz = ddr_max_rate / MHZ; + { + unsigned long ddr_min_rate; + ddr_min_rate = ddr_max_rate >> 1; + if (ddr_min_rate > 150 * MHZ) + ddr_min_rate >>= 1; + ddr_min_mhz = ddr_min_rate / MHZ; + } + + ddr_pll_clk = clk_get(NULL, "ddr_pll"); + aclk_lcdc = clk_get(NULL, "aclk_lcdc"); + + clk_notifier_register(ddr_pll_clk, &rk29_cpufreq_ddr_pll_notifier); + clk_notifier_register(aclk_lcdc, &rk29_cpufreq_aclk_lcdc_notifier); +#endif #ifdef CONFIG_REGULATOR vcore = regulator_get(NULL, "vcore"); @@ -492,7 +633,7 @@ static int rk29_cpufreq_init(struct cpufreq_policy *policy) #ifdef CONFIG_RK29_CPU_FREQ_LIMIT_BY_TEMP if (rk29_cpufreq_is_ondemand_policy(policy)) { - dprintk(DEBUG_TEMP, "start wq\n"); + dprintk(DEBUG_TEMP, "queue work\n"); queue_delayed_work(wq, &rk29_cpufreq_limit_by_temp_work, WORK_DELAY); } cpufreq_register_notifier(¬ifier_policy_block, CPUFREQ_POLICY_NOTIFIER); @@ -505,6 +646,10 @@ static int rk29_cpufreq_init(struct cpufreq_policy *policy) static int rk29_cpufreq_exit(struct cpufreq_policy *policy) { +#ifdef CONFIG_DDR_FREQ + clk_notifier_unregister(ddr_pll_clk, &rk29_cpufreq_ddr_pll_notifier); + clk_notifier_unregister(aclk_lcdc, &rk29_cpufreq_aclk_lcdc_notifier); +#endif #ifdef CONFIG_RK29_CPU_FREQ_LIMIT_BY_DISP rk29fb_unregister_notifier(&rk29_cpufreq_fb_notifier); #endif -- 2.34.1