blob: 7267f12000a0564bddc859fd4a0d9ba92b0d395d [file] [log] [blame]
Timothy Pearson59d0e042015-09-05 18:40:31 -05001/*
2 * This file is part of the coreboot project.
3 *
4 * Copyright (C) 2015 Timothy Pearson <tpearson@raptorengineeringinc.com>, Raptor Engineering
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; version 2 of the License.
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.
Timothy Pearson59d0e042015-09-05 18:40:31 -050014 */
15
16#include <string.h>
Timothy Pearson730a0432015-10-16 13:51:51 -050017#include <arch/cpu.h>
Timothy Pearson59d0e042015-09-05 18:40:31 -050018#include <arch/acpi.h>
19#include <cpu/x86/msr.h>
Elyes HAOUAS8a643702018-10-23 17:10:27 +020020#include <cpu/amd/msr.h>
21#include <cpu/x86/mtrr.h>
22#include <cpu/amd/mtrr.h>
Timothy Pearson59d0e042015-09-05 18:40:31 -050023#include <device/device.h>
24#include <device/pci_def.h>
25#include <device/pci_ops.h>
26#include <console/console.h>
27#include <cbfs.h>
Timothy Pearsondf1fb9c2015-06-04 00:11:03 -050028#include <cbmem.h>
Timothy Pearson59d0e042015-09-05 18:40:31 -050029#include <spi-generic.h>
30#include <spi_flash.h>
Timothy Pearsondf1fb9c2015-06-04 00:11:03 -050031#include <pc80/mc146818rtc.h>
Damien Zammit75a3d1f2016-11-28 00:29:10 +110032#include <inttypes.h>
Damien Zammit75a3d1f2016-11-28 00:29:10 +110033#include "mct_d.h"
34#include "mct_d_gcc.h"
Timothy Pearson59d0e042015-09-05 18:40:31 -050035
36#include "s3utils.h"
37
38#define S3NV_FILE_NAME "s3nv"
39
Damien Zammit75a3d1f2016-11-28 00:29:10 +110040static uint8_t is_fam15h(void)
Timothy Pearson730a0432015-10-16 13:51:51 -050041{
42 uint8_t fam15h = 0;
43 uint32_t family;
44
45 family = cpuid_eax(0x80000001);
46 family = ((family & 0xf00000) >> 16) | ((family & 0xf00) >> 8);
47
48 if (family >= 0x6f)
49 /* Family 15h or later */
50 fam15h = 1;
51
52 return fam15h;
53}
Timothy Pearson730a0432015-10-16 13:51:51 -050054
Timothy Pearson59d0e042015-09-05 18:40:31 -050055static ssize_t get_s3nv_file_offset(void);
56
57ssize_t get_s3nv_file_offset(void)
58{
59 struct region_device s3nv_region;
60 struct cbfsf s3nv_cbfs_file;
61 if (cbfs_boot_locate(&s3nv_cbfs_file, S3NV_FILE_NAME, NULL)) {
62 printk(BIOS_DEBUG, "S3 state file not found in CBFS: %s\n", S3NV_FILE_NAME);
63 return -1;
64 }
65 cbfs_file_data(&s3nv_region, &s3nv_cbfs_file);
66
67 return s3nv_region.region.offset;
68}
69
Elyes HAOUASafc63842018-12-05 11:02:11 +010070#ifdef __SIMPLE_DEVICE__
71static uint32_t read_config32_dct(pci_devfn_t dev, uint8_t node, uint8_t dct,
72 uint32_t reg)
73#else
74static uint32_t read_config32_dct(struct device *dev, uint8_t node, uint8_t dct,
75 uint32_t reg)
76#endif
77{
Timothy Pearson730a0432015-10-16 13:51:51 -050078 if (is_fam15h()) {
79 uint32_t dword;
80#ifdef __PRE_RAM__
Elyes HAOUAS68c851b2018-06-12 22:06:09 +020081 pci_devfn_t dev_fn1 = PCI_DEV(0, 0x18 + node, 1);
Timothy Pearson730a0432015-10-16 13:51:51 -050082#else
Kyösti Mälkkic70eed12018-05-22 02:18:00 +030083 struct device *dev_fn1 = pcidev_on_root(0x18 + node, 1);
Timothy Pearson730a0432015-10-16 13:51:51 -050084#endif
85
86 /* Select DCT */
87 dword = pci_read_config32(dev_fn1, 0x10c);
88 dword &= ~0x1;
89 dword |= (dct & 0x1);
90 pci_write_config32(dev_fn1, 0x10c, dword);
91 } else {
92 /* Apply offset */
93 reg += dct * 0x100;
94 }
95
96 return pci_read_config32(dev, reg);
97}
98
Elyes HAOUASafc63842018-12-05 11:02:11 +010099#ifdef __SIMPLE_DEVICE__
100static void write_config32_dct(pci_devfn_t dev, uint8_t node, uint8_t dct,
101 uint32_t reg, uint32_t value)
102#else
103static void write_config32_dct(struct device *dev, uint8_t node, uint8_t dct,
104 uint32_t reg, uint32_t value)
105#endif
106{
Timothy Pearson38508a02015-06-25 15:07:34 -0500107 if (is_fam15h()) {
108 uint32_t dword;
109#ifdef __PRE_RAM__
Elyes HAOUAS68c851b2018-06-12 22:06:09 +0200110 pci_devfn_t dev_fn1 = PCI_DEV(0, 0x18 + node, 1);
Timothy Pearson38508a02015-06-25 15:07:34 -0500111#else
Kyösti Mälkkic70eed12018-05-22 02:18:00 +0300112 struct device *dev_fn1 = pcidev_on_root(0x18 + node, 1);
Timothy Pearson38508a02015-06-25 15:07:34 -0500113#endif
114
115 /* Select DCT */
116 dword = pci_read_config32(dev_fn1, 0x10c);
117 dword &= ~0x1;
118 dword |= (dct & 0x1);
119 pci_write_config32(dev_fn1, 0x10c, dword);
120 } else {
121 /* Apply offset */
122 reg += dct * 0x100;
123 }
124
125 pci_write_config32(dev, reg, value);
126}
127
Elyes HAOUASafc63842018-12-05 11:02:11 +0100128#ifdef __SIMPLE_DEVICE__
129static uint32_t read_amd_dct_index_register(pci_devfn_t dev,
130 uint32_t index_ctl_reg, uint32_t index)
131#else
132static uint32_t read_amd_dct_index_register(struct device *dev,
133 uint32_t index_ctl_reg, uint32_t index)
134#endif
Timothy Pearson59d0e042015-09-05 18:40:31 -0500135{
136 uint32_t dword;
137
138 index &= ~(1 << 30);
139 pci_write_config32(dev, index_ctl_reg, index);
140 do {
141 dword = pci_read_config32(dev, index_ctl_reg);
142 } while (!(dword & (1 << 31)));
143 dword = pci_read_config32(dev, index_ctl_reg + 0x04);
144
145 return dword;
146}
147
Elyes HAOUASafc63842018-12-05 11:02:11 +0100148#ifdef __SIMPLE_DEVICE__
149static uint32_t read_amd_dct_index_register_dct(pci_devfn_t dev, uint8_t node,
150 uint8_t dct, uint32_t index_ctl_reg, uint32_t index)
151#else
152static uint32_t read_amd_dct_index_register_dct(struct device *dev,
153 uint8_t node, uint8_t dct, uint32_t index_ctl_reg,
154 uint32_t index)
155#endif
Timothy Pearson730a0432015-10-16 13:51:51 -0500156{
157 if (is_fam15h()) {
158 uint32_t dword;
159#ifdef __PRE_RAM__
Elyes HAOUAS68c851b2018-06-12 22:06:09 +0200160 pci_devfn_t dev_fn1 = PCI_DEV(0, 0x18 + node, 1);
Timothy Pearson730a0432015-10-16 13:51:51 -0500161#else
Kyösti Mälkkic70eed12018-05-22 02:18:00 +0300162 struct device *dev_fn1 = pcidev_on_root(0x18 + node, 1);
Timothy Pearson730a0432015-10-16 13:51:51 -0500163#endif
164
165 /* Select DCT */
166 dword = pci_read_config32(dev_fn1, 0x10c);
167 dword &= ~0x1;
168 dword |= (dct & 0x1);
169 pci_write_config32(dev_fn1, 0x10c, dword);
170 } else {
171 /* Apply offset */
172 index_ctl_reg += dct * 0x100;
173 }
174
175 return read_amd_dct_index_register(dev, index_ctl_reg, index);
176}
177
Timothy Pearsondf1fb9c2015-06-04 00:11:03 -0500178/* Non-cryptographic 64-bit hash function taken from Stack Overflow:
179 * http://stackoverflow.com/a/13326345
180 * Any 64-bit hash with sufficiently low collision potential
181 * could be used instead.
182 */
183void calculate_spd_hash(uint8_t *spd_data, uint64_t *spd_hash)
184{
185 const unsigned long long prime = 2654435789ULL;
186 uint16_t byte;
187 *spd_hash = 104395301;
188
189 for (byte = 0; byte < 256; byte++)
190 *spd_hash += (spd_data[byte] * prime) ^ (*spd_hash >> 23);
191
192 *spd_hash = *spd_hash ^ (*spd_hash << 37);
193}
194
Timothy Pearsonf70946f2015-06-10 10:46:17 -0500195uint16_t calculate_nvram_mct_hash(void)
196{
197 uint32_t nvram;
198 uint16_t ret;
199
200 ret = 0;
201 if (get_option(&nvram, "max_mem_clock") == CB_SUCCESS)
202 ret |= nvram & 0xf;
203 if (get_option(&nvram, "minimum_memory_voltage") == CB_SUCCESS)
204 ret |= (nvram & 0x3) << 4;
205 if (get_option(&nvram, "ECC_memory") == CB_SUCCESS)
206 ret |= (nvram & 0x1) << 6;
207 if (get_option(&nvram, "ECC_redirection") == CB_SUCCESS)
208 ret |= (nvram & 0x1) << 7;
209 if (get_option(&nvram, "ecc_scrub_rate") == CB_SUCCESS)
210 ret |= (nvram & 0x1) << 8;
211 if (get_option(&nvram, "interleave_chip_selects") == CB_SUCCESS)
212 ret |= (nvram & 0x1) << 9;
213 if (get_option(&nvram, "interleave_nodes") == CB_SUCCESS)
214 ret |= (nvram & 0x1) << 10;
215 if (get_option(&nvram, "interleave_memory_channels") == CB_SUCCESS)
216 ret |= (nvram & 0x1) << 11;
217 if (get_option(&nvram, "cpu_c_states") == CB_SUCCESS)
218 ret |= (nvram & 0x1) << 12;
219 if (get_option(&nvram, "cpu_cc6_state") == CB_SUCCESS)
220 ret |= (nvram & 0x1) << 13;
221
222 return ret;
223}
224
Elyes HAOUASfd051dc2018-07-08 12:39:34 +0200225static struct amd_s3_persistent_data *map_s3nv_in_nvram(void)
Timothy Pearsondf1fb9c2015-06-04 00:11:03 -0500226{
227 ssize_t s3nv_offset;
228 ssize_t s3nv_file_offset;
Elyes HAOUASfd051dc2018-07-08 12:39:34 +0200229 void *s3nv_cbfs_file_ptr;
Timothy Pearsondf1fb9c2015-06-04 00:11:03 -0500230 struct amd_s3_persistent_data *persistent_data;
231
232 /* Obtain CBFS file offset */
233 s3nv_offset = get_s3nv_file_offset();
234 if (s3nv_offset == -1)
235 return NULL;
236
237 /* Align flash pointer to nearest boundary */
238 s3nv_file_offset = s3nv_offset;
239 s3nv_offset &= ~(CONFIG_S3_DATA_SIZE-1);
240 s3nv_offset += CONFIG_S3_DATA_SIZE;
241 s3nv_file_offset = s3nv_offset - s3nv_file_offset;
242
243 /* Map data structure in CBFS and restore settings */
244 s3nv_cbfs_file_ptr = cbfs_boot_map_with_leak(S3NV_FILE_NAME, CBFS_TYPE_RAW, NULL);
245 if (!s3nv_cbfs_file_ptr) {
246 printk(BIOS_DEBUG, "S3 state file could not be mapped: %s\n", S3NV_FILE_NAME);
247 return NULL;
248 }
249 persistent_data = (s3nv_cbfs_file_ptr + s3nv_file_offset);
250
251 return persistent_data;
252}
253
254#ifdef __PRE_RAM__
Timothy Pearsonf70946f2015-06-10 10:46:17 -0500255int8_t load_spd_hashes_from_nvram(struct MCTStatStruc *pMCTstat, struct DCTStatStruc *pDCTstat)
Timothy Pearsondf1fb9c2015-06-04 00:11:03 -0500256{
257 struct amd_s3_persistent_data *persistent_data;
258
259 persistent_data = map_s3nv_in_nvram();
260 if (!persistent_data)
261 return -1;
262
263 memcpy(pDCTstat->spd_data.nvram_spd_hash, persistent_data->node[pDCTstat->Node_ID].spd_hash, sizeof(pDCTstat->spd_data.nvram_spd_hash));
264 memcpy(pDCTstat->spd_data.nvram_memclk, persistent_data->node[pDCTstat->Node_ID].memclk, sizeof(pDCTstat->spd_data.nvram_memclk));
265
Timothy Pearsonf70946f2015-06-10 10:46:17 -0500266 pMCTstat->nvram_checksum = persistent_data->nvram_checksum;
267
Timothy Pearsondf1fb9c2015-06-04 00:11:03 -0500268 return 0;
269}
270#endif
271
Timothy Pearson59d0e042015-09-05 18:40:31 -0500272#ifdef __RAMSTAGE__
273static uint64_t rdmsr_uint64_t(unsigned long index) {
274 msr_t msr = rdmsr(index);
275 return (((uint64_t)msr.hi) << 32) | ((uint64_t)msr.lo);
276}
277
Elyes HAOUAS68c851b2018-06-12 22:06:09 +0200278static uint32_t read_config32_dct_nbpstate(struct device *dev, uint8_t node,
279 uint8_t dct, uint8_t nb_pstate,
280 uint32_t reg)
281{
Timothy Pearson730a0432015-10-16 13:51:51 -0500282 uint32_t dword;
Kyösti Mälkkic70eed12018-05-22 02:18:00 +0300283 struct device *dev_fn1 = pcidev_on_root(0x18 + node, 1);
Timothy Pearson730a0432015-10-16 13:51:51 -0500284
285 /* Select DCT */
286 dword = pci_read_config32(dev_fn1, 0x10c);
287 dword &= ~0x1;
288 dword |= (dct & 0x1);
289 pci_write_config32(dev_fn1, 0x10c, dword);
290
291 /* Select NB Pstate index */
292 dword = pci_read_config32(dev_fn1, 0x10c);
293 dword &= ~(0x3 << 4);
294 dword |= (nb_pstate & 0x3) << 4;
295 pci_write_config32(dev_fn1, 0x10c, dword);
296
297 return pci_read_config32(dev, reg);
298}
299
Elyes HAOUASfd051dc2018-07-08 12:39:34 +0200300static void copy_cbmem_spd_data_to_save_variable(struct amd_s3_persistent_data *persistent_data, uint8_t *restored)
Timothy Pearsondf1fb9c2015-06-04 00:11:03 -0500301{
302 uint8_t node;
303 uint8_t dimm;
304 uint8_t channel;
305 struct amdmct_memory_info *mem_info;
306 mem_info = cbmem_find(CBMEM_ID_AMDMCT_MEMINFO);
307 if (mem_info == NULL) {
308 /* can't find amdmct information in cbmem */
309 for (node = 0; node < MAX_NODES_SUPPORTED; node++)
310 for (dimm = 0; dimm < MAX_DIMMS_SUPPORTED; dimm++)
311 persistent_data->node[node].spd_hash[dimm] = 0xffffffffffffffffULL;
312
313 return;
314 }
315
316 for (node = 0; node < MAX_NODES_SUPPORTED; node++)
317 for (dimm = 0; dimm < MAX_DIMMS_SUPPORTED; dimm++)
318 calculate_spd_hash(mem_info->dct_stat[node].spd_data.spd_bytes[dimm], &persistent_data->node[node].spd_hash[dimm]);
319
320 for (node = 0; node < MAX_NODES_SUPPORTED; node++)
321 for (channel = 0; channel < 2; channel++)
322 persistent_data->node[node].memclk[channel] = mem_info->dct_stat[node].Speed;
Timothy Pearson45de61d2015-06-08 19:54:56 -0500323
Timothy Pearsonf70946f2015-06-10 10:46:17 -0500324 persistent_data->nvram_checksum = calculate_nvram_mct_hash();
325
Timothy Pearson45de61d2015-06-08 19:54:56 -0500326 if (restored) {
327 if (mem_info->mct_stat.GStatus & (1 << GSB_ConfigRestored))
328 *restored = 1;
329 else
330 *restored = 0;
331 }
Timothy Pearsondf1fb9c2015-06-04 00:11:03 -0500332}
333
Elyes HAOUASfd051dc2018-07-08 12:39:34 +0200334void copy_mct_data_to_save_variable(struct amd_s3_persistent_data *persistent_data)
Timothy Pearson59d0e042015-09-05 18:40:31 -0500335{
336 uint8_t i;
337 uint8_t j;
338 uint8_t node;
339 uint8_t channel;
340
341 /* Zero out data structure */
342 memset(persistent_data, 0, sizeof(struct amd_s3_persistent_data));
343
344 /* Load data from DCTs into data structure */
345 for (node = 0; node < MAX_NODES_SUPPORTED; node++) {
Kyösti Mälkkic70eed12018-05-22 02:18:00 +0300346 struct device *dev_fn1 = pcidev_on_root(0x18 + node, 1);
347 struct device *dev_fn2 = pcidev_on_root(0x18 + node, 2);
348 struct device *dev_fn3 = pcidev_on_root(0x18 + node, 3);
Timothy Pearson730a0432015-10-16 13:51:51 -0500349 /* Test for node presence */
350 if ((!dev_fn1) || (pci_read_config32(dev_fn1, PCI_VENDOR_ID) == 0xffffffff)) {
Timothy Pearson59d0e042015-09-05 18:40:31 -0500351 persistent_data->node[node].node_present = 0;
352 continue;
353 }
354 persistent_data->node[node].node_present = 1;
355
356 for (channel = 0; channel < 2; channel++) {
Elyes HAOUASfd051dc2018-07-08 12:39:34 +0200357 struct amd_s3_persistent_mct_channel_data *data = &persistent_data->node[node].channel[channel];
Timothy Pearson59d0e042015-09-05 18:40:31 -0500358
359 /* Stage 1 */
360 data->f2x110 = pci_read_config32(dev_fn2, 0x110);
361
362 /* Stage 2 */
Timothy Pearson730a0432015-10-16 13:51:51 -0500363 data->f1x40 = read_config32_dct(dev_fn1, node, channel, 0x40);
364 data->f1x44 = read_config32_dct(dev_fn1, node, channel, 0x44);
365 data->f1x48 = read_config32_dct(dev_fn1, node, channel, 0x48);
366 data->f1x4c = read_config32_dct(dev_fn1, node, channel, 0x4c);
367 data->f1x50 = read_config32_dct(dev_fn1, node, channel, 0x50);
368 data->f1x54 = read_config32_dct(dev_fn1, node, channel, 0x54);
369 data->f1x58 = read_config32_dct(dev_fn1, node, channel, 0x58);
370 data->f1x5c = read_config32_dct(dev_fn1, node, channel, 0x5c);
371 data->f1x60 = read_config32_dct(dev_fn1, node, channel, 0x60);
372 data->f1x64 = read_config32_dct(dev_fn1, node, channel, 0x64);
373 data->f1x68 = read_config32_dct(dev_fn1, node, channel, 0x68);
374 data->f1x6c = read_config32_dct(dev_fn1, node, channel, 0x6c);
375 data->f1x70 = read_config32_dct(dev_fn1, node, channel, 0x70);
376 data->f1x74 = read_config32_dct(dev_fn1, node, channel, 0x74);
377 data->f1x78 = read_config32_dct(dev_fn1, node, channel, 0x78);
378 data->f1x7c = read_config32_dct(dev_fn1, node, channel, 0x7c);
Timothy Pearson59d0e042015-09-05 18:40:31 -0500379 data->f1xf0 = pci_read_config32(dev_fn1, 0xf0);
380 data->f1x120 = pci_read_config32(dev_fn1, 0x120);
381 data->f1x124 = pci_read_config32(dev_fn1, 0x124);
382 data->f2x10c = pci_read_config32(dev_fn2, 0x10c);
383 data->f2x114 = pci_read_config32(dev_fn2, 0x114);
384 data->f2x118 = pci_read_config32(dev_fn2, 0x118);
385 data->f2x11c = pci_read_config32(dev_fn2, 0x11c);
386 data->f2x1b0 = pci_read_config32(dev_fn2, 0x1b0);
387 data->f3x44 = pci_read_config32(dev_fn3, 0x44);
Elyes HAOUASe1606732016-09-19 10:25:41 -0600388 for (i = 0; i < 16; i++) {
Elyes HAOUAS8a643702018-10-23 17:10:27 +0200389 data->msr0000020[i] =
390 rdmsr_uint64_t(MTRR_PHYS_BASE(0) | i);
Timothy Pearson59d0e042015-09-05 18:40:31 -0500391 }
Elyes HAOUAS8a643702018-10-23 17:10:27 +0200392 data->msr00000250 = rdmsr_uint64_t(MTRR_FIX_64K_00000);
393 data->msr00000258 = rdmsr_uint64_t(MTRR_FIX_16K_80000);
Elyes HAOUASe1606732016-09-19 10:25:41 -0600394 for (i = 0; i < 8; i++)
Timothy Pearson59d0e042015-09-05 18:40:31 -0500395 data->msr0000026[i] = rdmsr_uint64_t(0x00000260 | (i + 8));
Elyes HAOUAS8a643702018-10-23 17:10:27 +0200396 data->msr000002ff = rdmsr_uint64_t(MTRR_DEF_TYPE_MSR);
397 data->msrc0010010 = rdmsr_uint64_t(SYSCFG_MSR);
398 data->msrc001001a = rdmsr_uint64_t(TOP_MEM);
399 data->msrc001001d = rdmsr_uint64_t(TOP_MEM2);
400 data->msrc001001f = rdmsr_uint64_t(NB_CFG_MSR);
Timothy Pearson59d0e042015-09-05 18:40:31 -0500401
402 /* Stage 3 */
Timothy Pearson730a0432015-10-16 13:51:51 -0500403 data->f2x40 = read_config32_dct(dev_fn2, node, channel, 0x40);
404 data->f2x44 = read_config32_dct(dev_fn2, node, channel, 0x44);
405 data->f2x48 = read_config32_dct(dev_fn2, node, channel, 0x48);
406 data->f2x4c = read_config32_dct(dev_fn2, node, channel, 0x4c);
407 data->f2x50 = read_config32_dct(dev_fn2, node, channel, 0x50);
408 data->f2x54 = read_config32_dct(dev_fn2, node, channel, 0x54);
409 data->f2x58 = read_config32_dct(dev_fn2, node, channel, 0x58);
410 data->f2x5c = read_config32_dct(dev_fn2, node, channel, 0x5c);
411 data->f2x60 = read_config32_dct(dev_fn2, node, channel, 0x60);
412 data->f2x64 = read_config32_dct(dev_fn2, node, channel, 0x64);
413 data->f2x68 = read_config32_dct(dev_fn2, node, channel, 0x68);
414 data->f2x6c = read_config32_dct(dev_fn2, node, channel, 0x6c);
415 data->f2x78 = read_config32_dct(dev_fn2, node, channel, 0x78);
416 data->f2x7c = read_config32_dct(dev_fn2, node, channel, 0x7c);
417 data->f2x80 = read_config32_dct(dev_fn2, node, channel, 0x80);
418 data->f2x84 = read_config32_dct(dev_fn2, node, channel, 0x84);
419 data->f2x88 = read_config32_dct(dev_fn2, node, channel, 0x88);
420 data->f2x8c = read_config32_dct(dev_fn2, node, channel, 0x8c);
421 data->f2x90 = read_config32_dct(dev_fn2, node, channel, 0x90);
422 data->f2xa4 = read_config32_dct(dev_fn2, node, channel, 0xa4);
423 data->f2xa8 = read_config32_dct(dev_fn2, node, channel, 0xa8);
424
425 /* Family 15h-specific configuration */
426 if (is_fam15h()) {
427 data->f2x200 = read_config32_dct(dev_fn2, node, channel, 0x200);
428 data->f2x204 = read_config32_dct(dev_fn2, node, channel, 0x204);
429 data->f2x208 = read_config32_dct(dev_fn2, node, channel, 0x208);
430 data->f2x20c = read_config32_dct(dev_fn2, node, channel, 0x20c);
Elyes HAOUASe1606732016-09-19 10:25:41 -0600431 for (i = 0; i < 4; i++)
Timothy Pearson730a0432015-10-16 13:51:51 -0500432 data->f2x210[i] = read_config32_dct_nbpstate(dev_fn2, node, channel, i, 0x210);
433 data->f2x214 = read_config32_dct(dev_fn2, node, channel, 0x214);
434 data->f2x218 = read_config32_dct(dev_fn2, node, channel, 0x218);
435 data->f2x21c = read_config32_dct(dev_fn2, node, channel, 0x21c);
436 data->f2x22c = read_config32_dct(dev_fn2, node, channel, 0x22c);
437 data->f2x230 = read_config32_dct(dev_fn2, node, channel, 0x230);
438 data->f2x234 = read_config32_dct(dev_fn2, node, channel, 0x234);
439 data->f2x238 = read_config32_dct(dev_fn2, node, channel, 0x238);
440 data->f2x23c = read_config32_dct(dev_fn2, node, channel, 0x23c);
441 data->f2x240 = read_config32_dct(dev_fn2, node, channel, 0x240);
442
443 data->f2x9cx0d0fe003 = read_amd_dct_index_register_dct(dev_fn2, node, channel, 0x98, 0x0d0fe003);
444 data->f2x9cx0d0fe013 = read_amd_dct_index_register_dct(dev_fn2, node, channel, 0x98, 0x0d0fe013);
Elyes HAOUASe1606732016-09-19 10:25:41 -0600445 for (i = 0; i < 9; i++)
Timothy Pearson730a0432015-10-16 13:51:51 -0500446 data->f2x9cx0d0f0_8_0_1f[i] = read_amd_dct_index_register_dct(dev_fn2, node, channel, 0x98, 0x0d0f001f | (i << 8));
447 data->f2x9cx0d0f201f = read_amd_dct_index_register_dct(dev_fn2, node, channel, 0x98, 0x0d0f201f);
448 data->f2x9cx0d0f211f = read_amd_dct_index_register_dct(dev_fn2, node, channel, 0x98, 0x0d0f211f);
449 data->f2x9cx0d0f221f = read_amd_dct_index_register_dct(dev_fn2, node, channel, 0x98, 0x0d0f221f);
450 data->f2x9cx0d0f801f = read_amd_dct_index_register_dct(dev_fn2, node, channel, 0x98, 0x0d0f801f);
451 data->f2x9cx0d0f811f = read_amd_dct_index_register_dct(dev_fn2, node, channel, 0x98, 0x0d0f811f);
452 data->f2x9cx0d0f821f = read_amd_dct_index_register_dct(dev_fn2, node, channel, 0x98, 0x0d0f821f);
453 data->f2x9cx0d0fc01f = read_amd_dct_index_register_dct(dev_fn2, node, channel, 0x98, 0x0d0fc01f);
454 data->f2x9cx0d0fc11f = read_amd_dct_index_register_dct(dev_fn2, node, channel, 0x98, 0x0d0fc11f);
455 data->f2x9cx0d0fc21f = read_amd_dct_index_register_dct(dev_fn2, node, channel, 0x98, 0x0d0fc21f);
456 data->f2x9cx0d0f4009 = read_amd_dct_index_register_dct(dev_fn2, node, channel, 0x98, 0x0d0f4009);
Elyes HAOUASe1606732016-09-19 10:25:41 -0600457 for (i = 0; i < 9; i++)
Timothy Pearson730a0432015-10-16 13:51:51 -0500458 data->f2x9cx0d0f0_8_0_02[i] = read_amd_dct_index_register_dct(dev_fn2, node, channel, 0x98, 0x0d0f0002 | (i << 8));
Elyes HAOUASe1606732016-09-19 10:25:41 -0600459 for (i = 0; i < 9; i++)
Timothy Pearson730a0432015-10-16 13:51:51 -0500460 data->f2x9cx0d0f0_8_0_06[i] = read_amd_dct_index_register_dct(dev_fn2, node, channel, 0x98, 0x0d0f0006 | (i << 8));
Elyes HAOUASe1606732016-09-19 10:25:41 -0600461 for (i = 0; i < 9; i++)
Timothy Pearson730a0432015-10-16 13:51:51 -0500462 data->f2x9cx0d0f0_8_0_0a[i] = read_amd_dct_index_register_dct(dev_fn2, node, channel, 0x98, 0x0d0f000a | (i << 8));
463
464 data->f2x9cx0d0f2002 = read_amd_dct_index_register_dct(dev_fn2, node, channel, 0x98, 0x0d0f2002);
465 data->f2x9cx0d0f2102 = read_amd_dct_index_register_dct(dev_fn2, node, channel, 0x98, 0x0d0f2102);
466 data->f2x9cx0d0f2202 = read_amd_dct_index_register_dct(dev_fn2, node, channel, 0x98, 0x0d0f2202);
467 data->f2x9cx0d0f8002 = read_amd_dct_index_register_dct(dev_fn2, node, channel, 0x98, 0x0d0f8002);
468 data->f2x9cx0d0f8006 = read_amd_dct_index_register_dct(dev_fn2, node, channel, 0x98, 0x0d0f8006);
469 data->f2x9cx0d0f800a = read_amd_dct_index_register_dct(dev_fn2, node, channel, 0x98, 0x0d0f800a);
470 data->f2x9cx0d0f8102 = read_amd_dct_index_register_dct(dev_fn2, node, channel, 0x98, 0x0d0f8102);
471 data->f2x9cx0d0f8106 = read_amd_dct_index_register_dct(dev_fn2, node, channel, 0x98, 0x0d0f8106);
472 data->f2x9cx0d0f810a = read_amd_dct_index_register_dct(dev_fn2, node, channel, 0x98, 0x0d0f810a);
473 data->f2x9cx0d0fc002 = read_amd_dct_index_register_dct(dev_fn2, node, channel, 0x98, 0x0d0fc002);
474 data->f2x9cx0d0fc006 = read_amd_dct_index_register_dct(dev_fn2, node, channel, 0x98, 0x0d0fc006);
475 data->f2x9cx0d0fc00a = read_amd_dct_index_register_dct(dev_fn2, node, channel, 0x98, 0x0d0fc00a);
476 data->f2x9cx0d0fc00e = read_amd_dct_index_register_dct(dev_fn2, node, channel, 0x98, 0x0d0fc00e);
477 data->f2x9cx0d0fc012 = read_amd_dct_index_register_dct(dev_fn2, node, channel, 0x98, 0x0d0fc012);
478
479 data->f2x9cx0d0f2031 = read_amd_dct_index_register_dct(dev_fn2, node, channel, 0x98, 0x0d0f2031);
480 data->f2x9cx0d0f2131 = read_amd_dct_index_register_dct(dev_fn2, node, channel, 0x98, 0x0d0f2131);
481 data->f2x9cx0d0f2231 = read_amd_dct_index_register_dct(dev_fn2, node, channel, 0x98, 0x0d0f2231);
482 data->f2x9cx0d0f8031 = read_amd_dct_index_register_dct(dev_fn2, node, channel, 0x98, 0x0d0f8031);
483 data->f2x9cx0d0f8131 = read_amd_dct_index_register_dct(dev_fn2, node, channel, 0x98, 0x0d0f8131);
484 data->f2x9cx0d0f8231 = read_amd_dct_index_register_dct(dev_fn2, node, channel, 0x98, 0x0d0f8231);
485 data->f2x9cx0d0fc031 = read_amd_dct_index_register_dct(dev_fn2, node, channel, 0x98, 0x0d0fc031);
486 data->f2x9cx0d0fc131 = read_amd_dct_index_register_dct(dev_fn2, node, channel, 0x98, 0x0d0fc131);
487 data->f2x9cx0d0fc231 = read_amd_dct_index_register_dct(dev_fn2, node, channel, 0x98, 0x0d0fc231);
Elyes HAOUASe1606732016-09-19 10:25:41 -0600488 for (i = 0; i < 9; i++)
Timothy Pearson730a0432015-10-16 13:51:51 -0500489 data->f2x9cx0d0f0_0_f_31[i] = read_amd_dct_index_register_dct(dev_fn2, node, channel, 0x98, 0x0d0f0031 | (i << 8));
490
491 data->f2x9cx0d0f8021 = read_amd_dct_index_register_dct(dev_fn2, node, channel, 0x98, 0x0d0f8021);
Timothy Pearsonb410d262015-11-24 14:12:02 -0600492
493 if (channel == 1)
494 data->f2x9cx0d0fe00a = read_amd_dct_index_register_dct(dev_fn2, node, channel, 0x98, 0x0d0fe00a);
Timothy Pearson730a0432015-10-16 13:51:51 -0500495 }
Timothy Pearson59d0e042015-09-05 18:40:31 -0500496
497 /* Stage 4 */
Timothy Pearson730a0432015-10-16 13:51:51 -0500498 data->f2x94 = read_config32_dct(dev_fn2, node, channel, 0x94);
Timothy Pearson59d0e042015-09-05 18:40:31 -0500499
500 /* Stage 6 */
Elyes HAOUASe1606732016-09-19 10:25:41 -0600501 for (i = 0; i < 9; i++)
502 for (j = 0; j < 3; j++)
Timothy Pearson730a0432015-10-16 13:51:51 -0500503 data->f2x9cx0d0f0_f_8_0_0_8_4_0[i][j] = read_amd_dct_index_register_dct(dev_fn2, node, channel, 0x98, 0x0d0f0000 | (i << 8) | (j * 4));
504 data->f2x9cx00 = read_amd_dct_index_register_dct(dev_fn2, node, channel, 0x98, 0x00);
505 data->f2x9cx0a = read_amd_dct_index_register_dct(dev_fn2, node, channel, 0x98, 0x0a);
506 data->f2x9cx0c = read_amd_dct_index_register_dct(dev_fn2, node, channel, 0x98, 0x0c);
Timothy Pearson59d0e042015-09-05 18:40:31 -0500507
508 /* Stage 7 */
Timothy Pearson730a0432015-10-16 13:51:51 -0500509 data->f2x9cx04 = read_amd_dct_index_register_dct(dev_fn2, node, channel, 0x98, 0x04);
Timothy Pearson59d0e042015-09-05 18:40:31 -0500510
511 /* Stage 9 */
Timothy Pearson730a0432015-10-16 13:51:51 -0500512 data->f2x9cx0d0fe006 = read_amd_dct_index_register_dct(dev_fn2, node, channel, 0x98, 0x0d0fe006);
513 data->f2x9cx0d0fe007 = read_amd_dct_index_register_dct(dev_fn2, node, channel, 0x98, 0x0d0fe007);
Timothy Pearson59d0e042015-09-05 18:40:31 -0500514
515 /* Stage 10 */
Elyes HAOUASe1606732016-09-19 10:25:41 -0600516 for (i = 0; i < 12; i++)
Timothy Pearson730a0432015-10-16 13:51:51 -0500517 data->f2x9cx10[i] = read_amd_dct_index_register_dct(dev_fn2, node, channel, 0x98, 0x10 + i);
Elyes HAOUASe1606732016-09-19 10:25:41 -0600518 for (i = 0; i < 12; i++)
Timothy Pearson730a0432015-10-16 13:51:51 -0500519 data->f2x9cx20[i] = read_amd_dct_index_register_dct(dev_fn2, node, channel, 0x98, 0x20 + i);
Elyes HAOUASe1606732016-09-19 10:25:41 -0600520 for (i = 0; i < 4; i++)
521 for (j = 0; j < 3; j++)
Timothy Pearson730a0432015-10-16 13:51:51 -0500522 data->f2x9cx3_0_0_3_1[i][j] = read_amd_dct_index_register_dct(dev_fn2, node, channel, 0x98, (0x01 + i) + (0x100 * j));
Elyes HAOUASe1606732016-09-19 10:25:41 -0600523 for (i = 0; i < 4; i++)
524 for (j = 0; j < 3; j++)
Timothy Pearson730a0432015-10-16 13:51:51 -0500525 data->f2x9cx3_0_0_7_5[i][j] = read_amd_dct_index_register_dct(dev_fn2, node, channel, 0x98, (0x05 + i) + (0x100 * j));
526 data->f2x9cx0d = read_amd_dct_index_register_dct(dev_fn2, node, channel, 0x98, 0x0d);
Elyes HAOUASe1606732016-09-19 10:25:41 -0600527 for (i = 0; i < 9; i++)
Timothy Pearson730a0432015-10-16 13:51:51 -0500528 data->f2x9cx0d0f0_f_0_13[i] = read_amd_dct_index_register_dct(dev_fn2, node, channel, 0x98, 0x0d0f0013 | (i << 8));
Elyes HAOUASe1606732016-09-19 10:25:41 -0600529 for (i = 0; i < 9; i++)
Timothy Pearson730a0432015-10-16 13:51:51 -0500530 data->f2x9cx0d0f0_f_0_30[i] = read_amd_dct_index_register_dct(dev_fn2, node, channel, 0x98, 0x0d0f0030 | (i << 8));
Elyes HAOUASe1606732016-09-19 10:25:41 -0600531 for (i = 0; i < 4; i++)
Timothy Pearson730a0432015-10-16 13:51:51 -0500532 data->f2x9cx0d0f2_f_0_30[i] = read_amd_dct_index_register_dct(dev_fn2, node, channel, 0x98, 0x0d0f2030 | (i << 8));
Elyes HAOUASe1606732016-09-19 10:25:41 -0600533 for (i = 0; i < 2; i++)
534 for (j = 0; j < 3; j++)
Timothy Pearson730a0432015-10-16 13:51:51 -0500535 data->f2x9cx0d0f8_8_4_0[i][j] = read_amd_dct_index_register_dct(dev_fn2, node, channel, 0x98, 0x0d0f0000 | (i << 8) | (j * 4));
536 data->f2x9cx0d0f812f = read_amd_dct_index_register_dct(dev_fn2, node, channel, 0x98, 0x0d0f812f);
Timothy Pearson59d0e042015-09-05 18:40:31 -0500537
538 /* Stage 11 */
539 if (IS_ENABLED(CONFIG_DIMM_DDR3)) {
Elyes HAOUASe1606732016-09-19 10:25:41 -0600540 for (i = 0; i < 12; i++)
Timothy Pearson730a0432015-10-16 13:51:51 -0500541 data->f2x9cx30[i] = read_amd_dct_index_register_dct(dev_fn2, node, channel, 0x98, 0x30 + i);
Elyes HAOUASe1606732016-09-19 10:25:41 -0600542 for (i = 0; i < 12; i++)
Timothy Pearson730a0432015-10-16 13:51:51 -0500543 data->f2x9cx40[i] = read_amd_dct_index_register_dct(dev_fn2, node, channel, 0x98, 0x40 + i);
Timothy Pearson59d0e042015-09-05 18:40:31 -0500544 }
545
546 /* Other */
547 /* ECC scrub rate control */
Timothy Pearson38508a02015-06-25 15:07:34 -0500548 data->f3x58 = read_config32_dct(dev_fn3, node, 0, 0x58);
549
550 /* ECC scrub location */
551 write_config32_dct(dev_fn3, node, 0, 0x58, 0x0); /* Disable sequential scrub to work around non-atomic location read */
552 data->f3x5c = read_config32_dct(dev_fn3, node, 0, 0x5c);
553 data->f3x60 = read_config32_dct(dev_fn3, node, 0, 0x60);
554 write_config32_dct(dev_fn3, node, 0, 0x58, data->f3x58); /* Re-enable sequential scrub */
Timothy Pearson59d0e042015-09-05 18:40:31 -0500555 }
556 }
557}
558#else
Elyes HAOUAS68c851b2018-06-12 22:06:09 +0200559static void write_config32_dct_nbpstate(pci_devfn_t dev, uint8_t node,
560 uint8_t dct, uint8_t nb_pstate,
561 uint32_t reg, uint32_t value)
562{
Timothy Pearson730a0432015-10-16 13:51:51 -0500563 uint32_t dword;
Elyes HAOUAS68c851b2018-06-12 22:06:09 +0200564 pci_devfn_t dev_fn1 = PCI_DEV(0, 0x18 + node, 1);
Timothy Pearson730a0432015-10-16 13:51:51 -0500565
566 /* Select DCT */
567 dword = pci_read_config32(dev_fn1, 0x10c);
568 dword &= ~0x1;
569 dword |= (dct & 0x1);
570 pci_write_config32(dev_fn1, 0x10c, dword);
571
572 /* Select NB Pstate index */
573 dword = pci_read_config32(dev_fn1, 0x10c);
574 dword &= ~(0x3 << 4);
575 dword |= (nb_pstate & 0x3) << 4;
576 pci_write_config32(dev_fn1, 0x10c, dword);
577
578 pci_write_config32(dev, reg, value);
579}
580
Elyes HAOUAS68c851b2018-06-12 22:06:09 +0200581static void write_amd_dct_index_register(pci_devfn_t dev,
582 uint32_t index_ctl_reg, uint32_t index,
583 uint32_t value)
Timothy Pearson59d0e042015-09-05 18:40:31 -0500584{
585 uint32_t dword;
586
587 pci_write_config32(dev, index_ctl_reg + 0x04, value);
588 index |= (1 << 30);
589 pci_write_config32(dev, index_ctl_reg, index);
590 do {
591 dword = pci_read_config32(dev, index_ctl_reg);
592 } while (!(dword & (1 << 31)));
593}
Timothy Pearson730a0432015-10-16 13:51:51 -0500594
Elyes HAOUAS68c851b2018-06-12 22:06:09 +0200595static void write_amd_dct_index_register_dct(pci_devfn_t dev, uint8_t node,
596 uint8_t dct,
597 uint32_t index_ctl_reg,
598 uint32_t index, uint32_t value)
Timothy Pearson730a0432015-10-16 13:51:51 -0500599{
600 if (is_fam15h()) {
601 uint32_t dword;
Elyes HAOUAS68c851b2018-06-12 22:06:09 +0200602 pci_devfn_t dev_fn1 = PCI_DEV(0, 0x18 + node, 1);
Timothy Pearson730a0432015-10-16 13:51:51 -0500603
604 /* Select DCT */
605 dword = pci_read_config32(dev_fn1, 0x10c);
606 dword &= ~0x1;
607 dword |= (dct & 0x1);
608 pci_write_config32(dev_fn1, 0x10c, dword);
609 } else {
610 /* Apply offset */
611 index_ctl_reg += dct * 0x100;
612 }
613
614 return write_amd_dct_index_register(dev, index_ctl_reg, index, value);
615}
Timothy Pearson59d0e042015-09-05 18:40:31 -0500616#endif
617
618#ifdef __PRE_RAM__
619static void wrmsr_uint64_t(unsigned long index, uint64_t value) {
620 msr_t msr;
621 msr.hi = (value & 0xffffffff00000000ULL) >> 32;
622 msr.lo = (value & 0xffffffff);
623 wrmsr(index, msr);
624}
625
Elyes HAOUASfd051dc2018-07-08 12:39:34 +0200626void restore_mct_data_from_save_variable(struct amd_s3_persistent_data *persistent_data, uint8_t training_only)
Timothy Pearson59d0e042015-09-05 18:40:31 -0500627{
628 uint8_t i;
629 uint8_t j;
630 uint8_t node;
631 uint8_t channel;
632 uint8_t ganged;
633 uint8_t dct_enabled;
634 uint32_t dword;
635
Timothy Pearsondf1fb9c2015-06-04 00:11:03 -0500636 if (training_only) {
637 /* Only restore the Receiver Enable and DQS training registers */
638 for (node = 0; node < MAX_NODES_SUPPORTED; node++) {
639 for (channel = 0; channel < 2; channel++) {
Elyes HAOUASfd051dc2018-07-08 12:39:34 +0200640 struct amd_s3_persistent_mct_channel_data *data = &persistent_data->node[node].channel[channel];
Timothy Pearsondf1fb9c2015-06-04 00:11:03 -0500641 if (!persistent_data->node[node].node_present)
642 continue;
643
644 /* Restore training parameters */
Elyes HAOUASe1606732016-09-19 10:25:41 -0600645 for (i = 0; i < 4; i++)
646 for (j = 0; j < 3; j++)
Timothy Pearsondf1fb9c2015-06-04 00:11:03 -0500647 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]);
Elyes HAOUASe1606732016-09-19 10:25:41 -0600648 for (i = 0; i < 4; i++)
649 for (j = 0; j < 3; j++)
Timothy Pearsondf1fb9c2015-06-04 00:11:03 -0500650 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]);
651
Elyes HAOUASe1606732016-09-19 10:25:41 -0600652 for (i = 0; i < 12; i++)
Timothy Pearsondf1fb9c2015-06-04 00:11:03 -0500653 write_amd_dct_index_register_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x98, 0x10 + i, data->f2x9cx10[i]);
Elyes HAOUASe1606732016-09-19 10:25:41 -0600654 for (i = 0; i < 12; i++)
Timothy Pearsondf1fb9c2015-06-04 00:11:03 -0500655 write_amd_dct_index_register_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x98, 0x20 + i, data->f2x9cx20[i]);
656
657 if (IS_ENABLED(CONFIG_DIMM_DDR3)) {
Elyes HAOUASe1606732016-09-19 10:25:41 -0600658 for (i = 0; i < 12; i++)
Timothy Pearsondf1fb9c2015-06-04 00:11:03 -0500659 write_amd_dct_index_register_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x98, 0x30 + i, data->f2x9cx30[i]);
Elyes HAOUASe1606732016-09-19 10:25:41 -0600660 for (i = 0; i < 12; i++)
Timothy Pearsondf1fb9c2015-06-04 00:11:03 -0500661 write_amd_dct_index_register_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x98, 0x40 + i, data->f2x9cx40[i]);
662 }
663
664 /* Restore MaxRdLatency */
665 if (is_fam15h()) {
Elyes HAOUASe1606732016-09-19 10:25:41 -0600666 for (i = 0; i < 4; i++)
Timothy Pearsondf1fb9c2015-06-04 00:11:03 -0500667 write_config32_dct_nbpstate(PCI_DEV(0, 0x18 + node, 2), node, channel, i, 0x210, data->f2x210[i]);
Timothy Pearson38508a02015-06-25 15:07:34 -0500668 } else {
Timothy Pearsondf1fb9c2015-06-04 00:11:03 -0500669 write_config32_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x78, data->f2x78);
670 }
671
672 /* Other timing control registers */
673 write_config32_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x8c, data->f2x8c);
674 }
675 }
676
677 return;
678 }
679
Timothy Pearson59d0e042015-09-05 18:40:31 -0500680 /* Load data from data structure into DCTs */
681 /* Stage 1 */
682 for (node = 0; node < MAX_NODES_SUPPORTED; node++) {
683 for (channel = 0; channel < 2; channel++) {
Elyes HAOUASfd051dc2018-07-08 12:39:34 +0200684 struct amd_s3_persistent_mct_channel_data *data = &persistent_data->node[node].channel[channel];
Timothy Pearson59d0e042015-09-05 18:40:31 -0500685 if (!persistent_data->node[node].node_present)
686 continue;
687
688 pci_write_config32(PCI_DEV(0, 0x18 + node, 2), 0x110, data->f2x110);
689 }
690 }
691
692 /* Stage 2 */
693 for (node = 0; node < MAX_NODES_SUPPORTED; node++) {
694 for (channel = 0; channel < 2; channel++) {
Elyes HAOUASfd051dc2018-07-08 12:39:34 +0200695 struct amd_s3_persistent_mct_channel_data *data = &persistent_data->node[node].channel[channel];
Timothy Pearson59d0e042015-09-05 18:40:31 -0500696 if (!persistent_data->node[node].node_present)
697 continue;
698
Timothy Pearson730a0432015-10-16 13:51:51 -0500699 write_config32_dct(PCI_DEV(0, 0x18 + node, 1), node, channel, 0x40, data->f1x40);
700 write_config32_dct(PCI_DEV(0, 0x18 + node, 1), node, channel, 0x44, data->f1x44);
701 write_config32_dct(PCI_DEV(0, 0x18 + node, 1), node, channel, 0x48, data->f1x48);
702 write_config32_dct(PCI_DEV(0, 0x18 + node, 1), node, channel, 0x4c, data->f1x4c);
703 write_config32_dct(PCI_DEV(0, 0x18 + node, 1), node, channel, 0x50, data->f1x50);
704 write_config32_dct(PCI_DEV(0, 0x18 + node, 1), node, channel, 0x54, data->f1x54);
705 write_config32_dct(PCI_DEV(0, 0x18 + node, 1), node, channel, 0x58, data->f1x58);
706 write_config32_dct(PCI_DEV(0, 0x18 + node, 1), node, channel, 0x5c, data->f1x5c);
707 write_config32_dct(PCI_DEV(0, 0x18 + node, 1), node, channel, 0x60, data->f1x60);
708 write_config32_dct(PCI_DEV(0, 0x18 + node, 1), node, channel, 0x64, data->f1x64);
709 write_config32_dct(PCI_DEV(0, 0x18 + node, 1), node, channel, 0x68, data->f1x68);
710 write_config32_dct(PCI_DEV(0, 0x18 + node, 1), node, channel, 0x6c, data->f1x6c);
711 write_config32_dct(PCI_DEV(0, 0x18 + node, 1), node, channel, 0x70, data->f1x70);
712 write_config32_dct(PCI_DEV(0, 0x18 + node, 1), node, channel, 0x74, data->f1x74);
713 write_config32_dct(PCI_DEV(0, 0x18 + node, 1), node, channel, 0x78, data->f1x78);
714 write_config32_dct(PCI_DEV(0, 0x18 + node, 1), node, channel, 0x7c, data->f1x7c);
715 write_config32_dct(PCI_DEV(0, 0x18 + node, 1), node, channel, 0xf0, data->f1xf0);
716 write_config32_dct(PCI_DEV(0, 0x18 + node, 1), node, channel, 0x120, data->f1x120);
717 write_config32_dct(PCI_DEV(0, 0x18 + node, 1), node, channel, 0x124, data->f1x124);
718 write_config32_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x10c, data->f2x10c);
719 write_config32_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x114, data->f2x114);
Timothy Pearson74e03a42015-06-05 21:14:23 -0500720 if (is_fam15h())
721 /* Do not set LockDramCfg or CC6SaveEn at this time */
722 write_config32_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x118, data->f2x118 & ~(0x3 << 18));
723 else
724 write_config32_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x118, data->f2x118);
Timothy Pearson730a0432015-10-16 13:51:51 -0500725 write_config32_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x11c, data->f2x11c);
726 write_config32_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x1b0, data->f2x1b0);
727 write_config32_dct(PCI_DEV(0, 0x18 + node, 3), node, channel, 0x44, data->f3x44);
Elyes HAOUASe1606732016-09-19 10:25:41 -0600728 for (i = 0; i < 16; i++) {
Elyes HAOUAS8a643702018-10-23 17:10:27 +0200729 wrmsr_uint64_t(MTRR_PHYS_BASE(0) | i,
730 data->msr0000020[i]);
Timothy Pearson59d0e042015-09-05 18:40:31 -0500731 }
Elyes HAOUAS8a643702018-10-23 17:10:27 +0200732 wrmsr_uint64_t(MTRR_FIX_64K_00000, data->msr00000250);
733 wrmsr_uint64_t(MTRR_FIX_16K_80000, data->msr00000258);
Timothy Pearson59d0e042015-09-05 18:40:31 -0500734 /* FIXME
Timothy Pearsondf1fb9c2015-06-04 00:11:03 -0500735 * Restoring these MSRs causes a hang on resume due to
736 * destroying CAR while still executing from CAR!
Timothy Pearson59d0e042015-09-05 18:40:31 -0500737 * For now, skip restoration...
738 */
Elyes HAOUASe1606732016-09-19 10:25:41 -0600739 // for (i = 0; i < 8; i++)
Elyes HAOUASb0f19882018-06-09 11:59:00 +0200740 // wrmsr_uint64_t(0x00000260 | (i + 8), data->msr0000026[i]);
Elyes HAOUAS8a643702018-10-23 17:10:27 +0200741 wrmsr_uint64_t(MTRR_DEF_TYPE_MSR, data->msr000002ff);
742 wrmsr_uint64_t(SYSCFG_MSR, data->msrc0010010);
743 wrmsr_uint64_t(TOP_MEM, data->msrc001001a);
744 wrmsr_uint64_t(TOP_MEM2, data->msrc001001d);
745 wrmsr_uint64_t(NB_CFG_MSR, data->msrc001001f);
Timothy Pearson59d0e042015-09-05 18:40:31 -0500746 }
747 }
748
749 /* Stage 3 */
750 for (node = 0; node < MAX_NODES_SUPPORTED; node++) {
751 for (channel = 0; channel < 2; channel++) {
Elyes HAOUASfd051dc2018-07-08 12:39:34 +0200752 struct amd_s3_persistent_mct_channel_data *data = &persistent_data->node[node].channel[channel];
Timothy Pearson59d0e042015-09-05 18:40:31 -0500753 if (!persistent_data->node[node].node_present)
754 continue;
755
Timothy Pearson730a0432015-10-16 13:51:51 -0500756 if (is_fam15h())
757 ganged = 0;
758 else
759 ganged = !!(data->f2x110 & 0x10);
Timothy Pearson59d0e042015-09-05 18:40:31 -0500760 if ((ganged == 1) && (channel > 0))
761 continue;
762
Timothy Pearson730a0432015-10-16 13:51:51 -0500763 write_config32_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x40, data->f2x40);
764 write_config32_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x44, data->f2x44);
765 write_config32_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x48, data->f2x48);
766 write_config32_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x4c, data->f2x4c);
767 write_config32_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x50, data->f2x50);
768 write_config32_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x54, data->f2x54);
769 write_config32_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x58, data->f2x58);
770 write_config32_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x5c, data->f2x5c);
771 write_config32_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x60, data->f2x60);
772 write_config32_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x64, data->f2x64);
773 write_config32_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x68, data->f2x68);
774 write_config32_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x6c, data->f2x6c);
775 write_config32_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x78, data->f2x78);
776 write_config32_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x7c, data->f2x7c);
777 write_config32_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x80, data->f2x80);
778 write_config32_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x84, data->f2x84);
779 write_config32_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x88, data->f2x88);
780 write_config32_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x8c, data->f2x8c);
781 write_config32_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x90, data->f2x90);
782 write_config32_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0xa4, data->f2xa4);
783 write_config32_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0xa8, data->f2xa8);
784 }
785 }
786
787 /* Family 15h-specific configuration */
788 if (is_fam15h()) {
789 for (node = 0; node < MAX_NODES_SUPPORTED; node++) {
790 for (channel = 0; channel < 2; channel++) {
Elyes HAOUASfd051dc2018-07-08 12:39:34 +0200791 struct amd_s3_persistent_mct_channel_data *data = &persistent_data->node[node].channel[channel];
Timothy Pearson730a0432015-10-16 13:51:51 -0500792 if (!persistent_data->node[node].node_present)
793 continue;
794
795 /* Initialize DCT */
796 write_amd_dct_index_register_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x98, 0x0000000b, 0x80000000);
797 dword = read_amd_dct_index_register_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x98, 0x0d0fe013);
798 dword &= ~0xffff;
799 dword |= 0x118;
800 write_amd_dct_index_register_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x98, 0x0d0fe013, dword);
801
802 /* Restore values */
803 write_config32_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x200, data->f2x200);
804 write_config32_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x204, data->f2x204);
805 write_config32_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x208, data->f2x208);
806 write_config32_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x20c, data->f2x20c);
Elyes HAOUASe1606732016-09-19 10:25:41 -0600807 for (i = 0; i < 4; i++)
Timothy Pearson730a0432015-10-16 13:51:51 -0500808 write_config32_dct_nbpstate(PCI_DEV(0, 0x18 + node, 2), node, channel, i, 0x210, data->f2x210[i]);
809 write_config32_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x214, data->f2x214);
810 write_config32_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x218, data->f2x218);
811 write_config32_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x21c, data->f2x21c);
812 write_config32_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x22c, data->f2x22c);
813 write_config32_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x230, data->f2x230);
814 write_config32_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x234, data->f2x234);
815 write_config32_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x238, data->f2x238);
816 write_config32_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x23c, data->f2x23c);
817 write_config32_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x240, data->f2x240);
818
819 write_amd_dct_index_register_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x98, 0x0d0fe013, data->f2x9cx0d0fe013);
Elyes HAOUASe1606732016-09-19 10:25:41 -0600820 for (i = 0; i < 9; i++)
Timothy Pearson730a0432015-10-16 13:51:51 -0500821 write_amd_dct_index_register_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x98, 0x0d0f001f | (i << 8), data->f2x9cx0d0f0_8_0_1f[i]);
822 write_amd_dct_index_register_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x98, 0x0d0f201f, data->f2x9cx0d0f201f);
823 write_amd_dct_index_register_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x98, 0x0d0f211f, data->f2x9cx0d0f211f);
824 write_amd_dct_index_register_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x98, 0x0d0f221f, data->f2x9cx0d0f221f);
825 write_amd_dct_index_register_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x98, 0x0d0f801f, data->f2x9cx0d0f801f);
826 write_amd_dct_index_register_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x98, 0x0d0f811f, data->f2x9cx0d0f811f);
827 write_amd_dct_index_register_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x98, 0x0d0f821f, data->f2x9cx0d0f821f);
828 write_amd_dct_index_register_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x98, 0x0d0fc01f, data->f2x9cx0d0fc01f);
829 write_amd_dct_index_register_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x98, 0x0d0fc11f, data->f2x9cx0d0fc11f);
830 write_amd_dct_index_register_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x98, 0x0d0fc21f, data->f2x9cx0d0fc21f);
831 write_amd_dct_index_register_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x98, 0x0d0f4009, data->f2x9cx0d0f4009);
832
833 write_amd_dct_index_register_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x98, 0x0d0f2031, data->f2x9cx0d0f2031);
834 write_amd_dct_index_register_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x98, 0x0d0f2131, data->f2x9cx0d0f2131);
835 write_amd_dct_index_register_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x98, 0x0d0f2231, data->f2x9cx0d0f2231);
836 write_amd_dct_index_register_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x98, 0x0d0f8031, data->f2x9cx0d0f8031);
837 write_amd_dct_index_register_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x98, 0x0d0f8131, data->f2x9cx0d0f8131);
838 write_amd_dct_index_register_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x98, 0x0d0f8231, data->f2x9cx0d0f8231);
839 write_amd_dct_index_register_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x98, 0x0d0fc031, data->f2x9cx0d0fc031);
840 write_amd_dct_index_register_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x98, 0x0d0fc131, data->f2x9cx0d0fc131);
841 write_amd_dct_index_register_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x98, 0x0d0fc231, data->f2x9cx0d0fc231);
Elyes HAOUASe1606732016-09-19 10:25:41 -0600842 for (i = 0; i < 9; i++)
Timothy Pearson730a0432015-10-16 13:51:51 -0500843 write_amd_dct_index_register_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x98, 0x0d0f0031 | (i << 8), data->f2x9cx0d0f0_0_f_31[i]);
844
845 write_amd_dct_index_register_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x98, 0x0d0f8021, data->f2x9cx0d0f8021);
Timothy Pearsonb410d262015-11-24 14:12:02 -0600846
847 if (channel == 1)
848 write_amd_dct_index_register_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x98, 0x0d0fe00a, data->f2x9cx0d0fe00a);
Timothy Pearson730a0432015-10-16 13:51:51 -0500849 }
Timothy Pearson59d0e042015-09-05 18:40:31 -0500850 }
851 }
852
853 /* Stage 4 */
854 for (node = 0; node < MAX_NODES_SUPPORTED; node++) {
855 for (channel = 0; channel < 2; channel++) {
Elyes HAOUASfd051dc2018-07-08 12:39:34 +0200856 struct amd_s3_persistent_mct_channel_data *data = &persistent_data->node[node].channel[channel];
Timothy Pearson59d0e042015-09-05 18:40:31 -0500857 if (!persistent_data->node[node].node_present)
858 continue;
859
Timothy Pearson730a0432015-10-16 13:51:51 -0500860 if (is_fam15h())
861 ganged = 0;
862 else
863 ganged = !!(data->f2x110 & 0x10);
Timothy Pearson59d0e042015-09-05 18:40:31 -0500864 if ((ganged == 1) && (channel > 0))
865 continue;
866
Timothy Pearson730a0432015-10-16 13:51:51 -0500867 if (is_fam15h()) {
868 /* Program PllLockTime = 0x190 */
869 dword = read_amd_dct_index_register_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x98, 0x0d0fe006);
870 dword &= ~0xffff;
871 dword |= 0x190;
872 write_amd_dct_index_register_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x98, 0x0d0fe006, dword);
Timothy Pearson59d0e042015-09-05 18:40:31 -0500873
Timothy Pearson730a0432015-10-16 13:51:51 -0500874 /* Program MemClkFreqVal = 0 */
875 dword = read_config32_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x94);
876 dword &= (0x1 << 7);
877 write_config32_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x94, dword);
878
Elyes HAOUAS64f6b712018-08-07 12:16:56 +0200879 /* Restore DRAM Address/Timing Control Register */
Timothy Pearson730a0432015-10-16 13:51:51 -0500880 write_amd_dct_index_register_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x98, 0x04, data->f2x9cx04);
881 } else {
882 /* Disable PHY auto-compensation engine */
883 dword = read_amd_dct_index_register_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x98, 0x08);
884 if (!(dword & (1 << 30))) {
885 dword |= (1 << 30);
886 write_amd_dct_index_register_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x98, 0x08, dword);
887
888 /* Wait for 5us */
889 mct_Wait(100);
890 }
Timothy Pearson59d0e042015-09-05 18:40:31 -0500891 }
892
893 /* Restore DRAM Configuration High Register */
Timothy Pearson730a0432015-10-16 13:51:51 -0500894 write_config32_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x94, data->f2x94);
Timothy Pearson59d0e042015-09-05 18:40:31 -0500895 }
896 }
897
Timothy Pearson59d0e042015-09-05 18:40:31 -0500898 /* Stage 5 */
899 for (node = 0; node < MAX_NODES_SUPPORTED; node++) {
900 for (channel = 0; channel < 2; channel++) {
Elyes HAOUASfd051dc2018-07-08 12:39:34 +0200901 struct amd_s3_persistent_mct_channel_data *data = &persistent_data->node[node].channel[channel];
Timothy Pearson59d0e042015-09-05 18:40:31 -0500902 if (!persistent_data->node[node].node_present)
903 continue;
904
Timothy Pearson730a0432015-10-16 13:51:51 -0500905 if (is_fam15h())
906 ganged = 0;
907 else
908 ganged = !!(data->f2x110 & 0x10);
Timothy Pearson59d0e042015-09-05 18:40:31 -0500909 if ((ganged == 1) && (channel > 0))
910 continue;
911
Timothy Pearson730a0432015-10-16 13:51:51 -0500912 dct_enabled = !(data->f2x94 & (1 << 14));
913 if (!dct_enabled)
914 continue;
915
Timothy Pearson59d0e042015-09-05 18:40:31 -0500916 /* Wait for any pending PHY frequency changes to complete */
917 do {
Timothy Pearson730a0432015-10-16 13:51:51 -0500918 dword = read_config32_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x94);
Timothy Pearson59d0e042015-09-05 18:40:31 -0500919 } while (dword & (1 << 21));
Timothy Pearson730a0432015-10-16 13:51:51 -0500920
921 if (is_fam15h()) {
922 /* Program PllLockTime = 0xf */
923 dword = read_amd_dct_index_register_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x98, 0x0d0fe006);
924 dword &= ~0xffff;
925 dword |= 0xf;
926 write_amd_dct_index_register_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x98, 0x0d0fe006, dword);
927 } else {
928 /* Enable PHY auto-compensation engine */
929 dword = read_amd_dct_index_register_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x98, 0x08);
930 dword &= ~(1 << 30);
931 write_amd_dct_index_register_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x98, 0x08, dword);
932 }
Timothy Pearson59d0e042015-09-05 18:40:31 -0500933 }
934 }
935
Timothy Pearson730a0432015-10-16 13:51:51 -0500936 /* Wait for 750us */
937 mct_Wait(15000);
938
Timothy Pearson59d0e042015-09-05 18:40:31 -0500939 /* Stage 6 */
940 for (node = 0; node < MAX_NODES_SUPPORTED; node++) {
941 for (channel = 0; channel < 2; channel++) {
Elyes HAOUASfd051dc2018-07-08 12:39:34 +0200942 struct amd_s3_persistent_mct_channel_data *data = &persistent_data->node[node].channel[channel];
Timothy Pearson59d0e042015-09-05 18:40:31 -0500943 if (!persistent_data->node[node].node_present)
944 continue;
945
Elyes HAOUASe1606732016-09-19 10:25:41 -0600946 for (i = 0; i < 9; i++)
947 for (j = 0; j < 3; j++)
Timothy Pearson730a0432015-10-16 13:51:51 -0500948 write_amd_dct_index_register_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x98, 0x0d0f0000 | (i << 8) | (j * 4), data->f2x9cx0d0f0_f_8_0_0_8_4_0[i][j]);
949 write_amd_dct_index_register_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x98, 0x00, data->f2x9cx00);
950 write_amd_dct_index_register_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x98, 0x0a, data->f2x9cx0a);
951 write_amd_dct_index_register_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x98, 0x0c, data->f2x9cx0c);
952 }
953 }
954
955 /* Family 15h-specific configuration */
956 if (is_fam15h()) {
957 for (node = 0; node < MAX_NODES_SUPPORTED; node++) {
958 for (channel = 0; channel < 2; channel++) {
Elyes HAOUASfd051dc2018-07-08 12:39:34 +0200959 struct amd_s3_persistent_mct_channel_data *data = &persistent_data->node[node].channel[channel];
Timothy Pearson730a0432015-10-16 13:51:51 -0500960 if (!persistent_data->node[node].node_present)
961 continue;
962
963 dword = read_amd_dct_index_register_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x98, 0x0d0fe003);
964 dword |= (0x3 << 13); /* DisAutoComp, DisablePredriverCal = 1 */
965 write_amd_dct_index_register_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x98, 0x0d0fe003, dword);
966
Elyes HAOUASe1606732016-09-19 10:25:41 -0600967 for (i = 0; i < 9; i++)
Timothy Pearson730a0432015-10-16 13:51:51 -0500968 write_amd_dct_index_register_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x98, 0x0d0f0006 | (i << 8), data->f2x9cx0d0f0_8_0_06[i]);
Elyes HAOUASe1606732016-09-19 10:25:41 -0600969 for (i = 0; i < 9; i++)
Timothy Pearson730a0432015-10-16 13:51:51 -0500970 write_amd_dct_index_register_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x98, 0x0d0f000a | (i << 8), data->f2x9cx0d0f0_8_0_0a[i]);
Elyes HAOUASe1606732016-09-19 10:25:41 -0600971 for (i = 0; i < 9; i++)
Timothy Pearson730a0432015-10-16 13:51:51 -0500972 write_amd_dct_index_register_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x98, 0x0d0f0002 | (i << 8), (0x8000 | data->f2x9cx0d0f0_8_0_02[i]));
973
974 write_amd_dct_index_register_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x98, 0x0d0f8006, data->f2x9cx0d0f8006);
975 write_amd_dct_index_register_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x98, 0x0d0f800a, data->f2x9cx0d0f800a);
976 write_amd_dct_index_register_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x98, 0x0d0f8106, data->f2x9cx0d0f8106);
977 write_amd_dct_index_register_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x98, 0x0d0f810a, data->f2x9cx0d0f810a);
978 write_amd_dct_index_register_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x98, 0x0d0fc006, data->f2x9cx0d0fc006);
979 write_amd_dct_index_register_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x98, 0x0d0fc00a, data->f2x9cx0d0fc00a);
980 write_amd_dct_index_register_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x98, 0x0d0fc00e, data->f2x9cx0d0fc00e);
981 write_amd_dct_index_register_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x98, 0x0d0fc012, data->f2x9cx0d0fc012);
982 write_amd_dct_index_register_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x98, 0x0d0f8002, (0x8000 | data->f2x9cx0d0f8002));
983 write_amd_dct_index_register_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x98, 0x0d0f8102, (0x8000 | data->f2x9cx0d0f8102));
984 write_amd_dct_index_register_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x98, 0x0d0fc002, (0x8000 | data->f2x9cx0d0fc002));
985 write_amd_dct_index_register_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x98, 0x0d0f2002, (0x8000 | data->f2x9cx0d0f2002));
986 write_amd_dct_index_register_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x98, 0x0d0f2102, (0x8000 | data->f2x9cx0d0f2102));
987 write_amd_dct_index_register_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x98, 0x0d0f2202, (0x8000 | data->f2x9cx0d0f2202));
988
989 write_amd_dct_index_register_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x98, 0x0d0fe003, data->f2x9cx0d0fe003);
990 }
Timothy Pearson59d0e042015-09-05 18:40:31 -0500991 }
992 }
993
994 /* Stage 7 */
995 for (node = 0; node < MAX_NODES_SUPPORTED; node++) {
996 for (channel = 0; channel < 2; channel++) {
Elyes HAOUASfd051dc2018-07-08 12:39:34 +0200997 struct amd_s3_persistent_mct_channel_data *data = &persistent_data->node[node].channel[channel];
Timothy Pearson59d0e042015-09-05 18:40:31 -0500998 if (!persistent_data->node[node].node_present)
999 continue;
1000
Timothy Pearson730a0432015-10-16 13:51:51 -05001001 if (is_fam15h())
1002 ganged = 0;
1003 else
1004 ganged = !!(data->f2x110 & 0x10);
Timothy Pearson59d0e042015-09-05 18:40:31 -05001005 if ((ganged == 1) && (channel > 0))
1006 continue;
1007
Timothy Pearson730a0432015-10-16 13:51:51 -05001008 if (!is_fam15h())
1009 write_amd_dct_index_register_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x98, 0x04, data->f2x9cx04);
Timothy Pearson59d0e042015-09-05 18:40:31 -05001010 }
1011 }
1012
1013 /* Stage 8 */
1014 for (node = 0; node < MAX_NODES_SUPPORTED; node++) {
1015 for (channel = 0; channel < 2; channel++) {
Elyes HAOUASfd051dc2018-07-08 12:39:34 +02001016 struct amd_s3_persistent_mct_channel_data *data = &persistent_data->node[node].channel[channel];
Timothy Pearson59d0e042015-09-05 18:40:31 -05001017 if (!persistent_data->node[node].node_present)
1018 continue;
1019
1020 dct_enabled = !(data->f2x94 & (1 << 14));
1021 if (!dct_enabled)
1022 continue;
1023
Timothy Pearson730a0432015-10-16 13:51:51 -05001024 if (is_fam15h())
1025 ganged = 0;
1026 else
1027 ganged = !!(data->f2x110 & 0x10);
Timothy Pearson59d0e042015-09-05 18:40:31 -05001028 if ((ganged == 1) && (channel > 0))
1029 continue;
1030
1031 printk(BIOS_SPEW, "Taking DIMMs out of self refresh node: %d channel: %d\n", node, channel);
1032
1033 /* Exit self refresh mode */
Timothy Pearson730a0432015-10-16 13:51:51 -05001034 dword = read_config32_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x90);
Timothy Pearson59d0e042015-09-05 18:40:31 -05001035 dword |= (1 << 1);
Timothy Pearson730a0432015-10-16 13:51:51 -05001036 write_config32_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x90, dword);
Timothy Pearson59d0e042015-09-05 18:40:31 -05001037 }
1038 }
1039
1040 /* Stage 9 */
1041 for (node = 0; node < MAX_NODES_SUPPORTED; node++) {
1042 for (channel = 0; channel < 2; channel++) {
Elyes HAOUASfd051dc2018-07-08 12:39:34 +02001043 struct amd_s3_persistent_mct_channel_data *data = &persistent_data->node[node].channel[channel];
Timothy Pearson59d0e042015-09-05 18:40:31 -05001044 if (!persistent_data->node[node].node_present)
1045 continue;
1046
1047 dct_enabled = !(data->f2x94 & (1 << 14));
1048 if (!dct_enabled)
1049 continue;
1050
1051 printk(BIOS_SPEW, "Waiting for DIMMs to exit self refresh node: %d channel: %d\n", node, channel);
1052
1053 /* Wait for transition from self refresh mode to complete */
1054 do {
Timothy Pearson730a0432015-10-16 13:51:51 -05001055 dword = read_config32_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x90);
Timothy Pearson59d0e042015-09-05 18:40:31 -05001056 } while (dword & (1 << 1));
1057
1058 /* Restore registers */
Timothy Pearson730a0432015-10-16 13:51:51 -05001059 write_amd_dct_index_register_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x98, 0x0d0fe006, data->f2x9cx0d0fe006);
1060 write_amd_dct_index_register_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x98, 0x0d0fe007, data->f2x9cx0d0fe007);
Timothy Pearson59d0e042015-09-05 18:40:31 -05001061 }
1062 }
1063
1064 /* Stage 10 */
1065 for (node = 0; node < MAX_NODES_SUPPORTED; node++) {
1066 for (channel = 0; channel < 2; channel++) {
Elyes HAOUASfd051dc2018-07-08 12:39:34 +02001067 struct amd_s3_persistent_mct_channel_data *data = &persistent_data->node[node].channel[channel];
Timothy Pearson59d0e042015-09-05 18:40:31 -05001068 if (!persistent_data->node[node].node_present)
1069 continue;
1070
Elyes HAOUASe1606732016-09-19 10:25:41 -06001071 for (i = 0; i < 12; i++)
Timothy Pearson730a0432015-10-16 13:51:51 -05001072 write_amd_dct_index_register_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x98, 0x10 + i, data->f2x9cx10[i]);
Elyes HAOUASe1606732016-09-19 10:25:41 -06001073 for (i = 0; i < 12; i++)
Timothy Pearson730a0432015-10-16 13:51:51 -05001074 write_amd_dct_index_register_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x98, 0x20 + i, data->f2x9cx20[i]);
Elyes HAOUASe1606732016-09-19 10:25:41 -06001075 for (i = 0; i < 4; i++)
1076 for (j = 0; j < 3; j++)
Timothy Pearson730a0432015-10-16 13:51:51 -05001077 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]);
Elyes HAOUASe1606732016-09-19 10:25:41 -06001078 for (i = 0; i < 4; i++)
1079 for (j = 0; j < 3; j++)
Timothy Pearson730a0432015-10-16 13:51:51 -05001080 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]);
1081 write_amd_dct_index_register_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x98, 0x0d, data->f2x9cx0d);
Elyes HAOUASe1606732016-09-19 10:25:41 -06001082 for (i = 0; i < 9; i++)
Timothy Pearson730a0432015-10-16 13:51:51 -05001083 write_amd_dct_index_register_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x98, 0x0d0f0013 | (i << 8), data->f2x9cx0d0f0_f_0_13[i]);
Elyes HAOUASe1606732016-09-19 10:25:41 -06001084 for (i = 0; i < 9; i++)
Timothy Pearson730a0432015-10-16 13:51:51 -05001085 write_amd_dct_index_register_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x98, 0x0d0f0030 | (i << 8), data->f2x9cx0d0f0_f_0_30[i]);
Elyes HAOUASe1606732016-09-19 10:25:41 -06001086 for (i = 0; i < 4; i++)
Timothy Pearson730a0432015-10-16 13:51:51 -05001087 write_amd_dct_index_register_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x98, 0x0d0f2030 | (i << 8), data->f2x9cx0d0f2_f_0_30[i]);
Elyes HAOUASe1606732016-09-19 10:25:41 -06001088 for (i = 0; i < 2; i++)
1089 for (j = 0; j < 3; j++)
Timothy Pearson730a0432015-10-16 13:51:51 -05001090 write_amd_dct_index_register_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x98, 0x0d0f0000 | (i << 8) | (j * 4), data->f2x9cx0d0f8_8_4_0[i][j]);
1091 write_amd_dct_index_register_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x98, 0x0d0f812f, data->f2x9cx0d0f812f);
Timothy Pearson59d0e042015-09-05 18:40:31 -05001092 }
1093 }
1094
1095 /* Stage 11 */
1096 if (IS_ENABLED(CONFIG_DIMM_DDR3)) {
1097 for (node = 0; node < MAX_NODES_SUPPORTED; node++) {
1098 for (channel = 0; channel < 2; channel++) {
Elyes HAOUASfd051dc2018-07-08 12:39:34 +02001099 struct amd_s3_persistent_mct_channel_data *data = &persistent_data->node[node].channel[channel];
Timothy Pearson59d0e042015-09-05 18:40:31 -05001100 if (!persistent_data->node[node].node_present)
1101 continue;
1102
Elyes HAOUASe1606732016-09-19 10:25:41 -06001103 for (i = 0; i < 12; i++)
Timothy Pearson730a0432015-10-16 13:51:51 -05001104 write_amd_dct_index_register_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x98, 0x30 + i, data->f2x9cx30[i]);
Elyes HAOUASe1606732016-09-19 10:25:41 -06001105 for (i = 0; i < 12; i++)
Timothy Pearson730a0432015-10-16 13:51:51 -05001106 write_amd_dct_index_register_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x98, 0x40 + i, data->f2x9cx40[i]);
Timothy Pearson59d0e042015-09-05 18:40:31 -05001107 }
1108 }
1109 }
1110
1111 /* Other */
1112 for (node = 0; node < MAX_NODES_SUPPORTED; node++) {
1113 for (channel = 0; channel < 2; channel++) {
Elyes HAOUASfd051dc2018-07-08 12:39:34 +02001114 struct amd_s3_persistent_mct_channel_data *data = &persistent_data->node[node].channel[channel];
Timothy Pearson59d0e042015-09-05 18:40:31 -05001115 if (!persistent_data->node[node].node_present)
1116 continue;
1117
Timothy Pearson38508a02015-06-25 15:07:34 -05001118 /* ECC scrub location */
1119 write_config32_dct(PCI_DEV(0, 0x18 + node, 3), node, 0, 0x5c, data->f3x5c);
1120 write_config32_dct(PCI_DEV(0, 0x18 + node, 3), node, 0, 0x60, data->f3x60);
1121
Timothy Pearson59d0e042015-09-05 18:40:31 -05001122 /* ECC scrub rate control */
Timothy Pearson38508a02015-06-25 15:07:34 -05001123 write_config32_dct(PCI_DEV(0, 0x18 + node, 3), node, 0, 0x58, data->f3x58);
Timothy Pearson74e03a42015-06-05 21:14:23 -05001124
1125 if (is_fam15h())
1126 /* Set LockDramCfg and CC6SaveEn */
1127 write_config32_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x118, data->f2x118);
Timothy Pearson59d0e042015-09-05 18:40:31 -05001128 }
1129 }
1130}
1131#endif
1132
1133#ifdef __RAMSTAGE__
1134int8_t save_mct_information_to_nvram(void)
1135{
Timothy Pearsondf1fb9c2015-06-04 00:11:03 -05001136 uint8_t nvram;
Timothy Pearson45de61d2015-06-08 19:54:56 -05001137 uint8_t restored = 0;
Timothy Pearsondf1fb9c2015-06-04 00:11:03 -05001138
Timothy Pearson59d0e042015-09-05 18:40:31 -05001139 if (acpi_is_wakeup_s3())
1140 return 0;
1141
1142 printk(BIOS_DEBUG, "Writing AMD DCT configuration to Flash\n");
1143
Furquan Shaikh30221b42017-05-15 14:35:15 -07001144 struct spi_flash flash;
Timothy Pearson59d0e042015-09-05 18:40:31 -05001145 ssize_t s3nv_offset;
Timothy Pearson69b11f92015-05-31 18:46:40 -05001146 struct amd_s3_persistent_data *persistent_data;
1147
1148 /* Allocate temporary data structures */
1149 persistent_data = malloc(sizeof(struct amd_s3_persistent_data));
1150 if (!persistent_data) {
1151 printk(BIOS_DEBUG, "Could not allocate S3 data structure in RAM\n");
1152 return -1;
1153 }
Timothy Pearson59d0e042015-09-05 18:40:31 -05001154
1155 /* Obtain MCT configuration data */
Timothy Pearson69b11f92015-05-31 18:46:40 -05001156 copy_mct_data_to_save_variable(persistent_data);
Timothy Pearson59d0e042015-09-05 18:40:31 -05001157
Timothy Pearsondf1fb9c2015-06-04 00:11:03 -05001158 /* Save RAM SPD data at the same time */
Timothy Pearson45de61d2015-06-08 19:54:56 -05001159 copy_cbmem_spd_data_to_save_variable(persistent_data, &restored);
1160
1161 if (restored) {
1162 /* Allow training bypass if DIMM configuration is unchanged on next boot */
1163 nvram = 1;
1164 set_option("allow_spd_nvram_cache_restore", &nvram);
1165
1166 printk(BIOS_DEBUG, "Hardware configuration unchanged since last boot; skipping write\n");
Timothy Pearsona20d0e02017-01-09 14:54:24 -06001167 free(persistent_data);
Timothy Pearson45de61d2015-06-08 19:54:56 -05001168 return 0;
1169 }
Timothy Pearsondf1fb9c2015-06-04 00:11:03 -05001170
Timothy Pearson59d0e042015-09-05 18:40:31 -05001171 /* Obtain CBFS file offset */
1172 s3nv_offset = get_s3nv_file_offset();
Timothy Pearsona20d0e02017-01-09 14:54:24 -06001173 if (s3nv_offset == -1) {
1174 free(persistent_data);
Timothy Pearson59d0e042015-09-05 18:40:31 -05001175 return -1;
Timothy Pearsona20d0e02017-01-09 14:54:24 -06001176 }
Timothy Pearson59d0e042015-09-05 18:40:31 -05001177
1178 /* Align flash pointer to nearest boundary */
1179 s3nv_offset &= ~(CONFIG_S3_DATA_SIZE-1);
1180 s3nv_offset += CONFIG_S3_DATA_SIZE;
1181
Timothy Pearson59d0e042015-09-05 18:40:31 -05001182 /* Initialize SPI and detect devices */
1183 spi_init();
Furquan Shaikh30221b42017-05-15 14:35:15 -07001184 if (spi_flash_probe(0, 0, &flash)) {
Timothy Pearson59d0e042015-09-05 18:40:31 -05001185 printk(BIOS_DEBUG, "Could not find SPI device\n");
1186 return -1;
1187 }
1188
Furquan Shaikh30221b42017-05-15 14:35:15 -07001189 spi_flash_volatile_group_begin(&flash);
Timothy Pearson59d0e042015-09-05 18:40:31 -05001190
1191 /* Erase and write data structure */
Furquan Shaikh30221b42017-05-15 14:35:15 -07001192 spi_flash_erase(&flash, s3nv_offset, CONFIG_S3_DATA_SIZE);
1193 spi_flash_write(&flash, s3nv_offset,
Furquan Shaikhc28984d2016-11-20 21:04:00 -08001194 sizeof(struct amd_s3_persistent_data), persistent_data);
Timothy Pearson69b11f92015-05-31 18:46:40 -05001195
1196 /* Deallocate temporary data structures */
1197 free(persistent_data);
Timothy Pearson59d0e042015-09-05 18:40:31 -05001198
Furquan Shaikh30221b42017-05-15 14:35:15 -07001199 spi_flash_volatile_group_end(&flash);
Timothy Pearson59d0e042015-09-05 18:40:31 -05001200
Timothy Pearsondf1fb9c2015-06-04 00:11:03 -05001201 /* Allow training bypass if DIMM configuration is unchanged on next boot */
1202 nvram = 1;
1203 set_option("allow_spd_nvram_cache_restore", &nvram);
1204
Timothy Pearson59d0e042015-09-05 18:40:31 -05001205 return 0;
1206}
1207#endif
1208
Timothy Pearsondf1fb9c2015-06-04 00:11:03 -05001209int8_t restore_mct_information_from_nvram(uint8_t training_only)
Timothy Pearson59d0e042015-09-05 18:40:31 -05001210{
Timothy Pearson59d0e042015-09-05 18:40:31 -05001211 struct amd_s3_persistent_data *persistent_data;
1212
Timothy Pearsondf1fb9c2015-06-04 00:11:03 -05001213 persistent_data = map_s3nv_in_nvram();
1214 if (!persistent_data)
Timothy Pearson59d0e042015-09-05 18:40:31 -05001215 return -1;
1216
Timothy Pearsondf1fb9c2015-06-04 00:11:03 -05001217 restore_mct_data_from_save_variable(persistent_data, training_only);
Timothy Pearson59d0e042015-09-05 18:40:31 -05001218
1219 return 0;
Timothy Pearsondf1fb9c2015-06-04 00:11:03 -05001220}
Damien Zammit75a3d1f2016-11-28 00:29:10 +11001221
1222void calculate_and_store_spd_hashes(struct MCTStatStruc *pMCTstat,
1223 struct DCTStatStruc *pDCTstat)
1224{
1225 uint8_t dimm;
1226
1227 for (dimm = 0; dimm < MAX_DIMMS_SUPPORTED; dimm++) {
1228 calculate_spd_hash(pDCTstat->spd_data.spd_bytes[dimm], &pDCTstat->spd_data.spd_hash[dimm]);
1229 }
1230}
1231
1232void compare_nvram_spd_hashes(struct MCTStatStruc *pMCTstat,
1233 struct DCTStatStruc *pDCTstat)
1234{
1235 uint8_t dimm;
1236
1237 pDCTstat->spd_data.nvram_spd_match = 1;
1238 for (dimm = 0; dimm < MAX_DIMMS_SUPPORTED; dimm++) {
1239 if (pDCTstat->spd_data.spd_hash[dimm] != pDCTstat->spd_data.nvram_spd_hash[dimm])
1240 pDCTstat->spd_data.nvram_spd_match = 0;
1241 }
1242}