#define __QSPI_DRIVER_INC__
#include "my_freeRTOS_config.h"

#include "nrf_gpio.h"
#include "nrf_delay.h"

#include "FreeRTOS_IO.h"
#include "QSPI_driver.h"
#include "nrf_drv_qspi.h"
/***********************************************************************
*                               macro define
************************************************************************
*/
//指令表
#define W25X_WriteEnable		0x06 
#define W25X_WriteDisable		0x04 
#define W25X_ReadStatusReg		0x05 
#define W25X_WriteStatusReg		0x01 
#define W25X_ReadData			0x03 
#define W25X_FastReadData		0x0B 
#define W25X_FastReadDual		0x3B 
#define W25X_PageProgram		0x02 
#define W25X_BlockErase			0xD8 
#define W25X_SectorErase		0x20 
#define W25X_ChipErase			0xC7 

#define W25X_PowerDown			0xB9 
#define W25X_ReleasePowerDown	0xAB 

/*
QSPI的指令集不包含read ID,故不用一下命令去读取
因为以下命令获取ID都需要写入无效数据，硬件中不支持
*/
#define W25X_DeviceID			0x90 
#define W25X_DeviceID_DUAL_IO   0x92
#define W25X_DeviceID_QUAD_IO   0x94

#define W25X_ManufactDeviceID	0x90 

/*
jedec包含了设备ID信息，此命令用于获取ID只有读，不用写，所以可以使用
nrfx_qspi_cinstr_xfer来获取 从这里读出来的ID会比deviceID多1
*/
#define W25X_JedecDeviceID		0x9F 


#define QSPI_STD_CMD_WRSR       0x01
#define QSPI_STD_CMD_RSTEN      0x66
#define QSPI_STD_CMD_RST        0x99

#define QSPI_TX_RX_BUF_SIZE     4096//4K

#define QSPI_MaxDelay           500
#define QSPI_INSTANCE_ID        0


#define FLASH_SECTOR_SIZE       0x1000//4K

#define CALCULATE_SECTOR_BASE_ADDR(addr)   ((addr) >> 12)
#define CALCULATE_ADDR_BASE_SECTOR(sector) ((sector) << 12)

#if QSPI_TX_RX_BUF_SIZE != FLASH_SECTOR_SIZE
#error QSPI_TX_RX_BUF_SIZE need equal to FLASH_SECTOR_SIZE!!!
#endif
/***********************************************************************
*                               my type
************************************************************************
*/
typedef struct
{
    bool dirty;
    uint32_t eunit_idx;
}qspi_work_t;

/***********************************************************************
*                               extern variable
************************************************************************
*/

/***********************************************************************
*                               extern function
************************************************************************
*/

/***********************************************************************
*                               global variable
************************************************************************
*/
 
/***********************************************************************
*                               global function
************************************************************************
*/

/***********************************************************************
*                               static variable
************************************************************************
*/
static Peripheral_Descriptor_t xQSPI_Port = NULL;

//向flash中写入的buf地址必须是四字节对齐，
//因为写入读出操作的buf都会转换为uint32_t类型，
//如果没有对齐，数据操作就会出错
static uint32_t m32_qspi_buf[QSPI_TX_RX_BUF_SIZE/4];
static uint8_t  *const pc_qspi_buf = (uint8_t *)m32_qspi_buf;


static uint32_t m_read_addr;
static uint32_t m_write_addr;

static nrfx_qspi_handler_t m_qspi_handler;

static qspi_work_t m_qspi_work;

/***********************************************************************
*                               static function
************************************************************************
*/
static void nrf52_qspi_set_mode(uint8_t trans_mode);

