/*
 * @file mx25l512.c
 *
 *  @brief This file contains the driver function of flash memory chip mx25l512
 *
 *	@copyright Copyright 2023 Antaris, Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */


#include "stdio.h"
#include "string.h"
#include "mx25l512.h"
#include "exo_io_al_qspi_common.h"
#ifndef LINUX_TEMP_PORT
#include "stm32f7xx_hal.h"
#endif


#include "exo_osal_thread.h"

/*
 * Size of MX25L512 is 64MB start from the address 0x0
 */
#define MX25L512_MEMORY_START_ADDRESS      0x0
#define MX25L512_MEMORY_END_ADDRESS        (MX25L512_MEMORY_START_ADDRESS + (64*(1024*1024)))

/*
 * This function is use to verify address range of MX25L512
 */
static uint8_t verify_address_range(uint32_t w_address)
{
	if ((w_address >= MX25L512_MEMORY_START_ADDRESS) && (w_address < MX25L512_MEMORY_END_ADDRESS))
	{
		return 0;
	}
	else
	{
		return 1;
	}
}

static uint8_t QSPI_WriteEnable(mx25l512_dev_t *dev_hdl);
static uint8_t QSPI_AutoPollingMemReady(mx25l512_dev_t *dev_hdl);
static uint8_t QSPI_Configuration(mx25l512_dev_t *dev_hdl);
static uint8_t QSPI_ResetChip(mx25l512_dev_t *dev_hdl);


/* Un-comment below macro to Enable NOR test */
//#define SPI_NOR_TEST_ENB

#ifdef SPI_NOR_TEST_ENB

#define SECTORS_COUNT 100
static void qspi_nor_test(mx25l512_dev_t *dev_hdl);
uint8_t buffer_test[MEMORY_SECTOR_SIZE];


/*
 * NOR flash test function
 * This function write memory and activated with memory mapped  mode
 * So data can be read via 0x9000000
 */
static void qspi_nor_test(mx25l512_dev_t *dev_hdl)
{
    uint32_t var = 0;


  	for (var = 0; var < MEMORY_SECTOR_SIZE; var++) {
  		buffer_test[var] = (var & 0xff);
  	}

  	for (var = 0; var < SECTORS_COUNT; var++) {

  		if (mx25l512_EraseSector(dev_hdl, var * MEMORY_SECTOR_SIZE,
  				(var + 1) * MEMORY_SECTOR_SIZE - 1) != HAL_SCS) {

  			while (1);  //breakpoint - error detected
  		}

  		if (mx25l512_write(dev_hdl, buffer_test, var * MEMORY_SECTOR_SIZE,
  				sizeof(buffer_test)) != HAL_SCS) {

  			while (1)
  				;  //breakpoint - error detected
  		}

  	}


  	/* testing : expected to erase 3 sector ( 4KB*3 = 12KB)
  	 * start address : 0
  	 * end address : 8448 >> 8448/1024 = 8.25 = 2 sector full and 256 bytes in 3rd sector
  	 */
//	if (mx25l512_EraseSector(dev_hdl, 0, (4096*2) + 256) != HAL_SCS)
//	{
//
//			while (1);  //breakpoint - error detected
//	}




  	if (mx25l512_EnableMemoryMappedMode(dev_hdl) != HAL_SCS) {

  		while (1)
  			; //breakpoint - error detected
  	}

  	for (var = 0; var < SECTORS_COUNT; var++)
  	{
  		if (memcmp(buffer_test,
  				(uint8_t*) (0x90000000 + var * MEMORY_SECTOR_SIZE),
  				MEMORY_SECTOR_SIZE) != HAL_SCS) {
  			DEBUG_CPRINT(("SPI NOR flash W/R Fail\n"));
  			while (1)
  				;  //breakpoint - error detected - otherwise QSPI works properly
  		}
  	}

}

#endif



/**
 * @brief Enables auto polling mode sequence
 *
 */
