/*
 * Copyright 2004 Freescale Semiconductor, Inc. All Rights Reserved.
 */

/* 
 * 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
 *
 */

/*!
 * @file atlas_power.c
 * @brief This is the main file of Atlas Power driver. 
 *
 * @ingroup ATLAS_POWER
 */

/*
 * Includes
 */

#include "../core/atlas_config.h"
#include "../core/atlas_event.h"
#include "../core/atlas_external.h"
#include "atlas_power.h"
#include "atlas_power_defs.h"
#include "linux/device.h"
#include "linux/delay.h"

static int atlas_power_major;
 
/*
 * Audio Atlas API
 */

/* EXPORTED FUNCTIONS */
EXPORT_SYMBOL(atlas_power_get_power_mode_sense);
EXPORT_SYMBOL(atlas_power_set_regen_assig);
EXPORT_SYMBOL(atlas_power_get_regen_assig);
EXPORT_SYMBOL(atlas_power_set_regen_inv);
EXPORT_SYMBOL(atlas_power_get_regen_inv);
EXPORT_SYMBOL(atlas_power_conf_switcher);
EXPORT_SYMBOL(atlas_power_get_switcher);
EXPORT_SYMBOL(atlas_power_regu_en);
EXPORT_SYMBOL(atlas_power_get_regu_state);
EXPORT_SYMBOL(atlas_power_conf_regu);
EXPORT_SYMBOL(atlas_power_get_regu_conf);
EXPORT_SYMBOL(atlas_power_set_regu);
EXPORT_SYMBOL(atlas_power_get_regu);
EXPORT_SYMBOL(atlas_power_cut_en);
EXPORT_SYMBOL(atlas_power_get_power_cut);
EXPORT_SYMBOL(atlas_power_warm_start_en);
EXPORT_SYMBOL(atlas_power_get_warm_start);
EXPORT_SYMBOL(atlas_power_user_off_en);
EXPORT_SYMBOL(atlas_power_get_user_off_en);
EXPORT_SYMBOL(atlas_power_32k_en);
EXPORT_SYMBOL(atlas_power_get_32k_state);
EXPORT_SYMBOL(atlas_power_cut_conf);
EXPORT_SYMBOL(atlas_power_cut_get_conf);
EXPORT_SYMBOL(atlas_power_vbkup_conf);
EXPORT_SYMBOL(atlas_power_get_vbkup_conf);
EXPORT_SYMBOL(atlas_power_set_bp_threshold);
EXPORT_SYMBOL(atlas_power_get_bp_threshold);
EXPORT_SYMBOL(atlas_power_set_eol_function);
EXPORT_SYMBOL(atlas_power_get_eol_function);
EXPORT_SYMBOL(atlas_power_set_coincell_voltage);
EXPORT_SYMBOL(atlas_power_get_coincell_voltage);
EXPORT_SYMBOL(atlas_power_coincell_charger_en);
EXPORT_SYMBOL(atlas_power_get_coincell_charger_en);
EXPORT_SYMBOL(atlas_power_set_auto_reset_en);
EXPORT_SYMBOL(atlas_power_get_auto_reset_en);
EXPORT_SYMBOL(atlas_power_set_conf_button);
EXPORT_SYMBOL(atlas_power_get_conf_button);
EXPORT_SYMBOL(atlas_power_set_conf_pll);
EXPORT_SYMBOL(atlas_power_get_conf_pll);
EXPORT_SYMBOL(atlas_power_set_sw3);
EXPORT_SYMBOL(atlas_power_get_sw3);
EXPORT_SYMBOL(atlas_power_event_sub);
EXPORT_SYMBOL(atlas_power_event_unsub);
#ifdef __ATLAS_2_0_
EXPORT_SYMBOL(atlas_power_vbkup2_auto_en);
EXPORT_SYMBOL(atlas_power_get_vbkup2_auto_state);
EXPORT_SYMBOL(atlas_power_bat_det_en);
EXPORT_SYMBOL(atlas_power_get_bat_det_state);
EXPORT_SYMBOL(atlas_power_esim_v_en);
EXPORT_SYMBOL(atlas_power_gets_esim_v_state);
EXPORT_SYMBOL(atlas_power_vib_pin_en);
EXPORT_SYMBOL(atlas_power_gets_vib_pin_state);
#endif

/*
 * Internal function
 */
int atlas_power_event(t_pwr_int event, void* callback, bool sub);

static int atlas_power_suspend(struct device * dev, u32 state, u32 level)
{
        switch (level) {
        case SUSPEND_NOTIFY:
                /* not supported for Atlas power */
        break;
        case SUSPEND_SAVE_STATE:
                /* not supported for Atlas power */
        break;
        case SUSPEND_POWER_DOWN:
                /* nothing for Atlas power */
        break;
        }
        
        return 0;
};

static int atlas_power_resume(struct device * dev, u32 level)
{
        switch (level) {
        case RESUME_RESTORE_STATE:
                /* nothing for Atlas power */
        break;
        case RESUME_POWER_ON:
                /* nothing for Atlas power */
        break;
        }

        return 0;
};

static struct device_driver atlas_power_driver_ldm = {
        .name           = "Atlas_power",
        .bus            = &platform_bus_type,
        .probe          = NULL,
        .remove         = NULL,
        .suspend        = atlas_power_suspend,
        .resume         = atlas_power_resume,
};

static struct platform_device atlas_power_ldm = {
        .name       = "Atlas_power",
        .id         = 1,
};

/*
 * Power Atlas API
 */

/*!
 * This function returns power up sense value
 *
 * @param        p_up_sense  	value of power up sense
 *
 * @return       This function returns 0 if successful.
 */
int atlas_power_get_power_mode_sense(struct t_p_up_sense *p_up_sense)
{
        unsigned int reg_value=0;
        CHECK_ERROR(atlas_read_reg(PRIO_PWM,REG_POWER_UP_MODE_SENSE, 
                &reg_value));
        p_up_sense->state_ictest=(STATE_ICTEST_MASK & reg_value);
        p_up_sense->state_clksel=((STATE_CLKSEL_MASK & reg_value) 
                >> STATE_CLKSEL_BIT);
        p_up_sense->state_pums1=((STATE_PUMS1_MASK & reg_value) 
                >> STATE_PUMS1_BITS);
        p_up_sense->state_pums2=((STATE_PUMS2_MASK & reg_value) 
                >> STATE_PUMS2_BITS);
        p_up_sense->state_pums3=((STATE_PUMS3_MASK & reg_value) 
                >> STATE_PUMS3_BITS);
        p_up_sense->state_chrgmode0=((STATE_CHRGM1_MASK & reg_value) 
                >> STATE_CHRGM1_BITS);
        p_up_sense->state_chrgmode1=((STATE_CHRGM2_MASK & reg_value) 
                >> STATE_CHRGM2_BITS);
        p_up_sense->state_umod=((STATE_UMOD_MASK & reg_value) 
                >> STATE_UMOD_BITS);
        p_up_sense->state_usben=((STATE_USBEN_MASK & reg_value) 
                >> STATE_USBEN_BIT);
        p_up_sense->state_sw_1a1b_joined=((STATE_SW1A_J_B_MASK & reg_value) 
                >> STATE_SW1A_J_B_BIT);
        p_up_sense->state_sw_2a2b_joined=((STATE_SW2A_J_B_MASK & reg_value) 
                >> STATE_SW2A_J_B_BIT);
        return ERROR_NONE;
}

/*!
 * This function configures the Regen assignment for all regulator
 *
 * @param        regu     	type of regulator
 * @param        en_dis  	if true, the regulator is enabled by regen.
 *
 * @return       This function returns 0 if successful.
 */
int atlas_power_set_regen_assig(t_regulator regu, bool en_dis)
{
        if ((REGULATOR_REGEN_BIT[regu]) != -1) {
                CHECK_ERROR(atlas_set_reg_bit(PRIO_PWM,REG_REGEN_ASSIGNMENT, 
                        REGULATOR_REGEN_BIT[regu], en_dis));
        } else {
                return ERROR_BAD_INPUT;
        }
        return ERROR_NONE;
}

