mirror of
https://github.com/hardkernel/linux.git
synced 2026-06-11 13:27:06 +09:00
rk2928:sdk: add cpufreq and dvfs support, BUT NOT compile
This commit is contained in:
@@ -6,6 +6,8 @@ obj-y += devices.o
|
||||
obj-y += iomux.o
|
||||
obj-y += clock.o
|
||||
obj-y += clock_data.o
|
||||
obj-$(CONFIG_CPU_FREQ) += cpufreq.o
|
||||
obj-$(CONFIG_DVFS) += dvfs.o
|
||||
obj-$(CONFIG_PM) += pm.o
|
||||
CFLAGS_pm.o += -Os -mthumb
|
||||
|
||||
|
||||
@@ -33,7 +33,7 @@
|
||||
#include <asm/mach/map.h>
|
||||
#include <asm/mach/flash.h>
|
||||
#include <asm/hardware/gic.h>
|
||||
//#include <mach/dvfs.h>
|
||||
#include <mach/dvfs.h>
|
||||
|
||||
#include <mach/board.h>
|
||||
#include <mach/hardware.h>
|
||||
@@ -762,10 +762,56 @@ static void __init rk2928_reserve(void)
|
||||
#endif
|
||||
board_mem_reserved();
|
||||
}
|
||||
/**
|
||||
* dvfs_cpu_logic_table: table for arm and logic dvfs
|
||||
* @frequency : arm frequency
|
||||
* @cpu_volt : arm voltage depend on frequency
|
||||
* @logic_volt : logic voltage arm requests depend on frequency
|
||||
* comments : min arm/logic voltage
|
||||
*/
|
||||
static struct dvfs_arm_table dvfs_cpu_logic_table[] = {
|
||||
{.frequency = 216 * 1000, .cpu_volt = 1200 * 1000, .logic_volt = 1200 * 1000},//0.975V/1.000V
|
||||
{.frequency = 312 * 1000, .cpu_volt = 1200 * 1000, .logic_volt = 1200 * 1000},//0.975V/1.000V
|
||||
{.frequency = 408 * 1000, .cpu_volt = 1200 * 1000, .logic_volt = 1200 * 1000},//1.000V/1.025V
|
||||
{.frequency = 504 * 1000, .cpu_volt = 1200 * 1000, .logic_volt = 1200 * 1000},//1.000V/1.025V
|
||||
{.frequency = 600 * 1000, .cpu_volt = 1200 * 1000, .logic_volt = 1200 * 1000},//1.025V/1.050V
|
||||
{.frequency = 696 * 1000, .cpu_volt = 1400 * 1000, .logic_volt = 1200 * 1000},//1.000V/1.025V
|
||||
{.frequency = 816 * 1000, .cpu_volt = 1400 * 1000, .logic_volt = 1200 * 1000},//1.100V/1.050V
|
||||
{.frequency = 912 * 1000, .cpu_volt = 1400 * 1000, .logic_volt = 1200 * 1000},//1.100V/1.050V
|
||||
#if 0
|
||||
{.frequency = 1008 * 1000, .cpu_volt = 1400 * 1000, .logic_volt = 1200 * 1000},//1.100V/1.050V
|
||||
{.frequency = 1104 * 1000, .cpu_volt = 1400 * 1000, .logic_volt = 1200 * 1000},//1.100V/1.050V
|
||||
{.frequency = 1200 * 1000, .cpu_volt = 1400 * 1000, .logic_volt = 1200 * 1000},//1.100V/1.050V
|
||||
{.frequency = 1104 * 1000, .cpu_volt = 1400 * 1000, .logic_volt = 1200 * 1000},//1.100V/1.050V
|
||||
{.frequency = 1248 * 1000, .cpu_volt = 1400 * 1000, .logic_volt = 1200 * 1000},//1.100V/1.050V
|
||||
#endif
|
||||
//{.frequency = 1000 * 1000, .cpu_volt = 1225 * 1000, .logic_volt = 1200 * 1000},//1.150V/1.100V
|
||||
{.frequency = CPUFREQ_TABLE_END},
|
||||
};
|
||||
|
||||
static struct cpufreq_frequency_table dvfs_gpu_table[] = {
|
||||
{.frequency = 266 * 1000, .index = 1050 * 1000},
|
||||
{.frequency = 400 * 1000, .index = 1275 * 1000},
|
||||
{.frequency = CPUFREQ_TABLE_END},
|
||||
};
|
||||
|
||||
static struct cpufreq_frequency_table dvfs_ddr_table[] = {
|
||||
{.frequency = 300 * 1000, .index = 1050 * 1000},
|
||||
{.frequency = 400 * 1000, .index = 1125 * 1000},
|
||||
{.frequency = CPUFREQ_TABLE_END},
|
||||
};
|
||||
|
||||
#define DVFS_CPU_TABLE_SIZE (ARRAY_SIZE(dvfs_cpu_logic_table))
|
||||
static struct cpufreq_frequency_table cpu_dvfs_table[DVFS_CPU_TABLE_SIZE];
|
||||
static struct cpufreq_frequency_table dep_cpu2core_table[DVFS_CPU_TABLE_SIZE];
|
||||
|
||||
void __init board_clock_init(void)
|
||||
{
|
||||
rk2928_clock_data_init(periph_pll_default, codec_pll_default, RK30_CLOCKS_DEFAULT_FLAGS);
|
||||
dvfs_set_arm_logic_volt(dvfs_cpu_logic_table, cpu_dvfs_table, dep_cpu2core_table);
|
||||
dvfs_set_freq_volt_table(clk_get(NULL, "gpu"), dvfs_gpu_table);
|
||||
//dvfs_set_freq_volt_table(clk_get(NULL, "ddr"), dvfs_ddr_table);
|
||||
printk("%s end\n", __func__);
|
||||
}
|
||||
|
||||
|
||||
|
||||
19
arch/arm/mach-rk2928/clock.c
Executable file → Normal file
19
arch/arm/mach-rk2928/clock.c
Executable file → Normal file
@@ -23,7 +23,7 @@
|
||||
#include <linux/delay.h>
|
||||
#include <mach/clock.h>
|
||||
#include "clock.h"
|
||||
//#include <mach/dvfs.h>
|
||||
#include <mach/dvfs.h>
|
||||
#include <linux/delay.h>
|
||||
|
||||
#define CLOCK_PRINTK_DBG(fmt, args...) pr_debug(fmt, ## args);
|
||||
@@ -126,6 +126,7 @@ int clk_register(struct clk *clk)
|
||||
return -EINVAL;
|
||||
//INIT_LIST_HEAD(&clk->sibling);
|
||||
INIT_LIST_HEAD(&clk->children);
|
||||
|
||||
/*
|
||||
* trap out already registered clocks
|
||||
*/
|
||||
@@ -138,11 +139,10 @@ int clk_register(struct clk *clk)
|
||||
else if (clk->parents)
|
||||
clk->parent =clk_default_get_parent(clk);
|
||||
|
||||
if (clk->parent){
|
||||
if (clk->parent)
|
||||
list_add(&clk->sibling, &clk->parent->children);
|
||||
} else {
|
||||
else
|
||||
list_add(&clk->sibling, &root_clks);
|
||||
}
|
||||
list_add(&clk->node, &clocks);
|
||||
mutex_unlock(&clocks_mutex);
|
||||
return 0;
|
||||
@@ -318,7 +318,7 @@ int clk_set_parent_nolock(struct clk *clk, struct clk *parent)
|
||||
return ret;
|
||||
}
|
||||
/**********************************dvfs****************************************************/
|
||||
#if 0
|
||||
|
||||
struct clk_node *clk_get_dvfs_info(struct clk *clk)
|
||||
{
|
||||
return clk->dvfs_info;
|
||||
@@ -338,7 +338,7 @@ void clk_register_dvfs(struct clk_node *dvfs_clk, struct clk *clk)
|
||||
{
|
||||
clk->dvfs_info = dvfs_clk;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
/*-------------------------------------------------------------------------
|
||||
* Optional clock functions defined in include/linux/clk.h
|
||||
@@ -398,10 +398,9 @@ int clk_set_rate(struct clk *clk, unsigned long rate)
|
||||
}
|
||||
if (rate == clk->rate)
|
||||
return 0;
|
||||
#if 0
|
||||
if (clk->dvfs_info!=NULL&&is_support_dvfs(clk->dvfs_info))
|
||||
return dvfs_set_rate(clk, rate);
|
||||
#endif
|
||||
|
||||
LOCK();
|
||||
ret = clk_set_rate_nolock(clk, rate);
|
||||
UNLOCK();
|
||||
@@ -703,7 +702,8 @@ void clk_register_dump_ops(struct clk_dump_ops *ops)
|
||||
{
|
||||
dump_def_ops=ops;
|
||||
}
|
||||
|
||||
|
||||
#ifdef CONFIG_RK_CLOCK_PROC
|
||||
static int proc_clk_show(struct seq_file *s, void *v)
|
||||
{
|
||||
struct clk* clk;
|
||||
@@ -747,4 +747,5 @@ static int __init clk_proc_init(void)
|
||||
|
||||
}
|
||||
late_initcall(clk_proc_init);
|
||||
#endif /* CONFIG_RK_CLOCK_PROC */
|
||||
|
||||
|
||||
@@ -24,6 +24,7 @@
|
||||
#include <mach/cru.h>
|
||||
#include <mach/iomux.h>
|
||||
#include <mach/clock.h>
|
||||
#include <mach/dvfs.h>
|
||||
#include "clock.h"
|
||||
//#include <mach/pmu.h>
|
||||
|
||||
@@ -126,8 +127,19 @@ struct pll_clk_set {
|
||||
}
|
||||
|
||||
static const struct apll_clk_set apll_clks[] = {
|
||||
_APLL_SET_CLKS( 650, 6, 325, 2, 1, 1, 0, 41, 21, 81, 21, 21),
|
||||
_APLL_SET_CLKS(1000, 3, 125, 1, 1, 1, 0, 41, 21, 41, 21, 21),
|
||||
_APLL_SET_CLKS(1248, 1, 52, 1, 1, 1, 0, 41, 21, 41, 21, 21),
|
||||
_APLL_SET_CLKS(1200, 1, 50, 1, 1, 1, 0, 41, 21, 41, 21, 21),
|
||||
_APLL_SET_CLKS(1104, 1, 46, 1, 1, 1, 0, 41, 21, 41, 21, 21),
|
||||
_APLL_SET_CLKS(1008, 1, 42, 1, 1, 1, 0, 41, 21, 41, 21, 21),
|
||||
_APLL_SET_CLKS( 912, 1, 38, 1, 1, 1, 0, 41, 21, 41, 21, 21),
|
||||
_APLL_SET_CLKS( 816, 1, 34, 1, 1, 1, 0, 41, 21, 41, 21, 21),
|
||||
_APLL_SET_CLKS( 696, 1, 29, 1, 1, 1, 0, 41, 21, 41, 21, 21),
|
||||
_APLL_SET_CLKS( 600, 1, 25, 1, 1, 1, 0, 41, 21, 41, 21, 21),
|
||||
_APLL_SET_CLKS( 504, 1, 21, 1, 1, 1, 0, 41, 21, 41, 21, 21),
|
||||
_APLL_SET_CLKS( 408, 1, 17, 1, 1, 1, 0, 41, 21, 41, 21, 21),
|
||||
_APLL_SET_CLKS( 312, 1, 52, 2, 2, 1, 0, 41, 21, 41, 21, 21),
|
||||
_APLL_SET_CLKS( 216, 1, 36, 2, 2, 1, 0, 41, 21, 41, 21, 21),
|
||||
_APLL_SET_CLKS( 0, 1, 0, 1, 1, 1, 0, 41, 21, 41, 21, 21),
|
||||
};
|
||||
|
||||
#define _PLL_SET_CLKS(_mhz, _refdiv, _fbdiv, _postdiv1, _postdiv2, _dsmpd, _frac) \
|
||||
@@ -734,7 +746,7 @@ static int apll_clk_set_rate(struct clk *clk, unsigned long rate)
|
||||
CLKDATA_DBG("pllcon0 %08x\n", cru_readl(PLL_CONS(0,0)));
|
||||
CLKDATA_DBG("pllcon1 %08x\n", cru_readl(PLL_CONS(0,1)));
|
||||
CLKDATA_DBG("pllcon2 %08x\n", cru_readl(PLL_CONS(0,2)));
|
||||
CLKDATA_DBG("pllcon3 %08x\n", cru_readl(PLL_CONS(0,3)));
|
||||
//CLKDATA_DBG("pllcon3 %08x\n", cru_readl(PLL_CONS(0,3)));
|
||||
CLKDATA_DBG("clksel0 %08x\n", cru_readl(CRU_CLKSELS_CON(0)));
|
||||
CLKDATA_DBG("clksel1 %08x\n", cru_readl(CRU_CLKSELS_CON(1)));
|
||||
if(clk_set->rate==rate) {
|
||||
@@ -761,7 +773,7 @@ static int apll_clk_set_rate(struct clk *clk, unsigned long rate)
|
||||
//rk2928_clock_udelay(5);
|
||||
|
||||
//wating lock state
|
||||
rk2928_clock_udelay(clk_set->rst_dly);
|
||||
rk2928_clock_udelay(clk_set->rst_dly);
|
||||
pll_wait_lock(pll_id);
|
||||
|
||||
//return form slow
|
||||
@@ -1003,7 +1015,7 @@ static int arm_core_clk_set_rate(struct clk *c, unsigned long rate)
|
||||
//set arm pll div 1
|
||||
//set_cru_bits_w_msk(0,c->div_mask,c->div_shift,c->clksel_con);
|
||||
|
||||
CLKDATA_DBG("Failed to change clk pll %s to %lu\n",c->name,rate);
|
||||
CLKDATA_DBG("change clk pll %s to %lu\n",c->name,rate);
|
||||
ret = clk_set_rate_nolock(c->parent, rate);
|
||||
if (ret) {
|
||||
CLKDATA_ERR("Failed to change clk pll %s to %lu\n",c->name,rate);
|
||||
@@ -1013,6 +1025,7 @@ static int arm_core_clk_set_rate(struct clk *c, unsigned long rate)
|
||||
return 0;
|
||||
}
|
||||
static struct clk clk_core_pre = {
|
||||
//.name = "cpu",
|
||||
.name = "core_pre",
|
||||
.parent = &arm_pll_clk,
|
||||
.recalc = clksel_recalc_div,
|
||||
@@ -1599,6 +1612,7 @@ static struct clk clk_saradc = {
|
||||
// name: gpu_aclk
|
||||
static struct clk *clk_gpu_pre_parents[] = SELECT_FROM_2PLLS_CG;
|
||||
static struct clk clk_gpu_pre = {
|
||||
//.name = "gpu",
|
||||
.name = "gpu_pre",
|
||||
.parent = &general_pll_clk,
|
||||
.mode = gate_mode,
|
||||
@@ -2428,7 +2442,7 @@ static void __init rk2928_clock_common_init(unsigned long gpll_rate,unsigned lon
|
||||
{
|
||||
CLKDATA_DBG("ENTER %s\n", __func__);
|
||||
|
||||
clk_set_rate_nolock(&clk_core_pre, 1000 * MHZ);//816
|
||||
clk_set_rate_nolock(&clk_core_pre, 816 * MHZ);//816
|
||||
//general
|
||||
clk_set_rate_nolock(&general_pll_clk, gpll_rate);
|
||||
//code pll
|
||||
@@ -2530,8 +2544,8 @@ void __init _rk2928_clock_data_init(unsigned long gpll,unsigned long cpll,int fl
|
||||
|
||||
void __init rk2928_clock_data_init(unsigned long gpll,unsigned long cpll,u32 flags)
|
||||
{
|
||||
printk("%s version: 2012-8-7\n", __func__);
|
||||
printk("%s version: 2012-8-14\n", __func__);
|
||||
_rk2928_clock_data_init(gpll,cpll,flags);
|
||||
//rk2928_dvfs_init();
|
||||
rk30_dvfs_init();
|
||||
}
|
||||
|
||||
|
||||
711
arch/arm/mach-rk2928/cpufreq.c
Normal file
711
arch/arm/mach-rk2928/cpufreq.c
Normal file
@@ -0,0 +1,711 @@
|
||||
/*
|
||||
* Copyright (C) 2012 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.
|
||||
*
|
||||
*/
|
||||
//#define DEBUG 1
|
||||
#define pr_fmt(fmt) "cpufreq: " fmt
|
||||
#include <linux/clk.h>
|
||||
#include <linux/cpufreq.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/reboot.h>
|
||||
#include <linux/suspend.h>
|
||||
#include <linux/tick.h>
|
||||
#include <linux/workqueue.h>
|
||||
#include <asm/smp_plat.h>
|
||||
#include <asm/cpu.h>
|
||||
#include <mach/dvfs.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/regulator/consumer.h>
|
||||
#include <linux/fs.h>
|
||||
#include <linux/string.h>
|
||||
#include <linux/earlysuspend.h>
|
||||
#include <asm/unistd.h>
|
||||
#include <asm/uaccess.h>
|
||||
#ifdef DEBUG
|
||||
#define FREQ_PRINTK_DBG(fmt, args...) pr_debug(fmt, ## args)
|
||||
#define FREQ_PRINTK_LOG(fmt, args...) pr_debug(fmt, ## args)
|
||||
#else
|
||||
#define FREQ_PRINTK_DBG(fmt, args...) do {} while(0)
|
||||
#define FREQ_PRINTK_LOG(fmt, args...) do {} while(0)
|
||||
#endif
|
||||
#define FREQ_PRINTK_ERR(fmt, args...) pr_err(fmt, ## args)
|
||||
|
||||
/* Frequency table index must be sequential starting at 0 */
|
||||
static struct cpufreq_frequency_table default_freq_table[] = {
|
||||
{.frequency = 816 * 1000, .index = 1100 * 1000},
|
||||
{.frequency = CPUFREQ_TABLE_END},
|
||||
};
|
||||
|
||||
static struct cpufreq_frequency_table *freq_table = default_freq_table;
|
||||
static unsigned int max_freq = -1;
|
||||
|
||||
/*********************************************************/
|
||||
|
||||
/* additional symantics for "relation" in cpufreq with pm */
|
||||
#define DISABLE_FURTHER_CPUFREQ 0x10
|
||||
#define ENABLE_FURTHER_CPUFREQ 0x20
|
||||
#define MASK_FURTHER_CPUFREQ 0x30
|
||||
/* With 0x00(NOCHANGE), it depends on the previous "further" status */
|
||||
static int no_cpufreq_access;
|
||||
static unsigned int suspend_freq = 816 * 1000;
|
||||
|
||||
static struct workqueue_struct *freq_wq;
|
||||
static struct clk *cpu_clk;
|
||||
static struct clk *cpu_pll;
|
||||
static struct clk *cpu_gpll;
|
||||
|
||||
|
||||
static DEFINE_MUTEX(cpufreq_mutex);
|
||||
|
||||
static struct clk *gpu_clk;
|
||||
#define GPU_MAX_RATE 350*1000*1000
|
||||
|
||||
static int cpufreq_scale_rate_for_dvfs(struct clk *clk, unsigned long rate, dvfs_set_rate_callback set_rate);
|
||||
|
||||
/*******************************************************/
|
||||
static unsigned int rk30_getspeed(unsigned int cpu)
|
||||
{
|
||||
unsigned long rate;
|
||||
|
||||
if (cpu >= NR_CPUS)
|
||||
return 0;
|
||||
|
||||
rate = clk_get_rate(cpu_clk) / 1000;
|
||||
return rate;
|
||||
}
|
||||
|
||||
static bool rk30_cpufreq_is_ondemand_policy(struct cpufreq_policy *policy)
|
||||
{
|
||||
char c = 0;
|
||||
if (policy && policy->governor)
|
||||
c = policy->governor->name[0];
|
||||
return (c == 'o' || c == 'i' || c == 'c' || c == 'h');
|
||||
}
|
||||
|
||||
/**********************thermal limit**************************/
|
||||
#define CONFIG_RK30_CPU_FREQ_LIMIT_BY_TEMP
|
||||
|
||||
#ifdef CONFIG_RK30_CPU_FREQ_LIMIT_BY_TEMP
|
||||
static void rk30_cpufreq_temp_limit_work_func(struct work_struct *work);
|
||||
|
||||
static DECLARE_DELAYED_WORK(rk30_cpufreq_temp_limit_work, rk30_cpufreq_temp_limit_work_func);
|
||||
|
||||
static unsigned int temp_limt_freq = -1;
|
||||
module_param(temp_limt_freq, uint, 0444);
|
||||
|
||||
#define TEMP_LIMIT_FREQ 816000
|
||||
|
||||
static const struct cpufreq_frequency_table temp_limits[] = {
|
||||
{.frequency = 1416 * 1000, .index = 50},
|
||||
{.frequency = 1200 * 1000, .index = 55},
|
||||
{.frequency = 1008 * 1000, .index = 60},
|
||||
{.frequency = 816 * 1000, .index = 75},
|
||||
};
|
||||
|
||||
//extern int rk30_tsadc_get_temp(unsigned int chn);
|
||||
|
||||
//#define get_cpu_thermal() rk30_tsadc_get_temp(0)
|
||||
static void rk30_cpufreq_temp_limit_work_func(struct work_struct *work)
|
||||
{
|
||||
struct cpufreq_policy *policy;
|
||||
int temp = 25, i;
|
||||
unsigned int new = -1;
|
||||
|
||||
if (clk_get_rate(gpu_clk) > GPU_MAX_RATE)
|
||||
goto out;
|
||||
|
||||
//temp = max(rk30_tsadc_get_temp(0), rk30_tsadc_get_temp(1));
|
||||
FREQ_PRINTK_LOG("cpu_thermal(%d)\n", temp);
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(temp_limits); i++) {
|
||||
if (temp > temp_limits[i].index) {
|
||||
new = temp_limits[i].frequency;
|
||||
}
|
||||
}
|
||||
if (temp_limt_freq != new) {
|
||||
temp_limt_freq = new;
|
||||
if (new != -1) {
|
||||
FREQ_PRINTK_DBG("temp_limit set rate %d kHz\n", temp_limt_freq);
|
||||
policy = cpufreq_cpu_get(0);
|
||||
cpufreq_driver_target(policy, policy->cur, CPUFREQ_RELATION_L);
|
||||
cpufreq_cpu_put(policy);
|
||||
}
|
||||
}
|
||||
|
||||
out:
|
||||
queue_delayed_work(freq_wq, &rk30_cpufreq_temp_limit_work, HZ);
|
||||
}
|
||||
|
||||
static int rk30_cpufreq_notifier_policy(struct notifier_block *nb,
|
||||
unsigned long val, void *data)
|
||||
{
|
||||
struct cpufreq_policy *policy = data;
|
||||
|
||||
if (val != CPUFREQ_NOTIFY)
|
||||
return 0;
|
||||
|
||||
if (rk30_cpufreq_is_ondemand_policy(policy)) {
|
||||
FREQ_PRINTK_DBG("queue work\n");
|
||||
queue_delayed_work(freq_wq, &rk30_cpufreq_temp_limit_work, 0);
|
||||
} else {
|
||||
FREQ_PRINTK_DBG("cancel work\n");
|
||||
cancel_delayed_work_sync(&rk30_cpufreq_temp_limit_work);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct notifier_block notifier_policy_block = {
|
||||
.notifier_call = rk30_cpufreq_notifier_policy
|
||||
};
|
||||
#endif
|
||||
|
||||
/************************************dvfs tst************************************/
|
||||
//#define CPU_FREQ_DVFS_TST
|
||||
#ifdef CPU_FREQ_DVFS_TST
|
||||
static unsigned int freq_dvfs_tst_rate;
|
||||
static void rk30_cpufreq_dvsf_tst_work_func(struct work_struct *work);
|
||||
static DECLARE_DELAYED_WORK(rk30_cpufreq_dvsf_tst_work, rk30_cpufreq_dvsf_tst_work_func);
|
||||
static int test_count;
|
||||
#define TEST_FRE_NUM 11
|
||||
static int test_tlb_rate[TEST_FRE_NUM] = { 504, 1008, 504, 1200, 252, 816, 1416, 252, 1512, 252, 816 };
|
||||
//static int test_tlb_rate[TEST_FRE_NUM]={504,1008,504,1200,252,816,1416,126,1512,126,816};
|
||||
|
||||
#define TEST_GPU_NUM 3
|
||||
|
||||
static int test_tlb_gpu[TEST_GPU_NUM] = { 360, 400, 180 };
|
||||
static int test_tlb_ddr[TEST_GPU_NUM] = { 401, 200, 500 };
|
||||
|
||||
static int gpu_ddr = 0;
|
||||
|
||||
static void rk30_cpufreq_dvsf_tst_work_func(struct work_struct *work)
|
||||
{
|
||||
struct cpufreq_policy *policy = cpufreq_cpu_get(0);
|
||||
|
||||
gpu_ddr++;
|
||||
|
||||
#if 0
|
||||
FREQ_PRINTK_LOG("cpufreq_dvsf_tst,ddr%u,gpu%u\n",
|
||||
test_tlb_ddr[gpu_ddr % TEST_GPU_NUM],
|
||||
test_tlb_gpu[gpu_ddr % TEST_GPU_NUM]);
|
||||
clk_set_rate(ddr_clk, test_tlb_ddr[gpu_ddr % TEST_GPU_NUM] * 1000 * 1000);
|
||||
clk_set_rate(gpu_clk, test_tlb_gpu[gpu_ddr % TEST_GPU_NUM] * 1000 * 1000);
|
||||
#endif
|
||||
|
||||
test_count++;
|
||||
freq_dvfs_tst_rate = test_tlb_rate[test_count % TEST_FRE_NUM] * 1000;
|
||||
FREQ_PRINTK_LOG("cpufreq_dvsf_tst,cpu set rate %d\n", freq_dvfs_tst_rate);
|
||||
cpufreq_driver_target(policy, policy->cur, CPUFREQ_RELATION_L);
|
||||
cpufreq_cpu_put(policy);
|
||||
|
||||
queue_delayed_work(freq_wq, &rk30_cpufreq_dvsf_tst_work, msecs_to_jiffies(1000));
|
||||
}
|
||||
#endif /* CPU_FREQ_DVFS_TST */
|
||||
|
||||
/***********************************************************************/
|
||||
static int rk30_verify_speed(struct cpufreq_policy *policy)
|
||||
{
|
||||
if (!freq_table)
|
||||
return -EINVAL;
|
||||
return cpufreq_frequency_table_verify(policy, freq_table);
|
||||
}
|
||||
|
||||
static int rk30_cpu_init(struct cpufreq_policy *policy)
|
||||
{
|
||||
if (policy->cpu == 0) {
|
||||
int i;
|
||||
struct clk *ddr_clk;
|
||||
gpu_clk = clk_get(NULL, "gpu");
|
||||
if (!IS_ERR(gpu_clk))
|
||||
clk_enable_dvfs(gpu_clk);
|
||||
#if 0
|
||||
|
||||
ddr_clk = clk_get(NULL, "ddr");
|
||||
if (!IS_ERR(ddr_clk))
|
||||
{
|
||||
clk_enable_dvfs(ddr_clk);
|
||||
clk_set_rate(ddr_clk,clk_get_rate(ddr_clk)-1);
|
||||
}
|
||||
|
||||
#endif
|
||||
cpu_clk = clk_get(NULL, "cpu");
|
||||
cpu_pll = clk_get(NULL, "arm_pll");
|
||||
|
||||
cpu_gpll = clk_get(NULL, "arm_gpll");
|
||||
if (IS_ERR(cpu_clk))
|
||||
return PTR_ERR(cpu_clk);
|
||||
|
||||
dvfs_clk_register_set_rate_callback(cpu_clk, cpufreq_scale_rate_for_dvfs);
|
||||
freq_table = dvfs_get_freq_volt_table(cpu_clk);
|
||||
if (freq_table == NULL) {
|
||||
freq_table = default_freq_table;
|
||||
}
|
||||
max_freq = freq_table[0].frequency;
|
||||
for (i = 0; freq_table[i].frequency != CPUFREQ_TABLE_END; i++) {
|
||||
max_freq = max(max_freq, freq_table[i].frequency);
|
||||
}
|
||||
clk_enable_dvfs(cpu_clk);
|
||||
|
||||
/* Limit gpu frequency between 133M to 400M */
|
||||
#if 0
|
||||
dvfs_clk_enable_limit(gpu_clk, 133000000, 400000000);
|
||||
#endif
|
||||
|
||||
freq_wq = create_singlethread_workqueue("rk30_cpufreqd");
|
||||
#ifdef CONFIG_RK30_CPU_FREQ_LIMIT_BY_TEMP
|
||||
if (rk30_cpufreq_is_ondemand_policy(policy)) {
|
||||
queue_delayed_work(freq_wq, &rk30_cpufreq_temp_limit_work, 0*HZ);
|
||||
}
|
||||
cpufreq_register_notifier(¬ifier_policy_block, CPUFREQ_POLICY_NOTIFIER);
|
||||
#endif
|
||||
#ifdef CPU_FREQ_DVFS_TST
|
||||
queue_delayed_work(freq_wq, &rk30_cpufreq_dvsf_tst_work, msecs_to_jiffies(20 * 1000));
|
||||
#endif
|
||||
}
|
||||
//set freq min max
|
||||
cpufreq_frequency_table_cpuinfo(policy, freq_table);
|
||||
//sys nod
|
||||
cpufreq_frequency_table_get_attr(freq_table, policy->cpu);
|
||||
|
||||
policy->cur = rk30_getspeed(0);
|
||||
|
||||
policy->cpuinfo.transition_latency = 40 * NSEC_PER_USEC; // make ondemand default sampling_rate to 40000
|
||||
|
||||
/*
|
||||
* On rk30 SMP configuartion, both processors share the voltage
|
||||
* and clock. So both CPUs needs to be scaled together and hence
|
||||
* needs software co-ordination. Use cpufreq affected_cpus
|
||||
* interface to handle this scenario. Additional is_smp() check
|
||||
* is to keep SMP_ON_UP build working.
|
||||
*/
|
||||
if (is_smp())
|
||||
cpumask_setall(policy->cpus);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int rk30_cpu_exit(struct cpufreq_policy *policy)
|
||||
{
|
||||
if (policy->cpu != 0)
|
||||
return 0;
|
||||
|
||||
cpufreq_frequency_table_cpuinfo(policy, freq_table);
|
||||
clk_put(cpu_clk);
|
||||
#ifdef CONFIG_RK30_CPU_FREQ_LIMIT_BY_TEMP
|
||||
cpufreq_unregister_notifier(¬ifier_policy_block, CPUFREQ_POLICY_NOTIFIER);
|
||||
if (freq_wq)
|
||||
cancel_delayed_work(&rk30_cpufreq_temp_limit_work);
|
||||
#endif
|
||||
if (freq_wq) {
|
||||
flush_workqueue(freq_wq);
|
||||
destroy_workqueue(freq_wq);
|
||||
freq_wq = NULL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct freq_attr *rk30_cpufreq_attr[] = {
|
||||
&cpufreq_freq_attr_scaling_available_freqs,
|
||||
NULL,
|
||||
};
|
||||
|
||||
/**************************earlysuspend freeze cpu frequency******************************/
|
||||
static struct early_suspend ff_early_suspend;
|
||||
|
||||
#define FILE_GOV_MODE "/sys/devices/system/cpu/cpu0/cpufreq/scaling_governor"
|
||||
#define FILE_SETSPEED "/sys/devices/system/cpu/cpu0/cpufreq/scaling_setspeed"
|
||||
#define FILE_CUR_FREQ "/sys/devices/system/cpu/cpu0/cpufreq/scaling_cur_freq"
|
||||
|
||||
#define FF_DEBUG(fmt, args...) printk(KERN_DEBUG "FREEZE FREQ DEBUG:\t"fmt, ##args)
|
||||
#define FF_ERROR(fmt, args...) printk(KERN_ERR "FREEZE FREQ ERROR:\t"fmt, ##args)
|
||||
|
||||
static int ff_read(char *file_path, char *buf)
|
||||
{
|
||||
struct file *file = NULL;
|
||||
mm_segment_t old_fs;
|
||||
loff_t offset = 0;
|
||||
|
||||
FF_DEBUG("read %s\n", file_path);
|
||||
file = filp_open(file_path, O_RDONLY, 0);
|
||||
|
||||
if (IS_ERR(file)) {
|
||||
FF_ERROR("%s error open file %s\n", __func__, file_path);
|
||||
return -1;
|
||||
}
|
||||
|
||||
old_fs = get_fs();
|
||||
set_fs(KERNEL_DS);
|
||||
|
||||
file->f_op->read(file, (char *)buf, 32, &offset);
|
||||
sscanf(buf, "%s", buf);
|
||||
|
||||
set_fs(old_fs);
|
||||
filp_close(file, NULL);
|
||||
|
||||
file = NULL;
|
||||
|
||||
return 0;
|
||||
|
||||
}
|
||||
|
||||
static int ff_write(char *file_path, char *buf)
|
||||
{
|
||||
struct file *file = NULL;
|
||||
mm_segment_t old_fs;
|
||||
loff_t offset = 0;
|
||||
|
||||
FF_DEBUG("write %s %s size = %d\n", file_path, buf, strlen(buf));
|
||||
file = filp_open(file_path, O_RDWR, 0);
|
||||
|
||||
if (IS_ERR(file)) {
|
||||
FF_ERROR("%s error open file %s\n", __func__, file_path);
|
||||
return -1;
|
||||
}
|
||||
|
||||
old_fs = get_fs();
|
||||
set_fs(KERNEL_DS);
|
||||
|
||||
file->f_op->write(file, (char *)buf, strlen(buf), &offset);
|
||||
|
||||
set_fs(old_fs);
|
||||
filp_close(file, NULL);
|
||||
|
||||
file = NULL;
|
||||
|
||||
return 0;
|
||||
|
||||
}
|
||||
|
||||
static void ff_scale_votlage(char *name, int volt)
|
||||
{
|
||||
struct regulator* regulator;
|
||||
int ret = 0;
|
||||
|
||||
FF_DEBUG("enter %s\n", __func__);
|
||||
regulator = dvfs_get_regulator(name);
|
||||
if (!regulator) {
|
||||
FF_ERROR("get regulator %s ERROR\n", name);
|
||||
return ;
|
||||
}
|
||||
|
||||
ret = regulator_set_voltage(regulator, volt, volt);
|
||||
if (ret != 0) {
|
||||
FF_ERROR("set voltage error %s %d, ret = %d\n", name, volt, ret);
|
||||
}
|
||||
|
||||
}
|
||||
int clk_set_parent_force(struct clk *clk, struct clk *parent);
|
||||
static void ff_early_suspend_func(struct early_suspend *h)
|
||||
{
|
||||
char buf[32];
|
||||
FF_DEBUG("enter %s\n", __func__);
|
||||
if (ff_read(FILE_GOV_MODE, buf) != 0) {
|
||||
FF_ERROR("read current governor error\n");
|
||||
return ;
|
||||
} else {
|
||||
FF_DEBUG("current governor = %s\n", buf);
|
||||
}
|
||||
|
||||
strcpy(buf, "userspace");
|
||||
if (ff_write(FILE_GOV_MODE, buf) != 0) {
|
||||
FF_ERROR("set current governor error\n");
|
||||
return ;
|
||||
}
|
||||
|
||||
strcpy(buf, "252000");
|
||||
if (ff_write(FILE_SETSPEED, buf) != 0) {
|
||||
FF_ERROR("set speed to 252MHz error\n");
|
||||
return ;
|
||||
}
|
||||
|
||||
if (!IS_ERR(cpu_pll)&&!IS_ERR(cpu_gpll)&&!IS_ERR(cpu_clk))
|
||||
{
|
||||
clk_set_parent_force(cpu_clk,cpu_gpll);
|
||||
clk_set_rate(cpu_clk,300*1000*1000);
|
||||
|
||||
clk_disable_dvfs(cpu_clk);
|
||||
}
|
||||
if (!IS_ERR(gpu_clk))
|
||||
dvfs_clk_enable_limit(gpu_clk,75*1000*1000,133*1000*1000);
|
||||
|
||||
//ff_scale_votlage("vdd_cpu", 1000000);
|
||||
//ff_scale_votlage("vdd_core", 1000000);
|
||||
#ifdef CONFIG_HOTPLUG_CPU
|
||||
cpu_down(1);
|
||||
#endif
|
||||
}
|
||||
|
||||
static void ff_early_resume_func(struct early_suspend *h)
|
||||
{
|
||||
char buf[32];
|
||||
FF_DEBUG("enter %s\n", __func__);
|
||||
|
||||
if (!IS_ERR(cpu_pll)&&!IS_ERR(cpu_gpll)&&!IS_ERR(cpu_clk))
|
||||
{
|
||||
clk_set_parent_force(cpu_clk,cpu_pll);
|
||||
clk_set_rate(cpu_clk,300*1000*1000);
|
||||
clk_enable_dvfs(cpu_clk);
|
||||
}
|
||||
|
||||
if (!IS_ERR(gpu_clk))
|
||||
dvfs_clk_disable_limit(gpu_clk);
|
||||
#ifdef CONFIG_HOTPLUG_CPU
|
||||
cpu_up(1);
|
||||
#endif
|
||||
if (ff_read(FILE_GOV_MODE, buf) != 0) {
|
||||
FF_ERROR("read current governor error\n");
|
||||
return ;
|
||||
} else {
|
||||
FF_DEBUG("current governor = %s\n", buf);
|
||||
}
|
||||
|
||||
if (ff_read(FILE_CUR_FREQ, buf) != 0) {
|
||||
FF_ERROR("read current frequency error\n");
|
||||
return ;
|
||||
} else {
|
||||
FF_DEBUG("current frequency = %s\n", buf);
|
||||
}
|
||||
|
||||
strcpy(buf, "interactive");
|
||||
if (ff_write(FILE_GOV_MODE, buf) != 0) {
|
||||
FF_ERROR("set current governor error\n");
|
||||
return ;
|
||||
}
|
||||
|
||||
strcpy(buf, "interactive");
|
||||
if (ff_write(FILE_GOV_MODE, buf) != 0) {
|
||||
FF_ERROR("set current governor error\n");
|
||||
return ;
|
||||
}
|
||||
}
|
||||
|
||||
static int __init ff_init(void)
|
||||
{
|
||||
FF_DEBUG("enter %s\n", __func__);
|
||||
ff_early_suspend.suspend = ff_early_suspend_func;
|
||||
ff_early_suspend.resume = ff_early_resume_func;
|
||||
ff_early_suspend.level = EARLY_SUSPEND_LEVEL_DISABLE_FB + 100;
|
||||
register_early_suspend(&ff_early_suspend);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void __exit ff_exit(void)
|
||||
{
|
||||
FF_DEBUG("enter %s\n", __func__);
|
||||
unregister_early_suspend(&ff_early_suspend);
|
||||
}
|
||||
|
||||
|
||||
/**************************target freq******************************/
|
||||
static unsigned int cpufreq_scale_limt(unsigned int target_freq, struct cpufreq_policy *policy)
|
||||
{
|
||||
bool is_ondemand = rk30_cpufreq_is_ondemand_policy(policy);
|
||||
static bool is_booting = true;
|
||||
|
||||
if (is_ondemand && clk_get_rate(gpu_clk) > GPU_MAX_RATE) // high performance?
|
||||
return max_freq;
|
||||
if (is_ondemand && is_booting && target_freq >= 1600 * 1000) {
|
||||
s64 boottime_ms = ktime_to_ms(ktime_get_boottime());
|
||||
if (boottime_ms > 30 * MSEC_PER_SEC) {
|
||||
is_booting = false;
|
||||
} else {
|
||||
target_freq = 1416 * 1000;
|
||||
}
|
||||
}
|
||||
#ifdef CONFIG_RK30_CPU_FREQ_LIMIT_BY_TEMP
|
||||
if (is_ondemand && target_freq > policy->cur && policy->cur >= TEMP_LIMIT_FREQ) {
|
||||
unsigned int i;
|
||||
if (cpufreq_frequency_table_target(policy, freq_table, policy->cur + 1, CPUFREQ_RELATION_L, &i) == 0) {
|
||||
unsigned int f = freq_table[i].frequency;
|
||||
if (f < target_freq) {
|
||||
target_freq = f;
|
||||
}
|
||||
}
|
||||
}
|
||||
/*
|
||||
* If the new frequency is more than the thermal max allowed
|
||||
* frequency, go ahead and scale the mpu device to proper frequency.
|
||||
*/
|
||||
if (is_ondemand) {
|
||||
target_freq = min(target_freq, temp_limt_freq);
|
||||
}
|
||||
#endif
|
||||
#ifdef CPU_FREQ_DVFS_TST
|
||||
if (freq_dvfs_tst_rate) {
|
||||
target_freq = freq_dvfs_tst_rate;
|
||||
freq_dvfs_tst_rate = 0;
|
||||
}
|
||||
#endif
|
||||
return target_freq;
|
||||
}
|
||||
|
||||
int cpufreq_scale_rate_for_dvfs(struct clk *clk, unsigned long rate, dvfs_set_rate_callback set_rate)
|
||||
{
|
||||
unsigned int i;
|
||||
int ret = -EINVAL;
|
||||
struct cpufreq_freqs freqs;
|
||||
|
||||
freqs.new = rate / 1000;
|
||||
freqs.old = rk30_getspeed(0);
|
||||
|
||||
for_each_online_cpu(freqs.cpu) {
|
||||
cpufreq_notify_transition(&freqs, CPUFREQ_PRECHANGE);
|
||||
}
|
||||
FREQ_PRINTK_DBG("cpufreq_scale_rate_for_dvfs(%lu)\n", rate);
|
||||
ret = set_rate(clk, rate);
|
||||
|
||||
#ifdef CONFIG_SMP
|
||||
/*
|
||||
* Note that loops_per_jiffy is not updated on SMP systems in
|
||||
* cpufreq driver. So, update the per-CPU loops_per_jiffy value
|
||||
* on frequency transition. We need to update all dependent CPUs.
|
||||
*/
|
||||
for_each_possible_cpu(i) {
|
||||
per_cpu(cpu_data, i).loops_per_jiffy = loops_per_jiffy;
|
||||
}
|
||||
#endif
|
||||
|
||||
freqs.new = rk30_getspeed(0);
|
||||
/* notifiers */
|
||||
for_each_online_cpu(freqs.cpu) {
|
||||
cpufreq_notify_transition(&freqs, CPUFREQ_POSTCHANGE);
|
||||
}
|
||||
return ret;
|
||||
|
||||
}
|
||||
|
||||
static int rk30_target(struct cpufreq_policy *policy, unsigned int target_freq, unsigned int relation)
|
||||
{
|
||||
unsigned int i, new_rate = 0;
|
||||
int ret = 0;
|
||||
|
||||
if (!freq_table) {
|
||||
FREQ_PRINTK_ERR("no freq table!\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
mutex_lock(&cpufreq_mutex);
|
||||
|
||||
if (relation & ENABLE_FURTHER_CPUFREQ)
|
||||
no_cpufreq_access--;
|
||||
if (no_cpufreq_access) {
|
||||
#ifdef CONFIG_PM_VERBOSE
|
||||
pr_err("denied access to %s as it is disabled temporarily\n", __func__);
|
||||
#endif
|
||||
ret = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
if (relation & DISABLE_FURTHER_CPUFREQ)
|
||||
no_cpufreq_access++;
|
||||
relation &= ~MASK_FURTHER_CPUFREQ;
|
||||
|
||||
ret = cpufreq_frequency_table_target(policy, freq_table, target_freq, relation, &i);
|
||||
if (ret) {
|
||||
FREQ_PRINTK_ERR("no freq match for %d(ret=%d)\n", target_freq, ret);
|
||||
goto out;
|
||||
}
|
||||
new_rate = freq_table[i].frequency;
|
||||
if (!no_cpufreq_access)
|
||||
new_rate = cpufreq_scale_limt(new_rate, policy);
|
||||
|
||||
FREQ_PRINTK_LOG("cpufreq req=%u,new=%u(was=%u)\n", target_freq, new_rate, rk30_getspeed(0));
|
||||
if (new_rate == rk30_getspeed(0))
|
||||
goto out;
|
||||
ret = clk_set_rate(cpu_clk, new_rate * 1000);
|
||||
out:
|
||||
mutex_unlock(&cpufreq_mutex);
|
||||
FREQ_PRINTK_DBG("cpureq set rate (%u) end\n", new_rate);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int rk30_cpufreq_pm_notifier_event(struct notifier_block *this,
|
||||
unsigned long event, void *ptr)
|
||||
{
|
||||
int ret = NOTIFY_DONE;
|
||||
struct cpufreq_policy *policy = cpufreq_cpu_get(0);
|
||||
|
||||
if (!policy)
|
||||
return ret;
|
||||
|
||||
if (!rk30_cpufreq_is_ondemand_policy(policy))
|
||||
goto out;
|
||||
|
||||
switch (event) {
|
||||
case PM_SUSPEND_PREPARE:
|
||||
ret = cpufreq_driver_target(policy, suspend_freq, DISABLE_FURTHER_CPUFREQ | CPUFREQ_RELATION_H);
|
||||
if (ret < 0) {
|
||||
ret = NOTIFY_BAD;
|
||||
goto out;
|
||||
}
|
||||
ret = NOTIFY_OK;
|
||||
break;
|
||||
case PM_POST_RESTORE:
|
||||
case PM_POST_SUSPEND:
|
||||
cpufreq_driver_target(policy, suspend_freq, ENABLE_FURTHER_CPUFREQ | CPUFREQ_RELATION_H);
|
||||
ret = NOTIFY_OK;
|
||||
break;
|
||||
}
|
||||
out:
|
||||
cpufreq_cpu_put(policy);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static struct notifier_block rk30_cpufreq_pm_notifier = {
|
||||
.notifier_call = rk30_cpufreq_pm_notifier_event,
|
||||
};
|
||||
|
||||
static int rk30_cpufreq_reboot_notifier_event(struct notifier_block *this,
|
||||
unsigned long event, void *ptr)
|
||||
{
|
||||
struct cpufreq_policy *policy = cpufreq_cpu_get(0);
|
||||
|
||||
if (policy) {
|
||||
cpufreq_driver_target(policy, suspend_freq, DISABLE_FURTHER_CPUFREQ | CPUFREQ_RELATION_H);
|
||||
cpufreq_cpu_put(policy);
|
||||
}
|
||||
|
||||
return NOTIFY_OK;
|
||||
}
|
||||
|
||||
static struct notifier_block rk30_cpufreq_reboot_notifier = {
|
||||
.notifier_call = rk30_cpufreq_reboot_notifier_event,
|
||||
};
|
||||
|
||||
static struct cpufreq_driver rk30_cpufreq_driver = {
|
||||
.flags = CPUFREQ_CONST_LOOPS,
|
||||
.verify = rk30_verify_speed,
|
||||
.target = rk30_target,
|
||||
.get = rk30_getspeed,
|
||||
.init = rk30_cpu_init,
|
||||
.exit = rk30_cpu_exit,
|
||||
.name = "rk30",
|
||||
.attr = rk30_cpufreq_attr,
|
||||
};
|
||||
|
||||
static int __init rk30_cpufreq_init(void)
|
||||
{
|
||||
register_pm_notifier(&rk30_cpufreq_pm_notifier);
|
||||
register_reboot_notifier(&rk30_cpufreq_reboot_notifier);
|
||||
return cpufreq_register_driver(&rk30_cpufreq_driver);
|
||||
}
|
||||
|
||||
static void __exit rk30_cpufreq_exit(void)
|
||||
{
|
||||
cpufreq_unregister_driver(&rk30_cpufreq_driver);
|
||||
}
|
||||
|
||||
MODULE_DESCRIPTION("cpufreq driver for rock chip rk30");
|
||||
MODULE_LICENSE("GPL");
|
||||
device_initcall(rk30_cpufreq_init);
|
||||
module_exit(rk30_cpufreq_exit);
|
||||
1923
arch/arm/mach-rk2928/dvfs.c
Normal file
1923
arch/arm/mach-rk2928/dvfs.c
Normal file
File diff suppressed because it is too large
Load Diff
183
arch/arm/mach-rk2928/include/mach/dvfs.h
Normal file
183
arch/arm/mach-rk2928/include/mach/dvfs.h
Normal file
@@ -0,0 +1,183 @@
|
||||
/* arch/arm/mach-rk30/rk30_dvfs.h
|
||||
*
|
||||
* Copyright (C) 2012 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 _RK30_DVFS_H_
|
||||
#define _RK30_DVFS_H_
|
||||
|
||||
#include <mach/clock.h>
|
||||
|
||||
typedef int (*vd_dvfs_target_callback)(struct clk *clk, unsigned long rate);
|
||||
|
||||
typedef int (*dvfs_set_rate_callback)(struct clk *clk, unsigned long rate);
|
||||
|
||||
typedef int (*clk_dvfs_target_callback)(struct clk *clk, unsigned long rate,
|
||||
dvfs_set_rate_callback set_rate);
|
||||
|
||||
/**
|
||||
* struct vd_node: To Store All Voltage Domains' info
|
||||
* @name: Voltage Domain's Name
|
||||
* @regulator_name: Voltage domain's regulator name
|
||||
* @cur_volt: Voltage Domain's Current Voltage
|
||||
* @regulator: Voltage Domain's regulator point
|
||||
* @node: Point of he Voltage Domain List Node
|
||||
* @pd_list: Head of Power Domain List Belongs to This Voltage Domain
|
||||
* @req_volt_list: The list of clocks requests
|
||||
* @dvfs_mutex: Lock
|
||||
* @vd_dvfs_target: Callback function
|
||||
*/
|
||||
|
||||
struct vd_node {
|
||||
char *name;
|
||||
char *regulator_name;
|
||||
int cur_volt;
|
||||
int volt_set_flag;
|
||||
struct regulator *regulator;
|
||||
struct list_head node;
|
||||
struct list_head pd_list;
|
||||
struct list_head req_volt_list;
|
||||
//struct mutex dvfs_mutex;
|
||||
vd_dvfs_target_callback vd_dvfs_target;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct pd_node: To Store All Power Domains' info
|
||||
* @name: Power Domain's Name
|
||||
* @cur_volt: Power Domain's Current Voltage
|
||||
* @pd_status: Power Domain's status
|
||||
* @vd: Voltage Domain the power domain belongs to
|
||||
* @pd_clk: Look power domain as a clock
|
||||
* @node: List node to Voltage Domain
|
||||
* @clk_list: Head of Power Domain's Clocks List
|
||||
*/
|
||||
struct pd_node {
|
||||
char *name;
|
||||
int cur_volt;
|
||||
unsigned char pd_status;
|
||||
struct vd_node *vd;
|
||||
//struct clk *pd_clk;
|
||||
struct list_head node;
|
||||
struct list_head clk_list;
|
||||
};
|
||||
|
||||
struct pd_node_lookup {
|
||||
struct pd_node *pd;
|
||||
};
|
||||
|
||||
struct clk_list{
|
||||
struct clk_node *dvfs_clk;
|
||||
struct list_head node;
|
||||
};
|
||||
|
||||
struct pds_list {
|
||||
struct clk_list clk_list;
|
||||
struct pd_node *pd;
|
||||
};
|
||||
|
||||
struct depend_list {
|
||||
int req_volt;
|
||||
struct clk_node *dvfs_clk;
|
||||
struct vd_node *dep_vd;
|
||||
struct list_head node2clk;
|
||||
struct list_head node2vd;
|
||||
struct cpufreq_frequency_table *dep_table;
|
||||
};
|
||||
|
||||
struct depend_lookup {
|
||||
char *clk_name;
|
||||
struct clk_node *dvfs_clk;
|
||||
struct vd_node *dep_vd;
|
||||
struct depend_list dep_list;
|
||||
struct cpufreq_frequency_table *dep_table;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct clk_node: To Store All dvfs clocks' info
|
||||
* @name: Dvfs clock's Name
|
||||
* @set_freq: Dvfs clock's Current Frequency
|
||||
* @set_volt: Dvfs clock's Current Voltage
|
||||
* @enable_dvfs: Sign if DVFS clock enable
|
||||
* @clk: System clk's point
|
||||
* @pds: Power Domains dvfs clock belongs to
|
||||
* @vd: Voltage Domains dvfs clock belongs to
|
||||
* @depend_list: Dvfs Clock depend list
|
||||
* @dvfs_nb: Notify list
|
||||
* @dvfs_table: Frequency and voltage table for dvfs
|
||||
* @clk_dvfs_target: Callback function
|
||||
*/
|
||||
struct clk_node {
|
||||
char *name;
|
||||
int set_freq; //KHZ
|
||||
int set_volt; //MV
|
||||
int enable_dvfs;
|
||||
int freq_limit_en; //sign if use limit frequency
|
||||
unsigned int min_rate; //limit min frequency
|
||||
unsigned int max_rate; //limit max frequency
|
||||
struct clk *clk;
|
||||
struct pds_list *pds;
|
||||
struct vd_node *vd;
|
||||
struct list_head depend_list;
|
||||
struct notifier_block *dvfs_nb;
|
||||
struct cpufreq_frequency_table *dvfs_table;
|
||||
clk_dvfs_target_callback clk_dvfs_target;
|
||||
};
|
||||
|
||||
struct dvfs_arm_table {
|
||||
unsigned int frequency; /* kHz - doesn't need to be in ascending
|
||||
* order */
|
||||
unsigned int cpu_volt; /* any */
|
||||
|
||||
unsigned int logic_volt;
|
||||
};
|
||||
|
||||
#ifdef CONFIG_DVFS
|
||||
int rk30_dvfs_init(void);
|
||||
int is_support_dvfs(struct clk_node *dvfs_info);
|
||||
int dvfs_set_rate(struct clk *clk, unsigned long rate);
|
||||
int clk_enable_dvfs(struct clk *clk);
|
||||
int clk_disable_dvfs(struct clk *clk);
|
||||
void dvfs_clk_register_set_rate_callback(struct clk *clk, clk_dvfs_target_callback clk_dvfs_target);
|
||||
struct cpufreq_frequency_table *dvfs_get_freq_volt_table(struct clk *clk);
|
||||
int dvfs_set_freq_volt_table(struct clk *clk, struct cpufreq_frequency_table *table);
|
||||
int dvfs_set_depend_table(struct clk *clk, char *vd_name, struct cpufreq_frequency_table *table);
|
||||
int dvfs_set_arm_logic_volt(struct dvfs_arm_table *dvfs_cpu_logic_table, struct cpufreq_frequency_table *cpu_dvfs_table, struct cpufreq_frequency_table *dep_cpu2core_table);
|
||||
struct regulator* dvfs_get_regulator(char *regulator_name);
|
||||
int dvfs_clk_enable_limit(struct clk *clk, unsigned int min_rate, unsigned max_rate);
|
||||
int dvfs_clk_disable_limit(struct clk *clk);
|
||||
|
||||
void avs_init(void);
|
||||
void avs_init_val_get(int index,int vol,char *s);
|
||||
int avs_set_scal_val(u8 avs_base);
|
||||
int dvfs_avs_scale_table(struct clk* clk, char* depend_vd_name);
|
||||
#else
|
||||
static inline int rk30_dvfs_init(void) { return 0; }
|
||||
static inline int is_support_dvfs(struct clk_node *dvfs_info) { return 0; }
|
||||
static inline int dvfs_set_rate(struct clk *clk, unsigned long rate) { return 0; }
|
||||
static inline int clk_enable_dvfs(struct clk *clk) { return 0; }
|
||||
static inline int clk_disable_dvfs(struct clk *clk) { return 0; }
|
||||
static inline void dvfs_clk_register_set_rate_callback(struct clk *clk, clk_dvfs_target_callback clk_dvfs_target) {}
|
||||
static inline struct cpufreq_frequency_table *dvfs_get_freq_volt_table(struct clk *clk) { return NULL; }
|
||||
static inline int dvfs_set_freq_volt_table(struct clk *clk, struct cpufreq_frequency_table *table) { return 0; }
|
||||
static inline int dvfs_set_depend_table(struct clk *clk, char *vd_name, struct cpufreq_frequency_table *table) {return 0;}
|
||||
static inline int dvfs_set_arm_logic_volt(struct dvfs_arm_table *dvfs_cpu_logic_table, struct cpufreq_frequency_table *cpu_dvfs_table, struct cpufreq_frequency_table *dep_cpu2core_table){ return 0; }
|
||||
static inline struct regulator* dvfs_get_regulator(char *regulator_name){ return NULL; }
|
||||
static inline int dvfs_clk_enable_limit(struct clk *clk, unsigned int min_rate, unsigned max_rate){ return 0; }
|
||||
static inline int dvfs_clk_disable_limit(struct clk *clk){ return 0; };
|
||||
|
||||
static inline void avs_init(void){};
|
||||
static inline void avs_init_val_get(int index, int vol, char *s){};
|
||||
static inline int avs_set_scal_val(u8 avs_base){ return 0; };
|
||||
static inline int dvfs_avs_scale_table(struct clk* clk, char* depend_vd_name){ return 0; };
|
||||
#endif
|
||||
|
||||
#endif
|
||||
Reference in New Issue
Block a user