/**
  *********************************************************************************
  *
  * @file    ald_nand.c
  * @brief   NAND module driver.
  *
  * @version V1.0
  * @date    18 Mar 2019
  * @author  AE Team
  * @note
  *          Change Logs:
  *          Date            Author          Notes
  *          18 Mar 2019     AE Team         The first version
  *
  * Copyright (C) Shanghai Eastsoft Microelectronics Co. Ltd. All rights reserved.
  *
  * SPDX-License-Identifier: Apache-2.0
  *
  * 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
  *
  * 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.
  **********************************************************************************
  * @verbatim
  ==============================================================================
                     ##### How to use this driver #####
  ==============================================================================   
    [..]
	This driver is used as follows:
      (#) NAND flash memory configuration sequence using the function ald_nand_init() 
          with control and timing parameters for both common and attribute spaces.  
      (#) Read NAND flash memory maker and device IDs using the function
          ald_nand_read_id(). The read information is stored in the nand_id_typedef 
          structure declared by the function caller. 
      (#) Access NAND flash memory by read/write operations using the functions
          ald_nand_read_page_8b()/nand_read_SpareArea_8b(), 
          ald_nand_write_page_8b()/ald_nand_write_sparearea_8b(),
          ald_nand_read_page_16b()/ald_nand_read_sparearea_16b(), 
          ald_nand_write_page_16b()/ald_nand_write_sparearea_16b()
          to read/write page(s)/spare area(s).
      (#) Perform NAND flash Reset chip operation using the function ald_nand_reset().
      (#) Perform NAND flash erase block operation using the function ald_nand_erase_block().
      (#) Read the NAND flash status operation using the function ald_nand_read_status().
      (#) You can also control the NAND device by calling the control APIs ald_nand_ecc_enable()/
          ald_nand_ecc_disable() to respectively enable/disable the ECC code correction
          feature or the function ald_nand_get_ecc() to get the ECC correction code. 

    @endverbatim
  ******************************************************************************
  */

#include "ald_conf.h"


/** @addtogroup ES32FXXX_ALD
  * @{
  */

/** @defgroup NAND NAND
  * @brief NAND driver modules
  * @{
  */
#ifdef ALD_NAND

/** @defgroup Nand_Public_Functions NAND Public Functions
  * @brief NAND Public functions
  * @{
  */
/** @defgroup Nand_Public_Functions_Group1 Initialization functions
  * @brief NAND Initialization functions
  * @{
  */
/**
  * @brief  Perform NAND memory initialization sequence
  * @param  hperh: pointer to a nand_handle_t structure 
  * @param  ctiming: pointer to common space timing structure
  * @param  atiming: pointer to attribute space timing structure
  * @retval ald status
  */
ald_status_t ald_nand_init(nand_handle_t *hperh, ald_ebi_nand_timing_t *ctiming, ald_ebi_nand_timing_t *atiming)
{
	if (hperh == NULL)
		return ERROR;
	if (hperh->state == ALD_NAND_STATE_RESET)
		hperh->lock = UNLOCK;

	ald_ebi_nand_init(hperh->instance, &(hperh->init));
	ald_ebi_nand_comm_timing_init(hperh->instance, ctiming, hperh->init.bank);
	ald_ebi_nand_attr_timing_init(hperh->instance, atiming, hperh->init.bank);
	ald_ebi_nand_enable(hperh->instance, hperh->init.bank);
	hperh->state = ALD_NAND_STATE_READY;

	return OK;
}

/**
  * @brief  Perform NAND memory De-Initialization sequence
  * @param  hperh: pointer to a nand_handle_t structure 
  * @retval ald status
  */
ald_status_t ald_nand_deinit(nand_handle_t *hperh)
{
	ald_ebi_nand_deinit(hperh->instance, hperh->init.bank);
	hperh->state = ALD_NAND_STATE_RESET;
	__UNLOCK(hperh);

	return OK;
}

/**
  * @brief  NAND memory reset
  * @param  hperh: pointer to a nand_handle_t structure 
  * @retval ald status
  */
ald_status_t ald_nand_reset(nand_handle_t *hperh)
{
	uint32_t deviceaddr = 0;

	__LOCK(hperh);

	if (hperh->state == ALD_NAND_STATE_BUSY)
		return BUSY;

	/* Identify the device address */
	if (hperh->init.bank == EBI_NAND_BANK2)
		deviceaddr = NAND_DEVICE1;
	else
		deviceaddr = NAND_DEVICE2;

	hperh->state = ALD_NAND_STATE_BUSY;

	/* Send NAND reset command */  
	*(__IO uint8_t *)((uint32_t)(deviceaddr | CMD_AREA)) = 0xFF;

	hperh->state = ALD_NAND_STATE_READY;
	__UNLOCK(hperh);

	return OK;
}

/**
  * @brief  configure the device: Enter the physical parameters of the device
  * @param  hperh: pointer to a nand_handle_t structure 
  * @param  pdcfg : pointer to nand_device_cfg_t structure
  * @retval ald status
  */
void nand_config_device(nand_handle_t *hperh, nand_device_cfg_t *pdcfg)
{
	hperh->config.page_size  = pdcfg->page_size;
	hperh->config.spare_size = pdcfg->spare_size;
	hperh->config.block_size = pdcfg->block_size;
	hperh->config.block_nbr  = pdcfg->block_nbr;
	hperh->config.plane_size = pdcfg->plane_size;
	hperh->config.plane_nbr  = pdcfg->plane_nbr;
	hperh->config.extra_cmd  = pdcfg->extra_cmd;
}
/**
  * @}
  */
/** @defgroup Nand_Public_Functions_Group2 IO operation functions
  * @brief IO operation functions
  * @{
  */
/**
  * @brief  Read the NAND memory electronic signature
  * @param  hperh: pointer to a nand_handle_t structure
  * @param  id: NAND ID structure
  * @retval ald status
  */
ald_status_t ald_nand_read_id(nand_handle_t *hperh, nand_id_t *id)
{
	__IO uint32_t data  = 0;
	__IO uint32_t data1 = 0;
	uint32_t deviceaddr = 0;

	__LOCK(hperh);

	if (hperh->state == ALD_NAND_STATE_BUSY)
		return BUSY;

	/* Identify the device address */
	if (hperh->init.bank == EBI_NAND_BANK2)
		deviceaddr = NAND_DEVICE1;
	else
		deviceaddr = NAND_DEVICE2;

	hperh->state = ALD_NAND_STATE_BUSY;

	/* Send Read ID command sequence */   
	*(__IO uint8_t *)((uint32_t)(deviceaddr | CMD_AREA))  = NAND_CMD_READID;
	*(__IO uint8_t *)((uint32_t)(deviceaddr | ADDR_AREA)) = 0x00;

	if (hperh->init.width == EBI_NAND_MEM_BUS_WIDTH_8) {
		data = *(__IO uint32_t *)deviceaddr;

		id->maker_id  = ADDR_1ST_CYCLE(data);
		id->device_id = ADDR_2ND_CYCLE(data);
		id->third_id  = ADDR_3RD_CYCLE(data);
		id->fourth_id = ADDR_4TH_CYCLE(data);
	}
	else {
		data  = *(__IO uint32_t *)deviceaddr;
		data1 = *((__IO uint32_t *)deviceaddr + 4);

		/* Return the data read */
		id->maker_id  = ADDR_1ST_CYCLE(data);
		id->device_id = ADDR_3RD_CYCLE(data);
		id->third_id  = ADDR_1ST_CYCLE(data1);
		id->fourth_id = ADDR_3RD_CYCLE(data1);
	}

	hperh->state = ALD_NAND_STATE_READY;
	__UNLOCK(hperh);

	return OK;
}

