From 00570c0159eee0bcd61232ebcf319842c644c35c Mon Sep 17 00:00:00 2001 From: =?utf8?q?=E9=99=88=E4=BA=AE?= Date: Wed, 16 Apr 2014 03:05:41 -0700 Subject: [PATCH] rk3288: add ddr auto adjust freq by ddr bandwidth --- arch/arm/boot/dts/rk3288-tb.dts | 1 + arch/arm/mach-rockchip/ddr_freq.c | 307 +++++++++++++++++++++++++----- arch/arm/mach-rockchip/ddr_rk32.c | 10 +- 3 files changed, 272 insertions(+), 46 deletions(-) diff --git a/arch/arm/boot/dts/rk3288-tb.dts b/arch/arm/boot/dts/rk3288-tb.dts index 0092e9d0570e..fc891e01c8b5 100644 --- a/arch/arm/boot/dts/rk3288-tb.dts +++ b/arch/arm/boot/dts/rk3288-tb.dts @@ -675,6 +675,7 @@ rockchip,power_type = ; SYS_STATUS_VIDEO 300000 SYS_STATUS_DUALVIEW 500000 >; + auto_freq=<0>; status="okay"; }; diff --git a/arch/arm/mach-rockchip/ddr_freq.c b/arch/arm/mach-rockchip/ddr_freq.c index 09ca42d54f8a..4990693b1c65 100755 --- a/arch/arm/mach-rockchip/ddr_freq.c +++ b/arch/arm/mach-rockchip/ddr_freq.c @@ -1,5 +1,6 @@ #define pr_fmt(fmt) "ddrfreq: " fmt #include +#include #include #include #include @@ -13,6 +14,8 @@ #include #include #include +#include +#include #include #include #include @@ -22,6 +25,9 @@ #include #include #include +static struct dvfs_node *clk_cpu_dvfs_node = NULL; +static int ddr_boost = 0; + enum { DEBUG_DDR = 1U << 0, @@ -30,6 +36,7 @@ enum { DEBUG_VERBOSE = 1U << 3, }; static int debug_mask = DEBUG_DDR; + module_param(debug_mask, int, S_IRUGO | S_IWUSR | S_IWGRP); #define dprintk(mask, fmt, ...) do { if (mask & debug_mask) pr_info(fmt, ##__VA_ARGS__); } while (0) @@ -37,9 +44,6 @@ module_param(debug_mask, int, S_IRUGO | S_IWUSR | S_IWGRP); #define KHZ 1000 struct ddr { -#ifdef CONFIG_HAS_EARLYSUSPEND - struct early_suspend early_suspend; -#endif struct dvfs_node *clk_dvfs_node; unsigned long normal_rate; unsigned long video_rate; @@ -47,6 +51,7 @@ struct ddr { unsigned long idle_rate; unsigned long suspend_rate; unsigned long reboot_rate; + bool auto_freq; bool auto_self_refresh; char *mode; unsigned long sys_status; @@ -70,7 +75,6 @@ static noinline void ddrfreq_clear_sys_status(int status) ddr.sys_status &= ~status; wake_up(&ddr.wait); } -int ddr_set_rate(uint32_t nMHz); static void ddrfreq_mode(bool auto_self_refresh, unsigned long *target_rate, char *name) { @@ -81,13 +85,192 @@ static void ddrfreq_mode(bool auto_self_refresh, unsigned long *target_rate, cha dprintk(DEBUG_DDR, "change auto self refresh to %d when %s\n", auto_self_refresh, name); } if (*target_rate != dvfs_clk_get_rate(ddr.clk_dvfs_node)) { + dvfs_clk_enable_limit(clk_cpu_dvfs_node, 600000000, -1); if (dvfs_clk_set_rate(ddr.clk_dvfs_node, *target_rate) == 0) { *target_rate = dvfs_clk_get_rate(ddr.clk_dvfs_node); dprintk(DEBUG_DDR, "change freq to %lu MHz when %s\n", *target_rate / MHZ, name); } + dvfs_clk_enable_limit(clk_cpu_dvfs_node, 0, -1); } } +static void ddr_freq_input_event(struct input_handle *handle, unsigned int type, + unsigned int code, int value) +{ + if (type == EV_ABS) + ddr_boost = 1; +} + +static int ddr_freq_input_connect(struct input_handler *handler, + struct input_dev *dev, const struct input_device_id *id) +{ + struct input_handle *handle; + int error; + + handle = kzalloc(sizeof(struct input_handle), GFP_KERNEL); + if (!handle) + return -ENOMEM; + + handle->dev = dev; + handle->handler = handler; + handle->name = "ddr_freq"; + + error = input_register_handle(handle); + if (error) + goto err2; + + error = input_open_device(handle); + if (error) + goto err1; + + return 0; +err1: + input_unregister_handle(handle); +err2: + kfree(handle); + return error; +} + +static void ddr_freq_input_disconnect(struct input_handle *handle) +{ + input_close_device(handle); + input_unregister_handle(handle); + kfree(handle); +} + +static const struct input_device_id ddr_freq_ids[] = { + + { + .flags = INPUT_DEVICE_ID_MATCH_EVBIT | + INPUT_DEVICE_ID_MATCH_ABSBIT, + .evbit = { BIT_MASK(EV_ABS) }, + .absbit = { [BIT_WORD(ABS_MT_POSITION_X)] = + BIT_MASK(ABS_MT_POSITION_X) | + BIT_MASK(ABS_MT_POSITION_Y) }, + }, + + { + .flags = INPUT_DEVICE_ID_MATCH_KEYBIT | + INPUT_DEVICE_ID_MATCH_ABSBIT, + .keybit = { [BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH) }, + .absbit = { [BIT_WORD(ABS_X)] = + BIT_MASK(ABS_X) | BIT_MASK(ABS_Y) }, + }, + + { + .flags = INPUT_DEVICE_ID_MATCH_EVBIT, + .evbit = { BIT_MASK(EV_KEY) }, + }, + { }, +}; + +static struct input_handler ddr_freq_input_handler = { + .event = ddr_freq_input_event, + .connect = ddr_freq_input_connect, + .disconnect = ddr_freq_input_disconnect, + .name = "ddr_freq", + .id_table = ddr_freq_ids, +}; + +enum ddr_bandwidth_id{ + ddrbw_wr_num=0, + ddrbw_rd_num, + ddrbw_act_num, + ddrbw_time_num, + ddrbw_eff, + ddrbw_id_end +}; + +static struct workqueue_struct *ddr_freq_wq; +static u32 high_load = 65; //65% +static u32 ddrbw_work_delay_ms = 20; + +#define DDR_BOOST_HOLD_MS 500 +#define HIGH_LOAD_HOLD_MS 500 +#define RAISE_DELAY_MS 60 +#define DDR_BOOST_HOLD (DDR_BOOST_HOLD_MS/ddrbw_work_delay_ms) +#define HIGH_LOAD_HOLD (DDR_BOOST_HOLD_MS/ddrbw_work_delay_ms) +#define RAISE_DELAY (RAISE_DELAY_MS/ddrbw_work_delay_ms) +#define DDR_NORMAL_RATE 240000000 +#define DDR_BOOST_RATE 324000000 +#define HIGH_LOAD_RATE 400000000 + +//#define ddr_monitor_start() grf_writel(0xc000c000,RK3288_GRF_SOC_CON4) +#define ddr_monitor_start() grf_writel((((readl_relaxed(RK_PMU_VIRT + 0x9c)>>13)&7)==3)?0xc000c000:0xe000e000,RK3288_GRF_SOC_CON4) +#define ddr_monitor_stop() grf_writel(0xc0000000,RK3288_GRF_SOC_CON4) + +#define grf_readl(offset) readl_relaxed(RK_GRF_VIRT + offset) +#define grf_writel(v, offset) do { writel_relaxed(v, RK_GRF_VIRT + offset); dsb(); } while (0) + + +void ddr_bandwidth_get(u32 *ch0_eff, u32 *ch1_eff) +{ + u32 ddr_bw_val[2][ddrbw_id_end]; + u64 temp64; + int i, j; + + for(j = 0; j < 2; j++) { + for(i = 0; i < ddrbw_eff; i++ ){ + ddr_bw_val[j][i] = grf_readl(RK3288_GRF_SOC_STATUS11+i*4+j*16); + } + } + + temp64 = ((u64)ddr_bw_val[0][0]+ddr_bw_val[0][1])*4*100; + do_div(temp64, ddr_bw_val[0][ddrbw_time_num]); + ddr_bw_val[0][ddrbw_eff] = temp64; + *ch0_eff = temp64; + + temp64 = ((u64)ddr_bw_val[1][0]+ddr_bw_val[1][1])*4*100; + do_div(temp64, ddr_bw_val[1][ddrbw_time_num]); + ddr_bw_val[1][ddrbw_eff] = temp64; + *ch1_eff = temp64; +} + +static void ddrbw_work_fn(struct work_struct *work) +{ + unsigned long rate; + u32 ch0_eff, ch1_eff; + static u32 ddr_boost_hold=0, high_load_hold=0, raise_delay = 0; + + ddr_monitor_stop(); + ddr_bandwidth_get(&ch0_eff, &ch1_eff); + + if (ddr_boost) { + ddr_boost = 0; + //dvfs_clk_set_rate(ddr.clk_dvfs_node, DDR_BOOST_RATE); + rate = DDR_BOOST_RATE; + ddrfreq_mode(false, &rate, "boost"); + ddr_boost_hold = DDR_BOOST_HOLD; + high_load_hold = 0; + raise_delay = RAISE_DELAY; + } else if(!ddr_boost_hold && ((ch0_eff>high_load)||(ch1_eff>high_load))){ + if (!raise_delay) { + //dvfs_clk_set_rate(ddr.clk_dvfs_node, HIGH_LOAD_RATE); + rate = HIGH_LOAD_RATE; + ddrfreq_mode(false, &rate, "high load"); + high_load_hold=HIGH_LOAD_HOLD; + } else { + raise_delay--; + } + } else { + if (ddr_boost_hold) { + ddr_boost_hold--; + } else if (high_load_hold) { + high_load_hold--; + } else { + raise_delay = RAISE_DELAY; + //dvfs_clk_set_rate(ddr.clk_dvfs_node, DDR_NORMAL_RATE); + rate = DDR_NORMAL_RATE; + ddrfreq_mode(false, &rate, "normal"); + } + } + + ddr_monitor_start(); + + queue_delayed_work_on(0, ddr_freq_wq, to_delayed_work(work), HZ*ddrbw_work_delay_ms/1000); +} +static DECLARE_DELAYED_WORK(ddrbw_work, ddrbw_work_fn); + static noinline void ddrfreq_work(unsigned long sys_status) { static struct clk *cpu = NULL; @@ -100,6 +283,9 @@ static noinline void ddrfreq_work(unsigned long sys_status) gpu = clk_get(NULL, "gpu"); dprintk(DEBUG_VERBOSE, "sys_status %02lx\n", sys_status); + + if (ddr.auto_freq) + cancel_delayed_work_sync(&ddrbw_work); if (ddr.reboot_rate && (s & SYS_STATUS_REBOOT)) { ddrfreq_mode(false, &ddr.reboot_rate, "shutdown/reboot"); @@ -121,7 +307,10 @@ static noinline void ddrfreq_work(unsigned long sys_status) ) { ddrfreq_mode(false, &ddr.idle_rate, "idle"); } else { - ddrfreq_mode(false, &ddr.normal_rate, "normal"); + if (ddr.auto_freq) + queue_delayed_work_on(0, ddr_freq_wq, &ddrbw_work, 0); + else + ddrfreq_mode(false, &ddr.normal_rate, "normal"); } } @@ -138,20 +327,6 @@ static int ddrfreq_task(void *data) return 0; } -#ifdef CONFIG_HAS_EARLYSUSPEND -static void ddrfreq_early_suspend(struct early_suspend *h) -{ - dprintk(DEBUG_SUSPEND, "early suspend\n"); - ddrfreq_set_sys_status(SYS_STATUS_SUSPEND); -} - -static void ddrfreq_late_resume(struct early_suspend *h) -{ - dprintk(DEBUG_SUSPEND, "late resume\n"); - ddrfreq_clear_sys_status(SYS_STATUS_SUSPEND); -} -#endif - static int video_state_release(struct inode *inode, struct file *file) { dprintk(DEBUG_VIDEO_STATE, "video_state release\n"); @@ -203,20 +378,24 @@ static ssize_t video_state_write(struct file *file, const char __user *buffer, switch (state) { case '0': - ddrfreq_clear_sys_status(SYS_STATUS_VIDEO); + if (!ddr.auto_freq) + ddrfreq_clear_sys_status(SYS_STATUS_VIDEO); break; case '1': if( (v_width == 0) && (v_height == 0)){ - ddrfreq_set_sys_status(SYS_STATUS_VIDEO_1080P); + if (!ddr.auto_freq) + ddrfreq_set_sys_status(SYS_STATUS_VIDEO_1080P); } - /*else if(v_sync==1){ - if(ddr.video_low_rate && ((v_width*v_height) <= VIDEO_LOW_RESOLUTION) ) - ddrfreq_set_sys_status(SYS_STATUS_VIDEO_720P); - else + else if(v_sync==1){ + //if(ddr.video_low_rate && ((v_width*v_height) <= VIDEO_LOW_RESOLUTION) ) + // ddrfreq_set_sys_status(SYS_STATUS_VIDEO_720P); + //else + if (!ddr.auto_freq) ddrfreq_set_sys_status(SYS_STATUS_VIDEO_1080P); - }*/ + } else{ - ddrfreq_clear_sys_status(SYS_STATUS_VIDEO); + if (!ddr.auto_freq) + ddrfreq_clear_sys_status(SYS_STATUS_VIDEO); } break; default: @@ -312,6 +491,10 @@ int of_init_ddr_freq_table(void) return PTR_ERR(clk_ddr_dev_node); } + prop = of_find_property(clk_ddr_dev_node, "auto_freq", NULL); + if (prop && prop->value) + ddr.auto_freq = be32_to_cpup(prop->value); + prop = of_find_property(clk_ddr_dev_node, "freq_table", NULL); if (!prop) return -ENODEV; @@ -347,35 +530,81 @@ int of_init_ddr_freq_table(void) return 0; } -#if defined(CONFIG_RK_PM_TESTS) +#if 0//defined(CONFIG_RK_PM_TESTS) static void ddrfreq_tst_init(void); #endif + + +static int ddr_freq_suspend_notifier_call(struct notifier_block *self, + unsigned long action, void *data) +{ + struct fb_event *event = data; + int blank_mode = *((int *)event->data); + + if (action == FB_EARLY_EVENT_BLANK) { + switch (blank_mode) { + case FB_BLANK_UNBLANK: + break; + default: + ddrfreq_set_sys_status(SYS_STATUS_SUSPEND); + break; + } + } + else if (action == FB_EVENT_BLANK) { + switch (blank_mode) { + case FB_BLANK_UNBLANK: + ddrfreq_clear_sys_status(SYS_STATUS_SUSPEND); + break; + default: + break; + } + } + + return NOTIFY_OK; +} + +static struct notifier_block ddr_freq_suspend_notifier = { + .notifier_call = ddr_freq_suspend_notifier_call, +}; + + + + static int ddrfreq_init(void) { struct sched_param param = { .sched_priority = MAX_RT_PRIO - 1 }; int ret; -#if defined(CONFIG_RK_PM_TESTS) +#if 0//defined(CONFIG_RK_PM_TESTS) ddrfreq_tst_init(); #endif + clk_cpu_dvfs_node = clk_get_dvfs_node("clk_core"); + if (!clk_cpu_dvfs_node){ + return -EINVAL; + } + + memset(&ddr, 0x00, sizeof(ddr)); ddr.clk_dvfs_node = clk_get_dvfs_node("clk_ddr"); if (!ddr.clk_dvfs_node){ return -EINVAL; } clk_enable_dvfs(ddr.clk_dvfs_node); + + ddr_freq_wq = alloc_workqueue("ddr_freq", WQ_NON_REENTRANT | WQ_MEM_RECLAIM | WQ_HIGHPRI | WQ_FREEZABLE, 1); init_waitqueue_head(&ddr.wait); ddr.mode = "normal"; ddr.normal_rate = dvfs_clk_get_rate(ddr.clk_dvfs_node); - ddr.video_rate = ddr.normal_rate; - ddr.dualview_rate = 0; - ddr.idle_rate = 0; ddr.suspend_rate = ddr.normal_rate; - ddr.reboot_rate = ddr.normal_rate; + ddr.reboot_rate = ddr.normal_rate; of_init_ddr_freq_table(); + ret = input_register_handler(&ddr_freq_input_handler); + if (ret) + ddr.auto_freq = false; + if (ddr.idle_rate) { //REGISTER_CLK_NOTIFIER(pd_gpu); //REGISTER_CLK_NOTIFIER(pd_rga); @@ -394,12 +623,6 @@ static int ddrfreq_init(void) goto err; } -#ifdef CONFIG_HAS_EARLYSUSPEND - ddr.early_suspend.suspend = ddrfreq_early_suspend; - ddr.early_suspend.resume = ddrfreq_late_resume; - ddr.early_suspend.level = EARLY_SUSPEND_LEVEL_DISABLE_FB + 50; - register_early_suspend(&ddr.early_suspend); -#endif ddr.task = kthread_create(ddrfreq_task, NULL, "ddrfreqd"); if (IS_ERR(ddr.task)) { @@ -413,6 +636,7 @@ static int ddrfreq_init(void) kthread_bind(ddr.task, 0); wake_up_process(ddr.task); + fb_register_client(&ddr_freq_suspend_notifier); register_reboot_notifier(&ddrfreq_reboot_notifier); pr_info("verion 1.0 20140228\n"); @@ -422,9 +646,6 @@ static int ddrfreq_init(void) return 0; err1: -#ifdef CONFIG_HAS_EARLYSUSPEND - unregister_early_suspend(&ddr.early_suspend); -#endif misc_deregister(&video_state_dev); err: if (ddr.idle_rate) { @@ -443,7 +664,7 @@ err: late_initcall(ddrfreq_init); /****************************ddr bandwith tst************************************/ -#if defined(CONFIG_RK_PM_TESTS) +#if 0//defined(CONFIG_RK_PM_TESTS) #define USE_NORMAL_TIME diff --git a/arch/arm/mach-rockchip/ddr_rk32.c b/arch/arm/mach-rockchip/ddr_rk32.c index a11a1a631e95..31383a5a4976 100755 --- a/arch/arm/mach-rockchip/ddr_rk32.c +++ b/arch/arm/mach-rockchip/ddr_rk32.c @@ -19,10 +19,12 @@ #include #include #include +#include #include "cpu_axi.h" typedef uint32_t uint32; +#define DDR_CHANGE_FREQ_IN_LCDC_VSYNC /*********************************** * Global Control Macro ***********************************/ @@ -3733,6 +3735,7 @@ void PIE_FUNC(ddr_change_freq_sram)(void *arg) } EXPORT_PIE_SYMBOL(FUNC(ddr_change_freq_sram)); +static int dclk_div; static noinline uint32 ddr_change_freq_sram(uint32 nMHz , struct ddr_freq_t ddr_freq_t) { uint32 freq; @@ -3753,6 +3756,8 @@ static noinline uint32 ddr_change_freq_sram(uint32 nMHz , struct ddr_freq_t ddr_ } #endif + dclk_div = (cru_readl(RK3288_CRU_CLKSELS_CON(29)) >> 8) & 0xff; + param.arm_freq = ddr_get_pll_freq(APLL); gpllvaluel = ddr_get_pll_freq(GPLL); if((200 < gpllvaluel) ||( gpllvaluel <1600)) //GPLL:200MHz~1600MHz @@ -3827,10 +3832,11 @@ static noinline uint32 ddr_change_freq_sram(uint32 nMHz , struct ddr_freq_t ddr_ param.freq = freq; param.freq_slew = freq_slew; param.dqstr_value = dqstr_value; + cru_writel(0 |CRU_W_MSK_SETBITS(0xff,8,0xff), RK3288_CRU_CLKSELS_CON(29)); call_with_stack(fn_to_pie(rockchip_pie_chunk, &FUNC(ddr_change_freq_sram)), ¶m, rockchip_sram_stack-(NR_CPUS-1)*PAUSE_CPU_STACK_SZIE); - + cru_writel(0 |CRU_W_MSK_SETBITS(dclk_div,8,0xff), RK3288_CRU_CLKSELS_CON(29)); #if defined (DDR_CHANGE_FREQ_IN_LCDC_VSYNC) end: #endif @@ -3907,9 +3913,7 @@ static int _ddr_change_freq(uint32 nMHz) if(test_count > 10) //test 10 times { ddr_freq_t.screen_ft_us = 0xfefefefe; - dprintk(DEBUG_DDR,"%s:test_count exceed maximum!\n",__func__); } - dprintk(DEBUG_VERBOSE,"%s:test_count=%d\n",__func__,test_count); usleep_range(ddr_freq_t.screen_ft_us-test_count*1000,ddr_freq_t.screen_ft_us-test_count*1000); flush_cache_all(); -- 2.34.1