summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRong Chang <rongchang@chromium.org>2018-09-05 16:36:12 -0400
committerchrome-bot <chrome-bot@chromium.org>2018-12-20 13:49:38 -0800
commitb437f03578e7e19a0ff369fe7ac574ab4414d588 (patch)
treed0858fbd1b7b4daa7799cf788dcf24c3d7663c69
parentfd13a286bc99cc2e56e3ef1bb82fcdb63734985a (diff)
downloadchrome-ec-b437f03578e7e19a0ff369fe7ac574ab4414d588.tar.gz
scp: Add mt_scp chip
SCP is a Cortex-M4 based sensor hub in Mediatek SoC. This change adds the chip folder and system level drivers. BRANCH=none BUG=b:114326670 TEST=manual make BOARD=kukui_scp -j copy ec.bin to /lib/firmware/scp.img echo 'stop' > /sys/class/remoteproc/remoteproc0/state echo 'start' > /sys/class/remoteproc/remoteproc0/state check EC uart console Change-Id: I6629149f352184108fa520e80b59fd2ce94c76f7 Signed-off-by: Rong Chang <rongchang@chromium.org> Signed-off-by: Yilun Lin <yllin@google.com> Reviewed-on: https://chromium-review.googlesource.com/1208770 Commit-Ready: ChromeOS CL Exonerator Bot <chromiumos-cl-exonerator@appspot.gserviceaccount.com> Tested-by: Yilun Lin <yllin@chromium.org> Reviewed-by: Yilun Lin <yllin@chromium.org> Reviewed-by: Nicolas Boichat <drinkcat@chromium.org>
-rw-r--r--chip/mt_scp/build.mk19
-rw-r--r--chip/mt_scp/clock.c78
-rw-r--r--chip/mt_scp/config_chip.h78
-rw-r--r--chip/mt_scp/gpio.c177
-rw-r--r--chip/mt_scp/hrtimer.c235
-rw-r--r--chip/mt_scp/registers.h430
-rw-r--r--chip/mt_scp/serial_reg.h90
-rw-r--r--chip/mt_scp/system.c307
-rw-r--r--chip/mt_scp/uart.c171
-rw-r--r--chip/mt_scp/watchdog.c33
10 files changed, 1618 insertions, 0 deletions
diff --git a/chip/mt_scp/build.mk b/chip/mt_scp/build.mk
new file mode 100644
index 0000000000..eb261fbd90
--- /dev/null
+++ b/chip/mt_scp/build.mk
@@ -0,0 +1,19 @@
+# -*- makefile -*-
+# Copyright 2018 The Chromium OS Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+#
+# SCP specific files build
+#
+
+CORE:=cortex-m
+CFLAGS_CPU+=-march=armv7e-m -mcpu=cortex-m4
+
+# Required chip modules
+chip-y=clock.o gpio.o system.o uart.o
+
+# Optional chip modules
+chip-$(CONFIG_COMMON_TIMER)+=hrtimer.o
+chip-$(CONFIG_I2C)+=i2c.o
+chip-$(CONFIG_SPI)+=spi.o
+chip-$(CONFIG_WATCHDOG)+=watchdog.o
diff --git a/chip/mt_scp/clock.c b/chip/mt_scp/clock.c
new file mode 100644
index 0000000000..524db8024a
--- /dev/null
+++ b/chip/mt_scp/clock.c
@@ -0,0 +1,78 @@
+/* Copyright 2018 The Chromium OS Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+/* Clocks, PLL and power settings */
+
+#include "clock.h"
+#include "common.h"
+#include "registers.h"
+#include "task.h"
+#include "util.h"
+
+void clock_init(void)
+{
+ /* Set VREQ to HW mode */
+ SCP_CPU_VREQ = CPU_VREQ_HW_MODE;
+ SCP_SECURE_CTRL &= ~ENABLE_SPM_MASK_VREQ;
+
+ /* Set DDREN auto mode */
+ SCP_SYS_CTRL |= AUTO_DDREN;
+
+ /* Initialize 26MHz system clock counter reset value to 1. */
+ SCP_CLK_SYS_VAL =
+ (SCP_CLK_SYS_VAL & ~CLK_SYS_VAL_MASK) | CLK_SYS_VAL(1);
+ /* Initialize high frequency ULPOSC counter reset value to 1. */
+ SCP_CLK_HIGH_VAL =
+ (SCP_CLK_HIGH_VAL & ~CLK_HIGH_VAL_MASK) | CLK_HIGH_VAL(1);
+ /* Initialize sleep mode control VREQ counter. */
+ SCP_CLK_SLEEP_CTRL =
+ (SCP_CLK_SLEEP_CTRL & ~VREQ_COUNTER_MASK) | VREQ_COUNTER_VAL(1);
+
+ /* Set normal wake clock */
+ SCP_WAKE_CKSW &= ~WAKE_CKSW_SEL_NORMAL_MASK;
+
+ /* Enable fast wakeup support */
+ SCP_CLK_SLEEP = 0;
+ SCP_CLK_ON_CTRL = (SCP_CLK_ON_CTRL & ~HIGH_FINAL_VAL_MASK) |
+ HIGH_FINAL_VAL_DEFAULT;
+ SCP_FAST_WAKE_CNT_END =
+ (SCP_FAST_WAKE_CNT_END & ~FAST_WAKE_CNT_END_MASK) |
+ FAST_WAKE_CNT_END_DEFAULT;
+
+ /* Set slow wake clock */
+ SCP_WAKE_CKSW = (SCP_WAKE_CKSW & ~WAKE_CKSW_SEL_SLOW_MASK) |
+ WAKE_CKSW_SEL_SLOW_DEFAULT;
+
+ /* Select CLK_HIGH as wakeup clock */
+ SCP_CLK_SLOW_SEL = (SCP_CLK_SLOW_SEL &
+ ~(CKSW_SEL_SLOW_MASK | CKSW_SEL_SLOW_DIV_MASK)) |
+ CKSW_SEL_SLOW_ULPOSC2_CLK;
+
+ /*
+ * Set legacy wakeup
+ * - disable SPM sleep control
+ * - disable SCP sleep mode
+ */
+ SCP_CLK_SLEEP_CTRL &= ~(EN_SLEEP_CTRL | SPM_SLEEP_MODE);
+
+ task_enable_irq(SCP_IRQ_CLOCK);
+ task_enable_irq(SCP_IRQ_CLOCK2);
+}
+
+void clock_control_irq(void)
+{
+ /* Read ack CLK_IRQ */
+ (SCP_CLK_IRQ_ACK);
+ task_clear_pending_irq(SCP_IRQ_CLOCK);
+}
+DECLARE_IRQ(SCP_IRQ_CLOCK, clock_control_irq, 3);
+
+void clock_fast_wakeup_irq(void)
+{
+ /* Ack fast wakeup */
+ SCP_SLEEP_IRQ2 = 1;
+ task_clear_pending_irq(SCP_IRQ_CLOCK2);
+}
+DECLARE_IRQ(SCP_IRQ_CLOCK2, clock_fast_wakeup_irq, 3);
diff --git a/chip/mt_scp/config_chip.h b/chip/mt_scp/config_chip.h
new file mode 100644
index 0000000000..fb303fe815
--- /dev/null
+++ b/chip/mt_scp/config_chip.h
@@ -0,0 +1,78 @@
+/* Copyright 2018 The Chromium OS Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef __CROS_EC_CONFIG_CHIP_H
+#define __CROS_EC_CONFIG_CHIP_H
+
+#include "core/cortex-m/config_core.h"
+
+/* Interval between HOOK_TICK notifications */
+#define HOOK_TICK_INTERVAL_MS 500
+#define HOOK_TICK_INTERVAL (HOOK_TICK_INTERVAL_MS * MSEC)
+
+/* Default to UART 2 (AP UART) for EC console */
+#define CONFIG_UART_CONSOLE 2
+
+/* Number of IRQ vectors */
+#define CONFIG_IRQ_COUNT 56
+
+/*
+ * Number of EINT can be 0 ~ 160. Change this to conditional macro
+ * on adding other variants.
+ */
+#define MAX_NUM_EINT 8
+#define MAX_EINT_PORT (MAX_NUM_EINT / 32)
+
+/* RW only, no flash
+ * +-------------------- 0x0
+ * | free shared space with AP
+ * +-------------------- 0x005B0
+ * | IPI shared buffer with AP (288 + 8) * 2
+ * +-------------------- 0x00800
+ * | scp.img, exception vectors starting location.
+ * +-------------------- 0x7B800
+ * | free shared space with AP 2KB
+ * +-------------------- 0x7C000
+ * | 8KB I-CACHE
+ * +-------------------- 0x7E000
+ * | 8KB D-CACHE
+ * +-------------------- 0x80000
+ */
+#undef CONFIG_FW_INCLUDE_RO
+#define CONFIG_RAM_BASE 0x00800
+#define CONFIG_RAM_SIZE 0x7B000
+#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_MAPPED_STORAGE_BASE 0
+
+/* Unsupported features/commands */
+#undef CONFIG_CMD_FLASHINFO
+#undef CONFIG_CMD_POWER_AP
+#undef CONFIG_FLASH
+#undef CONFIG_FLASH_PHYSICAL
+#undef CONFIG_FMAP
+#undef CONFIG_HIBERNATE
+
+/* Task stack size */
+#define CONFIG_STACK_SIZE 1024
+#define IDLE_TASK_STACK_SIZE 256
+#define SMALLER_TASK_STACK_SIZE 384
+#define TASK_STACK_SIZE 488
+#define LARGER_TASK_STACK_SIZE 640
+#define VENTI_TASK_STACK_SIZE 768
+
+#define CONFIG_CHIP_PRE_INIT
+
+#define GPIO_PIN(num) ((num) / 32), ((num) % 32)
+#define GPIO_PIN_MASK(p, m) .port = (p), .mask = (m)
+
+#endif /* __CROS_EC_CONFIG_CHIP_H */
diff --git a/chip/mt_scp/gpio.c b/chip/mt_scp/gpio.c
new file mode 100644
index 0000000000..7d680e6863
--- /dev/null
+++ b/chip/mt_scp/gpio.c
@@ -0,0 +1,177 @@
+/* Copyright 2018 The Chromium OS Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+/* GPIO module */
+
+#include "gpio.h"
+#include "hooks.h"
+#include "registers.h"
+#include "system.h"
+#include "task.h"
+#include "util.h"
+
+void gpio_set_alternate_function(uint32_t port, uint32_t mask, int func)
+{
+ int bit, mode_reg_index, shift;
+ uint32_t mode_bits, mode_mask;
+
+ /* Up to 8 alt functions per port */
+ if (func > 7)
+ return;
+
+ while (mask) {
+ /* 32 gpio per port */
+ bit = get_next_bit(&mask);
+ /* 8 gpio per mode reg */
+ mode_reg_index = (port << 2) | (bit >> 3);
+ /*
+ * b[3] - write enable(?)
+ * b[2:0] - mode
+ */
+ shift = (bit & 7) << 2;
+ mode_bits = func << shift;
+ mode_mask = ~(0xf << shift);
+ AP_GPIO_MODE(mode_reg_index) = (AP_GPIO_MODE(mode_reg_index) &
+ mode_mask) | mode_bits;
+ }
+}
+
+test_mockable int gpio_get_level(enum gpio_signal signal)
+{
+ return !!(AP_GPIO_DIN(gpio_list[signal].port) &
+ gpio_list[signal].mask);
+}
+
+
+void gpio_set_level(enum gpio_signal signal, int value)
+{
+ if (value)
+ AP_GPIO_DOUT(gpio_list[signal].port) |= gpio_list[signal].mask;
+ else
+ AP_GPIO_DOUT(gpio_list[signal].port) &= ~gpio_list[signal].mask;
+}
+
+void gpio_set_flags_by_mask(uint32_t port, uint32_t mask, uint32_t flags)
+{
+ /* Set input/output mode */
+ if (flags & GPIO_OUTPUT) {
+ /* Set level before changing to output mode */
+ if (flags & GPIO_HIGH)
+ AP_GPIO_DOUT(port) |= mask;
+ if (flags & GPIO_LOW)
+ AP_GPIO_DOUT(port) &= ~mask;
+ AP_GPIO_DIR(port) |= mask;
+ } else {
+ AP_GPIO_DIR(port) &= ~mask;
+ }
+
+ if (flags & (GPIO_INT_F_RISING | GPIO_INT_F_HIGH))
+ SCP_EINT_POLARITY_SET[port] = mask;
+
+ if (flags & (GPIO_INT_F_FALLING | GPIO_INT_F_LOW))
+ SCP_EINT_POLARITY_CLR[port] = mask;
+ else
+ SCP_EINT_POLARITY_SET[port] = mask;
+
+ /* Set sensitivity register on edge trigger */
+ if (flags & (GPIO_INT_F_RISING | GPIO_INT_F_FALLING))
+ SCP_EINT_SENS_SET[port] = mask;
+ else
+ SCP_EINT_SENS_CLR[port] = mask;
+}
+
+int gpio_get_flags_by_mask(uint32_t port, uint32_t mask)
+{
+ /* TODO(b/120167145): implement get flags */
+ return 0;
+}
+
+int gpio_enable_interrupt(enum gpio_signal signal)
+{
+ const struct gpio_info *g = gpio_list + signal;
+
+ if (signal >= GPIO_IH_COUNT || !g->mask)
+ return EC_ERROR_INVAL;
+
+ SCP_EINT_MASK_CLR[g->port] = g->mask;
+
+ return EC_SUCCESS;
+}
+
+int gpio_disable_interrupt(enum gpio_signal signal)
+{
+ const struct gpio_info *g = gpio_list + signal;
+
+ if (signal >= GPIO_IH_COUNT || !g->mask)
+ return EC_ERROR_INVAL;
+
+ SCP_EINT_MASK_SET[g->port] = g->mask;
+
+ return EC_SUCCESS;
+}
+
+int gpio_clear_pending_interrupt(enum gpio_signal signal)
+{
+ const struct gpio_info *g = gpio_list + signal;
+
+ if (signal >= GPIO_IH_COUNT || !g->mask)
+ return EC_ERROR_INVAL;
+
+ SCP_EINT_ACK[g->port] = g->mask;
+
+ return EC_SUCCESS;
+}
+
+void gpio_pre_init(void)
+{
+ const struct gpio_info *g = gpio_list;
+ int i;
+ int is_warm = system_is_reboot_warm();
+
+ for (i = 0; i < GPIO_COUNT; i++, g++) {
+ int flags = g->flags;
+
+ if (flags & GPIO_DEFAULT)
+ continue;
+
+ if (is_warm)
+ flags &= ~(GPIO_LOW | GPIO_HIGH);
+
+ gpio_set_flags_by_mask(g->port, g->mask, flags);
+ }
+}
+
+void gpio_init(void)
+{
+ /* Enable EINT IRQ */
+ task_enable_irq(SCP_IRQ_EINT);
+}
+DECLARE_HOOK(HOOK_INIT, gpio_init, HOOK_PRIO_DEFAULT);
+
+/* Interrupt handler */
+void __keep gpio_interrupt(void)
+{
+ int bit, port;
+ uint32_t pending;
+ enum gpio_signal signal;
+
+ for (port = 0; port <= MAX_EINT_PORT; port++) {
+ pending = SCP_EINT_STATUS[port];
+
+ while (pending) {
+ bit = get_next_bit(&pending);
+ SCP_EINT_ACK[port] = (1 << bit);
+ /* Skip masked gpio */
+ if (SCP_EINT_MASK_GET[port] & (1 << bit))
+ continue;
+ /* Call handler */
+ signal = port * 32 + bit;
+ if (signal < GPIO_IH_COUNT)
+ gpio_irq_handlers[signal](signal);
+ }
+ }
+}
+DECLARE_IRQ(SCP_IRQ_EINT, gpio_interrupt, 1);
+
diff --git a/chip/mt_scp/hrtimer.c b/chip/mt_scp/hrtimer.c
new file mode 100644
index 0000000000..b0e72fd948
--- /dev/null
+++ b/chip/mt_scp/hrtimer.c
@@ -0,0 +1,235 @@
+/* Copyright 2018 The Chromium OS Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+/*
+ * 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 26MHz frequency
+ * countdown and converts to micro second value matching common timer.
+ */
+
+#include "clock.h"
+#include "common.h"
+#include "console.h"
+#include "hooks.h"
+#include "hwtimer.h"
+#include "panic.h"
+#include "registers.h"
+#include "task.h"
+#include "timer.h"
+#include "watchdog.h"
+
+#define IRQ_TIMER(n) CONCAT2(SCP_IRQ_TIMER, n)
+
+#define TIMER_SYSTEM 5
+#define TIMER_EVENT 3
+
+/* Common timer overflows at 0x100000000 micro seconds */
+#define OVERFLOW_TICKS (26 * 0x100000000 - 1)
+
+static uint8_t sys_high;
+static uint8_t event_high;
+
+/* Convert hardware countdown timer to 64bit countup ticks */
+static inline uint64_t timer_read_raw_system(void)
+{
+ /* TODO(b/120173036): fix racing condition in read_raw */
+ return OVERFLOW_TICKS - (((uint64_t)sys_high << 32) |
+ SCP_TIMER_VAL(TIMER_SYSTEM));
+}
+
+static inline uint64_t timer_read_raw_event(void)
+{
+ return OVERFLOW_TICKS - (((uint64_t)event_high << 32) |
+ SCP_TIMER_VAL(TIMER_EVENT));
+}
+
+static inline void timer_set_clock(int n, uint32_t clock_source)
+{
+ SCP_TIMER_EN(n) = (SCP_TIMER_EN(n) & ~TIMER_CLK_MASK) |
+ clock_source;
+}
+
+static inline void timer_ack_irq(int n)
+{
+ SCP_TIMER_IRQ_CTRL(n) |= TIMER_IRQ_CLEAR;
+}
+
+/* Set hardware countdown value */
+static inline void timer_set_reset_value(int n, uint32_t reset_value)
+{
+ SCP_TIMER_RESET_VAL(n) = reset_value;
+}
+
+static void timer_reset(int n)
+{
+ __hw_timer_enable_clock(n, 0);
+ timer_ack_irq(n);
+ timer_set_reset_value(n, 0xffffffff);
+ timer_set_clock(n, TIMER_CLK_32K);
+}
+
+/* Reload a new 32bit countdown value */
+static void timer_reload(int n, uint32_t value)
+{
+ __hw_timer_enable_clock(n, 0);
+ timer_set_reset_value(n, value);
+ __hw_timer_enable_clock(n, 1);
+}
+
+static int timer_reload_event_high(void)
+{
+ if (event_high) {
+ if (SCP_TIMER_RESET_VAL(TIMER_EVENT) == 0xffffffff)
+ __hw_timer_enable_clock(TIMER_EVENT, 1);
+ else
+ timer_reload(TIMER_EVENT, 0xffffffff);
+ event_high--;
+ return 1;
+ }
+
+ /* Disable event timer clock when done. */
+ __hw_timer_enable_clock(TIMER_EVENT, 0);
+ return 0;
+}
+
+void __hw_clock_event_clear(void)
+{
+ __hw_timer_enable_clock(TIMER_EVENT, 0);
+ 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 * 26;
+ 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();
+}
+
+void __hw_timer_enable_clock(int n, int enable)
+{
+ if (enable) {
+ SCP_TIMER_IRQ_CTRL(n) |= 1;
+ SCP_TIMER_EN(n) |= 1;
+ } else {
+ SCP_TIMER_EN(n) &= ~1;
+ SCP_TIMER_IRQ_CTRL(n) &= ~1;
+ }
+}
+
+int __hw_clock_source_init(uint32_t start_t)
+{
+ int t;
+
+ /*
+ * TODO(b/120169529): check clock tree to see if we need to turn on
+ * MCLK and BCLK gate.
+ */
+ SCP_CLK_GATE |= (CG_TIMER_M | CG_TIMER_B);
+
+ /* Reset all timer, select 32768Hz clock source */
+ for (t = 0; t < NUM_TIMERS; t++)
+ timer_reset(t);
+
+ /* Enable timer IRQ wake source */
+ SCP_INTC_IRQ_WAKEUP |= (1 << IRQ_TIMER(0)) | (1 << IRQ_TIMER(1)) |
+ (1 << IRQ_TIMER(2)) | (1 << IRQ_TIMER(3)) |
+ (1 << IRQ_TIMER(4)) | (1 << IRQ_TIMER(5));
+ /*
+ * Timer configuration:
+ * OS TIMER - count up @ 13MHz, 64bit value with latch.
+ * SYS TICK - count down @ 26MHz
+ * EVENT TICK - count down @ 26MHz
+ */
+
+ /* Turn on OS TIMER, tick at 13MHz */
+ SCP_OSTIMER_CON |= 1;
+
+ /* System timestamp timer */
+ timer_set_clock(TIMER_SYSTEM, TIMER_CLK_26M);
+ sys_high = 25;
+ timer_set_reset_value(TIMER_SYSTEM, 0xffffffff);
+ __hw_timer_enable_clock(TIMER_SYSTEM, 1);
+ task_enable_irq(IRQ_TIMER(TIMER_SYSTEM));
+ /* Event tick timer */
+ timer_set_clock(TIMER_EVENT, TIMER_CLK_26M);
+ task_enable_irq(IRQ_TIMER(TIMER_EVENT));
+
+ return IRQ_TIMER(TIMER_SYSTEM);
+}
+
+uint32_t __hw_clock_source_read(void)
+{
+ return timer_read_raw_system() / 26;
+}
+
+uint32_t __hw_clock_event_get(void)
+{
+ return (timer_read_raw_event() + timer_read_raw_system()) / 26;
+}
+
+static void __hw_clock_source_irq(int n)
+{
+ uint32_t timer_ctrl = SCP_TIMER_IRQ_CTRL(n);
+
+ /* Ack if we're hardware interrupt */
+ if (timer_ctrl & TIMER_IRQ_STATUS)
+ timer_ack_irq(n);
+
+ switch (n) {
+ case TIMER_EVENT:
+ if (timer_ctrl & TIMER_IRQ_STATUS) {
+ if (timer_reload_event_high())
+ return;
+ }
+ process_timers(0);
+ break;
+ case TIMER_SYSTEM:
+ /* If this is a hardware irq, check overflow */
+ if (timer_ctrl & TIMER_IRQ_STATUS) {
+ if (sys_high) {
+ sys_high--;
+ process_timers(0);
+ } else {
+ /* Overflow, reload system timer */
+ sys_high = 25;
+ process_timers(1);
+ }
+ } else {
+ process_timers(0);
+ }
+ break;
+ default:
+ return;
+ }
+
+}
+
+#define DECLARE_TIMER_IRQ(n) \
+ void __hw_clock_source_irq_##n(void) { __hw_clock_source_irq(n); } \
+ DECLARE_IRQ(IRQ_TIMER(n), __hw_clock_source_irq_##n, 2)
+
+DECLARE_TIMER_IRQ(0);
+DECLARE_TIMER_IRQ(1);
+DECLARE_TIMER_IRQ(2);
+DECLARE_TIMER_IRQ(3);
+DECLARE_TIMER_IRQ(4);
+DECLARE_TIMER_IRQ(5);
diff --git a/chip/mt_scp/registers.h b/chip/mt_scp/registers.h
new file mode 100644
index 0000000000..3f88099e53
--- /dev/null
+++ b/chip/mt_scp/registers.h
@@ -0,0 +1,430 @@
+/* Copyright 2018 The Chromium OS Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ *
+ * Register map for SCP
+ */
+
+#ifndef __CROS_EC_REGISTERS_H
+#define __CROS_EC_REGISTERS_H
+
+#include "common.h"
+
+/* IRQ numbers */
+#define SCP_IRQ_IPC0 0
+#define SCP_IRQ_IPC1 1
+#define SCP_IRQ_IPC2 2
+#define SCP_IRQ_IPC3 3
+#define SCP_IRQ_SPM 4
+#define SCP_IRQ_CIRQ 5
+#define SCP_IRQ_EINT 6
+#define SCP_IRQ_PMIC 7
+#define SCP_IRQ_UART0 8
+#define SCP_IRQ_UART1 9
+#define SCP_IRQ_I2C0 10
+#define SCP_IRQ_I2C1 11
+#define SCP_IRQ_I2C2 12
+#define SCP_IRQ_CLOCK 13
+#define SCP_IRQ_MAD_FIFO 14
+#define SCP_IRQ_TIMER0 15
+#define SCP_IRQ_TIMER1 16
+#define SCP_IRQ_TIMER2 17
+#define SCP_IRQ_TIMER3 18
+#define SCP_IRQ_TIMER4 19
+#define SCP_IRQ_TIMER5 20
+#define SCP_IRQ_TIMER_STATUS 21
+#define SCP_IRQ_UART0_RX 22
+#define SCP_IRQ_UART1_RX 23
+#define SCP_IRQ_DMA 24
+#define SCP_IRQ_AUDIO 25
+#define SCP_IRQ_MD1_F216 26
+#define SCP_IRQ_MD1 27
+#define SCP_IRQ_C2K 28
+#define SCP_IRQ_SPI0 29
+#define SCP_IRQ_SPI1 30
+#define SCP_IRQ_SPI2 31
+#define SCP_IRQ_AP_EINT 32
+#define SCP_IRQ_DEBUG 33
+#define SCP_CCIF0 34
+#define SCP_CCIF1 35
+#define SCP_CCIF2 36
+#define SCP_IRQ_WDT 37
+#define SCP_IRQ_USB0 38
+#define SCP_IRQ_USB1 39
+#define SCP_IRQ_TWAM 40
+#define SCP_IRQ_INFRA 41
+#define SCP_IRQ_HWDVFS_HIGH 42
+#define SCP_IRQ_HWDVFS_LOW 43
+#define SCP_IRQ_CLOCK2 44
+/* RESERVED 45-52 */
+#define SCP_IRQ_AP_EINT2 53
+#define SCP_IRQ_AP_EINT_EVT 54
+#define SCP_IRQ_MAD_DATA 55
+
+#define SCP_CFG_BASE 0x405C0000
+
+#define SCP_AP_RESOURCE REG32(SCP_CFG_BASE + 0x04)
+#define SCP_BUS_RESOURCE REG32(SCP_CFG_BASE + 0x08)
+
+/* SCP to host interrupt */
+#define SCP_HOST_INT REG32(SCP_CFG_BASE + 0x1C)
+#define IPC_SCP2HOST_SSHUB 0xff0000
+#define WDT_INT 0x100
+#define IPC_SCP2HOST 0xff
+
+/* SCP to SPM interrupt */
+#define SCP_SPM_INT REG32(SCP_CFG_BASE + 0x20)
+#define SCP_SPM_INT2 REG32(SCP_CFG_BASE + 0x24)
+
+#define SCP_GIPC_IN REG32(SCP_CFG_BASE + 0x28)
+#define SCP_CONN_INT REG32(SCP_CFG_BASE + 0x2C)
+
+/* 8 general purpose registers, 0 ~ 7 */
+#define SCP_GPR REG32_ADDR(SCP_CFG_BASE + 0x50)
+/*
+ * SCP_GPR[0]
+ * b15-b0 : scratchpad
+ * b31-b16 : saved flags
+ * SCP_GPR[1]
+ * b15-b0 : power on state
+ */
+#define SCP_PWRON_STATE SCP_GPR[1]
+#define PWRON_DEFAULT 0xdee80000
+#define PWRON_WATCHDOG (1 << 0)
+#define PWRON_RESET (1 << 1)
+/* AP defined features */
+#define SCP_EXPECTED_FREQ SCP_GPR[3]
+#define SCP_CURRENT_FREQ SCP_GPR[4]
+#define SCP_REBOOT SCP_GPR[5]
+#define READY_TO_REBOOT 0x34
+#define REBOOT_OK 1
+
+/* Miscellaneous */
+#define SCP_SEMAPHORE REG32(SCP_CFG_BASE + 0x90)
+#define CORE_CONTROL REG32(SCP_CFG_BASE + 0xA0)
+#define CORE_FPU_FLAGS REG32(SCP_CFG_BASE + 0xA4)
+#define CORE_REG_SP REG32(SCP_CFG_BASE + 0xA8)
+#define CORE_REG_LR REG32(SCP_CFG_BASE + 0xAC)
+#define CORE_REG_PSP REG32(SCP_CFG_BASE + 0xB0)
+#define CORE_REG_PC REG32(SCP_CFG_BASE + 0xB4)
+#define SCP_SLP_PROTECT_CFG REG32(SCP_CFG_BASE + 0xC8)
+#define SCP_ONE_TIME_LOCK REG32(SCP_CFG_BASE + 0xDC)
+#define SCP_SECURE_CTRL REG32(SCP_CFG_BASE + 0xE0)
+#define ENABLE_SPM_MASK_VREQ (1 << 28)
+#define DISABLE_REMAP (1 << 22)
+#define DISABLE_JTAG (1 << 21)
+#define DISABLE_AP_TCM (1 << 20)
+#define SCP_SYS_CTRL REG32(SCP_CFG_BASE + 0xE4)
+#define DDREN_FIX_VALUE (1 << 28)
+#define AUTO_DDREN (1 << 18)
+
+/* Memory remap control */
+#define SCP_REMAP_CFG1 REG32(SCP_CFG_BASE + 0x120)
+#define SCP_REMAP_CFG2 REG32(SCP_CFG_BASE + 0x124)
+#define SCP_REMAP_CFG3 REG32(SCP_CFG_BASE + 0x128)
+#define SCP_L1_REMAP_CFG0 REG32(SCP_CFG_BASE + 0x130)
+#define SCP_L1_REMAP_CFG1 REG32(SCP_CFG_BASE + 0x130)
+#define SCP_L1_REMAP_CFG2 REG32(SCP_CFG_BASE + 0x134)
+#define SCP_L1_REMAP_CFG3 REG32(SCP_CFG_BASE + 0x138)
+#define SCP_L1_REMAP_OTHER REG32(SCP_CFG_BASE + 0x13C)
+
+/* INTC control */
+#define SCP_INTC_BASE (SCP_CFG_BASE + 0x2000)
+#define SCP_INTC_IRQ_STATUS REG32(SCP_INTC_BASE)
+#define SCP_INTC_IRQ_ENABLE REG32(SCP_INTC_BASE + 0x04)
+#define SCP_INTC_IRQ_OUTPUT REG32(SCP_INTC_BASE + 0x08)
+#define SCP_INTC_IRQ_WAKEUP REG32(SCP_INTC_BASE + 0x0C)
+#define SCP_INTC_NMI REG32(SCP_INTC_BASE + 0x10)
+#define SCP_INTC_SPM_WAKEUP REG32(SCP_INTC_BASE + 0x14)
+#define SCP_INTC_SPM_WAKEUP_MSB REG32(SCP_INTC_BASE + 0x18)
+#define SCP_INTC_UART_RX_IRQ REG32(SCP_INTC_BASE + 0x1C)
+#define SCP_INTC_IRQ_STATUS_MSB REG32(SCP_INTC_BASE + 0x80)
+#define SCP_INTC_IRQ_ENABLE_MSB REG32(SCP_INTC_BASE + 0x84)
+#define SCP_INTC_IRQ_OUTPUT_MSB REG32(SCP_INTC_BASE + 0x88)
+#define SCP_INTC_IRQ_WAKEUP_MSB REG32(SCP_INTC_BASE + 0x8C)
+
+/* Timer */
+#define NUM_TIMERS 6
+#define SCP_TIMER_BASE(n) (SCP_CFG_BASE + 0x3000 + (0x10 * (n)))
+#define SCP_TIMER_EN(n) REG32(SCP_TIMER_BASE(n))
+#define SCP_TIMER_RESET_VAL(n) REG32(SCP_TIMER_BASE(n) + 0x04)
+#define SCP_TIMER_VAL(n) REG32(SCP_TIMER_BASE(n) + 0x08)
+#define SCP_TIMER_IRQ_CTRL(n) REG32(SCP_TIMER_BASE(n) + 0x0C)
+#define TIMER_IRQ_ENABLE (1 << 0)
+#define TIMER_IRQ_STATUS (1 << 4)
+#define TIMER_IRQ_CLEAR (1 << 5)
+#define SCP_TIMER_CLK_SEL(n) REG32(SCP_TIMER_BASE(n) + 0x40)
+#define TIMER_CLK_32K (0 << 4)
+#define TIMER_CLK_26M (1 << 4)
+#define TIMER_CLK_BCLK (2 << 4)
+#define TIMER_CLK_PCLK (3 << 4)
+#define TIMER_CLK_MASK (3 << 4)
+/* OS timer */
+#define SCP_OSTIMER_BASE (SCP_CFG_BASE + 0x3080)
+#define SCP_OSTIMER_CON REG32(SCP_OSTIMER_BASE)
+#define SCP_OSTIMER_INIT_L REG32(SCP_OSTIMER_BASE + 0x04)
+#define SCP_OSTIMER_INIT_H REG32(SCP_OSTIMER_BASE + 0x08)
+#define SCP_OSTIMER_VAL_L REG32(SCP_OSTIMER_BASE + 0x0C)
+#define SCP_OSTIMER_VAL_H REG32(SCP_OSTIMER_BASE + 0x10)
+#define SCP_OSTIMER_TVAL REG32(SCP_OSTIMER_BASE + 0x14)
+#define SCP_OSTIMER_IRQ_ACK REG32(SCP_OSTIMER_BASE + 0x18)
+#define OSTIMER_LATCH0_EN (1 << 5)
+#define OSTIMER_LATCH1_EN (1 << 13)
+#define OSTIMER_LATCH2_EN (1 << 21)
+#define SCP_OSTIMER_LATCH_CTRL REG32(SCP_OSTIMER_BASE + 0x20)
+#define SCP_OSTIMER_LATCH0_L REG32(SCP_OSTIMER_BASE + 0x24)
+#define SCP_OSTIMER_LATCH0_H REG32(SCP_OSTIMER_BASE + 0x28)
+#define SCP_OSTIMER_LATCH1_L REG32(SCP_OSTIMER_BASE + 0x2C)
+#define SCP_OSTIMER_LATCH1_H REG32(SCP_OSTIMER_BASE + 0x30)
+#define SCP_OSTIMER_LATCH2_L REG32(SCP_OSTIMER_BASE + 0x34)
+#define SCP_OSTIMER_LATCH2_H REG32(SCP_OSTIMER_BASE + 0x38)
+
+/* Clock, PMIC wrapper, etc. */
+#define SCP_CLK_BASE (SCP_CFG_BASE + 0x4000)
+#define SCP_CLK_SEL REG32(SCP_CLK_BASE)
+#define SCP_CLK_EN REG32(SCP_CLK_BASE + 0x04)
+#define EN_CLK_SYS (1 << 0) /* System clock */
+#define EN_CLK_HIGH (1 << 1) /* ULPOSC */
+#define CG_CLK_HIGH (1 << 2)
+#define EN_SYS_IRQ (1 << 16)
+#define EN_HIGH_IRQ (1 << 17)
+#define SCP_CLK_SAFE_ACK REG32(SCP_CLK_BASE + 0x08)
+#define SCP_CLK_ACK REG32(SCP_CLK_BASE + 0x0C)
+#define SCP_CLK_IRQ_ACK REG32(SCP_CLK_BASE + 0x10)
+/*
+ * System clock counter value.
+ * CLK_SYS_VAL[9:0] System clock counter initial/reset value.
+ */
+#define SCP_CLK_SYS_VAL REG32(SCP_CLK_BASE + 0x14)
+#define CLK_SYS_VAL_MASK 0x3ff /* 10 bits */
+#define CLK_SYS_VAL(n) ((n) & CLK_SYS_VAL_MASK)
+/*
+ * ULPOSC clock counter value.
+ * CLK_HIGH_VAL[9:0] ULPOSC clock counter initial/reset value.
+ */
+#define SCP_CLK_HIGH_VAL REG32(SCP_CLK_BASE + 0x18)
+#define CLK_HIGH_VAL_MASK 0x3ff /* 10 bits */
+#define CLK_HIGH_VAL(n) ((n) & CLK_HIGH_VAL_MASK)
+#define SCP_CLK_SLOW_SEL REG32(SCP_CLK_BASE + 0x1C)
+#define CKSW_SEL_SLOW_MASK 0x3
+#define CKSW_SEL_SLOW_DIV_MASK 0x30
+#define CKSW_SEL_SLOW_SYS_CLK 0
+#define CKSW_SEL_SLOW_32K_CLK 1
+#define CKSW_SEL_SLOW_ULPOSC1_CLK 2
+#define CKSW_SEL_SLOW_ULPOSC2_CLK 3
+/*
+ * Sleep mode control.
+ * VREQ_COUNT[7:1] Number of cycles to wait when requesting PMIC to raise the
+ * voltage after returning from sleep mode.
+ */
+#define SCP_CLK_SLEEP_CTRL REG32(SCP_CLK_BASE + 0x20)
+#define EN_SLEEP_CTRL (1 << 0)
+#define VREQ_COUNTER_MASK 0xfe
+#define VREQ_COUNTER_VAL(v) (((v) << 1) & VREQ_COUNTER_MASK)
+#define SPM_SLEEP_MODE (1 << 8)
+#define SPM_SLEEP_MODE_CLK_AO (1 << 9)
+#define SCP_CLK_DIV_SEL REG32(SCP_CLK_BASE + 0x24)
+#define SCP_CLK_DEBUG REG32(SCP_CLK_BASE + 0x28)
+#define SCP_CLK_SRAM_POWERDOWN REG32(SCP_CLK_BASE + 0x2C)
+#define SCP_CLK_GATE REG32(SCP_CLK_BASE + 0x30)
+#define CG_TIMER_M (1 << 0)
+#define CG_TIMER_B (1 << 1)
+#define CG_MAD_M (1 << 2)
+#define CG_I2C_M (1 << 3)
+#define CG_I2C_B (1 << 4)
+#define CG_GPIO_M (1 << 5)
+#define CG_AP2P_M (1 << 6)
+#define CG_UART_M (1 << 7)
+#define CG_UART_B (1 << 8)
+#define CG_UART_RSTN (1 << 9)
+#define CG_UART1_M (1 << 10)
+#define CG_UART1_B (1 << 11)
+#define CG_UART1_RSTN (1 << 12)
+#define CG_SPI0 (1 << 13)
+#define CG_SPI1 (1 << 14)
+#define CG_SPI2 (1 << 15)
+#define CG_DMA_CH0 (1 << 16)
+#define CG_DMA_CH1 (1 << 17)
+#define CG_DMA_CH2 (1 << 18)
+#define CG_DMA_CH3 (1 << 19)
+#define CG_TWAM (1 << 20)
+#define CG_CACHE_I_CTRL (1 << 21)
+#define CG_CACHE_D_CTRL (1 << 22)
+#define SCP_PMICW_CTRL REG32(SCP_CLK_BASE + 0x34)
+#define PMICW_SLEEP_REQ (1 << 0)
+#define PMICW_SLEEP_ACK (1 << 4)
+#define PMICW_CLK_MUX (1 << 8)
+#define PMICW_DCM (1 << 9)
+#define SCP_SLEEP_WAKE_DEBUG REG32(SCP_CLK_BASE + 0x38)
+#define SCP_DCM_EN REG32(SCP_CLK_BASE + 0x3C)
+#define SCP_WAKE_CKSW REG32(SCP_CLK_BASE + 0x40)
+#define WAKE_CKSW_SEL_NORMAL_MASK 0x3
+#define WAKE_CKSW_SEL_SLOW_MASK 0x30
+#define WAKE_CKSW_SEL_SLOW_DEFAULT 0x10
+#define SCP_CLK_UART REG32(SCP_CLK_BASE + 0x44)
+#define SCP_CLK_BCK REG32(SCP_CLK_BASE + 0x48)
+#define SCP_CLK_SPI_BCK REG32(SCP_CLK_BASE + 0x4C)
+#define SCP_CLK_DIV_CNT REG32(SCP_CLK_BASE + 0x50)
+#define SCP_CPU_VREQ REG32(SCP_CLK_BASE + 0x54)
+#define CPU_VREQ_HW_MODE 0x10001
+#define SCP_CLK_CLEAR REG32(SCP_CLK_BASE + 0x58)
+#define SCP_CLK_HIGH_CORE REG32(SCP_CLK_BASE + 0x5C)
+#define SCP_SLEEP_IRQ2 REG32(SCP_CLK_BASE + 0x64)
+#define SCP_CLK_ON_CTRL REG32(SCP_CLK_BASE + 0x6C)
+#define HIGH_AO (1 << 0)
+#define HIGH_CG_AO (1 << 2)
+#define HIGH_CORE_AO (1 << 4)
+#define HIGH_CORE_DIS_SUB (1 << 5)
+#define HIGH_CORE_CG_AO (1 << 6)
+#define HIGH_FINAL_VAL_MASK 0x1f00
+#define HIGH_FINAL_VAL_DEFAULT 0x300
+#define SCP_CLK_L1_SRAM_PD REG32(SCP_CLK_BASE + 0x80)
+#define SCP_CLK_TCM_TAIL_SRAM_PD REG32(SCP_CLK_BASE + 0x94)
+#define SCP_CLK_SLEEP REG32(SCP_CLK_BASE + 0xA0)
+#define SLOW_WAKE_DISABLE 1
+#define SCP_FAST_WAKE_CNT_END REG32(SCP_CLK_BASE + 0xA4)
+#define FAST_WAKE_CNT_END_MASK 0xfff
+#define FAST_WAKE_CNT_END_DEFAULT 0x18
+#define MEM_CK_CS_ISO_CNT_END_MASK 0x7f0000
+
+/* Peripherals */
+#define SCP_I2C0_BASE (SCP_CFG_BASE + 0x5000)
+#define SCP_I2C1_BASE (SCP_CFG_BASE + 0x6000)
+#define SCP_I2C2_BASE (SCP_CFG_BASE + 0x7000)
+
+#define SCP_GPIO_BASE (SCP_CFG_BASE + 0x8000)
+#define SCP_UART0_BASE (SCP_CFG_BASE + 0x9000)
+#define SCP_UART1_BASE (SCP_CFG_BASE + 0xE000)
+#define SCP_UART_COUNT 2
+
+/* External GPIO interrupt */
+#define SCP_EINT_BASE (SCP_CFG_BASE + 0xA000)
+#define SCP_EINT_STATUS REG32_ADDR(SCP_EINT_BASE)
+#define SCP_EINT_ACK REG32_ADDR(SCP_EINT_BASE + 0x040)
+#define SCP_EINT_MASK_GET REG32_ADDR(SCP_EINT_BASE + 0x080)
+#define SCP_EINT_MASK_SET REG32_ADDR(SCP_EINT_BASE + 0x0C0)
+#define SCP_EINT_MASK_CLR REG32_ADDR(SCP_EINT_BASE + 0x100)
+#define SCP_EINT_SENS_GET REG32_ADDR(SCP_EINT_BASE + 0x140)
+#define SCP_EINT_SENS_SET REG32_ADDR(SCP_EINT_BASE + 0x180)
+#define SCP_EINT_SENS_CLR REG32_ADDR(SCP_EINT_BASE + 0x1C0)
+#define SCP_EINT_SOFT_GET REG32_ADDR(SCP_EINT_BASE + 0x200)
+#define SCP_EINT_SOFT_SET REG32_ADDR(SCP_EINT_BASE + 0x240)
+#define SCP_EINT_SOFT_CLR REG32_ADDR(SCP_EINT_BASE + 0x280)
+#define SCP_EINT_POLARITY_GET REG32_ADDR(SCP_EINT_BASE + 0x300)
+#define SCP_EINT_POLARITY_SET REG32_ADDR(SCP_EINT_BASE + 0x340)
+#define SCP_EINT_POLARITY_CLR REG32_ADDR(SCP_EINT_BASE + 0x380)
+#define SCP_EINT_D0_EN REG32_ADDR(SCP_EINT_BASE + 0x400)
+#define SCP_EINT_D1_EN REG32_ADDR(SCP_EINT_BASE + 0x420)
+#define SCP_EINT_DBNC_GET REG32_ADDR(SCP_EINT_BASE + 0x500)
+#define SCP_EINT_DBNC_SET REG32_ADDR(SCP_EINT_BASE + 0x600)
+#define SCP_EINT_DBNC_CLR REG32_ADDR(SCP_EINT_BASE + 0x700)
+
+#define SCP_PMICWP2P_BASE (SCP_CFG_BASE + 0xB000)
+#define PMICW_WACS_CMD REG32(SCP_PMICWP2P + 0x200)
+#define PMICW_WACS_RDATA REG32(SCP_PMICWP2P + 0x204)
+#define PMICW_WACS_VLDCLR REG32(SCP_PMICWP2P + 0x208)
+#define SCP_SPMP2P_BASE (SCP_CFG_BASE + 0xC000)
+#define SCP_DMA_BASE (SCP_CFG_BASE + 0xD000)
+#define DMA_ACKINT_CHX REG32(SCP_DMA_BASE + 0x20)
+#define SCP_SPI0_BASE (SCP_CFG_BASE + 0xF000)
+#define SCP_SPI1_BASE (SCP_CFG_BASE + 0x10000)
+#define SCP_SPI2_BASE (SCP_CFG_BASE + 0x11000)
+
+/* ARMV7 regs */
+#define ARM_SCB_SCR REG32(0xE000ED10)
+#define SCR_DEEPSLEEP (1 << 2)
+
+/* AP regs */
+#define AP_BASE 0xA0000000
+#define TOPCK_BASE AP_BASE /* Top clock */
+#define SCP_UART2_BASE (AP_BASE + 0x01002000) /* AP UART0 */
+
+/* GPIO */
+#define AP_GPIO_BASE (AP_BASE + 0x00005000)
+/*
+ * AP_GPIO_DIR
+ * GPIO input/out direction, 1 bit per pin.
+ * 0:input 1:output
+ */
+#define AP_GPIO_DIR(n) REG32(AP_GPIO_BASE + ((n) << 4))
+/*
+ * AP_GPIO_DOUT, n in [0..5]
+ * GPIO output level, 1 bit per pin
+ * 0:low 1:high
+ */
+#define AP_GPIO_DOUT(n) REG32(AP_GPIO_BASE + 0x100 + ((n) << 4))
+/*
+ * AP_GPIO_DIN, n in [0..5]
+ * GPIO input level, 1 bit per pin
+ * 0:low 1:high
+ */
+#define AP_GPIO_DIN(n) REG32(AP_GPIO_BASE + 0x200 + ((n) << 4))
+/*
+ * AP_GPIO_MODE, n in [0..22]
+ * Pin mode selection, 4 bit per pin
+ * bit3 - write enable, set to 1 for hw to fetch bit2,1,0.
+ * bit2-0 - mode 0 ~ 7
+ */
+#define AP_GPIO_MODE(n) REG32(AP_GPIO_BASE + 0x300 + ((n) << 4))
+#define AP_GPIO_TRAP REG32(AP_GPIO_BASE + 0x6B0)
+#define AP_GPIO_DUMMY REG32(AP_GPIO_BASE + 0x6C0)
+#define AP_GPIO_DBG REG32(AP_GPIO_BASE + 0x6D0)
+#define AP_GPIO_BANK REG32(AP_GPIO_BASE + 0x6E0)
+/* AP_GPIO_SEC, n in [0..5] */
+#define AP_GPIO_SEC(n) REG32(AP_GPIO_BASE + 0xF00 + ((n) << 4))
+
+/*
+ * PLL ULPOSC
+ * ULPOSC1: AP_ULPOSC_CON[0] AP_ULPOSC_CON[1]
+ * ULPOSC2: AP_ULPOSC_CON[2] AP_ULPOSC_CON[3]
+ * osc: 1 for ULPOSC1, 2 for ULPSOC2.
+ */
+#define AP_ULPOSC_BASE0 (AP_BASE + 0xC700 - 0x8)
+#define AP_ULPOSC_BASE1 (AP_BASE + 0xC704 - 0x8)
+#define AP_ULPOSC_CON02(osc) REG32(AP_ULPOSC_BASE0 + (osc) * 0x8)
+#define AP_ULPOSC_CON13(osc) REG32(AP_ULPOSC_BASE1 + (osc) * 0x8)
+/*
+ * AP_ULPOSC_CON[0,2]
+ * bit0-5: calibration
+ * bit6-12: I-band
+ * bit13-16: F-band
+ * bit17-22: div
+ * bit23: CP_EN
+ * bit24-31: reserved
+ */
+#define OSC_CALI_MSK (0x3f << 0)
+#define OSC_IBAND_MASK (0x7f << 6)
+#define OSC_FBAND_MASK (0x0f << 13)
+#define OSC_DIV_MASK (0x1f << 17)
+#define OSC_CP_EN (1 << 23)
+#define OSC_RESERVED_MASK (0xff << 24)
+/* AP_ILPOSC_CON[1,3] */
+#define OSC_MOD_MASK (0x03 << 0)
+#define OSC_DIV2_EN (1 << 2)
+
+#define DUMMY_GPIO_BANK 0
+
+#ifndef __ASSEMBLER__
+
+/* Cortex-M4 mod */
+#define CM4_MODIFICATION REG32(0xE00FE000)
+#define CM4_DCM_FEATURE REG32(0xE00FE004)
+/* UART, 16550 compatible */
+#define SCP_UART_BASE(n) CONCAT3(SCP_UART, n, _BASE)
+#define UART_REG(n, offset) REG32_ADDR(SCP_UART_BASE(n))[offset]
+#define UART_IRQ(n) CONCAT2(SCP_IRQ_UART, n)
+#define UART_RX_IRQ(n) CONCAT3(SCP_IRQ_UART, n, _RX)
+
+/* Watchdog */
+#define SCP_WDT_BASE (SCP_CFG_BASE + 0x84)
+#define SCP_WDT_REG(offset) REG32(SCP_WDT_BASE + offset)
+#define SCP_WDT_CFG SCP_WDT_REG(0)
+#define SCP_WDT_FREQ 33825
+#define SCP_WDT_MAX_PERIOD 0xFFFFF /* 31 seconds */
+#define SCP_WDT_PERIOD(ms) (SCP_WDT_FREQ * (ms) / 1000)
+#define SCP_WDT_ENABLE (1 << 31)
+#define SCP_WDT_RELOAD SCP_WDT_REG(4)
+#define SCP_WDT_RELOAD_VALUE 1
+
+#endif /* !__ASSEMBLER__ */
+#endif /* __CROS_EC_REGISTERS_H */
diff --git a/chip/mt_scp/serial_reg.h b/chip/mt_scp/serial_reg.h
new file mode 100644
index 0000000000..a23b19ded3
--- /dev/null
+++ b/chip/mt_scp/serial_reg.h
@@ -0,0 +1,90 @@
+/* Copyright 2018 The Chromium OS Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ *
+ * UART register map
+ */
+
+#ifndef __CROS_EC_SERIAL_REG_H
+#define __CROS_EC_SERIAL_REG_H
+
+#include "registers.h"
+
+/* Number of hardware ports */
+#define HW_UART_PORTS 2
+
+/* DLAB (Divisor Latch Access Bit) == 0 */
+
+/* Data register
+ * (Read) Rcvr buffer register
+ * (Write) Xmit holding register
+ */
+#define UART_DATA(n) UART_REG(n, 0)
+/* (Write) Interrupt enable register */
+#define UART_IER(n) UART_REG(n, 1)
+#define UART_IER_RDI (1 << 0) /* Recv data int */
+#define UART_IER_THRI (1 << 1) /* Xmit holding register int */
+#define UART_IER_RLSI (1 << 2) /* Rcvr line status int */
+#define UART_IER_MSI (1 << 3) /* Modem status int */
+/* (Read) Interrupt ID register */
+#define UART_IIR(n) UART_REG(n, 2)
+#define UART_IIR_NO_INT (1 << 0) /* No int pending */
+#define UART_IIR_ID_MASK 0x0e /* Interrupt ID mask */
+#define UART_IIR_MSI 0x00
+#define UART_IIR_THRI 0x02
+#define UART_IIR_RDI 0x04
+#define UART_IIR_RLSI 0x06
+#define UART_IIR_BUSY 0x07 /* DW APB busy */
+/* (Write) FIFO control register */
+#define UART_FCR(n) UART_REG(n, 2)
+#define UART_FCR_ENABLE_FIFO (1 << 0) /* Enable FIFO */
+#define UART_FCR_CLEAR_RCVR (1 << 1) /* Clear rcvr FIFO */
+#define UART_FCR_CLEAR_XMIT (1 << 2) /* Clear xmit FIFO */
+#define UART_FCR_DMA_SELECT (1 << 3)
+/* FIFO trigger levels */
+#define UART_FCR_T_TRIG_00 0x00
+#define UART_FCR_T_TRIG_01 0x10
+#define UART_FCR_T_TRIG_10 0x20
+#define UART_FCR_T_TRIG_11 0x30
+#define UART_FCR_R_TRIG_00 0x00
+#define UART_FCR_R_TRIG_01 0x40
+#define UART_FCR_R_TRIG_10 0x80
+#define UART_FCR_R_TRIG_11 0x80
+/* (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 (1 << 2) /* Stop bits: 1bit, 2bits */
+#define UART_LCR_PARITY (1 << 3) /* Parity enable */
+#define UART_LCR_EPAR (1 << 4) /* Even parity */
+#define UART_LCR_SPAR (1 << 5) /* Stick parity */
+#define UART_LCR_SBC (1 << 6) /* Set break control */
+#define UART_LCR_DLAB (1 << 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 (1 << 0) /* Data ready */
+#define UART_LSR_OE (1 << 1) /* Overrun error */
+#define UART_LSR_PE (1 << 2) /* Parity error */
+#define UART_LSR_FE (1 << 3) /* Frame error */
+#define UART_LSR_BI (1 << 4) /* Break interrupt */
+#define UART_LSR_THRE (1 << 5) /* Xmit-hold-register empty */
+#define UART_LSR_TEMT (1 << 6) /* Xmit empty */
+#define UART_LSR_FIFOE (1 << 7) /* FIFO error */
+
+/* DLAB == 1 */
+
+/* (Write) Divisor latch */
+#define UART_DLL(n) UART_REG(n, 0) /* Low */
+#define UART_DLH(n) UART_REG(n, 1) /* High */
+
+/* MTK extension */
+#define UART_HIGHSPEED(n) UART_REG(n, 9)
+#define UART_SAMPLE_COUNT(n) UART_REG(n, 10)
+#define UART_SAMPLE_POINT(n) UART_REG(n, 11)
+#define UART_RATE_FIX(n) UART_REG(n, 13)
+
+#endif /* __CROS_EC_SERIAL_REG_H */
diff --git a/chip/mt_scp/system.c b/chip/mt_scp/system.c
new file mode 100644
index 0000000000..77275a8e85
--- /dev/null
+++ b/chip/mt_scp/system.c
@@ -0,0 +1,307 @@
+/* Copyright 2018 The Chromium OS Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+/* System : hardware specific implementation */
+
+#include "console.h"
+#include "cpu.h"
+#include "flash.h"
+#include "hooks.h"
+#include "host_command.h"
+#include "registers.h"
+#include "system.h"
+#include "task.h"
+#include "util.h"
+#include "version.h"
+#include "watchdog.h"
+
+/*
+ * SCP_GPR[0] b15-b0 - scratchpad
+ * SCP_GPR[0] b31-b16 - saved_flags
+ */
+
+int system_set_scratchpad(uint32_t value)
+{
+ /* Check if value fits in 16 bits */
+ if (value & 0xffff0000)
+ return EC_ERROR_INVAL;
+
+ SCP_GPR[0] = (SCP_GPR[0] & 0xffff0000) | value;
+
+ return EC_SUCCESS;
+}
+
+uint32_t system_get_scratchpad(void)
+{
+ return SCP_GPR[0] & 0xffff;
+}
+
+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 "";
+}
+
+void chip_pre_init(void)
+{
+}
+
+static void scp_enable_tcm(void)
+{
+ /* Enable tightly coupled memory (TCM) */
+ SCP_CLK_L1_SRAM_PD = 0;
+ SCP_CLK_TCM_TAIL_SRAM_PD = 0;
+ /* SCP CM4 mod */
+ CM4_MODIFICATION = 3;
+ CM4_DCM_FEATURE = 3;
+}
+
+static void scp_enable_pirq(void)
+{
+ /* Enable peripheral to SCP IRQ */
+ SCP_INTC_IRQ_ENABLE = 0xFFFFFFFF;
+ SCP_INTC_IRQ_ENABLE_MSB = 0xFFFFFFFF;
+}
+
+/* TODO(b/120176040): move to clock.c */
+static void scp_ulposc_config(int osc)
+{
+ /* TODO(b/120176040): add ULPOSC calibration */
+ const struct {
+ uint8_t div;
+ uint8_t cali;
+ } ulposc_config[] = {
+ { .div = 12, .cali = 32},
+ { .div = 16, .cali = 32},
+ };
+ const int osc_index = osc - 1;
+ uint32_t val;
+
+ if (osc != 1 || osc != 2)
+ return;
+
+ /* Clear all bits */
+ val = 0;
+ /* Enable CP */
+ val |= OSC_CP_EN;
+ /* Set div */
+ val |= ulposc_config[osc_index].div << 17;
+ /* F-band = 0, I-band = 4 */
+ val |= 4 << 6;
+ /* Set calibration */
+ val |= ulposc_config[osc_index].cali;
+ /* Set control register 1 */
+ AP_ULPOSC_CON02(osc) = val;
+ /* Set control register 2, enable div2 */
+ AP_ULPOSC_CON13(osc) |= OSC_DIV2_EN;
+}
+
+/*
+ * TODO(b/120176040): move to clock.c and separate into
+ * scp_set_clock_high_enable and _disable functions.
+ */
+void scp_set_clock_high(int osc, int on)
+{
+ if (on) {
+ switch (osc) {
+ case 1:
+ /* Enable ULPOSC */
+ SCP_CLK_EN |= EN_CLK_HIGH;
+ /* TODO: Turn on clock gate after 25ms */
+ SCP_CLK_EN |= CG_CLK_HIGH;
+ break;
+ case 2:
+ /* Enable ULPOSC1 & ULPOSC2 */
+ SCP_CLK_EN |= EN_CLK_HIGH;
+ SCP_CLK_ON_CTRL &= ~HIGH_CORE_DIS_SUB;
+ /* TODO: Turn on clock gate after 25ms */
+ SCP_CLK_HIGH_CORE |= 1;
+ break;
+ default:
+ break;
+ }
+ } else {
+ switch (osc) {
+ case 1:
+ /* Disable clock gate */
+ SCP_CLK_EN &= CG_CLK_HIGH;
+ /* TODO: Turn off ULPOSC1 after 50us */
+ SCP_CLK_EN &= EN_CLK_HIGH;
+ break;
+ case 2:
+ SCP_CLK_HIGH_CORE &= ~1;
+ /* TODO: Turn off ULPOSC1 after 50us */
+ SCP_CLK_ON_CTRL |= HIGH_CORE_DIS_SUB;
+ break;
+ default:
+ break;
+ }
+ }
+ /* TODO: Wait 25us */
+}
+
+/* TODO(b/120176040): move to clock.c */
+static void scp_enable_clock(void)
+{
+ /* VREQ */
+ SCP_CPU_VREQ = 0x10001;
+ SCP_SECURE_CTRL &= ~ENABLE_SPM_MASK_VREQ;
+
+ /* DDREN auto mode */
+ SCP_SYS_CTRL |= AUTO_DDREN;
+
+ /* Set settle time */
+ SCP_CLK_SYS_VAL = 1; /* System clock */
+ SCP_CLK_HIGH_VAL = 1; /* ULPOSC */
+ SCP_CLK_SLEEP_CTRL = (SCP_CLK_SLEEP_CTRL & ~VREQ_COUNTER_MASK) | 2;
+
+ /* Disable slow wake */
+ SCP_CLK_SLEEP = SLOW_WAKE_DISABLE;
+ /* Disable SPM sleep control, disable sleep mode */
+ SCP_CLK_SLEEP_CTRL &= ~(SPM_SLEEP_MODE | EN_SLEEP_CTRL);
+
+ /* Turn off ULPOSC2 */
+ SCP_CLK_ON_CTRL |= HIGH_CORE_DIS_SUB;
+ scp_ulposc_config(1);
+ scp_set_clock_high(1, 1); /* Turn on ULPOSC1 */
+ scp_ulposc_config(2);
+ scp_set_clock_high(2, 1); /* Turn on ULPOSC2 */
+
+ /* Enable default clock gate */
+ SCP_CLK_GATE |= CG_DMA_CH3 | CG_DMA_CH2 | CG_DMA_CH1 | CG_DMA_CH0 |
+ CG_I2C_M | CG_MAD_M;
+}
+
+static void scp_memmap_init(void)
+{
+ /*
+ * SCP addr : AP addr
+ * 0xA0000000 0x10000000
+ * 0xB0000000 0x20000000
+ * 0xC0000000 0x30000000
+ * 0x20000000 0x40000000
+ * 0x30000000 0x50000000
+ * 0x60000000 0x60000000
+ * 0x70000000 0x70000000
+ * 0x80000000 0x80000000
+ * 0xF0000000 0x90000000
+ *
+ * Default config, LARGE DRAM not active:
+ * REG32(0xA0001F00) & 0x2000 != 0
+ */
+ SCP_REMAP_CFG1 = 0x07060504;
+ SCP_REMAP_CFG2 = 0x02010008;
+ SCP_REMAP_CFG3 = 0x10000A03;
+}
+
+void system_pre_init(void)
+{
+ /* SRAM */
+ scp_enable_tcm();
+ /* Clock */
+ scp_enable_clock();
+ /* Peripheral IRQ */
+ scp_enable_pirq();
+ /* Init dram mapping */
+ scp_memmap_init();
+ /* Init inter processor communication */
+ /* scp_ipi_init(); */
+}
+
+void system_reset(int flags)
+{
+ uint32_t save_flags = 0;
+
+ /* Disable interrupts to avoid task swaps during reboot */
+ interrupt_disable();
+
+ /* Save current reset reasons if necessary */
+ if (flags & SYSTEM_RESET_PRESERVE_FLAGS)
+ save_flags = system_get_reset_flags() | RESET_FLAG_PRESERVED;
+
+ if (flags & SYSTEM_RESET_LEAVE_AP_OFF)
+ save_flags |= RESET_FLAG_AP_OFF;
+
+ /* Remember that the software asked us to hard reboot */
+ if (flags & SYSTEM_RESET_HARD)
+ save_flags |= RESET_FLAG_HARD;
+
+ /* Reset flags are 32-bits, but save only 16 bits. */
+ ASSERT(!(save_flags >> 16));
+ SCP_GPR[0] = (save_flags << 16) | (SCP_GPR[0] & 0xffff);
+
+ /* SCP can not hard reset itself */
+ ASSERT(!(flags & SYSTEM_RESET_HARD));
+
+ if (flags & SYSTEM_RESET_WAIT_EXT) {
+ int i;
+
+ /* Wait 10 seconds for external reset */
+ for (i = 0; i < 1000; i++) {
+ watchdog_reload();
+ udelay(10000);
+ }
+ }
+
+ /* SCB AIRCR reset */
+ CPU_NVIC_APINT = 0x05fa0004;
+ /* Spin wait for chip to reboot */
+ while (1)
+ ;
+}
+
+static void check_reset_cause(void)
+{
+ uint32_t flags = 0;
+ uint32_t raw_reset_cause = SCP_GPR[1];
+
+ /* Set state to power-on */
+ SCP_PWRON_STATE = PWRON_DEFAULT;
+
+ if ((raw_reset_cause & 0xffff0000) == PWRON_DEFAULT) {
+ /* Reboot */
+ if (raw_reset_cause & PWRON_WATCHDOG)
+ flags |= RESET_FLAG_WATCHDOG;
+ else if (raw_reset_cause & PWRON_RESET)
+ flags |= RESET_FLAG_POWER_ON;
+ else
+ flags |= RESET_FLAG_OTHER;
+ } else {
+ /* Power lost restart */
+ flags |= RESET_FLAG_POWER_ON;
+ }
+ system_set_reset_flags(SCP_GPR[0] >> 16);
+ SCP_GPR[0] &= 0xffff;
+}
+
+int system_is_reboot_warm(void)
+{
+ const uint32_t cold_flags =
+ RESET_FLAG_RESET_PIN |
+ RESET_FLAG_POWER_ON |
+ RESET_FLAG_WATCHDOG |
+ RESET_FLAG_HARD |
+ RESET_FLAG_SOFT |
+ RESET_FLAG_HIBERNATE;
+
+ check_reset_cause();
+
+ return !(system_get_reset_flags() & cold_flags);
+}
+
+int system_get_bbram(enum system_bbram_idx idx, uint8_t *value)
+{
+ return EC_ERROR_INVAL;
+}
diff --git a/chip/mt_scp/uart.c b/chip/mt_scp/uart.c
new file mode 100644
index 0000000000..554a53cc1d
--- /dev/null
+++ b/chip/mt_scp/uart.c
@@ -0,0 +1,171 @@
+/* Copyright 2018 The Chromium OS Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+/* SCP UART module */
+
+#include "console.h"
+#include "registers.h"
+#include "serial_reg.h"
+#include "system.h"
+#include "task.h"
+#include "uart.h"
+#include "util.h"
+
+/* Console UART index */
+#define UARTN CONFIG_UART_CONSOLE
+#define UART_WAIT_US 50
+#define UART_IDLE_WAIT_US 500
+
+static uint8_t uart_done, tx_started;
+
+int uart_init_done(void)
+{
+ /*
+ * TODO: AP UART support
+ * When access AP UART port, wait for AP peripheral clock
+ */
+ return uart_done;
+}
+
+void uart_tx_start(void)
+{
+ tx_started = 1;
+
+ /* AP UART mode doesn't support interrupt */
+ if (UARTN >= SCP_UART_COUNT)
+ return;
+
+ if (UART_IER(UARTN) & UART_IER_THRI)
+ return;
+ disable_sleep(SLEEP_MASK_UART);
+ UART_IER(UARTN) |= UART_IER_THRI;
+}
+
+void uart_tx_stop(void)
+{
+ tx_started = 0;
+
+ /* AP UART mode doesn't support interrupt */
+ if (UARTN >= SCP_UART_COUNT)
+ return;
+
+ UART_IER(UARTN) &= ~UART_IER_THRI;
+ enable_sleep(SLEEP_MASK_UART);
+}
+
+void uart_tx_flush(void)
+{
+ while (!(UART_LSR(UARTN) & UART_LSR_TEMT))
+ usleep(uart_wait_us);
+}
+
+int uart_tx_ready(void)
+{
+ /* Check xmit FIFO empty */
+ return UART_LSR(UARTN) & UART_LSR_THRE;
+}
+
+int uart_rx_available(void)
+{
+ /* Check rcvr data ready */
+ return UART_LSR(UARTN) & UART_LSR_DR;
+}
+
+void uart_write_char(char c)
+{
+ while (!uart_tx_ready())
+ usleep(uart_wait_us);
+
+ UART_DATA(UARTN) = c;
+}
+
+int uart_read_char(void)
+{
+ return UART_DATA(UARTN);
+}
+
+void uart_process(void)
+{
+ uart_process_input();
+ uart_process_output();
+}
+
+#if (UARTN < SCP_UART_COUNT)
+void uart_interrupt(void)
+{
+ uint8_t ier;
+
+ task_clear_pending_irq(UART_IRQ(UARTN));
+ uart_process();
+ ier = UART_IER(UARTN);
+ UART_IER(UARTN) = 0;
+ UART_IER(UARTN) = ier;
+}
+DECLARE_IRQ(UART_IRQ(UARTN), uart_interrupt, 2);
+
+void uart_rx_interrupt(void)
+{
+ uint8_t ier;
+
+ task_clear_pending_irq(UART_RX_IRQ(UARTN));
+ SCP_INTC_UART_RX_IRQ &= ~(1 << UARTN);
+ uart_process();
+ ier = UART_IER(UARTN);
+ UART_IER(UARTN) = 0;
+ UART_IER(UARTN) = ier;
+ SCP_INTC_UART_RX_IRQ |= 1 << UARTN;
+}
+DECLARE_IRQ(UART_RX_IRQ(UARTN), uart_rx_interrupt, 2);
+#endif
+
+void uart_task(void)
+{
+#if (UARTN >= SCP_UART_COUNT)
+ while (1) {
+ if (uart_rx_available() || tx_started)
+ uart_process();
+ else
+ task_wait_event(UART_IDLE_WAIT_US);
+ }
+#endif
+}
+
+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);
+
+ /* Init clock */
+#if UARTN == 0
+ SCP_CLK_GATE |= CG_UART_M | CG_UART_B | CG_UART_RSTN;
+#elif UARTN == 1
+ SCP_CLK_GATE |= CG_UART1_M | CG_UART1_B | CG_UART1_RSTN;
+#endif
+
+ /* Init and 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 = 1 and update DLL DLH */
+ UART_LCR(UARTN) |= UART_LCR_DLAB;
+ UART_DLL(UARTN) = div & 0xff;
+ UART_DLH(UARTN) = (div >> 8) & 0xff;
+ UART_LCR(UARTN) &= ~UART_LCR_DLAB;
+ UART_IER(UARTN) |= UART_IER_RDI;
+
+#if (UARTN < SCP_UART_COUNT)
+ task_enable_irq(UART_IRQ(UARTN));
+ task_enable_irq(UART_RX_IRQ(UARTN));
+ /* UART RX IRQ needs an extra enable */
+ SCP_INTC_UART_RX_IRQ |= 1 << UARTN;
+#endif
+ gpio_config_module(MODULE_UART, 1);
+ uart_done = 1;
+}
diff --git a/chip/mt_scp/watchdog.c b/chip/mt_scp/watchdog.c
new file mode 100644
index 0000000000..74e2cad8e5
--- /dev/null
+++ b/chip/mt_scp/watchdog.c
@@ -0,0 +1,33 @@
+/* Copyright 2018 The Chromium OS Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+/* Watchdog driver */
+
+#include "common.h"
+#include "hooks.h"
+#include "panic.h"
+#include "registers.h"
+#include "watchdog.h"
+
+void watchdog_reload(void)
+{
+ SCP_WDT_RELOAD = SCP_WDT_RELOAD_VALUE;
+}
+DECLARE_HOOK(HOOK_TICK, watchdog_reload, HOOK_PRIO_DEFAULT);
+
+int watchdog_init(void)
+{
+ const uint32_t watchdog_timeout =
+ SCP_WDT_PERIOD(CONFIG_WATCHDOG_PERIOD_MS);
+
+ /* Disable watchdog */
+ SCP_WDT_CFG = 0;
+ /* Enable watchdog */
+ SCP_WDT_CFG = SCP_WDT_ENABLE | watchdog_timeout;
+ /* Reload watchdog */
+ watchdog_reload();
+
+ return EC_SUCCESS;
+}