summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorNamyoon Woo <namyoon@google.com>2020-03-21 21:17:28 -0700
committerCommit Bot <commit-bot@chromium.org>2020-05-29 22:10:40 +0000
commit57e170c71339961ea0d411e3ffa7c6d8e50c8ea3 (patch)
tree49bd4552be471158c6b0619951836e66d9c62f7d
parent39e05180c806091fa3b34ba719813d8eba57e474 (diff)
downloadchrome-ec-57e170c71339961ea0d411e3ffa7c6d8e50c8ea3.tar.gz
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 <namyoon@google.com> Change-Id: Ie74b236bc5352e9fc21fe600c12946e50955160a Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/platform/ec/+/2114430 Tested-by: Namyoon Woo <namyoon@chromium.org> Reviewed-by: Vadim Bendebury <vbendeb@chromium.org> Commit-Queue: Namyoon Woo <namyoon@chromium.org>
-rw-r--r--board/cr50/board.h21
-rw-r--r--board/cr50/build.mk1
-rw-r--r--board/cr50/int_ap_extension.c170
-rw-r--r--chip/g/sps.c57
4 files changed, 241 insertions, 8 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;
+}
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);
@@ -393,18 +425,19 @@ static void sps_cs_deassert_interrupt(uint32_t port)
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