From 57e170c71339961ea0d411e3ffa7c6d8e50c8ea3 Mon Sep 17 00:00:00 2001 From: Namyoon Woo Date: Sat, 21 Mar 2020 21:17:28 -0700 Subject: Use a long pulse of INT_AP_L for SPS This patch adds a feature to extend each level of GPIO_INT_AP_L at least for 100 microseconds. The assertion (low GPIO_INT_AP_L) duration might be shorter only if AP asserts a SPS CS before INT_AP_L deassertion, because it means means AP recognized GPIO_INT_AP_L assertion already. This patch increases the flash usage by 280 bytes. BUG=b:148691139 TEST=None Signed-off-by: Namyoon Woo Change-Id: Ie74b236bc5352e9fc21fe600c12946e50955160a Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/platform/ec/+/2114430 Tested-by: Namyoon Woo Reviewed-by: Vadim Bendebury Commit-Queue: Namyoon Woo --- board/cr50/board.h | 21 ++++++ board/cr50/build.mk | 1 + board/cr50/int_ap_extension.c | 170 ++++++++++++++++++++++++++++++++++++++++++ chip/g/sps.c | 57 ++++++++++++-- 4 files changed, 241 insertions(+), 8 deletions(-) create mode 100644 board/cr50/int_ap_extension.c 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; +} diff --git a/chip/g/sps.c b/chip/g/sps.c index 3a44a4fa0e..0387f8640e 100644 --- a/chip/g/sps.c +++ b/chip/g/sps.c @@ -12,7 +12,6 @@ #include "sps.h" #include "system.h" #include "task.h" -#include "timer.h" #include "watchdog.h" /* @@ -63,6 +62,8 @@ static uint32_t sps_tx_count, sps_rx_count, tx_empty_count, max_rx_batch; /* Flag indicating if there has been any data received while CS was asserted. */ static uint8_t seen_data; +static bool int_ap_extension_enabled_; + void sps_tx_status(uint8_t byte) { GREG32(SPS, DUMMY_WORD) = byte; @@ -212,6 +213,14 @@ static void sps_configure(enum sps_mode mode, enum spi_clock_mode clk_mode, GWRITE_FIELD(SPS, ICTRL, CS_DEASSERT, 1); } +static void enable_cs_assert_irq_(void) +{ + GWRITE_FIELD(SPS, ISTATE_CLR, CS_ASSERT, 1); + GWRITE_FIELD(SPS, ICTRL, CS_ASSERT, 1); + + task_enable_irq(GC_IRQNUM_SPS0_CS_ASSERT_INTR); +} + /* * Register and unregister rx_handler. Side effects of registering the handler * is reinitializing the interface. @@ -224,6 +233,11 @@ int sps_register_rx_handler(enum sps_mode mode, rx_handler_f rx_handler, task_disable_irq(GC_IRQNUM_SPS0_RXFIFO_LVL_INTR); task_disable_irq(GC_IRQNUM_SPS0_CS_DEASSERT_INTR); + if (int_ap_extension_enabled_) { + task_disable_irq(GC_IRQNUM_SPS0_CS_ASSERT_INTR); + int_ap_extension_stop_pulse(); + } + if (!rx_handler) return 0; @@ -235,9 +249,20 @@ int sps_register_rx_handler(enum sps_mode mode, rx_handler_f rx_handler, task_enable_irq(GC_IRQNUM_SPS0_RXFIFO_LVL_INTR); task_enable_irq(GC_IRQNUM_SPS0_CS_DEASSERT_INTR); + if (int_ap_extension_enabled_) + enable_cs_assert_irq_(); + return 0; } +/* Function that sets up for SPS to enable INT_AP_L extension. */ +static void sps_int_ap_extension_enable_(void) +{ + enable_cs_assert_irq_(); + + int_ap_extension_enabled_ = true; +} + static void sps_init(void) { /* @@ -257,6 +282,13 @@ static void sps_init(void) /* Configure the SPS_CS_L signal, DIOA12, as wake falling */ gpio_set_wakepin(GPIO_STRAP_B1, GPIO_HIB_WAKE_FALLING); + + int_ap_register(sps_int_ap_extension_enable_); + + /* + * TODO: if TPM_BOARD_CFG has INT_AP extension enabled, then call + * int_ap_extension_enable(). + */ } DECLARE_HOOK(HOOK_INIT, sps_init, HOOK_PRIO_DEFAULT); @@ -392,19 +424,20 @@ static void sps_cs_deassert_interrupt(uint32_t port) GREG32(SPS, TXFIFO_WPTR) = GREG32(SPS, TXFIFO_RPTR); if (pulse_needed) { + /* + * If assert_int_ap() returns 1, it generated a long + * pulse of INT_AP_L. Then, there is no need to generate + * a short pulse. + */ + if (assert_int_ap()) + return; + /* * Signal the AP that this SPI frame processing is * completed. */ gpio_set_level(GPIO_INT_AP_L, 0); - /* - * This is to meet the AP requirement of minimum 4 usec - * duration of INT_AP_L assertion. - * - * TODO(b/130515803): Ideally, this should be improved - * to support any duration requirement in future. - */ tick_delay(2); gpio_set_level(GPIO_INT_AP_L, 1); @@ -423,6 +456,14 @@ void _sps0_cs_deassert_interrupt(void) DECLARE_IRQ(GC_IRQNUM_SPS0_CS_DEASSERT_INTR, _sps0_cs_deassert_interrupt, 1); DECLARE_IRQ(GC_IRQNUM_SPS0_RXFIFO_LVL_INTR, _sps0_interrupt, 1); +void sps0_cs_assert_interrupt_(void) +{ + GWRITE_FIELD(SPS, ISTATE_CLR, CS_ASSERT, 1); + + deassert_int_ap(); +} +DECLARE_IRQ(GC_IRQNUM_SPS0_CS_ASSERT_INTR, sps0_cs_assert_interrupt_, 1); + #ifdef CONFIG_SPS_TEST /* Function to test SPS driver. It expects the host to send SPI frames of size -- cgit v1.2.1