/**
  * @brief  Read page(s) from NAND memory block (8-bits addressing)
  * @param  hperh: pointer to a nand_handle_t structure 
  * @param  addr: pointer to NAND address structure
  * @param  buf: pointer to destination read buffer
  * @param  nr: number of pages to read from block 
  * @retval ald status
  */
ald_status_t ald_nand_read_page_8b(nand_handle_t *hperh, nand_address_t *addr, uint8_t *buf, uint32_t nr)
{
	__IO uint32_t index  = 0;
	uint32_t tickstart = 0;
	uint32_t deviceaddr = 0, size = 0, num = 0, nandaddr = 0;

	__LOCK(hperh);

	if (hperh->state == ALD_NAND_STATE_BUSY)
		return BUSY;

	/* Identify the device address */
	if (hperh->init.bank == EBI_NAND_BANK2)
		deviceaddr = NAND_DEVICE1;
	else
		deviceaddr = NAND_DEVICE2;

	hperh->state = ALD_NAND_STATE_BUSY;

	/* NAND raw address calculation */
	nandaddr = ARRAY_ADDRESS(addr, hperh);

	/* Page(s) read loop */
	while ((nr != 0) && (nandaddr < ((hperh->config.block_size) * (hperh->config.block_nbr)))) {
		/* update the buffer size */
		size = (hperh->config.page_size) + ((hperh->config.page_size) * num);

		/* Send read page command sequence */
		*(__IO uint8_t *)((uint32_t)(deviceaddr | CMD_AREA)) = NAND_CMD_AREA_A;

		/* Cards with page size <= 512 bytes */
		if ((hperh->config.page_size) <= 512U) {
			if (((hperh->config.block_size)* (hperh->config.block_nbr)) <= 65536U) {
				*(__IO uint8_t *)((uint32_t)(deviceaddr | ADDR_AREA)) = 0x00;
				*(__IO uint8_t *)((uint32_t)(deviceaddr | ADDR_AREA)) = ADDR_1ST_CYCLE(nandaddr);
				*(__IO uint8_t *)((uint32_t)(deviceaddr | ADDR_AREA)) = ADDR_2ND_CYCLE(nandaddr);
			}
			else { /* ((hperh->config.block_size)*(hperh->config.block_nbr)) > 65536 */
				*(__IO uint8_t *)((uint32_t)(deviceaddr | ADDR_AREA)) = 0x00;
				*(__IO uint8_t *)((uint32_t)(deviceaddr | ADDR_AREA)) = ADDR_1ST_CYCLE(nandaddr);
				*(__IO uint8_t *)((uint32_t)(deviceaddr | ADDR_AREA)) = ADDR_2ND_CYCLE(nandaddr);
				*(__IO uint8_t *)((uint32_t)(deviceaddr | ADDR_AREA)) = ADDR_3RD_CYCLE(nandaddr);
			}
		}
		else { /* (hperh->config.page_size) > 512 */
			if (((hperh->config.block_size)* (hperh->config.block_nbr)) <= 65536U) {
				*(__IO uint8_t *)((uint32_t)(deviceaddr | ADDR_AREA)) = 0x00;
				*(__IO uint8_t *)((uint32_t)(deviceaddr | ADDR_AREA)) = 0x00;
				*(__IO uint8_t *)((uint32_t)(deviceaddr | ADDR_AREA)) = ADDR_1ST_CYCLE(nandaddr);
				*(__IO uint8_t *)((uint32_t)(deviceaddr | ADDR_AREA)) = ADDR_2ND_CYCLE(nandaddr);
			}
			else {/* ((hperh->config.block_size)*(hperh->config.block_nbr)) > 65536 */
				*(__IO uint8_t *)((uint32_t)(deviceaddr | ADDR_AREA)) = 0x00;
				*(__IO uint8_t *)((uint32_t)(deviceaddr | ADDR_AREA)) = 0x00;
				*(__IO uint8_t *)((uint32_t)(deviceaddr | ADDR_AREA)) = ADDR_1ST_CYCLE(nandaddr);
				*(__IO uint8_t *)((uint32_t)(deviceaddr | ADDR_AREA)) = ADDR_2ND_CYCLE(nandaddr);
				*(__IO uint8_t *)((uint32_t)(deviceaddr | ADDR_AREA)) = ADDR_3RD_CYCLE(nandaddr);
			}
		}

		*(__IO uint8_t *)((uint32_t)(deviceaddr | CMD_AREA))  = NAND_CMD_AREA_TRUE1;

		/* Check if an extra command is needed for reading pages  */
		if (hperh->config.extra_cmd == ENABLE) {
			/* Get tick */
			tickstart = ald_get_tick();

			/* Read status until NAND is ready */
			while (ald_nand_read_status(hperh) != NAND_READY) {
				if ((ald_get_tick() - tickstart ) > NAND_WRITE_TIMEOUT)
					return TIMEOUT;
			}

			/* Go back to read mode */
			*(__IO uint8_t *)((uint32_t)(deviceaddr | CMD_AREA)) = ((uint8_t)0x00);
		}

        /*延时25us,nand flash 手册中的最大值*/
		ald_delay_us(25);
        
        /* Get Data into Buffer */    
		for (; index < size; index++) {
			*(uint8_t *)buf++ = *(uint8_t *)deviceaddr;
		}

		num++;
		nr--;
		nandaddr = (uint32_t)(nandaddr + 1U);
	}

	hperh->state = ALD_NAND_STATE_READY;
	__UNLOCK(hperh);

	return OK;
}

/**
  * @brief  Write page(s) to NAND memory block (8-bits addressing)
  * @param  hperh: pointer to a nand_handle_t structure 
  * @param  addr: pointer to NAND address structure
  * @param  buf: pointer to source buffer to write  
  * @param  nr: number of pages to write to block 
  * @retval ald status
  */