/***********************************************************************
*                                   end
************************************************************************
*/
/*************************************************************************   
**	function name:	 
**	description:	                  
**	input para:		 
**                  	
**	return:			                                       
**************************************************************************/
static void QSPI_driver_handler(nrfx_qspi_evt_t event, void * p_context)
{
    m_qspi_handler(event, p_context);
}
/*************************************************************************   
**	function name:	 
**	description:	                  
**	input para:		 
**                  	
**	return:			                                       
**************************************************************************/
void nrf52_QSPI_init(uint32_t cPeripheralNumber , void *handler)
{
    nrfx_err_t err;    

    cPeripheralNumber = cPeripheralNumber;

    nrfx_qspi_config_t qspi_conf = NRFX_QSPI_DEFAULT_CONFIG;

    //初始化gpio pin
    qspi_conf.pins.csn_pin = QSPI_CS_PIN_MAP;
    qspi_conf.pins.sck_pin = QSPI_CLK_PIN_MAP;
    qspi_conf.pins.io0_pin = QSPI_DIO0_PIN_MAP;
    qspi_conf.pins.io1_pin = QSPI_DIO1_PIN_MAP;
    qspi_conf.pins.io2_pin = QSPI_DIO2_PIN_MAP;
    qspi_conf.pins.io3_pin = QSPI_DIO3_PIN_MAP;

    qspi_conf.prot_if.readoc    = NRF_QSPI_READOC_READ4O;//NRF_QSPI_READOC_FASTREAD;//
    qspi_conf.prot_if.writeoc   = NRF_QSPI_WRITEOC_PP4O;//NRF_QSPI_WRITEOC_PP;//
    qspi_conf.prot_if.addrmode  = NRF_QSPI_ADDRMODE_24BIT;
    qspi_conf.prot_if.dpmconfig = false;

    qspi_conf.phy_if.sck_delay = 1;
    qspi_conf.phy_if.dpmen     = false;
    qspi_conf.phy_if.spi_mode  = NRF_QSPI_MODE_0;
    qspi_conf.phy_if.sck_freq  = NRF_QSPI_FREQ_32MDIV1;

    qspi_conf.irq_priority     = NRFX_QSPI_CONFIG_IRQ_PRIORITY;//6


    err = nrf_drv_qspi_init(&qspi_conf, QSPI_driver_handler, NULL);
    APP_ERROR_CHECK(err);

    m_qspi_handler = (nrfx_qspi_handler_t)handler;

    //刚开启使用polling mode
    nrf52_qspi_set_mode(ioctlUSE_POLLED_TX);
    
}
/*************************************************************************   
**	function name:	 
**	description:	                  
**	input para:		 
**                  	
**	return:			                                       
**************************************************************************/
static void nrf52_qspi_set_mode(uint8_t trans_mode)
{
    nrfx_err_t err;    
    if((trans_mode == ioctlUSE_POLLED_TX) || (trans_mode == ioctlUSE_POLLED_RX))
    {
        err = nrf_drv_qspi_set_handler(NULL);
        APP_ERROR_CHECK(err);
    }
    else
    {
        if(trans_mode <= ioctlUSE_ZERO_COPY_TXRX)
        {
            err = nrf_drv_qspi_set_handler(QSPI_driver_handler);
            APP_ERROR_CHECK(err);
    
            FreeRTOS_ioctl(xQSPI_Port, trans_mode, NULL);
        }
    }
}
/*************************************************************************   
**	function name:	 
**	description:	                  
**	input para:		 
**                  	
**	return:			                                       
**************************************************************************/
void nrf52_qspi_int_config(const uint32_t ulPeripheralNumber, bool enable)
{
    //do nothing...
#if 0
//    nrfx_err_t err = NRFX_SUCCESS;

//    if(ulPeripheralNumber == QSPI_INSTANCE_ID)
//    {
//        err = nrf_drv_qspi_int_enable_ctrl(enable);
//        APP_ERROR_CHECK(err);
//    }
#error must not do this...
#endif
}
/*************************************************************************   
**	function name:	 
**	description:	                  
**	input para:		 
**                  	
**	return:			                                       
**************************************************************************/
void nrf52_qspi_set_frequency(const uint32_t ulPeripheralNumber, uint32_t freq)
{
    nrf_drv_qspi_set_frequency(freq);
}
/*************************************************************************   
**	function name:	 
**	description:	                  
**	input para:		 
**                  	
**	return:			                                       
**************************************************************************/
void nrf52_qspi_set_int_priority(const uint32_t ulPeripheralNumber, uint32_t priority)
{
    if(ulPeripheralNumber == QSPI_INSTANCE_ID)
    {
    }
}

