/*!
 * @file        apm32f4xx_smc.c
 *
 * @brief       This file provides all the SMC firmware functions
 *
 * @version     V1.0.4
 *
 * @date        2025-02-15
 *
 * @attention
 *
 *  Copyright (C) 2021-2025 Geehy Semiconductor
 *
 *  You may not use this file except in compliance with the
 *  GEEHY COPYRIGHT NOTICE (GEEHY SOFTWARE PACKAGE LICENSE).
 *
 *  The program is only for reference, which is distributed in the hope
 *  that it will be useful and instructional for customers to develop
 *  their software. Unless required by applicable law or agreed to in
 *  writing, the program is distributed on an "AS IS" BASIS, WITHOUT
 *  ANY WARRANTY OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the GEEHY SOFTWARE PACKAGE LICENSE for the governing permissions
 *  and limitations under the License.
 */

#include "apm32f4xx_smc.h"
#include "apm32f4xx_rcm.h"

/** @addtogroup APM32F4xx_StdPeriphDriver
  @{
*/

#if defined (SMC_Bank1) || defined (SMC_Bank2) || defined (SMC_Bank3) || defined (SMC_Bank4)

/** @defgroup SMC_Driver
  * @brief SMC driver modules
  @{
*/

/** @defgroup SMC_Variables Variables
  @{
  */
const SMC_NORSRAMTimingConfig_T SMC_Default_NORSRAMTimingConfig = {
    .addressSetupTime = 0x0F,
    .addressHoldTime = 0x0F,
    .dataSetupTime = 0xFF,
    .busTurnaroundTime = 0x0F,
    .clockDivision = 0x0F,
    .dataLatency = 0x0F,
    .accessMode = SMC_ACCESS_MODE_A
};


/**@} end of group SMC_Variables*/

/** @defgroup SMC_Functions
  @{
*/

/*!
 * @brief     Reset the SMC NOR/SRAM Banks registers
 *
 * @param     bank: Selects the SMC Bank.
 *                  The parameter can be one of following values:
 *                  @arg SMC_BANK1_NORSRAM_1 : SMC Bank1 NOR/SRAM1
 *                  @arg SMC_BANK1_NORSRAM_2 : SMC Bank1 NOR/SRAM2
 *                  @arg SMC_BANK1_NORSRAM_3 : SMC Bank1 NOR/SRAM3
 *                  @arg SMC_BANK1_NORSRAM_4 : SMC Bank1 NOR/SRAM4
 *
 * @retval    None
 */
void SMC_ResetNORSRAM(SMC_BANK1_NORSRAM_T bank)
{
    if (bank == SMC_BANK1_NORSRAM_1)
    {
        SMC_Bank1->CSCTRL1 = 0x000030DB;
        SMC_Bank1->CSTIM1 = 0x0FFFFFFF;
        SMC_Bank1E->WRTTIM1 = 0x0FFFFFFF;
    }
    else if (bank == SMC_BANK1_NORSRAM_2)
    {
        SMC_Bank1->CSCTRL2 =  0x000030D2;
        SMC_Bank1->CSTIM2 =   0x0FFFFFFF;
        SMC_Bank1E->WRTTIM2 = 0x0FFFFFFF;
    }
    else if (bank == SMC_BANK1_NORSRAM_3)
    {
        SMC_Bank1->CSCTRL3 =  0x000030D2;
        SMC_Bank1->CSTIM3 =   0x0FFFFFFF;
        SMC_Bank1E->WRTTIM3 = 0x0FFFFFFF;
    }
    else if (bank == SMC_BANK1_NORSRAM_4)
    {
        SMC_Bank1->CSCTRL4 =  0x000030D2;
        SMC_Bank1->CSTIM4 =   0x0FFFFFFF;
        SMC_Bank1E->WRTTIM4 = 0x0FFFFFFF;
    }
}

/*!
 * @brief     Reset the SMC NAND Banks registers
 *
 * @param     bank: Selects the SMC Bank.
 *                  The parameter can be one of following values:
 *                  @arg SMC_BANK2_NAND : SMC Bank2 NAND
 *                  @arg SMC_BANK3_NAND : SMC Bank3 NAND
 *
 * @retval    None
 */
void SMC_ResetNAND(SMC_BANK_NAND_T bank)
{
    if (bank == SMC_BANK2_NAND)
    {
        SMC_Bank2->CTRL2 = 0x00000018;
        SMC_Bank2->STSINT2 = 0x00000040;
        SMC_Bank2->CMSTIM2 = 0xFCFCFCFC;
        SMC_Bank2->AMSTIM2 = 0xFCFCFCFC;
    }
    else if (bank == SMC_BANK3_NAND)
    {
        SMC_Bank3->CTRL3 = 0x00000018;
        SMC_Bank3->STSINT3 = 0x00000040;
        SMC_Bank3->CMSTIM3 = 0xFCFCFCFC;
        SMC_Bank3->AMSTIM3 = 0xFCFCFCFC;
    }
}

/*!
 * @brief     Reset the SMC PCCARD Banks registers
 *
 * @param     None
 *
 * @retval    None
 */
void SMC_ResetPCCard(void)
{
    SMC_Bank4->CTRL4 = 0x00000018;
    SMC_Bank4->STSINT4 = 0x00000000;
    SMC_Bank4->CMSTIM4 = 0xFCFCFCFC;
    SMC_Bank4->AMSTIM4 = 0xFCFCFCFC;
    SMC_Bank4->IOSTIM4 = 0xFCFCFCFC;
}

/*!
 * @brief     Config the SMC NOR/SRAM Banks according to the specified parameters in the smcNORSRAMConfig.
 *
 * @param     smcNORSRAMConfig: Point to a SMC_NORSRAMConfig_T structure
 *
 * @retval    None
 */
