/*
 * ESPRSSIF MIT License
 *
 * Copyright (c) 2016 <ESPRESSIF SYSTEMS (SHANGHAI) PTE LTD>
 *
 * Permission is hereby granted for use on ESPRESSIF SYSTEMS ESP8266 only, in which case,
 * it is free of charge, to any person obtaining a copy of this software and associated
 * documentation files (the "Software"), to deal in the Software without restriction, including
 * without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense,
 * and/or sell copies of the Software, and to permit persons to whom the Software is furnished
 * to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all copies or
 * substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
 * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
 * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
 * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 *
 */

#include "driver/spi_overlap.h"
#include "driver/spi.h"
#include "gpio.h"

#define SPI_FLASH_READ_MODE_MASK 0x196000
#define WAIT_HSPI_IDLE() 	while(READ_PERI_REG(SPI_EXT2(HSPI))||(READ_PERI_REG(SPI_CMD(HSPI))&0xfffc0000));
#define CONF_HSPI_CLK_DIV(div)	WRITE_PERI_REG(SPI_CLOCK(HSPI), (((div<<1)+1)<<12)+(div<<6)+(div<<1)+1)
#define HSPI_FALLING_EDGE_SAMPLE()		SET_PERI_REG_MASK(SPI_USER(HSPI),  SPI_CK_OUT_EDGE)
#define HSPI_RISING_EDGE_SAMPLE()			CLEAR_PERI_REG_MASK(SPI_USER(HSPI),  SPI_CK_OUT_EDGE)
#define ACTIVE_HSPI_CS0	 	CLEAR_PERI_REG_MASK(SPI_PIN(HSPI), SPI_CS0_DIS);\
						SET_PERI_REG_MASK(SPI_PIN(HSPI), SPI_CS1_DIS |SPI_CS2_DIS)
#define ACTIVE_HSPI_CS1		CLEAR_PERI_REG_MASK(SPI_PIN(HSPI), SPI_CS1_DIS);\
						SET_PERI_REG_MASK(SPI_PIN(HSPI), SPI_CS0_DIS |SPI_CS2_DIS)
#define ACTIVE_HSPI_CS2		CLEAR_PERI_REG_MASK(SPI_PIN(HSPI), SPI_CS2_DIS);\
						SET_PERI_REG_MASK(SPI_PIN(HSPI), SPI_CS0_DIS |SPI_CS1_DIS)
#define ENABLE_HSPI_DEV_CS()		PIN_FUNC_SELECT(PERIPHS_IO_MUX_MTDO_U, 2)
#define DISABLE_HSPI_DEV_CS()		GPIO_OUTPUT_SET(15, 1);\
									PIN_FUNC_SELECT(PERIPHS_IO_MUX_MTDO_U, FUNC_GPIO15)
struct hspi_device_register hspi_dev_reg;
/******************************************************************************
 * FunctionName : hspi_overlap_init
 * Description  : enable hspi and spi module overlap mode
*******************************************************************************/
void ICACHE_FLASH_ATTR
hspi_overlap_init(void)
{
	//hspi overlap to spi, two spi masters on cspi
	SET_PERI_REG_MASK(HOST_INF_SEL, reg_cspi_overlap);

	//set higher priority for spi than hspi
	SET_PERI_REG_MASK(SPI_EXT3(SPI),0x1);
	SET_PERI_REG_MASK(SPI_EXT3(HSPI),0x3);
	SET_PERI_REG_MASK(SPI_USER(HSPI), BIT(5));
}
/******************************************************************************
 * FunctionName : hspi_overlap_deinit
 * Description  : recover hspi and spi module from overlap mode
*******************************************************************************/
void ICACHE_FLASH_ATTR
hspi_overlap_deinit(void)
{
	//hspi overlap to spi, two spi masters on cspi
	CLEAR_PERI_REG_MASK(HOST_INF_SEL, reg_cspi_overlap);

	//set higher priority for spi than hspi
	CLEAR_PERI_REG_MASK(SPI_EXT3(SPI),0x1);
	CLEAR_PERI_REG_MASK(SPI_EXT3(HSPI),0x3);
	CLEAR_PERI_REG_MASK(SPI_USER(HSPI), BIT(5));
}

