// SPDX-License-Identifier: GPL-2.0 /* * Copyright (c) 2019 MediaTek Inc. */ #include #include #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< 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< 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; } }