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>
This commit is contained in:
Anton Staaf
2015-06-16 10:34:56 -07:00
committed by ChromeOS Commit Bot
parent 88a1790bb7
commit 137959bb88
8 changed files with 222 additions and 126 deletions

View File

@@ -112,8 +112,8 @@ BUILD_ASSERT(ARRAY_SIZE(usb_strings) == USB_STR_COUNT);
* Define AP and SH console forwarding queues and associated USART and USB
* stream endpoints.
*/
struct usart_config const ap_usart;
struct usart_config const sh_usart;
static struct usart_config const ap_usart;
static struct usart_config const sh_usart;
struct usb_stream_config const ap_usb;
struct usb_stream_config const sh_usb;
@@ -131,8 +131,19 @@ static struct queue const sh_usb_to_usart = QUEUE_DIRECT(64, uint8_t,
sh_usb.producer,
sh_usart.consumer);
USART_CONFIG(ap_usart, usart1_hw, 115200, ap_usart_to_usb, ap_usb_to_usart)
USART_CONFIG(sh_usart, usart3_hw, 115200, sh_usart_to_usb, sh_usb_to_usart)
static struct usart_config const ap_usart = USART_CONFIG(usart1_hw,
usart_rx_interrupt,
usart_tx_interrupt,
115200,
ap_usart_to_usb,
ap_usb_to_usart);
static struct usart_config const sh_usart = USART_CONFIG(usart3_hw,
usart_rx_interrupt,
usart_tx_interrupt,
115200,
sh_usart_to_usb,
sh_usb_to_usart);
#define AP_USB_STREAM_RX_SIZE 16
#define AP_USB_STREAM_TX_SIZE 16

View File

@@ -111,8 +111,8 @@ BUILD_ASSERT(ARRAY_SIZE(usb_strings) == USB_STR_COUNT);
* Define AP and SH console forwarding queues and associated USART and USB
* stream endpoints.
*/
struct usart_config const ap_usart;
struct usart_config const sh_usart;
static struct usart_config const ap_usart;
static struct usart_config const sh_usart;
struct usb_stream_config const ap_usb;
struct usb_stream_config const sh_usb;
@@ -130,8 +130,19 @@ static struct queue const sh_usb_to_usart = QUEUE_DIRECT(64, uint8_t,
sh_usb.producer,
sh_usart.consumer);
USART_CONFIG(ap_usart, usart1_hw, 115200, ap_usart_to_usb, ap_usb_to_usart)
USART_CONFIG(sh_usart, usart3_hw, 115200, sh_usart_to_usb, sh_usb_to_usart)
static struct usart_config const ap_usart = USART_CONFIG(usart1_hw,
usart_rx_interrupt,
usart_tx_interrupt,
115200,
ap_usart_to_usb,
ap_usb_to_usart);
static struct usart_config const sh_usart = USART_CONFIG(usart3_hw,
usart_rx_interrupt,
usart_tx_interrupt,
115200,
sh_usart_to_usb,
sh_usb_to_usart);
#define AP_USB_STREAM_RX_SIZE 16
#define AP_USB_STREAM_TX_SIZE 16

View File

@@ -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

View File

@@ -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);
}

View File

@@ -45,6 +45,32 @@ struct usart_hw_ops {
void (*disable)(struct usart_config const *config);
};
/*
* 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.
@@ -87,13 +116,6 @@ struct usart_config {
struct producer producer;
};
/*
* 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
@@ -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

View File

@@ -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,
};

View File

@@ -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,
};

View File

@@ -38,7 +38,7 @@ static inline void print_buffer(uint8_t *buf, int cnt)
static inline void print_buffer(uint8_t *buf, int cnt) {}
#endif
struct usart_config const usart_mcdp;
static struct usart_config const usart_mcdp;
struct queue const usart_mcdp_rx_queue = QUEUE_DIRECT(MCDP_INBUF_MAX,
uint8_t,
@@ -49,11 +49,12 @@ struct queue const usart_mcdp_tx_queue = QUEUE_DIRECT(MCDP_OUTBUF_MAX,
null_producer,
usart_mcdp.consumer);
USART_CONFIG(usart_mcdp,
CONFIG_MCDP28X0,
115200,
usart_mcdp_rx_queue,
usart_mcdp_tx_queue);
static struct usart_config const usart_mcdp = USART_CONFIG(CONFIG_MCDP28X0,
usart_rx_interrupt,
usart_tx_interrupt,
115200,
usart_mcdp_rx_queue,
usart_mcdp_tx_queue);
/**
* Compute checksum.