blob: 0a51b1a228daa3c3f7a002d7b0f880a216aa732b [file] [log] [blame]
Zheng Baoeb75f652010-04-23 17:32:48 +00001/*
2 * This file is part of the coreboot project.
3 *
4 * Copyright (C) 2010 Advanced Micro Devices, Inc.
Timothy Pearson730a0432015-10-16 13:51:51 -05005 * Copyright (C) 2015 Timothy Pearson <tpearson@raptorengineeringinc.com>, Raptor Engineering
Zheng Baoeb75f652010-04-23 17:32:48 +00006 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; version 2 of the License.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
Zheng Baoeb75f652010-04-23 17:32:48 +000015 */
16
17#include "mct_d.h"
18
19static void setSyncOnUnEccEn_D(struct MCTStatStruc *pMCTstat,
20 struct DCTStatStruc *pDCTstatA);
21static u8 isDramECCEn_D(struct DCTStatStruc *pDCTstat);
22
23/* Initialize ECC modes of Integrated Dram+Memory Controllers of a network of
24 * Hammer processors. Use Dram background scrubber to fast initialize ECC bits
25 * of all dram.
26 *
27 * Notes:
28 *
29 * Order that items are set:
30 * 1. eccen bit in NB
31 * 2. Scrub Base
32 * 3. Temp Node Base
33 * 4. Temp Node Limit
34 * 5. Redir bit in NB
35 * 6. Scrub CTL
36 *
37 * Conditions for setting background scrubber.
38 * 1. node is present
39 * 2. node has dram functioning (WE=RE=1)
40 * 3. all eccdimms (or bit 17 of offset 90,fn 2)
41 * 4. no chip-select gap exists
42 *
43 * The dram background scrubber is used under very controlled circumstances to
44 * initialize all the ECC bits on the DIMMs of the entire dram address map
45 * (including hidden or lost dram and dram above 4GB). We will turn the scrub
46 * rate up to maximum, which should clear 4GB of dram in about 2.7 seconds.
47 * We will activate the scrubbers of all nodes with ecc dram and let them run in
48 * parallel, thereby reducing even further the time required to condition dram.
49 * Finally, we will go through each node and either disable background scrubber,
50 * or set the scrub rate to the user setup specified rate.
51 *
52 * To allow the NB to scrub, we need to wait a time period long enough to
53 * guarantee that the NB scrubs the entire dram on its node. Do do this, we
54 * simply sample the scrub ADDR once, for an initial value, then we sample and poll until the polled value of scrub ADDR
55 * has wrapped around at least once: Scrub ADDRi+1 < Scrub ADDRi. Since we let all
Zheng Bao53b52f32010-10-09 07:18:50 +000056 * Nodes run in parallel, we need to guarantee that all nodes have wrapped. To do
Zheng Baoeb75f652010-04-23 17:32:48 +000057 * this efficiently, we need only to sample one of the nodes, the node with the
58 * largest ammount of dram populated is the one which will take the longest amount
59 * of time (the scrub rate is set to max, the same rate, on all nodes). So,
60 * during setup of scrub Base, we determine how much memory and which node has
61 * the largest memory installed.
62 *
63 * Scrubbing should not ordinarily be enabled on a Node with a chip-select gap
64 * (aka SW memhole, cs hoisting, etc..).To init ECC memory on this node, the
65 * scrubber is used in two steps. First, the Dram Limit for the node is adjusted
66 * down to the bottom of the gap, and that ECC dram is initialized. Second, the
67 * orignal Limit is restored, the Scrub base is set to 4GB, and scrubber is
68 * allowed to run until the Scrub Addr wraps around to zero.
69 */
70u8 ECCInit_D(struct MCTStatStruc *pMCTstat, struct DCTStatStruc *pDCTstatA)
71{
72 u8 Node;
73 u8 AllECC;
74 u16 OB_NBECC;
75 u32 curBase;
76 u16 OB_ECCRedir;
77 u32 LDramECC;
78 u32 OF_ScrubCTL;
79 u16 OB_ChipKill;
80 u8 MemClrECC;
81
82 u32 dev;
83 u32 reg;
84 u32 val;
85 u16 nvbits;
86
87 mctHookBeforeECC();
88
89 /* Construct these booleans, based on setup options, for easy handling
90 later in this procedure */
Timothy Pearson730a0432015-10-16 13:51:51 -050091 OB_NBECC = mctGet_NVbits(NV_NBECC); /* MCA ECC (MCE) enable bit */
Zheng Baoeb75f652010-04-23 17:32:48 +000092
Timothy Pearson730a0432015-10-16 13:51:51 -050093 OB_ECCRedir = mctGet_NVbits(NV_ECCRedir); /* ECC Redirection */
Zheng Baoeb75f652010-04-23 17:32:48 +000094
Timothy Pearson730a0432015-10-16 13:51:51 -050095 OB_ChipKill = mctGet_NVbits(NV_ChipKill); /* ECC Chip-kill mode */
96 OF_ScrubCTL = 0; /* Scrub CTL for Dcache, L2, and dram */
Zheng Baoeb75f652010-04-23 17:32:48 +000097
Timothy Pearson730a0432015-10-16 13:51:51 -050098 if (!is_fam15h()) {
99 nvbits = mctGet_NVbits(NV_DCBKScrub);
100 /* mct_AdjustScrub_D(pDCTstatA, &nvbits); */ /* Need not adjust */
101 OF_ScrubCTL |= (u32) nvbits << 16;
Zheng Baoeb75f652010-04-23 17:32:48 +0000102
Timothy Pearson730a0432015-10-16 13:51:51 -0500103 nvbits = mctGet_NVbits(NV_L2BKScrub);
104 OF_ScrubCTL |= (u32) nvbits << 8;
105 }
Zheng Baoeb75f652010-04-23 17:32:48 +0000106
107 nvbits = mctGet_NVbits(NV_DramBKScrub);
108 OF_ScrubCTL |= nvbits;
109
110 AllECC = 1;
111 MemClrECC = 0;
112 for (Node = 0; Node < MAX_NODES_SUPPORTED; Node++) {
113 struct DCTStatStruc *pDCTstat;
114 pDCTstat = pDCTstatA + Node;
115 LDramECC = 0;
116 if (NodePresent_D(Node)) { /*If Node is present */
117 dev = pDCTstat->dev_map;
118 reg = 0x40+(Node << 3); /* Dram Base Node 0 + index */
119 val = Get_NB32(dev, reg);
120
121 /* WE/RE is checked */
122 if((val & 3)==3) { /* Node has dram populated */
123 /* Negate 'all nodes/dimms ECC' flag if non ecc
124 memory populated */
125 if( pDCTstat->Status & (1<<SB_ECCDIMMs)) {
126 LDramECC = isDramECCEn_D(pDCTstat);
127 if(pDCTstat->ErrCode != SC_RunningOK) {
128 pDCTstat->Status &= ~(1 << SB_ECCDIMMs);
Marc Jones067d2232012-02-21 17:06:40 -0700129 if (!OB_NBECC) {
Zheng Baoeb75f652010-04-23 17:32:48 +0000130 pDCTstat->ErrStatus |= (1 << SB_DramECCDis);
131 }
132 AllECC = 0;
Timothy Pearson730a0432015-10-16 13:51:51 -0500133 LDramECC = 0;
Zheng Baoeb75f652010-04-23 17:32:48 +0000134 }
135 } else {
136 AllECC = 0;
137 }
138 if(LDramECC) { /* if ECC is enabled on this dram */
139 if (OB_NBECC) {
140 mct_EnableDatIntlv_D(pMCTstat, pDCTstat);
141 dev = pDCTstat->dev_nbmisc;
Timothy Pearson730a0432015-10-16 13:51:51 -0500142 reg = 0x44; /* MCA NB Configuration */
Zheng Baoeb75f652010-04-23 17:32:48 +0000143 val = Get_NB32(dev, reg);
144 val |= 1 << 22; /* EccEn */
145 Set_NB32(dev, reg, val);
146 DCTMemClr_Init_D(pMCTstat, pDCTstat);
147 MemClrECC = 1;
Marc Jones067d2232012-02-21 17:06:40 -0700148 printk(BIOS_DEBUG, " ECC enabled on node: %02x\n", Node);
Zheng Baoeb75f652010-04-23 17:32:48 +0000149 }
150 } /* this node has ECC enabled dram */
151 } else {
152 LDramECC = 0;
153 } /* Node has Dram */
154
155 if (MemClrECC) {
156 MCTMemClrSync_D(pMCTstat, pDCTstatA);
157 }
158 } /* if Node present */
159 }
160
161 if(AllECC)
162 pMCTstat->GStatus |= 1<<GSB_ECCDIMMs;
163 else
164 pMCTstat->GStatus &= ~(1<<GSB_ECCDIMMs);
165
166 /* Program the Dram BKScrub CTL to the proper (user selected) value.*/
167 /* Reset MC4_STS. */
168 for (Node = 0; Node < MAX_NODES_SUPPORTED; Node++) {
169 struct DCTStatStruc *pDCTstat;
170 pDCTstat = pDCTstatA + Node;
171 LDramECC = 0;
172 if (NodePresent_D(Node)) { /* If Node is present */
173 reg = 0x40+(Node<<3); /* Dram Base Node 0 + index */
174 val = Get_NB32(pDCTstat->dev_map, reg);
175 curBase = val & 0xffff0000;
176 /*WE/RE is checked because memory config may have been */
177 if((val & 3)==3) { /* Node has dram populated */
178 if (isDramECCEn_D(pDCTstat)) { /* if ECC is enabled on this dram */
Timothy Pearson730a0432015-10-16 13:51:51 -0500179 if (is_fam15h()) {
180 /* Erratum 505 */
181 fam15h_switch_dct(pDCTstat->dev_map, 0);
182 }
Zheng Baoeb75f652010-04-23 17:32:48 +0000183 dev = pDCTstat->dev_nbmisc;
184 val = curBase << 8;
185 if(OB_ECCRedir) {
186 val |= (1<<0); /* enable redirection */
187 }
188 Set_NB32(dev, 0x5C, val); /* Dram Scrub Addr Low */
189 val = curBase>>24;
190 Set_NB32(dev, 0x60, val); /* Dram Scrub Addr High */
191 Set_NB32(dev, 0x58, OF_ScrubCTL); /*Scrub Control */
192
Timothy Pearson730a0432015-10-16 13:51:51 -0500193 if (!is_fam15h()) {
194 /* Divisor should not be set deeper than
195 * divide by 16 when Dcache scrubber or
196 * L2 scrubber is enabled.
197 */
198 if ((OF_ScrubCTL & (0x1F << 16)) || (OF_ScrubCTL & (0x1F << 8))) {
199 val = Get_NB32(dev, 0x84);
200 if ((val & 0xE0000000) > 0x80000000) { /* Get F3x84h[31:29]ClkDivisor for C1 */
201 val &= 0x1FFFFFFF; /* If ClkDivisor is deeper than divide-by-16 */
202 val |= 0x80000000; /* set it to divide-by-16 */
203 Set_NB32(dev, 0x84, val);
204 }
Zheng Baoeb75f652010-04-23 17:32:48 +0000205 }
206 }
207 } /* this node has ECC enabled dram */
208 } /*Node has Dram */
209 } /*if Node present */
210 }
211
212 if(mctGet_NVbits(NV_SyncOnUnEccEn))
213 setSyncOnUnEccEn_D(pMCTstat, pDCTstatA);
214
215 mctHookAfterECC();
Marc Jones067d2232012-02-21 17:06:40 -0700216 for (Node = 0; Node < MAX_NODES_SUPPORTED; Node++) {
217 struct DCTStatStruc *pDCTstat;
218 pDCTstat = pDCTstatA + Node;
219 if (NodePresent_D(Node)) {
220 printk(BIOS_DEBUG, "ECCInit: Node %02x\n", Node);
221 printk(BIOS_DEBUG, "ECCInit: Status %x\n", pDCTstat->Status);
222 printk(BIOS_DEBUG, "ECCInit: ErrStatus %x\n", pDCTstat->ErrStatus);
223 printk(BIOS_DEBUG, "ECCInit: ErrCode %x\n", pDCTstat->ErrCode);
224 printk(BIOS_DEBUG, "ECCInit: Done\n");
225 }
226 }
Zheng Baoeb75f652010-04-23 17:32:48 +0000227 return MemClrECC;
228}
229
230static void setSyncOnUnEccEn_D(struct MCTStatStruc *pMCTstat,
231 struct DCTStatStruc *pDCTstatA)
232{
233 u32 Node;
234 u32 reg;
235 u32 dev;
236 u32 val;
237
238 for (Node = 0; Node < MAX_NODES_SUPPORTED; Node++) {
239 struct DCTStatStruc *pDCTstat;
240 pDCTstat = pDCTstatA + Node;
241 if (NodePresent_D(Node)) { /* If Node is present*/
242 reg = 0x40+(Node<<3); /* Dram Base Node 0 + index*/
243 val = Get_NB32(pDCTstat->dev_map, reg);
244 /*WE/RE is checked because memory config may have been*/
245 if((val & 3)==3) { /* Node has dram populated*/
246 if( isDramECCEn_D(pDCTstat)) {
247 /*if ECC is enabled on this dram*/
248 dev = pDCTstat->dev_nbmisc;
249 reg = 0x44; /* MCA NB Configuration*/
250 val = Get_NB32(dev, reg);
251 val |= (1<<SyncOnUcEccEn);
252 Set_NB32(dev, reg, val);
253 }
254 } /* Node has Dram*/
255 } /* if Node present*/
256 }
257}
258
259static u8 isDramECCEn_D(struct DCTStatStruc *pDCTstat)
260{
261 u32 reg;
262 u32 val;
263 u8 i;
264 u32 dev = pDCTstat->dev_dct;
265 u8 ch_end;
266 u8 isDimmECCEn = 0;
267
268 if(pDCTstat->GangedMode) {
269 ch_end = 1;
270 } else {
271 ch_end = 2;
272 }
273 for(i=0; i<ch_end; i++) {
274 if(pDCTstat->DIMMValidDCT[i] > 0){
Timothy Pearson730a0432015-10-16 13:51:51 -0500275 reg = 0x90; /* Dram Config Low */
276 val = Get_NB32_DCT(dev, i, reg);
Zheng Baoeb75f652010-04-23 17:32:48 +0000277 if(val & (1<<DimmEcEn)) {
278 /* set local flag 'dram ecc capable' */
279 isDimmECCEn = 1;
280 break;
281 }
282 }
283 }
284 return isDimmECCEn;
285}