/**
 ******************************************************************************
 *
 * @file        MG32_I2C_Init.c
 * @brief       The I2C Init C file.
 *
 * @par         Project
 *              MG32
 * @version     V2.02
 * @date        2023/06/28
 * @author      Megawin Software Center
 * @copyright   Copyright (c) 2023 MegaWin Technology Co., Ltd.
 *              All rights reserved.
 *   
 ******************************************************************************* 
 * @par Disclaimer
 * The Demo software is provided "AS IS" without any warranty, either
 * expressed or implied, including, but not limited to, the implied warranties
 * of merchantability and fitness for a particular purpose. The author will
 * not be liable for any special, incidental, consequential or indirect
 * damages due to loss of data or any other reason.
 * These statements agree with the world wide and local dictated laws about
 * authorship and violence against these laws.
 *******************************************************************************
 *******************************************************************************
 */
/* Wizard menu ---------------------------------------------------------------*/

/* Includes ------------------------------------------------------------------*/
#include "MG32__Common_DRV.h"
#include "MG32_I2C_Init.h"
#include "MG32_CSC_Init.h"

#if defined(IRQHandler_Middleware_Level_)
  #include "MG32_MID.h"
#endif

/* External vairables --------------------------------------------------------*/
/* Exported variables --------------------------------------------------------*/
/* Exported functions --------------------------------------------------------*/

/* Private typedef -----------------------------------------------------------*/
/* Private define ------------------------------------------------------------*/
/* Private macro -------------------------------------------------------------*/
/* Private variables ---------------------------------------------------------*/
// (Middleware level)
#if defined(IRQHandler_Middleware_Level_)
    #if defined(ModuleExist_I2C0)
        I2C_HandleTypeDef        mI2C0;
    #endif
    #if defined(ModuleExist_I2C1)
        I2C_HandleTypeDef        mI2C1;
    #endif
#endif

/* Private function prototypes -----------------------------------------------*/
uint32_t I2C_CalculateTiming(uint32_t CK_I2Cx_PR, uint32_t SCLClock);
void I2C_Config(I2C_Struct* I2Cx);
void I2C_Init(void);

/**
 *******************************************************************************
 * @brief       I2C_CalculateTiming
 * @details     
 * @param[in]   CK_I2Cx_PR:
 * @param[in]   SCLClock:
 * @return      Timing 
 * @exception   No
 * @note        The I2C Calculate Timing.
 * @par         Example
 * @code
 * @endcode
 *******************************************************************************
 */
uint32_t I2C_CalculateTiming(uint32_t CK_I2Cx_PR, uint32_t SCLClock)
{
    __IO uint32_t lI2Cx_CK_PRFreq = 0;
    __IO uint32_t lI2C_PSC = 1;
    __IO uint32_t lI2C_DIV = 1;
    __IO uint32_t lI2C_DIV_CNT = 0;
    __IO uint32_t lI2C_HT_LT;

    union{
        uint32_t W;
        uint16_t H[2];
        struct {
            uint32_t                    :2;
            uint32_t CLKSource          :2;
            uint32_t Divider            :3;
            uint32_t                    :1;
            uint32_t Prescal            :4;
            uint32_t TimeoutClockSource :1;
            uint32_t                    :3;
            uint32_t LowTime            :8;
            uint32_t HighTime           :8;
        }Struct;
    }I2C_CLK_Config;

    I2C_CLK_Config.W = 0UL;

    lI2Cx_CK_PRFreq = CK_I2Cx_PR;

    if(lI2Cx_CK_PRFreq <= 900000)
    {
        lI2C_PSC = 1;
        lI2C_DIV = 1;
        lI2C_HT_LT = 9;
    }
    else
    {
      #if defined(MG32_1ST)
        do{           
            lI2C_HT_LT = lI2Cx_CK_PRFreq / SCLClock / lI2C_PSC / lI2C_DIV;
            if((lI2C_HT_LT >= 32) || (lI2C_HT_LT <= 9))
            {
                lI2C_PSC ++;
                if(lI2C_PSC > 8)
                {
                    lI2C_PSC = 1;
                    lI2C_DIV += lI2C_DIV;
                }
            }
        }while((lI2C_HT_LT >= 32) || (lI2C_HT_LT <= 9));
      #else
        do{
            lI2C_HT_LT = lI2Cx_CK_PRFreq / SCLClock / lI2C_PSC / lI2C_DIV;
            if((lI2C_HT_LT >= 64) || (lI2C_HT_LT <= 9)) 
            {
                lI2C_PSC ++;
                if(lI2C_PSC > 16)
                {
                    lI2C_PSC = 1;
                    lI2C_DIV += lI2C_DIV;
                }
            }
        }while((lI2C_HT_LT >= 64) || (lI2C_HT_LT <= 9));
      #endif
    }

    if(lI2C_DIV == 1)
        lI2C_DIV_CNT = 0;
    else
    {
        lI2C_DIV = (lI2C_DIV >> 1);
        do{
            lI2C_DIV_CNT ++;
            lI2C_DIV = (lI2C_DIV >> 1);
        }while(lI2C_DIV != 0);
    }

    I2C_CLK_Config.Struct.CLKSource = 0;
    I2C_CLK_Config.Struct.Prescal = lI2C_PSC - 1;
    I2C_CLK_Config.Struct.Divider = lI2C_DIV_CNT;
    I2C_CLK_Config.Struct.LowTime = (lI2C_HT_LT >> 1) - 1;
    I2C_CLK_Config.Struct.HighTime = (lI2C_HT_LT - (lI2C_HT_LT >> 1)) - 1;

    return I2C_CLK_Config.W;
}