void SMC_ConfigNORSRAM(SMC_NORSRAMConfig_T* smcNORSRAMConfig)
{
    if (smcNORSRAMConfig->bank == SMC_BANK1_NORSRAM_1)
    {
        SMC_Bank1->CSCTRL1_B.ADMUXEN   = smcNORSRAMConfig->dataAddressMux;
        SMC_Bank1->CSCTRL1_B.MTYPECFG  = smcNORSRAMConfig->memoryType;
        SMC_Bank1->CSCTRL1_B.MDBWIDCFG = smcNORSRAMConfig->memoryDataWidth;
        SMC_Bank1->CSCTRL1_B.BURSTEN = smcNORSRAMConfig->burstAcceesMode;
        SMC_Bank1->CSCTRL1_B.WSASYNCEN = smcNORSRAMConfig->asynchronousWait;
        SMC_Bank1->CSCTRL1_B.WSPOLCFG = smcNORSRAMConfig->waitSignalPolarity;
#if defined (APM32F405xx) || defined (APM32F407xx) || defined (APM32F415xx) || \
    defined (APM32F417xx) || defined (APM32F411xx)
        SMC_Bank1->CSCTRL1_B.WRAPBEN = smcNORSRAMConfig->wrapMode;
#endif /* APM32F405xx || APM32F407xx || APM32F415xx || APM32F417xx || APM32F411xx */
        SMC_Bank1->CSCTRL1_B.WTIMCFG = smcNORSRAMConfig->waitSignalActive;
        SMC_Bank1->CSCTRL1_B.WREN = smcNORSRAMConfig->writeOperation;
        SMC_Bank1->CSCTRL1_B.WAITEN = smcNORSRAMConfig->waitSignal;
        SMC_Bank1->CSCTRL1_B.EXTMODEEN = smcNORSRAMConfig->extendedMode;
        SMC_Bank1->CSCTRL1_B.WRBURSTEN = smcNORSRAMConfig->writeBurst;

        if (smcNORSRAMConfig->memoryType == SMC_MEMORY_TYPE_NOR)
        {
            SMC_Bank1->CSCTRL1_B.NORFMACCEN = BIT_SET;
        }

        SMC_Bank1->CSTIM1_B.ADDRSETCFG = \
            smcNORSRAMConfig->readWriteTimingStruct->addressSetupTime;
        SMC_Bank1->CSTIM1_B.ADDRHLDCFG = \
            smcNORSRAMConfig->readWriteTimingStruct->addressHoldTime;
        SMC_Bank1->CSTIM1_B.DATASETCFG = \
            smcNORSRAMConfig->readWriteTimingStruct->dataSetupTime;
        SMC_Bank1->CSTIM1_B.BUSTURNCFG = \
            smcNORSRAMConfig->readWriteTimingStruct->busTurnaroundTime;
        SMC_Bank1->CSTIM1_B.CLKDIVCFG = \
            smcNORSRAMConfig->readWriteTimingStruct->clockDivision;
        SMC_Bank1->CSTIM1_B.DATALATCFG = \
            smcNORSRAMConfig->readWriteTimingStruct->dataLatency;
        SMC_Bank1->CSTIM1_B.ASYNCACCCFG = \
            smcNORSRAMConfig->readWriteTimingStruct->accessMode;

        if (smcNORSRAMConfig->extendedMode == SMC_EXTENDEN_MODE_ENABLE)
        {
            SMC_Bank1E->WRTTIM1_B.ADDRSETCFG = \
                smcNORSRAMConfig->writeTimingStruct->addressSetupTime;
            SMC_Bank1E->WRTTIM1_B.ADDRHLDCFG = \
                smcNORSRAMConfig->writeTimingStruct->addressHoldTime;
            SMC_Bank1E->WRTTIM1_B.DATASETCFG = \
                smcNORSRAMConfig->writeTimingStruct->dataSetupTime;
            SMC_Bank1E->WRTTIM1_B.BUSTURNCFG = \
                smcNORSRAMConfig->writeTimingStruct->busTurnaroundTime;
            SMC_Bank1E->WRTTIM1_B.ASYNCACCCFG = \
                smcNORSRAMConfig->writeTimingStruct->accessMode;
        }
        else
        {
            SMC_Bank1E->WRTTIM1 = 0x0FFFFFFF;
        }
    }
    else if (smcNORSRAMConfig->bank == SMC_BANK1_NORSRAM_2)
    {
        SMC_Bank1->CSCTRL2_B.ADMUXEN   = smcNORSRAMConfig->dataAddressMux;
        SMC_Bank1->CSCTRL2_B.MTYPECFG  = smcNORSRAMConfig->memoryType;
        SMC_Bank1->CSCTRL2_B.MDBWIDCFG = smcNORSRAMConfig->memoryDataWidth;
        SMC_Bank1->CSCTRL2_B.BURSTEN = smcNORSRAMConfig->burstAcceesMode;
        SMC_Bank1->CSCTRL2_B.WSASYNCEN = smcNORSRAMConfig->asynchronousWait;
        SMC_Bank1->CSCTRL2_B.WSPOLCFG = smcNORSRAMConfig->waitSignalPolarity;
#if defined (APM32F405xx) || defined (APM32F407xx) || defined (APM32F415xx) || \
    defined (APM32F417xx) || defined (APM32F411xx)
        SMC_Bank1->CSCTRL2_B.WRAPBEN = smcNORSRAMConfig->wrapMode;
#endif /* APM32F405xx || APM32F407xx || APM32F415xx || APM32F417xx || APM32F411xx */
        SMC_Bank1->CSCTRL2_B.WTIMCFG = smcNORSRAMConfig->waitSignalActive;
        SMC_Bank1->CSCTRL2_B.WREN = smcNORSRAMConfig->writeOperation;
        SMC_Bank1->CSCTRL2_B.WAITEN = smcNORSRAMConfig->waitSignal;
        SMC_Bank1->CSCTRL2_B.EXTMODEEN = smcNORSRAMConfig->extendedMode;
        SMC_Bank1->CSCTRL2_B.WRBURSTEN = smcNORSRAMConfig->writeBurst;

        if (smcNORSRAMConfig->memoryType == SMC_MEMORY_TYPE_NOR)
        {
            SMC_Bank1->CSCTRL2_B.NORFMACCEN = BIT_SET;
        }

        SMC_Bank1->CSTIM2_B.ADDRSETCFG = \
            smcNORSRAMConfig->readWriteTimingStruct->addressSetupTime;
        SMC_Bank1->CSTIM2_B.ADDRHLDCFG = \
            smcNORSRAMConfig->readWriteTimingStruct->addressHoldTime;
        SMC_Bank1->CSTIM2_B.DATASETCFG = \
            smcNORSRAMConfig->readWriteTimingStruct->dataSetupTime;
        SMC_Bank1->CSTIM2_B.BUSTURNCFG = \
            smcNORSRAMConfig->readWriteTimingStruct->busTurnaroundTime;
        SMC_Bank1->CSTIM2_B.CLKDIVCFG = \
            smcNORSRAMConfig->readWriteTimingStruct->clockDivision;
        SMC_Bank1->CSTIM2_B.DATALATCFG = \
            smcNORSRAMConfig->readWriteTimingStruct->dataLatency;
        SMC_Bank1->CSTIM2_B.ASYNCACCCFG = \
            smcNORSRAMConfig->readWriteTimingStruct->accessMode;

        if (smcNORSRAMConfig->extendedMode == SMC_EXTENDEN_MODE_ENABLE)
        {
            SMC_Bank1E->WRTTIM2_B.ADDRSETCFG = \
                smcNORSRAMConfig->writeTimingStruct->addressSetupTime;
            SMC_Bank1E->WRTTIM2_B.ADDRHLDCFG = \
                smcNORSRAMConfig->writeTimingStruct->addressHoldTime;
            SMC_Bank1E->WRTTIM2_B.DATASETCFG = \
                smcNORSRAMConfig->writeTimingStruct->dataSetupTime;
            SMC_Bank1E->WRTTIM2_B.BUSTURNCFG = \
                smcNORSRAMConfig->writeTimingStruct->busTurnaroundTime;
            SMC_Bank1E->WRTTIM2_B.ASYNCACCCFG = \
                smcNORSRAMConfig->writeTimingStruct->accessMode;
        }
        else
        {
            SMC_Bank1E->WRTTIM2 = 0x0FFFFFFF;
        }
    }
    else if (smcNORSRAMConfig->bank == SMC_BANK1_NORSRAM_3)
    {
        SMC_Bank1->CSCTRL3_B.ADMUXEN   = smcNORSRAMConfig->dataAddressMux;
        SMC_Bank1->CSCTRL3_B.MTYPECFG  = smcNORSRAMConfig->memoryType;
        SMC_Bank1->CSCTRL3_B.MDBWIDCFG = smcNORSRAMConfig->memoryDataWidth;
        SMC_Bank1->CSCTRL3_B.BURSTEN = smcNORSRAMConfig->burstAcceesMode;
        SMC_Bank1->CSCTRL3_B.WSASYNCEN = smcNORSRAMConfig->asynchronousWait;
        SMC_Bank1->CSCTRL3_B.WSPOLCFG = smcNORSRAMConfig->waitSignalPolarity;
#if defined (APM32F405xx) || defined (APM32F407xx) || defined (APM32F415xx) || \
    defined (APM32F417xx) || defined (APM32F411xx)
        SMC_Bank1->CSCTRL3_B.WRAPBEN = smcNORSRAMConfig->wrapMode;
#endif /* APM32F405xx || APM32F407xx || APM32F415xx || APM32F417xx || APM32F411xx */
        SMC_Bank1->CSCTRL3_B.WTIMCFG = smcNORSRAMConfig->waitSignalActive;
        SMC_Bank1->CSCTRL3_B.WREN = smcNORSRAMConfig->writeOperation;
        SMC_Bank1->CSCTRL3_B.WAITEN = smcNORSRAMConfig->waitSignal;
        SMC_Bank1->CSCTRL3_B.EXTMODEEN = smcNORSRAMConfig->extendedMode;
        SMC_Bank1->CSCTRL3_B.WRBURSTEN = smcNORSRAMConfig->writeBurst;

        if (smcNORSRAMConfig->memoryType == SMC_MEMORY_TYPE_NOR)
        {
            SMC_Bank1->CSCTRL3_B.NORFMACCEN = BIT_SET;
        }

        SMC_Bank1->CSTIM3_B.ADDRSETCFG = \
            smcNORSRAMConfig->readWriteTimingStruct->addressSetupTime;
        SMC_Bank1->CSTIM3_B.ADDRHLDCFG = \
            smcNORSRAMConfig->readWriteTimingStruct->addressHoldTime;
        SMC_Bank1->CSTIM3_B.DATASETCFG = \
            smcNORSRAMConfig->readWriteTimingStruct->dataSetupTime;
        SMC_Bank1->CSTIM3_B.BUSTURNCFG = \
            smcNORSRAMConfig->readWriteTimingStruct->busTurnaroundTime;
        SMC_Bank1->CSTIM3_B.CLKDIVCFG = \
            smcNORSRAMConfig->readWriteTimingStruct->clockDivision;
        SMC_Bank1->CSTIM3_B.DATALATCFG = \
            smcNORSRAMConfig->readWriteTimingStruct->dataLatency;
        SMC_Bank1->CSTIM3_B.ASYNCACCCFG = \
            smcNORSRAMConfig->readWriteTimingStruct->accessMode;

        if (smcNORSRAMConfig->extendedMode == SMC_EXTENDEN_MODE_ENABLE)
        {
            SMC_Bank1E->WRTTIM3_B.ADDRSETCFG = \
                smcNORSRAMConfig->writeTimingStruct->addressSetupTime;
            SMC_Bank1E->WRTTIM3_B.ADDRHLDCFG = \
                smcNORSRAMConfig->writeTimingStruct->addressHoldTime;
            SMC_Bank1E->WRTTIM3_B.DATASETCFG = \
                smcNORSRAMConfig->writeTimingStruct->dataSetupTime;
            SMC_Bank1E->WRTTIM3_B.BUSTURNCFG = \
                smcNORSRAMConfig->writeTimingStruct->busTurnaroundTime;
            SMC_Bank1E->WRTTIM3_B.ASYNCACCCFG = \
                smcNORSRAMConfig->writeTimingStruct->accessMode;
        }
        else
        {
            SMC_Bank1E->WRTTIM3 = 0x0FFFFFFF;
        }
    }
    else if (smcNORSRAMConfig->bank == SMC_BANK1_NORSRAM_4)
    {
        SMC_Bank1->CSCTRL4_B.ADMUXEN   = smcNORSRAMConfig->dataAddressMux;
        SMC_Bank1->CSCTRL4_B.MTYPECFG  = smcNORSRAMConfig->memoryType;
        SMC_Bank1->CSCTRL4_B.MDBWIDCFG = smcNORSRAMConfig->memoryDataWidth;
        SMC_Bank1->CSCTRL4_B.BURSTEN = smcNORSRAMConfig->burstAcceesMode;
        SMC_Bank1->CSCTRL4_B.WSASYNCEN = smcNORSRAMConfig->asynchronousWait;
        SMC_Bank1->CSCTRL4_B.WSPOLCFG = smcNORSRAMConfig->waitSignalPolarity;
#if defined (APM32F405xx) || defined (APM32F407xx) || defined (APM32F415xx) || \
    defined (APM32F417xx) || defined (APM32F411xx)
        SMC_Bank1->CSCTRL4_B.WRAPBEN = smcNORSRAMConfig->wrapMode;
#endif /* APM32F405xx || APM32F407xx || APM32F415xx || APM32F417xx || APM32F411xx */
        SMC_Bank1->CSCTRL4_B.WTIMCFG = smcNORSRAMConfig->waitSignalActive;
        SMC_Bank1->CSCTRL4_B.WREN = smcNORSRAMConfig->writeOperation;
        SMC_Bank1->CSCTRL4_B.WAITEN = smcNORSRAMConfig->waitSignal;
        SMC_Bank1->CSCTRL4_B.EXTMODEEN = smcNORSRAMConfig->extendedMode;
        SMC_Bank1->CSCTRL4_B.WRBURSTEN = smcNORSRAMConfig->writeBurst;

        if (smcNORSRAMConfig->memoryType == SMC_MEMORY_TYPE_NOR)
        {
            SMC_Bank1->CSCTRL4_B.NORFMACCEN = BIT_SET;
        }

        SMC_Bank1->CSTIM4_B.ADDRSETCFG = \
            smcNORSRAMConfig->readWriteTimingStruct->addressSetupTime;
        SMC_Bank1->CSTIM4_B.ADDRHLDCFG = \
            smcNORSRAMConfig->readWriteTimingStruct->addressHoldTime;
        SMC_Bank1->CSTIM4_B.DATASETCFG = \
            smcNORSRAMConfig->readWriteTimingStruct->dataSetupTime;
        SMC_Bank1->CSTIM4_B.BUSTURNCFG = \
            smcNORSRAMConfig->readWriteTimingStruct->busTurnaroundTime;
        SMC_Bank1->CSTIM4_B.CLKDIVCFG = \
            smcNORSRAMConfig->readWriteTimingStruct->clockDivision;
        SMC_Bank1->CSTIM4_B.DATALATCFG = \
            smcNORSRAMConfig->readWriteTimingStruct->dataLatency;
        SMC_Bank1->CSTIM4_B.ASYNCACCCFG = \
            smcNORSRAMConfig->readWriteTimingStruct->accessMode;

        if (smcNORSRAMConfig->extendedMode == SMC_EXTENDEN_MODE_ENABLE)
        {
            SMC_Bank1E->WRTTIM4_B.ADDRSETCFG = \
                smcNORSRAMConfig->writeTimingStruct->addressSetupTime;
            SMC_Bank1E->WRTTIM4_B.ADDRHLDCFG = \
                smcNORSRAMConfig->writeTimingStruct->addressHoldTime;
            SMC_Bank1E->WRTTIM4_B.DATASETCFG = \
                smcNORSRAMConfig->writeTimingStruct->dataSetupTime;
            SMC_Bank1E->WRTTIM4_B.BUSTURNCFG = \
                smcNORSRAMConfig->writeTimingStruct->busTurnaroundTime;
            SMC_Bank1E->WRTTIM4_B.ASYNCACCCFG = \
                smcNORSRAMConfig->writeTimingStruct->accessMode;
        }
        else
        {
            SMC_Bank1E->WRTTIM4 = 0x0FFFFFFF;
        }
    }
}

