/*******************************************************************************
 * DISCLAIMER
 * This software is supplied by Renesas Electronics Corporation and is only
 * intended for use with Renesas products. No other uses are authorized. This
 * software is owned by Renesas Electronics Corporation and is protected under
 * all applicable laws, including copyright laws.
 * THIS SOFTWARE IS PROVIDED "AS IS" AND RENESAS MAKES NO WARRANTIES REGARDING
 * THIS SOFTWARE, WHETHER EXPRESS, IMPLIED OR STATUTORY, INCLUDING BUT NOT
 * LIMITED TO WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE
 * AND NON-INFRINGEMENT. ALL SUCH WARRANTIES ARE EXPRESSLY DISCLAIMED.
 * TO THE MAXIMUM EXTENT PERMITTED NOT PROHIBITED BY LAW, NEITHER RENESAS
 * ELECTRONICS CORPORATION NOR ANY OF ITS AFFILIATED COMPANIES SHALL BE LIABLE
 * FOR ANY DIRECT, INDIRECT, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES FOR
 * ANY REASON RELATED TO THIS SOFTWARE, EVEN IF RENESAS OR ITS AFFILIATES HAVE
 * BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
 * Renesas reserves the right, without notice, to make changes to this software
 * and to discontinue the availability of this software. By using this software,
 * you agree to the additional terms and conditions found by accessing the
 * following link:
 * http://www.renesas.com/disclaimer
 * Copyright 2023-2024 Renesas Electronics Corporation All rights reserved.
 *******************************************************************************/

/*******************************************************************************
 * DESCRIPTION   : I2C driver
 ******************************************************************************/
/******************************************************************************
 * @file          i2c.c
 * - Version      : 0.02
 * @brief         I2C driver.
 * .
 *****************************************************************************/
/******************************************************************************
 * History : DD.MM.YYYY Version  Description
 *         : 16.11.2023 0.01     First Release
 *         : 24.06.2024 0.02     Remove pre-process branch of i2c5_read().
 *****************************************************************************/

#include <stdbool.h>
#include <stdint.h>
#include <types.h>
#include <cpg.h>
#include <cpg_register.h>
#include <i2c.h>
#include <i2c_register.h>
#include <log.h>
#include <mem_io.h>
#include <pfc.h>
#include <pfc_register.h>

/* Setting value for PFC */
#define IP1SR8_SDA5_MASK    (0x0000F000U)   /* bit[15:12] */
#define IP1SR8_SCL5_MASK    (0x00000F00U)   /* bit[11:8] */
#define IP1SR8_SDA5         (0x00000000U)   /* bit[15:12] */
#define IP1SR8_SCL5         (0x00000000U)   /* bit[11:8] */
#define GPSR8_SDA5          (0x00000800U)   /* bit11 */
#define GPSR8_SCL5          (0x00000400U)   /* bit10 */
#define MODSEL8_SDA5        (0x00000800U)   /* bit11 */
#define MODSEL8_SCL5        (0x00000400U)   /* bit10 */
#define PUEN8_SDA5          (0x00000800U)   /* bit11 */
#define PUEN8_SCL5          (0x00000400U)   /* bit10 */

#define CPG_MSTPCR_I2C5     (((uint32_t)1U) << 23U)

static void i2c5_init_pin_function(void);

