summaryrefslogtreecommitdiff
path: root/chip/stm32/usart_tx_interrupt.c
diff options
context:
space:
mode:
authorAnton Staaf <robotboy@chromium.org>2015-06-16 10:34:56 -0700
committerChromeOS Commit Bot <chromeos-commit-bot@chromium.org>2015-07-15 21:57:46 +0000
commit137959bb88ef381cba67e24943388bd17ae89357 (patch)
treed4844e76548432b726cf40d57bf0214d92d64634 /chip/stm32/usart_tx_interrupt.c
parent88a1790bb7d82a30e052f1e10a9c7c88fb5c5c36 (diff)
downloadchrome-ec-137959bb88ef381cba67e24943388bd17ae89357.tar.gz
USART: Add flexibility needed to support DMA
In order to support DMA transfers in one or both directions the usart driver needs to be configurable with producer/consumer operations and interrupt handler functions. These are now packaged up in the usart_rx and usart_tx structs, and versions for interrupt driven RX and TX are provided. Signed-off-by: Anton Staaf <robotboy@chromium.org> BRANCH=None BUG=None TEST=make buildall -j Change-Id: I3fd14c675c90873e903195b8e20d2070d2eda5ac Reviewed-on: https://chromium-review.googlesource.com/285023 Trybot-Ready: Anton Staaf <robotboy@chromium.org> Tested-by: Anton Staaf <robotboy@chromium.org> Reviewed-by: Todd Broch <tbroch@chromium.org> Tested-by: Todd Broch <tbroch@chromium.org> Commit-Queue: Anton Staaf <robotboy@chromium.org>
Diffstat (limited to 'chip/stm32/usart_tx_interrupt.c')
-rw-r--r--chip/stm32/usart_tx_interrupt.c90
1 files changed, 90 insertions, 0 deletions
diff --git a/chip/stm32/usart_tx_interrupt.c b/chip/stm32/usart_tx_interrupt.c
new file mode 100644
index 0000000000..60f28d5a1d
--- /dev/null
+++ b/chip/stm32/usart_tx_interrupt.c
@@ -0,0 +1,90 @@
+/* Copyright (c) 2014 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.
+ */
+
+/* Interrupt based USART TX driver for STM32 */
+
+#include "usart.h"
+
+#include "common.h"
+#include "registers.h"
+#include "system.h"
+#include "util.h"
+
+static void usart_tx_init(struct usart_config const *config)
+{
+ intptr_t base = config->hw->base;
+
+ STM32_USART_CR1(base) |= STM32_USART_CR1_TE;
+}
+
+static void usart_written(struct consumer const *consumer, size_t count)
+{
+ struct usart_config const *config =
+ DOWNCAST(consumer, struct usart_config, consumer);
+
+ /*
+ * Enable USART interrupt. This causes the USART interrupt handler to
+ * start fetching from the TX queue if it wasn't already.
+ */
+ if (count)
+ STM32_USART_CR1(config->hw->base) |= STM32_USART_CR1_TXEIE;
+}
+
+static void usart_flush(struct consumer const *consumer)
+{
+ struct usart_config const *config =
+ DOWNCAST(consumer, struct usart_config, consumer);
+
+ /*
+ * Enable USART interrupt. This causes the USART interrupt handler to
+ * start fetching from the TX queue if it wasn't already.
+ */
+ STM32_USART_CR1(config->hw->base) |= STM32_USART_CR1_TXEIE;
+
+ while (queue_count(consumer->queue))
+ ;
+}
+
+static void usart_tx_interrupt_handler(struct usart_config const *config)
+{
+ intptr_t base = config->hw->base;
+ uint8_t byte;
+
+ if (!(STM32_USART_SR(base) & STM32_USART_SR_TXE))
+ return;
+
+ if (queue_remove_unit(config->consumer.queue, &byte)) {
+ STM32_USART_TDR(base) = byte;
+
+ /*
+ * Make sure the TXE interrupt is enabled and that we won't go
+ * into deep sleep. This invocation of the USART interrupt
+ * handler may have been manually triggered to start
+ * transmission.
+ */
+ disable_sleep(SLEEP_MASK_UART);
+
+ STM32_USART_CR1(base) |= STM32_USART_CR1_TXEIE;
+ } else {
+ /*
+ * The TX queue is empty, disable the TXE interrupt and enable
+ * deep sleep mode. The TXE interrupt will remain disabled
+ * until a write call happens.
+ */
+ enable_sleep(SLEEP_MASK_UART);
+
+ STM32_USART_CR1(base) &= ~STM32_USART_CR1_TXEIE;
+ }
+}
+
+struct usart_tx const usart_tx_interrupt = {
+ .consumer_ops = {
+ .written = usart_written,
+ .flush = usart_flush,
+ },
+
+ .init = usart_tx_init,
+ .interrupt = usart_tx_interrupt_handler,
+};