diff options
Diffstat (limited to 'drivers/power')
30 files changed, 12579 insertions, 0 deletions
diff --git a/drivers/power/Kconfig b/drivers/power/Kconfig index bdbc4f73fcdc..f90ffbf4c436 100644 --- a/drivers/power/Kconfig +++ b/drivers/power/Kconfig @@ -103,4 +103,25 @@ 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. + +config BATTERY_MXS + tristate "MXS SoC battery charger driver" + depends on ARCH_MXS + help + Say Y to enable support for the battery charger state machine + for the Sigmatel MXS based SoC's. + +config MXS_VBUS_CURRENT_DRAW + tristate "MXS SoC USB2.0 VBUS Current Limitation" + depends on ARCH_MXS + help + Say Y to enable 100mA limitation when USB vbus power on system + before enumeration to match USB2.0 requirement. + endif # POWER_SUPPLY diff --git a/drivers/power/Makefile b/drivers/power/Makefile index 380d17c9ae29..cc430bda39d3 100644 --- a/drivers/power/Makefile +++ b/drivers/power/Makefile @@ -28,3 +28,5 @@ 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/ +obj-$(CONFIG_BATTERY_MXS) += mxs/ diff --git a/drivers/power/mxs/Makefile b/drivers/power/mxs/Makefile new file mode 100644 index 000000000000..c7675a9ec52b --- /dev/null +++ b/drivers/power/mxs/Makefile @@ -0,0 +1,9 @@ +# +# Makefile for the MXS battery charger driver +# + +obj-$(CONFIG_BATTERY_MXS) += mxs-battery.o + +mxs-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 fiq.o + diff --git a/drivers/power/mxs/ddi_bc_api.c b/drivers/power/mxs/ddi_bc_api.c new file mode 100644 index 000000000000..26d064bff9a2 --- /dev/null +++ b/drivers/power/mxs/ddi_bc_api.c @@ -0,0 +1,559 @@ +/* + * Copyright (C) 2010 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 + */ + + +/* Includes */ + + +#include <linux/kernel.h> +#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() +{ + int ret, state; + + /* -------------------------------------------------------------------------- */ + /* 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. */ + /* -------------------------------------------------------------------------- */ + + state = g_ddi_bc_State; + ret = (stateFunctionTable[g_ddi_bc_State] ()); + if (state != g_ddi_bc_State) + pr_debug("Charger: transit from state %d to %d\n", + state, g_ddi_bc_State); + return ret; + +} + + + +/* 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; + + if (restarts < DDI_BC_MAX_RESTART_CYCLES) { + restarts++; + bRestartChargeCycle = true; + } + + return DDI_BC_STATUS_SUCCESS; +} + + +/* End of file */ + +/* @} */ diff --git a/drivers/power/mxs/ddi_bc_hw.c b/drivers/power/mxs/ddi_bc_hw.c new file mode 100644 index 000000000000..f1fdb6f2b065 --- /dev/null +++ b/drivers/power/mxs/ddi_bc_hw.c @@ -0,0 +1,397 @@ +/* + * Copyright (C) 2010 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" + + +/* 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 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/mxs/ddi_bc_hw.h b/drivers/power/mxs/ddi_bc_hw.h new file mode 100644 index 000000000000..9275c5b3a7ba --- /dev/null +++ b/drivers/power/mxs/ddi_bc_hw.h @@ -0,0 +1,77 @@ +/* + * Copyright (C) 2010 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 + */ + +#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/mxs/ddi_bc_init.c b/drivers/power/mxs/ddi_bc_init.c new file mode 100644 index 000000000000..c93f8969f92c --- /dev/null +++ b/drivers/power/mxs/ddi_bc_init.c @@ -0,0 +1,188 @@ +/* + * Copyright (C) 2010 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; + + /* -------------------------------------------------------------------------- */ + /* 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/mxs/ddi_bc_internal.h b/drivers/power/mxs/ddi_bc_internal.h new file mode 100644 index 000000000000..b5bceeffae98 --- /dev/null +++ b/drivers/power/mxs/ddi_bc_internal.h @@ -0,0 +1,53 @@ +/* + * Copyright (C) 2010 Freescale Semiconductor, Inc. + */ + +/* + * 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 */ + +#include <linux/kernel.h> + +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/mxs/ddi_bc_ramp.c b/drivers/power/mxs/ddi_bc_ramp.c new file mode 100644 index 000000000000..76efc0d5c32d --- /dev/null +++ b/drivers/power/mxs/ddi_bc_ramp.c @@ -0,0 +1,724 @@ +/* + * Copyright (C) 2010 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/mxs/ddi_bc_ramp.h b/drivers/power/mxs/ddi_bc_ramp.h new file mode 100644 index 000000000000..b43db8147f52 --- /dev/null +++ b/drivers/power/mxs/ddi_bc_ramp.h @@ -0,0 +1,50 @@ +/* + * Copyright (C) 2010 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/mxs/ddi_bc_sm.c b/drivers/power/mxs/ddi_bc_sm.c new file mode 100644 index 000000000000..6626ed82c192 --- /dev/null +++ b/drivers/power/mxs/ddi_bc_sm.c @@ -0,0 +1,918 @@ +/* + * Copyright (C) 2010 Freescale Semiconductor, Inc. + */ + +/* + * 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 */ + + +/* 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); + + +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}; + +/* Used by states that need to watch the time. */ +uint32_t g_ddi_bc_u32StateTimer; + +/* Always attempt to charge on first 5V connection */ +bool bRestartChargeCycle = true; + +#ifdef CONFIG_POWER_SUPPLY_DEBUG +static uint16_t u16ExternalBatteryPowerVoltageCheck; +#endif + +ddi_bc_BrokenReason_t ddi_bc_gBrokenReason = DDI_BC_BROKEN_UNINITIALIZED; + + +/* Code */ + + + +/* */ +/* 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; + + pr_info("charger------ ddi_bc_gBrokenReason=%d\n", + ddi_bc_gBrokenReason); +#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_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. + */ + + 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 + + + /* -------------------------------------------------------------------------- */ + /* 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; + /* -------------------------------------------------------------------------- */ + /* 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). */ + /* */ + /* -------------------------------------------------------------------------- */ + + + + 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++; + + + 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 to the Topping Off state. + * 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 + + + + /* 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/mxs/ddi_bc_sm.h b/drivers/power/mxs/ddi_bc_sm.h new file mode 100644 index 000000000000..40bd4a494fb3 --- /dev/null +++ b/drivers/power/mxs/ddi_bc_sm.h @@ -0,0 +1,46 @@ +/* + * Copyright (C) 2010 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/mxs/ddi_power_battery.c b/drivers/power/mxs/ddi_power_battery.c new file mode 100644 index 000000000000..762f29bd784e --- /dev/null +++ b/drivers/power/mxs/ddi_power_battery.c @@ -0,0 +1,1908 @@ +/* + * Copyright (C) 2010 Freescale Semiconductor, Inc. + */ + +/* + * 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 <linux/io.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" + +/* 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 */ + +#define VBUSVALID_THRESH_2_90V 0x0 +#define VBUSVALID_THRESH_4_00V 0x1 +#define VBUSVALID_THRESH_4_10V 0x2 +#define VBUSVALID_THRESH_4_20V 0x3 +#define VBUSVALID_THRESH_4_30V 0x4 +#define VBUSVALID_THRESH_4_40V 0x5 +#define VBUSVALID_THRESH_4_50V 0x6 +#define VBUSVALID_THRESH_4_60V 0x7 + +#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 + +#define VDD4P2_ENABLED + +#define DDI_POWER_BATTERY_XFER_THRESHOLD_MV 3200 + + +#ifndef BATTERY_VOLTAGE_CMPTRIP100_THRESHOLD_MV +#define BATTERY_VOLTAGE_CMPTRIP100_THRESHOLD_MV 4000 +#endif + +#ifndef BATTERY_VOLTAGE_CMPTRIP105_THRESHOLD_MV +#define BATTERY_VOLTAGE_CMPTRIP105_THRESHOLD_MV 3800 +#endif + +/* #define DEBUG_IRQS */ + +/* to be re-enabled once FIQ functionality is added */ +#define DISABLE_VDDIO_BO_PROTECTION + +#ifdef CONFIG_ARCH_MX28 +#define BM_POWER_STS_VBUSVALID BM_POWER_STS_VBUSVALID0 +#endif + +/* Globals & Variables */ + + + +/* Select your 5V Detection method */ + +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 */ + __raw_writel(BM_POWER_5VCTRL_PWDN_5VBRNOUT, + REGS_POWER_BASE + HW_POWER_5VCTRL_CLR); + + /* Enabling VBUSVALID hardware detection even if VDD5V_GT_VDDIO + * is the detection method being used for 5V status (hardware + * or software). This is in case any other drivers (such as + * USB) are specifically monitoring VBUSVALID status + */ + __raw_writel(BM_POWER_5VCTRL_VBUSVALID_5VDETECT, + REGS_POWER_BASE + HW_POWER_5VCTRL_SET); + + /* Set 5V detection threshold to 4.3V for VBUSVALID. */ + __raw_writel( + BF_POWER_5VCTRL_VBUSVALID_TRSH(VBUSVALID_THRESH_4_30V), + REGS_POWER_BASE + HW_POWER_5VCTRL_SET); + + /* 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_POWER_VDDIOCTRL_LINREG_OFFSET(LINREG_OFFSET_STEP_BELOW); + __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_POWER_VDDACTRL_LINREG_OFFSET(LINREG_OFFSET_STEP_BELOW); + __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_POWER_VDDDCTRL_LINREG_OFFSET(LINREG_OFFSET_STEP_BELOW); + __raw_writel(val, REGS_POWER_BASE + HW_POWER_VDDDCTRL); + + /* Clear vbusvalid interrupt flag */ + __raw_writel(BM_POWER_CTRL_VBUSVALID_IRQ, + REGS_POWER_BASE + HW_POWER_CTRL_CLR); + __raw_writel(BM_POWER_CTRL_VDD5V_GT_VDDIO_IRQ, + REGS_POWER_BASE + HW_POWER_CTRL_CLR); + /* enable vbusvalid irq */ + + + /* enable 5V Detection interrupt vbusvalid irq */ + switch (DetectionMethod) { + case DDI_POWER_5V_VBUSVALID: + /* Check VBUSVALID for 5V present */ + __raw_writel(BM_POWER_CTRL_ENIRQ_VBUS_VALID, + REGS_POWER_BASE + HW_POWER_CTRL_SET); + break; + case DDI_POWER_5V_VDD5V_GT_VDDIO: + /* Check VDD5V_GT_VDDIO for 5V present */ + __raw_writel(BM_POWER_CTRL_ENIRQ_VDD5V_GT_VDDIO, + REGS_POWER_BASE + HW_POWER_CTRL_SET); + break; + } +} + +/* + * 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 */ + __raw_writel(BM_POWER_CTRL_VBUSVALID_IRQ, + REGS_POWER_BASE + HW_POWER_CTRL_CLR); + __raw_writel(BM_POWER_CTRL_VDD5V_GT_VDDIO_IRQ, + REGS_POWER_BASE + HW_POWER_CTRL_CLR); + + /* detect 5v unplug */ + __raw_writel(BM_POWER_CTRL_POLARITY_VBUSVALID, + REGS_POWER_BASE + HW_POWER_CTRL_CLR); + __raw_writel(BM_POWER_CTRL_POLARITY_VDD5V_GT_VDDIO, + REGS_POWER_BASE + HW_POWER_CTRL_CLR); + +#ifndef VDD4P2_ENABLED + /* Enable automatic transition to DCDC */ + __raw_writel(BM_POWER_5VCTRL_DCDC_XFER, + REGS_POWER_BASE + HW_POWER_5VCTRL_SET); +#endif +} + +/* + * 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) +{ + int val; +#ifdef VDD4P2_ENABLED + val = __raw_readl(REGS_POWER_BASE + HW_POWER_DCDC4P2); + val &= ~(BM_POWER_DCDC4P2_ENABLE_DCDC | BM_POWER_DCDC4P2_ENABLE_4P2); + __raw_writel(val, REGS_POWER_BASE + HW_POWER_DCDC4P2); + + __raw_writel(BM_POWER_5VCTRL_PWD_CHARGE_4P2, + REGS_POWER_BASE + HW_POWER_5VCTRL_SET); + + /* make VBUSVALID_TRSH 4400mV and set PWD_CHARGE_4P2 */ + __raw_writel(BM_POWER_5VCTRL_VBUSVALID_TRSH, + REGS_POWER_BASE + HW_POWER_5VCTRL_CLR); + + __raw_writel(BF_POWER_5VCTRL_VBUSVALID_TRSH(VBUSVALID_THRESH_4_40V), + REGS_POWER_BASE + HW_POWER_5VCTRL_SET); + +#else + /* 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. */ + __raw_writel(BF_POWER_VDDDCTRL_LINREG_OFFSET(LINREG_OFFSET_STEP_BELOW), + HW_POWER_BASE + HW_POWER_VDDDCTRL_SET); + /* Turn on the VDDD DCDC output and turn off the VDDD LinReg output. */ + __raw_writel(BM_POWER_VDDDCTRL_DISABLE_FET, + HW_POWER_BASE + HW_POWER_VDDDCTRL_CLR); + + __raw_writel(BM_POWER_VDDDCTRL_ENABLE_LINREG, + HW_POWER_BASE + HW_POWER_VDDDCTRL_CLR); + /* Make sure stepping is enabled when using DCDC. */ + __raw_writel(BM_POWER_VDDDCTRL_DISABLE_STEPPING, + HW_POWER_BASE + HW_POWER_VDDDCTRL_CLR); + + /* Power VDDA and VDDIO from the DCDC. */ + + /* Use LinReg offset for DCDC mode. */ + __raw_writel(BF_POWER_VDDACTRL_LINREG_OFFSET(LINREG_OFFSET_STEP_BELOW), + HW_POWER_BASE + HW_POWER_VDDACTRL_SET); + /* Turn on the VDDA DCDC converter output and turn off LinReg output. */ + __raw_writel(BM_POWER_VDDACTRL_DISABLE_FET, + HW_POWER_BASE + HW_POWER_VDDACTRL_CLR); + __raw_writel(BM_POWER_VDDACTRL_ENABLE_LINREG, + HW_POWER_BASE + HW_POWER_VDDACTRL_CLR); + + /* Make sure stepping is enabled when using DCDC. */ + __raw_writel(BM_POWER_VDDACTRL_DISABLE_STEPPING, + HW_POWER_BASE + HW_POWER_VDDACTRL_CLR); + + /* Use LinReg offset for DCDC mode. */ + __raw_writel(BF_POWER_VDDIOCTRL_LINREG_OFFSET( + LINREG_OFFSET_STEP_BELOW + ), + HW_POWER_BASE + HW_POWER_VDDIOCTRL_SET); + + /* Turn on the VDDIO DCDC output and turn on the LinReg output.*/ + __raw_writel(BM_POWER_VDDIOCTRL_DISABLE_FET, + HW_POWER_BASE + HW_POWER_VDDIOCTRL_CLR); + + __raw_writel(BM_POWER_5VCTRL_ILIMIT_EQ_ZERO, + HW_POWER_BASE + HW_POWER_5VCTRL_CLR_CLR); + + /* Make sure stepping is enabled when using DCDC. */ + __raw_writel(BM_POWER_VDDIOCTRL_DISABLE_STEPPING, + HW_POWER_BASE + HW_POWER_VDDIOCTRL_CLR); +#endif + +} + +/* + * 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 */ + __raw_writel(BM_POWER_CTRL_VBUSVALID_IRQ, + REGS_POWER_BASE + HW_POWER_CTRL_CLR); + __raw_writel(BM_POWER_CTRL_VDD5V_GT_VDDIO_IRQ, + REGS_POWER_BASE + HW_POWER_CTRL_CLR); + + /* detect 5v plug-in */ + __raw_writel(BM_POWER_CTRL_POLARITY_VBUSVALID, + REGS_POWER_BASE + HW_POWER_CTRL_SET); + __raw_writel(BM_POWER_CTRL_POLARITY_VDD5V_GT_VDDIO, + REGS_POWER_BASE + HW_POWER_CTRL_SET); + +#ifndef VDD4P2_ENABLED + /* Force current from 5V to be zero by disabling its entry source. */ + __raw_writel(BM_POWER_5VCTRL_ILIMIT_EQ_ZERO, + REGS_POWER_BASE + HW_POWER_5VCTRL_SET); +#endif + /* Allow DCDC be to active when 5V is present. */ + __raw_writel(BM_POWER_5VCTRL_ENABLE_DCDC, + REGS_POWER_BASE + HW_POWER_5VCTRL_SET); +} + +/* This function handles the transitions on each of theVDD5V_GT_VDDIO 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) +{ + +#ifdef VDD4P2_ENABLED + ddi_power_Enable4p2(450); +#else + /* Disable the DCDC during 5V connections. */ + __raw_writel(BM_POWER_5VCTRL_ENABLE_DCDC, + HW_POWER_BAE + HW_POWER_5VCTRL_CLR); + + /* 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 */ + __raw_writel(BF_POWER_VDDDCTRL_LINREG_OFFSET(LINREG_OFFSET_STEP_BELOW), + HW_POWER_BAE + HW_POWER_VDDDCTRL_SET); + + /* Turn on the VDDD LinReg and turn on the VDDD DCDC output. The */ + /* ENABLE_DCDC must be cleared to avoid LinReg and DCDC conflict. */ + __raw_writel(BM_POWER_VDDDCTRL_ENABLE_LINREG, + HW_POWER_BAE + HW_POWER_VDDDCTRL_SET); + __raw_writel(BM_POWER_VDDDCTRL_DISABLE_FET, + HW_POWER_BAE + HW_POWER_VDDDCTRL_CLR); + + /* Make sure stepping is disabled when using linear regulators */ + __raw_writel(BM_POWER_VDDDCTRL_DISABLE_STEPPING, + HW_POWER_BAE + HW_POWER_VDDDCTRL_SET); + + /* Use LinReg offset for LinReg mode */ + __raw_writel(BM_POWER_VDDACTRL_LINREG_OFFSET(LINREG_OFFSET_STEP_BELOW), + HW_POWER_BAE + HW_POWER_VDDACTRL_SET); + + + /* Turn on the VDDA LinReg output and prepare the DCDC for transfer. */ + /* ENABLE_DCDC must be clear to avoid DCDC and LinReg conflict. */ + stmp3xxx_set(BM_POWER_VDDACTRL_ENABLE_LINREG, + HW_POWER_BASE + HW_POWER_VDDACTRL_SET); + __raw_writel(BM_POWER_VDDACTRL_DISABLE_FET, + HW_POWER_BASE + HW_POWER_VDDACTRL_CLR); + + /* Make sure stepping is disabled when using linear regulators */ + __raw_writel(BM_POWER_VDDACTRL_DISABLE_STEPPING, + HW_POWER_BASE + HW_POWER_VDDACTRL_SET); + + /* Use LinReg offset for LinReg mode. */ + __raw_writel(BF_POWER_VDDIOCTRL_LINREG_OFFSET( + LINREG_OFFSET_STEP_BELOW), + HW_POWER_BASE + HW_POWER_VDDIOCTRL_SET); + + /* Turn on the VDDIO LinReg output and prepare the VDDIO DCDC output. */ + /* ENABLE_DCDC must be cleared to prevent DCDC and LinReg conflict. */ + __raw_writel(BM_POWER_VDDIOCTRL_DISABLE_FET, + HW_POWER_BASE + HW_POWER_VDDIOCTRL_CLR); + __raw_writel(BM_POWER_5VCTRL_ILIMIT_EQ_ZERO, + REGS_POWER_BASE + HW_POWER_5VCTRL_CLR); + + /* Make sure stepping is disabled when using DCDC. */ + __raw_writel(BM_POWER_VDDIOCTRL_DISABLE_STEPPING, + REGS_POWER_BASE + HW_POWER_VDDIOCTRL_SET); +#endif +} + + +void ddi_power_Start4p2Dcdc(bool battery_ready) +{ + uint32_t temp_reg, old_values; + bool vdda_pwdn = false, vddd_pwdn = false, vddio_pwdn = false; + +#ifndef CONFIG_ARCH_MX28 + /* set vbusvalid threshold to 2.9V because of errata */ + __raw_writel(BM_POWER_5VCTRL_VBUSVALID_TRSH, + REGS_POWER_BASE + HW_POWER_5VCTRL_CLR); +#endif + +#if 0 + if (battery_ready) + ddi_power_EnableBatteryIrq(); + else + enable_4p2_fiq_shutdown(); +#endif + + /* enable hardware shutdown on battery brownout */ + __raw_writel( + BM_POWER_BATTMONITOR_PWDN_BATTBRNOUT | + __raw_readl(REGS_POWER_BASE + HW_POWER_BATTMONITOR), + REGS_POWER_BASE + HW_POWER_BATTMONITOR); + + /* set VBUS DROOP threshold to 4.3V */ + __raw_writel(BM_POWER_5VCTRL_VBUSDROOP_TRSH, + REGS_POWER_BASE + HW_POWER_5VCTRL_CLR); + + /* turn of vbus valid detection. Part of errate + * workaround. */ + __raw_writel(BM_POWER_5VCTRL_PWRUP_VBUS_CMPS, + REGS_POWER_BASE + HW_POWER_5VCTRL_SET); + + __raw_writel(BM_POWER_5VCTRL_VBUSVALID_5VDETECT, + REGS_POWER_BASE + HW_POWER_5VCTRL_CLR); + + if (__raw_readl(REGS_POWER_BASE + HW_POWER_VDDIOCTRL) + & BM_POWER_VDDIOCTRL_PWDN_BRNOUT) + vddio_pwdn = true; + + if (__raw_readl(REGS_POWER_BASE + HW_POWER_VDDDCTRL) + & BM_POWER_VDDDCTRL_PWDN_BRNOUT) + vddd_pwdn = true; + + if (__raw_readl(REGS_POWER_BASE + HW_POWER_VDDACTRL) + & BM_POWER_VDDACTRL_PWDN_BRNOUT) + vdda_pwdn = true; + + __raw_writel(__raw_readl(REGS_POWER_BASE + HW_POWER_VDDACTRL) + & (~BM_POWER_VDDACTRL_PWDN_BRNOUT), + REGS_POWER_BASE + HW_POWER_VDDACTRL); + + __raw_writel(__raw_readl(REGS_POWER_BASE + HW_POWER_VDDDCTRL) + & (~BM_POWER_VDDDCTRL_PWDN_BRNOUT), + REGS_POWER_BASE + HW_POWER_VDDDCTRL); + + __raw_writel(__raw_readl(REGS_POWER_BASE + HW_POWER_VDDIOCTRL) + & (~BM_POWER_VDDIOCTRL_PWDN_BRNOUT), + REGS_POWER_BASE + HW_POWER_VDDIOCTRL); + + if ((__raw_readl(REGS_POWER_BASE + HW_POWER_STS) + & BM_POWER_STS_VDDIO_BO) == 0) + __raw_writel(BM_POWER_CTRL_VDDIO_BO_IRQ, + REGS_POWER_BASE + HW_POWER_CTRL_CLR); + + if ((__raw_readl(REGS_POWER_BASE + HW_POWER_STS) + & BM_POWER_STS_VDDD_BO) == 0) + __raw_writel(BM_POWER_CTRL_VDDD_BO_IRQ, + REGS_POWER_BASE + HW_POWER_CTRL_CLR); + + if ((__raw_readl(REGS_POWER_BASE + HW_POWER_STS) + & BM_POWER_STS_VDDA_BO) == 0) + __raw_writel(BM_POWER_CTRL_VDDA_BO_IRQ, + REGS_POWER_BASE + HW_POWER_CTRL_CLR); + + temp_reg = (BM_POWER_CTRL_ENIRQ_VDDD_BO | + BM_POWER_CTRL_ENIRQ_VDDA_BO | + BM_POWER_CTRL_ENIRQ_VDDIO_BO | + BM_POWER_CTRL_ENIRQ_VDD5V_DROOP | + BM_POWER_CTRL_ENIRQ_VBUS_VALID); + + /* save off old brownout enable values */ + old_values = __raw_readl(REGS_POWER_BASE + HW_POWER_CTRL) & + temp_reg; + + /* disable irqs affected by errata */ + __raw_writel(temp_reg, REGS_POWER_BASE + HW_POWER_CTRL_CLR); + + /* Enable DCDC from 4P2 */ + __raw_writel(__raw_readl(REGS_POWER_BASE + HW_POWER_DCDC4P2) | + BM_POWER_DCDC4P2_ENABLE_DCDC, + REGS_POWER_BASE + HW_POWER_DCDC4P2); + + /* give a delay to check for errate noise problem */ + mdelay(1); + + temp_reg = (BM_POWER_CTRL_VDDD_BO_IRQ | + BM_POWER_CTRL_VDDA_BO_IRQ | + BM_POWER_CTRL_VDDIO_BO_IRQ | + BM_POWER_CTRL_VDD5V_DROOP_IRQ | + BM_POWER_CTRL_VBUSVALID_IRQ); + + __raw_writel(temp_reg, REGS_POWER_BASE + HW_POWER_CTRL_CLR); + /* stay in this loop until the false brownout indciations + * no longer occur or until 5V actually goes away + */ + while ((__raw_readl(REGS_POWER_BASE + HW_POWER_CTRL) & temp_reg) && + !(__raw_readl(REGS_POWER_BASE + HW_POWER_CTRL) & + BM_POWER_CTRL_VDD5V_GT_VDDIO_IRQ)) { + __raw_writel(temp_reg, REGS_POWER_BASE + HW_POWER_CTRL_CLR); + + mdelay(1); + } + /* revert to previous enable irq values */ + __raw_writel(old_values, REGS_POWER_BASE + HW_POWER_CTRL_SET); + + if (vdda_pwdn) + __raw_writel(__raw_readl(REGS_POWER_BASE + HW_POWER_VDDACTRL) + | BM_POWER_VDDACTRL_PWDN_BRNOUT, + REGS_POWER_BASE + HW_POWER_VDDACTRL); + + if (vddd_pwdn) + __raw_writel(__raw_readl(REGS_POWER_BASE + HW_POWER_VDDDCTRL) + | BM_POWER_VDDDCTRL_PWDN_BRNOUT, + REGS_POWER_BASE + HW_POWER_VDDDCTRL); + + if (vddio_pwdn) + __raw_writel(__raw_readl(REGS_POWER_BASE + HW_POWER_VDDIOCTRL) + | BM_POWER_VDDIOCTRL_PWDN_BRNOUT, + REGS_POWER_BASE + HW_POWER_VDDIOCTRL); + + if (DetectionMethod == DDI_POWER_5V_VBUSVALID) + __raw_writel(BM_POWER_5VCTRL_VBUSVALID_5VDETECT, + REGS_POWER_BASE + HW_POWER_5VCTRL_SET); +} + + +/* set the optimal CMPTRIP for the best possible 5V + * disconnection handling but without drawing power + * from the power on a stable 4p2 rails (at 4.2V). + */ +void ddi_power_handle_cmptrip(void) +{ + enum ddi_power_5v_status pmu_5v_status; + uint32_t temp = __raw_readl(REGS_POWER_BASE + HW_POWER_DCDC4P2); + temp &= ~(BM_POWER_DCDC4P2_CMPTRIP); + + pmu_5v_status = ddi_power_GetPmu5vStatus(); + + /* CMPTRIP should remain at 31 when 5v is disconnected + * or 5v is connected but hasn't been handled yet + */ + if (pmu_5v_status != existing_5v_connection) + temp |= (31 << BP_POWER_DCDC4P2_CMPTRIP); + else if (ddi_power_GetBattery() > + BATTERY_VOLTAGE_CMPTRIP100_THRESHOLD_MV) + temp |= (1 << BP_POWER_DCDC4P2_CMPTRIP); + else if (ddi_power_GetBattery() > + BATTERY_VOLTAGE_CMPTRIP105_THRESHOLD_MV) + temp |= (24 << BP_POWER_DCDC4P2_CMPTRIP); + else + temp |= (31 << BP_POWER_DCDC4P2_CMPTRIP); + + + __raw_writel(temp, REGS_POWER_BASE + HW_POWER_DCDC4P2); +} + +void ddi_power_Init4p2Params(void) +{ + uint32_t temp; + + ddi_power_handle_cmptrip(); + + temp = __raw_readl(REGS_POWER_BASE + HW_POWER_DCDC4P2); + + /* DROPOUT CTRL to 10, TRG to 0 */ + temp &= ~(BM_POWER_DCDC4P2_TRG | BM_POWER_DCDC4P2_DROPOUT_CTRL); + temp |= (0xa << BP_POWER_DCDC4P2_DROPOUT_CTRL); + + __raw_writel(temp, REGS_POWER_BASE + HW_POWER_DCDC4P2); + + + temp = __raw_readl(REGS_POWER_BASE + HW_POWER_5VCTRL); + + /* HEADROOM_ADJ to 4, CHARGE_4P2_ILIMIT to 0 */ + temp &= ~(BM_POWER_5VCTRL_HEADROOM_ADJ | + BM_POWER_5VCTRL_CHARGE_4P2_ILIMIT); + temp |= (4 << BP_POWER_5VCTRL_HEADROOM_ADJ); + +} + +bool ddi_power_IsBattRdyForXfer(void) +{ + uint16_t u16BatteryVoltage = ddi_power_GetBattery(); + + if (u16BatteryVoltage > DDI_POWER_BATTERY_XFER_THRESHOLD_MV) + return true; + else + return false; +} + +void ddi_power_EnableVbusDroopIrq(void) +{ + + __raw_writel(BM_POWER_CTRL_VDD5V_DROOP_IRQ, + REGS_POWER_BASE + HW_POWER_CTRL_CLR); + + __raw_writel(BM_POWER_CTRL_ENIRQ_VDD5V_DROOP, + REGS_POWER_BASE + HW_POWER_CTRL_SET); + +} + + +void ddi_power_Enable4p2(uint16_t target_current_limit_ma) +{ + + uint16_t u16BatteryVoltage; + uint32_t temp_reg; + + ddi_power_Init4p2Params(); + /* disable 4p2 rail brownouts for now. (they + * should have already been off at this point) */ + __raw_writel(BM_POWER_CTRL_ENIRQ_DCDC4P2_BO, + REGS_POWER_BASE + HW_POWER_CTRL_CLR); + + u16BatteryVoltage = ddi_power_GetBattery(); + + if (ddi_power_IsBattRdyForXfer()) { + + /* PWD_CHARGE_4P2 should already be set but just in case... */ + __raw_writel(BM_POWER_5VCTRL_PWD_CHARGE_4P2, + REGS_POWER_BASE + HW_POWER_5VCTRL_SET); + + /* set CMPTRIP to DCDC_4P2 pin >= BATTERY pin */ + temp_reg = __raw_readl(REGS_POWER_BASE + HW_POWER_DCDC4P2); + temp_reg &= ~(BM_POWER_DCDC4P2_CMPTRIP); + temp_reg |= (31 << BP_POWER_DCDC4P2_CMPTRIP); + __raw_writel(temp_reg, REGS_POWER_BASE + HW_POWER_DCDC4P2); + + /* since we have a good battery, we can go ahead + * and turn on the Dcdcing from the 4p2 source. + * This is helpful in working around the chip + * errata. + */ + ddi_power_Start4p2Dcdc(true); + + /* Enable VbusDroopIrq to handle errata */ + + /* set vbus droop detection level to 4.3V */ + __raw_writel(BM_POWER_5VCTRL_VBUSDROOP_TRSH, + REGS_POWER_BASE + HW_POWER_5VCTRL_CLR); + + ddi_power_EnableVbusDroopIrq(); + /* now that the DCDC4P2 problems are cleared, + * turn on and ramp up the 4p2 regulator + */ + temp_reg = ddi_power_BringUp4p2Regulator( + target_current_limit_ma, true); + + /* if we still have our 5V connection, we can disable + * battery brownout interrupt. This is because the + * VDD5V DROOP IRQ handler will also shutdown if battery + * is browned out and it will enable the battery brownout + * and bring VBUSVALID_TRSH level back to a normal level + * which caused the hardware battery brownout shutdown + * to be enabled. The benefit of this is that device + * that have detachable batteries (or devices going through + * the assembly line and running this firmware to test + * with) can avoid shutting down if 5V is present and + * battery voltage goes away. + */ + if (!(__raw_readl(REGS_POWER_BASE + HW_POWER_CTRL) & + (BM_POWER_CTRL_VBUSVALID_IRQ | + BM_POWER_CTRL_VDD5V_DROOP_IRQ))) { + ddi_power_EnableBatteryBoInterrupt(false); + } + + + + printk(KERN_DEBUG "4P2 rail started. 5V current limit\ + set to %dmA\n", temp_reg); + + } else { + + printk(KERN_ERR "4P2 rail was attempted to be started \ + from a system\ + with a very low battery voltage. This is not\ + yet handled by the kernel driver, only by the\ + bootlets. Remaining on battery power.\n"); + + if ((__raw_readl(REGS_POWER_BASE + HW_POWER_5VCTRL) && + BM_POWER_5VCTRL_ENABLE_DCDC)) + ddi_power_EnableBatteryBoInterrupt(true); + +#if 0 + /* enable hardware shutdown (if 5v disconnected) + * on battery brownout */ + __raw_writel( + BM_POWER_BATTMONITOR_PWDN_BATTBRNOUT | + __raw_readl(REGS_POWER_BASE + HW_POWER_BATTMONITOR), + REGS_POWER_BASE + HW_POWER_BATTMONITOR); + + /* turn on and ramp up the 4p2 regulator */ + temp_reg = ddi_power_BringUp4p2Regulator( + target_current_limit_ma, false); + + Configure4p2FiqShutdown(); + + SetVbusValidThresh(0); +#endif + } + +} + +/* enable and ramp up 4p2 regulator */ +uint16_t ddi_power_BringUp4p2Regulator( + uint16_t target_current_limit_ma, + bool b4p2_dcdc_enabled) +{ + uint32_t temp_reg; + uint16_t charge_4p2_ilimit = 0; + + /* initial current limit to 0 */ + __raw_writel(BM_POWER_5VCTRL_CHARGE_4P2_ILIMIT, + REGS_POWER_BASE + HW_POWER_5VCTRL_CLR); + + __raw_writel(__raw_readl(REGS_POWER_BASE + HW_POWER_DCDC4P2) | + BM_POWER_DCDC4P2_ENABLE_4P2, + REGS_POWER_BASE + HW_POWER_DCDC4P2); + + /* set 4p2 target voltage to zero */ + temp_reg = __raw_readl(REGS_POWER_BASE + HW_POWER_DCDC4P2); + temp_reg &= (~BM_POWER_DCDC4P2_TRG); + __raw_writel(temp_reg, REGS_POWER_BASE + HW_POWER_DCDC4P2); + + /* Enable 4P2 regulator*/ + __raw_writel(BM_POWER_5VCTRL_PWD_CHARGE_4P2, + REGS_POWER_BASE + HW_POWER_5VCTRL_CLR); + + if (target_current_limit_ma > 780) + target_current_limit_ma = 780; + + ddi_power_Set4p2BoLevel(4150); + + /* possibly not necessary but recommended for unloaded + * 4p2 rail + */ + __raw_writel(BM_POWER_CHARGE_ENABLE_LOAD, + REGS_POWER_BASE + HW_POWER_CHARGE_SET); + + while (charge_4p2_ilimit < target_current_limit_ma) { + + if (__raw_readl(REGS_POWER_BASE + HW_POWER_CTRL) & + (BM_POWER_CTRL_VBUSVALID_IRQ | + BM_POWER_CTRL_VDD5V_DROOP_IRQ)) + break; + + + charge_4p2_ilimit += 100; + if (charge_4p2_ilimit > target_current_limit_ma) + charge_4p2_ilimit = target_current_limit_ma; + + ddi_power_set_4p2_ilimit(charge_4p2_ilimit); + + /* dcdc4p2 enable_dcdc must be enabled for + * 4p2 bo indication to function. If not enabled, + * skip using bo level detection + */ + if (!(b4p2_dcdc_enabled)) + msleep(1); + else if (__raw_readl(REGS_POWER_BASE + HW_POWER_STS) & + BM_POWER_STS_DCDC_4P2_BO) + msleep(1); + else { + charge_4p2_ilimit = target_current_limit_ma; + ddi_power_set_4p2_ilimit(charge_4p2_ilimit); + } + } + + ddi_power_Set4p2BoLevel(3600); + + __raw_writel(BM_POWER_CTRL_DCDC4P2_BO_IRQ, + REGS_POWER_BASE + HW_POWER_CTRL_CLR); + + /* rail should now be up and loaded. Extra + * internal load is not necessary. + */ + __raw_writel(BM_POWER_CHARGE_ENABLE_LOAD, + REGS_POWER_BASE + HW_POWER_CHARGE_CLR); + + return charge_4p2_ilimit; + +} + + +void ddi_power_Set4p2BoLevel(uint16_t bo_voltage_mv) +{ + uint16_t bo_reg_value; + uint32_t temp; + + if (bo_voltage_mv < 3600) + bo_voltage_mv = 3600; + else if (bo_voltage_mv > 4375) + bo_voltage_mv = 4375; + + bo_reg_value = (bo_voltage_mv - 3600) / 25; + + temp = __raw_readl(REGS_POWER_BASE + HW_POWER_DCDC4P2); + temp &= (~BM_POWER_DCDC4P2_BO); + temp |= (bo_reg_value << BP_POWER_DCDC4P2_BO); + __raw_writel(temp, REGS_POWER_BASE + HW_POWER_DCDC4P2); +} + + + +void ddi_power_init_handoff(void) +{ + int val; + /* The following settings give optimal power supply capability */ + + /* 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); +} + + +void ddi_power_EnableBatteryInterrupt(bool enable) +{ + + __raw_writel(BM_POWER_CTRL_BATT_BO_IRQ, + REGS_POWER_BASE + HW_POWER_CTRL_CLR); + + __raw_writel(BM_POWER_CTRL_ENIRQBATT_BO, + REGS_POWER_BASE + HW_POWER_CTRL_SET); + +} + + +#define REGS_LRADC_BASE IO_ADDRESS(LRADC_PHYS_ADDR) + +int ddi_power_init_battery(void) +{ + + int ret = 0; + + if (!(__raw_readl(REGS_POWER_BASE + HW_POWER_5VCTRL) && + BM_POWER_5VCTRL_ENABLE_DCDC)) { + printk(KERN_ERR "WARNING: Power Supply not\ + initialized correctly by \ + pre-kernel bootlets. HW_POWER_5VCTRL \ + ENABLE_DCDC should already be set. Kernel \ + power driver behavior may not be reliable \n"); + ret = 1; + } + if ((__raw_readl(REGS_POWER_BASE + HW_POWER_BATTMONITOR) & + BM_POWER_BATTMONITOR_BATT_VAL) == 0) { + ret = 1; + printk(KERN_INFO "WARNING : No battery connected !\r\n"); + return ret; + } + + /* the following code to enable automatic battery measurement + * should have already been enabled in the boot prep files. Not + * sure if this is necessary or possibly susceptible to + * mis-coordination + */ + + + ret = !hw_lradc_present(BATTERY_VOLTAGE_CH); + + if (ret) { + printk(KERN_ERR "%s: hw_lradc_present failed\n", __func__); + return -ENODEV; + } else { + uint16_t wait_time = 0; + + hw_lradc_configure_channel(BATTERY_VOLTAGE_CH, 0 /* div2 */ , + 0 /* acc */ , + 0 /* num_samples */); + + /* Setup the trigger loop forever */ + hw_lradc_set_delay_trigger(LRADC_DELAY_TRIGGER_BATTERY, + 1 << BATTERY_VOLTAGE_CH, + 1 << LRADC_DELAY_TRIGGER_BATTERY, + 0, 200); + + /* Clear the accumulator & NUM_SAMPLES */ + __raw_writel(0xFFFFFFFF, + REGS_LRADC_BASE + HW_LRADC_CHn_CLR(BATTERY_VOLTAGE_CH)); + + /* clear previous "measurement performed" status */ + __raw_writel(1 << BATTERY_VOLTAGE_CH, + REGS_LRADC_BASE + HW_LRADC_CTRL1_CLR); + + /* set to LiIon scale factor */ + __raw_writel(BM_LRADC_CONVERSION_SCALE_FACTOR, + REGS_LRADC_BASE + HW_LRADC_CONVERSION_SET); + + /* kick off the trigger */ + hw_lradc_set_delay_trigger_kick( + LRADC_DELAY_TRIGGER_BATTERY, 1); + + + /* wait for 1st converstion to be complete before + * enabling automatic copy to power supply + * peripheral + */ + while (!(__raw_readl(REGS_LRADC_BASE + HW_LRADC_CTRL1) & + 1 << BATTERY_VOLTAGE_CH) && + (wait_time < 10)) { + wait_time++; + mdelay(1); + } + + __raw_writel(BM_LRADC_CONVERSION_AUTOMATIC, + REGS_LRADC_BASE + HW_LRADC_CONVERSION_SET); +#ifdef CONFIG_ARCH_MX28 + /* workaround for mx28 lradc result incorrect in the + first several ms */ + for (wait_time = 0; wait_time < 20; wait_time++) + if (ddi_bc_hwGetBatteryVoltage() < 1000) { + pr_info("ddi_bc_hwGetBatteryVoltage=%u\n", + ddi_bc_hwGetBatteryVoltage()); + mdelay(100); + } else + break; +#endif + } + +#ifndef VDD4P2_ENABLED + /* prepare handoff */ + ddi_power_init_handoff(); +#endif + return ret; +} + +/* + * Use the the lradc channel + * get the die temperature from on-chip sensor. + */ +uint16_t MeasureInternalDieTemperature(void) +{ + uint32_t ch8Value, ch9Value, lradc_irq_mask, channel; + + channel = g_ddi_bc_Configuration.u8BatteryTempChannel; + lradc_irq_mask = 1 << channel; + + /* power up internal tep sensor block */ + __raw_writel(BM_LRADC_CTRL2_TEMPSENSE_PWD, + REGS_LRADC_BASE + HW_LRADC_CTRL2_CLR); + + /* mux to the lradc 8th temp channel */ + __raw_writel((0xF << (4 * channel)), + REGS_LRADC_BASE + HW_LRADC_CTRL4_CLR); + __raw_writel((8 << (4 * channel)), + REGS_LRADC_BASE + HW_LRADC_CTRL4_SET); + + /* Clear the interrupt flag */ + __raw_writel(lradc_irq_mask, + REGS_LRADC_BASE + HW_LRADC_CTRL1_CLR); + __raw_writel(BF_LRADC_CTRL0_SCHEDULE(1 << channel), + REGS_LRADC_BASE + HW_LRADC_CTRL0_SET); + + /* Wait for conversion complete*/ + while (!(__raw_readl(REGS_LRADC_BASE + HW_LRADC_CTRL1) + & lradc_irq_mask)) + cpu_relax(); + + /* Clear the interrupt flag again */ + __raw_writel(lradc_irq_mask, + REGS_LRADC_BASE + HW_LRADC_CTRL1_CLR); + + /* read temperature value and clr lradc */ + ch8Value = __raw_readl(REGS_LRADC_BASE + + HW_LRADC_CHn(channel)) & BM_LRADC_CHn_VALUE; + + + __raw_writel(BM_LRADC_CHn_VALUE, + REGS_LRADC_BASE + HW_LRADC_CHn_CLR(channel)); + + /* mux to the lradc 9th temp channel */ + __raw_writel((0xF << (4 * channel)), + REGS_LRADC_BASE + HW_LRADC_CTRL4_CLR); + __raw_writel((9 << (4 * channel)), + REGS_LRADC_BASE + HW_LRADC_CTRL4_SET); + + /* Clear the interrupt flag */ + __raw_writel(lradc_irq_mask, + REGS_LRADC_BASE + HW_LRADC_CTRL1_CLR); + __raw_writel(BF_LRADC_CTRL0_SCHEDULE(1 << channel), + REGS_LRADC_BASE + HW_LRADC_CTRL0_SET); + /* Wait for conversion complete */ + while (!(__raw_readl(REGS_LRADC_BASE + HW_LRADC_CTRL1) + & lradc_irq_mask)) + cpu_relax(); + + /* Clear the interrupt flag */ + __raw_writel(lradc_irq_mask, + REGS_LRADC_BASE + HW_LRADC_CTRL1_CLR); + /* read temperature value */ + ch9Value = __raw_readl( + REGS_LRADC_BASE + HW_LRADC_CHn(channel)) + & BM_LRADC_CHn_VALUE; + + + __raw_writel(BM_LRADC_CHn_VALUE, + REGS_LRADC_BASE + HW_LRADC_CHn_CLR(channel)); + + /* power down temp sensor block */ + __raw_writel(BM_LRADC_CTRL2_TEMPSENSE_PWD, + REGS_LRADC_BASE + HW_LRADC_CTRL2_SET); + + + return (uint16_t)((ch9Value-ch8Value)*GAIN_CORRECTION/4000); +} + + + +/* Name: ddi_power_GetBatteryMode */ +/* */ +/* brief */ + +ddi_power_BatteryMode_t ddi_power_GetBatteryMode(void) +{ + 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) { + __raw_writel(BM_POWER_CHARGE_PWD_BATTCHRG, + REGS_POWER_BASE + HW_POWER_CHARGE_CLR); + __raw_writel(BM_POWER_5VCTRL_PWD_CHARGE_4P2, + REGS_POWER_BASE + HW_POWER_5VCTRL_CLR); + } else { + __raw_writel(BM_POWER_CHARGE_PWD_BATTCHRG, + REGS_POWER_BASE + HW_POWER_CHARGE_SET); +#ifndef VDD4P2_ENABLED + __raw_writel(BM_POWER_5VCTRL_PWD_CHARGE_4P2, + REGS_POWER_BASE + HW_POWER_5VCTRL_SET); +#endif + } + +/* #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 */ + __raw_writel( + BF_POWER_BATTMONITOR_BRWNOUT_LVL(i16BrownoutLevel), + REGS_POWER_BASE + HW_POWER_BATTMONITOR_SET); + } else + ret = -EINVAL; + + return ret; +} +#endif + + +/* Currents */ + + + + +/* 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); + + /* Compute the toggle mask. */ + u16ToggleMask = u16OldSetting ^ u16NewSetting; + + /* Write to the toggle register.*/ + __raw_writel(u16ToggleMask << BP_POWER_CHARGE_BATTCHRG_I, + REGS_POWER_BASE + HW_POWER_CHARGE_TOG); + + /* 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. */ + __raw_writel(BF_POWER_CHARGE_STOP_ILIMIT(u16ToggleMask), + REGS_POWER_BASE + HW_POWER_CHARGE_TOG); + + /* 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 */ +#ifndef CONFIG_ARCH_MX28 + if (bGate) { + __raw_writel(BM_POWER_CTRL_CLKGATE, + REGS_POWER_BASE + HW_POWER_CTRL_SET); + } else { + __raw_writel(BM_POWER_CTRL_CLKGATE, + REGS_POWER_BASE + HW_POWER_CTRL_CLR); + } +#endif +} + + +/* See hw_power.h for details. */ + +bool ddi_power_GetPowerClkGate(void) +{ +#ifdef CONFIG_ARCH_MX28 + return 0; +#else + return (__raw_readl(REGS_POWER_BASE + HW_POWER_CTRL) & BM_POWER_CTRL_CLKGATE) ? 1 : 0; +#endif +} + + +enum ddi_power_5v_status ddi_power_GetPmu5vStatus(void) +{ + + if (DetectionMethod == DDI_POWER_5V_VDD5V_GT_VDDIO) { + + if (__raw_readl(REGS_POWER_BASE + HW_POWER_CTRL) & + BM_POWER_CTRL_POLARITY_VDD5V_GT_VDDIO) { + if ((__raw_readl(REGS_POWER_BASE + HW_POWER_CTRL) & + BM_POWER_CTRL_VDD5V_GT_VDDIO_IRQ) || + ddi_power_Get5vPresentFlag()) + return new_5v_connection; + else + return existing_5v_disconnection; + } else { + if ((__raw_readl(REGS_POWER_BASE + HW_POWER_CTRL) & + BM_POWER_CTRL_VDD5V_GT_VDDIO_IRQ) || + !ddi_power_Get5vPresentFlag() || + ddi_power_Get5vDroopFlag()) + return new_5v_disconnection; + else + return existing_5v_connection; + } + } else { + + if (__raw_readl(REGS_POWER_BASE + HW_POWER_CTRL) & + BM_POWER_CTRL_POLARITY_VBUSVALID) { + if ((__raw_readl(REGS_POWER_BASE + HW_POWER_CTRL) & + BM_POWER_CTRL_VBUSVALID_IRQ) || + ddi_power_Get5vPresentFlag()) + return new_5v_connection; + else + return existing_5v_disconnection; + } else { + if ((__raw_readl(REGS_POWER_BASE + HW_POWER_CTRL) & + BM_POWER_CTRL_VBUSVALID_IRQ) || + !ddi_power_Get5vPresentFlag() || + ddi_power_Get5vDroopFlag()) + return new_5v_disconnection; + else + return existing_5v_connection; + } + + } +} + +void ddi_power_disable_5v_connection_irq(void) +{ + + __raw_writel((BM_POWER_CTRL_ENIRQ_VBUS_VALID | + BM_POWER_CTRL_ENIRQ_VDD5V_GT_VDDIO), + REGS_POWER_BASE + HW_POWER_CTRL_CLR); +} + +void ddi_power_enable_5v_disconnect_detection(void) +{ + __raw_writel(BM_POWER_CTRL_POLARITY_VDD5V_GT_VDDIO | + BM_POWER_CTRL_POLARITY_VBUSVALID, + REGS_POWER_BASE + HW_POWER_CTRL_CLR); + + __raw_writel(BM_POWER_CTRL_VDD5V_GT_VDDIO_IRQ | + BM_POWER_CTRL_VBUSVALID_IRQ, + REGS_POWER_BASE + HW_POWER_CTRL_CLR); + + if (DetectionMethod == DDI_POWER_5V_VDD5V_GT_VDDIO) { + __raw_writel(BM_POWER_CTRL_ENIRQ_VDD5V_GT_VDDIO, + REGS_POWER_BASE + HW_POWER_CTRL_SET); + } else { + __raw_writel(BM_POWER_CTRL_ENIRQ_VBUS_VALID, + REGS_POWER_BASE + HW_POWER_CTRL_SET); + } +} + +void ddi_power_enable_5v_connect_detection(void) +{ + __raw_writel(BM_POWER_CTRL_POLARITY_VDD5V_GT_VDDIO | + BM_POWER_CTRL_POLARITY_VBUSVALID, + REGS_POWER_BASE + HW_POWER_CTRL_SET); + + __raw_writel(BM_POWER_CTRL_VDD5V_GT_VDDIO_IRQ | + BM_POWER_CTRL_VBUSVALID_IRQ, + REGS_POWER_BASE + HW_POWER_CTRL_CLR); + + if (DetectionMethod == DDI_POWER_5V_VDD5V_GT_VDDIO) { + __raw_writel(BM_POWER_CTRL_ENIRQ_VDD5V_GT_VDDIO, + REGS_POWER_BASE + HW_POWER_CTRL_SET); + } else { + __raw_writel(BM_POWER_CTRL_ENIRQ_VBUS_VALID, + REGS_POWER_BASE + HW_POWER_CTRL_SET); + } +} + +void ddi_power_EnableBatteryBoInterrupt(bool bEnable) +{ + if (bEnable) { + + __raw_writel(BM_POWER_CTRL_BATT_BO_IRQ, + REGS_POWER_BASE + HW_POWER_CTRL_CLR); + __raw_writel(BM_POWER_CTRL_ENIRQBATT_BO, + REGS_POWER_BASE + HW_POWER_CTRL_SET); + /* todo: make sure the battery brownout comparator + * is enabled in HW_POWER_BATTMONITOR + */ + } else { + __raw_writel(BM_POWER_CTRL_ENIRQBATT_BO, + REGS_POWER_BASE + HW_POWER_CTRL_CLR); + } +} + +void ddi_power_EnableDcdc4p2BoInterrupt(bool bEnable) +{ + if (bEnable) { + + __raw_writel(BM_POWER_CTRL_DCDC4P2_BO_IRQ, + REGS_POWER_BASE + HW_POWER_CTRL_CLR); + __raw_writel(BM_POWER_CTRL_ENIRQ_DCDC4P2_BO, + REGS_POWER_BASE + HW_POWER_CTRL_SET); + } else { + __raw_writel(BM_POWER_CTRL_ENIRQ_DCDC4P2_BO, + REGS_POWER_BASE + HW_POWER_CTRL_CLR); + } +} + +void ddi_power_EnableVdd5vDroopInterrupt(bool bEnable) +{ + if (bEnable) { + + __raw_writel(BM_POWER_CTRL_VDD5V_DROOP_IRQ, + REGS_POWER_BASE + HW_POWER_CTRL_CLR); + __raw_writel(BM_POWER_CTRL_ENIRQ_VDD5V_DROOP, + REGS_POWER_BASE + HW_POWER_CTRL_SET); + } else { + __raw_writel(BM_POWER_CTRL_ENIRQ_VDD5V_DROOP, + REGS_POWER_BASE + HW_POWER_CTRL_CLR); + } +} + + +void ddi_power_Enable5vDisconnectShutdown(bool bEnable) +{ + if (bEnable) { + __raw_writel(BM_POWER_5VCTRL_PWDN_5VBRNOUT, + REGS_POWER_BASE + HW_POWER_5VCTRL_SET); + } else { + __raw_writel(BM_POWER_5VCTRL_PWDN_5VBRNOUT, + REGS_POWER_BASE + HW_POWER_5VCTRL_CLR); + } +} + + +void ddi_power_enable_5v_to_battery_xfer(bool bEnable) +{ + if (bEnable) { + /* order matters */ + + /* we can enable this in in vbus droop or 4p2 fiq handler + * ddi_power_EnableBatteryBoInterrupt(true); + */ + ddi_power_Enable5vDisconnectShutdown(false); + } else { + /* order matters */ + ddi_power_Enable5vDisconnectShutdown(true); + ddi_power_EnableBatteryBoInterrupt(false); + } +} + + +void ddi_power_init_4p2_protection(void) +{ + /* set vbus droop detection level to 4.3V */ + __raw_writel(BM_POWER_5VCTRL_VBUSDROOP_TRSH, + REGS_POWER_BASE + HW_POWER_5VCTRL_CLR); + + /* VBUSDROOP THRESHOLD to 4.3V */ + __raw_writel(BM_POWER_5VCTRL_VBUSDROOP_TRSH, + REGS_POWER_BASE + HW_POWER_5VCTRL_CLR); + + ddi_power_EnableVbusDroopIrq(); + +#ifndef CONFIG_ARCH_MX28 + /* VBUSVALID THRESH = 2.9V */ + __raw_writel(BM_POWER_5VCTRL_VBUSVALID_TRSH, + REGS_POWER_BASE + HW_POWER_5VCTRL_CLR); +#endif + +} + +/* determine if all the bits are in a 'DCDC 4P2 Enabled' state. */ +bool ddi_power_check_4p2_bits(void) +{ + + + uint32_t temp; + + temp = __raw_readl(REGS_POWER_BASE + HW_POWER_5VCTRL) & + BM_POWER_5VCTRL_PWD_CHARGE_4P2; + + /* if PWD_CHARGE_4P2 = 1, 4p2 is disabled */ + if (temp) + return false; + + temp = __raw_readl(REGS_POWER_BASE + HW_POWER_DCDC4P2) & + BM_POWER_DCDC4P2_ENABLE_DCDC; + + if (!temp) + return false; + + temp = __raw_readl(REGS_POWER_BASE + HW_POWER_DCDC4P2) & + BM_POWER_DCDC4P2_ENABLE_4P2; + + if (temp) + return true; + else + return false; + +} + +uint16_t ddi_power_set_4p2_ilimit(uint16_t ilimit) +{ + uint32_t temp_reg; + + if (ilimit > 780) + ilimit = 780; + temp_reg = __raw_readl(REGS_POWER_BASE + HW_POWER_5VCTRL); + temp_reg &= (~BM_POWER_5VCTRL_CHARGE_4P2_ILIMIT); + temp_reg |= BF_POWER_5VCTRL_CHARGE_4P2_ILIMIT( + ddi_power_convert_current_to_setting( + ilimit)); + __raw_writel(temp_reg, REGS_POWER_BASE + HW_POWER_5VCTRL); + + return ilimit; +} + +void ddi_power_shutdown(void) +{ + __raw_writel(0x3e770001, REGS_POWER_BASE + HW_POWER_RESET); +} + +void ddi_power_handle_dcdc4p2_bo(void) +{ + ddi_power_EnableBatteryBoInterrupt(true); + ddi_power_EnableDcdc4p2BoInterrupt(false); +} + +void ddi_power_enable_vddio_interrupt(bool enable) +{ + if (enable) { + __raw_writel(BM_POWER_CTRL_VDDIO_BO_IRQ, + REGS_POWER_BASE + HW_POWER_CTRL_CLR); +#ifndef DISABLE_VDDIO_BO_PROTECTION + __raw_writel(BM_POWER_CTRL_ENIRQ_VDDIO_BO, + REGS_POWER_BASE + HW_POWER_CTRL_SET); +#endif + } else { + __raw_writel(BM_POWER_CTRL_ENIRQ_VDDIO_BO, + REGS_POWER_BASE + HW_POWER_CTRL_CLR); + } + +} + + +void ddi_power_handle_vddio_brnout(void) +{ + if (ddi_power_GetPmu5vStatus() == new_5v_connection || + (ddi_power_GetPmu5vStatus() == new_5v_disconnection)) { + ddi_power_enable_vddio_interrupt(false); + } else { +#ifdef DEBUG_IRQS + ddi_power_enable_vddio_interrupt(false); + printk(KERN_ALERT "VDDIO BO TRIED TO SHUTDOWN!!!\n"); + return; +#else + ddi_power_shutdown(); +#endif + } +} + +void ddi_power_handle_vdd5v_droop(void) +{ + uint32_t temp; + + /* handle errata */ + temp = __raw_readl(REGS_POWER_BASE + HW_POWER_DCDC4P2); + temp |= (BF_POWER_DCDC4P2_CMPTRIP(31) | BM_POWER_DCDC4P2_TRG); + __raw_writel(temp, REGS_POWER_BASE + HW_POWER_DCDC4P2); + + + /* if battery is below brownout level, shutdown asap */ + if (__raw_readl(REGS_POWER_BASE + HW_POWER_STS) & BM_POWER_STS_BATT_BO) + ddi_power_shutdown(); + + /* due to 5v connect vddio bo chip bug, we need to + * disable vddio interrupts until we reset the 5v + * detection for 5v connect detect. We want to allow + * some debounce time before enabling connect detection. + */ + ddi_power_enable_vddio_interrupt(false); + + ddi_power_EnableBatteryBoInterrupt(true); + ddi_power_EnableDcdc4p2BoInterrupt(false); + ddi_power_EnableVdd5vDroopInterrupt(false); + +} + +void ddi_power_InitOutputBrownouts(void) +{ + uint32_t temp; + + __raw_writel(BM_POWER_CTRL_VDDD_BO_IRQ | + BM_POWER_CTRL_VDDA_BO_IRQ | + BM_POWER_CTRL_VDDIO_BO_IRQ, + REGS_POWER_BASE + HW_POWER_CTRL_CLR); + + __raw_writel(BM_POWER_CTRL_ENIRQ_VDDD_BO | + BM_POWER_CTRL_ENIRQ_VDDA_BO | + BM_POWER_CTRL_ENIRQ_VDDIO_BO, + REGS_POWER_BASE + HW_POWER_CTRL_SET); + + temp = __raw_readl(REGS_POWER_BASE + HW_POWER_VDDDCTRL); + temp &= ~BM_POWER_VDDDCTRL_PWDN_BRNOUT; + __raw_writel(temp, REGS_POWER_BASE + HW_POWER_VDDDCTRL); + + temp = __raw_readl(REGS_POWER_BASE + HW_POWER_VDDACTRL); + temp &= ~BM_POWER_VDDACTRL_PWDN_BRNOUT; + __raw_writel(temp, REGS_POWER_BASE + HW_POWER_VDDACTRL); + + temp = __raw_readl(REGS_POWER_BASE + HW_POWER_VDDIOCTRL); + temp &= ~BM_POWER_VDDIOCTRL_PWDN_BRNOUT; + __raw_writel(temp, REGS_POWER_BASE + HW_POWER_VDDIOCTRL); +} + +/* used for debugging purposes only */ +void ddi_power_disable_power_interrupts(void) +{ + __raw_writel(BM_POWER_CTRL_ENIRQ_DCDC4P2_BO | + BM_POWER_CTRL_ENIRQ_VDD5V_DROOP | + BM_POWER_CTRL_ENIRQ_PSWITCH | + BM_POWER_CTRL_ENIRQ_DC_OK | + BM_POWER_CTRL_ENIRQBATT_BO | + BM_POWER_CTRL_ENIRQ_VDDIO_BO | + BM_POWER_CTRL_ENIRQ_VDDA_BO | + BM_POWER_CTRL_ENIRQ_VDDD_BO | + BM_POWER_CTRL_ENIRQ_VBUS_VALID | + BM_POWER_CTRL_ENIRQ_VDD5V_GT_VDDIO, + REGS_POWER_BASE + HW_POWER_CTRL_CLR); + +} + +bool ddi_power_Get5vDroopFlag(void) +{ + if (__raw_readl(REGS_POWER_BASE + HW_POWER_STS) & + BM_POWER_STS_VDD5V_DROOP) + return true; + else + return false; +} + + +/* End of file */ + +/* @} */ diff --git a/drivers/power/mxs/ddi_power_battery.h b/drivers/power/mxs/ddi_power_battery.h new file mode 100644 index 000000000000..6a25569f25d3 --- /dev/null +++ b/drivers/power/mxs/ddi_power_battery.h @@ -0,0 +1,95 @@ +/* + * Copyright (C) 2010 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 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; + + +enum ddi_power_5v_status { + new_5v_connection, + existing_5v_connection, + new_5v_disconnection, + existing_5v_disconnection, +} ; + + +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); +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); +enum ddi_power_5v_status ddi_power_GetPmu5vStatus(void); +void ddi_power_EnableBatteryBoFiq(bool bEnable); +void ddi_power_disable_5v_connection_irq(void); +void ddi_power_enable_5v_disconnect_detection(void); +void ddi_power_enable_5v_connect_detection(void); +void ddi_power_Enable5vDisconnectShutdown(bool bEnable); +void ddi_power_enable_5v_to_battery_xfer(bool bEnable); +void ddi_power_init_4p2_protection(void); +bool ddi_power_check_4p2_bits(void); +void ddi_power_Start4p2Dcdc(bool battery_ready); +void ddi_power_Init4p2Params(void); +bool ddi_power_IsBattRdyForXfer(void); +void ddi_power_EnableVbusDroopIrq(void); +void ddi_power_Enable4p2(uint16_t target_current_limit_ma); +uint16_t ddi_power_BringUp4p2Regulator( + uint16_t target_current_limit_ma, + bool b4p2_dcdc_enabled); +void ddi_power_Set4p2BoLevel(uint16_t bo_voltage_mv); +void ddi_power_EnableBatteryBoInterrupt(bool bEnable); +void ddi_power_handle_cmptrip(void); +uint16_t ddi_power_set_4p2_ilimit(uint16_t ilimit); +void ddi_power_shutdown(void); +void ddi_power_handle_dcdc4p2_bo(void); +void ddi_power_enable_vddio_interrupt(bool enable); +void ddi_power_handle_vddio_brnout(void); +void ddi_power_EnableDcdc4p2BoInterrupt(bool bEnable); +void ddi_power_handle_vdd5v_droop(void); +void ddi_power_InitOutputBrownouts(void); +void ddi_power_disable_power_interrupts(void); +bool ddi_power_Get5vDroopFlag(void); diff --git a/drivers/power/mxs/fiq.S b/drivers/power/mxs/fiq.S new file mode 100644 index 000000000000..1ad380d07efd --- /dev/null +++ b/drivers/power/mxs/fiq.S @@ -0,0 +1,125 @@ +/* + * Copyright (C) 2009-2010 Freescale Semiconductor, Inc. All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + + +#include <linux/linkage.h> +#include <asm/assembler.h> +#include <mach/hardware.h> +#include <asm/pgtable-hwdef.h> +#include <mach/regs-power.h> +#include <mach/../../regs-clkctrl.h> +#include <mach/regs-timrot.h> + + .align 5 + .globl power_fiq_start + .globl power_fiq_end + .globl power_fiq_count + .globl lock_vector_tlb + +power_fiq_start: + ldr r8,power_reg + ldr r9,[r8,#HW_POWER_CTRL ] + ldr r10,power_off + + @ when VDDIO_BO_IRQ, + @ disabled, handled in IRQ for now + @tst r9, #BM_POWER_CTRL_VDDIO_BO_IRQ + + + @ when BATT_BO_IRQ, VDDD_BO_IRQ, VDDA_BO_IRQ, power off chip + ldr r11,power_bo + tst r9, r11 + strne r10,[r8,#HW_POWER_RESET] + + @VDD5V_DROOP_IRQ + tst r9, #BM_POWER_CTRL_VDD5V_DROOP_IRQ + beq check_dcdc4p2 + + @ handle errata + ldr r10, [r8, #HW_POWER_DCDC4P2] + orr r10,r10,#(BM_POWER_DCDC4P2_TRG) + orr r10,r10,#(BF_POWER_DCDC4P2_CMPTRIP(31)) + str r10,[r8, #(HW_POWER_DCDC4P2)] + + @ if battery is below brownout level, shutdown asap + ldr r10, [r8, #HW_POWER_STS] + tst r10, #BM_POWER_STS_BATT_BO + ldr r10, power_off + strne r10, [r8, #HW_POWER_RESET] + + @ disable viddio irq + mov r11, #BM_POWER_CTRL_ENIRQ_VDDIO_BO + str r11, [r8, #HW_POWER_CTRL_CLR] + + @ enable battery BO irq + mov r11, #BM_POWER_CTRL_BATT_BO_IRQ + str r11, [r8, #HW_POWER_CTRL_CLR] + mov r11, #BM_POWER_CTRL_ENIRQBATT_BO + str r11, [r8, #HW_POWER_CTRL_SET] + + @ disable dcdc4p2 interrupt + mov r11, #BM_POWER_CTRL_ENIRQ_DCDC4P2_BO + str r11, [r8, #HW_POWER_CTRL_CLR] + + @ disable vdd5v_droop interrupt + mov r11, #BM_POWER_CTRL_ENIRQ_VDD5V_DROOP + str r11, [r8, #HW_POWER_CTRL_CLR] + +check_dcdc4p2: + @ when DCDC4P2_BO_IRQ, + tst r9, #BM_POWER_CTRL_DCDC4P2_BO_IRQ + + mov r11, #BM_POWER_CTRL_BATT_BO_IRQ + strne r11, [r8, #HW_POWER_CTRL_CLR] + + mov r11, #BM_POWER_CTRL_ENIRQBATT_BO + strne r11, [r8, #HW_POWER_CTRL_SET] + + mov r11, #BM_POWER_CTRL_ENIRQ_DCDC4P2_BO + strne r11, [r8, #HW_POWER_CTRL_CLR] + + + + @return from fiq + subs pc,lr, #4 + +power_reg: + .long IO_ADDRESS(POWER_PHYS_ADDR) +power_off: + .long 0x3e770001 +power_bo: + .long BM_POWER_CTRL_BATT_BO_IRQ | \ + BM_POWER_CTRL_VDDA_BO_IRQ | BM_POWER_CTRL_VDDD_BO_IRQ +power_fiq_count: + .long 0 +power_fiq_end: + +lock_vector_tlb: + + mov r1, r0 @ set r1 to the value of the address to be locked down + mcr p15,0,r1,c8,c7,1 @ invalidate TLB single entry to ensure that + @ LockAddr is not already in the TLB + mrc p15,0,r0,c10,c0,0 @ read the lockdown register + orr r0,r0,#1 @ set the preserve bit + mcr p15,0,r0,c10,c0,0 @ write to the lockdown register + ldr r1,[r1] @ TLB will miss, and entry will be loaded + mrc p15,0,r0,c10,c0,0 @ read the lockdown register (victim will have + @ incremented) + bic r0,r0,#1 @ clear preserve bit + mcr p15,0,r0,c10,c0,0 @ write to the lockdown registerADR r1,LockAddr + mov pc,lr @ diff --git a/drivers/power/mxs/linux.c b/drivers/power/mxs/linux.c new file mode 100644 index 000000000000..1c2dfc10f7ca --- /dev/null +++ b/drivers/power/mxs/linux.c @@ -0,0 +1,1182 @@ +/* + * Linux glue to MXS battery state machine. + * + * Author: Steve Longerbeam <stevel@embeddedalley.com> + * + * Copyright (C) 2008 EmbeddedAlley Solutions Inc. + * Copyright 2008-2010 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/io.h> +#include <linux/sched.h> +#include <linux/clk.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/hardware.h> +#include <mach/irqs.h> +#include <mach/clock.h> +#include <linux/delay.h> +#include <linux/proc_fs.h> +#include <linux/interrupt.h> +#include <asm/fiq.h> + +enum application_5v_status{ + _5v_connected_verified, + _5v_connected_unverified, + _5v_disconnected_unverified, + _5v_disconnected_verified, +}; + +struct mxs_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 *irq_vdd5v; + struct resource *irq_dcdc4p2_bo; + struct resource *irq_batt_brnout; + struct resource *irq_vddd_brnout; + struct resource *irq_vdda_brnout; + struct resource *irq_vddio_brnout; + struct resource *irq_vdd5v_droop; + int is_ac_online; + int source_protection_mode; + uint32_t sm_new_5v_connection_jiffies; + uint32_t sm_new_5v_disconnection_jiffies; + enum application_5v_status sm_5v_connection_status; + + + + +#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_mxs_info(x) container_of((x), struct mxs_info, bat) + +#ifndef NON_USB_5V_SUPPLY_CURRENT_LIMIT_MA +#define NON_USB_5V_SUPPLY_CURRENT_LIMIT_MA 780 +#endif + +#ifndef POWERED_USB_5V_CURRENT_LIMIT_MA +#define POWERED_USB_5V_CURRENT_LIMIT_MA 450 +#endif + +#ifndef UNPOWERED_USB_5V_CURRENT_LIMIT_MA +#define UNPOWERED_USB_5V_CURRENT_LIMIT_MA 80 +#endif + +#ifndef _5V_DEBOUNCE_TIME_MS +#define _5V_DEBOUNCE_TIME_MS 500 +#endif + +#ifndef OS_SHUTDOWN_BATTERY_VOLTAGE_THRESHOLD_MV +#define OS_SHUTDOWN_BATTERY_VOLTAGE_THRESHOLD_MV 3350 +#endif + +#ifdef CONFIG_ARCH_MX23 +#define IRQ_DCDC4P2_BRNOUT IRQ_DCDC4P2_BO +#endif + +#define POWER_FIQ + +/* #define DEBUG_IRQS */ + +/* 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_ac_online() \ + (ddi_power_Get5vPresentFlag() ? (!fsl_is_usb_plugged()) : 0) +#define is_usb_online() \ + (ddi_power_Get5vPresentFlag() ? (!!fsl_is_usb_plugged()) : 0) + + + +void init_protection(struct mxs_info *info) +{ + enum ddi_power_5v_status pmu_5v_status; + uint16_t battery_voltage; + + pmu_5v_status = ddi_power_GetPmu5vStatus(); + battery_voltage = ddi_power_GetBattery(); + + /* InitializeFiqSystem(); */ + ddi_power_InitOutputBrownouts(); + + + /* if we start the kernel with 4p2 already started + * by the bootlets, we need to hand off from this + * state to the kernel 4p2 enabled state. + */ + if ((pmu_5v_status == existing_5v_connection) && + ddi_power_check_4p2_bits()) { + ddi_power_enable_5v_disconnect_detection(); + + /* includes VBUS DROOP workaround for errata */ + ddi_power_init_4p2_protection(); + + /* if we still have our 5V connection, we can disable + * battery brownout interrupt. This is because the + * VDD5V DROOP IRQ handler will also shutdown if battery + * is browned out and it will enable the battery brownout + * and bring VBUSVALID_TRSH level back to a normal level + * which caused the hardware battery brownout shutdown + * to be enabled. The benefit of this is that device + * that have detachable batteries (or devices going through + * the assembly line and running this firmware to test + * with) can avoid shutting down if 5V is present and + * battery voltage goes away. + */ + ddi_power_EnableBatteryBoInterrupt(false); + + info->sm_5v_connection_status = _5v_connected_verified; + } else { +#ifdef DEBUG_IRQS + if (battery_voltage < + OS_SHUTDOWN_BATTERY_VOLTAGE_THRESHOLD_MV) { + printk(KERN_CRIT "Polled battery voltage measurement is\ + less than %dmV. Kernel should be halted/\ + shutdown\n", + OS_SHUTDOWN_BATTERY_VOLTAGE_THRESHOLD_MV); + + return; + } +#endif + info->sm_5v_connection_status = _5v_disconnected_verified; + ddi_power_EnableBatteryBoInterrupt(true); + + } + + + /* all brownouts are now handled software fiqs. We + * can now disable the hardware protection mechanisms + * because leaving them on yields ~2kV ESD level + * versus ~4kV ESD levels when they are off. This + * difference is suspected to be cause by the fast + * falling edge pswitch functionality being tripped + * by ESD events. This functionality is disabled + * when PWD_OFF is disabled. + */ +#ifdef DISABLE_HARDWARE_PROTECTION_MECHANISMS + __raw_writel(BM_POWER_RESET_PWD_OFF, + HW_POWER_RESET_SET_ADDR); +#endif + + + + +} + + + +static void check_and_handle_5v_connection(struct mxs_info *info) +{ + + switch (ddi_power_GetPmu5vStatus()) { + + case new_5v_connection: + ddi_power_enable_5v_disconnect_detection(); + info->sm_5v_connection_status = _5v_connected_unverified; + + case existing_5v_connection: + if (info->sm_5v_connection_status != _5v_connected_verified) { + /* we allow some time to pass before considering + * the 5v connection to be ready to use. This + * will give the USB system time to enumerate + * (coordination with USB driver to be added + * in the future). + */ + + /* handle jiffies rollover case */ + if ((jiffies - info->sm_new_5v_connection_jiffies) + < 0) { + info->sm_new_5v_connection_jiffies = jiffies; + break; + } + + if ((jiffies_to_msecs(jiffies - + info->sm_new_5v_connection_jiffies)) > + _5V_DEBOUNCE_TIME_MS) { + info->sm_5v_connection_status = + _5v_connected_verified; + dev_dbg(info->dev, + "5v connection verified\n"); +#ifdef CONFIG_MXS_VBUS_CURRENT_DRAW + #ifdef CONFIG_USB_GADGET + /* if there is USB 2.0 current limitation requirement, + * waiting for USB enum done. + */ + if ((__raw_readl(REGS_POWER_BASE + HW_POWER_5VCTRL) + & BM_POWER_5VCTRL_CHARGE_4P2_ILIMIT) == + (0x20 << BP_POWER_5VCTRL_CHARGE_4P2_ILIMIT)) { + dev_info(info->dev, "waiting USB enum done...\r\n"); + } + while ((__raw_readl(REGS_POWER_BASE + HW_POWER_5VCTRL) + & BM_POWER_5VCTRL_CHARGE_4P2_ILIMIT) + == (0x20 << BP_POWER_5VCTRL_CHARGE_4P2_ILIMIT)) { + msleep(50); + } + #endif +#endif + ddi_power_Enable4p2(450); + + /* part of handling for errata. It is + * now "somewhat" safe to + * turn on vddio interrupts again + */ + ddi_power_enable_vddio_interrupt(true); + } + } + break; + + case new_5v_disconnection: + + ddi_bc_SetDisable(); + ddi_bc_SetCurrentLimit(0); + if (info->regulator) + regulator_set_current_limit(info->regulator, 0, 0); + info->is_usb_online = 0; + info->is_ac_online = 0; + + info->sm_5v_connection_status = _5v_disconnected_unverified; + + case existing_5v_disconnection: + + if (info->sm_5v_connection_status != + _5v_disconnected_verified) { + if ((jiffies - info->sm_new_5v_disconnection_jiffies) + < 0) { + info->sm_new_5v_connection_jiffies = jiffies; + break; + } + + if ((jiffies_to_msecs(jiffies - + info->sm_new_5v_disconnection_jiffies)) > + _5V_DEBOUNCE_TIME_MS) { + info->sm_5v_connection_status = + _5v_disconnected_verified; + ddi_power_execute_5v_to_battery_handoff(); + ddi_power_enable_5v_connect_detection(); + + /* part of handling for errata. + * It is now safe to + * turn on vddio interrupts again + */ + ddi_power_enable_vddio_interrupt(true); + dev_dbg(info->dev, + "5v disconnection handled\n"); + + __raw_writel(__raw_readl(REGS_POWER_BASE + + HW_POWER_5VCTRL) & + (~BM_POWER_5VCTRL_CHARGE_4P2_ILIMIT) + | (0x20 << BP_POWER_5VCTRL_CHARGE_4P2_ILIMIT), + REGS_POWER_BASE + HW_POWER_5VCTRL); + + } + } + + break; + } +} + + +static void handle_battery_voltage_changes(struct mxs_info *info) +{ +#if 0 + uint16_t battery_voltage; + + battery_voltage = ddi_power_GetBattery(); + + if (info->sm_5v_connection_status != _5v_connected_verified) { + if (battery_voltage < + OS_SHUTDOWN_BATTERY_VOLTAGE_THRESHOLD_MV) { + printk(KERN_CRIT "Polled battery voltage measurement is\ + less than %dmV. Shutting down the \ + system\n", + OS_SHUTDOWN_BATTERY_VOLTAGE_THRESHOLD_MV); + + shutdown_os(); + return; + } + } else +#endif + { + ddi_power_handle_cmptrip(); + + if (ddi_power_IsBattRdyForXfer()) + ddi_power_enable_5v_to_battery_xfer(true); + else + ddi_power_enable_5v_to_battery_xfer(false); + + } +} + + +/* + * Power properties + */ +static enum power_supply_property mxs_power_props[] = { + POWER_SUPPLY_PROP_ONLINE, +}; + +static int mxs_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 mxs_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 mxs_bat_get_property(struct power_supply *psy, + enum power_supply_property psp, + union power_supply_propval *val) +{ + struct mxs_info *info = to_mxs_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 mxs_info *info = (struct mxs_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"); + +} +/* + * Assumption: + * 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 mxs_info *info = + container_of(work, struct mxs_info, sm_work); + + mutex_lock(&info->sm_lock); + + handle_battery_voltage_changes(info); + + check_and_handle_5v_connection(info); + + if ((info->sm_5v_connection_status != _5v_connected_verified) || + !(info->regulator)) { + mod_timer(&info->sm_timer, jiffies + msecs_to_jiffies(100)); + goto out; + } + + /* if we made it here, we have a verified 5v connection */ +#ifndef CONFIG_MXS_VBUS_CURRENT_DRAW + if (info->is_ac_online) + goto done; + + /* ac supply connected */ + dev_dbg(info->dev, "changed power connection to ac/5v.\n)"); + dev_dbg(info->dev, "5v current limit set to %u.\n", + NON_USB_5V_SUPPLY_CURRENT_LIMIT_MA); + + info->is_ac_online = 1; + info->is_usb_online = 0; + ddi_power_set_4p2_ilimit( + NON_USB_5V_SUPPLY_CURRENT_LIMIT_MA); + ddi_bc_SetCurrentLimit( + NON_USB_5V_SUPPLY_CURRENT_LIMIT_MA /*mA*/); + if (regulator_set_current_limit(info->regulator, + 0, + NON_USB_5V_SUPPLY_CURRENT_LIMIT_MA*1000)) { + dev_err(info->dev, "reg_set_current(%duA) failed\n", + NON_USB_5V_SUPPLY_CURRENT_LIMIT_MA*1000); + } + ddi_bc_SetEnable(); + goto done; +#else + + 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->is_usb_online & USB_N_SEND)) { + info->is_usb_online |= USB_N_SEND; + } + + + dev_dbg(info->dev, "%s: charge current set to %dmA\n", __func__, + POWERED_USB_5V_CURRENT_LIMIT_MA); + + if (regulator_set_current_limit(info->regulator, + 0, + POWERED_USB_5V_CURRENT_LIMIT_MA*1000)) { + dev_err(info->dev, "reg_set_current(%duA) failed\n", + POWERED_USB_5V_CURRENT_LIMIT_MA*1000); + } else { + ddi_bc_SetCurrentLimit(POWERED_USB_5V_CURRENT_LIMIT_MA/*mA*/); + ddi_bc_SetEnable(); + } + + 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; + +#endif + dev_dbg(info->dev, "changed power connection to usb/5v present\n"); + +done: + ddi_bc_StateMachine(); +out: + mutex_unlock(&info->sm_lock); +} + + + +static int bc_sm_restart(struct mxs_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, "battery charger init failed: %d\n", bcret); + ret = -EIO; + goto out; + } else { + + 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; + } else { + regulator_set_current_limit( + info->regulator, 0, 0); + regulator_set_mode(info->regulator, + REGULATOR_MODE_FAST); + } + } + } + + + + /* schedule first call to state machine */ + mod_timer(&info->sm_timer, jiffies + 1); +out: + mutex_unlock(&info->sm_lock); + return ret; +} + +#ifndef POWER_FIQ + +static irqreturn_t mxs_irq_dcdc4p2_bo(int irq, void *cookie) +{ +#ifdef DEBUG_IRQS + struct mxs_info *info = (struct mxs_info *)cookie; + dev_info(info->dev, "dcdc4p2 brownout interrupt occurred\n"); + +#endif + ddi_power_handle_dcdc4p2_bo(); + return IRQ_HANDLED; +} + +static irqreturn_t mxs_irq_batt_brnout(int irq, void *cookie) +{ +#ifdef DEBUG_IRQS + struct mxs_info *info = (struct mxs_info *)cookie; + dev_info(info->dev, "battery brownout interrupt occurred\n"); + ddi_power_disable_power_interrupts(); +#else + ddi_power_shutdown(); +#endif + return IRQ_HANDLED; +} + + +static irqreturn_t mxs_irq_vddd_brnout(int irq, void *cookie) +{ +#ifdef DEBUG_IRQS + struct mxs_info *info = (struct mxs_info *)cookie; + dev_info(info->dev, "vddd brownout interrupt occurred\n"); + ddi_power_disable_power_interrupts(); +#else + ddi_power_shutdown(); +#endif + return IRQ_HANDLED; +} +static irqreturn_t mxs_irq_vdda_brnout(int irq, void *cookie) +{ +#ifdef DEBUG_IRQS + struct mxs_info *info = (struct mxs_info *)cookie; + dev_info(info->dev, "vdda brownout interrupt occurred\n"); + ddi_power_disable_power_interrupts(); +#else + ddi_power_shutdown(); +#endif + return IRQ_HANDLED; +} + +static irqreturn_t mxs_irq_vdd5v_droop(int irq, void *cookie) +{ +#ifdef DEBUG_IRQS + struct mxs_info *info = (struct mxs_info *)cookie; + dev_info(info->dev, "vdd5v droop interrupt occurred\n"); +#endif + ddi_power_handle_vdd5v_droop(); + + return IRQ_HANDLED; +} + +#endif /* if POWER_FIQ */ + +static irqreturn_t mxs_irq_vddio_brnout(int irq, void *cookie) +{ +#ifdef DEBUG_IRQS + struct mxs_info *info = (struct mxs_info *)cookie; + dev_info(info->dev, "vddio brownout interrupt occurred\n"); + ddi_power_disable_power_interrupts(); +#else + ddi_power_handle_vddio_brnout(); +#endif + return IRQ_HANDLED; +} + +static irqreturn_t mxs_irq_vdd5v(int irq, void *cookie) +{ + struct mxs_info *info = (struct mxs_info *)cookie; + + switch (ddi_power_GetPmu5vStatus()) { + + case new_5v_connection: + + ddi_power_disable_5v_connection_irq(); + dev_dbg(info->dev, "new 5v connection detected\n"); + info->sm_new_5v_connection_jiffies = jiffies; + mod_timer(&info->sm_timer, jiffies + 1); + break; + + case new_5v_disconnection: + + /* due to 5v connect vddio bo chip bug, we need to + * disable vddio interrupts until we reset the 5v + * detection for 5v connect detect. We want to allow + * some debounce time before enabling connect detection. + * This is handled in the vdd5v_droop interrupt for now. + */ + /* ddi_power_enable_vddio_interrupt(false); */ + + ddi_power_disable_5v_connection_irq(); + dev_dbg(info->dev, "new 5v disconnection detected\n"); + info->sm_new_5v_disconnection_jiffies = jiffies; + mod_timer(&info->sm_timer, jiffies + 1); + break; + + default: + + break; + + } + + return IRQ_HANDLED; +} + +static int mxs_bat_probe(struct platform_device *pdev) +{ + struct mxs_info *info; + int ret = 0; + + + /* enable usb device presence detection */ + fsl_enable_usb_plugindetect(); + + ret = ddi_power_init_battery(); + if (ret) { + printk(KERN_ERR "Aborting power driver initialization\n"); + return 1; + } + + + 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->irq_vdd5v = platform_get_resource(pdev, IORESOURCE_IRQ, 0); + if (info->irq_vdd5v == NULL) { + printk(KERN_ERR "%s: failed to get irq resouce\n", __func__); + goto free_info; + } + + info->irq_vddio_brnout = platform_get_resource( + pdev, IORESOURCE_IRQ, 5); + if (info->irq_vddio_brnout == NULL) { + printk(KERN_ERR "%s: failed to get irq resouce\n", __func__); + goto free_info; + } + +#ifndef POWER_FIQ + info->irq_dcdc4p2_bo = platform_get_resource(pdev, IORESOURCE_IRQ, 1); + if (info->irq_dcdc4p2_bo == NULL) { + printk(KERN_ERR "%s: failed to get irq resouce\n", __func__); + goto free_info; + } + + info->irq_batt_brnout = platform_get_resource(pdev, IORESOURCE_IRQ, 2); + if (info->irq_batt_brnout == NULL) { + printk(KERN_ERR "%s: failed to get irq resouce\n", __func__); + goto free_info; + } + + info->irq_vddd_brnout = platform_get_resource(pdev, IORESOURCE_IRQ, 3); + if (info->irq_vddd_brnout == NULL) { + printk(KERN_ERR "%s: failed to get irq resouce\n", __func__); + goto free_info; + } + + info->irq_vdda_brnout = platform_get_resource(pdev, IORESOURCE_IRQ, 4); + if (info->irq_vdda_brnout == NULL) { + printk(KERN_ERR "%s: failed to get irq resouce\n", __func__); + goto free_info; + } + + + info->irq_vdd5v_droop = platform_get_resource(pdev, IORESOURCE_IRQ, 6); + if (info->irq_vdd5v_droop == NULL) { + printk(KERN_ERR "%s: failed to get irq resouce\n", __func__); + goto free_info; + } +#endif + + + 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 = mxs_bat_props; + info->bat.num_properties = ARRAY_SIZE(mxs_bat_props); + info->bat.get_property = mxs_bat_get_property; + + /* initialize ac power_supply struct */ + info->ac.name = "ac"; + info->ac.type = POWER_SUPPLY_TYPE_MAINS; + info->ac.properties = mxs_power_props; + info->ac.num_properties = ARRAY_SIZE(mxs_power_props); + info->ac.get_property = mxs_power_get_property; + + /* initialize usb power_supply struct */ + info->usb.name = "usb"; + info->usb.type = POWER_SUPPLY_TYPE_USB; + info->usb.properties = mxs_power_props; + info->usb.num_properties = ARRAY_SIZE(mxs_power_props); + info->usb.get_property = mxs_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 */ + + __raw_writel(BM_POWER_5VCTRL_ENABLE_LINREG_ILIMIT, + REGS_POWER_BASE + HW_POWER_5VCTRL_CLR); + + ret = bc_sm_restart(info); + if (ret) + goto free_info; + + + ret = request_irq(info->irq_vdd5v->start, + mxs_irq_vdd5v, IRQF_DISABLED | IRQF_SHARED, + pdev->name, info); + if (ret) { + dev_err(info->dev, "failed to request irq\n"); + goto stop_sm; + } + + ret = request_irq(info->irq_vddio_brnout->start, + mxs_irq_vddio_brnout, IRQF_DISABLED, + pdev->name, info); + if (ret) { + dev_err(info->dev, "failed to request irq\n"); + goto stop_sm; + } + +#ifndef POWER_FIQ + ret = request_irq(info->irq_dcdc4p2_bo->start, + mxs_irq_dcdc4p2_bo, IRQF_DISABLED, + pdev->name, info); + if (ret) { + dev_err(info->dev, "failed to request irq\n"); + goto stop_sm; + } + + ret = request_irq(info->irq_batt_brnout->start, + mxs_irq_batt_brnout, IRQF_DISABLED, + pdev->name, info); + if (ret) { + dev_err(info->dev, "failed to request irq\n"); + goto stop_sm; + } + + ret = request_irq(info->irq_vddd_brnout->start, + mxs_irq_vddd_brnout, IRQF_DISABLED, + pdev->name, info); + if (ret) { + dev_err(info->dev, "failed to request irq\n"); + goto stop_sm; + } + + ret = request_irq(info->irq_vdda_brnout->start, + mxs_irq_vdda_brnout, IRQF_DISABLED, + pdev->name, info); + if (ret) { + dev_err(info->dev, "failed to request irq\n"); + goto stop_sm; + } + + + ret = request_irq(info->irq_vdd5v_droop->start, + mxs_irq_vdd5v_droop, IRQF_DISABLED, + pdev->name, info); + if (ret) { + dev_err(info->dev, "failed to request irq\n"); + goto stop_sm; + } +#endif + + 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; + } + + /* handoff protection handling from bootlets protection method + * to kernel protection method + */ + init_protection(info); + + + return 0; + +unregister_ac: + power_supply_unregister(&info->ac); +unregister_bat: + power_supply_unregister(&info->bat); +free_irq: + free_irq(info->irq_vdd5v->start, pdev); + free_irq(info->irq_vddio_brnout->start, pdev); +#ifndef POWER_FIQ + free_irq(info->irq_dcdc4p2_bo->start, pdev); + free_irq(info->irq_batt_brnout->start, pdev); + free_irq(info->irq_vddd_brnout->start, pdev); + free_irq(info->irq_vdda_brnout->start, pdev); + free_irq(info->irq_vdd5v_droop->start, pdev); +#endif + +stop_sm: + ddi_bc_ShutDown(); +free_info: + kfree(info); + return ret; +} + +static int mxs_bat_remove(struct platform_device *pdev) +{ + struct mxs_info *info = platform_get_drvdata(pdev); + + if (info->regulator) + regulator_put(info->regulator); + free_irq(info->irq_vdd5v->start, pdev); + free_irq(info->irq_vddio_brnout->start, pdev); +#ifndef POWER_FIQ + free_irq(info->irq_dcdc4p2_bo->start, pdev); + free_irq(info->irq_batt_brnout->start, pdev); + free_irq(info->irq_vddd_brnout->start, pdev); + free_irq(info->irq_vdda_brnout->start, pdev); + free_irq(info->irq_vdd5v_droop->start, pdev); +#endif + ddi_bc_ShutDown(); + power_supply_unregister(&info->usb); + power_supply_unregister(&info->ac); + power_supply_unregister(&info->bat); + return 0; +} + +static void mxs_bat_shutdown(struct platform_device *pdev) +{ + ddi_bc_ShutDown(); +} + + +#ifdef CONFIG_PM + +static int mxs_bat_suspend(struct platform_device *pdev, pm_message_t msg) +{ + struct mxs_info *info = platform_get_drvdata(pdev); + + mutex_lock(&info->sm_lock); + + /* enable USB 5v wake up so don't disable irq here*/ + + ddi_bc_SetDisable(); + /* cancel state machine timer */ + del_timer_sync(&info->sm_timer); + + mutex_unlock(&info->sm_lock); + return 0; +} + +static int mxs_bat_resume(struct platform_device *pdev) +{ + struct mxs_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_dbg(info->dev, "ac/5v present, enabling state machine\n"); + + info->is_ac_online = 1; + info->is_usb_online = 0; + ddi_bc_SetCurrentLimit( + NON_USB_5V_SUPPLY_CURRENT_LIMIT_MA /*mA*/); + ddi_bc_SetEnable(); + } else if (is_usb_online()) { + /* usb supply connected */ + dev_dbg(info->dev, "usb/5v present, enabling state machine\n"); + + info->is_ac_online = 0; + info->is_usb_online = 1; + ddi_bc_SetCurrentLimit(POWERED_USB_5V_CURRENT_LIMIT_MA /*mA*/); + ddi_bc_SetEnable(); + } else { + /* not powered */ + dev_dbg(info->dev, "%s: 5v not present\n", __func__); + + info->is_ac_online = 0; + info->is_usb_online = 0; + } + + /* enable 5v irq */ + __raw_writel(BM_POWER_CTRL_ENIRQ_VDD5V_GT_VDDIO, + REGS_POWER_BASE + HW_POWER_CTRL_SET); + + /* 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 mxs_bat_suspend NULL +#define mxs_bat_resume NULL +#endif + +static struct platform_driver mxs_batdrv = { + .probe = mxs_bat_probe, + .remove = mxs_bat_remove, + .shutdown = mxs_bat_shutdown, + .suspend = mxs_bat_suspend, + .resume = mxs_bat_resume, + .driver = { + .name = "mxs-battery", + .owner = THIS_MODULE, + }, +}; + +#ifdef POWER_FIQ +static int power_relinquish(void *data, int relinquish) +{ + return -1; +} + +static struct fiq_handler power_fiq = { + .name = "mxs-battery", + .fiq_op = power_relinquish +}; + +static struct pt_regs fiq_regs; +extern char power_fiq_start[], power_fiq_end[]; +extern void lock_vector_tlb(void *); +extern long power_fiq_count; +static struct proc_dir_entry *power_fiq_proc; +#endif + +static int __init mxs_bat_init(void) +{ + struct clk *cpu, *pll0; + +#ifdef POWER_FIQ + int ret; + ret = claim_fiq(&power_fiq); + if (ret) { + pr_err("Can't claim fiq"); + } else { + get_fiq_regs(&fiq_regs); + set_fiq_handler(power_fiq_start, power_fiq_end-power_fiq_start); + lock_vector_tlb((void *)0xffff0000); + lock_vector_tlb(REGS_POWER_BASE); + + /* disable interrupts to be configured as FIQs */ + disable_irq(IRQ_DCDC4P2_BRNOUT); + disable_irq(IRQ_BATT_BRNOUT); + disable_irq(IRQ_VDDD_BRNOUT); +#ifndef CONFIG_ARCH_MX28 + disable_irq(IRQ_VDD18_BRNOUT); +#endif + disable_irq(IRQ_VDD5V_DROOP); + + + /* Enable these interrupts as FIQs */ + mxs_set_irq_fiq(IRQ_DCDC4P2_BRNOUT, 1); + mxs_set_irq_fiq(IRQ_BATT_BRNOUT, 1); + mxs_set_irq_fiq(IRQ_VDDD_BRNOUT, 1); +#ifndef CONFIG_ARCH_MX28 + mxs_set_irq_fiq(IRQ_VDD18_BRNOUT, 1); +#endif + mxs_set_irq_fiq(IRQ_VDD5V_DROOP, 1); + + + /* enable FIQ functionality */ + mxs_enable_fiq_functionality(1); + + enable_irq(IRQ_DCDC4P2_BRNOUT); + enable_irq(IRQ_BATT_BRNOUT); + enable_irq(IRQ_VDDD_BRNOUT); +#ifndef CONFIG_ARCH_MX28 + enable_irq(IRQ_VDD18_BRNOUT); +#endif + enable_irq(IRQ_VDD5V_DROOP); + + } +#endif + +#ifdef CONFIG_MXS_VBUS_CURRENT_DRAW + if (((__raw_readl(REGS_POWER_BASE + HW_POWER_5VCTRL) & + BM_POWER_5VCTRL_CHARGE_4P2_ILIMIT) == 0x20000) + && ((__raw_readl(REGS_POWER_BASE + HW_POWER_5VCTRL) & + BM_POWER_5VCTRL_PWD_CHARGE_4P2) == 0)) { +#ifdef CONFIG_USB_GADGET + printk(KERN_INFO "USB GADGET exist,wait USB enum done...\r\n"); + while (((__raw_readl(REGS_POWER_BASE + HW_POWER_5VCTRL) + & BM_POWER_5VCTRL_CHARGE_4P2_ILIMIT) == 0x20000) && + ((__raw_readl(REGS_POWER_BASE + HW_POWER_5VCTRL) & + BM_POWER_5VCTRL_PWD_CHARGE_4P2) == 0)) + ; +#else + printk(KERN_INFO "USB GADGET not exist,\ + release current limit and let CPU clock up...\r\n"); +#endif + } + cpu = clk_get(NULL, "cpu"); + pll0 = clk_get(NULL, "ref_cpu"); + clk_set_parent(cpu, pll0); +#endif + return platform_driver_register(&mxs_batdrv); +} + +static void __exit mxs_bat_exit(void) +{ + platform_driver_unregister(&mxs_batdrv); +} +#ifdef CONFIG_MXS_VBUS_CURRENT_DRAW + fs_initcall(mxs_bat_init); +#else + module_init(mxs_bat_init); +#endif +module_exit(mxs_bat_exit); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Steve Longerbeam <stevel@embeddedalley.com>"); +MODULE_DESCRIPTION("Linux glue to MXS battery state machine"); diff --git a/drivers/power/stmp37xx/Makefile b/drivers/power/stmp37xx/Makefile new file mode 100644 index 000000000000..65f670efdc93 --- /dev/null +++ b/drivers/power/stmp37xx/Makefile @@ -0,0 +1,10 @@ +# +# 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 \ + fiq.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..71b170021cb0 --- /dev/null +++ b/drivers/power/stmp37xx/ddi_bc_hw.c @@ -0,0 +1,411 @@ +/* + * 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 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..246a35dcb3df --- /dev/null +++ b/drivers/power/stmp37xx/ddi_bc_init.c @@ -0,0 +1,188 @@ +/* + * 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; + + //-------------------------------------------------------------------------- + // 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..c4175688d9ab --- /dev/null +++ b/drivers/power/stmp37xx/ddi_bc_sm.c @@ -0,0 +1,916 @@ +/* + * 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 + +//////////////////////////////////////////////////////////////////////////////// +// 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); + + +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}; + +// Used by states that need to watch the time. +uint32_t g_ddi_bc_u32StateTimer = 0; + +/* Always attempt to charge on first 5V connection */ +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 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_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. + */ + + 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 + + + //-------------------------------------------------------------------------- + // 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; + //-------------------------------------------------------------------------- + // 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). + // + //-------------------------------------------------------------------------- + + + + 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++; + + + 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 to the Topping Off state. + * 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 + + + + /* 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..e2e46dd535a9 --- /dev/null +++ b/drivers/power/stmp37xx/ddi_power_battery.c @@ -0,0 +1,1815 @@ +/* + * 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/platform.h> +#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 + +#define VBUSVALID_THRESH_2_90V 0x0 +#define VBUSVALID_THRESH_4_00V 0x1 +#define VBUSVALID_THRESH_4_10V 0x2 +#define VBUSVALID_THRESH_4_20V 0x3 +#define VBUSVALID_THRESH_4_30V 0x4 +#define VBUSVALID_THRESH_4_40V 0x5 +#define VBUSVALID_THRESH_4_50V 0x6 +#define VBUSVALID_THRESH_4_60V 0x7 + +#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 + +#define VDD4P2_ENABLED + +#define DDI_POWER_BATTERY_XFER_THRESHOLD_MV 3200 + + +#ifndef BATTERY_VOLTAGE_CMPTRIP100_THRESHOLD_MV +#define BATTERY_VOLTAGE_CMPTRIP100_THRESHOLD_MV 4000 +#endif + +#ifndef BATTERY_VOLTAGE_CMPTRIP105_THRESHOLD_MV +#define BATTERY_VOLTAGE_CMPTRIP105_THRESHOLD_MV 3800 +#endif + +/* #define DEBUG_IRQS */ + +/* to be re-enabled once FIQ functionality is added */ +#define DISABLE_VDDIO_BO_PROTECTION +//////////////////////////////////////////////////////////////////////////////// +// Globals & Variables +//////////////////////////////////////////////////////////////////////////////// + + +/* Select your 5V Detection method */ + +/* 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 + __raw_writel(BM_POWER_5VCTRL_PWDN_5VBRNOUT, + REGS_POWER_BASE + HW_POWER_5VCTRL_CLR); + + /* Enabling VBUSVALID hardware detection even if VDD5V_GT_VDDIO + * is the detection method being used for 5V status (hardware + * or software). This is in case any other drivers (such as + * USB) are specifically monitoring VBUSVALID status + */ + __raw_writel(BM_POWER_5VCTRL_VBUSVALID_5VDETECT, + REGS_POWER_BASE + HW_POWER_5VCTRL_SET); + + // Set 5V detection threshold to 4.3V for VBUSVALID. + __raw_writel( + BF(VBUSVALID_THRESH_4_30V, POWER_5VCTRL_VBUSVALID_TRSH), + REGS_POWER_BASE + HW_POWER_5VCTRL_SET); + + // 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 */ + __raw_writel(BM_POWER_CTRL_VBUSVALID_IRQ, + REGS_POWER_BASE + HW_POWER_CTRL_CLR); + __raw_writel(BM_POWER_CTRL_VDD5V_GT_VDDIO_IRQ, + REGS_POWER_BASE + HW_POWER_CTRL_CLR); + /* enable vbusvalid irq */ + + + /* enable 5V Detection interrupt vbusvalid irq */ + switch (DetectionMethod) { + case DDI_POWER_5V_VBUSVALID: + /* Check VBUSVALID for 5V present */ + __raw_writel(BM_POWER_CTRL_ENIRQ_VBUS_VALID, + REGS_POWER_BASE + HW_POWER_CTRL_SET); + break; + case DDI_POWER_5V_VDD5V_GT_VDDIO: + /* Check VDD5V_GT_VDDIO for 5V present */ + __raw_writel(BM_POWER_CTRL_ENIRQ_VDD5V_GT_VDDIO, + REGS_POWER_BASE + HW_POWER_CTRL_SET); + break; + } +} + +/* + * 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 */ + __raw_writel(BM_POWER_CTRL_VBUSVALID_IRQ, + REGS_POWER_BASE + HW_POWER_CTRL_CLR); + __raw_writel(BM_POWER_CTRL_VDD5V_GT_VDDIO_IRQ, + REGS_POWER_BASE + HW_POWER_CTRL_CLR); + + /* detect 5v unplug */ + __raw_writel(BM_POWER_CTRL_POLARITY_VBUSVALID, + REGS_POWER_BASE + HW_POWER_CTRL_CLR); + __raw_writel(BM_POWER_CTRL_POLARITY_VDD5V_GT_VDDIO, + REGS_POWER_BASE + HW_POWER_CTRL_CLR); + +#ifndef VDD4P2_ENABLED + // Enable automatic transition to DCDC + __raw_writel(BM_POWER_5VCTRL_DCDC_XFER, + REGS_POWER_BASE + HW_POWER_5VCTRL_SET); +#endif +} + +/* + * 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) +{ + int val; +#ifdef VDD4P2_ENABLED + val = __raw_readl(REGS_POWER_BASE + HW_POWER_DCDC4P2); + val &= ~(BM_POWER_DCDC4P2_ENABLE_DCDC | BM_POWER_DCDC4P2_ENABLE_4P2); + __raw_writel(val, REGS_POWER_BASE + HW_POWER_DCDC4P2); + + __raw_writel(BM_POWER_5VCTRL_PWD_CHARGE_4P2, + REGS_POWER_BASE + HW_POWER_5VCTRL_SET); + + /* make VBUSVALID_TRSH 4400mV and set PWD_CHARGE_4P2 */ + __raw_writel(BM_POWER_5VCTRL_VBUSVALID_TRSH, + HW_POWER_5VCTRL_CLR_ADDR); + + __raw_writel(BF_POWER_5VCTRL_VBUSVALID_TRSH(VBUSVALID_THRESH_4_40V), + HW_POWER_5VCTRL_SET_ADDR); + +#else + // 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. + __raw_writel(BF_POWER_VDDDCTRL_LINREG_OFFSET(LINREG_OFFSET_STEP_BELOW), + HW_POWER_BASE + HW_POWER_VDDDCTRL_SET); + // Turn on the VDDD DCDC output and turn off the VDDD LinReg output. + __raw_writel(BM_POWER_VDDDCTRL_DISABLE_FET, + HW_POWER_BASE + HW_POWER_VDDDCTRL_CLR); + + __raw_writel(BM_POWER_VDDDCTRL_ENABLE_LINREG, + HW_POWER_BASE + HW_POWER_VDDDCTRL_CLR); + // Make sure stepping is enabled when using DCDC. + __raw_writel(BM_POWER_VDDDCTRL_DISABLE_STEPPING, + HW_POWER_BASE + HW_POWER_VDDDCTRL_CLR); + + // Power VDDA and VDDIO from the DCDC. + + /* Use LinReg offset for DCDC mode. */ + __raw_writel(BF_POWER_VDDACTRL_LINREG_OFFSET(LINREG_OFFSET_STEP_BELOW), + HW_POWER_BASE + HW_POWER_VDDACTRL_SET); + // Turn on the VDDA DCDC converter output and turn off LinReg output. + __raw_writel(BM_POWER_VDDACTRL_DISABLE_FET, + HW_POWER_BASE + HW_POWER_VDDACTRL_CLR); + __raw_writel(BM_POWER_VDDACTRL_ENABLE_LINREG, + HW_POWER_BASE + HW_POWER_VDDACTRL_CLR); + + // Make sure stepping is enabled when using DCDC. + __raw_writel(BM_POWER_VDDACTRL_DISABLE_STEPPING, + HW_POWER_BASE + HW_POWER_VDDACTRL_CLR); + + // Use LinReg offset for DCDC mode. + __raw_writel(BF_POWER_VDDIOCTRL_LINREG_OFFSET( + LINREG_OFFSET_STEP_BELOW + ), + HW_POWER_BASE + HW_POWER_VDDIOCTRL_SET); + + /* Turn on the VDDIO DCDC output and turn on the LinReg output.*/ + __raw_writel(BM_POWER_VDDIOCTRL_DISABLE_FET, + HW_POWER_BASE + HW_POWER_VDDIOCTRL_CLR); + + __raw_writel(BM_POWER_5VCTRL_ILIMIT_EQ_ZERO, + HW_POWER_BASE + HW_POWER_5VCTRL_CLR_CLR); + + // Make sure stepping is enabled when using DCDC. + __raw_writel(BM_POWER_VDDIOCTRL_DISABLE_STEPPING, + HW_POWER_BASE + HW_POWER_VDDIOCTRL_CLR); +#endif + +} + +/* + * 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 */ + __raw_writel(BM_POWER_CTRL_VBUSVALID_IRQ, + REGS_POWER_BASE + HW_POWER_CTRL_CLR); + __raw_writel(BM_POWER_CTRL_VDD5V_GT_VDDIO_IRQ, + REGS_POWER_BASE + HW_POWER_CTRL_CLR); + + /* detect 5v plug-in */ + __raw_writel(BM_POWER_CTRL_POLARITY_VBUSVALID, + REGS_POWER_BASE + HW_POWER_CTRL_SET); + __raw_writel(BM_POWER_CTRL_POLARITY_VDD5V_GT_VDDIO, + REGS_POWER_BASE + HW_POWER_CTRL_SET); + +#ifndef VDD4P2_ENABLED + // Force current from 5V to be zero by disabling its entry source. + __raw_writel(BM_POWER_5VCTRL_ILIMIT_EQ_ZERO, + REGS_POWER_BASE + HW_POWER_5VCTRL_SET); +#endif + // Allow DCDC be to active when 5V is present. + __raw_writel(BM_POWER_5VCTRL_ENABLE_DCDC, + REGS_POWER_BASE + HW_POWER_5VCTRL_SET); +} + +/* This function handles the transitions on each of theVDD5V_GT_VDDIO 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) +{ + +#ifdef VDD4P2_ENABLED + ddi_power_Enable4p2(450); +#else + // Disable the DCDC during 5V connections. + __raw_writel(BM_POWER_5VCTRL_ENABLE_DCDC, + HW_POWER_BAE + HW_POWER_5VCTRL_CLR); + + // 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 + __raw_writel(BF_POWER_VDDDCTRL_LINREG_OFFSET(LINREG_OFFSET_STEP_BELOW), + HW_POWER_BAE + HW_POWER_VDDDCTRL_SET); + + // Turn on the VDDD LinReg and turn on the VDDD DCDC output. The + // ENABLE_DCDC must be cleared to avoid LinReg and DCDC conflict. + __raw_writel(BM_POWER_VDDDCTRL_ENABLE_LINREG, + HW_POWER_BAE + HW_POWER_VDDDCTRL_SET); + __raw_writel(BM_POWER_VDDDCTRL_DISABLE_FET, + HW_POWER_BAE + HW_POWER_VDDDCTRL_CLR); + + // Make sure stepping is disabled when using linear regulators + __raw_writel(BM_POWER_VDDDCTRL_DISABLE_STEPPING, + HW_POWER_BAE + HW_POWER_VDDDCTRL_SET); + + // Use LinReg offset for LinReg mode + __raw_writel(BM_POWER_VDDACTRL_LINREG_OFFSET(LINREG_OFFSET_STEP_BELOW), + HW_POWER_BAE + HW_POWER_VDDACTRL_SET); + + + // Turn on the VDDA LinReg output and prepare the DCDC for transfer. + // ENABLE_DCDC must be clear to avoid DCDC and LinReg conflict. + stmp3xxx_set(BM_POWER_VDDACTRL_ENABLE_LINREG, + HW_POWER_BASE + HW_POWER_VDDACTRL_SET); + __raw_writel(BM_POWER_VDDACTRL_DISABLE_FET, + HW_POWER_BASE + HW_POWER_VDDACTRL_CLR); + + // Make sure stepping is disabled when using linear regulators + __raw_writel(BM_POWER_VDDACTRL_DISABLE_STEPPING, + HW_POWER_BASE + HW_POWER_VDDACTRL_SET); + + // Use LinReg offset for LinReg mode. + __raw_writel(BF_POWER_VDDIOCTRL_LINREG_OFFSET( + LINREG_OFFSET_STEP_BELOW), + HW_POWER_BASE + HW_POWER_VDDIOCTRL_SET); + + // Turn on the VDDIO LinReg output and prepare the VDDIO DCDC output. + // ENABLE_DCDC must be cleared to prevent DCDC and LinReg conflict. + __raw_writel(BM_POWER_VDDIOCTRL_DISABLE_FET, + HW_POWER_BASE + HW_POWER_VDDIOCTRL_CLR); + __raw_writel(BM_POWER_5VCTRL_ILIMIT_EQ_ZERO, + REGS_POWER_BASE + HW_POWER_5VCTRL_CLR); + + // Make sure stepping is disabled when using DCDC. + __raw_writel(BM_POWER_VDDIOCTRL_DISABLE_STEPPING, + REGS_POWER_BASE + HW_POWER_VDDIOCTRL_SET); +#endif +} + + +void ddi_power_Start4p2Dcdc(bool battery_ready) +{ + + uint32_t temp_reg, old_values; + + /* set vbusvalid threshold to 2.9V because of errata */ + __raw_writel(BM_POWER_5VCTRL_VBUSVALID_TRSH, + HW_POWER_5VCTRL_CLR_ADDR); + + +#if 0 + if (battery_ready) + ddi_power_EnableBatteryIrq(); + else + enable_4p2_fiq_shutdown(); +#endif + + /* enable hardware shutdown on battery brownout */ + __raw_writel( + BM_POWER_BATTMONITOR_PWDN_BATTBRNOUT | + __raw_readl(HW_POWER_BATTMONITOR_ADDR), + HW_POWER_BATTMONITOR_ADDR); + + /* set VBUS DROOP threshold to 4.3V */ + __raw_writel(BM_POWER_5VCTRL_VBUSDROOP_TRSH, + HW_POWER_5VCTRL_CLR_ADDR); + + /* turn of vbus valid detection. Part of errate + * workaround. */ + __raw_writel(BM_POWER_5VCTRL_PWRUP_VBUS_CMPS, + HW_POWER_5VCTRL_SET_ADDR); + + __raw_writel(BM_POWER_5VCTRL_VBUSVALID_5VDETECT, + HW_POWER_5VCTRL_CLR_ADDR); + + + temp_reg = (BM_POWER_CTRL_ENIRQ_VDDD_BO | + BM_POWER_CTRL_ENIRQ_VDDA_BO | + BM_POWER_CTRL_ENIRQ_VDDIO_BO | + BM_POWER_CTRL_ENIRQ_VDD5V_DROOP | + BM_POWER_CTRL_ENIRQ_VBUS_VALID); + + /* save off old brownout enable values */ + old_values = __raw_readl(HW_POWER_CTRL_ADDR) & + temp_reg; + + /* disable irqs affected by errata */ + __raw_writel(temp_reg, HW_POWER_CTRL_CLR_ADDR); + + /* Enable DCDC from 4P2 */ + __raw_writel(__raw_readl(HW_POWER_DCDC4P2_ADDR) | + BM_POWER_DCDC4P2_ENABLE_DCDC, + HW_POWER_DCDC4P2_ADDR); + + /* give a delay to check for errate noise problem */ + mdelay(1); + + temp_reg = (BM_POWER_CTRL_VDDD_BO_IRQ | + BM_POWER_CTRL_VDDA_BO_IRQ | + BM_POWER_CTRL_VDDIO_BO_IRQ | + BM_POWER_CTRL_VDD5V_DROOP_IRQ | + BM_POWER_CTRL_VBUSVALID_IRQ); + + /* stay in this loop until the false brownout indciations + * no longer occur or until 5V actually goes away + */ + while ((__raw_readl(HW_POWER_CTRL_ADDR) & temp_reg) && + !(__raw_readl(HW_POWER_CTRL_ADDR) & + BM_POWER_CTRL_VDD5V_GT_VDDIO_IRQ)) { + __raw_writel(temp_reg, HW_POWER_CTRL_CLR_ADDR); + + mdelay(1); + } + + /* revert to previous enable irq values */ + __raw_writel(old_values, HW_POWER_CTRL_SET_ADDR); + + if (DetectionMethod == DDI_POWER_5V_VBUSVALID) + __raw_writel(BM_POWER_5VCTRL_VBUSVALID_5VDETECT, + HW_POWER_5VCTRL_SET_ADDR); +} + + +/* set the optimal CMPTRIP for the best possible 5V + * disconnection handling but without drawing power + * from the power on a stable 4p2 rails (at 4.2V). + */ +void ddi_power_handle_cmptrip(void) +{ + enum ddi_power_5v_status pmu_5v_status; + uint32_t temp = __raw_readl(HW_POWER_DCDC4P2_ADDR); + temp &= ~(BM_POWER_DCDC4P2_CMPTRIP); + + pmu_5v_status = ddi_power_GetPmu5vStatus(); + + /* CMPTRIP should remain at 31 when 5v is disconnected + * or 5v is connected but hasn't been handled yet + */ + if (pmu_5v_status != existing_5v_connection) + temp |= (31 << BP_POWER_DCDC4P2_CMPTRIP); + else if (ddi_power_GetBattery() > + BATTERY_VOLTAGE_CMPTRIP100_THRESHOLD_MV) + temp |= (1 << BP_POWER_DCDC4P2_CMPTRIP); + else if (ddi_power_GetBattery() > + BATTERY_VOLTAGE_CMPTRIP105_THRESHOLD_MV) + temp |= (24 << BP_POWER_DCDC4P2_CMPTRIP); + else + temp |= (31 << BP_POWER_DCDC4P2_CMPTRIP); + + + __raw_writel(temp, HW_POWER_DCDC4P2_ADDR); +} + +void ddi_power_Init4p2Params(void) +{ + uint32_t temp; + + ddi_power_handle_cmptrip(); + + temp = __raw_readl(HW_POWER_DCDC4P2_ADDR); + + /* DROPOUT CTRL to 10, TRG to 0 */ + temp &= ~(BM_POWER_DCDC4P2_TRG | BM_POWER_DCDC4P2_DROPOUT_CTRL); + temp |= (0xa << BP_POWER_DCDC4P2_DROPOUT_CTRL); + + __raw_writel(temp, HW_POWER_DCDC4P2_ADDR); + + + temp = __raw_readl(HW_POWER_5VCTRL_ADDR); + + /* HEADROOM_ADJ to 4, CHARGE_4P2_ILIMIT to 0 */ + temp &= ~(BM_POWER_5VCTRL_HEADROOM_ADJ | + BM_POWER_5VCTRL_CHARGE_4P2_ILIMIT); + temp |= (4 << BP_POWER_5VCTRL_HEADROOM_ADJ); + +} + +bool ddi_power_IsBattRdyForXfer(void) +{ + uint16_t u16BatteryVoltage = ddi_power_GetBattery(); + + if (u16BatteryVoltage > DDI_POWER_BATTERY_XFER_THRESHOLD_MV) + return true; + else + return false; + +} + +void ddi_power_EnableVbusDroopIrq(void) +{ + + __raw_writel(BM_POWER_CTRL_VDD5V_DROOP_IRQ, + HW_POWER_CTRL_CLR_ADDR); + + __raw_writel(BM_POWER_CTRL_ENIRQ_VDD5V_DROOP, + HW_POWER_CTRL_SET_ADDR); + +} + + +void ddi_power_Enable4p2(uint16_t target_current_limit_ma) +{ + + uint16_t u16BatteryVoltage; + uint32_t temp_reg; + + ddi_power_Init4p2Params(); + + /* disable 4p2 rail brownouts for now. (they + * should have already been off at this point) */ + __raw_writel(BM_POWER_CTRL_ENIRQ_DCDC4P2_BO, + HW_POWER_CTRL_CLR_ADDR); + + u16BatteryVoltage = ddi_power_GetBattery(); + + if (ddi_power_IsBattRdyForXfer()) { + + /* PWD_CHARGE_4P2 should already be set but just in case... */ + __raw_writel(BM_POWER_5VCTRL_PWD_CHARGE_4P2, + HW_POWER_5VCTRL_SET_ADDR); + + /* set CMPTRIP to DCDC_4P2 pin >= BATTERY pin */ + temp_reg = __raw_readl(HW_POWER_DCDC4P2_ADDR); + temp_reg &= ~(BM_POWER_DCDC4P2_CMPTRIP); + temp_reg |= (31 << BP_POWER_DCDC4P2_CMPTRIP); + __raw_writel(temp_reg, HW_POWER_DCDC4P2_ADDR); + + /* since we have a good battery, we can go ahead + * and turn on the Dcdcing from the 4p2 source. + * This is helpful in working around the chip + * errata. + */ + ddi_power_Start4p2Dcdc(true); + + /* Enable VbusDroopIrq to handle errata */ + + /* set vbus droop detection level to 4.3V */ + __raw_writel(BM_POWER_5VCTRL_VBUSDROOP_TRSH, + HW_POWER_5VCTRL_CLR_ADDR); + + ddi_power_EnableVbusDroopIrq(); + /* now that the DCDC4P2 problems are cleared, + * turn on and ramp up the 4p2 regulator + */ + temp_reg = ddi_power_BringUp4p2Regulator( + target_current_limit_ma, true); + + /* if we still have our 5V connection, we can disable + * battery brownout interrupt. This is because the + * VDD5V DROOP IRQ handler will also shutdown if battery + * is browned out and it will enable the battery brownout + * and bring VBUSVALID_TRSH level back to a normal level + * which caused the hardware battery brownout shutdown + * to be enabled. The benefit of this is that device + * that have detachable batteries (or devices going through + * the assembly line and running this firmware to test + * with) can avoid shutting down if 5V is present and + * battery voltage goes away. + */ + if (!(__raw_readl(HW_POWER_CTRL_ADDR) & + (BM_POWER_CTRL_VBUSVALID_IRQ | + BM_POWER_CTRL_VDD5V_DROOP_IRQ))) { + ddi_power_EnableBatteryBoInterrupt(false); + } + + + + printk(KERN_INFO "4P2 rail started. 5V current limit\ + set to %dmA\n", temp_reg); + + } else { + + printk(KERN_ERR "4P2 rail was attempted to be started \ + from a system\ + with a very low battery voltage. This is not\ + yet handled by the kernel driver, only by the\ + bootlets. Remaining on battery power.\n"); + + if ((__raw_readl(HW_POWER_5VCTRL_ADDR) && + BM_POWER_5VCTRL_ENABLE_DCDC)) + ddi_power_EnableBatteryBoInterrupt(true); + +#if 0 + /* enable hardware shutdown (if 5v disconnected) + * on battery brownout */ + __raw_writel( + BM_POWER_BATTMONITOR_PWDN_BATTBRNOUT | + __raw_readl(HW_POWER_BATTMONITOR_ADDR), + HW_POWER_BATTMONITOR_ADDR); + + /* turn on and ramp up the 4p2 regulator */ + temp_reg = ddi_power_BringUp4p2Regulator( + target_current_limit_ma, false); + + Configure4p2FiqShutdown(); + + SetVbusValidThresh(0); +#endif + } +} + +/* enable and ramp up 4p2 regulator */ +uint16_t ddi_power_BringUp4p2Regulator( + uint16_t target_current_limit_ma, + bool b4p2_dcdc_enabled) +{ + uint32_t temp_reg; + uint16_t charge_4p2_ilimit = 0; + + /* initial current limit to 0 */ + __raw_writel(BM_POWER_5VCTRL_CHARGE_4P2_ILIMIT, + HW_POWER_5VCTRL_CLR_ADDR); + + __raw_writel(__raw_readl(HW_POWER_DCDC4P2_ADDR) | + BM_POWER_DCDC4P2_ENABLE_4P2, + HW_POWER_DCDC4P2_ADDR); + + /* set 4p2 target voltage to zero */ + temp_reg = __raw_readl(HW_POWER_DCDC4P2_ADDR); + temp_reg &= (~BM_POWER_DCDC4P2_TRG); + __raw_writel(temp_reg, HW_POWER_DCDC4P2_ADDR); + + /* Enable 4P2 regulator*/ + __raw_writel(BM_POWER_5VCTRL_PWD_CHARGE_4P2, + HW_POWER_5VCTRL_CLR_ADDR); + + if (target_current_limit_ma > 780) + target_current_limit_ma = 780; + + ddi_power_Set4p2BoLevel(4150); + + /* possibly not necessary but recommended for unloaded + * 4p2 rail + */ + __raw_writel(BM_POWER_CHARGE_ENABLE_LOAD, + HW_POWER_CHARGE_SET_ADDR); + + while (charge_4p2_ilimit < target_current_limit_ma) { + + if (__raw_readl(HW_POWER_CTRL_ADDR) & + (BM_POWER_CTRL_VBUSVALID_IRQ | + BM_POWER_CTRL_VDD5V_DROOP_IRQ)) + break; + + + charge_4p2_ilimit += 100; + if (charge_4p2_ilimit > target_current_limit_ma) + charge_4p2_ilimit = target_current_limit_ma; + + ddi_power_set_4p2_ilimit(charge_4p2_ilimit); + + /* dcdc4p2 enable_dcdc must be enabled for + * 4p2 bo indication to function. If not enabled, + * skip using bo level detection + */ + if (!(b4p2_dcdc_enabled)) + msleep(1); + else if (__raw_readl(HW_POWER_STS_ADDR) & + BM_POWER_STS_DCDC_4P2_BO) + msleep(1); + else { + charge_4p2_ilimit = target_current_limit_ma; + ddi_power_set_4p2_ilimit(charge_4p2_ilimit); + } + } + + ddi_power_Set4p2BoLevel(3600); + + __raw_writel(BM_POWER_CTRL_DCDC4P2_BO_IRQ, + HW_POWER_CTRL_CLR_ADDR); + + /* rail should now be up and loaded. Extra + * internal load is not necessary. + */ + __raw_writel(BM_POWER_CHARGE_ENABLE_LOAD, + HW_POWER_CHARGE_CLR_ADDR); + + return charge_4p2_ilimit; + +} + + +void ddi_power_Set4p2BoLevel(uint16_t bo_voltage_mv) +{ + uint16_t bo_reg_value; + uint32_t temp; + + if (bo_voltage_mv < 3600) + bo_voltage_mv = 3600; + else if (bo_voltage_mv > 4375) + bo_voltage_mv = 4375; + + bo_reg_value = (bo_voltage_mv - 3600) / 25; + + temp = __raw_readl(HW_POWER_DCDC4P2_ADDR); + temp &= (~BM_POWER_DCDC4P2_BO); + temp |= (bo_reg_value << BP_POWER_DCDC4P2_BO); + __raw_writel(temp, HW_POWER_DCDC4P2_ADDR); +} + + + +void ddi_power_init_handoff(void) +{ + int val; + /* The following settings give optimal power supply capability */ + + // 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); +} + + +void ddi_power_EnableBatteryInterrupt(bool enable) +{ + + __raw_writel(BM_POWER_CTRL_BATT_BO_IRQ, + HW_POWER_CTRL_CLR_ADDR); + + __raw_writel(BM_POWER_CTRL_ENIRQBATT_BO, + HW_POWER_CTRL_SET_ADDR); + +} + + +int ddi_power_init_battery(void) +{ + + int ret = 0; + + if (!(__raw_readl(HW_POWER_5VCTRL_ADDR) && + BM_POWER_5VCTRL_ENABLE_DCDC)) { + printk(KERN_ERR "WARNING: Power Supply not\ + initialized correctly by \ + pre-kernel bootlets. HW_POWER_5VCTRL \ + ENABLE_DCDC should already be set. Kernel \ + power driver behavior may not be reliable \n"); + ret = 1; + } + + /* the following code to enable automatic battery measurement + * should have already been enabled in the boot prep files. Not + * sure if this is necessary or possibly susceptible to + * mis-coordination + */ + + + ret = !hw_lradc_present(BATTERY_VOLTAGE_CH); + + if (ret) { + printk(KERN_ERR "%s: hw_lradc_present failed\n", __func__); + return -ENODEV; + } else { + uint16_t wait_time = 0; + + hw_lradc_configure_channel(BATTERY_VOLTAGE_CH, 0 /* div2 */ , + 0 /* acc */ , + 0 /* num_samples */); + + /* Setup the trigger loop forever */ + hw_lradc_set_delay_trigger(LRADC_DELAY_TRIGGER_BATTERY, + 1 << BATTERY_VOLTAGE_CH, + 1 << LRADC_DELAY_TRIGGER_BATTERY, + 0, 200); + + /* Clear the accumulator & NUM_SAMPLES */ + stmp3xxx_clearl(0xFFFFFFFF, + REGS_LRADC_BASE + HW_LRADC_CHn(BATTERY_VOLTAGE_CH)); + + /* clear previous "measurement performed" status */ + __raw_writel(1 << BATTERY_VOLTAGE_CH, + HW_LRADC_CTRL1_CLR_ADDR); + + /* set to LiIon scale factor */ + __raw_writel(BM_LRADC_CONVERSION_SCALE_FACTOR, + HW_LRADC_CONVERSION_SET_ADDR); + + /* kick off the trigger */ + hw_lradc_set_delay_trigger_kick( + LRADC_DELAY_TRIGGER_BATTERY, 1); + + + /* wait for 1st converstion to be complete before + * enabling automatic copy to power supply + * peripheral + */ + while (!(__raw_readl(HW_LRADC_CTRL1_ADDR) & + 1 << BATTERY_VOLTAGE_CH) && + (wait_time < 10)) { + wait_time++; + udelay(1); + } + + __raw_writel(BM_LRADC_CONVERSION_AUTOMATIC, + HW_LRADC_CONVERSION_SET_ADDR); + } + +#ifndef VDD4P2_ENABLED + /* prepare handoff */ + ddi_power_init_handoff(); +#endif + return ret; +} + +/* + * Use the the lradc1 channel + * get the die temperature from on-chip sensor. + */ +uint16_t MeasureInternalDieTemperature(void) +{ + uint32_t ch8Value, ch9Value; + + /* power up internal tep sensor block */ + __raw_writel(BM_LRADC_CTRL2_TEMPSENSE_PWD, + REGS_LRADC_BASE + HW_LRADC_CTRL2_CLR); + + /* mux to the lradc 8th temp channel */ + __raw_writel(BF(0xF, LRADC_CTRL4_LRADC1SELECT), + REGS_LRADC_BASE + HW_LRADC_CTRL4_CLR); + __raw_writel(BF(8, LRADC_CTRL4_LRADC1SELECT), + REGS_LRADC_BASE + HW_LRADC_CTRL4_SET); + + /* Clear the interrupt flag */ + __raw_writel(BM_LRADC_CTRL1_LRADC1_IRQ, + REGS_LRADC_BASE + HW_LRADC_CTRL1_CLR); + __raw_writel(BF(1 << LRADC_CH1, LRADC_CTRL0_SCHEDULE), + REGS_LRADC_BASE + HW_LRADC_CTRL0_SET); + + /* Wait for conversion complete*/ + while (!(__raw_readl(REGS_LRADC_BASE + HW_LRADC_CTRL1) + & BM_LRADC_CTRL1_LRADC1_IRQ)) + cpu_relax(); + + /* Clear the interrupt flag again */ + __raw_writel(BM_LRADC_CTRL1_LRADC1_IRQ, + REGS_LRADC_BASE + HW_LRADC_CTRL1_CLR); + + // read temperature value and clr lradc + ch8Value = __raw_readl(REGS_LRADC_BASE + + HW_LRADC_CHn(LRADC_CH1)) & BM_LRADC_CHn_VALUE; + + + __raw_writel(BM_LRADC_CHn_VALUE, + REGS_LRADC_BASE + HW_LRADC_CHn_CLR(LRADC_CH1)); + + /* mux to the lradc 9th temp channel */ + __raw_writel(BF(0xF, LRADC_CTRL4_LRADC1SELECT), + REGS_LRADC_BASE + HW_LRADC_CTRL4_CLR); + __raw_writel(BF(9, LRADC_CTRL4_LRADC1SELECT), + REGS_LRADC_BASE + HW_LRADC_CTRL4_SET); + + /* Clear the interrupt flag */ + __raw_writel(BM_LRADC_CTRL1_LRADC1_IRQ, + REGS_LRADC_BASE + HW_LRADC_CTRL1_CLR); + __raw_writel(BF(1 << LRADC_CH1, LRADC_CTRL0_SCHEDULE), + REGS_LRADC_BASE + HW_LRADC_CTRL0_SET); + // Wait for conversion complete + while (!(__raw_readl(REGS_LRADC_BASE + HW_LRADC_CTRL1) + & BM_LRADC_CTRL1_LRADC1_IRQ)) + cpu_relax(); + + /* Clear the interrupt flag */ + __raw_writel(BM_LRADC_CTRL1_LRADC1_IRQ, + REGS_LRADC_BASE + HW_LRADC_CTRL1_CLR); + // read temperature value + ch9Value = __raw_readl( + REGS_LRADC_BASE + HW_LRADC_CHn(LRADC_CH1)) + & BM_LRADC_CHn_VALUE; + + + __raw_writel(BM_LRADC_CHn_VALUE, + REGS_LRADC_BASE + HW_LRADC_CHn_CLR(LRADC_CH1)); + + /* power down temp sensor block */ + __raw_writel(BM_LRADC_CTRL2_TEMPSENSE_PWD, + REGS_LRADC_BASE + HW_LRADC_CTRL2_SET); + + + return (uint16_t)((ch9Value-ch8Value)*GAIN_CORRECTION/4000); +} + + +//////////////////////////////////////////////////////////////////////////////// +//! Name: ddi_power_GetBatteryMode +//! +//! \brief +//////////////////////////////////////////////////////////////////////////////// +ddi_power_BatteryMode_t ddi_power_GetBatteryMode(void) +{ + 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) { + __raw_writel(BM_POWER_CHARGE_PWD_BATTCHRG, + REGS_POWER_BASE + HW_POWER_CHARGE_CLR); + __raw_writel(BM_POWER_5VCTRL_PWD_CHARGE_4P2, + REGS_POWER_BASE + HW_POWER_5VCTRL_CLR); + } else { + __raw_writel(BM_POWER_CHARGE_PWD_BATTCHRG, + REGS_POWER_BASE + HW_POWER_CHARGE_SET); +#ifndef VDD4P2_ENABLED + __raw_writel(BM_POWER_5VCTRL_PWD_CHARGE_4P2, + REGS_POWER_BASE + HW_POWER_5VCTRL_SET); +#endif + } + +//#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 + __raw_writel( + BF(i16BrownoutLevel, POWER_BATTMONITOR_BRWNOUT_LVL), + REGS_POWER_BASE + HW_POWER_BATTMONITOR_SET); + } else + ret = -EINVAL; + + return ret; +} +#endif + +//////////////////////////////////////////////////////////////////////////////// +// Currents +//////////////////////////////////////////////////////////////////////////////// + + +//////////////////////////////////////////////////////////////////////////////// +//! 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); + + /* Compute the toggle mask. */ + u16ToggleMask = u16OldSetting ^ u16NewSetting; + + /* Write to the toggle register.*/ + __raw_writel(u16ToggleMask << BP_POWER_CHARGE_BATTCHRG_I, + REGS_POWER_BASE + HW_POWER_CHARGE_TOG); + + // 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. + __raw_writel(BF_POWER_CHARGE_STOP_ILIMIT(u16ToggleMask), + REGS_POWER_BASE + HW_POWER_CHARGE_TOG); + + // 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) { + __raw_writel(BM_POWER_CTRL_CLKGATE, + REGS_POWER_BASE + HW_POWER_CTRL_SET); + } else { + __raw_writel(BM_POWER_CTRL_CLKGATE, + REGS_POWER_BASE + HW_POWER_CTRL_CLR); + } +} + +//////////////////////////////////////////////////////////////////////////////// +//! 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; +} + + +enum ddi_power_5v_status ddi_power_GetPmu5vStatus(void) +{ + + if (DetectionMethod == DDI_POWER_5V_VDD5V_GT_VDDIO) { + + if (__raw_readl(HW_POWER_CTRL_ADDR) & + BM_POWER_CTRL_POLARITY_VDD5V_GT_VDDIO) { + if ((__raw_readl(HW_POWER_CTRL_ADDR) & + BM_POWER_CTRL_VDD5V_GT_VDDIO_IRQ) || + ddi_power_Get5vPresentFlag()) + return new_5v_connection; + else + return existing_5v_disconnection; + } else { + if ((__raw_readl(HW_POWER_CTRL_ADDR) & + BM_POWER_CTRL_VDD5V_GT_VDDIO_IRQ) || + !ddi_power_Get5vPresentFlag() || + ddi_power_Get5vDroopFlag()) + return new_5v_disconnection; + else + return existing_5v_connection; + } + } else { + + if (__raw_readl(HW_POWER_CTRL_ADDR) & + BM_POWER_CTRL_POLARITY_VBUSVALID) { + if ((__raw_readl(HW_POWER_CTRL_ADDR) & + BM_POWER_CTRL_VBUSVALID_IRQ) || + ddi_power_Get5vPresentFlag()) + return new_5v_connection; + else + return existing_5v_disconnection; + } else { + if ((__raw_readl(HW_POWER_CTRL_ADDR) & + BM_POWER_CTRL_VBUSVALID_IRQ) || + !ddi_power_Get5vPresentFlag() || + ddi_power_Get5vDroopFlag()) + return new_5v_disconnection; + else + return existing_5v_connection; + } + + } +} + +void ddi_power_disable_5v_connection_irq(void) +{ + + __raw_writel((BM_POWER_CTRL_ENIRQ_VBUS_VALID | + BM_POWER_CTRL_ENIRQ_VDD5V_GT_VDDIO), + HW_POWER_CTRL_CLR_ADDR); +} + +void ddi_power_enable_5v_disconnect_detection(void) +{ + __raw_writel(BM_POWER_CTRL_POLARITY_VDD5V_GT_VDDIO | + BM_POWER_CTRL_POLARITY_VBUSVALID, + HW_POWER_CTRL_CLR_ADDR); + + __raw_writel(BM_POWER_CTRL_VDD5V_GT_VDDIO_IRQ | + BM_POWER_CTRL_VBUSVALID_IRQ, + HW_POWER_CTRL_CLR_ADDR); + + if (DetectionMethod == DDI_POWER_5V_VDD5V_GT_VDDIO) { + __raw_writel(BM_POWER_CTRL_ENIRQ_VDD5V_GT_VDDIO, + HW_POWER_CTRL_SET_ADDR); + } else { + __raw_writel(BM_POWER_CTRL_ENIRQ_VBUS_VALID, + HW_POWER_CTRL_SET_ADDR); + } +} + +void ddi_power_enable_5v_connect_detection(void) +{ + __raw_writel(BM_POWER_CTRL_POLARITY_VDD5V_GT_VDDIO | + BM_POWER_CTRL_POLARITY_VBUSVALID, + HW_POWER_CTRL_SET_ADDR); + + __raw_writel(BM_POWER_CTRL_VDD5V_GT_VDDIO_IRQ | + BM_POWER_CTRL_VBUSVALID_IRQ, + HW_POWER_CTRL_CLR_ADDR); + + if (DetectionMethod == DDI_POWER_5V_VDD5V_GT_VDDIO) { + __raw_writel(BM_POWER_CTRL_ENIRQ_VDD5V_GT_VDDIO, + HW_POWER_CTRL_SET_ADDR); + } else { + __raw_writel(BM_POWER_CTRL_ENIRQ_VBUS_VALID, + HW_POWER_CTRL_SET_ADDR); + } +} + +void ddi_power_EnableBatteryBoInterrupt(bool bEnable) +{ + if (bEnable) { + + __raw_writel(BM_POWER_CTRL_BATT_BO_IRQ, + HW_POWER_CTRL_CLR_ADDR); + __raw_writel(BM_POWER_CTRL_ENIRQBATT_BO, + HW_POWER_CTRL_SET_ADDR); + /* todo: make sure the battery brownout comparator + * is enabled in HW_POWER_BATTMONITOR + */ + } else { + __raw_writel(BM_POWER_CTRL_ENIRQBATT_BO, + HW_POWER_CTRL_CLR_ADDR); + } +} + +void ddi_power_EnableDcdc4p2BoInterrupt(bool bEnable) +{ + if (bEnable) { + + __raw_writel(BM_POWER_CTRL_DCDC4P2_BO_IRQ, + HW_POWER_CTRL_CLR_ADDR); + __raw_writel(BM_POWER_CTRL_ENIRQ_DCDC4P2_BO, + HW_POWER_CTRL_SET_ADDR); + } else { + __raw_writel(BM_POWER_CTRL_ENIRQ_DCDC4P2_BO, + HW_POWER_CTRL_CLR_ADDR); + } +} + +void ddi_power_EnableVdd5vDroopInterrupt(bool bEnable) +{ + if (bEnable) { + + __raw_writel(BM_POWER_CTRL_VDD5V_DROOP_IRQ, + HW_POWER_CTRL_CLR_ADDR); + __raw_writel(BM_POWER_CTRL_ENIRQ_VDD5V_DROOP, + HW_POWER_CTRL_SET_ADDR); + } else { + __raw_writel(BM_POWER_CTRL_ENIRQ_VDD5V_DROOP, + HW_POWER_CTRL_CLR_ADDR); + } +} + + +void ddi_power_Enable5vDisconnectShutdown(bool bEnable) +{ + if (bEnable) { + __raw_writel(BM_POWER_5VCTRL_PWDN_5VBRNOUT, + HW_POWER_5VCTRL_SET_ADDR); + } else { + __raw_writel(BM_POWER_5VCTRL_PWDN_5VBRNOUT, + HW_POWER_5VCTRL_CLR_ADDR); + } +} + + +void ddi_power_enable_5v_to_battery_xfer(bool bEnable) +{ + if (bEnable) { + /* order matters */ + + /* we can enable this in in vbus droop or 4p2 fiq handler + * ddi_power_EnableBatteryBoInterrupt(true); + */ + ddi_power_Enable5vDisconnectShutdown(false); + } else { + /* order matters */ + ddi_power_Enable5vDisconnectShutdown(true); + ddi_power_EnableBatteryBoInterrupt(false); + } +} + + +void ddi_power_init_4p2_protection(void) +{ + /* set vbus droop detection level to 4.3V */ + __raw_writel(BM_POWER_5VCTRL_VBUSDROOP_TRSH, + HW_POWER_5VCTRL_CLR_ADDR); + + /* VBUSDROOP THRESHOLD to 4.3V */ + __raw_writel(BM_POWER_5VCTRL_VBUSDROOP_TRSH, + HW_POWER_5VCTRL_CLR_ADDR); + + ddi_power_EnableVbusDroopIrq(); + + /* VBUSVALID THRESH = 2.9V */ + __raw_writel(BM_POWER_5VCTRL_VBUSVALID_TRSH, + HW_POWER_5VCTRL_CLR_ADDR); + +} + +/* determine if all the bits are in a 'DCDC 4P2 Enabled' state. */ +bool ddi_power_check_4p2_bits(void) +{ + + + uint32_t temp; + + temp = __raw_readl(HW_POWER_5VCTRL_ADDR) & + BM_POWER_5VCTRL_PWD_CHARGE_4P2; + + /* if PWD_CHARGE_4P2 = 1, 4p2 is disabled */ + if (temp) + return false; + + temp = __raw_readl(HW_POWER_DCDC4P2_ADDR) & + BM_POWER_DCDC4P2_ENABLE_DCDC; + + if (!temp) + return false; + + temp = __raw_readl(HW_POWER_DCDC4P2_ADDR) & + BM_POWER_DCDC4P2_ENABLE_4P2; + + if (temp) + return true; + else + return false; + +} + +uint16_t ddi_power_set_4p2_ilimit(uint16_t ilimit) +{ + uint32_t temp_reg; + + if (ilimit > 780) + ilimit = 780; + temp_reg = __raw_readl(HW_POWER_5VCTRL_ADDR); + temp_reg &= (~BM_POWER_5VCTRL_CHARGE_4P2_ILIMIT); + temp_reg |= BF_POWER_5VCTRL_CHARGE_4P2_ILIMIT( + ddi_power_convert_current_to_setting( + ilimit)); + __raw_writel(temp_reg, HW_POWER_5VCTRL_ADDR); + + return ilimit; +} + +void ddi_power_shutdown(void) +{ + __raw_writel(0x3e770001, HW_POWER_RESET_ADDR); +} + +void ddi_power_handle_dcdc4p2_bo(void) +{ + ddi_power_EnableBatteryBoInterrupt(true); + ddi_power_EnableDcdc4p2BoInterrupt(false); +} + +void ddi_power_enable_vddio_interrupt(bool enable) +{ + if (enable) { + __raw_writel(BM_POWER_CTRL_VDDIO_BO_IRQ, + HW_POWER_CTRL_CLR_ADDR); +#ifndef DISABLE_VDDIO_BO_PROTECTION + __raw_writel(BM_POWER_CTRL_ENIRQ_VDDIO_BO, + HW_POWER_CTRL_SET_ADDR); +#endif + } else { + __raw_writel(BM_POWER_CTRL_ENIRQ_VDDIO_BO, + HW_POWER_CTRL_CLR_ADDR); + } +} + +void ddi_power_handle_vddio_brnout(void) +{ + if (ddi_power_GetPmu5vStatus() == new_5v_connection) { + ddi_power_enable_vddio_interrupt(false); + } else { +#ifdef DEBUG_IRQS + ddi_power_enable_vddio_interrupt(false); + printk(KERN_ALERT "VDDIO BO TRIED TO SHUTDOWN!!!\n"); + return; +#else + ddi_power_shutdown(); +#endif + } +} + +void ddi_power_handle_vdd5v_droop(void) +{ + uint32_t temp; + + /* handle errata */ + temp = __raw_readl(HW_POWER_DCDC4P2_ADDR); + temp |= (BF(31, POWER_DCDC4P2_CMPTRIP) | BM_POWER_DCDC4P2_TRG); + __raw_writel(temp, HW_POWER_DCDC4P2_ADDR); + + + /* if battery is below brownout level, shutdown asap */ + if (__raw_readl(HW_POWER_STS_ADDR) & BM_POWER_STS_BATT_BO) + ddi_power_shutdown(); + + /* due to 5v connect vddio bo chip bug, we need to + * disable vddio interrupts until we reset the 5v + * detection for 5v connect detect. We want to allow + * some debounce time before enabling connect detection. + */ + ddi_power_enable_vddio_interrupt(false); + + ddi_power_EnableBatteryBoInterrupt(true); + ddi_power_EnableDcdc4p2BoInterrupt(false); + ddi_power_EnableVdd5vDroopInterrupt(false); + +} + +void ddi_power_InitOutputBrownouts(void) +{ + uint32_t temp; + + __raw_writel(BM_POWER_CTRL_VDDD_BO_IRQ | + BM_POWER_CTRL_VDDA_BO_IRQ | + BM_POWER_CTRL_VDDIO_BO_IRQ, + HW_POWER_CTRL_CLR_ADDR); + + __raw_writel(BM_POWER_CTRL_ENIRQ_VDDD_BO | + BM_POWER_CTRL_ENIRQ_VDDA_BO | + BM_POWER_CTRL_ENIRQ_VDDIO_BO, + HW_POWER_CTRL_SET_ADDR); + + temp = __raw_readl(HW_POWER_VDDDCTRL_ADDR); + temp &= ~BM_POWER_VDDDCTRL_PWDN_BRNOUT; + __raw_writel(temp, HW_POWER_VDDDCTRL_ADDR); + + temp = __raw_readl(HW_POWER_VDDACTRL_ADDR); + temp &= ~BM_POWER_VDDACTRL_PWDN_BRNOUT; + __raw_writel(temp, HW_POWER_VDDACTRL_ADDR); + + temp = __raw_readl(HW_POWER_VDDIOCTRL_ADDR); + temp &= ~BM_POWER_VDDIOCTRL_PWDN_BRNOUT; + __raw_writel(temp, HW_POWER_VDDIOCTRL_ADDR); +} + +/* used for debugging purposes only */ +void ddi_power_disable_power_interrupts(void) +{ + __raw_writel(BM_POWER_CTRL_ENIRQ_DCDC4P2_BO | + BM_POWER_CTRL_ENIRQ_VDD5V_DROOP | + BM_POWER_CTRL_ENIRQ_PSWITCH | + BM_POWER_CTRL_ENIRQ_DC_OK | + BM_POWER_CTRL_ENIRQBATT_BO | + BM_POWER_CTRL_ENIRQ_VDDIO_BO | + BM_POWER_CTRL_ENIRQ_VDDA_BO | + BM_POWER_CTRL_ENIRQ_VDDD_BO | + BM_POWER_CTRL_ENIRQ_VBUS_VALID | + BM_POWER_CTRL_ENIRQ_VDD5V_GT_VDDIO, + HW_POWER_CTRL_CLR_ADDR); + +} + +bool ddi_power_Get5vDroopFlag(void) +{ + if (__raw_readl(HW_POWER_STS_ADDR) & + BM_POWER_STS_VDD5V_DROOP) + return true; + else + return false; +} + +//////////////////////////////////////////////////////////////////////////////// +// 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..892feb639566 --- /dev/null +++ b/drivers/power/stmp37xx/ddi_power_battery.h @@ -0,0 +1,95 @@ +/* + * 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 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; + + +enum ddi_power_5v_status { + new_5v_connection, + existing_5v_connection, + new_5v_disconnection, + existing_5v_disconnection, +} ; + + +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); +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); +enum ddi_power_5v_status ddi_power_GetPmu5vStatus(void); +void ddi_power_EnableBatteryBoFiq(bool bEnable); +void ddi_power_disable_5v_connection_irq(void); +void ddi_power_enable_5v_disconnect_detection(void); +void ddi_power_enable_5v_connect_detection(void); +void ddi_power_Enable5vDisconnectShutdown(bool bEnable); +void ddi_power_enable_5v_to_battery_xfer(bool bEnable); +void ddi_power_init_4p2_protection(void); +bool ddi_power_check_4p2_bits(void); +void ddi_power_Start4p2Dcdc(bool battery_ready); +void ddi_power_Init4p2Params(void); +bool ddi_power_IsBattRdyForXfer(void); +void ddi_power_EnableVbusDroopIrq(void); +void ddi_power_Enable4p2(uint16_t target_current_limit_ma); +uint16_t ddi_power_BringUp4p2Regulator( + uint16_t target_current_limit_ma, + bool b4p2_dcdc_enabled); +void ddi_power_Set4p2BoLevel(uint16_t bo_voltage_mv); +void ddi_power_EnableBatteryBoInterrupt(bool bEnable); +void ddi_power_handle_cmptrip(void); +uint16_t ddi_power_set_4p2_ilimit(uint16_t ilimit); +void ddi_power_shutdown(void); +void ddi_power_handle_dcdc4p2_bo(void); +void ddi_power_enable_vddio_interrupt(bool enable); +void ddi_power_handle_vddio_brnout(void); +void ddi_power_EnableDcdc4p2BoInterrupt(bool bEnable); +void ddi_power_handle_vdd5v_droop(void); +void ddi_power_InitOutputBrownouts(void); +void ddi_power_disable_power_interrupts(void); +bool ddi_power_Get5vDroopFlag(void); diff --git a/drivers/power/stmp37xx/fiq.S b/drivers/power/stmp37xx/fiq.S new file mode 100644 index 000000000000..09c185dd1536 --- /dev/null +++ b/drivers/power/stmp37xx/fiq.S @@ -0,0 +1,108 @@ +#include <linux/linkage.h> +#include <asm/assembler.h> +#include <mach/platform.h> +#include <mach/hardware.h> +#include <asm/pgtable-hwdef.h> +#include <mach/regs-power.h> +#include <mach/regs-clkctrl.h> +#include <mach/regs-timrot.h> + + .align 5 + .globl power_fiq_start + .globl power_fiq_end + .globl power_fiq_count + .globl lock_vector_tlb + +power_fiq_start: + + ldr r8,power_reg + ldr r9,[r8,#HW_POWER_CTRL ] + ldr r10,power_off + + @ when VDDIO_BO_IRQ, + @ disabled, handled in IRQ for now + @tst r9, #BM_POWER_CTRL_VDDIO_BO_IRQ + + + @ when BATT_BO_IRQ, VDDD_BO_IRQ, VDDA_BO_IRQ, power off chip + ldr r11,power_bo + tst r9, r11 + strne r10,[r8,#HW_POWER_RESET] + + @VDD5V_DROOP_IRQ + tst r9, #BM_POWER_CTRL_VDD5V_DROOP_IRQ + beq check_dcdc4p2 + + @ handle errata + ldr r10, [r8, #HW_POWER_DCDC4P2] + orr r10,r10,#(BM_POWER_DCDC4P2_TRG) + orr r10,r10,#(BF_POWER_DCDC4P2_CMPTRIP(31)) + str r10,[r8, #(HW_POWER_DCDC4P2)] + + @ if battery is below brownout level, shutdown asap + ldr r10, [r8, #HW_POWER_STS] + tst r10, #BM_POWER_STS_BATT_BO + ldr r10, power_off + strne r10, [r8, #HW_POWER_RESET] + + @ disable viddio irq + mov r11, #BM_POWER_CTRL_ENIRQ_VDDIO_BO + str r11, [r8, #HW_POWER_CTRL_CLR] + + @ enable battery BO irq + mov r11, #BM_POWER_CTRL_BATT_BO_IRQ + str r11, [r8, #HW_POWER_CTRL_CLR] + mov r11, #BM_POWER_CTRL_ENIRQBATT_BO + str r11, [r8, #HW_POWER_CTRL_SET] + + @ disable dcdc4p2 interrupt + mov r11, #BM_POWER_CTRL_ENIRQ_DCDC4P2_BO + str r11, [r8, #HW_POWER_CTRL_CLR] + + @ disable vdd5v_droop interrupt + mov r11, #BM_POWER_CTRL_ENIRQ_VDD5V_DROOP + str r11, [r8, #HW_POWER_CTRL_CLR] + +check_dcdc4p2: + @ when DCDC4P2_BO_IRQ, + tst r9, #BM_POWER_CTRL_DCDC4P2_BO_IRQ + + mov r11, #BM_POWER_CTRL_BATT_BO_IRQ + strne r11, [r8, #HW_POWER_CTRL_CLR] + + mov r11, #BM_POWER_CTRL_ENIRQBATT_BO + strne r11, [r8, #HW_POWER_CTRL_SET] + + mov r11, #BM_POWER_CTRL_ENIRQ_DCDC4P2_BO + strne r11, [r8, #HW_POWER_CTRL_CLR] + + + + @return from fiq + subs pc,lr, #4 + +power_reg: + .long REGS_POWER_BASE +power_off: + .long 0x3e770001 +power_bo: + .long BM_POWER_CTRL_BATT_BO_IRQ | \ + BM_POWER_CTRL_VDDA_BO_IRQ | BM_POWER_CTRL_VDDD_BO_IRQ +power_fiq_count: + .long 0 +power_fiq_end: + +lock_vector_tlb: + + mov r1, r0 @ set r1 to the value of the address to be locked down + mcr p15,0,r1,c8,c7,1 @ invalidate TLB single entry to ensure that + @ LockAddr is not already in the TLB + mrc p15,0,r0,c10,c0,0 @ read the lockdown register + orr r0,r0,#1 @ set the preserve bit + mcr p15,0,r0,c10,c0,0 @ write to the lockdown register + ldr r1,[r1] @ TLB will miss, and entry will be loaded + mrc p15,0,r0,c10,c0,0 @ read the lockdown register (victim will have + @ incremented) + bic r0,r0,#1 @ clear preserve bit + mcr p15,0,r0,c10,c0,0 @ write to the lockdown registerADR r1,LockAddr + mov pc,lr @ diff --git a/drivers/power/stmp37xx/linux.c b/drivers/power/stmp37xx/linux.c new file mode 100644 index 000000000000..8f9703753664 --- /dev/null +++ b/drivers/power/stmp37xx/linux.c @@ -0,0 +1,1151 @@ +/* + * Linux glue to STMP3xxx battery state machine. + * + * Author: Steve Longerbeam <stevel@embeddedalley.com> + * + * Copyright (C) 2008 EmbeddedAlley Solutions Inc. + * Copyright 2008-2010 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 <mach/irqs.h> +#include <mach/regs-icoll.h> +#include <linux/delay.h> +#include <linux/proc_fs.h> +#include <linux/interrupt.h> +#include <asm/fiq.h> + +enum application_5v_status{ + _5v_connected_verified, + _5v_connected_unverified, + _5v_disconnected_unverified, + _5v_disconnected_verified, +}; + +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 *irq_vdd5v; + struct resource *irq_dcdc4p2_bo; + struct resource *irq_batt_brnout; + struct resource *irq_vddd_brnout; + struct resource *irq_vdda_brnout; + struct resource *irq_vddio_brnout; + struct resource *irq_vdd5v_droop; + int is_ac_online; + int source_protection_mode; + uint32_t sm_new_5v_connection_jiffies; + uint32_t sm_new_5v_disconnection_jiffies; + enum application_5v_status sm_5v_connection_status; + + + + +#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) + +#ifndef NON_USB_5V_SUPPLY_CURRENT_LIMIT_MA +#define NON_USB_5V_SUPPLY_CURRENT_LIMIT_MA 780 +#endif + +#ifndef POWERED_USB_5V_CURRENT_LIMIT_MA +#define POWERED_USB_5V_CURRENT_LIMIT_MA 450 +#endif + +#ifndef UNPOWERED_USB_5V_CURRENT_LIMIT_MA +#define UNPOWERED_USB_5V_CURRENT_LIMIT_MA 80 +#endif + +#ifndef _5V_DEBOUNCE_TIME_MS +#define _5V_DEBOUNCE_TIME_MS 500 +#endif + +#ifndef OS_SHUTDOWN_BATTERY_VOLTAGE_THRESHOLD_MV +#define OS_SHUTDOWN_BATTERY_VOLTAGE_THRESHOLD_MV 3350 +#endif + +#define POWER_FIQ + +/* #define DEBUG_IRQS */ + +/* 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) + + + +void init_protection(struct stmp3xxx_info *info) +{ + enum ddi_power_5v_status pmu_5v_status; + uint16_t battery_voltage; + + pmu_5v_status = ddi_power_GetPmu5vStatus(); + battery_voltage = ddi_power_GetBattery(); + + /* InitializeFiqSystem(); */ + + ddi_power_InitOutputBrownouts(); + + + /* if we start the kernel with 4p2 already started + * by the bootlets, we need to hand off from this + * state to the kernel 4p2 enabled state. + */ + if ((pmu_5v_status == existing_5v_connection) && + ddi_power_check_4p2_bits()) { + ddi_power_enable_5v_disconnect_detection(); + + /* includes VBUS DROOP workaround for errata */ + ddi_power_init_4p2_protection(); + + /* if we still have our 5V connection, we can disable + * battery brownout interrupt. This is because the + * VDD5V DROOP IRQ handler will also shutdown if battery + * is browned out and it will enable the battery brownout + * and bring VBUSVALID_TRSH level back to a normal level + * which caused the hardware battery brownout shutdown + * to be enabled. The benefit of this is that device + * that have detachable batteries (or devices going through + * the assembly line and running this firmware to test + * with) can avoid shutting down if 5V is present and + * battery voltage goes away. + */ + ddi_power_EnableBatteryBoInterrupt(false); + + info->sm_5v_connection_status = _5v_connected_verified; + } else { +#ifdef DEBUG_IRQS + if (battery_voltage < + OS_SHUTDOWN_BATTERY_VOLTAGE_THRESHOLD_MV) { + printk(KERN_CRIT "Polled battery voltage measurement is\ + less than %dmV. Kernel should be halted/\ + shutdown\n", + OS_SHUTDOWN_BATTERY_VOLTAGE_THRESHOLD_MV); + + return; + } +#endif + info->sm_5v_connection_status = _5v_disconnected_verified; + ddi_power_EnableBatteryBoInterrupt(true); + + } + + + /* all brownouts are now handled software fiqs. We + * can now disable the hardware protection mechanisms + * because leaving them on yields ~2kV ESD level + * versus ~4kV ESD levels when they are off. This + * difference is suspected to be cause by the fast + * falling edge pswitch functionality being tripped + * by ESD events. This functionality is disabled + * when PWD_OFF is disabled. + */ +#ifdef DISABLE_HARDWARE_PROTECTION_MECHANISMS + __raw_writel(BM_POWER_RESET_PWD_OFF, + HW_POWER_RESET_SET_ADDR); +#endif + + + + +} + + + +static void check_and_handle_5v_connection(struct stmp3xxx_info *info) +{ + + switch (ddi_power_GetPmu5vStatus()) { + + case new_5v_connection: + ddi_power_enable_5v_disconnect_detection(); + info->sm_5v_connection_status = _5v_connected_unverified; + + case existing_5v_connection: + if (info->sm_5v_connection_status != _5v_connected_verified) { + /* we allow some time to pass before considering + * the 5v connection to be ready to use. This + * will give the USB system time to enumerate + * (coordination with USB driver to be added + * in the future). + */ + + /* handle jiffies rollover case */ + if ((jiffies - info->sm_new_5v_connection_jiffies) + < 0) { + info->sm_new_5v_connection_jiffies = jiffies; + break; + } + + if ((jiffies_to_msecs(jiffies - + info->sm_new_5v_connection_jiffies)) > + _5V_DEBOUNCE_TIME_MS) { + info->sm_5v_connection_status = + _5v_connected_verified; + dev_info(info->dev, + "5v connection verified\n"); + ddi_power_Enable4p2(450); + + + /* part of handling for errata. It is + * now "somewhat" safe to + * turn on vddio interrupts again + */ + ddi_power_enable_vddio_interrupt(true); + } + } + break; + + case new_5v_disconnection: + + ddi_bc_SetDisable(); + ddi_bc_SetCurrentLimit(0); + if (info->regulator) + regulator_set_current_limit(info->regulator, 0, 0); + info->is_usb_online = 0; + info->is_ac_online = 0; + + info->sm_5v_connection_status = _5v_disconnected_unverified; + + case existing_5v_disconnection: + + if (info->sm_5v_connection_status != + _5v_disconnected_verified) { + if ((jiffies - info->sm_new_5v_disconnection_jiffies) + < 0) { + info->sm_new_5v_connection_jiffies = jiffies; + break; + } + + if ((jiffies_to_msecs(jiffies - + info->sm_new_5v_disconnection_jiffies)) > + _5V_DEBOUNCE_TIME_MS) { + info->sm_5v_connection_status = + _5v_disconnected_verified; + ddi_power_execute_5v_to_battery_handoff(); + ddi_power_enable_5v_connect_detection(); + + /* part of handling for errata. + * It is now safe to + * turn on vddio interrupts again + */ + ddi_power_enable_vddio_interrupt(true); + dev_info(info->dev, + "5v disconnection handled\n"); + + } + } + + break; + } +} + + +static void handle_battery_voltage_changes(struct stmp3xxx_info *info) +{ +#if 0 + uint16_t battery_voltage; + + battery_voltage = ddi_power_GetBattery(); + + if (info->sm_5v_connection_status != _5v_connected_verified) { + if (battery_voltage < + OS_SHUTDOWN_BATTERY_VOLTAGE_THRESHOLD_MV) { + printk(KERN_CRIT "Polled battery voltage measurement is\ + less than %dmV. Shutting down the \ + system\n", + OS_SHUTDOWN_BATTERY_VOLTAGE_THRESHOLD_MV); + + shutdown_os(); + return; + } + } else +#endif + { + ddi_power_handle_cmptrip(); + + if (ddi_power_IsBattRdyForXfer()) + ddi_power_enable_5v_to_battery_xfer(true); + else + ddi_power_enable_5v_to_battery_xfer(false); + + } +} + + +/* + * 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"); + +} +/* + * Assumption: + * 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); + + handle_battery_voltage_changes(info); + + check_and_handle_5v_connection(info); + + if ((info->sm_5v_connection_status != _5v_connected_verified) || + !(info->regulator)) { + mod_timer(&info->sm_timer, jiffies + msecs_to_jiffies(100)); + goto out; + } + + /* if we made it here, we have a verified 5v connection */ + + 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)"); + dev_info(info->dev, "5v current limit set to %u.\n", + NON_USB_5V_SUPPLY_CURRENT_LIMIT_MA); + + info->is_ac_online = 1; + info->is_usb_online = 0; + ddi_power_set_4p2_ilimit( + NON_USB_5V_SUPPLY_CURRENT_LIMIT_MA); + ddi_bc_SetCurrentLimit( + NON_USB_5V_SUPPLY_CURRENT_LIMIT_MA /*mA*/); + if (regulator_set_current_limit(info->regulator, + 0, + NON_USB_5V_SUPPLY_CURRENT_LIMIT_MA*1000)) { + dev_err(info->dev, "reg_set_current(%duA) failed\n", + NON_USB_5V_SUPPLY_CURRENT_LIMIT_MA*1000); + } + ddi_bc_SetEnable(); + 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->is_usb_online & USB_N_SEND)) { + info->is_usb_online |= USB_N_SEND; + } + + + dev_dbg(info->dev, "%s: charge current set to %dmA\n", __func__, + POWERED_USB_5V_CURRENT_LIMIT_MA); + + if (regulator_set_current_limit(info->regulator, + 0, + POWERED_USB_5V_CURRENT_LIMIT_MA*1000)) { + dev_err(info->dev, "reg_set_current(%duA) failed\n", + POWERED_USB_5V_CURRENT_LIMIT_MA*1000); + } else { + ddi_bc_SetCurrentLimit(POWERED_USB_5V_CURRENT_LIMIT_MA/*mA*/); + ddi_bc_SetEnable(); + } + + 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"); + + +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, "battery charger init failed: %d\n", bcret); + ret = -EIO; + goto out; + } else { + + 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; + } else { + regulator_set_current_limit( + info->regulator, 0, 0); + regulator_set_mode(info->regulator, + REGULATOR_MODE_FAST); + } + } + } + + + + /* schedule first call to state machine */ + mod_timer(&info->sm_timer, jiffies + 1); +out: + mutex_unlock(&info->sm_lock); + return ret; +} + +#ifndef POWER_FIQ + +static irqreturn_t stmp3xxx_irq_dcdc4p2_bo(int irq, void *cookie) +{ +#ifdef DEBUG_IRQS + struct stmp3xxx_info *info = (struct stmp3xxx_info *)cookie; + dev_info(info->dev, "dcdc4p2 brownout interrupt occurred\n"); + +#endif + ddi_power_handle_dcdc4p2_bo(); + return IRQ_HANDLED; +} + +static irqreturn_t stmp3xxx_irq_batt_brnout(int irq, void *cookie) +{ +#ifdef DEBUG_IRQS + struct stmp3xxx_info *info = (struct stmp3xxx_info *)cookie; + dev_info(info->dev, "battery brownout interrupt occurred\n"); + ddi_power_disable_power_interrupts(); +#else + ddi_power_shutdown(); +#endif + return IRQ_HANDLED; +} +static irqreturn_t stmp3xxx_irq_vddd_brnout(int irq, void *cookie) +{ +#ifdef DEBUG_IRQS + struct stmp3xxx_info *info = (struct stmp3xxx_info *)cookie; + dev_info(info->dev, "vddd brownout interrupt occurred\n"); + ddi_power_disable_power_interrupts(); +#else + ddi_power_shutdown(); +#endif + return IRQ_HANDLED; +} +static irqreturn_t stmp3xxx_irq_vdda_brnout(int irq, void *cookie) +{ +#ifdef DEBUG_IRQS + struct stmp3xxx_info *info = (struct stmp3xxx_info *)cookie; + dev_info(info->dev, "vdda brownout interrupt occurred\n"); + ddi_power_disable_power_interrupts(); +#else + ddi_power_shutdown(); +#endif + return IRQ_HANDLED; +} + +static irqreturn_t stmp3xxx_irq_vdd5v_droop(int irq, void *cookie) +{ +#ifdef DEBUG_IRQS + struct stmp3xxx_info *info = (struct stmp3xxx_info *)cookie; + dev_info(info->dev, "vdd5v droop interrupt occurred\n"); +#endif + ddi_power_handle_vdd5v_droop(); + + return IRQ_HANDLED; +} + +#endif /* if POWER_FIQ */ + +static irqreturn_t stmp3xxx_irq_vddio_brnout(int irq, void *cookie) +{ +#ifdef DEBUG_IRQS + struct stmp3xxx_info *info = (struct stmp3xxx_info *)cookie; + dev_info(info->dev, "vddio brownout interrupt occurred\n"); + ddi_power_disable_power_interrupts(); +#else + ddi_power_handle_vddio_brnout(); +#endif + return IRQ_HANDLED; +} + +static irqreturn_t stmp3xxx_irq_vdd5v(int irq, void *cookie) +{ + struct stmp3xxx_info *info = (struct stmp3xxx_info *)cookie; + + + switch (ddi_power_GetPmu5vStatus()) { + + case new_5v_connection: + + ddi_power_disable_5v_connection_irq(); + dev_info(info->dev, "new 5v connection detected\n"); + info->sm_new_5v_connection_jiffies = jiffies; + mod_timer(&info->sm_timer, jiffies + 1); + break; + + case new_5v_disconnection: + + /* due to 5v connect vddio bo chip bug, we need to + * disable vddio interrupts until we reset the 5v + * detection for 5v connect detect. We want to allow + * some debounce time before enabling connect detection. + * This is handled in the vdd5v_droop interrupt for now. + */ + /* ddi_power_enable_vddio_interrupt(false); */ + + ddi_power_disable_5v_connection_irq(); + dev_info(info->dev, "new 5v disconnection detected\n"); + info->sm_new_5v_disconnection_jiffies = jiffies; + mod_timer(&info->sm_timer, jiffies + 1); + break; + + default: + + break; + + } + + return IRQ_HANDLED; +} + +static int stmp3xxx_bat_probe(struct platform_device *pdev) +{ + struct stmp3xxx_info *info; + int ret = 0; + + + + ret = ddi_power_init_battery(); + if (ret) { + printk(KERN_ERR "Aborting power driver initialization\n"); + return 1; + } + + + 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->irq_vdd5v = platform_get_resource(pdev, IORESOURCE_IRQ, 0); + if (info->irq_vdd5v == NULL) { + printk(KERN_ERR "%s: failed to get irq resouce\n", __func__); + goto free_info; + } + + info->irq_vddio_brnout = platform_get_resource( + pdev, IORESOURCE_IRQ, 5); + if (info->irq_vddio_brnout == NULL) { + printk(KERN_ERR "%s: failed to get irq resouce\n", __func__); + goto free_info; + } + +#ifndef POWER_FIQ + info->irq_dcdc4p2_bo = platform_get_resource(pdev, IORESOURCE_IRQ, 1); + if (info->irq_dcdc4p2_bo == NULL) { + printk(KERN_ERR "%s: failed to get irq resouce\n", __func__); + goto free_info; + } + + info->irq_batt_brnout = platform_get_resource(pdev, IORESOURCE_IRQ, 2); + if (info->irq_batt_brnout == NULL) { + printk(KERN_ERR "%s: failed to get irq resouce\n", __func__); + goto free_info; + } + + info->irq_vddd_brnout = platform_get_resource(pdev, IORESOURCE_IRQ, 3); + if (info->irq_vddd_brnout == NULL) { + printk(KERN_ERR "%s: failed to get irq resouce\n", __func__); + goto free_info; + } + + info->irq_vdda_brnout = platform_get_resource(pdev, IORESOURCE_IRQ, 4); + if (info->irq_vdda_brnout == NULL) { + printk(KERN_ERR "%s: failed to get irq resouce\n", __func__); + goto free_info; + } + + + info->irq_vdd5v_droop = platform_get_resource(pdev, IORESOURCE_IRQ, 6); + if (info->irq_vdd5v_droop == NULL) { + printk(KERN_ERR "%s: failed to get irq resouce\n", __func__); + goto free_info; + } +#endif + + + 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 */ + + __raw_writel(BM_POWER_5VCTRL_ENABLE_LINREG_ILIMIT, + REGS_POWER_BASE + HW_POWER_5VCTRL_CLR); + + ret = bc_sm_restart(info); + if (ret) + goto free_info; + + + ret = request_irq(info->irq_vdd5v->start, + stmp3xxx_irq_vdd5v, IRQF_DISABLED | IRQF_SHARED, + pdev->name, info); + if (ret) { + dev_err(info->dev, "failed to request irq\n"); + goto stop_sm; + } + + ret = request_irq(info->irq_vddio_brnout->start, + stmp3xxx_irq_vddio_brnout, IRQF_DISABLED, + pdev->name, info); + if (ret) { + dev_err(info->dev, "failed to request irq\n"); + goto stop_sm; + } + +#ifndef POWER_FIQ + ret = request_irq(info->irq_dcdc4p2_bo->start, + stmp3xxx_irq_dcdc4p2_bo, IRQF_DISABLED, + pdev->name, info); + if (ret) { + dev_err(info->dev, "failed to request irq\n"); + goto stop_sm; + } + + ret = request_irq(info->irq_batt_brnout->start, + stmp3xxx_irq_batt_brnout, IRQF_DISABLED, + pdev->name, info); + if (ret) { + dev_err(info->dev, "failed to request irq\n"); + goto stop_sm; + } + + ret = request_irq(info->irq_vddd_brnout->start, + stmp3xxx_irq_vddd_brnout, IRQF_DISABLED, + pdev->name, info); + if (ret) { + dev_err(info->dev, "failed to request irq\n"); + goto stop_sm; + } + + ret = request_irq(info->irq_vdda_brnout->start, + stmp3xxx_irq_vdda_brnout, IRQF_DISABLED, + pdev->name, info); + if (ret) { + dev_err(info->dev, "failed to request irq\n"); + goto stop_sm; + } + + + ret = request_irq(info->irq_vdd5v_droop->start, + stmp3xxx_irq_vdd5v_droop, IRQF_DISABLED, + pdev->name, info); + if (ret) { + dev_err(info->dev, "failed to request irq\n"); + goto stop_sm; + } +#endif + + 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; + } + + /* handoff protection handling from bootlets protection method + * to kernel protection method + */ + init_protection(info); + + /* enable usb device presence detection */ + __raw_writel(BM_USBPHY_CTRL_ENDEVPLUGINDETECT, + REGS_USBPHY_BASE + HW_USBPHY_CTRL_SET); + + return 0; + +unregister_ac: + power_supply_unregister(&info->ac); +unregister_bat: + power_supply_unregister(&info->bat); +free_irq: + free_irq(info->irq_vdd5v->start, pdev); + free_irq(info->irq_vddio_brnout->start, pdev); +#ifndef POWER_FIQ + free_irq(info->irq_dcdc4p2_bo->start, pdev); + free_irq(info->irq_batt_brnout->start, pdev); + free_irq(info->irq_vddd_brnout->start, pdev); + free_irq(info->irq_vdda_brnout->start, pdev); + free_irq(info->irq_vdd5v_droop->start, pdev); +#endif + +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->irq_vdd5v->start, pdev); + free_irq(info->irq_vddio_brnout->start, pdev); +#ifndef POWER_FIQ + free_irq(info->irq_dcdc4p2_bo->start, pdev); + free_irq(info->irq_batt_brnout->start, pdev); + free_irq(info->irq_vddd_brnout->start, pdev); + free_irq(info->irq_vdda_brnout->start, pdev); + free_irq(info->irq_vdd5v_droop->start, pdev); +#endif + 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); + + /* enable USB 5v wake up so don't disable irq here*/ + + 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( + NON_USB_5V_SUPPLY_CURRENT_LIMIT_MA /*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(POWERED_USB_5V_CURRENT_LIMIT_MA /*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 */ + __raw_writel(BM_POWER_CTRL_ENIRQ_VDD5V_GT_VDDIO, + REGS_POWER_BASE + HW_POWER_CTRL_SET); + + /* 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 power_relinquish(void *data, int relinquish) +{ + return -1; +} + +static struct fiq_handler power_fiq = { + .name = "stmp3xxx-battery", + .fiq_op = power_relinquish +}; + +static struct pt_regs fiq_regs; +extern char power_fiq_start[], power_fiq_end[]; +extern void lock_vector_tlb(void *); +extern long power_fiq_count; +static struct proc_dir_entry *power_fiq_proc; + +static int __init stmp3xxx_bat_init(void) +{ +#ifdef POWER_FIQ + int ret; + ret = claim_fiq(&power_fiq); + if (ret) { + pr_err("Can't claim fiq"); + } else { + get_fiq_regs(&fiq_regs); + set_fiq_handler(power_fiq_start, power_fiq_end-power_fiq_start); + lock_vector_tlb((void *)0xffff0000); + lock_vector_tlb(REGS_POWER_BASE); + + /* disable interrupts to be configured as FIQs */ + __raw_writel(BM_ICOLL_INTERRUPTn_ENABLE, + HW_ICOLL_INTERRUPTn_CLR_ADDR(IRQ_DCDC4P2_BO)); + + __raw_writel(BM_ICOLL_INTERRUPTn_ENABLE, + HW_ICOLL_INTERRUPTn_CLR_ADDR(IRQ_BATT_BRNOUT)); + + __raw_writel(BM_ICOLL_INTERRUPTn_ENABLE, + HW_ICOLL_INTERRUPTn_CLR_ADDR(IRQ_VDDD_BRNOUT)); + + __raw_writel(BM_ICOLL_INTERRUPTn_ENABLE, + HW_ICOLL_INTERRUPTn_CLR_ADDR(IRQ_VDD18_BRNOUT)); + + __raw_writel(BM_ICOLL_INTERRUPTn_ENABLE, + HW_ICOLL_INTERRUPTn_CLR_ADDR(IRQ_VDD5V_DROOP)); + + /* Enable these interrupts as FIQs */ + __raw_writel(BM_ICOLL_INTERRUPTn_ENFIQ, + HW_ICOLL_INTERRUPTn_SET_ADDR(IRQ_DCDC4P2_BO)); + + __raw_writel(BM_ICOLL_INTERRUPTn_ENFIQ, + HW_ICOLL_INTERRUPTn_SET_ADDR(IRQ_BATT_BRNOUT)); + + __raw_writel(BM_ICOLL_INTERRUPTn_ENFIQ, + HW_ICOLL_INTERRUPTn_SET_ADDR(IRQ_VDDD_BRNOUT)); + + __raw_writel(BM_ICOLL_INTERRUPTn_ENFIQ, + HW_ICOLL_INTERRUPTn_SET_ADDR(IRQ_VDD18_BRNOUT)); + + __raw_writel(BM_ICOLL_INTERRUPTn_ENFIQ, + HW_ICOLL_INTERRUPTn_SET_ADDR(IRQ_VDD5V_DROOP)); + + /* enable FIQ functionality */ + __raw_writel(BM_ICOLL_CTRL_FIQ_FINAL_ENABLE, + HW_ICOLL_CTRL_SET_ADDR); + + /* enable these interrupts */ + __raw_writel(BM_ICOLL_INTERRUPTn_ENABLE, + HW_ICOLL_INTERRUPTn_SET_ADDR(IRQ_DCDC4P2_BO)); + + __raw_writel(BM_ICOLL_INTERRUPTn_ENABLE, + HW_ICOLL_INTERRUPTn_SET_ADDR(IRQ_BATT_BRNOUT)); + + __raw_writel(BM_ICOLL_INTERRUPTn_ENABLE, + HW_ICOLL_INTERRUPTn_SET_ADDR(IRQ_VDDD_BRNOUT)); + + __raw_writel(BM_ICOLL_INTERRUPTn_ENABLE, + HW_ICOLL_INTERRUPTn_SET_ADDR(IRQ_VDD18_BRNOUT)); + + __raw_writel(BM_ICOLL_INTERRUPTn_ENABLE, + HW_ICOLL_INTERRUPTn_SET_ADDR(IRQ_VDD5V_DROOP)); + + } +#endif + 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"); |