view src/cs/drivers/drv_app/lcc/lcc_handle_message.c @ 286:ee16d57b32b2

Condat audio_Init(): rm setting of sidetone level The default sidetone level in the absence of audio mode loading is just that, a basic default without any promises of good tuning. In the original TCS211 code this default sidetone level was set in two places: first in L1 init of ABB-via-DSP registers (set to -17 dB), and then initialized again in Condat audio_Init(), this time set to -5 dB. The present change removes the redundant second initialization. The default sidetone level is now -17 dB instead of -5 dB with this change, but again it is just the default; all serious users are now expected to use audio mode config files.
author Mychaela Falconia <falcon@freecalypso.org>
date Tue, 09 Nov 2021 02:16:59 +0000
parents 4e78acac3d88
children
line wrap: on
line source

/******************************************************************************
 * Power Task (pwr)
 * Design and coding by Svend Kristian Lindholm, skl@ti.com
 *
 * PWR Message Processing
 *
 * $Id: pwr_handle_message.c 1.3 Wed, 20 Aug 2003 15:17:25 +0200 skl $
 *
 ******************************************************************************/
#include <string.h>
#include "lcc/lcc.h"
#include "lcc/lcc_api.h"
#include "lcc/lcc_trace.h"
#include "lcc/lcc_task.h"
#include "lcc/lcc_tm_i.h"
#include "lcc/lcc_cfg_i.h"
#include "lcc/lcc_cfg.h"
#include "lcc/lcc_modulate.h"

#include "rv/rv_defined_swe.h"

#include "ffs/ffs.h"

#include "etm/etm_env.h"

#include "abb/abb.h"
/******************************************************************************
 * Function prototypes
 ******************************************************************************/

void ttr(unsigned trmask, char *format, ...);
void str(unsigned mask, char *string);

void *pwr_malloc(int size);

T_RVM_RETURN pwr_task_init(void);

UINT32 pwr_timer_elapsed(UINT32 time_begin, UINT32 current_timer);
void build_name(const char *ch_pre, char *cfg_id , UINT8 index, const char * ch_post,  char * name);

void pwr_modulate_init(void);
void pwr_modulate_on(void);
int pwr_capacity(uint16 Vbat);

void pwr_check_timers();
void pwr_stop_timers();

T_RVM_RETURN pwr_start_timer(UINT32 *timer_begin);
T_RVM_RETURN pwr_stop_timer(UINT32 *timer_begin);

T_RVM_RETURN pwr_check_files(void);
T_RVM_RETURN pwr_read_files(void);
T_RVM_RETURN pwr_read_chg_files(void);
T_RVM_RETURN pwr_read_cal_files(void);

void pwr_send_msg(uint32 msg_id ,T_RVF_ADDR_ID src_addr_id, T_RVF_ADDR_ID  dest_addr_id);
void mmi_send_msg(struct mmi_info_ind_s *event);

void start_q401_charge(void);
void start_q402_charge(void);
void start_pre_charge(void);
void stop_q401_charge(void);
void stop_q402_charge(void);
void stop_pre_charge(void);
void charger_unplug_house_keeping(void);
void cv_charging_house_keeping(void);
void end_charging_house_keeping(void);
int check_chg_unplug(void);

#if (TEST_PWR_MMI_INTERFACE == 1)
    #include "lcc/pwr_mmi_api.c"
#endif

extern T_PWR_CTRL_BLOCK *pwr_ctrl;
extern T_PWR_CFG_BLOCK  *pwr_cfg;

/******************************************************************************
 * Functions 
 ******************************************************************************/

void build_name(const char *ch_pre, char *cfg_id, UINT8 index, const char * ch_post,  char * name) 
{
    char tmp[2];

    strcpy(name, ch_pre);
    tmp[0] = *cfg_id;
    tmp[1] = 0; // null-termination of strings!
    strcpy(&(name[index]), tmp);
    strcat(name, ch_post);
    ttw(ttr(TTrInit, "build_name: '%s'" NL, name));
}


T_RVM_RETURN pwr_task_init()
{
// Perform battery initialization of the pwr task by
// reading battery id and FFS configuration files
    T_FFS_SIZE       error;

    ttw(ttr(TTrInit, "pwr_task_init(%d)" NL, 0));

    // Check configuration and calibration files
    if ((error = pwr_check_files()) < 0)
        return error;

    // Read configuration files
    if ((error = pwr_read_files()) < 0)
        return error;

    ttw(ttr(TTrInit, "pwr_task_init(%d)" NL, 0xFF));
    return RV_OK;

}

int pwr_chg_start(int Tbat) {
    int capacity;
    // Tbat is the temperature measured in Celsius
    // Check start conditions for charging - it is assumed that a charger is plugged
    // This check is used in state SUP

    // Charging start will _not_ be performed if ...
    // 1) Battery temperature is outside limits (measurement)
    // 2) Charger identity is unknown

    ttw(ttr(TTrCharge, "pwr_chg_start(%d)" NL, 0));

    // 1)
    // Battery temperature is outside limits (measurement)
    // FIXME: Check also low temperatures
    //    if ((Tbat < pwr_cfg->temp.tbat_min) || (Tbat > pwr_cfg->temp.tbat_max)) {
    if ((Tbat > pwr_cfg->temp.tbat_max)) {
        ttw(ttr(TTrCharge, "pwr_chg_start(%d) Tbat=(%d)" NL, 0xFF, Tbat));
        return FALSE;
    }

    // 2) 
    // Charger identity unknown
    if ((pwr_ctrl->flag_chg_unknown == 1) && (pwr_ctrl->flag_mmi_registered == 1)) {
        ttw(ttr(TTrCharge, "pwr_chg_start(%d) Chg unknown=(%d)" NL, 0xFF, Tbat));
        pwr_ctrl->mmi_ptr->header.msg_id = MMI_CHG_UNKNOWN_IND;
        mmi_send_msg(pwr_ctrl->mmi_ptr);
        // Send only once
        pwr_ctrl->flag_bat_unknown = 0;
        return FALSE;
    }

    ttw(ttr(TTrCharge, "pwr_chg_start(%d)" NL, 0xFF));
    return TRUE;
}

