Logo Search packages:      
Sourcecode: linux-fsl-imx51 version File versions  Download package

ddi_power_battery.c

Go to the documentation of this file.
/*
 * Copyright 2008-2009 Freescale Semiconductor, Inc. All Rights Reserved.
 */

/*
 * The code contained herein is licensed under the GNU General Public
 * License. You may obtain a copy of the GNU General Public License
 * Version 2 or later at the following locations:
 *
 * http://www.opensource.org/licenses/gpl-license.html
 * http://www.gnu.org/copyleft/gpl.html
 */

////////////////////////////////////////////////////////////////////////////////
//! \addtogroup ddi_power
//! @{
//
// Copyright(C) 2005 SigmaTel, Inc.
//
//! \file ddi_power_battery.c
//! \brief Implementation file for the power driver battery charger.
//!
////////////////////////////////////////////////////////////////////////////////
//   Includes and external references
////////////////////////////////////////////////////////////////////////////////
#include <linux/kernel.h>
#include <linux/errno.h>
#include <linux/delay.h>
#include <asm/processor.h> /* cpu_relax */
#include <mach/platform.h>
#include <mach/hardware.h>
#include <mach/ddi_bc.h>
#include <mach/lradc.h>
#include <mach/regs-power.h>
#include <mach/regs-lradc.h>
#include <mach/lradc.h>
#include "ddi_bc_internal.h"
#include <mach/platform.h>

//! \brief Base voltage to start battery calculations for LiIon
00041 #define BATT_BRWNOUT_LIION_BASE_MV 2800
//! \brief Constant to help with determining whether to round up or
//! not during calculation
00044 #define BATT_BRWNOUT_LIION_CEILING_OFFSET_MV 39
//! \brief Number of mV to add if rounding up in LiIon mode
00046 #define BATT_BRWNOUT_LIION_LEVEL_STEP_MV 40
//! \brief Constant value to be calculated by preprocessing
00048 #define BATT_BRWNOUT_LIION_EQN_CONST \
      (BATT_BRWNOUT_LIION_BASE_MV - BATT_BRWNOUT_LIION_CEILING_OFFSET_MV)
//! \brief Base voltage to start battery calculations for Alkaline/NiMH
00051 #define BATT_BRWNOUT_ALKAL_BASE_MV 800
//! \brief Constant to help with determining whether to round up or
//! not during calculation
00054 #define BATT_BRWNOUT_ALKAL_CEILING_OFFSET_MV 19
//! \brief Number of mV to add if rounding up in Alkaline/NiMH mode
00056 #define BATT_BRWNOUT_ALKAL_LEVEL_STEP_MV 20
//! \brief Constant value to be calculated by preprocessing
00058 #define BATT_BRWNOUT_ALKAL_EQN_CONST \
      (BATT_BRWNOUT_ALKAL_BASE_MV - BATT_BRWNOUT_ALKAL_CEILING_OFFSET_MV)

#define GAIN_CORRECTION 1012    // 1.012

#define VBUSVALID_THRESH_2_90V            0x0
#define VBUSVALID_THRESH_4_00V            0x1
#define VBUSVALID_THRESH_4_10V            0x2
#define VBUSVALID_THRESH_4_20V            0x3
#define VBUSVALID_THRESH_4_30V            0x4
#define VBUSVALID_THRESH_4_40V            0x5
#define VBUSVALID_THRESH_4_50V            0x6
#define VBUSVALID_THRESH_4_60V            0x7

#define LINREG_OFFSET_STEP_BELOW    0x2
#define BP_POWER_BATTMONITOR_BATT_VAL     16
#define BP_POWER_CHARGE_BATTCHRG_I  0
#define BP_POWER_CHARGE_STOP_ILIMIT 8

#define VDD4P2_ENABLED

#define DDI_POWER_BATTERY_XFER_THRESHOLD_MV 3200


#ifndef BATTERY_VOLTAGE_CMPTRIP100_THRESHOLD_MV
#define BATTERY_VOLTAGE_CMPTRIP100_THRESHOLD_MV 4000
#endif

#ifndef BATTERY_VOLTAGE_CMPTRIP105_THRESHOLD_MV
#define BATTERY_VOLTAGE_CMPTRIP105_THRESHOLD_MV 3800
#endif

/* #define DEBUG_IRQS */

/* to be re-enabled once FIQ functionality is added */
#define DISABLE_VDDIO_BO_PROTECTION
////////////////////////////////////////////////////////////////////////////////
// Globals & Variables
////////////////////////////////////////////////////////////////////////////////


/* Select your 5V Detection method */

/* static ddi_power_5vDetection_t DetectionMethod =
                  DDI_POWER_5V_VDD5V_GT_VDDIO; */
static ddi_power_5vDetection_t DetectionMethod = DDI_POWER_5V_VBUSVALID;

////////////////////////////////////////////////////////////////////////////////
// Code
////////////////////////////////////////////////////////////////////////////////

#if 0
static void dump_regs(void)
{
      printk("HW_POWER_CHARGE      0x%08x\n", __raw_readl(REGS_POWER_BASE + HW_POWER_CHARGE));
      printk("HW_POWER_STS         0x%08x\n", __raw_readl(REGS_POWER_BASE + HW_POWER_STS));
      printk("HW_POWER_BATTMONITOR 0x%08x\n", __raw_readl(REGS_POWER_BASE + HW_POWER_BATTMONITOR));
}
#endif

//! This array maps bit numbers to current increments, as used in the register
//! fields HW_POWER_CHARGE.STOP_ILIMIT and HW_POWER_CHARGE.BATTCHRG_I.
00120 static const uint16_t currentPerBit[] = {  10,  20,  50, 100, 200, 400 };

uint16_t ddi_power_convert_current_to_setting(uint16_t u16Current)
{
      int       i;
      uint16_t  u16Mask;
      uint16_t  u16Setting = 0;

      // Scan across the bit field, adding in current increments.
      u16Mask = (0x1 << 5);

      for (i = 5; (i >= 0) && (u16Current > 0); i--, u16Mask >>= 1) {
            if (u16Current >= currentPerBit[i]) {
                  u16Current -= currentPerBit[i];
                  u16Setting |= u16Mask;
            }
      }

      // Return the result.
      return(u16Setting);
}

////////////////////////////////////////////////////////////////////////////////
//! See hw_power.h for details.
////////////////////////////////////////////////////////////////////////////////
00145 uint16_t ddi_power_convert_setting_to_current(uint16_t u16Setting)
{
      int       i;
      uint16_t  u16Mask;
      uint16_t  u16Current = 0;

      // Scan across the bit field, adding in current increments.
      u16Mask = (0x1 << 5);

      for (i = 5; i >= 0; i--, u16Mask >>= 1) {
            if (u16Setting & u16Mask) u16Current += currentPerBit[i];
      }

      // Return the result.
      return(u16Current);
}

void ddi_power_Enable5vDetection(void)
{
      u32 val;
      // Disable hardware power down when 5V is inserted or removed
      __raw_writel(BM_POWER_5VCTRL_PWDN_5VBRNOUT,
            REGS_POWER_BASE + HW_POWER_5VCTRL_CLR);

      /* Enabling VBUSVALID hardware detection even if VDD5V_GT_VDDIO
       * is the detection method being used for 5V status (hardware
       * or software).  This is in case any other drivers (such as
       * USB) are specifically monitoring VBUSVALID status
       */
      __raw_writel(BM_POWER_5VCTRL_VBUSVALID_5VDETECT,
                  REGS_POWER_BASE + HW_POWER_5VCTRL_SET);

      // Set 5V detection threshold to 4.3V for VBUSVALID.
      __raw_writel(
            BF(VBUSVALID_THRESH_4_30V, POWER_5VCTRL_VBUSVALID_TRSH),
                  REGS_POWER_BASE + HW_POWER_5VCTRL_SET);

      // gotta set LINREG_OFFSET to STEP_BELOW according to manual
      val = __raw_readl(REGS_POWER_BASE + HW_POWER_VDDIOCTRL);
      val &= ~(BM_POWER_VDDIOCTRL_LINREG_OFFSET);
      val |= BF(LINREG_OFFSET_STEP_BELOW, POWER_VDDIOCTRL_LINREG_OFFSET);
      __raw_writel(val, REGS_POWER_BASE + HW_POWER_VDDIOCTRL);

      val = __raw_readl(REGS_POWER_BASE + HW_POWER_VDDACTRL);
      val &= ~(BM_POWER_VDDACTRL_LINREG_OFFSET);
      val |= BF(LINREG_OFFSET_STEP_BELOW, POWER_VDDACTRL_LINREG_OFFSET);
      __raw_writel(val, REGS_POWER_BASE + HW_POWER_VDDACTRL);

      val = __raw_readl(REGS_POWER_BASE + HW_POWER_VDDDCTRL);
      val &= ~(BM_POWER_VDDDCTRL_LINREG_OFFSET);
      val |= BF(LINREG_OFFSET_STEP_BELOW, POWER_VDDDCTRL_LINREG_OFFSET);
      __raw_writel(val, REGS_POWER_BASE + HW_POWER_VDDDCTRL);

      /* Clear vbusvalid interrupt flag */
      __raw_writel(BM_POWER_CTRL_VBUSVALID_IRQ,
                        REGS_POWER_BASE + HW_POWER_CTRL_CLR);
      __raw_writel(BM_POWER_CTRL_VDD5V_GT_VDDIO_IRQ,
                        REGS_POWER_BASE + HW_POWER_CTRL_CLR);
      /* enable vbusvalid irq */


      /* enable 5V Detection interrupt vbusvalid irq */
      switch (DetectionMethod) {
      case DDI_POWER_5V_VBUSVALID:
            /* Check VBUSVALID for 5V present */
            __raw_writel(BM_POWER_CTRL_ENIRQ_VBUS_VALID,
                        REGS_POWER_BASE + HW_POWER_CTRL_SET);
            break;
      case DDI_POWER_5V_VDD5V_GT_VDDIO:
            /* Check VDD5V_GT_VDDIO for 5V present */
            __raw_writel(BM_POWER_CTRL_ENIRQ_VDD5V_GT_VDDIO,
                        REGS_POWER_BASE + HW_POWER_CTRL_SET);
            break;
      }
}