ald_status_t ald_nand_write_page_8b(nand_handle_t *hperh, nand_address_t *addr, uint8_t *buf, uint32_t nr)
{
	__IO uint32_t index = 0;
	uint32_t tickstart = 0;
	uint32_t deviceaddr = 0, size = 0, num = 0, nandaddr = 0;

	__LOCK(hperh);

	if (hperh->state == ALD_NAND_STATE_BUSY)
		return BUSY;

	/* Identify the device address */
	if (hperh->init.bank == EBI_NAND_BANK2)
		deviceaddr = NAND_DEVICE1;
	else
		deviceaddr = NAND_DEVICE2;

	/* Update the NAND controller state */ 
	hperh->state = ALD_NAND_STATE_BUSY;

	/* NAND raw address calculation */
	nandaddr = ARRAY_ADDRESS(addr, hperh);

	/* Page(s) write loop */
	while ((nr != 0) && (nandaddr < ((hperh->config.block_size) * (hperh->config.block_nbr)))) {
		/* update the buffer size */
		size = hperh->config.page_size + ((hperh->config.page_size) * num);

		/* Send write page command sequence */
		*(__IO uint8_t *)((uint32_t)(deviceaddr | CMD_AREA)) = NAND_CMD_AREA_A;
		*(__IO uint8_t *)((uint32_t)(deviceaddr | CMD_AREA)) = NAND_CMD_WRITE0;

		/* Cards with page size <= 512 bytes */
		if ((hperh->config.page_size) <= 512U) {
			if (((hperh->config.block_size)* (hperh->config.block_nbr)) <= 65536U) {
				*(__IO uint8_t *)((uint32_t)(deviceaddr | ADDR_AREA)) = 0x00;
				*(__IO uint8_t *)((uint32_t)(deviceaddr | ADDR_AREA)) = ADDR_1ST_CYCLE(nandaddr);
				*(__IO uint8_t *)((uint32_t)(deviceaddr | ADDR_AREA)) = ADDR_2ND_CYCLE(nandaddr);
			}
			else {/* ((hperh->config.block_size)*(hperh->config.block_nbr)) > 65536 */
				*(__IO uint8_t *)((uint32_t)(deviceaddr | ADDR_AREA)) = 0x00;
				*(__IO uint8_t *)((uint32_t)(deviceaddr | ADDR_AREA)) = ADDR_1ST_CYCLE(nandaddr);
				*(__IO uint8_t *)((uint32_t)(deviceaddr | ADDR_AREA)) = ADDR_2ND_CYCLE(nandaddr);
				*(__IO uint8_t *)((uint32_t)(deviceaddr | ADDR_AREA)) = ADDR_3RD_CYCLE(nandaddr);
			}
		}
		else {/* (hperh->config.page_size) > 512 */
			if (((hperh->config.block_size)* (hperh->config.block_nbr)) <= 65536U) {
				*(__IO uint8_t *)((uint32_t)(deviceaddr | ADDR_AREA)) = 0x00;
				*(__IO uint8_t *)((uint32_t)(deviceaddr | ADDR_AREA)) = 0x00;
				*(__IO uint8_t *)((uint32_t)(deviceaddr | ADDR_AREA)) = ADDR_1ST_CYCLE(nandaddr);
				*(__IO uint8_t *)((uint32_t)(deviceaddr | ADDR_AREA)) = ADDR_2ND_CYCLE(nandaddr);
			}
			else {/* ((hperh->config.block_size)*(hperh->config.block_nbr)) > 65536 */
				*(__IO uint8_t *)((uint32_t)(deviceaddr | ADDR_AREA)) = 0x00;
				*(__IO uint8_t *)((uint32_t)(deviceaddr | ADDR_AREA)) = 0x00;
				*(__IO uint8_t *)((uint32_t)(deviceaddr | ADDR_AREA)) = ADDR_1ST_CYCLE(nandaddr);
				*(__IO uint8_t *)((uint32_t)(deviceaddr | ADDR_AREA)) = ADDR_2ND_CYCLE(nandaddr);
				*(__IO uint8_t *)((uint32_t)(deviceaddr | ADDR_AREA)) = ADDR_3RD_CYCLE(nandaddr);
			}
		}

		/* Write data to memory */
		for (; index < size; index++)
			*(__IO uint8_t *)deviceaddr = *(uint8_t *)buf++;

		*(__IO uint8_t *)((uint32_t)(deviceaddr | CMD_AREA)) = NAND_CMD_WRITE_TRUE1;

		/* Read status until NAND is ready */
		while (ald_nand_read_status(hperh) != NAND_READY) {
			/* Get tick */
			tickstart = ald_get_tick();

			if ((ald_get_tick() - tickstart ) > NAND_WRITE_TIMEOUT)
				return TIMEOUT;
		}

		num++;
		nr--;
		nandaddr = (uint32_t)(nandaddr + 1U);
	}

	hperh->state = ALD_NAND_STATE_READY;
	__UNLOCK(hperh);

	return OK;
}

/**
  * @brief  Read spare area(s) from NAND memory (8-bits addressing)
  * @param  hperh: pointer to a nand_handle_t structure 
  * @param  addr: pointer to NAND address structure
  * @param  buf: pointer to source buffer to write  
  * @param  nr: Number of spare area to read  
  * @retval ald status
*/
ald_status_t ald_nand_read_sparearea_8b(nand_handle_t *hperh, nand_address_t *addr, uint8_t *buf, uint32_t nr)
{
	__IO uint32_t index = 0;
	uint32_t tickstart = 0;
	uint32_t deviceaddr = 0, size = 0, num = 0, nandaddr = 0, columnaddr = 0;

	/* Process Locked */
	__LOCK(hperh);

	if (hperh->state == ALD_NAND_STATE_BUSY)
		return BUSY;

	/* Identify the device address */
	if (hperh->init.bank == EBI_NAND_BANK2)
		deviceaddr = NAND_DEVICE1;
	else
		deviceaddr = NAND_DEVICE2;

	/* Update the NAND controller state */
	hperh->state = ALD_NAND_STATE_BUSY;
	/* NAND raw address calculation */
	nandaddr = ARRAY_ADDRESS(addr, hperh);
	/* Column in page address */
	columnaddr = COLUMN_ADDRESS(hperh);

	/* Spare area(s) read loop */ 
	while ((nr != 0) && (nandaddr < ((hperh->config.block_size) * (hperh->config.block_nbr)))) {
		/* update the buffer size */
		size = (hperh->config.spare_size) + ((hperh->config.spare_size) * num);

		/* Cards with page size <= 512 bytes */
		if ((hperh->config.page_size) <= 512U) {
			/* Send read spare area command sequence */     
			*(__IO uint8_t *)((uint32_t)(deviceaddr | CMD_AREA)) = NAND_CMD_AREA_C;

			if (((hperh->config.block_size)* (hperh->config.block_nbr)) <= 65536U) {
				*(__IO uint8_t *)((uint32_t)(deviceaddr | ADDR_AREA)) = 0x00;
				*(__IO uint8_t *)((uint32_t)(deviceaddr | ADDR_AREA)) = ADDR_1ST_CYCLE(nandaddr);
				*(__IO uint8_t *)((uint32_t)(deviceaddr | ADDR_AREA)) = ADDR_2ND_CYCLE(nandaddr);
			}
			else {/* ((hperh->config.block_size)*(hperh->config.block_nbr)) > 65536 */
				*(__IO uint8_t *)((uint32_t)(deviceaddr | ADDR_AREA)) = 0x00;
				*(__IO uint8_t *)((uint32_t)(deviceaddr | ADDR_AREA)) = ADDR_1ST_CYCLE(nandaddr);
				*(__IO uint8_t *)((uint32_t)(deviceaddr | ADDR_AREA)) = ADDR_2ND_CYCLE(nandaddr);
				*(__IO uint8_t *)((uint32_t)(deviceaddr | ADDR_AREA)) = ADDR_3RD_CYCLE(nandaddr);
			}
		}
		else {/* (hperh->config.page_size) > 512 */
			/* Send read spare area command sequence */ 
			*(__IO uint8_t *)((uint32_t)(deviceaddr | CMD_AREA)) = NAND_CMD_AREA_A;

			if (((hperh->config.block_size)* (hperh->config.block_nbr)) <= 65536U) {
				*(__IO uint8_t *)((uint32_t)(deviceaddr | ADDR_AREA)) = COLUMN_1ST_CYCLE(columnaddr);
				*(__IO uint8_t *)((uint32_t)(deviceaddr | ADDR_AREA)) = COLUMN_2ND_CYCLE(columnaddr);
				*(__IO uint8_t *)((uint32_t)(deviceaddr | ADDR_AREA)) = ADDR_1ST_CYCLE(nandaddr);
				*(__IO uint8_t *)((uint32_t)(deviceaddr | ADDR_AREA)) = ADDR_2ND_CYCLE(nandaddr);
			}
			else {/* ((hperh->config.block_size)*(hperh->config.block_nbr)) > 65536 */
				*(__IO uint8_t *)((uint32_t)(deviceaddr | ADDR_AREA)) = COLUMN_1ST_CYCLE(columnaddr);
				*(__IO uint8_t *)((uint32_t)(deviceaddr | ADDR_AREA)) = COLUMN_2ND_CYCLE(columnaddr);
				*(__IO uint8_t *)((uint32_t)(deviceaddr | ADDR_AREA)) = ADDR_1ST_CYCLE(nandaddr);
				*(__IO uint8_t *)((uint32_t)(deviceaddr | ADDR_AREA)) = ADDR_2ND_CYCLE(nandaddr);
				*(__IO uint8_t *)((uint32_t)(deviceaddr | ADDR_AREA)) = ADDR_3RD_CYCLE(nandaddr);
			}
		}

		*(__IO uint8_t *)((uint32_t)(deviceaddr | CMD_AREA)) = NAND_CMD_AREA_TRUE1;

		if (hperh->config.extra_cmd == ENABLE) {
			/* Get tick */
			tickstart = ald_get_tick();

			/* Read status until NAND is ready */
			while (ald_nand_read_status(hperh) != NAND_READY) {
				if ((ald_get_tick() - tickstart ) > NAND_WRITE_TIMEOUT)
					return TIMEOUT;
			}

			/* Go back to read mode */
			*(__IO uint8_t *)((uint32_t)(deviceaddr | CMD_AREA)) = ((uint8_t)0x00);
		}

		/* Get Data into Buffer */
		for (; index < size; index++)
			*(uint8_t *)buf++ = *(uint8_t *)deviceaddr;

		num++;
		nr--;
		nandaddr = (uint32_t)(nandaddr + 1U);
	}

	hperh->state = ALD_NAND_STATE_READY;
	__UNLOCK(hperh);

	return OK;
}

