/* 2012-09-16: File added and changed by Sony Corporation */
/* icx.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 "mini-boot/include/mini_config.h"
#include "mini-boot/include/mmc.h"
#include "mini-boot/include/icx_etc.h"
#include "mini-boot/mmc/mmc.h"

#include "reg.h"
#include "icx_io.h"
#include "icx_gpio.h"
#include "icx_spi.h"
#include "icx_pwc.h"
#include "icx_pmu.h"
#include "icx_adc.h"
#include "icx_key.h"
#include "icx_nvp.h"
#include "icx_lcdc.h"
#include "icx_lcd.h"
#include "icx_dac.h"
#include "icx_rtc.h"

/* for debug */
#define	ICX_DEBUG	0

#if ICX_DEBUG == 0
#define printf_dbg(fmt, arg...)
#else
#define printf_dbg(fmt, arg...)		printf(fmt, ## arg)
#endif


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

/* beep at play key push boot switch */
#define BEEP_AT_PLAY_KEY			0

/* linux image information */
#define LINUX_SIZE_OFFSET			4
#define LINUX_SUM_SIZE				4

#define REVISE_BOUNDARY(s, b)		((s + (b - 1)) & ~(b - 1))

/* event */
#define EVENT_TIMEOUT				0
#define EVENT_HOLD_CANCEL			1
#define EVENT_POWER_SUPPLY_CONNECT	2
#define EVENT_RTC_ALARM				3

/* hold key cancel check timeout */
#define HOLD_KEY_CANCEL_CHECK_COUNT          3000 /* 10ms * 500 = 30s             */
#define HOLD_KEY_CANCEL_CHECK_INTERVAL       7700 /* 10ms * 500 = 30s (- KEYAD*2) */

/* power supply connect - check timeout */
#define POWER_SUPPLY_CONNECT_CHECK_COUNT      200 /* 50ms * 200 = 10s */
#define POWER_SUPPLY_CONNECT_CHECK_INTERVAL	50000 /* 50ms * 200 = 10s */

/* beep pattern */
#define LOWBATT_BEEP_ON1   			0	/* ON  150ms	*/
#define LOWBATT_BEEP_OFF1  			3	/* OFF  50ms	*/
#define LOWBATT_BEEP_ON2   			4	/* ON  150ms	*/
#define LOWBATT_BEEP_OFF2  			7	/* OFF 500ms	*/
#define LOWBATT_BEEP_LOOP			17	/* total caount */


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

static int boot_sequence_install(
	unsigned int power_supply,
	unsigned int boot_factor,
	unsigned int key_value,
	unsigned int key_code
);

static int boot_sequence_adaptor(
	unsigned int power_supply,
	unsigned int boot_factor,
	unsigned int key_value,
	unsigned int key_code
);

static int boot_sequence_usb(
	unsigned int power_supply,
	unsigned int boot_factor,
	unsigned int key_value,
	unsigned int key_code
);

static int boot_sequence_rtc(
	unsigned int power_supply,
	unsigned int boot_factor,
	unsigned int key_value,
	unsigned int key_code
);

static int boot_sequence_key(
	unsigned int power_supply,
	unsigned int boot_factor,
	unsigned int key_value,
	unsigned int key_code
);

static int boot_sequence_hold(
	unsigned int power_supply,
	unsigned int boot_factor,
	unsigned int key_value,
	unsigned int key_code
);

static int boot_sequence_cradle(
	unsigned int power_supply,
	unsigned int boot_factor,
	unsigned int key_value,
	unsigned int key_code
);

static int boot_sequence_others(
	unsigned int power_supply,
	unsigned int boot_factor,
	unsigned int key_value,
	unsigned int key_code
);

static int  wait_hold_cancel(void);
static int  wait_power_supply_connect(void);
static int  is_now_alarm_date(void);
static void show_boot_image(void);
static int  beep_at_play_key(unsigned int key_code);
static void power_off(void);


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

int	config_cons_index;

unsigned int icx_inst_initrd_size = 0;
int			 icx_is_bb;


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

/*-----------------------------------------------------------------------------
 * Function : icx_board_init
 * Abstruct : Initialize ICX board
 * Input    : none
 * Returen  : none
 *----------------------------------------------------------------------------*/
