summaryrefslogtreecommitdiff
path: root/chip/mt_scp/rv32i_common
diff options
context:
space:
mode:
authorTzung-Bi Shih <tzungbi@chromium.org>2021-06-24 17:17:39 +0800
committerCommit Bot <commit-bot@chromium.org>2021-06-25 03:42:30 +0000
commitb409fd3b5ac6a82b6851f2e9af49761ee2d4766c (patch)
tree780a069f10a57222362a209abee19b27b7de9076 /chip/mt_scp/rv32i_common
parentcf92cf3c6c4135b511c8f7f6bc83036167b7c735 (diff)
downloadchrome-ec-b409fd3b5ac6a82b6851f2e9af49761ee2d4766c.tar.gz
chip/mt_scp: move rv32i specific to common folder
BRANCH=none BUG=b:191835814 TEST=make BOARD=asurada_scp && make BOARD=cherry_scp && make BOARD=kukui_scp Signed-off-by: Tzung-Bi Shih <tzungbi@chromium.org> Change-Id: I35e9fd5f7d3e83d35d09a093be09b194c821f63e Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/platform/ec/+/2985060 Reviewed-by: Eric Yilun Lin <yllin@google.com>
Diffstat (limited to 'chip/mt_scp/rv32i_common')
-rw-r--r--chip/mt_scp/rv32i_common/build.mk28
-rw-r--r--chip/mt_scp/rv32i_common/cache.c211
-rw-r--r--chip/mt_scp/rv32i_common/cache.h140
-rw-r--r--chip/mt_scp/rv32i_common/clock.c363
-rw-r--r--chip/mt_scp/rv32i_common/clock_chip.h23
-rw-r--r--chip/mt_scp/rv32i_common/config_chip.h57
-rw-r--r--chip/mt_scp/rv32i_common/csr.h111
-rw-r--r--chip/mt_scp/rv32i_common/gpio.c21
-rw-r--r--chip/mt_scp/rv32i_common/hostcmd.c128
-rw-r--r--chip/mt_scp/rv32i_common/hostcmd.h12
-rw-r--r--chip/mt_scp/rv32i_common/hrtimer.c221
-rw-r--r--chip/mt_scp/rv32i_common/intc.c260
-rw-r--r--chip/mt_scp/rv32i_common/ipi.c184
-rw-r--r--chip/mt_scp/rv32i_common/ipi_chip.h102
-rw-r--r--chip/mt_scp/rv32i_common/ipi_table.c67
-rw-r--r--chip/mt_scp/rv32i_common/memmap.c114
-rw-r--r--chip/mt_scp/rv32i_common/memmap.h31
-rw-r--r--chip/mt_scp/rv32i_common/registers.h391
-rw-r--r--chip/mt_scp/rv32i_common/system.c50
-rw-r--r--chip/mt_scp/rv32i_common/uart.c171
-rw-r--r--chip/mt_scp/rv32i_common/uart_regs.h77
-rw-r--r--chip/mt_scp/rv32i_common/watchdog.c33
22 files changed, 2795 insertions, 0 deletions
diff --git a/chip/mt_scp/rv32i_common/build.mk b/chip/mt_scp/rv32i_common/build.mk
new file mode 100644
index 0000000000..3c09548a8c
--- /dev/null
+++ b/chip/mt_scp/rv32i_common/build.mk
@@ -0,0 +1,28 @@
+# -*- makefile -*-
+# 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.
+#
+# SCP specific files build
+#
+
+CORE:=riscv-rv32i
+
+# Required chip modules
+chip-y+=rv32i_common/cache.o
+chip-y+=rv32i_common/clock.o
+chip-y+=rv32i_common/gpio.o
+chip-y+=rv32i_common/intc.o
+chip-y+=rv32i_common/memmap.o
+chip-y+=rv32i_common/system.o
+chip-y+=rv32i_common/uart.o
+
+ifeq ($(CONFIG_IPI),y)
+$(out)/RW/chip/$(CHIP)/rv32i_common/ipi_table.o: $(out)/ipi_table_gen.inc
+endif
+
+# Optional chip modules
+chip-$(CONFIG_COMMON_TIMER)+=rv32i_common/hrtimer.o
+chip-$(CONFIG_IPI)+=rv32i_common/ipi.o rv32i_common/ipi_table.o
+chip-$(CONFIG_WATCHDOG)+=rv32i_common/watchdog.o
+chip-$(HAS_TASK_HOSTCMD)+=rv32i_common/hostcmd.o
diff --git a/chip/mt_scp/rv32i_common/cache.c b/chip/mt_scp/rv32i_common/cache.c
new file mode 100644
index 0000000000..62147590fe
--- /dev/null
+++ b/chip/mt_scp/rv32i_common/cache.c
@@ -0,0 +1,211 @@
+/* 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 "cache.h"
+#include "console.h"
+#include "csr.h"
+
+extern struct mpu_entry mpu_entries[];
+
+void cache_init(void)
+{
+ int i;
+ uint32_t mpu_en = 0;
+
+ /* disable mpu */
+ clear_csr(CSR_MCTREN, CSR_MCTREN_MPU);
+
+ /* enable i$, d$ */
+ set_csr(CSR_MCTREN, CSR_MCTREN_ICACHE);
+ set_csr(CSR_MCTREN, CSR_MCTREN_DCACHE);
+
+ /* invalidate icache and dcache */
+ cache_invalidate_icache();
+ cache_invalidate_dcache();
+
+ /* set mpu entries
+ *
+ * The pragma is for force GCC unrolls the following loop.
+ * See b/172886808
+ */
+#pragma GCC unroll 16
+ for (i = 0; i < NR_MPU_ENTRIES; ++i) {
+ if (mpu_entries[i].end_addr - mpu_entries[i].start_addr) {
+ write_csr(CSR_MPU_L(i), mpu_entries[i].start_addr |
+ mpu_entries[i].attribute);
+ write_csr(CSR_MPU_H(i), mpu_entries[i].end_addr);
+ mpu_en |= BIT(i);
+ }
+ }
+
+ /* enable mpu entries */
+ write_csr(CSR_MPU_ENTRY_EN, mpu_en);
+
+ /* enable mpu */
+ set_csr(CSR_MCTREN, CSR_MCTREN_MPU);
+
+ /* fence */
+ asm volatile ("fence.i" ::: "memory");
+}
+
+#ifdef DEBUG
+/*
+ * I for I-cache
+ * D for D-cache
+ * C for control transfer instructions (branch, jump, ret, interrupt, ...)
+ */
+static enum {
+ PMU_SELECT_I = 0,
+ PMU_SELECT_D,
+ PMU_SELECT_C
+} pmu_select;
+
+int command_enable_pmu(int argc, char **argv)
+{
+ static const char * const selectors[] = {
+ [PMU_SELECT_I] = "I",
+ [PMU_SELECT_D] = "D",
+ [PMU_SELECT_C] = "C",
+ };
+ int i;
+
+ if (argc != 2)
+ return EC_ERROR_PARAM1;
+
+ for (i = 0; i < ARRAY_SIZE(selectors); ++i) {
+ if (strcasecmp(argv[1], selectors[i]) == 0) {
+ pmu_select = i;
+ break;
+ }
+ }
+ if (i >= ARRAY_SIZE(selectors))
+ return EC_ERROR_PARAM1;
+
+ ccprintf("select \"%s\"\n", selectors[pmu_select]);
+
+ /* disable all PMU */
+ clear_csr(CSR_PMU_MPMUCTR,
+ CSR_PMU_MPMUCTR_C | CSR_PMU_MPMUCTR_I |
+ CSR_PMU_MPMUCTR_H3 | CSR_PMU_MPMUCTR_H4 |
+ CSR_PMU_MPMUCTR_H5);
+
+ /* reset cycle count */
+ write_csr(CSR_PMU_MCYCLE, 0);
+ write_csr(CSR_PMU_MCYCLEH, 0);
+ /* reset retired-instruction count */
+ write_csr(CSR_PMU_MINSTRET, 0);
+ write_csr(CSR_PMU_MINSTRETH, 0);
+ /* reset counter{3,4,5} */
+ write_csr(CSR_PMU_MHPMCOUNTER3, 0);
+ write_csr(CSR_PMU_MHPMCOUNTER3H, 0);
+ write_csr(CSR_PMU_MHPMCOUNTER4, 0);
+ write_csr(CSR_PMU_MHPMCOUNTER4H, 0);
+ write_csr(CSR_PMU_MHPMCOUNTER5, 0);
+ write_csr(CSR_PMU_MHPMCOUNTER5H, 0);
+
+ /* select different event IDs for counter{3,4,5} */
+ switch (pmu_select) {
+ case PMU_SELECT_I:
+ /* I-cache access count */
+ write_csr(CSR_PMU_MHPMEVENT3, 1);
+ /* I-cache miss count */
+ write_csr(CSR_PMU_MHPMEVENT4, 3);
+ /* noncacheable I-AXI access count */
+ write_csr(CSR_PMU_MHPMEVENT5, 5);
+ break;
+ case PMU_SELECT_D:
+ /* D-cache access count */
+ write_csr(CSR_PMU_MHPMEVENT3, 11);
+ /* D-cache miss count */
+ write_csr(CSR_PMU_MHPMEVENT4, 12);
+ /* noncacheable D-AXI access count */
+ write_csr(CSR_PMU_MHPMEVENT5, 14);
+ break;
+ case PMU_SELECT_C:
+ /* control transfer instruction count */
+ write_csr(CSR_PMU_MHPMEVENT3, 27);
+ /* control transfer miss-predict count */
+ write_csr(CSR_PMU_MHPMEVENT4, 28);
+ /* interrupt count */
+ write_csr(CSR_PMU_MHPMEVENT5, 29);
+ break;
+ }
+
+ cache_invalidate_icache();
+ cache_flush_dcache();
+
+ /* enable all PMU */
+ set_csr(CSR_PMU_MPMUCTR,
+ CSR_PMU_MPMUCTR_C | CSR_PMU_MPMUCTR_I |
+ CSR_PMU_MPMUCTR_H3 | CSR_PMU_MPMUCTR_H4 |
+ CSR_PMU_MPMUCTR_H5);
+
+ return EC_SUCCESS;
+}
+DECLARE_SAFE_CONSOLE_COMMAND(enable_pmu, command_enable_pmu,
+ "[I | D | C]", "Enable PMU");
+
+int command_disable_pmu(int argc, char **argv)
+{
+ clear_csr(CSR_PMU_MPMUCTR,
+ CSR_PMU_MPMUCTR_C | CSR_PMU_MPMUCTR_I |
+ CSR_PMU_MPMUCTR_H3 | CSR_PMU_MPMUCTR_H4 |
+ CSR_PMU_MPMUCTR_H5);
+ return EC_SUCCESS;
+}
+DECLARE_SAFE_CONSOLE_COMMAND(disable_pmu, command_disable_pmu,
+ NULL, "Disable PMU");
+
+int command_show_pmu(int argc, char **argv)
+{
+ uint64_t val3, val4, val5;
+ uint32_t p;
+
+ val3 = ((uint64_t)read_csr(CSR_PMU_MCYCLEH) << 32) |
+ read_csr(CSR_PMU_MCYCLE);
+ ccprintf("cycles: %lld\n", val3);
+
+ val3 = ((uint64_t)read_csr(CSR_PMU_MINSTRETH) << 32) |
+ read_csr(CSR_PMU_MINSTRET);
+ ccprintf("retired instructions: %lld\n", val3);
+
+ val3 = ((uint64_t)read_csr(CSR_PMU_MHPMCOUNTER3H) << 32) |
+ read_csr(CSR_PMU_MHPMCOUNTER3);
+ val4 = ((uint64_t)read_csr(CSR_PMU_MHPMCOUNTER4H) << 32) |
+ read_csr(CSR_PMU_MHPMCOUNTER4);
+ val5 = ((uint64_t)read_csr(CSR_PMU_MHPMCOUNTER5H) << 32) |
+ read_csr(CSR_PMU_MHPMCOUNTER5);
+
+ if (val3)
+ p = val4 * 10000 / val3;
+ else
+ p = 0;
+
+ switch (pmu_select) {
+ case PMU_SELECT_I:
+ ccprintf("I-cache:\n");
+ ccprintf(" access: %lld\n", val3);
+ ccprintf(" miss: %lld (%d.%d%%)\n", val4, p / 100, p % 100);
+ ccprintf("non-cacheable I: %lld\n", val5);
+ break;
+ case PMU_SELECT_D:
+ ccprintf("D-cache:\n");
+ ccprintf(" access: %lld\n", val3);
+ ccprintf(" miss: %lld (%d.%d%%)\n", val4, p / 100, p % 100);
+ ccprintf("non-cacheable D: %lld\n", val5);
+ break;
+ case PMU_SELECT_C:
+ ccprintf("control transfer instruction:\n");
+ ccprintf(" total: %lld\n", val3);
+ ccprintf(" miss-predict: %lld (%d.%d%%)\n",
+ val4, p / 100, p % 100);
+ ccprintf("interrupts: %lld\n", val5);
+ break;
+ }
+
+ return EC_SUCCESS;
+}
+DECLARE_SAFE_CONSOLE_COMMAND(show_pmu, command_show_pmu, NULL, "Show PMU");
+#endif
diff --git a/chip/mt_scp/rv32i_common/cache.h b/chip/mt_scp/rv32i_common/cache.h
new file mode 100644
index 0000000000..13e5ad1a42
--- /dev/null
+++ b/chip/mt_scp/rv32i_common/cache.h
@@ -0,0 +1,140 @@
+/* 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_CACHE_H
+#define __CROS_EC_CACHE_H
+
+#include "common.h"
+#include "csr.h"
+#include "stdint.h"
+#include "util.h"
+
+/* rs1 0~31 register X0~X31 */
+#define COP(rs1) (((rs1) << 15) | 0x400f)
+
+#define COP_OP_BARRIER_ICACHE 0x0
+#define COP_OP_INVALIDATE_ICACHE 0x8
+#define COP_OP_INVALIDATE_ICACHE_ADDR 0x9
+
+#define COP_OP_BARRIER_DCACHE 0x10
+#define COP_OP_WRITEBACK_DCACHE 0x14
+#define COP_OP_WRITEBACK_DCACHE_ADDR 0x15
+#define COP_OP_INVALIDATE_DCACHE 0x18
+#define COP_OP_INVALIDATE_DCACHE_ADDR 0x19
+/* FLUSH = WRITEBACK + INVALIDATE */
+#define COP_OP_FLUSH_DCACHE 0x1C
+#define COP_OP_FLUSH_DCACHE_ADDR 0x1D
+
+static inline void cache_op_all(uint32_t op)
+{
+ register int t0 asm("t0") = op;
+ asm volatile (".word "STRINGIFY(COP(5)) :: "r"(t0));
+}
+
+static inline int cache_op_addr(uintptr_t addr, uint32_t length, uint32_t op)
+{
+ size_t offset;
+ register int t0 asm("t0");
+
+ /* NOTE: cache operations must use 32 byte aligned address */
+ if (addr & GENMASK(3, 0))
+ return EC_ERROR_INVAL;
+
+ for (offset = 0; offset < length; offset += 4) {
+ t0 = addr + offset + op;
+ asm volatile (".word "STRINGIFY(COP(5)) :: "r"(t0));
+ }
+
+ return EC_SUCCESS;
+}
+
+/* memory barrier of I$ */
+static inline void cache_barrier_icache(void)
+{
+ cache_op_all(COP_OP_BARRIER_ICACHE);
+}
+
+/* invalidate all I$ */
+static inline void cache_invalidate_icache(void)
+{
+ cache_op_all(COP_OP_INVALIDATE_ICACHE);
+}
+
+/* invalidate a range of I$ */
+static inline int cache_invalidate_icache_range(uintptr_t addr, uint32_t length)
+{
+ return cache_op_addr(addr, length, COP_OP_INVALIDATE_ICACHE_ADDR);
+}
+
+/* memory barrier of D$ */
+static inline void cache_barrier_dcache(void)
+{
+ cache_op_all(COP_OP_BARRIER_DCACHE);
+}
+
+/* writeback all D$ */
+static inline void cache_writeback_dcache(void)
+{
+ cache_op_all(COP_OP_WRITEBACK_DCACHE);
+ cache_barrier_icache();
+ cache_barrier_dcache();
+}
+
+/* writeback a range of D$ */
+static inline int cache_writeback_dcache_range(uintptr_t addr, uint32_t length)
+{
+ int ret = cache_op_addr(addr, length, COP_OP_WRITEBACK_DCACHE_ADDR);
+ cache_barrier_icache();
+ cache_barrier_dcache();
+ return ret;
+}
+
+/* invalidate all D$ */
+static inline void cache_invalidate_dcache(void)
+{
+ cache_op_all(COP_OP_INVALIDATE_DCACHE);
+}
+
+/* invalidate a range of D$ */
+static inline int cache_invalidate_dcache_range(uintptr_t addr, uint32_t length)
+{
+ return cache_op_addr(addr, length, COP_OP_INVALIDATE_DCACHE_ADDR);
+}
+
+/* writeback and invalidate all D$ */
+static inline void cache_flush_dcache(void)
+{
+ cache_op_all(COP_OP_FLUSH_DCACHE);
+ cache_barrier_icache();
+ cache_barrier_dcache();
+}
+
+/* writeback and invalidate a range of D$ */
+static inline int cache_flush_dcache_range(uintptr_t addr, uint32_t length)
+{
+ int ret = cache_op_addr(addr, length, COP_OP_FLUSH_DCACHE_ADDR);
+ cache_barrier_icache();
+ cache_barrier_dcache();
+ return ret;
+}
+
+struct mpu_entry {
+ /* 1k alignment and the address is inclusive */
+ uintptr_t start_addr;
+ /* 1k alignment in 4GB boundary and non-inclusive */
+ uintptr_t end_addr;
+ /* MPU_ATTR */
+ uint32_t attribute;
+};
+
+void cache_init(void);
+
+#ifdef DEBUG
+int command_enable_pmu(int argc, char **argv);
+int command_disable_pmu(int argc, char **argv);
+int command_show_pmu(int argc, char **argv);
+#endif
+
+#endif /* #ifndef __CROS_EC_CACHE_H */
diff --git a/chip/mt_scp/rv32i_common/clock.c b/chip/mt_scp/rv32i_common/clock.c
new file mode 100644
index 0000000000..a460d818c7
--- /dev/null
+++ b/chip/mt_scp/rv32i_common/clock.c
@@ -0,0 +1,363 @@
+/* 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.
+ */
+
+/* Clocks, PLL and power settings */
+
+#include <assert.h>
+#include <string.h>
+
+#include "clock_chip.h"
+#include "clock.h"
+#include "common.h"
+#include "console.h"
+#include "csr.h"
+#include "ec_commands.h"
+#include "power.h"
+#include "registers.h"
+#include "timer.h"
+
+#define CPRINTF(format, args...) cprintf(CC_CLOCK, format, ##args)
+#define CPRINTS(format, args...) cprints(CC_CLOCK, format, ##args)
+
+static struct opp_ulposc_cfg {
+ uint32_t osc;
+ uint32_t div;
+ uint32_t fband;
+ uint32_t mod;
+ uint32_t cali;
+ uint32_t target_mhz;
+} opp[] = {
+ {
+ .osc = 1, .target_mhz = 196, .div = 20, .fband = 10, .mod = 3,
+ .cali = 64,
+ },
+ {
+ .osc = 0, .target_mhz = 260, .div = 14, .fband = 2, .mod = 0,
+ .cali = 64,
+ },
+ {
+ .osc = 1, .target_mhz = 280, .div = 20, .fband = 2, .mod = 0,
+ .cali = 64,
+ },
+ {
+ .osc = 1, .target_mhz = 360, .div = 20, .fband = 10, .mod = 0,
+ .cali = 64,
+ },
+};
+
+static inline void clock_busy_udelay(int usec)
+{
+ /*
+ * Delaying by busy-looping, for place that can't use udelay because of
+ * the clock not configured yet. The value 28 is chosen approximately
+ * from experiment.
+ *
+ * `volatile' in order to avoid compiler to optimize the function out
+ * (otherwise, the function will be eliminated).
+ */
+ volatile int i = usec * 28;
+
+ while (--i)
+ ;
+}
+
+static void clock_ulposc_config_default(struct opp_ulposc_cfg *opp)
+{
+ unsigned int val = 0;
+
+ /* set div */
+ val |= opp->div << OSC_DIV_SHIFT;
+ /* set F-band; I-band = 82 */
+ val |= (opp->fband << OSC_FBAND_SHIFT) | (82 << OSC_IBAND_SHIFT);
+ /* set calibration */
+ val |= opp->cali;
+ /* set control register 0 */
+ AP_ULPOSC_CON0(opp->osc) = val;
+
+ /* set mod */
+ val = opp->mod << OSC_MOD_SHIFT;
+ /* rsv2 = 0, rsv1 = 41, cali_32k = 0 */
+ val |= 41 << OSC_RSV1_SHIFT;
+ /* set control register 1 */
+ AP_ULPOSC_CON1(opp->osc) = val;
+
+ /* bias = 64 */
+ AP_ULPOSC_CON2(opp->osc) = 64;
+}
+
+static void clock_ulposc_config_cali(struct opp_ulposc_cfg *opp,
+ uint32_t cali_val)
+{
+ uint32_t val;
+
+ val = AP_ULPOSC_CON0(opp->osc);
+ val &= ~OSC_CALI_MASK;
+ val |= cali_val;
+ AP_ULPOSC_CON0(opp->osc) = val;
+
+ clock_busy_udelay(50);
+}
+
+static uint32_t clock_ulposc_measure_freq(uint32_t osc)
+{
+ uint32_t result = 0;
+ int cnt;
+
+ /* before select meter clock input, bit[1:0] = b00 */
+ AP_CLK_DBG_CFG = (AP_CLK_DBG_CFG & ~DBG_MODE_MASK) |
+ DBG_MODE_SET_CLOCK;
+
+ /* select source, bit[21:16] = clk_src */
+ AP_CLK_DBG_CFG = (AP_CLK_DBG_CFG & ~DBG_BIST_SOURCE_MASK) |
+ (osc == 0 ? DBG_BIST_SOURCE_ULPOSC1 :
+ DBG_BIST_SOURCE_ULPOSC2);
+
+ /* set meter divisor to 1, bit[31:24] = b00000000 */
+ AP_CLK_MISC_CFG_0 = (AP_CLK_MISC_CFG_0 & ~MISC_METER_DIVISOR_MASK) |
+ MISC_METER_DIV_1;
+
+ /* enable frequency meter, without start */
+ AP_SCP_CFG_0 |= CFG_FREQ_METER_ENABLE;
+
+ /* trigger frequency meter start */
+ AP_SCP_CFG_0 |= CFG_FREQ_METER_RUN;
+
+ /*
+ * Frequency meter counts cycles in 1 / (26 * 1024) second period.
+ * freq_in_hz = freq_counter * 26 * 1024
+ *
+ * The hardware takes 38us to count cycles. Delay up to 100us,
+ * as clock_busy_udelay may not be accurate when sysclk is not 26Mhz
+ * (e.g. when recalibrating/measuring after boot).
+ */
+ for (cnt = 100; cnt > 0; --cnt) {
+ clock_busy_udelay(1);
+ if (!(AP_SCP_CFG_0 & CFG_FREQ_METER_RUN)) {
+ result = CFG_FREQ_COUNTER(AP_SCP_CFG_1);
+ break;
+ }
+ }
+
+ /* disable freq meter */
+ AP_SCP_CFG_0 &= ~CFG_FREQ_METER_ENABLE;
+
+ return result;
+}
+
+#define CAL_MIS_RATE 40
+static int clock_ulposc_is_calibrated(struct opp_ulposc_cfg *opp)
+{
+ uint32_t curr, target;
+
+ curr = clock_ulposc_measure_freq(opp->osc);
+ target = opp->target_mhz * 1024 / 26;
+
+ /* check if calibrated value is in the range of target value +- 4% */
+ if (curr > (target * (1000 - CAL_MIS_RATE) / 1000) &&
+ curr < (target * (1000 + CAL_MIS_RATE) / 1000))
+ return 1;
+ else
+ return 0;
+}
+
+static uint32_t clock_ulposc_process_cali(struct opp_ulposc_cfg *opp)
+{
+ uint32_t current_val = 0;
+ uint32_t target_val = opp->target_mhz * 1024 / 26;
+ uint32_t middle, min = 0, max = OSC_CALI_MASK;
+ uint32_t diff_by_min, diff_by_max, cal_result;
+
+ do {
+ middle = (min + max) / 2;
+ if (middle == min)
+ break;
+
+ clock_ulposc_config_cali(opp, middle);
+ current_val = clock_ulposc_measure_freq(opp->osc);
+
+ if (current_val > target_val)
+ max = middle;
+ else
+ min = middle;
+ } while (min <= max);
+
+ clock_ulposc_config_cali(opp, min);
+ current_val = clock_ulposc_measure_freq(opp->osc);
+ if (current_val > target_val)
+ diff_by_min = current_val - target_val;
+ else
+ diff_by_min = target_val - current_val;
+
+ clock_ulposc_config_cali(opp, max);
+ current_val = clock_ulposc_measure_freq(opp->osc);
+ if (current_val > target_val)
+ diff_by_max = current_val - target_val;
+ else
+ diff_by_max = target_val - current_val;
+
+ if (diff_by_min < diff_by_max)
+ cal_result = min;
+ else
+ cal_result = max;
+
+ clock_ulposc_config_cali(opp, cal_result);
+ if (!clock_ulposc_is_calibrated(opp))
+ assert(0);
+
+ return cal_result;
+}
+
+static void clock_high_enable(int osc)
+{
+ /* enable high speed clock */
+ SCP_CLK_ENABLE |= CLK_HIGH_EN;
+
+ switch (osc) {
+ case 0:
+ /* after 150us, enable ULPOSC */
+ clock_busy_udelay(150);
+ SCP_CLK_ENABLE |= CLK_HIGH_CG;
+ break;
+ case 1:
+ /* turn off ULPOSC2 high-core-disable switch */
+ SCP_CLK_ON_CTRL &= ~HIGH_CORE_DIS_SUB;
+ /* after 150us, turn on ULPOSC2 high core clock gate */
+ clock_busy_udelay(150);
+ SCP_CLK_HIGH_CORE_CG |= HIGH_CORE_CG;
+ clock_busy_udelay(50);
+ break;
+ default:
+ break;
+ }
+}
+
+static void clock_high_disable(int osc)
+{
+ switch (osc) {
+ case 0:
+ SCP_CLK_ENABLE &= ~CLK_HIGH_CG;
+ clock_busy_udelay(50);
+ SCP_CLK_ENABLE &= ~CLK_HIGH_EN;
+ clock_busy_udelay(50);
+ break;
+ case 1:
+ SCP_CLK_HIGH_CORE_CG &= ~HIGH_CORE_CG;
+ clock_busy_udelay(50);
+ SCP_CLK_ON_CTRL |= HIGH_CORE_DIS_SUB;
+ clock_busy_udelay(50);
+ break;
+ default:
+ break;
+ }
+}
+
+static void clock_calibrate_ulposc(struct opp_ulposc_cfg *opp)
+{
+ /*
+ * ULPOSC1(osc=0) is already
+ * - calibrated
+ * - enabled in coreboot
+ * - used by pmic wrapper
+ */
+ if (opp->osc != 0) {
+ clock_high_disable(opp->osc);
+ clock_ulposc_config_default(opp);
+ clock_high_enable(opp->osc);
+ }
+
+ /* Calibrate only if it is not accurate enough. */
+ if (!clock_ulposc_is_calibrated(opp))
+ opp->cali = clock_ulposc_process_cali(opp);
+
+#ifdef DEBUG
+ CPRINTF("osc:%u, target=%uMHz, cal:%u\n",
+ opp->osc, opp->target_mhz, opp->cali);
+#endif
+}
+
+void clock_select_clock(enum scp_clock_source src)
+{
+ /*
+ * DIV2 divider takes precedence over clock selection to prevent
+ * over-clocking.
+ */
+ if (src == SCP_CLK_ULPOSC1)
+ SCP_CLK_DIV_SEL = CLK_DIV_SEL2;
+
+ SCP_CLK_SW_SEL = src;
+
+ if (src != SCP_CLK_ULPOSC1)
+ SCP_CLK_DIV_SEL = CLK_DIV_SEL1;
+}
+
+__override void
+power_chipset_handle_host_sleep_event(enum host_sleep_event state,
+ struct host_sleep_event_context *ctx)
+{
+ if (state == HOST_SLEEP_EVENT_S3_SUSPEND) {
+ CPRINTS("AP suspend");
+ clock_select_clock(SCP_CLK_ULPOSC1);
+ } else if (state == HOST_SLEEP_EVENT_S3_RESUME) {
+ CPRINTS("AP resume");
+ clock_select_clock(SCP_CLK_ULPOSC2);
+ }
+}
+
+void clock_init(void)
+{
+ int i;
+
+ /* select default 26M system clock */
+ clock_select_clock(SCP_CLK_26M);
+
+ /* set VREQ to HW mode */
+ SCP_CPU_VREQ_CTRL = VREQ_SEL | VREQ_DVFS_SEL;
+ SCP_CLK_CTRL_GENERAL_CTRL &= ~VREQ_PMIC_WRAP_SEL;
+ SCP_SEC_CTRL &= ~VREQ_SECURE_DIS;
+
+ /* set DDREN to auto mode */
+ SCP_SYS_CTRL |= AUTO_DDREN;
+
+ /* set settle time */
+ SCP_CLK_SYS_VAL =
+ (SCP_CLK_SYS_VAL & ~CLK_SYS_VAL_MASK) | CLK_SYS_VAL_VAL(1);
+ SCP_CLK_HIGH_VAL =
+ (SCP_CLK_HIGH_VAL & ~CLK_HIGH_VAL_MASK) | CLK_HIGH_VAL_VAL(1);
+ SCP_SLEEP_CTRL =
+ (SCP_SLEEP_CTRL & ~VREQ_COUNT_MASK) | VREQ_COUNT_VAL(1);
+
+ /* turn off ULPOSC2 */
+ SCP_CLK_ON_CTRL |= HIGH_CORE_DIS_SUB;
+
+ /* calibrate ULPOSC */
+ for (i = 0; i < ARRAY_SIZE(opp); ++i)
+ clock_calibrate_ulposc(&opp[i]);
+
+ /* select ULPOSC2 high speed CPU clock */
+ clock_select_clock(SCP_CLK_ULPOSC2);
+
+ /* select BCLK to use ULPOSC1 / 8 = 260MHz / 8 = 32.5MHz */
+ SCP_BCLK_CK_SEL = BCLK_CK_SEL_ULPOSC_DIV8;
+
+ /* enable default clock gate */
+ SCP_SET_CLK_CG |= CG_DMA_CH3 | CG_DMA_CH2 | CG_DMA_CH1 | CG_DMA_CH0 |
+ CG_I2C_MCLK | CG_MAD_MCLK | CG_AP2P_MCLK;
+}
+
+#ifdef DEBUG
+int command_ulposc(int argc, char *argv[])
+{
+ int i;
+
+ for (i = 0; i <= 1; ++i)
+ ccprintf("ULPOSC%u frequency: %u kHz\n",
+ i + 1,
+ clock_ulposc_measure_freq(i) * 26 * 1000 / 1024);
+
+ return EC_SUCCESS;
+}
+DECLARE_CONSOLE_COMMAND(ulposc, command_ulposc, "[ulposc]",
+ "Measure ULPOSC frequency");
+#endif
diff --git a/chip/mt_scp/rv32i_common/clock_chip.h b/chip/mt_scp/rv32i_common/clock_chip.h
new file mode 100644
index 0000000000..ee1c22be92
--- /dev/null
+++ b/chip/mt_scp/rv32i_common/clock_chip.h
@@ -0,0 +1,23 @@
+/* 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.
+ */
+
+/* Clocks, PLL and power settings */
+
+#ifndef __CROS_EC_CLOCK_CHIP_H
+#define __CROS_EC_CLOCK_CHIP_H
+
+#include "registers.h"
+
+enum scp_clock_source {
+ SCP_CLK_26M = CLK_SW_SEL_26M,
+ SCP_CLK_32K = CLK_SW_SEL_32K,
+ SCP_CLK_ULPOSC2 = CLK_SW_SEL_ULPOSC2,
+ SCP_CLK_ULPOSC1 = CLK_SW_SEL_ULPOSC1,
+};
+
+/* Switches to use 'src' clock */
+void clock_select_clock(enum scp_clock_source src);
+
+#endif /* __CROS_EC_CLOCK_CHIP_H */
diff --git a/chip/mt_scp/rv32i_common/config_chip.h b/chip/mt_scp/rv32i_common/config_chip.h
new file mode 100644
index 0000000000..ac53d51732
--- /dev/null
+++ b/chip/mt_scp/rv32i_common/config_chip.h
@@ -0,0 +1,57 @@
+/* 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_CONFIG_CHIP_H
+#define __CROS_EC_CONFIG_CHIP_H
+
+#include "core/riscv-rv32i/config_core.h"
+
+/* Interval between HOOK_TICK notifications */
+#define HOOK_TICK_INTERVAL_MS 500
+#define HOOK_TICK_INTERVAL (HOOK_TICK_INTERVAL_MS * MSEC)
+
+/* RW only, no flash */
+#undef CONFIG_FW_INCLUDE_RO
+#define CONFIG_RO_MEM_OFF 0
+#define CONFIG_RO_SIZE 0
+#define CONFIG_RW_MEM_OFF 0
+#define CONFIG_RW_SIZE 0x40000 /* 256KB */
+#define CONFIG_EC_WRITABLE_STORAGE_OFF 0
+#define CONFIG_EC_PROTECTED_STORAGE_OFF 0
+#define CONFIG_RO_STORAGE_OFF 0
+#define CONFIG_RW_STORAGE_OFF 0
+#define CONFIG_PROGRAM_MEMORY_BASE 0
+#define CONFIG_PROGRAM_MEMORY_BASE_LOAD 0x10500000
+#define CONFIG_MAPPED_STORAGE_BASE 0
+
+/* Unsupported features/commands */
+#undef CONFIG_CMD_FLASHINFO
+#undef CONFIG_CMD_POWER_AP
+#undef CONFIG_FLASH_CROS
+#undef CONFIG_FLASH_PHYSICAL
+#undef CONFIG_FMAP
+#undef CONFIG_HIBERNATE
+#undef CONFIG_LID_SWITCH
+
+/* Task stack size */
+#define CONFIG_STACK_SIZE 1024
+#define IDLE_TASK_STACK_SIZE 640
+#define SMALLER_TASK_STACK_SIZE 384
+#define TASK_STACK_SIZE 488
+#define LARGER_TASK_STACK_SIZE 640
+#define VENTI_TASK_STACK_SIZE 768
+#define ULTRA_TASK_STACK_SIZE 1056
+#define TRENTA_TASK_STACK_SIZE 1184
+
+/* TODO: need to confirm, placeholder */
+#define GPIO_PIN(num) ((num) / 32), ((num) % 32)
+#define GPIO_PIN_MASK(p, m) .port = (p), .mask = (m)
+#undef CONFIG_TASK_PROFILING
+/* TODO: not yet supported */
+#undef CONFIG_MPU
+/* TODO: core/riscv-rv32i pollution */
+#define __ram_code
+
+#endif /* __CROS_EC_CONFIG_CHIP_H */
diff --git a/chip/mt_scp/rv32i_common/csr.h b/chip/mt_scp/rv32i_common/csr.h
new file mode 100644
index 0000000000..7c767d0592
--- /dev/null
+++ b/chip/mt_scp/rv32i_common/csr.h
@@ -0,0 +1,111 @@
+/* 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.
+ */
+
+/* Control and status register */
+
+/* TODO: move to core/riscv-rv32i? */
+
+#ifndef __CROS_EC_CSR_H
+#define __CROS_EC_CSR_H
+
+#include "common.h"
+
+static inline uint32_t read_csr(uint32_t reg)
+{
+ uint32_t val;
+
+ asm volatile("csrr %0, %1" : "=r"(val) : "i"(reg));
+ return val;
+}
+
+static inline void write_csr(uint32_t reg, uint32_t val)
+{
+ asm volatile ("csrw %0, %1" :: "i"(reg), "r"(val));
+}
+
+static inline uint32_t set_csr(uint32_t reg, uint32_t bit)
+{
+ uint32_t val;
+
+ asm volatile ("csrrs %0, %1, %2" : "=r"(val) : "i"(reg), "r"(bit));
+ return val;
+}
+
+static inline uint32_t clear_csr(uint32_t reg, uint32_t bit)
+{
+ uint32_t val;
+
+ asm volatile ("csrrc %0, %1, %2" : "=r"(val) : "i"(reg), "r"(bit));
+ return val;
+}
+
+/* VIC */
+#define CSR_VIC_MICAUSE (0x5c0)
+#define CSR_VIC_MIEMS (0x5c2)
+#define CSR_VIC_MIPEND_G0 (0x5d0)
+#define CSR_VIC_MIMASK_G0 (0x5d8)
+#define CSR_VIC_MIWAKEUP_G0 (0x5e0)
+#define CSR_VIC_MILSEL_G0 (0x5e8)
+#define CSR_VIC_MIEMASK_G0 (0x5f0)
+
+/* centralized control enable */
+#define CSR_MCTREN (0x7c0)
+/* I$, D$, ITCM, DTCM, BTB, RAS, VIC, CG, mpu */
+#define CSR_MCTREN_ICACHE BIT(0)
+#define CSR_MCTREN_DCACHE BIT(1)
+#define CSR_MCTREN_ITCM BIT(2)
+#define CSR_MCTREN_DTCM BIT(3)
+#define CSR_MCTREN_BTB BIT(4)
+#define CSR_MCTREN_RAS BIT(5)
+#define CSR_MCTREN_VIC BIT(6)
+#define CSR_MCTREN_CG BIT(7)
+#define CSR_MCTREN_MPU BIT(8)
+
+/* MPU */
+#define CSR_MPU_ENTRY_EN (0x9c0)
+#define CSR_MPU_LITCM (0x9dc)
+#define CSR_MPU_LDTCM (0x9dd)
+#define CSR_MPU_HITCM (0x9de)
+#define CSR_MPU_HDTCM (0x9df)
+#define CSR_MPU_L(n) (0x9e0 + (n))
+#define CSR_MPU_H(n) (0x9f0 + (n))
+/* MPU attributes: set if permitted */
+/* Privilege, machine mode in RISC-V. We don't use the flag because
+ * we don't separate user / machine mode in EC OS. */
+#define MPU_ATTR_P BIT(5)
+/* Readable */
+#define MPU_ATTR_R BIT(6)
+/* Writable */
+#define MPU_ATTR_W BIT(7)
+/* Cacheable */
+#define MPU_ATTR_C BIT(8)
+/* Bufferable */
+#define MPU_ATTR_B BIT(9)
+
+/* PMU */
+#define CSR_PMU_MPMUCTR (0xbc0)
+#define CSR_PMU_MPMUCTR_C BIT(0)
+#define CSR_PMU_MPMUCTR_I BIT(1)
+#define CSR_PMU_MPMUCTR_H3 BIT(2)
+#define CSR_PMU_MPMUCTR_H4 BIT(3)
+#define CSR_PMU_MPMUCTR_H5 BIT(4)
+
+#define CSR_PMU_MCYCLE (0xb00)
+#define CSR_PMU_MINSTRET (0xb02)
+#define CSR_PMU_MHPMCOUNTER3 (0xb03)
+#define CSR_PMU_MHPMCOUNTER4 (0xb04)
+#define CSR_PMU_MHPMCOUNTER5 (0xb05)
+
+#define CSR_PMU_MCYCLEH (0xb80)
+#define CSR_PMU_MINSTRETH (0xb82)
+#define CSR_PMU_MHPMCOUNTER3H (0xb83)
+#define CSR_PMU_MHPMCOUNTER4H (0xb84)
+#define CSR_PMU_MHPMCOUNTER5H (0xb85)
+
+#define CSR_PMU_MHPMEVENT3 (0x323)
+#define CSR_PMU_MHPMEVENT4 (0x324)
+#define CSR_PMU_MHPMEVENT5 (0x325)
+
+#endif /* __CROS_EC_CSR_H */
diff --git a/chip/mt_scp/rv32i_common/gpio.c b/chip/mt_scp/rv32i_common/gpio.c
new file mode 100644
index 0000000000..0ca3e3ac25
--- /dev/null
+++ b/chip/mt_scp/rv32i_common/gpio.c
@@ -0,0 +1,21 @@
+/* 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.
+ */
+
+/* GPIO module */
+
+#include "gpio.h"
+
+void gpio_pre_init(void)
+{
+}
+
+test_mockable int gpio_get_level(enum gpio_signal signal)
+{
+ return 0;
+}
+
+void gpio_set_level(enum gpio_signal signal, int value)
+{
+}
diff --git a/chip/mt_scp/rv32i_common/hostcmd.c b/chip/mt_scp/rv32i_common/hostcmd.c
new file mode 100644
index 0000000000..42a463ee56
--- /dev/null
+++ b/chip/mt_scp/rv32i_common/hostcmd.c
@@ -0,0 +1,128 @@
+/* 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 "console.h"
+#include "host_command.h"
+#include "ipi_chip.h"
+#include "util.h"
+
+#define CPRINTF(format, args...) cprintf(CC_IPI, format, ##args)
+#define CPRINTS(format, args...) cprints(CC_IPI, format, ##args)
+
+#define HOSTCMD_MAX_REQUEST_SIZE CONFIG_IPC_SHARED_OBJ_BUF_SIZE
+/* Reserve 1 extra byte for HOSTCMD_TYPE and 3 bytes for padding. */
+#define HOSTCMD_MAX_RESPONSE_SIZE (CONFIG_IPC_SHARED_OBJ_BUF_SIZE - 4)
+#define HOSTCMD_TYPE_HOSTCMD 1
+#define HOSTCMD_TYPE_HOSTEVENT 2
+
+/*
+ * hostcmd and hostevent share the same IPI ID, and use first byte type to
+ * indicate its type.
+ */
+static struct hostcmd_data {
+ const uint8_t type;
+ /* To be compatible with CONFIG_HOSTCMD_ALIGNED */
+ uint8_t response[HOSTCMD_MAX_RESPONSE_SIZE] __aligned(4);
+} hc_cmd_obj = { .type = HOSTCMD_TYPE_HOSTCMD };
+BUILD_ASSERT(sizeof(struct hostcmd_data) == CONFIG_IPC_SHARED_OBJ_BUF_SIZE);
+
+/* Size of the rpmsg device name, should sync across kernel and EC. */
+#define RPMSG_NAME_SIZE 32
+
+/*
+ * The layout of name service message.
+ * This should sync across kernel and EC.
+ */
+struct rpmsg_ns_msg {
+ /* Name of the corresponding rpmsg_driver. */
+ char name[RPMSG_NAME_SIZE];
+ /* IPC ID */
+ uint32_t addr;
+};
+
+static void hostcmd_send_response_packet(struct host_packet *pkt)
+{
+ int ret;
+
+ ret = ipi_send(SCP_IPI_HOST_COMMAND, &hc_cmd_obj,
+ pkt->response_size +
+ offsetof(struct hostcmd_data, response),
+ 1);
+ if (ret)
+ CPRINTS("failed to %s(), ret=%d", __func__, ret);
+}
+
+static void hostcmd_handler(int32_t id, void *buf, uint32_t len)
+{
+ static struct host_packet packet;
+ uint8_t *in_msg = buf;
+ struct ec_host_request *r = (struct ec_host_request *)in_msg;
+ int i;
+
+ if (in_msg[0] != EC_HOST_REQUEST_VERSION) {
+ CPRINTS("ERROR: Protocol V2 is not supported!");
+ CPRINTF("in_msg=[");
+ for (i = 0; i < len; i++)
+ CPRINTF("%02x ", in_msg[i]);
+ CPRINTF("]\n");
+ return;
+ }
+
+ /* Protocol version 3 */
+
+ packet.send_response = hostcmd_send_response_packet;
+
+ /*
+ * Just assign the buffer to request, host_packet_receive
+ * handles the buffer copy.
+ */
+ packet.request = (void *)r;
+ packet.request_temp = NULL;
+ packet.request_max = HOSTCMD_MAX_REQUEST_SIZE;
+ packet.request_size = host_request_expected_size(r);
+
+ packet.response = hc_cmd_obj.response;
+ /* Reserve space for the preamble and trailing byte */
+ packet.response_max = HOSTCMD_MAX_RESPONSE_SIZE;
+ packet.response_size = 0;
+
+ packet.driver_result = EC_RES_SUCCESS;
+
+ host_packet_receive(&packet);
+}
+DECLARE_IPI(SCP_IPI_HOST_COMMAND, hostcmd_handler, 0);
+
+/*
+ * Get protocol information
+ */
+static enum ec_status hostcmd_get_protocol_info(struct host_cmd_handler_args *args)
+{
+ struct ec_response_get_protocol_info *r = args->response;
+
+ memset(r, 0, sizeof(*r));
+ r->protocol_versions |= BIT(3);
+ r->max_request_packet_size = HOSTCMD_MAX_REQUEST_SIZE;
+ r->max_response_packet_size = HOSTCMD_MAX_RESPONSE_SIZE;
+
+ args->response_size = sizeof(*r);
+
+ return EC_SUCCESS;
+}
+DECLARE_HOST_COMMAND(EC_CMD_GET_PROTOCOL_INFO, hostcmd_get_protocol_info,
+ EC_VER_MASK(0));
+
+void hostcmd_init(void)
+{
+ int ret;
+ struct rpmsg_ns_msg ns_msg;
+
+ if (IS_ENABLED(CONFIG_RPMSG_NAME_SERVICE)) {
+ ns_msg.addr = SCP_IPI_HOST_COMMAND;
+ strncpy(ns_msg.name, "cros-ec-rpmsg", RPMSG_NAME_SIZE);
+ ret = ipi_send(SCP_IPI_NS_SERVICE, &ns_msg, sizeof(ns_msg), 1);
+ if (ret)
+ CPRINTS("Failed to announce host command channel");
+ }
+}
diff --git a/chip/mt_scp/rv32i_common/hostcmd.h b/chip/mt_scp/rv32i_common/hostcmd.h
new file mode 100644
index 0000000000..b93f1e725d
--- /dev/null
+++ b/chip/mt_scp/rv32i_common/hostcmd.h
@@ -0,0 +1,12 @@
+/* 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_HOSTCMD_H
+#define __CROS_EC_HOSTCMD_H
+
+/* Initialize hostcmd. */
+void hostcmd_init(void);
+
+#endif /* __CROS_EC_HOSTCMD_H */
diff --git a/chip/mt_scp/rv32i_common/hrtimer.c b/chip/mt_scp/rv32i_common/hrtimer.c
new file mode 100644
index 0000000000..89ffaa2fca
--- /dev/null
+++ b/chip/mt_scp/rv32i_common/hrtimer.c
@@ -0,0 +1,221 @@
+/* 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.
+ */
+
+/*
+ * High-res hardware timer
+ *
+ * SCP hardware 32bit count down timer can be configured to source clock from
+ * 32KHz, 26MHz, BCLK or PCLK. This implementation selects BCLK (ULPOSC1/8) as a
+ * source, countdown mode and converts to micro second value matching common
+ * timer.
+ */
+
+#include "common.h"
+#include "hwtimer.h"
+#include "registers.h"
+#include "task.h"
+
+#define TIMER_SYSTEM 5
+#define TIMER_EVENT 3
+#define TIMER_CLOCK_MHZ 32.5
+#define OVERFLOW_TICKS (TIMER_CLOCK_MHZ * 0x100000000 - 1)
+
+/* High 32-bit for system timer. */
+static uint8_t sys_high;
+/* High 32-bit for event timer. */
+static uint8_t event_high;
+
+static void timer_enable(int n)
+{
+ /* cannot be changed when timer is enabled */
+ SCP_CORE0_TIMER_IRQ_CTRL(n) |= TIMER_IRQ_EN;
+ SCP_CORE0_TIMER_EN(n) |= TIMER_EN;
+}
+
+static void timer_disable(int n)
+{
+ SCP_CORE0_TIMER_EN(n) &= ~TIMER_EN;
+ /* cannot be changed when timer is enabled */
+ SCP_CORE0_TIMER_IRQ_CTRL(n) &= ~TIMER_IRQ_EN;
+}
+
+static int timer_is_irq(int n)
+{
+ return SCP_CORE0_TIMER_IRQ_CTRL(n) & TIMER_IRQ_STATUS;
+}
+
+static void timer_ack_irq(int n)
+{
+ SCP_CORE0_TIMER_IRQ_CTRL(n) |= TIMER_IRQ_CLR;
+}
+
+static void timer_set_reset_value(int n, uint32_t reset_value)
+{
+ /* cannot be changed when timer is enabled */
+ SCP_CORE0_TIMER_RST_VAL(n) = reset_value;
+}
+
+static void timer_set_clock(int n, uint32_t clock_source)
+{
+ SCP_CORE0_TIMER_EN(n) =
+ (SCP_CORE0_TIMER_EN(n) & ~TIMER_CLK_SRC_MASK) | clock_source;
+}
+
+static void timer_reset(int n)
+{
+ timer_disable(n);
+ timer_ack_irq(n);
+ timer_set_reset_value(n, 0xffffffff);
+ timer_set_clock(n, TIMER_CLK_SRC_32K);
+}
+
+/* Convert hardware countdown timer to 64bit countup ticks. */
+static uint64_t timer_read_raw_system(void)
+{
+ uint32_t timer_ctrl = SCP_CORE0_TIMER_IRQ_CTRL(TIMER_SYSTEM);
+ uint32_t sys_high_adj = sys_high;
+
+ /*
+ * If an IRQ is pending, but has not been serviced yet, adjust the
+ * sys_high value.
+ */
+ if (timer_ctrl & TIMER_IRQ_STATUS)
+ sys_high_adj = sys_high ? (sys_high - 1)
+ : (TIMER_CLOCK_MHZ - 1);
+
+ return OVERFLOW_TICKS - (((uint64_t)sys_high_adj << 32) |
+ SCP_CORE0_TIMER_CUR_VAL(TIMER_SYSTEM));
+}
+
+static uint64_t timer_read_raw_event(void)
+{
+ return OVERFLOW_TICKS - (((uint64_t)event_high << 32) |
+ SCP_CORE0_TIMER_CUR_VAL(TIMER_EVENT));
+}
+
+static void timer_reload(int n, uint32_t value)
+{
+ timer_disable(n);
+ timer_set_reset_value(n, value);
+ timer_enable(n);
+}
+
+static int timer_reload_event_high(void)
+{
+ if (event_high) {
+ if (SCP_CORE0_TIMER_RST_VAL(TIMER_EVENT) == 0xffffffff)
+ timer_enable(TIMER_EVENT);
+ else
+ timer_reload(TIMER_EVENT, 0xffffffff);
+ event_high--;
+ return 1;
+ }
+
+ timer_disable(TIMER_EVENT);
+ return 0;
+}
+
+int __hw_clock_source_init(uint32_t start_t)
+{
+ int t;
+
+ /* enable clock gate */
+ SCP_SET_CLK_CG |= CG_TIMER_MCLK | CG_TIMER_BCLK;
+
+ /* reset all timer, select 32768Hz clock source */
+ for (t = 0; t < NUM_TIMERS; ++t)
+ timer_reset(t);
+
+ /* System timestamp timer */
+ timer_set_clock(TIMER_SYSTEM, TIMER_CLK_SRC_BCLK);
+ sys_high = TIMER_CLOCK_MHZ - 1;
+ timer_set_reset_value(TIMER_SYSTEM, 0xffffffff);
+ task_enable_irq(SCP_IRQ_TIMER(TIMER_SYSTEM));
+ timer_enable(TIMER_SYSTEM);
+
+ /* Event tick timer */
+ timer_set_clock(TIMER_EVENT, TIMER_CLK_SRC_BCLK);
+ task_enable_irq(SCP_IRQ_TIMER(TIMER_EVENT));
+
+ return SCP_IRQ_TIMER(TIMER_SYSTEM);
+}
+
+uint32_t __hw_clock_source_read(void)
+{
+ return timer_read_raw_system() / TIMER_CLOCK_MHZ;
+}
+
+uint32_t __hw_clock_event_get(void)
+{
+ return (timer_read_raw_event() + timer_read_raw_system())
+ / TIMER_CLOCK_MHZ;
+}
+
+void __hw_clock_event_clear(void)
+{
+ /* c1ea4, magic number for clear state */
+ timer_disable(TIMER_EVENT);
+ timer_set_reset_value(TIMER_EVENT, 0x0000c1ea4);
+ event_high = 0;
+}
+
+void __hw_clock_event_set(uint32_t deadline)
+{
+ uint64_t deadline_raw = (uint64_t)deadline * TIMER_CLOCK_MHZ;
+ uint64_t now_raw = timer_read_raw_system();
+ uint32_t event_deadline;
+
+ if (deadline_raw > now_raw) {
+ deadline_raw -= now_raw;
+ event_deadline = (uint32_t)deadline_raw;
+ event_high = deadline_raw >> 32;
+ } else {
+ event_deadline = 1;
+ event_high = 0;
+ }
+
+ if (event_deadline)
+ timer_reload(TIMER_EVENT, event_deadline);
+ else
+ timer_reload_event_high();
+}
+
+static void irq_group6_handler(void)
+{
+ extern volatile int ec_int;
+
+ switch (ec_int) {
+ case SCP_IRQ_TIMER(TIMER_EVENT):
+ if (timer_is_irq(TIMER_EVENT)) {
+ timer_ack_irq(TIMER_EVENT);
+
+ if (!timer_reload_event_high())
+ process_timers(0);
+
+ task_clear_pending_irq(ec_int);
+ }
+ break;
+ case SCP_IRQ_TIMER(TIMER_SYSTEM):
+ /* If this is a hardware irq, check overflow */
+ if (!in_soft_interrupt_context()) {
+ timer_ack_irq(TIMER_SYSTEM);
+
+ if (sys_high) {
+ --sys_high;
+ process_timers(0);
+ } else {
+ /* Overflow, reload system timer */
+ sys_high = TIMER_CLOCK_MHZ - 1;
+ process_timers(1);
+ }
+
+ task_clear_pending_irq(ec_int);
+ } else {
+ process_timers(0);
+ }
+ break;
+ }
+}
+DECLARE_IRQ(6, irq_group6_handler, 0);
diff --git a/chip/mt_scp/rv32i_common/intc.c b/chip/mt_scp/rv32i_common/intc.c
new file mode 100644
index 0000000000..bd7416c31e
--- /dev/null
+++ b/chip/mt_scp/rv32i_common/intc.c
@@ -0,0 +1,260 @@
+/* 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.
+ */
+
+/* INTC control module */
+
+#include "console.h"
+#include "csr.h"
+#include "registers.h"
+
+/*
+ * INTC_GRP_0 is reserved. See swirq of syscall_handler() in
+ * core/riscv-rv32i/task.c for more details.
+ *
+ * Lower group has higher priority.
+ */
+enum INTC_GROUP {
+ INTC_GRP_0 = 0x0,
+ INTC_GRP_1,
+ INTC_GRP_2,
+ INTC_GRP_3,
+ INTC_GRP_4,
+ INTC_GRP_5,
+ INTC_GRP_6,
+ INTC_GRP_7,
+ INTC_GRP_8,
+ INTC_GRP_9,
+ INTC_GRP_10,
+ INTC_GRP_11,
+ INTC_GRP_12,
+ INTC_GRP_13,
+ INTC_GRP_14,
+};
+
+static struct {
+ uint8_t group;
+} irqs[SCP_INTC_IRQ_COUNT] = {
+ /* 0 */
+ [SCP_IRQ_GIPC_IN0] = { INTC_GRP_7 },
+ [SCP_IRQ_GIPC_IN1] = { INTC_GRP_0 },
+ [SCP_IRQ_GIPC_IN2] = { INTC_GRP_0 },
+ [SCP_IRQ_GIPC_IN3] = { INTC_GRP_0 },
+ /* 4 */
+ [SCP_IRQ_SPM] = { INTC_GRP_0 },
+ [SCP_IRQ_AP_CIRQ] = { INTC_GRP_0 },
+ [SCP_IRQ_EINT] = { INTC_GRP_0 },
+ [SCP_IRQ_PMIC] = { INTC_GRP_0 },
+ /* 8 */
+ [SCP_IRQ_UART0_TX] = { INTC_GRP_12 },
+ [SCP_IRQ_UART1_TX] = { INTC_GRP_12 },
+ [SCP_IRQ_I2C0] = { INTC_GRP_0 },
+ [SCP_IRQ_I2C1_0] = { INTC_GRP_0 },
+ /* 12 */
+ [SCP_IRQ_BUS_DBG_TRACKER] = { INTC_GRP_0 },
+ [SCP_IRQ_CLK_CTRL] = { INTC_GRP_0 },
+ [SCP_IRQ_VOW] = { INTC_GRP_0 },
+ [SCP_IRQ_TIMER0] = { INTC_GRP_6 },
+ /* 16 */
+ [SCP_IRQ_TIMER1] = { INTC_GRP_6 },
+ [SCP_IRQ_TIMER2] = { INTC_GRP_6 },
+ [SCP_IRQ_TIMER3] = { INTC_GRP_6 },
+ [SCP_IRQ_TIMER4] = { INTC_GRP_6 },
+ /* 20 */
+ [SCP_IRQ_TIMER5] = { INTC_GRP_6 },
+ [SCP_IRQ_OS_TIMER] = { INTC_GRP_0 },
+ [SCP_IRQ_UART0_RX] = { INTC_GRP_12 },
+ [SCP_IRQ_UART1_RX] = { INTC_GRP_12 },
+ /* 24 */
+ [SCP_IRQ_GDMA] = { INTC_GRP_0 },
+ [SCP_IRQ_AUDIO] = { INTC_GRP_0 },
+ [SCP_IRQ_MD_DSP] = { INTC_GRP_0 },
+ [SCP_IRQ_ADSP] = { INTC_GRP_0 },
+ /* 28 */
+ [SCP_IRQ_CPU_TICK] = { INTC_GRP_0 },
+ [SCP_IRQ_SPI0] = { INTC_GRP_0 },
+ [SCP_IRQ_SPI1] = { INTC_GRP_0 },
+ [SCP_IRQ_SPI2] = { INTC_GRP_0 },
+ /* 32 */
+ [SCP_IRQ_NEW_INFRA_SYS_CIRQ] = { INTC_GRP_0 },
+ [SCP_IRQ_DBG] = { INTC_GRP_0 },
+ [SCP_IRQ_CCIF0] = { INTC_GRP_0 },
+ [SCP_IRQ_CCIF1] = { INTC_GRP_0 },
+ /* 36 */
+ [SCP_IRQ_CCIF2] = { INTC_GRP_0 },
+ [SCP_IRQ_WDT] = { INTC_GRP_0 },
+ [SCP_IRQ_USB0] = { INTC_GRP_0 },
+ [SCP_IRQ_USB1] = { INTC_GRP_0 },
+ /* 40 */
+ [SCP_IRQ_DPMAIF] = { INTC_GRP_0 },
+ [SCP_IRQ_INFRA] = { INTC_GRP_0 },
+ [SCP_IRQ_CLK_CTRL_CORE] = { INTC_GRP_0 },
+ [SCP_IRQ_CLK_CTRL2_CORE] = { INTC_GRP_0 },
+ /* 44 */
+ [SCP_IRQ_CLK_CTRL2] = { INTC_GRP_0 },
+ [SCP_IRQ_GIPC_IN4] = { INTC_GRP_0 },
+ [SCP_IRQ_PERIBUS_TIMEOUT] = { INTC_GRP_0 },
+ [SCP_IRQ_INFRABUS_TIMEOUT] = { INTC_GRP_0 },
+ /* 48 */
+ [SCP_IRQ_MET0] = { INTC_GRP_0 },
+ [SCP_IRQ_MET1] = { INTC_GRP_0 },
+ [SCP_IRQ_MET2] = { INTC_GRP_0 },
+ [SCP_IRQ_MET3] = { INTC_GRP_0 },
+ /* 52 */
+ [SCP_IRQ_AP_WDT] = { INTC_GRP_0 },
+ [SCP_IRQ_L2TCM_SEC_VIO] = { INTC_GRP_0 },
+ [SCP_IRQ_CPU_TICK1] = { INTC_GRP_0 },
+ [SCP_IRQ_MAD_DATAIN] = { INTC_GRP_0 },
+ /* 56 */
+ [SCP_IRQ_I3C0_IBI_WAKE] = { INTC_GRP_0 },
+ [SCP_IRQ_I3C1_IBI_WAKE] = { INTC_GRP_0 },
+ [SCP_IRQ_I3C2_IBI_WAKE] = { INTC_GRP_0 },
+ [SCP_IRQ_APU_ENGINE] = { INTC_GRP_0 },
+ /* 60 */
+ [SCP_IRQ_MBOX0] = { INTC_GRP_0 },
+ [SCP_IRQ_MBOX1] = { INTC_GRP_0 },
+ [SCP_IRQ_MBOX2] = { INTC_GRP_0 },
+ [SCP_IRQ_MBOX3] = { INTC_GRP_0 },
+ /* 64 */
+ [SCP_IRQ_MBOX4] = { INTC_GRP_0 },
+ [SCP_IRQ_SYS_CLK_REQ] = { INTC_GRP_0 },
+ [SCP_IRQ_BUS_REQ] = { INTC_GRP_0 },
+ [SCP_IRQ_APSRC_REQ] = { INTC_GRP_0 },
+ /* 68 */
+ [SCP_IRQ_APU_MBOX] = { INTC_GRP_0 },
+ [SCP_IRQ_DEVAPC_SECURE_VIO] = { INTC_GRP_0 },
+ /* 72 */
+ /* 76 */
+ [SCP_IRQ_I2C1_2] = { INTC_GRP_0 },
+ [SCP_IRQ_I2C2] = { INTC_GRP_0 },
+ /* 80 */
+ [SCP_IRQ_AUD2AUDIODSP] = { INTC_GRP_0 },
+ [SCP_IRQ_AUD2AUDIODSP_2] = { INTC_GRP_0 },
+ [SCP_IRQ_CONN2ADSP_A2DPOL] = { INTC_GRP_0 },
+ [SCP_IRQ_CONN2ADSP_BTCVSD] = { INTC_GRP_0 },
+ /* 84 */
+ [SCP_IRQ_CONN2ADSP_BLEISO] = { INTC_GRP_0 },
+ [SCP_IRQ_PCIE2ADSP] = { INTC_GRP_0 },
+ [SCP_IRQ_APU2ADSP_ENGINE] = { INTC_GRP_0 },
+ [SCP_IRQ_APU2ADSP_MBOX] = { INTC_GRP_0 },
+ /* 88 */
+ [SCP_IRQ_CCIF3] = { INTC_GRP_0 },
+ [SCP_IRQ_I2C_DMA0] = { INTC_GRP_0 },
+ [SCP_IRQ_I2C_DMA1] = { INTC_GRP_0 },
+ [SCP_IRQ_I2C_DMA2] = { INTC_GRP_0 },
+ /* 92 */
+ [SCP_IRQ_I2C_DMA3] = { INTC_GRP_0 },
+};
+BUILD_ASSERT(ARRAY_SIZE(irqs) == SCP_INTC_IRQ_COUNT);
+
+/*
+ * Find current interrupt source.
+ *
+ * Lower group has higher priority.
+ * Higher INT number has higher priority.
+ */
+int chip_get_ec_int(void)
+{
+ extern volatile int ec_int;
+ unsigned int group, sta;
+ int word;
+
+ if (!SCP_CORE0_INTC_IRQ_OUT)
+ goto error;
+
+ group = read_csr(CSR_VIC_MICAUSE);
+
+ for (word = SCP_INTC_GRP_LEN - 1; word >= 0; --word) {
+ sta = SCP_CORE0_INTC_IRQ_GRP_STA(group, word);
+ if (sta) {
+ ec_int = __fls(sta) + word * 32;
+ return ec_int;
+ }
+ }
+
+error:
+ /* unreachable, SCP crashes and dumps registers after returning */
+ return -1;
+}
+
+int chip_get_intc_group(int irq)
+{
+ return irqs[irq].group;
+}
+
+void chip_enable_irq(int irq)
+{
+ unsigned int word, group, mask;
+
+ word = SCP_INTC_WORD(irq);
+ group = irqs[irq].group;
+ mask = BIT(SCP_INTC_BIT(irq));
+
+ /* disable interrupt */
+ SCP_CORE0_INTC_IRQ_EN(word) &= ~mask;
+ /* set group */
+ SCP_CORE0_INTC_IRQ_GRP(group, word) |= mask;
+ /* set as a wakeup source */
+ SCP_CORE0_INTC_SLP_WAKE_EN(word) |= mask;
+ /* enable interrupt */
+ SCP_CORE0_INTC_IRQ_EN(word) |= mask;
+}
+
+void chip_disable_irq(int irq)
+{
+ /*
+ * Disabling INTC IRQ in runtime is unstable in MT8192 SCP.
+ * See b/163682416#comment17.
+ *
+ * Ideally, this function will be removed by LTO.
+ */
+ ccprints("WARNING: %s is unsupported", __func__);
+}
+
+void chip_clear_pending_irq(int irq)
+{
+ unsigned int group = irqs[irq].group;
+
+ /* must clear interrupt source before writing this */
+ write_csr(CSR_VIC_MIEMS, group);
+}
+
+int chip_trigger_irq(int irq)
+{
+ extern volatile int ec_int;
+
+ ec_int = irq;
+ return irqs[irq].group;
+}
+
+void chip_init_irqs(void)
+{
+ unsigned int word, group;
+
+ /* INTC init */
+ /* clear enable and wakeup settings */
+ for (word = 0; word < SCP_INTC_GRP_LEN; ++word) {
+ SCP_CORE0_INTC_IRQ_EN(word) = 0x0;
+ SCP_CORE0_INTC_SLP_WAKE_EN(word) = 0x0;
+
+ /* clear group settings */
+ for (group = 0; group < SCP_INTC_GRP_COUNT; ++group)
+ SCP_CORE0_INTC_IRQ_GRP(group, word) = 0x0;
+ }
+ /* reset to default polarity */
+ SCP_CORE0_INTC_IRQ_POL(0) = SCP_INTC_IRQ_POL0;
+ SCP_CORE0_INTC_IRQ_POL(1) = SCP_INTC_IRQ_POL1;
+ SCP_CORE0_INTC_IRQ_POL(2) = SCP_INTC_IRQ_POL2;
+
+ /* GVIC init */
+ /* enable all groups as interrupt sources */
+ write_csr(CSR_VIC_MIMASK_G0, 0xffffffff);
+ /* use level trigger */
+ write_csr(CSR_VIC_MILSEL_G0, 0xffffffff);
+ /* enable all groups as wakeup sources */
+ write_csr(CSR_VIC_MIWAKEUP_G0, 0xffffffff);
+
+ /* enable GVIC */
+ set_csr(CSR_MCTREN, CSR_MCTREN_VIC);
+}
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);
diff --git a/chip/mt_scp/rv32i_common/ipi_chip.h b/chip/mt_scp/rv32i_common/ipi_chip.h
new file mode 100644
index 0000000000..3484358a86
--- /dev/null
+++ b/chip/mt_scp/rv32i_common/ipi_chip.h
@@ -0,0 +1,102 @@
+/* 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
+
+#include "common.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
+ */
+#define VCODEC_CAPABILITY_4K_DISABLED BIT(4)
+#define VDEC_CAP_MM21 BIT(5)
+#define VDEC_CAP_MT21C BIT(6)
+#define VDEC_CAP_H264_SLICE BIT(8)
+#define VDEC_CAP_VP8_FRAME BIT(9)
+#define VDEC_CAP_VP9_FRAME BIT(10)
+
+/*
+ * Video encoder supported capability:
+ * BIT(0): enable 4K
+ */
+#define VENC_CAP_4K BIT(0)
+
+#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);
+
+/* IPI tables */
+extern void (*const ipi_handler_table[])(int32_t, void *, uint32_t);
+extern int *const 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_check##_id { \
+ int tmp1[_id < IPI_COUNT ? 1 : -1]; \
+ int tmp2[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/rv32i_common/ipi_table.c b/chip/mt_scp/rv32i_common/ipi_table.c
new file mode 100644
index 0000000000..8fe3f1e598
--- /dev/null
+++ b/chip/mt_scp/rv32i_common/ipi_table.c
@@ -0,0 +1,67 @@
+/* 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.
+ *
+ * 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 const name[] \
+ __attribute__((aligned(4), used, section(".rodata.ipi"))) = {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 */
+
+/*
+ * Include generated IPI table (by util/gen_ipi_table). The contents originate
+ * from IPI_COUNT definition in board.h
+ */
+#include "ipi_table_gen.inc"
+
+#if PASS == 1
+#undef PASS
+#define PASS 2
+#include "ipi_table.c"
+BUILD_ASSERT(ARRAY_SIZE(ipi_handler_table) == IPI_COUNT);
+BUILD_ASSERT(ARRAY_SIZE(ipi_wakeup_table) == IPI_COUNT);
+#endif
diff --git a/chip/mt_scp/rv32i_common/memmap.c b/chip/mt_scp/rv32i_common/memmap.c
new file mode 100644
index 0000000000..a666bb23d7
--- /dev/null
+++ b/chip/mt_scp/rv32i_common/memmap.c
@@ -0,0 +1,114 @@
+/* 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 "cache.h"
+#include "registers.h"
+#include "stdint.h"
+
+/*
+ * Map SCP address (bits 31~28) to AP address
+ *
+ * SCP address AP address Note
+ *
+ * 0x0000_0000 SRAM
+ * 0x1000_0000 0x5000_0000 CPU DRAM
+ * 0x2000_0000 0x7000_0000
+ * 0x3000_0000
+ *
+ * 0x4000_0000
+ * 0x5000_0000 0x0000_0000
+ * 0x6000_0000 0x1000_0000
+ * 0x7000_0000 0xa000_0000
+ *
+ * 0x8000_0000
+ * 0x9000_0000 0x8000_0000
+ * 0xa000_0000 0x9000_0000
+ * 0xb000_0000
+ *
+ * 0xc000_0000 0x8000_0000
+ * 0xd000_0000 0x2000_0000
+ * 0xe000_0000 0x3000_0000
+ * 0xf000_0000 0x6000_0000
+ */
+
+#define REMAP_ADDR_SHIFT 28
+#define REMAP_ADDR_LSB_MASK (BIT(REMAP_ADDR_SHIFT) - 1)
+#define REMAP_ADDR_MSB_MASK ((~0) << REMAP_ADDR_SHIFT)
+#define MAP_INVALID 0xff
+
+static const uint8_t addr_map[16] = {
+ MAP_INVALID, /* SRAM */
+ 0x5, /* ext_addr_0x1 */
+ 0x7, /* ext_addr_0x2 */
+ MAP_INVALID, /* no ext_addr_0x3 */
+
+ MAP_INVALID, /* no ext_addr_0x4 */
+ 0x0, /* ext_addr_0x5 */
+ 0x1, /* ext_addr_0x6 */
+ 0xa, /* ext_addr_0x7 */
+
+ MAP_INVALID, /* no ext_addr_0x8 */
+ 0x8, /* ext_addr_0x9 */
+ 0x9, /* ext_addr_0xa */
+ MAP_INVALID, /* no ext_addr_0xb */
+
+ 0x8, /* ext_addr_0xc */
+ 0x2, /* ext_addr_0xd */
+ 0x3, /* ext_addr_0xe */
+ 0x6, /* ext_addr_0xf */
+};
+
+void memmap_init(void)
+{
+ SCP_R_REMAP_0X0123 =
+ (uint32_t)addr_map[0x1] << 8 |
+ (uint32_t)addr_map[0x2] << 16;
+
+ SCP_R_REMAP_0X4567 =
+ (uint32_t)addr_map[0x5] << 8 |
+ (uint32_t)addr_map[0x6] << 16 |
+ (uint32_t)addr_map[0x7] << 24;
+
+ SCP_R_REMAP_0X89AB =
+ (uint32_t)addr_map[0x9] << 8 |
+ (uint32_t)addr_map[0xa] << 16;
+
+ SCP_R_REMAP_0XCDEF =
+ (uint32_t)addr_map[0xc] |
+ (uint32_t)addr_map[0xd] << 8 |
+ (uint32_t)addr_map[0xe] << 16 |
+ (uint32_t)addr_map[0xf] << 24;
+
+ cache_init();
+}
+
+int memmap_ap_to_scp(uintptr_t ap_addr, uintptr_t *scp_addr)
+{
+ int i;
+ uint8_t msb = ap_addr >> REMAP_ADDR_SHIFT;
+
+ for (i = 0; i < ARRAY_SIZE(addr_map); ++i) {
+ if (addr_map[i] != msb)
+ continue;
+
+ *scp_addr = (ap_addr & REMAP_ADDR_LSB_MASK) |
+ (i << REMAP_ADDR_SHIFT);
+ return EC_SUCCESS;
+ }
+
+ return EC_ERROR_INVAL;
+}
+
+int memmap_scp_to_ap(uintptr_t scp_addr, uintptr_t *ap_addr)
+{
+ int i = scp_addr >> REMAP_ADDR_SHIFT;
+
+ if (addr_map[i] == MAP_INVALID)
+ return EC_ERROR_INVAL;
+
+ *ap_addr = (scp_addr & REMAP_ADDR_LSB_MASK) |
+ (addr_map[i] << REMAP_ADDR_SHIFT);
+ return EC_SUCCESS;
+}
diff --git a/chip/mt_scp/rv32i_common/memmap.h b/chip/mt_scp/rv32i_common/memmap.h
new file mode 100644
index 0000000000..0857c9a89e
--- /dev/null
+++ b/chip/mt_scp/rv32i_common/memmap.h
@@ -0,0 +1,31 @@
+/* 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_MEMMAP_H
+#define __CROS_EC_MEMMAP_H
+
+#include "stdint.h"
+
+void memmap_init(void);
+
+/**
+ * Translate AP addr to SCP addr.
+ *
+ * @param ap_addr AP address to translate
+ * @param scp_addr Translated AP address
+ * @return EC_SUCCESS or EC_ERROR_INVAL
+ */
+int memmap_ap_to_scp(uintptr_t ap_addr, uintptr_t *scp_addr);
+
+/**
+ * Translate SCP addr to AP addr.
+ *
+ * @param scp_addr SCP address to translate
+ * @param ap_addr Translated SCP address
+ * @return EC_SUCCESS or EC_ERROR_INVAL
+ */
+int memmap_scp_to_ap(uintptr_t scp_addr, uintptr_t *ap_addr);
+
+#endif /* #ifndef __CROS_EC_MEMMAP_H */
diff --git a/chip/mt_scp/rv32i_common/registers.h b/chip/mt_scp/rv32i_common/registers.h
new file mode 100644
index 0000000000..a64c446c8c
--- /dev/null
+++ b/chip/mt_scp/rv32i_common/registers.h
@@ -0,0 +1,391 @@
+/* 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.
+ */
+
+/* Register map */
+
+#ifndef __CROS_EC_REGISTERS_H
+#define __CROS_EC_REGISTERS_H
+
+#include "common.h"
+#include "compile_time_macros.h"
+
+#define UNIMPLEMENTED_GPIO_BANK 0
+
+#define SCP_REG_BASE 0x70000000
+
+/* clock control */
+#define SCP_CLK_CTRL_BASE (SCP_REG_BASE + 0x21000)
+/* clock source select */
+#define SCP_CLK_SW_SEL REG32(SCP_CLK_CTRL_BASE + 0x0000)
+#define CLK_SW_SEL_26M 0
+#define CLK_SW_SEL_32K 1
+#define CLK_SW_SEL_ULPOSC2 2
+#define CLK_SW_SEL_ULPOSC1 3
+#define SCP_CLK_ENABLE REG32(SCP_CLK_CTRL_BASE + 0x0004)
+#define CLK_HIGH_EN BIT(1) /* ULPOSC */
+#define CLK_HIGH_CG BIT(2)
+/* system clock counter value */
+#define SCP_CLK_SYS_VAL REG32(SCP_CLK_CTRL_BASE + 0x0014)
+#define CLK_SYS_VAL_MASK (0x3ff << 0)
+#define CLK_SYS_VAL_VAL(v) ((v) & CLK_SYS_VAL_MASK)
+/* ULPOSC clock counter value */
+#define SCP_CLK_HIGH_VAL REG32(SCP_CLK_CTRL_BASE + 0x0018)
+#define CLK_HIGH_VAL_MASK (0x1f << 0)
+#define CLK_HIGH_VAL_VAL(v) ((v) & CLK_HIGH_VAL_MASK)
+/* sleep mode control */
+#define SCP_SLEEP_CTRL REG32(SCP_CLK_CTRL_BASE + 0x0020)
+#define SLP_CTRL_EN BIT(0)
+#define VREQ_COUNT_MASK (0x7F << 1)
+#define VREQ_COUNT_VAL(v) (((v) << 1) & VREQ_COUNT_MASK)
+#define SPM_SLP_MODE BIT(8)
+/* clock divider select */
+#define SCP_CLK_DIV_SEL REG32(SCP_CLK_CTRL_BASE + 0x0024)
+#define CLK_DIV_SEL1 0
+#define CLK_DIV_SEL2 1
+#define CLK_DIV_SEL4 2
+#define CLK_DIV_SEL3 3
+/* clock gate */
+#define SCP_SET_CLK_CG REG32(SCP_CLK_CTRL_BASE + 0x0030)
+#define CG_TIMER_MCLK BIT(0)
+#define CG_TIMER_BCLK BIT(1)
+#define CG_MAD_MCLK BIT(2)
+#define CG_I2C_MCLK BIT(3)
+#define CG_I2C_BCLK BIT(4)
+#define CG_GPIO_MCLK BIT(5)
+#define CG_AP2P_MCLK BIT(6)
+#define CG_UART0_MCLK BIT(7)
+#define CG_UART0_BCLK BIT(8)
+#define CG_UART0_RST BIT(9)
+#define CG_UART1_MCLK BIT(10)
+#define CG_UART1_BCLK BIT(11)
+#define CG_UART1_RST BIT(12)
+#define CG_SPI0 BIT(13)
+#define CG_SPI1 BIT(14)
+#define CG_SPI2 BIT(15)
+#define CG_DMA_CH0 BIT(16)
+#define CG_DMA_CH1 BIT(17)
+#define CG_DMA_CH2 BIT(18)
+#define CG_DMA_CH3 BIT(19)
+#define CG_I3C0 BIT(21)
+#define CG_I3C1 BIT(22)
+#define CG_DMA2_CH0 BIT(23)
+#define CG_DMA2_CH1 BIT(24)
+#define CG_DMA2_CH2 BIT(25)
+#define CG_DMA2_CH3 BIT(26)
+/* UART clock select */
+#define SCP_UART_CK_SEL REG32(SCP_CLK_CTRL_BASE + 0x0044)
+#define UART0_CK_SEL_SHIFT 0
+#define UART0_CK_SEL_MASK (0x3 << UART0_CK_SEL_SHIFT)
+#define UART0_CK_SEL_VAL(v) ((v) & UART0_CK_SEL_MASK)
+#define UART0_CK_SW_STATUS_MASK (0xf << 8)
+#define UART0_CK_SW_STATUS_VAL(v) ((v) & UART0_CK_SW_STATUS_MASK)
+#define UART1_CK_SEL_SHIFT 16
+#define UART1_CK_SEL_MASK (0x3 << UART1_CK_SEL_SHIFT)
+#define UART1_CK_SEL_VAL(v) ((v) & UART1_CK_SEL_MASK)
+#define UART1_CK_SW_STATUS_MASK (0xf << 24)
+#define UART1_CK_SW_STATUS_VAL(v) ((v) & UART1_CK_SW_STATUS_MASK)
+#define UART_CK_SEL_26M 0
+#define UART_CK_SEL_32K 1
+#define UART_CK_SEL_ULPOSC 2
+#define UART_CK_SW_STATUS_26M BIT(0)
+#define UART_CK_SW_STATUS_32K BIT(1)
+#define UART_CK_SW_STATUS_ULPOS BIT(2)
+/* BCLK clock select */
+#define SCP_BCLK_CK_SEL REG32(SCP_CLK_CTRL_BASE + 0x0048)
+#define BCLK_CK_SEL_SYS_DIV8 0
+#define BCLK_CK_SEL_32K 1
+#define BCLK_CK_SEL_ULPOSC_DIV8 2
+/* VREQ control */
+#define SCP_CPU_VREQ_CTRL REG32(SCP_CLK_CTRL_BASE + 0x0054)
+#define VREQ_SEL BIT(0)
+#define VREQ_VALUE BIT(4)
+#define VREQ_EXT_SEL BIT(8)
+#define VREQ_DVFS_SEL BIT(16)
+#define VREQ_DVFS_VALUE BIT(20)
+#define VREQ_DVFS_EXT_SEL BIT(24)
+#define VREQ_SRCLKEN_SEL BIT(27)
+#define VREQ_SRCLKEN_VALUE BIT(28)
+/* clock on control */
+#define SCP_CLK_HIGH_CORE_CG REG32(SCP_CLK_CTRL_BASE + 0x005C)
+#define HIGH_CORE_CG BIT(1)
+#define SCP_CLK_ON_CTRL REG32(SCP_CLK_CTRL_BASE + 0x006C)
+#define HIGH_AO BIT(0)
+#define HIGH_DIS_SUB BIT(1)
+#define HIGH_CG_AO BIT(2)
+#define HIGH_CORE_AO BIT(4)
+#define HIGH_CORE_DIS_SUB BIT(5)
+#define HIGH_CORE_CG_AO BIT(6)
+/* clock general control */
+#define SCP_CLK_CTRL_GENERAL_CTRL REG32(SCP_CLK_CTRL_BASE + 0x009C)
+#define VREQ_PMIC_WRAP_SEL (0x2)
+
+/* system control */
+#define SCP_SYS_CTRL REG32(SCP_REG_BASE + 0x24000)
+#define AUTO_DDREN BIT(9)
+
+/* 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)
+#define SCP_GIPC_IN_SET REG32(SCP_REG_BASE + 0x24098)
+#define SCP_GIPC_IN_CLR REG32(SCP_REG_BASE + 0x2409C)
+#define GIPC_IN(n) BIT(n)
+
+/* UART */
+#define SCP_UART_COUNT 2
+#define UART_TX_IRQ(n) CONCAT3(SCP_IRQ_UART, n, _TX)
+#define UART_RX_IRQ(n) CONCAT3(SCP_IRQ_UART, n, _RX)
+#define SCP_UART0_BASE (SCP_REG_BASE + 0x26000)
+#define SCP_UART1_BASE (SCP_REG_BASE + 0x27000)
+#define SCP_UART_BASE(n) CONCAT3(SCP_UART, n, _BASE)
+#define UART_REG(n, offset) REG32_ADDR(SCP_UART_BASE(n))[offset]
+
+/* WDT */
+#define SCP_CORE0_WDT_IRQ REG32(SCP_REG_BASE + 0x30030)
+#define SCP_CORE0_WDT_CFG REG32(SCP_REG_BASE + 0x30034)
+#define WDT_FREQ 33825 /* 0xFFFFF / 31 */
+#define WDT_MAX_PERIOD 0xFFFFF /* 31 seconds */
+#define WDT_PERIOD(ms) (WDT_FREQ * (ms) / 1000)
+#define WDT_EN BIT(31)
+#define SCP_CORE0_WDT_KICK REG32(SCP_REG_BASE + 0x30038)
+#define SCP_CORE0_WDT_CUR_VAL REG32(SCP_REG_BASE + 0x3003C)
+
+/* INTC */
+#define SCP_INTC_IRQ_POL0 0xef001f20
+#define SCP_INTC_IRQ_POL1 0x0800001d
+#define SCP_INTC_IRQ_POL2 0x00000020
+#define SCP_INTC_WORD(irq) ((irq) >> 5) /* word length = 2^5 */
+#define SCP_INTC_BIT(irq) ((irq) & 0x1F) /* bit shift =LSB[0:4] */
+#define SCP_INTC_GRP_COUNT 15
+#define SCP_INTC_GRP_LEN 3
+#define SCP_INTC_GRP_GAP 4
+#define SCP_INTC_IRQ_COUNT 96
+#define SCP_CORE0_INTC_IRQ_BASE (SCP_REG_BASE + 0x32000)
+#define SCP_CORE0_INTC_IRQ_STA(w) \
+ REG32_ADDR(SCP_CORE0_INTC_IRQ_BASE + 0x0010)[(w)]
+#define SCP_CORE0_INTC_IRQ_EN(w) \
+ REG32_ADDR(SCP_CORE0_INTC_IRQ_BASE + 0x0020)[(w)]
+#define SCP_CORE0_INTC_IRQ_POL(w) \
+ REG32_ADDR(SCP_CORE0_INTC_IRQ_BASE + 0x0040)[(w)]
+#define SCP_CORE0_INTC_IRQ_GRP(g, w) \
+ REG32_ADDR(SCP_CORE0_INTC_IRQ_BASE + 0x0050 + \
+ ((g) << SCP_INTC_GRP_GAP))[(w)]
+#define SCP_CORE0_INTC_IRQ_GRP_STA(g, w) \
+ REG32_ADDR(SCP_CORE0_INTC_IRQ_BASE + 0x0150 + \
+ ((g) << SCP_INTC_GRP_GAP))[(w)]
+#define SCP_CORE0_INTC_SLP_WAKE_EN(w) \
+ REG32_ADDR(SCP_CORE0_INTC_IRQ_BASE + 0x0240)[(w)]
+#define SCP_CORE0_INTC_IRQ_OUT REG32(SCP_CORE0_INTC_IRQ_BASE + 0x0250)
+/* UART */
+#define SCP_CORE0_INTC_UART0_RX_IRQ REG32(SCP_CORE0_INTC_IRQ_BASE + 0x0258)
+#define SCP_CORE0_INTC_UART1_RX_IRQ REG32(SCP_CORE0_INTC_IRQ_BASE + 0x025C)
+#define SCP_CORE0_INTC_UART_RX_IRQ(n) CONCAT3(SCP_CORE0_INTC_UART, n, _RX_IRQ)
+
+/* XGPT (general purpose timer) */
+#define NUM_TIMERS 6
+#define SCP_CORE0_TIMER_BASE(n) (SCP_REG_BASE + 0x33000 + (0x10 * (n)))
+#define SCP_CORE0_TIMER_EN(n) REG32(SCP_CORE0_TIMER_BASE(n) + 0x0000)
+#define TIMER_EN BIT(0)
+#define TIMER_CLK_SRC_32K (0 << 4)
+#define TIMER_CLK_SRC_26M (1 << 4)
+#define TIMER_CLK_SRC_BCLK (2 << 4)
+#define TIMER_CLK_SRC_MCLK (3 << 4)
+#define TIMER_CLK_SRC_MASK (3 << 4)
+#define SCP_CORE0_TIMER_RST_VAL(n) REG32(SCP_CORE0_TIMER_BASE(n) + 0x0004)
+#define SCP_CORE0_TIMER_CUR_VAL(n) REG32(SCP_CORE0_TIMER_BASE(n) + 0x0008)
+#define SCP_CORE0_TIMER_IRQ_CTRL(n) REG32(SCP_CORE0_TIMER_BASE(n) + 0x000C)
+#define TIMER_IRQ_EN BIT(0)
+#define TIMER_IRQ_STATUS BIT(4)
+#define TIMER_IRQ_CLR BIT(5)
+#define SCP_IRQ_TIMER(n) CONCAT2(SCP_IRQ_TIMER, n)
+
+/* secure control */
+#define SCP_SEC_CTRL REG32(SCP_REG_BASE + 0xA5000)
+#define VREQ_SECURE_DIS BIT(4)
+/* memory remap */
+#define SCP_R_REMAP_0X0123 REG32(SCP_REG_BASE + 0xA5060)
+#define SCP_R_REMAP_0X4567 REG32(SCP_REG_BASE + 0xA5064)
+#define SCP_R_REMAP_0X89AB REG32(SCP_REG_BASE + 0xA5068)
+#define SCP_R_REMAP_0XCDEF REG32(SCP_REG_BASE + 0xA506C)
+
+/* external address: AP */
+#define AP_REG_BASE 0x60000000 /* 0x10000000 remap to 0x6 */
+/* OSC meter */
+#define TOPCK_BASE AP_REG_BASE
+#define AP_CLK_MISC_CFG_0 REG32(TOPCK_BASE + 0x0140)
+#define MISC_METER_DIVISOR_MASK 0xff000000
+#define MISC_METER_DIV_1 0
+#define AP_CLK_DBG_CFG REG32(TOPCK_BASE + 0x017C)
+#define DBG_MODE_MASK 3
+#define DBG_MODE_SET_CLOCK 0
+#define DBG_BIST_SOURCE_MASK (0x3f << 16)
+#define DBG_BIST_SOURCE_ULPOSC1 (0x25 << 16)
+#define DBG_BIST_SOURCE_ULPOSC2 (0x24 << 16)
+#define AP_SCP_CFG_0 REG32(TOPCK_BASE + 0x0220)
+#define CFG_FREQ_METER_RUN BIT(4)
+#define CFG_FREQ_METER_ENABLE BIT(12)
+#define AP_SCP_CFG_1 REG32(TOPCK_BASE + 0x0224)
+#define CFG_FREQ_COUNTER(CFG1) ((CFG1) & 0xFFFF)
+/* AP GPIO */
+#define AP_GPIO_BASE (AP_REG_BASE + 0x5000)
+#define AP_GPIO_MODE11_SET REG32(AP_GPIO_BASE + 0x03B4)
+#define AP_GPIO_MODE11_CLR REG32(AP_GPIO_BASE + 0x03B8)
+#define AP_GPIO_MODE20_SET REG32(AP_GPIO_BASE + 0x0444)
+#define AP_GPIO_MODE20_CLR REG32(AP_GPIO_BASE + 0x0448)
+/*
+ * ULPOSC
+ * osc: 0 for ULPOSC1, 1 for ULPOSC2.
+ */
+#define AP_ULPOSC_CON0_BASE (AP_REG_BASE + 0xC2B0)
+#define AP_ULPOSC_CON1_BASE (AP_REG_BASE + 0xC2B4)
+#define AP_ULPOSC_CON2_BASE (AP_REG_BASE + 0xC2B8)
+#define AP_ULPOSC_CON0(osc) \
+ REG32(AP_ULPOSC_CON0_BASE + (osc) * 0x10)
+#define AP_ULPOSC_CON1(osc) \
+ REG32(AP_ULPOSC_CON1_BASE + (osc) * 0x10)
+#define AP_ULPOSC_CON2(osc) \
+ REG32(AP_ULPOSC_CON2_BASE + (osc) * 0x10)
+/*
+ * AP_ULPOSC_CON0
+ * bit0-6: calibration
+ * bit7-13: iband
+ * bit14-17: fband
+ * bit18-23: div
+ * bit24: cp_en
+ * bit25-31: reserved
+ */
+#define OSC_CALI_MASK 0x7f
+#define OSC_IBAND_SHIFT 7
+#define OSC_FBAND_SHIFT 14
+#define OSC_DIV_SHIFT 18
+#define OSC_CP_EN BIT(24)
+/* AP_ULPOSC_CON1
+ * bit0-7: 32K calibration
+ * bit 8-15: rsv1
+ * bit 16-23: rsv2
+ * bit 24-25: mod
+ * bit26: div2_en
+ * bit27-31: reserved
+ */
+#define OSC_RSV1_SHIFT 8
+#define OSC_RSV2_SHIFT 16
+#define OSC_MOD_SHIFT 24
+#define OSC_DIV2_EN BIT(26)
+/* AP_ULPOSC_CON2
+ * bit0-7: bias
+ * bit8-31: reserved
+ */
+
+/* IRQ numbers */
+#define SCP_IRQ_GIPC_IN0 0
+#define SCP_IRQ_GIPC_IN1 1
+#define SCP_IRQ_GIPC_IN2 2
+#define SCP_IRQ_GIPC_IN3 3
+/* 4 */
+#define SCP_IRQ_SPM 4
+#define SCP_IRQ_AP_CIRQ 5
+#define SCP_IRQ_EINT 6
+#define SCP_IRQ_PMIC 7
+/* 8 */
+#define SCP_IRQ_UART0_TX 8
+#define SCP_IRQ_UART1_TX 9
+#define SCP_IRQ_I2C0 10
+#define SCP_IRQ_I2C1_0 11
+/* 12 */
+#define SCP_IRQ_BUS_DBG_TRACKER 12
+#define SCP_IRQ_CLK_CTRL 13
+#define SCP_IRQ_VOW 14
+#define SCP_IRQ_TIMER0 15
+/* 16 */
+#define SCP_IRQ_TIMER1 16
+#define SCP_IRQ_TIMER2 17
+#define SCP_IRQ_TIMER3 18
+#define SCP_IRQ_TIMER4 19
+/* 20 */
+#define SCP_IRQ_TIMER5 20
+#define SCP_IRQ_OS_TIMER 21
+#define SCP_IRQ_UART0_RX 22
+#define SCP_IRQ_UART1_RX 23
+/* 24 */
+#define SCP_IRQ_GDMA 24
+#define SCP_IRQ_AUDIO 25
+#define SCP_IRQ_MD_DSP 26
+#define SCP_IRQ_ADSP 27
+/* 28 */
+#define SCP_IRQ_CPU_TICK 28
+#define SCP_IRQ_SPI0 29
+#define SCP_IRQ_SPI1 30
+#define SCP_IRQ_SPI2 31
+/* 32 */
+#define SCP_IRQ_NEW_INFRA_SYS_CIRQ 32
+#define SCP_IRQ_DBG 33
+#define SCP_IRQ_CCIF0 34
+#define SCP_IRQ_CCIF1 35
+/* 36 */
+#define SCP_IRQ_CCIF2 36
+#define SCP_IRQ_WDT 37
+#define SCP_IRQ_USB0 38
+#define SCP_IRQ_USB1 39
+/* 40 */
+#define SCP_IRQ_DPMAIF 40
+#define SCP_IRQ_INFRA 41
+#define SCP_IRQ_CLK_CTRL_CORE 42
+#define SCP_IRQ_CLK_CTRL2_CORE 43
+/* 44 */
+#define SCP_IRQ_CLK_CTRL2 44
+#define SCP_IRQ_GIPC_IN4 45 /* HALT */
+#define SCP_IRQ_PERIBUS_TIMEOUT 46
+#define SCP_IRQ_INFRABUS_TIMEOUT 47
+/* 48 */
+#define SCP_IRQ_MET0 48
+#define SCP_IRQ_MET1 49
+#define SCP_IRQ_MET2 50
+#define SCP_IRQ_MET3 51
+/* 52 */
+#define SCP_IRQ_AP_WDT 52
+#define SCP_IRQ_L2TCM_SEC_VIO 53
+#define SCP_IRQ_CPU_TICK1 54
+#define SCP_IRQ_MAD_DATAIN 55
+/* 56 */
+#define SCP_IRQ_I3C0_IBI_WAKE 56
+#define SCP_IRQ_I3C1_IBI_WAKE 57
+#define SCP_IRQ_I3C2_IBI_WAKE 58
+#define SCP_IRQ_APU_ENGINE 59
+/* 60 */
+#define SCP_IRQ_MBOX0 60
+#define SCP_IRQ_MBOX1 61
+#define SCP_IRQ_MBOX2 62
+#define SCP_IRQ_MBOX3 63
+/* 64 */
+#define SCP_IRQ_MBOX4 64
+#define SCP_IRQ_SYS_CLK_REQ 65
+#define SCP_IRQ_BUS_REQ 66
+#define SCP_IRQ_APSRC_REQ 67
+/* 68 */
+#define SCP_IRQ_APU_MBOX 68
+#define SCP_IRQ_DEVAPC_SECURE_VIO 69
+/* 72 */
+/* 76 */
+#define SCP_IRQ_I2C1_2 78
+#define SCP_IRQ_I2C2 79
+/* 80 */
+#define SCP_IRQ_AUD2AUDIODSP 80
+#define SCP_IRQ_AUD2AUDIODSP_2 81
+#define SCP_IRQ_CONN2ADSP_A2DPOL 82
+#define SCP_IRQ_CONN2ADSP_BTCVSD 83
+/* 84 */
+#define SCP_IRQ_CONN2ADSP_BLEISO 84
+#define SCP_IRQ_PCIE2ADSP 85
+#define SCP_IRQ_APU2ADSP_ENGINE 86
+#define SCP_IRQ_APU2ADSP_MBOX 87
+/* 88 */
+#define SCP_IRQ_CCIF3 88
+#define SCP_IRQ_I2C_DMA0 89
+#define SCP_IRQ_I2C_DMA1 90
+#define SCP_IRQ_I2C_DMA2 91
+/* 92 */
+#define SCP_IRQ_I2C_DMA3 92
+
+#endif /* __CROS_EC_REGISTERS_H */
diff --git a/chip/mt_scp/rv32i_common/system.c b/chip/mt_scp/rv32i_common/system.c
new file mode 100644
index 0000000000..0e12154f6d
--- /dev/null
+++ b/chip/mt_scp/rv32i_common/system.c
@@ -0,0 +1,50 @@
+/* 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.
+ */
+
+/* System : hardware specific implementation */
+
+#include "csr.h"
+#include "memmap.h"
+#include "registers.h"
+#include "system.h"
+
+void system_pre_init(void)
+{
+ memmap_init();
+
+ /* enable CPU and platform low power CG */
+ /* enable CPU DCM */
+ set_csr(CSR_MCTREN, CSR_MCTREN_CG);
+
+ /* Disable jump (it has only RW) */
+ system_disable_jump();
+}
+
+void system_reset(int flags)
+{
+ while (1)
+ ;
+}
+
+int system_get_bbram(enum system_bbram_idx idx, uint8_t *value)
+{
+ return EC_ERROR_INVAL;
+}
+
+const char *system_get_chip_vendor(void)
+{
+ return "mtk";
+}
+
+const char *system_get_chip_name(void)
+{
+ /* Support only SCP_A for now */
+ return "scp_a";
+}
+
+const char *system_get_chip_revision(void)
+{
+ return "";
+}
diff --git a/chip/mt_scp/rv32i_common/uart.c b/chip/mt_scp/rv32i_common/uart.c
new file mode 100644
index 0000000000..2479bb8711
--- /dev/null
+++ b/chip/mt_scp/rv32i_common/uart.c
@@ -0,0 +1,171 @@
+/* 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.
+ */
+
+/* SCP UART module */
+
+#include "csr.h"
+#include "system.h"
+#include "uart.h"
+#include "uart_regs.h"
+#include "util.h"
+
+/*
+ * UARTN == 0, SCP UART0
+ * UARTN == 1, SCP UART1
+ * UARTN == 2, AP UART1
+ */
+#define UARTN CONFIG_UART_CONSOLE
+#define UART_IDLE_WAIT_US 500
+#define UART_INTC_GROUP 12
+
+static uint8_t init_done, tx_started;
+
+void uart_init(void)
+{
+ const uint32_t baud_rate = CONFIG_UART_BAUD_RATE;
+ const uint32_t uart_clock = 26000000;
+ const uint32_t div = DIV_ROUND_NEAREST(uart_clock, baud_rate * 16);
+
+#if UARTN == 0
+ SCP_UART_CK_SEL |= UART0_CK_SEL_VAL(UART_CK_SEL_ULPOSC);
+ SCP_SET_CLK_CG |= CG_UART0_MCLK | CG_UART0_BCLK | CG_UART0_RST;
+
+ /* set AP GPIO164 and GPIO165 to alt func 3 */
+ AP_GPIO_MODE20_CLR = 0x00770000;
+ AP_GPIO_MODE20_SET = 0x00330000;
+#elif UARTN == 1
+ SCP_UART_CK_SEL |= UART1_CK_SEL_VAL(UART_CK_SEL_ULPOSC);
+ SCP_SET_CLK_CG |= CG_UART1_MCLK | CG_UART1_BCLK | CG_UART1_RST;
+#endif
+
+ /* Clear FIFO */
+ UART_FCR(UARTN) = UART_FCR_ENABLE_FIFO
+ | UART_FCR_CLEAR_RCVR
+ | UART_FCR_CLEAR_XMIT;
+ /* Line control: parity none, 8 bit, 1 stop bit */
+ UART_LCR(UARTN) = UART_LCR_WLEN8;
+ /* For baud rate <= 115200 */
+ UART_HIGHSPEED(UARTN) = 0;
+
+ /* DLAB start */
+ UART_LCR(UARTN) |= UART_LCR_DLAB;
+ UART_DLL(UARTN) = div & 0xff;
+ UART_DLH(UARTN) = (div >> 8) & 0xff;
+ UART_LCR(UARTN) &= ~UART_LCR_DLAB;
+ /* DLAB end */
+
+ /* Enable received data interrupt */
+ UART_IER(UARTN) |= UART_IER_RDI;
+
+#if (UARTN < SCP_UART_COUNT)
+ task_enable_irq(UART_TX_IRQ(UARTN));
+ task_enable_irq(UART_RX_IRQ(UARTN));
+#endif
+
+ init_done = 1;
+}
+
+int uart_init_done(void)
+{
+ return init_done;
+}
+
+void uart_tx_flush(void)
+{
+ while (!(UART_LSR(UARTN) & UART_LSR_TEMT))
+ ;
+}
+
+int uart_tx_ready(void)
+{
+ return UART_LSR(UARTN) & UART_LSR_THRE;
+}
+
+int uart_rx_available(void)
+{
+ return UART_LSR(UARTN) & UART_LSR_DR;
+}
+
+void uart_write_char(char c)
+{
+ while (!uart_tx_ready())
+ ;
+
+ UART_THR(UARTN) = c;
+}
+
+int uart_read_char(void)
+{
+ return UART_RBR(UARTN);
+}
+
+void uart_tx_start(void)
+{
+ tx_started = 1;
+ if (UART_IER(UARTN) & UART_IER_THRI)
+ return;
+ disable_sleep(SLEEP_MASK_UART);
+ UART_IER(UARTN) |= UART_IER_THRI;
+}
+
+void uart_tx_stop(void)
+{
+ /*
+ * Workaround for b/157541273.
+ * Don't unset the THRI flag unless we are in the UART ISR.
+ *
+ * Note:
+ * MICAUSE denotes current INTC group number.
+ */
+ if (in_interrupt_context() &&
+ read_csr(CSR_VIC_MICAUSE) != UART_INTC_GROUP)
+ return;
+
+ tx_started = 0;
+ UART_IER(UARTN) &= ~UART_IER_THRI;
+ enable_sleep(SLEEP_MASK_UART);
+}
+
+static void uart_process(void)
+{
+ uart_process_input();
+ uart_process_output();
+}
+
+#if (UARTN < SCP_UART_COUNT)
+static void uart_irq_handler(void)
+{
+ extern volatile int ec_int;
+
+ switch (ec_int) {
+ case UART_TX_IRQ(UARTN):
+ uart_process();
+ task_clear_pending_irq(ec_int);
+ break;
+ case UART_RX_IRQ(UARTN):
+ uart_process();
+ SCP_CORE0_INTC_UART_RX_IRQ(UARTN) = BIT(0);
+ asm volatile ("fence.i" ::: "memory");
+ task_clear_pending_irq(ec_int);
+ break;
+ }
+}
+DECLARE_IRQ(UART_INTC_GROUP, uart_irq_handler, 0);
+#else
+
+#ifndef HAS_TASK_APUART
+#error "APUART task hasn't defined in ec.tasklist."
+#endif
+
+void uart_task(void)
+{
+ while (1) {
+ if (uart_rx_available() || tx_started)
+ uart_process();
+ else
+ task_wait_event(UART_IDLE_WAIT_US);
+ }
+}
+#endif
diff --git a/chip/mt_scp/rv32i_common/uart_regs.h b/chip/mt_scp/rv32i_common/uart_regs.h
new file mode 100644
index 0000000000..1a421a413b
--- /dev/null
+++ b/chip/mt_scp/rv32i_common/uart_regs.h
@@ -0,0 +1,77 @@
+/* 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.
+ */
+
+/* SCP UART module registers */
+
+#ifndef __CROS_EC_UART_REGS_H
+#define __CROS_EC_UART_REGS_H
+
+#include "registers.h"
+
+/* DLAB (Divisor Latch Access Bit) == 0 */
+
+/* (Read) receiver buffer register */
+#define UART_RBR(n) UART_REG(n, 0)
+/* (Write) transmitter holding register */
+#define UART_THR(n) UART_REG(n, 0)
+/* (Write) interrupt enable register */
+#define UART_IER(n) UART_REG(n, 1)
+#define UART_IER_RDI BIT(0) /* received data */
+#define UART_IER_THRI BIT(1) /* THR empty */
+#define UART_IER_RLSI BIT(2) /* receiver LSR change */
+#define UART_IER_MSI BIT(3) /* MSR change */
+/* (Read) interrupt identification register */
+#define UART_IIR(n) UART_REG(n, 2)
+#define UART_IIR_ID_MASK 0x0e
+#define UART_IIR_MSI 0x00 /* modem status change */
+#define UART_IIR_NO_INT 0x01 /* no int pending */
+#define UART_IIR_THRI 0x02 /* THR empty */
+#define UART_IIR_RDI 0x04 /* received data available */
+#define UART_IIR_RLSI 0x06 /* line status change */
+/* (Write) FIFO control register */
+#define UART_FCR(n) UART_REG(n, 2)
+#define UART_FCR_ENABLE_FIFO BIT(0) /* enable FIFO */
+#define UART_FCR_CLEAR_RCVR BIT(1) /* clear receive FIFO */
+#define UART_FCR_CLEAR_XMIT BIT(2) /* clear transmit FIFO */
+#define UART_FCR_DMA_SELECT BIT(3) /* select DMA mode */
+/* (Write) line control register */
+#define UART_LCR(n) UART_REG(n, 3)
+#define UART_LCR_WLEN5 0 /* word length 5 bits */
+#define UART_LCR_WLEN6 1
+#define UART_LCR_WLEN7 2
+#define UART_LCR_WLEN8 3
+#define UART_LCR_STOP BIT(2) /* stop bits: 1bit, 2bits */
+#define UART_LCR_PARITY BIT(3) /* parity enable */
+#define UART_LCR_EPAR BIT(4) /* even parity */
+#define UART_LCR_SPAR BIT(5) /* stick parity */
+#define UART_LCR_SBC BIT(6) /* set break control */
+#define UART_LCR_DLAB BIT(7) /* divisor latch access */
+/* (Write) modem control register */
+#define UART_MCR(n) UART_REG(n, 4)
+/* (Read) line status register */
+#define UART_LSR(n) UART_REG(n, 5)
+#define UART_LSR_DR BIT(0) /* data ready */
+#define UART_LSR_OE BIT(1) /* overrun error */
+#define UART_LSR_PE BIT(2) /* parity error */
+#define UART_LSR_FE BIT(3) /* frame error */
+#define UART_LSR_BI BIT(4) /* break interrupt */
+#define UART_LSR_THRE BIT(5) /* THR empty */
+#define UART_LSR_TEMT BIT(6) /* THR empty, line idle */
+#define UART_LSR_FIFOE BIT(7) /* FIFO error */
+/* (Read) modem status register */
+#define UART_MSR(n) UART_REG(n, 6)
+/* (Read/Write) scratch register */
+#define UART_SCR(n) UART_REG(n, 7)
+
+/* DLAB == 1 */
+
+/* (Write) divisor latch */
+#define UART_DLL(n) UART_REG(n, 0)
+#define UART_DLH(n) UART_REG(n, 1)
+
+/* MTK extension */
+#define UART_HIGHSPEED(n) UART_REG(n, 9)
+
+#endif /* __CROS_EC_UART_REGS_H */
diff --git a/chip/mt_scp/rv32i_common/watchdog.c b/chip/mt_scp/rv32i_common/watchdog.c
new file mode 100644
index 0000000000..72ca5edad8
--- /dev/null
+++ b/chip/mt_scp/rv32i_common/watchdog.c
@@ -0,0 +1,33 @@
+/* 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.
+ */
+
+/* Watchdog driver */
+
+#include "common.h"
+#include "hooks.h"
+#include "registers.h"
+#include "watchdog.h"
+
+void watchdog_reload(void)
+{
+ SCP_CORE0_WDT_KICK = BIT(0);
+}
+DECLARE_HOOK(HOOK_TICK, watchdog_reload, HOOK_PRIO_DEFAULT);
+
+int watchdog_init(void)
+{
+ const uint32_t timeout = WDT_PERIOD(CONFIG_WATCHDOG_PERIOD_MS);
+
+ /* disable watchdog */
+ SCP_CORE0_WDT_CFG &= ~WDT_EN;
+ /* clear watchdog irq */
+ SCP_CORE0_WDT_IRQ |= BIT(0);
+ /* enable watchdog */
+ SCP_CORE0_WDT_CFG = WDT_EN | timeout;
+ /* reload watchdog */
+ watchdog_reload();
+
+ return EC_SUCCESS;
+}