diff options
author | Sebastian Huber <sebastian.huber@embedded-brains.de> | 2016-01-12 15:34:31 +0100 |
---|---|---|
committer | Sebastian Huber <sebastian.huber@embedded-brains.de> | 2016-01-19 08:36:20 +0100 |
commit | e1eeb883d82ce218c2a9c754795cb3c86ac0f36d (patch) | |
tree | 646a7c22772297094bb77303953eda0e71679dd9 /c/src/lib/libbsp/arm/atsam/libraries/libchip/source/twid.c | |
parent | bsps/arm: Copy vector table only if necessary (diff) | |
download | rtems-e1eeb883d82ce218c2a9c754795cb3c86ac0f36d.tar.bz2 |
bsp/atsam: Import SAM Software Package
Import selected files of the "SAM V71 / V70 / E70 / S70 Software
Package" obtained from the "SAMV71-XULT GNU Software Package 1.5".
Converted files via dos2unix before import.
Update #2529.
Diffstat (limited to 'c/src/lib/libbsp/arm/atsam/libraries/libchip/source/twid.c')
-rw-r--r-- | c/src/lib/libbsp/arm/atsam/libraries/libchip/source/twid.c | 761 |
1 files changed, 761 insertions, 0 deletions
diff --git a/c/src/lib/libbsp/arm/atsam/libraries/libchip/source/twid.c b/c/src/lib/libbsp/arm/atsam/libraries/libchip/source/twid.c new file mode 100644 index 0000000000..91196dfcef --- /dev/null +++ b/c/src/lib/libbsp/arm/atsam/libraries/libchip/source/twid.c @@ -0,0 +1,761 @@ +/* ---------------------------------------------------------------------------- */ +/* Atmel Microcontroller Software Support */ +/* SAM Software Package License */ +/* ---------------------------------------------------------------------------- */ +/* Copyright (c) 2015, Atmel Corporation */ +/* */ +/* All rights reserved. */ +/* */ +/* Redistribution and use in source and binary forms, with or without */ +/* modification, are permitted provided that the following condition is 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 "chip.h" + +#include <assert.h> + +/*---------------------------------------------------------------------------- + * Definition + *----------------------------------------------------------------------------*/ +#define TWITIMEOUTMAX 400 +static uint32_t dmaWriteChannel, dmaReadChannel; + +extern uint32_t twi_send_stop; + +/*---------------------------------------------------------------------------- + * Types + *----------------------------------------------------------------------------*/ + +/** TWI driver callback function.*/ +typedef void (*TwiCallback)(Async *); + +/** \brief TWI asynchronous transfer descriptor.*/ +typedef struct _AsyncTwi { + + /** Asynchronous transfer status. */ + volatile uint8_t status; + /** Callback function to invoke when transfer completes or fails.*/ + TwiCallback callback; + /** Pointer to the data buffer.*/ + uint8_t *pData; + /** Total number of bytes to transfer.*/ + uint32_t num; + /** Number of already transferred bytes.*/ + uint32_t transferred; + +} AsyncTwi; + +/** + * \brief Initializes a TWI DMA Read channel. + */ +static void TWID_DmaInitializeRead(TwihsDma *pTwiXdma) +{ + /* Allocate a XDMA channel, Read accesses into TWI_THR */ + dmaReadChannel = XDMAD_AllocateChannel(pTwiXdma->pTwiDma, pTwiXdma->Twi_id, + XDMAD_TRANSFER_MEMORY); + + if (dmaReadChannel == XDMAD_ALLOC_FAILED) + printf("-E- Can't allocate XDMA channel\n\r"); + + XDMAD_PrepareChannel(pTwiXdma->pTwiDma, dmaReadChannel); +} + +/** + * \brief Initializes a TWI DMA write channel. + */ +static void TWID_DmaInitializeWrite(TwihsDma *pTwiXdma) +{ + /* Allocate a XDMA channel, Write accesses into TWI_THR */ + dmaWriteChannel = XDMAD_AllocateChannel(pTwiXdma->pTwiDma, + XDMAD_TRANSFER_MEMORY, + pTwiXdma->Twi_id); + + if (dmaWriteChannel == XDMAD_ALLOC_FAILED) + printf("-E- Can't allocate XDMA channel\n\r"); + + XDMAD_PrepareChannel(pTwiXdma->pTwiDma, dmaWriteChannel); +} + +/** + * \brief Configure xDMA write linker list for TWI transfer. + */ +static uint8_t TWID_XdmaConfigureWrite(TwihsDma *pTwiXdma, uint8_t *buf, + uint32_t len) +{ + uint32_t xdmaCndc, Thr, xdmaInt; + sXdmadCfg xdmadTxCfg; + + Thr = (uint32_t) & (TWIHS0->TWIHS_THR); + + if (pTwiXdma->Twi_id == ID_TWIHS1) + Thr = (uint32_t) & (TWIHS1->TWIHS_THR); + + if (pTwiXdma->Twi_id == ID_TWIHS2) + Thr = (uint32_t) & (TWIHS2->TWIHS_THR); + + xdmadTxCfg.mbr_ubc = XDMA_UBC_NVIEW_NDV0 | + XDMA_UBC_NDE_FETCH_DIS | + XDMA_UBC_NSEN_UPDATED | len; + + xdmadTxCfg.mbr_sa = (uint32_t)buf; + xdmadTxCfg.mbr_da = Thr; + xdmadTxCfg.mbr_cfg = XDMAC_CC_TYPE_PER_TRAN | + XDMAC_CC_MBSIZE_SINGLE | + XDMAC_CC_DSYNC_MEM2PER | + XDMAC_CC_CSIZE_CHK_1 | + XDMAC_CC_DWIDTH_BYTE | + XDMAC_CC_SIF_AHB_IF1 | + XDMAC_CC_DIF_AHB_IF1 | + XDMAC_CC_SAM_INCREMENTED_AM | + XDMAC_CC_DAM_FIXED_AM | + XDMAC_CC_PERID(XDMAIF_Get_ChannelNumber( + pTwiXdma->Twi_id, XDMAD_TRANSFER_TX)); + + xdmadTxCfg.mbr_bc = 0; + xdmadTxCfg.mbr_sus = 0; + xdmadTxCfg.mbr_dus = 0; + xdmaCndc = 0; + + xdmaInt = (XDMAC_CIE_BIE | + XDMAC_CIE_RBIE | + XDMAC_CIE_WBIE); + + if (XDMAD_ConfigureTransfer(pTwiXdma->pTwiDma, dmaWriteChannel, + &xdmadTxCfg, xdmaCndc, 0, xdmaInt)) + return USARTD_ERROR; + + return 0; +} + + +/** + * \brief Configure xDMA read linker list for TWI transfer. + */ +static uint8_t TWID_XdmaConfigureRead(TwihsDma *pTwiXdma, uint8_t *buf, + uint32_t len) +{ + uint32_t xdmaCndc, Rhr, xdmaInt; + sXdmadCfg xdmadRxCfg; + + Rhr = (uint32_t) & (TWIHS0->TWIHS_RHR); + + if (pTwiXdma->Twi_id == ID_TWIHS1) + Rhr = (uint32_t) & (TWIHS1->TWIHS_RHR); + + if (pTwiXdma->Twi_id == ID_TWIHS2) + Rhr = (uint32_t) & (TWIHS2->TWIHS_RHR); + + xdmadRxCfg.mbr_ubc = XDMA_UBC_NVIEW_NDV0 | + XDMA_UBC_NDE_FETCH_DIS | + XDMA_UBC_NDEN_UPDATED | + len; + + xdmadRxCfg.mbr_da = (uint32_t)buf; + xdmadRxCfg.mbr_sa = Rhr; + + xdmadRxCfg.mbr_cfg = XDMAC_CC_TYPE_PER_TRAN | + XDMAC_CC_MBSIZE_SINGLE | + XDMAC_CC_DSYNC_PER2MEM | + XDMAC_CC_CSIZE_CHK_1 | + XDMAC_CC_DWIDTH_BYTE | + XDMAC_CC_SIF_AHB_IF1 | + XDMAC_CC_DIF_AHB_IF1 | + XDMAC_CC_SAM_FIXED_AM | + XDMAC_CC_DAM_INCREMENTED_AM | + XDMAC_CC_PERID(XDMAIF_Get_ChannelNumber( + pTwiXdma->Twi_id , XDMAD_TRANSFER_RX)); + + xdmadRxCfg.mbr_bc = 0; + xdmadRxCfg.mbr_sus = 0; + xdmadRxCfg.mbr_dus = 0; + xdmaCndc = 0; + xdmaInt = (XDMAC_CIE_BIE | + XDMAC_CIE_RBIE | + XDMAC_CIE_WBIE); + + if (XDMAD_ConfigureTransfer(pTwiXdma->pTwiDma, dmaReadChannel, + &xdmadRxCfg, xdmaCndc, 0, xdmaInt)) + return 1; + + return 0; +} + +/*---------------------------------------------------------------------------- + * Global functions + *----------------------------------------------------------------------------*/ + +/** + * \brief Returns 1 if the given transfer has ended; otherwise returns 0. + * \param pAsync Pointer to an Async instance. + */ +uint32_t ASYNC_IsFinished(Async *pAsync) +{ + return (pAsync->status != ASYNC_STATUS_PENDING); +} + +/** + * \brief Initializes a TWI driver instance, using the given TWI peripheral. + * \note The peripheral must have been initialized properly before calling this + * function. + * \param pTwid Pointer to the Twid instance to initialize. + * \param pTwi Pointer to the TWI peripheral to use. + */ +void TWID_Initialize(Twid *pTwid, Twihs *pTwi) +{ + TRACE_DEBUG("TWID_Initialize()\n\r"); + assert(pTwid != NULL); + assert(pTwi != NULL); + + /* Initialize driver. */ + pTwid->pTwi = pTwi; + pTwid->pTransfer = 0; +} + +/** + * \brief Interrupt handler for a TWI peripheral. Manages asynchronous transfer + * occurring on the bus. This function MUST be called by the interrupt service + * routine of the TWI peripheral if asynchronous read/write are needed. + * \param pTwid Pointer to a Twid instance. + */ +void TWID_Handler(Twid *pTwid) +{ + uint8_t status; + AsyncTwi *pTransfer; + Twihs *pTwi; + + assert(pTwid != NULL); + + pTransfer = (AsyncTwi *)pTwid->pTransfer; + assert(pTransfer != NULL); + pTwi = pTwid->pTwi; + assert(pTwi != NULL); + + /* Retrieve interrupt status */ + status = TWI_GetMaskedStatus(pTwi); + + /* Byte received */ + if (TWI_STATUS_RXRDY(status)) { + + pTransfer->pData[pTransfer->transferred] = TWI_ReadByte(pTwi); + pTransfer->transferred++; + + /* check for transfer finish */ + if (pTransfer->transferred == pTransfer->num) { + + TWI_DisableIt(pTwi, TWIHS_IDR_RXRDY); + TWI_EnableIt(pTwi, TWIHS_IER_TXCOMP); + } + /* Last byte? */ + else if (pTransfer->transferred == (pTransfer->num - 1)) + + TWI_Stop(pTwi); + } + /* Byte sent*/ + else if (TWI_STATUS_TXRDY(status)) { + + /* Transfer finished ? */ + if (pTransfer->transferred == pTransfer->num) { + + TWI_DisableIt(pTwi, TWIHS_IDR_TXRDY); + TWI_EnableIt(pTwi, TWIHS_IER_TXCOMP); + TWI_SendSTOPCondition(pTwi); + } + /* Bytes remaining */ + else { + + TWI_WriteByte(pTwi, pTransfer->pData[pTransfer->transferred]); + pTransfer->transferred++; + } + } + /* Transfer complete*/ + else if (TWI_STATUS_TXCOMP(status)) { + + TWI_DisableIt(pTwi, TWIHS_IDR_TXCOMP); + pTransfer->status = 0; + + if (pTransfer->callback) + pTransfer->callback((Async *) pTransfer); + + pTwid->pTransfer = 0; + } +} + +/** + * \brief Asynchronously reads data from a slave on the TWI bus. An optional + * callback function is triggered when the transfer is complete. + * \param pTwid Pointer to a Twid instance. + * \param address TWI slave address. + * \param iaddress Optional slave internal address. + * \param isize Internal address size in bytes. + * \param pData Data buffer for storing received bytes. + * \param num Number of bytes to read. + * \param pAsync Asynchronous transfer descriptor. + * \return 0 if the transfer has been started; otherwise returns a TWI error code. + */ +uint8_t TWID_Read( + Twid *pTwid, + uint8_t address, + uint32_t iaddress, + uint8_t isize, + uint8_t *pData, + uint32_t num, + Async *pAsync) +{ + Twihs *pTwi; + AsyncTwi *pTransfer; + uint32_t startTime; + assert(pTwid != NULL); + pTwi = pTwid->pTwi; + pTransfer = (AsyncTwi *) pTwid->pTransfer; + + assert((address & 0x80) == 0); + assert((iaddress & 0xFF000000) == 0); + assert(isize < 4); + + /* Check that no transfer is already pending*/ + if (pTransfer) { + + TRACE_ERROR("TWID_Read: A transfer is already pending\n\r"); + return TWID_ERROR_BUSY; + } + + /* In single data byte master read, the START and STOP must both be set */ + twi_send_stop = (num == 1) ? 1 : 0; + + /* Asynchronous transfer*/ + if (pAsync) { + + /* Update the transfer descriptor */ + pTwid->pTransfer = pAsync; + pTransfer = (AsyncTwi *) pAsync; + pTransfer->status = ASYNC_STATUS_PENDING; + pTransfer->pData = pData; + pTransfer->num = num; + pTransfer->transferred = 0; + + /* Enable read interrupt and start the transfer */ + TWI_EnableIt(pTwi, TWIHS_IER_RXRDY); + TWI_StartRead(pTwi, address, iaddress, isize); + } + /* Synchronous transfer*/ + else { + + /* Start read*/ + TWI_StartRead(pTwi, address, iaddress, isize); + + /* Read all bytes, setting STOP before the last byte*/ + while (num > 0) { + + /* Last byte ?*/ + if (num == 1) + TWI_Stop(pTwi); + + /* Wait for byte then read and store it*/ + startTime = GetTicks(); + + while (!TWI_ByteReceived(pTwi)) { + if ((GetDelayInTicks(startTime, GetTicks())) > TWITIMEOUTMAX) { + TRACE_ERROR("TWID Timeout BR\n\r"); + break; + } + } + + *pData++ = TWI_ReadByte(pTwi); + num--; + } + + /* Wait for transfer to be complete */ + startTime = GetTicks(); + + while (!TWI_TransferComplete(pTwi)) { + if ((GetDelayInTicks(startTime, GetTicks())) > TWITIMEOUTMAX) { + TRACE_ERROR("TWID Timeout TC\n\r"); + break; + } + } + } + + return 0; +} + +/** + * \brief Asynchronously sends data to a slave on the TWI bus. An optional + * callback function is invoked whenever the transfer is complete. + * \param pTwid Pointer to a Twid instance. + * \param address TWI slave address. + * \param iaddress Optional slave internal address. + * \param isize Number of internal address bytes. + * \param pData Data buffer for storing received bytes. + * \param num Data buffer to send. + * \param pAsync Asynchronous transfer descriptor. + * \return 0 if the transfer has been started; otherwise returns a TWI error code. + */ +uint8_t TWID_Write( + Twid *pTwid, + uint8_t address, + uint32_t iaddress, + uint8_t isize, + uint8_t *pData, + uint32_t num, + Async *pAsync) +{ + Twihs *pTwi = pTwid->pTwi; + uint32_t startTime; + AsyncTwi *pTransfer = (AsyncTwi *) pTwid->pTransfer; + + assert(pTwi != NULL); + assert((address & 0x80) == 0); + assert((iaddress & 0xFF000000) == 0); + assert(isize < 4); + + /* Check that no transfer is already pending */ + if (pTransfer) { + TRACE_ERROR("TWI_Write: A transfer is already pending\n\r"); + return TWID_ERROR_BUSY; + } + + /* Asynchronous transfer */ + if (pAsync) { + /* Update the transfer descriptor */ + pTwid->pTransfer = pAsync; + pTransfer = (AsyncTwi *) pAsync; + pTransfer->status = ASYNC_STATUS_PENDING; + pTransfer->pData = pData; + pTransfer->num = num; + pTransfer->transferred = 1; + + /* Enable write interrupt and start the transfer */ + TWI_StartWrite(pTwi, address, iaddress, isize, *pData); + TWI_EnableIt(pTwi, TWIHS_IER_TXRDY); + + } else { + /* Synchronous transfer*/ + // Start write + TWI_StartWrite(pTwi, address, iaddress, isize, *pData++); + num--; + + /* Send all bytes */ + while (num > 0) { + /* Wait before sending the next byte */ + startTime = GetTicks(); + + while (!TWI_ByteSent(pTwi)) { + if ((GetDelayInTicks(startTime, GetTicks())) > TWITIMEOUTMAX) { + TRACE_ERROR("TWID Timeout BS\n\r"); + break; + } + } + + TWI_WriteByte(pTwi, *pData++); + num--; + } + + /* Wait for actual end of transfer */ + startTime = GetTicks(); + /* Send a STOP condition */ + TWI_SendSTOPCondition(pTwi); + + while (!TWI_TransferComplete(pTwi)) { + if ((GetDelayInTicks(startTime, GetTicks())) > TWITIMEOUTMAX) { + TRACE_ERROR("TWID Timeout TC2\n\r"); + break; + } + } + } + + return 0; +} + +/** + * \brief Initializes a TWI driver instance, using the given TWI peripheral. + * \note The peripheral must have been initialized properly before calling this + * function. + * \param pTwid Pointer to the Twid instance to initialize. + * \param pTwi Pointer to the TWI peripheral to use. + */ +void TWID_DmaInitialize(TwihsDma *pTwidma, Twihs *pTwi, uint8_t bPolling) +{ + TRACE_DEBUG("TWID_Initialize()\n\r"); + assert(pTwidma != NULL); + + if ((unsigned int)pTwi == (unsigned int)TWIHS0) pTwidma->Twi_id = ID_TWIHS0; + + if ((unsigned int)pTwi == (unsigned int)TWIHS1) pTwidma->Twi_id = ID_TWIHS1; + + if ((unsigned int)pTwi == (unsigned int)TWIHS2) pTwidma->Twi_id = ID_TWIHS2; + + /* Initialize driver. */ + pTwidma->pTwid->pTwi = pTwi; + pTwidma->pTwid->pTransfer = 0; + + if (!bPolling) { + /* Enable XDMA interrupt and give it priority over any other peripheral + interrupt */ + NVIC_ClearPendingIRQ(XDMAC_IRQn); + NVIC_SetPriority(XDMAC_IRQn, 1); + NVIC_EnableIRQ(XDMAC_IRQn); + } + + /* Initialize XDMA driver instance with polling mode */ + XDMAD_Initialize(pTwidma->pTwiDma, bPolling); +} + +/** + * \brief Asynchronously reads data from a slave on the TWI bus. An optional + * callback function is triggered when the transfer is complete. + * \param pTwid Pointer to a Twid instance. + * \param address TWI slave address. + * \param iaddress Optional slave internal address. + * \param isize Internal address size in bytes. + * \param pData Data buffer for storing received bytes. + * \param num Number of bytes to read. + * \param pAsync Asynchronous transfer descriptor. + * \param TWI_ID TWI ID for TWI0, TWIHS1, TWIHS2. + * \return 0 if the transfer has been started; otherwise returns a TWI error code. + */ +uint8_t TWID_DmaRead( + TwihsDma *pTwiXdma, + uint8_t address, + uint32_t iaddress, + uint8_t isize, + uint8_t *pData, + uint32_t num, + Async *pAsync) +{ + Twihs *pTwi; + AsyncTwi *pTransfer; + uint32_t status, startTime; + + assert(pTwiXdma->pTwid != NULL); + pTwi = pTwiXdma->pTwid->pTwi; + pTransfer = (AsyncTwi *) pTwiXdma->pTwid->pTransfer; + + assert((address & 0x80) == 0); + assert((iaddress & 0xFF000000) == 0); + assert(isize < 4); + + /* Check that no transfer is already pending*/ + if (pTransfer) { + + TRACE_ERROR("TWID_Read: A transfer is already pending\n\r"); + return TWID_ERROR_BUSY; + } + + /* Asynchronous transfer*/ + if (pAsync) { + /* Update the transfer descriptor */ + pTwiXdma->pTwid->pTransfer = pAsync; + pTransfer = (AsyncTwi *) pAsync; + pTransfer->status = ASYNC_STATUS_PENDING; + pTransfer->pData = pData; + pTransfer->num = num; + pTransfer->transferred = 0; + + /* Enable read interrupt and start the transfer */ + TWI_EnableIt(pTwi, TWIHS_IER_RXRDY); + TWI_StartRead(pTwi, address, iaddress, isize); + } else { + /* Synchronous transfer*/ + TWID_DmaInitializeRead(pTwiXdma); + TWID_XdmaConfigureRead(pTwiXdma, pData, (num - 2)); + + /* Start read*/ + XDMAD_StartTransfer(pTwiXdma->pTwiDma, dmaReadChannel); + TWI_StartRead(pTwi, address, iaddress, isize); + + startTime = GetTicks(); + status = XDMAD_IsTransferDone(pTwiXdma->pTwiDma, dmaReadChannel); + + while (status != XDMAD_OK) { + status = XDMAD_IsTransferDone(pTwiXdma->pTwiDma, dmaReadChannel); + + if ((GetDelayInTicks(startTime, GetTicks())) > TWITIMEOUTMAX) { + TRACE_ERROR("TWID DMA not done\n\r"); + break; + } + } + + if (XDMAD_OK == status) + SCB_InvalidateDCache_by_Addr((uint32_t *)pData, (num - 2)); + + status = TWI_GetStatus(pTwi); + startTime = GetTicks(); + + while (!(status & TWIHS_SR_RXRDY)) { + status = TWI_GetStatus(pTwi); + + if ((GetDelayInTicks(startTime, GetTicks())) > TWITIMEOUTMAX) { + TRACE_ERROR("TWID DMA not done\n\r"); + break; + } + } + + TWI_Stop(pTwi); + + pData[num - 2] = TWI_ReadByte(pTwi); + status = TWI_GetStatus(pTwi); + startTime = GetTicks(); + + while (!(status & TWIHS_SR_RXRDY)) { + status = TWI_GetStatus(pTwi); + + if ((GetDelayInTicks(startTime, GetTicks())) > TWITIMEOUTMAX) { + TRACE_ERROR("TWID Timeout Read\n\r"); + break; + } + } + + pData[num - 1] = TWI_ReadByte(pTwi); + status = TWI_GetStatus(pTwi); + startTime = GetTicks(); + + while (!(status & TWIHS_SR_TXCOMP)) { + status = TWI_GetStatus(pTwi); + + if ((GetDelayInTicks(startTime, GetTicks())) > TWITIMEOUTMAX) { + TRACE_ERROR("TWID Timeout Read\n\r"); + break; + } + } + + XDMAD_StopTransfer(pTwiXdma->pTwiDma, dmaReadChannel); + XDMAD_FreeChannel(pTwiXdma->pTwiDma, dmaWriteChannel); + } + + return 0; +} + +/** + * \brief Asynchronously sends data to a slave on the TWI bus. An optional + * callback function is invoked whenever the transfer is complete. + * \param pTwid Pointer to a Twid instance. + * \param address TWI slave address. + * \param iaddress Optional slave internal address. + * \param isize Number of internal address bytes. + * \param pData Data buffer for storing received bytes. + * \param num Data buffer to send. + * \param pAsync Asynchronous transfer descriptor. + * \param TWI_ID TWIHS ID for TWIHS0, TWIHS1, TWIHS2. + * \return 0 if the transfer has been started; otherwise returns a TWI error code. + */ +uint8_t TWID_DmaWrite( + TwihsDma *pTwiXdma, + uint8_t address, + uint32_t iaddress, + uint8_t isize, + uint8_t *pData, + uint32_t num, + Async *pAsync) +{ + Twihs *pTwi = pTwiXdma->pTwid->pTwi; + AsyncTwi *pTransfer = (AsyncTwi *) pTwiXdma->pTwid->pTransfer; + uint32_t status, startTime; + //uint8_t singleTransfer = 0; + assert(pTwi != NULL); + assert((address & 0x80) == 0); + assert((iaddress & 0xFF000000) == 0); + assert(isize < 4); + + // if (num == 1) singleTransfer = 1; + /* Check that no transfer is already pending */ + if (pTransfer) { + + TRACE_ERROR("TWI_Write: A transfer is already pending\n\r"); + return TWID_ERROR_BUSY; + } + + /* Asynchronous transfer */ + if (pAsync) { + + /* Update the transfer descriptor */ + pTwiXdma->pTwid->pTransfer = pAsync; + pTransfer = (AsyncTwi *) pAsync; + pTransfer->status = ASYNC_STATUS_PENDING; + pTransfer->pData = pData; + pTransfer->num = num; + pTransfer->transferred = 1; + + /* Enable write interrupt and start the transfer */ + TWI_StartWrite(pTwi, address, iaddress, isize, *pData); + TWI_EnableIt(pTwi, TWIHS_IER_TXRDY); + } else { + /* Synchronous transfer*/ + TWID_DmaInitializeWrite(pTwiXdma); + TWID_XdmaConfigureWrite(pTwiXdma, pData, (num - 1)); + /* Set slave address and number of internal address bytes. */ + pTwi->TWIHS_MMR = 0; + pTwi->TWIHS_MMR = (isize << 8) | (address << 16); + + /* Set internal address bytes. */ + pTwi->TWIHS_IADR = 0; + pTwi->TWIHS_IADR = iaddress; + + // cache maintenance before starting DMA Xfr + SCB_CleanDCache_by_Addr((uint32_t *)pData, (num - 1)); + startTime = GetTicks(); + + XDMAD_StartTransfer(pTwiXdma->pTwiDma, dmaWriteChannel); + + while ((XDMAD_IsTransferDone(pTwiXdma->pTwiDma, dmaWriteChannel))) { + if ((GetDelayInTicks(startTime, GetTicks())) > TWITIMEOUTMAX) { + TRACE_ERROR("TWID DMA not done, Channel State is %d\n\r", + pTwiXdma->pTwiDma->XdmaChannels[dmaWriteChannel].state); + break; + } + } + + status = TWI_GetStatus(pTwi); + startTime = GetTicks(); + + while (!(status & TWIHS_SR_TXRDY)) { + status = TWI_GetStatus(pTwi); + + if ((GetDelayInTicks(startTime, GetTicks())) > TWITIMEOUTMAX) { + TRACE_ERROR("TWID Timeout TXRDY\n\r"); + break; + } + } + + /* Send a STOP condition */ + TWI_Stop(pTwi); + + TWI_WriteByte(pTwi, pData[num - 1]); + status = TWI_GetStatus(pTwi); + startTime = GetTicks(); + + while (!(status & TWIHS_SR_TXCOMP)) { + status = TWI_GetStatus(pTwi); + + if ((GetDelayInTicks(startTime, GetTicks())) > TWITIMEOUTMAX) { + TRACE_ERROR("TWID Timeout Write\n\r"); + break; + } + } + + XDMAD_StopTransfer(pTwiXdma->pTwiDma, dmaWriteChannel); + XDMAD_FreeChannel(pTwiXdma->pTwiDma, dmaWriteChannel); + + } + + return 0; +} |