void icx_board_init(void)
{
	/******************/
	/* get borad info */
	/******************/
	icx_inst_initrd_size = inl(ICX_IS_INSTALL_ADDR);
	icx_is_bb		     = inl(ICX_IS_BB_ADDR);

#ifdef CONFIG_ICX
	/******************/
	/* initialize I/O */
	/******************/

	/* TP_XRST */
	outl(( inl(CHG_PINSEL_G000)                | 0x00000020), CHG_PINSEL_G000);
	outl( (inl(CHG_PULL0      ) & ~0x00F00000)              , CHG_PULL0      );
	gpio_set_direction(GPIO_P005, GPIO_OUT);
	gpio_set_value(GPIO_P005, 1);
#endif
}

/*-----------------------------------------------------------------------------
 * Function	: icx_init
 * Abstruct	: ICX initialization
 * Input	: none
 * Returen	: 0
 *----------------------------------------------------------------------------*/
int icx_init(void)
{
	unsigned int power_supply;
	unsigned int boot_factor;
	unsigned int key_value;
	unsigned int key_code;

	/**********************/
	/* Initialize drivers */
	/**********************/
#ifdef CONFIG_ICX
	spi_init();
	pwc_init();
	adc_init();
	pmu_init();
	nvp_init();
	key_init();
	lcd_init();
	dac_init();
#else /* CONFIG_ICD */
	spi_init();
	pwc_init();
	adc_init();
	pmu_init();
	nvp_init();
	key_init();
	lcd_init();
#endif

	/* save system information */
	nvp_save_sysinfo();

	/* save misc flags */
#ifdef CONFIG_ICX
	if (icx_is_bb)
		nvp_save_misc_flag(MISC_FLAG_BRD_TYPE, MISC_FLAG_BRD_TYPE_BB);
	else
		nvp_save_misc_flag(MISC_FLAG_BRD_TYPE, MISC_FLAG_BRD_TYPE_SET);
#else
	nvp_save_misc_flag(MISC_FLAG_BRD_TYPE, MISC_FLAG_BRD_TYPE_BB);
#endif

	/*************************/
	/* Get boot informations */
	/*************************/
	power_supply = pmu_get_power_supply_state();
	boot_factor  = pmu_get_boot_factor();
	key_value    = key_get_boot_key_value();
	key_code     = key_get_key_code(key_value);

	printf_dbg("power_supply : %08X\n", power_supply);
	printf_dbg("boot_factor  : %08X\n", boot_factor);
	printf_dbg("key_value    : %08X\n", key_value);
	printf_dbg("key_code     : %08X\n", key_code);
	
	/*************************/
	/* Execute boot sequence */
	/*************************/
	if (icx_inst_initrd_size != 0) {
		/* install sequence */
		boot_sequence_install(power_supply, boot_factor, key_value, key_code);
	}
	else {

		if (power_supply & POWER_SUPPLY_AC_ADAPTOR)
			boot_sequence_adaptor(power_supply, boot_factor, key_value, key_code);
	
		else if (power_supply & POWER_SUPPLY_USB)
			boot_sequence_usb(power_supply, boot_factor, key_value, key_code);

		else if (boot_factor & BOOT_FACTOR_RTC_ALARM)
			boot_sequence_rtc(power_supply, boot_factor, key_value, key_code);

		else if ((boot_factor & BOOT_FACTOR_KEY_PAD) || (key_code != KEY_CODE_NONE))
			boot_sequence_key(power_supply, boot_factor, key_value, key_code);

		else if(boot_factor & BOOT_FACTOR_HOLD_KEY)
			boot_sequence_hold(power_supply, boot_factor, key_value, key_code);

		else if(boot_factor & BOOT_FACTOR_ACCESSARY)
			boot_sequence_cradle(power_supply, boot_factor, key_value, key_code);

		else
			boot_sequence_others(power_supply, boot_factor, key_value, key_code);

	}

#ifdef CONFIG_ICX
	/* call DAC function pre-boot */
	dac_on_boot();
#endif /* CONFIG_ICX */

	/* boot normal (boot linux) */
	return 0;
}


/*-----------------------------------------------------------------------------
 * Function	: icx_load_linux_image
 * Abstruct	: Load kernel or rootfs image
 * Input	: part_no	 : srouce patition
 *			: src        : source sector(eMMC)
 *          : dst        : target address(DRAM)
 *			: size_limit : limit of size 
 * Returen	: loaded size(0:failed to load)
 *----------------------------------------------------------------------------*/
