rk3188:linux3.10: add ddr freq support
author陈亮 <cl@rock-chips.com>
Fri, 28 Feb 2014 08:38:14 +0000 (00:38 -0800)
committer陈亮 <cl@rock-chips.com>
Fri, 28 Feb 2014 08:38:14 +0000 (00:38 -0800)
arch/arm/boot/dts/rk3188-tb.dts
arch/arm/mach-rockchip/Kconfig
arch/arm/mach-rockchip/Makefile
arch/arm/mach-rockchip/common.c
arch/arm/mach-rockchip/common.h
arch/arm/mach-rockchip/ddr.h [deleted file]
arch/arm/mach-rockchip/ddr_freq.c [new file with mode: 0644]
arch/arm/mach-rockchip/ddr_rk30.c
arch/arm/mach-rockchip/rk3188.c

index 57b87f8df02522a93a5aaec92cd6f6956f671b4b..09ca6848a36d73f9284c955e617b7c498fb459bb 100644 (file)
@@ -4,8 +4,7 @@
 #include "rk3188-clocks.dtsi"
 #include "lcd-b101ew05.dtsi"
 #include "rk3188-mmc.dtsi"
-
-
+#include <dt-bindings/clock/ddr.h>
 / {
        memory {
                device_type = "memory";
                /* KHz    uV */
                200000 1200000
                300000 1200000
-               400000 1300000
+               400000 1200000
                >;
 };
 
                /* KHz    uV */
                200000 1200000
                300000 1200000
-               400000 1300000
+               400000 1200000
+               >;
+
+       freq_table = <
+               /*status                freq(KHz)*/
+               SYS_STATUS_NORMAL       400000
+               SYS_STATUS_SUSPEND      200000
+               SYS_STATUS_VIDEO        300000
+               SYS_STATUS_DUALVIEW     500000
                >;
 };
 
index 1db585311f91f417b7475f6806a35bb70da6ae72..72fdf8767ed91d64345427e6cbe9ead1fac41a0b 100755 (executable)
@@ -37,6 +37,11 @@ config DVFS
        select PM_OPP
        select CPU_FREQ
 
+config DDR_FREQ
+       bool "Enable DDR frequency scaling"
+       default y
+       select DVFS
+
 config RK29_VPU
        tristate "VPU (Video Processing Unit) service driver in kernel"
        depends on ARCH_RK29 || ARCH_RK30 || ARCH_RK2928 || ARCH_RK3026 || ARCH_RK3188 || ARCH_RK319X || ARCH_ROCKCHIP
index b28b0d6fe6fb6a85e478c0cf2d3cb52a39744011..27af86d159401cc8a84825dfcb7895978251f870 100755 (executable)
@@ -12,3 +12,4 @@ obj-$(CONFIG_CPU_FREQ) += rk3188-cpufreq.o
 obj-$(CONFIG_DVFS) += dvfs.o
 obj-$(CONFIG_RK29_VPU) += vpu_service.o
 obj-$(CONFIG_RK_PL330_DMA_TEST) += dma_memcpy_test.o
+obj-$(CONFIG_DDR_FREQ) += ddr_freq.o
index 9bf34cc7bb2bfbd4afa3da4cfec63e52aa5da5fa..5a381733bc5911391733ffb1dcea28511825f9ae 100644 (file)
@@ -215,3 +215,7 @@ void rockchip_restart_get_boot_mode(const char *cmd, u32 *flag, u32 *mode)
 }
 
 struct rockchip_pmu_operations rockchip_pmu_ops;
+int (*ddr_change_freq)(uint32_t nMHz) = NULL;
+long (*ddr_round_rate)(uint32_t nMHz) = NULL;
+void (*ddr_set_auto_self_refresh)(bool en) = NULL;
+
index cfcf6cfe5e1fdb411b7eefdf8fc4c27a23067646..b16901ef2c39285883d2c84d71178eee41f46dc0 100644 (file)
 extern unsigned long rockchip_boot_fn;
 extern struct smp_operations rockchip_smp_ops;
 
+extern int (*ddr_change_freq)(uint32_t nMHz);
+extern long (*ddr_round_rate)(uint32_t nMHz);
+extern void (*ddr_set_auto_self_refresh)(bool en);
+
 extern int rockchip_cpu_kill(unsigned int cpu);
 extern void rockchip_cpu_die(unsigned int cpu);
 extern int rockchip_cpu_disable(unsigned int cpu);