static uint8_t QSPI_AutoPollingMemReady(mx25l512_dev_t *dev_hdl)
{

	ioal_qspi_CommandTypeDef sCommand;
	ioal_qspi_AutoPollingTypeDef sConfig;
	ioal_qspi_hdle *hqspi;

	hqspi = dev_hdl->io_intf_hdle;
	/* Configure automatic polling mode to wait for memory ready ------ */
	sCommand.InstructionMode = QSPI_INSTRUCTION_1_LINE;
	sCommand.Instruction = READ_STATUS_REG_CMD;
	sCommand.AddressMode = QSPI_ADDRESS_NONE;
	sCommand.AlternateByteMode = QSPI_ALTERNATE_BYTES_NONE;
	sCommand.DataMode = QSPI_DATA_1_LINE;
	sCommand.DummyCycles = 0;
	sCommand.DdrMode = QSPI_DDR_MODE_DISABLE;
	sCommand.DdrHoldHalfCycle = QSPI_DDR_HHC_ANALOG_DELAY;
	sCommand.SIOOMode = QSPI_SIOO_INST_EVERY_CMD;

	sConfig.Match = 0x00;
	sConfig.Mask = 0x01;
	sConfig.MatchMode = QSPI_MATCH_MODE_AND;
	sConfig.StatusBytesSize = 1;
	sConfig.Interval = 0x10;
	sConfig.AutomaticStop = QSPI_AUTOMATIC_STOP_ENABLE;

	if (io_hal_qspi_auto_polling(hqspi, &sCommand, &sConfig,
	HAL_QSPI_TIMEOUT_DEFAULT_VALUE) != HAL_SCS) {
		return HAL_AH_DRIVER_ERR;
	}

	return HAL_SCS;
}


/**
 * @brief Enable write mode to program memory
 *
 */
static uint8_t QSPI_WriteEnable(mx25l512_dev_t *dev_hdl) {
	ioal_qspi_CommandTypeDef sCommand;
	ioal_qspi_AutoPollingTypeDef sConfig;
	ioal_qspi_hdle *hqspi;

	hqspi = dev_hdl->io_intf_hdle;
	/* Enable write operations ------------------------------------------ */
	sCommand.InstructionMode = QSPI_INSTRUCTION_1_LINE;
	sCommand.Instruction = WRITE_ENABLE_CMD;
	sCommand.AddressMode = QSPI_ADDRESS_NONE;
	sCommand.AlternateByteMode = QSPI_ALTERNATE_BYTES_NONE;
	sCommand.DataMode = QSPI_DATA_NONE;
	sCommand.DummyCycles = 0;
	sCommand.DdrMode = QSPI_DDR_MODE_DISABLE;
	sCommand.DdrHoldHalfCycle = QSPI_DDR_HHC_ANALOG_DELAY;
	sCommand.SIOOMode = QSPI_SIOO_INST_EVERY_CMD;

	if (io_hal_qspi_command(hqspi, &sCommand, HAL_QSPI_TIMEOUT_DEFAULT_VALUE)
			!= HAL_SCS) {
		return HAL_IO_VDP_ERR;
	}

	/* Configure automatic polling mode to wait for write enabling ---- */
	sConfig.Match = 0x02;
	sConfig.Mask = 0x02;
	sConfig.MatchMode = QSPI_MATCH_MODE_AND;
	sConfig.StatusBytesSize = 1;
	sConfig.Interval = 0x10;
	sConfig.AutomaticStop = QSPI_AUTOMATIC_STOP_ENABLE;

	sCommand.Instruction = READ_STATUS_REG_CMD;
	sCommand.DataMode = QSPI_DATA_1_LINE;
	if (io_hal_qspi_auto_polling(hqspi, &sCommand, &sConfig,
	HAL_QSPI_TIMEOUT_DEFAULT_VALUE) != HAL_SCS) {
		return HAL_IO_VDP_ERR;
	}

	return HAL_SCS;
}

/**
 * @brief Perform chip reset sequence
 *
 */
