381 lines
9.8 KiB
C
381 lines
9.8 KiB
C
// SPDX-License-Identifier: GPL-2.0
|
|
/*
|
|
* Copyright (c) 2019 MediaTek Inc.
|
|
*/
|
|
|
|
#include <linux/mutex.h>
|
|
#include <linux/io.h>
|
|
#include "scp_ipi.h"
|
|
#include "scp_helper.h"
|
|
#include "scp_excep.h"
|
|
|
|
#define PRINT_THRESHOLD 10000
|
|
enum ipi_id scp_ipi_id_record;
|
|
enum ipi_id scp_ipi_mutex_owner[SCP_CORE_TOTAL];
|
|
enum ipi_id scp_ipi_owner[SCP_CORE_TOTAL];
|
|
|
|
unsigned int scp_ipi_id_record_count;
|
|
unsigned int scp_to_ap_ipi_count;
|
|
unsigned int ap_to_scp_ipi_count;
|
|
|
|
struct scp_ipi_desc scp_ipi_desc[SCP_NR_IPI];
|
|
struct share_obj *scp_send_obj[SCP_CORE_TOTAL];
|
|
struct share_obj *scp_rcv_obj[SCP_CORE_TOTAL];
|
|
struct mutex scp_ipi_mutex[SCP_CORE_TOTAL];
|
|
/*
|
|
* find an ipi handler and invoke it
|
|
*/
|
|
void scp_A_ipi_handler(void)
|
|
{
|
|
#if SCP_IPI_STAMP_SUPPORT
|
|
unsigned int flag = 0;
|
|
#endif
|
|
enum ipi_id scp_id;
|
|
|
|
scp_id = scp_rcv_obj[SCP_A_ID]->id;
|
|
/*pr_debug("scp A ipi handler %d\n", scp_id);*/
|
|
if (scp_id >= SCP_NR_IPI || scp_id <= 0) {
|
|
/* ipi id abnormal*/
|
|
pr_debug("[SCP] A ipi handler id abnormal, id=%d\n", scp_id);
|
|
} else if (scp_ipi_desc[scp_id].handler) {
|
|
memcpy_from_scp(scp_recv_buff[SCP_A_ID],
|
|
(void *)scp_rcv_obj[SCP_A_ID]->share_buf,
|
|
scp_rcv_obj[SCP_A_ID]->len);
|
|
|
|
scp_ipi_desc[scp_id].recv_count++;
|
|
scp_to_ap_ipi_count++;
|
|
#if SCP_IPI_STAMP_SUPPORT
|
|
flag = scp_ipi_desc[scp_id].recv_count % SCP_IPI_ID_STAMP_SIZE;
|
|
if (flag < SCP_IPI_ID_STAMP_SIZE) {
|
|
scp_ipi_desc[scp_id].recv_flag[flag] =
|
|
scp_ipi_desc[scp_id].recv_count;
|
|
scp_ipi_desc[scp_id].handler_timestamp[flag] = 0;
|
|
scp_ipi_desc[scp_id].recv_timestamp[flag] =
|
|
arch_counter_get_cntvct();
|
|
}
|
|
#endif
|
|
scp_ipi_desc[scp_id].handler(scp_id, scp_recv_buff[SCP_A_ID],
|
|
scp_rcv_obj[SCP_A_ID]->len);
|
|
#if SCP_IPI_STAMP_SUPPORT
|
|
if (flag < SCP_IPI_ID_STAMP_SIZE)
|
|
scp_ipi_desc[scp_id].handler_timestamp[flag] =
|
|
arch_counter_get_cntvct();
|
|
#endif
|
|
/* After SCP IPI handler,
|
|
* send a awake ipi to avoid
|
|
* SCP keeping in ipi busy idle state
|
|
*/
|
|
/* set a direct IPI to awake SCP */
|
|
writel((1 << SCP_A_IPI_AWAKE_NUM), SCP_GIPC_IN_REG);
|
|
} else {
|
|
/* scp_ipi_handler is null or ipi id abnormal */
|
|
pr_debug("[SCP] A ipi handler is null or abnormal, id=%d\n"
|
|
, scp_id);
|
|
}
|
|
/* AP side write 1 to clear SCP to SPM reg.
|
|
* scp side write 1 to set SCP to SPM reg.
|
|
* scp set bit[0]
|
|
*/
|
|
writel(0x1, SCP_TO_SPM_REG);
|
|
|
|
/*pr_debug("scp_ipi_handler done\n");*/
|
|
}
|
|
|
|
/*
|
|
* ipi initialize
|
|
*/
|
|
void scp_A_ipi_init(void)
|
|
{
|
|
#if SCP_IPI_STAMP_SUPPORT
|
|
int j = 0;
|
|
#endif
|
|
|
|
mutex_init(&scp_ipi_mutex[SCP_A_ID]);
|
|
scp_rcv_obj[SCP_A_ID] = SCP_A_SHARE_BUFFER;
|
|
scp_send_obj[SCP_A_ID] = scp_rcv_obj[SCP_A_ID] + 1;
|
|
pr_debug("[SCP] scp_rcv_obj[A] = 0x%p\n", scp_rcv_obj[SCP_A_ID]);
|
|
pr_debug("[SCP] scp_send_obj[A] = 0x%p\n", scp_send_obj[SCP_A_ID]);
|
|
memset_io(scp_send_obj[SCP_A_ID], 0, SHARE_BUF_SIZE);
|
|
scp_to_ap_ipi_count = 0;
|
|
ap_to_scp_ipi_count = 0;
|
|
}
|
|
|
|
|
|
/*
|
|
* API let apps can register an ipi handler to receive IPI
|
|
* @param id: IPI ID
|
|
* @param handler: IPI handler
|
|
* @param name: IPI name
|
|
*/
|
|
enum scp_ipi_status scp_ipi_registration(enum ipi_id id,
|
|
void (*ipi_handler)(int id, void *data, unsigned int len),
|
|
const char *name)
|
|
{
|
|
if (id < SCP_NR_IPI) {
|
|
scp_ipi_desc[id].name = name;
|
|
|
|
if (ipi_handler == NULL)
|
|
return SCP_IPI_ERROR;
|
|
|
|
scp_ipi_desc[id].handler = ipi_handler;
|
|
return SCP_IPI_DONE;
|
|
} else {
|
|
return SCP_IPI_ERROR;
|
|
}
|
|
}
|
|
EXPORT_SYMBOL_GPL(scp_ipi_registration);
|
|
|
|
/*
|
|
* API let apps unregister an ipi handler
|
|
* @param id: IPI ID
|
|
*/
|
|
enum scp_ipi_status scp_ipi_unregistration(enum ipi_id id)
|
|
{
|
|
if (id < SCP_NR_IPI) {
|
|
scp_ipi_desc[id].name = "";
|
|
scp_ipi_desc[id].handler = NULL;
|
|
return SCP_IPI_DONE;
|
|
} else {
|
|
return SCP_IPI_ERROR;
|
|
}
|
|
}
|
|
EXPORT_SYMBOL_GPL(scp_ipi_unregistration);
|
|
|
|
/*
|
|
* API for apps to send an IPI to scp
|
|
* @param id: IPI ID
|
|
* @param buf: the pointer of data
|
|
* @param len: data length
|
|
* @param wait: If true, wait (atomically) until data have been gotten by Host
|
|
* @param len: data length
|
|
*/
|
|
enum scp_ipi_status scp_ipi_send(enum ipi_id id, void *buf,
|
|
unsigned int len, unsigned int wait, enum scp_core_id scp_id)
|
|
{
|
|
#if SCP_IPI_STAMP_SUPPORT
|
|
unsigned long flag = 0;
|
|
#endif
|
|
/* the variable is for reading back the id from sram
|
|
* to check the if the sram is ready for accesses.
|
|
*/
|
|
enum ipi_id rb_id;
|
|
|
|
/*avoid scp log print too much*/
|
|
if (scp_ipi_id_record == id)
|
|
scp_ipi_id_record_count++;
|
|
else
|
|
scp_ipi_id_record_count = 0;
|
|
|
|
scp_ipi_id_record = id;
|
|
|
|
if (scp_id >= SCP_CORE_TOTAL) {
|
|
pr_err("[SCP] %s: scp_id:%d wrong\n", __func__, scp_id);
|
|
scp_ipi_desc[id].error_count++;
|
|
return SCP_IPI_ERROR;
|
|
}
|
|
|
|
if (in_interrupt()) {
|
|
if (wait) {
|
|
pr_err("[SCP] %s: cannot use in isr\n", __func__);
|
|
scp_ipi_desc[id].error_count++;
|
|
return SCP_IPI_ERROR;
|
|
}
|
|
}
|
|
|
|
if (id >= SCP_NR_IPI) {
|
|
pr_err("[SCP] %s: ipi id %d wrong\n", __func__, id);
|
|
return SCP_IPI_ERROR;
|
|
}
|
|
if (is_scp_ready(scp_id) == 0) {
|
|
/* pr_err("[SCP] %s: %s not enabled, id=%d\n", __func__
|
|
* , core_ids[scp_id], id);
|
|
*/
|
|
pr_notice("[SCP] %s: %s not ready\n", __func__,
|
|
core_ids[scp_id]);
|
|
scp_ipi_desc[id].error_count++;
|
|
return SCP_IPI_ERROR;
|
|
}
|
|
if (len > sizeof(scp_send_obj[scp_id]->share_buf) || buf == NULL) {
|
|
pr_err("[SCP] %s: %s buffer error\n", __func__,
|
|
core_ids[scp_id]);
|
|
scp_ipi_desc[id].error_count++;
|
|
return SCP_IPI_ERROR;
|
|
}
|
|
#if SCP_RECOVERY_SUPPORT
|
|
if (atomic_read(&scp_reset_status) == RESET_STATUS_START) {
|
|
pr_notice("[SCP] %s: %s reset start\n", __func__,
|
|
core_ids[scp_id]);
|
|
scp_ipi_desc[id].error_count++;
|
|
return SCP_IPI_ERROR;
|
|
}
|
|
#endif
|
|
if (mutex_trylock(&scp_ipi_mutex[scp_id]) == 0) {
|
|
/*avoid scp ipi send log print too much*/
|
|
if ((scp_ipi_id_record_count % PRINT_THRESHOLD == 0) ||
|
|
(scp_ipi_id_record_count % PRINT_THRESHOLD == 1)) {
|
|
pr_err("[SCP] %s:%s %d mutex busy, owner=%d\n",
|
|
__func__, core_ids[scp_id], id,
|
|
scp_ipi_mutex_owner[scp_id]);
|
|
}
|
|
scp_ipi_desc[id].busy_count++;
|
|
return SCP_IPI_BUSY;
|
|
}
|
|
|
|
/* keep scp awake for sram copy*/
|
|
if (scp_awake_lock(scp_id) == -1) {
|
|
mutex_unlock(&scp_ipi_mutex[scp_id]);
|
|
pr_err("[SCP] %s: %s ipi error, awake scp fail\n", __func__,
|
|
core_ids[scp_id]);
|
|
scp_ipi_desc[id].error_count++;
|
|
return SCP_IPI_ERROR;
|
|
}
|
|
|
|
/*get scp ipi mutex owner*/
|
|
scp_ipi_mutex_owner[scp_id] = id;
|
|
|
|
if ((readl(SCP_GIPC_IN_REG) & (1<<scp_id)) > 0) {
|
|
/*avoid scp ipi send log print too much*/
|
|
if ((scp_ipi_id_record_count % PRINT_THRESHOLD == 0) ||
|
|
(scp_ipi_id_record_count % PRINT_THRESHOLD == 1)) {
|
|
pr_err("[SCP] %s:%s %d ap->scp busy,last time=%d\n",
|
|
__func__, core_ids[scp_id], id,
|
|
scp_ipi_owner[scp_id]);
|
|
scp_A_dump_regs();
|
|
}
|
|
if (scp_awake_unlock(scp_id) == -1)
|
|
pr_debug("[SCP] %s: ap->scp busy awake unlock -1\n",
|
|
__func__);
|
|
scp_ipi_desc[id].busy_count++;
|
|
mutex_unlock(&scp_ipi_mutex[scp_id]);
|
|
return SCP_IPI_BUSY;
|
|
}
|
|
/*get scp ipi send owner*/
|
|
scp_ipi_owner[scp_id] = id;
|
|
|
|
memcpy(scp_send_buff[scp_id], buf, len);
|
|
memcpy_to_scp((void *)scp_send_obj[scp_id]->share_buf,
|
|
scp_send_buff[scp_id], len);
|
|
|
|
scp_send_obj[scp_id]->len = len;
|
|
scp_send_obj[scp_id]->id = id;
|
|
|
|
/*
|
|
* read the value back to guarantee that scp's sram is ready.
|
|
*/
|
|
rb_id = readl(&(scp_send_obj[scp_id]->id));
|
|
if (rb_id != id) {
|
|
pr_debug("[SCP]ERR: write/read id failed, %d, %d\n", id, rb_id);
|
|
WARN_ON(1);
|
|
}
|
|
|
|
dsb(SY);
|
|
/*record timestamp*/
|
|
scp_ipi_desc[id].success_count++;
|
|
ap_to_scp_ipi_count++;
|
|
|
|
#if SCP_IPI_STAMP_SUPPORT
|
|
flag = scp_ipi_desc[id].success_count % SCP_IPI_ID_STAMP_SIZE;
|
|
if (flag < SCP_IPI_ID_STAMP_SIZE) {
|
|
scp_ipi_desc[id].send_flag[flag] =
|
|
scp_ipi_desc[id].success_count;
|
|
scp_ipi_desc[id].send_timestamp[flag] =
|
|
arch_counter_get_cntvct();
|
|
}
|
|
#endif
|
|
/* toggle the related bit to trigger an interrupt to scp */
|
|
writel((1<<scp_id), SCP_GIPC_IN_REG);
|
|
|
|
if (wait)
|
|
while ((readl(SCP_GIPC_IN_REG) & (1<<scp_id)) > 0)
|
|
;
|
|
if (scp_awake_unlock(scp_id) == -1)
|
|
pr_debug("[SCP] %s: awake unlock fail\n", __func__);
|
|
|
|
/* scp is interrupted, so unlock mutex to let other users in */
|
|
mutex_unlock(&scp_ipi_mutex[scp_id]);
|
|
|
|
return SCP_IPI_DONE;
|
|
}
|
|
EXPORT_SYMBOL_GPL(scp_ipi_send);
|
|
|
|
void scp_ipi_info_dump(enum ipi_id id)
|
|
{
|
|
pr_debug("%u\t%u\t%u\t%u\t%u\t%s\n\r",
|
|
id,
|
|
scp_ipi_desc[id].recv_count,
|
|
scp_ipi_desc[id].success_count,
|
|
scp_ipi_desc[id].busy_count,
|
|
scp_ipi_desc[id].error_count,
|
|
scp_ipi_desc[id].name);
|
|
#if SCP_IPI_STAMP_SUPPORT
|
|
/*time stamp*/
|
|
for (i = 0; i < SCP_IPI_ID_STAMP_SIZE; i++) {
|
|
if (scp_ipi_desc[id].recv_timestamp[i] != 0) {
|
|
pr_debug("[SCP]scp->ap recv count:%u, ap recv:%llu, handler fin:%llu\n",
|
|
scp_ipi_desc[id].recv_flag[i],
|
|
scp_ipi_desc[id].recv_timestamp[i],
|
|
scp_ipi_desc[id].handler_timestamp[i]);
|
|
}
|
|
}
|
|
for (i = 0; i < SCP_IPI_ID_STAMP_SIZE; i++) {
|
|
if (scp_ipi_desc[id].send_timestamp[i] != 0) {
|
|
pr_debug("ap->scp send count:%u send time:%llu\n",
|
|
scp_ipi_desc[id].send_flag[i],
|
|
scp_ipi_desc[id].send_timestamp[i]);
|
|
}
|
|
}
|
|
#endif
|
|
|
|
}
|
|
|
|
void scp_ipi_status_dump_id(enum ipi_id id)
|
|
{
|
|
#if SCP_IPI_STAMP_SUPPORT
|
|
/*time stamp*/
|
|
unsigned int i;
|
|
#endif
|
|
|
|
pr_debug("[SCP]id\trecv\tsuccess\tbusy\terror\tname\n\r");
|
|
scp_ipi_info_dump(id);
|
|
|
|
}
|
|
|
|
void scp_ipi_status_dump(void)
|
|
{
|
|
enum ipi_id id;
|
|
#if SCP_IPI_STAMP_SUPPORT
|
|
/*time stamp*/
|
|
unsigned int i;
|
|
#endif
|
|
|
|
pr_debug("[SCP]id\trecv\tsuccess\tbusy\terror\tname\n\r");
|
|
for (id = 0; id < SCP_NR_IPI; id++) {
|
|
if (scp_ipi_desc[id].recv_count > 0 ||
|
|
scp_ipi_desc[id].success_count > 0 ||
|
|
scp_ipi_desc[id].busy_count > 0 ||
|
|
scp_ipi_desc[id].error_count > 0)
|
|
scp_ipi_info_dump(id);
|
|
}
|
|
pr_debug("ap->scp total=%u scp->ap total=%u\n\r",
|
|
ap_to_scp_ipi_count,
|
|
scp_to_ap_ipi_count);
|
|
}
|
|
|
|
void mt_print_scp_ipi_id(void)
|
|
{
|
|
enum ipi_id id = scp_rcv_obj[0]->id;
|
|
unsigned char *buf = scp_rcv_obj[0]->share_buf;
|
|
uint16_t *ipi_count = (uint16_t *)scp_rcv_obj[0]->reserve;
|
|
|
|
switch (id) {
|
|
case IPI_SENSOR:
|
|
pr_info("[SCP] ipi(%d) id/type/action/event/reserve = %d/%d/%d/%d/%d\n",
|
|
*ipi_count, id, buf[0], buf[1], buf[2], buf[3]);
|
|
break;
|
|
default:
|
|
pr_info("[SCP] ipi id = %d\n", id);
|
|
break;
|
|
}
|
|
}
|
|
|