/* 2009-08-18: File added and changed by Sony Corporation */
/*
 *  Copyright 2009 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;  version 2 of the  License.
 *
 *  THIS  SOFTWARE  IS PROVIDED   ``AS  IS'' AND   ANY  EXPRESS OR IMPLIED
 *  WARRANTIES,   INCLUDING, BUT NOT  LIMITED  TO, THE IMPLIED WARRANTIES OF
 *  MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN
 *  NO  EVENT  SHALL   THE AUTHOR  BE    LIABLE FOR ANY   DIRECT, INDIRECT,
 *  INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
 *  NOT LIMITED   TO, PROCUREMENT OF  SUBSTITUTE GOODS  OR SERVICES; LOSS OF
 *  USE, DATA,  OR PROFITS; OR  BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
 *  ANY THEORY OF LIABILITY, WHETHER IN  CONTRACT, STRICT LIABILITY, OR TORT
 *  (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
 *  THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 *  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.,
 *  675 Mass Ave, Cambridge, MA 02139, USA.
 *
 */

#include <linux/module.h>
#include <linux/init.h>
#include <linux/delay.h>

#include <asm/arch/pmic_adc.h>
#include <asm/arch/pmic_battery.h>
#include <asm/arch/pmic_light.h>

#include "chargerif.h"
#include "internal.h"

static int get_value_adc_convert_8x(t_channel channel, unsigned short *data);
static int set_charger_led(unsigned char channel, unsigned int level, int on);

static int ichrg_limit[ATLAS_ICHRG_NUM_MAX];

static int get_value_adc_convert_8x(t_channel channel, unsigned short *data)
{
	PMIC_STATUS ret;
	unsigned short result_array[SAMPLE_NUMBER];
	int i, count = 0;

	if(NULL == data){
		DPRINT("## Invalid argument\n");
		return -1;
	}

 	ret = pmic_adc_convert_8x(channel, result_array); 
	if(PMIC_SUCCESS != ret){
		return -1;
	}

	for(i = 0; i < ARRAY_SIZE(result_array); i++){
		if(result_array[i]){
			++count;
		}
		*data += result_array[i];
	}

	if(count){
		*data /= count;
	}

	return 0;
}

static int set_charger_led(unsigned char channel, unsigned int level, int on)
{
	PMIC_STATUS ret;
	unsigned char duty;

	/* Enable LED */
	pmic_write_reg(REG_LED_CONTROL_0, 0x01 << 0, PMIC_ALL_BITS);

	on ? (duty = LED_DUTY_MAX) : (duty = LED_DUTY_MIN);

	ret = pmic_tcled_fun_set_dutycycle(TCLED_FUN_BANK2, channel, duty);
	if (PMIC_SUCCESS != ret){
		DPRINT("## Error LED Setting\n");
		return -1;
	}

	ret = pmic_tcled_fun_set_current(TCLED_FUN_BANK2, channel, level);
	if (PMIC_SUCCESS != ret){
		DPRINT("## Error LED Setting\n");
		return -1;
	}

	return 0;
}

navu2pm_status_t chargerif_register_callback(chargerif_irq_callback_t *table) 
{ 
	int ret; 
	pmic_event_callback_t pmic_event_callback; 
	
	if(NULL == table){
		return NAVU2PM_ERR_ADDR;
	}
  
	pmic_event_callback.func  = table->callback; 
	pmic_event_callback.param = table->data; 
         
	ret = pmic_event_subscribe(table->event, pmic_event_callback); 
	if(PMIC_SUCCESS != ret){
		DPRINT("## Error pmic_event_subscribe()!!\n");
		return NAVU2PM_ERR_OTHER;
	}
  
	return NAVU2PM_SUCCESS; 
} 

navu2pm_status_t chargerif_get_battery_current(int *bc, unsigned short *bc_hex)
{
	int ret, val;
	unsigned short data = 0;
	
	if(NULL == bc || NULL == bc_hex){
		return NAVU2PM_ERR_ADDR;
	}

	ret = get_value_adc_convert_8x(BATTERY_CURRENT, &data);
	if(ret){
		return NAVU2PM_ERR_OTHER;
	}

	*bc_hex = data;

	if (data & ADC_MSB) {
        data = data - 1;
        data = ~data;
        data &= ADC_MAX_ABS;
		val = -1 * (int)data;
    } else {
        val =  (int)data; 
    }

	*bc = val *  MILI_SCALE * ADC_STEP / BATT_CUR_SCALE / BATT_CUR_MILIOHMIC;

	return NAVU2PM_SUCCESS;
}

navu2pm_status_t chargerif_get_battery_voltage(int *bv, unsigned short *bv_hex)
{
	int ret;
	unsigned short data = 0;

	if(NULL == bv || NULL == bv_hex){
		return NAVU2PM_ERR_ADDR;
	}
	
	ret = get_value_adc_convert_8x(BATTERY_VOLTAGE, &data);
	if(ret){
		return NAVU2PM_ERR_OTHER;
	}

	*bv = (int)data * ADC_STEP + BATT_VOL_OFFSET;
	*bv_hex = data;

	return NAVU2PM_SUCCESS;
}

navu2pm_status_t chargerif_get_supply_voltage(int *sv, unsigned short *sv_hex)
{
	int ret;
	unsigned short data = 0;

	if(NULL == sv || NULL == sv_hex){
		return NAVU2PM_ERR_ADDR;
	}
	
	ret = get_value_adc_convert_8x(APPLICATION_SUPPLY, &data);
	if(ret){
		return NAVU2PM_ERR_OTHER;
	}

	*sv = (int)data * ADC_STEP + BATT_VOL_OFFSET;
	*sv_hex = data;

	return NAVU2PM_SUCCESS;
}

