s32 invoke_reset_cb(DRV_RESET_CALLCBFUN_MOMENT stage)
{
	struct reset_cb_list *p = g_modem_reset_ctrl.list_head;
	s32 ret = RESET_ERROR;

	reset_print_debug("(%d) @reset %d\n", ++g_reset_debug.main_stage, (u32)stage);

	/*根据回调函数优先级,调用回调函数*/
    while (NULL != p)
    {
        if (NULL != p->cb_info.cbfun)
        {
			reset_print_debug("%s callback invoked\n", p->cb_info.name);
            ret = p->cb_info.cbfun(stage, p->cb_info.userdata);
            if (RESET_OK != ret)
            {
                reset_print_err("fail to run cbfunc of %s, at stage%d return %d\n", p->cb_info.name, stage, ret);
                return RESET_ERROR;
            }
        }
        p = p->next;
    }

	return RESET_OK;
}
s32 wait_for_ccore_reset_done(u32 timeout)
{
	reset_print_debug("(%d) waiting the reply from modem A9\n", ++g_reset_debug.main_stage);

	if (osl_sem_downtimeout(&(g_modem_reset_ctrl.wait_ccore_reset_ok_sem), msecs_to_jiffies(timeout)))/*lint !e713 */
	{
		reset_print_err("Get response from ccore reset timeout within %d\n", timeout);
	    return RESET_ERROR;
	}

	reset_print_debug("(%d) has received the reply from modem A9\n", ++g_reset_debug.main_stage);
	return RESET_OK;
}
void let_modem_master_in_idle(void)
{
	u32 regval = 0;

	/* nmi开关 */
	regval = readl((volatile const void *)(g_modem_reset_ctrl.crg_base + 0x12c));
	reset_print_debug("org ccore nmi regval[%p]=0x%x\n", (g_modem_reset_ctrl.crg_base + 0x12c), regval);
	regval &= (~((u32)1 << 12));
	writel(regval,(volatile void *)(g_modem_reset_ctrl.crg_base + 0x12c));
	regval = readl((volatile const void *)(g_modem_reset_ctrl.crg_base + 0x12c));
	reset_print_debug("(%d) ccore nmi regval: 0x%x\n", ++g_reset_debug.main_stage, regval);

	return;
}
s32 reset_prepare(enum MODEM_ACTION action)
{
	unsigned long flags = 0;
	u32 current_action = (u32)action;
	u32 global_action = g_modem_reset_ctrl.modem_action;

	if (current_action == global_action)
	{
		return RESET_OK;
	}
	else if ((u32)MODEM_NORMAL != global_action)
	{
		reset_print_err("action(%d) is doing, abundon action(%d)\n", global_action, action);
		return RESET_ERROR;
	}

	g_reset_debug.main_stage = 0;

	wake_lock(&(g_modem_reset_ctrl.wake_lock));
	reset_print_debug("(%d) wake_lock\n", ++g_reset_debug.main_stage);

	spin_lock_irqsave(&g_modem_reset_ctrl.action_lock, flags);
	g_modem_reset_ctrl.modem_action = action;
	spin_unlock_irqrestore(&g_modem_reset_ctrl.action_lock, flags);

	if ((MODEM_POWER_OFF ==  current_action) || (MODEM_RESET ==  current_action))
	{
		ccore_ipc_disable();
	}
	osl_sem_up(&(g_modem_reset_ctrl.task_sem));

	return RESET_OK;
}
void bsp_modem_power_on(void)
{
	reset_print_debug("\n");;
	if (g_modem_reset_ctrl.nv_config.is_feature_on)
	{
		queue_work(g_modem_reset_ctrl.reset_wq, &(g_modem_reset_ctrl.work_power_on));
	}
	else
	{
		reset_print_err("reset fearute is off\n");
	}
}
void modem_power_on_do_work(struct work_struct *work)
{
	reset_print_debug("\n");
	if (1 == g_modem_reset_ctrl.in_suspend_state)
	{
		if (!wait_for_completion_timeout(&(g_modem_reset_ctrl.suspend_completion), HZ*10))
		{
			machine_restart("system halt");
			return;
		}
	}
	reset_prepare(MODEM_POWER_ON);
}
int bsp_modem_reset(void)
{
	reset_print_debug("\n");
	if (g_modem_reset_ctrl.nv_config.is_feature_on)
	{
		queue_work(g_modem_reset_ctrl.reset_wq, &(g_modem_reset_ctrl.work_reset));
		return 0;
	}
	else
	{
		reset_print_err("reset fearute is off\n");
		return -1;
	}
}
void modem_reset_do_work(struct work_struct *work)
{
	reset_print_debug("\n");
	g_modem_reset_ctrl.exec_time = bsp_get_slice_value();
	if (1 == g_modem_reset_ctrl.in_suspend_state)
	{
		if (!wait_for_completion_timeout(&(g_modem_reset_ctrl.suspend_completion), HZ*10))
		{
			machine_restart("system halt"); /* 调systemError */
			return;
		}
	}
	reset_prepare(MODEM_RESET);
}
int modem_reset_task(void *arg)
{
	u16 action = 0;
	unsigned long flags = 0;

	for( ; ;)
	{
		osl_sem_down(&(g_modem_reset_ctrl.task_sem));
		action = (u16)g_modem_reset_ctrl.modem_action;
		reset_print_debug("(%d)has taken task_sem, action=%d\n", ++g_reset_debug.main_stage, action);

		if (MODEM_POWER_OFF == action)
		{
			(void)do_power_off(action);
		}
		else if (MODEM_POWER_ON == action)
		{
			(void)do_power_on(action);
		}
		else if (MODEM_RESET == action)
		{
			(void)do_reset(action);
			reset_print_err("reset count: %d\n", ++g_modem_reset_ctrl.reset_cnt);
		}
		if (action == g_modem_reset_ctrl.modem_action)
		{
			spin_lock_irqsave(&g_modem_reset_ctrl.action_lock, flags);
			g_modem_reset_ctrl.modem_action = MODEM_NORMAL;
			spin_unlock_irqrestore(&g_modem_reset_ctrl.action_lock, flags);
		}
		wake_unlock(&(g_modem_reset_ctrl.wake_lock));

		g_modem_reset_ctrl.exec_time = get_timer_slice_delta(g_modem_reset_ctrl.exec_time, bsp_get_slice_value());
		reset_print_debug("execute done, elapse time %d\n", g_modem_reset_ctrl.exec_time);
	}

}
void from_hifi_mailbox_readcb(void  *usr_handle, void *mail_handle, unsigned int mail_len)/*lint !e438 */
{
    unsigned long   ret = 0;
    unsigned int   msg_len = (unsigned int)sizeof(HIFI_AP_CCPU_RESET_CNF_STRU);
    HIFI_AP_CCPU_RESET_CNF_STRU msg_hifi = {0};

    ret = DRV_MAILBOX_READMAILDATA(mail_handle, (unsigned char *)(&msg_hifi), &msg_len);
	reset_print_debug("ret=%lu, uhwMsgId=%d, uhwResult=%d\n", ret, msg_hifi.uhwMsgId, msg_hifi.uhwResult);
    if (ID_HIFI_AP_CCPU_RESET_CNF == msg_hifi.uhwMsgId && 0 == msg_hifi.uhwResult)
    {
        up(&(g_modem_reset_ctrl.wait_hifi_reply_sem));
    }
    else
    {
	    up(&(g_modem_reset_ctrl.wait_hifi_reply_sem));
        reset_print_err("unkown msg from hifi\n");
		reset_reboot_system(RESET_TYPE_RECV_HIFI_MSG_FAIL);
    }

}
s32 send_msg_to_hifi(DRV_RESET_CALLCBFUN_MOMENT stage)
{
	s32 ret = RESET_ERROR;
	AP_HIFI_CCPU_RESET_REQ_STRU hifi_mailbox = {0};

	reset_print_debug("(%d) stage%d,ID_AP_HIFI_CCPU_RESET_REQ=%d\n", ++g_reset_debug.main_stage, (s32)stage, (s32)ID_AP_HIFI_CCPU_RESET_REQ);

	if (DRV_RESET_CALLCBFUN_RESET_BEFORE == stage)
	{
		/*消息ID*/
		hifi_mailbox.uhwMsgId = ID_AP_HIFI_CCPU_RESET_REQ;
		ret = DRV_MAILBOX_SENDMAIL(MAILBOX_MAILCODE_ACPU_TO_HIFI_CCORE_RESET_ID, (void *)(&hifi_mailbox), sizeof(hifi_mailbox)); /*lint !e713 */
		if(MAILBOX_OK != ret)
		{
			reset_print_err("fail to send msg to hifi\n");
			return RESET_ERROR;
		}
	}

	/* 如果有必要,其他阶段也通知hifi */
	return RESET_OK;
}
s32 load_modem_image(void)
{
	s32 ret = SEC_ERROR;

	reset_print_debug("(%d) vxworks & dsp image is loading...\n", ++g_reset_debug.main_stage);

	ret = bsp_load_modem();
	if(ret)
	{
		sec_print_err("vxworks fail\n");
		return SEC_ERROR;
	}

	ret = bsp_load_modem_dsp();
	if(ret)
	{
		sec_print_err("dsp fail\n");
		return SEC_ERROR;
	}

	return SEC_OK;
}
void reset_ipc_isr_reboot(u32 data)
{
	reset_print_debug("\n");
	osl_sem_up(&(g_modem_reset_ctrl.wait_ccore_reset_ok_sem));
}
void reset_ipc_isr_idle(u32 data)
{
	reset_print_debug("\n");
	osl_sem_up(&(g_modem_reset_ctrl.wait_modem_master_in_idle_sem));
}
s32 do_power_on(u16 action)
{
	u32 msg = 0;
	s32 ret = RESET_ERROR;
	u32 ack_val = 0xff;
	u32 i;

	/* C核和dsp镜像加载 */
	/* 如出现错误,重试3次,直至每次都错误,则复位系统 */
	for (i=0; i<RESET_RETRY_TIMES; i++)
	{
		ret = load_modem_image();
		if (ret == 0)
			break;
		else
			reset_print_err("Retry %d times to load modem image also failed\n", i);
	}
	if(ret < 0)
	{
		reset_reboot_system(RESET_TYPE_LOAD_MODEM_IMG_FAIL);
		return RESET_ERROR;
	}

	/* 清除C核dump区域 */
	rdr_modem_reset_dumpmem();

	/* 通知m3进行A9解复位及相关处理 */
	msg = RESET_INFO_MAKEUP(action, (u32)DRV_RESET_CALLCBFUN_RESETING); /*lint !e701 */
	ret = RESET_ERROR;
	ret = send_sync_msg_to_mcore(msg, &ack_val);
	if(ret)
	{
		reset_print_err("send_sync_msg_to_mcore(0x%x) at resting stage fail!\n", ret);
		reset_reboot_system(RESET_TYPE_SEND_MSG2_M3_FAIL_RESTING);
		return RESET_ERROR;
	}
	else if(RESET_MCORE_RESETING_OK != ack_val)
	{
		reset_print_err("modem unreset fail on m3, ack_val=0x%x, %s\n", ack_val, 
			lpm3_err[ack_val-RESET_MCORE_BEFORE_AP_TO_MDM_BUS_ERR]);
		reset_reboot_system(RESET_TYPE_RECV_WRONG_MSG_FROM_M3_RESTING);
		return RESET_ERROR;
	}
	reset_print_debug("(%d) at reseting stage has communicated with lpm3 succeed\n", ++g_reset_debug.main_stage);

	/* 复位中相关处理:与C核需要交互的模块在此阶段准备好 */
	if ((MODEM_POWER_ON == action) || (MODEM_RESET == action))
	{
		ccore_ipc_enable();
	}
	ret = drv_reset_cb(DRV_RESET_CALLCBFUN_RESETING, 0);
	if(ret < 0)
	{
		reset_reboot_system(RESET_TYPE_CB_INVOKE_RESTING);
		return RESET_ERROR;
	}

	ret = wait_for_ccore_reset_done(RESET_WAIT_CCPU_STARTUP_TIMEOUT);
	if(ret < 0)
	{
		reset_reboot_system(RESET_TYPE_WAIT_CCORE_RELAY_TIMEOUT);
		return RESET_ERROR;
	}


	/* 复位后相关处理 */
	ret = invoke_reset_cb(DRV_RESET_CALLCBFUN_RESET_AFTER);
	if(ret < 0)
	{
		reset_reboot_system(RESET_TYPE_CB_INVOKE_AFTER);
		return RESET_ERROR;
	}

	/* 复位后通知M3进行相关处理 */
	msg = RESET_INFO_MAKEUP(action, DRV_RESET_CALLCBFUN_RESET_AFTER); /*lint !e701 */
	ret = RESET_ERROR;
	ret = send_sync_msg_to_mcore(msg, &ack_val);
	if(ret)
	{
		reset_print_err("send_sync_msg_to_mcore(0x%x) after reset stage fail!\n", ret);
		reset_reboot_system(RESET_TYPE_SEND_MSG2_M3_FAIL_AFTER);
		return RESET_ERROR;
	}
	else if(RESET_MCORE_AFTER_RESET_OK != ack_val)
	{
		reset_print_err("after reset handle failed on m3, ack_val=0x%x, fail!\n", ack_val);
		reset_reboot_system(RESET_TYPE_RECV_WRONG_MSG_FROM_M3_AFTER);
		return RESET_ERROR;
	}
	reset_print_debug("(%d) after reset stage has communicated with lpm3 succeed\n", ++g_reset_debug.main_stage);

	/* 将启动模式置回普通模式 */
	bsp_reset_bootflag_set(CCORE_BOOT_NORMAL);

	return RESET_OK;
}
s32 do_power_off(u16 action)
{
	u32 msg = 0;
	s32 ret = RESET_ERROR;
	u32 ack_val = 0xff;

	/* 设置启动模式为C核单独复位 */
	bsp_reset_bootflag_set(CCORE_IS_REBOOT);
	g_modem_reset_ctrl.boot_mode = readl((volatile const void *)SCBAKDATA13);
	reset_print_debug("(%d) set boot mode:0x%x\n", ++g_reset_debug.main_stage, g_modem_reset_ctrl.boot_mode);

	/* 唤醒ccore */
	ret = bsp_ipc_int_send(IPC_CORE_CCORE, g_modem_reset_ctrl.ipc_send_irq_wakeup_ccore);
	if(ret != 0)
	{
		reset_print_err("wakeup ccore failt\n");
	}

	/* 复位前各组件回调 */
	ret = invoke_reset_cb(DRV_RESET_CALLCBFUN_RESET_BEFORE);
	if(ret < 0)
	{
		reset_reboot_system(RESET_TYPE_CB_INVOKE_BEFORE);
		return RESET_ERROR;
	}

	/* 阻止核间通信 */
	ccore_msg_switch_off(g_modem_reset_ctrl.multicore_msg_switch, CCORE_STATUS);
	reset_print_debug("(%d) switch off msg connect:%d\n", ++g_reset_debug.main_stage, g_modem_reset_ctrl.multicore_msg_switch);

	/* 通知hifi,停止与modem交互 */
	ret = send_msg_to_hifi(DRV_RESET_CALLCBFUN_RESET_BEFORE);
	if(ret < 0)
	{
		reset_print_err("send_msg_to_hifi=0x%x fail\n", ret);
		reset_reboot_system(RESET_TYPE_SEND_MSG2_M3_FAIL_BEFORE);
		return RESET_ERROR;
	}
	/*  等待hifi处理完成 */
	if (osl_sem_downtimeout(&(g_modem_reset_ctrl.wait_hifi_reply_sem), msecs_to_jiffies((u32)RESET_WAIT_RESP_TIMEOUT)))/*lint !e713 */
	{
		reset_print_err("waiting the reply from hifi timeout(%d), but not reboot system!\n", RESET_WAIT_RESP_TIMEOUT);
	}
	reset_print_debug("(%d) has received the reply from hifi\n", ++g_reset_debug.main_stage);

	/* 通知modem master进idle态,并等待ccore回复 */
	let_modem_master_in_idle();
	ret = osl_sem_downtimeout(&(g_modem_reset_ctrl.wait_modem_master_in_idle_sem),
		msecs_to_jiffies(RESET_WAIT_MODEM_IN_IDLE_TIMEOUT));/*lint !e713 */
	if (ret)
	{
		reset_print_debug("(%d) let modem master in idle timeout\n", ++g_reset_debug.main_stage);
		master_in_idle_timestamp_dump();
	}
	else
	{
		reset_print_debug("(%d) let modem master in idle successfully\n", ++g_reset_debug.main_stage);
	}

	/* 通知m3进行复位前辅助处理 */
	msg = RESET_INFO_MAKEUP(action, DRV_RESET_CALLCBFUN_RESET_BEFORE); /*lint !e701 */
	ret = RESET_ERROR;

	/* 复位解复位modem子系统期间不接收ipc消息 */
	disable_ipc_irq();
	ret = send_sync_msg_to_mcore(msg, &ack_val);
	if(ret)
	{
		reset_print_err("send_sync_msg_to_mcore(0x%x) before reset fail!\n", ret);
		reset_reboot_system(RESET_TYPE_SEND_MSG2_M3_FAIL_BEFORE);
		return RESET_ERROR;
	}
	else if(RESET_MCORE_BEFORE_RESET_OK != ack_val)
	{
		reset_print_err("bus error probed on m3, ack_val=0x%x, %s\n!\n", ack_val, 
			lpm3_err[ack_val-RESET_MCORE_BEFORE_AP_TO_MDM_BUS_ERR]);
		reset_reboot_system(RESET_TYPE_SEND_MSG2_M3_FAIL_BEFORE);
		return RESET_ERROR;
	}
	enable_ipc_irq();
	reset_print_debug("(%d) before reset stage has communicated with lpm3 succeed\n", ++g_reset_debug.main_stage);

	return RESET_OK;
}