diff options
author | Tzung-Bi Shih <tzungbi@chromium.org> | 2021-06-24 17:17:39 +0800 |
---|---|---|
committer | Commit Bot <commit-bot@chromium.org> | 2021-06-25 03:42:30 +0000 |
commit | b409fd3b5ac6a82b6851f2e9af49761ee2d4766c (patch) | |
tree | 780a069f10a57222362a209abee19b27b7de9076 /chip/mt_scp/rv32i_common | |
parent | cf92cf3c6c4135b511c8f7f6bc83036167b7c735 (diff) | |
download | chrome-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')
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; +} |