/*
 * Ethercat_QSPI.c
 *
 * Created: 31/07/2021 17:52:21
 *  Author: Nick-XMG
 */ 

#include "Ethercat_QSPI.h"
#include <hpl_qspi_config.h>
#include <hpl_sercom_config.h>

#define SQI_READ				(0x0B<<16)
#define SQI_WRITE				(0x02<<16)
#define SQI_INC					(0x40<<8)
#define SQI_DEC					(0x80<<8)
#define ECAT_PRAM_RD_DATA		0x0000
#define ECAT_PRAM_WR_DATA		0x0020
#define HW_CFG					0x0074
#define	BYTE_TEST				0x0064
#define ECAT_PRAM_RD_ADDR_LEN	0x0308
#define ECAT_PRAM_RD_CMD		0x030C
#define ECAT_PRAM_WR_ADDR_LEN	0x0310
#define ECAT_PRAM_WR_CMD		0x0314

#define LAN9252_RDY				(1<<27)

#define PDRAM_RD_ADDRESS		0x1100
#define PDRAM_RD_LENGTH			ECAT_SIZE_RD //do the math later to automatize. fixed to 64 for now.
#define PDRAM_WR_ADDRESS		0x1800
#define PDRAM_WR_LENGTH			ECAT_SIZE_WR
#define PDRAM_LENGTH_MAX		64
#define PDRAM_LENGTH_SHORT		32
#define FIFO_DEPTH				16
#define PDRAM_REG_MAX			PDRAM_LENGTH_SHORT/4
#define PRAM_X_ABORT			(1<<30)
#define CSR_BUSY				0x80000000

COMPILER_ALIGNED(16)
DmacDescriptor dummy_rx_descriptor;
DmacDescriptor dummy_tx_descriptor;

volatile uint32_t QSPI_tx_buffer[buffer_size]={0};
volatile uint32_t QSPI_rx_buffer[buffer_size]={0};

volatile enum ecat_states ecat_state = wait;
volatile enum ecat_states next_ecat_state = wait;

volatile uint8_t wr_cnt = 0;
volatile uint8_t rd_cnt = 0;

static uint8_t sync_rx_buffer[2*2*buffer_size/3]={0};
static uint8_t sync_tx_buffer[2*2*buffer_size/3]={0xED};


static uint32_t QSPI_cmds[]={0,PRAM_X_ABORT,0,PRAM_X_ABORT,
							 ((PDRAM_LENGTH_MAX)<<16)+PDRAM_RD_ADDRESS,						CSR_BUSY,(PDRAM_LENGTH_MAX<<16)+PDRAM_WR_ADDRESS,CSR_BUSY,
							 ((PDRAM_LENGTH_MAX)<<16)+PDRAM_RD_ADDRESS+PDRAM_LENGTH_MAX,	CSR_BUSY,(PDRAM_LENGTH_MAX<<16)+PDRAM_WR_ADDRESS+PDRAM_LENGTH_MAX,CSR_BUSY,
							 ((PDRAM_LENGTH_MAX)<<16)+PDRAM_RD_ADDRESS+PDRAM_LENGTH_MAX*2,	CSR_BUSY,(PDRAM_LENGTH_MAX<<16)+PDRAM_WR_ADDRESS+PDRAM_LENGTH_MAX*2,CSR_BUSY,
							 };
			
static uint32_t zero[FIFO_DEPTH]={0};	
				