static uint8_t QSPI_ResetChip(mx25l512_dev_t *dev_hdl)
{
	ioal_qspi_CommandTypeDef sCommand;
	uint32_t temp = 0;
	ioal_qspi_hdle *hqspi;

	hqspi = dev_hdl->io_intf_hdle;
	/* Erasing Sequence -------------------------------------------------- */
	sCommand.InstructionMode = QSPI_INSTRUCTION_1_LINE;
	sCommand.AddressSize = QSPI_ADDRESS_24_BITS;
	sCommand.AlternateByteMode = QSPI_ALTERNATE_BYTES_NONE;
	sCommand.DdrMode = QSPI_DDR_MODE_DISABLE;
	sCommand.DdrHoldHalfCycle = QSPI_DDR_HHC_ANALOG_DELAY;
	sCommand.SIOOMode = QSPI_SIOO_INST_EVERY_CMD;
	sCommand.Instruction = RESET_ENABLE_CMD;
	sCommand.AddressMode = QSPI_ADDRESS_NONE;
	sCommand.Address = 0;
	sCommand.DataMode = QSPI_DATA_NONE;
	sCommand.DummyCycles = 0;

	if (io_hal_qspi_command(hqspi, &sCommand, HAL_QSPI_TIMEOUT_DEFAULT_VALUE)
			!= HAL_SCS) {
		return HAL_IO_VDP_ERR;
	}
	for (temp = 0; temp < 0x2f; temp++) {
		__NOP();
	}

	sCommand.InstructionMode = QSPI_INSTRUCTION_1_LINE;
	sCommand.AddressSize = QSPI_ADDRESS_24_BITS;
	sCommand.AlternateByteMode = QSPI_ALTERNATE_BYTES_NONE;
	sCommand.DdrMode = QSPI_DDR_MODE_DISABLE;
	sCommand.DdrHoldHalfCycle = QSPI_DDR_HHC_ANALOG_DELAY;
	sCommand.SIOOMode = QSPI_SIOO_INST_EVERY_CMD;
	sCommand.Instruction = RESET_EXECUTE_CMD;
	sCommand.AddressMode = QSPI_ADDRESS_NONE;
	sCommand.Address = 0;
	sCommand.DataMode = QSPI_DATA_NONE;
	sCommand.DummyCycles = 0;

	if (io_hal_qspi_command(hqspi, &sCommand, HAL_QSPI_TIMEOUT_DEFAULT_VALUE)
			!= HAL_SCS) {
		return HAL_IO_VDP_ERR;
	}

	/* Erasing Sequence -------------------------------------------------- */
	sCommand.InstructionMode = QSPI_INSTRUCTION_2_LINES;
	sCommand.AddressSize = QSPI_ADDRESS_24_BITS;
	sCommand.AlternateByteMode = QSPI_ALTERNATE_BYTES_NONE;
	sCommand.DdrMode = QSPI_DDR_MODE_DISABLE;
	sCommand.DdrHoldHalfCycle = QSPI_DDR_HHC_ANALOG_DELAY;
	sCommand.SIOOMode = QSPI_SIOO_INST_EVERY_CMD;
	sCommand.Instruction = RESET_ENABLE_CMD;
	sCommand.AddressMode = QSPI_ADDRESS_NONE;
	sCommand.Address = 0;
	sCommand.DataMode = QSPI_DATA_NONE;
	sCommand.DummyCycles = 0;

	if (io_hal_qspi_command(hqspi, &sCommand, HAL_QSPI_TIMEOUT_DEFAULT_VALUE)
			!= HAL_SCS) {
		return HAL_IO_VDP_ERR;
	}
	for (temp = 0; temp < 0x2f; temp++) {
		__NOP();
	}

	sCommand.InstructionMode = QSPI_INSTRUCTION_2_LINES;
	sCommand.AddressSize = QSPI_ADDRESS_24_BITS;
	sCommand.AlternateByteMode = QSPI_ALTERNATE_BYTES_NONE;
	sCommand.DdrMode = QSPI_DDR_MODE_DISABLE;
	sCommand.DdrHoldHalfCycle = QSPI_DDR_HHC_ANALOG_DELAY;
	sCommand.SIOOMode = QSPI_SIOO_INST_EVERY_CMD;
	sCommand.Instruction = RESET_EXECUTE_CMD;
	sCommand.AddressMode = QSPI_ADDRESS_NONE;
	sCommand.Address = 0;
	sCommand.DataMode = QSPI_DATA_NONE;
	sCommand.DummyCycles = 0;

	if (io_hal_qspi_command(hqspi, &sCommand, HAL_QSPI_TIMEOUT_DEFAULT_VALUE)
			!= HAL_SCS) {
		return HAL_IO_VDP_ERR;
	}

	/* Erasing Sequence -------------------------------------------------- */
	sCommand.InstructionMode = QSPI_INSTRUCTION_4_LINES;
	sCommand.AddressSize = QSPI_ADDRESS_24_BITS;
	sCommand.AlternateByteMode = QSPI_ALTERNATE_BYTES_NONE;
	sCommand.DdrMode = QSPI_DDR_MODE_DISABLE;
	sCommand.DdrHoldHalfCycle = QSPI_DDR_HHC_ANALOG_DELAY;
	sCommand.SIOOMode = QSPI_SIOO_INST_EVERY_CMD;
	sCommand.Instruction = RESET_ENABLE_CMD;
	sCommand.AddressMode = QSPI_ADDRESS_NONE;
	sCommand.Address = 0;
	sCommand.DataMode = QSPI_DATA_NONE;
	sCommand.DummyCycles = 0;

	if (io_hal_qspi_command(hqspi, &sCommand, HAL_QSPI_TIMEOUT_DEFAULT_VALUE)
			!= HAL_SCS) {
		return HAL_IO_VDP_ERR;
	}
	for (temp = 0; temp < 0x2f; temp++) {
		__NOP();
	}

	sCommand.InstructionMode = QSPI_INSTRUCTION_4_LINES;
	sCommand.AddressSize = QSPI_ADDRESS_24_BITS;
	sCommand.AlternateByteMode = QSPI_ALTERNATE_BYTES_NONE;
	sCommand.DdrMode = QSPI_DDR_MODE_DISABLE;
	sCommand.DdrHoldHalfCycle = QSPI_DDR_HHC_ANALOG_DELAY;
	sCommand.SIOOMode = QSPI_SIOO_INST_EVERY_CMD;
	sCommand.Instruction = RESET_EXECUTE_CMD;
	sCommand.AddressMode = QSPI_ADDRESS_NONE;
	sCommand.Address = 0;
	sCommand.DataMode = QSPI_DATA_NONE;
	sCommand.DummyCycles = 0;

	if (io_hal_qspi_command(hqspi, &sCommand, HAL_QSPI_TIMEOUT_DEFAULT_VALUE)
			!= HAL_SCS) {
		return HAL_IO_VDP_ERR;
	}

	return HAL_SCS;
}



