blob: 3c2043be5402feb746460f8db31c733f7991eca2 [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>
20#include <device/device.h>
21#include <device/pci_def.h>
22#include <device/pci_ops.h>
23#include <console/console.h>
24#include <cbfs.h>
25#include <spi-generic.h>
26#include <spi_flash.h>
27
28#include "s3utils.h"
29
30#define S3NV_FILE_NAME "s3nv"
31
Timothy Pearson730a0432015-10-16 13:51:51 -050032#ifdef __RAMSTAGE__
33static inline uint8_t is_fam15h(void)
34{
35 uint8_t fam15h = 0;
36 uint32_t family;
37
38 family = cpuid_eax(0x80000001);
39 family = ((family & 0xf00000) >> 16) | ((family & 0xf00) >> 8);
40
41 if (family >= 0x6f)
42 /* Family 15h or later */
43 fam15h = 1;
44
45 return fam15h;
46}
47#endif
48
Timothy Pearson59d0e042015-09-05 18:40:31 -050049static ssize_t get_s3nv_file_offset(void);
50
51ssize_t get_s3nv_file_offset(void)
52{
53 struct region_device s3nv_region;
54 struct cbfsf s3nv_cbfs_file;
55 if (cbfs_boot_locate(&s3nv_cbfs_file, S3NV_FILE_NAME, NULL)) {
56 printk(BIOS_DEBUG, "S3 state file not found in CBFS: %s\n", S3NV_FILE_NAME);
57 return -1;
58 }
59 cbfs_file_data(&s3nv_region, &s3nv_cbfs_file);
60
61 return s3nv_region.region.offset;
62}
63
Timothy Pearson730a0432015-10-16 13:51:51 -050064static uint32_t read_config32_dct(device_t dev, uint8_t node, uint8_t dct, uint32_t reg) {
65 if (is_fam15h()) {
66 uint32_t dword;
67#ifdef __PRE_RAM__
68 device_t dev_fn1 = PCI_DEV(0, 0x18 + node, 1);
69#else
70 device_t dev_fn1 = dev_find_slot(0, PCI_DEVFN(0x18 + node, 1));
71#endif
72
73 /* Select DCT */
74 dword = pci_read_config32(dev_fn1, 0x10c);
75 dword &= ~0x1;
76 dword |= (dct & 0x1);
77 pci_write_config32(dev_fn1, 0x10c, dword);
78 } else {
79 /* Apply offset */
80 reg += dct * 0x100;
81 }
82
83 return pci_read_config32(dev, reg);
84}
85
Timothy Pearson59d0e042015-09-05 18:40:31 -050086static uint32_t read_amd_dct_index_register(device_t dev, uint32_t index_ctl_reg, uint32_t index)
87{
88 uint32_t dword;
89
90 index &= ~(1 << 30);
91 pci_write_config32(dev, index_ctl_reg, index);
92 do {
93 dword = pci_read_config32(dev, index_ctl_reg);
94 } while (!(dword & (1 << 31)));
95 dword = pci_read_config32(dev, index_ctl_reg + 0x04);
96
97 return dword;
98}
99
Timothy Pearson730a0432015-10-16 13:51:51 -0500100static uint32_t read_amd_dct_index_register_dct(device_t dev, uint8_t node, uint8_t dct, uint32_t index_ctl_reg, uint32_t index)
101{
102 if (is_fam15h()) {
103 uint32_t dword;
104#ifdef __PRE_RAM__
105 device_t dev_fn1 = PCI_DEV(0, 0x18 + node, 1);
106#else
107 device_t dev_fn1 = dev_find_slot(0, PCI_DEVFN(0x18 + node, 1));
108#endif
109
110 /* Select DCT */
111 dword = pci_read_config32(dev_fn1, 0x10c);
112 dword &= ~0x1;
113 dword |= (dct & 0x1);
114 pci_write_config32(dev_fn1, 0x10c, dword);
115 } else {
116 /* Apply offset */
117 index_ctl_reg += dct * 0x100;
118 }
119
120 return read_amd_dct_index_register(dev, index_ctl_reg, index);
121}
122
Timothy Pearson59d0e042015-09-05 18:40:31 -0500123#ifdef __RAMSTAGE__
124static uint64_t rdmsr_uint64_t(unsigned long index) {
125 msr_t msr = rdmsr(index);
126 return (((uint64_t)msr.hi) << 32) | ((uint64_t)msr.lo);
127}
128
Timothy Pearson730a0432015-10-16 13:51:51 -0500129static uint32_t read_config32_dct_nbpstate(device_t dev, uint8_t node, uint8_t dct, uint8_t nb_pstate, uint32_t reg) {
130 uint32_t dword;
131 device_t dev_fn1 = dev_find_slot(0, PCI_DEVFN(0x18 + node, 1));
132
133 /* Select DCT */
134 dword = pci_read_config32(dev_fn1, 0x10c);
135 dword &= ~0x1;
136 dword |= (dct & 0x1);
137 pci_write_config32(dev_fn1, 0x10c, dword);
138
139 /* Select NB Pstate index */
140 dword = pci_read_config32(dev_fn1, 0x10c);
141 dword &= ~(0x3 << 4);
142 dword |= (nb_pstate & 0x3) << 4;
143 pci_write_config32(dev_fn1, 0x10c, dword);
144
145 return pci_read_config32(dev, reg);
146}
147
Timothy Pearson59d0e042015-09-05 18:40:31 -0500148void copy_mct_data_to_save_variable(struct amd_s3_persistent_data* persistent_data)
149{
150 uint8_t i;
151 uint8_t j;
152 uint8_t node;
153 uint8_t channel;
154
155 /* Zero out data structure */
156 memset(persistent_data, 0, sizeof(struct amd_s3_persistent_data));
157
158 /* Load data from DCTs into data structure */
159 for (node = 0; node < MAX_NODES_SUPPORTED; node++) {
160 device_t dev_fn1 = dev_find_slot(0, PCI_DEVFN(0x18 + node, 1));
161 device_t dev_fn2 = dev_find_slot(0, PCI_DEVFN(0x18 + node, 2));
162 device_t dev_fn3 = dev_find_slot(0, PCI_DEVFN(0x18 + node, 3));
Timothy Pearson730a0432015-10-16 13:51:51 -0500163 /* Test for node presence */
164 if ((!dev_fn1) || (pci_read_config32(dev_fn1, PCI_VENDOR_ID) == 0xffffffff)) {
Timothy Pearson59d0e042015-09-05 18:40:31 -0500165 persistent_data->node[node].node_present = 0;
166 continue;
167 }
168 persistent_data->node[node].node_present = 1;
169
170 for (channel = 0; channel < 2; channel++) {
171 struct amd_s3_persistent_mct_channel_data* data = &persistent_data->node[node].channel[channel];
172
173 /* Stage 1 */
174 data->f2x110 = pci_read_config32(dev_fn2, 0x110);
175
176 /* Stage 2 */
Timothy Pearson730a0432015-10-16 13:51:51 -0500177 data->f1x40 = read_config32_dct(dev_fn1, node, channel, 0x40);
178 data->f1x44 = read_config32_dct(dev_fn1, node, channel, 0x44);
179 data->f1x48 = read_config32_dct(dev_fn1, node, channel, 0x48);
180 data->f1x4c = read_config32_dct(dev_fn1, node, channel, 0x4c);
181 data->f1x50 = read_config32_dct(dev_fn1, node, channel, 0x50);
182 data->f1x54 = read_config32_dct(dev_fn1, node, channel, 0x54);
183 data->f1x58 = read_config32_dct(dev_fn1, node, channel, 0x58);
184 data->f1x5c = read_config32_dct(dev_fn1, node, channel, 0x5c);
185 data->f1x60 = read_config32_dct(dev_fn1, node, channel, 0x60);
186 data->f1x64 = read_config32_dct(dev_fn1, node, channel, 0x64);
187 data->f1x68 = read_config32_dct(dev_fn1, node, channel, 0x68);
188 data->f1x6c = read_config32_dct(dev_fn1, node, channel, 0x6c);
189 data->f1x70 = read_config32_dct(dev_fn1, node, channel, 0x70);
190 data->f1x74 = read_config32_dct(dev_fn1, node, channel, 0x74);
191 data->f1x78 = read_config32_dct(dev_fn1, node, channel, 0x78);
192 data->f1x7c = read_config32_dct(dev_fn1, node, channel, 0x7c);
Timothy Pearson59d0e042015-09-05 18:40:31 -0500193 data->f1xf0 = pci_read_config32(dev_fn1, 0xf0);
194 data->f1x120 = pci_read_config32(dev_fn1, 0x120);
195 data->f1x124 = pci_read_config32(dev_fn1, 0x124);
196 data->f2x10c = pci_read_config32(dev_fn2, 0x10c);
197 data->f2x114 = pci_read_config32(dev_fn2, 0x114);
198 data->f2x118 = pci_read_config32(dev_fn2, 0x118);
199 data->f2x11c = pci_read_config32(dev_fn2, 0x11c);
200 data->f2x1b0 = pci_read_config32(dev_fn2, 0x1b0);
201 data->f3x44 = pci_read_config32(dev_fn3, 0x44);
202 for (i=0; i<16; i++) {
203 data->msr0000020[i] = rdmsr_uint64_t(0x00000200 | i);
204 }
205 data->msr00000250 = rdmsr_uint64_t(0x00000250);
206 data->msr00000258 = rdmsr_uint64_t(0x00000258);
207 for (i=0; i<8; i++)
208 data->msr0000026[i] = rdmsr_uint64_t(0x00000260 | (i + 8));
209 data->msr000002ff = rdmsr_uint64_t(0x000002ff);
210 data->msrc0010010 = rdmsr_uint64_t(0xc0010010);
211 data->msrc001001a = rdmsr_uint64_t(0xc001001a);
212 data->msrc001001d = rdmsr_uint64_t(0xc001001d);
213 data->msrc001001f = rdmsr_uint64_t(0xc001001f);
214
215 /* Stage 3 */
Timothy Pearson730a0432015-10-16 13:51:51 -0500216 data->f2x40 = read_config32_dct(dev_fn2, node, channel, 0x40);
217 data->f2x44 = read_config32_dct(dev_fn2, node, channel, 0x44);
218 data->f2x48 = read_config32_dct(dev_fn2, node, channel, 0x48);
219 data->f2x4c = read_config32_dct(dev_fn2, node, channel, 0x4c);
220 data->f2x50 = read_config32_dct(dev_fn2, node, channel, 0x50);
221 data->f2x54 = read_config32_dct(dev_fn2, node, channel, 0x54);
222 data->f2x58 = read_config32_dct(dev_fn2, node, channel, 0x58);
223 data->f2x5c = read_config32_dct(dev_fn2, node, channel, 0x5c);
224 data->f2x60 = read_config32_dct(dev_fn2, node, channel, 0x60);
225 data->f2x64 = read_config32_dct(dev_fn2, node, channel, 0x64);
226 data->f2x68 = read_config32_dct(dev_fn2, node, channel, 0x68);
227 data->f2x6c = read_config32_dct(dev_fn2, node, channel, 0x6c);
228 data->f2x78 = read_config32_dct(dev_fn2, node, channel, 0x78);
229 data->f2x7c = read_config32_dct(dev_fn2, node, channel, 0x7c);
230 data->f2x80 = read_config32_dct(dev_fn2, node, channel, 0x80);
231 data->f2x84 = read_config32_dct(dev_fn2, node, channel, 0x84);
232 data->f2x88 = read_config32_dct(dev_fn2, node, channel, 0x88);
233 data->f2x8c = read_config32_dct(dev_fn2, node, channel, 0x8c);
234 data->f2x90 = read_config32_dct(dev_fn2, node, channel, 0x90);
235 data->f2xa4 = read_config32_dct(dev_fn2, node, channel, 0xa4);
236 data->f2xa8 = read_config32_dct(dev_fn2, node, channel, 0xa8);
237
238 /* Family 15h-specific configuration */
239 if (is_fam15h()) {
240 data->f2x200 = read_config32_dct(dev_fn2, node, channel, 0x200);
241 data->f2x204 = read_config32_dct(dev_fn2, node, channel, 0x204);
242 data->f2x208 = read_config32_dct(dev_fn2, node, channel, 0x208);
243 data->f2x20c = read_config32_dct(dev_fn2, node, channel, 0x20c);
244 for (i=0; i<4; i++)
245 data->f2x210[i] = read_config32_dct_nbpstate(dev_fn2, node, channel, i, 0x210);
246 data->f2x214 = read_config32_dct(dev_fn2, node, channel, 0x214);
247 data->f2x218 = read_config32_dct(dev_fn2, node, channel, 0x218);
248 data->f2x21c = read_config32_dct(dev_fn2, node, channel, 0x21c);
249 data->f2x22c = read_config32_dct(dev_fn2, node, channel, 0x22c);
250 data->f2x230 = read_config32_dct(dev_fn2, node, channel, 0x230);
251 data->f2x234 = read_config32_dct(dev_fn2, node, channel, 0x234);
252 data->f2x238 = read_config32_dct(dev_fn2, node, channel, 0x238);
253 data->f2x23c = read_config32_dct(dev_fn2, node, channel, 0x23c);
254 data->f2x240 = read_config32_dct(dev_fn2, node, channel, 0x240);
255
256 data->f2x9cx0d0fe003 = read_amd_dct_index_register_dct(dev_fn2, node, channel, 0x98, 0x0d0fe003);
257 data->f2x9cx0d0fe013 = read_amd_dct_index_register_dct(dev_fn2, node, channel, 0x98, 0x0d0fe013);
258 for (i=0; i<9; i++)
259 data->f2x9cx0d0f0_8_0_1f[i] = read_amd_dct_index_register_dct(dev_fn2, node, channel, 0x98, 0x0d0f001f | (i << 8));
260 data->f2x9cx0d0f201f = read_amd_dct_index_register_dct(dev_fn2, node, channel, 0x98, 0x0d0f201f);
261 data->f2x9cx0d0f211f = read_amd_dct_index_register_dct(dev_fn2, node, channel, 0x98, 0x0d0f211f);
262 data->f2x9cx0d0f221f = read_amd_dct_index_register_dct(dev_fn2, node, channel, 0x98, 0x0d0f221f);
263 data->f2x9cx0d0f801f = read_amd_dct_index_register_dct(dev_fn2, node, channel, 0x98, 0x0d0f801f);
264 data->f2x9cx0d0f811f = read_amd_dct_index_register_dct(dev_fn2, node, channel, 0x98, 0x0d0f811f);
265 data->f2x9cx0d0f821f = read_amd_dct_index_register_dct(dev_fn2, node, channel, 0x98, 0x0d0f821f);
266 data->f2x9cx0d0fc01f = read_amd_dct_index_register_dct(dev_fn2, node, channel, 0x98, 0x0d0fc01f);
267 data->f2x9cx0d0fc11f = read_amd_dct_index_register_dct(dev_fn2, node, channel, 0x98, 0x0d0fc11f);
268 data->f2x9cx0d0fc21f = read_amd_dct_index_register_dct(dev_fn2, node, channel, 0x98, 0x0d0fc21f);
269 data->f2x9cx0d0f4009 = read_amd_dct_index_register_dct(dev_fn2, node, channel, 0x98, 0x0d0f4009);
270 for (i=0; i<9; i++)
271 data->f2x9cx0d0f0_8_0_02[i] = read_amd_dct_index_register_dct(dev_fn2, node, channel, 0x98, 0x0d0f0002 | (i << 8));
272 for (i=0; i<9; i++)
273 data->f2x9cx0d0f0_8_0_06[i] = read_amd_dct_index_register_dct(dev_fn2, node, channel, 0x98, 0x0d0f0006 | (i << 8));
274 for (i=0; i<9; i++)
275 data->f2x9cx0d0f0_8_0_0a[i] = read_amd_dct_index_register_dct(dev_fn2, node, channel, 0x98, 0x0d0f000a | (i << 8));
276
277 data->f2x9cx0d0f2002 = read_amd_dct_index_register_dct(dev_fn2, node, channel, 0x98, 0x0d0f2002);
278 data->f2x9cx0d0f2102 = read_amd_dct_index_register_dct(dev_fn2, node, channel, 0x98, 0x0d0f2102);
279 data->f2x9cx0d0f2202 = read_amd_dct_index_register_dct(dev_fn2, node, channel, 0x98, 0x0d0f2202);
280 data->f2x9cx0d0f8002 = read_amd_dct_index_register_dct(dev_fn2, node, channel, 0x98, 0x0d0f8002);
281 data->f2x9cx0d0f8006 = read_amd_dct_index_register_dct(dev_fn2, node, channel, 0x98, 0x0d0f8006);
282 data->f2x9cx0d0f800a = read_amd_dct_index_register_dct(dev_fn2, node, channel, 0x98, 0x0d0f800a);
283 data->f2x9cx0d0f8102 = read_amd_dct_index_register_dct(dev_fn2, node, channel, 0x98, 0x0d0f8102);
284 data->f2x9cx0d0f8106 = read_amd_dct_index_register_dct(dev_fn2, node, channel, 0x98, 0x0d0f8106);
285 data->f2x9cx0d0f810a = read_amd_dct_index_register_dct(dev_fn2, node, channel, 0x98, 0x0d0f810a);
286 data->f2x9cx0d0fc002 = read_amd_dct_index_register_dct(dev_fn2, node, channel, 0x98, 0x0d0fc002);
287 data->f2x9cx0d0fc006 = read_amd_dct_index_register_dct(dev_fn2, node, channel, 0x98, 0x0d0fc006);
288 data->f2x9cx0d0fc00a = read_amd_dct_index_register_dct(dev_fn2, node, channel, 0x98, 0x0d0fc00a);
289 data->f2x9cx0d0fc00e = read_amd_dct_index_register_dct(dev_fn2, node, channel, 0x98, 0x0d0fc00e);
290 data->f2x9cx0d0fc012 = read_amd_dct_index_register_dct(dev_fn2, node, channel, 0x98, 0x0d0fc012);
291
292 data->f2x9cx0d0f2031 = read_amd_dct_index_register_dct(dev_fn2, node, channel, 0x98, 0x0d0f2031);
293 data->f2x9cx0d0f2131 = read_amd_dct_index_register_dct(dev_fn2, node, channel, 0x98, 0x0d0f2131);
294 data->f2x9cx0d0f2231 = read_amd_dct_index_register_dct(dev_fn2, node, channel, 0x98, 0x0d0f2231);
295 data->f2x9cx0d0f8031 = read_amd_dct_index_register_dct(dev_fn2, node, channel, 0x98, 0x0d0f8031);
296 data->f2x9cx0d0f8131 = read_amd_dct_index_register_dct(dev_fn2, node, channel, 0x98, 0x0d0f8131);
297 data->f2x9cx0d0f8231 = read_amd_dct_index_register_dct(dev_fn2, node, channel, 0x98, 0x0d0f8231);
298 data->f2x9cx0d0fc031 = read_amd_dct_index_register_dct(dev_fn2, node, channel, 0x98, 0x0d0fc031);
299 data->f2x9cx0d0fc131 = read_amd_dct_index_register_dct(dev_fn2, node, channel, 0x98, 0x0d0fc131);
300 data->f2x9cx0d0fc231 = read_amd_dct_index_register_dct(dev_fn2, node, channel, 0x98, 0x0d0fc231);
301 for (i=0; i<9; i++)
302 data->f2x9cx0d0f0_0_f_31[i] = read_amd_dct_index_register_dct(dev_fn2, node, channel, 0x98, 0x0d0f0031 | (i << 8));
303
304 data->f2x9cx0d0f8021 = read_amd_dct_index_register_dct(dev_fn2, node, channel, 0x98, 0x0d0f8021);
305 }
Timothy Pearson59d0e042015-09-05 18:40:31 -0500306
307 /* Stage 4 */
Timothy Pearson730a0432015-10-16 13:51:51 -0500308 data->f2x94 = read_config32_dct(dev_fn2, node, channel, 0x94);
Timothy Pearson59d0e042015-09-05 18:40:31 -0500309
310 /* Stage 6 */
311 for (i=0; i<9; i++)
312 for (j=0; j<3; j++)
Timothy Pearson730a0432015-10-16 13:51:51 -0500313 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));
314 data->f2x9cx00 = read_amd_dct_index_register_dct(dev_fn2, node, channel, 0x98, 0x00);
315 data->f2x9cx0a = read_amd_dct_index_register_dct(dev_fn2, node, channel, 0x98, 0x0a);
316 data->f2x9cx0c = read_amd_dct_index_register_dct(dev_fn2, node, channel, 0x98, 0x0c);
Timothy Pearson59d0e042015-09-05 18:40:31 -0500317
318 /* Stage 7 */
Timothy Pearson730a0432015-10-16 13:51:51 -0500319 data->f2x9cx04 = read_amd_dct_index_register_dct(dev_fn2, node, channel, 0x98, 0x04);
Timothy Pearson59d0e042015-09-05 18:40:31 -0500320
321 /* Stage 9 */
Timothy Pearson730a0432015-10-16 13:51:51 -0500322 data->f2x9cx0d0fe006 = read_amd_dct_index_register_dct(dev_fn2, node, channel, 0x98, 0x0d0fe006);
323 data->f2x9cx0d0fe007 = read_amd_dct_index_register_dct(dev_fn2, node, channel, 0x98, 0x0d0fe007);
Timothy Pearson59d0e042015-09-05 18:40:31 -0500324
325 /* Stage 10 */
326 for (i=0; i<12; i++)
Timothy Pearson730a0432015-10-16 13:51:51 -0500327 data->f2x9cx10[i] = read_amd_dct_index_register_dct(dev_fn2, node, channel, 0x98, 0x10 + i);
Timothy Pearson59d0e042015-09-05 18:40:31 -0500328 for (i=0; i<12; i++)
Timothy Pearson730a0432015-10-16 13:51:51 -0500329 data->f2x9cx20[i] = read_amd_dct_index_register_dct(dev_fn2, node, channel, 0x98, 0x20 + i);
Timothy Pearson59d0e042015-09-05 18:40:31 -0500330 for (i=0; i<4; i++)
331 for (j=0; j<3; j++)
Timothy Pearson730a0432015-10-16 13:51:51 -0500332 data->f2x9cx3_0_0_3_1[i][j] = read_amd_dct_index_register_dct(dev_fn2, node, channel, 0x98, (0x01 + i) + (0x100 * j));
Timothy Pearson59d0e042015-09-05 18:40:31 -0500333 for (i=0; i<4; i++)
334 for (j=0; j<3; j++)
Timothy Pearson730a0432015-10-16 13:51:51 -0500335 data->f2x9cx3_0_0_7_5[i][j] = read_amd_dct_index_register_dct(dev_fn2, node, channel, 0x98, (0x05 + i) + (0x100 * j));
336 data->f2x9cx0d = read_amd_dct_index_register_dct(dev_fn2, node, channel, 0x98, 0x0d);
Timothy Pearson59d0e042015-09-05 18:40:31 -0500337 for (i=0; i<9; i++)
Timothy Pearson730a0432015-10-16 13:51:51 -0500338 data->f2x9cx0d0f0_f_0_13[i] = read_amd_dct_index_register_dct(dev_fn2, node, channel, 0x98, 0x0d0f0013 | (i << 8));
Timothy Pearson59d0e042015-09-05 18:40:31 -0500339 for (i=0; i<9; i++)
Timothy Pearson730a0432015-10-16 13:51:51 -0500340 data->f2x9cx0d0f0_f_0_30[i] = read_amd_dct_index_register_dct(dev_fn2, node, channel, 0x98, 0x0d0f0030 | (i << 8));
Timothy Pearson59d0e042015-09-05 18:40:31 -0500341 for (i=0; i<4; i++)
Timothy Pearson730a0432015-10-16 13:51:51 -0500342 data->f2x9cx0d0f2_f_0_30[i] = read_amd_dct_index_register_dct(dev_fn2, node, channel, 0x98, 0x0d0f2030 | (i << 8));
Timothy Pearson59d0e042015-09-05 18:40:31 -0500343 for (i=0; i<2; i++)
344 for (j=0; j<3; j++)
Timothy Pearson730a0432015-10-16 13:51:51 -0500345 data->f2x9cx0d0f8_8_4_0[i][j] = read_amd_dct_index_register_dct(dev_fn2, node, channel, 0x98, 0x0d0f0000 | (i << 8) | (j * 4));
346 data->f2x9cx0d0f812f = read_amd_dct_index_register_dct(dev_fn2, node, channel, 0x98, 0x0d0f812f);
Timothy Pearson59d0e042015-09-05 18:40:31 -0500347
348 /* Stage 11 */
349 if (IS_ENABLED(CONFIG_DIMM_DDR3)) {
350 for (i=0; i<12; i++)
Timothy Pearson730a0432015-10-16 13:51:51 -0500351 data->f2x9cx30[i] = read_amd_dct_index_register_dct(dev_fn2, node, channel, 0x98, 0x30 + i);
Timothy Pearson59d0e042015-09-05 18:40:31 -0500352 for (i=0; i<12; i++)
Timothy Pearson730a0432015-10-16 13:51:51 -0500353 data->f2x9cx40[i] = read_amd_dct_index_register_dct(dev_fn2, node, channel, 0x98, 0x40 + i);
Timothy Pearson59d0e042015-09-05 18:40:31 -0500354 }
355
356 /* Other */
357 /* ECC scrub rate control */
358 data->f3x58 = pci_read_config32(dev_fn3, 0x58);
359 }
360 }
361}
362#else
Timothy Pearson730a0432015-10-16 13:51:51 -0500363static void write_config32_dct(device_t dev, uint8_t node, uint8_t dct, uint32_t reg, uint32_t value) {
364 if (is_fam15h()) {
365 uint32_t dword;
366 device_t dev_fn1 = PCI_DEV(0, 0x18 + node, 1);
367
368 /* Select DCT */
369 dword = pci_read_config32(dev_fn1, 0x10c);
370 dword &= ~0x1;
371 dword |= (dct & 0x1);
372 pci_write_config32(dev_fn1, 0x10c, dword);
373 } else {
374 /* Apply offset */
375 reg += dct * 0x100;
376 }
377
378 pci_write_config32(dev, reg, value);
379}
380
381static void write_config32_dct_nbpstate(device_t dev, uint8_t node, uint8_t dct, uint8_t nb_pstate, uint32_t reg, uint32_t value) {
382 uint32_t dword;
383 device_t dev_fn1 = PCI_DEV(0, 0x18 + node, 1);
384
385 /* Select DCT */
386 dword = pci_read_config32(dev_fn1, 0x10c);
387 dword &= ~0x1;
388 dword |= (dct & 0x1);
389 pci_write_config32(dev_fn1, 0x10c, dword);
390
391 /* Select NB Pstate index */
392 dword = pci_read_config32(dev_fn1, 0x10c);
393 dword &= ~(0x3 << 4);
394 dword |= (nb_pstate & 0x3) << 4;
395 pci_write_config32(dev_fn1, 0x10c, dword);
396
397 pci_write_config32(dev, reg, value);
398}
399
Timothy Pearson59d0e042015-09-05 18:40:31 -0500400static void write_amd_dct_index_register(device_t dev, uint32_t index_ctl_reg, uint32_t index, uint32_t value)
401{
402 uint32_t dword;
403
404 pci_write_config32(dev, index_ctl_reg + 0x04, value);
405 index |= (1 << 30);
406 pci_write_config32(dev, index_ctl_reg, index);
407 do {
408 dword = pci_read_config32(dev, index_ctl_reg);
409 } while (!(dword & (1 << 31)));
410}
Timothy Pearson730a0432015-10-16 13:51:51 -0500411
412static void write_amd_dct_index_register_dct(device_t dev, uint8_t node, uint8_t dct, uint32_t index_ctl_reg, uint32_t index, uint32_t value)
413{
414 if (is_fam15h()) {
415 uint32_t dword;
416 device_t dev_fn1 = PCI_DEV(0, 0x18 + node, 1);
417
418 /* Select DCT */
419 dword = pci_read_config32(dev_fn1, 0x10c);
420 dword &= ~0x1;
421 dword |= (dct & 0x1);
422 pci_write_config32(dev_fn1, 0x10c, dword);
423 } else {
424 /* Apply offset */
425 index_ctl_reg += dct * 0x100;
426 }
427
428 return write_amd_dct_index_register(dev, index_ctl_reg, index, value);
429}
Timothy Pearson59d0e042015-09-05 18:40:31 -0500430#endif
431
432#ifdef __PRE_RAM__
433static void wrmsr_uint64_t(unsigned long index, uint64_t value) {
434 msr_t msr;
435 msr.hi = (value & 0xffffffff00000000ULL) >> 32;
436 msr.lo = (value & 0xffffffff);
437 wrmsr(index, msr);
438}
439
440void restore_mct_data_from_save_variable(struct amd_s3_persistent_data* persistent_data)
441{
442 uint8_t i;
443 uint8_t j;
444 uint8_t node;
445 uint8_t channel;
446 uint8_t ganged;
447 uint8_t dct_enabled;
448 uint32_t dword;
449
450 /* Load data from data structure into DCTs */
451 /* Stage 1 */
452 for (node = 0; node < MAX_NODES_SUPPORTED; node++) {
453 for (channel = 0; channel < 2; channel++) {
454 struct amd_s3_persistent_mct_channel_data* data = &persistent_data->node[node].channel[channel];
455 if (!persistent_data->node[node].node_present)
456 continue;
457
458 pci_write_config32(PCI_DEV(0, 0x18 + node, 2), 0x110, data->f2x110);
459 }
460 }
461
462 /* Stage 2 */
463 for (node = 0; node < MAX_NODES_SUPPORTED; node++) {
464 for (channel = 0; channel < 2; channel++) {
465 struct amd_s3_persistent_mct_channel_data* data = &persistent_data->node[node].channel[channel];
466 if (!persistent_data->node[node].node_present)
467 continue;
468
Timothy Pearson730a0432015-10-16 13:51:51 -0500469 write_config32_dct(PCI_DEV(0, 0x18 + node, 1), node, channel, 0x40, data->f1x40);
470 write_config32_dct(PCI_DEV(0, 0x18 + node, 1), node, channel, 0x44, data->f1x44);
471 write_config32_dct(PCI_DEV(0, 0x18 + node, 1), node, channel, 0x48, data->f1x48);
472 write_config32_dct(PCI_DEV(0, 0x18 + node, 1), node, channel, 0x4c, data->f1x4c);
473 write_config32_dct(PCI_DEV(0, 0x18 + node, 1), node, channel, 0x50, data->f1x50);
474 write_config32_dct(PCI_DEV(0, 0x18 + node, 1), node, channel, 0x54, data->f1x54);
475 write_config32_dct(PCI_DEV(0, 0x18 + node, 1), node, channel, 0x58, data->f1x58);
476 write_config32_dct(PCI_DEV(0, 0x18 + node, 1), node, channel, 0x5c, data->f1x5c);
477 write_config32_dct(PCI_DEV(0, 0x18 + node, 1), node, channel, 0x60, data->f1x60);
478 write_config32_dct(PCI_DEV(0, 0x18 + node, 1), node, channel, 0x64, data->f1x64);
479 write_config32_dct(PCI_DEV(0, 0x18 + node, 1), node, channel, 0x68, data->f1x68);
480 write_config32_dct(PCI_DEV(0, 0x18 + node, 1), node, channel, 0x6c, data->f1x6c);
481 write_config32_dct(PCI_DEV(0, 0x18 + node, 1), node, channel, 0x70, data->f1x70);
482 write_config32_dct(PCI_DEV(0, 0x18 + node, 1), node, channel, 0x74, data->f1x74);
483 write_config32_dct(PCI_DEV(0, 0x18 + node, 1), node, channel, 0x78, data->f1x78);
484 write_config32_dct(PCI_DEV(0, 0x18 + node, 1), node, channel, 0x7c, data->f1x7c);
485 write_config32_dct(PCI_DEV(0, 0x18 + node, 1), node, channel, 0xf0, data->f1xf0);
486 write_config32_dct(PCI_DEV(0, 0x18 + node, 1), node, channel, 0x120, data->f1x120);
487 write_config32_dct(PCI_DEV(0, 0x18 + node, 1), node, channel, 0x124, data->f1x124);
488 write_config32_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x10c, data->f2x10c);
489 write_config32_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x114, data->f2x114);
490 write_config32_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x118, data->f2x118);
491 write_config32_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x11c, data->f2x11c);
492 write_config32_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x1b0, data->f2x1b0);
493 write_config32_dct(PCI_DEV(0, 0x18 + node, 3), node, channel, 0x44, data->f3x44);
Timothy Pearson59d0e042015-09-05 18:40:31 -0500494 for (i=0; i<16; i++) {
495 wrmsr_uint64_t(0x00000200 | i, data->msr0000020[i]);
496 }
497 wrmsr_uint64_t(0x00000250, data->msr00000250);
498 wrmsr_uint64_t(0x00000258, data->msr00000258);
499 /* FIXME
500 * Restoring these MSRs causes a hang on resume
501 * For now, skip restoration...
502 */
503 // for (i=0; i<8; i++)
504 // wrmsr_uint64_t(0x00000260 | (i + 8), data->msr0000026[i]);
505 wrmsr_uint64_t(0x000002ff, data->msr000002ff);
506 wrmsr_uint64_t(0xc0010010, data->msrc0010010);
507 wrmsr_uint64_t(0xc001001a, data->msrc001001a);
508 wrmsr_uint64_t(0xc001001d, data->msrc001001d);
509 wrmsr_uint64_t(0xc001001f, data->msrc001001f);
510 }
511 }
512
513 /* Stage 3 */
514 for (node = 0; node < MAX_NODES_SUPPORTED; node++) {
515 for (channel = 0; channel < 2; channel++) {
516 struct amd_s3_persistent_mct_channel_data* data = &persistent_data->node[node].channel[channel];
517 if (!persistent_data->node[node].node_present)
518 continue;
519
Timothy Pearson730a0432015-10-16 13:51:51 -0500520 if (is_fam15h())
521 ganged = 0;
522 else
523 ganged = !!(data->f2x110 & 0x10);
Timothy Pearson59d0e042015-09-05 18:40:31 -0500524 if ((ganged == 1) && (channel > 0))
525 continue;
526
Timothy Pearson730a0432015-10-16 13:51:51 -0500527 write_config32_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x40, data->f2x40);
528 write_config32_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x44, data->f2x44);
529 write_config32_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x48, data->f2x48);
530 write_config32_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x4c, data->f2x4c);
531 write_config32_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x50, data->f2x50);
532 write_config32_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x54, data->f2x54);
533 write_config32_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x58, data->f2x58);
534 write_config32_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x5c, data->f2x5c);
535 write_config32_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x60, data->f2x60);
536 write_config32_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x64, data->f2x64);
537 write_config32_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x68, data->f2x68);
538 write_config32_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x6c, data->f2x6c);
539 write_config32_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x78, data->f2x78);
540 write_config32_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x7c, data->f2x7c);
541 write_config32_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x80, data->f2x80);
542 write_config32_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x84, data->f2x84);
543 write_config32_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x88, data->f2x88);
544 write_config32_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x8c, data->f2x8c);
545 write_config32_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x90, data->f2x90);
546 write_config32_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0xa4, data->f2xa4);
547 write_config32_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0xa8, data->f2xa8);
548 }
549 }
550
551 /* Family 15h-specific configuration */
552 if (is_fam15h()) {
553 for (node = 0; node < MAX_NODES_SUPPORTED; node++) {
554 for (channel = 0; channel < 2; channel++) {
555 struct amd_s3_persistent_mct_channel_data* data = &persistent_data->node[node].channel[channel];
556 if (!persistent_data->node[node].node_present)
557 continue;
558
559 /* Initialize DCT */
560 write_amd_dct_index_register_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x98, 0x0000000b, 0x80000000);
561 dword = read_amd_dct_index_register_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x98, 0x0d0fe013);
562 dword &= ~0xffff;
563 dword |= 0x118;
564 write_amd_dct_index_register_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x98, 0x0d0fe013, dword);
565
566 /* Restore values */
567 write_config32_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x200, data->f2x200);
568 write_config32_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x204, data->f2x204);
569 write_config32_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x208, data->f2x208);
570 write_config32_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x20c, data->f2x20c);
571 for (i=0; i<4; i++)
572 write_config32_dct_nbpstate(PCI_DEV(0, 0x18 + node, 2), node, channel, i, 0x210, data->f2x210[i]);
573 write_config32_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x214, data->f2x214);
574 write_config32_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x218, data->f2x218);
575 write_config32_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x21c, data->f2x21c);
576 write_config32_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x22c, data->f2x22c);
577 write_config32_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x230, data->f2x230);
578 write_config32_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x234, data->f2x234);
579 write_config32_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x238, data->f2x238);
580 write_config32_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x23c, data->f2x23c);
581 write_config32_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x240, data->f2x240);
582
583 write_amd_dct_index_register_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x98, 0x0d0fe013, data->f2x9cx0d0fe013);
584 for (i=0; i<9; i++)
585 write_amd_dct_index_register_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x98, 0x0d0f001f | (i << 8), data->f2x9cx0d0f0_8_0_1f[i]);
586 write_amd_dct_index_register_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x98, 0x0d0f201f, data->f2x9cx0d0f201f);
587 write_amd_dct_index_register_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x98, 0x0d0f211f, data->f2x9cx0d0f211f);
588 write_amd_dct_index_register_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x98, 0x0d0f221f, data->f2x9cx0d0f221f);
589 write_amd_dct_index_register_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x98, 0x0d0f801f, data->f2x9cx0d0f801f);
590 write_amd_dct_index_register_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x98, 0x0d0f811f, data->f2x9cx0d0f811f);
591 write_amd_dct_index_register_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x98, 0x0d0f821f, data->f2x9cx0d0f821f);
592 write_amd_dct_index_register_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x98, 0x0d0fc01f, data->f2x9cx0d0fc01f);
593 write_amd_dct_index_register_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x98, 0x0d0fc11f, data->f2x9cx0d0fc11f);
594 write_amd_dct_index_register_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x98, 0x0d0fc21f, data->f2x9cx0d0fc21f);
595 write_amd_dct_index_register_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x98, 0x0d0f4009, data->f2x9cx0d0f4009);
596
597 write_amd_dct_index_register_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x98, 0x0d0f2031, data->f2x9cx0d0f2031);
598 write_amd_dct_index_register_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x98, 0x0d0f2131, data->f2x9cx0d0f2131);
599 write_amd_dct_index_register_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x98, 0x0d0f2231, data->f2x9cx0d0f2231);
600 write_amd_dct_index_register_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x98, 0x0d0f8031, data->f2x9cx0d0f8031);
601 write_amd_dct_index_register_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x98, 0x0d0f8131, data->f2x9cx0d0f8131);
602 write_amd_dct_index_register_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x98, 0x0d0f8231, data->f2x9cx0d0f8231);
603 write_amd_dct_index_register_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x98, 0x0d0fc031, data->f2x9cx0d0fc031);
604 write_amd_dct_index_register_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x98, 0x0d0fc131, data->f2x9cx0d0fc131);
605 write_amd_dct_index_register_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x98, 0x0d0fc231, data->f2x9cx0d0fc231);
606 for (i=0; i<9; i++)
607 write_amd_dct_index_register_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x98, 0x0d0f0031 | (i << 8), data->f2x9cx0d0f0_0_f_31[i]);
608
609 write_amd_dct_index_register_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x98, 0x0d0f8021, data->f2x9cx0d0f8021);
610 }
Timothy Pearson59d0e042015-09-05 18:40:31 -0500611 }
612 }
613
614 /* Stage 4 */
615 for (node = 0; node < MAX_NODES_SUPPORTED; node++) {
616 for (channel = 0; channel < 2; channel++) {
617 struct amd_s3_persistent_mct_channel_data* data = &persistent_data->node[node].channel[channel];
618 if (!persistent_data->node[node].node_present)
619 continue;
620
Timothy Pearson730a0432015-10-16 13:51:51 -0500621 if (is_fam15h())
622 ganged = 0;
623 else
624 ganged = !!(data->f2x110 & 0x10);
Timothy Pearson59d0e042015-09-05 18:40:31 -0500625 if ((ganged == 1) && (channel > 0))
626 continue;
627
Timothy Pearson730a0432015-10-16 13:51:51 -0500628 if (is_fam15h()) {
629 /* Program PllLockTime = 0x190 */
630 dword = read_amd_dct_index_register_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x98, 0x0d0fe006);
631 dword &= ~0xffff;
632 dword |= 0x190;
633 write_amd_dct_index_register_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x98, 0x0d0fe006, dword);
Timothy Pearson59d0e042015-09-05 18:40:31 -0500634
Timothy Pearson730a0432015-10-16 13:51:51 -0500635 /* Program MemClkFreqVal = 0 */
636 dword = read_config32_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x94);
637 dword &= (0x1 << 7);
638 write_config32_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x94, dword);
639
640 /* Restore DRAM Adddress/Timing Control Register */
641 write_amd_dct_index_register_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x98, 0x04, data->f2x9cx04);
642 } else {
643 /* Disable PHY auto-compensation engine */
644 dword = read_amd_dct_index_register_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x98, 0x08);
645 if (!(dword & (1 << 30))) {
646 dword |= (1 << 30);
647 write_amd_dct_index_register_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x98, 0x08, dword);
648
649 /* Wait for 5us */
650 mct_Wait(100);
651 }
Timothy Pearson59d0e042015-09-05 18:40:31 -0500652 }
653
654 /* Restore DRAM Configuration High Register */
Timothy Pearson730a0432015-10-16 13:51:51 -0500655 write_config32_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x94, data->f2x94);
Timothy Pearson59d0e042015-09-05 18:40:31 -0500656 }
657 }
658
Timothy Pearson59d0e042015-09-05 18:40:31 -0500659 /* Stage 5 */
660 for (node = 0; node < MAX_NODES_SUPPORTED; node++) {
661 for (channel = 0; channel < 2; channel++) {
662 struct amd_s3_persistent_mct_channel_data* data = &persistent_data->node[node].channel[channel];
663 if (!persistent_data->node[node].node_present)
664 continue;
665
Timothy Pearson730a0432015-10-16 13:51:51 -0500666 if (is_fam15h())
667 ganged = 0;
668 else
669 ganged = !!(data->f2x110 & 0x10);
Timothy Pearson59d0e042015-09-05 18:40:31 -0500670 if ((ganged == 1) && (channel > 0))
671 continue;
672
Timothy Pearson730a0432015-10-16 13:51:51 -0500673 dct_enabled = !(data->f2x94 & (1 << 14));
674 if (!dct_enabled)
675 continue;
676
Timothy Pearson59d0e042015-09-05 18:40:31 -0500677 /* Wait for any pending PHY frequency changes to complete */
678 do {
Timothy Pearson730a0432015-10-16 13:51:51 -0500679 dword = read_config32_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x94);
Timothy Pearson59d0e042015-09-05 18:40:31 -0500680 } while (dword & (1 << 21));
Timothy Pearson730a0432015-10-16 13:51:51 -0500681
682 if (is_fam15h()) {
683 /* Program PllLockTime = 0xf */
684 dword = read_amd_dct_index_register_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x98, 0x0d0fe006);
685 dword &= ~0xffff;
686 dword |= 0xf;
687 write_amd_dct_index_register_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x98, 0x0d0fe006, dword);
688 } else {
689 /* Enable PHY auto-compensation engine */
690 dword = read_amd_dct_index_register_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x98, 0x08);
691 dword &= ~(1 << 30);
692 write_amd_dct_index_register_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x98, 0x08, dword);
693 }
Timothy Pearson59d0e042015-09-05 18:40:31 -0500694 }
695 }
696
Timothy Pearson730a0432015-10-16 13:51:51 -0500697 /* Wait for 750us */
698 mct_Wait(15000);
699
Timothy Pearson59d0e042015-09-05 18:40:31 -0500700 /* Stage 6 */
701 for (node = 0; node < MAX_NODES_SUPPORTED; node++) {
702 for (channel = 0; channel < 2; channel++) {
703 struct amd_s3_persistent_mct_channel_data* data = &persistent_data->node[node].channel[channel];
704 if (!persistent_data->node[node].node_present)
705 continue;
706
707 for (i=0; i<9; i++)
708 for (j=0; j<3; j++)
Timothy Pearson730a0432015-10-16 13:51:51 -0500709 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]);
710 write_amd_dct_index_register_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x98, 0x00, data->f2x9cx00);
711 write_amd_dct_index_register_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x98, 0x0a, data->f2x9cx0a);
712 write_amd_dct_index_register_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x98, 0x0c, data->f2x9cx0c);
713 }
714 }
715
716 /* Family 15h-specific configuration */
717 if (is_fam15h()) {
718 for (node = 0; node < MAX_NODES_SUPPORTED; node++) {
719 for (channel = 0; channel < 2; channel++) {
720 struct amd_s3_persistent_mct_channel_data* data = &persistent_data->node[node].channel[channel];
721 if (!persistent_data->node[node].node_present)
722 continue;
723
724 dword = read_amd_dct_index_register_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x98, 0x0d0fe003);
725 dword |= (0x3 << 13); /* DisAutoComp, DisablePredriverCal = 1 */
726 write_amd_dct_index_register_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x98, 0x0d0fe003, dword);
727
728 for (i=0; i<9; i++)
729 write_amd_dct_index_register_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x98, 0x0d0f0006 | (i << 8), data->f2x9cx0d0f0_8_0_06[i]);
730 for (i=0; i<9; i++)
731 write_amd_dct_index_register_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x98, 0x0d0f000a | (i << 8), data->f2x9cx0d0f0_8_0_0a[i]);
732 for (i=0; i<9; i++)
733 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]));
734
735 write_amd_dct_index_register_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x98, 0x0d0f8006, data->f2x9cx0d0f8006);
736 write_amd_dct_index_register_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x98, 0x0d0f800a, data->f2x9cx0d0f800a);
737 write_amd_dct_index_register_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x98, 0x0d0f8106, data->f2x9cx0d0f8106);
738 write_amd_dct_index_register_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x98, 0x0d0f810a, data->f2x9cx0d0f810a);
739 write_amd_dct_index_register_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x98, 0x0d0fc006, data->f2x9cx0d0fc006);
740 write_amd_dct_index_register_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x98, 0x0d0fc00a, data->f2x9cx0d0fc00a);
741 write_amd_dct_index_register_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x98, 0x0d0fc00e, data->f2x9cx0d0fc00e);
742 write_amd_dct_index_register_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x98, 0x0d0fc012, data->f2x9cx0d0fc012);
743 write_amd_dct_index_register_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x98, 0x0d0f8002, (0x8000 | data->f2x9cx0d0f8002));
744 write_amd_dct_index_register_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x98, 0x0d0f8102, (0x8000 | data->f2x9cx0d0f8102));
745 write_amd_dct_index_register_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x98, 0x0d0fc002, (0x8000 | data->f2x9cx0d0fc002));
746 write_amd_dct_index_register_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x98, 0x0d0f2002, (0x8000 | data->f2x9cx0d0f2002));
747 write_amd_dct_index_register_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x98, 0x0d0f2102, (0x8000 | data->f2x9cx0d0f2102));
748 write_amd_dct_index_register_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x98, 0x0d0f2202, (0x8000 | data->f2x9cx0d0f2202));
749
750 write_amd_dct_index_register_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x98, 0x0d0fe003, data->f2x9cx0d0fe003);
751 }
Timothy Pearson59d0e042015-09-05 18:40:31 -0500752 }
753 }
754
755 /* Stage 7 */
756 for (node = 0; node < MAX_NODES_SUPPORTED; node++) {
757 for (channel = 0; channel < 2; channel++) {
758 struct amd_s3_persistent_mct_channel_data* data = &persistent_data->node[node].channel[channel];
759 if (!persistent_data->node[node].node_present)
760 continue;
761
Timothy Pearson730a0432015-10-16 13:51:51 -0500762 if (is_fam15h())
763 ganged = 0;
764 else
765 ganged = !!(data->f2x110 & 0x10);
Timothy Pearson59d0e042015-09-05 18:40:31 -0500766 if ((ganged == 1) && (channel > 0))
767 continue;
768
Timothy Pearson730a0432015-10-16 13:51:51 -0500769 if (!is_fam15h())
770 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 -0500771 }
772 }
773
774 /* Stage 8 */
775 for (node = 0; node < MAX_NODES_SUPPORTED; node++) {
776 for (channel = 0; channel < 2; channel++) {
777 struct amd_s3_persistent_mct_channel_data* data = &persistent_data->node[node].channel[channel];
778 if (!persistent_data->node[node].node_present)
779 continue;
780
781 dct_enabled = !(data->f2x94 & (1 << 14));
782 if (!dct_enabled)
783 continue;
784
Timothy Pearson730a0432015-10-16 13:51:51 -0500785 if (is_fam15h())
786 ganged = 0;
787 else
788 ganged = !!(data->f2x110 & 0x10);
Timothy Pearson59d0e042015-09-05 18:40:31 -0500789 if ((ganged == 1) && (channel > 0))
790 continue;
791
792 printk(BIOS_SPEW, "Taking DIMMs out of self refresh node: %d channel: %d\n", node, channel);
793
794 /* Exit self refresh mode */
Timothy Pearson730a0432015-10-16 13:51:51 -0500795 dword = read_config32_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x90);
Timothy Pearson59d0e042015-09-05 18:40:31 -0500796 dword |= (1 << 1);
Timothy Pearson730a0432015-10-16 13:51:51 -0500797 write_config32_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x90, dword);
Timothy Pearson59d0e042015-09-05 18:40:31 -0500798 }
799 }
800
801 /* Stage 9 */
802 for (node = 0; node < MAX_NODES_SUPPORTED; node++) {
803 for (channel = 0; channel < 2; channel++) {
804 struct amd_s3_persistent_mct_channel_data* data = &persistent_data->node[node].channel[channel];
805 if (!persistent_data->node[node].node_present)
806 continue;
807
808 dct_enabled = !(data->f2x94 & (1 << 14));
809 if (!dct_enabled)
810 continue;
811
812 printk(BIOS_SPEW, "Waiting for DIMMs to exit self refresh node: %d channel: %d\n", node, channel);
813
814 /* Wait for transition from self refresh mode to complete */
815 do {
Timothy Pearson730a0432015-10-16 13:51:51 -0500816 dword = read_config32_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x90);
Timothy Pearson59d0e042015-09-05 18:40:31 -0500817 } while (dword & (1 << 1));
818
819 /* Restore registers */
Timothy Pearson730a0432015-10-16 13:51:51 -0500820 write_amd_dct_index_register_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x98, 0x0d0fe006, data->f2x9cx0d0fe006);
821 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 -0500822 }
823 }
824
825 /* Stage 10 */
826 for (node = 0; node < MAX_NODES_SUPPORTED; node++) {
827 for (channel = 0; channel < 2; channel++) {
828 struct amd_s3_persistent_mct_channel_data* data = &persistent_data->node[node].channel[channel];
829 if (!persistent_data->node[node].node_present)
830 continue;
831
832 for (i=0; i<12; i++)
Timothy Pearson730a0432015-10-16 13:51:51 -0500833 write_amd_dct_index_register_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x98, 0x10 + i, data->f2x9cx10[i]);
Timothy Pearson59d0e042015-09-05 18:40:31 -0500834 for (i=0; i<12; i++)
Timothy Pearson730a0432015-10-16 13:51:51 -0500835 write_amd_dct_index_register_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x98, 0x20 + i, data->f2x9cx20[i]);
Timothy Pearson59d0e042015-09-05 18:40:31 -0500836 for (i=0; i<4; i++)
837 for (j=0; j<3; j++)
Timothy Pearson730a0432015-10-16 13:51:51 -0500838 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]);
Timothy Pearson59d0e042015-09-05 18:40:31 -0500839 for (i=0; i<4; i++)
840 for (j=0; j<3; j++)
Timothy Pearson730a0432015-10-16 13:51:51 -0500841 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]);
842 write_amd_dct_index_register_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x98, 0x0d, data->f2x9cx0d);
Timothy Pearson59d0e042015-09-05 18:40:31 -0500843 for (i=0; i<9; i++)
Timothy Pearson730a0432015-10-16 13:51:51 -0500844 write_amd_dct_index_register_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x98, 0x0d0f0013 | (i << 8), data->f2x9cx0d0f0_f_0_13[i]);
Timothy Pearson59d0e042015-09-05 18:40:31 -0500845 for (i=0; i<9; i++)
Timothy Pearson730a0432015-10-16 13:51:51 -0500846 write_amd_dct_index_register_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x98, 0x0d0f0030 | (i << 8), data->f2x9cx0d0f0_f_0_30[i]);
Timothy Pearson59d0e042015-09-05 18:40:31 -0500847 for (i=0; i<4; i++)
Timothy Pearson730a0432015-10-16 13:51:51 -0500848 write_amd_dct_index_register_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x98, 0x0d0f2030 | (i << 8), data->f2x9cx0d0f2_f_0_30[i]);
Timothy Pearson59d0e042015-09-05 18:40:31 -0500849 for (i=0; i<2; i++)
850 for (j=0; j<3; j++)
Timothy Pearson730a0432015-10-16 13:51:51 -0500851 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]);
852 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 -0500853 }
854 }
855
856 /* Stage 11 */
857 if (IS_ENABLED(CONFIG_DIMM_DDR3)) {
858 for (node = 0; node < MAX_NODES_SUPPORTED; node++) {
859 for (channel = 0; channel < 2; channel++) {
860 struct amd_s3_persistent_mct_channel_data* data = &persistent_data->node[node].channel[channel];
861 if (!persistent_data->node[node].node_present)
862 continue;
863
864 for (i=0; i<12; i++)
Timothy Pearson730a0432015-10-16 13:51:51 -0500865 write_amd_dct_index_register_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x98, 0x30 + i, data->f2x9cx30[i]);
Timothy Pearson59d0e042015-09-05 18:40:31 -0500866 for (i=0; i<12; i++)
Timothy Pearson730a0432015-10-16 13:51:51 -0500867 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 -0500868 }
869 }
870 }
871
872 /* Other */
873 for (node = 0; node < MAX_NODES_SUPPORTED; node++) {
874 for (channel = 0; channel < 2; channel++) {
875 struct amd_s3_persistent_mct_channel_data* data = &persistent_data->node[node].channel[channel];
876 if (!persistent_data->node[node].node_present)
877 continue;
878
879 /* ECC scrub rate control */
880 pci_write_config32(PCI_DEV(0, 0x18 + node, 3), 0x58, data->f3x58);
881 }
882 }
883}
884#endif
885
886#ifdef __RAMSTAGE__
887int8_t save_mct_information_to_nvram(void)
888{
889 if (acpi_is_wakeup_s3())
890 return 0;
891
892 printk(BIOS_DEBUG, "Writing AMD DCT configuration to Flash\n");
893
894 struct spi_flash *flash;
895 ssize_t s3nv_offset;
Timothy Pearson69b11f92015-05-31 18:46:40 -0500896 struct amd_s3_persistent_data *persistent_data;
897
898 /* Allocate temporary data structures */
899 persistent_data = malloc(sizeof(struct amd_s3_persistent_data));
900 if (!persistent_data) {
901 printk(BIOS_DEBUG, "Could not allocate S3 data structure in RAM\n");
902 return -1;
903 }
Timothy Pearson59d0e042015-09-05 18:40:31 -0500904
905 /* Obtain MCT configuration data */
Timothy Pearson69b11f92015-05-31 18:46:40 -0500906 copy_mct_data_to_save_variable(persistent_data);
Timothy Pearson59d0e042015-09-05 18:40:31 -0500907
908 /* Obtain CBFS file offset */
909 s3nv_offset = get_s3nv_file_offset();
910 if (s3nv_offset == -1)
911 return -1;
912
913 /* Align flash pointer to nearest boundary */
914 s3nv_offset &= ~(CONFIG_S3_DATA_SIZE-1);
915 s3nv_offset += CONFIG_S3_DATA_SIZE;
916
917 /* Set temporary SPI MMIO address */
918 device_t lpc_dev = dev_find_slot(0, PCI_DEVFN(0x14, 3));
919 uint32_t spi_mmio_prev = pci_read_config32(lpc_dev, 0xa0);
920 pci_write_config32(lpc_dev, 0xa0, (spi_mmio_prev & 0x1f) | 0xf0000000);
921
922 /* Initialize SPI and detect devices */
923 spi_init();
924 flash = spi_flash_probe(0, 0);
925 if (!flash) {
926 printk(BIOS_DEBUG, "Could not find SPI device\n");
927 return -1;
928 }
929
930 /* Set up SPI flash access */
931 flash->spi->rw = SPI_WRITE_FLAG;
932 spi_claim_bus(flash->spi);
933
934 /* Erase and write data structure */
935 flash->erase(flash, s3nv_offset, CONFIG_S3_DATA_SIZE);
Timothy Pearson69b11f92015-05-31 18:46:40 -0500936 flash->write(flash, s3nv_offset, sizeof(struct amd_s3_persistent_data), persistent_data);
937
938 /* Deallocate temporary data structures */
939 free(persistent_data);
Timothy Pearson59d0e042015-09-05 18:40:31 -0500940
941 /* Tear down SPI flash access */
942 flash->spi->rw = SPI_WRITE_FLAG;
943 spi_release_bus(flash->spi);
944
945 /* Restore SPI MMIO address */
946 pci_write_config32(lpc_dev, 0xa0, spi_mmio_prev);
947
948 return 0;
949}
950#endif
951
952int8_t restore_mct_information_from_nvram(void)
953{
954 ssize_t s3nv_offset;
955 ssize_t s3nv_file_offset;
956 void * s3nv_cbfs_file_ptr;
957 struct amd_s3_persistent_data *persistent_data;
958
959 /* Obtain CBFS file offset */
960 s3nv_offset = get_s3nv_file_offset();
961 if (s3nv_offset == -1)
962 return -1;
963
964 /* Align flash pointer to nearest boundary */
965 s3nv_file_offset = s3nv_offset;
966 s3nv_offset &= ~(CONFIG_S3_DATA_SIZE-1);
967 s3nv_offset += CONFIG_S3_DATA_SIZE;
968 s3nv_file_offset = s3nv_offset - s3nv_file_offset;
969
970 /* Map data structure in CBFS and restore settings */
971 s3nv_cbfs_file_ptr = cbfs_boot_map_with_leak(S3NV_FILE_NAME, CBFS_TYPE_RAW, NULL);
972 if (!s3nv_cbfs_file_ptr) {
973 printk(BIOS_DEBUG, "S3 state file could not be mapped: %s\n", S3NV_FILE_NAME);
974 return -1;
975 }
976 persistent_data = (s3nv_cbfs_file_ptr + s3nv_file_offset);
977 restore_mct_data_from_save_variable(persistent_data);
978
979 return 0;
980}