mirror of
https://github.com/hardkernel/linux.git
synced 2026-06-09 20:32:04 +09:00
rk3188:linux3.10: add ddr freq support
This commit is contained in:
@@ -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";
|
||||
@@ -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
|
||||
>;
|
||||
};
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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
|
||||
435
arch/arm/mach-rockchip/ddr_freq.c
Normal file
435
arch/arm/mach-rockchip/ddr_freq.c
Normal file
@@ -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, ¶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);
|
||||
@@ -16,9 +16,10 @@
|
||||
|
||||
#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)),
|
||||
¶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
|
||||
|
||||
static void ddr_set_auto_self_refresh(bool en)
|
||||
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)
|
||||
{
|
||||
//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++)
|
||||
|
||||
@@ -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);
|
||||
|
||||
Reference in New Issue
Block a user