/*
 * This function prepares the hardware for a 5V-to-battery handoff. It assumes
 * the current configuration is using 5V as the power source.  The 5V
 * interrupt will be set up for a 5V removal.
 */
void ddi_power_enable_5v_to_battery_handoff(void)
{
      /* Clear vbusvalid interrupt flag */
      __raw_writel(BM_POWER_CTRL_VBUSVALID_IRQ,
                        REGS_POWER_BASE + HW_POWER_CTRL_CLR);
      __raw_writel(BM_POWER_CTRL_VDD5V_GT_VDDIO_IRQ,
                        REGS_POWER_BASE + HW_POWER_CTRL_CLR);

      /* detect 5v unplug */
      __raw_writel(BM_POWER_CTRL_POLARITY_VBUSVALID,
                        REGS_POWER_BASE + HW_POWER_CTRL_CLR);
      __raw_writel(BM_POWER_CTRL_POLARITY_VDD5V_GT_VDDIO,
                        REGS_POWER_BASE + HW_POWER_CTRL_CLR);

#ifndef VDD4P2_ENABLED
      // Enable automatic transition to DCDC
      __raw_writel(BM_POWER_5VCTRL_DCDC_XFER,
                        REGS_POWER_BASE + HW_POWER_5VCTRL_SET);
#endif
}

/*
 * This function will handle all the power rail transitions necesarry to power
 * the chip from the battery when it was previously powered from the 5V power
 * source.
 */
void ddi_power_execute_5v_to_battery_handoff(void)
{
      int val;
#ifdef VDD4P2_ENABLED
      val = __raw_readl(REGS_POWER_BASE + HW_POWER_DCDC4P2);
      val &= ~(BM_POWER_DCDC4P2_ENABLE_DCDC | BM_POWER_DCDC4P2_ENABLE_4P2);
      __raw_writel(val, REGS_POWER_BASE + HW_POWER_DCDC4P2);

      __raw_writel(BM_POWER_5VCTRL_PWD_CHARGE_4P2,
                        REGS_POWER_BASE + HW_POWER_5VCTRL_SET);

      /* make VBUSVALID_TRSH 4400mV and set PWD_CHARGE_4P2 */
      __raw_writel(BM_POWER_5VCTRL_VBUSVALID_TRSH,
            HW_POWER_5VCTRL_CLR_ADDR);

      __raw_writel(BF_POWER_5VCTRL_VBUSVALID_TRSH(VBUSVALID_THRESH_4_40V),
            HW_POWER_5VCTRL_SET_ADDR);

#else
      // VDDD has different configurations depending on the battery type
      // and battery level.

      // For LiIon battery, we will use the DCDC to power VDDD.
      // Use LinReg offset for DCDC mode.
      __raw_writel(BF_POWER_VDDDCTRL_LINREG_OFFSET(LINREG_OFFSET_STEP_BELOW),
                        HW_POWER_BASE + HW_POWER_VDDDCTRL_SET);
      // Turn on the VDDD DCDC output and turn off the VDDD LinReg output.
      __raw_writel(BM_POWER_VDDDCTRL_DISABLE_FET,
                        HW_POWER_BASE + HW_POWER_VDDDCTRL_CLR);

      __raw_writel(BM_POWER_VDDDCTRL_ENABLE_LINREG,
                        HW_POWER_BASE + HW_POWER_VDDDCTRL_CLR);
      // Make sure stepping is enabled when using DCDC.
      __raw_writel(BM_POWER_VDDDCTRL_DISABLE_STEPPING,
                        HW_POWER_BASE + HW_POWER_VDDDCTRL_CLR);

      // Power VDDA and VDDIO from the DCDC.

      /* Use LinReg offset for DCDC mode. */
      __raw_writel(BF_POWER_VDDACTRL_LINREG_OFFSET(LINREG_OFFSET_STEP_BELOW),
                        HW_POWER_BASE + HW_POWER_VDDACTRL_SET);
      // Turn on the VDDA DCDC converter output and turn off LinReg output.
      __raw_writel(BM_POWER_VDDACTRL_DISABLE_FET,
                        HW_POWER_BASE + HW_POWER_VDDACTRL_CLR);
      __raw_writel(BM_POWER_VDDACTRL_ENABLE_LINREG,
                        HW_POWER_BASE + HW_POWER_VDDACTRL_CLR);

      // Make sure stepping is enabled when using DCDC.
      __raw_writel(BM_POWER_VDDACTRL_DISABLE_STEPPING,
                        HW_POWER_BASE + HW_POWER_VDDACTRL_CLR);

      // Use LinReg offset for DCDC mode.
      __raw_writel(BF_POWER_VDDIOCTRL_LINREG_OFFSET(
                              LINREG_OFFSET_STEP_BELOW
                                    ),
                        HW_POWER_BASE + HW_POWER_VDDIOCTRL_SET);

      /* Turn on the VDDIO DCDC output and turn on the LinReg output.*/
      __raw_writel(BM_POWER_VDDIOCTRL_DISABLE_FET,
                        HW_POWER_BASE + HW_POWER_VDDIOCTRL_CLR);

      __raw_writel(BM_POWER_5VCTRL_ILIMIT_EQ_ZERO,
                        HW_POWER_BASE + HW_POWER_5VCTRL_CLR_CLR);

      // Make sure stepping is enabled when using DCDC.
      __raw_writel(BM_POWER_VDDIOCTRL_DISABLE_STEPPING,
                        HW_POWER_BASE + HW_POWER_VDDIOCTRL_CLR);
#endif

}

/*
 * This function sets up battery-to-5V handoff. The power switch from
 * battery to 5V is automatic. This funtion enables the 5V present detection
 * such that the 5V interrupt can be generated if it is enabled. (The interrupt
 * handler can inform software the 5V present event.) To deal with noise or
 * a high current, this function enables DCDC1/2 based on the battery mode.
 */
void ddi_power_enable_battery_to_5v_handoff(void)
{
      /* Clear vbusvalid interrupt flag */
      __raw_writel(BM_POWER_CTRL_VBUSVALID_IRQ,
                        REGS_POWER_BASE + HW_POWER_CTRL_CLR);
      __raw_writel(BM_POWER_CTRL_VDD5V_GT_VDDIO_IRQ,
                        REGS_POWER_BASE + HW_POWER_CTRL_CLR);

      /* detect 5v plug-in */
      __raw_writel(BM_POWER_CTRL_POLARITY_VBUSVALID,
                        REGS_POWER_BASE + HW_POWER_CTRL_SET);
      __raw_writel(BM_POWER_CTRL_POLARITY_VDD5V_GT_VDDIO,
                        REGS_POWER_BASE + HW_POWER_CTRL_SET);

#ifndef VDD4P2_ENABLED
      // Force current from 5V to be zero by disabling its entry source.
      __raw_writel(BM_POWER_5VCTRL_ILIMIT_EQ_ZERO,
                        REGS_POWER_BASE + HW_POWER_5VCTRL_SET);
#endif
      // Allow DCDC be to active when 5V is present.
      __raw_writel(BM_POWER_5VCTRL_ENABLE_DCDC,
                        REGS_POWER_BASE + HW_POWER_5VCTRL_SET);
}

/* This function handles the transitions on each of theVDD5V_GT_VDDIO power
 * rails necessary to power the chip from the 5V power supply when it was
 * previously powered from the battery power supply.
 */