/*************************************************************************   
**	function name:	 
**	description:	                  
**	input para:		 
**                  	
**	return:			                                       
**************************************************************************/
void nrf52_qspi_zero_copy_TX(const uint32_t ulPeripheralNumber, const uint8_t *p_tx_data, uint32_t length)
{
    nrfx_err_t err;

    //长度必须是4的倍数
	configASSERT( (length&0x03) == 0);
    //buf地址必须四字节对齐
	configASSERT( ((uint32_t)p_tx_data&0x03) == 0);
    
    err = nrfx_qspi_write(p_tx_data, length, m_write_addr);
    APP_ERROR_CHECK(err);
//	#ifdef __QSPI_DRIVRE_DEBUG__
//    printf("write err:%#x, %#x, %d\r\n", err, m_write_addr, length);
//    #endif
}
/*************************************************************************   
**	function name:	 
**	description:	                  
**	input para:		 
**                  	
**	return:			                                       
**************************************************************************/
void nrf52_qspi_zero_copy_RX(const uint32_t ulPeripheralNumber, uint8_t *p_rx_data, uint32_t length)
{
    nrfx_err_t err;

    //长度必须是4的倍数
	configASSERT( (length&0x03) == 0);
    //buf地址必须四字节对齐
	configASSERT(((uint32_t)p_rx_data&0x03) == 0);
    
    err = nrfx_qspi_read(p_rx_data, length, m_read_addr);
    APP_ERROR_CHECK(err);
}


/*************************************************************************   
**	function name:	 
**	description:此函数发送唤醒命令，执行为polled 模式	                  
**	input para:		 
**                  	
**	return:			                                       
**************************************************************************/
static void flash_wakeup(void)
{
    nrfx_err_t err;
    err = nrf_drv_qspi_cinstr_quick_send(W25X_ReleasePowerDown, NRF_QSPI_CINSTR_LEN_1B, NULL);
//    #ifdef __QSPI_DRIVRE_DEBUG__
//    printf("110err:%#x\r\n", err);
//    #endif

    APP_ERROR_CHECK(err);
}
/*************************************************************************   
**	function name:	 
**	description:此函数发送休眠命令，执行为polled 模式	                  
**	input para:		 
**                  	
**	return:			                                       
**************************************************************************/
static void flash_sleep(void)
{
    nrfx_err_t err;
    err= nrf_drv_qspi_cinstr_quick_send(W25X_PowerDown, NRF_QSPI_CINSTR_LEN_1B, NULL);
    APP_ERROR_CHECK(err);
}

/*************************************************************************   
**	function name:	 
**	description:	                  
**	input para:		 
**                  	
**	return:			                                       
**************************************************************************/
static uint16_t read_flash_id(void)
{
    nrfx_err_t err;
    uint16_t   jedec_id;

    nrf_qspi_cinstr_conf_t cinstr_cfg = 
    {
        .opcode    = W25X_JedecDeviceID,
        .length    = NRF_QSPI_CINSTR_LEN_4B,
        .io2_level = true,
        .io3_level = true,
        .wipwait   = true,
        .wren      = true
    };
    
    flash_wakeup();    
    err = nrfx_qspi_cinstr_xfer(&cinstr_cfg, NULL, pc_qspi_buf);
//    #ifdef __QSPI_DRIVRE_DEBUG__
//    printf("111err:%#x\r\n", err);
//    #endif
    APP_ERROR_CHECK(err);
    flash_sleep();

    jedec_id = (pc_qspi_buf[0] <<8) | pc_qspi_buf[2];

    #ifdef __QSPI_DRIVRE_DEBUG__
    for(uint8_t i=0; i<6; i++)
        printf("QSPI read id:%#x\r\n", pc_qspi_buf[i]);
    #endif

    return (jedec_id-1);
}

