/* 2012-09-16: File added and changed by Sony Corporation */
/*
 * icx_nvp.c
 *
 * Copyright 2011 Sony Corporation.
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
 *
 */

#include <common.h>

#if defined(CONFIG_ICX) || defined(CONFIG_ICD)

#include <asm/errno.h>

#include "mini-boot/include/mini_config.h"		/* for SRAM address		*/
#include "mini-boot/include/mmc.h"				/* for MMC_BLOCKLEN_VAL	*/
#include "mini-boot/mmc/mmc.h"					/* for MBRecord 		*/

#include "icx_io.h"
#include "icx_nvp.h"
#include "../../../../kernel/top/arch/arm/mach-emxx/include/mach/icx_emmc_map.h"

/****************/
/*@ Definitions */
/****************/

/* sector information */
#define NVP_TABLE_SECTOR				16
#define NVP_DATA_SECTOR_MIN				64
#define NVP_DATA_SECTOR_MAX				32767

/* image data */
#define NVP_IMAGE_CLUSTER_NUM_MAX		32
#define NVP_IMAGE_SECTOR_PER_CLUSTER    16

/* macros */
#define c2s(c)							(c * NVP_IMAGE_SECTOR_PER_CLUSTER)

/* U-Boot parameter area index */
enum {
	NVP_UBOOT_PARAM_SYSINFO = 0,
	NVP_UBOOT_PARAM_PASSWORD,
	NVP_UBOOT_PARAM_UPDATE,
	NVP_UBOOT_PARAM_BEEP,
	NVP_UBOOT_PARAM_HOLD,
	NVP_UBOOT_PARAM_RTC,
	NVP_UBOOT_PARAM_NUM,
};

/* flag information */
#define NVP_FLAG_FW_UPDATE			0x70555766
#define NVP_FLAG_HOLD_MODE			0x64485074
#define NVP_FLAG_BEEP				0x70454562

/* system information */
#define SYSINFO_UPPER_SAVE_ADDR		0xF0000120
#define SYSINFO_LOWER_SAVE_ADDR		0xF0000124
#define SYSINFO_UPPER_MAGIC			0x31530000	/* S1 */
#define SYSINFO_LOWER_MAGIC			0x32530000	/* S2 */
#define SYSINFO_UPPER_SHIFT			16
#define SYSINFO_VAL_MASK			0x0000FFFF

/* misc flag */
#define MISC_FLAG_SAVE_ADDR			0xF0000108
#define MISC_FLAG_SAVE_MAGIC		0x664D0000	/* Mf */
#define MISC_FLAG_SAVE_MAGIC_MASK	0xFFFF0000


/***************/
/*@ Structures */
/***************/

/* U-Boot parameter typedef */
typedef struct {
	unsigned int	sysinfo;
	unsigned char   password[32];
	unsigned int    update_flag;
	unsigned int    beep_flag;
	unsigned int    hold_mode;
	int             rtc_alarm[2];
} nvp_uboot_parameter_t;

/* U-Boot parameter info */
typedef struct {
	void *param;    						/* address to variable      */
	int  size;      						/* data size [byte]         */
} nvp_uboot_param_info_t;


/*********************/
/*@ Global variables */
/*********************/

/* area offset table */
static unsigned int nvp_area_sector_table[NVP_ID_NUM] = { 
	 0,							/* area 00 table : u-boot parameter     */
	 8,          				/* area 08 table : boot image           */
	 9,          				/* area 09 table : hold image           */
	10,         				/* area 10 table : low battery image    */
};

/* U-Boot parameter */
static nvp_uboot_parameter_t nvp_uboot_param;

static nvp_uboot_param_info_t nvp_uboot_param_table[NVP_UBOOT_PARAM_NUM] = {
/*   +-------------------------------------------- address to variable  */
/*   |                              +------------- size [byte]          */
/*   |                              |                                   */
	{&nvp_uboot_param.sysinfo,      4},     	/* System information   */
	{&nvp_uboot_param.password[0], 32},     	/* U-Boot password      */
	{&nvp_uboot_param.update_flag,  4},  	    /* F/W update flag      */
	{&nvp_uboot_param.beep_flag,    4},    		/* Beep OK flag         */
	{&nvp_uboot_param.hold_mode,    4},     	/* Hold mode            */
	{&nvp_uboot_param.rtc_alarm[0], 8},     	/* RTC alarm info       */
};

