/**************************************************************************//**
 * @file     NuEdu-Basic01_SPI_Flash.c
 * @version  V1.00
 * $Revision: 3 $
 * $Date: 14/09/16 9:27a $
 * @brief    NuEdu-Basic01_SPI_Flash driver source file for NuEdu-SDK-Nano130
 *
 * @note
 * SPDX-License-Identifier: Apache-2.0
 * Copyright (C) 2014 Nuvoton Technology Corp. All rights reserved.
*****************************************************************************/
#include <stdio.h>
#include "Nano100Series.h"
#include "NuEdu-Basic01_SPI_Flash.h"

/** @addtogroup NANO100_Library NANO100 Library
  @{
*/

/** @addtogroup NuEdu-SDK-Nano130_Basic01 Nano130_Basic01 Library
  @{
*/

/** @addtogroup Nano130_Basic01_FUNCTIONS SPI Flash Functions
  @{
*/

/// @cond HIDDEN_SYMBOLS
/*---------------------------------------------------------------------------------------------------------*/
/*  Definitons                                                                                             */
/*---------------------------------------------------------------------------------------------------------*/
#define SPI_FLASH_PORT SPI0
/// @endcond

/**
 * @brief       Open GPIO port for SPI interface and configure this SPI controller as Master,
 *              MSB first, clock idle low, TX at falling-edge, RX at rising-edge, 32-bit length
 *      transaction, disable the automatic hardware slave select function and SPI serial
 *      clock rate = 2 MHz.
 *
 * @return      None
 */
void Open_SPI_Flash(void)
{

    /* Init GPIO for SPI Flash Port, set PE1, PE2, PE3 and PE4 for SPI0 */
    SYS->PE_L_MFP |= (SYS_PE_L_MFP_PE1_MFP_SPI0_SS0 | SYS_PE_L_MFP_PE2_MFP_SPI0_SCLK |
                      SYS_PE_L_MFP_PE3_MFP_SPI0_MISO0 | SYS_PE_L_MFP_PE4_MFP_SPI0_MOSI0);

    /* Enable SPI0 IP clock */
    CLK->APBCLK |= CLK_APBCLK_SPI0_EN_Msk;

    /* Configure SPI_FLASH_PORT as a master, MSB first, clock idle low, TX at falling-edge, RX at rising-edge and 32-bit transaction */
    SPI_FLASH_PORT->CTL = (SPI_FLASH_PORT->CTL & ~(SPI_CTL_SLAVE_Msk | SPI_CTL_LSB_Msk | SPI_CTL_CLKP_Msk | SPI_CTL_TX_BIT_LEN_Msk | SPI_CTL_TX_NEG_Msk | SPI_CTL_RX_NEG_Msk)) | SPI_CTL_TX_NEG_Msk;

    /* Disable the automatic hardware slave select function. Select the SS pin and configure as low-active. */
    SPI_FLASH_PORT->SSR = (SPI_FLASH_PORT->SSR & ~SPI_SSR_SSR_Msk);

    /* Set SPI clock rate = HCLK / (20+1) = 42MHz / 21 = 2MHz */
    SPI_FLASH_PORT->CLKDIV = (SPI_FLASH_PORT->CLKDIV & ~SPI_CLKDIV_DIVIDER1_Msk) | (0x14 << SPI_CLKDIV_DIVIDER1_Pos);

}