/*************************************************************************   
**	function name:	 
**	description:	例程中有，不知道啥意思                  
**	input para:		 
**                  	
**	return:			                                       
**************************************************************************/
static void configure_memory(void)
{
    uint8_t temporary = 0x40;
    uint32_t err_code;
    nrf_qspi_cinstr_conf_t cinstr_cfg = {
        .opcode    = QSPI_STD_CMD_RSTEN,
        .length    = NRF_QSPI_CINSTR_LEN_1B,
        .io2_level = true,
        .io3_level = true,
        .wipwait   = true,
        .wren      = true
    };

    // Send reset enable
    err_code = nrf_drv_qspi_cinstr_xfer(&cinstr_cfg, NULL, NULL);
    APP_ERROR_CHECK(err_code);

    // Send reset command
    cinstr_cfg.opcode = QSPI_STD_CMD_RST;
    err_code = nrf_drv_qspi_cinstr_xfer(&cinstr_cfg, NULL, NULL);
    APP_ERROR_CHECK(err_code);

    // Switch to qspi mode
    cinstr_cfg.opcode = QSPI_STD_CMD_WRSR;
    cinstr_cfg.length = NRF_QSPI_CINSTR_LEN_2B;
    err_code = nrf_drv_qspi_cinstr_xfer(&cinstr_cfg, &temporary, NULL);
    APP_ERROR_CHECK(err_code);
}
/*************************************************************************   
**	function name:	 
**	description:	                  
**	input para:		 
**                  	
**	return:			                                       
**************************************************************************/
void QSPI_driver_init(void * xQSPI_PortIn)
{    
	configASSERT( xQSPI_PortIn );
    
    if(xQSPI_Port == NULL)
    {
        xQSPI_Port = (Peripheral_Descriptor_t*)xQSPI_PortIn;
    }

    configure_memory();    

    //必须将读ID放在最前面
    W25QXX.W25QXX_TYPE = read_flash_id();

    W25QXX.W25QXX_error = false;
    
    switch(W25QXX.W25QXX_TYPE)
    {
        case W25Q80:
            
            W25Q80_CAPACITY(W25QXX.capacity.physical_sector_size, 
                            W25QXX.capacity.physical_sector_count,
                            W25QXX.capacity.physical_block_count);
            break;

        case W25Q16:
            
            W25Q16_CAPACITY(W25QXX.capacity.physical_sector_size, 
                            W25QXX.capacity.physical_sector_count,
                            W25QXX.capacity.physical_block_count);
            break;

        case W25Q32:
            
            W25Q32_CAPACITY(W25QXX.capacity.physical_sector_size, 
                            W25QXX.capacity.physical_sector_count,
                            W25QXX.capacity.physical_block_count);
            break;

        case W25Q64:
            
            W25Q64_CAPACITY(W25QXX.capacity.physical_sector_size, 
                            W25QXX.capacity.physical_sector_count,
                            W25QXX.capacity.physical_block_count);
            break;

        case W25Q128:
            
            W25Q128_CAPACITY(W25QXX.capacity.physical_sector_size, 
                             W25QXX.capacity.physical_sector_count,
                             W25QXX.capacity.physical_block_count);
            break;

        default:
            
            W25QXX.W25QXX_error = true;
            break;
    }
    
    if(W25QXX.W25QXX_error)
    {
        #ifdef __QSPI_DRIVRE_DEBUG__
        printf("external flash is error!!!\r\n");
        #endif
        return;
    }

    m_qspi_work.dirty = true;
    m_qspi_work.eunit_idx = 0xFFFFFFFF;
    
    
    nrf52_qspi_set_mode(ioctlUSE_ZERO_COPY_TXRX);
    
#if 0
    nrf_gpio_cfg_output(I2C_SDA_PIN_MAP);
    nrf_gpio_pin_clear(I2C_SDA_PIN_MAP);


//    for(uint8_t i=0; i<5; i++)
    {
        uint8_t buf[10] = {1,2,3,4,5,6,7,8,9,10};

        for(uint16_t  i=0; i<10; i++)
        {
            buf[i] = i;
        }
        QSPI_flash_write(buf, 10,  4090);
		
//		#ifdef __QSPI_DRIVRE_DEBUG__
//        printf("QSPI write:");
//        for(uint16_t i=0; i<10; i++)
//        {
//            printf("%#x ", buf[i]);
//        }
//        printf("\r\n");
//        #endif
    
        QSPI_flash_read(buf, 10, 4090);

        
        #ifdef __QSPI_DRIVRE_DEBUG__
        printf("buf a:%#x, m_test_addr:%#x\r\n", buf, buf);
        printf("QSPI read:");
        for(uint16_t i=0; i<10; i++)
        {
            if((i & 0x0f) == 0)
                printf("\r\n");
            
            printf("%#4x\t", buf[i]);
        }
        printf("\r\n");
        #endif
    }    
#endif
}

