dma.c 10 KB
Newer Older
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
/*
 * This file is part of the Micro Python project, http://micropython.org/
 *
 * The MIT License (MIT)
 *
 * Copyright (c) 2015 Damien P. George
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 */

#include <stdio.h>
#include <string.h>
#include <stdint.h>
30
#include STM32_HAL_H
31
32

#include "dma.h"
33
34
#include "py/obj.h"
#include "irq.h"
35

36
37
#define NSTREAMS_PER_CONTROLLER_LOG2 (3)
#define NSTREAMS_PER_CONTROLLER (1 << NSTREAMS_PER_CONTROLLER_LOG2)
38
39
#define NCONTROLLERS            (2)
#define NSTREAM                 (NCONTROLLERS * NSTREAMS_PER_CONTROLLER)
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59

static const uint8_t dma_irqn[NSTREAM] = {
    DMA1_Stream0_IRQn,
    DMA1_Stream1_IRQn,
    DMA1_Stream2_IRQn,
    DMA1_Stream3_IRQn,
    DMA1_Stream4_IRQn,
    DMA1_Stream5_IRQn,
    DMA1_Stream6_IRQn,
    DMA1_Stream7_IRQn,
    DMA2_Stream0_IRQn,
    DMA2_Stream1_IRQn,
    DMA2_Stream2_IRQn,
    DMA2_Stream3_IRQn,
    DMA2_Stream4_IRQn,
    DMA2_Stream5_IRQn,
    DMA2_Stream6_IRQn,
    DMA2_Stream7_IRQn,
};

60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
// Default parameters to dma_init() shared by spi and i2c; Channel and Direction
// vary depending on the peripheral instance so they get passed separately
const DMA_InitTypeDef dma_init_struct_spi_i2c = {
    .Channel             = 0,
    .Direction           = 0,
    .PeriphInc           = DMA_PINC_DISABLE,
    .MemInc              = DMA_MINC_ENABLE,
    .PeriphDataAlignment = DMA_PDATAALIGN_BYTE,
    .MemDataAlignment    = DMA_MDATAALIGN_BYTE,
    .Mode                = DMA_NORMAL,
    .Priority            = DMA_PRIORITY_LOW,
    .FIFOMode            = DMA_FIFOMODE_DISABLE,
    .FIFOThreshold       = DMA_FIFO_THRESHOLD_FULL,
    .MemBurst            = DMA_MBURST_INC4,
    .PeriphBurst         = DMA_PBURST_INC4
};

77
static DMA_HandleTypeDef *dma_handle[NSTREAM] = {NULL};
78
79
80
81
82
83
84
85
86
87
static uint8_t  dma_last_channel[NSTREAM];
static volatile uint32_t dma_enable_mask = 0;

volatile dma_idle_count_t dma_idle;

#define DMA1_ENABLE_MASK    0x00ff  // Bits in dma_enable_mask corresponfing to DMA1
#define DMA2_ENABLE_MASK    0xff00  // Bits in dma_enable_mask corresponding to DMA2
#define DMA_INVALID_CHANNEL 0xff    // Value stored in dma_last_channel which means invalid

#define DMA_CHANNEL_AS_UINT8(dma_channel)   (((dma_channel) & DMA_SxCR_CHSEL) >> 24)
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105

