diff options
author | Anton Staaf <robotboy@chromium.org> | 2015-06-16 10:34:56 -0700 |
---|---|---|
committer | ChromeOS Commit Bot <chromeos-commit-bot@chromium.org> | 2015-07-15 21:57:46 +0000 |
commit | 137959bb88ef381cba67e24943388bd17ae89357 (patch) | |
tree | d4844e76548432b726cf40d57bf0214d92d64634 /chip/stm32 | |
parent | 88a1790bb7d82a30e052f1e10a9c7c88fb5c5c36 (diff) | |
download | chrome-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')
-rw-r--r-- | chip/stm32/build.mk | 1 | ||||
-rw-r--r-- | chip/stm32/usart.c | 102 | ||||
-rw-r--r-- | chip/stm32/usart.h | 56 | ||||
-rw-r--r-- | chip/stm32/usart_rx_interrupt.c | 48 | ||||
-rw-r--r-- | chip/stm32/usart_tx_interrupt.c | 90 |
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, +}; |