//
// ssiflash.c - Driver for the Winbond Serial Flash
// author Brian, 2016/3/29
//

//*****************************************************************************
#include <stdio.h>
#include "bsp_ssi_flash.h"
#include "spi_master.h"
#include "nrf_gpio.h"
#include "app_util_platform.h"
#include "zkutil.h"
//*****************************************************************************
//
//! \addtogroup ssiflash_api
//! @{
//
//*****************************************************************************
static struct
{
	struct
	{
		uint32_t m_transfer_completed:1;
	}flags;
}ssi_ctx_priv;
//*****************************************************************************
//
// The rate of the SSI clock and derived values.
//
//*****************************************************************************
#define SSI_CLK_RATE            10000000
#define SSI_CLKS_PER_MS         (SSI_CLK_RATE / 1000)
#define STATUS_READS_PER_MS     (SSI_CLKS_PER_MS / 16)

//*****************************************************************************
//
// Labels for the instructions supported by the Winbond part.
//
//*****************************************************************************
#define INSTR_WRITE_ENABLE      0x06
#define INSTR_WRITE_DISABLE     0x04
#define INSTR_READ_STATUS       0x05
#define INSTR_WRITE_STATUS      0x01
#define INSTR_READ_DATA         0x03
#define INSTR_FAST_READ         0x0B
#define INSTR_PAGE_PROGRAM      0x02
#define INSTR_BLOCK_ERASE       0xD8
#define INSTR_SECTOR_ERASE      0x20
#define INSTR_CHIP_ERASE        0xC7
#define INSTR_POWER_DOWN        0xB9
#define INSTR_POWER_UP          0xAB
#define INSTR_MAN_DEV_ID        0x90
#define INSTR_JEDEC_ID          0x9F

//*****************************************************************************
//
// Status register bit definitions
//
//*****************************************************************************
#define STATUS_BUSY                 0x01
#define STATUS_WRITE_ENABLE_LATCH   0x02
#define STATUS_BLOCK_PROTECT_0      0x04
#define STATUS_BLOCK_PROTECT_1      0x08
#define STATUS_BLOCK_PROTECT_2      0x10
#define STATUS_TOP_BOTTON_WP        0x20
#define STATUS_REGISTER_PROTECT     0x80

//*****************************************************************************
//
// Manufacturer and device IDs that we expect to see.
//
//*****************************************************************************
#define MANUFACTURER_WINBOND    0xEF
#define DEVICE_ID_W25X20A       0x13
#define DEVICE_ID_W25X20CL      0x11

//*****************************************************************************
//
// Block, sector, page and chip sizes for the supported device.  Some of the
// code here assumes (reasonably) that these are all powers of two.
//
//*****************************************************************************
#define W25X20A_BLOCK_SIZE      (64 * 1024)
//#define W25X20A_SECTOR_SIZE     (4 * 1024)
#define W25X20A_CHIP_SIZE       (256 * 1024)

#define NRF51SSI_ONE_SHOT       (128)
//*****************************************************************************
//
// The number of times we query the device status waiting for it to be idle
// after various operations have taken place and during initialization.
//
//*****************************************************************************
#define MAX_BUSY_POLL_IDLE              100
#define MAX_BUSY_POLL_ERASE_SECTOR      (STATUS_READS_PER_MS * 250)
#define MAX_BUSY_POLL_ERASE_BLOCK       (STATUS_READS_PER_MS * 1000)
#define MAX_BUSY_POLL_ERASE_CHIP        (STATUS_READS_PER_MS * 10000)
#define MAX_BUSY_POLL_PROGRAM_PAGE      (STATUS_READS_PER_MS * 3)


//*****************************************************************************
//
// Reads the serial flash device status register.读取FLASH的状态
//
// This function reads the serial flash status register and returns the value.
//
// \return Returns the current contents of the serial flash device status
// register.
//
//*****************************************************************************
static uint8_t
SSIFlashStatusGet(void)
{
	uint8_t txdata;
	uint8_t rxdata;
	uint32_t nrfRet;
	uint8_t loopWait;

    //
    // Assert the chip select for the serial flash.
    //
    nrf_gpio_pin_clear(SPIM_CSN_PIN);

    //
    // Send the status register read instruction and read back a dummy byte.
    //
    loopWait = MAX_BUSY_POLL_IDLE;
    do 
    {
	    txdata = INSTR_READ_STATUS;
		  nrfRet = spi_master_send_recv(SPI_MASTER_0, &txdata,1,NULL,0);
		  loopWait--;
	}while(nrfRet == NRF_ERROR_BUSY && loopWait > 1);
    //
    // Write a dummy byte then read back the actual status.
    //
    txdata = 0xFF; //dummy
    rxdata =  0;
    loopWait = MAX_BUSY_POLL_IDLE;

    do
    {
	    nrfRet = spi_master_send_recv(SPI_MASTER_0, NULL,0,&rxdata,1);
	    loopWait--;
    }while(nrfRet == NRF_ERROR_BUSY && loopWait > 1);

    //
    // Deassert the chip select for the serial flash.
    //
    nrf_gpio_pin_set(SPIM_CSN_PIN);

    //
    // Return the status read.
    //
    return(rxdata);
}

