From a75141735c09b416175dd4fce64bcd4a98e739a6 Mon Sep 17 00:00:00 2001 From: chenxing Date: Wed, 25 Apr 2012 15:17:08 +0800 Subject: [PATCH] rk30:sdk: add logic dynamic voltage scale --- arch/arm/mach-rk30/dvfs.c | 474 ++++++++++++++++++------- arch/arm/mach-rk30/include/mach/dvfs.h | 120 ++++--- 2 files changed, 435 insertions(+), 159 deletions(-) diff --git a/arch/arm/mach-rk30/dvfs.c b/arch/arm/mach-rk30/dvfs.c index a15998c9bd12..95a84a8097aa 100755 --- a/arch/arm/mach-rk30/dvfs.c +++ b/arch/arm/mach-rk30/dvfs.c @@ -26,11 +26,10 @@ #include #include -#define JUMP_RATE 816000000 -#define LOGIC_VOLT_SCALING -#define DVFS_DBG(fmt, args...) {while(0);} //pr_debug(fmt, ##args) +#define DVFS_DBG(fmt, args...) {while(0);} #define DVFS_ERR(fmt, args...) pr_err(fmt, ##args) -#define DVFS_LOG(fmt, args...) pr_debug(fmt, ##args)//while(0) +#define DVFS_LOG(fmt, args...) pr_debug(fmt, ##args) +//#define DVFS_LOG(fmt, args...) pr_err(fmt, ##args) #define dvfs_regulator_get(dev,id) regulator_get((dev),(id)) #define dvfs_regulator_put(regu) regulator_put((regu)) @@ -56,6 +55,11 @@ static DEFINE_MUTEX(mutex); extern int rk30_clk_notifier_register(struct clk *clk, struct notifier_block *nb); extern int rk30_clk_notifier_unregister(struct clk *clk, struct notifier_block *nb); +// #define DVFS_DUMP_TREE +#ifdef DVFS_DUMP_TREE +static void dump_dbg_map(void); +#endif + #define PD_ON 1 #define PD_OFF 0 @@ -82,12 +86,33 @@ int dvfs_set_rate(struct clk *clk, unsigned long rate) return ret; } +static int dvfs_clk_get_ref_volt_depend(struct depend_list *depend, int rate_khz, + struct cpufreq_frequency_table *clk_fv) +{ + int i = 0; + if (rate_khz == 0 || !depend || !depend->dep_table) { + return -1; + } + clk_fv->frequency = rate_khz; + clk_fv->index = 0; + + for (i = 0; (depend->dep_table[i].frequency != CPUFREQ_TABLE_END); i++) { + if (depend->dep_table[i].frequency >= rate_khz) { + clk_fv->frequency = depend->dep_table[i].frequency; + clk_fv->index = depend->dep_table[i].index; + return 0; + } + } + clk_fv->frequency = 0; + clk_fv->index = 0; + return -1; +} static int dvfs_clk_get_ref_volt(struct clk_node *dvfs_clk, int rate_khz, struct cpufreq_frequency_table *clk_fv) { int i = 0; if (rate_khz == 0 || !dvfs_clk || !dvfs_clk->dvfs_table) { - /* since no need*/ + /* since no need */ return -1; } clk_fv->frequency = rate_khz; @@ -121,8 +146,8 @@ static int dvfs_pd_get_newvolt_byclk(struct pd_node *pd, struct clk_node *dvfs_c } list_for_each_entry(child, &pd->clk_list, node) { - //DVFS_DBG("%s ,pd(%s),dvfs(%s),volt(%u)\n",__func__,pd->name, - //dvfs_clk->name,dvfs_clk->set_volt); + // DVFS_DBG("%s ,pd(%s),dvfs(%s),volt(%u)\n",__func__,pd->name, + // dvfs_clk->name,dvfs_clk->set_volt); volt_max = max(volt_max, child->dvfs_clk->set_volt); } return volt_max; @@ -143,12 +168,20 @@ void dvfs_update_clk_pds_volt(struct clk_node *dvfs_clk) static int dvfs_vd_get_newvolt_bypd(struct vd_node *vd) { - struct pd_node *pd; + struct pd_node *pd; + struct depend_list *depend; int volt_max_vd = 0; list_for_each_entry(pd, &vd->pd_list, node) { - //DVFS_DBG("%s pd(%s,%u)\n",__func__,pd->name,pd->cur_volt); + // DVFS_DBG("%s pd(%s,%u)\n",__func__,pd->name,pd->cur_volt); volt_max_vd = max(volt_max_vd, pd->cur_volt); } + + /* some clks depend on this voltage domain */ + if (!list_empty(&vd->req_volt_list)) { + list_for_each_entry(depend, &vd->req_volt_list, node2vd) { + volt_max_vd = max(volt_max_vd, depend->req_volt); + } + } return volt_max_vd; } @@ -191,6 +224,39 @@ int dvfs_set_freq_volt_table(struct clk *clk, struct cpufreq_frequency_table *ta return 0; } +int dvfs_set_depend_table(struct clk *clk, char *vd_name, struct cpufreq_frequency_table *table) +{ + struct vd_node *vd; + struct depend_list *depend; + struct clk_node *info; + info = clk_get_dvfs_info(clk); + if (!table || !info || !vd_name) { + DVFS_ERR("%s :DVFS SET DEPEND TABLE ERROR! table or info or name empty\n", __func__); + return -1; + } + + list_for_each_entry(vd, &rk_dvfs_tree, node) { + if (0 == strcmp(vd->name, vd_name)) { + DVFS_LOG("FOUND A MATCH\n"); + mutex_lock(&mutex); + list_for_each_entry(depend, &info->depend_list, node2clk) { + if (vd == depend->dep_vd && info == depend->dvfs_clk) { + depend->dep_table = table; + break; + } + } + mutex_unlock(&mutex); +#ifdef DVFS_DUMP_TREE + dump_dbg_map(); +#endif + return 0; + } + } + DVFS_ERR("%s :DVFS SET DEPEND TABLE ERROR! can not find vd:%s\n", __func__, vd_name); + + return 0; +} + int clk_enable_dvfs(struct clk *clk) { struct regulator *regulator; @@ -212,7 +278,7 @@ int clk_enable_dvfs(struct clk *clk) if (dvfs_clk->vd->regulator_name) regulator = dvfs_regulator_get(NULL, dvfs_clk->vd->regulator_name); if (regulator) { - //DVFS_DBG("dvfs_regulator_get(%s)\n",dvfs_clk->vd->regulator_name); + // DVFS_DBG("dvfs_regulator_get(%s)\n",dvfs_clk->vd->regulator_name); dvfs_clk->vd->regulator = regulator; } else { dvfs_clk->vd->regulator = NULL; @@ -222,18 +288,18 @@ int clk_enable_dvfs(struct clk *clk) } } else { dvfs_clk->vd->cur_volt = dvfs_regulator_get_voltage(dvfs_clk->vd->regulator); - //DVFS_DBG("%s(%s) vd volt=%u\n",__func__,dvfs_clk->name,dvfs_clk->vd->cur_volt); + // DVFS_DBG("%s(%s) vd volt=%u\n",__func__,dvfs_clk->name,dvfs_clk->vd->cur_volt); } dvfs_clk->set_freq = dvfs_clk_get_rate_kz(clk); - //DVFS_DBG("%s ,%s get freq%u!\n",__func__,dvfs_clk->name,dvfs_clk->set_freq); + // DVFS_DBG("%s ,%s get freq%u!\n",__func__,dvfs_clk->name,dvfs_clk->set_freq); if (dvfs_clk_get_ref_volt(dvfs_clk, dvfs_clk->set_freq, &clk_fv)) { dvfs_clk->enable_dvfs = 0; return -1; } dvfs_clk->set_volt = clk_fv.index; - //DVFS_DBG("%s,%s,freq%u(ref vol %u)\n",__func__,dvfs_clk->name, + // DVFS_DBG("%s,%s,freq%u(ref vol %u)\n",__func__,dvfs_clk->name, // dvfs_clk->set_freq,dvfs_clk->set_volt); #if 0 if (dvfs_clk->dvfs_nb) { @@ -318,6 +384,22 @@ static struct notifier_block rk_dvfs_clk_notifier = { .notifier_call = rk_dvfs_clk_notifier_event, }; +static struct clk_node *dvfs_get_dvfs_clk_byname(char *name) +{ + struct vd_node *vd; + struct pd_node *pd; + struct clk_list *child; + list_for_each_entry(vd, &rk_dvfs_tree, node) { + list_for_each_entry(pd, &vd->pd_list, node) { + list_for_each_entry(child, &pd->clk_list, node) { + if (0 == strcmp(child->dvfs_clk->name, name)) { + return child->dvfs_clk; + } + } + } + } + return NULL; +} static int rk_regist_vd(struct vd_node *vd) { if (!vd) @@ -326,6 +408,7 @@ static int rk_regist_vd(struct vd_node *vd) mutex_init(&vd->dvfs_mutex); list_add(&vd->node, &rk_dvfs_tree); INIT_LIST_HEAD(&vd->pd_list); + INIT_LIST_HEAD(&vd->req_volt_list); mutex_unlock(&mutex); return 0; @@ -372,8 +455,40 @@ static int rk_regist_clk(struct clk_node *dvfs_clk) list_add(&child->node, &pd->clk_list); } clk = dvfs_clk_get(NULL, dvfs_clk->name); - dvfs_clk->ck = clk; + dvfs_clk->clk = clk; clk_register_dvfs(dvfs_clk, clk); + INIT_LIST_HEAD(&dvfs_clk->depend_list); + mutex_unlock(&mutex); + return 0; +} + +static int rk_regist_depends(struct depend_lookup *dep_node) +{ + struct depend_list *depend_list; + struct clk_node *dvfs_clk; + + if (!dep_node) { + DVFS_ERR("%s : DVFS BAD depend node!\n", __func__); + return -1; + } + + if (!dep_node->clk_name || !dep_node->dep_vd) { + DVFS_ERR("%s : DVFS BAD depend members!\n", __func__); + return -1; + } + + depend_list = &dep_node->dep_list; + dvfs_clk = dvfs_get_dvfs_clk_byname(dep_node->clk_name); + + mutex_lock(&mutex); + + depend_list->dvfs_clk = dvfs_clk; + depend_list->dep_vd = dep_node->dep_vd; + depend_list->dep_table = dep_node->dep_table; + + list_add(&depend_list->node2clk, &dvfs_clk->depend_list); + list_add(&depend_list->node2vd, &depend_list->dep_vd->req_volt_list); + mutex_unlock(&mutex); return 0; } @@ -381,43 +496,121 @@ static int rk_regist_clk(struct clk_node *dvfs_clk) #define get_volt_up_delay(new_volt, old_volt) \ ((new_volt) > (old_volt) ? (((new_volt) - (old_volt)) >> 10) : 0) -#ifdef LOGIC_VOLT_SCALING -#define DVFS_LOGIC_VOLT_H 1175000 -#define DVFS_LOGIC_VOLT_L 1025000 -static int dvfs_up_volt_logic(void) +static int dvfs_set_depend_pre(struct clk_node *dvfs_clk, unsigned long rate_old, unsigned long rate_new) { - struct regulator *vdd_core; - int ret = 0; - DVFS_DBG("second jump > 816M\n"); - vdd_core = dvfs_regulator_get(NULL, "vdd_core"); - ret = dvfs_regulator_set_voltage(vdd_core, DVFS_LOGIC_VOLT_H, DVFS_LOGIC_VOLT_H); - udelay(200); - DVFS_DBG("DVFS > 816M\n"); - if (ret != 0) { - DVFS_ERR("%s err, ret = %d\n", __func__, ret); - return -1; + struct depend_list *depend; + struct cpufreq_frequency_table clk_fv; + int ret = -1; + int volt = 0; + struct regulator *regulator; + + if (rate_old >= rate_new) { + return 0; } - dvfs_regulator_put(vdd_core); - return ret; + list_for_each_entry(depend, &dvfs_clk->depend_list, node2clk) { + ret = dvfs_clk_get_ref_volt_depend(depend, rate_new / 1000, &clk_fv); + if (ret != 0) { + DVFS_ERR("%s LOGIC DVFS CAN NOT GET REF VOLT!, frequency too large!\n", __func__); + return -1; + } + + if (!depend->dep_vd->regulator) { + DVFS_LOG("%s regulator empty\n", __func__); + regulator = dvfs_regulator_get(NULL, depend->dep_vd->regulator_name); + if (!regulator) { + DVFS_ERR("%s get regulator err\n", __func__); + return -1; + } + + depend->dep_vd->regulator = regulator; + } + if (!depend->dep_vd->regulator) { + DVFS_ERR("%s vd's(%s) regulator empty\n", __func__, depend->dep_vd->name); + return -1; + } + + if (clk_fv.index == dvfs_regulator_get_voltage(depend->dep_vd->regulator)) { + depend->req_volt = clk_fv.index; + DVFS_LOG("%s same voltage\n", __func__); + return 0; + } + + depend->req_volt = clk_fv.index; + volt = dvfs_vd_get_newvolt_bypd(depend->dep_vd); + DVFS_LOG("%s setting voltage = %d\n", __func__, volt); + ret = dvfs_regulator_set_voltage(depend->dep_vd->regulator, volt, volt); + if (0 != ret) { + DVFS_ERR("%s set voltage = %d ERROR, ret = %d\n", __func__, volt, ret); + return -1; + } + udelay(200); + DVFS_LOG("%s set voltage = %d OK, ret = %d\n", __func__, volt, ret); + if (ret != 0) { + DVFS_ERR("%s err, ret = %d\n", __func__, ret); + return -1; + } + } + + return 0; } -static int dvfs_down_volt_logic(void) +static int dvfs_set_depend_post(struct clk_node *dvfs_clk, unsigned long rate_old, unsigned long rate_new) { - struct regulator *vdd_core; - int ret = 0; - DVFS_DBG("first jump %d\n", JUMP_RATE); - vdd_core = dvfs_regulator_get(NULL, "vdd_core"); - ret = dvfs_regulator_set_voltage(vdd_core, DVFS_LOGIC_VOLT_L, DVFS_LOGIC_VOLT_L); - //udelay(200); - DVFS_DBG("DVFS <= 816M\n"); - if (ret != 0) { - DVFS_ERR("%s err, ret = %d\n", __func__, ret); - return -1; + struct depend_list *depend; + struct cpufreq_frequency_table clk_fv; + int ret = -1; + int volt = 0; + struct regulator *regulator; + + if (rate_old <= rate_new) + return 0; + list_for_each_entry(depend, &dvfs_clk->depend_list, node2clk) { + ret = dvfs_clk_get_ref_volt_depend(depend, rate_new / 1000, &clk_fv); + if (ret != 0) { + DVFS_ERR("%s LOGIC DVFS CAN NOT GET REF VOLT!, frequency too large!\n", __func__); + return -1; + } + + if (!depend->dep_vd->regulator) { + DVFS_LOG("%s regulator empty\n", __func__); + regulator = dvfs_regulator_get(NULL, depend->dep_vd->regulator_name); + if (!regulator) { + DVFS_ERR("%s get regulator err\n", __func__); + return -1; + } + + depend->dep_vd->regulator = regulator; + } + if (!depend->dep_vd->regulator) { + DVFS_ERR("%s vd's(%s) regulator empty\n", __func__, depend->dep_vd->name); + return -1; + } + + if (clk_fv.index == dvfs_regulator_get_voltage(depend->dep_vd->regulator)) { + depend->req_volt = clk_fv.index; + DVFS_LOG("%s same voltage\n", __func__); + return 0; + } + + depend->req_volt = clk_fv.index; + volt = dvfs_vd_get_newvolt_bypd(depend->dep_vd); + DVFS_LOG("%s setting voltage = %d\n", __func__, volt); + ret = dvfs_regulator_set_voltage(depend->dep_vd->regulator, volt, volt); + if (0 != ret) { + DVFS_ERR("%s set voltage = %d ERROR, ret = %d\n", __func__, volt, ret); + return -1; + } + udelay(200); + DVFS_LOG("%s set voltage = %d OK, ret = %d\n", __func__, volt, ret); + if (ret != 0) { + DVFS_ERR("%s err, ret = %d\n", __func__, ret); + return -1; + } } - dvfs_regulator_put(vdd_core); - return ret; + + return 0; } -#endif + static int flag_core_set_volt_err = 0; int dvfs_target_core(struct clk *clk, unsigned long rate_hz) { @@ -426,7 +619,7 @@ int dvfs_target_core(struct clk *clk, unsigned long rate_hz) struct cpufreq_frequency_table clk_fv = {0, 0}; int ret = 0; int flag_set_volt_correct = 0; - unsigned long temp_hz; + unsigned long rate_new, rate_old; if (!clk) { DVFS_ERR("%s is not clk\n", __func__); @@ -439,14 +632,16 @@ int dvfs_target_core(struct clk *clk, unsigned long rate_hz) return -1; } - temp_hz = rate_hz;//clk_round_rate_nolock(clk, rate_hz); + // clk_round_rate_nolock(clk, rate_hz); + rate_new = rate_hz; + rate_old = clk_get_rate(clk); - //DVFS_DBG("dvfs(%s) round rate(%lu)(rount %lu)\n",dvfs_clk->name,rate_hz,temp_hz); + // DVFS_DBG("dvfs(%s) round rate(%lu)(rount %lu)\n",dvfs_clk->name,rate_hz,rate_new); /* find the clk corresponding voltage */ - if (dvfs_clk_get_ref_volt(dvfs_clk, temp_hz / 1000, &clk_fv)) { + if (dvfs_clk_get_ref_volt(dvfs_clk, rate_new / 1000, &clk_fv)) { DVFS_ERR("%s--%s:rate%lu,Get corresponding voltage error!\n", - __func__, dvfs_clk->name, temp_hz); + __func__, dvfs_clk->name, rate_new); return -1; } volt_old = dvfs_clk->vd->cur_volt; @@ -457,8 +652,8 @@ int dvfs_target_core(struct clk *clk, unsigned long rate_hz) volt_new = dvfs_vd_get_newvolt_byclk(dvfs_clk); - DVFS_LOG("dvfs--(%s),volt=%d(was %dmV),rate=%lu(was %lu),vd%u=(was%u)\n", - dvfs_clk->name, clk_fv.index, dvfs_clk->set_volt, temp_hz, clk_get_rate(clk) + DVFS_DBG("dvfs--(%s),volt=%d(was %dmV),rate=%lu(was %lu),vd%u=(was%u)\n", + dvfs_clk->name, clk_fv.index, dvfs_clk->set_volt, rate_new, rate_old , volt_new, volt_old); if (flag_core_set_volt_err) { @@ -467,14 +662,14 @@ int dvfs_target_core(struct clk *clk, unsigned long rate_hz) if (flag_set_volt_correct <= 0) { DVFS_ERR("%s (clk:%s),volt=%d(was %dmV),rate=%lu(was %lu), try to reload core_volt error %d!!! stop scaling\n", __func__, dvfs_clk->name, volt_new, volt_old, - temp_hz, clk_get_rate(clk), flag_set_volt_correct); + rate_new, rate_old, flag_set_volt_correct); return -1; } flag_core_set_volt_err = 0; DVFS_ERR("%s (clk:%s),volt=%d(was %dmV),rate=%lu(was %lu), try to reload core_volt! core_volt_correct = %d\n", __func__, dvfs_clk->name, volt_new, volt_old, - temp_hz, clk_get_rate(clk), flag_set_volt_correct); + rate_new, rate_old, flag_set_volt_correct); /* Reset vd's voltage */ dvfs_clk->vd->cur_volt = flag_set_volt_correct; @@ -489,7 +684,7 @@ int dvfs_target_core(struct clk *clk, unsigned long rate_hz) flag_core_set_volt_err = 1; DVFS_ERR("%s %s set voltage up err ret = %d, Rnew = %lu(was %lu)Hz, Vnew = %d(was %d)mV\n", __func__, dvfs_clk->name, ret, - temp_hz, clk_get_rate(clk), volt_new, volt_old); + rate_new, rate_old, volt_new, volt_old); return -1; } @@ -500,14 +695,14 @@ int dvfs_target_core(struct clk *clk, unsigned long rate_hz) dvfs_clk->vd->cur_volt = volt_new; udelay(get_volt_up_delay(volt_new, volt_old)); - DVFS_LOG("%s %s set voltage OK up ret = %d, Vnew = %d(was %d), Rnew = %lu(was %lu)\n", - __func__, dvfs_clk->name, ret, volt_new, volt_old, temp_hz, clk_get_rate(clk)); + DVFS_DBG("%s %s set voltage OK up ret = %d, Vnew = %d(was %d), Rnew = %lu(was %lu)\n", + __func__, dvfs_clk->name, ret, volt_new, volt_old, rate_new, rate_old); } if (dvfs_clk->clk_dvfs_target) { - ret = dvfs_clk->clk_dvfs_target(clk, temp_hz, clk_set_rate_locked); + ret = dvfs_clk->clk_dvfs_target(clk, rate_new, clk_set_rate_locked); } else { - ret = clk_set_rate_locked(clk, temp_hz); + ret = clk_set_rate_locked(clk, rate_new); } if (ret < 0) { @@ -517,7 +712,7 @@ int dvfs_target_core(struct clk *clk, unsigned long rate_hz) DVFS_ERR("set rate err\n"); return -1; } - dvfs_clk->set_freq = temp_hz / 1000; + dvfs_clk->set_freq = rate_new / 1000; /* if down the voltage */ if (volt_old > volt_new) { @@ -526,7 +721,7 @@ int dvfs_target_core(struct clk *clk, unsigned long rate_hz) if (ret < 0) { flag_core_set_volt_err = 1; DVFS_ERR("%s %s set voltage down err ret = %d, Rnew = %lu(was %lu)Hz, Vnew = %d(was %d)mV\n", - __func__, dvfs_clk->name, ret, temp_hz, clk_get_rate(clk), + __func__, dvfs_clk->name, ret, rate_new, rate_old, volt_new, volt_old); return -1; } @@ -537,7 +732,7 @@ int dvfs_target_core(struct clk *clk, unsigned long rate_hz) } dvfs_clk->vd->cur_volt = volt_new; - DVFS_LOG("dvfs %s set volt ok dn\n", dvfs_clk->name); + DVFS_DBG("dvfs %s set volt ok dn\n", dvfs_clk->name); } @@ -552,7 +747,7 @@ int dvfs_target_cpu(struct clk *clk, unsigned long rate_hz) struct cpufreq_frequency_table clk_fv; int ret = 0; int flag_set_volt_correct = 0; - unsigned long temp_hz, pre_rate_hz; + unsigned long rate_new, rate_old; if (!clk) { @@ -567,12 +762,12 @@ int dvfs_target_cpu(struct clk *clk, unsigned long rate_hz) } /* need round rate */ - temp_hz = clk_round_rate_nolock(clk, rate_hz); - DVFS_DBG("dvfs(%s) round rate (%lu)(rount %lu)\n", dvfs_clk->name, rate_hz, temp_hz); + rate_new = clk_round_rate_nolock(clk, rate_hz); + DVFS_DBG("dvfs(%s) round rate (%lu)(rount %lu)\n", dvfs_clk->name, rate_hz, rate_new); - pre_rate_hz = clk_get_rate(clk); + rate_old = clk_get_rate(clk); /* find the clk corresponding voltage */ - if (0 != dvfs_clk_get_ref_volt(dvfs_clk, temp_hz / 1000, &clk_fv)) { + if (0 != dvfs_clk_get_ref_volt(dvfs_clk, rate_new / 1000, &clk_fv)) { DVFS_ERR("dvfs(%s) rate %luhz is larger,not support\n", dvfs_clk->name, rate_hz); return -1; } @@ -585,28 +780,20 @@ int dvfs_target_cpu(struct clk *clk, unsigned long rate_hz) if (flag_set_volt_correct <= 0) { DVFS_ERR("%s (clk:%s),volt=%d(was %dmV),rate=%lu(was %lu), try to reload arm_volt error %d!!! stop scaling\n", __func__, dvfs_clk->name, volt_new, volt_old, - temp_hz, clk_get_rate(clk), flag_set_volt_correct); + rate_new, rate_old, flag_set_volt_correct); return -1; } flag_arm_set_volt_err = 0; DVFS_ERR("%s (clk:%s),volt=%d(was %dmV),rate=%lu(was %lu), try to reload arm_volt! arm_volt_correct = %d\n", __func__, dvfs_clk->name, volt_new, volt_old, - temp_hz, clk_get_rate(clk), flag_set_volt_correct); + rate_new, rate_old, flag_set_volt_correct); /* Reset vd's voltage */ dvfs_clk->vd->cur_volt = flag_set_volt_correct; volt_old = dvfs_clk->vd->cur_volt; } -#ifdef LOGIC_VOLT_SCALING - if (temp_hz > JUMP_RATE && pre_rate_hz <= JUMP_RATE) { - ret = dvfs_up_volt_logic(); - if (ret != 0) { - DVFS_ERR("%s up logic volt error %d", __func__, ret); - return -1; - } - } -#endif + /* if up the voltage */ if (volt_old < volt_new) { if (dvfs_clk->vd->regulator) { @@ -614,7 +801,7 @@ int dvfs_target_cpu(struct clk *clk, unsigned long rate_hz) if (ret < 0) { flag_arm_set_volt_err = 1; DVFS_ERR("%s %s set voltage up err ret = %d, Rnew = %lu(was %lu)Hz, Vnew = %d(was %d)mV\n", - __func__, dvfs_clk->name, ret, temp_hz, clk_get_rate(clk), + __func__, dvfs_clk->name, ret, rate_new, rate_old, volt_new, volt_old); return -1; } @@ -626,23 +813,39 @@ int dvfs_target_cpu(struct clk *clk, unsigned long rate_hz) dvfs_clk->vd->cur_volt = volt_new; udelay(get_volt_up_delay(volt_new, volt_old)); - DVFS_LOG("%s %s set voltage OK up ret = %d, Vnew = %d(was %d), Rnew = %lu(was %lu)\n", - __func__, dvfs_clk->name, ret, volt_new, volt_old, temp_hz, clk_get_rate(clk)); + DVFS_DBG("%s %s set voltage OK up ret = %d, Vnew = %d(was %d), Rnew = %lu(was %lu)\n", + __func__, dvfs_clk->name, ret, volt_new, volt_old, rate_new, rate_old); + } + + /* depend voltage domain set up*/ + if (0 != dvfs_set_depend_pre(dvfs_clk, rate_old, rate_new)) { + DVFS_ERR("%s (clk:%s),volt=%d(was %dmV),rate=%lu(was %lu), set depend pre voltage err, stop scaling\n", + __func__, dvfs_clk->name, volt_new, volt_old, + rate_new, rate_old); + return -1; } if (dvfs_clk->clk_dvfs_target) { - ret = dvfs_clk->clk_dvfs_target(clk, temp_hz, clk_set_rate_locked); + ret = dvfs_clk->clk_dvfs_target(clk, rate_new, clk_set_rate_locked); } else { - ret = clk_set_rate_locked(clk, temp_hz); + ret = clk_set_rate_locked(clk, rate_new); } if (ret < 0) { DVFS_ERR("set rate err\n"); return -1; } - dvfs_clk->set_freq = temp_hz / 1000; + dvfs_clk->set_freq = rate_new / 1000; - DVFS_LOG("dvfs %s set rate%lu ok\n", dvfs_clk->name, clk_get_rate(clk)); + DVFS_DBG("dvfs %s set rate %lu ok\n", dvfs_clk->name, clk_get_rate(clk)); + + /* depend voltage domain set down*/ + if (0 != dvfs_set_depend_post(dvfs_clk, rate_old, rate_new)) { + DVFS_ERR("%s (clk:%s),volt=%d(was %dmV),rate=%lu(was %lu), set depend post voltage err, stop scaling\n", + __func__, dvfs_clk->name, volt_new, volt_old, + rate_new, rate_old); + return -1; + } /* if down the voltage */ if (volt_old > volt_new) { @@ -651,7 +854,7 @@ int dvfs_target_cpu(struct clk *clk, unsigned long rate_hz) if (ret < 0) { flag_arm_set_volt_err = 1; DVFS_ERR("%s %s set voltage down err ret = %d, Rnew = %lu(was %lu)Hz, Vnew = %d(was %d)mV\n", - __func__, dvfs_clk->name, ret, temp_hz, clk_get_rate(clk), + __func__, dvfs_clk->name, ret, rate_new, rate_old, volt_new, volt_old); return -1; } @@ -662,18 +865,10 @@ int dvfs_target_cpu(struct clk *clk, unsigned long rate_hz) } dvfs_clk->vd->cur_volt = volt_new; - DVFS_LOG("dvfs %s set volt ok dn\n", dvfs_clk->name); + DVFS_DBG("dvfs %s set volt ok dn\n", dvfs_clk->name); } -#ifdef LOGIC_VOLT_SCALING - if (temp_hz <= JUMP_RATE && pre_rate_hz > JUMP_RATE) { - ret = dvfs_down_volt_logic(); - if (ret != 0) { - DVFS_ERR("%s down logic volt error %d", __func__, ret); - return -1; - } - } -#endif + return ret; } @@ -684,17 +879,17 @@ int dvfs_target_cpu(struct clk *clk, unsigned long rate_hz) * rate must be raising sequence */ static struct cpufreq_frequency_table cpu_dvfs_table[] = { - //{.frequency = 48 * DVFS_KHZ, .index = 920*DVFS_MV}, - //{.frequency = 126 * DVFS_KHZ, .index = 970 * DVFS_MV}, + // {.frequency = 48 * DVFS_KHZ, .index = 920*DVFS_MV}, + // {.frequency = 126 * DVFS_KHZ, .index = 970 * DVFS_MV}, // {.frequency = 252 * DVFS_KHZ, .index = 1040 * DVFS_MV}, - // {.frequency = 504 * DVFS_KHZ, .index = 1060 * DVFS_MV}, - {.frequency = 816 * DVFS_KHZ, .index = 1080 * DVFS_MV}, - // {.frequency = 1008 * DVFS_KHZ, .index = 1100 * DVFS_MV}, + // {.frequency = 504 * DVFS_KHZ, .index = 1050 * DVFS_MV}, + {.frequency = 816 * DVFS_KHZ, .index = 1050 * DVFS_MV}, + // {.frequency = 1008 * DVFS_KHZ, .index = 1100 * DVFS_MV}, {.frequency = CPUFREQ_TABLE_END}, }; static struct cpufreq_frequency_table ddr_dvfs_table[] = { - //{.frequency = 100 * DVFS_KHZ, .index = 1100 * DVFS_MV}, + // {.frequency = 100 * DVFS_KHZ, .index = 1100 * DVFS_MV}, {.frequency = 200 * DVFS_KHZ, .index = 1000 * DVFS_MV}, {.frequency = 300 * DVFS_KHZ, .index = 1050 * DVFS_MV}, {.frequency = 400 * DVFS_KHZ, .index = 1100 * DVFS_MV}, @@ -720,6 +915,19 @@ static struct cpufreq_frequency_table peri_aclk_dvfs_table[] = { {.frequency = CPUFREQ_TABLE_END}, }; +static struct cpufreq_frequency_table dep_cpu2core_table[] = { + // {.frequency = 252 * DVFS_KHZ, .index = 1025 * DVFS_MV}, + // {.frequency = 504 * DVFS_KHZ, .index = 1025 * DVFS_MV}, + {.frequency = 816 * DVFS_KHZ, .index = 1050 * DVFS_MV},//logic 1.050V + // {.frequency = 1008 * DVFS_KHZ,.index = 1050 * DVFS_MV}, + // {.frequency = 1200 * DVFS_KHZ,.index = 1050 * DVFS_MV}, + // {.frequency = 1272 * DVFS_KHZ,.index = 1050 * DVFS_MV},//logic 1.050V + // {.frequency = 1416 * DVFS_KHZ,.index = 1100 * DVFS_MV},//logic 1.100V + // {.frequency = 1512 * DVFS_KHZ,.index = 1125 * DVFS_MV},//logic 1.125V + // {.frequency = 1608 * DVFS_KHZ,.index = 1175 * DVFS_MV},//logic 1.175V + {.frequency = CPUFREQ_TABLE_END}, +}; + static struct vd_node vd_cpu = { .name = "vd_cpu", .regulator_name = "vdd_cpu", @@ -823,6 +1031,11 @@ static struct pds_list gpu_pds[] = { CLK_PDS(NULL), }; +static struct pds_list aclk_periph_pds[] = { + CLK_PDS(&pd_peri), + CLK_PDS(NULL), +}; + #define RK_CLKS(_clk_name, _ppds, _dvfs_table, _dvfs_nb) \ { \ .name = _clk_name, \ @@ -831,12 +1044,6 @@ static struct pds_list gpu_pds[] = { .dvfs_nb = _dvfs_nb, \ } -static struct pds_list aclk_periph_pds[] = { - CLK_PDS(&pd_peri), - CLK_PDS(NULL), -}; - - static struct clk_node rk30_clks[] = { RK_CLKS("cpu", cpu_pds, cpu_dvfs_table, &rk_dvfs_clk_notifier), RK_CLKS("ddr", ddr_pds, ddr_dvfs_table, &rk_dvfs_clk_notifier), @@ -844,6 +1051,17 @@ static struct clk_node rk30_clks[] = { RK_CLKS("aclk_periph", aclk_periph_pds, peri_aclk_dvfs_table, &rk_dvfs_clk_notifier), }; +#define RK_DEPPENDS(_clk_name, _pvd, _dep_table) \ +{ \ + .clk_name = _clk_name, \ + .dep_vd = _pvd,\ + .dep_table = _dep_table, \ +} + +static struct depend_lookup rk30_depends[] = { + RK_DEPPENDS("cpu", &vd_core, dep_cpu2core_table), +}; + int rk30_dvfs_init(void) { int i = 0; @@ -856,12 +1074,16 @@ int rk30_dvfs_init(void) for (i = 0; i < ARRAY_SIZE(rk30_clks); i++) { rk_regist_clk(&rk30_clks[i]); } - + for (i = 0; i < ARRAY_SIZE(rk30_depends); i++) { + rk_regist_depends(&rk30_depends[i]); + } +#ifdef DVFS_DUMP_TREE + dump_dbg_map(); +#endif return 0; } -//#define DVFS_DUMP_DBG -#ifdef DVFS_DUMP_DBG +#ifdef DVFS_DUMP_TREE /** * dump_dbg_map() : Draw all informations of dvfs while debug */ @@ -872,38 +1094,52 @@ static void dump_dbg_map(void) struct pd_node *pd, *clkparent; struct clk_list *child; struct clk_node *dvfs_clk; + struct depend_list *depend; - DVFS_DBG("-------------DVFS DEBUG-----------\n\n\n"); - DVFS_DBG("RK30 DVFS TREE:\n"); + DVFS_LOG("-------------DVFS DEBUG-----------\n\n\n"); + DVFS_LOG("RK30 DVFS TREE:\n"); list_for_each_entry(vd, &rk_dvfs_tree, node) { - DVFS_DBG("|\n|- voltage domain:%s\n", vd->name); - DVFS_DBG("|- current voltage:%d\n", vd->cur_volt); + DVFS_LOG("|\n|- voltage domain:%s\n", vd->name); + DVFS_LOG("|- current voltage:%d\n", vd->cur_volt); + list_for_each_entry(depend, &vd->req_volt_list, node2vd) { + DVFS_LOG("|- request voltage:%d, clk:%s\n", depend->req_volt, depend->dvfs_clk->name); + } list_for_each_entry(pd, &vd->pd_list, node) { - DVFS_DBG("| |\n| |- power domain:%s, status = %s, current volt = %d\n", + DVFS_LOG("| |\n| |- power domain:%s, status = %s, current volt = %d\n", pd->name, (pd->pd_status == PD_ON) ? "ON" : "OFF", pd->cur_volt); list_for_each_entry(child, &pd->clk_list, node) { dvfs_clk = child->dvfs_clk; - DVFS_DBG("| | |\n| | |- clock: %s current: rate %d, volt = %d, enable_dvfs = %s\n", + DVFS_LOG("| | |\n| | |- clock: %s current: rate %d, volt = %d, enable_dvfs = %s\n", dvfs_clk->name, dvfs_clk->set_freq, dvfs_clk->set_volt, dvfs_clk->enable_dvfs == 0 ? "DISABLE" : "ENABLE"); for (i = 0; dvfs_clk->pds[i].pd != NULL; i++) { clkparent = dvfs_clk->pds[i].pd; - DVFS_DBG("| | | |- clock parents: %s, vd_parent = %s\n", + DVFS_LOG("| | | |- clock parents: %s, vd_parent = %s\n", clkparent->name, clkparent->vd->name); } for (i = 0; (dvfs_clk->dvfs_table[i].frequency != CPUFREQ_TABLE_END); i++) { - DVFS_DBG("| | | |- freq = %d, volt = %d\n", + DVFS_LOG("| | | |- freq = %d, volt = %d\n", dvfs_clk->dvfs_table[i].frequency, dvfs_clk->dvfs_table[i].index); } + + list_for_each_entry(depend, &dvfs_clk->depend_list, node2clk) { + DVFS_LOG("| | | | |- DEPEND VD: %s\n", depend->dep_vd->name); + for (i = 0; (depend->dep_table[i].frequency != CPUFREQ_TABLE_END); i++) { + DVFS_LOG("| | | | |- freq = %d, req_volt = %d\n", + depend->dep_table[i].frequency, + depend->dep_table[i].index); + + } + } } } } - DVFS_DBG("-------------DVFS DEBUG END------------\n"); + DVFS_LOG("-------------DVFS DEBUG END------------\n"); } #endif diff --git a/arch/arm/mach-rk30/include/mach/dvfs.h b/arch/arm/mach-rk30/include/mach/dvfs.h index 18e9ef36e322..8f4ce715ede4 100755 --- a/arch/arm/mach-rk30/include/mach/dvfs.h +++ b/arch/arm/mach-rk30/include/mach/dvfs.h @@ -24,94 +24,134 @@ typedef int (*clk_dvfs_target_callback)(struct clk *clk, unsigned long rate, /** * struct vd_node: To Store All Voltage Domains' info - * @vd_name: Voltage Domain's Name - * @cur_volt: Voltage Domain's Current Voltage - * @vd_list: Point of he Voltage Domain List Node - * @pd_list: Head of Power Domain List Belongs to This Voltage Domain - * @vd_voltreq_list: Head of Voltage Request List for Voltage Domain + * @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; + char *name; + char *regulator_name; + int cur_volt; struct regulator *regulator; - struct mutex dvfs_mutex; 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 vd_node_lookup { - struct vd_node *vd; - char *regulator_name; -}; + /** - * struct pd_node: To Store All Power Domains' info per Voltage Domain - * @pd_name: Power Domain's Name - * @cur_volt: Power Domain's Current Voltage - * @pd_list: Point of the Power Domain List Node - * @clk_list: Head of Power Domain's Clocks List - * @pd_status: If The Power Domain On: 1 means on, 0 means off + * 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; + 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 pd_node *pd; }; struct clk_list{ - struct clk_node *dvfs_clk; - struct list_head node; + struct clk_node *dvfs_clk; + struct list_head node; }; struct pds_list { - struct clk_list clk_list; - struct pd_node *pd; + 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; - int enable_dvfs; - struct clk *ck; + char *name; + int set_freq; //KHZ + int set_volt; //MV + int enable_dvfs; + 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; - struct notifier_block *dvfs_nb; - struct list_head node; - clk_dvfs_target_callback clk_dvfs_target; + 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); -int cpufreq_dvfs_init(struct clk *ck, struct cpufreq_frequency_table **table, clk_dvfs_target_callback clk_dvfs_target); 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 rk30_dvfs_init(void); +int dvfs_set_depend_table(struct clk *clk, char *vd_name, struct cpufreq_frequency_table *table); #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 int cpufreq_dvfs_init(struct clk *ck, struct cpufreq_frequency_table **table, clk_dvfs_target_callback clk_dvfs_target) { 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;} #endif #endif -- 2.34.1