/******************************************************************************
 * FunctionName : spi_reg_backup
 * Description  : backup SPI normal operation register value and disable CPU cache to modify some flash registers.
 * Parameters   : 	uint8 spi_no - SPI module number, Only "SPI" and "HSPI" are valid
*******************************************************************************/
void ICACHE_FLASH_ATTR
    spi_reg_backup(uint8 spi_no,uint32* backup_mem)
{
	if(spi_no>1) 		return; //handle invalid input number

	backup_mem[PERIPHS_IO_MUX_BACKUP]	=READ_PERI_REG(PERIPHS_IO_MUX);
	backup_mem[SPI_USER_BACKUP]	=READ_PERI_REG(SPI_USER(spi_no)); 	
	backup_mem[SPI_CTRL_BACKUP]	=READ_PERI_REG(SPI_CTRL(spi_no)); 	
	backup_mem[SPI_CLOCK_BACKUP]	=READ_PERI_REG(SPI_CLOCK(spi_no));
	backup_mem[SPI_USER1_BACKUP]	=READ_PERI_REG(SPI_USER1(spi_no));
	backup_mem[SPI_USER2_BACKUP]	=READ_PERI_REG(SPI_USER2(spi_no));	
	backup_mem[SPI_CMD_BACKUP]	=READ_PERI_REG(SPI_CMD(spi_no));
	backup_mem[SPI_PIN_BACKUP]	=READ_PERI_REG(SPI_PIN(spi_no));
	backup_mem[SPI_SLAVE_BACKUP]	=READ_PERI_REG(SPI_SLAVE(spi_no));
}
/******************************************************************************
 * FunctionName : spi_reg_recover
 * Description  : recover SPI normal operation register value and enable CPU cache.
 * Parameters   : 	uint8 spi_no - SPI module number, Only "SPI" and "HSPI" are valid
*******************************************************************************/
void ICACHE_FLASH_ATTR
   spi_reg_recover(uint8 spi_no,uint32* backup_mem)
{
	if(spi_no>1) 		return; //handle invalid input number

//	WRITE_PERI_REG(PERIPHS_IO_MUX, backup_mem[PERIPHS_IO_MUX_BACKUP]); 	
	WRITE_PERI_REG(SPI_USER(spi_no), backup_mem[SPI_USER_BACKUP]); 	
	WRITE_PERI_REG(SPI_CTRL(spi_no), backup_mem[SPI_CTRL_BACKUP]); 	
	WRITE_PERI_REG(SPI_CLOCK(spi_no), backup_mem[SPI_CLOCK_BACKUP]); 	
	WRITE_PERI_REG(SPI_USER1(spi_no), backup_mem[SPI_USER1_BACKUP]); 	
	WRITE_PERI_REG(SPI_USER2(spi_no), backup_mem[SPI_USER2_BACKUP]); 	
	WRITE_PERI_REG(SPI_CMD(spi_no), backup_mem[SPI_CMD_BACKUP]); 	
	WRITE_PERI_REG(SPI_PIN(spi_no), backup_mem[SPI_PIN_BACKUP]); 
//	WRITE_PERI_REG(SPI_SLAVE(spi_no), backup_mem[SPI_SLAVE_BACKUP]); 
}

void ICACHE_FLASH_ATTR  
    hspi_master_dev_init(uint8 dev_no,uint8 clk_polar,uint8 clk_div)
{
	uint32 regtemp;
	if((dev_no>3)||(clk_polar>1)||(clk_div>0x1f))
	{
		os_printf("hspi_master_dev_init parameter is out of range!\n\r");
		return;
	}
	
	WAIT_HSPI_IDLE();
	if(!hspi_dev_reg.hspi_reg_backup_flag){
		if(READ_PERI_REG(PERIPHS_IO_MUX)&BIT8){
			hspi_dev_reg.spi_io_80m=1;
			SET_PERI_REG_MASK(SPI_CLOCK(HSPI),SPI_CLK_EQU_SYSCLK);
		}else{
			hspi_dev_reg.spi_io_80m=0;
			CLEAR_PERI_REG_MASK(SPI_CLOCK(HSPI),SPI_CLK_EQU_SYSCLK);
		}
		
		regtemp=READ_PERI_REG(SPI_CTRL(SPI))&SPI_FLASH_READ_MODE_MASK;
		CLEAR_PERI_REG_MASK(SPI_CTRL(HSPI), SPI_FLASH_READ_MODE_MASK);
		SET_PERI_REG_MASK(SPI_CTRL(HSPI), regtemp);
		spi_reg_backup(HSPI, hspi_dev_reg.hspi_flash_reg_backup);

		spi_master_init(HSPI);
		spi_reg_backup(HSPI, hspi_dev_reg.hspi_dev_reg_backup);

		hspi_dev_reg.hspi_reg_backup_flag=1;
		
	//	spi_reg_recover(HSPI, hspi_dev_reg.hspi_flash_reg_backup);
		hspi_dev_reg.selected_dev_num=HSPI_IDLE;
	}

	hspi_dev_reg.hspi_dev_conf[dev_no].active=1;
	hspi_dev_reg.hspi_dev_conf[dev_no].clk_div=clk_div;
	hspi_dev_reg.hspi_dev_conf[dev_no].clk_polar=clk_polar;
	
	switch(dev_no){
		case HSPI_CS_DEV :
			PIN_FUNC_SELECT(PERIPHS_IO_MUX_MTDI_U, 2);
			PIN_FUNC_SELECT(PERIPHS_IO_MUX_MTCK_U, 2);
			PIN_FUNC_SELECT(PERIPHS_IO_MUX_MTMS_U, 2);	
			PIN_FUNC_SELECT(PERIPHS_IO_MUX_MTDO_U, 2);
			CLEAR_PERI_REG_MASK(PERIPHS_IO_MUX, BIT9);
			break;

		case SPI_CS1_DEV :	
			PIN_FUNC_SELECT(PERIPHS_IO_MUX_U0TXD_U, FUNC_SPI_CS1);
			if(hspi_dev_reg.spi_io_80m){	
				os_printf("SPI CS1 device must work at 80Mhz");
			}
			break;
			
		case SPI_CS2_DEV :	
			PIN_FUNC_SELECT(PERIPHS_IO_MUX_GPIO0_U, FUNC_SPI_CS2);
			if(hspi_dev_reg.spi_io_80m){	
				os_printf("SPI CS2 device must work at 80Mhz");
			}
			break;
			
		default: break;
	}
}