//*****************************************************************************
//
// Empties the SSI receive FIFO of any data it may contain.清空缓冲中的数据
//
// \return None.
//
//*****************************************************************************
static void
SSIFlashRxFlush(void)
{
#if 0
    uint32_t ulDummy;

    while(SSIDataGetNonBlocking(SFLASH_SSI_BASE, &ulDummy))
    {
        //
        // Spin until there is no more data to read.
        //
    }
#endif
}

//*****************************************************************************
//
// Write an instruction to the serial flash.写命令
//
// \param ucInstruction is the instruction code that is to be sent.
// \param ucData is a pointer to optional instruction data that will be sent
// following the instruction code provided in \e ucInstruction.  This parameter
// is ignored if \e usLen is 0.
// \param usLen is the length of the optional instruction data.
//
// This function writes an instruction to the serial flash along with any
// provided instruction-specific data.  On return, the flash chip select
// remains asserted allowing an immediate call to SSIFlashInstructionRead().
// To finish an instruction and deassert the chip select, a call must be made
// to SSIFlashInstructionEnd() after this call.
//
// It is assumed that the caller has already determined that the serial flash
// is not busy and is able to accept a new instruction at this point.
//
//*****************************************************************************
static void
SSIFlashInstructionWrite(uint8_t ucInstruction, uint8_t *ucData,
                         unsigned short usLen)
{
    uint32_t nrfRet;
	uint8_t loopWait;
    //
    // Throw away any data that may be sitting in the receive FIFO.
    //
    SSIFlashRxFlush();

    //
    // Assert the chip select for the serial flash.
    //
 	nrf_gpio_pin_clear(SPIM_CSN_PIN);    

#if 0
 	//
    // Send the instruction byte and receive a dummy byte to pace the
    // transaction.
    //
    spi_master_send_recv(SPI_MASTER_0, &ucInstruction,1,&rxdata,1);

    //
    // Send any optional bytes.
    //
    txdata = 0xFF;
    for(ulLoop = 0; ulLoop < (uint32_t)usLen; ulLoop++)
    {
        //SSIDataPut(SFLASH_SSI_BASE, ucData[ulLoop]);
        //SSIDataGet(SFLASH_SSI_BASE, &ulDummy);
        spi_master_send_recv(SPI_MASTER_0, &ucInstruction,1,&rxdata,1);
        ucData[ulLoop] = rxdata;
    }
 #endif 
  	//
    // Send the instruction byte and receive a dummy byte to pace the
    // transaction.
    //
    loopWait = MAX_BUSY_POLL_IDLE;
    do
    {
	    nrfRet =  spi_master_send_recv(SPI_MASTER_0, &ucInstruction,1,NULL,0);
	    loopWait--;
    }while(nrfRet == NRF_ERROR_BUSY && loopWait > 1);
    //
    // Send any optional bytes.
    //
    loopWait = MAX_BUSY_POLL_IDLE;
    if(ucData && usLen > 0)
    {
    	do
    	{
		 	nrfRet = spi_master_send_recv(SPI_MASTER_0, ucData,(uint8_t)usLen,NULL,0);
		 	loopWait--;
	 	}while(nrfRet == NRF_ERROR_BUSY && loopWait > 1);
	 }
}

//*****************************************************************************
//
// Write additional data following an instruction to the serial flash.写数据
//
// \param ucData is a pointer to data that will be sent to the device.
// \param usLen is the length of the data to send.
//
// This function writes a block of data to the serial flash device.  Typically
// this will be data to be written to a flash page.
//
// It is assumed that SSIFlashInstructionWrite() has previously been called to
// set the device chip select and send the initial instruction to the device.
//
//*****************************************************************************
static void
SSIFlashInstructionDataWrite(uint8_t *ucData, unsigned short usLen)
{
	uint32_t nrfRet;
	uint8_t loopWait;
	loopWait = MAX_BUSY_POLL_IDLE;
	do
	{
		nrfRet =  spi_master_send_recv(SPI_MASTER_0, ucData,(uint8_t)usLen,NULL,0);
		loopWait--;
	}while(nrfRet == NRF_ERROR_BUSY && loopWait > 1);
}

