/*
 * @Description: 存储模块-bsp层-型号w25x
 * @Author: yangsw
 * @Date: 2021-05-20 18:35:04
 * @LastEditTime: 2023-05-25 11:43:45
 * @LastEditors: Lida
 * @Reference:
 */

#include "sr_bsp_w25x.h"

#include "sr_log.h"

    

#define Flash_Select()         SR_HalGpioWrite(FLASH_SPI_CS_ID, SR_GPIO_LOW)
#define Flash_Not_Select()     SR_HalGpioWrite(FLASH_SPI_CS_ID, SR_GPIO_HIGH)
#define Flash_Spi_SCK_Low()    SR_HalGpioWrite(FLASH_SPI_SCK_ID, SR_GPIO_LOW)
#define Flash_Spi_SCK_High()   SR_HalGpioWrite(FLASH_SPI_SCK_ID, SR_GPIO_HIGH)
#define Flash_Spi_MOSI_Low()   SR_HalGpioWrite(FLASH_SPI_MOSI_ID, SR_GPIO_LOW)
#define Flash_Spi_MOSI_High()  SR_HalGpioWrite(FLASH_SPI_MOSI_ID, SR_GPIO_HIGH)
#define Flash_Spi_MISO_Read()  SR_HalGpioRead(FLASH_SPI_MISO_ID)


#define Flash_Delay() \
    ;                 \
    {                 \
        __NOP();      \
        __NOP();      \
        __NOP();      \
        __NOP();      \
        __NOP();      \
        __NOP();      \
        __NOP();      \
        __NOP();      \
        __NOP();      \
        __NOP();      \
        __NOP();      \
        __NOP();      \
        __NOP();      \
        __NOP();      \
        __NOP();      \
        __NOP();      \
    }

static void Flash_Write_Enable(void);


/**
 * @brief: 存储模块初始化
 * @param  {void}
 * @return {void}
 * @note:
 * @see:
 */
int Bsp_Flash_Init(void)
{
    SR_GPIO_CFG_T gpioCfg = {0};

    //PWR_EN
    gpioCfg.gpioMode = SR_GPIO_OUTPUT;      // 推挽输出
    gpioCfg.altFun =  SR_GPIO_NOAF;         // 无复用
    gpioCfg.pull = SR_GPIO_NP;				// 无上下拉
    gpioCfg.initLvl = SR_GPIO_HIGH;			// 高电平
    SR_HalGpioInit(FLASH_PWR_EN_ID, &gpioCfg);  // 初始化

    //MOSI
    SR_HalGpioInit(FLASH_SPI_MOSI_ID, &gpioCfg);  // 初始化

    //CS
    SR_HalGpioInit(FLASH_SPI_CS_ID, &gpioCfg);  // 初始化

    //SCK
    gpioCfg.initLvl = SR_GPIO_LOW;			// 低电平
    SR_HalGpioInit(FLASH_SPI_SCK_ID, &gpioCfg);  // 初始化


    //MISO
    gpioCfg.gpioMode = SR_GPIO_INPUT;      // 输入
    gpioCfg.pull = SR_GPIO_PU;			   // 上拉
    SR_HalGpioInit(FLASH_SPI_MISO_ID, &gpioCfg);  // 初始化

    //SR_HalGpioWrite(FLASH_SPI_CS_ID, SR_GPIO_LOW);
    //SR_HalGpioWrite(FLASH_SPI_MOSI_ID, SR_GPIO_HIGH);
    //SR_HalGpioWrite(FLASH_SPI_SCK_ID, SR_GPIO_LOW);
    //Flash_Not_Select();
    
    Bsp_Flash_Power_On();  //使能电源
    //读取芯片ID，测试是否初始化成功
    SR_LOG(DEBUG, "Flash ID: %X", Bsp_Flash_Read_Id());

    return 0;
}
SR_INIT_BSP_EXPORT(Bsp_Flash_Init, SR_PRIORITY_1);

/**
 * @brief: SPI读写一个字节数据
 * @return {uint8_t} 读取的数据
 * @note:  CPOL = 0, CPHA = 0, MSB first
 * @see:
 * @param {uint8_t} u8Byte 待写数据
 */
