blob: a18587826156bf18664dbbe1c165dbf9a87daf26 [file] [log] [blame]
/* Copyright 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_rx_dma.h"
#include "atomic.h"
#include "common.h"
#include "console.h"
#include "registers.h"
#include "system.h"
#include "usart_host_command.h"
#include "util.h"
typedef size_t (*add_data_t)(struct usart_config const *config,
const uint8_t *src, size_t count);
void usart_rx_dma_init(struct usart_config const *config)
{
struct usart_rx_dma const *dma_config =
DOWNCAST(config->rx, struct usart_rx_dma const, usart_rx);
intptr_t base = config->hw->base;
struct dma_option options = {
.channel = dma_config->channel,
.periph = (void *)&STM32_USART_RDR(base),
.flags = (STM32_DMA_CCR_MSIZE_8_BIT |
STM32_DMA_CCR_PSIZE_8_BIT |
STM32_DMA_CCR_CIRC),
};
if (IS_ENABLED(CHIP_FAMILY_STM32F4))
options.flags |= STM32_DMA_CCR_CHANNEL(STM32_REQ_USART1_RX);
STM32_USART_CR1(base) |= STM32_USART_CR1_RXNEIE;
STM32_USART_CR1(base) |= STM32_USART_CR1_RE;
STM32_USART_CR3(base) |= STM32_USART_CR3_DMAR;
dma_config->state->index = 0;
dma_config->state->max_bytes = 0;
dma_start_rx(&options, dma_config->fifo_size, dma_config->fifo_buffer);
}
static void usart_rx_dma_interrupt_common(
struct usart_config const *config,
add_data_t add_data)
{
struct usart_rx_dma const *dma_config =
DOWNCAST(config->rx, struct usart_rx_dma const, usart_rx);
dma_chan_t *channel = dma_get_channel(dma_config->channel);
size_t new_index = dma_bytes_done(channel, dma_config->fifo_size);
size_t old_index = dma_config->state->index;
size_t new_bytes = 0;
size_t added = 0;
if (new_index > old_index) {
new_bytes = new_index - old_index;
added = add_data(config,
dma_config->fifo_buffer + old_index,
new_bytes);
} else if (new_index < old_index) {
/*
* Handle the case where the received bytes are not contiguous
* in the circular DMA buffer. This is done with two queue
* adds.
*/
new_bytes = dma_config->fifo_size - (old_index - new_index);
added = add_data(config,
dma_config->fifo_buffer + old_index,
dma_config->fifo_size - old_index) +
add_data(config,
dma_config->fifo_buffer,
new_index);
} else {
/* (new_index == old_index): nothing to add to the queue. */
}
atomic_add((uint32_t *)&(config->state->rx_dropped), new_bytes - added);
if (dma_config->state->max_bytes < new_bytes)
dma_config->state->max_bytes = new_bytes;
dma_config->state->index = new_index;
}
static size_t queue_add(struct usart_config const *config,
const uint8_t *src, size_t count)
{
return queue_add_units(config->producer.queue, (void *)src, count);
}
void usart_rx_dma_interrupt(struct usart_config const *config)
{
usart_rx_dma_interrupt_common(config, &queue_add);
}
#if defined(CONFIG_USART_HOST_COMMAND)
void usart_host_command_rx_dma_interrupt(struct usart_config const *config)
{
usart_rx_dma_interrupt_common(config,
&usart_host_command_rx_append_data);
}
#endif /* CONFIG_USART_HOST_COMMAND */
void usart_rx_dma_info(struct usart_config const *config)
{
struct usart_rx_dma const *dma_config =
DOWNCAST(config->rx, struct usart_rx_dma const, usart_rx);
ccprintf(" DMA RX max_bytes %d\n",
atomic_clear((uint32_t *)&dma_config->state->max_bytes));
}