//*****************************************************************************
//
// Read data from the serial flash following the write portion of an读数据
// instruction.
//
// \param ucData is a pointer to storage for the bytes read from the serial
// flash.
// \param ulLen is the number of bytes to read from the device.
//
// This function read a given number of bytes from the device.  It is assumed
// that the flash chip select is already asserted on entry and that an
// instruction has previously been written using a call to
// SSIFlashInstructionWrite().
//
//*****************************************************************************
static void
SSIFlashInstructionRead(uint8_t *ucData, uint32_t ulLen)
{
	uint32_t nrfRet;
	uint8_t loopWait;
	loopWait = MAX_BUSY_POLL_IDLE;
	do{
		nrfRet = spi_master_send_recv(SPI_MASTER_0, NULL,0, ucData, (uint8_t)ulLen);
		loopWait--;
	}while(nrfRet == NRF_ERROR_BUSY && loopWait > 1);

}

//*****************************************************************************
//
// Finish an instruction by deasserting the serial flash chip select.中断片选
//
// This function must be called following SSIFlashInstructionWrite() and,
// depending upon the instruction, SSIFlashInstructionRead() to complete the
// instruction by deasserting the chip select to the serial flash device.
//
//*****************************************************************************
static void
SSIFlashInstructionEnd(void)
{
    //
    // Pull CS high to deassert it and complete the previous instruction.
    //
    nrf_gpio_pin_set(SPIM_CSN_PIN);    
}

//*****************************************************************************
//
// Waits for the flash device to report that it is idle.
//
// \param ulMaxRetries is the maximum number of times the serial flash device
// should be polled before we give up and report an error.  If this value is
// 0, the function continues polling indefinitely.
//
// This function polls the serial flash device and returns when either the
// maximum number of polling attempts is reached or the device reports that it
// is no longer busy.
//
// \return Returns \e true if the device reports that it is idle before the
// specified number of polling attempts is exceeded or \e false otherwise.等待空闲
//
//*****************************************************************************
static bool
SSIFlashIdleWait(uint32_t ulMaxRetries)
{
    uint32_t ulDelay;
    bool bBusy;

    //
    // Wait for the device to be ready to receive an instruction.
    //
    ulDelay = ulMaxRetries;

    do
    {
        bBusy = bsp_ssi_flash_is_busy();

        //
        // Increment our counter.  If we have waited too long, assume the
        // device is not present.
        //
        ulDelay--;
    }
    while(bBusy && (ulDelay || !ulMaxRetries));

    //
    // If we get here and we're still busy, we need to return false to indicate
    // a problem.
    //
    return(!bBusy);
}

//*****************************************************************************
//
// Sends a command to the flash to enable program and erase operations.写使能
//
// This function sends a write enable instruction to the serial flash device
// in preparation for an erase or program operation.
//
// \return Returns \b true if the instruction was accepted or \b false if
// write operations could not be enabled.
//
//*****************************************************************************
static bool
SSIFlashWriteEnable(void)
{
    bool bRetcode;
    uint8_t ucStatus;

    //
    // Issue the instruction we need to write-enable the chip.
    //
    SSIFlashInstructionWrite(INSTR_WRITE_ENABLE, (uint8_t *)0, 0);
    SSIFlashInstructionEnd();

    //
    // Wait for the instruction to complete.
    //
    bRetcode = SSIFlashIdleWait(MAX_BUSY_POLL_IDLE);

    //
    // Is the flash idle?
    //
    if(bRetcode)
    {
        //
        // Read the status and make sure that the Write Enable Latch bit is
        // set (indicating that a write may proceed).
        //
        ucStatus = SSIFlashStatusGet();

        bRetcode = (ucStatus & STATUS_WRITE_ENABLE_LATCH) ? true : false;
    }

    //
    // Tell the caller how we got on.
    //
    return(bRetcode);
}

