/******************************************************************************* NAME $RCSfile: memLib.c,v $ SUMMARY Memory Diagnostics for Crystal Beach DMA units VERSION $Revision: 1.7 $ UPDATE DATE $Date: 2010/04/28 06:22:52 $ PROGRAMMER $Author: lecter $ Copyright 2009 LSI Corporation. All Rights Reserved. DESCRIPTION: Contains the diagnostic functions for the register test and the interrupt test. REFERENCE: *******************************************************************************/ /*** INCLUDES ***/ #include #include #include #include "memLib.h" #include "commDebug.h" /*** CONSTANT DEFINATIONS ***/ #define MEM_IMC_CHAN_ADDR_MC_DOD_CH_DIMMPRESENT 0x200 #define MEM_IMC_CHAN_ADDR_MC_DOD_CH_BANKMASK 0x00000180 #define MEM_IMC_CHAN_ADDR_MC_DOD_CH_BANK_SHIFT 7 #define MEM_IMC_CHAN_ADDR_MC_DOD_CH_RANKMASK 0x00000060 #define MEM_IMC_CHAN_ADDR_MC_DOD_CH_RANK_SHIFT 5 #define MEM_IMC_CHAN_ADDR_MC_DOD_CH_ROWMASK 0x0000001C #define MEM_IMC_CHAN_ADDR_MC_DOD_CH_ROW_SHIFT 2 #define MEM_IMC_CHAN_ADDR_MC_DOD_CH_COLMASK 0x00000003 #define MEM_DATA_WIDTH_IN_BYTE 8 /*** MACRO DEFINATIONS ***/ #define M_GET_BANK_INDEX(startAddr) do { \ if((startAddr < gMemDiagStartAddr) \ || (startAddr > (gMemDiagStartAddr + gMemDiagRegionSize))) \ startAddr = gMemDiagStartAddr; \ } while (0) /*** EXTERNAL REFERENCES ***/ extern unsigned long gDiagAvailAddr; extern int raw_pci_read(unsigned int domain, unsigned int bus, unsigned int devfn, int reg, int len, u32 *val); extern int raw_pci_write(unsigned int domain, unsigned int bus, unsigned int devfn, int reg, int len, u32 val); /*** GLOBAL DATA ***/ MEM_CHAN_CFG sysMemConfig[MEM_CONFIG_MAX_CHAN]; UINT64 gMemDiagStartAddr; UINT64 gMemDiagRegionSize; UINT64 gMemDiagTOLMAddr; UINT64 gMemDiagTOHMAddr; UINT64 gMemDiagTSEGAddr; UINT64 gMemDiagTSEGSize; /*** LOCAL DATA ***/ const UINT32 imcDevFuncTrans[4] = { MEM_IMC_CHAN0_DEVICE_ID, MEM_IMC_CHAN1_DEVICE_ID, MEM_IMC_CHAN2_DEVICE_ID }; const UINT32 dimmOffsetTrans[4] = { MEM_IMC_CHAN_ADDR_MC_DOD_CH_0, MEM_IMC_CHAN_ADDR_MC_DOD_CH_1, MEM_IMC_CHAN_ADDR_MC_DOD_CH_2 }; const UINT8 bankTrans[4] = {4, 8, 16}; const UINT8 rankTrans[4] = {1, 2, 4}; const UINT8 rowTrans[8] = {12, 13, 14, 15, 16}; const UINT8 colTrans[4] = {10, 11, 12}; unsigned long gDiagAvailAddr = 0x20000000ULL; // Start from 512MB UINT8 gDMAPatternCnt=12; UINT8 gMemDLPatternCnt=7; UINT8 gMemDWPatternCnt=6; #define DMA_DIAG_PATTERN_NUM 12 #define RAM_DATALINES_PATTERNS_NUMBER 7 #define RAM_DWORD_PATTERNS_NUMBER 6 /*Crystal Beach DMA diagnostic pattern*/ UINT32 gDMAPattern[DMA_DIAG_PATTERN_NUM]={ 0xFFFFFFFF, 0x00000000, 0x55555555, 0x33333333, 0xBB44BB44, 0xFF00FF00, 0xE1E1E1E1, 0xCCCCCCCC, 0xAAAAAAAA, 0x1E1E1E1E, 0x00FF00FF, 0x44BB44BB }; /*Memory Data Line diagnostic pattern*/ UINT32 gMemDLPattern[RAM_DATALINES_PATTERNS_NUMBER] = { 0xAAAAAAAA, 0x55555555, 0x33333333, 0xCCCCCCCC, 0xF0F0F0F0, 0xFF00FF00, 0xFFFF0000 }; /*Memory DWORD diagnostic pattern*/ UINT32 gMemDWPattern[RAM_DWORD_PATTERNS_NUMBER] = { 0xFFFFFFFF, //inverse: 0x00000000 //syndrome: 0x00 0xAAAAAAAA, //inverse: 0x55555555 //syndrome: 0x00 0xE1E1E1E1, //inverse: 0x1E1E1E1E //syndrome: 0xFF 0x33333333, //inverse: 0xCCCCCCCC //syndrome: 0x99 0xFF00FF00, //inverse: 0x00FF00FF //syndrome: 0x00 0xBB44BB44 //inverse: 0x44BB44BB //syndrome: 0x66 }; /******************************************************************************* * PROCEDURE * * NAME: memWriteIMCReg * SUMMARY: This procedure will set the starting address for memory diagnostics * SCOPE: public * * DESCRIPTION: This function will set the starting address for memory diagnostics * with user specific value or default configuration * * RETURNS: * * NOTES: * */ VOID memWriteIMCReg(UINT32 idxFunc, INT32 addrReg, INT32 length, UINT32 val) { INT32 result; result = raw_pci_write(MEM_IMC_DOMAIN_ID, MEM_IMC_BUS_ID, idxFunc, addrReg, length, val); if(result) { printk("Integrated Memory Controller Control Registers read Failed\n"); ASSERT(0); } } /******************************************************************************* * PROCEDURE * * NAME: memReadIMCReg * SUMMARY: This procedure will set the starting address for memory diagnostics * SCOPE: public * * DESCRIPTION: This function will set the starting address for memory diagnostics * with user specific value or default configuration * * RETURNS: * * NOTES: * */ VOID memReadIMCReg(UINT32 idxFunc, INT32 addrReg, INT32 length, UINT32 *val) { INT32 result; result = raw_pci_read(MEM_IMC_DOMAIN_ID, MEM_IMC_BUS_ID, idxFunc, addrReg, length, val); if(result) { printk("Integrated Memory Controller Control Registers read Failed\n"); ASSERT(0); } } /******************************************************************************* * PROCEDURE * * NAME: memGetMemConfig * SUMMARY: This procedure will get memory configuration from the integrated memory controller * SCOPE: public * * DESCRIPTION: This function will read memory configuration infomation * form the pci configuration register of the integrated memory controller * * RETURNS: the total size of memory * * NOTES: * */ UINT64 memGetMemConfig(VOID) { UINT8 idxChan; UINT8 idxDimm; UINT32 bitmapMCControl; UINT32 valDimm; UINT64 sizeMem; UINT32 numRow; UINT32 numCol; UINT8 numBank; MEM_CHAN_CFG* pChanCfg; sizeMem = 0; memReadIMCReg(MEM_IMC_CTL_DEVICE_ID, MEM_IMC_CTL_MC_CONTROL, sizeof(UINT16), &bitmapMCControl); bitmapMCControl = bitmapMCControl >> 8; for(idxChan = 0; idxChan < MEM_CONFIG_MAX_CHAN; idxChan++) { pChanCfg = &sysMemConfig[idxChan]; pChanCfg->enableChan = FALSE; pChanCfg->totalDimm = 0; if(bitmapMCControl & 0x1) { for(idxDimm=0; idxDimm < MEM_CONFIG_MAX_DIMM; idxDimm++) { memReadIMCReg(imcDevFuncTrans[idxChan] + MEM_IMC_CHAN_ADDR_FUNC, dimmOffsetTrans[idxDimm], sizeof(UINT32), &valDimm); pChanCfg->dimmCfg[idxDimm].numRank = 0; pChanCfg->dimmCfg[idxDimm].sizeRank = 0; if(valDimm & MEM_IMC_CHAN_ADDR_MC_DOD_CH_DIMMPRESENT) { pChanCfg->dimmCfg[idxDimm].numRank = rankTrans[(valDimm & MEM_IMC_CHAN_ADDR_MC_DOD_CH_RANKMASK) >> MEM_IMC_CHAN_ADDR_MC_DOD_CH_RANK_SHIFT]; numBank = bankTrans[(valDimm & MEM_IMC_CHAN_ADDR_MC_DOD_CH_BANKMASK) >> MEM_IMC_CHAN_ADDR_MC_DOD_CH_BANK_SHIFT]; numRow = 1 << rowTrans[(valDimm & MEM_IMC_CHAN_ADDR_MC_DOD_CH_ROWMASK) >> MEM_IMC_CHAN_ADDR_MC_DOD_CH_ROW_SHIFT]; numCol = 1 << colTrans[valDimm & MEM_IMC_CHAN_ADDR_MC_DOD_CH_COLMASK]; pChanCfg->dimmCfg[idxDimm].sizeRank = MEM_DATA_WIDTH_IN_BYTE * numCol * numRow * numBank ; sizeMem += pChanCfg->dimmCfg[idxDimm].numRank * pChanCfg->dimmCfg[idxDimm].sizeRank; pChanCfg->totalDimm++; } } if(pChanCfg->totalDimm > 0) pChanCfg->enableChan = TRUE; } bitmapMCControl = bitmapMCControl >> 1; } return sizeMem; } /******************************************************************************* * PROCEDURE * * NAME: memShowMemConfig * SUMMARY: This procedure will get memory configuration from the integrated memory controller * SCOPE: public * * DESCRIPTION: This function will read memory configuration infomation * form the pci configuration register of the integrated memory controller * * RETURNS: the total size of memory * * NOTES: * */ VOID memShowMemConfig(VOID) { UINT8 idxChan; UINT8 idxDimm; UINT8 numDimm; UINT32 sizeDimm; UINT32 sizeMem; MEM_CHAN_CFG* pChanCfg; numDimm = 0; sizeMem = 0; printk("Memory Configuration\n"); for(idxChan = 0; idxChan < MEM_CONFIG_MAX_CHAN; idxChan++) { if(sysMemConfig[idxChan].enableChan) { pChanCfg = &sysMemConfig[idxChan]; for(idxDimm=0; idxDimm < pChanCfg->totalDimm; idxDimm++) { sizeDimm = pChanCfg->dimmCfg[idxDimm].numRank * pChanCfg->dimmCfg[idxDimm].sizeRank / MB; if(sizeDimm > 0) { printk("Chan %u Dimm %u size %uMB\n", idxChan, idxDimm, sizeDimm); numDimm ++; sizeMem += sizeDimm; } } } } printk("Total Dimm: %u Total Mem Size:%uMB\n", numDimm, sizeMem); } /******************************************************************************* * PROCEDURE * * NAME: memSetDiagStartAddr * SUMMARY: This procedure will set the starting address for memory diagnostics * SCOPE: public * * DESCRIPTION: This function will set the starting address for memory diagnostics * with user specific value or default configuration * * RETURNS: * * NOTES: * */ VOID memSetDiagStartAddr( UINT64 start_addr) { struct pci_dev* pdev = NULL; UINT32 obs_val; if(start_addr == UINT64_MAX) gMemDiagStartAddr = gDiagAvailAddr; else gMemDiagStartAddr = start_addr; if (memGetMemConfig() != 0) { gMemDiagRegionSize = memGetMemConfig() - gDiagAvailAddr; } else { gMemDiagRegionSize = 512 * MB - gDiagAvailAddr; } pdev = pci_get_device(PCI_VENDOR_ID_INTEL, MEM_JF_CORE_DID, pdev); if(pdev != NULL) { pci_read_config_dword(pdev, MEM_JF_CORE_REG_TOLM_OFFSET, &obs_val); gMemDiagTOLMAddr = obs_val & MEM_JF_CORE_REG_TOM_MASK; pci_read_config_dword(pdev, MEM_JF_CORE_REG_TOHML_OFFSET, &obs_val); gMemDiagTOHMAddr = obs_val & MEM_JF_CORE_REG_TOM_MASK; pci_read_config_dword(pdev, MEM_JF_CORE_REG_TOHMH_OFFSET, &obs_val); gMemDiagTOHMAddr = (UINT64)obs_val << 32 | (gMemDiagTOHMAddr & 0x00000000FFFFFFFF); pci_read_config_dword(pdev, MEM_JF_CORE_REG_TSEGCTRL_OFFSET, &obs_val); if(obs_val & MEM_JF_CORE_REG_TSEG_ENABLE) { gMemDiagTSEGAddr = obs_val & MEM_JF_CORE_REG_TSEG_ADDR_MASK; if((gMemDiagTSEGAddr > gMemDiagTOLMAddr) && (gMemDiagTSEGAddr < MEM_JF_CORE_REG_TOHMH_START)) { gMemDiagTSEGAddr = 0; gMemDiagTSEGSize = 0; } else { gMemDiagTSEGSize = MEM_JF_CORE_REG_TSEG_SIZE_BASE << ((obs_val & MEM_JF_CORE_REG_TSEG_SIZE_MASK) / 2); } } } if((gMemDiagStartAddr + gMemDiagRegionSize) > gMemDiagTOLMAddr) { gMemDiagRegionSize = gMemDiagTOLMAddr - gMemDiagStartAddr; if(gMemDiagTOHMAddr > 0) { gMemDiagRegionSize += gMemDiagTOHMAddr - MEM_JF_CORE_REG_TOHMH_START; } } printk("kernel size %Lx memory size %Lx\n", gMemDiagStartAddr, gMemDiagRegionSize); } VOID memWriteIOPort(UINT32 v, UINT16 port) { asm volatile("outl %0,%1" : : "a" (v), "dN" (port)); } VOID memUnlockBIOSCfg(VOID) { memWriteIOPort(0x70,0xB2); } VOID memlockBIOSCfg(VOID) { memWriteIOPort(0x71,0xB2); } UINT32 memReadCMCICnt(VOID) { UINT64 status; //rdmsrl(MSR_IA32_MCG_CAP, status); //printk("MSR_IA32_MCG_CAP %Lx\n", status); rdmsrl(MSR_IA32_MC0_STATUS + 8*4, status); return ((status >> 38) & 0x3FFF); //printk("MSR_IA32_MC8_STATUS %Lx %Lx\n", status, (status >> 38) & 0x3FFF); } static int __init memInit( void ) { memSetDiagStartAddr( gDiagAvailAddr ); return 0; } static int __init memExit( void ) { return 0; } module_init(memInit); module_exit(memExit); MODULE_AUTHOR( "merck.hung@netapp.com" ); MODULE_DESCRIPTION( "NetApp DDR3/Memory Diag Code" ); MODULE_LICENSE( "GPL" ); MODULE_VERSION( "0.1" );