/* initialized flag */
static int nvp_initialized = 0;


/***************/
/*@ Prototypes */
/***************/

static int nvp_get_uboot_param(nvp_uboot_param_info_t *info, int sector);


/********************/
/*@ Global routines */
/********************/

/*-----------------------------------------------------------------------------
 * Function	: nvp_init
 * Abstruct : Initialize NVP driver(get U-Boot parameters)
 * Input    : none
 * Return   : none
 *----------------------------------------------------------------------------*/
void nvp_init(void)
{
#ifdef CONFIG_EMXX_EMMCBOOT
	struct boot_info	emmc_info;
	char				emmc_buf[MMC_BLOCKLEN_VAL];
	int					sector_base;
	int					sector;
	unsigned int	   *sector_buf;
	int					ret;
	int					i;

	if (nvp_initialized)
		return;

	/* get NVP control sector head */
	sector_base = ((MBRecord *)ROMBOOT_WORK_START)->partitionTable[0].firstSectorNumbers;
	sector_base += NVP_AREA_START;

	/************************/
	/* get U-Boot parameter */
	/************************/
	memset(&nvp_uboot_param, 0, sizeof(nvp_uboot_param));
	nvp_uboot_param.sysinfo = NVP_SYSINFO_INVALID;

	sector = sector_base + NVP_TABLE_SECTOR + nvp_area_sector_table[NVP_ID_UBOOT_PARAM];

	mmc_set_param(emmc_info, (unsigned long)&emmc_buf, sector, MMC_BLOCKLEN_VAL, 0);

	ret = mmc_read(&emmc_info);
	if (ret != 0)
		return;

	sector_buf = (unsigned int *)emmc_buf;

	for (i = 0; i < NVP_UBOOT_PARAM_NUM; i++) {
		if ((NVP_DATA_SECTOR_MIN <= sector_buf[i]) && (sector_buf[i] <= NVP_DATA_SECTOR_MAX))
			nvp_get_uboot_param(&nvp_uboot_param_table[i], (sector_base + sector_buf[i]));
	}   

	nvp_initialized = 1;
#else
	if (nvp_initialized)
		return;

	memset(&nvp_uboot_param, 0, sizeof(nvp_uboot_param));
	nvp_uboot_param.sysinfo = NVP_SYSINFO_INVALID;

	nvp_initialized = 1;
#endif /* CONFIG_EMXX_EMMCBOOT */
}

/*-----------------------------------------------------------------------------
 * Function	: nvp_get_sysinfo
 * Abstruct : Get system information
 * Input    : none
 * Return   : system information
 *----------------------------------------------------------------------------*/
unsigned int nvp_get_sysinfo(void)
{
	if (nvp_initialized == 0)
		nvp_init();
	
	return nvp_uboot_param.sysinfo;
}

/*-----------------------------------------------------------------------------
 * Function	: nvp_get_sysinfo_mem
 * Abstruct : Get mem information
 * Input    : none
 * Return   : mem information
 *----------------------------------------------------------------------------*/
unsigned int nvp_get_sysinfo_mem(void)
{
	if (nvp_initialized == 0)
		nvp_init();
	
	if (nvp_uboot_param.sysinfo == NVP_SYSINFO_INVALID)
		return NVP_SYSINFO_INVALID;

	return (nvp_uboot_param.sysinfo & NVP_SYSINFO_MEM_MSK);
}


/*-----------------------------------------------------------------------------
 * Function	: nvp_get_sysinfo_lcd
 * Abstruct : Get LCD device information
 * Input    : none
 * Return   : LCD device information
 *----------------------------------------------------------------------------*/