volatile uint32_t status = 0;	
	
	struct _qspi_command rd_cmd = {
	.inst_frame.bits.width = QSPI_INST4_ADDR4_DATA4,
	 .inst_frame.bits.inst_en = 0,
	 .inst_frame.bits.data_en = 1,
	 .inst_frame.bits.addr_en = 1,
	 .inst_frame.bits.dummy_cycles = 6,
	 .inst_frame.bits.tfr_type = QSPI_READMEM_ACCESS,
 };	
 
 struct _qspi_command wr_cmd = {
	 .inst_frame.bits.width = QSPI_INST4_ADDR4_DATA4,
	 .inst_frame.bits.inst_en = 0,
	 .inst_frame.bits.data_en = 1,
	 .inst_frame.bits.addr_en = 1,
	 .inst_frame.bits.dummy_cycles = 0,
	 .inst_frame.bits.tfr_type = QSPI_WRITEMEM_ACCESS,
 };	
 
 struct _qspi_command qspi_cmd = {
	 .inst_frame.bits.width = QSPI_INST1_ADDR1_DATA1,
	 .inst_frame.bits.inst_en = 1,
	 .inst_frame.bits.data_en = 0,
	 .inst_frame.bits.addr_en = 0,
	 .inst_frame.bits.dummy_cycles = 0,
	 .instruction = 0x38,
	 .inst_frame.bits.tfr_type = QSPI_WRITE_ACCESS,
 };
 
struct _qspi_command *spi_cmd= &wr_cmd;
volatile uint8_t tx_complete =3;
volatile uint32_t *ECAT_BYTE_TEST=			QSPI_AHB+BYTE_TEST+SQI_READ;
volatile uint32_t *ECAT_FIFO_RD_RD=			QSPI_AHB+SQI_READ +ECAT_PRAM_RD_DATA;
//volatile uint32_t *ECAT_FIFO_RD_RD=			QSPI_AHB+SQI_READ +SQI_INC+ECAT_PRAM_RD_DATA;

volatile uint32_t *ECAT_FIFO_WR_WR=			QSPI_AHB+SQI_WRITE+ECAT_PRAM_WR_DATA;
//volatile uint32_t *ECAT_FIFO_WR_WR=			QSPI_AHB+SQI_WRITE+SQI_INC+ECAT_PRAM_WR_DATA;

volatile uint32_t *ECAT_HW_CFG_RD=			QSPI_AHB+SQI_READ+HW_CFG;
volatile uint32_t *ECAT_FIFO_RD_ADLEN_WR=   QSPI_AHB+SQI_WRITE+SQI_INC+ECAT_PRAM_RD_ADDR_LEN;
volatile uint32_t *ECAT_FIFO_WR_ADLEN_WR=   QSPI_AHB+SQI_WRITE+SQI_INC+ECAT_PRAM_WR_ADDR_LEN;
volatile uint32_t *ECAT_FIFO_WR_ADLEN_RD=   QSPI_AHB+SQI_READ+SQI_INC+ECAT_PRAM_WR_ADDR_LEN;
volatile uint32_t *ECAT_FIFO_WR_CMD_RD=		QSPI_AHB+SQI_READ+ECAT_PRAM_WR_CMD;
volatile uint32_t *ECAT_FIFO_WR_CMD_WR=		QSPI_AHB+SQI_WRITE+ECAT_PRAM_WR_CMD;
 
volatile uint32_t *INPUT_ADDRESS ;
volatile uint32_t *OUTPUT_ADDRESS;
volatile uint32_t read_buffer =0;
volatile uint8_t ecat_length= 0;

volatile bool run_ECAT = false;
volatile bool synced = false;