static uint8_t Flash_Read_write_Byte(uint8_t u8Byte)
{
    uint8_t i, u8ReadData;
    for (i = 0; i < 8; i++)
    {
        if (u8Byte & 0x80)
            Flash_Spi_MOSI_High();
        else
            Flash_Spi_MOSI_Low();
        u8Byte <<= 1;
        Flash_Delay();
        Flash_Spi_SCK_High();
        u8ReadData <<= 1;
        if (Flash_Spi_MISO_Read() != RESET)
        {
            u8ReadData++;
        }
        Flash_Delay();
        Flash_Spi_SCK_Low();
        Flash_Delay();
    }
    return u8ReadData;
}

/**
 * @brief: 读取Flash的ID
 * @return {uint16_t}
 */
 uint16_t Bsp_Flash_Read_Id(void)
{
    uint8_t pu8Id[2]={0};

    Flash_Select();

    Flash_Read_write_Byte(0x90);
    Flash_Read_write_Byte(0x00);
    Flash_Read_write_Byte(0x00);
    Flash_Read_write_Byte(0x00);
    pu8Id[0] = Flash_Read_write_Byte(0xFF);
    pu8Id[1] = Flash_Read_write_Byte(0xFF);

    Flash_Not_Select();

    return ((pu8Id[0] << 8)| pu8Id[1]);

}


/**
 * @brief: flash下电
 * @param  {void}
 * @return {void}
 * @note:
 * @see:
 */
void Bsp_Flash_Powerdown(void)
{
    Flash_Select();
    Flash_Read_write_Byte(W25X_PowerDown);
    Flash_Not_Select();
}

/**
 * @brief: flash释放下电
 * @param  {void}
 * @return {void}
 * @note:
 * @see:
 */
void Bsp_Flash_Release_Powerdown(void)
{
    Flash_Select();
    Flash_Read_write_Byte(W25X_ReleasePowerDown);
    Flash_Not_Select();
}

/**
 * @brief: 等待flash空闲
 * @param  {void}
 * @return {void}
 * @note:
 * @see:
 */
static void Flash_Wait_Busy(void)
{
    uint8_t state = 0x00;
    uint32_t errCnt = 0;
    Flash_Not_Select();
    Flash_Select();
    Flash_Read_write_Byte(W25X_ReadStatusReg);
    state = Flash_Read_write_Byte(0xFF);
    while ((state & 0x01) != 0)
    {

        if (errCnt > 1000)
        { // 一直读到忙状态超过一定次数，要退出，防止卡死在这里
            Flash_Not_Select();
            return;
        }
        Flash_Read_write_Byte(W25X_ReadStatusReg);
        state = Flash_Read_write_Byte(0xFF);
        errCnt++;
    }
    Flash_Not_Select();
}

/**
 * @brief: flash写使能
 * @param  {void}
 * @return {void}
 * @note:
 * @see:
 */
static void Flash_Write_Enable(void)
{
    Flash_Select();
    Flash_Read_write_Byte(W25X_WriteEnable);
    Flash_Not_Select();
}



/**
 * @brief: flash写失能
 * @param  {void}
 * @return {void}
 * @note:
 * @see:
 */
static void Flash_Write_Disable(void)
{
    Flash_Select();
    Flash_Read_write_Byte(W25X_WriteDisable);
    Flash_Not_Select();
}


#if  0
/**
 * @brief: flash读数据
 * @return {void}
 * @note:
 * @see:
 * @param {uint8_t} *out_buff 读取数据存放地址指针
 * @param {uint32_t} read_addr 待读取数据的flash地址
 * @param {uint16_t} read_num 待读取数据字节数
 */
void Bsp_Flash_Read(uint8_t *out_buff, uint32_t read_addr, uint16_t read_num)
{
    uint16_t i = 0;
    Flash_Select();
    Flash_Read_write_Byte(W25X_ReadData);
    Flash_Read_write_Byte((uint8_t)((read_addr) >> 16));
    Flash_Read_write_Byte((uint8_t)((read_addr) >> 8));
    Flash_Read_write_Byte((uint8_t)read_addr);
    for (i = 0; i < read_num; i++)
    {
        out_buff[i] = Flash_Read_write_Byte(0xFF);
    }
    Flash_Not_Select();
}
#endif