//*****************************************************************************
//
//! Returns the manufacturer and device IDs for the attached serial flash.
//!
//! \param pucManufacturer points to storage which will be written with the
//! manufacturer ID of the attached serial flash device.
//! \param pucDevice points to storage which will be written with the device
//! ID of the attached serial flash device.
//!
//! This function may be used to determine the manufacturer and device IDs
//! of the attached serial flash device.
//!
//! \return Returns \b true on success or \b false on failure.返回连接的串行闪存的制造商和设备ID 。
//
//*****************************************************************************
static bool
SSIFlashIDGet(uint8_t *pucManufacturer, uint8_t *pucDevice)
{
    bool bRetcode;
    uint8_t pucBuffer[3];

    //
    // Wait for the flash to be idle.
    //
    bRetcode = SSIFlashIdleWait(MAX_BUSY_POLL_IDLE);

    //
    // If the device is not idle, return an error.
    //
    if(!bRetcode)
    {
        return(bRetcode);
    }

    //
    // Now perform the instruction we need to read the IDs.
    //
    pucBuffer[0] = 0;
    pucBuffer[1] = 0;
    pucBuffer[2] = 0;
    SSIFlashInstructionWrite(INSTR_MAN_DEV_ID, pucBuffer, 3);
    SSIFlashInstructionRead(pucBuffer, 2);
    SSIFlashInstructionEnd();

    //
    // Copy the returned IDs into the caller's storage.
    //
    *pucManufacturer = pucBuffer[0];
    *pucDevice = pucBuffer[1];

    //
    // Tell the caller everything went well.
    //
    return(true);
}

#if (SPI0_ENABLED == 1)
/**@brief Handler for SPI0 master events.
 *
 * @param[in] event SPI master event.
 */
void spi_master_0_event_handler(spi_master_evt_t event)
{
   // uint32_t err_code = NRF_SUCCESS;
   // bool result = false;
    switch(event.evt_type)
    {
        case SPI_MASTER_EVT_TRANSFER_STARTED:
            // Check if received data is correct.
            ssi_ctx_priv.flags.m_transfer_completed = true;
            break;

        default:
            // No implementation needed.
            break;
    }
}
#endif // (SPI0_ENABLED == 1)

//*****************************************************************************
//
//! Initializes the SSI port and determines if the serial flash is available.
//!
//! This function must be called prior to any other function offered by the
//! serial flash driver.  It configures the SSI port to run in mode 0 at 10MHz
//! and queries the ID of the serial flash device to ensure that it is
//! available.
//!
//! \note SSI0 is shared between the serial flash and the SDCard on the
//! dk-lm3s9b96 boards.  Two independent GPIOs are used to provide chip selects
//! for these two devices but care must be taken when using both in a single
//! application, especially during initialization of the SDCard when the SSI
//! clock rate must initially be set to 400KHz and later increased.  Since both
//! the SSI flash and SDCard drivers initialize the SSI peripheral, application
//! writers must be aware of the possible contention and ensure that they do
//! not allow the possibility of two different interrupt handlers or execution
//! threads from attempting to access both peripherals simultaneously.
//!
//! This driver assumes that the application is aware of the possibility of
//! contention and has been designed with this in mind.  Other than disabling
//! the SDCard when an attempt is made to access the serial flash, no code is
//! included here to arbitrate for ownership of the SSI peripheral.
//!
//! \return Returns \b true on success or \b false if an error is reported or
//! the expected serial flash device is not present.			FLASH初始化
//!
//*****************************************************************************
bool bsp_ssi_flash_init(void)
{
    bool bRetcode = false;
    uint8_t ucManufacturer, ucDevice;
    uint8_t ucStatus;

    memset(&ssi_ctx_priv,0,sizeof(ssi_ctx_priv));
    //
    // Initialize nf51 spi hardware driver
    //
    nrf_gpio_pin_set(SPIM_CSN_PIN);
    nrf_gpio_cfg_output(SPIM_CSN_PIN);
 	
		spi_master_config_t spi_config = SPI_MASTER_INIT_DEFAULT;
		spi_config.SPI_Pin_SCK  = SPIM_SCK_PIN;
		spi_config.SPI_Pin_MISO = SPIM_MISO_PIN;
		spi_config.SPI_Pin_MOSI = SPIM_MOSI_PIN;
		spi_config.SPI_Freq = SPIM_FREQUENCY_FREQUENCY_M8;
		spi_config.SPI_CONFIG_ORDER = SPI_CONFIG_ORDER_MsbFirst;
	  
		if(spi_master_open(SPI_MASTER_0, &spi_config) != NRF_SUCCESS)
		{
			return bRetcode;
		}
      //spi_master_evt_handler_reg(SPI_MASTER_0, spi_master_0_event_handler);

    //
    // Wait for the device to be ready to receive an instruction.
    //
    bRetcode = SSIFlashIdleWait(MAX_BUSY_POLL_IDLE);

    //
    // If the device didn't report that it was idle, assume it isn't there
    // and return an error.
    //
    if(!bRetcode)
    {
        return(bRetcode);
    }

    //
    // Read the device ID and check to see that it is one we recognize.
    //
    bRetcode = SSIFlashIDGet(&ucManufacturer, &ucDevice);
    if(!bRetcode || (ucManufacturer != MANUFACTURER_WINBOND) ||
       (ucDevice != DEVICE_ID_W25X20CL))
    {
        //
        // This is not a device we recognize so return an error.
        //
        return(false);
    }
    else
    {
        //
        // All is well.
        //
        ucStatus = SSIFlashStatusGet();
        ZK_TRACE("fs:%x\n",ucStatus);
        if(ucStatus & ( STATUS_BLOCK_PROTECT_0 |
                        STATUS_BLOCK_PROTECT_1 |
                        STATUS_BLOCK_PROTECT_2 |
                        STATUS_TOP_BOTTON_WP   |
                        STATUS_REGISTER_PROTECT))
        {
            //
            // Issue the instruction we need to write-enable the chip.
            //
            uint8_t status = 0x02;
            SSIFlashInstructionWrite(INSTR_WRITE_STATUS, &status, sizeof(status));
            SSIFlashInstructionEnd();

            //
            // Wait for the instruction to complete.
            //
            bRetcode = SSIFlashIdleWait(MAX_BUSY_POLL_IDLE);
        }
        return(true);
    }
}

