#define pr_fmt(fmt) "ddrfreq: " fmt
#include <linux/clk.h>
+#include <linux/fb.h>
#include <linux/cpu.h>
#include <linux/cpufreq.h>
#include <linux/delay.h>
#include <linux/uaccess.h>
#include <linux/sched/rt.h>
#include <linux/of.h>
+#include <linux/fb.h>
+#include <linux/input.h>
#include <asm/cacheflush.h>
#include <asm/tlbflush.h>
#include <linux/vmalloc.h>
#include <asm/io.h>
#include <linux/rockchip/grf.h>
#include <linux/rockchip/iomap.h>
+static struct dvfs_node *clk_cpu_dvfs_node = NULL;
+static int ddr_boost = 0;
+
enum {
DEBUG_DDR = 1U << 0,
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)
#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;
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;
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)
{
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;
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");
) {
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");
}
}
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");
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:
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;
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);
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)) {
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");
return 0;
err1:
-#ifdef CONFIG_HAS_EARLYSUSPEND
- unregister_early_suspend(&ddr.early_suspend);
-#endif
misc_deregister(&video_state_dev);
err:
if (ddr.idle_rate) {
late_initcall(ddrfreq_init);
/****************************ddr bandwith tst************************************/
-#if defined(CONFIG_RK_PM_TESTS)
+#if 0//defined(CONFIG_RK_PM_TESTS)
#define USE_NORMAL_TIME