/*
 * 设置profile
 * success: return BSP_OK
 * fail:    return BSP_ERROR
 */
int pwrctrl_dfs_set_profile(int profile)
{
	struct cpufreq_msg set_msg = {0,0,0,0};
	if ((profile < BALONG_FREQ_MIN) || (profile > BALONG_FREQ_MAX))
	{
		cpufreq_err("profile in right bound??%d\n", profile);
		return BSP_ERROR;
	}
	set_msg.msg_type = CPUFREQ_ADJUST_FREQ;
	set_msg.source = CPUFREQ_ACORE;
	if (pwrctrl_dfs_get_profile() < profile)
	{
		set_msg.content = DFS_PROFILE_UP_TARGET;
	}
	else if (pwrctrl_dfs_get_profile() > profile)
	{
		set_msg.content = DFS_PROFILE_DOWN_TARGET;
	}
	else
	{
		return BSP_OK;
	}
	set_msg.profile = (u32)profile;
	return balong_cpufreq_icc_send(&set_msg);
}
static void  pwrctrl_dfs_mgrmsg_task(void)
{
    int cur_profile = 0;
    unsigned int flowctrl_cpuload = 0;
    PWRCTRLFUNCPTR pRoutine = NULL;
    struct cpufreq_msg task_msg = {0,0,0,0};
	g_stDfsCpuControl.ulStartTime = bsp_get_slice_value();
	/* coverity[INFINITE_LOOP] */
	/* coverity[no_escape] */
    for (;;)
    {
		if (NULL != g_sem_calccpu_flag)
		{	
			semTake(g_sem_calccpu_flag, DFS_WAIT_FOREVER);
			 /*调用ttf回调函数*/
		    if ((NULL != FlowCtrlCallBack) && (g_flowctrl_in_interr_times >= 200))
		    {
		    	 flowctrl_cpuload = cpufreq_calccpu_cpuload();
		        pRoutine = FlowCtrlCallBack;
		        (void)(*pRoutine)(flowctrl_cpuload);
		    }
		}
		else
		{
			taskDelay((int)g_stDfsCpuConfigInfo.ulTimerLen);
			g_calccpu_load_result = cpufreq_calccpu_result(&g_next_freq);
		}
		if (!g_cpufreq_lock_status_flag)
		{
			continue;
		}
		
		cur_profile = pwrctrl_dfs_get_profile();
		cpufreq_assistant_regulate_ddr(cur_profile);
		if (DFS_PROFILE_NOCHANGE != g_calccpu_load_result)
		{
			if (g_icc_run_flag == 1)
			{
				task_msg.msg_type = CPUFREQ_ADJUST_FREQ;
				task_msg.source = CPUFREQ_CCORE;
				task_msg.content = g_calccpu_load_result;
				if (DFS_PROFILE_UP_TARGET == g_calccpu_load_result)
				{
					cur_profile = DC_RESV;
				}
				else if ((u32)cur_profile == CPUFREQ_MIN_PROFILE_LIMIT)
				{
					continue;
				}
				task_msg.profile = (unsigned int)cur_profile - 1;
				balong_cpufreq_icc_send(&task_msg);
			}
			else if (g_icc_run_flag == 2)
			{
				cpufreq_excute_result_cpu(g_calccpu_load_result, g_next_freq);
			}
        }

    }
}
/*检查调频请求是否符合限制*/
static int balong_check_msg(struct cpufreq_msg *msg)
{
	int ret = BSP_OK;
	int max_limit = 0;
	int min_limit = 0;
	balong_check_profile_limit(&max_limit, &min_limit);
	int cur_profile = pwrctrl_dfs_get_profile();
	if (CPUFREQ_ADJUST_FREQ == msg->msg_type)
	{
		switch(msg->content)
		{
			case DFS_PROFILE_UP:
			case DFS_PROFILE_UP_TARGET:
				if (max_limit == cur_profile)
				{
					ret = BSP_ERROR;
				}
				if (msg->profile > (u32)max_limit)
				{
					msg->profile = (u32)max_limit;
				}
				break;
			case DFS_PROFILE_DOWN:
			case DFS_PROFILE_DOWN_TARGET:
				if (min_limit == cur_profile)
				{
					ret = BSP_ERROR;
				}
				if (msg->profile < (u32)min_limit)
				{
					msg->profile = (u32)min_limit;
				}
#ifdef CPUFREQ_IS_SYNC_DDR
			/*判断本次请求是否和上次重复,重复则停止再次发送*/
			if ((1 == CPUFREQ_DOWN_FLAG(msg->source)) 
					&& ((msg->profile) == CPUFREQ_DOWN_PROFILE(msg->source)))
			{
				ret = BSP_ERROR;
			}
#endif
				break;
			case DFS_PROFILE_DOWN_LIMIT:
				if ((u32)min_limit == msg->profile)
				{
					ret =BSP_ERROR;
				}
				break;
			case DFS_PROFILE_UP_LIMIT:
				if ((u32)max_limit == msg->profile)
				{
					ret =BSP_ERROR;
				}
				break;
			default:
				break;
		}
	}/*CPUFREQ_ADJUST_FREQ == msg->msg_type*/
	return ret;
}
static inline void cpufreq_pm_om_log(struct cpufreq_msg *msg)
{
	struct cpufreq_debug_msg debug_msg_log = {msg->msg_type, msg->source, msg->content, msg->profile, 0, 0, 0};
	debug_msg_log.taskid = taskIdSelf();
	debug_msg_log.cur_profile = (unsigned int)pwrctrl_dfs_get_profile();
	debug_msg_log.cur_load = g_ulCCpuload;
	bsp_pm_log_type(PM_OM_CPUF, CPUFREQ_ICC_LOG, sizeof(struct cpufreq_debug_msg), &debug_msg_log);
}
/*
 * Here we notify other drivers of the proposed change and the final change.
 *
 *
 *根据relation做相应动作
 */
