diff options
Diffstat (limited to 'src/northbridge/amd/amdmct/mct/mctecc_d.c')
-rw-r--r-- | src/northbridge/amd/amdmct/mct/mctecc_d.c | 296 |
1 files changed, 296 insertions, 0 deletions
diff --git a/src/northbridge/amd/amdmct/mct/mctecc_d.c b/src/northbridge/amd/amdmct/mct/mctecc_d.c new file mode 100644 index 0000000000..b48c1f5417 --- /dev/null +++ b/src/northbridge/amd/amdmct/mct/mctecc_d.c @@ -0,0 +1,296 @@ +/* + * This file is part of the LinuxBIOS project. + * + * Copyright (C) 2007 Advanced Micro Devices, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + + +#include "mct_d.h" + +static void setSyncOnUnEccEn_D(struct MCTStatStruc *pMCTstat, + struct DCTStatStruc *pDCTstatA); +static u32 GetScrubAddr_D(u32 Node); +static u8 isDramECCEn_D(struct DCTStatStruc *pDCTstat); + + +/* Initialize ECC modes of Integrated Dram+Memory Controllers of a network of + * Hammer processors. Use Dram background scrubber to fast initialize ECC bits + * of all dram. + * + * Notes: + * + * Order that items are set: + * 1. eccen bit in NB + * 2. Scrub Base + * 3. Temp Node Base + * 4. Temp Node Limit + * 5. Redir bit in NB + * 6. Scrub CTL + * + * Conditions for setting background scrubber. + * 1. node is present + * 2. node has dram functioning (WE=RE=1) + * 3. all eccdimms (or bit 17 of offset 90,fn 2) + * 4. no chip-select gap exists + * + * The dram background scrubber is used under very controlled circumstances to + * initialize all the ECC bits on the DIMMs of the entire dram address map + * (including hidden or lost dram and dram above 4GB). We will turn the scrub + * rate up to maximum, which should clear 4GB of dram in about 2.7 seconds. + * We will activate the scrubbers of all nodes with ecc dram and let them run in + * parallel, thereby reducing even further the time required to condition dram. + * Finally, we will go through each node and either disable background scrubber, + * or set the scrub rate to the user setup specified rate. + * + * To allow the NB to scrub, we need to wait a time period long enough to + * guarantee that the NB scrubs the entire dram on its node. Do do this, we + * simply sample the scrub ADDR once, for an initial value, then we sample and poll until the polled value of scrub ADDR + * has wrapped around at least once: Scrub ADDRi+1 < Scrub ADDRi. Since we let all + * Nodes run in parallel, we need to gaurantee that all nodes have wrapped. To do + * this efficiently, we need only to sample one of the nodes, the node with the + * largest ammount of dram populated is the one which will take the longest amount + * of time (the scrub rate is set to max, the same rate, on all nodes). So, + * during setup of scrub Base, we determine how much memory and which node has + * the largest memory installed. + * + * Scrubbing should not ordinarily be enabled on a Node with a chip-select gap + * (aka SW memhole, cs hoisting, etc..).To init ECC memory on this node, the + * scrubber is used in two steps. First, the Dram Limit for the node is adjusted + * down to the bottom of the gap, and that ECC dram is initialized. Second, the + * orignal Limit is restored, the Scrub base is set to 4GB, and scrubber is + * allowed to run until the Scrub Addr wraps around to zero. + */ +u8 ECCInit_D(struct MCTStatStruc *pMCTstat, struct DCTStatStruc *pDCTstatA) +{ + u8 Node; + u8 AllECC; + u16 OB_NBECC; + u32 curBase; + u16 OB_ECCRedir; + u32 LDramECC; + u32 OF_ScrubCTL; + u16 OB_ChipKill; + u8 MemClrECC; + + u32 dev; + u32 reg; + u32 val; + + mctHookBeforeECC(); + + /* Construct these booleans, based on setup options, for easy handling + later in this procedure */ + OB_NBECC = mctGet_NVbits(NV_NBECC); /* MCA ECC (MCE) enable bit */ + + OB_ECCRedir = mctGet_NVbits(NV_ECCRedir); /* ECC Redirection */ + + OB_ChipKill = mctGet_NVbits(NV_ChipKill); /* ECC Chip-kill mode */ + + OF_ScrubCTL = 0; /* Scrub CTL for Dcache, L2, and dram */ + val = mctGet_NVbits(NV_DCBKScrub); + mct_AdjustScrub_D(pDCTstatA, val); + OF_ScrubCTL |= val << 16; + val = mctGet_NVbits(NV_L2BKScrub); + OF_ScrubCTL |= val << 8; + + val = mctGet_NVbits(NV_DramBKScrub); + OF_ScrubCTL |= val; + + AllECC = 1; + MemClrECC = 0; + print_t(" ECCInit 0 \n"); + for (Node = 0; Node < MAX_NODES_SUPPORTED; Node++) { + struct DCTStatStruc *pDCTstat; + pDCTstat = pDCTstatA + Node; + LDramECC = 0; + if (NodePresent_D(Node)) { /*If Node is present */ + dev = pDCTstat->dev_map; + reg = 0x40+(Node << 3); /* Dram Base Node 0 + index */ + val = Get_NB32(dev, reg); + + /* WE/RE is checked */ + if((val & 3)==3) { /* Node has dram populated */ + /* Negate 'all nodes/dimms ECC' flag if non ecc + memory populated */ + if( pDCTstat->Status & (1<<SB_ECCDIMMs)) { + LDramECC = isDramECCEn_D(pDCTstat); + if(pDCTstat->ErrCode != SC_RunningOK) { + pDCTstat->Status &= ~(1 << SB_ECCDIMMs); + if (OB_NBECC) { + pDCTstat->ErrStatus |= (1 << SB_DramECCDis); + } + AllECC = 0; + LDramECC =0; + } + } else { + AllECC = 0; + } + if(LDramECC) { /* if ECC is enabled on this dram */ + if (OB_NBECC) { + mct_EnableDatIntlv_D(pMCTstat, pDCTstat); + dev = pDCTstat->dev_nbmisc; + reg =0x44; /* MCA NB Configuration */ + val = Get_NB32(dev, reg); + val |= 1 << 22; /* EccEn */ + Set_NB32(dev, reg, val); + DCTMemClr_Init_D(pMCTstat, pDCTstat); + MemClrECC = 1; + print_tx(" ECC enabled on node: ", Node); + } + } /* this node has ECC enabled dram */ + } else { + LDramECC = 0; + } /* Node has Dram */ + + if (MemClrECC) { + MCTMemClrSync_D(pMCTstat, pDCTstatA); + } + } /* if Node present */ + } + print_t(" ECCInit 1 \n"); + + if(AllECC) + pMCTstat->GStatus |= 1<<GSB_ECCDIMMs; + else + pMCTstat->GStatus &= ~(1<<GSB_ECCDIMMs); + + print_t(" ECCInit 2 \n"); + + /* Program the Dram BKScrub CTL to the proper (user selected) value.*/ + /* Reset MC4_STS. */ + for (Node = 0; Node < MAX_NODES_SUPPORTED; Node++) { + struct DCTStatStruc *pDCTstat; + pDCTstat = pDCTstatA + Node; + LDramECC = 0; + if (NodePresent_D(Node)) { /* If Node is present */ + reg = 0x40+(Node<<3); /* Dram Base Node 0 + index */ + val = Get_NB32(pDCTstat->dev_map, reg); + curBase = val & 0xffff0000; + /*WE/RE is checked because memory config may have been */ + if((val & 3)==3) { /* Node has dram populated */ + if (isDramECCEn_D(pDCTstat)) { /* if ECC is enabled on this dram */ + dev = pDCTstat->dev_nbmisc; + val = curBase << 8; + if(OB_ECCRedir) { + val |= (1<<0); /* enable redirection */ + } + Set_NB32(dev, 0x5C, val); /* Dram Scrub Addr Low */ + val = curBase>>24; + Set_NB32(dev, 0x60, val); /* Dram Scrub Addr High */ + Set_NB32(dev, 0x58, OF_ScrubCTL); /*Scrub Control */ /*set dram background scrubbing to setup value */ + } /* this node has ECC enabled dram */ + } /*Node has Dram */ + } /*if Node present */ + } + print_t(" ECCInit 3 \n"); + + if(mctGet_NVbits(NV_SyncOnUnEccEn)) + setSyncOnUnEccEn_D(pMCTstat, pDCTstatA); + + mctHookAfterECC(); + return MemClrECC; +} + + +static void setSyncOnUnEccEn_D(struct MCTStatStruc *pMCTstat, + struct DCTStatStruc *pDCTstatA) +{ + u32 Node; + u32 reg; + u32 dev; + u32 val; + + for (Node = 0; Node < MAX_NODES_SUPPORTED; Node++) { + struct DCTStatStruc *pDCTstat; + pDCTstat = pDCTstatA + Node; + if (NodePresent_D(Node)) { /* If Node is present*/ + reg = 0x40+(Node<<3); /* Dram Base Node 0 + index*/ + val = Get_NB32(pDCTstat->dev_map, reg); + /*WE/RE is checked because memory config may have been*/ + if((val & 3)==3) { /* Node has dram populated*/ + if( isDramECCEn_D(pDCTstat)) { + /*if ECC is enabled on this dram*/ + dev = pDCTstat->dev_nbmisc; + reg = 0x44; /* MCA NB Configuration*/ + val = Get_NB32(dev, reg); + val |= (1<<SyncOnUcEccEn); + Set_NB32(dev, reg, val); + } + } /* Node has Dram*/ + } /* if Node present*/ + } +} + + +static u32 GetScrubAddr_D(u32 Node) +{ + /* Get the current 40-bit Scrub ADDR address, scaled to 32-bits, + * of the specified Node. + */ + + u32 reg; + u32 regx; + u32 lo, hi; + u32 val; + u32 dev = PA_NBMISC(Node); + + + reg = 0x60; /* Scrub Addr High */ + hi = Get_NB32(dev, reg); + + regx = 0x5C; /* Scrub Addr Low */ + lo = Get_NB32(dev, regx); + /* Scrub Addr High again, detect 32-bit wrap */ + val = Get_NB32(dev, reg); + if(val != hi) { + hi = val; /* Scrub Addr Low again, if wrap occured */ + lo = Get_NB32(dev, regx); + } + + val = hi << 24; + val |= lo >> 8; + + return val; /* ScrubAddr[39:8] */ +} + + +static u8 isDramECCEn_D(struct DCTStatStruc *pDCTstat) +{ + u32 reg; + u32 val; + u8 i; + u32 dev = pDCTstat->dev_dct; + u8 ch_end; + u8 isDimmECCEn = 0; + + if(pDCTstat->GangedMode) { + ch_end = 1; + } else { + ch_end = 2; + } + for(i=0; i<ch_end; i++) { + if(pDCTstat->DIMMValidDCT[i] > 0){ + reg = 0x90 + i * 0x100; /* Dram Config Low */ + val = Get_NB32(dev, reg); + if(val & (1<<DimmEcEn)) { + /* set local flag 'dram ecc capable' */ + isDimmECCEn = 1; + break; + } + } + } + return isDimmECCEn; +} |