/********************************** (C) COPYRIGHT  *******************************
* File Name          : ch32f20x_fsmc.c
* Author             : WCH
* Version            : V1.0.0
* Date               : 2021/08/08
* Description        : This file provides all the FSMC firmware functions.
* Copyright (c) 2021 Nanjing Qinheng Microelectronics Co., Ltd.
* SPDX-License-Identifier: Apache-2.0
*******************************************************************************/
#include "ch32f20x_fsmc.h"
#include "ch32f20x_rcc.h"


/* FSMC BCRx Mask */
#define BCR_MBKEN_Set                       ((uint32_t)0x00000001)
#define BCR_MBKEN_Reset                     ((uint32_t)0x000FFFFE)
#define BCR_FACCEN_Set                      ((uint32_t)0x00000040)

/* FSMC PCRx Mask */
#define PCR_PBKEN_Set                       ((uint32_t)0x00000004)
#define PCR_PBKEN_Reset                     ((uint32_t)0x000FFFFB)
#define PCR_ECCEN_Set                       ((uint32_t)0x00000040)
#define PCR_ECCEN_Reset                     ((uint32_t)0x000FFFBF)
#define PCR_MemoryType_NAND                 ((uint32_t)0x00000008)


/*********************************************************************
 * @fn      FSMC_NORSRAMDeInit
 *
 * @brief   Deinitializes the FSMC NOR/SRAM Banks registers to their default
 *        reset values.
 *
 * @param   FSMC_Bank-
 *            FSMC_Bank1_NORSRAM1 - FSMC Bank1 NOR/SRAM1.
 *
 * @return  none
 */
void FSMC_NORSRAMDeInit( uint32_t FSMC_Bank )
{
    if( FSMC_Bank == FSMC_Bank1_NORSRAM1 )
    {
        FSMC_Bank1->BTCR[FSMC_Bank] = 0x000030DB;
    }
    else
    {
        FSMC_Bank1->BTCR[FSMC_Bank] = 0x000030D2;
    }
    FSMC_Bank1->BTCR[FSMC_Bank + 1] = 0x0FFFFFFF;
    FSMC_Bank1E->BWTR[FSMC_Bank] = 0x0FFFFFFF;
}

/*********************************************************************
 * @fn      FSMC_NANDDeInit
 *
 * @brief   Deinitializes the FSMC NAND Banks registers to their default
 *        reset values.
 *
 * @param   FSMC_Bank -
 *            FSMC_Bank2_NAND - FSMC Bank2 NAND.
 *
 * @return  none
 */
void FSMC_NANDDeInit( uint32_t FSMC_Bank )
{
    if( FSMC_Bank == FSMC_Bank2_NAND )
    {
        FSMC_Bank2->PCR2 = 0x00000018;
        FSMC_Bank2->SR2 = 0x00000040;
        FSMC_Bank2->PMEM2 = 0xFCFCFCFC;
        FSMC_Bank2->PATT2 = 0xFCFCFCFC;
    }
}

/*********************************************************************
 * @fn      FSMC_NORSRAMInit
 *
 * @brief   Initializes the FSMC NOR/SRAM Banks according to the specified
 *        parameters in the FSMC_NORSRAMInitStruct.
 *
 * @param   SMC_NORSRAMInitStruct:pointer to a FSMC_NORSRAMInitTypeDef
 *        structure that contains the configuration information for the FSMC NOR/SRAM
 *        specified Banks.
 *
 * @return  none
 */