unsigned int icx_load_linux_image(int part_no, unsigned int src, unsigned char *dst, unsigned int size_limit)
{
	struct boot_info emmc_info;
	unsigned char	 emmc_buf[MMC_BLOCKLEN_VAL];
	unsigned int	 size, size_1st, size_total;
	unsigned int	 sum;
	unsigned int	*bufp;
	int				 ret;
	int				 i;

	src += ((MBRecord *)ROMBOOT_WORK_START)->partitionTable[part_no].firstSectorNumbers;

	/* read 1st sector */
	mmc_set_param(emmc_info, (unsigned long)&emmc_buf, src, MMC_BLOCKLEN_VAL, 0);
	ret = mmc_read(&emmc_info);
	if (ret != 0) {
		printf("Failed to read(eMMC info: %08Xh).\n", ret);
		return 0;
	}

	size_1st   = MMC_BLOCKLEN_VAL - LINUX_SIZE_OFFSET;
	memcpy(dst, &emmc_buf[LINUX_SIZE_OFFSET], size_1st);

	size	   = inl(emmc_buf);
	size_total = size + LINUX_SIZE_OFFSET + LINUX_SUM_SIZE;
	size_total = REVISE_BOUNDARY(size_total, MMC_BLOCKLEN_VAL);

	if (size_total > size_limit) {
		printf("Too large image(%08Xh) - remain:%08Xh.\n", size_total, size_limit);
		return 0;
	}

	/* read remains */
	mmc_set_param(emmc_info, (unsigned long)(dst + size_1st), (src + 1), (size_total - MMC_BLOCKLEN_VAL), 0);
	ret = mmc_read(&emmc_info);
	if (ret != 0) {
		printf("Failed to read(eMMC info: %08Xh).\n", ret);
		return 0;
	}

	/* check sum */
	sum  = 0;
	bufp = (unsigned int *)dst;
	size_total = (size_total - LINUX_SIZE_OFFSET) / 4;
	for (i = 0; i < size_total; i++) {
		sum ^= *bufp++;
	}
	if (sum != 0) {
		printf("Sum error(%08Xh).\n", sum);
		return 0;
	}

	/* succeeded to load */
    return size;
}

/*-----------------------------------------------------------------------------
 * Function	: icx_boot_error
 * Abstruct	: Stop to boot when error occured
 * Input	: none
 * Returen	: none
 *----------------------------------------------------------------------------*/
void icx_boot_error(void)
{
	printf("!! Failed to boot Linux!\n");
	while(1);
}


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

/*-----------------------------------------------------------------------------
 * Function	: boot_sequence_install
 * Abstruct	: Boot sequence of installation
 * Input	: power_supply	: AC adaptor/USB
 *			: boot_factor	: AC adaptor/USB/RTC alarm/Hold/Key
 *			: key_value		: key value
 *			: key_code		: key code
 * Returen	: 0
 *----------------------------------------------------------------------------*/
static int boot_sequence_install(
	unsigned int power_supply,
	unsigned int boot_factor,
	unsigned int key_value,
	unsigned int key_code
)
{
	printf_dbg("%s\n", __FUNCTION__);

	/* normal boot */
	pmu_clear_reset_flag();
	pmu_set_vbus_limit(VBUS_LIMIT_100);
	pmu_save_boot_factor(boot_factor);
	key_save_key_value(key_value);
	show_boot_image();
	beep_at_play_key(key_code);

	return 0;
}

/*-----------------------------------------------------------------------------
 * Function	: boot_sequence_adaptor
 * Abstruct	: Boot sequence via connecting AC adaptor
 * Input	: power_supply	: AC adaptor/USB
 *			: boot_factor	: AC adaptor/USB/RTC alarm/Hold/Key
 *			: key_value		: key value
 *			: key_code		: key code
 * Returen	: 0
 *----------------------------------------------------------------------------*/
static int boot_sequence_adaptor(
	unsigned int power_supply,
	unsigned int boot_factor,
	unsigned int key_value,
	unsigned int key_code
)
{
	printf_dbg("%s\n", __FUNCTION__);

	/* normal boot */
	pmu_clear_reset_flag();
	pmu_set_vbus_limit(VBUS_LIMIT_100);
	pmu_save_boot_factor(boot_factor);
	key_save_key_value(key_value);
	show_boot_image();
	beep_at_play_key(key_code);

	return 0;
}

