#include "master.h"
#include "system_setup.h"
#include "DMAScheduler.h"
#include "hardware.h"
#include "data_indicator.h"
#include "data_transfer.h"
#include "com.h"
#include "spb_system.h"
#include <csl_emifa.h>
#include "HPI_Controller.h"
#include "srio_master.h"

/*
#pragma DATA_ALIGN(Master, 8);
TMaster Master;
*/

#pragma DATA_ALIGN(CPBDeviceInfo, 8);
TCPBDeviceInfo CPBDeviceInfo[SYSTEM_CPB_COUNT];

#pragma DATA_ALIGN(MasterTempBuffer, 8);
unsigned int MasterTempBuffer[1024];

// command_state
#define COMMAND_STATE_IDLE				0
#define COMMAND_STATE_SEND_READY		1
#define COMMAND_STATE_SENDING			2
#define COMMAND_STATE_SEND_COMPLETE		3
#define COMMAND_STATE_POLLING_RESPONSE	4
#define COMMAND_STATE_POLLING_WAIT		5
#define COMMAND_STATE_POLLING_COMPLETE	6
#define COMMAND_STATE_RECEIVE_RESPONSE	7
#define COMMAND_STATE_RECEIVE_WAIT		8
#define COMMAND_STATE_RECEIVE_COMPLETE	9
#define COMMAND_STATE_COMPLETE			10
#define COMMAND_STATE_CRITICAL_ERROR	0xFF


//#define C64PLUS_HPI_CEVAL			0x0151C620u
#define C64PLUS_HPI_CEVAL			0x1151C620u
//#define C64PLUS_HPI_CEVAL			0x0151C620u
#define C64_HPI_CEVAL				0x1151C620u


// 준비된 command 리스트
unsigned char Command_Ready_List[SYSTEM_CPB_COUNT][TOTAL_COMMAND_COUNT];
// 보낼 command 들의 대기열. 리스트를 먼저 체크하므로 절대 넘치지 않는다.
unsigned short Command_Ready_Queue[SYSTEM_CPB_COUNT][TOTAL_COMMAND_COUNT];
int Command_Queue_Start[SYSTEM_CPB_COUNT], Command_Queue_End[SYSTEM_CPB_COUNT];

unsigned int command_state[SYSTEM_CPB_COUNT];


volatile unsigned int cpb_transfer_status[SYSTEM_CPB_COUNT];
volatile unsigned int cpb_data_size[SYSTEM_CPB_COUNT];

volatile unsigned int DMA_Fin[SYSTEM_CPB_COUNT];
unsigned int cpb_sent_time[SYSTEM_CPB_COUNT];
volatile unsigned int MasterOnProcess = 0;
extern volatile int srio_lock;
TSRIOTransferStatus CommandTransferStatus[SYSTEM_CPB_COUNT];
unsigned int Current_HPI_CPU_Kind;

void ReceiveErrorList(int cpbIndex);

void MasterInit()
{
	int i, j;
	memset(MasterTempBuffer,0x00,sizeof(MasterTempBuffer));
	memset(Command_Ready_Queue, 0x00,sizeof(Command_Ready_Queue));
	memset(Command_Ready_List, 0x00,sizeof(Command_Ready_Queue));
	memset(Command_Queue_Start, 0x00,sizeof(Command_Queue_Start));
	memset(Command_Queue_End, 0x00,sizeof(Command_Queue_End));
	memset(CommandTransferStatus, 0x00,sizeof(CommandTransferStatus));
	memset(command_state, 0x00,sizeof(command_state));
	memset(cpb_transfer_status, 0x00,sizeof(cpb_transfer_status));
	memset(cpb_data_size, 0x00,sizeof(cpb_data_size));
	for (i = 0; i < SYSTEM_CPB_MAX_COUNT; i++)
	{
		CommandTransferStatus[i].Status = HPI_TRANSEFR_STATUS_COMPLETE;
	}
	Current_HPI_CPU_Kind = CPU_KIND_C64PLUS;
}
// timer interrupt 에서 호출되는 함수
void Master10msTimer()
{
	int cpbIndex;
	static unsigned int tmrStatusList = 24;
	
	if (tmrStatusList) tmrStatusList--;
	else
	{
		for (cpbIndex = 0; cpbIndex < SYSTEM_CPB_MAX_COUNT; cpbIndex++)
		{
			if (!CPBStatus[cpbIndex].IsDown)
			{
				MasterPushCommand(cpbIndex, HPI_CMD_STATUSLIST);
			}		
		}	
		tmrStatusList = 24;
	}
}


