summaryrefslogtreecommitdiff
path: root/chip
diff options
context:
space:
mode:
authorYilun Lin <yllin@google.com>2018-11-21 14:22:42 +0800
committerchrome-bot <chrome-bot@chromium.org>2018-12-29 05:45:28 -0800
commitc7804fd61b3bacf29cb4f1da9483860435ecac20 (patch)
tree4c47beee640868f19107a057d3df1a3d21732791 /chip
parent02983ae05319d71bf8b29e063bf40acfd53b1e02 (diff)
downloadchrome-ec-c7804fd61b3bacf29cb4f1da9483860435ecac20.tar.gz
mt_scp: Support inter-process interrupt/communication (IPI).
This CL enables the IPI/IPC functions in mt_scp on MTK SOC. TEST=Run ec.RW.bin on kukui, and see EC version string in AP console: remoteproc remoteproc0: powering up scp remoteproc remoteproc0: Booting fw image scp.img, size 29800 mtk-scp 10500000.scp: scp is ready. kukui_scp_v2.0.519+164255084 BRANCH=None BUG=b:117917141, b:120172001, b:120953723 Change-Id: I2a43aee13141535bf71f839cf9e6cc0460b65844 Signed-off-by: Yilun Lin <yllin@google.com> Reviewed-on: https://chromium-review.googlesource.com/1351924 Commit-Ready: Nicolas Boichat <drinkcat@chromium.org> Tested-by: Yilun Lin <yllin@chromium.org> Reviewed-by: Nicolas Boichat <drinkcat@chromium.org>
Diffstat (limited to 'chip')
-rw-r--r--chip/mt_scp/build.mk1
-rw-r--r--chip/mt_scp/config_chip.h6
-rw-r--r--chip/mt_scp/ipi.c179
-rw-r--r--chip/mt_scp/ipi_chip.h82
-rw-r--r--chip/mt_scp/ipi_table.c87
-rw-r--r--chip/mt_scp/registers.h11
-rw-r--r--chip/mt_scp/system.c6
7 files changed, 368 insertions, 4 deletions
diff --git a/chip/mt_scp/build.mk b/chip/mt_scp/build.mk
index 5729de35a7..dac5e3dfcb 100644
--- a/chip/mt_scp/build.mk
+++ b/chip/mt_scp/build.mk
@@ -15,5 +15,6 @@ chip-y=clock.o gpio.o stepping_stone.o system.o uart.o
# Optional chip modules
chip-$(CONFIG_COMMON_TIMER)+=hrtimer.o
chip-$(CONFIG_I2C)+=i2c.o
+chip-$(CONFIG_IPI)+=ipi.o ipi_table.o
chip-$(CONFIG_SPI)+=spi.o
chip-$(CONFIG_WATCHDOG)+=watchdog.o
diff --git a/chip/mt_scp/config_chip.h b/chip/mt_scp/config_chip.h
index 2f982246b2..cc6a95af0a 100644
--- a/chip/mt_scp/config_chip.h
+++ b/chip/mt_scp/config_chip.h
@@ -74,6 +74,12 @@
#define LARGER_TASK_STACK_SIZE 640
#define VENTI_TASK_STACK_SIZE 768
+/* IPI */
+#define CONFIG_IPC_SHARED_OBJ_BUF_SIZE 288
+#define CONFIG_IPC_SHARED_OBJ_ADDR \
+ (CONFIG_RAM_BASE - \
+ (CONFIG_IPC_SHARED_OBJ_BUF_SIZE + 2 * sizeof(int32_t)) * 2)
+
#define CONFIG_CHIP_PRE_INIT
#define GPIO_PIN(num) ((num) / 32), ((num) % 32)
diff --git a/chip/mt_scp/ipi.c b/chip/mt_scp/ipi.c
new file mode 100644
index 0000000000..91e46740c9
--- /dev/null
+++ b/chip/mt_scp/ipi.c
@@ -0,0 +1,179 @@
+/* Copyright 2018 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.
+ *
+ * Inter-Processor Communication (IPC) and Inter-Processor Interrupt (IPI)
+ *
+ * IPC is a communication bridge between AP and SCP. AP/SCP sends an IPC
+ * interrupt to SCP/AP to inform to collect the commmunication mesesages in the
+ * shared buffer.
+ *
+ * There are 4 IPCs in the current architecture, from IPC0 to IPC3. The
+ * priority of IPC is proportional to its IPC index. IPC3 has the highest
+ * priority and IPC0 has the lowest one.
+ *
+ * IPC0 may contain zero or more IPIs. Each IPI represents a task or a service,
+ * e.g. host command, or video encoding. IPIs are recognized by IPI ID, which
+ * should sync across AP and SCP. Shared buffer should designated which IPI
+ * ID it talks to.
+ *
+ * Currently, we don't have IPC handlers for IPC1, IPC2, and IPC3.
+ */
+
+#include "console.h"
+#include "hooks.h"
+#include "host_command.h"
+#include "ipi_chip.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)
+
+#define IPI_MAX_REQUEST_SIZE CONFIG_IPC_SHARED_OBJ_BUF_SIZE
+#define IPI_MAX_RESPONSE_SIZE CONFIG_IPC_SHARED_OBJ_BUF_SIZE
+
+static struct mutex ipi_lock;
+/* IPC0 shared objects, including send object and receive object. */
+static struct ipc_shared_obj *const scp_send_obj =
+ (struct ipc_shared_obj *)CONFIG_IPC_SHARED_OBJ_ADDR;
+static struct ipc_shared_obj *const scp_recv_obj =
+ (struct ipc_shared_obj *)(CONFIG_IPC_SHARED_OBJ_ADDR +
+ sizeof(struct ipc_shared_obj));
+
+/* Check if SCP to AP IPI is in use. */
+static inline int is_ipi_busy(void)
+{
+ return SCP_HOST_INT & IPC_SCP2HOST_BIT;
+}
+
+/* If IPI is declared as a wake-up source, wake AP up. */
+static inline void try_to_wakeup_ap(int32_t id)
+{
+ if (*ipi_wakeup_table[id])
+ SCP_SPM_INT = SPM_INT_A2SPM;
+}
+
+/* Send data from SCP to AP. */
+int ipi_send(int32_t id, void *buf, uint32_t len, int wait)
+{
+ /*
+ * TODO(b:117917141): Evaluate if we can remove this once we have the
+ * video/camera feature code base.
+ */
+ if (wait && in_interrupt_context())
+ /* Prevent from infinity wait when be in ISR context. */
+ return EC_ERROR_BUSY;
+
+ if (len > sizeof(scp_send_obj->buffer))
+ return EC_ERROR_INVAL;
+
+ task_disable_irq(SCP_IRQ_IPC0);
+ mutex_lock(&ipi_lock);
+
+ /* Check if there is already an IPI pending in AP. */
+ if (is_ipi_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.
+ */
+ try_to_wakeup_ap(id);
+
+ mutex_unlock(&ipi_lock);
+ task_enable_irq(SCP_IRQ_IPC0);
+
+ return EC_ERROR_BUSY;
+ }
+
+
+ scp_send_obj->id = id;
+ scp_send_obj->len = len;
+ memcpy(scp_send_obj->buffer, buf, len);
+
+ /* Send IPI to AP: interrutp AP to receive IPI messages. */
+ try_to_wakeup_ap(id);
+ SCP_HOST_INT = IPC_SCP2HOST_BIT;
+
+ while (wait && is_ipi_busy())
+ ;
+
+ mutex_unlock(&ipi_lock);
+ task_enable_irq(SCP_IRQ_IPC0);
+
+ return EC_SUCCESS;
+}
+
+static void ipi_handler(void)
+{
+ if (scp_recv_obj->id >= IPI_COUNT) {
+ CPRINTS("#ERR IPI %d", scp_recv_obj->id);
+ return;
+ }
+
+ /*
+ * Pass the buffer to handler. Each handler should be in charge of
+ * the buffer copying/reading before returning from handler.
+ */
+ ipi_handler_table[scp_recv_obj->id](
+ scp_recv_obj->id, scp_recv_obj->buffer, scp_recv_obj->len);
+}
+
+void ipi_inform_ap(void)
+{
+ struct scp_run_t scp_run;
+ int ret;
+
+ scp_run.signaled = 1;
+ strncpy(scp_run.fw_ver, system_get_version(SYSTEM_IMAGE_RW),
+ SCP_FW_VERSION_LEN);
+ scp_run.dec_capability = 0;
+ scp_run.enc_capability = 0;
+
+ ret = ipi_send(IPI_SCP_INIT, (void *)&scp_run, sizeof(scp_run), 1);
+
+ if (ret)
+ ccprintf("Failed to send initialization IPC messages.\n");
+}
+
+static void ipi_enable_ipc0_deferred(void)
+{
+ /* Clear IPC0 IRQs. */
+ SCP_GIPC_IN = SCP_GPIC_IN_CLEAR_ALL;
+
+ /* All tasks are up, we can safely enable IPC0 IRQ now. */
+ SCP_INTC_IRQ_ENABLE |= IPC0_IRQ_EN;
+ task_enable_irq(SCP_IRQ_IPC0);
+
+ /* Inform AP that SCP is inited. */
+ ipi_inform_ap();
+
+ CPRINTS("ipi init");
+}
+DECLARE_DEFERRED(ipi_enable_ipc0_deferred);
+
+/* Initialize IPI. */
+static void ipi_init(void)
+{
+ /* Clear send share buffer. */
+ memset(scp_send_obj, 0, sizeof(struct ipc_shared_obj));
+
+ /* Enable IRQ after all tasks are up. */
+ hook_call_deferred(&ipi_enable_ipc0_deferred_data, 0);
+}
+DECLARE_HOOK(HOOK_INIT, ipi_init, HOOK_PRIO_DEFAULT);
+
+void ipc_handler(void)
+{
+ /* TODO(b/117917141): We only support IPC_ID(0) for now. */
+ if (SCP_GIPC_IN & SCP_GIPC_IN_CLEAR_IPCN(0))
+ ipi_handler();
+
+ SCP_GIPC_IN = SCP_GPIC_IN_CLEAR_ALL;
+}
+DECLARE_IRQ(SCP_IRQ_IPC0, ipc_handler, 4);
diff --git a/chip/mt_scp/ipi_chip.h b/chip/mt_scp/ipi_chip.h
new file mode 100644
index 0000000000..528f340bf0
--- /dev/null
+++ b/chip/mt_scp/ipi_chip.h
@@ -0,0 +1,82 @@
+/* Copyright 2018 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.
+ */
+
+#ifndef __CROS_EC_IPI_CHIP_H
+#define __CROS_EC_IPI_CHIP_H
+
+#include "chip/mt_scp/registers.h"
+#include "common.h"
+
+#define IPC_MAX 1
+#define IPC_ID(n) (n)
+
+/*
+ * Length of EC version string is at most 32 byte (NULL included), which
+ * also aligns SCP fw_version length.
+ */
+#define SCP_FW_VERSION_LEN 32
+
+#ifndef IPI_SCP_INIT
+#error If CONFIG_IPI is enabled, IPI_SCP_INIT must be defined.
+#endif
+
+/*
+ * Share buffer layout for IPI_SCP_INIT response. This structure should sync
+ * across kernel and EC.
+ */
+struct scp_run_t {
+ uint32_t signaled;
+ int8_t fw_ver[SCP_FW_VERSION_LEN];
+ uint32_t dec_capability;
+ uint32_t enc_capability;
+};
+
+/*
+ * The layout of the IPC0 AP/SCP shared buffer.
+ * This should sync across kernel and EC.
+ */
+struct ipc_shared_obj {
+ /* IPI ID */
+ int32_t id;
+ /* Length of the contents in buffer. */
+ uint32_t len;
+ /* Shared buffer contents. */
+ uint8_t buffer[CONFIG_IPC_SHARED_OBJ_BUF_SIZE];
+};
+
+/* Send a IPI contents to AP. */
+int ipi_send(int32_t id, void *buf, uint32_t len, int wait);
+
+/*
+ * IPC Handler.
+ */
+void ipc_handler(void);
+
+/* IPI tables */
+extern void (*ipi_handler_table[])(int32_t, void *, uint32_t);
+extern int *ipi_wakeup_table[];
+
+/* Helper macros to build the IPI handler and wakeup functions. */
+#define IPI_HANDLER(id) CONCAT3(ipi_, id, _handler)
+#define IPI_WAKEUP(id) CONCAT3(ipi_, id, _wakeup)
+
+/*
+ * Macro to declare an IPI handler.
+ * _id: The ID of the IPI
+ * handler: The IPI handler function
+ * is_wakeup_src: Declare IPI ID as a wake-up source or not
+ */
+#define DECLARE_IPI(_id, handler, is_wakeup_src) \
+ struct ipi_num_check1_##ipi { \
+ int dummy1[_id < IPI_COUNT ? 1 : -1]; \
+ int dummy2[is_wakeup_src == 0 || is_wakeup_src == 1 ? 1 : -1]; \
+ }; \
+ void __keep IPI_HANDLER(_id)(int32_t id, void *buf, uint32_t len) \
+ { \
+ handler(id, buf, len); \
+ } \
+ const int __keep IPI_WAKEUP(_id) = is_wakeup_src
+
+#endif /* __CROS_EC_IPI_CHIP_H */
diff --git a/chip/mt_scp/ipi_table.c b/chip/mt_scp/ipi_table.c
new file mode 100644
index 0000000000..aaeadb40d5
--- /dev/null
+++ b/chip/mt_scp/ipi_table.c
@@ -0,0 +1,87 @@
+/* Copyright 2018 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.
+ *
+ * IPI handlers declaration
+ */
+
+#include "common.h"
+#include "ipi_chip.h"
+
+typedef void (*ipi_handler_t)(int32_t id, void *data, uint32_t len);
+
+#ifndef PASS
+#define PASS 1
+#endif
+
+#define ipi_arguments int32_t id, void *data, uint32_t len
+
+#if PASS == 1
+void ipi_handler_undefined(ipi_arguments) { }
+
+const int ipi_wakeup_undefined;
+
+#define table(type, name, x) x
+
+#define ipi_x_func(suffix, args, number) \
+ extern void __attribute__( \
+ (used, weak, alias(STRINGIFY(ipi_##suffix##_undefined)))) \
+ ipi_##number##_##suffix(args);
+
+#define ipi_x_var(suffix, number) \
+ extern int __attribute__( \
+ (weak, alias(STRINGIFY(ipi_##suffix##_undefined)))) \
+ ipi_##number##_##suffix;
+
+#endif /* PASS == 1 */
+
+#if PASS == 2
+
+#undef table
+#undef ipi_x_func
+#undef ipi_x_var
+
+#define table(type, name, x) \
+ type name[] __aligned(4) \
+ __attribute__((section(".rodata.ipi, \"a\" @"))) = {x}
+
+#define ipi_x_var(suffix, number) \
+ [number < IPI_COUNT ? number : -1] = &ipi_##number##_##suffix,
+
+#define ipi_x_func(suffix, args, number) ipi_x_var(suffix, number)
+
+#endif /* PASS == 2 */
+
+/*
+ * Table to hold all the IPI handler function pointer.
+ */
+table(ipi_handler_t, ipi_handler_table,
+ ipi_x_func(handler, ipi_arguments, 0)
+ ipi_x_func(handler, ipi_arguments, 1)
+ ipi_x_func(handler, ipi_arguments, 2)
+ ipi_x_func(handler, ipi_arguments, 3)
+ ipi_x_func(handler, ipi_arguments, 4)
+ ipi_x_func(handler, ipi_arguments, 5)
+ ipi_x_func(handler, ipi_arguments, 6)
+ ipi_x_func(handler, ipi_arguments, 7)
+);
+
+/*
+ * Table to hold all the wake-up bool address.
+ */
+table(int*, ipi_wakeup_table,
+ ipi_x_var(wakeup, 0)
+ ipi_x_var(wakeup, 1)
+ ipi_x_var(wakeup, 2)
+ ipi_x_var(wakeup, 3)
+ ipi_x_var(wakeup, 4)
+ ipi_x_var(wakeup, 5)
+ ipi_x_var(wakeup, 6)
+ ipi_x_var(wakeup, 7)
+);
+
+#if PASS == 1
+#undef PASS
+#define PASS 2
+#include "ipi_table.c"
+#endif
diff --git a/chip/mt_scp/registers.h b/chip/mt_scp/registers.h
index 3f88099e53..718cc82b02 100644
--- a/chip/mt_scp/registers.h
+++ b/chip/mt_scp/registers.h
@@ -71,12 +71,22 @@
#define IPC_SCP2HOST_SSHUB 0xff0000
#define WDT_INT 0x100
#define IPC_SCP2HOST 0xff
+#define IPC_SCP2HOST_BIT 0x1
/* SCP to SPM interrupt */
#define SCP_SPM_INT REG32(SCP_CFG_BASE + 0x20)
+#define SPM_INT_A2SPM (1 << 0)
+#define SPM_INT_B2SPM (1 << 1)
#define SCP_SPM_INT2 REG32(SCP_CFG_BASE + 0x24)
+/*
+ * AP side to SCP IPC
+ * APMCU writes 1 bit to trigger ith IPC to SCP.
+ * SCP writes 1 bit to ith bit to clear ith IPC.
+ */
#define SCP_GIPC_IN REG32(SCP_CFG_BASE + 0x28)
+ #define SCP_GIPC_IN_CLEAR_IPCN(n) (1 << (n))
+ #define SCP_GPIC_IN_CLEAR_ALL 0x7FFFF
#define SCP_CONN_INT REG32(SCP_CFG_BASE + 0x2C)
/* 8 general purpose registers, 0 ~ 7 */
@@ -132,6 +142,7 @@
#define SCP_INTC_BASE (SCP_CFG_BASE + 0x2000)
#define SCP_INTC_IRQ_STATUS REG32(SCP_INTC_BASE)
#define SCP_INTC_IRQ_ENABLE REG32(SCP_INTC_BASE + 0x04)
+#define IPC0_IRQ_EN (1 << 0)
#define SCP_INTC_IRQ_OUTPUT REG32(SCP_INTC_BASE + 0x08)
#define SCP_INTC_IRQ_WAKEUP REG32(SCP_INTC_BASE + 0x0C)
#define SCP_INTC_NMI REG32(SCP_INTC_BASE + 0x10)
diff --git a/chip/mt_scp/system.c b/chip/mt_scp/system.c
index 77275a8e85..5a341a74e0 100644
--- a/chip/mt_scp/system.c
+++ b/chip/mt_scp/system.c
@@ -70,8 +70,8 @@ static void scp_enable_tcm(void)
static void scp_enable_pirq(void)
{
- /* Enable peripheral to SCP IRQ */
- SCP_INTC_IRQ_ENABLE = 0xFFFFFFFF;
+ /* Enable all peripheral to SCP IRQ, except IPC0. */
+ SCP_INTC_IRQ_ENABLE = 0xFFFFFFFE;
SCP_INTC_IRQ_ENABLE_MSB = 0xFFFFFFFF;
}
@@ -216,8 +216,6 @@ void system_pre_init(void)
scp_enable_pirq();
/* Init dram mapping */
scp_memmap_init();
- /* Init inter processor communication */
- /* scp_ipi_init(); */
}
void system_reset(int flags)