/**
 * @brief Configure chip
 *
 * Enable quad mode and set dummy cycles count
 *
 * */
static uint8_t QSPI_Configuration(mx25l512_dev_t *dev_hdl)
{

	ioal_qspi_CommandTypeDef sCommand;
	uint8_t test_buffer[4] = { 0 };
	ioal_qspi_hdle *hqspi;

	hqspi = dev_hdl->io_intf_hdle;
	/*read status register*/
	sCommand.InstructionMode = QSPI_INSTRUCTION_1_LINE;
	sCommand.Instruction = READ_STATUS_REG_CMD;
	sCommand.AddressMode = QSPI_ADDRESS_NONE;
	sCommand.AlternateByteMode = QSPI_ALTERNATE_BYTES_NONE;
	sCommand.DataMode = QSPI_DATA_1_LINE;
	sCommand.DummyCycles = 0;
	sCommand.DdrMode = QSPI_DDR_MODE_DISABLE;
	sCommand.DdrHoldHalfCycle = QSPI_DDR_HHC_ANALOG_DELAY;
	sCommand.SIOOMode = QSPI_SIOO_INST_EVERY_CMD;
	sCommand.NbData = 1;

	if (io_hal_qspi_command(hqspi, &sCommand, HAL_QSPI_TIMEOUT_DEFAULT_VALUE)
			!= HAL_SCS) {
		return HAL_IO_VDP_ERR;
	}
	if (io_hal_qspi_receive(hqspi, test_buffer,
	HAL_QSPI_TIMEOUT_DEFAULT_VALUE) != HAL_SCS) {
		return HAL_IO_VDP_ERR;
	}
	/*read configuration register*/
	sCommand.InstructionMode = QSPI_INSTRUCTION_1_LINE;
	sCommand.Instruction = READ_CONFIGURATION_REG_CMD;
	sCommand.AddressMode = QSPI_ADDRESS_NONE;
	sCommand.AlternateByteMode = QSPI_ALTERNATE_BYTES_NONE;
	sCommand.DataMode = QSPI_DATA_1_LINE;
	sCommand.DummyCycles = 0;
	sCommand.DdrMode = QSPI_DDR_MODE_DISABLE;
	sCommand.DdrHoldHalfCycle = QSPI_DDR_HHC_ANALOG_DELAY;
	sCommand.SIOOMode = QSPI_SIOO_INST_EVERY_CMD;
	sCommand.NbData = 1;

	if (io_hal_qspi_command(hqspi, &sCommand, HAL_QSPI_TIMEOUT_DEFAULT_VALUE)
			!= HAL_SCS) {
		return HAL_IO_VDP_ERR;
	}
	if (io_hal_qspi_receive(hqspi, &(test_buffer[1]),
	HAL_QSPI_TIMEOUT_DEFAULT_VALUE) != HAL_SCS) {
		return HAL_IO_VDP_ERR;
	}
	/*modify buffer to enable quad mode*/
	test_buffer[0] |= 0x40;

	/*set dummy cycles*/
	test_buffer[1] |= 0xC0;

	sCommand.InstructionMode = QSPI_INSTRUCTION_1_LINE;
	sCommand.AddressSize = QSPI_ADDRESS_24_BITS;
	sCommand.AlternateByteMode = QSPI_ALTERNATE_BYTES_NONE;
	sCommand.DdrMode = QSPI_DDR_MODE_DISABLE;
	sCommand.DdrHoldHalfCycle = QSPI_DDR_HHC_ANALOG_DELAY;
	sCommand.SIOOMode = QSPI_SIOO_INST_EVERY_CMD;
	sCommand.Instruction = WRITE_STATUS_REG_CMD;
	sCommand.AddressMode = QSPI_ADDRESS_NONE;
	sCommand.DataMode = QSPI_DATA_1_LINE;
	sCommand.DummyCycles = 0;
	sCommand.NbData = 2;

	if (io_hal_qspi_command(hqspi, &sCommand, HAL_QSPI_TIMEOUT_DEFAULT_VALUE)
			!= HAL_SCS) {
		return HAL_IO_VDP_ERR;
	}

	if (io_hal_qspi_transmit(hqspi, test_buffer,
	HAL_QSPI_TIMEOUT_DEFAULT_VALUE) != HAL_SCS) {
		return HAL_IO_VDP_ERR;
	}
	return HAL_SCS;
}