void FSMC_NORSRAMInit( FSMC_NORSRAMInitTypeDef *FSMC_NORSRAMInitStruct )
{
    FSMC_Bank1->BTCR[FSMC_NORSRAMInitStruct->FSMC_Bank] =
        ( uint32_t )FSMC_NORSRAMInitStruct->FSMC_DataAddressMux |
        FSMC_NORSRAMInitStruct->FSMC_MemoryType |
        FSMC_NORSRAMInitStruct->FSMC_MemoryDataWidth |
        FSMC_NORSRAMInitStruct->FSMC_BurstAccessMode |
        FSMC_NORSRAMInitStruct->FSMC_AsynchronousWait |
        FSMC_NORSRAMInitStruct->FSMC_WaitSignalPolarity |
        FSMC_NORSRAMInitStruct->FSMC_WrapMode |
        FSMC_NORSRAMInitStruct->FSMC_WaitSignalActive |
        FSMC_NORSRAMInitStruct->FSMC_WriteOperation |
        FSMC_NORSRAMInitStruct->FSMC_WaitSignal |
        FSMC_NORSRAMInitStruct->FSMC_ExtendedMode |
        FSMC_NORSRAMInitStruct->FSMC_WriteBurst;

    if( FSMC_NORSRAMInitStruct->FSMC_MemoryType == FSMC_MemoryType_NOR )
    {
        FSMC_Bank1->BTCR[FSMC_NORSRAMInitStruct->FSMC_Bank] |= ( uint32_t )BCR_FACCEN_Set;
    }

    FSMC_Bank1->BTCR[FSMC_NORSRAMInitStruct->FSMC_Bank + 1] =
        ( uint32_t )FSMC_NORSRAMInitStruct->FSMC_ReadWriteTimingStruct->FSMC_AddressSetupTime |
        ( FSMC_NORSRAMInitStruct->FSMC_ReadWriteTimingStruct->FSMC_AddressHoldTime << 4 ) |
        ( FSMC_NORSRAMInitStruct->FSMC_ReadWriteTimingStruct->FSMC_DataSetupTime << 8 ) |
        ( FSMC_NORSRAMInitStruct->FSMC_ReadWriteTimingStruct->FSMC_BusTurnAroundDuration << 16 ) |
        ( FSMC_NORSRAMInitStruct->FSMC_ReadWriteTimingStruct->FSMC_CLKDivision << 20 ) |
        ( FSMC_NORSRAMInitStruct->FSMC_ReadWriteTimingStruct->FSMC_DataLatency << 24 ) |
        FSMC_NORSRAMInitStruct->FSMC_ReadWriteTimingStruct->FSMC_AccessMode;

    if( FSMC_NORSRAMInitStruct->FSMC_ExtendedMode == FSMC_ExtendedMode_Enable )
    {
        FSMC_Bank1E->BWTR[FSMC_NORSRAMInitStruct->FSMC_Bank] =
            ( uint32_t )FSMC_NORSRAMInitStruct->FSMC_WriteTimingStruct->FSMC_AddressSetupTime |
            ( FSMC_NORSRAMInitStruct->FSMC_WriteTimingStruct->FSMC_AddressHoldTime << 4 ) |
            ( FSMC_NORSRAMInitStruct->FSMC_WriteTimingStruct->FSMC_DataSetupTime << 8 ) |
            ( FSMC_NORSRAMInitStruct->FSMC_WriteTimingStruct->FSMC_CLKDivision << 20 ) |
            ( FSMC_NORSRAMInitStruct->FSMC_WriteTimingStruct->FSMC_DataLatency << 24 ) |
            FSMC_NORSRAMInitStruct->FSMC_WriteTimingStruct->FSMC_AccessMode;
    }
    else
    {
        FSMC_Bank1E->BWTR[FSMC_NORSRAMInitStruct->FSMC_Bank] = 0x0FFFFFFF;
    }
}

/*********************************************************************
 * @fn      FSMC_NANDInit
 *
 * @brief   Initializes the FSMC NAND Banks according to the specified
 *        parameters in the FSMC_NANDInitStruct.
 *
 * @param   FSMC_NANDInitStruct - pointer to a FSMC_NANDInitTypeDef
 *        structure that contains the configuration information for the FSMC
 *        NAND specified Banks.
 *
 * @return  none
 */
