2 * This file is part of the coreboot project.
4 * Copyright (C) 2007-2008 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);
26 static u32 GetScrubAddr_D(u32 Node);
28 static u8 isDramECCEn_D(struct DCTStatStruc *pDCTstat);
31 /* Initialize ECC modes of Integrated Dram+Memory Controllers of a network of
32 * Hammer processors. Use Dram background scrubber to fast initialize ECC bits
37 * Order that items are set:
45 * Conditions for setting background scrubber.
47 * 2. node has dram functioning (WE=RE=1)
48 * 3. all eccdimms (or bit 17 of offset 90,fn 2)
49 * 4. no chip-select gap exists
51 * The dram background scrubber is used under very controlled circumstances to
52 * initialize all the ECC bits on the DIMMs of the entire dram address map
53 * (including hidden or lost dram and dram above 4GB). We will turn the scrub
54 * rate up to maximum, which should clear 4GB of dram in about 2.7 seconds.
55 * We will activate the scrubbers of all nodes with ecc dram and let them run in
56 * parallel, thereby reducing even further the time required to condition dram.
57 * Finally, we will go through each node and either disable background scrubber,
58 * or set the scrub rate to the user setup specified rate.
60 * To allow the NB to scrub, we need to wait a time period long enough to
61 * guarantee that the NB scrubs the entire dram on its node. Do do this, we
62 * simply sample the scrub ADDR once, for an initial value, then we sample and poll until the polled value of scrub ADDR
63 * has wrapped around at least once: Scrub ADDRi+1 < Scrub ADDRi. Since we let all
64 * Nodes run in parallel, we need to guarantee that all nodes have wrapped. To do
65 * this efficiently, we need only to sample one of the nodes, the node with the
66 * largest ammount of dram populated is the one which will take the longest amount
67 * of time (the scrub rate is set to max, the same rate, on all nodes). So,
68 * during setup of scrub Base, we determine how much memory and which node has
69 * the largest memory installed.
71 * Scrubbing should not ordinarily be enabled on a Node with a chip-select gap
72 * (aka SW memhole, cs hoisting, etc..).To init ECC memory on this node, the
73 * scrubber is used in two steps. First, the Dram Limit for the node is adjusted
74 * down to the bottom of the gap, and that ECC dram is initialized. Second, the
75 * orignal Limit is restored, the Scrub base is set to 4GB, and scrubber is
76 * allowed to run until the Scrub Addr wraps around to zero.
78 u8 ECCInit_D(struct MCTStatStruc *pMCTstat, struct DCTStatStruc *pDCTstatA)
97 /* Construct these booleans, based on setup options, for easy handling
98 later in this procedure */
99 OB_NBECC = mctGet_NVbits(NV_NBECC); /* MCA ECC (MCE) enable bit */
101 OB_ECCRedir = mctGet_NVbits(NV_ECCRedir); /* ECC Redirection */
103 OB_ChipKill = mctGet_NVbits(NV_ChipKill); /* ECC Chip-kill mode */
105 OF_ScrubCTL = 0; /* Scrub CTL for Dcache, L2, and dram */
106 nvbits = mctGet_NVbits(NV_DCBKScrub);
107 mct_AdjustScrub_D(pDCTstatA, &nvbits);
108 OF_ScrubCTL |= (u32) nvbits << 16;
110 nvbits = mctGet_NVbits(NV_L2BKScrub);
111 OF_ScrubCTL |= (u32) nvbits << 8;
113 nvbits = mctGet_NVbits(NV_DramBKScrub);
114 OF_ScrubCTL |= nvbits;
118 for (Node = 0; Node < MAX_NODES_SUPPORTED; Node++) {
119 struct DCTStatStruc *pDCTstat;
120 pDCTstat = pDCTstatA + Node;
122 if (NodePresent_D(Node)) { /*If Node is present */
123 dev = pDCTstat->dev_map;
124 reg = 0x40+(Node << 3); /* Dram Base Node 0 + index */
125 val = Get_NB32(dev, reg);
127 /* WE/RE is checked */
128 if((val & 3)==3) { /* Node has dram populated */
129 /* Negate 'all nodes/dimms ECC' flag if non ecc
131 if( pDCTstat->Status & (1<<SB_ECCDIMMs)) {
132 LDramECC = isDramECCEn_D(pDCTstat);
133 if(pDCTstat->ErrCode != SC_RunningOK) {
134 pDCTstat->Status &= ~(1 << SB_ECCDIMMs);
136 pDCTstat->ErrStatus |= (1 << SB_DramECCDis);
144 if(LDramECC) { /* if ECC is enabled on this dram */
146 mct_EnableDatIntlv_D(pMCTstat, pDCTstat);
147 dev = pDCTstat->dev_nbmisc;
148 reg =0x44; /* MCA NB Configuration */
149 val = Get_NB32(dev, reg);
150 val |= 1 << 22; /* EccEn */
151 Set_NB32(dev, reg, val);
152 DCTMemClr_Init_D(pMCTstat, pDCTstat);
154 print_tx(" ECC enabled on node: ", Node);
156 } /* this node has ECC enabled dram */
159 } /* Node has Dram */
162 MCTMemClrSync_D(pMCTstat, pDCTstatA);
164 } /* if Node present */
168 pMCTstat->GStatus |= 1<<GSB_ECCDIMMs;
170 pMCTstat->GStatus &= ~(1<<GSB_ECCDIMMs);
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 */
195 /* Divisor should not be set deeper than
196 * divide by 16 when Dcache scrubber or
197 * L2 scrubber is enabled.
199 if ((OF_ScrubCTL & (0x1F << 16)) || (OF_ScrubCTL & (0x1F << 8))) {
200 val = Get_NB32(dev, 0x84);
201 if ((val & 0xE0000000) > 0x80000000) { /* Get F3x84h[31:29]ClkDivisor for C1 */
202 val &= 0x1FFFFFFF; /* If ClkDivisor is deeper than divide-by-16 */
203 val |= 0x80000000; /* set it to divide-by-16 */
204 Set_NB32(dev, 0x84, val);
207 } /* this node has ECC enabled dram */
209 } /*if Node present */
212 if(mctGet_NVbits(NV_SyncOnUnEccEn))
213 setSyncOnUnEccEn_D(pMCTstat, pDCTstatA);
216 for (Node = 0; Node < MAX_NODES_SUPPORTED; Node++) {
217 struct DCTStatStruc *pDCTstat;
218 pDCTstat = pDCTstatA + Node;
219 if (NodePresent_D(Node)) {
220 print_tx("ECCInit: Node ", Node);
221 print_tx("ECCInit: Status ", pDCTstat->Status);
222 print_tx("ECCInit: ErrStatus ", pDCTstat->ErrStatus);
223 print_tx("ECCInit: ErrCode ", pDCTstat->ErrCode);
224 print_t("ECCInit: Done\n");
231 static void setSyncOnUnEccEn_D(struct MCTStatStruc *pMCTstat,
232 struct DCTStatStruc *pDCTstatA)
239 for (Node = 0; Node < MAX_NODES_SUPPORTED; Node++) {
240 struct DCTStatStruc *pDCTstat;
241 pDCTstat = pDCTstatA + Node;
242 if (NodePresent_D(Node)) { /* If Node is present*/
243 reg = 0x40+(Node<<3); /* Dram Base Node 0 + index*/
244 val = Get_NB32(pDCTstat->dev_map, reg);
245 /*WE/RE is checked because memory config may have been*/
246 if((val & 3)==3) { /* Node has dram populated*/
247 if( isDramECCEn_D(pDCTstat)) {
248 /*if ECC is enabled on this dram*/
249 dev = pDCTstat->dev_nbmisc;
250 reg = 0x44; /* MCA NB Configuration*/
251 val = Get_NB32(dev, reg);
252 val |= (1<<SyncOnUcEccEn);
253 Set_NB32(dev, reg, val);
256 } /* if Node present*/
261 static u32 GetScrubAddr_D(u32 Node)
263 /* Get the current 40-bit Scrub ADDR address, scaled to 32-bits,
264 * of the specified Node.
271 u32 dev = PA_NBMISC(Node);
274 reg = 0x60; /* Scrub Addr High */
275 hi = Get_NB32(dev, reg);
277 regx = 0x5C; /* Scrub Addr Low */
278 lo = Get_NB32(dev, regx);
279 /* Scrub Addr High again, detect 32-bit wrap */
280 val = Get_NB32(dev, reg);
282 hi = val; /* Scrub Addr Low again, if wrap occured */
283 lo = Get_NB32(dev, regx);
289 return val; /* ScrubAddr[39:8] */
293 static u8 isDramECCEn_D(struct DCTStatStruc *pDCTstat)
298 u32 dev = pDCTstat->dev_dct;
302 if(pDCTstat->GangedMode) {
307 for(i=0; i<ch_end; i++) {
308 if(pDCTstat->DIMMValidDCT[i] > 0){
309 reg = 0x90 + i * 0x100; /* Dram Config Low */
310 val = Get_NB32(dev, reg);
311 if(val & (1<<DimmEcEn)) {
312 /* set local flag 'dram ecc capable' */