/*******************************************************************************
 * 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 <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>
#include <micro_wait.h>

/* Setting value for PFC */
#define GPSR4_TSN0_MDIO     (0x00000001U)   /* bit0 */

#define PMIC_ADDR       ((0x54) << 1)    /* regulation register */
#define PMIC_ADDR_P     ((0x55) << 1)    /* protection register */
#define PMIC_ADDR_OTP   ((0x64) << 1)    /* OTP register ? */

void PMIC_SM_27A(void) {
/*
// PRESET -> PRESETOUT Check
// Set SDI and EN High, and Presetout low
MCU_VMONOUT0 		 = B_PIN_STATE_HIGH;
MCU_VMONOUT1 		 = B_PIN_STATE_HIGH;
MCU_SDI4 			 = B_PIN_STATE_HIGH;
MCU_ERROROUT_M 		 = B_PIN_STATE_HIGH;
MCU_PRESET_OUT		 = B_PIN_STATE_LOW;
MCU_EN_PIN 			 = B_PIN_STATE_HIGH;

delay_1ms(20); // Set delay to wait for LBIST to finish

while (PMIC_GLOBAL_PGOOD != B_PIN_STATE_HIGH){} // Wait for PGOOD to go high

while (PMIC_PRESETB != B_PIN_STATE_HIGH) {} // Wait for PRESET to go high

delay_1ms(6); // Set delay to pass 25% of TIMEOUT_PRESET_CHK_TOUT

MCU_PRESET_OUT = B_PIN_STATE_HIGH; // Set PRESETOUT High
*/

    // from RAA271005_SAN.pdf page 91
    // PRESET# Check is executed by the PMIC automatically

    // // PGOOD_PMIC(TSN0_MDIO, GP4_00)
    // /* Set GPSR to GPIO */
    // data  = mem_read32((uintptr_t)PFC_GPSR4_RW);
    // data &= ~(GPSR4_TSN0_MDIO);
    // pfc_reg_write(PFC_GPSR4_RW, data);

    // /* PUEN disable */
    // data  = mem_read32((uintptr_t)PFC_PUEN4_RW);
    // data &= ~(GPSR4_TSN0_MDIO);
    // pfc_reg_write(PFC_PUEN4_RW, data);

    // /* skip POSNEG
    // data  = mem_read32((uintptr_t)PFC_PUEN4_RW);
    // data |= (GPSR4_TSN0_MDIO);
    // mem_write32((uintptr_t)PFC_PUEN4_RW, data);
    // */
    // /* skip INOUTSEL
    // data  = mem_read32((uintptr_t)PFC_INOUTSEL4_RW);
    // data &= ~(GPSR4_TSN0_MDIO);
    // mem_write32((uintptr_t)PFC_INOUTSEL4_RW, data);
    // */
    // /* INEN */
    // data  = mem_read32((uintptr_t)PFC_INEN4_RW);
    // data |= (GPSR4_TSN0_MDIO);
    // mem_write32((uintptr_t)PFC_INEN4_RW, data);

    // /* INDT */
    // do {
    //     data  = mem_read32((uintptr_t)PFC_INDT4_R);
    // } while ((data & GPSR4_TSN0_MDIO) != 0);
}

#define D_SINT_CODE     0x2A
#define FUSA_CTRL_2     (0x09)
#define FUSA_CTRL_3     (0x0A)
#define FUSA_SOC_CHK_1  (0x15)
// SINT Check
void PMIC_SM_27B(void) {
    uint32_t data = D_SINT_CODE;

    i2c5_write(PMIC_ADDR_P, FUSA_CTRL_2, data);
    i2c5_read(PMIC_ADDR_P, FUSA_SOC_CHK_1, &data);
    NOTICE("SINT Check = 0x%x\n", data);
    i2c5_write(PMIC_ADDR_P, FUSA_CTRL_3, data);
}

#define FUSA_CTRL_4            (0x0B)
#define EXT_PIN_CHK_EN         (1U << 5)
#define EXT_PIN_CHK_SD1        (1U << 4)
#define EXT_PIN_CHK_SD2        (1U << 2)
#define EXT_PIN_CHK_SD3        (1U << 1)
#define EXT_PIN_CHK_SD4        (1U << 0)

