summaryrefslogtreecommitdiff
path: root/arch/arm/mach-tegra/odm_kit/adaptations/pmu/pcf50626/pcf50626.c
diff options
context:
space:
mode:
Diffstat (limited to 'arch/arm/mach-tegra/odm_kit/adaptations/pmu/pcf50626/pcf50626.c')
-rw-r--r--arch/arm/mach-tegra/odm_kit/adaptations/pmu/pcf50626/pcf50626.c938
1 files changed, 938 insertions, 0 deletions
diff --git a/arch/arm/mach-tegra/odm_kit/adaptations/pmu/pcf50626/pcf50626.c b/arch/arm/mach-tegra/odm_kit/adaptations/pmu/pcf50626/pcf50626.c
new file mode 100644
index 000000000000..9272dbdddbc5
--- /dev/null
+++ b/arch/arm/mach-tegra/odm_kit/adaptations/pmu/pcf50626/pcf50626.c
@@ -0,0 +1,938 @@
+/*
+ * Copyright (c) 2009 NVIDIA Corporation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ *
+ * Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * Neither the name of the NVIDIA Corporation nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+#include "nvodm_query_discovery.h"
+#include "nvodm_query.h"
+#include "nvodm_services.h"
+#include "pcf50626_i2c.h"
+#include "pcf50626.h"
+#include "pcf50626_batterycharger.h"
+#include "pcf50626_adc.h"
+#include "pcf50626_interrupt.h"
+#include "pcf50626_supply_info_table.h"
+
+#ifndef PMU_MAX
+#define PMU_MAX(a,b) ((a)<(b)?(b):(a))
+#endif
+
+#define BATTEMP_CONTROL (0)
+
+// Board IDs
+#define NVODM_PMU_BOARD_ID_E924 0x0918
+#define NVODM_PMU_BOARD_ID_E934 0x0922
+
+// SKUs
+#define NVODM_PMU_BOARD_SAMSUNG_26_MHZ_OSC 0x0A00
+#define NVODM_PMU_BOARD_HYNIX_12_MHZ_XTAL 0x0A01
+
+// h/w configuration
+#define CHARGER_CONSTANT_CURRENT_SET_MA (NvU32)(125000/127)
+#define MAX_CHARGER_LIMIT_MA 850
+
+// This PMU does not have differnet charger programming
+// So setting all types of charger limit to the default charger limit
+#define SE0_TYPE_CHARGER_LIMIT_MA MAX_CHARGER_LIMIT_MA
+#define SE1_TYPE_CHARGER_LIMIT_MA MAX_CHARGER_LIMIT_MA
+#define SJ_TYPE_CHARGER_LIMIT_MA MAX_CHARGER_LIMIT_MA
+#define SK_TYPE_CHARGER_LIMIT_MA MAX_CHARGER_LIMIT_MA
+
+
+// threshold for battery status. need to fine tune based on battery/system characterisation
+#define NVODM_BATTERY_FULL_VOLTAGE_MV 4150
+#define NVODM_BATTERY_HIGH_VOLTAGE_MV 3900
+#define NVODM_BATTERY_LOW_VOLTAGE_MV 3300
+#define NVODM_BATTERY_CRITICAL_VOLTAGE_MV 3100
+
+#define NVODM_BATTERY_OVERHEAT_THRESHOLD 70
+
+
+Pcf50626PrivData *pPrivData;
+//Concorde WAR for the USB Host mode
+NvBool UsbHostMode;
+
+#define PMUGUID NV_ODM_GUID('p','c','f','_','p','m','u','0')
+
+
+// Calulate the battery life percentage according to the battery voltage.
+static NvU32
+Pcf50626CalulateBatteryLifePercent_int(NvU32 vBatSense);
+
+#if BATTEMP_CONTROL
+// switch off the chargeer if the battery temperature is too high
+static NvBool
+Pcf50626BatteryTemperatureControl_int(
+ NvOdmPmuDeviceHandle hDevice,
+ NvU32 batTemp);
+#endif
+
+// Read the voltage setting from PCF50626 registers
+static NvBool
+Pcf50626ReadVoltageReg(
+ NvOdmPmuDeviceHandle hDevice,
+ NvU32 vddRail,
+ NvU32* pMilliVolts);
+
+// Write the voltage setting from PCF50626 registers
+static NvBool
+Pcf50626WriteVoltageReg(
+ NvOdmPmuDeviceHandle hDevice,
+ NvU32 vddRail,
+ NvU32 MilliVolts,
+ NvU32* pSettleMicroSeconds);
+
+
+void
+Pcf50626GetCapabilities(
+ NvU32 vddRail,
+ NvOdmPmuVddRailCapabilities* pCapabilities)
+{
+ NvOdmBoardInfo BoardInfo;
+ NvBool Status = NV_FALSE;
+
+ NV_ASSERT(pCapabilities);
+ NV_ASSERT(vddRail < PCF50626PmuSupply_Num);
+
+ *pCapabilities = pcf50626SupplyInfoTable[vddRail].cap;
+
+ if (vddRail == PCF50626PmuSupply_DCD2)
+ {
+ Status = NvOdmPeripheralGetBoardInfo(NVODM_PMU_BOARD_ID_E924, &BoardInfo);
+ if (Status == NV_TRUE)
+ {
+ if ((BoardInfo.SKU == NVODM_PMU_BOARD_SAMSUNG_26_MHZ_OSC) ||
+ (BoardInfo.SKU == NVODM_PMU_BOARD_HYNIX_12_MHZ_XTAL))
+ {
+ // Use 1.8v DDR (TO DO: Don't use a magic number here; define this.)
+ pCapabilities->requestMilliVolts = 1800;
+ }
+ }
+ else
+ {
+ Status = NvOdmPeripheralGetBoardInfo(NVODM_PMU_BOARD_ID_E934, &BoardInfo);
+ if (Status == NV_TRUE)
+ {
+ if (BoardInfo.SKU == NVODM_PMU_BOARD_HYNIX_12_MHZ_XTAL)
+ {
+ // Use 1.8v DDR (TO DO: Don't use a magic number here; define this.)
+ pCapabilities->requestMilliVolts = 1800;
+ }
+ }
+ else
+ {
+ // Use default DDR voltage (1.925v)
+ ;
+ }
+ }
+ }
+}
+
+
+NvBool Pcf50626Setup(NvOdmPmuDeviceHandle hDevice)
+{
+ NvOdmIoModule I2cModule = NvOdmIoModule_I2c;
+ NvU32 I2cInstance = 0;
+ NvU32 I2cAddress = 0;
+ NvU32 i = 0;
+ NvBool status = NV_FALSE;
+
+ const NvOdmPeripheralConnectivity *pConnectivity =
+ NvOdmPeripheralGetGuid(PMUGUID);
+
+ NV_ASSERT(hDevice);
+
+
+ pPrivData = (Pcf50626PrivData*) NvOdmOsAlloc(sizeof(Pcf50626PrivData));
+ if (pPrivData == NULL)
+ {
+ NVODMPMU_PRINTF(("Error Allocating Pcf50626PrivData. \n"));
+ return NV_FALSE;
+ }
+ NvOdmOsMemset(pPrivData, 0, sizeof(Pcf50626PrivData));
+ hDevice->pPrivate = pPrivData;
+
+ ((Pcf50626PrivData*)hDevice->pPrivate)->supplyRefCntTable = NvOdmOsAlloc(sizeof(NvU32) * PCF50626PmuSupply_Num);
+ if (((Pcf50626PrivData*)hDevice->pPrivate)->supplyRefCntTable == NULL)
+ {
+ NVODMPMU_PRINTF(("Error Allocating RefCntTable. \n"));
+ goto fail;
+ }
+
+ // memset
+ for (i = 0; i < PCF50626PmuSupply_Num; i++)
+ {
+ ((Pcf50626PrivData*)hDevice->pPrivate)->supplyRefCntTable[i] = 0;
+ }
+
+
+ if (pConnectivity != NULL) // PMU is in database
+ {
+ for (i = 0; i < pConnectivity->NumAddress; i ++)
+ {
+ if (pConnectivity->AddressList[i].Interface == NvOdmIoModule_I2c_Pmu)
+ {
+ I2cModule = NvOdmIoModule_I2c_Pmu;
+ I2cInstance = pConnectivity->AddressList[i].Instance;
+ I2cAddress = pConnectivity->AddressList[i].Address;
+ break;
+ }
+ }
+
+ NV_ASSERT(I2cModule == NvOdmIoModule_I2c_Pmu);
+ NV_ASSERT(I2cAddress != 0);
+
+ ((Pcf50626PrivData*)hDevice->pPrivate)->hOdmI2C = NvOdmI2cOpen(I2cModule, I2cInstance);
+ if (!((Pcf50626PrivData*)hDevice->pPrivate)->hOdmI2C)
+ {
+ NVODMPMU_PRINTF(("[NVODM PMU]Pcf50626Setup: Error Open I2C device. \n"));
+ NVODMPMU_PRINTF(("[NVODM PMU]Please check PMU device I2C settings. \n"));
+ goto fail;
+ }
+
+ ((Pcf50626PrivData*)hDevice->pPrivate)->DeviceAddr = I2cAddress;
+ ((Pcf50626PrivData*)hDevice->pPrivate)->hOdmPmuSevice = NvOdmServicesPmuOpen();
+ if (!((Pcf50626PrivData*)hDevice->pPrivate)->hOdmPmuSevice)
+ {
+ NVODMPMU_PRINTF(("[NVODM PMU]Pcf50626Setup: Error Open PMU Odm service. \n"));
+ goto fail;
+ }
+ }
+ else
+ {
+ // if PMU is not presented in the database, then the platform is PMU-less.
+ NVODMPMU_PRINTF(("[NVODM PMU]Pcf50626Setup: The system did not doscover PMU fromthe data base. \n"));
+ NVODMPMU_PRINTF(("[NVODM PMU]Pcf50626Setup: If this is not intended, please check the peripheral database for PMU settings. \n"));
+ goto fail;
+ }
+
+ if (!Pcf50626BatteryChargerSetup(hDevice))
+ {
+ NVODMPMU_PRINTF(("[NVODM PMU]Pcf50626Setup: Pcf50626BatteryChargerSetup() failed. \n"));
+ goto fail;
+ }
+
+ //Check battery presence
+ if (!Pcf50626BatteryChargerCBCMainBatt(hDevice,&((Pcf50626PrivData*)hDevice->pPrivate)->battPresence))
+ {
+ NVODMPMU_PRINTF(("[NVODM PMU]Pcf50626Setup: Pcf50626BatteryChargerCBCMainBatt() failed. \n"));
+ goto fail;
+ }
+
+ // The interrupt assumes not supported until pcf50626InterruptHandler() is called.
+ ((Pcf50626PrivData*)hDevice->pPrivate)->pmuInterruptSupported = NV_FALSE;
+
+ // setup the interrupt any way.
+ if (!Pcf50626SetupInterrupt(hDevice, &((Pcf50626PrivData*)hDevice->pPrivate)->pmuStatus))
+ {
+ NVODMPMU_PRINTF(("[NVODM PMU]Pcf50626Setup: Pcf50626SetupInterrupt() failed. \n"));
+ goto fail;
+ }
+
+ // Check battery Fullness
+ if (((Pcf50626PrivData*)hDevice->pPrivate)->battPresence == NV_TRUE)
+ {
+ if (!Pcf50626BatteryChargerCBCBattFul(hDevice,&status))
+ {
+ NVODMPMU_PRINTF(("[NVODM PMU]Pcf50626Setup: Pcf50626BatteryChargerCBCBattFul() failed. \n"));
+ goto fail;
+ }
+
+ ((Pcf50626PrivData*)hDevice->pPrivate)->pmuStatus.batFull = status;
+ }
+ else
+ {
+ ((Pcf50626PrivData*)hDevice->pPrivate)->pmuStatus.batFull = NV_FALSE;
+ }
+
+ return NV_TRUE;
+
+fail:
+ Pcf50626Release(hDevice);
+ return NV_FALSE;
+
+
+}
+
+void Pcf50626Release(NvOdmPmuDeviceHandle hDevice)
+{
+ if (hDevice->pPrivate != NULL)
+ {
+ if (((Pcf50626PrivData*)hDevice->pPrivate)->hOdmPmuSevice != NULL)
+ {
+ NvOdmServicesPmuClose(((Pcf50626PrivData*)hDevice->pPrivate)->hOdmPmuSevice);
+ ((Pcf50626PrivData*)hDevice->pPrivate)->hOdmPmuSevice = NULL;
+ }
+
+ if (((Pcf50626PrivData*)hDevice->pPrivate)->hOdmI2C != NULL)
+ {
+ NvOdmI2cClose(((Pcf50626PrivData*)hDevice->pPrivate)->hOdmI2C);
+ ((Pcf50626PrivData*)hDevice->pPrivate)->hOdmI2C = NULL;
+ }
+
+ if (((Pcf50626PrivData*)hDevice->pPrivate)->supplyRefCntTable != NULL)
+ {
+ NvOdmOsFree(((Pcf50626PrivData*)hDevice->pPrivate)->supplyRefCntTable);
+ ((Pcf50626PrivData*)hDevice->pPrivate)->supplyRefCntTable = NULL;
+ }
+
+ NvOdmOsFree(hDevice->pPrivate);
+ hDevice->pPrivate = NULL;
+ }
+}
+
+
+NvBool
+Pcf50626GetVoltage(
+ NvOdmPmuDeviceHandle hDevice,
+ NvU32 vddRail,
+ NvU32* pMilliVolts)
+{
+ NV_ASSERT(hDevice);
+ NV_ASSERT(pMilliVolts);
+ NV_ASSERT(vddRail < PCF50626PmuSupply_Num);
+
+ if(! Pcf50626ReadVoltageReg(hDevice, vddRail,pMilliVolts))
+ return NV_FALSE;
+
+ return NV_TRUE;
+}
+
+
+NvBool
+Pcf50626SetVoltage(
+ NvOdmPmuDeviceHandle hDevice,
+ NvU32 vddRail,
+ NvU32 MilliVolts,
+ NvU32* pSettleMicroSeconds)
+{
+ NvU8 data = 0;
+
+ NV_ASSERT(hDevice);
+ NV_ASSERT(vddRail < PCF50626PmuSupply_Num);
+
+ if (pcf50626SupplyInfoTable[vddRail].cap.OdmProtected == NV_TRUE)
+ {
+ NVODMPMU_PRINTF(("[NVODM PMU] Pcf50626SetVoltage Warning: The voltage is protected and can not be set: %d.\n", vddRail));
+ return NV_TRUE;
+ }
+
+ if ((MilliVolts == ODM_VOLTAGE_OFF) ||
+ ((MilliVolts <= pcf50626SupplyInfoTable[vddRail].cap.MaxMilliVolts)
+ && (MilliVolts >= pcf50626SupplyInfoTable[vddRail].cap.MinMilliVolts)))
+ {
+ if (! Pcf50626WriteVoltageReg(hDevice, vddRail, MilliVolts, pSettleMicroSeconds))
+ return NV_FALSE;
+ }
+ else
+ {
+ NVODMPMU_PRINTF(("[NVODM OPMU] Pcf50626SetVoltage Error: The required voltage is not supported..\n"));
+ return NV_FALSE;
+ }
+
+ if (vddRail == PCF50626PmuSupply_DCUD)
+ {
+ // VBUs rail is enabled bydefault, so no need to enable set voltage.
+ // "Millivolts" field is used as Enable or disable VBUS GPIO
+ if (MilliVolts == ODM_VOLTAGE_OFF)
+ {
+ data = 0x7; // all bits to low fixed 0
+ }
+ else
+ {
+ data = 0x0; // default reset value high impedence state
+ }
+ if (!Pcf50626I2cWrite8(hDevice,PCF50626_GPIO5C1_ADDR, data))
+ return NV_FALSE;
+ }
+ return NV_TRUE;
+}
+
+static NvBool
+Pcf50626ReadVoltageReg(
+ NvOdmPmuDeviceHandle hDevice,
+ NvU32 vddRail,
+ NvU32* pMilliVolts)
+{
+ NvU32 milliVolts = 0;
+ NvU8 data = 0;
+ const PCF50626PmuSupplyInfo *pSupplyInfo = &pcf50626SupplyInfoTable[vddRail];
+
+ NV_ASSERT(pSupplyInfo->supply == (PCF50626PmuSupply)vddRail);
+
+ if(! Pcf50626I2cRead8(hDevice, pSupplyInfo->control2Addr, &data))
+ return NV_FALSE;
+
+ data >>= PCF50626_C2_OPMOD_SHIFT;
+ if (!data) //OFF
+ milliVolts = 0;
+ else
+ {
+ if (!Pcf50626I2cRead8(hDevice, pSupplyInfo->control1Addr, &data))
+ return NV_FALSE;
+
+ if ( (vddRail == PCF50626PmuSupply_DCD1)
+ |(vddRail == PCF50626PmuSupply_DCD2)
+ |(vddRail == PCF50626PmuSupply_DCUD))
+ {
+ milliVolts = pSupplyInfo->offsetVoltage + pSupplyInfo->cap.StepMilliVolts * ((NvU32)(data & 0x7F));
+ }
+ else if (vddRail == PCF50626PmuSupply_LCREG)
+ {
+ milliVolts = pSupplyInfo->offsetVoltage + pSupplyInfo->cap.StepMilliVolts * ((NvU32)(data & 0x7F) >> 1);
+ }
+ else
+ {
+ milliVolts = pSupplyInfo->offsetVoltage + pSupplyInfo->cap.StepMilliVolts * ((NvU32)(data & 0x7F) >> 2);
+ }
+ }
+
+ *pMilliVolts = milliVolts;
+ return NV_TRUE;
+}
+
+
+static NvBool
+Pcf50626WriteVoltageReg(
+ NvOdmPmuDeviceHandle hDevice,
+ NvU32 vddRail,
+ NvU32 MilliVolts,
+ NvU32* pSettleMicroSeconds)
+{
+ NvU8 data = 0;
+ NvU8 reg = 0;
+ NvU32 settleTime = 0;
+
+ const PCF50626PmuSupplyInfo* pSupplyInfo = &pcf50626SupplyInfoTable[vddRail];
+ const PCF50626PmuSupplyInfo* pSupplyInputInfo = &pcf50626SupplyInfoTable[pSupplyInfo->supplyInput];
+
+ NV_ASSERT(pSupplyInfo->supply == (PCF50626PmuSupply)vddRail);
+
+ // Require to turn off the supply
+ if (MilliVolts == ODM_VOLTAGE_OFF)
+ {
+ // check if the supply can be turned off
+ if (((Pcf50626PrivData*)hDevice->pPrivate)->supplyRefCntTable[pSupplyInfo->supply] == 1)
+ {
+ // turn off the supply
+ data = PCF50626_C2_OPMOD_OFF;
+ NvOdmServicesPmuSetSocRailPowerState(
+ ((Pcf50626PrivData*)hDevice->pPrivate)->hOdmPmuSevice, pSupplyInfo->supply, NV_FALSE);
+ if (!Pcf50626I2cWrite8(hDevice, pSupplyInfo->control2Addr, data))
+ return NV_FALSE;
+ }
+
+ //check if the supply input can be turned off
+ if (((Pcf50626PrivData*)hDevice->pPrivate)->supplyRefCntTable[pSupplyInputInfo->supply] == 1)
+ {
+ // turn off the supply input
+ data = PCF50626_C2_OPMOD_OFF;
+ NvOdmServicesPmuSetSocRailPowerState(
+ ((Pcf50626PrivData*)hDevice->pPrivate)->hOdmPmuSevice, pSupplyInputInfo->supply, NV_FALSE);
+ if(! Pcf50626I2cWrite8(hDevice, pSupplyInputInfo->control2Addr, data))
+ return NV_FALSE;
+ }
+
+ if (((Pcf50626PrivData*)hDevice->pPrivate)->supplyRefCntTable[pSupplyInfo->supply] != 0)
+ ((Pcf50626PrivData*)hDevice->pPrivate)->supplyRefCntTable[pSupplyInfo->supply] --;
+ if (((Pcf50626PrivData*)hDevice->pPrivate)->supplyRefCntTable[pSupplyInputInfo->supply] != 0)
+ ((Pcf50626PrivData*)hDevice->pPrivate)->supplyRefCntTable[pSupplyInputInfo->supply] --;
+
+ settleTime = PMU_MAX (pSupplyInfo->switchTimeMicroSec, pSupplyInputInfo->switchTimeMicroSec);
+
+ if (pSettleMicroSeconds != NULL)
+ *pSettleMicroSeconds = settleTime;
+ else
+ NvOdmOsWaitUS(settleTime);
+ return NV_TRUE;
+ }
+
+ // set voltage
+ if ( (vddRail == PCF50626PmuSupply_HCREG) ||
+ ((vddRail == PCF50626PmuSupply_LCREG) &&
+ (MilliVolts > PCF50626_LCREGOUT_VOLTAGE_RESCHANGE_MV)))
+ {
+ data = (NvU8)((MilliVolts - pSupplyInfo->offsetVoltage) / pSupplyInfo->cap.StepMilliVolts);
+ if (data % 2)
+ data --;
+ }
+ else
+ {
+ data = (NvU8)((MilliVolts - pSupplyInfo->offsetVoltage) / pSupplyInfo->cap.StepMilliVolts);
+ }
+
+ reg = 0;
+ reg &= ~PCF50626_C1_OUTPUT_MASK;
+ if ( (pSupplyInfo->supply == PCF50626PmuSupply_DCD1)
+ |(pSupplyInfo->supply == PCF50626PmuSupply_DCD2)
+ |(pSupplyInfo->supply == PCF50626PmuSupply_DCUD))
+ {
+ reg |= data;
+ }
+ else if (pSupplyInfo->supply == PCF50626PmuSupply_LCREG)
+ {
+ reg |= (data << 1);
+ }
+ else
+ {
+ reg |= (data << 2);
+ }
+
+ if(! Pcf50626I2cWrite8(hDevice, pSupplyInfo->control1Addr, reg))
+ return NV_FALSE;
+
+ settleTime = pSupplyInfo->switchTimeMicroSec;
+
+ // turn on supply
+ if (((Pcf50626PrivData*)hDevice->pPrivate)->supplyRefCntTable[pSupplyInfo->supply] == 0)
+ {
+ if (! Pcf50626I2cRead8(hDevice, pSupplyInfo->control2Addr, &data))
+ return NV_FALSE;
+ data >>= PCF50626_C2_OPMOD_SHIFT;
+ if (!data)
+ {
+ // Require to turn on the supply
+ data = PCF50626_C2_OPMOD_ON;
+ NvOdmServicesPmuSetSocRailPowerState(
+ ((Pcf50626PrivData*)hDevice->pPrivate)->hOdmPmuSevice, pSupplyInfo->supply, NV_TRUE);
+ if(! Pcf50626I2cWrite8(hDevice, pSupplyInfo->control2Addr, data))
+ return NV_FALSE;
+
+ settleTime += pSupplyInfo->turnOnTimeMicroSec;
+ }
+ }
+
+ // turn on supply input if necessary
+ if (((Pcf50626PrivData*)hDevice->pPrivate)->supplyRefCntTable[pSupplyInputInfo->supply] == 0)
+ {
+ if(! Pcf50626I2cRead8(hDevice, pSupplyInputInfo->control2Addr, &data))
+ return NV_FALSE;
+
+ data >>= PCF50626_C2_OPMOD_SHIFT;
+ if (!data)
+ {
+ // Require to turn on the supply input
+ data = PCF50626_C2_OPMOD_ON;
+ NvOdmServicesPmuSetSocRailPowerState(
+ ((Pcf50626PrivData*)hDevice->pPrivate)->hOdmPmuSevice, pSupplyInputInfo->supply, NV_TRUE);
+ if(! Pcf50626I2cWrite8(hDevice,pSupplyInputInfo->control2Addr, data))
+ return NV_FALSE;
+
+ settleTime += pSupplyInputInfo->turnOnTimeMicroSec;
+ }
+ }
+
+ ((Pcf50626PrivData*)hDevice->pPrivate)->supplyRefCntTable[pSupplyInputInfo->supply] ++;
+ ((Pcf50626PrivData*)hDevice->pPrivate)->supplyRefCntTable[pSupplyInfo->supply] ++;
+
+
+ if (pSettleMicroSeconds != NULL)
+ *pSettleMicroSeconds = settleTime;
+ else
+ NvOdmOsWaitUS(settleTime);
+
+ return NV_TRUE;
+}
+
+NvBool
+Pcf50626GetAcLineStatus(
+ NvOdmPmuDeviceHandle hDevice,
+ NvOdmPmuAcLineStatus *pStatus)
+{
+ NvBool acLineStatus = NV_FALSE;
+
+ NV_ASSERT(hDevice);
+ NV_ASSERT(pStatus);
+
+ // check if charger presents
+ if (((Pcf50626PrivData*)hDevice->pPrivate)->battPresence == NV_FALSE)
+ {
+ *pStatus = NvOdmPmuAcLine_Online;
+ return NV_TRUE;
+ }
+
+ if (((Pcf50626PrivData*)hDevice->pPrivate)->pmuInterruptSupported == NV_TRUE)
+ {
+ if (( ((Pcf50626PrivData*)hDevice->pPrivate)->pmuStatus.mChgPresent == NV_TRUE ) &&
+ (UsbHostMode == NV_FALSE))
+ {
+ *pStatus = NvOdmPmuAcLine_Online;
+ acLineStatus = NV_TRUE;
+ }
+ else
+ {
+ *pStatus = NvOdmPmuAcLine_Offline;
+ acLineStatus = NV_FALSE;
+ }
+ }
+ else
+ {
+ // battery is present, now check if charger presents
+ if (!Pcf50626BatteryChargerMainChgPresent(hDevice, &acLineStatus))
+ {
+ NVODMPMU_PRINTF(("[NVODM PMU] Pcf50626GetAcLineStatus: Error in checking main charger presence.\n"));
+ return NV_FALSE;
+ }
+
+ if ((acLineStatus == NV_TRUE) && (UsbHostMode == NV_FALSE))
+ *pStatus = NvOdmPmuAcLine_Online;
+ else
+ *pStatus = NvOdmPmuAcLine_Offline;
+ }
+ return NV_TRUE;
+}
+
+
+NvBool
+Pcf50626GetBatteryStatus(
+ NvOdmPmuDeviceHandle hDevice,
+ NvOdmPmuBatteryInstance batteryInst,
+ NvU8 *pStatus)
+{
+ NvU8 status = 0;
+
+ NV_ASSERT(hDevice);
+ NV_ASSERT(pStatus);
+ NV_ASSERT(batteryInst <= NvOdmPmuBatteryInst_Num);
+
+ if (batteryInst == NvOdmPmuBatteryInst_Main)
+ {
+ if (((Pcf50626PrivData*)hDevice->pPrivate)->battPresence == NV_TRUE)
+ {
+ NvOdmPmuAcLineStatus stat = NvOdmPmuAcLine_Offline;
+ NvU32 VBatSense = 0;
+ if (!Pcf50626GetAcLineStatus(hDevice, &stat))
+ return NV_FALSE;
+
+ if (stat == NvOdmPmuAcLine_Online)
+ {
+ if (((Pcf50626PrivData*)hDevice->pPrivate)->pmuInterruptSupported == NV_TRUE)
+ {
+ if (((Pcf50626PrivData*)hDevice->pPrivate)->pmuStatus.batFull == NV_FALSE)
+ status = NVODM_BATTERY_STATUS_CHARGING;
+ }
+ else
+ {
+ NvBool batFull = NV_FALSE;
+ if (!Pcf50626BatteryChargerCBCBattFul(hDevice, &batFull))
+ return NV_FALSE;
+ if (batFull == NV_FALSE)
+ status = NVODM_BATTERY_STATUS_CHARGING;
+ }
+ }
+
+ // Get VBatSense
+ if (!Pcf50626AdcVBatSenseRead(hDevice, &VBatSense))
+ return NV_FALSE;
+
+ if (VBatSense > NVODM_BATTERY_HIGH_VOLTAGE_MV)
+ status |= NVODM_BATTERY_STATUS_HIGH;
+ else if ((VBatSense < NVODM_BATTERY_LOW_VOLTAGE_MV)&&
+ (VBatSense > NVODM_BATTERY_CRITICAL_VOLTAGE_MV))
+ status |= NVODM_BATTERY_STATUS_LOW;
+ else if (VBatSense <= NVODM_BATTERY_CRITICAL_VOLTAGE_MV)
+ status |= NVODM_BATTERY_STATUS_CRITICAL;
+
+ }
+ else
+ {
+ /* Battery is actually not present */
+ status = NVODM_BATTERY_STATUS_NO_BATTERY;
+ }
+
+ *pStatus = status;
+ }
+
+ else
+ {
+ *pStatus = NVODM_BATTERY_STATUS_UNKNOWN;
+ }
+
+ return NV_TRUE;
+}
+
+NvBool
+Pcf50626GetBatteryData(
+ NvOdmPmuDeviceHandle hDevice,
+ NvOdmPmuBatteryInstance batteryInst,
+ NvOdmPmuBatteryData *pData)
+{
+ NvOdmPmuBatteryData batteryData;
+ batteryData.batteryAverageCurrent = NVODM_BATTERY_DATA_UNKNOWN;
+ batteryData.batteryAverageInterval = NVODM_BATTERY_DATA_UNKNOWN;
+ batteryData.batteryCurrent = NVODM_BATTERY_DATA_UNKNOWN;
+ batteryData.batteryLifePercent = NVODM_BATTERY_DATA_UNKNOWN;
+ batteryData.batteryLifeTime = NVODM_BATTERY_DATA_UNKNOWN;
+ batteryData.batteryMahConsumed = NVODM_BATTERY_DATA_UNKNOWN;
+ batteryData.batteryTemperature = NVODM_BATTERY_DATA_UNKNOWN;
+ batteryData.batteryVoltage = NVODM_BATTERY_DATA_UNKNOWN;
+
+
+ NV_ASSERT(hDevice);
+ NV_ASSERT(pData);
+ NV_ASSERT(batteryInst <= NvOdmPmuBatteryInst_Num);
+
+
+ if (batteryInst == NvOdmPmuBatteryInst_Main)
+ {
+ NvU32 VBatSense = 0;
+ NvU32 VBatTemp = 0;
+
+ if (((Pcf50626PrivData*)hDevice->pPrivate)->battPresence == NV_TRUE)
+ {
+ /* retrieve Battery voltage and temperature */
+
+ // Get VBatSense
+ if (!Pcf50626AdcVBatSenseRead(hDevice, &VBatSense))
+ {
+ NVODMPMU_PRINTF(("[NVODM PMU] Pcf50626GetBatteryData: Error reading VBATSense. \n"));
+ return NV_FALSE;
+ }
+
+ // Get VBatTemp
+ if (!Pcf50626AdcVBatTempRead(hDevice, &VBatTemp))
+ {
+ NVODMPMU_PRINTF(("[NVODM PMU] Pcf50626GetBatteryData: Error reading VBATSense. \n"));
+ return NV_FALSE;
+ }
+
+ batteryData.batteryLifePercent =
+ Pcf50626CalulateBatteryLifePercent_int(VBatSense);
+
+#if BATTEMP_CONTROL
+ if (!Pcf50626BatteryTemperatureControl_int(hDevice, VBatTemp))
+ {
+ NVODMPMU_PRINTF(("[NVODM PMU] Pcf50626GetBatteryData: Error in battery ctemperature controls. \n"));
+ return NV_FALSE;
+ }
+#endif
+
+ batteryData.batteryVoltage = VBatSense;
+ batteryData.batteryTemperature = Pcf50626BatteryTemperature(VBatSense,
+ VBatTemp);
+ }
+
+ *pData = batteryData;
+ }
+ else
+ {
+ *pData = batteryData;
+ }
+
+ return NV_TRUE;
+}
+
+void
+Pcf50626GetBatteryFullLifeTime(
+ NvOdmPmuDeviceHandle hDevice,
+ NvOdmPmuBatteryInstance batteryInst,
+ NvU32 *pLifeTime)
+{
+ *pLifeTime = NVODM_BATTERY_DATA_UNKNOWN;
+}
+
+void
+Pcf50626GetBatteryChemistry(
+ NvOdmPmuDeviceHandle hDevice,
+ NvOdmPmuBatteryInstance batteryInst,
+ NvOdmPmuBatteryChemistry *pChemistry)
+{
+ //return fixed data for now.
+ *pChemistry = NvOdmPmuBatteryChemistry_LION;
+}
+
+NvBool
+Pcf50626SetChargingCurrent(
+NvOdmPmuDeviceHandle hDevice,
+NvOdmPmuChargingPath chargingPath,
+NvU32 chargingCurrentLimitMa,
+NvOdmUsbChargerType chargerType)
+{
+ NvU8 data = 0;
+ NV_ASSERT(hDevice);
+
+ // if no battery, then do nothing
+ if (((Pcf50626PrivData*)hDevice->pPrivate)->battPresence == NV_FALSE)
+ return NV_TRUE;
+ //Concorde s/w WAR for USB Host mode
+ if (chargingCurrentLimitMa == NVODM_USB_HOST_MODE_LIMIT)
+ {
+ chargingCurrentLimitMa = 0; // turn off the charging path
+ UsbHostMode = NV_TRUE;
+ }
+ else
+ {
+ UsbHostMode = NV_FALSE;
+ }
+
+ // if requested current is more than max supported current then limit to supported
+ if ( chargingCurrentLimitMa > MAX_CHARGER_LIMIT_MA )
+ chargingCurrentLimitMa = MAX_CHARGER_LIMIT_MA;
+
+ if (chargingPath == NvOdmPmuChargingPath_UsbBus)
+ {
+ switch (chargerType)
+ {
+ case NvOdmUsbChargerType_SJ:
+ chargingCurrentLimitMa = SJ_TYPE_CHARGER_LIMIT_MA;
+ break;
+ case NvOdmUsbChargerType_SK:
+ chargingCurrentLimitMa = SK_TYPE_CHARGER_LIMIT_MA;
+ break;
+ case NvOdmUsbChargerType_SE1:
+ chargingCurrentLimitMa = SE1_TYPE_CHARGER_LIMIT_MA;
+ break;
+ case NvOdmUsbChargerType_SE0:
+ chargingCurrentLimitMa = SE0_TYPE_CHARGER_LIMIT_MA;
+ break;
+ case NvOdmUsbChargerType_UsbHost:
+ default:
+ // USB Host based charging, nothing to do. Just pass current limit to PMU.
+ break;
+ }
+ }
+
+ data = (NvU8)((( chargingCurrentLimitMa << 8 ) - chargingCurrentLimitMa )
+ / CHARGER_CONSTANT_CURRENT_SET_MA );
+
+ if (!Pcf50626I2cWrite8(hDevice, PCF50626_CBCC3_ADDR, data))
+ return NV_FALSE;
+
+ // turn off the charger path if the requested current limit is 0mA. Turn on the path otherwise.
+ data = 0;
+ if ( !Pcf50626I2cRead8(hDevice, PCF50626_CBCC1_ADDR, &data) )
+ return NV_FALSE;
+
+ if ( chargingCurrentLimitMa == 0 )
+ data &= ~(PCF50626_CBCC1_CHGENA_MASK); //off
+ else
+ data |= PCF50626_CBCC1_CHGENA_MASK; //on
+
+ if ( !Pcf50626I2cWrite8(hDevice, PCF50626_CBCC1_ADDR, data) )
+ return NV_FALSE;
+
+
+ data = 0;
+ if ( !Pcf50626I2cRead8(hDevice, PCF50626_CBCC2_ADDR, &data) )
+ return NV_FALSE;
+ if ( chargingCurrentLimitMa == 0 )
+ {
+ //enable USB suspend mode regardless of the SCUSB pin state
+ data |= (PCF50626_CBCC2_SUSPENA_MASK << PCF50626_CBCC2_SUSPENA_SHIFT);
+ }
+ else
+ {
+ //disable USB suspend mode regardless of the SCUSB pin state
+ data &= ~(PCF50626_CBCC2_SUSPENA_MASK << PCF50626_CBCC2_SUSPENA_SHIFT);
+ }
+ if ( !Pcf50626I2cWrite8(hDevice, PCF50626_CBCC2_ADDR, data) )
+ return NV_FALSE;
+
+ //Dump the register value for debug purpose, can be commented out is undesired..
+ NVODMPMU_PRINTF(("NvOdmPmuSetChargingCurrent: \n"));
+ NVODMPMU_PRINTF((" chargingCurrentLimitMa:%d\n", chargingCurrentLimitMa));
+
+ if ( !Pcf50626I2cRead8(hDevice, PCF50626_CBCC1_ADDR, &data) )
+ return NV_FALSE;
+ NVODMPMU_PRINTF((" CBCC1:0x%02x\n", data));
+
+ if ( !Pcf50626I2cRead8(hDevice, PCF50626_CBCC2_ADDR, &data) )
+ return NV_FALSE;
+ NVODMPMU_PRINTF((" CBCC2:0x%02x\n", data));
+
+ if ( !Pcf50626I2cRead8(hDevice, PCF50626_CBCC3_ADDR, &data) )
+ return NV_FALSE;
+ NVODMPMU_PRINTF((" CBCC3:0x%02x\n", data));
+
+ return NV_TRUE;
+}
+
+void Pcf50626InterruptHandler( NvOdmPmuDeviceHandle hDevice)
+{
+ // If the interrupt handle is called, the interrupt is supported.
+ ((Pcf50626PrivData*)hDevice->pPrivate)->pmuInterruptSupported = NV_TRUE;
+
+ Pcf50626InterruptHandler_int(hDevice, &((Pcf50626PrivData*)hDevice->pPrivate)->pmuStatus);
+}
+
+
+static NvU32 Pcf50626CalulateBatteryLifePercent_int(NvU32 vBatSense)
+{
+ NvU32 lifePerc = 0;
+ NvU32 vbat = vBatSense;
+
+ if (vbat < NVODM_BATTERY_CRITICAL_VOLTAGE_MV)
+ vbat = NVODM_BATTERY_CRITICAL_VOLTAGE_MV;
+
+ // using the linear mapping between the battery voltage and the life percentage.
+ lifePerc = ( ( vbat - NVODM_BATTERY_CRITICAL_VOLTAGE_MV ) * 50
+ / ( NVODM_BATTERY_FULL_VOLTAGE_MV - NVODM_BATTERY_CRITICAL_VOLTAGE_MV ) ) << 1;
+
+ if (lifePerc > 100)
+ lifePerc = 100;
+
+ return lifePerc;
+}
+
+
+#if BATTEMP_CONTROL
+static NvBool Pcf50626BatteryTemperatureControl_int(
+ NvOdmPmuDeviceHandle hDevice,
+ NvU32 batTemp)
+{
+ NvU8 data = 0;
+
+ //turn off the charger if the battery is overheating.
+ if ( batTemp > NVODM_BATTERY_OVERHEAT_THRESHOLD )
+ {
+ if (!Pcf50626I2cRead8(hDevice, PCF50626_CBCC1_ADDR, &data))
+ return NV_FALSE;
+
+ data &= 0xFE;
+ if (!Pcf50626I2cWrite8(hDevice, PCF50626_CBCC1_ADDR, data))
+ return NV_FALSE;
+ }
+ // turn it on otherwise
+ else
+ {
+ if (!Pcf50626I2cRead8(hDevice, PCF50626_CBCC1_ADDR, &data))
+ return NV_FALSE;
+
+ data |= 0x01;
+ if (!Pcf50626I2cWrite8(hDevice, PCF50626_CBCC1_ADDR, data))
+ return NV_FALSE;
+ }
+
+ return NV_TRUE;
+}
+#endif
+
+
+