/**
 * @brief: 擦除整个芯片
 * @param  {void}
 * @return {void}
 * @note: 整片擦除时间，W25X16:25s，W25X32:40s，W25X64:40s，等待时间超长
 * @see:
 */
void Bsp_Flash_Erase_Chip(void)
{
    Flash_Write_Enable(); // SET WEL
    Flash_Wait_Busy();
    Flash_Select();                        // 片选使能
    Flash_Read_write_Byte(W25X_ChipErase); // 发送擦除命令
    Flash_Not_Select();                    // 片选禁能
    Flash_Wait_Busy();                     // 等待芯片擦除结束
}


#if  0
/**
 * @brief: 擦除一个扇区
 * @return {void}
 * @note: 擦除一个扇区的最少时间：150ms
 * @see:
 * @param {uint16_t} sector_addr 扇区地址 0~511 for w25x16
 */
void Bsp_Flash_Erase_Sector(uint16_t sector_addr)
{
    uint32_t dst_addr;
    dst_addr = FLASH_SECTOR_SIZE * sector_addr;
    Flash_Write_Enable(); // SET WEL
    Flash_Wait_Busy();
    Flash_Select();                                     // 片选使能
    Flash_Read_write_Byte(W25X_SectorErase);            // 发送扇区擦除指令
    Flash_Read_write_Byte((uint8_t)((dst_addr) >> 16)); // 发送24Bit地址
    Flash_Read_write_Byte((uint8_t)((dst_addr) >> 8));
    Flash_Read_write_Byte((uint8_t)dst_addr);
    Flash_Not_Select(); // 取消片选
    Flash_Wait_Busy();  // 等待擦除完成
}
#endif

void Bsp_Flash_Erase_One_Sector(uint32_t dst_addr)
{
    Flash_Write_Enable(); // SET WEL
    Flash_Wait_Busy();
    Flash_Select();                                     // 片选使能
    Flash_Read_write_Byte(W25X_SectorErase);            // 发送扇区擦除指令
    Flash_Read_write_Byte((uint8_t)((dst_addr) >> 16)); // 发送24Bit地址
    Flash_Read_write_Byte((uint8_t)((dst_addr) >> 8));
    Flash_Read_write_Byte((uint8_t)dst_addr);
    Flash_Not_Select(); // 取消片选
    Flash_Wait_Busy();  // 等待擦除完成
}

/**
 * @brief: flash页写数据
 * @param  {*}
 * @return {*}
 * @note: 在指定地址开始写入最大256字节的数据
 * @see:
 * @param {uint8_t*} data 数据存储区
 * @param {uint32_t} write_addr 开始写入的地址(24bit)
 * @param {uint16_t} write_num 要写入的字节数（最大256），该数不应该超过该页的剩余字节数
 */
static void Flash_Write_Page(uint8_t *data, uint32_t write_addr, uint16_t write_num)
{
    uint16_t i;
    Flash_Write_Enable();                                 // SET WEL
    Flash_Select();                                       // 片选使能
    Flash_Read_write_Byte(W25X_PageProgram);              // 发送写页命令
    Flash_Read_write_Byte((uint8_t)((write_addr) >> 16)); // 发送24bit地址
    Flash_Read_write_Byte((uint8_t)((write_addr) >> 8));
    Flash_Read_write_Byte((uint8_t)write_addr);
    for (i = 0; i < write_num; i++)
    {
        Flash_Read_write_Byte(data[i]);
    } // 循环写入数据
    Flash_Not_Select(); // 片选禁能
    Flash_Wait_Busy();  // 等待写入结束
}

/**
 * @brief: 无检验写SPI FLASH
 * @return {void}
 * @note: 必须确保所写的地址范围内数据全部为0xFF，否则在非0xFF处写入的数据将失败
 * 具有自动换页功能
 * 在指定地址开始写入指定长度的数据，但要确保地址不越界
 * @see:
 * @param {uint8_t*} data 数据存储区
 * @param {uint32_t} write_addr 开始写入的地址(24bit)
 * @param {uint16_t} write_num 要写入的字节数(最大65535)
 */
