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

/************/
/*@ defines */ 
/************/

/* for mirror of PWC register */
#define PWC_REG_MAGIC_NO  0x63495770 /* pWIc */
#define PWC_REG_MAGIC_ADR 0xF0000000
#define PWC_REG_SRAM_BASE 0xF0000004
#define PWC_REG_NUM       0xFC

/********************/
/*@ type definition */
/********************/

/* reset value */
typedef struct {
	u_char addr;
	u_char data;
} pwc_reg_data_t;

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

/* mirror of PWC registers */
static u_char *pwc_reg_mirror = (u_char *)PWC_REG_SRAM_BASE;
static u_char  pwc_reg_rd[PWC_REG_NUM];

/* table of initial vaule */
static pwc_reg_data_t pwc_reset_data[] = {
	{PWC_PWR_REGA,	 	0x03},
	{PWC_PWR_PSCNT2, 	0x02},
	{PWC_PWR_VOUTSET1,	0x48},
	{PWC_PWR_VOUTSET2,	0x0D},
	{PWC_GPIO_GPPUPD3, 	0x55},
	{PWC_GPIO_GPPUPD4, 	0x05},
	{PWC_ADC_ADSET1H, 	0xFF},
	{PWC_ADC_ADSET2H, 	0xFF},
	{PWC_ADC_ADSET3H, 	0xFF},
	{PWC_ADC_ADSET4H, 	0xFF},
	{PWC_CHG_TSET, 		0x2F},
	{PWC_CHG_BTMPSET, 	0x03},
	{PWC_CHG_LEDEN, 	0x01},
};

#define PWC_REG_RESET_NUM (sizeof(pwc_reset_data)/sizeof(pwc_reset_data[0]))

/* table of readable registers */
static u_char pwc_reg_rd_tbl[] = {
	PWC_GPIO_IOIN1,
	PWC_GPIO_IOIN2,
	PWC_INTC_FACTOR1,
	PWC_INTC_FACTOR2,
	PWC_INTC_FACTOR3,
	PWC_ADC_ADDATA1,
	PWC_ADC_ADDATA2,
	PWC_ADC_ADDATA3,
	PWC_ADC_ADDATA4,
	PWC_ADC_ADDATA5,
	PWC_ADC_ADDATA6,
	PWC_ADC_ADDATA7,
	PWC_ADC_ADDATA8,
	PWC_CHG_PSW_STATE,
	PWC_INTC_WRSTFCT,
	PWC_LSIVER,
	PWC_RTC_SEC_RD,
	PWC_RTC_MIN_RD,
	PWC_RTC_HOUR_RD,
	PWC_RTC_WEEK_RD,
	PWC_RTC_DAY_RD,
	PWC_RTC_MONTH_RD,
	PWC_RTC_YEAR_RD,
	PWC_RTC_TRIM_RD,
	PWC_RTC_ALM_WM_RD,
	PWC_RTC_ALM_WH_RD,
	PWC_RTC_ALM_WW_RD,
	PWC_RTC_ALM_DM_RD,
	PWC_RTC_ALM_DH_RD,
	PWC_RTC_RTC1_RD,
	PWC_RTC_RTC2_RD,
	PWC_ADC_ADFACT,
	PWC_CHG_RDSTATE,
	PWC_CHG_CHGIRR,
	PWC_CHG_CHGMONI,
	PWC_IF_RDBAK_DATA1,
};

#define PWC_REG_RD_NUM (sizeof(pwc_reg_rd_tbl)/sizeof(pwc_reg_rd_tbl[0]))


/* SPI config (w) */
static spi_config_t spi_config_w = {
	.dev = SPI_DEV_SP0,
	.clk = SPI_CLK_12MHZ,
	.cs  = SPI_CS_SEL_CS0,
	.pol = (SPI_CK_DLY_ON | SPI_CK_POL_POS | SPI_CS_POL_POS),
	.nbw = SPI_NB_16BIT,
	.nbr = 0,
};

/* SPI config (r) */
static spi_config_t spi_config_r = {
	.dev = SPI_DEV_SP0,
	.clk = SPI_CLK_12MHZ,
	.cs  = SPI_CS_SEL_CS0,
	.pol = (SPI_CK_DLY_ON | SPI_CK_POL_POS | SPI_CS_POL_POS),
	.nbw = SPI_NB_8BIT,
	.nbr = SPI_NB_8BIT,
};

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

/*-----------------------------------------------------------------------------
 *  Function    : pwc_init
 *  Abstruct    : Initialize PowerIC
 *  Input       : none
 *  Return      : none
 *----------------------------------------------------------------------------*/
