361 lines
9.3 KiB
C
361 lines
9.3 KiB
C
// SPDX-License-Identifier: GPL-2.0
|
|
/*
|
|
* Copyright (C) 2016 MediaTek Inc.
|
|
*/
|
|
|
|
#include "ccci_config.h"
|
|
#include "ccci_common_config.h"
|
|
#include "ccci_fsm_internal.h"
|
|
|
|
#ifdef FEATURE_SCP_CCCI_SUPPORT
|
|
#include <scp.h>
|
|
|
|
|
|
static atomic_t scp_state = ATOMIC_INIT(SCP_CCCI_STATE_INVALID);
|
|
static struct ccci_ipi_msg scp_ipi_tx_msg;
|
|
static struct mutex scp_ipi_tx_mutex;
|
|
static struct work_struct scp_ipi_rx_work;
|
|
static wait_queue_head_t scp_ipi_rx_wq;
|
|
static struct ccci_skb_queue scp_ipi_rx_skb_list;
|
|
static unsigned int init_work_done;
|
|
#if (MD_GENERATION >= 6297)
|
|
static struct ccci_ipi_msg scp_ipi_rx_msg;
|
|
#endif
|
|
|
|
static int ccci_scp_ipi_send(int md_id, int op_id, void *data)
|
|
{
|
|
int ret = 0;
|
|
#if (MD_GENERATION >= 6297)
|
|
int ipi_status = 0;
|
|
unsigned int cnt = 0;
|
|
#endif
|
|
if (atomic_read(&scp_state) == SCP_CCCI_STATE_INVALID) {
|
|
CCCI_ERROR_LOG(md_id, FSM,
|
|
"ignore IPI %d, SCP state %d!\n",
|
|
op_id, atomic_read(&scp_state));
|
|
return -CCCI_ERR_MD_NOT_READY;
|
|
}
|
|
|
|
mutex_lock(&scp_ipi_tx_mutex);
|
|
memset(&scp_ipi_tx_msg, 0, sizeof(scp_ipi_tx_msg));
|
|
scp_ipi_tx_msg.md_id = md_id;
|
|
scp_ipi_tx_msg.op_id = op_id;
|
|
scp_ipi_tx_msg.data[0] = *((u32 *)data);
|
|
CCCI_NORMAL_LOG(scp_ipi_tx_msg.md_id, FSM,
|
|
"IPI send %d/0x%x, %d\n",
|
|
scp_ipi_tx_msg.op_id, scp_ipi_tx_msg.data[0],
|
|
(int)sizeof(struct ccci_ipi_msg));
|
|
#if (MD_GENERATION >= 6297)
|
|
while (1) {
|
|
ipi_status = mtk_ipi_send(&scp_ipidev, IPI_OUT_APCCCI_0,
|
|
0, &scp_ipi_tx_msg, (sizeof(scp_ipi_tx_msg) / 4), 1);
|
|
if (ipi_status != IPI_PIN_BUSY)
|
|
break;
|
|
cnt++;
|
|
if (cnt > 10) {
|
|
CCCI_ERROR_LOG(md_id, FSM, "IPI send 10 times!\n");
|
|
/* aee_kernel_warning("ccci", "ipi:tx busy");*/
|
|
break;
|
|
}
|
|
}
|
|
if (ipi_status != IPI_ACTION_DONE) {
|
|
CCCI_ERROR_LOG(md_id, FSM, "IPI send fail!\n");
|
|
ret = -CCCI_ERR_MD_NOT_READY;
|
|
}
|
|
#else
|
|
if (scp_ipi_send(IPI_APCCCI, &scp_ipi_tx_msg,
|
|
sizeof(scp_ipi_tx_msg), 1, SCP_A_ID) != SCP_IPI_DONE) {
|
|
CCCI_ERROR_LOG(md_id, FSM, "IPI send fail!\n");
|
|
ret = -CCCI_ERR_MD_NOT_READY;
|
|
}
|
|
#endif
|
|
mutex_unlock(&scp_ipi_tx_mutex);
|
|
return ret;
|
|
}
|
|
|
|
extern struct ccci_fsm_ctl *fsm_get_entity_by_md_id(int md_id);
|
|
static void ccci_scp_md_state_sync_work(struct work_struct *work)
|
|
{
|
|
struct ccci_fsm_scp *scp_ctl = container_of(work,
|
|
struct ccci_fsm_scp, scp_md_state_sync_work);
|
|
struct ccci_fsm_ctl *ctl = fsm_get_entity_by_md_id(scp_ctl->md_id);
|
|
int ret;
|
|
enum MD_STATE_FOR_USER state;
|
|
int count = 0;
|
|
|
|
if (ctl == NULL) {
|
|
CCCI_ERROR_LOG(-1, FSM, "%s ctl is NULL !\n", __func__);
|
|
return;
|
|
}
|
|
|
|
switch (ctl->md_state) {
|
|
case READY:
|
|
switch (scp_ctl->md_id) {
|
|
case MD_SYS1:
|
|
while (count < SCP_BOOT_TIMEOUT/EVENT_POLL_INTEVAL) {
|
|
if (atomic_read(&scp_state) ==
|
|
SCP_CCCI_STATE_BOOTING
|
|
|| atomic_read(&scp_state)
|
|
== SCP_CCCI_STATE_RBREADY
|
|
|| atomic_read(&scp_state)
|
|
== SCP_CCCI_STATE_STOP)
|
|
break;
|
|
count++;
|
|
msleep(EVENT_POLL_INTEVAL);
|
|
}
|
|
if (count == SCP_BOOT_TIMEOUT/EVENT_POLL_INTEVAL)
|
|
CCCI_ERROR_LOG(scp_ctl->md_id, FSM,
|
|
"SCP init not ready!\n");
|
|
else {
|
|
ret = ccci_port_send_msg_to_md(scp_ctl->md_id,
|
|
CCCI_SYSTEM_TX, CCISM_SHM_INIT, 0, 1);
|
|
if (ret < 0)
|
|
CCCI_ERROR_LOG(scp_ctl->md_id, FSM,
|
|
"fail to send CCISM_SHM_INIT %d\n",
|
|
ret);
|
|
}
|
|
break;
|
|
case MD_SYS3:
|
|
ret = ccci_port_send_msg_to_md(scp_ctl->md_id,
|
|
CCCI_CONTROL_TX, C2K_CCISM_SHM_INIT, 0, 1);
|
|
if (ret < 0)
|
|
CCCI_ERROR_LOG(scp_ctl->md_id, CORE,
|
|
"fail to send CCISM_SHM_INIT %d\n",
|
|
ret);
|
|
break;
|
|
};
|
|
break;
|
|
case INVALID:
|
|
case GATED:
|
|
state = MD_STATE_INVALID;
|
|
ccci_scp_ipi_send(scp_ctl->md_id, CCCI_OP_MD_STATE, &state);
|
|
break;
|
|
case EXCEPTION:
|
|
state = MD_STATE_EXCEPTION;
|
|
ccci_scp_ipi_send(scp_ctl->md_id, CCCI_OP_MD_STATE, &state);
|
|
break;
|
|
default:
|
|
break;
|
|
};
|
|
}
|
|
|
|
extern void scp_set_clk_off(void);
|
|
static void ccci_scp_ipi_rx_work(struct work_struct *work)
|
|
{
|
|
struct ccci_ipi_msg *ipi_msg_ptr = NULL;
|
|
struct sk_buff *skb = NULL;
|
|
int data;
|
|
|
|
while (!skb_queue_empty(&scp_ipi_rx_skb_list.skb_list)) {
|
|
skb = ccci_skb_dequeue(&scp_ipi_rx_skb_list);
|
|
if (skb == NULL) {
|
|
CCCI_ERROR_LOG(-1, CORE,
|
|
"ccci_skb_dequeue fail\n");
|
|
return;
|
|
}
|
|
ipi_msg_ptr = (struct ccci_ipi_msg *)skb->data;
|
|
if (!get_modem_is_enabled(ipi_msg_ptr->md_id)) {
|
|
CCCI_ERROR_LOG(ipi_msg_ptr->md_id,
|
|
CORE, "MD not exist\n");
|
|
return;
|
|
}
|
|
switch (ipi_msg_ptr->op_id) {
|
|
case CCCI_OP_SCP_STATE:
|
|
switch (ipi_msg_ptr->data[0]) {
|
|
case SCP_CCCI_STATE_BOOTING:
|
|
if (atomic_read(&scp_state) ==
|
|
SCP_CCCI_STATE_RBREADY) {
|
|
CCCI_NORMAL_LOG(ipi_msg_ptr->md_id, FSM,
|
|
"SCP reset detected\n");
|
|
ccci_port_send_msg_to_md(MD_SYS1,
|
|
CCCI_SYSTEM_TX, CCISM_SHM_INIT, 0, 1);
|
|
ccci_port_send_msg_to_md(MD_SYS3,
|
|
CCCI_CONTROL_TX,
|
|
C2K_CCISM_SHM_INIT, 0, 1);
|
|
} else {
|
|
CCCI_NORMAL_LOG(ipi_msg_ptr->md_id, FSM,
|
|
"SCP boot up\n");
|
|
}
|
|
/* too early to init share memory here,
|
|
* EMI MPU may not be ready yet
|
|
*/
|
|
break;
|
|
case SCP_CCCI_STATE_RBREADY:
|
|
switch (ipi_msg_ptr->md_id) {
|
|
case MD_SYS1:
|
|
ccci_port_send_msg_to_md(MD_SYS1,
|
|
CCCI_SYSTEM_TX,
|
|
CCISM_SHM_INIT_DONE, 0, 1);
|
|
break;
|
|
case MD_SYS3:
|
|
ccci_port_send_msg_to_md(MD_SYS3,
|
|
CCCI_CONTROL_TX,
|
|
C2K_CCISM_SHM_INIT_DONE, 0, 1);
|
|
break;
|
|
};
|
|
data =
|
|
ccci_fsm_get_md_state_for_user(
|
|
ipi_msg_ptr->md_id);
|
|
ccci_scp_ipi_send(ipi_msg_ptr->md_id,
|
|
CCCI_OP_MD_STATE, &data);
|
|
break;
|
|
case SCP_CCCI_STATE_STOP:
|
|
CCCI_NORMAL_LOG(ipi_msg_ptr->md_id, FSM,
|
|
"MD INVALID,scp send ack to ap\n");
|
|
scp_set_clk_off();
|
|
break;
|
|
default:
|
|
break;
|
|
};
|
|
atomic_set(&scp_state, ipi_msg_ptr->data[0]);
|
|
break;
|
|
default:
|
|
break;
|
|
};
|
|
ccci_free_skb(skb);
|
|
}
|
|
}
|
|
|
|
#if (MD_GENERATION >= 6297)
|
|
/*
|
|
* IPI for logger init
|
|
* @param id: IPI id
|
|
* @param prdata: callback function parameter
|
|
* @param data: IPI data
|
|
* @param len: IPI data length
|
|
*/
|
|
static int ccci_scp_ipi_handler(unsigned int id, void *prdata, void *data,
|
|
unsigned int len)
|
|
{
|
|
struct ccci_ipi_msg *ipi_msg_ptr = (struct ccci_ipi_msg *)data;
|
|
struct sk_buff *skb = NULL;
|
|
|
|
if (len != sizeof(struct ccci_ipi_msg)) {
|
|
CCCI_ERROR_LOG(-1, CORE,
|
|
"IPI handler, data length wrong %d vs. %d\n",
|
|
len, (int)sizeof(struct ccci_ipi_msg));
|
|
return -1;
|
|
}
|
|
CCCI_NORMAL_LOG(ipi_msg_ptr->md_id, CORE,
|
|
"IPI handler %d/0x%x, %d\n",
|
|
ipi_msg_ptr->op_id,
|
|
ipi_msg_ptr->data[0], len);
|
|
|
|
skb = ccci_alloc_skb(len, 0, 0);
|
|
if (!skb)
|
|
return -1;
|
|
memcpy(skb_put(skb, len), data, len);
|
|
ccci_skb_enqueue(&scp_ipi_rx_skb_list, skb);
|
|
/* ipi_send use mutex, can not be called from ISR context */
|
|
schedule_work(&scp_ipi_rx_work);
|
|
|
|
return 0;
|
|
}
|
|
#else
|
|
static void ccci_scp_ipi_handler(int id, void *data, unsigned int len)
|
|
{
|
|
struct ccci_ipi_msg *ipi_msg_ptr = (struct ccci_ipi_msg *)data;
|
|
struct sk_buff *skb = NULL;
|
|
|
|
if (len != sizeof(struct ccci_ipi_msg)) {
|
|
CCCI_ERROR_LOG(-1, CORE,
|
|
"IPI handler, data length wrong %d vs. %d\n",
|
|
len, (int)sizeof(struct ccci_ipi_msg));
|
|
return;
|
|
}
|
|
CCCI_NORMAL_LOG(ipi_msg_ptr->md_id, CORE,
|
|
"IPI handler %d/0x%x, %d\n",
|
|
ipi_msg_ptr->op_id,
|
|
ipi_msg_ptr->data[0], len);
|
|
|
|
skb = ccci_alloc_skb(len, 0, 0);
|
|
if (!skb)
|
|
return;
|
|
memcpy(skb_put(skb, len), data, len);
|
|
ccci_skb_enqueue(&scp_ipi_rx_skb_list, skb);
|
|
/* ipi_send use mutex, can not be called from ISR context */
|
|
schedule_work(&scp_ipi_rx_work);
|
|
}
|
|
#endif
|
|
#endif
|
|
|
|
int fsm_ccism_init_ack_handler(int md_id, int data)
|
|
{
|
|
#ifdef FEATURE_SCP_CCCI_SUPPORT
|
|
struct ccci_smem_region *ccism_scp =
|
|
ccci_md_get_smem_by_user_id(md_id, SMEM_USER_CCISM_SCP);
|
|
|
|
memset_io(ccism_scp->base_ap_view_vir, 0, ccism_scp->size);
|
|
ccci_scp_ipi_send(md_id, CCCI_OP_SHM_INIT,
|
|
&ccism_scp->base_ap_view_phy);
|
|
#endif
|
|
return 0;
|
|
}
|
|
|
|
#ifdef CONFIG_MTK_SIM_LOCK_POWER_ON_WRITE_PROTECT
|
|
static int fsm_sim_lock_handler(int md_id, int data)
|
|
{
|
|
fsm_monitor_send_message(md_id, CCCI_MD_MSG_RANDOM_PATTERN, 0);
|
|
return 0;
|
|
}
|
|
#endif
|
|
|
|
static int fsm_sim_type_handler(int md_id, int data)
|
|
{
|
|
struct ccci_per_md *per_md_data = ccci_get_per_md_data(md_id);
|
|
|
|
per_md_data->sim_type = data;
|
|
return 0;
|
|
}
|
|
|
|
#ifdef FEATURE_SCP_CCCI_SUPPORT
|
|
void fsm_scp_init0(void)
|
|
{
|
|
mutex_init(&scp_ipi_tx_mutex);
|
|
CCCI_NORMAL_LOG(-1, FSM, "register IPI\n");
|
|
|
|
#if (MD_GENERATION >= 6297)
|
|
if (mtk_ipi_register(&scp_ipidev, IPI_IN_APCCCI_0,
|
|
(void *)ccci_scp_ipi_handler, NULL,
|
|
&scp_ipi_rx_msg) != IPI_ACTION_DONE)
|
|
CCCI_ERROR_LOG(-1, FSM, "register IPI fail!\n");
|
|
#else
|
|
if (scp_ipi_registration(IPI_APCCCI, ccci_scp_ipi_handler,
|
|
"AP CCCI") != SCP_IPI_DONE)
|
|
CCCI_ERROR_LOG(-1, FSM, "register IPI fail!\n");
|
|
#endif
|
|
if (!init_work_done) {
|
|
INIT_WORK(&scp_ipi_rx_work, ccci_scp_ipi_rx_work);
|
|
init_work_done = 1;
|
|
}
|
|
init_waitqueue_head(&scp_ipi_rx_wq);
|
|
ccci_skb_queue_init(&scp_ipi_rx_skb_list, 16, 16, 0);
|
|
atomic_set(&scp_state, SCP_CCCI_STATE_BOOTING);
|
|
}
|
|
EXPORT_SYMBOL(fsm_scp_init0);
|
|
#endif
|
|
|
|
int fsm_scp_init(struct ccci_fsm_scp *scp_ctl)
|
|
{
|
|
struct ccci_fsm_ctl *ctl =
|
|
container_of(scp_ctl, struct ccci_fsm_ctl, scp_ctl);
|
|
int ret = 0;
|
|
|
|
scp_ctl->md_id = ctl->md_id;
|
|
#ifdef FEATURE_SCP_CCCI_SUPPORT
|
|
INIT_WORK(&scp_ctl->scp_md_state_sync_work,
|
|
ccci_scp_md_state_sync_work);
|
|
register_ccci_sys_call_back(scp_ctl->md_id, CCISM_SHM_INIT_ACK,
|
|
fsm_ccism_init_ack_handler);
|
|
#endif
|
|
#ifdef CONFIG_MTK_SIM_LOCK_POWER_ON_WRITE_PROTECT
|
|
register_ccci_sys_call_back(scp_ctl->md_id, SIM_LOCK_RANDOM_PATTERN,
|
|
fsm_sim_lock_handler);
|
|
#endif
|
|
register_ccci_sys_call_back(scp_ctl->md_id, MD_SIM_TYPE,
|
|
fsm_sim_type_handler);
|
|
|
|
return ret;
|
|
}
|
|
|