unsigned int nvp_get_sysinfo_lcd(void)
{
	if (nvp_initialized == 0)
		nvp_init();

	if (nvp_uboot_param.sysinfo == NVP_SYSINFO_INVALID)
		return NVP_SYSINFO_INVALID;

	return (nvp_uboot_param.sysinfo & NVP_SYSINFO_LCD_MSK);
}

/*-----------------------------------------------------------------------------
 * Function	: nvp_get_sysinfo_dac
 * Abstruct : Get DAC device information
 * Input    : none
 * Return   : DAC device information
 *----------------------------------------------------------------------------*/
unsigned int nvp_get_sysinfo_dac(void)
{
	if (nvp_initialized == 0)
		nvp_init();

	if (nvp_uboot_param.sysinfo == NVP_SYSINFO_INVALID)
		return NVP_SYSINFO_INVALID;

	return (nvp_uboot_param.sysinfo & NVP_SYSINFO_DAC_MSK);
}

/*-----------------------------------------------------------------------------
 * Function	: nvp_get_alarm_date
 * Abstruct : Get RTC date(year/month/day/hour/minute)
 * Input    : year : buffer address
 *			: mdmh : buffer address
 * Return   : none
 *----------------------------------------------------------------------------*/
void nvp_get_alarm_date(int *year, int *mdmh)
{
	if (nvp_initialized == 0)
		nvp_init();

	*year = nvp_uboot_param.rtc_alarm[0];
	*mdmh = nvp_uboot_param.rtc_alarm[1];
}

/*-----------------------------------------------------------------------------
 * Function	: nvp_get_image_data
 * Abstruct : Get a image data
 * Input    : id   : area ID
 *			: buf  : address to destination area
 *			: size : size of the image data
 * Return   : none
 *----------------------------------------------------------------------------*/
void nvp_get_image_data(int id, char *buf, int size)
{
	struct boot_info emmc_info;
	unsigned short	 cluster_buf[MMC_BLOCKLEN_VAL/sizeof(short)];
	int  sector, sector_base;
	int	 remain;
	char *dst;
	int  ret;
	int  i, j;

	/******************/
	/* get area table */
	/******************/

	sector_base = ((MBRecord *)ROMBOOT_WORK_START)->partitionTable[0].firstSectorNumbers;
	sector_base += NVP_AREA_START;

	sector = sector_base + NVP_TABLE_SECTOR + nvp_area_sector_table[id];

	mmc_set_param(emmc_info, (unsigned long)&cluster_buf, sector, MMC_BLOCKLEN_VAL, 0);

	ret = mmc_read(&emmc_info);
	if (ret != 0)
		return;

	/*******************/
	/* read image data */
	/*******************/

	dst    = buf;
	remain = size;
	for (i = 0; i < NVP_IMAGE_CLUSTER_NUM_MAX; i++) {
		sector = c2s(cluster_buf[i]);

		if ((sector < NVP_DATA_SECTOR_MIN)
		 || ((sector + NVP_IMAGE_SECTOR_PER_CLUSTER) > NVP_DATA_SECTOR_MAX))
			return;

		sector += sector_base;

		for (j = 0; j < NVP_IMAGE_SECTOR_PER_CLUSTER; j++) {
			if (remain <= 0)
				return;

			emmc_info.addr  = (unsigned long)dst;
			emmc_info.block = sector;

			ret = mmc_read(&emmc_info);
			if (ret != 0)
				return;

			dst = (char *)((unsigned int)dst + MMC_BLOCKLEN_VAL);
			remain -= MMC_BLOCKLEN_VAL;
			sector++;
		}
	}
}

/*-----------------------------------------------------------------------------
 * Function	: nvp_get_pwd
 * Abstruct : Get a password
 * Input    : pwd_buf : buffer address
 * Return   : none
 *----------------------------------------------------------------------------*/
void nvp_get_pwd(char *pwd_buf, int size)
{
	memcpy(pwd_buf, nvp_uboot_param.password, size);
}