/*!
 * @brief     Config the SMC NAND Banks according to the specified parameters in the smcNANDConfig.
 *
 * @param     smcNANDConfig : Point to a SMC_NANDConfig_T structure.
 *
 * @retval    None
 */
void SMC_ConfigNAND(SMC_NANDConfig_T* smcNANDConfig)
{
    if (smcNANDConfig->bank == SMC_BANK2_NAND)
    {
        SMC_Bank2->CTRL2_B.WAITFEN = smcNANDConfig->waitFeature;
        SMC_Bank2->CTRL2_B.DBWIDCFG = smcNANDConfig->memoryDataWidth;
        SMC_Bank2->CTRL2_B.ECCEN = smcNANDConfig->ECC;
        SMC_Bank2->CTRL2_B.ECCPSCFG = smcNANDConfig->ECCPageSize;
        SMC_Bank2->CTRL2_B.C2RDCFG = smcNANDConfig->TCLRSetupTime;
        SMC_Bank2->CTRL2_B.A2RDCFG = smcNANDConfig->TARSetupTime;
        SMC_Bank2->CTRL2_B.MTYPECFG = BIT_SET;

        SMC_Bank2->CMSTIM2_B.SET2 = \
            smcNANDConfig->commonSpaceTimingStruct->setupTime;
        SMC_Bank2->CMSTIM2_B.WAIT2 = \
            smcNANDConfig->commonSpaceTimingStruct->waitSetupTime;
        SMC_Bank2->CMSTIM2_B.HLD2 = \
            smcNANDConfig->commonSpaceTimingStruct->holdSetupTime;
        SMC_Bank2->CMSTIM2_B.HIZ2 = \
            smcNANDConfig->commonSpaceTimingStruct->HiZSetupTime;

        SMC_Bank2->AMSTIM2_B.SET2 = \
            smcNANDConfig->attributeSpaceTimingStruct->setupTime;
        SMC_Bank2->AMSTIM2_B.WAIT2 = \
            smcNANDConfig->attributeSpaceTimingStruct->waitSetupTime;
        SMC_Bank2->AMSTIM2_B.HLD2 = \
            smcNANDConfig->attributeSpaceTimingStruct->holdSetupTime;
        SMC_Bank2->AMSTIM2_B.HIZ2 = \
            smcNANDConfig->attributeSpaceTimingStruct->HiZSetupTime;
    }
    else if (smcNANDConfig->bank == SMC_BANK3_NAND)
    {
        SMC_Bank3->CTRL3_B.WAITFEN = smcNANDConfig->waitFeature;
        SMC_Bank3->CTRL3_B.DBWIDCFG = smcNANDConfig->memoryDataWidth;
        SMC_Bank3->CTRL3_B.ECCEN = smcNANDConfig->ECC;
        SMC_Bank3->CTRL3_B.ECCPSCFG = smcNANDConfig->ECCPageSize;
        SMC_Bank3->CTRL3_B.C2RDCFG = smcNANDConfig->TCLRSetupTime;
        SMC_Bank3->CTRL3_B.A2RDCFG = smcNANDConfig->TARSetupTime;
        SMC_Bank3->CTRL3_B.MTYPECFG = BIT_SET;

        SMC_Bank3->CMSTIM3_B.SET3 = \
            smcNANDConfig->commonSpaceTimingStruct->setupTime;
        SMC_Bank3->CMSTIM3_B.WAIT3 = \
            smcNANDConfig->commonSpaceTimingStruct->waitSetupTime;
        SMC_Bank3->CMSTIM3_B.HLD3 = \
            smcNANDConfig->commonSpaceTimingStruct->holdSetupTime;
        SMC_Bank3->CMSTIM3_B.HIZ3 = \
            smcNANDConfig->commonSpaceTimingStruct->HiZSetupTime;

        SMC_Bank3->AMSTIM3_B.SET3 = \
            smcNANDConfig->attributeSpaceTimingStruct->setupTime;
        SMC_Bank3->AMSTIM3_B.WAIT3 = \
            smcNANDConfig->attributeSpaceTimingStruct->waitSetupTime;
        SMC_Bank3->AMSTIM3_B.HLD3 = \
            smcNANDConfig->attributeSpaceTimingStruct->holdSetupTime;
        SMC_Bank3->AMSTIM3_B.HIZ3 = \
            smcNANDConfig->attributeSpaceTimingStruct->HiZSetupTime;
    }
}

