Nils Jacobs | 76890dd | 2010-11-01 15:20:27 +0000 | [diff] [blame] | 1 | /* |
| 2 | * This file is part of the coreboot project. |
| 3 | * |
| 4 | * Copyright (C) 2007 Advanced Micro Devices, Inc. |
| 5 | * Copyright (C) 2010 Nils Jacobs |
| 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 version 2 as |
| 9 | * published by the Free Software Foundation. |
| 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. |
Nils Jacobs | 76890dd | 2010-11-01 15:20:27 +0000 | [diff] [blame] | 15 | */ |
| 16 | |
Ronald G. Minnich | 2bb216a | 2006-01-27 23:46:30 +0000 | [diff] [blame] | 17 | #include <cpu/amd/gx2def.h> |
Nils Jacobs | 76890dd | 2010-11-01 15:20:27 +0000 | [diff] [blame] | 18 | #include <spd.h> |
| 19 | |
| 20 | static const unsigned char NumColAddr[] = { |
| 21 | 0x00, 0x10, 0x11, 0x00, 0x00, 0x00, 0x00, 0x07, |
| 22 | 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, |
| 23 | }; |
| 24 | |
Edward O'Callaghan | d2a7523 | 2014-06-29 02:27:19 +1000 | [diff] [blame] | 25 | static void __attribute__((noreturn)) hcf(void) |
Nils Jacobs | 76890dd | 2010-11-01 15:20:27 +0000 | [diff] [blame] | 26 | { |
Nils Jacobs | a1e2c56 | 2010-11-05 00:13:14 +0000 | [diff] [blame] | 27 | printk(BIOS_EMERG, "DIE\n"); |
Nils Jacobs | 76890dd | 2010-11-01 15:20:27 +0000 | [diff] [blame] | 28 | /* this guarantees we flush the UART fifos (if any) and also |
| 29 | * ensures that things, in general, keep going so no debug output |
| 30 | * is lost |
| 31 | */ |
| 32 | while (1) |
Nils Jacobs | a1e2c56 | 2010-11-05 00:13:14 +0000 | [diff] [blame] | 33 | printk(BIOS_EMERG, (0)); |
Nils Jacobs | 76890dd | 2010-11-01 15:20:27 +0000 | [diff] [blame] | 34 | } |
| 35 | |
| 36 | static void auto_size_dimm(unsigned int dimm) |
| 37 | { |
| 38 | uint32_t dimm_setting; |
| 39 | uint16_t dimm_size; |
| 40 | uint8_t spd_byte; |
| 41 | msr_t msr; |
| 42 | |
| 43 | dimm_setting = 0; |
| 44 | |
Nils Jacobs | a1e2c56 | 2010-11-05 00:13:14 +0000 | [diff] [blame] | 45 | printk(BIOS_DEBUG, "Check present\n"); |
Nils Jacobs | 76890dd | 2010-11-01 15:20:27 +0000 | [diff] [blame] | 46 | /* Check that we have a dimm */ |
| 47 | if (spd_read_byte(dimm, SPD_MEMORY_TYPE) == 0xFF) { |
| 48 | return; |
| 49 | } |
| 50 | |
Nils Jacobs | a1e2c56 | 2010-11-05 00:13:14 +0000 | [diff] [blame] | 51 | printk(BIOS_DEBUG, "MODBANKS\n"); |
Nils Jacobs | 76890dd | 2010-11-01 15:20:27 +0000 | [diff] [blame] | 52 | /* Field: Module Banks per DIMM */ |
| 53 | /* EEPROM byte usage: (5) Number of DIMM Banks */ |
| 54 | spd_byte = spd_read_byte(dimm, SPD_NUM_DIMM_BANKS); |
| 55 | if ((MIN_MOD_BANKS > spd_byte) || (spd_byte > MAX_MOD_BANKS)) { |
Nils Jacobs | a1e2c56 | 2010-11-05 00:13:14 +0000 | [diff] [blame] | 56 | printk(BIOS_EMERG, "Number of module banks not compatible\n"); |
Nils Jacobs | 76890dd | 2010-11-01 15:20:27 +0000 | [diff] [blame] | 57 | post_code(ERROR_BANK_SET); |
| 58 | hcf(); |
| 59 | } |
| 60 | dimm_setting |= (spd_byte >> 1) << CF07_UPPER_D0_MB_SHIFT; |
Nils Jacobs | 76890dd | 2010-11-01 15:20:27 +0000 | [diff] [blame] | 61 | |
Nils Jacobs | a1e2c56 | 2010-11-05 00:13:14 +0000 | [diff] [blame] | 62 | printk(BIOS_DEBUG, "FIELDBANKS\n"); |
Nils Jacobs | 76890dd | 2010-11-01 15:20:27 +0000 | [diff] [blame] | 63 | /* Field: Banks per SDRAM device */ |
| 64 | /* EEPROM byte usage: (17) Number of Banks on SDRAM Device */ |
| 65 | spd_byte = spd_read_byte(dimm, SPD_NUM_BANKS_PER_SDRAM); |
| 66 | if ((MIN_DEV_BANKS > spd_byte) || (spd_byte > MAX_DEV_BANKS)) { |
Nils Jacobs | a1e2c56 | 2010-11-05 00:13:14 +0000 | [diff] [blame] | 67 | printk(BIOS_EMERG, "Number of device banks not compatible\n"); |
Nils Jacobs | 76890dd | 2010-11-01 15:20:27 +0000 | [diff] [blame] | 68 | post_code(ERROR_BANK_SET); |
| 69 | hcf(); |
| 70 | } |
| 71 | dimm_setting |= (spd_byte >> 2) << CF07_UPPER_D0_CB_SHIFT; |
Nils Jacobs | 76890dd | 2010-11-01 15:20:27 +0000 | [diff] [blame] | 72 | |
Nils Jacobs | a1e2c56 | 2010-11-05 00:13:14 +0000 | [diff] [blame] | 73 | printk(BIOS_DEBUG, "SPDNUMROWS\n"); |
Nils Jacobs | 5beac7f | 2010-11-03 13:19:50 +0000 | [diff] [blame] | 74 | /* Field: DIMM size |
| 75 | * EEPROM byte usage: |
| 76 | * (3) Number of Row Addresses |
| 77 | * (4) Number of Column Addresses |
| 78 | * (5) Number of DIMM Banks |
| 79 | * (31) Module Bank Density |
| 80 | * Size = Module Density * Module Banks |
Nils Jacobs | 76890dd | 2010-11-01 15:20:27 +0000 | [diff] [blame] | 81 | */ |
| 82 | if ((spd_read_byte(dimm, SPD_NUM_ROWS) & 0xF0) |
| 83 | || (spd_read_byte(dimm, SPD_NUM_COLUMNS) & 0xF0)) { |
Nils Jacobs | a1e2c56 | 2010-11-05 00:13:14 +0000 | [diff] [blame] | 84 | printk(BIOS_EMERG, "Asymmetric DIMM not compatible\n"); |
Nils Jacobs | 76890dd | 2010-11-01 15:20:27 +0000 | [diff] [blame] | 85 | post_code(ERROR_UNSUPPORTED_DIMM); |
| 86 | hcf(); |
| 87 | } |
Nils Jacobs | 76890dd | 2010-11-01 15:20:27 +0000 | [diff] [blame] | 88 | |
Nils Jacobs | a1e2c56 | 2010-11-05 00:13:14 +0000 | [diff] [blame] | 89 | printk(BIOS_DEBUG, "SPDBANKDENSITY\n"); |
Nils Jacobs | 76890dd | 2010-11-01 15:20:27 +0000 | [diff] [blame] | 90 | dimm_size = spd_read_byte(dimm, SPD_BANK_DENSITY); |
Nils Jacobs | a1e2c56 | 2010-11-05 00:13:14 +0000 | [diff] [blame] | 91 | printk(BIOS_DEBUG, "DIMMSIZE\n"); |
Nils Jacobs | 76890dd | 2010-11-01 15:20:27 +0000 | [diff] [blame] | 92 | dimm_size |= (dimm_size << 8); /* align so 1GB(bit0) is bit 8, this is a little weird to get gcc to not optimize this out */ |
| 93 | dimm_size &= 0x01FC; /* and off 2GB DIMM size : not supported and the 1GB size we just moved up to bit 8 as well as all the extra on top */ |
| 94 | |
Nils Jacobs | 5beac7f | 2010-11-03 13:19:50 +0000 | [diff] [blame] | 95 | /* Module Density * Module Banks */ |
Nils Jacobs | 76890dd | 2010-11-01 15:20:27 +0000 | [diff] [blame] | 96 | dimm_size <<= (dimm_setting >> CF07_UPPER_D0_MB_SHIFT) & 1; /* shift to multiply by # DIMM banks */ |
Nils Jacobs | a1e2c56 | 2010-11-05 00:13:14 +0000 | [diff] [blame] | 97 | printk(BIOS_DEBUG, "BEFORT CTZ\n"); |
Nils Jacobs | 76890dd | 2010-11-01 15:20:27 +0000 | [diff] [blame] | 98 | dimm_size = __builtin_ctz(dimm_size); |
Nils Jacobs | a1e2c56 | 2010-11-05 00:13:14 +0000 | [diff] [blame] | 99 | printk(BIOS_DEBUG, "TEST DIMM SIZE>7\n"); |
Nils Jacobs | 76890dd | 2010-11-01 15:20:27 +0000 | [diff] [blame] | 100 | if (dimm_size > 7) { /* 7 is 512MB only support 512MB per DIMM */ |
Nils Jacobs | a1e2c56 | 2010-11-05 00:13:14 +0000 | [diff] [blame] | 101 | printk(BIOS_EMERG, "Only support up to 512MB per DIMM\n"); |
Nils Jacobs | 76890dd | 2010-11-01 15:20:27 +0000 | [diff] [blame] | 102 | post_code(ERROR_DENSITY_DIMM); |
| 103 | hcf(); |
| 104 | } |
| 105 | dimm_setting |= dimm_size << CF07_UPPER_D0_SZ_SHIFT; |
Nils Jacobs | a1e2c56 | 2010-11-05 00:13:14 +0000 | [diff] [blame] | 106 | printk(BIOS_DEBUG, "PAGESIZE\n"); |
Nils Jacobs | 76890dd | 2010-11-01 15:20:27 +0000 | [diff] [blame] | 107 | |
Nils Jacobs | 5beac7f | 2010-11-03 13:19:50 +0000 | [diff] [blame] | 108 | /* |
| 109 | * Field: PAGE size |
| 110 | * EEPROM byte usage: (4) Number of Column Addresses |
| 111 | * PageSize = 2^# Column Addresses * Data width in bytes |
| 112 | * (should be 8bytes for a normal DIMM) |
| 113 | * |
| 114 | * But this really works by magic. |
| 115 | * If ma[11:0] is the memory address pins, and pa[13:0] is the physical column |
| 116 | * address that MC generates, here is how the MC assigns the pa onto the |
| 117 | * ma pins: |
| 118 | * |
| 119 | * ma 11 10 09 08 07 06 05 04 03 02 01 00 |
| 120 | * --------------------------------------- |
| 121 | * pa 09 08 07 06 05 04 03 (7 col addr bits = 1K page size) |
| 122 | * pa 10 09 08 07 06 05 04 03 (8 col addr bits = 2K page size) |
| 123 | * pa 11 10 09 08 07 06 05 04 03 (9 col addr bits = 4K page size) |
| 124 | * pa 12 11 10 09 08 07 06 05 04 03 (10 col addr bits = 8K page size) |
| 125 | * pa 13 AP 12 11 10 09 08 07 06 05 04 03 (11 col addr bits = 16K page size) |
| 126 | * |
| 127 | * (AP = autoprecharge bit) |
| 128 | * |
| 129 | * Remember that pa[2:0] are zeroed out since it's a 64-bit data bus (8 bytes), |
| 130 | * so lower 3 address bits are dont_cares. So from the table above, |
| 131 | * it's easier to see what the old code is doing: if for example, |
| 132 | * #col_addr_bits=7(06h), it adds 3 to get 10, then does 2^10=1K. |
| 133 | */ |
Nils Jacobs | 76890dd | 2010-11-01 15:20:27 +0000 | [diff] [blame] | 134 | |
| 135 | spd_byte = NumColAddr[spd_read_byte(dimm, SPD_NUM_COLUMNS) & 0xF]; |
Nils Jacobs | a1e2c56 | 2010-11-05 00:13:14 +0000 | [diff] [blame] | 136 | printk(BIOS_DEBUG, "MAXCOLADDR\n"); |
Nils Jacobs | 76890dd | 2010-11-01 15:20:27 +0000 | [diff] [blame] | 137 | if (spd_byte > MAX_COL_ADDR) { |
Nils Jacobs | a1e2c56 | 2010-11-05 00:13:14 +0000 | [diff] [blame] | 138 | printk(BIOS_EMERG, "DIMM page size not compatible\n"); |
Nils Jacobs | 76890dd | 2010-11-01 15:20:27 +0000 | [diff] [blame] | 139 | post_code(ERROR_SET_PAGE); |
| 140 | hcf(); |
| 141 | } |
Nils Jacobs | a1e2c56 | 2010-11-05 00:13:14 +0000 | [diff] [blame] | 142 | printk(BIOS_DEBUG, ">11address test\n"); |
Nils Jacobs | 76890dd | 2010-11-01 15:20:27 +0000 | [diff] [blame] | 143 | spd_byte -= 7; |
| 144 | if (spd_byte > 4) { /* if the value is above 4 it means >11 col address lines */ |
| 145 | spd_byte = 7; /* which means >16k so set to disabled */ |
| 146 | } |
| 147 | dimm_setting |= spd_byte << CF07_UPPER_D0_PSZ_SHIFT; /* 0=1k,1=2k,2=4k,etc */ |
| 148 | |
Nils Jacobs | a1e2c56 | 2010-11-05 00:13:14 +0000 | [diff] [blame] | 149 | printk(BIOS_DEBUG, "RDMSR CF07\n"); |
Nils Jacobs | 76890dd | 2010-11-01 15:20:27 +0000 | [diff] [blame] | 150 | msr = rdmsr(MC_CF07_DATA); |
Nils Jacobs | a1e2c56 | 2010-11-05 00:13:14 +0000 | [diff] [blame] | 151 | printk(BIOS_DEBUG, "WRMSR CF07\n"); |
Nils Jacobs | 76890dd | 2010-11-01 15:20:27 +0000 | [diff] [blame] | 152 | if (dimm == DIMM0) { |
| 153 | msr.hi &= 0xFFFF0000; |
| 154 | msr.hi |= dimm_setting; |
| 155 | } else { |
| 156 | msr.hi &= 0x0000FFFF; |
| 157 | msr.hi |= dimm_setting << 16; |
| 158 | } |
| 159 | wrmsr(MC_CF07_DATA, msr); |
Nils Jacobs | a1e2c56 | 2010-11-05 00:13:14 +0000 | [diff] [blame] | 160 | printk(BIOS_DEBUG, "ALL DONE\n"); |
Nils Jacobs | 76890dd | 2010-11-01 15:20:27 +0000 | [diff] [blame] | 161 | } |
| 162 | |
| 163 | static void checkDDRMax(void) |
| 164 | { |
| 165 | uint8_t spd_byte0, spd_byte1; |
| 166 | uint16_t speed; |
| 167 | |
| 168 | /* PC133 identifier */ |
| 169 | spd_byte0 = spd_read_byte(DIMM0, SPD_MIN_CYCLE_TIME_AT_CAS_MAX); |
| 170 | if (spd_byte0 == 0xFF) { |
| 171 | spd_byte0 = 0; |
| 172 | } |
| 173 | spd_byte1 = spd_read_byte(DIMM1, SPD_MIN_CYCLE_TIME_AT_CAS_MAX); |
| 174 | if (spd_byte1 == 0xFF) { |
| 175 | spd_byte1 = 0; |
| 176 | } |
| 177 | |
| 178 | /* Use the slowest DIMM */ |
| 179 | if (spd_byte0 < spd_byte1) { |
| 180 | spd_byte0 = spd_byte1; |
| 181 | } |
| 182 | |
| 183 | /* Turn SPD ns time into MHZ. Check what the asm does to this math. */ |
| 184 | speed = 20000 / (((spd_byte0 >> 4) * 10) + (spd_byte0 & 0x0F)); |
| 185 | |
| 186 | /* current speed > max speed? */ |
| 187 | if (GeodeLinkSpeed() > speed) { |
Nils Jacobs | a1e2c56 | 2010-11-05 00:13:14 +0000 | [diff] [blame] | 188 | printk(BIOS_EMERG, "DIMM overclocked. Check GeodeLink Speed\n"); |
Nils Jacobs | 76890dd | 2010-11-01 15:20:27 +0000 | [diff] [blame] | 189 | post_code(POST_PLL_MEM_FAIL); |
| 190 | hcf(); |
| 191 | } |
| 192 | } |
| 193 | |
| 194 | const uint16_t REF_RATE[] = { 15, 3, 7, 31, 62, 125 }; /* ns */ |
| 195 | |
| 196 | static void set_refresh_rate(void) |
| 197 | { |
| 198 | uint8_t spd_byte0, spd_byte1; |
| 199 | uint16_t rate0, rate1; |
| 200 | msr_t msr; |
| 201 | |
| 202 | spd_byte0 = spd_read_byte(DIMM0, SPD_REFRESH); |
| 203 | spd_byte0 &= 0xF; |
| 204 | if (spd_byte0 > 5) { |
| 205 | spd_byte0 = 5; |
| 206 | } |
| 207 | rate0 = REF_RATE[spd_byte0]; |
| 208 | |
| 209 | spd_byte1 = spd_read_byte(DIMM1, SPD_REFRESH); |
| 210 | spd_byte1 &= 0xF; |
| 211 | if (spd_byte1 > 5) { |
| 212 | spd_byte1 = 5; |
| 213 | } |
| 214 | rate1 = REF_RATE[spd_byte1]; |
| 215 | |
| 216 | /* Use the faster rate (lowest number) */ |
| 217 | if (rate0 > rate1) { |
| 218 | rate0 = rate1; |
| 219 | } |
| 220 | |
| 221 | msr = rdmsr(MC_CF07_DATA); |
| 222 | msr.lo |= ((rate0 * (GeodeLinkSpeed() / 2)) / 16) |
| 223 | << CF07_LOWER_REF_INT_SHIFT; |
| 224 | wrmsr(MC_CF07_DATA, msr); |
| 225 | } |
| 226 | |
| 227 | const uint8_t CASDDR[] = { 5, 5, 2, 6, 0 }; /* 1(1.5), 1.5, 2, 2.5, 0 */ |
| 228 | |
| 229 | static u8 getcasmap(u32 dimm, u16 glspeed) |
| 230 | { |
| 231 | u16 dimm_speed; |
| 232 | u8 spd_byte, casmap, casmap_shift=0; |
| 233 | |
| 234 | /************************** DIMM0 **********************************/ |
| 235 | casmap = spd_read_byte(dimm, SPD_ACCEPTABLE_CAS_LATENCIES); |
| 236 | if (casmap != 0xFF) { |
| 237 | /* IF -.5 timing is supported, check -.5 timing > GeodeLink */ |
| 238 | spd_byte = spd_read_byte(dimm, SPD_SDRAM_CYCLE_TIME_2ND); |
| 239 | if (spd_byte != 0) { |
| 240 | /* Turn SPD ns time into MHZ. Check what the asm does to this math. */ |
| 241 | dimm_speed = 20000 / (((spd_byte >> 4) * 10) + (spd_byte & 0x0F)); |
| 242 | if (dimm_speed >= glspeed) { |
| 243 | casmap_shift = 1; /* -.5 is a shift of 1 */ |
| 244 | /* IF -1 timing is supported, check -1 timing > GeodeLink */ |
| 245 | spd_byte = spd_read_byte(dimm, SPD_SDRAM_CYCLE_TIME_3RD); |
| 246 | if (spd_byte != 0) { |
| 247 | /* Turn SPD ns time into MHZ. Check what the asm does to this math. */ |
| 248 | dimm_speed = 20000 / (((spd_byte >> 4) * 10) + (spd_byte & 0x0F)); |
| 249 | if (dimm_speed >= glspeed) { |
| 250 | casmap_shift = 2; /* -1 is a shift of 2 */ |
| 251 | } |
| 252 | } /* SPD_SDRAM_CYCLE_TIME_3RD (-1) !=0 */ |
| 253 | } else { |
| 254 | casmap_shift = 0; |
| 255 | } |
| 256 | } /* SPD_SDRAM_CYCLE_TIME_2ND (-.5) !=0 */ |
| 257 | /* set the casmap based on the shift to limit possible CAS settings */ |
| 258 | spd_byte = 31 - __builtin_clz((uint32_t) casmap); |
| 259 | /* just want bits in the lower byte since we have to cast to a 32 */ |
| 260 | casmap &= 0xFF << (spd_byte - casmap_shift); |
| 261 | } else { /* No DIMM */ |
| 262 | casmap = 0; |
| 263 | } |
| 264 | return casmap; |
| 265 | } |
| 266 | |
| 267 | static void setCAS(void) |
| 268 | { |
Nils Jacobs | 5beac7f | 2010-11-03 13:19:50 +0000 | [diff] [blame] | 269 | /* |
| 270 | * setCAS |
| 271 | * EEPROM byte usage: (18) SDRAM device attributes - CAS latency |
| 272 | * EEPROM byte usage: (23) SDRAM Minimum Clock Cycle Time @ CLX -.5 |
| 273 | * EEPROM byte usage: (25) SDRAM Minimum Clock Cycle Time @ CLX -1 |
| 274 | * |
| 275 | * The CAS setting is based on the information provided in each DIMMs SPD. |
| 276 | * The speed at which a DIMM can run is described relative to the slowest |
| 277 | * CAS the DIMM supports. Each speed for the relative CAS settings is |
| 278 | * checked that it is within the GeodeLink speed. If it isn't within the GeodeLink |
| 279 | * speed, the CAS setting is removed from the list of good settings for |
| 280 | * the DIMM. This is done for both DIMMs and the lists are compared to |
| 281 | * find the lowest common CAS latency setting. If there are no CAS settings |
| 282 | * in common we out a ERROR_DIFF_DIMMS (78h) to port 80h and halt. |
| 283 | * |
| 284 | * Entry: |
| 285 | * Exit: Set fastest CAS Latency based on GeodeLink speed and SPD information. |
| 286 | * Destroys: We really use everything ! |
| 287 | */ |
Nils Jacobs | 76890dd | 2010-11-01 15:20:27 +0000 | [diff] [blame] | 288 | uint16_t glspeed; |
| 289 | uint8_t spd_byte, casmap0, casmap1; |
| 290 | msr_t msr; |
| 291 | |
| 292 | glspeed = GeodeLinkSpeed(); |
| 293 | |
| 294 | casmap0 = getcasmap(DIMM0, glspeed); |
| 295 | casmap1 = getcasmap(DIMM1, glspeed); |
| 296 | |
Nils Jacobs | 5beac7f | 2010-11-03 13:19:50 +0000 | [diff] [blame] | 297 | /* CAS_LAT MAP COMPARE */ |
Nils Jacobs | 76890dd | 2010-11-01 15:20:27 +0000 | [diff] [blame] | 298 | if (casmap0 == 0) { |
| 299 | spd_byte = CASDDR[__builtin_ctz(casmap1)]; |
| 300 | } else if (casmap1 == 0) { |
| 301 | spd_byte = CASDDR[__builtin_ctz(casmap0)]; |
| 302 | } else if ((casmap0 &= casmap1)) { |
| 303 | spd_byte = CASDDR[__builtin_ctz(casmap0)]; |
| 304 | } else { |
Nils Jacobs | a1e2c56 | 2010-11-05 00:13:14 +0000 | [diff] [blame] | 305 | printk(BIOS_EMERG, "DIMM CAS Latencies not compatible\n"); |
Nils Jacobs | 76890dd | 2010-11-01 15:20:27 +0000 | [diff] [blame] | 306 | post_code(ERROR_DIFF_DIMMS); |
| 307 | hcf(); |
| 308 | } |
| 309 | |
| 310 | msr = rdmsr(MC_CF8F_DATA); |
| 311 | msr.lo &= ~(7 << CF8F_LOWER_CAS_LAT_SHIFT); |
| 312 | msr.lo |= spd_byte << CF8F_LOWER_CAS_LAT_SHIFT; |
| 313 | wrmsr(MC_CF8F_DATA, msr); |
| 314 | } |
| 315 | |
| 316 | static void set_latencies(void) |
| 317 | { |
| 318 | uint32_t memspeed, dimm_setting; |
| 319 | uint8_t spd_byte0, spd_byte1; |
| 320 | msr_t msr; |
| 321 | |
| 322 | memspeed = GeodeLinkSpeed() / 2; |
| 323 | dimm_setting = 0; |
| 324 | |
| 325 | /* MC_CF8F setup */ |
| 326 | /* tRAS */ |
| 327 | spd_byte0 = spd_read_byte(DIMM0, SPD_tRAS); |
| 328 | if (spd_byte0 == 0xFF) { |
| 329 | spd_byte0 = 0; |
| 330 | } |
| 331 | spd_byte1 = spd_read_byte(DIMM1, SPD_tRAS); |
| 332 | if (spd_byte1 == 0xFF) { |
| 333 | spd_byte1 = 0; |
| 334 | } |
| 335 | if (spd_byte0 < spd_byte1) { |
| 336 | spd_byte0 = spd_byte1; |
| 337 | } |
| 338 | /* (ns/(1/MHz) = (us*MHZ)/1000 = clocks/1000 = clocks) */ |
| 339 | spd_byte1 = (spd_byte0 * memspeed) / 1000; |
| 340 | if (((spd_byte0 * memspeed) % 1000)) { |
| 341 | ++spd_byte1; |
| 342 | } |
| 343 | if (spd_byte1 > 6) { |
| 344 | --spd_byte1; |
| 345 | } |
| 346 | dimm_setting |= spd_byte1 << CF8F_LOWER_ACT2PRE_SHIFT; |
| 347 | |
| 348 | /* tRP */ |
| 349 | spd_byte0 = spd_read_byte(DIMM0, SPD_tRP); |
| 350 | if (spd_byte0 == 0xFF) { |
| 351 | spd_byte0 = 0; |
| 352 | } |
| 353 | spd_byte1 = spd_read_byte(DIMM1, SPD_tRP); |
| 354 | if (spd_byte1 == 0xFF) { |
| 355 | spd_byte1 = 0; |
| 356 | } |
| 357 | if (spd_byte0 < spd_byte1) { |
| 358 | spd_byte0 = spd_byte1; |
| 359 | } |
| 360 | /* (ns/(1/MHz) = (us*MHZ)/1000 = clocks/1000 = clocks) */ |
| 361 | spd_byte1 = ((spd_byte0 >> 2) * memspeed) / 1000; |
| 362 | if ((((spd_byte0 >> 2) * memspeed) % 1000)) { |
| 363 | ++spd_byte1; |
| 364 | } |
| 365 | dimm_setting |= spd_byte1 << CF8F_LOWER_PRE2ACT_SHIFT; |
| 366 | |
| 367 | /* tRCD */ |
| 368 | spd_byte0 = spd_read_byte(DIMM0, SPD_tRCD); |
| 369 | if (spd_byte0 == 0xFF) { |
| 370 | spd_byte0 = 0; |
| 371 | } |
| 372 | spd_byte1 = spd_read_byte(DIMM1, SPD_tRCD); |
| 373 | if (spd_byte1 == 0xFF) { |
| 374 | spd_byte1 = 0; |
| 375 | } |
| 376 | if (spd_byte0 < spd_byte1) { |
| 377 | spd_byte0 = spd_byte1; |
| 378 | } |
| 379 | /* (ns/(1/MHz) = (us*MHZ)/1000 = clocks/1000 = clocks) */ |
| 380 | spd_byte1 = ((spd_byte0 >> 2) * memspeed) / 1000; |
| 381 | if ((((spd_byte0 >> 2) * memspeed) % 1000)) { |
| 382 | ++spd_byte1; |
| 383 | } |
| 384 | dimm_setting |= spd_byte1 << CF8F_LOWER_ACT2CMD_SHIFT; |
| 385 | |
| 386 | /* tRRD */ |
| 387 | spd_byte0 = spd_read_byte(DIMM0, SPD_tRRD); |
| 388 | if (spd_byte0 == 0xFF) { |
| 389 | spd_byte0 = 0; |
| 390 | } |
| 391 | spd_byte1 = spd_read_byte(DIMM1, SPD_tRRD); |
| 392 | if (spd_byte1 == 0xFF) { |
| 393 | spd_byte1 = 0; |
| 394 | } |
| 395 | if (spd_byte0 < spd_byte1) { |
| 396 | spd_byte0 = spd_byte1; |
| 397 | } |
| 398 | /* (ns/(1/MHz) = (us*MHZ)/1000 = clocks/1000 = clocks) */ |
| 399 | spd_byte1 = ((spd_byte0 >> 2) * memspeed) / 1000; |
| 400 | if ((((spd_byte0 >> 2) * memspeed) % 1000)) { |
| 401 | ++spd_byte1; |
| 402 | } |
| 403 | dimm_setting |= spd_byte1 << CF8F_LOWER_ACT2ACT_SHIFT; |
| 404 | |
| 405 | /* tRC = tRP + tRAS */ |
| 406 | dimm_setting |= (((dimm_setting >> CF8F_LOWER_ACT2PRE_SHIFT) & 0x0F) + |
| 407 | ((dimm_setting >> CF8F_LOWER_PRE2ACT_SHIFT) & 0x07)) |
| 408 | << CF8F_LOWER_REF2ACT_SHIFT; |
| 409 | |
| 410 | msr = rdmsr(MC_CF8F_DATA); |
| 411 | msr.lo &= 0xF00000FF; |
| 412 | msr.lo |= dimm_setting; |
| 413 | msr.hi |= CF8F_UPPER_REORDER_DIS_SET; |
| 414 | wrmsr(MC_CF8F_DATA, msr); |
| 415 | printk(BIOS_DEBUG, "MSR MC_CF8F_DATA (%08x) value is %08x:%08x\n", |
| 416 | MC_CF8F_DATA, msr.hi, msr.lo); |
| 417 | } |
| 418 | |
| 419 | static void set_extended_mode_registers(void) |
| 420 | { |
| 421 | uint8_t spd_byte0, spd_byte1; |
| 422 | msr_t msr; |
| 423 | spd_byte0 = spd_read_byte(DIMM0, SPD_DEVICE_ATTRIBUTES_GENERAL); |
| 424 | if (spd_byte0 == 0xFF) { |
| 425 | spd_byte0 = 0; |
| 426 | } |
| 427 | spd_byte1 = spd_read_byte(DIMM1, SPD_DEVICE_ATTRIBUTES_GENERAL); |
| 428 | if (spd_byte1 == 0xFF) { |
| 429 | spd_byte1 = 0; |
| 430 | } |
| 431 | spd_byte1 &= spd_byte0; |
| 432 | |
| 433 | msr = rdmsr(MC_CF07_DATA); |
| 434 | if (spd_byte1 & 1) { /* Drive Strength Control */ |
| 435 | msr.lo |= CF07_LOWER_EMR_DRV_SET; |
| 436 | } |
| 437 | if (spd_byte1 & 2) { /* FET Control */ |
| 438 | msr.lo |= CF07_LOWER_EMR_QFC_SET; |
| 439 | } |
| 440 | wrmsr(MC_CF07_DATA, msr); |
| 441 | } |
Ronald G. Minnich | 2bb216a | 2006-01-27 23:46:30 +0000 | [diff] [blame] | 442 | |
Li-Ta Lo | 108dd2c | 2006-02-23 21:39:19 +0000 | [diff] [blame] | 443 | static void sdram_set_registers(const struct mem_controller *ctrl) |
Ronald G. Minnich | 2bb216a | 2006-01-27 23:46:30 +0000 | [diff] [blame] | 444 | { |
Nils Jacobs | 76890dd | 2010-11-01 15:20:27 +0000 | [diff] [blame] | 445 | msr_t msr; |
| 446 | uint32_t msrnum; |
| 447 | |
| 448 | /* Set Refresh Staggering */ |
| 449 | msrnum = MC_CF07_DATA; |
| 450 | msr = rdmsr(msrnum); |
| 451 | msr.lo &= ~0xC0; |
| 452 | msr.lo |= 0x0; /* set refresh to 4SDRAM clocks */ |
| 453 | wrmsr(msrnum, msr); |
Nils Jacobs | 76890dd | 2010-11-01 15:20:27 +0000 | [diff] [blame] | 454 | } |
| 455 | |
| 456 | static void sdram_set_spd_registers(const struct mem_controller *ctrl) |
| 457 | { |
| 458 | uint8_t spd_byte; |
| 459 | |
Nils Jacobs | a1e2c56 | 2010-11-05 00:13:14 +0000 | [diff] [blame] | 460 | printk(BIOS_DEBUG, "sdram_set_spd_register\n"); |
Nils Jacobs | 5beac7f | 2010-11-03 13:19:50 +0000 | [diff] [blame] | 461 | post_code(POST_MEM_SETUP); /* post_70h */ |
Nils Jacobs | 76890dd | 2010-11-01 15:20:27 +0000 | [diff] [blame] | 462 | |
| 463 | spd_byte = spd_read_byte(DIMM0, SPD_MODULE_ATTRIBUTES); |
Nils Jacobs | a1e2c56 | 2010-11-05 00:13:14 +0000 | [diff] [blame] | 464 | printk(BIOS_DEBUG, "Check DIMM 0\n"); |
Nils Jacobs | 76890dd | 2010-11-01 15:20:27 +0000 | [diff] [blame] | 465 | /* Check DIMM is not Register and not Buffered DIMMs. */ |
| 466 | if ((spd_byte != 0xFF) && (spd_byte & 3)) { |
Nils Jacobs | a1e2c56 | 2010-11-05 00:13:14 +0000 | [diff] [blame] | 467 | printk(BIOS_EMERG, "DIMM0 NOT COMPATIBLE\n"); |
Nils Jacobs | 76890dd | 2010-11-01 15:20:27 +0000 | [diff] [blame] | 468 | post_code(ERROR_UNSUPPORTED_DIMM); |
| 469 | hcf(); |
| 470 | } |
Nils Jacobs | a1e2c56 | 2010-11-05 00:13:14 +0000 | [diff] [blame] | 471 | printk(BIOS_DEBUG, "Check DIMM 1\n"); |
Nils Jacobs | 76890dd | 2010-11-01 15:20:27 +0000 | [diff] [blame] | 472 | spd_byte = spd_read_byte(DIMM1, SPD_MODULE_ATTRIBUTES); |
| 473 | if ((spd_byte != 0xFF) && (spd_byte & 3)) { |
Nils Jacobs | a1e2c56 | 2010-11-05 00:13:14 +0000 | [diff] [blame] | 474 | printk(BIOS_EMERG, "DIMM1 NOT COMPATIBLE\n"); |
Nils Jacobs | 76890dd | 2010-11-01 15:20:27 +0000 | [diff] [blame] | 475 | post_code(ERROR_UNSUPPORTED_DIMM); |
| 476 | hcf(); |
| 477 | } |
| 478 | |
Nils Jacobs | 5beac7f | 2010-11-03 13:19:50 +0000 | [diff] [blame] | 479 | post_code(POST_MEM_SETUP2); /* post_72h */ |
Nils Jacobs | a1e2c56 | 2010-11-05 00:13:14 +0000 | [diff] [blame] | 480 | printk(BIOS_DEBUG, "Check DDR MAX\n"); |
Nils Jacobs | 76890dd | 2010-11-01 15:20:27 +0000 | [diff] [blame] | 481 | |
| 482 | /* Check that the memory is not overclocked. */ |
| 483 | checkDDRMax(); |
| 484 | |
| 485 | /* Size the DIMMS */ |
Nils Jacobs | 5beac7f | 2010-11-03 13:19:50 +0000 | [diff] [blame] | 486 | post_code(POST_MEM_SETUP3); /* post_73h */ |
Nils Jacobs | a1e2c56 | 2010-11-05 00:13:14 +0000 | [diff] [blame] | 487 | printk(BIOS_DEBUG, "AUTOSIZE DIMM 0\n"); |
Nils Jacobs | 76890dd | 2010-11-01 15:20:27 +0000 | [diff] [blame] | 488 | auto_size_dimm(DIMM0); |
Nils Jacobs | 5beac7f | 2010-11-03 13:19:50 +0000 | [diff] [blame] | 489 | post_code(POST_MEM_SETUP4); /* post_74h */ |
Nils Jacobs | a1e2c56 | 2010-11-05 00:13:14 +0000 | [diff] [blame] | 490 | printk(BIOS_DEBUG, "AUTOSIZE DIMM 1\n"); |
Nils Jacobs | 76890dd | 2010-11-01 15:20:27 +0000 | [diff] [blame] | 491 | auto_size_dimm(DIMM1); |
| 492 | |
| 493 | /* Set CAS latency */ |
Nils Jacobs | a1e2c56 | 2010-11-05 00:13:14 +0000 | [diff] [blame] | 494 | printk(BIOS_DEBUG, "set cas latency\n"); |
Nils Jacobs | 5beac7f | 2010-11-03 13:19:50 +0000 | [diff] [blame] | 495 | post_code(POST_MEM_SETUP5); /* post_75h */ |
Nils Jacobs | 76890dd | 2010-11-01 15:20:27 +0000 | [diff] [blame] | 496 | setCAS(); |
| 497 | |
| 498 | /* Set all the other latencies here (tRAS, tRP....) */ |
Nils Jacobs | a1e2c56 | 2010-11-05 00:13:14 +0000 | [diff] [blame] | 499 | printk(BIOS_DEBUG, "set all latency\n"); |
Nils Jacobs | 76890dd | 2010-11-01 15:20:27 +0000 | [diff] [blame] | 500 | set_latencies(); |
| 501 | |
| 502 | /* Set Extended Mode Registers */ |
Nils Jacobs | a1e2c56 | 2010-11-05 00:13:14 +0000 | [diff] [blame] | 503 | printk(BIOS_DEBUG, "set emrs\n"); |
Nils Jacobs | 76890dd | 2010-11-01 15:20:27 +0000 | [diff] [blame] | 504 | set_extended_mode_registers(); |
| 505 | |
Nils Jacobs | a1e2c56 | 2010-11-05 00:13:14 +0000 | [diff] [blame] | 506 | printk(BIOS_DEBUG, "set ref rate\n"); |
Nils Jacobs | 76890dd | 2010-11-01 15:20:27 +0000 | [diff] [blame] | 507 | /* Set Memory Refresh Rate */ |
| 508 | set_refresh_rate(); |
Ronald G. Minnich | 2bb216a | 2006-01-27 23:46:30 +0000 | [diff] [blame] | 509 | } |
| 510 | |
Li-Ta Lo | 108dd2c | 2006-02-23 21:39:19 +0000 | [diff] [blame] | 511 | /* Section 6.1.3, LX processor databooks, BIOS Initialization Sequence |
| 512 | * Section 4.1.4, GX/CS5535 GeodeROM Porting guide */ |
| 513 | static void sdram_enable(int controllers, const struct mem_controller *ctrl) |
Ronald G. Minnich | 2bb216a | 2006-01-27 23:46:30 +0000 | [diff] [blame] | 514 | { |
Li-Ta Lo | 108dd2c | 2006-02-23 21:39:19 +0000 | [diff] [blame] | 515 | int i; |
| 516 | msr_t msr; |
Ronald G. Minnich | 2bb216a | 2006-01-27 23:46:30 +0000 | [diff] [blame] | 517 | |
Li-Ta Lo | ec9cdc9 | 2006-03-02 21:33:01 +0000 | [diff] [blame] | 518 | /* 2. clock gating for PMode */ |
Nils Jacobs | fc9fcf7 | 2010-11-01 14:18:11 +0000 | [diff] [blame] | 519 | msr = rdmsr(MC_GLD_MSR_PM); |
Li-Ta Lo | ec9cdc9 | 2006-03-02 21:33:01 +0000 | [diff] [blame] | 520 | msr.lo &= ~0x04; |
| 521 | msr.lo |= 0x01; |
Nils Jacobs | fc9fcf7 | 2010-11-01 14:18:11 +0000 | [diff] [blame] | 522 | wrmsr(MC_GLD_MSR_PM, msr); |
Li-Ta Lo | 108dd2c | 2006-02-23 21:39:19 +0000 | [diff] [blame] | 523 | /* undocmented bits in GX, in LX there are |
| 524 | * 8 bits in PM1_UP_DLY */ |
Nils Jacobs | fc9fcf7 | 2010-11-01 14:18:11 +0000 | [diff] [blame] | 525 | msr = rdmsr(MC_CF1017_DATA); |
Li-Ta Lo | 108dd2c | 2006-02-23 21:39:19 +0000 | [diff] [blame] | 526 | msr.lo = 0x0101; |
Nils Jacobs | fc9fcf7 | 2010-11-01 14:18:11 +0000 | [diff] [blame] | 527 | wrmsr(MC_CF1017_DATA, msr); |
Nils Jacobs | a1e2c56 | 2010-11-05 00:13:14 +0000 | [diff] [blame] | 528 | printk(BIOS_DEBUG, "sdram_enable step 2\n"); |
Ronald G. Minnich | 2bb216a | 2006-01-27 23:46:30 +0000 | [diff] [blame] | 529 | |
Li-Ta Lo | 108dd2c | 2006-02-23 21:39:19 +0000 | [diff] [blame] | 530 | /* 3. release CKE mask to enable CKE */ |
Nils Jacobs | fc9fcf7 | 2010-11-01 14:18:11 +0000 | [diff] [blame] | 531 | msr = rdmsr(MC_CFCLK_DBUG); |
Li-Ta Lo | ec9cdc9 | 2006-03-02 21:33:01 +0000 | [diff] [blame] | 532 | msr.lo &= ~(0x03 << 8); |
Nils Jacobs | fc9fcf7 | 2010-11-01 14:18:11 +0000 | [diff] [blame] | 533 | wrmsr(MC_CFCLK_DBUG, msr); |
Nils Jacobs | a1e2c56 | 2010-11-05 00:13:14 +0000 | [diff] [blame] | 534 | printk(BIOS_DEBUG, "sdram_enable step 3\n"); |
Ronald G. Minnich | 2bb216a | 2006-01-27 23:46:30 +0000 | [diff] [blame] | 535 | |
Li-Ta Lo | ec9cdc9 | 2006-03-02 21:33:01 +0000 | [diff] [blame] | 536 | /* 4. set and clear REF_TST 16 times, more shouldn't hurt |
| 537 | * why this is before EMRS and MRS ? */ |
Li-Ta Lo | c0fe319 | 2006-02-28 23:07:27 +0000 | [diff] [blame] | 538 | for (i = 0; i < 19; i++) { |
Nils Jacobs | fc9fcf7 | 2010-11-01 14:18:11 +0000 | [diff] [blame] | 539 | msr = rdmsr(MC_CF07_DATA); |
Li-Ta Lo | c0fe319 | 2006-02-28 23:07:27 +0000 | [diff] [blame] | 540 | msr.lo |= (0x01 << 3); |
Nils Jacobs | fc9fcf7 | 2010-11-01 14:18:11 +0000 | [diff] [blame] | 541 | wrmsr(MC_CF07_DATA, msr); |
Li-Ta Lo | ec9cdc9 | 2006-03-02 21:33:01 +0000 | [diff] [blame] | 542 | msr.lo &= ~(0x01 << 3); |
Nils Jacobs | fc9fcf7 | 2010-11-01 14:18:11 +0000 | [diff] [blame] | 543 | wrmsr(MC_CF07_DATA, msr); |
Li-Ta Lo | c0fe319 | 2006-02-28 23:07:27 +0000 | [diff] [blame] | 544 | } |
Nils Jacobs | a1e2c56 | 2010-11-05 00:13:14 +0000 | [diff] [blame] | 545 | printk(BIOS_DEBUG, "sdram_enable step 4\n"); |
Ronald G. Minnich | 2bb216a | 2006-01-27 23:46:30 +0000 | [diff] [blame] | 546 | |
Li-Ta Lo | ec9cdc9 | 2006-03-02 21:33:01 +0000 | [diff] [blame] | 547 | /* 6. enable DLL, load Extended Mode Register by set and clear PROG_DRAM */ |
Nils Jacobs | fc9fcf7 | 2010-11-01 14:18:11 +0000 | [diff] [blame] | 548 | msr = rdmsr(MC_CF07_DATA); |
Li-Ta Lo | 108dd2c | 2006-02-23 21:39:19 +0000 | [diff] [blame] | 549 | msr.lo |= ((0x01 << 28) | 0x01); |
Nils Jacobs | fc9fcf7 | 2010-11-01 14:18:11 +0000 | [diff] [blame] | 550 | wrmsr(MC_CF07_DATA, msr); |
Li-Ta Lo | ec9cdc9 | 2006-03-02 21:33:01 +0000 | [diff] [blame] | 551 | msr.lo &= ~((0x01 << 28) | 0x01); |
Nils Jacobs | fc9fcf7 | 2010-11-01 14:18:11 +0000 | [diff] [blame] | 552 | wrmsr(MC_CF07_DATA, msr); |
Nils Jacobs | a1e2c56 | 2010-11-05 00:13:14 +0000 | [diff] [blame] | 553 | printk(BIOS_DEBUG, "sdram_enable step 6\n"); |
Ronald G. Minnich | 2bb216a | 2006-01-27 23:46:30 +0000 | [diff] [blame] | 554 | |
Li-Ta Lo | 108dd2c | 2006-02-23 21:39:19 +0000 | [diff] [blame] | 555 | /* 7. Reset DLL, Bit 27 is undocumented in GX datasheet, |
Nils Jacobs | 5beac7f | 2010-11-03 13:19:50 +0000 | [diff] [blame] | 556 | * it is documented in LX datasheet */ |
Li-Ta Lo | 108dd2c | 2006-02-23 21:39:19 +0000 | [diff] [blame] | 557 | /* load Mode Register by set and clear PROG_DRAM */ |
Nils Jacobs | fc9fcf7 | 2010-11-01 14:18:11 +0000 | [diff] [blame] | 558 | msr = rdmsr(MC_CF07_DATA); |
Li-Ta Lo | 108dd2c | 2006-02-23 21:39:19 +0000 | [diff] [blame] | 559 | msr.lo |= ((0x01 << 27) | 0x01); |
Nils Jacobs | fc9fcf7 | 2010-11-01 14:18:11 +0000 | [diff] [blame] | 560 | wrmsr(MC_CF07_DATA, msr); |
Li-Ta Lo | ec9cdc9 | 2006-03-02 21:33:01 +0000 | [diff] [blame] | 561 | msr.lo &= ~((0x01 << 27) | 0x01); |
Nils Jacobs | fc9fcf7 | 2010-11-01 14:18:11 +0000 | [diff] [blame] | 562 | wrmsr(MC_CF07_DATA, msr); |
Nils Jacobs | a1e2c56 | 2010-11-05 00:13:14 +0000 | [diff] [blame] | 563 | printk(BIOS_DEBUG, "sdram_enable step 7\n"); |
Ronald G. Minnich | 2bb216a | 2006-01-27 23:46:30 +0000 | [diff] [blame] | 564 | |
Li-Ta Lo | 108dd2c | 2006-02-23 21:39:19 +0000 | [diff] [blame] | 565 | /* 8. load Mode Register by set and clear PROG_DRAM */ |
Nils Jacobs | fc9fcf7 | 2010-11-01 14:18:11 +0000 | [diff] [blame] | 566 | msr = rdmsr(MC_CF07_DATA); |
Li-Ta Lo | 108dd2c | 2006-02-23 21:39:19 +0000 | [diff] [blame] | 567 | msr.lo |= 0x01; |
Nils Jacobs | fc9fcf7 | 2010-11-01 14:18:11 +0000 | [diff] [blame] | 568 | wrmsr(MC_CF07_DATA, msr); |
Li-Ta Lo | ec9cdc9 | 2006-03-02 21:33:01 +0000 | [diff] [blame] | 569 | msr.lo &= ~0x01; |
Nils Jacobs | fc9fcf7 | 2010-11-01 14:18:11 +0000 | [diff] [blame] | 570 | wrmsr(MC_CF07_DATA, msr); |
Nils Jacobs | a1e2c56 | 2010-11-05 00:13:14 +0000 | [diff] [blame] | 571 | printk(BIOS_DEBUG, "sdram_enable step 8\n"); |
Li-Ta Lo | bab9446 | 2006-02-28 15:39:25 +0000 | [diff] [blame] | 572 | |
Li-Ta Lo | 108dd2c | 2006-02-23 21:39:19 +0000 | [diff] [blame] | 573 | /* wait 200 SDCLKs */ |
| 574 | for (i = 0; i < 200; i++) |
| 575 | outb(0xaa, 0x80); |
Ronald G. Minnich | 2bb216a | 2006-01-27 23:46:30 +0000 | [diff] [blame] | 576 | |
Li-Ta Lo | 108dd2c | 2006-02-23 21:39:19 +0000 | [diff] [blame] | 577 | /* load RDSYNC */ |
Nils Jacobs | fc9fcf7 | 2010-11-01 14:18:11 +0000 | [diff] [blame] | 578 | msr = rdmsr(MC_CF_RDSYNC); |
Li-Ta Lo | 71eae20 | 2006-03-13 21:58:43 +0000 | [diff] [blame] | 579 | msr.hi = 0x000ff310; |
Stefan Reinauer | 14e2277 | 2010-04-27 06:56:47 +0000 | [diff] [blame] | 580 | /* the above setting is supposed to be good for "slow" ram. We have found that for |
| 581 | * some dram, at some clock rates, e.g. hynix at 366/244, this will actually |
Ronald G. Minnich | 0740c31 | 2006-09-18 04:23:23 +0000 | [diff] [blame] | 582 | * cause errors. The fix is to just set it to 0x310. Tested on 3 boards |
Stefan Reinauer | 14e2277 | 2010-04-27 06:56:47 +0000 | [diff] [blame] | 583 | * with 3 different type of dram -- Hynix, PSC, infineon. |
Ronald G. Minnich | 0740c31 | 2006-09-18 04:23:23 +0000 | [diff] [blame] | 584 | * I am leaving this comment here so that at some future time nobody is tempted |
| 585 | * to mess with this setting -- RGM, 9/2006 |
| 586 | */ |
| 587 | msr.hi = 0x00000310; |
Li-Ta Lo | c0fe319 | 2006-02-28 23:07:27 +0000 | [diff] [blame] | 588 | msr.lo = 0x00000000; |
Nils Jacobs | fc9fcf7 | 2010-11-01 14:18:11 +0000 | [diff] [blame] | 589 | wrmsr(MC_CF_RDSYNC, msr); |
Ronald G. Minnich | 2bb216a | 2006-01-27 23:46:30 +0000 | [diff] [blame] | 590 | |
Li-Ta Lo | c0fe319 | 2006-02-28 23:07:27 +0000 | [diff] [blame] | 591 | /* set delay control */ |
Nils Jacobs | fc9fcf7 | 2010-11-01 14:18:11 +0000 | [diff] [blame] | 592 | msr = rdmsr(GLCP_DELAY_CONTROLS); |
Li-Ta Lo | ec9cdc9 | 2006-03-02 21:33:01 +0000 | [diff] [blame] | 593 | msr.hi = 0x830d415a; |
| 594 | msr.lo = 0x8ea0ad6a; |
Nils Jacobs | fc9fcf7 | 2010-11-01 14:18:11 +0000 | [diff] [blame] | 595 | wrmsr(GLCP_DELAY_CONTROLS, msr); |
Li-Ta Lo | c0fe319 | 2006-02-28 23:07:27 +0000 | [diff] [blame] | 596 | |
Ronald G. Minnich | aefa3d7 | 2006-09-13 01:57:47 +0000 | [diff] [blame] | 597 | /* The RAM dll needs a write to lock on so generate a few dummy writes */ |
Nils Jacobs | e474070 | 2010-07-26 23:46:25 +0000 | [diff] [blame] | 598 | /* Note: The descriptor needs to be enabled to point at memory */ |
Ronald G. Minnich | aefa3d7 | 2006-09-13 01:57:47 +0000 | [diff] [blame] | 599 | volatile unsigned long *ptr; |
Nils Jacobs | e474070 | 2010-07-26 23:46:25 +0000 | [diff] [blame] | 600 | for (i = 0; i < 5; i++) { |
Ronald G. Minnich | aefa3d7 | 2006-09-13 01:57:47 +0000 | [diff] [blame] | 601 | ptr = (void *)i; |
| 602 | *ptr = (unsigned long)i; |
| 603 | } |
Ronald G. Minnich | d3ba4aa | 2006-05-02 03:07:11 +0000 | [diff] [blame] | 604 | |
Stefan Reinauer | 65b72ab | 2015-01-05 12:59:54 -0800 | [diff] [blame] | 605 | printk(BIOS_INFO, "RAM DLL lock\n"); |
Nils Jacobs | e474070 | 2010-07-26 23:46:25 +0000 | [diff] [blame] | 606 | |
Ronald G. Minnich | 2bb216a | 2006-01-27 23:46:30 +0000 | [diff] [blame] | 607 | } |