int pwr_chg_stop(int Tbat, int Vbat) {
    // Check stop conditions for charging
    // I.e.
    // - temperature (RF+Battery) thresholds
    // - battery voltage thresholds
    // - regulation loop gain (duty cycle)
    //
    // This check is used regularly in state CCI, LCI, CCV and LCV

    // Charging will be stopped if ...
    // 1) Battery temperature is outside limits
    // 2) Battery voltage is equal to - or above - charging stop battery voltage

    // Nickel checks:
    // 3) The RF temperature versus battery temperature rise is above threshold (Possible?)
    //
    // Lithium checks:
    // 4) The regulation parameter k is below configured threshold limit
    // Returns TRUE if the charging algorithm should be stopped
    // Returns FALSE if the charging algorithm should be continued


    ttw(ttr(TTrCharge, "pwr_chg_stop(%d)" NL, 0));

    // 1)
    // Battery temperature is outside limits
    // FIXME: Check also low temperatures
    if (Tbat > pwr_cfg->temp.tbat_max) {
        ttw(ttr(TTrCharge, "pwr_chg_stop(%d) Tbat=(%d)" NL, 0xFF, Tbat));
        if (pwr_ctrl->flag_mmi_registered == 1) {
            pwr_ctrl->mmi_ptr->header.msg_id = MMI_CHG_FAILED_IND;
            pwr_ctrl->mmi_ptr->cause         = BATTERY_TEMPERATURE_HIGH;
            mmi_send_msg(pwr_ctrl->mmi_ptr);
        }
        return TRUE;
    }

    // 2)
    // Battery voltage is equal to - or above - charging stop battery voltage
    if ((Vbat > pwr_cfg->bat.chg_stop_thr)) {
        ttw(ttr(TTrCharge, "pwr_chg_stop(%d) Vbat=(%d)" NL, 0xFF, Vbat));
        ttw(ttr(TTrCharge, "pwr_cfg->bat.chg_stop_thr=%d" NL, 0xFF, pwr_cfg->bat.chg_stop_thr));
        return TRUE;
    }
#if 0
#ifndef PWR_FFS_CFG
    pwr_cfg->common.rise_thr = PWR_RISE_THR ;
#endif

    // 3)
    // The RF temperature versus battery temperature rise is above threshold (Possible?)
    // Did the battery temperature rise more than the RF temperature rise - stop for Nickel charging!
    // Only for Nickel batteries
    // FIXME: How big time difference should there be between the deltas? Configurable?
    if ((pwr_cfg->data.dTbat - pwr_cfg->data.dT_rf > pwr_cfg->common.rise_thr) == TRUE) {
        ttw(ttr(TTrCharge, "pwr_chg_stop(%d) d(Tbat-Trf)=(%d)" NL, 0xFF, pwr_cfg->data.dTbat - pwr_cfg->data.dT_rf));
        return TRUE;
    }
#endif

    // 4) FIXME: Remove states in this part of the function
    // Regulation parameter k is below the configured limit
    if ((pwr_cfg->bat.type == LITHIUM) &&
        ((pwr_ctrl->state == LCV) || (pwr_ctrl->state == CCV)) &&
        (pwr_cfg->data.k < pwr_cfg->bat.chg_ctrl_thr)) {
        ttw(ttr(TTrCharge, "k=%d < %d" NL, pwr_cfg->data.k, pwr_cfg->bat.chg_ctrl_thr));
        return TRUE;
    }

    ttw(ttr(TTrCharge, "pwr_chg_stop(%d)" NL, 0xFF));
    return FALSE;
}

int pwr_chg_ci_cv_transition(int Vbat) {
    // Check CI -> CV condition for charging
    // Only for Lithium batteries
    // Returns TRUE if CI-> CV transition should be made
    // Returns FALSE if CI-> CV transition should NOT be made

    ttw(ttr(TTrCharge, "pwr_chg_ci_cv_transition(%d)" NL, 0));

    // 1) Check CV charging start threshold
    if ((Vbat > pwr_cfg->bat.chg_start_thr)) {
        ttw(ttr(TTrCharge, "pwr_chg_ci_cv_transition(%d) Vbat=(%d)" NL, 0xFF, Vbat));
        return TRUE;
    }

    ttw(ttr(TTrCharge, "pwr_chg_ci_cv_transition(%d)" NL, 0xFF));
    return FALSE;
}

int pwr_capacity(uint16 Vbat) {
    // Calculate the capacity, C(T,V), of the battery based on
    // - Battery Voltage Measurement
    // - Battery Temperature Measurement
    // - Configuration Parameters
    // Returns a value in the interval 0..100 [%]
    uint8 i;
    uint8 cap;

    ttw(ttr(TTrEventLow,"pwr_capacity (Vbat=%d)" NL, Vbat));
    cap = 100; // 100%
    for (i=0;i<=9;i++) {
        if (Vbat < pwr_cfg->temp.cap[i] ) {
            cap -= 10; // Count down 10% for each index
            ttw(ttr(TTrEventLow,"Trying cap=%d" NL, cap));
        } else {
            // FIXME: Interpolate ... instead of truncate
            ttw(ttr(TTrEventLow,"capacity=%d" NL, cap));
            return cap;
        }
    }
    ttw(ttr(TTrEventLow,"pwr_capacity (%d)" NL, 0xFF));
    return 0; // Couldn't lookup capacity
}

int8 pwr_temp_lookup(uint16 ADC, uint8 i_meas) {
// temperature = f(ADC, I_meas)
// f(x) is table-lized using ETM command 'pww 4 ...'

    int i;
    int temp;

#define MAX_TEMP_SIZE 9

    i = 0;
    temp = -20;
    // FIXME: Always using the second current - should also use the first...
    while (ADC < pwr_cfg->temp.v2t_2[i] && i < MAX_TEMP_SIZE) {
        ttw(ttr(TTrEventLow,"ADC=%d < pwr_cfg->temp.v2t_2[%d]=0x%x" NL, ADC,i, pwr_cfg->temp.v2t_2[i]));
        ttw(ttr(TTrEventLow,"temp=%d" NL, temp));
        i++;
        temp += 10;
    }
    if (i == MAX_TEMP_SIZE) {
        // No temperature found - return NULL value (0xFF)
        ttr(TTrWarning, "Temperature lookup failed %d" NL, ADC);
        return 0xFF;
    }
    if (i != 0) {
        // Interpolate
        if (pwr_cfg->temp.v2t_2[i-1] == 0xFFFF)
            temp += 10*(ADC - pwr_cfg->temp.v2t_2[i])/(pwr_cfg->temp.v2t_2[i] - 1024);
        else
            temp += 10*(ADC - pwr_cfg->temp.v2t_2[i])/(pwr_cfg->temp.v2t_2[i] - pwr_cfg->temp.v2t_2[i-1]);
        ttw(ttr(TTrEventLow,"Interpolated temp=%d" NL, temp));
    }
    return ((int8)temp);
}