/**
  * @brief  Write spare area(s) to NAND memory (8-bits addressing)
  * @param  hperh: pointer to a nand_handle_t structure 
  * @param  addr: pointer to NAND address structure
  * @param  buf: pointer to source buffer to write  
  * @param  nr: number of spare areas to write to block
  * @retval ald status
  */
ald_status_t ald_nand_write_sparearea_8b(nand_handle_t *hperh, nand_address_t *addr, uint8_t *buf, uint32_t nr)
{
	__IO uint32_t index = 0;
	uint32_t tickstart = 0;
	uint32_t deviceaddr = 0, size = 0, num = 0, nandaddr = 0, columnaddr = 0;

	__LOCK(hperh);

	if (hperh->state == ALD_NAND_STATE_BUSY)
		return BUSY;

	/* Identify the device address */
	if (hperh->init.bank == EBI_NAND_BANK2)
		deviceaddr = NAND_DEVICE1;
	else
		deviceaddr = NAND_DEVICE2;

	/* Update the FSMC_NAND controller state */
	hperh->state = ALD_NAND_STATE_BUSY;
	/* Page address calculation */
	nandaddr = ARRAY_ADDRESS(addr, hperh);
	/* Column in page address */
	columnaddr = COLUMN_ADDRESS(hperh);

	/* Spare area(s) write loop */
	while ((nr != 0) && (nandaddr < ((hperh->config.block_size) * (hperh->config.block_nbr)))) {
		/* update the buffer size */
		size = (hperh->config.spare_size) + ((hperh->config.spare_size) * num);

		/* Cards with page size <= 512 bytes */
		if ((hperh->config.page_size) <= 512U) {
			/* Send write Spare area command sequence */
			*(__IO uint8_t *)((uint32_t)(deviceaddr | CMD_AREA)) = NAND_CMD_AREA_C;
			*(__IO uint8_t *)((uint32_t)(deviceaddr | CMD_AREA)) = NAND_CMD_WRITE0;

			if (((hperh->config.block_size)* (hperh->config.block_nbr)) <= 65536U) {
				*(__IO uint8_t *)((uint32_t)(deviceaddr | ADDR_AREA)) = 0x00;
				*(__IO uint8_t *)((uint32_t)(deviceaddr | ADDR_AREA)) = ADDR_1ST_CYCLE(nandaddr);
				*(__IO uint8_t *)((uint32_t)(deviceaddr | ADDR_AREA)) = ADDR_2ND_CYCLE(nandaddr);
			}
			else {/* ((hperh->config.block_size)*(hperh->config.block_nbr)) > 65536 */
				*(__IO uint8_t *)((uint32_t)(deviceaddr | ADDR_AREA)) = 0x00;
				*(__IO uint8_t *)((uint32_t)(deviceaddr | ADDR_AREA)) = ADDR_1ST_CYCLE(nandaddr);
				*(__IO uint8_t *)((uint32_t)(deviceaddr | ADDR_AREA)) = ADDR_2ND_CYCLE(nandaddr);
				*(__IO uint8_t *)((uint32_t)(deviceaddr | ADDR_AREA)) = ADDR_3RD_CYCLE(nandaddr);
			}
		}
		else {/* (hperh->config.page_size) > 512 */
			/* Send write Spare area command sequence */
			*(__IO uint8_t *)((uint32_t)(deviceaddr | CMD_AREA)) = NAND_CMD_AREA_A;
			*(__IO uint8_t *)((uint32_t)(deviceaddr | CMD_AREA)) = NAND_CMD_WRITE0;

			if (((hperh->config.block_size)* (hperh->config.block_nbr)) <= 65536U) {
				*(__IO uint8_t *)((uint32_t)(deviceaddr | ADDR_AREA)) = COLUMN_1ST_CYCLE(columnaddr);
				*(__IO uint8_t *)((uint32_t)(deviceaddr | ADDR_AREA)) = COLUMN_2ND_CYCLE(columnaddr);
				*(__IO uint8_t *)((uint32_t)(deviceaddr | ADDR_AREA)) = ADDR_1ST_CYCLE(nandaddr);
				*(__IO uint8_t *)((uint32_t)(deviceaddr | ADDR_AREA)) = ADDR_2ND_CYCLE(nandaddr);
			}
			else {/* ((hperh->config.block_size)*(hperh->config.block_nbr)) > 65536 */
				*(__IO uint8_t *)((uint32_t)(deviceaddr | ADDR_AREA)) = COLUMN_1ST_CYCLE(columnaddr);
				*(__IO uint8_t *)((uint32_t)(deviceaddr | ADDR_AREA)) = COLUMN_2ND_CYCLE(columnaddr);
				*(__IO uint8_t *)((uint32_t)(deviceaddr | ADDR_AREA)) = ADDR_1ST_CYCLE(nandaddr);
				*(__IO uint8_t *)((uint32_t)(deviceaddr | ADDR_AREA)) = ADDR_2ND_CYCLE(nandaddr);
				*(__IO uint8_t *)((uint32_t)(deviceaddr | ADDR_AREA)) = ADDR_3RD_CYCLE(nandaddr);
			}
		}

		/* Write data to memory */
		for (; index < size; index++)
			*(__IO uint8_t *)deviceaddr = *(uint8_t *)buf++;

		*(__IO uint8_t *)((uint32_t)(deviceaddr | CMD_AREA)) = NAND_CMD_WRITE_TRUE1;
		tickstart = ald_get_tick();

		/* Read status until NAND is ready */
		while (ald_nand_read_status(hperh) != NAND_READY) {
			if ((ald_get_tick() - tickstart ) > NAND_WRITE_TIMEOUT)
				return TIMEOUT;
		}

		num++;
		nr--;
		nandaddr = (uint32_t)(nandaddr + 1U);
	}

	hperh->state = ALD_NAND_STATE_READY;
	__UNLOCK(hperh);

	return OK;
}

/**
  * @brief  Read page(s) from NAND memory block (16-bits addressing)
  * @param  hperh: pointer to a nand_handle_t structure 
  * @param  addr: pointer to NAND address structure
  * @param  buf: pointer to destination read buffer
  * @param  nr: number of pages to read from block 
  * @retval ald status
  */
