rk30xx: add dvfs table auto adjust by leakage interface

This commit is contained in:
chenxing
2013-08-10 09:50:19 +08:00
parent f28bf8262b
commit 8b1e456728
3 changed files with 84 additions and 1 deletions

View File

@@ -26,6 +26,7 @@
#include <linux/delay.h>
#include <linux/io.h>
#include <linux/hrtimer.h>
#include <plat/efuse.h>
static int rk_dvfs_clk_notifier_event(struct notifier_block *this,
unsigned long event, void *ptr)
@@ -76,6 +77,86 @@ static int rk_dvfs_clk_notifier_event(struct notifier_block *this,
static struct notifier_block rk_dvfs_clk_notifier = {
.notifier_call = rk_dvfs_clk_notifier_event,
};
struct lkg_maxvolt {
int leakage_level;
unsigned int maxvolt;
};
static struct lkg_maxvolt lkg_volt_table[] = {
{.leakage_level = 3, .maxvolt = 1350 * 1000},
{.leakage_level = 6, .maxvolt = 1300 * 1000},
{.leakage_level = 15, .maxvolt = 1250 * 1000},
};
static int leakage_level = 0;
#define MHZ (1000 * 1000)
#define KHZ (1000)
// Delayline bound for nandc = 148.5MHz, Varm = Vlog = 1.00V
#define HIGH_DELAYLINE 125
#define LOW_DELAYLINE 125
static u8 rk30_get_avs_val(void);
void dvfs_adjust_table_lmtvolt(struct clk *clk, struct cpufreq_frequency_table *table)
{
int i = 0;
unsigned int maxvolt = 0;
if (IS_ERR_OR_NULL(clk) || IS_ERR_OR_NULL(table)) {
DVFS_ERR("%s: clk error OR table error\n", __func__);
return ;
}
leakage_level = rk_leakage_val();
printk("DVFS MSG: %s: %s get leakage_level = %d\n", clk->name, __func__, leakage_level);
if (leakage_level == 0) {
/*
* This is for delayline auto scale voltage,
* FIXME: HIGH_DELAYLINE / LOW_DELAYLINE value maybe redefined under
* Varm = Vlog = 1.00V.
* Warning: this value is frequency/voltage sensitive, care
* about Freq nandc/Volt log.
*
*/
unsigned long delayline_val = 0;
unsigned long high_delayline = 0, low_delayline = 0;
unsigned long rate_nandc = 0;
// rk3168: do nothing
return;
rate_nandc = clk_get_rate(clk_get(NULL, "nandc")) / KHZ;
printk("Get nandc rate = %lu KHz\n", rate_nandc);
high_delayline = HIGH_DELAYLINE * 148500 / rate_nandc;
low_delayline = LOW_DELAYLINE * 148500 / rate_nandc;
delayline_val = rk30_get_avs_val();
printk("This chip no leakage msg, use delayline instead, val = %lu.(HDL=%lu, LDL=%lu)\n",
delayline_val, high_delayline, low_delayline);
if (delayline_val >= high_delayline) {
leakage_level = 4; //same as leakage_level > 4
} else if (delayline_val <= low_delayline) {
leakage_level = 1;
printk("Delayline TOO LOW, high voltage request\n");
} else
leakage_level = 2; //same as leakage_level = 3
}
for (i = 0; i < ARRAY_SIZE(lkg_volt_table); i++) {
if (leakage_level <= lkg_volt_table[i].leakage_level) {
maxvolt = lkg_volt_table[i].maxvolt;
break;
}
}
for (i = 0; table[i].frequency != CPUFREQ_TABLE_END; i++) {
if (table[i].index > maxvolt) {
printk("\t\tadjust table freq=%d KHz, index=%d mV", table[i].frequency, table[i].index);
table[i].index = maxvolt;
printk(" to index=%d mV\n", table[i].index);
}
}
}
#ifdef CONFIG_ARCH_RK3066B
static int g_arm_high_logic = 0 * 1000;
static int g_logic_high_arm = 50 * 1000;

View File

@@ -5,8 +5,10 @@
#ifdef CONFIG_DVFS
int rk_dvfs_init(void);
void dvfs_adjust_table_lmtvolt(struct clk *clk, struct cpufreq_frequency_table *table);
#else
static inline int rk_dvfs_init(void){ return 0; }
static inline void dvfs_adjust_table_lmtvolt(struct clk *clk, struct cpufreq_frequency_table *table){}
#endif
#endif

View File

@@ -12,7 +12,7 @@
#include <linux/spinlock.h>
#include <plat/efuse.h>
#if defined(CONFIG_ARCH_RK3188) || defined(CONFIG_SOC_RK3028)
#if defined(CONFIG_ARCH_RK3188) || defined(CONFIG_SOC_RK3028) || defined(CONFIG_ARCH_RK3066B)
#define efuse_readl(offset) readl_relaxed(RK30_EFUSE_BASE + offset)
#define efuse_writel(val, offset) writel_relaxed(val, RK30_EFUSE_BASE + offset)
#endif