void FSMC_NANDInit( FSMC_NANDInitTypeDef *FSMC_NANDInitStruct )
{
    uint32_t tmppcr = 0x00000000, tmppmem = 0x00000000, tmppatt = 0x00000000;

    tmppcr = ( uint32_t )FSMC_NANDInitStruct->FSMC_Waitfeature |
             PCR_MemoryType_NAND |
             FSMC_NANDInitStruct->FSMC_MemoryDataWidth |
             FSMC_NANDInitStruct->FSMC_ECC |
             FSMC_NANDInitStruct->FSMC_ECCPageSize |
             ( FSMC_NANDInitStruct->FSMC_TCLRSetupTime << 9 ) |
             ( FSMC_NANDInitStruct->FSMC_TARSetupTime << 13 );

    tmppmem = ( uint32_t )FSMC_NANDInitStruct->FSMC_CommonSpaceTimingStruct->FSMC_SetupTime |
              ( FSMC_NANDInitStruct->FSMC_CommonSpaceTimingStruct->FSMC_WaitSetupTime << 8 ) |
              ( FSMC_NANDInitStruct->FSMC_CommonSpaceTimingStruct->FSMC_HoldSetupTime << 16 ) |
              ( FSMC_NANDInitStruct->FSMC_CommonSpaceTimingStruct->FSMC_HiZSetupTime << 24 );

    tmppatt = ( uint32_t )FSMC_NANDInitStruct->FSMC_AttributeSpaceTimingStruct->FSMC_SetupTime |
              ( FSMC_NANDInitStruct->FSMC_AttributeSpaceTimingStruct->FSMC_WaitSetupTime << 8 ) |
              ( FSMC_NANDInitStruct->FSMC_AttributeSpaceTimingStruct->FSMC_HoldSetupTime << 16 ) |
              ( FSMC_NANDInitStruct->FSMC_AttributeSpaceTimingStruct->FSMC_HiZSetupTime << 24 );

    if( FSMC_NANDInitStruct->FSMC_Bank == FSMC_Bank2_NAND )
    {
        FSMC_Bank2->PCR2 = tmppcr;
        FSMC_Bank2->PMEM2 = tmppmem;
        FSMC_Bank2->PATT2 = tmppatt;
    }
}

/*********************************************************************
 * @fn      FSMC_NORSRAMStructInit
 *
 * @brief   Fills each FSMC_NORSRAMInitStruct member with its default value.
 *
 * @param   FSMC_NORSRAMInitStruct - pointer to a FSMC_NORSRAMInitTypeDef
 *        structure which will be initialized.
 *
 * @return  none
 */
void FSMC_NORSRAMStructInit( FSMC_NORSRAMInitTypeDef *FSMC_NORSRAMInitStruct )
{
    FSMC_NORSRAMInitStruct->FSMC_Bank = FSMC_Bank1_NORSRAM1;
    FSMC_NORSRAMInitStruct->FSMC_DataAddressMux = FSMC_DataAddressMux_Enable;
    FSMC_NORSRAMInitStruct->FSMC_MemoryType = FSMC_MemoryType_SRAM;
    FSMC_NORSRAMInitStruct->FSMC_MemoryDataWidth = FSMC_MemoryDataWidth_8b;
    FSMC_NORSRAMInitStruct->FSMC_BurstAccessMode = FSMC_BurstAccessMode_Disable;
    FSMC_NORSRAMInitStruct->FSMC_AsynchronousWait = FSMC_AsynchronousWait_Disable;
    FSMC_NORSRAMInitStruct->FSMC_WaitSignalPolarity = FSMC_WaitSignalPolarity_Low;
    FSMC_NORSRAMInitStruct->FSMC_WrapMode = FSMC_WrapMode_Disable;
    FSMC_NORSRAMInitStruct->FSMC_WaitSignalActive = FSMC_WaitSignalActive_BeforeWaitState;
    FSMC_NORSRAMInitStruct->FSMC_WriteOperation = FSMC_WriteOperation_Enable;
    FSMC_NORSRAMInitStruct->FSMC_WaitSignal = FSMC_WaitSignal_Enable;
    FSMC_NORSRAMInitStruct->FSMC_ExtendedMode = FSMC_ExtendedMode_Disable;
    FSMC_NORSRAMInitStruct->FSMC_WriteBurst = FSMC_WriteBurst_Disable;
    FSMC_NORSRAMInitStruct->FSMC_ReadWriteTimingStruct->FSMC_AddressSetupTime = 0xF;
    FSMC_NORSRAMInitStruct->FSMC_ReadWriteTimingStruct->FSMC_AddressHoldTime = 0xF;
    FSMC_NORSRAMInitStruct->FSMC_ReadWriteTimingStruct->FSMC_DataSetupTime = 0xFF;
    FSMC_NORSRAMInitStruct->FSMC_ReadWriteTimingStruct->FSMC_BusTurnAroundDuration = 0xF;
    FSMC_NORSRAMInitStruct->FSMC_ReadWriteTimingStruct->FSMC_CLKDivision = 0xF;
    FSMC_NORSRAMInitStruct->FSMC_ReadWriteTimingStruct->FSMC_DataLatency = 0xF;
    FSMC_NORSRAMInitStruct->FSMC_ReadWriteTimingStruct->FSMC_AccessMode = FSMC_AccessMode_A;
    FSMC_NORSRAMInitStruct->FSMC_WriteTimingStruct->FSMC_AddressSetupTime = 0xF;
    FSMC_NORSRAMInitStruct->FSMC_WriteTimingStruct->FSMC_AddressHoldTime = 0xF;
    FSMC_NORSRAMInitStruct->FSMC_WriteTimingStruct->FSMC_DataSetupTime = 0xFF;
    FSMC_NORSRAMInitStruct->FSMC_WriteTimingStruct->FSMC_BusTurnAroundDuration = 0xF;
    FSMC_NORSRAMInitStruct->FSMC_WriteTimingStruct->FSMC_CLKDivision = 0xF;
    FSMC_NORSRAMInitStruct->FSMC_WriteTimingStruct->FSMC_DataLatency = 0xF;
    FSMC_NORSRAMInitStruct->FSMC_WriteTimingStruct->FSMC_AccessMode = FSMC_AccessMode_A;
}

