diff options
Diffstat (limited to 'drivers/power/stmp37xx/ddi_bc_ramp.c')
-rw-r--r-- | drivers/power/stmp37xx/ddi_bc_ramp.c | 724 |
1 files changed, 724 insertions, 0 deletions
diff --git a/drivers/power/stmp37xx/ddi_bc_ramp.c b/drivers/power/stmp37xx/ddi_bc_ramp.c new file mode 100644 index 000000000000..1de3a53259f8 --- /dev/null +++ b/drivers/power/stmp37xx/ddi_bc_ramp.c @@ -0,0 +1,724 @@ +/* + * 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_ramp.c +//! \brief Contains the Battery Charger current ramp controller. +//! \date 06/2005 +//! +//! This file contains Battery Charger current ramp controller. +//! +//////////////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////////////// +// Includes and external references +//////////////////////////////////////////////////////////////////////////////// + +#include <mach/ddi_bc.h> +#include "ddi_bc_internal.h" + +//////////////////////////////////////////////////////////////////////////////// +// Definitions +//////////////////////////////////////////////////////////////////////////////// + +//! This is the control structure for the current ramp. + +typedef struct _ddi_bc_RampControl { + + uint32_t u32AccumulatedTime; + + //!< The accumulated time since we last changed the actual + //!< current setting in the hardware. If the time between + //!< steps is quite short, we may have to wait for several steps + //!< before we can actually change the hardware setting. + + uint16_t u16Target; + + //!< The target current, regardless of expressibility. + + uint16_t u16Limit; + + //!< The current limit, regardless of expressibility. + + uint8_t dieTempAlarm:1; + + //!< Indicates if we are operating under a die temperature + //!< alarm. + + uint8_t batteryTempAlarm:1; + + //!< Indicates if we are operating under a battery temperature + //!< alarm. + + uint8_t ambientTempAlarm:1; + + //!< Indicates if we are operating under an ambient temperature + //!< alarm. + +} ddi_bc_RampControl_t; + +//////////////////////////////////////////////////////////////////////////////// +// Variables +//////////////////////////////////////////////////////////////////////////////// + +//! This structure contains control information for the current ramp. + +static ddi_bc_RampControl_t g_RampControl; + +//////////////////////////////////////////////////////////////////////////////// +// Code +//////////////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////////////// +//! +//! \brief Reset the current ramp. +//! +//! \fntype Function +//! +//! This function resets the current ramp. +//! +//! Note that this function does NOT reset the temperature alarms or the current +//! limit. Those can only be changed explicitly. +//! +//////////////////////////////////////////////////////////////////////////////// +void ddi_bc_RampReset() +{ + + //-------------------------------------------------------------------------- + // Reset the control structure. + //-------------------------------------------------------------------------- + + g_RampControl.u32AccumulatedTime = 0; + g_RampControl.u16Target = 0; + + //-------------------------------------------------------------------------- + // Step the ramp. Note that we don't care if this function returns an error. + // We're stepping the ramp to make sure it takes immediate effect, if + // possible. But, for example, if the Battery Charger is not yet + // initialized, it doesn't matter. + //-------------------------------------------------------------------------- + + ddi_bc_RampStep(0); + +} + +//////////////////////////////////////////////////////////////////////////////// +//! +//! \brief Set the target current. +//! +//! \fntype Function +//! +//! This function sets the target current and implements it immediately. +//! +//! Note that this function does NOT reset the temperature alarms. Those can +//! only be reset explicitly. +//! +//! \param[in] u16Target The target current. +//! +//! \retval The expressible version of the target. +//! +//////////////////////////////////////////////////////////////////////////////// +uint16_t ddi_bc_RampSetTarget(uint16_t u16Target) +{ + + //-------------------------------------------------------------------------- + // Set the target. + //-------------------------------------------------------------------------- + + g_RampControl.u16Target = u16Target; + + //-------------------------------------------------------------------------- + // Step the ramp. Note that we don't care if this function returns an error. + // We're stepping the ramp to make sure it takes immediate effect, if + // possible. But, for example, if the Battery Charger is not yet + // initialized, it doesn't matter. + //-------------------------------------------------------------------------- + + ddi_bc_RampStep(0); + + //-------------------------------------------------------------------------- + // Compute and return the expressible target. + //-------------------------------------------------------------------------- + + return (ddi_bc_hwExpressibleCurrent(u16Target)); + +} + +//////////////////////////////////////////////////////////////////////////////// +//! +//! \brief Report the target. +//! +//! \fntype Function +//! +//! This function reports the target. +//! +//! \retval The target. +//! +//////////////////////////////////////////////////////////////////////////////// +uint16_t ddi_bc_RampGetTarget(void) +{ + + //-------------------------------------------------------------------------- + // Return the target. + //-------------------------------------------------------------------------- + + return (g_RampControl.u16Target); + +} + +//////////////////////////////////////////////////////////////////////////////// +//! +//! \brief Set the current limit. +//! +//! \fntype Function +//! +//! This function sets the current limit and implements it immediately. +//! +//! \param[in] u16Limit The current limit. +//! +//! \retval The expressible version of the limit. +//! +//////////////////////////////////////////////////////////////////////////////// +uint16_t ddi_bc_RampSetLimit(uint16_t u16Limit) +{ + + //-------------------------------------------------------------------------- + // Set the limit. + //-------------------------------------------------------------------------- + + g_RampControl.u16Limit = u16Limit; + + //-------------------------------------------------------------------------- + // Step the ramp. Note that we don't care if this function returns an error. + // We're stepping the ramp to make sure it takes immediate effect, if + // possible. But, for example, if the Battery Charger is not yet + // initialized, it doesn't matter. + //-------------------------------------------------------------------------- + + ddi_bc_RampStep(0); + + //-------------------------------------------------------------------------- + // Compute and return the expressible limit. + //-------------------------------------------------------------------------- + + return (ddi_bc_hwExpressibleCurrent(u16Limit)); + +} + +//////////////////////////////////////////////////////////////////////////////// +//! +//! \brief Report the current limit. +//! +//! \fntype Function +//! +//! This function reports the current limit. +//! +//! \retval The current limit. +//! +//////////////////////////////////////////////////////////////////////////////// +uint16_t ddi_bc_RampGetLimit(void) +{ + + //-------------------------------------------------------------------------- + // Return the current limit. + //-------------------------------------------------------------------------- + + return (g_RampControl.u16Limit); + +} + +//////////////////////////////////////////////////////////////////////////////// +//! +//! \brief Update alarms. +//! +//! \fntype Function +//! +//! This function checks for all alarms and updates the current ramp +//! accordingly. +//! +//////////////////////////////////////////////////////////////////////////////// +void ddi_bc_RampUpdateAlarms() +{ + + // Set to true if something changed and we need to step the ramp right away. + + int iStepTheRamp = 0; + + //-------------------------------------------------------------------------- + // Are we monitoring die temperature? + //-------------------------------------------------------------------------- + + if (g_ddi_bc_Configuration.monitorDieTemp) { + + //---------------------------------------------------------------------- + // Get the die temperature range. + //---------------------------------------------------------------------- + + int16_t i16Low; + int16_t i16High; + + ddi_bc_hwGetDieTemp(&i16Low, &i16High); + + //---------------------------------------------------------------------- + // Now we need to decide if it's time to raise or lower the alarm. The + // first question to ask is: Were we already under an alarm? + //---------------------------------------------------------------------- + + if (g_RampControl.dieTempAlarm) { + + //------------------------------------------------------------------ + // If control arrives here, we were already under an alarm. We'll + // change that if the high end of the temperature range drops below + // the low temperature mark. + //------------------------------------------------------------------ + + if (i16High < g_ddi_bc_Configuration.u8DieTempLow) { + + //-------------------------------------------------------------- + // If control arrives here, we're safe now. Drop the alarm. + //-------------------------------------------------------------- + + g_RampControl.dieTempAlarm = 0; + + iStepTheRamp = !0; + +#ifdef CONFIG_POWER_SUPPLY_DEBUG + printk("Battery charger: releasing " + "die temp alarm: [%d, %d] < %d\r\n", + (int32_t) i16Low, (int32_t) i16High, + (int32_t) g_ddi_bc_Configuration. + u8DieTempLow); +#endif + + } + + } else { + + //------------------------------------------------------------------ + // If control arrives here, we were not under an alarm. We'll change + // that if the high end of the temperature range rises above the + // high temperature mark. + //------------------------------------------------------------------ + + if (i16High >= g_ddi_bc_Configuration.u8DieTempHigh) { + + //-------------------------------------------------------------- + // If control arrives here, we're running too hot. Raise the + // alarm. + //-------------------------------------------------------------- + + g_RampControl.dieTempAlarm = 1; + + iStepTheRamp = !0; + +#ifdef CONFIG_POWER_SUPPLY_DEBUG + printk("Battery charger: declaring " + "die temp alarm: [%d, %d] >= %d\r\n", + (int32_t) i16Low, (int32_t) i16High, + (int32_t) g_ddi_bc_Configuration. + u8DieTempLow); +#endif + } + + } + + } + //-------------------------------------------------------------------------- + // Are we monitoring battery temperature? + //-------------------------------------------------------------------------- + + if (g_ddi_bc_Configuration.monitorBatteryTemp) { + + ddi_bc_Status_t status; + + //---------------------------------------------------------------------- + // Get the battery temperature reading. + //---------------------------------------------------------------------- + + uint16_t u16Reading; + + status = ddi_bc_hwGetBatteryTemp(&u16Reading); + + //---------------------------------------------------------------------- + // If there was a problem, then we ignore the reading. Otherwise, let's + // have a look. + //---------------------------------------------------------------------- + + if (status == DDI_BC_STATUS_SUCCESS) { + + //------------------------------------------------------------------ + // Now we need to decide if it's time to raise or lower the alarm. + // The first question to ask is: Were we already under an alarm? + //------------------------------------------------------------------ + + if (g_RampControl.batteryTempAlarm) { + + //-------------------------------------------------------------- + // If control arrives here, we were already under an alarm. + // We'll change that if the reading drops below the low mark. + //-------------------------------------------------------------- + + if (u16Reading < + g_ddi_bc_Configuration.u16BatteryTempLow) { + + //---------------------------------------------------------- + // If control arrives here, we're safe now. Drop the alarm. + //---------------------------------------------------------- + + g_RampControl.batteryTempAlarm = 0; + + iStepTheRamp = !0; + + } + + } else { + + //-------------------------------------------------------------- + // If control arrives here, we were not under an alarm. We'll + // change that if the reading rises above the high mark. + //-------------------------------------------------------------- + + if (u16Reading >= + g_ddi_bc_Configuration.u16BatteryTempHigh) { + + //---------------------------------------------------------- + // If control arrives here, we're running too hot. Raise the + // alarm. + //---------------------------------------------------------- + + g_RampControl.batteryTempAlarm = 1; + + iStepTheRamp = !0; + + } + + } + + } + + } + //-------------------------------------------------------------------------- + // Do we need to step the ramp? + //-------------------------------------------------------------------------- + + if (iStepTheRamp) + ddi_bc_RampStep(0); + +} + +//////////////////////////////////////////////////////////////////////////////// +//! +//! \brief Reports the state of the die temperature alarm. +//! +//! \fntype Function +//! +//! This function reports the state of the die temperature alarm. +//! +//! \retval The state of the die temperature alarm. +//! +//////////////////////////////////////////////////////////////////////////////// +int ddi_bc_RampGetDieTempAlarm(void) +{ + return (g_RampControl.dieTempAlarm); +} + +//////////////////////////////////////////////////////////////////////////////// +//! +//! \brief Reports the state of the battery temperature alarm. +//! +//! \fntype Function +//! +//! This function reports the state of the battery temperature alarm. +//! +//! \retval The state of the battery temperature alarm. +//! +//////////////////////////////////////////////////////////////////////////////// +int ddi_bc_RampGetBatteryTempAlarm(void) +{ + return (g_RampControl.batteryTempAlarm); +} + +//////////////////////////////////////////////////////////////////////////////// +//! +//! \brief Reports the state of the ambient temperature alarm. +//! +//! \fntype Function +//! +//! This function reports the state of the ambient temperature alarm. +//! +//! \retval The state of the ambient temperature alarm. +//! +//////////////////////////////////////////////////////////////////////////////// +int ddi_bc_RampGetAmbientTempAlarm(void) +{ + return (g_RampControl.ambientTempAlarm); +} + +//////////////////////////////////////////////////////////////////////////////// +//! +//! \brief Step the current ramp. +//! +//! \fntype Function +//! +//! This function steps the current ramp forward through the given amount of time. +//! +//! \param[in] u32Time The time increment to add. +//! +//! \retval DDI_BC_STATUS_SUCCESS If the operation succeeded. +//! \retval DDI_BC_STATUS_NOT_INITIALIZED If the Battery Charger is not yet +//! initialized. +//! +//////////////////////////////////////////////////////////////////////////////// +ddi_bc_Status_t ddi_bc_RampStep(uint32_t u32Time) +{ + + uint16_t u16MaxNow; + uint16_t u16Target; + uint16_t u16Cart; + int32_t i32Delta; + + //-------------------------------------------------------------------------- + // Make sure the Battery Charger is initialized. + //-------------------------------------------------------------------------- + + if (g_ddi_bc_State == DDI_BC_STATE_UNINITIALIZED) { + return (DDI_BC_STATUS_NOT_INITIALIZED); + } + //-------------------------------------------------------------------------- + // Figure out how much current the hardware is set to draw right now. + //-------------------------------------------------------------------------- + + u16MaxNow = ddi_bc_hwGetMaxCurrent(); + + //-------------------------------------------------------------------------- + // Start with the target. + //-------------------------------------------------------------------------- + + u16Target = g_RampControl.u16Target; + + //-------------------------------------------------------------------------- + // Check the target against the hard limit. + //-------------------------------------------------------------------------- + + if (u16Target > g_RampControl.u16Limit) + u16Target = g_RampControl.u16Limit; + + //-------------------------------------------------------------------------- + // Check if the die temperature alarm is active. + //-------------------------------------------------------------------------- + + if (g_RampControl.dieTempAlarm) { + + //---------------------------------------------------------------------- + // If control arrives here, we are under a die temperature alarm. Clamp + // the target current. + //---------------------------------------------------------------------- + + if (u16Target > g_ddi_bc_Configuration.u16DieTempSafeCurrent) { + u16Target = + g_ddi_bc_Configuration.u16DieTempSafeCurrent; + } + + } + //-------------------------------------------------------------------------- + // Check if the battery temperature alarm is active. + //-------------------------------------------------------------------------- + + if (g_RampControl.batteryTempAlarm) { + + //---------------------------------------------------------------------- + // If control arrives here, we are under a battery temperature alarm. + // Clamp the target current. + //---------------------------------------------------------------------- + + if (u16Target > + g_ddi_bc_Configuration.u16BatteryTempSafeCurrent) { + u16Target = + g_ddi_bc_Configuration.u16BatteryTempSafeCurrent; + } + + } + //-------------------------------------------------------------------------- + // Now we know the target current. Figure out what is actually expressible + // in the hardware. + //-------------------------------------------------------------------------- + + u16Target = ddi_bc_hwExpressibleCurrent(u16Target); + + //-------------------------------------------------------------------------- + // Compute the difference between the expressible target and what's actually + // set in the hardware right now. + //-------------------------------------------------------------------------- + + i32Delta = ((int32_t) u16Target) - ((int32_t) u16MaxNow); + + //-------------------------------------------------------------------------- + // Check if the delta is zero. + //-------------------------------------------------------------------------- + + if (i32Delta == 0) { + + //---------------------------------------------------------------------- + // If control arrives here, there is no difference between what we want + // and what's set in the hardware. + // + // Before we leave, though, we don't want to leave any accumulated time + // laying around for the next ramp up. Zero it out. + //---------------------------------------------------------------------- + + g_RampControl.u32AccumulatedTime = 0; + + //---------------------------------------------------------------------- + // Return success. + //---------------------------------------------------------------------- + + return (DDI_BC_STATUS_SUCCESS); + + } + //-------------------------------------------------------------------------- + // Check if the delta is negative. + //-------------------------------------------------------------------------- + + if (i32Delta < 0) { + + //---------------------------------------------------------------------- + // If control arrives here, the new target is lower than what's + // currently set in the hardware. Since that means we're *reducing* the + // current draw, we can do it right now. Just gimme a sec here... + //---------------------------------------------------------------------- + + ddi_bc_hwSetMaxCurrent(u16Target); + +#ifdef CONFIG_POWER_SUPPLY_DEBUG + printk("Battery charger: setting max charge " + "current to: %hdmA\r\n", u16Target); +#endif + + //---------------------------------------------------------------------- + // Flip the power switch on the charging hardware according to the new + // current setting. + //---------------------------------------------------------------------- + + ddi_bc_hwSetChargerPower(u16Target != 0); + + //---------------------------------------------------------------------- + // We don't want to leave any accumulated time laying around for the + // next ramp up. Zero it out. + //---------------------------------------------------------------------- + + g_RampControl.u32AccumulatedTime = 0; + + //---------------------------------------------------------------------- + // Return success. + //---------------------------------------------------------------------- + + return (DDI_BC_STATUS_SUCCESS); + + } + //-------------------------------------------------------------------------- + // If control arrives here, the target current is higher than what's set in + // the hardware right now. That means we're going to ramp it up. To do that, + // we're going to "buy" more milliamps by "spending" milliseconds of time. + // Add the time we've "banked" to the time we've been credited in this call. + //-------------------------------------------------------------------------- + + u32Time += g_RampControl.u32AccumulatedTime; + + //-------------------------------------------------------------------------- + // Now we know how much we can spend. How much current will it buy? + //-------------------------------------------------------------------------- + + u16Cart = (g_ddi_bc_Configuration.u16CurrentRampSlope * u32Time) / 1000; + + //-------------------------------------------------------------------------- + // Check how the current we can afford stacks up against the target we want. + //-------------------------------------------------------------------------- + + if ((u16MaxNow + u16Cart) < u16Target) { + + //---------------------------------------------------------------------- + // If control arrives here, we can't afford to buy all the current we + // want. Compute the maximum we can afford, and then figure out what we + // can actually express in the hardware. + //---------------------------------------------------------------------- + + u16Target = ddi_bc_hwExpressibleCurrent(u16MaxNow + u16Cart); + + //---------------------------------------------------------------------- + // Check if the result isn't actually different from what's set in the + // the hardware right now. + //---------------------------------------------------------------------- + + if (u16Target == u16MaxNow) { + + //------------------------------------------------------------------ + // If control arrives here, we are so poor that we can't yet afford + // to buy enough current to make a change in the expressible + // hardware setting. Since we didn't spend any of our time, put the + // new balance back in the bank. + //------------------------------------------------------------------ + + g_RampControl.u32AccumulatedTime = u32Time; + + //------------------------------------------------------------------ + // Leave dispiritedly. + //------------------------------------------------------------------ + + return (DDI_BC_STATUS_SUCCESS); + + } + + } + //-------------------------------------------------------------------------- + // If control arrives here, we can afford to buy enough current to get us + // all the way to the target. Set it. + //-------------------------------------------------------------------------- + + ddi_bc_hwSetMaxCurrent(u16Target); + +#ifdef CONFIG_POWER_SUPPLY_DEBUG + printk("Battery charger: setting max charge" + "current to: %hdmA\r\n", u16Target); +#endif + + //-------------------------------------------------------------------------- + // Flip the power switch on the charging hardware according to the new + // current setting. + //-------------------------------------------------------------------------- + + ddi_bc_hwSetChargerPower(u16Target != 0); + + //-------------------------------------------------------------------------- + // We're at the target, so we're finished buying current. Zero out the + // account. + //-------------------------------------------------------------------------- + + g_RampControl.u32AccumulatedTime = 0; + + //-------------------------------------------------------------------------- + // Return success. + //-------------------------------------------------------------------------- + + return (DDI_BC_STATUS_SUCCESS); + +} + +//////////////////////////////////////////////////////////////////////////////// +// End of file +//////////////////////////////////////////////////////////////////////////////// +//! @} |