/*!
 * @brief     Config the SMC PCCARD according to the specified parameters in the smcPCCardConfig.
 *
 * @param     smcPCCardConfig: Point to a SMC_PCCARDConfig_T structure.
 *
 * @retval    None
 */
void SMC_ConfigPCCard(SMC_PCCARDConfig_T* smcPCCardConfig)
{
    SMC_Bank4->CTRL4_B.WAITFEN = smcPCCardConfig->waitFeature;
    SMC_Bank4->CTRL4_B.C2RDCFG = smcPCCardConfig->TCLRSetupTime;
    SMC_Bank4->CTRL4_B.A2RDCFG = smcPCCardConfig->TARSetupTime;
    SMC_Bank4->CTRL4_B.DBWIDCFG = BIT_SET;

    SMC_Bank4->CMSTIM4_B.SET4 = \
        smcPCCardConfig->commonSpaceTimingStruct->setupTime;
    SMC_Bank4->CMSTIM4_B.WAIT4 = \
        smcPCCardConfig->commonSpaceTimingStruct->waitSetupTime;
    SMC_Bank4->CMSTIM4_B.HLD4 = \
        smcPCCardConfig->commonSpaceTimingStruct->holdSetupTime;
    SMC_Bank4->CMSTIM4_B.HIZ4 = \
        smcPCCardConfig->commonSpaceTimingStruct->HiZSetupTime;

    SMC_Bank4->AMSTIM4_B.SET4 = \
        smcPCCardConfig->attributeSpaceTimingStruct->setupTime;
    SMC_Bank4->AMSTIM4_B.WAIT4 = \
        smcPCCardConfig->attributeSpaceTimingStruct->waitSetupTime;
    SMC_Bank4->AMSTIM4_B.HLD4 = \
        smcPCCardConfig->attributeSpaceTimingStruct->holdSetupTime;
    SMC_Bank4->AMSTIM4_B.HIZ4 = \
        smcPCCardConfig->attributeSpaceTimingStruct->HiZSetupTime;

    SMC_Bank4->IOSTIM4_B.SET = \
        smcPCCardConfig->IOSpaceTimingStruct->setupTime;
    SMC_Bank4->IOSTIM4_B.WAIT = \
        smcPCCardConfig->IOSpaceTimingStruct->waitSetupTime;
    SMC_Bank4->IOSTIM4_B.HLD = \
        smcPCCardConfig->IOSpaceTimingStruct->holdSetupTime;
    SMC_Bank4->IOSTIM4_B.HIZ = \
        smcPCCardConfig->IOSpaceTimingStruct->HiZSetupTime;
}

