/*
 * Copyright (c) 2006-2023, RT-Thread Development Team
 *
 * SPDX-License-Identifier: Apache-2.0
 *
 * Change Logs:
 * Date           Author            Notes
 * 2022-09-28     mingmiaojing      first version
 */

#include <rtconfig.h>
#ifdef BSP_USING_NAND1
#include <board.h>
#include "drv_nand.h"
#include "drv_config.h"

#include <lpm.h>

#define NAND_PAGE_SIZE             ((uint16_t)2048)
#define NAND1_RW_TEST
#ifndef RT_FIOGETXIPADDR
    #define RT_FIOGETXIPADDR 0x52540001U
#endif

struct rt_device _hw_nand1;
NAND_HandleTypeDef hnand1;

/* FSMC initialization function */
static void rt_nand_init(void)
{
  /* USER CODE BEGIN FSMC_Init 0 */

  /* USER CODE END FSMC_Init 0 */

  FSMC_NAND_PCC_TimingTypeDef ComSpaceTiming = {0};
  FSMC_NAND_PCC_TimingTypeDef AttSpaceTiming = {0};

  /* USER CODE BEGIN FSMC_Init 1 */

  /* USER CODE END FSMC_Init 1 */

  /** Perform the NAND1 memory initialization sequence
  */
  hnand1.Instance = FSMC_NAND_DEVICE;
  /* hnand1.Init */
  hnand1.Init.NandBank = FSMC_NAND_BANK2;
  hnand1.Init.Waitfeature = FSMC_NAND_PCC_WAIT_FEATURE_ENABLE;
  hnand1.Init.MemoryDataWidth = FSMC_NAND_PCC_MEM_BUS_WIDTH_8;
  hnand1.Init.EccComputation = FSMC_NAND_ECC_ENABLE;
  hnand1.Init.ECCPageSize = FSMC_NAND_ECC_PAGE_SIZE_512BYTE;
  hnand1.Init.TCLRSetupTime = 0;
  hnand1.Init.TARSetupTime = 0;
  /* hnand1.Config */
  hnand1.Config.PageSize = NAND_PAGE_SIZE;
  hnand1.Config.SpareAreaSize = 64;
  hnand1.Config.BlockSize = 64;
  hnand1.Config.BlockNbr = 1024;
  hnand1.Config.PlaneNbr = 1;
  hnand1.Config.PlaneSize = 1024;
  hnand1.Config.ExtraCommandEnable = DISABLE;
  /* ComSpaceTiming */
  ComSpaceTiming.SetupTime = 4;
  ComSpaceTiming.WaitSetupTime = 3;
  ComSpaceTiming.HoldSetupTime = 2;
  ComSpaceTiming.HiZSetupTime = 4;
  /* AttSpaceTiming */
  AttSpaceTiming.SetupTime = 4;
  AttSpaceTiming.WaitSetupTime = 3;
  AttSpaceTiming.HoldSetupTime = 2;
  AttSpaceTiming.HiZSetupTime = 4;

  if (HAL_NAND_Init(&hnand1, &ComSpaceTiming, &AttSpaceTiming) != HAL_OK)
  {
    Error_Handler( );
  }

  /** Disconnect NADV
  */

  __HAL_AFIO_FSMCNADV_DISCONNECTED();

  /* USER CODE BEGIN FSMC_Init 2 */

  /* USER CODE END FSMC_Init 2 */
}

rt_err_t  rt_nand_open(rt_device_t dev, rt_uint16_t oflag)
{
    return RT_EOK;
}

rt_err_t  rt_nand_control(rt_device_t dev, int cmd, void *args)
{
    if(RT_DEVICE_CTRL_BLK_GETGEOME == cmd)
    {
        struct rt_device_blk_geometry *geometry = (struct rt_device_blk_geometry *)args;
        geometry->bytes_per_sector = 2048;
        geometry->sector_count = 64 * 1024;
        geometry->block_size = 2048 * 64;
        return RT_EOK;
    }
    else if(RT_FIOGETXIPADDR == cmd)
    {
        uint32_t *start_addr = (uint32_t *)args;
        *start_addr = 0;
        return RT_EOK;
    }
    else if(RT_DEVICE_CTRL_BLK_ERASE == cmd)
    {
        uint32_t *blk = (uint32_t *)args;
        NAND_AddressTypeDef Addr;
        Addr.Plane = 0x00;
        Addr.Block = *blk;
        Addr.Page = 0x00;
        HAL_NAND_Erase_Block(&hnand1,&Addr);
        return RT_EOK;
    }

    return -RT_ERROR;
}