enum {
    Vbat = 0,
    Vchg = 1,
    Ichg = 2,
    Vbt2 = 3,
    Type = 4,
    Tbat = 5,
    T_rf = 6,
    Tc_x = 7,
    Tc_y = 8,
    State= 9
} adc_index_e;

// The driving element of the LCC task - the ADC measurements
T_RV_RET process_spi_adc_indication (T_PWR_REQ *request)
{
    struct pwr_adc_ind_s *ind;
    int error, i, index;
    int8 temp;
    UINT32 timer;
    UINT16 status; // Result of charger plug - unplug - polled

    ind = (struct pwr_adc_ind_s*)request;

    ttw(ttr(TTrEventLow,"State=%d" NL, ind->data[State]));

    // Basic trace - not for CAL
    switch (pwr_ctrl->state) {
    case PRE :
        ttw(ttr(TTrEventLow,"Vbat=%d" NL, ADC_to_mV(ind->data[Vbat])));
        ttw(ttr(TTrEventLow,"Vchg=%d" NL, ADC_to_mV(ind->data[Vchg])));
        ttw(ttr(TTrEventLow,"Ichg=%d" NL, ind->data[Ichg]));
        break;
    case INI :
    case SUP :
    case CCI :
    case LCI :
    case CCV :
    case LCV :
        ttw(ttr(TTrEventLow,"Vbat=%d" NL, ADC_to_mV(ind->data[Vbat])));
        ttw(ttr(TTrEventLow,"Vbat_adc=%d" NL, ind->data[Vbat]));
#if (USE_Q402_CHG_CIRCUIT == 1)
        // Vchg Scaling factor for Q402 circuit
        pwr_cfg->data.Vchg = 5*ADC_to_mV(ind->data[Vchg])/4;
        ttw(ttr(TTrEventLow,"Vchg=%d" NL, pwr_cfg->data.Vchg));
#else
        pwr_cfg->data.Vchg = ADC_to_mV(ind->data[Vchg]);
        ttw(ttr(TTrEventLow,"Vchg=%d" NL, pwr_cfg->data.Vchg));
#endif
        ttw(ttr(TTrEventLow,"Ichg=%d" NL, ind->data[Ichg]));
        ttw(ttr(TTrEventLow,"Type=%d" NL, ind->data[Type]));
        ttw(ttr(TTrEventLow,"Tbat=%d" NL, ind->data[Tbat]));
        ttw(ttr(TTrEventLow,"T_rf=%d" NL, ind->data[T_rf]));
        ttw(ttr(TTrEventLow,"Vbt2=%d" NL, ADC_to_mV(ind->data[Vbt2])));
        break;
    }

    // Updates variables in 'service' mode - not for CAL, PRE & INI
    switch (pwr_ctrl->state) {
    case SUP :
    case CCI :
    case LCI :
    case CCV :
    case LCV :
        index = pwr_ctrl->index % CONSECUTIVE_CHG_UNPLUGS;
        ttw(ttr(TTrEventLow,"Using index: (%d)" NL, index));
        pwr_ctrl->index++;
        // Get current nucleus time
        timer  = rvf_get_tick_count();

        // T0 timer expired?
        pwr_ctrl->time_elapsed_T0  = pwr_timer_elapsed(pwr_ctrl->time_begin_T0, timer);
        ttw(ttr(TTrTimerLow,"T0: %d ms elapsed " NL, pwr_ctrl->time_elapsed_T0));

        // Compare T0 with configured sampling rate
        if (pwr_ctrl->time_elapsed_T0 > pwr_cfg->common.sampling) {
            ttw(ttr(TTrTimer, "T0 Reset: elapsed (%d)" NL, pwr_ctrl->time_elapsed_T0));
            pwr_ctrl->time_begin_T0 = timer;
            ttr(TTrAll,"Vbat_avg=%d" NL, ADC_to_mV(pwr_cfg->data.Vbat_avg));

        }

        pwr_cfg->data.Vbat = ind->data[Vbat]; 
        pwr_cfg->data.Vbat_avg = (ind->data[Vbat] + pwr_cfg->common.alfa1 * pwr_cfg->data.Vbat_avg)/(pwr_cfg->common.alfa1 + 1);
        pwr_cfg->data.Vbat_avg_mV = ADC_to_mV(pwr_cfg->data.Vbat_avg);
        ttw(ttr(TTrEventLow,"Vbat_avg=%d" NL, pwr_cfg->data.Vbat_avg_mV));

        pwr_cfg->data.Tbat = ind->data[Tbat];
        pwr_cfg->data.Tbat_avg = (ind->data[Tbat] + pwr_cfg->common.alfa2 * pwr_cfg->data.Tbat_avg)/(pwr_cfg->common.alfa2 + 1);
        ttw(ttr(TTrEventLow,"Tbat_avg=%d" NL, pwr_cfg->data.Tbat_avg));
        temp = pwr_temp_lookup(pwr_cfg->data.Tbat_avg, 0x01);
        ttw(ttr(TTrEventLow,"temp=%d" NL, temp));

        pwr_cfg->data.T_rf_avg = ind->data[T_rf];
        pwr_cfg->data.T_rf_avg = (ind->data[T_rf] + pwr_cfg->common.alfa3 * pwr_cfg->data.T_rf_avg)/(pwr_cfg->common.alfa3 + 1);
        ttw(ttr(TTrEventLow,"T_rf_avg=%d" NL, pwr_cfg->data.T_rf_avg));

        pwr_cfg->data.Ichg = ind->data[Ichg];
        break;
    }

    // 'Main' switch
    ttw(ttr(TTrEventLow,"state=%d" NL, pwr_ctrl->state));
    switch (pwr_ctrl->state) {
    case CAL :
        // Read calibration files
        // Decide if SW precharge should be started (state PRE) or if a normal configuration scheme should be inititated (state INI)
        error = pwr_read_cal_files();
        if (error >= 0) {
            // Save measurements - also initial values for averaging
            pwr_cfg->data.Vbat_avg = pwr_cfg->data.Vbat = ind->data[Vbat];
            pwr_cfg->data.Vbat_avg_mV = ADC_to_mV(pwr_cfg->data.Vbat_avg);
            ttw(ttr(TTrEventLow,"Vbat=%d" NL, ADC_to_mV(ind->data[Vbat])));
            ttw(ttr(TTrEventLow,"Vchg=%d" NL, ADC_to_mV(ind->data[Vchg])));

            // Charger was already inserted?
            status = ind->data[State];

            if (status & CHGPRES) {
                // Charger plugged caused a wakeup
                pwr_ctrl->flag_chg_plugged = 1;
                pwr_ctrl->flag_chg_prev_plugged = 1;
                ttw(ttr(TTrInitLow,"Polled - charger plugged  (%d)" NL, status));
            }

            // Check Pre-charge - immediately change state to PRE
            if ((pwr_cfg->data.Vbat_avg_mV < VBAT_PRECHG_START) && (pwr_ctrl->flag_chg_plugged == 1)) {
                ttw(ttr(TTrInitLow,"precharge (%d)" NL, ADC_to_mV(ind->data[Vbat])));
                // Charger interrupts (plug/unplug) are already masked off - in case it was a linear charger
                pwr_ctrl->state = PRE;
            } else {
                pwr_ctrl->state = INI;
            }
        }
        break;
    case PRE :
        // Start fast charge immediately after 3.2V boot
        // Enter INI state - in order to read FFS configuration files - when Vbat reaches 3.6V

        // Hardcoding moving average to PRECHG_AVG_WINDOW_SIZE since we haven't read any configuration
        pwr_cfg->data.Vbat = ind->data[Vbat];
        pwr_cfg->data.Vbat_avg = (ind->data[Vbat] + PRECHG_AVG_WINDOW_SIZE  * pwr_cfg->data.Vbat_avg)/(PRECHG_AVG_WINDOW_SIZE + 1); 
        pwr_cfg->data.Vbat_avg_mV = ADC_to_mV(pwr_cfg->data.Vbat_avg);
        if (pwr_cfg->data.Vbat_avg_mV > VBAT_PRECHG_STOP) {
            ttw(ttr(TTrInitLow,"state PRE (%d > VBAT_PRECHG_STOP)" NL, pwr_cfg->data.Vbat_avg_mV));
            pwr_ctrl->state = INI;
            pwr_ctrl->flag_prechg_started = 0;
            stop_pre_charge();
        } else {
            ttw(ttr(TTrInitLow,"state PRE (%d < VBAT_PRECHG_STOP)" NL, pwr_cfg->data.Vbat_avg_mV));
            // Start fast charging NOW
            if (pwr_ctrl->flag_prechg_started == 0) {
                pwr_ctrl->flag_prechg_started = 1;
                start_pre_charge();
            }
        }
        break;
    case INI :
        // Charger was already inserted?
        status = ind->data[State];
        pwr_ctrl->chg_unplug_vec[index] = (status & CHGPRES);
        if (status & CHGPRES) {
            // Charger plugged caused a wakeup
            pwr_ctrl->flag_chg_plugged = 1;
            pwr_ctrl->flag_chg_prev_plugged = 1;
            ttw(ttr(TTrInitLow,"Polled - charger plugged  (%d)" NL, status));
        }

        if (pwr_ctrl->flag_ini_virgo == 0) {
            // Perform some very basic initialization

            /* Precharge (C/20) is switched OFF since it was finished in state PRE */
            ABB_Write_Register_on_page(PAGE0, BCICTL2, 0x0002);

            // Enable battery type identification
            // Switch on battery type identification (TYPEN)
            // NOTE: Battery type identification is ALWAYS done with 10uA (TYPEN)
            ttw(ttr(TTrInitLow,"Enable type ident (%d)" NL, 0x00));
            ABB_Write_Register_on_page(PAGE0, BCICTL1, THERMAL_SENSOR_30uA | TYPEN);
            pwr_ctrl->flag_ini_virgo = 1;

        }

        // Don't read battery files before we know which battery id to use
        // We will ignore the first BATTERY_TYPE_SLIP measurements of battery id
        if ((pwr_ctrl->count_bat_type == BATTERY_TYPE_SLIP))  {

            error = pwr_task_init();

            // Change state
            pwr_ctrl->state = SUP;

            // Readout /pwr/common.cfg
            ttw(ttr(TTrInitLow,"common.cfg: pins: %d" NL, pwr_cfg->common.pins));
            ttw(ttr(TTrInitLow,"chg_dedic %d" NL, pwr_cfg->common.chg_dedic));
            ttw(ttr(TTrInitLow,"sampling %d" NL, pwr_cfg->common.sampling));
            ttw(ttr(TTrInitLow,"mod_cycle %d" NL, pwr_cfg->common.mod_cycle));
            ttw(ttr(TTrInitLow,"alfa1 %d" NL, pwr_cfg->common.alfa1));
            ttw(ttr(TTrInitLow,"alfa2 %d" NL, pwr_cfg->common.alfa2));
            ttw(ttr(TTrInitLow,"alfa3 %d" NL, pwr_cfg->common.alfa3));
            ttw(ttr(TTrInitLow,"rise_thr %d" NL, pwr_cfg->common.rise_thr));

            // Readout /pwr/bat/bat.cfg
            ttw(ttr(TTrInitLow,"bat.cfg: type: %d" NL, pwr_cfg->bat.type));
            ttw(ttr(TTrInitLow,"rf_temp: %d" NL, pwr_cfg->bat.rf_temp));
            ttw(ttr(TTrInitLow,"id_low: %d" NL, pwr_cfg->bat.id_low));
            ttw(ttr(TTrInitLow,"id_high: %d" NL, pwr_cfg->bat.id_high));
            ttw(ttr(TTrInitLow,"cbat: %d" NL, pwr_cfg->bat.cbat));
            ttw(ttr(TTrInitLow,"ratio: %d" NL, pwr_cfg->bat.ratio));
            ttw(ttr(TTrInitLow,"T1: %d" NL, pwr_cfg->bat.T1));
            ttw(ttr(TTrInitLow,"T2: %d" NL, pwr_cfg->bat.T2));
            ttw(ttr(TTrInitLow,"T3: %d" NL, pwr_cfg->bat.T3));
            ttw(ttr(TTrInitLow,"chg_start_thr: %d" NL, pwr_cfg->bat.chg_start_thr));
            ttw(ttr(TTrInitLow,"chg_stop_thr: %d" NL, pwr_cfg->bat.chg_stop_thr));
            ttw(ttr(TTrInitLow,"chg_ctrl_thr: %d" NL, pwr_cfg->bat.chg_ctrl_thr));
            ttw(ttr(TTrInitLow,"chg_again_thr: %d" NL, pwr_cfg->bat.chg_again_thr));

            // Readout /pwr/bat/temp<N>.cfg
            ttw(ttr(TTrInitLow,"sizeof(temp): %d" NL, sizeof(pwr_cfg->temp)));
            ttw(ttr(TTrInitLow,"sizeof(T_PWR_BAT_TEMP_CFG_BLOCK): %d" NL, sizeof(T_PWR_BAT_TEMP_CFG_BLOCK)));
            ttw(ttr(TTrInitLow,"temp.cfg: tbat_min: %d" NL, pwr_cfg->temp.tbat_min));
            ttw(ttr(TTrInitLow,"tbat_max: %d" NL, pwr_cfg->temp.tbat_max));
            ttw(ttr(TTrInitLow,"i_meas1: %d" NL, pwr_cfg->temp.i_meas1));
            for (i=0;i<=8;i++) {
                ttw(ttr(TTrInitLow,"v2t_1[]: %d" NL, pwr_cfg->temp.v2t_1[i]));
            }
            ttw(ttr(TTrInitLow,"i_meas2: %d" NL, pwr_cfg->temp.i_meas2));
            for (i=0;i<=8;i++) {
                ttw(ttr(TTrInitLow,"v2t_2[]: %d" NL, pwr_cfg->temp.v2t_2[i]));
            }
            for (i=0;i<=9;i++) {
                ttw(ttr(TTrInitLow,"cap[]: %d" NL, pwr_cfg->temp.cap[i]));
            }
            ttw(ttr(TTrInitLow,"a0: %d" NL, pwr_cfg->temp.a0));
            ttw(ttr(TTrInitLow,"a1: %d" NL, pwr_cfg->temp.a1));
            ttw(ttr(TTrInitLow,"a2: %d" NL, pwr_cfg->temp.a2));

            // Readout /mmi/pwr/bsie.cfg
            ttw(ttr(TTrInitLow,"mmi repetition: %d" NL, pwr_cfg->mmi.repetition));

            // Initialize battery temperature - must not be made in the virgo part since Tbat=0
            pwr_cfg->data.Tbat_avg = ind->data[Tbat];

            // Setup thermal sensor - don't re-enable TYPEN
            if (pwr_cfg->temp.i_meas2 == 50) {
                ttw(ttr(TTrInitLow,"ABB set i_current: %d" NL, pwr_cfg->temp.i_meas2));
                ABB_Write_Register_on_page(PAGE0, BCICTL1, THERMAL_SENSOR_50uA);
            } else  if (pwr_cfg->temp.i_meas2 == 30) {
                ttw(ttr(TTrInitLow,"ABB set i_current: %d" NL, pwr_cfg->temp.i_meas2));
                ABB_Write_Register_on_page(PAGE0, BCICTL1, THERMAL_SENSOR_30uA);
            } else {
                // Default 30uA
                ttw(ttr(TTrInitLow,"ABB set i_current default: %d" NL, pwr_cfg->temp.i_meas2));
                ABB_Write_Register_on_page(PAGE0, BCICTL1, THERMAL_SENSOR_30uA);
            }

        } else {
            // Compare battery id with values found in FFS
            // First value(s) are NOT to be trusted
            pwr_ctrl->count_bat_type++;
            pwr_cfg->data.bat_id = ind->data[Type];

        }
        break;
    case SUP :
        // Charger inserted?? Poll!!
        // Reason: 100Hz interrupts will occur if an unregulated charger is inserted & charger interrupts are enabled
        //         This will cause an immediate crash!
        status = ind->data[State];
        pwr_ctrl->chg_unplug_vec[index] = (status & CHGPRES);
        if (status & CHGPRES) {
            // Charger is plugged - continue
            ttw(ttr(TTrEventLow,"Polled - chg plugged (%d)" NL, status));
            pwr_ctrl->flag_chg_plugged = 1;
            pwr_ctrl->capacity = pwr_capacity(pwr_cfg->data.Vbat_avg_mV);
        } else {
            pwr_ctrl->flag_chg_plugged = 0;
        }

        if (pwr_ctrl->flag_mmi_registered == 1)  {

            // If the charger is plugged and no charging is initiated this message will be repeated until charging has started
            // Charger plugged
            if ((pwr_ctrl->flag_chg_plugged == 1) &&  (pwr_ctrl->flag_chg_prev_plugged == 0)) {
                pwr_ctrl->flag_chg_prev_plugged = 1;
                pwr_ctrl->mmi_ptr->header.msg_id = MMI_CHG_PLUG_IND;
                mmi_send_msg(pwr_ctrl->mmi_ptr);
            }
            // Charger unplugged
            if ((pwr_ctrl->flag_chg_plugged == 0) && (pwr_ctrl->flag_chg_prev_plugged == 1)) {
                pwr_ctrl->flag_chg_prev_plugged = 0;
                pwr_ctrl->mmi_ptr->header.msg_id = MMI_CHG_UNPLUG_IND;
                mmi_send_msg(pwr_ctrl->mmi_ptr);
            }

            if (pwr_ctrl->flag_bat_unknown == 1) {
                // Battery unknown - inform the MMI
                pwr_ctrl->mmi_ptr->header.msg_id = MMI_BAT_UNKNOWN_IND;
                mmi_send_msg(pwr_ctrl->mmi_ptr);
                // Send only once
                pwr_ctrl->flag_bat_unknown = 0;
            }

        }
        pwr_check_timers();

        // If charger is plugged AND charger configuration is not read AND capacity is below configured limit - charger id Vchg(Load=0) must be measured
        if ((pwr_ctrl->flag_chg_plugged == 1) && 
            (pwr_ctrl->flag_chg_cfg_read == 0) && 
            (pwr_ctrl->capacity < pwr_cfg->bat.chg_again_thr)
           ) {

            // Measurement loop - Take the 2nd Vchg(0) measurements
            if (pwr_ctrl->count_chg_type == CHARGER_TYPE_SLIP)  {
                if (pwr_cfg->data.Vchg > CHARGER_TYPE_OUT_OF_BOUND_LOW)
                    pwr_cfg->data.chg_id = pwr_cfg->data.Vchg;
                else {
                    // Out of bound!!
                    ttr(TTrWarning, "Charger id out of bound! %d" NL, pwr_cfg->data.Vchg);
                    pwr_cfg->data.chg_id = CHARGER_TYPE_TYPICAL;
                }
                ttw(ttr(TTrEventLow,"Using chg id=%d" NL, pwr_cfg->data.chg_id));
                error = pwr_read_chg_files();
                pwr_ctrl->flag_chg_cfg_read = 1;

                // Initialize the circular buffer with charger plugs
                for (i=0;i<= CONSECUTIVE_CHG_UNPLUGS-1 ;i++)
                    pwr_ctrl->chg_unplug_vec[i] = CHGPRES;

                if (pwr_chg_start(pwr_temp_lookup(pwr_cfg->data.Tbat_avg, 0x01)) == TRUE ) {
                    if (pwr_cfg->chg.type == CI) {
                        pwr_ctrl->state = CCI;
                        // Enable charger plug/unplug interrupts for a CI charger
                        ABB_Write_Register_on_page(PAGE0, ITMASK, ALL_IT_UMSK);
                    } else {
                        if (pwr_cfg->chg.type == UNREGULATED)
                            pwr_ctrl->state = LCI;
                        else {
                            ttr(TTrWarning, "Unknown charger type! %d" NL, pwr_cfg->chg.type);
                            pwr_ctrl->state = LCI;
                        }
                    }
#if (USE_Q401_CHG_CIRCUIT == 1)
                    start_q401_charge();
#endif

#if (USE_Q402_CHG_CIRCUIT == 1)
                    start_q402_charge();
#endif

                    // Stop the mmi info repetition timer
                    pwr_stop_timer(&pwr_ctrl->time_begin_mmi_rep);
                }
            } else {
                pwr_ctrl->count_chg_type++;
                ttw(ttr(TTrEventLow,"count_chg_type=%d" NL, pwr_ctrl->count_chg_type));
            }
        }
        break;
    case CCI :
        pwr_check_timers();
        // We are in the middle of a CI charging process - check stop criterias
        //
        if ( pwr_chg_stop(pwr_temp_lookup(pwr_cfg->data.Tbat_avg, 0x01), pwr_cfg->data.Vbat_avg_mV) == FALSE) {
            // Continue to charge - change state?
            if ((pwr_cfg->bat.type == LITHIUM) && (pwr_chg_ci_cv_transition(pwr_cfg->data.Vbat_avg_mV)== TRUE)) {
                // Change state to CCV
                // Calculate k value
                // Start T2 timer
                // Start DC timer
                // Start T4 timer based on k value
                // Modulation ON
                pwr_ctrl->state = CCV;
                cv_charging_house_keeping();
            }
        } else {
            // Change state
            pwr_ctrl->state = SUP;
            end_charging_house_keeping();

        }
        break;
    case LCI :
        // Poll ABB for charger unplug - Linear - unregulated chargers ONLY
        status = ind->data[State];
        pwr_ctrl->chg_unplug_vec[index] = (status & CHGPRES);
        if (status & CHGPRES) {
            // Charger is still plugged - continue
            ttw(ttr(TTrEventLow,"Polled - chg plugged (%d)" NL, status));
        } else {
            if (check_chg_unplug() == 1) {
                // Charger is not plugged anymore - stop!!
                ttw(ttr(TTrEventLow,"Verdict - chg not plugged (%d)" NL, status));
                // Change state
                pwr_ctrl->state = SUP;
                charger_unplug_house_keeping(); 
                pwr_free(request);
                return;
            } else {
                // False alarm - don't set flags
                ttw(ttr(TTrEventLow,"Polled - chg not plugged - FALSE alarm? (%d)" NL, status));
#if (USE_Q401_CHG_CIRCUIT == 1)
                if ((ind->data[Ichg] == 0) && (pwr_cfg->data.Vchg > 0)) {
                    // Charging current has disappeared due to a fast unplug??? Bug in IOTA ABB?
                    pwr_ctrl->state = SUP;
                    charger_unplug_house_keeping(); 
                    pwr_free(request);
                    return;
                }
#endif
#if (USE_Q402_CHG_CIRCUIT == 1)
                // FIXME: Really no false alarm code for Q402??
#endif

            }
        }
        pwr_check_timers();
        // We are in the middle of a CI charging process - check stop criterias
        if ( pwr_chg_stop(pwr_temp_lookup(pwr_cfg->data.Tbat_avg, 0x01), pwr_cfg->data.Vbat_avg_mV) == FALSE) {
            // Continue to charge - change state?
            if ((pwr_cfg->bat.type == LITHIUM) && (pwr_chg_ci_cv_transition(pwr_cfg->data.Vbat_avg_mV)== TRUE)) {
                // Change state to LCV
                // Calculate k value
                // Start T2 timer
                // Start DC timer
                // Start T4 timer based on k value
                // Modulation ON
                pwr_ctrl->state = LCV;
                cv_charging_house_keeping();
            }
        } else {

            // Change state
            pwr_ctrl->state = SUP;
            end_charging_house_keeping();

        }
        break;
    case CCV :
        pwr_check_timers();
        // We are in the middle of a CV charging process - check stop criterias
        if (pwr_chg_stop(pwr_temp_lookup(pwr_cfg->data.Tbat_avg, 0x01), pwr_cfg->data.Vbat_avg_mV) == FALSE) {
           // EMPTY - waiting for T4 and DC timeouts OR T2 timeout
        } else {
            // Change state
            pwr_ctrl->state = SUP;
            end_charging_house_keeping();
        }
        break;
    case LCV :
        // Poll ABB for charger unplug - Linear - unregulated chargers ONLY
        status = ind->data[State];
        pwr_ctrl->chg_unplug_vec[index] = (status & CHGPRES);
        if (status & CHGPRES) {
            // Charger is still plugged - continue
            ttw(ttr(TTrEventLow,"Polled - chg plugged (%d)" NL, status));
        } else {
#if (USE_Q401_CHG_CIRCUIT == 1)
            // Charger is not plugged anymore - stop!!
            ttw(ttr(TTrEventLow,"Verdict - chg not plugged (%d)" NL, status));
            // Change state
            pwr_ctrl->state = SUP;
            charger_unplug_house_keeping(); 
            pwr_free(request);
            return;
#endif
#if (USE_Q402_CHG_CIRCUIT == 1)
            if (check_chg_unplug() == 1) {
                // Charger is not plugged anymore - stop!!
                ttw(ttr(TTrEventLow,"Verdict - chg not plugged (%d)" NL, status));
                // Change state
                pwr_ctrl->state = SUP;
                charger_unplug_house_keeping(); 
                pwr_free(request);
                return;
            }
#endif
        }
        pwr_check_timers();
        // We are in the middle of a CV charging process - check stop criterias
        if (pwr_chg_stop(pwr_temp_lookup(pwr_cfg->data.Tbat_avg, 0x01), pwr_cfg->data.Vbat_avg_mV) == FALSE) {
           // EMPTY - waiting for T4 and DC timeouts OR T2 timeout
        } else {
            // Change state
            pwr_ctrl->state = SUP;
            end_charging_house_keeping();
        }
        break;
    default :
        {
        // Exception Handling - Unknown State
        ttr(TTrFatal, "process_spi_adc_indication: Unknown State: %d" NL, pwr_ctrl->state);
        }
    }
    pwr_free(request);
    return RV_OK;
}