/*!
 * @brief     Fills each smcNORSRAMConfig member with its default value.
 *
 * @param     smcNORSRAMConfig : Point to a SMC_NORSRAMConfig_T structure.
 *
 * @retval    None
 */
void SMC_ConfigNORSRAMStructInit(SMC_NORSRAMConfig_T* smcNORSRAMConfig)
{
    smcNORSRAMConfig->bank = SMC_BANK1_NORSRAM_1;
    smcNORSRAMConfig->dataAddressMux = SMC_DATA_ADDRESS_MUX_ENABLE;
    smcNORSRAMConfig->memoryType = SMC_MEMORY_TYPE_SRAM;
    smcNORSRAMConfig->memoryDataWidth = SMC_MEMORY_DATA_WIDTH_8BIT;
    smcNORSRAMConfig->burstAcceesMode = SMC_BURST_ACCESS_MODE_DISABLE;
    smcNORSRAMConfig->asynchronousWait = SMC_ASYNCHRONOUS_WAIT_DISABLE;
    smcNORSRAMConfig->waitSignalPolarity = SMC_WAIT_SIGNAL_POLARITY_LOW;
    smcNORSRAMConfig->wrapMode = SMC_WRAP_MODE_DISABLE;
    smcNORSRAMConfig->waitSignalActive = SMC_WAIT_SIGNAL_ACTIVE_BEFORE_WAIT_STATE;
    smcNORSRAMConfig->writeOperation = SMC_WRITE_OPERATION_ENABLE;
    smcNORSRAMConfig->waitSignal = SMC_WAIT_SIGNAL_ENABLE;
    smcNORSRAMConfig->extendedMode = SMC_EXTENDEN_MODE_DISABLE;
    smcNORSRAMConfig->writeBurst = SMC_WRITE_BURST_DISABLE;
    smcNORSRAMConfig->readWriteTimingStruct = (SMC_NORSRAMTimingConfig_T*)((uint32_t)&SMC_Default_NORSRAMTimingConfig);
    smcNORSRAMConfig->writeTimingStruct = (SMC_NORSRAMTimingConfig_T*)((uint32_t)&SMC_Default_NORSRAMTimingConfig);
}

