From c56df0d8314358709ef58f2dd82a858d62e30866 Mon Sep 17 00:00:00 2001 From: Scott Worley Date: Thu, 21 Dec 2017 14:49:50 -0500 Subject: 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 --- chip/mchp/hwtimer.c | 121 ++++++++++++++++++++++++ chip/mchp/keyboard_raw.c | 175 ++++++++++++++++++++++++++++++++++ chip/mchp/port80.c | 51 ++++++++++ chip/mchp/uart.c | 237 +++++++++++++++++++++++++++++++++++++++++++++++ chip/mchp/watchdog.c | 127 +++++++++++++++++++++++++ 5 files changed, 711 insertions(+) create mode 100644 chip/mchp/hwtimer.c create mode 100644 chip/mchp/keyboard_raw.c create mode 100644 chip/mchp/port80.c create mode 100644 chip/mchp/uart.c create mode 100644 chip/mchp/watchdog.c (limited to 'chip') 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 -- cgit v1.2.1