/** * 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; }
/** * 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; }
/** * This function gets the mask for the interrupts that are currently enabled * * @param InstancePtr is the driver instance we are working on * * @return The bit mask for the interrupts that are currently enabled * * @note None. * *****************************************************************************/ u32 XAxiCdma_IntrGetEnabled(XAxiCdma *InstancePtr) { return (XAxiCdma_ReadReg(InstancePtr->BaseAddr, XAXICDMA_CR_OFFSET) & XAXICDMA_XR_IRQ_ALL_MASK); }
/** * This function is the interrupt handler for the driver, it handles all the * interrupts. For the completion of a transfer that has a callback function, * the callback function is called. * * @param HandlerRef is a reference pointer passed to the interrupt * registration function. It will be a pointer to the driver * instance we are working on * * @return None * * @note If one transfer does not have all its submitted BDs completed * successfully,then a reset is needed to clean up the mess left * by that transfer.Otherwise, the wrong interrupt callback maybe * called for the following transfers. However, if you always use * the same interrupt callback for all the transfers, and you are * the only user of the DMA engine, then you do not have to worry * about this. *****************************************************************************/ void XAxiCdma_IntrHandler(void *HandlerRef) { XAxiCdma *InstancePtr; u32 Status; u32 Irq; u32 Error = 0x0; InstancePtr = (XAxiCdma *)HandlerRef; Status = XAxiCdma_ReadReg(InstancePtr->BaseAddr, XAXICDMA_SR_OFFSET); /* Check what interrupts have fired */ Irq = Status & XAXICDMA_XR_IRQ_ALL_MASK; if (Irq == 0x0) { xdbg_printf(XDBG_DEBUG_ERROR, "No interrupt for intr handler\r\n"); return; } /* Acknowledge the interrupt */ XAxiCdma_WriteReg(InstancePtr->BaseAddr, XAXICDMA_SR_OFFSET, Irq); /* Pass the interrupt and error status to the callback function * if the transfer has one */ /* If SimpleNotDone flag is set, then it is a simple transfer */ if (InstancePtr->SimpleNotDone) { if (InstancePtr->SimpleCallBackFn) { (InstancePtr->SimpleCallBackFn)( InstancePtr->SimpleCallBackRef, Irq, NULL); InstancePtr->SimpleCallBackFn = NULL; } InstancePtr->SimpleNotDone = 0; if (InstancePtr->SGWaiting) { XAxiCdma_BdRingStartTransfer(InstancePtr); } } else { /* SG transfer */ if (InstancePtr->SgHandlerHead != InstancePtr->SgHandlerTail) { int Tmp; XAxiCdma_IntrHandlerList Handler = InstancePtr->Handlers[InstancePtr->SgHandlerHead]; Tmp = Handler.NumBds; /* Caution: may have race condition here * * If an interrupt for another transfer happens * while in callback function, then the wrong callback * function may be called. */ Handler.CallBackFn(Handler.CallBackRef, Irq, &(Tmp)); InstancePtr->Handlers[InstancePtr->SgHandlerHead].NumBds = Tmp; /* Update the handler head if this transfer is done */ if (Tmp == 0) { Tmp = InstancePtr->SgHandlerHead + 1; if (Tmp == XAXICDMA_MAXIMUM_MAX_HANDLER) { Tmp = 0; } InstancePtr->SgHandlerHead = Tmp; } } else { xdbg_printf(XDBG_DEBUG_ERROR, "ERROR: SG transfer intr without handler\r\n"); } } /* If has error interrupt, hardware needs to be reset */ Error = Status & XAXICDMA_SR_ERR_ALL_MASK; if ((Irq & XAXICDMA_XR_IRQ_ERROR_MASK) && Error) { int TimeOut; TimeOut = XAXICDMA_RESET_LOOP_LIMIT; /* Need to reset the hardware to clear the errors */ XAxiCdma_Reset(InstancePtr); while (TimeOut) { if (XAxiCdma_ResetIsDone(InstancePtr)) { break; } TimeOut -= 1; } /* Reset failed */ if (!TimeOut) { /* Mark the driver/engine is not in working state */ InstancePtr->Initialized = 0; } /* In case of error, no further handling is needed * * User should check send/receive buffers to see what happened * as well as check the DMA engine registers */ return; } return; }
/** * 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); }
/* * 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_BdSetCurBdPtr(InstancePtr, (UINTPTR)InstancePtr->BdaRestart); return XST_SUCCESS; } else { return XST_FAILURE; } } else { /* Invalid mode */ return XST_INVALID_PARAM; } }
/* * 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 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. * *****************************************************************************/ u32 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; InstancePtr->AddrWidth = CfgPtr->AddrWidth; /* 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; }