/*
 * @file mt25ql512.c
 *
 *  @brief This file contains the driver function of flash memory chip mt25ql512_
 *
 *	@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 "mt25ql512.h"
#include "exo_io_al_qspi_common.h"
#ifndef LINUX_TEMP_PORT
#include "stm32f7xx_hal.h"
#endif

/*
 * Size of MT25QL512 is 64MB start from the address 0x0
 */
#define MT25QL512_MEMORY_START_ADDRESS      0x0
#define MT25QL512_MEMORY_END_ADDRESS        (MT25QL512_MEMORY_START_ADDRESS + (64*(1024*1024)))

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

static uint8_t QSPI_WriteEnable(mt25ql512_dev_t *dev_hdl);
static uint8_t QSPI_AutoPollingMemReady(mt25ql512_dev_t *dev_hdl, uint32_t Timeout);
static uint8_t QSPI_Configuration(mt25ql512_dev_t *dev_hdl);
static uint8_t QSPI_ResetChip(mt25ql512_dev_t *dev_hdl);


/**
 * @brief Enables auto polling mode sequence
 *
 */
static uint8_t QSPI_AutoPollingMemReady(mt25ql512_dev_t *dev_hdl, uint32_t Timeout)
{

	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 = 0x0000;
    sConfig.Mask = 0x0101;
    sConfig.MatchMode = QSPI_MATCH_MODE_AND;
    sConfig.StatusBytesSize = 2;
    sConfig.Interval = 0x10;
    sConfig.AutomaticStop = QSPI_AUTOMATIC_STOP_ENABLE;

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

    return HAL_SCS;
}


/**
 * @brief Enable write mode to program memory
 *
 */
static uint8_t QSPI_WriteEnable(mt25ql512_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_QPSI_TIMEOUT_DEFAULT_VALUE)
        != HAL_SCS) {
        return HAL_AH_DRIVER_ERR;
    }

    /* Configure automatic polling mode to wait for write enabling ---- */
    sConfig.Match = 0x0202;
    sConfig.Mask = 0x0202;
    sConfig.MatchMode = QSPI_MATCH_MODE_AND;
    sConfig.StatusBytesSize = 2;
    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_QPSI_TIMEOUT_DEFAULT_VALUE) != HAL_SCS) {
        return HAL_AH_DRIVER_ERR;
    }

    return HAL_SCS;
}


/**
 * @brief Perform chip reset sequence
 *
 */