void WriteHPI(int cpbIndex, unsigned int addr, unsigned int value)
{
	volatile int i;
	*HPI_CTRL(cpbIndex) = 0;
	if (CPBHWInfo[cpbIndex].CPUKind == CPU_KIND_C64PLUS)
	{
		*((volatile unsigned int *) CPBHWInfo[cpbIndex].HPIAddr_Addr) = (addr >> 2);
		for (i = 0; i < 20; i++);
		//*((volatile unsigned int *) CPBHWInfo[cpbIndex].HPIAddr_Addr) = (addr >> 2);
		/*
		for (i = 0; i < 20; i++)
		{
			if (*((volatile unsigned int *) CPBHWInfo[cpbIndex].HPIAddr_Addr) == (addr >> 2))
			{
				*((volatile unsigned int *) CPBHWInfo[cpbIndex].HPIAddr_Addr) = (addr >> 2);
				break;
			}
		}
		*/
	}
	else
	{
		*((volatile unsigned int *) CPBHWInfo[cpbIndex].HPIAddr_Addr) = addr;
	}	
	*((volatile unsigned int *) CPBHWInfo[cpbIndex].HPIData_Inc_Addr) = value;
}

//unsigned int *SetHPI(int cpb, unsigned int addr)
void SetHPI(int cpbIndex, unsigned int addr)
{
	volatile int i;

	*HPI_CTRL(cpbIndex) = 0;
	if (CPBHWInfo[cpbIndex].CPUKind == CPU_KIND_C64PLUS)
	{
		if (Current_HPI_CPU_Kind != CPU_KIND_C64PLUS)
		{			
//			*(volatile unsigned int *)_EMIFA_CECTL3_ADDR = C64PLUS_HPI_CEVAL; 
			Current_HPI_CPU_Kind = CPU_KIND_C64PLUS;
		}
		*((volatile unsigned int *) CPBHWInfo[cpbIndex].HPIAddr_Addr) = (addr >> 2);
		//for (i = 0; i < 30; i++);
		
		/*
		for (i = 0; i < 20; i++)
		{
			if (*((volatile unsigned int *) CPBHWInfo[cpbIndex].HPIAddr_Addr) == (addr >> 2))break;
		}
		*/
	}
	else
	{
		if (Current_HPI_CPU_Kind != CPU_KIND_C64)
		{
//			*(volatile unsigned int *)_EMIFA_CECTL3_ADDR = C64_HPI_CEVAL; 
			Current_HPI_CPU_Kind = CPU_KIND_C64;
		}
		*((volatile unsigned int *) CPBHWInfo[cpbIndex].HPIAddr_Addr) = addr;
		for (i = 0; i < 15; i++);	// wait for HPIA write cycle
	}
//	return HPI_DATA_INC(cpb);
}


// dma interrupt 에서 호출되는 함수
/*
static void OnMasterSendComplete(TMasterData *data)
{
	command_state[data->cpbIndex] = COMMAND_STATE_SEND_COMPLETE;
}
*/
/*
static void OnMasterReceiveResponseComplete(TMasterData *data)
{
	command_state[data->cpbIndex] = COMMAND_STATE_RECEIVE_COMPLETE;
}
*/


void Master_Process_CPB(int cpbIndex)
{
	if (CommandTransferStatus[cpbIndex].Status == SRIO_TRANSFER_STATUS_COMPLETE){
		Request_SRIO_ReadData(cpbIndex, (Uint32) &(CPB_StatusList[cpbIndex]), (Uint32) CPBHWInfo[cpbIndex].CPBStatusAddr, 1, &CommandTransferStatus[cpbIndex]);
	}
}
/*
void Master_Process_CPB(int cpbIndex)
{
	unsigned short command;
//	TMasterData *data;
	//int length;
	// send command	
// enter critical section
	if (MasterOnProcess) return;
	MasterOnProcess = 1;

	if (CommandTransferStatus[cpbIndex].Status == SRIO_TRANSFER_STATUS_COMPLETE)
	{
		if (command_state[cpbIndex] == COMMAND_STATE_IDLE)
		{
			if (Command_Queue_Start[cpbIndex] != Command_Queue_End[cpbIndex])	// command queue is not empty
			{
				command = Command_Ready_Queue[cpbIndex][Command_Queue_Start[cpbIndex]];
				if (command == HPI_CMD_STATUSLIST)  // status 통신은 종료 확인을 하지 않는다. 
				{
					Command_Queue_Start[cpbIndex] = (Command_Queue_Start[cpbIndex] + 1) % TOTAL_COMMAND_COUNT;
					Request_SRIO_ReadData(cpbIndex, (Uint32) &(CPB_StatusList[cpbIndex]), (Uint32) CPBHWInfo[cpbIndex].CPBStatusAddr, 1, &CommandTransferStatus[cpbIndex]);
					Command_Ready_List[cpbIndex][command] = 0;
				}
			}
			MasterTempBuffer[cpbIndex]++;
		}
	}
	MasterOnProcess = 0;
}
*/
void Master_Process()
{
	int cpbIndex;
	for (cpbIndex = 0; cpbIndex < SYSTEM_CPB_MAX_COUNT; cpbIndex++)
	{
		if (!CPBStatus[cpbIndex].IsDown)
		{
			Master_Process_CPB(cpbIndex);
		}		
	}	
}



// clear 명령을 받았을 때 호출
void MasterClear()
{

}

