drivers/device/MicoDMA.c

changeset 0
11aef665a5d8
     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 +