/*********************************************************************
 * @fn      FSMC_NANDStructInit
 *
 * @brief   Fills each FSMC_NANDInitStruct member with its default value.
 *
 * @param   FSMC_NANDInitStruct - pointer to a FSMC_NANDInitTypeDef
 *        structure which will be initialized.
 *
 * @return  none
 */
void FSMC_NANDStructInit( FSMC_NANDInitTypeDef *FSMC_NANDInitStruct )
{
    FSMC_NANDInitStruct->FSMC_Bank = FSMC_Bank2_NAND;
    FSMC_NANDInitStruct->FSMC_Waitfeature = FSMC_Waitfeature_Disable;
    FSMC_NANDInitStruct->FSMC_MemoryDataWidth = FSMC_MemoryDataWidth_8b;
    FSMC_NANDInitStruct->FSMC_ECC = FSMC_ECC_Disable;
    FSMC_NANDInitStruct->FSMC_ECCPageSize = FSMC_ECCPageSize_256Bytes;
    FSMC_NANDInitStruct->FSMC_TCLRSetupTime = 0x0;
    FSMC_NANDInitStruct->FSMC_TARSetupTime = 0x0;
    FSMC_NANDInitStruct->FSMC_CommonSpaceTimingStruct->FSMC_SetupTime = 0xFC;
    FSMC_NANDInitStruct->FSMC_CommonSpaceTimingStruct->FSMC_WaitSetupTime = 0xFC;
    FSMC_NANDInitStruct->FSMC_CommonSpaceTimingStruct->FSMC_HoldSetupTime = 0xFC;
    FSMC_NANDInitStruct->FSMC_CommonSpaceTimingStruct->FSMC_HiZSetupTime = 0xFC;
    FSMC_NANDInitStruct->FSMC_AttributeSpaceTimingStruct->FSMC_SetupTime = 0xFC;
    FSMC_NANDInitStruct->FSMC_AttributeSpaceTimingStruct->FSMC_WaitSetupTime = 0xFC;
    FSMC_NANDInitStruct->FSMC_AttributeSpaceTimingStruct->FSMC_HoldSetupTime = 0xFC;
    FSMC_NANDInitStruct->FSMC_AttributeSpaceTimingStruct->FSMC_HiZSetupTime = 0xFC;
}
/*********************************************************************
 * @fn      FSMC_NORSRAMCmd
 *
 * @brief   Enables or disables the specified NOR/SRAM Memory Bank.
 *
 * @param   FSMC_Bank - specifies the FSMC Bank to be used
 *            FSMC_Bank1_NORSRAM1 - FSMC Bank1 NOR/SRAM1
 *          NewState:ENABLE or DISABLE.
 *
 * @return  none
 */
void FSMC_NORSRAMCmd( uint32_t FSMC_Bank, FunctionalState NewState )
{

    if( NewState != DISABLE )
    {
        FSMC_Bank1->BTCR[FSMC_Bank] |= BCR_MBKEN_Set;
    }
    else
    {
        FSMC_Bank1->BTCR[FSMC_Bank] &= BCR_MBKEN_Reset;
    }
}

