/*******************************************************************************
 * 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 2021-2024 Renesas Electronics Corporation All rights reserved.
 *******************************************************************************/

/*******************************************************************************
 * DESCRIPTION   : RPC driver
 ******************************************************************************/
/******************************************************************************
 * @file          rpc.c
 * - Version      : 0.08
 * @brief         Initial setting process of RPC.
 * .
 *****************************************************************************/
/******************************************************************************
 * History : DD.MM.YYYY Version  Description
 *         : 28.07.2021 0.01     First Release
 *         : 03.09.2021 0.02     Add rpc_release function.
 *         : 27.07.2022 0.03     Add QSPI Flash vendor ID check and QSPI Flash
 *         :                     command initialization.
 *         : 22.08.2022 0.04     Add DDR mode for QSPI Flash.
 *         : 21.09.2022 0.05     Fix comparison of test data
 *         :                     in adjust_strobe_timing function.
 *         : 12.01.2023 0.06     Add PFC setting to qspi_ddr_transfer_mode()
 *         :                     function.
 *         : 04.04.2023 0.07     Removed stdio.h.
 *         : 17.06.2024 0.08     Fix PUEN register setting when QSPI DDR mode.
 *****************************************************************************/

#include <stdint.h>
#include <stddef.h>
#include <rpc.h>
#include <rpc_register.h>
#include <mem_io.h>
#include <rst_register.h>
#include <image_load_flash.h>
#include <cpg_register.h>
#include <cpg.h>
#include <remap.h>
#include <log.h>
#include <pfc.h>
#include <bit.h>
#include <rpcqspidrv.h>

#define RPC_PHYCNT_DDRCAL               ((uint32_t)1U   << 19U)
#define RPC_PHYCNT_PHYMEM_SPI_DDR       ((uint32_t)1U   << 0U)
#define RPC_PHYCNT_PHYMEM_MASK          (0x00000003U)
#define RPC_DRCR_RCF                    ((uint32_t)1U   << 9U)
#define RPC_DRCR_SSLN_NEGATE            (0x01000000U)
#define RPC_DRCR_RBE_RBURST_MASK        (0x001F0000U)
#define RPC_DRCR_RBE_RBURST_1DATA       (0x00000000U) /* Smallest data unit (8byte) */
#define RPC_DRCR_RBE_RBURST_32DATA      (0x001F0000U) /* biggest data unit (256byte) */
#define RPC_DRCR_RBE_ENABLE             (0x00000100U)
#define RPC_DRCMR_CMD_SHIFT             (16U)
#define RPC_DRCMR_CMD_MASK              ((uint32_t)0xFFU << RPC_DRCMR_CMD_SHIFT)
#define RPC_DRCMR_OCMD_MASK             ((uint32_t)0xFFU << 0U)
#define RPC_DRCMR_HW_INIT               (0x00030000U)
#define RPC_DREAR_EAV_MASK              ((uint32_t)0xFFU << 16U)
#define RPC_DREAR_EAC_MASK              ((uint32_t)0x7U << 0U)
#define RPC_DREAR_EAC_EXT_ADDR_25BIT    ((uint32_t)0x1U << 0U)
#define RPC_DREAR_HW_INIT               (0x00000000U)
#define RPC_DRENR_CDB_MASK              (0xC0000000U)
#define RPC_DRENR_CDB_1BIT              (0x00000000U)
#define RPC_DRENR_ADB_MASK              (0x03000000U)
#define RPC_DRENR_ADB_4BIT              (0x02000000U)
#define RPC_DRENR_DRDB_MASK             (0x00030000U)
#define RPC_DRENR_DRDB_4BIT             (0x00020000U)
#define RPC_DRENR_DME_MASK              (0x00008000U)
#define RPC_DRENR_DME_ENABLE            (0x00008000U)
#define RPC_DRENR_CDE_ENABLE            ((uint32_t)0x1U << 14U)
#define RPC_DRENR_ADE_MASK              (0x00000F00U)
#define RPC_DRENR_ADE_32BIT_ADDR        ((uint32_t)0xFU << 8U)
#define RPC_DRENR_HW_INIT               (0x00004700U)
#define RPC_DRENR_TRANS_DISABLE         (0x00000000U)
#define RPC_SMCMR_CMD_SHIFT             (16U)
#define RPC_SMCMR_CMD_MASK              (0x00FF0000U)
#define RPC_SMCMR_OCMD_MASK             (0x000000FFU)
#define RPC_SMDRENR_HYPE_MASK           (0x00007000U)
#define RPC_SMDRENR_ADDRE               (0x00000100U)
#define RPC_SMDRENR_OPDRE               (0x00000010U)
#define RPC_SMDRENR_SPIDRE              (0x00000001U)
#define RPC_SMENR_CDB_MASK              (0xC0000000U)
#define RPC_SMENR_OCDB_MASK             (0x30000000U)
#define RPC_SMENR_ADB_MASK              (0x03000000U)
#define RPC_SMENR_OPDB_MASK             (0x00300000U)
#define RPC_SMENR_SPIDB_MASK            (0x00030000U)
#define RPC_SMENR_DME_EN                (0x00008000U)
#define RPC_SMENR_CDE_EN                (0x00004000U)
#define RPC_SMENR_OCDE_EN               (0x00001000U)
#define RPC_SMENR_ADE_MASK              (0x00000F00U)
#define RPC_SMENR_OPDE_MASK             (0x000000F0U)
#define RPC_SMENR_SPIDE_MASK            (0x0000000FU)
#define RPC_SMENR_SPIDE_SPI_32          (0x0000000FU)
#define RPC_SMCR_SSLKP                  (0x00000100U)
#define RPC_SMCR_SPIRE                  (0x00000004U)
#define RPC_SMCR_SPIWE                  (0x00000002U)
#define RPC_SMCR_SPIE                   (0x00000001U)
#define RPC_CMNCR_MD                    (0x80000000U)    /* bit[31]:Operating Mode Switch -> Manual mode */
#define RPC_CMNCR_MOIIO_MASK            (0x00FF0000U)
#define RPC_CMNCR_MOIIO3_HI_Z           (0x00C00000U)
#define RPC_CMNCR_MOIIO2_HI_Z           (0x00300000U)
#define RPC_CMNCR_MOIIO1_HI_Z           (0x000C0000U)
#define RPC_CMNCR_MOIIO0_HI_Z           (0x00030000U)
#define RPC_CMNCR_BSZ_MASK              (0x00000003U)
#define RPC_DRDRENR_DRDRE               (0x00000001U)
#define RPC_DRDRENR_ADDRE               (0x00000100U)
#define RPC_PHYOFFSET1_DDRTMG_MSK       (0x30000000U)
#define RPC_PHYOFFSET1_DDRTMG_DDR       (0x20000000U)

