Initial AMD Barcelona support for rev Bx.
[coreboot.git] / src / northbridge / amd / amdmct / mct / mctecc_d.c
1 /*
2  * This file is part of the LinuxBIOS project.
3  *
4  * Copyright (C) 2007 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 static u32 GetScrubAddr_D(u32 Node);
26 static u8 isDramECCEn_D(struct DCTStatStruc *pDCTstat);
27
28
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
31  * of all dram.
32  *
33  * Notes:
34  *
35  * Order that items are set:
36  *  1. eccen bit in NB
37  *  2. Scrub Base
38  *  3. Temp Node Base
39  *  4. Temp Node Limit
40  *  5. Redir bit in NB
41  *  6. Scrub CTL
42  *
43  * Conditions for setting background scrubber.
44  *  1. node is present
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
48  *
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.
57  *
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.
68  *
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.
75  */
76 u8 ECCInit_D(struct MCTStatStruc *pMCTstat, struct DCTStatStruc *pDCTstatA)
77 {
78         u8 Node;
79         u8 AllECC;
80         u16 OB_NBECC;
81         u32 curBase;
82         u16 OB_ECCRedir;
83         u32 LDramECC;
84         u32 OF_ScrubCTL;
85         u16 OB_ChipKill;
86         u8 MemClrECC;
87
88         u32 dev;
89         u32 reg;
90         u32 val;
91
92         mctHookBeforeECC();
93
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 */
97
98         OB_ECCRedir =  mctGet_NVbits(NV_ECCRedir);      /* ECC Redirection */
99
100         OB_ChipKill = mctGet_NVbits(NV_ChipKill);       /* ECC Chip-kill mode */
101
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;
108
109         val = mctGet_NVbits(NV_DramBKScrub);
110         OF_ScrubCTL |= val;
111
112         AllECC = 1;
113         MemClrECC = 0;
114         print_t(" ECCInit 0 \n");
115         for (Node = 0; Node < MAX_NODES_SUPPORTED; Node++) {
116                 struct DCTStatStruc *pDCTstat;
117                 pDCTstat = pDCTstatA + Node;
118                 LDramECC = 0;
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);
123
124                         /* WE/RE is checked */
125                         if((val & 3)==3) {      /* Node has dram populated */
126                                 /* Negate 'all nodes/dimms ECC' flag if non ecc
127                                    memory populated */
128                                 if( pDCTstat->Status & (1<<SB_ECCDIMMs)) {
129                                         LDramECC = isDramECCEn_D(pDCTstat);
130                                         if(pDCTstat->ErrCode != SC_RunningOK) {
131                                                 pDCTstat->Status &=  ~(1 << SB_ECCDIMMs);
132                                                 if (OB_NBECC) {
133                                                         pDCTstat->ErrStatus |= (1 << SB_DramECCDis);
134                                                 }
135                                                 AllECC = 0;
136                                                 LDramECC =0;
137                                         }
138                                 } else {
139                                         AllECC = 0;
140                                 }
141                                 if(LDramECC) {  /* if ECC is enabled on this dram */
142                                         if (OB_NBECC) {
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);
150                                                 MemClrECC = 1;
151                                                 print_tx("  ECC enabled on node: ", Node);
152                                         }
153                                 }       /* this node has ECC enabled dram */
154                         } else {
155                                 LDramECC = 0;
156                         }       /* Node has Dram */
157
158                         if (MemClrECC) {
159                                 MCTMemClrSync_D(pMCTstat, pDCTstatA);
160                         }
161                 }       /* if Node present */
162         }
163         print_t(" ECCInit 1 \n");
164
165         if(AllECC)
166                 pMCTstat->GStatus |= 1<<GSB_ECCDIMMs;
167         else
168                 pMCTstat->GStatus &= ~(1<<GSB_ECCDIMMs);
169
170         print_t(" ECCInit 2 \n");
171
172         /* Program the Dram BKScrub CTL to the proper (user selected) value.*/
173         /* Reset MC4_STS. */
174         for (Node = 0; Node < MAX_NODES_SUPPORTED; Node++) {
175                 struct DCTStatStruc *pDCTstat;
176                 pDCTstat = pDCTstatA + Node;
177                 LDramECC = 0;
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;
186                                         val = curBase << 8;
187                                         if(OB_ECCRedir) {
188                                                 val |= (1<<0); /* enable redirection */
189                                         }
190                                         Set_NB32(dev, 0x5C, val); /* Dram Scrub Addr Low */
191                                         val = curBase>>24;
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 */
195                         }       /*Node has Dram */
196                 }       /*if Node present */
197         }
198         print_t(" ECCInit 3 \n");
199
200         if(mctGet_NVbits(NV_SyncOnUnEccEn))
201                 setSyncOnUnEccEn_D(pMCTstat, pDCTstatA);
202
203         mctHookAfterECC();
204         return MemClrECC;
205 }
206
207
208 static void setSyncOnUnEccEn_D(struct MCTStatStruc *pMCTstat,
209                                 struct DCTStatStruc *pDCTstatA)
210 {
211         u32 Node;
212         u32 reg;
213         u32 dev;
214         u32 val;
215
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);
231                                 }
232                         }       /* Node has Dram*/
233                 }       /* if Node present*/
234         }
235 }
236
237
238 static u32 GetScrubAddr_D(u32 Node)
239 {
240         /* Get the current 40-bit Scrub ADDR address, scaled to 32-bits,
241          * of the specified Node.
242          */
243
244         u32 reg;
245         u32 regx;
246         u32 lo, hi;
247         u32 val;
248         u32 dev = PA_NBMISC(Node);
249
250
251         reg = 0x60;             /* Scrub Addr High */
252         hi = Get_NB32(dev, reg);
253
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);
258         if(val != hi) {
259                 hi = val;       /* Scrub Addr Low again, if wrap occured */
260                 lo = Get_NB32(dev, regx);
261         }
262
263         val = hi << 24;
264         val |= lo >> 8;
265
266         return val;             /* ScrubAddr[39:8] */
267 }
268
269
270 static u8 isDramECCEn_D(struct DCTStatStruc *pDCTstat)
271 {
272         u32 reg;
273         u32 val;
274         u8 i;
275         u32 dev = pDCTstat->dev_dct;
276         u8 ch_end;
277         u8 isDimmECCEn = 0;
278
279         if(pDCTstat->GangedMode) {
280                 ch_end = 1;
281         } else {
282                 ch_end = 2;
283         }
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' */
290                                 isDimmECCEn = 1;
291                                 break;
292                         }
293                 }
294         }
295         return isDimmECCEn;
296 }