From 2dd2c6028ad2ca14448ffcd6fb4b36c33f255dcb Mon Sep 17 00:00:00 2001 From: =?utf8?q?=E9=99=88=E4=BA=AE?= Date: Fri, 28 Feb 2014 00:38:14 -0800 Subject: [PATCH] rk3188:linux3.10: add ddr freq support --- arch/arm/boot/dts/rk3188-tb.dts | 15 +- arch/arm/mach-rockchip/Kconfig | 5 + arch/arm/mach-rockchip/Makefile | 1 + arch/arm/mach-rockchip/common.c | 4 + arch/arm/mach-rockchip/common.h | 4 + arch/arm/mach-rockchip/ddr.h | 52 ---- arch/arm/mach-rockchip/ddr_freq.c | 435 ++++++++++++++++++++++++++++++ arch/arm/mach-rockchip/ddr_rk30.c | 133 ++++++++- arch/arm/mach-rockchip/rk3188.c | 5 + 9 files changed, 589 insertions(+), 65 deletions(-) delete mode 100644 arch/arm/mach-rockchip/ddr.h create mode 100644 arch/arm/mach-rockchip/ddr_freq.c diff --git a/arch/arm/boot/dts/rk3188-tb.dts b/arch/arm/boot/dts/rk3188-tb.dts index 57b87f8df025..09ca6848a36d 100644 --- a/arch/arm/boot/dts/rk3188-tb.dts +++ b/arch/arm/boot/dts/rk3188-tb.dts @@ -4,8 +4,7 @@ #include "rk3188-clocks.dtsi" #include "lcd-b101ew05.dtsi" #include "rk3188-mmc.dtsi" - - +#include / { memory { device_type = "memory"; @@ -240,7 +239,7 @@ /* KHz uV */ 200000 1200000 300000 1200000 - 400000 1300000 + 400000 1200000 >; }; @@ -249,7 +248,15 @@ /* 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 >; }; diff --git a/arch/arm/mach-rockchip/Kconfig b/arch/arm/mach-rockchip/Kconfig index 1db585311f91..72fdf8767ed9 100755 --- a/arch/arm/mach-rockchip/Kconfig +++ b/arch/arm/mach-rockchip/Kconfig @@ -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 diff --git a/arch/arm/mach-rockchip/Makefile b/arch/arm/mach-rockchip/Makefile index b28b0d6fe6fb..27af86d15940 100755 --- a/arch/arm/mach-rockchip/Makefile +++ b/arch/arm/mach-rockchip/Makefile @@ -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 diff --git a/arch/arm/mach-rockchip/common.c b/arch/arm/mach-rockchip/common.c index 9bf34cc7bb2b..5a381733bc59 100644 --- a/arch/arm/mach-rockchip/common.c +++ b/arch/arm/mach-rockchip/common.c @@ -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; + diff --git a/arch/arm/mach-rockchip/common.h b/arch/arm/mach-rockchip/common.h index cfcf6cfe5e1f..b16901ef2c39 100644 --- a/arch/arm/mach-rockchip/common.h +++ b/arch/arm/mach-rockchip/common.h @@ -12,6 +12,10 @@ 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 index e9d7f46c617d..000000000000 --- a/arch/arm/mach-rockchip/ddr.h +++ /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 index 000000000000..4385884489f0 --- /dev/null +++ b/arch/arm/mach-rockchip/ddr_freq.c @@ -0,0 +1,435 @@ +#define pr_fmt(fmt) "ddrfreq: " fmt +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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, ¶m); + 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); diff --git a/arch/arm/mach-rockchip/ddr_rk30.c b/arch/arm/mach-rockchip/ddr_rk30.c index f416fa7c4ad7..5fe2c7aade34 100644 --- a/arch/arm/mach-rockchip/ddr_rk30.c +++ b/arch/arm/mach-rockchip/ddr_rk30.c @@ -16,9 +16,10 @@ #include #include +#include +#include #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)), ¶m, - 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++) diff --git a/arch/arm/mach-rockchip/rk3188.c b/arch/arm/mach-rockchip/rk3188.c index 1869fde1193f..9c23678188d4 100644 --- a/arch/arm/mach-rockchip/rk3188.c +++ b/arch/arm/mach-rockchip/rk3188.c @@ -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); -- 2.34.1