void i2c5_init(void)
{
    uint32_t reg;

    /*
     * Module Standby setting for I2C5
     */
    reg = mem_read32(CPG_MSTPSR5D0);
    /* If supply of clock to I2C5 is stopped */
    if (FALSE != (CPG_MSTPCR_I2C5 & reg))
    {
        /* Supply of clock to I2C5 is start */
        reg &= ~(CPG_MSTPCR_I2C5);
        cpg_reg_write(CPG_MSTPCR5D0, CPG_MSTPSR5D0, reg);
    }

    /* PFC setting for I2C5. */
    i2c5_init_pin_function();

    /* clock init */
    /* CDFD=1, HLSE=1, SME=1, FMPE=1 */
    mem_write32((uintptr_t)I2C5_ICCCR2, 0x7U | (1 << 7));
    //Clock to filter glitches = 133.3/(1 + 6) = 19Mhz
    mem_write32((uintptr_t)I2C5_ICCCR, 0x6);
    mem_write32((uintptr_t)I2C5_ICMPR, 21);
    mem_write32((uintptr_t)I2C5_ICHPR, 133);
    mem_write32((uintptr_t)I2C5_ICLPR, 150);
    /* 1st bit setup cycle */
    mem_write32((uintptr_t)I2C5_ICFBSCR, 0x07);

    /* reset slave interface */
    mem_write32((uintptr_t)I2C5_ICSIER, 0U);
    mem_write32((uintptr_t)I2C5_ICSCR,  FLAG_SDBS);
    mem_write32((uintptr_t)I2C5_ICSAR,  0U);
    mem_write32((uintptr_t)I2C5_ICSSR,  0U);

    /* reset master interface */
    mem_write32((uintptr_t)I2C5_ICMIER, 0U);
    mem_write32((uintptr_t)I2C5_ICMCR,  FLAG_MDBS);
    mem_write32((uintptr_t)I2C5_ICMAR,  0U);
    mem_write32((uintptr_t)I2C5_ICMSR,  0U);
}
/* End of function i2c5_init(void) */

static void i2c5_init_pin_function(void)
{
    uint32_t data;

    /* SDA5(GP8_11), SCL5(GP8_10) -> 0 */
    data  = mem_read32((uintptr_t)PFC_IP1SR8_RW);
    data &= ~(IP1SR8_SDA5_MASK | IP1SR8_SCL5_MASK);
    // data |= (IP1SR8_SDA5 | IP1SR8_SCL5);    /* useless */
    pfc_reg_write(PFC_IP1SR8_RW, data);

    /* SDA5, SCL5 -> 1 */
    data  = mem_read32((uintptr_t)PFC_GPSR8_RW);
    data |= (GPSR8_SDA5 | GPSR8_SCL5);
    pfc_reg_write(PFC_GPSR8_RW, data);

    /* Select SDA5 and SCL5 to I2C mode */
    data  = mem_read32((uintptr_t)PFC_MODSEL8_RW);
    data |= (MODSEL8_SDA5 | MODSEL8_SCL5);
    pfc_reg_write(PFC_MODSEL8_RW, data);

    /* SDA5, SCL5 -> 0 */
    data  = mem_read32((uintptr_t)PFC_PUEN8_RW);
    data &= ~(PUEN8_SDA5 | PUEN8_SCL5);
    pfc_reg_write(PFC_PUEN8_RW, data);
}
/* End of function i2c5_init_pin_function(void) */

static void i2c5_set_address_reg(uint32_t slaveAdd, uint32_t regAdd)
{
    uint32_t data;
    uint32_t err_count = 0U;
    uint32_t status;

    while(true)
    {
        data  = mem_read32((uintptr_t)I2C5_ICMCR);
        data &= (FLAG_FSCL | FLAG_FSDA);
        if(data == FLAG_FSCL)
        {
            break;
        }
    }

    /* 107.3.10.1. Master Transmitter
        (b) set value for the master control registers, first data byte, and address */
    status = I2C_NG;
    while(I2C_NG == status)
    {
        /* Clear all ICMSR */
        mem_write32((uintptr_t)I2C5_ICMSR, 0U);
        /* Set slave address */
        slaveAdd &= ~FLG_RW;    /* write mode */
        mem_write32((uintptr_t)I2C5_ICMAR, slaveAdd);
        /* Set register address */
        mem_write32((uintptr_t)I2C5_ICTXD, regAdd);

        while(true)
        {
            data  = mem_read32((uintptr_t)I2C5_ICMCR);
            data &= (FLAG_FSCL | FLAG_FSDA);
            if(data == FLAG_FSCL)
            {
                break;
            }
        }

    /*  5. set the Master Control register (ICMCR) = H'89 */
        /* Set MDBS, MIE and ESG */
        mem_write32((uintptr_t)I2C5_ICMCR, (FLAG_MDBS | FLAG_MIE | FLAG_ESG));  /* start condition */

    /*  (c) waiting for outputting address
        1. wait for master event (ICMSR.MAT and ICMSR.MDE) */
        /* MDE(master data empty) & MAT(master address transmitted) */
        status = i2c5_err_check(FLAG_MDE, FLAG_MAT, (FLAG_MNR|FLAG_MAL));
        if(I2C_NG == status)
        {
            err_count++;
            if(err_count > ERR_MAX)
            {
                ERROR("1:I2C data write error\n");
                panic;
            }
        }
    }
}

