summaryrefslogtreecommitdiff
path: root/chip/stm32/dma.h
blob: c9832fcfe389d7aecd88a9f1917eb770abb8fb0d (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
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
/* Copyright (c) 2012 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.
 *
 * Register map and API for STM32 processor dma registers
 */

#ifndef __STM32_DMA
#define __STM32_DMA

#include "common.h"

/*
 * Available DMA channels, numbered from 0
 *
 * Note: The STM datasheet tends to number things from 1. We should ask
 * the European elevator engineers to talk to MCU engineer counterparts
 * about this.  This means that if the datasheet refers to channel n,
 * you need to use n-1 in the code.
 */
enum {
	DMAC_ADC,
	DMAC_SPI1_RX,
	DMAC_SPI1_TX,
	DMAC_SPI2_RX,
	DMAC_SPI2_TX,

	/*
	 * The same channels are used for i2c2 and spi, you can't use them at
	 * the same time or it will cause dma to not work
	 */
	DMAC_I2C2_TX = 3,
	DMAC_I2C2_RX = 4,
	DMAC_I2C1_TX = 5,
	DMAC_I2C1_RX = 6,

	/* DMA1 has 7 channels, DMA2 has 5 */
	DMA1_NUM_CHANNELS = 7,
	DMA2_NUM_CHANNELS = 5,
	DMA_NUM_CHANNELS = DMA1_NUM_CHANNELS + DMA2_NUM_CHANNELS,
};

/* A single channel of the DMA controller */
struct dma_channel {
	uint32_t	ccr;		/* Control */
	uint32_t	cndtr;		/* Number of data to transfer */
	uint32_t	cpar;		/* Peripheral address */
	uint32_t	cmar;		/* Memory address */
	uint32_t	reserved;
};

/* Registers for the DMA controller */
struct dma_ctlr {
	uint32_t	isr;
	uint32_t	ifcr;
	struct dma_channel chan[DMA_NUM_CHANNELS];
};

/* DMA channel options */
struct dma_option {
	unsigned channel;	/* DMA channel */
	void *periph;		/* Pointer to peripheral data register */
	unsigned flags;		/* DMA flags for the control register. Normally
				   used to select memory size. */
};

/* Defines for accessing DMA ccr */
#define DMA_PL_SHIFT		12
#define DMA_PL_MASK		(3 << DMA_PL_SHIFT)
enum {
	DMA_PL_LOW,
	DMA_PL_MEDIUM,
	DMA_PL_HIGH,
	DMA_PL_VERY_HIGH,
};

#define DMA_EN			(1 << 0)
#define DMA_TCIE		(1 << 1)
#define DMA_HTIE		(1 << 2)
#define DMA_TEIE		(1 << 3)
#define DMA_DIR_FROM_MEM_MASK	(1 << 4)
#define DMA_MINC_MASK		(1 << 7)
#define DMA_TCIF(channel)	(1 << (1 + 4 * channel))

#define DMA_MSIZE_BYTE		(0 << 10)
#define DMA_MSIZE_HALF_WORD	(1 << 10)
#define DMA_MSIZE_WORD		(2 << 10)

#define DMA_PSIZE_BYTE		(0 << 8)
#define DMA_PSIZE_HALF_WORD	(1 << 8)
#define DMA_PSIZE_WORD		(2 << 8)

#define DMA_POLLING_INTERVAL_US	100	/* us */
#define DMA_TRANSFER_TIMEOUT_US	100000	/* us */

/*
 * Certain DMA channels must be used for certain peripherals and transfer
 * directions. We provide an easy way for drivers to select the correct
 * channel.
 */

/**
 * @param spi	SPI port to request: STM32_SPI1_PORT or STM32_SPI2_PORT
 * @return DMA channel to use for rx / tx on that port
 */
#define DMA_CHANNEL_FOR_SPI_RX(spi) \
	((spi) == STM32_SPI1_PORT ? DMAC_SPI1_RX : DMAC_SPI2_RX)
#define DMA_CHANNEL_FOR_SPI_TX(spi) \
	((spi) == STM32_SPI1_PORT ? DMAC_SPI1_TX : DMAC_SPI2_TX)

/**
 * Get a pointer to a DMA channel.
 *
 * @param channel	Channel number to read (DMAC_...)
 * @return pointer to DMA channel registers
 */
struct dma_channel *dma_get_channel(int channel);

/**
 * Prepare a DMA transfer to transmit data from memory to a peripheral
 *
 * Call dma_go() afterwards to actually start the transfer.
 *
 * @param option	DMA channel options
 * @param count		Number of bytes to transfer
 * @param memory	Pointer to memory address
 *
 * @return pointer to prepared channel
 */
void dma_prepare_tx(const struct dma_option *option, unsigned count,
		    const void *memory);

/**
 * Start a DMA transfer to receive data to memory from a peripheral
 *
 * @param option	DMA channel options
 * @param count		Number of bytes to transfer
 * @param memory	Pointer to memory address
 */
int dma_start_rx(const struct dma_option *option, unsigned count,
		 const void *memory);

/**
 * Stop a DMA transfer on a channel
 *
 * Disable the DMA channel and immediate stop all transfers on it.
 *
 * @param channel	Channel number to stop (DMAC_...)
 */
void dma_disable(unsigned channel);

/**
 * Get the number of bytes available to read, or number of bytes written
 *
 * Since the DMA controller counts downwards, if we know the starting value
 * we can work out how many bytes have been completed so far.
 *
 * @param chan		DMA channel to check (use dma_get_channel())
 * @param orig_count	Original number of bytes requested on channel
 * @return number of bytes completed on a channel, or 0 if this channel is
 *		not enabled
 */
int dma_bytes_done(struct dma_channel *chan, int orig_count);

/**
 * Start a previously-prepared dma channel
 *
 * @param chan	Channel to start (returned from dma_prepare...())
 */
void dma_go(struct dma_channel *chan);

/**
 * Testing: Print out the data transferred by a channel
 *
 * @param channel	Channel number to read (DMAC_...)
 * @param buff		Start of DMA buffer
 */
void dma_check(int channel, char *buff);

/**
 * Dump out imformation about a dma channel
 *
 * @param channel	Channel number to read (DMAC_...)
 */
void dma_dump(unsigned channel);

/**
 * Testing: Test that DMA works correctly for memory to memory transfers
 */
void dma_test(void);

/**
 * Init DMA peripheral ready for use
 */
void dma_init(void);

/**
 * Clear the DMA interrupt/event flags for a given channel
 *
 * @param channel	Which channel's isr to clear (DMAC_...)
 */
void dma_clear_isr(int channel);

/**
 * Enable "Transfer Complete" interrupt for a DMA channel
 *
 * @param channel	Which channel's interrupts to change (DMAC_...)
 */
void dma_enable_tc_interrupt(int channel);

/**
 * Disable "Transfer Complete" interrupt for a DMA channel
 *
 * @param channel	Which channel's interrupts to change (DMAC_...)
 */
void dma_disable_tc_interrupt(int channel);

/**
 * Get a pointer to the DMA peripheral controller that owns the channel
 *
 * @param channel	Channel number to get the controller for (DMAC_...)
 * @return pointer to DMA channel registers
 */
struct dma_ctlr *dma_get_ctlr(int channel);

/**
 * Wait for the DMA transfer to complete by polling the transfer complete flag
 *
 * @param channelĀ»      Channel number to wait on (DMAC_...)
 * @return -1 for timeout, 0 for sucess
 */
int dma_wait(int channel);

#endif