/**
 * @brief Erase memory by sector mode
 *
 * Erase by Sector of size 4kb
 * This function will perform erase operation by sector mode.
 * One sector size is 4KB in mx25l512, it will erase memory from start address till end address
 * by sector mode operation
 */
uint8_t mx25l512_EraseSector(mx25l512_dev_t *dev_hdl, uint32_t EraseStartAddress, uint32_t EraseEndAddress, uint32_t erase_mode)
{

	ioal_qspi_CommandTypeDef sCommand;
	ioal_qspi_hdle *hqspi;
	uint32_t erase_blocksize;
	if(verify_address_range( EraseStartAddress ) != 0)
	{
		return HAL_IO_VDP_ERR;
	}
	if(verify_address_range( EraseEndAddress ) != 0)
	{
		return HAL_IO_VDP_ERR;
	}

	if(erase_mode == 0)
	{
		/* erase sector by sector by size 4kb */
		erase_blocksize = MEMORY_SECTOR_SIZE;
	}
	else
	{
		/* erase block by block by size 64kb */
		erase_blocksize = MEMORY_BLOCK_SIZE;
	}

	hqspi = dev_hdl->io_intf_hdle;
	EraseStartAddress = EraseStartAddress
			- EraseStartAddress % erase_blocksize;

	/* Erasing Sequence -------------------------------------------------- */
	sCommand.InstructionMode = QSPI_INSTRUCTION_1_LINE;
	sCommand.AddressSize = QSPI_ADDRESS_24_BITS;
	sCommand.AlternateByteMode = QSPI_ALTERNATE_BYTES_NONE;
	sCommand.DdrMode = QSPI_DDR_MODE_DISABLE;
	sCommand.DdrHoldHalfCycle = QSPI_DDR_HHC_ANALOG_DELAY;
	sCommand.SIOOMode = QSPI_SIOO_INST_EVERY_CMD;
	sCommand.Instruction = SECTOR_ERASE_CMD;
	sCommand.AddressMode = QSPI_ADDRESS_1_LINE;

	sCommand.DataMode = QSPI_DATA_NONE;
	sCommand.DummyCycles = 0;

	while (EraseEndAddress >= EraseStartAddress)
	{
		sCommand.Address = (EraseStartAddress & 0x0FFFFFFF);

		if (QSPI_WriteEnable(dev_hdl) != HAL_SCS) {
			return HAL_IO_VDP_ERR;
		}

		if (io_hal_qspi_command(hqspi, &sCommand, HAL_QSPI_TIMEOUT_DEFAULT_VALUE)
				!= HAL_SCS) {
			return HAL_IO_VDP_ERR;
		}
		EraseStartAddress += erase_blocksize;

		if (QSPI_AutoPollingMemReady(dev_hdl) != HAL_SCS) {
			return HAL_IO_VDP_ERR;
		}
	}

	return HAL_SCS;
}