void pwc_init(void)
{
	int i;

	/* set magic number */
	outl(PWC_REG_MAGIC_NO, PWC_REG_MAGIC_ADR);

	/* clear mirror region */
	memset(pwc_reg_mirror, 0, PWC_REG_NUM);

	/* set reset values */
	for (i = 0; i < PWC_REG_RESET_NUM; i++)
		pwc_reg_mirror[pwc_reset_data[i].addr] = pwc_reset_data[i].data;

	/* clear readable flag table */
	memset(pwc_reg_rd, 0, PWC_REG_NUM);

	/* set readable flag */
	for (i = 0; i < PWC_REG_RD_NUM; i++)
		pwc_reg_rd[pwc_reg_rd_tbl[i]] = 1;

#if !defined(CONFIG_ICX) && !defined(CONFIG_ICD)	/* only NEC reference board */
	pwc_reg_mirror[PWC_PWR_PSCNT2]  = 0x0F; 
	pwc_reg_mirror[PWC_GPIO_IOSEL1] = 0x80;
	pwc_reg_mirror[PWC_GPIO_IOOUT1] = 0x00;
#endif /* !CONFIG_ICX && !CONFIG_ICD */

	/* clear PWONMSK */
	pwc_write(PWC_INTC_REGPWONMSK, 0x00, 0xFF);

	/* clear WRSTMSK */
	pwc_write(PWC_INTC_WRSTMSK, 0x00, 0xFF);

	/* all bit low */
	pwc_write(PWC_GPIO_IOOUT1, 0x00, 0xFF);
	pwc_write(PWC_GPIO_IOOUT2, 0x00, 0xFF);

	/* all bit input */
	pwc_write(PWC_GPIO_IOSEL1, 0x00, 0xFF);
	pwc_write(PWC_GPIO_IOSEL2, 0x00, 0xFF);

	/* default pull-down */
	pwc_write(PWC_GPIO_GPPUPD1, 0x55, 0xFF);
	pwc_write(PWC_GPIO_GPPUPD2, 0x55, 0xFF);
#if defined(CONFIG_ICX)
	pwc_write(PWC_GPIO_GPPUPD3, 0x54, 0xFF);
#else
	pwc_write(PWC_GPIO_GPPUPD3, 0x55, 0xFF);
#endif /* CONFIG_ICX */
	pwc_write(PWC_GPIO_GPPUPD4, 0x05, 0x0F);
}

/*-----------------------------------------------------------------------------
 *  Function    : pwc_read
 *  Abstruct    : Read a data from register of PowerIC
 *  Input       : addr      : address of register
 *              : data      : data
 *  Return      : 0         : success(read from PowerIC)
 *              : 1			: success(read from mirror)
 *              : -EINVAL   : invalid parameter
 *----------------------------------------------------------------------------*/
int pwc_read(u_int addr, u_char *data)
{
	u_int spi_data;
	int   ret;

	if (addr >= PWC_REG_NUM)
		return -EINVAL;

	if (pwc_reg_rd[addr]) {
		ret = 0;

		/* SPI read */
		spi_probe(&spi_config_r);
		ret = spi_read(&spi_config_r, &addr, &spi_data, SPI_READ_1CYCLE);
		*data = (u_char)spi_data;

		return ret;
	}
	else {
		*data = pwc_reg_mirror[addr];
		return 1;
	}
}

/*-----------------------------------------------------------------------------
 *  Function    : pwc_write
 *  Abstruct    : Write a data to register of PowerIC
 *  Input       : addr      : address of register
 *              : data      : data
 *              : mask      : mask
 *  Return      : 0         : success
 *              : -EINVAL   : invalid parameter
 *----------------------------------------------------------------------------*/
int pwc_write(u_int addr, u_char data, u_char mask)
{
	u_char tmp_data;
	u_int  buf;
	int    ret;

	/* printf("%02X <- %02X & %02X\n",addr,data,mask); */

	/* read current value */
	ret = pwc_read(addr, &tmp_data);
	if (ret <= 0)
		return ret;

	/* update value and write to PowerIC */
    spi_probe(&spi_config_w);
	tmp_data = (tmp_data & ~mask) | (data & mask);
	buf = (u_int)(tmp_data & 0xFF) | (addr << 8);
	ret = spi_write(&spi_config_w, &buf, 1);
	if (ret < 0)
		return ret;

	/* update mirror */
	pwc_reg_mirror[addr] = tmp_data;

	return 0;
}

#endif	/* CONFIG_ICX || CONFIG_ICD */ 