void MasterPushCommand(int dst_cpb_index, unsigned short command)
{
	int gie;
	// 명령 대기열에 집어넣은 다음 바로 dma job에 집어넣는다.
//	gie = IRQ_globalDisable();
	if (Command_Ready_List[dst_cpb_index][command] == 0)
	{
		Command_Ready_List[dst_cpb_index][command] = 1;
		Command_Ready_Queue[dst_cpb_index][Command_Queue_End[dst_cpb_index]] = command;
		Command_Queue_End[dst_cpb_index] = (Command_Queue_End[dst_cpb_index] + 1) % TOTAL_COMMAND_COUNT;
		//Master_Process_CPB(dst_cpb_index);
	}	
//	IRQ_globalRestore(gie);
}


#define C64_DEVICE_ID_ADDR			0x01B00200u
#define C64PLUS_DEVICE_ID_ADDR		0x02049018u
#define DM647_DEVICE_PART_NUMBER	0xB77Au


void Master_CheckDeviceID(void)
{
	unsigned int cpbIndex, regValue;
	volatile unsigned int *hpiAddr;
	for (cpbIndex = 0; cpbIndex < SYSTEM_CPB_MAX_COUNT; cpbIndex++)
	{
		CPBDeviceInfo[cpbIndex].InfoValid = 0;
		CPBDeviceInfo[cpbIndex].IsC64PlusDevice = 0;
		// check c64x+ device ID
		SetHPI(cpbIndex, C64PLUS_DEVICE_ID_ADDR);
		//hpiAddr = SetHPI(cpbIndex, C64PLUS_DEVICE_ID_ADDR);
		hpiAddr = (volatile unsigned int *) CPBHWInfo[cpbIndex].HPIData_Inc_Addr;
		regValue = *hpiAddr;
		if (regValue != C64PLUS_DEVICE_ID_ADDR)
		{
			CPBDeviceInfo[cpbIndex].Version = ((regValue >> 28) & 0x000F);
			CPBDeviceInfo[cpbIndex].PartNumber = ((regValue >> 12) & 0xFFFF);
			CPBDeviceInfo[cpbIndex].Manufacturer = ((regValue >> 1) & 0x07FF);
			if (CPBDeviceInfo[cpbIndex].PartNumber == DM647_DEVICE_PART_NUMBER)
			{
				CPBDeviceInfo[cpbIndex].InfoValid = 1;
				CPBDeviceInfo[cpbIndex].IsC64PlusDevice = 1;
			}
		}

		if (!CPBDeviceInfo[cpbIndex].InfoValid)
		{
			// check c64x device ID
			SetHPI(cpbIndex, C64_DEVICE_ID_ADDR);
			//hpiAddr = SetHPI(cpbIndex, C64_DEVICE_ID_ADDR);
			hpiAddr = (volatile unsigned int *) CPBHWInfo[cpbIndex].HPIData_Inc_Addr;
			regValue = *hpiAddr;
			if (regValue == C64_DEVICE_ID_ADDR)
			{
				CPBDeviceInfo[cpbIndex].Version = 0;
				CPBDeviceInfo[cpbIndex].InfoValid = 0;
			}
			else
			{
				CPBDeviceInfo[cpbIndex].Version = (regValue >> 16) & 0x001F;
				CPBDeviceInfo[cpbIndex].InfoValid = 1;				
			}
			CPBDeviceInfo[cpbIndex].PartNumber = 0;
			CPBDeviceInfo[cpbIndex].Manufacturer = 0;
			CPBDeviceInfo[cpbIndex].IsC64PlusDevice = 0;
		}
/*
		if (CPBDeviceInfo[cpbIndex].InfoValid && CPBDeviceInfo[cpbIndex].IsC64PlusDevice)
		{
			SystemInfo.CPBStatus[cpbIndex].BaseL2Addr = C64PLUS_L2_ADDR; 
			SystemInfo.CPBStatus[cpbIndex].CPUKind = 1;
		}
		else
		{
			SystemInfo.CPBStatus[cpbIndex].BaseL2Addr = C64_L2_ADDR; 
			SystemInfo.CPBStatus[cpbIndex].CPUKind = 0;
		}
*/
	}
}

/*
// cpb에서 응답이 왔을 때
static void UnitResponseErrorList(TPacket *rxPacket, int cpbIndex)
{
	ErrorQueue_Put(rxPacket->data + 1, rxPacket->data[0], cpbIndex);
}


void ReceiveErrorList(int cpbIndex)
{
	int errorCount;
	Request_SysReceiveData(SIND_ERROR_LIST_COUNT, &errorCount, sizeof(int), cpbIndex);
//	ErrorUnit_Put(BoardID + cpbIndex + 1, "error count", ERROR_TYPE_MESSAGE, ERROR_CODE_NONE, __LINE__, errorCount, 0);	
	if (errorCount > 0)
	{
		Request_SysReceiveData(SIND_ERROR_LIST, MasterTempBuffer, sizeof(TErrorUnit) + sizeof(int), cpbIndex);
		ErrorQueue_Put(&(MasterTempBuffer[1]), 1, cpbIndex);
	}

}
*/