void ICACHE_FLASH_ATTR
    hspi_dev_sel(uint8 dev_no)
{
	uint32 regval;
	
	if(dev_no>3){
		os_printf("hspi_dev_sel parameter is out of range!\n\r");
		return;
	}

	if(!hspi_dev_reg.hspi_dev_conf[dev_no].active){
		os_printf("device%d has not been initialized!\n\r",dev_no);
		return;
	}
	
	switch(hspi_dev_reg.selected_dev_num){
		case HSPI_CS_DEV:
			if((dev_no==SPI_CS1_DEV)||(dev_no==SPI_CS2_DEV)){
				WAIT_HSPI_IDLE();
				DISABLE_HSPI_DEV_CS();
				hspi_overlap_init();
				
				if(hspi_dev_reg.spi_io_80m)	{SET_PERI_REG_MASK(SPI_CLOCK(HSPI), SPI_CLK_EQU_SYSCLK);}
				else							{CONF_HSPI_CLK_DIV(hspi_dev_reg.hspi_dev_conf[dev_no].clk_div);}
				
				if(hspi_dev_reg.hspi_dev_conf[dev_no].clk_polar)	{HSPI_FALLING_EDGE_SAMPLE();}
				else												{HSPI_RISING_EDGE_SAMPLE();}
				
				if(dev_no==SPI_CS1_DEV)		{ACTIVE_HSPI_CS1;}
				else							{ACTIVE_HSPI_CS2;}
			}
			else if(dev_no==SPI_CS0_FLASH){
				WAIT_HSPI_IDLE();
				DISABLE_HSPI_DEV_CS();
				hspi_overlap_init();
				spi_reg_recover(HSPI, hspi_dev_reg.hspi_flash_reg_backup);

				if(hspi_dev_reg.spi_io_80m)	{SET_PERI_REG_MASK(SPI_CLOCK(HSPI), SPI_CLK_EQU_SYSCLK);}

				HSPI_RISING_EDGE_SAMPLE();
				ACTIVE_HSPI_CS0	;
			}
			break;

		case SPI_CS1_DEV:
			if(dev_no==SPI_CS2_DEV){
				WAIT_HSPI_IDLE();
				if(!hspi_dev_reg.spi_io_80m)	{CONF_HSPI_CLK_DIV(hspi_dev_reg.hspi_dev_conf[dev_no].clk_div);}

				if(hspi_dev_reg.hspi_dev_conf[dev_no].clk_polar)	{HSPI_FALLING_EDGE_SAMPLE();}
				else												{HSPI_RISING_EDGE_SAMPLE();}
				ACTIVE_HSPI_CS2;			
			}
			else if(dev_no==SPI_CS0_FLASH){
				WAIT_HSPI_IDLE();
				spi_reg_recover(HSPI, hspi_dev_reg.hspi_flash_reg_backup);
				HSPI_RISING_EDGE_SAMPLE();
				ACTIVE_HSPI_CS0;
			}
			else if(dev_no==HSPI_CS_DEV){
				WAIT_HSPI_IDLE();
				ENABLE_HSPI_DEV_CS();
				hspi_overlap_deinit();
				CONF_HSPI_CLK_DIV(hspi_dev_reg.hspi_dev_conf[dev_no].clk_div);

				if(hspi_dev_reg.hspi_dev_conf[dev_no].clk_polar)	{HSPI_FALLING_EDGE_SAMPLE();}
				else												{HSPI_RISING_EDGE_SAMPLE();}

				ACTIVE_HSPI_CS0;
			}
			break;

		case SPI_CS2_DEV:
			if(dev_no==SPI_CS1_DEV){
				WAIT_HSPI_IDLE();
				if(!hspi_dev_reg.spi_io_80m)	{CONF_HSPI_CLK_DIV(hspi_dev_reg.hspi_dev_conf[dev_no].clk_div);}

				if(hspi_dev_reg.hspi_dev_conf[dev_no].clk_polar)	{HSPI_FALLING_EDGE_SAMPLE();}
				else												{HSPI_RISING_EDGE_SAMPLE();}

				ACTIVE_HSPI_CS1;
			}
			else if(dev_no==SPI_CS0_FLASH){
				WAIT_HSPI_IDLE();
				spi_reg_recover(HSPI, hspi_dev_reg.hspi_flash_reg_backup);
				HSPI_RISING_EDGE_SAMPLE();
				ACTIVE_HSPI_CS0;
			}
			else if(dev_no==HSPI_CS_DEV){
				WAIT_HSPI_IDLE();
				ENABLE_HSPI_DEV_CS();
				hspi_overlap_deinit();
				CONF_HSPI_CLK_DIV(hspi_dev_reg.hspi_dev_conf[dev_no].clk_div);

				if(hspi_dev_reg.hspi_dev_conf[dev_no].clk_polar)	{HSPI_FALLING_EDGE_SAMPLE();}
				else												{HSPI_RISING_EDGE_SAMPLE();}

				ACTIVE_HSPI_CS0;
			}
			break;
			
		case SPI_CS0_FLASH:
			if((dev_no==SPI_CS1_DEV)||(dev_no==SPI_CS2_DEV)){
				WAIT_HSPI_IDLE();
				spi_reg_recover(HSPI, hspi_dev_reg.hspi_dev_reg_backup);

				if(hspi_dev_reg.spi_io_80m)	{SET_PERI_REG_MASK(SPI_CLOCK(HSPI), SPI_CLK_EQU_SYSCLK);}
				else							{CONF_HSPI_CLK_DIV(hspi_dev_reg.hspi_dev_conf[dev_no].clk_div);}

				if(hspi_dev_reg.hspi_dev_conf[dev_no].clk_polar)	{HSPI_FALLING_EDGE_SAMPLE();}
				else												{HSPI_RISING_EDGE_SAMPLE();}

				if(dev_no==SPI_CS1_DEV)		{ACTIVE_HSPI_CS1;}
				else							{ACTIVE_HSPI_CS2;}			
			}
			else if(dev_no==HSPI_CS_DEV){
				WAIT_HSPI_IDLE();
				ENABLE_HSPI_DEV_CS();
				hspi_overlap_deinit();
				spi_reg_recover(HSPI, hspi_dev_reg.hspi_dev_reg_backup);
				CONF_HSPI_CLK_DIV(hspi_dev_reg.hspi_dev_conf[dev_no].clk_div);

				if(hspi_dev_reg.hspi_dev_conf[dev_no].clk_polar)	{HSPI_FALLING_EDGE_SAMPLE();}
				else												{HSPI_RISING_EDGE_SAMPLE();}

				ACTIVE_HSPI_CS0;
			}
			break;

		default: 
			if((dev_no==SPI_CS1_DEV)||(dev_no==SPI_CS2_DEV)){
				WAIT_HSPI_IDLE();
				DISABLE_HSPI_DEV_CS();
				hspi_overlap_init();
				spi_reg_recover(HSPI, hspi_dev_reg.hspi_dev_reg_backup);
				
				if(hspi_dev_reg.spi_io_80m)	{SET_PERI_REG_MASK(SPI_CLOCK(HSPI), SPI_CLK_EQU_SYSCLK);}
				else							{CONF_HSPI_CLK_DIV(hspi_dev_reg.hspi_dev_conf[dev_no].clk_div);}

				if(hspi_dev_reg.hspi_dev_conf[dev_no].clk_polar)	{HSPI_FALLING_EDGE_SAMPLE();}
				else												{HSPI_RISING_EDGE_SAMPLE();}

				if(dev_no==SPI_CS1_DEV)		{ACTIVE_HSPI_CS1;}
				else							{ACTIVE_HSPI_CS2;}
			}
			else if(dev_no==SPI_CS0_FLASH){
				WAIT_HSPI_IDLE();
				DISABLE_HSPI_DEV_CS();
				hspi_overlap_init();
				spi_reg_recover(HSPI, hspi_dev_reg.hspi_flash_reg_backup);

				if(hspi_dev_reg.spi_io_80m)	{SET_PERI_REG_MASK(SPI_CLOCK(HSPI), SPI_CLK_EQU_SYSCLK);}
				
				HSPI_RISING_EDGE_SAMPLE();
				ACTIVE_HSPI_CS0	;
			}			
			else if(dev_no==HSPI_CS_DEV){
				WAIT_HSPI_IDLE();
				ENABLE_HSPI_DEV_CS();
				hspi_overlap_deinit();
				spi_reg_recover(HSPI, hspi_dev_reg.hspi_dev_reg_backup);
				CONF_HSPI_CLK_DIV(hspi_dev_reg.hspi_dev_conf[dev_no].clk_div);

				if(hspi_dev_reg.hspi_dev_conf[dev_no].clk_polar)	{HSPI_FALLING_EDGE_SAMPLE();}
				else												{HSPI_RISING_EDGE_SAMPLE();}

				ACTIVE_HSPI_CS0;
			}
			break;
	}
	hspi_dev_reg.selected_dev_num=dev_no;
}