/*-----------------------------------------------------------------------------
 * Function	: boot_sequence_usb
 * Abstruct	: Boot sequence via connecting USB
 * Input	: power_supply	: AC adaptor/USB
 *			: boot_factor	: AC adaptor/USB/RTC alarm/Hold/Key
 *			: key_value		: key value
 *			: key_code		: key code
 * Returen	: 0
 *----------------------------------------------------------------------------*/
static int boot_sequence_usb(
	unsigned int power_supply,
	unsigned int boot_factor,
	unsigned int key_value,
	unsigned int key_code
)
{
	unsigned int battery;
	unsigned int low		= 0;
	unsigned int very_low	= 0;

	printf_dbg("%s\n", __FUNCTION__);

	/* detect low power reset */
	if (pmu_check_reset_flag()) {
		/* low power boot */
		pmu_set_reset_flag();
		pmu_save_boot_flag(BOOT_FLAG_BATTERY, BOOT_FLAG_BATTERY_NONE);
		pmu_save_boot_factor(boot_factor & ~BOOT_FACTOR_KEY_PAD);
		key_save_key_value(KEY_VALUE_NOT_PRESSED);
		lcd_draw_image(IMAGE_ID_BOOT);
	}

	/* no low power reset */
	else {
		/* get battery value */
		battery = pmu_get_battery_value();
		pmu_get_battery_threshold(&low, &very_low);

		printf_dbg("battery = %d, low_batt = %d, very_low_batt = %d\n", battery, low, very_low);

		/* very low battery */
		if (battery < very_low + 3) {
			/* low power boot */
			pmu_set_reset_flag();
			icx_etc_select_clock_mode(CLOCK_MODE_NORMAL_D);
			pmu_save_boot_flag(BOOT_FLAG_BATTERY, BOOT_FLAG_BATTERY_VLOW);
			pmu_save_boot_factor(boot_factor & ~BOOT_FACTOR_KEY_PAD);
			key_save_key_value(KEY_VALUE_NOT_PRESSED);
			lcd_draw_image(IMAGE_ID_BOOT);
		}

		/* enough battery */
		else {
			/* normal boot */
			pmu_set_reset_flag();
			pmu_set_vbus_limit(VBUS_LIMIT_100);
			pmu_save_boot_factor(boot_factor);
			key_save_key_value(key_value);
			show_boot_image();
			beep_at_play_key(key_code);
		}
	}

	return 0;
}

/*-----------------------------------------------------------------------------
 * Function	: boot_sequence_rtc
 * Abstruct	: Boot sequence via RTC alarm
 * Input	: power_supply	: AC adaptor/USB
 *			: boot_factor	: AC adaptor/USB/RTC alarm/Hold/Key
 *			: key_value		: key value
 *			: key_code		: key code
 * Returen	: 0
 *----------------------------------------------------------------------------*/
static int boot_sequence_rtc(
	unsigned int power_supply,
	unsigned int boot_factor,
	unsigned int key_value,
	unsigned int key_code
)
{
	unsigned int battery;
	unsigned int low		= 0;
	unsigned int very_low	= 0;

	printf_dbg("%s\n", __FUNCTION__);

	/* get battery value */
	battery = pmu_get_battery_value();
	pmu_get_battery_threshold(&low, &very_low);

	printf_dbg("battery = %d, low_batt = %d, very_low_batt = %d\n", battery, low, very_low);

	/* very low battery */
	if (battery < very_low) {
		pmu_clear_reset_flag();
		power_off();
	}

	/* low battery */
	else if (battery < low) {
		pmu_clear_reset_flag();
		power_off();
	}

	else {
		/* match the alarm date */
		if (is_now_alarm_date()) {
			/* diplay off boot */
			pmu_clear_reset_flag();
			pmu_set_vbus_limit(100);
			pmu_save_boot_flag(BOOT_FLAG_RTC_INT, BOOT_FLAG_RTC_INT_ON);
			pmu_save_boot_factor(boot_factor & ~BOOT_FACTOR_KEY_PAD);
			key_save_key_value(KEY_VALUE_NOT_PRESSED);
			lcd_draw_image(IMAGE_ID_BOOT);
		}

		/* not match */
		else {
			pmu_clear_reset_flag();
			power_off();
		}
	}

	return 0;
}

