2 * This file is part of the LinuxBIOS project.
4 * Copyright (C) 2007 Advanced Micro Devices, Inc.
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; version 2 of the License.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
23 static void setSyncOnUnEccEn_D(struct MCTStatStruc *pMCTstat,
24 struct DCTStatStruc *pDCTstatA);
25 static u32 GetScrubAddr_D(u32 Node);
26 static u8 isDramECCEn_D(struct DCTStatStruc *pDCTstat);
29 /* Initialize ECC modes of Integrated Dram+Memory Controllers of a network of
30 * Hammer processors. Use Dram background scrubber to fast initialize ECC bits
35 * Order that items are set:
43 * Conditions for setting background scrubber.
45 * 2. node has dram functioning (WE=RE=1)
46 * 3. all eccdimms (or bit 17 of offset 90,fn 2)
47 * 4. no chip-select gap exists
49 * The dram background scrubber is used under very controlled circumstances to
50 * initialize all the ECC bits on the DIMMs of the entire dram address map
51 * (including hidden or lost dram and dram above 4GB). We will turn the scrub
52 * rate up to maximum, which should clear 4GB of dram in about 2.7 seconds.
53 * We will activate the scrubbers of all nodes with ecc dram and let them run in
54 * parallel, thereby reducing even further the time required to condition dram.
55 * Finally, we will go through each node and either disable background scrubber,
56 * or set the scrub rate to the user setup specified rate.
58 * To allow the NB to scrub, we need to wait a time period long enough to
59 * guarantee that the NB scrubs the entire dram on its node. Do do this, we
60 * simply sample the scrub ADDR once, for an initial value, then we sample and poll until the polled value of scrub ADDR
61 * has wrapped around at least once: Scrub ADDRi+1 < Scrub ADDRi. Since we let all
62 * Nodes run in parallel, we need to gaurantee that all nodes have wrapped. To do
63 * this efficiently, we need only to sample one of the nodes, the node with the
64 * largest ammount of dram populated is the one which will take the longest amount
65 * of time (the scrub rate is set to max, the same rate, on all nodes). So,
66 * during setup of scrub Base, we determine how much memory and which node has
67 * the largest memory installed.
69 * Scrubbing should not ordinarily be enabled on a Node with a chip-select gap
70 * (aka SW memhole, cs hoisting, etc..).To init ECC memory on this node, the
71 * scrubber is used in two steps. First, the Dram Limit for the node is adjusted
72 * down to the bottom of the gap, and that ECC dram is initialized. Second, the
73 * orignal Limit is restored, the Scrub base is set to 4GB, and scrubber is
74 * allowed to run until the Scrub Addr wraps around to zero.
76 u8 ECCInit_D(struct MCTStatStruc *pMCTstat, struct DCTStatStruc *pDCTstatA)
94 /* Construct these booleans, based on setup options, for easy handling
95 later in this procedure */
96 OB_NBECC = mctGet_NVbits(NV_NBECC); /* MCA ECC (MCE) enable bit */
98 OB_ECCRedir = mctGet_NVbits(NV_ECCRedir); /* ECC Redirection */
100 OB_ChipKill = mctGet_NVbits(NV_ChipKill); /* ECC Chip-kill mode */
102 OF_ScrubCTL = 0; /* Scrub CTL for Dcache, L2, and dram */
103 val = mctGet_NVbits(NV_DCBKScrub);
104 mct_AdjustScrub_D(pDCTstatA, val);
105 OF_ScrubCTL |= val << 16;
106 val = mctGet_NVbits(NV_L2BKScrub);
107 OF_ScrubCTL |= val << 8;
109 val = mctGet_NVbits(NV_DramBKScrub);
114 print_t(" ECCInit 0 \n");
115 for (Node = 0; Node < MAX_NODES_SUPPORTED; Node++) {
116 struct DCTStatStruc *pDCTstat;
117 pDCTstat = pDCTstatA + Node;
119 if (NodePresent_D(Node)) { /*If Node is present */
120 dev = pDCTstat->dev_map;
121 reg = 0x40+(Node << 3); /* Dram Base Node 0 + index */
122 val = Get_NB32(dev, reg);
124 /* WE/RE is checked */
125 if((val & 3)==3) { /* Node has dram populated */
126 /* Negate 'all nodes/dimms ECC' flag if non ecc
128 if( pDCTstat->Status & (1<<SB_ECCDIMMs)) {
129 LDramECC = isDramECCEn_D(pDCTstat);
130 if(pDCTstat->ErrCode != SC_RunningOK) {
131 pDCTstat->Status &= ~(1 << SB_ECCDIMMs);
133 pDCTstat->ErrStatus |= (1 << SB_DramECCDis);
141 if(LDramECC) { /* if ECC is enabled on this dram */
143 mct_EnableDatIntlv_D(pMCTstat, pDCTstat);
144 dev = pDCTstat->dev_nbmisc;
145 reg =0x44; /* MCA NB Configuration */
146 val = Get_NB32(dev, reg);
147 val |= 1 << 22; /* EccEn */
148 Set_NB32(dev, reg, val);
149 DCTMemClr_Init_D(pMCTstat, pDCTstat);
151 print_tx(" ECC enabled on node: ", Node);
153 } /* this node has ECC enabled dram */
156 } /* Node has Dram */
159 MCTMemClrSync_D(pMCTstat, pDCTstatA);
161 } /* if Node present */
163 print_t(" ECCInit 1 \n");
166 pMCTstat->GStatus |= 1<<GSB_ECCDIMMs;
168 pMCTstat->GStatus &= ~(1<<GSB_ECCDIMMs);
170 print_t(" ECCInit 2 \n");
172 /* Program the Dram BKScrub CTL to the proper (user selected) value.*/
174 for (Node = 0; Node < MAX_NODES_SUPPORTED; Node++) {
175 struct DCTStatStruc *pDCTstat;
176 pDCTstat = pDCTstatA + Node;
178 if (NodePresent_D(Node)) { /* If Node is present */
179 reg = 0x40+(Node<<3); /* Dram Base Node 0 + index */
180 val = Get_NB32(pDCTstat->dev_map, reg);
181 curBase = val & 0xffff0000;
182 /*WE/RE is checked because memory config may have been */
183 if((val & 3)==3) { /* Node has dram populated */
184 if (isDramECCEn_D(pDCTstat)) { /* if ECC is enabled on this dram */
185 dev = pDCTstat->dev_nbmisc;
188 val |= (1<<0); /* enable redirection */
190 Set_NB32(dev, 0x5C, val); /* Dram Scrub Addr Low */
192 Set_NB32(dev, 0x60, val); /* Dram Scrub Addr High */
193 Set_NB32(dev, 0x58, OF_ScrubCTL); /*Scrub Control */ /*set dram background scrubbing to setup value */
194 } /* this node has ECC enabled dram */
196 } /*if Node present */
198 print_t(" ECCInit 3 \n");
200 if(mctGet_NVbits(NV_SyncOnUnEccEn))
201 setSyncOnUnEccEn_D(pMCTstat, pDCTstatA);
208 static void setSyncOnUnEccEn_D(struct MCTStatStruc *pMCTstat,
209 struct DCTStatStruc *pDCTstatA)
216 for (Node = 0; Node < MAX_NODES_SUPPORTED; Node++) {
217 struct DCTStatStruc *pDCTstat;
218 pDCTstat = pDCTstatA + Node;
219 if (NodePresent_D(Node)) { /* If Node is present*/
220 reg = 0x40+(Node<<3); /* Dram Base Node 0 + index*/
221 val = Get_NB32(pDCTstat->dev_map, reg);
222 /*WE/RE is checked because memory config may have been*/
223 if((val & 3)==3) { /* Node has dram populated*/
224 if( isDramECCEn_D(pDCTstat)) {
225 /*if ECC is enabled on this dram*/
226 dev = pDCTstat->dev_nbmisc;
227 reg = 0x44; /* MCA NB Configuration*/
228 val = Get_NB32(dev, reg);
229 val |= (1<<SyncOnUcEccEn);
230 Set_NB32(dev, reg, val);
233 } /* if Node present*/
238 static u32 GetScrubAddr_D(u32 Node)
240 /* Get the current 40-bit Scrub ADDR address, scaled to 32-bits,
241 * of the specified Node.
248 u32 dev = PA_NBMISC(Node);
251 reg = 0x60; /* Scrub Addr High */
252 hi = Get_NB32(dev, reg);
254 regx = 0x5C; /* Scrub Addr Low */
255 lo = Get_NB32(dev, regx);
256 /* Scrub Addr High again, detect 32-bit wrap */
257 val = Get_NB32(dev, reg);
259 hi = val; /* Scrub Addr Low again, if wrap occured */
260 lo = Get_NB32(dev, regx);
266 return val; /* ScrubAddr[39:8] */
270 static u8 isDramECCEn_D(struct DCTStatStruc *pDCTstat)
275 u32 dev = pDCTstat->dev_dct;
279 if(pDCTstat->GangedMode) {
284 for(i=0; i<ch_end; i++) {
285 if(pDCTstat->DIMMValidDCT[i] > 0){
286 reg = 0x90 + i * 0x100; /* Dram Config Low */
287 val = Get_NB32(dev, reg);
288 if(val & (1<<DimmEcEn)) {
289 /* set local flag 'dram ecc capable' */