ald_status_t ald_nand_read_page_16b(nand_handle_t *hperh, nand_address_t *addr, uint16_t *buf, uint32_t nr)
{
	__IO uint32_t index  = 0;
	uint32_t tickstart = 0;
	uint32_t deviceaddr = 0, size = 0, num = 0, nandaddr = 0;

	__LOCK(hperh);

	if (hperh->state == ALD_NAND_STATE_BUSY)
		return BUSY;

	/* Identify the device address */
	if (hperh->init.bank == EBI_NAND_BANK2)
		deviceaddr = NAND_DEVICE1;
	else
		deviceaddr = NAND_DEVICE2;

	hperh->state = ALD_NAND_STATE_BUSY;
	nandaddr = ARRAY_ADDRESS(addr, hperh);

	/* Page(s) read loop */  
	while ((nr != 0) && (nandaddr < ((hperh->config.block_size) * (hperh->config.block_nbr)))) {
		/* update the buffer size */
		size = (hperh->config.page_size) + ((hperh->config.page_size) * num);

		/* Send read page command sequence */
		*(__IO uint8_t *)((uint32_t)(deviceaddr | CMD_AREA)) = NAND_CMD_AREA_A;

		/* Cards with page size <= 512 bytes */
		if ((hperh->config.page_size) <= 512U) {
			if (((hperh->config.block_size)* (hperh->config.block_nbr)) <= 65536U) {
				*(__IO uint8_t *)((uint32_t)(deviceaddr | ADDR_AREA)) = 0x00;
				*(__IO uint8_t *)((uint32_t)(deviceaddr | ADDR_AREA)) = ADDR_1ST_CYCLE(nandaddr);
				*(__IO uint8_t *)((uint32_t)(deviceaddr | ADDR_AREA)) = ADDR_2ND_CYCLE(nandaddr);
			}
			else {/* ((hperh->config.block_size)*(hperh->config.block_nbr)) > 65536 */
				*(__IO uint8_t *)((uint32_t)(deviceaddr | ADDR_AREA)) = 0x00;
				*(__IO uint8_t *)((uint32_t)(deviceaddr | ADDR_AREA)) = ADDR_1ST_CYCLE(nandaddr);
				*(__IO uint8_t *)((uint32_t)(deviceaddr | ADDR_AREA)) = ADDR_2ND_CYCLE(nandaddr);
				*(__IO uint8_t *)((uint32_t)(deviceaddr | ADDR_AREA)) = ADDR_3RD_CYCLE(nandaddr);
			}
		}
		else {/* (hperh->config.page_size) > 512 */
			if (((hperh->config.block_size)* (hperh->config.block_nbr)) <= 65536U) {
				*(__IO uint8_t *)((uint32_t)(deviceaddr | ADDR_AREA)) = 0x00;
				*(__IO uint8_t *)((uint32_t)(deviceaddr | ADDR_AREA)) = 0x00;
				*(__IO uint8_t *)((uint32_t)(deviceaddr | ADDR_AREA)) = ADDR_1ST_CYCLE(nandaddr);
				*(__IO uint8_t *)((uint32_t)(deviceaddr | ADDR_AREA)) = ADDR_2ND_CYCLE(nandaddr);
			}
			else {/* ((hperh->config.block_size)*(hperh->config.block_nbr)) > 65536 */
				*(__IO uint8_t *)((uint32_t)(deviceaddr | ADDR_AREA)) = 0x00;
				*(__IO uint8_t *)((uint32_t)(deviceaddr | ADDR_AREA)) = 0x00;
				*(__IO uint8_t *)((uint32_t)(deviceaddr | ADDR_AREA)) = ADDR_1ST_CYCLE(nandaddr);
				*(__IO uint8_t *)((uint32_t)(deviceaddr | ADDR_AREA)) = ADDR_2ND_CYCLE(nandaddr);
				*(__IO uint8_t *)((uint32_t)(deviceaddr | ADDR_AREA)) = ADDR_3RD_CYCLE(nandaddr);
			}
		}

		*(__IO uint8_t *)((uint32_t)(deviceaddr | CMD_AREA))  = NAND_CMD_AREA_TRUE1;

		if (hperh->config.extra_cmd == ENABLE) {
			/* Get tick */
			tickstart = ald_get_tick();

			/* Read status until NAND is ready */
			while (ald_nand_read_status(hperh) != NAND_READY) {
				if ((ald_get_tick() - tickstart ) > NAND_WRITE_TIMEOUT)
				  return TIMEOUT;
			}

			/* Go back to read mode */
			*(__IO uint8_t *)((uint32_t)(deviceaddr | CMD_AREA)) = ((uint8_t)0x00);
		}

		/* Get Data into Buffer */    
		for (; index < size; index++)
			*(uint16_t *)buf++ = *(uint16_t *)deviceaddr;

		num++;
		nr--;
		nandaddr = (uint32_t)(nandaddr + 1U);
	}

	hperh->state = ALD_NAND_STATE_READY;
	__UNLOCK(hperh);

	return OK;
}

/**
  * @brief  Write page(s) to NAND memory block (16-bits addressing)
  * @param  hperh: pointer to a nand_handle_t structure
  * @param  addr : pointer to NAND address structure
  * @param  buf : pointer to source buffer to write. pbuffer should be 16bits aligned
  * @param  nr : number of pages to write to block 
  * @retval ald status
  */
