Zheng Bao | eb75f65 | 2010-04-23 17:32:48 +0000 | [diff] [blame] | 1 | /* |
| 2 | * This file is part of the coreboot project. |
| 3 | * |
| 4 | * Copyright (C) 2010 Advanced Micro Devices, Inc. |
Timothy Pearson | 730a043 | 2015-10-16 13:51:51 -0500 | [diff] [blame^] | 5 | * Copyright (C) 2015 Timothy Pearson <tpearson@raptorengineeringinc.com>, Raptor Engineering |
Zheng Bao | eb75f65 | 2010-04-23 17:32:48 +0000 | [diff] [blame] | 6 | * |
| 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 Bao | eb75f65 | 2010-04-23 17:32:48 +0000 | [diff] [blame] | 15 | */ |
| 16 | |
| 17 | #include "mct_d.h" |
| 18 | |
| 19 | static void setSyncOnUnEccEn_D(struct MCTStatStruc *pMCTstat, |
| 20 | struct DCTStatStruc *pDCTstatA); |
| 21 | static 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 Bao | 53b52f3 | 2010-10-09 07:18:50 +0000 | [diff] [blame] | 56 | * Nodes run in parallel, we need to guarantee that all nodes have wrapped. To do |
Zheng Bao | eb75f65 | 2010-04-23 17:32:48 +0000 | [diff] [blame] | 57 | * 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 | */ |
| 70 | u8 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 Pearson | 730a043 | 2015-10-16 13:51:51 -0500 | [diff] [blame^] | 91 | OB_NBECC = mctGet_NVbits(NV_NBECC); /* MCA ECC (MCE) enable bit */ |
Zheng Bao | eb75f65 | 2010-04-23 17:32:48 +0000 | [diff] [blame] | 92 | |
Timothy Pearson | 730a043 | 2015-10-16 13:51:51 -0500 | [diff] [blame^] | 93 | OB_ECCRedir = mctGet_NVbits(NV_ECCRedir); /* ECC Redirection */ |
Zheng Bao | eb75f65 | 2010-04-23 17:32:48 +0000 | [diff] [blame] | 94 | |
Timothy Pearson | 730a043 | 2015-10-16 13:51:51 -0500 | [diff] [blame^] | 95 | OB_ChipKill = mctGet_NVbits(NV_ChipKill); /* ECC Chip-kill mode */ |
| 96 | OF_ScrubCTL = 0; /* Scrub CTL for Dcache, L2, and dram */ |
Zheng Bao | eb75f65 | 2010-04-23 17:32:48 +0000 | [diff] [blame] | 97 | |
Timothy Pearson | 730a043 | 2015-10-16 13:51:51 -0500 | [diff] [blame^] | 98 | 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 Bao | eb75f65 | 2010-04-23 17:32:48 +0000 | [diff] [blame] | 102 | |
Timothy Pearson | 730a043 | 2015-10-16 13:51:51 -0500 | [diff] [blame^] | 103 | nvbits = mctGet_NVbits(NV_L2BKScrub); |
| 104 | OF_ScrubCTL |= (u32) nvbits << 8; |
| 105 | } |
Zheng Bao | eb75f65 | 2010-04-23 17:32:48 +0000 | [diff] [blame] | 106 | |
| 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 Jones | 067d223 | 2012-02-21 17:06:40 -0700 | [diff] [blame] | 129 | if (!OB_NBECC) { |
Zheng Bao | eb75f65 | 2010-04-23 17:32:48 +0000 | [diff] [blame] | 130 | pDCTstat->ErrStatus |= (1 << SB_DramECCDis); |
| 131 | } |
| 132 | AllECC = 0; |
Timothy Pearson | 730a043 | 2015-10-16 13:51:51 -0500 | [diff] [blame^] | 133 | LDramECC = 0; |
Zheng Bao | eb75f65 | 2010-04-23 17:32:48 +0000 | [diff] [blame] | 134 | } |
| 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 Pearson | 730a043 | 2015-10-16 13:51:51 -0500 | [diff] [blame^] | 142 | reg = 0x44; /* MCA NB Configuration */ |
Zheng Bao | eb75f65 | 2010-04-23 17:32:48 +0000 | [diff] [blame] | 143 | 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 Jones | 067d223 | 2012-02-21 17:06:40 -0700 | [diff] [blame] | 148 | printk(BIOS_DEBUG, " ECC enabled on node: %02x\n", Node); |
Zheng Bao | eb75f65 | 2010-04-23 17:32:48 +0000 | [diff] [blame] | 149 | } |
| 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 Pearson | 730a043 | 2015-10-16 13:51:51 -0500 | [diff] [blame^] | 179 | if (is_fam15h()) { |
| 180 | /* Erratum 505 */ |
| 181 | fam15h_switch_dct(pDCTstat->dev_map, 0); |
| 182 | } |
Zheng Bao | eb75f65 | 2010-04-23 17:32:48 +0000 | [diff] [blame] | 183 | 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 Pearson | 730a043 | 2015-10-16 13:51:51 -0500 | [diff] [blame^] | 193 | 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 Bao | eb75f65 | 2010-04-23 17:32:48 +0000 | [diff] [blame] | 205 | } |
| 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 Jones | 067d223 | 2012-02-21 17:06:40 -0700 | [diff] [blame] | 216 | 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 Bao | eb75f65 | 2010-04-23 17:32:48 +0000 | [diff] [blame] | 227 | return MemClrECC; |
| 228 | } |
| 229 | |
| 230 | static 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 | |
| 259 | static 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 Pearson | 730a043 | 2015-10-16 13:51:51 -0500 | [diff] [blame^] | 275 | reg = 0x90; /* Dram Config Low */ |
| 276 | val = Get_NB32_DCT(dev, i, reg); |
Zheng Bao | eb75f65 | 2010-04-23 17:32:48 +0000 | [diff] [blame] | 277 | if(val & (1<<DimmEcEn)) { |
| 278 | /* set local flag 'dram ecc capable' */ |
| 279 | isDimmECCEn = 1; |
| 280 | break; |
| 281 | } |
| 282 | } |
| 283 | } |
| 284 | return isDimmECCEn; |
| 285 | } |