| /* 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)); |
| } |