diff options
-rw-r--r-- | arch/arm/mach-stmp378x/pm.c | 18 | ||||
-rw-r--r-- | arch/arm/plat-stmp3xxx/devices.c | 33 | ||||
-rw-r--r-- | drivers/power/stmp37xx/ddi_bc_hw.c | 63 | ||||
-rw-r--r-- | drivers/power/stmp37xx/ddi_bc_init.c | 16 | ||||
-rw-r--r-- | drivers/power/stmp37xx/ddi_bc_sm.c | 13 | ||||
-rw-r--r-- | drivers/power/stmp37xx/ddi_power_battery.c | 966 | ||||
-rw-r--r-- | drivers/power/stmp37xx/ddi_power_battery.h | 48 | ||||
-rw-r--r-- | drivers/power/stmp37xx/linux.c | 607 |
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 */ |