diff options
Diffstat (limited to 'src/northbridge/amd/amdmct/mct_ddr3/s3utils.c')
-rw-r--r-- | src/northbridge/amd/amdmct/mct_ddr3/s3utils.c | 175 |
1 files changed, 151 insertions, 24 deletions
diff --git a/src/northbridge/amd/amdmct/mct_ddr3/s3utils.c b/src/northbridge/amd/amdmct/mct_ddr3/s3utils.c index 3c2043be54..c4410443e0 100644 --- a/src/northbridge/amd/amdmct/mct_ddr3/s3utils.c +++ b/src/northbridge/amd/amdmct/mct_ddr3/s3utils.c @@ -22,8 +22,10 @@ #include <device/pci_ops.h> #include <console/console.h> #include <cbfs.h> +#include <cbmem.h> #include <spi-generic.h> #include <spi_flash.h> +#include <pc80/mc146818rtc.h> #include "s3utils.h" @@ -120,6 +122,68 @@ static uint32_t read_amd_dct_index_register_dct(device_t dev, uint8_t node, uint return read_amd_dct_index_register(dev, index_ctl_reg, index); } +/* Non-cryptographic 64-bit hash function taken from Stack Overflow: + * http://stackoverflow.com/a/13326345 + * Any 64-bit hash with sufficiently low collision potential + * could be used instead. + */ +void calculate_spd_hash(uint8_t *spd_data, uint64_t *spd_hash) +{ + const unsigned long long prime = 2654435789ULL; + uint16_t byte; + *spd_hash = 104395301; + + for (byte = 0; byte < 256; byte++) + *spd_hash += (spd_data[byte] * prime) ^ (*spd_hash >> 23); + + *spd_hash = *spd_hash ^ (*spd_hash << 37); +} + +static struct amd_s3_persistent_data * map_s3nv_in_nvram(void) +{ + ssize_t s3nv_offset; + ssize_t s3nv_file_offset; + void * s3nv_cbfs_file_ptr; + struct amd_s3_persistent_data *persistent_data; + + /* Obtain CBFS file offset */ + s3nv_offset = get_s3nv_file_offset(); + if (s3nv_offset == -1) + return NULL; + + /* Align flash pointer to nearest boundary */ + s3nv_file_offset = s3nv_offset; + s3nv_offset &= ~(CONFIG_S3_DATA_SIZE-1); + s3nv_offset += CONFIG_S3_DATA_SIZE; + s3nv_file_offset = s3nv_offset - s3nv_file_offset; + + /* Map data structure in CBFS and restore settings */ + s3nv_cbfs_file_ptr = cbfs_boot_map_with_leak(S3NV_FILE_NAME, CBFS_TYPE_RAW, NULL); + if (!s3nv_cbfs_file_ptr) { + printk(BIOS_DEBUG, "S3 state file could not be mapped: %s\n", S3NV_FILE_NAME); + return NULL; + } + persistent_data = (s3nv_cbfs_file_ptr + s3nv_file_offset); + + return persistent_data; +} + +#ifdef __PRE_RAM__ +int8_t load_spd_hashes_from_nvram(struct DCTStatStruc *pDCTstat) +{ + struct amd_s3_persistent_data *persistent_data; + + persistent_data = map_s3nv_in_nvram(); + if (!persistent_data) + return -1; + + memcpy(pDCTstat->spd_data.nvram_spd_hash, persistent_data->node[pDCTstat->Node_ID].spd_hash, sizeof(pDCTstat->spd_data.nvram_spd_hash)); + memcpy(pDCTstat->spd_data.nvram_memclk, persistent_data->node[pDCTstat->Node_ID].memclk, sizeof(pDCTstat->spd_data.nvram_memclk)); + + return 0; +} +#endif + #ifdef __RAMSTAGE__ static uint64_t rdmsr_uint64_t(unsigned long index) { msr_t msr = rdmsr(index); @@ -145,6 +209,31 @@ static uint32_t read_config32_dct_nbpstate(device_t dev, uint8_t node, uint8_t d return pci_read_config32(dev, reg); } +static void copy_cbmem_spd_data_to_save_variable(struct amd_s3_persistent_data* persistent_data) +{ + uint8_t node; + uint8_t dimm; + uint8_t channel; + struct amdmct_memory_info *mem_info; + mem_info = cbmem_find(CBMEM_ID_AMDMCT_MEMINFO); + if (mem_info == NULL) { + /* can't find amdmct information in cbmem */ + for (node = 0; node < MAX_NODES_SUPPORTED; node++) + for (dimm = 0; dimm < MAX_DIMMS_SUPPORTED; dimm++) + persistent_data->node[node].spd_hash[dimm] = 0xffffffffffffffffULL; + + return; + } + + for (node = 0; node < MAX_NODES_SUPPORTED; node++) + for (dimm = 0; dimm < MAX_DIMMS_SUPPORTED; dimm++) + calculate_spd_hash(mem_info->dct_stat[node].spd_data.spd_bytes[dimm], &persistent_data->node[node].spd_hash[dimm]); + + for (node = 0; node < MAX_NODES_SUPPORTED; node++) + for (channel = 0; channel < 2; channel++) + persistent_data->node[node].memclk[channel] = mem_info->dct_stat[node].Speed; +} + void copy_mct_data_to_save_variable(struct amd_s3_persistent_data* persistent_data) { uint8_t i; @@ -437,7 +526,7 @@ static void wrmsr_uint64_t(unsigned long index, uint64_t value) { wrmsr(index, msr); } -void restore_mct_data_from_save_variable(struct amd_s3_persistent_data* persistent_data) +void restore_mct_data_from_save_variable(struct amd_s3_persistent_data* persistent_data, uint8_t training_only) { uint8_t i; uint8_t j; @@ -447,6 +536,51 @@ void restore_mct_data_from_save_variable(struct amd_s3_persistent_data* persiste uint8_t dct_enabled; uint32_t dword; + if (training_only) { + /* Only restore the Receiver Enable and DQS training registers */ + 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; + + /* Restore training parameters */ + for (i=0; i<4; i++) + for (j=0; j<3; j++) + write_amd_dct_index_register_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x98, (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_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x98, (0x05 + i) + (0x100 * j), data->f2x9cx3_0_0_7_5[i][j]); + + for (i=0; i<12; i++) + write_amd_dct_index_register_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x98, 0x10 + i, data->f2x9cx10[i]); + for (i=0; i<12; i++) + write_amd_dct_index_register_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x98, 0x20 + i, data->f2x9cx20[i]); + + if (IS_ENABLED(CONFIG_DIMM_DDR3)) { + for (i=0; i<12; i++) + write_amd_dct_index_register_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x98, 0x30 + i, data->f2x9cx30[i]); + for (i=0; i<12; i++) + write_amd_dct_index_register_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x98, 0x40 + i, data->f2x9cx40[i]); + } + + /* Restore MaxRdLatency */ + if (is_fam15h()) { + for (i=0; i<4; i++) + write_config32_dct_nbpstate(PCI_DEV(0, 0x18 + node, 2), node, channel, i, 0x210, data->f2x210[i]); + } + else { + write_config32_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x78, data->f2x78); + } + + /* Other timing control registers */ + write_config32_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x8c, data->f2x8c); + } + } + + return; + } + /* Load data from data structure into DCTs */ /* Stage 1 */ for (node = 0; node < MAX_NODES_SUPPORTED; node++) { @@ -497,7 +631,8 @@ void restore_mct_data_from_save_variable(struct amd_s3_persistent_data* persiste wrmsr_uint64_t(0x00000250, data->msr00000250); wrmsr_uint64_t(0x00000258, data->msr00000258); /* FIXME - * Restoring these MSRs causes a hang on resume + * Restoring these MSRs causes a hang on resume due to + * destroying CAR while still executing from CAR! * For now, skip restoration... */ // for (i=0; i<8; i++) @@ -886,6 +1021,8 @@ void restore_mct_data_from_save_variable(struct amd_s3_persistent_data* persiste #ifdef __RAMSTAGE__ int8_t save_mct_information_to_nvram(void) { + uint8_t nvram; + if (acpi_is_wakeup_s3()) return 0; @@ -905,6 +1042,9 @@ int8_t save_mct_information_to_nvram(void) /* Obtain MCT configuration data */ copy_mct_data_to_save_variable(persistent_data); + /* Save RAM SPD data at the same time */ + copy_cbmem_spd_data_to_save_variable(persistent_data); + /* Obtain CBFS file offset */ s3nv_offset = get_s3nv_file_offset(); if (s3nv_offset == -1) @@ -945,36 +1085,23 @@ int8_t save_mct_information_to_nvram(void) /* Restore SPI MMIO address */ pci_write_config32(lpc_dev, 0xa0, spi_mmio_prev); + /* Allow training bypass if DIMM configuration is unchanged on next boot */ + nvram = 1; + set_option("allow_spd_nvram_cache_restore", &nvram); + return 0; } #endif -int8_t restore_mct_information_from_nvram(void) +int8_t restore_mct_information_from_nvram(uint8_t training_only) { - ssize_t s3nv_offset; - ssize_t s3nv_file_offset; - void * s3nv_cbfs_file_ptr; struct amd_s3_persistent_data *persistent_data; - /* Obtain CBFS file offset */ - s3nv_offset = get_s3nv_file_offset(); - if (s3nv_offset == -1) + persistent_data = map_s3nv_in_nvram(); + if (!persistent_data) return -1; - /* Align flash pointer to nearest boundary */ - s3nv_file_offset = s3nv_offset; - s3nv_offset &= ~(CONFIG_S3_DATA_SIZE-1); - s3nv_offset += CONFIG_S3_DATA_SIZE; - s3nv_file_offset = s3nv_offset - s3nv_file_offset; - - /* Map data structure in CBFS and restore settings */ - s3nv_cbfs_file_ptr = cbfs_boot_map_with_leak(S3NV_FILE_NAME, CBFS_TYPE_RAW, NULL); - if (!s3nv_cbfs_file_ptr) { - printk(BIOS_DEBUG, "S3 state file could not be mapped: %s\n", S3NV_FILE_NAME); - return -1; - } - persistent_data = (s3nv_cbfs_file_ptr + s3nv_file_offset); - restore_mct_data_from_save_variable(persistent_data); + restore_mct_data_from_save_variable(persistent_data, training_only); return 0; -}
\ No newline at end of file +} |