/**
 * @brief       Read back the Manufacturer ID and Device ID from SPI Flash device.
 *
 * @return      Manufacturer ID and Device ID of SPI Flash device.
 *          For W25Q16BV, Manufacturer ID: 0xEF; Device ID: 0x14
 *      For W26X16, Manufacturer ID: 0xEF; Device ID: 0x14
*/
unsigned int SpiFlash_ReadMidDid(void)
{
    unsigned int au32SourceData;
    unsigned int au32DestinationData;

    // configure transaction length as 8 bits
    SPI_FLASH_PORT->CTL = (SPI_FLASH_PORT->CTL & ~SPI_CTL_TX_BIT_LEN_Msk) | (0x08 << SPI_CTL_TX_BIT_LEN_Pos);

    // /CS: active
    SPI_FLASH_PORT->SSR = (SPI_FLASH_PORT->SSR & ~SPI_SSR_SSR_Msk) | 0x1;

    // send Command: 0x90, Read Manufacturer/Device ID
    au32SourceData = 0x90;
    SPI_FLASH_PORT->TX0 = au32SourceData;
    SPI_FLASH_PORT->CTL |= SPI_CTL_GO_BUSY_Msk;

    // wait
    while (SPI_FLASH_PORT->CTL & SPI_CTL_GO_BUSY_Msk) {}

    // configure transaction length as 24 bits
    SPI_FLASH_PORT->CTL = (SPI_FLASH_PORT->CTL & ~SPI_CTL_TX_BIT_LEN_Msk) | (0x18 << SPI_CTL_TX_BIT_LEN_Pos);

    // send 24-bit '0', dummy
    au32SourceData = 0x0;
    SPI_FLASH_PORT->TX0 = au32SourceData;
    SPI_FLASH_PORT->CTL |= SPI_CTL_GO_BUSY_Msk;

    // wait
    while (SPI_FLASH_PORT->CTL & SPI_CTL_GO_BUSY_Msk) {}

    // configure transaction length as 16 bits
    SPI_FLASH_PORT->CTL = (SPI_FLASH_PORT->CTL & ~SPI_CTL_TX_BIT_LEN_Msk) | (0x10 << SPI_CTL_TX_BIT_LEN_Pos);

    // receive
    au32SourceData = 0x0;
    SPI_FLASH_PORT->TX0 = au32SourceData;
    SPI_FLASH_PORT->CTL |= SPI_CTL_GO_BUSY_Msk;

    // wait
    while (SPI_FLASH_PORT->CTL & SPI_CTL_GO_BUSY_Msk) {}

    // /CS: de-active
    SPI_FLASH_PORT->SSR = (SPI_FLASH_PORT->SSR & ~SPI_SSR_SSR_Msk);

    // dump Rx register
    au32DestinationData = SPI_FLASH_PORT->RX0;

    return (au32DestinationData & 0xffff);

}

/**
 * @brief       This function do the chip erasing to SPI Flash device.
 *
 * @return      None
 */
void SpiFlash_ChipErase(void)
{
    unsigned int au32SourceData;

    // configure transaction length as 8 bits
    SPI_FLASH_PORT->CTL = (SPI_FLASH_PORT->CTL & ~SPI_CTL_TX_BIT_LEN_Msk) | (0x08 << SPI_CTL_TX_BIT_LEN_Pos);

    // /CS: active
    SPI_FLASH_PORT->SSR = (SPI_FLASH_PORT->SSR & ~SPI_SSR_SSR_Msk) | 0x1;

    // send Command: 0x06, Write enable
    au32SourceData = 0x06;
    SPI_FLASH_PORT->TX0 = au32SourceData;
    SPI_FLASH_PORT->CTL |= SPI_CTL_GO_BUSY_Msk;

    // wait
    while (SPI_FLASH_PORT->CTL & SPI_CTL_GO_BUSY_Msk) {}

    // /CS: de-active
    SPI_FLASH_PORT->SSR = (SPI_FLASH_PORT->SSR & ~SPI_SSR_SSR_Msk);

    // /CS: active
    SPI_FLASH_PORT->SSR = (SPI_FLASH_PORT->SSR & ~SPI_SSR_SSR_Msk) | 0x1;

    // send Command: 0xC7, Chip Erase
    au32SourceData = 0xc7;
    SPI_FLASH_PORT->TX0 = au32SourceData;
    SPI_FLASH_PORT->CTL |= SPI_CTL_GO_BUSY_Msk;

    // wait
    while (SPI_FLASH_PORT->CTL & SPI_CTL_GO_BUSY_Msk) {}

    // /CS: de-active
    SPI_FLASH_PORT->SSR = (SPI_FLASH_PORT->SSR & ~SPI_SSR_SSR_Msk);

}

/**
 * @brief       Read back the Status Register 1 from SPI Flash device.
 *
 * @return      Status Register 1 value of SPI Flash device.
 */
unsigned int SpiFlash_ReadStatusReg1(void)
{
    unsigned int au32SourceData;
    unsigned int au32DestinationData;

    // configure transaction length as 16 bits
    SPI_FLASH_PORT->CTL = (SPI_FLASH_PORT->CTL & ~SPI_CTL_TX_BIT_LEN_Msk) | (0x10 << SPI_CTL_TX_BIT_LEN_Pos);

    // /CS: active
    SPI_FLASH_PORT->SSR = (SPI_FLASH_PORT->SSR & ~SPI_SSR_SSR_Msk) | 0x1;

    // send Command: 0x05, Read status register 1
    au32SourceData = 0x0500;
    SPI_FLASH_PORT->TX0 = au32SourceData;
    SPI_FLASH_PORT->CTL |= SPI_CTL_GO_BUSY_Msk;

    // wait
    while (SPI_FLASH_PORT->CTL & SPI_CTL_GO_BUSY_Msk) {}

    // /CS: de-active
    SPI_FLASH_PORT->SSR = (SPI_FLASH_PORT->SSR & ~SPI_SSR_SSR_Msk);

    // dump Rx register
    au32DestinationData = SPI_FLASH_PORT->RX0;

    return (au32DestinationData & 0xFF);

}

