embeddedsw/XilinxProcessorIPLib/drivers/axicdma/src/xaxicdma.c
Jagannadha Sutradharudu Teki 2c8f92039d embeddesw: Add initial code support
Added initial support Xilinx Embedded Software.

Signed-off-by: Jagannadha Sutradharudu Teki <jaganna@xilinx.com>
2014-06-24 16:45:01 +05:30

687 lines
20 KiB
C
Executable file

/******************************************************************************
*
* Copyright (C) 2010 - 2014 Xilinx, Inc. All rights reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* Use of the Software is limited solely to applications:
* (a) running on a Xilinx device, or
* (b) that interact with a Xilinx device through a bus or interconnect.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* XILINX CONSORTIUM BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF
* OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*
* Except as contained in this notice, the name of the Xilinx shall not be used
* in advertising or otherwise to promote the sale, use or other dealings in
* this Software without prior written authorization from Xilinx.
*
******************************************************************************/
/*****************************************************************************/
/**
* @file xaxicdma.c
*
* The implementation of the API of Xilinx CDMA engine.
*
* <pre>
* MODIFICATION HISTORY:
*
* Ver Who Date Changes
* ----- ---- -------- -------------------------------------------------------
* 1.00a jz 04/18/10 First release
* 2.01a rkv 01/25/11 Replaced with "\r\n" in place on "\n\r" in printf
* statements Changed XAxiCdma_CfgInitialize to use
* EffectiveAddress.
* 2.02a srt 01/18/13 Added support for Key Hole feature (CR: 687217).
* </pre>
*
*****************************************************************************/
#include "xaxicdma.h"
#include "xaxicdma_i.h"
/*****************************************************************************/
/**
* This function gets the status on error bits.
*
* @param InstancePtr is the driver instance we are working on
*
* @return The error bits in the status register. Zero indicates no errors.
*
* @note None.
*
*****************************************************************************/
u32 XAxiCdma_GetError(XAxiCdma *InstancePtr)
{
return (XAxiCdma_ReadReg(InstancePtr->BaseAddr, XAXICDMA_SR_OFFSET) &
XAXICDMA_SR_ERR_ALL_MASK);
}
/*****************************************************************************/
/**
* This function conducts hardware reset
*
* Current transfer will finish gracefully. However, all queued SG transfers
* that have not started will be flushed from the hardware.
*
* @param InstancePtr is the driver instance we are working on
*
* @return None
*
* @note None.
*
*****************************************************************************/
void XAxiCdma_Reset(XAxiCdma *InstancePtr)
{
XAxiCdma_WriteReg(InstancePtr->BaseAddr, XAXICDMA_CR_OFFSET,
XAXICDMA_CR_RESET_MASK);
/* Mark no outstanding transfers
*/
InstancePtr->SimpleNotDone = 0;
InstancePtr->SGWaiting = 0;
/* Reset will lose all interrupt handers
*/
InstancePtr->SgHandlerHead = 0;
InstancePtr->SgHandlerTail = 0;
/* Clear the call back function for simple transfers
*/
InstancePtr->SimpleCallBackFn = NULL;
InstancePtr->SimpleCallBackRef = NULL;
return;
}
/*****************************************************************************/
/**
* This function checks whether the hardware reset is done
*
* @param InstancePtr is the driver instance we are working on
*
* @return
* - 1 if the reset has finished successfully
* - 0 if the reset is not done
*
* @note None.
*
*****************************************************************************/
int XAxiCdma_ResetIsDone(XAxiCdma *InstancePtr)
{
/* If the reset bit is still high, then reset is not done
*/
return ((XAxiCdma_ReadReg(InstancePtr->BaseAddr, XAXICDMA_CR_OFFSET) &
XAXICDMA_CR_RESET_MASK) ? 0 : 1);
}
/*****************************************************************************/
/**
* This function initializes the driver. It should be called before any other
* function calls to the driver.
*
* It sets up the driver according to the hardware build. It resets the
* hardware at the end.
*
* @param InstancePtr is the driver instance that is working on
* @param CfgPtr is the pointer to the hardware configuration structure
* @param EffectiveAddr is the virtual address of the hardware instance.
* If address translation is not in use, please use the physical
* address
*
* @return
* - XST_SUCCESS for success
* - XST_INVALID_PARAM if word length is less than 4
* - XST_FAILURE for reset failure
*
* @note None.
*
*****************************************************************************/
int XAxiCdma_CfgInitialize(XAxiCdma *InstancePtr, XAxiCdma_Config *CfgPtr,
u32 EffectiveAddr)
{
u32 RegValue;
int TimeOut;
/* Mark the driver is not in working state yet
*/
InstancePtr->Initialized = 0;
InstancePtr->BaseAddr = EffectiveAddr;
InstancePtr->HasDRE = CfgPtr->HasDRE;
InstancePtr->IsLite = CfgPtr->IsLite;
InstancePtr->WordLength = ((unsigned int)CfgPtr->DataWidth) >> 3;
/* AXI CDMA supports 32 bits data width and up
*/
if (InstancePtr->WordLength < 4) {
xdbg_printf(XDBG_DEBUG_ERROR,
"Word length too short %d\r\n", InstancePtr->WordLength);
return XST_INVALID_PARAM;
}
RegValue = XAxiCdma_ReadReg(CfgPtr->BaseAddress, XAXICDMA_SR_OFFSET);
InstancePtr->SimpleOnlyBuild = !(RegValue & XAXICDMA_SR_SGINCLD_MASK);
/* Lite mode only supports data_width * burst_len
*
* Lite mode is ignored if SG mode is selected
*/
if (InstancePtr->SimpleOnlyBuild && CfgPtr->IsLite) {
InstancePtr->MaxTransLen = InstancePtr->WordLength *
CfgPtr->BurstLen;
}
else {
InstancePtr->MaxTransLen = XAXICDMA_MAX_TRANSFER_LEN;
}
TimeOut = XAXICDMA_RESET_LOOP_LIMIT;
/* Reset the hardware
*/
XAxiCdma_Reset(InstancePtr);
/* The hardware should be pretty quick on reset, the reset is only
* slow if there is an active large transfer
*/
while (TimeOut) {
if (XAxiCdma_ResetIsDone(InstancePtr)) {
break;
}
TimeOut -= 1;
}
if (!TimeOut) {
xdbg_printf(XDBG_DEBUG_ERROR, "Reset failed\r\n");
return XST_FAILURE;
}
/* Initialize the BD ring statistics, to prevent BD ring being used
* before being created
*/
InstancePtr->AllBdCnt = 0;
InstancePtr->FreeBdCnt = 0;
InstancePtr->HwBdCnt = 0;
InstancePtr->PreBdCnt = 0;
InstancePtr->PostBdCnt = 0;
/* Mark that the driver/engine is in working state now
*/
InstancePtr->Initialized = 1;
return XST_SUCCESS;
}
/*****************************************************************************/
/**
* This function checks whether the hardware is doing transfer
*
* @param InstancePtr is the driver instance we are working on
*
* @return
* - 1 if the hardware is doing a transfer
* - 0 if the hardware is idle
*
* @note None.
*
*****************************************************************************/
int XAxiCdma_IsBusy(XAxiCdma *InstancePtr)
{
/* If the idle bit is high, then hardware is not busy
*/
return ((XAxiCdma_ReadReg(InstancePtr->BaseAddr, XAXICDMA_SR_OFFSET) &
XAXICDMA_SR_IDLE_MASK) ? 0 : 1);
}
/*****************************************************************************/
/*
* Check whether the hardware is in simple mode
*
* @param InstancePtr is the driver instance we are working on
*
* @return
* - 1 if the hardware is in simple mode
* - 0 if the hardware is in SG mode
*
* @note None.
*
*****************************************************************************/
int XAxiCdma_IsSimpleMode(XAxiCdma *InstancePtr)
{
return ((XAxiCdma_ReadReg(InstancePtr->BaseAddr, XAXICDMA_CR_OFFSET) &
XAXICDMA_CR_SGMODE_MASK) ? 0 : 1);
}
/*****************************************************************************/
/**
* This function configures KeyHole Write/Read Feature
*
* @param InstancePtr is the driver instance we are working on
*
* @param Direction is WRITE/READ
* @Select Select is the option to enable (TRUE) or disable (FALSE).
*
* @return - XST_SUCCESS for success
* - XST_DEVICE_BUSY when transfer is in progress
* - XST_NO_FEATURE when not configured with feature
*
* @note None.
*
*****************************************************************************/
int XAxiCdma_SelectKeyHole(XAxiCdma *InstancePtr, u32 Direction, u32 Select)
{
u32 Value;
if (XAxiCdma_IsBusy(InstancePtr)) {
xdbg_printf(XDBG_DEBUG_ERROR,
"KeyHole: Transfer is in Progress\n\r");
return XST_DEVICE_BUSY;
}
Value = XAxiCdma_ReadReg(InstancePtr->BaseAddr,
XAXICDMA_CR_OFFSET);
if (Select) {
if (XPAR_AXICDMA_0_M_AXI_MAX_BURST_LEN == 16) {
if (Direction == XAXICDMA_KEYHOLE_WRITE)
Value |= XAXICDMA_CR_KHOLE_WR_MASK;
else
Value |= XAXICDMA_CR_KHOLE_RD_MASK;
} else {
xdbg_printf(XDBG_DEBUG_ERROR,
"KeyHole: Max Burst length should be 16\n\r");
return XST_NO_FEATURE;
}
}
else {
if (Direction == XAXICDMA_KEYHOLE_WRITE)
Value &= ~XAXICDMA_CR_KHOLE_WR_MASK;
else
Value &= ~XAXICDMA_CR_KHOLE_RD_MASK;
}
XAxiCdma_WriteReg(InstancePtr->BaseAddr,
XAXICDMA_CR_OFFSET, Value);
return XST_SUCCESS;
}
/*****************************************************************************/
/*
* Change the hardware mode
*
* If to switch to SG mode, check whether needs to setup the current BD
* pointer register.
*
* @param InstancePtr is the driver instance we are working on
* @param Mode is the mode to switch to.
*
* @return
* - XST_SUCCESS if mode switch is successful
* - XST_DEVICE_BUSY if the engine is busy, so cannot switch mode
* - XST_INVALID_PARAM if pass in invalid mode value
* - XST_FAILURE if:Hardware is simple mode only build
* Mode switch failed
*
* @note None.
*
*****************************************************************************/
int XAxiCdma_SwitchMode(XAxiCdma *InstancePtr, int Mode)
{
if (Mode == XAXICDMA_SIMPLE_MODE) {
if (XAxiCdma_IsSimpleMode(InstancePtr)) {
return XST_SUCCESS;
}
if (XAxiCdma_IsBusy(InstancePtr)) {
xdbg_printf(XDBG_DEBUG_ERROR,
"SwitchMode: engine is busy\r\n");
return XST_DEVICE_BUSY;
}
/* Keep the CDESC so that CDESC will be
* reloaded when switch to SG mode again
*
* We know CDESC is valid because the hardware can only
* be in SG mode if a SG transfer has been submitted.
*/
InstancePtr->BdaRestart = XAxiCdma_BdRingNext(InstancePtr,
XAxiCdma_BdRingGetCurrBd(InstancePtr));
/* Update the CR register to switch to simple mode
*/
XAxiCdma_WriteReg(InstancePtr->BaseAddr, XAXICDMA_CR_OFFSET,
(XAxiCdma_ReadReg(InstancePtr->BaseAddr, XAXICDMA_CR_OFFSET)
& ~XAXICDMA_CR_SGMODE_MASK));
/* Hardware mode switch is quick, should succeed right away
*/
if (XAxiCdma_IsSimpleMode(InstancePtr)) {
return XST_SUCCESS;
}
else {
return XST_FAILURE;
}
}
else if (Mode == XAXICDMA_SG_MODE) {
if (!XAxiCdma_IsSimpleMode(InstancePtr)) {
return XST_SUCCESS;
}
if (InstancePtr->SimpleOnlyBuild) {
xdbg_printf(XDBG_DEBUG_ERROR,
"SwitchMode: hardware simple mode only\r\n");
return XST_FAILURE;
}
if (XAxiCdma_IsBusy(InstancePtr)) {
xdbg_printf(XDBG_DEBUG_ERROR,
"SwitchMode: engine is busy\r\n");
return XST_DEVICE_BUSY;
}
/* Update the CR register to switch to SG mode
*/
XAxiCdma_WriteReg(InstancePtr->BaseAddr, XAXICDMA_CR_OFFSET,
(XAxiCdma_ReadReg(InstancePtr->BaseAddr, XAXICDMA_CR_OFFSET)
| XAXICDMA_CR_SGMODE_MASK));
/* Hardware mode switch is quick, should succeed right away
*/
if (!XAxiCdma_IsSimpleMode(InstancePtr)) {
/* Update the CDESC register, because the hardware is
* to start from the CDESC
*/
XAxiCdma_WriteReg(InstancePtr->BaseAddr,
XAXICDMA_CDESC_OFFSET,
(u32)InstancePtr->BdaRestart);
return XST_SUCCESS;
}
else {
return XST_FAILURE;
}
}
else { /* Invalid mode */
return XST_INVALID_PARAM;
}
}
/*****************************************************************************/
/**
* This function does one simple transfer submission
*
* It checks in the following sequence:
* - if engine is busy, cannot submit
* - if software is still handling the completion of the previous simple
* transfer, cannot submit
* - if engine is in SG mode and cannot switch to simple mode, cannot submit
*
* @param InstancePtr is the pointer to the driver instance
* @param SrcAddr is the address of the source buffer
* @param DstAddr is the address of the destination buffer
* @param Length is the length of the transfer
* @param SimpleCallBack is the callback function for the simple transfer
* @param CallBackRef is the callback reference pointer
*
* @return
* - XST_SUCCESS for success of submission
* - XST_FAILURE for submission failure, maybe caused by:
* Another simple transfer is still going
* . Another SG transfer is still going
* - XST_INVALID_PARAM if:
* Length out of valid range [1:8M]
* Or, address not aligned when DRE is not built in
*
* @note Only set the callback function if using interrupt to signal
* the completion.If used in polling mode, please set the callback
* function to be NULL.
*
*****************************************************************************/
int XAxiCdma_SimpleTransfer(XAxiCdma *InstancePtr, u32 SrcAddr, u32 DstAddr,
int Length, XAxiCdma_CallBackFn SimpleCallBack, void *CallBackRef)
{
u32 WordBits;
if ((Length < 1) || (Length > XAXICDMA_MAX_TRANSFER_LEN)) {
return XST_INVALID_PARAM;
}
WordBits = (u32)(InstancePtr->WordLength - 1);
if ((SrcAddr & WordBits) || (DstAddr & WordBits)) {
if (!InstancePtr->HasDRE) {
xdbg_printf(XDBG_DEBUG_ERROR,
"Unaligned transfer without DRE %x/%x\r\n",
(unsigned int)SrcAddr, (unsigned int)DstAddr);
return XST_INVALID_PARAM;
}
}
/* If the engine is doing transfer, cannot submit
*/
if (XAxiCdma_IsBusy(InstancePtr)) {
xdbg_printf(XDBG_DEBUG_ERROR, "Engine is busy\r\n");
return XST_FAILURE;
}
/* The driver is still handling the previous simple transfer
*/
if (InstancePtr->SimpleNotDone) {
xdbg_printf(XDBG_DEBUG_ERROR, "Simple ongoing\r\n");
return XST_FAILURE;
}
/* If the engine is in scatter gather mode, try switch to simple mode
*/
if (!XAxiCdma_IsSimpleMode(InstancePtr)) {
if (XAxiCdma_SwitchMode(InstancePtr, XAXICDMA_SIMPLE_MODE) !=
XST_SUCCESS) {
xdbg_printf(XDBG_DEBUG_ERROR,
"Cannot switch to simple mode\r\n");
return XST_FAILURE;
}
}
/* Setup the flag so that others will not step on us
*
* This flag is only set if callback function is used and if the
* system is in interrupt mode; otherwise, when the hardware is done
* with the transfer, the driver is done with the transfer
*/
if ((SimpleCallBack != NULL) ||
((XAxiCdma_IntrGetEnabled(InstancePtr) &
XAXICDMA_XR_IRQ_SIMPLE_ALL_MASK) != 0x0)) {
InstancePtr->SimpleNotDone = 1;
}
InstancePtr->SimpleCallBackFn = SimpleCallBack;
InstancePtr->SimpleCallBackRef = CallBackRef;
XAxiCdma_WriteReg(InstancePtr->BaseAddr, XAXICDMA_SRCADDR_OFFSET,
SrcAddr);
XAxiCdma_WriteReg(InstancePtr->BaseAddr, XAXICDMA_DSTADDR_OFFSET,
DstAddr);
/* Writing to the BTT register starts the transfer
*/
XAxiCdma_WriteReg(InstancePtr->BaseAddr, XAXICDMA_BTT_OFFSET,
Length);
return XST_SUCCESS;
}
/*****************************************************************************/
/**
* This function tries to set the interrupt coalescing threshold counter and
* the delay counter. If to set only one of the counters, set the value of
* the other counter to be XAXICDMA_COALESCE_NO_CHANGE.
*
* @param InstancePtr is the driver instance we are working on
* @param Counter is the coalescing threshold to set to, the valid range is
* 1 to XAXICDMA_COALESCE_MAX.
* @param Delay is the delay timeout counter to set to, the valid range is
* 0 to XAXICDMA_DELAY_MAX. Setting a value of 0 disables the delay
* interrupt.
*
* @return
* - XST_SUCCESS for success
* - XST_FAILURE if hardware is in invalid state, for example,
* reset failed
* - XST_INVALID_PARAM if one of the counters is not in the
* valid range
*
* @note None.
*
*****************************************************************************/
int XAxiCdma_SetCoalesce(XAxiCdma *InstancePtr, u32 Counter, u32 Delay)
{
u32 RegValue;
int NoChange;
NoChange = 1;
if (!InstancePtr->Initialized) {
return XST_FAILURE;
}
RegValue = XAxiCdma_ReadReg(InstancePtr->BaseAddr, XAXICDMA_CR_OFFSET);
if (Counter != XAXICDMA_COALESCE_NO_CHANGE) {
NoChange = 0;
if ((Counter < 1) || (Counter > XAXICDMA_COALESCE_MAX)) {
return XST_INVALID_PARAM;
}
RegValue = (RegValue & ~XAXICDMA_XR_COALESCE_MASK) |
(Counter << XAXICDMA_COALESCE_SHIFT);
}
if (Delay != XAXICDMA_COALESCE_NO_CHANGE) {
NoChange = 0;
if (Delay > XAXICDMA_DELAY_MAX) {
return XST_INVALID_PARAM;
}
RegValue = (RegValue & ~XAXICDMA_XR_DELAY_MASK) |
(Delay << XAXICDMA_DELAY_SHIFT);
}
if (!NoChange) {
XAxiCdma_WriteReg(InstancePtr->BaseAddr, XAXICDMA_CR_OFFSET,
RegValue);
}
return XST_SUCCESS;
}
/*****************************************************************************/
/**
* This function gets the current setting of the interrupt coalescing threshold
* counter and the delay counter.
*
* @param InstancePtr is the driver instance we are working on
* @param CounterPtr is the return value for the coalescing counter
* setting
* @param DelayPtr is the return value for the delay counter setting
*
* @return A zero coalescing threshold indicates invalid results
*
* @note None.
*
*****************************************************************************/
void XAxiCdma_GetCoalesce(XAxiCdma *InstancePtr, u32 *CounterPtr,
u32 *DelayPtr)
{
u32 RegValue;
if (!InstancePtr->Initialized) {
/* A zero coalescing threshold indicates invalid results
*/
*CounterPtr = 0;
*DelayPtr = 0;
return;
}
RegValue = XAxiCdma_ReadReg(InstancePtr->BaseAddr, XAXICDMA_CR_OFFSET);
*CounterPtr = (RegValue & XAXICDMA_XR_COALESCE_MASK)
>> XAXICDMA_COALESCE_SHIFT;
*DelayPtr = (RegValue & XAXICDMA_XR_DELAY_MASK)
>> XAXICDMA_DELAY_SHIFT;
return;
}
/*****************************************************************************/
/**
* This function dumps the registers of this DMA instance
*
* @param InstancePtr is the driver instance we are working on
*
* @return None
*
* @note None.
*
*****************************************************************************/
void XAxiCdma_DumpRegisters(XAxiCdma *InstancePtr)
{
u32 RegBase;
RegBase = InstancePtr->BaseAddr;
xil_printf("Dump registers:\r\n");
xil_printf("Control register: %x\r\n",
XAxiCdma_ReadReg(RegBase, XAXICDMA_CR_OFFSET));
xil_printf("Status register: %x\r\n",
XAxiCdma_ReadReg(RegBase, XAXICDMA_SR_OFFSET));
xil_printf("Current BD register: %x\r\n",
XAxiCdma_ReadReg(RegBase, XAXICDMA_CDESC_OFFSET));
xil_printf("Tail BD register: %x\r\n",
XAxiCdma_ReadReg(RegBase, XAXICDMA_TDESC_OFFSET));
xil_printf("Source Addr register: %x\r\n",
XAxiCdma_ReadReg(RegBase, XAXICDMA_SRCADDR_OFFSET));
xil_printf("Destination Addr register: %x\r\n",
XAxiCdma_ReadReg(RegBase, XAXICDMA_DSTADDR_OFFSET));
xil_printf("BTT register: %x\r\n",
XAxiCdma_ReadReg(RegBase, XAXICDMA_BTT_OFFSET));
return;
}