void ddi_power_execute_battery_to_5v_handoff(void)
{

#ifdef VDD4P2_ENABLED
      ddi_power_Enable4p2(450);
#else
      // Disable the DCDC during 5V connections.
      __raw_writel(BM_POWER_5VCTRL_ENABLE_DCDC,
                        HW_POWER_BAE + HW_POWER_5VCTRL_CLR);

      // Power the VDDD/VDDA/VDDIO rail from the linear regulator.  The DCDC
      // is ready to automatically power the chip when 5V is removed.
      // Use this configuration when powering from 5V

      // Use LinReg offset for LinReg mode
      __raw_writel(BF_POWER_VDDDCTRL_LINREG_OFFSET(LINREG_OFFSET_STEP_BELOW),
                        HW_POWER_BAE + HW_POWER_VDDDCTRL_SET);

      // Turn on the VDDD LinReg and turn on the VDDD DCDC output.  The
      // ENABLE_DCDC must be cleared to avoid LinReg and DCDC conflict.
      __raw_writel(BM_POWER_VDDDCTRL_ENABLE_LINREG,
                        HW_POWER_BAE + HW_POWER_VDDDCTRL_SET);
      __raw_writel(BM_POWER_VDDDCTRL_DISABLE_FET,
                        HW_POWER_BAE + HW_POWER_VDDDCTRL_CLR);

      // Make sure stepping is disabled when using linear regulators
      __raw_writel(BM_POWER_VDDDCTRL_DISABLE_STEPPING,
                        HW_POWER_BAE + HW_POWER_VDDDCTRL_SET);

      // Use LinReg offset for LinReg mode
      __raw_writel(BM_POWER_VDDACTRL_LINREG_OFFSET(LINREG_OFFSET_STEP_BELOW),
                        HW_POWER_BAE + HW_POWER_VDDACTRL_SET);


      // Turn on the VDDA LinReg output and prepare the DCDC for transfer.
      // ENABLE_DCDC must be clear to avoid DCDC and LinReg conflict.
      stmp3xxx_set(BM_POWER_VDDACTRL_ENABLE_LINREG,
                        HW_POWER_BASE + HW_POWER_VDDACTRL_SET);
      __raw_writel(BM_POWER_VDDACTRL_DISABLE_FET,
                        HW_POWER_BASE + HW_POWER_VDDACTRL_CLR);

      // Make sure stepping is disabled when using linear regulators
      __raw_writel(BM_POWER_VDDACTRL_DISABLE_STEPPING,
                         HW_POWER_BASE + HW_POWER_VDDACTRL_SET);

      // Use LinReg offset for LinReg mode.
      __raw_writel(BF_POWER_VDDIOCTRL_LINREG_OFFSET(
                              LINREG_OFFSET_STEP_BELOW),
                        HW_POWER_BASE + HW_POWER_VDDIOCTRL_SET);

      // Turn on the VDDIO LinReg output and prepare the VDDIO DCDC output.
        // ENABLE_DCDC must be cleared to prevent DCDC and LinReg conflict.
      __raw_writel(BM_POWER_VDDIOCTRL_DISABLE_FET,
                        HW_POWER_BASE + HW_POWER_VDDIOCTRL_CLR);
      __raw_writel(BM_POWER_5VCTRL_ILIMIT_EQ_ZERO,
                  REGS_POWER_BASE + HW_POWER_5VCTRL_CLR);

      // Make sure stepping is disabled when using DCDC.
      __raw_writel(BM_POWER_VDDIOCTRL_DISABLE_STEPPING,
                  REGS_POWER_BASE + HW_POWER_VDDIOCTRL_SET);
#endif
}


void ddi_power_Start4p2Dcdc(bool battery_ready)
{

      uint32_t temp_reg, old_values;

      /* set vbusvalid threshold to 2.9V because of errata */
      __raw_writel(BM_POWER_5VCTRL_VBUSVALID_TRSH,
                  HW_POWER_5VCTRL_CLR_ADDR);


#if 0
      if (battery_ready)
            ddi_power_EnableBatteryIrq();
      else
            enable_4p2_fiq_shutdown();
#endif

      /* enable hardware shutdown on battery brownout */
      __raw_writel(
                  BM_POWER_BATTMONITOR_PWDN_BATTBRNOUT |
                  __raw_readl(HW_POWER_BATTMONITOR_ADDR),
                  HW_POWER_BATTMONITOR_ADDR);

      /* set VBUS DROOP threshold to 4.3V */
      __raw_writel(BM_POWER_5VCTRL_VBUSDROOP_TRSH,
                  HW_POWER_5VCTRL_CLR_ADDR);

      /* turn of vbus valid detection.  Part of errate
       * workaround. */
      __raw_writel(BM_POWER_5VCTRL_PWRUP_VBUS_CMPS,
                  HW_POWER_5VCTRL_SET_ADDR);

      __raw_writel(BM_POWER_5VCTRL_VBUSVALID_5VDETECT,
                  HW_POWER_5VCTRL_CLR_ADDR);


      temp_reg = (BM_POWER_CTRL_ENIRQ_VDDD_BO |
            BM_POWER_CTRL_ENIRQ_VDDA_BO |
            BM_POWER_CTRL_ENIRQ_VDDIO_BO |
            BM_POWER_CTRL_ENIRQ_VDD5V_DROOP |
            BM_POWER_CTRL_ENIRQ_VBUS_VALID);

      /* save off old brownout enable values */
      old_values = __raw_readl(HW_POWER_CTRL_ADDR) &
            temp_reg;

      /* disable irqs affected by errata */
      __raw_writel(temp_reg, HW_POWER_CTRL_CLR_ADDR);

      /* Enable DCDC from 4P2 */
      __raw_writel(__raw_readl(HW_POWER_DCDC4P2_ADDR) |
                  BM_POWER_DCDC4P2_ENABLE_DCDC,
                  HW_POWER_DCDC4P2_ADDR);

      /* give a delay to check for errate noise problem */
      mdelay(1);

      temp_reg = (BM_POWER_CTRL_VDDD_BO_IRQ |
            BM_POWER_CTRL_VDDA_BO_IRQ |
            BM_POWER_CTRL_VDDIO_BO_IRQ |
            BM_POWER_CTRL_VDD5V_DROOP_IRQ |
            BM_POWER_CTRL_VBUSVALID_IRQ);

      /* stay in this loop until the false brownout indciations
       * no longer occur or until 5V actually goes away
       */
      while ((__raw_readl(HW_POWER_CTRL_ADDR) & temp_reg) &&
            !(__raw_readl(HW_POWER_CTRL_ADDR) &
                        BM_POWER_CTRL_VDD5V_GT_VDDIO_IRQ)) {
            __raw_writel(temp_reg, HW_POWER_CTRL_CLR_ADDR);

            mdelay(1);
      }

      /* revert to previous enable irq values */
      __raw_writel(old_values, HW_POWER_CTRL_SET_ADDR);

      if (DetectionMethod == DDI_POWER_5V_VBUSVALID)
            __raw_writel(BM_POWER_5VCTRL_VBUSVALID_5VDETECT,
                  HW_POWER_5VCTRL_SET_ADDR);
}


/* set the optimal CMPTRIP for the best possible 5V
 * disconnection handling but without drawing power
 * from the power on a stable 4p2 rails (at 4.2V).
 */
void ddi_power_handle_cmptrip(void)
{
      enum ddi_power_5v_status pmu_5v_status;
      uint32_t temp = __raw_readl(HW_POWER_DCDC4P2_ADDR);
      temp &= ~(BM_POWER_DCDC4P2_CMPTRIP);

      pmu_5v_status = ddi_power_GetPmu5vStatus();

      /* CMPTRIP should remain at 31 when 5v is disconnected
       * or 5v is connected but hasn't been handled yet
       */
      if (pmu_5v_status != existing_5v_connection)
            temp |= (31 << BP_POWER_DCDC4P2_CMPTRIP);
      else if (ddi_power_GetBattery() >
                  BATTERY_VOLTAGE_CMPTRIP100_THRESHOLD_MV)
            temp |= (1 << BP_POWER_DCDC4P2_CMPTRIP);
      else if (ddi_power_GetBattery() >
                  BATTERY_VOLTAGE_CMPTRIP105_THRESHOLD_MV)
            temp |= (24 << BP_POWER_DCDC4P2_CMPTRIP);
      else
            temp |= (31 << BP_POWER_DCDC4P2_CMPTRIP);


      __raw_writel(temp, HW_POWER_DCDC4P2_ADDR);
}

void ddi_power_Init4p2Params(void)
{
      uint32_t temp;

      ddi_power_handle_cmptrip();

      temp = __raw_readl(HW_POWER_DCDC4P2_ADDR);

      /* DROPOUT CTRL to 10, TRG to 0 */
      temp &= ~(BM_POWER_DCDC4P2_TRG | BM_POWER_DCDC4P2_DROPOUT_CTRL);
      temp |= (0xa << BP_POWER_DCDC4P2_DROPOUT_CTRL);

      __raw_writel(temp, HW_POWER_DCDC4P2_ADDR);


      temp = __raw_readl(HW_POWER_5VCTRL_ADDR);

      /* HEADROOM_ADJ to 4, CHARGE_4P2_ILIMIT to 0 */
      temp &= ~(BM_POWER_5VCTRL_HEADROOM_ADJ |
                  BM_POWER_5VCTRL_CHARGE_4P2_ILIMIT);
      temp |= (4 << BP_POWER_5VCTRL_HEADROOM_ADJ);

}

bool ddi_power_IsBattRdyForXfer(void)
{
      uint16_t u16BatteryVoltage = ddi_power_GetBattery();

      if (u16BatteryVoltage > DDI_POWER_BATTERY_XFER_THRESHOLD_MV)
            return true;
      else
            return false;

}

void ddi_power_EnableVbusDroopIrq(void)
{

      __raw_writel(BM_POWER_CTRL_VDD5V_DROOP_IRQ,
                  HW_POWER_CTRL_CLR_ADDR);

      __raw_writel(BM_POWER_CTRL_ENIRQ_VDD5V_DROOP,
                  HW_POWER_CTRL_SET_ADDR);

}


