/*
 * @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 "mt28ew128.h"
#include "exo_io_al_fmc_nor_flash_common.h"

#ifndef LINUX_TEMP_PORT

/*
 * Size of MT28EW128 is 16MB start from the address 0x60000000
 */
#define MT28EW128_MEMORY_START_ADDRESS      0x60000000
#define MT28EW128_MEMORY_END_ADDRESS        (MT28EW128_MEMORY_START_ADDRESS + (16*(1024*1024)))

uint8_t nor_buff[MT28EW128_PAGE_SIZE*2];

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

/**
 * @brief  This API initializes the control block memory
 * and do the basic configurations for FMC-NOR-FLASH
 */
uint8_t mt28ew128_init(mt28ew128_mem_t* flsh_hdlr)
{
	uint8_t sts;
	ioal_nor_id_t nor_esign;
	sts = io_hal_fmc_nor_flash_read_id(flsh_hdlr->io_intf_hdle,&nor_esign);
	return sts;
}

/**
 * @brief  This API write data in FMC-NOR-FLASH
 */
uint8_t mt28ew128_write(mt28ew128_mem_t* flsh_hdlr, uint32_t w_address, uint8_t *data, int32_t data_len)
{
	uint8_t sts =MT28EW128_SCS;
	int count;
	uint32_t end_addr, current_size, current_addr;

	if(verify_address_range(w_address) != 0)
	{
		return MT28EW128_ERR;
	}

	current_size = MT28EW128_PAGE_SIZE - (w_address % (MT28EW128_PAGE_SIZE));
	if (current_size > data_len)
	{
		current_size = data_len;
	}
	current_addr = w_address - NOR_MEMORY_ADRESS;
	if(current_addr>=0)
	{
		end_addr = (w_address-NOR_MEMORY_ADRESS) + data_len;
		io_hal_fmc_nor_flash_write_enable(flsh_hdlr->io_intf_hdle);
		do
		{
			if(current_size==MT28EW128_PAGE_SIZE)
			{
				for(count=0;count<MT28EW128_PAGE_SIZE*2;count++)
				{
					nor_buff[count]=data[count];
				}
			}
			else
			{
				for(count=0;count<current_size;count++)
				{
					nor_buff[count]=data[count];
				}
			}
			if(io_hal_fmc_nor_flash_get_status(flsh_hdlr->io_intf_hdle,NOR_MEMORY_ADRESS,mt28ew128_mem_tIMEOUT) !=
					IOAL_NOR_STATUS_SUCCESS)
			{
				sts = MT28EW128_ERR;
			}
			else
			{
				io_hal_fmc_nor_flash_program_buffer(flsh_hdlr->io_intf_hdle,current_addr,nor_buff,current_size);
				current_addr += current_size*2;
				data += current_size*2;
				current_size = ((current_addr + MT28EW128_PAGE_SIZE) > end_addr) ? (end_addr - current_addr) : MT28EW128_PAGE_SIZE;
			}
		} while (current_addr < end_addr);
	}
	else
	{
		sts = MT28EW128_ERR;
	}
	return sts;
}

/**
 * @brief  This API erase and write data in FMC-NOR-FLASH
 */
uint8_t mt28ew128_erase_and_write(mt28ew128_mem_t* flsh_hdlr, uint32_t w_address, uint8_t *data, int32_t data_len)
{
	uint8_t sts=MT28EW128_ERR;
	if(verify_address_range(w_address) != 0)
	{
		return MT28EW128_ERR;
	}
	sts=mt28ew128_erase(flsh_hdlr,w_address,data_len);
	if(sts==MT28EW128_SCS)
	{
		sts = mt28ew128_write(flsh_hdlr,w_address,data,data_len);
	}
	return sts;
}


/**
 * @brief  This API read data from FMC-NOR-FLASH
 */
uint8_t mt28ew128_read(mt28ew128_mem_t* flsh_hdlr, uint32_t r_address, uint8_t *data, uint32_t data_len)
{
	uint8_t sts=MT28EW128_ERR;
	if(verify_address_range(r_address) != 0)
	{
		return MT28EW128_ERR;
	}
//	if(io_hal_fmc_nor_flash_write_enable(flsh_hdlr->io_intf_hdle)==MT28EW128_SCS)
//	{
	io_hal_fmc_nor_flash_return_to_read_mode(flsh_hdlr->io_intf_hdle);
	sts=io_hal_fmc_nor_flash_read_buffer(flsh_hdlr->io_intf_hdle,r_address,data,data_len/2);
//	}
	return sts;
}

/**
 * @brief  This API erase the multiple block of the NOR memory with respect to given length
 */
uint8_t mt28ew128_erase(mt28ew128_mem_t* flsh_hdlr, uint32_t start_address , int32_t erase_len)
{
	uint8_t sts = MT28EW128_ERR;
	if(verify_address_range(start_address) != 0)
	{
		return MT28EW128_ERR;
	}
	while(erase_len>0)
	{
		sts = mt28ew128_erase_block(flsh_hdlr,start_address);
		start_address = start_address + MT28EW128_SECTOR_SIZE*2;
		erase_len = erase_len-(MT28EW128_SECTOR_SIZE*2);
	}
	return sts;
}

/**
 * @brief  This API erase the specified block of the NOR memory
 */
uint8_t mt28ew128_erase_block(mt28ew128_mem_t* flsh_hdlr, uint32_t b_address)
{
	uint8_t sts=MT28EW128_ERR;
	if(io_hal_fmc_nor_flash_get_status(flsh_hdlr->io_intf_hdle,NOR_MEMORY_ADRESS,mt28ew128_mem_tIMEOUT) !=
			IOAL_NOR_STATUS_SUCCESS)
	{
		sts = MT28EW128_ERR;
	}
	else
	{
//	if(io_hal_fmc_nor_flash_write_enable(flsh_hdlr->io_intf_hdle)==MT28EW128_SCS)
//	{
	sts=io_hal_fmc_nor_flash_erase_block(flsh_hdlr->io_intf_hdle,b_address);
//	}
	}
	return sts;
}

/**
 * @brief  This API erase the entire NOR chip.
 */
uint8_t mt28ew128_erase_chip(mt28ew128_mem_t* flsh_hdlr)
{
	uint8_t sts=MT28EW128_ERR;
	if(io_hal_fmc_nor_flash_write_enable(flsh_hdlr->io_intf_hdle)==MT28EW128_SCS)
	{
	sts=io_hal_fmc_nor_flash_erase_chip(flsh_hdlr->io_intf_hdle);
	}
	return sts;
}

#endif