/*pos: sector offset   size: page count*/
rt_ssize_t rt_nand_read(rt_device_t dev, rt_off_t pos, void *buffer, rt_size_t size)
{
    uint32_t page_cnt = size;
    NAND_AddressTypeDef ReadAddr;
    ReadAddr.Page = pos%64;
    ReadAddr.Plane = 0;
    ReadAddr.Block = pos/64;
    HAL_NAND_Read_Page(&hnand1, &ReadAddr, (uint8_t *)buffer, page_cnt);
    return RT_EOK;
}

rt_ssize_t rt_nand_write(rt_device_t dev, rt_off_t pos, const void *buffer, rt_size_t size)
{
    uint32_t page_cnt = size;
    NAND_AddressTypeDef WriteAddr;
    WriteAddr.Page = pos%64;
    WriteAddr.Plane = 0;
    WriteAddr.Block = pos/64;
    HAL_NAND_Write_Page(&hnand1, &WriteAddr, (uint8_t *)buffer, page_cnt);
    return RT_EOK;
}

static int stm32_nand1_init(void)
{
    NAND_IDTypeDef NAND_ID;
    rt_nand_init();
    HAL_NAND_Read_ID(&hnand1, &NAND_ID);
    rt_kprintf("Nand Flash ID = 0x%02X,0x%02X,0x%02X,0x%02X",
                NAND_ID.Maker_Id, NAND_ID.Device_Id,
                NAND_ID.Third_Id, NAND_ID.Fourth_Id);

#ifdef NAND1_RW_TEST
    uint32_t i = 0;
    static uint8_t TxBuffer [NAND_PAGE_SIZE];
    static uint8_t RxBuffer [NAND_PAGE_SIZE];
    NAND_AddressTypeDef WriteReadAddr;

    WriteReadAddr.Plane = 0x00;
    WriteReadAddr.Block = 0x00;
    WriteReadAddr.Page = 0x00;

    /* Erase the NAND first Block */
    for(i = 0; i < 64; i++)
    {
        WriteReadAddr.Block = i;
        HAL_NAND_Erase_Block(&hnand1,&WriteReadAddr);
    }

//  /* Fill the buffer to send */
//  for (i = 0; i < NAND_PAGE_SIZE; i++ )
//  {
//      TxBuffer[i] = i;
//  }
//
//  /* Write data to FMC NAND memory */
//  HAL_NAND_Write_Page(&hnand1, &WriteReadAddr, TxBuffer, 1);
//  rt_kprintf("\r\nWritten to the number:\r\n");
//  for(i = 0; i < 2048; i++)
//  {
//      rt_kprintf("0x%02X \t",TxBuffer[i]);
//  }
//  rt_kprintf("\n");
//  HAL_Delay(100);
    /* Read data from FMC NAND memory */
    WriteReadAddr.Block = 0;
    HAL_NAND_Read_Page(&hnand1, &WriteReadAddr, RxBuffer, 1);
    rt_kprintf("\r\nRead receive: \r\n");
    for(i = 0; i < 16; i++)
    {
      rt_kprintf("0x%02X \t",RxBuffer[i]);
    }
    rt_kprintf("\n");
#endif

    //_hw_nand1.ops    = &_hw_nand1;
    _hw_nand1.type     = RT_Device_Class_MTD;
    _hw_nand1.init     = RT_NULL;//rt_nand_init
    _hw_nand1.open     = rt_nand_open;
    _hw_nand1.close    = RT_NULL;
    _hw_nand1.read     = rt_nand_read;
    _hw_nand1.write    = rt_nand_write;
    _hw_nand1.control  = rt_nand_control;
    _hw_nand1.user_data  = RT_NULL;

    rt_device_register(&_hw_nand1,"nand1", RT_DEVICE_FLAG_RDWR);

    rt_kprintf("nand1 init done\n");

    lpm_init();
    lpm_dev_blk_append(&_hw_nand1);
    return 0;
}
INIT_BOARD_EXPORT(stm32_nand1_init);


#endif