summaryrefslogtreecommitdiff
path: root/chip/stm32/usart_tx_dma.c
diff options
context:
space:
mode:
authorAnton Staaf <robotboy@chromium.org>2015-07-15 15:03:40 -0700
committerChromeOS Commit Bot <chromeos-commit-bot@chromium.org>2015-07-21 18:30:40 +0000
commit1e3d00ff7ae32d58ac9c27ef09b22df58e48b1c4 (patch)
tree190ad8a9e3b463fb7acc6068b1cf73f0286baadd /chip/stm32/usart_tx_dma.c
parentff9934264163dd0d7e795f32144c6c1082fdf040 (diff)
downloadchrome-ec-1e3d00ff7ae32d58ac9c27ef09b22df58e48b1c4.tar.gz
USART: Add DMA based transmitter
This adds a new transmission implementation for the multi UART driver. It is a DMA based transmitter that can directly read from the TX queue with zero copy overhead. The DMA channel used as well as the maximum DMA transmission size are configurable per UART at the board level. This also updates the Ryu AP UART to use DMA transmission. Signed-off-by: Anton Staaf <robotboy@chromium.org> BRANCH=None BUG=None TEST=make buildall -j Manually verify that the AP UART forwarding works Change-Id: I3cb27d0f9015043d75a38c12919388afe90dc4af Reviewed-on: https://chromium-review.googlesource.com/286274 Trybot-Ready: Anton Staaf <robotboy@chromium.org> Tested-by: Anton Staaf <robotboy@chromium.org> Reviewed-by: Randall Spangler <rspangler@chromium.org> Commit-Queue: Anton Staaf <robotboy@chromium.org>
Diffstat (limited to 'chip/stm32/usart_tx_dma.c')
-rw-r--r--chip/stm32/usart_tx_dma.c117
1 files changed, 117 insertions, 0 deletions
diff --git a/chip/stm32/usart_tx_dma.c b/chip/stm32/usart_tx_dma.c
new file mode 100644
index 0000000000..c17f2ae1bb
--- /dev/null
+++ b/chip/stm32/usart_tx_dma.c
@@ -0,0 +1,117 @@
+/* Copyright (c) 2015 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.
+ */
+#include "usart_tx_dma.h"
+
+#include "usart.h"
+#include "common.h"
+#include "registers.h"
+#include "system.h"
+#include "task.h"
+#include "util.h"
+
+void usart_tx_dma_written(struct consumer const *consumer, size_t count)
+{
+ struct usart_config const *config =
+ DOWNCAST(consumer, struct usart_config, consumer);
+
+ task_trigger_irq(config->hw->irq);
+}
+
+void usart_tx_dma_flush(struct consumer const *consumer)
+{
+ struct usart_config const *config =
+ DOWNCAST(consumer, struct usart_config, consumer);
+
+ /*
+ * Enable the USART interrupt. This causes the USART interrupt handler
+ * to start fetching from the TX queue if it wasn't already.
+ */
+ task_trigger_irq(config->hw->irq);
+
+ while (queue_count(consumer->queue))
+ ;
+}
+
+void usart_tx_dma_init(struct usart_config const *config)
+{
+ struct usart_tx_dma const *dma_config =
+ DOWNCAST(config->tx, struct usart_tx_dma const, usart_tx);
+
+ intptr_t base = config->hw->base;
+
+ STM32_USART_CR1(base) |= STM32_USART_CR1_TE;
+ STM32_USART_CR3(base) |= STM32_USART_CR3_DMAT;
+
+ dma_config->state->dma_active = 0;
+}
+
+static void usart_tx_dma_start(struct usart_config const *config,
+ struct usart_tx_dma const *dma_config)
+{
+ struct usart_tx_dma_state volatile *state = dma_config->state;
+ intptr_t base = config->hw->base;
+
+ struct dma_option options = {
+ .channel = dma_config->channel,
+ .periph = (void *)&STM32_USART_TDR(base),
+ .flags = (STM32_DMA_CCR_MSIZE_8_BIT |
+ STM32_DMA_CCR_PSIZE_8_BIT),
+ };
+
+ /*
+ * Limit our DMA transfer. If we didn't do this then it would be
+ * possible to start a large DMA transfer of an entirely full buffer
+ * that would hold up any additional writes to the TX queue
+ * unnecessarily.
+ */
+ state->chunk.length = MIN(state->chunk.length, dma_config->max_bytes);
+
+ dma_prepare_tx(&options, state->chunk.length, state->chunk.buffer);
+
+ state->dma_active = 1;
+
+ usart_clear_tc(config);
+ STM32_USART_CR1(base) |= STM32_USART_CR1_TCIE;
+
+ dma_go(dma_get_channel(options.channel));
+}
+
+static void usart_tx_dma_stop(struct usart_config const *config,
+ struct usart_tx_dma const *dma_config)
+{
+ dma_config->state->dma_active = 0;
+
+ STM32_USART_CR1(config->hw->base) &= ~STM32_USART_CR1_TCIE;
+}
+
+void usart_tx_dma_interrupt(struct usart_config const *config)
+{
+ struct usart_tx_dma const *dma_config =
+ DOWNCAST(config->tx, struct usart_tx_dma const, usart_tx);
+ struct usart_tx_dma_state volatile *state = dma_config->state;
+
+ /*
+ * If we have completed a DMA transaction, or if we haven't yet started
+ * one then we clean up and start one now.
+ */
+ if ((STM32_USART_SR(config->hw->base) & STM32_USART_SR_TC) ||
+ !state->dma_active) {
+ struct queue const *queue = config->consumer.queue;
+
+ /*
+ * Only advance the queue head (indicating that we have read
+ * units from the queue if we had an active DMA transfer.
+ */
+ if (state->dma_active)
+ queue_advance_head(queue, state->chunk.length);
+
+ state->chunk = queue_get_read_chunk(queue);
+
+ if (state->chunk.length)
+ usart_tx_dma_start(config, dma_config);
+ else
+ usart_tx_dma_stop(config, dma_config);
+ }
+}