void ddi_power_Enable4p2(uint16_t target_current_limit_ma)
{

      uint16_t u16BatteryVoltage;
      uint32_t temp_reg;

      ddi_power_Init4p2Params();

      /* disable 4p2 rail brownouts for now. (they
       * should have already been off at this point) */
      __raw_writel(BM_POWER_CTRL_ENIRQ_DCDC4P2_BO,
            HW_POWER_CTRL_CLR_ADDR);

      u16BatteryVoltage = ddi_power_GetBattery();

      if (ddi_power_IsBattRdyForXfer()) {

            /* PWD_CHARGE_4P2 should already be set but just in case... */
            __raw_writel(BM_POWER_5VCTRL_PWD_CHARGE_4P2,
                        HW_POWER_5VCTRL_SET_ADDR);

            /* set CMPTRIP to DCDC_4P2 pin >= BATTERY pin */
            temp_reg = __raw_readl(HW_POWER_DCDC4P2_ADDR);
            temp_reg &= ~(BM_POWER_DCDC4P2_CMPTRIP);
            temp_reg |= (31 << BP_POWER_DCDC4P2_CMPTRIP);
            __raw_writel(temp_reg, HW_POWER_DCDC4P2_ADDR);

            /* since we have a good battery, we can go ahead
             * and turn on the Dcdcing from the 4p2 source.
             * This is helpful in working around the chip
             * errata.
             */
            ddi_power_Start4p2Dcdc(true);

            /* Enable VbusDroopIrq to handle errata */

            /* set vbus droop detection level to 4.3V */
            __raw_writel(BM_POWER_5VCTRL_VBUSDROOP_TRSH,
                        HW_POWER_5VCTRL_CLR_ADDR);

            ddi_power_EnableVbusDroopIrq();
            /* now that the DCDC4P2 problems are cleared,
             * turn on and ramp up the 4p2 regulator
             */
            temp_reg = ddi_power_BringUp4p2Regulator(
                  target_current_limit_ma, true);

            /* if we still have our 5V connection, we can disable
             * battery brownout interrupt.  This is because the
             * VDD5V DROOP IRQ handler will also shutdown if battery
             * is browned out and it will enable the battery brownout
             * and bring VBUSVALID_TRSH level back to a normal level
             * which caused the hardware battery brownout shutdown
             * to be enabled.  The benefit of this is that device
             * that have detachable batteries (or devices going through
             * the assembly line and running this firmware to test
             *  with) can avoid shutting down if 5V is present and
             *  battery voltage goes away.
             */
            if (!(__raw_readl(HW_POWER_CTRL_ADDR) &
                  (BM_POWER_CTRL_VBUSVALID_IRQ |
                  BM_POWER_CTRL_VDD5V_DROOP_IRQ))) {
                  ddi_power_EnableBatteryBoInterrupt(false);
            }



            printk(KERN_INFO "4P2 rail started.  5V current limit\
                  set to %dmA\n",   temp_reg);

      } else {

            printk(KERN_ERR "4P2 rail was attempted to be started \
                  from a system\
                  with a very low battery voltage.  This is not\
                  yet handled by the kernel driver, only by the\
                  bootlets.  Remaining on battery power.\n");

            if ((__raw_readl(HW_POWER_5VCTRL_ADDR) &&
                              BM_POWER_5VCTRL_ENABLE_DCDC))
                  ddi_power_EnableBatteryBoInterrupt(true);

#if 0
            /* enable hardware shutdown (if 5v disconnected)
             * on battery brownout */
            __raw_writel(
                  BM_POWER_BATTMONITOR_PWDN_BATTBRNOUT |
                  __raw_readl(HW_POWER_BATTMONITOR_ADDR),
                  HW_POWER_BATTMONITOR_ADDR);

            /* turn on and ramp up the 4p2 regulator */
            temp_reg = ddi_power_BringUp4p2Regulator(
                  target_current_limit_ma, false);

            Configure4p2FiqShutdown();

            SetVbusValidThresh(0);
#endif
      }
}

/* enable and ramp up 4p2 regulator */
uint16_t ddi_power_BringUp4p2Regulator(
            uint16_t target_current_limit_ma,
            bool b4p2_dcdc_enabled)
{
      uint32_t temp_reg;
      uint16_t charge_4p2_ilimit = 0;

      /* initial current limit to 0 */
      __raw_writel(BM_POWER_5VCTRL_CHARGE_4P2_ILIMIT,
            HW_POWER_5VCTRL_CLR_ADDR);

      __raw_writel(__raw_readl(HW_POWER_DCDC4P2_ADDR) |
            BM_POWER_DCDC4P2_ENABLE_4P2,
            HW_POWER_DCDC4P2_ADDR);

      /* set 4p2 target voltage to zero */
      temp_reg = __raw_readl(HW_POWER_DCDC4P2_ADDR);
      temp_reg &= (~BM_POWER_DCDC4P2_TRG);
      __raw_writel(temp_reg, HW_POWER_DCDC4P2_ADDR);

      /* Enable 4P2 regulator*/
      __raw_writel(BM_POWER_5VCTRL_PWD_CHARGE_4P2,
                  HW_POWER_5VCTRL_CLR_ADDR);

      if (target_current_limit_ma > 780)
            target_current_limit_ma = 780;

      ddi_power_Set4p2BoLevel(4150);

      /* possibly not necessary but recommended for unloaded
       * 4p2 rail
       */
      __raw_writel(BM_POWER_CHARGE_ENABLE_LOAD,
            HW_POWER_CHARGE_SET_ADDR);

      while (charge_4p2_ilimit < target_current_limit_ma) {

            if (__raw_readl(HW_POWER_CTRL_ADDR) &
                  (BM_POWER_CTRL_VBUSVALID_IRQ |
                  BM_POWER_CTRL_VDD5V_DROOP_IRQ))
                  break;


            charge_4p2_ilimit += 100;
            if (charge_4p2_ilimit > target_current_limit_ma)
                  charge_4p2_ilimit = target_current_limit_ma;

            ddi_power_set_4p2_ilimit(charge_4p2_ilimit);

            /* dcdc4p2 enable_dcdc must be enabled for
             * 4p2 bo indication to function.  If not enabled,
             * skip using bo level detection
             */
            if (!(b4p2_dcdc_enabled))
                  msleep(1);
            else if     (__raw_readl(HW_POWER_STS_ADDR) &
                  BM_POWER_STS_DCDC_4P2_BO)
                  msleep(1);
            else {
                  charge_4p2_ilimit = target_current_limit_ma;
                  ddi_power_set_4p2_ilimit(charge_4p2_ilimit);
            }
      }

      ddi_power_Set4p2BoLevel(3600);

      __raw_writel(BM_POWER_CTRL_DCDC4P2_BO_IRQ,
                  HW_POWER_CTRL_CLR_ADDR);

      /* rail should now be up and loaded.  Extra
       * internal load is not necessary.
       */
      __raw_writel(BM_POWER_CHARGE_ENABLE_LOAD,
            HW_POWER_CHARGE_CLR_ADDR);

      return charge_4p2_ilimit;

}


void ddi_power_Set4p2BoLevel(uint16_t bo_voltage_mv)
{
      uint16_t bo_reg_value;
      uint32_t temp;

      if (bo_voltage_mv < 3600)
            bo_voltage_mv = 3600;
      else if (bo_voltage_mv > 4375)
            bo_voltage_mv = 4375;

      bo_reg_value = (bo_voltage_mv - 3600) / 25;

      temp = __raw_readl(HW_POWER_DCDC4P2_ADDR);
      temp &= (~BM_POWER_DCDC4P2_BO);
      temp |= (bo_reg_value << BP_POWER_DCDC4P2_BO);
      __raw_writel(temp, HW_POWER_DCDC4P2_ADDR);
}



void ddi_power_init_handoff(void)
{
      int val;
      /* The following settings give optimal power supply capability */

      // enable 5v presence detection
      ddi_power_Enable5vDetection();

      if (ddi_power_Get5vPresentFlag())
            /* It's 5V mode, enable 5V-to-battery handoff */
            ddi_power_enable_5v_to_battery_handoff();
      else
            /* It's battery mode, enable battery-to-5V handoff */
            ddi_power_enable_battery_to_5v_handoff();

      // Finally enable the battery adjust
      val = __raw_readl(REGS_POWER_BASE + HW_POWER_BATTMONITOR);
      val |= BM_POWER_BATTMONITOR_EN_BATADJ;
      __raw_writel(val, REGS_POWER_BASE + HW_POWER_BATTMONITOR);
}


void ddi_power_EnableBatteryInterrupt(bool enable)
{

      __raw_writel(BM_POWER_CTRL_BATT_BO_IRQ,
                  HW_POWER_CTRL_CLR_ADDR);

      __raw_writel(BM_POWER_CTRL_ENIRQBATT_BO,
                  HW_POWER_CTRL_SET_ADDR);

}


