/***************************************************************************//**
 * @file
 * @brief I2C1 poll based driver for master mode operation on FreeWatch.
 * @author Energy Micro AS
 * @version 3.20.0
 *******************************************************************************
 * @section License
 * <b>(C) Copyright 2012 Energy Micro AS, http://www.energymicro.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.
 * 4. The source and compiled code may only be used on Energy Micro "EFM32"
 *    microcontrollers and "EFR4" radios.
 *
 * DISCLAIMER OF WARRANTY/LIMITATION OF REMEDIES: Energy Micro AS has no
 * obligation to support this Software. Energy Micro AS 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.
 *
 * Energy Micro AS 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.
 *
 * Modifications:
 * Maciej Suminski: added semaphore synchronization.
 *
 *****************************************************************************/

#include <stddef.h>
#include "em_cmu.h"
#include "em_gpio.h"
#include "i2cdrv.h"
#undef FREERTOS
#ifdef FREERTOS
#include <FreeRTOS.h>
#include <semphr.h>
xSemaphoreHandle i2c_sem;
#endif /* FREERTOS */

/*******************************************************************************
 **************************   GLOBAL FUNCTIONS   *******************************
 ******************************************************************************/

/***************************************************************************//**
 * @brief
 *   Initalize basic I2C master mode driver for use on the DK.
 *
 * @details
 *   This driver only supports master mode, single bus-master. In addition
 *   to configuring the EFM32 I2C peripheral module, it also configures DK
 *   specific setup in order to use the I2C bus.
 *
 * @param[in] init
 *   Pointer to I2C initialization structure.
 ******************************************************************************/
void I2CDRV_Init(const I2C_Init_TypeDef *init)
{
  int i;

  CMU_ClockEnable(cmuClock_HFPER, true);
  CMU_ClockEnable(cmuClock_I2C1, true);

  /* Use location #1: SDA - Pin B11, SCL - Pin B12 */
  /* Output value must be set to 1 to not drive lines low... We set */
  /* SCL first, to ensure it is high before changing SDA. */
  GPIO_PinModeSet(gpioPortB, 12, gpioModeWiredAnd, 1);
  GPIO_PinModeSet(gpioPortB, 11, gpioModeWiredAnd, 1);

  /* In some situations (after a reset during an I2C transfer), the slave */
  /* device may be left in an unknown state. Send 9 clock pulses just in case. */
  for (i = 0; i < 9; i++)
  {
    /*
     * TBD: Seems to be clocking at appr 80kHz-120kHz depending on compiler
     * optimization when running at 14MHz. A bit high for standard mode devices,
     * but DK only has fast mode devices. Need however to add some time
     * measurement in order to not be dependable on frequency and code executed.
     */
    GPIO_PinModeSet(gpioPortB, 12, gpioModeWiredAnd, 0);
    GPIO_PinModeSet(gpioPortB, 12, gpioModeWiredAnd, 1);
  }

  /* Enable pins at location 1 */
  I2C1->ROUTE = I2C_ROUTE_SDAPEN |
                I2C_ROUTE_SCLPEN |
                (1 << _I2C_ROUTE_LOCATION_SHIFT);

  I2C_Init(I2C1, init);

#ifdef FREERTOS
    i2c_sem = xSemaphoreCreateMutex();
    if(i2c_sem == NULL) {
        // TODO oops..
    }
#endif /* FREERTOS */
}


/***************************************************************************//**
 * @brief
 *   Perform I2C transfer.
 *
 * @details
 *   This driver only supports master mode, single bus-master. It does not
 *   return until the transfer is complete, polling for completion.
 *
 * @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.
 ******************************************************************************/
I2C_TransferReturn_TypeDef I2CDRV_Transfer(I2C_TransferSeq_TypeDef *seq)
{
  I2C_TransferReturn_TypeDef ret;

#ifdef FREERTOS
  if(xSemaphoreTake(i2c_sem, 300000) != pdTRUE)
    return i2cTransferInProgress;

  I2C_TransferInit(I2C1, seq);
  ret = I2C_Transfer(I2C1);

  xSemaphoreGive(i2c_sem);
#else
  uint32_t                   timeout = 300000;

  /* Do a polled transfer */
  ret = I2C_TransferInit(I2C1, seq);
  while (ret == i2cTransferInProgress && timeout--)
  {
    ret = I2C_Transfer(I2C1);
  }
#endif

  return(ret);
}