/* To read/write an adress after 0x100,
   it is necessary to write a value to IO_PAGE and
    access the target address after changing the page.
 */
#define FUSA_CTRL_D     (0x11D)
// EXT PINCHECK2
void PMIC_SM_27C(void) {
/*
// SDI1 Test
pmic_rtn_code_tmp = r_pmic_i2c_write(D_PMIC_I2C_PROT_ADDRESS, 0x0B, 0xFF); // Tell the PMIC to look for all SDIs to be high, to do this we set all bits (0xFF)
delay_1us(1);
MCU_ERROROUT_M = B_PIN_STATE_LOW; // Set SDI1 LOW
pmic_rtn_code_tmp = r_pmic_i2c_write(D_PMIC_I2C_PROT_ADDRESS, 0x0B, 0xEF); // Check for the SDI1 to be low
delay_1us(1);
MCU_ERROROUT_M = B_PIN_STATE_HIGH; // Set SDI1 HIGH

// SDI2 Test
pmic_rtn_code_tmp = r_pmic_i2c_write(D_PMIC_I2C_PROT_ADDRESS, 0x0B, 0xFF); // Tell the PMIC to look for all SDIs to be high, to do this we set all bits (0xFF)
delay_1us(1);
MCU_VMONOUT0 = B_PIN_STATE_LOW; // Set SDI2 LOW
pmic_rtn_code_tmp = r_pmic_i2c_write(D_PMIC_I2C_PROT_ADDRESS, 0x0B, 0xFB); // Check for the SDI2 to be low
delay_1us(1);
MCU_VMONOUT0 = B_PIN_STATE_HIGH; // Set SDI2 HIGH

// SDI3 Test
pmic_rtn_code_tmp = r_pmic_i2c_write(D_PMIC_I2C_PROT_ADDRESS, 0x0B, 0xFF); // Tell the PMIC to look for all SDIs to be high, to do this we set all bits (0xFF)
delay_1us(1);
MCU_VMONOUT1 = B_PIN_STATE_LOW; // Set SDI3 LOW
pmic_rtn_code_tmp = r_pmic_i2c_write(D_PMIC_I2C_PROT_ADDRESS, 0x0B, 0xFC); // Check for the SDI3 to be low
delay_1us(1);
MCU_VMONOUT1 = B_PIN_STATE_HIGH; // Set SDI3 HIGH

// SDI4 Test
pmic_rtn_code_tmp = r_pmic_i2c_write(D_PMIC_I2C_PROT_ADDRESS, 0x0B, 0xFF); // Tell the PMIC to look for all SDIs to be high, to do this we set all bits (0xFF)
delay_1us(1);
MCU_SDI4 = B_PIN_STATE_LOW; // Set SDI4 LOW
pmic_rtn_code_tmp = r_pmic_i2c_write(D_PMIC_I2C_PROT_ADDRESS, 0x0B, 0xFE); // Check for the SDI4 to be low
delay_1us(1);
MCU_SDI4 = B_PIN_STATE_HIGH; // Set SDI4 HIGH

comms_rtrncode_tmp = r_pmic_i2c_write(D_PMIC_I2C_PROT_ADDRESS, 0x0B, 0x00); // Must clear out 0x0B after external pin check 2 complete
*/
    uint32_t data = 0xFF;

    NOTICE("EXT PINCHECK2\n");
    // SDI1,2,3,4 Test
    // Tell the PMIC to look for all SDIs to be high, to do this we set all bits (0xFF)
    i2c5_write(PMIC_ADDR_P, FUSA_CTRL_4, data);
    micro_wait(1U);

    // Must clear out 0x0B after external pin check 2 complete
    i2c5_write(PMIC_ADDR_P, FUSA_CTRL_4, 0x0);
}