//*****************************************************************************
//
//! Determines if the serial flash is able to accept a new instruction.
//!
//! This function reads the serial flash status register and determines whether
//! or not the device is currently busy.  No new instruction may be issued to
//! the device if it is busy.
//!
//! \return Returns \b true if the device is busy and unable to receive a new
//! instruction or \b false if it is idle.				判断是否可以接受新的命令
//
//*****************************************************************************
bool bsp_ssi_flash_is_busy(void)
{
    uint8_t ucStatus;

    //
    // Get the flash status.
    //
    ucStatus = SSIFlashStatusGet();

    //
    // Is the busy bit set?
    //
    return((ucStatus & STATUS_BUSY) ? true : false);
}



//*****************************************************************************
//
//! Returns the size of a sector for this device.
//!
//! This function returns the size of an erasable sector for the serial flash
//! device.  All addresses passed to SSIFlashSectorErase() must be aligned on
//! a sector boundary.
//!
//! \return Returns the number of bytes in a sector.	  返回扇区的大小
//
//*****************************************************************************
uint32_t bsp_ssi_flash_sector_size_get(void)
{
    //
    // This device supports 4KB sectors.
    //
    return(W25X20A_SECTOR_SIZE);
}

//*****************************************************************************
//
//! Returns the size of a block for this device.
//!
//! This function returns the size of an erasable block for the serial flash
//! device.  All addresses passed to SSIFlashBlockErase() must be aligned on
//! a block boundary.
//!
//! \return Returns the number of bytes in a block.		   返回块的大小
//
//*****************************************************************************
uint32_t bsp_ssi_flash_block_size_get(void)
{
    //
    // This device support 64KB blocks.
    //
    return(W25X20A_BLOCK_SIZE);
}

//*****************************************************************************
//
//! Returns the total amount of storage offered by this device.
//!
//! This function returns the size of the programmable area provided by the
//! attached SSI flash device.
//!
//! \return Returns the number of bytes in the device.	 返回正品FLASH的大小
//
//*****************************************************************************
uint32_t bsp_ssi_flash_chip_size_get(void)
{
    //
    // This device is 256KB in size.
    //
    return(W25X20A_CHIP_SIZE);
}