/*!
 * This function gets the Regen assignment for all regulator
 *
 * @param        regu     	type of regulator
 * @param        en_dis  	return value, if true :
 *                              the regulator is enabled by regen.
 * @return       This function returns 0 if successful.
 */
int atlas_power_get_regen_assig(t_regulator regu, bool *en_dis)
{
        if ((REGULATOR_REGEN_BIT[regu]) != -1) {
                CHECK_ERROR(atlas_get_reg_bit(PRIO_PWM,REG_REGEN_ASSIGNMENT, 
                        REGULATOR_REGEN_BIT[regu], en_dis));
        } else {
                return ERROR_BAD_INPUT;
        }
        return ERROR_NONE;
}

/*!
 * This function sets the Regen polarity.
 *
 * @param        en_dis  	If true regen is inverted.
 *
 * @return       This function returns 0 if successful.
 */
int atlas_power_set_regen_inv(bool en_dis)
{
        CHECK_ERROR(atlas_set_reg_bit(PRIO_PWM,REG_REGEN_ASSIGNMENT, 
                BIT_REGEN, en_dis));
        return ERROR_NONE;
}

/*!
 * This function gets the Regen polarity.
 *
 * @param        en_dis  	If true regen is inverted.
 *
 * @return       This function returns 0 if successful.
 */
int atlas_power_get_regen_inv(bool *en_dis)
{
        CHECK_ERROR(atlas_get_reg_bit(PRIO_PWM,REG_REGEN_ASSIGNMENT, 
                BIT_REGEN, en_dis));
        return ERROR_NONE;
}

/*!
 * This function configures switcher.
 *
 * @param        sw  	define type of switcher.
 * @param        sw_conf define new configuration.
 *
 * @return       This function returns 0 if successful.
 */
int atlas_power_conf_switcher(t_switcher sw, struct t_switcher_conf *sw_conf)
{
        /* check variable */
        int error=0,offset=0,o_b=0;
        int conf_sw=0,conf_2=0;
        
        if (sw_conf->sw_setting > (MASK_SET >> BIT_SET)) {
                error=true;
        }
        if (sw_conf->sw_setting_dvs > ( MASK_SET_DVS >> BIT_SET_DVS)) {
                error=true;
        }
        if (sw_conf->sw_setting_standby > (MASK_SET_SDBY >> BIT_SET_SDBY)) {
                error=true;
        }
        if (sw_conf->sw_op_mode > (MASK_OP_MODE >> BIT_OP_MODE)) {
                error=true;
        }
        if (sw_conf->sw_op_mode_standby > (MASK_OP_SDBY >> BIT_OP_SDBY)) {
                error=true;
        }
        if (sw_conf->sw_dvs_speed > (MASK_SPD_DVS >> BIT_SPD_DVS)) {
                error=true;
        }
        if (sw_conf->sw_panic_mode > ( MASK_PANIC >> BIT_PANIC)) {
                error=true;
        }
        if (sw_conf->sw_softstart > ( MASK_SOFTSTART >> BIT_SOFTSTART)) {
                error=true;
        }
        if (sw > SW_SW2B) {
                error=true;
        }
        if (error) {
                return ERROR_BAD_INPUT;
        }
        
        conf_sw=sw_conf->sw_setting << BIT_SET         |
                sw_conf->sw_setting_dvs << BIT_SET_DVS |
                sw_conf->sw_setting_standby << BIT_SET_SDBY ;        

        conf_2= sw_conf->sw_op_mode << BIT_OP_MODE      |
                sw_conf->sw_op_mode_standby << BIT_OP_SDBY |
                sw_conf->sw_dvs_speed << BIT_SPD_DVS    |
                sw_conf->sw_panic_mode << BIT_PANIC     |
                sw_conf->sw_softstart << BIT_SOFTSTART;

        offset = sw;
        CHECK_ERROR(atlas_set_reg_value(PRIO_PWM,REG_SWITCHERS_0 + offset, 
                BITS_SW, (int) conf_sw, LONG_SW));
        offset = 0;
        if (sw == SW_SW2A || sw ==SW_SW2B ) {
                offset = 1;
        }
        o_b=0;
        if (sw == SW_SW1B || sw ==SW_SW2B ) {
                o_b = LONG_CONF_SW+1;
        }
        CHECK_ERROR(atlas_set_reg_value(PRIO_PWM,REG_SWITCHERS_4 + offset, 
                BITS_CONF_SW + o_b, (int) conf_2, LONG_CONF_SW));
        return ERROR_NONE;
}

/*!
 * This function returns configuration of one switcher.
 *
 * @param        sw  	define type of switcher.
 * @param        sw_conf return value of configuration.
 *
 * @return       This function returns 0 if successful.
 */
int atlas_power_get_switcher(t_switcher sw, struct t_switcher_conf *sw_conf)
{
        /* check variable */
        int offset=0,o_b=0;
        int conf_sw=0,conf_2=0;

        offset = sw;
        CHECK_ERROR(atlas_get_reg_value(PRIO_PWM,REG_SWITCHERS_0 + offset,
                BITS_SW, (int*) &conf_sw, LONG_SW));
        offset = 0;
        if (sw == SW_SW2A || sw ==SW_SW2B ) {
                offset = 1;
        }
        o_b=0;
        if (sw == SW_SW1B || sw ==SW_SW2B ) {
                o_b = LONG_CONF_SW+1;
        }
        CHECK_ERROR(atlas_get_reg_value(PRIO_PWM,REG_SWITCHERS_4 + offset, 
                BITS_CONF_SW + o_b, (int*) &conf_2, LONG_CONF_SW));
        
        sw_conf->sw_setting = ((MASK_SET & conf_sw) >> BIT_SET);
        
        sw_conf->sw_setting_dvs = ((MASK_SET_DVS & conf_sw)>> BIT_SET_DVS);
        sw_conf->sw_setting_standby = ((MASK_SET_SDBY & conf_sw) 
                >> BIT_SET_SDBY);
        sw_conf->sw_op_mode = ((MASK_OP_MODE & conf_2) >> BIT_OP_MODE);
        sw_conf->sw_op_mode_standby = ((MASK_OP_SDBY & conf_2) >> BIT_OP_SDBY);
        sw_conf->sw_dvs_speed = ((MASK_SPD_DVS & conf_2) >> BIT_SPD_DVS);
        sw_conf->sw_panic_mode = ((MASK_PANIC & conf_2) >> BIT_PANIC);
        sw_conf->sw_softstart = ((MASK_SOFTSTART & conf_2) >> BIT_SOFTSTART);
        return ERROR_NONE;
}

/*!
 * This function enables or disables a regulator.
 *
 * @param        regu           define the regulator.
 * @param        en_dis  	If true, enable the regulator.
 *
 * @return       This function returns 0 if successful.
 */
int atlas_power_regu_en(t_regulator regu, bool en_dis)
{
        int reg=0,regu_index=0,en_bit_val=0;

        en_bit_val =  REGULATOR_EN_BIT[regu];
        if(en_bit_val >= 200 ) {
                reg=REG_POWER_MISCELLANEOUS;
                regu_index = en_bit_val - 200;
        } else if (en_bit_val >= 100 ) {
                reg=REG_REGULATOR_MODE_1;
                regu_index = en_bit_val - 100;
        } else {
                reg=REG_REGULATOR_MODE_0;
                regu_index = regu;
        }
        CHECK_ERROR(atlas_set_reg_bit(PRIO_PWM,reg, regu_index, en_dis));
        return ERROR_NONE;
}

/*!
 * This function returns state of regulator.
 *
 * @param        regu           define the regulator.
 * @param        en_dis  	If true, regulator is enabled.
 *
 * @return       This function returns 0 if successful.
 */
