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