//*****************************************************************************
//
//! Erases the contents of a single serial flash sector.
//!
//! \param ulAddress is the start address of the sector which is to be erased.
//! This value must be an integer multiple of the sector size returned
//! by SSIFlashSectorSizeGet().
//! \param bSync should be set to \b true if the function is to block until
//! the erase operation is complete or \b false to return immediately after
//! the operation is started.
//!
//! This function erases a single sector of the serial flash, returning all
//! bytes in that sector to their erased value of 0xFF.  The sector size and,
//! hence, start address granularity can be determined by calling
//! SSIFlashSectorSizeGet().
//!
//! The function may be synchronous (\e bSync set to \b true) or asynchronous
//! (\e bSync set to \b false).  If asynchronous, the caller is responsible for
//! ensuring that no further serial flash operations are requested until the
//! erase operation has completed.  The state of the device may be queried by
//! calling SSIFlashIsBusy().
//!
//! Three options for erasing are provided. Sectors provide the smallest
//! erase granularity, blocks provide the option of erasing a larger section of
//! the device in one operation and, finally, the whole device may be erased
//! in a single operation via SSIFlashChipErase().
//!
//! \note This operation will take between 120mS and 250mS to complete. If the
//! \e bSync parameter is set to \b true, this function will, therefore, not
//! not return for a significant period of time.
//!
//! \return Returns \b true on success or \b false on failure.	  擦除一个扇区
//
//*****************************************************************************
bool bsp_ssi_flash_sector_erase(uint32_t ulAddress, bool bSync)
{
    bool bRetcode;
    uint8_t pucBuffer[3];

    //
    // Make sure the address passed is aligned correctly.
    //
    if(ulAddress & (W25X20A_SECTOR_SIZE - 1))
    {
        //
        // Oops - it's not on a sector boundary so fail the call.
        //
        return(false);
    }

    //
    // Wait for the flash to be idle.
    //
    bRetcode = SSIFlashIdleWait(0);

    //
    // If the device is not idle, return an error.
    //
    if(!bRetcode)
    {
        return(bRetcode);
    }

    //
    // Enable write operations.
    //
    bRetcode = SSIFlashWriteEnable();

    //
    // If write operations could not be enabled, return the error.
    //
    if(!bRetcode)
    {
        return(bRetcode);
    }

    //
    // Now perform the instruction we need to erase the sector.
    //
    pucBuffer[0] = (uint8_t)((ulAddress >> 16) & 0xFF);
    pucBuffer[1] = (uint8_t)((ulAddress >> 8) & 0xFF);
    pucBuffer[2] = (uint8_t)(ulAddress & 0xFF);
    SSIFlashInstructionWrite(INSTR_SECTOR_ERASE, pucBuffer, 3);
    SSIFlashInstructionEnd();

    //
    // Wait for the instruction to complete if the function is being called
    // synchronously.
    //
    if(bSync)
    {
        bRetcode = SSIFlashIdleWait(MAX_BUSY_POLL_ERASE_SECTOR);
    }

    //
    // Tell the caller how things went.
    //
    return(bRetcode);
}

//*****************************************************************************
//
//! Erases the contents of a single serial flash block.
//!
//! \param ulAddress is the start address of the block which is to be erased.
//! This value must be an integer multiple of the block size returned
//! by SSIFlashBlockSizeGet().
//! \param bSync should be set to \b true if the function is to block until
//! the erase operation is complete or \b false to return immediately after
//! the operation is started.
//!
//! This function erases a single block of the serial flash, returning all
//! bytes in that block to their erased value of 0xFF.  The block size and,
//! hence, start address granularity can be determined by calling
//! SSIFlashBlockSizeGet().
//!
//! The function may be synchronous (\e bSync set to \b true) or asynchronous
//! (\e bSync set to \b false).  If asynchronous, the caller is responsible for
//! ensuring that no further serial flash operations are requested until the
//! erase operation has completed.  The state of the device may be queried by
//! calling SSIFlashIsBusy().
//!
//! Three options for erasing are provided. Sectors provide the smallest
//! erase granularity, blocks provide the option of erasing a larger section of
//! the device in one operation and, finally, the whole device may be erased
//! in a single operation via SSIFlashChipErase().
//!
//! \note This operation will take between 400mS and 1000mS to complete.  If the
//! \e bSync parameter is set to \b true, this function will, therefore, not
//! not return for a significant period of time.
//!
//! \return Returns \b true on success or \b false on failure.		 擦除一个块
//
//*****************************************************************************
bool bsp_ssi_flash_block_erase(uint32_t ulAddress, bool bSync)
{
    bool bRetcode;
    uint8_t pucBuffer[3];

    //
    // Make sure the address passed is aligned correctly.
    //
    if(ulAddress & (W25X20A_BLOCK_SIZE - 1))
    {
        //
        // Oops - it's not on a block boundary so fail the call.
        //
        return(false);
    }

    //
    // Wait for the flash to be idle.
    //
    bRetcode = SSIFlashIdleWait(0);

    //
    // If the device is not idle, return an error.
    //
    if(!bRetcode)
    {
        return(bRetcode);
    }

    //
    // Enable write operations.
    //
    bRetcode = SSIFlashWriteEnable();

    //
    // If write operations could not be enabled, return the error.
    //
    if(!bRetcode)
    {
        return(bRetcode);
    }

    //
    // Now perform the instruction we need to erase the block.
    //
    pucBuffer[0] = (uint8_t)((ulAddress >> 16) & 0xFF);
    pucBuffer[1] = (uint8_t)((ulAddress >> 8) & 0xFF);
    pucBuffer[2] = (uint8_t)(ulAddress & 0xFF);
    SSIFlashInstructionWrite(INSTR_BLOCK_ERASE, pucBuffer, 3);
    SSIFlashInstructionEnd();

    //
    // Wait for the instruction to complete if the function is being called
    // synchronously.
    //
    if(bSync)
    {
        bRetcode = SSIFlashIdleWait(MAX_BUSY_POLL_ERASE_BLOCK);
    }

    //
    // Tell the caller how things went.
    //
    return(bRetcode);
}

