added prescaler for 16 bit pwm in LPC1347 target

Fork of mbed-dev by mbed official

Revision:
144:ef7eb2e8f9f7
Parent:
50:a417edff4437
--- a/targets/hal/TARGET_Silicon_Labs/TARGET_EFM32/emlib/src/em_i2c.c	Tue Aug 02 14:07:36 2016 +0000
+++ b/targets/hal/TARGET_Silicon_Labs/TARGET_EFM32/emlib/src/em_i2c.c	Fri Sep 02 15:07:44 2016 +0100
@@ -1,874 +1,874 @@
-/***************************************************************************//**
- * @file em_i2c.c
- * @brief Inter-integrated Circuit (I2C) Peripheral API
- * @version 4.2.1
- *******************************************************************************
- * @section License
- * <b>(C) Copyright 2015 Silicon Labs, http://www.silabs.com</b>
- *******************************************************************************
- *
- * Permission is granted to anyone to use this software for any purpose,
- * including commercial applications, and to alter it and redistribute it
- * freely, subject to the following restrictions:
- *
- * 1. The origin of this software must not be misrepresented; you must not
- *    claim that you wrote the original software.
- * 2. Altered source versions must be plainly marked as such, and must not be
- *    misrepresented as being the original software.
- * 3. This notice may not be removed or altered from any source distribution.
- *
- * DISCLAIMER OF WARRANTY/LIMITATION OF REMEDIES: Silicon Labs has no
- * obligation to support this Software. Silicon Labs is providing the
- * Software "AS IS", with no express or implied warranties of any kind,
- * including, but not limited to, any implied warranties of merchantability
- * or fitness for any particular purpose or warranties against infringement
- * of any proprietary rights of a third party.
- *
- * Silicon Labs will not be liable for any consequential, incidental, or
- * special damages, or any other relief, or for any claim by any third party,
- * arising from your use of this Software.
- *
- ******************************************************************************/
-
-#include "em_i2c.h"
-#if defined(I2C_COUNT) && (I2C_COUNT > 0)
-
-#include "em_cmu.h"
-#include "em_bus.h"
-#include "em_assert.h"
-
- #include <limits.h>
-
-/***************************************************************************//**
- * @addtogroup EM_Library
- * @{
- ******************************************************************************/
-
-/***************************************************************************//**
- * @addtogroup I2C
- * @brief Inter-integrated Circuit (I2C) Peripheral API
- * @{
- ******************************************************************************/
-
-/*******************************************************************************
- *******************************   DEFINES   ***********************************
- ******************************************************************************/
-
-/** @cond DO_NOT_INCLUDE_WITH_DOXYGEN */
-
-/** Validation of I2C register block pointer reference for assert statements. */
-#if (I2C_COUNT == 1)
-#define I2C_REF_VALID(ref)    ((ref) == I2C0)
-#elif (I2C_COUNT == 2)
-#define I2C_REF_VALID(ref)    ((ref == I2C0) || (ref == I2C1))
-#elif (I2C_COUNT == 3)
-#define I2C_REF_VALID(ref)    ((ref == I2C0) || (ref == I2C1)|| (ref == I2C2))
-#endif
-
-/** Error flags indicating I2C transfer has failed somehow. */
-/* Notice that I2C_IF_TXOF (transmit overflow) is not really possible with */
-/* this SW supporting master mode. Likewise for I2C_IF_RXUF (receive underflow) */
-/* RXUF is only likely to occur with this SW if using a debugger peeking into */
-/* RXDATA register. Thus, we ignore those types of fault. */
-#define I2C_IF_ERRORS    (I2C_IF_BUSERR | I2C_IF_ARBLOST)
-
-/* Max I2C transmission rate constant  */
-#if defined( _SILICON_LABS_32B_PLATFORM_1 )
-#define I2C_CR_MAX       4
-#elif defined( _SILICON_LABS_32B_PLATFORM_2 )
-#define I2C_CR_MAX       8
-#else
-#warning "Max I2C transmission rate constant is not defined"
-#endif
-
-/** @endcond */
-
-/*******************************************************************************
- ********************************   ENUMS   ************************************
- ******************************************************************************/
-
-/** @cond DO_NOT_INCLUDE_WITH_DOXYGEN */
-
-/** Master mode transfer states. */
-typedef enum
-{
-  i2cStateStartAddrSend,       /**< Send start + (first part of) address. */
-  i2cStateAddrWFAckNack,       /**< Wait for ACK/NACK on (first part of) address. */
-  i2cStateAddrWF2ndAckNack,    /**< Wait for ACK/NACK on second part of 10 bit address. */
-  i2cStateRStartAddrSend,      /**< Send repeated start + (first part of) address. */
-  i2cStateRAddrWFAckNack,      /**< Wait for ACK/NACK on address sent after repeated start. */
-  i2cStateDataSend,            /**< Send data. */
-  i2cStateDataWFAckNack,       /**< Wait for ACK/NACK on data sent. */
-  i2cStateWFData,              /**< Wait for data. */
-  i2cStateWFStopSent,          /**< Wait for STOP to have been transmitted. */
-  i2cStateDone                 /**< Transfer completed successfully. */
-} I2C_TransferState_TypeDef;
-
-/** @endcond */
-
-/*******************************************************************************
- *******************************   STRUCTS   ***********************************
- ******************************************************************************/
-
-/** @cond DO_NOT_INCLUDE_WITH_DOXYGEN */
-
-/** Structure used to store state information on an ongoing master mode transfer. */
-typedef struct
-{
-  /** Current state. */
-  I2C_TransferState_TypeDef  state;
-
-  /** Result return code. */
-  I2C_TransferReturn_TypeDef result;
-
-  /** Offset in current sequence buffer. */
-  uint16_t                   offset;
-
-  /* Index to current sequence buffer in use. */
-  uint8_t                    bufIndx;
-
-  /** Reference to I2C transfer sequence definition provided by user. */
-  I2C_TransferSeq_TypeDef    *seq;
-} I2C_Transfer_TypeDef;
-
-/** @endcond */
-
-/*******************************************************************************
- *****************************   LOCAL DATA   *******^**************************
- ******************************************************************************/
-
-/** @cond DO_NOT_INCLUDE_WITH_DOXYGEN */
-
-/**
- * Lookup table for Nlow + Nhigh setting defined by CLHR. Set undefined
- * index (0x3) to reflect default setting just in case.
- */
-static const uint8_t i2cNSum[] = { 4 + 4, 6 + 3, 11 + 6, 4 + 4 };
-
-/** Transfer state info for ongoing master mode transfer */
-static I2C_Transfer_TypeDef i2cTransfer[I2C_COUNT];
-
-/** @endcond */
-
-/*******************************************************************************
- **************************   GLOBAL FUNCTIONS   *******************************
- ******************************************************************************/
-
-/***************************************************************************//**
- * @brief
- *   Get current configured I2C bus frequency.
- *
- * @details
- *   This frequency is only of relevance when acting as master.
- *
- * @param[in] i2c
- *   Pointer to I2C peripheral register block.
- *
- * @return
- *   Current I2C frequency in Hz.
- ******************************************************************************/
-uint32_t I2C_BusFreqGet(I2C_TypeDef *i2c)
-{
-  uint32_t freqHfper;
-  uint32_t n;
-
-  /* Max frequency is given by freqScl = freqHfper/((Nlow + Nhigh)(DIV + 1) + I2C_CR_MAX)
-   * More details can be found in the reference manual,
-   * I2C Clock Generation chapter. */
-  freqHfper = CMU_ClockFreqGet(cmuClock_HFPER);
-  /* n = Nlow + Nhigh */
-  n        = (uint32_t)(i2cNSum[(i2c->CTRL & _I2C_CTRL_CLHR_MASK) >> _I2C_CTRL_CLHR_SHIFT]);
-
-  return (freqHfper / ((n * (i2c->CLKDIV + 1)) + I2C_CR_MAX));
-}
-
-
-/***************************************************************************//**
- * @brief
- *   Set I2C bus frequency.
- *
- * @details
- *   The bus frequency is only of relevance when acting as a master. The bus
- *   frequency should not be set higher than the max frequency accepted by the
- *   slowest device on the bus.
- *
- *   Notice that due to asymmetric requirements on low and high I2C clock
- *   cycles by the I2C specification, the actual max frequency allowed in order
- *   to comply with the specification may be somewhat lower than expected.
- *
- *   Please refer to the reference manual, details on I2C clock generation,
- *   for max allowed theoretical frequencies for different modes.
- *
- * @param[in] i2c
- *   Pointer to I2C peripheral register block.
- *
- * @param[in] freqRef
- *   I2C reference clock frequency in Hz that will be used. If set to 0,
- *   then HFPER clock is used. Setting it to a higher than actual configured
- *   value only has the consequence of reducing the real I2C frequency.
- *
- * @param[in] freqScl
- *   Bus frequency to set (actual bus speed may be lower due to integer
- *   prescaling). Safe (according to I2C specification) max frequencies for
- *   standard, fast and fast+ modes are available using I2C_FREQ_ defines.
- *   (Using I2C_FREQ_ defines requires corresponding setting of @p type.)
- *   Slowest slave device on bus must always be considered.
- *
- * @param[in] i2cMode
- *   Clock low to high ratio type to use. If not using i2cClockHLRStandard,
- *   make sure all devices on the bus support the specified mode. Using a
- *   non-standard ratio is useful to achieve higher bus clock in fast and
- *   fast+ modes.
- ******************************************************************************/
-void I2C_BusFreqSet(I2C_TypeDef *i2c,
-                    uint32_t freqRef,
-                    uint32_t freqScl,
-                    I2C_ClockHLR_TypeDef i2cMode)
-{
-  uint32_t n, minFreq;
-  int32_t div;
-
-  /* Avoid divide by 0 */
-  EFM_ASSERT(freqScl);
-  if (!freqScl)
-  {
-    return;
-  }
-
-  /* Set the CLHR (clock low to high ratio). */
-  i2c->CTRL &= ~_I2C_CTRL_CLHR_MASK;
-  BUS_RegMaskedWrite(&i2c->CTRL,
-                     _I2C_CTRL_CLHR_MASK,
-                     i2cMode <<_I2C_CTRL_CLHR_SHIFT);
-
-  if (!freqRef)
-  {
-    freqRef = CMU_ClockFreqGet(cmuClock_HFPER);
-  }
-
-    /* Check minumum HF peripheral clock */
-  minFreq = UINT_MAX;
-  if (i2c->CTRL & I2C_CTRL_SLAVE)
-  {
-    switch(i2cMode)
-    {
-      case i2cClockHLRStandard:
-#if defined( _SILICON_LABS_32B_PLATFORM_1 )
-        minFreq = 4200000; break;
-#elif defined( _SILICON_LABS_32B_PLATFORM_2 )
-        minFreq = 2000000; break;
-#endif
-      case i2cClockHLRAsymetric:
-#if defined( _SILICON_LABS_32B_PLATFORM_1 )
-        minFreq = 11000000; break;
-#elif defined( _SILICON_LABS_32B_PLATFORM_2 )
-        minFreq = 5000000; break;
-#endif
-      case i2cClockHLRFast:
-#if defined( _SILICON_LABS_32B_PLATFORM_1 )
-        minFreq = 24400000; break;
-#elif defined( _SILICON_LABS_32B_PLATFORM_2 )
-        minFreq = 14000000; break;
-#endif
-    }
-  }
-  else
-  {
-    /* For master mode, platform 1 and 2 share the same
-       min frequencies */
-    switch(i2cMode)
-    {
-      case i2cClockHLRStandard:
-        minFreq = 2000000; break;
-      case i2cClockHLRAsymetric:
-        minFreq = 9000000; break;
-      case i2cClockHLRFast:
-        minFreq = 20000000; break;
-    }
-  }
-
-  /* Frequency most be larger-than */
-  EFM_ASSERT(freqRef > minFreq);
-
-  /* SCL frequency is given by
-   * freqScl = freqRef/((Nlow + Nhigh) * (DIV + 1) + I2C_C_MAX)
-   *
-   * Thus
-   * DIV = ((freqRef - (I2C_C_MAX * freqScl))/((Nlow + Nhigh) * freqScl)) - 1
-   *
-   * More details can be found in the reference manual,
-   * I2C Clock Generation chapter.  */
-
-  /* n = Nlow + Nhigh */
-  n = (uint32_t)(i2cNSum[i2cMode]);
-  div = ((freqRef - (I2C_CR_MAX * freqScl)) / (n * freqScl)) - 1;
-  EFM_ASSERT(div >= 0);
-  EFM_ASSERT((uint32_t)div <= _I2C_CLKDIV_DIV_MASK);
-
-  /* Clock divisor must be at least 1 in slave mode according to reference */
-  /* manual (in which case there is normally no need to set bus frequency). */
-  if ((i2c->CTRL & I2C_CTRL_SLAVE) && !div)
-  {
-    div = 1;
-  }
-  i2c->CLKDIV = (uint32_t)div;
-}
-
-
-/***************************************************************************//**
- * @brief
- *   Enable/disable I2C.
- *
- * @note
- *   After enabling the I2C (from being disabled), the I2C is in BUSY state.
- *
- * @param[in] i2c
- *   Pointer to I2C peripheral register block.
- *
- * @param[in] enable
- *   true to enable counting, false to disable.
- ******************************************************************************/
-void I2C_Enable(I2C_TypeDef *i2c, bool enable)
-{
-  EFM_ASSERT(I2C_REF_VALID(i2c));
-
-  BUS_RegBitWrite(&(i2c->CTRL), _I2C_CTRL_EN_SHIFT, enable);
-}
-
-
-/***************************************************************************//**
- * @brief
- *   Initialize I2C.
- *
- * @param[in] i2c
- *   Pointer to I2C peripheral register block.
- *
- * @param[in] init
- *   Pointer to I2C initialization structure.
- ******************************************************************************/
-void I2C_Init(I2C_TypeDef *i2c, const I2C_Init_TypeDef *init)
-{
-  EFM_ASSERT(I2C_REF_VALID(i2c));
-
-  i2c->IEN = 0;
-  i2c->IFC = _I2C_IFC_MASK;
-
-  /* Set SLAVE select mode */
-  BUS_RegBitWrite(&(i2c->CTRL), _I2C_CTRL_SLAVE_SHIFT, init->master ? 0 : 1);
-
-  I2C_BusFreqSet(i2c, init->refFreq, init->freq, init->clhr);
-
-  BUS_RegBitWrite(&(i2c->CTRL), _I2C_CTRL_EN_SHIFT, init->enable);
-}
-
-
-/***************************************************************************//**
- * @brief
- *   Reset I2C to same state as after a HW reset.
- *
- * @note
- *   The ROUTE register is NOT reset by this function, in order to allow for
- *   centralized setup of this feature.
- *
- * @param[in] i2c
- *   Pointer to I2C peripheral register block.
- ******************************************************************************/
-void I2C_Reset(I2C_TypeDef *i2c)
-{
-  i2c->CTRL      = _I2C_CTRL_RESETVALUE;
-  i2c->CLKDIV    = _I2C_CLKDIV_RESETVALUE;
-  i2c->SADDR     = _I2C_SADDR_RESETVALUE;
-  i2c->SADDRMASK = _I2C_SADDRMASK_RESETVALUE;
-  i2c->IEN       = _I2C_IEN_RESETVALUE;
-  i2c->IFC       = _I2C_IFC_MASK;
-  /* Do not reset route register, setting should be done independently */
-}
-
-
-/***************************************************************************//**
- * @brief
- *   Continue an initiated I2C transfer (single master mode only).
- *
- * @details
- *   This function is used repeatedly after a I2C_TransferInit() in order to
- *   complete a transfer. It may be used in polled mode as the below example
- *   shows:
- * @verbatim
- * I2C_TransferReturn_TypeDef ret;
- *
- * // Do a polled transfer
- * ret = I2C_TransferInit(I2C0, seq);
- * while (ret == i2cTransferInProgress)
- * {
- *   ret = I2C_Transfer(I2C0);
- * }
- * @endverbatim
- *  It may also be used in interrupt driven mode, where this function is invoked
- *  from the interrupt handler. Notice that if used in interrupt mode, NVIC
- *  interrupts must be configured and enabled for the I2C bus used. I2C
- *  peripheral specific interrupts are managed by this SW.
- *
- * @note
- *   Only single master mode is supported.
- *
- * @param[in] i2c
- *   Pointer to I2C peripheral register block.
- *
- * @return
- *   Returns status for ongoing transfer.
- *   @li #i2cTransferInProgress - indicates that transfer not finished.
- *   @li #i2cTransferDone - transfer completed successfully.
- *   @li otherwise some sort of error has occurred.
- *
- ******************************************************************************/
-I2C_TransferReturn_TypeDef I2C_Transfer(I2C_TypeDef *i2c)
-{
-  uint32_t                tmp;
-  uint32_t                pending;
-  I2C_Transfer_TypeDef    *transfer;
-  I2C_TransferSeq_TypeDef *seq;
-
-  EFM_ASSERT(I2C_REF_VALID(i2c));
-
-  /* Support up to 2 I2C buses */
-  if (i2c == I2C0)
-  {
-    transfer = i2cTransfer;
-  }
-#if (I2C_COUNT > 1)
-  else if (i2c == I2C1)
-  {
-    transfer = i2cTransfer + 1;
-  }
-#endif
-  else
-  {
-    return i2cTransferUsageFault;
-  }
-
-  seq = transfer->seq;
-  for (;; )
-  {
-    pending = i2c->IF;
-
-    /* If some sort of fault, abort transfer. */
-    if (pending & I2C_IF_ERRORS)
-    {
-      if (pending & I2C_IF_ARBLOST)
-      {
-        /* If arbitration fault, it indicates either a slave device */
-        /* not responding as expected, or other master which is not */
-        /* supported by this SW. */
-        transfer->result = i2cTransferArbLost;
-      }
-      else if (pending & I2C_IF_BUSERR)
-      {
-        /* A bus error indicates a misplaced start or stop, which should */
-        /* not occur in master mode controlled by this SW. */
-        transfer->result = i2cTransferBusErr;
-      }
-
-      /* If error situation occurred, it is difficult to know */
-      /* exact cause and how to resolve. It will be up to a wrapper */
-      /* to determine how to handle a fault/recovery if possible. */
-      transfer->state = i2cStateDone;
-      goto done;
-    }
-
-    switch (transfer->state)
-    {
-    /***************************************************/
-    /* Send first start+address (first byte if 10 bit) */
-    /***************************************************/
-    case i2cStateStartAddrSend:
-      if (seq->flags & I2C_FLAG_10BIT_ADDR)
-      {
-        tmp = (((uint32_t)(seq->addr) >> 8) & 0x06) | 0xf0;
-
-        /* In 10 bit address mode, the address following the first */
-        /* start always indicate write. */
-      }
-      else
-      {
-        tmp = (uint32_t)(seq->addr) & 0xfe;
-
-        if (seq->flags & I2C_FLAG_READ)
-        {
-          /* Indicate read request */
-          tmp |= 1;
-        }
-      }
-
-      transfer->state = i2cStateAddrWFAckNack;
-      i2c->TXDATA     = tmp; /* Data not transmitted until START sent */
-      i2c->CMD        = I2C_CMD_START;
-      goto done;
-
-    /*******************************************************/
-    /* Wait for ACK/NACK on address (first byte if 10 bit) */
-    /*******************************************************/
-    case i2cStateAddrWFAckNack:
-      if (pending & I2C_IF_NACK)
-      {
-        i2c->IFC         = I2C_IFC_NACK;
-        transfer->result = i2cTransferNack;
-        transfer->state  = i2cStateWFStopSent;
-        i2c->CMD         = I2C_CMD_STOP;
-      }
-      else if (pending & I2C_IF_ACK)
-      {
-        i2c->IFC = I2C_IFC_ACK;
-
-        /* If 10 bit address, send 2nd byte of address. */
-        if (seq->flags & I2C_FLAG_10BIT_ADDR)
-        {
-          transfer->state = i2cStateAddrWF2ndAckNack;
-          i2c->TXDATA     = (uint32_t)(seq->addr) & 0xff;
-        }
-        else
-        {
-          /* Determine whether receiving or sending data */
-          if (seq->flags & I2C_FLAG_READ)
-          {
-            transfer->state = i2cStateWFData;
-            if(seq->buf[transfer->bufIndx].len==1)
-            {
-              i2c->CMD  = I2C_CMD_NACK;
-            }
-          }
-          else
-          {
-            transfer->state = i2cStateDataSend;
-            continue;
-          }
-        }
-      }
-      goto done;
-
-    /******************************************************/
-    /* Wait for ACK/NACK on second byte of 10 bit address */
-    /******************************************************/
-    case i2cStateAddrWF2ndAckNack:
-      if (pending & I2C_IF_NACK)
-      {
-        i2c->IFC         = I2C_IFC_NACK;
-        transfer->result = i2cTransferNack;
-        transfer->state  = i2cStateWFStopSent;
-        i2c->CMD         = I2C_CMD_STOP;
-      }
-      else if (pending & I2C_IF_ACK)
-      {
-        i2c->IFC = I2C_IFC_ACK;
-
-        /* If using plain read sequence with 10 bit address, switch to send */
-        /* repeated start. */
-        if (seq->flags & I2C_FLAG_READ)
-        {
-          transfer->state = i2cStateRStartAddrSend;
-        }
-        /* Otherwise expected to write 0 or more bytes */
-        else
-        {
-          transfer->state = i2cStateDataSend;
-        }
-        continue;
-      }
-      goto done;
-
-    /*******************************/
-    /* Send repeated start+address */
-    /*******************************/
-    case i2cStateRStartAddrSend:
-      if (seq->flags & I2C_FLAG_10BIT_ADDR)
-      {
-        tmp = ((seq->addr >> 8) & 0x06) | 0xf0;
-      }
-      else
-      {
-        tmp = seq->addr & 0xfe;
-      }
-
-      /* If this is a write+read combined sequence, then read is about to start */
-      if (seq->flags & I2C_FLAG_WRITE_READ)
-      {
-        /* Indicate read request */
-        tmp |= 1;
-      }
-
-      transfer->state = i2cStateRAddrWFAckNack;
-      /* We have to write START cmd first since repeated start, otherwise */
-      /* data would be sent first. */
-      i2c->CMD    = I2C_CMD_START;
-      i2c->TXDATA = tmp;
-      goto done;
-
-    /**********************************************************************/
-    /* Wait for ACK/NACK on repeated start+address (first byte if 10 bit) */
-    /**********************************************************************/
-    case i2cStateRAddrWFAckNack:
-      if (pending & I2C_IF_NACK)
-      {
-        i2c->IFC         = I2C_IFC_NACK;
-        transfer->result = i2cTransferNack;
-        transfer->state  = i2cStateWFStopSent;
-        i2c->CMD         = I2C_CMD_STOP;
-      }
-      else if (pending & I2C_IF_ACK)
-      {
-        i2c->IFC = I2C_IFC_ACK;
-
-        /* Determine whether receiving or sending data */
-        if (seq->flags & I2C_FLAG_WRITE_READ)
-        {
-          transfer->state = i2cStateWFData;
-        }
-        else
-        {
-          transfer->state = i2cStateDataSend;
-          continue;
-        }
-      }
-      goto done;
-
-    /*****************************/
-    /* Send a data byte to slave */
-    /*****************************/
-    case i2cStateDataSend:
-      /* Reached end of data buffer? */
-      if (transfer->offset >= seq->buf[transfer->bufIndx].len)
-      {
-        /* Move to next message part */
-        transfer->offset = 0;
-        transfer->bufIndx++;
-
-        /* Send repeated start when switching to read mode on 2nd buffer */
-        if (seq->flags & I2C_FLAG_WRITE_READ)
-        {
-          transfer->state = i2cStateRStartAddrSend;
-          continue;
-        }
-
-        /* Only writing from one buffer, or finished both buffers */
-        if ((seq->flags & I2C_FLAG_WRITE) || (transfer->bufIndx > 1))
-        {
-          transfer->state = i2cStateWFStopSent;
-          i2c->CMD        = I2C_CMD_STOP;
-          goto done;
-        }
-
-        /* Reprocess in case next buffer is empty */
-        continue;
-      }
-
-      /* Send byte */
-      i2c->TXDATA     = (uint32_t)(seq->buf[transfer->bufIndx].data[transfer->offset++]);
-      transfer->state = i2cStateDataWFAckNack;
-      goto done;
-
-    /*********************************************************/
-    /* Wait for ACK/NACK from slave after sending data to it */
-    /*********************************************************/
-    case i2cStateDataWFAckNack:
-      if (pending & I2C_IF_NACK)
-      {
-        i2c->IFC         = I2C_IFC_NACK;
-        transfer->result = i2cTransferNack;
-        transfer->state  = i2cStateWFStopSent;
-        i2c->CMD         = I2C_CMD_STOP;
-      }
-      else if (pending & I2C_IF_ACK)
-      {
-        i2c->IFC        = I2C_IFC_ACK;
-        transfer->state = i2cStateDataSend;
-        continue;
-      }
-      goto done;
-
-    /****************************/
-    /* Wait for data from slave */
-    /****************************/
-    case i2cStateWFData:
-      if (pending & I2C_IF_RXDATAV)
-      {
-        uint8_t       data;
-        unsigned int  rxLen = seq->buf[transfer->bufIndx].len;
-
-        /* Must read out data in order to not block further progress */
-        data = (uint8_t)(i2c->RXDATA);
-
-        /* Make sure not storing beyond end of buffer just in case */
-        if (transfer->offset < rxLen)
-        {
-          seq->buf[transfer->bufIndx].data[transfer->offset++] = data;
-        }
-
-        /* If we have read all requested data, then the sequence should end */
-        if (transfer->offset >= rxLen)
-        {
-          /* If there is only one byte to receive we need to transmit the
-             NACK now, before the stop. */
-          if (1 == rxLen)
-          {
-            i2c->CMD  = I2C_CMD_NACK;
-          }
-
-          transfer->state = i2cStateWFStopSent;
-          i2c->CMD        = I2C_CMD_STOP;
-        }
-        else
-        {
-          /* Send ACK and wait for next byte */
-          i2c->CMD = I2C_CMD_ACK;
-
-          if ( (1<rxLen) && (transfer->offset == (rxLen-1)) )
-          {
-            /* If there is more than one byte to receive and this is the next
-               to last byte we need to transmit the NACK now, before receiving
-               the last byte. */
-            i2c->CMD  = I2C_CMD_NACK;
-          }
-        }
-      }
-      goto done;
-
-    /***********************************/
-    /* Wait for STOP to have been sent */
-    /***********************************/
-    case i2cStateWFStopSent:
-      if (pending & I2C_IF_MSTOP)
-      {
-        i2c->IFC        = I2C_IFC_MSTOP;
-        transfer->state = i2cStateDone;
-      }
-      goto done;
-
-    /******************************/
-    /* Unexpected state, SW fault */
-    /******************************/
-    default:
-      transfer->result = i2cTransferSwFault;
-      transfer->state  = i2cStateDone;
-      goto done;
-    }
-  }
-
- done:
-
-  if (transfer->state == i2cStateDone)
-  {
-    /* Disable interrupt sources when done */
-    i2c->IEN = 0;
-
-    /* Update result unless some fault already occurred */
-    if (transfer->result == i2cTransferInProgress)
-    {
-      transfer->result = i2cTransferDone;
-    }
-  }
-  /* Until transfer is done keep returning i2cTransferInProgress */
-  else
-  {
-    return i2cTransferInProgress;
-  }
-
-  return transfer->result;
-}
-
-
-/***************************************************************************//**
- * @brief
- *   Prepare and start an I2C transfer (single master mode only).
- *
- * @details
- *   This function must be invoked in order to start an I2C transfer
- *   sequence. In order to actually complete the transfer, I2C_Transfer() must
- *   be used either in polled mode or by adding a small driver wrapper utilizing
- *   interrupts.
- *
- * @note
- *   Only single master mode is supported.
- *
- * @param[in] i2c
- *   Pointer to I2C peripheral register block.
- *
- * @param[in] seq
- *   Pointer to sequence structure defining the I2C transfer to take place. The
- *   referenced structure must exist until the transfer has fully completed.
- *
- * @return
- *   Returns status for ongoing transfer:
- *   @li #i2cTransferInProgress - indicates that transfer not finished.
- *   @li otherwise some sort of error has occurred.
- ******************************************************************************/
-I2C_TransferReturn_TypeDef I2C_TransferInit(I2C_TypeDef *i2c,
-                                            I2C_TransferSeq_TypeDef *seq)
-{
-  I2C_Transfer_TypeDef *transfer;
-
-  EFM_ASSERT(I2C_REF_VALID(i2c));
-  EFM_ASSERT(seq);
-
-  /* Support up to 2 I2C buses */
-  if (i2c == I2C0)
-  {
-    transfer = i2cTransfer;
-  }
-#if (I2C_COUNT > 1)
-  else if (i2c == I2C1)
-  {
-    transfer = i2cTransfer + 1;
-  }
-#endif
-  else
-  {
-    return i2cTransferUsageFault;
-  }
-
-  /* Check if in busy state. Since this SW assumes single master, we can */
-  /* just issue an abort. The BUSY state is normal after a reset. */
-  if (i2c->STATE & I2C_STATE_BUSY)
-  {
-    i2c->CMD = I2C_CMD_ABORT;
-  }
-
-  /* Make sure user is not trying to read 0 bytes, it is not */
-  /* possible according to I2C spec, since slave will always start */
-  /* sending first byte ACK on address. The read operation can */
-  /* only be stopped by NACKing a received byte, ie minimum 1 byte. */
-  if (((seq->flags & I2C_FLAG_READ) && !(seq->buf[0].len)) ||
-      ((seq->flags & I2C_FLAG_WRITE_READ) && !(seq->buf[1].len))
-      )
-  {
-    return i2cTransferUsageFault;
-  }
-
-  /* Prepare for a transfer */
-  transfer->state   = i2cStateStartAddrSend;
-  transfer->result  = i2cTransferInProgress;
-  transfer->offset  = 0;
-  transfer->bufIndx = 0;
-  transfer->seq     = seq;
-
-  /* Ensure buffers are empty */
-  i2c->CMD = I2C_CMD_CLEARPC | I2C_CMD_CLEARTX;
-  if (i2c->IF & I2C_IF_RXDATAV)
-  {
-    (void)i2c->RXDATA;
-  }
-
-  /* Clear all pending interrupts prior to starting transfer. */
-  i2c->IFC = _I2C_IFC_MASK;
-
-  /* Enable those interrupts we are interested in throughout transfer. */
-  /* Notice that the I2C interrupt must also be enabled in the NVIC, but */
-  /* that is left for an additional driver wrapper. */
-  i2c->IEN = I2C_IF_NACK | I2C_IF_ACK | I2C_IF_MSTOP |
-             I2C_IF_RXDATAV | I2C_IF_ERRORS;
-
-  /* Start transfer */
-  return I2C_Transfer(i2c);
-}
-
-/** @} (end addtogroup I2C) */
-/** @} (end addtogroup EM_Library) */
-#endif /* defined(I2C_COUNT) && (I2C_COUNT > 0) */
+/***************************************************************************//**
+ * @file em_i2c.c
+ * @brief Inter-integrated Circuit (I2C) Peripheral API
+ * @version 4.2.1
+ *******************************************************************************
+ * @section License
+ * <b>(C) Copyright 2015 Silicon Labs, http://www.silabs.com</b>
+ *******************************************************************************
+ *
+ * Permission is granted to anyone to use this software for any purpose,
+ * including commercial applications, and to alter it and redistribute it
+ * freely, subject to the following restrictions:
+ *
+ * 1. The origin of this software must not be misrepresented; you must not
+ *    claim that you wrote the original software.
+ * 2. Altered source versions must be plainly marked as such, and must not be
+ *    misrepresented as being the original software.
+ * 3. This notice may not be removed or altered from any source distribution.
+ *
+ * DISCLAIMER OF WARRANTY/LIMITATION OF REMEDIES: Silicon Labs has no
+ * obligation to support this Software. Silicon Labs is providing the
+ * Software "AS IS", with no express or implied warranties of any kind,
+ * including, but not limited to, any implied warranties of merchantability
+ * or fitness for any particular purpose or warranties against infringement
+ * of any proprietary rights of a third party.
+ *
+ * Silicon Labs will not be liable for any consequential, incidental, or
+ * special damages, or any other relief, or for any claim by any third party,
+ * arising from your use of this Software.
+ *
+ ******************************************************************************/
+
+#include "em_i2c.h"
+#if defined(I2C_COUNT) && (I2C_COUNT > 0)
+
+#include "em_cmu.h"
+#include "em_bus.h"
+#include "em_assert.h"
+
+ #include <limits.h>
+
+/***************************************************************************//**
+ * @addtogroup EM_Library
+ * @{
+ ******************************************************************************/
+
+/***************************************************************************//**
+ * @addtogroup I2C
+ * @brief Inter-integrated Circuit (I2C) Peripheral API
+ * @{
+ ******************************************************************************/
+
+/*******************************************************************************
+ *******************************   DEFINES   ***********************************
+ ******************************************************************************/
+
+/** @cond DO_NOT_INCLUDE_WITH_DOXYGEN */
+
+/** Validation of I2C register block pointer reference for assert statements. */
+#if (I2C_COUNT == 1)
+#define I2C_REF_VALID(ref)    ((ref) == I2C0)
+#elif (I2C_COUNT == 2)
+#define I2C_REF_VALID(ref)    ((ref == I2C0) || (ref == I2C1))
+#elif (I2C_COUNT == 3)
+#define I2C_REF_VALID(ref)    ((ref == I2C0) || (ref == I2C1)|| (ref == I2C2))
+#endif
+
+/** Error flags indicating I2C transfer has failed somehow. */
+/* Notice that I2C_IF_TXOF (transmit overflow) is not really possible with */
+/* this SW supporting master mode. Likewise for I2C_IF_RXUF (receive underflow) */
+/* RXUF is only likely to occur with this SW if using a debugger peeking into */
+/* RXDATA register. Thus, we ignore those types of fault. */
+#define I2C_IF_ERRORS    (I2C_IF_BUSERR | I2C_IF_ARBLOST)
+
+/* Max I2C transmission rate constant  */
+#if defined( _SILICON_LABS_32B_PLATFORM_1 )
+#define I2C_CR_MAX       4
+#elif defined( _SILICON_LABS_32B_PLATFORM_2 )
+#define I2C_CR_MAX       8
+#else
+#warning "Max I2C transmission rate constant is not defined"
+#endif
+
+/** @endcond */
+
+/*******************************************************************************
+ ********************************   ENUMS   ************************************
+ ******************************************************************************/
+
+/** @cond DO_NOT_INCLUDE_WITH_DOXYGEN */
+
+/** Master mode transfer states. */
+typedef enum
+{
+  i2cStateStartAddrSend,       /**< Send start + (first part of) address. */
+  i2cStateAddrWFAckNack,       /**< Wait for ACK/NACK on (first part of) address. */
+  i2cStateAddrWF2ndAckNack,    /**< Wait for ACK/NACK on second part of 10 bit address. */
+  i2cStateRStartAddrSend,      /**< Send repeated start + (first part of) address. */
+  i2cStateRAddrWFAckNack,      /**< Wait for ACK/NACK on address sent after repeated start. */
+  i2cStateDataSend,            /**< Send data. */
+  i2cStateDataWFAckNack,       /**< Wait for ACK/NACK on data sent. */
+  i2cStateWFData,              /**< Wait for data. */
+  i2cStateWFStopSent,          /**< Wait for STOP to have been transmitted. */
+  i2cStateDone                 /**< Transfer completed successfully. */
+} I2C_TransferState_TypeDef;
+
+/** @endcond */
+
+/*******************************************************************************
+ *******************************   STRUCTS   ***********************************
+ ******************************************************************************/
+
+/** @cond DO_NOT_INCLUDE_WITH_DOXYGEN */
+
+/** Structure used to store state information on an ongoing master mode transfer. */
+typedef struct
+{
+  /** Current state. */
+  I2C_TransferState_TypeDef  state;
+
+  /** Result return code. */
+  I2C_TransferReturn_TypeDef result;
+
+  /** Offset in current sequence buffer. */
+  uint16_t                   offset;
+
+  /* Index to current sequence buffer in use. */
+  uint8_t                    bufIndx;
+
+  /** Reference to I2C transfer sequence definition provided by user. */
+  I2C_TransferSeq_TypeDef    *seq;
+} I2C_Transfer_TypeDef;
+
+/** @endcond */
+
+/*******************************************************************************
+ *****************************   LOCAL DATA   *******^**************************
+ ******************************************************************************/
+
+/** @cond DO_NOT_INCLUDE_WITH_DOXYGEN */
+
+/**
+ * Lookup table for Nlow + Nhigh setting defined by CLHR. Set undefined
+ * index (0x3) to reflect default setting just in case.
+ */
+static const uint8_t i2cNSum[] = { 4 + 4, 6 + 3, 11 + 6, 4 + 4 };
+
+/** Transfer state info for ongoing master mode transfer */
+static I2C_Transfer_TypeDef i2cTransfer[I2C_COUNT];
+
+/** @endcond */
+
+/*******************************************************************************
+ **************************   GLOBAL FUNCTIONS   *******************************
+ ******************************************************************************/
+
+/***************************************************************************//**
+ * @brief
+ *   Get current configured I2C bus frequency.
+ *
+ * @details
+ *   This frequency is only of relevance when acting as master.
+ *
+ * @param[in] i2c
+ *   Pointer to I2C peripheral register block.
+ *
+ * @return
+ *   Current I2C frequency in Hz.
+ ******************************************************************************/
+uint32_t I2C_BusFreqGet(I2C_TypeDef *i2c)
+{
+  uint32_t freqHfper;
+  uint32_t n;
+
+  /* Max frequency is given by freqScl = freqHfper/((Nlow + Nhigh)(DIV + 1) + I2C_CR_MAX)
+   * More details can be found in the reference manual,
+   * I2C Clock Generation chapter. */
+  freqHfper = CMU_ClockFreqGet(cmuClock_HFPER);
+  /* n = Nlow + Nhigh */
+  n        = (uint32_t)(i2cNSum[(i2c->CTRL & _I2C_CTRL_CLHR_MASK) >> _I2C_CTRL_CLHR_SHIFT]);
+
+  return (freqHfper / ((n * (i2c->CLKDIV + 1)) + I2C_CR_MAX));
+}
+
+
+/***************************************************************************//**
+ * @brief
+ *   Set I2C bus frequency.
+ *
+ * @details
+ *   The bus frequency is only of relevance when acting as a master. The bus
+ *   frequency should not be set higher than the max frequency accepted by the
+ *   slowest device on the bus.
+ *
+ *   Notice that due to asymmetric requirements on low and high I2C clock
+ *   cycles by the I2C specification, the actual max frequency allowed in order
+ *   to comply with the specification may be somewhat lower than expected.
+ *
+ *   Please refer to the reference manual, details on I2C clock generation,
+ *   for max allowed theoretical frequencies for different modes.
+ *
+ * @param[in] i2c
+ *   Pointer to I2C peripheral register block.
+ *
+ * @param[in] freqRef
+ *   I2C reference clock frequency in Hz that will be used. If set to 0,
+ *   then HFPER clock is used. Setting it to a higher than actual configured
+ *   value only has the consequence of reducing the real I2C frequency.
+ *
+ * @param[in] freqScl
+ *   Bus frequency to set (actual bus speed may be lower due to integer
+ *   prescaling). Safe (according to I2C specification) max frequencies for
+ *   standard, fast and fast+ modes are available using I2C_FREQ_ defines.
+ *   (Using I2C_FREQ_ defines requires corresponding setting of @p type.)
+ *   Slowest slave device on bus must always be considered.
+ *
+ * @param[in] i2cMode
+ *   Clock low to high ratio type to use. If not using i2cClockHLRStandard,
+ *   make sure all devices on the bus support the specified mode. Using a
+ *   non-standard ratio is useful to achieve higher bus clock in fast and
+ *   fast+ modes.
+ ******************************************************************************/
+void I2C_BusFreqSet(I2C_TypeDef *i2c,
+                    uint32_t freqRef,
+                    uint32_t freqScl,
+                    I2C_ClockHLR_TypeDef i2cMode)
+{
+  uint32_t n, minFreq;
+  int32_t div;
+
+  /* Avoid divide by 0 */
+  EFM_ASSERT(freqScl);
+  if (!freqScl)
+  {
+    return;
+  }
+
+  /* Set the CLHR (clock low to high ratio). */
+  i2c->CTRL &= ~_I2C_CTRL_CLHR_MASK;
+  BUS_RegMaskedWrite(&i2c->CTRL,
+                     _I2C_CTRL_CLHR_MASK,
+                     i2cMode <<_I2C_CTRL_CLHR_SHIFT);
+
+  if (!freqRef)
+  {
+    freqRef = CMU_ClockFreqGet(cmuClock_HFPER);
+  }
+
+    /* Check minumum HF peripheral clock */
+  minFreq = UINT_MAX;
+  if (i2c->CTRL & I2C_CTRL_SLAVE)
+  {
+    switch(i2cMode)
+    {
+      case i2cClockHLRStandard:
+#if defined( _SILICON_LABS_32B_PLATFORM_1 )
+        minFreq = 4200000; break;
+#elif defined( _SILICON_LABS_32B_PLATFORM_2 )
+        minFreq = 2000000; break;
+#endif
+      case i2cClockHLRAsymetric:
+#if defined( _SILICON_LABS_32B_PLATFORM_1 )
+        minFreq = 11000000; break;
+#elif defined( _SILICON_LABS_32B_PLATFORM_2 )
+        minFreq = 5000000; break;
+#endif
+      case i2cClockHLRFast:
+#if defined( _SILICON_LABS_32B_PLATFORM_1 )
+        minFreq = 24400000; break;
+#elif defined( _SILICON_LABS_32B_PLATFORM_2 )
+        minFreq = 14000000; break;
+#endif
+    }
+  }
+  else
+  {
+    /* For master mode, platform 1 and 2 share the same
+       min frequencies */
+    switch(i2cMode)
+    {
+      case i2cClockHLRStandard:
+        minFreq = 2000000; break;
+      case i2cClockHLRAsymetric:
+        minFreq = 9000000; break;
+      case i2cClockHLRFast:
+        minFreq = 20000000; break;
+    }
+  }
+
+  /* Frequency most be larger-than */
+  EFM_ASSERT(freqRef > minFreq);
+
+  /* SCL frequency is given by
+   * freqScl = freqRef/((Nlow + Nhigh) * (DIV + 1) + I2C_C_MAX)
+   *
+   * Thus
+   * DIV = ((freqRef - (I2C_C_MAX * freqScl))/((Nlow + Nhigh) * freqScl)) - 1
+   *
+   * More details can be found in the reference manual,
+   * I2C Clock Generation chapter.  */
+
+  /* n = Nlow + Nhigh */
+  n = (uint32_t)(i2cNSum[i2cMode]);
+  div = ((freqRef - (I2C_CR_MAX * freqScl)) / (n * freqScl)) - 1;
+  EFM_ASSERT(div >= 0);
+  EFM_ASSERT((uint32_t)div <= _I2C_CLKDIV_DIV_MASK);
+
+  /* Clock divisor must be at least 1 in slave mode according to reference */
+  /* manual (in which case there is normally no need to set bus frequency). */
+  if ((i2c->CTRL & I2C_CTRL_SLAVE) && !div)
+  {
+    div = 1;
+  }
+  i2c->CLKDIV = (uint32_t)div;
+}
+
+
+/***************************************************************************//**
+ * @brief
+ *   Enable/disable I2C.
+ *
+ * @note
+ *   After enabling the I2C (from being disabled), the I2C is in BUSY state.
+ *
+ * @param[in] i2c
+ *   Pointer to I2C peripheral register block.
+ *
+ * @param[in] enable
+ *   true to enable counting, false to disable.
+ ******************************************************************************/
+void I2C_Enable(I2C_TypeDef *i2c, bool enable)
+{
+  EFM_ASSERT(I2C_REF_VALID(i2c));
+
+  BUS_RegBitWrite(&(i2c->CTRL), _I2C_CTRL_EN_SHIFT, enable);
+}
+
+
+/***************************************************************************//**
+ * @brief
+ *   Initialize I2C.
+ *
+ * @param[in] i2c
+ *   Pointer to I2C peripheral register block.
+ *
+ * @param[in] init
+ *   Pointer to I2C initialization structure.
+ ******************************************************************************/
+void I2C_Init(I2C_TypeDef *i2c, const I2C_Init_TypeDef *init)
+{
+  EFM_ASSERT(I2C_REF_VALID(i2c));
+
+  i2c->IEN = 0;
+  i2c->IFC = _I2C_IFC_MASK;
+
+  /* Set SLAVE select mode */
+  BUS_RegBitWrite(&(i2c->CTRL), _I2C_CTRL_SLAVE_SHIFT, init->master ? 0 : 1);
+
+  I2C_BusFreqSet(i2c, init->refFreq, init->freq, init->clhr);
+
+  BUS_RegBitWrite(&(i2c->CTRL), _I2C_CTRL_EN_SHIFT, init->enable);
+}
+
+
+/***************************************************************************//**
+ * @brief
+ *   Reset I2C to same state as after a HW reset.
+ *
+ * @note
+ *   The ROUTE register is NOT reset by this function, in order to allow for
+ *   centralized setup of this feature.
+ *
+ * @param[in] i2c
+ *   Pointer to I2C peripheral register block.
+ ******************************************************************************/
+void I2C_Reset(I2C_TypeDef *i2c)
+{
+  i2c->CTRL      = _I2C_CTRL_RESETVALUE;
+  i2c->CLKDIV    = _I2C_CLKDIV_RESETVALUE;
+  i2c->SADDR     = _I2C_SADDR_RESETVALUE;
+  i2c->SADDRMASK = _I2C_SADDRMASK_RESETVALUE;
+  i2c->IEN       = _I2C_IEN_RESETVALUE;
+  i2c->IFC       = _I2C_IFC_MASK;
+  /* Do not reset route register, setting should be done independently */
+}
+
+
+/***************************************************************************//**
+ * @brief
+ *   Continue an initiated I2C transfer (single master mode only).
+ *
+ * @details
+ *   This function is used repeatedly after a I2C_TransferInit() in order to
+ *   complete a transfer. It may be used in polled mode as the below example
+ *   shows:
+ * @verbatim
+ * I2C_TransferReturn_TypeDef ret;
+ *
+ * // Do a polled transfer
+ * ret = I2C_TransferInit(I2C0, seq);
+ * while (ret == i2cTransferInProgress)
+ * {
+ *   ret = I2C_Transfer(I2C0);
+ * }
+ * @endverbatim
+ *  It may also be used in interrupt driven mode, where this function is invoked
+ *  from the interrupt handler. Notice that if used in interrupt mode, NVIC
+ *  interrupts must be configured and enabled for the I2C bus used. I2C
+ *  peripheral specific interrupts are managed by this SW.
+ *
+ * @note
+ *   Only single master mode is supported.
+ *
+ * @param[in] i2c
+ *   Pointer to I2C peripheral register block.
+ *
+ * @return
+ *   Returns status for ongoing transfer.
+ *   @li #i2cTransferInProgress - indicates that transfer not finished.
+ *   @li #i2cTransferDone - transfer completed successfully.
+ *   @li otherwise some sort of error has occurred.
+ *
+ ******************************************************************************/
+I2C_TransferReturn_TypeDef I2C_Transfer(I2C_TypeDef *i2c)
+{
+  uint32_t                tmp;
+  uint32_t                pending;
+  I2C_Transfer_TypeDef    *transfer;
+  I2C_TransferSeq_TypeDef *seq;
+
+  EFM_ASSERT(I2C_REF_VALID(i2c));
+
+  /* Support up to 2 I2C buses */
+  if (i2c == I2C0)
+  {
+    transfer = i2cTransfer;
+  }
+#if (I2C_COUNT > 1)
+  else if (i2c == I2C1)
+  {
+    transfer = i2cTransfer + 1;
+  }
+#endif
+  else
+  {
+    return i2cTransferUsageFault;
+  }
+
+  seq = transfer->seq;
+  for (;; )
+  {
+    pending = i2c->IF;
+
+    /* If some sort of fault, abort transfer. */
+    if (pending & I2C_IF_ERRORS)
+    {
+      if (pending & I2C_IF_ARBLOST)
+      {
+        /* If arbitration fault, it indicates either a slave device */
+        /* not responding as expected, or other master which is not */
+        /* supported by this SW. */
+        transfer->result = i2cTransferArbLost;
+      }
+      else if (pending & I2C_IF_BUSERR)
+      {
+        /* A bus error indicates a misplaced start or stop, which should */
+        /* not occur in master mode controlled by this SW. */
+        transfer->result = i2cTransferBusErr;
+      }
+
+      /* If error situation occurred, it is difficult to know */
+      /* exact cause and how to resolve. It will be up to a wrapper */
+      /* to determine how to handle a fault/recovery if possible. */
+      transfer->state = i2cStateDone;
+      goto done;
+    }
+
+    switch (transfer->state)
+    {
+    /***************************************************/
+    /* Send first start+address (first byte if 10 bit) */
+    /***************************************************/
+    case i2cStateStartAddrSend:
+      if (seq->flags & I2C_FLAG_10BIT_ADDR)
+      {
+        tmp = (((uint32_t)(seq->addr) >> 8) & 0x06) | 0xf0;
+
+        /* In 10 bit address mode, the address following the first */
+        /* start always indicate write. */
+      }
+      else
+      {
+        tmp = (uint32_t)(seq->addr) & 0xfe;
+
+        if (seq->flags & I2C_FLAG_READ)
+        {
+          /* Indicate read request */
+          tmp |= 1;
+        }
+      }
+
+      transfer->state = i2cStateAddrWFAckNack;
+      i2c->TXDATA     = tmp; /* Data not transmitted until START sent */
+      i2c->CMD        = I2C_CMD_START;
+      goto done;
+
+    /*******************************************************/
+    /* Wait for ACK/NACK on address (first byte if 10 bit) */
+    /*******************************************************/
+    case i2cStateAddrWFAckNack:
+      if (pending & I2C_IF_NACK)
+      {
+        i2c->IFC         = I2C_IFC_NACK;
+        transfer->result = i2cTransferNack;
+        transfer->state  = i2cStateWFStopSent;
+        i2c->CMD         = I2C_CMD_STOP;
+      }
+      else if (pending & I2C_IF_ACK)
+      {
+        i2c->IFC = I2C_IFC_ACK;
+
+        /* If 10 bit address, send 2nd byte of address. */
+        if (seq->flags & I2C_FLAG_10BIT_ADDR)
+        {
+          transfer->state = i2cStateAddrWF2ndAckNack;
+          i2c->TXDATA     = (uint32_t)(seq->addr) & 0xff;
+        }
+        else
+        {
+          /* Determine whether receiving or sending data */
+          if (seq->flags & I2C_FLAG_READ)
+          {
+            transfer->state = i2cStateWFData;
+            if(seq->buf[transfer->bufIndx].len==1)
+            {
+              i2c->CMD  = I2C_CMD_NACK;
+            }
+          }
+          else
+          {
+            transfer->state = i2cStateDataSend;
+            continue;
+          }
+        }
+      }
+      goto done;
+
+    /******************************************************/
+    /* Wait for ACK/NACK on second byte of 10 bit address */
+    /******************************************************/
+    case i2cStateAddrWF2ndAckNack:
+      if (pending & I2C_IF_NACK)
+      {
+        i2c->IFC         = I2C_IFC_NACK;
+        transfer->result = i2cTransferNack;
+        transfer->state  = i2cStateWFStopSent;
+        i2c->CMD         = I2C_CMD_STOP;
+      }
+      else if (pending & I2C_IF_ACK)
+      {
+        i2c->IFC = I2C_IFC_ACK;
+
+        /* If using plain read sequence with 10 bit address, switch to send */
+        /* repeated start. */
+        if (seq->flags & I2C_FLAG_READ)
+        {
+          transfer->state = i2cStateRStartAddrSend;
+        }
+        /* Otherwise expected to write 0 or more bytes */
+        else
+        {
+          transfer->state = i2cStateDataSend;
+        }
+        continue;
+      }
+      goto done;
+
+    /*******************************/
+    /* Send repeated start+address */
+    /*******************************/
+    case i2cStateRStartAddrSend:
+      if (seq->flags & I2C_FLAG_10BIT_ADDR)
+      {
+        tmp = ((seq->addr >> 8) & 0x06) | 0xf0;
+      }
+      else
+      {
+        tmp = seq->addr & 0xfe;
+      }
+
+      /* If this is a write+read combined sequence, then read is about to start */
+      if (seq->flags & I2C_FLAG_WRITE_READ)
+      {
+        /* Indicate read request */
+        tmp |= 1;
+      }
+
+      transfer->state = i2cStateRAddrWFAckNack;
+      /* We have to write START cmd first since repeated start, otherwise */
+      /* data would be sent first. */
+      i2c->CMD    = I2C_CMD_START;
+      i2c->TXDATA = tmp;
+      goto done;
+
+    /**********************************************************************/
+    /* Wait for ACK/NACK on repeated start+address (first byte if 10 bit) */
+    /**********************************************************************/
+    case i2cStateRAddrWFAckNack:
+      if (pending & I2C_IF_NACK)
+      {
+        i2c->IFC         = I2C_IFC_NACK;
+        transfer->result = i2cTransferNack;
+        transfer->state  = i2cStateWFStopSent;
+        i2c->CMD         = I2C_CMD_STOP;
+      }
+      else if (pending & I2C_IF_ACK)
+      {
+        i2c->IFC = I2C_IFC_ACK;
+
+        /* Determine whether receiving or sending data */
+        if (seq->flags & I2C_FLAG_WRITE_READ)
+        {
+          transfer->state = i2cStateWFData;
+        }
+        else
+        {
+          transfer->state = i2cStateDataSend;
+          continue;
+        }
+      }
+      goto done;
+
+    /*****************************/
+    /* Send a data byte to slave */
+    /*****************************/
+    case i2cStateDataSend:
+      /* Reached end of data buffer? */
+      if (transfer->offset >= seq->buf[transfer->bufIndx].len)
+      {
+        /* Move to next message part */
+        transfer->offset = 0;
+        transfer->bufIndx++;
+
+        /* Send repeated start when switching to read mode on 2nd buffer */
+        if (seq->flags & I2C_FLAG_WRITE_READ)
+        {
+          transfer->state = i2cStateRStartAddrSend;
+          continue;
+        }
+
+        /* Only writing from one buffer, or finished both buffers */
+        if ((seq->flags & I2C_FLAG_WRITE) || (transfer->bufIndx > 1))
+        {
+          transfer->state = i2cStateWFStopSent;
+          i2c->CMD        = I2C_CMD_STOP;
+          goto done;
+        }
+
+        /* Reprocess in case next buffer is empty */
+        continue;
+      }
+
+      /* Send byte */
+      i2c->TXDATA     = (uint32_t)(seq->buf[transfer->bufIndx].data[transfer->offset++]);
+      transfer->state = i2cStateDataWFAckNack;
+      goto done;
+
+    /*********************************************************/
+    /* Wait for ACK/NACK from slave after sending data to it */
+    /*********************************************************/
+    case i2cStateDataWFAckNack:
+      if (pending & I2C_IF_NACK)
+      {
+        i2c->IFC         = I2C_IFC_NACK;
+        transfer->result = i2cTransferNack;
+        transfer->state  = i2cStateWFStopSent;
+        i2c->CMD         = I2C_CMD_STOP;
+      }
+      else if (pending & I2C_IF_ACK)
+      {
+        i2c->IFC        = I2C_IFC_ACK;
+        transfer->state = i2cStateDataSend;
+        continue;
+      }
+      goto done;
+
+    /****************************/
+    /* Wait for data from slave */
+    /****************************/
+    case i2cStateWFData:
+      if (pending & I2C_IF_RXDATAV)
+      {
+        uint8_t       data;
+        unsigned int  rxLen = seq->buf[transfer->bufIndx].len;
+
+        /* Must read out data in order to not block further progress */
+        data = (uint8_t)(i2c->RXDATA);
+
+        /* Make sure not storing beyond end of buffer just in case */
+        if (transfer->offset < rxLen)
+        {
+          seq->buf[transfer->bufIndx].data[transfer->offset++] = data;
+        }
+
+        /* If we have read all requested data, then the sequence should end */
+        if (transfer->offset >= rxLen)
+        {
+          /* If there is only one byte to receive we need to transmit the
+             NACK now, before the stop. */
+          if (1 == rxLen)
+          {
+            i2c->CMD  = I2C_CMD_NACK;
+          }
+
+          transfer->state = i2cStateWFStopSent;
+          i2c->CMD        = I2C_CMD_STOP;
+        }
+        else
+        {
+          /* Send ACK and wait for next byte */
+          i2c->CMD = I2C_CMD_ACK;
+
+          if ( (1<rxLen) && (transfer->offset == (rxLen-1)) )
+          {
+            /* If there is more than one byte to receive and this is the next
+               to last byte we need to transmit the NACK now, before receiving
+               the last byte. */
+            i2c->CMD  = I2C_CMD_NACK;
+          }
+        }
+      }
+      goto done;
+
+    /***********************************/
+    /* Wait for STOP to have been sent */
+    /***********************************/
+    case i2cStateWFStopSent:
+      if (pending & I2C_IF_MSTOP)
+      {
+        i2c->IFC        = I2C_IFC_MSTOP;
+        transfer->state = i2cStateDone;
+      }
+      goto done;
+
+    /******************************/
+    /* Unexpected state, SW fault */
+    /******************************/
+    default:
+      transfer->result = i2cTransferSwFault;
+      transfer->state  = i2cStateDone;
+      goto done;
+    }
+  }
+
+ done:
+
+  if (transfer->state == i2cStateDone)
+  {
+    /* Disable interrupt sources when done */
+    i2c->IEN = 0;
+
+    /* Update result unless some fault already occurred */
+    if (transfer->result == i2cTransferInProgress)
+    {
+      transfer->result = i2cTransferDone;
+    }
+  }
+  /* Until transfer is done keep returning i2cTransferInProgress */
+  else
+  {
+    return i2cTransferInProgress;
+  }
+
+  return transfer->result;
+}
+
+
+/***************************************************************************//**
+ * @brief
+ *   Prepare and start an I2C transfer (single master mode only).
+ *
+ * @details
+ *   This function must be invoked in order to start an I2C transfer
+ *   sequence. In order to actually complete the transfer, I2C_Transfer() must
+ *   be used either in polled mode or by adding a small driver wrapper utilizing
+ *   interrupts.
+ *
+ * @note
+ *   Only single master mode is supported.
+ *
+ * @param[in] i2c
+ *   Pointer to I2C peripheral register block.
+ *
+ * @param[in] seq
+ *   Pointer to sequence structure defining the I2C transfer to take place. The
+ *   referenced structure must exist until the transfer has fully completed.
+ *
+ * @return
+ *   Returns status for ongoing transfer:
+ *   @li #i2cTransferInProgress - indicates that transfer not finished.
+ *   @li otherwise some sort of error has occurred.
+ ******************************************************************************/
+I2C_TransferReturn_TypeDef I2C_TransferInit(I2C_TypeDef *i2c,
+                                            I2C_TransferSeq_TypeDef *seq)
+{
+  I2C_Transfer_TypeDef *transfer;
+
+  EFM_ASSERT(I2C_REF_VALID(i2c));
+  EFM_ASSERT(seq);
+
+  /* Support up to 2 I2C buses */
+  if (i2c == I2C0)
+  {
+    transfer = i2cTransfer;
+  }
+#if (I2C_COUNT > 1)
+  else if (i2c == I2C1)
+  {
+    transfer = i2cTransfer + 1;
+  }
+#endif
+  else
+  {
+    return i2cTransferUsageFault;
+  }
+
+  /* Check if in busy state. Since this SW assumes single master, we can */
+  /* just issue an abort. The BUSY state is normal after a reset. */
+  if (i2c->STATE & I2C_STATE_BUSY)
+  {
+    i2c->CMD = I2C_CMD_ABORT;
+  }
+
+  /* Make sure user is not trying to read 0 bytes, it is not */
+  /* possible according to I2C spec, since slave will always start */
+  /* sending first byte ACK on address. The read operation can */
+  /* only be stopped by NACKing a received byte, ie minimum 1 byte. */
+  if (((seq->flags & I2C_FLAG_READ) && !(seq->buf[0].len)) ||
+      ((seq->flags & I2C_FLAG_WRITE_READ) && !(seq->buf[1].len))
+      )
+  {
+    return i2cTransferUsageFault;
+  }
+
+  /* Prepare for a transfer */
+  transfer->state   = i2cStateStartAddrSend;
+  transfer->result  = i2cTransferInProgress;
+  transfer->offset  = 0;
+  transfer->bufIndx = 0;
+  transfer->seq     = seq;
+
+  /* Ensure buffers are empty */
+  i2c->CMD = I2C_CMD_CLEARPC | I2C_CMD_CLEARTX;
+  if (i2c->IF & I2C_IF_RXDATAV)
+  {
+    (void)i2c->RXDATA;
+  }
+
+  /* Clear all pending interrupts prior to starting transfer. */
+  i2c->IFC = _I2C_IFC_MASK;
+
+  /* Enable those interrupts we are interested in throughout transfer. */
+  /* Notice that the I2C interrupt must also be enabled in the NVIC, but */
+  /* that is left for an additional driver wrapper. */
+  i2c->IEN = I2C_IF_NACK | I2C_IF_ACK | I2C_IF_MSTOP |
+             I2C_IF_RXDATAV | I2C_IF_ERRORS;
+
+  /* Start transfer */
+  return I2C_Transfer(i2c);
+}
+
+/** @} (end addtogroup I2C) */
+/** @} (end addtogroup EM_Library) */
+#endif /* defined(I2C_COUNT) && (I2C_COUNT > 0) */