/* 2012-09-16: File added and changed by Sony Corporation */
/*
 * icx_key.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 "icx_io.h"
#include "icx_pwc.h"
#include "icx_adc.h"
#include "icx_key.h"

/****************/
/*@ definitions */
/****************/

/* key balue save address & magic */
#define KEY_VALUE_SAVE_ADDR  0xF0000104
#define KEY_VALUE_SAVE_MAGIC 0x794B0000 /* "Ky" */
#define KEY_VALUE_SAVE_MASK  0x0000FFFF

/* gpio */
#define GPIO_XHOLD    PWC_GPIO_IO01
#define GPIO_NC_ON    PWC_GPIO_IO02
#define GPIO_KEY_WAKE PWC_GPIO_IO06

/* pull value */
#define GPPUPD_XHOLD        (PWC_GPIO_GPPUPD_OPEN << PWC_GPIO_GPPUPD_SFT_IO01)
#define GPPUPD_XHOLD_MSK    (PWC_GPIO_GPPUPD_MSK  << PWC_GPIO_GPPUPD_SFT_IO01)
#define GPPUPD_NC_ON        (PWC_GPIO_GPPUPD_OPEN << PWC_GPIO_GPPUPD_SFT_IO02)
#define GPPUPD_NC_ON_MSK    (PWC_GPIO_GPPUPD_MSK  << PWC_GPIO_GPPUPD_SFT_IO02)
#define GPPUPD_KEY_WAKE     (PWC_GPIO_GPPUPD_OPEN << PWC_GPIO_GPPUPD_SFT_IO06)
#define GPPUPD_KEY_WAKE_MSK (PWC_GPIO_GPPUPD_MSK  << PWC_GPIO_GPPUPD_SFT_IO06)

/* key value */
#define KEY_VALUE_DEFAULT 0xFF /* not pressed  */

/* hold */
#define KEY_HOLD_CHAT_INTERVAL 30000 /* 30ms   */

/* others */
#define KEY0_SFT 8
#define KEY_NUM  2

/********************/
/*@ local variables */
/********************/

/* key code table */
const static unsigned char key_value_table[] = {
#ifdef CONFIG_ICX
	29, 61, 93, 125, 158, 192, 224, 247
#else
    0x0a, 0x1f, 0x3a, 0x56, 0x74, 0x99, 0xbe, 0xe8
#endif
};

static unsigned int boot_key_value[ADC_CH_NUM] = {0};

/********************/
/*@ global routines */
/********************/

/*-----------------------------------------------------------------------------
 * Function	: key_init
 * Abstruct : Initialize KEY driver
 * Input    : none
 * Return   : none
 *----------------------------------------------------------------------------*/
void key_init(void)
{
	unsigned int val;
	int ch;
	int ret;

	/* set input */
	pwc_write(PWC_GPIO_IOSEL1, PWC_GPIO_IOSEL_IN, (GPIO_KEY_WAKE | GPIO_XHOLD | GPIO_NC_ON));

	/* set pull */
	pwc_write(PWC_GPIO_GPPUPD1, (GPPUPD_KEY_WAKE | GPPUPD_XHOLD), (GPPUPD_KEY_WAKE_MSK | GPPUPD_XHOLD_MSK));

	for (ch = ADC_CH_3; ch <= ADC_CH_8; ch++) {

		ret = adc_read(ch, &val);
		if (ret < 0) {
			boot_key_value[ch] = 0xFF;
			continue;
		}

		boot_key_value[ch] = val;
	}
}

/*-----------------------------------------------------------------------------
 * Function	: key_get_key_code
 * Abstruct : get the key code
 * Input    : key value 
 * Return   : key code
 *----------------------------------------------------------------------------*/
unsigned int key_get_key_code(unsigned int key_value)
{
	unsigned int value;
	unsigned int code;
	int i;
	int j;

	code = 0;

	for (i = 0; i < KEY_NUM; i++ ) {

		value = (key_value >> (i * 8)) & 0x00FF;

		for (j = 0; j < 8; j++) {

			if (value <= key_value_table[j]) {
				code = code | (0x1 << j) << (i * 8);
				break;
			}
		}
	}

	return code;
}

/*-----------------------------------------------------------------------------
 * Function	: key_get_key_value
 * Abstruct : Get key A/D value
 * Input    : none
 * Return   : key value
 *          :   bit 15-8: KEY0
 *          :   bit  7-0: KEY1
 *----------------------------------------------------------------------------*/
unsigned int key_get_key_value()
{
	unsigned int key_value;
	unsigned int key_ad;
	int sch;
	int i;

	sch = ADC_CH_7;

	key_value = 0;

	for (i = 0; i < KEY_NUM; i++) {
		if (adc_read((sch + i), &key_ad) < 0) {
			key_ad = KEY_VALUE_DEFAULT;
		}

		key_value <<= KEY0_SFT;
		key_value |= (key_ad & 0xFF);
	}

	return key_value;
}

/*-----------------------------------------------------------------------------
 * Function	: key_get_boot_key_value
 * Astruct  : Get key A/D value
 * Input    : none
 * Return   : key value
 *          :   bit 15-8: KEY0
 *          :   bit  7-0: KEY1
 *----------------------------------------------------------------------------*/
unsigned int key_get_boot_key_value()
{
	unsigned int key_value;

	key_value = (boot_key_value[ADC_CH_7] << KEY0_SFT) | boot_key_value[ADC_CH_8];

	return key_value;
}

/*-----------------------------------------------------------------------------
 * Function	: key_save_key_value
 * Abstruct : Save key A/D value to SRAM
 * Input    : key_value
 * Return   : none
 *----------------------------------------------------------------------------*/
void key_save_key_value(unsigned int key_value)
{
	outl( (KEY_VALUE_SAVE_MAGIC | (key_value & KEY_VALUE_SAVE_MASK)), KEY_VALUE_SAVE_ADDR);
}

/*-----------------------------------------------------------------------------
 * Function	: key_get_hold_state
 * Abstruct : Get hold ON/OFF
 * Input    : [OUT] elapsed time
 * Return   : hold_state : 1=HOLD ON / 0=HOLD OFF
 *----------------------------------------------------------------------------*/
unsigned int key_get_hold_state(unsigned int *elapsed_time)
{
	unsigned char hold_cur;
	unsigned char hold_pre = 0xFF;
	unsigned int  count	   = 0;

	/* read HOLD state */
	pwc_read(PWC_GPIO_IOIN1, &hold_cur);
	hold_cur &= GPIO_XHOLD;

	/* anti-chattering */
	while (hold_cur != hold_pre) {

		udelay(KEY_HOLD_CHAT_INTERVAL);
		count++;

		hold_pre = hold_cur;

		/* read HOLD state */
		pwc_read(PWC_GPIO_IOIN1, &hold_cur);
		hold_cur &= GPIO_XHOLD;
	}

	if (elapsed_time)
		*elapsed_time = count * KEY_HOLD_CHAT_INTERVAL;

	return (hold_cur ? KEY_HOLD_OFF : KEY_HOLD_ON);
}

#endif	/* CONFIG_ICX || CONFIG_ICD */ 