T_RV_RET process_abb_chg_unplugged_ind       (T_PWR_REQ *request)
{

    charger_unplug_house_keeping(); 
    // Disable charger interrupts - they where enabled when the CI charger was connected
    ABB_Write_Register_on_page(PAGE0, ITMASK, CHARGER_IT_MSK);

    // A charger can be unplugged in any state
    switch (pwr_ctrl->state) {
    case SUP :
    case CCI :
    case CCV :
        // Change state - T3 is running
        pwr_ctrl->state = SUP;
        break;
    default :
        {
        // Exception Handling - Unknown State
        ttr(TTrFatal, "process_abb_chg_unplugged_ind: Unknown State: %d" NL, pwr_ctrl->state);
        }
    }
    pwr_free(request);
    return RV_OK;
}

void pwr_send_msg(uint32 msg_id, T_RVF_ADDR_ID src_addr_id, T_RVF_ADDR_ID  dest_addr_id) {
    struct pwr_req_s *msg;

    if ((msg = pwr_malloc(sizeof(struct pwr_req_s))) == NULL) {
        return;
    }
    msg->header.msg_id        = msg_id;
    msg->header.src_addr_id   = src_addr_id;
    msg->header.dest_addr_id  = dest_addr_id;
    msg->header.callback_func = NULL;
    if (rvf_send_msg(dest_addr_id, msg) != RV_OK) {
        ttr(TTrFatal, "PWR FATAL: Send failed! %d" NL, 0xFF);
    }
}