int atlas_power_get_regu_state(t_regulator regu, bool *en_dis)
{
        int reg=0,regu_index=0,en_bit_val=0;

        en_bit_val =  REGULATOR_EN_BIT[regu];
        if(en_bit_val >= 200 ) {
                reg=REG_POWER_MISCELLANEOUS;
                regu_index = en_bit_val - 200;
        } else if (en_bit_val >= 100 ) {
                reg=REG_REGULATOR_MODE_1;
                regu_index = en_bit_val - 100;
        } else {
                reg=REG_REGULATOR_MODE_0;
                regu_index = regu;
        }
        CHECK_ERROR(atlas_get_reg_bit(PRIO_PWM,reg, regu_index, en_dis));
        return ERROR_NONE;
}

/*!
 * This function configures a regulator.
 *
 * @param        regu           define the regulator.
 * @param        stby  	        If true, the regulator is controlled by standby.
 * @param        mode  	        configure the regulator operating mode.
 *
 * @return       This function returns 0 if successful.
 */
int atlas_power_conf_regu(t_regulator regu, bool stby, bool mode)
{
        int reg1=0, regu_index_sdby=0, stby_bit_val=0;
        int reg2=0, regu_index_mode=0, mode_bit_val=0;

        stby_bit_val =  REGULATOR_SDBY_BIT[regu];
      
        if(stby_bit_val >= 200 ) {
                reg1=REG_POWER_MISCELLANEOUS;
                regu_index_sdby = stby_bit_val - 200;
        } else if (stby_bit_val >= 100 ) {
                reg1=REG_REGULATOR_MODE_1;
                regu_index_sdby = stby_bit_val - 100;
        } else {
                reg1=REG_REGULATOR_MODE_0;
                regu_index_sdby = regu;
        }
        
        mode_bit_val =  REGULATOR_OP_MODE_BIT[regu];
        if ( mode_bit_val != -1 ){
                if(mode_bit_val >= 200 ) {
                        reg2=REG_POWER_MISCELLANEOUS;
                        regu_index_mode = mode_bit_val - 200;
                } else if (stby_bit_val >= 100 ) {
                        reg2=REG_REGULATOR_MODE_1;
                        regu_index_mode = mode_bit_val - 100;
                } else {
                        reg2=REG_REGULATOR_MODE_0;
                        regu_index_mode = regu;
                }
                if ( reg1 != reg2 ) {
                        return ERROR_BAD_INPUT;
                }
                CHECK_ERROR(atlas_set_reg_bit(PRIO_PWM,reg1, regu_index_mode, 
                        mode));
        }
        CHECK_ERROR(atlas_set_reg_bit(PRIO_PWM,reg1, regu_index_sdby, stby));
        return ERROR_NONE;
}

/*!
 * This function gets configuration of one regulator.
 *
 * @param        regu           define the regulator.
 * @param        stby  	        If true, the regulator is controlled by standby.
 * @param        mode  	        return the regulator operating mode.
 *
 * @return       This function returns 0 if successful.
 */
int atlas_power_get_regu_conf(t_regulator regu, bool *stby, bool *mode)
{
        int reg1=0, regu_index_sdby=0, stby_bit_val=0;
        int reg2=0, regu_index_mode=0, mode_bit_val=0;
        *mode=0;

        stby_bit_val =  REGULATOR_SDBY_BIT[regu];
      
        if(stby_bit_val >= 200 ) {
                reg1=REG_POWER_MISCELLANEOUS;
                regu_index_sdby = stby_bit_val - 200;
        } else if (stby_bit_val >= 100 ) {
                reg1=REG_REGULATOR_MODE_1;
                regu_index_sdby = stby_bit_val - 100;
        } else {
                reg1=REG_REGULATOR_MODE_0;
                regu_index_sdby = regu;
        }
        
        mode_bit_val =  REGULATOR_OP_MODE_BIT[regu];
        if ( mode_bit_val != -1 ){
                if(mode_bit_val >= 200 ) {
                        reg2=REG_POWER_MISCELLANEOUS;
                        regu_index_mode = mode_bit_val - 200;
                } else if (stby_bit_val >= 100 ) {
                        reg2=REG_REGULATOR_MODE_1;
                        regu_index_mode = mode_bit_val - 100;
                } else {
                        reg2=REG_REGULATOR_MODE_0;
                        regu_index_mode = regu;
                }
                if ( reg1 != reg2 ) {
                        return ERROR_BAD_INPUT;
                }
                CHECK_ERROR(atlas_get_reg_bit(PRIO_PWM,reg1, regu_index_mode, 
                        mode));
        }
        
        CHECK_ERROR(atlas_get_reg_bit(PRIO_PWM,reg1, regu_index_sdby, stby));
        return ERROR_NONE;
}

/*!
 * This function sets value of regulator setting.
 *
 * @param        regu           define the regulator.
 * @param        setting        define the regulator setting.
 *
 * @return       This function returns 0 if successful.
 */
int atlas_power_set_regu(t_regulator regu, int setting)
{
        int reg=0, size_set=0, bit_index=0;
        int regu_val_tab=0, max_val=0;
        
        regu_val_tab = REGULATOR_SETTING_BITS[regu];
        if (regu_val_tab < 0) {
                return ERROR_BAD_INPUT; 
        }
        reg = REG_REGULATOR_SETTING_0 + (regu_val_tab / 10000);
        if ((regu_val_tab / 10000) == 1 ) {
                regu_val_tab -= 10000; 
        }
        bit_index = (regu_val_tab / 100); 
        size_set = regu_val_tab & 0x0F;
        max_val= (1 << size_set) - 1;
        if (setting > max_val ) {
                return ERROR_BAD_INPUT; 
        }
        CHECK_ERROR(atlas_set_reg_value(PRIO_PWM,reg,bit_index, setting, 
                size_set));
        return ERROR_NONE;
}

/*!
 * This function gets value of regulator setting.
 *
 * @param        regu           define the regulator.
 * @param        setting        return the regulator setting.
 *
 * @return       This function returns 0 if successful.
 */
int atlas_power_get_regu(t_regulator regu, int *setting)
{
        int reg=0, size_set=0, bit_index=0;
        int regu_val_tab=0;
        
        regu_val_tab = REGULATOR_SETTING_BITS[regu];
        if (regu_val_tab < 0) {
                return ERROR_BAD_INPUT; 
        }
        reg = REG_REGULATOR_SETTING_0 + (regu_val_tab / 10000);
        if ((regu_val_tab / 10000) == 1 ) {
                regu_val_tab -= 10000; 
        }
        bit_index = (regu_val_tab / 100); 
        size_set = regu_val_tab & 0x0F;
        CHECK_ERROR(atlas_get_reg_value(PRIO_PWM,reg,bit_index, 
                (int*) setting, size_set));
        return ERROR_NONE;
}

/*!
 * This function enables power cut mode.
 *
 * @param        en           if true, power cut enable.
 *
 * @return       This function returns 0 if successful.
 */
int atlas_power_cut_en(bool en)
{
        CHECK_ERROR(atlas_set_reg_bit(PRIO_PWM,REG_POWER_CONTROL_0, 
                BIT_POWER_CUT, en));
        return ERROR_NONE;
}

/*!
 * This function gets power cut mode.
 *
 * @param        en           if true, power cut is enabled.
 *
 * @return       This function returns 0 if successful.
 */
int atlas_power_get_power_cut(bool *en)
{
        CHECK_ERROR(atlas_get_reg_bit(PRIO_PWM,REG_POWER_CONTROL_0, 
                BIT_POWER_CUT, en));
        return ERROR_NONE;
}

/*!
 * This function enables warm start mode.
 *
 * @param        en           if true, warm start enable.
 *
 * @return       This function returns 0 if successful.
 */
int atlas_power_warm_start_en(bool en)
{
        CHECK_ERROR(atlas_set_reg_bit(PRIO_PWM,REG_POWER_CONTROL_0, 
                BIT_WARM_EN, en));
        return ERROR_NONE;
}

/*!
 * This function gets warm start mode.
 *
 * @param        en           if true, warm start is enabled.
 *
 * @return       This function returns 0 if successful.
 */