/**
 * @brief Write data to memory
 *
 * This function will write data page by page
 * Page size is 256, so if it has to write 500 bytes , it will write first 256 bytes in one cycle and remainig bytes in another cycles.
 *
 */
uint8_t mx25l512_write(mx25l512_dev_t *dev_hdl, uint8_t* buffer, uint32_t address,uint32_t buffer_size)
{

	ioal_qspi_CommandTypeDef sCommand;
	uint32_t end_addr, current_size, current_addr;
	ioal_qspi_hdle *hqspi;
	if(verify_address_range(address) != 0)
	{
		return HAL_IO_VDP_ERR;
	}

	hqspi = dev_hdl->io_intf_hdle;
	/* Calculation of the size between the write address and the end of the page */
	current_addr = 0;


	//
	while (current_addr <= address)
	{
		current_addr += MEMORY_PAGE_SIZE;
	}
	//current_addr = MEMORY_PAGE_SIZE - (address % MEMORY_PAGE_SIZE);

	/*
	 * it will find the forward page address
	 * if address is 1030, then this calculation will return 1280 since it the forward address with multiples of 256
	 *
	 * 1030 % 256 = 6
	 *
	 * so c_address = ((1030/256) * 256) + 256 => ((4) * 256) +256 => 1024 + 256 = 1280
	 * else   ((1030/256) * 256) => ((4) * 256) => 1024
	 *
	 */
#if 0
    if((address % MEMORY_PAGE_SIZE) != 0)
    {
    	current_addr = ((address / MEMORY_PAGE_SIZE) * MEMORY_PAGE_SIZE) + MEMORY_PAGE_SIZE;
    }
    else
    {
    	current_addr = (address / MEMORY_PAGE_SIZE) * MEMORY_PAGE_SIZE;
    }

#endif

	current_size = current_addr - address;

	/* Check if the size of the data is less than the remaining place in the page */
	if (current_size > buffer_size)
	{
		current_size = buffer_size;
	}

	/* Initialize the adress variables */
	current_addr = address;
	end_addr = address + buffer_size;

	sCommand.InstructionMode = QSPI_INSTRUCTION_1_LINE;
	sCommand.AddressSize = QSPI_ADDRESS_24_BITS;
	sCommand.AlternateByteMode = QSPI_ALTERNATE_BYTES_NONE;
	sCommand.DdrMode = QSPI_DDR_MODE_DISABLE;
	sCommand.DdrHoldHalfCycle = QSPI_DDR_HHC_ANALOG_DELAY;
	sCommand.SIOOMode = QSPI_SIOO_INST_EVERY_CMD;
	sCommand.Instruction = QUAD_IN_FAST_PROG_CMD;
	sCommand.AddressMode = QSPI_ADDRESS_4_LINES;
	sCommand.DataMode = QSPI_DATA_4_LINES;
	sCommand.NbData = buffer_size;
	sCommand.Address = address;
	sCommand.DummyCycles = 0;

	/* Perform the write page by page */
	do {
		sCommand.Address = current_addr;
		sCommand.NbData = current_size;

		if (current_size == 0) {
			return HAL_SCS;
		}

		/* Enable write operations */
		if (QSPI_WriteEnable(dev_hdl) != HAL_SCS) {
			return HAL_IO_VDP_ERR;
		}

		/* Configure the command */
		if (io_hal_qspi_command(hqspi, &sCommand, HAL_QSPI_TIMEOUT_DEFAULT_VALUE)
				!= HAL_SCS) {

			return HAL_IO_VDP_ERR;
		}

		/* Transmission of the data */
		if (io_hal_qspi_transmit(hqspi, buffer, HAL_QSPI_TIMEOUT_DEFAULT_VALUE) != HAL_SCS) {

			return HAL_IO_VDP_ERR;
		}

		/* Configure automatic polling mode to wait for end of program */
		if (QSPI_AutoPollingMemReady(dev_hdl) != HAL_SCS) {
			return HAL_IO_VDP_ERR;
		}

		/* Update the address and size variables for next page programming */
		current_addr += current_size;
		buffer += current_size;
		current_size =
				((current_addr + MEMORY_PAGE_SIZE) > end_addr) ?
						(end_addr - current_addr) : MEMORY_PAGE_SIZE;
	} while (current_addr <= end_addr);

	return HAL_SCS;

}



