/*
 * Copyright (c) 2016, Freescale Semiconductor, Inc.
 * Copyright 2016-2018 NXP
 * All rights reserved.
 *
 *
 * SPDX-License-Identifier: BSD-3-Clause
 */

#include "fsl_flexspi.h"
#include "app.h"
#include "fsl_debug_console.h"
#include "fsl_cache.h"

#include "pin_mux.h"
#include "board.h"
#include "clock_config.h"
#include "fsl_common.h"
/*******************************************************************************
* Definitions
******************************************************************************/

#define FIRMWARE_ADDRESS_START    			0x20000
#define FIRMWARE_LENGTH					0x10000
#define NEW_FIRMWARE_ADDRESS_START			(8u * 1024u * 1024u)
#define SECTOR_SIZE					0x1000
#define PAGE_SIZE	                        	256

#define FLASH_A_SIZE                                    (8u * 1024u * 1024u)
/*******************************************************************************
* Prototypes
******************************************************************************/

/*******************************************************************************
 * Variables
 ******************************************************************************/
#pragma data_alignment=8
static uint8_t s_nor_program_buffer[256];
static uint8_t s_nor_read_buffer[256];

extern status_t flexspi_nor_flash_erase_sector(FLEXSPI_Type *base, uint32_t address);
extern status_t flexspi_nor_flash_page_program(FLEXSPI_Type *base, uint32_t dstAddr, const uint32_t *src);
extern void flexspi_nor_flash_init(FLEXSPI_Type *base);

extern __ramfunc status_t flexspi_nor_flash_erase_sector_RAM(FLEXSPI_Type *base, uint32_t address);
extern __ramfunc status_t flexspi_nor_flash_page_program_RAM(FLEXSPI_Type *base, uint32_t dstAddr, const uint32_t *src);
extern __ramfunc void FLEXSPI_UpdateLUT_RAM(FLEXSPI_Type *base, uint32_t index, const uint32_t *cmd, uint32_t count);
extern __ramfunc void FLEXSPI_SoftwareReset_RAM(FLEXSPI_Type *base);
extern __ramfunc status_t flexspi_nor_wait_bus_busy_RAM(FLEXSPI_Type *base);
extern __ramfunc void AHBPrefetchEnable(FLEXSPI_Type *base);
extern __ramfunc void AHBPrefetchDisable(FLEXSPI_Type *base);

extern void FLEXSPI_DMA_Init(void);

volatile static uint32_t uiTimer = 0;
/*******************************************************************************
 * Code
 ******************************************************************************/


