1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/drivers/device/MicoDMA.c Fri Aug 13 10:43:05 2010 +0100 1.3 @@ -0,0 +1,376 @@ 1.4 +/**************************************************************************** 1.5 +** 1.6 +** Name: MicoDMA.c 1.7 +** 1.8 +** Description: 1.9 +** Implements functions for manipulating LatticeMico32 DMA 1.10 +** 1.11 +** Revision: 3.0 1.12 +** 1.13 +** Disclaimer: 1.14 +** 1.15 +** This source code is intended as a design reference which 1.16 +** illustrates how these types of functions can be implemented. It 1.17 +** is the user's responsibility to verify their design for 1.18 +** consistency and functionality through the use of formal 1.19 +** verification methods. Lattice Semiconductor provides no warranty 1.20 +** regarding the use or functionality of this code. 1.21 +** 1.22 +** -------------------------------------------------------------------- 1.23 +** 1.24 +** Lattice Semiconductor Corporation 1.25 +** 5555 NE Moore Court 1.26 +** Hillsboro, OR 97214 1.27 +** U.S.A 1.28 +** 1.29 +** TEL: 1-800-Lattice (USA and Canada) 1.30 +** (503)268-8001 (other locations) 1.31 +** 1.32 +** web: http://www.latticesemi.com 1.33 +** email: techsupport@latticesemi.com 1.34 +** 1.35 +** -------------------------------------------------------------------------- 1.36 +** 1.37 +** Change History (Latest changes on top) 1.38 +** 1.39 +** Ver Date Description 1.40 +** -------------------------------------------------------------------------- 1.41 +** 1.42 +** 3.0 Mar-25-2008 Added Header 1.43 +** 1.44 +**--------------------------------------------------------------------------- 1.45 +*****************************************************************************/ 1.46 + 1.47 + 1.48 +#include "MicoDMA.h" 1.49 +#include "MicoDMAService.h" 1.50 +#include "MicoInterrupts.h" 1.51 + 1.52 + 1.53 +#ifdef __cplusplus 1.54 +extern "C" { 1.55 +#endif 1.56 + 1.57 +#define MICODMA_CTX_DMA_PAUSED (0x00000001) 1.58 + 1.59 + 1.60 +/* program a DMA operation based on the descriptor contents */ 1.61 +static void MicoDMA_Program(unsigned int DMABase, DMADesc_t *desc) 1.62 +{ 1.63 + volatile MicoDMA_t *dma = (volatile MicoDMA_t *)DMABase; 1.64 + 1.65 + /* program the source-address */ 1.66 + dma->sAddr = desc->sAddr; 1.67 + 1.68 + /* program the destination-address */ 1.69 + dma->dAddr = desc->dAddr; 1.70 + 1.71 + /* program length */ 1.72 + dma->len = desc->reserved; 1.73 + 1.74 + /* program type */ 1.75 + dma->control = desc->type; 1.76 + 1.77 + /* start DMA */ 1.78 + dma->status = (MICODMA_STATUS_IE|MICODMA_STATUS_START); 1.79 + 1.80 + /* all done */ 1.81 + return; 1.82 +} 1.83 + 1.84 + 1.85 +/* handles interrupts from the DMA device */ 1.86 +static void MicoDMA_ISR(unsigned int isrCtx, void *data) 1.87 +{ 1.88 + volatile unsigned int iStatus; 1.89 + DMADesc_t *desc; 1.90 + MicoDMACtx_t *ctx; 1.91 + volatile MicoDMA_t *dma; 1.92 + 1.93 + ctx = (MicoDMACtx_t *)data; 1.94 + dma = (volatile MicoDMA_t *)ctx->base; 1.95 + desc = (DMADesc_t *)ctx->pHead; 1.96 + 1.97 + 1.98 + /* read the status: this acknowledges device's interrupt */ 1.99 + iStatus = dma->status; 1.100 + 1.101 + /* if this is a spurious interrupt, ignore it */ 1.102 + if(desc == 0){ 1.103 + return; 1.104 + } 1.105 + 1.106 + /* set the head-descriptor's status accordingly */ 1.107 + desc->state = ((iStatus & MICODMA_STATUS_SUCCESS) == 0)?MICODMA_STATE_SUCCESS: 1.108 + MICODMA_STATE_ERROR; 1.109 + 1.110 + /* fetch the next descriptor */ 1.111 + if(desc->next == desc){ 1.112 + /* this was the last descriptor in the list */ 1.113 + ctx->pHead = 0; 1.114 + }else{ 1.115 + /* there are more descriptors queued up */ 1.116 + desc->prev->next = desc->next; 1.117 + desc->next->prev = desc->prev; 1.118 + ctx->pHead = (void *)desc->next; 1.119 + } 1.120 + 1.121 + /* invoke the callback routine if there's one specified */ 1.122 + if(desc->onCompletion) 1.123 + desc->onCompletion(desc, desc->state); 1.124 + 1.125 + 1.126 + /* 1.127 + * If pause-flag isn't set, go ahead and start 1.128 + * the next dma transaction 1.129 + */ 1.130 + if( ((ctx->flags & MICODMA_CTX_DMA_PAUSED)==0) && ctx->pHead) 1.131 + MicoDMA_Program(ctx->base, (DMADesc_t *)ctx->pHead); 1.132 + 1.133 + 1.134 + /* all done */ 1.135 + return; 1.136 +} 1.137 + 1.138 + 1.139 +#ifdef __cplusplus 1.140 +} 1.141 +#endif 1.142 + 1.143 + 1.144 +/* initialization routine */ 1.145 +void MicoDMAInit(MicoDMACtx_t *ctx) 1.146 +{ 1.147 + /* 1.148 + * Unfortunately, we cannot "stop" the DMA if it was already 1.149 + * running.. 1.150 + */ 1.151 + volatile int i; 1.152 + volatile MicoDMA_t *dma = (volatile MicoDMA_t *)ctx->base; 1.153 + 1.154 + 1.155 + i = dma->status; 1.156 + dma->status = 0; 1.157 + 1.158 + 1.159 + /* set to "no flags" */ 1.160 + ctx->flags = 0; 1.161 + 1.162 + 1.163 + /* compute max-length based on the length-register width */ 1.164 + if(ctx->maxLength > 0){ 1.165 + i = ctx->maxLength; 1.166 + ctx->maxLength = 1; 1.167 + do{ 1.168 + ctx->maxLength += ctx->maxLength; 1.169 + }while(--i > 0); 1.170 + ctx->maxLength--; 1.171 + } 1.172 + 1.173 + 1.174 + /* initialize descriptor to null */ 1.175 + ctx->pCurr = 0; 1.176 + ctx->pHead = 0; 1.177 + 1.178 + 1.179 + /* register ISR for this DMA */ 1.180 + MicoRegisterISR(ctx->irq, (void *)ctx, MicoDMA_ISR); 1.181 + 1.182 + 1.183 + /* Register this DMA as an available device for the lookup service */ 1.184 + ctx->lookupReg.name = ctx->name; 1.185 + ctx->lookupReg.deviceType = "DMADevice"; 1.186 + ctx->lookupReg.priv = ctx; 1.187 + MicoRegisterDevice( &(ctx->lookupReg) ); 1.188 + 1.189 + 1.190 + /* all done */ 1.191 + return; 1.192 +} 1.193 + 1.194 + 1.195 +/* queue a new descriptor */ 1.196 +unsigned int MicoDMAQueueRequest(MicoDMACtx_t *ctx, DMADesc_t *desc, DMACallback_t callback) 1.197 +{ 1.198 + int byteLength; 1.199 + DMADesc_t *pPrevTail, *pHead; 1.200 + 1.201 + 1.202 + /* make sure descriptor and context are valid */ 1.203 + if((ctx == 0) || (desc == 0)) 1.204 + return(MICODMA_ERR_INVALID_POINTERS); 1.205 + 1.206 + 1.207 + /* convert read/write length into byte-length */ 1.208 + byteLength = desc->length; 1.209 + if(desc->type & DMA_16BIT_TRANSFER){ 1.210 + byteLength += byteLength; /* left-shift by 1 to multiply by 2 for 16-bit transfer */ 1.211 + } else if(desc->type & DMA_32BIT_TRANSFER) { 1.212 + byteLength += byteLength; /* left-shift by 1 to multiply by 2 */ 1.213 + byteLength += byteLength; /* left-shift by 1 to multiply by 2, again */ 1.214 + } 1.215 + 1.216 + 1.217 + 1.218 + /* make sure length of transaction is okay */ 1.219 + if((byteLength > ctx->maxLength) || (byteLength == 0)) 1.220 + return(MICODMA_ERR_DESC_LEN_ERR); 1.221 + 1.222 + 1.223 + /* save the new byte-length */ 1.224 + desc->reserved = byteLength; 1.225 + 1.226 + 1.227 + /* prepare descriptor */ 1.228 + desc->state = MICODMA_STATE_PENDING; 1.229 + desc->onCompletion = callback; 1.230 + 1.231 + 1.232 + /* disable this DMA's interrupt */ 1.233 + MicoDisableInterrupt(ctx->irq); 1.234 + 1.235 + 1.236 + /* attach to this DMA's descriptor list */ 1.237 + if(ctx->pHead == 0){ 1.238 + ctx->pHead = (void *)desc; 1.239 + desc->next = desc; 1.240 + desc->prev = desc; 1.241 + 1.242 + /* 1.243 + * Since this is the first element in the list, 1.244 + * kick-off the DMA, if we're not paused 1.245 + */ 1.246 + if((ctx->flags & MICODMA_CTX_DMA_PAUSED)==0) 1.247 + MicoDMA_Program(ctx->base, desc); 1.248 + 1.249 + }else{ 1.250 + pHead = (DMADesc_t *)(ctx->pHead); 1.251 + pPrevTail = pHead->prev; 1.252 + 1.253 + pPrevTail->next = desc; 1.254 + pHead->prev = desc; 1.255 + desc->next = pHead; 1.256 + desc->prev = pPrevTail; 1.257 + } 1.258 + 1.259 + /* reenable this DMA's interrupts */ 1.260 + MicoEnableInterrupt(ctx->irq); 1.261 + 1.262 + return(0); 1.263 +} 1.264 + 1.265 + 1.266 +/* dequeue an existing descriptor */ 1.267 +unsigned int MicoDMADequeueRequest(MicoDMACtx_t *ctx, DMADesc_t *desc, unsigned int callback) 1.268 +{ 1.269 + DMADesc_t *pHead, *pTail; 1.270 + 1.271 + /* cannot dequeue a request if it is in a state other than pending */ 1.272 + if((ctx == 0) || (desc == 0) || (desc->next == 0) || (desc->prev == 0)) 1.273 + return(MICODMA_ERR_INVALID_POINTERS); 1.274 + 1.275 + 1.276 + /* disable interrupts */ 1.277 + MicoDisableInterrupt(ctx->irq); 1.278 + 1.279 + if(desc->state != MICODMA_STATE_PENDING){ 1.280 + 1.281 + /* cannot dequeue a request that isn't "pending" */ 1.282 + MicoEnableInterrupt(ctx->irq); 1.283 + return(MICODMA_ERR_DESCRIPTOR_NOT_PENDING); 1.284 + 1.285 + }else{ 1.286 + /* 1.287 + * mark this descriptor as non-pending and 1.288 + * remove it from the queue 1.289 + */ 1.290 + desc->state = MICODMA_STATE_ABORTED; 1.291 + if(desc->next != desc){ 1.292 + /* this isn't the only descriptor in the list */ 1.293 + pHead = (DMADesc_t *)ctx->pHead; 1.294 + pTail = pHead->prev; 1.295 + 1.296 + /* adjust pointers */ 1.297 + desc->prev->next = desc->next; 1.298 + desc->next->prev = desc->prev; 1.299 + 1.300 + /* if this was the head, readjust the head */ 1.301 + if(pHead == desc) 1.302 + pHead = desc->next; 1.303 + }else{ 1.304 + /* this was the only descriptor */ 1.305 + ctx->pHead = 0; 1.306 + } 1.307 + desc->prev = 0; 1.308 + desc->next = 0; 1.309 + 1.310 + /* call the callback with 'abort status' if requested */ 1.311 + if(callback != 0){ 1.312 + (desc->onCompletion)(desc, MICODMA_STATE_ABORTED); 1.313 + } 1.314 + 1.315 + /* reenable interrupt */ 1.316 + MicoEnableInterrupt(ctx->irq); 1.317 + 1.318 + /* all done successfully */ 1.319 + return(0); 1.320 + } 1.321 + 1.322 +} 1.323 + 1.324 + 1.325 +/* query status of a descriptor */ 1.326 +unsigned int MicoDMAGetState(DMADesc_t *desc) 1.327 +{ 1.328 + return(desc->state); 1.329 +} 1.330 + 1.331 + 1.332 +/* pause DMA operations (after completion of the currently active descr.) */ 1.333 +unsigned int MicoDMAPause(MicoDMACtx_t *ctx) 1.334 +{ 1.335 + if(ctx == 0) 1.336 + return(MICODMA_ERR_INVALID_POINTERS); 1.337 + 1.338 + /* disable interrupt for this DMA device */ 1.339 + MicoDisableInterrupt(ctx->irq); 1.340 + 1.341 + /* set the pause flag */ 1.342 + ctx->flags |= MICODMA_CTX_DMA_PAUSED; 1.343 + 1.344 + /* enable interrupts */ 1.345 + MicoEnableInterrupt(ctx->irq); 1.346 + 1.347 + return(0); 1.348 +} 1.349 + 1.350 + 1.351 + 1.352 +/* resume DMA operations */ 1.353 +unsigned int MicoDMAResume(MicoDMACtx_t *ctx) 1.354 +{ 1.355 + if(ctx == 0) 1.356 + return(MICODMA_ERR_INVALID_POINTERS); 1.357 + 1.358 + /* disable interrupt for the dma-device */ 1.359 + MicoDisableInterrupt(ctx->irq); 1.360 + 1.361 + if(ctx->flags & MICODMA_CTX_DMA_PAUSED){ 1.362 + /* reset the pause-flag */ 1.363 + ctx->flags &= ~(MICODMA_CTX_DMA_PAUSED); 1.364 + 1.365 + /* 1.366 + * If there are descriptors queued up, 1.367 + * kick-start the dma process 1.368 + */ 1.369 + if(ctx->pHead) 1.370 + MicoDMA_Program(ctx->base, (DMADesc_t *)ctx->pHead); 1.371 + 1.372 + } 1.373 + 1.374 + /* reenable interrupt for this dma device */ 1.375 + MicoEnableInterrupt(ctx->irq); 1.376 + 1.377 + return(0); 1.378 +} 1.379 +