aboutsummaryrefslogtreecommitdiff
path: root/src/northbridge/amd/amdk8/exit_from_self.c
diff options
context:
space:
mode:
authorRudolf Marek <r.marek@assembler.cz>2009-04-13 18:34:35 +0000
committerRudolf Marek <r.marek@assembler.cz>2009-04-13 18:34:35 +0000
commit15bf50d8203af20b3079e6691bf0d9eee66ea1bd (patch)
tree93e1ebdfcf792914356209a2bb3b17641133ed81 /src/northbridge/amd/amdk8/exit_from_self.c
parent33cafe5bfb440d150e36872d091037fa0785863d (diff)
Following patch adds resume (exit from self refresh) support for AMD K8 revF
CPUs. It handles both type of erratas on those CPUs. Signed-off-by: Rudolf Marek <r.marek@assembler.cz> Acked-by: Peter Stuge <peter@stuge.se> git-svn-id: svn://svn.coreboot.org/coreboot/trunk@4102 2b7e53f0-3cfb-0310-b3e9-8179ed1497e1
Diffstat (limited to 'src/northbridge/amd/amdk8/exit_from_self.c')
-rw-r--r--src/northbridge/amd/amdk8/exit_from_self.c189
1 files changed, 189 insertions, 0 deletions
diff --git a/src/northbridge/amd/amdk8/exit_from_self.c b/src/northbridge/amd/amdk8/exit_from_self.c
new file mode 100644
index 0000000000..f8c6744ed1
--- /dev/null
+++ b/src/northbridge/amd/amdk8/exit_from_self.c
@@ -0,0 +1,189 @@
+/*
+ * This file is part of the coreboot project.
+ *
+ * Copyright (C) 2009 Rudolf Marek <r.marek@assembler.cz>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License v2 as published by
+ * the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+void exit_from_self(int controllers, const struct mem_controller *ctrl,
+ struct sys_info *sysinfo)
+{
+ int i;
+ u32 dcl, dch;
+ u32 pcidev;
+ u8 bitmask;
+ u8 is_post_rev_g;
+ u32 cpuid;
+
+ for (i = 0; i < controllers; i++) {
+ if (!sysinfo->ctrl_present[i])
+ continue;
+ /* Skip everything if I don't have any memory on this controller */
+ dch = pci_read_config32(ctrl[i].f2, DRAM_CONFIG_HIGH);
+ if (!(dch & DCH_MemClkFreqVal)) {
+ continue;
+ }
+
+ cpuid = pci_read_config32(ctrl[i].f3, 0xfc);
+ is_post_rev_g = ((cpuid & 0xfff00) > 0x50f00);
+
+ /* ChipKill */
+ dcl = pci_read_config32(ctrl[i].f2, DRAM_CONFIG_LOW);
+ if (dcl & DCL_DimmEccEn) {
+ u32 mnc;
+ printk_spew("ECC enabled\n");
+ mnc = pci_read_config32(ctrl[i].f3, MCA_NB_CONFIG);
+ mnc |= MNC_ECC_EN;
+ if (dcl & DCL_Width128) {
+ mnc |= MNC_CHIPKILL_EN;
+ }
+ pci_write_config32(ctrl[i].f3, MCA_NB_CONFIG, mnc);
+ }
+
+ printk_debug("before resume errata #%d\n",
+ (is_post_rev_g) ? 270 : 125);
+ /*
+ 1. Restore memory controller registers as normal.
+ 2. Set the DisAutoRefresh bit (Dev:2x8C[18]). (270 only)
+ 3. Set the EnDramInit bit (Dev:2x7C[31]), clear all other bits in the same register).
+ 4. Wait at least 750 us.
+ 5. Clear the EnDramInit bit.
+ 6. Clear the DisAutoRefresh bit. (270 only)
+ 7. Read the value of Dev:2x80 and write that value back to Dev:2x80.
+ 8. Set the exit from the self refresh bit (Dev:2x90[1]).
+ 9. Clear the exit from self refresh bit immediately.
+ Note: Steps 8 and 9 must be executed in a single 64-byte aligned uninterrupted instruction stream.
+ */
+
+ enable_lapic();
+ init_timer();
+
+ printk_debug("before exit errata - timer enabled\n");
+
+ if (is_post_rev_g) {
+ dcl =
+ pci_read_config32(ctrl[i].f2,
+ DRAM_TIMING_HIGH);
+ dcl |= (1 << 18);
+ pci_write_config32(ctrl[i].f2, DRAM_TIMING_HIGH,
+ dcl);
+ }
+
+ dcl = DI_EnDramInit;
+ pci_write_config32(ctrl[i].f2, DRAM_INIT, dcl);
+
+ udelay(800);
+
+ printk_debug("before exit errata - after mdelay\n");
+
+ dcl = pci_read_config32(ctrl[i].f2, DRAM_INIT);
+ dcl &= ~DI_EnDramInit;
+ pci_write_config32(ctrl[i].f2, DRAM_INIT, dcl);
+
+ if (is_post_rev_g) {
+ dcl =
+ pci_read_config32(ctrl[i].f2,
+ DRAM_TIMING_HIGH);
+ dcl &= ~(1 << 18);
+ pci_write_config32(ctrl[i].f2, DRAM_TIMING_HIGH,
+ dcl);
+ }
+
+ dcl = pci_read_config32(ctrl[i].f2, DRAM_BANK_ADDR_MAP);
+ pci_write_config32(ctrl[i].f2, DRAM_BANK_ADDR_MAP, dcl);
+
+ /* I was unable to do that like: ctrl[i].f2->path.pci.devfn << 8 */
+ pcidev =
+ 0x80000000 | ((((ctrl[i].node_id + 0x18) << 3) | 0x2)
+ << 8) | 0x90;
+ printk_debug("pcidev is %x\n", pcidev);
+ bitmask = 2;
+ __asm__ __volatile__("pushl %0\n\t"
+ "movw $0xcf8, %%dx\n\t"
+ "out %%eax, (%%dx)\n\t"
+ "movw $0xcfc, %%dx\n\t"
+ "inl %%dx, %%eax\n\t"
+ "orb %1, %%al\n\t"
+ "not %1\n\t"
+ ".align 64\n\t"
+ "outl %%eax, (%%dx) \n\t"
+ "andb %1, %%al\n\t"
+ "outl %%eax, (%%dx)\n\t"
+ "popl %0\n\t"::"a"(pcidev),
+ "q"(bitmask):"edx");
+ }
+
+ printk_debug("after exit errata\n");
+
+
+ for (i = 0; i < controllers; i++) {
+ u32 dcm;
+ if (!sysinfo->ctrl_present[i])
+ continue;
+ /* Skip everything if I don't have any memory on this controller */
+ if (sysinfo->meminfo[i].dimm_mask == 0x00)
+ continue;
+
+ printk_debug("Exiting memory from self refresh: ");
+ int loops = 0;
+ do {
+ loops++;
+ if ((loops & 1023) == 0) {
+ printk_debug(".");
+ }
+ dcm =
+ pci_read_config32(ctrl[i].f2, DRAM_CTRL_MISC);
+ } while (((dcm & DCM_MemClrStatus) ==
+ 0) /* || ((dcm & DCM_DramEnabled) == 0) */ );
+
+ if (loops >= TIMEOUT_LOOPS) {
+ printk_debug("timeout with with cntrl[%d]\n", i);
+ continue;
+ }
+
+ printk_debug(" done\n");
+ }
+
+#if HW_MEM_HOLE_SIZEK != 0
+ /* init hw mem hole here */
+ /* DramHoleValid bit only can be set after MemClrStatus is set by Hardware */
+ set_hw_mem_hole(controllers, ctrl);
+#endif
+
+ /* store tom to sysinfo, and it will be used by dqs_timing */
+ {
+ msr_t msr;
+ //[1M, TOM)
+ msr = rdmsr(TOP_MEM);
+ sysinfo->tom_k = ((msr.hi << 24) | (msr.lo >> 8)) >> 2;
+
+ //[4G, TOM2)
+ msr = rdmsr(TOP_MEM2);
+ sysinfo->tom2_k = ((msr.hi << 24) | (msr.lo >> 8)) >> 2;
+ }
+
+ for (i = 0; i < controllers; i++) {
+
+ if (!sysinfo->ctrl_present[i])
+ continue;
+
+ /* Skip everything if I don't have any memory on this controller */
+ if (sysinfo->meminfo[i].dimm_mask == 0x00)
+ continue;
+
+ dqs_restore_MC_NVRAM((ctrl + i)->f2);
+ sysinfo->mem_trained[i] = 1; // mem was trained
+ }
+}