static uint8_t QSPI_ResetChip(mt25ql512_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_QPSI_TIMEOUT_DEFAULT_VALUE)
        != HAL_SCS) {
        return HAL_AH_DRIVER_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_QPSI_TIMEOUT_DEFAULT_VALUE)
        != HAL_SCS) {
        return HAL_AH_DRIVER_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_QPSI_TIMEOUT_DEFAULT_VALUE)
        != HAL_SCS) {
        return HAL_AH_DRIVER_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_QPSI_TIMEOUT_DEFAULT_VALUE)
        != HAL_SCS) {
        return HAL_AH_DRIVER_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_QPSI_TIMEOUT_DEFAULT_VALUE)
        != HAL_SCS) {
        return HAL_AH_DRIVER_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_QPSI_TIMEOUT_DEFAULT_VALUE)
        != HAL_SCS) {
        return HAL_AH_DRIVER_ERR;
    }

    return HAL_SCS;
}


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

	ioal_qspi_CommandTypeDef sCommand;
	ioal_qspi_hdle *hqspi;
    uint16_t reg;

	hqspi = dev_hdl->io_intf_hdle;
    /*enter 4 byte address*/
    sCommand.InstructionMode = QSPI_INSTRUCTION_1_LINE;
    sCommand.Instruction = ENTER_4_BYTE_ADD_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;
    sCommand.NbData = 0;

    if (io_hal_qspi_command(hqspi, &sCommand, HAL_QPSI_TIMEOUT_DEFAULT_VALUE)
        != HAL_SCS) {
        return HAL_AH_DRIVER_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 = 2;

    if (io_hal_qspi_command(hqspi, &sCommand, HAL_QPSI_TIMEOUT_DEFAULT_VALUE)
        != HAL_SCS) {
        return HAL_AH_DRIVER_ERR;
    }


    if (io_hal_qspi_receive(hqspi, (uint8_t*)(&reg),
                         HAL_QPSI_TIMEOUT_DEFAULT_VALUE) != HAL_SCS) {
        return HAL_AH_DRIVER_ERR;
    }



    if (QSPI_WriteEnable(dev_hdl) != HAL_SCS) {

        return HAL_AH_DRIVER_ERR;
    }


    /*set dummy cycles*/
    MODIFY_REG(reg, 0xF0F0, ((DUMMY_CLOCK_CYCLES_READ_QUAD << 4) |
                             (DUMMY_CLOCK_CYCLES_READ_QUAD << 12)));


    sCommand.Instruction = WRITE_VOL_CFG_REG_CMD;


    if (io_hal_qspi_command(hqspi, &sCommand, HAL_QPSI_TIMEOUT_DEFAULT_VALUE)
        != HAL_SCS) {
        return HAL_AH_DRIVER_ERR;
    }

    if (io_hal_qspi_transmit(hqspi, (uint8_t*)(&reg),
                          HAL_QPSI_TIMEOUT_DEFAULT_VALUE) != HAL_SCS) {
        return HAL_AH_DRIVER_ERR;
    }
    return HAL_SCS;
}



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

	ioal_qspi_CommandTypeDef sCommand;
	ioal_qspi_hdle *hqspi;
	if(verify_address_range(EraseStartAddress) != 0)
	{
		return HAL_AH_DRIVER_ERR;
	}
	if(verify_address_range(EraseStartAddress) != 0)
	{
   		return HAL_AH_DRIVER_ERR;
	}

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

    /* Erasing Sequence -------------------------------------------------- */
    sCommand.InstructionMode = QSPI_INSTRUCTION_1_LINE;
    sCommand.AddressSize = QSPI_ADDRESS_32_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 = SUB_SECTOR_ERASE_CMD2;
    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_AH_DRIVER_ERR;
        }

        if (io_hal_qspi_command(hqspi, &sCommand, HAL_QPSI_TIMEOUT_DEFAULT_VALUE)
            != HAL_SCS) {
            return HAL_AH_DRIVER_ERR;
        }
        EraseStartAddress += MEMORY_SUBSECTOR2_SIZE;

        if (QSPI_AutoPollingMemReady(dev_hdl,HAL_QPSI_TIMEOUT_DEFAULT_VALUE) != HAL_SCS) {
            return HAL_AH_DRIVER_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 mt25ql512_write(mt25ql512_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_AH_DRIVER_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_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_32_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_1_LINE;
    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_AH_DRIVER_ERR;
        }

        /* Configure the command */
        if (io_hal_qspi_command(hqspi, &sCommand, HAL_QPSI_TIMEOUT_DEFAULT_VALUE)
            != HAL_SCS) {
            return HAL_AH_DRIVER_ERR;
        }

        /* Transmission of the data */
        if (io_hal_qspi_transmit(hqspi, buffer, HAL_QPSI_TIMEOUT_DEFAULT_VALUE) != HAL_SCS) {
            return HAL_AH_DRIVER_ERR;
        }

        /* Configure automatic polling mode to wait for end of program */
        if (QSPI_AutoPollingMemReady(dev_hdl, HAL_QPSI_TIMEOUT_DEFAULT_VALUE) != HAL_SCS) {
            return HAL_AH_DRIVER_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 mt25ql512_read(mt25ql512_dev_t *dev_hdl, uint8_t* buffer, uint32_t address,uint32_t buffer_size)
{
	ioal_qspi_CommandTypeDef sCommand;
	ioal_qspi_hdle *hqspi;
	if(verify_address_range(address) != 0)
	{
		return HAL_AH_DRIVER_ERR;
	}

	hqspi = dev_hdl->io_intf_hdle;

	   sCommand.InstructionMode = QSPI_INSTRUCTION_1_LINE;
	   sCommand.AddressSize = QSPI_ADDRESS_32_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;

	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;
	}

	return HAL_SCS;
}


uint8_t mt25ql512_EnableMemoryMappedMode(mt25ql512_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_32_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_AH_DRIVER_ERR;
    }
    return HAL_SCS;
}


/**
 * @brief Initialize MT25QL512 chip
 *
 */uint8_t mt25ql512_init(mt25ql512_dev_t *dev_hdl)
{

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

    HAL_Delay(1);

    if (QSPI_AutoPollingMemReady(dev_hdl, HAL_QPSI_TIMEOUT_DEFAULT_VALUE) != HAL_SCS) {
        return HAL_AH_DRIVER_ERR;
    }

    if (QSPI_WriteEnable(dev_hdl) != HAL_SCS) {

        return HAL_AH_DRIVER_ERR;
    }

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

    if (QSPI_AutoPollingMemReady(dev_hdl, HAL_QPSI_TIMEOUT_DEFAULT_VALUE) != HAL_SCS) {
        return HAL_AH_DRIVER_ERR;
    }


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


    return HAL_SCS;
}



 /**
  * @brief Erase full chip
  *
  */
uint8_t mt25ql512_Erase_Chip(mt25ql512_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_AH_DRIVER_ERR;
    }


    /* Erasing Sequence --------------------------------- */
    sCommand.Instruction = CHIP_ERASE_CMD;
    sCommand.InstructionMode = QSPI_INSTRUCTION_1_LINE;
    sCommand.AddressSize = QSPI_ADDRESS_32_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_QPSI_TIMEOUT_DEFAULT_VALUE)
        != HAL_SCS) {
        return HAL_AH_DRIVER_ERR;
    }

    if (QSPI_AutoPollingMemReady(dev_hdl,QUADSPI_MAX_ERASE_TIMEOUT) != HAL_SCS) {
        return HAL_AH_DRIVER_ERR;
    }


    return HAL_SCS;
}