/******************************************************************************
 * FunctionName : spi_read_data
 * Description  : use hspi to read flash data for stability test
 * Parameters   : 	SpiFlashChip * spi-- flash parameter structure pointer
 * 				uint32 flash_addr--flash start address
 *				uint32 * addr_dest--start address for preped destination memory space
 *				uint32 byte_length--length of the data which needs to be read from flash
*******************************************************************************/
SpiFlashOpResult ICACHE_FLASH_ATTR
hspi_overlap_read_flash_data(SpiFlashChip * spi, uint32 flash_addr, uint32 * addr_dest, uint32 byte_length)
{
    uint32  temp_addr,reg_tmp;
    sint32  temp_length;
    uint8   i;
    uint8   remain_word_num;	
	
   hspi_dev_sel(SPI_CS0_FLASH);

      //address range check 	
   if ((flash_addr+byte_length) > (spi->chip_size))
   {
        return SPI_FLASH_RESULT_ERR;
   }
   
    temp_addr = flash_addr;
    temp_length = byte_length;
	
    while(temp_length > 0)
    {
       if(temp_length >= SPI_BUFF_BYTE_NUM)
       {
	//   reg_tmp=((temp_addr&0xff)<<16)|(temp_addr&0xff00)|((temp_addr&0xff0000)>>16)|(SPI_BUFF_BYTE_NUM << SPI_FLASH_BYTES_LEN);
	   reg_tmp= temp_addr |(SPI_BUFF_BYTE_NUM<< SPI_FLASH_BYTES_LEN) ;
	   WRITE_PERI_REG(SPI_ADDR(HSPI), reg_tmp);
          WRITE_PERI_REG(SPI_CMD(HSPI), SPI_FLASH_READ);
          while(READ_PERI_REG(SPI_CMD(HSPI)) != 0);

          for(i=0; i<(SPI_BUFF_BYTE_NUM>>2);i++)
          {    
              *addr_dest++ = READ_PERI_REG(SPI_W0(HSPI)+i*4);
          }
          temp_length = temp_length - SPI_BUFF_BYTE_NUM;
          temp_addr = temp_addr + SPI_BUFF_BYTE_NUM;
       }
       else
       {
            WRITE_PERI_REG(SPI_ADDR(HSPI), temp_addr |(temp_length << SPI_FLASH_BYTES_LEN ));
            WRITE_PERI_REG(SPI_CMD(HSPI), SPI_FLASH_READ);
            while(READ_PERI_REG(SPI_CMD(HSPI)) != 0);

            remain_word_num = (0== (temp_length&0x3))? (temp_length>>2) : (temp_length>>2)+1;
	     for (i=0; i<remain_word_num; i++)		
	     {
               *addr_dest++ = READ_PERI_REG(SPI_W0(HSPI)+i*4);
	     }	 
            temp_length = 0;
        }
    }

    return SPI_FLASH_RESULT_OK;
}

void ICACHE_FLASH_ATTR
hspi_overlap_flash_init(void)
{
	 hspi_master_dev_init(SPI_CS0_FLASH,0,0);

	 spi_flash_set_read_func(hspi_overlap_read_flash_data);
}