int ddi_power_init_battery(void)
{

      int ret = 0;

      if (!(__raw_readl(HW_POWER_5VCTRL_ADDR) &&
                  BM_POWER_5VCTRL_ENABLE_DCDC)) {
            printk(KERN_ERR "WARNING: Power Supply not\
                  initialized correctly by \
                  pre-kernel bootlets. HW_POWER_5VCTRL \
                  ENABLE_DCDC should already be set.  Kernel \
                  power driver behavior may not be reliable \n");
            ret = 1;
      }

      /* the following code to enable automatic battery measurement
       * should have already been enabled in the boot prep files.  Not
       * sure if this is necessary or possibly susceptible to
       * mis-coordination
       */


      ret = !hw_lradc_present(BATTERY_VOLTAGE_CH);

      if (ret) {
            printk(KERN_ERR "%s: hw_lradc_present failed\n", __func__);
            return -ENODEV;
      } else {
            uint16_t wait_time = 0;

            hw_lradc_configure_channel(BATTERY_VOLTAGE_CH, 0 /* div2 */ ,
                                 0 /* acc */ ,
                                 0 /* num_samples */);

            /* Setup the trigger loop forever */
            hw_lradc_set_delay_trigger(LRADC_DELAY_TRIGGER_BATTERY,
                  1 << BATTERY_VOLTAGE_CH,
                  1 << LRADC_DELAY_TRIGGER_BATTERY,
                  0, 200);

            /* Clear the accumulator & NUM_SAMPLES */
            stmp3xxx_clearl(0xFFFFFFFF,
                  REGS_LRADC_BASE + HW_LRADC_CHn(BATTERY_VOLTAGE_CH));

            /* clear previous "measurement performed" status */
            __raw_writel(1 << BATTERY_VOLTAGE_CH,
                  HW_LRADC_CTRL1_CLR_ADDR);

            /* set to LiIon scale factor */
            __raw_writel(BM_LRADC_CONVERSION_SCALE_FACTOR,
                  HW_LRADC_CONVERSION_SET_ADDR);

            /* kick off the trigger */
            hw_lradc_set_delay_trigger_kick(
                        LRADC_DELAY_TRIGGER_BATTERY, 1);


            /* wait for 1st converstion to be complete before
             * enabling automatic copy to power supply
             * peripheral
             */
            while (!(__raw_readl(HW_LRADC_CTRL1_ADDR) &
                  1 << BATTERY_VOLTAGE_CH) &&
                        (wait_time < 10)) {
                  wait_time++;
                  udelay(1);
            }

            __raw_writel(BM_LRADC_CONVERSION_AUTOMATIC,
                  HW_LRADC_CONVERSION_SET_ADDR);
      }

#ifndef VDD4P2_ENABLED
      /* prepare handoff */
      ddi_power_init_handoff();
#endif
      return ret;
}

/*
 * Use the the lradc1 channel
 * get the die temperature from on-chip sensor.
 */
uint16_t MeasureInternalDieTemperature(void)
{
      uint32_t  ch8Value, ch9Value;

      /* power up internal tep sensor block */
      __raw_writel(BM_LRADC_CTRL2_TEMPSENSE_PWD,
                  REGS_LRADC_BASE + HW_LRADC_CTRL2_CLR);

      /* mux to the lradc 8th temp channel */
      __raw_writel(BF(0xF, LRADC_CTRL4_LRADC1SELECT),
                  REGS_LRADC_BASE + HW_LRADC_CTRL4_CLR);
      __raw_writel(BF(8, LRADC_CTRL4_LRADC1SELECT),
                  REGS_LRADC_BASE + HW_LRADC_CTRL4_SET);

      /* Clear the interrupt flag */
      __raw_writel(BM_LRADC_CTRL1_LRADC1_IRQ,
                  REGS_LRADC_BASE + HW_LRADC_CTRL1_CLR);
      __raw_writel(BF(1 << LRADC_CH1, LRADC_CTRL0_SCHEDULE),
                  REGS_LRADC_BASE + HW_LRADC_CTRL0_SET);

      /* Wait for conversion complete*/
      while (!(__raw_readl(REGS_LRADC_BASE + HW_LRADC_CTRL1)
                  & BM_LRADC_CTRL1_LRADC1_IRQ))
                cpu_relax();

      /* Clear the interrupt flag again */
      __raw_writel(BM_LRADC_CTRL1_LRADC1_IRQ,
                  REGS_LRADC_BASE + HW_LRADC_CTRL1_CLR);

      // read temperature value and clr lradc
      ch8Value = __raw_readl(REGS_LRADC_BASE +
                  HW_LRADC_CHn(LRADC_CH1)) & BM_LRADC_CHn_VALUE;


      __raw_writel(BM_LRADC_CHn_VALUE,
                  REGS_LRADC_BASE + HW_LRADC_CHn_CLR(LRADC_CH1));

      /* mux to the lradc 9th temp channel */
      __raw_writel(BF(0xF, LRADC_CTRL4_LRADC1SELECT),
                  REGS_LRADC_BASE + HW_LRADC_CTRL4_CLR);
      __raw_writel(BF(9, LRADC_CTRL4_LRADC1SELECT),
                  REGS_LRADC_BASE + HW_LRADC_CTRL4_SET);

      /* Clear the interrupt flag */
      __raw_writel(BM_LRADC_CTRL1_LRADC1_IRQ,
                  REGS_LRADC_BASE + HW_LRADC_CTRL1_CLR);
      __raw_writel(BF(1 << LRADC_CH1, LRADC_CTRL0_SCHEDULE),
                  REGS_LRADC_BASE + HW_LRADC_CTRL0_SET);
      // Wait for conversion complete
      while (!(__raw_readl(REGS_LRADC_BASE + HW_LRADC_CTRL1)
                  & BM_LRADC_CTRL1_LRADC1_IRQ))
                cpu_relax();

      /* Clear the interrupt flag */
      __raw_writel(BM_LRADC_CTRL1_LRADC1_IRQ,
                  REGS_LRADC_BASE + HW_LRADC_CTRL1_CLR);
      // read temperature value
      ch9Value = __raw_readl(
                  REGS_LRADC_BASE + HW_LRADC_CHn(LRADC_CH1))
              & BM_LRADC_CHn_VALUE;


      __raw_writel(BM_LRADC_CHn_VALUE,
                  REGS_LRADC_BASE + HW_LRADC_CHn_CLR(LRADC_CH1));

      /* power down temp sensor block */
      __raw_writel(BM_LRADC_CTRL2_TEMPSENSE_PWD,
                  REGS_LRADC_BASE + HW_LRADC_CTRL2_SET);


      return (uint16_t)((ch9Value-ch8Value)*GAIN_CORRECTION/4000);
}


////////////////////////////////////////////////////////////////////////////////
//! Name: ddi_power_GetBatteryMode
//!
//! \brief
////////////////////////////////////////////////////////////////////////////////
00980 ddi_power_BatteryMode_t ddi_power_GetBatteryMode(void)
{
      return DDI_POWER_BATT_MODE_LIION;
}

////////////////////////////////////////////////////////////////////////////////
//! Name: ddi_power_GetBatteryChargerEnabled
//!
//! \brief
////////////////////////////////////////////////////////////////////////////////
00990 bool ddi_power_GetBatteryChargerEnabled(void)
{
#if 0
      return (__raw_readl(REGS_POWER_BASE + HW_POWER_STS) & BM_POWER_STS_BATT_CHRG_PRESENT) ? 1 : 0;
#endif
      return 1;
}

////////////////////////////////////////////////////////////////////////////////
//!
//! \brief Report if the charger hardware power is on.
//!
//! \fntype Function
//!
//! This function reports if the charger hardware power is on.
//!
//! \retval  Zero if the charger hardware is not powered. Non-zero otherwise.
//!
//! Note that the bit we're looking at is named PWD_BATTCHRG. The "PWD"
//! stands for "power down". Thus, when the bit is set, the battery charger
//! hardware is POWERED DOWN.
////////////////////////////////////////////////////////////////////////////////
01012 bool ddi_power_GetChargerPowered(void)
{
      return (__raw_readl(REGS_POWER_BASE + HW_POWER_CHARGE) & BM_POWER_CHARGE_PWD_BATTCHRG) ? 0 : 1;
}

////////////////////////////////////////////////////////////////////////////////
//!
//! \brief Turn the charging hardware on or off.
//!
//! \fntype Function
//!
//! This function turns the charging hardware on or off.
//!
//! \param[in]  on  Indicates whether the charging hardware should be on or off.
//!
//! Note that the bit we're looking at is named PWD_BATTCHRG. The "PWD"
//! stands for "power down". Thus, when the bit is set, the battery charger
//! hardware is POWERED DOWN.
////////////////////////////////////////////////////////////////////////////////
01031 void ddi_power_SetChargerPowered(bool bPowerOn)
{
      // Hit the battery charge power switch.
      if (bPowerOn) {
            __raw_writel(BM_POWER_CHARGE_PWD_BATTCHRG,
                  REGS_POWER_BASE + HW_POWER_CHARGE_CLR);
            __raw_writel(BM_POWER_5VCTRL_PWD_CHARGE_4P2,
                  REGS_POWER_BASE + HW_POWER_5VCTRL_CLR);
      } else {
            __raw_writel(BM_POWER_CHARGE_PWD_BATTCHRG,
                  REGS_POWER_BASE + HW_POWER_CHARGE_SET);
#ifndef VDD4P2_ENABLED
            __raw_writel(BM_POWER_5VCTRL_PWD_CHARGE_4P2,
                  REGS_POWER_BASE + HW_POWER_5VCTRL_SET);
#endif
      }

//#ifdef CONFIG_POWER_SUPPLY_DEBUG
#if 0
      printk("Battery charger: charger %s\n", bPowerOn ? "ON!" : "OFF");
      dump_regs();
#endif
}

////////////////////////////////////////////////////////////////////////////////
//!
//! \brief Reports if the charging current has fallen below the threshold.
//!
//! \fntype Function
//!
//! This function reports if the charging current that the battery is accepting
//! has fallen below the threshold.
//!
//! Note that this bit is regarded by the hardware guys as very slightly
//! unreliable. They recommend that you don't believe a value of zero until
//! you've sampled it twice.
//!
//! \retval  Zero if the battery is accepting less current than indicated by the
//!          charging threshold. Non-zero otherwise.
//!
////////////////////////////////////////////////////////////////////////////////
01072 int ddi_power_GetChargeStatus(void)
{
      return (__raw_readl(REGS_POWER_BASE + HW_POWER_STS) & BM_POWER_STS_CHRGSTS) ? 1 : 0;
}