/*********************************************************************
 * @fn      FSMC_NANDCmd
 *
 * @brief   Enables or disables the specified NAND Memory Bank.
 *
 * @param   FSMC_Bank - specifies the FSMC Bank to be used
 *            FSMC_Bank2_NAND - FSMC Bank2 NAND
 *          NewStat - ENABLE or DISABLE.
 *
 * @return  none
 */
void FSMC_NANDCmd( uint32_t FSMC_Bank, FunctionalState NewState )
{

    if( NewState != DISABLE )
    {
        if( FSMC_Bank == FSMC_Bank2_NAND )
        {
            FSMC_Bank2->PCR2 |= PCR_PBKEN_Set;
        }
    }
    else
    {
        if( FSMC_Bank == FSMC_Bank2_NAND )
        {
            FSMC_Bank2->PCR2 &= PCR_PBKEN_Reset;
        }
    }
}

/*********************************************************************
 * @fn      FSMC_NANDECCCmd
 *
 * @brief   Enables or disables the FSMC NAND ECC feature.
 *
 * @param   FSMC_Bank - specifies the FSMC Bank to be used
 *            FSMC_Bank2_NAND - FSMC Bank2 NAND
 *          NewState - ENABLE or DISABLE.
 *
 * @return  none
 */
void FSMC_NANDECCCmd( uint32_t FSMC_Bank, FunctionalState NewState )
{
    if( NewState != DISABLE )
    {
        if( FSMC_Bank == FSMC_Bank2_NAND )
        {
            FSMC_Bank2->PCR2 |= PCR_ECCEN_Set;
        }
    }
    else
    {
        if( FSMC_Bank == FSMC_Bank2_NAND )
        {
            FSMC_Bank2->PCR2 &= PCR_ECCEN_Reset;
        }
    }
}

/*********************************************************************
 * @fn      FSMC_GetECC
 *
 * @brief   Returns the error correction code register value.
 *
 * @param   FSMC_Bank - specifies the FSMC Bank to be used
 *            FSMC_Bank2_NAND - FSMC Bank2 NAND
 *          NewState - ENABLE or DISABLE.
 *
 * @return  eccval - The Error Correction Code (ECC) value.
 */
uint32_t FSMC_GetECC( uint32_t FSMC_Bank )
{
    uint32_t eccval = 0x00000000;

    if( FSMC_Bank == FSMC_Bank2_NAND )
    {
        eccval = FSMC_Bank2->ECCR2;
    }
    return( eccval );
}

/*********************************************************************
 * @fn      FSMC_ITConfig
 *
 * @brief   Enables or disables the specified FSMC interrupts.
 *
 * @param   FSMC_Bank - specifies the FSMC Bank to be used
 *            FSMC_Bank2_NAND - FSMC Bank2 NAND
 *          FSMC_IT - specifies the FSMC interrupt sources to be enabled or disabled.
 *            FSMC_IT_RisingEdge - Rising edge detection interrupt.
 *            FSMC_IT_Level - Level edge detection interrupt.
 *            FSMC_IT_FallingEdge - Falling edge detection interrupt.
 *          NewState - ENABLE or DISABLE.
 *
 * @return  none
 */
void FSMC_ITConfig( uint32_t FSMC_Bank, uint32_t FSMC_IT, FunctionalState NewState )
{

    if( NewState != DISABLE )
    {
        if( FSMC_Bank == FSMC_Bank2_NAND )
        {
            FSMC_Bank2->SR2 |= FSMC_IT;
        }
    }
    else
    {
        if( FSMC_Bank == FSMC_Bank2_NAND )
        {

            FSMC_Bank2->SR2 &= ( uint32_t )~FSMC_IT;
        }
    }
}

