forked from zephyrproject-rtos/zephyr
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathdmamux_stm32.c
327 lines (272 loc) · 9.69 KB
/
dmamux_stm32.c
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
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
/*
* Copyright (c) 2020 STMicroelectronics
*
* SPDX-License-Identifier: Apache-2.0
*/
/**
* @brief Common part of DMAMUX drivers for stm32.
* @note api functions named dmamux_stm32_
* are calling the dma_stm32 corresponding function
* implemented in dma_stm32.c
*/
#include <soc.h>
#include <stm32_ll_dmamux.h>
#include <zephyr/init.h>
#include <zephyr/drivers/dma.h>
#include <zephyr/drivers/clock_control.h>
#include <zephyr/drivers/clock_control/stm32_clock_control.h>
#include "dma_stm32.h"
#include <zephyr/logging/log.h>
LOG_MODULE_REGISTER(dmamux_stm32, CONFIG_DMA_LOG_LEVEL);
#define DT_DRV_COMPAT st_stm32_dmamux
/* this is the configuration of one dmamux channel */
struct dmamux_stm32_channel {
/* pointer to the associated dma instance */
const struct device *dev_dma;
/* ref of the associated dma stream for this instance */
uint8_t dma_id;
};
/* the table of all the dmamux channel */
struct dmamux_stm32_data {
void *callback_arg;
void (*dmamux_callback)(void *arg, uint32_t id,
int error_code);
};
/* this is the configuration of the dmamux IP */
struct dmamux_stm32_config {
#if DT_INST_NODE_HAS_PROP(0, clocks)
struct stm32_pclken pclken;
#endif
uint32_t base;
uint8_t channel_nb; /* total nb of channels */
uint8_t gen_nb; /* total nb of Request generator */
uint8_t req_nb; /* total nb of Peripheral Request inputs */
const struct dmamux_stm32_channel *mux_channels;
};
/*
* LISTIFY is used to generate arrays with function pointers to check
* and clear interrupt flags using LL functions
*/
#define DMAMUX_CHANNEL(i, _) LL_DMAMUX_CHANNEL_ ## i
#define IS_ACTIVE_FLAG_SOX(i, _) LL_DMAMUX_IsActiveFlag_SO ## i
#define CLEAR_FLAG_SOX(i, _) LL_DMAMUX_ClearFlag_SO ## i
#define IS_ACTIVE_FLAG_RGOX(i, _) LL_DMAMUX_IsActiveFlag_RGO ## i
#define CLEAR_FLAG_RGOX(i, _) LL_DMAMUX_ClearFlag_RGO ## i
uint32_t table_ll_channel[] = {
LISTIFY(DT_INST_PROP(0, dma_channels), DMAMUX_CHANNEL, (,))
};
uint32_t (*func_ll_is_active_so[])(DMAMUX_Channel_TypeDef *DMAMUXx) = {
LISTIFY(DT_INST_PROP(0, dma_channels), IS_ACTIVE_FLAG_SOX, (,))
};
void (*func_ll_clear_so[])(DMAMUX_Channel_TypeDef *DMAMUXx) = {
LISTIFY(DT_INST_PROP(0, dma_channels), CLEAR_FLAG_SOX, (,))
};
uint32_t (*func_ll_is_active_rgo[])(DMAMUX_Channel_TypeDef *DMAMUXx) = {
LISTIFY(DT_INST_PROP(0, dma_generators), IS_ACTIVE_FLAG_RGOX, (,))
};
void (*func_ll_clear_rgo[])(DMAMUX_Channel_TypeDef *DMAMUXx) = {
LISTIFY(DT_INST_PROP(0, dma_generators), CLEAR_FLAG_RGOX, (,))
};
int dmamux_stm32_configure(const struct device *dev, uint32_t id,
struct dma_config *config)
{
/* device is the dmamux, id is the dmamux channel from 0 */
const struct dmamux_stm32_config *dev_config = dev->config;
/*
* request line ID for this mux channel is stored
* in the dma_slot parameter
*/
int request_id = config->dma_slot;
if (request_id > dev_config->req_nb + dev_config->gen_nb) {
LOG_ERR("request ID %d is not valid.", request_id);
return -EINVAL;
}
/* check if this channel is valid */
if (id >= dev_config->channel_nb) {
LOG_ERR("channel ID %d is too big.", id);
return -EINVAL;
}
/*
* Also configures the corresponding dma channel
* instance is given by the dev_dma
* stream is given by the index i
* config is directly this dma_config
*/
/*
* This dmamux channel 'id' is now used for this peripheral request
* It gives this mux request ID to the dma through the config.dma_slot
*/
if (dma_stm32_configure(dev_config->mux_channels[id].dev_dma,
dev_config->mux_channels[id].dma_id, config) != 0) {
LOG_ERR("cannot configure the dmamux.");
return -EINVAL;
}
/* set the Request Line ID to this dmamux channel i */
DMAMUX_Channel_TypeDef *dmamux =
(DMAMUX_Channel_TypeDef *)dev_config->base;
LL_DMAMUX_SetRequestID(dmamux, id, request_id);
return 0;
}
int dmamux_stm32_start(const struct device *dev, uint32_t id)
{
const struct dmamux_stm32_config *dev_config = dev->config;
/* check if this channel is valid */
if (id >= dev_config->channel_nb) {
LOG_ERR("channel ID %d is too big.", id);
return -EINVAL;
}
if (dma_stm32_start(dev_config->mux_channels[id].dev_dma,
dev_config->mux_channels[id].dma_id) != 0) {
LOG_ERR("cannot start the dmamux channel %d.", id);
return -EINVAL;
}
return 0;
}
int dmamux_stm32_stop(const struct device *dev, uint32_t id)
{
const struct dmamux_stm32_config *dev_config = dev->config;
/* check if this channel is valid */
if (id >= dev_config->channel_nb) {
LOG_ERR("channel ID %d is too big.", id);
return -EINVAL;
}
if (dma_stm32_stop(dev_config->mux_channels[id].dev_dma,
dev_config->mux_channels[id].dma_id) != 0) {
LOG_ERR("cannot stop the dmamux channel %d.", id);
return -EINVAL;
}
return 0;
}
int dmamux_stm32_reload(const struct device *dev, uint32_t id,
uint32_t src, uint32_t dst, size_t size)
{
const struct dmamux_stm32_config *dev_config = dev->config;
/* check if this channel is valid */
if (id >= dev_config->channel_nb) {
LOG_ERR("channel ID %d is too big.", id);
return -EINVAL;
}
if (dma_stm32_reload(dev_config->mux_channels[id].dev_dma,
dev_config->mux_channels[id].dma_id,
src, dst, size) != 0) {
LOG_ERR("cannot reload the dmamux channel %d.", id);
return -EINVAL;
}
return 0;
}
int dmamux_stm32_get_status(const struct device *dev, uint32_t id,
struct dma_status *stat)
{
const struct dmamux_stm32_config *dev_config = dev->config;
/* check if this channel is valid */
if (id >= dev_config->channel_nb) {
LOG_ERR("channel ID %d is too big.", id);
return -EINVAL;
}
if (dma_stm32_get_status(dev_config->mux_channels[id].dev_dma,
dev_config->mux_channels[id].dma_id, stat) != 0) {
LOG_ERR("cannot get the status of dmamux channel %d.", id);
return -EINVAL;
}
return 0;
}
static int dmamux_stm32_init(const struct device *dev)
{
#if DT_INST_NODE_HAS_PROP(0, clocks)
const struct dmamux_stm32_config *config = dev->config;
const struct device *const clk = DEVICE_DT_GET(STM32_CLOCK_CONTROL_NODE);
if (!device_is_ready(clk)) {
LOG_ERR("clock control device not ready");
return -ENODEV;
}
if (clock_control_on(clk,
(clock_control_subsys_t *) &config->pclken) != 0) {
LOG_ERR("clock op failed\n");
return -EIO;
}
#endif /* DT_INST_NODE_HAS_PROP(0, clocks) */
/* DMAs assigned to DMAMUX channels at build time might not be ready. */
#if DT_NODE_HAS_STATUS(DT_NODELABEL(dma1), okay)
if (device_is_ready(DEVICE_DT_GET(DT_NODELABEL(dma1))) == false) {
return -ENODEV;
}
#endif
#if DT_NODE_HAS_STATUS(DT_NODELABEL(dma2), okay)
if (device_is_ready(DEVICE_DT_GET(DT_NODELABEL(dma2))) == false) {
return -ENODEV;
}
#endif
return 0;
}
static const struct dma_driver_api dma_funcs = {
.reload = dmamux_stm32_reload,
.config = dmamux_stm32_configure,
.start = dmamux_stm32_start,
.stop = dmamux_stm32_stop,
.get_status = dmamux_stm32_get_status,
};
/*
* Each dmamux channel is hardwired to one dma controllers dma channel.
* DMAMUX_CHANNEL_INIT_X macros resolve this mapping at build time for each
* dmamux channel using the dma dt properties dma_offset and dma_requests,
* such that it can be stored in dmamux_stm32_channels_X configuration.
* The Macros to get the corresponding dma device binding and dma channel
* for a given dmamux channel, are currently valid for series having
* 1 dmamux and 1 or 2 dmas.
*/
#define DMA_1_BEGIN_DMAMUX_CHANNEL DT_PROP_OR(DT_NODELABEL(dma1), dma_offset, 0)
#define DMA_1_END_DMAMUX_CHANNEL (DMA_1_BEGIN_DMAMUX_CHANNEL + \
DT_PROP_OR(DT_NODELABEL(dma1), dma_requests, 0))
#define DEV_DMA1 COND_CODE_1(DT_NODE_HAS_STATUS(DT_NODELABEL(dma1), okay), \
DEVICE_DT_GET(DT_NODELABEL(dma1)), NULL)
#define DMA_2_BEGIN_DMAMUX_CHANNEL DT_PROP_OR(DT_NODELABEL(dma2), dma_offset, 0)
#define DMA_2_END_DMAMUX_CHANNEL (DMA_2_BEGIN_DMAMUX_CHANNEL + \
DT_PROP_OR(DT_NODELABEL(dma2), dma_requests, 0))
#define DEV_DMA2 COND_CODE_1(DT_NODE_HAS_STATUS(DT_NODELABEL(dma2), okay), \
DEVICE_DT_GET(DT_NODELABEL(dma2)), NULL)
#define DEV_DMA_BINDING(mux_channel) \
((mux_channel < DMA_1_END_DMAMUX_CHANNEL) ? DEV_DMA1 : DEV_DMA2)
#define DMA_CHANNEL(mux_channel) \
((mux_channel < DMA_1_END_DMAMUX_CHANNEL) ? \
(mux_channel + 1) : (mux_channel - DMA_2_BEGIN_DMAMUX_CHANNEL + 1))
/*
* No series implements more than 1 dmamux yet, dummy define added for easier
* future extension.
*/
#define INIT_DMAMUX_0_CHANNEL(x, ...) \
{ .dev_dma = DEV_DMA_BINDING(x), .dma_id = DMA_CHANNEL(x), }
#define INIT_DMAMUX_1_CHANNEL(x, ...) \
{ .dev_dma = 0, .dma_id = 0, }
#define DMAMUX_CHANNELS_INIT_0(count) \
LISTIFY(count, INIT_DMAMUX_0_CHANNEL, (,))
#define DMAMUX_CHANNELS_INIT_1(count) \
LISTIFY(count, INIT_DMAMUX_1_CHANNEL, (,))
#define DMAMUX_CLOCK_INIT(index) \
COND_CODE_1(DT_INST_NODE_HAS_PROP(index, clocks), \
(.pclken = { .bus = DT_INST_CLOCKS_CELL(index, bus), \
.enr = DT_INST_CLOCKS_CELL(index, bits)},), \
())
#define DMAMUX_INIT(index) \
static const struct dmamux_stm32_channel \
dmamux_stm32_channels_##index[DT_INST_PROP(index, dma_channels)] = { \
DMAMUX_CHANNELS_INIT_##index(DT_INST_PROP(index, dma_channels))\
}; \
\
const struct dmamux_stm32_config dmamux_stm32_config_##index = { \
DMAMUX_CLOCK_INIT(index) \
.base = DT_INST_REG_ADDR(index), \
.channel_nb = DT_INST_PROP(index, dma_channels), \
.gen_nb = DT_INST_PROP(index, dma_generators), \
.req_nb = DT_INST_PROP(index, dma_requests), \
.mux_channels = dmamux_stm32_channels_##index, \
}; \
\
static struct dmamux_stm32_data dmamux_stm32_data_##index; \
\
DEVICE_DT_INST_DEFINE(index, \
&dmamux_stm32_init, \
NULL, \
&dmamux_stm32_data_##index, &dmamux_stm32_config_##index,\
PRE_KERNEL_1, CONFIG_DMA_INIT_PRIORITY, \
&dma_funcs);
DT_INST_FOREACH_STATUS_OKAY(DMAMUX_INIT)