////////////////////////////////////////////////////////////////////////////////
// Battery Voltage
////////////////////////////////////////////////////////////////////////////////

////////////////////////////////////////////////////////////////////////////////
//!
//! \brief Report the voltage across the battery.
//!
//! \fntype Function
//!
//! This function reports the voltage across the battery. Should return a
//! value in range ~3000 - 4200 mV.
//!
//! \retval The voltage across the battery, in mV.
//!
////////////////////////////////////////////////////////////////////////////////

//! \brief Constant value for 8mV steps used in battery translation
01095 #define BATT_VOLTAGE_8_MV 8

uint16_t ddi_power_GetBattery(void)
{
      uint32_t    u16BattVolt;

      // Get the raw result of battery measurement
      u16BattVolt = __raw_readl(REGS_POWER_BASE + HW_POWER_BATTMONITOR);
      u16BattVolt &= BM_POWER_BATTMONITOR_BATT_VAL;
      u16BattVolt >>= BP_POWER_BATTMONITOR_BATT_VAL;

      // Adjust for 8-mV LSB resolution and return
      u16BattVolt *= BATT_VOLTAGE_8_MV;

//#ifdef CONFIG_POWER_SUPPLY_DEBUG
#if 0
      printk("Battery charger: %u mV\n", u16BattVolt);
#endif

      return u16BattVolt;
}

#if 0
////////////////////////////////////////////////////////////////////////////////
//!
//! \brief Report the voltage across the battery.
//!
//! \fntype Function
//!
//! This function reports the voltage across the battery.
//!
//! \retval The voltage across the battery, in mV.
//!
////////////////////////////////////////////////////////////////////////////////
uint16_t ddi_power_GetBatteryBrownout(void)
{
      uint32_t    u16BatteryBrownoutLevel;

      // Get battery brownout level
      u16BatteryBrownoutLevel = __raw_readl(REGS_POWER_BASE + HW_POWER_BATTMONITOR);
      u16BatteryBrownoutLevel &= BM_POWER_BATTMONITOR_BRWNOUT_LVL;
      u16BatteryBrownoutLevel >>= BP_POWER_BATTMONITOR_BRWNOUT_LVL;

      // Calculate battery brownout level
      switch (ddi_power_GetBatteryMode()) {
        case DDI_POWER_BATT_MODE_LIION:
            u16BatteryBrownoutLevel *= BATT_BRWNOUT_LIION_LEVEL_STEP_MV;
            u16BatteryBrownoutLevel += BATT_BRWNOUT_LIION_BASE_MV;
            break;
        case DDI_POWER_BATT_MODE_ALKALINE_NIMH:
            u16BatteryBrownoutLevel *= BATT_BRWNOUT_ALKAL_LEVEL_STEP_MV;
            u16BatteryBrownoutLevel += BATT_BRWNOUT_ALKAL_BASE_MV;
            break;
        default:
            u16BatteryBrownoutLevel = 0;
            break;
      }
      return u16BatteryBrownoutLevel;
}

////////////////////////////////////////////////////////////////////////////////
//!
//! \brief Set battery brownout level
//!
//! \fntype     Reentrant Function
//!
//! This function sets the battery brownout level in millivolt. It transforms the
//! input brownout value from millivolts to the hardware register bit field value
//! taking the ceiling value in the calculation.
//!
//! \param[in]  u16BattBrownout_mV      Battery battery brownout level in mV
//!
//! \return     SUCCESS
//!
////////////////////////////////////////////////////////////////////////////////
int ddi_power_SetBatteryBrownout(uint16_t u16BattBrownout_mV)
{
      int16_t i16BrownoutLevel;
      int ret = 0;

      // Calculate battery brownout level
      switch (ddi_power_GetBatteryMode()) {
        case DDI_POWER_BATT_MODE_LIION:
            i16BrownoutLevel  = u16BattBrownout_mV -
                  BATT_BRWNOUT_LIION_EQN_CONST;
            i16BrownoutLevel /= BATT_BRWNOUT_LIION_LEVEL_STEP_MV;
            break;
        case DDI_POWER_BATT_MODE_ALKALINE_NIMH:
            i16BrownoutLevel  = u16BattBrownout_mV -
                  BATT_BRWNOUT_ALKAL_EQN_CONST;
            i16BrownoutLevel /= BATT_BRWNOUT_ALKAL_LEVEL_STEP_MV;
            break;
        default:
            return -EINVAL;
      }

      // Do a check to make sure nothing went wrong.
      if (i16BrownoutLevel <= 0x0f) {
            //Write the battery brownout level
            __raw_writel(
                  BF(i16BrownoutLevel, POWER_BATTMONITOR_BRWNOUT_LVL),
                        REGS_POWER_BASE + HW_POWER_BATTMONITOR_SET);
      } else
            ret = -EINVAL;

      return ret;
}
#endif

////////////////////////////////////////////////////////////////////////////////
// Currents
////////////////////////////////////////////////////////////////////////////////


////////////////////////////////////////////////////////////////////////////////
//! Name: ddi_power_SetMaxBatteryChargeCurrent
//!
//! \brief
////////////////////////////////////////////////////////////////////////////////
01214 uint16_t ddi_power_SetMaxBatteryChargeCurrent(uint16_t u16MaxCur)
{
      uint32_t   u16OldSetting;
      uint32_t   u16NewSetting;
      uint32_t   u16ToggleMask;

      // Get the old setting.
      u16OldSetting = (__raw_readl(REGS_POWER_BASE + HW_POWER_CHARGE) & BM_POWER_CHARGE_BATTCHRG_I) >>
            BP_POWER_CHARGE_BATTCHRG_I;

      // Convert the new threshold into a setting.
      u16NewSetting = ddi_power_convert_current_to_setting(u16MaxCur);

      /* Compute the toggle mask. */
      u16ToggleMask = u16OldSetting ^ u16NewSetting;

      /* Write to the toggle register.*/
      __raw_writel(u16ToggleMask << BP_POWER_CHARGE_BATTCHRG_I,
                  REGS_POWER_BASE + HW_POWER_CHARGE_TOG);

      // Tell the caller what current we're set at now.
      return ddi_power_convert_setting_to_current(u16NewSetting);
}

////////////////////////////////////////////////////////////////////////////////
//! Name: ddi_power_GetMaxBatteryChargeCurrent
//!
//! \brief
////////////////////////////////////////////////////////////////////////////////
01243 uint16_t ddi_power_GetMaxBatteryChargeCurrent(void)
{
      uint32_t u8Bits;

      // Get the raw data from register
      u8Bits = (__raw_readl(REGS_POWER_BASE + HW_POWER_CHARGE) & BM_POWER_CHARGE_BATTCHRG_I) >>
            BP_POWER_CHARGE_BATTCHRG_I;

      // Translate raw data to current (in mA) and return it
      return ddi_power_convert_setting_to_current(u8Bits);
}

////////////////////////////////////////////////////////////////////////////////
//! Name: ddi_power_GetMaxChargeCurrent
//!
//! \brief
////////////////////////////////////////////////////////////////////////////////
01260 uint16_t ddi_power_SetBatteryChargeCurrentThreshold(uint16_t u16Thresh)
{
      uint32_t   u16OldSetting;
      uint32_t   u16NewSetting;
      uint32_t   u16ToggleMask;

      //-------------------------------------------------------------------
      // See ddi_power_SetMaxBatteryChargeCurrent for an explanation of
      // why we're using the toggle register here.
      //
      // Since this function doesn't have any major hardware effect,
      // we could use the usual macros for writing to this bit field. But,
      // for the sake of parallel construction and any potentially odd
      // effects on the status bit, we use the toggle register in the same
      // way as ddi_bc_hwSetMaxCurrent.
      //-------------------------------------------------------------------

      //-------------------------------------------------------------------
      // The threshold hardware can't express as large a range as the max
      // current setting, but we can use the same functions as long as we
      // add an extra check here.
      //
      // Thresholds larger than 180mA can't be expressed.
      //-------------------------------------------------------------------

      if (u16Thresh > 180)
            u16Thresh = 180;

      ////////////////////////////////////////
      // Create the mask
      ////////////////////////////////////////

      // Get the old setting.
      u16OldSetting = (__raw_readl(REGS_POWER_BASE + HW_POWER_CHARGE) & BM_POWER_CHARGE_STOP_ILIMIT) >>
            BP_POWER_CHARGE_STOP_ILIMIT;

      // Convert the new threshold into a setting.
      u16NewSetting = ddi_power_convert_current_to_setting(u16Thresh);

      // Compute the toggle mask.
      u16ToggleMask = u16OldSetting ^ u16NewSetting;

      /////////////////////////////////////////
      // Write to the register
      /////////////////////////////////////////

      // Write to the toggle register.
      __raw_writel(BF_POWER_CHARGE_STOP_ILIMIT(u16ToggleMask),
                  REGS_POWER_BASE + HW_POWER_CHARGE_TOG);

      // Tell the caller what current we're set at now.
      return ddi_power_convert_setting_to_current(u16NewSetting);
}

////////////////////////////////////////////////////////////////////////////////
//! Name: ddi_power_GetBatteryChargeCurrentThreshold
//!
//! \brief
////////////////////////////////////////////////////////////////////////////////
01319 uint16_t ddi_power_GetBatteryChargeCurrentThreshold(void)
{
      uint32_t u16Threshold;

      u16Threshold = (__raw_readl(REGS_POWER_BASE + HW_POWER_CHARGE) & BM_POWER_CHARGE_STOP_ILIMIT) >>
            BP_POWER_CHARGE_STOP_ILIMIT;

      return ddi_power_convert_setting_to_current(u16Threshold);
}