int atlas_power_get_warm_start(bool *en)
{
        CHECK_ERROR(atlas_get_reg_bit(PRIO_PWM,REG_POWER_CONTROL_0, 
                BIT_WARM_EN, en));
        return ERROR_NONE;
}

/*!
 * This function send SPI command for entering user off modes.
 *
 * @param        en           if true, entering user off modes
 *
 * @return       This function returns 0 if successful.
 */
int atlas_power_user_off_en(bool en)
{
        CHECK_ERROR(atlas_set_reg_bit(PRIO_PWM,REG_POWER_CONTROL_0, 
                BIT_USER_OFF, en));
        return ERROR_NONE;
}

/*!
 * This function returns value of user off modes SPI command.
 *
 * @param        en           if true, user off modes is started
 *
 * @return       This function returns 0 if successful.
 */
int atlas_power_get_user_off_en(bool *en)
{
        CHECK_ERROR(atlas_get_reg_bit(PRIO_PWM,REG_POWER_CONTROL_0, 
                BIT_USER_OFF, en));
        return ERROR_NONE;
}

/*!
 * This function enables CLK32KMCU clock.
 *
 * @param        en           if true, enable CLK32K
 *
 * @return       This function returns 0 if successful.
 */
int atlas_power_32k_en(bool en)
{
        CHECK_ERROR(atlas_set_reg_bit(PRIO_PWM,REG_POWER_CONTROL_0, BIT_32K_EN,
                en));
        return ERROR_NONE;
}

/*!
 * This function gets state of CLK32KMCU clock.
 *
 * @param        en           if true, CLK32K is enabled
 *
 * @return       This function returns 0 if successful.
 */
int atlas_power_get_32k_state(bool *en)
{
        CHECK_ERROR(atlas_get_reg_bit(PRIO_PWM,REG_POWER_CONTROL_0, BIT_32K_EN,
                en));
        return ERROR_NONE;
}

#ifdef __ATLAS_2_0_

/*!
 * This function enables automatically VBKUP2 in the memory hold modes.
 * Only on Atlas 2.0 or higher
 *
 * @param        en           if true, enable VBKUP2AUTOMH
 *
 * @return       This function returns 0 if successful.
 */
int atlas_power_vbkup2_auto_en(bool en)
{
        CHECK_ERROR(atlas_set_reg_bit(PRIO_PWM,REG_POWER_CONTROL_0, BIT_VBKUP2AUTOMH,
                en));
        return ERROR_NONE;
}

/*!
 * This function gets state of automatically VBKUP2.
 * Only on Atlas 2.0 or higher
 *
 * @param        en           if true, VBKUP2AUTOMH is enabled
 *
 * @return       This function returns 0 if successful.
 */
int atlas_power_get_vbkup2_auto_state(bool *en)
{
        CHECK_ERROR(atlas_get_reg_bit(PRIO_PWM,REG_POWER_CONTROL_0, BIT_VBKUP2AUTOMH,
                en));
        return ERROR_NONE;
}

/*!
 * This function enables battery detect function.
 * Only on Atlas 2.0 or higher
 *
 * @param        en           if true, enable BATTDETEN
 *
 * @return       This function returns 0 if successful.
 */
int atlas_power_bat_det_en(bool en)
{
        CHECK_ERROR(atlas_set_reg_bit(PRIO_PWM,REG_POWER_CONTROL_0, BIT_BATTDETEN,
                en));
        return ERROR_NONE;
}

/*!
 * This function gets state of battery detect function.
 * Only on Atlas 2.0 or higher
 *
 * @param        en           if true, BATTDETEN is enabled
 *
 * @return       This function returns 0 if successful.
 */
int atlas_power_get_bat_det_state(bool *en)
{
        CHECK_ERROR(atlas_get_reg_bit(PRIO_PWM,REG_POWER_CONTROL_0, BIT_BATTDETEN,
                en));
        return ERROR_NONE;
}

/*!
 * This function enables esim control voltage.
 * Only on Atlas 2.0 or higher
 *
 * @param        vesim          if true, enable VESIMESIMEN
 * @param        vmmc1          if true, enable VMMC1ESIMEN
 * @param        vmmc2          if true, enable VMMC2ESIMEN
 *
 * @return       This function returns 0 if successful.
 */
int atlas_power_esim_v_en(bool vesim, bool vmmc1, bool vmmc2)
{
        CHECK_ERROR(atlas_set_reg_bit(PRIO_PWM,REG_REGEN_ASSIGNMENT, BIT_VESIM,
                vesim));
        CHECK_ERROR(atlas_set_reg_bit(PRIO_PWM,REG_REGEN_ASSIGNMENT, BIT_VMMC1,
                vmmc1));
        CHECK_ERROR(atlas_set_reg_bit(PRIO_PWM,REG_REGEN_ASSIGNMENT, BIT_VMMC2,
                vmmc2));
        return ERROR_NONE;
}

/*!
 * This function gets esim control voltage values.
 * Only on Atlas 2.0 or higher
 *
 * @param        vesim          if true, enable VESIMESIMEN
 * @param        vmmc1          if true, enable VMMC1ESIMEN
 * @param        vmmc2          if true, enable VMMC2ESIMEN
 *
 * @return       This function returns 0 if successful.
 */
int atlas_power_gets_esim_v_state(bool* vesim, bool* vmmc1, bool* vmmc2)
{
        CHECK_ERROR(atlas_get_reg_bit(PRIO_PWM,REG_REGEN_ASSIGNMENT, BIT_VESIM,
                vesim));
        CHECK_ERROR(atlas_get_reg_bit(PRIO_PWM,REG_REGEN_ASSIGNMENT, BIT_VMMC1,
                vmmc1));
        CHECK_ERROR(atlas_get_reg_bit(PRIO_PWM,REG_REGEN_ASSIGNMENT, BIT_VMMC2,
                vmmc2));
        return ERROR_NONE;
}

/*!
 * This function enables control of VVIB by VIBEN pin.
 * Only on Atlas 2.0 or higher
 *
 * @param        en           if true, enable VIBPINCTRL
 *
 * @return       This function returns 0 if successful.
 */
int atlas_power_vib_pin_en(bool en)
{
        CHECK_ERROR(atlas_set_reg_bit(PRIO_PWM,REG_POWER_MISCELLANEOUS,
                BIT_VIBPINCTRL, en));
        return ERROR_NONE;
}

/*!
 * This function gets state of control of VVIB by VIBEN pin.
 * Only on Atlas 2.0 or higher
 * @param        en           if true, VIBPINCTRL is enabled
 *
 * @return       This function returns 0 if successful.
 */
int atlas_power_gets_vib_pin_state(bool *en)
{
        CHECK_ERROR(atlas_get_reg_bit(PRIO_PWM,REG_POWER_MISCELLANEOUS, 
                BIT_VIBPINCTRL, en));
        return ERROR_NONE;
}

#endif

/*!
 * This function configures power cut mode.
 *
 * @param        pc  	define configuration of power cut mode.
 *
 * @return       This function returns 0 if successful.
 */