void Bsp_Flash_Write_No_Check(uint8_t *data, uint32_t write_addr, uint16_t write_num)
{
    uint16_t pageremain;
    pageremain = 256 - write_addr % 256; // 单页剩余的字节数
    if (write_num <= pageremain)
        pageremain = write_num; // 不大于256个字节
    while (1)
    {
        Flash_Write_Page(data, write_addr, pageremain);
        if (write_num == pageremain)
            break; // 写入结束了
        else       // NumByteToWrite>pageremain
        {
            data += pageremain;
            write_addr += pageremain;

            write_num -= pageremain; // 减去已经写入了的字节数
            if (write_num > 256)
                pageremain = 256; // 一次可以写入256个字节
            else
                pageremain = write_num; // 不够256个字节了
        }
    };
}



#if  0
/**
 * @brief: flash写数据
 * @return {void}
 * @note: 写数据前会擦掉整个扇区的数据，需要自己保存需要的数据
 * @see:
 * @param {uint8_t*} data 数据存储区
 * @param {uint32_t} write_addr 开始写入的地址(24bit)
 * @param {uint16_t} write_num 要写入的字节数(最大65535)
 */
void Bsp_Flash_Write(uint8_t *data, uint32_t write_addr, uint16_t write_num)
{
    uint32_t secpos;
    uint16_t secoff;
    uint16_t secremain;
    uint32_t u32Addr;
    u32Addr = write_addr;
    secpos = u32Addr / FLASH_SECTOR_SIZE;   // 扇区地址 0~511 for w25x16
    secoff = u32Addr % FLASH_SECTOR_SIZE;   // 在扇区内的偏移
    secremain = FLASH_SECTOR_SIZE - secoff; // 扇区剩余空间大小
    if (write_num <= secremain)
        secremain = write_num; // 不大于4096个字节
    while (1)
    {
        Bsp_Flash_Erase_Sector(secpos);
        Bsp_Flash_Write_No_Check(data, u32Addr, secremain);
        if (write_num == secremain)
            break; // 写入结束
        else       // 写入未结束
        {
            secpos++;               // 扇区地址增1
            secoff = 0;             // 偏移地址为0
            data += secremain;      // 指针偏移
            u32Addr += secremain;   // 写地址偏移
            write_num -= secremain; // 字节数递减
            if (write_num > FLASH_SECTOR_SIZE)
                secremain = FLASH_SECTOR_SIZE; // 下一个扇区还是写不完
            else
                secremain = write_num; // 下一个扇区可以写完
        }
    }
}
#endif


/**************************FAL 注册***************************************/

#include  "fal_part.h"

#define   BSP_FAL_SPI_FLASH_NAME    "w25x"
#define   BSP_FLASH_PHYADDR_START   0
#define   BSP_FLASH_SPACE_SIZE      512*1024
#define   BSP_FLASH_SECTOR_SIZE     4096

int BspFlashRead(uint32_t addr,uint8_t *buff,  uint32_t size)
{
    uint16_t i = 0;
    Flash_Select();
    Flash_Read_write_Byte(W25X_ReadData);
    Flash_Read_write_Byte((uint8_t)((addr) >> 16));
    Flash_Read_write_Byte((uint8_t)((addr) >> 8));
    Flash_Read_write_Byte((uint8_t)addr);
    for (i = 0; i < size; i++)
    {
        buff[i] = Flash_Read_write_Byte(0xFF);
    }
    Flash_Not_Select();

    return size;
}
int BspFlashErase(uint32_t addr, uint16_t num)
{
    uint16_t i;
    uint32_t dstAddr = addr;
    for(i=0;i<num;i++)
    {
        Bsp_Flash_Erase_One_Sector(dstAddr);
        dstAddr+=BSP_FLASH_SECTOR_SIZE;
    }

    return num;
}


