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