/**
 * @file  gpio-ese.c
 * @brief GPIO API for Fastcall driver(ese)
 *
 * Copyright (c) 2014-2015 Samsung Electronics Co., Ltd.
 *
 * This software is proprietary of Samsung Electronics.
 * No part of this software, either material or conceptual may be copied
 * or distributed, transmitted, transcribed, stored in a retrieval system
 * or translated into any human or computer language in any form by any means,
 * electronic, mechanical, manual or otherwise, or disclosed to third parties
 * without the express written permission of Samsung Electronics.
 */

#include "drStd.h"
#include "drMobicore.h"
#include "DrApi/DrApiFastCall.h"

#include "gpio-ese-platform.h"
#include "gpio-ese.h"

#if TBASE_API_LEVEL >= 5
#include "DrApi/DrApiMm.h"
#include "DrApi/DrApiMmExt.h"
#include "dbg.h"
#include "DrApi/DrApiError.h"
#endif

ese_tzpc_config *ese_tzpc_configs;
ese_gpio_config *ese_gpio_configs;

static int is_ese_secured;
#define GET_MASK_VALUE(value, mask, shift) (((value) & (mask)) >> (shift))
/************************************************************************/
/* TZPC control functions                                               */
/* mode bit0: spi protection, bit1: gpio protection, bit4~7: spi port#	*/
/* 	bit 8~32: sleep gpio configuration				*/
/*									*/
/*      sleep configuration 1						*/
/*		bit8~11(4): CON, bit12(1): DAT, bit13~14(2):PUD		*/
/*	sleep configuration 2						*/
/*		bit16~19(4): CON, bit20(1): DAT, bit21~22(2):PUD	*/
/*	bit24: enable sleep configuration 1				*/
/*	bit25: enable sleep configuration 2				*/
/*	bit28~29: GPIO config selection					*/
/*	bit30~31: TZPC config selection					*/
/************************************************************************/

uint32_t make_repeated_pattern(uint32_t value, uint32_t size_of_bit)
{
        uint32_t ret = 0;
        uint32_t loop_count = 32 / size_of_bit;

        while(loop_count--)
                ret = (ret<< size_of_bit) + value;

        return ret;
}

uint32_t set_tzpc_ese_secure(uint32_t mode) {
	uint32_t ret = 0;
	uint32_t is_secure;
	uint32_t config_value;
	uint32_t cfg_index;
	uint32_t spi_port;
	bool spi_secure;
	bool gpio_secure;
	uint32_t config_mask;
	bool secured;
	uint32_t readr, gpio_value;
	int i;

	if (is_ese_secured)
		return EBUSY;

	is_ese_secured = 1;
	spi_secure = 0x1 & mode;
	gpio_secure = 0x2 & mode;
	spi_port = (mode >> 4) & 0xF;

	cfg_index = GET_MASK_VALUE(mode, 0xC0000000, 30);
	ese_tzpc_configs = get_ese_tzpc_config(cfg_index);

	cfg_index = GET_MASK_VALUE(mode, 0x30000000, 28);
	ese_gpio_configs = get_ese_gpio_config(cfg_index);
	
	if (ese_tzpc_configs == NULL || ese_gpio_configs == NULL)
		return ret;

	config_mask = GET_MASK_VALUE(mode, 0x0F000000, 24);
	if (config_mask > 3)
		config_mask = 0;

	for(i=0; i < 2; i++) {
		if (i >= ESE_TZPC_CONFIG_COUNT) break;

		if (config_mask & (1 << i)) {
			config_value = GET_MASK_VALUE(mode, (0x00000F00 << i*8), 8 + i*8);
			config_value = make_repeated_pattern(config_value, 0x4);
			ese_gpio_configs[i].gpio_cfg[ESE_GPIO_CFG_CON].sleep_value =
				config_value & ese_gpio_configs[i].gpio_cfg[ESE_GPIO_CFG_CON].mask;

			config_value = GET_MASK_VALUE(mode, (0x00001000 << i*8), 12 + i*8);
			config_value = make_repeated_pattern(config_value, 0x1);
			ese_gpio_configs[i].gpio_cfg[ESE_GPIO_CFG_DAT].sleep_value =
				config_value & ese_gpio_configs[i].gpio_cfg[ESE_GPIO_CFG_DAT].mask;

			config_value = GET_MASK_VALUE(mode, (0x00006000 << i*8), 13 + i*8);
			config_value = make_repeated_pattern(config_value, 0x2);
			ese_gpio_configs[i].gpio_cfg[ESE_GPIO_CFG_PUD].sleep_value =
				config_value & ese_gpio_configs[i].gpio_cfg[ESE_GPIO_CFG_PUD].mask;
		}
	}

	for(i=0; i < ESE_TZPC_CONFIG_COUNT; i++)
	{
		secured = false;

		if (spi_secure && ese_tzpc_configs[i].tzpc_type == ESE_SPI_PROTECTION)
			secured = true;
		if (gpio_secure && ese_tzpc_configs[i].tzpc_type == ESE_GPIO_PROTECTION)
			secured = true;

		if (gpio_secure && ese_tzpc_configs[i].tzpc_type == ESE_GPIO_PERI_PROTECTION) {
			readr = read_sfr_value(ese_tzpc_configs[i].base_va +
					ese_tzpc_configs[i].decprotx_clr);
			gpio_value = readr & ~(ese_tzpc_configs[i].decprotx_bit);
			write_sfr_value(ese_tzpc_configs[i].base_va +
					ese_tzpc_configs[i].decprotx_clr, gpio_value);
		}

		if (secured) {
			write_sfr_value(ese_tzpc_configs[i].base_va + ese_tzpc_configs[i].decprotx_clr,
					ese_tzpc_configs[i].decprotx_bit);

			/* check TZPC Status */
			is_secure = read_sfr_value(ese_tzpc_configs[i].base_va + ese_tzpc_configs[i].decprotx_stat);
			is_secure &= ese_tzpc_configs[i].decprotx_bit;
			if (is_secure != TZPC_SECURE)
				ret = 1;
		}
	}
	gpio_set_ese_init(PM_SUSPEND);

	return ret;
}

