]>
Commit | Line | Data |
---|---|---|
8ef07571 SG |
1 | /* |
2 | * Copyright (c) 2011 The Chromium OS Authors. | |
3 | * (C) Copyright 2010,2011 | |
4 | * Graeme Russ, <graeme.russ@gmail.com> | |
5 | * | |
6 | * Portions from Coreboot mainboard/google/link/romstage.c | |
7 | * Copyright (C) 2007-2010 coresystems GmbH | |
8 | * Copyright (C) 2011 Google Inc. | |
9 | * | |
10 | * SPDX-License-Identifier: GPL-2.0 | |
11 | */ | |
12 | ||
13 | #include <common.h> | |
65dd74a6 SG |
14 | #include <errno.h> |
15 | #include <fdtdec.h> | |
16 | #include <malloc.h> | |
191c008a SG |
17 | #include <net.h> |
18 | #include <rtc.h> | |
19 | #include <spi.h> | |
20 | #include <spi_flash.h> | |
98655f3a SG |
21 | #include <syscon.h> |
22 | #include <asm/cpu.h> | |
65dd74a6 SG |
23 | #include <asm/processor.h> |
24 | #include <asm/gpio.h> | |
25 | #include <asm/global_data.h> | |
f6220f1a | 26 | #include <asm/mrccache.h> |
aaafcd6c | 27 | #include <asm/mtrr.h> |
65dd74a6 SG |
28 | #include <asm/pci.h> |
29 | #include <asm/arch/me.h> | |
30 | #include <asm/arch/pei_data.h> | |
31 | #include <asm/arch/pch.h> | |
32 | #include <asm/post.h> | |
33 | #include <asm/arch/sandybridge.h> | |
34 | ||
35 | DECLARE_GLOBAL_DATA_PTR; | |
36 | ||
191c008a SG |
37 | #define CMOS_OFFSET_MRC_SEED 152 |
38 | #define CMOS_OFFSET_MRC_SEED_S3 156 | |
39 | #define CMOS_OFFSET_MRC_SEED_CHK 160 | |
40 | ||
65dd74a6 SG |
41 | /* |
42 | * This function looks for the highest region of memory lower than 4GB which | |
43 | * has enough space for U-Boot where U-Boot is aligned on a page boundary. | |
44 | * It overrides the default implementation found elsewhere which simply | |
45 | * picks the end of ram, wherever that may be. The location of the stack, | |
46 | * the relocation address, and how far U-Boot is moved by relocation are | |
47 | * set in the global data structure. | |
48 | */ | |
49 | ulong board_get_usable_ram_top(ulong total_size) | |
50 | { | |
51 | struct memory_info *info = &gd->arch.meminfo; | |
52 | uintptr_t dest_addr = 0; | |
53 | struct memory_area *largest = NULL; | |
54 | int i; | |
55 | ||
56 | /* Find largest area of memory below 4GB */ | |
57 | ||
58 | for (i = 0; i < info->num_areas; i++) { | |
59 | struct memory_area *area = &info->area[i]; | |
60 | ||
61 | if (area->start >= 1ULL << 32) | |
62 | continue; | |
63 | if (!largest || area->size > largest->size) | |
64 | largest = area; | |
65 | } | |
66 | ||
67 | /* If no suitable area was found, return an error. */ | |
68 | assert(largest); | |
69 | if (!largest || largest->size < (2 << 20)) | |
70 | panic("No available memory found for relocation"); | |
71 | ||
72 | dest_addr = largest->start + largest->size; | |
73 | ||
74 | return (ulong)dest_addr; | |
75 | } | |
76 | ||
77 | void dram_init_banksize(void) | |
78 | { | |
79 | struct memory_info *info = &gd->arch.meminfo; | |
80 | int num_banks; | |
81 | int i; | |
82 | ||
83 | for (i = 0, num_banks = 0; i < info->num_areas; i++) { | |
84 | struct memory_area *area = &info->area[i]; | |
85 | ||
86 | if (area->start >= 1ULL << 32) | |
87 | continue; | |
88 | gd->bd->bi_dram[num_banks].start = area->start; | |
89 | gd->bd->bi_dram[num_banks].size = area->size; | |
90 | num_banks++; | |
91 | } | |
92 | } | |
93 | ||
191c008a SG |
94 | static int read_seed_from_cmos(struct pei_data *pei_data) |
95 | { | |
96 | u16 c1, c2, checksum, seed_checksum; | |
93f8a311 | 97 | struct udevice *dev; |
53327d3e | 98 | int ret = 0; |
93f8a311 | 99 | |
53327d3e SG |
100 | ret = uclass_get_device(UCLASS_RTC, 0, &dev); |
101 | if (ret) { | |
102 | debug("Cannot find RTC: err=%d\n", ret); | |
93f8a311 BM |
103 | return -ENODEV; |
104 | } | |
191c008a SG |
105 | |
106 | /* | |
107 | * Read scrambler seeds from CMOS RAM. We don't want to store them in | |
108 | * SPI flash since they change on every boot and that would wear down | |
109 | * the flash too much. So we store these in CMOS and the large MRC | |
110 | * data in SPI flash. | |
111 | */ | |
9fbc5ccd SG |
112 | ret = rtc_read32(dev, CMOS_OFFSET_MRC_SEED, &pei_data->scrambler_seed); |
113 | if (!ret) { | |
114 | ret = rtc_read32(dev, CMOS_OFFSET_MRC_SEED_S3, | |
115 | &pei_data->scrambler_seed_s3); | |
116 | } | |
117 | if (ret) { | |
118 | debug("Failed to read from RTC %s\n", dev->name); | |
119 | return ret; | |
120 | } | |
121 | ||
191c008a SG |
122 | debug("Read scrambler seed 0x%08x from CMOS 0x%02x\n", |
123 | pei_data->scrambler_seed, CMOS_OFFSET_MRC_SEED); | |
191c008a SG |
124 | debug("Read S3 scrambler seed 0x%08x from CMOS 0x%02x\n", |
125 | pei_data->scrambler_seed_s3, CMOS_OFFSET_MRC_SEED_S3); | |
126 | ||
127 | /* Compute seed checksum and compare */ | |
128 | c1 = compute_ip_checksum((u8 *)&pei_data->scrambler_seed, | |
129 | sizeof(u32)); | |
130 | c2 = compute_ip_checksum((u8 *)&pei_data->scrambler_seed_s3, | |
131 | sizeof(u32)); | |
132 | checksum = add_ip_checksums(sizeof(u32), c1, c2); | |
133 | ||
93f8a311 BM |
134 | seed_checksum = rtc_read8(dev, CMOS_OFFSET_MRC_SEED_CHK); |
135 | seed_checksum |= rtc_read8(dev, CMOS_OFFSET_MRC_SEED_CHK + 1) << 8; | |
191c008a SG |
136 | |
137 | if (checksum != seed_checksum) { | |
138 | debug("%s: invalid seed checksum\n", __func__); | |
139 | pei_data->scrambler_seed = 0; | |
140 | pei_data->scrambler_seed_s3 = 0; | |
141 | return -EINVAL; | |
142 | } | |
143 | ||
144 | return 0; | |
145 | } | |
146 | ||
147 | static int prepare_mrc_cache(struct pei_data *pei_data) | |
148 | { | |
149 | struct mrc_data_container *mrc_cache; | |
4b9f6a66 | 150 | struct mrc_region entry; |
191c008a SG |
151 | int ret; |
152 | ||
153 | ret = read_seed_from_cmos(pei_data); | |
154 | if (ret) | |
155 | return ret; | |
42913a1c | 156 | ret = mrccache_get_region(NULL, &entry); |
191c008a SG |
157 | if (ret) |
158 | return ret; | |
159 | mrc_cache = mrccache_find_current(&entry); | |
160 | if (!mrc_cache) | |
161 | return -ENOENT; | |
162 | ||
3e45de6e BM |
163 | pei_data->mrc_input = mrc_cache->data; |
164 | pei_data->mrc_input_len = mrc_cache->data_size; | |
191c008a SG |
165 | debug("%s: at %p, size %x checksum %04x\n", __func__, |
166 | pei_data->mrc_input, pei_data->mrc_input_len, | |
167 | mrc_cache->checksum); | |
168 | ||
169 | return 0; | |
170 | } | |
171 | ||
191c008a SG |
172 | static int write_seeds_to_cmos(struct pei_data *pei_data) |
173 | { | |
174 | u16 c1, c2, checksum; | |
93f8a311 | 175 | struct udevice *dev; |
53327d3e | 176 | int ret = 0; |
93f8a311 | 177 | |
53327d3e SG |
178 | ret = uclass_get_device(UCLASS_RTC, 0, &dev); |
179 | if (ret) { | |
180 | debug("Cannot find RTC: err=%d\n", ret); | |
93f8a311 BM |
181 | return -ENODEV; |
182 | } | |
191c008a SG |
183 | |
184 | /* Save the MRC seed values to CMOS */ | |
93f8a311 | 185 | rtc_write32(dev, CMOS_OFFSET_MRC_SEED, pei_data->scrambler_seed); |
191c008a SG |
186 | debug("Save scrambler seed 0x%08x to CMOS 0x%02x\n", |
187 | pei_data->scrambler_seed, CMOS_OFFSET_MRC_SEED); | |
188 | ||
93f8a311 | 189 | rtc_write32(dev, CMOS_OFFSET_MRC_SEED_S3, pei_data->scrambler_seed_s3); |
191c008a SG |
190 | debug("Save s3 scrambler seed 0x%08x to CMOS 0x%02x\n", |
191 | pei_data->scrambler_seed_s3, CMOS_OFFSET_MRC_SEED_S3); | |
192 | ||
193 | /* Save a simple checksum of the seed values */ | |
194 | c1 = compute_ip_checksum((u8 *)&pei_data->scrambler_seed, | |
195 | sizeof(u32)); | |
196 | c2 = compute_ip_checksum((u8 *)&pei_data->scrambler_seed_s3, | |
197 | sizeof(u32)); | |
198 | checksum = add_ip_checksums(sizeof(u32), c1, c2); | |
199 | ||
93f8a311 BM |
200 | rtc_write8(dev, CMOS_OFFSET_MRC_SEED_CHK, checksum & 0xff); |
201 | rtc_write8(dev, CMOS_OFFSET_MRC_SEED_CHK + 1, (checksum >> 8) & 0xff); | |
191c008a SG |
202 | |
203 | return 0; | |
204 | } | |
205 | ||
191c008a SG |
206 | /* Use this hook to save our SDRAM parameters */ |
207 | int misc_init_r(void) | |
208 | { | |
209 | int ret; | |
210 | ||
42913a1c | 211 | ret = mrccache_save(); |
191c008a SG |
212 | if (ret) |
213 | printf("Unable to save MRC data: %d\n", ret); | |
214 | ||
215 | return 0; | |
216 | } | |
217 | ||
65dd74a6 SG |
218 | static const char *const ecc_decoder[] = { |
219 | "inactive", | |
220 | "active on IO", | |
221 | "disabled on IO", | |
222 | "active" | |
223 | }; | |
224 | ||
225 | /* | |
226 | * Dump in the log memory controller configuration as read from the memory | |
227 | * controller registers. | |
228 | */ | |
229 | static void report_memory_config(void) | |
230 | { | |
231 | u32 addr_decoder_common, addr_decode_ch[2]; | |
232 | int i; | |
233 | ||
234 | addr_decoder_common = readl(MCHBAR_REG(0x5000)); | |
235 | addr_decode_ch[0] = readl(MCHBAR_REG(0x5004)); | |
236 | addr_decode_ch[1] = readl(MCHBAR_REG(0x5008)); | |
237 | ||
238 | debug("memcfg DDR3 clock %d MHz\n", | |
239 | (readl(MCHBAR_REG(0x5e04)) * 13333 * 2 + 50) / 100); | |
240 | debug("memcfg channel assignment: A: %d, B % d, C % d\n", | |
241 | addr_decoder_common & 3, | |
242 | (addr_decoder_common >> 2) & 3, | |
243 | (addr_decoder_common >> 4) & 3); | |
244 | ||
245 | for (i = 0; i < ARRAY_SIZE(addr_decode_ch); i++) { | |
246 | u32 ch_conf = addr_decode_ch[i]; | |
247 | debug("memcfg channel[%d] config (%8.8x):\n", i, ch_conf); | |
248 | debug(" ECC %s\n", ecc_decoder[(ch_conf >> 24) & 3]); | |
249 | debug(" enhanced interleave mode %s\n", | |
250 | ((ch_conf >> 22) & 1) ? "on" : "off"); | |
251 | debug(" rank interleave %s\n", | |
252 | ((ch_conf >> 21) & 1) ? "on" : "off"); | |
253 | debug(" DIMMA %d MB width x%d %s rank%s\n", | |
254 | ((ch_conf >> 0) & 0xff) * 256, | |
255 | ((ch_conf >> 19) & 1) ? 16 : 8, | |
256 | ((ch_conf >> 17) & 1) ? "dual" : "single", | |
257 | ((ch_conf >> 16) & 1) ? "" : ", selected"); | |
258 | debug(" DIMMB %d MB width x%d %s rank%s\n", | |
259 | ((ch_conf >> 8) & 0xff) * 256, | |
260 | ((ch_conf >> 20) & 1) ? 16 : 8, | |
261 | ((ch_conf >> 18) & 1) ? "dual" : "single", | |
262 | ((ch_conf >> 16) & 1) ? ", selected" : ""); | |
263 | } | |
264 | } | |
265 | ||
266 | static void post_system_agent_init(struct pei_data *pei_data) | |
267 | { | |
268 | /* If PCIe init is skipped, set the PEG clock gating */ | |
269 | if (!pei_data->pcie_init) | |
270 | setbits_le32(MCHBAR_REG(0x7010), 1); | |
271 | } | |
272 | ||
273 | static asmlinkage void console_tx_byte(unsigned char byte) | |
274 | { | |
275 | #ifdef DEBUG | |
276 | putc(byte); | |
277 | #endif | |
278 | } | |
279 | ||
191c008a SG |
280 | static int recovery_mode_enabled(void) |
281 | { | |
282 | return false; | |
283 | } | |
284 | ||
65dd74a6 SG |
285 | /** |
286 | * Find the PEI executable in the ROM and execute it. | |
287 | * | |
1641bb8c SG |
288 | * @dev: Northbridge device |
289 | * @pei_data: configuration data for UEFI PEI reference code | |
65dd74a6 | 290 | */ |
c02a4242 SG |
291 | int sdram_initialise(struct udevice *dev, struct udevice *me_dev, |
292 | struct pei_data *pei_data) | |
65dd74a6 SG |
293 | { |
294 | unsigned version; | |
295 | const char *data; | |
296 | uint16_t done; | |
297 | int ret; | |
298 | ||
fad12961 | 299 | report_platform_info(dev); |
65dd74a6 SG |
300 | |
301 | /* Wait for ME to be ready */ | |
c02a4242 | 302 | ret = intel_early_me_init(me_dev); |
65dd74a6 SG |
303 | if (ret) |
304 | return ret; | |
c02a4242 | 305 | ret = intel_early_me_uma_size(me_dev); |
65dd74a6 SG |
306 | if (ret < 0) |
307 | return ret; | |
308 | ||
309 | debug("Starting UEFI PEI System Agent\n"); | |
310 | ||
191c008a SG |
311 | /* |
312 | * Do not pass MRC data in for recovery mode boot, | |
313 | * Always pass it in for S3 resume. | |
314 | */ | |
315 | if (!recovery_mode_enabled() || | |
316 | pei_data->boot_mode == PEI_BOOT_RESUME) { | |
317 | ret = prepare_mrc_cache(pei_data); | |
318 | if (ret) | |
319 | debug("prepare_mrc_cache failed: %d\n", ret); | |
320 | } | |
321 | ||
65dd74a6 SG |
322 | /* If MRC data is not found we cannot continue S3 resume. */ |
323 | if (pei_data->boot_mode == PEI_BOOT_RESUME && !pei_data->mrc_input) { | |
324 | debug("Giving up in sdram_initialize: No MRC data\n"); | |
5021c81f | 325 | reset_cpu(0); |
65dd74a6 SG |
326 | } |
327 | ||
328 | /* Pass console handler in pei_data */ | |
329 | pei_data->tx_byte = console_tx_byte; | |
330 | ||
331 | debug("PEI data at %p, size %x:\n", pei_data, sizeof(*pei_data)); | |
332 | ||
8c5224c9 | 333 | data = (char *)CONFIG_X86_MRC_ADDR; |
65dd74a6 SG |
334 | if (data) { |
335 | int rv; | |
336 | int (*func)(struct pei_data *); | |
fd8f4729 | 337 | ulong start; |
65dd74a6 SG |
338 | |
339 | debug("Calling MRC at %p\n", data); | |
340 | post_code(POST_PRE_MRC); | |
fd8f4729 | 341 | start = get_timer(0); |
65dd74a6 SG |
342 | func = (int (*)(struct pei_data *))data; |
343 | rv = func(pei_data); | |
344 | post_code(POST_MRC); | |
345 | if (rv) { | |
346 | switch (rv) { | |
347 | case -1: | |
348 | printf("PEI version mismatch.\n"); | |
349 | break; | |
350 | case -2: | |
351 | printf("Invalid memory frequency.\n"); | |
352 | break; | |
353 | default: | |
354 | printf("MRC returned %x.\n", rv); | |
355 | } | |
356 | printf("Nonzero MRC return value.\n"); | |
357 | return -EFAULT; | |
358 | } | |
fd8f4729 | 359 | debug("MRC execution time %lu ms\n", get_timer(start)); |
65dd74a6 SG |
360 | } else { |
361 | printf("UEFI PEI System Agent not found.\n"); | |
362 | return -ENOSYS; | |
363 | } | |
364 | ||
365 | #if CONFIG_USBDEBUG | |
366 | /* mrc.bin reconfigures USB, so reinit it to have debug */ | |
367 | early_usbdebug_init(); | |
368 | #endif | |
369 | ||
370 | version = readl(MCHBAR_REG(0x5034)); | |
371 | debug("System Agent Version %d.%d.%d Build %d\n", | |
372 | version >> 24 , (version >> 16) & 0xff, | |
373 | (version >> 8) & 0xff, version & 0xff); | |
c6c80d8b | 374 | debug("MRC output data length %#x at %p\n", pei_data->mrc_output_len, |
191c008a | 375 | pei_data->mrc_output); |
65dd74a6 SG |
376 | |
377 | /* | |
378 | * Send ME init done for SandyBridge here. This is done inside the | |
379 | * SystemAgent binary on IvyBridge | |
380 | */ | |
1641bb8c | 381 | dm_pci_read_config16(dev, PCI_DEVICE_ID, &done); |
65dd74a6 SG |
382 | done &= BASE_REV_MASK; |
383 | if (BASE_REV_SNB == done) | |
c02a4242 | 384 | intel_early_me_init_done(dev, me_dev, ME_INIT_STATUS_SUCCESS); |
65dd74a6 | 385 | else |
c02a4242 | 386 | intel_early_me_status(me_dev); |
65dd74a6 SG |
387 | |
388 | post_system_agent_init(pei_data); | |
389 | report_memory_config(); | |
390 | ||
191c008a SG |
391 | /* S3 resume: don't save scrambler seed or MRC data */ |
392 | if (pei_data->boot_mode != PEI_BOOT_RESUME) { | |
393 | /* | |
394 | * This will be copied to SDRAM in reserve_arch(), then written | |
42913a1c | 395 | * to SPI flash in mrccache_save() |
191c008a SG |
396 | */ |
397 | gd->arch.mrc_output = (char *)pei_data->mrc_output; | |
398 | gd->arch.mrc_output_len = pei_data->mrc_output_len; | |
399 | ret = write_seeds_to_cmos(pei_data); | |
400 | if (ret) | |
401 | debug("Failed to write seeds to CMOS: %d\n", ret); | |
402 | } | |
403 | ||
404 | return 0; | |
405 | } | |
406 | ||
407 | int reserve_arch(void) | |
408 | { | |
42913a1c | 409 | return mrccache_reserve(); |
65dd74a6 SG |
410 | } |
411 | ||
412 | static int copy_spd(struct pei_data *peid) | |
413 | { | |
414 | const int gpio_vector[] = {41, 42, 43, 10, -1}; | |
415 | int spd_index; | |
416 | const void *blob = gd->fdt_blob; | |
417 | int node, spd_node; | |
418 | int ret, i; | |
419 | ||
420 | for (i = 0; ; i++) { | |
421 | if (gpio_vector[i] == -1) | |
422 | break; | |
423 | ret = gpio_requestf(gpio_vector[i], "spd_id%d", i); | |
424 | if (ret) { | |
425 | debug("%s: Could not request gpio %d\n", __func__, | |
426 | gpio_vector[i]); | |
427 | return ret; | |
428 | } | |
429 | } | |
430 | spd_index = gpio_get_values_as_int(gpio_vector); | |
431 | debug("spd index %d\n", spd_index); | |
432 | node = fdtdec_next_compatible(blob, 0, COMPAT_MEMORY_SPD); | |
433 | if (node < 0) { | |
434 | printf("SPD data not found.\n"); | |
435 | return -ENOENT; | |
436 | } | |
437 | ||
438 | for (spd_node = fdt_first_subnode(blob, node); | |
439 | spd_node > 0; | |
440 | spd_node = fdt_next_subnode(blob, spd_node)) { | |
441 | const char *data; | |
442 | int len; | |
443 | ||
444 | if (fdtdec_get_int(blob, spd_node, "reg", -1) != spd_index) | |
445 | continue; | |
446 | data = fdt_getprop(blob, spd_node, "data", &len); | |
447 | if (len < sizeof(peid->spd_data[0])) { | |
448 | printf("Missing SPD data\n"); | |
449 | return -EINVAL; | |
450 | } | |
451 | ||
452 | debug("Using SDRAM SPD data for '%s'\n", | |
453 | fdt_get_name(blob, spd_node, NULL)); | |
454 | memcpy(peid->spd_data[0], data, sizeof(peid->spd_data[0])); | |
455 | break; | |
456 | } | |
457 | ||
458 | if (spd_node < 0) { | |
459 | printf("No SPD data found for index %d\n", spd_index); | |
460 | return -ENOENT; | |
461 | } | |
462 | ||
463 | return 0; | |
464 | } | |
465 | ||
466 | /** | |
467 | * add_memory_area() - Add a new usable memory area to our list | |
468 | * | |
469 | * Note: @start and @end must not span the first 4GB boundary | |
470 | * | |
471 | * @info: Place to store memory info | |
472 | * @start: Start of this memory area | |
473 | * @end: End of this memory area + 1 | |
474 | */ | |
475 | static int add_memory_area(struct memory_info *info, | |
476 | uint64_t start, uint64_t end) | |
477 | { | |
478 | struct memory_area *ptr; | |
479 | ||
480 | if (info->num_areas == CONFIG_NR_DRAM_BANKS) | |
481 | return -ENOSPC; | |
482 | ||
483 | ptr = &info->area[info->num_areas]; | |
484 | ptr->start = start; | |
485 | ptr->size = end - start; | |
486 | info->total_memory += ptr->size; | |
487 | if (ptr->start < (1ULL << 32)) | |
488 | info->total_32bit_memory += ptr->size; | |
489 | debug("%d: memory %llx size %llx, total now %llx / %llx\n", | |
490 | info->num_areas, ptr->start, ptr->size, | |
491 | info->total_32bit_memory, info->total_memory); | |
492 | info->num_areas++; | |
493 | ||
494 | return 0; | |
495 | } | |
496 | ||
497 | /** | |
498 | * sdram_find() - Find available memory | |
499 | * | |
500 | * This is a bit complicated since on x86 there are system memory holes all | |
501 | * over the place. We create a list of available memory blocks | |
2588e711 SG |
502 | * |
503 | * @dev: Northbridge device | |
65dd74a6 | 504 | */ |
2588e711 | 505 | static int sdram_find(struct udevice *dev) |
65dd74a6 SG |
506 | { |
507 | struct memory_info *info = &gd->arch.meminfo; | |
508 | uint32_t tseg_base, uma_size, tolud; | |
509 | uint64_t tom, me_base, touud; | |
510 | uint64_t uma_memory_base = 0; | |
511 | uint64_t uma_memory_size; | |
512 | unsigned long long tomk; | |
513 | uint16_t ggc; | |
2588e711 | 514 | u32 val; |
65dd74a6 SG |
515 | |
516 | /* Total Memory 2GB example: | |
517 | * | |
518 | * 00000000 0000MB-1992MB 1992MB RAM (writeback) | |
519 | * 7c800000 1992MB-2000MB 8MB TSEG (SMRR) | |
520 | * 7d000000 2000MB-2002MB 2MB GFX GTT (uncached) | |
521 | * 7d200000 2002MB-2034MB 32MB GFX UMA (uncached) | |
522 | * 7f200000 2034MB TOLUD | |
523 | * 7f800000 2040MB MEBASE | |
524 | * 7f800000 2040MB-2048MB 8MB ME UMA (uncached) | |
525 | * 80000000 2048MB TOM | |
526 | * 100000000 4096MB-4102MB 6MB RAM (writeback) | |
527 | * | |
528 | * Total Memory 4GB example: | |
529 | * | |
530 | * 00000000 0000MB-2768MB 2768MB RAM (writeback) | |
531 | * ad000000 2768MB-2776MB 8MB TSEG (SMRR) | |
532 | * ad800000 2776MB-2778MB 2MB GFX GTT (uncached) | |
533 | * ada00000 2778MB-2810MB 32MB GFX UMA (uncached) | |
534 | * afa00000 2810MB TOLUD | |
535 | * ff800000 4088MB MEBASE | |
536 | * ff800000 4088MB-4096MB 8MB ME UMA (uncached) | |
537 | * 100000000 4096MB TOM | |
538 | * 100000000 4096MB-5374MB 1278MB RAM (writeback) | |
539 | * 14fe00000 5368MB TOUUD | |
540 | */ | |
541 | ||
542 | /* Top of Upper Usable DRAM, including remap */ | |
2588e711 SG |
543 | dm_pci_read_config32(dev, TOUUD + 4, &val); |
544 | touud = (uint64_t)val << 32; | |
545 | dm_pci_read_config32(dev, TOUUD, &val); | |
546 | touud |= val; | |
65dd74a6 SG |
547 | |
548 | /* Top of Lower Usable DRAM */ | |
2588e711 | 549 | dm_pci_read_config32(dev, TOLUD, &tolud); |
65dd74a6 SG |
550 | |
551 | /* Top of Memory - does not account for any UMA */ | |
2588e711 SG |
552 | dm_pci_read_config32(dev, 0xa4, &val); |
553 | tom = (uint64_t)val << 32; | |
554 | dm_pci_read_config32(dev, 0xa0, &val); | |
555 | tom |= val; | |
65dd74a6 SG |
556 | |
557 | debug("TOUUD %llx TOLUD %08x TOM %llx\n", touud, tolud, tom); | |
558 | ||
559 | /* ME UMA needs excluding if total memory <4GB */ | |
2588e711 SG |
560 | dm_pci_read_config32(dev, 0x74, &val); |
561 | me_base = (uint64_t)val << 32; | |
562 | dm_pci_read_config32(dev, 0x70, &val); | |
563 | me_base |= val; | |
65dd74a6 SG |
564 | |
565 | debug("MEBASE %llx\n", me_base); | |
566 | ||
567 | /* TODO: Get rid of all this shifting by 10 bits */ | |
568 | tomk = tolud >> 10; | |
569 | if (me_base == tolud) { | |
570 | /* ME is from MEBASE-TOM */ | |
571 | uma_size = (tom - me_base) >> 10; | |
572 | /* Increment TOLUD to account for ME as RAM */ | |
573 | tolud += uma_size << 10; | |
574 | /* UMA starts at old TOLUD */ | |
575 | uma_memory_base = tomk * 1024ULL; | |
576 | uma_memory_size = uma_size * 1024ULL; | |
577 | debug("ME UMA base %llx size %uM\n", me_base, uma_size >> 10); | |
578 | } | |
579 | ||
580 | /* Graphics memory comes next */ | |
2588e711 | 581 | dm_pci_read_config16(dev, GGC, &ggc); |
65dd74a6 SG |
582 | if (!(ggc & 2)) { |
583 | debug("IGD decoded, subtracting "); | |
584 | ||
585 | /* Graphics memory */ | |
586 | uma_size = ((ggc >> 3) & 0x1f) * 32 * 1024ULL; | |
587 | debug("%uM UMA", uma_size >> 10); | |
588 | tomk -= uma_size; | |
589 | uma_memory_base = tomk * 1024ULL; | |
590 | uma_memory_size += uma_size * 1024ULL; | |
591 | ||
592 | /* GTT Graphics Stolen Memory Size (GGMS) */ | |
593 | uma_size = ((ggc >> 8) & 0x3) * 1024ULL; | |
594 | tomk -= uma_size; | |
595 | uma_memory_base = tomk * 1024ULL; | |
596 | uma_memory_size += uma_size * 1024ULL; | |
597 | debug(" and %uM GTT\n", uma_size >> 10); | |
598 | } | |
599 | ||
600 | /* Calculate TSEG size from its base which must be below GTT */ | |
2588e711 | 601 | dm_pci_read_config32(dev, 0xb8, &tseg_base); |
65dd74a6 SG |
602 | uma_size = (uma_memory_base - tseg_base) >> 10; |
603 | tomk -= uma_size; | |
604 | uma_memory_base = tomk * 1024ULL; | |
605 | uma_memory_size += uma_size * 1024ULL; | |
606 | debug("TSEG base 0x%08x size %uM\n", tseg_base, uma_size >> 10); | |
607 | ||
608 | debug("Available memory below 4GB: %lluM\n", tomk >> 10); | |
609 | ||
610 | /* Report the memory regions */ | |
611 | add_memory_area(info, 1 << 20, 2 << 28); | |
612 | add_memory_area(info, (2 << 28) + (2 << 20), 4 << 28); | |
613 | add_memory_area(info, (4 << 28) + (2 << 20), tseg_base); | |
614 | add_memory_area(info, 1ULL << 32, touud); | |
aaafcd6c SG |
615 | |
616 | /* Add MTRRs for memory */ | |
617 | mtrr_add_request(MTRR_TYPE_WRBACK, 0, 2ULL << 30); | |
618 | mtrr_add_request(MTRR_TYPE_WRBACK, 2ULL << 30, 512 << 20); | |
619 | mtrr_add_request(MTRR_TYPE_WRBACK, 0xaULL << 28, 256 << 20); | |
620 | mtrr_add_request(MTRR_TYPE_UNCACHEABLE, tseg_base, 16 << 20); | |
621 | mtrr_add_request(MTRR_TYPE_UNCACHEABLE, tseg_base + (16 << 20), | |
622 | 32 << 20); | |
623 | ||
65dd74a6 SG |
624 | /* |
625 | * If >= 4GB installed then memory from TOLUD to 4GB | |
626 | * is remapped above TOM, TOUUD will account for both | |
627 | */ | |
628 | if (touud > (1ULL << 32ULL)) { | |
629 | debug("Available memory above 4GB: %lluM\n", | |
630 | (touud >> 20) - 4096); | |
631 | } | |
632 | ||
633 | return 0; | |
634 | } | |
635 | ||
636 | static void rcba_config(void) | |
637 | { | |
638 | /* | |
639 | * GFX INTA -> PIRQA (MSI) | |
640 | * D28IP_P3IP WLAN INTA -> PIRQB | |
641 | * D29IP_E1P EHCI1 INTA -> PIRQD | |
642 | * D26IP_E2P EHCI2 INTA -> PIRQF | |
643 | * D31IP_SIP SATA INTA -> PIRQF (MSI) | |
644 | * D31IP_SMIP SMBUS INTB -> PIRQH | |
645 | * D31IP_TTIP THRT INTC -> PIRQA | |
646 | * D27IP_ZIP HDA INTA -> PIRQA (MSI) | |
647 | * | |
648 | * TRACKPAD -> PIRQE (Edge Triggered) | |
649 | * TOUCHSCREEN -> PIRQG (Edge Triggered) | |
650 | */ | |
651 | ||
652 | /* Device interrupt pin register (board specific) */ | |
653 | writel((INTC << D31IP_TTIP) | (NOINT << D31IP_SIP2) | | |
654 | (INTB << D31IP_SMIP) | (INTA << D31IP_SIP), RCB_REG(D31IP)); | |
655 | writel(NOINT << D30IP_PIP, RCB_REG(D30IP)); | |
656 | writel(INTA << D29IP_E1P, RCB_REG(D29IP)); | |
657 | writel(INTA << D28IP_P3IP, RCB_REG(D28IP)); | |
658 | writel(INTA << D27IP_ZIP, RCB_REG(D27IP)); | |
659 | writel(INTA << D26IP_E2P, RCB_REG(D26IP)); | |
660 | writel(NOINT << D25IP_LIP, RCB_REG(D25IP)); | |
661 | writel(NOINT << D22IP_MEI1IP, RCB_REG(D22IP)); | |
662 | ||
663 | /* Device interrupt route registers */ | |
664 | writel(DIR_ROUTE(PIRQB, PIRQH, PIRQA, PIRQC), RCB_REG(D31IR)); | |
665 | writel(DIR_ROUTE(PIRQD, PIRQE, PIRQF, PIRQG), RCB_REG(D29IR)); | |
666 | writel(DIR_ROUTE(PIRQB, PIRQC, PIRQD, PIRQE), RCB_REG(D28IR)); | |
667 | writel(DIR_ROUTE(PIRQA, PIRQH, PIRQA, PIRQB), RCB_REG(D27IR)); | |
668 | writel(DIR_ROUTE(PIRQF, PIRQE, PIRQG, PIRQH), RCB_REG(D26IR)); | |
669 | writel(DIR_ROUTE(PIRQA, PIRQB, PIRQC, PIRQD), RCB_REG(D25IR)); | |
670 | writel(DIR_ROUTE(PIRQA, PIRQB, PIRQC, PIRQD), RCB_REG(D22IR)); | |
671 | ||
672 | /* Enable IOAPIC (generic) */ | |
673 | writew(0x0100, RCB_REG(OIC)); | |
674 | /* PCH BWG says to read back the IOAPIC enable register */ | |
675 | (void)readw(RCB_REG(OIC)); | |
676 | ||
677 | /* Disable unused devices (board specific) */ | |
678 | setbits_le32(RCB_REG(FD), PCH_DISABLE_ALWAYS); | |
679 | } | |
8ef07571 SG |
680 | |
681 | int dram_init(void) | |
682 | { | |
65dd74a6 SG |
683 | struct pei_data pei_data __aligned(8) = { |
684 | .pei_version = PEI_VERSION, | |
685 | .mchbar = DEFAULT_MCHBAR, | |
686 | .dmibar = DEFAULT_DMIBAR, | |
687 | .epbar = DEFAULT_EPBAR, | |
2d934e57 | 688 | .pciexbar = CONFIG_PCIE_ECAM_BASE, |
65dd74a6 SG |
689 | .smbusbar = SMBUS_IO_BASE, |
690 | .wdbbar = 0x4000000, | |
691 | .wdbsize = 0x1000, | |
692 | .hpet_address = CONFIG_HPET_ADDRESS, | |
693 | .rcba = DEFAULT_RCBABASE, | |
694 | .pmbase = DEFAULT_PMBASE, | |
695 | .gpiobase = DEFAULT_GPIOBASE, | |
696 | .thermalbase = 0xfed08000, | |
697 | .system_type = 0, /* 0 Mobile, 1 Desktop/Server */ | |
698 | .tseg_size = CONFIG_SMM_TSEG_SIZE, | |
699 | .ts_addresses = { 0x00, 0x00, 0x00, 0x00 }, | |
700 | .ec_present = 1, | |
701 | .ddr3lv_support = 1, | |
702 | /* | |
703 | * 0 = leave channel enabled | |
704 | * 1 = disable dimm 0 on channel | |
705 | * 2 = disable dimm 1 on channel | |
706 | * 3 = disable dimm 0+1 on channel | |
707 | */ | |
708 | .dimm_channel0_disabled = 2, | |
709 | .dimm_channel1_disabled = 2, | |
710 | .max_ddr3_freq = 1600, | |
711 | .usb_port_config = { | |
712 | /* | |
713 | * Empty and onboard Ports 0-7, set to un-used pin | |
714 | * OC3 | |
715 | */ | |
716 | { 0, 3, 0x0000 }, /* P0= Empty */ | |
717 | { 1, 0, 0x0040 }, /* P1= Left USB 1 (OC0) */ | |
718 | { 1, 1, 0x0040 }, /* P2= Left USB 2 (OC1) */ | |
719 | { 1, 3, 0x0040 }, /* P3= SDCARD (no OC) */ | |
720 | { 0, 3, 0x0000 }, /* P4= Empty */ | |
721 | { 1, 3, 0x0040 }, /* P5= WWAN (no OC) */ | |
722 | { 0, 3, 0x0000 }, /* P6= Empty */ | |
723 | { 0, 3, 0x0000 }, /* P7= Empty */ | |
724 | /* | |
725 | * Empty and onboard Ports 8-13, set to un-used pin | |
726 | * OC4 | |
727 | */ | |
728 | { 1, 4, 0x0040 }, /* P8= Camera (no OC) */ | |
729 | { 1, 4, 0x0040 }, /* P9= Bluetooth (no OC) */ | |
730 | { 0, 4, 0x0000 }, /* P10= Empty */ | |
731 | { 0, 4, 0x0000 }, /* P11= Empty */ | |
732 | { 0, 4, 0x0000 }, /* P12= Empty */ | |
733 | { 0, 4, 0x0000 }, /* P13= Empty */ | |
734 | }, | |
735 | }; | |
c02a4242 | 736 | struct udevice *dev, *me_dev; |
65dd74a6 SG |
737 | int ret; |
738 | ||
3f603cbb | 739 | ret = uclass_first_device_err(UCLASS_NORTHBRIDGE, &dev); |
1641bb8c SG |
740 | if (ret) |
741 | return ret; | |
98655f3a | 742 | ret = syscon_get_by_driver_data(X86_SYSCON_ME, &me_dev); |
c02a4242 SG |
743 | if (ret) |
744 | return ret; | |
65dd74a6 | 745 | debug("Boot mode %d\n", gd->arch.pei_boot_mode); |
c6c80d8b | 746 | debug("mrc_input %p\n", pei_data.mrc_input); |
65dd74a6 SG |
747 | pei_data.boot_mode = gd->arch.pei_boot_mode; |
748 | ret = copy_spd(&pei_data); | |
749 | if (!ret) | |
c02a4242 | 750 | ret = sdram_initialise(dev, me_dev, &pei_data); |
65dd74a6 SG |
751 | if (ret) |
752 | return ret; | |
753 | ||
754 | rcba_config(); | |
755 | quick_ram_check(); | |
756 | ||
757 | writew(0xCAFE, MCHBAR_REG(SSKPD)); | |
758 | ||
759 | post_code(POST_DRAM); | |
760 | ||
761 | ret = sdram_find(dev); | |
762 | if (ret) | |
763 | return ret; | |
764 | ||
765 | gd->ram_size = gd->arch.meminfo.total_32bit_memory; | |
8ef07571 SG |
766 | |
767 | return 0; | |
768 | } |