/*
* 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.
*
*/
/**
* @file
* NVIDIA APX ODM Kit::
* Implementation of the ODM Peripheral Discovery API
*
* @b Description: The peripheral connectivity database implementation.
*/
#include "nvcommon.h"
#include "nvodm_query_gpio.h"
#include "nvodm_modules.h"
#include "nvodm_query_discovery.h"
#include "nvodm_keylist_reserved.h"
#include "tegra_devkit_custopt.h"
#include "nvodm_query.h"
#include "nvrm_drf.h"
#include "subboards/nvodm_query_discovery_e1162_addresses.h"
static NvOdmPeripheralConnectivity s_Peripherals_Default[] =
{
#include "subboards/nvodm_query_discovery_e1162_peripherals.h"
};
#define NVODM_QUERY_BOARD_ID_UNKNOWN 0xFFFF
#define NVODM_QUERY_MAX_PERIPHERALS 0x400
#define NVODM_QUERY_MAX_IO_ADDRESSES 0x400
#define NVODM_QUERY_MAX_EEPROMS 8 // Maximum number of EEPROMs per bus segment
#define NVODM_QUERY_ERASED_EEPROM_VALUE 0xFF
#define PROCESSOR_BOARD_ID_I2C_ADDRESS ((0x56)<<1)
#define PROCESSOR_BOARD_ID_I2C_SEGMENT (0x00)
NvBool IsBoardTango(void);
// The following are used to store entries read from EEPROMs at runtime.
static NvOdmPeripheralConnectivity s_Peripherals[NVODM_QUERY_MAX_PERIPHERALS];
static NvOdmIoAddress s_Peripheral_IoAddresses[NVODM_QUERY_MAX_IO_ADDRESSES];
static NvOdmBoardInfo s_BoardModuleTable[NVODM_QUERY_MAX_EEPROMS];
#define NVODM_QUERY_I2C_CLOCK_SPEED 100 // kHz
#define NVODM_QUERY_ENTRY_HEADER_SIZE 0x30 // Size of EERPOM "Entry Header"
#define NVODM_QUERY_BOARD_HEADER_START 0x04 // Offset to Part Number in EERPOM
#define NVODM_QUERY_I2C_EEPROM_ADDRESS 0xA0 // I2C device base address for EEPROM (7'h50)
#define NVODM_QUERY_PERIPH_CONN_STRUCT_COMPRESSED 10 // See EEPROM_format.txt
#define NVODM_PERIPH_IO_ADDR_STRUCT_SZ_COMPRESSED 2 // See EEPROM_format.txt
#define EEPROM_ID_E1206 0x0C06
#define ACCEL_TANGO_GUID NV_ODM_GUID('k','x','t','9','-','0','0','0')
static NvOdmI2cStatus
NvOdmPeripheralI2cRead8(
NvOdmServicesI2cHandle hOdmI2c,
NvU8 I2cAddr,
NvU8 Offset,
NvU8 *pData)
{
NvU8 ReadBuffer[1];
NvOdmI2cStatus Error;
NvOdmI2cTransactionInfo TransactionInfo;
ReadBuffer[0] = Offset;
TransactionInfo.Address = I2cAddr;
TransactionInfo.Buf = ReadBuffer;
TransactionInfo.Flags = NVODM_I2C_IS_WRITE;
TransactionInfo.NumBytes = 1;
Error = NvOdmI2cTransaction(
hOdmI2c, &TransactionInfo, 1, NVODM_QUERY_I2C_CLOCK_SPEED, NV_WAIT_INFINITE);
if (Error != NvOdmI2cStatus_Success)
{
return Error;
}
NvOdmOsMemset(ReadBuffer, 0, sizeof(ReadBuffer));
TransactionInfo.Address = (I2cAddr | 0x1);
TransactionInfo.Buf = ReadBuffer;
TransactionInfo.Flags = 0;
TransactionInfo.NumBytes = 1;
// Read data from ROM at the specified offset
Error = NvOdmI2cTransaction(
hOdmI2c, &TransactionInfo, 1, NVODM_QUERY_I2C_CLOCK_SPEED, NV_WAIT_INFINITE);
if (Error != NvOdmI2cStatus_Success)
{
return Error;
}
*pData = ReadBuffer[0];
return Error;
}
static NvBool
NvOdmPeripheralReadNumPeripherals(
NvOdmServicesI2cHandle hOdmI2c,
NvU8 EepromInst,
NvU8 *pNumModulePeripherals)
{
NvOdmI2cStatus Error;
NvU8 I2cAddr, Offset;
// EepromInst*2, since 7-bit addressing
I2cAddr = NVODM_QUERY_I2C_EEPROM_ADDRESS + (EepromInst << 1);
/**
* Offset to numPeripherals in NvOdmPeripheralConnectivity Structure.
* It's the first parameter after the "Entry Header."
*/
Offset = NVODM_QUERY_ENTRY_HEADER_SIZE;
Error = NvOdmPeripheralI2cRead8(
hOdmI2c, I2cAddr, Offset, pNumModulePeripherals);
if (Error != NvOdmI2cStatus_Success)
{
return NV_FALSE;
}
return NV_TRUE;
}
static NvBool
NvOdmPeripheralReadPeripheral(
NvOdmServicesI2cHandle hOdmI2c,
NvU8 EepromInst,
NvU8 Peripheral,
NvU64 *pGuid,
NvU8 *pEepromAddressListOffset,
NvU32 *pNumAddress,
NvOdmPeripheralClass *pClass)
{
NvOdmI2cStatus Error;
NvU32 i;
NvU8 ConnMemberIndex=0; // Offset to members in NvOdmPeripheralConnectivity
NvU8 I2cAddr, Offset;
NvU8 ReadBuffer[NVODM_QUERY_PERIPH_CONN_STRUCT_COMPRESSED];
NvU8 NumAddrAndClass;
// EepromInst*2, since 7-bit addressing
I2cAddr = NVODM_QUERY_I2C_EEPROM_ADDRESS + (EepromInst << 1);
/**
* Calculate offset to pGuid in NvOdmPeripheralConnectivity Structure
*
* Offset = sizeof(eeprom Entry Header) +
* sizeof(NvOdmPeripheralConnectivity)*peripheral +
* pGuid offset <-- First field, so this is 0
*/
Offset = NVODM_QUERY_ENTRY_HEADER_SIZE +
sizeof(NvOdmPeripheralConnectivity)*Peripheral;
for (i=0; i> 3) & 0x0000001F);
*pClass = (NvOdmPeripheralClass)(NumAddrAndClass & 0x00000007);
return NV_TRUE;
}
static NvBool
NvOdmPeripheralReadIoAddressData(
NvOdmServicesI2cHandle hOdmI2c,
NvU8 EepromInst,
NvU8 EepromAddressListOffset,
NvOdmIoAddress *pIoAddressEntry)
{
NvOdmI2cStatus Error;
NvU32 i;
NvU8 I2cAddr;
NvU8 ReadBuffer[NVODM_PERIPH_IO_ADDR_STRUCT_SZ_COMPRESSED];
NvU16 CompressedIoAddressEntry;
// EepromInst*2, since 7-bit addressing
I2cAddr = NVODM_QUERY_I2C_EEPROM_ADDRESS + (EepromInst << 1);
for (i=0; iInterface = (NvOdmIoModule)((CompressedIoAddressEntry >> 11) & 0x1F);
if (pIoAddressEntry->Interface != NvOdmIoModule_Gpio)
{
pIoAddressEntry->Instance = (NvU32)((CompressedIoAddressEntry >> 7) & 0xF);
pIoAddressEntry->Address = (NvU32)(CompressedIoAddressEntry & 0x7F);
}
else
{
pIoAddressEntry->Address = (NvU32)((CompressedIoAddressEntry >> 6) & 0x3F);
pIoAddressEntry->Instance = (NvU32)(CompressedIoAddressEntry & 0x3F);
}
return NV_TRUE;
}
static NvBool NvOdmPeripheralGetEntries(NvU32 *pNum)
{
NvBool RetVal;
NvBool IsMatch = NV_FALSE;
NvOdmServicesI2cHandle hOdmI2c = NULL;
NvU8 EepromInst;
// Peripheral counters
NvU8 NumPeripherals = 0;
NvU8 CurrentPeripheral = 0;
NvU32 TotalPeripherals = 0;
NvU32 StaticPeripherals;
NvU32 CurrentIoAddressNum = 0;
NvU32 TotalIoAddressEntries = 0;
NvU32 i,j;
NvU8 EepromAddressListOffset;
if (!pNum) {
return NV_FALSE;
}
// Auto-detect -- Read I2C-EEPROMs on each sub-board
hOdmI2c = NvOdmI2cOpen(NvOdmIoModule_I2c_Pmu, 0);
if (!hOdmI2c)
return NV_FALSE;
for (EepromInst=0; EepromInst < NVODM_QUERY_MAX_EEPROMS; EepromInst++)
{
RetVal = NvOdmPeripheralReadNumPeripherals(
hOdmI2c, EepromInst, &NumPeripherals);
if ( (RetVal == NV_TRUE) &&
(NumPeripherals != NVODM_QUERY_ERASED_EEPROM_VALUE) )
{
if (NumPeripherals > 0)
{
if ((NumPeripherals + TotalPeripherals) > NVODM_QUERY_MAX_PERIPHERALS)
{
NV_ASSERT( !"ERROR: s_Peripherals[] is too small to accommodate entries!" );
// Break out of loop and use static/default configuration
break;
}
for (CurrentPeripheral=0; \
CurrentPeripheral < NumPeripherals; \
CurrentPeripheral++)
{
RetVal = NvOdmPeripheralReadPeripheral(
hOdmI2c,
EepromInst,
CurrentPeripheral,
&s_Peripherals[TotalPeripherals+CurrentPeripheral].Guid,
&EepromAddressListOffset,
&s_Peripherals[TotalPeripherals+CurrentPeripheral].NumAddress,
&s_Peripherals[TotalPeripherals+CurrentPeripheral].Class);
if (RetVal == NV_FALSE)
{
NV_ASSERT(!"Unable to read EEPROM peripheral entry!");
break; // Go to next EEPROM
}
else // Process peripheral entry
{
/**
* Process NvOdmIoAddress arrays --
*
* These are separate data structures. The addressList value
* read from the EEPROM (EepromAddressListOffset) represents
* an offset address within the I2C-EEPROM. This offset value
* identifies where to find the first instance of the
* NvOdmIoAddress data.
*
* The total number of NvOdmIoAddress entries is identified
* by the numAddress variable following the addressList entry
* in EEPROM.
*
* Once the offset and number of entries are determined (from
* above NvOdmPeripheralReadPeripheral function call), a loop
* fills in entries within the fixed storage area
* (e.g., s_Peripheral_IoAddresses) and the actual
* addressList pointer is assigned a value that corresponds
* to the first entry of the current class within this array.
* In other words, there might be prior entries in the
* s_Peripheral_IoAddresses array, but the first entry
* corresponding to the current class might be the third
* element in this array. Therefore, the actual addressList
* pointer for the current NvOdmPeripheralConnectivity.addressList
* parameter would be the address of the third entry, which is
* &s_Peripheral_IoAddresses[2] in this example.
*/
// Read all of the entries and save them in s_Peripheral_IoAddresses
for (CurrentIoAddressNum=0; \
CurrentIoAddressNum < s_Peripherals[TotalPeripherals+CurrentPeripheral].NumAddress; \
CurrentIoAddressNum++)
{
if (TotalIoAddressEntries > NVODM_QUERY_MAX_IO_ADDRESSES)
{
NV_ASSERT( !"ERROR: s_Peripheral_IoAddresses[] is too small to accommodate entries!" );
// Cannot recover from this error.
NvOdmI2cClose(hOdmI2c);
return NV_FALSE;
}
RetVal = NvOdmPeripheralReadIoAddressData(
hOdmI2c,
EepromInst,
EepromAddressListOffset,
&s_Peripheral_IoAddresses[TotalIoAddressEntries+CurrentIoAddressNum]);
if (RetVal == NV_FALSE)
{
NV_ASSERT(!"Unable to read EEPROM (IoAddresses)!");
// Cannot recover from this error.
NvOdmI2cClose(hOdmI2c);
return NV_FALSE;
}
else // Process IoAddresses entry
{
/**
* Save the addressList pointer. This points to the first
* IoAddresses entry of this class. Then update the overall
* IoAddresses array counter (TotalIoAddressEntries).
*/
s_Peripherals[TotalPeripherals+CurrentPeripheral].AddressList =
&s_Peripheral_IoAddresses[TotalIoAddressEntries];
TotalIoAddressEntries += CurrentIoAddressNum;
// >-- End of NvOdmIoAddress array processing --<
}
}
}
}
}
TotalPeripherals += NumPeripherals;
}
}
// Done reading I2C-EEPROM; close it.
NvOdmI2cClose(hOdmI2c);
/**
* Append static peripheral entries (if any) to dynamic list
* read from EEPROMs (this list may also be empty), except for
* duplicate GUIDs. The dynamic list takes precedence when
* duplicate entries are found in the static list.
*/
StaticPeripherals = NV_ARRAY_SIZE(s_Peripherals_Default);
for (i=0; iBoardID = s_BoardModuleTable[CurrentBoard].BoardID;
pBoardInfo->SKU = s_BoardModuleTable[CurrentBoard].SKU;
pBoardInfo->Fab = s_BoardModuleTable[CurrentBoard].Fab;
pBoardInfo->Revision = s_BoardModuleTable[CurrentBoard].Revision;
pBoardInfo->MinorRevision = s_BoardModuleTable[CurrentBoard].MinorRevision;
return NV_TRUE;
}
}
}
// Match not found
pBoardInfo = NULL;
return NV_FALSE;
}
// This will compare the peripheral GUID against a list of known-bad GUIDs
// for certain development kit personalities, and return NV_TRUE if it is
// known to be unsupported (filtered) on the current configuration
static NvBool
NvIsFilteredPeripheral(const NvOdmPeripheralConnectivity* pConnectivity)
{
NvOdmServicesKeyListHandle hKeyList;
NvU32 Personality = 0;
NvU32 opt = 0;
NvOdmIoModule OdmModule;
const NvU32 *OdmConfigs=NULL;
NvU32 NumOdmConfigs = 0;
const NvOdmPeripheralConnectivity* pFilteredPeriph = pConnectivity;
if((!pConnectivity) || (!pConnectivity->NumAddress))
return NV_TRUE;
hKeyList = NvOdmServicesKeyListOpen();
if (hKeyList)
{
Personality =
NvOdmServicesGetKeyValue(hKeyList,
NvOdmKeyListId_ReservedBctCustomerOption);
NvOdmServicesKeyListClose(hKeyList);
Personality =
NV_DRF_VAL(TEGRA_DEVKIT, BCT_CUSTOPT, PERSONALITY, Personality);
opt =
NV_DRF_VAL(TEGRA_DEVKIT, BCT_CUSTOPT, DISPLAY_OPTION, opt);
}
if (!Personality)
Personality = TEGRA_DEVKIT_DEFAULT_PERSONALITY;
OdmModule = pFilteredPeriph->AddressList[0].Interface;
if(OdmModule != NvOdmIoModule_Gpio)
NvOdmQueryPinMux(OdmModule, &OdmConfigs, &NumOdmConfigs);
switch (OdmModule)
{
case NvOdmIoModule_Gpio:
// Filter scroll wheel when trace is enabled
if ( (pConnectivity->Guid == NV_ODM_GUID('s','c','r','o','l','w','h','l')) &&
((Personality == TEGRA_DEVKIT_BCT_CUSTOPT_0_PERSONALITY_11) ||
(Personality == TEGRA_DEVKIT_BCT_CUSTOPT_0_PERSONALITY_15) ||
(Personality == TEGRA_DEVKIT_BCT_CUSTOPT_0_PERSONALITY_C1)) )
return NV_TRUE;
else
return NV_FALSE;
default:
return NV_FALSE;
}
}
static const NvOdmPeripheralConnectivity*
NvApGetAllPeripherals (NvU32 *pNum)
{
static NvBool s_AutoDetectDone = NV_FALSE;
NvBool RetVal = NV_TRUE;
static NvU32 s_TotalPeripherals;
NvOdmBoardInfo BoardInfo;
if (!pNum)
return NULL;
if (!s_AutoDetectDone)
{
/**
* Read & cache the board ID info from the I2C-EEPROMs. This
* is necessary because once Whistler's thermal power rail is
* enabled, the ID ROMs cannot be read. NvApGetAllPeripherals()
* is called before that rail is enabled.
*/
NvOdmPeripheralGetBoardInfo(NVODM_QUERY_BOARD_ID_UNKNOWN, &BoardInfo);
RetVal = NvOdmPeripheralGetEntries(&s_TotalPeripherals);
if (RetVal == NV_FALSE)
{
*pNum = 0;
return NULL;
}
s_AutoDetectDone = NV_TRUE;
}
*pNum = s_TotalPeripherals;
return (const NvOdmPeripheralConnectivity *)s_Peripherals;
}
NvBool IsBoardTango(void)
{
NvOdmBoardInfo BoardInfo;
static NvBool s_IsBoardIdRead = NV_FALSE;
static NvBool IsBoardTango = NV_FALSE;
if (!s_IsBoardIdRead)
{
IsBoardTango = NvOdmPeripheralGetBoardInfo(EEPROM_ID_E1206, &BoardInfo);
s_IsBoardIdRead = NV_TRUE;
}
return IsBoardTango;
}
// This implements a simple linear search across the entire set of currently-
// connected peripherals to find the set of GUIDs that Match the search
// criteria. More clever implementations are possible, but given the
// relatively small search space (max dozens of peripherals) and the relative
// infrequency of enumerating peripherals, this is the easiest implementation.
const NvOdmPeripheralConnectivity *
NvOdmPeripheralGetGuid(NvU64 SearchGuid)
{
const NvOdmPeripheralConnectivity *pAllPeripherals;
NvU32 NumPeripherals;
NvU32 i;
NvBool IsE1206Board;
pAllPeripherals = NvApGetAllPeripherals(&NumPeripherals);
IsE1206Board = IsBoardTango();
if (!pAllPeripherals || !NumPeripherals)
return NULL;
if ((SearchGuid == ACCEL_TANGO_GUID) && (!IsE1206Board))
return NULL;
for (i=0; iNumAddress; i++)
{
j = offset;
do
{
switch (pSearchAttrs[j])
{
case NvOdmPeripheralSearch_IoModule:
IsMatch = (pSearchVals[j] ==
(NvU32)(pPeriph->AddressList[i].Interface));
break;
case NvOdmPeripheralSearch_Address:
IsMatch = (pSearchVals[j] == pPeriph->AddressList[i].Address);
break;
case NvOdmPeripheralSearch_Instance:
IsMatch = (pSearchVals[j] == pPeriph->AddressList[i].Instance);
break;
case NvOdmPeripheralSearch_PeripheralClass:
default:
NV_ASSERT(!"Bad Query!");
break;
}
j++;
} while (IsMatch && jClass));
break;
case NvOdmPeripheralSearch_IoModule:
IsMatch = IsBusMatch(pPeriph, pSearchAttrs, pSearchVals, i, NumAttrs);
break;
case NvOdmPeripheralSearch_Address:
case NvOdmPeripheralSearch_Instance:
// In correctly-formed searches, these parameters will be parsed by
// IsBusMatch, so we ignore them here.
break;
default:
NV_ASSERT(!"Bad search attribute!");
break;
}
}
return IsMatch;
}
NvU32
NvOdmPeripheralEnumerate(
const NvOdmPeripheralSearch *pSearchAttrs,
const NvU32 *pSearchVals,
NvU32 NumAttrs,
NvU64 *pGuidList,
NvU32 NumGuids)
{
const NvOdmPeripheralConnectivity *pAllPeripherals;
NvU32 NumPeripherals;
NvU32 Matches;
NvU32 i;
pAllPeripherals = NvApGetAllPeripherals(&NumPeripherals);
if (!pAllPeripherals || !NumPeripherals)
{
return 0;
}
if (!pSearchAttrs || !pSearchVals)
{
NumAttrs = 0;
}
for (i=0, Matches=0; i