static s32 balong_cpufreq_target(struct cpufreq_policy *policy,
				     u32 target_freq,
				     u32 relation)
{
	u32 result = 2;
	u32 new_index = 0;
	int cur_profile = 0;
	struct cpufreq_msg task_msg = {0,0,0,0};
	
	
	cpufreq_frequency_table_target(policy, balong_clockrate_table,
					   target_freq, relation, &new_index);
					   
	cpufreq_debug("target_freq %d new_index%d\n", target_freq, new_index);

	cur_profile = pwrctrl_dfs_get_profile();
	
	if ((CPUFREQ_RELATION_H == relation) && (BALONG_FREQ_MAX != cur_profile))
	{
		result = DFS_PROFILE_UP_TARGET;
	}
	else if ((CPUFREQ_RELATION_L == relation) && (BALONG_FREQ_MIN != cur_profile))
	{
		result = DFS_PROFILE_DOWN_TARGET;
	}
	else
	{
		policy->cur = balong_clockrate_table[cur_profile].frequency;
		g_cur_freq = policy->cur;
		cpufreq_info("set target relation %d, cur pro %d\n", relation, policy->cur);
		return BSP_ERROR;
	}
	
	cpufreq_frequency_target_profile(balong_clockrate_table[new_index].frequency, relation, &new_index);

	task_msg.msg_type = CPUFREQ_ADJUST_FREQ;
	task_msg.source = CPUFREQ_ACORE;
	task_msg.content = result;
	task_msg.profile = new_index;
	balong_cpufreq_icc_send(&task_msg);
	
	policy->cur = balong_clockrate_table[cur_profile].frequency;
	g_cur_freq = policy->cur;
	return BSP_OK;
}
/*
 * 获取当前频率 BSP_ERROR 设置失败;BSP_OK 设置成功
 * 
 */