int atlas_power_cut_conf(struct t_power_cut_conf *pc)
{
        int val_max=0;
        
        val_max = (1 << LONG_TIMER) - 1;
        if ( pc->pc_timer > val_max ) {
                return ERROR_BAD_INPUT;               
        }
        val_max = (1 << LONG_CUT_CUNT) - 1;
        if ( pc->pc_counter > val_max ) {
                return ERROR_BAD_INPUT;               
        }
        val_max = (1 << LONG_MAX_PW_CUT) - 1;
        if ( pc->pc_max_nb_pc > val_max ) {
                return ERROR_BAD_INPUT;               
        }
        val_max = (1 << LONG_EXT_PW_CUT) - 1;
        if ( pc->pc_ext_timer > val_max ) {
                return ERROR_BAD_INPUT;               
        }

        CHECK_ERROR(atlas_set_reg_bit(PRIO_PWM,REG_POWER_CONTROL_0, 
                BIT_PC_CNT_EN, pc->pc_counter_en));
        CHECK_ERROR(atlas_set_reg_bit(PRIO_PWM,REG_POWER_CONTROL_0, 
                BIT_AUTO_OFF_EN, pc->pc_auto_user_off));
        CHECK_ERROR(atlas_set_reg_bit(PRIO_PWM,REG_POWER_CONTROL_0, 
                BIT_KEEP_32K_UOFF, pc->pc_user_off_32k_en));
        CHECK_ERROR(atlas_set_reg_value(PRIO_PWM,REG_POWER_CONTROL_1,
                BITS_TIMER, pc->pc_timer, LONG_TIMER));
        CHECK_ERROR(atlas_set_reg_value(PRIO_PWM,REG_POWER_CONTROL_1,
                BITS_CUT_CUNT, pc->pc_counter, LONG_CUT_CUNT));
        CHECK_ERROR(atlas_set_reg_value(PRIO_PWM,REG_POWER_CONTROL_1,
                BITS_MAX_PW_CUT, pc->pc_max_nb_pc, LONG_MAX_PW_CUT));
        CHECK_ERROR(atlas_set_reg_value(PRIO_PWM,REG_POWER_CONTROL_1,
                BITS_EXT_PW_CUT, pc->pc_ext_timer, LONG_EXT_PW_CUT));
        CHECK_ERROR(atlas_set_reg_bit(PRIO_PWM,REG_POWER_CONTROL_1, 
                BIT_TIMER_INF, pc->pc_ext_timer_inf));
        return ERROR_NONE;
}

/*!
 * This function gets configuration of power cut mode.
 *
 * @param        pc  	return configuration of power cut mode.
 *
 * @return       This function returns 0 if successful.
 */
int atlas_power_cut_get_conf(struct t_power_cut_conf *pc)
{
        CHECK_ERROR(atlas_get_reg_bit(PRIO_PWM,REG_POWER_CONTROL_0, 
                BIT_PC_CNT_EN, &(pc->pc_counter_en)));
        CHECK_ERROR(atlas_get_reg_bit(PRIO_PWM,REG_POWER_CONTROL_0, 
                BIT_AUTO_OFF_EN, &(pc->pc_auto_user_off)));
        CHECK_ERROR(atlas_get_reg_bit(PRIO_PWM,REG_POWER_CONTROL_0,
                BIT_KEEP_32K_UOFF, &(pc->pc_user_off_32k_en)));
        CHECK_ERROR(atlas_get_reg_value(PRIO_PWM,REG_POWER_CONTROL_1,
                BITS_TIMER, &(pc->pc_timer), LONG_TIMER));
        CHECK_ERROR(atlas_get_reg_value(PRIO_PWM,REG_POWER_CONTROL_1,
                BITS_CUT_CUNT, &(pc->pc_counter), LONG_CUT_CUNT));
        CHECK_ERROR(atlas_get_reg_value(PRIO_PWM,REG_POWER_CONTROL_1,
                BITS_MAX_PW_CUT, &(pc->pc_max_nb_pc), LONG_MAX_PW_CUT));
        CHECK_ERROR(atlas_get_reg_value(PRIO_PWM,REG_POWER_CONTROL_1,
                BITS_EXT_PW_CUT, &(pc->pc_ext_timer), LONG_EXT_PW_CUT));
        CHECK_ERROR(atlas_get_reg_bit(PRIO_PWM,REG_POWER_CONTROL_1, 
                BIT_TIMER_INF, &(pc->pc_ext_timer_inf)));
        return ERROR_NONE;
}

/*!
 * This function configures v-backup during power cut.
 *
 * @param        vbkup  	type of VBLUP (1 or2).
 * @param        vbkup_conf  	define configuration of VBLUP.
 *
 * @return       This function returns 0 if successful.
 */
int atlas_power_vbkup_conf(t_vbkup vbkup, struct t_vbkup_conf *vbkup_conf)
{
        int offset=0;
        int max_val=0;
        max_val = (1 << LONG_VBLUP_VOLT) - 1;
        if (vbkup_conf->vbkup_voltage > max_val ){
                return ERROR_BAD_INPUT;
        }
        
        if (vbkup == VBKUP_2) {
                offset = VBLUP2_OFFSET; 
        }
        
        CHECK_ERROR(atlas_set_reg_bit(PRIO_PWM,REG_POWER_CONTROL_0, 
                BIT_VBLUP_EN+offset, vbkup_conf->vbkup_en));
        CHECK_ERROR(atlas_set_reg_bit(PRIO_PWM,REG_POWER_CONTROL_0,
                BIT_VBLUP_AUTO_EN+offset, vbkup_conf->vbkup_auto_en));
        CHECK_ERROR(atlas_set_reg_value(PRIO_PWM,REG_POWER_CONTROL_0,
                BITS_VBLUP_VOLT+offset, vbkup_conf->vbkup_voltage, 
                        LONG_VBLUP_VOLT));
        return ERROR_NONE;
}

/*!
 * This function gets configuration of v-backup.
 *
 * @param        vbkup  	type of VBLUP (1 or2).
 * @param        vbkup_conf  	return configuration of VBLUP.
 *
 * @return       This function returns 0 if successful.
 */
int atlas_power_get_vbkup_conf(t_vbkup vbkup, struct t_vbkup_conf *vbkup_conf)
{
        int offset=0;
        if (vbkup == VBKUP_2) {
                offset = VBLUP2_OFFSET; 
        }
        CHECK_ERROR(atlas_get_reg_bit(PRIO_PWM,REG_POWER_CONTROL_0, 
                BIT_VBLUP_EN+offset, &(vbkup_conf->vbkup_en)));
        CHECK_ERROR(atlas_get_reg_bit(PRIO_PWM,REG_POWER_CONTROL_0, 
                BIT_VBLUP_AUTO_EN+offset, &(vbkup_conf->vbkup_auto_en)));
        CHECK_ERROR(atlas_get_reg_value(PRIO_PWM,REG_POWER_CONTROL_0,
                BITS_VBLUP_VOLT+offset, &(vbkup_conf->vbkup_voltage),
                        LONG_VBLUP_VOLT));
        return ERROR_NONE;
}

/*!
 * This function sets BP detection threshold.
 *
 * @param        threshold           define threshold
 *
 * @return       This function returns 0 if successful.
 */
int atlas_power_set_bp_threshold(int threshold)
{
        CHECK_ERROR(atlas_set_reg_value(PRIO_PWM,REG_POWER_CONTROL_0,
                BITS_BP_THRESHOLD, threshold, LONG_BP_THRESHOLD));
        return ERROR_NONE;
}

/*!
 * This function gets BP detection threshold.
 *
 * @param        threshold           return threshold
 *
 * @return       This function returns 0 if successful.
 */
int atlas_power_get_bp_threshold(int *threshold)
{
        CHECK_ERROR(atlas_get_reg_value(PRIO_PWM,REG_POWER_CONTROL_0,
                BITS_BP_THRESHOLD, (int*) threshold, LONG_BP_THRESHOLD));
        return ERROR_NONE;
}

/*!
 * This function selects EOL function instead of LOBAT.
 *
 * @param        eol          If true, selects EOL function
 *
 * @return       This function returns 0 if successful.
 */
int atlas_power_set_eol_function(bool eol)
{
        CHECK_ERROR(atlas_set_reg_bit(PRIO_PWM,REG_POWER_CONTROL_0, 
                BIT_EOL_FCT, eol));
        return ERROR_NONE;
}

/*!
 * This function gets EOL function instead of LOBAT.
 *
 * @param        eol          If true, selects EOL function
 *
 * @return       This function returns 0 if successful.
 */
int atlas_power_get_eol_function(bool *eol)
{
        CHECK_ERROR(atlas_get_reg_bit(PRIO_PWM,REG_POWER_CONTROL_0, 
                BIT_EOL_FCT, eol));
        return ERROR_NONE;
}

/*!
 * This function sets coincell charger voltage setting.
 *
 * @param        voltage         value of voltage setting
 *
 * @return       This function returns 0 if successful.
 */
