// +FHDR------------------------------------------------------------
//                 Copyright (c) 2022 SmartLogic.
//                       ALL RIGHTS RESERVED
// -----------------------------------------------------------------
// Filename      : arm_csu.c
// Author        : xinxin.li
// Created On    : 2022-11-23
// Last Modified :
// -----------------------------------------------------------------
// Description:
//
//
// -FHDR------------------------------------------------------------
#include "ucp_csu.h"
#include "arm_csu.h"

#include <assert.h>
#include <stddef.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <ucontext.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/mman.h>
#include <fcntl.h>
#include "ucp_utility.h"
#include "ucp_printf.h"
#include "drv_init.h"


uint8_t* virMemAddr = NULL;
int arm_csu_init()
{
	if (-1 == g_drv_mem_fd)
	{
//		printf("open dev mem. \r\n");
		g_drv_mem_fd = open("/dev/mem", O_RDWR|O_SYNC);
		if (0 > g_drv_mem_fd)
		{
			UCP_PRINT_ERROR("open /dev/mem error\n");
			return -1;
		}
	}

//	printf("start mmap. \r\n");
	virMemAddr = (uint8_t*)mmap(NULL, CSU_DEV_MAP_LEN, PROT_READ|PROT_WRITE, MAP_SHARED, g_drv_mem_fd, AP_CSU_BASE);
	if ((uint8_t*)(-1) == virMemAddr)
	{
		UCP_PRINT_ERROR("mmap error!!");
		return -1;
	}

//	printf("csu init. \r\n");
	int i;
	for (i = 0; i < 16; i++)
	{
		*((volatile uint32_t*)(virMemAddr+AP_CSU_DMAYNUMXNUM0) + (i<<3)) = ((4<<16)|1024);
		*((volatile uint32_t*)(virMemAddr+AP_CSU_DMAYSTEPL0) + (i<<3)) = 1024;
		*((volatile uint32_t*)(virMemAddr+AP_CSU_DMAYSTEPH0) + (i<<3)) = 0;
		*((volatile uint32_t*)(virMemAddr+AP_CSU_DMAZSTEPL0) + (i<<3)) = 4096;
		*((volatile uint32_t*)(virMemAddr+AP_CSU_DMAZSTEPH0) + (i<<3)) = 0;
	}

	*((volatile uint32_t*)(virMemAddr+AP_CSU_EM_BS_SMSEL_PREDATANUM)) = (0x2A<<16) | (0x0<<14) | (0x5<<5) | 0x9; // (0x3F<<16);
	*((volatile uint32_t*)(virMemAddr+AP_CSU_EM_BS_SMSEL_PREDATANUM)) = (0x2A<<16) | (0x1<<14) | (0x5<<5) | 0x9;
	*((volatile uint32_t*)(virMemAddr+AP_CSU_EM_BS_SMSEL_PREDATANUM)) = (0x2A<<16) | (0x2<<14) | (0x5<<5) | 0x9;
	*((volatile uint32_t*)(virMemAddr+AP_CSU_EM_BS_SMSEL_PREDATANUM)) = (0x2A<<16) | (0x3<<14) | (0x5<<5) | 0x9;

	*((volatile uint32_t*)(virMemAddr+AP_CSU_FINDDMATAG)) = 0x60;
	*((volatile uint32_t*)(virMemAddr+AP_CSU_TAGMASK0)) = 0xFFFFFFFF;
	*((volatile uint32_t*)(virMemAddr+AP_CSU_TAGMASK1)) = 0xFFFFFFFF;
	*((volatile uint32_t*)(virMemAddr+AP_CSU_TAGMASK2)) = 0xFFFFFFFF;
	*((volatile uint32_t*)(virMemAddr+AP_CSU_TAGMASK3)) = 0xFFFFFFFF;

	return 0;
}

int get_arm_csu_status(uint8_t tag)
{
	if (31 < tag)
	{
		UCP_PRINT_ERROR("input para error! tag = %d. \r\n", tag);
		return -1;
	}
	
	uint32_t status = VIR_ADDR_VAL(virMemAddr, AP_CSU_DMASTATUS);
	return (status&(1<<tag)); // 0: not finished, 1: finished
}

int arm_csu_wait_done(uint8_t tag)
{
	if (31 < tag)
	{
		UCP_PRINT_ERROR("input para error! tag = %d. \r\n", tag);
		return -1;
	}
	
	volatile uint32_t status = 0;
	while (1)
	{
		status = VIR_ADDR_VAL(virMemAddr, AP_CSU_DMASTATUS);
		//		printf("tag = %d, dmastatus = 0x%x. \r\n", tag, status);
		if (0 != (status&(1<<tag)))
		{
			break;
		}
	}
//	while (0 == (VIR_ADDR_VAL(virMemAddr, AP_CSU_DMASTATUS) & (1<<tag)));
	return 0;
}

