87ac3acf3687bf0584a5c03c3d2fff6aaf2351ed
[coreboot.git] / src / northbridge / amd / amdmct / mct / mctecc_d.c
1 /*
2  * This file is part of the coreboot project.
3  *
4  * Copyright (C) 2007-2008 Advanced Micro Devices, Inc.
5  *
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.
9  *
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.
14  *
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
18  */
19
20
21 #include "mct_d.h"
22
23 static void setSyncOnUnEccEn_D(struct MCTStatStruc *pMCTstat,
24                                 struct DCTStatStruc *pDCTstatA);
25 #ifdef UNUSED_CODE
26 static u32 GetScrubAddr_D(u32 Node);
27 #endif
28 static u8 isDramECCEn_D(struct DCTStatStruc *pDCTstat);
29
30
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
33  * of all dram.
34  *
35  * Notes:
36  *
37  * Order that items are set:
38  *  1. eccen bit in NB
39  *  2. Scrub Base
40  *  3. Temp Node Base
41  *  4. Temp Node Limit
42  *  5. Redir bit in NB
43  *  6. Scrub CTL
44  *
45  * Conditions for setting background scrubber.
46  *  1. node is present
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
50  *
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.
59  *
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.
70  *
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.
77  */
78 u8 ECCInit_D(struct MCTStatStruc *pMCTstat, struct DCTStatStruc *pDCTstatA)
79 {
80         u8 Node;
81         u8 AllECC;
82         u16 OB_NBECC;
83         u32 curBase;
84         u16 OB_ECCRedir;
85         u32 LDramECC;
86         u32 OF_ScrubCTL;
87         u16 OB_ChipKill;
88         u8 MemClrECC;
89
90         u32 dev;
91         u32 reg;
92         u32 val;
93         u16 nvbits;
94
95         mctHookBeforeECC();
96
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 */
100
101         OB_ECCRedir =  mctGet_NVbits(NV_ECCRedir);      /* ECC Redirection */
102
103         OB_ChipKill = mctGet_NVbits(NV_ChipKill);       /* ECC Chip-kill mode */
104
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;
109
110         nvbits = mctGet_NVbits(NV_L2BKScrub);
111         OF_ScrubCTL |= (u32) nvbits << 8;
112
113         nvbits = mctGet_NVbits(NV_DramBKScrub);
114         OF_ScrubCTL |= nvbits;
115
116         AllECC = 1;
117         MemClrECC = 0;
118         print_t(" ECCInit 0 \n");
119         for (Node = 0; Node < MAX_NODES_SUPPORTED; Node++) {
120                 struct DCTStatStruc *pDCTstat;
121                 pDCTstat = pDCTstatA + Node;
122                 LDramECC = 0;
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);
127
128                         /* WE/RE is checked */
129                         if((val & 3)==3) {      /* Node has dram populated */
130                                 /* Negate 'all nodes/dimms ECC' flag if non ecc
131                                    memory populated */
132                                 if( pDCTstat->Status & (1<<SB_ECCDIMMs)) {
133                                         LDramECC = isDramECCEn_D(pDCTstat);
134                                         if(pDCTstat->ErrCode != SC_RunningOK) {
135                                                 pDCTstat->Status &=  ~(1 << SB_ECCDIMMs);
136                                                 if (OB_NBECC) {
137                                                         pDCTstat->ErrStatus |= (1 << SB_DramECCDis);
138                                                 }
139                                                 AllECC = 0;
140                                                 LDramECC =0;
141                                         }
142                                 } else {
143                                         AllECC = 0;
144                                 }
145                                 if(LDramECC) {  /* if ECC is enabled on this dram */
146                                         if (OB_NBECC) {
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);
154                                                 MemClrECC = 1;
155                                                 print_tx("  ECC enabled on node: ", Node);
156                                         }
157                                 }       /* this node has ECC enabled dram */
158                         } else {
159                                 LDramECC = 0;
160                         }       /* Node has Dram */
161
162                         if (MemClrECC) {
163                                 MCTMemClrSync_D(pMCTstat, pDCTstatA);
164                         }
165                 }       /* if Node present */
166         }
167         print_t(" ECCInit 1 \n");
168
169         if(AllECC)
170                 pMCTstat->GStatus |= 1<<GSB_ECCDIMMs;
171         else
172                 pMCTstat->GStatus &= ~(1<<GSB_ECCDIMMs);
173
174         print_t(" ECCInit 2 \n");
175
176         /* Program the Dram BKScrub CTL to the proper (user selected) value.*/
177         /* Reset MC4_STS. */
178         for (Node = 0; Node < MAX_NODES_SUPPORTED; Node++) {
179                 struct DCTStatStruc *pDCTstat;
180                 pDCTstat = pDCTstatA + Node;
181                 LDramECC = 0;
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;
190                                         val = curBase << 8;
191                                         if(OB_ECCRedir) {
192                                                 val |= (1<<0); /* enable redirection */
193                                         }
194                                         Set_NB32(dev, 0x5C, val); /* Dram Scrub Addr Low */
195                                         val = curBase>>24;
196                                         Set_NB32(dev, 0x60, val); /* Dram Scrub Addr High */
197                                         Set_NB32(dev, 0x58, OF_ScrubCTL);       /*Scrub Control */
198
199                                         /* Divisor should not be set deeper than
200                                          * divide by 16 when Dcache scrubber or
201                                          * L2 scrubber is enabled.
202                                          */
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);
209                                                 }
210                                         }
211                                 }       /* this node has ECC enabled dram */
212                         }       /*Node has Dram */
213                 }       /*if Node present */
214         }
215         print_t(" ECCInit 3 \n");
216
217         if(mctGet_NVbits(NV_SyncOnUnEccEn))
218                 setSyncOnUnEccEn_D(pMCTstat, pDCTstatA);
219
220         mctHookAfterECC();
221         return MemClrECC;
222 }
223
224
225 static void setSyncOnUnEccEn_D(struct MCTStatStruc *pMCTstat,
226                                 struct DCTStatStruc *pDCTstatA)
227 {
228         u32 Node;
229         u32 reg;
230         u32 dev;
231         u32 val;
232
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);
248                                 }
249                         }       /* Node has Dram*/
250                 }       /* if Node present*/
251         }
252 }
253
254 #ifdef UNUSED_CODE
255 static u32 GetScrubAddr_D(u32 Node)
256 {
257         /* Get the current 40-bit Scrub ADDR address, scaled to 32-bits,
258          * of the specified Node.
259          */
260
261         u32 reg;
262         u32 regx;
263         u32 lo, hi;
264         u32 val;
265         u32 dev = PA_NBMISC(Node);
266
267
268         reg = 0x60;             /* Scrub Addr High */
269         hi = Get_NB32(dev, reg);
270
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);
275         if(val != hi) {
276                 hi = val;       /* Scrub Addr Low again, if wrap occured */
277                 lo = Get_NB32(dev, regx);
278         }
279
280         val = hi << 24;
281         val |= lo >> 8;
282
283         return val;             /* ScrubAddr[39:8] */
284 }
285 #endif
286
287 static u8 isDramECCEn_D(struct DCTStatStruc *pDCTstat)
288 {
289         u32 reg;
290         u32 val;
291         u8 i;
292         u32 dev = pDCTstat->dev_dct;
293         u8 ch_end;
294         u8 isDimmECCEn = 0;
295
296         if(pDCTstat->GangedMode) {
297                 ch_end = 1;
298         } else {
299                 ch_end = 2;
300         }
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' */
307                                 isDimmECCEn = 1;
308                                 break;
309                         }
310                 }
311         }
312         return isDimmECCEn;
313 }