////////////////////////////////////////////////////////////////////////////////
// Conversion
////////////////////////////////////////////////////////////////////////////////

////////////////////////////////////////////////////////////////////////////////
//!
//! \brief Compute the actual current expressible in the hardware.
//!
//! \fntype Function
//!
//! Given a desired current, this function computes the actual current
//! expressible in the hardware.
//!
//! Note that the hardware has a minimum resolution of 10mA and a maximum
//! expressible value of 780mA (see the data sheet for details). If the given
//! current cannot be expressed exactly, then the largest expressible smaller
//! value will be used.
//!
//! \param[in]  u16Current  The current of interest.
//!
//! \retval  The corresponding current in mA.
//!
////////////////////////////////////////////////////////////////////////////////
01352 uint16_t ddi_power_ExpressibleCurrent(uint16_t u16Current)
{
      return ddi_power_convert_setting_to_current(
            ddi_power_convert_current_to_setting(u16Current));
}

////////////////////////////////////////////////////////////////////////////////
//! Name: ddi_power_Get5VPresent
//!
//! \brief
////////////////////////////////////////////////////////////////////////////////
01363 bool ddi_power_Get5vPresentFlag(void)
{
      switch (DetectionMethod) {
      case DDI_POWER_5V_VBUSVALID:
            // Check VBUSVALID for 5V present
            return ((__raw_readl(REGS_POWER_BASE + HW_POWER_STS) & BM_POWER_STS_VBUSVALID) != 0);
      case DDI_POWER_5V_VDD5V_GT_VDDIO:
            // Check VDD5V_GT_VDDIO for 5V present
            return ((__raw_readl(REGS_POWER_BASE + HW_POWER_STS) & BM_POWER_STS_VDD5V_GT_VDDIO) != 0);
      default:
            break;
      }

      return 0;
}



////////////////////////////////////////////////////////////////////////////////
//!
//! \brief Report on the die temperature.
//!
//! \fntype Function
//!
//! This function reports on the die temperature.
//!
//! \param[out]  pLow   The low  end of the temperature range.
//! \param[out]  pHigh  The high end of the temperature range.
//!
////////////////////////////////////////////////////////////////////////////////
// Temperature constant
01394 #define TEMP_READING_ERROR_MARGIN 5
#define KELVIN_TO_CELSIUS_CONST 273

void ddi_power_GetDieTemp(int16_t * pLow, int16_t * pHigh)
{
      int16_t i16High, i16Low;
      uint16_t u16Reading;

      // Get the reading in Kelvins
      u16Reading = MeasureInternalDieTemperature();

      // Adjust for error margin
      i16High = u16Reading + TEMP_READING_ERROR_MARGIN;
      i16Low  = u16Reading - TEMP_READING_ERROR_MARGIN;

      // Convert to Celsius
      i16High -= KELVIN_TO_CELSIUS_CONST;
      i16Low  -= KELVIN_TO_CELSIUS_CONST;

//#ifdef CONFIG_POWER_SUPPLY_DEBUG
#if 0
      printk("Battery charger: Die temp %d to %d C\n", i16Low, i16High);
#endif
      // Return the results
      *pHigh = i16High;
      *pLow  = i16Low;
}

///////////////////////////////////////////////////////////////////////////////
//!
//! \brief Checks to see if the DCDC has been manually enabled
//!
//! \fntype Function
//!
//! \retval  true if DCDC is ON, false if DCDC is OFF.
//!
////////////////////////////////////////////////////////////////////////////////
01431 bool ddi_power_IsDcdcOn(void)
{
      return (__raw_readl(REGS_POWER_BASE + HW_POWER_5VCTRL) & BM_POWER_5VCTRL_ENABLE_DCDC) ? 1 : 0;
}


////////////////////////////////////////////////////////////////////////////////
//! See hw_power.h for details.
////////////////////////////////////////////////////////////////////////////////
01440 void ddi_power_SetPowerClkGate(bool bGate)
{
      // Gate/Ungate the clock to the power block
      if (bGate) {
            __raw_writel(BM_POWER_CTRL_CLKGATE,
                  REGS_POWER_BASE + HW_POWER_CTRL_SET);
      } else {
            __raw_writel(BM_POWER_CTRL_CLKGATE,
                  REGS_POWER_BASE + HW_POWER_CTRL_CLR);
      }
}

////////////////////////////////////////////////////////////////////////////////
//! See hw_power.h for details.
////////////////////////////////////////////////////////////////////////////////
01455 bool ddi_power_GetPowerClkGate(void)
{
      return (__raw_readl(REGS_POWER_BASE + HW_POWER_CTRL) & BM_POWER_CTRL_CLKGATE) ? 1 : 0;
}


enum ddi_power_5v_status ddi_power_GetPmu5vStatus(void)
{

      if (DetectionMethod == DDI_POWER_5V_VDD5V_GT_VDDIO) {

            if (__raw_readl(HW_POWER_CTRL_ADDR) &
                  BM_POWER_CTRL_POLARITY_VDD5V_GT_VDDIO) {
                  if ((__raw_readl(HW_POWER_CTRL_ADDR) &
                        BM_POWER_CTRL_VDD5V_GT_VDDIO_IRQ) ||
                        ddi_power_Get5vPresentFlag())
                        return new_5v_connection;
                  else
                        return existing_5v_disconnection;
            } else {
                  if ((__raw_readl(HW_POWER_CTRL_ADDR) &
                        BM_POWER_CTRL_VDD5V_GT_VDDIO_IRQ) ||
                        !ddi_power_Get5vPresentFlag() ||
                        ddi_power_Get5vDroopFlag())
                        return new_5v_disconnection;
                  else
                        return existing_5v_connection;
            }
      } else {

            if (__raw_readl(HW_POWER_CTRL_ADDR) &
                  BM_POWER_CTRL_POLARITY_VBUSVALID) {
                  if ((__raw_readl(HW_POWER_CTRL_ADDR) &
                        BM_POWER_CTRL_VBUSVALID_IRQ) ||
                        ddi_power_Get5vPresentFlag())
                        return new_5v_connection;
                  else
                        return existing_5v_disconnection;
            } else {
                  if ((__raw_readl(HW_POWER_CTRL_ADDR) &
                        BM_POWER_CTRL_VBUSVALID_IRQ) ||
                        !ddi_power_Get5vPresentFlag() ||
                        ddi_power_Get5vDroopFlag())
                        return new_5v_disconnection;
                  else
                        return existing_5v_connection;
            }

      }
}

void ddi_power_disable_5v_connection_irq(void)
{

      __raw_writel((BM_POWER_CTRL_ENIRQ_VBUS_VALID |
            BM_POWER_CTRL_ENIRQ_VDD5V_GT_VDDIO),
            HW_POWER_CTRL_CLR_ADDR);
}

void ddi_power_enable_5v_disconnect_detection(void)
{
      __raw_writel(BM_POWER_CTRL_POLARITY_VDD5V_GT_VDDIO |
            BM_POWER_CTRL_POLARITY_VBUSVALID,
            HW_POWER_CTRL_CLR_ADDR);

      __raw_writel(BM_POWER_CTRL_VDD5V_GT_VDDIO_IRQ |
            BM_POWER_CTRL_VBUSVALID_IRQ,
            HW_POWER_CTRL_CLR_ADDR);

      if (DetectionMethod == DDI_POWER_5V_VDD5V_GT_VDDIO) {
            __raw_writel(BM_POWER_CTRL_ENIRQ_VDD5V_GT_VDDIO,
                  HW_POWER_CTRL_SET_ADDR);
      } else {
            __raw_writel(BM_POWER_CTRL_ENIRQ_VBUS_VALID,
                  HW_POWER_CTRL_SET_ADDR);
      }
}

void ddi_power_enable_5v_connect_detection(void)
{
      __raw_writel(BM_POWER_CTRL_POLARITY_VDD5V_GT_VDDIO |
            BM_POWER_CTRL_POLARITY_VBUSVALID,
            HW_POWER_CTRL_SET_ADDR);

      __raw_writel(BM_POWER_CTRL_VDD5V_GT_VDDIO_IRQ |
            BM_POWER_CTRL_VBUSVALID_IRQ,
            HW_POWER_CTRL_CLR_ADDR);

      if (DetectionMethod == DDI_POWER_5V_VDD5V_GT_VDDIO) {
            __raw_writel(BM_POWER_CTRL_ENIRQ_VDD5V_GT_VDDIO,
                  HW_POWER_CTRL_SET_ADDR);
      } else {
            __raw_writel(BM_POWER_CTRL_ENIRQ_VBUS_VALID,
                  HW_POWER_CTRL_SET_ADDR);
      }
}

void ddi_power_EnableBatteryBoInterrupt(bool bEnable)
{
      if (bEnable) {

            __raw_writel(BM_POWER_CTRL_BATT_BO_IRQ,
                  HW_POWER_CTRL_CLR_ADDR);
            __raw_writel(BM_POWER_CTRL_ENIRQBATT_BO,
                  HW_POWER_CTRL_SET_ADDR);
            /* todo: make sure the battery brownout comparator
             * is enabled in HW_POWER_BATTMONITOR
             */
      } else {
            __raw_writel(BM_POWER_CTRL_ENIRQBATT_BO,
                  HW_POWER_CTRL_CLR_ADDR);
      }
}