/**
 * @brief       Read back the Status Register 2 from SPI Flash device.
 *
 * @return      Status Register 2 value of SPI Flash device.
 */
unsigned int SpiFlash_ReadStatusReg2(void)
{
    unsigned int au32SourceData;
    unsigned int au32DestinationData;

    // configure transaction length as 16 bits
    SPI_FLASH_PORT->CTL = (SPI_FLASH_PORT->CTL & ~SPI_CTL_TX_BIT_LEN_Msk) | (0x10 << SPI_CTL_TX_BIT_LEN_Pos);

    // /CS: active
    SPI_FLASH_PORT->SSR = (SPI_FLASH_PORT->SSR & ~SPI_SSR_SSR_Msk) | 0x1;

    // send Command: 0x35, Read status register 2
    au32SourceData = 0x3500;
    SPI_FLASH_PORT->TX0 = au32SourceData;
    SPI_FLASH_PORT->CTL |= SPI_CTL_GO_BUSY_Msk;

    // wait
    while (SPI_FLASH_PORT->CTL & SPI_CTL_GO_BUSY_Msk) {}

    // /CS: de-active
    SPI_FLASH_PORT->SSR = (SPI_FLASH_PORT->SSR & ~SPI_SSR_SSR_Msk);

    // dump Rx register
    au32DestinationData = SPI_FLASH_PORT->RX0;

    return (au32DestinationData & 0xFF);

}

/**
 * @brief       Waiting for the BUSY bit of SPI Flash that be cleared to 0.
 *
 * @return      None
 */
void SpiFlash_WaitReady(void)
{
    unsigned int ReturnValue;

    do
    {
        ReturnValue = SpiFlash_ReadStatusReg1();
        ReturnValue = ReturnValue & 1;
    }
    while(ReturnValue!=0);   // check the BUSY bit

}

/**
 * @brief       This function do the page programming to SPI Flash device.
 *
 * @param[in]   *DataBuffer A Point that point to source data buffer.
 *
 * @param[in]   StartAddress    A start address of SPI Flash that will be programmed.
 *
 * @param[in]   ByteCount   Byte count number that will be programmed.
 *
 * @return      None
 */
void SpiFlash_PageProgram(unsigned char *DataBuffer, unsigned int StartAddress, unsigned int ByteCount)
{
    unsigned int au32SourceData;
    unsigned int Counter;

    // configure transaction length as 8 bits
    SPI_FLASH_PORT->CTL = (SPI_FLASH_PORT->CTL & ~SPI_CTL_TX_BIT_LEN_Msk) | (0x08 << SPI_CTL_TX_BIT_LEN_Pos);

    // /CS: active
    SPI_FLASH_PORT->SSR = (SPI_FLASH_PORT->SSR & ~SPI_SSR_SSR_Msk) | 0x1;

    // send Command: 0x06, Write enable
    au32SourceData = 0x06;
    SPI_FLASH_PORT->TX0 = au32SourceData;
    SPI_FLASH_PORT->CTL |= SPI_CTL_GO_BUSY_Msk;

    // wait
    while (SPI_FLASH_PORT->CTL & SPI_CTL_GO_BUSY_Msk) {}

    // /CS: de-active
    SPI_FLASH_PORT->SSR = (SPI_FLASH_PORT->SSR & ~SPI_SSR_SSR_Msk);

    // /CS: active
    SPI_FLASH_PORT->SSR = (SPI_FLASH_PORT->SSR & ~SPI_SSR_SSR_Msk) | 0x1;

    // send Command: 0x02, Page program
    au32SourceData = 0x02;
    SPI_FLASH_PORT->TX0 = au32SourceData;
    SPI_FLASH_PORT->CTL |= SPI_CTL_GO_BUSY_Msk;

    // wait
    while (SPI_FLASH_PORT->CTL & SPI_CTL_GO_BUSY_Msk) {}

    // configure transaction length as 24 bits
    SPI_FLASH_PORT->CTL = (SPI_FLASH_PORT->CTL & ~SPI_CTL_TX_BIT_LEN_Msk) | (0x18 << SPI_CTL_TX_BIT_LEN_Pos);

    // send 24-bit start address
    au32SourceData = StartAddress;
    SPI_FLASH_PORT->TX0 = au32SourceData;
    SPI_FLASH_PORT->CTL |= SPI_CTL_GO_BUSY_Msk;

    // wait
    while (SPI_FLASH_PORT->CTL & SPI_CTL_GO_BUSY_Msk) {}

    // configure transaction length as 8 bits
    SPI_FLASH_PORT->CTL = (SPI_FLASH_PORT->CTL & ~SPI_CTL_TX_BIT_LEN_Msk) | (0x08 << SPI_CTL_TX_BIT_LEN_Pos);

    for(Counter=0; Counter<ByteCount; Counter++)
    {
        // send   data to program
        au32SourceData = DataBuffer[Counter];
        SPI_FLASH_PORT->TX0 = au32SourceData;
        SPI_FLASH_PORT->CTL |= SPI_CTL_GO_BUSY_Msk;

        // wait
        while (SPI_FLASH_PORT->CTL & SPI_CTL_GO_BUSY_Msk) {}
    }

    // /CS: de-active
    SPI_FLASH_PORT->SSR = (SPI_FLASH_PORT->SSR & ~SPI_SSR_SSR_Msk);

}