#define D_LENGTH_FUSA_CTRL_CVM 4 // Based on how many CVM slots is enabled
#define FUSA_CHK_CVM1          (0x0D)
#define CVM_TEST_START         (1U << 0)
#define CVM_TEST_DONE          (1U << 7)
#define FUSA_STATUS_CVM1       (0x0E)
#define CVM_TEST_FAIL_3        (0x3U << 6)
#define CVM_TEST_FAIL_2        (0x3U << 4)
#define CVM_TEST_FAIL_1        (0x3U << 2)
#define FUSA_STATUS_CVM2       (0x0F)
#define CVM_TEST_FAIL_6        (0x3U << 4)
#define CVM_TEST_FAIL_5        (0x3U << 2)
#define CVM_TEST_FAIL_4        (0x3U << 0)

#define FUSA_CHK_CVM2H         (0x10D)
#define FUSA_CHK_CVM2L         (0x10E)
#define FUSA_CHK_CVM3H         (0x10F)
#define FUSA_CHK_CVM3L         (0x110)
#define FUSA_CHK_CVM4H         (0x111)
#define FUSA_CHK_CVM4L         (0x112)
#define FUSA_CHK_CVM5H         (0x113)
#define FUSA_CHK_CVM5L         (0x114)

#define FUSA_CTRL_CVM1         (0x125)
#define FUSA_CTRL_CVM2         (0x126)
#define FUSA_CTRL_CVM3         (0x127)
#define FUSA_CTRL_CVM4         (0x128)
#define FUSA_CTRL_CVM5         (0x129)
#define FUSA_CTRL_CVM6         (0x12A)

#define ADCMON_MASK_ExtINPs    (0x133)
#define FaultMask_EXT_0_Prot   (1U << 0)
#define FaultMask_EXT_1_Prot   (1U << 0)

// CVM Test
void PMIC_SM_27D(void) {
/*
//Array that holds the 8bit dac information ADC1
uint8_t g_ary_cvm_vthref[4] = {
    0x0F,  //2
    0x3A,  //3
    0x60,  //4
    0x3A   //3
};

//Array that holds the 12bit dac information ADC2
uint8_t g_ary_cvm_vthsense[8] = {
    0x03, 0xE1,
    0x00, 0xD3,
    0x00, 0xD3,
    0x03, 0xE1
};

D_LENGTH_FUSA_CTRL_CVM = 4; // Based on how many CVM slots is enabled

comms_rtn_code_tmp = r_pmic_i2c_write(D_PMIC_I2C_PROT_ADDRESS, 0x0D, 0x01); // Start CVM Test
delay_1ms(1);

for(idx = 0; idx <D_LENGTH_FUSA_CTRL_CVM; idx++){
	write_data_buffer[0] = g_ary_cvm_vthsense[idx2];
	write_data_buffer[1] = g_ary_cvm_vthsense[idx2+1];
	dac8_value_tmp = g_ary_cvm_vthref[idx];

	dac_i2c_tmp = R_IICA0_Master_Send(0x60 << 1, (uint8_t * )write_data_buffer , 2, 50); // Write to external DAC (ADC2)
	delay_1ms(1);
	idx2 += 2;
	R_DAC0_Set_ConversionValue(dac8_value_tmp); // Write to internal DAC (ADC1)

	g_ary_fusa_chk_cvm_status[idx] = r_pmic_i2c_read(D_PMIC_I2C_PROT_ADDRESS, 0x0D);
	comms_rtn_code_tmp = r_pmic_i2c_write(D_PMIC_I2C_PROT_ADDRESS, 0x0D, 0x01); // Start next slot test

	delay_1ms(2);
}

// Check to see if CVM passed
pmic_data_tmp = r_pmic_i2c_read(D_PMIC_I2C_PROT_ADDRESS, 0x0D);
while(pmic_data_tmp != 0x80 ){
	pmic_data_tmp = r_pmic_i2c_read(D_PMIC_I2C_PROT_ADDRESS, 0x0D);
	pmic_data_tmp &= 0x80;
}

R_DAC0_Set_ConversionValue(26U); // Set ADC1 value back to be within protection limits

write_data_buffer[0] = 0x03;
write_data_buffer[1] = 0xF0;
dac_i2c_tmp = R_IICA0_Master_Send(0x60 << 1, (uint8_t * )write_data_buffer , 2, 200); // Set ADC2 value back to be within protection limits
*/
    uint32_t data, data2;

    i2c5_write(PMIC_ADDR_P, FUSA_CHK_CVM1, CVM_TEST_START);

    for(int i = 0; i < D_LENGTH_FUSA_CTRL_CVM; i++) {
        micro_wait(2U);
        i2c5_write(PMIC_ADDR_P, FUSA_CHK_CVM1, CVM_TEST_START);
    }

    do {
        i2c5_read(PMIC_ADDR_P, FUSA_CHK_CVM1, &data);
    } while ((data & CVM_TEST_DONE) == 0);

    i2c5_read(PMIC_ADDR_P, FUSA_STATUS_CVM1, &data);
    i2c5_read(PMIC_ADDR_P, FUSA_STATUS_CVM2, &data2);
    NOTICE("CVM Test: 0x%x, 0x%x\n", data, data2);
}