int pwrctrl_dfs_current(int *a9freq, int *ddrfreq, int *slowfreq)
{
	int cur_profile = 0;
	int ret = BSP_OK;
	if ((a9freq != NULL) && (ddrfreq != NULL) && (slowfreq != NULL))
	{
		cur_profile = pwrctrl_dfs_get_profile();
		*a9freq = (s32)balong_query_profile_table[cur_profile].cpu_frequency;
		*ddrfreq = (s32)balong_query_profile_table[cur_profile].ddr_frequency;
		*slowfreq = (s32)balong_query_profile_table[cur_profile].sbus_frequency;
	}
	else
	{
		cpufreq_err("argv is NULL,check it\n");
		ret = BSP_ERROR;
	}
	return ret;
}
/*find the first profile that is the target*/
static void cpufreq_frequency_target_profile( unsigned int target_freq,
				   unsigned int relation,  unsigned int *new_profile)
{
	int i = 0;
	int cur_profile = pwrctrl_dfs_get_profile();
	switch (relation)
	{
		case DFS_PROFILE_UP:
			*new_profile = balong_clockrate_table[BALONG_FREQ_MAX].index;
			return;
		case DFS_PROFILE_DOWN:
			cpufreq_debug("down to frequency\n");
			break;
		default :
			break;
	}

	for (i = cur_profile; i >= BALONG_FREQ_MIN; i--)
	{
		if (target_freq != balong_query_profile_table[i].cpu_frequency)
		{
			continue;
		}
		if ((u32)cur_profile == balong_query_profile_table[i].profile)
		{
			continue;
		}
		*new_profile = balong_query_profile_table[i].profile;
		goto out;
	}
	if (cur_profile != BALONG_FREQ_MIN)
	{
		*new_profile = balong_query_profile_table[cur_profile - 1].profile;
	}
	else
	{
		*new_profile = balong_query_profile_table[BALONG_FREQ_MIN].profile;
	}
out:
	return;
}
void cpufreq_print_debug(void)
{
	pwrctrl_dfs_get_profile();
	cpufreq_err("cur\t profile: %d\n", g_cur_profile);
	cpufreq_err("ccore\t load: %d\n", g_ulCCpuload);
	cpufreq_err("lase\t ddr: %d\n", g_last_ddr_value_id);
	cpufreq_err("up      times: %d\n", g_stDfsCpuControl.ulCurSysUpTime);
	cpufreq_err("down  times: %d\n", g_stDfsCpuControl.ulCurSysDownTime);
	cpufreq_err("up_threshold: %d\n", g_stDfsCpuConfigInfo.astThresHold[0].usProfileUpLimit);
	cpufreq_err("up_threshold_times: %d\n",  g_stDfsCpuConfigInfo.usProfileUpTime);
	cpufreq_err("down_threshold: %d\n", g_stDfsCpuConfigInfo.astThresHold[0].usProfileDownLimit);
	cpufreq_err("down_threshold_times: %d\n",  g_stDfsCpuConfigInfo.usProfileDownTime);
	cpufreq_err("sampling_rate: %d\n", g_stDfsCpuConfigInfo.ulTimerLen);
	cpufreq_err("last icc msg\n");
	cpufreq_err("icc msg_type: %d\n", debug_msg.msg_type);
	cpufreq_err("icc source: %d\n", debug_msg.source);
	cpufreq_err("icc content: %d\n", debug_msg.content);
	cpufreq_err("icc profile: %d\n", debug_msg.profile);
	cpufreq_err("cur max limit:%d\n", CPUFREQ_MAX_PROFILE_LIMIT);
	cpufreq_err("cur min limit:%d\n", CPUFREQ_MIN_PROFILE_LIMIT);
}
void cpufreq_print_debug(void)
{
    struct cpu_dbs_info_s *dbs_info;
    dbs_info = &per_cpu(g_acpu_dbs_info, 0);
    pwrctrl_dfs_get_profile();
    cpufreq_err("cur    profile: %d\n", g_cur_profile);
    cpufreq_err("acore    load: %d\n", g_ulACpuload);
    cpufreq_err("up      times: %d\n", dbs_info->cpu_up_time);
    cpufreq_err("down  times: %d\n", dbs_info->cpu_down_time);
    cpufreq_err("up_threshold: %d\n", dbs_tuners_ins.up_threshold);
    cpufreq_err("up_threshold_times: %d\n", dbs_tuners_ins.up_threshold_times);
    cpufreq_err("down_threshold: %d\n", dbs_tuners_ins.down_threshold);
    cpufreq_err("down_threshold_times: %d\n", dbs_tuners_ins.down_threshold_times);
    cpufreq_err("sampling_rate: %d\n", dbs_tuners_ins.sampling_rate);
    cpufreq_err("last icc msg\n");
    cpufreq_err("icc msg_type: %d\n", debug_msg.msg_type);
    cpufreq_err("icc source: %d\n", debug_msg.source);
    cpufreq_err("icc content: %d\n", debug_msg.content);
    cpufreq_err("icc profile: %d\n", debug_msg.profile);
    cpufreq_err("cur max limit:%d\n", CPUFREQ_MAX_PROFILE_LIMIT);
    cpufreq_err("cur min limit:%d\n", CPUFREQ_MIN_PROFILE_LIMIT);
}
/*
 * 该接口负责cpu负载检测,
 * 并根据预设阈值判决是否需要向M3请求调频
 */
