From 044ad7c3987460ede48ff27afd6bdb0ca05a0432 Mon Sep 17 00:00:00 2001 From: Harald Welte Date: Mon, 4 Jul 2011 20:52:54 +0200 Subject: import at91lib from at91lib_20100901_softpack_1_9_v_1_0_svn_v15011 it's sad to see that atmel doesn't publish their svn repo or has a centralized location or even puts proper version/release info into the library itself --- drivers/dmad/dmad.c | 300 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 300 insertions(+) create mode 100644 drivers/dmad/dmad.c (limited to 'drivers/dmad/dmad.c') diff --git a/drivers/dmad/dmad.c b/drivers/dmad/dmad.c new file mode 100644 index 0000000..75187d0 --- /dev/null +++ b/drivers/dmad/dmad.c @@ -0,0 +1,300 @@ +/* ---------------------------------------------------------------------------- + * ATMEL Microcontroller Software Support + * ---------------------------------------------------------------------------- + * Copyright (c) 2008, Atmel Corporation + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * - Redistributions of source code must retain the above copyright notice, + * this list of conditions and the disclaimer below. + * + * Atmel's name may not be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * DISCLAIMER: THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE + * DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, + * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * ---------------------------------------------------------------------------- + */ + +//------------------------------------------------------------------------------ +// Headers +//------------------------------------------------------------------------------ + +#include "dmad.h" +#include +#include +#include +#include + +//------------------------------------------------------------------------------ +// Local types +//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ +/// DMA transfer descriptor. Tracks the status and parameters of a transfer +/// on the DMA bus. +//------------------------------------------------------------------------------ +typedef struct _DmaTransfer { + /// Buffer transfer status. + volatile unsigned char status; + /// Transfer buffer size in byte. + unsigned int bufSize; + /// Total transfer size to byte. + volatile unsigned int transferSize; + /// Optional callback function. + DmaCallback callback; +} DmaTransfer; + +//------------------------------------------------------------------------------ +/// DMAD driver structure. Monitors the status of transfers on all +/// DMA channels. +//------------------------------------------------------------------------------ +typedef struct _Dmad { + + /// List of transfers occuring on each channel. + DmaTransfer transfers[DMA_CHANNEL_NUM]; +} Dmad; + +//------------------------------------------------------------------------------ +// Local variables +//------------------------------------------------------------------------------ + +/// Global DMA transfer instance. +static Dmad dmad; + +//------------------------------------------------------------------------------ +// Local functions +//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ +/// This handler function must be called by the DMAC interrupt service routine. +/// Identifies which event was activated and calls the associated function. +//------------------------------------------------------------------------------ +void DMAD_Handler() +{ + unsigned int status; + unsigned char channel; + DmaTransfer *pTransfer; + status = DMA_GetStatus(); + // Check if the buffer transfer completed is set. + if(status & AT91C_BTC) + { + // Scan each channel status. + for(channel = 0; channel < DMA_CHANNEL_NUM; channel++) { + if(!(status & (DMA_BTC << channel))){ + continue; + } + + dmad.transfers[channel].transferSize -= dmad.transfers[channel].bufSize; + // if next buffer is to be the last buffer in the transfer, then clear the automatic mode bit. + if(dmad.transfers[channel].transferSize <= dmad.transfers[channel].bufSize) { + DMA_ClearAutoMode(channel); + } + // Transfer finished + if(dmad.transfers[channel].transferSize == 0) { + pTransfer = &(dmad.transfers[channel]); + pTransfer->callback(); + DMA_DisableIt(DMA_BTC << channel); + DMA_DisableChannel(channel); + } + else + { + // Write the KEEPON field to clear the STALL states. + DMA_KeeponChannel(channel); + } + } + } +} + +//------------------------------------------------------------------------------ +/// Initializes the DMA controller. +/// \param channel Particular channel number +/// \param defaultHandler Using the default dmad interrupt handler. +//------------------------------------------------------------------------------ +void DMAD_Initialize(unsigned char channel, unsigned char defaultHandler) +{ + unsigned int status; + unsigned int flag; + + // Enable peripheral clock +#if !defined(at91sam9rl64) + AT91C_BASE_PMC->PMC_PCER = 1 << AT91C_ID_HDMA; +#endif + + // Read the channel handler status to ensure the channel is a free channel. + status = DMA_GetChannelStatus(); + TRACE_INFO ("DMAD_Initialize channel %x \n\r", channel); + SANITY_CHECK(!(status & (1 << channel))); + // Clear any pending interrupts on the channel. + DMA_GetStatus(); + // Disble the channel. + DMA_DisableChannel(channel); + // Disable the interrupt + flag = 0xffffff; + DMA_DisableIt(flag); + // Enable DMA. + DMA_Enable(); + if(defaultHandler) + { + IRQ_ConfigureIT(AT91C_ID_HDMA, 0, DMAD_Handler); + IRQ_EnableIT(AT91C_ID_HDMA); + } + // Initialize transfer instance. + dmad.transfers[channel].transferSize = 0; +} + +//------------------------------------------------------------------------------ +/// Configure the DMA transfer buffer by giving transfer mode, it could be single +/// buffer or multi-buffer(LLI/auto-reload/contiguous buffers) with or without +/// Picture-In-Picture mode. +/// \param channel Particular channel number. +/// \param sourceTransferMode Source buffer transfer mode. +/// \param destTransferMode Destination buffer transfer mode. +/// \param lli Pointer to a DmaLinkList structure instance. +/// \param pip Pointer to a PictureInPicture structure. +//------------------------------------------------------------------------------ +unsigned char DMAD_Configure_Buffer(unsigned char channel, + unsigned char sourceTransferMode, + unsigned char destTransferMode, + DmaLinkList *lli, + PictureInPicture *pip) +{ + DmaTransfer *pTransfer = &(dmad.transfers[channel]); + // Check that no transfer is pending on the channel + if (pTransfer-> transferSize > 0 ) { + TRACE_ERROR("DAM transfer is already pending\n\r"); + return DMAD_ERROR_BUSY; + } + // Configure source transfer mode. + DMA_SetSourceBufferMode(channel, sourceTransferMode, 0); + + // Configure destination transfer mode. + DMA_SetDestBufferMode(channel, destTransferMode, 0); + + if(lli){ + DMA_SetDescriptorAddr(channel, (unsigned int)(&lli[0])); + } + else { + DMA_SetDescriptorAddr(channel, 0); + } + + if(pip){ + #if defined(AT91C_SRC_PIP) + // If source picture-in-picture mode is enabled, program the DMAC_SPIP. + if(pip->pipSourceBoundarySize){ + // If destination picture-in-picture mode is enabled, program the DMAC_DPIP. + DMA_SPIPconfiguration(channel, pip->pipSourceHoleSize, pip->pipSourceBoundarySize); + } + #endif + + #if defined(AT91C_DST_PIP) + if(pip->pipDestBoundarySize){ + DMA_DPIPconfiguration(channel, pip->pipDestHoleSize, pip->pipDestBoundarySize); + } + #endif + } + return 0; +} + +//------------------------------------------------------------------------------ +/// Configure the DMA transfer control infomation. +/// \param channel Particular channel number. +/// \param bufSize Buffer transfer size in byte. +/// \param sourceWidth Source transfer width. +/// \param destWidth Destination transfer width. +/// \param sourceAddress Destination transfer width. +/// \param destAddress Destination transfer width. +//------------------------------------------------------------------------------ +unsigned char DMAD_Configure_TransferController(unsigned char channel, + unsigned int bufSize, + unsigned char sourceWidth, + unsigned char destWidth, + unsigned int sourceAddress, + unsigned int destAddress) +{ + DmaTransfer *pTransfer = &(dmad.transfers[channel]); + // Check that no transfer is pending on the channel + if (pTransfer-> transferSize > 0 ) { + TRACE_ERROR("DAM transfer is already pending\n\r"); + return DMAD_ERROR_BUSY; + } + pTransfer->bufSize = bufSize; + + // Set up the transfer width and transfer size. + DMA_SetSourceBufferSize(channel, bufSize, sourceWidth, destWidth, 0); + + if(sourceAddress) { + // Write the starting source address. + DMA_SetSourceAddr(channel, sourceAddress); + } + if(destAddress){ + // Write the starting destination address. + DMA_SetDestinationAddr(channel, destAddress); + } + return 0; +} + +//------------------------------------------------------------------------------ +/// Starts buffer transfer on the given channel +/// \param channel Particular channel number. +/// \param size Total transfer size in byte. +/// \param callback Optional callback function. +/// \param polling Polling channel status enable. +//------------------------------------------------------------------------------ +unsigned char DMAD_BufferTransfer(unsigned char channel, + unsigned int size, + DmaCallback callback, + unsigned char polling) +{ + DmaTransfer *pTransfer = &(dmad.transfers[channel]); + // Check that no transfer is pending on the channel + if (pTransfer-> transferSize > 0 ) { + TRACE_ERROR("DAM transfer is already pending\n\r"); + return DMAD_ERROR_BUSY; + } + pTransfer->status = DMAD_ERROR_BUSY; + pTransfer->transferSize = size; + pTransfer->callback = callback; + + if(!polling){ + DMA_EnableIt(DMA_BTC << channel); + } + // Enable the channel. + DMA_EnableChannel(channel); + + if(polling){ + while ((DMA_GetChannelStatus() & (DMA_ENA << channel)) == (DMA_ENA << channel)); + if (pTransfer->callback) { + pTransfer->callback(); + } + pTransfer->transferSize = 0; + DMA_DisableChannel(channel); + } + return 0; +} + +//------------------------------------------------------------------------------ +/// Returns 1 if no transfer is currently pending on the given channel; +/// otherwise, returns 0. +/// \param channel Channel number. +//------------------------------------------------------------------------------ +unsigned char DMAD_IsFinished(unsigned char channel) +{ + SANITY_CHECK(channel <= DMA_CHANNEL_NUM); + if (dmad.transfers[channel].transferSize > 0) { + return 0; + } + else { + DMA_DisableChannel(channel); + return 1; + } +} -- cgit v1.2.3