/*!
 * @brief     Fills each smcNANDConfig member with its default value.
 *
 * @param     smcNANDConfig : Point to a SMC_NANDConfig_T structure.
 *
 * @retval    None
 */
void SMC_ConfigNANDStructInit(SMC_NANDConfig_T* smcNANDConfig)
{
    smcNANDConfig->bank = SMC_BANK2_NAND;
    smcNANDConfig->waitFeature = SMC_WAIT_FEATURE_DISABLE;
    smcNANDConfig->memoryDataWidth = SMC_MEMORY_DATA_WIDTH_8BIT;
    smcNANDConfig->ECC = SMC_ECC_DISABLE;
    smcNANDConfig->ECCPageSize = SMC_ECC_PAGE_SIZE_BYTE_256;
    smcNANDConfig->TCLRSetupTime = 0x0;
    smcNANDConfig->TARSetupTime = 0x0;
    smcNANDConfig->commonSpaceTimingStruct->setupTime = 0xFC;
    smcNANDConfig->commonSpaceTimingStruct->waitSetupTime = 0xFC;
    smcNANDConfig->commonSpaceTimingStruct->holdSetupTime = 0xFC;
    smcNANDConfig->commonSpaceTimingStruct->HiZSetupTime = 0xFC;
    smcNANDConfig->attributeSpaceTimingStruct->setupTime = 0xFC;
    smcNANDConfig->attributeSpaceTimingStruct->waitSetupTime = 0xFC;
    smcNANDConfig->attributeSpaceTimingStruct->holdSetupTime = 0xFC;
    smcNANDConfig->attributeSpaceTimingStruct->HiZSetupTime = 0xFC;
}

/*!
 * @brief     Fills each smcPCCardConfig member with its default value.
 *
 * @param     smcPCCardConfig : Point to a SMC_PCCARDConfig_T structure.
 *
 * @retval    None
 */
void SMC_ConfigPCCardStructInit(SMC_PCCARDConfig_T* smcPCCardConfig)
{
    smcPCCardConfig->waitFeature = SMC_WAIT_FEATURE_DISABLE;
    smcPCCardConfig->TCLRSetupTime = 0x0;
    smcPCCardConfig->TARSetupTime = 0x0;
    smcPCCardConfig->commonSpaceTimingStruct->setupTime = 0xFC;
    smcPCCardConfig->commonSpaceTimingStruct->waitSetupTime = 0xFC;
    smcPCCardConfig->commonSpaceTimingStruct->holdSetupTime = 0xFC;
    smcPCCardConfig->commonSpaceTimingStruct->HiZSetupTime = 0xFC;
    smcPCCardConfig->attributeSpaceTimingStruct->setupTime = 0xFC;
    smcPCCardConfig->attributeSpaceTimingStruct->waitSetupTime = 0xFC;
    smcPCCardConfig->attributeSpaceTimingStruct->holdSetupTime = 0xFC;
    smcPCCardConfig->attributeSpaceTimingStruct->HiZSetupTime = 0xFC;
    smcPCCardConfig->IOSpaceTimingStruct->setupTime = 0xFC;
    smcPCCardConfig->IOSpaceTimingStruct->waitSetupTime = 0xFC;
    smcPCCardConfig->IOSpaceTimingStruct->holdSetupTime = 0xFC;
    smcPCCardConfig->IOSpaceTimingStruct->HiZSetupTime = 0xFC;
}

/*!
 * @brief     Enables the specified NOR/SRAM Memory Bank.
 *
 * @param     bank: Selects the SMC Bank.
 *                  The parameter can be one of following values:
 *                  @arg SMC_BANK1_NORSRAM_1 : SMC Bank1 NOR/SRAM1
 *                  @arg SMC_BANK1_NORSRAM_2 : SMC Bank1 NOR/SRAM2
 *                  @arg SMC_BANK1_NORSRAM_3 : SMC Bank1 NOR/SRAM3
 *                  @arg SMC_BANK1_NORSRAM_4 : SMC Bank1 NOR/SRAM4
 *
 * @retval    None
 */
void SMC_EnableNORSRAM(SMC_BANK1_NORSRAM_T bank)
{
    if (bank == SMC_BANK1_NORSRAM_1)
    {
        SMC_Bank1->CSCTRL1_B.MBKEN = BIT_SET;
    }
    else if (bank == SMC_BANK1_NORSRAM_2)
    {
        SMC_Bank1->CSCTRL2_B.MBKEN = BIT_SET;
    }
    else if (bank == SMC_BANK1_NORSRAM_3)
    {
        SMC_Bank1->CSCTRL3_B.MBKEN = BIT_SET;
    }
    else if (bank == SMC_BANK1_NORSRAM_4)
    {
        SMC_Bank1->CSCTRL4_B.MBKEN = BIT_SET;
    }
}

/*!
 * @brief     Disbles the specified NOR/SRAM Memory Bank.
 *
 * @param     bank: Selects the SMC Bank.
 *                  The parameter can be one of following values:
 *                  @arg SMC_BANK1_NORSRAM_1 : SMC Bank1 NOR/SRAM1
 *                  @arg SMC_BANK1_NORSRAM_2 : SMC Bank1 NOR/SRAM2
 *                  @arg SMC_BANK1_NORSRAM_3 : SMC Bank1 NOR/SRAM3
 *                  @arg SMC_BANK1_NORSRAM_4 : SMC Bank1 NOR/SRAM4
 *
 * @retval    None
 */
void SMC_DisableNORSRAM(SMC_BANK1_NORSRAM_T bank)
{
    if (bank == SMC_BANK1_NORSRAM_1)
    {
        SMC_Bank1->CSCTRL1_B.MBKEN = BIT_RESET;
    }
    else if (bank == SMC_BANK1_NORSRAM_2)
    {
        SMC_Bank1->CSCTRL2_B.MBKEN = BIT_RESET;
    }
    else if (bank == SMC_BANK1_NORSRAM_3)
    {
        SMC_Bank1->CSCTRL3_B.MBKEN = BIT_RESET;
    }
    else if (bank == SMC_BANK1_NORSRAM_4)
    {
        SMC_Bank1->CSCTRL4_B.MBKEN = BIT_RESET;
    }
}

/*!
 * @brief     Enables the specified NAND Memory Bank.
 *
 * @param     bank: Selects the SMC Bank.
 *                  The parameter can be one of following values:
 *                  @arg SMC_BANK2_NAND : SMC Bank2 NAND
 *                  @arg SMC_BANK3_NAND : SMC Bank3 NAND
 *
 * @retval    None
 */