/*-----------------------------------------------------------------------------
 * Function	: boot_sequence_key
 * Abstruct	: Boot sequence via pressed KEY
 * Input	: power_supply	: AC adaptor/USB
 *			: boot_factor	: AC adaptor/USB/RTC alarm/Hold/Key
 *			: key_value		: key value
 *			: key_code		: key code
 * Returen	: 0
 *----------------------------------------------------------------------------*/
static int boot_sequence_key(
	unsigned int power_supply,
	unsigned int boot_factor,
	unsigned int key_value,
	unsigned int key_code
)
{
	unsigned int battery;
	unsigned int low		= 0;
	unsigned int very_low	= 0;
	unsigned int hold;
	int			 hold_mode;
	int			 event;

	printf_dbg("%s\n", __FUNCTION__);

	/* get battery value */
	battery = pmu_get_battery_value();
	pmu_get_battery_threshold(&low, &very_low);

	printf_dbg("battery = %d, low_batt = %d, very_low_batt = %d\n", battery, low, very_low);

	/* very low battery */
	if (battery < very_low) {
		pmu_clear_reset_flag();
		power_off();
	}

	/* low battery */
	else if (battery < low) {

		pmu_clear_reset_flag();
		pmu_set_vbus_limit(100);

		event = wait_power_supply_connect();

		/* low battery and no power supply */
		if (event == EVENT_TIMEOUT){
			power_off();
		}

		/* power supply connected */
		else {
			pmu_save_boot_factor(boot_factor & ~BOOT_FACTOR_KEY_PAD);
			key_save_key_value(KEY_VALUE_NOT_PRESSED);
			show_boot_image();
		}
	}

	/* enough battery */
	else {

		/* get hold state */
		hold      = key_get_hold_state(NULL);
		hold_mode = nvp_check_hold_mode();

		/* hold mode */
		if ((hold_mode == NVP_HOLD_MODE_ALL) && (hold == KEY_HOLD_ON)) {
			
			pmu_clear_reset_flag();
			pmu_set_vbus_limit(100);

			event = wait_hold_cancel();

			/* keep hold mode */
			if (event == EVENT_TIMEOUT) {
				power_off();
			}

			/* alarm occur */
			else if (event == EVENT_RTC_ALARM) {
				/* lcd off boot */
				lcd_power_off();
				pmu_save_boot_flag(BOOT_FLAG_RTC_INT, BOOT_FLAG_RTC_INT_ON);
				pmu_save_boot_factor(boot_factor & ~BOOT_FACTOR_KEY_PAD);
				key_save_key_value(KEY_VALUE_NOT_PRESSED);
				lcd_draw_image(IMAGE_ID_BOOT);
			}

			/* hold mode canceld */
			else {
				/* normal boot without key push */
				pmu_save_boot_factor(boot_factor & ~BOOT_FACTOR_KEY_PAD);
				key_save_key_value(KEY_VALUE_NOT_PRESSED);
				show_boot_image();
			}
		}

		/* key push boot */
		else {
			/* normal boot */
			pmu_clear_reset_flag();
			pmu_set_vbus_limit(100);
			pmu_save_boot_factor(boot_factor);
			key_save_key_value(key_value);
			show_boot_image();
			beep_at_play_key(key_code);
		}
	}

	return 0;
}

/*-----------------------------------------------------------------------------
 * Function	: boot_sequence_hold
 * Abstruct	: Boot sequence via turn on/off hold
 * Input	: power_supply	: AC adaptor/USB
 *			: boot_factor	: AC adaptor/USB/RTC alarm/Hold/Key
 *			: key_value		: key value
 *			: key_code		: key code
 * Returen	: 0
 *----------------------------------------------------------------------------*/