/**
 *******************************************************************************
 * @brief       I2C_Config
 * @details     
 * @param[in]   I2Cx:
 *  @arg\b      I2C0: URT0 relationship control.
 *  @arg\b      I2C1: URT1 relationship control.
 * @return      No
 * @exception   No
 * @note        The I2C Config
 * @par         Example
 * @code
 * @endcode
 *******************************************************************************
 */
void I2C_Config(I2C_Struct* I2Cx)
{
  #if defined(I2C1)
      #if(((CONF_I2C1_SCL_CLK != CONF_I2C1_COMP_SCL_CLK) || ((CONF_I2C1_HIGHTIME < 5) || (CONF_I2C1_LOWTIME < 4))) || \
          ((CONF_I2C0_SCL_CLK != CONF_I2C0_COMP_SCL_CLK) || ((CONF_I2C0_HIGHTIME < 5) || (CONF_I2C0_LOWTIME < 4))))
        uint32_t CK_I2Cx_PR_Freq = 0;
      #endif
  #else
      #if((CONF_I2C0_SCL_CLK != CONF_I2C0_COMP_SCL_CLK) || ((CONF_I2C0_HIGHTIME < 5) || (CONF_I2C0_LOWTIME < 4)))
          uint32_t CK_I2Cx_PR_Freq = 0;
      #endif
  #endif
    
    uint32_t lI2C_CR0 = 0;
    uint32_t lI2C_SADR = 0;
    uint32_t lI2C_INT = 0;
  #if defined(I2C_MASK_default)
    uint32_t lI2C_MASK = 0x000000FE;
  #endif
    union{
        uint32_t W;
        uint16_t H[2];
        struct {
            uint32_t                    :2;
            uint32_t CLKSource          :2;
            uint32_t Divider            :3;
            uint32_t                    :1;
            uint32_t Prescal            :4;
            uint32_t TimeoutClockSource :1;
            uint32_t                    :3;
            uint32_t LowTime            :8;
            uint32_t HighTime           :8;
        }Struct;
    }I2C_CLK_Config;
    
    I2C_CLK_Config.W = 0;
    
  #if defined(ModuleExist_I2C0)
    if(I2Cx == I2C0)
    {
      #if((CONF_I2C0_SCL_CLK != CONF_I2C0_COMP_SCL_CLK) || ((CONF_I2C0_HIGHTIME < 5) || (CONF_I2C0_LOWTIME < 4)))
        if(CSC->CKS1.MBIT.I2C0_CKS == 0) 
            CK_I2Cx_PR_Freq = CONF_CK_APB_FREQ;
        else
            CK_I2Cx_PR_Freq = CONF_CK_AHB_FREQ;

        I2C_CLK_Config.W = I2C_CalculateTiming(CK_I2Cx_PR_Freq, CONF_I2C0_SCL_CLK * 1000);
      #else
        I2C_CLK_Config.Struct.CLKSource = CONF_I2C0_CLKSOURCE;
        I2C_CLK_Config.Struct.Prescal = CONF_I2C0_PRESCALER - 1;
        I2C_CLK_Config.Struct.Divider = CONF_I2C0_DIVIDER;
        I2C_CLK_Config.Struct.HighTime = CONF_I2C0_HIGHTIME - 1;
        I2C_CLK_Config.Struct.LowTime = CONF_I2C0_LOWTIME - 1;
      #endif
        lI2C_SADR = (CONF_I2C0_OWN2_ADDR << 9) | (CONF_I2C0_OWN1_ADDR << 1);

      #if(CONF_I2C0_PDT != 0)
        lI2C_CR0 |= (CONF_I2C0_PDT << 14 | I2C_CR0_SFBD_EN_mask_w);
      #endif

      #if(CONF_I2C0_GCEN != 0)
        lI2C_CR0 |= I2C_CR0_GC_EN_enable_w;
      #endif

      #if(CONF_I2C0_OWN1_ADDR != 0)
        lI2C_CR0 |= I2C_CR0_SADR_EN_enable_w;
        lI2C_SADR |= ((uint32_t)CONF_I2C0_OWN1_ADDR) << 1;
        lI2C_MASK |= ((uint32_t)CONF_I2C0_OWN1_ADDR_MASK) << 1;
      #endif

      #if(CONF_I2C0_DUAL_ADDR_ACK != 0)
        lI2C_CR0 |= I2C_CR0_SADR2_EN_enable_w;
        lI2C_SADR |= ((uint32_t)CONF_I2C0_OWN2_ADDR) << 9;
      #endif

      #if defined(IRQHandler_Middleware_Level_)
        mI2C0.Instance = I2C0;
          #if((CONF_I2C0_SCL_CLK != CONF_I2C0_COMP_SCL_CLK) || ((CONF_I2C0_HIGHTIME < 5) || (CONF_I2C0_LOWTIME < 4)))
            mI2C0.Init.Timing = I2C_CLK_Config.W;
          #else
            mI2C0.Init.Timing = CONF_I2C0_TIMING;
          #endif
        mI2C0.Init.OwnAddress1 = CONF_I2C0_OWN1_ADDR;
          #if !defined(MG32_1ST)
            mI2C0.Init.OwnAddress1Masks = CONF_I2C0_OWN1_ADDR_MASK;
          #endif
        mI2C0.Init.DualAddressMode = CONF_I2C0_DUAL_ADDR_ACK;
        mI2C0.Init.OwnAddress2 = CONF_I2C0_OWN2_ADDR;
          #if(CONF_I2C0_GCEN != 0)
            mI2C0.Init.GeneralCallMode = I2C_GENERALCALL_ENABLE;
          #else
            mI2C0.Init.GeneralCallMode = I2C_GENERALCALL_DISABLE;
          #endif
        mI2C0.ErrorCode = MID_I2C_ERROR_NONE;
        mI2C0.State = MID_I2C_STATE_READY;
        mI2C0.Mode = MID_I2C_MODE_NONE;
      #endif

      #if(CONF_I2C0_EVENT_IE != 0)
        lI2C_INT |= I2C_INT_EVENT_IE_enable_w;
      #endif

      #if(CONF_I2C0_TMOUT_IE != 0)
        lI2C_INT |= I2C_INT_TMOUT_IE_enable_w;
      #endif

      #if(CONF_I2C0_STPSTR_IE != 0)
        lI2C_INT |= I2C_INT_STPSTR_IE_enable_w;
      #endif

      #if(CONF_I2C0_WUP_IE != 0)
        lI2C_INT |= I2C_INT_WUP_IE_enable_w;
      #endif

      #if(CONF_I2C0_IEA != 0)
        lI2C_INT |= I2C_INT_IEA_enable_w;
      #endif
    }

  #endif

  #if defined(ModuleExist_I2C1)
    if(I2Cx == I2C1)
    {
      #if((CONF_I2C1_SCL_CLK != CONF_I2C1_COMP_SCL_CLK) || ((CONF_I2C1_HIGHTIME < 5) || (CONF_I2C1_LOWTIME < 4)))
        if(CSC->CKS1.MBIT.I2C1_CKS == 0) 
            CK_I2Cx_PR_Freq = CONF_CK_APB_FREQ;
        else
            CK_I2Cx_PR_Freq = CONF_CK_AHB_FREQ;

        I2C_CLK_Config.W = I2C_CalculateTiming(CK_I2Cx_PR_Freq, CONF_I2C1_SCL_CLK * 1000);
      #else
        I2C_CLK_Config.Struct.CLKSource = CONF_I2C1_CLKSOURCE;
        I2C_CLK_Config.Struct.Prescal = CONF_I2C1_PRESCALER - 1;
        I2C_CLK_Config.Struct.Divider = CONF_I2C1_DIVIDER;
        I2C_CLK_Config.Struct.HighTime = CONF_I2C1_HIGHTIME - 1;
        I2C_CLK_Config.Struct.LowTime = CONF_I2C1_LOWTIME - 1;
      #endif
        lI2C_SADR = (CONF_I2C1_OWN2_ADDR << 9) | (CONF_I2C1_OWN1_ADDR << 1);

      #if(CONF_I2C1_PDT != 0)
        lI2C_CR0 |= (CONF_I2C1_PDT << 14 | I2C_CR0_SFBD_EN_mask_w);
      #endif

      #if(CONF_I2C1_GCEN != 0)
        lI2C_CR0 |= I2C_CR0_GC_EN_enable_w;
      #endif

      #if(CONF_I2C1_OWN1_ADDR != 0)
        lI2C_CR0 |= I2C_CR0_SADR_EN_enable_w;
        lI2C_SADR |= ((uint32_t)CONF_I2C1_OWN1_ADDR) << 1;
        lI2C_MASK |= ((uint32_t)CONF_I2C1_OWN1_ADDR_MASK) << 1;
      #endif

      #if(CONF_I2C1_DUAL_ADDR_ACK != 0)
        lI2C_CR0 |= I2C_CR0_SADR2_EN_enable_w;
        lI2C_SADR |= ((uint32_t)CONF_I2C1_OWN2_ADDR) << 9;
      #endif

      #if defined(IRQHandler_Middleware_Level_)
        mI2C1.Instance = I2C1;
          #if((CONF_I2C1_SCL_CLK != CONF_I2C1_COMP_SCL_CLK) || ((CONF_I2C1_HIGHTIME < 5) || (CONF_I2C1_LOWTIME < 4)))
            mI2C1.Init.Timing = I2C_CLK_Config.W;
          #else
            mI2C1.Init.Timing = CONF_I2C1_TIMING;
          #endif
        mI2C1.Init.OwnAddress1 = CONF_I2C1_OWN1_ADDR;
          #if !defined(MG32_1ST)
            mI2C1.Init.OwnAddress1Masks = CONF_I2C1_OWN1_ADDR_MASK;
          #endif
        mI2C1.Init.DualAddressMode = CONF_I2C1_DUAL_ADDR_ACK;
        mI2C1.Init.OwnAddress2 = CONF_I2C1_OWN2_ADDR;
          #if(CONF_I2C1_GCEN != 0)
            mI2C1.Init.GeneralCallMode = I2C_GENERALCALL_ENABLE;
          #else
            mI2C1.Init.GeneralCallMode = I2C_GENERALCALL_DISABLE;
          #endif
        mI2C1.ErrorCode = MID_I2C_ERROR_NONE;
        mI2C1.State = MID_I2C_STATE_READY;
        mI2C1.Mode = MID_I2C_MODE_NONE;
      #endif

      #if(CONF_I2C1_EVENT_IE != 0)
        lI2C_INT |= I2C_INT_EVENT_IE_enable_w;
      #endif

      #if(CONF_I2C1_TMOUT_IE != 0)
        lI2C_INT |= I2C_INT_TMOUT_IE_enable_w;
      #endif

      #if(CONF_I2C1_STPSTR_IE != 0)
        lI2C_INT |= I2C_INT_STPSTR_IE_enable_w;
      #endif

    #if !defined(MG32_1ST) || !defined(MG32_2ND)
      #if(CONF_I2C1_WUP_IE != 0)
        lI2C_INT |= I2C_INT_WUP_IE_enable_w;
      #endif
    #endif
      #if(CONF_I2C1_IEA != 0)
        lI2C_INT |= I2C_INT_IEA_enable_w;
      #endif
    }
  #endif

    I2Cx->CLK.W = I2C_CLK_Config.H[0];
    I2Cx->CR0.W = lI2C_CR0;
    I2Cx->CR1.H[0] = I2C_CLK_Config.H[1];
    I2Cx->SADR.W = lI2C_SADR;

  #if !defined(MG32_1ST)
    I2Cx->MASK.W = lI2C_MASK;
  #endif
    I2Cx->INT.W = lI2C_INT;

  #if defined(ModuleExist_I2C0) && (CONF_I2C0_Mode != 0)
    if(I2Cx == I2C0)
        I2Cx->CR0.W |= I2C_CR0_EN_enable_w;
  #endif

  #if defined(ModuleExist_I2C1) && (CONF_I2C1_Mode != 0) 
    if(I2Cx == I2C1)
        I2Cx->CR0.W |= I2C_CR0_EN_enable_w;
  #endif
}


/**
 *******************************************************************************
 * @brief       I2C_Init
 * @details     
 * @return      I2Cx inital is whether pass or not.
 * @exception   No
 * @note        The I2C initial
 * @par         Example
 * @code
 * @endcode
 *******************************************************************************
 */
void I2C_Init(void)
{
  #if defined(ModuleExist_I2C0)
    I2C_Config(I2C0);
  #endif
  #if defined(ModuleExist_I2C1)
    I2C_Config(I2C1);
  #endif
}

