1 /******************************************************************************
2 * Filename: udma.c
3 *
4 * Description: Driver for the uDMA controller
5 *
6 * Copyright (c) 2022 Texas Instruments Incorporated
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions are met:
10 *
11 * 1) Redistributions of source code must retain the above copyright notice,
12 * this list of conditions and the following disclaimer.
13 *
14 * 2) Redistributions in binary form must reproduce the above copyright notice,
15 * this list of conditions and the following disclaimer in the documentation
16 * and/or other materials provided with the distribution.
17 *
18 * 3) Neither the name of the copyright holder nor the names of its
19 * contributors may be used to endorse or promote products derived from this
20 * software without specific prior written permission.
21 *
22 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
23 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
24 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
25 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
26 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
27 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
28 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
29 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
30 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
31 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
32 * POSSIBILITY OF SUCH DAMAGE.
33 *
34 ******************************************************************************/
35
36 #include "udma.h"
37
38 //*****************************************************************************
39 //
40 // Enables attributes of a uDMA channel
41 //
42 //*****************************************************************************
uDMAEnableChannelAttribute(uint32_t channelBitMask,uint32_t attr)43 void uDMAEnableChannelAttribute(uint32_t channelBitMask, uint32_t attr)
44 {
45 // Check the arguments.
46 ASSERT((attr & ~(UDMA_ATTR_USEBURST | UDMA_ATTR_ALTSELECT | UDMA_ATTR_HIGH_PRIORITY | UDMA_ATTR_REQMASK)) == 0);
47
48 // Set the useburst bit for this channel if set in attr.
49 if (attr & UDMA_ATTR_USEBURST)
50 {
51 HWREG(DMA_BASE + DMA_O_SETBURST) = channelBitMask;
52 }
53
54 // Set the request mask bit for this channel, if set in attr.
55 if (attr & UDMA_ATTR_REQMASK)
56 {
57 HWREG(DMA_BASE + DMA_O_SETREQMASK) = channelBitMask;
58 }
59
60 // Set the alternate control select bit for this channel,
61 // if set in attr.
62 if (attr & UDMA_ATTR_ALTSELECT)
63 {
64 HWREG(DMA_BASE + DMA_O_SETCHNLPRIALT) = channelBitMask;
65 }
66
67 // Set the high priority bit for this channel, if set in attr.
68 if (attr & UDMA_ATTR_HIGH_PRIORITY)
69 {
70 HWREG(DMA_BASE + DMA_O_SETCHNLPRIORITY) = channelBitMask;
71 }
72 }
73
74 //*****************************************************************************
75 //
76 // Disables attributes of a uDMA channel
77 //
78 //*****************************************************************************
uDMADisableChannelAttribute(uint32_t channelBitMask,uint32_t attr)79 void uDMADisableChannelAttribute(uint32_t channelBitMask, uint32_t attr)
80 {
81 // Check the arguments.
82 ASSERT((attr & ~(UDMA_ATTR_USEBURST | UDMA_ATTR_ALTSELECT | UDMA_ATTR_HIGH_PRIORITY | UDMA_ATTR_REQMASK)) == 0);
83
84 // Clear the useburst bit for this channel if set in attr.
85 if (attr & UDMA_ATTR_USEBURST)
86 {
87 HWREG(DMA_BASE + DMA_O_CLEARBURST) = channelBitMask;
88 }
89
90 // Clear the request mask bit for this channel, if set in attr.
91 if (attr & UDMA_ATTR_REQMASK)
92 {
93 HWREG(DMA_BASE + DMA_O_CLEARREQMASK) = channelBitMask;
94 }
95
96 // Clear the alternate control select bit for this channel, if set in
97 // ululAttr.
98 if (attr & UDMA_ATTR_ALTSELECT)
99 {
100 HWREG(DMA_BASE + DMA_O_CLEARCHNLPRIALT) = channelBitMask;
101 }
102
103 // Clear the high priority bit for this channel, if set in attr.
104 if (attr & UDMA_ATTR_HIGH_PRIORITY)
105 {
106 HWREG(DMA_BASE + DMA_O_CLEARCHNLPRIORITY) = channelBitMask;
107 }
108 }
109
110 //*****************************************************************************
111 //
112 // Gets the enabled attributes of a uDMA channel
113 //
114 //*****************************************************************************
uDMAGetChannelAttribute(uint32_t channelBitMask)115 uint32_t uDMAGetChannelAttribute(uint32_t channelBitMask)
116 {
117 uint32_t attr = 0;
118
119 // Check to see if useburst bit is set for this channel.
120 if (HWREG(DMA_BASE + DMA_O_SETBURST) & (channelBitMask))
121 {
122 attr |= UDMA_ATTR_USEBURST;
123 }
124
125 // Check to see if the alternate control bit is set for this channel.
126 if (HWREG(DMA_BASE + DMA_O_SETCHNLPRIALT) & (channelBitMask))
127 {
128 attr |= UDMA_ATTR_ALTSELECT;
129 }
130
131 // Check to see if the high priority bit is set for this channel.
132 if (HWREG(DMA_BASE + DMA_O_SETCHNLPRIORITY) & (channelBitMask))
133 {
134 attr |= UDMA_ATTR_HIGH_PRIORITY;
135 }
136
137 // Check to see if the request mask bit is set for this channel.
138 if (HWREG(DMA_BASE + DMA_O_SETREQMASK) & (channelBitMask))
139 {
140 attr |= UDMA_ATTR_REQMASK;
141 }
142
143 // Return the configuration flags.
144 return (attr);
145 }
146
147 //*****************************************************************************
148 //
149 // Sets the control parameters for a uDMA channel control structure
150 //
151 //*****************************************************************************
uDMASetChannelControl(volatile uDMAControlTableEntry * pChannelControlStruct,uint32_t control)152 void uDMASetChannelControl(volatile uDMAControlTableEntry *pChannelControlStruct, uint32_t control)
153 {
154 // Check the arguments.
155 ASSERT(HWREG(DMA_BASE + DMA_O_CTRL) != 0);
156
157 // Get the current control word value and mask off the fields to be
158 // changed, then OR in the new settings.
159 pChannelControlStruct->control = ((pChannelControlStruct->control &
160 ~(UDMA_DST_INC_M | UDMA_SRC_INC_M | UDMA_SIZE_M | UDMA_ARB_M |
161 UDMA_NEXT_USEBURST)) |
162 control);
163 }
164
165 //*****************************************************************************
166 //
167 // Sets the transfer parameters for a uDMA channel control structure
168 //
169 //*****************************************************************************
uDMASetChannelTransfer(volatile uDMAControlTableEntry * pChannelControlStruct,uint32_t mode,void * pSrcAddr,void * pDstAddr,uint32_t transferSize)170 void uDMASetChannelTransfer(volatile uDMAControlTableEntry *pChannelControlStruct,
171 uint32_t mode,
172 void *pSrcAddr,
173 void *pDstAddr,
174 uint32_t transferSize)
175 {
176 uint32_t controlTableAddr;
177 uint32_t control;
178 uint32_t incValue;
179 uint32_t bufferBytes;
180 uint32_t channelStructIndex;
181
182 // Check the arguments.
183 ASSERT(HWREG(DMA_BASE + DMA_O_CTRL) != 0);
184 ASSERT(mode <= UDMA_MODE_PER_SCATTER_GATHER);
185 ASSERT((transferSize != 0) && (transferSize <= UDMA_XFER_SIZE_MAX));
186
187 // Get the base address of the control table.
188 controlTableAddr = HWREG(DMA_BASE + DMA_O_CTRL);
189
190 // Get the current control word value and mask off the mode and size
191 // fields.
192 control = (pChannelControlStruct->control & ~(UDMA_XFER_SIZE_M | UDMA_MODE_M));
193
194 // Evaluate if the received table is a primary or alternate control table.
195 // To do this, calculate the index of the received control table among the
196 // entire control table array. This is the offset between the received table
197 // and the base address of the control table, divided by the size of the single
198 // control table block (16B).
199 channelStructIndex = (((uint32_t)pChannelControlStruct) - controlTableAddr) >> 4;
200
201 // Adjust the mode if the alt control structure is selected.
202 if (channelStructIndex & UDMA_ALT_SELECT)
203 {
204 if ((mode == UDMA_MODE_MEM_SCATTER_GATHER) || (mode == UDMA_MODE_PER_SCATTER_GATHER))
205 {
206 mode |= UDMA_MODE_ALT_SELECT;
207 }
208 }
209
210 // Set the transfer size and mode in the control word (but don't write the
211 // control word yet as it could kick off a transfer).
212 control |= mode | ((transferSize - 1) << UDMA_XFER_SIZE_S);
213
214 // Get the address increment value for the source, from the control word.
215 incValue = (control & UDMA_SRC_INC_M);
216
217 // Compute the ending source address of the transfer. If the source
218 // increment is set to none, then the ending address is the same as the
219 // beginning.
220 if (incValue != UDMA_SRC_INC_NONE)
221 {
222 incValue = incValue >> UDMA_SRC_INC_S;
223 bufferBytes = transferSize << incValue;
224 pSrcAddr = (void *)((uint32_t)pSrcAddr + bufferBytes - (1 << incValue));
225 }
226
227 // Load the source ending address into the control block.
228 pChannelControlStruct->pSrcEndAddr = pSrcAddr;
229
230 // Get the address increment value for the destination, from the control
231 // word.
232 incValue = control & UDMA_DST_INC_M;
233
234 // Compute the ending destination address of the transfer. If the
235 // destination increment is set to none, then the ending address is the
236 // same as the beginning.
237 if (incValue != UDMA_DST_INC_NONE)
238 {
239 // There is a special case if this is setting up a scatter-gather
240 // transfer. The destination pointer needs to point to the end of
241 // the alternate structure for this channel instead of calculating
242 // the end of the buffer in the normal way.
243 if ((mode == UDMA_MODE_MEM_SCATTER_GATHER) || (mode == UDMA_MODE_PER_SCATTER_GATHER))
244 {
245 if (channelStructIndex & UDMA_ALT_SELECT)
246 {
247 pDstAddr = (void *)&(pChannelControlStruct->spare);
248 }
249 else
250 {
251 pDstAddr = (void *)&pChannelControlStruct[UDMA_ALT_SELECT].spare;
252 }
253 }
254 // Not a scatter-gather transfer, calculate end pointer normally.
255 else
256 {
257 incValue = incValue >> UDMA_DST_INC_S;
258 bufferBytes = transferSize << incValue;
259 pDstAddr = (void *)((uint32_t)pDstAddr + bufferBytes - 1);
260 }
261 }
262
263 // Load the destination ending address into the control block.
264 pChannelControlStruct->pDstEndAddr = pDstAddr;
265
266 // Write the new control word value.
267 pChannelControlStruct->control = control;
268 }
269
270 //*****************************************************************************
271 //
272 // Gets the current transfer size for a uDMA channel control structure
273 //
274 //*****************************************************************************
uDMAGetChannelSize(volatile uDMAControlTableEntry const * pChannelControlStruct)275 uint32_t uDMAGetChannelSize(volatile uDMAControlTableEntry const *pChannelControlStruct)
276 {
277 uint32_t control;
278
279 // Check the arguments.
280 ASSERT(HWREG(DMA_BASE + DMA_O_CTRL) != 0);
281
282 // Get the current control word value and mask off all but the size field
283 // and the mode field.
284 control = (pChannelControlStruct->control & (UDMA_XFER_SIZE_M | UDMA_MODE_M));
285
286 // If the size field and mode field are 0 then the transfer is finished
287 // and there are no more items to transfer.
288 if (control == 0)
289 {
290 return (0);
291 }
292
293 // Otherwise, if either the size field or more field is non-zero, then
294 // not all the items have been transferred.
295 else
296 {
297 // Shift the size field and add one, then return to user.
298 return ((control >> UDMA_XFER_SIZE_S) + 1);
299 }
300 }
301
302 //*****************************************************************************
303 //
304 // Gets the transfer mode for a uDMA channel control structure
305 //
306 //*****************************************************************************
uDMAGetChannelMode(volatile uDMAControlTableEntry const * pChannelControlStruct)307 uint32_t uDMAGetChannelMode(volatile uDMAControlTableEntry const *pChannelControlStruct)
308 {
309 uint32_t control;
310
311 // Check the arguments.
312 ASSERT(HWREG(DMA_BASE + DMA_O_CTRL) != 0);
313
314 // Get the current control word value and mask off all but the mode field.
315 control = (pChannelControlStruct->control & UDMA_MODE_M);
316
317 // Check if scatter/gather mode, and if so, mask off the alt bit.
318 if (((control & ~UDMA_MODE_ALT_SELECT) == UDMA_MODE_MEM_SCATTER_GATHER) ||
319 ((control & ~UDMA_MODE_ALT_SELECT) == UDMA_MODE_PER_SCATTER_GATHER))
320 {
321 control &= ~UDMA_MODE_ALT_SELECT;
322 }
323
324 // Return the mode to the caller.
325 return (control);
326 }
327