void ECAT_STATE_MACHINE(void){
	if ((ecat_state != wait)&(ecat_state != wait2)){
		run_ECAT = false;
		switch(ecat_state){
		case en_SQI:
			spi_cmd = &qspi_cmd;
			QSPI->INSTRCTRL.bit.INSTR=spi_cmd->instruction;
			OUTPUT_ADDRESS = ECAT_FIFO_RD_ADLEN_WR;
			INPUT_ADDRESS = &QSPI_cmds[0];
			ecat_length = 0;
			next_ecat_state = rd_rdy;
			
			break;
		case rd_rdy:
			if (QSPI_rx_buffer[0]==LAN9252_RDY){
				next_ecat_state = abort_fifo;
			}
			else if (QSPI_rx_buffer[0]== 0xFFFFFFFF){
				next_ecat_state = en_SQI;
				QSPI_rx_buffer[0] = 0;
			}
			else{
			spi_cmd = &rd_cmd;
			OUTPUT_ADDRESS = &QSPI_rx_buffer[0];
			INPUT_ADDRESS = ECAT_HW_CFG_RD;
			ecat_length = 1;
			wr_cnt=0;
			}
		
		break;
		case abort_fifo:	
			spi_cmd = &wr_cmd;
			OUTPUT_ADDRESS = ECAT_FIFO_WR_CMD_WR;
			INPUT_ADDRESS = &QSPI_cmds[1];
			ecat_length = 1;
			next_ecat_state = dlt_rdram;
			wr_cnt=0;
			rd_cnt=0;
		break;
		case dlt_rdram:
			spi_cmd = &wr_cmd;
			OUTPUT_ADDRESS = ECAT_FIFO_WR_WR;
			INPUT_ADDRESS = &zero[0];
			//if (wr_cnt >= 1) ecat_length = FIFO_DEPTH/2;
			//else ecat_length = FIFO_DEPTH;
			ecat_length = FIFO_DEPTH;
			next_ecat_state = cf_dlt_rdram;
		break;
		case cf_dlt_rdram:
			spi_cmd = &wr_cmd;
			OUTPUT_ADDRESS = ECAT_FIFO_WR_ADLEN_WR;
			INPUT_ADDRESS = &QSPI_cmds[4*(wr_cnt+1)];
			ecat_length = 2;
			if (wr_cnt >= 2) {
				next_ecat_state = wait;
				wr_cnt =0;
			} else {
				next_ecat_state = dlt_rdram;
				wr_cnt++;
			}
			
		break;
		case write_fifo:
			spi_cmd = &wr_cmd;
			OUTPUT_ADDRESS = ECAT_FIFO_WR_WR;
			INPUT_ADDRESS = &QSPI_tx_buffer[wr_cnt*FIFO_DEPTH];
			/*if (wr_cnt >= 1) ecat_length = FIFO_DEPTH/2;
			else ecat_length = FIFO_DEPTH;*/
			ecat_length = FIFO_DEPTH;
			next_ecat_state = config_fifo;
		break;
		case config_fifo:	
			spi_cmd = &wr_cmd;
			OUTPUT_ADDRESS = ECAT_FIFO_RD_ADLEN_WR;
			INPUT_ADDRESS = &QSPI_cmds[4*(wr_cnt+1)];
			ecat_length = 4;
			next_ecat_state = read_fifo;
		break;
		case read_fifo:	
			spi_cmd = &rd_cmd;
			OUTPUT_ADDRESS = &QSPI_rx_buffer[wr_cnt*FIFO_DEPTH];
			INPUT_ADDRESS = ECAT_FIFO_RD_RD;
			ecat_length = FIFO_DEPTH;
			if (wr_cnt >= 2) {
//				ecat_length = FIFO_DEPTH/2;
				next_ecat_state = wait2;
				wr_cnt=0;
				}
			else {
//				ecat_length = FIFO_DEPTH;
				next_ecat_state = write_fifo;
				wr_cnt++;
			}	
		break;
		}
		qspi_dma_enable(&ECAT_QSPI);
		QSPI->INSTRFRAME.reg = ((*spi_cmd).inst_frame.word);
		for (uint8_t i=0;i<ecat_length;i++)
		{
		*(OUTPUT_ADDRESS+i) = *(INPUT_ADDRESS+i);
		}
		QSPI->CTRLA.bit.LASTXFER = 1;	
		ecat_state = next_ecat_state; 
	}
}

void config_qspi()
{
	QSPI->INTENSET.bit.CSRISE = 1;
	NVIC_EnableIRQ(QSPI_IRQn);
	ecat_state =en_SQI;
}

QSPI_Handler(){
	if (QSPI->INTFLAG.bit.CSRISE == 1){
		QSPI->INTFLAG.bit.CSRISE = 1;
		run_ECAT =true;
	}
}

