ixp425PciDma.c
资源名称:ixp425BSP.rar [点击查看]
上传用户:luoyougen
上传日期:2008-05-12
资源大小:23136k
文件大小:20k
源码类别:
VxWorks
开发平台:
C/C++
- /* ixp425PciDma.c - PCI DMA driver for the IXP425 */
- /* Copyright 2002 Wind River Systems, Inc. */
- /*
- modification history
- --------------------
- 01a,05jun02,jb initial version...
- */
- /*
- DESCRIPTION
- This is device driver code for the Intel IXP425 PCI DMA unit.
- In order for the functionality defined herein to be available,
- INCLUDE_PCI and INCLUDE_PCI_DMA must be defined in config.h
- The DMA controller has two unidirectional channels. PCI to AHB transfers are
- performed on the PTA channel, while AHB to PCI transfers are performed on the
- ATP channel.
- Each channel has two sets of registers which accept transfer requests. These
- registers are provided to ensure maximum bandwidth utilisation. As soon as
- the transfer corresponding to the data written to one set of registers completes
- the transfer corresponding to the data in the second set commences.
- SEE ALSO:
- ixp425Pci.c
- */
- /*
- * system defined include files required.
- */
- #include "vxWorks.h"
- #include "config.h"
- #include "dllLib.h"
- #include "intLib.h"
- #include "netBufLib.h"
- /*
- * user defined include files required.
- */
- #include "ixp425Pci.h"
- #include "ixp425Pci_p.h"
- /*
- * Typedefs whose scope is limited to this file.
- */
- typedef struct pciDmaRequest
- {
- void *buffer;
- void *pciAddr;
- UINT32 length;
- PCICALLBACKFUNC callback; /* ISR context callback*/
- UINT32 parameter;
- } PCI_DMA_REQUEST;
- /*
- * Variable declarations global to this file only. Externs are followed by
- * static variables.
- */
- extern STATUS pciLibInitStatus;
- /*These descriptors represent the transfers that have been written to the
- transfer registers in the controller*/
- LOCAL PCI_DMA_REQUEST currAtp0;
- LOCAL PCI_DMA_REQUEST currAtp1;
- LOCAL PCI_DMA_REQUEST currPta0;
- LOCAL PCI_DMA_REQUEST currPta1;
- /*The following are variables used to implement circular queues for
- queueing DMA transfer requests
- The value of [atp|pta]RequestQueueSize is used herein to track the
- current state of the transfers.
- Value Meaning
- 0 No requests are active or queued, all requests
- have been processed and are complete
- 1 There is a single transfer currently executing,
- and one triplet of registers is vacant, we use
- freeRegs to track which set.
- 2 There is one DMA transfer executing and another has
- been set up and is pending in the other register triplet.
- 3 One DMA transfer is executing, another is pending in
- the second register triplet, while a third is pending on
- the queue
- 4 and above One DMA transfer is executing, another is pending in
- the second register triplet, while 2 or more are pending on
- the queue
- */
- LOCAL PCI_DMA_REQUEST atpRequestPool[NUM_REQ_POOL];
- LOCAL UINT32 atpRequestQueueHead = 0;
- LOCAL UINT32 atpRequestQueueTail = 0;
- LOCAL UINT32 atpRequestQueueSize = 0;
- LOCAL PCI_DMA_REQUEST ptaRequestPool[NUM_REQ_POOL];
- LOCAL UINT32 ptaRequestQueueHead = 0;
- LOCAL UINT32 ptaRequestQueueTail = 0;
- LOCAL UINT32 ptaRequestQueueSize = 0;
- /*
- * function prototypes.
- */
- void pciAtpIsr (int param);
- void pciPtaIsr (int param);
- /*
- * Function definition.
- */
- /**************************************************************************
- *
- * pciDmaInit - Initialize the PCI DMA controller
- *
- * This routine performes any required initialisation for the PCI DMA driver.
- * The PCI controller must be initialised before this function is called.
- *
- * RETURNS: OK on success, ERROR on failure
- */
- STATUS
- pciDmaInit (void)
- {
- UINT32 regval;
- if (pciLibInitStatus != OK)
- {
- return ERROR;
- }
- /*Clear any spurious completion indicators*/
- regval = PCI_DMACTRL_APDCEN | PCI_DMACTRL_APDC0 | PCI_DMACTRL_APDC1
- | PCI_DMACTRL_PADCEN | PCI_DMACTRL_PADC0 | PCI_DMACTRL_PADC1;
- REG_WRITE (PCI_CSR_BASE, PCI_DMACTRL_OFFSET, regval);
- /*Enable PTA and ATP interrupts */
- REG_READ (PCI_CSR_BASE, PCI_INTEN_OFFSET, regval);
- regval |= PCI_INTEN_APDC | PCI_INTEN_PADC;
- REG_WRITE (PCI_CSR_BASE, PCI_INTEN_OFFSET, regval);
- /*initialize request queues*/
- atpRequestQueueHead=0;
- atpRequestQueueTail=0;
- atpRequestQueueSize=0;
- ptaRequestQueueHead=0;
- ptaRequestQueueTail=0;
- ptaRequestQueueSize=0;
- return OK;
- }
- /*return the next available ATP transfer request from the ATP queue*/
- LOCAL PCI_DMA_REQUEST*
- atpTransferGet (void)
- {
- PCI_DMA_REQUEST* req;
- if (atpRequestQueueHead == atpRequestQueueTail)
- {
- return NULL;
- }
- req = &atpRequestPool[atpRequestQueueHead];
- atpRequestQueueHead++;
- atpRequestQueueHead %= NUM_REQ_POOL;
- return req;
- }
- /*queue an ATP transfer*/
- LOCAL STATUS
- atpTransferAdd (void *buffer,
- void *pciAddr,
- UINT32 length,
- PCICALLBACKFUNC callback,
- UINT32 parameter)
- {
- PCI_DMA_REQUEST* req;
- if (((atpRequestQueueTail+1) % NUM_REQ_POOL) == atpRequestQueueHead )
- {
- /*No space*/
- return ERROR;
- }
- else
- {
- req = &atpRequestPool[atpRequestQueueTail];
- req->buffer = buffer;
- req->pciAddr = pciAddr;
- req->length = length;
- req->callback = callback;
- req->parameter = parameter;
- atpRequestQueueTail++;
- atpRequestQueueTail %= NUM_REQ_POOL;
- return OK;
- }
- }
- /*return the next available PTA transfer request from the PTA queue*/
- LOCAL PCI_DMA_REQUEST*
- ptaTransferGet (void)
- {
- PCI_DMA_REQUEST* req;
- if (ptaRequestQueueHead == ptaRequestQueueTail)
- {
- return NULL;
- }
- req = &ptaRequestPool[ptaRequestQueueHead];
- ptaRequestQueueHead++;
- ptaRequestQueueHead %= NUM_REQ_POOL;
- return req;
- }
- /*queue a PTA transfer*/
- LOCAL STATUS
- ptaTransferAdd (void *buffer,
- void *pciAddr,
- UINT32 length,
- PCICALLBACKFUNC callback,
- UINT32 parameter)
- {
- PCI_DMA_REQUEST* req;
- if (((ptaRequestQueueTail+1) % NUM_REQ_POOL) == ptaRequestQueueHead )
- {
- /*No space*/
- return ERROR;
- }
- else
- {
- req = &ptaRequestPool[ptaRequestQueueTail];
- req->buffer = buffer;
- req->pciAddr = pciAddr;
- req->length = length;
- req->callback = callback;
- req->parameter = parameter;
- ptaRequestQueueTail++;
- ptaRequestQueueTail %= NUM_REQ_POOL;
- return OK;
- }
- }
- /**************************************************************************
- *
- * atpTransferInitiate - take a request from the queue and setup the registers
- *
- * This routine takes a DMA transfer request and sets up the
- * request by writing the relevant data to the control registers.
- *
- * RETURNS: N/A
- */
- LOCAL void
- atpTransferInitiate (PCI_DMA_REQUEST* req)
- {
- static UINT8 freeRegs=0;
- if(req == NULL)
- {
- return;
- }
- /*put the addresses and length in the free registers*/
- if (freeRegs==0)
- {
- currAtp0.callback = req->callback;
- currAtp0.parameter = req->parameter;
- REG_WRITE (PCI_CSR_BASE, PCI_ATPDMA0_AHBADDR, (UINT32)req->buffer);
- REG_WRITE (PCI_CSR_BASE, PCI_ATPDMA0_PCIADDR, (UINT32)req->pciAddr);
- REG_WRITE (PCI_CSR_BASE, PCI_ATPDMA0_LENADDR, (UINT32)(PCI_DMA_LEN_EN | req->length));
- /*we automatically set the freeRegs to the other set of registers, since
- we know that this function will not get invoked unless at least one set is free*/
- freeRegs=1;
- }
- else
- {
- currAtp1.callback = req->callback;
- currAtp1.parameter = req->parameter;
- REG_WRITE (PCI_CSR_BASE, PCI_ATPDMA1_AHBADDR, (UINT32)req->buffer);
- REG_WRITE (PCI_CSR_BASE, PCI_ATPDMA1_PCIADDR, (UINT32)req->pciAddr);
- REG_WRITE (PCI_CSR_BASE, PCI_ATPDMA1_LENADDR, (UINT32)(PCI_DMA_LEN_EN | req->length));
- freeRegs=0;
- }
- }
- /**************************************************************************
- *
- * ptaTransferInitiate - take a request from the queue and setup the registers
- *
- * This routine takes a DMA transfer request and sets up the
- * request by writing the relevant data to the control registers.
- *
- * RETURNS: void
- */
- LOCAL void
- ptaTransferInitiate (PCI_DMA_REQUEST* req)
- {
- static UINT8 freeRegs=0;
- if(req == NULL)
- {
- return;
- }
- /*put the addresses and length in the specified registers*/
- if (freeRegs==0)
- {
- currPta0.callback = req->callback;
- currPta0.parameter = req->parameter;
- REG_WRITE (PCI_CSR_BASE, PCI_PTADMA0_AHBADDR, (UINT32)req->buffer);
- REG_WRITE (PCI_CSR_BASE, PCI_PTADMA0_PCIADDR, (UINT32)req->pciAddr);
- REG_WRITE (PCI_CSR_BASE, PCI_PTADMA0_LENADDR, (UINT32)(PCI_DMA_LEN_EN | req->length));
- /*we automatically set the freeRegs to the other set of registers, since
- we know that this function will not get invoked unless at least one set is free*/
- freeRegs=1;
- }
- else
- {
- currPta1.callback = req->callback;
- currPta1.parameter = req->parameter;
- REG_WRITE (PCI_CSR_BASE, PCI_PTADMA1_AHBADDR, (UINT32)req->buffer);
- REG_WRITE (PCI_CSR_BASE, PCI_PTADMA1_PCIADDR, (UINT32)req->pciAddr);
- REG_WRITE (PCI_CSR_BASE, PCI_PTADMA1_LENADDR, (UINT32)(PCI_DMA_LEN_EN | req->length));
- freeRegs=0;
- }
- }
- /**************************************************************************
- *
- * pciAtpIsr - interrupt handler for ATP DMA channel
- *
- * This routine handles pci_atpdma_int interrupts. Such interrupts may indicate that
- * one or both of the register sets' corresponding ATP DMA transfers have completed.
- *
- * RETURNS: N/A
- */
- void
- pciAtpIsr (int param)
- {
- UINT32 regval;
- UINT32 newval;
- UINT32 key;
- PCI_DMA_REQUEST lastReq0;
- PCI_DMA_REQUEST lastReq1;
- PCI_DMA_REQUEST* req;
- UINT32 error0;
- UINT32 error1;
- if (atpRequestQueueSize == 0)
- {
- return;
- }
- key=intLock();
- /*Check what caused the interrupt*/
- REG_READ (PCI_CSR_BASE, PCI_DMACTRL_OFFSET, regval);
- error0=0;
- error1=0;
- lastReq0.callback=NULL;
- lastReq1.callback=NULL;
- if (regval & PCI_DMACTRL_APDC0)
- {
- /*register set 0 transfer complete, enable completion interrupts
- and clear the completion indicator*/
- newval = (regval & PCI_DMACTRL_APDCEN) | (regval & PCI_DMACTRL_PADCEN)
- | (PCI_DMACTRL_APDC0);
- REG_WRITE(PCI_CSR_BASE, PCI_DMACTRL_OFFSET, newval);
- lastReq0.callback = currAtp0.callback;
- lastReq0.parameter = currAtp0.parameter;
- error0 = regval & PCI_DMACTRL_APDE0;
- /*If there is a transfer queued, initiate it*/
- if (atpRequestQueueSize > 2)
- {
- req=atpTransferGet ();
- atpTransferInitiate (req);
- }
- atpRequestQueueSize--;
- }
- /* note that this is intentionally a separate "if", and should never be an
- "else if" since both transfers can have completed by the time we read
- the register*/
- if(regval & PCI_DMACTRL_APDC1)
- {
- /*register set 1 transfer complete, enable completion interrupts
- and clear the completion indicator*/
- newval = (regval & PCI_DMACTRL_APDCEN) | (regval & PCI_DMACTRL_PADCEN )
- | PCI_DMACTRL_APDC1;
- REG_WRITE(PCI_CSR_BASE, PCI_DMACTRL_OFFSET, newval);
- lastReq1.callback = currAtp1.callback;
- lastReq1.parameter = currAtp1.parameter;
- error1 = regval & PCI_DMACTRL_APDE1;
- /*If there is a transfer queued, initiate it*/
- if (atpRequestQueueSize > 2)
- {
- req = atpTransferGet ();
- atpTransferInitiate (req);
- }
- atpRequestQueueSize--;
- }
- /*This code would appear to be more at home within the body of the if
- blocks above, but the point of this control flow structure is to
- invoke the next atpTransferInitiate as soon as possible after
- getting the interrupt, so that we maximize utilisation of the DMA
- channel, so we obviously want to avoid invoking client code
- callbacks before initiating our transfer*/
- if (lastReq0.callback != NULL)
- {
- if (error0 != 0)
- {
- (lastReq0.callback)(PCI_ERROR, lastReq0.parameter);
- }
- else
- {
- (lastReq0.callback)(PCI_OK, lastReq0.parameter);
- }
- }
- if (lastReq1.callback != NULL)
- {
- if (error1 != 0)
- {
- (lastReq1.callback)(PCI_ERROR, lastReq1.parameter);
- }
- else
- {
- (lastReq1.callback)(PCI_OK, lastReq1.parameter);
- }
- }
- intUnlock(key);
- return;
- }
- /**************************************************************************
- *
- * pciPtaIsr - interrupt handler for PTA DMA channel
- *
- * This routine handles pci_ptadma_int interrupts. Such interrupts may indicate that
- * one or both of the register sets' corresponding PTA DMA transfers have completed.
- *
- * RETURNS: N/A
- */
- void
- pciPtaIsr (int param)
- {
- UINT32 regval;
- UINT32 newval;
- UINT32 key;
- PCI_DMA_REQUEST lastReq0;
- PCI_DMA_REQUEST lastReq1;
- PCI_DMA_REQUEST *req;
- UINT32 error0;
- UINT32 error1;
- if (ptaRequestQueueSize == 0)
- {
- return;
- }
- key=intLock();
- /*Check what caused the interrupt*/
- REG_READ (PCI_CSR_BASE, PCI_DMACTRL_OFFSET, regval);
- error0 = 0;
- error1 = 0;
- lastReq0.callback = NULL;
- lastReq1.callback = NULL;
- if (regval & PCI_DMACTRL_PADC0)
- {
- /*register set 0 transfer complete*/
- newval = (regval & PCI_DMACTRL_APDCEN) | (regval & PCI_DMACTRL_PADCEN)
- | (PCI_DMACTRL_PADC0);
- REG_WRITE(PCI_CSR_BASE, PCI_DMACTRL_OFFSET, newval);
- lastReq0.callback = currPta0.callback;
- lastReq0.parameter = currPta0.parameter;
- error0 = regval & PCI_DMACTRL_PADE0;
- /*If there is a transfer queued, initiate it*/
- if (ptaRequestQueueSize > 2)
- {
- req=ptaTransferGet ();
- ptaTransferInitiate (req);
- }
- ptaRequestQueueSize--;
- }
- /* note that this is intentionally a separate "if", and should never be an
- "else if" since both transfers can have completed by the time we read
- the register*/
- if(regval & PCI_DMACTRL_PADC1)
- {
- /*register set 1 transfer complete*/
- newval = (regval & PCI_DMACTRL_APDCEN) | (regval & PCI_DMACTRL_PADCEN)
- | (PCI_DMACTRL_PADC1);
- REG_WRITE(PCI_CSR_BASE, PCI_DMACTRL_OFFSET, newval);
- lastReq1.callback = currPta1.callback;
- lastReq1.parameter = currPta1.parameter;
- error1 = regval & PCI_DMACTRL_PADE1;
- /*If there is a transfer queued, initiate it*/
- if (ptaRequestQueueSize > 2)
- {
- req=ptaTransferGet ();
- ptaTransferInitiate (req);
- }
- ptaRequestQueueSize--;
- }
- /*This code would appear to be more at home within the body of the if
- blocks above, but the point of this control flow structure is to
- invoke the next ptaTransferInitiate as soon as possible after
- getting the interrupt, so that we maximize utilisation of the DMA
- channel, so we obviously want to avoid invoking client code
- callbacks before initiating our transfer*/
- if (lastReq0.callback != NULL)
- {
- if (error0 != 0)
- {
- (lastReq0.callback)(PCI_ERROR, lastReq0.parameter);
- }
- else
- {
- (lastReq0.callback)(PCI_OK, lastReq0.parameter);
- }
- }
- /*Again, both requests may have completed so this is not an "else if"*/
- if (lastReq1.callback != NULL)
- {
- if (error1 != 0)
- {
- (lastReq1.callback)(PCI_ERROR, lastReq1.parameter);
- }
- else
- {
- (lastReq1.callback)(PCI_OK, lastReq1.parameter);
- }
- }
- intUnlock(key);
- return;
- }
- /**********************************************
- *
- * pciDmaTransfer - transfer a block of data using DMA
- *
- * This routine sets up a DMA transfer. If possible, it initiates
- * the transfer immediately, otherwise it adds it to a queue.
- * The destination and source addresses must be 4-byte aligned.
- *
- * RETURNS: OK or ERROR if invalid parameters are supplied, or if the
- * relevant queue is full.
- */
- STATUS
- pciDmaTransfer (void *buffer,
- void *pciAddr,
- UINT32 direction,
- UINT32 length,
- PCICALLBACKFUNC callback,
- UINT32 parameter)
- {
- UINT32 key;
- PCI_DMA_REQUEST newTransfer;
- if (length > IXP425_PCI_DMA_MAX_LEN)
- {
- return ERROR;
- }
- /*Check alignment of addresses*/
- if (((UINT32)buffer & (UINT32)0x3) != 0)
- {
- return ERROR;
- }
- if (((UINT32)pciAddr & (UINT32)0x3) != 0)
- {
- return ERROR;
- }
- if (direction == IXP425_PCI_DMA_DIR_OUT)
- {
- /*AHB to PCI transfer*/
- key=intLock();
- if (atpRequestQueueSize < 2)
- {
- /*If the queue size is 0 or 1, this means that there is a free register
- set, so we can send the transfer directly to the registers*/
- newTransfer.buffer = buffer;
- newTransfer.pciAddr = pciAddr;
- newTransfer.length = length;
- newTransfer.callback = callback;
- newTransfer.parameter = parameter;
- atpTransferInitiate(&newTransfer);
- }
- else
- {
- /*Otherwise we must add the request to the queue*/
- if(atpTransferAdd(buffer, pciAddr, length, callback, parameter)!=OK)
- {
- intUnlock(key);
- return ERROR;
- }
- }
- /*regardless of whether we sent the transfer directly to the registers
- or queued it, the queue size has increased, since we consider the
- registers to be part of the queue*/
- atpRequestQueueSize++;
- intUnlock(key);
- }
- else if (direction == IXP425_PCI_DMA_DIR_IN)
- {
- /*PCI to AHB transfer*/
- key=intLock();
- if (ptaRequestQueueSize < 2)
- {
- newTransfer.buffer = buffer;
- newTransfer.pciAddr = pciAddr;
- newTransfer.length = length;
- newTransfer.callback = callback;
- newTransfer.parameter = parameter;
- ptaTransferInitiate(&newTransfer);
- }
- else
- {
- if(ptaTransferAdd(buffer, pciAddr, length, callback, parameter)!=OK)
- {
- intUnlock(key);
- return ERROR;
- }
- }
- ptaRequestQueueSize++;
- intUnlock(key);
- }
- else
- {
- return ERROR;
- }
- return OK;
- }
- LOCAL void
- pciOddByteTransfer (void *pciAddr,
- UINT8 *data,
- UINT32 direction,
- UINT32 length)
- {
- UINT32 i;
- for (i=0; i<length; i++)
- {
- if (direction == IXP425_PCI_DMA_DIR_OUT)
- {
- pciMemOutByte (pciAddr, *data);
- }
- else
- {
- pciMemInByte (pciAddr, (UINT8*)data);
- }
- pciAddr = (void*)((UINT8*)pciAddr+1);
- data = data + 1;
- }
- }
- LOCAL STATUS
- pciDmaSingleMbufTransfer (M_BLK *buffer,
- void *pciAddr,
- UINT32 direction,
- PCICALLBACKFUNC callback,
- UINT32 parameter)
- {
- UINT8 *data;
- UINT32 length;
- UINT32 lengthInLwords;
- UINT32 lengthDmaInBytes;
- UINT32 nonAlMaskAhb;
- UINT32 nonAlMaskPci;
- UINT32 numOdd;
- STATUS retval;
- data = buffer->mBlkHdr.mData;
- length = buffer->mBlkHdr.mLen;
- nonAlMaskAhb = (UINT32)data & (UINT32)0x3;
- nonAlMaskPci = (UINT32)pciAddr & (UINT32)0x3;
- /*If the AHB and PCI addresses are not equally misaligned we cannot do a DMA transfer*/
- if (nonAlMaskPci != nonAlMaskAhb)
- {
- return ERROR;
- }
- if (nonAlMaskAhb)
- {
- /*there are odd bytes at the start of the transfer*/
- numOdd = 4 - nonAlMaskAhb;
- pciOddByteTransfer (pciAddr, (UINT8*)data, direction, numOdd);
- pciAddr = (void*)((UINT8*)pciAddr+numOdd);
- data = data + numOdd;
- length -= numOdd;
- }
- /*now do the dma transfer*/
- lengthDmaInBytes = ((UINT32)length & ~((UINT32)0x3));
- lengthInLwords = lengthDmaInBytes >> 2;
- retval=pciDmaTransfer (data, pciAddr, direction, lengthInLwords,
- callback, parameter);
- if (retval != OK)
- {
- return retval;
- }
- /*transfer any odd bytes left over*/
- length = length - lengthDmaInBytes;
- if (length != 0)
- {
- data = data + lengthDmaInBytes;
- pciAddr = (void*)((UINT8*)pciAddr+lengthDmaInBytes);
- pciOddByteTransfer(pciAddr, (UINT8*)data, direction, length);
- }
- return OK;
- }
- /**********************************************
- *
- * pciDmaMbufTransfer - transfer an Mbuf or chain of Mbufs of data using DMA
- *
- * This routine transfers an mBuf's data across the PCI
- * bus using as much DMA as possible. If the data to be
- * transferred is not word aligned, the odd bytes at the
- * beginning and end of the block are transferred using
- * individual non-prefetch memory transactions, which will
- * have a negative impact on throughput.
- *
- * RETURNS: OK, or ERROR if invalid parameters are supplied, or if the
- * relevant queue is full.
- */
- STATUS
- pciDmaMbufTransfer (M_BLK* buffer,
- void *pciAddr,
- UINT32 direction,
- PCICALLBACKFUNC callback,
- UINT32 parameter)
- {
- M_BLK* currentMblk;
- STATUS retval;
- if ((direction != IXP425_PCI_DMA_DIR_IN) && (direction != IXP425_PCI_DMA_DIR_OUT))
- {
- return ERROR;
- }
- currentMblk = buffer;
- while (currentMblk != NULL)
- {
- retval=pciDmaSingleMbufTransfer (currentMblk, pciAddr, direction,
- callback, parameter);
- if (retval!=OK)
- {
- return retval;
- }
- pciAddr = (void*)((UINT8*)pciAddr+currentMblk->mBlkHdr.mLen);
- currentMblk = currentMblk->mBlkHdr.mNext;
- }
- return OK;
- }