/**
 * @brief This API read the data from direct memory since memory mapped has already been enabled
 */
uint8_t mx25l512_read(mx25l512_dev_t *dev_hdl, uint8_t* buffer, uint32_t address,uint32_t buffer_size)
{
	ioal_qspi_CommandTypeDef sCommand;
	ioal_qspi_hdle *hqspi;
	uint32_t current_addr, end_addr, current_size;

	if(verify_address_range(address) != 0)
	{
		return HAL_IO_VDP_ERR;
	}

	hqspi = dev_hdl->io_intf_hdle;

	sCommand.InstructionMode = QSPI_INSTRUCTION_1_LINE;
	sCommand.AddressSize = QSPI_ADDRESS_24_BITS;
	sCommand.AlternateByteMode = QSPI_ALTERNATE_BYTES_NONE;
	sCommand.DdrMode = QSPI_DDR_MODE_DISABLE;
	sCommand.DdrHoldHalfCycle = QSPI_DDR_HHC_ANALOG_DELAY;
	sCommand.SIOOMode = QSPI_SIOO_INST_EVERY_CMD;
	sCommand.AddressMode = QSPI_ADDRESS_1_LINE;
	sCommand.DataMode = QSPI_DATA_4_LINES;
	sCommand.NbData = buffer_size;
	sCommand.Address = address;
	sCommand.Instruction = QUAD_OUT_FAST_READ_CMD;
	sCommand.DummyCycles = DUMMY_CLOCK_CYCLES_READ_QUAD;


	/*
	 *  size = 500
	 *  c_addr = 1000
	 *  e_addr = 1500
	 *
	 *  c_size = 256
	 *
	 *
	 */
	current_addr = address;
	end_addr = address + buffer_size;

	if(buffer_size <= MEMORY_PAGE_SIZE)
	{
		current_size =  buffer_size;
	}
	else
	{
		current_size = MEMORY_PAGE_SIZE;
	}

	do {


		sCommand.NbData = current_size;
		sCommand.Address = current_addr;

		if (io_hal_qspi_command(hqspi, &sCommand, HAL_QSPI_TIMEOUT_DEFAULT_VALUE)!= HAL_SCS)
		{
			return HAL_IO_VDP_ERR;
		}

		if (io_hal_qspi_receive(hqspi, buffer, HAL_QSPI_TIMEOUT_DEFAULT_VALUE) != HAL_SCS)
		{
			return HAL_IO_VDP_ERR;
		}


		/*
		 *  size = 500
		 *  c_addr = 1000
		 *  e_addr = 1500
		 *
		 *  c_size = 256
		 *
		 *
		 *  iter0:
		 *  c_addr = 1000
		 *  buffer
		 *
		 *  iter1:
		 *
		 *   c_addr =  1000 + MEMORY_PAGE_SIZE = 1256
		 *   buffer = buffer + MEMORY_PAGE_SIZE	 *
		 *   c_size = 1256 + 256 = 1512  > 1500 yes so c_size = 1500 - 1256 = 244
		 *
		 *
		 */

		current_addr += current_size;
		buffer += current_size;

		current_size =
				((current_addr + MEMORY_PAGE_SIZE) > end_addr) ?
						(end_addr - current_addr) : MEMORY_PAGE_SIZE;

		os_delay(10);
	} while(current_addr < end_addr);

	return HAL_SCS;
}



/**
 * @brief Enable memory mapped mode
 *
 */