#define FUSA_STATUS_1          (0x10)
#define SOC_ACTIVATED          (1U << 4)
#define SCS_POWER_OFF          (0x0 << 5)
#define SCS_SELF_DIAG          (0x1 << 5)
#define SCS_POWER_UP           (0x2 << 5)
#define SCS_SOC_ACTIV          (0x3 << 5)
#define SCS_ACTIVE             (0x4 << 5)
#define SCS_RESET              (0x5 << 5)
#define SCS_ERROR              (0x6 << 5)
#define SCS_LOCK               (0x7 << 5)
#define SAS_IDLE               (0x0)
#define SAS_EXT_PIN_CHK_1      (0x4)
#define SAS_SINT_CHK           (0x5)
#define SAS_EXT_PIN_CHK_2      (0x6)
#define SAS_START_WDT          (0x7)
void check_SoC_Activation(void) {
#if LOG_LEVEL >= LOG_NOTICE
    uint32_t data = 0xFF;
    char *str, *str2, *str3;

    i2c5_read(PMIC_ADDR_P, FUSA_STATUS_1, &data);
    if ((data & SOC_ACTIVATED) != 0)
        str = "PASSED";
    else
        str = "FAILED";

    switch (data & (0x7 << 5)) {
    case SCS_POWER_OFF: str2 = "power off";         break;
    case SCS_SELF_DIAG: str2 = "self diagnosis";    break;
    case SCS_POWER_UP : str2 = "power up sequence"; break;
    case SCS_SOC_ACTIV: str2 = "SoC activation";    break;
    case SCS_ACTIVE: str2 = "ACTIVE"; break;
    case SCS_RESET: str2 = "RESET";  break;
    case SCS_ERROR: str2 = "ERROR";  break;
    case SCS_LOCK: str2 = "LOCK";    break;
    default:
        str2 = "unknown";
        break;
    }

    switch (data & 0x7) {
    case SAS_IDLE:          str3 = "idle";       break;
    case SAS_EXT_PIN_CHK_1: str3 = "EXT PIN check(1)"; break;
    case SAS_SINT_CHK:      str3 = "SINT check"; break;
    case SAS_EXT_PIN_CHK_2: str3 = "EXT PIN check(2)"; break;
    case SAS_START_WDT:     str3 = "start WDT";  break;
    default:
        str3 = "unknown";
        break;
    }

    NOTICE("SoC Activation: %s(%s), %s, 0x%x\n", str, str3, str2, data);
#endif
}