void SMC_EnableNAND(SMC_BANK_NAND_T bank)
{
    if (bank == SMC_BANK2_NAND)
    {
        SMC_Bank2->CTRL2_B.MBKEN = BIT_SET;
    }
    else if (bank == SMC_BANK3_NAND)
    {
        SMC_Bank3->CTRL3_B.MBKEN = BIT_SET;
    }
}

/*!
 * @brief     Disbles the specified NAND Memory Bank.
 *
 * @param     bank: Selects the SMC Bank.
 *                  The parameter can be one of following values:
 *                  @arg SMC_BANK2_NAND : SMC Bank2 NAND
 *                  @arg SMC_BANK3_NAND : SMC Bank3 NAND
 *
 * @retval    None
 */
void SMC_DisableNAND(SMC_BANK_NAND_T bank)
{
    if (bank == SMC_BANK2_NAND)
    {
        SMC_Bank2->CTRL2_B.MBKEN = BIT_RESET;
    }
    else if (bank == SMC_BANK3_NAND)
    {
        SMC_Bank3->CTRL3_B.MBKEN = BIT_RESET;
    }
}

/*!
 * @brief     Enables the specified PC Card Memory Bank.
 *
 * @param     None
 *
 * @retval    None
 */
void SMC_EnablePCCARD(void)
{
    SMC_Bank4->CTRL4_B.MBKEN = BIT_SET;
}

/*!
 * @brief     Disables the specified PC Card Memory Bank.
 *
 * @param     None
 *
 * @retval    None
 */
void SMC_DisablePCCARD(void)
{
    SMC_Bank4->CTRL4_B.MBKEN = BIT_RESET;
}

/*!
 * @brief     Enbles the SMC NAND ECC feature.
 *
 * @param     bank: Selects the SMC Bank.
 *                  The parameter can be one of following values:
 *                  @arg SMC_BANK2_NAND : SMC Bank2 NAND
 *                  @arg SMC_BANK3_NAND : SMC Bank3 NAND
 *
 * @retval    None
 */
void SMC_EnableNANDECC(SMC_BANK_NAND_T bank)
{
    if (bank == SMC_BANK2_NAND)
    {
        SMC_Bank2->CTRL2_B.ECCEN = BIT_SET;
    }
    else if (bank == SMC_BANK3_NAND)
    {
        SMC_Bank3->CTRL3_B.ECCEN = BIT_SET;
    }
}

/*!
 * @brief     Disbles or disables the SMC NAND ECC feature.
 *
 * @param     bank: Selects the SMC Bank.
 *                  The parameter can be one of following values:
 *                  @arg SMC_BANK2_NAND : SMC Bank2 NAND
 *                  @arg SMC_BANK3_NAND : SMC Bank3 NAND
 *
 * @retval    None
 *
 * @note
 */
void SMC_DisableNANDECC(SMC_BANK_NAND_T bank)
{
    if (bank == SMC_BANK2_NAND)
    {
        SMC_Bank2->CTRL2_B.ECCEN = BIT_RESET;
    }
    else if (bank == SMC_BANK3_NAND)
    {
        SMC_Bank3->CTRL3_B.ECCEN = BIT_RESET;
    }
}

/*!
 * @brief     Read the error correction code register value.
 *
 * @param     bank: Selects the SMC Bank.
 *                  The parameter can be one of following values:
 *                  @arg SMC_BANK2_NAND : SMC Bank2 NAND
 *                  @arg SMC_BANK3_NAND : SMC Bank3 NAND
 *
 * @retval    The value of Error Correction Code (ECC).
 */
uint32_t  SMC_ReadECC(SMC_BANK_NAND_T bank)
{
    uint32_t eccval = 0x00000000;

    if (bank == SMC_BANK2_NAND)
    {
        eccval = SMC_Bank2->ECCRS2;
    }
    else if (bank == SMC_BANK3_NAND)
    {
        eccval = SMC_Bank3->ECCRS3;
    }

    return eccval;
}

/*!
 * @brief    Enables the specified SMC interrupts.
 *
 * @param    bank: Selects the SMC Bank.
 *                 The parameter can be one of following values:
 *                 @arg SMC_BANK2_NAND   : SMC Bank2 NAND
 *                 @arg SMC_BANK3_NAND   : SMC Bank3 NAND
 *                 @arg SMC_BANK4_PCCARD : SMC Bank4 PCCARD
 *
 * @param    interrupt: Select the SMC interrupt sources.
 *                      This parameter can be any combination of the following values:
 *                      @arg SMC_INT_EDGE_RISING  : Rising edge detection interrupt.
 *                      @arg SMC_INT_LEVEL_HIGH   : High level detection interrupt.
 *                      @arg SMC_INT_EDGE_FALLING : Falling edge detection interrupt.
 *
 * @retval   None
 */
void SMC_EnableInterrupt(SMC_BANK_NAND_T bank, uint32_t interrupt)
{
    if (bank == SMC_BANK2_NAND)
    {
        SMC_Bank2->STSINT2 |= interrupt;
    }
    else if (bank == SMC_BANK3_NAND)
    {
        SMC_Bank3->STSINT3 |= interrupt;
    }
    else
    {
        SMC_Bank4->STSINT4 |= interrupt;
    }
}

/*!
 * @brief    Enables the specified SMC interrupts.
 *
 * @param    bank: Selects the SMC Bank.
 *                 The parameter can be one of following values:
 *                 @arg SMC_BANK2_NAND   : SMC Bank2 NAND
 *                 @arg SMC_BANK3_NAND   : SMC Bank3 NAND
 *                 @arg SMC_BANK4_PCCARD : SMC Bank4 PCCARD
 *
 * @param    interrupt: Select the SMC interrupt sources.
 *                      This parameter can be any combination of the following values:
 *                      @arg SMC_INT_EDGE_RISING  : Rising edge detection interrupt.
 *                      @arg SMC_INT_LEVEL_HIGH   : High level edge detection interrupt.
 *                      @arg SMC_INT_EDGE_FALLING : Falling edge detection interrupt.
 *
 * @retval   None
 */