uint32_t customLUT[CUSTOM_LUT_LENGTH] = {
        /* Normal read mode -SDR */

        [4 * NOR_CMD_LUT_SEQ_IDX_READ_NORMAL] =
            FLEXSPI_LUT_SEQ(kFLEXSPI_Command_SDR, kFLEXSPI_1PAD, 0x03, kFLEXSPI_Command_RADDR_SDR, kFLEXSPI_1PAD, 0x18),
        [4 * NOR_CMD_LUT_SEQ_IDX_READ_NORMAL + 1] =
            FLEXSPI_LUT_SEQ(kFLEXSPI_Command_READ_SDR, kFLEXSPI_1PAD, 0x04, kFLEXSPI_Command_STOP, kFLEXSPI_1PAD, 0),

        /* Fast read mode - SDR */
        [4 * NOR_CMD_LUT_SEQ_IDX_READ_FAST] =
            FLEXSPI_LUT_SEQ(kFLEXSPI_Command_SDR, kFLEXSPI_1PAD, 0x0B, kFLEXSPI_Command_RADDR_SDR, kFLEXSPI_1PAD, 0x18),
        [4 * NOR_CMD_LUT_SEQ_IDX_READ_FAST + 1] = FLEXSPI_LUT_SEQ(
            kFLEXSPI_Command_DUMMY_SDR, kFLEXSPI_1PAD, 0x08, kFLEXSPI_Command_READ_SDR, kFLEXSPI_1PAD, 0x04),

        /* Fast read quad mode - SDR */
        [4 * NOR_CMD_LUT_SEQ_IDX_READ_FAST_QUAD] =
            FLEXSPI_LUT_SEQ(kFLEXSPI_Command_SDR, kFLEXSPI_1PAD, 0xEB, kFLEXSPI_Command_RADDR_SDR, kFLEXSPI_4PAD, 0x18),
        [4 * NOR_CMD_LUT_SEQ_IDX_READ_FAST_QUAD + 1] = FLEXSPI_LUT_SEQ(
            kFLEXSPI_Command_DUMMY_SDR, kFLEXSPI_4PAD, 0x06, kFLEXSPI_Command_READ_SDR, kFLEXSPI_4PAD, 0x04),

        /* Read extend parameters */
        [4 * NOR_CMD_LUT_SEQ_IDX_READSTATUS] =
            FLEXSPI_LUT_SEQ(kFLEXSPI_Command_SDR, kFLEXSPI_1PAD, 0x81, kFLEXSPI_Command_READ_SDR, kFLEXSPI_1PAD, 0x04),

        /* Write Enable */
        [4 * NOR_CMD_LUT_SEQ_IDX_WRITEENABLE] =
            FLEXSPI_LUT_SEQ(kFLEXSPI_Command_SDR, kFLEXSPI_1PAD, 0x06, kFLEXSPI_Command_STOP, kFLEXSPI_1PAD, 0),

        /* Erase Sector  */
        [4 * NOR_CMD_LUT_SEQ_IDX_ERASESECTOR] =
            FLEXSPI_LUT_SEQ(kFLEXSPI_Command_SDR, kFLEXSPI_1PAD, 0x20, kFLEXSPI_Command_RADDR_SDR, kFLEXSPI_1PAD, 0x18),

        /* Page Program - single mode */
        [4 * NOR_CMD_LUT_SEQ_IDX_PAGEPROGRAM_SINGLE] =
            FLEXSPI_LUT_SEQ(kFLEXSPI_Command_SDR, kFLEXSPI_1PAD, 0x02, kFLEXSPI_Command_RADDR_SDR, kFLEXSPI_1PAD, 0x18),
        [4 * NOR_CMD_LUT_SEQ_IDX_PAGEPROGRAM_SINGLE + 1] =
            FLEXSPI_LUT_SEQ(kFLEXSPI_Command_WRITE_SDR, kFLEXSPI_1PAD, 0x04, kFLEXSPI_Command_STOP, kFLEXSPI_1PAD, 0),

        /* Page Program - quad mode */
        [4 * NOR_CMD_LUT_SEQ_IDX_PAGEPROGRAM_QUAD] =
            FLEXSPI_LUT_SEQ(kFLEXSPI_Command_SDR, kFLEXSPI_1PAD, 0x32, kFLEXSPI_Command_RADDR_SDR, kFLEXSPI_1PAD, 0x18),
        [4 * NOR_CMD_LUT_SEQ_IDX_PAGEPROGRAM_QUAD + 1] =
            FLEXSPI_LUT_SEQ(kFLEXSPI_Command_WRITE_SDR, kFLEXSPI_4PAD, 0x04, kFLEXSPI_Command_STOP, kFLEXSPI_1PAD, 0),

        /* Read ID */
        [4 * NOR_CMD_LUT_SEQ_IDX_READID] =
            FLEXSPI_LUT_SEQ(kFLEXSPI_Command_SDR, kFLEXSPI_1PAD, 0xAB, kFLEXSPI_Command_DUMMY_SDR, kFLEXSPI_1PAD, 0x18),
        [4 * NOR_CMD_LUT_SEQ_IDX_READID + 1] =
            FLEXSPI_LUT_SEQ(kFLEXSPI_Command_READ_SDR, kFLEXSPI_1PAD, 0x04, kFLEXSPI_Command_STOP, kFLEXSPI_1PAD, 0),

        /* Enable Quad mode */
        [4 * NOR_CMD_LUT_SEQ_IDX_WRITESTATUSREG] =
            FLEXSPI_LUT_SEQ(kFLEXSPI_Command_SDR, kFLEXSPI_1PAD, 0x01, kFLEXSPI_Command_WRITE_SDR, kFLEXSPI_1PAD, 0x04),

        /* Enter QPI mode */
        [4 * NOR_CMD_LUT_SEQ_IDX_ENTERQPI] =
            FLEXSPI_LUT_SEQ(kFLEXSPI_Command_SDR, kFLEXSPI_1PAD, 0x35, kFLEXSPI_Command_STOP, kFLEXSPI_1PAD, 0),

        /* Exit QPI mode */
        [4 * NOR_CMD_LUT_SEQ_IDX_EXITQPI] =
            FLEXSPI_LUT_SEQ(kFLEXSPI_Command_SDR, kFLEXSPI_4PAD, 0xF5, kFLEXSPI_Command_STOP, kFLEXSPI_1PAD, 0),

        /* Read status register */
        [4 * NOR_CMD_LUT_SEQ_IDX_READSTATUSREG] =
            FLEXSPI_LUT_SEQ(kFLEXSPI_Command_SDR, kFLEXSPI_1PAD, 0x05, kFLEXSPI_Command_READ_SDR, kFLEXSPI_1PAD, 0x04),

        /* Erase whole chip */
        [4 * NOR_CMD_LUT_SEQ_IDX_ERASECHIP] =
            FLEXSPI_LUT_SEQ(kFLEXSPI_Command_SDR, kFLEXSPI_1PAD, 0xC7, kFLEXSPI_Command_STOP, kFLEXSPI_1PAD, 0),
};