static int boot_sequence_hold(
	unsigned int power_supply,
	unsigned int boot_factor,
	unsigned int key_value,
	unsigned int key_code
)
{
	unsigned int battery;
	unsigned int low		= 0;
	unsigned int very_low	= 0;
	unsigned int hold;
	int			 event;

	printf_dbg("%s\n", __FUNCTION__);

	hold = key_get_hold_state(NULL);

	/* hold off -> on */
	if (hold == KEY_HOLD_ON) {
		pmu_clear_reset_flag();
		power_off();
	}

	/* hold on -> off */
	else {
		
		battery = pmu_get_battery_value();
		pmu_get_battery_threshold(&low,&very_low);

		printf_dbg("battery = %d, low_batt = %d, very_low_batt = %d\n", battery, low, very_low);

		/* very low battery */
		if (battery < very_low) {
			pmu_clear_reset_flag();
			power_off();
		}

		/* low battery */
		else if(battery < low){

			pmu_clear_reset_flag();
			pmu_set_vbus_limit(100);

			event = wait_power_supply_connect();

			/* low battery and no power supply */
			if (event == EVENT_TIMEOUT) {
				power_off();
			}

			/* power supply connected */
			else {
				pmu_save_boot_factor(boot_factor & ~BOOT_FACTOR_KEY_PAD);
				key_save_key_value(KEY_VALUE_NOT_PRESSED);
				show_boot_image();
			}
		}

		/* enough battery */
		else {
			/* normal boot */
			pmu_clear_reset_flag();
			pmu_set_vbus_limit(100);
			pmu_save_boot_factor(boot_factor);
			key_save_key_value(key_value);
			show_boot_image();
			beep_at_play_key(key_code);
		}
	}
	
	return 0;
}

/*-----------------------------------------------------------------------------
 * Function	: boot_sequence_cradle
 * Abstruct	: Boot sequence via connecting cradle
 * Input	: power_supply	: AC adaptor/USB
 *			: boot_factor	: AC adaptor/USB/RTC alarm/Hold/Key
 *			: key_value		: key value
 *			: key_code		: key code
 * Returen	: 0
 *----------------------------------------------------------------------------*/
static int boot_sequence_cradle(
	unsigned int power_supply,
	unsigned int boot_factor,
	unsigned int key_value,
	unsigned int key_code
)
{
	unsigned int battery;
	unsigned int low		= 0;
	unsigned int very_low	= 0;
	int			 event;

	printf_dbg("%s\n", __FUNCTION__);

	battery = pmu_get_battery_value();
	pmu_get_battery_threshold(&low,&very_low);

	printf_dbg("battery = %d, low_batt = %d, very_low_batt = %d\n", battery, low, very_low);

	/* very low battery */
	if (battery < very_low) {
		pmu_clear_reset_flag();
		power_off();
	}

	/* low battery */
	else if(battery < low){

		pmu_clear_reset_flag();
		pmu_set_vbus_limit(100);

		event = wait_power_supply_connect();

		/* low battery and no power supply */
		if (event == EVENT_TIMEOUT) {
			power_off();
		}

		/* power supply connected */
		else {
			pmu_save_boot_factor(boot_factor & ~BOOT_FACTOR_KEY_PAD);
			key_save_key_value(KEY_VALUE_NOT_PRESSED);
			show_boot_image();
		}
	}

	/* enough battery */
	else {
		/* normal boot */
		pmu_clear_reset_flag();
		pmu_set_vbus_limit(100);
		pmu_save_boot_factor(boot_factor);
		key_save_key_value(key_value);
		show_boot_image();
		beep_at_play_key(key_code);
	}

	return 0;
}

/*-----------------------------------------------------------------------------
 * Function	: boot_sequence_others
 * Abstruct	: Boot sequence 
 * Input	: power_supply	: AC adaptor/USB
 *			: boot_factor	: AC adaptor/USB/RTC alarm/Hold/Key
 *			: key_value		: key value
 *			: key_code		: key code
 * Returen	: 0
 *----------------------------------------------------------------------------*/
static int boot_sequence_others(
	unsigned int power_supply,
	unsigned int boot_factor,
	unsigned int key_value,
	unsigned int key_code
)
{
	unsigned int battery;
	unsigned int low		= 0;
	unsigned int very_low	= 0;
	int			 event;

	printf_dbg("%s\n", __FUNCTION__);

	/* get battery value */
	battery = pmu_get_battery_value();
	pmu_get_battery_threshold(&low, &very_low);

	printf_dbg("battery = %d, low_batt = %d, very_low_batt = %d\n", battery, low, very_low);

	/* very low battery */
	if (battery < very_low) {
		pmu_clear_reset_flag();
		power_off();
	}

	/* low battery */
	else if (battery < low) {
		/* low power boot */
		pmu_clear_reset_flag();
		pmu_set_vbus_limit(VBUS_LIMIT_100);

		event = wait_power_supply_connect();

		/* low battery and no power supply */
		if (event == EVENT_TIMEOUT) {
			power_off();
		}

		/* power supply connected */
		else {
			pmu_save_boot_factor(boot_factor & ~BOOT_FACTOR_KEY_PAD);
			key_save_key_value(KEY_VALUE_NOT_PRESSED);
			show_boot_image();
		}
	}

	/* enough battery */
	else {
		/* normal boot */
		pmu_clear_reset_flag();
		pmu_set_vbus_limit(VBUS_LIMIT_100);
		pmu_save_boot_factor(boot_factor);
		key_save_key_value(key_value);
		show_boot_image();
		beep_at_play_key(key_code);
	}

	return 0;
}