void SMC_DisableInterrupt(SMC_BANK_NAND_T bank, uint32_t interrupt)
{
    if (bank == SMC_BANK2_NAND)
    {
        SMC_Bank2->STSINT2 &= ~interrupt;
    }
    else if (bank == SMC_BANK3_NAND)
    {
        SMC_Bank3->STSINT3 &= ~interrupt;
    }
    else
    {
        SMC_Bank4->STSINT4 &= ~interrupt;
    }
}

/*!
 * @brief     Read the status of specified SMC flag.
 *
 * @param    bank: Selects the SMC Bank.
 *                 The parameter can be one of following values:
 *                 @arg SMC_BANK2_NAND   : SMC Bank2 NAND
 *                 @arg SMC_BANK3_NAND   : SMC Bank3 NAND
 *                 @arg SMC_BANK4_PCCARD : SMC Bank4 PCCARD
 *
 * @param    flag: Select the SMC interrupt sources.
 *                 This parameter can be one of the following values:
 *                 @arg SMC_FLAG_EDGE_RISING  : Rising egde detection Flag.
 *                 @arg SMC_FLAG_LEVEL_HIGH   : High level detection Flag.
 *                 @arg SMC_FLAG_EDGE_FALLING : Falling egde detection Flag.
 *                 @arg SMC_FLAG_FIFO_EMPTY   : FIFO empty Flag.
 *
 * @retval    SET or RESET
 *
 * @note
 */
uint16_t SMC_ReadStatusFlag(SMC_BANK_NAND_T bank, SMC_FLAG_T flag)
{
    if (bank == SMC_BANK2_NAND)
    {
        return (SMC_Bank2->STSINT2 & flag) ? SET : RESET;
    }
    else if (bank == SMC_BANK3_NAND)
    {
        return (SMC_Bank3->STSINT3 & flag) ? SET : RESET;
    }
    else
    {
        return (SMC_Bank4->STSINT4 & flag) ? SET : RESET;
    }
}

/*!
 * @brief     Clears the SMC's pending flags.
 *
 * @param    bank: Selects the SMC Bank.
 *                 The parameter can be one of following values:
 *                 @arg SMC_BANK2_NAND   : SMC Bank2 NAND
 *                 @arg SMC_BANK3_NAND   : SMC Bank3 NAND
 *                 @arg SMC_BANK4_PCCARD : SMC Bank4 PCCARD
 *
 * @param    flag: Select the SMC interrupt sources.
 *                 This parameter can be any combination of the following values:
 *                 @arg SMC_FLAG_EDGE_RISING  : Rising egde detection Flag.
 *                 @arg SMC_FLAG_LEVEL_HIGH   : High level detection Flag.
 *                 @arg SMC_FLAG_EDGE_FALLING : Falling egde detection Flag.
 *
 * @retval    None
 */
void SMC_ClearStatusFlag(SMC_BANK_NAND_T bank, uint32_t flag)
{
    if (bank == SMC_BANK2_NAND)
    {
        SMC_Bank2->STSINT2 &= ~flag;
    }
    else if (bank == SMC_BANK3_NAND)
    {
        SMC_Bank3->STSINT3 &= ~flag;
    }
    else
    {
        SMC_Bank4->STSINT4 &= ~flag;
    }
}

/*!
 * @brief    Read the specified SMC interrupt has occurred or not.
 *
 * @param    bank: Selects the SMC Bank.
 *                 The parameter can be one of following values:
 *                 @arg SMC_BANK2_NAND   : SMC Bank2 NAND
 *                 @arg SMC_BANK3_NAND   : SMC Bank3 NAND
 *                 @arg SMC_BANK4_PCCARD : SMC Bank4 PCCARD
 *
 * @param    flag: Select the SMC interrupt source.
 *                 This parameter can be one of the following values:
 *                 @arg SMC_INT_EDGE_RISING  : Rising edge detection interrupt.
 *                 @arg SMC_INT_LEVEL_HIGH   : High level edge detection interrupt.
 *                 @arg SMC_INT_EDGE_FALLING : Falling edge detection interrupt.
 *
 * @retval   The status of specified SMC interrupt source.
 */
uint16_t  SMC_ReadIntFlag(SMC_BANK_NAND_T bank, SMC_INT_T flag)
{
    uint32_t tmpStatus = 0x0, itstatus = 0x0, itenable = 0x0;

    if (bank == SMC_BANK2_NAND)
    {
        tmpStatus = SMC_Bank2->STSINT2;
    }
    else if (bank == SMC_BANK3_NAND)
    {
        tmpStatus = SMC_Bank3->STSINT3;
    }
    else
    {
        tmpStatus = SMC_Bank4->STSINT4;
    }

    itstatus = tmpStatus & flag;
    itenable = tmpStatus & (flag >> 3);

    if ((itstatus != RESET) && (itenable != RESET))
    {
        return SET;
    }
    else
    {
        return RESET;
    }
}

/*!
 * @brief    Clears the SMC's interrupt Flag.
 *
 * @param    bank: Selects the SMC Bank.
 *                 The parameter can be one of following values:
 *                 @arg SMC_BANK2_NAND   : SMC Bank2 NAND
 *                 @arg SMC_BANK3_NAND   : SMC Bank3 NAND
 *                 @arg SMC_BANK4_PCCARD : SMC Bank4 PCCARD
 *
 * @param    interrupt: Select the SMC interrupt sources.
 *                      This parameter can be any combination of the following values:
 *                      @arg SMC_INT_EDGE_RISING  : Rising edge detection interrupt.
 *                      @arg SMC_INT_LEVEL_HIGH   : High level edge detection interrupt.
 *                      @arg SMC_INT_EDGE_FALLING : Falling edge detection interrupt.
 *
 * @retval   None
 */
void SMC_ClearIntFlag(SMC_BANK_NAND_T bank, uint32_t flag)
{
    if (bank == SMC_BANK2_NAND)
    {
        SMC_Bank2->STSINT2 &= ~(flag >> 3);
    }
    else if (bank == SMC_BANK3_NAND)
    {
        SMC_Bank3->STSINT3 &= ~(flag >> 3);
    }
    else
    {
        SMC_Bank4->STSINT4 &= ~(flag >> 3);
    }
}

/**@} end of group SMC_Functions */
/**@} end of group SMC_Driver */

#endif /* SMC_Bank1 || SMC_Bank2 || SMC_Bank3 || SMC_Bank4 */

/**@} end of group APM32F4xx_StdPeriphDriver */
