summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRobert Lee <robert.lee@freescale.com>2009-12-02 20:54:38 -0600
committerDavid Ungar <david.ungar@timesys.com>2010-04-19 17:03:32 -0400
commit7c3efb1e821de58d416eab19428ca10bb13094f9 (patch)
tree2b482b4e4439102820b2edb8f1a5b0097f00c5b4
parent01d959cd155926db3cbb6090a2048054cd40eebf (diff)
ENGR00118995 iMX23: power supply protection mechanism must be implemented.
iMX23: power supply protection mechanism must be implemented in order to properly protect the SoC from damage or getting into a locked up state. Several chip errata exists which (some are documented and some are not yet documented at the time of this writing). Signed-off-by: Robert Lee <robert.lee@freescale.com>
-rw-r--r--arch/arm/mach-stmp378x/pm.c18
-rw-r--r--arch/arm/plat-stmp3xxx/devices.c33
-rw-r--r--drivers/power/stmp37xx/ddi_bc_hw.c63
-rw-r--r--drivers/power/stmp37xx/ddi_bc_init.c16
-rw-r--r--drivers/power/stmp37xx/ddi_bc_sm.c13
-rw-r--r--drivers/power/stmp37xx/ddi_power_battery.c966
-rw-r--r--drivers/power/stmp37xx/ddi_power_battery.h48
-rw-r--r--drivers/power/stmp37xx/linux.c607
8 files changed, 1429 insertions, 335 deletions
diff --git a/arch/arm/mach-stmp378x/pm.c b/arch/arm/mach-stmp378x/pm.c
index a3662c3a5627..32f87ee18a9d 100644
--- a/arch/arm/mach-stmp378x/pm.c
+++ b/arch/arm/mach-stmp378x/pm.c
@@ -123,6 +123,8 @@ static void stmp378x_standby(void)
/* half the fets */
__raw_writel(BM_POWER_MINPWR_HALF_FETS,
REGS_POWER_BASE + HW_POWER_MINPWR_SET);
+ __raw_writel(BM_POWER_MINPWR_DOUBLE_FETS,
+ REGS_POWER_BASE + HW_POWER_MINPWR_CLR);
__raw_writel(BM_POWER_LOOPCTRL_CM_HYST_THRESH,
REGS_POWER_BASE + HW_POWER_LOOPCTRL_CLR);
@@ -130,6 +132,15 @@ static void stmp378x_standby(void)
REGS_POWER_BASE + HW_POWER_LOOPCTRL_CLR);
__raw_writel(BM_POWER_LOOPCTRL_EN_DF_HYST,
REGS_POWER_BASE + HW_POWER_LOOPCTRL_CLR);
+
+
+ stmp3xxx_clearl(BM_POWER_LOOPCTRL_CM_HYST_THRESH,
+ REGS_POWER_BASE + HW_POWER_LOOPCTRL);
+ stmp3xxx_clearl(BM_POWER_LOOPCTRL_EN_CM_HYST,
+ REGS_POWER_BASE + HW_POWER_LOOPCTRL);
+ stmp3xxx_clearl(BM_POWER_LOOPCTRL_EN_DF_HYST,
+ REGS_POWER_BASE + HW_POWER_LOOPCTRL);
+
/* enable PFM */
__raw_writel(BM_POWER_LOOPCTRL_HYST_SIGN,
REGS_POWER_BASE + HW_POWER_LOOPCTRL_SET);
@@ -163,6 +174,13 @@ static void stmp378x_standby(void)
(3 << BP_POWER_LOOPCTRL_EN_RCSCALE),
REGS_POWER_BASE + HW_POWER_LOOPCTRL);
+ /* double the fets */
+ __raw_writel(BM_POWER_MINPWR_HALF_FETS,
+ REGS_POWER_BASE + HW_POWER_MINPWR_CLR);
+ __raw_writel(BM_POWER_MINPWR_DOUBLE_FETS,
+ REGS_POWER_BASE + HW_POWER_MINPWR_SET);
+
+
/* Restore VDDD */
__raw_writel(reg_vddd, REGS_POWER_BASE + HW_POWER_VDDDCTRL);
diff --git a/arch/arm/plat-stmp3xxx/devices.c b/arch/arm/plat-stmp3xxx/devices.c
index e55846559362..c0502ac9f506 100644
--- a/arch/arm/plat-stmp3xxx/devices.c
+++ b/arch/arm/plat-stmp3xxx/devices.c
@@ -484,11 +484,42 @@ struct platform_device stmp3xxx_dcp = {
};
static struct resource battery_resource[] = {
- {
+ {/* 0 */
.flags = IORESOURCE_IRQ,
.start = IRQ_VDD5V,
.end = IRQ_VDD5V,
},
+ {/* 1 */
+ .flags = IORESOURCE_IRQ,
+ .start = IRQ_DCDC4P2_BO,
+ .end = IRQ_DCDC4P2_BO,
+ },
+ {/* 2 */
+ .flags = IORESOURCE_IRQ,
+ .start = IRQ_BATT_BRNOUT,
+ .end = IRQ_BATT_BRNOUT,
+ },
+ {/* 3 */
+ .flags = IORESOURCE_IRQ,
+ .start = IRQ_VDDD_BRNOUT,
+ .end = IRQ_VDDD_BRNOUT,
+ },
+ {/* 4 */
+ .flags = IORESOURCE_IRQ,
+ .start = IRQ_VDD18_BRNOUT,
+ .end = IRQ_VDD18_BRNOUT,
+ },
+ {/* 5 */
+ .flags = IORESOURCE_IRQ,
+ .start = IRQ_VDDIO_BRNOUT,
+ .end = IRQ_VDDIO_BRNOUT,
+ },
+ {/* 6 */
+ .flags = IORESOURCE_IRQ,
+ .start = IRQ_VDD5V_DROOP,
+ .end = IRQ_VDD5V_DROOP,
+ },
+
};
struct platform_device stmp3xxx_battery = {
diff --git a/drivers/power/stmp37xx/ddi_bc_hw.c b/drivers/power/stmp37xx/ddi_bc_hw.c
index fab82a738b01..71b170021cb0 100644
--- a/drivers/power/stmp37xx/ddi_bc_hw.c
+++ b/drivers/power/stmp37xx/ddi_bc_hw.c
@@ -75,69 +75,6 @@ ddi_bc_BatteryMode_t ddi_bc_hwGetBatteryMode(void)
return (ddi_bc_BatteryMode_t) ddi_power_GetBatteryMode();
}
-////////////////////////////////////////////////////////////////////////////////
-//!
-//! \brief Report the bias current source.
-//!
-//! \fntype Function
-//!
-//! This function Battery Charger.
-//!
-//! \retval A value that indicates the current bias current source.
-//!
-////////////////////////////////////////////////////////////////////////////////
-ddi_bc_BiasCurrentSource_t ddi_bc_hwGetBiasCurrentSource(void)
-{
- // TODO: replace ddi_bc_BiasCurrentSource_t() with the function below.
- return (ddi_bc_BiasCurrentSource_t) ddi_power_GetBiasCurrentSource();
-}
-
-////////////////////////////////////////////////////////////////////////////////
-//!
-//! \brief Sets the bias current source.
-//!
-//! \fntype Function
-//!
-//! This function sets the bias current source used by the battery charger
-//! hardware. The battery charger is usually configured to use an externally
-//! generated bias current, which is expected to be quite precise. To reduce
-//! component count, for example, it can be configured to generate a lesser-
-//! quality bias current internally.
-//!
-//! \param[in] source Indicates the source of the bias current.
-//!
-//! \retval DDI_BC_STATUS_SUCCESS If the operation succeeded.
-//! \retval DDI_BC_STATUS_BAD_ARGUMENT If the given source argument isn't one
-//! of the expected values.
-//!
-////////////////////////////////////////////////////////////////////////////////
-ddi_bc_Status_t ddi_bc_hwSetBiasCurrentSource(ddi_bc_BiasCurrentSource_t source)
-{
- ddi_power_BiasCurrentSource_t eSource =
- (ddi_power_BiasCurrentSource_t) source;
-
- //--------------------------------------------------------------------------
- // In the following code, there are two points that can be confusing. Both
- // of these problems derive from silliness in the data sheet.
- //
- // The field of interest here is HW_POWER_BATTCHRG.USE_EXTERN_R. This is
- // poorly named for two reasons:
- //
- // 1) What we're really doing is selecting the source of the bias current.
- // If the application chooses to use an external bias current, then
- // there will, of course, be a resistor involved (apparently, a good-
- // quality one at about 620 Ohms).
- //
- // 2) If the bit is SET, that tells the hardware to use the INTERNAL bias
- // current. If this bit is CLEAR, that tells the hardware to use the
- // EXTERNAL bias current. Thus, the actual logic for this bit is
- // reversed from the sense indicated by its name.
- //--------------------------------------------------------------------------
-
- // TODO: replace all ddi_bc_hwSetBiasCurrentSource() with function below.
- return (ddi_bc_Status_t) ddi_power_SetBiasCurrentSource(eSource);
-
-}
////////////////////////////////////////////////////////////////////////////////
//!
diff --git a/drivers/power/stmp37xx/ddi_bc_init.c b/drivers/power/stmp37xx/ddi_bc_init.c
index ef323b33b74f..246a35dcb3df 100644
--- a/drivers/power/stmp37xx/ddi_bc_init.c
+++ b/drivers/power/stmp37xx/ddi_bc_init.c
@@ -152,22 +152,6 @@ ddi_bc_Status_t ddi_bc_Init(ddi_bc_Cfg_t * pCfg)
g_ddi_bc_Configuration = *pCfg;
//--------------------------------------------------------------------------
- // Set the bias current source.
- //--------------------------------------------------------------------------
-
- {
- ddi_power_BiasCurrentSource_t Flag;
-
- Flag =
- g_ddi_bc_Configuration.useInternalBias ?
- DDI_POWER_INTERNAL_BIAS_CURRENT :
- DDI_POWER_EXTERNAL_BIAS_CURRENT;
-
- ddi_power_SetBiasCurrentSource(Flag);
-
- }
-
- //--------------------------------------------------------------------------
// Turn the charger hardware off. This is a very important initial condition
// because we only flip the power switch on the hardware when we make
// transitions. Baseline, it needs to be off.
diff --git a/drivers/power/stmp37xx/ddi_bc_sm.c b/drivers/power/stmp37xx/ddi_bc_sm.c
index 9ab44f133876..c4175688d9ab 100644
--- a/drivers/power/stmp37xx/ddi_bc_sm.c
+++ b/drivers/power/stmp37xx/ddi_bc_sm.c
@@ -416,13 +416,12 @@ static ddi_bc_Status_t ddi_bc_WaitingToCharge(void)
if (u16ExternalBatteryPowerVoltageCheck) {
if ((u16ExternalBatteryPowerVoltageCheck - u16BatteryVoltage) >
300) {
- //----------------------------------------------------------------------
- // If control arrives here, battery voltage has dropped too quickly after
- // the first charge cycle. We think an external voltage regulator is
- // connected. If the DCDCs are on and this voltage drops too quickly,
- // it will cause large droops and possible brownouts so we disable
- // charging.
- //----------------------------------------------------------------------
+ /*
+ * 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;
diff --git a/drivers/power/stmp37xx/ddi_power_battery.c b/drivers/power/stmp37xx/ddi_power_battery.c
index 11cdd05d56db..e2e46dd535a9 100644
--- a/drivers/power/stmp37xx/ddi_power_battery.c
+++ b/drivers/power/stmp37xx/ddi_power_battery.c
@@ -76,15 +76,31 @@
#define VDD4P2_ENABLED
+#define DDI_POWER_BATTERY_XFER_THRESHOLD_MV 3200
+
+
+#ifndef BATTERY_VOLTAGE_CMPTRIP100_THRESHOLD_MV
+#define BATTERY_VOLTAGE_CMPTRIP100_THRESHOLD_MV 4000
+#endif
+
+#ifndef BATTERY_VOLTAGE_CMPTRIP105_THRESHOLD_MV
+#define BATTERY_VOLTAGE_CMPTRIP105_THRESHOLD_MV 3800
+#endif
+
+/* #define DEBUG_IRQS */
+
+/* to be re-enabled once FIQ functionality is added */
+#define DISABLE_VDDIO_BO_PROTECTION
////////////////////////////////////////////////////////////////////////////////
// Globals & Variables
////////////////////////////////////////////////////////////////////////////////
/* Select your 5V Detection method */
- static ddi_power_5vDetection_t DetectionMethod =
- DDI_POWER_5V_VDD5V_GT_VDDIO;
-/* static ddi_power_5vDetection_t DetectionMethod = DDI_POWER_5V_VBUSVALID; */
+
+/* static ddi_power_5vDetection_t DetectionMethod =
+ DDI_POWER_5V_VDD5V_GT_VDDIO; */
+static ddi_power_5vDetection_t DetectionMethod = DDI_POWER_5V_VBUSVALID;
////////////////////////////////////////////////////////////////////////////////
// Code
@@ -243,6 +259,14 @@ void ddi_power_execute_5v_to_battery_handoff(void)
__raw_writel(BM_POWER_5VCTRL_PWD_CHARGE_4P2,
REGS_POWER_BASE + HW_POWER_5VCTRL_SET);
+
+ /* make VBUSVALID_TRSH 4400mV and set PWD_CHARGE_4P2 */
+ __raw_writel(BM_POWER_5VCTRL_VBUSVALID_TRSH,
+ HW_POWER_5VCTRL_CLR_ADDR);
+
+ __raw_writel(BF_POWER_5VCTRL_VBUSVALID_TRSH(VBUSVALID_THRESH_4_40V),
+ HW_POWER_5VCTRL_SET_ADDR);
+
#else
// VDDD has different configurations depending on the battery type
// and battery level.
@@ -333,75 +357,9 @@ void ddi_power_enable_battery_to_5v_handoff(void)
*/
void ddi_power_execute_battery_to_5v_handoff(void)
{
- u32 val;
#ifdef VDD4P2_ENABLED
-
- bool orig_vbusvalid_5vdetect = false;
- bool orig_pwd_bo = false;
- uint8_t orig_vbusvalid_threshold;
-
-
- /* recording orignal values that will be modified
- * temporarlily to handle a chip bug. See chip errata
- * for CQ ENGR00115837
- */
- orig_vbusvalid_threshold =
- (__raw_readl(HW_POWER_5VCTRL_ADDR)
- & BM_POWER_5VCTRL_VBUSVALID_TRSH)
- >> BP_POWER_5VCTRL_VBUSVALID_TRSH;
-
- if (__raw_readl(HW_POWER_5VCTRL_ADDR) &
- BM_POWER_5VCTRL_VBUSVALID_5VDETECT)
- orig_vbusvalid_5vdetect = true;
-
- if (__raw_readl(HW_POWER_MINPWR_ADDR) &
- BM_POWER_MINPWR_PWD_BO)
- orig_pwd_bo = true;
-
- /* disable mechanisms that get erroneously tripped by
- * when setting the DCDC4P2 EN_DCDC
- */
- __raw_writel(BM_POWER_5VCTRL_VBUSVALID_5VDETECT,
- REGS_POWER_BASE + HW_POWER_5VCTRL_CLR);
- __raw_writel(BF_POWER_5VCTRL_VBUSVALID_TRSH(0x7),
- REGS_POWER_BASE + HW_POWER_5VCTRL_CLR);
-
- __raw_writel(BM_POWER_MINPWR_PWD_BO,
- REGS_POWER_BASE + HW_POWER_MINPWR_SET);
-
- val = __raw_readl(REGS_POWER_BASE + HW_POWER_DCDC4P2);
- val |= BM_POWER_DCDC4P2_ENABLE_4P2;
- __raw_writel(val, REGS_POWER_BASE + HW_POWER_DCDC4P2);
-
-
- /* as a todo, we'll want to ramp up the charge current first
- * to minimize disturbances on the VDD5V rail
- */
- ddi_power_SetChargerPowered(1);
-
- /* Until the previous todo is completed, we'll want to give a delay
- * to allow the charging up of the 4p2 capacitor.
- */
- udelay(10);
-
- val = __raw_readl(REGS_POWER_BASE + HW_POWER_DCDC4P2);
- val |= BM_POWER_DCDC4P2_ENABLE_DCDC;
- __raw_writel(val, REGS_POWER_BASE + HW_POWER_DCDC4P2);
-
- udelay(20);
- /* coming from a known value of 0 so this is ok */
- __raw_writel(BF_POWER_5VCTRL_VBUSVALID_TRSH(orig_vbusvalid_threshold),
- REGS_POWER_BASE + HW_POWER_5VCTRL_SET);
-
- if (orig_vbusvalid_5vdetect)
- __raw_writel(BM_POWER_5VCTRL_VBUSVALID_5VDETECT,
- REGS_POWER_BASE + HW_POWER_5VCTRL_SET);
-
-
- if (!orig_pwd_bo)
- __raw_writel(BM_POWER_MINPWR_PWD_BO,
- REGS_POWER_BASE + HW_POWER_MINPWR_CLR);
+ ddi_power_Enable4p2(450);
#else
// Disable the DCDC during 5V connections.
__raw_writel(BM_POWER_5VCTRL_ENABLE_DCDC,
@@ -460,6 +418,369 @@ void ddi_power_execute_battery_to_5v_handoff(void)
#endif
}
+
+void ddi_power_Start4p2Dcdc(bool battery_ready)
+{
+
+ uint32_t temp_reg, old_values;
+
+ /* set vbusvalid threshold to 2.9V because of errata */
+ __raw_writel(BM_POWER_5VCTRL_VBUSVALID_TRSH,
+ HW_POWER_5VCTRL_CLR_ADDR);
+
+
+#if 0
+ if (battery_ready)
+ ddi_power_EnableBatteryIrq();
+ else
+ enable_4p2_fiq_shutdown();
+#endif
+
+ /* enable hardware shutdown on battery brownout */
+ __raw_writel(
+ BM_POWER_BATTMONITOR_PWDN_BATTBRNOUT |
+ __raw_readl(HW_POWER_BATTMONITOR_ADDR),
+ HW_POWER_BATTMONITOR_ADDR);
+
+ /* set VBUS DROOP threshold to 4.3V */
+ __raw_writel(BM_POWER_5VCTRL_VBUSDROOP_TRSH,
+ HW_POWER_5VCTRL_CLR_ADDR);
+
+ /* turn of vbus valid detection. Part of errate
+ * workaround. */
+ __raw_writel(BM_POWER_5VCTRL_PWRUP_VBUS_CMPS,
+ HW_POWER_5VCTRL_SET_ADDR);
+
+ __raw_writel(BM_POWER_5VCTRL_VBUSVALID_5VDETECT,
+ HW_POWER_5VCTRL_CLR_ADDR);
+
+
+ temp_reg = (BM_POWER_CTRL_ENIRQ_VDDD_BO |
+ BM_POWER_CTRL_ENIRQ_VDDA_BO |
+ BM_POWER_CTRL_ENIRQ_VDDIO_BO |
+ BM_POWER_CTRL_ENIRQ_VDD5V_DROOP |
+ BM_POWER_CTRL_ENIRQ_VBUS_VALID);
+
+ /* save off old brownout enable values */
+ old_values = __raw_readl(HW_POWER_CTRL_ADDR) &
+ temp_reg;
+
+ /* disable irqs affected by errata */
+ __raw_writel(temp_reg, HW_POWER_CTRL_CLR_ADDR);
+
+ /* Enable DCDC from 4P2 */
+ __raw_writel(__raw_readl(HW_POWER_DCDC4P2_ADDR) |
+ BM_POWER_DCDC4P2_ENABLE_DCDC,
+ HW_POWER_DCDC4P2_ADDR);
+
+ /* give a delay to check for errate noise problem */
+ mdelay(1);
+
+ temp_reg = (BM_POWER_CTRL_VDDD_BO_IRQ |
+ BM_POWER_CTRL_VDDA_BO_IRQ |
+ BM_POWER_CTRL_VDDIO_BO_IRQ |
+ BM_POWER_CTRL_VDD5V_DROOP_IRQ |
+ BM_POWER_CTRL_VBUSVALID_IRQ);
+
+ /* stay in this loop until the false brownout indciations
+ * no longer occur or until 5V actually goes away
+ */
+ while ((__raw_readl(HW_POWER_CTRL_ADDR) & temp_reg) &&
+ !(__raw_readl(HW_POWER_CTRL_ADDR) &
+ BM_POWER_CTRL_VDD5V_GT_VDDIO_IRQ)) {
+ __raw_writel(temp_reg, HW_POWER_CTRL_CLR_ADDR);
+
+ mdelay(1);
+ }
+
+ /* revert to previous enable irq values */
+ __raw_writel(old_values, HW_POWER_CTRL_SET_ADDR);
+
+ if (DetectionMethod == DDI_POWER_5V_VBUSVALID)
+ __raw_writel(BM_POWER_5VCTRL_VBUSVALID_5VDETECT,
+ HW_POWER_5VCTRL_SET_ADDR);
+}
+
+
+/* set the optimal CMPTRIP for the best possible 5V
+ * disconnection handling but without drawing power
+ * from the power on a stable 4p2 rails (at 4.2V).
+ */
+void ddi_power_handle_cmptrip(void)
+{
+ enum ddi_power_5v_status pmu_5v_status;
+ uint32_t temp = __raw_readl(HW_POWER_DCDC4P2_ADDR);
+ temp &= ~(BM_POWER_DCDC4P2_CMPTRIP);
+
+ pmu_5v_status = ddi_power_GetPmu5vStatus();
+
+ /* CMPTRIP should remain at 31 when 5v is disconnected
+ * or 5v is connected but hasn't been handled yet
+ */
+ if (pmu_5v_status != existing_5v_connection)
+ temp |= (31 << BP_POWER_DCDC4P2_CMPTRIP);
+ else if (ddi_power_GetBattery() >
+ BATTERY_VOLTAGE_CMPTRIP100_THRESHOLD_MV)
+ temp |= (1 << BP_POWER_DCDC4P2_CMPTRIP);
+ else if (ddi_power_GetBattery() >
+ BATTERY_VOLTAGE_CMPTRIP105_THRESHOLD_MV)
+ temp |= (24 << BP_POWER_DCDC4P2_CMPTRIP);
+ else
+ temp |= (31 << BP_POWER_DCDC4P2_CMPTRIP);
+
+
+ __raw_writel(temp, HW_POWER_DCDC4P2_ADDR);
+}
+
+void ddi_power_Init4p2Params(void)
+{
+ uint32_t temp;
+
+ ddi_power_handle_cmptrip();
+
+ temp = __raw_readl(HW_POWER_DCDC4P2_ADDR);
+
+ /* DROPOUT CTRL to 10, TRG to 0 */
+ temp &= ~(BM_POWER_DCDC4P2_TRG | BM_POWER_DCDC4P2_DROPOUT_CTRL);
+ temp |= (0xa << BP_POWER_DCDC4P2_DROPOUT_CTRL);
+
+ __raw_writel(temp, HW_POWER_DCDC4P2_ADDR);
+
+
+ temp = __raw_readl(HW_POWER_5VCTRL_ADDR);
+
+ /* HEADROOM_ADJ to 4, CHARGE_4P2_ILIMIT to 0 */
+ temp &= ~(BM_POWER_5VCTRL_HEADROOM_ADJ |
+ BM_POWER_5VCTRL_CHARGE_4P2_ILIMIT);
+ temp |= (4 << BP_POWER_5VCTRL_HEADROOM_ADJ);
+
+}
+
+bool ddi_power_IsBattRdyForXfer(void)
+{
+ uint16_t u16BatteryVoltage = ddi_power_GetBattery();
+
+ if (u16BatteryVoltage > DDI_POWER_BATTERY_XFER_THRESHOLD_MV)
+ return true;
+ else
+ return false;
+
+}
+
+void ddi_power_EnableVbusDroopIrq(void)
+{
+
+ __raw_writel(BM_POWER_CTRL_VDD5V_DROOP_IRQ,
+ HW_POWER_CTRL_CLR_ADDR);
+
+ __raw_writel(BM_POWER_CTRL_ENIRQ_VDD5V_DROOP,
+ HW_POWER_CTRL_SET_ADDR);
+
+}
+
+
+void ddi_power_Enable4p2(uint16_t target_current_limit_ma)
+{
+
+ uint16_t u16BatteryVoltage;
+ uint32_t temp_reg;
+
+ ddi_power_Init4p2Params();
+
+ /* disable 4p2 rail brownouts for now. (they
+ * should have already been off at this point) */
+ __raw_writel(BM_POWER_CTRL_ENIRQ_DCDC4P2_BO,
+ HW_POWER_CTRL_CLR_ADDR);
+
+ u16BatteryVoltage = ddi_power_GetBattery();
+
+ if (ddi_power_IsBattRdyForXfer()) {
+
+ /* PWD_CHARGE_4P2 should already be set but just in case... */
+ __raw_writel(BM_POWER_5VCTRL_PWD_CHARGE_4P2,
+ HW_POWER_5VCTRL_SET_ADDR);
+
+ /* set CMPTRIP to DCDC_4P2 pin >= BATTERY pin */
+ temp_reg = __raw_readl(HW_POWER_DCDC4P2_ADDR);
+ temp_reg &= ~(BM_POWER_DCDC4P2_CMPTRIP);
+ temp_reg |= (31 << BP_POWER_DCDC4P2_CMPTRIP);
+ __raw_writel(temp_reg, HW_POWER_DCDC4P2_ADDR);
+
+ /* since we have a good battery, we can go ahead
+ * and turn on the Dcdcing from the 4p2 source.
+ * This is helpful in working around the chip
+ * errata.
+ */
+ ddi_power_Start4p2Dcdc(true);
+
+ /* Enable VbusDroopIrq to handle errata */
+
+ /* set vbus droop detection level to 4.3V */
+ __raw_writel(BM_POWER_5VCTRL_VBUSDROOP_TRSH,
+ HW_POWER_5VCTRL_CLR_ADDR);
+
+ ddi_power_EnableVbusDroopIrq();
+ /* now that the DCDC4P2 problems are cleared,
+ * turn on and ramp up the 4p2 regulator
+ */
+ temp_reg = ddi_power_BringUp4p2Regulator(
+ target_current_limit_ma, true);
+
+ /* if we still have our 5V connection, we can disable
+ * battery brownout interrupt. This is because the
+ * VDD5V DROOP IRQ handler will also shutdown if battery
+ * is browned out and it will enable the battery brownout
+ * and bring VBUSVALID_TRSH level back to a normal level
+ * which caused the hardware battery brownout shutdown
+ * to be enabled. The benefit of this is that device
+ * that have detachable batteries (or devices going through
+ * the assembly line and running this firmware to test
+ * with) can avoid shutting down if 5V is present and
+ * battery voltage goes away.
+ */
+ if (!(__raw_readl(HW_POWER_CTRL_ADDR) &
+ (BM_POWER_CTRL_VBUSVALID_IRQ |
+ BM_POWER_CTRL_VDD5V_DROOP_IRQ))) {
+ ddi_power_EnableBatteryBoInterrupt(false);
+ }
+
+
+
+ printk(KERN_INFO "4P2 rail started. 5V current limit\
+ set to %dmA\n", temp_reg);
+
+ } else {
+
+ printk(KERN_ERR "4P2 rail was attempted to be started \
+ from a system\
+ with a very low battery voltage. This is not\
+ yet handled by the kernel driver, only by the\
+ bootlets. Remaining on battery power.\n");
+
+ if ((__raw_readl(HW_POWER_5VCTRL_ADDR) &&
+ BM_POWER_5VCTRL_ENABLE_DCDC))
+ ddi_power_EnableBatteryBoInterrupt(true);
+
+#if 0
+ /* enable hardware shutdown (if 5v disconnected)
+ * on battery brownout */
+ __raw_writel(
+ BM_POWER_BATTMONITOR_PWDN_BATTBRNOUT |
+ __raw_readl(HW_POWER_BATTMONITOR_ADDR),
+ HW_POWER_BATTMONITOR_ADDR);
+
+ /* turn on and ramp up the 4p2 regulator */
+ temp_reg = ddi_power_BringUp4p2Regulator(
+ target_current_limit_ma, false);
+
+ Configure4p2FiqShutdown();
+
+ SetVbusValidThresh(0);
+#endif
+ }
+}
+
+/* enable and ramp up 4p2 regulator */
+uint16_t ddi_power_BringUp4p2Regulator(
+ uint16_t target_current_limit_ma,
+ bool b4p2_dcdc_enabled)
+{
+ uint32_t temp_reg;
+ uint16_t charge_4p2_ilimit = 0;
+
+ /* initial current limit to 0 */
+ __raw_writel(BM_POWER_5VCTRL_CHARGE_4P2_ILIMIT,
+ HW_POWER_5VCTRL_CLR_ADDR);
+
+ __raw_writel(__raw_readl(HW_POWER_DCDC4P2_ADDR) |
+ BM_POWER_DCDC4P2_ENABLE_4P2,
+ HW_POWER_DCDC4P2_ADDR);
+
+ /* set 4p2 target voltage to zero */
+ temp_reg = __raw_readl(HW_POWER_DCDC4P2_ADDR);
+ temp_reg &= (~BM_POWER_DCDC4P2_TRG);
+ __raw_writel(temp_reg, HW_POWER_DCDC4P2_ADDR);
+
+ /* Enable 4P2 regulator*/
+ __raw_writel(BM_POWER_5VCTRL_PWD_CHARGE_4P2,
+ HW_POWER_5VCTRL_CLR_ADDR);
+
+ if (target_current_limit_ma > 780)
+ target_current_limit_ma = 780;
+
+ ddi_power_Set4p2BoLevel(4150);
+
+ /* possibly not necessary but recommended for unloaded
+ * 4p2 rail
+ */
+ __raw_writel(BM_POWER_CHARGE_ENABLE_LOAD,
+ HW_POWER_CHARGE_SET_ADDR);
+
+ while (charge_4p2_ilimit < target_current_limit_ma) {
+
+ if (__raw_readl(HW_POWER_CTRL_ADDR) &
+ (BM_POWER_CTRL_VBUSVALID_IRQ |
+ BM_POWER_CTRL_VDD5V_DROOP_IRQ))
+ break;
+
+
+ charge_4p2_ilimit += 100;
+ if (charge_4p2_ilimit > target_current_limit_ma)
+ charge_4p2_ilimit = target_current_limit_ma;
+
+ ddi_power_set_4p2_ilimit(charge_4p2_ilimit);
+
+ /* dcdc4p2 enable_dcdc must be enabled for
+ * 4p2 bo indication to function. If not enabled,
+ * skip using bo level detection
+ */
+ if (!(b4p2_dcdc_enabled))
+ msleep(1);
+ else if (__raw_readl(HW_POWER_STS_ADDR) &
+ BM_POWER_STS_DCDC_4P2_BO)
+ msleep(1);
+ else {
+ charge_4p2_ilimit = target_current_limit_ma;
+ ddi_power_set_4p2_ilimit(charge_4p2_ilimit);
+ }
+ }
+
+ ddi_power_Set4p2BoLevel(3600);
+
+ __raw_writel(BM_POWER_CTRL_DCDC4P2_BO_IRQ,
+ HW_POWER_CTRL_CLR_ADDR);
+
+ /* rail should now be up and loaded. Extra
+ * internal load is not necessary.
+ */
+ __raw_writel(BM_POWER_CHARGE_ENABLE_LOAD,
+ HW_POWER_CHARGE_CLR_ADDR);
+
+ return charge_4p2_ilimit;
+
+}
+
+
+void ddi_power_Set4p2BoLevel(uint16_t bo_voltage_mv)
+{
+ uint16_t bo_reg_value;
+ uint32_t temp;
+
+ if (bo_voltage_mv < 3600)
+ bo_voltage_mv = 3600;
+ else if (bo_voltage_mv > 4375)
+ bo_voltage_mv = 4375;
+
+ bo_reg_value = (bo_voltage_mv - 3600) / 25;
+
+ temp = __raw_readl(HW_POWER_DCDC4P2_ADDR);
+ temp &= (~BM_POWER_DCDC4P2_BO);
+ temp |= (bo_reg_value << BP_POWER_DCDC4P2_BO);
+ __raw_writel(temp, HW_POWER_DCDC4P2_ADDR);
+}
+
+
+
void ddi_power_init_handoff(void)
{
int val;
@@ -481,57 +802,96 @@ void ddi_power_init_handoff(void)
__raw_writel(val, REGS_POWER_BASE + HW_POWER_BATTMONITOR);
}
+
+void ddi_power_EnableBatteryInterrupt(bool enable)
+{
+
+ __raw_writel(BM_POWER_CTRL_BATT_BO_IRQ,
+ HW_POWER_CTRL_CLR_ADDR);
+
+ __raw_writel(BM_POWER_CTRL_ENIRQBATT_BO,
+ HW_POWER_CTRL_SET_ADDR);
+
+}
+
+
int ddi_power_init_battery(void)
{
- int ret;
+ int ret = 0;
+
+ if (!(__raw_readl(HW_POWER_5VCTRL_ADDR) &&
+ BM_POWER_5VCTRL_ENABLE_DCDC)) {
+ printk(KERN_ERR "WARNING: Power Supply not\
+ initialized correctly by \
+ pre-kernel bootlets. HW_POWER_5VCTRL \
+ ENABLE_DCDC should already be set. Kernel \
+ power driver behavior may not be reliable \n");
+ ret = 1;
+ }
/* the following code to enable automatic battery measurement
* should have already been enabled in the boot prep files. Not
- * sure if this is necessary or possibly suceptible to mis-coordination
+ * sure if this is necessary or possibly susceptible to
+ * mis-coordination
*/
- // Init LRADC channel 7
- ret = hw_lradc_init_ladder(BATTERY_VOLTAGE_CH,
- LRADC_DELAY_TRIGGER_BATTERY,
- 200);
- if (ret) {
- printk(KERN_ERR "%s: hw_lradc_init_ladder failed\n", __func__);
- return ret;
- }
-
- __raw_writel(BM_LRADC_CONVERSION_AUTOMATIC,
- REGS_LRADC_BASE + HW_LRADC_CONVERSION_SET);
-
- // Set li-ion mode
- __raw_writel(BF(2, LRADC_CONVERSION_SCALE_FACTOR),
- REGS_LRADC_BASE + HW_LRADC_CONVERSION_SET);
- // Turn off divide-by-two - we already have a divide-by-four
- // as part of the hardware
- __raw_writel(
- BF(1 << BATTERY_VOLTAGE_CH, LRADC_CTRL2_DIVIDE_BY_TWO),
- REGS_LRADC_BASE + HW_LRADC_CTRL2_CLR);
- __raw_writel(BM_POWER_CHARGE_ENABLE_FAULT_DETECT,
- REGS_POWER_BASE + HW_POWER_CHARGE_SET);
+ ret = !hw_lradc_present(BATTERY_VOLTAGE_CH);
- // kick off the trigger
- hw_lradc_set_delay_trigger_kick(LRADC_DELAY_TRIGGER_BATTERY, 1);
-
- __raw_writel(BM_POWER_LOOPCTRL_RCSCALE_THRESH,
- REGS_POWER_BASE + HW_POWER_LOOPCTRL_SET);
- __raw_writel(BF_POWER_LOOPCTRL_EN_RCSCALE(3),
- REGS_POWER_BASE + HW_POWER_LOOPCTRL_SET);
+ if (ret) {
+ printk(KERN_ERR "%s: hw_lradc_present failed\n", __func__);
+ return -ENODEV;
+ } else {
+ uint16_t wait_time = 0;
+
+ hw_lradc_configure_channel(BATTERY_VOLTAGE_CH, 0 /* div2 */ ,
+ 0 /* acc */ ,
+ 0 /* num_samples */);
+
+ /* Setup the trigger loop forever */
+ hw_lradc_set_delay_trigger(LRADC_DELAY_TRIGGER_BATTERY,
+ 1 << BATTERY_VOLTAGE_CH,
+ 1 << LRADC_DELAY_TRIGGER_BATTERY,
+ 0, 200);
+
+ /* Clear the accumulator & NUM_SAMPLES */
+ stmp3xxx_clearl(0xFFFFFFFF,
+ REGS_LRADC_BASE + HW_LRADC_CHn(BATTERY_VOLTAGE_CH));
+
+ /* clear previous "measurement performed" status */
+ __raw_writel(1 << BATTERY_VOLTAGE_CH,
+ HW_LRADC_CTRL1_CLR_ADDR);
+
+ /* set to LiIon scale factor */
+ __raw_writel(BM_LRADC_CONVERSION_SCALE_FACTOR,
+ HW_LRADC_CONVERSION_SET_ADDR);
+
+ /* kick off the trigger */
+ hw_lradc_set_delay_trigger_kick(
+ LRADC_DELAY_TRIGGER_BATTERY, 1);
+
+
+ /* wait for 1st converstion to be complete before
+ * enabling automatic copy to power supply
+ * peripheral
+ */
+ while (!(__raw_readl(HW_LRADC_CTRL1_ADDR) &
+ 1 << BATTERY_VOLTAGE_CH) &&
+ (wait_time < 10)) {
+ wait_time++;
+ udelay(1);
+ }
- __raw_writel(BM_POWER_MINPWR_HALF_FETS,
- REGS_POWER_BASE + HW_POWER_MINPWR_CLR);
- __raw_writel(BM_POWER_MINPWR_DOUBLE_FETS,
- REGS_POWER_BASE + HW_POWER_MINPWR_SET);
+ __raw_writel(BM_LRADC_CONVERSION_AUTOMATIC,
+ HW_LRADC_CONVERSION_SET_ADDR);
+ }
+#ifndef VDD4P2_ENABLED
/* prepare handoff */
ddi_power_init_handoff();
-
- return 0;
+#endif
+ return ret;
}
/*
@@ -619,11 +979,6 @@ uint16_t MeasureInternalDieTemperature(void)
////////////////////////////////////////////////////////////////////////////////
ddi_power_BatteryMode_t ddi_power_GetBatteryMode(void)
{
-#if 0
- return (__raw_readl(REGS_POWER_BASE + HW_POWER_STS) & BM_POWER_STS_MODE) ?
- DDI_POWER_BATT_MODE_ALKALINE_NIMH :
- DDI_POWER_BATT_MODE_LIION;
-#endif
return DDI_POWER_BATT_MODE_LIION;
}
@@ -850,40 +1205,6 @@ int ddi_power_SetBatteryBrownout(uint16_t u16BattBrownout_mV)
// Currents
////////////////////////////////////////////////////////////////////////////////
-////////////////////////////////////////////////////////////////////////////////
-//! Name: ddi_power_SetBiasCurrentSource
-//!
-//! \brief
-////////////////////////////////////////////////////////////////////////////////
-int ddi_power_SetBiasCurrentSource(ddi_power_BiasCurrentSource_t eSource)
-{
- switch (eSource) {
- case DDI_POWER_INTERNAL_BIAS_CURRENT:
- __raw_writel(BM_POWER_CHARGE_USE_EXTERN_R,
- REGS_POWER_BASE + HW_POWER_CHARGE_SET);
- break;
- case DDI_POWER_EXTERNAL_BIAS_CURRENT:
- __raw_writel(BM_POWER_CHARGE_USE_EXTERN_R,
- REGS_POWER_BASE + HW_POWER_CHARGE_CLR);
- break;
- default:
- return -EINVAL;
- }
-
- return 0;
-}
-
-////////////////////////////////////////////////////////////////////////////////
-//! Name: ddi_power_GetBiasCurrentSource
-//!
-//! \brief
-////////////////////////////////////////////////////////////////////////////////
-ddi_power_BiasCurrentSource_t ddi_power_GetBiasCurrentSource(void)
-{
- return (__raw_readl(REGS_POWER_BASE + HW_POWER_CHARGE) & BM_POWER_CHARGE_USE_EXTERN_R) ?
- DDI_POWER_INTERNAL_BIAS_CURRENT :
- DDI_POWER_EXTERNAL_BIAS_CURRENT;
-}
////////////////////////////////////////////////////////////////////////////////
//! Name: ddi_power_SetMaxBatteryChargeCurrent
@@ -1055,6 +1376,8 @@ bool ddi_power_Get5vPresentFlag(void)
return 0;
}
+
+
////////////////////////////////////////////////////////////////////////////////
//!
//! \brief Report on the die temperature.
@@ -1135,6 +1458,357 @@ bool ddi_power_GetPowerClkGate(void)
}
+enum ddi_power_5v_status ddi_power_GetPmu5vStatus(void)
+{
+
+ if (DetectionMethod == DDI_POWER_5V_VDD5V_GT_VDDIO) {
+
+ if (__raw_readl(HW_POWER_CTRL_ADDR) &
+ BM_POWER_CTRL_POLARITY_VDD5V_GT_VDDIO) {
+ if ((__raw_readl(HW_POWER_CTRL_ADDR) &
+ BM_POWER_CTRL_VDD5V_GT_VDDIO_IRQ) ||
+ ddi_power_Get5vPresentFlag())
+ return new_5v_connection;
+ else
+ return existing_5v_disconnection;
+ } else {
+ if ((__raw_readl(HW_POWER_CTRL_ADDR) &
+ BM_POWER_CTRL_VDD5V_GT_VDDIO_IRQ) ||
+ !ddi_power_Get5vPresentFlag() ||
+ ddi_power_Get5vDroopFlag())
+ return new_5v_disconnection;
+ else
+ return existing_5v_connection;
+ }
+ } else {
+
+ if (__raw_readl(HW_POWER_CTRL_ADDR) &
+ BM_POWER_CTRL_POLARITY_VBUSVALID) {
+ if ((__raw_readl(HW_POWER_CTRL_ADDR) &
+ BM_POWER_CTRL_VBUSVALID_IRQ) ||
+ ddi_power_Get5vPresentFlag())
+ return new_5v_connection;
+ else
+ return existing_5v_disconnection;
+ } else {
+ if ((__raw_readl(HW_POWER_CTRL_ADDR) &
+ BM_POWER_CTRL_VBUSVALID_IRQ) ||
+ !ddi_power_Get5vPresentFlag() ||
+ ddi_power_Get5vDroopFlag())
+ return new_5v_disconnection;
+ else
+ return existing_5v_connection;
+ }
+
+ }
+}
+
+void ddi_power_disable_5v_connection_irq(void)
+{
+
+ __raw_writel((BM_POWER_CTRL_ENIRQ_VBUS_VALID |
+ BM_POWER_CTRL_ENIRQ_VDD5V_GT_VDDIO),
+ HW_POWER_CTRL_CLR_ADDR);
+}
+
+void ddi_power_enable_5v_disconnect_detection(void)
+{
+ __raw_writel(BM_POWER_CTRL_POLARITY_VDD5V_GT_VDDIO |
+ BM_POWER_CTRL_POLARITY_VBUSVALID,
+ HW_POWER_CTRL_CLR_ADDR);
+
+ __raw_writel(BM_POWER_CTRL_VDD5V_GT_VDDIO_IRQ |
+ BM_POWER_CTRL_VBUSVALID_IRQ,
+ HW_POWER_CTRL_CLR_ADDR);
+
+ if (DetectionMethod == DDI_POWER_5V_VDD5V_GT_VDDIO) {
+ __raw_writel(BM_POWER_CTRL_ENIRQ_VDD5V_GT_VDDIO,
+ HW_POWER_CTRL_SET_ADDR);
+ } else {
+ __raw_writel(BM_POWER_CTRL_ENIRQ_VBUS_VALID,
+ HW_POWER_CTRL_SET_ADDR);
+ }
+}
+
+void ddi_power_enable_5v_connect_detection(void)
+{
+ __raw_writel(BM_POWER_CTRL_POLARITY_VDD5V_GT_VDDIO |
+ BM_POWER_CTRL_POLARITY_VBUSVALID,
+ HW_POWER_CTRL_SET_ADDR);
+
+ __raw_writel(BM_POWER_CTRL_VDD5V_GT_VDDIO_IRQ |
+ BM_POWER_CTRL_VBUSVALID_IRQ,
+ HW_POWER_CTRL_CLR_ADDR);
+
+ if (DetectionMethod == DDI_POWER_5V_VDD5V_GT_VDDIO) {
+ __raw_writel(BM_POWER_CTRL_ENIRQ_VDD5V_GT_VDDIO,
+ HW_POWER_CTRL_SET_ADDR);
+ } else {
+ __raw_writel(BM_POWER_CTRL_ENIRQ_VBUS_VALID,
+ HW_POWER_CTRL_SET_ADDR);
+ }
+}
+
+void ddi_power_EnableBatteryBoInterrupt(bool bEnable)
+{
+ if (bEnable) {
+
+ __raw_writel(BM_POWER_CTRL_BATT_BO_IRQ,
+ HW_POWER_CTRL_CLR_ADDR);
+ __raw_writel(BM_POWER_CTRL_ENIRQBATT_BO,
+ HW_POWER_CTRL_SET_ADDR);
+ /* todo: make sure the battery brownout comparator
+ * is enabled in HW_POWER_BATTMONITOR
+ */
+ } else {
+ __raw_writel(BM_POWER_CTRL_ENIRQBATT_BO,
+ HW_POWER_CTRL_CLR_ADDR);
+ }
+}
+
+void ddi_power_EnableDcdc4p2BoInterrupt(bool bEnable)
+{
+ if (bEnable) {
+
+ __raw_writel(BM_POWER_CTRL_DCDC4P2_BO_IRQ,
+ HW_POWER_CTRL_CLR_ADDR);
+ __raw_writel(BM_POWER_CTRL_ENIRQ_DCDC4P2_BO,
+ HW_POWER_CTRL_SET_ADDR);
+ } else {
+ __raw_writel(BM_POWER_CTRL_ENIRQ_DCDC4P2_BO,
+ HW_POWER_CTRL_CLR_ADDR);
+ }
+}
+
+void ddi_power_EnableVdd5vDroopInterrupt(bool bEnable)
+{
+ if (bEnable) {
+
+ __raw_writel(BM_POWER_CTRL_VDD5V_DROOP_IRQ,
+ HW_POWER_CTRL_CLR_ADDR);
+ __raw_writel(BM_POWER_CTRL_ENIRQ_VDD5V_DROOP,
+ HW_POWER_CTRL_SET_ADDR);
+ } else {
+ __raw_writel(BM_POWER_CTRL_ENIRQ_VDD5V_DROOP,
+ HW_POWER_CTRL_CLR_ADDR);
+ }
+}
+
+
+void ddi_power_Enable5vDisconnectShutdown(bool bEnable)
+{
+ if (bEnable) {
+ __raw_writel(BM_POWER_5VCTRL_PWDN_5VBRNOUT,
+ HW_POWER_5VCTRL_SET_ADDR);
+ } else {
+ __raw_writel(BM_POWER_5VCTRL_PWDN_5VBRNOUT,
+ HW_POWER_5VCTRL_CLR_ADDR);
+ }
+}
+
+
+void ddi_power_enable_5v_to_battery_xfer(bool bEnable)
+{
+ if (bEnable) {
+ /* order matters */
+
+ /* we can enable this in in vbus droop or 4p2 fiq handler
+ * ddi_power_EnableBatteryBoInterrupt(true);
+ */
+ ddi_power_Enable5vDisconnectShutdown(false);
+ } else {
+ /* order matters */
+ ddi_power_Enable5vDisconnectShutdown(true);
+ ddi_power_EnableBatteryBoInterrupt(false);
+ }
+}
+
+
+void ddi_power_init_4p2_protection(void)
+{
+ /* set vbus droop detection level to 4.3V */
+ __raw_writel(BM_POWER_5VCTRL_VBUSDROOP_TRSH,
+ HW_POWER_5VCTRL_CLR_ADDR);
+
+ /* VBUSDROOP THRESHOLD to 4.3V */
+ __raw_writel(BM_POWER_5VCTRL_VBUSDROOP_TRSH,
+ HW_POWER_5VCTRL_CLR_ADDR);
+
+ ddi_power_EnableVbusDroopIrq();
+
+ /* VBUSVALID THRESH = 2.9V */
+ __raw_writel(BM_POWER_5VCTRL_VBUSVALID_TRSH,
+ HW_POWER_5VCTRL_CLR_ADDR);
+
+}
+
+/* determine if all the bits are in a 'DCDC 4P2 Enabled' state. */
+bool ddi_power_check_4p2_bits(void)
+{
+
+
+ uint32_t temp;
+
+ temp = __raw_readl(HW_POWER_5VCTRL_ADDR) &
+ BM_POWER_5VCTRL_PWD_CHARGE_4P2;
+
+ /* if PWD_CHARGE_4P2 = 1, 4p2 is disabled */
+ if (temp)
+ return false;
+
+ temp = __raw_readl(HW_POWER_DCDC4P2_ADDR) &
+ BM_POWER_DCDC4P2_ENABLE_DCDC;
+
+ if (!temp)
+ return false;
+
+ temp = __raw_readl(HW_POWER_DCDC4P2_ADDR) &
+ BM_POWER_DCDC4P2_ENABLE_4P2;
+
+ if (temp)
+ return true;
+ else
+ return false;
+
+}
+
+uint16_t ddi_power_set_4p2_ilimit(uint16_t ilimit)
+{
+ uint32_t temp_reg;
+
+ if (ilimit > 780)
+ ilimit = 780;
+ temp_reg = __raw_readl(HW_POWER_5VCTRL_ADDR);
+ temp_reg &= (~BM_POWER_5VCTRL_CHARGE_4P2_ILIMIT);
+ temp_reg |= BF_POWER_5VCTRL_CHARGE_4P2_ILIMIT(
+ ddi_power_convert_current_to_setting(
+ ilimit));
+ __raw_writel(temp_reg, HW_POWER_5VCTRL_ADDR);
+
+ return ilimit;
+}
+
+void ddi_power_shutdown(void)
+{
+ __raw_writel(0x3e770001, HW_POWER_RESET_ADDR);
+}
+
+void ddi_power_handle_dcdc4p2_bo(void)
+{
+ ddi_power_EnableBatteryBoInterrupt(true);
+ ddi_power_EnableDcdc4p2BoInterrupt(false);
+}
+
+void ddi_power_enable_vddio_interrupt(bool enable)
+{
+ if (enable) {
+ __raw_writel(BM_POWER_CTRL_VDDIO_BO_IRQ,
+ HW_POWER_CTRL_CLR_ADDR);
+#ifndef DISABLE_VDDIO_BO_PROTECTION
+ __raw_writel(BM_POWER_CTRL_ENIRQ_VDDIO_BO,
+ HW_POWER_CTRL_SET_ADDR);
+#endif
+ } else {
+ __raw_writel(BM_POWER_CTRL_ENIRQ_VDDIO_BO,
+ HW_POWER_CTRL_CLR_ADDR);
+ }
+}
+
+void ddi_power_handle_vddio_brnout(void)
+{
+ if (ddi_power_GetPmu5vStatus() == new_5v_connection) {
+ ddi_power_enable_vddio_interrupt(false);
+ } else {
+#ifdef DEBUG_IRQS
+ ddi_power_enable_vddio_interrupt(false);
+ printk(KERN_ALERT "VDDIO BO TRIED TO SHUTDOWN!!!\n");
+ return;
+#else
+ ddi_power_shutdown();
+#endif
+ }
+}
+
+void ddi_power_handle_vdd5v_droop(void)
+{
+ uint32_t temp;
+
+ /* handle errata */
+ temp = __raw_readl(HW_POWER_DCDC4P2_ADDR);
+ temp |= (BF(31, POWER_DCDC4P2_CMPTRIP) | BM_POWER_DCDC4P2_TRG);
+ __raw_writel(temp, HW_POWER_DCDC4P2_ADDR);
+
+
+ /* if battery is below brownout level, shutdown asap */
+ if (__raw_readl(HW_POWER_STS_ADDR) & BM_POWER_STS_BATT_BO)
+ ddi_power_shutdown();
+
+ /* due to 5v connect vddio bo chip bug, we need to
+ * disable vddio interrupts until we reset the 5v
+ * detection for 5v connect detect. We want to allow
+ * some debounce time before enabling connect detection.
+ */
+ ddi_power_enable_vddio_interrupt(false);
+
+ ddi_power_EnableBatteryBoInterrupt(true);
+ ddi_power_EnableDcdc4p2BoInterrupt(false);
+ ddi_power_EnableVdd5vDroopInterrupt(false);
+
+}
+
+void ddi_power_InitOutputBrownouts(void)
+{
+ uint32_t temp;
+
+ __raw_writel(BM_POWER_CTRL_VDDD_BO_IRQ |
+ BM_POWER_CTRL_VDDA_BO_IRQ |
+ BM_POWER_CTRL_VDDIO_BO_IRQ,
+ HW_POWER_CTRL_CLR_ADDR);
+
+ __raw_writel(BM_POWER_CTRL_ENIRQ_VDDD_BO |
+ BM_POWER_CTRL_ENIRQ_VDDA_BO |
+ BM_POWER_CTRL_ENIRQ_VDDIO_BO,
+ HW_POWER_CTRL_SET_ADDR);
+
+ temp = __raw_readl(HW_POWER_VDDDCTRL_ADDR);
+ temp &= ~BM_POWER_VDDDCTRL_PWDN_BRNOUT;
+ __raw_writel(temp, HW_POWER_VDDDCTRL_ADDR);
+
+ temp = __raw_readl(HW_POWER_VDDACTRL_ADDR);
+ temp &= ~BM_POWER_VDDACTRL_PWDN_BRNOUT;
+ __raw_writel(temp, HW_POWER_VDDACTRL_ADDR);
+
+ temp = __raw_readl(HW_POWER_VDDIOCTRL_ADDR);
+ temp &= ~BM_POWER_VDDIOCTRL_PWDN_BRNOUT;
+ __raw_writel(temp, HW_POWER_VDDIOCTRL_ADDR);
+}
+
+/* used for debugging purposes only */
+void ddi_power_disable_power_interrupts(void)
+{
+ __raw_writel(BM_POWER_CTRL_ENIRQ_DCDC4P2_BO |
+ BM_POWER_CTRL_ENIRQ_VDD5V_DROOP |
+ BM_POWER_CTRL_ENIRQ_PSWITCH |
+ BM_POWER_CTRL_ENIRQ_DC_OK |
+ BM_POWER_CTRL_ENIRQBATT_BO |
+ BM_POWER_CTRL_ENIRQ_VDDIO_BO |
+ BM_POWER_CTRL_ENIRQ_VDDA_BO |
+ BM_POWER_CTRL_ENIRQ_VDDD_BO |
+ BM_POWER_CTRL_ENIRQ_VBUS_VALID |
+ BM_POWER_CTRL_ENIRQ_VDD5V_GT_VDDIO,
+ HW_POWER_CTRL_CLR_ADDR);
+
+}
+
+bool ddi_power_Get5vDroopFlag(void)
+{
+ if (__raw_readl(HW_POWER_STS_ADDR) &
+ BM_POWER_STS_VDD5V_DROOP)
+ return true;
+ else
+ return false;
+}
+
////////////////////////////////////////////////////////////////////////////////
// End of file
////////////////////////////////////////////////////////////////////////////////
diff --git a/drivers/power/stmp37xx/ddi_power_battery.h b/drivers/power/stmp37xx/ddi_power_battery.h
index 2d7c1a5411ad..892feb639566 100644
--- a/drivers/power/stmp37xx/ddi_power_battery.h
+++ b/drivers/power/stmp37xx/ddi_power_battery.h
@@ -21,14 +21,6 @@ typedef enum {
} ddi_power_BatteryMode_t;
-//! \brief Available sources for bias currents
-typedef enum {
- //! \brief Use external resistor to generate bias current
- DDI_POWER_EXTERNAL_BIAS_CURRENT = 0x0,
- //! \brief Use internal resistor to generate bias current
- DDI_POWER_INTERNAL_BIAS_CURRENT = 0x1
-} ddi_power_BiasCurrentSource_t;
-
//! \brief Possible 5V detection methods
typedef enum {
//! \brief Use VBUSVALID comparator for detection
@@ -38,6 +30,14 @@ typedef enum {
} 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);
@@ -53,8 +53,6 @@ int ddi_power_GetChargeStatus(void);
uint16_t ddi_power_GetBattery(void);
uint16_t ddi_power_GetBatteryBrownout(void);
int ddi_power_SetBatteryBrownout(uint16_t u16BattBrownout_mV);
-int ddi_power_SetBiasCurrentSource(ddi_power_BiasCurrentSource_t eSource);
-ddi_power_BiasCurrentSource_t ddi_power_GetBiasCurrentSource(void);
uint16_t ddi_power_SetMaxBatteryChargeCurrent(uint16_t u16MaxCur);
uint16_t ddi_power_GetMaxBatteryChargeCurrent(void);
uint16_t ddi_power_SetBatteryChargeCurrentThreshold(uint16_t u16Thresh);
@@ -65,3 +63,33 @@ void ddi_power_GetDieTemp(int16_t * pLow, int16_t * pHigh);
bool ddi_power_IsDcdcOn(void);
void ddi_power_SetPowerClkGate(bool bGate);
bool ddi_power_GetPowerClkGate(void);
+enum ddi_power_5v_status ddi_power_GetPmu5vStatus(void);
+void ddi_power_EnableBatteryBoFiq(bool bEnable);
+void ddi_power_disable_5v_connection_irq(void);
+void ddi_power_enable_5v_disconnect_detection(void);
+void ddi_power_enable_5v_connect_detection(void);
+void ddi_power_Enable5vDisconnectShutdown(bool bEnable);
+void ddi_power_enable_5v_to_battery_xfer(bool bEnable);
+void ddi_power_init_4p2_protection(void);
+bool ddi_power_check_4p2_bits(void);
+void ddi_power_Start4p2Dcdc(bool battery_ready);
+void ddi_power_Init4p2Params(void);
+bool ddi_power_IsBattRdyForXfer(void);
+void ddi_power_EnableVbusDroopIrq(void);
+void ddi_power_Enable4p2(uint16_t target_current_limit_ma);
+uint16_t ddi_power_BringUp4p2Regulator(
+ uint16_t target_current_limit_ma,
+ bool b4p2_dcdc_enabled);
+void ddi_power_Set4p2BoLevel(uint16_t bo_voltage_mv);
+void ddi_power_EnableBatteryBoInterrupt(bool bEnable);
+void ddi_power_handle_cmptrip(void);
+uint16_t ddi_power_set_4p2_ilimit(uint16_t ilimit);
+void ddi_power_shutdown(void);
+void ddi_power_handle_dcdc4p2_bo(void);
+void ddi_power_enable_vddio_interrupt(bool enable);
+void ddi_power_handle_vddio_brnout(void);
+void ddi_power_EnableDcdc4p2BoInterrupt(bool bEnable);
+void ddi_power_handle_vdd5v_droop(void);
+void ddi_power_InitOutputBrownouts(void);
+void ddi_power_disable_power_interrupts(void);
+bool ddi_power_Get5vDroopFlag(void);
diff --git a/drivers/power/stmp37xx/linux.c b/drivers/power/stmp37xx/linux.c
index 69bafafee7f5..2f76018321a7 100644
--- a/drivers/power/stmp37xx/linux.c
+++ b/drivers/power/stmp37xx/linux.c
@@ -29,6 +29,12 @@
#include <linux/interrupt.h>
+enum application_5v_status{
+ _5v_connected_verified,
+ _5v_connected_unverified,
+ _5v_disconnected_unverified,
+ _5v_disconnected_verified,
+};
struct stmp3xxx_info {
struct device *dev;
@@ -42,8 +48,19 @@ struct stmp3xxx_info {
struct mutex sm_lock;
struct timer_list sm_timer;
struct work_struct sm_work;
- struct resource *vdd5v_irq;
+ 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
@@ -54,18 +71,243 @@ struct stmp3xxx_info {
#define to_stmp3xxx_info(x) container_of((x), struct stmp3xxx_info, bat)
+#ifndef NON_USB_5V_SUPPLY_CURRENT_LIMIT_MA
+#define NON_USB_5V_SUPPLY_CURRENT_LIMIT_MA 780
+#endif
+
+#ifndef POWERED_USB_5V_CURRENT_LIMIT_MA
+#define POWERED_USB_5V_CURRENT_LIMIT_MA 450
+#endif
+
+#ifndef UNPOWERED_USB_5V_CURRENT_LIMIT_MA
+#define UNPOWERED_USB_5V_CURRENT_LIMIT_MA 80
+#endif
+
+#ifndef _5V_DEBOUNCE_TIME_MS
+#define _5V_DEBOUNCE_TIME_MS 500
+#endif
+
+#ifndef OS_SHUTDOWN_BATTERY_VOLTAGE_THRESHOLD_MV
+#define OS_SHUTDOWN_BATTERY_VOLTAGE_THRESHOLD_MV 3350
+#endif
+
+/* #define DEBUG_IRQS */
+
/* There is no direct way to detect wall power presence, so assume the AC
* power source is valid if 5V presents and USB device is disconnected.
* If USB device is connected then assume that AC is offline and USB power
* is online.
*/
+
#define is_usb_plugged()(__raw_readl(REGS_USBPHY_BASE + HW_USBPHY_STATUS) & \
BM_USBPHY_STATUS_DEVPLUGIN_STATUS)
+
#define is_ac_online() \
(ddi_power_Get5vPresentFlag() ? (!is_usb_plugged()) : 0)
#define is_usb_online() \
(ddi_power_Get5vPresentFlag() ? (!!is_usb_plugged()) : 0)
+
+
+void init_protection(struct stmp3xxx_info *info)
+{
+ enum ddi_power_5v_status pmu_5v_status;
+ uint16_t battery_voltage;
+
+ pmu_5v_status = ddi_power_GetPmu5vStatus();
+ battery_voltage = ddi_power_GetBattery();
+
+ /* InitializeFiqSystem(); */
+
+ ddi_power_InitOutputBrownouts();
+
+
+ /* if we start the kernel with 4p2 already started
+ * by the bootlets, we need to hand off from this
+ * state to the kernel 4p2 enabled state.
+ */
+ if ((pmu_5v_status == existing_5v_connection) &&
+ ddi_power_check_4p2_bits()) {
+ ddi_power_enable_5v_disconnect_detection();
+
+ /* includes VBUS DROOP workaround for errata */
+ ddi_power_init_4p2_protection();
+
+ /* if we still have our 5V connection, we can disable
+ * battery brownout interrupt. This is because the
+ * VDD5V DROOP IRQ handler will also shutdown if battery
+ * is browned out and it will enable the battery brownout
+ * and bring VBUSVALID_TRSH level back to a normal level
+ * which caused the hardware battery brownout shutdown
+ * to be enabled. The benefit of this is that device
+ * that have detachable batteries (or devices going through
+ * the assembly line and running this firmware to test
+ * with) can avoid shutting down if 5V is present and
+ * battery voltage goes away.
+ */
+ ddi_power_EnableBatteryBoInterrupt(false);
+
+ info->sm_5v_connection_status = _5v_connected_verified;
+ } else {
+#ifdef DEBUG_IRQS
+ if (battery_voltage <
+ OS_SHUTDOWN_BATTERY_VOLTAGE_THRESHOLD_MV) {
+ printk(KERN_CRIT "Polled battery voltage measurement is\
+ less than %dmV. Kernel should be halted/\
+ shutdown\n",
+ OS_SHUTDOWN_BATTERY_VOLTAGE_THRESHOLD_MV);
+
+ return;
+ }
+#endif
+ info->sm_5v_connection_status = _5v_disconnected_verified;
+ ddi_power_EnableBatteryBoInterrupt(true);
+
+ }
+
+
+ /* all brownouts are now handled software fiqs. We
+ * can now disable the hardware protection mechanisms
+ * because leaving them on yields ~2kV ESD level
+ * versus ~4kV ESD levels when they are off. This
+ * difference is suspected to be cause by the fast
+ * falling edge pswitch functionality being tripped
+ * by ESD events. This functionality is disabled
+ * when PWD_OFF is disabled.
+ */
+#ifdef DISABLE_HARDWARE_PROTECTION_MECHANISMS
+ __raw_writel(BM_POWER_RESET_PWD_OFF,
+ HW_POWER_RESET_SET_ADDR);
+#endif
+
+
+
+
+}
+
+
+
+static void check_and_handle_5v_connection(struct stmp3xxx_info *info)
+{
+
+ switch (ddi_power_GetPmu5vStatus()) {
+
+ case new_5v_connection:
+ ddi_power_enable_5v_disconnect_detection();
+ info->sm_5v_connection_status = _5v_connected_unverified;
+
+ case existing_5v_connection:
+ if (info->sm_5v_connection_status != _5v_connected_verified) {
+ /* we allow some time to pass before considering
+ * the 5v connection to be ready to use. This
+ * will give the USB system time to enumerate
+ * (coordination with USB driver to be added
+ * in the future).
+ */
+
+ /* handle jiffies rollover case */
+ if ((jiffies - info->sm_new_5v_connection_jiffies)
+ < 0) {
+ info->sm_new_5v_connection_jiffies = jiffies;
+ break;
+ }
+
+ if ((jiffies_to_msecs(jiffies -
+ info->sm_new_5v_connection_jiffies)) >
+ _5V_DEBOUNCE_TIME_MS) {
+ info->sm_5v_connection_status =
+ _5v_connected_verified;
+ dev_info(info->dev,
+ "5v connection verified\n");
+ ddi_power_Enable4p2(450);
+
+
+ /* part of handling for errata. It is
+ * now "somewhat" safe to
+ * turn on vddio interrupts again
+ */
+ ddi_power_enable_vddio_interrupt(true);
+ }
+ }
+ break;
+
+ case new_5v_disconnection:
+
+ ddi_bc_SetDisable();
+ ddi_bc_SetCurrentLimit(0);
+ if (info->regulator)
+ regulator_set_current_limit(info->regulator, 0, 0);
+ info->is_usb_online = 0;
+ info->is_ac_online = 0;
+
+ info->sm_5v_connection_status = _5v_disconnected_unverified;
+
+ case existing_5v_disconnection:
+
+ if (info->sm_5v_connection_status !=
+ _5v_disconnected_verified) {
+ if ((jiffies - info->sm_new_5v_disconnection_jiffies)
+ < 0) {
+ info->sm_new_5v_connection_jiffies = jiffies;
+ break;
+ }
+
+ if ((jiffies_to_msecs(jiffies -
+ info->sm_new_5v_disconnection_jiffies)) >
+ _5V_DEBOUNCE_TIME_MS) {
+ info->sm_5v_connection_status =
+ _5v_disconnected_verified;
+ ddi_power_execute_5v_to_battery_handoff();
+ ddi_power_enable_5v_connect_detection();
+
+ /* part of handling for errata.
+ * It is now safe to
+ * turn on vddio interrupts again
+ */
+ ddi_power_enable_vddio_interrupt(true);
+ dev_info(info->dev,
+ "5v disconnection handled\n");
+
+ }
+ }
+
+ break;
+ }
+}
+
+
+static void handle_battery_voltage_changes(struct stmp3xxx_info *info)
+{
+#if 0
+ uint16_t battery_voltage;
+
+ battery_voltage = ddi_power_GetBattery();
+
+ if (info->sm_5v_connection_status != _5v_connected_verified) {
+ if (battery_voltage <
+ OS_SHUTDOWN_BATTERY_VOLTAGE_THRESHOLD_MV) {
+ printk(KERN_CRIT "Polled battery voltage measurement is\
+ less than %dmV. Shutting down the \
+ system\n",
+ OS_SHUTDOWN_BATTERY_VOLTAGE_THRESHOLD_MV);
+
+ shutdown_os();
+ return;
+ }
+ } else
+#endif
+ {
+ ddi_power_handle_cmptrip();
+
+ if (ddi_power_IsBattRdyForXfer())
+ ddi_power_enable_5v_to_battery_xfer(true);
+ else
+ ddi_power_enable_5v_to_battery_xfer(false);
+
+ }
+}
+
+
/*
* Power properties
*/
@@ -220,7 +462,7 @@ static void state_machine_timer(unsigned long data)
}
/*
- * Assumtion:
+ * Assumption:
* AC power can't be switched to USB w/o system reboot
* and vice-versa
*/
@@ -231,26 +473,40 @@ static void state_machine_work(struct work_struct *work)
mutex_lock(&info->sm_lock);
- if (info->is_usb_online & USB_SHUTDOWN) {
- info->is_usb_online = 0;
- if (!info->regulator)
- goto out;
- regulator_set_current_limit(info->regulator, 0, 0);
+ handle_battery_voltage_changes(info);
+
+ check_and_handle_5v_connection(info);
+
+ if ((info->sm_5v_connection_status != _5v_connected_verified) ||
+ !(info->regulator)) {
+ mod_timer(&info->sm_timer, jiffies + msecs_to_jiffies(100));
goto out;
}
+ /* if we made it here, we have a verified 5v connection */
+
if (is_ac_online()) {
if (info->is_ac_online)
goto done;
/* ac supply connected */
- dev_info(info->dev, "changed power connection to ac/5v \n");
+ dev_info(info->dev, "changed power connection to ac/5v.\n)");
+ dev_info(info->dev, "5v current limit set to %u.\n",
+ NON_USB_5V_SUPPLY_CURRENT_LIMIT_MA);
info->is_ac_online = 1;
info->is_usb_online = 0;
- ddi_bc_SetCurrentLimit(600 /*mA*/);
- ddi_power_execute_battery_to_5v_handoff();
- ddi_power_enable_5v_to_battery_handoff();
+ 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;
}
@@ -263,37 +519,26 @@ static void state_machine_work(struct work_struct *work)
info->is_ac_online = 0;
info->is_usb_online |= USB_ONLINE;
- if (!info->regulator) {
- info->regulator = regulator_get(NULL, "charger-1");
- if (!info->regulator || IS_ERR(info->regulator)) {
- dev_err(info->dev,
- "%s: failed to get regulator\n", __func__);
- info->regulator = NULL;
- ddi_bc_SetCurrentLimit(350 /*mA*/);
- ddi_power_execute_battery_to_5v_handoff();
- ddi_power_enable_5v_to_battery_handoff();
- goto done;
- } else
- regulator_set_mode(info->regulator,
- REGULATOR_MODE_FAST);
- }
+
if (!(info->is_usb_online & USB_N_SEND)) {
info->is_usb_online |= USB_N_SEND;
}
- if (regulator_set_current_limit(info->regulator, 150000, 150000)) {
- dev_err(info->dev, "reg_set_current(150000) failed\n");
- ddi_bc_SetCurrentLimit(0 /*mA*/);
- dev_dbg(info->dev, "charge current set to 0\n");
- mod_timer(&info->sm_timer, jiffies + msecs_to_jiffies(1000));
- goto done;
+ dev_dbg(info->dev, "%s: charge current set to %dmA\n", __func__,
+ POWERED_USB_5V_CURRENT_LIMIT_MA);
+
+ if (regulator_set_current_limit(info->regulator,
+ 0,
+ POWERED_USB_5V_CURRENT_LIMIT_MA*1000)) {
+ dev_err(info->dev, "reg_set_current(%duA) failed\n",
+ POWERED_USB_5V_CURRENT_LIMIT_MA*1000);
+ } else {
+ ddi_bc_SetCurrentLimit(POWERED_USB_5V_CURRENT_LIMIT_MA/*mA*/);
+ ddi_bc_SetEnable();
}
- dev_dbg(info->dev, "%s: charge current set to 100mA\n", __func__);
- ddi_bc_SetCurrentLimit(100 /*mA*/);
- regulator_set_current_limit(info->regulator, 100000, 100000);
if (info->is_usb_online & USB_SM_RESTART) {
info->is_usb_online &= ~USB_SM_RESTART;
ddi_bc_SetEnable();
@@ -302,9 +547,7 @@ static void state_machine_work(struct work_struct *work)
info->is_usb_online |= USB_REG_SET;
dev_info(info->dev, "changed power connection to usb/5v present\n");
- ddi_power_execute_battery_to_5v_handoff();
- ddi_power_enable_5v_to_battery_handoff();
- ddi_bc_SetEnable();
+
done:
ddi_bc_StateMachine();
@@ -312,6 +555,8 @@ out:
mutex_unlock(&info->sm_lock);
}
+
+
static int bc_sm_restart(struct stmp3xxx_info *info)
{
ddi_bc_Status_t bcret;
@@ -328,40 +573,28 @@ static int bc_sm_restart(struct stmp3xxx_info *info)
*/
bcret = ddi_bc_Init(info->sm_cfg);
if (bcret != DDI_BC_STATUS_SUCCESS) {
- dev_err(info->dev, "state machine init failed: %d\n", bcret);
+ dev_err(info->dev, "battery charger init failed: %d\n", bcret);
ret = -EIO;
goto out;
- }
-
- /*
- * Check what power supply options we have right now. If
- * we're able to do any battery charging, then set the
- * appropriate current limit and enable. Otherwise, leave
- * the battery charger disabled.
- */
- if (is_ac_online()) {
- /* ac supply connected */
- dev_info(info->dev, "ac/5v present, enabling state machine\n");
-
- info->is_ac_online = 1;
- info->is_usb_online = 0;
- ddi_bc_SetCurrentLimit(600 /*mA*/);
- ddi_bc_SetEnable();
- } else if (is_usb_online()) {
- /* usb supply connected */
- dev_info(info->dev, "usb/5v present, enabling state machine\n");
-
- info->is_ac_online = 0;
- info->is_usb_online = USB_ONLINE | USB_SM_RESTART;
} else {
- /* not powered */
- dev_info(info->dev, "%s: 5v not present\n", __func__);
- info->is_ac_online = 0;
- info->is_usb_online = 0;
- ddi_bc_SetDisable();
+ 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:
@@ -369,33 +602,107 @@ out:
return ret;
}
-static irqreturn_t stmp3xxx_vdd5v_irq(int irq, void *cookie)
+
+static irqreturn_t stmp3xxx_irq_dcdc4p2_bo(int irq, void *cookie)
{
+#ifdef DEBUG_IRQS
struct stmp3xxx_info *info = (struct stmp3xxx_info *)cookie;
+ dev_info(info->dev, "dcdc4p2 brownout interrupt occurred\n");
- if (ddi_power_Get5vPresentFlag()) {
- dev_info(info->dev, "5v present, reenable state machine\n");
+#endif
+ ddi_power_handle_dcdc4p2_bo();
+ return IRQ_HANDLED;
+}
- ddi_bc_SetEnable();
+static irqreturn_t stmp3xxx_irq_batt_brnout(int irq, void *cookie)
+{
+#ifdef DEBUG_IRQS
+ struct stmp3xxx_info *info = (struct stmp3xxx_info *)cookie;
+ dev_info(info->dev, "battery brownout interrupt occurred\n");
+ ddi_power_disable_power_interrupts();
+#else
+ ddi_power_shutdown();
+#endif
+ return IRQ_HANDLED;
+}
+static irqreturn_t stmp3xxx_irq_vddd_brnout(int irq, void *cookie)
+{
+#ifdef DEBUG_IRQS
+ struct stmp3xxx_info *info = (struct stmp3xxx_info *)cookie;
+ dev_info(info->dev, "vddd brownout interrupt occurred\n");
+ ddi_power_disable_power_interrupts();
+#else
+ ddi_power_shutdown();
+#endif
+ return IRQ_HANDLED;
+}
+static irqreturn_t stmp3xxx_irq_vdda_brnout(int irq, void *cookie)
+{
+#ifdef DEBUG_IRQS
+ struct stmp3xxx_info *info = (struct stmp3xxx_info *)cookie;
+ dev_info(info->dev, "vdda brownout interrupt occurred\n");
+ ddi_power_disable_power_interrupts();
+#else
+ ddi_power_shutdown();
+#endif
+ return IRQ_HANDLED;
+}
+static irqreturn_t stmp3xxx_irq_vddio_brnout(int irq, void *cookie)
+{
+#ifdef DEBUG_IRQS
+ struct stmp3xxx_info *info = (struct stmp3xxx_info *)cookie;
+ dev_info(info->dev, "vddio brownout interrupt occurred\n");
+ ddi_power_disable_power_interrupts();
+#else
+ ddi_power_handle_vddio_brnout();
+#endif
+ return IRQ_HANDLED;
+}
+static irqreturn_t stmp3xxx_irq_vdd5v_droop(int irq, void *cookie)
+{
+#ifdef DEBUG_IRQS
+ struct stmp3xxx_info *info = (struct stmp3xxx_info *)cookie;
+ dev_info(info->dev, "vdd5v droop interrupt occurred\n");
+#endif
+ ddi_power_handle_vdd5v_droop();
- /*
- * We only ack/negate the interrupt here,
- * as we can't decide yet if we really can
- * switch to 5V (USB bits not ready)
- */
- ddi_power_enable_5v_to_battery_handoff();
- } else {
- dev_info(info->dev, "5v went away, disabling state machine\n");
+ return IRQ_HANDLED;
+}
- ddi_bc_SetDisable();
+static irqreturn_t stmp3xxx_irq_vdd5v(int irq, void *cookie)
+{
+ struct stmp3xxx_info *info = (struct stmp3xxx_info *)cookie;
- info->is_ac_online = 0;
- if (info->is_usb_online)
- info->is_usb_online = USB_SHUTDOWN;
- ddi_power_execute_5v_to_battery_handoff();
- ddi_power_enable_battery_to_5v_handoff();
+ switch (ddi_power_GetPmu5vStatus()) {
+
+ case new_5v_connection:
+
+ ddi_power_disable_5v_connection_irq();
+ dev_info(info->dev, "new 5v connection detected\n");
+ info->sm_new_5v_connection_jiffies = jiffies;
mod_timer(&info->sm_timer, jiffies + 1);
+ break;
+
+ case new_5v_disconnection:
+
+ /* due to 5v connect vddio bo chip bug, we need to
+ * disable vddio interrupts until we reset the 5v
+ * detection for 5v connect detect. We want to allow
+ * some debounce time before enabling connect detection.
+ * This is handled in the vdd5v_droop interrupt for now.
+ */
+ /* ddi_power_enable_vddio_interrupt(false); */
+
+ ddi_power_disable_5v_connection_irq();
+ dev_info(info->dev, "new 5v disconnection detected\n");
+ info->sm_new_5v_disconnection_jiffies = jiffies;
+ mod_timer(&info->sm_timer, jiffies + 1);
+ break;
+
+ default:
+
+ break;
}
@@ -407,6 +714,15 @@ static int stmp3xxx_bat_probe(struct platform_device *pdev)
struct stmp3xxx_info *info;
int ret = 0;
+
+
+ ret = ddi_power_init_battery();
+ if (ret) {
+ printk(KERN_ERR "Aborting power driver initialization\n");
+ return 1;
+ }
+
+
if (!pdev->dev.platform_data) {
printk(KERN_ERR "%s: missing platform data\n", __func__);
return -ENODEV;
@@ -416,12 +732,51 @@ static int stmp3xxx_bat_probe(struct platform_device *pdev)
if (!info)
return -ENOMEM;
- info->vdd5v_irq = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
- if (info->vdd5v_irq == NULL) {
+ 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_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_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;
+ }
+
+ 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;
+ }
+
+
+
platform_set_drvdata(pdev, info);
info->dev = &pdev->dev;
@@ -456,7 +811,7 @@ static int stmp3xxx_bat_probe(struct platform_device *pdev)
INIT_WORK(&info->sm_work, state_machine_work);
/* init LRADC channels to measure battery voltage and die temp */
- ddi_power_init_battery();
+
__raw_writel(BM_POWER_5VCTRL_ENABLE_LINREG_ILIMIT,
REGS_POWER_BASE + HW_POWER_5VCTRL_CLR);
@@ -464,14 +819,64 @@ static int stmp3xxx_bat_probe(struct platform_device *pdev)
if (ret)
goto free_info;
- ret = request_irq(info->vdd5v_irq->start,
- stmp3xxx_vdd5v_irq, IRQF_DISABLED | IRQF_SHARED,
+
+ ret = request_irq(info->irq_vdd5v->start,
+ stmp3xxx_irq_vdd5v, IRQF_DISABLED | IRQF_SHARED,
pdev->name, info);
if (ret) {
dev_err(info->dev, "failed to request irq\n");
goto stop_sm;
}
+ ret = request_irq(info->irq_dcdc4p2_bo->start,
+ stmp3xxx_irq_dcdc4p2_bo, IRQF_DISABLED,
+ pdev->name, info);
+ if (ret) {
+ dev_err(info->dev, "failed to request irq\n");
+ goto stop_sm;
+ }
+
+ ret = request_irq(info->irq_batt_brnout->start,
+ stmp3xxx_irq_batt_brnout, IRQF_DISABLED,
+ pdev->name, info);
+ if (ret) {
+ dev_err(info->dev, "failed to request irq\n");
+ goto stop_sm;
+ }
+
+ ret = request_irq(info->irq_vddd_brnout->start,
+ stmp3xxx_irq_vddd_brnout, IRQF_DISABLED,
+ pdev->name, info);
+ if (ret) {
+ dev_err(info->dev, "failed to request irq\n");
+ goto stop_sm;
+ }
+
+ ret = request_irq(info->irq_vdda_brnout->start,
+ stmp3xxx_irq_vdda_brnout, IRQF_DISABLED,
+ pdev->name, info);
+ if (ret) {
+ dev_err(info->dev, "failed to request irq\n");
+ goto stop_sm;
+ }
+
+ ret = request_irq(info->irq_vddio_brnout->start,
+ stmp3xxx_irq_vddio_brnout, IRQF_DISABLED,
+ pdev->name, info);
+ if (ret) {
+ dev_err(info->dev, "failed to request irq\n");
+ goto stop_sm;
+ }
+
+ ret = request_irq(info->irq_vdd5v_droop->start,
+ stmp3xxx_irq_vdd5v_droop, IRQF_DISABLED,
+ pdev->name, info);
+ if (ret) {
+ dev_err(info->dev, "failed to request irq\n");
+ goto stop_sm;
+ }
+
+
ret = power_supply_register(&pdev->dev, &info->bat);
if (ret) {
dev_err(info->dev, "failed to register battery\n");
@@ -490,6 +895,11 @@ static int stmp3xxx_bat_probe(struct platform_device *pdev)
goto unregister_ac;
}
+ /* handoff protection handling from bootlets protection method
+ * to kernel protection method
+ */
+ init_protection(info);
+
/* enable usb device presence detection */
__raw_writel(BM_USBPHY_CTRL_ENDEVPLUGINDETECT,
REGS_USBPHY_BASE + HW_USBPHY_CTRL_SET);
@@ -501,7 +911,13 @@ unregister_ac:
unregister_bat:
power_supply_unregister(&info->bat);
free_irq:
- free_irq(info->vdd5v_irq->start, pdev);
+ free_irq(info->irq_vdd5v->start, pdev);
+ 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_vddio_brnout->start, pdev);
+ free_irq(info->irq_vdd5v_droop->start, pdev);
stop_sm:
ddi_bc_ShutDown();
free_info:
@@ -515,7 +931,13 @@ static int stmp3xxx_bat_remove(struct platform_device *pdev)
if (info->regulator)
regulator_put(info->regulator);
- free_irq(info->vdd5v_irq->start, pdev);
+ free_irq(info->irq_vdd5v->start, pdev);
+ 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_vddio_brnout->start, pdev);
+ free_irq(info->irq_vdd5v_droop->start, pdev);
ddi_bc_ShutDown();
power_supply_unregister(&info->usb);
power_supply_unregister(&info->ac);
@@ -562,7 +984,8 @@ static int stmp3xxx_bat_resume(struct platform_device *pdev)
info->is_ac_online = 1;
info->is_usb_online = 0;
- ddi_bc_SetCurrentLimit(600 /*mA*/);
+ ddi_bc_SetCurrentLimit(
+ NON_USB_5V_SUPPLY_CURRENT_LIMIT_MA /*mA*/);
ddi_bc_SetEnable();
} else if (is_usb_online()) {
/* usb supply connected */
@@ -570,7 +993,7 @@ static int stmp3xxx_bat_resume(struct platform_device *pdev)
info->is_ac_online = 0;
info->is_usb_online = 1;
- ddi_bc_SetCurrentLimit(350 /*mA*/);
+ ddi_bc_SetCurrentLimit(POWERED_USB_5V_CURRENT_LIMIT_MA /*mA*/);
ddi_bc_SetEnable();
} else {
/* not powered */