/*-----------------------------------------------------------------------------
 * Function	: nvp_check_fw_update
 * Abstruct : Check the F/W update flag
 * Input    : none
 * Return   : 1:F/W update / 0:normal
 *----------------------------------------------------------------------------*/
int nvp_check_fw_update(void)
{
	if (nvp_initialized == 0)
		nvp_init();

	return (nvp_uboot_param.update_flag == NVP_FLAG_FW_UPDATE);
}

/*-----------------------------------------------------------------------------
 * Function	: nvp_check_hold_mode
 * Abstruct : Check the Hold mode flag
 * Input    : none
 * Return   : 1:all / 0:only touch panel
 *----------------------------------------------------------------------------*/
int nvp_check_hold_mode(void)
{
	if (nvp_initialized == 0)
		nvp_init();

	if (nvp_uboot_param.hold_mode == NVP_FLAG_HOLD_MODE)
		return NVP_HOLD_MODE_TP;
	else
		return NVP_HOLD_MODE_ALL;
}

/*-----------------------------------------------------------------------------
 * Function	: nvp_check_beep_flag
 * Abstruct : Check the beep flag
 * Input    : none
 * Return   : 1:Beep ON / 0:Beep OFF
 *----------------------------------------------------------------------------*/
int nvp_check_beep_flag(void)
{
	if (nvp_initialized == 0)
		nvp_init();

	return (nvp_uboot_param.update_flag != NVP_FLAG_BEEP);
}

/*-----------------------------------------------------------------------------
 * Function	: nvp_save_sysinfo
 * Abstruct : Save system information to SRAM
 * Input    : none
 * Return   : none
 *----------------------------------------------------------------------------*/
void nvp_save_sysinfo(void)
{
	unsigned int val;

	if (nvp_uboot_param.sysinfo != NVP_SYSINFO_INVALID) {
		val = (nvp_uboot_param.sysinfo >> SYSINFO_UPPER_SHIFT) & SYSINFO_VAL_MASK;
		outl((SYSINFO_UPPER_MAGIC | val), SYSINFO_UPPER_SAVE_ADDR);

		val = nvp_uboot_param.sysinfo & SYSINFO_VAL_MASK;
		outl((SYSINFO_LOWER_MAGIC | val), SYSINFO_LOWER_SAVE_ADDR);
	}

	return;
}

/*-----------------------------------------------------------------------------
 * Function	: nvp_save_misc_flag
 * Abstruct : Save misc flags to SRAM
 * Input    : target	: kind of misc
 *			: value		: misc value
 * Return   : none
 *----------------------------------------------------------------------------*/
void nvp_save_misc_flag(unsigned int target, unsigned int value)
{
	unsigned int flag;

	flag = inl(MISC_FLAG_SAVE_ADDR);

	if ((flag & MISC_FLAG_SAVE_MAGIC_MASK) != MISC_FLAG_SAVE_MAGIC)
		flag=0;

	outl((MISC_FLAG_SAVE_MAGIC | (flag & ~target) | value), MISC_FLAG_SAVE_ADDR);

	return;
}

/*******************/
/*@ Local routines */
/*******************/

/*-----------------------------------------------------------------------------
 * Function	: nvp_get_uboot_param
 * Abstruct : Get U-Boot parameter
 * Input    : info		: buffer
 *			: sector	: NVP sector
 * Return   : 0:OK/-1:NG
 *----------------------------------------------------------------------------*/
static int nvp_get_uboot_param(nvp_uboot_param_info_t *info, int sector)
{
	struct boot_info	emmc_info;
	char				emmc_buf[MMC_BLOCKLEN_VAL];
	int					ret;

	mmc_set_param(emmc_info, (unsigned long)&emmc_buf, sector, MMC_BLOCKLEN_VAL, 0);

	ret = mmc_read(&emmc_info);
	if (ret != 0) {
		return -1;
	}

	/* save to info. buffer */
	memcpy(info->param, emmc_buf, info->size);

	return 0;
}


#endif /* CONFIG_ICX || CONFIG_ICD */