__ramfunc void FlexSPI_SetChange(void)
{
  // ensure TX watemark same to DMA burst length
  FLEXSPI->IPTXFCR |= FLEXSPI_IPTXFCR_TXWMRK(0);
}
int main(void)
{
    status_t status;
    char ch;
    uint8_t *pAddress;

    BOARD_ConfigMPU();
    BOARD_InitPins();
    BOARD_BootClockRUN();
    BOARD_InitDebugConsole();
    FLEXSPI_DMA_Init();
    
    PRINTF("RWW test start ...\r\n");
    
    pAddress =(uint8_t *)0x60000000+FLASH_A_SIZE;
    
    
    // this is just for RWW test, enable cache can improve performance
    PRINTF("Disable DCACHE and ICACHE\r\n");
    SCB_DisableICache();
    SCB_DisableDCache();

    IOMUXC_GPR->GPR26 |= 0xffffffff; 
    GPIO6->DR_SET = 1<<9;
    GPIO6->GDIR |= 1<<9;
    GPIO6->GDIR |= 1<<16;
    
    
    FLEXSPI_UpdateLUT_RAM(EXAMPLE_FLEXSPI, 4, &customLUT[4], CUSTOM_LUT_LENGTH-4);  
    
    //disable interrupt setting.
    uint32_t regPrimask = DisableGlobalIRQ();
    
    FlexSPI_SetChange();  
    
    // configure to quad mode, this is to enable program with quad mode
    flexspi_nor_enable_quad_mode(EXAMPLE_FLEXSPI);
    
    //restore interrupt setting.
    EnableGlobalIRQ(regPrimask);
    
    uint32_t Index = 0;
    while (1)
    {
      for (uint32_t i = 0; i < 0x100U; i++)
      {
          s_nor_program_buffer[i] = i+Index;
      }
      Index ++;
      if(Index > 999)
      {
        Index = 0;
      }
      PRINTF("\r\ncurrent index is %d\r\n",Index);
      pAddress = (uint8_t *)(0x60800000+Index*SECTOR_SIZE);
      PRINTF("start erase flash with address 0x%x.\r\n",pAddress); 
      status = flexspi_nor_flash_erase_sector(FLEXSPI,0x800000+Index*SECTOR_SIZE);
      if(status!=kStatus_Success)
      {
        PRINTF("\r\nerase failed! -- 0x%x\r\n",status);
      }
      else
      {
        PRINTF("\r\nerase successed!\r\n");
      }
     
      PRINTF("start program flash with address 0x%x.\r\n",pAddress); 
      status = flexspi_nor_flash_page_program(FLEXSPI,0x800000+Index*SECTOR_SIZE,(uint32_t *)s_nor_program_buffer);

      if(status!=kStatus_Success)
      {
        PRINTF("\r\npage program failed! -- 0x%x\r\n",status);
      }
      else
      {
        PRINTF("\r\npage program successed!\r\n");
      }

   
      PRINTF("\r\ncheck flash address 0x%x contents.\r\n",pAddress);
      memcpy(s_nor_read_buffer, (void *)pAddress,
             sizeof(s_nor_read_buffer));
      if(!memcmp(s_nor_read_buffer,s_nor_program_buffer,256))
      {
        PRINTF("FlexSPI read/write successfully!\r\n");
      }
      else
      {
        for(uint32_t i=0;i<FLASH_PAGE_SIZE;i++)
        {
          PRINTF("0x%x,",s_nor_read_buffer[i]);
          if(((i%16)==0)&&(i!=0))
          {
            PRINTF("\r\n");
          }
        }
      }
      
      ch = GETCHAR();
      PUTCHAR(ch);
    }
}