int atlas_power_set_coincell_voltage(int voltage)
{
        int max_val=0;
        max_val = (1 << LONG_CHARGE_VOLT) - 1;
        if (voltage > max_val ){
                return ERROR_BAD_INPUT;
        } 
        CHECK_ERROR(atlas_set_reg_value(PRIO_PWM,REG_POWER_CONTROL_0,
                BITS_CHARGE_VOLT, voltage, LONG_CHARGE_VOLT));
        return ERROR_NONE;
}

/*!
 * This function gets coincell charger voltage setting.
 *
 * @param        voltage         return value of voltage setting
 *
 * @return       This function returns 0 if successful.
 */
int atlas_power_get_coincell_voltage(int *voltage)
{
        CHECK_ERROR(atlas_get_reg_value(PRIO_PWM,REG_POWER_CONTROL_0,
                BITS_CHARGE_VOLT,(int*) voltage, LONG_CHARGE_VOLT));
        return ERROR_NONE;
}

/*!
 * This function enables coincell charger.
 *
 * @param        en         if true, enable the coincell charger
 *
 * @return       This function returns 0 if successful.
 */
int atlas_power_coincell_charger_en(bool en)
{
        CHECK_ERROR(atlas_set_reg_bit(PRIO_PWM,REG_POWER_CONTROL_0, 
                BIT_CHARGER_EN, en));
        return ERROR_NONE;
}

/*!
 * This function gets state of coincell charger.
 *
 * @param        en         if true, the coincell charger is enabled
 *
 * @return       This function returns 0 if successful.
 */
int atlas_power_get_coincell_charger_en(bool *en)
{
        CHECK_ERROR(atlas_get_reg_bit(PRIO_PWM,REG_POWER_CONTROL_0, 
                BIT_CHARGER_EN, (int*) en));
        return ERROR_NONE;
}

/*!
 * This function enables auto reset after a system reset.
 *
 * @param        en         if true, the auto reset is enabled
 *
 * @return       This function returns 0 if successful.
 */
int atlas_power_set_auto_reset_en(bool en)
{
        CHECK_ERROR(atlas_set_reg_bit(PRIO_PWM,REG_POWER_CONTROL_2, 
                BIT_AUTO_RESTART, en));
        return ERROR_NONE;
}

/*!
 * This function gets auto reset configuration.
 *
 * @param        en         if true, the auto reset is enabled
 *
 * @return       This function returns 0 if successful.
 */
int atlas_power_get_auto_reset_en(bool *en)
{
        CHECK_ERROR(atlas_get_reg_bit(PRIO_PWM,REG_POWER_CONTROL_2, 
                BIT_AUTO_RESTART, (int*) en));
        return ERROR_NONE;
}

/*!
 * This function configures a system reset on a button.
 *
 * @param       bt         type of button.
 * @param       sys_rst    if true, enable the system reset on this button
 * @param       deb_time   sets the debounce time on this button pin
 *
 * @return       This function returns 0 if successful.
 */
int atlas_power_set_conf_button(t_button bt, bool sys_rst, int deb_time)
{
        int max_val=0;
        max_val = (1 << LONG_DEB_TIME_ON) - 1;
        if (deb_time > max_val ){
                return ERROR_BAD_INPUT;
        }
        CHECK_ERROR(atlas_set_reg_bit(PRIO_PWM,REG_POWER_CONTROL_2, 
                BIT_RESET_ON + bt, sys_rst));

        CHECK_ERROR(atlas_set_reg_value(PRIO_PWM,REG_POWER_CONTROL_2,
                BITS_DEB_TIME_ON + bt*LONG_DEB_TIME_ON, deb_time, 
                        LONG_DEB_TIME_ON));
        return ERROR_NONE;
}

/*!
 * This function gets configuration of a button.
 *
 * @param       bt         type of button.
 * @param       sys_rst    if true, the system reset is enabled on this button
 * @param       deb_time   gets the debounce time on this button pin
 *
 * @return       This function returns 0 if successful.
 */
int atlas_power_get_conf_button(t_button bt, bool *sys_rst, int *deb_time)
{
        CHECK_ERROR(atlas_get_reg_bit(PRIO_PWM,REG_POWER_CONTROL_2,
                BIT_RESET_ON + bt, (int*) sys_rst));

        CHECK_ERROR(atlas_get_reg_value(PRIO_PWM,REG_POWER_CONTROL_2,
                BITS_DEB_TIME_ON + bt*LONG_DEB_TIME_ON, (int*) deb_time, 
                        LONG_DEB_TIME_ON));
        return ERROR_NONE;
}

/*!
 * This function configures switcher PLL.
 *
 * @param       pll_conf   configuration of PLL.
 *
 * @return       This function returns 0 if successful.
 */
int atlas_power_set_conf_pll(struct t_pll_conf *pll_conf)
{
        int max_val=0;
        max_val = (1 << LONG_PLL_MULTI) - 1;
        if (pll_conf->pll_factor > max_val ){
                return ERROR_BAD_INPUT;
        }

        CHECK_ERROR(atlas_set_reg_bit(PRIO_PWM,REG_SWITCHERS_4, BITS_PLL_EN, 
                pll_conf->pll_en));
        CHECK_ERROR(atlas_set_reg_value(PRIO_PWM,REG_SWITCHERS_4,BITS_PLL_MULTI 
                , pll_conf->pll_factor, LONG_PLL_MULTI));
        return ERROR_NONE;
}

/*!
 * This function gets configuration of switcher PLL.
 *
 * @param       pll_conf   configuration of PLL.
 *
 * @return       This function returns 0 if successful.
 */
int atlas_power_get_conf_pll(struct t_pll_conf *pll_conf)
{
        CHECK_ERROR(atlas_get_reg_bit(PRIO_PWM,REG_SWITCHERS_4, BITS_PLL_EN, 
                (int*) &(pll_conf->pll_en)));
        CHECK_ERROR(atlas_get_reg_value(PRIO_PWM,REG_SWITCHERS_4,BITS_PLL_MULTI 
                ,(int*) &(pll_conf->pll_factor), LONG_PLL_MULTI));
        return ERROR_NONE;
}

/*!
 * This function configures SW3 switcher.
 *
 * @param       sw3_conf   configuration of sw3 switcher.
 *
 * @return       This function returns 0 if successful.
 */
int atlas_power_set_sw3(struct t_sw3_conf *sw3_conf)
{
        int max_val=0;
        max_val = (1 << LONG_SW3_SET) - 1;
        if (sw3_conf->sw3_setting > max_val ){
                return ERROR_BAD_INPUT;
        }
        CHECK_ERROR(atlas_set_reg_bit(PRIO_PWM,REG_SWITCHERS_5, BIT_SW3_EN, 
                sw3_conf->sw3_en));
        CHECK_ERROR(atlas_set_reg_bit(PRIO_PWM,REG_SWITCHERS_5, BIT_SW3_CTR_DBY,
                sw3_conf->sw3_ctr_stby));
        CHECK_ERROR(atlas_set_reg_bit(PRIO_PWM,REG_SWITCHERS_5, BIT_SW3_MODE, 
                sw3_conf->sw3_op_mode));
        CHECK_ERROR(atlas_set_reg_value(PRIO_PWM,REG_SWITCHERS_5,BITS_SW3_SET,
                sw3_conf->sw3_setting, LONG_SW3_SET));
        return ERROR_NONE;
}

/*!
 * This function gets configuration of SW3 switcher.
 *
 * @param       sw3_conf   configuration of sw3 switcher.
 *
 * @return       This function returns 0 if successful.
 */
int atlas_power_get_sw3(struct  t_sw3_conf *sw3_conf)
{
        CHECK_ERROR(atlas_get_reg_bit(PRIO_PWM,REG_SWITCHERS_5, BIT_SW3_EN, 
                (int*) &(sw3_conf->sw3_en)));
        CHECK_ERROR(atlas_get_reg_bit(PRIO_PWM,REG_SWITCHERS_5, BIT_SW3_CTR_DBY,
                (int*) &(sw3_conf->sw3_ctr_stby)));
        CHECK_ERROR(atlas_get_reg_bit(PRIO_PWM,REG_SWITCHERS_5, BIT_SW3_MODE, 
                (int*) &(sw3_conf->sw3_op_mode)));
        CHECK_ERROR(atlas_get_reg_value(PRIO_PWM,REG_SWITCHERS_5,BITS_SW3_SET,
                (int*) &(sw3_conf->sw3_setting), LONG_SW3_SET));
        return ERROR_NONE;
}

