/**
 * @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 <stdbool.h>
#include <stdint.h>
#include "handler.h"
#include "kernel_api.h"

#if defined(CONFIG_ESE_TZ_CONTROL)
#include "gpio-ese-platform.h"
#include "gpio-ese.h"

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))

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 > 1 )
		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(ese_tzpc_configs[i].base_pa, ese_tzpc_configs[i].decprotx_clr);
			gpio_value = readr & ese_tzpc_configs[i].decprotx_bit;
			WRITE_SFR(ese_tzpc_configs[i].base_pa, ese_tzpc_configs[i].decprotx_clr, gpio_value);
		}


		if (secured) {
			WRITE_SFR(ese_tzpc_configs[i].base_pa, ese_tzpc_configs[i].decprotx_clr, ese_tzpc_configs[i].decprotx_bit);

			/* check TZPC Status */
			is_secure = READ_SFR(ese_tzpc_configs[i].base_pa, 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;
	volatile unsigned int vad[2];
	 
	for ( i = 0; i < ESE_GPIO_CONFIG_COUNT; i++) {
		readr_base = ese_gpio_configs[i].base_pa;

		for ( j = 0 ; j < ESE_GPIO_CFG_MOD_CNT; j++) {
			if (ese_gpio_configs[i].gpio_cfg[j].mask != 0) {
				readr = READ_SFR(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(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;

	readr_base = ese_gpio_configs[0].base_pa;
	gpio_set_ese_init(PM_SUSPEND);
	if (mode) {
		ret = READ_SFR(readr_base, ese_gpio_configs[0].gpio_cfg[ESE_GPIO_CFG_CON].offset);
	}

	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 (ese_gpio_configs == NULL)
		return ret;

	if (ESE_GPIO_CONFIG_COUNT < 1)
		return ret;

	readr_base = ese_gpio_configs[0].base_pa;
	gpio_set_ese_init(PM_RESUME);
	if (mode) {
		ret = READ_SFR(readr_base, ese_gpio_configs[0].gpio_cfg[ESE_GPIO_CFG_CON].offset);
	}

	return ret;
}
#endif
