diff options
author | Francis Rowe <info@gluglug.org.uk> | 2015-10-19 00:12:53 +0100 |
---|---|---|
committer | Francis Rowe <info@gluglug.org.uk> | 2015-10-19 02:32:36 +0100 |
commit | 0622df6194dbb1b2120743c0fd1cc5e72c380128 (patch) | |
tree | 4c858b8c5667fe001a9907ae0578b4ec28a8f513 /resources/libreboot/patch/kgpe-d16/0019-northbridge-amd-amdmct-mct_ddr3-Add-initial-Suspend-.patch | |
parent | 5999dba5f71f1c05040a551d2420ab8c7f3a9da4 (diff) | |
download | librebootfr-0622df6194dbb1b2120743c0fd1cc5e72c380128.tar.gz librebootfr-0622df6194dbb1b2120743c0fd1cc5e72c380128.zip |
KGPE-D16: update patch set (also update coreboot and vboot)
Also contains other fixes from coreboot, like:
* 551cff0 Derive lvds_dual_channel from EDID timings.
^ makes single/dual channel LVDS selection on GM45 automatic
* 26fc544 lenovo/t60: Enable native intel gfx init.
^ was being maintained in libreboot, now upstreamed so not needed
Framebuffer mode was disabled for the KGPE-D16, because only
text-mode works at the moment.
Diffstat (limited to 'resources/libreboot/patch/kgpe-d16/0019-northbridge-amd-amdmct-mct_ddr3-Add-initial-Suspend-.patch')
-rw-r--r-- | resources/libreboot/patch/kgpe-d16/0019-northbridge-amd-amdmct-mct_ddr3-Add-initial-Suspend-.patch | 1007 |
1 files changed, 1007 insertions, 0 deletions
diff --git a/resources/libreboot/patch/kgpe-d16/0019-northbridge-amd-amdmct-mct_ddr3-Add-initial-Suspend-.patch b/resources/libreboot/patch/kgpe-d16/0019-northbridge-amd-amdmct-mct_ddr3-Add-initial-Suspend-.patch new file mode 100644 index 00000000..482bedbd --- /dev/null +++ b/resources/libreboot/patch/kgpe-d16/0019-northbridge-amd-amdmct-mct_ddr3-Add-initial-Suspend-.patch @@ -0,0 +1,1007 @@ +From b1e36a17d254e15459729ebfc3a83df4a2b28468 Mon Sep 17 00:00:00 2001 +From: Timothy Pearson <tpearson@raptorengineeringinc.com> +Date: Sat, 5 Sep 2015 18:40:31 -0500 +Subject: [PATCH 019/139] northbridge/amd/amdmct/mct_ddr3: Add initial Suspend + to RAM (S3) support + +Change-Id: Ic97567851fa40295bc21cefd7537407b99d71709 +Signed-off-by: Timothy Pearson <tpearson@raptorengineeringinc.com> +--- + src/northbridge/amd/amdfam10/northbridge.c | 8 + + src/northbridge/amd/amdmct/mct_ddr3/mct_d.c | 154 ++++--- + src/northbridge/amd/amdmct/mct_ddr3/mct_d.h | 112 +++++ + src/northbridge/amd/amdmct/mct_ddr3/s3utils.c | 610 ++++++++++++++++++++++++++ + src/northbridge/amd/amdmct/mct_ddr3/s3utils.h | 28 ++ + 5 files changed, 840 insertions(+), 72 deletions(-) + create mode 100644 src/northbridge/amd/amdmct/mct_ddr3/s3utils.c + create mode 100644 src/northbridge/amd/amdmct/mct_ddr3/s3utils.h + +diff --git a/src/northbridge/amd/amdfam10/northbridge.c b/src/northbridge/amd/amdfam10/northbridge.c +index 74cecc8..9cc3d96 100644 +--- a/src/northbridge/amd/amdfam10/northbridge.c ++++ b/src/northbridge/amd/amdfam10/northbridge.c +@@ -54,6 +54,10 @@ + #include <sb_cimx.h> + #endif + ++#if IS_ENABLED(CONFIG_DIMM_DDR3) ++#include "../amdmct/mct_ddr3/s3utils.h" ++#endif ++ + struct amdfam10_sysconf_t sysconf; + + #define FX_DEVS NODE_NUMS +@@ -1413,6 +1417,10 @@ static void root_complex_enable_dev(struct device *dev) + /* Do not delay UMA setup, as a device on the PCI bus may evaluate + the global uma_memory variables already in its enable function. */ + if (!done) { ++#if IS_ENABLED(CONFIG_DIMM_DDR3) ++ save_mct_information_to_nvram(); ++#endif ++ + setup_bsp_ramtop(); + setup_uma_memory(); + done = 1; +diff --git a/src/northbridge/amd/amdmct/mct_ddr3/mct_d.c b/src/northbridge/amd/amdmct/mct_ddr3/mct_d.c +index fa59d71..a8212c5 100644 +--- a/src/northbridge/amd/amdmct/mct_ddr3/mct_d.c ++++ b/src/northbridge/amd/amdmct/mct_ddr3/mct_d.c +@@ -272,91 +272,101 @@ static void mctAutoInitMCT_D(struct MCTStatStruc *pMCTstat, + u8 Node, NodesWmem; + u32 node_sys_base; + ++ uint8_t s3resume = acpi_is_wakeup_s3(); ++ + restartinit: + mctInitMemGPIOs_A_D(); /* Set any required GPIOs*/ +- NodesWmem = 0; +- node_sys_base = 0; +- for (Node = 0; Node < MAX_NODES_SUPPORTED; Node++) { +- struct DCTStatStruc *pDCTstat; +- pDCTstat = pDCTstatA + Node; ++ if (s3resume) { ++#if IS_ENABLED(CONFIG_HAVE_ACPI_RESUME) ++ printk(BIOS_DEBUG, "mctAutoInitMCT_D: Restoring DCT configuration from NVRAM\n"); ++ restore_mct_information_from_nvram(); ++#endif ++ } else { ++ NodesWmem = 0; ++ node_sys_base = 0; ++ for (Node = 0; Node < MAX_NODES_SUPPORTED; Node++) { ++ struct DCTStatStruc *pDCTstat; ++ pDCTstat = pDCTstatA + Node; + +- /* Zero out data structures to avoid false detection of DIMMs */ +- memset(pDCTstat, 0, sizeof(struct DCTStatStruc)); +- +- /* Initialize data structures */ +- pDCTstat->Node_ID = Node; +- pDCTstat->dev_host = PA_HOST(Node); +- pDCTstat->dev_map = PA_MAP(Node); +- pDCTstat->dev_dct = PA_DCT(Node); +- pDCTstat->dev_nbmisc = PA_NBMISC(Node); +- pDCTstat->NodeSysBase = node_sys_base; +- +- printk(BIOS_DEBUG, "mctAutoInitMCT_D: mct_init Node %d\n", Node); +- mct_init(pMCTstat, pDCTstat); +- mctNodeIDDebugPort_D(); +- pDCTstat->NodePresent = NodePresent_D(Node); +- if (pDCTstat->NodePresent) { /* See if Node is there*/ +- printk(BIOS_DEBUG, "mctAutoInitMCT_D: clear_legacy_Mode\n"); +- clear_legacy_Mode(pMCTstat, pDCTstat); +- pDCTstat->LogicalCPUID = mctGetLogicalCPUID_D(Node); +- +- printk(BIOS_DEBUG, "mctAutoInitMCT_D: mct_InitialMCT_D\n"); +- mct_InitialMCT_D(pMCTstat, pDCTstat); +- +- printk(BIOS_DEBUG, "mctAutoInitMCT_D: mctSMBhub_Init\n"); +- mctSMBhub_Init(Node); /* Switch SMBUS crossbar to proper node*/ +- +- printk(BIOS_DEBUG, "mctAutoInitMCT_D: mct_initDCT\n"); +- mct_initDCT(pMCTstat, pDCTstat); +- if (pDCTstat->ErrCode == SC_FatalErr) { +- goto fatalexit; /* any fatal errors?*/ +- } else if (pDCTstat->ErrCode < SC_StopError) { +- NodesWmem++; +- } +- } /* if Node present */ +- node_sys_base = pDCTstat->NodeSysBase; +- node_sys_base += (pDCTstat->NodeSysLimit + 2) & ~0x0F; +- } +- if (NodesWmem == 0) { +- printk(BIOS_DEBUG, "No Nodes?!\n"); +- goto fatalexit; +- } ++ /* Zero out data structures to avoid false detection of DIMMs */ ++ memset(pDCTstat, 0, sizeof(struct DCTStatStruc)); ++ ++ /* Initialize data structures */ ++ pDCTstat->Node_ID = Node; ++ pDCTstat->dev_host = PA_HOST(Node); ++ pDCTstat->dev_map = PA_MAP(Node); ++ pDCTstat->dev_dct = PA_DCT(Node); ++ pDCTstat->dev_nbmisc = PA_NBMISC(Node); ++ pDCTstat->NodeSysBase = node_sys_base; ++ ++ printk(BIOS_DEBUG, "mctAutoInitMCT_D: mct_init Node %d\n", Node); ++ mct_init(pMCTstat, pDCTstat); ++ mctNodeIDDebugPort_D(); ++ pDCTstat->NodePresent = NodePresent_D(Node); ++ if (pDCTstat->NodePresent) { /* See if Node is there*/ ++ printk(BIOS_DEBUG, "mctAutoInitMCT_D: clear_legacy_Mode\n"); ++ clear_legacy_Mode(pMCTstat, pDCTstat); ++ pDCTstat->LogicalCPUID = mctGetLogicalCPUID_D(Node); ++ ++ printk(BIOS_DEBUG, "mctAutoInitMCT_D: mct_InitialMCT_D\n"); ++ mct_InitialMCT_D(pMCTstat, pDCTstat); ++ ++ printk(BIOS_DEBUG, "mctAutoInitMCT_D: mctSMBhub_Init\n"); ++ mctSMBhub_Init(Node); /* Switch SMBUS crossbar to proper node*/ ++ ++ printk(BIOS_DEBUG, "mctAutoInitMCT_D: mct_initDCT\n"); ++ mct_initDCT(pMCTstat, pDCTstat); ++ if (pDCTstat->ErrCode == SC_FatalErr) { ++ goto fatalexit; /* any fatal errors?*/ ++ } else if (pDCTstat->ErrCode < SC_StopError) { ++ NodesWmem++; ++ } ++ } /* if Node present */ ++ node_sys_base = pDCTstat->NodeSysBase; ++ node_sys_base += (pDCTstat->NodeSysLimit + 2) & ~0x0F; ++ } ++ if (NodesWmem == 0) { ++ printk(BIOS_DEBUG, "No Nodes?!\n"); ++ goto fatalexit; ++ } + +- printk(BIOS_DEBUG, "mctAutoInitMCT_D: SyncDCTsReady_D\n"); +- SyncDCTsReady_D(pMCTstat, pDCTstatA); /* Make sure DCTs are ready for accesses.*/ ++ printk(BIOS_DEBUG, "mctAutoInitMCT_D: SyncDCTsReady_D\n"); ++ SyncDCTsReady_D(pMCTstat, pDCTstatA); /* Make sure DCTs are ready for accesses.*/ + +- printk(BIOS_DEBUG, "mctAutoInitMCT_D: HTMemMapInit_D\n"); +- HTMemMapInit_D(pMCTstat, pDCTstatA); /* Map local memory into system address space.*/ +- mctHookAfterHTMap(); ++ printk(BIOS_DEBUG, "mctAutoInitMCT_D: HTMemMapInit_D\n"); ++ HTMemMapInit_D(pMCTstat, pDCTstatA); /* Map local memory into system address space.*/ ++ mctHookAfterHTMap(); + +- printk(BIOS_DEBUG, "mctAutoInitMCT_D: CPUMemTyping_D\n"); +- CPUMemTyping_D(pMCTstat, pDCTstatA); /* Map dram into WB/UC CPU cacheability */ +- mctHookAfterCPU(); /* Setup external northbridge(s) */ ++ printk(BIOS_DEBUG, "mctAutoInitMCT_D: CPUMemTyping_D\n"); ++ CPUMemTyping_D(pMCTstat, pDCTstatA); /* Map dram into WB/UC CPU cacheability */ ++ mctHookAfterCPU(); /* Setup external northbridge(s) */ + +- printk(BIOS_DEBUG, "mctAutoInitMCT_D: DQSTiming_D\n"); +- DQSTiming_D(pMCTstat, pDCTstatA); /* Get Receiver Enable and DQS signal timing*/ ++ printk(BIOS_DEBUG, "mctAutoInitMCT_D: DQSTiming_D\n"); ++ DQSTiming_D(pMCTstat, pDCTstatA); /* Get Receiver Enable and DQS signal timing*/ + +- printk(BIOS_DEBUG, "mctAutoInitMCT_D: UMAMemTyping_D\n"); +- UMAMemTyping_D(pMCTstat, pDCTstatA); /* Fix up for UMA sizing */ ++ printk(BIOS_DEBUG, "mctAutoInitMCT_D: UMAMemTyping_D\n"); ++ UMAMemTyping_D(pMCTstat, pDCTstatA); /* Fix up for UMA sizing */ + +- printk(BIOS_DEBUG, "mctAutoInitMCT_D: :OtherTiming\n"); +- mct_OtherTiming(pMCTstat, pDCTstatA); ++ printk(BIOS_DEBUG, "mctAutoInitMCT_D: :OtherTiming\n"); ++ mct_OtherTiming(pMCTstat, pDCTstatA); + +- if (ReconfigureDIMMspare_D(pMCTstat, pDCTstatA)) { /* RESET# if 1st pass of DIMM spare enabled*/ +- goto restartinit; +- } + +- InterleaveNodes_D(pMCTstat, pDCTstatA); +- InterleaveChannels_D(pMCTstat, pDCTstatA); ++ if (ReconfigureDIMMspare_D(pMCTstat, pDCTstatA)) { /* RESET# if 1st pass of DIMM spare enabled*/ ++ goto restartinit; ++ } + +- printk(BIOS_DEBUG, "mctAutoInitMCT_D: ECCInit_D\n"); +- if (ECCInit_D(pMCTstat, pDCTstatA)) { /* Setup ECC control and ECC check-bits*/ +- printk(BIOS_DEBUG, "mctAutoInitMCT_D: MCTMemClr_D\n"); +- MCTMemClr_D(pMCTstat,pDCTstatA); +- } ++ InterleaveNodes_D(pMCTstat, pDCTstatA); ++ InterleaveChannels_D(pMCTstat, pDCTstatA); ++ ++ printk(BIOS_DEBUG, "mctAutoInitMCT_D: ECCInit_D\n"); ++ if (ECCInit_D(pMCTstat, pDCTstatA)) { /* Setup ECC control and ECC check-bits*/ ++ printk(BIOS_DEBUG, "mctAutoInitMCT_D: MCTMemClr_D\n"); ++ MCTMemClr_D(pMCTstat,pDCTstatA); ++ } + +- mct_FinalMCT_D(pMCTstat, pDCTstatA); +- printk(BIOS_DEBUG, "mctAutoInitMCT_D Done: Global Status: %x\n", pMCTstat->GStatus); ++ mct_FinalMCT_D(pMCTstat, pDCTstatA); ++ printk(BIOS_DEBUG, "mctAutoInitMCT_D Done: Global Status: %x\n", pMCTstat->GStatus); ++ } + + return; + +diff --git a/src/northbridge/amd/amdmct/mct_ddr3/mct_d.h b/src/northbridge/amd/amdmct/mct_ddr3/mct_d.h +index 219aa42..c790d7e 100644 +--- a/src/northbridge/amd/amdmct/mct_ddr3/mct_d.h ++++ b/src/northbridge/amd/amdmct/mct_ddr3/mct_d.h +@@ -24,6 +24,8 @@ + #ifndef MCT_D_H + #define MCT_D_H + ++#include <cpu/x86/msr.h> ++ + /*=========================================================================== + CPU - K8/FAM10 + ===========================================================================*/ +@@ -596,6 +598,116 @@ struct DCTStatStruc { /* A per Node structure*/ + uint32_t DimmSerialNumber[MAX_DIMMS_SUPPORTED]; + } __attribute__((packed)); + ++struct amd_s3_persistent_mct_channel_data { ++ /* Stage 1 (1 dword) */ ++ uint32_t f2x110; ++ ++ /* Stage 2 (88 dwords) */ ++ uint32_t f1x40; ++ uint32_t f1x44; ++ uint32_t f1x48; ++ uint32_t f1x4c; ++ uint32_t f1x50; ++ uint32_t f1x54; ++ uint32_t f1x58; ++ uint32_t f1x5c; ++ uint32_t f1x60; ++ uint32_t f1x64; ++ uint32_t f1x68; ++ uint32_t f1x6c; ++ uint32_t f1x70; ++ uint32_t f1x74; ++ uint32_t f1x78; ++ uint32_t f1x7c; ++ uint32_t f1xf0; ++ uint32_t f1x120; ++ uint32_t f1x124; ++ uint32_t f2x10c; ++ uint32_t f2x114; ++ uint32_t f2x118; ++ uint32_t f2x11c; ++ uint32_t f2x1b0; ++ uint32_t f3x44; ++ uint64_t msr0000020[16]; ++ uint64_t msr00000250; ++ uint64_t msr00000258; ++ uint64_t msr0000026[8]; ++ uint64_t msr000002ff; ++ uint64_t msrc0010010; ++ uint64_t msrc001001a; ++ uint64_t msrc001001d; ++ uint64_t msrc001001f; ++ ++ /* Stage 3 (21 dwords) */ ++ uint32_t f2x40; ++ uint32_t f2x44; ++ uint32_t f2x48; ++ uint32_t f2x4c; ++ uint32_t f2x50; ++ uint32_t f2x54; ++ uint32_t f2x58; ++ uint32_t f2x5c; ++ uint32_t f2x60; ++ uint32_t f2x64; ++ uint32_t f2x68; ++ uint32_t f2x6c; ++ uint32_t f2x78; ++ uint32_t f2x7c; ++ uint32_t f2x80; ++ uint32_t f2x84; ++ uint32_t f2x88; ++ uint32_t f2x8c; ++ uint32_t f2x90; ++ uint32_t f2xa4; ++ uint32_t f2xa8; ++ ++ /* Stage 4 (1 dword) */ ++ uint32_t f2x94; ++ ++ /* Stage 6 (33 dwords) */ ++ uint32_t f2x9cx0d0f0_f_8_0_0_8_4_0[9][3]; /* [lane][setting] */ ++ uint32_t f2x9cx00; ++ uint32_t f2x9cx0a; ++ uint32_t f2x9cx0c; ++ ++ /* Stage 7 (1 dword) */ ++ uint32_t f2x9cx04; ++ ++ /* Stage 9 (2 dwords) */ ++ uint32_t f2x9cx0d0fe006; ++ uint32_t f2x9cx0d0fe007; ++ ++ /* Stage 10 (78 dwords) */ ++ uint32_t f2x9cx10[12]; ++ uint32_t f2x9cx20[12]; ++ uint32_t f2x9cx3_0_0_3_1[4][3]; /* [dimm][setting] */ ++ uint32_t f2x9cx3_0_0_7_5[4][3]; /* [dimm][setting] */ ++ uint32_t f2x9cx0d; ++ uint32_t f2x9cx0d0f0_f_0_13[9]; /* [lane] */ ++ uint32_t f2x9cx0d0f0_f_0_30[9]; /* [lane] */ ++ uint32_t f2x9cx0d0f2_f_0_30[4]; /* [pad select] */ ++ uint32_t f2x9cx0d0f8_8_4_0[2][3]; /* [offset][pad select] */ ++ uint32_t f2x9cx0d0f812f; ++ ++ /* Stage 11 (24 dwords) */ ++ uint32_t f2x9cx30[12]; ++ uint32_t f2x9cx40[12]; ++ ++ /* Other (1 dword) */ ++ uint32_t f3x58; ++ ++ /* TOTAL: 250 dwords */ ++} __attribute__((packed)); ++ ++struct amd_s3_persistent_node_data { ++ uint32_t node_present; ++ struct amd_s3_persistent_mct_channel_data channel[2]; ++} __attribute__((packed)); ++ ++struct amd_s3_persistent_data { ++ struct amd_s3_persistent_node_data node[MAX_NODES_SUPPORTED]; ++} __attribute__((packed)); ++ + /*=============================================================================== + Local Error Status Codes (DCTStatStruc.ErrCode) + ===============================================================================*/ +diff --git a/src/northbridge/amd/amdmct/mct_ddr3/s3utils.c b/src/northbridge/amd/amdmct/mct_ddr3/s3utils.c +new file mode 100644 +index 0000000..78523e8 +--- /dev/null ++++ b/src/northbridge/amd/amdmct/mct_ddr3/s3utils.c +@@ -0,0 +1,610 @@ ++/* ++ * This file is part of the coreboot project. ++ * ++ * Copyright (C) 2015 Timothy Pearson <tpearson@raptorengineeringinc.com>, Raptor Engineering ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; version 2 of the License. ++ * ++ * 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 ++ */ ++ ++#include <string.h> ++#include <arch/acpi.h> ++#include <cpu/x86/msr.h> ++#include <device/device.h> ++#include <device/pci_def.h> ++#include <device/pci_ops.h> ++#include <console/console.h> ++#include <cbfs.h> ++#include <spi-generic.h> ++#include <spi_flash.h> ++ ++#include "s3utils.h" ++ ++#define S3NV_FILE_NAME "s3nv" ++ ++static ssize_t get_s3nv_file_offset(void); ++ ++ssize_t get_s3nv_file_offset(void) ++{ ++ struct region_device s3nv_region; ++ struct cbfsf s3nv_cbfs_file; ++ if (cbfs_boot_locate(&s3nv_cbfs_file, S3NV_FILE_NAME, NULL)) { ++ printk(BIOS_DEBUG, "S3 state file not found in CBFS: %s\n", S3NV_FILE_NAME); ++ return -1; ++ } ++ cbfs_file_data(&s3nv_region, &s3nv_cbfs_file); ++ ++ return s3nv_region.region.offset; ++} ++ ++static uint32_t read_amd_dct_index_register(device_t dev, uint32_t index_ctl_reg, uint32_t index) ++{ ++ uint32_t dword; ++ ++ index &= ~(1 << 30); ++ pci_write_config32(dev, index_ctl_reg, index); ++ do { ++ dword = pci_read_config32(dev, index_ctl_reg); ++ } while (!(dword & (1 << 31))); ++ dword = pci_read_config32(dev, index_ctl_reg + 0x04); ++ ++ return dword; ++} ++ ++#ifdef __RAMSTAGE__ ++static uint64_t rdmsr_uint64_t(unsigned long index) { ++ msr_t msr = rdmsr(index); ++ return (((uint64_t)msr.hi) << 32) | ((uint64_t)msr.lo); ++} ++ ++void copy_mct_data_to_save_variable(struct amd_s3_persistent_data* persistent_data) ++{ ++ uint8_t i; ++ uint8_t j; ++ uint8_t node; ++ uint8_t channel; ++ ++ /* Zero out data structure */ ++ memset(persistent_data, 0, sizeof(struct amd_s3_persistent_data)); ++ ++ /* Load data from DCTs into data structure */ ++ for (node = 0; node < MAX_NODES_SUPPORTED; node++) { ++ device_t dev_fn1 = dev_find_slot(0, PCI_DEVFN(0x18 + node, 1)); ++ device_t dev_fn2 = dev_find_slot(0, PCI_DEVFN(0x18 + node, 2)); ++ device_t dev_fn3 = dev_find_slot(0, PCI_DEVFN(0x18 + node, 3)); ++ if ((!dev_fn1) || (!dev_fn2) || (!dev_fn3)) { ++ persistent_data->node[node].node_present = 0; ++ continue; ++ } ++ persistent_data->node[node].node_present = 1; ++ ++ for (channel = 0; channel < 2; channel++) { ++ struct amd_s3_persistent_mct_channel_data* data = &persistent_data->node[node].channel[channel]; ++ ++ /* Stage 1 */ ++ data->f2x110 = pci_read_config32(dev_fn2, 0x110); ++ ++ /* Stage 2 */ ++ data->f1x40 = pci_read_config32(dev_fn1, 0x40 + (0x100 * channel)); ++ data->f1x44 = pci_read_config32(dev_fn1, 0x44 + (0x100 * channel)); ++ data->f1x48 = pci_read_config32(dev_fn1, 0x48 + (0x100 * channel)); ++ data->f1x4c = pci_read_config32(dev_fn1, 0x4c + (0x100 * channel)); ++ data->f1x50 = pci_read_config32(dev_fn1, 0x50 + (0x100 * channel)); ++ data->f1x54 = pci_read_config32(dev_fn1, 0x54 + (0x100 * channel)); ++ data->f1x58 = pci_read_config32(dev_fn1, 0x58 + (0x100 * channel)); ++ data->f1x5c = pci_read_config32(dev_fn1, 0x5c + (0x100 * channel)); ++ data->f1x60 = pci_read_config32(dev_fn1, 0x60 + (0x100 * channel)); ++ data->f1x64 = pci_read_config32(dev_fn1, 0x64 + (0x100 * channel)); ++ data->f1x68 = pci_read_config32(dev_fn1, 0x68 + (0x100 * channel)); ++ data->f1x6c = pci_read_config32(dev_fn1, 0x6c + (0x100 * channel)); ++ data->f1x70 = pci_read_config32(dev_fn1, 0x70 + (0x100 * channel)); ++ data->f1x74 = pci_read_config32(dev_fn1, 0x74 + (0x100 * channel)); ++ data->f1x78 = pci_read_config32(dev_fn1, 0x78 + (0x100 * channel)); ++ data->f1x7c = pci_read_config32(dev_fn1, 0x7c + (0x100 * channel)); ++ data->f1xf0 = pci_read_config32(dev_fn1, 0xf0); ++ data->f1x120 = pci_read_config32(dev_fn1, 0x120); ++ data->f1x124 = pci_read_config32(dev_fn1, 0x124); ++ data->f2x10c = pci_read_config32(dev_fn2, 0x10c); ++ data->f2x114 = pci_read_config32(dev_fn2, 0x114); ++ data->f2x118 = pci_read_config32(dev_fn2, 0x118); ++ data->f2x11c = pci_read_config32(dev_fn2, 0x11c); ++ data->f2x1b0 = pci_read_config32(dev_fn2, 0x1b0); ++ data->f3x44 = pci_read_config32(dev_fn3, 0x44); ++ for (i=0; i<16; i++) { ++ data->msr0000020[i] = rdmsr_uint64_t(0x00000200 | i); ++ } ++ data->msr00000250 = rdmsr_uint64_t(0x00000250); ++ data->msr00000258 = rdmsr_uint64_t(0x00000258); ++ for (i=0; i<8; i++) ++ data->msr0000026[i] = rdmsr_uint64_t(0x00000260 | (i + 8)); ++ data->msr000002ff = rdmsr_uint64_t(0x000002ff); ++ data->msrc0010010 = rdmsr_uint64_t(0xc0010010); ++ data->msrc001001a = rdmsr_uint64_t(0xc001001a); ++ data->msrc001001d = rdmsr_uint64_t(0xc001001d); ++ data->msrc001001f = rdmsr_uint64_t(0xc001001f); ++ ++ /* Stage 3 */ ++ data->f2x40 = pci_read_config32(dev_fn2, 0x40 + (0x100 * channel)); ++ data->f2x44 = pci_read_config32(dev_fn2, 0x44 + (0x100 * channel)); ++ data->f2x48 = pci_read_config32(dev_fn2, 0x48 + (0x100 * channel)); ++ data->f2x4c = pci_read_config32(dev_fn2, 0x4c + (0x100 * channel)); ++ data->f2x50 = pci_read_config32(dev_fn2, 0x50 + (0x100 * channel)); ++ data->f2x54 = pci_read_config32(dev_fn2, 0x54 + (0x100 * channel)); ++ data->f2x58 = pci_read_config32(dev_fn2, 0x58 + (0x100 * channel)); ++ data->f2x5c = pci_read_config32(dev_fn2, 0x5c + (0x100 * channel)); ++ data->f2x60 = pci_read_config32(dev_fn2, 0x60 + (0x100 * channel)); ++ data->f2x64 = pci_read_config32(dev_fn2, 0x64 + (0x100 * channel)); ++ data->f2x68 = pci_read_config32(dev_fn2, 0x68 + (0x100 * channel)); ++ data->f2x6c = pci_read_config32(dev_fn2, 0x6c + (0x100 * channel)); ++ data->f2x78 = pci_read_config32(dev_fn2, 0x78 + (0x100 * channel)); ++ data->f2x7c = pci_read_config32(dev_fn2, 0x7c + (0x100 * channel)); ++ data->f2x80 = pci_read_config32(dev_fn2, 0x80 + (0x100 * channel)); ++ data->f2x84 = pci_read_config32(dev_fn2, 0x84 + (0x100 * channel)); ++ data->f2x88 = pci_read_config32(dev_fn2, 0x88 + (0x100 * channel)); ++ data->f2x8c = pci_read_config32(dev_fn2, 0x8c + (0x100 * channel)); ++ data->f2x90 = pci_read_config32(dev_fn2, 0x90 + (0x100 * channel)); ++ data->f2xa4 = pci_read_config32(dev_fn2, 0xa4 + (0x100 * channel)); ++ data->f2xa8 = pci_read_config32(dev_fn2, 0xa8 + (0x100 * channel)); ++ ++ /* Stage 4 */ ++ data->f2x94 = pci_read_config32(dev_fn2, 0x94 + (0x100 * channel)); ++ ++ /* Stage 6 */ ++ for (i=0; i<9; i++) ++ for (j=0; j<3; j++) ++ data->f2x9cx0d0f0_f_8_0_0_8_4_0[i][j] = read_amd_dct_index_register(dev_fn2, 0x98 + (0x100 * channel), 0x0d0f0000 | (i << 8) | (j * 4)); ++ data->f2x9cx00 = read_amd_dct_index_register(dev_fn2, 0x98 + (0x100 * channel), 0x00); ++ data->f2x9cx0a = read_amd_dct_index_register(dev_fn2, 0x98 + (0x100 * channel), 0x0a); ++ data->f2x9cx0c = read_amd_dct_index_register(dev_fn2, 0x98 + (0x100 * channel), 0x0c); ++ ++ /* Stage 7 */ ++ data->f2x9cx04 = read_amd_dct_index_register(dev_fn2, 0x98 + (0x100 * channel), 0x04); ++ ++ /* Stage 9 */ ++ data->f2x9cx0d0fe006 = read_amd_dct_index_register(dev_fn2, 0x98 + (0x100 * channel), 0x0d0fe006); ++ data->f2x9cx0d0fe007 = read_amd_dct_index_register(dev_fn2, 0x98 + (0x100 * channel), 0x0d0fe007); ++ ++ /* Stage 10 */ ++ for (i=0; i<12; i++) ++ data->f2x9cx10[i] = read_amd_dct_index_register(dev_fn2, 0x98 + (0x100 * channel), 0x10 + i); ++ for (i=0; i<12; i++) ++ data->f2x9cx20[i] = read_amd_dct_index_register(dev_fn2, 0x98 + (0x100 * channel), 0x20 + i); ++ for (i=0; i<4; i++) ++ for (j=0; j<3; j++) ++ data->f2x9cx3_0_0_3_1[i][j] = read_amd_dct_index_register(dev_fn2, 0x98 + (0x100 * channel), (0x01 + i) + (0x100 * j)); ++ for (i=0; i<4; i++) ++ for (j=0; j<3; j++) ++ data->f2x9cx3_0_0_7_5[i][j] = read_amd_dct_index_register(dev_fn2, 0x98 + (0x100 * channel), (0x05 + i) + (0x100 * j)); ++ data->f2x9cx0d = read_amd_dct_index_register(dev_fn2, 0x98 + (0x100 * channel), 0x0d); ++ for (i=0; i<9; i++) ++ data->f2x9cx0d0f0_f_0_13[i] = read_amd_dct_index_register(dev_fn2, 0x98 + (0x100 * channel), 0x0d0f0013 | (i << 8)); ++ for (i=0; i<9; i++) ++ data->f2x9cx0d0f0_f_0_30[i] = read_amd_dct_index_register(dev_fn2, 0x98 + (0x100 * channel), 0x0d0f0030 | (i << 8)); ++ for (i=0; i<4; i++) ++ data->f2x9cx0d0f2_f_0_30[i] = read_amd_dct_index_register(dev_fn2, 0x98 + (0x100 * channel), 0x0d0f2030 | (i << 8)); ++ for (i=0; i<2; i++) ++ for (j=0; j<3; j++) ++ data->f2x9cx0d0f8_8_4_0[i][j] = read_amd_dct_index_register(dev_fn2, 0x98 + (0x100 * channel), 0x0d0f0000 | (i << 8) | (j * 4)); ++ data->f2x9cx0d0f812f = read_amd_dct_index_register(dev_fn2, 0x98 + (0x100 * channel), 0x0d0f812f); ++ ++ /* Stage 11 */ ++ if (IS_ENABLED(CONFIG_DIMM_DDR3)) { ++ for (i=0; i<12; i++) ++ data->f2x9cx30[i] = read_amd_dct_index_register(dev_fn2, 0x98 + (0x100 * channel), 0x30 + i); ++ for (i=0; i<12; i++) ++ data->f2x9cx40[i] = read_amd_dct_index_register(dev_fn2, 0x98 + (0x100 * channel), 0x40 + i); ++ } ++ ++ /* Other */ ++ /* ECC scrub rate control */ ++ data->f3x58 = pci_read_config32(dev_fn3, 0x58); ++ } ++ } ++} ++#else ++static void write_amd_dct_index_register(device_t dev, uint32_t index_ctl_reg, uint32_t index, uint32_t value) ++{ ++ uint32_t dword; ++ ++ pci_write_config32(dev, index_ctl_reg + 0x04, value); ++ index |= (1 << 30); ++ pci_write_config32(dev, index_ctl_reg, index); ++ do { ++ dword = pci_read_config32(dev, index_ctl_reg); ++ } while (!(dword & (1 << 31))); ++} ++#endif ++ ++#ifdef __PRE_RAM__ ++static void wrmsr_uint64_t(unsigned long index, uint64_t value) { ++ msr_t msr; ++ msr.hi = (value & 0xffffffff00000000ULL) >> 32; ++ msr.lo = (value & 0xffffffff); ++ wrmsr(index, msr); ++} ++ ++void restore_mct_data_from_save_variable(struct amd_s3_persistent_data* persistent_data) ++{ ++ uint8_t i; ++ uint8_t j; ++ uint8_t node; ++ uint8_t channel; ++ uint8_t ganged; ++ uint8_t dct_enabled; ++ uint32_t dword; ++ ++ /* Load data from data structure into DCTs */ ++ /* Stage 1 */ ++ for (node = 0; node < MAX_NODES_SUPPORTED; node++) { ++ for (channel = 0; channel < 2; channel++) { ++ struct amd_s3_persistent_mct_channel_data* data = &persistent_data->node[node].channel[channel]; ++ if (!persistent_data->node[node].node_present) ++ continue; ++ ++ pci_write_config32(PCI_DEV(0, 0x18 + node, 2), 0x110, data->f2x110); ++ } ++ } ++ ++ /* Stage 2 */ ++ for (node = 0; node < MAX_NODES_SUPPORTED; node++) { ++ for (channel = 0; channel < 2; channel++) { ++ struct amd_s3_persistent_mct_channel_data* data = &persistent_data->node[node].channel[channel]; ++ if (!persistent_data->node[node].node_present) ++ continue; ++ ++ pci_write_config32(PCI_DEV(0, 0x18 + node, 1), 0x40 + (0x100 * channel), data->f1x40); ++ pci_write_config32(PCI_DEV(0, 0x18 + node, 1), 0x44 + (0x100 * channel), data->f1x44); ++ pci_write_config32(PCI_DEV(0, 0x18 + node, 1), 0x48 + (0x100 * channel), data->f1x48); ++ pci_write_config32(PCI_DEV(0, 0x18 + node, 1), 0x4c + (0x100 * channel), data->f1x4c); ++ pci_write_config32(PCI_DEV(0, 0x18 + node, 1), 0x50 + (0x100 * channel), data->f1x50); ++ pci_write_config32(PCI_DEV(0, 0x18 + node, 1), 0x54 + (0x100 * channel), data->f1x54); ++ pci_write_config32(PCI_DEV(0, 0x18 + node, 1), 0x58 + (0x100 * channel), data->f1x58); ++ pci_write_config32(PCI_DEV(0, 0x18 + node, 1), 0x5c + (0x100 * channel), data->f1x5c); ++ pci_write_config32(PCI_DEV(0, 0x18 + node, 1), 0x60 + (0x100 * channel), data->f1x60); ++ pci_write_config32(PCI_DEV(0, 0x18 + node, 1), 0x64 + (0x100 * channel), data->f1x64); ++ pci_write_config32(PCI_DEV(0, 0x18 + node, 1), 0x68 + (0x100 * channel), data->f1x68); ++ pci_write_config32(PCI_DEV(0, 0x18 + node, 1), 0x6c + (0x100 * channel), data->f1x6c); ++ pci_write_config32(PCI_DEV(0, 0x18 + node, 1), 0x70 + (0x100 * channel), data->f1x70); ++ pci_write_config32(PCI_DEV(0, 0x18 + node, 1), 0x74 + (0x100 * channel), data->f1x74); ++ pci_write_config32(PCI_DEV(0, 0x18 + node, 1), 0x78 + (0x100 * channel), data->f1x78); ++ pci_write_config32(PCI_DEV(0, 0x18 + node, 1), 0x7c + (0x100 * channel), data->f1x7c); ++ pci_write_config32(PCI_DEV(0, 0x18 + node, 1), 0xf0 + (0x100 * channel), data->f1xf0); ++ pci_write_config32(PCI_DEV(0, 0x18 + node, 1), 0x120 + (0x100 * channel), data->f1x120); ++ pci_write_config32(PCI_DEV(0, 0x18 + node, 1), 0x124 + (0x100 * channel), data->f1x124); ++ pci_write_config32(PCI_DEV(0, 0x18 + node, 2), 0x10c + (0x100 * channel), data->f2x10c); ++ pci_write_config32(PCI_DEV(0, 0x18 + node, 2), 0x114 + (0x100 * channel), data->f2x114); ++ pci_write_config32(PCI_DEV(0, 0x18 + node, 2), 0x118 + (0x100 * channel), data->f2x118); ++ pci_write_config32(PCI_DEV(0, 0x18 + node, 2), 0x11c + (0x100 * channel), data->f2x11c); ++ pci_write_config32(PCI_DEV(0, 0x18 + node, 2), 0x1b0 + (0x100 * channel), data->f2x1b0); ++ pci_write_config32(PCI_DEV(0, 0x18 + node, 3), 0x44 + (0x100 * channel), data->f3x44); ++ for (i=0; i<16; i++) { ++ wrmsr_uint64_t(0x00000200 | i, data->msr0000020[i]); ++ } ++ wrmsr_uint64_t(0x00000250, data->msr00000250); ++ wrmsr_uint64_t(0x00000258, data->msr00000258); ++ /* FIXME ++ * Restoring these MSRs causes a hang on resume ++ * For now, skip restoration... ++ */ ++ // for (i=0; i<8; i++) ++ // wrmsr_uint64_t(0x00000260 | (i + 8), data->msr0000026[i]); ++ wrmsr_uint64_t(0x000002ff, data->msr000002ff); ++ wrmsr_uint64_t(0xc0010010, data->msrc0010010); ++ wrmsr_uint64_t(0xc001001a, data->msrc001001a); ++ wrmsr_uint64_t(0xc001001d, data->msrc001001d); ++ wrmsr_uint64_t(0xc001001f, data->msrc001001f); ++ } ++ } ++ ++ /* Stage 3 */ ++ for (node = 0; node < MAX_NODES_SUPPORTED; node++) { ++ for (channel = 0; channel < 2; channel++) { ++ struct amd_s3_persistent_mct_channel_data* data = &persistent_data->node[node].channel[channel]; ++ if (!persistent_data->node[node].node_present) ++ continue; ++ ++ ganged = !!(data->f2x110 & 0x10); ++ if ((ganged == 1) && (channel > 0)) ++ continue; ++ ++ pci_write_config32(PCI_DEV(0, 0x18 + node, 2), 0x40 + (0x100 * channel), data->f2x40); ++ pci_write_config32(PCI_DEV(0, 0x18 + node, 2), 0x44 + (0x100 * channel), data->f2x44); ++ pci_write_config32(PCI_DEV(0, 0x18 + node, 2), 0x48 + (0x100 * channel), data->f2x48); ++ pci_write_config32(PCI_DEV(0, 0x18 + node, 2), 0x4c + (0x100 * channel), data->f2x4c); ++ pci_write_config32(PCI_DEV(0, 0x18 + node, 2), 0x50 + (0x100 * channel), data->f2x50); ++ pci_write_config32(PCI_DEV(0, 0x18 + node, 2), 0x54 + (0x100 * channel), data->f2x54); ++ pci_write_config32(PCI_DEV(0, 0x18 + node, 2), 0x58 + (0x100 * channel), data->f2x58); ++ pci_write_config32(PCI_DEV(0, 0x18 + node, 2), 0x5c + (0x100 * channel), data->f2x5c); ++ pci_write_config32(PCI_DEV(0, 0x18 + node, 2), 0x60 + (0x100 * channel), data->f2x60); ++ pci_write_config32(PCI_DEV(0, 0x18 + node, 2), 0x64 + (0x100 * channel), data->f2x64); ++ pci_write_config32(PCI_DEV(0, 0x18 + node, 2), 0x68 + (0x100 * channel), data->f2x68); ++ pci_write_config32(PCI_DEV(0, 0x18 + node, 2), 0x6c + (0x100 * channel), data->f2x6c); ++ pci_write_config32(PCI_DEV(0, 0x18 + node, 2), 0x78 + (0x100 * channel), data->f2x78); ++ pci_write_config32(PCI_DEV(0, 0x18 + node, 2), 0x7c + (0x100 * channel), data->f2x7c); ++ pci_write_config32(PCI_DEV(0, 0x18 + node, 2), 0x80 + (0x100 * channel), data->f2x80); ++ pci_write_config32(PCI_DEV(0, 0x18 + node, 2), 0x84 + (0x100 * channel), data->f2x84); ++ pci_write_config32(PCI_DEV(0, 0x18 + node, 2), 0x88 + (0x100 * channel), data->f2x88); ++ pci_write_config32(PCI_DEV(0, 0x18 + node, 2), 0x8c + (0x100 * channel), data->f2x8c); ++ pci_write_config32(PCI_DEV(0, 0x18 + node, 2), 0x90 + (0x100 * channel), data->f2x90); ++ pci_write_config32(PCI_DEV(0, 0x18 + node, 2), 0xa4 + (0x100 * channel), data->f2xa4); ++ pci_write_config32(PCI_DEV(0, 0x18 + node, 2), 0xa8 + (0x100 * channel), data->f2xa8); ++ } ++ } ++ ++ /* Stage 4 */ ++ for (node = 0; node < MAX_NODES_SUPPORTED; node++) { ++ for (channel = 0; channel < 2; channel++) { ++ struct amd_s3_persistent_mct_channel_data* data = &persistent_data->node[node].channel[channel]; ++ if (!persistent_data->node[node].node_present) ++ continue; ++ ++ ganged = !!(data->f2x110 & 0x10); ++ if ((ganged == 1) && (channel > 0)) ++ continue; ++ ++ /* Disable PHY auto-compensation engine */ ++ dword = read_amd_dct_index_register(PCI_DEV(0, 0x18 + node, 2), 0x98 + (0x100 * channel), 0x08); ++ if (!(dword & (1 << 30))) { ++ dword |= (1 << 30); ++ write_amd_dct_index_register(PCI_DEV(0, 0x18 + node, 2), 0x98 + (0x100 * channel), 0x08, dword); ++ ++ /* Wait for 5us */ ++ mct_Wait(100); ++ } ++ ++ /* Restore DRAM Configuration High Register */ ++ pci_write_config32(PCI_DEV(0, 0x18 + node, 2), 0x94 + (0x100 * channel), data->f2x94); ++ ++ /* Enable PHY auto-compensation engine */ ++ dword = read_amd_dct_index_register(PCI_DEV(0, 0x18 + node, 2), 0x98 + (0x100 * channel), 0x08); ++ dword &= ~(1 << 30); ++ write_amd_dct_index_register(PCI_DEV(0, 0x18 + node, 2), 0x98 + (0x100 * channel), 0x08, dword); ++ } ++ } ++ ++ /* Wait for 750us */ ++ mct_Wait(15000); ++ ++ /* Stage 5 */ ++ for (node = 0; node < MAX_NODES_SUPPORTED; node++) { ++ for (channel = 0; channel < 2; channel++) { ++ struct amd_s3_persistent_mct_channel_data* data = &persistent_data->node[node].channel[channel]; ++ if (!persistent_data->node[node].node_present) ++ continue; ++ ++ ganged = !!(data->f2x110 & 0x10); ++ if ((ganged == 1) && (channel > 0)) ++ continue; ++ ++ /* Wait for any pending PHY frequency changes to complete */ ++ do { ++ dword = read_amd_dct_index_register(PCI_DEV(0, 0x18 + node, 2), 0x98 + (0x100 * channel), 0x08); ++ } while (dword & (1 << 21)); ++ } ++ } ++ ++ /* Stage 6 */ ++ for (node = 0; node < MAX_NODES_SUPPORTED; node++) { ++ for (channel = 0; channel < 2; channel++) { ++ struct amd_s3_persistent_mct_channel_data* data = &persistent_data->node[node].channel[channel]; ++ if (!persistent_data->node[node].node_present) ++ continue; ++ ++ for (i=0; i<9; i++) ++ for (j=0; j<3; j++) ++ write_amd_dct_index_register(PCI_DEV(0, 0x18 + node, 2), 0x98 + (0x100 * channel), 0x0d0f0000 | (i << 8) | (j * 4), data->f2x9cx0d0f0_f_8_0_0_8_4_0[i][j]); ++ write_amd_dct_index_register(PCI_DEV(0, 0x18 + node, 2), 0x98 + (0x100 * channel), 0x00, data->f2x9cx00); ++ write_amd_dct_index_register(PCI_DEV(0, 0x18 + node, 2), 0x98 + (0x100 * channel), 0x0a, data->f2x9cx0a); ++ write_amd_dct_index_register(PCI_DEV(0, 0x18 + node, 2), 0x98 + (0x100 * channel), 0x0c, data->f2x9cx0c); ++ } ++ } ++ ++ /* Stage 7 */ ++ for (node = 0; node < MAX_NODES_SUPPORTED; node++) { ++ for (channel = 0; channel < 2; channel++) { ++ struct amd_s3_persistent_mct_channel_data* data = &persistent_data->node[node].channel[channel]; ++ if (!persistent_data->node[node].node_present) ++ continue; ++ ++ ganged = !!(data->f2x110 & 0x10); ++ if ((ganged == 1) && (channel > 0)) ++ continue; ++ ++ write_amd_dct_index_register(PCI_DEV(0, 0x18 + node, 2), 0x98 + (0x100 * channel), 0x04, data->f2x9cx04); ++ } ++ } ++ ++ /* Stage 8 */ ++ for (node = 0; node < MAX_NODES_SUPPORTED; node++) { ++ for (channel = 0; channel < 2; channel++) { ++ struct amd_s3_persistent_mct_channel_data* data = &persistent_data->node[node].channel[channel]; ++ if (!persistent_data->node[node].node_present) ++ continue; ++ ++ dct_enabled = !(data->f2x94 & (1 << 14)); ++ if (!dct_enabled) ++ continue; ++ ++ ganged = !!(data->f2x110 & 0x10); ++ if ((ganged == 1) && (channel > 0)) ++ continue; ++ ++ printk(BIOS_SPEW, "Taking DIMMs out of self refresh node: %d channel: %d\n", node, channel); ++ ++ /* Exit self refresh mode */ ++ dword = pci_read_config32(PCI_DEV(0, 0x18 + node, 2), 0x90 + (0x100 * channel)); ++ dword |= (1 << 1); ++ pci_write_config32(PCI_DEV(0, 0x18 + node, 2), 0x90 + (0x100 * channel), dword); ++ } ++ } ++ ++ /* Stage 9 */ ++ for (node = 0; node < MAX_NODES_SUPPORTED; node++) { ++ for (channel = 0; channel < 2; channel++) { ++ struct amd_s3_persistent_mct_channel_data* data = &persistent_data->node[node].channel[channel]; ++ if (!persistent_data->node[node].node_present) ++ continue; ++ ++ dct_enabled = !(data->f2x94 & (1 << 14)); ++ if (!dct_enabled) ++ continue; ++ ++ printk(BIOS_SPEW, "Waiting for DIMMs to exit self refresh node: %d channel: %d\n", node, channel); ++ ++ /* Wait for transition from self refresh mode to complete */ ++ do { ++ dword = pci_read_config32(PCI_DEV(0, 0x18 + node, 2), 0x90 + (0x100 * channel)); ++ } while (dword & (1 << 1)); ++ ++ /* Restore registers */ ++ write_amd_dct_index_register(PCI_DEV(0, 0x18 + node, 2), 0x98 + (0x100 * channel), 0x0d0fe006, data->f2x9cx0d0fe006); ++ write_amd_dct_index_register(PCI_DEV(0, 0x18 + node, 2), 0x98 + (0x100 * channel), 0x0d0fe007, data->f2x9cx0d0fe007); ++ } ++ } ++ ++ /* Stage 10 */ ++ for (node = 0; node < MAX_NODES_SUPPORTED; node++) { ++ for (channel = 0; channel < 2; channel++) { ++ struct amd_s3_persistent_mct_channel_data* data = &persistent_data->node[node].channel[channel]; ++ if (!persistent_data->node[node].node_present) ++ continue; ++ ++ for (i=0; i<12; i++) ++ write_amd_dct_index_register(PCI_DEV(0, 0x18 + node, 2), 0x98 + (0x100 * channel), 0x10 + i, data->f2x9cx10[i]); ++ for (i=0; i<12; i++) ++ write_amd_dct_index_register(PCI_DEV(0, 0x18 + node, 2), 0x98 + (0x100 * channel), 0x20 + i, data->f2x9cx20[i]); ++ for (i=0; i<4; i++) ++ for (j=0; j<3; j++) ++ write_amd_dct_index_register(PCI_DEV(0, 0x18 + node, 2), 0x98 + (0x100 * channel), (0x01 + i) + (0x100 * j), data->f2x9cx3_0_0_3_1[i][j]); ++ for (i=0; i<4; i++) ++ for (j=0; j<3; j++) ++ write_amd_dct_index_register(PCI_DEV(0, 0x18 + node, 2), 0x98 + (0x100 * channel), (0x05 + i) + (0x100 * j), data->f2x9cx3_0_0_7_5[i][j]); ++ write_amd_dct_index_register(PCI_DEV(0, 0x18 + node, 2), 0x98 + (0x100 * channel), 0x0d, data->f2x9cx0d); ++ for (i=0; i<9; i++) ++ write_amd_dct_index_register(PCI_DEV(0, 0x18 + node, 2), 0x98 + (0x100 * channel), 0x0d0f0013 | (i << 8), data->f2x9cx0d0f0_f_0_13[i]); ++ for (i=0; i<9; i++) ++ write_amd_dct_index_register(PCI_DEV(0, 0x18 + node, 2), 0x98 + (0x100 * channel), 0x0d0f0030 | (i << 8), data->f2x9cx0d0f0_f_0_30[i]); ++ for (i=0; i<4; i++) ++ write_amd_dct_index_register(PCI_DEV(0, 0x18 + node, 2), 0x98 + (0x100 * channel), 0x0d0f2030 | (i << 8), data->f2x9cx0d0f2_f_0_30[i]); ++ for (i=0; i<2; i++) ++ for (j=0; j<3; j++) ++ write_amd_dct_index_register(PCI_DEV(0, 0x18 + node, 2), 0x98 + (0x100 * channel), 0x0d0f0000 | (i << 8) | (j * 4), data->f2x9cx0d0f8_8_4_0[i][j]); ++ write_amd_dct_index_register(PCI_DEV(0, 0x18 + node, 2), 0x98 + (0x100 * channel), 0x0d0f812f, data->f2x9cx0d0f812f); ++ } ++ } ++ ++ /* Stage 11 */ ++ if (IS_ENABLED(CONFIG_DIMM_DDR3)) { ++ for (node = 0; node < MAX_NODES_SUPPORTED; node++) { ++ for (channel = 0; channel < 2; channel++) { ++ struct amd_s3_persistent_mct_channel_data* data = &persistent_data->node[node].channel[channel]; ++ if (!persistent_data->node[node].node_present) ++ continue; ++ ++ for (i=0; i<12; i++) ++ write_amd_dct_index_register(PCI_DEV(0, 0x18 + node, 2), 0x98 + (0x100 * channel), 0x30 + i, data->f2x9cx30[i]); ++ for (i=0; i<12; i++) ++ write_amd_dct_index_register(PCI_DEV(0, 0x18 + node, 2), 0x98 + (0x100 * channel), 0x40 + i, data->f2x9cx40[i]); ++ } ++ } ++ } ++ ++ /* Other */ ++ for (node = 0; node < MAX_NODES_SUPPORTED; node++) { ++ for (channel = 0; channel < 2; channel++) { ++ struct amd_s3_persistent_mct_channel_data* data = &persistent_data->node[node].channel[channel]; ++ if (!persistent_data->node[node].node_present) ++ continue; ++ ++ /* ECC scrub rate control */ ++ pci_write_config32(PCI_DEV(0, 0x18 + node, 3), 0x58, data->f3x58); ++ } ++ } ++} ++#endif ++ ++#ifdef __RAMSTAGE__ ++int8_t save_mct_information_to_nvram(void) ++{ ++ if (acpi_is_wakeup_s3()) ++ return 0; ++ ++ printk(BIOS_DEBUG, "Writing AMD DCT configuration to Flash\n"); ++ ++ struct spi_flash *flash; ++ ssize_t s3nv_offset; ++ struct amd_s3_persistent_data persistent_data; ++ ++ /* Obtain MCT configuration data */ ++ copy_mct_data_to_save_variable(&persistent_data); ++ ++ /* Obtain CBFS file offset */ ++ s3nv_offset = get_s3nv_file_offset(); ++ if (s3nv_offset == -1) ++ return -1; ++ ++ /* Align flash pointer to nearest boundary */ ++ s3nv_offset &= ~(CONFIG_S3_DATA_SIZE-1); ++ s3nv_offset += CONFIG_S3_DATA_SIZE; ++ ++ /* Set temporary SPI MMIO address */ ++ device_t lpc_dev = dev_find_slot(0, PCI_DEVFN(0x14, 3)); ++ uint32_t spi_mmio_prev = pci_read_config32(lpc_dev, 0xa0); ++ pci_write_config32(lpc_dev, 0xa0, (spi_mmio_prev & 0x1f) | 0xf0000000); ++ ++ /* Initialize SPI and detect devices */ ++ spi_init(); ++ flash = spi_flash_probe(0, 0); ++ if (!flash) { ++ printk(BIOS_DEBUG, "Could not find SPI device\n"); ++ return -1; ++ } ++ ++ /* Set up SPI flash access */ ++ flash->spi->rw = SPI_WRITE_FLAG; ++ spi_claim_bus(flash->spi); ++ ++ /* Erase and write data structure */ ++ flash->erase(flash, s3nv_offset, CONFIG_S3_DATA_SIZE); ++ flash->write(flash, s3nv_offset, sizeof(struct amd_s3_persistent_data), &persistent_data); ++ ++ /* Tear down SPI flash access */ ++ flash->spi->rw = SPI_WRITE_FLAG; ++ spi_release_bus(flash->spi); ++ ++ /* Restore SPI MMIO address */ ++ pci_write_config32(lpc_dev, 0xa0, spi_mmio_prev); ++ ++ return 0; ++} ++#endif ++ ++int8_t restore_mct_information_from_nvram(void) ++{ ++ ssize_t s3nv_offset; ++ struct amd_s3_persistent_data persistent_data; ++ ++ /* Obtain CBFS file offset */ ++ s3nv_offset = get_s3nv_file_offset(); ++ if (s3nv_offset == -1) ++ return -1; ++ ++ /* Align flash pointer to nearest boundary */ ++ s3nv_offset &= ~(CONFIG_S3_DATA_SIZE-1); ++ s3nv_offset += CONFIG_S3_DATA_SIZE; ++ ++ cbfs_read(CBFS_DEFAULT_MEDIA, &persistent_data, s3nv_offset, sizeof(struct amd_s3_persistent_data)); ++ restore_mct_data_from_save_variable(&persistent_data); ++ ++ return 0; ++} +\ No newline at end of file +diff --git a/src/northbridge/amd/amdmct/mct_ddr3/s3utils.h b/src/northbridge/amd/amdmct/mct_ddr3/s3utils.h +new file mode 100644 +index 0000000..dcddcad +--- /dev/null ++++ b/src/northbridge/amd/amdmct/mct_ddr3/s3utils.h +@@ -0,0 +1,28 @@ ++/* ++ * This file is part of the coreboot project. ++ * ++ * Copyright (C) 2015 Timothy Pearson <tpearson@raptorengineeringinc.com>, Raptor Engineering ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; version 2 of the License. ++ * ++ * 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 ++ */ ++ ++#include "../wrappers/mcti.h" ++#include "mct_d.h" ++ ++#ifdef __RAMSTAGE__ ++int8_t save_mct_information_to_nvram(void); ++#endif ++int8_t restore_mct_information_from_nvram(void); ++void copy_mct_data_to_save_variable(struct amd_s3_persistent_data* persistent_data); ++void restore_mct_data_from_save_variable(struct amd_s3_persistent_data* persistent_data); +\ No newline at end of file +-- +1.9.1 + |