/**
  * @brief  Write Spare area(s) to NAND memory 
  * @param  hnand: pointer to a NAND_HandleTypeDef structure that contains
  *                the configuration information for NAND module.
  * @param  pAddress : pointer to NAND address structure
  * @param  pBuffer : pointer to source buffer to write  
  * @param  NumSpareAreaTowrite  : number of spare areas to write to block
  * @retval HAL status
  */
HAL_StatusTypeDef HAL_NAND_Write_SpareArea(NAND_HandleTypeDef *hnand, NAND_AddressTypeDef *pAddress, uint8_t *pBuffer, uint32_t NumSpareAreaTowrite)
{
  __IO uint32_t index = 0;
  uint32_t tickstart = 0;
  uint32_t deviceaddress = 0, size = 0, num_spare_area_written = 0, addressstatus = NAND_VALID_ADDRESS;
  NAND_AddressTypeDef nandaddress;
  uint32_t addressoffset = 0;

  /* Process Locked */
  __HAL_LOCK(hnand); 
  
  /* Check the NAND controller state */
  if(hnand->State == HAL_NAND_STATE_BUSY)
  {
     return HAL_BUSY;
  }
  
  /* Identify the device address */
  if(hnand->Init.NandBank == FMC_NAND_BANK2)
  {
    deviceaddress = NAND_DEVICE1;
  }
  else
  {
    deviceaddress = NAND_DEVICE2;
  }
  
  /* Update the FMC_NAND controller state */
  hnand->State = HAL_NAND_STATE_BUSY;  
  
  /* Save the content of pAddress as it will be modified */
  nandaddress.Block     = pAddress->Block;
  nandaddress.Page      = pAddress->Page;
  nandaddress.Zone      = pAddress->Zone;
  
  /* Spare area(s) write loop */
  while((NumSpareAreaTowrite != 0) && (addressstatus == NAND_VALID_ADDRESS))
  {
    /* update the buffer size */
    size = (hnand->Info.SpareAreaSize) + ((hnand->Info.SpareAreaSize) * num_spare_area_written);

    /* Get the address offset */
    addressoffset = ARRAY_ADDRESS(&nandaddress, hnand);
    
    /* Send write Spare area command sequence */
    *(__IO uint8_t *)((uint32_t)(deviceaddress | CMD_AREA)) = NAND_CMD_AREA_C;
    *(__IO uint8_t *)((uint32_t)(deviceaddress | CMD_AREA)) = NAND_CMD_WRITE0;

    *(__IO uint8_t *)((uint32_t)(deviceaddress | ADDR_AREA)) = 0x00;  
    *(__IO uint8_t *)((uint32_t)(deviceaddress | ADDR_AREA)) = ADDR_1ST_CYCLE(addressoffset);
    *(__IO uint8_t *)((uint32_t)(deviceaddress | ADDR_AREA)) = ADDR_2ND_CYCLE(addressoffset);
    *(__IO uint8_t *)((uint32_t)(deviceaddress | ADDR_AREA)) = ADDR_3RD_CYCLE(addressoffset);
  
    /* for 512 and 1 GB devices, 4th cycle is required */     
    if(hnand->Info.BlockNbr >= 1024)
    {
      *(__IO uint8_t *)((uint32_t)(deviceaddress | ADDR_AREA)) = ADDR_4TH_CYCLE(addressoffset);
    }
  
    /* Write data to memory */
    for(; index < size; index++)
    {
      *(__IO uint8_t *)deviceaddress = *(uint8_t *)pBuffer++;
    }
   
    *(__IO uint8_t *)((uint32_t)(deviceaddress | CMD_AREA)) = NAND_CMD_WRITE_TRUE1;
    
    /* Get tick */
    tickstart = HAL_GetTick();
    
    /* Read status until NAND is ready */
    while(HAL_NAND_Read_Status(hnand) != NAND_READY)
    {
      if((HAL_GetTick() - tickstart ) > NAND_WRITE_TIMEOUT)
      {
        return HAL_TIMEOUT; 
      }
    }

    /* Increment written spare areas number */
    num_spare_area_written++;
    
    /* Decrement spare areas to write */
    NumSpareAreaTowrite--;
    
    /* Increment the NAND address */
    addressstatus = HAL_NAND_Address_Inc(hnand, &nandaddress);
  }

  /* Update the NAND controller state */
  hnand->State = HAL_NAND_STATE_READY;

  /* Process unlocked */
  __HAL_UNLOCK(hnand);
    
  return HAL_OK;
}
/**
  * @brief  Write Spare area(s) to NAND memory 
  * @param  hnand: pointer to a NAND_HandleTypeDef structure that contains
  *                the configuration information for NAND module.
  * @param  pAddress : pointer to NAND address structure
  * @param  pBuffer : pointer to source buffer to write  
  * @param  NumSpareAreaTowrite  : number of spare areas to write to block
  * @retval HAL status
  */
