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