void mmi_send_msg(struct mmi_info_ind_s *event) {
    ttw(ttr(TTrInit,"mmi_send_msg(%d)" NL, 0));
    if (pwr_ctrl->rpath.callback_func) {
        ttw(ttr(TTrInit,"Using callback (0x%x)" NL, pwr_ctrl->rpath.callback_func));
        (pwr_ctrl->rpath.callback_func) (event);
    } else {
        if (pwr_ctrl->rpath.addr_id) {
            // TESTME
            rvf_send_msg(pwr_ctrl->rpath.addr_id, event);
        } else {
            ttr(TTrFatal,"PWR FATAL: mmi_send_msg(%d) No return path" NL, 0xFF);
            return;
        }
    }
    ttw(ttr(TTrInit,"mmi_send_msg(%d)" NL, 0xFF));
}

void start_pre_charge(void) {
    ttw(ttr(TTrInitLow,"start_pre_charge" NL, 0x00));
#if (USE_Q401_CHG_CIRCUIT == 1)
    /* Program the DAC with the constant current value */
    // Value is hardcoded!! No configuration exist for state PRE
    ABB_Write_Register_on_page(PAGE0, CHGREG, ICHG_PRECHG); 

    /* Enable the charger  */
    ABB_Write_Register_on_page(PAGE0, BCICTL2, 0x0003);
#endif
#if (USE_Q402_CHG_CIRCUIT == 1)
    pwr_modulate_init();
#endif
    ttw(ttr(TTrInitLow,"start_pre_charge" NL, 0xFF));

}