#define WDT_KICK_REG    (0x95)
#define WDT_LFSR        (0x96)
#define WDT_CFG0        (0x107)
#define WDT_CFG0_QNA    (0x1 << 2)
#define WDT_CFG0_16QNA  (0x1 << 1)
#define WDT_CFG0_EN     (0x1 << 0)
#define WDT_CFG3        (0x10A)
void PMIC_SM_12_wdt(void) {
    uint32_t tmp, question, answer0;

    i2c5_read(PMIC_ADDR_P, WDT_CFG0, &tmp);
    if ((tmp & WDT_CFG0_EN) == 0) {
        NOTICE("WDT is disabled\n");
        i2c5_read(PMIC_ADDR_P, 0x01, &tmp); /* set PAGE to 0x00 */
        return;
    }

    i2c5_read(PMIC_ADDR_P, WDT_LFSR, &question);    // Read the question
  if ((tmp & WDT_CFG0_16QNA) == 0) {
    // Grab only the first two bits to figure out what to do
    tmp = question >> 6;
    answer0 = question & 0x3F;

    micro_wait(4U);
    if (tmp == 0x1) {
        answer0 = (answer0 << 1) | (tmp << 6);
    } else if (tmp == 0x2) {
        answer0 = (answer0 >> 1) | (tmp << 6);
    } else if (tmp == 0x3) {
        answer0 = (~answer0) | (tmp << 6);
    } /* else if (tmp == 0x0) {
        // answer0 = (answer0) | (tmp << 6);
    } */
    i2c5_write(PMIC_ADDR_P, WDT_KICK_REG, answer0);
    NOTICE("4Q&A: DONE\n");
  } else {
    static uint8_t g_ary_wdt_answer[] = {
        0x00, 0x4F, 0x16, 0x59,
        0x8a, 0xc5, 0x9c, 0xd3,
        0x2d, 0x62, 0x3b, 0x74,
        0xa7, 0xe8, 0xb1, 0xfe,
    };
    uint32_t err_cnt = 0;

    // Grab the upper nibble to respond to, logic shift the lower nibble away
    tmp = question >> 4;
    answer0 = g_ary_wdt_answer[tmp];

    i2c5_write(PMIC_ADDR_P, WDT_KICK_REG, answer0);
    i2c5_read(PMIC_ADDR_P, WDT_CFG3, &question);
    err_cnt |= (question & 0xFF) << 24; // Check WDT Error counter
    micro_wait(4U);

    // For the second answer, take the first answer and flip the upper nibble
    i2c5_write(PMIC_ADDR_P, WDT_KICK_REG, (answer0 ^ 0xF0) & 0xFF);
    i2c5_read(PMIC_ADDR_P, WDT_CFG3, &question);
    err_cnt |= (question & 0xFF) << 16; // Check WDT Error counter
    micro_wait(4U);

    // For the third answer, take the first answer and flip the lower nibble
    i2c5_write(PMIC_ADDR_P, WDT_KICK_REG, (answer0 ^ 0x0F) & 0xFF);
    i2c5_read(PMIC_ADDR_P, WDT_CFG3, &question);
    err_cnt |= (question & 0xFF) << 8; // Check WDT Error counter
    micro_wait(4U);

    // For the fourth answer, take the first answer and invert the whole byte
    i2c5_write(PMIC_ADDR_P, WDT_KICK_REG, (answer0 ^ 0xFF) & 0xFF);
    i2c5_read(PMIC_ADDR_P, WDT_CFG3, &question);
    err_cnt |= (question & 0xFF) << 0; // Check WDT Error counter
    if (err_cnt) {
        NOTICE("16Q&A: Error 0x%x\n", err_cnt);
    } else {
        NOTICE("16Q&A: DONE\n");
    }
    i2c5_read(PMIC_ADDR_P, 0x01, &tmp); /* set PAGE to 0x00 */
  }
}

// External Pin Check for ECM
void SM_6_3_1(void) {}
// External Pin Check for RST
void SM_6_3_2(void) {}
// initial Toggle Test
void SM_5_3_6(void) {}
// check POST result of other than "stop" type hierachies
void SM_5_1(void) {}
// read Back Test for Integrity Check
void SM_6_23(void) {}
// self test for AES-ACC
void SM_4_21(void) {}
// self check for Field BIST
void SM_5_1_6(void) {}
// self check for Field BIST
void SM_5_2_6(void) {}
// write access protection check at start-up
void SM_6_2(void) {}
// timeout monitoring for confirmation of PHY start
void SM_6_11(void) {}
// initial loopback test using PWM
void SM_6_14_6(void) {}
// integrity check for Program Code
void SM_6_22(void) {}
// start-up test for THS
void SM_6_24_2(void) {}
// start-up test for CVM
void SM_6_24_3(void) {}
// Power isolation Cell Error Detector Test at start-up
void SM_6_29(void) {}
// clock monitor test at start-up
void SM_6_30(void) {}