ald_status_t ald_nand_write_page_16b(nand_handle_t *hperh, nand_address_t *addr, uint16_t *buf, uint32_t nr)
{
	__IO uint32_t index = 0;
	uint32_t tickstart = 0;
	uint32_t deviceaddr = 0, size = 0, num = 0, nandaddr = 0;

	__LOCK(hperh);

	if (hperh->state == ALD_NAND_STATE_BUSY)
		return BUSY;

	/* Identify the device address */
	if (hperh->init.bank == EBI_NAND_BANK2)
		deviceaddr = NAND_DEVICE1;
	else
		deviceaddr = NAND_DEVICE2;

	hperh->state = ALD_NAND_STATE_BUSY;
	/* NAND raw address calculation */
	nandaddr = ARRAY_ADDRESS(addr, hperh);

	/* Page(s) write loop */
	while ((nr != 0) && (nandaddr < ((hperh->config.block_size) * (hperh->config.block_nbr)))) {
		/* update the buffer size */
		size = (hperh->config.page_size) + ((hperh->config.page_size) * num);

		/* Send write page command sequence */
		*(__IO uint8_t *)((uint32_t)(deviceaddr | CMD_AREA)) = NAND_CMD_AREA_A;
		*(__IO uint8_t *)((uint32_t)(deviceaddr | CMD_AREA)) = NAND_CMD_WRITE0;

		/* Cards with page size <= 512 bytes */
		if ((hperh->config.page_size) <= 512U) {
			if (((hperh->config.block_size)* (hperh->config.block_nbr)) <= 65536U) {
				*(__IO uint8_t *)((uint32_t)(deviceaddr | ADDR_AREA)) = 0x00;
				*(__IO uint8_t *)((uint32_t)(deviceaddr | ADDR_AREA)) = ADDR_1ST_CYCLE(nandaddr);
				*(__IO uint8_t *)((uint32_t)(deviceaddr | ADDR_AREA)) = ADDR_2ND_CYCLE(nandaddr);
			}
			else {	/* ((hperh->config.block_size)*(hperh->config.block_nbr)) > 65536 */
				*(__IO uint8_t *)((uint32_t)(deviceaddr | ADDR_AREA)) = 0x00;
				*(__IO uint8_t *)((uint32_t)(deviceaddr | ADDR_AREA)) = ADDR_1ST_CYCLE(nandaddr);
				*(__IO uint8_t *)((uint32_t)(deviceaddr | ADDR_AREA)) = ADDR_2ND_CYCLE(nandaddr);
				*(__IO uint8_t *)((uint32_t)(deviceaddr | ADDR_AREA)) = ADDR_3RD_CYCLE(nandaddr);
			}
		}
		else {	/* (hperh->config.page_size) > 512 */
			if (((hperh->config.block_size)* (hperh->config.block_nbr)) <= 65536U) {
				*(__IO uint8_t *)((uint32_t)(deviceaddr | ADDR_AREA)) = 0x00;
				*(__IO uint8_t *)((uint32_t)(deviceaddr | ADDR_AREA)) = 0x00;
				*(__IO uint8_t *)((uint32_t)(deviceaddr | ADDR_AREA)) = ADDR_1ST_CYCLE(nandaddr);
				*(__IO uint8_t *)((uint32_t)(deviceaddr | ADDR_AREA)) = ADDR_2ND_CYCLE(nandaddr);
			}
			else {	/* ((hperh->config.block_size)*(hperh->config.block_nbr)) > 65536 */
				*(__IO uint8_t *)((uint32_t)(deviceaddr | ADDR_AREA)) = 0x00;
				*(__IO uint8_t *)((uint32_t)(deviceaddr | ADDR_AREA)) = 0x00;
				*(__IO uint8_t *)((uint32_t)(deviceaddr | ADDR_AREA)) = ADDR_1ST_CYCLE(nandaddr);
				*(__IO uint8_t *)((uint32_t)(deviceaddr | ADDR_AREA)) = ADDR_2ND_CYCLE(nandaddr);
				*(__IO uint8_t *)((uint32_t)(deviceaddr | ADDR_AREA)) = ADDR_3RD_CYCLE(nandaddr);
			}
		}

		/* Write data to memory */
		for (; index < size; index++)
			*(__IO uint16_t *)deviceaddr = *(uint16_t *)buf++;

		*(__IO uint8_t *)((uint32_t)(deviceaddr | CMD_AREA)) = NAND_CMD_WRITE_TRUE1;

		/* Read status until NAND is ready */
		while (ald_nand_read_status(hperh) != NAND_READY) {
			/* Get tick */
			tickstart = ald_get_tick();

			if ((ald_get_tick() - tickstart ) > NAND_WRITE_TIMEOUT)
			return TIMEOUT;
		}

		num++;
		nr--;
		nandaddr = (uint32_t)(nandaddr + 1U);
	}

	hperh->state = ALD_NAND_STATE_READY;
	__UNLOCK(hperh);

	return OK;
}



/**
  * @brief  Read spare area(s) from NAND memory (16-bits addressing)
  * @param  hperh: pointer to a nand_handle_t structure 
  * @param  addr: pointer to NAND address structure
  * @param  buf: pointer to source buffer to write. pbuffer should be 16bits aligned.
  * @param  nr: Number of spare area to read  
  * @retval ald status
*/
ald_status_t ald_nand_read_sparearea_16b(nand_handle_t *hperh, nand_address_t *addr, uint16_t *buf, uint32_t nr)
{
	__IO uint32_t index = 0;
	uint32_t tickstart = 0;
	uint32_t deviceaddr = 0, size = 0, num = 0, nandaddr = 0, columnaddr = 0;

	__LOCK(hperh);

	/* Check the NAND controller state */
	if (hperh->state == ALD_NAND_STATE_BUSY)
		return BUSY;

	/* Identify the device address */
	if (hperh->init.bank == EBI_NAND_BANK2)
		deviceaddr = NAND_DEVICE1;
	else
		deviceaddr = NAND_DEVICE2;

	/* Update the NAND controller state */
	hperh->state = ALD_NAND_STATE_BUSY;
	/* NAND raw address calculation */
	nandaddr = ARRAY_ADDRESS(addr, hperh);
	/* Column in page address */
	columnaddr = (uint32_t)(COLUMN_ADDRESS(hperh) * 2U);

	/* Spare area(s) read loop */ 
	while ((nr != 0) && (nandaddr < ((hperh->config.block_size) * (hperh->config.block_nbr)))) {
		/* update the buffer size */
		size = (hperh->config.spare_size) + ((hperh->config.spare_size) * num);

		/* Cards with page size <= 512 bytes */
		if ((hperh->config.page_size) <= 512U) {
			/* Send read spare area command sequence */     
			*(__IO uint8_t *)((uint32_t)(deviceaddr | CMD_AREA)) = NAND_CMD_AREA_C;

			if (((hperh->config.block_size)* (hperh->config.block_nbr)) <= 65536U) {
				*(__IO uint8_t *)((uint32_t)(deviceaddr | ADDR_AREA)) = 0x00;
				*(__IO uint8_t *)((uint32_t)(deviceaddr | ADDR_AREA)) = ADDR_1ST_CYCLE(nandaddr);
				*(__IO uint8_t *)((uint32_t)(deviceaddr | ADDR_AREA)) = ADDR_2ND_CYCLE(nandaddr);
			}
			else {/* ((hperh->config.block_size)*(hperh->config.block_nbr)) > 65536 */
				*(__IO uint8_t *)((uint32_t)(deviceaddr | ADDR_AREA)) = 0x00;
				*(__IO uint8_t *)((uint32_t)(deviceaddr | ADDR_AREA)) = ADDR_1ST_CYCLE(nandaddr);
				*(__IO uint8_t *)((uint32_t)(deviceaddr | ADDR_AREA)) = ADDR_2ND_CYCLE(nandaddr);
				*(__IO uint8_t *)((uint32_t)(deviceaddr | ADDR_AREA)) = ADDR_3RD_CYCLE(nandaddr);
			}
		}
		else {/* (hperh->config.page_size) > 512 */
			/* Send read spare area command sequence */     
			*(__IO uint8_t *)((uint32_t)(deviceaddr | CMD_AREA)) = NAND_CMD_AREA_A;

			if (((hperh->config.block_size)* (hperh->config.block_nbr)) <= 65536U) {
				*(__IO uint8_t *)((uint32_t)(deviceaddr | ADDR_AREA)) = COLUMN_1ST_CYCLE(columnaddr);
				*(__IO uint8_t *)((uint32_t)(deviceaddr | ADDR_AREA)) = COLUMN_2ND_CYCLE(columnaddr);
				*(__IO uint8_t *)((uint32_t)(deviceaddr | ADDR_AREA)) = ADDR_1ST_CYCLE(nandaddr);
				*(__IO uint8_t *)((uint32_t)(deviceaddr | ADDR_AREA)) = ADDR_2ND_CYCLE(nandaddr);
			}
			else {/* ((hperh->config.block_size)*(hperh->config.block_nbr)) > 65536 */
				*(__IO uint8_t *)((uint32_t)(deviceaddr | ADDR_AREA)) = COLUMN_1ST_CYCLE(columnaddr);
				*(__IO uint8_t *)((uint32_t)(deviceaddr | ADDR_AREA)) = COLUMN_2ND_CYCLE(columnaddr);
				*(__IO uint8_t *)((uint32_t)(deviceaddr | ADDR_AREA)) = ADDR_1ST_CYCLE(nandaddr);
				*(__IO uint8_t *)((uint32_t)(deviceaddr | ADDR_AREA)) = ADDR_2ND_CYCLE(nandaddr);
				*(__IO uint8_t *)((uint32_t)(deviceaddr | ADDR_AREA)) = ADDR_3RD_CYCLE(nandaddr);
			}
		}

		*(__IO uint8_t *)((uint32_t)(deviceaddr | CMD_AREA)) = NAND_CMD_AREA_TRUE1;

		if (hperh->config.extra_cmd == ENABLE) {
			/* Get tick */
			tickstart = ald_get_tick();

			/* Read status until NAND is ready */
			while (ald_nand_read_status(hperh) != NAND_READY) {
				if ((ald_get_tick() - tickstart ) > NAND_WRITE_TIMEOUT)
				  return TIMEOUT;
			}

			/* Go back to read mode */
			*(__IO uint8_t *)((uint32_t)(deviceaddr | CMD_AREA)) = ((uint8_t)0x00);
		}

		/* Get Data into Buffer */
		for (; index < size; index++)
			*(uint16_t *)buf++ = *(uint16_t *)deviceaddr;

		num++;
		nr--;
		nandaddr = (uint32_t)(nandaddr + 1U);
	}

	hperh->state = ALD_NAND_STATE_READY;
	__UNLOCK(hperh);

	return OK;
}


