diff options
author | Tzung-Bi Shih <tzungbi@chromium.org> | 2020-06-30 09:43:23 +0800 |
---|---|---|
committer | Commit Bot <commit-bot@chromium.org> | 2020-07-03 19:35:21 +0000 |
commit | e8d3c6227ebb1492d9ad94394a3c6ac9badb4b4e (patch) | |
tree | 0a6e4b04adadda23172deb94b1cc1deb5f9d6821 | |
parent | 6e7f31783acc91e781e93e1712fc9375f8da59eb (diff) | |
download | chrome-ec-e8d3c6227ebb1492d9ad94394a3c6ac9badb4b4e.tar.gz |
chip/mt8192_scp: support ipi_send()
Supports ipi_send() which sends IPI messages to AP.
BRANCH=none
BUG=b:146213943
BUG=b:156223050
TEST=make BOARD=asurada_scp
Signed-off-by: Tzung-Bi Shih <tzungbi@chromium.org>
Change-Id: Ia146b29607bc5bf4150b637368b3a99986de677d
Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/platform/ec/+/2275709
Reviewed-by: Eric Yilun Lin <yllin@chromium.org>
-rw-r--r-- | chip/mt8192_scp/build.mk | 1 | ||||
-rw-r--r-- | chip/mt8192_scp/ipi.c | 121 | ||||
-rw-r--r-- | chip/mt8192_scp/ipi_chip.h | 66 | ||||
-rw-r--r-- | chip/mt8192_scp/registers.h | 5 |
4 files changed, 193 insertions, 0 deletions
diff --git a/chip/mt8192_scp/build.mk b/chip/mt8192_scp/build.mk index 19de637af2..966c1a29ef 100644 --- a/chip/mt8192_scp/build.mk +++ b/chip/mt8192_scp/build.mk @@ -17,4 +17,5 @@ chip-y+=uart.o # Optional chip modules chip-$(CONFIG_COMMON_TIMER)+=hrtimer.o +chip-$(CONFIG_IPI)+=ipi.o chip-$(CONFIG_WATCHDOG)+=watchdog.o diff --git a/chip/mt8192_scp/ipi.c b/chip/mt8192_scp/ipi.c new file mode 100644 index 0000000000..fdbcde3811 --- /dev/null +++ b/chip/mt8192_scp/ipi.c @@ -0,0 +1,121 @@ +/* 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 "common.h" +#include "console.h" +#include "hooks.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 int ipi_is_busy(void) +{ + return SCP_SCP2APMCU_IPC_SET & IPC_SCP2HOST; +} + +static void ipi_wake_ap(int32_t id) +{ + 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; + } + + 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); + + /* 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); + 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; + scp_run.enc_capability = 0; + + 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; + } +} +DECLARE_DEFERRED(ipi_enable_deferred); + +static void ipi_init(void) +{ + memset(ipi_send_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); diff --git a/chip/mt8192_scp/ipi_chip.h b/chip/mt8192_scp/ipi_chip.h new file mode 100644 index 0000000000..1fa30275d9 --- /dev/null +++ b/chip/mt8192_scp/ipi_chip.h @@ -0,0 +1,66 @@ +/* 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. + */ + +#ifndef __CROS_EC_IPI_CHIP_H +#define __CROS_EC_IPI_CHIP_H + +/* + * 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 + +/* + * Video decoder supported capability: + * BIT(4): 0 enable 4K + * 1 disable 4K + */ +#define VCODEC_CAPABILITY_4K_DISABLED BIT(4) + +#ifndef SCP_IPI_INIT +#error If CONFIG_IPI is enabled, SCP_IPI_INIT must be defined. +#endif + +/* + * Share buffer layout for SCP_IPI_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. This shouldn't be used in ISR context. */ +int ipi_send(int32_t id, const void *buf, uint32_t len, int wait); + +/* + * An IPC IRQ could be shared across many IPI handlers. + * Those handlers would usually operate on disabling or enabling the IPC IRQ. + * This may disorder the actual timing to on/off the IRQ when there are many + * tasks try to operate on it. As a result, any access to the SCP_IRQ_* + * should go through ipi_{en,dis}able_irq(), which support a counter to + * enable/disable the IRQ at correct timing. + */ +/* Disable IPI IRQ. */ +void ipi_disable_irq(void); +/* Enable IPI IRQ. */ +void ipi_enable_irq(void); + +#endif /* __CROS_EC_IPI_CHIP_H */ diff --git a/chip/mt8192_scp/registers.h b/chip/mt8192_scp/registers.h index 7893a57a67..5620dcb6dc 100644 --- a/chip/mt8192_scp/registers.h +++ b/chip/mt8192_scp/registers.h @@ -64,6 +64,11 @@ #define UART_CK_SW_STATUS_32K BIT(1) #define UART_CK_SW_STATUS_ULPOS BIT(2) +/* IPC */ +#define SCP_SCP2APMCU_IPC_SET REG32(SCP_REG_BASE + 0x24080) +#define SCP_SCP2SPM_IPC_SET REG32(SCP_REG_BASE + 0x24090) +#define IPC_SCP2HOST BIT(0) + /* UART */ #define SCP_UART_COUNT 2 #define UART_TX_IRQ(n) CONCAT3(SCP_IRQ_UART, n, _TX) |