void start_q401_charge(void) {

    ttw(ttr(TTrInitLow,"start_q401_charge" NL, 0x00));
    if (pwr_ctrl->flag_mmi_registered == 1) {
        pwr_ctrl->mmi_ptr->header.msg_id = MMI_CHG_START_IND;
        mmi_send_msg(pwr_ctrl->mmi_ptr);
    }

    /* Program the DAC with the constant current value taken from /pwr/chg/chg<N>.cfg */
    ABB_Write_Register_on_page(PAGE0, CHGREG, pwr_cfg->chg.ichg_max);

    /* Enable the CI charger  */
    ABB_Write_Register_on_page(PAGE0, BCICTL2, 0x0003);

    /* If not already started - start the overall charger timer T1 */
    if (pwr_ctrl->time_begin_T1 == 0) {
        pwr_start_timer(&pwr_ctrl->time_begin_T1);
        ttw(ttr(TTrTimerLow,"T1 started(%d)" NL, 0));
    }
    ttw(ttr(TTrInitLow,"start_q401_charge" NL, 0xFF));

}

void start_q402_charge(void) {

    ttw(ttr(TTrInitLow,"start_q402_charge(%d)" NL, 0x00));
    if (pwr_ctrl->flag_mmi_registered == 1) {
        pwr_ctrl->mmi_ptr->header.msg_id = MMI_CHG_START_IND;
        mmi_send_msg(pwr_ctrl->mmi_ptr);
    }

    // Needed if we want Ichg measurements - else they are disabled!!
    ABB_Write_Register_on_page(PAGE0, CHGREG, pwr_cfg->chg.ichg_max);
    ABB_Write_Register_on_page(PAGE0, BCICTL2, 0x0003);

    pwr_modulate_init();

    /* If not already started - start the overall charger timer T1 */
    if (pwr_ctrl->time_begin_T1 == 0) {
        pwr_start_timer(&pwr_ctrl->time_begin_T1);
        ttw(ttr(TTrTimerLow,"T1 started(%d)" NL, 0));
    }
    ttw(ttr(TTrInitLow,"start_q402_charge(%d)" NL, 0xFF));

}