diff --git a/arch/arm/mach-rockchip/ddr.h b/arch/arm/mach-rockchip/ddr.h
deleted file mode 100644 (file)
index e9d7f46..0000000
+++ /dev/null
@@ -1,52 +0,0 @@
-/*
- *
- * Copyright (C) 2011-2014 ROCKCHIP, Inc.
- *
- * This software is licensed under the terms of the GNU General Public
- * License version 2, as published by the Free Software Foundation, and
- * may be copied, distributed, and modified under those terms.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- */
-
-#ifndef __MACH_ROCKCHIP_DDR_H
-#define __MACH_ROCKCHIP_DDR_H
-
-#define DDR3_800D   (0)     // 5-5-5
-#define DDR3_800E   (1)     // 6-6-6
-#define DDR3_1066E  (2)     // 6-6-6
-#define DDR3_1066F  (3)     // 7-7-7
-#define DDR3_1066G  (4)     // 8-8-8
-#define DDR3_1333F  (5)     // 7-7-7
-#define DDR3_1333G  (6)     // 8-8-8
-#define DDR3_1333H  (7)     // 9-9-9
-#define DDR3_1333J  (8)     // 10-10-10
-#define DDR3_1600G  (9)     // 8-8-8
-#define DDR3_1600H  (10)    // 9-9-9
-#define DDR3_1600J  (11)    // 10-10-10
-#define DDR3_1600K  (12)    // 11-11-11
-#define DDR3_1866J  (13)    // 10-10-10
-#define DDR3_1866K  (14)    // 11-11-11
-#define DDR3_1866L  (15)    // 12-12-12
-#define DDR3_1866M  (16)    // 13-13-13
-#define DDR3_2133K  (17)    // 11-11-11
-#define DDR3_2133L  (18)    // 12-12-12
-#define DDR3_2133M  (19)    // 13-13-13
-#define DDR3_2133N  (20)    // 14-14-14
-#define DDR3_DEFAULT (21)
-#define DDR_DDR2     (22)
-#define DDR_LPDDR    (23)
-#define DDR_LPDDR2   (24)
-
-struct ddr_freq_t {
-    unsigned long screen_ft_us;
-    unsigned long long t0;
-    unsigned long long t1;
-    unsigned long t2;
-};
-
-#endif
diff --git a/arch/arm/mach-rockchip/ddr_freq.c b/arch/arm/mach-rockchip/ddr_freq.c
new file mode 100644 (file)
index 0000000..4385884
--- /dev/null
@@ -0,0 +1,435 @@
+#define pr_fmt(fmt) "ddrfreq: " fmt
+#include <linux/clk.h>
+#include <linux/cpu.h>
+#include <linux/cpufreq.h>
+#include <linux/delay.h>
+#include <linux/freezer.h>
+#include <linux/fs.h>
+#include <linux/kthread.h>
+#include <linux/miscdevice.h>
+#include <linux/module.h>
+#include <linux/reboot.h>
+#include <linux/slab.h>
+#include <linux/uaccess.h>
+#include <linux/sched/rt.h>
+#include <linux/of.h>
+#include <asm/cacheflush.h>
+#include <asm/tlbflush.h>
+#include <linux/vmalloc.h>
+#include <dt-bindings/clock/ddr.h>
+
+#include "dvfs.h"
+#include "common.h"
+
+enum {
+       DEBUG_DDR = 1U << 0,
+       DEBUG_VIDEO_STATE = 1U << 1,
+       DEBUG_SUSPEND = 1U << 2,
+       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 MHZ    (1000*1000)
+#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 dualview_rate;
+       unsigned long idle_rate;
+       unsigned long suspend_rate;
+       unsigned long reboot_rate;
+       bool auto_self_refresh;
+       char *mode;
+       unsigned long sys_status;
+       struct task_struct *task;
+       wait_queue_head_t wait;
+};
+static struct ddr ddr;
+
+module_param_named(sys_status, ddr.sys_status, ulong, S_IRUGO);
+module_param_named(auto_self_refresh, ddr.auto_self_refresh, bool, S_IRUGO);
+module_param_named(mode, ddr.mode, charp, S_IRUGO);
+
+static noinline void ddrfreq_set_sys_status(int status)
+{
+       ddr.sys_status |= status;
+       wake_up(&ddr.wait);
+}
+
+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)
+{
+       ddr.mode = name;
+       if (auto_self_refresh != ddr.auto_self_refresh) {
+               ddr_set_auto_self_refresh(auto_self_refresh);
+               ddr.auto_self_refresh = auto_self_refresh;
+               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)) {
+               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);
+               }
+       }
+}
+
+static noinline void ddrfreq_work(unsigned long sys_status)
+{
+       static struct clk *cpu = NULL;
+       static struct clk *gpu = NULL;
+       unsigned long s = sys_status;
+
+       if (!cpu)
+               cpu = clk_get(NULL, "cpu");
+       if (!gpu)
+               gpu = clk_get(NULL, "gpu");
+       
+       dprintk(DEBUG_VERBOSE, "sys_status %02lx\n", sys_status);
+       
+       if (ddr.reboot_rate && (s & SYS_STATUS_REBOOT)) {
+               ddrfreq_mode(false, &ddr.reboot_rate, "shutdown/reboot");
+       } else if (ddr.suspend_rate && (s & SYS_STATUS_SUSPEND)) {
+               ddrfreq_mode(true, &ddr.suspend_rate, "suspend");
+       } else if (ddr.dualview_rate && 
+               (s & SYS_STATUS_LCDC0) && (s & SYS_STATUS_LCDC1)) {
+               ddrfreq_mode(false, &ddr.dualview_rate, "dual-view");
+       } else if (ddr.video_rate && 
+               ((s & SYS_STATUS_VIDEO_720P)||(s & SYS_STATUS_VIDEO_1080P))) {
+               ddrfreq_mode(false, &ddr.video_rate, "video");
+       } else if (ddr.idle_rate
+               && !(s & SYS_STATUS_GPU)
+               && !(s & SYS_STATUS_RGA)
+               && !(s & SYS_STATUS_CIF0)
+               && !(s & SYS_STATUS_CIF1)
+               && (clk_get_rate(cpu) < 816 * MHZ)
+               && (clk_get_rate(gpu) <= 200 * MHZ)
+               ) {
+               ddrfreq_mode(false, &ddr.idle_rate, "idle");
+       } else {
+               ddrfreq_mode(false, &ddr.normal_rate, "normal");
+       }
+}
+
+static int ddrfreq_task(void *data)
+{
+       set_freezable();
+
+       do {
+               unsigned long status = ddr.sys_status;
+               ddrfreq_work(status);
+               wait_event_freezable(ddr.wait, (status != ddr.sys_status) || kthread_should_stop());
+       } while (!kthread_should_stop());
+
+       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");
+       ddrfreq_clear_sys_status(SYS_STATUS_VIDEO);
+       return 0;
+}
+
+#define VIDEO_LOW_RESOLUTION       (1080*720)
+static ssize_t video_state_write(struct file *file, const char __user *buffer,
+                                size_t count, loff_t *ppos)
+{
+       char state;
+       char *cookie_pot;
+       char *p;
+       char *buf = vzalloc(count);
+       uint32_t v_width=0,v_height=0,v_sync=0;
+       cookie_pot = buf;
+
+       if(!buf)
+               return -ENOMEM;
+
+       if (count < 1){
+               vfree(buf);
+               return -EPERM;
+       }
+
+       if (copy_from_user(cookie_pot, buffer, count)) {
+               vfree(buf);
+               return -EFAULT;
+       }
+
+       dprintk(DEBUG_VIDEO_STATE, "video_state write %s,len %d\n", cookie_pot,count);
+
+       state=cookie_pot[0];
+       if( (count>=3) && (cookie_pot[2]=='w') )
+       {
+               strsep(&cookie_pot,",");
+               strsep(&cookie_pot,"=");
+               p=strsep(&cookie_pot,",");
+               v_width = simple_strtol(p,NULL,10);
+               strsep(&cookie_pot,"=");
+               p=strsep(&cookie_pot,",");
+               v_height= simple_strtol(p,NULL,10);
+               strsep(&cookie_pot,"=");
+               p=strsep(&cookie_pot,",");
+               v_sync= simple_strtol(p,NULL,10);
+               dprintk(DEBUG_VIDEO_STATE, "video_state %c,width=%d,height=%d,sync=%d\n", state,v_width,v_height,v_sync);
+       }
+
+       switch (state) {
+       case '0':
+               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);
+               }
+               /*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
+                               ddrfreq_set_sys_status(SYS_STATUS_VIDEO_1080P);
+               }*/
+               else{
+                       ddrfreq_clear_sys_status(SYS_STATUS_VIDEO);
+               }
+               break;
+       default:
+               vfree(buf);
+               return -EINVAL;
+
+       }
+       vfree(buf);
+       return count;
+}
+
+static const struct file_operations video_state_fops = {
+       .owner  = THIS_MODULE,
+       .release= video_state_release,
+       .write  = video_state_write,
+};
+
+static struct miscdevice video_state_dev = {
+       .fops   = &video_state_fops,
+       .name   = "video_state",
+       .minor  = MISC_DYNAMIC_MINOR,
+};
+/*
+static int ddrfreq_clk_event(int status, unsigned long event)
+{
+       switch (event) {
+       case PRE_RATE_CHANGE:
+               ddrfreq_set_sys_status(status);
+               break;
+       case POST_RATE_CHANGE:
+       case ABORT_RATE_CHANGE:
+               ddrfreq_clear_sys_status(status);
+               break;
+       }
+       return NOTIFY_OK;
+}
+*/
+#define CLK_NOTIFIER(name, status) \
+static int ddrfreq_clk_##name##_event(struct notifier_block *this, unsigned long event, void *ptr) \
+{ \
+       return ddrfreq_clk_event(SYS_STATUS_##status, event); \
+} \
+static struct notifier_block ddrfreq_clk_##name##_notifier = { .notifier_call = ddrfreq_clk_##name##_event };
+
+#define REGISTER_CLK_NOTIFIER(name) \
+do { \
+       struct clk *clk = clk_get(NULL, #name); \
+       clk_notifier_register(clk, &ddrfreq_clk_##name##_notifier); \
+       clk_put(clk); \
+} while (0)
+
+#define UNREGISTER_CLK_NOTIFIER(name) \
+do { \
+       struct clk *clk = clk_get(NULL, #name); \
+       clk_notifier_unregister(clk, &ddrfreq_clk_##name##_notifier); \
+       clk_put(clk); \
+} while (0)
+/*
+CLK_NOTIFIER(pd_gpu, GPU);
+CLK_NOTIFIER(pd_rga, RGA);
+CLK_NOTIFIER(pd_cif0, CIF0);
+CLK_NOTIFIER(pd_cif1, CIF1);
+CLK_NOTIFIER(pd_lcdc0, LCDC0);
+CLK_NOTIFIER(pd_lcdc1, LCDC1);
+*/
+static int ddrfreq_reboot_notifier_event(struct notifier_block *this, unsigned long event, void *ptr)
+{
+       u32 timeout = 1000; // 10s
+       ddrfreq_set_sys_status(SYS_STATUS_REBOOT);
+       while (dvfs_clk_get_rate(ddr.clk_dvfs_node) != ddr.reboot_rate && --timeout) {
+               msleep(10);
+       }
+       if (!timeout) {
+               pr_err("failed to set ddr clk from %luMHz to %luMHz when shutdown/reboot\n", dvfs_clk_get_rate(ddr.clk_dvfs_node) / MHZ, ddr.reboot_rate / MHZ);
+       }
+       return NOTIFY_OK;
+}
+
+static struct notifier_block ddrfreq_reboot_notifier = {
+       .notifier_call = ddrfreq_reboot_notifier_event,
+};
+
+int of_init_ddr_freq_table(void)
+{
+       struct device_node *clk_ddr_dev_node;
+       const struct property *prop;
+       const __be32 *val;
+       int nr;
+       
+       clk_ddr_dev_node = of_find_node_by_name(NULL, "clk_ddr");
+       if (IS_ERR_OR_NULL(clk_ddr_dev_node)) {
+               pr_err("%s: get clk ddr dev node err\n", __func__);
+               return PTR_ERR(clk_ddr_dev_node);
+       }
+
+       prop = of_find_property(clk_ddr_dev_node, "freq_table", NULL);
+       if (!prop)
+               return -ENODEV;
+       if (!prop->value)
+               return -ENODATA;
+
+       nr = prop->length / sizeof(u32);
+       if (nr % 2) {
+               pr_err("%s: Invalid freq list\n", __func__);
+               return -EINVAL;
+       }
+
+       val = prop->value;
+       while (nr) {
+               unsigned long status = be32_to_cpup(val++);
+               unsigned long rate = be32_to_cpup(val++) * 1000;
+
+               if (status & SYS_STATUS_NORMAL)
+                       ddr.normal_rate = rate;
+               if (status & SYS_STATUS_SUSPEND)
+                       ddr.suspend_rate = rate;
+               if ((status & SYS_STATUS_VIDEO_720P)||(status & SYS_STATUS_VIDEO_720P))
+                       ddr.video_rate = rate;
+               if ((status & SYS_STATUS_LCDC0)&&(status & SYS_STATUS_LCDC1))
+                       ddr.dualview_rate = rate;
+               if (status & SYS_STATUS_IDLE)
+                       ddr.idle_rate= rate;
+               if (status & SYS_STATUS_REBOOT)
+                       ddr.reboot_rate= rate;
+
+               nr -= 2;
+       }
+
+       return 0;
+}
+
+static int ddrfreq_init(void)
+{
+       struct sched_param param = { .sched_priority = MAX_RT_PRIO - 1 };
+       int ret;
+       
+       ddr.clk_dvfs_node = clk_get_dvfs_node("clk_ddr");
+       if (!ddr.clk_dvfs_node){
+               return -EINVAL;
+       }
+       
+       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;      
+
+       of_init_ddr_freq_table();
+
+       if (ddr.idle_rate) {
+               //REGISTER_CLK_NOTIFIER(pd_gpu);
+               //REGISTER_CLK_NOTIFIER(pd_rga);
+               //REGISTER_CLK_NOTIFIER(pd_cif0);
+               //REGISTER_CLK_NOTIFIER(pd_cif1);
+       }
+
+       if (ddr.dualview_rate) {
+             //REGISTER_CLK_NOTIFIER(pd_lcdc0);
+             //REGISTER_CLK_NOTIFIER(pd_lcdc1);
+       }       
+
+       ret = misc_register(&video_state_dev);
+       if (unlikely(ret)) {
+               pr_err("failed to register video_state misc device! error %d\n", ret);
+               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)) {
+               ret = PTR_ERR(ddr.task);
+               pr_err("failed to create kthread! error %d\n", ret);
+               goto err1;
+       }
+
+       sched_setscheduler_nocheck(ddr.task, SCHED_FIFO, &param);
+       get_task_struct(ddr.task);
+       kthread_bind(ddr.task, 0);
+       wake_up_process(ddr.task);
+
+       register_reboot_notifier(&ddrfreq_reboot_notifier);
+
+       pr_info("verion 1.0 20140228\n");
+       dprintk(DEBUG_DDR, "normal %luMHz video %luMHz dualview %luMHz idle %luMHz suspend %luMHz reboot %luMHz\n",
+               ddr.normal_rate / MHZ, ddr.video_rate / MHZ, ddr.dualview_rate / MHZ, ddr.idle_rate / MHZ, ddr.suspend_rate / MHZ, ddr.reboot_rate / MHZ);
+
+       return 0;
+
+err1:
+#ifdef CONFIG_HAS_EARLYSUSPEND
+       unregister_early_suspend(&ddr.early_suspend);
+#endif
+       misc_deregister(&video_state_dev);
+err:
+       if (ddr.idle_rate) {
+               //UNREGISTER_CLK_NOTIFIER(pd_gpu);
+               //UNREGISTER_CLK_NOTIFIER(pd_rga);
+               //UNREGISTER_CLK_NOTIFIER(pd_cif0);
+               //UNREGISTER_CLK_NOTIFIER(pd_cif1);
+       }
+       if (ddr.dualview_rate) {
+        //UNREGISTER_CLK_NOTIFIER(pd_lcdc0);
+        //UNREGISTER_CLK_NOTIFIER(pd_lcdc1);
+       }
+
+       return ret;
+}
+late_initcall(ddrfreq_init);
index f416fa7c4ad7697b482ed601cbeed616d152ca86..5fe2c7aade344a8ac92c44634beb0389222bf2a8 100644 (file)
 
 #include <asm/cacheflush.h>
 #include <asm/tlbflush.h>
+#include <linux/cpu.h>
+#include <dt-bindings/clock/ddr.h>
 
 #include "cru.h"
-#include "ddr.h"
 
 typedef uint32_t uint32;
 
@@ -82,6 +83,13 @@ typedef uint32_t uint32;
 #define pd_video_pwr_st    (1<<8)
 #define pd_gpu_pwr_st    (1<<9)
 
+struct ddr_freq_t {
+    unsigned long screen_ft_us;
+    unsigned long long t0;
+    unsigned long long t1;
+    unsigned long t2;
+};
+
 //PMU registers
 typedef volatile struct tagPMU_FILE
 {
@@ -3526,7 +3534,7 @@ static noinline uint32_t ddr_change_freq_sram(uint32_t nMHz , struct ddr_freq_t
     param.dqstr_value = dqstr_value;
     call_with_stack(fn_to_pie(rockchip_pie_chunk, &FUNC(ddr_change_freq_sram)),
                     &param,
-                    rockchip_sram_stack);
+                    rockchip_sram_stack-(NR_CPUS-1)*PAUSE_CPU_STACK_SZIE);
 
 #if defined (DDR_CHANGE_FREQ_IN_LCDC_VSYNC)
 end:
@@ -3592,19 +3600,126 @@ if rk3188 DPLL is bad,use GPLL
 500MHz-800MHz          2:250MHz-400MHz
 200MHz-500MHz          1:200MHz-500MHz
 ******************************************/
+#if 0
 static noinline uint32_t ddr_change_freq(uint32_t nMHz)
 {
     struct ddr_freq_t ddr_freq_t;
     ddr_freq_t.screen_ft_us = 0;
-
+    
 #if defined(ENABLE_DDR_CLCOK_GPLL_PATH) && (defined(CONFIG_ARCH_RK3188) || defined(CONFIG_ARCH_RK319X))
     return ddr_change_freq_gpll_dpll(nMHz);
 #else
     return ddr_change_freq_sram(nMHz,ddr_freq_t);
 #endif
 }
+#endif
+
+bool DEFINE_PIE_DATA(cpu_pause[NR_CPUS]);
+volatile bool *p_cpu_pause;
+static inline bool is_cpu0_paused(unsigned int cpu) { smp_rmb(); return DATA(cpu_pause)[0]; }
+static inline void set_cpuX_paused(unsigned int cpu, bool pause) { DATA(cpu_pause)[cpu] = pause; smp_wmb(); }
+static inline bool is_cpuX_paused(unsigned int cpu) { smp_rmb(); return p_cpu_pause[cpu]; }
+static inline void set_cpu0_paused(bool pause) { p_cpu_pause[0] = pause; smp_wmb();}
+
+#define MAX_TIMEOUT (16000000UL << 6) //>0.64s
+
+/* Do not use stack, safe on SMP */
+void PIE_FUNC(_pause_cpu)(void *arg)
+{      
+       unsigned int cpu = (unsigned int)arg;
+       
+       set_cpuX_paused(cpu, true);
+       while (is_cpu0_paused(cpu));
+       set_cpuX_paused(cpu, false);
+}
+
+static void pause_cpu(void *info)
+{
+       unsigned int cpu = raw_smp_processor_id();
+
+       call_with_stack(fn_to_pie(rockchip_pie_chunk, &FUNC(_pause_cpu)),
+                       (void *)cpu,
+                       rockchip_sram_stack-(cpu-1)*PAUSE_CPU_STACK_SZIE);
+}
+
+static void wait_cpu(void *info)
+{
+}
+
+static int __ddr_change_freq(uint32_t nMHz, struct ddr_freq_t ddr_freq_t)
+{
+       u32 timeout = MAX_TIMEOUT;
+       unsigned int cpu;
+       unsigned int this_cpu = smp_processor_id();
+       int ret = 0;
+
+       cpu_maps_update_begin();
+       local_bh_disable();
+       set_cpu0_paused(true);
+       smp_call_function((smp_call_func_t)pause_cpu, NULL, 0);
+
+       for_each_online_cpu(cpu) {
+               if (cpu == this_cpu)
+                       continue;
+               while (!is_cpuX_paused(cpu) && --timeout);
+               if (timeout == 0) {
+                       pr_err("pause cpu %d timeout\n", cpu);
+                       goto out;
+               }
+       }
+
+       ret = ddr_change_freq_sram(nMHz, ddr_freq_t);
+
+out:
+       set_cpu0_paused(false);
+       local_bh_enable();
+       smp_call_function(wait_cpu, NULL, true);
+       cpu_maps_update_done();
+
+       return ret;
+}
+
+static int _ddr_change_freq(uint32_t nMHz)
+{
+       struct ddr_freq_t ddr_freq_t;
+       //int test_count=0;
+
+       ddr_freq_t.screen_ft_us = 0;
+       ddr_freq_t.t0 = 0;
+       ddr_freq_t.t1 = 0;
+#if defined (DDR_CHANGE_FREQ_IN_LCDC_VSYNC)
+       do
+       {
+               if(rk_fb_poll_wait_frame_complete() == true)
+               {
+                       ddr_freq_t.t0 = cpu_clock(0);
+                       ddr_freq_t.screen_ft_us = rk_fb_get_prmry_screen_ft();
+
+                       test_count++;
+                        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();
+                       outer_flush_all();
+                       flush_tlb_all();
+               }
+       }while(__ddr_change_freq(nMHz, ddr_freq_t)==0);
+#else
+       return __ddr_change_freq(nMHz, ddr_freq_t);
+#endif
+}
+
+static long _ddr_round_rate(uint32_t nMHz)
+{
+       return p_ddr_set_pll(nMHz, 0);
+}
 
-static void ddr_set_auto_self_refresh(bool en)
+static void _ddr_set_auto_self_refresh(bool en)
 {
     //set auto self-refresh idle
     *kern_to_pie(rockchip_pie_chunk, &DATA(ddr_sr_idle)) = en ? SR_IDLE : 0;
@@ -3803,12 +3918,13 @@ static int ddr_init(uint32_t dram_speed_bin, uint32_t freq)
     uint32_t gsr,dqstr;
     struct clk *clk;
 
-    ddr_print("version 1.00 20131223 \n");
+    ddr_print("version 1.00 20140228 \n");
 
     p_ddr_reg = kern_to_pie(rockchip_pie_chunk, &DATA(ddr_reg));
     p_ddr_select_gpll_div = kern_to_pie(rockchip_pie_chunk, &DATA(ddr_select_gpll_div));
     p_mem_type = kern_to_pie(rockchip_pie_chunk, &DATA(mem_type));
     p_ddr_set_pll = fn_to_pie(rockchip_pie_chunk, &FUNC(ddr_set_pll));
+    p_cpu_pause = kern_to_pie(rockchip_pie_chunk, &DATA(cpu_pause[0]));
 
     *p_mem_type = pPHY_Reg->DCR.b.DDRMD;
     ddr_speed_bin = dram_speed_bin;
@@ -3876,7 +3992,7 @@ static int ddr_init(uint32_t dram_speed_bin, uint32_t freq)
                                                                     (ddr_get_cap()>>20));
     ddr_adjust_config(*p_mem_type);
 
-    clk = clk_get(NULL, "ddr");
+    clk = clk_get(NULL, "clk_ddr");
     if (IS_ERR(clk)) {
         ddr_print("failed to get ddr clk\n");
         clk = NULL;
@@ -3884,11 +4000,10 @@ static int ddr_init(uint32_t dram_speed_bin, uint32_t freq)
     if(ddr_rk3188_dpll_is_good == true)
     {
         if(freq != 0)
-            value=ddr_change_freq(freq);
+            value = clk_set_rate(clk, 1000*1000*freq);
         else
-            value=ddr_change_freq(clk_get_rate(clk)/1000000);
+            value = clk_set_rate(clk, clk_get_rate(clk));
     }
-    clk_set_rate(clk, 0);
     ddr_print("init success!!! freq=%luMHz\n", clk ? clk_get_rate(clk)/1000000 : freq);
 
     for(value=0;value<4;value++)
index 1869fde1193fe268a17d36cad1c7abb8e0605185..9c23678188d41e36b0fd861d479d9729a569cb4a 100644 (file)
@@ -337,8 +337,13 @@ arch_initcall(rk3188_pie_init);
 
 static int __init rk3188_ddr_init(void)
 {
+       ddr_change_freq = _ddr_change_freq;
+       ddr_round_rate = _ddr_round_rate;
+       ddr_set_auto_self_refresh = _ddr_set_auto_self_refresh;
+       
        if (cpu_is_rk3188())
                ddr_init(DDR3_DEFAULT, 300);
+
        return 0;
 }
 arch_initcall_sync(rk3188_ddr_init);