/*!
 * This function is used to un/subscribe on power event IT.
 *
 * @param        event  	type of event.
 * @param        callback  	event callback function.
 * @param        sub      	define if Un/subscribe event.
 *
 * @return       This function returns 0 if successful.
 */
int atlas_power_event(t_pwr_int event, void* callback, bool sub)
{
        type_event_notification power_event;
       
        atlas_event_init(&power_event);
        power_event.callback = callback;
        switch (event) {
        case PWR_IT_BPONI:
                power_event.event = EVENT_BPONI;
        break;
        case PWR_IT_LOBATLI:
                power_event.event = EVENT_LOBATLI;
        break;
        case PWR_IT_LOBATHI:
                power_event.event = EVENT_LOBATHI;
        break;
        case PWR_IT_ONOFD1I:
                power_event.event = EVENT_ONOFD1I;
        break;
        case PWR_IT_ONOFD2I:
                power_event.event = EVENT_ONOFD2I;
        break;
        case PWR_IT_ONOFD3I:
                power_event.event = EVENT_ONOFD3I;
        break;
        case PWR_IT_SYSRSTI:
                power_event.event = EVENT_SYSRSTI;
        break;
        case PWR_IT_PWRRDYI:
                power_event.event = EVENT_PWRRDYI;
        break;
        case PWR_IT_PCI:
                power_event.event = EVENT_PCI;
        break;
        case PWR_IT_WARMI:
                power_event.event = EVENT_WARMI;
        break;
        default:
                return ERROR_BAD_INPUT;
        break;
        }
        if (sub == true) {
                CHECK_ERROR(atlas_event_subscribe(power_event));
        } else {
                CHECK_ERROR(atlas_event_unsubscribe(power_event));
        }
        return ERROR_NONE;
}

/*!
 * This function is used to subscribe on power event IT.
 *
 * @param        event  	type of event.
 * @param        callback  	event callback function.
 *
 * @return       This function returns 0 if successful.
 */
int atlas_power_event_sub(t_pwr_int event, void* callback)
{
        return atlas_power_event(event, callback, true);
}

/*!
 * This function is used to un subscribe on power event IT.
 *
 * @param        event  	type of event.
 * @param        callback  	event callback function.
 *
 * @return       This function returns 0 if successful.
 */
int atlas_power_event_unsub(t_pwr_int event, void* callback)
{
        return atlas_power_event(event, callback, false);
}

#ifdef __TEST_CODE_ENABLE__

int atlas_power_check_power(void) 
{
        int i=0;
        int val=0, val2=0;
        struct t_p_up_sense var_p_up_sense;
        struct t_power_cut_conf var_pc, var2_pc;
        
        CHECK_ERROR(atlas_power_get_power_mode_sense(&var_p_up_sense)); 
        
        CHECK_ERROR(atlas_power_get_power_cut(&val));
        CHECK_ERROR(atlas_power_get_warm_start(&val));
        CHECK_ERROR(atlas_power_get_user_off_en(&val));
        
        var_pc.pc_counter_en = false;
        var_pc.pc_auto_user_off = false;
        var_pc.pc_user_off_32k_en = false;
        var_pc.pc_timer = 5;
        var_pc.pc_counter = 4;
        var_pc.pc_max_nb_pc = 2;
        var_pc.pc_ext_timer = 3;
        var_pc.pc_ext_timer_inf = false;
        CHECK_ERROR(atlas_power_cut_conf(&var_pc));

        CHECK_ERROR(atlas_power_cut_conf(&var_pc));
        CHECK_ERROR(atlas_power_cut_get_conf(&var2_pc));
        
        if (var_pc.pc_auto_user_off != var2_pc.pc_auto_user_off) {
                TRACEMSG_POWER(_K_I("Error in pc_auto_user_off"));
        }
        if (var_pc.pc_user_off_32k_en != var2_pc.pc_user_off_32k_en) {
                TRACEMSG_POWER(_K_I("Error in pc_user_off_32k_en"));
        }
        if (var_pc.pc_timer != var2_pc.pc_timer) {
                TRACEMSG_POWER(_K_I("Error in pc_timer"));
        }
        if (var_pc.pc_counter != var2_pc.pc_counter) {
                TRACEMSG_POWER(_K_I("Error in pc_counter"));
        }
        if (var_pc.pc_counter_en != var2_pc.pc_counter_en) {
                TRACEMSG_POWER(_K_I("Error in pc_counter_en"));
        }
        if (var_pc.pc_max_nb_pc != var2_pc.pc_max_nb_pc) {
                TRACEMSG_POWER(_K_I("Error in pc_max_nb_pc"));
        }
        if (var_pc.pc_ext_timer != var2_pc.pc_ext_timer) {
                TRACEMSG_POWER(_K_I("Error in pc_ext_timer"));
        }
        if (var_pc.pc_ext_timer_inf != var2_pc.pc_ext_timer_inf) {
                TRACEMSG_POWER(_K_I("Error in pc_ext_timer_inf"));
        }

        CHECK_ERROR(atlas_power_set_bp_threshold(2));
        CHECK_ERROR(atlas_power_get_bp_threshold(&val));
        if (val!=2) {
                TRACEMSG_POWER(_K_I("Error in threshold conf"));        
        }
        CHECK_ERROR(atlas_power_set_bp_threshold(0));

        CHECK_ERROR(atlas_power_set_eol_function(false));
        CHECK_ERROR(atlas_power_get_eol_function(&val));
        if (val!=false) {
                TRACEMSG_POWER(_K_I("Error in EOL conf"));        
        }

        CHECK_ERROR(atlas_power_set_coincell_voltage(5));
        CHECK_ERROR(atlas_power_get_coincell_voltage(&val));
        if (val!=5) {
                TRACEMSG_POWER(_K_I("Error in coincell volt conf"));        
        }
        CHECK_ERROR(atlas_power_set_coincell_voltage(0));
        
        CHECK_ERROR(atlas_power_coincell_charger_en(false));
        CHECK_ERROR(atlas_power_get_coincell_charger_en(&val));
        if (val!=false) {
                TRACEMSG_POWER(_K_I("Error in charger en volt conf"));        
        }

        CHECK_ERROR(atlas_power_set_auto_reset_en(true));
        CHECK_ERROR(atlas_power_get_auto_reset_en(&val));
        if (val!=true) {
                TRACEMSG_POWER(_K_I("Error in auto reset conf"));        
        }
        CHECK_ERROR(atlas_power_set_auto_reset_en(false));

        for (i=0;i<BT_ON3B;i++) {
                CHECK_ERROR(atlas_power_set_conf_button(i, true, 2));
                CHECK_ERROR(atlas_power_get_conf_button(i, &val, &val2));
                if ( (val!=true ) || (val2!=2) ) {
                        TRACEMSG_POWER(_K_I("Error in button conf"));        
                }
                CHECK_ERROR(atlas_power_set_conf_button(i, false, 0));
        }

#ifdef __ATLAS_2_0_

        CHECK_ERROR(atlas_power_vbkup2_auto_en(true));
        CHECK_ERROR(atlas_power_get_vbkup2_auto_state(&val));
        if (val!=true) {
                TRACEMSG_POWER(_K_I("Error in auto vbkup conf"));        
        }
        CHECK_ERROR(atlas_power_vbkup2_auto_en(false));

        CHECK_ERROR(atlas_power_bat_det_en(true));
        CHECK_ERROR(atlas_power_get_bat_det_state(&val));
        if (val!=true) {
                TRACEMSG_POWER(_K_I("Error in bat detect conf"));        
        }
        CHECK_ERROR(atlas_power_bat_det_en(false));

#endif

        return ERROR_NONE;
}

