summaryrefslogtreecommitdiff
path: root/arch/arm/mach-tegra/odm_kit/adaptations/tmon/adt7461/nvodm_tmon_adt7461.c
diff options
context:
space:
mode:
Diffstat (limited to 'arch/arm/mach-tegra/odm_kit/adaptations/tmon/adt7461/nvodm_tmon_adt7461.c')
-rw-r--r--arch/arm/mach-tegra/odm_kit/adaptations/tmon/adt7461/nvodm_tmon_adt7461.c934
1 files changed, 934 insertions, 0 deletions
diff --git a/arch/arm/mach-tegra/odm_kit/adaptations/tmon/adt7461/nvodm_tmon_adt7461.c b/arch/arm/mach-tegra/odm_kit/adaptations/tmon/adt7461/nvodm_tmon_adt7461.c
new file mode 100644
index 000000000000..dc76869f6dec
--- /dev/null
+++ b/arch/arm/mach-tegra/odm_kit/adaptations/tmon/adt7461/nvodm_tmon_adt7461.c
@@ -0,0 +1,934 @@
+/*
+ * 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_tmon_adt7461.h"
+#include "tmon_hal.h"
+
+// TODO: Always Disable before check-in
+// Always debug module: 0=disable, 1=enable
+#define NV_ADT7461_DEBUG (0)
+
+#if (NV_DEBUG || NV_ADT7461_DEBUG)
+#define NVODM_ADT7461_PRINTF(x) NvOdmOsDebugPrintf x
+#else
+#define NVODM_ADT7461_PRINTF(x)
+#endif
+
+#define ADT7461_ALERT_DEBOUNCE (1)
+
+// ADT7461 Descrriptor
+static const ADT7461Info s_Adt7461Info =
+{
+ // TMON device conversion channels
+ {
+ // Invalid channel
+ {0},
+
+ // Local channel
+ {
+ ADT7461ChannelID_Local,
+ {
+ ADT7461_ODM_LOCAL_RATE_PROTECTED,
+ ADT7461_ODM_LOCAL_INTR_LIMITS_PROTECTED,
+ ADT7461_ODM_LOCAL_COMPARATOR_LIMIT_PROTECTED
+ },
+ {
+ ADT7461_LOCAL_INTR_LIMIT_HIGH_RD_ADDR,
+ ADT7461_LOCAL_INTR_LIMIT_HIGH_WR_ADDR,
+ },
+ {
+ ADT7461_LOCAL_INTR_LIMIT_LOW_RD_ADDR,
+ ADT7461_LOCAL_INTR_LIMIT_LOW_WR_ADDR,
+ },
+ {
+ ADT7461_LOCAL_COMPARATOR_LIMIT_ADDR,
+ ADT7461_LOCAL_COMPARATOR_LIMIT_ADDR,
+ },
+ {
+ ADT7461_INVALID_ADDR, // Local offset does not exist
+ ADT7461_INVALID_ADDR,
+ },
+ {
+ ADT7461_LOCAL_TDATA_RD_ADDR,
+ ADT7461_INVALID_ADDR,
+ },
+ },
+
+ // Remote channel
+ {
+ ADT7461ChannelID_Remote,
+ {
+ ADT7461_ODM_REMOTE_RATE_PROTECTED,
+ ADT7461_ODM_REMOTE_INTR_LIMITS_PROTECTED,
+ ADT7461_ODM_REMOTE_COMPARATOR_LIMIT_PROTECTED
+ },
+ {
+ ADT7461_REMOTE_INTR_LIMIT_HIGH_RD_ADDR,
+ ADT7461_REMOTE_INTR_LIMIT_HIGH_WR_ADDR,
+ },
+ {
+ ADT7461_REMOTE_INTR_LIMIT_LOW_RD_ADDR,
+ ADT7461_REMOTE_INTR_LIMIT_LOW_WR_ADDR,
+ },
+ {
+ ADT7461_REMOTE_COMPARATOR_LIMIT_ADDR,
+ ADT7461_REMOTE_COMPARATOR_LIMIT_ADDR,
+ },
+ {
+ ADT7461_REMOTE_TOFFSET_ADDR,
+ ADT7461_REMOTE_TOFFSET_ADDR,
+ },
+ {
+ ADT7461_REMOTE_TDATA_RD_ADDR,
+ ADT7461_INVALID_ADDR,
+ },
+ }
+ },
+
+ // TMON device common status/control registers
+ {
+ ADT7461_STATUS_RD_ADDR,
+ ADT7461_INVALID_ADDR,
+ },
+ {
+ ADT7461_CONFIG_RD_ADDR,
+ ADT7461_CONFIG_WR_ADDR,
+ },
+ {
+ ADT7461_RATE_RD_ADDR,
+ ADT7461_RATE_WR_ADDR,
+ },
+ {
+ ADT7461_INVALID_ADDR,
+ ADT7461_ONE_SHOT_WR_ADDR,
+ },
+ {
+ ADT7461_COMPARATOR_HYSTERESIS_ADDR,
+ ADT7461_COMPARATOR_HYSTERESIS_ADDR,
+ },
+ {
+ ADT7461_INTR_CNT_DELAY_ADDR,
+ ADT7461_INTR_CNT_DELAY_ADDR,
+ },
+};
+
+// ADT7461 sample intervals
+static const NvS32 s_Adt7461SampleIntervalsMS[] =
+{
+ ADT7461_SAMPLE_INTERVALS_MS
+};
+
+// ADT7461 converison times
+static const NvS32 s_Adt7461ConversionTimesMS[] =
+{
+ ADT7461_CONVERSION_TIME_MS
+};
+
+NV_CT_ASSERT(NV_ARRAY_SIZE(s_Adt7461SampleIntervalsMS) ==
+ NV_ARRAY_SIZE(s_Adt7461ConversionTimesMS));
+
+/*****************************************************************************/
+
+#define ADT7461_T_DATA_TO_VALUE(ExtRange, data) \
+ ( (ExtRange) ? \
+ ((NvS32)((NvU32)(data) - ADT7461_RANGE_EXTENDED_DATA_OFFSET)) : \
+ ((NvS32)((NvS8)data)) \
+ )
+
+#define ADT7461_T_VALUE_TO_DATA(ExtRange, val) \
+ ( (ExtRange) ? \
+ ((NvU8)((NvU32)(val) + ADT7461_RANGE_EXTENDED_DATA_OFFSET)) : \
+ ((NvU8)(val)) \
+ )
+
+#define ADT7461_T_RANGE_LIMIT_HIGH(ExtRange) \
+ ( (ExtRange) ? \
+ ADT7461_RANGE_EXTENDED_LIMIT_HIGH : \
+ ADT7461_RANGE_STANDARD_LIMIT_HIGH \
+ )
+
+#define ADT7461_T_RANGE_LIMIT_LOW(ExtRange) \
+ ( (ExtRange) ? \
+ ADT7461_RANGE_EXTENDED_LIMIT_LOW : \
+ ADT7461_RANGE_STANDARD_LIMIT_LOW \
+ )
+
+/*****************************************************************************/
+
+static NvBool
+Adt7461WriteReg(
+ ADT7461PrivData* pPrivData,
+ const ADT7461RegisterInfo* pReg,
+ NvU8 Data)
+{
+ NvU32 i;
+ NvU8 WriteBuffer[2];
+ NvOdmI2cStatus status;
+ NvOdmI2cTransactionInfo TransactionInfo;
+
+ NV_ASSERT(pPrivData && pReg);
+ NV_ASSERT(pReg->WrAddr != ADT7461_INVALID_ADDR);
+
+ for (i = 0; i < ADT7461_I2C_RETRY_CNT; i++)
+ {
+ WriteBuffer[0] = pReg->WrAddr;
+ WriteBuffer[1] = Data;
+
+ TransactionInfo.Address = pPrivData->DeviceI2cAddr;
+ TransactionInfo.Buf = &WriteBuffer[0];
+ TransactionInfo.Flags = NVODM_I2C_IS_WRITE;
+ TransactionInfo.NumBytes = 2;
+
+ status = NvOdmI2cTransaction(pPrivData->hOdmI2C, &TransactionInfo, 1,
+ ADT7461_I2C_SPEED_KHZ, ADT7461_I2C_TIMEOUT_MS);
+ if (status == NvOdmI2cStatus_Success)
+ break;
+ }
+
+ switch (status)
+ {
+ case NvOdmI2cStatus_Success:
+ pPrivData->ShadowRegPtr = pReg->WrAddr;
+ return NV_TRUE;
+
+ case NvOdmI2cStatus_Timeout:
+ NVODM_ADT7461_PRINTF(("ADT7461: WriteReg Timeout\n"));
+ return NV_FALSE;
+
+ case NvOdmI2cStatus_SlaveNotFound:
+ default:
+ NVODM_ADT7461_PRINTF(("ADT7461: WriteReg SlaveNotFound\n"));
+ return NV_FALSE;
+ }
+}
+
+static NvBool
+Adt7461ReadReg(
+ ADT7461PrivData* pPrivData,
+ const ADT7461RegisterInfo* pReg,
+ NvU8* pData)
+{
+ NvU32 i;
+ NvU8 Buffer = 0;
+ NvOdmI2cStatus status;
+ NvOdmI2cTransactionInfo TransactionInfo[2];
+
+ NV_ASSERT(pPrivData && pReg && pData);
+ NV_ASSERT(pReg->RdAddr != ADT7461_INVALID_ADDR);
+
+ // TODO: possible optimization - is shadow pointer matches register
+ // address, just send one read transaction (can be done only if Read/Wr
+ // Reg routines are serialized).
+
+ for (i = 0; i < ADT7461_I2C_RETRY_CNT; i++)
+ {
+ Buffer = pReg->RdAddr;
+
+ TransactionInfo[0].Address = pPrivData->DeviceI2cAddr;
+ TransactionInfo[0].Buf = &Buffer;
+ TransactionInfo[0].Flags = NVODM_I2C_IS_WRITE;
+ TransactionInfo[0].NumBytes = 1;
+
+ TransactionInfo[1].Address = (pPrivData->DeviceI2cAddr | 0x1);
+ TransactionInfo[1].Buf = &Buffer;
+ TransactionInfo[1].Flags = 0;
+ TransactionInfo[1].NumBytes = 1;
+
+ status = NvOdmI2cTransaction(pPrivData->hOdmI2C, &TransactionInfo[0], 2,
+ ADT7461_I2C_SPEED_KHZ, ADT7461_I2C_TIMEOUT_MS);
+ if (status == NvOdmI2cStatus_Success)
+ break;
+ }
+
+ switch (status)
+ {
+ case NvOdmI2cStatus_Success:
+ pPrivData->ShadowRegPtr = pReg->RdAddr;
+ *pData = Buffer;
+ return NV_TRUE;
+
+ case NvOdmI2cStatus_Timeout:
+ NVODM_ADT7461_PRINTF(("ADT7461: ReadReg Timeout\n"));
+ return NV_FALSE;
+
+ case NvOdmI2cStatus_SlaveNotFound:
+ default:
+ NVODM_ADT7461_PRINTF(("ADT7461: ReadReg SlaveNotFound\n"));
+ return NV_FALSE;
+ }
+}
+
+static void Adt7461ReadAra(ADT7461PrivData* pPrivData)
+{
+ NvU32 i;
+ NvU8 Buffer = 0;
+ NvOdmI2cStatus status;
+ NvOdmI2cTransactionInfo TransactionInfo;
+
+ NV_ASSERT(pPrivData);
+
+ for (i = 0; i < ADT7461_ARA_RETRY_CNT; i++)
+ {
+ TransactionInfo.Address = (ADT7461_ARA | 0x1);
+ TransactionInfo.Buf = &Buffer;
+ TransactionInfo.Flags = 0;
+ TransactionInfo.NumBytes = 1;
+
+ status = NvOdmI2cTransaction(pPrivData->hOdmI2C, &TransactionInfo, 1,
+ ADT7461_I2C_SPEED_KHZ, ADT7461_I2C_TIMEOUT_MS);
+ if ((status == NvOdmI2cStatus_SlaveNotFound) || // False alarm
+ ((status == NvOdmI2cStatus_Success) &&
+ ((Buffer & 0xFE) == (NvU8)pPrivData->DeviceI2cAddr)) // Cleared ARA
+ )
+ break;
+ }
+}
+
+static NvBool
+Adt7461ConfigureSampleInterval(
+ ADT7461PrivData* pPrivData,
+ NvBool OdmProtected,
+ NvS32* pTargetMs)
+{
+ NvU8 i;
+ NvS32 Delta;
+ const ADT7461RegisterInfo* pReg = &pPrivData->pDeviceInfo->Rate;
+
+ if (OdmProtected ||
+ ((*pTargetMs) == ODM_TMON_PARAMETER_UNSPECIFIED))
+ {
+ // Read ADT7461 rate register (fail the call if returned data
+ // does not make sense)
+ if(!Adt7461ReadReg(pPrivData, pReg, &i))
+ return NV_FALSE;
+ if (i >= NV_ARRAY_SIZE(s_Adt7461SampleIntervalsMS))
+ return NV_FALSE;
+ }
+ else
+ {
+ // Find and set the best floor approximation of the target sample
+ // interval. Note the descending order of sample intervals array.
+ for (i = 0; i < NV_ARRAY_SIZE(s_Adt7461SampleIntervalsMS); i++)
+ {
+ Delta = (*pTargetMs) - s_Adt7461SampleIntervalsMS[i];
+ if(Delta >= 0)
+ break;
+ }
+ if (i == NV_ARRAY_SIZE(s_Adt7461SampleIntervalsMS))
+ i--; // min interval is the best we can do
+
+ if(!Adt7461WriteReg(pPrivData, pReg, i))
+ return NV_FALSE;
+ pPrivData->ShadowRate = i;
+ }
+ *pTargetMs = s_Adt7461SampleIntervalsMS[i];
+ return NV_TRUE;
+}
+
+/*****************************************************************************/
+
+static void Adt7461Isr(void* arg)
+{
+ NvU8 Data;
+ ADT7461PrivData* pPrivData = (ADT7461PrivData*)arg;
+ NvOdmInterruptHandler volatile Callback = pPrivData->Callback;
+ void* volatile CallbackArg = pPrivData->CallbackArg;
+ const ADT7461RegisterInfo* pReg = NULL;
+
+ if (Callback && CallbackArg)
+ {
+ Callback(CallbackArg);
+ }
+#if ADT7461_ALERT_DEBOUNCE
+ // New range limits set by callback are not guaranteed to take effect
+ // before the next temperature conversion is completed, and interrupt
+ // can not be cleared until then. Hence, the debounce delay below.
+ NvOdmOsSleepMS(s_Adt7461SampleIntervalsMS[pPrivData->ShadowRate] +
+ s_Adt7461ConversionTimesMS[pPrivData->ShadowRate] + 1);
+#endif
+ // Read status and ARA to finish clearing interrupt after callback
+ pReg = &pPrivData->pDeviceInfo->Status;
+ (void)Adt7461ReadReg(pPrivData, pReg, &Data);
+ Adt7461ReadAra(pPrivData);
+
+ // Re-enable interrupt
+ if (pPrivData->hGpioIntr)
+ NvOdmGpioInterruptDone(pPrivData->hGpioIntr);
+}
+
+static void Adt7461FreePrivData(ADT7461PrivData* pPrivData)
+{
+ if (pPrivData)
+ {
+ if (pPrivData->hGpioIntr)
+ {
+ NvOdmGpioInterruptUnregister(
+ pPrivData->hGpio, pPrivData->hGpioPin, pPrivData->hGpioIntr);
+ }
+ NvOdmI2cClose(pPrivData->hOdmI2C);
+ NvOdmGpioReleasePinHandle(pPrivData->hGpio, pPrivData->hGpioPin);
+ NvOdmGpioClose(pPrivData->hGpio);
+ NvOdmServicesPmuClose(pPrivData->hOdmPmuSevice);
+ NvOdmOsFree(pPrivData);
+ }
+}
+
+/*****************************************************************************/
+
+NvBool Adt7461Init(NvOdmTmonDeviceHandle hTmon)
+{
+ NvU8 Data;
+ NvBool ExtRange;
+ NvU32 i = 0;
+ NvU32 I2cInstance = 0;
+ NvOdmIoModule I2cModule = NvOdmIoModule_Num; // Inavlid module
+ const ADT7461RegisterInfo* pReg = NULL;
+ ADT7461PrivData* pPrivData = NULL;
+
+ NV_ASSERT(hTmon && hTmon->pConn && hTmon->pConn->AddressList);
+
+ // Allocate and clear priavte data
+ pPrivData = (ADT7461PrivData*) NvOdmOsAlloc(sizeof(ADT7461PrivData));
+ if (pPrivData == NULL)
+ {
+ NVODM_ADT7461_PRINTF(("ADT7461: Error Allocating PrivData. \n"));
+ return NV_FALSE;
+ }
+ NvOdmOsMemset(pPrivData, 0, sizeof(ADT7461PrivData));
+ hTmon->pPrivate = pPrivData;
+
+ // Register for PMU services
+ pPrivData->hOdmPmuSevice = NvOdmServicesPmuOpen();
+ if (pPrivData->hOdmPmuSevice == NULL)
+ {
+ NVODM_ADT7461_PRINTF(("ADT7461: Error Open PMU service. \n"));
+ goto fail;
+ }
+
+ // Register for GPIO services
+ pPrivData->hGpio = NvOdmGpioOpen();
+ if (pPrivData->hOdmPmuSevice == NULL)
+ {
+ NVODM_ADT7461_PRINTF(("ADT7461: Error Open GPIO service. \n"));
+ goto fail;
+ }
+
+ /*
+ * Parse connectivity data: turn On power to the device, acquire I2C
+ * interface and GPIO interrupt (optional); map device channels to
+ * thermal zones
+ */
+ for (i = 0; i < hTmon->pConn->NumAddress; i ++)
+ {
+ const NvOdmIoAddress* pIoAddress = &hTmon->pConn->AddressList[i];
+ if (pIoAddress->Interface == NvOdmIoModule_I2c_Pmu)
+ {
+ I2cModule = NvOdmIoModule_I2c_Pmu;
+ I2cInstance = pIoAddress->Instance;
+ NV_ASSERT(pIoAddress->Address != 0);
+ pPrivData->DeviceI2cAddr = pIoAddress->Address;
+ }
+ else if (pIoAddress->Interface == NvOdmIoModule_Tsense)
+ {
+ NV_ASSERT(pIoAddress->Instance < NvOdmTmonZoneID_Num);
+ NV_ASSERT(pIoAddress->Address < ADT7461ChannelID_Num);
+ pPrivData->ConnectivityMap[pIoAddress->Instance] =
+ pIoAddress->Address;
+ }
+ else if (pIoAddress->Interface == NvOdmIoModule_Vdd)
+ {
+ NvU32 usec = 0;
+ NvU32 RailAddress = pIoAddress->Address;
+ NvOdmServicesPmuVddRailCapabilities RailCapabilities = {0};
+ NvOdmServicesPmuGetCapabilities(
+ pPrivData->hOdmPmuSevice, RailAddress, &RailCapabilities);
+ NvOdmServicesPmuSetVoltage(pPrivData->hOdmPmuSevice, RailAddress,
+ RailCapabilities.requestMilliVolts, &usec);
+ NvOdmOsWaitUS(usec + (ADT7461_POWERUP_DELAY_MS * 1000));
+ }
+ else if (pIoAddress->Interface == NvOdmIoModule_Gpio)
+ {
+ NvU32 port = pIoAddress->Instance;
+ NvU32 pin = pIoAddress->Address;
+ pPrivData->hGpioPin = NvOdmGpioAcquirePinHandle(
+ pPrivData->hGpio, port, pin);
+ }
+
+ }
+ NV_ASSERT(I2cModule == NvOdmIoModule_I2c_Pmu);
+ pPrivData->hOdmI2C = NvOdmI2cOpen(I2cModule, I2cInstance);
+ if (pPrivData->hOdmI2C == NULL)
+ {
+ NVODM_ADT7461_PRINTF(("ADT7461: Error Open I2C device. \n"));
+ goto fail;
+ }
+
+ /*
+ * Initialize device info and configuration. Force standby mode to avoid
+ * glitch on shutdown comparator output when temperature range and/or
+ * comparator limit is changing during initialization. The Adt7461Run()
+ * call from the hal that follows initialization will switch device to
+ * run mode and re-start temperature monitoring (note that out of limit
+ * interrupt is always masked during and after initialization)
+ */
+ pPrivData->pDeviceInfo = &s_Adt7461Info;
+ pPrivData->ShadowRegPtr = ADT7461_INVALID_ADDR;
+
+ pReg = &pPrivData->pDeviceInfo->Config;
+ if (!Adt7461ReadReg(pPrivData, pReg, &Data))
+ goto fail;
+ if ((Data & ADT7461ConfigBits_ExtendedRange) !=
+ (ADT7461_INITIAL_CONFIG & ADT7461ConfigBits_ExtendedRange))
+ {
+ // Only switch from standard to extended range is supported
+ NV_ASSERT((Data & ADT7461ConfigBits_ExtendedRange) == 0);
+ Data |= ADT7461ConfigBits_Standby;
+ if(!Adt7461WriteReg(pPrivData, pReg, Data))
+ goto fail;
+ }
+ Data = ADT7461_INITIAL_CONFIG | ADT7461ConfigBits_Standby;
+ if(!Adt7461WriteReg(pPrivData, pReg, Data))
+ goto fail;
+ pPrivData->ShadowConfig = Data;
+ ExtRange = ((Data & ADT7461ConfigBits_ExtendedRange) != 0);
+
+ // Program shutdown comparators settings
+ Data = ADT7461_T_VALUE_TO_DATA(
+ ExtRange, ADT7461_ODM_LOCAL_COMPARATOR_LIMIT_VALUE);
+ pReg = &pPrivData->pDeviceInfo->Channels[
+ ADT7461ChannelID_Local].ComparatorLimit;
+ if(!Adt7461WriteReg(pPrivData, pReg, Data))
+ goto fail;
+
+ Data = ADT7461_T_VALUE_TO_DATA(
+ ExtRange, ADT7461_ODM_REMOTE_COMPARATOR_LIMIT_VALUE);
+ pReg = &pPrivData->pDeviceInfo->Channels[
+ ADT7461ChannelID_Remote].ComparatorLimit;
+ if(!Adt7461WriteReg(pPrivData, pReg, Data))
+ goto fail;
+
+ // Set interrupt limits to the range boundaries to prevent out of limit
+ // interrupt
+ Data = ADT7461_T_VALUE_TO_DATA(
+ ExtRange, ADT7461_T_RANGE_LIMIT_HIGH(ExtRange));
+ pReg = &pPrivData->pDeviceInfo->Channels[
+ ADT7461ChannelID_Local].IntrLimitHigh;
+ if(!Adt7461WriteReg(pPrivData, pReg, Data))
+ goto fail;
+ pReg = &pPrivData->pDeviceInfo->Channels[
+ ADT7461ChannelID_Remote].IntrLimitHigh;
+ if(!Adt7461WriteReg(pPrivData, pReg, Data))
+ goto fail;
+
+ Data = ADT7461_T_VALUE_TO_DATA(
+ ExtRange, ADT7461_T_RANGE_LIMIT_LOW(ExtRange));
+ pReg = &pPrivData->pDeviceInfo->Channels[
+ ADT7461ChannelID_Local].IntrLimitLow;
+ if(!Adt7461WriteReg(pPrivData, pReg, Data))
+ goto fail;
+ pReg = &pPrivData->pDeviceInfo->Channels[
+ ADT7461ChannelID_Remote].IntrLimitLow;
+ if(!Adt7461WriteReg(pPrivData, pReg, Data))
+ goto fail;
+
+ // Set initial rate
+ Data = ADT7461_INITIAL_RATE_SETTING;
+ pReg = &pPrivData->pDeviceInfo->Rate;
+ if(!Adt7461WriteReg(pPrivData, pReg, Data))
+ goto fail;
+ pPrivData->ShadowRate = Data;
+
+ // Set remote channel offset (8-bit 2's complement value for any range)
+ Data = ((NvU8)ADT7461_ODM_REMOTE_OFFSET_VALUE);
+ pReg = &pPrivData->pDeviceInfo->Channels[
+ ADT7461ChannelID_Remote].Toffset;
+ if(!Adt7461WriteReg(pPrivData, pReg, Data))
+ goto fail;
+
+ // Read ADT7461 status and ARA (clear pending Alert interrupt, if any)
+ pReg = &pPrivData->pDeviceInfo->Status;
+ if (!Adt7461ReadReg(pPrivData, pReg, &Data))
+ goto fail;
+ // TODO: check open remote circuit error
+
+ Adt7461ReadAra(pPrivData);
+ return NV_TRUE;
+
+fail:
+ Adt7461FreePrivData(pPrivData);
+ hTmon->pPrivate = NULL;
+ return NV_FALSE;
+}
+
+void Adt7461Deinit(NvOdmTmonDeviceHandle hTmon)
+{
+ if (hTmon && hTmon->pPrivate)
+ {
+ ADT7461PrivData* pPrivData = hTmon->pPrivate;
+ (void)Adt7461WriteReg(pPrivData, &pPrivData->pDeviceInfo->Config,
+ ADT7461_INITIAL_CONFIG); //leave device in default configuration
+ // with power rail ON (forever)
+ Adt7461FreePrivData(pPrivData);
+ hTmon->pPrivate = NULL;
+ }
+}
+
+/*****************************************************************************/
+
+NvBool Adt7461Run(NvOdmTmonDeviceHandle hTmon, NvOdmTmonZoneID ZoneId)
+{
+ NvU8 Data;
+ NvBool IsRunning;
+ ADT7461PrivData* pPrivData;
+
+ NV_ASSERT(hTmon && hTmon->pPrivate);
+ pPrivData = hTmon->pPrivate;
+ IsRunning = (pPrivData->ShadowConfig & ADT7461ConfigBits_Standby) == 0;
+
+ if (!IsRunning)
+ {
+ Data = pPrivData->ShadowConfig & (~ADT7461ConfigBits_Standby);
+ if(!Adt7461WriteReg(pPrivData, &pPrivData->pDeviceInfo->Config, Data))
+ return NV_FALSE;
+ pPrivData->ShadowConfig = Data;
+ }
+ pPrivData->RunRefCount++;
+ return NV_TRUE;
+}
+
+NvBool Adt7461Stop(NvOdmTmonDeviceHandle hTmon, NvOdmTmonZoneID ZoneId)
+{
+ NvU8 Data;
+ NvBool IsRunning;
+ ADT7461PrivData* pPrivData;
+
+ NV_ASSERT(hTmon && hTmon->pPrivate);
+ pPrivData = hTmon->pPrivate;
+ IsRunning = (pPrivData->ShadowConfig & ADT7461ConfigBits_Standby) == 0;
+
+ if (ADT7461_ODM_STANDBY_ENABLED &&
+ IsRunning && (pPrivData->RunRefCount == 1))
+ {
+ Data = pPrivData->ShadowConfig | ADT7461ConfigBits_Standby;
+ if(!Adt7461WriteReg(pPrivData, &pPrivData->pDeviceInfo->Config, Data))
+ return NV_FALSE;
+ pPrivData->ShadowConfig = Data;
+ }
+ if (pPrivData->RunRefCount != 0)
+ {
+ pPrivData->RunRefCount--;
+ return NV_TRUE;
+ }
+ NV_ASSERT(!"RunRefCount balance failed");
+ NVODM_ADT7461_PRINTF(("ADT7461: RunRefCount balance failed. \n"));
+ return NV_FALSE;
+}
+
+/*****************************************************************************/
+// ADT7461 aborts and restarts conversion cycle when temperature is read
+// (actually on any I2C access for that matter, but other accesses are rare).
+// TODO: add time stamps and implement refresh policy to make sure that
+// frequent temperature reads would not stall the conversion forever.
+
+NvBool
+Adt7461TemperatureGet(
+ NvOdmTmonDeviceHandle hTmon,
+ NvOdmTmonZoneID ZoneId,
+ NvS32* pDegreesC)
+{
+ NvU8 Data;
+ NvBool ExtRange;
+ ADT7461ChannelID ChannelId;
+ ADT7461PrivData* pPrivData;
+ const ADT7461RegisterInfo* pReg;
+
+ NV_ASSERT(hTmon && hTmon->pPrivate && pDegreesC);
+ pPrivData = hTmon->pPrivate;
+ ExtRange = ((pPrivData->ShadowConfig &
+ ADT7461ConfigBits_ExtendedRange) != 0);
+ ChannelId = pPrivData->ConnectivityMap[ZoneId];
+ pReg = &pPrivData->pDeviceInfo->Channels[ChannelId].Tdata;
+
+ if(!Adt7461ReadReg(pPrivData, pReg, &Data))
+ return NV_FALSE;
+
+ *pDegreesC = ADT7461_T_DATA_TO_VALUE(ExtRange, Data);
+ return NV_TRUE;
+}
+
+/*****************************************************************************/
+
+void
+Adt7461CapabilitiesGet(
+ NvOdmTmonDeviceHandle hTmon,
+ NvOdmTmonZoneID ZoneId,
+ NvOdmTmonCapabilities* pCaps)
+{
+ NvBool ExtRange;
+ ADT7461PrivData* pPrivData;
+
+ NV_ASSERT(hTmon && hTmon->pPrivate && pCaps);
+ pPrivData = hTmon->pPrivate;
+ ExtRange = ((pPrivData->ShadowConfig &
+ ADT7461ConfigBits_ExtendedRange) != 0);
+
+ pCaps->Tmax = ADT7461_T_RANGE_LIMIT_HIGH(ExtRange);
+ pCaps->Tmin = ADT7461_T_RANGE_LIMIT_LOW(ExtRange);
+ pCaps->IntrSupported = NV_TRUE;
+ pCaps->HwCriticalSupported = NV_TRUE;
+ pCaps->HwCoolingSupported = NV_FALSE;
+}
+
+void
+Adt7461ParameterCapsGet(
+ NvOdmTmonDeviceHandle hTmon,
+ NvOdmTmonZoneID ZoneId,
+ NvOdmTmonConfigParam ParamId,
+ NvOdmTmonParameterCaps* pCaps)
+{
+ NvBool ExtRange;
+ ADT7461PrivData* pPrivData;
+ const ADT7461ChannelInfo* pChannel;
+
+ NV_ASSERT(hTmon && hTmon->pPrivate && pCaps);
+ pPrivData = hTmon->pPrivate;
+ ExtRange = ((pPrivData->ShadowConfig &
+ ADT7461ConfigBits_ExtendedRange) != 0);
+ pChannel = &pPrivData->pDeviceInfo->Channels[(
+ pPrivData->ConnectivityMap[ZoneId])];
+
+ switch (ParamId)
+ {
+ case NvOdmTmonConfigParam_IntrLimitHigh:
+ case NvOdmTmonConfigParam_IntrLimitLow:
+ pCaps->OdmProtected =
+ pChannel->ChannelPolicy.IntrLimitsOdmProtected;
+ break;
+
+ case NvOdmTmonConfigParam_HwLimitCrit:
+ pCaps->OdmProtected =
+ pChannel->ChannelPolicy.HwLimitCritOdmProtected;
+ break;
+
+ case NvOdmTmonConfigParam_SampleMs:
+ // smaple intervals in descending order
+ pCaps->MaxValue = s_Adt7461SampleIntervalsMS[0];
+ pCaps->MinValue = s_Adt7461SampleIntervalsMS[(
+ NV_ARRAY_SIZE(s_Adt7461SampleIntervalsMS) - 1)];
+ pCaps->OdmProtected = pChannel->ChannelPolicy.RateOdmProtected;
+ return;
+
+ default: // unsupported parameter
+ pCaps->MaxValue = ODM_TMON_PARAMETER_UNSPECIFIED;
+ pCaps->MinValue = ODM_TMON_PARAMETER_UNSPECIFIED;
+ pCaps->OdmProtected = NV_TRUE;
+ return;
+ }
+
+ // Common range for limits
+ pCaps->MaxValue = ADT7461_T_RANGE_LIMIT_HIGH(ExtRange);
+ pCaps->MinValue = ADT7461_T_RANGE_LIMIT_LOW(ExtRange);
+}
+
+NvBool
+Adt7461ParameterConfig(
+ NvOdmTmonDeviceHandle hTmon,
+ NvOdmTmonZoneID ZoneId,
+ NvOdmTmonConfigParam ParamId,
+ NvS32* pSetting)
+{
+ NvU8 Data;
+ NvBool ExtRange, OdmProtected;
+ ADT7461PrivData* pPrivData;
+ const ADT7461RegisterInfo* pReg;
+ const ADT7461ChannelInfo* pChannel;
+
+ NV_ASSERT(hTmon && hTmon->pPrivate && pSetting);
+ pPrivData = hTmon->pPrivate;
+ ExtRange = ((pPrivData->ShadowConfig &
+ ADT7461ConfigBits_ExtendedRange) != 0);
+ pChannel = &pPrivData->pDeviceInfo->Channels[(
+ pPrivData->ConnectivityMap[ZoneId])];
+
+ switch (ParamId)
+ {
+ case NvOdmTmonConfigParam_IntrLimitHigh:
+ pReg = &pChannel->IntrLimitHigh;
+ OdmProtected = pChannel->ChannelPolicy.IntrLimitsOdmProtected;
+ break;
+
+ case NvOdmTmonConfigParam_IntrLimitLow:
+ pReg = &pChannel->IntrLimitLow;
+ OdmProtected = pChannel->ChannelPolicy.IntrLimitsOdmProtected;
+ break;
+
+ case NvOdmTmonConfigParam_HwLimitCrit:
+ pReg = &pChannel->ComparatorLimit;
+ OdmProtected = pChannel->ChannelPolicy.HwLimitCritOdmProtected;
+ break;
+
+ case NvOdmTmonConfigParam_SampleMs:
+ OdmProtected = pChannel->ChannelPolicy.RateOdmProtected;
+ return Adt7461ConfigureSampleInterval(
+ pPrivData, OdmProtected, pSetting);
+
+ default: // unsupported parameter
+ *pSetting = ODM_TMON_PARAMETER_UNSPECIFIED;
+ return NV_TRUE;
+ }
+
+ // Common processing for temperature limits configuration
+ if ((OdmProtected) ||
+ ((*pSetting) == ODM_TMON_PARAMETER_UNSPECIFIED))
+ {
+ // Read ADT7461 register and convert data to current parameter value
+ if(!Adt7461ReadReg(pPrivData, pReg, &Data))
+ return NV_FALSE;
+
+ *pSetting = ADT7461_T_DATA_TO_VALUE(ExtRange, Data);
+ }
+ else
+ {
+ // Clip target setting to temperature range
+ if ((*pSetting) > ADT7461_T_RANGE_LIMIT_HIGH(ExtRange))
+ *pSetting = ADT7461_T_RANGE_LIMIT_HIGH(ExtRange);
+ else if ((*pSetting) < ADT7461_T_RANGE_LIMIT_LOW(ExtRange))
+ *pSetting = ADT7461_T_RANGE_LIMIT_LOW(ExtRange);
+
+ // Convert new configuration setting and write to ADT7461 register
+ Data = ADT7461_T_VALUE_TO_DATA(ExtRange, *pSetting);
+ if(!Adt7461WriteReg(pPrivData, pReg, Data))
+ return NV_FALSE;
+ }
+ return NV_TRUE;
+}
+
+/*****************************************************************************/
+
+NvOdmTmonIntrHandle
+Adt7461IntrRegister(
+ NvOdmTmonDeviceHandle hTmon,
+ NvOdmTmonZoneID ZoneId,
+ NvOdmInterruptHandler Callback,
+ void* CallbackArg)
+{
+ NvU8 Data;
+ ADT7461PrivData* pPrivData;
+ const ADT7461ChannelInfo* pChannel;
+ NvOdmServicesGpioIntrHandle hGpioIntr = NULL;
+
+ NV_ASSERT(hTmon && hTmon->pPrivate && Callback && CallbackArg);
+ pPrivData = hTmon->pPrivate;
+
+ // No registration, if no GPIO pin available or interrupt already registred
+ if (!pPrivData->hGpioPin || pPrivData->hGpioIntr)
+ return NULL;
+
+ // No registration for other than remote channel
+ pChannel = &pPrivData->pDeviceInfo->Channels[(
+ pPrivData->ConnectivityMap[ZoneId])];
+ if (pChannel->ChannelId != ADT7461ChannelID_Remote)
+ return NULL;
+
+ // Register GPIO interrupt (will be enabled at SoC IC, but still disabled
+ // at ADT7461 device)
+ pPrivData->Callback = Callback;
+ pPrivData->CallbackArg = CallbackArg;
+ if (!NvOdmGpioInterruptRegister(
+ pPrivData->hGpio, &hGpioIntr, pPrivData->hGpioPin,
+ ADT7461_ODM_INTR_POLARITY, Adt7461Isr, (void *)pPrivData, 0))
+ {
+ pPrivData->Callback = NULL;
+ pPrivData->CallbackArg = NULL;
+ return NULL;
+ }
+ NV_ASSERT(hGpioIntr);
+ pPrivData->hGpioIntr = hGpioIntr;
+
+ // Finally enable ADT7461 device interrupt output (interrupt may or may
+ // not be generated depending on temperature and limt settings).
+ Data = pPrivData->ShadowConfig & (~ADT7461ConfigBits_IntrDisabled);
+ if(!Adt7461WriteReg(pPrivData, &pPrivData->pDeviceInfo->Config, Data))
+ {
+ NvOdmGpioInterruptUnregister(
+ pPrivData->hGpio, pPrivData->hGpioPin, hGpioIntr);
+ pPrivData->Callback = NULL;
+ pPrivData->CallbackArg = NULL;
+ pPrivData->hGpioIntr = NULL;
+ return NULL;
+ }
+ pPrivData->ShadowConfig = Data;
+
+ return (NvOdmTmonIntrHandle)hGpioIntr;
+}
+
+void
+Adt7461IntrUnregister(
+ NvOdmTmonDeviceHandle hTmon,
+ NvOdmTmonZoneID ZoneId,
+ NvOdmTmonIntrHandle hIntr)
+{
+ NvU8 Data;
+ ADT7461PrivData* pPrivData;
+ const ADT7461ChannelInfo* pChannel;
+
+ // Ignore invalid handles
+ if(!hIntr || !hTmon || !hTmon->pPrivate)
+ return;
+
+ pPrivData = hTmon->pPrivate;
+ if (hIntr != ((NvOdmTmonIntrHandle)pPrivData->hGpioIntr))
+ return;
+
+ // Ignore any channel other than remote
+ pChannel = &pPrivData->pDeviceInfo->Channels[(
+ pPrivData->ConnectivityMap[ZoneId])];
+ if (pChannel->ChannelId != ADT7461ChannelID_Remote)
+ return;
+
+ // Disable ADT7461 interrupt output
+ Data = pPrivData->ShadowConfig | ADT7461ConfigBits_IntrDisabled;
+ if(Adt7461WriteReg(pPrivData, &pPrivData->pDeviceInfo->Config, Data))
+ pPrivData->ShadowConfig = Data;
+
+ // Unregister GPIO interrupt, clear callbacks and handle
+ NvOdmGpioInterruptUnregister(
+ pPrivData->hGpio, pPrivData->hGpioPin, pPrivData->hGpioIntr);
+
+ pPrivData->Callback = NULL;
+ pPrivData->CallbackArg = NULL;
+ pPrivData->hGpioIntr = NULL;
+}
+
+/*****************************************************************************/
+