diff options
author | Yilun Lin <yllin@google.com> | 2018-11-21 14:22:42 +0800 |
---|---|---|
committer | chrome-bot <chrome-bot@chromium.org> | 2018-12-29 05:45:28 -0800 |
commit | c7804fd61b3bacf29cb4f1da9483860435ecac20 (patch) | |
tree | 4c47beee640868f19107a057d3df1a3d21732791 /chip | |
parent | 02983ae05319d71bf8b29e063bf40acfd53b1e02 (diff) | |
download | chrome-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.mk | 1 | ||||
-rw-r--r-- | chip/mt_scp/config_chip.h | 6 | ||||
-rw-r--r-- | chip/mt_scp/ipi.c | 179 | ||||
-rw-r--r-- | chip/mt_scp/ipi_chip.h | 82 | ||||
-rw-r--r-- | chip/mt_scp/ipi_table.c | 87 | ||||
-rw-r--r-- | chip/mt_scp/registers.h | 11 | ||||
-rw-r--r-- | chip/mt_scp/system.c | 6 |
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) |