static void __i2c5_write(uint32_t slaveAdd, uint32_t regAdd, uint32_t setData)
{
    uint32_t data;
    uint32_t err_count = 0U;
    uint32_t status;

    /* 107.3.10.1. Master Transmitter */
    i2c5_set_address_reg(slaveAdd, regAdd);

    /*  2. set ICMCR to H'88 */
    mem_write32((uintptr_t)I2C5_ICMCR, (FLAG_MDBS | FLAG_MIE));

    status = I2C_NG;
    while(I2C_NG == status)
    {
    /*  3. Clear the ICMSR.MAT and ICMSR.MDE bits. */
        data  = mem_read32((uintptr_t)I2C5_ICMSR);
        data &= __INV(FLAG_MAT | FLAG_MDE);
        mem_write32((uintptr_t)I2C5_ICMSR, data);

    /*  (d). Monitoring transmission of data
        1. wait for master event ICMSR.MDE(master data empty) */
        status = i2c5_err_check(FLAG_MDE, FLAG_NONE, FLAG_MNR);
        if(I2C_NG == status)
        {
            err_count++;
            if(err_count > ERR_MAX)
            {
                ERROR("2:I2C data write error\n");
                panic;
            }
        }
    }

    /*  2. ICTXD = data */
    mem_write32((uintptr_t)I2C5_ICTXD, setData);

    /*  3. clear the ICMSR.MDE bit
        Clear ICMSR.MDE after setting the last byte to be transmitted.
        After the last byte data is loaded into the shift register,
        ICMSR.MDE is generated. Before clearning ICMSR.MDE, you must
        set ICMCR to H'8A (set the force stop control bit). */
    status = I2C_NG;
    while(I2C_NG == status)
    {
        data  = mem_read32((uintptr_t)I2C5_ICMSR);
        data &= __INV(FLAG_MDE);
        mem_write32((uintptr_t)I2C5_ICMSR, data);

        status = i2c5_err_check(FLAG_MDE, FLAG_NONE, FLAG_MNR);
        if(I2C_NG == status)
        {
            err_count++;
            if(err_count > ERR_MAX)
            {
                ERROR("3:I2C data write error\n");
                panic;
            }
        }
    }

    /* Before clearning ICMSR.MDE, you must
        set ICMCR to H'8A (set the force stop control bit). */
    mem_write32((uintptr_t)I2C5_ICMCR, (FLAG_MDBS | FLAG_MIE | FLAG_FSB));

    status = I2C_NG;
    while(I2C_NG == status)
    {
        data  = mem_read32((uintptr_t)I2C5_ICMSR);
        data &= __INV(FLAG_MDE);
        mem_write32((uintptr_t)I2C5_ICMSR, data);

    /*  (e) wait for end of transmission
        1. wait for the master event, ICMSR.MST */
        status = i2c5_err_check(FLAG_MST, FLAG_NONE, FLAG_MNR);
        if(I2C_NG == status)
        {
            err_count++;
            if(err_count > ERR_MAX)
            {
                ERROR("3:I2C data write error\n");
                panic;
            }
        }
    }

    /*  2. clear the ICMSR.MST bit */
    data  = mem_read32((uintptr_t)I2C5_ICMSR);
    data &= __INV(FLAG_MST);
    mem_write32((uintptr_t)I2C5_ICMSR, data);
}
/* End of function i2c5_write(uint32_t slaveAdd, uint32_t regAdd, uint32_t setData) */