/*-----------------------------------------------------------------------------
 * Function	: wait_hold_cancel
 * Abstruct	: Wait to cancel hold 
 * Input	: none
 * Returen	: 0(EVENT_TIMEOUT)              : not canceled
 *			: 1(EVENT_HOLD_CANCEL)		    : canceled(hold off)
 *			: 2(EVENT_POWER_SUPPLY_CONNECT) : canceled(power supply)
 *			: 3(EVENT_RTC_ALARM)			: canceled(RTC alarm)
 *----------------------------------------------------------------------------*/
static int wait_hold_cancel(void)
{
	unsigned int power_supply;
	unsigned int hold;
	unsigned int elapsed;
	unsigned int key_value;
	unsigned int key_code;
	int count;
	int ret = EVENT_TIMEOUT;

	/* display hold image */
	lcd_draw_image(IMAGE_ID_HOLD);
	lcd_power_on();
	lcd_set_brightness(LCD_BRIGHTNESS_MAX, LCD_FADE_INTERVAL_HOLD);

	/* check loop */
	for (count = HOLD_KEY_CANCEL_CHECK_COUNT; count > 0; count--) {
		
		udelay(HOLD_KEY_CANCEL_CHECK_INTERVAL);

		/* check power supply */
		power_supply = pmu_get_power_supply_state();
		if (power_supply & (POWER_SUPPLY_AC_ADAPTOR | POWER_SUPPLY_USB)) {
			ret = EVENT_POWER_SUPPLY_CONNECT;
			break;
		}

        /* check rtc alarm */
#ifdef CONFIG_ICX
        if (rtc_is_alarm() && is_now_alarm_date()) {
            ret = EVENT_RTC_ALARM;
            break;
        }
#endif

        /* check HOLD */
        hold = key_get_hold_state(&elapsed);
        if (hold == KEY_HOLD_OFF) {
            ret = EVENT_HOLD_CANCEL;
            break;
        }

        /* revise the counter */
        count = count - elapsed / HOLD_KEY_CANCEL_CHECK_INTERVAL;

        /* extend the checking time if KEY is pressed */
        key_value = key_get_key_value();
        key_code  = key_get_key_code(key_value);
        if (key_code != KEY_CODE_NONE) {
            /* count = HOLD_KEY_CANCEL_CHECK_COUNT + 1; */
        }
	}

    /* stop LCD */
    lcd_set_brightness(LCD_BRIGHTNESS_MIN, LCD_FADE_INTERVAL_OFF);

    return ret;
}

/*-----------------------------------------------------------------------------
 * Function	: wait_power_supply_connect
 * Abstruct	: Wait to connect power supply 
 * Input	: none
 * Returen	: 0(EVENT_TIMEOUT)              : not connected
 *			: 2(EVENT_POWER_SUPPLY_CONNECT) : connected
 *----------------------------------------------------------------------------*/
static int wait_power_supply_connect(void)
{
	unsigned int power_supply;
	int count;
	int step;
	int ret;

	/* display low battery image */
	lcd_draw_image(IMAGE_ID_LOWBATT);
	lcd_power_on();
	lcd_set_brightness(LCD_BRIGHTNESS_MAX, LCD_FADE_INTERVAL_LOWBATT);

#ifdef CONFIG_ICX
	/* enable beep */
	dac_disable_mute();
#endif

	ret = EVENT_TIMEOUT;

	for (count = 0; count < POWER_SUPPLY_CONNECT_CHECK_COUNT; count++) {

		step = count % LOWBATT_BEEP_LOOP;

		if ((step == 0) || (step > LOWBATT_BEEP_OFF2)) { 

			power_supply = pmu_get_power_supply_state();
			if (power_supply & (POWER_SUPPLY_AC_ADAPTOR | POWER_SUPPLY_USB)) {
				ret = EVENT_POWER_SUPPLY_CONNECT;
				break;
			}
		}

		/* sound a beep */
		switch (step) {
			
			case LOWBATT_BEEP_ON1 :
			case LOWBATT_BEEP_ON2 :
#ifdef CONFIG_ICX
				dac_beep_on(BEEP_TIME_CONTINUOUS);
#endif
				break;

			case LOWBATT_BEEP_OFF1 :
			case LOWBATT_BEEP_OFF2 :
#ifdef CONFIG_ICX
				dac_beep_off();
#endif
				break;

			default :
				break;
		}

		udelay(POWER_SUPPLY_CONNECT_CHECK_INTERVAL);
	}

	/* stop LCD */
	lcd_set_brightness(LCD_BRIGHTNESS_MIN, LCD_FADE_INTERVAL_OFF);

	return ret;
}

