//-----------------------------------------------------------------------------
//
//  Copyright (C) 2004, Motorola Inc. All Rights Reserved
//
//------------------------------------------------------------------------------
//
// Copyright (C) 2004-2007, Freescale Semiconductor, Inc. All Rights Reserved.
// THIS SOURCE CODE, AND ITS USE AND DISTRIBUTION, IS SUBJECT TO THE TERMS
// AND CONDITIONS OF THE APPLICABLE LICENSE AGREEMENT
//
//-----------------------------------------------------------------------------
//
//  File:  bspbattdrvr.cpp
//
//  Provides the implementation for BSP-specific battdrvr support.
//
//-----------------------------------------------------------------------------
#pragma warning(push)
#pragma warning(disable: 4115 4201 4204 4214)
#include <windows.h>
#pragma warning(pop)
#include <pmpolicy.h>
#include <battimpl.h>

#include "csp.h"

#include "pmic_lla.h"
#include "pmic_battery.h"
#include "pmic_adc.h"
#include "regs_adc.h"
#include "bspbattdrvr.h"
#include "ioctl_batt.h"


//-----------------------------------------------------------------------------
// External Functions

//-----------------------------------------------------------------------------
// External Variables


//-----------------------------------------------------------------------------
// Defines

#define BSP_BATTDRVR_DEBUG     FALSE
#define MAX_SAFE_VOLTAGE_LEVEL  4   //4.25V
#define MAX_SAFE_CURRENT_LEVEL  14 //1800 mA Max
#define MIN_VOLTAGE_LEVEL       6  //3.8V
#define SLOW_POLL_TIMEOUT       (5*60*1000) //


//-----------------------------------------------------------------------------
// Types


//-----------------------------------------------------------------------------
// Global Variables


//-----------------------------------------------------------------------------
// Local Variables
static LPTSTR gChargeDetectEventName = TEXT("Charge_Detect");
static HANDLE hIntrEventChargeDetect;
static HANDLE hChargeDetectThread;
  
static DWORD dwPollTimeout = 1000;
//-----------------------------------------------------------------------------
// Local Functions
BOOL BSPBattdrvrIsChargerDetect(void)
{
    UINT32 isense0;
    PmicRegisterRead(/*MC13783_INT_SEN0_ADDR*/ 0x2, &isense0);
            //charger OFF sense bit = 0
            //charger ON  sense bit = 1
    return isense0 & (1U <<PMIC_MC13783_INT_CHGDETI);
}

//-----------------------------------------------------------------------------
//
// Function: BSPBattdrvrGetInfo
//
// This function returns the hardware specific battery information
//
// Parameters:
//      pBattInfo
//          [out] pointer to the structure containing the battery information
//
// Returns:
//      TRUE
//
//-----------------------------------------------------------------------------
BOOL BSPBattdrvrGetInfo(PBATT_INFO pBattInfo)
{
    pBattInfo->adc_level = ADC_SAMPLE_MAX_VALUE;
    pBattInfo->adc_batt_max_V = ADC_BATT_MAX_VOLTAGE;
    pBattInfo->adc_batt_min_V = ADC_BATT_MIN_VOLTAGE;
    pBattInfo->adc_batt_max_I = ADC_BATT_MAX_CURRENT;
    pBattInfo->adc_batt_min_I = ADC_BATT_MIN_CURRENT;
    pBattInfo->adc_charger_max_V = ADC_CHARGER_MAX_VOLTAGE;
    pBattInfo->adc_charger_min_V = ADC_CHARGER_MIN_VOLTAGE;
    pBattInfo->adc_charger_max_I = ADC_CHARGER_MAX_CURRENT;
    pBattInfo->adc_charger_min_I = ADC_CHARGER_MIN_CURRENT;
    pBattInfo->charger_V_limit = MC13783_BATTERY_CHRGDET_VOLT_THRESHOLD_HIGH;

    return TRUE;
}



//-----------------------------------------------------------------------------
//
// Function: BSPBattdrvrGetChargingMode
//
// This function returns the whether MC13783 is using the Dual charging mode
//
// Parameters:
//      ChargeMode
//          [out] pointer to the charge mode, TRUE for dual path charge mode, 
//              FALSE for else
//
// Returns:
//      TRUE if success FALSE if else
//
//-----------------------------------------------------------------------------
BOOL BSPBattdrvrGetChargingMode(BOOL *ChargeMode)
{
    PMIC_STATUS status;
    CHARGER_MODE mode;
    
    //later need to call pmic batt interface api to get the mode
    status = PmicBatteryGetChargerMode(&mode);
    if (status != PMIC_SUCCESS)
    {
        ERRORMSG(TRUE, (TEXT("PmicBatteryGetChargerMode failed\r\n")));
        return FALSE;
    }

    if (mode == DUAL_PATH || mode == DUAL_INPUT_DUAL_PATH)
        *ChargeMode = TRUE;
    else
        *ChargeMode = FALSE;
    
    return TRUE;
}


//-----------------------------------------------------------------------------
//
// Function: BSPBattdrvrGetSample
//
// This function returns the batt voltage sample
//
// Parameters:
//      psample
//          [out] pointer to the sample value, 
//                  psample[0] = battery voltage;
//                  psample[1] = battery current;
//                  psample[2] = charger voltage;
//                  psample[3] = charger current; 
//
// Returns:
//      TRUE if success FALSE if else
//
//-----------------------------------------------------------------------------
BOOL BSPBattdrvrGetSample(UINT16 *psample)
{
    BOOL ret = TRUE;
    PMIC_STATUS status;
    UINT16  adc_mul_samples[8];         // must reserve 8
    UINT16  adc_i_sample[8];            // reserve eight ...depending on
                                        // the mode you want to use

    DEBUGMSG(BSP_BATTDRVR_DEBUG, (TEXT("+BSPBattdrvrGetSample\r\n")));

    //call PmicADCGetMultipleChannelsSamples to get the battery voltage
    // and charger voltage & current samples
    status = PmicADCGetMultipleChannelsSamples(
        (1<<MC13783_ADC1_ADA_SEL0_BATT)|
        (1<<MC13783_ADC1_ADA_SEL0_CHRGRAW)|
        (1<<MC13783_ADC1_ADA_SEL0_CHRGISNS), adc_mul_samples);
    if (status != PMIC_SUCCESS)
    {
        ERRORMSG(TRUE, (TEXT("PmicADCGetBattSample failed\r\n")));
        ret = FALSE;
    }
  
    //call PmicADCGetHandsetCurrent to get the battery current sample
    if (ret == TRUE)
    {
        status = PmicADCGetHandsetCurrent(ADC_8CHAN_1X, &adc_i_sample[1]);
        if (status != PMIC_SUCCESS)
        {
            ERRORMSG(TRUE, (TEXT("PmicADCGetBattSample failed\r\n")));
            ret = FALSE;
        }
    }

    psample[BattVoltage] = adc_mul_samples[0];
    psample[BattCurrent] = adc_i_sample[1];
    psample[ChargerVoltage] = adc_mul_samples[1];
    psample[ChargerCurrent] = adc_mul_samples[2];

    DEBUGMSG(BSP_BATTDRVR_DEBUG, (TEXT("samples %d %d %d %d\r\n"), 
        psample[BattVoltage], psample[BattCurrent], psample[ChargerVoltage],
        psample[ChargerCurrent]));

    DEBUGMSG(BSP_BATTDRVR_DEBUG, (TEXT("-BSPBattdrvrGetSample\r\n")));

    return ret;
}


//-----------------------------------------------------------------------------
//
// Function: BSPBattdrvrGetParameters
//
// This function returns the battery and charger voltages, the current as well 
// as a flag that indicates if the curren tis charging or discharging
// 
// Parameters:
//      pBatt_V
//          [out] pointer of the battery voltage
//      pCharger_V
//          [out] pointer of the charger voltage
//      fCharge
//          [out] pointer of the flag TRUE for charging FALSE for discharging
//      I
//          [out] pointer of the charging/discharging current
//
// Returns:
//      TRUE if successful, FALSE for else
//
//-----------------------------------------------------------------------------
BOOL BSPBattdrvrGetParameters(DWORD *pBatt_V,
                              DWORD *pCharger_V,
                              BOOL *fCharge,
                              DWORD *I)
{
    BOOL ret = TRUE;
    BOOL    fDualCharge = FALSE;
    UINT16  samples[TotalChannels];
    UINT16  batt_i_sample;
    UINT16  charger_i_sample;
    LONG batt_I;
    LONG charger_I;
    BATT_INFO    BattInfo;      //batt info structure obtained from bsp layer

    DEBUGMSG(BSP_BATTDRVR_DEBUG, (TEXT("+BSPBattdrvrGetParameters\r\n")));
    
    //Get Info from BSP layer
    if(BSPBattdrvrGetInfo(&BattInfo) != TRUE)
    { 
        ERRORMSG(TRUE, (TEXT("BSPBattdrvrGetInfo fail !!\r\n")));
        ret = FALSE;
    }   
    
    if (ret == TRUE)
    {
        //check whether the dual charging mode is used
        if (BSPBattdrvrGetChargingMode(&fDualCharge) != TRUE)
        { 
            ERRORMSG(TRUE, (TEXT("Get battery charge mode fail !!\r\n")));
            ret = FALSE; 
        }
    }

    if (ret == TRUE)
    {
        //check whether charger is available -- using the threshold voltage 
        //from pmic spec
        if(BSPBattdrvrGetSample(samples) != TRUE)
        { 
            ERRORMSG(TRUE, (TEXT("Get battery sample fail !!\r\n")));
            ret = FALSE; 
        }
    }


    // determine the battery voltage according to the corresponding ADC sample
    *pBatt_V = 
        BattInfo.adc_batt_min_V +
        (DWORD)((BattInfo.adc_batt_max_V-BattInfo.adc_batt_min_V) *
        samples[BattVoltage]/BattInfo.adc_level);  
    DEBUGMSG(BSP_BATTDRVR_DEBUG, (TEXT("batt_V = %d sample = %d\r\n"),
             *pBatt_V, samples[BattVoltage]));  


    // determine the battery current according to the corresponding ADC sample
    batt_i_sample = samples[BattCurrent];
    if (batt_i_sample & 0x200)
    {
        //perform the 2's complementary conversion
        batt_i_sample = batt_i_sample -1;
        batt_i_sample = ~batt_i_sample;
        batt_i_sample &= 0x1FF;
        batt_I = 
            (-1)*(LONG)(BattInfo.adc_batt_min_I +
            (DWORD)((BattInfo.adc_batt_max_I-BattInfo.adc_batt_min_I) *
            batt_i_sample/BattInfo.adc_level));
    }
    else
    {
        batt_I = 
            (LONG)(BattInfo.adc_batt_min_I +
            (DWORD)((BattInfo.adc_batt_max_I-BattInfo.adc_batt_min_I) *
            batt_i_sample/BattInfo.adc_level));
    }
    DEBUGMSG(BSP_BATTDRVR_DEBUG, (TEXT("batt_I = %d sample = %d\r\n"),
             batt_I, batt_i_sample));   

    
    // determine the charger voltage according to the corresponding ADC sample
    *pCharger_V = 
        BattInfo.adc_charger_min_V +
        (DWORD)((BattInfo.adc_charger_max_V-BattInfo.adc_charger_min_V) *
        samples[ChargerVoltage]/BattInfo.adc_level);    
    DEBUGMSG(BSP_BATTDRVR_DEBUG, (TEXT("charger_V = %d sample = %d\r\n"),
            *pCharger_V, samples[ChargerVoltage]));   

    // determine the charger current according to the corresponding ADC sample
    charger_i_sample = samples[ChargerCurrent];
    if (charger_i_sample & 0x200)
    {
        //perform the 2's complementary conversion
        charger_i_sample = charger_i_sample -1;
        charger_i_sample = ~charger_i_sample;
        charger_i_sample &= 0x1FF;
        charger_I = 
            (-1)*(LONG)(BattInfo.adc_charger_min_I +
            (LONG)((BattInfo.adc_charger_max_I-BattInfo.adc_charger_min_I) *
            charger_i_sample/BattInfo.adc_level));
    }
    else
    {
        charger_I = 
            (LONG)(BattInfo.adc_charger_min_I +
            (LONG)((BattInfo.adc_charger_max_I-BattInfo.adc_charger_min_I) *
            charger_i_sample/BattInfo.adc_level));
    }
    DEBUGMSG(BSP_BATTDRVR_DEBUG, (TEXT("charger_I = %d sample = %d\r\n"), 
             charger_I, charger_i_sample));    


    //according to the charging mode, determine the current to used in the
    //capacity calculating
    if (fDualCharge)
    {
        DEBUGMSG(BSP_BATTDRVR_DEBUG, (TEXT("Dual Charging Mode!\r\n")));  
        if (charger_I <= 0)
        {
            *fCharge = FALSE;
            //discharging current equals to the current flowing out of battery
            // in dual charge mode
            *I = abs(batt_I);
            DEBUGMSG(BSP_BATTDRVR_DEBUG,(TEXT("discharging_I = %d \r\n"), *I));
        }
        else
        {
            *fCharge = TRUE;
            //charging current equals to the difference between the charger 
            //current and the battery current in dual charge mode
            *I = abs(charger_I);
            DEBUGMSG(BSP_BATTDRVR_DEBUG, (TEXT("charging_I = %d \r\n"), *I)); 
        } 
    }
    else
    {
        DEBUGMSG(BSP_BATTDRVR_DEBUG, (TEXT("Other Charging Modes!\r\n")));  
        if (batt_I > 0)
        {
            *fCharge = FALSE;
            //discharging current equals to the current flowing out of battery 
            //in other charge modes
            *I = abs(batt_I);
            DEBUGMSG(BSP_BATTDRVR_DEBUG,(TEXT("discharging_I = %d \r\n"), *I));  
        }
        else
        {
            *fCharge = TRUE;
            //charging current equals to the current flowing into battery in
            //other charge modes
            *I = abs(batt_I);
            DEBUGMSG(BSP_BATTDRVR_DEBUG, (TEXT("charging_I = %d \r\n"), *I)); 
        }
    }
    if ((*pBatt_V <= BattInfo.adc_batt_min_V) && (*fCharge))
        *fCharge = FALSE;
    if (*pCharger_V < BattInfo.charger_V_limit)
        *fCharge = FALSE;
    DEBUGMSG(BSP_BATTDRVR_DEBUG, (TEXT("-BSPBattdrvrGetParameters\r\n")));
    return ret;
}
//-----------------------------------------------------------------------------
//
// Function: BSPBattdrvrSetCharger
//
// This function set the charger voltage and current limit
// 
// Parameters:
//      voltage
//          [in] charging voltage limit
//            level 0 = 4.05V
//            1 = 4.375V
//            2 = 4.15V
//            3 = 4.20V
//            4 = 4.25V
//            5 = 4.30V
//            6 = 3.80V ... lowest setting
//            7 = 4.50V     
//      current
//          [in] charging current limit
//            level 0 = 0 mA    (max value)
//            1 = 100 mA  (max value)
//              ...       (in increment of 100 mA)
//           13 = 1300 mA (max value)
//           14 = 1800 mA (max value)
//           15 = disables the current limit
//          
//
// Returns:
//      TRUE if successful, FALSE for else
//
//-----------------------------------------------------------------------------
BOOL BSPBattdrvrSetCharger(UINT8 voltage,
                              UINT8 current)
                    
{
    PMIC_STATUS status;
    BOOL bSuspendEnable = FALSE;
     
    if (current > MAX_SAFE_CURRENT_LEVEL)  
    {
        ERRORMSG(TRUE, (TEXT("BSPBattdrvrSetCharger fail, the current set = %d is so large\r\n"), current));
        return FALSE;
    }

    if ((voltage > MAX_SAFE_VOLTAGE_LEVEL) && (voltage != MIN_VOLTAGE_LEVEL))  
    {
        ERRORMSG(TRUE, (TEXT("BSPBattdrvrSetCharger fail, the voltage set = %d is so large\r\n"), voltage));
        return FALSE;
    }
    
    status = PmicBatterSetCharger(BATT_MAIN_CHGR, voltage, current);  
    if (status != PMIC_SUCCESS)
    {
        ERRORMSG(TRUE, (TEXT("PmicBatterSetCharger failed, voltage = %d, current = %d\r\n"), voltage, current));
        return FALSE;
    }

    KernelIoControl(IOCTL_HAL_ENABLE_SUSPEND, &bSuspendEnable, 
        sizeof(bSuspendEnable), NULL, 0, NULL);

    PmicBatterLedControl(TRUE);
    
    return TRUE;
   
}

BOOL BSPBattdrvrLedControl(BOOL state)
{
    PmicBatterLedControl(state);
    return TRUE;
}
BOOL BSPBattdrvrSetTrickleCurrent(UINT8 current)
{
    if (PmicBatterySetTrickleCurrent(current) != PMIC_SUCCESS)
    {
        ERRORMSG(TRUE, (TEXT("PmicBatterySetTrickleCurrent failed, current = %d\r\n"), current));
        return FALSE;
    }
    else
        return TRUE;
}


BOOL BSPBattdrvrDisableCharger(void)
{
    PMIC_STATUS status;
    BOOL bSuspendEnable = TRUE;

    status = PmicBatterDisableCharger(BATT_MAIN_CHGR);
    if (status != PMIC_SUCCESS)
    {
        ERRORMSG(TRUE, (TEXT("PmicBatterDisableCharger failed \r\n")));
        return FALSE;
    }

    PmicBatterLedControl(FALSE);
    
    KernelIoControl(IOCTL_HAL_ENABLE_SUSPEND, &bSuspendEnable, 
        sizeof(bSuspendEnable), NULL, 0, NULL);
    return TRUE;
}


static BOOL
BSPBattdrvrChargeDetectThreadProc(LPVOID lpParam)
{
	UNREFERENCED_PARAMETER(lpParam);
    //SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_HIGHEST);
    for(;;)
    {
        if (WaitForSingleObject(hIntrEventChargeDetect, INFINITE) == WAIT_OBJECT_0)
        {
            if (BSPBattdrvrIsChargerDetect()) //1 ON, 0 OFF.
            {
                LOCKBATTERY();
                gBatteryContext.dwPollTimeout = dwPollTimeout;
                BatteryAPIGetSystemPowerStatusEx2(&gBatteryContext.st, sizeof(gBatteryContext.st), TRUE);
                UNLOCKBATTERY();
                PowerPolicyNotify(PPN_POWERCHANGE, 0);
            }
            else
            {
                LOCKBATTERY();
                gBatteryContext.dwPollTimeout = SLOW_POLL_TIMEOUT; 
                BatteryAPIGetSystemPowerStatusEx2(&gBatteryContext.st, sizeof(gBatteryContext.st), TRUE);
                UNLOCKBATTERY();
                PowerPolicyNotify(PPN_POWERCHANGE, 0);
            } 

            // Make sure charger detect interrupt is unmasked
            if (PmicInterruptHandlingComplete(PMIC_MC13783_INT_CHGDETI) != PMIC_SUCCESS)
            {
				ERRORMSG(TRUE, (TEXT("%s() : PmicInterruptHandlingComplete failed\r\n"), __WFUNCTION__));
            }
        }
    }
}

BOOL BSPBattAdjustPollTimeout(DWORD level)
{
    if (level == 0) //restore
        gBatteryContext.dwPollTimeout = dwPollTimeout;
    else
        gBatteryContext.dwPollTimeout = SLOW_POLL_TIMEOUT;
    return TRUE;
}

BOOL BSPBattAttach(void)
{
    BOOL rc = FALSE;
    PMIC_STATUS status;

    if (PmicInterruptRegister(PMIC_MC13783_INT_CHGDETI, gChargeDetectEventName) != PMIC_SUCCESS)
    {
        ERRORMSG(TRUE, (TEXT("%s() : PmicInterruptRegister failed\r\n"), __WFUNCTION__));
        goto cleanUp;
    }
    if (PmicInterruptEnable(PMIC_MC13783_INT_CHGDETI) != PMIC_SUCCESS)
    {
        ERRORMSG(TRUE, (TEXT("%s() : PmicInterruptEnable failed\r\n"), __WFUNCTION__));
        goto cleanUp;
    }

    hIntrEventChargeDetect = CreateEvent(NULL, FALSE, FALSE, gChargeDetectEventName);
    hChargeDetectThread = CreateThread(NULL, 0,
                                  (LPTHREAD_START_ROUTINE)BSPBattdrvrChargeDetectThreadProc,
                                  0, 0, NULL);
    if (!hChargeDetectThread)
    {
        ERRORMSG(TRUE ,(TEXT("%s() : CreateThread failed\r\n"), __WFUNCTION__));
        goto cleanUp;    
    }

    // for Priority of BatteryThreadProc is 249
    CeSetThreadPriority(hChargeDetectThread, 248);

    //Enable coin cell charger
    status = PmicBatteryEnableCoincellCharger();
    if (status != PMIC_SUCCESS)
    {
        ERRORMSG(TRUE, (TEXT("%s() : PmicBatteryEnableCoincellCharger failed \r\n"), __WFUNCTION__));
        return rc;
    }

    status = PmicBatterySetCoincellVoltage(0);
    if (status != PMIC_SUCCESS)
    {
        ERRORMSG(TRUE, (TEXT("%s() : PmicBatterySetCoincellVoltage failed \r\n"), __WFUNCTION__));
        return rc;
    }
    rc = TRUE;
    return rc;
cleanUp:
    if (PmicInterruptDeregister(PMIC_MC13783_INT_CHGDETI) != PMIC_SUCCESS)
    {
        ERRORMSG(TRUE, (TEXT("%s() : PmicInterruptDeregister failed\r\n"), __WFUNCTION__));
    }

    return rc;
    
}