void cpufreq_update_frequency(void)
{
    u32 cpuload = 0;
    int cur_profile = 0;
    struct cpufreq_msg task_msg = {CPUFREQ_ADJUST_FREQ, CPUFREQ_ACORE, 0, BALONG_FREQ_MAX};
    cpuload = (u32)cpufreq_calccpu_cpuload();
    cur_profile = (int)pwrctrl_dfs_get_profile();
    if (cpuload > dbs_tuners_ins.up_threshold)
    {
        task_msg.content = DFS_PROFILE_UP_TARGET;
    }
    else if (cpuload < dbs_tuners_ins.down_threshold)
    {
        task_msg.profile = (cur_profile != BALONG_FREQ_MIN) ? (cur_profile - 1) : (BALONG_FREQ_MIN);
        task_msg.content = DFS_PROFILE_DOWN;
    }
    else
    {
        return;
    }
    balong_cpufreq_icc_send(&task_msg);
}
/*
 * 该接口负责cpu负载检测,
 * 并根据预设阈值判决是否需要向M3请求调频
 */
void cpufreq_update_frequency(void)
{
	u32 cpuload = 0;
	int cur_profile = 0;
	struct cpufreq_msg task_msg = {CPUFREQ_ADJUST_FREQ, CPUFREQ_CCORE, 0, BALONG_FREQ_MAX};
	cpuload = cpufreq_calccpu_cpuload();
	cur_profile = pwrctrl_dfs_get_profile();
	if (cpuload > g_stDfsCpuConfigInfo.astThresHold[0].usProfileUpLimit)
   {
		task_msg.content = DFS_PROFILE_UP_TARGET;
	}
    else if (cpuload < g_stDfsCpuConfigInfo.astThresHold[0].usProfileDownLimit)
    {
		task_msg.profile = (cur_profile != BALONG_FREQ_MIN) ? (cur_profile - 1) : (BALONG_FREQ_MIN);
    	task_msg.content = DFS_PROFILE_DOWN;
    }
    else
    {
		return;
    }
    balong_cpufreq_icc_send(&task_msg);
}
/*find a freq in all table*/
static int cpufreq_frequency_table_target(struct cpufreq_frequency_table *table,
				   unsigned int target_freq,
				   unsigned int relation,
				   unsigned int *index)
{/*lint !e578 */
	struct cpufreq_frequency_table optimal = {
		.index = ~0,
		.frequency = 0,
	};
	struct cpufreq_frequency_table suboptimal = {
		.index = ~0,
		.frequency = 0,
	};
	unsigned int i;
	/*lint --e{744} */
	switch (relation) {
	case DFS_PROFILE_UP:
		suboptimal.frequency = ~0;
		break;
	case DFS_PROFILE_DOWN:
		optimal.frequency = ~0;
		break;
	}

	for (i = 0; (table[i].frequency != (u32)CPUFREQ_TABLE_END); i++) {
		unsigned int freq = table[i].frequency;
		if (freq == (u32)CPUFREQ_ENTRY_INVALID)
			continue;
		if ((freq < balong_query_profile_table[BALONG_FREQ_MIN].cpu_frequency) || (freq > balong_query_profile_table[BALONG_FREQ_MAX].cpu_frequency))
			continue;
		switch (relation) {
		case DFS_PROFILE_UP:
			if (freq <= target_freq) {
				if (freq >= optimal.frequency) {
					optimal.frequency = freq;
					optimal.index = i;
				}
			} else {
				if (freq <= suboptimal.frequency) {
					suboptimal.frequency = freq;
					suboptimal.index = i;
				}
			}
			break;
		case DFS_PROFILE_DOWN:
			if (freq >= target_freq) {
				if (freq <= optimal.frequency) {
					optimal.frequency = freq;
					optimal.index = i;
				}
			} else {
				if (freq >= suboptimal.frequency) {
					suboptimal.frequency = freq;
					suboptimal.index = i;
				}
			}
			break;
		}
	}
	if (optimal.index > i) {
		if (suboptimal.index > i)
			return BSP_ERROR;
		*index = suboptimal.index;
	} else
		*index = optimal.index;

	return BSP_OK;
}
/*****************************************************************************
Function:   PWRCTRL_DfsMgrExcuteVoteResultCpu
Description:Handle the Profile Vote Result
Input:      enResult:   The Vote Value
Output:     None
Return:     None
Others:
*****************************************************************************/
static int  cpufreq_excute_result_cpu(u32 relation, u32 target_freq)
{
	u32 result = 2;
	u32 new_index = 0;
	int cur_profile = 0;
	struct cpufreq_msg task_msg = {0,0,0,0};

	cpufreq_frequency_table_target(balong_clockrate_table,
					   target_freq, relation, &new_index);

	cpufreq_debug("target_freq %d new_index%d\n", target_freq, new_index);

	cur_profile = pwrctrl_dfs_get_profile();
	if ((DFS_PROFILE_UP == relation) && (BALONG_FREQ_MAX != cur_profile))
	{
		result = DFS_PROFILE_UP_TARGET;
	}
	else if ((DFS_PROFILE_DOWN == relation) && (BALONG_FREQ_MIN != cur_profile))
	{
		result = DFS_PROFILE_DOWN_TARGET;
	}
	else
	{
		cpufreq_err("set target relation %d, cur pro %d\n", relation, cur_profile);
		return BSP_ERROR;
	}

	cpufreq_frequency_target_profile(balong_clockrate_table[new_index].frequency, relation, &new_index);

	task_msg.msg_type = CPUFREQ_ADJUST_FREQ;
	task_msg.source = CPUFREQ_CCORE;
	task_msg.content = result;
	task_msg.profile = new_index;
	balong_cpufreq_icc_send(&task_msg);
	g_stDfsCpuControl.enCurProfile = (u32)cur_profile;
	return BSP_OK;

}

