/*
* Copyright (c) 2020 - 2024 Renesas Electronics Corporation and/or its affiliates
*
* SPDX-License-Identifier: BSD-3-Clause
*/

/*******************************************************************************************************************//**
 * @addtogroup BSP_SDRAM
 * @brief SDRAM initialization
 *
 * This file contains code that the initializes SDRAMC and SDR SDRAM device memory.
 * @{
 **********************************************************************************************************************/

/***********************************************************************************************************************
 * Includes
 **********************************************************************************************************************/
#include "bsp_api.h"

/***********************************************************************************************************************
 * Macro definitions
 **********************************************************************************************************************/

/** Due to hardware limitations of the SDRAM peripheral,
 * it is not expected any of these need to be changable by end user.
 * Only sequential, single access at a time is supported. */
#define BSP_PRV_SDRAM_MR_WB_SINGLE_LOC_ACC    (1U) /* MR.M9                : Single Location Access */
#define BSP_PRV_SDRAM_MR_OP_MODE              (0U) /* MR.M8:M7             : Standard Operation */
#define BSP_PRV_SDRAM_MR_BT_SEQUENTIAL        (0U) /* MR.M3 Burst Type     : Sequential */
#define BSP_PRV_SDRAM_MR_BURST_LENGTH         (0U) /* MR.M2:M0 Burst Length: 0(1 burst) */

/***********************************************************************************************************************
 * Typedef definitions
 **********************************************************************************************************************/

/***********************************************************************************************************************
 * Private function prototypes
 **********************************************************************************************************************/

/***********************************************************************************************************************
 * Private global variables
 **********************************************************************************************************************/

/***********************************************************************************************************************
 * Functions
 **********************************************************************************************************************/

#if 0 != BSP_FEATURE_SDRAM_START_ADDRESS

/*******************************************************************************************************************//**
 * @brief   Initializes SDRAM.
 * @param   init_memory  If true, this function will execute the initialization of the external modules.
 *                       Otherwise, it will only initialize the SDRAMC and leave the memory in self-refresh mode.
 *
 * This function initializes SDRAMC and SDR SDRAM device.
 *
 * @note This function must only be called once after reset.
 **********************************************************************************************************************/
void R_BSP_SdramInit (bool init_memory)
{
    /** Setting for SDRAM initialization sequence */
    while (R_BUS->SDRAM.SDSR)
    {
        /* According to h/w manual, need to confirm that all the status bits in SDSR are 0 before SDICR modification. */
    }

    /* Must only write to SDIR once after reset. */
    R_BUS->SDRAM.SDIR = ((BSP_CFG_SDRAM_INIT_ARFI - 3U) << R_BUS_SDRAM_SDIR_ARFI_Pos) |
                        (BSP_CFG_SDRAM_INIT_ARFC << R_BUS_SDRAM_SDIR_ARFC_Pos) |
                        ((BSP_CFG_SDRAM_INIT_PRC - 3U) << R_BUS_SDRAM_SDIR_PRC_Pos);

    R_BUS->SDRAM.SDCCR = (BSP_CFG_SDRAM_BUS_WIDTH << R_BUS_SDRAM_SDCCR_BSIZE_Pos); /* set SDRAM bus width */

    if (init_memory)
    {
        /* Enable the SDCLK output. */
        R_BSP_RegisterProtectDisable(BSP_REG_PROTECT_CGC);
        R_SYSTEM->SDCKOCR = 1;
        R_BSP_RegisterProtectEnable(BSP_REG_PROTECT_CGC);

        /** If requested, start SDRAM initialization sequence. */
        R_BUS->SDRAM.SDICR = 1U;
        while (R_BUS->SDRAM.SDSR_b.INIST)
        {
            /* Wait the end of initialization sequence. */
        }
    }

    /** Setting for SDRAM controller */
    R_BUS->SDRAM.SDAMOD = BSP_CFG_SDRAM_ACCESS_MODE; /* enable continuous access */
    R_BUS->SDRAM.SDCMOD = BSP_CFG_SDRAM_ENDIAN_MODE; /* set endian mode for SDRAM address space */

    while (R_BUS->SDRAM.SDSR)
    {
        /* According to h/w manual, need to confirm that all the status bits in SDSR are 0 before SDMOD modification. */
    }

    if (init_memory)
    {
        /** Using LMR command, program the mode register */
        R_BUS->SDRAM.SDMOD = (BSP_PRV_SDRAM_MR_WB_SINGLE_LOC_ACC << 9) |
                             (BSP_PRV_SDRAM_MR_OP_MODE << 7) |
                             (BSP_CFG_SDRAM_TCL << 4) |
                             (BSP_PRV_SDRAM_MR_BT_SEQUENTIAL << 3) |
                             (BSP_PRV_SDRAM_MR_BURST_LENGTH << 0);

        /** wait at least tMRD time */
        while (R_BUS->SDRAM.SDSR_b.MRSST)
        {
            /* Wait until Mode Register setting done. */
        }
    }

    /** Set timing parameters for SDRAM. Must do in single write. */
    R_BUS->SDRAM.SDTR = ((BSP_CFG_SDRAM_TRAS - 1U) << R_BUS_SDRAM_SDTR_RAS_Pos) |
                        ((BSP_CFG_SDRAM_TRCD - 1U) << R_BUS_SDRAM_SDTR_RCD_Pos) |
                        ((BSP_CFG_SDRAM_TRP - 1U) << R_BUS_SDRAM_SDTR_RP_Pos) |
                        ((BSP_CFG_SDRAM_TWR - 1U) << R_BUS_SDRAM_SDTR_WR_Pos) |
                        (BSP_CFG_SDRAM_TCL << R_BUS_SDRAM_SDTR_CL_Pos);

    /** Set row address offset for target SDRAM */
    R_BUS->SDRAM.SDADR = BSP_CFG_SDRAM_MULTIPLEX_ADDR_SHIFT;

    /* Set Auto-Refresh timings. */
    R_BUS->SDRAM.SDRFCR = ((BSP_CFG_SDRAM_TREFW - 1U) << R_BUS_SDRAM_SDRFCR_REFW_Pos) |
                          ((BSP_CFG_SDRAM_TRFC - 1U) << R_BUS_SDRAM_SDRFCR_RFC_Pos);

    /** Start Auto-refresh */
    R_BUS->SDRAM.SDRFEN = 1U;

    if (init_memory)
    {
        /** Enable SDRAM access */
        R_BUS->SDRAM.SDCCR = R_BUS_SDRAM_SDCCR_EXENB_Msk | (BSP_CFG_SDRAM_BUS_WIDTH << R_BUS_SDRAM_SDCCR_BSIZE_Pos);
    }
    else
    {
        /* If not initializing memory modules, start in self-refresh mode. */
        while (R_BUS->SDRAM.SDCCR_b.EXENB || (0U != R_BUS->SDRAM.SDSR))
        {
            /* Wait for access to be disabled and no status bits set. */
        }

        /* Enable the self-refresh mode. */
        R_BUS->SDRAM.SDSELF = 1U;
    }
}