navu2pm_status_t chargerif_get_battery_temperature(int *bt, unsigned short *bt_hex)
{
	int ret;
	unsigned short data = 0;

	if(NULL == bt || NULL == bt_hex){
		return NAVU2PM_ERR_ADDR;
	}

	ret = get_value_adc_convert_8x(THERMISTOR, &data);
	if(ret){
		return NAVU2PM_ERR_OTHER;
	}

	/* should be changed into Temperature */
	*bt_hex = data; 

	return NAVU2PM_SUCCESS;
}

navu2pm_status_t chargerif_set_ichrg(atlas_ichrg_t val)
{
	PMIC_STATUS ret;
	unsigned char c_voltage, c_current;

	if(val < 0 || val >= ATLAS_ICHRG_NUM_MAX){
		return NAVU2PM_ERR_OTHER;
	}

	ret = pmic_batt_get_charger_setting(BATT_MAIN_CHGR, &c_voltage, &c_current);
	if(PMIC_SUCCESS != ret){
		return NAVU2PM_ERR_OTHER;
	}
	
	ret = pmic_batt_set_charger(BATT_MAIN_CHGR, c_voltage, (unsigned char)val);
	if(PMIC_SUCCESS != ret){
		return NAVU2PM_ERR_OTHER;
	}

	return NAVU2PM_SUCCESS;
}
EXPORT_SYMBOL(chargerif_set_ichrg);

navu2pm_status_t chargerif_get_ichrg(unsigned char *val)
{
	PMIC_STATUS ret;
	unsigned char c_voltage;

	if(NULL == val){
		return NAVU2PM_ERR_ADDR;
	}

	ret = pmic_batt_get_charger_setting(BATT_MAIN_CHGR, &c_voltage, val);
	if(PMIC_SUCCESS != ret){
		return NAVU2PM_ERR_OTHER;
	}

	return NAVU2PM_SUCCESS;
}

navu2pm_status_t chargerif_set_charger_led_red(int on)
{
	int ret;
	unsigned char channel;
	unsigned int  level;

	if(PGPF_PRODUCTION_LEVEL_RS == pgpf_get_production_level()){
		channel = TCLED_FUN_CHANNEL2;
	}else{
		channel = TCLED_FUN_CHANNEL1;
	}
	level = TCLED_CUR_LEVEL_3;

	ret = set_charger_led(channel, level, on);
	if(ret){
		return NAVU2PM_ERR_OTHER;
	}
		
	return NAVU2PM_SUCCESS;	
}

navu2pm_status_t chargerif_set_charger_led_green(int on)
{
	int ret;
	unsigned char channel;
	unsigned int  level;

	if(PGPF_PRODUCTION_LEVEL_RS == pgpf_get_production_level()){
		channel = TCLED_FUN_CHANNEL1;
	}else{
		channel = TCLED_FUN_CHANNEL2;
	}
	level = TCLED_CUR_LEVEL_1;
	

	ret = set_charger_led(channel, level, on);
	if(ret){
		return NAVU2PM_ERR_OTHER;
	}
		
	return NAVU2PM_SUCCESS;	
}

navu2pm_status_t chargerif_get_acpin(int *connect)
{
	PMIC_STATUS ret;
	t_sensor_bits sensor_bits;

	if(NULL == connect){
		return NAVU2PM_ERR_ADDR;
	}

	ret = pmic_get_sensors(&sensor_bits);
	if(PMIC_SUCCESS != ret){
		return NAVU2PM_ERR_OTHER;
	}

	*connect = sensor_bits.sense_se1s; 
	
	return NAVU2PM_SUCCESS;
}

atlas_ichrg_t chargerif_get_usb_ilimit_setting(int ilimit)
{
	atlas_ichrg_t val;

	if(ilimit < ichrg_limit[ATLAS_ICHRG_70]){
		val = ATLAS_ICHRG_0;
	}else if(ilimit < ichrg_limit[ATLAS_ICHRG_177]){
		val = ATLAS_ICHRG_70;
	}else if(ilimit < ichrg_limit[ATLAS_ICHRG_266]){
		val = ATLAS_ICHRG_177;
	}else if(ilimit < ichrg_limit[ATLAS_ICHRG_355]){
		val = ATLAS_ICHRG_266;
	}else if(ilimit < ichrg_limit[ATLAS_ICHRG_443]){
		val = ATLAS_ICHRG_355;
	}else{
		val = ATLAS_ICHRG_443;
	}

	return val;
}

navu2pm_status_t chargerif_init(void)
{
	/* Initial LED */
	pmic_write_reg(REG_LED_CONTROL_0, 0x01 << 0, PMIC_ALL_BITS);

	/* Init Charge Limit Array */ 
	ichrg_limit[ATLAS_ICHRG_0]   =   0;
	ichrg_limit[ATLAS_ICHRG_70]  =  70;
	ichrg_limit[ATLAS_ICHRG_177] = 177;
	ichrg_limit[ATLAS_ICHRG_266] = 266;
	ichrg_limit[ATLAS_ICHRG_355] = 355;
	ichrg_limit[ATLAS_ICHRG_443] = 443;
	ichrg_limit[ATLAS_ICHRG_532] = 532;
	ichrg_limit[ATLAS_ICHRG_621] = 621;
	ichrg_limit[ATLAS_ICHRG_709] = 709;
	
	return NAVU2PM_SUCCESS;
}