static uint8_t tempBuf[BSP_FLASH_SECTOR_SIZE];
int BspFlashWrite(uint32_t offset, uint8_t *buf, uint32_t size)
{ 
	uint32_t secpos;
	uint16_t i,secoff,secremain;    
	int ret = 0;	
	uint32_t tempSize =size;

	//SR_ASSERT((buf!=SR_NULL)&&(size>0)&&(offset<BS_FLASH_WR_ADDR_MAX)&&(offset+size<BS_FLASH_WR_ADDR_MAX));
	
 	secpos=offset/BSP_FLASH_SECTOR_SIZE;//扇区地址  
	secoff=offset%BSP_FLASH_SECTOR_SIZE;//在扇区内的偏移
	secremain=BSP_FLASH_SECTOR_SIZE-secoff;//扇区剩余空间大小   
 	//printf("ad:%X,nb:%X\r\n",offset,size);//测试用
	
 	if(size<=secremain)secremain=size;//不大于BSP_FLASH_SECTOR_SIZE个字节
	while(1) 
	{	

		BspFlashRead(secpos*BSP_FLASH_SECTOR_SIZE, tempBuf, BSP_FLASH_SECTOR_SIZE); //读出整个扇区的内容
		for(i=0;i<secremain;i++)//校验数据
		{
			if(tempBuf[secoff+i]!=0XFF)break;//需要擦除  	  
		}
		if(i<secremain)//需要擦除
		{
			ret = BspFlashErase(secpos*BSP_FLASH_SECTOR_SIZE, 1);  //擦除这个扇区
            
			if(ret!=1) return SR_FAIL;
			
			for(i=0;i<secremain;i++)	   //复制
			{
				tempBuf[i+secoff]=buf[i];	  
			}

			
            Bsp_Flash_Write_No_Check(tempBuf, secpos*BSP_FLASH_SECTOR_SIZE, BSP_FLASH_SECTOR_SIZE);//写入整个扇区		

		}
		else {
		
		   Bsp_Flash_Write_No_Check(buf,offset,secremain);//写已经擦除了的,直接写入扇区剩余区间. 	
			
		}
		if(size==secremain)  break;//写入结束了
		else//写入未结束
		{
			secpos++;//扇区地址增1
			secoff=0;//偏移位置为0 	 

		    buf+=secremain;    //指针偏移
			offset+=secremain; //写地址偏移	   
		    size-=secremain;	 //字节数递减
			if(size>BSP_FLASH_SECTOR_SIZE)secremain=BSP_FLASH_SECTOR_SIZE;	//下一个扇区还是写不完
			else secremain=size;			//下一个扇区可以写完了
		}	 
	};	

	return tempSize;
}

static int _FalRead(fal_flash_t *flash, uint32_t offset, uint8_t *buf, uint32_t size)
{
  uint32_t addr = flash->addr + offset;

  return  BspFlashRead(addr, buf, size);
  
}
static int _FalWrite(fal_flash_t *flash, uint32_t offset, uint8_t *buf, uint32_t size)
{
  uint32_t addr = flash->addr + offset;

  return  BspFlashWrite(addr, buf, size);

}
static int _FalEraseBlock(fal_flash_t *flash, uint32_t offset, uint32_t num)
{

   uint32_t addr = flash->addr + offset;

   return BspFlashErase(addr, num);
}



fal_flash_t hzOnflash;

static int BspFalOnflashInit(void)
{


    hzOnflash.name=BSP_FAL_SPI_FLASH_NAME;

    hzOnflash.addr = BSP_FLASH_PHYADDR_START;
    hzOnflash.len   = BSP_FLASH_SPACE_SIZE;
    hzOnflash.blk_size = BSP_FLASH_SECTOR_SIZE;
    //hzOnflash.page_size  = ;

    hzOnflash.ops.read  = _FalRead ;
    hzOnflash.ops.write  = _FalWrite;
    hzOnflash.ops.erase_block = _FalEraseBlock;

    //hzOnflash.priv = SR_NULL;

    return fal_flash_register(&hzOnflash);
}
SR_INIT_BSP_EXPORT(BspFalOnflashInit,SR_PRIORITY_1);



#if  1
#include "sr_shell.h"

//测试
static int w25Test(int argc, char *argv[])
{
  uint16_t id;
  id = Bsp_Flash_Read_Id();
  SR_LOG(DEBUG,"spi flash: id 0x%x",id);

  return 0;
}
SR_SHELL_CMD_EXPORT(w25Test, w25Test);
#endif