//*****************************************************************************
//
//! Erases the entire serial flash device.
//!
//! \param bSync should be set to \b true if the function is to block until
//! the erase operation is complete or \b false to return immediately after
//! the operation is started.
//!
//! This function erases the entire serial flash device, returning all
//! bytes in the device to their erased value of 0xFF.
//!
//! The function may be synchronous (\e bSync set to \b true) or asynchronous
//! (\e bSync set to \b false).  If asynchronous, the caller is responsible for
//! ensuring that no further serial flash operations are requested until the
//! erase operation has completed.  The state of the device may be queried by
//! calling SSIFlashIsBusy().
//!
//! \note This operation will take between 6 and 10 seconds to complete.  If the
//! \e bSync parameter is set to \b true, this function will, therefore, not
//! not return for a significant period of time.
//!
//! \return Returns \b true on success or \b false on failure.		  擦除整个FLASH
//
//*****************************************************************************
bool bsp_ssi_flash_chip_erase(bool bSync)
{
    bool bRetcode;

    //
    // Wait for the flash to be idle.
    //
    bRetcode = SSIFlashIdleWait(0);

    //
    // If the device is not idle, return an error.
    //
    if(!bRetcode)
    {
        return(bRetcode);
    }

    //
    // Enable write operations.
    //
    bRetcode = SSIFlashWriteEnable();

    //
    // If write operations could not be enabled, return the error.
    //
    if(!bRetcode)
    {
        return(bRetcode);
    }

    //
    // Now perform the instruction we need to erase the chip.
    //
    SSIFlashInstructionWrite(INSTR_CHIP_ERASE, (uint8_t *)0, 0);
    SSIFlashInstructionEnd();

    //
    // Wait for the instruction to complete if the function is being called
    // synchronously.
    //
    if(bSync)
    {
        bRetcode = SSIFlashIdleWait(MAX_BUSY_POLL_ERASE_CHIP);
    }

    //
    // Tell the caller how things went.
    //
    return(bRetcode);
}

//*****************************************************************************
//
//! Reads a block of serial flash into a buffer.
//!
//! \param ulAddress is the serial flash address of the first byte to be read.
//! \param ulLength is the number of bytes of data to read.
//! \param pucDst is a pointer to storage for the data read.
//!
//! This function reads a contiguous block of data from a given address in the
//! serial flash device into a buffer supplied by the caller.
//!
//! \return Returns the number of bytes read.			从块中读数据入缓冲
//
//*****************************************************************************
uint32_t
bsp_ssi_flash_read(uint32_t ulAddress, uint32_t ulLength,
             uint8_t *pucDst)
{
    bool bRetcode;
    uint8_t pucBuffer[3];

    //
    // Wait for the flash to be idle.
    //
    bRetcode = SSIFlashIdleWait(0);

    //
    // If the device is not idle, return an error.
    //
    if(!bRetcode)
    {
        return(0);
    }

    //
    // Send the read instruction and start address.
    //
    pucBuffer[0] = (uint8_t)((ulAddress >> 16) & 0xFF);
    pucBuffer[1] = (uint8_t)((ulAddress >> 8) & 0xFF);
    pucBuffer[2] = (uint8_t)(ulAddress & 0xFF);
    SSIFlashInstructionWrite(INSTR_READ_DATA, pucBuffer, 3);

    //
    // Read back the data.
    //
    uint32_t left_page = ulLength;
    uint8_t* shot_start = pucDst;
    do
    {
    	uint32_t shot_page = left_page > NRF51SSI_ONE_SHOT ? NRF51SSI_ONE_SHOT : left_page;
    	SSIFlashInstructionRead(shot_start, shot_page);
    	left_page -= shot_page;
    	shot_start += shot_page;
    }while(left_page);

    //
    // Tell the flash device we have read everything we need.
    //
    SSIFlashInstructionEnd();

    //
    // Return the number of bytes read.
    //
    return(ulLength);
}

