summaryrefslogtreecommitdiff
path: root/chip
diff options
context:
space:
mode:
authorScott Worley <scott.worley@microchip.corp-partner.google.com>2017-12-21 14:49:50 -0500
committerchrome-bot <chrome-bot@chromium.org>2017-12-28 14:50:32 -0800
commitc56df0d8314358709ef58f2dd82a858d62e30866 (patch)
tree85635ded5457962fb9ad63353abf86d68c695ece /chip
parentf8dc4617128f72cdcef4aae33afd665d3fbc5a2f (diff)
downloadchrome-ec-c56df0d8314358709ef58f2dd82a858d62e30866.tar.gz
ec_chip_mchp: Add other hardware files
Add Micorchip MEC17xx family files for hardware timers, keyboard scan, host port 80h, UART, and watch dog timer. BRANCH=none BUG= TEST=Review only. Change-Id: Iac8a912af4d29521964f606637041b06fa7238ee Signed-off-by: Scott Worley <scott.worley@microchip.corp-partner.google.com>
Diffstat (limited to 'chip')
-rw-r--r--chip/mchp/hwtimer.c121
-rw-r--r--chip/mchp/keyboard_raw.c175
-rw-r--r--chip/mchp/port80.c51
-rw-r--r--chip/mchp/uart.c237
-rw-r--r--chip/mchp/watchdog.c127
5 files changed, 711 insertions, 0 deletions
diff --git a/chip/mchp/hwtimer.c b/chip/mchp/hwtimer.c
new file mode 100644
index 0000000000..fa68d09f18
--- /dev/null
+++ b/chip/mchp/hwtimer.c
@@ -0,0 +1,121 @@
+/* Copyright 2017 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.
+ */
+
+/* Hardware timers driver */
+
+#include "clock.h"
+#include "common.h"
+#include "hooks.h"
+#include "hwtimer.h"
+#include "registers.h"
+#include "task.h"
+#include "timer.h"
+#include "tfdp_chip.h"
+
+void __hw_clock_event_set(uint32_t deadline)
+{
+ MCHP_TMR32_CNT(1) = MCHP_TMR32_CNT(0) -
+ (0xffffffff - deadline);
+ MCHP_TMR32_CTL(1) |= (1 << 5);
+}
+
+uint32_t __hw_clock_event_get(void)
+{
+ return MCHP_TMR32_CNT(1) - MCHP_TMR32_CNT(0) + 0xffffffff;
+}
+
+void __hw_clock_event_clear(void)
+{
+ MCHP_TMR32_CTL(1) &= ~(1 << 5);
+}
+
+uint32_t __hw_clock_source_read(void)
+{
+ return 0xffffffff - MCHP_TMR32_CNT(0);
+}
+
+void __hw_clock_source_set(uint32_t ts)
+{
+ MCHP_TMR32_CTL(0) &= ~(1 << 5);
+ MCHP_TMR32_CNT(0) = 0xffffffff - ts;
+ MCHP_TMR32_CTL(0) |= (1 << 5);
+}
+
+/*
+ * Always clear both timer and aggregator status
+ */
+static void __hw_clock_source_irq(int timer_id)
+{
+ MCHP_TMR32_STS(timer_id & 0x01) |= 1;
+ MCHP_INT_SOURCE(MCHP_TMR32_GIRQ) =
+ MCHP_TMR32_GIRQ_BIT(timer_id & 0x01);
+
+ /* If IRQ is from timer 0, 32-bit timer overflowed */
+ process_timers(timer_id == 0);
+}
+
+void __hw_clock_source_irq_0(void) { __hw_clock_source_irq(0); }
+DECLARE_IRQ(MCHP_IRQ_TIMER32_0, __hw_clock_source_irq_0, 1);
+void __hw_clock_source_irq_1(void) { __hw_clock_source_irq(1); }
+DECLARE_IRQ(MCHP_IRQ_TIMER32_1, __hw_clock_source_irq_1, 1);
+
+static void configure_timer(int timer_id)
+{
+ uint32_t val;
+
+ /* Ensure timer is not running */
+ MCHP_TMR32_CTL(timer_id) &= ~(1 << 5);
+
+ /* Enable timer */
+ MCHP_TMR32_CTL(timer_id) |= (1 << 0);
+
+ val = MCHP_TMR32_CTL(timer_id);
+
+ /* Pre-scale = 48 -> 1MHz -> Period = 1us */
+ val = (val & 0xffff) | (47 << 16);
+
+ MCHP_TMR32_CTL(timer_id) = val;
+
+ /* Set preload to use the full 32 bits of the timer */
+ MCHP_TMR32_PRE(timer_id) = 0xffffffff;
+
+ /* Enable interrupt */
+ MCHP_TMR32_IEN(timer_id) |= 1;
+}
+
+int __hw_clock_source_init(uint32_t start_t)
+{
+ MCHP_PCR_SLP_DIS_DEV_MASK(3, MCHP_PCR_SLP_EN3_BTMR32_0 +
+ MCHP_PCR_SLP_EN3_BTMR32_1);
+
+ /*
+ * The timer can only fire interrupt when its value reaches zero.
+ * Therefore we need two timers:
+ * - Timer 0 as free running timer
+ * - Timer 1 as event timer
+ */
+ configure_timer(0);
+ configure_timer(1);
+
+ /* Override the count */
+ MCHP_TMR32_CNT(0) = 0xffffffff - start_t;
+
+ /* Auto restart */
+ MCHP_TMR32_CTL(0) |= (1 << 3);
+
+ /* Start counting in timer 0 */
+ MCHP_TMR32_CTL(0) |= (1 << 5);
+
+ /* Enable interrupt */
+ task_enable_irq(MCHP_IRQ_TIMER32_0);
+ task_enable_irq(MCHP_IRQ_TIMER32_1);
+ MCHP_INT_ENABLE(MCHP_TMR32_GIRQ) = MCHP_TMR32_GIRQ_BIT(0) +
+ MCHP_TMR32_GIRQ_BIT(1);
+ /*
+ * Not needed when using direct mode interrupts
+ * MCHP_INT_BLK_EN |= (1 << MCHP_TMR32_GIRQ);
+ */
+ return MCHP_IRQ_TIMER32_1;
+}
diff --git a/chip/mchp/keyboard_raw.c b/chip/mchp/keyboard_raw.c
new file mode 100644
index 0000000000..048c53fc56
--- /dev/null
+++ b/chip/mchp/keyboard_raw.c
@@ -0,0 +1,175 @@
+/* Copyright 2017 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.
+ *
+ * Raw keyboard I/O layer for MCHP MEC
+ */
+
+#include "gpio.h"
+#include "keyboard_config.h"
+#include "keyboard_raw.h"
+#include "keyboard_scan.h"
+#include "registers.h"
+#include "task.h"
+#include "util.h"
+#include "tfdp_chip.h"
+
+/*
+ * Using direct mode interrupt, do not enable
+ * GIRQ bit in aggregator block enable register.
+ */
+void keyboard_raw_init(void)
+{
+ /* clear key scan PCR sleep enable */
+ MCHP_PCR_SLP_DIS_DEV(MCHP_PCR_KEYSCAN);
+
+ keyboard_raw_enable_interrupt(0);
+ gpio_config_module(MODULE_KEYBOARD_SCAN, 1);
+
+ /* Enable keyboard scan interrupt */
+ MCHP_INT_ENABLE(MCHP_KS_GIRQ) = MCHP_KS_GIRQ_BIT;
+ MCHP_KS_KSI_INT_EN = 0xff;
+}
+
+void keyboard_raw_task_start(void)
+{
+ task_enable_irq(MCHP_IRQ_KSC_INT);
+}
+
+test_mockable void keyboard_raw_drive_column(int out)
+{
+ if (out == KEYBOARD_COLUMN_ALL) {
+ MCHP_KS_KSO_SEL = 1 << 5; /* KSEN=0, KSALL=1 */
+#ifdef CONFIG_KEYBOARD_COL2_INVERTED
+ gpio_set_level(GPIO_KBD_KSO2, 1);
+#endif
+ } else if (out == KEYBOARD_COLUMN_NONE) {
+ MCHP_KS_KSO_SEL = 1 << 6; /* KSEN=1 */
+#ifdef CONFIG_KEYBOARD_COL2_INVERTED
+ gpio_set_level(GPIO_KBD_KSO2, 0);
+#endif
+ } else {
+#ifdef CONFIG_KEYBOARD_COL2_INVERTED
+ if (out == 2) {
+ MCHP_KS_KSO_SEL = 1 << 6; /* KSEN=1 */
+ gpio_set_level(GPIO_KBD_KSO2, 1);
+ } else {
+ MCHP_KS_KSO_SEL = out + CONFIG_KEYBOARD_KSO_BASE;
+ gpio_set_level(GPIO_KBD_KSO2, 0);
+ }
+#else
+ MCHP_KS_KSO_SEL = out + CONFIG_KEYBOARD_KSO_BASE;
+#endif
+ }
+}
+
+test_mockable int keyboard_raw_read_rows(void)
+{
+ uint8_t b1, b2;
+
+ b1 = MCHP_KS_KSI_INPUT;
+ b2 = (b1 & 0xff) ^ 0xff;
+ trace2(0, MEC, 0, "raw_read_rows: KSI_IN=0x%02x Invert=0x%02x",
+ b1, b2);
+
+ /* Invert it so 0=not pressed, 1=pressed */
+ /* return (MCHP_KS_KSI_INPUT & 0xff) ^ 0xff; */
+ return b2;
+}
+
+void keyboard_raw_enable_interrupt(int enable)
+{
+ if (enable) {
+ MCHP_INT_SOURCE(MCHP_KS_GIRQ) = MCHP_KS_GIRQ_BIT;
+ task_clear_pending_irq(MCHP_IRQ_KSC_INT);
+ task_enable_irq(MCHP_IRQ_KSC_INT);
+ } else {
+ task_disable_irq(MCHP_IRQ_KSC_INT);
+ }
+}
+
+void keyboard_raw_interrupt(void)
+{
+ trace3(0, MEC, 0,
+ "KEYSCAN IRQ KSI_IN=0x%02x KSI_STS=0x%02x KSI_IEN=0x%02x",
+ MCHP_KS_KSI_INPUT,
+ MCHP_KS_KSI_STATUS,
+ MCHP_KS_KSI_INT_EN);
+
+ /* Clear interrupt status bits */
+ MCHP_KS_KSI_STATUS = 0xff;
+
+ MCHP_INT_SOURCE(MCHP_KS_GIRQ) = MCHP_KS_GIRQ_BIT;
+
+ /* Wake keyboard scan task to handle interrupt */
+ task_wake(TASK_ID_KEYSCAN);
+}
+DECLARE_IRQ(MCHP_IRQ_KSC_INT, keyboard_raw_interrupt, 1);
+
+#ifdef CONFIG_KEYBOARD_FACTORY_TEST
+
+/* Run keyboard factory testing, scan out KSO/KSI if any shorted. */
+int keyboard_factory_test_scan(void)
+{
+ int i, j;
+ uint16_t shorted = 0;
+ uint32_t port, id, val;
+
+ /* Disable keyboard scan while testing */
+ keyboard_scan_enable(0, KB_SCAN_DISABLE_LID_CLOSED);
+
+ /* Set all of KSO/KSI pins to internal pull-up and input */
+ for (i = 0; i < keyboard_factory_scan_pins_used; i++) {
+
+ if (keyboard_factory_scan_pins[i][0] < 0)
+ continue;
+
+ port = keyboard_factory_scan_pins[i][0];
+ id = keyboard_factory_scan_pins[i][1];
+
+ gpio_set_alternate_function(port, 1 << id, -1);
+ gpio_set_flags_by_mask(port, 1 << id,
+ GPIO_INPUT | GPIO_PULL_UP);
+ }
+
+ /*
+ * Set start pin to output low, then check other pins
+ * going to low level, it indicate the two pins are shorted.
+ */
+ for (i = 0; i < keyboard_factory_scan_pins_used; i++) {
+
+ if (keyboard_factory_scan_pins[i][0] < 0)
+ continue;
+
+ port = keyboard_factory_scan_pins[i][0];
+ id = keyboard_factory_scan_pins[i][1];
+
+ gpio_set_flags_by_mask(port, 1 << id, GPIO_OUT_LOW);
+
+ for (j = 0; j < i; j++) {
+
+ if (keyboard_factory_scan_pins[j][0] < 0)
+ continue;
+
+ /*
+ * Get gpio pin control register,
+ * bit 24 indicate GPIO input from the pad.
+ */
+ val = MCHP_GPIO_CTL(keyboard_factory_scan_pins[j][0],
+ keyboard_factory_scan_pins[j][1]);
+
+ if ((val & (1 << 24)) == 0) {
+ shorted = i << 8 | j;
+ goto done;
+ }
+ }
+ gpio_set_flags_by_mask(port, 1 << id,
+ GPIO_INPUT | GPIO_PULL_UP);
+ }
+done:
+ gpio_config_module(MODULE_KEYBOARD_SCAN, 1);
+ keyboard_scan_enable(1, KB_SCAN_DISABLE_LID_CLOSED);
+
+ return shorted;
+}
+#endif
diff --git a/chip/mchp/port80.c b/chip/mchp/port80.c
new file mode 100644
index 0000000000..30812a9dba
--- /dev/null
+++ b/chip/mchp/port80.c
@@ -0,0 +1,51 @@
+/* Copyright 2017 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.
+ */
+
+/* Port 80 Timer Interrupt for MCHP MEC family */
+
+#include "common.h"
+#include "console.h"
+#include "hooks.h"
+#include "lpc.h"
+#include "port80.h"
+#include "registers.h"
+#include "task.h"
+#include "tfdp_chip.h"
+
+
+/*
+ * Interrupt fires when number of bytes written
+ * to eSPI/LPC I/O 80h-81h exceeds Por80_0 FIFO level
+ * Issues:
+ * 1. eSPI will not break 16-bit I/O into two 8-bit writes
+ * as LPC does. This means Port80 hardware will capture
+ * only bits[7:0] of data.
+ * 2. If Host performs write of 16-bit code as consecutive
+ * byte writes the Port80 hardware will capture both but
+ * we do not know the order it was written.
+ * 3. If Host sometimes writes one byte code to I/O 80h and
+ * sometimes two byte code to I/O 80h/81h how do we determine
+ * what to do?
+ *
+ * An alternative is to document Host must write 16-bit codes
+ * to I/O 80h and 90h. LSB to 0x80 and MSB to 0x90.
+ *
+ */
+void port_80_interrupt(void)
+{
+ int d;
+
+ while (MCHP_P80_STS(0) & MCHP_P80_STS_NOT_EMPTY) {
+ /* this masks off time stamp d = port_80_read(); */
+ d = MCHP_P80_CAP(0); /* b[7:0] = data, b[31:8] = timestamp */
+ trace1(0, P80, 0, "Port80h = 0x%02x", (d & 0xff));
+ port_80_write(d & 0xff);
+ }
+
+ MCHP_INT_SOURCE(MCHP_P80_GIRQ) = MCHP_P80_GIRQ_BIT(0);
+}
+DECLARE_IRQ(MCHP_IRQ_PORT80DBG0, port_80_interrupt, 2);
+
+
diff --git a/chip/mchp/uart.c b/chip/mchp/uart.c
new file mode 100644
index 0000000000..c38ab33aea
--- /dev/null
+++ b/chip/mchp/uart.c
@@ -0,0 +1,237 @@
+/* Copyright 2017 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 module for MCHP MEC */
+
+#include "clock.h"
+#include "common.h"
+#include "console.h"
+#include "gpio.h"
+#include "lpc.h"
+#include "registers.h"
+#include "system.h"
+#include "task.h"
+#include "uart.h"
+#include "util.h"
+#include "tfdp_chip.h"
+
+#define TX_FIFO_SIZE 16
+
+static int init_done;
+static int tx_fifo_used;
+
+int uart_init_done(void)
+{
+ return init_done;
+}
+
+void uart_tx_start(void)
+{
+ /* If interrupt is already enabled, nothing to do */
+ if (MCHP_UART_IER(0) & (1 << 1))
+ return;
+
+ /* Do not allow deep sleep while transmit in progress */
+ disable_sleep(SLEEP_MASK_UART);
+
+ /*
+ * Re-enable the transmit interrupt, then forcibly trigger the
+ * interrupt. This works around a hardware problem with the
+ * UART where the FIFO only triggers the interrupt when its
+ * threshold is _crossed_, not just met.
+ */
+ MCHP_UART_IER(0) |= (1 << 1);
+ task_trigger_irq(MCHP_IRQ_UART0);
+}
+
+void uart_tx_stop(void)
+{
+ MCHP_UART_IER(0) &= ~(1 << 1);
+
+ /* Re-allow deep sleep */
+ enable_sleep(SLEEP_MASK_UART);
+}
+
+void uart_tx_flush(void)
+{
+ /* Wait for transmit FIFO empty */
+ while (!(MCHP_UART_LSR(0) & MCHP_LSR_TX_EMPTY))
+ ;
+}
+
+int uart_tx_ready(void)
+{
+ /*
+ * We have no indication of free space in transmit FIFO. To work around
+ * this, we check transmit FIFO empty bit every 16 characters written.
+ */
+ return tx_fifo_used != 0 ||
+ (MCHP_UART_LSR(0) & MCHP_LSR_TX_EMPTY);
+}
+
+int uart_tx_in_progress(void)
+{
+ /* return 0: FIFO is empty, 1: FIFO NOT Empty */
+ return !(MCHP_UART_LSR(0) & MCHP_LSR_TX_EMPTY);
+}
+
+int uart_rx_available(void)
+{
+ return MCHP_UART_LSR(0) & (1 << 0);
+}
+
+void uart_write_char(char c)
+{
+ /* Wait for space in transmit FIFO. */
+ while (!uart_tx_ready())
+ ;
+
+ tx_fifo_used = (tx_fifo_used + 1) % TX_FIFO_SIZE;
+ MCHP_UART_TB(0) = c;
+}
+
+int uart_read_char(void)
+{
+ return MCHP_UART_RB(0);
+}
+
+static void uart_clear_rx_fifo(int channel)
+{
+ MCHP_UART_FCR(0) = (1 << 0) | (1 << 1);
+}
+
+void uart_disable_interrupt(void)
+{
+ task_disable_irq(MCHP_IRQ_UART0);
+}
+
+void uart_enable_interrupt(void)
+{
+ task_enable_irq(MCHP_IRQ_UART0);
+}
+
+/**
+ * Interrupt handler for UART
+ */
+void uart_ec_interrupt(void)
+{
+ /* Read input FIFO until empty, then fill output FIFO */
+ uart_process_input();
+ /* Trace statement to provide time marker for UART output? */
+ uart_process_output();
+}
+DECLARE_IRQ(MCHP_IRQ_UART0, uart_ec_interrupt, 1);
+
+void uart_init(void)
+{
+ /* Clear UART PCR sleep enable */
+ MCHP_PCR_SLP_DIS_DEV(MCHP_PCR_UART0);
+
+ /* Set UART to reset on VCC1_RESET instead of nSIO_RESET */
+ MCHP_UART_CFG(0) &= ~(1 << 1);
+
+ /* Baud rate = 115200. 1.8432MHz clock. Divisor = 1 */
+
+ /* Set CLK_SRC = 0 */
+ MCHP_UART_CFG(0) &= ~(1 << 0);
+
+ /* Set DLAB = 1 */
+ MCHP_UART_LCR(0) |= (1 << 7);
+
+ /* PBRG0/PBRG1 */
+ MCHP_UART_PBRG0(0) = 1;
+ MCHP_UART_PBRG1(0) = 0;
+
+ /* Set DLAB = 0 */
+ MCHP_UART_LCR(0) &= ~(1 << 7);
+
+ /* Set word length to 8-bit */
+ MCHP_UART_LCR(0) |= (1 << 0) | (1 << 1);
+
+ /* Enable FIFO */
+ MCHP_UART_FCR(0) = (1 << 0);
+
+ /* Activate UART */
+ MCHP_UART_ACT(0) |= (1 << 0);
+
+ gpio_config_module(MODULE_UART, 1);
+
+ /*
+ * Enable interrupts for UART0.
+ */
+ uart_clear_rx_fifo(0);
+ MCHP_UART_IER(0) |= (1 << 0);
+ MCHP_UART_MCR(0) |= (1 << 3);
+ MCHP_INT_ENABLE(MCHP_UART_GIRQ) = MCHP_UART_GIRQ_BIT(0);
+
+ task_enable_irq(MCHP_IRQ_UART0);
+
+ init_done = 1;
+}
+
+#ifdef CONFIG_LOW_POWER_IDLE
+void uart_enter_dsleep(void)
+{
+ /* Disable the UART interrupt. */
+ task_disable_irq(MCHP_IRQ_UART0); /* NVIC interrupt for UART=13 */
+
+ /*
+ * Set the UART0 RX pin to be a GPIO-162(fixed pin) interrupt
+ * with the flags defined in the gpio.inc file.
+ */
+ gpio_reset(GPIO_UART0_RX);
+
+ /* power-down/de-activate UART0 */
+ MCHP_UART_ACT(0) &= ~(1 << 0);
+
+ /* clear interrupt enable for UART0 */
+ MCHP_INT_DISABLE(MCHP_UART_GIRQ) = MCHP_UART_GIRQ_BIT(0);
+
+ /* Clear pending interrupts on GPIO_UART0_RX(GPIO105, girq=9, bit=5) */
+ MCHP_INT_SOURCE(9) = (1 << 5);
+
+ /* Enable GPIO interrupts on the UART0 RX pin. */
+ gpio_enable_interrupt(GPIO_UART0_RX);
+}
+
+
+void uart_exit_dsleep(void)
+{
+ /*
+ * If the UART0 RX GPIO interrupt has not fired, then no edge has been
+ * detected. Disable the GPIO interrupt so that switching the pin over
+ * to a UART pin doesn't inadvertently cause a GPIO edge interrupt.
+ * Note: we can't disable this interrupt if it has already fired
+ * because then the IRQ will not run at all.
+ */
+ if (!((1 << 5) & MCHP_INT_SOURCE(9))) /* if edge interrupt */
+ gpio_disable_interrupt(GPIO_UART0_RX);
+
+ /* Configure UART0 pins for use in UART peripheral. */
+ gpio_config_module(MODULE_UART, 1);
+
+ /* Clear pending interrupts on UART peripheral and enable interrupts. */
+ uart_clear_rx_fifo(0);
+ MCHP_INT_SOURCE(MCHP_UART_GIRQ) = MCHP_UART_GIRQ_BIT(0);
+ MCHP_INT_ENABLE(MCHP_UART_GIRQ) = MCHP_UART_GIRQ_BIT(0);
+ task_enable_irq(MCHP_IRQ_UART0); /* NVIC interrupt for UART = 40 */
+
+ /* power-up/activate UART0 */
+ MCHP_UART_ACT(0) |= (1 << 0);
+}
+
+void uart_deepsleep_interrupt(enum gpio_signal signal)
+{
+ /*
+ * Activity seen on UART RX pin while UART was disabled for deep sleep.
+ * The console won't see that character because the UART is disabled,
+ * so we need to inform the clock module of UART activity ourselves.
+ */
+ clock_refresh_console_in_use();
+
+ /* Disable interrupts on UART0 RX pin to avoid repeated interrupts. */
+ gpio_disable_interrupt(GPIO_UART0_RX);
+}
+#endif /* CONFIG_LOW_POWER_IDLE */
diff --git a/chip/mchp/watchdog.c b/chip/mchp/watchdog.c
new file mode 100644
index 0000000000..156edfc750
--- /dev/null
+++ b/chip/mchp/watchdog.c
@@ -0,0 +1,127 @@
+/* Copyright 2017 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 "hooks.h"
+#include "registers.h"
+#include "task.h"
+#include "watchdog.h"
+#include "tfdp_chip.h"
+
+void watchdog_reload(void)
+{
+ MCHP_WDG_KICK = 1;
+
+#ifdef CONFIG_WATCHDOG_HELP
+ /* Reload the auxiliary timer */
+ MCHP_TMR16_CTL(0) &= ~(1 << 5);
+ MCHP_TMR16_CNT(0) = CONFIG_AUX_TIMER_PERIOD_MS;
+#ifndef CONFIG_CHIPSET_DEBUG
+ MCHP_TMR16_CTL(0) |= 1 << 5;
+#endif
+#endif
+}
+DECLARE_HOOK(HOOK_TICK, watchdog_reload, HOOK_PRIO_DEFAULT);
+
+int watchdog_init(void)
+{
+#ifdef CONFIG_WATCHDOG_HELP
+ uint32_t val;
+
+ /*
+ * Watchdog does not warn us before expiring. Let's use a 16-bit
+ * timer as an auxiliary timer.
+ */
+
+ /* Clear 16-bit basic timer 0 PCR sleep enable */
+ MCHP_PCR_SLP_DIS_DEV(MCHP_PCR_BTMR16_0);
+
+ /* Stop the auxiliary timer if it's running */
+ MCHP_TMR16_CTL(0) &= ~(1 << 5);
+
+ /* Enable auxiliary timer */
+ MCHP_TMR16_CTL(0) |= 1 << 0;
+
+ val = MCHP_TMR16_CTL(0);
+
+ /* Pre-scale = 48000 -> 1kHz -> Period = 1ms */
+ val = (val & 0xffff) | (47999 << 16);
+
+ /* No auto restart */
+ val &= ~(1 << 3);
+
+ /* Count down */
+ val &= ~(1 << 2);
+
+ MCHP_TMR16_CTL(0) = val;
+
+#ifndef CONFIG_CHIPSET_DEBUG
+ /* Enable interrupt from auxiliary timer */
+ MCHP_TMR16_IEN(0) |= 1;
+ task_enable_irq(MCHP_IRQ_TIMER16_0);
+ MCHP_INT_ENABLE(MCHP_TMR16_GIRQ) = MCHP_TMR16_GIRQ_BIT(0);
+
+ /* Load and start the auxiliary timer */
+ MCHP_TMR16_CNT(0) = CONFIG_AUX_TIMER_PERIOD_MS;
+ MCHP_TMR16_CNT(0) |= 1 << 5;
+#endif
+#endif
+
+ /* Clear WDT PCR sleep enable */
+ MCHP_PCR_SLP_DIS_DEV(MCHP_PCR_WDT);
+
+ /* Set timeout. It takes 1007us to decrement WDG_CNT by 1. */
+ MCHP_WDG_LOAD = CONFIG_WATCHDOG_PERIOD_MS * 1000 / 1007;
+
+ /* Start watchdog */
+#ifdef CONFIG_CHIPSET_DEBUG
+ /* debug, set stall and do not start */
+ MCHP_WDG_CTL = (1 << 4); /* enable WDG stall on active JTAG */
+#else
+ MCHP_WDG_CTL |= 1;
+#endif
+
+ return EC_SUCCESS;
+}
+
+#ifdef CONFIG_WATCHDOG_HELP
+void __keep watchdog_check(uint32_t excep_lr, uint32_t excep_sp)
+{
+ trace0(0, WDT, 0, "Watchdog check from 16-bit basic timer0 ISR");
+
+ /* Clear status */
+ MCHP_TMR16_STS(0) |= 1;
+ /* clear aggregator status */
+ MCHP_INT_SOURCE(MCHP_TMR16_GIRQ) = MCHP_TMR16_GIRQ_BIT(0);
+
+ watchdog_trace(excep_lr, excep_sp);
+}
+
+void
+IRQ_HANDLER(MCHP_IRQ_TIMER16_0)(void) __keep __attribute__((naked));
+void IRQ_HANDLER(MCHP_IRQ_TIMER16_0)(void)
+{
+ /* Naked call so we can extract raw LR and SP */
+ asm volatile("mov r0, lr\n"
+ "mov r1, sp\n"
+ /*
+ * Must push registers in pairs to keep 64-bit aligned
+ * stack for ARM EABI. This also conveninently saves
+ * R0=LR so we can pass it to task_resched_if_needed.
+ */
+ "push {r0, lr}\n"
+ "bl watchdog_check\n"
+ "pop {r0, lr}\n"
+ "b task_resched_if_needed\n");
+}
+
+/*
+ * Put the watchdog at the highest interrupt priority.
+ */
+const struct irq_priority __keep IRQ_PRIORITY(MEC1322_IRQ_TIMER16_0)
+ __attribute__((section(".rodata.irqprio")))
+ = {MCHP_IRQ_TIMER16_0, 0};
+#endif