#define STRTIM_SMALLEST                 (0x0000000FU)
#define STRTIM_MASK_3                   (0x00000008U)
#define STRTIM_MASK                     (0x00000007U)
#define STRTIM_MATCH_ERROR              (0xFFFFFFFFU)

/* For PFC register */
#define PFC_PUEN3_QSPI0_IO3     (0x00010000U)       /* bit16 */
#define PFC_PUEN3_QSPI0_IO2     (0x00020000U)       /* bit17 */

/* verification data for strobe timing adjustment  */
#define QSPI_TESTDATA               (0x5A5AA5A5U)
#define QSPI_TESTDATA_OFFSET        (0x00000400U)
#define QSPI_TESTDATA_FLASH_ADDR    (FLASH_CONTENT_CERT_ADDR + QSPI_TESTDATA_OFFSET)    /* Offset 0x00240400 */

static inline void set_strtim(uint32_t strobe_timing);
#if (QSPI_DDR_MODE==1)
static void adjust_strobe_timing(void);
#endif

#if (QSPI_DDR_MODE==0)
void qspi_sdr_transfer_mode(uint32_t command)
{
    uint32_t reg;
    /* check the transfer end flag */
    rpc_end_state_check();

    /* For the initial setting flow of RPC, see Figure 112.12 in    */
    /* "R-Car Series, S4 Series User's Manual" and                  */
    /* "R-Car Series, V4H Series User's Manual".                    */
    /* This RPC setting is for S25FS512S device                     */
    /* A register that does not set a value expects                 */
    /* the initial value of HW.                                     */

    /* PHY calibration */
    set_strtim(STRTIM_SMALLEST);
    reg = mem_read32(RPC_PHYCNT);
    reg |= RPC_PHYCNT_CAL;
    mem_write32(RPC_PHYCNT, reg);
    /* External Address Space Read Mode */
    reg = mem_read32(RPC_CMNCR);
    reg &= ~(RPC_CMNCR_MD);
    mem_write32(RPC_CMNCR, reg);
    /* Read cache Flash */
    reg = mem_read32(RPC_DRCR);
    mem_write32(RPC_DRCR, (reg | RPC_DRCR_RCF));
    /* 32bit address read command*/
    reg = mem_read32(RPC_DRCMR);
    reg &= ~(RPC_DRCMR_CMD_MASK | RPC_DRCMR_OCMD_MASK);
    reg |= (command << RPC_SMCMR_CMD_SHIFT);
    mem_write32(RPC_DRCMR, reg);
    /* Extended external address valid range is [25:0]*/
    reg = mem_read32(RPC_DREAR);
    reg &= ~(RPC_DREAR_EAV_MASK | RPC_DREAR_EAC_MASK);
    reg |= RPC_DREAR_EAC_EXT_ADDR_25BIT;
    mem_write32(RPC_DREAR, reg);
    /* output command is 32bit width */
    reg = mem_read32(RPC_DRENR);
    reg &= ~(RPC_DRENR_CDE_ENABLE | RPC_DRENR_ADE_MASK);
    reg |= (RPC_DRENR_CDE_ENABLE | RPC_DRENR_ADE_32BIT_ADDR);
    mem_write32(RPC_DRENR, reg);
}
/* End of function rpc_init(void) */