uint8_t mx25l512_EnableMemoryMappedMode(mx25l512_dev_t *dev_hdl) {

	ioal_qspi_CommandTypeDef sCommand;
	ioal_qspi_MemoryMappedTypeDef sMemMappedCfg;
	ioal_qspi_hdle *hqspi;

	hqspi = dev_hdl->io_intf_hdle;
	/* Enable Memory-Mapped mode-------------------------------------------------- */

	sCommand.InstructionMode = QSPI_INSTRUCTION_1_LINE;
	sCommand.AddressSize = QSPI_ADDRESS_24_BITS;
	sCommand.AlternateByteMode = QSPI_ALTERNATE_BYTES_NONE;
	sCommand.DdrMode = QSPI_DDR_MODE_DISABLE;
	sCommand.DdrHoldHalfCycle = QSPI_DDR_HHC_ANALOG_DELAY;
	sCommand.SIOOMode = QSPI_SIOO_INST_EVERY_CMD;
	sCommand.AddressMode = QSPI_ADDRESS_1_LINE;
	sCommand.DataMode = QSPI_DATA_4_LINES;
	sCommand.NbData = 0;
	sCommand.Address = 0;
	sCommand.Instruction = QUAD_OUT_FAST_READ_CMD;
	sCommand.DummyCycles = DUMMY_CLOCK_CYCLES_READ_QUAD;

	sMemMappedCfg.TimeOutActivation = QSPI_TIMEOUT_COUNTER_DISABLE;

	if (io_hal_qspi_memory_mapped(hqspi, &sCommand, &sMemMappedCfg) != HAL_SCS) {
		return HAL_IO_VDP_ERR;
	}
	return HAL_SCS;
}





/**
 * @brief Initialize Mx25l512 chip
 *
 */
uint8_t mx25l512_init(mx25l512_dev_t *dev_hdl)
{

	if (QSPI_ResetChip(dev_hdl) != HAL_SCS) {
		return HAL_IO_VDP_ERR;
	}

	HAL_Delay(1);

	if (QSPI_AutoPollingMemReady(dev_hdl) != HAL_SCS) {
		return HAL_IO_VDP_ERR;
	}

	if (QSPI_WriteEnable(dev_hdl) != HAL_SCS) {

		return HAL_IO_VDP_ERR;
	}

	if (QSPI_Configuration(dev_hdl) != HAL_SCS) {
		return HAL_IO_VDP_ERR;
	}

	/* Enable Memory mapped mode for CPU address 0x90000000
	 *
	 */
//  	if (mx25l512_qspi_EnableMemoryMappedMode(dev_hdl) != HAL_SCS) {
//		return HAL_IO_VDP_ERR;
//  	}


#ifdef SPI_NOR_TEST_ENB
		qspi_nor_test(dev_hdl);
	//	mx25l512_qspi_qread(dev_hdl);
#endif

	return HAL_SCS;
}



/**
 * @brief Erase full chip
 *
 */
uint8_t mx25l512_qspi_Erase_Chip(mx25l512_dev_t *dev_hdl)
{
	ioal_qspi_CommandTypeDef sCommand;
	ioal_qspi_hdle *hqspi;

	hqspi = dev_hdl->io_intf_hdle;

	if (QSPI_WriteEnable(dev_hdl) != HAL_SCS) {
		return HAL_IO_VDP_ERR;
	}


	/* Erasing Sequence --------------------------------- */
	sCommand.Instruction = CHIP_ERASE_CMD;
	sCommand.InstructionMode = QSPI_INSTRUCTION_1_LINE;
	sCommand.AddressSize = QSPI_ADDRESS_24_BITS;
	sCommand.AlternateByteMode = QSPI_ALTERNATE_BYTES_NONE;
	sCommand.DdrMode = QSPI_DDR_MODE_DISABLE;
	sCommand.DdrHoldHalfCycle = QSPI_DDR_HHC_ANALOG_DELAY;
	sCommand.SIOOMode = QSPI_SIOO_INST_EVERY_CMD;
	sCommand.AddressMode = QSPI_ADDRESS_NONE;
	sCommand.Address = 0;
	sCommand.DataMode = QSPI_DATA_NONE;
	sCommand.DummyCycles = 0;


	if (io_hal_qspi_command(hqspi, &sCommand, HAL_QSPI_TIMEOUT_DEFAULT_VALUE)
			!= HAL_SCS) {
		return HAL_IO_VDP_ERR;
	}

	if (QSPI_AutoPollingMemReady(dev_hdl) != HAL_SCS) {
				return HAL_IO_VDP_ERR;
			}

	return HAL_SCS;
}






