diff options
Diffstat (limited to 'board')
-rw-r--r-- | board/cr50/board.h | 21 | ||||
-rw-r--r-- | board/cr50/build.mk | 1 | ||||
-rw-r--r-- | board/cr50/int_ap_extension.c | 170 |
3 files changed, 192 insertions, 0 deletions
diff --git a/board/cr50/board.h b/board/cr50/board.h index 540f4c622d..ea0ac15dae 100644 --- a/board/cr50/board.h +++ b/board/cr50/board.h @@ -366,6 +366,27 @@ int ec_is_on(void); int ec_is_rx_allowed(void); int servo_is_connected(void); +/* + * Assert INT_AP_L to acknowledge AP that cr50 is ready for next TPM command. + * NOTE: must be called by ISR only. + * + * Returns 1 if it successfully asserted (or scheduled to assert), or + * 0 if the extended long pulse was disabled. + */ +int assert_int_ap(void); + +/* + * Deassert INT_AP_L immediately. + * NOTE: must be called by ISR only. + */ +void deassert_int_ap(void); + +/* Register a function that should be called when INT_AP_L extension starts. */ +void int_ap_register(void (*func_enable)(void)); + +void int_ap_extension_enable(void); +void int_ap_extension_stop_pulse(void); + /* Moving from legacy versions might require NVMEM transition. */ int board_nvmem_legacy_check_needed(void); diff --git a/board/cr50/build.mk b/board/cr50/build.mk index 5b0d788401..b84f2dc65f 100644 --- a/board/cr50/build.mk +++ b/board/cr50/build.mk @@ -43,6 +43,7 @@ board-y = board.o board-y += ap_state.o board-y += closed_source_set1.o board-y += ec_state.o +board-y += int_ap_extension.o board-y += power_button.o board-y += servo_state.o board-y += ap_uart_state.o diff --git a/board/cr50/int_ap_extension.c b/board/cr50/int_ap_extension.c new file mode 100644 index 0000000000..bacb44ad4d --- /dev/null +++ b/board/cr50/int_ap_extension.c @@ -0,0 +1,170 @@ +/* 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. + * + * INT_AP_L extension + */ + +#include "config.h" +#include "console.h" +#include "gpio.h" +#include "registers.h" +#include "stdbool.h" +#include "task.h" +#include "timer.h" + +/* + * Minimum time length (in microseconds) of INT_AP_L pulse required by CPU. + * It is set to be 100 microseconds, which a requirement of INTL TGL, GPIO + * in low power mode. + */ +#define PULSE_LENGTH 100ul + +#define USEC_TO_TIMEHS_TICKS(usec) ((usec) * (PCLK_FREQ / SEC_UL)) + +/* INT_AP_L pulse length in TIMEHS ticks. 0 means the extension is disabled. */ +static uint32_t pulse_length; + +/* true if INT_AP_L is asserted, or false if it is deasserted. */ +static bool int_ap_asserted_; + +/* + * true if INT_AP assertion was requested but delayed for a process, or + * false if there is no delayed INT_AP assertion request. + */ +static bool assertion_delayed_; + +/* + * true if the high-speed timer for int_ap pulse (timehs0_timer1) is enabled, or + * false otherwise. + */ +static bool timer_running_; + +/* function that should be called when INT_AP_L extension is enabled. */ +static void (*interface_func_enable_)(void); + +/* + * Change INT_AP_L level, and set a timer for PULSE_LENGTH + * @param do_assert true asserts INT_AP_L (LOW) + * false deasserts INT_AP_L (HIGH) + */ +static void set_int_ap_(bool do_assert) +{ + /* Set INT_AP_L level. */ + int_ap_asserted_ = do_assert; + /* It is asserted when low. */ + gpio_set_level(GPIO_INT_AP_L, !int_ap_asserted_); + + /* Schedule to toggle INT_AP_L. */ + GR_TIMEHS_LOAD(0, 1) = pulse_length; + GR_TIMEHS_CONTROL(0, 1) = GC_TIMEHS_TIMER1CONTROL_ONESHOT_MASK | + GC_TIMEHS_TIMER1CONTROL_SIZE_MASK | + GC_TIMEHS_TIMER1CONTROL_INTENABLE_MASK | + GC_TIMEHS_TIMER1CONTROL_ENABLE_MASK; + timer_running_ = true; +} + +static void disable_timer_(void) +{ + /* Disable Timer. */ + GR_TIMEHS_CONTROL(0, 1) = 0; + + /* Clear interrupt status of TIMEHS0 TIMER1. */ + GR_TIMEHS_INTCLR(0, 1) = 1; + + timer_running_ = false; +} + +/* Interrupt handler of timehs0_timint1, a timer for INT_AP_L extension. */ +void timer_int_ap_irq_handler(void) +{ + disable_timer_(); + + if (!int_ap_asserted_) { + /* + * While INT_AP_L is being deasserted, if assertion was + * requested (and delayed), then toggle the signal. + * Otherwise, just return without changing INT_AP_L level. + */ + if (!assertion_delayed_) + return; + + assertion_delayed_ = false; + } + + /* Toggle the INT_AP_L level. */ + set_int_ap_(!int_ap_asserted_); +} +DECLARE_IRQ(GC_IRQNUM_TIMEHS0_TIMINT1, timer_int_ap_irq_handler, 1); + +int assert_int_ap(void) +{ +#ifdef CR50_DEV + if (!in_interrupt_context()) + ccprintf("WARN: %s in non-ISR ctx.", __func__); +#endif + /* If the INT_AP_L extension is not enabled, then just return 0. */ + if (!pulse_length) + return 0; + + if (int_ap_asserted_) { +#ifdef CR50_DEV + ccprintf("WARN: INT_AP_L assertion request is duplicated."); +#endif + return 1; + } + + /* + * If the timer is running, it means INT_AP_L deassertion pulse isn't + * long enough yet. If so, let's delay to assert. + * Otherwise, assert INT_AP_L immediately. + */ + if (timer_running_) + assertion_delayed_ = true; + else + set_int_ap_(true); + + return 1; +} + +void deassert_int_ap(void) +{ +#ifdef CR50_DEV + if (!in_interrupt_context()) + ccprintf("WARN: %s in non-ISR ctx", __func__); +#endif + /* If INT_AP_L is deasserted already, do nothing. */ + if (!int_ap_asserted_) { + assertion_delayed_ = false; + return; + } + timer_int_ap_irq_handler(); +} + +void int_ap_register(void (*func_enable)(void)) +{ + interface_func_enable_ = func_enable; +} + +void int_ap_extension_enable(void) +{ + int_ap_extension_stop_pulse(); + + pulse_length = USEC_TO_TIMEHS_TICKS(PULSE_LENGTH); + + task_enable_irq(GC_IRQNUM_TIMEHS0_TIMINT1); + + if (interface_func_enable_) + interface_func_enable_(); +} + +void int_ap_extension_stop_pulse(void) +{ + disable_timer_(); + + /* Initialize INT_AP_L status. */ + int_ap_asserted_ = false; + gpio_set_level(GPIO_INT_AP_L, 1); + + assertion_delayed_ = false; +} |