/*******************************************************************************************************************//**
 * @brief   Changes SDRAM from Auto-refresh to Self-refresh
 *
 * This function allows Software Standby and Deep Software Standby modes to be entered without data loss.
 *
 * @note SDRAM cannot be accessed after calling this function. Use @ref R_BSP_SdramSelfRefreshDisable to resume normal
 *  SDRAM operation.
 **********************************************************************************************************************/
void R_BSP_SdramSelfRefreshEnable (void)
{
    R_BUS->SDRAM.SDCCR = (BSP_CFG_SDRAM_BUS_WIDTH << R_BUS_SDRAM_SDCCR_BSIZE_Pos);
    while (R_BUS->SDRAM.SDCCR_b.EXENB || (0U != R_BUS->SDRAM.SDSR))
    {
        /* Wait for access to be disabled and no status bits set. */
    }

    /* Enable the self-refresh mode. */
    R_BUS->SDRAM.SDSELF = 1U;
}

/*******************************************************************************************************************//**
 * @brief   Changes SDRAM from Self-refresh to Auto-refresh
 *
 * This function changes back to Auto-refresh and allows normal SDRAM operation to resume.
 *
 **********************************************************************************************************************/
void R_BSP_SdramSelfRefreshDisable (void)
{
    if (0 == R_SYSTEM->SDCKOCR)
    {
        /* Enable the SDCLK output. It may not already be enabled here if recovering from Deep Software Standby. */
        R_BSP_RegisterProtectDisable(BSP_REG_PROTECT_CGC);
        R_SYSTEM->SDCKOCR = 1;
        R_BSP_RegisterProtectEnable(BSP_REG_PROTECT_CGC);
    }

    while (0U != R_BUS->SDRAM.SDSR)
    {
        /* Wait for all status bits to be cleared. */
    }

    /* Disable the self-refresh mode. */
    R_BUS->SDRAM.SDSELF = 0U;

    /* Reenable SDRAM bus access. */
    R_BUS->SDRAM.SDCCR = R_BUS_SDRAM_SDCCR_EXENB_Msk | (BSP_CFG_SDRAM_BUS_WIDTH << R_BUS_SDRAM_SDCCR_BSIZE_Pos);
}

#endif

/** @} (end addtogroup BSP_SDRAM) */
