text: /***************************************************************************//**
   * @file em_i2c.h
   * @brief Inter-intergrated circuit (I2C) peripheral API
   * @version 5.1.2
   *******************************************************************************
   * @section License
   * <b>Copyright 2016 Silicon Laboratories, Inc. 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.
   *
   ******************************************************************************/
  
  #ifndef EM_I2C_H
  #define EM_I2C_H
  
  #include "em_device.h"
  #if defined(I2C_COUNT) && (I2C_COUNT > 0)
  
  #include <stdbool.h>
  
  #ifdef __cplusplus
  extern "C" {
  #endif
  
  /***************************************************************************//**
   * @addtogroup emlib
   * @{
   ******************************************************************************/
  
  /***************************************************************************//**
   * @addtogroup I2C
   * @{
   ******************************************************************************/
  
  /*******************************************************************************
   *******************************   DEFINES   ***********************************
   ******************************************************************************/
  
  /**
   * @brief
   *   Standard mode max frequency assuming using 4:4 ratio for Nlow:Nhigh.
   * @details
   *   From I2C specification: Min Tlow = 4.7us, min Thigh = 4.0us,
   *   max Trise=1.0us, max Tfall=0.3us. Since ratio is 4:4, have to use
   *   worst case value of Tlow or Thigh as base.
   *
   *   1/(Tlow + Thigh + 1us + 0.3us) = 1/(4.7 + 4.7 + 1.3)us = 93458Hz
   * @note
   *   Due to chip characteristics, the max value is somewhat reduced.
   */
  #if defined(_SILICON_LABS_32B_SERIES_0)     \
      && (defined(_EFM32_GECKO_FAMILY)        \
          || defined(_EFM32_TINY_FAMILY)      \
          || defined(_EFM32_ZERO_FAMILY)      \
          || defined(_EFM32_HAPPY_FAMILY))
  #define I2C_FREQ_STANDARD_MAX    93000
  #elif defined(_SILICON_LABS_32B_SERIES_0)   \
        && (defined(_EFM32_GIANT_FAMILY)      \
            || defined(_EFM32_WONDER_FAMILY))
  #define I2C_FREQ_STANDARD_MAX    92000
  #elif defined(_SILICON_LABS_32B_SERIES_1)
  // None of the chips on this platform has been characterized on this parameter.
  // Use same value as on Wonder until further notice.
  #define I2C_FREQ_STANDARD_MAX    92000
  #else
  #error "Unknown device family."
  #endif
  
  /**
   * @brief
   *   Fast mode max frequency assuming using 6:3 ratio for Nlow:Nhigh.
   * @details
   *   From I2C specification: Min Tlow = 1.3us, min Thigh = 0.6us,
   *   max Trise=0.3us, max Tfall=0.3us. Since ratio is 6:3, have to use
   *   worst case value of Tlow or 2xThigh as base.
   *
   *   1/(Tlow + Thigh + 0.3us + 0.3us) = 1/(1.3 + 0.65 + 0.6)us = 392157Hz
   */
  #define I2C_FREQ_FAST_MAX        392157
  
  
  /**
   * @brief
   *   Fast mode+ max frequency assuming using 11:6 ratio for Nlow:Nhigh.
   * @details
   *   From I2C specification: Min Tlow = 0.5us, min Thigh = 0.26us,
   *   max Trise=0.12us, max Tfall=0.12us. Since ratio is 11:6, have to use
   *   worst case value of Tlow or (11/6)xThigh as base.
   *
   *   1/(Tlow + Thigh + 0.12us + 0.12us) = 1/(0.5 + 0.273 + 0.24)us = 987167Hz
   */
  #define I2C_FREQ_FASTPLUS_MAX    987167
  
  
  /**
   * @brief
   *   Indicate plain write sequence: S+ADDR(W)+DATA0+P.
   * @details
   *   @li S - Start
   *   @li ADDR(W) - address with W/R bit cleared
   *   @li DATA0 - Data taken from buffer with index 0
   *   @li P - Stop
   */
  #define I2C_FLAG_WRITE          0x0001
  
  /**
   * @brief
   *   Indicate plain read sequence: S+ADDR(R)+DATA0+P.
   * @details
   *   @li S - Start
   *   @li ADDR(R) - address with W/R bit set
   *   @li DATA0 - Data read into buffer with index 0
   *   @li P - Stop
   */
  #define I2C_FLAG_READ           0x0002
  
  /**
   * @brief
   *   Indicate combined write/read sequence: S+ADDR(W)+DATA0+Sr+ADDR(R)+DATA1+P.
   * @details
   *   @li S - Start
   *   @li Sr - Repeated start
   *   @li ADDR(W) - address with W/R bit cleared
   *   @li ADDR(R) - address with W/R bit set
   *   @li DATAn - Data written from/read into buffer with index n
   *   @li P - Stop
   */
  #define I2C_FLAG_WRITE_READ     0x0004
  
  /**
   * @brief
   *   Indicate write sequence using two buffers: S+ADDR(W)+DATA0+DATA1+P.
   * @details
   *   @li S - Start
   *   @li ADDR(W) - address with W/R bit cleared
   *   @li DATAn - Data written from buffer with index n
   *   @li P - Stop
   */
  #define I2C_FLAG_WRITE_WRITE    0x0008
  
  /** Use 10 bit address. */
  #define I2C_FLAG_10BIT_ADDR     0x0010
  
  
  /*******************************************************************************
   ********************************   ENUMS   ************************************
   ******************************************************************************/
  
  /** Clock low to high ratio settings. */
  typedef enum
  {
    i2cClockHLRStandard  = _I2C_CTRL_CLHR_STANDARD,      /**< Ratio is 4:4 */
    i2cClockHLRAsymetric = _I2C_CTRL_CLHR_ASYMMETRIC,    /**< Ratio is 6:3 */
    i2cClockHLRFast      = _I2C_CTRL_CLHR_FAST           /**< Ratio is 11:3 */
  } I2C_ClockHLR_TypeDef;
  
  
  /** Return codes for single master mode transfer function. */
  typedef enum
  {
    /* In progress code (>0) */
    i2cTransferInProgress = 1,    /**< Transfer in progress. */
  
    /* Complete code (=0) */
    i2cTransferDone       = 0,    /**< Transfer completed successfully. */
  
    /* Transfer error codes (<0) */
    i2cTransferNack       = -1,   /**< NACK received during transfer. */
    i2cTransferBusErr     = -2,   /**< Bus error during transfer (misplaced START/STOP). */
    i2cTransferArbLost    = -3,   /**< Arbitration lost during transfer. */
    i2cTransferUsageFault = -4,   /**< Usage fault. */
    i2cTransferSwFault    = -5    /**< SW fault. */
  } I2C_TransferReturn_TypeDef;
  
  
  /*******************************************************************************
   *******************************   STRUCTS   ***********************************
   ******************************************************************************/
  
  /** I2C initialization structure. */
  typedef struct
  {
    /** Enable I2C peripheral when init completed. */
    bool                 enable;
  
    /** Set to master (true) or slave (false) mode */
    bool                 master;
  
    /**
     * I2C reference clock assumed when configuring bus frequency setup.
     * Set it to 0 if currently configurated reference clock shall be used
     * This parameter is only applicable if operating in master mode.
     */
    uint32_t             refFreq;
  
    /**
     * (Max) I2C bus frequency to use. This parameter is only applicable
     * if operating in master mode.
     */
    uint32_t             freq;
  
    /** Clock low/high ratio control. */
    I2C_ClockHLR_TypeDef clhr;
  } I2C_Init_TypeDef;
  
  /** Suggested default config for I2C init structure. */
  #define I2C_INIT_DEFAULT                                                  \
  {                                                                         \
    true,                    /* Enable when init done */                    \
    true,                    /* Set to master mode */                       \
    0,                       /* Use currently configured reference clock */ \
    I2C_FREQ_STANDARD_MAX,   /* Set to standard rate assuring being */      \
                             /* within I2C spec */                          \
    i2cClockHLRStandard      /* Set to use 4:4 low/high duty cycle */       \
  }
  
  
  /**
   * @brief
   *   Master mode transfer message structure used to define a complete
   *   I2C transfer sequence (from start to stop).
   * @details
   *   The structure allows for defining the following types of sequences,
   *   please refer to defines for sequence details.
   *   @li #I2C_FLAG_READ - data read into buf[0].data
   *   @li #I2C_FLAG_WRITE - data written from buf[0].data
   *   @li #I2C_FLAG_WRITE_READ - data written from buf[0].data and read
   *     into buf[1].data
   *   @li #I2C_FLAG_WRITE_WRITE - data written from buf[0].data and
   *     buf[1].data
   */
  typedef struct
  {
    /**
     * @brief
     *   Address to use after (repeated) start.
     * @details
     *   Layout details, A = address bit, X = don't care bit (set to 0):
     *   @li 7 bit address - use format AAAA AAAX.
     *   @li 10 bit address - use format XXXX XAAX AAAA AAAA
     */
    uint16_t addr;
  
    /** Flags defining sequence type and details, see I2C_FLAG_... defines. */
    uint16_t flags;
  
    /**
     * Buffers used to hold data to send from or receive into depending
     * on sequence type.
     */
    struct
    {
      /** Buffer used for data to transmit/receive, must be @p len long. */
      uint8_t  *data;
  
      /**
       * Number of bytes in @p data to send or receive. Notice that when
       * receiving data to this buffer, at least 1 byte must be received.
       * Setting @p len to 0 in the receive case is considered a usage fault.
       * Transmitting 0 bytes is legal, in which case only the address
       * is transmitted after the start condition.
       */
      uint16_t len;
    } buf[2];
  } I2C_TransferSeq_TypeDef;
  
  
  /*******************************************************************************
   *****************************   PROTOTYPES   **********************************
   ******************************************************************************/
  
  uint32_t I2C_BusFreqGet(I2C_TypeDef *i2c);
  void I2C_BusFreqSet(I2C_TypeDef *i2c,
                      uint32_t freqRef,
                      uint32_t freqScl,
                      I2C_ClockHLR_TypeDef i2cMode);
  void I2C_Enable(I2C_TypeDef *i2c, bool enable);
  void I2C_Init(I2C_TypeDef *i2c, const I2C_Init_TypeDef *init);
  
  /***************************************************************************//**
   * @brief
   *   Clear one or more pending I2C interrupts.
   *
   * @param[in] i2c
   *   Pointer to I2C peripheral register block.
   *
   * @param[in] flags
   *   Pending I2C interrupt source to clear. Use a bitwse logic OR combination of
   *   valid interrupt flags for the I2C module (I2C_IF_nnn).
   ******************************************************************************/
  __STATIC_INLINE void I2C_IntClear(I2C_TypeDef *i2c, uint32_t flags)
  {
    i2c->IFC = flags;
  }
  
  
  /***************************************************************************//**
   * @brief
   *   Disable one or more I2C interrupts.
   *
   * @param[in] i2c
   *   Pointer to I2C peripheral register block.
   *
   * @param[in] flags
   *   I2C interrupt sources to disable. Use a bitwise logic OR combination of
   *   valid interrupt flags for the I2C module (I2C_IF_nnn).
   ******************************************************************************/
  __STATIC_INLINE void I2C_IntDisable(I2C_TypeDef *i2c, uint32_t flags)
  {
    i2c->IEN &= ~(flags);
  }
  
  
  /***************************************************************************//**
   * @brief
   *   Enable one or more I2C interrupts.
   *
   * @note
   *   Depending on the use, a pending interrupt may already be set prior to
   *   enabling the interrupt. Consider using I2C_IntClear() prior to enabling
   *   if such a pending interrupt should be ignored.
   *
   * @param[in] i2c
   *   Pointer to I2C peripheral register block.
   *
   * @param[in] flags
   *   I2C interrupt sources to enable. Use a bitwise logic OR combination of
   *   valid interrupt flags for the I2C module (I2C_IF_nnn).
   ******************************************************************************/
  __STATIC_INLINE void I2C_IntEnable(I2C_TypeDef *i2c, uint32_t flags)
  {
    i2c->IEN |= flags;
  }
  
  
  /***************************************************************************//**
   * @brief
   *   Get pending I2C interrupt flags.
   *
   * @note
   *   The event bits are not cleared by the use of this function.
   *
   * @param[in] i2c
   *   Pointer to I2C peripheral register block.
   *
   * @return
   *   I2C interrupt sources pending. A bitwise logic OR combination of valid
   *   interrupt flags for the I2C module (I2C_IF_nnn).
   ******************************************************************************/
  __STATIC_INLINE uint32_t I2C_IntGet(I2C_TypeDef *i2c)
  {
    return i2c->IF;
  }
  
  
  /***************************************************************************//**
   * @brief
   *   Get enabled and pending I2C interrupt flags.
   *   Useful for handling more interrupt sources in the same interrupt handler.
   *
   * @note
   *   Interrupt flags are not cleared by the use of this function.
   *
   * @param[in] i2c
   *   Pointer to I2C peripheral register block.
   *
   * @return
   *   Pending and enabled I2C interrupt sources
   *   The return value is the bitwise AND of
   *   - the enabled interrupt sources in I2Cn_IEN and
   *   - the pending interrupt flags I2Cn_IF
   ******************************************************************************/
  __STATIC_INLINE uint32_t I2C_IntGetEnabled(I2C_TypeDef *i2c)
  {
    uint32_t ien;
  
    ien = i2c->IEN;
    return i2c->IF & ien;
  }
  
  
  /***************************************************************************//**
   * @brief
   *   Set one or more pending I2C interrupts from SW.
   *
   * @param[in] i2c
   *   Pointer to I2C peripheral register block.
   *
   * @param[in] flags
   *   I2C interrupt sources to set to pending. Use a bitwise logic OR combination
   *   of valid interrupt flags for the I2C module (I2C_IF_nnn).
   ******************************************************************************/
  __STATIC_INLINE void I2C_IntSet(I2C_TypeDef *i2c, uint32_t flags)
  {
    i2c->IFS = flags;
  }
  
  void I2C_Reset(I2C_TypeDef *i2c);
  
  /***************************************************************************//**
   * @brief
   *   Get slave address used for I2C peripheral (when operating in slave mode).
   *
   * @details
   *   For 10 bit addressing mode, the address is split in two bytes, and only
   *   the first byte setting is fetched, effectively only controlling the 2 most
   *   significant bits of the 10 bit address. Full handling of 10 bit addressing
   *   in slave mode requires additional SW handling.
   *
   * @param[in] i2c
   *   Pointer to I2C peripheral register block.
   *
   * @return
   *   I2C slave address in use. The 7 most significant bits define the actual
   *   address, the least significant bit is reserved and always returned as 0.
   ******************************************************************************/
  __STATIC_INLINE uint8_t I2C_SlaveAddressGet(I2C_TypeDef *i2c)
  {
    return ((uint8_t)(i2c->SADDR));
  }
  
  
  /***************************************************************************//**
   * @brief
   *   Set slave address to use for I2C peripheral (when operating in slave mode).
   *
   * @details
   *   For 10 bit addressing mode, the address is split in two bytes, and only
   *   the first byte is set, effectively only controlling the 2 most significant
   *   bits of the 10 bit address. Full handling of 10 bit addressing in slave
   *   mode requires additional SW handling.
   *
   * @param[in] i2c
   *   Pointer to I2C peripheral register block.
   *
   * @param[in] addr
   *   I2C slave address to use. The 7 most significant bits define the actual
   *   address, the least significant bit is reserved and always set to 0.
   ******************************************************************************/
  __STATIC_INLINE void I2C_SlaveAddressSet(I2C_TypeDef *i2c, uint8_t addr)
  {
    i2c->SADDR = (uint32_t)addr & 0xfe;
  }
  
  
  /***************************************************************************//**
   * @brief
   *   Get slave address mask used for I2C peripheral (when operating in slave
   *   mode).
   *
   * @details
   *   The address mask defines how the comparator works. A bit position with
   *   value 0 means that the corresponding slave address bit is ignored during
   *   comparison (don't care). A bit position with value 1 means that the
   *   corresponding slave address bit must match.
   *
   *   For 10 bit addressing mode, the address is split in two bytes, and only
   *   the mask for the first address byte is fetched, effectively only
   *   controlling the 2 most significant bits of the 10 bit address.
   *
   * @param[in] i2c
   *   Pointer to I2C peripheral register block.
   *
   * @return
   *   I2C slave address mask in use. The 7 most significant bits define the
   *   actual address mask, the least significant bit is reserved and always
   *   returned as 0.
   ******************************************************************************/
  __STATIC_INLINE uint8_t I2C_SlaveAddressMaskGet(I2C_TypeDef *i2c)
  {
    return ((uint8_t)(i2c->SADDRMASK));
  }
  
  
  /***************************************************************************//**
   * @brief
   *   Set slave address mask used for I2C peripheral (when operating in slave
   *   mode).
   *
   * @details
   *   The address mask defines how the comparator works. A bit position with
   *   value 0 means that the corresponding slave address bit is ignored during
   *   comparison (don't care). A bit position with value 1 means that the
   *   corresponding slave address bit must match.
   *
   *   For 10 bit addressing mode, the address is split in two bytes, and only
   *   the mask for the first address byte is set, effectively only controlling
   *   the 2 most significant bits of the 10 bit address.
   *
   * @param[in] i2c
   *   Pointer to I2C peripheral register block.
   *
   * @param[in] mask
   *   I2C slave address mask to use. The 7 most significant bits define the
   *   actual address mask, the least significant bit is reserved and should
   *   be 0.
   ******************************************************************************/
  __STATIC_INLINE void I2C_SlaveAddressMaskSet(I2C_TypeDef *i2c, uint8_t mask)
  {
    i2c->SADDRMASK = (uint32_t)mask & 0xfe;
  }
  
  
  I2C_TransferReturn_TypeDef I2C_Transfer(I2C_TypeDef *i2c);
  I2C_TransferReturn_TypeDef I2C_TransferInit(I2C_TypeDef *i2c,
                                              I2C_TransferSeq_TypeDef *seq);
  
  /** @} (end addtogroup I2C) */
  /** @} (end addtogroup emlib) */
  
  #ifdef __cplusplus
  }
  #endif
  
  #endif /* defined(I2C_COUNT) && (I2C_COUNT > 0) */
  #endif /* EM_I2C_H */
  