unsigned int cpufreq_calccpu_cpuload(void)
{
	u32 end_time = 0;
	u32 idle_time = 0;
	u32 wall_time = 0;
	u32 cpu_load = 0;
	unsigned long irqlock = 0;
	local_irq_save(irqlock);
	end_time = bsp_get_slice_value();
	wall_time = get_timer_slice_delta(g_cpufreq_start_time, end_time);
	idle_time = g_ulDfsCcpuIdleTime_long;
	g_cpufreq_start_time = end_time;
	g_ulDfsCcpuIdleTime_long = 0;
	g_flowctrl_in_interr_times = 0;
	cpu_load = (wall_time == 0) ? (0) : (((wall_time - idle_time) * 100) / wall_time);
	local_irq_restore(irqlock);
	if (cpu_load > 100)
	{
		cpu_load = g_ulCCpuload;
		cpufreq_info("cpuload: %d, wall:%d, idle:%d\n", cpu_load, wall_time, idle_time);
	}
	return cpu_load;
}
/*
	调频策略
*/
static u32  cpufreq_calccpu_result(u32 *next_freq)
{
    u32 ulSleepTime;
    u32 ulCpuFree;
    u32 ulCpuLoad_C;
    u32 ulProTime;
    u32 ulEndTime;
    int cur_profile = 0;
    PWRCTRLFUNCPTR pRoutine = NULL;

    /*这里是读取时间,暂时大桩*/
	ulEndTime = bsp_get_slice_value();
  	ulProTime = ulEndTime - g_stDfsCpuControl.ulStartTime;
	ulSleepTime = g_ulDfsCcpuIdleTime;
	g_ulDfsCcpuIdleTime = 0;
	g_stDfsCpuControl.ulStartTime = ulEndTime;
	ulCpuFree= (ulSleepTime* 100) / (ulProTime);    /*Calc the Cpu Free Value*/

	if (ulCpuFree > 100)
    {
		cpufreq_err("calc cpuload error!\n");
        return DFS_PROFILE_NOCHANGE;
    }
	ulCpuLoad_C= 100 - ulCpuFree;
	g_ulCCpuload = ulCpuLoad_C;
	 /*调用ttf回调函数*/
    if (NULL != FlowCtrlCallBack)
    {
        pRoutine = FlowCtrlCallBack;
        (void)(*pRoutine)(g_ulCCpuload);
    }
    else
    {
	}
    if (ulCpuLoad_C > g_stDfsCpuConfigInfo.astThresHold[0].usProfileUpLimit)
    {
        /*Clean the Value of the System Down Counter*/
        g_stDfsCpuControl.ulCurSysDownTime = 0;
		g_stDfsCpuControl.ulCurSysUpTime++;
	}
    else if (ulCpuLoad_C < g_stDfsCpuConfigInfo.astThresHold[0].usProfileDownLimit)
    {
        /*Clean the Value of the System Over Load Counter*/
		g_stDfsCpuControl.ulCurSysUpTime = 0;
		g_stDfsCpuControl.ulCurSysDownTime++;
    }
    else /*The System Load is Normal Value*/
    {
		g_stDfsCpuControl.ulCurSysDownTime = 0;
        g_stDfsCpuControl.ulCurSysUpTime = 0;

        return DFS_PROFILE_NOCHANGE;
    }
	cur_profile = pwrctrl_dfs_get_profile();
    if (g_stDfsCpuControl.ulCurSysDownTime >= g_stDfsCpuConfigInfo.usProfileDownTime)
    {
		g_stDfsCpuControl.ulCurSysDownTime = 0;
        g_stDfsCpuControl.ulCurSysUpTime = 0;

        *next_freq =( ulCpuLoad_C * (balong_query_profile_table[cur_profile].cpu_frequency))
        							/ (g_stDfsCpuConfigInfo.astThresHold[0].usProfileDownLimit);
        return DFS_PROFILE_DOWN;
    }

    if (g_stDfsCpuControl.ulCurSysUpTime >= g_stDfsCpuConfigInfo.usProfileUpTime)
    {
		 g_stDfsCpuControl.ulCurSysDownTime = 0;
        g_stDfsCpuControl.ulCurSysUpTime = 0;
        *next_freq = balong_query_profile_table[BALONG_FREQ_MAX].cpu_frequency;/*max cpufreq*/
        return DFS_PROFILE_UP_TARGET;
    }

	return DFS_PROFILE_NOCHANGE;

}