void ddi_power_EnableDcdc4p2BoInterrupt(bool bEnable)
{
      if (bEnable) {

            __raw_writel(BM_POWER_CTRL_DCDC4P2_BO_IRQ,
                  HW_POWER_CTRL_CLR_ADDR);
            __raw_writel(BM_POWER_CTRL_ENIRQ_DCDC4P2_BO,
                  HW_POWER_CTRL_SET_ADDR);
      } else {
            __raw_writel(BM_POWER_CTRL_ENIRQ_DCDC4P2_BO,
                  HW_POWER_CTRL_CLR_ADDR);
      }
}

void ddi_power_EnableVdd5vDroopInterrupt(bool bEnable)
{
      if (bEnable) {

            __raw_writel(BM_POWER_CTRL_VDD5V_DROOP_IRQ,
                  HW_POWER_CTRL_CLR_ADDR);
            __raw_writel(BM_POWER_CTRL_ENIRQ_VDD5V_DROOP,
                  HW_POWER_CTRL_SET_ADDR);
      } else {
            __raw_writel(BM_POWER_CTRL_ENIRQ_VDD5V_DROOP,
                  HW_POWER_CTRL_CLR_ADDR);
      }
}


void ddi_power_Enable5vDisconnectShutdown(bool bEnable)
{
      if (bEnable) {
            __raw_writel(BM_POWER_5VCTRL_PWDN_5VBRNOUT,
                  HW_POWER_5VCTRL_SET_ADDR);
      } else {
            __raw_writel(BM_POWER_5VCTRL_PWDN_5VBRNOUT,
                  HW_POWER_5VCTRL_CLR_ADDR);
      }
}


void ddi_power_enable_5v_to_battery_xfer(bool bEnable)
{
      if (bEnable) {
            /* order matters */

            /* we can enable this in in vbus droop or 4p2 fiq handler
             * ddi_power_EnableBatteryBoInterrupt(true);
             */
            ddi_power_Enable5vDisconnectShutdown(false);
      } else {
            /* order matters */
            ddi_power_Enable5vDisconnectShutdown(true);
            ddi_power_EnableBatteryBoInterrupt(false);
      }
}


void ddi_power_init_4p2_protection(void)
{
      /* set vbus droop detection level to 4.3V */
      __raw_writel(BM_POWER_5VCTRL_VBUSDROOP_TRSH,
                  HW_POWER_5VCTRL_CLR_ADDR);

      /* VBUSDROOP THRESHOLD to 4.3V */
      __raw_writel(BM_POWER_5VCTRL_VBUSDROOP_TRSH,
            HW_POWER_5VCTRL_CLR_ADDR);

      ddi_power_EnableVbusDroopIrq();

      /* VBUSVALID THRESH = 2.9V */
      __raw_writel(BM_POWER_5VCTRL_VBUSVALID_TRSH,
            HW_POWER_5VCTRL_CLR_ADDR);

}

/* determine if all the bits are in a 'DCDC 4P2 Enabled' state. */
bool ddi_power_check_4p2_bits(void)
{


      uint32_t temp;

      temp = __raw_readl(HW_POWER_5VCTRL_ADDR) &
            BM_POWER_5VCTRL_PWD_CHARGE_4P2;

      /* if PWD_CHARGE_4P2 = 1, 4p2 is disabled */
      if (temp)
            return false;

      temp = __raw_readl(HW_POWER_DCDC4P2_ADDR) &
            BM_POWER_DCDC4P2_ENABLE_DCDC;

      if (!temp)
            return false;

      temp = __raw_readl(HW_POWER_DCDC4P2_ADDR) &
                  BM_POWER_DCDC4P2_ENABLE_4P2;

      if (temp)
            return true;
      else
            return false;

}

uint16_t ddi_power_set_4p2_ilimit(uint16_t ilimit)
{
      uint32_t temp_reg;

      if (ilimit > 780)
            ilimit = 780;
      temp_reg = __raw_readl(HW_POWER_5VCTRL_ADDR);
      temp_reg &= (~BM_POWER_5VCTRL_CHARGE_4P2_ILIMIT);
      temp_reg |= BF_POWER_5VCTRL_CHARGE_4P2_ILIMIT(
            ddi_power_convert_current_to_setting(
                        ilimit));
      __raw_writel(temp_reg, HW_POWER_5VCTRL_ADDR);

      return ilimit;
}

void ddi_power_shutdown(void)
{
      __raw_writel(0x3e770001, HW_POWER_RESET_ADDR);
}

void ddi_power_handle_dcdc4p2_bo(void)
{
      ddi_power_EnableBatteryBoInterrupt(true);
      ddi_power_EnableDcdc4p2BoInterrupt(false);
}

void ddi_power_enable_vddio_interrupt(bool enable)
{
      if (enable) {
            __raw_writel(BM_POWER_CTRL_VDDIO_BO_IRQ,
                  HW_POWER_CTRL_CLR_ADDR);
#ifndef DISABLE_VDDIO_BO_PROTECTION
            __raw_writel(BM_POWER_CTRL_ENIRQ_VDDIO_BO,
                  HW_POWER_CTRL_SET_ADDR);
#endif
      } else {
            __raw_writel(BM_POWER_CTRL_ENIRQ_VDDIO_BO,
                  HW_POWER_CTRL_CLR_ADDR);
      }
}

void ddi_power_handle_vddio_brnout(void)
{
      if (ddi_power_GetPmu5vStatus() == new_5v_connection) {
            ddi_power_enable_vddio_interrupt(false);
      } else {
#ifdef DEBUG_IRQS
            ddi_power_enable_vddio_interrupt(false);
            printk(KERN_ALERT "VDDIO BO TRIED TO SHUTDOWN!!!\n");
            return;
#else
            ddi_power_shutdown();
#endif
      }
}

void ddi_power_handle_vdd5v_droop(void)
{
      uint32_t temp;

      /* handle errata */
      temp = __raw_readl(HW_POWER_DCDC4P2_ADDR);
      temp |= (BF(31, POWER_DCDC4P2_CMPTRIP) | BM_POWER_DCDC4P2_TRG);
      __raw_writel(temp, HW_POWER_DCDC4P2_ADDR);


      /* if battery is below brownout level, shutdown asap */
      if (__raw_readl(HW_POWER_STS_ADDR) & BM_POWER_STS_BATT_BO)
            ddi_power_shutdown();

      /* due to 5v connect vddio bo chip bug, we need to
       * disable vddio interrupts until we reset the 5v
       * detection for 5v connect detect.  We want to allow
       * some debounce time before enabling connect detection.
       */
      ddi_power_enable_vddio_interrupt(false);

      ddi_power_EnableBatteryBoInterrupt(true);
      ddi_power_EnableDcdc4p2BoInterrupt(false);
      ddi_power_EnableVdd5vDroopInterrupt(false);

}

void ddi_power_InitOutputBrownouts(void)
{
      uint32_t temp;

      __raw_writel(BM_POWER_CTRL_VDDD_BO_IRQ |
            BM_POWER_CTRL_VDDA_BO_IRQ |
            BM_POWER_CTRL_VDDIO_BO_IRQ,
            HW_POWER_CTRL_CLR_ADDR);

      __raw_writel(BM_POWER_CTRL_ENIRQ_VDDD_BO |
            BM_POWER_CTRL_ENIRQ_VDDA_BO |
            BM_POWER_CTRL_ENIRQ_VDDIO_BO,
            HW_POWER_CTRL_SET_ADDR);

      temp = __raw_readl(HW_POWER_VDDDCTRL_ADDR);
      temp &= ~BM_POWER_VDDDCTRL_PWDN_BRNOUT;
      __raw_writel(temp, HW_POWER_VDDDCTRL_ADDR);

      temp = __raw_readl(HW_POWER_VDDACTRL_ADDR);
      temp &= ~BM_POWER_VDDACTRL_PWDN_BRNOUT;
      __raw_writel(temp, HW_POWER_VDDACTRL_ADDR);

      temp = __raw_readl(HW_POWER_VDDIOCTRL_ADDR);
      temp &= ~BM_POWER_VDDIOCTRL_PWDN_BRNOUT;
      __raw_writel(temp, HW_POWER_VDDIOCTRL_ADDR);
}

/* used for debugging purposes only */
void ddi_power_disable_power_interrupts(void)
{
      __raw_writel(BM_POWER_CTRL_ENIRQ_DCDC4P2_BO |
            BM_POWER_CTRL_ENIRQ_VDD5V_DROOP |
            BM_POWER_CTRL_ENIRQ_PSWITCH |
            BM_POWER_CTRL_ENIRQ_DC_OK |
            BM_POWER_CTRL_ENIRQBATT_BO |
            BM_POWER_CTRL_ENIRQ_VDDIO_BO |
            BM_POWER_CTRL_ENIRQ_VDDA_BO |
            BM_POWER_CTRL_ENIRQ_VDDD_BO |
            BM_POWER_CTRL_ENIRQ_VBUS_VALID |
            BM_POWER_CTRL_ENIRQ_VDD5V_GT_VDDIO,
            HW_POWER_CTRL_CLR_ADDR);

}

bool ddi_power_Get5vDroopFlag(void)
{
      if (__raw_readl(HW_POWER_STS_ADDR) &
            BM_POWER_STS_VDD5V_DROOP)
            return true;
      else
            return false;
}

////////////////////////////////////////////////////////////////////////////////
// End of file
////////////////////////////////////////////////////////////////////////////////
//! @}

Generated by  Doxygen 1.6.0   Back to index