📄 ixp425pcidma.c
字号:
/* ixp425PciDma.c - PCI DMA driver for the IXP425 *//* Copyright 2002 Wind River Systems, Inc. *//*modification history--------------------01a,05jun02,jb initial version...*//*DESCRIPTIONThis 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.hThe DMA controller has two unidirectional channels. PCI to AHB transfers areperformed on the PTA channel, while AHB to PCI transfers are performed on theATP channel.Each channel has two sets of registers which accept transfer requests. Theseregisters are provided to ensure maximum bandwidth utilisation. As soon as the transfer corresponding to the data written to one set of registers completesthe 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 STATUSatpTransferAdd (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 STATUSptaTransferAdd (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*/voidpciAtpIsr (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);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -