diff options
Diffstat (limited to 'drivers/power/stmp37xx/ddi_bc_sm.c')
-rw-r--r-- | drivers/power/stmp37xx/ddi_bc_sm.c | 1122 |
1 files changed, 1122 insertions, 0 deletions
diff --git a/drivers/power/stmp37xx/ddi_bc_sm.c b/drivers/power/stmp37xx/ddi_bc_sm.c new file mode 100644 index 000000000000..8bd8d2cbb58b --- /dev/null +++ b/drivers/power/stmp37xx/ddi_bc_sm.c @@ -0,0 +1,1122 @@ +/* + * Copyright 2008-2009 Freescale Semiconductor, Inc. All Rights Reserved. + */ + +/* + * The code contained herein is licensed under the GNU General Public + * License. You may obtain a copy of the GNU General Public License + * Version 2 or later at the following locations: + * + * http://www.opensource.org/licenses/gpl-license.html + * http://www.gnu.org/copyleft/gpl.html + */ + +//////////////////////////////////////////////////////////////////////////////// +//! \addtogroup ddi_bc +//! @{ +// +// Copyright (c) 2004-2005 SigmaTel, Inc. +// +//! \file ddi_bc_sm.c +//! \brief Contains the Battery Charger state machine. +//////////////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////////////// +// Includes +//////////////////////////////////////////////////////////////////////////////// + +#include <mach/ddi_bc.h> +#include "ddi_bc_internal.h" + +#include <linux/delay.h> + +//////////////////////////////////////////////////////////////////////////////// +// Definitions +//////////////////////////////////////////////////////////////////////////////// + +// This is the minimum time we must charge before we transition from +// the charging state to the topping off. If we reach the +// u16ChargingThresholdCurrent charge curent before then, the battery was +// already full so we can avoid the risk of charging it past .1C for +// too long. + +#define TRANSITION_TO_TOPOFF_MINIMUM_CHARGE_TIME_mS 1 * 60 * 1000 // 1 minute + +// If DCDCs are active, we can't complete the charging cycle. Once +// they are stay off for this amount of time, we will restart the +// charge cycle. +#define DCDC_INACTIVITY_TIMER_THRESHOLD 1 * 60 * 1000 // 1 minute + +//////////////////////////////////////////////////////////////////////////////// +// Variables +//////////////////////////////////////////////////////////////////////////////// + +// The current state. + +ddi_bc_State_t g_ddi_bc_State = DDI_BC_STATE_UNINITIALIZED; + +// This table contains pointers to the functions that implement states. The +// table is indexed by state. Note that it's critically important for this +// table to agree with the state enumeration in ddi_bc.h. + +static ddi_bc_Status_t ddi_bc_Uninitialized(void); +static ddi_bc_Status_t ddi_bc_Broken(void); +static ddi_bc_Status_t ddi_bc_Disabled(void); +static ddi_bc_Status_t ddi_bc_WaitingToCharge(void); +static ddi_bc_Status_t ddi_bc_Conditioning(void); +static ddi_bc_Status_t ddi_bc_Charging(void); +static ddi_bc_Status_t ddi_bc_ToppingOff(void); +static ddi_bc_Status_t ddi_bc_DcdcModeWaitingToCharge(void); + +ddi_bc_Status_t(*const (stateFunctionTable[])) (void) = { +ddi_bc_Uninitialized, + ddi_bc_Broken, + ddi_bc_Disabled, + ddi_bc_WaitingToCharge, + ddi_bc_Conditioning, + ddi_bc_Charging, ddi_bc_ToppingOff, ddi_bc_DcdcModeWaitingToCharge}; + +// Used by states that need to watch the time. +uint32_t g_ddi_bc_u32StateTimer = 0; + +// If DCDCs are active, we can't complete the charging cycle. Once +// they are stay off for this amount of time, we will restart the +// charge cycle. +static uint32_t DcdcInactityTimer = DCDC_INACTIVITY_TIMER_THRESHOLD; + +// Always attempt to charge on first 5V connection even if DCDCs are ON. +bool bRestartChargeCycle = true; + +#ifdef CONFIG_POWER_SUPPLY_DEBUG +static uint16_t u16ExternalBatteryPowerVoltageCheck = 0; +#endif + +ddi_bc_BrokenReason_t ddi_bc_gBrokenReason = DDI_BC_BROKEN_UNINITIALIZED; + +//////////////////////////////////////////////////////////////////////////////// +// Code +//////////////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////////////// +//! +//! \brief Transition to the DCDC mode Waiting to Charge state. +//! +//! \fntype Function +//! +//! This function implements the transition to the DCDC Mode Waiting +//! to Charge state. +//! +//////////////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////////////// +//! +//! \brief Transition to the DCDC mode Waiting to Charge state. +//! +//! \fntype Function +//! +//! This function implements the transition to the DCDC Mode Waiting +//! to Charge state. +//! +//////////////////////////////////////////////////////////////////////////////// +static void TransitionToDcdcModeWaitingToCharge(void) +{ + + //-------------------------------------------------------------------------- + // Reset the state timer. + //-------------------------------------------------------------------------- + + g_ddi_bc_u32StateTimer = 0; + + //-------------------------------------------------------------------------- + // Reset the current ramp. + //-------------------------------------------------------------------------- + + ddi_bc_RampReset(); + + //-------------------------------------------------------------------------- + // Move to the Waiting to Charge state. + //-------------------------------------------------------------------------- + + g_ddi_bc_State = DDI_BC_STATE_DCDC_MODE_WAITING_TO_CHARGE; + +#ifdef CONFIG_POWER_SUPPLY_DEBUG + printk("Battery charger: now waiting to charge (DCDC Mode)\n"); +#endif + +} + +//////////////////////////////////////////////////////////////////////////////// +//! +//! \brief Transition to the Waiting to Charge state. +//! +//! \fntype Function +//! +//! This function implements the transition to the Waiting to Charge state. +//! +//////////////////////////////////////////////////////////////////////////////// +static void TransitionToWaitingToCharge(void) +{ + + //-------------------------------------------------------------------------- + // Reset the state timer. + //-------------------------------------------------------------------------- + + g_ddi_bc_u32StateTimer = 0; + + //-------------------------------------------------------------------------- + // Reset the current ramp. + //-------------------------------------------------------------------------- + + ddi_bc_RampReset(); + + //-------------------------------------------------------------------------- + // Move to the Waiting to Charge state. + //-------------------------------------------------------------------------- + + g_ddi_bc_State = DDI_BC_STATE_WAITING_TO_CHARGE; + +#ifdef CONFIG_POWER_SUPPLY_DEBUG + printk("Battery charger: now waiting to charge\n"); +#endif + +} + +//////////////////////////////////////////////////////////////////////////////// +//! +//! \brief Transition to the Conditioning state. +//! +//! \fntype Function +//! +//! This function implements the transition to the Conditioning state. +//! +//////////////////////////////////////////////////////////////////////////////// +static void TransitionToConditioning(void) +{ + + //-------------------------------------------------------------------------- + // Reset the state timer. + //-------------------------------------------------------------------------- + + g_ddi_bc_u32StateTimer = 0; + + //-------------------------------------------------------------------------- + // Set up the current ramp for conditioning. + //-------------------------------------------------------------------------- + + ddi_bc_RampSetTarget(g_ddi_bc_Configuration.u16ConditioningCurrent); + + //-------------------------------------------------------------------------- + // Move to the Conditioning state. + //-------------------------------------------------------------------------- + + g_ddi_bc_State = DDI_BC_STATE_CONDITIONING; + +#ifdef CONFIG_POWER_SUPPLY_DEBUG + printk("Battery charger: now conditioning\n"); +#endif + +} + +//////////////////////////////////////////////////////////////////////////////// +//! +//! \brief Transition to the Charging state. +//! +//! \fntype Function +//! +//! This function implements the transition to the Charging state. +//! +//////////////////////////////////////////////////////////////////////////////// +static void TransitionToCharging(void) +{ + + //-------------------------------------------------------------------------- + // Reset the state timer. + //-------------------------------------------------------------------------- + + g_ddi_bc_u32StateTimer = 0; + + //-------------------------------------------------------------------------- + // Set up the current ramp for charging. + //-------------------------------------------------------------------------- + + ddi_bc_RampSetTarget(g_ddi_bc_Configuration.u16ChargingCurrent); + + //-------------------------------------------------------------------------- + // We'll be finished charging when the current flow drops below this level. + //-------------------------------------------------------------------------- + + ddi_bc_hwSetCurrentThreshold(g_ddi_bc_Configuration. + u16ChargingThresholdCurrent); + + //-------------------------------------------------------------------------- + // Move to the Charging state. + //-------------------------------------------------------------------------- + + g_ddi_bc_State = DDI_BC_STATE_CHARGING; +#ifdef CONFIG_POWER_SUPPLY_DEBUG + printk("Battery charger: now charging\n"); +#endif +} + +//////////////////////////////////////////////////////////////////////////////// +//! +//! \brief Transition to the Topping Off state. +//! +//! \fntype Function +//! +//! This function implements the transition to the Topping Off state. +//! +//////////////////////////////////////////////////////////////////////////////// +static void TransitionToToppingOff(void) +{ + + //-------------------------------------------------------------------------- + // Reset the state timer. + //-------------------------------------------------------------------------- + + g_ddi_bc_u32StateTimer = 0; + + //-------------------------------------------------------------------------- + // Set up the current ramp for topping off. + //-------------------------------------------------------------------------- + + ddi_bc_RampSetTarget(g_ddi_bc_Configuration.u16ChargingCurrent); + + //-------------------------------------------------------------------------- + // Move to the Topping Off state. + //-------------------------------------------------------------------------- + + g_ddi_bc_State = DDI_BC_STATE_TOPPING_OFF; + +#ifdef CONFIG_POWER_SUPPLY_DEBUG + printk("Battery charger: now topping off\n"); +#endif + +} + +//////////////////////////////////////////////////////////////////////////////// +//! +//! \brief Transition to the Broken state. +//! +//! \fntype Function +//! +//! This function implements the transition to the Broken state. +//! +//////////////////////////////////////////////////////////////////////////////// +static void TransitionToBroken(void) +{ + + //-------------------------------------------------------------------------- + // Reset the state timer. + //-------------------------------------------------------------------------- + + g_ddi_bc_u32StateTimer = 0; + + //-------------------------------------------------------------------------- + // Reset the current ramp. + //-------------------------------------------------------------------------- + + ddi_bc_RampReset(); + + //-------------------------------------------------------------------------- + // Move to the Broken state. + //-------------------------------------------------------------------------- + + g_ddi_bc_State = DDI_BC_STATE_BROKEN; + +#ifdef CONFIG_POWER_SUPPLY_DEBUG + printk("Battery charger: declaring a broken battery\n"); +#endif + +} + +//////////////////////////////////////////////////////////////////////////////// +//! +//! \brief Uninitialized state function. +//! +//! \fntype Function +//! +//! This function implements the Uninitialized state. +//! +//////////////////////////////////////////////////////////////////////////////// +static ddi_bc_Status_t ddi_bc_Uninitialized(void) +{ + + //-------------------------------------------------------------------------- + // The first order of business is to update alarms. + //-------------------------------------------------------------------------- + + ddi_bc_RampUpdateAlarms(); + + //-------------------------------------------------------------------------- + // Increment the state timer. + //-------------------------------------------------------------------------- + + g_ddi_bc_u32StateTimer += g_ddi_bc_Configuration.u32StateMachinePeriod; + + //-------------------------------------------------------------------------- + // The only way to leave this state is with a call to ddi_bc_Initialize. So, + // calling this state function does nothing. + //-------------------------------------------------------------------------- + + return (DDI_BC_STATUS_SUCCESS); + +} + +//////////////////////////////////////////////////////////////////////////////// +//! +//! \brief Broken state function. +//! +//! \fntype Function +//! +//! This function implements the Broken state. +//! +//////////////////////////////////////////////////////////////////////////////// +static ddi_bc_Status_t ddi_bc_Broken(void) +{ + + //-------------------------------------------------------------------------- + // The first order of business is to update alarms. + //-------------------------------------------------------------------------- + + ddi_bc_RampUpdateAlarms(); + + //-------------------------------------------------------------------------- + // Increment the state timer. + //-------------------------------------------------------------------------- + + g_ddi_bc_u32StateTimer += g_ddi_bc_Configuration.u32StateMachinePeriod; + + //-------------------------------------------------------------------------- + // The only way to leave this state is with a call to ddi_bc_SetFixed. So, + // calling this state function does nothing. + //-------------------------------------------------------------------------- + + return (DDI_BC_STATUS_SUCCESS); + +} + +//////////////////////////////////////////////////////////////////////////////// +//! +//! \brief Disabled state function. +//! +//! \fntype Function +//! +//! This function implements the Disabled state. +//! +//////////////////////////////////////////////////////////////////////////////// +static ddi_bc_Status_t ddi_bc_Disabled(void) +{ + + //-------------------------------------------------------------------------- + // The first order of business is to update alarms. + //-------------------------------------------------------------------------- + + ddi_bc_RampUpdateAlarms(); + + //-------------------------------------------------------------------------- + // Increment the state timer. + //-------------------------------------------------------------------------- + + g_ddi_bc_u32StateTimer += g_ddi_bc_Configuration.u32StateMachinePeriod; + + //-------------------------------------------------------------------------- + // The only way to leave this state is with a call to ddi_bc_SetEnable. So, + // calling this state function does nothing. + //-------------------------------------------------------------------------- + + return (DDI_BC_STATUS_SUCCESS); + +} + +//////////////////////////////////////////////////////////////////////////////// +//! +//! \brief Waitin to Charge state function. +//! +//! \fntype Function +//! +//! This function implements the Waiting to Charge state. +//! +//////////////////////////////////////////////////////////////////////////////// +static ddi_bc_Status_t ddi_bc_DcdcModeWaitingToCharge(void) +{ + + uint16_t u16BatteryVoltage; + + //-------------------------------------------------------------------------- + // The first order of business is to update alarms. + //-------------------------------------------------------------------------- + + ddi_bc_RampUpdateAlarms(); + + //-------------------------------------------------------------------------- + // Increment the state timer. + //-------------------------------------------------------------------------- + + g_ddi_bc_u32StateTimer += g_ddi_bc_Configuration.u32StateMachinePeriod; + + //-------------------------------------------------------------------------- + // Check if the power supply is present. If not, we're not going anywhere. + //-------------------------------------------------------------------------- + + if (!ddi_bc_hwPowerSupplyIsPresent()) { + TransitionToCharging(); + return (DDI_BC_STATUS_SUCCESS); + } + //-------------------------------------------------------------------------- + // If control arrives here, we're connected to a power supply. Have a look + // at the battery voltage. + //-------------------------------------------------------------------------- + + u16BatteryVoltage = ddi_bc_hwGetBatteryVoltage(); + + //-------------------------------------------------------------------------- + // If topping off has not yet occurred, we do not care if DCDCs are on or not. + // If we have already topped off at least once, then we want to delay so that + // we give the battery a chance to discharge some instead of constantly topping + // it off. + //-------------------------------------------------------------------------- + + if (ddi_bc_hwIsDcdcOn()) { + + //-------------------------------------------------------------------------- + // If DCDCs have turned on, restert the DCDCInactivityTimer; + //-------------------------------------------------------------------------- + DcdcInactityTimer = 0; + + //-------------------------------------------------------------------------- + // If the battery voltage measurement is at or below the LowDcdcBatteryVoltage + // level, we should definetly start charging the battery. The + // LowDcdcBatteryVoltage value should be low enough to account for IR voltage + // drop from the battery under heavy DCDC load. + //-------------------------------------------------------------------------- + + if (u16BatteryVoltage < + g_ddi_bc_Configuration.u16LowDcdcBatteryVoltage_mv) { + bRestartChargeCycle = true; + + } else { + return (DDI_BC_STATUS_SUCCESS); + } + } else if (DcdcInactityTimer < DCDC_INACTIVITY_TIMER_THRESHOLD) { + DcdcInactityTimer += + g_ddi_bc_Configuration.u32StateMachinePeriod; + if (DcdcInactityTimer >= DCDC_INACTIVITY_TIMER_THRESHOLD) { + bRestartChargeCycle = true; + } + } + + if (bRestartChargeCycle) { + bRestartChargeCycle = false; + // start charging + if (u16BatteryVoltage < + g_ddi_bc_Configuration.u16ConditioningThresholdVoltage) { + + //---------------------------------------------------------------------- + // If control arrives here, the battery is very low and it needs to be + // conditioned. + //---------------------------------------------------------------------- + + TransitionToConditioning(); + } else { + + //---------------------------------------------------------------------- + // If control arrives here, the battery isn't too terribly low. + //---------------------------------------------------------------------- + + TransitionToCharging(); + } + + } + + return (DDI_BC_STATUS_SUCCESS); +} + +//////////////////////////////////////////////////////////////////////////////// +//! +//! \brief Waitin to Charge state function. +//! +//! \fntype Function +//! +//! This function implements the Waiting to Charge state. +//! +//////////////////////////////////////////////////////////////////////////////// +static ddi_bc_Status_t ddi_bc_WaitingToCharge(void) +{ + uint16_t u16BatteryVoltage; + //-------------------------------------------------------------------------- + // The first order of business is to update alarms. + //-------------------------------------------------------------------------- + + ddi_bc_RampUpdateAlarms(); + + //-------------------------------------------------------------------------- + // Increment the state timer. + //-------------------------------------------------------------------------- + + g_ddi_bc_u32StateTimer += g_ddi_bc_Configuration.u32StateMachinePeriod; + + //-------------------------------------------------------------------------- + // Check if the power supply is present. If not, we're not going anywhere. + //-------------------------------------------------------------------------- + + if (!ddi_bc_hwPowerSupplyIsPresent()) { +#ifdef CONFIG_POWER_SUPPLY_DEBUG + u16ExternalBatteryPowerVoltageCheck = 0; +#endif + return (DDI_BC_STATUS_SUCCESS); + } + //-------------------------------------------------------------------------- + // If control arrives here, we're connected to a power supply. Have a look + // at the battery voltage. + //-------------------------------------------------------------------------- + + u16BatteryVoltage = ddi_bc_hwGetBatteryVoltage(); + +#ifdef CONFIG_POWER_SUPPLY_DEBUG + if (u16ExternalBatteryPowerVoltageCheck) { + if ((u16ExternalBatteryPowerVoltageCheck - u16BatteryVoltage) > + 300) { + //---------------------------------------------------------------------- + // If control arrives here, battery voltage has dropped too quickly after + // the first charge cycle. We think an external voltage regulator is + // connected. If the DCDCs are on and this voltage drops too quickly, + // it will cause large droops and possible brownouts so we disable + // charging. + //---------------------------------------------------------------------- + + ddi_bc_gBrokenReason = + DDI_BC_BROKEN_EXTERNAL_BATTERY_VOLTAGE_DETECTED; + + TransitionToBroken(); + + //---------------------------------------------------------------------- + // Tell our caller the battery appears to be broken. + //---------------------------------------------------------------------- + + return (DDI_BC_STATUS_BROKEN); + } else { + // reset this check + u16ExternalBatteryPowerVoltageCheck = 0; + } + + } +#endif + + //-------------------------------------------------------------------------- + // In addition to 5V, is DCDC on also? If so, swith to DCDC Mode Waiting + // to Charge state. + //-------------------------------------------------------------------------- + + if (ddi_bc_hwIsDcdcOn()) { + TransitionToDcdcModeWaitingToCharge(); + return (DDI_BC_STATUS_SUCCESS); + } + + //-------------------------------------------------------------------------- + // If the battery voltage isn't low, we don't need to be charging it. We + // use a 5% margin to decide. + //-------------------------------------------------------------------------- + + if (!bRestartChargeCycle) { + uint16_t x; + + x = u16BatteryVoltage + (u16BatteryVoltage / 20); + + if (x >= g_ddi_bc_Configuration.u16ChargingVoltage) + return (DDI_BC_STATUS_SUCCESS); + + } + + bRestartChargeCycle = false; + //-------------------------------------------------------------------------- + // If control arrives here, the battery is low. How low? + //-------------------------------------------------------------------------- + + if (u16BatteryVoltage < + g_ddi_bc_Configuration.u16ConditioningThresholdVoltage) { + + //---------------------------------------------------------------------- + // If control arrives here, the battery is very low and it needs to be + // conditioned. + //---------------------------------------------------------------------- + + TransitionToConditioning(); + + } else { + + //---------------------------------------------------------------------- + // If control arrives here, the battery isn't too terribly low. + //---------------------------------------------------------------------- + + TransitionToCharging(); + + } + + //-------------------------------------------------------------------------- + // Return success. + //-------------------------------------------------------------------------- + + return (DDI_BC_STATUS_SUCCESS); + +} + +//////////////////////////////////////////////////////////////////////////////// +//! +//! \brief Conditioning state function. +//! +//! \fntype Function +//! +//! This function implements the Conditioning state. +//! +//////////////////////////////////////////////////////////////////////////////// +static ddi_bc_Status_t ddi_bc_Conditioning(void) +{ + + //-------------------------------------------------------------------------- + // The first order of business is to update alarms. + //-------------------------------------------------------------------------- + + ddi_bc_RampUpdateAlarms(); + + //-------------------------------------------------------------------------- + // If we're not under an alarm, increment the state timer. + //-------------------------------------------------------------------------- + + if (!ddi_bc_RampGetDieTempAlarm() && !ddi_bc_RampGetBatteryTempAlarm()) { + g_ddi_bc_u32StateTimer += + g_ddi_bc_Configuration.u32StateMachinePeriod; + } + //-------------------------------------------------------------------------- + // Check if the power supply is still around. + //-------------------------------------------------------------------------- + + if (!ddi_bc_hwPowerSupplyIsPresent()) { + + //---------------------------------------------------------------------- + // If control arrives here, the power supply has been removed. Go back + // and wait. + //---------------------------------------------------------------------- + + TransitionToWaitingToCharge(); + + //---------------------------------------------------------------------- + // Return success. + //---------------------------------------------------------------------- + + return (DDI_BC_STATUS_SUCCESS); + + } + + //-------------------------------------------------------------------------- + // If control arrives here, we're still connected to a power supply. + // Check if a battery is connected. If the voltage rises to high with only + // conditioning charge current, we determine that a battery is not connected. + // If that is not the case and a battery is connected, check + // if the battery voltage indicates it still needs conditioning. + //-------------------------------------------------------------------------- + +// if (ddi_bc_hwGetBatteryVoltage() >= 3900) { + if ((ddi_bc_hwGetBatteryVoltage() > + g_ddi_bc_Configuration.u16ConditioningMaxVoltage) && + (ddi_power_GetMaxBatteryChargeCurrent() < + g_ddi_bc_Configuration.u16ConditioningCurrent)) { + //---------------------------------------------------------------------- + // If control arrives here, voltage has risen too quickly for so + // little charge being applied so their must be no battery connected. + //---------------------------------------------------------------------- + + ddi_bc_gBrokenReason = DDI_BC_BROKEN_NO_BATTERY_DETECTED; + + TransitionToBroken(); + + //---------------------------------------------------------------------- + // Tell our caller the battery appears to be broken. + //---------------------------------------------------------------------- + + return (DDI_BC_STATUS_BROKEN); + + } + + if (ddi_bc_hwGetBatteryVoltage() >= + g_ddi_bc_Configuration.u16ConditioningMaxVoltage) { + + //---------------------------------------------------------------------- + // If control arrives here, this battery no longer needs conditioning. + //---------------------------------------------------------------------- + + TransitionToCharging(); + + //---------------------------------------------------------------------- + // Return success. + //---------------------------------------------------------------------- + + return (DDI_BC_STATUS_SUCCESS); + + } + //-------------------------------------------------------------------------- + // Have we been in this state too long? + //-------------------------------------------------------------------------- + + if (g_ddi_bc_u32StateTimer >= + g_ddi_bc_Configuration.u32ConditioningTimeout) { + + //---------------------------------------------------------------------- + // If control arrives here, we've been here too long. + //---------------------------------------------------------------------- + + ddi_bc_gBrokenReason = DDI_BC_BROKEN_CHARGING_TIMEOUT; + + TransitionToBroken(); + + //---------------------------------------------------------------------- + // Tell our caller the battery appears to be broken. + //---------------------------------------------------------------------- + + return (DDI_BC_STATUS_BROKEN); + + } + //-------------------------------------------------------------------------- + // If control arrives here, we're staying in this state. Step the current + // ramp. + //-------------------------------------------------------------------------- + + ddi_bc_RampStep(g_ddi_bc_Configuration.u32StateMachinePeriod); + + //-------------------------------------------------------------------------- + // Return success. + //-------------------------------------------------------------------------- + + return (DDI_BC_STATUS_SUCCESS); + +} + +//////////////////////////////////////////////////////////////////////////////// +//! +//! \brief Charging state function. +//! +//! \fntype Function +//! +//! This function implements the Charging state. +//! +//////////////////////////////////////////////////////////////////////////////// +static ddi_bc_Status_t ddi_bc_Charging(void) +{ + + //-------------------------------------------------------------------------- + // This variable counts the number of times we've seen the charging status + // bit cleared. + //-------------------------------------------------------------------------- + + static int iStatusCount = 0; + bool bIsDcdcOn; + + //-------------------------------------------------------------------------- + // The first order of business is to update alarms. + //-------------------------------------------------------------------------- + + ddi_bc_RampUpdateAlarms(); + + //-------------------------------------------------------------------------- + // If we're not under an alarm, increment the state timer. + //-------------------------------------------------------------------------- + + if (!ddi_bc_RampGetDieTempAlarm() && !ddi_bc_RampGetBatteryTempAlarm()) { + g_ddi_bc_u32StateTimer += + g_ddi_bc_Configuration.u32StateMachinePeriod; + } + //-------------------------------------------------------------------------- + // Check if the power supply is still around. + //-------------------------------------------------------------------------- + + if (!ddi_bc_hwPowerSupplyIsPresent()) { + + //---------------------------------------------------------------------- + // If control arrives here, the power supply has been removed. Go back + // and wait. + //---------------------------------------------------------------------- + + TransitionToWaitingToCharge(); + + //---------------------------------------------------------------------- + // Return success. + //---------------------------------------------------------------------- + + return (DDI_BC_STATUS_SUCCESS); + + } + //-------------------------------------------------------------------------- + // If control arrives here, we're still connected to a power supply. We need + // to decide now if the battery is still charging, or if it's nearly full. + // If it's still charging, we'll stay in this state. Otherwise, we'll move + // to the Topping Off state. + // + // Most of the time, we decide that the battery is still charging simply by + // checking if the the actual current flow is above the charging threshold + // current (as indicated by the charge status bit). However, if we're + // still ramping up to full charging current, the hardware may still be set + // to deliver an amount that's less than the threshold. In that case, the + // charging status bit would *definitely* show a low charging current, but + // that doesn't mean the battery is ready for topping off. + // + // So, in summary, we will move to the Topping Off state if both of the + // following are true: + // + // 1) The maximum current set in the hardware is greater than the charging + // threshold. + // -AND- + // 2) The actual current flow is also higher than the threshold (as + // indicated by the charge status bit). + // + //-------------------------------------------------------------------------- + + bIsDcdcOn = ddi_bc_hwIsDcdcOn(); + if (bIsDcdcOn) { + ddi_bc_hwSetCurrentThreshold(g_ddi_bc_Configuration. + u16DdcdModeChargingThresholdCurrent); + } else { + ddi_bc_hwSetCurrentThreshold(g_ddi_bc_Configuration. + u16ChargingThresholdCurrent); + } + + { + uint16_t u16ActualProgrammedCurrent = ddi_bc_hwGetMaxCurrent(); + + //---------------------------------------------------------------------- + // Get the Maximum current that we will ramp to. + //---------------------------------------------------------------------- + + //---------------------------------------------------------------------- + // Not all possible values are expressible by the BATTCHRG_I bitfield. + // The following coverts the max current value into the the closest hardware + // expressible bitmask equivalent. Then, it converts this back to the actual + // decimal current value that this bitmask represents. + //---------------------------------------------------------------------- + + uint16_t u16CurrentRampTarget = ddi_bc_RampGetTarget(); + + if (u16CurrentRampTarget > ddi_bc_RampGetLimit()) + u16CurrentRampTarget = ddi_bc_RampGetLimit(); + + //---------------------------------------------------------------------- + // Not all possible values are expressible by the BATTCHRG_I bitfield. + // The following coverts the max current value into the the closest hardware + // expressible bitmask equivalent. Then, it converts this back to the actual + // decimal current value that this bitmask represents. + //---------------------------------------------------------------------- + + u16CurrentRampTarget = + ddi_bc_hwExpressibleCurrent(u16CurrentRampTarget); + + //---------------------------------------------------------------------- + // We want to wait before we check the charge status bit until the ramping + // up is complete. Because the charge status bit is noisy, we want to + // disregard it until the programmed charge currint in BATTCHRG_I is well + // beyond the STOP_ILIMIT value. + //---------------------------------------------------------------------- + if ((u16ActualProgrammedCurrent >= u16CurrentRampTarget) && + !ddi_bc_hwGetChargeStatus()) { + uint8_t u8IlimitThresholdLimit; + //---------------------------------------------------------------------- + // If control arrives here, the hardware flag is telling us that the + // charging current has fallen below the threshold. We need to see this + // happen twice consecutively before we believe it. Increment the count. + //---------------------------------------------------------------------- + + iStatusCount++; + + //---------------------------------------------------------------------- + // If we are in DCDC operting mode, we only need this criteria to be true + // once before we advance to topping off. In 5V only mode, we want 10 + // consecutive times before advancing to topping off. + //---------------------------------------------------------------------- + + if (bIsDcdcOn) + u8IlimitThresholdLimit = 1; + else + u8IlimitThresholdLimit = 10; + + //---------------------------------------------------------------------- + // How many times in a row have we seen this status bit low? + //---------------------------------------------------------------------- + + if (iStatusCount >= u8IlimitThresholdLimit) { + + //------------------------------------------------------------------ + // If control arrives here, we've seen the CHRGSTS bit low too many + // times. This means it's time to move back to the waiting to charge + // state if DCDCs are present or move to the Topping Off state if + // no DCDCs are present. Because we can't measure only the current + // going to the battery when the DCDCs are active, we don't know when + // to start topping off or how long to top off. + // + // First, reset the status count for the next time we're in this + // state. + //------------------------------------------------------------------ + + iStatusCount = 0; + +#ifdef CONFIG_POWER_SUPPLY_DEBUG + u16ExternalBatteryPowerVoltageCheck = + ddi_bc_hwGetBatteryVoltage(); +#endif + + if (bIsDcdcOn) { + // We will restart the charge cycle once the DCDCs are no + // longer present. + DcdcInactityTimer = 0; + + TransitionToWaitingToCharge(); + } else if (g_ddi_bc_u32StateTimer <= + TRANSITION_TO_TOPOFF_MINIMUM_CHARGE_TIME_mS) + { + //------------------------------------------------------------------ + // If we haven't actually didn't have to charge very long + // then the battery was already full. In this case, we do + // not top off so that we do not needlessly overcharge the + // battery. + //------------------------------------------------------------------ + TransitionToWaitingToCharge(); + } else { + + //------------------------------------------------------------------ + // Move to the Topping Off state. + //------------------------------------------------------------------ + + TransitionToToppingOff(); + } + //------------------------------------------------------------------ + // Return success. + //------------------------------------------------------------------ + + return (DDI_BC_STATUS_SUCCESS); + + } + + } else { + + //---------------------------------------------------------------------- + // If control arrives here, the battery is still charging. Clear the + // status count. + //---------------------------------------------------------------------- + + iStatusCount = 0; + + } + + } + + //-------------------------------------------------------------------------- + // Have we been in this state too long? + //-------------------------------------------------------------------------- + + if (g_ddi_bc_u32StateTimer >= g_ddi_bc_Configuration.u32ChargingTimeout) { + + //---------------------------------------------------------------------- + // If control arrives here, we've been here too long. + //---------------------------------------------------------------------- + + ddi_bc_gBrokenReason = DDI_BC_BROKEN_CHARGING_TIMEOUT; + + TransitionToBroken(); + + //---------------------------------------------------------------------- + // Tell our caller the battery appears to be broken. + //---------------------------------------------------------------------- + + return (DDI_BC_STATUS_BROKEN); + + } + //-------------------------------------------------------------------------- + // If control arrives here, we're staying in this state. Step the current + // ramp. + //-------------------------------------------------------------------------- + + ddi_bc_RampStep(g_ddi_bc_Configuration.u32StateMachinePeriod); + + //-------------------------------------------------------------------------- + // Return success. + //-------------------------------------------------------------------------- + + return (DDI_BC_STATUS_SUCCESS); + +} + +//////////////////////////////////////////////////////////////////////////////// +//! +//! \brief Topping Off state function. +//! +//! \fntype Function +//! +//! This function implements the Topping Off state. +//! +//////////////////////////////////////////////////////////////////////////////// +static ddi_bc_Status_t ddi_bc_ToppingOff(void) +{ + + //-------------------------------------------------------------------------- + // The first order of business is to update alarms. + + //-------------------------------------------------------------------------- + + ddi_bc_RampUpdateAlarms(); + + //-------------------------------------------------------------------------- + // Increment the state timer. Notice that, unlike other states, we increment + // the state timer whether or not we're under an alarm. + //-------------------------------------------------------------------------- + + g_ddi_bc_u32StateTimer += g_ddi_bc_Configuration.u32StateMachinePeriod; + + //-------------------------------------------------------------------------- + // Check if the power supply is still around. + //-------------------------------------------------------------------------- + + if (!ddi_bc_hwPowerSupplyIsPresent()) { + + //---------------------------------------------------------------------- + // If control arrives here, the power supply has been removed. Go back + // and wait. + //--------------------------------------------------------------------- + + TransitionToWaitingToCharge(); + + //---------------------------------------------------------------------- + // Return success. + //---------------------------------------------------------------------- + + return (DDI_BC_STATUS_SUCCESS); + + } + + //-------------------------------------------------------------------------- + // Are we done topping off? + //-------------------------------------------------------------------------- + if (g_ddi_bc_u32StateTimer >= g_ddi_bc_Configuration.u32TopOffPeriod) { + + //---------------------------------------------------------------------- + // If control arrives here, we're done topping off. + //---------------------------------------------------------------------- + + TransitionToWaitingToCharge(); + + } + //-------------------------------------------------------------------------- + // If control arrives here, we're staying in this state. Step the current + // ramp. + //-------------------------------------------------------------------------- + + ddi_bc_RampStep(g_ddi_bc_Configuration.u32StateMachinePeriod); + + //-------------------------------------------------------------------------- + // Return success. + //-------------------------------------------------------------------------- + + return (DDI_BC_STATUS_SUCCESS); + +} + +//////////////////////////////////////////////////////////////////////////////// +// End of file +//////////////////////////////////////////////////////////////////////////////// +//! @} |