Rudolf Marek | 15bf50d | 2009-04-13 18:34:35 +0000 | [diff] [blame] | 1 | /* |
| 2 | * This file is part of the coreboot project. |
| 3 | * |
| 4 | * Copyright (C) 2009 Rudolf Marek <r.marek@assembler.cz> |
| 5 | * |
| 6 | * This program is free software; you can redistribute it and/or modify |
Uwe Hermann | c70e9fc | 2010-02-15 23:10:19 +0000 | [diff] [blame] | 7 | * it under the terms of the GNU General Public License as published by |
| 8 | * the Free Software Foundation; version 2 of the License. |
Rudolf Marek | 15bf50d | 2009-04-13 18:34:35 +0000 | [diff] [blame] | 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. |
Rudolf Marek | 15bf50d | 2009-04-13 18:34:35 +0000 | [diff] [blame] | 14 | */ |
| 15 | |
Stefan Reinauer | d55e26f | 2010-04-25 13:54:30 +0000 | [diff] [blame] | 16 | #include "raminit.h" |
| 17 | |
Rudolf Marek | 15bf50d | 2009-04-13 18:34:35 +0000 | [diff] [blame] | 18 | void exit_from_self(int controllers, const struct mem_controller *ctrl, |
| 19 | struct sys_info *sysinfo) |
| 20 | { |
| 21 | int i; |
| 22 | u32 dcl, dch; |
| 23 | u32 pcidev; |
| 24 | u8 bitmask; |
| 25 | u8 is_post_rev_g; |
Myles Watson | 6e23576 | 2009-09-29 14:56:15 +0000 | [diff] [blame] | 26 | u32 local_cpuid; |
Rudolf Marek | 15bf50d | 2009-04-13 18:34:35 +0000 | [diff] [blame] | 27 | |
| 28 | for (i = 0; i < controllers; i++) { |
| 29 | if (!sysinfo->ctrl_present[i]) |
| 30 | continue; |
| 31 | /* Skip everything if I don't have any memory on this controller */ |
| 32 | dch = pci_read_config32(ctrl[i].f2, DRAM_CONFIG_HIGH); |
| 33 | if (!(dch & DCH_MemClkFreqVal)) { |
| 34 | continue; |
| 35 | } |
| 36 | |
Myles Watson | 6e23576 | 2009-09-29 14:56:15 +0000 | [diff] [blame] | 37 | local_cpuid = pci_read_config32(ctrl[i].f3, 0xfc); |
| 38 | is_post_rev_g = ((local_cpuid & 0xfff00) > 0x50f00); |
Rudolf Marek | 15bf50d | 2009-04-13 18:34:35 +0000 | [diff] [blame] | 39 | |
| 40 | /* ChipKill */ |
| 41 | dcl = pci_read_config32(ctrl[i].f2, DRAM_CONFIG_LOW); |
| 42 | if (dcl & DCL_DimmEccEn) { |
| 43 | u32 mnc; |
Stefan Reinauer | c02b4fc | 2010-03-22 11:42:32 +0000 | [diff] [blame] | 44 | printk(BIOS_SPEW, "ECC enabled\n"); |
Rudolf Marek | 15bf50d | 2009-04-13 18:34:35 +0000 | [diff] [blame] | 45 | mnc = pci_read_config32(ctrl[i].f3, MCA_NB_CONFIG); |
| 46 | mnc |= MNC_ECC_EN; |
| 47 | if (dcl & DCL_Width128) { |
| 48 | mnc |= MNC_CHIPKILL_EN; |
| 49 | } |
| 50 | pci_write_config32(ctrl[i].f3, MCA_NB_CONFIG, mnc); |
| 51 | } |
| 52 | |
Stefan Reinauer | c02b4fc | 2010-03-22 11:42:32 +0000 | [diff] [blame] | 53 | printk(BIOS_DEBUG, "before resume errata #%d\n", |
Rudolf Marek | 15bf50d | 2009-04-13 18:34:35 +0000 | [diff] [blame] | 54 | (is_post_rev_g) ? 270 : 125); |
Stefan Reinauer | 14e2277 | 2010-04-27 06:56:47 +0000 | [diff] [blame] | 55 | /* |
Rudolf Marek | 15bf50d | 2009-04-13 18:34:35 +0000 | [diff] [blame] | 56 | 1. Restore memory controller registers as normal. |
| 57 | 2. Set the DisAutoRefresh bit (Dev:2x8C[18]). (270 only) |
| 58 | 3. Set the EnDramInit bit (Dev:2x7C[31]), clear all other bits in the same register). |
| 59 | 4. Wait at least 750 us. |
| 60 | 5. Clear the EnDramInit bit. |
| 61 | 6. Clear the DisAutoRefresh bit. (270 only) |
| 62 | 7. Read the value of Dev:2x80 and write that value back to Dev:2x80. |
| 63 | 8. Set the exit from the self refresh bit (Dev:2x90[1]). |
| 64 | 9. Clear the exit from self refresh bit immediately. |
| 65 | Note: Steps 8 and 9 must be executed in a single 64-byte aligned uninterrupted instruction stream. |
| 66 | */ |
| 67 | |
| 68 | enable_lapic(); |
| 69 | init_timer(); |
| 70 | |
Stefan Reinauer | c02b4fc | 2010-03-22 11:42:32 +0000 | [diff] [blame] | 71 | printk(BIOS_DEBUG, "before exit errata - timer enabled\n"); |
Rudolf Marek | 15bf50d | 2009-04-13 18:34:35 +0000 | [diff] [blame] | 72 | |
| 73 | if (is_post_rev_g) { |
| 74 | dcl = |
| 75 | pci_read_config32(ctrl[i].f2, |
| 76 | DRAM_TIMING_HIGH); |
| 77 | dcl |= (1 << 18); |
| 78 | pci_write_config32(ctrl[i].f2, DRAM_TIMING_HIGH, |
| 79 | dcl); |
| 80 | } |
| 81 | |
| 82 | dcl = DI_EnDramInit; |
| 83 | pci_write_config32(ctrl[i].f2, DRAM_INIT, dcl); |
| 84 | |
| 85 | udelay(800); |
| 86 | |
Stefan Reinauer | c02b4fc | 2010-03-22 11:42:32 +0000 | [diff] [blame] | 87 | printk(BIOS_DEBUG, "before exit errata - after mdelay\n"); |
Rudolf Marek | 15bf50d | 2009-04-13 18:34:35 +0000 | [diff] [blame] | 88 | |
| 89 | dcl = pci_read_config32(ctrl[i].f2, DRAM_INIT); |
| 90 | dcl &= ~DI_EnDramInit; |
| 91 | pci_write_config32(ctrl[i].f2, DRAM_INIT, dcl); |
| 92 | |
| 93 | if (is_post_rev_g) { |
| 94 | dcl = |
| 95 | pci_read_config32(ctrl[i].f2, |
| 96 | DRAM_TIMING_HIGH); |
| 97 | dcl &= ~(1 << 18); |
| 98 | pci_write_config32(ctrl[i].f2, DRAM_TIMING_HIGH, |
| 99 | dcl); |
| 100 | } |
| 101 | |
| 102 | dcl = pci_read_config32(ctrl[i].f2, DRAM_BANK_ADDR_MAP); |
| 103 | pci_write_config32(ctrl[i].f2, DRAM_BANK_ADDR_MAP, dcl); |
| 104 | |
| 105 | /* I was unable to do that like: ctrl[i].f2->path.pci.devfn << 8 */ |
| 106 | pcidev = |
| 107 | 0x80000000 | ((((ctrl[i].node_id + 0x18) << 3) | 0x2) |
| 108 | << 8) | 0x90; |
Stefan Reinauer | c02b4fc | 2010-03-22 11:42:32 +0000 | [diff] [blame] | 109 | printk(BIOS_DEBUG, "pcidev is %x\n", pcidev); |
Rudolf Marek | 15bf50d | 2009-04-13 18:34:35 +0000 | [diff] [blame] | 110 | bitmask = 2; |
| 111 | __asm__ __volatile__("pushl %0\n\t" |
| 112 | "movw $0xcf8, %%dx\n\t" |
| 113 | "out %%eax, (%%dx)\n\t" |
| 114 | "movw $0xcfc, %%dx\n\t" |
| 115 | "inl %%dx, %%eax\n\t" |
| 116 | "orb %1, %%al\n\t" |
| 117 | "not %1\n\t" |
| 118 | ".align 64\n\t" |
| 119 | "outl %%eax, (%%dx) \n\t" |
| 120 | "andb %1, %%al\n\t" |
| 121 | "outl %%eax, (%%dx)\n\t" |
| 122 | "popl %0\n\t"::"a"(pcidev), |
| 123 | "q"(bitmask):"edx"); |
| 124 | } |
| 125 | |
Stefan Reinauer | c02b4fc | 2010-03-22 11:42:32 +0000 | [diff] [blame] | 126 | printk(BIOS_DEBUG, "after exit errata\n"); |
Rudolf Marek | 15bf50d | 2009-04-13 18:34:35 +0000 | [diff] [blame] | 127 | |
| 128 | |
| 129 | for (i = 0; i < controllers; i++) { |
| 130 | u32 dcm; |
| 131 | if (!sysinfo->ctrl_present[i]) |
| 132 | continue; |
| 133 | /* Skip everything if I don't have any memory on this controller */ |
| 134 | if (sysinfo->meminfo[i].dimm_mask == 0x00) |
| 135 | continue; |
| 136 | |
Stefan Reinauer | c02b4fc | 2010-03-22 11:42:32 +0000 | [diff] [blame] | 137 | printk(BIOS_DEBUG, "Exiting memory from self refresh: "); |
Rudolf Marek | 15bf50d | 2009-04-13 18:34:35 +0000 | [diff] [blame] | 138 | int loops = 0; |
| 139 | do { |
| 140 | loops++; |
| 141 | if ((loops & 1023) == 0) { |
Stefan Reinauer | c02b4fc | 2010-03-22 11:42:32 +0000 | [diff] [blame] | 142 | printk(BIOS_DEBUG, "."); |
Rudolf Marek | 15bf50d | 2009-04-13 18:34:35 +0000 | [diff] [blame] | 143 | } |
| 144 | dcm = |
| 145 | pci_read_config32(ctrl[i].f2, DRAM_CTRL_MISC); |
| 146 | } while (((dcm & DCM_MemClrStatus) == |
| 147 | 0) /* || ((dcm & DCM_DramEnabled) == 0) */ ); |
| 148 | |
| 149 | if (loops >= TIMEOUT_LOOPS) { |
Stefan Reinauer | c02b4fc | 2010-03-22 11:42:32 +0000 | [diff] [blame] | 150 | printk(BIOS_DEBUG, "timeout with with cntrl[%d]\n", i); |
Rudolf Marek | 15bf50d | 2009-04-13 18:34:35 +0000 | [diff] [blame] | 151 | continue; |
| 152 | } |
| 153 | |
Stefan Reinauer | c02b4fc | 2010-03-22 11:42:32 +0000 | [diff] [blame] | 154 | printk(BIOS_DEBUG, " done\n"); |
Rudolf Marek | 15bf50d | 2009-04-13 18:34:35 +0000 | [diff] [blame] | 155 | } |
| 156 | |
Stefan Reinauer | 0867062 | 2009-06-30 15:17:49 +0000 | [diff] [blame] | 157 | #if CONFIG_HW_MEM_HOLE_SIZEK != 0 |
Rudolf Marek | 15bf50d | 2009-04-13 18:34:35 +0000 | [diff] [blame] | 158 | /* init hw mem hole here */ |
| 159 | /* DramHoleValid bit only can be set after MemClrStatus is set by Hardware */ |
| 160 | set_hw_mem_hole(controllers, ctrl); |
| 161 | #endif |
| 162 | |
| 163 | /* store tom to sysinfo, and it will be used by dqs_timing */ |
| 164 | { |
| 165 | msr_t msr; |
| 166 | //[1M, TOM) |
| 167 | msr = rdmsr(TOP_MEM); |
| 168 | sysinfo->tom_k = ((msr.hi << 24) | (msr.lo >> 8)) >> 2; |
| 169 | |
| 170 | //[4G, TOM2) |
| 171 | msr = rdmsr(TOP_MEM2); |
| 172 | sysinfo->tom2_k = ((msr.hi << 24) | (msr.lo >> 8)) >> 2; |
| 173 | } |
| 174 | |
| 175 | for (i = 0; i < controllers; i++) { |
| 176 | |
| 177 | if (!sysinfo->ctrl_present[i]) |
| 178 | continue; |
| 179 | |
| 180 | /* Skip everything if I don't have any memory on this controller */ |
| 181 | if (sysinfo->meminfo[i].dimm_mask == 0x00) |
| 182 | continue; |
| 183 | |
| 184 | dqs_restore_MC_NVRAM((ctrl + i)->f2); |
| 185 | sysinfo->mem_trained[i] = 1; // mem was trained |
| 186 | } |
| 187 | } |