// Copyright (C) 2008-2009 Apple, Inc. All rights reserved. // // This document is the property of Apple, Inc. // It is considered confidential and proprietary. // // This document may not be reproduced or transmitted in any form, // in whole or in part, without the express written permission of // Apple, Inc. // #include "WMRFeatures.h" #if WMR_BUILDING_IBOOT #include "nandid.h" #include #include #include #include #else #include "NandSpec.h" #include "NandSpecTables.h" #endif #include "WMROAM.h" #include "FIL.h" // ============================================================================= // Preprocessor Definitions #define WITH_NAND_DT_CONFIG (WITH_NAND_FILESYSTEM && WITH_DEVICETREE) // ============================================================================= // Extern global variables // ============================================================================= // Private Function Declarations static BOOL32 isSameChipId(const NandChipId chipId1, const NandChipId chipId2); static BOOL32 isSameConfigId(const NandConfigId* configId1, const NandConfigId* configId2); static const NandGeometry* lookupNandGeometry(const NandChipId chipId); static const NandTiming* lookupNandTiming(const NandConfigId* configId); static const NandBoardSupport* lookupBoardSupport(const NandConfigId* configId); static BOOL32 isValidChipId(const NandChipId chipId); static BOOL32 getChipIdAndTotalCECountAndCEMap(const NandChipId* chipIds, NandChipId* foundChipId, UInt32* totalCECount, UInt32* ceMap); static BOOL32 areChipIdsIdentical(const NandChipId* chipIds, NandChipId* chipId); static UInt32 setBitCount(UInt32 x); static BOOL32 checkCESymmetryAndGetPackageCnt(NandLandingMap *landingMap, UInt32 ceMap, UInt8* pkgCnt); // ============================================================================= // Private Global Variables #if WITH_NAND_DT_CONFIG static NandInfo currentNandInfo; static UInt32 currentCECount = 0; static UInt32 currentCEBitmap = 0; static UInt32 currentActiveDataBus = 0; #endif //WITH_NAND_DT_CONFIG static UInt32 currentCorrectableBits = 0; static UInt32 currentRefreshThreshold = 0; static BOOL32 ppnDevice = FALSE32; static BOOL32 toggleDevice = FALSE32; static BOOL32 _retire_on_invalid_refresh; static void *_ppn_feature_list = NULL; static UInt32 _ppn_feature_size = 0; static void *_dbg_chipids_list = NULL; static UInt32 _dbg_chipids_size = 0; static UInt32 _dqsHalfCycleClocks; static UInt32 _ceSetupClocks; static UInt32 _ceHoldClocks; static UInt32 _ADLClocks; static UInt32 _WHRClocks; static UInt32 _readPreClocks; static UInt32 _readPostClocks; static UInt32 _writePreClocks; static UInt32 _writePostClocks; static UInt32 _readSetupClocks; static UInt32 _readHoldClocks; static UInt32 _readDccycleClocks; static UInt32 _writeSetupClocks; static UInt32 _writeHoldClocks; static UInt32 _regDQSTimingCtrl; // ============================================================================= // Local constants // ============================================================================= // Private Function Definitions // compare the contents of a buffer to the given value, repeated static BOOL32 comparePattern(const UInt8 *buffer, UInt8 value, UInt32 length) { UInt32 idx; for (idx = 0; idx < length; ++idx) { if (buffer[idx] != value) { return FALSE32; } } return TRUE32; } static BOOL32 isSameChipId(const NandChipId chipId1, const NandChipId chipId2) { UInt32 idx; // Treat 0x00 as dont-care for (idx = 0; idx < COMPARE_NAND_CHIP_ID_SIZE; ++idx) { if (chipId1[idx] && chipId2[idx] && (chipId1[idx] != chipId2[idx])) { return FALSE32; } } return TRUE32; } static BOOL32 isSameConfigId(const NandConfigId* configId1, const NandConfigId* configId2) { if (0 == WMR_MEMCMP(configId1, configId2, sizeof(NandConfigId))) { return TRUE32; } else { return FALSE32; } } static const NandGeometry* lookupNandGeometry(const NandChipId chipId) { UInt32 count = sizeof(_nandGeometryTable) / sizeof(_nandGeometryTable[0]); UInt32 idx; for (idx = 0; idx < count; idx++) { const NandGeometry* geometry = &_nandGeometryTable[idx]; if (isSameChipId(chipId, geometry->chipId)) { return geometry; } } return NULL; } #if AND_ENABLE_NAND_DESCRIPTION_TEXT static const NandDescription* lookupNandDescription(const NandPackageId* packageId) { UInt32 idx; for (idx = 0; idx < _nandDescriptionTableSize; idx++) { const NandDescription* description = &_nandDescriptionTable[idx]; if (isSamePackageId(packageId, &(description->packageId))) { return description; } } return NULL; } #endif static const NandTiming* lookupNandTiming(const NandConfigId* configId) { UInt32 count = sizeof(_nandTimingTable) / sizeof(_nandTimingTable[0]); UInt32 idx; for (idx = 0; idx < count; idx++) { const NandTiming* timing = &_nandTimingTable[idx]; if (isSameConfigId(configId, &(timing->configId))) { return timing; } } return NULL; } static const NandBoardSupport* lookupBoardSupport(const NandConfigId* configId) { UInt32 count = sizeof(_nandBoardSupportTable) / sizeof(_nandBoardSupportTable[0]); UInt32 idx; for (idx = 0; idx < count; idx++) { const NandBoardSupport* support = &_nandBoardSupportTable[idx]; if (isSameConfigId(configId, &(support->configId))) { return support; } } return NULL; } static BOOL32 isValidChipId(NandChipId const chipId) { if (comparePattern(&chipId[0], 0x00, NAND_CHIP_ID_SIZE) || comparePattern(&chipId[0], 0xFF, NAND_CHIP_ID_SIZE)) { return FALSE32; } else { return TRUE32; } } static BOOL32 getChipIdAndTotalCECountAndCEMap(const NandChipId* chipIds, NandChipId* foundChipId, UInt32* totalCECount, UInt32* ceMap) { BOOL32 found = FALSE32; UInt32 i; *ceMap = 0; *totalCECount = 0; for (i = 0; i < NAND_MAX_CE_COUNT_TOTAL; i++) { if (isValidChipId(chipIds[i])) { if (!found) { WMR_MEMSET(foundChipId, 0, sizeof(NandChipId)); WMR_MEMCPY(foundChipId, chipIds[i], VALID_NAND_CHIP_ID_SIZE); found = TRUE32; } (*totalCECount)++; (*ceMap) |= (1 << i); } } return found; } static BOOL32 areChipIdsIdentical(const NandChipId* chipIds, NandChipId* chipId) { UInt32 i; for (i = 0; i < NAND_MAX_CE_COUNT_TOTAL; i++) { if (isValidChipId(chipIds[i]) && (!isSameChipId(*chipId, chipIds[i]))) { return FALSE32; } } return TRUE32; } static UInt32 setBitCount(UInt32 x) { // Count the ones UInt32 cnt = 0; while (x) { if (x & 1) { cnt++; } x = x >> 1; } return cnt; } static BOOL32 checkCESymmetryAndGetPackageCnt(NandLandingMap *landingMap, UInt32 ceMap, UInt8* pkgCnt) { UInt32 mask; UInt32 i = 0; UInt32 cePerPkg = setBitCount((*landingMap)[0] & ceMap); *pkgCnt = 0; do { mask = (*landingMap)[i]; if (mask & ceMap) { if (cePerPkg != setBitCount(mask & ceMap)) { return FALSE32; } (*pkgCnt)++; } i++; } while (mask != 0); return TRUE32; } // ============================================================================= // Public Function Definitions BOOL32 findNandInfo(const NandChipId* chipIds, NandInfo* nandInfo, const UInt32 numActiveDataBus) { NandConfigId detectedConfigId; NandChipId chipId; UInt32 totalCECount; UInt32 ceMap; UInt32 i; nandInfo->boardInfo = &_nandBoardInfo; nandInfo->boardSupport = NULL; nandInfo->configTiming = NULL; nandInfo->format = &_nandFormatRaw; WMR_MEMSET(&detectedConfigId, 0, sizeof(NandConfigId)); if (!getChipIdAndTotalCECountAndCEMap(chipIds, &chipId, &totalCECount, &ceMap)) { WMR_PRINT(INIT, "No NAND Detected\n"); return FALSE32; } if (!areChipIdsIdentical(chipIds, &chipId)) { WMR_PRINT(INIT, "Chip IDs not identical!\n"); return FALSE32; } if (!checkCESymmetryAndGetPackageCnt(&_nandBoardInfo.landingMap, ceMap, &(detectedConfigId.packageCnt))) { WMR_PRINT(INIT, "Chip IDs not symmetrical!\n"); return FALSE32; } detectedConfigId.connectedBusCnt = numActiveDataBus; nandInfo->geometry = lookupNandGeometry(chipId); if (!nandInfo->geometry) { WMR_PRINT(INIT, "Unknown Chip ID!\n"); return FALSE32; } for (i = 0; i < detectedConfigId.packageCnt; i++) { WMR_MEMCPY(&(detectedConfigId.packageIds[i].chipId), nandInfo->geometry->chipId, sizeof(NandChipId)); detectedConfigId.packageIds[i].ceCnt = totalCECount / detectedConfigId.packageCnt; } nandInfo->boardSupport = lookupBoardSupport(&detectedConfigId); nandInfo->configTiming = lookupNandTiming(&detectedConfigId); if (!nandInfo->boardSupport) { WMR_PRINT(INIT, "Board support not found!\n"); return FALSE32; } if (!nandInfo->configTiming) { WMR_PRINT(INIT, "Nand timing not found!\n"); return FALSE32; } #if WITH_NAND_DT_CONFIG currentNandInfo = *nandInfo; currentCECount = totalCECount; currentCEBitmap = ceMap; currentActiveDataBus = numActiveDataBus; #endif //WITH_NAND_DT_CONFIG return TRUE32; } void setECCLevels(UInt32 numCorrectableBits, UInt32 refreshThresholdBits) { currentCorrectableBits = numCorrectableBits; currentRefreshThreshold = refreshThresholdBits; } #if WITH_NAND_DT_CONFIG void addNandProperty(DTNode *node, char *name, uint32_t value) { uint32_t propSize = 0; void *propData = NULL; if (FindProperty(node, &name, &propData, &propSize) && (propSize == sizeof(value))) { *(uint32_t*)propData = value; } else { WMR_PRINT(ERROR, "Failed to find NAND property: %s\n", name); } } static void fillPpnFeatures(DTNode *node) { char *propName = "ppn-features"; void *propData = NULL; UInt32 propSize = 0; UInt32 totalSize = sizeof(_ppn_feature_size) + _ppn_feature_size; UInt8 *cursor; if (!FindProperty(node, &propName, &propData, &propSize) || (totalSize > propSize)) { WMR_PANIC("expected device tree property %s of at least %u bytes", propName, (unsigned)totalSize); } cursor = propData; WMR_MEMCPY(cursor, &_ppn_feature_size, sizeof(_ppn_feature_size)); cursor += sizeof(_ppn_feature_size); WMR_MEMCPY(cursor, _ppn_feature_list, _ppn_feature_size); cursor += _ppn_feature_size; } static void fillNandPpnProperties(DTNode *node) { const NandPpn *ppn = currentNandInfo.ppn; addNandProperty(node, "ppn-spec-version", ppn->specVersion); addNandProperty(node, "blocks-cau", ppn->blocksPerCau); addNandProperty(node, "caus-ce", ppn->causPerCe); addNandProperty(node, "slc-pages", ppn->slcPagesPerBlock); addNandProperty(node, "mlc-pages", ppn->mlcPagesPerBlock); addNandProperty(node, "match-oddeven-caus", ppn->matchOddEvenCaus); addNandProperty(node, "cau-bits", ppn->bitsPerCau); addNandProperty(node, "page-bits", ppn->bitsPerPage); addNandProperty(node, "block-bits", ppn->bitsPerBlock); addNandProperty(node, "max-transaction-size", ppn->maxTransactionSize); fillPpnFeatures(node); } static void fillDbgChipIds(DTNode *node) { char *propName = "dbg-chipids"; void *propData = NULL; UInt32 propSize = 0; UInt32 totalSize = _dbg_chipids_size; UInt8 *cursor; if (NULL == _dbg_chipids_list) { goto exit; } if (!FindProperty(node, &propName, &propData, &propSize) || (totalSize > propSize)) { WMR_PRINT(ERROR, "expected device tree property %s of at least %lu bytes", propName, totalSize); goto exit; } cursor = propData; WMR_MEMCPY(cursor, _dbg_chipids_list, _dbg_chipids_size); cursor += _dbg_chipids_size; exit: return; } void fillNandConfigProperties(DTNode *node) { uint32_t propSize = 0; void *propData = NULL; char *propName = NULL; if (0 == currentCECount) { fillDbgChipIds(node); return; } // Configuration addNandProperty(node, "#ce", currentCECount); addNandProperty(node, "ce-bitmap", currentCEBitmap); propName = "device-readid"; if (FindProperty(node, &propName, &propData, &propSize) && (propSize == NAND_CHIP_ID_SIZE)) { WMR_MEMCPY(propData, currentNandInfo.geometry->chipId, NAND_CHIP_ID_SIZE); } // Geometry addNandProperty(node, "#databus", currentActiveDataBus); addNandProperty(node, "#ce-blocks", currentNandInfo.geometry->blocksPerCS); addNandProperty(node, "#block-pages", currentNandInfo.geometry->pagesPerBlock); addNandProperty(node, "#page-bytes", currentNandInfo.geometry->dataBytesPerPage); addNandProperty(node, "#spare-bytes", currentNandInfo.geometry->spareBytesPerPage); addNandProperty(node, "#die-ce", currentNandInfo.geometry->diesPerCS); // Formatting addNandProperty(node, "bbt-format", currentNandInfo.geometry->initialBBType); addNandProperty(node, "vendor-type", currentNandInfo.boardSupport->vsType); addNandProperty(node, "ecc-threshold", currentRefreshThreshold); addNandProperty(node, "ecc-correctable", currentCorrectableBits); // Bus Timing addNandProperty(node, "read-setup-clks", _readSetupClocks); addNandProperty(node, "read-hold-clks", _readHoldClocks); addNandProperty(node, "read-dccycle-clks", _readDccycleClocks); addNandProperty(node, "write-setup-clks", _writeSetupClocks); addNandProperty(node, "write-hold-clks", _writeHoldClocks); // NAND Format parameters addNandProperty(node, "meta-per-logical-page", currentNandInfo.format->metaPerLogicalPage); addNandProperty(node, "valid-meta-per-logical-page", currentNandInfo.format->validMetaPerLogicalPage); if (currentNandInfo.format->logicalPageSize == 0) { // Use native page size UInt32 pageSize = currentNandInfo.geometry->dataBytesPerPage; addNandProperty(node, "logical-page-size", pageSize); } else { addNandProperty(node, "logical-page-size", currentNandInfo.format->logicalPageSize); } if (toggleDevice) { addNandProperty(node, "toggle-device", 1); addNandProperty(node, "enable-diff-dqs", _nandBoardInfo.useDiffDQSMode); addNandProperty(node, "enable-diff-re", _nandBoardInfo.useDiffREMode); addNandProperty(node, "enable-vref", _nandBoardInfo.useVref); addNandProperty(node, "dqs-half-cycle-clks", _dqsHalfCycleClocks); addNandProperty(node, "ce-setup-clks", _ceSetupClocks); addNandProperty(node, "ce-hold-clks", _ceHoldClocks); addNandProperty(node, "adl-clks", _ADLClocks); addNandProperty(node, "whr-clks", _WHRClocks); addNandProperty(node, "read-pre-clks", _readPreClocks); addNandProperty(node, "read-post-clks", _readPostClocks); addNandProperty(node, "write-pre-clks", _writePreClocks); addNandProperty(node, "write-post-clks", _writePostClocks); addNandProperty(node, "reg-dqs-delay", _regDQSTimingCtrl); } else { addNandProperty(node, "toggle-device", 0); } if (ppnDevice == TRUE32) { addNandProperty(node, "ppn-device", 1); fillNandPpnProperties(node); } else { addNandProperty(node, "ppn-device", 0); } addNandProperty(node, "retire-on-invalid-refresh", _retire_on_invalid_refresh); } #endif //WITH_NAND_CONFIG void reportToggleModeFMCTimingValues(UInt32 dqsHalfCycleClocks, UInt32 ceSetupClocks, UInt32 ceHoldClocks, UInt32 adlClocks, UInt32 whrClocks, UInt32 readPreClocks, UInt32 readPostClocks, UInt32 writePreClocks, UInt32 writePostClocks, UInt32 regDQSTimingCtrl) { _dqsHalfCycleClocks = dqsHalfCycleClocks; _ceSetupClocks = ceSetupClocks; _ceHoldClocks = ceHoldClocks; _ADLClocks = adlClocks; _WHRClocks = whrClocks; _readPreClocks = readPreClocks; _readPostClocks = readPostClocks; _writePreClocks = writePreClocks; _writePostClocks = writePostClocks; _regDQSTimingCtrl = regDQSTimingCtrl; } void setToggleMode() { toggleDevice = TRUE32; } void reportFMCTimingValues(UInt32 readSetupClocks, UInt32 readHoldClocks, UInt32 readDccycleClocks, UInt32 writeSetupClocks, UInt32 writeHoldClocks) { _readSetupClocks = readSetupClocks; _readHoldClocks = readHoldClocks; _readDccycleClocks = readDccycleClocks; _writeSetupClocks = writeSetupClocks; _writeHoldClocks = writeHoldClocks; } void setNandIdPpnConfig(const NandChipId* chipIds, NandInfo* nandInfo, UInt32 busses, UInt32 ces) { #if WITH_NAND_DT_CONFIG currentActiveDataBus = busses; currentNandInfo = *nandInfo; getChipIdAndTotalCECountAndCEMap(chipIds, (NandChipId*)¤tNandInfo.geometry->chipId, ¤tCECount, ¤tCEBitmap); WMR_ASSERT(ces == currentCECount); #endif //WITH_NAND_DT_CONFIG nandInfo->format = &_nandFormatPpn; ppnDevice = TRUE32; } void setPPNOptions(BOOL32 retire_on_invalid_refresh) { _retire_on_invalid_refresh = retire_on_invalid_refresh; } void reportPpnFeatures(void *list, UInt32 size) { _ppn_feature_list = list; _ppn_feature_size = size; } void reportDbgChipIds(void *list, UInt32 size) { _dbg_chipids_list = list; _dbg_chipids_size = size; } // Bitmap of valid CEs (all CEs are identical to CE0) gets cross-checked // against landing map from nand table // If a valid CE bit is set on an invalid landing bit, returns error BOOL32 checkPpnLandingMap(UInt32 validCEMap) { NandLandingMap *landingMap; UInt32 i = 0; UInt32 current; UInt32 intersect, remainder; landingMap = &_nandBoardInfo.landingMap; remainder = validCEMap; do { current = (*landingMap)[i]; intersect = remainder & current; remainder &= ~intersect; i++; } while ((current != 0) && (remainder != 0)); if (remainder != 0) { WMR_PRINT(ERROR, "Valid CE bit detected on an invalid landing bit!\n"); return FALSE32; } return TRUE32; } const NandFormat * getPPNFormat(void) { return &_nandFormatPpn; } BOOL32 targetSupportsToggle(void) { return _nandBoardInfo.useToggleMode ? TRUE32 : FALSE32; } BOOL32 targetSupportsDiffDQS(void) { return _nandBoardInfo.useDiffDQSMode ? TRUE32 : FALSE32; } BOOL32 targetSupportsDiffRE(void) { return _nandBoardInfo.useDiffREMode ? TRUE32 : FALSE32; } BOOL32 targetSupportsVREF(void) { return _nandBoardInfo.useVref ? TRUE32 : FALSE32; } BOOL32 targetSupportsSHC(void) { return _nandBoardInfo.useSingleHostChannel; } BOOL32 targetSupportsSingleCE(void) { return _nandBoardInfo.allowSingleChipEnable; } // Run only NAND chip identification int flash_nand_id(void) { OAM_Init(); if (FIL_SUCCESS != FIL_Init()) return -1; return 0; }