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);
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)
95 /* Construct these booleans, based on setup options, for easy handling
96 later in this procedure */
97 OB_NBECC = mctGet_NVbits(NV_NBECC); /* MCA ECC (MCE) enable bit */
99 OB_ECCRedir = mctGet_NVbits(NV_ECCRedir); /* ECC Redirection */
101 OB_ChipKill = mctGet_NVbits(NV_ChipKill); /* ECC Chip-kill mode */
103 OF_ScrubCTL = 0; /* Scrub CTL for Dcache, L2, and dram */
104 nvbits = mctGet_NVbits(NV_DCBKScrub);
105 mct_AdjustScrub_D(pDCTstatA, &nvbits);
106 OF_ScrubCTL |= (u32) nvbits << 16;
108 nvbits = mctGet_NVbits(NV_L2BKScrub);
109 OF_ScrubCTL |= (u32) nvbits << 8;
111 nvbits = mctGet_NVbits(NV_DramBKScrub);
112 OF_ScrubCTL |= nvbits;
116 print_t(" ECCInit 0 \n");
117 for (Node = 0; Node < MAX_NODES_SUPPORTED; Node++) {
118 struct DCTStatStruc *pDCTstat;
119 pDCTstat = pDCTstatA + Node;
121 if (NodePresent_D(Node)) { /*If Node is present */
122 dev = pDCTstat->dev_map;
123 reg = 0x40+(Node << 3); /* Dram Base Node 0 + index */
124 val = Get_NB32(dev, reg);
126 /* WE/RE is checked */
127 if((val & 3)==3) { /* Node has dram populated */
128 /* Negate 'all nodes/dimms ECC' flag if non ecc
130 if( pDCTstat->Status & (1<<SB_ECCDIMMs)) {
131 LDramECC = isDramECCEn_D(pDCTstat);
132 if(pDCTstat->ErrCode != SC_RunningOK) {
133 pDCTstat->Status &= ~(1 << SB_ECCDIMMs);
135 pDCTstat->ErrStatus |= (1 << SB_DramECCDis);
143 if(LDramECC) { /* if ECC is enabled on this dram */
145 mct_EnableDatIntlv_D(pMCTstat, pDCTstat);
146 dev = pDCTstat->dev_nbmisc;
147 reg =0x44; /* MCA NB Configuration */
148 val = Get_NB32(dev, reg);
149 val |= 1 << 22; /* EccEn */
150 Set_NB32(dev, reg, val);
151 DCTMemClr_Init_D(pMCTstat, pDCTstat);
153 print_tx(" ECC enabled on node: ", Node);
155 } /* this node has ECC enabled dram */
158 } /* Node has Dram */
161 MCTMemClrSync_D(pMCTstat, pDCTstatA);
163 } /* if Node present */
165 print_t(" ECCInit 1 \n");
168 pMCTstat->GStatus |= 1<<GSB_ECCDIMMs;
170 pMCTstat->GStatus &= ~(1<<GSB_ECCDIMMs);
172 print_t(" ECCInit 2 \n");
174 /* Program the Dram BKScrub CTL to the proper (user selected) value.*/
176 for (Node = 0; Node < MAX_NODES_SUPPORTED; Node++) {
177 struct DCTStatStruc *pDCTstat;
178 pDCTstat = pDCTstatA + Node;
180 if (NodePresent_D(Node)) { /* If Node is present */
181 reg = 0x40+(Node<<3); /* Dram Base Node 0 + index */
182 val = Get_NB32(pDCTstat->dev_map, reg);
183 curBase = val & 0xffff0000;
184 /*WE/RE is checked because memory config may have been */
185 if((val & 3)==3) { /* Node has dram populated */
186 if (isDramECCEn_D(pDCTstat)) { /* if ECC is enabled on this dram */
187 dev = pDCTstat->dev_nbmisc;
190 val |= (1<<0); /* enable redirection */
192 Set_NB32(dev, 0x5C, val); /* Dram Scrub Addr Low */
194 Set_NB32(dev, 0x60, val); /* Dram Scrub Addr High */
195 Set_NB32(dev, 0x58, OF_ScrubCTL); /*Scrub Control */
197 /* Divisor should not be set deeper than
198 * divide by 16 when Dcache scrubber or
199 * L2 scrubber is enabled.
201 if ((OF_ScrubCTL & (0x1F << 16)) || (OF_ScrubCTL & (0x1F << 8))) {
202 val = Get_NB32(dev, 0x84);
203 if ((val & 0xE0000000) > 0x80000000) { /* Get F3x84h[31:29]ClkDivisor for C1 */
204 val &= 0x1FFFFFFF; /* If ClkDivisor is deeper than divide-by-16 */
205 val |= 0x80000000; /* set it to divide-by-16 */
206 Set_NB32(dev, 0x84, val);
209 } /* this node has ECC enabled dram */
211 } /*if Node present */
213 print_t(" ECCInit 3 \n");
215 if(mctGet_NVbits(NV_SyncOnUnEccEn))
216 setSyncOnUnEccEn_D(pMCTstat, pDCTstatA);
223 static void setSyncOnUnEccEn_D(struct MCTStatStruc *pMCTstat,
224 struct DCTStatStruc *pDCTstatA)
231 for (Node = 0; Node < MAX_NODES_SUPPORTED; Node++) {
232 struct DCTStatStruc *pDCTstat;
233 pDCTstat = pDCTstatA + Node;
234 if (NodePresent_D(Node)) { /* If Node is present*/
235 reg = 0x40+(Node<<3); /* Dram Base Node 0 + index*/
236 val = Get_NB32(pDCTstat->dev_map, reg);
237 /*WE/RE is checked because memory config may have been*/
238 if((val & 3)==3) { /* Node has dram populated*/
239 if( isDramECCEn_D(pDCTstat)) {
240 /*if ECC is enabled on this dram*/
241 dev = pDCTstat->dev_nbmisc;
242 reg = 0x44; /* MCA NB Configuration*/
243 val = Get_NB32(dev, reg);
244 val |= (1<<SyncOnUcEccEn);
245 Set_NB32(dev, reg, val);
248 } /* if Node present*/
253 static u32 GetScrubAddr_D(u32 Node)
255 /* Get the current 40-bit Scrub ADDR address, scaled to 32-bits,
256 * of the specified Node.
263 u32 dev = PA_NBMISC(Node);
266 reg = 0x60; /* Scrub Addr High */
267 hi = Get_NB32(dev, reg);
269 regx = 0x5C; /* Scrub Addr Low */
270 lo = Get_NB32(dev, regx);
271 /* Scrub Addr High again, detect 32-bit wrap */
272 val = Get_NB32(dev, reg);
274 hi = val; /* Scrub Addr Low again, if wrap occured */
275 lo = Get_NB32(dev, regx);
281 return val; /* ScrubAddr[39:8] */
285 static u8 isDramECCEn_D(struct DCTStatStruc *pDCTstat)
290 u32 dev = pDCTstat->dev_dct;
294 if(pDCTstat->GangedMode) {
299 for(i=0; i<ch_end; i++) {
300 if(pDCTstat->DIMMValidDCT[i] > 0){
301 reg = 0x90 + i * 0x100; /* Dram Config Low */
302 val = Get_NB32(dev, reg);
303 if(val & (1<<DimmEcEn)) {
304 /* set local flag 'dram ecc capable' */