void stop_q401_charge(void) {

    ttw(ttr(TTrInitLow,"stop_q401_charge(%d)" NL, 0x00));
    if (pwr_ctrl->flag_mmi_registered == 1) {
        pwr_ctrl->mmi_ptr->header.msg_id = MMI_CHG_STOP_IND;
        mmi_send_msg(pwr_ctrl->mmi_ptr);
    }
    ABB_Write_Register_on_page(PAGE0, BCICTL2, 0);
    ttw(ttr(TTrInitLow,"stop_q401_charge(%d)" NL, 0xFF));

}

void stop_q402_charge(void) {

    ttw(ttr(TTrInitLow,"stop_q402_charge(%d)" NL, 0x00));
    if (pwr_ctrl->flag_mmi_registered == 1) {
        pwr_ctrl->mmi_ptr->header.msg_id = MMI_CHG_STOP_IND;
        mmi_send_msg(pwr_ctrl->mmi_ptr);
    }
    ABB_Write_Register_on_page(PAGE0, BCICTL2, 0);

    pwr_modulate_off();
    ttw(ttr(TTrInitLow,"stop_q402_charge(%d)" NL, 0xFF));

}

void stop_pre_charge(void) {

    ABB_Write_Register_on_page(PAGE0, BCICTL2, 0);

}