int arm_csu_wait_all_done(void)
{
#if 0
	for (int i = 0; i < 32; i++)
	{
		arm_csu_wait_done(i);
	}
#endif
	while (1)
	{
		int cmdNum = 0;
		for (int i = 0; i < 4; i++)
		{
			cmdNum += VIR_ADDR_VAL(virMemAddr, AP_CSU_CMDFIFO0_NUM+(i<<2));
		}
		if (0 == cmdNum)
		{
			break;
		}
	}
	
	return 0;
}

//CmdFIFO		0			1			2			3	
//tag			0			1			2			3		reg_group = 0(0->1)
//tag			4			5			6			7					1(2->3)			
//tag			8			9			10			11					2(4->5)			
//tag			12			13			14			15					3(6->7)			
//tag			16			17			18			19					4(8->9)			
//tag			20			21			22			23					5(10->11)			
//tag			24			25			26			27					6(12->13)			
//tag			28			29			30			31					7(14->15)			

// get unused register group according to tag number
int get_free_reg_group(uint8_t tag)
{
	if (31 < tag)
	{
		return -1;
	}

	uint32_t dmaStatus = VIR_ADDR_VAL(virMemAddr, AP_CSU_DMASTATUS);
	uint8_t fifoNum = tag % 4;
	uint8_t regGroup = tag / 4;
	if (0 == (dmaStatus & (0xF<<regGroup)))
	{
		return -1;
	}

	return ((fifoNum<<16) | regGroup);	// [31:16]fifoNum(0~3), [15:0]reg group(0~7)
}

int get_free_channel()
{
	while (1)
	{
		for (int tag = 0; tag < 32; tag++)
		{
			if (0 == (VIR_ADDR_VAL(virMemAddr, AP_CSU_DMASTATUS) & (1<<tag)))
			{
				continue;
			}
			else
			{
				uint32_t temp = get_free_reg_group(tag);
				if (-1 == temp)
				{
					continue;
				}
				else
				{
					uint8_t regGroup = temp & 0xFF;
					uint8_t fifoNum = (temp>>16) & 0xFF;
					int ret = regGroup | (fifoNum<<8) | (tag<<16);
					return ret;
				}
			}
		}
	}
}

int arm_csu_dma_1D_transfer(uint64_t addrSrc, uint64_t addrDst, uint32_t dataLen)
{
	uint8_t regGroup = 0;
	uint8_t fifoNum = 0;
	uint8_t tag = 0;
	uint32_t temp = get_free_channel();
//	printf("get free channle: 0x%x. \r\n", temp);

	regGroup = temp & 0xFF;
	fifoNum = (temp>>8) & 0xFF;
	tag = (temp>>16)&0xFF;

	uint8_t offset = regGroup << 6;
	// src reg
	VIR_ADDR_VAL(virMemAddr, AP_CSU_DMAADDRL0 + offset) = addrSrc & 0xFFFFFFFF;
	VIR_ADDR_VAL(virMemAddr, AP_CSU_DMAADDRH0 + offset) = addrSrc >> 32;
	VIR_ADDR_VAL(virMemAddr, AP_CSU_DMASIZEGRANALLNUM0 + offset) = (0xF<<28) | dataLen;
	// dst reg
	VIR_ADDR_VAL(virMemAddr, AP_CSU_DMAADDRL1 + offset) = addrDst & 0xFFFFFFFF;
	VIR_ADDR_VAL(virMemAddr, AP_CSU_DMAADDRH1 + offset) = addrDst >> 32;
	VIR_ADDR_VAL(virMemAddr, AP_CSU_DMASIZEGRANALLNUM1 + offset) = (0xE<<24) | dataLen;	// write global, size=0
	
	VIR_ADDR_VAL(virMemAddr, AP_CSU_CMDHIGHDATA) = 0;
	stCsuDmaCmdL dmaCmdL;
	dmaCmdL.dmaType = 1;
	dmaCmdL.idSrc = 0 + (regGroup<<1);
	dmaCmdL.idDst = 1 + (regGroup<<1);
	dmaCmdL.dmaTag = tag;
	//(uint32_t)(VIR_ADDR_VAL(virMemAddr, AP_CSU_CMDFIFO0+(fifoNum<<2))) = (uint32_t)(*((uint32_t*)(&dmaCmdL)));
	memcpy((uint32_t*)(virMemAddr+AP_CSU_CMDFIFO0+(fifoNum<<2)), (uint32_t*)(&dmaCmdL), 4);

	return tag;
}



