diff options
author | Rob Herring <r.herring@freescale.com> | 2009-10-19 14:43:19 -0500 |
---|---|---|
committer | Alejandro Gonzalez <alex.gonzalez@digi.com> | 2010-02-12 17:19:16 +0100 |
commit | cdde68e3a7d4cbf4701005ab6032366e76009419 (patch) | |
tree | 5690552665f0b7843e6552e4d5fe7b63cbc78f51 /drivers/power | |
parent | 57d1417ea543b83760b3fd76a46b9d29deb2e444 (diff) |
ENGR00117389 Port 5.0.0 release to 2.6.31
This is i.MX BSP 5.0.0 release ported to 2.6.31
Signed-off-by: Rob Herring <r.herring@freescale.com>
Signed-off-by: Alan Tull <r80115@freescale.com>
Signed-off-by: Xinyu Chen <xinyu.chen@freescale.com>
Diffstat (limited to 'drivers/power')
-rw-r--r-- | drivers/power/Kconfig | 7 | ||||
-rw-r--r-- | drivers/power/Makefile | 1 | ||||
-rw-r--r-- | drivers/power/stmp37xx/Makefile | 8 | ||||
-rw-r--r-- | drivers/power/stmp37xx/ddi_bc_api.c | 566 | ||||
-rw-r--r-- | drivers/power/stmp37xx/ddi_bc_hw.c | 474 | ||||
-rw-r--r-- | drivers/power/stmp37xx/ddi_bc_hw.h | 93 | ||||
-rw-r--r-- | drivers/power/stmp37xx/ddi_bc_init.c | 204 | ||||
-rw-r--r-- | drivers/power/stmp37xx/ddi_bc_internal.h | 52 | ||||
-rw-r--r-- | drivers/power/stmp37xx/ddi_bc_ramp.c | 724 | ||||
-rw-r--r-- | drivers/power/stmp37xx/ddi_bc_ramp.h | 50 | ||||
-rw-r--r-- | drivers/power/stmp37xx/ddi_bc_sm.c | 1122 | ||||
-rw-r--r-- | drivers/power/stmp37xx/ddi_bc_sm.h | 46 | ||||
-rw-r--r-- | drivers/power/stmp37xx/ddi_power_battery.c | 998 | ||||
-rw-r--r-- | drivers/power/stmp37xx/ddi_power_battery.h | 67 | ||||
-rw-r--r-- | drivers/power/stmp37xx/linux.c | 623 |
15 files changed, 5035 insertions, 0 deletions
diff --git a/drivers/power/Kconfig b/drivers/power/Kconfig index bdbc4f73fcdc..e7b1040b410e 100644 --- a/drivers/power/Kconfig +++ b/drivers/power/Kconfig @@ -103,4 +103,11 @@ config CHARGER_PCF50633 help Say Y to include support for NXP PCF50633 Main Battery Charger. +config BATTERY_STMP3XXX + tristate "Sigmatel STMP3xxx SoC battery charger driver" + depends on ARCH_STMP3XXX + help + Say Y to enable support for the battery charger state machine + for the Sigmatel STMP3xxx based SoC's. + endif # POWER_SUPPLY diff --git a/drivers/power/Makefile b/drivers/power/Makefile index 380d17c9ae29..0d6b10db79b7 100644 --- a/drivers/power/Makefile +++ b/drivers/power/Makefile @@ -28,3 +28,4 @@ obj-$(CONFIG_BATTERY_BQ27x00) += bq27x00_battery.o obj-$(CONFIG_BATTERY_DA9030) += da9030_battery.o obj-$(CONFIG_BATTERY_MAX17040) += max17040_battery.o obj-$(CONFIG_CHARGER_PCF50633) += pcf50633-charger.o +obj-$(CONFIG_BATTERY_STMP3XXX) += stmp37xx/ diff --git a/drivers/power/stmp37xx/Makefile b/drivers/power/stmp37xx/Makefile new file mode 100644 index 000000000000..25b340e5b014 --- /dev/null +++ b/drivers/power/stmp37xx/Makefile @@ -0,0 +1,8 @@ +# +# Makefile for the STMP3xxx battery charger driver +# + +obj-$(CONFIG_BATTERY_STMP3XXX) += stmp3xxx-battery.o + +stmp3xxx-battery-objs := ddi_bc_api.o ddi_bc_hw.o ddi_bc_init.o \ + ddi_bc_ramp.o ddi_bc_sm.o ddi_power_battery.o linux.o diff --git a/drivers/power/stmp37xx/ddi_bc_api.c b/drivers/power/stmp37xx/ddi_bc_api.c new file mode 100644 index 000000000000..5fd062ec4eb9 --- /dev/null +++ b/drivers/power/stmp37xx/ddi_bc_api.c @@ -0,0 +1,566 @@ +/* + * 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_api.c +//! \brief Contains the Battery Charger API. +//! \date 06/2005 +//! +//! This file contains Battery Charger API. +//! +//////////////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////////////// +// Includes +//////////////////////////////////////////////////////////////////////////////// + +#include "ddi_bc_internal.h" + +//////////////////////////////////////////////////////////////////////////////// +// Variables +//////////////////////////////////////////////////////////////////////////////// + +//! This structure holds the current Battery Charger configuration. + +ddi_bc_Cfg_t g_ddi_bc_Configuration; + +extern uint32_t g_ddi_bc_u32StateTimer; +extern ddi_bc_BrokenReason_t ddi_bc_gBrokenReason; +extern bool bRestartChargeCycle; + +//////////////////////////////////////////////////////////////////////////////// +// Code +//////////////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////////////// +//! +//! \brief Report the Battery Charger configuration. +//! +//! \fntype Function +//! +//! This function reports the Battery Charger configuration. +//! +//! Note that, if the Battery Charger has not yet been initialized, the data +//! returned by this function is unknown. +//! +//! \param[in,out] pCfg A pointer to a structure that will receive the data. +//! +//////////////////////////////////////////////////////////////////////////////// +void ddi_bc_QueryCfg(ddi_bc_Cfg_t * pCfg) +{ + + //-------------------------------------------------------------------------- + // Return the current configuration. + //-------------------------------------------------------------------------- + + *pCfg = g_ddi_bc_Configuration; + +} + +//////////////////////////////////////////////////////////////////////////////// +//! +//! \brief Shut down the Battery Charger. +//! +//! \fntype Function +//! +//! This function immediately shuts down the Battery Charger hardware and +//! returns the state machine to the Uninitialized state. Use this function to +//! safely “mummify” the battery charger before retiring it from memory. +//! +//////////////////////////////////////////////////////////////////////////////// +void ddi_bc_ShutDown() +{ + + //-------------------------------------------------------------------------- + // Reset the current ramp. + //-------------------------------------------------------------------------- + + ddi_bc_RampReset(); + + //-------------------------------------------------------------------------- + // Move to the Uninitialized state. + //-------------------------------------------------------------------------- + + g_ddi_bc_State = DDI_BC_STATE_UNINITIALIZED; + +} + +//////////////////////////////////////////////////////////////////////////////// +//! +//! \brief Advances the state machine. +//! +//! \fntype Function +//! +//! This function advances the state machine. +//! +//! \retval DDI_BC_STATUS_SUCCESS If all goes well +//! \retval DDI_BC_STATUS_NOT_INITIALIZED If the Battery Charger is not yet +//! initialized. +//! \retval DDI_BC_STATUS_BROKEN If the battery violated a time-out +//! and has been declared broken. +//! +//////////////////////////////////////////////////////////////////////////////// +ddi_bc_Status_t ddi_bc_StateMachine() +{ + + //-------------------------------------------------------------------------- + // Check if we've been initialized yet. + //-------------------------------------------------------------------------- + + if (g_ddi_bc_State == DDI_BC_STATE_UNINITIALIZED) { + return (DDI_BC_STATUS_NOT_INITIALIZED); + } + //-------------------------------------------------------------------------- + // Execute the function for the current state. + //-------------------------------------------------------------------------- + + return (stateFunctionTable[g_ddi_bc_State] ()); + +} + +//////////////////////////////////////////////////////////////////////////////// +//! +//! \brief Get the Battery Charger's current state. +//! +//! \fntype Function +//! +//! This function returns the current state. +//! +//! \retval The current state. +//! +//////////////////////////////////////////////////////////////////////////////// +ddi_bc_State_t ddi_bc_GetState() +{ + //-------------------------------------------------------------------------- + // Return the current state. + //-------------------------------------------------------------------------- + + return (g_ddi_bc_State); + +} + +//////////////////////////////////////////////////////////////////////////////// +//! +//! \brief Disable the Battery Charger. +//! +//! \fntype Function +//! +//! This function forces the Battery Charger into the Disabled state. +//! +//! \retval DDI_BC_STATUS_SUCCESS If all goes well +//! \retval DDI_BC_STATUS_NOT_INITIALIZED If the Battery Charger is not yet +//! initialized. +//! +//////////////////////////////////////////////////////////////////////////////// +ddi_bc_Status_t ddi_bc_SetDisable() +{ + + //-------------------------------------------------------------------------- + // Check if we've been initialized yet. + //-------------------------------------------------------------------------- + + if (g_ddi_bc_State == DDI_BC_STATE_UNINITIALIZED) { + return (DDI_BC_STATUS_NOT_INITIALIZED); + } + //-------------------------------------------------------------------------- + // Check if we've been initialized yet. + //-------------------------------------------------------------------------- + + if (g_ddi_bc_State == DDI_BC_STATE_BROKEN) { + return (DDI_BC_STATUS_BROKEN); + } + //-------------------------------------------------------------------------- + // Reset the current ramp. This will jam the current to zero and power off + // the charging hardware. + //-------------------------------------------------------------------------- + + ddi_bc_RampReset(); + + //-------------------------------------------------------------------------- + // Reset the state timer. + //-------------------------------------------------------------------------- + + g_ddi_bc_u32StateTimer = 0; + + //-------------------------------------------------------------------------- + // Move to the Disabled state. + //-------------------------------------------------------------------------- + + g_ddi_bc_State = DDI_BC_STATE_DISABLED; + + //-------------------------------------------------------------------------- + // Return success. + //-------------------------------------------------------------------------- + + return (DDI_BC_STATUS_SUCCESS); + +} + +//////////////////////////////////////////////////////////////////////////////// +//! +//! \brief Enable the Battery Charger. +//! +//! \fntype Function +//! +//! If the Battery Charger is in the Disabled state, this function moves it to +//! the Waiting to Charge state. +//! +//! \retval DDI_BC_STATUS_SUCCESS If all goes well +//! \retval DDI_BC_STATUS_NOT_INITIALIZED If the Battery Charger is not yet +//! initialized. +//! \retval DDI_BC_STATUS_NOT_DISABLED If the Battery Charger is not +//! disabled. +//! +//////////////////////////////////////////////////////////////////////////////// +ddi_bc_Status_t ddi_bc_SetEnable() +{ + + //-------------------------------------------------------------------------- + // Check if we've been initialized yet. + //-------------------------------------------------------------------------- + + if (g_ddi_bc_State == DDI_BC_STATE_UNINITIALIZED) { + return (DDI_BC_STATUS_NOT_INITIALIZED); + } + //-------------------------------------------------------------------------- + // If we're not in the Disabled state, this is pointless. + //-------------------------------------------------------------------------- + + if (g_ddi_bc_State != DDI_BC_STATE_DISABLED) { + return (DDI_BC_STATUS_NOT_DISABLED); + } + //-------------------------------------------------------------------------- + // Reset the state timer. + //-------------------------------------------------------------------------- + + g_ddi_bc_u32StateTimer = 0; + //-------------------------------------------------------------------------- + // Move to the Waiting to Charge state. + //-------------------------------------------------------------------------- + + g_ddi_bc_State = DDI_BC_STATE_WAITING_TO_CHARGE; + + //-------------------------------------------------------------------------- + // Return success. + //-------------------------------------------------------------------------- + + return (DDI_BC_STATUS_SUCCESS); + +} + +//////////////////////////////////////////////////////////////////////////////// +//! +//! \brief Declare the battery to be broken. +//! +//! \fntype Function +//! +//! This function forces the Battery Charger into the Broken state. +//! +//! \retval DDI_BC_STATUS_SUCCESS If all goes well +//! \retval DDI_BC_STATUS_NOT_INITIALIZED If the Battery Charger is not yet +//! initialized. +//! +//////////////////////////////////////////////////////////////////////////////// +ddi_bc_Status_t ddi_bc_SetBroken() +{ + + //-------------------------------------------------------------------------- + // Check if we've been initialized yet. + //-------------------------------------------------------------------------- + + if (g_ddi_bc_State == DDI_BC_STATE_UNINITIALIZED) { + return (DDI_BC_STATUS_NOT_INITIALIZED); + } + //-------------------------------------------------------------------------- + // Reset the current ramp. This will jam the current to zero and power off + // the charging hardware. + //-------------------------------------------------------------------------- + + ddi_bc_RampReset(); + + //-------------------------------------------------------------------------- + // Reset the state timer. + //-------------------------------------------------------------------------- + + g_ddi_bc_u32StateTimer = 0; + + //-------------------------------------------------------------------------- + // Move to the Broken state. + //-------------------------------------------------------------------------- + + ddi_bc_gBrokenReason = DDI_BC_BROKEN_CHARGING_TIMEOUT; + + g_ddi_bc_State = DDI_BC_STATE_BROKEN; + + //-------------------------------------------------------------------------- + // Return success. + //-------------------------------------------------------------------------- + + return (DDI_BC_STATUS_SUCCESS); + +} + +//////////////////////////////////////////////////////////////////////////////// +//! +//! \brief Declare the battery to be fixed. +//! +//! \fntype Function +//! +//! If the Battery Charger is in the Broken state, this function moves it to +//! the Disabled state. +//! +//! \retval DDI_BC_STATUS_SUCCESS If all goes well +//! \retval DDI_BC_STATUS_NOT_INITIALIZED If the Battery Charger is not yet +//! initialized. +//! \retval DDI_BC_STATUS_NOT_BROKEN If the Battery Charger is not broken. +//! +//////////////////////////////////////////////////////////////////////////////// +ddi_bc_Status_t ddi_bc_SetFixed() +{ + + //-------------------------------------------------------------------------- + // Check if we've been initialized yet. + //-------------------------------------------------------------------------- + + if (g_ddi_bc_State == DDI_BC_STATE_UNINITIALIZED) { + return (DDI_BC_STATUS_NOT_INITIALIZED); + } + //-------------------------------------------------------------------------- + // If we're not in the Broken state, this is pointless. + //-------------------------------------------------------------------------- + + if (g_ddi_bc_State != DDI_BC_STATE_BROKEN) { + return (DDI_BC_STATUS_NOT_BROKEN); + } + //-------------------------------------------------------------------------- + // Reset the state timer. + //-------------------------------------------------------------------------- + + g_ddi_bc_u32StateTimer = 0; + + //-------------------------------------------------------------------------- + // Unitialize the Broken Reason + //-------------------------------------------------------------------------- + + ddi_bc_gBrokenReason = DDI_BC_BROKEN_UNINITIALIZED; + + //-------------------------------------------------------------------------- + // Move to the Disabled state. + //-------------------------------------------------------------------------- + + g_ddi_bc_State = DDI_BC_STATE_DISABLED; + + //-------------------------------------------------------------------------- + // Return success. + //-------------------------------------------------------------------------- + + return (DDI_BC_STATUS_SUCCESS); + +} + +//////////////////////////////////////////////////////////////////////////////// +//! +//! \brief Set the current limit. +//! +//! \fntype Function +//! +//! This function applies a limit to the current that the Battery Charger can +//! draw. +//! +//! \param[in] u16Limit The maximum current the Battery Charger can draw +//! (in mA). +//! +//! \retval The expressible version of the limit. +//! +//////////////////////////////////////////////////////////////////////////////// +uint16_t ddi_bc_SetCurrentLimit(uint16_t u16Limit) +{ + + //-------------------------------------------------------------------------- + // Set the limit and return what is actually expressible. + //-------------------------------------------------------------------------- + + return (ddi_bc_RampSetLimit(u16Limit)); + +} + +//////////////////////////////////////////////////////////////////////////////// +//! +//! \brief Report the current limit. +//! +//! \fntype Function +//! +//! This function reports the limit to the current that the Battery Charger can +//! draw. +//! +//! \retval The current limit. +//! +//////////////////////////////////////////////////////////////////////////////// +uint16_t ddi_bc_GetCurrentLimit(void) +{ + + //-------------------------------------------------------------------------- + // Set the limit and return what is actually expressible. + //-------------------------------------------------------------------------- + + return (ddi_bc_RampGetLimit()); + +} + +//////////////////////////////////////////////////////////////////////////////// +//! +//! \brief Set the battery charger state machine period. +//! +//! \fntype Function +//! +//! This function sets a new state machine period. The Period and Slope should +//! be coordinated to achieve the minimal ramp step current which will minimize +//! transients on the system. +//! +//! \param[in] u32StateMachinePeriod (in milliseconds) +//! \param[in] u16CurrentRampSlope (in mA/s) +//! +//! \retval SUCCESS If all goes well +//! \retval ERROR_DDI_BCM_NOT_INITIALIZED If the Battery Charger is not yet +//! initialized. +//! +//////////////////////////////////////////////////////////////////////////////// +ddi_bc_Status_t ddi_bc_SetNewPeriodAndSlope(uint32_t u32StateMachinePeriod, + uint16_t u16CurrentRampSlope) +{ + //-------------------------------------------------------------------------- + // Check if we've been initialized yet. + //-------------------------------------------------------------------------- + bool bDisableRequired; + + if (g_ddi_bc_State == DDI_BC_STATE_UNINITIALIZED) { + return (DDI_BC_STATUS_NOT_INITIALIZED); + } + + if (g_ddi_bc_State == DDI_BC_STATE_DISABLED) + bDisableRequired = false; + else { + bDisableRequired = true; + ddi_bc_SetDisable(); + } + + // Looking at the code, changing the period while the battery charger is running + // doesn't seem to have a negative affect. One could wrap this in the mutex + // or implement further coordination if it did. + g_ddi_bc_Configuration.u32StateMachinePeriod = u32StateMachinePeriod; + g_ddi_bc_Configuration.u16CurrentRampSlope = u16CurrentRampSlope; + + if (bDisableRequired) + ddi_bc_SetEnable(); + + return (DDI_BC_STATUS_SUCCESS); + +} + +//////////////////////////////////////////////////////////////////////////////// +//! +//! \brief Report the state machine period. +//! +//! \fntype Function +//! +//! This function reports the battery charger period. +//! +//! \retval The battery charger period (in milliseconds). +//! +//////////////////////////////////////////////////////////////////////////////// +uint32_t ddi_bc_GetStateMachinePeriod() +{ + return (g_ddi_bc_Configuration.u32StateMachinePeriod); +} + +//////////////////////////////////////////////////////////////////////////////// +//! +//! \brief Report the current ramp slope. +//! +//! \fntype Function +//! +//! This function reports the current ramp slope. +//! +//! \retval The current ramp slope (in mA/s). +//! +//////////////////////////////////////////////////////////////////////////////// +uint32_t ddi_bc_GetCurrentRampSlope() +{ + return (g_ddi_bc_Configuration.u16CurrentRampSlope); +} + +//////////////////////////////////////////////////////////////////////////////// +//! +//! \brief Report the time spent in the present state (milliseconds) +//! +//! \fntype Function +//! +//! This function reports the time spent in the present charging state. Note that +//! for the states that actually charge the battery, this time does not include the +//! time spent under alarm conditions such as die termperature alarm or battery +//! temperature alarm. +//! +//! \retval The time spent in the current state in milliseconds. +//! +//////////////////////////////////////////////////////////////////////////////// +uint32_t ddi_bc_GetStateTime(void) +{ + return g_ddi_bc_u32StateTimer; +} + +//////////////////////////////////////////////////////////////////////////////// +//! +//! \brief Report the reason for being in the broken state +//! +//! \fntype Function +//! +//! +//! \retval ddi_bc_BrokenReason_t enumeration +//! +//////////////////////////////////////////////////////////////////////////////// +ddi_bc_BrokenReason_t ddi_bc_GetBrokenReason(void) +{ + return ddi_bc_gBrokenReason; +} + +//////////////////////////////////////////////////////////////////////////////// +//! +//! \brief Restart the charge cycle +//! +//! \fntype Function +//! +//! +//! \retval SUCCESS +//! +//////////////////////////////////////////////////////////////////////////////// +ddi_bc_Status_t ddi_bc_ForceChargingToStart(void) +{ + static int16_t restarts = 0; + + if (restarts < DDI_BC_MAX_RESTART_CYCLES) { + restarts++; + bRestartChargeCycle = true; + } + + return (DDI_BC_STATUS_SUCCESS); +} + +//////////////////////////////////////////////////////////////////////////////// +// End of file +//////////////////////////////////////////////////////////////////////////////// +//! @} diff --git a/drivers/power/stmp37xx/ddi_bc_hw.c b/drivers/power/stmp37xx/ddi_bc_hw.c new file mode 100644 index 000000000000..fab82a738b01 --- /dev/null +++ b/drivers/power/stmp37xx/ddi_bc_hw.c @@ -0,0 +1,474 @@ +/* + * 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 + */ + +#include "ddi_bc_internal.h" + +//////////////////////////////////////////////////////////////////////////////// +//! \addtogroup ddi_bc +//! @{ +// +// Copyright (c) 2004-2005 SigmaTel, Inc. +// +//! \file ddi_bc_hw.c +//! \brief Contains the Battery Charger hardware operations. +//! \date 06/2005 +//! +//! This file contains Battery Charger hardware operations. +//! +//////////////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////////////// +// Includes and external references +//////////////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////////////// +// Variables +//////////////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////////////// +// Code +//////////////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////////////// +//! +//! \brief Report if the battery charging hardware is available. +//! +//! \fntype Function +//! +//! This function reports if the battery charging hardware is available by +//! reading the corresponding laser fuse bit. +//! +//! \retval Zero if the battery charging hardware is not available. Non-zero +//! otherwise. +//! +//////////////////////////////////////////////////////////////////////////////// +int ddi_bc_hwBatteryChargerIsEnabled(void) +{ + //TODO: replace ddi_bc_hwBatteryChargerIsEnabled with the function below in the code + return (int)ddi_power_GetBatteryChargerEnabled(); +} + +//////////////////////////////////////////////////////////////////////////////// +//! +//! \brief Report the battery configuration. +//! +//! \fntype Function +//! +//! This function reports the hardware battery configuration. +//! +//! \retval A value that indicates the battery configuration. +//! +//////////////////////////////////////////////////////////////////////////////// +ddi_bc_BatteryMode_t ddi_bc_hwGetBatteryMode(void) +{ + //TODO: replace ddi_bc_hwGetBatteryMode() with the function below. + return (ddi_bc_BatteryMode_t) ddi_power_GetBatteryMode(); +} + +//////////////////////////////////////////////////////////////////////////////// +//! +//! \brief Report the bias current source. +//! +//! \fntype Function +//! +//! This function Battery Charger. +//! +//! \retval A value that indicates the current bias current source. +//! +//////////////////////////////////////////////////////////////////////////////// +ddi_bc_BiasCurrentSource_t ddi_bc_hwGetBiasCurrentSource(void) +{ + // TODO: replace ddi_bc_BiasCurrentSource_t() with the function below. + return (ddi_bc_BiasCurrentSource_t) ddi_power_GetBiasCurrentSource(); +} + +//////////////////////////////////////////////////////////////////////////////// +//! +//! \brief Sets the bias current source. +//! +//! \fntype Function +//! +//! This function sets the bias current source used by the battery charger +//! hardware. The battery charger is usually configured to use an externally +//! generated bias current, which is expected to be quite precise. To reduce +//! component count, for example, it can be configured to generate a lesser- +//! quality bias current internally. +//! +//! \param[in] source Indicates the source of the bias current. +//! +//! \retval DDI_BC_STATUS_SUCCESS If the operation succeeded. +//! \retval DDI_BC_STATUS_BAD_ARGUMENT If the given source argument isn't one +//! of the expected values. +//! +//////////////////////////////////////////////////////////////////////////////// +ddi_bc_Status_t ddi_bc_hwSetBiasCurrentSource(ddi_bc_BiasCurrentSource_t source) +{ + ddi_power_BiasCurrentSource_t eSource = + (ddi_power_BiasCurrentSource_t) source; + + //-------------------------------------------------------------------------- + // In the following code, there are two points that can be confusing. Both + // of these problems derive from silliness in the data sheet. + // + // The field of interest here is HW_POWER_BATTCHRG.USE_EXTERN_R. This is + // poorly named for two reasons: + // + // 1) What we're really doing is selecting the source of the bias current. + // If the application chooses to use an external bias current, then + // there will, of course, be a resistor involved (apparently, a good- + // quality one at about 620 Ohms). + // + // 2) If the bit is SET, that tells the hardware to use the INTERNAL bias + // current. If this bit is CLEAR, that tells the hardware to use the + // EXTERNAL bias current. Thus, the actual logic for this bit is + // reversed from the sense indicated by its name. + //-------------------------------------------------------------------------- + + // TODO: replace all ddi_bc_hwSetBiasCurrentSource() with function below. + return (ddi_bc_Status_t) ddi_power_SetBiasCurrentSource(eSource); + +} + +//////////////////////////////////////////////////////////////////////////////// +//! +//! \brief Report the voltage across the battery. +//! +//! \fntype Function +//! +//! This function reports the voltage across the battery. +//! +//! \retval The voltage across the battery, in mV. +//! +//////////////////////////////////////////////////////////////////////////////// +uint16_t ddi_bc_hwGetBatteryVoltage(void) +{ + //TODO: replace ddi_bc_hwGetBattery with function below + return ddi_power_GetBattery(); +} + +//////////////////////////////////////////////////////////////////////////////// +//! +//! \brief Report on the presence of the power supply. +//! +//! \fntype Function +//! +//! This function repots on whether or not the 5V power supply is present. +//! +//! \retval Zero if the power supply is not present. Non-zero otherwise. +//! +//////////////////////////////////////////////////////////////////////////////// +int ddi_bc_hwPowerSupplyIsPresent(void) +{ + // TODO: replace ddi_bc_hwPowerSupplyIsPresent with the functino below. + return (int)ddi_power_Get5vPresentFlag(); +} + +//////////////////////////////////////////////////////////////////////////////// +//! +//! \brief Report the maximum charging current. +//! +//! \fntype Function +//! +//! This function reports the maximum charging current that will be offered to +//! the battery, as currently set in the hardware. +//! +//! \retval The maximum current setting in the hardware. +//! +//////////////////////////////////////////////////////////////////////////////// +uint16_t ddi_bc_hwGetMaxCurrent(void) +{ + // TODO: replace ddi_bc_hwGetMaxCurrent() with the below function + return (uint16_t) ddi_power_GetMaxBatteryChargeCurrent(); +} + +//////////////////////////////////////////////////////////////////////////////// +//! +//! \brief Set the maximum charging current. +//! +//! \fntype Function +//! +//! This function sets the maximum charging current that will be offered to the +//! battery. +//! +//! Note that the hardware has a minimum resolution of 10mA and a maximum +//! expressible value of 780mA (see the data sheet for details). If the given +//! current cannot be expressed exactly, then the largest expressible smaller +//! value will be used. The return reports the actual value that was effected. +//! +//! \param[in] u16Limit The maximum charging current, in mA. +//! +//! \retval The actual value that was effected. +//! +//////////////////////////////////////////////////////////////////////////////// +uint16_t ddi_bc_hwSetMaxCurrent(uint16_t u16Limit) +{ + //TODO: replace ddi_bc_hwSetMaxChargeCurrent + return ddi_power_SetMaxBatteryChargeCurrent(u16Limit); +} + +//////////////////////////////////////////////////////////////////////////////// +//! +//! \brief Set the charging current threshold. +//! +//! \fntype Function +//! +//! This function sets the charging current threshold. When the actual current +//! flow to the battery is less than this threshold, the HW_POWER_STS.CHRGSTS +//! flag is clear. +//! +//! Note that the hardware has a minimum resolution of 10mA and a maximum +//! expressible value of 180mA (see the data sheet for details). If the given +//! current cannot be expressed exactly, then the largest expressible smaller +//! value will be used. The return reports the actual value that was effected. +//! +//! \param[in] u16Threshold The charging current threshold, in mA. +//! +//! \retval The actual value that was effected. +//! +//////////////////////////////////////////////////////////////////////////////// +uint16_t ddi_bc_hwSetCurrentThreshold(uint16_t u16Threshold) +{ + //TODO: replace calls to ddi_bc_hwSetCurrentThreshold with the one below + return ddi_power_SetBatteryChargeCurrentThreshold(u16Threshold); + +} + +//////////////////////////////////////////////////////////////////////////////// +//! +//! \brief Report the charging current threshold. +//! +//! \fntype Function +//! +//! This function reports the charging current threshold. When the actual +//! current flow to the battery is less than this threshold, the +//! HW_POWER_STS.CHRGSTS flag is clear. +//! +//! Note that the hardware has a minimum resolution of 10mA and a maximum +//! expressible value of 180mA (see the data sheet for details). +//! +//! \retval The charging current threshold, in mA. +//! +//////////////////////////////////////////////////////////////////////////////// +uint16_t ddi_bc_hwGetCurrentThreshold(void) +{ + //TODO: replace calls to ddi_bc_hwGetCurrentThreshold with function below + return ddi_power_GetBatteryChargeCurrentThreshold(); +} + +//////////////////////////////////////////////////////////////////////////////// +//! +//! \brief Report if the charger hardware power is on. +//! +//! \fntype Function +//! +//! This function reports if the charger hardware power is on. +//! +//! \retval Zero if the charger hardware is not powered. Non-zero otherwise. +//! +//////////////////////////////////////////////////////////////////////////////// +int ddi_bc_hwChargerPowerIsOn(void) +{ + + //-------------------------------------------------------------------------- + // Note that the bit we're looking at is named PWD_BATTCHRG. The "PWD" + // stands for "power down". Thus, when the bit is set, the battery charger + // hardware is POWERED DOWN. + //-------------------------------------------------------------------------- + + //-------------------------------------------------------------------------- + // Read the register and return the result. + //-------------------------------------------------------------------------- + + //TODO: replace ddi_bc_hwChargerPowerIsOn with function below + return ddi_power_GetChargerPowered(); +} + +//////////////////////////////////////////////////////////////////////////////// +//! +//! \brief Turn the charging hardware on or off. +//! +//! \fntype Function +//! +//! This function turns the charging hardware on or off. +//! +//! \param[in] on Indicates whether the charging hardware should be on or off. +//! +//////////////////////////////////////////////////////////////////////////////// +void ddi_bc_hwSetChargerPower(int on) +{ + + //-------------------------------------------------------------------------- + // Note that the bit we're looking at is named PWD_BATTCHRG. The "PWD" + // stands for "power down". Thus, when the bit is set, the battery charger + // hardware is POWERED DOWN. + //-------------------------------------------------------------------------- + + //-------------------------------------------------------------------------- + // Hit the power switch. + //-------------------------------------------------------------------------- + + //TODO: replace ddi_bc_hwSetChargerPower with functino below + ddi_power_SetChargerPowered(on); +} + +//////////////////////////////////////////////////////////////////////////////// +//! +//! \brief Reports if the charging current has fallen below the threshold. +//! +//! \fntype Function +//! +//! This function reports if the charging current that the battery is accepting +//! has fallen below the threshold. +//! +//! Note that this bit is regarded by the hardware guys as very slightly +//! unreliable. They recommend that you don't believe a value of zero until +//! you've sampled it twice. +//! +//! \retval Zero if the battery is accepting less current than indicated by the +//! charging threshold. Non-zero otherwise. +//! +//////////////////////////////////////////////////////////////////////////////// +int ddi_bc_hwGetChargeStatus(void) +{ + return ddi_power_GetChargeStatus(); +} + +//////////////////////////////////////////////////////////////////////////////// +//! +//! \brief Report on the die temperature. +//! +//! \fntype Function +//! +//! This function reports on the die temperature. +//! +//! \param[out] pLow The low end of the temperature range. +//! \param[out] pHigh The high end of the temperature range. +//! +//////////////////////////////////////////////////////////////////////////////// +void ddi_bc_hwGetDieTemp(int16_t * pLow, int16_t * pHigh) +{ + // TODO: replace ddi_bc_hwGetDieTemp with function below + ddi_power_GetDieTemp(pLow, pHigh); +} + +//////////////////////////////////////////////////////////////////////////////// +//! +//! \brief Report the battery temperature reading. +//! +//! \fntype Function +//! +//! This function examines the configured LRADC channel and reports the battery +//! temperature reading. +//! +//! \param[out] pReading A pointer to a variable that will receive the +//! temperature reading. +//! +//! \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_hwGetBatteryTemp(uint16_t * pReading) +{ + return (ddi_bc_Status_t)DDI_BC_STATUS_HARDWARE_DISABLED; +} + +//////////////////////////////////////////////////////////////////////////////// +//! +//! \brief Convert a current in mA to a hardware setting. +//! +//! \fntype Function +//! +//! This function converts a current measurement in mA to a hardware setting +//! used by HW_POWER_BATTCHRG.STOP_ILIMIT or HW_POWER_BATTCHRG.BATTCHRG_I. +//! +//! Note that the hardware has a minimum resolution of 10mA and a maximum +//! expressible value of 780mA (see the data sheet for details). If the given +//! current cannot be expressed exactly, then the largest expressible smaller +//! value will be used. +//! +//! \param[in] u16Current The current of interest. +//! +//! \retval The corresponding setting. +//! +//////////////////////////////////////////////////////////////////////////////// +uint8_t ddi_bc_hwCurrentToSetting(uint16_t u16Current) +{ + return ddi_power_convert_current_to_setting(u16Current); +} + +//////////////////////////////////////////////////////////////////////////////// +//! +//! \brief Convert a hardware current setting to a value in mA. +//! +//! \fntype Function +//! +//! This function converts a setting used by HW_POWER_BATTCHRG.STOP_ILIMIT or +//! HW_POWER_BATTCHRG.BATTCHRG_I into an actual current measurement in mA. +//! +//! Note that the hardware current fields are 6 bits wide. The higher bits in +//! the 8-bit input parameter are ignored. +//! +//! \param[in] u8Setting A hardware current setting. +//! +//! \retval The corresponding current in mA. +//! +//////////////////////////////////////////////////////////////////////////////// +uint16_t ddi_bc_hwSettingToCurrent(uint8_t u8Setting) +{ + return ddi_power_convert_setting_to_current(u8Setting); +} + +//////////////////////////////////////////////////////////////////////////////// +//! +//! \brief Compute the actual current expressible in the hardware. +//! +//! \fntype Function +//! +//! Given a desired current, this function computes the actual current +//! expressible in the hardware. +//! +//! Note that the hardware has a minimum resolution of 10mA and a maximum +//! expressible value of 780mA (see the data sheet for details). If the given +//! current cannot be expressed exactly, then the largest expressible smaller +//! value will be used. +//! +//! \param[in] u16Current The current of interest. +//! +//! \retval The corresponding current in mA. +//! +//////////////////////////////////////////////////////////////////////////////// +uint16_t ddi_bc_hwExpressibleCurrent(uint16_t u16Current) +{ + //TODO: replace the bc function with this one + return ddi_power_ExpressibleCurrent(u16Current); +} + +//////////////////////////////////////////////////////////////////////////////// +//! +//! \brief Checks to see if the DCDC has been manually enabled +//! +//! \fntype Function +//! +//! \retval true if DCDC is ON, false if DCDC is OFF. +//! +//////////////////////////////////////////////////////////////////////////////// +bool ddi_bc_hwIsDcdcOn(void) +{ + return ddi_power_IsDcdcOn(); +} + +//////////////////////////////////////////////////////////////////////////////// +// End of file +//////////////////////////////////////////////////////////////////////////////// +//! @} diff --git a/drivers/power/stmp37xx/ddi_bc_hw.h b/drivers/power/stmp37xx/ddi_bc_hw.h new file mode 100644 index 000000000000..68ef98aa4303 --- /dev/null +++ b/drivers/power/stmp37xx/ddi_bc_hw.h @@ -0,0 +1,93 @@ +/* + * 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_hw.h +//! \brief Internal header file for Battery Charger hardware operations. +//! \date 06/2005 +//! +//! This file contains internal declarations for Battery Charger hardware +//! operations. +//! +//! \see ddi_bc.c and related files. +//////////////////////////////////////////////////////////////////////////////// + +#ifndef _DDI_BC_HW_H +#define _DDI_BC_HW_H + +//////////////////////////////////////////////////////////////////////////////// +// Definitions +//////////////////////////////////////////////////////////////////////////////// + +//! The enumeration of battery modes. + +typedef enum _ddi_bc_BatteryMode { + DDI_BC_BATTERY_MODE_LI_ION_2_CELLS = 0, + DDI_BC_BATTERY_MODE_LI_ION_1_CELL = 1, + DDI_BC_BATTERY_MODE_2_CELLS = 2, + DDI_BC_BATTERY_MODE_1_CELL = 3 +} ddi_bc_BatteryMode_t; + +//! The enumeration of bias current sources. + +typedef enum _ddi_bc_BiasCurrentSource { + DDI_BC_EXTERNAL_BIAS_CURRENT = 0, + DDI_BC_INTERNAL_BIAS_CURRENT = 1, +} ddi_bc_BiasCurrentSource_t; + +//////////////////////////////////////////////////////////////////////////////// +// Prototypes +//////////////////////////////////////////////////////////////////////////////// + +extern int ddi_bc_hwBatteryChargerIsEnabled(void); +extern ddi_bc_BatteryMode_t ddi_bc_hwGetBatteryMode(void); +extern ddi_bc_BiasCurrentSource_t ddi_bc_hwGetBiasCurrentSource(void); +extern ddi_bc_Status_t +ddi_bc_hwSetBiasCurrentSource(ddi_bc_BiasCurrentSource_t); +extern ddi_bc_Status_t ddi_bc_hwSetChargingVoltage(uint16_t); +extern uint16_t ddi_bc_hwGetBatteryVoltage(void); +extern int ddi_bc_hwPowerSupplyIsPresent(void); +extern uint16_t ddi_bc_hwSetMaxCurrent(uint16_t); +extern uint16_t ddi_bc_hwGetMaxCurrent(void); +extern uint16_t ddi_bc_hwSetCurrentThreshold(uint16_t); +extern uint16_t ddi_bc_hwGetCurrentThreshold(void); +extern int ddi_bc_hwChargerPowerIsOn(void); +extern void ddi_bc_hwSetChargerPower(int); +extern int ddi_bc_hwGetChargeStatus(void); +extern void ddi_bc_hwGetDieTemp(int16_t *, int16_t *); +extern ddi_bc_Status_t ddi_bc_hwGetBatteryTemp(uint16_t *); +uint8_t ddi_bc_hwCurrentToSetting(uint16_t); +uint16_t ddi_bc_hwSettingToCurrent(uint8_t); +uint16_t ddi_bc_hwExpressibleCurrent(uint16_t); + +//////////////////////////////////////////////////////////////////////////////// +//! +//! \brief Checks to see if the DCDC has been manually enabled +//! +//! \fntype Function +//! +//! \retval true if DCDC is ON, false if DCDC is OFF. +//! +//////////////////////////////////////////////////////////////////////////////// +bool ddi_bc_hwIsDcdcOn(void); + +//////////////////////////////////////////////////////////////////////////////// +// End of file +//////////////////////////////////////////////////////////////////////////////// +#endif // _DDI_BC_H +//! @} diff --git a/drivers/power/stmp37xx/ddi_bc_init.c b/drivers/power/stmp37xx/ddi_bc_init.c new file mode 100644 index 000000000000..ef323b33b74f --- /dev/null +++ b/drivers/power/stmp37xx/ddi_bc_init.c @@ -0,0 +1,204 @@ +/* + * 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 + */ + +#include "ddi_bc_internal.h" + +//////////////////////////////////////////////////////////////////////////////// +//! \addtogroup ddi_bc +//! @{ +// +// Copyright (c) 2004-2005 SigmaTel, Inc. +// +//! \file ddi_bc_init.c +//! \brief Contains the Battery Charger initialization function. +//! \date 06/2005 +//! +//! This file contains Battery Charger initialization function. +//! +//////////////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////////////// +// Includes and external references +//////////////////////////////////////////////////////////////////////////////// +#include <mach/ddi_bc.h> +#include "ddi_bc_internal.h" + +//////////////////////////////////////////////////////////////////////////////// +// Code +//////////////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////////////// +//! \brief Initialize the Battery Charger. +//! +//! \fntype Function +//! +//! This function initializes the Battery Charger. +//! +//! \param[in] pCfg A pointer to the new configuration. +//! +//! \retval DDI_BC_STATUS_SUCCESS +//! If the operation succeeded. +//! \retval DDI_BC_STATUS_ALREADY_INITIALIZED +//! If the Battery Charger is already initialized. +//! \retval DDI_BC_STATUS_HARDWARE_DISABLED +//! If the Battery Charger hardware is disabled by a laser fuse. +//! \retval DDI_BC_STATUS_BAD_BATTERY_MODE +//! If the power supply is set up for a non-rechargeable battery. +//! \retval DDI_BC_STATUS_CLOCK_GATE_CLOSED +//! If the clock gate for the power supply registers is closed. +//! \retval DDI_BC_STATUS_CFG_BAD_CHARGING_VOLTAGE +//! If the charging voltage is not either 4100 or 4200. +//! \retval DDI_BC_STATUS_CFG_BAD_BATTERY_TEMP_CHANNEL +//! If the LRADC channel number for monitoring battery temperature +//! is bad. +//! +//////////////////////////////////////////////////////////////////////////////// +ddi_bc_Status_t ddi_bc_Init(ddi_bc_Cfg_t * pCfg) +{ + + //-------------------------------------------------------------------------- + // We can only be initialized if we're in the Uninitialized state. + //-------------------------------------------------------------------------- + + if (g_ddi_bc_State != DDI_BC_STATE_UNINITIALIZED) { + return (DDI_BC_STATUS_ALREADY_INITIALIZED); + } + //-------------------------------------------------------------------------- + // Check if the battery charger hardware has been disabled by laser fuse. + //-------------------------------------------------------------------------- + + if (!ddi_power_GetBatteryChargerEnabled()) + return (DDI_BC_STATUS_HARDWARE_DISABLED); + + //-------------------------------------------------------------------------- + // Check if the power supply has been set up for a non-rechargeable battery. + //-------------------------------------------------------------------------- + + switch (ddi_power_GetBatteryMode()) { + + case DDI_POWER_BATT_MODE_LIION: + break; + + // TODO: we'll need to do NiMH also + default: + return (DDI_BC_STATUS_BAD_BATTERY_MODE); + //break; + + } + + //-------------------------------------------------------------------------- + // Make sure that the clock gate has been opened for the power supply + // registers. If not, then none of our writes to those registers will + // succeed, which will kind of slow us down... + //-------------------------------------------------------------------------- + + if (ddi_power_GetPowerClkGate()) { + return (DDI_BC_STATUS_CLOCK_GATE_CLOSED); + } + //-------------------------------------------------------------------------- + // Check the incoming configuration for nonsense. + //-------------------------------------------------------------------------- + + // + // Only permitted charging voltage: 4200mV. + // + + if (pCfg->u16ChargingVoltage != DDI_BC_LIION_CHARGING_VOLTAGE) { + return (DDI_BC_STATUS_CFG_BAD_CHARGING_VOLTAGE); + } + // + // There are 8 LRADC channels. + // + + if (pCfg->u8BatteryTempChannel > 7) { + return (DDI_BC_STATUS_CFG_BAD_BATTERY_TEMP_CHANNEL); + } + //-------------------------------------------------------------------------- + // Accept the configuration. + //-------------------------------------------------------------------------- + + //-------------------------------------------------------------------------- + // ddi_bc_Cfg_t.u16ChargingThresholdCurrent is destined for the + // register field HW_POWER_BATTCHRG.STOP_ILIMIT. This 4-bit field + // is unevenly quantized to provide a useful range of currents. A + // side effect of the quantization is that the field can only be + // set to certain unevenly-spaced values. + // + // Here, we use the two functions that manipulate the register field + // to adjust u16ChargingThresholdCurrent to match the quantized value. + //-------------------------------------------------------------------------- + pCfg->u16ChargingThresholdCurrent = + ddi_power_ExpressibleCurrent(pCfg->u16ChargingThresholdCurrent); + + //-------------------------------------------------------------------------- + // ...similar situation with ddi_bc_Cfg_t.u16BatteryTempSafeCurrent and + // u16DieTempSafeCurrent. + //-------------------------------------------------------------------------- + pCfg->u16BatteryTempSafeCurrent = + ddi_power_ExpressibleCurrent(pCfg->u16BatteryTempSafeCurrent); + pCfg->u16DieTempSafeCurrent = + ddi_power_ExpressibleCurrent(pCfg->u16DieTempSafeCurrent); + + g_ddi_bc_Configuration = *pCfg; + + //-------------------------------------------------------------------------- + // Set the bias current source. + //-------------------------------------------------------------------------- + + { + ddi_power_BiasCurrentSource_t Flag; + + Flag = + g_ddi_bc_Configuration.useInternalBias ? + DDI_POWER_INTERNAL_BIAS_CURRENT : + DDI_POWER_EXTERNAL_BIAS_CURRENT; + + ddi_power_SetBiasCurrentSource(Flag); + + } + + //-------------------------------------------------------------------------- + // Turn the charger hardware off. This is a very important initial condition + // because we only flip the power switch on the hardware when we make + // transitions. Baseline, it needs to be off. + //-------------------------------------------------------------------------- + + ddi_power_SetChargerPowered(0); + + //-------------------------------------------------------------------------- + // Reset the current ramp. This will jam the current to zero and power off + // the charging hardware. + //-------------------------------------------------------------------------- + + ddi_bc_RampReset(); + + //-------------------------------------------------------------------------- + // Move to the Disabled state. + //-------------------------------------------------------------------------- + + g_ddi_bc_State = DDI_BC_STATE_DISABLED; + + //-------------------------------------------------------------------------- + // Return success. + //-------------------------------------------------------------------------- +#ifdef CONFIG_POWER_SUPPLY_DEBUG + printk("%s: success\n", __func__); +#endif + return (DDI_BC_STATUS_SUCCESS); + +} + +//////////////////////////////////////////////////////////////////////////////// +// End of file +//////////////////////////////////////////////////////////////////////////////// +//! @} diff --git a/drivers/power/stmp37xx/ddi_bc_internal.h b/drivers/power/stmp37xx/ddi_bc_internal.h new file mode 100644 index 000000000000..c1fb1b20f271 --- /dev/null +++ b/drivers/power/stmp37xx/ddi_bc_internal.h @@ -0,0 +1,52 @@ +/* + * 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_internal.h +//! \brief Internal header file for the Battery Charger device driver. +//! \date 06/2005 +//! +//! This file contains internal declarations for the Battery Charger device +//! driver. +//////////////////////////////////////////////////////////////////////////////// + +#ifndef _DDI_BC_INTERNAL_H +#define _DDI_BC_INTERNAL_H + +//////////////////////////////////////////////////////////////////////////////// +// Includes +//////////////////////////////////////////////////////////////////////////////// + +#include <mach/ddi_bc.h> +#include "ddi_bc_hw.h" +#include "ddi_bc_ramp.h" +#include "ddi_bc_sm.h" +#include "ddi_power_battery.h" + +//////////////////////////////////////////////////////////////////////////////// +// Externs +//////////////////////////////////////////////////////////////////////////////// + +extern bool g_ddi_bc_Configured; +extern ddi_bc_Cfg_t g_ddi_bc_Configuration; + +//////////////////////////////////////////////////////////////////////////////// +// End of file +//////////////////////////////////////////////////////////////////////////////// +#endif // _DDI_BC_H +//! @} 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 +//////////////////////////////////////////////////////////////////////////////// +//! @} diff --git a/drivers/power/stmp37xx/ddi_bc_ramp.h b/drivers/power/stmp37xx/ddi_bc_ramp.h new file mode 100644 index 000000000000..5111a5496fcf --- /dev/null +++ b/drivers/power/stmp37xx/ddi_bc_ramp.h @@ -0,0 +1,50 @@ +/* + * 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.h +//! \brief Internal header file for Battery Charger current ramp controller. +//! \date 06/2005 +//! +//! This file contains internal declarations for Battery current ramp +//! controller. +//////////////////////////////////////////////////////////////////////////////// + +#ifndef _DDI_BC_RAMP_H +#define _DDI_BC_RAMP_H + +//////////////////////////////////////////////////////////////////////////////// +// Prototypes +//////////////////////////////////////////////////////////////////////////////// + +extern void ddi_bc_RampReset(void); +extern uint16_t ddi_bc_RampSetTarget(uint16_t); +extern uint16_t ddi_bc_RampGetTarget(void); +extern uint16_t ddi_bc_RampSetLimit(uint16_t); +extern uint16_t ddi_bc_RampGetLimit(void); +extern void ddi_bc_RampUpdateAlarms(void); +extern int ddi_bc_RampGetDieTempAlarm(void); +extern int ddi_bc_RampGetBatteryTempAlarm(void); +extern int ddi_bc_RampGetAmbientTempAlarm(void); +extern ddi_bc_Status_t ddi_bc_RampStep(uint32_t); + +//////////////////////////////////////////////////////////////////////////////// +// End of file +//////////////////////////////////////////////////////////////////////////////// +#endif // _DDI_BC_H +//! @} 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 +//////////////////////////////////////////////////////////////////////////////// +//! @} diff --git a/drivers/power/stmp37xx/ddi_bc_sm.h b/drivers/power/stmp37xx/ddi_bc_sm.h new file mode 100644 index 000000000000..0e5fd8a1f144 --- /dev/null +++ b/drivers/power/stmp37xx/ddi_bc_sm.h @@ -0,0 +1,46 @@ +/* + * 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.h +//! \brief Header file for the Battery Charger state machine. +//! \date 06/2005 +//! +//! This file contains declarations for the Battery Charger state machine. +//////////////////////////////////////////////////////////////////////////////// + +#ifndef _DDI_BC_SM_H +#define _DDI_BC_SM_H + +//////////////////////////////////////////////////////////////////////////////// +// Externs +//////////////////////////////////////////////////////////////////////////////// + +//! The current state. + +extern ddi_bc_State_t g_ddi_bc_State; + +//! The state function table. + +extern ddi_bc_Status_t(*const (stateFunctionTable[])) (void); + +//////////////////////////////////////////////////////////////////////////////// +// End of file +//////////////////////////////////////////////////////////////////////////////// +#endif // _DDI_BC_H +//! @} diff --git a/drivers/power/stmp37xx/ddi_power_battery.c b/drivers/power/stmp37xx/ddi_power_battery.c new file mode 100644 index 000000000000..f9079fcce916 --- /dev/null +++ b/drivers/power/stmp37xx/ddi_power_battery.c @@ -0,0 +1,998 @@ +/* + * 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_power +//! @{ +// +// Copyright(C) 2005 SigmaTel, Inc. +// +//! \file ddi_power_battery.c +//! \brief Implementation file for the power driver battery charger. +//! +//////////////////////////////////////////////////////////////////////////////// +// Includes and external references +//////////////////////////////////////////////////////////////////////////////// +#include <linux/kernel.h> +#include <linux/errno.h> +#include <linux/delay.h> +#include <asm/processor.h> /* cpu_relax */ +#include <mach/hardware.h> +#include <mach/ddi_bc.h> +#include <mach/lradc.h> +#include <mach/regs-power.h> +#include <mach/regs-lradc.h> +#include <mach/lradc.h> +#include "ddi_bc_internal.h" +#include <mach/platform.h> + +//! \brief Base voltage to start battery calculations for LiIon +#define BATT_BRWNOUT_LIION_BASE_MV 2800 +//! \brief Constant to help with determining whether to round up or +//! not during calculation +#define BATT_BRWNOUT_LIION_CEILING_OFFSET_MV 39 +//! \brief Number of mV to add if rounding up in LiIon mode +#define BATT_BRWNOUT_LIION_LEVEL_STEP_MV 40 +//! \brief Constant value to be calculated by preprocessing +#define BATT_BRWNOUT_LIION_EQN_CONST \ + (BATT_BRWNOUT_LIION_BASE_MV - BATT_BRWNOUT_LIION_CEILING_OFFSET_MV) +//! \brief Base voltage to start battery calculations for Alkaline/NiMH +#define BATT_BRWNOUT_ALKAL_BASE_MV 800 +//! \brief Constant to help with determining whether to round up or +//! not during calculation +#define BATT_BRWNOUT_ALKAL_CEILING_OFFSET_MV 19 +//! \brief Number of mV to add if rounding up in Alkaline/NiMH mode +#define BATT_BRWNOUT_ALKAL_LEVEL_STEP_MV 20 +//! \brief Constant value to be calculated by preprocessing +#define BATT_BRWNOUT_ALKAL_EQN_CONST \ + (BATT_BRWNOUT_ALKAL_BASE_MV - BATT_BRWNOUT_ALKAL_CEILING_OFFSET_MV) + +#define GAIN_CORRECTION 1012 // 1.012 + +/* NOTE: the below define is different for 37xx and 378x */ +#define VBUSVALID_THRESH_4_30V 0x4 +#define LINREG_OFFSET_STEP_BELOW 0x2 +#define BP_POWER_BATTMONITOR_BATT_VAL 16 +#define BP_POWER_CHARGE_BATTCHRG_I 0 +#define BP_POWER_CHARGE_STOP_ILIMIT 8 + +//////////////////////////////////////////////////////////////////////////////// +// Globals & Variables +//////////////////////////////////////////////////////////////////////////////// + +// FIXME +/* We cant use VBUSVALID signal for VDD5V detection, since setting in + * USB driver POWER_DEBUG.VBUSVALIDPIOLOCK bit locks the POWER_STS.VBUSVALID to + * active state for all power states (even if the 5v went away). The + * POWER_CTRL.VBUSVALID_IRQ is also affected and it's impossible to get + * valid information about 5v presence. + */ +/* static ddi_power_5vDetection_t DetectionMethod = + DDI_POWER_5V_VDD5V_GT_VDDIO; */ +static ddi_power_5vDetection_t DetectionMethod = DDI_POWER_5V_VBUSVALID; + +//////////////////////////////////////////////////////////////////////////////// +// Code +//////////////////////////////////////////////////////////////////////////////// + +#if 0 +static void dump_regs(void) +{ + printk("HW_POWER_CHARGE 0x%08x\n", __raw_readl(REGS_POWER_BASE + HW_POWER_CHARGE)); + printk("HW_POWER_STS 0x%08x\n", __raw_readl(REGS_POWER_BASE + HW_POWER_STS)); + printk("HW_POWER_BATTMONITOR 0x%08x\n", __raw_readl(REGS_POWER_BASE + HW_POWER_BATTMONITOR)); +} +#endif + +//! This array maps bit numbers to current increments, as used in the register +//! fields HW_POWER_CHARGE.STOP_ILIMIT and HW_POWER_CHARGE.BATTCHRG_I. +static const uint16_t currentPerBit[] = { 10, 20, 50, 100, 200, 400 }; + +uint16_t ddi_power_convert_current_to_setting(uint16_t u16Current) +{ + int i; + uint16_t u16Mask; + uint16_t u16Setting = 0; + + // Scan across the bit field, adding in current increments. + u16Mask = (0x1 << 5); + + for (i = 5; (i >= 0) && (u16Current > 0); i--, u16Mask >>= 1) { + if (u16Current >= currentPerBit[i]) { + u16Current -= currentPerBit[i]; + u16Setting |= u16Mask; + } + } + + // Return the result. + return(u16Setting); +} + +//////////////////////////////////////////////////////////////////////////////// +//! See hw_power.h for details. +//////////////////////////////////////////////////////////////////////////////// +uint16_t ddi_power_convert_setting_to_current(uint16_t u16Setting) +{ + int i; + uint16_t u16Mask; + uint16_t u16Current = 0; + + // Scan across the bit field, adding in current increments. + u16Mask = (0x1 << 5); + + for (i = 5; i >= 0; i--, u16Mask >>= 1) { + if (u16Setting & u16Mask) u16Current += currentPerBit[i]; + } + + // Return the result. + return(u16Current); +} + +void ddi_power_Enable5vDetection(void) +{ + u32 val; + // Disable hardware power down when 5V is inserted or removed + stmp3xxx_clearl(BM_POWER_5VCTRL_PWDN_5VBRNOUT, REGS_POWER_BASE + HW_POWER_5VCTRL); + + /* + * Prepare the hardware for the detection method. We used to set + * and clear the VBUSVALID_5VDETECT bit, but that is also used for + * the DCDC 5V detection. It is sufficient to just check the status + * bits to see if 5V is present. + * + * Use VBUSVALID for DCDC 5V detection. The DCDC's detection is + * different than the USB/5V detection used to switch profiles. This + * is used to determine when a handoff should occur. + */ + stmp3xxx_setl(BM_POWER_5VCTRL_VBUSVALID_5VDETECT, REGS_POWER_BASE + HW_POWER_5VCTRL); + + // Set 5V detection threshold to 4.3V for VBUSVALID. + stmp3xxx_setl( + BF(VBUSVALID_THRESH_4_30V, POWER_5VCTRL_VBUSVALID_TRSH), REGS_POWER_BASE + HW_POWER_5VCTRL); + + // gotta set LINREG_OFFSET to STEP_BELOW according to manual + val = __raw_readl(REGS_POWER_BASE + HW_POWER_VDDIOCTRL); + val &= ~(BM_POWER_VDDIOCTRL_LINREG_OFFSET); + val |= BF(LINREG_OFFSET_STEP_BELOW, POWER_VDDIOCTRL_LINREG_OFFSET); + __raw_writel(val, REGS_POWER_BASE + HW_POWER_VDDIOCTRL); + + val = __raw_readl(REGS_POWER_BASE + HW_POWER_VDDACTRL); + val &= ~(BM_POWER_VDDACTRL_LINREG_OFFSET); + val |= BF(LINREG_OFFSET_STEP_BELOW, POWER_VDDACTRL_LINREG_OFFSET); + __raw_writel(val, REGS_POWER_BASE + HW_POWER_VDDACTRL); + + val = __raw_readl(REGS_POWER_BASE + HW_POWER_VDDDCTRL); + val &= ~(BM_POWER_VDDDCTRL_LINREG_OFFSET); + val |= BF(LINREG_OFFSET_STEP_BELOW, POWER_VDDDCTRL_LINREG_OFFSET); + __raw_writel(val, REGS_POWER_BASE + HW_POWER_VDDDCTRL); + + /* Clear vbusvalid interrupt flag */ +// stmp3xxx_clearl(BM_POWER_CTRL_VBUSVALID_IRQ, REGS_POWER_BASE + HW_POWER_CTRL); + stmp3xxx_clearl(BM_POWER_CTRL_VDD5V_GT_VDDIO_IRQ, REGS_POWER_BASE + HW_POWER_CTRL); + /* enable vbusvalid irq */ +// stmp3xxx_setl(BM_POWER_CTRL_ENIRQ_VBUS_VALID, REGS_POWER_BASE + HW_POWER_CTRL); + stmp3xxx_setl(BM_POWER_CTRL_ENIRQ_VDD5V_GT_VDDIO, REGS_POWER_BASE + HW_POWER_CTRL); +} + +/* + * This function prepares the hardware for a 5V-to-battery handoff. It assumes + * the current configuration is using 5V as the power source. The 5V + * interrupt will be set up for a 5V removal. + */ +void ddi_power_enable_5v_to_battery_handoff(void) +{ + /* Clear vbusvalid interrupt flag */ +// stmp3xxx_clearl(BM_POWER_CTRL_VBUSVALID_IRQ, REGS_POWER_BASE + HW_POWER_CTRL); + stmp3xxx_clearl(BM_POWER_CTRL_VDD5V_GT_VDDIO_IRQ, REGS_POWER_BASE + HW_POWER_CTRL); + + /* detect 5v unplug */ +// stmp3xxx_clearl(BM_POWER_CTRL_POLARITY_VBUSVALID, REGS_POWER_BASE + HW_POWER_CTRL); + stmp3xxx_clearl(BM_POWER_CTRL_POLARITY_VDD5V_GT_VDDIO, REGS_POWER_BASE + HW_POWER_CTRL); + + // Enable automatic transition to DCDC + stmp3xxx_setl(BM_POWER_5VCTRL_DCDC_XFER, REGS_POWER_BASE + HW_POWER_5VCTRL); +} + +/* + * This function will handle all the power rail transitions necesarry to power + * the chip from the battery when it was previously powered from the 5V power + * source. + */ +void ddi_power_execute_5v_to_battery_handoff(void) +{ + u32 val; + // VDDD has different configurations depending on the battery type + // and battery level. + + // For LiIon battery, we will use the DCDC to power VDDD. + // Use LinReg offset for DCDC mode. + // Turn on the VDDD DCDC output and turn off the VDDD LinReg output. + // Make sure stepping is enabled when using DCDC. + val = __raw_readl(REGS_POWER_BASE + HW_POWER_VDDDCTRL); + val &= ~(BM_POWER_VDDDCTRL_DISABLE_FET | BM_POWER_VDDDCTRL_ENABLE_LINREG | + BM_POWER_VDDDCTRL_DISABLE_STEPPING | BM_POWER_VDDDCTRL_LINREG_OFFSET); + val |= BF(LINREG_OFFSET_STEP_BELOW, POWER_VDDDCTRL_LINREG_OFFSET); + __raw_writel(val, REGS_POWER_BASE + HW_POWER_VDDDCTRL); + + // Power VDDA and VDDIO from the DCDC. + // Use LinReg offset for DCDC mode. + val = __raw_readl(REGS_POWER_BASE + HW_POWER_VDDACTRL); + val &= ~(BM_POWER_VDDACTRL_LINREG_OFFSET); + val |= BF(LINREG_OFFSET_STEP_BELOW, POWER_VDDACTRL_LINREG_OFFSET); + __raw_writel(val, REGS_POWER_BASE + HW_POWER_VDDACTRL); + + // Turn on the VDDA DCDC converter output and turn off LinReg output. + // Make sure stepping is enabled when using DCDC. + val = __raw_readl(REGS_POWER_BASE + HW_POWER_VDDACTRL); + val &= ~(BM_POWER_VDDACTRL_DISABLE_FET | + BM_POWER_VDDACTRL_ENABLE_LINREG | + BM_POWER_VDDACTRL_DISABLE_STEPPING); + __raw_writel(val, REGS_POWER_BASE + HW_POWER_VDDACTRL); + + // Use LinReg offset for DCDC mode. + val = __raw_readl(REGS_POWER_BASE + HW_POWER_VDDIOCTRL); + val &= ~(BM_POWER_VDDIOCTRL_LINREG_OFFSET); + val |= BF(LINREG_OFFSET_STEP_BELOW, POWER_VDDIOCTRL_LINREG_OFFSET); + __raw_writel(val, REGS_POWER_BASE + HW_POWER_VDDIOCTRL); + // Turn on the VDDIO DCDC output and turn on the LinReg output. + val = __raw_readl(REGS_POWER_BASE + HW_POWER_VDDIOCTRL); + val &= ~(BM_POWER_VDDIOCTRL_DISABLE_FET); + __raw_writel(val, REGS_POWER_BASE + HW_POWER_VDDIOCTRL); + + stmp3xxx_clearl(BM_POWER_5VCTRL_ILIMIT_EQ_ZERO, REGS_POWER_BASE + HW_POWER_5VCTRL); + // Make sure stepping is enabled when using DCDC. + val = __raw_readl(REGS_POWER_BASE + HW_POWER_VDDIOCTRL); + val &= ~(BM_POWER_VDDIOCTRL_DISABLE_STEPPING); + __raw_writel(val, REGS_POWER_BASE + HW_POWER_VDDIOCTRL); +} + +/* + * This function sets up battery-to-5V handoff. The power switch from + * battery to 5V is automatic. This funtion enables the 5V present detection + * such that the 5V interrupt can be generated if it is enabled. (The interrupt + * handler can inform software the 5V present event.) To deal with noise or + * a high current, this function enables DCDC1/2 based on the battery mode. + */ +void ddi_power_enable_battery_to_5v_handoff(void) +{ + /* Clear vbusvalid interrupt flag */ +// stmp3xxx_clearl(BM_POWER_CTRL_VBUSVALID_IRQ, REGS_POWER_BASE + HW_POWER_CTRL); + stmp3xxx_clearl(BM_POWER_CTRL_VDD5V_GT_VDDIO_IRQ, REGS_POWER_BASE + HW_POWER_CTRL); + + /* detect 5v plug-in */ +// stmp3xxx_setl(BM_POWER_CTRL_POLARITY_VBUSVALID, REGS_POWER_BASE + HW_POWER_CTRL); + stmp3xxx_setl(BM_POWER_CTRL_POLARITY_VDD5V_GT_VDDIO, REGS_POWER_BASE + HW_POWER_CTRL); + + // Force current from 5V to be zero by disabling its entry source. + stmp3xxx_setl(BM_POWER_5VCTRL_ILIMIT_EQ_ZERO, REGS_POWER_BASE + HW_POWER_5VCTRL); + + // Allow DCDC be to active when 5V is present. + stmp3xxx_setl(BM_POWER_5VCTRL_ENABLE_DCDC, REGS_POWER_BASE + HW_POWER_5VCTRL); +} + +/* This function handles the transitions on each of the power rails necessary + * to power the chip from the 5V power supply when it was previously powered + * from the battery power supply. + */ +void ddi_power_execute_battery_to_5v_handoff(void) +{ + u32 val; + // Disable the DCDC during 5V connections. + stmp3xxx_clearl(BM_POWER_5VCTRL_ENABLE_DCDC, REGS_POWER_BASE + HW_POWER_5VCTRL); + + // Power the VDDD/VDDA/VDDIO rail from the linear regulator. The DCDC + // is ready to automatically power the chip when 5V is removed. + // Use this configuration when powering from 5V + + // Use LinReg offset for LinReg mode + val = __raw_readl(REGS_POWER_BASE + HW_POWER_VDDDCTRL); + val &= ~BM_POWER_VDDDCTRL_LINREG_OFFSET; + val |= BF(LINREG_OFFSET_STEP_BELOW, POWER_VDDDCTRL_LINREG_OFFSET); + __raw_writel(val, REGS_POWER_BASE + HW_POWER_VDDDCTRL); + + // Turn on the VDDD LinReg and turn on the VDDD DCDC output. The + // ENABLE_DCDC must be cleared to avoid LinReg and DCDC conflict. + val = __raw_readl(REGS_POWER_BASE + HW_POWER_VDDDCTRL); + val &= ~(BM_POWER_VDDDCTRL_ENABLE_LINREG | BM_POWER_VDDDCTRL_DISABLE_FET); + val |= BM_POWER_VDDDCTRL_ENABLE_LINREG; + __raw_writel(val, REGS_POWER_BASE + HW_POWER_VDDDCTRL); + // Make sure stepping is disabled when using linear regulators + val = __raw_readl(REGS_POWER_BASE + HW_POWER_VDDDCTRL); + val &= ~(BM_POWER_VDDDCTRL_DISABLE_STEPPING); + val |= BM_POWER_VDDDCTRL_DISABLE_STEPPING; + __raw_writel(val, REGS_POWER_BASE + HW_POWER_VDDDCTRL); + + // Use LinReg offset for LinReg mode + val = __raw_readl(REGS_POWER_BASE + HW_POWER_VDDACTRL); + val &= ~(BM_POWER_VDDACTRL_LINREG_OFFSET); + val |= BF(LINREG_OFFSET_STEP_BELOW, POWER_VDDACTRL_LINREG_OFFSET); + __raw_writel(val, REGS_POWER_BASE + HW_POWER_VDDACTRL); + + // Turn on the VDDA LinReg output and prepare the DCDC for transfer. + // ENABLE_DCDC must be clear to avoid DCDC and LinReg conflict. + // Make sure stepping is disabled when using linear regulators + val = __raw_readl(REGS_POWER_BASE + HW_POWER_VDDACTRL); + val &= ~(BM_POWER_VDDACTRL_ENABLE_LINREG | BM_POWER_VDDACTRL_DISABLE_FET | + BM_POWER_VDDACTRL_DISABLE_STEPPING); + val |= BM_POWER_VDDACTRL_ENABLE_LINREG | BM_POWER_VDDACTRL_DISABLE_STEPPING; + __raw_writel(val, REGS_POWER_BASE + HW_POWER_VDDACTRL); + + // Use LinReg offset for LinReg mode. + val = __raw_readl(REGS_POWER_BASE + HW_POWER_VDDIOCTRL); + val &= ~(BM_POWER_VDDIOCTRL_LINREG_OFFSET); + val |= BF(LINREG_OFFSET_STEP_BELOW, POWER_VDDIOCTRL_LINREG_OFFSET); + __raw_writel(val, REGS_POWER_BASE + HW_POWER_VDDIOCTRL); + + // Turn on the VDDIO LinReg output and prepare the VDDIO DCDC output. + // ENABLE_DCDC must be cleared to prevent DCDC and LinReg conflict. + val = __raw_readl(REGS_POWER_BASE + HW_POWER_VDDIOCTRL); + val &= ~(BM_POWER_VDDIOCTRL_DISABLE_FET); + __raw_writel(val, REGS_POWER_BASE + HW_POWER_VDDIOCTRL); + + stmp3xxx_clearl(BM_POWER_5VCTRL_ILIMIT_EQ_ZERO, REGS_POWER_BASE + HW_POWER_5VCTRL); + // Make sure stepping is disabled when using DCDC. + val = __raw_readl(REGS_POWER_BASE + HW_POWER_VDDIOCTRL); + val |= BM_POWER_VDDIOCTRL_DISABLE_STEPPING; + __raw_writel(val, REGS_POWER_BASE + HW_POWER_VDDIOCTRL); +} + +void ddi_power_init_handoff(void) +{ + u32 val; + /* + * The following settings give optimal power supply capability and + * efficiency. Extreme loads will need HALF_FETS cleared and + * possibly DOUBLE_FETS set. The below setting are probably also + * the best for alkaline mode also but more characterization is + * needed to know for sure. + */ + // Increase the RCSCALE_THRESHOLD + stmp3xxx_setl(BM_POWER_LOOPCTRL_RCSCALE_THRESH, + REGS_POWER_BASE + HW_POWER_LOOPCTRL); + // Increase the RCSCALE level for quick DCDC response to dynamic load + stmp3xxx_setl(BF(3, POWER_LOOPCTRL_EN_RCSCALE), + REGS_POWER_BASE + HW_POWER_LOOPCTRL); // 8x + + // Enable half fets for increase efficiency. + stmp3xxx_setl(BM_POWER_MINPWR_HALF_FETS, REGS_POWER_BASE + HW_POWER_MINPWR); + + // enable 5v presence detection + ddi_power_Enable5vDetection(); + + if (ddi_power_Get5vPresentFlag()) + /* It's 5V mode, enable 5V-to-battery handoff */ + ddi_power_enable_5v_to_battery_handoff(); + else + /* It's battery mode, enable battery-to-5V handoff */ + ddi_power_enable_battery_to_5v_handoff(); + + // Finally enable the battery adjust + val = __raw_readl(REGS_POWER_BASE + HW_POWER_BATTMONITOR); + val |= BM_POWER_BATTMONITOR_EN_BATADJ; + __raw_writel(val, REGS_POWER_BASE + HW_POWER_BATTMONITOR); +} + +int ddi_power_init_battery(void) +{ + int ret; + + // Init LRADC channel 7 + ret = hw_lradc_init_ladder(BATTERY_VOLTAGE_CH, + LRADC_DELAY_TRIGGER_BATTERY, + 200); + if (ret) { + printk(KERN_ERR "%s: hw_lradc_init_ladder failed\n", __func__); + return ret; + } + + stmp3xxx_setl(BM_LRADC_CONVERSION_AUTOMATIC, REGS_LRADC_BASE + HW_LRADC_CONVERSION); + + // Set li-ion mode + stmp3xxx_setl(BF(2, LRADC_CONVERSION_SCALE_FACTOR), REGS_LRADC_BASE + HW_LRADC_CONVERSION); + + // Turn off divide-by-two - we already have a divide-by-four + // as part of the hardware + stmp3xxx_clearl( + BF(1 << BATTERY_VOLTAGE_CH, LRADC_CTRL2_DIVIDE_BY_TWO), REGS_LRADC_BASE + HW_LRADC_CTRL2); + + stmp3xxx_setl(BM_POWER_CHARGE_ENABLE_FAULT_DETECT, REGS_POWER_BASE + HW_POWER_CHARGE); + + // kick off the trigger + hw_lradc_set_delay_trigger_kick(LRADC_DELAY_TRIGGER_BATTERY, 1); + + /* prepare handoff */ + ddi_power_init_handoff(); + + return 0; +} + +/* + * Use the the lradc7 channel dedicated for battery voltage measurement to + * get the die temperature from on-chip sensor. + */ +uint16_t MeasureInternalDieTemperature(void) +{ + uint32_t ch8Value, ch9Value; + + /* power up internal tep sensor block */ + stmp3xxx_clearl(BM_LRADC_CTRL2_TEMPSENSE_PWD, REGS_LRADC_BASE + HW_LRADC_CTRL2); + + /* mux to the lradc 8th temp channel */ + stmp3xxx_clearl(BF(0xF, LRADC_CTRL4_LRADC7SELECT), REGS_LRADC_BASE + HW_LRADC_CTRL4); + stmp3xxx_setl(BF(8, LRADC_CTRL4_LRADC7SELECT), REGS_LRADC_BASE + HW_LRADC_CTRL4); + + /* Clear the interrupt flag */ + stmp3xxx_clearl(BM_LRADC_CTRL1_LRADC7_IRQ, REGS_LRADC_BASE + HW_LRADC_CTRL1); + stmp3xxx_setl(BF(1 << BATTERY_VOLTAGE_CH, LRADC_CTRL0_SCHEDULE), REGS_LRADC_BASE + HW_LRADC_CTRL0); + // Wait for conversion complete + while (!(__raw_readl(REGS_LRADC_BASE + HW_LRADC_CTRL1) & BM_LRADC_CTRL1_LRADC7_IRQ)) + cpu_relax(); + /* Clear the interrupt flag again */ + stmp3xxx_clearl(BM_LRADC_CTRL1_LRADC7_IRQ, REGS_LRADC_BASE + HW_LRADC_CTRL1); + // read temperature value and clr lradc + ch8Value = __raw_readl(REGS_LRADC_BASE + HW_LRADC_CHn(BATTERY_VOLTAGE_CH)) & BM_LRADC_CHn_VALUE; + stmp3xxx_clearl(BM_LRADC_CHn_VALUE, REGS_LRADC_BASE + HW_LRADC_CHn(BATTERY_VOLTAGE_CH)); + + /* mux to the lradc 9th temp channel */ + stmp3xxx_clearl(BF(0xF, LRADC_CTRL4_LRADC7SELECT), REGS_LRADC_BASE + HW_LRADC_CTRL4); + stmp3xxx_setl(BF(9, LRADC_CTRL4_LRADC7SELECT), REGS_LRADC_BASE + HW_LRADC_CTRL4); + + /* Clear the interrupt flag */ + stmp3xxx_clearl(BM_LRADC_CTRL1_LRADC7_IRQ, REGS_LRADC_BASE + HW_LRADC_CTRL1); + stmp3xxx_setl(BF(1 << BATTERY_VOLTAGE_CH, LRADC_CTRL0_SCHEDULE), REGS_LRADC_BASE + HW_LRADC_CTRL0); + // Wait for conversion complete + while (!(__raw_readl(REGS_LRADC_BASE + HW_LRADC_CTRL1) & BM_LRADC_CTRL1_LRADC7_IRQ)) + cpu_relax(); + /* Clear the interrupt flag */ + stmp3xxx_clearl(BM_LRADC_CTRL1_LRADC7_IRQ, REGS_LRADC_BASE + HW_LRADC_CTRL1); + // read temperature value + ch9Value = __raw_readl(REGS_LRADC_BASE + HW_LRADC_CHn(BATTERY_VOLTAGE_CH)) & BM_LRADC_CHn_VALUE; + stmp3xxx_clearl(BM_LRADC_CHn_VALUE, REGS_LRADC_BASE + HW_LRADC_CHn(BATTERY_VOLTAGE_CH)); + + /* power down temp sensor block */ + stmp3xxx_setl(BM_LRADC_CTRL2_TEMPSENSE_PWD, REGS_LRADC_BASE + HW_LRADC_CTRL2); + + /* mux back to the lradc 7th battery voltage channel */ + stmp3xxx_clearl(BF(0xF, LRADC_CTRL4_LRADC7SELECT), REGS_LRADC_BASE + HW_LRADC_CTRL4); + stmp3xxx_setl(BF(7, LRADC_CTRL4_LRADC7SELECT), REGS_LRADC_BASE + HW_LRADC_CTRL4); + + /* Clear the interrupt flag */ + stmp3xxx_clearl(BM_LRADC_CTRL1_LRADC7_IRQ, REGS_LRADC_BASE + HW_LRADC_CTRL1); + stmp3xxx_setl(BF(1 << BATTERY_VOLTAGE_CH, LRADC_CTRL0_SCHEDULE), REGS_LRADC_BASE + HW_LRADC_CTRL0); + // Wait for conversion complete + while (!(__raw_readl(REGS_LRADC_BASE + HW_LRADC_CTRL1) & BM_LRADC_CTRL1_LRADC7_IRQ)) + cpu_relax(); + /* Clear the interrupt flag */ + stmp3xxx_clearl(BM_LRADC_CTRL1_LRADC7_IRQ, REGS_LRADC_BASE + HW_LRADC_CTRL1); + + return (uint16_t)((ch9Value-ch8Value)*GAIN_CORRECTION/4000); +} + + +//////////////////////////////////////////////////////////////////////////////// +//! Name: ddi_power_GetBatteryMode +//! +//! \brief +//////////////////////////////////////////////////////////////////////////////// +ddi_power_BatteryMode_t ddi_power_GetBatteryMode(void) +{ +#if 0 + return (__raw_readl(REGS_POWER_BASE + HW_POWER_STS) & BM_POWER_STS_MODE) ? + DDI_POWER_BATT_MODE_ALKALINE_NIMH : + DDI_POWER_BATT_MODE_LIION; +#endif + return DDI_POWER_BATT_MODE_LIION; +} + +//////////////////////////////////////////////////////////////////////////////// +//! Name: ddi_power_GetBatteryChargerEnabled +//! +//! \brief +//////////////////////////////////////////////////////////////////////////////// +bool ddi_power_GetBatteryChargerEnabled(void) +{ +#if 0 + return (__raw_readl(REGS_POWER_BASE + HW_POWER_STS) & BM_POWER_STS_BATT_CHRG_PRESENT) ? 1 : 0; +#endif + return 1; +} + +//////////////////////////////////////////////////////////////////////////////// +//! +//! \brief Report if the charger hardware power is on. +//! +//! \fntype Function +//! +//! This function reports if the charger hardware power is on. +//! +//! \retval Zero if the charger hardware is not powered. Non-zero otherwise. +//! +//! Note that the bit we're looking at is named PWD_BATTCHRG. The "PWD" +//! stands for "power down". Thus, when the bit is set, the battery charger +//! hardware is POWERED DOWN. +//////////////////////////////////////////////////////////////////////////////// +bool ddi_power_GetChargerPowered(void) +{ + return (__raw_readl(REGS_POWER_BASE + HW_POWER_CHARGE) & BM_POWER_CHARGE_PWD_BATTCHRG) ? 0 : 1; +} + +//////////////////////////////////////////////////////////////////////////////// +//! +//! \brief Turn the charging hardware on or off. +//! +//! \fntype Function +//! +//! This function turns the charging hardware on or off. +//! +//! \param[in] on Indicates whether the charging hardware should be on or off. +//! +//! Note that the bit we're looking at is named PWD_BATTCHRG. The "PWD" +//! stands for "power down". Thus, when the bit is set, the battery charger +//! hardware is POWERED DOWN. +//////////////////////////////////////////////////////////////////////////////// +void ddi_power_SetChargerPowered(bool bPowerOn) +{ + // Hit the battery charge power switch. + if (bPowerOn) { + stmp3xxx_clearl(BM_POWER_CHARGE_PWD_BATTCHRG, REGS_POWER_BASE + HW_POWER_CHARGE); + stmp3xxx_clearl(BM_POWER_5VCTRL_PWD_CHARGE_4P2, REGS_POWER_BASE + HW_POWER_5VCTRL); + } else { + stmp3xxx_setl(BM_POWER_CHARGE_PWD_BATTCHRG, REGS_POWER_BASE + HW_POWER_CHARGE); + stmp3xxx_setl(BM_POWER_5VCTRL_PWD_CHARGE_4P2, REGS_POWER_BASE + HW_POWER_5VCTRL); + } + +//#ifdef CONFIG_POWER_SUPPLY_DEBUG +#if 0 + printk("Battery charger: charger %s\n", bPowerOn ? "ON!" : "OFF"); + dump_regs(); +#endif +} + +//////////////////////////////////////////////////////////////////////////////// +//! +//! \brief Reports if the charging current has fallen below the threshold. +//! +//! \fntype Function +//! +//! This function reports if the charging current that the battery is accepting +//! has fallen below the threshold. +//! +//! Note that this bit is regarded by the hardware guys as very slightly +//! unreliable. They recommend that you don't believe a value of zero until +//! you've sampled it twice. +//! +//! \retval Zero if the battery is accepting less current than indicated by the +//! charging threshold. Non-zero otherwise. +//! +//////////////////////////////////////////////////////////////////////////////// +int ddi_power_GetChargeStatus(void) +{ + return (__raw_readl(REGS_POWER_BASE + HW_POWER_STS) & BM_POWER_STS_CHRGSTS) ? 1 : 0; +} + +//////////////////////////////////////////////////////////////////////////////// +// Battery Voltage +//////////////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////////////// +//! +//! \brief Report the voltage across the battery. +//! +//! \fntype Function +//! +//! This function reports the voltage across the battery. Should return a +//! value in range ~3000 - 4200 mV. +//! +//! \retval The voltage across the battery, in mV. +//! +//////////////////////////////////////////////////////////////////////////////// + +//! \brief Constant value for 8mV steps used in battery translation +#define BATT_VOLTAGE_8_MV 8 + +uint16_t ddi_power_GetBattery(void) +{ + uint32_t u16BattVolt; + + // Get the raw result of battery measurement + u16BattVolt = __raw_readl(REGS_POWER_BASE + HW_POWER_BATTMONITOR); + u16BattVolt &= BM_POWER_BATTMONITOR_BATT_VAL; + u16BattVolt >>= BP_POWER_BATTMONITOR_BATT_VAL; + + // Adjust for 8-mV LSB resolution and return + u16BattVolt *= BATT_VOLTAGE_8_MV; + +//#ifdef CONFIG_POWER_SUPPLY_DEBUG +#if 0 + printk("Battery charger: %u mV\n", u16BattVolt); +#endif + + return u16BattVolt; +} + +#if 0 +//////////////////////////////////////////////////////////////////////////////// +//! +//! \brief Report the voltage across the battery. +//! +//! \fntype Function +//! +//! This function reports the voltage across the battery. +//! +//! \retval The voltage across the battery, in mV. +//! +//////////////////////////////////////////////////////////////////////////////// +uint16_t ddi_power_GetBatteryBrownout(void) +{ + uint32_t u16BatteryBrownoutLevel; + + // Get battery brownout level + u16BatteryBrownoutLevel = __raw_readl(REGS_POWER_BASE + HW_POWER_BATTMONITOR); + u16BatteryBrownoutLevel &= BM_POWER_BATTMONITOR_BRWNOUT_LVL; + u16BatteryBrownoutLevel >>= BP_POWER_BATTMONITOR_BRWNOUT_LVL; + + // Calculate battery brownout level + switch (ddi_power_GetBatteryMode()) { + case DDI_POWER_BATT_MODE_LIION: + u16BatteryBrownoutLevel *= BATT_BRWNOUT_LIION_LEVEL_STEP_MV; + u16BatteryBrownoutLevel += BATT_BRWNOUT_LIION_BASE_MV; + break; + case DDI_POWER_BATT_MODE_ALKALINE_NIMH: + u16BatteryBrownoutLevel *= BATT_BRWNOUT_ALKAL_LEVEL_STEP_MV; + u16BatteryBrownoutLevel += BATT_BRWNOUT_ALKAL_BASE_MV; + break; + default: + u16BatteryBrownoutLevel = 0; + break; + } + return u16BatteryBrownoutLevel; +} + +//////////////////////////////////////////////////////////////////////////////// +//! +//! \brief Set battery brownout level +//! +//! \fntype Reentrant Function +//! +//! This function sets the battery brownout level in millivolt. It transforms the +//! input brownout value from millivolts to the hardware register bit field value +//! taking the ceiling value in the calculation. +//! +//! \param[in] u16BattBrownout_mV Battery battery brownout level in mV +//! +//! \return SUCCESS +//! +//////////////////////////////////////////////////////////////////////////////// +int ddi_power_SetBatteryBrownout(uint16_t u16BattBrownout_mV) +{ + int16_t i16BrownoutLevel; + int ret = 0; + + // Calculate battery brownout level + switch (ddi_power_GetBatteryMode()) { + case DDI_POWER_BATT_MODE_LIION: + i16BrownoutLevel = u16BattBrownout_mV - + BATT_BRWNOUT_LIION_EQN_CONST; + i16BrownoutLevel /= BATT_BRWNOUT_LIION_LEVEL_STEP_MV; + break; + case DDI_POWER_BATT_MODE_ALKALINE_NIMH: + i16BrownoutLevel = u16BattBrownout_mV - + BATT_BRWNOUT_ALKAL_EQN_CONST; + i16BrownoutLevel /= BATT_BRWNOUT_ALKAL_LEVEL_STEP_MV; + break; + default: + return -EINVAL; + } + + // Do a check to make sure nothing went wrong. + if (i16BrownoutLevel <= 0x0f) { + //Write the battery brownout level + stmp3xxx_setl( + BF(i16BrownoutLevel, POWER_BATTMONITOR_BRWNOUT_LVL), REGS_POWER_BASE + HW_POWER_BATTMONITOR); + } else + ret = -EINVAL; + + return ret; +} +#endif + +//////////////////////////////////////////////////////////////////////////////// +// Currents +//////////////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////////////// +//! Name: ddi_power_SetBiasCurrentSource +//! +//! \brief +//////////////////////////////////////////////////////////////////////////////// +int ddi_power_SetBiasCurrentSource(ddi_power_BiasCurrentSource_t eSource) +{ + switch (eSource) { + case DDI_POWER_INTERNAL_BIAS_CURRENT: + stmp3xxx_setl(BM_POWER_CHARGE_USE_EXTERN_R, REGS_POWER_BASE + HW_POWER_CHARGE); + break; + case DDI_POWER_EXTERNAL_BIAS_CURRENT: + stmp3xxx_clearl(BM_POWER_CHARGE_USE_EXTERN_R, REGS_POWER_BASE + HW_POWER_CHARGE); + break; + default: + return -EINVAL; + } + + return 0; +} + +//////////////////////////////////////////////////////////////////////////////// +//! Name: ddi_power_GetBiasCurrentSource +//! +//! \brief +//////////////////////////////////////////////////////////////////////////////// +ddi_power_BiasCurrentSource_t ddi_power_GetBiasCurrentSource(void) +{ + return (__raw_readl(REGS_POWER_BASE + HW_POWER_CHARGE) & BM_POWER_CHARGE_USE_EXTERN_R) ? + DDI_POWER_INTERNAL_BIAS_CURRENT : + DDI_POWER_EXTERNAL_BIAS_CURRENT; +} + +//////////////////////////////////////////////////////////////////////////////// +//! Name: ddi_power_SetMaxBatteryChargeCurrent +//! +//! \brief +//////////////////////////////////////////////////////////////////////////////// +uint16_t ddi_power_SetMaxBatteryChargeCurrent(uint16_t u16MaxCur) +{ + uint32_t u16OldSetting; + uint32_t u16NewSetting; + uint32_t u16ToggleMask; + + // Get the old setting. + u16OldSetting = (__raw_readl(REGS_POWER_BASE + HW_POWER_CHARGE) & BM_POWER_CHARGE_BATTCHRG_I) >> + BP_POWER_CHARGE_BATTCHRG_I; + + // Convert the new threshold into a setting. + u16NewSetting = ddi_power_convert_current_to_setting(u16MaxCur); + + // Write to the toggle register. + u16ToggleMask = __raw_readl(REGS_POWER_BASE + HW_POWER_CHARGE); + u16ToggleMask &= ~BM_POWER_CHARGE_BATTCHRG_I; + u16ToggleMask |= u16NewSetting << BP_POWER_CHARGE_BATTCHRG_I; + + __raw_writel(u16ToggleMask, REGS_POWER_BASE + HW_POWER_CHARGE); + + // Tell the caller what current we're set at now. + return ddi_power_convert_setting_to_current(u16NewSetting); +} + +//////////////////////////////////////////////////////////////////////////////// +//! Name: ddi_power_GetMaxBatteryChargeCurrent +//! +//! \brief +//////////////////////////////////////////////////////////////////////////////// +uint16_t ddi_power_GetMaxBatteryChargeCurrent(void) +{ + uint32_t u8Bits; + + // Get the raw data from register + u8Bits = (__raw_readl(REGS_POWER_BASE + HW_POWER_CHARGE) & BM_POWER_CHARGE_BATTCHRG_I) >> + BP_POWER_CHARGE_BATTCHRG_I; + + // Translate raw data to current (in mA) and return it + return ddi_power_convert_setting_to_current(u8Bits); +} + +//////////////////////////////////////////////////////////////////////////////// +//! Name: ddi_power_GetMaxChargeCurrent +//! +//! \brief +//////////////////////////////////////////////////////////////////////////////// +uint16_t ddi_power_SetBatteryChargeCurrentThreshold(uint16_t u16Thresh) +{ + uint32_t u16OldSetting; + uint32_t u16NewSetting; + uint32_t u16ToggleMask; + + //------------------------------------------------------------------- + // See ddi_power_SetMaxBatteryChargeCurrent for an explanation of + // why we're using the toggle register here. + // + // Since this function doesn't have any major hardware effect, + // we could use the usual macros for writing to this bit field. But, + // for the sake of parallel construction and any potentially odd + // effects on the status bit, we use the toggle register in the same + // way as ddi_bc_hwSetMaxCurrent. + //------------------------------------------------------------------- + + //------------------------------------------------------------------- + // The threshold hardware can't express as large a range as the max + // current setting, but we can use the same functions as long as we + // add an extra check here. + // + // Thresholds larger than 180mA can't be expressed. + //------------------------------------------------------------------- + + if (u16Thresh > 180) + u16Thresh = 180; + + //////////////////////////////////////// + // Create the mask + //////////////////////////////////////// + + // Get the old setting. + u16OldSetting = (__raw_readl(REGS_POWER_BASE + HW_POWER_CHARGE) & BM_POWER_CHARGE_STOP_ILIMIT) >> + BP_POWER_CHARGE_STOP_ILIMIT; + + // Convert the new threshold into a setting. + u16NewSetting = ddi_power_convert_current_to_setting(u16Thresh); + + // Compute the toggle mask. + u16ToggleMask = u16OldSetting ^ u16NewSetting; + + ///////////////////////////////////////// + // Write to the register + ///////////////////////////////////////// + + // Write to the toggle register. + u16ToggleMask = __raw_readl(REGS_POWER_BASE + HW_POWER_CHARGE); + u16ToggleMask &= ~BM_POWER_CHARGE_STOP_ILIMIT; + u16ToggleMask |= u16NewSetting << BP_POWER_CHARGE_STOP_ILIMIT; + + // Tell the caller what current we're set at now. + return ddi_power_convert_setting_to_current(u16NewSetting); +} + +//////////////////////////////////////////////////////////////////////////////// +//! Name: ddi_power_GetBatteryChargeCurrentThreshold +//! +//! \brief +//////////////////////////////////////////////////////////////////////////////// +uint16_t ddi_power_GetBatteryChargeCurrentThreshold(void) +{ + uint32_t u16Threshold; + + u16Threshold = (__raw_readl(REGS_POWER_BASE + HW_POWER_CHARGE) & BM_POWER_CHARGE_STOP_ILIMIT) >> + BP_POWER_CHARGE_STOP_ILIMIT; + + return ddi_power_convert_setting_to_current(u16Threshold); +} + +//////////////////////////////////////////////////////////////////////////////// +// Conversion +//////////////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////////////// +//! +//! \brief Compute the actual current expressible in the hardware. +//! +//! \fntype Function +//! +//! Given a desired current, this function computes the actual current +//! expressible in the hardware. +//! +//! Note that the hardware has a minimum resolution of 10mA and a maximum +//! expressible value of 780mA (see the data sheet for details). If the given +//! current cannot be expressed exactly, then the largest expressible smaller +//! value will be used. +//! +//! \param[in] u16Current The current of interest. +//! +//! \retval The corresponding current in mA. +//! +//////////////////////////////////////////////////////////////////////////////// +uint16_t ddi_power_ExpressibleCurrent(uint16_t u16Current) +{ + return ddi_power_convert_setting_to_current( + ddi_power_convert_current_to_setting(u16Current)); +} + +//////////////////////////////////////////////////////////////////////////////// +//! Name: ddi_power_Get5VPresent +//! +//! \brief +//////////////////////////////////////////////////////////////////////////////// +bool ddi_power_Get5vPresentFlag(void) +{ + switch (DetectionMethod) { + case DDI_POWER_5V_VBUSVALID: + // Check VBUSVALID for 5V present + return ((__raw_readl(REGS_POWER_BASE + HW_POWER_STS) & BM_POWER_STS_VBUSVALID) != 0); + case DDI_POWER_5V_VDD5V_GT_VDDIO: + // Check VDD5V_GT_VDDIO for 5V present + return ((__raw_readl(REGS_POWER_BASE + HW_POWER_STS) & BM_POWER_STS_VDD5V_GT_VDDIO) != 0); + default: + break; + } + + return 0; +} + +//////////////////////////////////////////////////////////////////////////////// +//! +//! \brief Report on the die temperature. +//! +//! \fntype Function +//! +//! This function reports on the die temperature. +//! +//! \param[out] pLow The low end of the temperature range. +//! \param[out] pHigh The high end of the temperature range. +//! +//////////////////////////////////////////////////////////////////////////////// +// Temperature constant +#define TEMP_READING_ERROR_MARGIN 5 +#define KELVIN_TO_CELSIUS_CONST 273 + +void ddi_power_GetDieTemp(int16_t * pLow, int16_t * pHigh) +{ + int16_t i16High, i16Low; + uint16_t u16Reading; + + // Get the reading in Kelvins + u16Reading = MeasureInternalDieTemperature(); + + // Adjust for error margin + i16High = u16Reading + TEMP_READING_ERROR_MARGIN; + i16Low = u16Reading - TEMP_READING_ERROR_MARGIN; + + // Convert to Celsius + i16High -= KELVIN_TO_CELSIUS_CONST; + i16Low -= KELVIN_TO_CELSIUS_CONST; + +//#ifdef CONFIG_POWER_SUPPLY_DEBUG +#if 0 + printk("Battery charger: Die temp %d to %d C\n", i16Low, i16High); +#endif + // Return the results + *pHigh = i16High; + *pLow = i16Low; +} + +/////////////////////////////////////////////////////////////////////////////// +//! +//! \brief Checks to see if the DCDC has been manually enabled +//! +//! \fntype Function +//! +//! \retval true if DCDC is ON, false if DCDC is OFF. +//! +//////////////////////////////////////////////////////////////////////////////// +bool ddi_power_IsDcdcOn(void) +{ + return (__raw_readl(REGS_POWER_BASE + HW_POWER_5VCTRL) & BM_POWER_5VCTRL_ENABLE_DCDC) ? 1 : 0; +} + + +//////////////////////////////////////////////////////////////////////////////// +//! See hw_power.h for details. +//////////////////////////////////////////////////////////////////////////////// +void ddi_power_SetPowerClkGate(bool bGate) +{ + // Gate/Ungate the clock to the power block + if (bGate) { + stmp3xxx_setl(BM_POWER_CTRL_CLKGATE, REGS_POWER_BASE + HW_POWER_CTRL); + } else { + stmp3xxx_clearl(BM_POWER_CTRL_CLKGATE, REGS_POWER_BASE + HW_POWER_CTRL); + } +} + +//////////////////////////////////////////////////////////////////////////////// +//! See hw_power.h for details. +//////////////////////////////////////////////////////////////////////////////// +bool ddi_power_GetPowerClkGate(void) +{ + return (__raw_readl(REGS_POWER_BASE + HW_POWER_CTRL) & BM_POWER_CTRL_CLKGATE) ? 1 : 0; +} + + +//////////////////////////////////////////////////////////////////////////////// +// End of file +//////////////////////////////////////////////////////////////////////////////// +//! @} diff --git a/drivers/power/stmp37xx/ddi_power_battery.h b/drivers/power/stmp37xx/ddi_power_battery.h new file mode 100644 index 000000000000..2d7c1a5411ad --- /dev/null +++ b/drivers/power/stmp37xx/ddi_power_battery.h @@ -0,0 +1,67 @@ +/* + * 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 + */ + +//! \brief Battery modes +typedef enum { + // 37xx battery modes + //! \brief LiIon battery powers the player + DDI_POWER_BATT_MODE_LIION = 0, + //! \brief Alkaline/NiMH battery powers the player + DDI_POWER_BATT_MODE_ALKALINE_NIMH = 1, +} ddi_power_BatteryMode_t; + + +//! \brief Available sources for bias currents +typedef enum { + //! \brief Use external resistor to generate bias current + DDI_POWER_EXTERNAL_BIAS_CURRENT = 0x0, + //! \brief Use internal resistor to generate bias current + DDI_POWER_INTERNAL_BIAS_CURRENT = 0x1 +} ddi_power_BiasCurrentSource_t; + +//! \brief Possible 5V detection methods +typedef enum { + //! \brief Use VBUSVALID comparator for detection + DDI_POWER_5V_VBUSVALID, + //! \brief Use VDD5V_GT_VDDIO comparison for detection + DDI_POWER_5V_VDD5V_GT_VDDIO +} ddi_power_5vDetection_t; + + +uint16_t ddi_power_convert_current_to_setting(uint16_t u16Current); +uint16_t ddi_power_convert_setting_to_current(uint16_t u16Setting); +void ddi_power_enable_5v_to_battery_handoff(void); +void ddi_power_execute_5v_to_battery_handoff(void); +void ddi_power_enable_battery_to_5v_handoff(void); +void ddi_power_execute_battery_to_5v_handoff(void); +int ddi_power_init_battery(void); +ddi_power_BatteryMode_t ddi_power_GetBatteryMode(void); +bool ddi_power_GetBatteryChargerEnabled(void); +bool ddi_power_GetChargerPowered(void); +void ddi_power_SetChargerPowered(bool bPowerOn); +int ddi_power_GetChargeStatus(void); +uint16_t ddi_power_GetBattery(void); +uint16_t ddi_power_GetBatteryBrownout(void); +int ddi_power_SetBatteryBrownout(uint16_t u16BattBrownout_mV); +int ddi_power_SetBiasCurrentSource(ddi_power_BiasCurrentSource_t eSource); +ddi_power_BiasCurrentSource_t ddi_power_GetBiasCurrentSource(void); +uint16_t ddi_power_SetMaxBatteryChargeCurrent(uint16_t u16MaxCur); +uint16_t ddi_power_GetMaxBatteryChargeCurrent(void); +uint16_t ddi_power_SetBatteryChargeCurrentThreshold(uint16_t u16Thresh); +uint16_t ddi_power_GetBatteryChargeCurrentThreshold(void); +uint16_t ddi_power_ExpressibleCurrent(uint16_t u16Current); +bool ddi_power_Get5vPresentFlag(void); +void ddi_power_GetDieTemp(int16_t * pLow, int16_t * pHigh); +bool ddi_power_IsDcdcOn(void); +void ddi_power_SetPowerClkGate(bool bGate); +bool ddi_power_GetPowerClkGate(void); diff --git a/drivers/power/stmp37xx/linux.c b/drivers/power/stmp37xx/linux.c new file mode 100644 index 000000000000..178255eceea6 --- /dev/null +++ b/drivers/power/stmp37xx/linux.c @@ -0,0 +1,623 @@ +/* + * Linux glue to STMP3xxx battery state machine. + * + * Author: Steve Longerbeam <stevel@embeddedalley.com> + * + * Copyright (C) 2008 EmbeddedAlley Solutions Inc. + * Copyright 2008-2009 Freescale Semiconductor, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, as + * published by the Free Software Foundation. + */ + +#include <linux/module.h> +#include <linux/err.h> +#include <linux/platform_device.h> +#include <linux/power_supply.h> +#include <linux/jiffies.h> +#include <linux/sched.h> +#include <mach/ddi_bc.h> +#include "ddi_bc_internal.h" +#include <linux/regulator/consumer.h> +#include <linux/regulator/driver.h> +#include <mach/regulator.h> +#include <mach/regs-power.h> +#include <mach/regs-usbphy.h> +#include <mach/platform.h> +#include <linux/delay.h> + +#include <linux/interrupt.h> + + +struct stmp3xxx_info { + struct device *dev; + struct regulator *regulator; + + struct power_supply bat; + struct power_supply ac; + struct power_supply usb; + + ddi_bc_Cfg_t *sm_cfg; + struct mutex sm_lock; + struct timer_list sm_timer; + struct work_struct sm_work; + struct resource *vdd5v_irq; + int is_ac_online; +#define USB_ONLINE 0x01 +#define USB_REG_SET 0x02 +#define USB_SM_RESTART 0x04 +#define USB_SHUTDOWN 0x08 +#define USB_N_SEND 0x10 + int is_usb_online; +}; + +#define to_stmp3xxx_info(x) container_of((x), struct stmp3xxx_info, bat) + +/* There is no direct way to detect wall power presence, so assume the AC + * power source is valid if 5V presents and USB device is disconnected. + * If USB device is connected then assume that AC is offline and USB power + * is online. + */ +#define is_usb_plugged()(__raw_readl(REGS_USBPHY_BASE + HW_USBPHY_STATUS) & \ + BM_USBPHY_STATUS_DEVPLUGIN_STATUS) +#define is_ac_online() \ + (ddi_power_Get5vPresentFlag() ? (!is_usb_plugged()) : 0) +#define is_usb_online() \ + (ddi_power_Get5vPresentFlag() ? (!!is_usb_plugged()) : 0) + +/* + * Power properties + */ +static enum power_supply_property stmp3xxx_power_props[] = { + POWER_SUPPLY_PROP_ONLINE, +}; + +static int stmp3xxx_power_get_property(struct power_supply *psy, + enum power_supply_property psp, + union power_supply_propval *val) +{ + switch (psp) { + case POWER_SUPPLY_PROP_ONLINE: + if (psy->type == POWER_SUPPLY_TYPE_MAINS) + /* ac online */ + val->intval = is_ac_online(); + else + /* usb online */ + val->intval = is_usb_online(); + break; + default: + return -EINVAL; + } + + return 0; +} +/* + * Battery properties + */ +static enum power_supply_property stmp3xxx_bat_props[] = { + POWER_SUPPLY_PROP_STATUS, + POWER_SUPPLY_PROP_PRESENT, + POWER_SUPPLY_PROP_HEALTH, + POWER_SUPPLY_PROP_TECHNOLOGY, + POWER_SUPPLY_PROP_VOLTAGE_NOW, + POWER_SUPPLY_PROP_CURRENT_NOW, + POWER_SUPPLY_PROP_TEMP, +}; + +static int stmp3xxx_bat_get_property(struct power_supply *psy, + enum power_supply_property psp, + union power_supply_propval *val) +{ + struct stmp3xxx_info *info = to_stmp3xxx_info(psy); + ddi_bc_State_t state; + ddi_bc_BrokenReason_t reason; + int temp_alarm; + int16_t temp_lo, temp_hi; + + switch (psp) { + case POWER_SUPPLY_PROP_STATUS: + state = ddi_bc_GetState(); + switch (state) { + case DDI_BC_STATE_CONDITIONING: + case DDI_BC_STATE_CHARGING: + case DDI_BC_STATE_TOPPING_OFF: + val->intval = POWER_SUPPLY_STATUS_CHARGING; + break; + case DDI_BC_STATE_DISABLED: + val->intval = ddi_power_Get5vPresentFlag() ? + POWER_SUPPLY_STATUS_NOT_CHARGING : + POWER_SUPPLY_STATUS_DISCHARGING; + break; + default: + /* TODO: detect full */ + val->intval = POWER_SUPPLY_STATUS_NOT_CHARGING; + break; + } + break; + case POWER_SUPPLY_PROP_PRESENT: + /* is battery present */ + state = ddi_bc_GetState(); + switch (state) { + case DDI_BC_STATE_WAITING_TO_CHARGE: + case DDI_BC_STATE_DCDC_MODE_WAITING_TO_CHARGE: + case DDI_BC_STATE_CONDITIONING: + case DDI_BC_STATE_CHARGING: + case DDI_BC_STATE_TOPPING_OFF: + case DDI_BC_STATE_DISABLED: + val->intval = 1; + break; + case DDI_BC_STATE_BROKEN: + val->intval = !(ddi_bc_GetBrokenReason() == + DDI_BC_BROKEN_NO_BATTERY_DETECTED); + break; + default: + val->intval = 0; + break; + } + break; + case POWER_SUPPLY_PROP_HEALTH: + temp_alarm = ddi_bc_RampGetDieTempAlarm(); + if (temp_alarm) { + val->intval = POWER_SUPPLY_HEALTH_OVERHEAT; + } else { + state = ddi_bc_GetState(); + switch (state) { + case DDI_BC_STATE_BROKEN: + reason = ddi_bc_GetBrokenReason(); + val->intval = + (reason == DDI_BC_BROKEN_CHARGING_TIMEOUT) ? + POWER_SUPPLY_HEALTH_DEAD : + POWER_SUPPLY_HEALTH_UNSPEC_FAILURE; + break; + case DDI_BC_STATE_UNINITIALIZED: + val->intval = POWER_SUPPLY_HEALTH_UNKNOWN; + break; + default: + val->intval = POWER_SUPPLY_HEALTH_GOOD; + break; + } + } + break; + case POWER_SUPPLY_PROP_TECHNOLOGY: + val->intval = POWER_SUPPLY_TECHNOLOGY_LION; + break; + case POWER_SUPPLY_PROP_VOLTAGE_NOW: + /* uV */ + val->intval = ddi_power_GetBattery() * 1000; + break; + case POWER_SUPPLY_PROP_CURRENT_NOW: + /* uA */ + val->intval = ddi_power_GetMaxBatteryChargeCurrent() * 1000; + break; + case POWER_SUPPLY_PROP_TEMP: + mutex_lock(&info->sm_lock); + ddi_power_GetDieTemp(&temp_lo, &temp_hi); + mutex_unlock(&info->sm_lock); + val->intval = temp_lo + (temp_hi - temp_lo) / 2; + + break; + default: + return -EINVAL; + } + + return 0; +} + +static void state_machine_timer(unsigned long data) +{ + struct stmp3xxx_info *info = (struct stmp3xxx_info *)data; + ddi_bc_Cfg_t *cfg = info->sm_cfg; + int ret; + + /* schedule next call to state machine */ + mod_timer(&info->sm_timer, + jiffies + msecs_to_jiffies(cfg->u32StateMachinePeriod)); + + ret = schedule_work(&info->sm_work); + if (!ret) + dev_dbg(info->dev, "state machine failed to schedule\n"); + +} +/* + * Assumtion: + * AC power can't be switched to USB w/o system reboot + * and vice-versa + */ +static void state_machine_work(struct work_struct *work) +{ + struct stmp3xxx_info *info = + container_of(work, struct stmp3xxx_info, sm_work); + + mutex_lock(&info->sm_lock); + + if (info->is_usb_online & USB_SHUTDOWN) { + info->is_usb_online = 0; + if (!info->regulator) + goto out; + regulator_set_current_limit(info->regulator, 0, 0); + goto out; + } + + if (is_ac_online()) { + if (info->is_ac_online) + goto done; + + /* ac supply connected */ + dev_info(info->dev, "changed power connection to ac/5v \n"); + + info->is_ac_online = 1; + info->is_usb_online = 0; + ddi_bc_SetCurrentLimit(600 /*mA*/); + ddi_power_execute_battery_to_5v_handoff(); + ddi_power_enable_5v_to_battery_handoff(); + goto done; + } + + if (!is_usb_online()) + goto out; + + if (info->is_usb_online & USB_REG_SET) + goto done; + + info->is_ac_online = 0; + info->is_usb_online |= USB_ONLINE; + + if (!info->regulator) { + info->regulator = regulator_get(NULL, "charger-1"); + if (!info->regulator || IS_ERR(info->regulator)) { + dev_err(info->dev, + "%s: failed to get regulator\n", __func__); + info->regulator = NULL; + ddi_bc_SetCurrentLimit(350 /*mA*/); + ddi_power_execute_battery_to_5v_handoff(); + ddi_power_enable_5v_to_battery_handoff(); + goto done; + } else + regulator_set_mode(info->regulator, + REGULATOR_MODE_FAST); + } + + if (!(info->is_usb_online & USB_N_SEND)) { + info->is_usb_online |= USB_N_SEND; + } + + if (regulator_set_current_limit(info->regulator, 150000, 150000)) { + dev_err(info->dev, "reg_set_current(150000) failed\n"); + + ddi_bc_SetCurrentLimit(0 /*mA*/); + dev_dbg(info->dev, "charge current set to 0\n"); + mod_timer(&info->sm_timer, jiffies + msecs_to_jiffies(1000)); + goto done; + } + + dev_dbg(info->dev, "%s: charge current set to 100mA\n", __func__); + ddi_bc_SetCurrentLimit(100 /*mA*/); + regulator_set_current_limit(info->regulator, 100000, 100000); + if (info->is_usb_online & USB_SM_RESTART) { + info->is_usb_online &= ~USB_SM_RESTART; + ddi_bc_SetEnable(); + } + + info->is_usb_online |= USB_REG_SET; + + dev_info(info->dev, "changed power connection to usb/5v present\n"); + ddi_power_execute_battery_to_5v_handoff(); + ddi_power_enable_5v_to_battery_handoff(); + ddi_bc_SetEnable(); + +done: + ddi_bc_StateMachine(); +out: + mutex_unlock(&info->sm_lock); +} + +static int bc_sm_restart(struct stmp3xxx_info *info) +{ + ddi_bc_Status_t bcret; + int ret = 0; + + mutex_lock(&info->sm_lock); + + /* ungate power clk */ + ddi_power_SetPowerClkGate(0); + + /* + * config battery charger state machine and move it to the Disabled + * state. This must be done before starting the state machine. + */ + bcret = ddi_bc_Init(info->sm_cfg); + if (bcret != DDI_BC_STATUS_SUCCESS) { + dev_err(info->dev, "state machine init failed: %d\n", bcret); + ret = -EIO; + goto out; + } + + /* + * Check what power supply options we have right now. If + * we're able to do any battery charging, then set the + * appropriate current limit and enable. Otherwise, leave + * the battery charger disabled. + */ + if (is_ac_online()) { + /* ac supply connected */ + dev_info(info->dev, "ac/5v present, enabling state machine\n"); + + info->is_ac_online = 1; + info->is_usb_online = 0; + ddi_bc_SetCurrentLimit(600 /*mA*/); + ddi_bc_SetEnable(); + } else if (is_usb_online()) { + /* usb supply connected */ + dev_info(info->dev, "usb/5v present, enabling state machine\n"); + + info->is_ac_online = 0; + info->is_usb_online = USB_ONLINE | USB_SM_RESTART; + } else { + /* not powered */ + dev_info(info->dev, "%s: 5v not present\n", __func__); + + info->is_ac_online = 0; + info->is_usb_online = 0; + ddi_bc_SetDisable(); + } + + /* schedule first call to state machine */ + mod_timer(&info->sm_timer, jiffies + 1); +out: + mutex_unlock(&info->sm_lock); + return ret; +} + +static irqreturn_t stmp3xxx_vdd5v_irq(int irq, void *cookie) +{ + struct stmp3xxx_info *info = (struct stmp3xxx_info *)cookie; + + if (ddi_power_Get5vPresentFlag()) { + dev_info(info->dev, "5v present, reenable state machine\n"); + + ddi_bc_SetEnable(); + + /* + * We only ack/negate the interrupt here, + * as we can't decide yet if we really can + * switch to 5V (USB bits not ready) + */ + ddi_power_enable_5v_to_battery_handoff(); + } else { + dev_info(info->dev, "5v went away, disabling state machine\n"); + + ddi_bc_SetDisable(); + + info->is_ac_online = 0; + if (info->is_usb_online) + info->is_usb_online = USB_SHUTDOWN; + + ddi_power_execute_5v_to_battery_handoff(); + ddi_power_enable_battery_to_5v_handoff(); + mod_timer(&info->sm_timer, jiffies + 1); + + } + + return IRQ_HANDLED; +} + +static int stmp3xxx_bat_probe(struct platform_device *pdev) +{ + struct stmp3xxx_info *info; + int ret = 0; + + if (!pdev->dev.platform_data) { + printk(KERN_ERR "%s: missing platform data\n", __func__); + return -ENODEV; + } + + info = kzalloc(sizeof(*info), GFP_KERNEL); + if (!info) + return -ENOMEM; + + info->vdd5v_irq = platform_get_resource(pdev, IORESOURCE_IRQ, 0); + if (info->vdd5v_irq == NULL) { + printk(KERN_ERR "%s: failed to get irq resouce\n", __func__); + goto free_info; + } + + platform_set_drvdata(pdev, info); + + info->dev = &pdev->dev; + info->sm_cfg = pdev->dev.platform_data; + + /* initialize bat power_supply struct */ + info->bat.name = "battery"; + info->bat.type = POWER_SUPPLY_TYPE_BATTERY; + info->bat.properties = stmp3xxx_bat_props; + info->bat.num_properties = ARRAY_SIZE(stmp3xxx_bat_props); + info->bat.get_property = stmp3xxx_bat_get_property; + + /* initialize ac power_supply struct */ + info->ac.name = "ac"; + info->ac.type = POWER_SUPPLY_TYPE_MAINS; + info->ac.properties = stmp3xxx_power_props; + info->ac.num_properties = ARRAY_SIZE(stmp3xxx_power_props); + info->ac.get_property = stmp3xxx_power_get_property; + + /* initialize usb power_supply struct */ + info->usb.name = "usb"; + info->usb.type = POWER_SUPPLY_TYPE_USB; + info->usb.properties = stmp3xxx_power_props; + info->usb.num_properties = ARRAY_SIZE(stmp3xxx_power_props); + info->usb.get_property = stmp3xxx_power_get_property; + + init_timer(&info->sm_timer); + info->sm_timer.data = (unsigned long)info; + info->sm_timer.function = state_machine_timer; + + mutex_init(&info->sm_lock); + INIT_WORK(&info->sm_work, state_machine_work); + + /* init LRADC channels to measure battery voltage and die temp */ + ddi_power_init_battery(); + stmp3xxx_clearl(BM_POWER_5VCTRL_ENABLE_LINREG_ILIMIT, REGS_POWER_BASE + HW_POWER_5VCTRL); + + ret = bc_sm_restart(info); + if (ret) + goto free_info; + + ret = request_irq(info->vdd5v_irq->start, + stmp3xxx_vdd5v_irq, IRQF_DISABLED, + pdev->name, info); + if (ret) { + dev_err(info->dev, "failed to request irq\n"); + goto stop_sm; + } + + ret = power_supply_register(&pdev->dev, &info->bat); + if (ret) { + dev_err(info->dev, "failed to register battery\n"); + goto free_irq; + } + + ret = power_supply_register(&pdev->dev, &info->ac); + if (ret) { + dev_err(info->dev, "failed to register ac power supply\n"); + goto unregister_bat; + } + + ret = power_supply_register(&pdev->dev, &info->usb); + if (ret) { + dev_err(info->dev, "failed to register usb power supply\n"); + goto unregister_ac; + } + + /* enable usb device presence detection */ + stmp3xxx_setl(BM_USBPHY_CTRL_ENDEVPLUGINDETECT, REGS_USBPHY_BASE + HW_USBPHY_CTRL); + + return 0; + +unregister_ac: + power_supply_unregister(&info->ac); +unregister_bat: + power_supply_unregister(&info->bat); +free_irq: + free_irq(info->vdd5v_irq->start, pdev); +stop_sm: + ddi_bc_ShutDown(); +free_info: + kfree(info); + return ret; +} + +static int stmp3xxx_bat_remove(struct platform_device *pdev) +{ + struct stmp3xxx_info *info = platform_get_drvdata(pdev); + + if (info->regulator) + regulator_put(info->regulator); + free_irq(info->vdd5v_irq->start, pdev); + ddi_bc_ShutDown(); + power_supply_unregister(&info->usb); + power_supply_unregister(&info->ac); + power_supply_unregister(&info->bat); + return 0; +} + +static void stmp3xxx_bat_shutdown(struct platform_device *pdev) +{ + ddi_bc_ShutDown(); +} + + +#ifdef CONFIG_PM + +static int stmp3xxx_bat_suspend(struct platform_device *pdev, pm_message_t msg) +{ + struct stmp3xxx_info *info = platform_get_drvdata(pdev); + + mutex_lock(&info->sm_lock); + + /* disable 5v irq */ + stmp3xxx_clearl(BM_POWER_CTRL_ENIRQ_VDD5V_GT_VDDIO, REGS_POWER_BASE + HW_POWER_CTRL); + + ddi_bc_SetDisable(); + /* cancel state machine timer */ + del_timer_sync(&info->sm_timer); + + mutex_unlock(&info->sm_lock); + return 0; +} + +static int stmp3xxx_bat_resume(struct platform_device *pdev) +{ + struct stmp3xxx_info *info = platform_get_drvdata(pdev); + ddi_bc_Cfg_t *cfg = info->sm_cfg; + + mutex_lock(&info->sm_lock); + + if (is_ac_online()) { + /* ac supply connected */ + dev_info(info->dev, "ac/5v present, enabling state machine\n"); + + info->is_ac_online = 1; + info->is_usb_online = 0; + ddi_bc_SetCurrentLimit(600 /*mA*/); + ddi_bc_SetEnable(); + } else if (is_usb_online()) { + /* usb supply connected */ + dev_info(info->dev, "usb/5v present, enabling state machine\n"); + + info->is_ac_online = 0; + info->is_usb_online = 1; + ddi_bc_SetCurrentLimit(350 /*mA*/); + ddi_bc_SetEnable(); + } else { + /* not powered */ + dev_info(info->dev, "%s: 5v not present\n", __func__); + + info->is_ac_online = 0; + info->is_usb_online = 0; + } + + /* enable 5v irq */ + stmp3xxx_setl(BM_POWER_CTRL_ENIRQ_VDD5V_GT_VDDIO, REGS_POWER_BASE + HW_POWER_CTRL); + + /* reschedule calls to state machine */ + mod_timer(&info->sm_timer, + jiffies + msecs_to_jiffies(cfg->u32StateMachinePeriod)); + + mutex_unlock(&info->sm_lock); + return 0; +} + +#else +#define stmp3xxx_bat_suspend NULL +#define stmp3xxx_bat_resume NULL +#endif + +static struct platform_driver stmp3xxx_batdrv = { + .probe = stmp3xxx_bat_probe, + .remove = stmp3xxx_bat_remove, + .shutdown = stmp3xxx_bat_shutdown, + .suspend = stmp3xxx_bat_suspend, + .resume = stmp3xxx_bat_resume, + .driver = { + .name = "stmp3xxx-battery", + .owner = THIS_MODULE, + }, +}; + +static int __init stmp3xxx_bat_init(void) +{ + return platform_driver_register(&stmp3xxx_batdrv); +} + +static void __exit stmp3xxx_bat_exit(void) +{ + platform_driver_unregister(&stmp3xxx_batdrv); +} + +module_init(stmp3xxx_bat_init); +module_exit(stmp3xxx_bat_exit); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Steve Longerbeam <stevel@embeddedalley.com>"); +MODULE_DESCRIPTION("Linux glue to STMP3xxx battery state machine"); |