/************************************************************************/
/* GPIO control functions						*/
/************************************************************************/

uint32_t gpio_set_ese_init(enum pm_mode mode) {
	/* Setting GPIO SFR for SPI function */
	uint32_t readr, readr_base, gpio_value;
	int i, j;
	
	for (i=0; i < ESE_GPIO_CONFIG_COUNT; i++) {
		readr_base = ese_gpio_configs[i].base_va;
		for (j=0 ; j < ESE_GPIO_CFG_MOD_CNT; j++)
			if (ese_gpio_configs[i].gpio_cfg[j].mask) {
				readr = read_sfr_value(readr_base
						+ ese_gpio_configs[i].gpio_cfg[j].offset)
					& (~ese_gpio_configs[i].gpio_cfg[j].mask);

				if (mode == PM_SUSPEND)
					gpio_value = ese_gpio_configs[i].gpio_cfg[j].sleep_value;
				else
					gpio_value = ese_gpio_configs[i].gpio_cfg[j].active_value;

				write_sfr_value(readr_base
						+ ese_gpio_configs[i].gpio_cfg[j].offset,
						readr | gpio_value);
			}
	}

	return 0;
}

/* mode 1: return pin setting */
uint32_t suspend_ese_gpio_regs(uint32_t mode) {
	uint32_t ret = 0;
	uint32_t readr_base;

	if (!is_ese_secured)
		return EBUSY;

	if (ESE_GPIO_CONFIG_COUNT < 1)
		return ret;

#if defined(CONFIG_EXYNOS9810)
	/* NXP p80T */
	if ((mode & 0xFFFF) == 1) {
		ese_gpio_configs = get_ese_gpio_config(1);
	}
#endif
	if (ese_gpio_configs == NULL)
		return ret;

	readr_base = ese_gpio_configs[0].base_va;
	gpio_set_ese_init(PM_SUSPEND);
	if (mode & 0xFFFF0000) { /* for debugging */
		ret = read_sfr_value(readr_base
					+ ese_gpio_configs[0].gpio_cfg[ESE_GPIO_CFG_CON].offset);
	}

	return ret;
}

uint32_t suspend_ese_gpio_regs_ot(uint32_t mode) {
	uint32_t ret = 0;
#if defined(CONFIG_EXYNOS7420) || defined(CONFIG_EXYNOS8890)
	int i;
	uint32_t readr_base;

	if (!is_ese_secured)
		return EBUSY;

	if (ese_gpio_configs == NULL)
		return ret;

	if (ESE_GPIO_CONFIG_COUNT < 1)
		return ret;

	readr_base = ese_gpio_configs[0].base_va;
	ese_gpio_configs[0].gpio_cfg[ESE_GPIO_CFG_CON].sleep_value = 0x0000;
	ese_gpio_configs[0].gpio_cfg[ESE_GPIO_CFG_PUD].sleep_value = 0x00FF;
	gpio_set_ese_init(PM_SUSPEND);
	if (mode) {
		ret = read_sfr_value(readr_base
					+ ese_gpio_configs[0].gpio_cfg[ESE_GPIO_CFG_PUD].offset);
	}
#endif
	return ret;
}