static void __i2c5_read(uint32_t slaveAdd, uint32_t regAdd, uint32_t *revData)
{
    uint32_t data;
    uint32_t err_count = 0;
    uint32_t status;

    /* 107.3.10.3. Master Transmitter - repeated START - Master receiver */
    i2c5_set_address_reg(slaveAdd, regAdd);

    status = I2C_NG;
    while(I2C_NG == status)
    {
    /*  2. set ICMAR to address of slave ... (read mode: 1) */
        /* Set slave address */
        slaveAdd |= FLG_RW; /* read mode */
        mem_write32((uintptr_t)I2C5_ICMAR, slaveAdd);
        /* Set MDBS, MIE and ESG */
        mem_write32((uintptr_t)I2C5_ICMCR, (FLAG_MDBS | FLAG_MIE | FLAG_ESG));  /* start condition */
    /*  3. Clear  the ICMSR.MAT and ICMSR.MDE bits. */
        data  = mem_read32((uintptr_t)I2C5_ICMSR);
        data &= __INV(FLAG_MAT | FLAG_MDE);
        mem_write32((uintptr_t)I2C5_ICMSR, data);

    /*  (d). Monitor transmission of data.
        1. wait for master event, ICMSR.MDE */
        /* MDE(master data empty) */
        status = i2c5_err_check(FLAG_MDE, FLAG_NONE, FLAG_MNR);
        if(I2C_NG == status)
        {
            err_count++;
            if(err_count > ERR_MAX)
            {
                ERROR("2:I2C data read  error\n");
                panic;
            }
        }
    }

    status = I2C_NG;
    while(I2C_NG == status)
    {
    /*  2. set ICMCR = H'89 (MDBS = 1, MIE = 1, ESG = 1) */
        mem_write32((uintptr_t)I2C5_ICMCR, (FLAG_MDBS | FLAG_MIE | FLAG_ESG));  /* start condition */

    /*  3. clear the MDE bit. */
        data  = mem_read32((uintptr_t)I2C5_ICMSR);
        data &= __INV(FLAG_MDE);
        mem_write32((uintptr_t)I2C5_ICMSR, data);

    /*  (e) wait for outputting slave-address of master reception
        1. wait for master event (ICMSR.MAT and ICMSR.MDR bits.) */
        status = i2c5_err_check(FLAG_MDR, FLAG_MAT, (FLAG_MNR|FLAG_MAL));
        if(I2C_NG == status)
        {
            err_count++;
            if(err_count > ERR_MAX)
            {
                ERROR("3:I2C data read  error\n");
                panic;
            }
        }
    }

    status = I2C_NG;
    while(I2C_NG == status)
    {
    /*  2. set ICMCR to H'88(MDBS, MIE) */
        mem_write32((uintptr_t)I2C5_ICMCR, (FLAG_MDBS | FLAG_MIE));
    /*  3. clear the ICMSR.MAT and ICMSR.MDR bits. */
        data  = mem_read32((uintptr_t)I2C5_ICMSR);
        data &= __INV(FLAG_MAT | FLAG_MDR);
        mem_write32((uintptr_t)I2C5_ICMSR, data);
    /*  (f) Monitor reception of data
        3. set ICMR.FSB to 1 before the last byte data transfer is started */
        mem_write32((uintptr_t)I2C5_ICMCR, (FLAG_MDBS | FLAG_MIE | FLAG_FSB));  /* stop condition */
    /*  (g) Monitor reception of data
        1. handle the receive interrupt(ICMSR.MDR) in the last byte:
           that is, read the data and clear the ICMSR.MDR. */
        status = i2c5_err_check(FLAG_MDR, FLAG_NONE, FLAG_MNR);
        if(I2C_NG == status)
        {
            err_count++;
            if(err_count > ERR_MAX)
            {
                ERROR("4:I2C data read error\n");
                panic;
            }
        }
        if(I2C_OK == status)
        {
            *revData = mem_read32((uintptr_t)I2C5_ICRXD) & 0x000000FFU;
        }
        /* clear ICMSR.MDR */
        data  = mem_read32((uintptr_t)I2C5_ICMSR);
        data &= __INV(FLAG_MDR);
        mem_write32((uintptr_t)I2C5_ICMSR, data);
    }

    /*  2. wait for master event, ICMSR.MST */
    while(true)
    {
        data = mem_read32((uintptr_t)I2C5_ICMSR);
        if((data & FLAG_MST) != 0U)
        {
            break;
        }
    }

    /*  3. clear the ICMSR.MST(master stop transmitted) bit */
    data  = mem_read32((uintptr_t)I2C5_ICMSR);
    data &= __INV(FLAG_MST);
    mem_write32((uintptr_t)I2C5_ICMSR, data);
}
/* End of function i2c5_read(uint32_t slaveAdd, uint32_t regAdd, uint32_t *revData) */