/**
 * @brief       This function do the data reading from SPI Flash device.
 *
 * @param[in]   *DataBuffer A Point that point to destination data buffer.
 *
 * @param[in]   StartAddress    A start address of SPI Flash that will be read.
 *
 * @param[in]   ByteCount   Byte count number that will be read.
 *
 * @return      None
 */
void SpiFlash_ReadData(unsigned char *DataBuffer, unsigned int StartAddress, unsigned int ByteCount)
{
    unsigned int au32SourceData;
    unsigned int au32DestinationData;
    unsigned int Counter;

    // configure transaction length as 8 bits
    SPI_FLASH_PORT->CTL = (SPI_FLASH_PORT->CTL & ~SPI_CTL_TX_BIT_LEN_Msk) | (0x08 << SPI_CTL_TX_BIT_LEN_Pos);

    // /CS: active
    SPI_FLASH_PORT->SSR = (SPI_FLASH_PORT->SSR & ~SPI_SSR_SSR_Msk) | 0x1;

    // send Command: 0x03, Read data
    au32SourceData = 0x03;
    SPI_FLASH_PORT->TX0 = au32SourceData;
    SPI_FLASH_PORT->CTL |= SPI_CTL_GO_BUSY_Msk;

    // wait
    while (SPI_FLASH_PORT->CTL & SPI_CTL_GO_BUSY_Msk) {}

    // configure transaction length as 24 bits
    SPI_FLASH_PORT->CTL = (SPI_FLASH_PORT->CTL & ~SPI_CTL_TX_BIT_LEN_Msk) | (0x18 << SPI_CTL_TX_BIT_LEN_Pos);

    // send 24-bit start address
    au32SourceData = StartAddress;
    SPI_FLASH_PORT->TX0 = au32SourceData;
    SPI_FLASH_PORT->CTL |= SPI_CTL_GO_BUSY_Msk;

    // wait
    while (SPI_FLASH_PORT->CTL & SPI_CTL_GO_BUSY_Msk) {}

    // configure transaction length as 8 bits
    SPI_FLASH_PORT->CTL = (SPI_FLASH_PORT->CTL & ~SPI_CTL_TX_BIT_LEN_Msk) | (0x08 << SPI_CTL_TX_BIT_LEN_Pos);

    for(Counter=0; Counter<ByteCount; Counter++)
    {
        // receive
        au32SourceData = 0x0;
        SPI_FLASH_PORT->TX0 = au32SourceData;
        SPI_FLASH_PORT->CTL |= SPI_CTL_GO_BUSY_Msk;

        // wait
        while (SPI_FLASH_PORT->CTL & SPI_CTL_GO_BUSY_Msk) {}

        // dump Rx register
        au32DestinationData = SPI_FLASH_PORT->RX0;
        DataBuffer[Counter] = (unsigned char) au32DestinationData;
    }

    // /CS: de-active
    SPI_FLASH_PORT->SSR = (SPI_FLASH_PORT->SSR & ~SPI_SSR_SSR_Msk);

}


/*@}*/ /* end of group Nano130_Basic01_FUNCTIONS */

/*@}*/ /* end of group NuEdu-SDK-Nano130_Basic01 */

/*@}*/ /* end of group NANO100_Library */

/*** (C) COPYRIGHT 2013 Nuvoton Technology Corp. ***/