void DMA1_Stream0_IRQHandler(void) { if (dma_handle[0] != NULL) { HAL_DMA_IRQHandler(dma_handle[0]); } }
void DMA1_Stream1_IRQHandler(void) { if (dma_handle[1] != NULL) { HAL_DMA_IRQHandler(dma_handle[1]); } }
void DMA1_Stream2_IRQHandler(void) { if (dma_handle[2] != NULL) { HAL_DMA_IRQHandler(dma_handle[2]); } }
void DMA1_Stream3_IRQHandler(void) { if (dma_handle[3] != NULL) { HAL_DMA_IRQHandler(dma_handle[3]); } }
void DMA1_Stream4_IRQHandler(void) { if (dma_handle[4] != NULL) { HAL_DMA_IRQHandler(dma_handle[4]); } }
void DMA1_Stream5_IRQHandler(void) { if (dma_handle[5] != NULL) { HAL_DMA_IRQHandler(dma_handle[5]); } }
void DMA1_Stream6_IRQHandler(void) { if (dma_handle[6] != NULL) { HAL_DMA_IRQHandler(dma_handle[6]); } }
void DMA1_Stream7_IRQHandler(void) { if (dma_handle[7] != NULL) { HAL_DMA_IRQHandler(dma_handle[7]); } }
void DMA2_Stream0_IRQHandler(void) { if (dma_handle[8] != NULL) { HAL_DMA_IRQHandler(dma_handle[8]); } }
void DMA2_Stream1_IRQHandler(void) { if (dma_handle[9] != NULL) { HAL_DMA_IRQHandler(dma_handle[9]); } }
void DMA2_Stream2_IRQHandler(void) { if (dma_handle[10] != NULL) { HAL_DMA_IRQHandler(dma_handle[10]); } }
void DMA2_Stream3_IRQHandler(void) { if (dma_handle[11] != NULL) { HAL_DMA_IRQHandler(dma_handle[11]); } }
void DMA2_Stream4_IRQHandler(void) { if (dma_handle[12] != NULL) { HAL_DMA_IRQHandler(dma_handle[12]); } }
void DMA2_Stream5_IRQHandler(void) { if (dma_handle[13] != NULL) { HAL_DMA_IRQHandler(dma_handle[13]); } }
void DMA2_Stream6_IRQHandler(void) { if (dma_handle[14] != NULL) { HAL_DMA_IRQHandler(dma_handle[14]); } }
void DMA2_Stream7_IRQHandler(void) { if (dma_handle[15] != NULL) { HAL_DMA_IRQHandler(dma_handle[15]); } }

106
107
108
#define DMA1_IS_CLK_ENABLED()   ((RCC->AHB1ENR & RCC_AHB1ENR_DMA1EN) != 0)
#define DMA2_IS_CLK_ENABLED()   ((RCC->AHB1ENR & RCC_AHB1ENR_DMA2EN) != 0)

109
static int get_dma_id(DMA_Stream_TypeDef *dma_stream) {
110
111
112
113
114
115
116
117
118
119
120
    int dma_id;
    if (dma_stream < DMA2_Stream0) {
        dma_id = dma_stream - DMA1_Stream0;
    } else {
        dma_id =  NSTREAMS_PER_CONTROLLER + (dma_stream - DMA2_Stream0);
    }
    return dma_id;
}

// Resets the idle counter for the DMA controller associated with dma_id.
static void dma_tickle(int dma_id) {
121
    dma_idle.counter[(dma_id >> NSTREAMS_PER_CONTROLLER_LOG2) & 1] = 1;
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
}

static void dma_enable_clock(int dma_id) {
    // We don't want dma_tick_handler() to turn off the clock right after we
    // enable it, so we need to mark the channel in use in an atomic fashion.
    mp_uint_t irq_state = MICROPY_BEGIN_ATOMIC_SECTION();
    uint32_t old_enable_mask = dma_enable_mask;
    dma_enable_mask |= (1 << dma_id);
    MICROPY_END_ATOMIC_SECTION(irq_state);

    if (dma_id <= 7) {
        if (((old_enable_mask & DMA1_ENABLE_MASK) == 0) && !DMA1_IS_CLK_ENABLED()) {
            __DMA1_CLK_ENABLE();

            // We just turned on the clock. This means that anything stored
            // in dma_last_channel (for DMA1) needs to be invalidated.

            for (int channel = 0; channel < NSTREAMS_PER_CONTROLLER; channel++) {
                dma_last_channel[channel] = DMA_INVALID_CHANNEL;
            }
        }
143
    } else {
144
145
146
147
148
149
150
151
152
153
        if (((old_enable_mask & DMA2_ENABLE_MASK) == 0) && !DMA2_IS_CLK_ENABLED()) {
            __DMA2_CLK_ENABLE();

            // We just turned on the clock. This means that anything stored
            // in dma_last_channel (for DMA1) needs to be invalidated.

            for (int channel = NSTREAMS_PER_CONTROLLER; channel < NSTREAM; channel++) {
                dma_last_channel[channel] = DMA_INVALID_CHANNEL;
            }
        }
154
155
156
    }
}