/*-----------------------------------------------------------------------------
 * Function	: is_now_alarm_date
 * Abstruct	: Check the alarm date
 * Input	: none
 * Returen	: 0 : not equal / 1 : equal
 *----------------------------------------------------------------------------*/
static int is_now_alarm_date(void)
{
#ifdef CONFIG_ICX
	rtc_date_t date_nvp;
	rtc_date_t date_now;

	/* read alarm time from NVP */
	nvp_get_alarm_date(&date_nvp.year, &date_nvp.tm.mdhm);

	/* read current date */
	if (rtc_get_date(&date_now) != 0)
		return 0;
	
	/* compare current date with NVP */
	if ((date_now.year == date_nvp.year) && (date_now.tm.mdhm == date_nvp.tm.mdhm))
		return 1;
#endif

	return 0;
}


/*-----------------------------------------------------------------------------
 * Function	: show_boot_image
 * Abstruct	: Display boot image
 * Input	: none
 * Returen	: none
 *----------------------------------------------------------------------------*/
static void show_boot_image(void)
{
	lcd_draw_image(IMAGE_ID_BOOT);

	lcd_power_on();
	lcd_set_brightness(LCD_BRIGHTNESS_MAX, LCD_FADE_INTERVAL_OPENING);

	return;
}

/*-----------------------------------------------------------------------------
 * Function	: beep_at_play_key
 * Abstruct	: Sound a beep at play key
 * Input	: key_code : key code
 * Returen	: 0 : not sound a beep / 1 : sound a beep
 *----------------------------------------------------------------------------*/
static int beep_at_play_key(unsigned int key_code)
{
	int ret = 0;

#ifdef CONFIG_ICX

#if BEEP_AT_PLAY_KEY

	if (nvp_check_beep_flag()) {
		if (key_code == KEY_CODE_PLAY) {
			dac_disable_mute();
			dac_beep_on(BEEP_TIME_100MS);

			ret = 1;
		}   
	}   

#endif

#endif

	return ret;
}

/*-----------------------------------------------------------------------------
 * Function	: check_commandline
 * Abstruct	: Check the command line
 * Input	: commandline : command line
 * Returen	: none
 *----------------------------------------------------------------------------*/
/*  */
void check_commandline(char *commandline)
{
	char  buf[CONFIG_SYS_BARGSIZE + 1] = {0};
	char *pre_end;
	char *suf_start;

	if (commandline) {
		strncpy(buf, commandline, CONFIG_SYS_BARGSIZE);
		buf[CONFIG_SYS_BARGSIZE] = '\0';

		/* remove "console" if the parameter isn't set */
		pre_end = strstr(buf, "console=");
		if (pre_end != NULL) {
			suf_start = pre_end + strlen("console=");
			if ((*suf_start == ' ') || (*suf_start == '\0')) {
				for (; *suf_start == ' '; suf_start++);

				*pre_end = '\0'; 
				strncat(buf, suf_start, (CONFIG_SYS_BARGSIZE - strlen(buf)));

				strncpy(commandline, buf, CONFIG_SYS_BARGSIZE);
			}   
		} 
	}
}

/*-----------------------------------------------------------------------------
 * Function	: power_off
 * Abstruct	: Power off
 * Input	: none
 * Returen	: none
 *----------------------------------------------------------------------------*/
static void power_off(void)
{
#ifdef CONFIG_ICX
	dac_disable_mute();
#endif
	lcd_power_off();

	lcd_exit();
#ifdef CONFIG_ICX
	dac_exit();
#endif

	pmu_regulator_off(REGULATOR_GP3);

	pmu_power_off();

	return;
}

#endif /* CONFIG_ICX || CONFIG_ICD */