//*****************************************************************************
//
//! Writes a block of data to the serial flash device.
//!
//! \param ulAddress is the first serial flash address to be written.
//! \param ulLength is the number of bytes of data to write.
//! \param pucSrc is a pointer to the data which is to be written.
//!
//! This function writes a block of data into the serial flash device at the
//! given address.  It is assumed that the area to be written has previously
//! been erased.
//!
//! \return Returns the number of bytes written.		   写块数据
//
//*****************************************************************************
uint32_t bsp_ssi_flash_write(uint32_t ulAddress, uint32_t ulLength,uint8_t *pucSrc)
{
    bool bRetcode;
    uint32_t ulLeft, ulStart, ulPageLeft;
    uint8_t pucBuffer[3];
    uint8_t *pucStart;

    //
    // Wait for the flash to be idle.
    //
    bRetcode = SSIFlashIdleWait(MAX_BUSY_POLL_IDLE);

    //
    // If the device is not idle, return an error.
    //
    if(!bRetcode)
    {
        return(bRetcode);
    }

    //
    // Get set up to start writing pages.
    //
    ulStart = ulAddress;
    ulLeft = ulLength;
    pucStart = pucSrc;

    //
    // Keep writing pages until we have nothing left to write.
    //
    while(ulLeft)
    {
        //
        // How many bytes do we have to write in the current page?
        //
        ulPageLeft = W25X20A_PAGE_SIZE - (ulStart & (W25X20A_PAGE_SIZE - 1));

        //
        // How many bytes can we write in the current page?
        //
        ulPageLeft = (ulPageLeft >= ulLeft) ? ulLeft : ulPageLeft;

        //
        // Enable write operations.
        //
        if(!SSIFlashWriteEnable())
        {
            //
            // If we can't write enable the device, exit, telling the caller
            // how many bytes we've already written.
            //
            return(ulLength - ulLeft);
        }

        //
        // Write a chunk of data into one flash page.
        //
        pucBuffer[0] = (uint8_t)((ulStart >> 16) & 0xFF);
        pucBuffer[1] = (uint8_t)((ulStart >> 8) & 0xFF);
        pucBuffer[2] = (uint8_t)(ulStart & 0xFF);
        SSIFlashInstructionWrite(INSTR_PAGE_PROGRAM, pucBuffer, 3);

        uint32_t left_page = ulPageLeft;
        uint8_t* shot_start = pucStart;
        do
        {
        	uint32_t shot_page = left_page > NRF51SSI_ONE_SHOT ? NRF51SSI_ONE_SHOT : left_page;
        	SSIFlashInstructionDataWrite(shot_start, shot_page);
        	left_page -= shot_page;
        	shot_start += shot_page;
        }while(left_page);

        SSIFlashInstructionEnd();
        //
        // Wait for the page write to complete.
        //
        bRetcode = SSIFlashIdleWait(MAX_BUSY_POLL_PROGRAM_PAGE);
        if(!bRetcode)
        {
            //
            // If we timed out waiting for the program operation to finish,
            // exit, telling the caller how many bytes we've already written.
            //
            return(ulLength - ulLeft);
        }

        //
        // Update our pointers and counters for the next page.
        //
        ulLeft -= ulPageLeft;
        ulStart += ulPageLeft;
        pucStart += ulPageLeft;
    }

    //
    // If we get here, all is well and we wrote all the required data.
    //
    return(ulLength);
}

//*****************************************************************************
//
//! Force device to enter deep sleep mode.
//!
//!
//! This function puts the device into deep sleep mode to save up to 45uA power consumption
//! Note only Release-from-power-down/device will restore the device into operation 
//!
//!  \return whether the operation is success.
//
//*****************************************************************************
bool bsp_ssi_flash_sleep(void)
{
    bool bRetcode = true;

    //
    // Wait for the flash to be idle.
    //
    bRetcode = SSIFlashIdleWait(0);

    //
    // If the device is not idle, return an error.
    //
    if(!bRetcode)
    {
        return(bRetcode);
    }

    //
    // Enable write operations.
    //
    bRetcode = SSIFlashWriteEnable();

    //
    // If write operations could not be enabled, return the error.
    //
    if(!bRetcode)
    {
        return(bRetcode);
    }

    //
    // Now perform the instruction we need to erase the chip.
    //
    SSIFlashInstructionWrite(INSTR_POWER_DOWN, (uint8_t *)0, 0);
    SSIFlashInstructionEnd();

    //
    // Tell the caller how things went.
    //
    return(bRetcode);
}


//*****************************************************************************
//
//! Wake up device from sleep mode.
//!
//!
//! This function wakes up the device back to normal operation
//! 
//!
//!  \return whether the operation is success.
//
//*****************************************************************************
bool bsp_ssi_flash_wakeup(void)
{
    bool bRetcode = true;
    //
    // Now perform the instruction we need to erase the chip.
    //
    SSIFlashInstructionWrite(INSTR_POWER_UP, (uint8_t *)0, 0);
    SSIFlashInstructionEnd();

    //
    // Tell the caller how things went.
    //

    return(bRetcode);
}
//*****************************************************************************
//
// Close the Doxygen group.
//! @}
//
//*****************************************************************************

