summaryrefslogtreecommitdiff
path: root/chip/stm32/usart_rx_dma.c
blob: a18587826156bf18664dbbe1c165dbf9a87daf26 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
/* 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));
}