HAL_StatusTypeDef HAL_NAND_Write_SpareArea(NAND_HandleTypeDef *hnand, NAND_AddressTypedef *pAddress, uint8_t *pBuffer, uint32_t NumSpareAreaTowrite)
{
  __IO uint32_t index = 0;
  uint32_t tickstart = 0;
  uint32_t deviceAddress = 0, numSpareAreaWritten = 0, nandAddress = 0, addressStatus = NAND_VALID_ADDRESS;

  /* Process Locked */
  __HAL_LOCK(hnand); 
  
  /* Check the NAND controller state */
  if(hnand->State == HAL_NAND_STATE_BUSY)
  {
     return HAL_BUSY;
  }
  
  /* Identify the device address */
  if(hnand->Init.NandBank == FMC_NAND_BANK2)
  {
    deviceAddress = NAND_DEVICE1;
  }
  else
  {
    deviceAddress = NAND_DEVICE2;
  }
  
  /* Update the FMC_NAND controller state */
  hnand->State = HAL_NAND_STATE_BUSY;  
  
  /* Spare area(s) write loop */
  while((NumSpareAreaTowrite != 0) && (addressStatus == NAND_VALID_ADDRESS))
  {  
    /* NAND raw address calculation */
    nandAddress = __ARRAY_ADDRESS(pAddress, hnand);
    
    /* Send write Spare area command sequence */
    *(__IO uint8_t *)((uint32_t)(deviceAddress | CMD_AREA)) = NAND_CMD_AREA_C;
    *(__IO uint8_t *)((uint32_t)(deviceAddress | CMD_AREA)) = NAND_CMD_WRITE0;

    *(__IO uint8_t *)((uint32_t)(deviceAddress | ADDR_AREA)) = 0x00;  
    *(__IO uint8_t *)((uint32_t)(deviceAddress | ADDR_AREA)) = __ADDR_1st_CYCLE(nandAddress);  
    *(__IO uint8_t *)((uint32_t)(deviceAddress | ADDR_AREA)) = __ADDR_2nd_CYCLE(nandAddress);  
    *(__IO uint8_t *)((uint32_t)(deviceAddress | ADDR_AREA)) = __ADDR_3rd_CYCLE(nandAddress); 
  
    /* for 512 and 1 GB devices, 4th cycle is required */     
    if(hnand->Info.BlockNbr >= 1024)
    {
      *(__IO uint8_t *)((uint32_t)(deviceAddress | ADDR_AREA)) = __ADDR_4th_CYCLE(nandAddress);
    }
  
    /* Write data to memory */
    for(index = 0 ; index < hnand->Info.SpareAreaSize; index++)
    {
      *(__IO uint8_t *)deviceAddress = *(uint8_t *)pBuffer++;
    }
   
    *(__IO uint8_t *)((uint32_t)(deviceAddress | CMD_AREA)) = NAND_CMD_WRITE_TRUE1;
    
   
    /* Read status until NAND is ready */
    while(HAL_NAND_Read_Status(hnand) != NAND_READY)
    {
      /* Get tick */
      tickstart = HAL_GetTick();
    
      if((HAL_GetTick() - tickstart ) > NAND_WRITE_TIMEOUT)
      {
        return HAL_TIMEOUT; 
      }   
    }

    /* Increment written spare areas number */
    numSpareAreaWritten++;
    
    /* Decrement spare areas to write */
    NumSpareAreaTowrite--;
    
    /* Increment the NAND address */
    HAL_NAND_Address_Inc(hnand, pAddress); 
      
  }

  /* Update the NAND controller state */
  hnand->State = HAL_NAND_STATE_READY;

  /* Process unlocked */
  __HAL_UNLOCK(hnand);
    
  return HAL_OK;  
}
/**
  * @brief  Read Spare area(s) from NAND memory 
  * @param  hnand: pointer to a NAND_HandleTypeDef structure that contains
  *                the configuration information for NAND module.
  * @param  pAddress : pointer to NAND address structure
  * @param  pBuffer: pointer to source buffer to write  
  * @param  NumSpareAreaToRead: Number of spare area to read  
  * @retval HAL status
*/
HAL_StatusTypeDef HAL_NAND_Read_SpareArea(NAND_HandleTypeDef *hnand, NAND_AddressTypeDef *pAddress, uint8_t *pBuffer, uint32_t NumSpareAreaToRead)
{
  __IO uint32_t index   = 0; 
  uint32_t deviceaddress = 0, size = 0, num_spare_area_read = 0, addressstatus = NAND_VALID_ADDRESS;
  NAND_AddressTypeDef nandaddress;
  uint32_t addressoffset = 0;
  
  /* Process Locked */
  __HAL_LOCK(hnand);  
  
  /* Check the NAND controller state */
  if(hnand->State == HAL_NAND_STATE_BUSY)
  {
     return HAL_BUSY;
  }
  
  /* Identify the device address */
  if(hnand->Init.NandBank == FMC_NAND_BANK2)
  {
    deviceaddress = NAND_DEVICE1;
  }
  else
  {
    deviceaddress = NAND_DEVICE2;
  }
  
  /* Update the NAND controller state */
  hnand->State = HAL_NAND_STATE_BUSY;
  
  /* Save the content of pAddress as it will be modified */
  nandaddress.Block     = pAddress->Block;
  nandaddress.Page      = pAddress->Page;
  nandaddress.Zone      = pAddress->Zone;
  
  /* Spare area(s) read loop */ 
  while((NumSpareAreaToRead != 0) && (addressstatus == NAND_VALID_ADDRESS))
  {     
    /* update the buffer size */
    size = (hnand->Info.SpareAreaSize) + ((hnand->Info.SpareAreaSize) * num_spare_area_read);

    /* Get the address offset */
    addressoffset = ARRAY_ADDRESS(&nandaddress, hnand);
    
    /* Send read spare area command sequence */     
    *(__IO uint8_t *)((uint32_t)(deviceaddress | CMD_AREA)) = NAND_CMD_AREA_C;

    *(__IO uint8_t *)((uint32_t)(deviceaddress | ADDR_AREA)) = 0x00; 
    *(__IO uint8_t *)((uint32_t)(deviceaddress | ADDR_AREA)) = ADDR_1ST_CYCLE(addressoffset);
    *(__IO uint8_t *)((uint32_t)(deviceaddress | ADDR_AREA)) = ADDR_2ND_CYCLE(addressoffset);
    *(__IO uint8_t *)((uint32_t)(deviceaddress | ADDR_AREA)) = ADDR_3RD_CYCLE(addressoffset);
  
    /* for 512 and 1 GB devices, 4th cycle is required */    
    if(hnand->Info.BlockNbr >= 1024)
    {
      *(__IO uint8_t *)((uint32_t)(deviceaddress | ADDR_AREA)) = ADDR_4TH_CYCLE(addressoffset);
    } 

    *(__IO uint8_t *)((uint32_t)(deviceaddress | CMD_AREA)) = NAND_CMD_AREA_TRUE1;
    
    /* Get Data into Buffer */
    for (index = size ;index != 0; index--)
    {
      *(uint8_t *)pBuffer++ = *(uint8_t *)deviceaddress;
    }
    
    /* Increment read spare areas number */
    num_spare_area_read++;
    
    /* Decrement spare areas to read */
    NumSpareAreaToRead--;
    
    /* Increment the NAND address */
    addressstatus = HAL_NAND_Address_Inc(hnand, &nandaddress);
  }
  
  /* Update the NAND controller state */
  hnand->State = HAL_NAND_STATE_READY;
  
  /* Process unlocked */
  __HAL_UNLOCK(hnand);

  return HAL_OK;  
}
/**
  * @brief  Read Spare area(s) from NAND memory 
  * @param  hnand: pointer to a NAND_HandleTypeDef structure that contains
  *                the configuration information for NAND module.
  * @param  pAddress : pointer to NAND address structure
  * @param  pBuffer: pointer to source buffer to write  
  * @param  NumSpareAreaToRead: Number of spare area to read  
  * @retval HAL status
*/
HAL_StatusTypeDef HAL_NAND_Read_SpareArea(NAND_HandleTypeDef *hnand, NAND_AddressTypedef *pAddress, uint8_t *pBuffer, uint32_t NumSpareAreaToRead)
{
  __IO uint32_t index   = 0; 
  uint32_t deviceAddress = 0, numSpareAreaRead = 0, nandAddress = 0, addressStatus = NAND_VALID_ADDRESS;
  
  /* Process Locked */
  __HAL_LOCK(hnand);  
  
  /* Check the NAND controller state */
  if(hnand->State == HAL_NAND_STATE_BUSY)
  {
     return HAL_BUSY;
  }
  
  /* Identify the device address */
  if(hnand->Init.NandBank == FMC_NAND_BANK2)
  {
    deviceAddress = NAND_DEVICE1;
  }
  else
  {
    deviceAddress = NAND_DEVICE2;
  }
  
  /* Update the NAND controller state */
  hnand->State = HAL_NAND_STATE_BUSY;    
  
  /* Spare area(s) read loop */ 
  while((NumSpareAreaToRead != 0) && (addressStatus == NAND_VALID_ADDRESS))
  { 
    /* NAND raw address calculation */
    nandAddress = __ARRAY_ADDRESS(pAddress, hnand);     

    /* Send read spare area command sequence */     
    *(__IO uint8_t *)((uint32_t)(deviceAddress | CMD_AREA)) = NAND_CMD_AREA_C;
    
    *(__IO uint8_t *)((uint32_t)(deviceAddress | ADDR_AREA)) = 0x00; 
    *(__IO uint8_t *)((uint32_t)(deviceAddress | ADDR_AREA)) = __ADDR_1st_CYCLE(nandAddress);     
    *(__IO uint8_t *)((uint32_t)(deviceAddress | ADDR_AREA)) = __ADDR_2nd_CYCLE(nandAddress);     
    *(__IO uint8_t *)((uint32_t)(deviceAddress | ADDR_AREA)) = __ADDR_3rd_CYCLE(nandAddress);
  
    /* for 512 and 1 GB devices, 4th cycle is required */    
    if(hnand->Info.BlockNbr > 1024)
    {
      *(__IO uint8_t *)((uint32_t)(deviceAddress | ADDR_AREA)) = __ADDR_4th_CYCLE(nandAddress);
    } 

    *(__IO uint8_t *)((uint32_t)(deviceAddress | CMD_AREA)) = NAND_CMD_AREA_TRUE1;    
    
    /* Get Data into Buffer */
    for(index = 0 ; index < hnand->Info.SpareAreaSize; index++)
    {
      *(uint8_t *)pBuffer++ = *(uint8_t *)deviceAddress;
    }
    
    /* Increment read spare areas number */
    numSpareAreaRead++;
    
    /* Decrement spare areas to read */
    NumSpareAreaToRead--;
    
    /* Increment the NAND address */
    HAL_NAND_Address_Inc(hnand, pAddress);
  }
  
  /* Update the NAND controller state */
  hnand->State = HAL_NAND_STATE_READY;
  
  /* Process unlocked */
  __HAL_UNLOCK(hnand);     

  return HAL_OK;  
}