blob: d368f42c6cc43c4907bb9b599b6cfff7e90836b9 [file] [log] [blame]
/* ----------------------------------------------------------------------------
* 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 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.
* ----------------------------------------------------------------------------
*/
/** \file */
/*----------------------------------------------------------------------------
* Headers
*----------------------------------------------------------------------------*/
#include "peripherals/twid_legacy.h"
#include "peripherals/xdmad.h"
#include "cortex-a/cp15.h"
#include "trace.h"
#include <assert.h>
/*----------------------------------------------------------------------------
* Definition
*----------------------------------------------------------------------------*/
#define TWITIMEOUTMAX 0xfffff
static struct _xdmad_cfg twi_dmaCfg;
static struct _xdmad_channel *dmaWriteChannel;
static struct _xdmad_channel *dmaReadChannel;
static struct _xdmad_desc_view1 dmaWriteLinkList[1];
static struct _xdmad_desc_view1 dmaReadLinkList[1];
/*----------------------------------------------------------------------------
* Types
*----------------------------------------------------------------------------*/
/** TWI driver callback function.*/
typedef void (*TwiCallback) (struct _async *);
/** \brief TWI asynchronous transfer descriptor.*/
struct _async_twi {
volatile uint32_t status; /** Asynchronous transfer status. */
TwiCallback callback; /** Callback function to invoke when transfer completes or fails.*/
uint8_t *pData; /** Pointer to the data buffer.*/
uint32_t num; /** Total number of bytes to transfer.*/
uint32_t transferred; /** Number of already transferred bytes.*/
};
//struct _async_twi async_twi ;
/*----------------------------------------------------------------------------
* Global functions
*----------------------------------------------------------------------------*/
/**
* \brief Initializes a TWI DMA Read channel.
*/
static void twid_dma_initialize_read(uint8_t TWI_ID)
{
/* Allocate a XDMA channel, Read accesses into TWI_THR */
dmaReadChannel = xdmad_allocate_channel(TWI_ID, XDMAD_PERIPH_MEMORY);
if (!dmaReadChannel) {
printf("-E- Can't allocate XDMA channel\n\r");
}
xdmad_prepare_channel(dmaReadChannel);
}
/**
* \brief Initializes a TWI DMA write channel.
*/
static void twid_dma_initialize_write(uint8_t TWI_ID)
{
/* Allocate a XDMA channel, Write accesses into TWI_THR */
dmaWriteChannel = xdmad_allocate_channel(XDMAD_PERIPH_MEMORY, TWI_ID);
if (!dmaWriteChannel) {
printf("-E- Can't allocate XDMA channel\n\r");
}
xdmad_prepare_channel(dmaWriteChannel);
}
/**
* \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(struct _twid* pTwid, Twi * pTwi)
{
trace_debug("twid_initialize()\n\r");
assert(pTwid != NULL);
assert(pTwi != NULL);
/* Initialize driver. */
pTwid->pTwi = pTwi;
pTwid->pTransfer = 0;
/* Initialize XDMA driver instance with polling mode */
// ************* need to be updated XDMAD_Initialize(&twi_dma, 1);
}
/**
* \brief Configure xDMA write linker list for TWI transfer.
*/
static void _xdma_configure_write(uint8_t * buf, uint32_t len, uint8_t twi_id)
{
uint32_t i;
uint32_t xdmaCndc, Thr;
Twi* twi = (Twi*)get_twi_addr_from_id(twi_id);
Thr = (uint32_t) & (TWI0->TWI_THR);
if (twi)
Thr = (uint32_t) & (twi->TWI_THR);
for (i = 0; i < 1; i++) {
dmaWriteLinkList[i].ublock_size = XDMA_UBC_NVIEW_NDV1
| ((i == len - 1) ? 0 : XDMA_UBC_NDE_FETCH_EN)
| len;
dmaWriteLinkList[i].src_addr = & buf[i];
dmaWriteLinkList[i].dest_addr = (void*)Thr;
if (i == len - 1)
dmaWriteLinkList[i].next_desc = 0;
else
dmaWriteLinkList[i].next_desc =
& dmaWriteLinkList[i + 1];
}
twi_dmaCfg.cfg.uint32_value = 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_IF0
| XDMAC_CC_DIF_AHB_IF1
| XDMAC_CC_SAM_INCREMENTED_AM
| XDMAC_CC_DAM_FIXED_AM
|
XDMAC_CC_PERID(get_peripheral_xdma_channel(twi_id, XDMAC0, true));
xdmaCndc =
XDMAC_CNDC_NDVIEW_NDV1 | XDMAC_CNDC_NDE_DSCR_FETCH_EN |
XDMAC_CNDC_NDSUP_SRC_PARAMS_UPDATED |
XDMAC_CNDC_NDDUP_DST_PARAMS_UNCHANGED;
cp15_coherent_dcache_for_dma((uint32_t) & dmaWriteLinkList,
((uint32_t) & dmaWriteLinkList +
sizeof (*dmaWriteLinkList) * len));
xdmad_configure_transfer(dmaWriteChannel, &twi_dmaCfg,
xdmaCndc, dmaWriteLinkList);
}
/**
* \brief Configure xDMA read linker list for TWI transfer.
*/
static void _xdma_configure_read(uint8_t * buf, uint32_t len, uint8_t twi_id)
{
uint32_t i;
uint32_t xdmaCndc, Rhr;
Twi* twi = get_twi_addr_from_id(twi_id);
Rhr = (uint32_t) & (TWI0->TWI_RHR);
if (twi) {
Rhr = twi->TWI_RHR;
}
/* if (twi_id == ID_TWI1) { */
/* Rhr = (uint32_t) & (TWI1->TWI_RHR); */
/* } */
/* if (twi_id == ID_TWI2) { */
/* Rhr = (uint32_t) & (TWI2->TWI_RHR); */
/* } */
for (i = 0; i < 1; i++) {
dmaReadLinkList[i].ublock_size = XDMA_UBC_NVIEW_NDV1
| ((i == len - 1) ? 0 : XDMA_UBC_NDE_FETCH_EN)
| len;
dmaReadLinkList[i].src_addr = (void*)Rhr;
dmaReadLinkList[i].dest_addr = & buf[i];
if (i == len - 1)
dmaReadLinkList[i].next_desc = 0;
else
dmaReadLinkList[i].next_desc =
& dmaReadLinkList[i + 1];
}
twi_dmaCfg.cfg.uint32_value = 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_IF0
| XDMAC_CC_SAM_FIXED_AM
| XDMAC_CC_DAM_INCREMENTED_AM
|
XDMAC_CC_PERID(get_peripheral_xdma_channel(twi_id, XDMAC0, false));
xdmaCndc =
XDMAC_CNDC_NDVIEW_NDV1 | XDMAC_CNDC_NDE_DSCR_FETCH_EN |
XDMAC_CNDC_NDSUP_SRC_PARAMS_UPDATED |
XDMAC_CNDC_NDDUP_DST_PARAMS_UPDATED;
cp15_coherent_dcache_for_dma((uint32_t) & dmaReadLinkList,
((uint32_t) & dmaReadLinkList +
sizeof (*dmaReadLinkList) * len));
xdmad_configure_transfer(dmaReadChannel, &twi_dmaCfg, xdmaCndc,
dmaReadLinkList);
}
/**
* \brief Interrupt handler for a TWI peripheral. Manages asynchronous transfer
* occuring 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(struct _twid* pTwid)
{
uint32_t status;
struct _async_twi* pTransfer;
Twi *pTwi;
assert(pTwid != NULL);
pTransfer = (struct _async_twi *) pTwid->pTransfer;
assert(pTransfer != NULL);
pTwi = pTwid->pTwi;
assert(pTwi != NULL);
/* Retrieve interrupt status */
status = twi_get_masked_status(pTwi);
/* Byte received */
if (TWI_STATUS_RXRDY(status)) {
pTransfer->pData[pTransfer->transferred] = twi_read_byte(pTwi);
pTransfer->transferred++;
/* check for transfer finish */
if (pTransfer->transferred == pTransfer->num) {
twi_enable_it(pTwi, TWI_IDR_RXRDY);
twi_enable_it(pTwi, TWI_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_enable_it(pTwi, TWI_IDR_TXRDY);
twi_enable_it(pTwi, TWI_IER_TXCOMP);
twi_send_stop_condition(pTwi);
}
/* Bytes remaining */
else {
twi_write_byte(pTwi,
pTransfer->pData[pTransfer->transferred]);
pTransfer->transferred++;
}
}
/* Transfer complete */
else if (TWI_STATUS_TXCOMP(status)) {
twi_enable_it(pTwi, TWI_IDR_TXCOMP);
pTransfer->status = 0;
if (pTransfer->callback) {
pTransfer->callback((struct _async *) (void *) 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(struct _twid* pTwid, uint8_t address, uint32_t iaddress,
uint8_t isize, uint8_t * pData, uint32_t num, struct _async * pAsync)
{
Twi *pTwi;
struct _async_twi* pTransfer;
uint32_t timeout = 0;
uint32_t i = 0;
uint32_t status;
assert(pTwid != NULL);
pTwi = pTwid->pTwi;
pTransfer = (struct _async_twi*) 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 */
pTwid->pTransfer = pAsync;
pTransfer = (struct _async_twi*) pAsync;
pTransfer->status = ASYNC_STATUS_PENDING;
pTransfer->pData = pData;
pTransfer->num = num;
pTransfer->transferred = 0;
/* Enable read interrupt and start the transfer */
twi_enable_it(pTwi, TWI_IER_RXRDY);
twi_start_read(pTwi, address, iaddress, isize);
}
/* Synchronous transfer */
else {
/* Start read */
twi_start_read(pTwi, address, iaddress, isize);
if (num != 1) {
status = twi_get_status(pTwi);
if (status & TWI_SR_NACK)
trace_error("TWID NACK error\n\r");
timeout = 0;
while (!(status & TWI_SR_RXRDY)
&& (++timeout < TWITIMEOUTMAX)) {
status = twi_get_status(pTwi);
//trace_error("TWID status %x\n\r",twi_get_status(pTwi));
}
pData[0] = twi_read_byte(pTwi);
for (i = 1; i < num - 1; i++) {
status = twi_get_status(pTwi);
if (status & TWI_SR_NACK)
trace_error("TWID NACK error\n\r");
timeout = 0;
while (!(status & TWI_SR_RXRDY)
&& (++timeout < TWITIMEOUTMAX)) {
status = twi_get_status(pTwi);
//trace_error("TWID status %x\n\r",twi_get_status(pTwi));
}
pData[i] = twi_read_byte(pTwi);
}
}
twi_stop(pTwi);
status = twi_get_status(pTwi);
if (status & TWI_SR_NACK)
trace_error("TWID NACK error\n\r");
timeout = 0;
while (!(status & TWI_SR_RXRDY) && (++timeout < TWITIMEOUTMAX)) {
status = twi_get_status(pTwi);
//trace_error("TWID status %x\n\r",twi_get_status(pTwi));
}
pData[i] = twi_read_byte(pTwi);
timeout = 0;
status = twi_get_status(pTwi);
while (!(status & TWI_SR_TXCOMP) && (++timeout < TWITIMEOUTMAX)) {
status = twi_get_status(pTwi);
//trace_error("TWID status %x\n\r",twi_get_status(pTwi));
}
}
return 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.
* \param twi_id TWI ID for TWI0, TWI1, TWI2.
* \return 0 if the transfer has been started; otherwise returns a TWI error code.
*/
uint8_t twid_dma_read(struct _twid* pTwid, uint8_t address, uint32_t iaddress,
uint8_t isize, uint8_t * pData, uint32_t num, struct _async * pAsync, uint8_t twi_id)
{
Twi *pTwi;
struct _async_twi* pTransfer;
uint32_t timeout = 0;
uint32_t status;
assert(pTwid != NULL);
pTwi = pTwid->pTwi;
pTransfer = (struct _async_twi*) 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 */
pTwid->pTransfer = pAsync;
pTransfer = (struct _async_twi*) pAsync;
pTransfer->status = ASYNC_STATUS_PENDING;
pTransfer->pData = pData;
pTransfer->num = num;
pTransfer->transferred = 0;
/* Enable read interrupt and start the transfer */
twi_enable_it(pTwi, TWI_IER_RXRDY);
twi_start_read(pTwi, address, iaddress, isize);
}
/* Synchronous transfer */
else {
twid_dma_initialize_read(twi_id);
_xdma_configure_read(pData, num, twi_id);
/* Start read */
xdmad_start_transfer(dmaReadChannel);
twi_start_read(pTwi, address, iaddress, isize);
while ((!xdmad_is_transfer_done(dmaReadChannel))
&& (++timeout < TWITIMEOUTMAX))
xdmad_poll();
xdmad_stop_transfer(dmaReadChannel);
status = twi_get_status(pTwi);
timeout = 0;
while (!(status & TWI_SR_RXRDY)
&& (++timeout < TWITIMEOUTMAX)) ;
twi_stop(pTwi);
twi_read_byte(pTwi);
status = twi_get_status(pTwi);
timeout = 0;
while (!(status & TWI_SR_RXRDY)
&& (++timeout < TWITIMEOUTMAX)) ;
twi_read_byte(pTwi);
status = twi_get_status(pTwi);
timeout = 0;
while (!(status & TWI_SR_TXCOMP)
&& (++timeout < TWITIMEOUTMAX)) ;
if (timeout == TWITIMEOUTMAX) {
trace_error("TWID Timeout Read\n\r");
}
xdmad_free_channel(dmaReadChannel);
}
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 TWI ID for TWI0, TWI1, TWI2.
* \return 0 if the transfer has been started; otherwise returns a TWI error code.
*/
uint8_t twid_dma_write(struct _twid* pTwid, uint8_t address, uint32_t iaddress,
uint8_t isize, uint8_t * pData, uint32_t num, struct _async * pAsync, uint8_t twi_id)
{
Twi *pTwi = pTwid->pTwi;
struct _async_twi* pTransfer;
uint32_t timeout = 0;
uint32_t status;
//uint8_t singleTransfer = 0;
assert(pTwi != NULL);
assert((address & 0x80) == 0);
assert((iaddress & 0xFF000000) == 0);
assert(isize < 4);
pTransfer = (struct _async_twi *) pTwid->pTransfer;
// 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 */
pTwid->pTransfer = pAsync;
pTransfer = (struct _async_twi*) pAsync;
pTransfer->status = ASYNC_STATUS_PENDING;
pTransfer->pData = pData;
pTransfer->num = num;
pTransfer->transferred = 1;
/* Enable write interrupt and start the transfer */
twi_start_write(pTwi, address, iaddress, isize, *pData);
twi_enable_it(pTwi, TWI_IER_TXRDY);
}
/* Synchronous transfer */
else {
cp15_coherent_dcache_for_dma((uint32_t) pData, (uint32_t) pData);
twid_dma_initialize_write(twi_id);
_xdma_configure_write(pData, num, twi_id);
/* Set slave address and number of internal address bytes. */
pTwi->TWI_MMR = 0;
pTwi->TWI_MMR = (isize << 8) | (address << 16);
/* Set internal address bytes. */
pTwi->TWI_IADR = 0;
pTwi->TWI_IADR = iaddress;
xdmad_start_transfer(dmaWriteChannel);
while (!xdmad_is_transfer_done(dmaWriteChannel))
xdmad_poll();
xdmad_stop_transfer(dmaWriteChannel);
status = twi_get_status(pTwi);
timeout = 0;
while (!(status & TWI_SR_TXRDY) && (timeout++ < TWITIMEOUTMAX)) {
status = twi_get_status(pTwi);
}
if (timeout == TWITIMEOUTMAX) {
trace_error("TWID Timeout TXRDY\n\r");
}
/* Send a STOP condition */
twi_stop(pTwi);
status = twi_get_status(pTwi);
timeout = 0;
while (!(status & TWI_SR_TXCOMP) && (++timeout < TWITIMEOUTMAX)) {
status = twi_get_status(pTwi);
}
if (timeout == TWITIMEOUTMAX) {
trace_error("TWID Timeout Write\n\r");
}
cp15_invalidate_dcache_for_dma((uint32_t) pData, (uint32_t) (pData));
xdmad_free_channel(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.
* \return 0 if the transfer has been started; otherwise returns a TWI error code.
*/
uint8_t twid_write(struct _twid* pTwid, uint8_t address, uint32_t iaddress,
uint8_t isize, uint8_t * pData, uint32_t num, struct _async * pAsync)
{
Twi *pTwi = pTwid->pTwi;
struct _async_twi* pTransfer;
uint32_t timeout = 0;
uint32_t status;
uint8_t singleTransfer = 0;
assert(pTwi != NULL);
assert((address & 0x80) == 0);
assert((iaddress & 0xFF000000) == 0);
assert(isize < 4);
pTransfer = (struct _async_twi *) pTwid->pTransfer;
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 */
pTwid->pTransfer = pAsync;
pTransfer = (struct _async_twi*) pAsync;
pTransfer->status = ASYNC_STATUS_PENDING;
pTransfer->pData = pData;
pTransfer->num = num;
pTransfer->transferred = 1;
/* Enable write interrupt and start the transfer */
twi_start_write(pTwi, address, iaddress, isize, *pData);
twi_enable_it(pTwi, TWI_IER_TXRDY);
}
/* Synchronous transfer */
else {
// Start write
twi_start_write(pTwi, address, iaddress, isize, *pData++);
num--;
if (singleTransfer) {
/* Send a STOP condition */
twi_send_stop_condition(pTwi);
}
status = twi_get_status(pTwi);
if (status & TWI_SR_NACK)
trace_error("TWID NACK error\n\r");
while (!(status & TWI_SR_TXRDY) && (timeout++ < TWITIMEOUTMAX)) {
status = twi_get_status(pTwi);
}
if (timeout == TWITIMEOUTMAX) {
trace_error("TWID Timeout BS\n\r");
}
timeout = 0;
/* Send all bytes */
while (num > 0) {
/* Wait before sending the next byte */
timeout = 0;
twi_write_byte(pTwi, *pData++);
status = twi_get_status(pTwi);
if (status & TWI_SR_NACK)
trace_error("TWID NACK error\n\r");
while (!(status & TWI_SR_TXRDY)
&& (timeout++ < TWITIMEOUTMAX)) {
status = twi_get_status(pTwi);
}
if (timeout == TWITIMEOUTMAX) {
trace_error("TWID Timeout BS\n\r");
}
num--;
}
/* Wait for actual end of transfer */
timeout = 0;
if (!singleTransfer) {
/* Send a STOP condition */
twi_send_stop_condition(pTwi);
}
while (!twi_is_transfer_complete(pTwi)
&& (++timeout < TWITIMEOUTMAX)) ;
if (timeout == TWITIMEOUTMAX) {
trace_error("TWID Timeout TC2\n\r");
}
}
return 0;
}