#else
void qspi_ddr_transfer_mode(uint32_t command)
{
    uint32_t reg;

    /* check the transfer end flag */
    rpc_end_state_check();

    cpg_reg_write(CPG_RPCCKCR, CPG_RPCCKCR, RPC_CLK_160MHZ);    /* RPCD2 = 80MHz */

    /* Disable pull-up/down function of QSPI0_IO2/QSPI0_IO3 when QSPI DDR transfer mode. */
    reg  = mem_read32(PFC_PUEN3_RW);
    reg &= ~(PFC_PUEN3_QSPI0_IO3 | PFC_PUEN3_QSPI0_IO2);
    pfc_reg_write(PFC_PUEN3_RW, reg);

    /* For the initial setting flow of RPC, see Figure 112.12 in    */
    /* "R-Car Series, S4 Series User's Manual" and                  */
    /* "R-Car Series, V4H Series User's Manual".                    */
    /* This RPC setting is for S25FS512S device                     */
    /* A register that does not set a value expects                 */
    /* the initial value of HW.                                     */

    /* PHY calibration */
    set_strtim(STRTIM_SMALLEST);
    reg = mem_read32(RPC_PHYCNT);
    reg |= RPC_PHYCNT_DDRCAL;
    reg |= RPC_PHYCNT_PHYMEM_SPI_DDR;
    mem_write32(RPC_PHYCNT, reg);
    /* External Address Space Read Mode */
    reg = mem_read32(RPC_CMNCR);
    reg &= ~(RPC_CMNCR_MD | RPC_CMNCR_MOIIO_MASK | RPC_CMNCR_BSZ_MASK);
    reg |= (RPC_CMNCR_MOIIO0_HI_Z | RPC_CMNCR_MOIIO1_HI_Z | RPC_CMNCR_MOIIO2_HI_Z | RPC_CMNCR_MOIIO3_HI_Z);
    mem_write32(RPC_CMNCR, reg);
  /* Read cache Flash */
    reg = mem_read32(RPC_DRCR);
    reg &= ~(RPC_DRCR_RBE_RBURST_MASK);
    reg |= (RPC_DRCR_SSLN_NEGATE | RPC_DRCR_RBE_RBURST_1DATA | RPC_DRCR_RCF | RPC_DRCR_RBE_ENABLE);
    mem_write32(RPC_DRCR, (reg | RPC_DRCR_RCF));
    /* 32bit address read command*/
    reg = mem_read32(RPC_DRCMR);
    reg &= ~(RPC_DRCMR_CMD_MASK | RPC_DRCMR_OCMD_MASK);
    reg |= (command << RPC_SMCMR_CMD_SHIFT);
    mem_write32(RPC_DRCMR, reg);
    /* Extended external address valid range is [25:0]*/
    reg = mem_read32(RPC_DREAR);
    reg &= ~(RPC_DREAR_EAV_MASK | RPC_DREAR_EAC_MASK);
    reg |= RPC_DREAR_EAC_EXT_ADDR_25BIT;
    mem_write32(RPC_DREAR, reg);
    /* output command is 32bit width */
    reg = mem_read32(RPC_DRENR);
    reg &= ~(RPC_DRENR_CDB_MASK | RPC_DRENR_ADB_MASK   | RPC_DRENR_DRDB_MASK
        |    RPC_DRENR_DME_MASK | RPC_DRENR_CDE_ENABLE | RPC_DRENR_ADE_MASK);
    reg |= (RPC_DRENR_CDB_1BIT   | RPC_DRENR_ADB_4BIT   | RPC_DRENR_DRDB_4BIT
        |   RPC_DRENR_DME_ENABLE | RPC_DRENR_CDE_ENABLE | RPC_DRENR_ADE_32BIT_ADDR);
    mem_write32(RPC_DRENR, reg);
    /* Set Dummy Cycle */
    mem_write32(RPC_DRDMCR, (uint32_t)RCAR_QSPI_DDR_DUMMY_CYCLE - 1U);
    /* Specifies DDR transfer of the data read. */
    reg = mem_read32(RPC_DRDRENR);
    reg |= RPC_DRDRENR_DRDRE | RPC_DRDRENR_ADDRE;
    mem_write32(RPC_DRDRENR, reg);
    /* timing adjustment in DDR read operation. */
    reg = mem_read32(RPC_PHYOFFSET1);
    reg &= ~(RPC_PHYOFFSET1_DDRTMG_MSK);
    reg |= RPC_PHYOFFSET1_DDRTMG_DDR;
    mem_write32(RPC_PHYOFFSET1, reg);

    /* Change PHYCNT.DDRCAL */
    reg = mem_read32(RPC_PHYCNT);
    reg |=  RPC_PHYCNT_PHYMEM_SPI_DDR;
    reg &=  ~RPC_PHYCNT_CAL;
    reg |=  RPC_PHYCNT_DDRCAL;
    mem_write32(RPC_PHYCNT, reg);

    adjust_strobe_timing();

    /* Set data burst length to 256 byte */
    reg = mem_read32(RPC_DRCR);
    reg |= RPC_DRCR_RBE_RBURST_32DATA;
    mem_write32(RPC_DRCR, reg);
}

