/*******************************************************************************
 * Copyright (c) 2018 Renesas Electronics Corporation. All rights reserved.
 *
 * DESCRIPTION   : remap driver
 ******************************************************************************/

#include <stdint.h>
#include <remap.h>
#include <remap_register.h>
#include <mem_io.h>
#include <log.h>


typedef struct {
	uint8_t number;
	uint32_t address;
}REMAP_ADDRESS_TABLE;

#define REMAP_TBL_MAX	(sizeof(remap_tbl)/sizeof(remap_tbl[0]))

static REMAP_ADDRESS_TABLE remap_tbl[] = {
	[0] =	{0xFF,	0x00000000U},
	[1] =	{0xFF,	0x00000000U},
	[2] =	{0xFF,	0x00000000U},
	[3] =	{0xFF,	0x00000000U},
	[4] =	{0xFF,	0x00000000U},
	[5] =	{0xFF,	0x00000000U},
	[6] =	{6,	0xE7200000U},
	[7] =	{7,	0xE6400000U},
	[8] =	{8,	0xFFC10000U},
	[9] =	{9,	0xE6C00000U},
	[10] =	{10,	0xFFC10000U},
	[11] =	{11,	0xEE200000U},
	[12] =	{12,	0xE6200000U},
	[13] =	{13,	0xE6000000U},
	[14] =	{14,	0xE6600000U},
	[15] =	{15,	0xEB200000U},
};

void remap_write_SICREMAP2M(uint32_t num, uint32_t value)
{
	/* Write-Protected Register Write Procedure */
	do {
		mem_write32(ICUMX_PROT0PCMD, PROTCMD_START);
		mem_write32(SICREMAP2M(num), value);
		mem_write32(SICREMAP2M(num), ~value);
		mem_write32(SICREMAP2M(num), value);
	} while (mem_read32(ICUMX_PROT0PS) == PROTS0ERR);
}

uint32_t remap_register(uint32_t addr, uint32_t size, uint32_t *remap_addr)
{
	uint32_t loop;
	uint32_t loop2;
	uint32_t map_num;
	uint32_t set_addr;

	for (loop = 0; loop < REMAP_TBL_MAX; ++loop) {
		if (0xFF == remap_tbl[loop].number) {
			map_num = (addr & REMAP_2M_MASK) + size;
			map_num = (map_num + REMAP_2M_MASK)
				/ (1 << REMAP_2M_BITS);
			for (loop2 = 0; loop2 < map_num; ++loop2) {
				if (0xFF != remap_tbl[loop].number) {
					break;
				}
			}
			if (map_num <= loop2) {
				break;
			}
		}
	}

	if (REMAP_TBL_MAX <= loop) {
		return 2;
	}

	__asm__ __volatile__ ("SYNCM");

	set_addr = addr & ~REMAP_2M_MASK;
	for (loop2 = loop; loop2 < (map_num + loop); ++loop2) {
		remap_tbl[loop2].address = set_addr;
		remap_tbl[loop2].number = loop;
		remap_write_SICREMAP2M(loop2, set_addr);
		set_addr += (1 << REMAP_2M_BITS);
	}

	__asm__ __volatile__ ("SYNCM");

	*remap_addr = ICU_REMAP_CALC(loop);
	*remap_addr += addr & REMAP_2M_MASK;
	return 0;
}

uint32_t remap_unregister(uint32_t remap_addr)
{
	uint32_t loop;
	uint32_t loop2;
	uint32_t remove_num;

	for (loop = 0; loop < REMAP_TBL_MAX; ++loop) {
		if ((0xFF != remap_tbl[loop].number)
			&& ((ICU_REMAP_CALC(loop) <= remap_addr)
				&& (remap_addr < ICU_REMAP_CALC(loop+1)))) {
			break;
		}
	}

	if (REMAP_TBL_MAX <= loop) {
		return 1;
	}
	__asm__ __volatile__ ("SYNCM");

	remove_num = remap_tbl[loop].number;
	for (loop2 = remove_num; loop2 < REMAP_TBL_MAX; ++loop2) {
		if (remap_tbl[loop2].number != remove_num) {
			break;
		}
		remap_tbl[loop2].address = 0;
		remap_tbl[loop2].number = 0xFF;
		remap_write_SICREMAP2M(loop2, ICU_REMAP_CALC(loop2));
	}

	__asm__ __volatile__ ("SYNCM");
	return 0;
}

uint32_t remap_get_phys_addr(uint32_t remap_addr)
{
	uint32_t phys_addr;
	uint32_t reg;

	if ((ICU_REMAP0 > remap_addr)
		|| (ICU_REMAP_CALC(REMAP_REG_MAX) <= remap_addr)) {
		ERROR("remap address Error\n");
		panic();
	}

	phys_addr = remap_addr - ICU_REMAP0;
	phys_addr >>= REMAP_2M_BITS;
	reg = SICREMAP2M(phys_addr);
	reg = mem_read32(reg);
	phys_addr = reg + (remap_addr & REMAP_2M_MASK);

	return phys_addr;
}

uint32_t remap_get_remap_addr(uint32_t phys_addr)
{
	uint32_t remap_addr;
	uint32_t reg;
	uint32_t loop;

	for (loop = 0; loop < REMAP_REG_MAX; loop++) {
		reg = mem_read32(SICREMAP2M(loop));
		if ((reg <= phys_addr)
			&& (phys_addr <= (reg + REMAP_2M_MASK))) {
			break;
		}
	}

	if (REMAP_REG_MAX <= loop) {
		ERROR("remap address Error\n");
		panic();
	}

	remap_addr = ICU_REMAP_CALC(loop);
	remap_addr += phys_addr - reg;

	return remap_addr;
}