/*************************************************************************   
**	function name:	 
**	description:	                  
**	input para:		 
**                  	
**	return:			                                       
**************************************************************************/
void QSPI_flash_write(const void *p_tx_buf, uint32_t size, uint32_t addr)
{
    const uint8_t *p_buffer;
    uint32_t i, secpos, secoff, secremain;

    if(W25QXX.W25QXX_error)
    {
        return;
    }

    p_buffer = (const uint8_t *)p_tx_buf;

    secpos = addr >> 12;//扇区地址  
    secoff = addr & (FLASH_SECTOR_SIZE-1);//在扇区内的偏移
    secremain = FLASH_SECTOR_SIZE - secoff;//扇区剩余空间大小  
    
 	if(size <= secremain)
    {
        secremain = size;//没有跨扇区
    }
    
    if(FreeRTOS_ioctl(xQSPI_Port, ioctlOBTAIN_WRITE_MUTEX, (void *)QSPI_MaxDelay) == pdTRUE)
    {
        flash_wakeup();
        
        while(1) 
        {	
            if(m_qspi_work.dirty || (m_qspi_work.eunit_idx != secpos))
            {
                if(FreeRTOS_ioctl(xQSPI_Port, ioctlBATAIN_WRITE_BINARY_SEMPHR,(void *)QSPI_MaxDelay) == pdTRUE)
                {
                    m_qspi_work.eunit_idx = secpos;
                    m_qspi_work.dirty     = false;
                    
                    m_read_addr = CALCULATE_ADDR_BASE_SECTOR(secpos);
                    FreeRTOS_read(xQSPI_Port, pc_qspi_buf, QSPI_TX_RX_BUF_SIZE);                
                    //等待数据读取完毕
                    FreeRTOS_ioctl(xQSPI_Port, ioctlWAITBINARY_PREVIOUS_WRITE_COMPLETE, (void *)QSPI_MaxDelay); 
                }
            }
            
            for(i=0;i<secremain;i++)//校验数据
            {
                if(pc_qspi_buf[secoff+i] != p_buffer[i])
                {
                    break;//需要擦除      
                }
            }

            //需要写入的数据和现目前所存的数据完全相同，则不用写入            
            if(i != secremain)
            {
                for(i=0;i<secremain;i++)//校验数据
                {
                    if(pc_qspi_buf[secoff+i] != 0xFF)
                    {
                        break;//需要擦除      
                    }                        
                }
                if(i<secremain)//需要擦除
                {
                    nrfx_qspi_erase(NRF_QSPI_ERASE_LEN_4KB, CALCULATE_ADDR_BASE_SECTOR(secpos)); //4K对齐
                    while(nrfx_qspi_mem_busy_check() != NRFX_SUCCESS)
                    {
                        vTaskDelay(40);//抓波形大概是(31-34ms之间)
                    }                    
                }
                
                for(i=0;i<secremain;i++)	   //复制
                {
                    pc_qspi_buf[i+secoff] = p_buffer[i];	  
                }                
                
                if(FreeRTOS_ioctl(xQSPI_Port, ioctlBATAIN_WRITE_BINARY_SEMPHR,(void *)QSPI_MaxDelay) == pdTRUE)
                {
                    m_write_addr = m_read_addr;
                    FreeRTOS_write(xQSPI_Port, pc_qspi_buf, QSPI_TX_RX_BUF_SIZE);                 
                    FreeRTOS_ioctl(xQSPI_Port, ioctlWAITBINARY_PREVIOUS_WRITE_COMPLETE, (void *)QSPI_MaxDelay);                     
                    
                    //等待传输完毕，然后等待flash操作执行完毕(1-2ms)
                    while(nrfx_qspi_mem_busy_check() != NRFX_SUCCESS);
                }
            }            
            
            if(size == secremain)
            {
                break;//写入结束了
            }
            else//写入未结束
            {
                secpos++;//扇区地址增1
                secoff = 0;//偏移位置为0  
                
                p_buffer += secremain; //指针偏移
                addr     += secremain; //写地址偏移	   
                size     -= secremain; //字节数递减
                
                if(size > FLASH_SECTOR_SIZE)
                {
                    secremain = FLASH_SECTOR_SIZE; //下一个扇区还是写不完
                }
                else 
                {
                    secremain = size;           //下一个扇区可以写完了
                }
            }	 
        }
            
        flash_sleep();
        FreeRTOS_ioctl(xQSPI_Port, ioctlWAIT_PREVIOUS_WRITE_COMPLETE, (void *)QSPI_MaxDelay);
    }
}

