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 print_t(" ECCInit 0 \n");
119 for (Node = 0; Node < MAX_NODES_SUPPORTED; Node++) {
120 struct DCTStatStruc *pDCTstat;
121 pDCTstat = pDCTstatA + Node;
123 if (NodePresent_D(Node)) { /*If Node is present */
124 dev = pDCTstat->dev_map;
125 reg = 0x40+(Node << 3); /* Dram Base Node 0 + index */
126 val = Get_NB32(dev, reg);
128 /* WE/RE is checked */
129 if((val & 3)==3) { /* Node has dram populated */
130 /* Negate 'all nodes/dimms ECC' flag if non ecc
132 if( pDCTstat->Status & (1<<SB_ECCDIMMs)) {
133 LDramECC = isDramECCEn_D(pDCTstat);
134 if(pDCTstat->ErrCode != SC_RunningOK) {
135 pDCTstat->Status &= ~(1 << SB_ECCDIMMs);
137 pDCTstat->ErrStatus |= (1 << SB_DramECCDis);
145 if(LDramECC) { /* if ECC is enabled on this dram */
147 mct_EnableDatIntlv_D(pMCTstat, pDCTstat);
148 dev = pDCTstat->dev_nbmisc;
149 reg =0x44; /* MCA NB Configuration */
150 val = Get_NB32(dev, reg);
151 val |= 1 << 22; /* EccEn */
152 Set_NB32(dev, reg, val);
153 DCTMemClr_Init_D(pMCTstat, pDCTstat);
155 print_tx(" ECC enabled on node: ", Node);
157 } /* this node has ECC enabled dram */
160 } /* Node has Dram */
163 MCTMemClrSync_D(pMCTstat, pDCTstatA);
165 } /* if Node present */
167 print_t(" ECCInit 1 \n");
170 pMCTstat->GStatus |= 1<<GSB_ECCDIMMs;
172 pMCTstat->GStatus &= ~(1<<GSB_ECCDIMMs);
174 print_t(" ECCInit 2 \n");
176 /* Program the Dram BKScrub CTL to the proper (user selected) value.*/
178 for (Node = 0; Node < MAX_NODES_SUPPORTED; Node++) {
179 struct DCTStatStruc *pDCTstat;
180 pDCTstat = pDCTstatA + Node;
182 if (NodePresent_D(Node)) { /* If Node is present */
183 reg = 0x40+(Node<<3); /* Dram Base Node 0 + index */
184 val = Get_NB32(pDCTstat->dev_map, reg);
185 curBase = val & 0xffff0000;
186 /*WE/RE is checked because memory config may have been */
187 if((val & 3)==3) { /* Node has dram populated */
188 if (isDramECCEn_D(pDCTstat)) { /* if ECC is enabled on this dram */
189 dev = pDCTstat->dev_nbmisc;
192 val |= (1<<0); /* enable redirection */
194 Set_NB32(dev, 0x5C, val); /* Dram Scrub Addr Low */
196 Set_NB32(dev, 0x60, val); /* Dram Scrub Addr High */
197 Set_NB32(dev, 0x58, OF_ScrubCTL); /*Scrub Control */
199 /* Divisor should not be set deeper than
200 * divide by 16 when Dcache scrubber or
201 * L2 scrubber is enabled.
203 if ((OF_ScrubCTL & (0x1F << 16)) || (OF_ScrubCTL & (0x1F << 8))) {
204 val = Get_NB32(dev, 0x84);
205 if ((val & 0xE0000000) > 0x80000000) { /* Get F3x84h[31:29]ClkDivisor for C1 */
206 val &= 0x1FFFFFFF; /* If ClkDivisor is deeper than divide-by-16 */
207 val |= 0x80000000; /* set it to divide-by-16 */
208 Set_NB32(dev, 0x84, val);
211 } /* this node has ECC enabled dram */
213 } /*if Node present */
215 print_t(" ECCInit 3 \n");
217 if(mctGet_NVbits(NV_SyncOnUnEccEn))
218 setSyncOnUnEccEn_D(pMCTstat, pDCTstatA);
225 static void setSyncOnUnEccEn_D(struct MCTStatStruc *pMCTstat,
226 struct DCTStatStruc *pDCTstatA)
233 for (Node = 0; Node < MAX_NODES_SUPPORTED; Node++) {
234 struct DCTStatStruc *pDCTstat;
235 pDCTstat = pDCTstatA + Node;
236 if (NodePresent_D(Node)) { /* If Node is present*/
237 reg = 0x40+(Node<<3); /* Dram Base Node 0 + index*/
238 val = Get_NB32(pDCTstat->dev_map, reg);
239 /*WE/RE is checked because memory config may have been*/
240 if((val & 3)==3) { /* Node has dram populated*/
241 if( isDramECCEn_D(pDCTstat)) {
242 /*if ECC is enabled on this dram*/
243 dev = pDCTstat->dev_nbmisc;
244 reg = 0x44; /* MCA NB Configuration*/
245 val = Get_NB32(dev, reg);
246 val |= (1<<SyncOnUcEccEn);
247 Set_NB32(dev, reg, val);
250 } /* if Node present*/
255 static u32 GetScrubAddr_D(u32 Node)
257 /* Get the current 40-bit Scrub ADDR address, scaled to 32-bits,
258 * of the specified Node.
265 u32 dev = PA_NBMISC(Node);
268 reg = 0x60; /* Scrub Addr High */
269 hi = Get_NB32(dev, reg);
271 regx = 0x5C; /* Scrub Addr Low */
272 lo = Get_NB32(dev, regx);
273 /* Scrub Addr High again, detect 32-bit wrap */
274 val = Get_NB32(dev, reg);
276 hi = val; /* Scrub Addr Low again, if wrap occured */
277 lo = Get_NB32(dev, regx);
283 return val; /* ScrubAddr[39:8] */
287 static u8 isDramECCEn_D(struct DCTStatStruc *pDCTstat)
292 u32 dev = pDCTstat->dev_dct;
296 if(pDCTstat->GangedMode) {
301 for(i=0; i<ch_end; i++) {
302 if(pDCTstat->DIMMValidDCT[i] > 0){
303 reg = 0x90 + i * 0x100; /* Dram Config Low */
304 val = Get_NB32(dev, reg);
305 if(val & (1<<DimmEcEn)) {
306 /* set local flag 'dram ecc capable' */