summaryrefslogtreecommitdiff
path: root/drivers/power/mxs
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/power/mxs')
-rw-r--r--drivers/power/mxs/Makefile9
-rw-r--r--drivers/power/mxs/ddi_bc_api.c559
-rw-r--r--drivers/power/mxs/ddi_bc_hw.c397
-rw-r--r--drivers/power/mxs/ddi_bc_hw.h77
-rw-r--r--drivers/power/mxs/ddi_bc_init.c188
-rw-r--r--drivers/power/mxs/ddi_bc_internal.h53
-rw-r--r--drivers/power/mxs/ddi_bc_ramp.c724
-rw-r--r--drivers/power/mxs/ddi_bc_ramp.h50
-rw-r--r--drivers/power/mxs/ddi_bc_sm.c918
-rw-r--r--drivers/power/mxs/ddi_bc_sm.h46
-rw-r--r--drivers/power/mxs/ddi_power_battery.c1908
-rw-r--r--drivers/power/mxs/ddi_power_battery.h95
-rw-r--r--drivers/power/mxs/fiq.S125
-rw-r--r--drivers/power/mxs/linux.c1213
14 files changed, 6362 insertions, 0 deletions
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..33883075e50c
--- /dev/null
+++ b/drivers/power/mxs/linux.c
@@ -0,0 +1,1213 @@
+/*
+ * 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 regulator *onboard_vbus5v;
+
+ 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;
+ int onboard_vbus5v_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");
+ if (info->onboard_vbus5v) {
+ if (regulator_is_enabled(
+ info->onboard_vbus5v) > 0) {
+ info->onboard_vbus5v_online = 1;
+ pr_debug("When supply from \
+ onboard vbus 5v ,\
+ DO NOT switch to 4p2 \n");
+ break;
+ }
+ }
+#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->onboard_vbus5v_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)
+{
+ struct mxs_info *info;
+ switch (psp) {
+ case POWER_SUPPLY_PROP_ONLINE:
+ if (psy->type == POWER_SUPPLY_TYPE_MAINS) {
+ /* ac online */
+ info = container_of(psy, struct mxs_info, ac);
+ val->intval = info->onboard_vbus5v_online ?
+ 0 : is_ac_online();
+ } else {
+ /* usb online */
+ info = container_of(psy, struct mxs_info, usb);
+ val->intval = info->onboard_vbus5v_online ?
+ 0 : 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()
+ && !info->onboard_vbus5v_online) ?
+ 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 || info->onboard_vbus5v_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 {
+
+ if (info->onboard_vbus5v_online == 0) {
+ ddi_bc_SetCurrentLimit(
+ POWERED_USB_5V_CURRENT_LIMIT_MA/*mA*/);
+ ddi_bc_SetEnable();
+ } else
+ pr_debug("DO NOT charge from onboard 5v");
+ }
+
+ 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);
+
+
+ info->onboard_vbus5v = regulator_get(NULL, "vbus5v");
+ if (IS_ERR(info->regulator)) {
+
+ pr_debug("No onboard vbus 5v reg provided\n");
+ info->onboard_vbus5v = NULL;
+ }
+ 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");