summaryrefslogtreecommitdiff
path: root/chip
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
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')
-rw-r--r--chip/stm32/build.mk1
-rw-r--r--chip/stm32/usart.c102
-rw-r--r--chip/stm32/usart.h56
-rw-r--r--chip/stm32/usart_rx_interrupt.c48
-rw-r--r--chip/stm32/usart_tx_interrupt.c90
5 files changed, 185 insertions, 112 deletions
diff --git a/chip/stm32/build.mk b/chip/stm32/build.mk
index 1e0d6a60df..d4a8152897 100644
--- a/chip/stm32/build.mk
+++ b/chip/stm32/build.mk
@@ -35,6 +35,7 @@ chip-$(CONFIG_COMMON_GPIO)+=gpio.o gpio-$(CHIP_FAMILY).o
chip-$(CONFIG_COMMON_TIMER)+=hwtimer$(TIMER_TYPE).o
chip-$(CONFIG_I2C)+=i2c-$(CHIP_FAMILY).o
chip-$(CONFIG_STREAM_USART)+=usart.o usart-$(CHIP_FAMILY).o
+chip-$(CONFIG_STREAM_USART)+=usart_rx_interrupt.o usart_tx_interrupt.o
chip-$(CONFIG_STREAM_USB)+=usb-stream.o
chip-$(CONFIG_WATCHDOG)+=watchdog.o
chip-$(HAS_TASK_CONSOLE)+=uart.o
diff --git a/chip/stm32/usart.c b/chip/stm32/usart.c
index b4104a9396..32dbeaf7d0 100644
--- a/chip/stm32/usart.c
+++ b/chip/stm32/usart.c
@@ -14,47 +14,6 @@
#include "usart.h"
#include "util.h"
-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))
- ;
-}
-
-struct producer_ops const usart_producer_ops = {
- /*
- * Nothing to do here, we either had enough space in the queue when
- * a character came in or we dropped it already.
- */
- .read = NULL,
-};
-
-struct consumer_ops const usart_consumer_ops = {
- .written = usart_written,
- .flush = usart_flush,
-};
-
void usart_init(struct usart_config const *config)
{
intptr_t base = config->hw->base;
@@ -78,18 +37,18 @@ void usart_init(struct usart_config const *config)
gpio_config_module(MODULE_USART, 1);
/*
- * 8N1, 16 samples per bit, enable TX and RX (and associated RX
- * interrupt) DMA, error interrupts, and special modes disabled.
+ * 8N1, 16 samples per bit. error interrupts, and special modes
+ * disabled.
*/
- STM32_USART_CR1(base) = (STM32_USART_CR1_TE |
- STM32_USART_CR1_RE |
- STM32_USART_CR1_RXNEIE);
+ STM32_USART_CR1(base) = 0x0000;
STM32_USART_CR2(base) = 0x0000;
STM32_USART_CR3(base) = STM32_USART_CR3_OVRDIS;
/*
- * Enable the variant specific HW.
+ * Enable the RX, TX, and variant specific HW.
*/
+ config->rx->init(config);
+ config->tx->init(config);
config->hw->ops->enable(config);
/*
@@ -137,51 +96,8 @@ void usart_set_baud_f(struct usart_config const *config, int frequency_hz)
STM32_USART_BRR(config->hw->base) = div;
}
-static void usart_interrupt_tx(struct usart_config const *config)
-{
- intptr_t base = config->hw->base;
- uint8_t byte;
-
- 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;
- }
-}
-
-static void usart_interrupt_rx(struct usart_config const *config)
-{
- intptr_t base = config->hw->base;
- uint8_t byte = STM32_USART_RDR(base);
-
- if (!queue_add_unit(config->producer.queue, &byte))
- atomic_add((uint32_t *) &config->state->rx_dropped, 1);
-}
-
void usart_interrupt(struct usart_config const *config)
{
- intptr_t base = config->hw->base;
-
- if (STM32_USART_SR(base) & STM32_USART_SR_TXE)
- usart_interrupt_tx(config);
-
- if (STM32_USART_SR(base) & STM32_USART_SR_RXNE)
- usart_interrupt_rx(config);
-}
+ config->tx->interrupt(config);
+ config->rx->interrupt(config);
+} \ No newline at end of file
diff --git a/chip/stm32/usart.h b/chip/stm32/usart.h
index 625f6e970f..589af6c26b 100644
--- a/chip/stm32/usart.h
+++ b/chip/stm32/usart.h
@@ -46,6 +46,32 @@ struct usart_hw_ops {
};
/*
+ * The usart_rx/usart_tx structures contain functions pointers for the
+ * interrupt handler and producer/consumer operations required to implement a
+ * particular RX/TX strategy.
+ *
+ * These structures are defined by the various RX/TX implementations, and are
+ * used to initialize the usart_config structure to configure the USART driver
+ * for interrupt or DMA based transfer.
+ */
+struct usart_rx {
+ void (*init)(struct usart_config const *config);
+ void (*interrupt)(struct usart_config const *config);
+
+ struct producer_ops producer_ops;
+};
+
+struct usart_tx {
+ void (*init)(struct usart_config const *config);
+ void (*interrupt)(struct usart_config const *config);
+
+ struct consumer_ops consumer_ops;
+};
+
+extern struct usart_rx const usart_rx_interrupt;
+extern struct usart_tx const usart_tx_interrupt;
+
+/*
* Per-USART hardware configuration stored in flash. Instances of this
* structure are provided by each variants driver, one per physical USART.
*/
@@ -72,6 +98,9 @@ struct usart_config {
*/
struct usart_hw_config const *hw;
+ struct usart_rx const *rx;
+ struct usart_tx const *tx;
+
/*
* Pointer to USART state structure. The state structure maintains per
* USART information.
@@ -88,13 +117,6 @@ struct usart_config {
};
/*
- * These function tables are defined by the USART driver and are used to
- * initialize the consumer and producer in the usart_config.
- */
-extern struct consumer_ops const usart_consumer_ops;
-extern struct producer_ops const usart_producer_ops;
-
-/*
* Convenience macro for defining USARTs and their associated state and buffers.
* NAME is used to construct the names of the usart_state struct, and
* usart_config struct, the latter is just called NAME.
@@ -111,26 +133,22 @@ extern struct producer_ops const usart_producer_ops;
* BUILD_ASSERT(RX_QUEUE.unit_bytes == 1);
* BUILD_ASSERT(TX_QUEUE.unit_bytes == 1);
*/
-#define USART_CONFIG(NAME, \
- HW, \
- BAUD, \
- RX_QUEUE, \
- TX_QUEUE) \
- \
- static struct usart_state CONCAT2(NAME, _state); \
- struct usart_config const NAME = { \
+#define USART_CONFIG(HW, RX, TX, BAUD, RX_QUEUE, TX_QUEUE) \
+ ((struct usart_config const) { \
.hw = &HW, \
- .state = &CONCAT2(NAME, _state), \
+ .rx = &RX, \
+ .tx = &TX, \
+ .state = &((struct usart_state){}), \
.baud = BAUD, \
.consumer = { \
.queue = &TX_QUEUE, \
- .ops = &usart_consumer_ops, \
+ .ops = &TX.consumer_ops, \
}, \
.producer = { \
.queue = &RX_QUEUE, \
- .ops = &usart_producer_ops, \
+ .ops = &RX.producer_ops, \
}, \
- };
+ })
/*
* Initialize the given USART. Once init is finished the USART streams are
diff --git a/chip/stm32/usart_rx_interrupt.c b/chip/stm32/usart_rx_interrupt.c
new file mode 100644
index 0000000000..6a46b3fb11
--- /dev/null
+++ b/chip/stm32/usart_rx_interrupt.c
@@ -0,0 +1,48 @@
+/* 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 RX driver for STM32 */
+
+#include "usart.h"
+
+#include "atomic.h"
+#include "common.h"
+#include "queue.h"
+#include "registers.h"
+
+static void usart_rx_init(struct usart_config const *config)
+{
+ intptr_t base = config->hw->base;
+
+ STM32_USART_CR1(base) |= STM32_USART_CR1_RXNEIE;
+ STM32_USART_CR1(base) |= STM32_USART_CR1_RE;
+}
+
+static void usart_rx_interrupt_handler(struct usart_config const *config)
+{
+ intptr_t base = config->hw->base;
+ uint8_t byte;
+
+ if (!(STM32_USART_SR(base) & STM32_USART_SR_RXNE))
+ return;
+
+ byte = STM32_USART_RDR(base);
+
+ if (!queue_add_unit(config->producer.queue, &byte))
+ atomic_add((uint32_t *) &config->state->rx_dropped, 1);
+}
+
+struct usart_rx const usart_rx_interrupt = {
+ .producer_ops = {
+ /*
+ * Nothing to do here, we either had enough space in the queue
+ * when a character came in or we dropped it already.
+ */
+ .read = NULL,
+ },
+
+ .init = usart_rx_init,
+ .interrupt = usart_rx_interrupt_handler,
+};
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,
+};