int check_chg_unplug() {
    int i;

#if (USE_Q401_CHG_CIRCUIT == 1)
    // Check that the latest values of charger status confirm that we have a charger unplug
    // Search through the vector - charger is only assumed unplugged when ALL CONSECUTIVE_CHG_UNPLUGS elements are 0
    // Returns 1 (TRUE) if the charger is assumed unplugged
    // Returns 0 (FALSE) if the charger is assumed plugged
    // Assume that the charger unplug was true if the battery voltage is sufficiently high
    if (pwr_cfg->data.Vbat_avg_mV < VBAT_PRECHG_STOP) {
        for (i=0;i<= CONSECUTIVE_CHG_UNPLUGS-1 ;i++) {
            ttw(ttr(TTrTimerLow,"chg_unplug_vec(%d)" NL, pwr_ctrl->chg_unplug_vec[i]));
            if (pwr_ctrl->chg_unplug_vec[i] != 0)
                return 0;
        }
    }
    return 1;
#endif
#if (USE_Q402_CHG_CIRCUIT == 1)
    // Assume that the charger unplug was true if BOTH the charger voltage & charger current 
    // are below configurable Vchg & Ichg threshold values
    if ((pwr_cfg->data.Vchg < VCHG_Q402_THR) && (pwr_cfg->data.Ichg < ICHG_Q402_THR)) {
        ttw(ttr(TTrInitLow,"check_chg_unplug: Vchg=(%d)" NL, pwr_cfg->data.Vchg));
        ttw(ttr(TTrInitLow,"check_chg_unplug: Ichg=(%d)" NL, pwr_cfg->data.Ichg));
        return 1;
    }
    return 0;
#endif
}

void charger_unplug_house_keeping(void) {
    // Flag the charger unplug
    pwr_ctrl->flag_chg_plugged = 0;
    pwr_ctrl->flag_chg_prev_plugged = 0;

    // Read charger configuration next time charger is inserted
    pwr_ctrl->flag_chg_cfg_read = 0;

    // We must have more charger id measurements if the charger is re-connected
    pwr_ctrl->count_chg_type = 0;

    if (pwr_ctrl->flag_mmi_registered == 1) {
        // Send charger unplug to the MMI
        pwr_ctrl->mmi_ptr->header.msg_id = MMI_CHG_UNPLUG_IND;
        mmi_send_msg(pwr_ctrl->mmi_ptr);

        // Re-start the mmi info repetition timer
        pwr_start_timer(&pwr_ctrl->time_begin_mmi_rep);
    }

    // if T1 or T2 is running (which mean we have NOT had a normal charging stop)
    if ((pwr_ctrl->time_begin_T1 != 0) || (pwr_ctrl->time_begin_T2 != 0)) {
        // Start T3 timer if not already started
        if (pwr_ctrl->time_begin_T3 == 0) {
            pwr_start_timer(&pwr_ctrl->time_begin_T3);
            ttw(ttr(TTrTimerLow,"T3 started(%d)" NL, 0));
        }
        // Switch of charging circuit

#if (USE_Q401_CHG_CIRCUIT == 1)
        stop_q401_charge();
#endif
#if (USE_Q402_CHG_CIRCUIT == 1)
        stop_q402_charge();
#endif
    }
}

void cv_charging_house_keeping(void) {
    // Calculate k value
    // Start DC timer
    // Start T4 timer based on k value
    // Modulation ON
    ttw(ttr(TTrTimerLow,"Vbat_avg, chg_start_thr, chg_stop_thr (%d), (%d), (%d)" NL, pwr_cfg->data.Vbat_avg_mV, pwr_cfg->bat.chg_start_thr, pwr_cfg->bat.chg_stop_thr));
    if ((pwr_cfg->data.Vbat_avg_mV - pwr_cfg->bat.chg_start_thr) <= 0) {
        // Assign k to max value if Vbat_avg value is below start value
        pwr_cfg->data.k = PWR_MAX_K;
    } else {
        pwr_cfg->data.k = (255 * (pwr_cfg->bat.chg_stop_thr - pwr_cfg->data.Vbat_avg_mV) / (pwr_cfg->bat.chg_stop_thr - pwr_cfg->bat.chg_start_thr));
        if (pwr_cfg->data.k <= PWR_MIN_K)
           pwr_cfg->data.k = PWR_MIN_K;
    }
    pwr_start_timer(&pwr_ctrl->time_begin_T2);
    ttw(ttr(TTrTimerLow,"T2 started(%d)" NL, 0));

    ttw(ttr(TTrTimerLow, "k=%d" NL, pwr_cfg->data.k));
    pwr_cfg->data.T4 = pwr_cfg->data.k * pwr_cfg->common.mod_cycle/255;
    ttw(ttr(TTrTimerLow,"T4 timer (%d)" NL, pwr_cfg->data.T4));

    ttw(ttr(TTrTimerLow,"DC started(%d)" NL, 0));
    pwr_start_timer(&pwr_ctrl->time_begin_mod_cycle);

    ttw(ttr(TTrTimerLow,"T4 started(%d)" NL, 0));
    pwr_start_timer(&pwr_ctrl->time_begin_T4);

    pwr_modulate_on();
}

void end_charging_house_keeping(void) {
    // Stop & reset timers
    pwr_stop_timers();

    // Stop charging
#if (USE_Q401_CHG_CIRCUIT == 1)
    stop_q401_charge();
#endif
#if (USE_Q402_CHG_CIRCUIT == 1)
    stop_q402_charge();
#endif

    // Start the mmi info repetition timer
    if (pwr_ctrl->flag_mmi_registered == 1)
        pwr_start_timer(&pwr_ctrl->time_begin_mmi_rep);

    // Read charger configuration next time charger is inserted
    pwr_ctrl->flag_chg_cfg_read = 0;
}

#if (TEST_PWR_MMI_INTERFACE == 1)
// Callback function used for testing MMI reporting
static void mmi_test_cb_function(void *ptr) {
T_PWR_MMI_INFO_IND_EVENT *event;


    event = (T_PWR_MMI_INFO_IND_EVENT *)ptr;

    ttw(ttr(TTrInit, "mmi_test_cb_function (%d)" NL, 0));
    ttw(ttr(TTrInit, "MMI event: (%d)" NL, event->header.msg_id));
    ttw(ttr(TTrInit, "mmi_test_cb_function (%d)" NL, 0xFF));
}
#endif