/*************************************************************************   
**	function name:	 
**	description:	                  
**	input para:		 
**                  	
**	return:			                                       
**************************************************************************/
void QSPI_flash_read(void *p_rx_buf, uint32_t size, uint32_t addr)
{
    uint8_t  *p_buffer;
    uint32_t read_size, copy_size, rx_offset;

    if(W25QXX.W25QXX_error)
    {
        return;
    }

    p_buffer = (uint8_t *)p_rx_buf;
    
    if(FreeRTOS_ioctl(xQSPI_Port, ioctlOBTAIN_WRITE_MUTEX, (void *)QSPI_MaxDelay) == pdTRUE)
    {
        flash_wakeup(); 

        if((((uint32_t)p_rx_buf & 0x03) == 0) && ((size & 0x03) == 0))
        {
            if(FreeRTOS_ioctl(xQSPI_Port, ioctlBATAIN_WRITE_BINARY_SEMPHR, (void *)QSPI_MaxDelay) == pdTRUE)
            {
                m_read_addr = addr;                
                FreeRTOS_read(xQSPI_Port, p_rx_buf, size);
                FreeRTOS_ioctl(xQSPI_Port, ioctlWAITBINARY_PREVIOUS_WRITE_COMPLETE, (void *)QSPI_MaxDelay); 
            }
        }
        else
        {
            rx_offset   = 0;
            m_qspi_work.dirty = true;
    
            while(size > 0)
            {
                if(FreeRTOS_ioctl(xQSPI_Port, ioctlBATAIN_WRITE_BINARY_SEMPHR, (void *)QSPI_MaxDelay) == pdTRUE)
                {
                    if(size > QSPI_TX_RX_BUF_SIZE)
                    {
                        read_size = QSPI_TX_RX_BUF_SIZE;
                        copy_size = QSPI_TX_RX_BUF_SIZE;
                        size     -= QSPI_TX_RX_BUF_SIZE;
                    }
                    else
                    {
                        read_size = ALIGN(size, 4);//4字节对齐
                        copy_size = size;
                        size      = 0;
                    }
                
                    m_read_addr = addr;                
                    FreeRTOS_read(xQSPI_Port, pc_qspi_buf, read_size);
                    FreeRTOS_ioctl(xQSPI_Port, ioctlWAITBINARY_PREVIOUS_WRITE_COMPLETE, (void *)QSPI_MaxDelay); 
    
                    memcpy(&p_buffer[rx_offset], pc_qspi_buf, copy_size);
                    rx_offset += copy_size;
                    addr      += read_size;
                }
            }
        }           
                
        flash_sleep();        
        FreeRTOS_ioctl(xQSPI_Port, ioctlWAIT_PREVIOUS_WRITE_COMPLETE, (void *)QSPI_MaxDelay);
    }
}


