summaryrefslogtreecommitdiff
path: root/chip/mt_scp/rv32i_common/ipi.c
diff options
context:
space:
mode:
Diffstat (limited to 'chip/mt_scp/rv32i_common/ipi.c')
-rw-r--r--chip/mt_scp/rv32i_common/ipi.c184
1 files changed, 184 insertions, 0 deletions
diff --git a/chip/mt_scp/rv32i_common/ipi.c b/chip/mt_scp/rv32i_common/ipi.c
new file mode 100644
index 0000000000..462865be83
--- /dev/null
+++ b/chip/mt_scp/rv32i_common/ipi.c
@@ -0,0 +1,184 @@
+/* Copyright 2020 The Chromium OS Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "atomic.h"
+#include "cache.h"
+#include "common.h"
+#include "console.h"
+#include "hooks.h"
+#include "hostcmd.h"
+#include "ipi_chip.h"
+#include "registers.h"
+#include "system.h"
+#include "task.h"
+#include "util.h"
+
+#define CPRINTF(format, args...) cprintf(CC_IPI, format, ##args)
+#define CPRINTS(format, args...) cprints(CC_IPI, format, ##args)
+
+static uint8_t init_done;
+
+static struct mutex ipi_lock;
+static struct ipc_shared_obj *const ipi_send_buf =
+ (struct ipc_shared_obj *)CONFIG_IPC_SHARED_OBJ_ADDR;
+static struct ipc_shared_obj *const ipi_recv_buf =
+ (struct ipc_shared_obj *)(CONFIG_IPC_SHARED_OBJ_ADDR +
+ sizeof(struct ipc_shared_obj));
+
+static uint32_t disable_irq_count, saved_int_mask;
+
+void ipi_disable_irq(void)
+{
+ if (atomic_read_add(&disable_irq_count, 1) == 0)
+ saved_int_mask = read_clear_int_mask();
+}
+
+void ipi_enable_irq(void)
+{
+ if (atomic_read_sub(&disable_irq_count, 1) == 1)
+ set_int_mask(saved_int_mask);
+}
+
+static int ipi_is_busy(void)
+{
+ return SCP_SCP2APMCU_IPC_SET & IPC_SCP2HOST;
+}
+
+static void ipi_wake_ap(int32_t id)
+{
+ if (id >= IPI_COUNT)
+ return;
+
+ if (*ipi_wakeup_table[id])
+ SCP_SCP2SPM_IPC_SET = IPC_SCP2HOST;
+}
+
+int ipi_send(int32_t id, const void *buf, uint32_t len, int wait)
+{
+ int ret;
+
+ if (!init_done) {
+ CPRINTS("IPI has not initialized");
+ return EC_ERROR_BUSY;
+ }
+
+ if (in_interrupt_context()) {
+ CPRINTS("invoke %s() in ISR context", __func__);
+ return EC_ERROR_BUSY;
+ }
+
+ if (len > sizeof(ipi_send_buf->buffer)) {
+ CPRINTS("data length exceeds limitation");
+ return EC_ERROR_INVAL;
+ }
+
+ ipi_disable_irq();
+ mutex_lock(&ipi_lock);
+
+ if (ipi_is_busy()) {
+ /*
+ * If the following conditions meet,
+ * 1) There is an IPI pending in AP.
+ * 2) The incoming IPI is a wakeup IPI.
+ * then it assumes that AP is in suspend state.
+ * Send a AP wakeup request to SPM.
+ *
+ * The incoming IPI will be checked if it's a wakeup source.
+ */
+ ipi_wake_ap(id);
+
+ CPRINTS("IPI busy, id=%d", id);
+ ret = EC_ERROR_BUSY;
+ goto error;
+ }
+
+ ipi_send_buf->id = id;
+ ipi_send_buf->len = len;
+ memcpy(ipi_send_buf->buffer, buf, len);
+
+ /* flush memory cache (if any) */
+ cache_flush_dcache_range((uintptr_t)ipi_send_buf,
+ sizeof(*ipi_send_buf));
+
+ /* interrupt AP to handle the message */
+ ipi_wake_ap(id);
+ SCP_SCP2APMCU_IPC_SET = IPC_SCP2HOST;
+
+ if (wait)
+ while (ipi_is_busy())
+ ;
+
+ ret = EC_SUCCESS;
+error:
+ mutex_unlock(&ipi_lock);
+ ipi_enable_irq();
+ return ret;
+}
+
+static void ipi_enable_deferred(void)
+{
+ struct scp_run_t scp_run;
+ int ret;
+
+ init_done = 1;
+
+ /* inform AP that SCP is up */
+ scp_run.signaled = 1;
+ strncpy(scp_run.fw_ver, system_get_version(EC_IMAGE_RW),
+ SCP_FW_VERSION_LEN);
+ scp_run.dec_capability = VCODEC_CAPABILITY_4K_DISABLED | VDEC_CAP_MM21 | VDEC_CAP_H264_SLICE |
+ VDEC_CAP_VP8_FRAME | VDEC_CAP_VP9_FRAME;
+ scp_run.enc_capability = VENC_CAP_4K;
+
+ ret = ipi_send(SCP_IPI_INIT, (void *)&scp_run, sizeof(scp_run), 1);
+ if (ret) {
+ CPRINTS("failed to send initialization IPC messages");
+ init_done = 0;
+ return;
+ }
+
+#ifdef HAS_TASK_HOSTCMD
+ hostcmd_init();
+#endif
+
+ task_enable_irq(SCP_IRQ_GIPC_IN0);
+}
+DECLARE_DEFERRED(ipi_enable_deferred);
+
+static void ipi_init(void)
+{
+ memset(ipi_send_buf, 0, sizeof(struct ipc_shared_obj));
+ memset(ipi_recv_buf, 0, sizeof(struct ipc_shared_obj));
+
+ /* enable IRQ after all tasks are up */
+ hook_call_deferred(&ipi_enable_deferred_data, 0);
+}
+DECLARE_HOOK(HOOK_INIT, ipi_init, HOOK_PRIO_DEFAULT);
+
+static void ipi_handler(void)
+{
+ if (ipi_recv_buf->id >= IPI_COUNT) {
+ CPRINTS("invalid IPI, id=%d", ipi_recv_buf->id);
+ return;
+ }
+
+ CPRINTS("IPI %d", ipi_recv_buf->id);
+
+ ipi_handler_table[ipi_recv_buf->id](
+ ipi_recv_buf->id, ipi_recv_buf->buffer, ipi_recv_buf->len);
+}
+
+static void irq_group7_handler(void)
+{
+ extern volatile int ec_int;
+
+ if (SCP_GIPC_IN_SET & GIPC_IN(0)) {
+ ipi_handler();
+ SCP_GIPC_IN_CLR = GIPC_IN(0);
+ asm volatile ("fence.i" ::: "memory");
+ task_clear_pending_irq(ec_int);
+ }
+}
+DECLARE_IRQ(7, irq_group7_handler, 0);