int atlas_power_check_switcher(void) 
{
        /* switcher can't be tested on hardware */
        return ERROR_NONE;
}

int atlas_power_check_regulator(void) 
{
        int i=0;
        int val=0,val2=0;
        
        for (i=0;i<REGU_NUMBER;i++) {
                if( ((i == REGU_VAUDIO) || 
                     (i == REGU_VCAM)   || 
                     (i == REGU_VVIB)   ||
                     (i == REGU_VMMC1)  ||
                     (i == REGU_VMMC2)) ) { 
                val=0;        
                TRACEMSG_POWER(_K_D("TEST Regu conf %d"),i);     
                if(   !((i == REGU_VSIM) || 
                        (i == REGU_VESIM) || 
                        (i == REGU_VVIB) )) { 

                        CHECK_ERROR(atlas_power_set_regen_assig(i,true));
                        CHECK_ERROR(atlas_power_get_regen_assig(i,&val));
                        if (val!=true) {
                                TRACEMSG_POWER(_K_I("Error in Regen conf %d"),
                                        i);     
                        }
                        CHECK_ERROR(atlas_power_set_regen_assig(i,false));
                }

                CHECK_ERROR(atlas_power_regu_en(i,true));
                CHECK_ERROR(atlas_power_get_regu_state(i,&val));
                if (val!=true) {
                        TRACEMSG_POWER(_K_I("Error in Regu en %d"),i);        
                }
                CHECK_ERROR(atlas_power_regu_en(i,false));

                if(   !((i == REGU_VVIB) || 
                        (i == REGU_VRFBG)||
                        (i == REGU_GPO1) ||
                        (i == REGU_GPO2) ||
                        (i == REGU_GPO3) ||
                        (i == REGU_GPO4) )) { 
                        CHECK_ERROR(atlas_power_conf_regu(i, true, true));
                        CHECK_ERROR(atlas_power_get_regu_conf(i, &val, &val2));
                        if ( (val != true) || (val2 != true) ) {
                                TRACEMSG_POWER(_K_I("Error in Regu conf %d"),i);
                        }
                        CHECK_ERROR(atlas_power_conf_regu(i, false, false));
                }
                if(   !((i == REGU_VAUDIO) || 
                        (i == REGU_VIOHI) || 
                        (i == REGU_VRFBG) || 
                        (i == REGU_GPO1) || 
                        (i == REGU_GPO2) || 
                        (i == REGU_GPO3) || 
                        (i == REGU_GPO4) )) { 

                        CHECK_ERROR(atlas_power_set_regu(i,1));
                        CHECK_ERROR(atlas_power_get_regu(i,&val));
                        if (val!=1) {
                                TRACEMSG_POWER(_K_I("Error in regu setting %d"),
                                        i);    
                        }
                        CHECK_ERROR(atlas_power_set_regu(i,0));
                }
                }
        }
        
        /* test Vibrator */
        
        CHECK_ERROR(atlas_power_regu_en(REGU_VVIB,true));
        CHECK_ERROR(atlas_power_get_regu_state(REGU_VVIB,&val));
                if (val!=true) {
                        TRACEMSG_POWER(_K_I("Error in Regu en %d"),i);        
                }
        
        for (i=0; i<=3 ; i++) {
                CHECK_ERROR(atlas_power_set_regu(REGU_VVIB,i));
                CHECK_ERROR(atlas_power_get_regu(REGU_VVIB,&val));
                if (val!=1) {
                      TRACEMSG_POWER(_K_I("Error in regu setting %d"),
                              i);    
                }
                msleep(3000);
        }
        CHECK_ERROR(atlas_power_set_regu(REGU_VVIB,0));
        CHECK_ERROR(atlas_power_regu_en(REGU_VVIB,false));
        
        CHECK_ERROR(atlas_power_set_regen_inv(false));
        CHECK_ERROR(atlas_power_get_regen_inv(&val));
        if (val!=true) {
                TRACEMSG_POWER(_K_I("Error in Regen conf"));        
        }
        return ERROR_NONE;
}

#endif /* __TEST_CODE_ENABLE__ */

/*!
 * This function implements IOCTL controls on a Atlas power device.
 *
 * @param        inode       pointer on the node
 * @param        file        pointer on the file
 * @param        cmd         the command
 * @param        arg         the parameter
 * @return       This function returns 0 if successful.
 */
static int atlas_power_ioctl( struct inode *inode,  struct file *file, 
        unsigned int cmd, unsigned long arg)
{

        if (_IOC_TYPE(cmd) != 'P')
                return -ENOTTY;

        switch (cmd) {
        case ATLAS_POWER_INIT :
                TRACEMSG_POWER(_K_D("Init Power"));
                CHECK_ERROR(0);
                break;
#ifdef __TEST_CODE_ENABLE__
        case ATLAS_POWER_FULL_CHECK :
                TRACEMSG_POWER(_K_D("Check Power"));
                CHECK_ERROR(atlas_power_check_power());
                CHECK_ERROR(atlas_power_check_switcher());
                CHECK_ERROR(atlas_power_check_regulator());
                break;		
        case ATLAS_POWER_CONTROL_CHECK :
                TRACEMSG_POWER(_K_D("Check Power"));
                CHECK_ERROR(atlas_power_check_power());
                break;		
        case ATLAS_SWITCHER_CHECK :
                TRACEMSG_POWER(_K_D("Check Switcher"));
                CHECK_ERROR(atlas_power_check_switcher());
                break;		
        case ATLAS_REGULATOR_CHECK :
                TRACEMSG_POWER(_K_D("Check Regulator"));
                CHECK_ERROR(atlas_power_check_regulator());
                break;		
#endif /* __TEST_CODE_ENABLE__ */
        default :
                TRACEMSG_POWER(_K_D("%d unsupported ioctl command"),(int) cmd);
                return -EINVAL;
        }
        return ERROR_NONE;
}

/*!
 * This function implements the open method on a Atlas power device.
 *
 * @param        inode       pointer on the node
 * @param        file        pointer on the file
 * @return       This function returns 0.
 */
static int atlas_power_open( struct inode *inode,  struct file *file) 
{
        return ERROR_NONE;
}

/*!
 * This function implements the release method on a Atlas power device.
 *
 * @param        inode       pointer on the node
 * @param        file        pointer on the file
 * @return       This function returns 0.
 */
static int atlas_power_release( struct inode *inode, struct file *file) 
{
        return ERROR_NONE;
}

static struct file_operations atlas_power_fops = {
        .owner	 =	THIS_MODULE,
        .ioctl	 =	atlas_power_ioctl,
        .open	 =	atlas_power_open,
        .release =	atlas_power_release,
};

/*
 * Init and Exit
 */

static int __init atlas_power_init(void)
{
        atlas_power_major = register_chrdev(0, "atlas_power", 
                &atlas_power_fops);
        
        if (atlas_power_major < 0) {
                TRACEMSG_POWER(_K_D("Unable to get a major for atlas_power"));
                return -1;
        }
        
        devfs_mk_cdev(MKDEV(atlas_power_major,0), S_IFCHR | S_IRUGO | S_IWUSR,
                "atlas_power");

        driver_register(&atlas_power_driver_ldm);

        platform_device_register(&atlas_power_ldm);
        
        printk(KERN_INFO "Atlas Power loading\n");
        
        return 0;
}

static void __exit atlas_power_exit(void)
{
        devfs_remove("atlas_power");

        driver_unregister(&atlas_power_driver_ldm);

        platform_device_unregister(&atlas_power_ldm);

        unregister_chrdev(atlas_power_major, "atlas_power");

        TRACEMSG_POWER(_K_I("Atlas_power : successfully unloaded"));
}

/*
 * Module entry points
 */

subsys_initcall(atlas_power_init);
module_exit(atlas_power_exit);

MODULE_DESCRIPTION("atlas_power driver");
MODULE_AUTHOR("FreeScale");
MODULE_LICENSE("GPL");