uint32_t resume_ese_gpio_regs(uint32_t mode) {
	uint32_t ret = 0;
	uint32_t readr_base;

	if (!is_ese_secured)
		return EBUSY;

#if defined(CONFIG_EXYNOS9810)
	/* NXP p80T */
	if ((mode & 0xFFFF) == 1) {
		ese_gpio_configs = get_ese_gpio_config(1);
	}
#endif

	if (ese_gpio_configs == NULL)
		return ret;

	if (ESE_GPIO_CONFIG_COUNT < 1)
		return ret;

	readr_base = ese_gpio_configs[0].base_va;
	gpio_set_ese_init(PM_RESUME);
	if (mode & 0xFFFF0000) { /* for debugging */
		ret = read_sfr_value(readr_base
					+ ese_gpio_configs[0].gpio_cfg[ESE_GPIO_CFG_CON].offset);
	}

	return ret;
}

#if TBASE_API_LEVEL >= 5
uint32_t ese_sec_map()
{
	int i, j;
	addr_t ese_tzpc_base_va[3][ESE_TZPC_CONFIG_COUNT];
	addr_t ese_gpio_base_va[3][ESE_GPIO_CONFIG_COUNT];
	drApiResult_t ret = DRAPI_OK;

	for(j=0; j < 3; j++) {
		ese_tzpc_configs = get_ese_tzpc_config(j);
		if (ese_tzpc_configs == NULL)
			break;

		for(i=0; i < ESE_TZPC_CONFIG_COUNT; i++)
		{
			if (ese_tzpc_configs[i].base_pa && ese_tzpc_configs[i].base_va == 0) {
				ret = drApiMapPhysicalBuffer((uint64_t)ese_tzpc_configs[i].base_pa,
						SIZE_4KB,
						MAP_READABLE | MAP_WRITABLE | MAP_IO,
						(void **)&ese_tzpc_base_va[j][i]);
				if (ret != DRAPI_OK) {
					sec_errh("ese_sec_map(): ese_base_va(tzpc) error: ", ret);
					return ret;
				}
				ese_tzpc_configs[i].base_va = (uint32_t *)ese_tzpc_base_va[j][i];
				sec_errh("ese_sec_map(): ese_tzpc_configs[i].base_va = ", ese_tzpc_configs[i].base_va);
			}
		}
	}

	for(j=0; j < 3; j++) {
		ese_gpio_configs = get_ese_gpio_config(j);
		if (ese_gpio_configs == NULL)
			break;	

		for(i=0; i < ESE_GPIO_CONFIG_COUNT; i++)
		{
			if (ese_gpio_configs[i].base_pa && ese_gpio_configs[i].base_va == 0) {
				ret = drApiMapPhysicalBuffer((uint64_t)ese_gpio_configs[i].base_pa,
						SIZE_4KB,
						MAP_READABLE | MAP_WRITABLE | MAP_IO,
						(void **)&ese_gpio_base_va[j][i]);
				if (ret != DRAPI_OK) {
					sec_errh("ese_sec_map(): ese_base_va(gpio) error: ", ret);
					return ret;
				}
				ese_gpio_configs[i].base_va = (uint32_t *)ese_gpio_base_va[j][i];
			}
		}
	}

	return 0;
}

#else
uint32_t ese_sec_map(uint32_t virtaddr, size_t size)
{
	int i, j;
	uint32_t ese_base_va = virtaddr;

	for(j=0; j < 3; j++) {
		ese_tzpc_configs = get_ese_tzpc_config(j);
		if (ese_tzpc_configs == NULL)
			break;

		for(i=0; i < ESE_TZPC_CONFIG_COUNT; i++)
		{
			if (ese_tzpc_configs[i].base_pa && ese_tzpc_configs[i].base_va == 0) {
				ese_tzpc_configs[i].base_va = ese_base_va;
				sec_map(ese_base_va, ese_tzpc_configs[i].base_pa, size);
				ese_base_va += 0x1000;
			}
		}
	}

	for(j=0; j < 3; j++) {
		ese_gpio_configs = get_ese_gpio_config(j);
		if (ese_gpio_configs == NULL)
			break;	

		for(i=0; i < ESE_GPIO_CONFIG_COUNT; i++)
		{
			if (ese_gpio_configs[i].base_pa && ese_gpio_configs[i].base_va == 0) {
				ese_gpio_configs[i].base_va = ese_base_va;
				sec_map(ese_base_va, ese_gpio_configs[i].base_pa, size);
				ese_base_va += 0x1000;
			}
		}
	}

	return ese_base_va;
}
#endif
