aboutsummaryrefslogtreecommitdiff
path: root/lib/chibios-contrib/os/hal/ports/MSP430X/hal_dma_lld.c
diff options
context:
space:
mode:
Diffstat (limited to 'lib/chibios-contrib/os/hal/ports/MSP430X/hal_dma_lld.c')
-rw-r--r--lib/chibios-contrib/os/hal/ports/MSP430X/hal_dma_lld.c246
1 files changed, 246 insertions, 0 deletions
diff --git a/lib/chibios-contrib/os/hal/ports/MSP430X/hal_dma_lld.c b/lib/chibios-contrib/os/hal/ports/MSP430X/hal_dma_lld.c
new file mode 100644
index 000000000..fa68b0578
--- /dev/null
+++ b/lib/chibios-contrib/os/hal/ports/MSP430X/hal_dma_lld.c
@@ -0,0 +1,246 @@
1/*
2 ChibiOS - Copyright (C) 2016 Andrew Wygle aka awygle
3
4 Licensed under the Apache License, Version 2.0 (the "License");
5 you may not use this file except in compliance with the License.
6 You may obtain a copy of the License at
7
8 http://www.apache.org/licenses/LICENSE-2.0
9
10 Unless required by applicable law or agreed to in writing, software
11 distributed under the License is distributed on an "AS IS" BASIS,
12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 See the License for the specific language governing permissions and
14 limitations under the License.
15*/
16
17/**
18 * @file MSP430X hal_dma_lld.c
19 * @brief MSP430X DMA subsystem low level driver source.
20 *
21 * @addtogroup MSP430X_DMA
22 * @{
23 */
24
25#include "hal.h"
26#include "ch.h"
27#include "hal_dma_lld.h"
28
29#if (HAL_USE_DMA == TRUE) || defined(__DOXYGEN__)
30
31/*===========================================================================*/
32/* Driver local definitions. */
33/*===========================================================================*/
34
35/*===========================================================================*/
36/* Driver exported variables. */
37/*===========================================================================*/
38
39/*===========================================================================*/
40/* Driver local variables and types. */
41/*===========================================================================*/
42
43static msp430x_dma_ch_reg_t * const dma_channels =
44 (msp430x_dma_ch_reg_t *)&DMA0CTL;
45
46static msp430x_dma_cb_t callbacks[MSP430X_DMA_CHANNELS];
47static threads_queue_t dma_queue;
48static unsigned int queue_length;
49
50/*===========================================================================*/
51/* Driver local functions. */
52/*===========================================================================*/
53
54/**
55 * @brief Set a DMA trigger using an index.
56 *
57 * @param[in] index The index of the DMA channel whose trigger is set.
58 * @param[in] trigger The trigger to use.
59 * @note This is all to get around weird MSP behavior when writing to memory-
60 * mapped registers using bytewise instructions.
61 */
62static void dma_trigger_set(uint8_t index, uint8_t trigger) {
63 uint16_t * ctl = ((uint16_t *)((uintptr_t)(&DMACTL0)) + (index / 2));
64 *ctl &= 0xFF00 >> (8 * (index % 2));
65 *ctl |= trigger << (8 * (index % 2));
66}
67static void init_request(const msp430x_dma_req_t * request, uint8_t index) {
68
69 dma_trigger_set(index, request->trigger);
70 callbacks[index] = request->callback;
71 msp430x_dma_ch_reg_t * ch = &dma_channels[index];
72 ch->sa = (uintptr_t)request->source_addr;
73 ch->da = (uintptr_t)request->dest_addr;
74 ch->sz = request->size;
75 ch->ctl = DMAREQ | DMAIE | DMAEN | request->data_mode | request->addr_mode |
76 request->transfer_mode;
77}
78
79/*===========================================================================*/
80/* Driver interrupt handlers. */
81/*===========================================================================*/
82
83PORT_IRQ_HANDLER(DMA_VECTOR) {
84 uint8_t index;
85 OSAL_IRQ_PROLOGUE();
86
87 index = (DMAIV >> 1) - 1;
88
89 if (index < MSP430X_DMA_CHANNELS) {
90 osalSysLockFromISR();
91 osalThreadDequeueNextI(&dma_queue, MSG_OK);
92 osalSysUnlockFromISR();
93
94 msp430x_dma_cb_t * cb = &callbacks[index];
95
96 /* WARNING: CALLBACKS ARE CALLED IN AN ISR CONTEXT! */
97 if (cb->callback != NULL) {
98 cb->callback(cb->args);
99 }
100 }
101
102 OSAL_IRQ_EPILOGUE();
103}
104
105/*===========================================================================*/
106/* Driver exported functions. */
107/*===========================================================================*/
108
109/**
110 * @brief Initialize the DMA engine.
111 *
112 * @init
113 */
114void dmaInit(void) {
115 osalThreadQueueObjectInit(&dma_queue);
116}
117
118/**
119 * @brief Requests a DMA transfer operation from the DMA engine.
120 * @note The DMA engine uses unclaimed DMA channels to provide DMA services
121 * for one-off or infrequent uses. If all channels are busy, and
122 * semaphores are enabled, the calling thread will sleep until a
123 * channel is available or the request times out. If semaphores are
124 * disabled, the calling thread will busy-wait instead of sleeping.
125 *
126 * @sclass
127 */
128int dmaRequestS(msp430x_dma_req_t * request, systime_t timeout) {
129
130 osalDbgCheckClassS();
131
132 /* Check if a DMA channel is available */
133 if (queue_length >= MSP430X_DMA_CHANNELS) {
134 msg_t queueresult = osalThreadEnqueueTimeoutS(&dma_queue, timeout);
135 if (queueresult != MSG_OK)
136 return -1;
137 }
138
139 /* Grab the correct DMA channel to use */
140 int i = 0;
141 for (i = 0; i < MSP430X_DMA_CHANNELS; i++) {
142 if (!(dma_channels[i].ctl & DMAEN)) {
143 break;
144 }
145 }
146
147 /* Make the request */
148 init_request(request, i);
149
150 return i;
151}
152
153/**
154 * @brief Acquires exclusive control of a DMA channel.
155 * @pre The channel must not be already acquired or an error is returned.
156 * @note If the channel is in use by the DMA engine, blocks until acquired.
157 * @post This channel must be interacted with using only the functions
158 * defined in this module.
159 *
160 * @param[out] channel The channel handle. Must be pre-allocated.
161 * @param[in] index The index of the channel (< MSP430X_DMA_CHANNELS).
162 * @return The operation status.
163 * @retval false no error, channel acquired.
164 * @retval true error, channel already acquired.
165 *
166 * @iclass
167 */
168bool dmaAcquireI(msp430x_dma_ch_t * channel, uint8_t index) {
169
170 osalDbgCheckClassI();
171
172 /* Is the channel already acquired? */
173 osalDbgAssert(index < MSP430X_DMA_CHANNELS, "invalid channel index");
174 if (dma_channels[index].ctl & DMADT_4) {
175 return true;
176 }
177
178 /* Increment the DMA counter */
179 queue_length++;
180
181 while (dma_channels[index].ctl & DMAEN)
182 ;
183
184 /* Acquire the channel in an idle mode */
185 dma_trigger_set(index, DMA_TRIGGER_MNEM(DMAREQ));
186 dma_channels[index].sz = 0;
187 dma_channels[index].ctl = DMAEN | DMAABORT | DMADT_4;
188
189 channel->registers = dma_channels + index;
190 channel->index = index;
191 channel->cb = callbacks + index;
192
193 return false;
194}
195
196/**
197 * @brief Initiates a DMA transfer operation using an acquired channel.
198 * @pre The channel must have been acquired using @p dmaAcquire().
199 *
200 * @param[in] channel pointer to a DMA channel from @p dmaAcquire().
201 * @param[in] request pointer to a DMA request object.
202 */
203void dmaTransfer(msp430x_dma_ch_t * channel, msp430x_dma_req_t * request) {
204
205 dma_trigger_set(channel->index, request->trigger);
206 /**(channel->ctl) = request->trigger;*/
207
208 channel->cb->callback = request->callback.callback;
209 channel->cb->args = request->callback.args;
210
211 channel->registers->ctl &= (~DMAEN);
212 channel->registers->sa = (uintptr_t)request->source_addr;
213 channel->registers->da = (uintptr_t)request->dest_addr;
214 channel->registers->sz = request->size;
215 channel->registers->ctl = DMAIE | request->data_mode | request->addr_mode |
216 request->transfer_mode | DMADT_4 | DMAEN |
217 DMAREQ; /* repeated transfers */
218}
219
220/**
221 * @brief Releases exclusive control of a DMA channel.
222 * @details The channel is released from control and returned to the DMA
223 * engine
224 * pool. Trying to release an unallocated channel is an illegal
225 * operation and is trapped if assertions are enabled.
226 * @pre The channel must have been acquired using @p dmaAcquire().
227 * @post The channel is returned to the DMA engine pool.
228 */
229void dmaRelease(msp430x_dma_ch_t * channel) {
230 syssts_t sts;
231
232 sts = osalSysGetStatusAndLockX();
233 osalDbgCheck(channel != NULL);
234
235 /* Release the channel in an idle mode */
236 channel->registers->ctl = DMAABORT;
237
238 /* release the DMA counter */
239 osalThreadDequeueAllI(&dma_queue, MSG_RESET);
240 queue_length = 0;
241 osalSysRestoreStatusX(sts);
242}
243
244#endif /* HAL_USE_DMA == TRUE */
245
246/** @} */