/**
  * @brief  Write spare area(s) to NAND memory (16-bits addressing)
  * @param  hperh: pointer to a nand_handle_t structure 
  * @param  addr: pointer to NAND address structure
  * @param  buf: pointer to source buffer to write. pbuffer should be 16bits aligned.  
  * @param  nr: number of spare areas to write to block
  * @retval ald status
  */
ald_status_t ald_nand_write_sparearea_16b(nand_handle_t *hperh, nand_address_t *addr, uint16_t *buf, uint32_t nr)
{
	__IO uint32_t index = 0;
	uint32_t tickstart = 0;
	uint32_t deviceaddr = 0, size = 0, num = 0, nandaddr = 0, columnaddr = 0;

	__LOCK(hperh);

	if (hperh->state == ALD_NAND_STATE_BUSY)
		return BUSY;

	/* Identify the device address */
	if (hperh->init.bank == EBI_NAND_BANK2)
		deviceaddr = NAND_DEVICE1;
	else
		deviceaddr = NAND_DEVICE2;

	/* Update the FSMC_NAND controller state */
	hperh->state = ALD_NAND_STATE_BUSY;
	/* NAND raw address calculation */
	nandaddr = ARRAY_ADDRESS(addr, hperh);
	/* Column in page address */
	columnaddr = (uint32_t)(COLUMN_ADDRESS(hperh) * 2);

	/* Spare area(s) write loop */
	while ((nr != 0) && (nandaddr < ((hperh->config.block_size) * (hperh->config.block_nbr)))) {
		/* update the buffer size */
		size = (hperh->config.spare_size) + ((hperh->config.spare_size) * num);

		/* Cards with page size <= 512 bytes */
		if ((hperh->config.page_size) <= 512U) {
			/* Send write Spare area command sequence */
			*(__IO uint8_t *)((uint32_t)(deviceaddr | CMD_AREA)) = NAND_CMD_AREA_C;
			*(__IO uint8_t *)((uint32_t)(deviceaddr | CMD_AREA)) = NAND_CMD_WRITE0;

			if (((hperh->config.block_size)* (hperh->config.block_nbr)) <= 65536U) {
			*(__IO uint8_t *)((uint32_t)(deviceaddr | ADDR_AREA)) = 0x00;
			*(__IO uint8_t *)((uint32_t)(deviceaddr | ADDR_AREA)) = ADDR_1ST_CYCLE(nandaddr);
			*(__IO uint8_t *)((uint32_t)(deviceaddr | ADDR_AREA)) = ADDR_2ND_CYCLE(nandaddr);
			}
			else {/* ((hperh->config.block_size)*(hperh->config.block_nbr)) > 65536 */
			*(__IO uint8_t *)((uint32_t)(deviceaddr | ADDR_AREA)) = 0x00;
			*(__IO uint8_t *)((uint32_t)(deviceaddr | ADDR_AREA)) = ADDR_1ST_CYCLE(nandaddr);
			*(__IO uint8_t *)((uint32_t)(deviceaddr | ADDR_AREA)) = ADDR_2ND_CYCLE(nandaddr);
			*(__IO uint8_t *)((uint32_t)(deviceaddr | ADDR_AREA)) = ADDR_3RD_CYCLE(nandaddr);
			}
		}
		else {/* (hperh->config.page_size) > 512 */
			/* Send write Spare area command sequence */
			*(__IO uint8_t *)((uint32_t)(deviceaddr | CMD_AREA)) = NAND_CMD_AREA_A;
			*(__IO uint8_t *)((uint32_t)(deviceaddr | CMD_AREA)) = NAND_CMD_WRITE0;

			if (((hperh->config.block_size)* (hperh->config.block_nbr)) <= 65536U) {
				*(__IO uint8_t *)((uint32_t)(deviceaddr | ADDR_AREA)) = COLUMN_1ST_CYCLE(columnaddr);
				*(__IO uint8_t *)((uint32_t)(deviceaddr | ADDR_AREA)) = COLUMN_2ND_CYCLE(columnaddr);
				*(__IO uint8_t *)((uint32_t)(deviceaddr | ADDR_AREA)) = ADDR_1ST_CYCLE(nandaddr);
				*(__IO uint8_t *)((uint32_t)(deviceaddr | ADDR_AREA)) = ADDR_2ND_CYCLE(nandaddr);
			}
			else {/* ((hperh->config.block_size)*(hperh->config.block_nbr)) > 65536 */
				*(__IO uint8_t *)((uint32_t)(deviceaddr | ADDR_AREA)) = COLUMN_1ST_CYCLE(columnaddr);
				*(__IO uint8_t *)((uint32_t)(deviceaddr | ADDR_AREA)) = COLUMN_2ND_CYCLE(columnaddr);
				*(__IO uint8_t *)((uint32_t)(deviceaddr | ADDR_AREA)) = ADDR_1ST_CYCLE(nandaddr);
				*(__IO uint8_t *)((uint32_t)(deviceaddr | ADDR_AREA)) = ADDR_2ND_CYCLE(nandaddr);
				*(__IO uint8_t *)((uint32_t)(deviceaddr | ADDR_AREA)) = ADDR_3RD_CYCLE(nandaddr);
			}
		}

		/* Write data to memory */
		for (; index < size; index++)
			*(__IO uint16_t *)deviceaddr = *(uint16_t *)buf++;

		*(__IO uint8_t *)((uint32_t)(deviceaddr | CMD_AREA)) = NAND_CMD_WRITE_TRUE1;

		/* Read status until NAND is ready */
		while (ald_nand_read_status(hperh) != NAND_READY) {
			/* Get tick */
			tickstart = ald_get_tick();

			if ((ald_get_tick() - tickstart ) > NAND_WRITE_TIMEOUT)
			return TIMEOUT;
		}

		num++;
		nr--;
		nandaddr = (uint32_t)(nandaddr + 1U);
	}

	hperh->state = ALD_NAND_STATE_READY;
	__UNLOCK(hperh);

	return OK;
}

/**
  * @brief  NAND memory Block erase 
  * @param  hperh: pointer to a nand_handle_t structure 
  * @param  addr: pointer to NAND address structure
  * @retval ald status
  */