/*********************************************************************
 * @fn      FSMC_GetFlagStatus
 *
 * @brief   Checks whether the specified FSMC flag is set or not.
 *
 * @param   FSMC_Bank - specifies the FSMC Bank to be used
 *            FSMC_Bank2_NAND - FSMC Bank2 NAND
 *          FSMC_FLAG - specifies the flag to check.
 *            FSMC_FLAG_RisingEdge - Rising egde detection Flag.
 *            FSMC_FLAG_Level - Level detection Flag.
 *            FSMC_FLAG_FallingEdge - Falling egde detection Flag.
 *            FSMC_FLAG_FEMPT - Fifo empty Flag.
 *          NewState - ENABLE or DISABLE.
 *
 * @return  FlagStatus - The new state of FSMC_FLAG (SET or RESET).
 */
FlagStatus FSMC_GetFlagStatus( uint32_t FSMC_Bank, uint32_t FSMC_FLAG )
{
    FlagStatus bitstatus = RESET;
    uint32_t tmpsr = 0x00000000;


    if( FSMC_Bank == FSMC_Bank2_NAND )
    {
        tmpsr = FSMC_Bank2->SR2;
    }

    if( ( tmpsr & FSMC_FLAG ) != ( uint16_t )RESET )
    {
        bitstatus = SET;
    }
    else
    {
        bitstatus = RESET;
    }

    return bitstatus;
}

/*********************************************************************
 * @fn      FSMC_ClearFlag
 *
 * @brief   Clears the FSMC's pending flags.
 *
 * @param   FSMC_Bank - specifies the FSMC Bank to be used
 *            FSMC_Bank2_NAND - FSMC Bank2 NAND
 *          FSMC_FLAG - specifies the flag to check.
 *            FSMC_FLAG_RisingEdge - Rising egde detection Flag.
 *            FSMC_FLAG_Level - Level detection Flag.
 *            FSMC_FLAG_FallingEdge - Falling egde detection Flag.
 *
 * @return  none
 */
void FSMC_ClearFlag( uint32_t FSMC_Bank, uint32_t FSMC_FLAG )
{

    if( FSMC_Bank == FSMC_Bank2_NAND )
    {
        FSMC_Bank2->SR2 &= ~FSMC_FLAG;
    }
}

/*********************************************************************
 * @fn      FSMC_GetITStatus
 *
 * @brief   Checks whether the specified FSMC interrupt has occurred or not.
 *
 * @param   FSMC_Bank - specifies the FSMC Bank to be used
 *            FSMC_Bank2_NAND - FSMC Bank2 NAND
 *          FSMC_IT - specifies the FSMC interrupt source to check.
 *            FSMC_IT_RisingEdge - Rising edge detection interrupt.
 *            FSMC_IT_Level - Level edge detection interrupt.
 *            FSMC_IT_FallingEdge - Falling edge detection interrupt.
 *
 * @return  ITStatus - The new state of FSMC_IT (SET or RESET).
 */
ITStatus FSMC_GetITStatus( uint32_t FSMC_Bank, uint32_t FSMC_IT )
{
    ITStatus bitstatus = RESET;
    uint32_t tmpsr = 0x0, itstatus = 0x0, itenable = 0x0;


    if( FSMC_Bank == FSMC_Bank2_NAND )
    {
        tmpsr = FSMC_Bank2->SR2;
    }

    itstatus = tmpsr & FSMC_IT;

    itenable = tmpsr & ( FSMC_IT >> 3 );
    if( ( itstatus != ( uint32_t )RESET )  && ( itenable != ( uint32_t )RESET ) )
    {
        bitstatus = SET;
    }
    else
    {
        bitstatus = RESET;
    }
    return bitstatus;
}

/*********************************************************************
 * @fn      FSMC_ClearITPendingBit
 *
 * @brief   Clears the FSMC's interrupt pending bits.
 *
 * @param   FSMC_Bank - specifies the FSMC Bank to be used
 *            FSMC_Bank2_NAND - FSMC Bank2 NAND
 *          FSMC_IT - specifies the FSMC interrupt source to check.
 *            FSMC_IT_RisingEdge - Rising edge detection interrupt.
 *            FSMC_IT_Level - Level edge detection interrupt.
 *            FSMC_IT_FallingEdge - Falling edge detection interrupt.
 *
 * @return  none
 */
void FSMC_ClearITPendingBit( uint32_t FSMC_Bank, uint32_t FSMC_IT )
{

    if( FSMC_Bank == FSMC_Bank2_NAND )
    {
        FSMC_Bank2->SR2 &= ~( FSMC_IT >> 3 );
    }
}