static void adjust_strobe_timing(void)
{
    uint32_t reg;
    uint32_t flash_data;
    uint32_t remap_flash_addr;
    uint32_t strobe_timing = STRTIM_SMALLEST;
    uint32_t first_match = STRTIM_MATCH_ERROR;
    uint32_t match_count = 0U;
    uint32_t loop;

    /* Convert verification data to logical addresses. */
    remap_register(QSPI_TESTDATA_FLASH_ADDR, &remap_flash_addr);

    INFO("Adjust strobe timing\n");

    INFO("QSPI_TESTDATA_FLASH_ADDR = 0x%08x\n",remap_flash_addr);

    for(loop = 0U; loop <= STRTIM_SMALLEST; loop++)
    {
        /* RPC Transfer Disable */
        mem_write32(RPC_DRENR, RPC_DRENR_TRANS_DISABLE);

        /* Read cache Flash */
        reg = mem_read32(RPC_DRCR);
        mem_write32(RPC_DRCR, (reg | RPC_DRCR_RCF));

        /* set strobe timing */
        set_strtim(strobe_timing);

        /* RPC Transfer Enable */
        reg = mem_read32(RPC_DRENR);
        reg &= ~(RPC_DRENR_CDB_MASK | RPC_DRENR_ADB_MASK   | RPC_DRENR_DRDB_MASK
            |    RPC_DRENR_DME_MASK | RPC_DRENR_CDE_ENABLE | RPC_DRENR_ADE_MASK);
        reg |= (RPC_DRENR_CDB_1BIT   | RPC_DRENR_ADB_4BIT   | RPC_DRENR_DRDB_4BIT
            |   RPC_DRENR_DME_ENABLE | RPC_DRENR_CDE_ENABLE | RPC_DRENR_ADE_32BIT_ADDR);
        mem_write32(RPC_DRENR, reg);

        /* Read verification Data on QSPIFlash */
        flash_data = mem_read32(remap_flash_addr);

        /* check the transfer end flag */
        rpc_end_state_check();

        INFO("strobe timing:0x%x\tflash_data:0x%x\ttest_data:0x%x\n", strobe_timing, flash_data, QSPI_TESTDATA);

        /* Comparison of Verification Data */
        if(flash_data == QSPI_TESTDATA)
        {
            /* First match of validation data. */
            if(first_match == STRTIM_MATCH_ERROR)
            {
                first_match = strobe_timing;
            }
            match_count++;
        }
        else
        {
            /* If out of timing to match */
            if(first_match != STRTIM_MATCH_ERROR)
            {
                /* Terminate the exploration of Strobe timing. */
                break;
            }
        }
        strobe_timing--;
    }

    /* Verification Data is not matched */
    if(first_match == STRTIM_MATCH_ERROR)
    {
        ERROR("Failed Strobe timing adjustment of DDR transfer mode.\n");
        panic;
    }

    INFO("first_match:0x%x\tmatch_count:0x%x\n", first_match, match_count);
    remap_unregister(remap_flash_addr);

    /* strobe timing value adjustment */
    strobe_timing = first_match - (match_count / 2U);

    /* set strobe timing */
    set_strtim(strobe_timing);
    INFO("RPC_PHYCNT\t = 0x%08x\n",mem_read32(RPC_PHYCNT));
}
#endif

static inline void set_strtim(uint32_t strobe_timing)
{
    uint32_t reg;
    reg = mem_read32(RPC_PHYCNT);
    reg &= ~((STRTIM_MASK_3 << 24U) | (STRTIM_MASK << 15U));
    reg |= ((strobe_timing & STRTIM_MASK_3) << 24U);    /* bit[27] */
       reg |= ((strobe_timing & STRTIM_MASK) << 15U);   /* bit[17:15] */
    mem_write32(RPC_PHYCNT, reg);
}