ald_status_t ald_nand_erase_block(nand_handle_t *hperh, nand_address_t *addr)
{
	uint32_t deviceaddr = 0;
	uint32_t tickstart = 0;

	__LOCK(hperh);

	if (hperh->state == ALD_NAND_STATE_BUSY)
		return BUSY;

	/* Identify the device address */
	if (hperh->init.bank == EBI_NAND_BANK2)
		deviceaddr = NAND_DEVICE1;
	else
		deviceaddr = NAND_DEVICE2;

	/* Update the NAND controller state */
	hperh->state = ALD_NAND_STATE_BUSY;

	/* Send Erase block command sequence */
	*(__IO uint8_t *)((uint32_t)(deviceaddr | CMD_AREA)) = NAND_CMD_ERASE0;
	*(__IO uint8_t *)((uint32_t)(deviceaddr | ADDR_AREA)) = ADDR_1ST_CYCLE(ARRAY_ADDRESS(addr, hperh));
	*(__IO uint8_t *)((uint32_t)(deviceaddr | ADDR_AREA)) = ADDR_2ND_CYCLE(ARRAY_ADDRESS(addr, hperh));
	*(__IO uint8_t *)((uint32_t)(deviceaddr | ADDR_AREA)) = ADDR_3RD_CYCLE(ARRAY_ADDRESS(addr, hperh));
	*(__IO uint8_t *)((uint32_t)(deviceaddr | CMD_AREA)) = NAND_CMD_ERASE1;

	/* Update the NAND controller state */
	hperh->state = ALD_NAND_STATE_READY;
	/* Get tick */
	tickstart = ald_get_tick();

	/* Read status until NAND is ready */
	while (ald_nand_read_status(hperh) != NAND_READY) {
		if ((ald_get_tick() - tickstart ) > NAND_WRITE_TIMEOUT) {
			__UNLOCK(hperh);

			return TIMEOUT;
		}
	}

	__UNLOCK(hperh);

	return OK;
}
/**
  * @}
  */

/** @defgroup Nand_Public_Functions_Group3 NAND Control functions
  * @brief NAND Control functions
  * @{
  */
/**
  * @brief  This function handles NAND device interrupt request.
  * @param  hperh: pointer to a nand_handle_t structure
  * @retval ald status
*/
void ald_nand_irq_handler(nand_handle_t *hperh)
{
	/* Check NAND interrupt Rising edge flag */
	if (ald_ebi_nand_get_flag(hperh->instance, hperh->init.bank, EBI_FLAG_RISING_EDGE)) {
		ald_nand_irq_cbk(hperh);
		ald_ebi_nand_clear_flag(hperh->instance, hperh->init.bank, EBI_FLAG_RISING_EDGE);
	}

	/* Check NAND interrupt Level flag */
	if (ald_ebi_nand_get_flag(hperh->instance, hperh->init.bank, EBI_FLAG_LEVEL)) {
		ald_nand_irq_cbk(hperh);
		ald_ebi_nand_clear_flag(hperh->instance, hperh->init.bank, EBI_FLAG_LEVEL);
	}

	if (ald_ebi_nand_get_flag(hperh->instance, hperh->init.bank, EBI_FLAG_FALLING_EDGE)) {
		/* NAND interrupt callback*/
		ald_nand_irq_cbk(hperh);
		ald_ebi_nand_clear_flag(hperh->instance, hperh->init.bank, EBI_FLAG_FALLING_EDGE);
	}

	/* Check NAND interrupt FIFO empty flag */
	if (ald_ebi_nand_get_flag(hperh->instance, hperh->init.bank, EBI_FLAG_FEMPT)) {
		ald_nand_irq_cbk(hperh);
		ald_ebi_nand_clear_flag(hperh->instance, hperh->init.bank, EBI_FLAG_FEMPT);
	}
}

/**
  * @brief  NAND interrupt feature callback
  * @param  hperh: pointer to a nand_handle_t structure 
  * @retval None
  */
__weak void ald_nand_irq_cbk(nand_handle_t *hperh)
{
	  /* Prevent unused argument(s) compilation warning */
	  UNUSED(hperh);
}

/**
  * @brief  Increment the NAND memory address
  * @param  hperh: pointer to a nand_handle_t structure
  * @param  addr: pointer to NAND address structure
  * @retval The new status of the increment address operation. It can be:
  *           - NAND_VALID_ADDRESS: When the new address is valid address
  *           - NAND_INVALID_ADDRESS: When the new address is invalid address
  */
uint32_t ald_nand_address_inc(nand_handle_t *hperh, nand_address_t *addr)
{
	uint32_t status = NAND_VALID_ADDRESS;

	addr->page++;

	/* Check NAND address is valid */
	if (addr->page == hperh->config.block_size) {
		addr->page = 0;
		addr->block++;

		if (addr->block == hperh->config.plane_size) {
			addr->block = 0;
			addr->plane++;

			if (addr->plane == (hperh->config.plane_nbr))
				status = NAND_INVALID_ADDRESS;
		}
	}

	return status;
}

/**
  * @brief  NAND memory read status 
  * @param  hperh: pointer to a nand_handle_t structure 
  * @retval NAND status
  */
uint32_t ald_nand_read_status(nand_handle_t *hperh)
{
	uint32_t data = 0;
	uint32_t addr = 0;

	/* Identify the device address */
	if (hperh->init.bank == EBI_NAND_BANK2)
		addr = NAND_DEVICE1;
	else
		addr = NAND_DEVICE2;

	/* Send Read status operation command */
	*(__IO uint8_t *)((uint32_t)(addr | CMD_AREA)) = NAND_CMD_STATUS;
	/* Read status register data */
	data = *(__IO uint8_t *)addr;

	/* Return the status */
	if ((data & NAND_ERROR) == NAND_ERROR)
		return NAND_ERROR;
	else if ((data & NAND_READY) == NAND_READY)
		return NAND_READY;

	return NAND_BUSY;
}

/**
  * @brief  return the NAND state
  * @param  hperh: pointer to a nand_handle_t structure 
  * @retval ald state
  */
ald_nand_state_t ald_nand_get_state(nand_handle_t *hperh)
{
	return hperh->state;
}

/**
  * @brief  Enables dynamically NAND ECC feature.
  * @param  hperh: pointer to a nand_handle_t structure 
  * @retval ald status
  */    
ald_status_t ald_nand_ecc_enable(nand_handle_t *hperh)
{
	if (hperh->state == ALD_NAND_STATE_BUSY)
		return BUSY;

	/* Update the NAND state */
	hperh->state = ALD_NAND_STATE_BUSY;
	/* Enable ECC feature */
	ald_ebi_nand_ecc_enable(hperh->instance, hperh->init.bank);
	/* Update the NAND state */
	hperh->state = ALD_NAND_STATE_READY;

	return OK;
}

/**
  * @brief  Disables dynamically FSMC_NAND ECC feature.
  * @param  hperh: pointer to a nand_handle_t structure 
  * @retval ald status
  */  
ald_status_t ald_nand_ecc_disable(nand_handle_t *hperh)
{
	if (hperh->state == ALD_NAND_STATE_BUSY)
		return BUSY;

	/* Update the NAND state */
	hperh->state = ALD_NAND_STATE_BUSY;
	/* Disable ECC feature */
	ald_ebi_nand_ecc_disable(hperh->instance, hperh->init.bank);
	hperh->state = ALD_NAND_STATE_READY;

	return OK;
}

/**
  * @brief  Disables dynamically NAND ECC feature.
  * @param  hperh: pointer to a nand_handle_t structure 
  * @param  val: pointer to ECC value 
  * @param  timeout: maximum timeout to wait    
  * @retval ald status
  */
ald_status_t ald_nand_get_ecc(nand_handle_t *hperh, uint32_t *val, uint32_t timeout)
{
	ald_status_t status = OK;

	if (hperh->state == ALD_NAND_STATE_BUSY)
		return BUSY;

	hperh->state = ALD_NAND_STATE_BUSY;
	status = ald_ebi_nand_get_ecc(hperh->instance, val, hperh->init.bank, timeout);
	hperh->state = ALD_NAND_STATE_READY;

	return status;
}
/**
  * @}
  */
/**
  * @}
  */
#endif
/**
  * @}
  */
/**
  * @}
  */