uint32_t i2c5_err_check(uint32_t first, uint32_t second, uint32_t error)
{
    uint32_t data;
    uint32_t status = I2C_OK;

    while(true)
    {
        data = mem_read32((uintptr_t)I2C5_ICMSR);
        if((data & first) != 0U)
        {
            if((second == FLAG_NONE) || ((data & second) != 0U))
            {
                status = I2C_OK;
                break;
            }
        }
        if((data & error) != 0U)
        {
            mem_write32((uintptr_t)I2C5_ICMSR, ~error);
            status = I2C_NG;
            break;
        }
    }
    return status;
}
/* End of function i2c5_err_check(uint32_t first, uint32_t second, uint32_t error) */

void i2c5_release(void)
{
    mem_write32((uintptr_t)I2C5_ICCCR2, 0x00000000U);
    mem_write32((uintptr_t)I2C5_ICCCR,  0x00000000U);
    mem_write32((uintptr_t)I2C5_ICSCR,  0x00000000U);
    mem_write32((uintptr_t)I2C5_ICSSR,  0x00000000U);
    mem_write32((uintptr_t)I2C5_ICSIER, 0x00000000U);
    mem_write32((uintptr_t)I2C5_ICSAR,  0x00000000U);
    mem_write32((uintptr_t)I2C5_ICMCR,  0x00000000U);
    mem_write32((uintptr_t)I2C5_ICMSR,  0x00000000U);
    mem_write32((uintptr_t)I2C5_ICMIER, 0x00000000U);
    mem_write32((uintptr_t)I2C5_ICMAR,  0x00000000U);
}
/* End of function i2c5_release(void) */

static uint32_t cur_page = 0x00;
static inline void i2c5_page_change(uint32_t slaveAdd, uint32_t new_page) {
    if (cur_page != new_page) {
        __i2c5_write(slaveAdd, 0x00, new_page);
        __i2c5_write(slaveAdd, 0x00, new_page);
        __i2c5_read(slaveAdd, 0x00, &cur_page);
        if (cur_page != new_page) {
            ERROR("I2C5: page change error (0x%x:0x%x)\n", cur_page, new_page);
            panic;
        }
    }
}

void i2c5_write(uint32_t slaveAdd, uint32_t regAdd, uint32_t revData) {
    i2c5_page_change(slaveAdd, (regAdd & 0xFF00) >> 8);
    __i2c5_write(slaveAdd, regAdd & 0xFF, revData);
}

void i2c5_read(uint32_t slaveAdd, uint32_t regAdd, uint32_t *revData) {
    i2c5_page_change(slaveAdd, (regAdd & 0xFF00) >> 8);
    __i2c5_read(slaveAdd, regAdd & 0xFF, revData);
}