157
158
159
160
161
162
163
164
static void dma_disable_clock(int dma_id) {
    // We just mark the clock as disabled here, but we don't actually disable it.
    // We wait for the timer to expire first, which means that back-to-back
    // transfers don't have to initialize as much.
    dma_tickle(dma_id);
    dma_enable_mask &= ~(1 << dma_id);
}

165
void dma_init(DMA_HandleTypeDef *dma, DMA_Stream_TypeDef *dma_stream, const DMA_InitTypeDef *dma_init, uint32_t dma_channel, uint32_t direction, void *data) {
166
167
168
    int dma_id = get_dma_id(dma_stream);
    //printf("dma_init(%p, %p(%d), 0x%x, 0x%x, %p)\n", dma, dma_stream, dma_id, (uint)dma_channel, (uint)direction, data);

169
170
171
    // Some drivers allocate the DMA_HandleTypeDef from the stack
    // (i.e. dac, i2c, spi) and for those cases we need to clear the
    // structure so we don't get random values from the stack)
172
173
174
175
176
    memset(dma, 0, sizeof(*dma));

    // set global pointer for IRQ handler
    dma_handle[dma_id] = dma;

177
    // initialise parameters
178
    dma->Instance = dma_stream;
179
    dma->Init = *dma_init;
180
    dma->Init.Direction = direction;
181
    dma->Init.Channel = dma_channel;
182
183
184
185
186

    // half of __HAL_LINKDMA(data, xxx, *dma)
    // caller must implement other half by doing: data->xxx = dma
    dma->Parent = data;

187
188
    dma_enable_clock(dma_id);

189
190
    // if this stream was previously configured for this channel then we
    // can skip most of the initialisation
191
192
    uint8_t channel_uint8 = DMA_CHANNEL_AS_UINT8(dma_channel);
    if (dma_last_channel[dma_id] == channel_uint8) {
193
194
        goto same_channel;
    }
195
    dma_last_channel[dma_id] = channel_uint8;
196
197

    // reset and configure DMA peripheral
198
199
200
    if (HAL_DMA_GetState(dma) != HAL_DMA_STATE_RESET) {
        HAL_DMA_DeInit(dma);
    }
201
    HAL_DMA_Init(dma);
202
    HAL_NVIC_SetPriority(dma_irqn[dma_id], IRQ_PRI_DMA, IRQ_SUBPRI_DMA);
203
204
205
206
207
208
209
210
211

same_channel:
    HAL_NVIC_EnableIRQ(dma_irqn[dma_id]);
}

void dma_deinit(DMA_HandleTypeDef *dma) {
    int dma_id = get_dma_id(dma->Instance);
    HAL_NVIC_DisableIRQ(dma_irqn[dma_id]);
    dma_handle[dma_id] = NULL;
212
213

    dma_disable_clock(dma_id);
214
215
216
217
}

void dma_invalidate_channel(DMA_Stream_TypeDef *dma_stream, uint32_t dma_channel) {
    int dma_id = get_dma_id(dma_stream);
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
    if (dma_last_channel[dma_id] == DMA_CHANNEL_AS_UINT8(dma_channel)) {
        dma_last_channel[dma_id] = DMA_INVALID_CHANNEL;
    }
}

// Called from the SysTick handler (once per millisecond)
void dma_idle_handler() {
    static const uint32_t   controller_mask[] = {
        DMA1_ENABLE_MASK, DMA2_ENABLE_MASK
    };
    for (int controller = 0; controller < NCONTROLLERS; controller++) {
        if (dma_idle.counter[controller] == 0) {
            continue;
        }
        if (++dma_idle.counter[controller] > DMA_IDLE_TICK_MAX) {
            if ((dma_enable_mask & controller_mask[controller]) == 0) {
                // Nothing is active and we've reached our idle timeout,
                // Now we'll really disable the clock.
                dma_idle.counter[controller] = 0;
                if (controller == 0) {
                    __DMA1_CLK_DISABLE();
                } else {
                    __DMA2_CLK_DISABLE();
                }
            } else {
                // Something is still active, but the counter never got
                // reset, so we'll reset the counter here.
                dma_idle.counter[controller] = 1;
            }
        }
248
249
    }
}