#include <linux/io.h>\r
#include <linux/hrtimer.h>\r
\r
+static int clk_disable_target(struct clk *clk, int on)\r
+{\r
+ struct clk_node *dvfs_clk;\r
+ int volt_new = 0, clk_volt_store = 0;\r
+ struct cpufreq_frequency_table clk_fv;\r
+ unsigned long rate_hz;\r
+ int ret = 0;\r
+\r
+ if (!clk) {\r
+ DVFS_ERR("%s is not a clk\n", __func__);\r
+ return -1;\r
+ }\r
+ dvfs_clk = clk_get_dvfs_info(clk);\r
+\r
+ if (!dvfs_clk || dvfs_clk->vd == NULL || IS_ERR_OR_NULL(dvfs_clk->vd->regulator)) {\r
+ DVFS_ERR("dvfs(%s) is not register regulator\n", dvfs_clk->name);\r
+ return -1;\r
+ }\r
+\r
+ if (dvfs_clk->vd->volt_set_flag == DVFS_SET_VOLT_FAILURE) {\r
+ /* It means the last time set voltage error */\r
+ ret = dvfs_reset_volt(dvfs_clk->vd);\r
+ if (ret < 0) {\r
+ return -1;\r
+ }\r
+ }\r
+\r
+ clk_volt_store = dvfs_clk->set_volt; \r
+ // firsh up volt in this,next on clk out off this fun\r
+ if(on||clk_used_count(clk))\r
+ {\r
+ rate_hz = clk_get_rate(clk);\r
+ /* find the clk corresponding voltage */\r
+ if (0 != dvfs_clk_get_ref_volt(dvfs_clk, rate_hz / 1000, &clk_fv)) {\r
+ DVFS_ERR("dvfs(%s) rate %luhz is larger,not support\n", dvfs_clk->name, rate_hz);\r
+ return -1;\r
+ } \r
+ dvfs_clk->set_volt = clk_fv.index;\r
+\r
+ }\r
+ else// in this clk is real disable\r
+ {\r
+ dvfs_clk->set_volt =0; \r
+ } \r
+ \r
+\r
+ volt_new = dvfs_vd_get_newvolt_byclk(dvfs_clk);\r
+\r
+ DVFS_DBG("**%s:clk=%s(%s),rate=%lu,clk volt=%d,vd volt=%d\n",__FUNCTION__,dvfs_clk->name,\r
+ on?"enable":"disable",clk_get_rate(clk)/1000,dvfs_clk->set_volt,volt_new);\r
+\r
+\r
+ if (volt_new == dvfs_clk->vd->cur_volt)\r
+ return 0;\r
+\r
+ ret = dvfs_scale_volt_direct(dvfs_clk->vd, volt_new);\r
+ if (ret < 0)\r
+ {\r
+ printk("%s:clk=%s set volt error\n",__FUNCTION__,dvfs_clk->name);\r
+\r
+ if(!clk_used_count(clk)) \r
+ dvfs_clk->set_volt = 0;\r
+ else\r
+ {\r
+ rate_hz = clk_get_rate(clk);\r
+ /* find the clk corresponding voltage */\r
+ if (0 != dvfs_clk_get_ref_volt(dvfs_clk, rate_hz / 1000, &clk_fv)) {\r
+ DVFS_ERR("dvfs(%s) rate %luhz is larger,not support\n", dvfs_clk->name, rate_hz);\r
+ return -1;\r
+ } \r
+ dvfs_clk->set_volt = clk_fv.index;\r
+ }\r
+ }\r
+\r
+ return ret;\r
+}\r
static int rk_dvfs_clk_notifier_event(struct notifier_block *this,\r
unsigned long event, void *ptr)\r
{\r
#else\r
.vd_dvfs_target = dvfs_target_core,\r
#endif\r
-\r
+ .vd_clk_disable_target= dvfs_vd_clk_disable_target,\r
};\r
\r
static struct vd_node vd_rtc = {\r
CLK_PDS(NULL),\r
};\r
\r
-#define RK_CLKS(_clk_name, _ppds, _dvfs_table, _dvfs_nb) \\r
+#define RK_CLKS(_clk_name, _ppds, _dvfs_table, _dvfs_nb, _disable) \\r
{ \\r
.name = _clk_name, \\r
.pds = _ppds,\\r
.dvfs_table = _dvfs_table, \\r
.dvfs_nb = _dvfs_nb, \\r
+ .disable_ctr = _disable, \\r
}\r
\r
+static struct clk_disable_ctr gpu_disable= {\r
+ .disable_work_fn=dvfs_clk_disable_delay_work,\r
+ .clk_disable_target=clk_disable_target,\r
+ .delay=40,//ms\r
+};\r
static struct clk_node rk30_clks[] = {\r
- RK_CLKS("cpu", cpu_pds, cpu_dvfs_table, &rk_dvfs_clk_notifier),\r
- RK_CLKS("ddr", ddr_pds, ddr_dvfs_table, &rk_dvfs_clk_notifier),\r
- RK_CLKS("gpu", gpu_pds, gpu_dvfs_table, &rk_dvfs_clk_notifier),\r
- RK_CLKS("aclk_periph", aclk_periph_pds, peri_aclk_dvfs_table, &rk_dvfs_clk_notifier),\r
+ RK_CLKS("cpu", cpu_pds, cpu_dvfs_table, &rk_dvfs_clk_notifier, NULL),\r
+ RK_CLKS("ddr", ddr_pds, ddr_dvfs_table, &rk_dvfs_clk_notifier, NULL),\r
+ RK_CLKS("gpu", gpu_pds, gpu_dvfs_table, &rk_dvfs_clk_notifier,&gpu_disable),\r
+ RK_CLKS("aclk_periph", aclk_periph_pds, peri_aclk_dvfs_table, &rk_dvfs_clk_notifier, NULL),\r
};\r
\r
#if 0\r
return ret;
}
/**********************************dvfs****************************************************/
-
-struct clk_node *clk_get_dvfs_info(struct clk *clk)
-{
- return clk->dvfs_info;
-}
-
int clk_set_rate_locked(struct clk * clk,unsigned long rate)
{
int ret;
{
clk->dvfs_info = dvfs_clk;
}
-
-
+int clk_set_enable_locked(struct clk * clk,int on)
+{
+ int ret=0;
+ LOCK();
+ if(on)
+ ret=clk_enable_nolock(clk);
+ else
+ clk_disable_nolock(clk);
+ UNLOCK();
+ return ret;
+}
+EXPORT_SYMBOL(clk_set_enable_locked);
/*-------------------------------------------------------------------------
* Optional clock functions defined in include/linux/clk.h
*-------------------------------------------------------------------------*/
}
if (rate == clk->rate)
return 0;
- if (clk->dvfs_info!=NULL&&is_support_dvfs(clk->dvfs_info))
- return dvfs_set_rate(clk, rate);
+ if (dvfs_support_clk_set_rate(clk->dvfs_info)==true)
+ return dvfs_vd_clk_set_rate(clk, rate);
LOCK();
ret = clk_set_rate_nolock(clk, rate);
{
if (clk == NULL || IS_ERR(clk))
return;
+ if (dvfs_support_clk_disable(clk->dvfs_info)==true)
+ return dvfs_vd_clk_disable(clk, 0);
LOCK();
clk_disable_nolock(clk);
if (clk == NULL || IS_ERR(clk))
return -EINVAL;
+ if (dvfs_support_clk_disable(clk->dvfs_info)==true)
+ return dvfs_vd_clk_disable(clk, 1);
LOCK();
ret = clk_enable_nolock(clk);
\r
static int dump_dbg_map(char *buf);\r
\r
+static struct workqueue_struct *dvfs_wq;\r
#define PD_ON 1\r
#define PD_OFF 0\r
+#define DVFS_STR_DISABLE(on) ((on)?"enable":"disable")\r
\r
#define get_volt_up_delay(new_volt, old_volt) \\r
((new_volt) > (old_volt) ? (((new_volt) - (old_volt)) >> 9) : 0)\r
return 0;\r
}\r
\r
-int is_support_dvfs(struct clk_node *dvfs_info)\r
+int dvfs_vd_clk_set_rate(struct clk *clk, unsigned long rate)\r
{\r
- return (dvfs_info->vd && dvfs_info->vd->vd_dvfs_target && dvfs_info->enable_dvfs);\r
+ int ret = -1;\r
+ struct clk_node *dvfs_info=clk_get_dvfs_info(clk);\r
+ \r
+ DVFS_DBG("%s(%s(%lu))\n", __func__, dvfs_info->name, rate);\r
+\r
+ #if 0 // judge by reference func in rk\r
+ if (dvfs_support_clk_set_rate(dvfs_info)==false) {\r
+ DVFS_ERR("dvfs func:%s is not support!\n", __func__);\r
+ return ret;\r
+ }\r
+ #endif\r
+ \r
+ if(dvfs_info->vd&&dvfs_info->vd->vd_dvfs_target){\r
+ // mutex_lock(&vd->dvfs_mutex);\r
+ mutex_lock(&rk_dvfs_mutex);\r
+ ret = dvfs_info->vd->vd_dvfs_target(clk, rate);\r
+ mutex_unlock(&rk_dvfs_mutex);\r
+ // mutex_unlock(&vd->dvfs_mutex);\r
+ }\r
+ DVFS_DBG("%s(%s(%lu)),is end\n", __func__, clk->name, rate);\r
+ return ret;\r
}\r
+EXPORT_SYMBOL(dvfs_vd_clk_set_rate);\r
\r
-int dvfs_set_rate(struct clk *clk, unsigned long rate)\r
+int dvfs_vd_clk_disable(struct clk *clk, int on)\r
{\r
- int ret = 0;\r
- struct vd_node *vd;\r
- DVFS_DBG("%s(%s(%lu))\n", __func__, clk->name, rate);\r
- if (!clk->dvfs_info) {\r
- DVFS_ERR("%s :This clk do not support dvfs!\n", __func__);\r
- ret = -1;\r
- } else {\r
- vd = clk->dvfs_info->vd;\r
+ int ret = -1;\r
+ struct clk_node *dvfs_info=clk_get_dvfs_info(clk); \r
+ DVFS_DBG("%s(%s(%s,%lu))\n", __func__, dvfs_info->name, DVFS_STR_DISABLE(on),clk_get_rate(clk));\r
+\r
+\r
+ #if 0 // judge by reference func in rk\r
+ if (dvfs_support_clk_disable(dvfs_info)==false) {\r
+ DVFS_ERR("dvfs func:%s is not support!\n", __func__);\r
+ return ret;\r
+ }\r
+ #endif\r
+ \r
+ if(dvfs_info->vd&&dvfs_info->vd->vd_clk_disable_target){\r
// mutex_lock(&vd->dvfs_mutex);\r
mutex_lock(&rk_dvfs_mutex);\r
- ret = vd->vd_dvfs_target(clk, rate);\r
+ ret = dvfs_info->vd->vd_clk_disable_target(clk, on);\r
mutex_unlock(&rk_dvfs_mutex);\r
// mutex_unlock(&vd->dvfs_mutex);\r
}\r
- DVFS_DBG("%s(%s(%lu)),is end\n", __func__, clk->name, rate);\r
+ DVFS_DBG("%s(%s(%lu)),is end\n", __func__, dvfs_info->name, DVFS_STR_ON(on));\r
+\r
+ return ret;\r
+}\r
+\r
+EXPORT_SYMBOL(dvfs_vd_clk_disable);\r
+\r
+\r
+int dvfs_vd_clk_disable_target(struct clk *clk, int on)\r
+{\r
+ struct clk_node *dvfs_clk;\r
+ struct clk_disable_ctr *disable_ctr;\r
+ int ret = 0;\r
+ int i;\r
+ if (!clk) {\r
+ DVFS_ERR("%s is not a clk\n", __func__);\r
+ return -1;\r
+ }\r
+ dvfs_clk = clk_get_dvfs_info(clk);\r
+ if(!dvfs_clk)\r
+ {\r
+ DVFS_ERR("%s is not a dvfs\n", __func__);\r
+ return -1;\r
+ }\r
+\r
+ DVFS_DBG("%s:clk=%s(%s),count=%d\n",__FUNCTION__,dvfs_clk->name,\r
+ DVFS_STR_DISABLE,clk_used_count(clk));\r
+ if(on)\r
+ {\r
+ // enable ,is usecount =0,this time will set volt\r
+ if(clk_used_count(clk)!= 0)\r
+ return clk_set_enable_locked(clk, on);\r
+ }\r
+ else\r
+ {\r
+ //disabe, is usecount =1,this time will set volt\r
+ if(clk_used_count(clk)!= 1)\r
+ return clk_set_enable_locked(clk, on);\r
+ }\r
+ if(!dvfs_clk->disable_ctr)\r
+ return clk_set_enable_locked(clk, on);\r
+ else\r
+ disable_ctr=dvfs_clk->disable_ctr;\r
+ if(on)\r
+ { \r
+ if(disable_ctr->delay&&disable_ctr->disable_work_fn)\r
+ cancel_delayed_work(&disable_ctr->disable_work);\r
+ if(disable_ctr->clk_disable_target)\r
+ {\r
+ for(i=0;i<2;i++)\r
+ {\r
+ ret = disable_ctr->clk_disable_target(clk, on);\r
+ if(ret>=0)\r
+ break;\r
+ mdelay(1000);\r
+ }\r
+ }\r
+ else\r
+ ret=0;\r
+ \r
+ // volt resume fail, Muse set rate is mini\r
+ if(ret<0)\r
+ {\r
+ clk_set_rate_locked(clk,dvfs_clk->min_rate);\r
+ DVFS_WARNING("%s:clk=%s enable set volt fail,set min rate\n",__FUNCTION__,dvfs_clk->name);\r
+ }\r
+ }\r
+\r
+ ret = clk_set_enable_locked(clk, on);\r
+ if(ret < 0)\r
+ return ret;\r
+ if(!on)\r
+ {\r
+ if(disable_ctr->delay&&disable_ctr->disable_work_fn)\r
+ {\r
+ DVFS_DBG("%s:clk=%s disable delay=%d\n",__FUNCTION__,dvfs_clk->name,disable_ctr->delay);\r
+ queue_delayed_work_on(0,dvfs_wq, &disable_ctr->disable_work, \r
+ msecs_to_jiffies(disable_ctr->delay));\r
+ }\r
+ else\r
+ {\r
+ DVFS_DBG("%s:clk=%s disable now\n",__FUNCTION__,dvfs_clk->name);\r
+ if(disable_ctr->clk_disable_target)\r
+ ret=disable_ctr->clk_disable_target(clk, on);\r
+ }\r
+ }\r
return ret;\r
}\r
\r
+EXPORT_SYMBOL(dvfs_vd_clk_disable_target);\r
+\r
+void dvfs_clk_disable_delay_work(struct work_struct *work)\r
+{\r
+ struct clk_disable_ctr *disable_ctr=container_of(work, struct clk_disable_ctr, disable_work.work);\r
+ struct clk_node *dvfs_clk;\r
+ if(!disable_ctr->dvfs_clk)\r
+ return;\r
+ dvfs_clk=disable_ctr->dvfs_clk;\r
+ mutex_lock(&rk_dvfs_mutex);\r
+ DVFS_DBG("%s:clk=%s disable delay work\n",__FUNCTION__,dvfs_clk->name);\r
+ if(disable_ctr->clk_disable_target&&(!clk_used_count(dvfs_clk->clk)))\r
+ disable_ctr->clk_disable_target(dvfs_clk->clk,0);\r
+ mutex_unlock(&rk_dvfs_mutex);\r
+}\r
+EXPORT_SYMBOL(dvfs_clk_disable_delay_work);\r
+\r
static void dvfs_table_round_clk_rate(struct clk_node *dvfs_clk)\r
{\r
int i;\r
struct pd_node *pd;\r
struct clk_list *child;\r
struct clk *clk;\r
+ struct clk_disable_ctr *disable_ctr;\r
int i = 0;\r
\r
if (!dvfs_clk)\r
}\r
clk = dvfs_clk_get(NULL, dvfs_clk->name);\r
dvfs_clk->clk = clk;\r
+ disable_ctr=dvfs_clk->disable_ctr;\r
+ if(disable_ctr)\r
+ {\r
+ if(disable_ctr->clk_disable_target)\r
+ {\r
+ disable_ctr->dvfs_clk=dvfs_clk;\r
+\r
+ if(disable_ctr->delay&&disable_ctr->disable_work_fn)\r
+ {\r
+ INIT_DELAYED_WORK(&disable_ctr->disable_work,disable_ctr->disable_work_fn); \r
+ }\r
+ }else\r
+ dvfs_clk->disable_ctr=NULL;\r
+ }\r
clk_register_dvfs(dvfs_clk, clk);\r
INIT_LIST_HEAD(&dvfs_clk->depend_list);\r
mutex_unlock(&mutex);\r
return ret;\r
}\r
}\r
+ dvfs_wq = create_singlethread_workqueue("rk dvfs wq");\r
\r
return ret;\r
}\r
int clk_set_parent_nolock(struct clk *clk, struct clk *parent);
int clk_set_rate_locked(struct clk * clk,unsigned long rate);
void clk_register_dvfs(struct clk_node *dvfs_clk, struct clk *clk);
-struct clk_node *clk_get_dvfs_info(struct clk *clk);
int is_suport_round_rate(struct clk *clk);
+int clk_set_enable_locked(struct clk * clk,int on);
+/************************inline fun*****************************/
+static inline struct clk_node *clk_get_dvfs_info(struct clk *clk)
+{
+ return clk->dvfs_info;
+}
+static inline s16 clk_used_count(struct clk * clk)
+{
+ return (clk->usecount);
+}
#ifdef RK30_CLK_OFFBOARD_TEST
#include <linux/device.h>
typedef int (*clk_dvfs_target_callback)(struct clk *clk, unsigned long rate,\r
dvfs_set_rate_callback set_rate);\r
\r
+typedef int (*dvfs_vd_clk_disable_callback)(struct clk *clk,int on);\r
+\r
+typedef void (*dvfs_clk_disable_work_callback)(struct work_struct *work);\r
/**\r
* struct vd_node: To Store All Voltage Domains' info\r
* @name: Voltage Domain's Name\r
struct list_head pd_list;\r
struct list_head req_volt_list;\r
//struct mutex dvfs_mutex;\r
+ dvfs_vd_clk_disable_callback vd_clk_disable_target;\r
vd_dvfs_target_callback vd_dvfs_target;\r
unsigned n_voltages;\r
int volt_list[VD_VOL_LIST_CNT];\r
struct cpufreq_frequency_table *dep_table;\r
};\r
\r
+struct clk_disable_ctr {\r
+ struct delayed_work disable_work;\r
+ dvfs_clk_disable_work_callback disable_work_fn;\r
+ dvfs_vd_clk_disable_callback clk_disable_target;\r
+ unsigned int delay;//ms\r
+ struct clk_node *dvfs_clk;\r
+};\r
/**\r
* struct clk_node: To Store All dvfs clocks' info\r
* @name: Dvfs clock's Name\r
struct notifier_block *dvfs_nb;\r
struct cpufreq_frequency_table *dvfs_table;\r
clk_dvfs_target_callback clk_dvfs_target;\r
+ struct clk_disable_ctr *disable_ctr;\r
};\r
\r
struct dvfs_arm_table {\r
#define dvfs_clk_get(a,b) clk_get((a),(b))\r
#define dvfs_clk_get_rate_kz(a) (clk_get_rate((a))/1000)\r
#define dvfs_clk_set_rate(a,b) clk_set_rate((a),(b))\r
-#define dvfs_clk_enable(a) clk_enable((a))\r
-#define dvfs_clk_disable(a) clk_disable((a))\r
\r
\r
\r
int rk_regist_depends(struct depend_lookup *dep_node);\r
struct clk_node *dvfs_get_dvfs_clk_byname(char *name);\r
int vd_regulator_round_volt(struct vd_node *vd, int volt,int flags);\r
+int dvfs_vd_clk_disable_target(struct clk *clk, int on);\r
+void dvfs_clk_disable_delay_work(struct work_struct *work);\r
\r
-/******************************************************************************/\r
-\r
-int is_support_dvfs(struct clk_node *dvfs_info);\r
-int dvfs_set_rate(struct clk *clk, unsigned long rate);\r
+/*********************************if not define dvfs ,the following function is need defined func{}******************************/\r
+int dvfs_vd_clk_set_rate(struct clk *clk, unsigned long rate);\r
+int dvfs_vd_clk_disable(struct clk *clk, int on);\r
int clk_enable_dvfs(struct clk *clk);\r
int clk_disable_dvfs(struct clk *clk);\r
void dvfs_clk_register_set_rate_callback(struct clk *clk, clk_dvfs_target_callback clk_dvfs_target);\r
int dvfs_clk_enable_limit(struct clk *clk, unsigned int min_rate, unsigned max_rate);\r
int dvfs_clk_disable_limit(struct clk *clk);\r
int dvfs_scale_volt_direct(struct vd_node *vd_clk, int volt_new);\r
-\r
+/******************************** inline *******************************/\r
+static inline bool dvfs_support_clk_set_rate(struct clk_node *dvfs_info)\r
+{\r
+ return (dvfs_info&&dvfs_info->enable_dvfs);\r
+}\r
+static inline bool dvfs_support_clk_disable(struct clk_node *dvfs_info)\r
+{\r
+ return (dvfs_info&&dvfs_info->disable_ctr&&dvfs_info->enable_dvfs);\r
+}\r
+/********************************avs*******************************/\r
void avs_init(void);\r
void avs_init_val_get(int index,int vol,char *s);\r
int avs_set_scal_val(u8 avs_base);\r
void avs_board_init(struct avs_ctr_st *data);\r
\r
#else\r
-static inline int is_support_dvfs(struct clk_node *dvfs_info) { return 0; }\r
-static inline int dvfs_set_rate(struct clk *clk, unsigned long rate) { return 0; }\r
+static inline bool dvfs_support_clk_set_rate(struct clk_node *dvfs_info) { return 0; }\r
+static inline bool dvfs_support_clk_disable(struct clk_node *dvfs_info) { return 0; }\r
+\r
+static inline int dvfs_vd_clk_set_rate(struct clk *clk, unsigned long rate) { return 0; }\r
+static inline int dvfs_vd_clk_disable(struct clk *clk, int on) { return 0; }\r
static inline int clk_enable_dvfs(struct clk *clk) { return 0; }\r
static inline int clk_disable_dvfs(struct clk *clk) { return 0; }\r
static inline void dvfs_clk_register_set_rate_callback(struct clk *clk, clk_dvfs_target_callback clk_dvfs_target) {}\r